From noreply at scummvm.org  Mon Jun  1 05:06:45 2026
From: noreply at scummvm.org (mgerhardy)
Date: Mon,  1 Jun 2026 05:06:45 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm-tools master ->
 309b22a7c4958350e5ef1e35dc7bbeee098d04ae
Message-ID: <20260601050645.0608A700591@ron.scummvm.net>

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

Summary:
309b22a7c4 MACS2: updated decompiler


Commit: 309b22a7c4958350e5ef1e35dc7bbeee098d04ae
    https://github.com/scummvm/scummvm-tools/commit/309b22a7c4958350e5ef1e35dc7bbeee098d04ae
Author: Martin Gerhardy (martin.gerhardy at gmail.com)
Date: 2026-05-31T17:32:32+02:00

Commit Message:
MACS2: updated decompiler

Changed paths:
    engines/macs2/demacs2.cpp


diff --git a/engines/macs2/demacs2.cpp b/engines/macs2/demacs2.cpp
index da344f37..099ec06e 100644
--- a/engines/macs2/demacs2.cpp
+++ b/engines/macs2/demacs2.cpp
@@ -19,7 +19,7 @@
  *
  */
 
-/* MACS2 Script disassembler */
+/* MACS2 Script decompiler */
 
 #include <stdio.h>
 #include <stdint.h>
@@ -45,76 +45,36 @@ static uint16_t readWord() {
 
 static std::string formatValue() {
 	uint8_t type = readByte();
-	uint16_t index = readWord();
+	uint16_t val = readWord();
 	if (type == 0x00) {
 		char buf[32];
-		snprintf(buf, sizeof(buf), "%u", index);
+		snprintf(buf, sizeof(buf), "%u", val);
 		return buf;
 	} else if (type == 0xFF) {
-		// Special runtime values
-		const char *name = nullptr;
-		switch (index) {
-		case 0x01: name = "interacted_use"; break;
-		case 0x02: name = "interacted_look"; break;
-		case 0x03: name = "interacted_talk"; break;
-		case 0x04: name = "char_area"; break;
-		case 0x05: name = "noop"; break;
-		case 0x06: return "1";
-		case 0x07: return "0";
-		case 0x08: return "0";
-		case 0x09: return "0";
-		case 0x0A: return "1";
-		case 0x0B: name = "repeat_run_flag"; break;
-		case 0x0C: return "1";
-		case 0x0D: name = "chosen_dialogue_option"; break;
-		case 0x23: name = "path_walkable_result"; break;
-		case 0x24: name = "actor_x"; break;
-		case 0x25: name = "actor_y"; break;
-		case 0x26: name = "is_scene_init"; break;
-		case 0x27: name = "repeat_char_area"; break;
-		case 0x28: name = "inventory_check_result"; break;
-		case 0x29: name = "anim_range_test_result"; break;
-		case 0x2A: name = "inventory_combine_flag"; break;
-		case 0x2B: name = "inventory_action_flag"; break;
-		case 0x2C: name = "interacted_panel_use"; break;
-		case 0x2D: name = "current_scene"; break;
-		case 0x2E: return "2";
-		case 0x2F: name = "last_scene"; break;
-		case 0x30: name = "music_enabled"; break;
-		case 0x31: name = "sound_enabled"; break;
+		switch (val) {
+		case 0x01: return "interactedUse";
+		case 0x02: return "interactedLook";
+		case 0x03: return "interactedTalk";
+		case 0x04: return "areaAtActor";
+		case 0x0B: return "isRepeatRun";
+		case 0x0D: return "dialogueResult";
+		case 0x23: return "pathWalkable";
+		case 0x24: return "actorX";
+		case 0x25: return "actorY";
+		case 0x26: return "isSceneInit";
+		case 0x28: return "invCheck";
+		case 0x2A: return "invCombine";
+		case 0x2B: return "invAction";
+		case 0x2D: return "curScene";
+		case 0x2F: return "prevScene";
 		default:
-			if (index >= 0x0E && index <= 0x22) {
-				char buf[32];
-				snprintf(buf, sizeof(buf), "%u", index - 0x0D);
-				return buf;
-			}
 			char buf[32];
-			snprintf(buf, sizeof(buf), "$special_0x%02x", index);
+			snprintf(buf, sizeof(buf), "special[0x%02x]", val);
 			return buf;
 		}
-		return std::string("$") + name;
-	} else {
-		char buf[32];
-		snprintf(buf, sizeof(buf), "var[%u]", index);
-		return buf;
 	}
-}
-
-// Read a value but return both words as "val" (for 32-bit context)
-static std::string formatValue32() {
-	return formatValue();
-}
-
-static std::string formatObjectId() {
-	std::string v = formatValue();
-	return v + " - 0x400";
-}
-
-static std::string formatSaveTarget() {
-	readByte(); // type (ignored for save target)
-	uint16_t index = readWord();
 	char buf[32];
-	snprintf(buf, sizeof(buf), "var[%u]", index);
+	snprintf(buf, sizeof(buf), "var[%u]", val);
 	return buf;
 }
 
@@ -126,7 +86,7 @@ static const char *cmpOpName(uint8_t op) {
 	case 0x04: return ">";
 	case 0x05: return "<=";
 	case 0x06: return ">=";
-	default: return "??";
+	default: return "?";
 	}
 }
 
@@ -141,490 +101,472 @@ static void disassemble() {
 		uint8_t length = readByte();
 		uint32_t endPos = pos + length;
 
-		// Adjust indent before printing for block-end opcodes
+		// Adjust indent for block-end opcodes
 		if (opcode == 0x07 && indent > 0) indent--;
+		if (opcode == 0x08 && indent > 0) indent--;
 
-		// Print indent
 		for (int i = 0; i < indent; i++) printf("  ");
-		printf("[%04X] ", instrAddr);
+		printf("%04x: ", instrAddr);
 
 		switch (opcode) {
-		case 0x01: {
-			readByte(); // padding
+		case 0x01: { // setVar
+			readByte();
 			uint16_t varIdx = readWord();
 			std::string val = formatValue();
-			printf("SET var[%u] = %s\n", varIdx, val.c_str());
+			printf("setVar var[%u] = %s\n", varIdx, val.c_str());
 			break;
 		}
-		case 0x02: {
-			readByte(); // padding
+		case 0x02: { // setVarOr
+			readByte();
 			uint16_t varIdx = readWord();
-			std::string val1 = formatValue();
-			std::string val2 = formatValue();
-			printf("SET_OR var[%u] = %s | %s\n", varIdx, val1.c_str(), val2.c_str());
+			std::string val = formatValue();
+			printf("setVarOr var[%u] = %s\n", varIdx, val.c_str());
 			break;
 		}
-		case 0x03: {
+		case 0x03: { // ifTrue
 			std::string val = formatValue();
-			printf("IF_TRUE %s THEN SKIP {\n", val.c_str());
+			printf("ifTrue (%s)\n", val.c_str());
 			indent++;
 			break;
 		}
-		case 0x04: {
+		case 0x04: { // ifFalse
 			std::string val = formatValue();
-			printf("IF_FALSE %s THEN SKIP {\n", val.c_str());
+			printf("ifFalse (%s)\n", val.c_str());
 			indent++;
 			break;
 		}
-		case 0x05: {
+		case 0x05: { // compare
 			uint8_t cmpOp = readByte();
-			std::string v1 = formatValue();
-			std::string v2 = formatValue();
-			printf("IF %s %s %s {\n", v1.c_str(), cmpOpName(cmpOp), v2.c_str());
+			std::string a = formatValue();
+			std::string b = formatValue();
+			printf("compare (%s %s %s)\n", a.c_str(), cmpOpName(cmpOp), b.c_str());
 			indent++;
 			break;
 		}
-		case 0x06: {
+		case 0x06: { // ifInteraction
 			uint8_t subOp = readByte();
-			std::string obj1 = formatValue();
-			std::string obj2 = formatValue();
-			std::string cmp1 = formatValue();
-			std::string cmp2 = formatValue();
-			if (subOp == 0x01)
-				printf("IF_USE_ON (%s, %s) MATCHES (%s, %s) {\n", obj1.c_str(), obj2.c_str(), cmp1.c_str(), cmp2.c_str());
-			else
-				printf("IF_USE_ON (%s, %s) NOT_MATCHES (%s, %s) {\n", obj1.c_str(), obj2.c_str(), cmp1.c_str(), cmp2.c_str());
+			std::string i = formatValue();
+			std::string a = formatValue();
+			std::string b = formatValue();
+			printf("ifInteraction %s(%s, %s, %s)\n", subOp == 2 ? "NOT " : "", i.c_str(), a.c_str(), b.c_str());
 			indent++;
 			break;
 		}
-		case 0x07: {
-			printf("}\n");
+		case 0x07: { // endIf
+			printf("endIf\n");
 			break;
 		}
-		case 0x08: {
-			printf("} ELSE {\n");
+		case 0x08: { // else
+			printf("else\n");
+			indent++;
 			break;
 		}
-		case 0x0A: {
+		case 0x0A: { // printString
 			std::string x = formatValue();
 			std::string y = formatValue();
 			uint16_t strOffset = readWord();
 			uint16_t numLines = readWord();
-			printf("PRINT_STRING pos=(%s, %s) strOffset=%u numLines=%u\n", x.c_str(), y.c_str(), strOffset, numLines);
+			printf("printString pos=(%s, %s) strOffset=%u numLines=%u\n", x.c_str(), y.c_str(), strOffset, numLines);
 			break;
 		}
-		case 0x0B: {
-			std::string objId = formatObjectId();
+		case 0x0B: { // moveObject
+			std::string obj = formatValue();
 			std::string scene = formatValue();
 			std::string x = formatValue();
 			std::string y = formatValue();
-			printf("MOVE_OBJECT obj=%s toScene=%s pos=(%s, %s)\n", objId.c_str(), scene.c_str(), x.c_str(), y.c_str());
+			printf("moveObject obj=%s scene=%s pos=(%s, %s)\n", obj.c_str(), scene.c_str(), x.c_str(), y.c_str());
 			break;
 		}
-		case 0x0C: {
-			std::string scene = formatValue32();
+		case 0x0C: { // changeScene
+			std::string scene = formatValue();
 			std::string mode = formatValue();
 			std::string speed = formatValue();
-			printf("CHANGE_SCENE scene=%s mode=%s speed=%s\n", scene.c_str(), mode.c_str(), speed.c_str());
+			printf("changeScene scene=%s mode=%s speed=%s\n", scene.c_str(), mode.c_str(), speed.c_str());
 			break;
 		}
-		case 0x0D: {
-			std::string objId = formatObjectId();
+		case 0x0D: { // showDialogue
+			std::string obj = formatValue();
 			std::string x = formatValue();
 			std::string y = formatValue();
 			std::string side = formatValue();
 			uint16_t strOffset = readWord();
 			uint16_t numLines = readWord();
-			printf("DIALOGUE speaker=%s pos=(%s, %s) side=%s strOffset=%u numLines=%u\n",
-				   objId.c_str(), x.c_str(), y.c_str(), side.c_str(), strOffset, numLines);
+			printf("showDialogue obj=%s pos=(%s, %s) side=%s strOffset=%u numLines=%u\n",
+				   obj.c_str(), x.c_str(), y.c_str(), side.c_str(), strOffset, numLines);
 			break;
 		}
-		case 0x0E: {
-			std::string id = formatValue32();
-			std::string frame = formatValue();
-			printf("CHANGE_ANIM id=%s frame=%s\n", id.c_str(), frame.c_str());
+		case 0x0E: { // changeAnim
+			printf("changeAnim\n");
 			break;
 		}
-		case 0x0F: {
-			std::string duration = formatValue();
-			printf("WAIT_FRAMES %s\n", duration.c_str());
+		case 0x0F: { // frameWait
+			std::string val = formatValue();
+			printf("frameWait %s\n", val.c_str());
 			break;
 		}
-		case 0x10: {
-			std::string objId = formatObjectId();
+		case 0x10: { // walkTo
+			std::string obj = formatValue();
 			std::string x = formatValue();
 			std::string y = formatValue();
-			printf("WALK_TO obj=%s pos=(%s, %s)\n", objId.c_str(), x.c_str(), y.c_str());
+			printf("walkTo obj=%s pos=(%s, %s)\n", obj.c_str(), x.c_str(), y.c_str());
 			break;
 		}
-		case 0x11: {
-			std::string objId = formatObjectId();
-			printf("WAIT_WALK obj=%s\n", objId.c_str());
+		case 0x11: { // waitForWalk
+			std::string obj = formatValue();
+			printf("waitForWalk obj=%s\n", obj.c_str());
 			break;
 		}
-		case 0x12: {
+		case 0x12: { // setPathOverride
 			std::string area = formatValue();
 			std::string active = formatValue();
-			std::string override_val = formatValue();
-			printf("SET_PATHFINDING area=%s active=%s override=%s\n", area.c_str(), active.c_str(), override_val.c_str());
+			std::string val = formatValue();
+			printf("setPathOverride area=%s active=%s val=%s\n", area.c_str(), active.c_str(), val.c_str());
 			break;
 		}
-		case 0x13: {
-			uint16_t tag = readWord();
-			printf("GOTO_TAG 0x%04X\n", tag);
+		case 0x13: { // loadAnim
+			printf("loadAnim\n");
 			break;
 		}
-		case 0x14: {
-			uint16_t tag = readWord();
-			printf("TAG 0x%04X\n", tag);
+		case 0x14: { // skipWord
+			uint16_t w = readWord();
+			printf("skipWord 0x%04x\n", w);
 			break;
 		}
-		case 0x15: {
-			printf("DIALOGUE_CHOICES_BEGIN\n");
+		case 0x15: { // clearDialogueChoices
+			printf("clearDialogueChoices\n");
 			break;
 		}
-		case 0x16: {
+		case 0x16: { // addDialogueChoice
 			std::string idx = formatValue();
 			uint16_t strOffset = readWord();
 			uint16_t numLines = readWord();
-			printf("DIALOGUE_CHOICE index=%s strOffset=%u numLines=%u\n", idx.c_str(), strOffset, numLines);
+			printf("addDialogueChoice idx=%s strOffset=%u numLines=%u\n", idx.c_str(), strOffset, numLines);
 			break;
 		}
-		case 0x17: {
-			std::string x = formatValue32();
-			std::string y = formatValue32();
+		case 0x17: { // showDialogueChoice
+			std::string obj = formatValue();
+			std::string x = formatValue();
+			std::string y = formatValue();
 			std::string side = formatValue();
-			printf("DIALOGUE_CHOICES_SHOW pos=(%s, %s) side=%s\n", x.c_str(), y.c_str(), side.c_str());
+			printf("showDialogueChoice obj=%s pos=(%s, %s) side=%s\n", obj.c_str(), x.c_str(), y.c_str(), side.c_str());
 			break;
 		}
-		case 0x18: {
-			printf("END_SCRIPT\n");
+		case 0x18: { // dismissPanel
+			printf("dismissPanel\n");
 			break;
 		}
-		case 0x19: {
-			std::string actor = formatObjectId();
-			std::string target = formatObjectId();
-			printf("PICKUP actor=%s target=%s\n", actor.c_str(), target.c_str());
+		case 0x19: { // walkToAndPickup
+			std::string actor = formatValue();
+			std::string obj = formatValue();
+			printf("walkToAndPickup actor=%s obj=%s\n", actor.c_str(), obj.c_str());
 			break;
 		}
-		case 0x1A: {
-			std::string objId = formatObjectId();
-			std::string val217 = formatValue();
-			std::string val219 = formatValue();
-			printf("SET_OBJECT_RUNTIME obj=%s val217=%s val219=%s\n", objId.c_str(), val217.c_str(), val219.c_str());
+		case 0x1A: { // setPickupFrames
+			std::string obj = formatValue();
+			std::string start = formatValue();
+			std::string end = formatValue();
+			printf("setPickupFrames obj=%s start=%s end=%s\n", obj.c_str(), start.c_str(), end.c_str());
 			break;
 		}
-		case 0x1B: {
-			std::string objId = formatObjectId();
+		case 0x1B: { // setAnimSpeed
+			std::string obj = formatValue();
 			std::string slot = formatValue();
-			std::string val = formatValue();
-			printf("SET_OBJECT_SLOT obj=%s slot=%s value=%s\n", objId.c_str(), slot.c_str(), val.c_str());
+			std::string speed = formatValue();
+			printf("setAnimSpeed obj=%s slot=%s speed=%s\n", obj.c_str(), slot.c_str(), speed.c_str());
 			break;
 		}
-		case 0x1C: {
-			printf("SKIPPABLE_BEGIN\n");
+		case 0x1C: { // setSkippable
+			printf("setSkippable\n");
 			break;
 		}
-		case 0x1D: {
-			printf("SKIPPABLE_END\n");
+		case 0x1D: { // clearSkippable
+			printf("clearSkippable\n");
 			break;
 		}
-		case 0x1E: {
-			std::string objId = formatObjectId();
+		case 0x1E: { // loadAnimBlob
+			std::string obj = formatValue();
 			std::string slot = formatValue();
 			std::string frame = formatValue();
-			printf("PLAY_ANIM obj=%s slot=%s frame=%s\n", objId.c_str(), slot.c_str(), frame.c_str());
+			printf("loadAnimBlob obj=%s slot=%s frame=%s\n", obj.c_str(), slot.c_str(), frame.c_str());
 			break;
 		}
-		case 0x1F: {
-			std::string objId = formatObjectId();
-			std::string x = formatValue32();
-			std::string y = formatValue32();
-			printf("TEST_PATH_WALKABLE obj=%s to=(%s, %s)\n", objId.c_str(), x.c_str(), y.c_str());
+		case 0x1F: { // setPosition
+			std::string obj = formatValue();
+			std::string x = formatValue();
+			std::string y = formatValue();
+			printf("setPosition obj=%s pos=(%s, %s)\n", obj.c_str(), x.c_str(), y.c_str());
 			break;
 		}
-		case 0x20: {
-			std::string objId = formatObjectId();
+		case 0x20: { // setVerticalOffset
+			std::string obj = formatValue();
 			std::string offset = formatValue();
-			printf("SET_Y_OFFSET obj=%s offset=%s\n", objId.c_str(), offset.c_str());
+			printf("setVerticalOffset obj=%s offset=%s\n", obj.c_str(), offset.c_str());
 			break;
 		}
-		case 0x21: {
-			std::string objId = formatObjectId();
+		case 0x21: { // setMotion
+			std::string obj = formatValue();
 			std::string target = formatValue();
 			std::string delta = formatValue();
 			std::string dist = formatValue();
-			printf("SET_MOTION obj=%s targetOffset=%s delta=%s distance=%s\n", objId.c_str(), target.c_str(), delta.c_str(), dist.c_str());
+			printf("setMotion obj=%s target=%s delta=%s dist=%s\n", obj.c_str(), target.c_str(), delta.c_str(), dist.c_str());
 			break;
 		}
-		case 0x22: {
-			std::string objId = formatObjectId();
-			std::string orient = formatValue();
-			printf("SET_ORIENTATION obj=%s orientation=%s\n", objId.c_str(), orient.c_str());
+		case 0x22: { // setAnimIndex
+			std::string obj = formatValue();
+			std::string anim = formatValue();
+			printf("setAnimIndex obj=%s anim=%s\n", obj.c_str(), anim.c_str());
 			break;
 		}
-		case 0x23: {
-			std::string objId = formatObjectId();
-			std::string x = formatValue32();
-			std::string y = formatValue32();
-			std::string targetOffset = formatValue();
-			printf("MOVE_TO_POSITION obj=%s pos=(%s, %s) targetOffset=%s\n", objId.c_str(), x.c_str(), y.c_str(), targetOffset.c_str());
+		case 0x23: { // moveToPosition
+			std::string obj = formatValue();
+			std::string x = formatValue();
+			std::string y = formatValue();
+			std::string voff = formatValue();
+			printf("moveToPosition obj=%s pos=(%s, %s) voff=%s\n", obj.c_str(), x.c_str(), y.c_str(), voff.c_str());
 			break;
 		}
-		case 0x24: {
-			std::string a = formatValue32();
-			std::string b = formatValue32();
-			// The save target is embedded in the first value's position
-			// We can't perfectly reconstruct it without re-reading, so show the operands
-			printf("ADD %s + %s -> (save to first operand var)\n", a.c_str(), b.c_str());
+		case 0x24: { // add
+			std::string a = formatValue();
+			std::string b = formatValue();
+			printf("add %s, %s\n", a.c_str(), b.c_str());
 			break;
 		}
-		case 0x25: {
-			std::string a = formatValue32();
-			std::string b = formatValue32();
-			printf("SUB %s - %s -> (save to first operand var)\n", a.c_str(), b.c_str());
+		case 0x25: { // subtract
+			std::string a = formatValue();
+			std::string b = formatValue();
+			printf("subtract %s, %s\n", a.c_str(), b.c_str());
 			break;
 		}
-		case 0x26: {
-			std::string objId = formatObjectId();
-			std::string val = formatValue();
-			uint8_t animId = readByte();
-			printf("LOAD_SPECIAL_ANIM obj=%s val=%s animId=%u\n", objId.c_str(), val.c_str(), animId);
+		case 0x26: { // setAnimSlot
+			std::string obj = formatValue();
+			formatValue(); // unused
+			uint8_t animIdx = readByte();
+			printf("setAnimSlot obj=%s anim=%u\n", obj.c_str(), animIdx);
 			break;
 		}
-		case 0x27: {
-			std::string charId = formatValue();
-			std::string val = formatValue();
-			printf("SET_DIRECTION charId=%s value=%s\n", charId.c_str(), val.c_str());
+		case 0x27: { // setMaxAnimFrame
+			std::string obj = formatValue();
+			std::string maxFrame = formatValue();
+			printf("setMaxAnimFrame obj=%s maxFrame=%s\n", obj.c_str(), maxFrame.c_str());
 			break;
 		}
-		case 0x28: {
-			std::string objId = formatObjectId();
-			printf("STOP_ANIM obj=%s\n", objId.c_str());
+		case 0x28: { // nop28
+			printf("nop28\n");
 			break;
 		}
-		case 0x29: {
-			std::string objId = formatObjectId();
-			printf("OPEN_INVENTORY source=%s\n", objId.c_str());
+		case 0x29: { // loadSong
+			std::string obj = formatValue();
+			printf("loadSong obj=%s\n", obj.c_str());
 			break;
 		}
-		case 0x2A: {
-			std::string objId = formatObjectId();
+		case 0x2A: { // loadAnimFromScene
+			std::string obj = formatValue();
 			std::string slot = formatValue();
 			std::string decode = formatValue();
-			uint8_t arrayIdx = readByte();
-			printf("LOAD_ANIM obj=%s slot=%s decode=%s arrayIdx=%u\n", objId.c_str(), slot.c_str(), decode.c_str(), arrayIdx);
+			uint8_t idx = readByte();
+			printf("loadAnimFromScene obj=%s slot=%s decode=%s idx=%u\n", obj.c_str(), slot.c_str(), decode.c_str(), idx);
 			break;
 		}
-		case 0x2B: {
-			std::string objId = formatValue();
-			printf("REFRESH_OBJECT obj=%s\n", objId.c_str());
+		case 0x2B: { // setShading
+			std::string obj = formatValue();
+			printf("setShading obj=%s\n", obj.c_str());
 			break;
 		}
-		case 0x2C: {
-			std::string objId = formatValue();
-			std::string parentId = formatValue();
-			printf("CHECK_INVENTORY obj=%s parent=%s\n", objId.c_str(), parentId.c_str());
+		case 0x2C: { // setParent
+			std::string obj = formatValue();
+			std::string val = formatValue();
+			printf("setParent obj=%s val=%s\n", obj.c_str(), val.c_str());
 			break;
 		}
-		case 0x2D: {
-			std::string objId = formatValue();
-			std::string enabled = formatValue();
-			printf("SET_RUNTIME_FLAG obj=%s enabled=%s\n", objId.c_str(), enabled.c_str());
+		case 0x2D: { // setScaling
+			std::string obj = formatValue();
+			std::string val = formatValue();
+			printf("setScaling obj=%s val=%s\n", obj.c_str(), val.c_str());
 			break;
 		}
-		case 0x2E: {
-			std::string animIdx = formatValue32();
-			std::string minFrame = formatValue32();
-			std::string maxFrame = formatValue32();
-			printf("TEST_SCENE_ANIM_FRAME animIdx=%s min=%s max=%s\n", animIdx.c_str(), minFrame.c_str(), maxFrame.c_str());
+		case 0x2E: { // checkAnimRange
+			std::string anim = formatValue();
+			std::string lo = formatValue();
+			std::string hi = formatValue();
+			printf("checkAnimRange anim=%s range=[%s, %s]\n", anim.c_str(), lo.c_str(), hi.c_str());
 			break;
 		}
-		case 0x2F: {
-			std::string objId = formatObjectId();
+		case 0x2F: { // checkBlobRange
+			std::string obj = formatValue();
 			std::string slot = formatValue();
-			std::string minFrame = formatValue();
-			std::string maxFrame = formatValue();
-			printf("TEST_OBJECT_ANIM_FRAME obj=%s slot=%s min=%s max=%s\n", objId.c_str(), slot.c_str(), minFrame.c_str(), maxFrame.c_str());
+			std::string lo = formatValue();
+			std::string hi = formatValue();
+			printf("checkBlobRange obj=%s slot=%s range=[%s, %s]\n", obj.c_str(), slot.c_str(), lo.c_str(), hi.c_str());
 			break;
 		}
-		case 0x30: {
-			std::string x = formatValue();
-			std::string y = formatValue();
-			uint16_t strOffset = readWord();
-			uint16_t numLines = readWord();
-			printf("PRINT_STRING_RIGHT pos=(%s, %s) strOffset=%u numLines=%u\n", x.c_str(), y.c_str(), strOffset, numLines);
+		case 0x30: { // nop30
+			printf("nop30\n");
 			break;
 		}
-		case 0x31: {
+		case 0x31: { // setMusicVolume
 			std::string vol = formatValue();
-			printf("SET_VOLUME %s\n", vol.c_str());
+			printf("setMusicVolume %s\n", vol.c_str());
 			break;
 		}
-		case 0x32: {
-			std::string objId = formatValue();
-			std::string clickable = formatValue();
-			printf("SET_CLICKABLE obj=%s clickable=%s\n", objId.c_str(), clickable.c_str());
+		case 0x32: { // setClickable
+			std::string obj = formatValue();
+			std::string val = formatValue();
+			printf("setClickable obj=%s val=%s\n", obj.c_str(), val.c_str());
 			break;
 		}
-		case 0x33: {
-			std::string objId = formatValue();
-			std::string visible = formatValue();
-			printf("SET_VISIBLE obj=%s visible=%s\n", objId.c_str(), visible.c_str());
+		case 0x33: { // setVisible
+			std::string obj = formatValue();
+			std::string val = formatValue();
+			printf("setVisible obj=%s val=%s\n", obj.c_str(), val.c_str());
 			break;
 		}
-		case 0x34: {
-			std::string v1 = formatValue();
-			std::string v2 = formatValue();
-			printf("SET_HOTSPOT_OVERRIDE %s -> %s\n", v1.c_str(), v2.c_str());
+		case 0x34: { // setHotspotRemap
+			std::string a = formatValue();
+			std::string b = formatValue();
+			printf("setHotspotRemap %s -> %s\n", a.c_str(), b.c_str());
 			break;
 		}
-		case 0x35: {
-			std::string objId = formatValue();
-			std::string otherId = formatValue();
-			std::string val1 = formatValue();
-			std::string val2 = formatValue();
-			std::string val3 = formatValue();
-			printf("SET_BOUNDS_ATTACHMENT obj=%s other=%s v1=%s v2=%s v3=%s\n",
-				   objId.c_str(), otherId.c_str(), val1.c_str(), val2.c_str(), val3.c_str());
+		case 0x35: { // setBoundsAttach
+			std::string obj = formatValue();
+			std::string parent = formatValue();
+			std::string a = formatValue();
+			std::string b = formatValue();
+			std::string c = formatValue();
+			printf("setBoundsAttach obj=%s parent=%s (%s, %s, %s)\n",
+				   obj.c_str(), parent.c_str(), a.c_str(), b.c_str(), c.c_str());
 			break;
 		}
-		case 0x36: {
-			printf("DISMISS_PANEL\n");
+		case 0x36: { // dismissAllPanels
+			printf("dismissAllPanels\n");
 			break;
 		}
-		case 0x37: {
-			printf("RESET_TO_SCENE_SCRIPT\n");
+		case 0x37: { // resetScript
+			printf("resetScript\n");
 			break;
 		}
-		case 0x38: {
+		case 0x38: { // loadOverlayFont
 			uint8_t resIdx = readByte();
-			printf("LOAD_OVERLAY_FONT resource=%u\n", resIdx);
+			printf("loadOverlayFont res=%u\n", resIdx);
 			break;
 		}
-		case 0x39: {
-			printf("END_OVERLAY_TEXT\n");
+		case 0x39: { // endOverlayText
+			printf("endOverlayText\n");
 			break;
 		}
-		case 0x3A: {
+		case 0x3A: { // addOverlayEntry
 			std::string x = formatValue();
 			std::string y = formatValue();
 			std::string align = formatValue();
 			uint16_t strOffset = readWord();
 			uint16_t entryType = readWord();
-			printf("OVERLAY_TEXT_ENTRY pos=(%s, %s) align=%s strOffset=%u type=%u\n",
+			printf("addOverlayEntry pos=(%s, %s) align=%s str=%u type=%u\n",
 				   x.c_str(), y.c_str(), align.c_str(), strOffset, entryType);
 			break;
 		}
-		case 0x3B: {
-			printf("CLEAR_OVERLAY_TEXT\n");
+		case 0x3B: { // clearOverlayEntries
+			printf("clearOverlayEntries\n");
 			break;
 		}
-		case 0x3C: {
+		case 0x3C: { // fadeToBlack
 			std::string speed = formatValue();
-			printf("FADE_TO_BLACK speed=%s\n", speed.c_str());
+			printf("fadeToBlack %s\n", speed.c_str());
 			break;
 		}
-		case 0x3D: {
+		case 0x3D: { // fadeFromBlack
 			std::string speed = formatValue();
-			printf("FADE_IN speed=%s\n", speed.c_str());
+			printf("fadeFromBlack %s\n", speed.c_str());
 			break;
 		}
-		case 0x3E: {
+		case 0x3E: { // loadSoundRes
 			uint8_t resIdx = readByte();
-			printf("LOAD_SOUND resource=%u\n", resIdx);
+			printf("loadSoundRes res=%u\n", resIdx);
 			break;
 		}
-		case 0x3F: {
-			printf("STOP_SOUND\n");
+		case 0x3F: { // clearSound
+			printf("clearSound\n");
 			break;
 		}
-		case 0x40: {
-			printf("PLAY_SOUND\n");
+		case 0x40: { // playSound
+			printf("playSound\n");
 			break;
 		}
-		case 0x41: {
-			printf("WAIT_SOUND\n");
+		case 0x41: { // waitForSound
+			printf("waitForSound\n");
 			break;
 		}
-		case 0x42: {
-			printf("STOP_CURRENT_SOUND\n");
+		case 0x42: { // stopSound
+			printf("stopSound\n");
 			break;
 		}
-		case 0x43: {
+		case 0x43: { // loadMusicSlot
 			std::string slot = formatValue();
 			uint8_t resIdx = readByte();
-			printf("LOAD_MUSIC slot=%s resource=%u\n", slot.c_str(), resIdx);
+			printf("loadMusicSlot slot=%s res=%u\n", slot.c_str(), resIdx);
 			break;
 		}
-		case 0x44: {
+		case 0x44: { // playMusic
 			std::string slot = formatValue();
-			std::string startMuted = formatValue();
-			std::string fadeParam = formatValue();
-			printf("PLAY_MUSIC slot=%s startMuted=%s fade=%s\n", slot.c_str(), startMuted.c_str(), fadeParam.c_str());
+			std::string a = formatValue();
+			std::string b = formatValue();
+			printf("playMusic slot=%s %s %s\n", slot.c_str(), a.c_str(), b.c_str());
 			break;
 		}
-		case 0x45: {
+		case 0x45: { // stopMusic
 			std::string slot = formatValue();
-			std::string immediate = formatValue();
-			std::string fadeParam = formatValue();
-			printf("STOP_MUSIC slot=%s immediate=%s fade=%s\n", slot.c_str(), immediate.c_str(), fadeParam.c_str());
+			std::string a = formatValue();
+			std::string b = formatValue();
+			printf("stopMusic slot=%s %s %s\n", slot.c_str(), a.c_str(), b.c_str());
 			break;
 		}
-		case 0x46: {
+		case 0x46: { // freeMusic
 			std::string slot = formatValue();
-			printf("FREE_MUSIC slot=%s\n", slot.c_str());
+			printf("freeMusic slot=%s\n", slot.c_str());
 			break;
 		}
-		case 0x47: {
-			printf("WAIT_MUSIC\n");
+		case 0x47: { // waitForMusic
+			printf("waitForMusic\n");
 			break;
 		}
-		case 0x48: {
-			std::string objId = formatObjectId();
-			std::string target = formatSaveTarget();
-			printf("GET_OBJECT_X obj=%s -> %s\n", objId.c_str(), target.c_str());
+		case 0x48: { // getObjectX
+			std::string obj = formatValue();
+			printf("getObjectX obj=%s\n", obj.c_str());
 			break;
 		}
-		case 0x49: {
-			std::string objId = formatObjectId();
-			std::string target = formatSaveTarget();
-			printf("GET_OBJECT_Y obj=%s -> %s\n", objId.c_str(), target.c_str());
+		case 0x49: { // getObjectY
+			std::string obj = formatValue();
+			printf("getObjectY obj=%s\n", obj.c_str());
 			break;
 		}
-		case 0x4A: {
-			std::string objId = formatObjectId();
-			std::string target = formatSaveTarget();
-			printf("GET_OBJECT_FIELD obj=%s -> %s\n", objId.c_str(), target.c_str());
+		case 0x4A: { // getObjectField
+			std::string obj = formatValue();
+			printf("getObjectField obj=%s\n", obj.c_str());
 			break;
 		}
-		case 0x4B: {
-			std::string objId = formatObjectId();
-			std::string target = formatSaveTarget();
-			printf("GET_OBJECT_ORIENTATION obj=%s -> %s\n", objId.c_str(), target.c_str());
+		case 0x4B: { // getObjectOrient
+			std::string obj = formatValue();
+			printf("getObjectOrient obj=%s\n", obj.c_str());
 			break;
 		}
-		case 0x4C: {
-			printf("CLEAR_INVENTORY\n");
+		case 0x4C: { // clearActorItems
+			printf("clearActorItems\n");
 			break;
 		}
-		case 0x4D: {
-			std::string src = formatValue();
-			std::string dst = formatValue();
-			printf("SET_AREA_REMAP %s -> %s\n", src.c_str(), dst.c_str());
+		case 0x4D: { // setAreaRemap
+			std::string a = formatValue();
+			std::string b = formatValue();
+			printf("setAreaRemap %s -> %s\n", a.c_str(), b.c_str());
 			break;
 		}
-		case 0x4E: {
-			printf("WAIT_ADLIB_READY\n");
+		case 0x4E: { // waitForAdlib
+			printf("waitForAdlib\n");
 			break;
 		}
 		default: {
-			printf("UNKNOWN_OPCODE 0x%02X (len=%u)\n", opcode, length);
+			printf("??? opcode=0x%02x len=%u\n", opcode, length);
 			break;
 		}
 		}
 
-		// Ensure we advance to the correct position regardless of parsing
 		if (pos != endPos) {
 			pos = endPos;
 		}
@@ -632,23 +574,20 @@ static void disassemble() {
 }
 
 static void printHelp(const char *bin) {
-	printf("MACS2 Script Disassembler\n\n");
+	printf("MACS2 Script Decompiler\n\n");
 	printf("Usage: %s <game_data_file> [scene_index]\n\n", bin);
 	printf("  game_data_file  - The main game data file\n");
-	printf("  scene_index     - Scene number to disassemble (1-based)\n");
-	printf("                    If omitted, disassembles all scenes\n\n");
-	printf("The disassembled script will be written to stdout.\n");
+	printf("  scene_index     - Scene number to decompile (1-based)\n");
+	printf("                    If omitted, decompiles all scenes\n\n");
+	printf("The decompiled script will be written to stdout.\n");
 }
 
 static bool loadSceneScript(FILE *f, uint16_t sceneIndex) {
-	// Calculate offset: sceneIndex * 0xC + 0xC + 0x4 - 0x8
 	uint32_t offset = (uint32_t)sceneIndex * 0xC + 0xC + 0x4 - 0x8;
 	fseek(f, offset, SEEK_SET);
 
 	uint32_t sceneDataOffset;
 	if (fread(&sceneDataOffset, 4, 1, f) != 1) return false;
-	// Little-endian conversion (assuming host is LE for simplicity)
-	// For portability we should do proper byte swapping but this matches the game's target
 
 	fseek(f, sceneDataOffset + 0x80, SEEK_SET);
 
@@ -690,36 +629,30 @@ int main(int argc, char **argv) {
 		startScene = atoi(argv[2]);
 		endScene = startScene;
 	} else {
-		// Try all scenes from 1 to 512
 		startScene = 1;
 		endScene = 512;
 	}
 
 	printf("; MACS2 Script Runtime Variables (type 0xFF, read-only)\n");
 	printf("; These are computed by the engine at read-time, not stored in script data.\n");
-	printf("; $interacted_use         (FF:01) Object ID interacted with via Use/UseInventory cursor\n");
-	printf("; $interacted_look        (FF:02) Object ID interacted with via Look cursor\n");
-	printf("; $interacted_talk        (FF:03) Object ID interacted with via Talk cursor\n");
-	printf("; $char_area              (FF:04) Area ID at protagonist's current position\n");
-	printf("; $repeat_run_flag        (FF:0B) 1 during repeat/object script pass, 0 otherwise\n");
-	printf("; $chosen_dialogue_option (FF:0D) Index of last chosen dialogue option\n");
-	printf("; $path_walkable_result   (FF:23) 1 if last TEST_PATH_WALKABLE succeeded\n");
-	printf("; $actor_x                (FF:24) Protagonist X position\n");
-	printf("; $actor_y                (FF:25) Protagonist Y position\n");
-	printf("; $is_scene_init          (FF:26) 1 during scene initialization pass\n");
-	printf("; $repeat_char_area       (FF:27) Area at char position (only during repeat run)\n");
-	printf("; $inventory_check_result (FF:28) 1 if last CHECK_INVENTORY matched\n");
-	printf("; $anim_range_test_result (FF:29) 1 if last TEST_*_ANIM_FRAME matched\n");
-	printf("; $inventory_combine_flag (FF:2A) 1 if inventory combine pending (no UI open)\n");
-	printf("; $inventory_action_flag  (FF:2B) 1 if inventory action pending (no UI open)\n");
-	printf("; $interacted_panel_use   (FF:2C) Object ID interacted with via PanelUse cursor\n");
-	printf("; $current_scene          (FF:2D) Current scene index\n");
-	printf("; $last_scene             (FF:2F) Previous scene index\n");
-	printf("; $music_enabled          (FF:30) 1 if music enabled and sound system active\n");
-	printf("; $sound_enabled          (FF:31) 1 if sound enabled and sound system active\n");
+	printf("; interactedUse    (FF:01) Object ID interacted with via Use/UseInventory cursor\n");
+	printf("; interactedLook   (FF:02) Object ID interacted with via Look cursor\n");
+	printf("; interactedTalk   (FF:03) Object ID interacted with via Talk cursor\n");
+	printf("; areaAtActor      (FF:04) Area ID at protagonist's current position\n");
+	printf("; isRepeatRun      (FF:0B) 1 during repeat/object script pass, 0 otherwise\n");
+	printf("; dialogueResult   (FF:0D) Index of last chosen dialogue option\n");
+	printf("; pathWalkable     (FF:23) 1 if last setPosition path test succeeded\n");
+	printf("; actorX           (FF:24) Protagonist X position\n");
+	printf("; actorY           (FF:25) Protagonist Y position\n");
+	printf("; isSceneInit      (FF:26) 1 during scene initialization pass\n");
+	printf("; invCheck         (FF:28) 1 if last setParent inventory check matched\n");
+	printf("; invCombine       (FF:2A) 1 if inventory combine pending (no UI open)\n");
+	printf("; invAction        (FF:2B) 1 if inventory action pending (no UI open)\n");
+	printf("; curScene         (FF:2D) Current scene index\n");
+	printf("; prevScene        (FF:2F) Previous scene index\n");
 	printf(";\n");
-	printf("; Script variables: var[1]..var[2048] (read/write, all zeroed on scene load)\n");
-	printf("; Object IDs: raw value - 0x400 = object index (1..512)\n");
+	printf("; Script variables: var[N] (read/write, all zeroed on scene load)\n");
+	printf("; Object IDs in moveObject/etc.: raw value - 0x400 = object index\n");
 	printf(";\n\n");
 
 	for (int scene = startScene; scene <= endScene; scene++) {



From noreply at scummvm.org  Mon Jun  1 07:44:05 2026
From: noreply at scummvm.org (neuromancer)
Date: Mon,  1 Jun 2026 07:44:05 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 e44a371ebd858f7fb84d427b2c478bafefd1fa4e
Message-ID: <20260601074405.D0ECB700591@ron.scummvm.net>

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

Summary:
892b4a95d5 SCUMM: RA1: clean-up of debug statements
bc9e07470d SCUMM: RA2: clean-up of debug statements
f046a35f78 SCUMM: RA2: removed expensive non-zero pixel counter used for debug
a36e9516b1 SCUMM: RA1: use DEBUG_INSANE for debug statements
912e8b3bf4 SCUMM: RA: improve debug statements with class/function names
e44a371ebd SCUMM: RA: removed some redundant/useless debug statement


Commit: 892b4a95d5e9735999dd87ded09d3f83a2a51a54
    https://github.com/scummvm/scummvm/commit/892b4a95d5e9735999dd87ded09d3f83a2a51a54
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-01T08:35:35+02:00

Commit Message:
SCUMM: RA1: clean-up of debug statements

Changed paths:
    engines/scumm/insane/rebel1/audio.cpp
    engines/scumm/insane/rebel1/iact.cpp
    engines/scumm/insane/rebel1/menu.cpp
    engines/scumm/insane/rebel1/render.cpp
    engines/scumm/smush/rebel/smush_player_ra1.cpp


diff --git a/engines/scumm/insane/rebel1/audio.cpp b/engines/scumm/insane/rebel1/audio.cpp
index bb807f9df2d..64cc5e095f2 100644
--- a/engines/scumm/insane/rebel1/audio.cpp
+++ b/engines/scumm/insane/rebel1/audio.cpp
@@ -75,14 +75,14 @@ void InsaneRebel1::loadSfx() {
 		ScummFile *file = _vm->instantiateScummFile();
 		_vm->openFile(*file, kRA1SfxFiles[i]);
 		if (!file->isOpen()) {
-			debug("InsaneRebel1::loadSfx: could not open %s", kRA1SfxFiles[i]);
+			debugC(DEBUG_INSANE, "InsaneRebel1::loadSfx: could not open %s", kRA1SfxFiles[i]);
 			delete file;
 			continue;
 		}
 
 		const uint32 fileSize = file->size();
 		if (fileSize < 16) {
-			debug("InsaneRebel1::loadSfx: %s too small (%u bytes)", kRA1SfxFiles[i], fileSize);
+			debugC(DEBUG_INSANE, "InsaneRebel1::loadSfx: %s too small (%u bytes)", kRA1SfxFiles[i], fileSize);
 			file->close();
 			delete file;
 			continue;
@@ -90,7 +90,7 @@ void InsaneRebel1::loadSfx() {
 
 		const uint32 tag = file->readUint32BE();
 		if (tag != MKTAG('S', 'A', 'U', 'D')) {
-			debug("InsaneRebel1::loadSfx: %s not a SAUD file (tag=0x%08x)", kRA1SfxFiles[i], tag);
+			debugC(DEBUG_INSANE, "InsaneRebel1::loadSfx: %s not a SAUD file (tag=0x%08x)", kRA1SfxFiles[i], tag);
 			file->close();
 			delete file;
 			continue;
@@ -108,7 +108,7 @@ void InsaneRebel1::loadSfx() {
 					file->read(pcm, pcmSize);
 					_sfxData[i] = pcm;
 					_sfxSize[i] = pcmSize;
-					debug("InsaneRebel1::loadSfx: loaded %s (%u bytes PCM)", kRA1SfxFiles[i], pcmSize);
+					debugC(DEBUG_INSANE, "InsaneRebel1::loadSfx: loaded %s (%u bytes PCM)", kRA1SfxFiles[i], pcmSize);
 				}
 				foundSdat = true;
 				break;
@@ -117,7 +117,7 @@ void InsaneRebel1::loadSfx() {
 		}
 
 		if (!foundSdat)
-			debug("InsaneRebel1::loadSfx: no SDAT chunk in %s", kRA1SfxFiles[i]);
+			debugC(DEBUG_INSANE, "InsaneRebel1::loadSfx: no SDAT chunk in %s", kRA1SfxFiles[i]);
 
 		file->close();
 		delete file;
diff --git a/engines/scumm/insane/rebel1/iact.cpp b/engines/scumm/insane/rebel1/iact.cpp
index c89961c50a4..a6f94915ecb 100644
--- a/engines/scumm/insane/rebel1/iact.cpp
+++ b/engines/scumm/insane/rebel1/iact.cpp
@@ -781,11 +781,9 @@ void InsaneRebel1::updateFlightVariantCursor() {
 	_flightAimY = CLIP<int32>(shipBaseY + ((liftTerm * kRA1Op09AimYScale[bucket]) >> 8),
 		kRA1MinY, kRA1MaxY);
 
-	if (_currentLevel == 4) {
-		debug(1, "RA1 op09 cursor: frame=%d shipBase=(%d,%d) shipPos=(%d,%d) aim=(%d,%d) roll=%d lift=%d bucket=%d dir=%d persp=(%d,%d)",
-			_gameCounter, shipBaseX, shipBaseY, _shipPosX, _shipPosY, _flightAimX, _flightAimY,
-			_rollAccum, _liftSmooth, bucket, _shipDirIndex, _perspectiveX, _perspectiveY);
-	}
+	debugC(DEBUG_INSANE, "RA1 op09 cursor: frame=%d shipBase=(%d,%d) shipPos=(%d,%d) aim=(%d,%d) roll=%d lift=%d bucket=%d dir=%d persp=(%d,%d)",
+		_gameCounter, shipBaseX, shipBaseY, _shipPosX, _shipPosY, _flightAimX, _flightAimY,
+		_rollAccum, _liftSmooth, bucket, _shipDirIndex, _perspectiveX, _perspectiveY);
 }
 
 // preprocessMouseAxes ??? FUN_231BE (0x231BE) centered-axis output law, adapted to
@@ -1132,7 +1130,7 @@ void InsaneRebel1::updateShipPhysics() {
 	if (_shipBank.numSprites > 0)
 		_shipDirIndex = CLIP<int16>((int16)(vComponent + hComponent), 0, _shipBank.numSprites - 1);
 
-	debug(1, "RA1 ship input: frame=%d source=%s controls=%s turbulence=%d usedJoystick=%d raw=(%d,%d) clipped=(%d,%d) storedAxis=(%d,%d) actionState(L,R,U,D)=(%d,%d,%d,%d) roll=%d lift=%d pos=(%d,%d) view=(%d,%d) dir=%d level=%d mode=%d opcode=0x%X",
+	debugC(DEBUG_INSANE, "RA1 ship input: frame=%d source=%s controls=%s turbulence=%d usedJoystick=%d raw=(%d,%d) clipped=(%d,%d) storedAxis=(%d,%d) actionState(L,R,U,D)=(%d,%d,%d,%d) roll=%d lift=%d pos=(%d,%d) view=(%d,%d) dir=%d level=%d mode=%d opcode=0x%X",
 		_gameCounter, inputSourceName,
 		_optEnhancedControls ? "enhanced" : "original", originalTurbulence, usedJoystick,
 		rawInputX, rawInputY, inputX, inputY,
@@ -1373,7 +1371,7 @@ void InsaneRebel1::updateTurretPhysics() {
 			inputY /= 2;
 		}
 
-		debug("RA1 turret input: source=%s controls=%s mouse=(%d,%d) actions(L,R,U,D)=(%d,%d,%d,%d) raw=(%d,%d) final=(%d,%d) level=%d mode=%d opcode=0x%X",
+		debugC(DEBUG_INSANE, "RA1 turret input: source=%s controls=%s mouse=(%d,%d) actions(L,R,U,D)=(%d,%d,%d,%d) raw=(%d,%d) final=(%d,%d) level=%d mode=%d opcode=0x%X",
 			usedJoystick ? "joystick-actions" : "mouse-path",
 			_optEnhancedControls ? "enhanced" : "original",
 			_vm->_mouse.x, _vm->_mouse.y,
@@ -1621,7 +1619,7 @@ void InsaneRebel1::updateGameOp0BPhysics() {
 	}
 	_inputAxisDeltaX = inputX;
 
-	debug("RA1 GAME 0x0B input: frame=%d source=%s controls=%s window=%d view=(%d,%d) health=%d prevFlags=0x%02x axis=(%d,%d) mouse=(%d,%d) actions(L,R,U,D)=(%d,%d,%d,%d) raw=(%d,%d) final=(%d,%d) level=%d opcode=0x%X",
+	debugC(DEBUG_INSANE, "RA1 GAME 0x0B input: frame=%d source=%s controls=%s window=%d view=(%d,%d) health=%d prevFlags=0x%02x axis=(%d,%d) mouse=(%d,%d) actions(L,R,U,D)=(%d,%d,%d,%d) raw=(%d,%d) final=(%d,%d) level=%d opcode=0x%X",
 		_gameCounter,
 		inputSourceName,
 		_optEnhancedControls ? "enhanced" : "original",
@@ -2260,8 +2258,8 @@ void InsaneRebel1::handleGameCounterOpcode(uint32 opcode, int32 subSize, Common:
 		uint32 param2 = b.readUint32BE();
 		uint32 param3 = b.readUint32BE();
 		uint32 param4 = b.readUint32BE();
-		if (opcode == 0x09 && _currentLevel == 4) {
-			debug(1, "RA1 GAME 0x09: counter=%d params=(%d,%d,%d) opcodeMask=0x%08x",
+		if (opcode == 0x09) {
+			debugC(DEBUG_INSANE, "RA1 GAME 0x09: counter=%d params=(%d,%d,%d) opcodeMask=0x%08x",
 				_gameCounter, param2, param3, param4, _frameGameOpcodeMask);
 		} else {
 			debug(5, "RA1 GAME 0x%02x: counter=%d params=(%d,%d,%d)",
@@ -2477,7 +2475,7 @@ void InsaneRebel1::checkTargetHit(int16 targetIdx, int16 left, int16 top, int16
 						_killCount++;
 						applyFrameObjectHitState(targetIdx);
 
-						if (_currentLevel == 7) {
+						if (debugChannelSet(-1, DEBUG_INSANE)) {
 							Common::String damagedState;
 							Common::String hiddenState;
 							for (int objectId = 20; objectId <= 43; objectId++) {
@@ -2498,7 +2496,7 @@ void InsaneRebel1::checkTargetHit(int16 targetIdx, int16 left, int16 top, int16
 									damagedState += Common::String::format("%d,", objectId);
 							}
 
-							debug(1, "RA1 L8 armor: hitObject=%d damaged=[%s] hidden=[%s]",
+							debugC(DEBUG_INSANE, "RA1 L8 armor: hitObject=%d damaged=[%s] hidden=[%s]",
 								targetIdx + 1, damagedState.c_str(), hiddenState.c_str());
 						}
 
diff --git a/engines/scumm/insane/rebel1/menu.cpp b/engines/scumm/insane/rebel1/menu.cpp
index 43f7c341a06..bcd73aff0de 100644
--- a/engines/scumm/insane/rebel1/menu.cpp
+++ b/engines/scumm/insane/rebel1/menu.cpp
@@ -509,7 +509,7 @@ bool InsaneRebel1::notifyEvent(const Common::Event &event) {
 
 	if (event.type == Common::EVENT_JOYAXIS_MOTION) {
 		_lastJoystickAxisEventTime = _vm->_system->getMillis();
-		debug(1, "RA1 input raw-joy-axis: axis=%d pos=%d menu=%d gameplay=%d storedAxis=(%d,%d)",
+		debugC(DEBUG_INSANE, "RA1 input raw-joy-axis: axis=%d pos=%d menu=%d gameplay=%d storedAxis=(%d,%d)",
 			event.joystick.axis, event.joystick.position,
 			_menuActive, _interactiveVideoActive && !_menuActive,
 			_joystickAxisX, _joystickAxisY);
@@ -532,12 +532,12 @@ bool InsaneRebel1::notifyEvent(const Common::Event &event) {
 		switch (event.customType) {
 		case kScummBackendActionRebel1AxisUp:
 			if (event.joystick.position == 0 && _joystickAxisY > 0) {
-				debug(1, "RA1 input mapped-axis ignored-reset: %s pos=0 current=(%d,%d)",
+				debugC(DEBUG_INSANE, "RA1 input mapped-axis ignored-reset: %s pos=0 current=(%d,%d)",
 					getRebel1BackendAxisName(event.customType), _joystickAxisX, _joystickAxisY);
 				return true;
 			}
 			_joystickAxisY = -axisPosition;
-			debug(1, "RA1 input mapped-axis: %s pos=%d rawPos=%d old=(%d,%d) new=(%d,%d) menu=%d gameplay=%d",
+			debugC(DEBUG_INSANE, "RA1 input mapped-axis: %s pos=%d rawPos=%d old=(%d,%d) new=(%d,%d) menu=%d gameplay=%d",
 				getRebel1BackendAxisName(event.customType), axisPosition, event.joystick.position,
 				oldAxisX, oldAxisY, _joystickAxisX, _joystickAxisY,
 				_menuActive, _interactiveVideoActive && !_menuActive);
@@ -546,12 +546,12 @@ bool InsaneRebel1::notifyEvent(const Common::Event &event) {
 			return true;
 		case kScummBackendActionRebel1AxisDown:
 			if (event.joystick.position == 0 && _joystickAxisY < 0) {
-				debug(1, "RA1 input mapped-axis ignored-reset: %s pos=0 current=(%d,%d)",
+				debugC(DEBUG_INSANE, "RA1 input mapped-axis ignored-reset: %s pos=0 current=(%d,%d)",
 					getRebel1BackendAxisName(event.customType), _joystickAxisX, _joystickAxisY);
 				return true;
 			}
 			_joystickAxisY = axisPosition;
-			debug(1, "RA1 input mapped-axis: %s pos=%d rawPos=%d old=(%d,%d) new=(%d,%d) menu=%d gameplay=%d",
+			debugC(DEBUG_INSANE, "RA1 input mapped-axis: %s pos=%d rawPos=%d old=(%d,%d) new=(%d,%d) menu=%d gameplay=%d",
 				getRebel1BackendAxisName(event.customType), axisPosition, event.joystick.position,
 				oldAxisX, oldAxisY, _joystickAxisX, _joystickAxisY,
 				_menuActive, _interactiveVideoActive && !_menuActive);
@@ -560,12 +560,12 @@ bool InsaneRebel1::notifyEvent(const Common::Event &event) {
 			return true;
 		case kScummBackendActionRebel1AxisLeft:
 			if (event.joystick.position == 0 && _joystickAxisX > 0) {
-				debug(1, "RA1 input mapped-axis ignored-reset: %s pos=0 current=(%d,%d)",
+				debugC(DEBUG_INSANE, "RA1 input mapped-axis ignored-reset: %s pos=0 current=(%d,%d)",
 					getRebel1BackendAxisName(event.customType), _joystickAxisX, _joystickAxisY);
 				return true;
 			}
 			_joystickAxisX = -axisPosition;
-			debug(1, "RA1 input mapped-axis: %s pos=%d rawPos=%d old=(%d,%d) new=(%d,%d) menu=%d gameplay=%d",
+			debugC(DEBUG_INSANE, "RA1 input mapped-axis: %s pos=%d rawPos=%d old=(%d,%d) new=(%d,%d) menu=%d gameplay=%d",
 				getRebel1BackendAxisName(event.customType), axisPosition, event.joystick.position,
 				oldAxisX, oldAxisY, _joystickAxisX, _joystickAxisY,
 				_menuActive, _interactiveVideoActive && !_menuActive);
@@ -574,12 +574,12 @@ bool InsaneRebel1::notifyEvent(const Common::Event &event) {
 			return true;
 		case kScummBackendActionRebel1AxisRight:
 			if (event.joystick.position == 0 && _joystickAxisX < 0) {
-				debug(1, "RA1 input mapped-axis ignored-reset: %s pos=0 current=(%d,%d)",
+				debugC(DEBUG_INSANE, "RA1 input mapped-axis ignored-reset: %s pos=0 current=(%d,%d)",
 					getRebel1BackendAxisName(event.customType), _joystickAxisX, _joystickAxisY);
 				return true;
 			}
 			_joystickAxisX = axisPosition;
-			debug(1, "RA1 input mapped-axis: %s pos=%d rawPos=%d old=(%d,%d) new=(%d,%d) menu=%d gameplay=%d",
+			debugC(DEBUG_INSANE, "RA1 input mapped-axis: %s pos=%d rawPos=%d old=(%d,%d) new=(%d,%d) menu=%d gameplay=%d",
 				getRebel1BackendAxisName(event.customType), axisPosition, event.joystick.position,
 				oldAxisX, oldAxisY, _joystickAxisX, _joystickAxisY,
 				_menuActive, _interactiveVideoActive && !_menuActive);
@@ -676,7 +676,7 @@ bool InsaneRebel1::notifyEvent(const Common::Event &event) {
 				return true;
 			}
 
-			debug("Rebel1: ESC pressed during gameplay - opening ScummVM menu");
+			debugC(DEBUG_INSANE, "Rebel1: ESC pressed during gameplay - opening ScummVM menu");
 			const bool wasPaused = _player->_paused;
 			if (!wasPaused)
 				_player->pause();
@@ -689,13 +689,13 @@ bool InsaneRebel1::notifyEvent(const Common::Event &event) {
 		if (_interactiveVideoActive && !_menuActive &&
 			event.kbd.keycode == Common::KEYCODE_s &&
 			event.kbd.hasFlags(Common::KBD_SHIFT)) {
-			debug("Rebel1: Shift+S pressed - skipping gameplay section");
+			debugC(DEBUG_INSANE, "Rebel1: Shift+S pressed - skipping gameplay section");
 			_vm->_smushVideoShouldFinish = true;
 			return true;
 		}
 
 		if (!_interactiveVideoActive && event.kbd.keycode == Common::KEYCODE_ESCAPE) {
-			debug("Rebel1: ESC pressed - skipping cinematic");
+			debugC(DEBUG_INSANE, "Rebel1: ESC pressed - skipping cinematic");
 			_vm->_smushVideoShouldFinish = true;
 			return true;
 		}
diff --git a/engines/scumm/insane/rebel1/render.cpp b/engines/scumm/insane/rebel1/render.cpp
index c5397079d76..ae7f90294a6 100644
--- a/engines/scumm/insane/rebel1/render.cpp
+++ b/engines/scumm/insane/rebel1/render.cpp
@@ -1254,12 +1254,10 @@ void InsaneRebel1::renderLaserShots(byte *dst, int pitch, int width, int height)
 				const int start1Y = shipBaseY + emit.y1;
 				const int start2X = shipBaseX + emit.x2;
 				const int start2Y = shipBaseY + emit.y2;
-				if (_currentLevel == 4) {
-					debug(1, "RA1 op09 shotRender: frame=%d timer=%d shipBase=(%d,%d) target=(%d,%d) emit1=(%d,%d) emit2=(%d,%d) dir=%d variant=%d mode=%d",
-						_gameCounter, timer, shipBaseX, shipBaseY, targetX, targetY,
-						start1X, start1Y, start2X, start2Y, _shipDirIndex,
-						_shotSlots[i].variant, _flyControlMode);
-				}
+				debugC(DEBUG_INSANE, "RA1 op09 shotRender: frame=%d timer=%d shipBase=(%d,%d) target=(%d,%d) emit1=(%d,%d) emit2=(%d,%d) dir=%d variant=%d mode=%d",
+					_gameCounter, timer, shipBaseX, shipBaseY, targetX, targetY,
+					start1X, start1Y, start2X, start2Y, _shipDirIndex,
+					_shotSlots[i].variant, _flyControlMode);
 
 				renderAimedShotPair(dst, pitch, width, height, _laserBank,
 					start1X, start1Y, start2X, start2Y, targetX, targetY, lerp);
diff --git a/engines/scumm/smush/rebel/smush_player_ra1.cpp b/engines/scumm/smush/rebel/smush_player_ra1.cpp
index d04d673155f..de6aad330bf 100644
--- a/engines/scumm/smush/rebel/smush_player_ra1.cpp
+++ b/engines/scumm/smush/rebel/smush_player_ra1.cpp
@@ -260,13 +260,13 @@ bool SmushPlayerRebel1::handleGameFetch(int32 subSize, Common::SeekableReadStrea
 			}
 		}
 
-		debug("RA1 FTCH: frame=%d id=0x%08x pos=(%d,%d) using stored FOBJ codec=%d size=%dx%d",
+		debugC(DEBUG_SMUSH, "RA1 FTCH: frame=%d id=0x%08x pos=(%d,%d) using stored FOBJ codec=%d size=%dx%d",
 			_frame, fetchId, left, top, storedCodec, _storedFobjWidth, _storedFobjHeight);
 		decodeFrameObject(storedCodec, _storedFobjData, left, top,
 			_storedFobjWidth, _storedFobjHeight, _storedFobjDataSize,
 			storedParam, _storedFobjParm2);
 	} else {
-		debug("RA1 FTCH: frame=%d id=0x%08x with no stored FOBJ data", _frame, fetchId);
+		debugC(DEBUG_SMUSH, "RA1 FTCH: frame=%d id=0x%08x with no stored FOBJ data", _frame, fetchId);
 	}
 
 	return true;
@@ -283,7 +283,7 @@ void SmushPlayerRebel1::ra1HandleGost(int32 subSize, Common::SeekableReadStream
 	const int32 ghostY = b.readSint32BE();
 
 	if (!_hasFrameFobjForGost || _lastFobjData == nullptr || _lastFobjDataSize <= 0) {
-		debug("RA1 GOST: frame=%d ignored type=0x%08x pos=(%d,%d) (no current-frame FOBJ cached)",
+		debugC(DEBUG_SMUSH, "RA1 GOST: frame=%d ignored type=0x%08x pos=(%d,%d) (no current-frame FOBJ cached)",
 			_frame, ghostType, ghostX, ghostY);
 		return;
 	}
@@ -300,12 +300,12 @@ void SmushPlayerRebel1::ra1HandleGost(int32 subSize, Common::SeekableReadStream
 		priorityFlags = 0x6000;
 		break;
 	default:
-		debug("RA1 GOST: frame=%d ignored unknown type=0x%08x pos=(%d,%d)",
+		debugC(DEBUG_SMUSH, "RA1 GOST: frame=%d ignored unknown type=0x%08x pos=(%d,%d)",
 			_frame, ghostType, ghostX, ghostY);
 		return;
 	}
 
-	debug("RA1 GOST: frame=%d type=0x%08x flags=0x%04x pos=(%d,%d) size=%dx%d codec=%d",
+	debugC(DEBUG_SMUSH, "RA1 GOST: frame=%d type=0x%08x flags=0x%04x pos=(%d,%d) size=%dx%d codec=%d",
 		_frame, ghostType, priorityFlags, ghostX, ghostY,
 		_lastFobjWidth, _lastFobjHeight, _lastFobjCodec);
 
@@ -593,7 +593,7 @@ bool SmushPlayerRebel1::handleGameStoreFrame() {
 }
 
 void SmushPlayerRebel1::handleGameFrameObjectPre(int codec, int left, int top, int width, int height, int dataSize) {
-	debug("RA1 FOBJ: frame=%d codec=%d pos=(%d,%d) size=%dx%d dataSize=%d storeFrame=%d",
+	debugC(DEBUG_SMUSH, "RA1 FOBJ: frame=%d codec=%d pos=(%d,%d) size=%dx%d dataSize=%d storeFrame=%d",
 		_frame, codec, left, top, width, height, dataSize, _storeFrame);
 }
 


Commit: bc9e07470d7f2a02e390caf1530cde95cc700122
    https://github.com/scummvm/scummvm/commit/bc9e07470d7f2a02e390caf1530cde95cc700122
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-01T08:39:25+02:00

Commit Message:
SCUMM: RA2: clean-up of debug statements

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 fe8165bde42..0d6a8b0e663 100644
--- a/engines/scumm/smush/rebel/smush_player_ra2.cpp
+++ b/engines/scumm/smush/rebel/smush_player_ra2.cpp
@@ -122,12 +122,14 @@ void SmushPlayerRebel2::initGameVideoState() {
 			memset(_dst, 0, vs->w * vs->h);
 		} else {
 			// Gameplay mode (flags 0x28) - do nothing, preserve existing screen content
-			int nonZero = 0;
-			for (int i = 0; i < vs->w * vs->h; i++) {
-				if (_dst[i] != 0) nonZero++;
+			if (debugChannelSet(-1, DEBUG_SMUSH)) {
+				int nonZero = 0;
+				for (int i = 0; i < vs->w * vs->h; i++) {
+					if (_dst[i] != 0) nonZero++;
+				}
+				debugC(DEBUG_SMUSH, "SmushPlayer::init: Preserving screen for gameplay video (%dx%d, %d%% non-zero)",
+					vs->w, vs->h, (nonZero * 100) / (vs->w * vs->h));
 			}
-			debug("SmushPlayer::init: Preserving screen for gameplay video (%dx%d, %d%% non-zero)",
-				vs->w, vs->h, (nonZero * 100) / (vs->w * vs->h));
 		}
 	}
 }
@@ -159,7 +161,7 @@ bool SmushPlayerRebel2::handleGameFetch(int32 subSize, Common::SeekableReadStrea
 	int16 ftchX = b.readSint16LE();
 	int16 ftchY = b.readSint16LE();
 
-	debug("SmushPlayer::handleFetch: frame=%d unknown=%d x=%d y=%d",
+	debugC(DEBUG_SMUSH, "SmushPlayer::handleFetch: frame=%d unknown=%d x=%d y=%d",
 		_frame, ftchUnknown, ftchX, ftchY);
 
 	// For Handler 25, skip FTCH because the frame buffer only contains the
@@ -169,14 +171,14 @@ bool SmushPlayerRebel2::handleGameFetch(int32 subSize, Common::SeekableReadStrea
 		InsaneRebel2 *rebel2 = static_cast<InsaneRebel2 *>(_insane);
 		int handler = rebel2->getHandler();
 		if (handler == 25) {
-			debug("SmushPlayer::handleFetch: Skipping FTCH for Handler 25 - preserving overlays");
+			debugC(DEBUG_SMUSH, "SmushPlayer::handleFetch: Skipping FTCH for Handler 25 - preserving overlays");
 			return true;
 		}
 	}
 
 	// Re-decode stored FOBJ data with current offsets (matching original FUN_004246d0).
 	if (_storedFobjData != nullptr) {
-		debug("SmushPlayer FTCH: Re-decoding stored FOBJ codec=%d pos=(%d,%d) size=%dx%d dataSize=%d",
+		debugC(DEBUG_SMUSH, "SmushPlayer FTCH: Re-decoding stored FOBJ codec=%d pos=(%d,%d) size=%dx%d dataSize=%d",
 			_storedFobjCodec, _storedFobjLeft, _storedFobjTop,
 			_storedFobjWidth, _storedFobjHeight, _storedFobjDataSize);
 		decodeFrameObject(_storedFobjCodec, _storedFobjData,
@@ -184,7 +186,7 @@ bool SmushPlayerRebel2::handleGameFetch(int32 subSize, Common::SeekableReadStrea
 			_storedFobjWidth, _storedFobjHeight,
 			_storedFobjDataSize);
 	} else {
-		debug("SmushPlayer FTCH: No stored FOBJ data! (frame=%d)", _frame);
+		debugC(DEBUG_SMUSH, "SmushPlayer FTCH: No stored FOBJ data! (frame=%d)", _frame);
 	}
 
 	return true;
@@ -399,7 +401,7 @@ bool SmushPlayerRebel2::handleGameAnimHeader(byte *headerContent) {
 	if (width == 0 && height == 0) {
 		_width = _vm->_screenWidth;
 		_height = _vm->_screenHeight;
-		debug("SmushPlayer::handleAnimHeader: RA2 AHDR has 0x0 dims - using screen size %dx%d", _width, _height);
+		debugC(DEBUG_SMUSH, "SmushPlayer::handleAnimHeader: RA2 AHDR has 0x0 dims - using screen size %dx%d", _width, _height);
 	} else {
 		_width = width;
 		_height = height;
@@ -497,7 +499,7 @@ void SmushPlayerRebel2::ra2HandleTextResource(const char *str, int fontId, int c
 	ensureMultiFont();
 	_multiFont->setDefaultFont(fontId);
 
-	debug("SmushPlayer::handleTextResource: RA2 TRES frame=%d fontId=%d color=%d flags=0x%x flg=%d pos=(%d,%d) clip=(%d,%d,%d,%d) str=\"%.40s\"",
+	debugC(DEBUG_SMUSH, "SmushPlayer::handleTextResource: RA2 TRES frame=%d fontId=%d color=%d flags=0x%x flg=%d pos=(%d,%d) clip=(%d,%d,%d,%d) str=\"%.40s\"",
 		  _frame, fontId, color, (int)flg, (int)flg, pos_x, pos_y, left, top, width, height, str);
 
 	if (flg & kStyleWordWrap) {
@@ -528,24 +530,24 @@ void SmushPlayerRebel2::ra2SelectFrameBuffer(int width, int height) {
 			_height = height;
 			// Zero-fill the new buffer to avoid garbage in areas not written by FOBJ codec
 			memset(_specialBuffer, 0, bufSize);
-			debug("SmushPlayer: Allocated new _specialBuffer %dx%d (%d bytes)", width, height, bufSize);
+			debugC(DEBUG_SMUSH, "SmushPlayer: Allocated new _specialBuffer %dx%d (%d bytes)", width, height, bufSize);
 		}
 	}
 
 	if (bufSize > _vm->_screenWidth * _vm->_screenHeight &&
 	    _specialBuffer != nullptr && _specialBufferSize >= bufSize) {
 		_dst = _specialBuffer;
-		debug("SmushPlayer: Using _specialBuffer for oversized FOBJ %dx%d", width, height);
+		debugC(DEBUG_SMUSH, "SmushPlayer: Using _specialBuffer for oversized FOBJ %dx%d", width, height);
 	} else {
 		if (_specialBuffer == nullptr) {
 			VirtScreen *vs = &_vm->_virtscr[kMainVirtScreen];
 			_dst = vs->getPixels(0, 0);
-			debug("SmushPlayer: Reset _dst to virtual screen for FOBJ %dx%d at (%d,%d) _dst=%p",
+			debugC(DEBUG_SMUSH, "SmushPlayer: Reset _dst to virtual screen for FOBJ %dx%d at (%d,%d) _dst=%p",
 				width, height, 0, 0, (void*)_dst);
 		} else {
 			// Large frame was in this video, use _specialBuffer for compositing
 			_dst = _specialBuffer;
-			debug("SmushPlayer: Using _specialBuffer for small FOBJ %dx%d (compositing with large frame)",
+			debugC(DEBUG_SMUSH, "SmushPlayer: Using _specialBuffer for small FOBJ %dx%d (compositing with large frame)",
 				width, height);
 		}
 	}
@@ -612,7 +614,7 @@ void SmushPlayerRebel2::ra2HandleGost(int32 subSize, Common::SeekableReadStream
 	int16 ghostY = b.readSint16LE();
 
 	if (!_hasFrameFobjForGost || _lastFobjData == nullptr || _lastFobjDataSize <= 0) {
-		debug("SmushPlayer GOST: frame=%d ignored (no current-frame FOBJ cached)", _frame);
+		debugC(DEBUG_SMUSH, "SmushPlayer GOST: frame=%d ignored (no current-frame FOBJ cached)", _frame);
 		return;
 	}
 
@@ -630,7 +632,7 @@ void SmushPlayerRebel2::ra2HandleGost(int32 subSize, Common::SeekableReadStream
 	int left = _lastFobjLeft + ghostX;
 	int top = _lastFobjTop + ghostY;
 
-	debug("SmushPlayer GOST: frame=%d type=%d flags=0x%04x gostPos=(%d,%d) basePos=(%d,%d) finalPos=(%d,%d) size=%dx%d codec=%d",
+	debugC(DEBUG_SMUSH, "SmushPlayer GOST: frame=%d type=%d flags=0x%04x gostPos=(%d,%d) basePos=(%d,%d) finalPos=(%d,%d) size=%dx%d codec=%d",
 		_frame, ghostType, priorityFlags, ghostX, ghostY,
 		_lastFobjLeft, _lastFobjTop, left, top,
 		_lastFobjWidth, _lastFobjHeight, _lastFobjCodec);
@@ -698,7 +700,7 @@ bool SmushPlayerRebel2::handleGameStoreFrame() {
 }
 
 void SmushPlayerRebel2::handleGameFrameObjectPre(int codec, int left, int top, int width, int height, int dataSize) {
-	debug("SmushPlayer FOBJ: frame=%d codec=%d pos=(%d,%d) size=%dx%d dataSize=%d storeFrame=%d _width=%d _height=%d",
+	debugC(DEBUG_SMUSH, "SmushPlayer FOBJ: frame=%d codec=%d pos=(%d,%d) size=%dx%d dataSize=%d storeFrame=%d _width=%d _height=%d",
 		_frame, codec, left, top, width, height, dataSize, _storeFrame, _width, _height);
 }
 


Commit: f046a35f7809212b8b895e7b38e5af505f82bb96
    https://github.com/scummvm/scummvm/commit/f046a35f7809212b8b895e7b38e5af505f82bb96
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-01T08:41:48+02:00

Commit Message:
SCUMM: RA2: removed expensive non-zero pixel counter used for debug

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 0d6a8b0e663..53afda31784 100644
--- a/engines/scumm/smush/rebel/smush_player_ra2.cpp
+++ b/engines/scumm/smush/rebel/smush_player_ra2.cpp
@@ -120,17 +120,8 @@ void SmushPlayerRebel2::initGameVideoState() {
 		if ((_curVideoFlags & 0x08) == 0) {
 			// Cinematic mode (flags 0x20) - clear buffer for fresh video
 			memset(_dst, 0, vs->w * vs->h);
-		} else {
-			// Gameplay mode (flags 0x28) - do nothing, preserve existing screen content
-			if (debugChannelSet(-1, DEBUG_SMUSH)) {
-				int nonZero = 0;
-				for (int i = 0; i < vs->w * vs->h; i++) {
-					if (_dst[i] != 0) nonZero++;
-				}
-				debugC(DEBUG_SMUSH, "SmushPlayer::init: Preserving screen for gameplay video (%dx%d, %d%% non-zero)",
-					vs->w, vs->h, (nonZero * 100) / (vs->w * vs->h));
-			}
 		}
+		// Gameplay mode (flags 0x28): no-op, the existing screen content is preserved.
 	}
 }
 


Commit: a36e9516b199c7249c24697b2ba1e9871096f1f8
    https://github.com/scummvm/scummvm/commit/a36e9516b199c7249c24697b2ba1e9871096f1f8
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-01T09:02:32+02:00

Commit Message:
SCUMM: RA1: use DEBUG_INSANE for debug statements

Changed paths:
    engines/scumm/insane/rebel1/iact.cpp
    engines/scumm/insane/rebel1/levels.cpp
    engines/scumm/insane/rebel1/menu.cpp
    engines/scumm/insane/rebel1/rebel.cpp
    engines/scumm/insane/rebel1/render.cpp
    engines/scumm/insane/rebel1/runlevels.cpp
    engines/scumm/insane/rebel1/saveload.cpp


diff --git a/engines/scumm/insane/rebel1/iact.cpp b/engines/scumm/insane/rebel1/iact.cpp
index a6f94915ecb..68b0e2afac4 100644
--- a/engines/scumm/insane/rebel1/iact.cpp
+++ b/engines/scumm/insane/rebel1/iact.cpp
@@ -639,7 +639,7 @@ void InsaneRebel1::checkDynamicLevelBranch(int32 curFrame) {
 			_vm->_smushVideoShouldFinish = true;
 			const int32 resumeFrame = (_currentLevel == 6 && _pendingRouteStartFrame < 0) ?
 				0 : _pendingRouteStartFrame;
-			debug(1, "RA1 L%d cutover: route=%d -> %d at %s=%u (resumeFrame=%d)",
+			debugC(DEBUG_INSANE, "RA1 L%d cutover: route=%d -> %d at %s=%u (resumeFrame=%d)",
 				_currentLevel + 1, _levelRouteIndex, _pendingRouteIndex,
 				_currentLevel == 6 ? "localFrame" : "frame",
 				(unsigned)routeFrame, (int)resumeFrame);
@@ -681,7 +681,7 @@ void InsaneRebel1::checkDynamicLevelBranch(int32 curFrame) {
 				: (branchX < kLevel7BranchThreshold[nextRoute]);
 			if (!takeBranch) {
 				if (routeFrame == decisionFrame)
-					debug(1, "RA1 L7 branch miss: route=%d candidate=%d localFrame=%u gameFrame=%d shipX=%d dir=%d threshold=%d",
+					debugC(DEBUG_INSANE, "RA1 L7 branch miss: route=%d candidate=%d localFrame=%u gameFrame=%d shipX=%d dir=%d threshold=%d",
 						route, nextRoute, (unsigned)routeFrame, (int)_gameCounter, branchX,
 						kLevel7BranchDir[nextRoute], kLevel7BranchThreshold[nextRoute]);
 				continue;
@@ -692,7 +692,7 @@ void InsaneRebel1::checkDynamicLevelBranch(int32 curFrame) {
 			_pendingRouteStartFrame = (int32)routeFrame;
 			_pendingRouteVideoStartFrame = 1 + (_pendingRouteCutoverFrame - _pendingRouteStartFrame);
 			_level7WarningFrames = 0;
-			debug(1, "RA1 L7 branch: route=%d -> %d at localFrame=%u gameFrame=%d decisionFrame=%u shipX=%d resumeSourceFrame=%d cutoverFrame=%d destFrame=%d",
+			debugC(DEBUG_INSANE, "RA1 L7 branch: route=%d -> %d at localFrame=%u gameFrame=%d decisionFrame=%u shipX=%d resumeSourceFrame=%d cutoverFrame=%d destFrame=%d",
 				route, nextRoute, (unsigned)routeFrame, (int)_gameCounter, (unsigned)decisionFrame, branchX,
 				(int)_pendingRouteStartFrame, (int)_pendingRouteCutoverFrame,
 				(int)_pendingRouteVideoStartFrame);
@@ -1213,9 +1213,9 @@ void InsaneRebel1::updateShipPhysics() {
 		if (_shipPosX > kRA1CenterX) {
 			_rightPathSelected = true;
 			_vm->_smushVideoShouldFinish = true;
-			debug(1, "RA1: Right path selected (counter=%d, shipX=%d)", _gameCounter, _shipPosX);
+			debugC(DEBUG_INSANE, "RA1: Right path selected (counter=%d, shipX=%d)", _gameCounter, _shipPosX);
 		} else {
-			debug(1, "RA1: Left path retained (counter=%d, shipX=%d)", _gameCounter, _shipPosX);
+			debugC(DEBUG_INSANE, "RA1: Left path retained (counter=%d, shipX=%d)", _gameCounter, _shipPosX);
 		}
 		_pathBranchEnabled = false;
 	}
@@ -1552,13 +1552,13 @@ void InsaneRebel1::updateGameOp0BPhysics() {
 		// FUN_1CDA7 dispatches g_sfxDamageHit, initialized from SYS/BOOM.SAD.
 		playSfx(kSfxBoom, 127, 0);
 		if (_currentLevel == 1) {
-			debug(1, "RA1 L2 player hit: frame=%u view=(%d,%d) latch=%u asteroid=%d flags=0x%02x health=%d->%d",
+			debugC(DEBUG_INSANE, "RA1 L2 player hit: frame=%u view=(%d,%d) latch=%u asteroid=%d flags=0x%02x health=%d->%d",
 				(unsigned)(uint16)_gameCounter, _perspectiveX, _perspectiveY,
 				(unsigned)_gameLatch5D, level2AsteroidHit ? 1 : 0,
 				appliedDamageFlags, oldHealth, _health);
 		}
 		if (level8WalkerPlayerHit) {
-			debug(1, "RA1 L8 player hit by walker: route=%d frame=%u view=(%d,%d) flags=0x%02x health=%d->%d",
+			debugC(DEBUG_INSANE, "RA1 L8 player hit by walker: route=%d frame=%u view=(%d,%d) flags=0x%02x health=%d->%d",
 				CLIP<int>(_levelRouteIndex, 0, 2), (unsigned)(uint16)_gameCounter,
 				_perspectiveX, _perspectiveY, appliedDamageFlags, oldHealth, _health);
 		}
@@ -1691,7 +1691,7 @@ void InsaneRebel1::updateGameOp0BPhysics() {
 			const bool target215Destroyed = isFrameObjectPrimarySet(215);
 			if (!target211Destroyed || !target213Destroyed || !target215Destroyed) {
 				_levelGameplayPhase = 1;
-				debug(1, "RA1 L12 retry armed: frame=0x%04x targets=(%d,%d,%d)",
+				debugC(DEBUG_INSANE, "RA1 L12 retry armed: frame=0x%04x targets=(%d,%d,%d)",
 					_frameCounter,
 					target211Destroyed ? 1 : 0,
 					target213Destroyed ? 1 : 0,
@@ -2135,7 +2135,7 @@ void InsaneRebel1::handleGameOpcode0DCorridor(int32 subSize, Common::SeekableRea
 		}
 	}
 	if ((_damageFlags & 0x0F) != oldDirectionalFlags) {
-		debug(1, "RA1 0x0D hit: ship=(%d,%d) corridor=[%d,%d]-[%d,%d] flags=0x%02x zoneSuppressed=%d",
+		debugC(DEBUG_INSANE, "RA1 0x0D hit: ship=(%d,%d) corridor=[%d,%d]-[%d,%d] flags=0x%02x zoneSuppressed=%d",
 			collisionShipX, collisionShipY,
 			_corridorLeftX, _corridorTopY, _corridorRightX, _corridorBottomY,
 			_damageFlags, suppressDirectionalDamage ? 1 : 0);
@@ -2177,7 +2177,7 @@ void InsaneRebel1::handleGameOpcode0EZone(int32 subSize, Common::SeekableReadStr
 		collisionShipX > zoneLeft && collisionShipX < zoneRight &&
 		collisionShipY > zoneTop && collisionShipY < zoneBottom) {
 		_damageFlags |= 0x10;
-		debug(1, "RA1 0x0E hit: ship=(%d,%d) zone=[%d,%d]-[%d,%d] raw=[%d,%d]+(%d,%d) cam=(%d,%d) flags=0x%02x",
+		debugC(DEBUG_INSANE, "RA1 0x0E hit: ship=(%d,%d) zone=[%d,%d]-[%d,%d] raw=[%d,%d]+(%d,%d) cam=(%d,%d) flags=0x%02x",
 			collisionShipX, collisionShipY, zoneLeft, zoneTop, zoneRight, zoneBottom,
 			rawZoneLeft, rawZoneTop, zoneWidth, zoneHeight,
 			_perspectiveX, _perspectiveY, _damageFlags);
@@ -2203,7 +2203,7 @@ void InsaneRebel1::handleGameOpcode0BFirstPerson(int32 subSize, Common::Seekable
 		if (_interactiveVideoActive && maxFrames > 0 &&
 			_gameCounter >= (int32)maxFrames - 1) {
 			_vm->_smushVideoShouldFinish = true;
-			debug(1, "RA1: finishing 0x0B interactive video at counter=%d/%u", _gameCounter, maxFrames);
+			debugC(DEBUG_INSANE, "RA1: finishing 0x0B interactive video at counter=%d/%u", _gameCounter, maxFrames);
 		}
 	}
 	debug(7, "RA1 GAME 0x0B: counter=%d", _gameCounter);
@@ -2397,7 +2397,7 @@ void InsaneRebel1::processShot() {
 	playSfx(torpedoMode ? kSfxAlert : kSfxLaserShot, 127, 0);
 
 	if (effectiveOpcode == 0x09 || _currentLevel == 4) {
-		debug(1, "RA1 shot: opcode=0x%02x frame=%d slot=%d cursor=(%d,%d) origin=(%d,%d) dir=%d mode=%d",
+		debugC(DEBUG_INSANE, "RA1 shot: opcode=0x%02x frame=%d slot=%d cursor=(%d,%d) origin=(%d,%d) dir=%d mode=%d",
 			effectiveOpcode, _gameCounter, slot, cursorX, cursorY, originX, originY,
 			_shipDirIndex, _flyControlMode);
 	} else {
diff --git a/engines/scumm/insane/rebel1/levels.cpp b/engines/scumm/insane/rebel1/levels.cpp
index f32b598aee8..5e050f17d8c 100644
--- a/engines/scumm/insane/rebel1/levels.cpp
+++ b/engines/scumm/insane/rebel1/levels.cpp
@@ -156,7 +156,7 @@ bool InsaneRebel1::loadRA1Nut(const char *filename, RA1SpriteBank &bank) {
 	free(fobjOffsets);
 
 	free(data);
-	debug(1, "InsaneRebel1::loadRA1Nut('%s'): expected=%d found=%d decoded=%d bytes",
+	debugC(DEBUG_INSANE, "InsaneRebel1::loadRA1Nut('%s'): expected=%d found=%d decoded=%d bytes",
 		  filename, expectedSprites, foundSprites, decodedSize);
 	return true;
 }
@@ -169,14 +169,14 @@ void InsaneRebel1::loadLevelSprites(int level) {
 		if (!loadRA1Nut(legacyBankFile.c_str(), _shipBank)) {
 			Common::String pilotFile = Common::String::format("LVL%d/L%dPILOT.NUT", level, level);
 			if (!loadRA1Nut(pilotFile.c_str(), _shipBank))
-				debug(1, "InsaneRebel1: No BANK1/BANK/PILOT for level %d", level);
+				debugC(DEBUG_INSANE, "InsaneRebel1: 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)) {
-		debug(1, "InsaneRebel1: No BANK2 for level %d", level);
+		debugC(DEBUG_INSANE, "InsaneRebel1: No BANK2 for level %d", level);
 	}
 
 	loadRA1Nut("SYS/DISPLAY.NUT", _displayBank);
diff --git a/engines/scumm/insane/rebel1/menu.cpp b/engines/scumm/insane/rebel1/menu.cpp
index bcd73aff0de..42c09914d77 100644
--- a/engines/scumm/insane/rebel1/menu.cpp
+++ b/engines/scumm/insane/rebel1/menu.cpp
@@ -516,7 +516,7 @@ bool InsaneRebel1::notifyEvent(const Common::Event &event) {
 	}
 
 	if (event.type == Common::EVENT_JOYBUTTON_DOWN || event.type == Common::EVENT_JOYBUTTON_UP) {
-		debug(1, "RA1 input raw-joy-button: button=%d pressed=%d menu=%d gameplay=%d storedAxis=(%d,%d)",
+		debugC(DEBUG_INSANE, "RA1 input raw-joy-button: button=%d pressed=%d menu=%d gameplay=%d storedAxis=(%d,%d)",
 			event.joystick.button, event.type == Common::EVENT_JOYBUTTON_DOWN,
 			_menuActive, _interactiveVideoActive && !_menuActive,
 			_joystickAxisX, _joystickAxisY);
@@ -595,7 +595,7 @@ bool InsaneRebel1::notifyEvent(const Common::Event &event) {
 		event.type == Common::EVENT_CUSTOM_ENGINE_ACTION_END) {
 		const bool pressed = (event.type == Common::EVENT_CUSTOM_ENGINE_ACTION_START);
 
-		debug(1, "RA1 input mapped-action: action=%s custom=%u pressed=%d menu=%d gameplay=%d storedAxis=(%d,%d) actionState(L,R,U,D)=(%d,%d,%d,%d)",
+		debugC(DEBUG_INSANE, "RA1 input mapped-action: action=%s custom=%u pressed=%d menu=%d gameplay=%d storedAxis=(%d,%d) actionState(L,R,U,D)=(%d,%d,%d,%d)",
 			getRebel1ActionName(event.customType), event.customType, pressed,
 			_menuActive, _interactiveVideoActive && !_menuActive,
 			_joystickAxisX, _joystickAxisY,
@@ -655,10 +655,10 @@ bool InsaneRebel1::notifyEvent(const Common::Event &event) {
 	if (event.type == Common::EVENT_MAINMENU && _interactiveVideoActive && !_menuActive) {
 		const uint32 now = _vm->_system->getMillis();
 		const uint32 elapsedSinceAxis = _lastJoystickAxisEventTime ? now - _lastJoystickAxisEventTime : 0xffffffffu;
-		debug(1, "RA1 input mainmenu-event: gameplay=1 elapsedSinceAxis=%u storedAxis=(%d,%d)",
+		debugC(DEBUG_INSANE, "RA1 input mainmenu-event: gameplay=1 elapsedSinceAxis=%u storedAxis=(%d,%d)",
 			elapsedSinceAxis, _joystickAxisX, _joystickAxisY);
 		if (elapsedSinceAxis <= kRA1JoystickAxisEscGuardMs) {
-			debug(1, "RA1 input ignored mainmenu event after recent joystick axis movement (%u ms)", elapsedSinceAxis);
+			debugC(DEBUG_INSANE, "RA1 input ignored mainmenu event after recent joystick axis movement (%u ms)", elapsedSinceAxis);
 			return true;
 		}
 	}
@@ -668,11 +668,11 @@ bool InsaneRebel1::notifyEvent(const Common::Event &event) {
 			event.kbd.keycode == Common::KEYCODE_ESCAPE) {
 			const uint32 now = _vm->_system->getMillis();
 			const uint32 elapsedSinceAxis = _lastJoystickAxisEventTime ? now - _lastJoystickAxisEventTime : 0xffffffffu;
-			debug(1, "RA1 input keydown-escape: gameplay=1 ascii=%d flags=0x%x repeat=%d elapsedSinceAxis=%u storedAxis=(%d,%d)",
+			debugC(DEBUG_INSANE, "RA1 input keydown-escape: gameplay=1 ascii=%d flags=0x%x repeat=%d elapsedSinceAxis=%u storedAxis=(%d,%d)",
 				event.kbd.ascii, event.kbd.flags, event.kbdRepeat,
 				elapsedSinceAxis, _joystickAxisX, _joystickAxisY);
 			if (elapsedSinceAxis <= kRA1JoystickAxisEscGuardMs) {
-				debug(1, "RA1 input ignored ESC after recent joystick axis movement (%u ms)", elapsedSinceAxis);
+				debugC(DEBUG_INSANE, "RA1 input ignored ESC after recent joystick axis movement (%u ms)", elapsedSinceAxis);
 				return true;
 			}
 
@@ -929,7 +929,7 @@ bool InsaneRebel1::runTextEntryMenuLoop() {
 }
 
 int InsaneRebel1::runMainMenu() {
-	debug(1, "InsaneRebel1: Main menu");
+	debugC(DEBUG_INSANE, "InsaneRebel1: Main menu");
 
 	_menuSelection = 0;
 	while (!shouldAbortGameFlow()) {
@@ -962,13 +962,13 @@ int InsaneRebel1::runPasscodeEntryDialog() {
 			_maxChapterUnlocked = MAX<int16>(_maxChapterUnlocked, i);
 			if (targetLevel <= kRA1NumLevels)
 				_startLevel = targetLevel;
-			debug(1, "RA1 passcode accepted: slot=%d password=%s difficulty=%d target=%d",
+			debugC(DEBUG_INSANE, "RA1 passcode accepted: slot=%d password=%s difficulty=%d target=%d",
 				i, password, _difficulty, targetLevel);
 			return targetLevel;
 		}
 	}
 
-	debug(1, "RA1 passcode rejected: '%s'", _textEntryBuffer);
+	debugC(DEBUG_INSANE, "RA1 passcode rejected: '%s'", _textEntryBuffer);
 	return 0;
 }
 
@@ -995,7 +995,7 @@ bool InsaneRebel1::runHighScoreNameEntry() {
 	Common::strlcpy(_highScores[slot].name, storedName.c_str(), sizeof(_highScores[slot].name));
 	_highScores[slot].difficulty = _difficulty;
 	_highScoreEntryIndex = -1;
-	debug(1, "RA1 high score inserted: slot=%d name=%s score=%ld difficulty=%d",
+	debugC(DEBUG_INSANE, "RA1 high score inserted: slot=%d name=%s score=%ld difficulty=%d",
 		slot, _highScores[slot].name, (long)_highScores[slot].score, _highScores[slot].difficulty);
 	return true;
 }
diff --git a/engines/scumm/insane/rebel1/rebel.cpp b/engines/scumm/insane/rebel1/rebel.cpp
index 875a305541e..6ebd1efd8e8 100644
--- a/engines/scumm/insane/rebel1/rebel.cpp
+++ b/engines/scumm/insane/rebel1/rebel.cpp
@@ -192,7 +192,7 @@ void InsaneRebel1::loadTuningForLevel(int level) {
 	_protectedTargetA = 0;
 	_protectedTargetB = 0;
 
-	debug(1, "RA1: Loaded tuning level=%d diff=%d: roll=%d lift=%d slide=%d drift=%d snap=%d "
+	debugC(DEBUG_INSANE, "RA1: Loaded tuning level=%d diff=%d: roll=%d lift=%d slide=%d drift=%d snap=%d "
 		"miss=%d wham=%d shot=%d kill=%d time=%d levelPts=%d bonus=%d flags=0x%x",
 		level, d, _tuning.roll, _tuning.lift, _tuning.slide, _tuning.drift, _tuning.snap,
 		_tuning.miss, _tuning.wham, _tuning.shot, _tuning.kill,
@@ -404,17 +404,17 @@ InsaneRebel1::InsaneRebel1(ScummEngine_v7 *scumm) : Insane(), _vm(scumm) {
 	resetFrameObjectState();
 
 	if (loadRA1Nut("SYS/TALKFONT.NUT", _hudFontBank)) {
-		debug(1, "InsaneRebel1: HUD/menu glyph font loaded from SYS/TALKFONT.NUT (%d chars)", _hudFontBank.numSprites);
+		debugC(DEBUG_INSANE, "InsaneRebel1: HUD/menu glyph font loaded from SYS/TALKFONT.NUT (%d chars)", _hudFontBank.numSprites);
 	} else if (loadRA1Nut("SYS/TECHFONT.NUT", _hudFontBank)) {
-		debug(1, "InsaneRebel1: HUD/menu glyph font loaded from SYS/TECHFONT.NUT (%d chars)", _hudFontBank.numSprites);
+		debugC(DEBUG_INSANE, "InsaneRebel1: HUD/menu glyph font loaded from SYS/TECHFONT.NUT (%d chars)", _hudFontBank.numSprites);
 	} else {
 		warning("InsaneRebel1: failed to load RA1 HUD font bank (TECHFONT/TALKFONT)");
 	}
 
 	if (loadRA1Nut("SYS/TITLFONT.NUT", _titleFontBank)) {
-		debug(1, "InsaneRebel1: title glyph font loaded from SYS/TITLFONT.NUT (%d chars)", _titleFontBank.numSprites);
+		debugC(DEBUG_INSANE, "InsaneRebel1: title glyph font loaded from SYS/TITLFONT.NUT (%d chars)", _titleFontBank.numSprites);
 	} else if (loadRA1Nut("SYS/TALKFONT.NUT", _titleFontBank)) {
-		debug(1, "InsaneRebel1: title glyph font fallback loaded from SYS/TALKFONT.NUT (%d chars)", _titleFontBank.numSprites);
+		debugC(DEBUG_INSANE, "InsaneRebel1: title glyph font fallback loaded from SYS/TALKFONT.NUT (%d chars)", _titleFontBank.numSprites);
 	} else {
 		warning("InsaneRebel1: failed to load title font bank (TITLFONT/TALKFONT)");
 	}
@@ -422,9 +422,9 @@ InsaneRebel1::InsaneRebel1(ScummEngine_v7 *scumm) : Insane(), _vm(scumm) {
 	// FUN_1CB22 uses "<<" layer markers that resolve to TECHFONT in the original.
 	// Keep a dedicated TECH font bank for targeting markers/lock indicators.
 	if (loadRA1Nut("SYS/TECHFONT.NUT", _techFontBank)) {
-		debug(1, "InsaneRebel1: targeting glyph font loaded from SYS/TECHFONT.NUT (%d chars)", _techFontBank.numSprites);
+		debugC(DEBUG_INSANE, "InsaneRebel1: targeting glyph font loaded from SYS/TECHFONT.NUT (%d chars)", _techFontBank.numSprites);
 	} else if (loadRA1Nut("SYS/TALKFONT.NUT", _techFontBank)) {
-		debug(1, "InsaneRebel1: targeting glyph font fallback loaded from SYS/TALKFONT.NUT (%d chars)", _techFontBank.numSprites);
+		debugC(DEBUG_INSANE, "InsaneRebel1: targeting glyph font fallback loaded from SYS/TALKFONT.NUT (%d chars)", _techFontBank.numSprites);
 	} else {
 		warning("InsaneRebel1: failed to load targeting font bank (TECHFONT/TALKFONT)");
 	}
diff --git a/engines/scumm/insane/rebel1/render.cpp b/engines/scumm/insane/rebel1/render.cpp
index ae7f90294a6..110c3911e43 100644
--- a/engines/scumm/insane/rebel1/render.cpp
+++ b/engines/scumm/insane/rebel1/render.cpp
@@ -1908,7 +1908,7 @@ void InsaneRebel1::updateLevel8WalkerState() {
 			_pendingRouteIndex = newRoute;
 			_pendingRouteCutoverFrame = _gameCounter + 7;
 			_pendingRouteStartFrame = _pendingRouteCutoverFrame;
-			debug(1, "RA1 L8 branch: route=%d -> %d at frame=%u shipX=%d resumeTimelineFrame=%d cutoverFrame=%d",
+			debugC(DEBUG_INSANE, "RA1 L8 branch: route=%d -> %d at frame=%u shipX=%d resumeTimelineFrame=%d cutoverFrame=%d",
 				route, newRoute, (unsigned)_gameCounter, _shipPosX,
 				(int)_pendingRouteStartFrame, (int)_pendingRouteCutoverFrame);
 		}
diff --git a/engines/scumm/insane/rebel1/runlevels.cpp b/engines/scumm/insane/rebel1/runlevels.cpp
index 8dffb1c5a27..62c35eaf205 100644
--- a/engines/scumm/insane/rebel1/runlevels.cpp
+++ b/engines/scumm/insane/rebel1/runlevels.cpp
@@ -147,7 +147,7 @@ static int calculateThresholdBonus(int kills, int perfectThreshold, int perKillT
 // Reuses RA2's pattern: reset handler, set cinematic flags, play video.
 // startFrame > 0: fast-forward (decode without display/audio) to that frame.
 void InsaneRebel1::playCinematic(const char *filename, int32 startFrame) {
-	debug(1, "InsaneRebel1::playCinematic('%s', startFrame=%d)", filename, startFrame);
+	debugC(DEBUG_INSANE, "InsaneRebel1::playCinematic('%s', startFrame=%d)", filename, startFrame);
 	if (shouldAbortGameFlow())
 		return;
 
@@ -315,7 +315,7 @@ void InsaneRebel1::clearVideoBuffer() {
 //   1. O1LOGO.ANM ??? LucasArts logo
 //   2. O1OPEN.ANM ??? Star Wars opening crawl
 void InsaneRebel1::playIntroSequence() {
-	debug(1, "InsaneRebel1: Playing intro sequence");
+	debugC(DEBUG_INSANE, "InsaneRebel1: Playing intro sequence");
 
 	// LucasArts logo (original: PUSH 0x57cc, CALL FUN_1BA32 with flags 0x0420)
 	playCinematic("OPEN/O1LOGO.ANM");
@@ -347,7 +347,7 @@ void InsaneRebel1::playIntroSequence() {
 //     lives==0: L1DEATH ??? return to menu
 
 bool InsaneRebel1::runLevel1() {
-	debug(1, "InsaneRebel1: Running level 1");
+	debugC(DEBUG_INSANE, "InsaneRebel1: Running level 1");
 
 	_currentLevel = 0;
 	loadTuningForLevel(0);
@@ -448,7 +448,7 @@ bool InsaneRebel1::runLevel1() {
 }
 
 bool InsaneRebel1::runLevel2() {
-	debug(1, "InsaneRebel1: Running level 2");
+	debugC(DEBUG_INSANE, "InsaneRebel1: Running level 2");
 
 	_currentLevel = 1;
 	loadLevelSprites(2);
@@ -489,7 +489,7 @@ bool InsaneRebel1::runLevel2() {
 }
 
 bool InsaneRebel1::runLevel3() {
-	debug(1, "InsaneRebel1: Running level 3");
+	debugC(DEBUG_INSANE, "InsaneRebel1: Running level 3");
 
 	_currentLevel = 2;
 	loadLevelSprites(3);
@@ -530,7 +530,7 @@ bool InsaneRebel1::runLevel3() {
 }
 
 bool InsaneRebel1::runLevel4() {
-	debug(1, "InsaneRebel1: Running level 4");
+	debugC(DEBUG_INSANE, "InsaneRebel1: Running level 4");
 
 	_currentLevel = 3;
 	loadLevelSprites(4);
@@ -600,7 +600,7 @@ bool InsaneRebel1::runLevel4() {
 }
 
 bool InsaneRebel1::runLevel5() {
-	debug(1, "InsaneRebel1: Running level 5");
+	debugC(DEBUG_INSANE, "InsaneRebel1: Running level 5");
 
 	_currentLevel = 4;
 	loadLevelSprites(5);
@@ -682,7 +682,7 @@ bool InsaneRebel1::runLevel5() {
 }
 
 bool InsaneRebel1::runLevel6() {
-	debug(1, "InsaneRebel1: Running level 6");
+	debugC(DEBUG_INSANE, "InsaneRebel1: Running level 6");
 
 	_currentLevel = 5;
 	loadLevelSprites(6);
@@ -727,7 +727,7 @@ bool InsaneRebel1::runLevel6() {
 }
 
 bool InsaneRebel1::runLevel7() {
-	debug(1, "InsaneRebel1: Running level 7");
+	debugC(DEBUG_INSANE, "InsaneRebel1: Running level 7");
 
 	const char *const kLevel7Segments[] = {
 		"LVL7/L7PLAY1.ANM",
@@ -811,7 +811,7 @@ bool InsaneRebel1::runLevel7() {
 }
 
 bool InsaneRebel1::runLevel8() {
-	debug(1, "InsaneRebel1: Running level 8");
+	debugC(DEBUG_INSANE, "InsaneRebel1: Running level 8");
 
 	const char *const kLevel8Routes[] = {
 		"LVL8/L8PLAY.ANM",
@@ -887,7 +887,7 @@ bool InsaneRebel1::runLevel8() {
 }
 
 bool InsaneRebel1::runLevel9() {
-	debug(1, "InsaneRebel1: Running level 9");
+	debugC(DEBUG_INSANE, "InsaneRebel1: Running level 9");
 
 	// DOS RunLevel9Flow calls RandScaleByte(2) three times before the intro.
 	// That helper advances a byte seed with seed = seed * 9 + 0x35 and returns
@@ -921,7 +921,7 @@ bool InsaneRebel1::runLevel9() {
 			if (_killCount > 0)
 				return (_shipPosX < kRA1CenterX) ? 0 : 1;
 
-			debug(1, "RA1 L9 selector '%s' ended without target hit; replaying", filename);
+			debugC(DEBUG_INSANE, "RA1 L9 selector '%s' ended without target hit; replaying", filename);
 		}
 		return -1;
 	};
@@ -1077,7 +1077,7 @@ bool InsaneRebel1::runLevel9() {
 }
 
 bool InsaneRebel1::runLevel10() {
-	debug(1, "InsaneRebel1: Running level 10");
+	debugC(DEBUG_INSANE, "InsaneRebel1: Running level 10");
 
 	_currentLevel = 9;
 	loadLevelSprites(10);
@@ -1118,7 +1118,7 @@ bool InsaneRebel1::runLevel10() {
 // Turret-style level. Single interactive phase with kill-count retry.
 // Original: L11INTRO ??? L11PLAY (turret, killCount>4 to pass) ??? L11RETRY ??? retry/L11END
 bool InsaneRebel1::runLevel11() {
-	debug(1, "InsaneRebel1: Running level 11");
+	debugC(DEBUG_INSANE, "InsaneRebel1: Running level 11");
 
 	_currentLevel = 10;
 	loadLevelSprites(11);
@@ -1175,7 +1175,7 @@ bool InsaneRebel1::runLevel11() {
 // Single interactive phase with mid-level retry mechanism.
 // Original: L12INTRO ??? L12PLAY ??? (retry at specific frame) ??? L12END
 bool InsaneRebel1::runLevel12() {
-	debug(1, "InsaneRebel1: Running level 12");
+	debugC(DEBUG_INSANE, "InsaneRebel1: Running level 12");
 
 	_currentLevel = 11;
 	loadLevelSprites(12);
@@ -1238,7 +1238,7 @@ bool InsaneRebel1::runLevel12() {
 // Flight level with enemy projectile system (original has 5-slot projectile tracking).
 // Original: L13INTRO ??? L13PLAY ??? L13END/L13NEW/L13DEATH
 bool InsaneRebel1::runLevel13() {
-	debug(1, "InsaneRebel1: Running level 13");
+	debugC(DEBUG_INSANE, "InsaneRebel1: Running level 13");
 
 	_currentLevel = 12;
 	loadLevelSprites(13);
@@ -1281,7 +1281,7 @@ bool InsaneRebel1::runLevel13() {
 // Original: L14INTRO ??? L14PLAY ??? L14PLAY2 ??? optional L14PLY2B splice
 //   ??? L14END/L14NEW/L14DEATH
 bool InsaneRebel1::runLevel14() {
-	debug(1, "InsaneRebel1: Running level 14");
+	debugC(DEBUG_INSANE, "InsaneRebel1: Running level 14");
 
 	_currentLevel = 13;
 	loadLevelSprites(14);
@@ -1355,7 +1355,7 @@ bool InsaneRebel1::runLevel14() {
 // Original: L15INTRO ??? L15PLAY1 (trench run) ??? L15INTR2 (torpedo lock cutscene)
 //   ??? L15PLAY2 (final approach + torpedo) ??? L15END1/L15NEW/L15DEATH
 bool InsaneRebel1::runLevel15() {
-	debug(1, "InsaneRebel1: Running level 15");
+	debugC(DEBUG_INSANE, "InsaneRebel1: Running level 15");
 
 	_currentLevel = 14;
 	loadLevelSprites(15);
@@ -1401,7 +1401,7 @@ bool InsaneRebel1::runLevel15() {
 		}
 
 		if (_health >= 0 && !_torpedoFired) {
-			debug(1, "InsaneRebel1: Level 15 torpedo run ended without exhaust-port hit");
+			debugC(DEBUG_INSANE, "InsaneRebel1: Level 15 torpedo run ended without exhaust-port hit");
 			return false;
 		}
 
@@ -1625,30 +1625,30 @@ void InsaneRebel1::resolveSeek(const char *filename, int32 startFrame, int32 &vi
 			_pendingRouteVideoStartFrame : 1;
 		videoOffset = findAnimFrameChunkOffset(_vm, filename, videoStartFrame);
 		if (videoOffset < 0) {
-			debug(1, "RA1 L7 route switch: route=%d destinationFrame=%d offset lookup failed",
+			debugC(DEBUG_INSANE, "RA1 L7 route switch: route=%d destinationFrame=%d offset lookup failed",
 				_levelRouteIndex, (int)videoStartFrame);
 			videoStartFrame = 0;
 			videoOffset = 0;
 		} else {
-			debug(1, "RA1 L7 route switch: route=%d decisionLocalFrame=%d opens destination at localFrame=%d offset=0x%x",
+			debugC(DEBUG_INSANE, "RA1 L7 route switch: route=%d decisionLocalFrame=%d opens destination at localFrame=%d offset=0x%x",
 				_levelRouteIndex, (int)_pendingRouteStartFrame,
 				(int)videoStartFrame, (unsigned)videoOffset);
 		}
 	} else if (_currentLevel == 7 && resumingRoute) {
 		videoOffset = findAnimFrameChunkOffsetByGameCounter(_vm, filename, startFrame, videoStartFrame);
 		if (videoOffset < 0) {
-			debug(1, "RA1 L8 resume: route=%d timelineFrame=%d GAME counter lookup failed",
+			debugC(DEBUG_INSANE, "RA1 L8 resume: route=%d timelineFrame=%d GAME counter lookup failed",
 				_levelRouteIndex, (int)startFrame);
 			videoStartFrame = startFrame;
 			videoOffset = findAnimFrameChunkOffset(_vm, filename, videoStartFrame);
 		}
 		if (videoOffset < 0) {
-			debug(1, "RA1 L8 resume: route=%d timelineFrame=%d localFrame=%d offset lookup failed",
+			debugC(DEBUG_INSANE, "RA1 L8 resume: route=%d timelineFrame=%d localFrame=%d offset lookup failed",
 				_levelRouteIndex, (int)startFrame, (int)videoStartFrame);
 			videoStartFrame = 0;
 			videoOffset = 0;
 		} else {
-			debug(1, "RA1 L8 resume: route=%d timelineFrame=%d -> localFrame=%d offset=0x%x",
+			debugC(DEBUG_INSANE, "RA1 L8 resume: route=%d timelineFrame=%d -> localFrame=%d offset=0x%x",
 				_levelRouteIndex, (int)startFrame, (int)videoStartFrame, (unsigned)videoOffset);
 		}
 	} else if (_currentLevel == 13 && resumingRoute) {
@@ -1656,7 +1656,7 @@ void InsaneRebel1::resolveSeek(const char *filename, int32 startFrame, int32 &vi
 		// oldMaxFrame-0x0F, 1, -1). That frame number belongs to L14PLAY2's
 		// timeline; L14PLY2B is already the continuation clip and starts at its
 		// matching lead-in frame. Preserve the current state, but do not seek.
-		debug(1, "RA1 L14 splice: L14PLAY2 timelineFrame=%d -> L14PLY2B frame 0",
+		debugC(DEBUG_INSANE, "RA1 L14 splice: L14PLAY2 timelineFrame=%d -> L14PLY2B frame 0",
 			(int)startFrame);
 	}
 }
@@ -1690,7 +1690,7 @@ void InsaneRebel1::playInteractiveVideoFile(const char *filename, int32 videoOff
 
 // Play interactive gameplay video (with ship physics + HUD).
 void InsaneRebel1::playInteractiveVideo(const char *filename, int32 startFrame) {
-	debug(1, "InsaneRebel1::playInteractiveVideo('%s', startFrame=%d)", filename, startFrame);
+	debugC(DEBUG_INSANE, "InsaneRebel1::playInteractiveVideo('%s', startFrame=%d)", filename, startFrame);
 	if (shouldAbortGameFlow())
 		return;
 
diff --git a/engines/scumm/insane/rebel1/saveload.cpp b/engines/scumm/insane/rebel1/saveload.cpp
index 06849925a7f..09335106b4d 100644
--- a/engines/scumm/insane/rebel1/saveload.cpp
+++ b/engines/scumm/insane/rebel1/saveload.cpp
@@ -115,7 +115,7 @@ Common::Error InsaneRebel1::writeSaveState(int slot, const Common::String &desc,
 		return Common::kWritingFailed;
 	}
 
-	debug(1, "RA1: saved slot=%d level=%d lives=%d score=%d desc='%s'",
+	debugC(DEBUG_INSANE, "RA1: saved slot=%d level=%d lives=%d score=%d desc='%s'",
 		slot, state.resumeLevel, state.lives, state.score, desc.c_str());
 	return Common::kNoError;
 }
@@ -219,7 +219,7 @@ Common::Error InsaneRebel1::loadGameState(int slot, bool startupLoad) {
 		_vm->_smushVideoShouldFinish = true;
 	}
 
-	debug(1, "RA1: loaded slot=%d level=%d lives=%d score=%d", slot, _resumeLevel, _lives, _score);
+	debugC(DEBUG_INSANE, "RA1: loaded slot=%d level=%d lives=%d score=%d", slot, _resumeLevel, _lives, _score);
 	return Common::kNoError;
 }
 
@@ -238,7 +238,7 @@ void InsaneRebel1::autosaveProgress() {
 	if (readSaveState(slot, oldState, &oldDesc)) {
 		if (oldState.resumeLevel > state.resumeLevel ||
 				(oldState.resumeLevel == state.resumeLevel && oldState.lives >= state.lives)) {
-			debug(1, "RA1: skipping autosave slot=%d level=%d lives=%d; existing level=%d lives=%d",
+			debugC(DEBUG_INSANE, "RA1: skipping autosave slot=%d level=%d lives=%d; existing level=%d lives=%d",
 				slot, state.resumeLevel, state.lives, oldState.resumeLevel, oldState.lives);
 			return;
 		}


Commit: 912e8b3bf4d05644c64d0a5df38a942d90bf29bb
    https://github.com/scummvm/scummvm/commit/912e8b3bf4d05644c64d0a5df38a942d90bf29bb
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-01T09:31:15+02:00

Commit Message:
SCUMM: RA: improve debug statements with class/function names

Changed paths:
    engines/scumm/insane/rebel1/levels.cpp
    engines/scumm/insane/rebel1/menu.cpp
    engines/scumm/insane/rebel1/rebel.cpp
    engines/scumm/insane/rebel1/runlevels.cpp
    engines/scumm/smush/rebel/codec_ra2.cpp
    engines/scumm/smush/rebel/smush_player_ra1.cpp
    engines/scumm/smush/rebel/smush_player_ra2.cpp


diff --git a/engines/scumm/insane/rebel1/levels.cpp b/engines/scumm/insane/rebel1/levels.cpp
index 5e050f17d8c..b24fb5b85d0 100644
--- a/engines/scumm/insane/rebel1/levels.cpp
+++ b/engines/scumm/insane/rebel1/levels.cpp
@@ -169,14 +169,14 @@ void InsaneRebel1::loadLevelSprites(int level) {
 		if (!loadRA1Nut(legacyBankFile.c_str(), _shipBank)) {
 			Common::String pilotFile = Common::String::format("LVL%d/L%dPILOT.NUT", level, level);
 			if (!loadRA1Nut(pilotFile.c_str(), _shipBank))
-				debugC(DEBUG_INSANE, "InsaneRebel1: No BANK1/BANK/PILOT for level %d", level);
+				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)) {
-		debugC(DEBUG_INSANE, "InsaneRebel1: No BANK2 for level %d", level);
+		debugC(DEBUG_INSANE, "InsaneRebel1::loadLevelSprites: No BANK2 for level %d", level);
 	}
 
 	loadRA1Nut("SYS/DISPLAY.NUT", _displayBank);
diff --git a/engines/scumm/insane/rebel1/menu.cpp b/engines/scumm/insane/rebel1/menu.cpp
index 42c09914d77..3ded18a95d8 100644
--- a/engines/scumm/insane/rebel1/menu.cpp
+++ b/engines/scumm/insane/rebel1/menu.cpp
@@ -676,7 +676,7 @@ bool InsaneRebel1::notifyEvent(const Common::Event &event) {
 				return true;
 			}
 
-			debugC(DEBUG_INSANE, "Rebel1: ESC pressed during gameplay - opening ScummVM menu");
+			debugC(DEBUG_INSANE, "InsaneRebel1::notifyEvent: ESC pressed during gameplay - opening ScummVM menu");
 			const bool wasPaused = _player->_paused;
 			if (!wasPaused)
 				_player->pause();
@@ -689,13 +689,13 @@ bool InsaneRebel1::notifyEvent(const Common::Event &event) {
 		if (_interactiveVideoActive && !_menuActive &&
 			event.kbd.keycode == Common::KEYCODE_s &&
 			event.kbd.hasFlags(Common::KBD_SHIFT)) {
-			debugC(DEBUG_INSANE, "Rebel1: Shift+S pressed - skipping gameplay section");
+			debugC(DEBUG_INSANE, "InsaneRebel1::notifyEvent: Shift+S pressed - skipping gameplay section");
 			_vm->_smushVideoShouldFinish = true;
 			return true;
 		}
 
 		if (!_interactiveVideoActive && event.kbd.keycode == Common::KEYCODE_ESCAPE) {
-			debugC(DEBUG_INSANE, "Rebel1: ESC pressed - skipping cinematic");
+			debugC(DEBUG_INSANE, "InsaneRebel1::notifyEvent: ESC pressed - skipping cinematic");
 			_vm->_smushVideoShouldFinish = true;
 			return true;
 		}
@@ -929,7 +929,7 @@ bool InsaneRebel1::runTextEntryMenuLoop() {
 }
 
 int InsaneRebel1::runMainMenu() {
-	debugC(DEBUG_INSANE, "InsaneRebel1: Main menu");
+	debugC(DEBUG_INSANE, "InsaneRebel1::runMainMenu: Main menu");
 
 	_menuSelection = 0;
 	while (!shouldAbortGameFlow()) {
diff --git a/engines/scumm/insane/rebel1/rebel.cpp b/engines/scumm/insane/rebel1/rebel.cpp
index 6ebd1efd8e8..af3ecab61c7 100644
--- a/engines/scumm/insane/rebel1/rebel.cpp
+++ b/engines/scumm/insane/rebel1/rebel.cpp
@@ -404,29 +404,29 @@ InsaneRebel1::InsaneRebel1(ScummEngine_v7 *scumm) : Insane(), _vm(scumm) {
 	resetFrameObjectState();
 
 	if (loadRA1Nut("SYS/TALKFONT.NUT", _hudFontBank)) {
-		debugC(DEBUG_INSANE, "InsaneRebel1: HUD/menu glyph font loaded from SYS/TALKFONT.NUT (%d chars)", _hudFontBank.numSprites);
+		debugC(DEBUG_INSANE, "InsaneRebel1::InsaneRebel1(): HUD/menu glyph font loaded from SYS/TALKFONT.NUT (%d chars)", _hudFontBank.numSprites);
 	} else if (loadRA1Nut("SYS/TECHFONT.NUT", _hudFontBank)) {
-		debugC(DEBUG_INSANE, "InsaneRebel1: HUD/menu glyph font loaded from SYS/TECHFONT.NUT (%d chars)", _hudFontBank.numSprites);
+		debugC(DEBUG_INSANE, "InsaneRebel1::InsaneRebel1(): HUD/menu glyph font loaded from SYS/TECHFONT.NUT (%d chars)", _hudFontBank.numSprites);
 	} else {
-		warning("InsaneRebel1: failed to load RA1 HUD font bank (TECHFONT/TALKFONT)");
+		warning("InsaneRebel1::InsaneRebel1(): failed to load RA1 HUD font bank (TECHFONT/TALKFONT)");
 	}
 
 	if (loadRA1Nut("SYS/TITLFONT.NUT", _titleFontBank)) {
-		debugC(DEBUG_INSANE, "InsaneRebel1: title glyph font loaded from SYS/TITLFONT.NUT (%d chars)", _titleFontBank.numSprites);
+		debugC(DEBUG_INSANE, "InsaneRebel1::InsaneRebel1(): title glyph font loaded from SYS/TITLFONT.NUT (%d chars)", _titleFontBank.numSprites);
 	} else if (loadRA1Nut("SYS/TALKFONT.NUT", _titleFontBank)) {
-		debugC(DEBUG_INSANE, "InsaneRebel1: title glyph font fallback loaded from SYS/TALKFONT.NUT (%d chars)", _titleFontBank.numSprites);
+		debugC(DEBUG_INSANE, "InsaneRebel1::InsaneRebel1(): title glyph font fallback loaded from SYS/TALKFONT.NUT (%d chars)", _titleFontBank.numSprites);
 	} else {
-		warning("InsaneRebel1: failed to load title font bank (TITLFONT/TALKFONT)");
+		warning("InsaneRebel1::InsaneRebel1(): failed to load title font bank (TITLFONT/TALKFONT)");
 	}
 
 	// FUN_1CB22 uses "<<" layer markers that resolve to TECHFONT in the original.
 	// Keep a dedicated TECH font bank for targeting markers/lock indicators.
 	if (loadRA1Nut("SYS/TECHFONT.NUT", _techFontBank)) {
-		debugC(DEBUG_INSANE, "InsaneRebel1: targeting glyph font loaded from SYS/TECHFONT.NUT (%d chars)", _techFontBank.numSprites);
+		debugC(DEBUG_INSANE, "InsaneRebel1::InsaneRebel1(): targeting glyph font loaded from SYS/TECHFONT.NUT (%d chars)", _techFontBank.numSprites);
 	} else if (loadRA1Nut("SYS/TALKFONT.NUT", _techFontBank)) {
-		debugC(DEBUG_INSANE, "InsaneRebel1: targeting glyph font fallback loaded from SYS/TALKFONT.NUT (%d chars)", _techFontBank.numSprites);
+		debugC(DEBUG_INSANE, "InsaneRebel1::InsaneRebel1(): targeting glyph font fallback loaded from SYS/TALKFONT.NUT (%d chars)", _techFontBank.numSprites);
 	} else {
-		warning("InsaneRebel1: failed to load targeting font bank (TECHFONT/TALKFONT)");
+		warning("InsaneRebel1::InsaneRebel1(): failed to load targeting font bank (TECHFONT/TALKFONT)");
 	}
 
 	// Audio
diff --git a/engines/scumm/insane/rebel1/runlevels.cpp b/engines/scumm/insane/rebel1/runlevels.cpp
index 62c35eaf205..0e9f86c23c0 100644
--- a/engines/scumm/insane/rebel1/runlevels.cpp
+++ b/engines/scumm/insane/rebel1/runlevels.cpp
@@ -315,7 +315,7 @@ void InsaneRebel1::clearVideoBuffer() {
 //   1. O1LOGO.ANM ??? LucasArts logo
 //   2. O1OPEN.ANM ??? Star Wars opening crawl
 void InsaneRebel1::playIntroSequence() {
-	debugC(DEBUG_INSANE, "InsaneRebel1: Playing intro sequence");
+	debugC(DEBUG_INSANE, "InsaneRebel1::playIntroSequence: Playing intro sequence");
 
 	// LucasArts logo (original: PUSH 0x57cc, CALL FUN_1BA32 with flags 0x0420)
 	playCinematic("OPEN/O1LOGO.ANM");
@@ -347,7 +347,7 @@ void InsaneRebel1::playIntroSequence() {
 //     lives==0: L1DEATH ??? return to menu
 
 bool InsaneRebel1::runLevel1() {
-	debugC(DEBUG_INSANE, "InsaneRebel1: Running level 1");
+	debugC(DEBUG_INSANE, "InsaneRebel1::runLevel1: Running level 1");
 
 	_currentLevel = 0;
 	loadTuningForLevel(0);
@@ -448,7 +448,7 @@ bool InsaneRebel1::runLevel1() {
 }
 
 bool InsaneRebel1::runLevel2() {
-	debugC(DEBUG_INSANE, "InsaneRebel1: Running level 2");
+	debugC(DEBUG_INSANE, "InsaneRebel1::runLevel2: Running level 2");
 
 	_currentLevel = 1;
 	loadLevelSprites(2);
@@ -489,7 +489,7 @@ bool InsaneRebel1::runLevel2() {
 }
 
 bool InsaneRebel1::runLevel3() {
-	debugC(DEBUG_INSANE, "InsaneRebel1: Running level 3");
+	debugC(DEBUG_INSANE, "InsaneRebel1::runLevel3: Running level 3");
 
 	_currentLevel = 2;
 	loadLevelSprites(3);
@@ -530,7 +530,7 @@ bool InsaneRebel1::runLevel3() {
 }
 
 bool InsaneRebel1::runLevel4() {
-	debugC(DEBUG_INSANE, "InsaneRebel1: Running level 4");
+	debugC(DEBUG_INSANE, "InsaneRebel1::runLevel4: Running level 4");
 
 	_currentLevel = 3;
 	loadLevelSprites(4);
@@ -600,7 +600,7 @@ bool InsaneRebel1::runLevel4() {
 }
 
 bool InsaneRebel1::runLevel5() {
-	debugC(DEBUG_INSANE, "InsaneRebel1: Running level 5");
+	debugC(DEBUG_INSANE, "InsaneRebel1::runLevel5: Running level 5");
 
 	_currentLevel = 4;
 	loadLevelSprites(5);
@@ -682,7 +682,7 @@ bool InsaneRebel1::runLevel5() {
 }
 
 bool InsaneRebel1::runLevel6() {
-	debugC(DEBUG_INSANE, "InsaneRebel1: Running level 6");
+	debugC(DEBUG_INSANE, "InsaneRebel1::runLevel6: Running level 6");
 
 	_currentLevel = 5;
 	loadLevelSprites(6);
@@ -727,7 +727,7 @@ bool InsaneRebel1::runLevel6() {
 }
 
 bool InsaneRebel1::runLevel7() {
-	debugC(DEBUG_INSANE, "InsaneRebel1: Running level 7");
+	debugC(DEBUG_INSANE, "InsaneRebel1::runLevel7: Running level 7");
 
 	const char *const kLevel7Segments[] = {
 		"LVL7/L7PLAY1.ANM",
@@ -811,7 +811,7 @@ bool InsaneRebel1::runLevel7() {
 }
 
 bool InsaneRebel1::runLevel8() {
-	debugC(DEBUG_INSANE, "InsaneRebel1: Running level 8");
+	debugC(DEBUG_INSANE, "InsaneRebel1::runLevel8: Running level 8");
 
 	const char *const kLevel8Routes[] = {
 		"LVL8/L8PLAY.ANM",
@@ -887,7 +887,7 @@ bool InsaneRebel1::runLevel8() {
 }
 
 bool InsaneRebel1::runLevel9() {
-	debugC(DEBUG_INSANE, "InsaneRebel1: Running level 9");
+	debugC(DEBUG_INSANE, "InsaneRebel1::runLevel9: Running level 9");
 
 	// DOS RunLevel9Flow calls RandScaleByte(2) three times before the intro.
 	// That helper advances a byte seed with seed = seed * 9 + 0x35 and returns
@@ -1077,7 +1077,7 @@ bool InsaneRebel1::runLevel9() {
 }
 
 bool InsaneRebel1::runLevel10() {
-	debugC(DEBUG_INSANE, "InsaneRebel1: Running level 10");
+	debugC(DEBUG_INSANE, "InsaneRebel1::runLevel10: Running level 10");
 
 	_currentLevel = 9;
 	loadLevelSprites(10);
@@ -1118,7 +1118,7 @@ bool InsaneRebel1::runLevel10() {
 // Turret-style level. Single interactive phase with kill-count retry.
 // Original: L11INTRO ??? L11PLAY (turret, killCount>4 to pass) ??? L11RETRY ??? retry/L11END
 bool InsaneRebel1::runLevel11() {
-	debugC(DEBUG_INSANE, "InsaneRebel1: Running level 11");
+	debugC(DEBUG_INSANE, "InsaneRebel1::runLevel11: Running level 11");
 
 	_currentLevel = 10;
 	loadLevelSprites(11);
@@ -1175,7 +1175,7 @@ bool InsaneRebel1::runLevel11() {
 // Single interactive phase with mid-level retry mechanism.
 // Original: L12INTRO ??? L12PLAY ??? (retry at specific frame) ??? L12END
 bool InsaneRebel1::runLevel12() {
-	debugC(DEBUG_INSANE, "InsaneRebel1: Running level 12");
+	debugC(DEBUG_INSANE, "InsaneRebel1::runLevel12: Running level 12");
 
 	_currentLevel = 11;
 	loadLevelSprites(12);
@@ -1238,7 +1238,7 @@ bool InsaneRebel1::runLevel12() {
 // Flight level with enemy projectile system (original has 5-slot projectile tracking).
 // Original: L13INTRO ??? L13PLAY ??? L13END/L13NEW/L13DEATH
 bool InsaneRebel1::runLevel13() {
-	debugC(DEBUG_INSANE, "InsaneRebel1: Running level 13");
+	debugC(DEBUG_INSANE, "InsaneRebel1::runLevel13: Running level 13");
 
 	_currentLevel = 12;
 	loadLevelSprites(13);
@@ -1281,7 +1281,7 @@ bool InsaneRebel1::runLevel13() {
 // Original: L14INTRO ??? L14PLAY ??? L14PLAY2 ??? optional L14PLY2B splice
 //   ??? L14END/L14NEW/L14DEATH
 bool InsaneRebel1::runLevel14() {
-	debugC(DEBUG_INSANE, "InsaneRebel1: Running level 14");
+	debugC(DEBUG_INSANE, "InsaneRebel1::runLevel14: Running level 14");
 
 	_currentLevel = 13;
 	loadLevelSprites(14);
@@ -1355,7 +1355,7 @@ bool InsaneRebel1::runLevel14() {
 // Original: L15INTRO ??? L15PLAY1 (trench run) ??? L15INTR2 (torpedo lock cutscene)
 //   ??? L15PLAY2 (final approach + torpedo) ??? L15END1/L15NEW/L15DEATH
 bool InsaneRebel1::runLevel15() {
-	debugC(DEBUG_INSANE, "InsaneRebel1: Running level 15");
+	debugC(DEBUG_INSANE, "InsaneRebel1::runLevel15: Running level 15");
 
 	_currentLevel = 14;
 	loadLevelSprites(15);
@@ -1401,7 +1401,7 @@ bool InsaneRebel1::runLevel15() {
 		}
 
 		if (_health >= 0 && !_torpedoFired) {
-			debugC(DEBUG_INSANE, "InsaneRebel1: Level 15 torpedo run ended without exhaust-port hit");
+			debugC(DEBUG_INSANE, "InsaneRebel1::runLevel15: Level 15 torpedo run ended without exhaust-port hit");
 			return false;
 		}
 
diff --git a/engines/scumm/smush/rebel/codec_ra2.cpp b/engines/scumm/smush/rebel/codec_ra2.cpp
index 7ca7dd2e0d1..44f454f88b4 100644
--- a/engines/scumm/smush/rebel/codec_ra2.cpp
+++ b/engines/scumm/smush/rebel/codec_ra2.cpp
@@ -274,7 +274,7 @@ void smushDecodeRA2Bomp(byte *dst, const byte *src, int left, int top, int width
 	}
 
 	if (!foundValidOffset) {
-		warning("Rebel2: Codec 45 couldn't find valid RLE offset, using offset 0");
+		warning("smushDecodeRA2Bomp: Codec 45 couldn't find valid RLE offset, using offset 0");
 	}
 
 	src += headerSkip;
diff --git a/engines/scumm/smush/rebel/smush_player_ra1.cpp b/engines/scumm/smush/rebel/smush_player_ra1.cpp
index de6aad330bf..6c6712b787c 100644
--- a/engines/scumm/smush/rebel/smush_player_ra1.cpp
+++ b/engines/scumm/smush/rebel/smush_player_ra1.cpp
@@ -581,7 +581,7 @@ bool SmushPlayerRebel1::handleGameCodecDecode(int codec, const uint8 *src, int l
 		return true;
 	}
 	default:
-		debugC(DEBUG_SMUSH, "SmushPlayer::decodeFrameObject: Skipping unknown codec %d (left=%d, top=%d, %dx%d)",
+		debugC(DEBUG_SMUSH, "SmushPlayerRebel1::handleGameCodecDecode: Skipping unknown codec %d (left=%d, top=%d, %dx%d)",
 			codec, left, top, width, height);
 		return true;
 	}
diff --git a/engines/scumm/smush/rebel/smush_player_ra2.cpp b/engines/scumm/smush/rebel/smush_player_ra2.cpp
index 53afda31784..6b3da4f7524 100644
--- a/engines/scumm/smush/rebel/smush_player_ra2.cpp
+++ b/engines/scumm/smush/rebel/smush_player_ra2.cpp
@@ -152,7 +152,7 @@ bool SmushPlayerRebel2::handleGameFetch(int32 subSize, Common::SeekableReadStrea
 	int16 ftchX = b.readSint16LE();
 	int16 ftchY = b.readSint16LE();
 
-	debugC(DEBUG_SMUSH, "SmushPlayer::handleFetch: frame=%d unknown=%d x=%d y=%d",
+	debugC(DEBUG_SMUSH, "SmushPlayerRebel2::handleGameFetch: frame=%d unknown=%d x=%d y=%d",
 		_frame, ftchUnknown, ftchX, ftchY);
 
 	// For Handler 25, skip FTCH because the frame buffer only contains the
@@ -162,14 +162,14 @@ bool SmushPlayerRebel2::handleGameFetch(int32 subSize, Common::SeekableReadStrea
 		InsaneRebel2 *rebel2 = static_cast<InsaneRebel2 *>(_insane);
 		int handler = rebel2->getHandler();
 		if (handler == 25) {
-			debugC(DEBUG_SMUSH, "SmushPlayer::handleFetch: Skipping FTCH for Handler 25 - preserving overlays");
+			debugC(DEBUG_SMUSH, "SmushPlayerRebel2::handleGameFetch: Skipping FTCH for Handler 25 - preserving overlays");
 			return true;
 		}
 	}
 
 	// Re-decode stored FOBJ data with current offsets (matching original FUN_004246d0).
 	if (_storedFobjData != nullptr) {
-		debugC(DEBUG_SMUSH, "SmushPlayer FTCH: Re-decoding stored FOBJ codec=%d pos=(%d,%d) size=%dx%d dataSize=%d",
+		debugC(DEBUG_SMUSH, "SmushPlayerRebel2::handleGameFetch FTCH: Re-decoding stored FOBJ codec=%d pos=(%d,%d) size=%dx%d dataSize=%d",
 			_storedFobjCodec, _storedFobjLeft, _storedFobjTop,
 			_storedFobjWidth, _storedFobjHeight, _storedFobjDataSize);
 		decodeFrameObject(_storedFobjCodec, _storedFobjData,
@@ -177,7 +177,7 @@ bool SmushPlayerRebel2::handleGameFetch(int32 subSize, Common::SeekableReadStrea
 			_storedFobjWidth, _storedFobjHeight,
 			_storedFobjDataSize);
 	} else {
-		debugC(DEBUG_SMUSH, "SmushPlayer FTCH: No stored FOBJ data! (frame=%d)", _frame);
+		debugC(DEBUG_SMUSH, "SmushPlayerRebel2::handleGameFetch FTCH: No stored FOBJ data! (frame=%d)", _frame);
 	}
 
 	return true;
@@ -210,7 +210,7 @@ SmushFont *SmushPlayerRebel2::getGameFont(int font) {
 	if (font >= 0 && font < numFonts) {
 		_sf[font] = new SmushFont(_vm, ra2_fonts[font], true);
 	} else {
-		debugC(DEBUG_SMUSH, "SmushPlayer::getFont: RA2 unknown font %d, using TALKFONT", font);
+		debugC(DEBUG_SMUSH, "SmushPlayerRebel2::getGameFont: RA2 unknown font %d, using TALKFONT", font);
 		_sf[font] = new SmushFont(_vm, ra2_fonts[0], true);
 	}
 	return _sf[font];
@@ -392,7 +392,7 @@ bool SmushPlayerRebel2::handleGameAnimHeader(byte *headerContent) {
 	if (width == 0 && height == 0) {
 		_width = _vm->_screenWidth;
 		_height = _vm->_screenHeight;
-		debugC(DEBUG_SMUSH, "SmushPlayer::handleAnimHeader: RA2 AHDR has 0x0 dims - using screen size %dx%d", _width, _height);
+		debugC(DEBUG_SMUSH, "SmushPlayerRebel2::handleGameAnimHeader: RA2 AHDR has 0x0 dims - using screen size %dx%d", _width, _height);
 	} else {
 		_width = width;
 		_height = height;
@@ -415,10 +415,10 @@ void SmushPlayerRebel2::handleGameLoad(int32 subSize, Common::SeekableReadStream
  * The data is accumulated in a buffer and consumed by the audio system.
  */
 void SmushPlayerRebel2::handleLoad(int32 subSize, Common::SeekableReadStream &b) {
-	debugC(DEBUG_SMUSH, "SmushPlayer::handleLoad()");
+	debugC(DEBUG_SMUSH, "SmushPlayerRebel2::handleLoad()");
 
 	if (subSize < 10) {
-		warning("SmushPlayer::handleLoad: chunk too small (%d bytes)", subSize);
+		warning("SmushPlayerRebel2::handleLoad: chunk too small (%d bytes)", subSize);
 		return;
 	}
 
@@ -428,7 +428,7 @@ void SmushPlayerRebel2::handleLoad(int32 subSize, Common::SeekableReadStream &b)
 
 	int32 dataSize = subSize - 10;
 
-	debugC(DEBUG_SMUSH, "SmushPlayer::handleLoad: chunk %d/%d, dataSize=%d, bufferOffset=%d",
+	debugC(DEBUG_SMUSH, "SmushPlayerRebel2::handleLoad: chunk %d/%d, dataSize=%d, bufferOffset=%d",
 		chunkIndex, totalChunks, dataSize, _loadBufferOffset);
 
 	// First chunk in sequence - reset buffer state
@@ -444,26 +444,26 @@ void SmushPlayerRebel2::handleLoad(int32 subSize, Common::SeekableReadStream &b)
 			_loadBufferSize = estimatedSize;
 			_loadBuffer = (byte *)malloc(_loadBufferSize);
 			if (_loadBuffer == nullptr) {
-				warning("SmushPlayer::handleLoad: Failed to allocate %d bytes for LOAD buffer",
+				warning("SmushPlayerRebel2::handleLoad: Failed to allocate %d bytes for LOAD buffer",
 					_loadBufferSize);
 				_loadBufferSize = 0;
 				return;
 			}
-			debugC(DEBUG_SMUSH, "SmushPlayer::handleLoad: Allocated %d bytes for LOAD buffer",
+			debugC(DEBUG_SMUSH, "SmushPlayerRebel2::handleLoad: Allocated %d bytes for LOAD buffer",
 				_loadBufferSize);
 		}
 	}
 
 	// Check sequential order
 	if (_lastLoadChunkIdx + 1 != chunkIndex) {
-		debugC(DEBUG_SMUSH, "SmushPlayer::handleLoad: Non-sequential chunk %d (expected %d), skipping",
+		debugC(DEBUG_SMUSH, "SmushPlayerRebel2::handleLoad: Non-sequential chunk %d (expected %d), skipping",
 			chunkIndex, _lastLoadChunkIdx + 1);
 		return;
 	}
 
 	// Check buffer capacity
 	if (_loadBuffer == nullptr || _loadBufferOffset + dataSize >= _loadBufferSize) {
-		warning("SmushPlayer::handleLoad: Buffer overflow - offset=%d size=%d limit=%d",
+		warning("SmushPlayerRebel2::handleLoad: Buffer overflow - offset=%d size=%d limit=%d",
 			_loadBufferOffset, dataSize, _loadBufferSize);
 		return;
 	}
@@ -473,10 +473,10 @@ void SmushPlayerRebel2::handleLoad(int32 subSize, Common::SeekableReadStream &b)
 	_loadBufferOffset += dataSize;
 	_lastLoadChunkIdx = chunkIndex;
 
-	debugC(DEBUG_SMUSH, "SmushPlayer::handleLoad: Accumulated %d bytes total", _loadBufferOffset);
+	debugC(DEBUG_SMUSH, "SmushPlayerRebel2::handleLoad: Accumulated %d bytes total", _loadBufferOffset);
 
 	if (chunkIndex == totalChunks - 1) {
-		debugC(DEBUG_SMUSH, "SmushPlayer::handleLoad: Sequence complete - %d chunks, %d bytes total",
+		debugC(DEBUG_SMUSH, "SmushPlayerRebel2::handleLoad: Sequence complete - %d chunks, %d bytes total",
 			totalChunks, _loadBufferOffset);
 	}
 }
@@ -490,7 +490,7 @@ void SmushPlayerRebel2::ra2HandleTextResource(const char *str, int fontId, int c
 	ensureMultiFont();
 	_multiFont->setDefaultFont(fontId);
 
-	debugC(DEBUG_SMUSH, "SmushPlayer::handleTextResource: RA2 TRES frame=%d fontId=%d color=%d flags=0x%x flg=%d pos=(%d,%d) clip=(%d,%d,%d,%d) str=\"%.40s\"",
+	debugC(DEBUG_SMUSH, "SmushPlayerRebel2::ra2HandleTextResource: RA2 TRES frame=%d fontId=%d color=%d flags=0x%x flg=%d pos=(%d,%d) clip=(%d,%d,%d,%d) str=\"%.40s\"",
 		  _frame, fontId, color, (int)flg, (int)flg, pos_x, pos_y, left, top, width, height, str);
 
 	if (flg & kStyleWordWrap) {
@@ -521,24 +521,24 @@ void SmushPlayerRebel2::ra2SelectFrameBuffer(int width, int height) {
 			_height = height;
 			// Zero-fill the new buffer to avoid garbage in areas not written by FOBJ codec
 			memset(_specialBuffer, 0, bufSize);
-			debugC(DEBUG_SMUSH, "SmushPlayer: Allocated new _specialBuffer %dx%d (%d bytes)", width, height, bufSize);
+			debugC(DEBUG_SMUSH, "SmushPlayerRebel2::ra2SelectFrameBuffer: Allocated new _specialBuffer %dx%d (%d bytes)", width, height, bufSize);
 		}
 	}
 
 	if (bufSize > _vm->_screenWidth * _vm->_screenHeight &&
 	    _specialBuffer != nullptr && _specialBufferSize >= bufSize) {
 		_dst = _specialBuffer;
-		debugC(DEBUG_SMUSH, "SmushPlayer: Using _specialBuffer for oversized FOBJ %dx%d", width, height);
+		debugC(DEBUG_SMUSH, "SmushPlayerRebel2::ra2SelectFrameBuffer: Using _specialBuffer for oversized FOBJ %dx%d", width, height);
 	} else {
 		if (_specialBuffer == nullptr) {
 			VirtScreen *vs = &_vm->_virtscr[kMainVirtScreen];
 			_dst = vs->getPixels(0, 0);
-			debugC(DEBUG_SMUSH, "SmushPlayer: Reset _dst to virtual screen for FOBJ %dx%d at (%d,%d) _dst=%p",
+			debugC(DEBUG_SMUSH, "SmushPlayerRebel2::ra2SelectFrameBuffer: Reset _dst to virtual screen for FOBJ %dx%d at (%d,%d) _dst=%p",
 				width, height, 0, 0, (void*)_dst);
 		} else {
 			// Large frame was in this video, use _specialBuffer for compositing
 			_dst = _specialBuffer;
-			debugC(DEBUG_SMUSH, "SmushPlayer: Using _specialBuffer for small FOBJ %dx%d (compositing with large frame)",
+			debugC(DEBUG_SMUSH, "SmushPlayerRebel2::ra2SelectFrameBuffer: Using _specialBuffer for small FOBJ %dx%d (compositing with large frame)",
 				width, height);
 		}
 	}
@@ -605,7 +605,7 @@ void SmushPlayerRebel2::ra2HandleGost(int32 subSize, Common::SeekableReadStream
 	int16 ghostY = b.readSint16LE();
 
 	if (!_hasFrameFobjForGost || _lastFobjData == nullptr || _lastFobjDataSize <= 0) {
-		debugC(DEBUG_SMUSH, "SmushPlayer GOST: frame=%d ignored (no current-frame FOBJ cached)", _frame);
+		debugC(DEBUG_SMUSH, "SmushPlayerRebel2::ra2HandleGost GOST: frame=%d ignored (no current-frame FOBJ cached)", _frame);
 		return;
 	}
 
@@ -623,7 +623,7 @@ void SmushPlayerRebel2::ra2HandleGost(int32 subSize, Common::SeekableReadStream
 	int left = _lastFobjLeft + ghostX;
 	int top = _lastFobjTop + ghostY;
 
-	debugC(DEBUG_SMUSH, "SmushPlayer GOST: frame=%d type=%d flags=0x%04x gostPos=(%d,%d) basePos=(%d,%d) finalPos=(%d,%d) size=%dx%d codec=%d",
+	debugC(DEBUG_SMUSH, "SmushPlayerRebel2::ra2HandleGost GOST: frame=%d type=%d flags=0x%04x gostPos=(%d,%d) basePos=(%d,%d) finalPos=(%d,%d) size=%dx%d codec=%d",
 		_frame, ghostType, priorityFlags, ghostX, ghostY,
 		_lastFobjLeft, _lastFobjTop, left, top,
 		_lastFobjWidth, _lastFobjHeight, _lastFobjCodec);
@@ -691,7 +691,7 @@ bool SmushPlayerRebel2::handleGameStoreFrame() {
 }
 
 void SmushPlayerRebel2::handleGameFrameObjectPre(int codec, int left, int top, int width, int height, int dataSize) {
-	debugC(DEBUG_SMUSH, "SmushPlayer FOBJ: frame=%d codec=%d pos=(%d,%d) size=%dx%d dataSize=%d storeFrame=%d _width=%d _height=%d",
+	debugC(DEBUG_SMUSH, "SmushPlayerRebel2::handleGameFrameObjectPre FOBJ: frame=%d codec=%d pos=(%d,%d) size=%dx%d dataSize=%d storeFrame=%d _width=%d _height=%d",
 		_frame, codec, left, top, width, height, dataSize, _storeFrame, _width, _height);
 }
 
@@ -710,7 +710,7 @@ void SmushPlayerRebel2::handleGameFrameStart() {
 bool SmushPlayerRebel2::handleGameSkipChunk(uint32 subType, int32 subSize, Common::SeekableReadStream &b) {
 	if (_skipNext) {
 		_skipNext = false;
-		debugC(DEBUG_SMUSH, "SmushPlayer::handleFrame: SKIP consumed chunk %s frame=%d", tag2str(subType), _frame);
+		debugC(DEBUG_SMUSH, "SmushPlayerRebel2::handleGameSkipChunk: SKIP consumed chunk %s frame=%d", tag2str(subType), _frame);
 		return true;
 	}
 	return false;


Commit: e44a371ebd858f7fb84d427b2c478bafefd1fa4e
    https://github.com/scummvm/scummvm/commit/e44a371ebd858f7fb84d427b2c478bafefd1fa4e
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-01T09:43:41+02:00

Commit Message:
SCUMM: RA: removed some redundant/useless debug statement

Changed paths:
    engines/scumm/insane/rebel1/menu.cpp
    engines/scumm/insane/rebel1/render.cpp
    engines/scumm/insane/rebel1/runlevels.cpp
    engines/scumm/smush/rebel/smush_player_ra2.cpp


diff --git a/engines/scumm/insane/rebel1/menu.cpp b/engines/scumm/insane/rebel1/menu.cpp
index 3ded18a95d8..cf8a11e568a 100644
--- a/engines/scumm/insane/rebel1/menu.cpp
+++ b/engines/scumm/insane/rebel1/menu.cpp
@@ -676,7 +676,6 @@ bool InsaneRebel1::notifyEvent(const Common::Event &event) {
 				return true;
 			}
 
-			debugC(DEBUG_INSANE, "InsaneRebel1::notifyEvent: ESC pressed during gameplay - opening ScummVM menu");
 			const bool wasPaused = _player->_paused;
 			if (!wasPaused)
 				_player->pause();
@@ -689,13 +688,11 @@ bool InsaneRebel1::notifyEvent(const Common::Event &event) {
 		if (_interactiveVideoActive && !_menuActive &&
 			event.kbd.keycode == Common::KEYCODE_s &&
 			event.kbd.hasFlags(Common::KBD_SHIFT)) {
-			debugC(DEBUG_INSANE, "InsaneRebel1::notifyEvent: Shift+S pressed - skipping gameplay section");
 			_vm->_smushVideoShouldFinish = true;
 			return true;
 		}
 
 		if (!_interactiveVideoActive && event.kbd.keycode == Common::KEYCODE_ESCAPE) {
-			debugC(DEBUG_INSANE, "InsaneRebel1::notifyEvent: ESC pressed - skipping cinematic");
 			_vm->_smushVideoShouldFinish = true;
 			return true;
 		}
@@ -929,8 +926,6 @@ bool InsaneRebel1::runTextEntryMenuLoop() {
 }
 
 int InsaneRebel1::runMainMenu() {
-	debugC(DEBUG_INSANE, "InsaneRebel1::runMainMenu: Main menu");
-
 	_menuSelection = 0;
 	while (!shouldAbortGameFlow()) {
 		playMenuBackground();
diff --git a/engines/scumm/insane/rebel1/render.cpp b/engines/scumm/insane/rebel1/render.cpp
index 110c3911e43..ac5c801a121 100644
--- a/engines/scumm/insane/rebel1/render.cpp
+++ b/engines/scumm/insane/rebel1/render.cpp
@@ -1731,9 +1731,6 @@ void InsaneRebel1::renderHUD(byte *dst, int pitch, int width, int height) {
 				memcpy(d, s, drawW);
 			}
 		}
-
-		debug(5, "RA1 HUD: drawn at (%d,%d) size=%dx%d",
-			hudPlateX, hudPlateY, bar.width, bar.height);
 	}
 
 	// Draw health bar from FUN_1BBCB (0x1BBCB) + FUN_21D66 (0x21D66):
diff --git a/engines/scumm/insane/rebel1/runlevels.cpp b/engines/scumm/insane/rebel1/runlevels.cpp
index 0e9f86c23c0..cb9c68b8058 100644
--- a/engines/scumm/insane/rebel1/runlevels.cpp
+++ b/engines/scumm/insane/rebel1/runlevels.cpp
@@ -315,8 +315,6 @@ void InsaneRebel1::clearVideoBuffer() {
 //   1. O1LOGO.ANM ??? LucasArts logo
 //   2. O1OPEN.ANM ??? Star Wars opening crawl
 void InsaneRebel1::playIntroSequence() {
-	debugC(DEBUG_INSANE, "InsaneRebel1::playIntroSequence: Playing intro sequence");
-
 	// LucasArts logo (original: PUSH 0x57cc, CALL FUN_1BA32 with flags 0x0420)
 	playCinematic("OPEN/O1LOGO.ANM");
 	if (shouldAbortGameFlow())
@@ -347,8 +345,6 @@ void InsaneRebel1::playIntroSequence() {
 //     lives==0: L1DEATH ??? return to menu
 
 bool InsaneRebel1::runLevel1() {
-	debugC(DEBUG_INSANE, "InsaneRebel1::runLevel1: Running level 1");
-
 	_currentLevel = 0;
 	loadTuningForLevel(0);
 	loadLevelSprites(1);
@@ -448,8 +444,6 @@ bool InsaneRebel1::runLevel1() {
 }
 
 bool InsaneRebel1::runLevel2() {
-	debugC(DEBUG_INSANE, "InsaneRebel1::runLevel2: Running level 2");
-
 	_currentLevel = 1;
 	loadLevelSprites(2);
 	// DOS RunLevel2Flow launches L2PLAY.ANM with gameplay selector 2.
@@ -489,8 +483,6 @@ bool InsaneRebel1::runLevel2() {
 }
 
 bool InsaneRebel1::runLevel3() {
-	debugC(DEBUG_INSANE, "InsaneRebel1::runLevel3: Running level 3");
-
 	_currentLevel = 2;
 	loadLevelSprites(3);
 	// DOS RunLevel3Flow launches L3PLAY.ANM with gameplay selector 3.
@@ -530,8 +522,6 @@ bool InsaneRebel1::runLevel3() {
 }
 
 bool InsaneRebel1::runLevel4() {
-	debugC(DEBUG_INSANE, "InsaneRebel1::runLevel4: Running level 4");
-
 	_currentLevel = 3;
 	loadLevelSprites(4);
 	// DOS RunLevel4Flow launches L4PLAY1.ANM with selector 4 and L4PLAY2.ANM with selector 5.
@@ -600,8 +590,6 @@ bool InsaneRebel1::runLevel4() {
 }
 
 bool InsaneRebel1::runLevel5() {
-	debugC(DEBUG_INSANE, "InsaneRebel1::runLevel5: Running level 5");
-
 	_currentLevel = 4;
 	loadLevelSprites(5);
 	// DOS RunLevel5Flow passes segment 6 for L5PLAY and segment 7 for L5PLAY2.
@@ -682,8 +670,6 @@ bool InsaneRebel1::runLevel5() {
 }
 
 bool InsaneRebel1::runLevel6() {
-	debugC(DEBUG_INSANE, "InsaneRebel1::runLevel6: Running level 6");
-
 	_currentLevel = 5;
 	loadLevelSprites(6);
 	// DOS RunLevel6Flow starts L6PLAY with PlayAnmFile(..., 8), so chapter 6
@@ -727,8 +713,6 @@ bool InsaneRebel1::runLevel6() {
 }
 
 bool InsaneRebel1::runLevel7() {
-	debugC(DEBUG_INSANE, "InsaneRebel1::runLevel7: Running level 7");
-
 	const char *const kLevel7Segments[] = {
 		"LVL7/L7PLAY1.ANM",
 		"LVL7/L7PLAY2.ANM",
@@ -811,8 +795,6 @@ bool InsaneRebel1::runLevel7() {
 }
 
 bool InsaneRebel1::runLevel8() {
-	debugC(DEBUG_INSANE, "InsaneRebel1::runLevel8: Running level 8");
-
 	const char *const kLevel8Routes[] = {
 		"LVL8/L8PLAY.ANM",
 		"LVL8/L8PLAY2.ANM",
@@ -887,8 +869,6 @@ bool InsaneRebel1::runLevel8() {
 }
 
 bool InsaneRebel1::runLevel9() {
-	debugC(DEBUG_INSANE, "InsaneRebel1::runLevel9: Running level 9");
-
 	// DOS RunLevel9Flow calls RandScaleByte(2) three times before the intro.
 	// That helper advances a byte seed with seed = seed * 9 + 0x35 and returns
 	// (2 * seed) >> 8. Do not use ScummVM's session RNG here: it can turn the
@@ -1077,8 +1057,6 @@ bool InsaneRebel1::runLevel9() {
 }
 
 bool InsaneRebel1::runLevel10() {
-	debugC(DEBUG_INSANE, "InsaneRebel1::runLevel10: Running level 10");
-
 	_currentLevel = 9;
 	loadLevelSprites(10);
 	// DOS RunLevel10Flow starts L10PLAY.ANM with initLevelFlag=0x0D.
@@ -1118,8 +1096,6 @@ bool InsaneRebel1::runLevel10() {
 // Turret-style level. Single interactive phase with kill-count retry.
 // Original: L11INTRO ??? L11PLAY (turret, killCount>4 to pass) ??? L11RETRY ??? retry/L11END
 bool InsaneRebel1::runLevel11() {
-	debugC(DEBUG_INSANE, "InsaneRebel1::runLevel11: Running level 11");
-
 	_currentLevel = 10;
 	loadLevelSprites(11);
 	// DOS RunLevel11Flow starts L11PLAY.ANM with initLevelFlag=0x0E.
@@ -1175,8 +1151,6 @@ bool InsaneRebel1::runLevel11() {
 // Single interactive phase with mid-level retry mechanism.
 // Original: L12INTRO ??? L12PLAY ??? (retry at specific frame) ??? L12END
 bool InsaneRebel1::runLevel12() {
-	debugC(DEBUG_INSANE, "InsaneRebel1::runLevel12: Running level 12");
-
 	_currentLevel = 11;
 	loadLevelSprites(12);
 	// DOS RunLevel12Flow starts L12PLAY.ANM with initLevelFlag=0x0F.
@@ -1238,8 +1212,6 @@ bool InsaneRebel1::runLevel12() {
 // Flight level with enemy projectile system (original has 5-slot projectile tracking).
 // Original: L13INTRO ??? L13PLAY ??? L13END/L13NEW/L13DEATH
 bool InsaneRebel1::runLevel13() {
-	debugC(DEBUG_INSANE, "InsaneRebel1::runLevel13: Running level 13");
-
 	_currentLevel = 12;
 	loadLevelSprites(13);
 	loadRA1Nut("LVL13/L13LASR2.NUT", _enemyLaserBank);
@@ -1281,8 +1253,6 @@ bool InsaneRebel1::runLevel13() {
 // Original: L14INTRO ??? L14PLAY ??? L14PLAY2 ??? optional L14PLY2B splice
 //   ??? L14END/L14NEW/L14DEATH
 bool InsaneRebel1::runLevel14() {
-	debugC(DEBUG_INSANE, "InsaneRebel1::runLevel14: Running level 14");
-
 	_currentLevel = 13;
 	loadLevelSprites(14);
 	// DOS RunLevel14Flow uses selector 0x11 for L14PLAY and 0x12 for L14PLAY2.
@@ -1355,8 +1325,6 @@ bool InsaneRebel1::runLevel14() {
 // Original: L15INTRO ??? L15PLAY1 (trench run) ??? L15INTR2 (torpedo lock cutscene)
 //   ??? L15PLAY2 (final approach + torpedo) ??? L15END1/L15NEW/L15DEATH
 bool InsaneRebel1::runLevel15() {
-	debugC(DEBUG_INSANE, "InsaneRebel1::runLevel15: Running level 15");
-
 	_currentLevel = 14;
 	loadLevelSprites(15);
 	// DOS RunLevel1GameLoop uses selector 0x13 for L15PLAY1 and 0x14 for L15PLAY2.
diff --git a/engines/scumm/smush/rebel/smush_player_ra2.cpp b/engines/scumm/smush/rebel/smush_player_ra2.cpp
index 6b3da4f7524..dc0ffb3b21b 100644
--- a/engines/scumm/smush/rebel/smush_player_ra2.cpp
+++ b/engines/scumm/smush/rebel/smush_player_ra2.cpp
@@ -490,8 +490,8 @@ void SmushPlayerRebel2::ra2HandleTextResource(const char *str, int fontId, int c
 	ensureMultiFont();
 	_multiFont->setDefaultFont(fontId);
 
-	debugC(DEBUG_SMUSH, "SmushPlayerRebel2::ra2HandleTextResource: RA2 TRES frame=%d fontId=%d color=%d flags=0x%x flg=%d pos=(%d,%d) clip=(%d,%d,%d,%d) str=\"%.40s\"",
-		  _frame, fontId, color, (int)flg, (int)flg, pos_x, pos_y, left, top, width, height, str);
+	debugC(DEBUG_SMUSH, "SmushPlayerRebel2::ra2HandleTextResource: RA2 TRES frame=%d fontId=%d color=%d flags=0x%x pos=(%d,%d) clip=(%d,%d,%d,%d) str=\"%.40s\"",
+		  _frame, fontId, color, (int)flg, pos_x, pos_y, left, top, width, height, str);
 
 	if (flg & kStyleWordWrap) {
 		Common::Rect clipRect(MAX<int>(0, left), MAX<int>(0, top), MIN<int>(left + width, _width), MIN<int>(top + height, _height));
@@ -533,8 +533,8 @@ void SmushPlayerRebel2::ra2SelectFrameBuffer(int width, int height) {
 		if (_specialBuffer == nullptr) {
 			VirtScreen *vs = &_vm->_virtscr[kMainVirtScreen];
 			_dst = vs->getPixels(0, 0);
-			debugC(DEBUG_SMUSH, "SmushPlayerRebel2::ra2SelectFrameBuffer: Reset _dst to virtual screen for FOBJ %dx%d at (%d,%d) _dst=%p",
-				width, height, 0, 0, (void*)_dst);
+			debugC(DEBUG_SMUSH, "SmushPlayerRebel2::ra2SelectFrameBuffer: Reset _dst to virtual screen for FOBJ %dx%d at (%d,%d)",
+				width, height, 0, 0);
 		} else {
 			// Large frame was in this video, use _specialBuffer for compositing
 			_dst = _specialBuffer;



From noreply at scummvm.org  Mon Jun  1 09:35:36 2026
From: noreply at scummvm.org (ScummVM-Translations)
Date: Mon,  1 Jun 2026 09:35:36 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 e0b2dabb2b974e3e3a59e896e16ae6eb3b95eef7
Message-ID: <20260601093536.AF298700591@ron.scummvm.net>

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:
5a1c3ff325 I18N: Update translation (Spanish)
e0b2dabb2b I18N: Update translation (French)


Commit: 5a1c3ff325702fe72f749281a6190fcba6a643df
    https://github.com/scummvm/scummvm/commit/5a1c3ff325702fe72f749281a6190fcba6a643df
Author: IlDucci (pajaroloco_2 at hotmail.com)
Date: 2026-06-01T09:35:27Z

Commit Message:
I18N: Update translation (Spanish)

Currently translated at 100.0% (3202 of 3202 strings)

Changed paths:
    po/es_ES.po


diff --git a/po/es_ES.po b/po/es_ES.po
index db6115d14bf..66eb78584aa 100644
--- a/po/es_ES.po
+++ b/po/es_ES.po
@@ -8,7 +8,7 @@ msgstr ""
 "Project-Id-Version: ScummVM 1.4.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
 "POT-Creation-Date: 2026-05-31 14:10+0000\n"
-"PO-Revision-Date: 2026-05-23 21:54+0000\n"
+"PO-Revision-Date: 2026-06-01 09:35+0000\n"
 "Last-Translator: IlDucci <pajaroloco_2 at hotmail.com>\n"
 "Language-Team: Spanish <https://translations.scummvm.org/projects/scummvm/"
 "scummvm/es/>\n"
@@ -13676,21 +13676,18 @@ msgid "Enable ambience sounds."
 msgstr "Activa los sonidos ambientales."
 
 #: engines/scumm/metaengine.cpp:882
-#, fuzzy
-#| msgctxt "Options menu"
-#| msgid "High resolution on"
 msgid "High resolution mode"
-msgstr "Alta resoluci??n activada"
+msgstr "Modo en alta resoluci??n"
 
 #: engines/scumm/metaengine.cpp:883
 msgid "Run the game in 640x400 high resolution mode instead of 320x200"
 msgstr ""
+"Muestra el juego en el modo de alta resoluci??n a 640??400 p??xeles, en vez de "
+"a 320??200"
 
 #: engines/scumm/metaengine.cpp:892
-#, fuzzy
-#| msgid "All levels will be available to play."
 msgid "All levels will be available without requiring passwords"
-msgstr "Permite el acceso a todos los niveles."
+msgstr "Desbloquea todos los niveles sin necesitar contrase??as"
 
 #: engines/scumm/metaengine.cpp:1025
 msgid "Down left"
@@ -13719,75 +13716,59 @@ msgstr "Truco para ganar pelea de motos"
 
 #: engines/scumm/metaengine.cpp:1106
 msgid "Rebel Assault controls"
-msgstr ""
+msgstr "Controles de Rebel Assault"
 
 #: engines/scumm/metaengine.cpp:1108 engines/scumm/metaengine.cpp:1170
 msgid "Aim up / menu up"
-msgstr ""
+msgstr "Apuntar arriba/Subir men??"
 
 #: engines/scumm/metaengine.cpp:1113 engines/scumm/metaengine.cpp:1176
 msgid "Aim down / menu down"
-msgstr ""
+msgstr "Apuntar abajo/Bajar men??"
 
 #: engines/scumm/metaengine.cpp:1118 engines/scumm/metaengine.cpp:1182
 msgid "Aim left / menu left"
-msgstr ""
+msgstr "Apuntar a izda./Men?? a izda."
 
 #: engines/scumm/metaengine.cpp:1123 engines/scumm/metaengine.cpp:1188
 msgid "Aim right / menu right"
-msgstr ""
+msgstr "Apuntar a dcha./Men?? a dcha."
 
 #: engines/scumm/metaengine.cpp:1128
-#, fuzzy
-#| msgid "Pick up"
 msgid "Stick up"
-msgstr "Recoger"
+msgstr "Stick hacia arriba"
 
 #: engines/scumm/metaengine.cpp:1134
-#, fuzzy
-#| msgid "Scroll down"
 msgid "Stick down"
-msgstr "Bajar"
+msgstr "Stick hacia abajo"
 
 #: engines/scumm/metaengine.cpp:1140
-#, fuzzy
-#| msgid "Slide left"
 msgid "Stick left"
-msgstr "Deslizarse a la izquierda"
+msgstr "Stick hacia la izquierda"
 
 #: engines/scumm/metaengine.cpp:1146
-#, fuzzy
-#| msgid "Slide right"
 msgid "Stick right"
-msgstr "Deslizarse a la derecha"
+msgstr "Stick hacia la derecha"
 
 #: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1194
-#, fuzzy
-#| msgid "Move / Select"
 msgid "Fire / select"
-msgstr "Mover/Seleccionar"
+msgstr "Disparar/Seleccionar"
 
 #: engines/scumm/metaengine.cpp:1157
-#, fuzzy
-#| msgid "Backspace"
 msgid "Back / skip"
-msgstr "Retroceso"
+msgstr "Volver/Omitir"
 
 #: engines/scumm/metaengine.cpp:1168
 msgid "Rebel Assault II controls"
-msgstr ""
+msgstr "Controles de Rebel Assault II"
 
 #: engines/scumm/metaengine.cpp:1199
-#, fuzzy
-#| msgid "Move back"
 msgid "Cover / back"
-msgstr "Retroceder"
+msgstr "Cubrirse/Volver"
 
 #: engines/scumm/metaengine.cpp:1204
-#, fuzzy
-#| msgid "Skip main menu"
 msgid "Skip / menu"
-msgstr "Omitir men?? principal"
+msgstr "Omitir/Men??"
 
 #: engines/scumm/saveload.cpp:1990
 #, c-format


Commit: e0b2dabb2b974e3e3a59e896e16ae6eb3b95eef7
    https://github.com/scummvm/scummvm/commit/e0b2dabb2b974e3e3a59e896e16ae6eb3b95eef7
Author: Purple T (ZEONK at hotmail.com)
Date: 2026-06-01T09:35:28Z

Commit Message:
I18N: Update translation (French)

Currently translated at 100.0% (3202 of 3202 strings)

Changed paths:
    po/fr_FR.po


diff --git a/po/fr_FR.po b/po/fr_FR.po
index d4b954e7f30..7f847ed26dc 100644
--- a/po/fr_FR.po
+++ b/po/fr_FR.po
@@ -8,7 +8,7 @@ msgstr ""
 "Project-Id-Version: ScummVM 1.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
 "POT-Creation-Date: 2026-05-31 14:10+0000\n"
-"PO-Revision-Date: 2026-05-24 13:22+0000\n"
+"PO-Revision-Date: 2026-06-01 09:35+0000\n"
 "Last-Translator: Purple T <ZEONK at hotmail.com>\n"
 "Language-Team: French <https://translations.scummvm.org/projects/scummvm/"
 "scummvm/fr/>\n"
@@ -13703,21 +13703,16 @@ msgid "Enable ambience sounds."
 msgstr "Active les sons d'ambiance."
 
 #: engines/scumm/metaengine.cpp:882
-#, fuzzy
-#| msgctxt "Options menu"
-#| msgid "High resolution on"
 msgid "High resolution mode"
-msgstr "Haute r??solution activ??e"
+msgstr "Mode Haute r??solution"
 
 #: engines/scumm/metaengine.cpp:883
 msgid "Run the game in 640x400 high resolution mode instead of 320x200"
-msgstr ""
+msgstr "Lance le jeu en 640x400 haute r??solution au lieu de 320x200"
 
 #: engines/scumm/metaengine.cpp:892
-#, fuzzy
-#| msgid "All levels will be available to play."
 msgid "All levels will be available without requiring passwords"
-msgstr "Tous les niveaux seront disponibles."
+msgstr "Tous les niveaux seront disponibles sans demander de mots de passe"
 
 #: engines/scumm/metaengine.cpp:1025
 msgid "Down left"
@@ -13746,75 +13741,59 @@ msgstr "Gagner les bagarres en b??cane (cheat)"
 
 #: engines/scumm/metaengine.cpp:1106
 msgid "Rebel Assault controls"
-msgstr ""
+msgstr "Contr??les Rebel Assault"
 
 #: engines/scumm/metaengine.cpp:1108 engines/scumm/metaengine.cpp:1170
 msgid "Aim up / menu up"
-msgstr ""
+msgstr "Vis??e haut / Menu haut"
 
 #: engines/scumm/metaengine.cpp:1113 engines/scumm/metaengine.cpp:1176
 msgid "Aim down / menu down"
-msgstr ""
+msgstr "Vis??e bas / Menu bas"
 
 #: engines/scumm/metaengine.cpp:1118 engines/scumm/metaengine.cpp:1182
 msgid "Aim left / menu left"
-msgstr ""
+msgstr "Vis??e gauche / menu gauche"
 
 #: engines/scumm/metaengine.cpp:1123 engines/scumm/metaengine.cpp:1188
 msgid "Aim right / menu right"
-msgstr ""
+msgstr "Vis??e droite / Menu droite"
 
 #: engines/scumm/metaengine.cpp:1128
-#, fuzzy
-#| msgid "Pick up"
 msgid "Stick up"
-msgstr "Prendre"
+msgstr "Manche haut"
 
 #: engines/scumm/metaengine.cpp:1134
-#, fuzzy
-#| msgid "Scroll down"
 msgid "Stick down"
-msgstr "D??filer vers le bas"
+msgstr "Manche bas"
 
 #: engines/scumm/metaengine.cpp:1140
-#, fuzzy
-#| msgid "Slide left"
 msgid "Stick left"
-msgstr "Faire un pas vers la gauche"
+msgstr "Manche gauche"
 
 #: engines/scumm/metaengine.cpp:1146
-#, fuzzy
-#| msgid "Slide right"
 msgid "Stick right"
-msgstr "Faire un pas vers la droite"
+msgstr "Manche droite"
 
 #: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1194
-#, fuzzy
-#| msgid "Move / Select"
 msgid "Fire / select"
-msgstr "Se d??placer / S??lectionner"
+msgstr "Tirer / S??lectionner"
 
 #: engines/scumm/metaengine.cpp:1157
-#, fuzzy
-#| msgid "Backspace"
 msgid "Back / skip"
-msgstr "Retour arri??re"
+msgstr "Retour / passer"
 
 #: engines/scumm/metaengine.cpp:1168
 msgid "Rebel Assault II controls"
-msgstr ""
+msgstr "Contr??les Rebel Assault II"
 
 #: engines/scumm/metaengine.cpp:1199
-#, fuzzy
-#| msgid "Move back"
 msgid "Cover / back"
-msgstr "Reculer"
+msgstr "Couverture / retour"
 
 #: engines/scumm/metaengine.cpp:1204
-#, fuzzy
-#| msgid "Skip main menu"
 msgid "Skip / menu"
-msgstr "Passer le menu principal"
+msgstr "Passer / menu"
 
 #: engines/scumm/saveload.cpp:1990
 #, c-format



From noreply at scummvm.org  Mon Jun  1 09:40:30 2026
From: noreply at scummvm.org (mduggan)
Date: Mon,  1 Jun 2026 09:40:30 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 ae8c870dea59928e37df83e5259239e2261c6528
Message-ID: <20260601094030.AEC01700591@ron.scummvm.net>

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

Summary:
ae8c870dea ACCESS: Fix sfx number for comic viewer


Commit: ae8c870dea59928e37df83e5259239e2261c6528
    https://github.com/scummvm/scummvm/commit/ae8c870dea59928e37df83e5259239e2261c6528
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2026-06-01T19:39:55+10:00

Commit Message:
ACCESS: Fix sfx number for comic viewer

Don't overwrite active room sfx, only add them to the end.

This fixes a crash if you clicked on something that plays sfx in the initial
comic.

Changed paths:
    engines/access/noctropolis/noctropolis_comicviewer.cpp
    engines/access/sound.cpp
    engines/access/sound.h


diff --git a/engines/access/noctropolis/noctropolis_comicviewer.cpp b/engines/access/noctropolis/noctropolis_comicviewer.cpp
index b47601a5a32..c099bea5a6a 100644
--- a/engines/access/noctropolis/noctropolis_comicviewer.cpp
+++ b/engines/access/noctropolis/noctropolis_comicviewer.cpp
@@ -78,7 +78,7 @@ PageResult ComicViewer::runPage(const ComicPage *page) {
 	_bubbleSprites = new SpriteResource(_vm, bubbleData);
 	delete bubbleData;
 
-	bool playedSound = false;
+	int loadedSoundIdx = -1;
 
 	while (result == kPageResultNone) {
 		_vm->_events->pollEvents();
@@ -128,11 +128,12 @@ PageResult ComicViewer::runPage(const ComicPage *page) {
 				const ComicBlock &hotspot = page->blocks[hotspotIndex];
 
 				if (hotspot.soundFileIndex >= 0) {
-					_vm->_sound->loadSoundTable(1, hotspot.soundFileIndex, hotspot.soundResIndex);
-					_vm->_sound->playSound(1);
-					playedSound = true;
+					if (loadedSoundIdx >= 0)
+						_vm->_sound->freeSound(loadedSoundIdx);
+					loadedSoundIdx = _vm->_sound->loadAndAddSound(hotspot.soundFileIndex, hotspot.soundResIndex);
+					_vm->_sound->playSound(loadedSoundIdx);
 				}
-				// TODO: Play hotspot sound
+
 				for (int bubbleIndex = 0; bubbleIndex < hotspot.numBoxes; bubbleIndex++) {
 					const ComicBox &bubble = hotspot.boxes[bubbleIndex];
 					// Slightly lazy, just save/restore whole screen when
@@ -157,9 +158,8 @@ PageResult ComicViewer::runPage(const ComicPage *page) {
 		}
 	}
 
-	if (playedSound) {
-		_vm->_sound->freeSound(1);
-	}
+	if (loadedSoundIdx >= 0)
+		_vm->_sound->freeSound(loadedSoundIdx);
 
 	delete _bubbleSprites;
 
diff --git a/engines/access/sound.cpp b/engines/access/sound.cpp
index 0f7f4dc861a..b54bb14c776 100644
--- a/engines/access/sound.cpp
+++ b/engines/access/sound.cpp
@@ -84,13 +84,14 @@ void SoundManager::loadSoundTable(int idx, int fileNum, int subfile, int priorit
 	_soundTable[idx] = SoundEntry(soundResource, priority, fileNum, subfile);
 }
 
-void SoundManager::loadAndAddSound(const FileIdent &ident, int priority) {
-	loadAndAddSound(ident._fileNum, ident._subFile);
+int SoundManager::loadAndAddSound(const FileIdent &ident, int priority) {
+	return loadAndAddSound(ident._fileNum, ident._subFile);
 }
 
-void SoundManager::loadAndAddSound(int fileNum, int subfile, int priority) {
+int SoundManager::loadAndAddSound(int fileNum, int subfile, int priority) {
 	Resource *res = _vm->_files->loadFile(fileNum, subfile);
 	_soundTable.push_back(SoundEntry(res, priority, fileNum, subfile));
+	return _soundTable.size() - 1;
 }
 
 bool SoundManager::hasLoadedSound(const FileIdent &ident) const {
@@ -107,7 +108,10 @@ void SoundManager::freeSound(int idx) {
 	stopSound();
 	assert(!isSoundQueued(idx));
 	delete _soundTable[idx]._res;
-	_soundTable[idx] = SoundEntry();
+	if (idx == (int)_soundTable.size() - 1)
+		_soundTable.pop_back();
+	else
+		_soundTable[idx] = SoundEntry();
 }
 
 void SoundManager::playSound(int soundIndex, bool loop /* = false */) {
diff --git a/engines/access/sound.h b/engines/access/sound.h
index 61ab0d74c85..739d5c8a59e 100644
--- a/engines/access/sound.h
+++ b/engines/access/sound.h
@@ -79,8 +79,8 @@ public:
 	void loadSoundTable(int idx, int fileNum, int subfile, int priority = 1);
 
 	// load and add a single sound resource to the table
-	void loadAndAddSound(int fileNum, int subfile, int priority = 1);
-	void loadAndAddSound(const FileIdent &ident, int priority = 1);
+	int loadAndAddSound(int fileNum, int subfile, int priority = 1);
+	int loadAndAddSound(const FileIdent &ident, int priority = 1);
 
 	bool hasLoadedSound(const FileIdent &ident) const;
 



From noreply at scummvm.org  Mon Jun  1 11:15:59 2026
From: noreply at scummvm.org (neuromancer)
Date: Mon,  1 Jun 2026 11:15:59 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 c4980b5fc133e82742b50b85e8f2810131f0a12d
Message-ID: <20260601111559.A093D700591@ron.scummvm.net>

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:
fd84ee8ad9 SCUMM: RA1: allow mouse navigation in menus
3143a91c42 SCUMM: RA1: honor subtitles config
c4980b5fc1 SCUMM: RA2: honor subtitles config


Commit: fd84ee8ad906ab655792c72a9da6b58ba1ab039c
    https://github.com/scummvm/scummvm/commit/fd84ee8ad906ab655792c72a9da6b58ba1ab039c
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-01T13:15:46+02:00

Commit Message:
SCUMM: RA1: allow mouse navigation in menus

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


diff --git a/engines/scumm/insane/rebel1/menu.cpp b/engines/scumm/insane/rebel1/menu.cpp
index cf8a11e568a..35ecd26bc26 100644
--- a/engines/scumm/insane/rebel1/menu.cpp
+++ b/engines/scumm/insane/rebel1/menu.cpp
@@ -26,6 +26,8 @@
 
 #include "audio/mixer.h"
 
+#include "graphics/cursorman.h"
+
 #include "scumm/scumm_v7.h"
 #include "scumm/smush/smush_player.h"
 #include "scumm/insane/rebel1/rebel.h"
@@ -43,6 +45,14 @@ const int kRA1MenuFrameW = 0xdc;
 const int kRA1MenuFrameH = 0x0f;
 const int kRA1MenuRowH = 0x0f;
 const byte kRA1MenuFrameColor = 0xdf;
+// Highlight-frame geometry shared by the render*Overlay drawing and the mouse hit-testing
+// (clicking menu items is a ScummVM-exclusive feature, so the rects must stay in sync).
+const int kRA1MainMenuFrameYBase    = 0x2c;  // frame Y = (item + 1) * kRA1MenuRowH + this
+const int kRA1OptionsFrameYBase     = 0x1d;  // frame Y = (item + 1) * kRA1MenuRowH + this
+const int kRA1LevelSelectFrameYBase = 0x2c;  // frame Y = row * kRA1MenuRowH + this
+const int kRA1LevelSelectLeftX      = 20;
+const int kRA1LevelSelectRightX     = 170;
+const int kRA1LevelSelectColW       = 130;
 const uint32 kRA1JoystickAxisEscGuardMs = 250;
 // Original picker traversal uses fixed max indices, not strlen(): passcodes
 // stop at 0x1d and high-score names stop at 0x37.
@@ -496,6 +506,70 @@ bool InsaneRebel1::handleControllerMenuAxis(int16 oldAxisX, int16 oldAxisY) {
 	return false;
 }
 
+// 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
+// highlight frames drawn by the render*Overlay() functions, so they share the
+// kRA1*FrameYBase / kRA1LevelSelect* geometry constants. Returns true if consumed.
+bool InsaneRebel1::handleMenuMouse(const Common::Event &event) {
+	if (!_menuActive || _textEntryActive || _highScoresActive)
+		return false;
+	if (event.type != Common::EVENT_MOUSEMOVE && event.type != Common::EVENT_LBUTTONDOWN)
+		return false;
+
+	const int mx = event.mouse.x;
+	const int my = event.mouse.y;
+	int *selection = nullptr;
+	int hit = -1;
+
+	if (_levelSelectActive) {
+		selection = &_levelSelectSel;
+		for (int i = 0; i < kRA1LevelSelectItemCount; i++) {
+			const int col = i / kRA1LevelSelectRowsPerCol;
+			const int row = i % kRA1LevelSelectRowsPerCol;
+			const int frameX = (col == 0) ? kRA1LevelSelectLeftX : kRA1LevelSelectRightX;
+			const int frameY = row * kRA1MenuRowH + kRA1LevelSelectFrameYBase;
+			if (mx >= frameX && mx < frameX + kRA1LevelSelectColW &&
+				my >= frameY && my < frameY + kRA1MenuFrameH) {
+				hit = i;
+				break;
+			}
+		}
+	} else if (_optionsActive) {
+		selection = &_optionsSel;
+		for (int i = 0; i < kOptionsItemCount; i++) {
+			const int frameY = (i + 1) * kRA1MenuRowH + kRA1OptionsFrameYBase;
+			if (mx >= kRA1MenuFrameX && mx < kRA1MenuFrameX + kRA1MenuFrameW &&
+				my >= frameY && my < frameY + kRA1MenuFrameH) {
+				hit = i;
+				break;
+			}
+		}
+	} else {
+		selection = &_menuSelection;
+		for (int i = 0; i < kRA1MainMenuItemCount; i++) {
+			const int frameY = (i + 1) * kRA1MenuRowH + kRA1MainMenuFrameYBase;
+			if (mx >= kRA1MenuFrameX && mx < kRA1MenuFrameX + kRA1MenuFrameW &&
+				my >= frameY && my < frameY + kRA1MenuFrameH) {
+				hit = i;
+				break;
+			}
+		}
+	}
+
+	if (hit < 0)
+		return false;
+
+	_activeInputSource = kInputSourceMouse;
+	*selection = hit;
+
+	// A left click activates the hovered item, just like pressing accept.
+	if (event.type == Common::EVENT_LBUTTONDOWN)
+		handleMenuCommand(kRA1MenuCommandAccept);
+
+	return true;
+}
+
 bool InsaneRebel1::notifyEvent(const Common::Event &event) {
 	// Global ScummVM dialogs pause the engine while their modal event loop runs.
 	// Do not consume those mouse/key events as RA1 gameplay/menu input, or the
@@ -507,6 +581,10 @@ bool InsaneRebel1::notifyEvent(const Common::Event &event) {
 		_activeInputSource = kInputSourceMouse;
 	}
 
+	// ScummVM-exclusive feature: mouse navigation/clicking of the RA1 front-end menus.
+	if (handleMenuMouse(event))
+		return true;
+
 	if (event.type == Common::EVENT_JOYAXIS_MOTION) {
 		_lastJoystickAxisEventTime = _vm->_system->getMillis();
 		debugC(DEBUG_INSANE, "RA1 input raw-joy-axis: axis=%d pos=%d menu=%d gameplay=%d storedAxis=(%d,%d)",
@@ -772,7 +850,7 @@ void InsaneRebel1::renderOptionsOverlay(byte *dst, int pitch, int width, int hei
 
 		if (i == _optionsSel)
 			drawRebel1MenuFrame(dst, pitch, width, height,
-				kRA1MenuFrameX, (i + 1) * kRA1MenuRowH + 0x1d, kRA1MenuFrameW);
+				kRA1MenuFrameX, (i + 1) * kRA1MenuRowH + kRA1OptionsFrameYBase, kRA1MenuFrameW);
 	}
 }
 
@@ -801,9 +879,9 @@ void InsaneRebel1::renderLevelSelectOverlay(byte *dst, int pitch, int width, int
 	};
 
 	const int menuY = 0x2d;
-	const int leftFrameX = 20;
-	const int rightFrameX = 170;
-	const int columnW = 130;
+	const int leftFrameX = kRA1LevelSelectLeftX;
+	const int rightFrameX = kRA1LevelSelectRightX;
+	const int columnW = kRA1LevelSelectColW;
 
 	for (int i = 0; i < kRA1LevelSelectItemCount; i++) {
 		const int col = i / kRA1LevelSelectRowsPerCol;
@@ -817,7 +895,7 @@ void InsaneRebel1::renderLevelSelectOverlay(byte *dst, int pitch, int width, int
 
 		if (i == _levelSelectSel)
 			drawRebel1MenuFrame(dst, pitch, width, height,
-				frameX, row * kRA1MenuRowH + 0x2c, columnW);
+				frameX, row * kRA1MenuRowH + kRA1LevelSelectFrameYBase, columnW);
 	}
 }
 
@@ -847,13 +925,20 @@ void InsaneRebel1::renderMainMenuItems(byte *dst, int pitch, int width, int heig
 
 		if (i == _menuSelection)
 			drawRebel1MenuFrame(dst, pitch, width, height,
-				kRA1MenuFrameX, (i + 1) * kRA1MenuRowH + 0x2c, kRA1MenuFrameW);
+				kRA1MenuFrameX, (i + 1) * kRA1MenuRowH + kRA1MainMenuFrameYBase, kRA1MenuFrameW);
 	}
 }
 
 void InsaneRebel1::renderMainMenuOverlay(byte *dst, int pitch, int width, int height) {
 	_menuFrameCounter++;
 
+	// ScummVM-exclusive feature: the menus are mouse-clickable, so keep the default
+	// arrow cursor visible. SmushPlayer::play() hides the system cursor for the whole
+	// video (and re-hides it every video), so re-assert visibility each rendered frame
+	// while a menu overlay is on screen. The arrow bitmap/palette is set in
+	// playMenuBackground(); gameplay hides it again via captureInteractiveVideoInput().
+	CursorMan.showMouse(true);
+
 	if (_textEntryActive) {
 		renderTextEntryOverlay(dst, pitch, width, height);
 		return;
@@ -913,6 +998,12 @@ void InsaneRebel1::playMenuBackground() {
 	_menuActive = true;
 	_menuConfirmed = false;
 	_menuFrameCounter = 0;
+	// Show ScummVM's built-in arrow pointer for the (mouse-clickable) menus. Set it once
+	// here; renderMainMenuOverlay() re-asserts showMouse() each frame because the SMUSH
+	// player forces the cursor off while a video plays. disableCursorPalette(false) ensures
+	// the arrow's own CLUT palette is used rather than the game palette.
+	CursorMan.disableCursorPalette(false);
+	CursorMan.setDefaultArrowCursor();
 	clearVideoBuffer();
 	playCinematic("OPEN/O1OPTION.ANM");
 	_menuActive = false;
diff --git a/engines/scumm/insane/rebel1/rebel.h b/engines/scumm/insane/rebel1/rebel.h
index 22f93bd3e49..8bedfdff0e0 100644
--- a/engines/scumm/insane/rebel1/rebel.h
+++ b/engines/scumm/insane/rebel1/rebel.h
@@ -529,6 +529,8 @@ private:
 	bool handleMenuCommand(RA1MenuCommand command);
 	bool handleControllerMenuAction(ScummAction action);
 	bool handleControllerMenuAxis(int16 oldAxisX, int16 oldAxisY);
+	// ScummVM-exclusive feature: navigate/click the menus with the mouse.
+	bool handleMenuMouse(const Common::Event &event);
 	bool handleTextEntryAction(ScummAction action);
 	bool handleTextEntryKey(const Common::Event &event);
 	void playMenuBackground();


Commit: 3143a91c420e08db3fb020ff1d7de0c159b80b3c
    https://github.com/scummvm/scummvm/commit/3143a91c420e08db3fb020ff1d7de0c159b80b3c
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-01T13:15:46+02:00

Commit Message:
SCUMM: RA1: honor subtitles config

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


diff --git a/engines/scumm/insane/rebel1/rebel.cpp b/engines/scumm/insane/rebel1/rebel.cpp
index af3ecab61c7..9b4a89b174f 100644
--- a/engines/scumm/insane/rebel1/rebel.cpp
+++ b/engines/scumm/insane/rebel1/rebel.cpp
@@ -19,6 +19,7 @@
  *
  */
 
+#include "common/config-manager.h"
 #include "common/system.h"
 #include "common/events.h"
 #include "common/endian.h"
@@ -343,7 +344,10 @@ InsaneRebel1::InsaneRebel1(ScummEngine_v7 *scumm) : Insane(), _vm(scumm) {
 	_optRookieOneFemale = false;
 	_optMusicEnabled = !_vm->_mixer->isSoundTypeMuted(Audio::Mixer::kMusicSoundType);
 	_optSfxEnabled = !_vm->_mixer->isSoundTypeMuted(Audio::Mixer::kSFXSoundType);
-	_optTextEnabled = true;
+	// Initialize the dialogue-text (subtitles) toggle from ScummVM's global setting so the
+	// game and the in-game DIALOGUE TEXT menu label reflect it. The menu toggle writes the
+	// same "subtitles" key, and ra1HandleText() gates rendering on it.
+	_optTextEnabled = ConfMan.getBool("subtitles");
 	_optEnhancedControls = true;
 	_optControlsYFlip = false;
 	_optVolume = _vm->_mixer->getVolumeForSoundType(Audio::Mixer::kPlainSoundType) * 127 / Audio::Mixer::kMaxChannelVolume;
diff --git a/engines/scumm/smush/rebel/smush_player_ra1.cpp b/engines/scumm/smush/rebel/smush_player_ra1.cpp
index 6c6712b787c..aa213b17a65 100644
--- a/engines/scumm/smush/rebel/smush_player_ra1.cpp
+++ b/engines/scumm/smush/rebel/smush_player_ra1.cpp
@@ -24,6 +24,7 @@
 // Keep these in a dedicated file so the shared smush_player.cpp stays close
 // to upstream while RA1 behavior is isolated in one place.
 
+#include "common/config-manager.h"
 #include "common/endian.h"
 #include "common/memstream.h"
 
@@ -320,7 +321,13 @@ bool SmushPlayerRebel1::handleGameTextResource(uint32 subType, int32 subSize, Co
 	if (subType != MKTAG('T','E','X','T'))
 		return false;
 
-	ra1HandleText(subSize, b);
+	// RA1 dialogue subtitles. Only render them when subtitles are enabled ??? this honors
+	// both ScummVM's global "subtitles" setting and the in-game DIALOGUE TEXT toggle
+	// (which writes the same ConfMan key). Still return true so the base handler doesn't
+	// try to parse RA1's custom TEXT format. Querying ConfMan per chunk also lets the
+	// setting take effect mid-video.
+	if (ConfMan.getBool("subtitles"))
+		ra1HandleText(subSize, b);
 	return true;
 }
 


Commit: c4980b5fc133e82742b50b85e8f2810131f0a12d
    https://github.com/scummvm/scummvm/commit/c4980b5fc133e82742b50b85e8f2810131f0a12d
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-01T13:15:46+02:00

Commit Message:
SCUMM: RA2: honor subtitles config

Changed paths:
    engines/scumm/insane/rebel2/iact.cpp
    engines/scumm/insane/rebel2/menu.cpp
    engines/scumm/insane/rebel2/rebel.cpp
    engines/scumm/smush/rebel/smush_player_ra2.cpp


diff --git a/engines/scumm/insane/rebel2/iact.cpp b/engines/scumm/insane/rebel2/iact.cpp
index fd69da34581..114ecc49136 100644
--- a/engines/scumm/insane/rebel2/iact.cpp
+++ b/engines/scumm/insane/rebel2/iact.cpp
@@ -19,6 +19,7 @@
  *
  */
 
+#include "common/config-manager.h"
 #include "common/system.h"
 #include "common/memstream.h"
 #include "common/util.h"
@@ -2510,13 +2511,18 @@ void InsaneRebel2::iactRebel2Opcode9(byte *renderBitmap, Common::SeekableReadStr
 	}
 	convertedText[dstIdx] = '\0';
 
-	// Draw the text string (with converted character indices)
-	if (textFlags & 0x04) {
-		// Word-wrapped text
-		_rebelMsgFont->drawStringWrap(convertedText, renderBitmap, clipRect, posX, posY, textColor, styleFlags);
-	} else {
-		// Single-line text
-		_rebelMsgFont->drawString(convertedText, renderBitmap, clipRect, posX, posY, textColor, styleFlags);
+	// Draw the text string (with converted character indices), but only when subtitles are
+	// enabled ??? opcode 9 is a subtitle/message path, so it honors ScummVM's global
+	// "subtitles" setting and the in-game TEXT toggle (same ConfMan key). The chunk is
+	// still fully parsed above so stream consumption is unaffected.
+	if (ConfMan.getBool("subtitles")) {
+		if (textFlags & 0x04) {
+			// Word-wrapped text
+			_rebelMsgFont->drawStringWrap(convertedText, renderBitmap, clipRect, posX, posY, textColor, styleFlags);
+		} else {
+			// Single-line text
+			_rebelMsgFont->drawString(convertedText, renderBitmap, clipRect, posX, posY, textColor, styleFlags);
+		}
 	}
 
 	debug("Rebel2 Opcode 9: Rendered subtitle at (%d,%d) flags=0x%x clip=(%d,%d,%d,%d)",
diff --git a/engines/scumm/insane/rebel2/menu.cpp b/engines/scumm/insane/rebel2/menu.cpp
index ffe9ccdb0cf..8dfdd0cac36 100644
--- a/engines/scumm/insane/rebel2/menu.cpp
+++ b/engines/scumm/insane/rebel2/menu.cpp
@@ -19,6 +19,7 @@
  *
  */
 
+#include "common/config-manager.h"
 #include "common/system.h"
 #include "common/events.h"
 #include "common/util.h"
@@ -1797,6 +1798,7 @@ int InsaneRebel2::processOptionsInput() {
 					break;
 				case 3:  // Text toggle
 					_optTextEnabled = !_optTextEnabled;
+					ConfMan.setBool("subtitles", _optTextEnabled);
 					break;
 				case 4:  // Controls toggle
 					_optControlsFlipped = !_optControlsFlipped;
diff --git a/engines/scumm/insane/rebel2/rebel.cpp b/engines/scumm/insane/rebel2/rebel.cpp
index ba9e1b5ed92..c843a74296b 100644
--- a/engines/scumm/insane/rebel2/rebel.cpp
+++ b/engines/scumm/insane/rebel2/rebel.cpp
@@ -454,7 +454,10 @@ InsaneRebel2::InsaneRebel2(ScummEngine_v7 *scumm) {
 	_optMusicEnabled = !_vm->_mixer->isSoundTypeMuted(Audio::Mixer::kMusicSoundType);
 	_optSfxEnabled = !_vm->_mixer->isSoundTypeMuted(Audio::Mixer::kSFXSoundType);
 	_optVoicesEnabled = !_vm->_mixer->isSoundTypeMuted(Audio::Mixer::kSpeechSoundType);
-	_optTextEnabled = true;
+	// Initialize the dialogue-text (subtitles) toggle from ScummVM's global setting so the
+	// game and the in-game TEXT menu label reflect it. The menu toggle writes the same
+	// "subtitles" key, which the text-render paths gate on.
+	_optTextEnabled = ConfMan.getBool("subtitles");
 	_optControlsFlipped = false;
 	_optRapidFire = false;
 	_optVolumeLevel = _vm->_mixer->getVolumeForSoundType(Audio::Mixer::kMusicSoundType) / 2;
diff --git a/engines/scumm/smush/rebel/smush_player_ra2.cpp b/engines/scumm/smush/rebel/smush_player_ra2.cpp
index dc0ffb3b21b..1299f5034b0 100644
--- a/engines/scumm/smush/rebel/smush_player_ra2.cpp
+++ b/engines/scumm/smush/rebel/smush_player_ra2.cpp
@@ -24,6 +24,7 @@
 // Overrides the virtual hooks defined in SmushPlayer to provide
 // Rebel Assault 2 specific video, font, text and codec handling.
 
+#include "common/config-manager.h"
 #include "common/endian.h"
 #include "common/rect.h"
 #include "common/system.h"
@@ -189,7 +190,12 @@ bool SmushPlayerRebel2::handleGameFetch(int32 subSize, Common::SeekableReadStrea
 bool SmushPlayerRebel2::handleGameTextRendering(const char *str, int fontId, int color,
 												int pos_x, int pos_y, int left, int top,
 												int width, int height, TextStyleFlags flg) {
-	ra2HandleTextResource(str, fontId, color, pos_x, pos_y, left, top, width, height, flg);
+	// RA2 dialogue subtitles. Only render them when subtitles are enabled ??? this honors
+	// both ScummVM's global "subtitles" setting and the in-game TEXT toggle (which writes
+	// the same ConfMan key). Still return true so the base handler treats the chunk as
+	// handled. Querying ConfMan per chunk also lets the setting take effect mid-video.
+	if (ConfMan.getBool("subtitles"))
+		ra2HandleTextResource(str, fontId, color, pos_x, pos_y, left, top, width, height, flg);
 	return true;
 }
 



From noreply at scummvm.org  Mon Jun  1 14:35:04 2026
From: noreply at scummvm.org (neuromancer)
Date: Mon,  1 Jun 2026 14:35:04 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 793b12545ac64040201408a037438c2525579849
Message-ID: <20260601143504.4FD3F700591@ron.scummvm.net>

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:
06333e5f71 SCUMM: RA1: make it touchpad friendly
790e05d816 SCUMM: RA2: fixed invalid palette handling in L2
ec305dee37 SCUMM: RA2: improved gamepad support
0025c4e108 SCUMM: RA2: match the joytick/gamepad input speed
793b12545a SCUMM: RA2: make sure input starts centered


Commit: 06333e5f71e11b2a9e9b332c2c42a89e87ed69a1
    https://github.com/scummvm/scummvm/commit/06333e5f71e11b2a9e9b332c2c42a89e87ed69a1
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-01T13:46:31+02:00

Commit Message:
SCUMM: RA1: make it touchpad friendly

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/insane/rebel1/runlevels.cpp


diff --git a/engines/scumm/insane/rebel1/iact.cpp b/engines/scumm/insane/rebel1/iact.cpp
index 68b0e2afac4..f3d2a507789 100644
--- a/engines/scumm/insane/rebel1/iact.cpp
+++ b/engines/scumm/insane/rebel1/iact.cpp
@@ -857,6 +857,16 @@ void InsaneRebel1::preprocessMouseAxes(int16 &inputX, int16 &inputY, bool *usedJ
 		return;
 	}
 
+	// On touchscreen devices aiming comes from the on-screen gamepad (joystick), never the
+	// absolute touch/cursor position; and we must not recenter the system cursor
+	// (smush_warpMouse below), which would inject spurious motion and drift the reticle.
+	if (isTouchscreenActive()) {
+		inputX = 0;
+		inputY = 0;
+		_mouseVirtualValid = false;
+		return;
+	}
+
 	int16 logicalX = (int16)CLIP<int>(_vm->_mouse.x, 0, 319);
 	int16 logicalY = (int16)CLIP<int>(_vm->_mouse.y, 0, 199);
 
diff --git a/engines/scumm/insane/rebel1/menu.cpp b/engines/scumm/insane/rebel1/menu.cpp
index 35ecd26bc26..0dbc31baf07 100644
--- a/engines/scumm/insane/rebel1/menu.cpp
+++ b/engines/scumm/insane/rebel1/menu.cpp
@@ -577,7 +577,9 @@ bool InsaneRebel1::notifyEvent(const Common::Event &event) {
 	if (_vm->isPaused())
 		return false;
 
-	if (event.type == Common::EVENT_MOUSEMOVE && !_mouseRecentering) {
+	// On touchscreen devices, finger drags arrive as mouse-move events; do not let them
+	// hijack RA1 into absolute-cursor aiming (aiming there comes from the on-screen gamepad).
+	if (event.type == Common::EVENT_MOUSEMOVE && !_mouseRecentering && !isTouchscreenActive()) {
 		_activeInputSource = kInputSourceMouse;
 	}
 
diff --git a/engines/scumm/insane/rebel1/rebel.cpp b/engines/scumm/insane/rebel1/rebel.cpp
index 9b4a89b174f..8341fa0c1cd 100644
--- a/engines/scumm/insane/rebel1/rebel.cpp
+++ b/engines/scumm/insane/rebel1/rebel.cpp
@@ -200,6 +200,10 @@ void InsaneRebel1::loadTuningForLevel(int level) {
 		_tuning.time, _tuning.levelPts, _tuning.bonus, _tuning.flags);
 }
 
+bool InsaneRebel1::isTouchscreenActive() const {
+	return g_system->hasFeature(OSystem::kFeatureTouchscreen);
+}
+
 void InsaneRebel1::resetGameplayFlagsFromTuning() {
 	const uint16 tuningFlags = (uint16)_tuning.flags;
 	_gameplayFlags75fe = tuningFlags & 0x00FF;
diff --git a/engines/scumm/insane/rebel1/rebel.h b/engines/scumm/insane/rebel1/rebel.h
index 8bedfdff0e0..0b7d2da299b 100644
--- a/engines/scumm/insane/rebel1/rebel.h
+++ b/engines/scumm/insane/rebel1/rebel.h
@@ -105,6 +105,10 @@ public:
 
 	void handleGameChunk(int32 subSize, Common::SeekableReadStream &b);
 	bool isInteractiveVideoActive() const { return _interactiveVideoActive; }
+	// True on touchscreen devices (e.g. Android). RA1 then aims from the on-screen gamepad
+	// joystick instead of the DOS absolute-mouse model, and skips cursor warping/locking,
+	// which otherwise inject spurious motion that drifts the reticle and on-screen buttons.
+	bool isTouchscreenActive() const;
 	void setFrameHasGameChunk(bool hasGameChunk) { _frameHasGameChunk = hasGameChunk; }
 	int getCurrentLevel() const { return _currentLevel; }
 	uint16 getActiveGameOpcode() const { return _activeGameOpcode; }
diff --git a/engines/scumm/insane/rebel1/runlevels.cpp b/engines/scumm/insane/rebel1/runlevels.cpp
index cb9c68b8058..f1d67c8e49b 100644
--- a/engines/scumm/insane/rebel1/runlevels.cpp
+++ b/engines/scumm/insane/rebel1/runlevels.cpp
@@ -1636,7 +1636,11 @@ void InsaneRebel1::captureInteractiveVideoInput() {
 	// Level 7 route splices happen inside one original gameplay loop, so keep
 	// the current input state instead of recentering between route clips.
 	if (!level7RouteSplice) {
-		smush_warpMouse(160, 100, -1);
+		// On touchscreen devices the DOS recenter-the-cursor aiming model does not apply
+		// (aiming uses the on-screen gamepad); warping/locking the system mouse there only
+		// injects spurious motion that drifts the reticle and on-screen buttons.
+		if (!isTouchscreenActive())
+			smush_warpMouse(160, 100, -1);
 		_mouseVirtualRawX = 0x140;
 		_mouseVirtualRawY = 100;
 		_mouseVirtualPrevLogicalX = kRA1CenterX;
@@ -1644,7 +1648,8 @@ void InsaneRebel1::captureInteractiveVideoInput() {
 		_mouseVirtualValid = false;
 	}
 	CursorMan.showMouse(false);
-	g_system->lockMouse(true);
+	if (!isTouchscreenActive())
+		g_system->lockMouse(true);
 }
 
 void InsaneRebel1::releaseInteractiveVideoInput() {


Commit: 790e05d8162689cbf73cb223a32bf9a912a87bc8
    https://github.com/scummvm/scummvm/commit/790e05d8162689cbf73cb223a32bf9a912a87bc8
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-01T14:36:44+02:00

Commit Message:
SCUMM: RA2: fixed invalid palette handling in L2

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


diff --git a/engines/scumm/insane/rebel2/iact.cpp b/engines/scumm/insane/rebel2/iact.cpp
index 114ecc49136..2ed784f0480 100644
--- a/engines/scumm/insane/rebel2/iact.cpp
+++ b/engines/scumm/insane/rebel2/iact.cpp
@@ -74,7 +74,7 @@ void InsaneRebel2::procPreRendering(byte *renderBitmap) {
 		_rebelOp6Initialized = false;
 	}
 
-	// For Level 2 gameplay (Handler 8 only), restore the background BEFORE FOBJ decoding.
+	// For Level 2 handler 8 gameplay, restore the background BEFORE FOBJ decoding.
 	// The tiny FOBJ sprites (7x10, 9x38 pixels) only draw new sprite positions but don't
 	// clear old ones. By restoring the full background each frame, we ensure old sprite
 	// positions are erased before new ones are drawn.
@@ -2267,10 +2267,9 @@ bool InsaneRebel2::loadLevel2Background(byte *animData, int32 size, byte *render
 				_level2BackgroundLoaded = true;
 				foundBackground = true;
 
-				// Copy to render bitmap immediately if provided.
-				// Only copy when render buffer pitch is 320 (standard screen size).
-				// For oversized buffers, the FOBJ/FETCH system handles background rendering.
-				if (renderBitmap) {
+				// Handler 25 uses this buffer as a lookup mask; the retail code does not
+				// copy it to the live screen. Handler 8 still uses it as a restore source.
+				if (renderBitmap && _rebelHandler != 25) {
 					int bufferPitch = (_player && _player->_width > 0) ? _player->_width : 320;
 					if (bufferPitch == 320) {
 						for (int by = 0; by < 200; by++) {
diff --git a/engines/scumm/insane/rebel2/render.cpp b/engines/scumm/insane/rebel2/render.cpp
index e9b35a408b6..c93f1c24aa0 100644
--- a/engines/scumm/insane/rebel2/render.cpp
+++ b/engines/scumm/insane/rebel2/render.cpp
@@ -2339,11 +2339,9 @@ void InsaneRebel2::renderGameplayPostFrame(byte *renderBitmap, int pitch, int wi
 	// Original: FUN_00403240 only runs handlers when DAT_0047a814 == 0.
 	processMouse();
 
-	// NOTE: Level 2 background is drawn ONCE during IACT opcode 8 par4=5 processing
-	// (in procIACT when the background ANIM is first loaded). The 0x08 video flag
-	// (preserve background) prevents the frame buffer from being cleared, so the
-	// background persists. FOBJ sprites (enemies) are then decoded on top by SMUSH.
-	// We do NOT redraw the background here as that would overwrite FOBJ content.
+	// NOTE: Level 2 handler 8's background is restored in procPreRendering before
+	// SMUSH decodes the frame's FOBJ sprites. Handler 25 draws its corridor overlay
+	// from IACT opcode 6 instead. Redrawing either here would overwrite enemies.
 
 	// --- HUD Drawing Order (from FUN_004089ab / FUN_40D836 assembly analysis) ---
 	// Original assembly render order for handler 0x26:
diff --git a/engines/scumm/insane/rebel2/runlevels.cpp b/engines/scumm/insane/rebel2/runlevels.cpp
index 93a763db401..673a67def8a 100644
--- a/engines/scumm/insane/rebel2/runlevels.cpp
+++ b/engines/scumm/insane/rebel2/runlevels.cpp
@@ -235,6 +235,9 @@ void InsaneRebel2::resetLevelAttemptState(int initialPhase) {
 	_playerShield = 255;
 	_playerDamage = 0;
 	_currentPhase = initialPhase;
+	resetDamageFlash();
+	_damageHighFlashCounter = 0;
+	_damageShakeCounter = 0;
 
 	_rebelAutopilot = 0;
 	_rebelDamageLevel = 0;
@@ -253,6 +256,28 @@ void InsaneRebel2::resetLevelPhaseState(bool clearEnemies) {
 	_rebelHitCounter = 0;
 	resetLevelWaveState();
 
+	delete _grd001Sprite;
+	_grd001Sprite = nullptr;
+	delete _grd002Sprite;
+	_grd002Sprite = nullptr;
+	_grdShotOriginTableLoaded = false;
+
+	static const int handler25FrameSlots[] = { 4, 6, 7, 10, 12, 13 };
+	for (uint i = 0; i < ARRAYSIZE(handler25FrameSlots); ++i) {
+		EmbeddedSanFrame &frame = _rebelEmbeddedHud[handler25FrameSlots[i]];
+		free(frame.pixels);
+		frame.pixels = nullptr;
+		frame.width = 0;
+		frame.height = 0;
+		frame.renderX = 0;
+		frame.renderY = 0;
+		frame.valid = false;
+	}
+
+	free(_level2Background);
+	_level2Background = nullptr;
+	_level2BackgroundLoaded = false;
+
 	if (clearEnemies)
 		_enemies.clear();
 }
diff --git a/engines/scumm/smush/rebel/smush_player_ra2.cpp b/engines/scumm/smush/rebel/smush_player_ra2.cpp
index 1299f5034b0..87fb6db2eef 100644
--- a/engines/scumm/smush/rebel/smush_player_ra2.cpp
+++ b/engines/scumm/smush/rebel/smush_player_ra2.cpp
@@ -387,6 +387,56 @@ void SmushPlayerRebel2::adjustGamePalette() {
 	memset(_deltaPal, 0, sizeof(_deltaPal));
 }
 
+bool SmushPlayerRebel2::shouldLoadAnimHeaderPalette() const {
+	// Original RA2 AHDR handler skips the embedded palette when video flag 0x400 is set.
+	return (_curVideoFlags & 0x400) == 0;
+}
+
+void SmushPlayerRebel2::ra2HandleDeltaPalette(int32 subSize, Common::SeekableReadStream &b) {
+	if (subSize < 4) {
+		b.skip(subSize);
+		return;
+	}
+
+	b.readUint16LE();
+	const uint16 xpalCommand = b.readUint16LE();
+
+	if (xpalCommand != 0 && xpalCommand != 512) {
+		if (subSize > 4)
+			b.skip(subSize - 4);
+
+		for (int i = 0; i < 768; ++i) {
+			_shiftedDeltaPal[i] += _deltaPal[i];
+			_pal[i] = CLIP<int32>(_shiftedDeltaPal[i] >> 7, 0, 255);
+		}
+		setDirtyColors(0, 255);
+		return;
+	}
+
+	const int32 deltaBytes = 0x300 * 2;
+	if (subSize < 4 + deltaBytes) {
+		b.skip(subSize - 4);
+		return;
+	}
+
+	int16 deltaPal[0x300];
+	for (int i = 0; i < 768; ++i)
+		deltaPal[i] = b.readSint16LE();
+
+	if (xpalCommand == 512 && subSize >= 4 + deltaBytes + 0x300) {
+		b.read(_pal, 0x300);
+	} else if (subSize > 4 + deltaBytes) {
+		b.skip(subSize - 4 - deltaBytes);
+	}
+
+	for (int i = 0; i < 768; ++i) {
+		_shiftedDeltaPal[i] = _pal[i] << 7;
+		_deltaPal[i] = deltaPal[i];
+	}
+
+	setDirtyColors(0, 255);
+}
+
 /**
  * RA2-specific handleAnimHeader fixup: when AHDR reports 0x0 dimensions,
  * use screen dimensions instead.
@@ -510,18 +560,31 @@ void SmushPlayerRebel2::ra2HandleTextResource(const char *str, int fontId, int c
 
 /**
  * RA2-specific buffer selection for non-standard FOBJ dimensions.
- * Returns the destination buffer to use and updates _dst, _width, _height.
+ * Returns true when the dimensions are valid and updates _dst, _width, _height as needed.
  */
-void SmushPlayerRebel2::ra2SelectFrameBuffer(int width, int height) {
+bool SmushPlayerRebel2::ra2SelectFrameBuffer(int width, int height) {
 	// Rebel2 uses a special buffer for all non-matching frames.
 	// Level 1: First frame is 424x260 (background), small sprites reuse same buffer
 	// Level 2: Uses virtual screen directly (handled below when _specialBuffer stays null)
-	int bufSize = width * height;
-	if (bufSize > _vm->_screenWidth * _vm->_screenHeight) {
+	const int64 bufSize64 = (int64)width * height;
+	if (width <= 0 || height <= 0 || bufSize64 > INT_MAX) {
+		debugC(DEBUG_SMUSH, "SmushPlayerRebel2::ra2SelectFrameBuffer: Skipping invalid FOBJ dimensions %dx%d", width, height);
+		return false;
+	}
+
+	const int bufSize = (int)bufSize64;
+	const int screenSize = _vm->_screenWidth * _vm->_screenHeight;
+	if (bufSize > screenSize) {
 		// Frame is larger than screen - need special buffer
 		if (_specialBuffer == nullptr || bufSize > _specialBufferSize) {
+			byte *newSpecialBuffer = (byte *)malloc(bufSize);
+			if (newSpecialBuffer == nullptr) {
+				warning("SmushPlayerRebel2::ra2SelectFrameBuffer: Failed to allocate %d bytes for FOBJ %dx%d",
+					bufSize, width, height);
+				return false;
+			}
 			free(_specialBuffer);
-			_specialBuffer = (byte *)malloc(bufSize);
+			_specialBuffer = newSpecialBuffer;
 			_specialBufferSize = bufSize;
 			_width = width;
 			_height = height;
@@ -531,7 +594,7 @@ void SmushPlayerRebel2::ra2SelectFrameBuffer(int width, int height) {
 		}
 	}
 
-	if (bufSize > _vm->_screenWidth * _vm->_screenHeight &&
+	if (bufSize > screenSize &&
 	    _specialBuffer != nullptr && _specialBufferSize >= bufSize) {
 		_dst = _specialBuffer;
 		debugC(DEBUG_SMUSH, "SmushPlayerRebel2::ra2SelectFrameBuffer: Using _specialBuffer for oversized FOBJ %dx%d", width, height);
@@ -548,6 +611,8 @@ void SmushPlayerRebel2::ra2SelectFrameBuffer(int width, int height) {
 				width, height);
 		}
 	}
+
+	return true;
 }
 
 /**
@@ -656,8 +721,7 @@ void SmushPlayerRebel2::handleGameParseNextFrame() {
 
 bool SmushPlayerRebel2::handleGameFrameBufferSelect(int codec, int width, int height) {
 	if ((height != _vm->_screenHeight) || (width != _vm->_screenWidth)) {
-		ra2SelectFrameBuffer(width, height);
-		return true;
+		return ra2SelectFrameBuffer(width, height);
 	}
 	return false;
 }
@@ -719,6 +783,12 @@ bool SmushPlayerRebel2::handleGameSkipChunk(uint32 subType, int32 subSize, Commo
 		debugC(DEBUG_SMUSH, "SmushPlayerRebel2::handleGameSkipChunk: SKIP consumed chunk %s frame=%d", tag2str(subType), _frame);
 		return true;
 	}
+
+	if (subType == MKTAG('X','P','A','L')) {
+		ra2HandleDeltaPalette(subSize, b);
+		return true;
+	}
+
 	return false;
 }
 
diff --git a/engines/scumm/smush/rebel/smush_player_ra2.h b/engines/scumm/smush/rebel/smush_player_ra2.h
index 7af86cf66f0..084a727fb3d 100644
--- a/engines/scumm/smush/rebel/smush_player_ra2.h
+++ b/engines/scumm/smush/rebel/smush_player_ra2.h
@@ -42,6 +42,7 @@ protected:
 	bool shouldAlwaysShowSubtitles() const override { return true; }
 	SmushFont *getGameFont(int font) override;
 	void adjustGamePalette() override;
+	bool shouldLoadAnimHeaderPalette() const override;
 	bool handleGameAnimHeader(byte *headerContent) override;
 	bool handleGameSetupStrings() override;
 	void handleGameParseNextFrame() override;
@@ -65,9 +66,10 @@ private:
 	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);
-	void ra2SelectFrameBuffer(int width, int height);
+	bool ra2SelectFrameBuffer(int width, int height);
 	bool ra2DecodeCodec(int codec, const uint8 *src, int left, int top,
 						int width, int height, int pitch, int dataSize);
+	void ra2HandleDeltaPalette(int32 subSize, Common::SeekableReadStream &b);
 	void ra2StoreFobjData(int codec, const byte *data, int32 dataSize,
 						  int left, int top, int width, int height);
 	void ra2HandleGost(int32 subSize, Common::SeekableReadStream &b);
diff --git a/engines/scumm/smush/smush_player.cpp b/engines/scumm/smush/smush_player.cpp
index 795a90be08f..a6db75dfc8e 100644
--- a/engines/scumm/smush/smush_player.cpp
+++ b/engines/scumm/smush/smush_player.cpp
@@ -1155,7 +1155,7 @@ void SmushPlayer::handleAnimHeader(int32 subSize, Common::SeekableReadStream &b)
 			}
 		}
 
-		if (!_skipPalette) {
+		if (!_skipPalette && shouldLoadAnimHeaderPalette()) {
 			byte *palettePtr = &headerContent[6];
 			memcpy(_pal, palettePtr, sizeof(_pal));
 			adjustGamePalette();
diff --git a/engines/scumm/smush/smush_player.h b/engines/scumm/smush/smush_player.h
index 4f967932c5a..a5dc1ff6f79 100644
--- a/engines/scumm/smush/smush_player.h
+++ b/engines/scumm/smush/smush_player.h
@@ -311,6 +311,7 @@ protected:
 	virtual bool shouldAlwaysShowSubtitles() const { return false; }
 	virtual SmushFont *getGameFont(int font) { return nullptr; }
 	virtual void adjustGamePalette() {}
+	virtual bool shouldLoadAnimHeaderPalette() const { return true; }
 	virtual bool handleGameAnimHeader(byte *headerContent) { return false; }
 	virtual bool handleGameSetupStrings() { return false; }
 	virtual void handleGameParseNextFrame() {}


Commit: ec305dee3713343ba93643eb27ab1d0534f16100
    https://github.com/scummvm/scummvm/commit/ec305dee3713343ba93643eb27ab1d0534f16100
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-01T16:13:54+02:00

Commit Message:
SCUMM: RA2: improved gamepad support

Changed paths:
    engines/scumm/insane/rebel2/iact.cpp
    engines/scumm/insane/rebel2/levels.cpp
    engines/scumm/insane/rebel2/rebel.cpp
    engines/scumm/insane/rebel2/rebel.h
    engines/scumm/metaengine.cpp
    engines/scumm/scumm.h


diff --git a/engines/scumm/insane/rebel2/iact.cpp b/engines/scumm/insane/rebel2/iact.cpp
index 2ed784f0480..fe30e70a2dc 100644
--- a/engines/scumm/insane/rebel2/iact.cpp
+++ b/engines/scumm/insane/rebel2/iact.cpp
@@ -67,6 +67,10 @@ static bool readLevel2BackgroundChunkHeader(Common::SeekableReadStream &stream,
 void InsaneRebel2::procPreRendering(byte *renderBitmap) {
 	Insane::procPreRendering(renderBitmap);
 
+	// Pan the reticle from held directional controls (on-screen/physical gamepad dpad,
+	// keyboard arrows) once per frame. No-op outside gameplay; mouse aiming is unaffected.
+	updateGameplayAimFromGamepad();
+
 	// Reset opcode 6 init flag at the start of each new video.
 	// This ensures the per-wave init (clearBit, link table reset, wave state)
 	// fires exactly once per wave video, not every frame.
diff --git a/engines/scumm/insane/rebel2/levels.cpp b/engines/scumm/insane/rebel2/levels.cpp
index d0d2368c7d7..cef25c6b9e2 100644
--- a/engines/scumm/insane/rebel2/levels.cpp
+++ b/engines/scumm/insane/rebel2/levels.cpp
@@ -403,6 +403,9 @@ int InsaneRebel2::runLevel(int levelId) {
 	smush_warpMouse(160, 100, -1);
 	CursorMan.showMouse(false);
 	g_system->lockMouse(true);
+	// Start each level with the centered cursor as the authoritative aim source;
+	// the first gamepad input reclaims it (see updateGameplayAimFromGamepad).
+	_gamepadAimActive = false;
 
 	// Initialize common player state
 	_playerLives = 3;
diff --git a/engines/scumm/insane/rebel2/rebel.cpp b/engines/scumm/insane/rebel2/rebel.cpp
index c843a74296b..a91e46c276c 100644
--- a/engines/scumm/insane/rebel2/rebel.cpp
+++ b/engines/scumm/insane/rebel2/rebel.cpp
@@ -465,6 +465,13 @@ InsaneRebel2::InsaneRebel2(ScummEngine_v7 *scumm) {
 	// Initialize menu input capture system
 	_menuInputActive = false;
 
+	// Analog stick state for gamepad aiming (mirrors RA1's analog model).
+	// Ingested from EVENT_CUSTOM_BACKEND_ACTION_AXIS, read with a deadzone and
+	// integrated as velocity into the reticle in updateGameplayAimFromGamepad().
+	_joystickAxisX = 0;
+	_joystickAxisY = 0;
+	_gamepadAimActive = false;
+
 	// Initialize level state tracking for multi-phase levels
 	_currentPhase = 1;
 	_deathFrame = 0;
@@ -524,6 +531,67 @@ InsaneRebel2::~InsaneRebel2() {
 bool InsaneRebel2::notifyEvent(const Common::Event &event) {
 	SmushPlayer *splayer = ((ScummEngine_v7 *)_vm)->_splayer;
 
+	// Keep the gamepad reticle authoritative against stray pointer events. During
+	// gameplay the cursor is locked ~screen-center (levels.cpp lockMouse), so any
+	// spurious pointer event ??? e.g. a gamepad "fire" surfacing as a button-down ???
+	// carries that centered position, and SCUMM's input layer copies it straight
+	// into _mouse on button-down (scumm/input.cpp), yanking the reticle to center on
+	// every shot. We observe before ScummEngine (priority 1 > kEventManPriority), so
+	// dropping the event here prevents the clobber. Firing is driven by the
+	// kScummActionInsaneAttack action, not the pointer, so this does not affect shots.
+	// A genuine mouse/touch motion (nonzero relative delta) hands control back.
+	if (_gamepadAimActive && _gameState == kStateGameplay && !_menuInputActive) {
+		switch (event.type) {
+		case Common::EVENT_MOUSEMOVE:
+			if (event.relMouse.x != 0 || event.relMouse.y != 0) {
+				_gamepadAimActive = false; // real pointer motion takes over
+				break;
+			}
+			return true; // drop zero-delta artifact (e.g. locked-cursor recentre)
+		case Common::EVENT_LBUTTONDOWN:
+		case Common::EVENT_RBUTTONDOWN:
+			return true; // drop stray button-down that would recenter the reticle
+			// Let LBUTTONUP/RBUTTONUP fall through so the backend _buttonState latch
+			// always clears and can never stick when a real mouse later takes over.
+		default:
+			break;
+		}
+	}
+
+	// Analog stick ??? reticle velocity (mirrors RA1's analog model). The mapped
+	// axis actions carry a signed position; a centered stick reports position 0.
+	// We ignore a recentre that would fight the opposite direction so a quick
+	// flick doesn't get cancelled by the trailing zero of the other axis half.
+	if (event.type == Common::EVENT_CUSTOM_BACKEND_ACTION_AXIS) {
+		const int16 axisPosition = (event.joystick.position == Common::JOYAXIS_MIN)
+			? Common::JOYAXIS_MAX : event.joystick.position;
+
+		switch (event.customType) {
+		case kScummBackendActionRebel2AxisUp:
+			if (event.joystick.position == 0 && _joystickAxisY > 0)
+				return true;
+			_joystickAxisY = -axisPosition;
+			return true;
+		case kScummBackendActionRebel2AxisDown:
+			if (event.joystick.position == 0 && _joystickAxisY < 0)
+				return true;
+			_joystickAxisY = axisPosition;
+			return true;
+		case kScummBackendActionRebel2AxisLeft:
+			if (event.joystick.position == 0 && _joystickAxisX > 0)
+				return true;
+			_joystickAxisX = -axisPosition;
+			return true;
+		case kScummBackendActionRebel2AxisRight:
+			if (event.joystick.position == 0 && _joystickAxisX < 0)
+				return true;
+			_joystickAxisX = axisPosition;
+			return true;
+		default:
+			break;
+		}
+	}
+
 	if (event.type == Common::EVENT_CUSTOM_ENGINE_ACTION_START ||
 		event.type == Common::EVENT_CUSTOM_ENGINE_ACTION_END) {
 		const bool pressed = (event.type == Common::EVENT_CUSTOM_ENGINE_ACTION_START);
@@ -593,10 +661,12 @@ bool InsaneRebel2::notifyEvent(const Common::Event &event) {
 			}
 		}
 
-		if (event.customType == kScummActionInsaneAttack ||
-			event.customType == kScummActionInsaneSwitch) {
-			return true;
-		}
+		// Do NOT consume Attack/Switch during gameplay: like the dpad actions, these
+		// custom-action events must fall through to ScummEngine::parseEvent() so it keeps
+		// _actionMap in sync (input.cpp). processMouse() reads getActionState() for the
+		// gamepad trigger; consuming here breaks the dispatch loop (events.cpp) before the
+		// map is updated, leaving fire dead. The menu/paused branches above already
+		// consumed (and returned true for) the cases they handle.
 	}
 
 	if (event.type == Common::EVENT_KEYDOWN) {
@@ -1285,29 +1355,58 @@ int32 InsaneRebel2::processMouse() {
 }
 
 Common::Point InsaneRebel2::getGameplayAimPoint() {
-	Common::Point aimPos(_vm->_mouse.x, _vm->_mouse.y);
+	// Pure getter (queried many times per frame): the aim/reticle follows the virtual
+	// mouse position. Directional controls pan that position incrementally once per frame
+	// via updateGameplayAimFromGamepad(), rather than snapping the reticle to a screen edge.
+	return Common::Point(_vm->_mouse.x, _vm->_mouse.y);
+}
 
+// Apply the user's configured analog deadzone so a resting stick reports no
+// motion. Mirrors RA1's applyRebel1AnalogDeadzone (iact.cpp).
+static inline int16 applyRebel2AnalogDeadzone(int16 axisValue) {
+	const int deadZone = MAX(0, ConfMan.getInt("joystick_deadzone")) * 1000;
+	return (ABS((int)axisValue) <= deadZone) ? 0 : axisValue;
+}
+
+void InsaneRebel2::updateGameplayAimFromGamepad() {
 	if (_menuInputActive || _gameState != kStateGameplay)
-		return aimPos;
-
-	int dx = 0;
-	int dy = 0;
-
-	if (_vm->getActionState(kScummActionInsaneLeft))
-		dx--;
-	if (_vm->getActionState(kScummActionInsaneRight))
-		dx++;
-	if (_vm->getActionState(kScummActionInsaneUp))
-		dy--;
-	if (_vm->getActionState(kScummActionInsaneDown))
-		dy++;
-
-	if (dx || dy) {
-		aimPos.x = (dx < 0) ? 0 : (dx > 0) ? 319 : 160;
-		aimPos.y = (dy < 0) ? 0 : (dy > 0) ? 199 : 100;
+		return;
+
+	// Velocity model ported from RA1 (insane/rebel1/iact.cpp preprocessMouseAxes):
+	// the digital dpad pans at full rate, while the analog stick pans proportionally
+	// to its deflection past the deadzone. This replaces the old edge-snap that pinned
+	// the reticle to the screen borders. Mouse input is untouched: with nothing held the
+	// reticle simply follows _vm->_mouse as before.
+	int velX = 0;
+	int velY = 0;
+
+	const int dpadX = (_vm->getActionState(kScummActionInsaneRight) ? 1 : 0) -
+	                  (_vm->getActionState(kScummActionInsaneLeft) ? 1 : 0);
+	const int dpadY = (_vm->getActionState(kScummActionInsaneDown) ? 1 : 0) -
+	                  (_vm->getActionState(kScummActionInsaneUp) ? 1 : 0);
+
+	if (dpadX || dpadY) {
+		velX = dpadX * 127;
+		velY = dpadY * 127;
+	} else {
+		const int16 ax = applyRebel2AnalogDeadzone(_joystickAxisX);
+		const int16 ay = applyRebel2AnalogDeadzone(_joystickAxisY);
+		velX = CLIP<int>((int)ax * 127 / Common::JOYAXIS_MAX, -127, 127);
+		velY = CLIP<int>((int)ay * 127 / Common::JOYAXIS_MAX, -127, 127);
 	}
 
-	return aimPos;
+	if (!velX && !velY)
+		return;
+
+	// The gamepad is now driving the reticle: mark it the active aim source so
+	// notifyEvent() suppresses stray pointer events that would recenter it on fire.
+	_gamepadAimActive = true;
+
+	// Integrate velocity into the reticle, clamped to the 320x200 play area.
+	// kStep sets the max pixels-per-frame at full deflection; tune for feel.
+	const int kStep = 8;
+	_vm->_mouse.x = (int16)CLIP<int>(_vm->_mouse.x + velX * kStep / 127, 0, 319);
+	_vm->_mouse.y = (int16)CLIP<int>(_vm->_mouse.y + velY * kStep / 127, 0, 199);
 }
 
 bool InsaneRebel2::isBitSet(int n) {
diff --git a/engines/scumm/insane/rebel2/rebel.h b/engines/scumm/insane/rebel2/rebel.h
index bf19ea2af37..28161f25106 100644
--- a/engines/scumm/insane/rebel2/rebel.h
+++ b/engines/scumm/insane/rebel2/rebel.h
@@ -492,6 +492,21 @@ public:
 
 	int32 processMouse() override;
 	Common::Point getGameplayAimPoint();
+	// Per-frame: pan the gameplay reticle incrementally from the held directional controls
+	// (on-screen/physical gamepad dpad, keyboard arrows) instead of snapping it to a screen
+	// edge. Call once per frame; getGameplayAimPoint() stays a pure getter.
+	void updateGameplayAimFromGamepad();
+
+	// Analog stick state, ingested from EVENT_CUSTOM_BACKEND_ACTION_AXIS in notifyEvent()
+	// and read (with a deadzone) by updateGameplayAimFromGamepad(). Signed; 0 = centered.
+	int16 _joystickAxisX;
+	int16 _joystickAxisY;
+
+	// True once the gamepad has driven the gameplay reticle, until a genuine mouse/touch
+	// motion takes over. While set, notifyEvent() drops stray pointer events so they can't
+	// recenter the reticle (the cursor is locked ~center during gameplay, so a gamepad
+	// "click" lands at center). See notifyEvent()/updateGameplayAimFromGamepad().
+	bool _gamepadAimActive;
 	bool isBitSet(int n) override;
 	void setBit(int n) override;
 
diff --git a/engines/scumm/metaengine.cpp b/engines/scumm/metaengine.cpp
index 5c8c0025a02..93e61776248 100644
--- a/engines/scumm/metaengine.cpp
+++ b/engines/scumm/metaengine.cpp
@@ -1170,25 +1170,45 @@ Common::KeymapArray ScummMetaEngine::initKeymaps(const char *target) const {
 		act = new Action("RA2UP", _("Aim up / menu up"));
 		act->setCustomEngineActionEvent(kScummActionInsaneUp);
 		act->addDefaultInputMapping("JOY_UP");
-		act->addDefaultInputMapping("JOY_LEFT_STICK_Y-");
 		rebel2Keymap->addAction(act);
 
 		act = new Action("RA2DOWN", _("Aim down / menu down"));
 		act->setCustomEngineActionEvent(kScummActionInsaneDown);
 		act->addDefaultInputMapping("JOY_DOWN");
-		act->addDefaultInputMapping("JOY_LEFT_STICK_Y+");
 		rebel2Keymap->addAction(act);
 
 		act = new Action("RA2LEFT", _("Aim left / menu left"));
 		act->setCustomEngineActionEvent(kScummActionInsaneLeft);
 		act->addDefaultInputMapping("JOY_LEFT");
-		act->addDefaultInputMapping("JOY_LEFT_STICK_X-");
 		rebel2Keymap->addAction(act);
 
 		act = new Action("RA2RIGHT", _("Aim right / menu right"));
 		act->setCustomEngineActionEvent(kScummActionInsaneRight);
 		act->addDefaultInputMapping("JOY_RIGHT");
+		rebel2Keymap->addAction(act);
+
+		act = new Action("RA2STICKUP", _("Stick up"));
+		act->setCustomBackendActionAxisEvent(kScummBackendActionRebel2AxisUp);
+		act->addDefaultInputMapping("JOY_LEFT_STICK_Y-");
+		act->addDefaultInputMapping("JOY_RIGHT_STICK_Y-");
+		rebel2Keymap->addAction(act);
+
+		act = new Action("RA2STICKDOWN", _("Stick down"));
+		act->setCustomBackendActionAxisEvent(kScummBackendActionRebel2AxisDown);
+		act->addDefaultInputMapping("JOY_LEFT_STICK_Y+");
+		act->addDefaultInputMapping("JOY_RIGHT_STICK_Y+");
+		rebel2Keymap->addAction(act);
+
+		act = new Action("RA2STICKLEFT", _("Stick left"));
+		act->setCustomBackendActionAxisEvent(kScummBackendActionRebel2AxisLeft);
+		act->addDefaultInputMapping("JOY_LEFT_STICK_X-");
+		act->addDefaultInputMapping("JOY_RIGHT_STICK_X-");
+		rebel2Keymap->addAction(act);
+
+		act = new Action("RA2STICKRIGHT", _("Stick right"));
+		act->setCustomBackendActionAxisEvent(kScummBackendActionRebel2AxisRight);
 		act->addDefaultInputMapping("JOY_LEFT_STICK_X+");
+		act->addDefaultInputMapping("JOY_RIGHT_STICK_X+");
 		rebel2Keymap->addAction(act);
 
 		act = new Action("RA2FIRE", _("Fire / select"));
diff --git a/engines/scumm/scumm.h b/engines/scumm/scumm.h
index 4a7b88a8421..94ebcfc4a7a 100644
--- a/engines/scumm/scumm.h
+++ b/engines/scumm/scumm.h
@@ -507,7 +507,11 @@ enum ScummBackendAction {
 	kScummBackendActionRebel1AxisUp = 11000,
 	kScummBackendActionRebel1AxisDown,
 	kScummBackendActionRebel1AxisLeft,
-	kScummBackendActionRebel1AxisRight
+	kScummBackendActionRebel1AxisRight,
+	kScummBackendActionRebel2AxisUp,
+	kScummBackendActionRebel2AxisDown,
+	kScummBackendActionRebel2AxisLeft,
+	kScummBackendActionRebel2AxisRight
 };
 
 extern const char *const insaneKeymapId;


Commit: 0025c4e108654c7f9224366be4fab93f6c0a7a8c
    https://github.com/scummvm/scummvm/commit/0025c4e108654c7f9224366be4fab93f6c0a7a8c
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-01T16:26:14+02:00

Commit Message:
SCUMM: RA2: match the joytick/gamepad input speed

Changed paths:
    engines/scumm/insane/rebel2/rebel.cpp


diff --git a/engines/scumm/insane/rebel2/rebel.cpp b/engines/scumm/insane/rebel2/rebel.cpp
index a91e46c276c..3b34d0bd1cc 100644
--- a/engines/scumm/insane/rebel2/rebel.cpp
+++ b/engines/scumm/insane/rebel2/rebel.cpp
@@ -1372,13 +1372,12 @@ void InsaneRebel2::updateGameplayAimFromGamepad() {
 	if (_menuInputActive || _gameState != kStateGameplay)
 		return;
 
-	// Velocity model ported from RA1 (insane/rebel1/iact.cpp preprocessMouseAxes):
-	// the digital dpad pans at full rate, while the analog stick pans proportionally
-	// to its deflection past the deadzone. This replaces the old edge-snap that pinned
-	// the reticle to the screen borders. Mouse input is untouched: with nothing held the
-	// reticle simply follows _vm->_mouse as before.
-	int velX = 0;
-	int velY = 0;
+	// The analog stick still reports the original joystick-style centered axis range
+	// [-127,127], while the dpad follows the original keyboard handler's normal
+	// movement step (FUN_405822: 3 units, or 5 with the DOS fast modifier).
+	int deltaX = 0;
+	int deltaY = 0;
+	bool activeGamepadAim = false;
 
 	const int dpadX = (_vm->getActionState(kScummActionInsaneRight) ? 1 : 0) -
 	                  (_vm->getActionState(kScummActionInsaneLeft) ? 1 : 0);
@@ -1386,16 +1385,25 @@ void InsaneRebel2::updateGameplayAimFromGamepad() {
 	                  (_vm->getActionState(kScummActionInsaneUp) ? 1 : 0);
 
 	if (dpadX || dpadY) {
-		velX = dpadX * 127;
-		velY = dpadY * 127;
+		const int kOriginalDigitalStep = 3;
+		deltaX = dpadX * kOriginalDigitalStep;
+		deltaY = dpadY * kOriginalDigitalStep;
+		activeGamepadAim = true;
 	} else {
 		const int16 ax = applyRebel2AnalogDeadzone(_joystickAxisX);
 		const int16 ay = applyRebel2AnalogDeadzone(_joystickAxisY);
-		velX = CLIP<int>((int)ax * 127 / Common::JOYAXIS_MAX, -127, 127);
-		velY = CLIP<int>((int)ay * 127 / Common::JOYAXIS_MAX, -127, 127);
+		const int velX = CLIP<int>((int)ax * 127 / Common::JOYAXIS_MAX, -127, 127);
+		const int velY = CLIP<int>((int)ay * 127 / Common::JOYAXIS_MAX, -127, 127);
+
+		if (velX || velY) {
+			const int kAnalogMaxStep = 8;
+			deltaX = velX * kAnalogMaxStep / 127;
+			deltaY = velY * kAnalogMaxStep / 127;
+			activeGamepadAim = true;
+		}
 	}
 
-	if (!velX && !velY)
+	if (!activeGamepadAim)
 		return;
 
 	// The gamepad is now driving the reticle: mark it the active aim source so
@@ -1403,10 +1411,8 @@ void InsaneRebel2::updateGameplayAimFromGamepad() {
 	_gamepadAimActive = true;
 
 	// Integrate velocity into the reticle, clamped to the 320x200 play area.
-	// kStep sets the max pixels-per-frame at full deflection; tune for feel.
-	const int kStep = 8;
-	_vm->_mouse.x = (int16)CLIP<int>(_vm->_mouse.x + velX * kStep / 127, 0, 319);
-	_vm->_mouse.y = (int16)CLIP<int>(_vm->_mouse.y + velY * kStep / 127, 0, 199);
+	_vm->_mouse.x = (int16)CLIP<int>(_vm->_mouse.x + deltaX, 0, 319);
+	_vm->_mouse.y = (int16)CLIP<int>(_vm->_mouse.y + deltaY, 0, 199);
 }
 
 bool InsaneRebel2::isBitSet(int n) {


Commit: 793b12545ac64040201408a037438c2525579849
    https://github.com/scummvm/scummvm/commit/793b12545ac64040201408a037438c2525579849
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-01T16:34:43+02:00

Commit Message:
SCUMM: RA2: make sure input starts centered

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


diff --git a/engines/scumm/insane/rebel2/levels.cpp b/engines/scumm/insane/rebel2/levels.cpp
index cef25c6b9e2..ad3f32077be 100644
--- a/engines/scumm/insane/rebel2/levels.cpp
+++ b/engines/scumm/insane/rebel2/levels.cpp
@@ -31,6 +31,9 @@
 
 namespace Scumm {
 
+static const int kRebel2GameplayAimCenterX = 160;
+static const int kRebel2GameplayAimCenterY = 100;
+
 // ---------------------------------------------------------------------------
 // Level Loading System
 // ---------------------------------------------------------------------------
@@ -363,6 +366,17 @@ void InsaneRebel2::playCreditsSequence() {
 	splayer->play("OPEN/O_CREDIT.SAN", 12);
 }
 
+void InsaneRebel2::centerGameplayAim() {
+	_vm->_mouse.x = kRebel2GameplayAimCenterX;
+	_vm->_mouse.y = kRebel2GameplayAimCenterY;
+
+	_joystickAxisX = 0;
+	_joystickAxisY = 0;
+	_gamepadAimActive = false;
+
+	smush_warpMouse(kRebel2GameplayAimCenterX, kRebel2GameplayAimCenterY, -1);
+}
+
 // runLevel -- Main level dispatcher, calls per-level handlers.
 int InsaneRebel2::runLevel(int levelId) {
 
@@ -400,12 +414,9 @@ int InsaneRebel2::runLevel(int levelId) {
 	// The original hides the cursor (ShowCursor(0)) and relies on Windows confining
 	// the mouse to the game window. Without locking, the cursor can escape the
 	// ScummVM window making the ship uncontrollable.
-	smush_warpMouse(160, 100, -1);
+	centerGameplayAim();
 	CursorMan.showMouse(false);
 	g_system->lockMouse(true);
-	// Start each level with the centered cursor as the authoritative aim source;
-	// the first gamepad input reclaims it (see updateGameplayAimFromGamepad).
-	_gamepadAimActive = false;
 
 	// Initialize common player state
 	_playerLives = 3;
diff --git a/engines/scumm/insane/rebel2/rebel.h b/engines/scumm/insane/rebel2/rebel.h
index 28161f25106..fefd80a6f96 100644
--- a/engines/scumm/insane/rebel2/rebel.h
+++ b/engines/scumm/insane/rebel2/rebel.h
@@ -459,6 +459,9 @@ public:
 	// Play retry video (phase-specific for multi-phase levels)
 	void playLevelRetryVariant(int levelId, int phase);
 
+	// Reset gameplay aim to the original centered mouse/joystick baseline.
+	void centerGameplayAim();
+
 	// Level state tracking for multi-phase levels
 	int _currentPhase;        // Current gameplay phase (1, 2, 3 for Level 2; 1, 2 for Level 3/6)
 	int _deathFrame;          // Frame number where player died (for death video selection)
diff --git a/engines/scumm/insane/rebel2/runlevels.cpp b/engines/scumm/insane/rebel2/runlevels.cpp
index 673a67def8a..2d054d0c888 100644
--- a/engines/scumm/insane/rebel2/runlevels.cpp
+++ b/engines/scumm/insane/rebel2/runlevels.cpp
@@ -190,6 +190,13 @@ InsaneRebel2::WaveEndResult InsaneRebel2::processWaveEnd(int16 mask, int16 *budg
 
 bool InsaneRebel2::playLevelSegment(const char *filename, uint16 flags, bool recordFrame) {
 	SmushPlayer *splayer = ((ScummEngine_v7 *)_vm)->_splayer;
+
+	// Retail reset helpers clear the centered input axes before gameplay starts.
+	// Do the same for playable segments, but keep seamless continuation videos
+	// (0x40, e.g. 13PLAY_B) from snapping the aim point mid-sequence.
+	if (recordFrame && (flags & 0x08) != 0 && (flags & 0x40) == 0)
+		centerGameplayAim();
+
 	splayer->setCurVideoFlags(flags);
 	splayer->play(filename, 12);
 	if (recordFrame)



From noreply at scummvm.org  Mon Jun  1 14:35:07 2026
From: noreply at scummvm.org (ScummVM-Translations)
Date: Mon,  1 Jun 2026 14:35:07 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 660e5010a45a535821210d19a0cb2e34cb97cb3b
Message-ID: <20260601143507.0BFF27005C2@ron.scummvm.net>

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

Summary:
660e5010a4 I18N: Update translations templates


Commit: 660e5010a45a535821210d19a0cb2e34cb97cb3b
    https://github.com/scummvm/scummvm/commit/660e5010a45a535821210d19a0cb2e34cb97cb3b
Author: Weblate (noreply at weblate.org)
Date: 2026-06-01T14:35:01Z

Commit Message:
I18N: Update translations templates

Changed paths:
    po/scummvm.pot


diff --git a/po/scummvm.pot b/po/scummvm.pot
index 8d434f2fd95..a0615fac668 100644
--- a/po/scummvm.pot
+++ b/po/scummvm.pot
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2026.2.1git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-05-31 14:10+0000\n"
+"POT-Creation-Date: 2026-06-01 14:35+0000\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL at ADDRESS>\n"
 "Language-Team: LANGUAGE <LL at li.org>\n"
@@ -12425,35 +12425,35 @@ msgstr ""
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1113 engines/scumm/metaengine.cpp:1176
+#: engines/scumm/metaengine.cpp:1113 engines/scumm/metaengine.cpp:1175
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1118 engines/scumm/metaengine.cpp:1182
+#: engines/scumm/metaengine.cpp:1118 engines/scumm/metaengine.cpp:1180
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1123 engines/scumm/metaengine.cpp:1188
+#: engines/scumm/metaengine.cpp:1123 engines/scumm/metaengine.cpp:1185
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1128
+#: engines/scumm/metaengine.cpp:1128 engines/scumm/metaengine.cpp:1190
 msgid "Stick up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1134
+#: engines/scumm/metaengine.cpp:1134 engines/scumm/metaengine.cpp:1196
 msgid "Stick down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1140
+#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
 msgid "Stick left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1146
+#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1208
 msgid "Stick right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1194
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1214
 msgid "Fire / select"
 msgstr ""
 
@@ -12465,11 +12465,11 @@ msgstr ""
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1199
+#: engines/scumm/metaengine.cpp:1219
 msgid "Cover / back"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1204
+#: engines/scumm/metaengine.cpp:1224
 msgid "Skip / menu"
 msgstr ""
 



From noreply at scummvm.org  Mon Jun  1 14:35:39 2026
From: noreply at scummvm.org (ScummVM-Translations)
Date: Mon,  1 Jun 2026 14:35:39 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 7c3ffa518dea6b8886c80476bc7c8836742d93e4
Message-ID: <20260601143539.48216700591@ron.scummvm.net>

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

Summary:
7c3ffa518d I18N: Update translation files


Commit: 7c3ffa518dea6b8886c80476bc7c8836742d93e4
    https://github.com/scummvm/scummvm/commit/7c3ffa518dea6b8886c80476bc7c8836742d93e4
Author: Weblate (noreply at weblate.org)
Date: 2026-06-01T14:35:18Z

Commit Message:
I18N: Update translation files

Updated by "Update PO files to match POT (msgmerge)" hook in Weblate.

Translation: ScummVM/scummvm
Translate-URL: https://translations.scummvm.org/projects/scummvm/scummvm/

Changed paths:
    po/ar.po
    po/be-tarask.po
    po/be_BY.po
    po/ca_ES.po
    po/cs_CZ.po
    po/da.po
    po/de_DE.po
    po/el.po
    po/es_ES.po
    po/eu.po
    po/fi_FI.po
    po/fr_FR.po
    po/gl_ES.po
    po/he.po
    po/hi.po
    po/hu_HU.po
    po/it_IT.po
    po/ja.po
    po/ka.po
    po/ko.po
    po/nb_NO.po
    po/nl_NL.po
    po/nn_NO.po
    po/pl_PL.po
    po/pt_BR.po
    po/pt_PT.po
    po/ro.po
    po/ru_RU.po
    po/sv_SE.po
    po/tr.po
    po/uk_UA.po
    po/zh.po
    po/zh_Hans.po
    po/zh_Hant.po


diff --git a/po/ar.po b/po/ar.po
index bfa3167691c..fc404c6b896 100644
--- a/po/ar.po
+++ b/po/ar.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-05-31 14:10+0000\n"
+"POT-Creation-Date: 2026-06-01 14:35+0000\n"
 "PO-Revision-Date: 2026-02-24 22:26+0000\n"
 "Last-Translator: Mohamed Shaaban <mohmedbashamedo at hotmail.com>\n"
 "Language-Team: Arabic <https://translations.scummvm.org/projects/scummvm/"
@@ -14214,43 +14214,43 @@ msgstr ""
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1113 engines/scumm/metaengine.cpp:1176
+#: engines/scumm/metaengine.cpp:1113 engines/scumm/metaengine.cpp:1175
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1118 engines/scumm/metaengine.cpp:1182
+#: engines/scumm/metaengine.cpp:1118 engines/scumm/metaengine.cpp:1180
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1123 engines/scumm/metaengine.cpp:1188
+#: engines/scumm/metaengine.cpp:1123 engines/scumm/metaengine.cpp:1185
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1128
+#: engines/scumm/metaengine.cpp:1128 engines/scumm/metaengine.cpp:1190
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "??????????"
 
-#: engines/scumm/metaengine.cpp:1134
+#: engines/scumm/metaengine.cpp:1134 engines/scumm/metaengine.cpp:1196
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "?????? ???????????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1140
+#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
 #, fuzzy
 #| msgid "Slide Left"
 msgid "Stick left"
 msgstr "?????????????? ?????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1146
+#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1208
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick right"
 msgstr "???????????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1194
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1214
 #, fuzzy
 #| msgid "Move left"
 msgid "Fire / select"
@@ -14266,13 +14266,13 @@ msgstr "??????????????"
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1199
+#: engines/scumm/metaengine.cpp:1219
 #, fuzzy
 #| msgid "Move Back"
 msgid "Cover / back"
 msgstr "???????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1204
+#: engines/scumm/metaengine.cpp:1224
 #, fuzzy
 #| msgid "Open main menu"
 msgid "Skip / menu"
diff --git a/po/be-tarask.po b/po/be-tarask.po
index 8f2deef1aaf..b44ab3754d2 100644
--- a/po/be-tarask.po
+++ b/po/be-tarask.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-05-31 14:10+0000\n"
+"POT-Creation-Date: 2026-06-01 14:35+0000\n"
 "PO-Revision-Date: 2023-02-26 15:01+0000\n"
 "Last-Translator: Kastu?? K. <oakum2334 at gmail.com>\n"
 "Language-Team: Belarusian (Tara??kievica) <https://translations.scummvm.org/"
@@ -14141,43 +14141,43 @@ msgstr ""
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1113 engines/scumm/metaengine.cpp:1176
+#: engines/scumm/metaengine.cpp:1113 engines/scumm/metaengine.cpp:1175
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1118 engines/scumm/metaengine.cpp:1182
+#: engines/scumm/metaengine.cpp:1118 engines/scumm/metaengine.cpp:1180
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1123 engines/scumm/metaengine.cpp:1188
+#: engines/scumm/metaengine.cpp:1123 engines/scumm/metaengine.cpp:1185
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1128
+#: engines/scumm/metaengine.cpp:1128 engines/scumm/metaengine.cpp:1190
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "??????????????"
 
-#: engines/scumm/metaengine.cpp:1134
+#: engines/scumm/metaengine.cpp:1134 engines/scumm/metaengine.cpp:1196
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "?????????????????????????? ????????"
 
-#: engines/scumm/metaengine.cpp:1140
+#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
 #, fuzzy
 #| msgid "Slide Left"
 msgid "Stick left"
 msgstr "?????????????????????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1146
+#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1208
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick right"
 msgstr "?????????????????????? ??????????????"
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1194
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1214
 #, fuzzy
 #| msgid "Move left"
 msgid "Fire / select"
@@ -14193,13 +14193,13 @@ msgstr "????????????"
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1199
+#: engines/scumm/metaengine.cpp:1219
 #, fuzzy
 #| msgid "Move Back"
 msgid "Cover / back"
 msgstr "?????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1204
+#: engines/scumm/metaengine.cpp:1224
 #, fuzzy
 #| msgid "Open main menu"
 msgid "Skip / menu"
diff --git a/po/be_BY.po b/po/be_BY.po
index 8fa7fc2cdf8..9187b491e79 100644
--- a/po/be_BY.po
+++ b/po/be_BY.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-05-31 14:10+0000\n"
+"POT-Creation-Date: 2026-06-01 14:35+0000\n"
 "PO-Revision-Date: 2021-03-21 08:24+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Belarusian <https://translations.scummvm.org/projects/scummvm/"
@@ -13951,43 +13951,43 @@ msgstr ""
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1113 engines/scumm/metaengine.cpp:1176
+#: engines/scumm/metaengine.cpp:1113 engines/scumm/metaengine.cpp:1175
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1118 engines/scumm/metaengine.cpp:1182
+#: engines/scumm/metaengine.cpp:1118 engines/scumm/metaengine.cpp:1180
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1123 engines/scumm/metaengine.cpp:1188
+#: engines/scumm/metaengine.cpp:1123 engines/scumm/metaengine.cpp:1185
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1128
+#: engines/scumm/metaengine.cpp:1128 engines/scumm/metaengine.cpp:1190
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "??????????????"
 
-#: engines/scumm/metaengine.cpp:1134
+#: engines/scumm/metaengine.cpp:1134 engines/scumm/metaengine.cpp:1196
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "???????????????????? ????????"
 
-#: engines/scumm/metaengine.cpp:1140
+#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
 #, fuzzy
 #| msgid "Slide Left"
 msgid "Stick left"
 msgstr "???????????????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1146
+#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1208
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick right"
 msgstr "???????????????? ??????????????"
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1194
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1214
 #, fuzzy
 #| msgid "Move left"
 msgid "Fire / select"
@@ -14003,13 +14003,13 @@ msgstr "???????????????? ??????????"
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1199
+#: engines/scumm/metaengine.cpp:1219
 #, fuzzy
 #| msgid "Move Back"
 msgid "Cover / back"
 msgstr "???????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1204
+#: engines/scumm/metaengine.cpp:1224
 #, fuzzy
 #| msgid "Open main menu"
 msgid "Skip / menu"
diff --git a/po/ca_ES.po b/po/ca_ES.po
index c696e0b33dc..83fb44112df 100644
--- a/po/ca_ES.po
+++ b/po/ca_ES.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.6.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-05-31 14:10+0000\n"
+"POT-Creation-Date: 2026-06-01 14:35+0000\n"
 "PO-Revision-Date: 2026-05-26 04:22+0000\n"
 "Last-Translator: Xaviu <xaviu at xaviu.com>\n"
 "Language-Team: Catalan <https://translations.scummvm.org/projects/scummvm/"
@@ -13018,43 +13018,43 @@ msgstr ""
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1113 engines/scumm/metaengine.cpp:1176
+#: engines/scumm/metaengine.cpp:1113 engines/scumm/metaengine.cpp:1175
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1118 engines/scumm/metaengine.cpp:1182
+#: engines/scumm/metaengine.cpp:1118 engines/scumm/metaengine.cpp:1180
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1123 engines/scumm/metaengine.cpp:1188
+#: engines/scumm/metaengine.cpp:1123 engines/scumm/metaengine.cpp:1185
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1128
+#: engines/scumm/metaengine.cpp:1128 engines/scumm/metaengine.cpp:1190
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "Recull"
 
-#: engines/scumm/metaengine.cpp:1134
+#: engines/scumm/metaengine.cpp:1134 engines/scumm/metaengine.cpp:1196
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "Despla??ament avall"
 
-#: engines/scumm/metaengine.cpp:1140
+#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
 #, fuzzy
 #| msgid "Slide left"
 msgid "Stick left"
 msgstr "Llisca cap a l'esquerra"
 
-#: engines/scumm/metaengine.cpp:1146
+#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1208
 #, fuzzy
 #| msgid "Slide right"
 msgid "Stick right"
 msgstr "Llisca cap a la dreta"
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1194
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1214
 #, fuzzy
 #| msgid "Move / Select"
 msgid "Fire / select"
@@ -13070,13 +13070,13 @@ msgstr "Retroc??s"
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1199
+#: engines/scumm/metaengine.cpp:1219
 #, fuzzy
 #| msgid "Move back"
 msgid "Cover / back"
 msgstr "Mou enrere"
 
-#: engines/scumm/metaengine.cpp:1204
+#: engines/scumm/metaengine.cpp:1224
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu"
diff --git a/po/cs_CZ.po b/po/cs_CZ.po
index 503063c1b21..8d7660ce8b6 100644
--- a/po/cs_CZ.po
+++ b/po/cs_CZ.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.7.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-05-31 14:10+0000\n"
+"POT-Creation-Date: 2026-06-01 14:35+0000\n"
 "PO-Revision-Date: 2026-02-09 20:22+0000\n"
 "Last-Translator: AsciiWolf <mail at asciiwolf.com>\n"
 "Language-Team: Czech <https://translations.scummvm.org/projects/scummvm/"
@@ -13761,42 +13761,42 @@ msgstr ""
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1113 engines/scumm/metaengine.cpp:1176
+#: engines/scumm/metaengine.cpp:1113 engines/scumm/metaengine.cpp:1175
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1118 engines/scumm/metaengine.cpp:1182
+#: engines/scumm/metaengine.cpp:1118 engines/scumm/metaengine.cpp:1180
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1123 engines/scumm/metaengine.cpp:1188
+#: engines/scumm/metaengine.cpp:1123 engines/scumm/metaengine.cpp:1185
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1128
+#: engines/scumm/metaengine.cpp:1128 engines/scumm/metaengine.cpp:1190
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "Sebrat"
 
-#: engines/scumm/metaengine.cpp:1134
+#: engines/scumm/metaengine.cpp:1134 engines/scumm/metaengine.cpp:1196
 #, fuzzy
 msgid "Stick down"
 msgstr "Posunout seznam dolu"
 
-#: engines/scumm/metaengine.cpp:1140
+#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
 #, fuzzy
 #| msgid "Slide Left"
 msgid "Stick left"
 msgstr "P??esunout se Doleva"
 
-#: engines/scumm/metaengine.cpp:1146
+#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1208
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick right"
 msgstr "P??esunout se Doprava"
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1194
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1214
 #, fuzzy
 msgid "Fire / select"
 msgstr "P??esunout se doleva"
@@ -13810,13 +13810,13 @@ msgstr "Vzad"
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1199
+#: engines/scumm/metaengine.cpp:1219
 #, fuzzy
 #| msgid "Move Back"
 msgid "Cover / back"
 msgstr "Vzad"
 
-#: engines/scumm/metaengine.cpp:1204
+#: engines/scumm/metaengine.cpp:1224
 #, fuzzy
 msgid "Skip / menu"
 msgstr "~N~??vrat do Spou??t????e"
diff --git a/po/da.po b/po/da.po
index f59b2cbed9f..49dd91ad47b 100644
--- a/po/da.po
+++ b/po/da.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-05-31 14:10+0000\n"
+"POT-Creation-Date: 2026-06-01 14:35+0000\n"
 "PO-Revision-Date: 2023-09-07 17:04+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Danish <https://translations.scummvm.org/projects/scummvm/"
@@ -13852,42 +13852,42 @@ msgstr ""
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1113 engines/scumm/metaengine.cpp:1176
+#: engines/scumm/metaengine.cpp:1113 engines/scumm/metaengine.cpp:1175
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1118 engines/scumm/metaengine.cpp:1182
+#: engines/scumm/metaengine.cpp:1118 engines/scumm/metaengine.cpp:1180
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1123 engines/scumm/metaengine.cpp:1188
+#: engines/scumm/metaengine.cpp:1123 engines/scumm/metaengine.cpp:1185
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1128
+#: engines/scumm/metaengine.cpp:1128 engines/scumm/metaengine.cpp:1190
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "Tag op"
 
-#: engines/scumm/metaengine.cpp:1134
+#: engines/scumm/metaengine.cpp:1134 engines/scumm/metaengine.cpp:1196
 #, fuzzy
 msgid "Stick down"
 msgstr "Rul liste ned"
 
-#: engines/scumm/metaengine.cpp:1140
+#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
 #, fuzzy
 #| msgid "Slide Left"
 msgid "Stick left"
 msgstr "Flyt til venstre"
 
-#: engines/scumm/metaengine.cpp:1146
+#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1208
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick right"
 msgstr "Flyt til h??jre"
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1194
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1214
 #, fuzzy
 msgid "Fire / select"
 msgstr "Flyt til venstre"
@@ -13902,13 +13902,13 @@ msgstr "Backend"
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1199
+#: engines/scumm/metaengine.cpp:1219
 #, fuzzy
 #| msgid "Move Back"
 msgid "Cover / back"
 msgstr "Flyt bagud"
 
-#: engines/scumm/metaengine.cpp:1204
+#: engines/scumm/metaengine.cpp:1224
 #, fuzzy
 msgid "Skip / menu"
 msgstr "~R~etur til oversigt"
diff --git a/po/de_DE.po b/po/de_DE.po
index 1903b73f968..969bd0696d8 100644
--- a/po/de_DE.po
+++ b/po/de_DE.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.10.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-05-31 14:10+0000\n"
+"POT-Creation-Date: 2026-06-01 14:35+0000\n"
 "PO-Revision-Date: 2026-05-05 16:31+0000\n"
 "Last-Translator: Lothar Serra Mari <lothar.serramari at scummvm.org>\n"
 "Language-Team: German <https://translations.scummvm.org/projects/scummvm/"
@@ -13749,43 +13749,43 @@ msgstr ""
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1113 engines/scumm/metaengine.cpp:1176
+#: engines/scumm/metaengine.cpp:1113 engines/scumm/metaengine.cpp:1175
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1118 engines/scumm/metaengine.cpp:1182
+#: engines/scumm/metaengine.cpp:1118 engines/scumm/metaengine.cpp:1180
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1123 engines/scumm/metaengine.cpp:1188
+#: engines/scumm/metaengine.cpp:1123 engines/scumm/metaengine.cpp:1185
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1128
+#: engines/scumm/metaengine.cpp:1128 engines/scumm/metaengine.cpp:1190
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "Nimm"
 
-#: engines/scumm/metaengine.cpp:1134
+#: engines/scumm/metaengine.cpp:1134 engines/scumm/metaengine.cpp:1196
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "Nach unten scrollen"
 
-#: engines/scumm/metaengine.cpp:1140
+#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
 #, fuzzy
 #| msgid "Slide left"
 msgid "Stick left"
 msgstr "Slide nach links"
 
-#: engines/scumm/metaengine.cpp:1146
+#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1208
 #, fuzzy
 #| msgid "Slide right"
 msgid "Stick right"
 msgstr "Slide nach rechts"
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1194
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1214
 #, fuzzy
 #| msgid "Move / Select"
 msgid "Fire / select"
@@ -13801,13 +13801,13 @@ msgstr "R??cktaste"
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1199
+#: engines/scumm/metaengine.cpp:1219
 #, fuzzy
 #| msgid "Move back"
 msgid "Cover / back"
 msgstr "Zur??ckgehen"
 
-#: engines/scumm/metaengine.cpp:1204
+#: engines/scumm/metaengine.cpp:1224
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu"
diff --git a/po/el.po b/po/el.po
index a86b25ce8e7..4d225495b85 100644
--- a/po/el.po
+++ b/po/el.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.10.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-05-31 14:10+0000\n"
+"POT-Creation-Date: 2026-06-01 14:35+0000\n"
 "PO-Revision-Date: 2026-04-23 00:22+0000\n"
 "Last-Translator: Antoniou Athanasios <a.antoniou79 at gmail.com>\n"
 "Language-Team: Greek <https://translations.scummvm.org/projects/scummvm/"
@@ -13904,43 +13904,43 @@ msgstr ""
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1113 engines/scumm/metaengine.cpp:1176
+#: engines/scumm/metaengine.cpp:1113 engines/scumm/metaengine.cpp:1175
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1118 engines/scumm/metaengine.cpp:1182
+#: engines/scumm/metaengine.cpp:1118 engines/scumm/metaengine.cpp:1180
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1123 engines/scumm/metaengine.cpp:1188
+#: engines/scumm/metaengine.cpp:1123 engines/scumm/metaengine.cpp:1185
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1128
+#: engines/scumm/metaengine.cpp:1128 engines/scumm/metaengine.cpp:1190
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "????????????"
 
-#: engines/scumm/metaengine.cpp:1134
+#: engines/scumm/metaengine.cpp:1134 engines/scumm/metaengine.cpp:1196
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "???????????? ????????"
 
-#: engines/scumm/metaengine.cpp:1140
+#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
 #, fuzzy
 #| msgid "Slide left"
 msgid "Stick left"
 msgstr "???????????????? ????????????????"
 
-#: engines/scumm/metaengine.cpp:1146
+#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1208
 #, fuzzy
 #| msgid "Slide right"
 msgid "Stick right"
 msgstr "???????????????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1194
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1214
 #, fuzzy
 #| msgid "Move / Select"
 msgid "Fire / select"
@@ -13956,13 +13956,13 @@ msgstr "Backspace"
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1199
+#: engines/scumm/metaengine.cpp:1219
 #, fuzzy
 #| msgid "Move Back"
 msgid "Cover / back"
 msgstr "?????????????????? ????????"
 
-#: engines/scumm/metaengine.cpp:1204
+#: engines/scumm/metaengine.cpp:1224
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu"
diff --git a/po/es_ES.po b/po/es_ES.po
index 66eb78584aa..cfe179bacf1 100644
--- a/po/es_ES.po
+++ b/po/es_ES.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.4.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-05-31 14:10+0000\n"
+"POT-Creation-Date: 2026-06-01 14:35+0000\n"
 "PO-Revision-Date: 2026-06-01 09:35+0000\n"
 "Last-Translator: IlDucci <pajaroloco_2 at hotmail.com>\n"
 "Language-Team: Spanish <https://translations.scummvm.org/projects/scummvm/"
@@ -13722,35 +13722,35 @@ msgstr "Controles de Rebel Assault"
 msgid "Aim up / menu up"
 msgstr "Apuntar arriba/Subir men??"
 
-#: engines/scumm/metaengine.cpp:1113 engines/scumm/metaengine.cpp:1176
+#: engines/scumm/metaengine.cpp:1113 engines/scumm/metaengine.cpp:1175
 msgid "Aim down / menu down"
 msgstr "Apuntar abajo/Bajar men??"
 
-#: engines/scumm/metaengine.cpp:1118 engines/scumm/metaengine.cpp:1182
+#: engines/scumm/metaengine.cpp:1118 engines/scumm/metaengine.cpp:1180
 msgid "Aim left / menu left"
 msgstr "Apuntar a izda./Men?? a izda."
 
-#: engines/scumm/metaengine.cpp:1123 engines/scumm/metaengine.cpp:1188
+#: engines/scumm/metaengine.cpp:1123 engines/scumm/metaengine.cpp:1185
 msgid "Aim right / menu right"
 msgstr "Apuntar a dcha./Men?? a dcha."
 
-#: engines/scumm/metaengine.cpp:1128
+#: engines/scumm/metaengine.cpp:1128 engines/scumm/metaengine.cpp:1190
 msgid "Stick up"
 msgstr "Stick hacia arriba"
 
-#: engines/scumm/metaengine.cpp:1134
+#: engines/scumm/metaengine.cpp:1134 engines/scumm/metaengine.cpp:1196
 msgid "Stick down"
 msgstr "Stick hacia abajo"
 
-#: engines/scumm/metaengine.cpp:1140
+#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
 msgid "Stick left"
 msgstr "Stick hacia la izquierda"
 
-#: engines/scumm/metaengine.cpp:1146
+#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1208
 msgid "Stick right"
 msgstr "Stick hacia la derecha"
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1194
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1214
 msgid "Fire / select"
 msgstr "Disparar/Seleccionar"
 
@@ -13762,11 +13762,11 @@ msgstr "Volver/Omitir"
 msgid "Rebel Assault II controls"
 msgstr "Controles de Rebel Assault II"
 
-#: engines/scumm/metaengine.cpp:1199
+#: engines/scumm/metaengine.cpp:1219
 msgid "Cover / back"
 msgstr "Cubrirse/Volver"
 
-#: engines/scumm/metaengine.cpp:1204
+#: engines/scumm/metaengine.cpp:1224
 msgid "Skip / menu"
 msgstr "Omitir/Men??"
 
diff --git a/po/eu.po b/po/eu.po
index 9c4e50335b9..455147ba79c 100644
--- a/po/eu.po
+++ b/po/eu.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.5.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-05-31 14:10+0000\n"
+"POT-Creation-Date: 2026-06-01 14:35+0000\n"
 "PO-Revision-Date: 2024-02-05 18:42+0000\n"
 "Last-Translator: Josu Igoa <josuigoa at ni.eus>\n"
 "Language-Team: Basque <https://translations.scummvm.org/projects/scummvm/"
@@ -13819,42 +13819,42 @@ msgstr ""
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1113 engines/scumm/metaengine.cpp:1176
+#: engines/scumm/metaengine.cpp:1113 engines/scumm/metaengine.cpp:1175
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1118 engines/scumm/metaengine.cpp:1182
+#: engines/scumm/metaengine.cpp:1118 engines/scumm/metaengine.cpp:1180
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1123 engines/scumm/metaengine.cpp:1188
+#: engines/scumm/metaengine.cpp:1123 engines/scumm/metaengine.cpp:1185
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1128
+#: engines/scumm/metaengine.cpp:1128 engines/scumm/metaengine.cpp:1190
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "Jaso"
 
-#: engines/scumm/metaengine.cpp:1134
+#: engines/scumm/metaengine.cpp:1134 engines/scumm/metaengine.cpp:1196
 #, fuzzy
 msgid "Stick down"
 msgstr "Behera"
 
-#: engines/scumm/metaengine.cpp:1140
+#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
 #, fuzzy
 #| msgid "Slide Left"
 msgid "Stick left"
 msgstr "Ezkerrera irristatu"
 
-#: engines/scumm/metaengine.cpp:1146
+#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1208
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick right"
 msgstr "Eskuinera irristatu"
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1194
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1214
 #, fuzzy
 msgid "Fire / select"
 msgstr "Ezkerrera irristatu"
@@ -13869,13 +13869,13 @@ msgstr "Ezabatu"
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1199
+#: engines/scumm/metaengine.cpp:1219
 #, fuzzy
 #| msgid "Move Back"
 msgid "Cover / back"
 msgstr "Atzera mugitu"
 
-#: engines/scumm/metaengine.cpp:1204
+#: engines/scumm/metaengine.cpp:1224
 #, fuzzy
 msgid "Skip / menu"
 msgstr "It~z~uli abiarazlera"
diff --git a/po/fi_FI.po b/po/fi_FI.po
index 94b6f44bdf4..bfe0a4b6747 100644
--- a/po/fi_FI.po
+++ b/po/fi_FI.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.6.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-05-31 14:10+0000\n"
+"POT-Creation-Date: 2026-06-01 14:35+0000\n"
 "PO-Revision-Date: 2026-02-07 15:22+0000\n"
 "Last-Translator: Timo Mikkolainen <tmikkola at gmail.com>\n"
 "Language-Team: Finnish <https://translations.scummvm.org/projects/scummvm/"
@@ -13949,43 +13949,43 @@ msgstr ""
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1113 engines/scumm/metaengine.cpp:1176
+#: engines/scumm/metaengine.cpp:1113 engines/scumm/metaengine.cpp:1175
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1118 engines/scumm/metaengine.cpp:1182
+#: engines/scumm/metaengine.cpp:1118 engines/scumm/metaengine.cpp:1180
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1123 engines/scumm/metaengine.cpp:1188
+#: engines/scumm/metaengine.cpp:1123 engines/scumm/metaengine.cpp:1185
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1128
+#: engines/scumm/metaengine.cpp:1128 engines/scumm/metaengine.cpp:1190
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "Ota"
 
-#: engines/scumm/metaengine.cpp:1134
+#: engines/scumm/metaengine.cpp:1134 engines/scumm/metaengine.cpp:1196
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "Vierit?? alas"
 
-#: engines/scumm/metaengine.cpp:1140
+#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
 #, fuzzy
 #| msgid "Slide left"
 msgid "Stick left"
 msgstr "Liu'u vasemmalle"
 
-#: engines/scumm/metaengine.cpp:1146
+#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1208
 #, fuzzy
 #| msgid "Slide right"
 msgid "Stick right"
 msgstr "Liu'u oikealle"
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1194
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1214
 #, fuzzy
 #| msgid "Move / Select"
 msgid "Fire / select"
@@ -14001,13 +14001,13 @@ msgstr "Askelpalautin"
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1199
+#: engines/scumm/metaengine.cpp:1219
 #, fuzzy
 #| msgid "Move back"
 msgid "Cover / back"
 msgstr "Liiku taaksep??in"
 
-#: engines/scumm/metaengine.cpp:1204
+#: engines/scumm/metaengine.cpp:1224
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu"
diff --git a/po/fr_FR.po b/po/fr_FR.po
index 7f847ed26dc..f106cacfcda 100644
--- a/po/fr_FR.po
+++ b/po/fr_FR.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-05-31 14:10+0000\n"
+"POT-Creation-Date: 2026-06-01 14:35+0000\n"
 "PO-Revision-Date: 2026-06-01 09:35+0000\n"
 "Last-Translator: Purple T <ZEONK at hotmail.com>\n"
 "Language-Team: French <https://translations.scummvm.org/projects/scummvm/"
@@ -13747,35 +13747,35 @@ msgstr "Contr??les Rebel Assault"
 msgid "Aim up / menu up"
 msgstr "Vis??e haut / Menu haut"
 
-#: engines/scumm/metaengine.cpp:1113 engines/scumm/metaengine.cpp:1176
+#: engines/scumm/metaengine.cpp:1113 engines/scumm/metaengine.cpp:1175
 msgid "Aim down / menu down"
 msgstr "Vis??e bas / Menu bas"
 
-#: engines/scumm/metaengine.cpp:1118 engines/scumm/metaengine.cpp:1182
+#: engines/scumm/metaengine.cpp:1118 engines/scumm/metaengine.cpp:1180
 msgid "Aim left / menu left"
 msgstr "Vis??e gauche / menu gauche"
 
-#: engines/scumm/metaengine.cpp:1123 engines/scumm/metaengine.cpp:1188
+#: engines/scumm/metaengine.cpp:1123 engines/scumm/metaengine.cpp:1185
 msgid "Aim right / menu right"
 msgstr "Vis??e droite / Menu droite"
 
-#: engines/scumm/metaengine.cpp:1128
+#: engines/scumm/metaengine.cpp:1128 engines/scumm/metaengine.cpp:1190
 msgid "Stick up"
 msgstr "Manche haut"
 
-#: engines/scumm/metaengine.cpp:1134
+#: engines/scumm/metaengine.cpp:1134 engines/scumm/metaengine.cpp:1196
 msgid "Stick down"
 msgstr "Manche bas"
 
-#: engines/scumm/metaengine.cpp:1140
+#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
 msgid "Stick left"
 msgstr "Manche gauche"
 
-#: engines/scumm/metaengine.cpp:1146
+#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1208
 msgid "Stick right"
 msgstr "Manche droite"
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1194
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1214
 msgid "Fire / select"
 msgstr "Tirer / S??lectionner"
 
@@ -13787,11 +13787,11 @@ msgstr "Retour / passer"
 msgid "Rebel Assault II controls"
 msgstr "Contr??les Rebel Assault II"
 
-#: engines/scumm/metaengine.cpp:1199
+#: engines/scumm/metaengine.cpp:1219
 msgid "Cover / back"
 msgstr "Couverture / retour"
 
-#: engines/scumm/metaengine.cpp:1204
+#: engines/scumm/metaengine.cpp:1224
 msgid "Skip / menu"
 msgstr "Passer / menu"
 
diff --git a/po/gl_ES.po b/po/gl_ES.po
index f8e253e57b1..bdf9f428f07 100644
--- a/po/gl_ES.po
+++ b/po/gl_ES.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-05-31 14:10+0000\n"
+"POT-Creation-Date: 2026-06-01 14:35+0000\n"
 "PO-Revision-Date: 2020-09-26 21:13+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Galician <https://translations.scummvm.org/projects/scummvm/"
@@ -13775,42 +13775,42 @@ msgstr ""
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1113 engines/scumm/metaengine.cpp:1176
+#: engines/scumm/metaengine.cpp:1113 engines/scumm/metaengine.cpp:1175
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1118 engines/scumm/metaengine.cpp:1182
+#: engines/scumm/metaengine.cpp:1118 engines/scumm/metaengine.cpp:1180
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1123 engines/scumm/metaengine.cpp:1188
+#: engines/scumm/metaengine.cpp:1123 engines/scumm/metaengine.cpp:1185
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1128
+#: engines/scumm/metaengine.cpp:1128 engines/scumm/metaengine.cpp:1190
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "Coller"
 
-#: engines/scumm/metaengine.cpp:1134
+#: engines/scumm/metaengine.cpp:1134 engines/scumm/metaengine.cpp:1196
 #, fuzzy
 msgid "Stick down"
 msgstr "Baixar lista"
 
-#: engines/scumm/metaengine.cpp:1140
+#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
 #, fuzzy
 #| msgid "Slide Left"
 msgid "Stick left"
 msgstr "Esvarar ?? esquerda"
 
-#: engines/scumm/metaengine.cpp:1146
+#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1208
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick right"
 msgstr "Esvarar ?? dereita"
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1194
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1214
 #, fuzzy
 msgid "Fire / select"
 msgstr "Mover ?? esquerda"
@@ -13824,13 +13824,13 @@ msgstr "Mover cara atr??s"
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1199
+#: engines/scumm/metaengine.cpp:1219
 #, fuzzy
 #| msgid "Move Back"
 msgid "Cover / back"
 msgstr "Mover cara atr??s"
 
-#: engines/scumm/metaengine.cpp:1204
+#: engines/scumm/metaengine.cpp:1224
 #, fuzzy
 msgid "Skip / menu"
 msgstr "~V~olver ao Iniciador"
diff --git a/po/he.po b/po/he.po
index 512cc632b28..5561c8404a5 100644
--- a/po/he.po
+++ b/po/he.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.1.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-05-31 14:10+0000\n"
+"POT-Creation-Date: 2026-06-01 14:35+0000\n"
 "PO-Revision-Date: 2025-04-09 20:18+0000\n"
 "Last-Translator: Niv Baehr <bloop93 at gmail.com>\n"
 "Language-Team: Hebrew <https://translations.scummvm.org/projects/scummvm/"
@@ -13996,43 +13996,43 @@ msgstr ""
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1113 engines/scumm/metaengine.cpp:1176
+#: engines/scumm/metaengine.cpp:1113 engines/scumm/metaengine.cpp:1175
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1118 engines/scumm/metaengine.cpp:1182
+#: engines/scumm/metaengine.cpp:1118 engines/scumm/metaengine.cpp:1180
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1123 engines/scumm/metaengine.cpp:1188
+#: engines/scumm/metaengine.cpp:1123 engines/scumm/metaengine.cpp:1185
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1128
+#: engines/scumm/metaengine.cpp:1128 engines/scumm/metaengine.cpp:1190
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "??????????"
 
-#: engines/scumm/metaengine.cpp:1134
+#: engines/scumm/metaengine.cpp:1134 engines/scumm/metaengine.cpp:1196
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "?????????? ??????"
 
-#: engines/scumm/metaengine.cpp:1140
+#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
 #, fuzzy
 #| msgid "Slide Left"
 msgid "Stick left"
 msgstr "???????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1146
+#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1208
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick right"
 msgstr "???????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1194
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1214
 #, fuzzy
 #| msgid "Move left"
 msgid "Fire / select"
@@ -14048,13 +14048,13 @@ msgstr "??????????"
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1199
+#: engines/scumm/metaengine.cpp:1219
 #, fuzzy
 #| msgid "Move Back"
 msgid "Cover / back"
 msgstr "??????????"
 
-#: engines/scumm/metaengine.cpp:1204
+#: engines/scumm/metaengine.cpp:1224
 #, fuzzy
 #| msgid "Open main menu"
 msgid "Skip / menu"
diff --git a/po/hi.po b/po/hi.po
index 31953de130a..afe12a7051d 100644
--- a/po/hi.po
+++ b/po/hi.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-05-31 14:10+0000\n"
+"POT-Creation-Date: 2026-06-01 14:35+0000\n"
 "PO-Revision-Date: 2026-05-12 22:22+0000\n"
 "Last-Translator: KARTIK SAXENA <kartiksaxena2005 at gmail.com>\n"
 "Language-Team: Hindi <https://translations.scummvm.org/projects/scummvm/"
@@ -13254,40 +13254,40 @@ msgstr ""
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1113 engines/scumm/metaengine.cpp:1176
+#: engines/scumm/metaengine.cpp:1113 engines/scumm/metaengine.cpp:1175
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1118 engines/scumm/metaengine.cpp:1182
+#: engines/scumm/metaengine.cpp:1118 engines/scumm/metaengine.cpp:1180
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1123 engines/scumm/metaengine.cpp:1188
+#: engines/scumm/metaengine.cpp:1123 engines/scumm/metaengine.cpp:1185
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1128
+#: engines/scumm/metaengine.cpp:1128 engines/scumm/metaengine.cpp:1190
 msgid "Stick up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1134
+#: engines/scumm/metaengine.cpp:1134 engines/scumm/metaengine.cpp:1196
 #, fuzzy
 msgid "Stick down"
 msgstr "???????????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1140
+#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick left"
 msgstr "?????????????????? ?????????????????? ?????????"
 
-#: engines/scumm/metaengine.cpp:1146
+#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1208
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick right"
 msgstr "?????????????????? ?????????????????? ?????????"
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1194
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1214
 #, fuzzy
 #| msgid "Move Left"
 msgid "Fire / select"
@@ -13303,13 +13303,13 @@ msgstr "????????????????????????"
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1199
+#: engines/scumm/metaengine.cpp:1219
 #, fuzzy
 #| msgid "Move Forward"
 msgid "Cover / back"
 msgstr "????????? ?????????"
 
-#: engines/scumm/metaengine.cpp:1204
+#: engines/scumm/metaengine.cpp:1224
 #, fuzzy
 #| msgid "Skip autosave"
 msgid "Skip / menu"
diff --git a/po/hu_HU.po b/po/hu_HU.po
index 32506b10c72..243e4072ead 100644
--- a/po/hu_HU.po
+++ b/po/hu_HU.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-05-31 14:10+0000\n"
+"POT-Creation-Date: 2026-06-01 14:35+0000\n"
 "PO-Revision-Date: 2025-03-07 00:18+0000\n"
 "Last-Translator: Hov??nszki Tam??s <gahex68379 at lofiey.com>\n"
 "Language-Team: Hungarian <https://translations.scummvm.org/projects/scummvm/"
@@ -14456,43 +14456,43 @@ msgstr ""
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1113 engines/scumm/metaengine.cpp:1176
+#: engines/scumm/metaengine.cpp:1113 engines/scumm/metaengine.cpp:1175
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1118 engines/scumm/metaengine.cpp:1182
+#: engines/scumm/metaengine.cpp:1118 engines/scumm/metaengine.cpp:1180
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1123 engines/scumm/metaengine.cpp:1188
+#: engines/scumm/metaengine.cpp:1123 engines/scumm/metaengine.cpp:1185
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1128
+#: engines/scumm/metaengine.cpp:1128 engines/scumm/metaengine.cpp:1190
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "Felvesz"
 
-#: engines/scumm/metaengine.cpp:1134
+#: engines/scumm/metaengine.cpp:1134 engines/scumm/metaengine.cpp:1196
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "G??rget??s LE"
 
-#: engines/scumm/metaengine.cpp:1140
+#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
 #, fuzzy
 #| msgid "Slide Left"
 msgid "Stick left"
 msgstr "Cs??sz??s balra"
 
-#: engines/scumm/metaengine.cpp:1146
+#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1208
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick right"
 msgstr "Cs??sz??s jobbra"
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1194
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1214
 #, fuzzy
 #| msgid "Move left"
 msgid "Fire / select"
@@ -14508,13 +14508,13 @@ msgstr "T??rl??sgomb"
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1199
+#: engines/scumm/metaengine.cpp:1219
 #, fuzzy
 #| msgid "Move Back"
 msgid "Cover / back"
 msgstr "Mozg??s h??tra"
 
-#: engines/scumm/metaengine.cpp:1204
+#: engines/scumm/metaengine.cpp:1224
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu"
diff --git a/po/it_IT.po b/po/it_IT.po
index d5736acdecd..f68a94966b4 100644
--- a/po/it_IT.po
+++ b/po/it_IT.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-05-31 14:10+0000\n"
+"POT-Creation-Date: 2026-06-01 14:35+0000\n"
 "PO-Revision-Date: 2026-05-25 15:48+0000\n"
 "Last-Translator: Walter Agazzi <walter.agazzi at protonmail.com>\n"
 "Language-Team: Italian <https://translations.scummvm.org/projects/scummvm/"
@@ -13697,43 +13697,43 @@ msgstr ""
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1113 engines/scumm/metaengine.cpp:1176
+#: engines/scumm/metaengine.cpp:1113 engines/scumm/metaengine.cpp:1175
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1118 engines/scumm/metaengine.cpp:1182
+#: engines/scumm/metaengine.cpp:1118 engines/scumm/metaengine.cpp:1180
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1123 engines/scumm/metaengine.cpp:1188
+#: engines/scumm/metaengine.cpp:1123 engines/scumm/metaengine.cpp:1185
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1128
+#: engines/scumm/metaengine.cpp:1128 engines/scumm/metaengine.cpp:1190
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "Raccogli"
 
-#: engines/scumm/metaengine.cpp:1134
+#: engines/scumm/metaengine.cpp:1134 engines/scumm/metaengine.cpp:1196
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "Scorri verso il basso"
 
-#: engines/scumm/metaengine.cpp:1140
+#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
 #, fuzzy
 #| msgid "Slide left"
 msgid "Stick left"
 msgstr "Scorri a sinistra"
 
-#: engines/scumm/metaengine.cpp:1146
+#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1208
 #, fuzzy
 #| msgid "Slide right"
 msgid "Stick right"
 msgstr "Scorri a destra"
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1194
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1214
 #, fuzzy
 #| msgid "Move / Select"
 msgid "Fire / select"
@@ -13749,13 +13749,13 @@ msgstr "Backspace"
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1199
+#: engines/scumm/metaengine.cpp:1219
 #, fuzzy
 #| msgid "Move back"
 msgid "Cover / back"
 msgstr "Arretra"
 
-#: engines/scumm/metaengine.cpp:1204
+#: engines/scumm/metaengine.cpp:1224
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu"
diff --git a/po/ja.po b/po/ja.po
index ee512dcfe91..970b70f2e39 100644
--- a/po/ja.po
+++ b/po/ja.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-05-31 14:10+0000\n"
+"POT-Creation-Date: 2026-06-01 14:35+0000\n"
 "PO-Revision-Date: 2026-04-26 07:22+0000\n"
 "Last-Translator: VAN-Gluon <van.gluonp at gmail.com>\n"
 "Language-Team: Japanese <https://translations.scummvm.org/projects/scummvm/"
@@ -14100,43 +14100,43 @@ msgstr ""
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1113 engines/scumm/metaengine.cpp:1176
+#: engines/scumm/metaengine.cpp:1113 engines/scumm/metaengine.cpp:1175
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1118 engines/scumm/metaengine.cpp:1182
+#: engines/scumm/metaengine.cpp:1118 engines/scumm/metaengine.cpp:1180
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1123 engines/scumm/metaengine.cpp:1188
+#: engines/scumm/metaengine.cpp:1123 engines/scumm/metaengine.cpp:1185
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1128
+#: engines/scumm/metaengine.cpp:1128 engines/scumm/metaengine.cpp:1190
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "??????"
 
-#: engines/scumm/metaengine.cpp:1134
+#: engines/scumm/metaengine.cpp:1134 engines/scumm/metaengine.cpp:1196
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "?????????????????????"
 
-#: engines/scumm/metaengine.cpp:1140
+#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
 #, fuzzy
 #| msgid "Slide Left"
 msgid "Stick left"
 msgstr "??????????????????"
 
-#: engines/scumm/metaengine.cpp:1146
+#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1208
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick right"
 msgstr "??????????????????"
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1194
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1214
 #, fuzzy
 #| msgid "Move left"
 msgid "Fire / select"
@@ -14152,13 +14152,13 @@ msgstr "?????????????????????"
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1199
+#: engines/scumm/metaengine.cpp:1219
 #, fuzzy
 #| msgid "Move Back"
 msgid "Cover / back"
 msgstr "??????"
 
-#: engines/scumm/metaengine.cpp:1204
+#: engines/scumm/metaengine.cpp:1224
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu"
diff --git a/po/ka.po b/po/ka.po
index 6e308048bad..d8b60230e34 100644
--- a/po/ka.po
+++ b/po/ka.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-05-31 14:10+0000\n"
+"POT-Creation-Date: 2026-06-01 14:35+0000\n"
 "PO-Revision-Date: 2026-05-09 12:22+0000\n"
 "Last-Translator: NorwayFun <temuri.doghonadze at gmail.com>\n"
 "Language-Team: Georgian <https://translations.scummvm.org/projects/scummvm/"
@@ -12465,43 +12465,43 @@ msgstr ""
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1113 engines/scumm/metaengine.cpp:1176
+#: engines/scumm/metaengine.cpp:1113 engines/scumm/metaengine.cpp:1175
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1118 engines/scumm/metaengine.cpp:1182
+#: engines/scumm/metaengine.cpp:1118 engines/scumm/metaengine.cpp:1180
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1123 engines/scumm/metaengine.cpp:1188
+#: engines/scumm/metaengine.cpp:1123 engines/scumm/metaengine.cpp:1185
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1128
+#: engines/scumm/metaengine.cpp:1128 engines/scumm/metaengine.cpp:1190
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "???????????????"
 
-#: engines/scumm/metaengine.cpp:1134
+#: engines/scumm/metaengine.cpp:1134 engines/scumm/metaengine.cpp:1196
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "??????????????? ????????????????????????"
 
-#: engines/scumm/metaengine.cpp:1140
+#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
 #, fuzzy
 #| msgid "Slide left"
 msgid "Stick left"
 msgstr "?????????????????????????????? ????????????????????????"
 
-#: engines/scumm/metaengine.cpp:1146
+#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1208
 #, fuzzy
 #| msgid "Slide right"
 msgid "Stick right"
 msgstr "?????????????????????????????? ????????????????????????"
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1194
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1214
 #, fuzzy
 #| msgid "Move / Select"
 msgid "Fire / select"
@@ -12517,13 +12517,13 @@ msgstr "Backspace"
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1199
+#: engines/scumm/metaengine.cpp:1219
 #, fuzzy
 #| msgid "Move back"
 msgid "Cover / back"
 msgstr "???????????? ????????????????????????"
 
-#: engines/scumm/metaengine.cpp:1204
+#: engines/scumm/metaengine.cpp:1224
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu"
diff --git a/po/ko.po b/po/ko.po
index 649b0a3583d..8cb0e52511d 100644
--- a/po/ko.po
+++ b/po/ko.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-05-31 14:10+0000\n"
+"POT-Creation-Date: 2026-06-01 14:35+0000\n"
 "PO-Revision-Date: 2026-05-19 20:22+0000\n"
 "Last-Translator: Hoseok Seo <ddinghoya at gmail.com>\n"
 "Language-Team: Korean <https://translations.scummvm.org/projects/scummvm/"
@@ -13377,43 +13377,43 @@ msgstr ""
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1113 engines/scumm/metaengine.cpp:1176
+#: engines/scumm/metaengine.cpp:1113 engines/scumm/metaengine.cpp:1175
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1118 engines/scumm/metaengine.cpp:1182
+#: engines/scumm/metaengine.cpp:1118 engines/scumm/metaengine.cpp:1180
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1123 engines/scumm/metaengine.cpp:1188
+#: engines/scumm/metaengine.cpp:1123 engines/scumm/metaengine.cpp:1185
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1128
+#: engines/scumm/metaengine.cpp:1128 engines/scumm/metaengine.cpp:1190
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "??????"
 
-#: engines/scumm/metaengine.cpp:1134
+#: engines/scumm/metaengine.cpp:1134 engines/scumm/metaengine.cpp:1196
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "????????? ?????????"
 
-#: engines/scumm/metaengine.cpp:1140
+#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
 #, fuzzy
 #| msgid "Slide left"
 msgid "Stick left"
 msgstr "???????????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1146
+#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1208
 #, fuzzy
 #| msgid "Slide right"
 msgid "Stick right"
 msgstr "????????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1194
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1214
 #, fuzzy
 #| msgid "Move / Select"
 msgid "Fire / select"
@@ -13429,13 +13429,13 @@ msgstr "Backspace ???"
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1199
+#: engines/scumm/metaengine.cpp:1219
 #, fuzzy
 #| msgid "Move back"
 msgid "Cover / back"
 msgstr "?????? ??????"
 
-#: engines/scumm/metaengine.cpp:1204
+#: engines/scumm/metaengine.cpp:1224
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu"
diff --git a/po/nb_NO.po b/po/nb_NO.po
index ab7d2dbb63c..96ddc99c1bd 100644
--- a/po/nb_NO.po
+++ b/po/nb_NO.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-05-31 14:10+0000\n"
+"POT-Creation-Date: 2026-06-01 14:35+0000\n"
 "PO-Revision-Date: 2026-02-07 15:22+0000\n"
 "Last-Translator: Einar Johan Tr??an S??m??en <einarjohants at gmail.com>\n"
 "Language-Team: Norwegian Bokm??l <https://translations.scummvm.org/projects/"
@@ -14547,43 +14547,43 @@ msgstr ""
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1113 engines/scumm/metaengine.cpp:1176
+#: engines/scumm/metaengine.cpp:1113 engines/scumm/metaengine.cpp:1175
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1118 engines/scumm/metaengine.cpp:1182
+#: engines/scumm/metaengine.cpp:1118 engines/scumm/metaengine.cpp:1180
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1123 engines/scumm/metaengine.cpp:1188
+#: engines/scumm/metaengine.cpp:1123 engines/scumm/metaengine.cpp:1185
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1128
+#: engines/scumm/metaengine.cpp:1128 engines/scumm/metaengine.cpp:1190
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "Plukk opp"
 
-#: engines/scumm/metaengine.cpp:1134
+#: engines/scumm/metaengine.cpp:1134 engines/scumm/metaengine.cpp:1196
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "Bla ned"
 
-#: engines/scumm/metaengine.cpp:1140
+#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
 #, fuzzy
 #| msgid "Slide Left"
 msgid "Stick left"
 msgstr "Skli mot Venstre"
 
-#: engines/scumm/metaengine.cpp:1146
+#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1208
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick right"
 msgstr "Skli mot H??yre"
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1194
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1214
 #, fuzzy
 #| msgid "Move left"
 msgid "Fire / select"
@@ -14599,13 +14599,13 @@ msgstr "Rettetast"
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1199
+#: engines/scumm/metaengine.cpp:1219
 #, fuzzy
 #| msgid "Move Back"
 msgid "Cover / back"
 msgstr "Beveg Bakover"
 
-#: engines/scumm/metaengine.cpp:1204
+#: engines/scumm/metaengine.cpp:1224
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu"
diff --git a/po/nl_NL.po b/po/nl_NL.po
index 5d5ccc49342..87e9a1bffed 100644
--- a/po/nl_NL.po
+++ b/po/nl_NL.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.9.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-05-31 14:10+0000\n"
+"POT-Creation-Date: 2026-06-01 14:35+0000\n"
 "PO-Revision-Date: 2026-02-11 14:23+0000\n"
 "Last-Translator: SecularSteve <fairfull.playing at gmail.com>\n"
 "Language-Team: Dutch <https://translations.scummvm.org/projects/scummvm/"
@@ -13740,43 +13740,43 @@ msgstr ""
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1113 engines/scumm/metaengine.cpp:1176
+#: engines/scumm/metaengine.cpp:1113 engines/scumm/metaengine.cpp:1175
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1118 engines/scumm/metaengine.cpp:1182
+#: engines/scumm/metaengine.cpp:1118 engines/scumm/metaengine.cpp:1180
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1123 engines/scumm/metaengine.cpp:1188
+#: engines/scumm/metaengine.cpp:1123 engines/scumm/metaengine.cpp:1185
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1128
+#: engines/scumm/metaengine.cpp:1128 engines/scumm/metaengine.cpp:1190
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "Pak op"
 
-#: engines/scumm/metaengine.cpp:1134
+#: engines/scumm/metaengine.cpp:1134 engines/scumm/metaengine.cpp:1196
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "Scroll omlaag"
 
-#: engines/scumm/metaengine.cpp:1140
+#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
 #, fuzzy
 #| msgid "Slide left"
 msgid "Stick left"
 msgstr "Schuif naar link"
 
-#: engines/scumm/metaengine.cpp:1146
+#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1208
 #, fuzzy
 #| msgid "Slide right"
 msgid "Stick right"
 msgstr "Schuif naar rechts"
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1194
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1214
 #, fuzzy
 #| msgid "Move / Select"
 msgid "Fire / select"
@@ -13792,13 +13792,13 @@ msgstr "Backspace"
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1199
+#: engines/scumm/metaengine.cpp:1219
 #, fuzzy
 #| msgid "Move back"
 msgid "Cover / back"
 msgstr "Teruggaan"
 
-#: engines/scumm/metaengine.cpp:1204
+#: engines/scumm/metaengine.cpp:1224
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu"
diff --git a/po/nn_NO.po b/po/nn_NO.po
index 5c50524f7a7..14d7272b2dc 100644
--- a/po/nn_NO.po
+++ b/po/nn_NO.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-05-31 14:10+0000\n"
+"POT-Creation-Date: 2026-06-01 14:35+0000\n"
 "PO-Revision-Date: 2023-12-14 14:28+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Norwegian Nynorsk <https://translations.scummvm.org/projects/"
@@ -13672,42 +13672,42 @@ msgstr ""
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1113 engines/scumm/metaengine.cpp:1176
+#: engines/scumm/metaengine.cpp:1113 engines/scumm/metaengine.cpp:1175
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1118 engines/scumm/metaengine.cpp:1182
+#: engines/scumm/metaengine.cpp:1118 engines/scumm/metaengine.cpp:1180
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1123 engines/scumm/metaengine.cpp:1188
+#: engines/scumm/metaengine.cpp:1123 engines/scumm/metaengine.cpp:1185
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1128
+#: engines/scumm/metaengine.cpp:1128 engines/scumm/metaengine.cpp:1190
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "Plukk opp"
 
-#: engines/scumm/metaengine.cpp:1134
+#: engines/scumm/metaengine.cpp:1134 engines/scumm/metaengine.cpp:1196
 #, fuzzy
 msgid "Stick down"
 msgstr "Bla liste ned"
 
-#: engines/scumm/metaengine.cpp:1140
+#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
 #, fuzzy
 #| msgid "Slide Left"
 msgid "Stick left"
 msgstr "Skli til Venstre"
 
-#: engines/scumm/metaengine.cpp:1146
+#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1208
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick right"
 msgstr "Skli til H??yre"
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1194
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1214
 #, fuzzy
 msgid "Fire / select"
 msgstr "Skli til Venstre"
@@ -13722,13 +13722,13 @@ msgstr "Tilbaketast"
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1199
+#: engines/scumm/metaengine.cpp:1219
 #, fuzzy
 #| msgid "Move Back"
 msgid "Cover / back"
 msgstr "Beveg Bakover"
 
-#: engines/scumm/metaengine.cpp:1204
+#: engines/scumm/metaengine.cpp:1224
 #, fuzzy
 msgid "Skip / menu"
 msgstr "Tilbake til Oppsta~r~tar"
diff --git a/po/pl_PL.po b/po/pl_PL.po
index 178feb57d29..eace59066f2 100644
--- a/po/pl_PL.po
+++ b/po/pl_PL.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-05-31 14:10+0000\n"
+"POT-Creation-Date: 2026-06-01 14:35+0000\n"
 "PO-Revision-Date: 2026-04-17 00:22+0000\n"
 "Last-Translator: SkiffPL <skiff at windowslive.com>\n"
 "Language-Team: Polish <https://translations.scummvm.org/projects/scummvm/"
@@ -13919,43 +13919,43 @@ msgstr ""
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1113 engines/scumm/metaengine.cpp:1176
+#: engines/scumm/metaengine.cpp:1113 engines/scumm/metaengine.cpp:1175
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1118 engines/scumm/metaengine.cpp:1182
+#: engines/scumm/metaengine.cpp:1118 engines/scumm/metaengine.cpp:1180
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1123 engines/scumm/metaengine.cpp:1188
+#: engines/scumm/metaengine.cpp:1123 engines/scumm/metaengine.cpp:1185
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1128
+#: engines/scumm/metaengine.cpp:1128 engines/scumm/metaengine.cpp:1190
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "Podnie??"
 
-#: engines/scumm/metaengine.cpp:1134
+#: engines/scumm/metaengine.cpp:1134 engines/scumm/metaengine.cpp:1196
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "Przewi?? w d????"
 
-#: engines/scumm/metaengine.cpp:1140
+#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
 #, fuzzy
 #| msgid "Slide left"
 msgid "Stick left"
 msgstr "??lizg w lewo"
 
-#: engines/scumm/metaengine.cpp:1146
+#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1208
 #, fuzzy
 #| msgid "Slide right"
 msgid "Stick right"
 msgstr "??lizg w prawo"
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1194
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1214
 #, fuzzy
 #| msgid "Move / Select"
 msgid "Fire / select"
@@ -13971,13 +13971,13 @@ msgstr "Backspace"
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1199
+#: engines/scumm/metaengine.cpp:1219
 #, fuzzy
 #| msgid "Move back"
 msgid "Cover / back"
 msgstr "Ruch do ty??u"
 
-#: engines/scumm/metaengine.cpp:1204
+#: engines/scumm/metaengine.cpp:1224
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu"
diff --git a/po/pt_BR.po b/po/pt_BR.po
index 26291b54102..a35db613a10 100644
--- a/po/pt_BR.po
+++ b/po/pt_BR.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-05-31 14:10+0000\n"
+"POT-Creation-Date: 2026-06-01 14:35+0000\n"
 "PO-Revision-Date: 2024-11-16 02:13+0000\n"
 "Last-Translator: Marcel Souza Lemes <marcosoutsider at gmail.com>\n"
 "Language-Team: Portuguese (Brazil) <https://translations.scummvm.org/"
@@ -14519,43 +14519,43 @@ msgstr ""
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1113 engines/scumm/metaengine.cpp:1176
+#: engines/scumm/metaengine.cpp:1113 engines/scumm/metaengine.cpp:1175
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1118 engines/scumm/metaengine.cpp:1182
+#: engines/scumm/metaengine.cpp:1118 engines/scumm/metaengine.cpp:1180
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1123 engines/scumm/metaengine.cpp:1188
+#: engines/scumm/metaengine.cpp:1123 engines/scumm/metaengine.cpp:1185
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1128
+#: engines/scumm/metaengine.cpp:1128 engines/scumm/metaengine.cpp:1190
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "Pegar"
 
-#: engines/scumm/metaengine.cpp:1134
+#: engines/scumm/metaengine.cpp:1134 engines/scumm/metaengine.cpp:1196
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "Rolar para baixo"
 
-#: engines/scumm/metaengine.cpp:1140
+#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
 #, fuzzy
 #| msgid "Slide Left"
 msgid "Stick left"
 msgstr "Deslizar para Esquerda"
 
-#: engines/scumm/metaengine.cpp:1146
+#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1208
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick right"
 msgstr "Deslizar para Direita"
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1194
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1214
 #, fuzzy
 #| msgid "Move left"
 msgid "Fire / select"
@@ -14571,13 +14571,13 @@ msgstr "Retroceder"
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1199
+#: engines/scumm/metaengine.cpp:1219
 #, fuzzy
 #| msgid "Move Back"
 msgid "Cover / back"
 msgstr "Mover para Tr??s"
 
-#: engines/scumm/metaengine.cpp:1204
+#: engines/scumm/metaengine.cpp:1224
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu"
diff --git a/po/pt_PT.po b/po/pt_PT.po
index a365768b22d..e66ef2ce01a 100644
--- a/po/pt_PT.po
+++ b/po/pt_PT.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.10.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-05-31 14:10+0000\n"
+"POT-Creation-Date: 2026-06-01 14:35+0000\n"
 "PO-Revision-Date: 2022-06-05 14:09+0000\n"
 "Last-Translator: Daniel <supsuper at gmail.com>\n"
 "Language-Team: Portuguese (Portugal) <https://translations.scummvm.org/"
@@ -13794,43 +13794,43 @@ msgstr ""
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1113 engines/scumm/metaengine.cpp:1176
+#: engines/scumm/metaengine.cpp:1113 engines/scumm/metaengine.cpp:1175
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1118 engines/scumm/metaengine.cpp:1182
+#: engines/scumm/metaengine.cpp:1118 engines/scumm/metaengine.cpp:1180
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1123 engines/scumm/metaengine.cpp:1188
+#: engines/scumm/metaengine.cpp:1123 engines/scumm/metaengine.cpp:1185
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1128
+#: engines/scumm/metaengine.cpp:1128 engines/scumm/metaengine.cpp:1190
 #, fuzzy
 #| msgid "Left Stick X"
 msgid "Stick up"
 msgstr "Anal??gico Esquerdo X"
 
-#: engines/scumm/metaengine.cpp:1134
+#: engines/scumm/metaengine.cpp:1134 engines/scumm/metaengine.cpp:1196
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "Deslizar para baixo"
 
-#: engines/scumm/metaengine.cpp:1140
+#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
 #, fuzzy
 #| msgid "Slide Left"
 msgid "Stick left"
 msgstr "Deslizar ?? esquerda"
 
-#: engines/scumm/metaengine.cpp:1146
+#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1208
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick right"
 msgstr "Deslizar ?? direita"
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1194
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1214
 #, fuzzy
 #| msgid "Move left"
 msgid "Fire / select"
@@ -13846,13 +13846,13 @@ msgstr "Sistema"
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1199
+#: engines/scumm/metaengine.cpp:1219
 #, fuzzy
 #| msgid "Move Back"
 msgid "Cover / back"
 msgstr "Retroceder"
 
-#: engines/scumm/metaengine.cpp:1204
+#: engines/scumm/metaengine.cpp:1224
 #, fuzzy
 msgid "Skip / menu"
 msgstr "~M~enu principal"
diff --git a/po/ro.po b/po/ro.po
index f2a964edc3c..f1f1b3187ee 100644
--- a/po/ro.po
+++ b/po/ro.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.10.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-05-31 14:10+0000\n"
+"POT-Creation-Date: 2026-06-01 14:35+0000\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: Automatically generated\n"
 "Language-Team: none\n"
@@ -12424,35 +12424,35 @@ msgstr ""
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1113 engines/scumm/metaengine.cpp:1176
+#: engines/scumm/metaengine.cpp:1113 engines/scumm/metaengine.cpp:1175
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1118 engines/scumm/metaengine.cpp:1182
+#: engines/scumm/metaengine.cpp:1118 engines/scumm/metaengine.cpp:1180
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1123 engines/scumm/metaengine.cpp:1188
+#: engines/scumm/metaengine.cpp:1123 engines/scumm/metaengine.cpp:1185
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1128
+#: engines/scumm/metaengine.cpp:1128 engines/scumm/metaengine.cpp:1190
 msgid "Stick up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1134
+#: engines/scumm/metaengine.cpp:1134 engines/scumm/metaengine.cpp:1196
 msgid "Stick down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1140
+#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
 msgid "Stick left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1146
+#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1208
 msgid "Stick right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1194
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1214
 msgid "Fire / select"
 msgstr ""
 
@@ -12464,11 +12464,11 @@ msgstr ""
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1199
+#: engines/scumm/metaengine.cpp:1219
 msgid "Cover / back"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1204
+#: engines/scumm/metaengine.cpp:1224
 msgid "Skip / menu"
 msgstr ""
 
diff --git a/po/ru_RU.po b/po/ru_RU.po
index 522de3af323..04732a46262 100644
--- a/po/ru_RU.po
+++ b/po/ru_RU.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.8.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-05-31 14:10+0000\n"
+"POT-Creation-Date: 2026-06-01 14:35+0000\n"
 "PO-Revision-Date: 2026-05-06 08:54+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Russian <https://translations.scummvm.org/projects/scummvm/"
@@ -13588,43 +13588,43 @@ msgstr ""
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1113 engines/scumm/metaengine.cpp:1176
+#: engines/scumm/metaengine.cpp:1113 engines/scumm/metaengine.cpp:1175
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1118 engines/scumm/metaengine.cpp:1182
+#: engines/scumm/metaengine.cpp:1118 engines/scumm/metaengine.cpp:1180
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1123 engines/scumm/metaengine.cpp:1188
+#: engines/scumm/metaengine.cpp:1123 engines/scumm/metaengine.cpp:1185
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1128
+#: engines/scumm/metaengine.cpp:1128 engines/scumm/metaengine.cpp:1190
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "??????????????"
 
-#: engines/scumm/metaengine.cpp:1134
+#: engines/scumm/metaengine.cpp:1134 engines/scumm/metaengine.cpp:1196
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "???????????????????? ????????"
 
-#: engines/scumm/metaengine.cpp:1140
+#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
 #, fuzzy
 #| msgid "Slide left"
 msgid "Stick left"
 msgstr "?????????????????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1146
+#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1208
 #, fuzzy
 #| msgid "Slide right"
 msgid "Stick right"
 msgstr "?????????????????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1194
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1214
 #, fuzzy
 #| msgid "Move / Select"
 msgid "Fire / select"
@@ -13640,13 +13640,13 @@ msgstr "Backspace"
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1199
+#: engines/scumm/metaengine.cpp:1219
 #, fuzzy
 #| msgid "Move back"
 msgid "Cover / back"
 msgstr "???????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1204
+#: engines/scumm/metaengine.cpp:1224
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu"
diff --git a/po/sv_SE.po b/po/sv_SE.po
index 5d9c2c2a1b5..48b00d26343 100644
--- a/po/sv_SE.po
+++ b/po/sv_SE.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.5.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-05-31 14:10+0000\n"
+"POT-Creation-Date: 2026-06-01 14:35+0000\n"
 "PO-Revision-Date: 2026-04-19 13:22+0000\n"
 "Last-Translator: Daniel Nylander <po at danielnylander.se>\n"
 "Language-Team: Swedish <https://translations.scummvm.org/projects/scummvm/"
@@ -13637,43 +13637,43 @@ msgstr ""
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1113 engines/scumm/metaengine.cpp:1176
+#: engines/scumm/metaengine.cpp:1113 engines/scumm/metaengine.cpp:1175
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1118 engines/scumm/metaengine.cpp:1182
+#: engines/scumm/metaengine.cpp:1118 engines/scumm/metaengine.cpp:1180
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1123 engines/scumm/metaengine.cpp:1188
+#: engines/scumm/metaengine.cpp:1123 engines/scumm/metaengine.cpp:1185
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1128
+#: engines/scumm/metaengine.cpp:1128 engines/scumm/metaengine.cpp:1190
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "Ta"
 
-#: engines/scumm/metaengine.cpp:1134
+#: engines/scumm/metaengine.cpp:1134 engines/scumm/metaengine.cpp:1196
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "Bl??ddra ned??t"
 
-#: engines/scumm/metaengine.cpp:1140
+#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
 #, fuzzy
 #| msgid "Slide left"
 msgid "Stick left"
 msgstr "Glid v??nster"
 
-#: engines/scumm/metaengine.cpp:1146
+#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1208
 #, fuzzy
 #| msgid "Slide right"
 msgid "Stick right"
 msgstr "Glid h??ger"
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1194
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1214
 #, fuzzy
 #| msgid "Move / Select"
 msgid "Fire / select"
@@ -13689,13 +13689,13 @@ msgstr "Backsteg"
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1199
+#: engines/scumm/metaengine.cpp:1219
 #, fuzzy
 #| msgid "Move back"
 msgid "Cover / back"
 msgstr "Stega bak??t"
 
-#: engines/scumm/metaengine.cpp:1204
+#: engines/scumm/metaengine.cpp:1224
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu"
diff --git a/po/tr.po b/po/tr.po
index 146a2a68a8e..fb298fe5471 100644
--- a/po/tr.po
+++ b/po/tr.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-05-31 14:10+0000\n"
+"POT-Creation-Date: 2026-06-01 14:35+0000\n"
 "PO-Revision-Date: 2024-10-10 12:33+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Turkish <https://translations.scummvm.org/projects/scummvm/"
@@ -13298,38 +13298,38 @@ msgstr ""
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1113 engines/scumm/metaengine.cpp:1176
+#: engines/scumm/metaengine.cpp:1113 engines/scumm/metaengine.cpp:1175
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1118 engines/scumm/metaengine.cpp:1182
+#: engines/scumm/metaengine.cpp:1118 engines/scumm/metaengine.cpp:1180
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1123 engines/scumm/metaengine.cpp:1188
+#: engines/scumm/metaengine.cpp:1123 engines/scumm/metaengine.cpp:1185
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1128
+#: engines/scumm/metaengine.cpp:1128 engines/scumm/metaengine.cpp:1190
 msgid "Stick up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1134
+#: engines/scumm/metaengine.cpp:1134 engines/scumm/metaengine.cpp:1196
 #, fuzzy
 msgid "Stick down"
 msgstr "A??a????"
 
-#: engines/scumm/metaengine.cpp:1140
+#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
 #, fuzzy
 msgid "Stick left"
 msgstr "Sa??"
 
-#: engines/scumm/metaengine.cpp:1146
+#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1208
 #, fuzzy
 msgid "Stick right"
 msgstr "Sa??"
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1194
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1214
 #, fuzzy
 msgid "Fire / select"
 msgstr "Sol"
@@ -13344,13 +13344,13 @@ msgstr "Altyap??"
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1199
+#: engines/scumm/metaengine.cpp:1219
 #, fuzzy
 #| msgid "Backend"
 msgid "Cover / back"
 msgstr "Altyap??"
 
-#: engines/scumm/metaengine.cpp:1204
+#: engines/scumm/metaengine.cpp:1224
 #, fuzzy
 msgid "Skip / menu"
 msgstr "??ptal"
diff --git a/po/uk_UA.po b/po/uk_UA.po
index 71f0d36ec09..f5a32f3b53b 100644
--- a/po/uk_UA.po
+++ b/po/uk_UA.po
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.9.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-05-31 14:10+0000\n"
+"POT-Creation-Date: 2026-06-01 14:35+0000\n"
 "PO-Revision-Date: 2026-05-06 08:54+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Ukrainian <https://translations.scummvm.org/projects/scummvm/"
@@ -13547,43 +13547,43 @@ msgstr ""
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1113 engines/scumm/metaengine.cpp:1176
+#: engines/scumm/metaengine.cpp:1113 engines/scumm/metaengine.cpp:1175
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1118 engines/scumm/metaengine.cpp:1182
+#: engines/scumm/metaengine.cpp:1118 engines/scumm/metaengine.cpp:1180
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1123 engines/scumm/metaengine.cpp:1188
+#: engines/scumm/metaengine.cpp:1123 engines/scumm/metaengine.cpp:1185
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1128
+#: engines/scumm/metaengine.cpp:1128 engines/scumm/metaengine.cpp:1190
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "??????????????????"
 
-#: engines/scumm/metaengine.cpp:1134
+#: engines/scumm/metaengine.cpp:1134 engines/scumm/metaengine.cpp:1196
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "?????????????????????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1140
+#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
 #, fuzzy
 #| msgid "Slide left"
 msgid "Stick left"
 msgstr "???????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1146
+#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1208
 #, fuzzy
 #| msgid "Slide right"
 msgid "Stick right"
 msgstr "???????? ??????????????"
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1194
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1214
 #, fuzzy
 #| msgid "Move / Select"
 msgid "Fire / select"
@@ -13599,13 +13599,13 @@ msgstr "Backspace"
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1199
+#: engines/scumm/metaengine.cpp:1219
 #, fuzzy
 #| msgid "Move back"
 msgid "Cover / back"
 msgstr "???????????????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1204
+#: engines/scumm/metaengine.cpp:1224
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu"
diff --git a/po/zh.po b/po/zh.po
index efacfa61666..e83a026f05d 100644
--- a/po/zh.po
+++ b/po/zh.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-05-31 14:10+0000\n"
+"POT-Creation-Date: 2026-06-01 14:35+0000\n"
 "PO-Revision-Date: 2024-05-14 12:42+0000\n"
 "Last-Translator: ?????? <clonewith at qq.com>\n"
 "Language-Team: Chinese <https://translations.scummvm.org/projects/scummvm/"
@@ -13122,38 +13122,38 @@ msgstr ""
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1113 engines/scumm/metaengine.cpp:1176
+#: engines/scumm/metaengine.cpp:1113 engines/scumm/metaengine.cpp:1175
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1118 engines/scumm/metaengine.cpp:1182
+#: engines/scumm/metaengine.cpp:1118 engines/scumm/metaengine.cpp:1180
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1123 engines/scumm/metaengine.cpp:1188
+#: engines/scumm/metaengine.cpp:1123 engines/scumm/metaengine.cpp:1185
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1128
+#: engines/scumm/metaengine.cpp:1128 engines/scumm/metaengine.cpp:1190
 msgid "Stick up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1134
+#: engines/scumm/metaengine.cpp:1134 engines/scumm/metaengine.cpp:1196
 #, fuzzy
 msgid "Stick down"
 msgstr "??????"
 
-#: engines/scumm/metaengine.cpp:1140
+#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
 #, fuzzy
 msgid "Stick left"
 msgstr "??????"
 
-#: engines/scumm/metaengine.cpp:1146
+#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1208
 #, fuzzy
 msgid "Stick right"
 msgstr "??????"
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1194
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1214
 #, fuzzy
 msgid "Fire / select"
 msgstr "??????"
@@ -13168,13 +13168,13 @@ msgstr "??????"
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1199
+#: engines/scumm/metaengine.cpp:1219
 #, fuzzy
 #| msgid "Backend"
 msgid "Cover / back"
 msgstr "??????"
 
-#: engines/scumm/metaengine.cpp:1204
+#: engines/scumm/metaengine.cpp:1224
 #, fuzzy
 #| msgid "Skip autosave"
 msgid "Skip / menu"
diff --git a/po/zh_Hans.po b/po/zh_Hans.po
index 5dd36141a18..6ae91f8ee85 100644
--- a/po/zh_Hans.po
+++ b/po/zh_Hans.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.9.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-05-31 14:10+0000\n"
+"POT-Creation-Date: 2026-06-01 14:35+0000\n"
 "PO-Revision-Date: 2024-12-01 22:18+0000\n"
 "Last-Translator: ?????? <clonewith at qq.com>\n"
 "Language-Team: Chinese (Simplified Han script) <https://"
@@ -14097,43 +14097,43 @@ msgstr ""
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1113 engines/scumm/metaengine.cpp:1176
+#: engines/scumm/metaengine.cpp:1113 engines/scumm/metaengine.cpp:1175
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1118 engines/scumm/metaengine.cpp:1182
+#: engines/scumm/metaengine.cpp:1118 engines/scumm/metaengine.cpp:1180
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1123 engines/scumm/metaengine.cpp:1188
+#: engines/scumm/metaengine.cpp:1123 engines/scumm/metaengine.cpp:1185
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1128
+#: engines/scumm/metaengine.cpp:1128 engines/scumm/metaengine.cpp:1190
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "??????"
 
-#: engines/scumm/metaengine.cpp:1134
+#: engines/scumm/metaengine.cpp:1134 engines/scumm/metaengine.cpp:1196
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "????????????"
 
-#: engines/scumm/metaengine.cpp:1140
+#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
 #, fuzzy
 #| msgid "Slide Left"
 msgid "Stick left"
 msgstr "????????????"
 
-#: engines/scumm/metaengine.cpp:1146
+#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1208
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick right"
 msgstr "????????????"
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1194
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1214
 #, fuzzy
 #| msgid "Move left"
 msgid "Fire / select"
@@ -14149,13 +14149,13 @@ msgstr "??????"
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1199
+#: engines/scumm/metaengine.cpp:1219
 #, fuzzy
 #| msgid "Move Back"
 msgid "Cover / back"
 msgstr "????????????"
 
-#: engines/scumm/metaengine.cpp:1204
+#: engines/scumm/metaengine.cpp:1224
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu"
diff --git a/po/zh_Hant.po b/po/zh_Hant.po
index 1439b02c9ea..d979bd21073 100644
--- a/po/zh_Hant.po
+++ b/po/zh_Hant.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.9.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-05-31 14:10+0000\n"
+"POT-Creation-Date: 2026-06-01 14:35+0000\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: Automatically generated\n"
 "Language-Team: none\n"
@@ -12425,35 +12425,35 @@ msgstr ""
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1113 engines/scumm/metaengine.cpp:1176
+#: engines/scumm/metaengine.cpp:1113 engines/scumm/metaengine.cpp:1175
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1118 engines/scumm/metaengine.cpp:1182
+#: engines/scumm/metaengine.cpp:1118 engines/scumm/metaengine.cpp:1180
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1123 engines/scumm/metaengine.cpp:1188
+#: engines/scumm/metaengine.cpp:1123 engines/scumm/metaengine.cpp:1185
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1128
+#: engines/scumm/metaengine.cpp:1128 engines/scumm/metaengine.cpp:1190
 msgid "Stick up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1134
+#: engines/scumm/metaengine.cpp:1134 engines/scumm/metaengine.cpp:1196
 msgid "Stick down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1140
+#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
 msgid "Stick left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1146
+#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1208
 msgid "Stick right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1194
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1214
 msgid "Fire / select"
 msgstr ""
 
@@ -12465,11 +12465,11 @@ msgstr ""
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1199
+#: engines/scumm/metaengine.cpp:1219
 msgid "Cover / back"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1204
+#: engines/scumm/metaengine.cpp:1224
 msgid "Skip / menu"
 msgstr ""
 



From noreply at scummvm.org  Mon Jun  1 15:58:11 2026
From: noreply at scummvm.org (sev-)
Date: Mon,  1 Jun 2026 15:58:11 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 2f5bbc4c20dc1a2e6f7bbd0291dee480dd2ec760
Message-ID: <20260601155811.97B35700591@ron.scummvm.net>

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

Summary:
8f552fa4ef DM: Fix floor sensors failing to open closed door in Hall of Champions
72b3ad705a DM: Fix crash on save dialog selection highlight
d18af2b285 DM: Implement EventManager::highlightScreenBox()
7bce6e9e88 DM: Fix in-game save loading failing on first attempt
92a8dc48ed DM: Fix stairs transition crash in Hall of Champions
1f57def68f DM: Fix null pointer dereference when rendering creature groups
182c24df56 DM: Correct bitfield masks and sensor data setter
6aeb6b5b32 DM: Fix debugger crash on wrong spelling in 'gimme' command
7238392ca2 DM: Fix crash by correcting the order of values in CLIP
dc8e5ad33b DM: Correct evaluation order for footprint scent checks. PVS-Studio V501
3f611279e0 DM: Fix out-of-bounds read. PVS-Studio V781
22841aa24c DM: Correct sign mismatch using explicit cast. PVS-Studio V1112
e59edaa25d DM: Fix out-of-bounds pointer arithmetic. PVS-Studio V557
2f5bbc4c20 DM: Fix out-of-bounds map data access. PVS-Studio V781


Commit: 8f552fa4efbfa76203ccc229c66a2f7d4b36addc
    https://github.com/scummvm/scummvm/commit/8f552fa4efbfa76203ccc229c66a2f7d4b36addc
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-06-01T17:57:59+02:00

Commit Message:
DM: Fix floor sensors failing to open closed door in Hall of Champions

Previously, checking both open and closed door states under the kDMSensorEffectSet caused incorrect early return.

Changed paths:
    engines/dm/timeline.cpp


diff --git a/engines/dm/timeline.cpp b/engines/dm/timeline.cpp
index 42cc84dea34..324de237264 100644
--- a/engines/dm/timeline.cpp
+++ b/engines/dm/timeline.cpp
@@ -482,11 +482,16 @@ void Timeline::processEventSquareDoor(TimelineEvent *event) {
 	if (doorState == kDMDoorStateDestroyed)
 		return;
 
-	if (event->_Cu.A._effect == kDMSensorEffectToggle)
+	if (event->_Cu.A._effect == kDMSensorEffectToggle) {
 		event->_Cu.A._effect = (doorState == kDMDoorStateOpen) ? kDMSensorEffectClear : kDMSensorEffectSet;
-	else if (event->_Cu.A._effect == kDMSensorEffectSet) {
-		if ((doorState == kDMDoorStateOpen) || (doorState == kDMDoorStateClosed))
-			return;
+	} else {
+		if (event->_Cu.A._effect == kDMSensorEffectSet) {
+			if (doorState == kDMDoorStateOpen)
+           		return;
+		} else if (event->_Cu.A._effect == kDMSensorEffectClear) {
+			if (doorState == kDMDoorStateClosed)
+            	return;
+		}
 	}
 	event->_type = kDMEventTypeDoorAnimation;
 	addEventGetEventIndex(event);


Commit: 72b3ad705a5e274255357b8ca00607ad84f83ce6
    https://github.com/scummvm/scummvm/commit/72b3ad705a5e274255357b8ca00607ad84f83ce6
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-06-01T17:57:59+02:00

Commit Message:
DM: Fix crash on save dialog selection highlight

Correct the order of parameter in the Box constructor

Changed paths:
    engines/dm/dialog.cpp


diff --git a/engines/dm/dialog.cpp b/engines/dm/dialog.cpp
index d6478fe681f..839eaed1c02 100644
--- a/engines/dm/dialog.cpp
+++ b/engines/dm/dialog.cpp
@@ -206,7 +206,7 @@ int16 DialogMan::getChoice(uint16 choiceCount, uint16 dialogSetIndex, int16 driv
 	boxA._rect.bottom += 4;
 	evtMan.showMouse();
 	displMan._drawFloorAndCeilingRequested = true;
-	Box boxB(0, 0, boxA._rect.right - boxA._rect.left + 3, boxA._rect.bottom - boxA._rect.top + 3);
+	Box boxB(0, boxA._rect.right - boxA._rect.left + 3, 0, boxA._rect.bottom - boxA._rect.top + 3);
 	displMan.blitToBitmap(displMan._bitmapScreen, displMan._bitmapViewport,
 										boxB, boxA._rect.left, boxA._rect.top, k160_byteWidthScreen, k160_byteWidthScreen, kDMColorNoTransparency, 200, 25);
 	_vm->delay(1);


Commit: d18af2b285ea1e2cdb12452ac957d154f223ed63
    https://github.com/scummvm/scummvm/commit/d18af2b285ea1e2cdb12452ac957d154f223ed63
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-06-01T17:57:59+02:00

Commit Message:
DM: Implement EventManager::highlightScreenBox()

This enablers move indicator highlight in game UI.

Changed paths:
    engines/dm/eventman.cpp
    engines/dm/eventman.h


diff --git a/engines/dm/eventman.cpp b/engines/dm/eventman.cpp
index 03e418f6776..9a0377cc39b 100644
--- a/engines/dm/eventman.cpp
+++ b/engines/dm/eventman.cpp
@@ -1678,4 +1678,20 @@ void EventManager::highlightBoxDisable() {
 	}
 }
 
+void EventManager::highlightScreenBox(int16 x1, int16 x2, int16 y1, int16 y2) {
+	x1 = CLIP<int16>(x1, 0, _vm->_displayMan->_screenWidth - 1);
+	x2 = CLIP<int16>(x2, 0, _vm->_displayMan->_screenWidth - 1);
+	y1 = CLIP<int16>(y1, 0, _vm->_displayMan->_screenHeight - 1);
+	y2 = CLIP<int16>(y2, 0, _vm->_displayMan->_screenHeight - 1);
+
+	byte *screen = _vm->_displayMan->_bitmapScreen;
+	uint16 pitch = _vm->_displayMan->_screenWidth;
+
+	for (int16 y = y1; y <= y2; ++y) {
+		for (int16 x = x1; x <= x2; ++x) {
+			screen[y * pitch + x] ^= 0x0F;
+		}
+	}
+}
+
 } // end of namespace DM
diff --git a/engines/dm/eventman.h b/engines/dm/eventman.h
index 2f81ce82fee..4fde15d62a6 100644
--- a/engines/dm/eventman.h
+++ b/engines/dm/eventman.h
@@ -286,7 +286,7 @@ public:
 	void waitForMouseOrKeyActivity(); // @ F0541_INPUT_WaitForMouseOrKeyboardActivity
 	void commandHighlightBoxEnable(int16 x1, int16 x2, int16 y1, int16 y2); // @ F0362_COMMAND_HighlightBoxEnable
 	void highlightBoxDisable(); // @ F0363_COMMAND_HighlightBoxDisable
-	void highlightScreenBox(int16 x1, int16 x2, int16 y1, int16 y2) { warning("STUB METHOD: highlightScreenBox"); } // @  F0006_MAIN_HighlightScreenBox
+	void highlightScreenBox(int16 x1, int16 x2, int16 y1, int16 y2); // @  F0006_MAIN_HighlightScreenBox
 
 	KeyboardInput _primaryKeyboardInputInterface[7]; // @ G0458_as_Graphic561_PrimaryKeyboardInput_Interface
 	KeyboardInput _secondaryKeyboardInputMovement[19]; // @ G0459_as_Graphic561_SecondaryKeyboardInput_Movement


Commit: 7bce6e9e883e12f40e6c7d0bbf8beaab1249f49a
    https://github.com/scummvm/scummvm/commit/7bce6e9e883e12f40e6c7d0bbf8beaab1249f49a
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-06-01T17:57:59+02:00

Commit Message:
DM: Fix in-game save loading failing on first attempt

Set _gameMode to kDMModeLoadSavedGame when a valid slot is loaded.

Changed paths:
    engines/dm/loadsave.cpp


diff --git a/engines/dm/loadsave.cpp b/engines/dm/loadsave.cpp
index bd177faf5af..3b567325058 100644
--- a/engines/dm/loadsave.cpp
+++ b/engines/dm/loadsave.cpp
@@ -42,6 +42,9 @@
 namespace DM {
 
 LoadgameResult DMEngine::loadgame(int16 slot) {
+	if (slot >= 0)
+		_gameMode = kDMModeLoadSavedGame;
+
 	if (slot == -1 && _gameMode == kDMModeLoadSavedGame)
 		return kDMLoadgameFailure;
 


Commit: 92a8dc48ed958a77ef46563af53989e82c86db6d
    https://github.com/scummvm/scummvm/commit/92a8dc48ed958a77ef46563af53989e82c86db6d
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-06-01T17:57:59+02:00

Commit Message:
DM: Fix stairs transition crash in Hall of Champions

Make CreatureType strictly a 16-bit enum to prevent C++ struct padding and over-read.

Changed paths:
    engines/dm/group.h


diff --git a/engines/dm/group.h b/engines/dm/group.h
index 7fd95bdb43a..cc96c8d8c70 100644
--- a/engines/dm/group.h
+++ b/engines/dm/group.h
@@ -37,7 +37,7 @@ namespace DM {
 	class CreatureInfo;
 
 /* Creature types */
-enum CreatureType {
+enum CreatureType : uint16 {
 	kDMCreatureTypeGiantScorpion = 0, // @ C00_CREATURE_GIANT_SCORPION_SCORPION
 	kDMCreatureTypeSwampSlime = 1, // @ C01_CREATURE_SWAMP_SLIME_SLIME_DEVIL
 	kDMCreatureTypeGiggler = 2, // @ C02_CREATURE_GIGGLER


Commit: 1f57def68f9d85ce0cd5a50cadae5c6602065b07
    https://github.com/scummvm/scummvm/commit/1f57def68f9d85ce0cd5a50cadae5c6602065b07
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-06-01T17:57:59+02:00

Commit Message:
DM: Fix null pointer dereference when rendering creature groups

Changed paths:
    engines/dm/gfx.cpp


diff --git a/engines/dm/gfx.cpp b/engines/dm/gfx.cpp
index 8f4894cd016..cd0a4428ad7 100644
--- a/engines/dm/gfx.cpp
+++ b/engines/dm/gfx.cpp
@@ -3368,12 +3368,12 @@ T0115015_DrawProjectileAsObject:
 
 		if (group == nullptr) { /* If all creature data and info has not already been gathered */
 			group = (Group *)dungeon.getThingData(groupThing);
-			activeGroup = &_vm->_groupMan->_activeGroups[group->getActiveGroupIndex()];
 			CreatureInfo *creatureInfo = &dungeon._creatureInfos[group->_type];
 			creatureAspectStruct = &_creatureAspects219[creatureInfo->_creatureAspectIndex];
 			creatureSize = getFlag(creatureInfo->_attributes, kDMCreatureMaskSize);
 			creatureGraphicInfoGreen = creatureInfo->_graphicInfo;
 		}
+		activeGroup = &_vm->_groupMan->_activeGroups[group->getActiveGroupIndex()];
 		objectAspect = (ObjectAspect *)creatureAspectStruct;
 		AL_0_creatureIndexRed = _vm->_groupMan->getCreatureOrdinalInCell(group, cellYellowBear);
 


Commit: 182c24df56619e8ee151e0e181db770d6c115991
    https://github.com/scummvm/scummvm/commit/182c24df56619e8ee151e0e181db770d6c115991
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-06-01T17:57:59+02:00

Commit Message:
DM: Correct bitfield masks and sensor data setter

Fix Sensor::setData & some getters to use correct bit masks matching the original.

Changed paths:
    engines/dm/dungeonman.h


diff --git a/engines/dm/dungeonman.h b/engines/dm/dungeonman.h
index 66ca67c61a3..59df5773c2a 100644
--- a/engines/dm/dungeonman.h
+++ b/engines/dm/dungeonman.h
@@ -354,11 +354,11 @@ public:
 	explicit Teleporter(uint16 *rawDat) : _nextThing(rawDat[0]), _attributes(rawDat[1]), _destMapIndex(rawDat[2]) {}
 	Thing getNextThing() { return _nextThing; }
 	bool isAudible() { return (_attributes >> 15) & 1; }
-	TeleporterScope getScope() { return (TeleporterScope)((_attributes >> 13) & 1); }
+	TeleporterScope getScope() { return (TeleporterScope)((_attributes >> 13) & 3); }
 	bool getAbsoluteRotation() { return (_attributes >> 12) & 1; }
-	Direction getRotation() { return (Direction)((_attributes >> 10) & 1); }
-	byte getTargetMapY() { return (_attributes >> 5) & 0xF; }
-	byte getTargetMapX() { return _attributes & 0xF; }
+	Direction getRotation() { return (Direction)((_attributes >> 10) & 3); }
+	byte getTargetMapY() { return (_attributes >> 5) & 0x1F; }
+	byte getTargetMapX() { return _attributes & 0x1F; }
 	uint16 getTargetMapIndex() { return _destMapIndex >> 8; }
 }; // @ TELEPORTER
 
@@ -388,7 +388,7 @@ public:
 	uint16 getData() { return (_datAndType >> 7) & 0x1FF; } // @ M40_DATA
 	static uint16 getDataMask1(uint16 data) { return (data >> 7) & 0xF; } // @ M42_MASK1
 	static uint16 getDataMask2(uint16 data) { return (data >> 11) & 0xF; } // @ M43_MASK2
-	void setData(uint16 dat) { _datAndType = dat; } // @ M41_SET_DATA
+	void setData(uint16 dat) { _datAndType = (_datAndType & 0x7F) | (dat << 7); } // @ M41_SET_DATA
 	void setTypeDisabled() { _datAndType &= 0xFF80; } // @ M44_SET_TYPE_DISABLED
 
 	bool getAttrOnlyOnce() { return (_attributes >> 2) & 1; }


Commit: 6aeb6b5b3272c3f8e8e03269f616140098391832
    https://github.com/scummvm/scummvm/commit/6aeb6b5b3272c3f8e8e03269f616140098391832
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-06-01T17:57:59+02:00

Commit Message:
DM: Fix debugger crash on wrong spelling in 'gimme' command

Changed paths:
    engines/dm/console.cpp


diff --git a/engines/dm/console.cpp b/engines/dm/console.cpp
index 8d7dcfa8dfc..7372da0bb18 100644
--- a/engines/dm/console.cpp
+++ b/engines/dm/console.cpp
@@ -264,7 +264,7 @@ bool Console::Cmd_gimme(int argc, const char** argv) {
 		for (int16 thingIndex = 0; thingIndex < thingCount; ++thingIndex) {
 			dummyThing.setIndex(thingIndex);
 			int16 iconIndex = _vm->_objectMan->getIconIndex(dummyThing);
-			if (iconIndex != -1) {
+			if (iconIndex >= 0 && iconIndex < kDMObjectNameCount) {
 				const char *displayName = _vm->_objectMan->_objectNames[iconIndex];
 				if (cstrEquals(displayName, requestedItemName.c_str())) {
 					uint16 *newThingData = new uint16[(thingCount + 1) * thingTypeSize];


Commit: 7238392ca2926771a81aa428e3fb51fc42d2631e
    https://github.com/scummvm/scummvm/commit/7238392ca2926771a81aa428e3fb51fc42d2631e
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-06-01T17:57:59+02:00

Commit Message:
DM: Fix crash by correcting the order of values in CLIP

Changed paths:
    engines/dm/champion.cpp
    engines/dm/gfx.cpp
    engines/dm/menus.cpp


diff --git a/engines/dm/champion.cpp b/engines/dm/champion.cpp
index 1d2c8b518e7..21fb97016f2 100644
--- a/engines/dm/champion.cpp
+++ b/engines/dm/champion.cpp
@@ -661,7 +661,7 @@ uint16 ChampionMan::getStrength(int16 champIndex, int16 slotIndex) {
 	if (getFlag(curChampion->_wounds, (slotIndex == kDMSlotReadyHand) ? kDMWoundReadHand : kDMWoundActionHand))
 		strength >>= 1;
 
-	return CLIP(0, strength >> 1, 100);
+	return CLIP(strength >> 1, 0, 100);
 }
 
 Thing ChampionMan::getObjectRemovedFromSlot(uint16 champIndex, uint16 slotIndex) {
@@ -883,7 +883,7 @@ int16 ChampionMan::getWoundDefense(int16 champIndex, uint16 woundIndex) {
 	if (_partyIsSleeping)
 		woundDefense >>= 1;
 
-	return CLIP(0, woundDefense >> 1, 100);
+	return CLIP(woundDefense >> 1, 0, 100);
 }
 
 uint16 ChampionMan::getStatisticAdjustedAttack(Champion *champ, uint16 statIndex, uint16 attack) {
@@ -920,7 +920,7 @@ void ChampionMan::wakeUp() {
 
 int16 ChampionMan::getThrowingStaminaCost(Thing thing) {
 	int16 weight = _vm->_dungeonMan->getObjectWeight(thing) >> 1;
-	int16 staminaCost = CLIP<int16>(1, weight, 10);
+	int16 staminaCost = CLIP<int16>(weight, 1, 10);
 
 	while ((weight -= 10) > 0)
 		staminaCost += weight >> 1;
@@ -979,7 +979,7 @@ void ChampionMan::addSkillExperience(uint16 champIndex, uint16 skillIndex, uint1
 		Skill *curSkill = &curChampion->_skills[skillIndex];
 		curSkill->_experience += exp;
 		if (curSkill->_temporaryExperience < 32000)
-			curSkill->_temporaryExperience += CLIP(1, exp >> 3, 100);
+			curSkill->_temporaryExperience += CLIP(exp >> 3, 1, 100);
 
 		curSkill = &curChampion->_skills[baseSkillIndex];
 		if (skillIndex >= kDMSkillSwing)
@@ -1121,7 +1121,7 @@ bool ChampionMan::isLucky(Champion *champ, uint16 percentage) {
 
 	unsigned char *curStat = champ->_statistics[kDMStatLuck];
 	bool retVal = (_vm->getRandomNumber(curStat[kDMStatCurrent]) > percentage);
-	curStat[kDMStatCurrent] = CLIP<unsigned char>(curStat[kDMStatMinimum], curStat[kDMStatCurrent] + (retVal ? -2 : 2), curStat[kDMStatMaximum]);
+	curStat[kDMStatCurrent] = CLIP<unsigned char>(curStat[kDMStatCurrent] + (retVal ? -2 : 2), curStat[kDMStatMinimum], curStat[kDMStatMaximum]);
 	return retVal;
 }
 
@@ -1660,7 +1660,7 @@ void ChampionMan::applyTimeEffects() {
 				staminaGainCycleCount += 2;
 
 			int16 staminaLoss = 0;
-			int16 staminaAmount = CLIP(1, (championPtr->_maxStamina >> 8) - 1, 6);
+			int16 staminaAmount = CLIP((championPtr->_maxStamina >> 8) - 1, 1, 6);
 			if (_partyIsSleeping)
 				staminaAmount <<= 1;
 
diff --git a/engines/dm/gfx.cpp b/engines/dm/gfx.cpp
index cd0a4428ad7..41b6f422c55 100644
--- a/engines/dm/gfx.cpp
+++ b/engines/dm/gfx.cpp
@@ -3580,12 +3580,12 @@ T0115077_DrawSecondHalfSquareCreature:
 		else if (viewLane != kDMViewLaneCenter) /* Lane right */
 			AL_4_xPos += 100;
 
-		boxByteGreen._rect.right = CLIP(0, AL_4_xPos + byteWidth, 223);
+		boxByteGreen._rect.right = CLIP(AL_4_xPos + byteWidth, 0, 223);
 
 		if (!boxByteGreen._rect.right)
 			goto T0115126_CreatureNotVisible;
 		int16 AL_0_creaturePosX;
-		boxByteGreen._rect.left = CLIP(0, AL_4_xPos - byteWidth + 1, 223);
+		boxByteGreen._rect.left = CLIP(AL_4_xPos - byteWidth + 1, 0, 223);
 		if (boxByteGreen._rect.left) {
 			if (boxByteGreen._rect.left == 223)
 				goto T0115126_CreatureNotVisible;
@@ -3845,7 +3845,7 @@ T0115200_DrawExplosion:
 					continue;
 				boxByteGreen._rect.right = AL_4_xPos;
 				AL_4_xPos = explosionCoordinates[0];
-				boxByteGreen._rect.left = CLIP(0, AL_4_xPos - byteWidth + 1, 223);
+				boxByteGreen._rect.left = CLIP(AL_4_xPos - byteWidth + 1, 0, 223);
 
 				if (boxByteGreen._rect.left)
 					AL_4_xPos = paddingPixelCount;
diff --git a/engines/dm/menus.cpp b/engines/dm/menus.cpp
index 8747273341b..7fca0f08134 100644
--- a/engines/dm/menus.cpp
+++ b/engines/dm/menus.cpp
@@ -589,7 +589,7 @@ int16 MenuMan::getChampionSpellCastResult(uint16 champIndex) {
 		if (curSpell->getType() == kDMSpellTypeProjectileOpenDoor)
 			skillLevel <<= 1;
 
-		championMan.isProjectileSpellCast(champIndex, Thing(curSpell->getType() + _vm->_thingFirstExplosion.toUint16()), CLIP(21, (powerSymbolOrdinal + 2) * (4 + (skillLevel << 1)), 255), 0);
+		championMan.isProjectileSpellCast(champIndex, Thing(curSpell->getType() + _vm->_thingFirstExplosion.toUint16()), CLIP((powerSymbolOrdinal + 2) * (4 + (skillLevel << 1)), 21, 255), 0);
 		break;
 	case kDMSpellKindOther: {
 		TimelineEvent newEvent;


Commit: dc8e5ad33b3b1154fb73807c62f302281d826180
    https://github.com/scummvm/scummvm/commit/dc8e5ad33b3b1154fb73807c62f302281d826180
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-06-01T17:57:59+02:00

Commit Message:
DM: Correct evaluation order for footprint scent checks. PVS-Studio V501

Check footprints-allowed before assigning scent ordinal to avoid overwriting their shared memory too early, matching the original.

Changed paths:
    engines/dm/dungeonman.cpp


diff --git a/engines/dm/dungeonman.cpp b/engines/dm/dungeonman.cpp
index b585ca68733..a989070b636 100644
--- a/engines/dm/dungeonman.cpp
+++ b/engines/dm/dungeonman.cpp
@@ -962,8 +962,7 @@ T0172010_ClosedFakeWall:
 			curThing = getNextThing(curThing);
 		}
 
-		AL0307_uc_ScentOrdinal = championMan.getScentOrdinal(mapX, mapY);
-		if (AL0307_uc_FootprintsAllowed && (AL0307_uc_ScentOrdinal) && (--AL0307_uc_ScentOrdinal >= championMan._party._firstScentIndex) && (AL0307_uc_ScentOrdinal < championMan._party._lastScentIndex))
+		if (AL0307_uc_FootprintsAllowed && (AL0307_uc_ScentOrdinal = championMan.getScentOrdinal(mapX, mapY)) && (--AL0307_uc_ScentOrdinal >= championMan._party._firstScentIndex) && (AL0307_uc_ScentOrdinal < championMan._party._lastScentIndex))
 			setFlag(aspectArray[kDMSquareAspectFloorOrn], kDMMaskFootprints);
 
 		break;
@@ -987,8 +986,7 @@ T0172010_ClosedFakeWall:
 		while ((curThing != _vm->_thingEndOfList) && (curThing.getType() <= kDMThingTypeSensor))
 			curThing = getNextThing(curThing);
 
-		AL0307_uc_ScentOrdinal = championMan.getScentOrdinal(mapX, mapY);
-		if (AL0307_uc_FootprintsAllowed && (AL0307_uc_ScentOrdinal) && (--AL0307_uc_ScentOrdinal >= championMan._party._firstScentIndex) && (AL0307_uc_ScentOrdinal < championMan._party._lastScentIndex))
+		if (AL0307_uc_FootprintsAllowed && (AL0307_uc_ScentOrdinal = championMan.getScentOrdinal(mapX, mapY)) && (--AL0307_uc_ScentOrdinal >= championMan._party._firstScentIndex) && (AL0307_uc_ScentOrdinal < championMan._party._lastScentIndex))
 			setFlag(aspectArray[kDMSquareAspectFloorOrn], kDMMaskFootprints);
 		break;
 	default:


Commit: 3f611279e0bc389acb00f81a51a1267ccb785674
    https://github.com/scummvm/scummvm/commit/3f611279e0bc389acb00f81a51a1267ccb785674
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-06-01T17:57:59+02:00

Commit Message:
DM: Fix out-of-bounds read. PVS-Studio V781

Move coordinates bounds checks before accessing map data to prevent crashes

Changed paths:
    engines/dm/dungeonman.cpp


diff --git a/engines/dm/dungeonman.cpp b/engines/dm/dungeonman.cpp
index a989070b636..f6e6ea312a8 100644
--- a/engines/dm/dungeonman.cpp
+++ b/engines/dm/dungeonman.cpp
@@ -831,8 +831,11 @@ Square DungeonMan::getRelSquare(Direction dir, int16 stepsForward, int16 stepsRi
 }
 
 int16 DungeonMan::getSquareFirstThingIndex(int16 mapX, int16 mapY) {
+	if ((mapX < 0) || (mapX >= _currMapWidth) || (mapY < 0) || (mapY >= _currMapHeight))
+		return -1;
+
 	unsigned char *curSquare = _currMapData[mapX];
-	if ((mapX < 0) || (mapX >= _currMapWidth) || (mapY < 0) || (mapY >= _currMapHeight) || !getFlag(curSquare[mapY], kDMSquareMaskThingListPresent))
+	if (!getFlag(curSquare[mapY], kDMSquareMaskThingListPresent))
 		return -1;
 
 	int16 curMapY = 0;


Commit: 22841aa24c85be7c35dc12efb783899949d3b874
    https://github.com/scummvm/scummvm/commit/22841aa24c85be7c35dc12efb783899949d3b874
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-06-01T17:57:59+02:00

Commit Message:
DM: Correct sign mismatch using explicit cast. PVS-Studio V1112

Changed paths:
    engines/dm/champion.cpp
    engines/dm/eventman.cpp


diff --git a/engines/dm/champion.cpp b/engines/dm/champion.cpp
index 21fb97016f2..436a529dae2 100644
--- a/engines/dm/champion.cpp
+++ b/engines/dm/champion.cpp
@@ -1604,7 +1604,7 @@ void ChampionMan::unpoison(int16 champIndex) {
 
 	TimelineEvent *eventPtr = _vm->_timeline->_events;
 	for (uint16 eventIndex = 0; eventIndex < _vm->_timeline->_eventMaxCount; eventPtr++, eventIndex++) {
-		if ((eventPtr->_type == kDMEventTypePoisonChampion) && (eventPtr->_priority == champIndex))
+		if ((eventPtr->_type == kDMEventTypePoisonChampion) && ((int16)eventPtr->_priority == champIndex))
 			_vm->_timeline->deleteEvent(eventIndex);
 	}
 	_champions[champIndex]._poisonEventCount = 0;
diff --git a/engines/dm/eventman.cpp b/engines/dm/eventman.cpp
index 9a0377cc39b..7515380d0df 100644
--- a/engines/dm/eventman.cpp
+++ b/engines/dm/eventman.cpp
@@ -530,7 +530,7 @@ void EventManager::buildpointerScreenArea(int16 mousePosX, int16 mousePosY) {
 			_mousePointerType = k4_pointerTypeAutoselect;
 		else {
 			championIdx++;
-			if (championIdx == _vm->_inventoryMan->_inventoryChampionOrdinal)
+			if (championIdx == (uint16)_vm->_inventoryMan->_inventoryChampionOrdinal)
 				_mousePointerType = k0_pointerTypeArrow;
 			else if (mousePosY <= 6)
 				_mousePointerType = k0_pointerTypeArrow;


Commit: e59edaa25dc38e778775b95dc3980484510bc833
    https://github.com/scummvm/scummvm/commit/e59edaa25dc38e778775b95dc3980484510bc833
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-06-01T17:57:59+02:00

Commit Message:
DM: Fix out-of-bounds pointer arithmetic. PVS-Studio V557

Use row-pointer incrementing to traverse _doorButtonCoordSets safely, avoiding out-of-bounds warnings when traversing to the next sub-array in contiguous memory.

Changed paths:
    engines/dm/gfx.cpp


diff --git a/engines/dm/gfx.cpp b/engines/dm/gfx.cpp
index 41b6f422c55..13c9c31a5c5 100644
--- a/engines/dm/gfx.cpp
+++ b/engines/dm/gfx.cpp
@@ -2642,10 +2642,10 @@ void DisplayMan::loadCurrentMapGraphics() {
 	}
 
 	for (uint16 index = kDMDerivedBitmapFirstDoorButton, counter = 0; counter < k1_DoorButtonCount; counter++) {
-		uint16 *coords = _doorButtonCoordSets[_doorButtonCoordSet[counter]][1];
-		_derivedBitmapByteCount[index++] = coords[4] * coords[5];
-		coords += 6;
-		_derivedBitmapByteCount[index++] = coords[4] * coords[5];
+		uint16 (*rowPtr)[6] = _doorButtonCoordSets[_doorButtonCoordSet[counter]] + 1;
+		_derivedBitmapByteCount[index++] = (*rowPtr)[4] * (*rowPtr)[5];
+		rowPtr++;
+		_derivedBitmapByteCount[index++] = (*rowPtr)[4] * (*rowPtr)[5];
 	}
 
 	applyCreatureReplColors(9, 8);


Commit: 2f5bbc4c20dc1a2e6f7bbd0291dee480dd2ec760
    https://github.com/scummvm/scummvm/commit/2f5bbc4c20dc1a2e6f7bbd0291dee480dd2ec760
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-06-01T17:57:59+02:00

Commit Message:
DM: Fix out-of-bounds map data access. PVS-Studio V781

Move the map coordinate boundary check to run before indexing dungeon._currMapData array.

Changed paths:
    engines/dm/group.cpp


diff --git a/engines/dm/group.cpp b/engines/dm/group.cpp
index e268cb1028f..84bda38c9db 100644
--- a/engines/dm/group.cpp
+++ b/engines/dm/group.cpp
@@ -1105,12 +1105,14 @@ bool GroupMan::isMovementPossible(CreatureInfo *creatureInfo, int16 mapX, int16
 
 
 	dungeon.mapCoordsAfterRelMovement((Direction)dir, 1, 0, mapX, mapY);
+	if ((mapX < 0) || (mapX >= dungeon._currMapWidth) || (mapY < 0) || (mapY >= dungeon._currMapHeight)) {
+		_groupMovBlockedByWallStairsPitFakeWalFluxCageTeleporter = true;
+		return false;
+	}
 	uint16 curSquare = dungeon._currMapData[mapX][mapY];
 	int16 curSquareType = Square(curSquare).getType();
 	_groupMovBlockedByWallStairsPitFakeWalFluxCageTeleporter =
-		!(((mapX >= 0) && (mapX < dungeon._currMapWidth)) &&
-		 ((mapY >= 0) && (mapY < dungeon._currMapHeight)) &&
-		  (curSquareType != kDMElementTypeWall) &&
+		!((curSquareType != kDMElementTypeWall) &&
 		  (curSquareType != kDMElementTypeStairs) &&
 		 ((curSquareType != kDMElementTypePit) || (getFlag(curSquare, kDMSquareMaskPitImaginary) && allowMovementOverImaginaryPitsAndFakeWalls) || !getFlag(curSquare, kDMSquareMaskPitOpen) || getFlag(creatureInfo->_attributes, kDMCreatureMaskLevitation)) &&
 		 ((curSquareType != kDMElementTypeFakeWall) || getFlag(curSquare, kDMSquareMaskFakeWallOpen) || (getFlag(curSquare, kDMSquareMaskFakeWallImaginary) && allowMovementOverImaginaryPitsAndFakeWalls)));



From noreply at scummvm.org  Mon Jun  1 16:27:19 2026
From: noreply at scummvm.org (neuromancer)
Date: Mon,  1 Jun 2026 16:27:19 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 56763c322d0a69c065105df8bb385b4dc330fb22
Message-ID: <20260601162719.2DCFC700591@ron.scummvm.net>

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

Summary:
56763c322d SCUMM: RA2: allow gamepad navigation


Commit: 56763c322d0a69c065105df8bb385b4dc330fb22
    https://github.com/scummvm/scummvm/commit/56763c322d0a69c065105df8bb385b4dc330fb22
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-01T18:26:45+02:00

Commit Message:
SCUMM: RA2: allow gamepad navigation

Changed paths:
    engines/scumm/insane/rebel2/rebel.cpp
    engines/scumm/insane/rebel2/rebel.h
    engines/scumm/metaengine.cpp
    engines/scumm/scumm.h


diff --git a/engines/scumm/insane/rebel2/rebel.cpp b/engines/scumm/insane/rebel2/rebel.cpp
index 3b34d0bd1cc..4c363df1be4 100644
--- a/engines/scumm/insane/rebel2/rebel.cpp
+++ b/engines/scumm/insane/rebel2/rebel.cpp
@@ -51,6 +51,47 @@
 
 namespace Scumm {
 
+const int kRA2MenuAxisThreshold = Common::JOYAXIS_MAX / 5;
+const uint32 kRA2MenuGamepadNavigationDebounceMs = 250;
+const uint32 kRA2MenuGamepadMouseSuppressMs = 250;
+
+bool isRebel2RawMenuAxis(int axis) {
+	return axis == Common::JOYSTICK_AXIS_LEFT_STICK_X ||
+	       axis == Common::JOYSTICK_AXIS_LEFT_STICK_Y ||
+	       axis == Common::JOYSTICK_AXIS_RIGHT_STICK_X ||
+	       axis == Common::JOYSTICK_AXIS_RIGHT_STICK_Y ||
+	       axis == Common::JOYSTICK_AXIS_HAT_X ||
+	       axis == Common::JOYSTICK_AXIS_HAT_Y ||
+	       axis >= 8;
+}
+
+Common::KeyCode getRebel2RawMenuAxisKey(int axis, int16 position) {
+	const bool horizontalAxis = axis == Common::JOYSTICK_AXIS_LEFT_STICK_X ||
+	                            axis == Common::JOYSTICK_AXIS_RIGHT_STICK_X ||
+	                            axis == Common::JOYSTICK_AXIS_HAT_X ||
+	                            (axis >= 8 && (axis % 2) == 0);
+	const bool verticalAxis = axis == Common::JOYSTICK_AXIS_LEFT_STICK_Y ||
+	                          axis == Common::JOYSTICK_AXIS_RIGHT_STICK_Y ||
+	                          axis == Common::JOYSTICK_AXIS_HAT_Y ||
+	                          (axis >= 8 && (axis % 2) != 0);
+
+	if (!horizontalAxis && !verticalAxis)
+		return Common::KEYCODE_INVALID;
+
+	if (position >= kRA2MenuAxisThreshold)
+		return horizontalAxis ? Common::KEYCODE_RIGHT : Common::KEYCODE_DOWN;
+	if (position <= -kRA2MenuAxisThreshold)
+		return horizontalAxis ? Common::KEYCODE_LEFT : Common::KEYCODE_UP;
+
+	return Common::KEYCODE_INVALID;
+}
+
+bool isRebel2MenuDirectionKey(Common::KeyCode keycode) {
+	return keycode == Common::KEYCODE_UP ||
+	       keycode == Common::KEYCODE_DOWN ||
+	       keycode == Common::KEYCODE_LEFT ||
+	       keycode == Common::KEYCODE_RIGHT;
+}
 
 InsaneRebel2::InsaneRebel2(ScummEngine_v7 *scumm) {
 	_vm = scumm;
@@ -471,6 +512,8 @@ InsaneRebel2::InsaneRebel2(ScummEngine_v7 *scumm) {
 	_joystickAxisX = 0;
 	_joystickAxisY = 0;
 	_gamepadAimActive = false;
+	_lastGameplayMenuCloseTime = 0;
+	_lastMenuGamepadNavigationTime = 0;
 
 	// Initialize level state tracking for multi-phase levels
 	_currentPhase = 1;
@@ -525,6 +568,23 @@ InsaneRebel2::~InsaneRebel2() {
 	}
 }
 
+void InsaneRebel2::openGameplayMainMenu(SmushPlayer *splayer) {
+	if (!splayer)
+		return;
+
+	if (_pauseOverlayActive) {
+		_vm->_system->getPaletteManager()->setPalette(_savedPausePalette, 0, 256);
+		_pauseOverlayActive = false;
+	}
+
+	if (!splayer->_paused)
+		splayer->pause();
+
+	_vm->openMainMenuDialog();
+	splayer->unpause();
+	_lastGameplayMenuCloseTime = _vm->_system->getMillis();
+}
+
 // 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.
@@ -558,11 +618,82 @@ bool InsaneRebel2::notifyEvent(const Common::Event &event) {
 		}
 	}
 
+	if (_menuInputActive && event.type == Common::EVENT_JOYAXIS_MOTION) {
+		const int axis = event.joystick.axis;
+
+		if (!isRebel2RawMenuAxis(axis))
+			return false;
+
+		const Common::KeyCode keycode = getRebel2RawMenuAxisKey(axis, event.joystick.position);
+		if (keycode == Common::KEYCODE_INVALID)
+			return true;
+
+		const uint32 now = _vm->_system->getMillis();
+		if (_lastMenuGamepadNavigationTime != 0 &&
+				now - _lastMenuGamepadNavigationTime < kRA2MenuGamepadNavigationDebounceMs)
+			return true;
+
+		_lastMenuGamepadNavigationTime = now;
+		Common::Event syntheticEvent = Common::Event();
+		syntheticEvent.type = Common::EVENT_KEYDOWN;
+		syntheticEvent.kbd.keycode = keycode;
+		_menuEventQueue.push(syntheticEvent);
+		return true;
+	}
+
 	// Analog stick ??? reticle velocity (mirrors RA1's analog model). The mapped
 	// axis actions carry a signed position; a centered stick reports position 0.
 	// We ignore a recentre that would fight the opposite direction so a quick
 	// flick doesn't get cancelled by the trailing zero of the other axis half.
 	if (event.type == Common::EVENT_CUSTOM_BACKEND_ACTION_AXIS) {
+		if (_menuInputActive) {
+			if (event.joystick.position == 0) {
+				switch (event.customType) {
+				case kScummBackendActionRebel2AxisUp:
+				case kScummBackendActionRebel2AxisDown:
+					return true;
+				case kScummBackendActionRebel2AxisLeft:
+				case kScummBackendActionRebel2AxisRight:
+					return true;
+				default:
+					break;
+				}
+				return true;
+			}
+
+			Common::KeyCode keycode = Common::KEYCODE_INVALID;
+			switch (event.customType) {
+			case kScummBackendActionRebel2AxisUp:
+				keycode = Common::KEYCODE_UP;
+				break;
+			case kScummBackendActionRebel2AxisDown:
+				keycode = Common::KEYCODE_DOWN;
+				break;
+			case kScummBackendActionRebel2AxisLeft:
+				keycode = Common::KEYCODE_LEFT;
+				break;
+			case kScummBackendActionRebel2AxisRight:
+				keycode = Common::KEYCODE_RIGHT;
+				break;
+			default:
+				break;
+			}
+
+			if (keycode != Common::KEYCODE_INVALID) {
+				const uint32 now = _vm->_system->getMillis();
+				if (_lastMenuGamepadNavigationTime != 0 &&
+						now - _lastMenuGamepadNavigationTime < kRA2MenuGamepadNavigationDebounceMs)
+					return true;
+
+				_lastMenuGamepadNavigationTime = now;
+				Common::Event syntheticEvent = Common::Event();
+				syntheticEvent.type = Common::EVENT_KEYDOWN;
+				syntheticEvent.kbd.keycode = keycode;
+				_menuEventQueue.push(syntheticEvent);
+				return true;
+			}
+		}
+
 		const int16 axisPosition = (event.joystick.position == Common::JOYAXIS_MIN)
 			? Common::JOYAXIS_MAX : event.joystick.position;
 
@@ -595,6 +726,67 @@ bool InsaneRebel2::notifyEvent(const Common::Event &event) {
 	if (event.type == Common::EVENT_CUSTOM_ENGINE_ACTION_START ||
 		event.type == Common::EVENT_CUSTOM_ENGINE_ACTION_END) {
 		const bool pressed = (event.type == Common::EVENT_CUSTOM_ENGINE_ACTION_START);
+		const bool menuState = _gameState == kStateMainMenu ||
+		                       _gameState == kStatePilotSelect ||
+		                       _gameState == kStateDifficultySelect ||
+		                       _gameState == kStateChapterSelect ||
+		                       _gameState == kStateOptions ||
+		                       _gameState == kStateTopPilots;
+
+		if (event.customType == kScummActionInsaneSkip) {
+			if (!pressed)
+				return true;
+
+			if (_menuInputActive && menuState) {
+				Common::Event syntheticEvent = Common::Event();
+				syntheticEvent.type = Common::EVENT_KEYDOWN;
+				syntheticEvent.kbd.keycode = Common::KEYCODE_ESCAPE;
+				syntheticEvent.kbd.ascii = Common::ASCII_ESCAPE;
+				_menuEventQueue.push(syntheticEvent);
+				return true;
+			}
+
+			if (_gameState == kStateGameplay && _rebelHandler != 0) {
+				debug("Rebel2: Skip/back action ignored during gameplay");
+				return true;
+			}
+
+			debug("Rebel2: Skip/back action - skipping video");
+			_vm->_smushVideoShouldFinish = true;
+			return true;
+		}
+
+		if (event.customType == kScummActionInsaneBack) {
+			if (!pressed)
+				return true;
+
+			if (_menuInputActive && menuState) {
+				Common::Event syntheticEvent = Common::Event();
+				syntheticEvent.type = Common::EVENT_KEYDOWN;
+				syntheticEvent.kbd.keycode = Common::KEYCODE_ESCAPE;
+				syntheticEvent.kbd.ascii = Common::ASCII_ESCAPE;
+				_menuEventQueue.push(syntheticEvent);
+				return true;
+			}
+
+			if (_gameState == kStateGameplay && _rebelHandler != 0) {
+				if (_lastGameplayMenuCloseTime != 0) {
+					const uint32 elapsedSinceMenuClose = _vm->_system->getMillis() - _lastGameplayMenuCloseTime;
+					if (elapsedSinceMenuClose < 500) {
+						debug("Rebel2: Ignoring repeated gameplay menu action (%u ms)", elapsedSinceMenuClose);
+						return true;
+					}
+				}
+
+				debug("Rebel2: Back/menu action during gameplay - opening ScummVM menu");
+				openGameplayMainMenu(splayer);
+				return true;
+			}
+
+			debug("Rebel2: Back/menu action - skipping video");
+			_vm->_smushVideoShouldFinish = true;
+			return true;
+		}
 
 		if (pressed && splayer && splayer->_paused && _gameState == kStateGameplay) {
 			debug("Rebel2: Joystick action while paused - unpausing");
@@ -634,6 +826,12 @@ bool InsaneRebel2::notifyEvent(const Common::Event &event) {
 				case kScummActionInsaneSwitch:
 					keycode = Common::KEYCODE_ESCAPE;
 					break;
+				case kScummActionInsaneBack:
+					keycode = Common::KEYCODE_ESCAPE;
+					break;
+				case kScummActionInsaneSkip:
+					keycode = Common::KEYCODE_ESCAPE;
+					break;
 				default:
 					break;
 				}
@@ -642,7 +840,9 @@ bool InsaneRebel2::notifyEvent(const Common::Event &event) {
 			case kStateTopPilots:
 				if (event.customType == kScummActionInsaneAttack)
 					keycode = Common::KEYCODE_RETURN;
-				else if (event.customType == kScummActionInsaneSwitch)
+				else if (event.customType == kScummActionInsaneSwitch ||
+				         event.customType == kScummActionInsaneBack ||
+				         event.customType == kScummActionInsaneSkip)
 					keycode = Common::KEYCODE_ESCAPE;
 				break;
 
@@ -651,6 +851,14 @@ bool InsaneRebel2::notifyEvent(const Common::Event &event) {
 			}
 
 			if (keycode != Common::KEYCODE_INVALID) {
+				if (isRebel2MenuDirectionKey(keycode)) {
+					const uint32 now = _vm->_system->getMillis();
+					if (_lastMenuGamepadNavigationTime != 0 &&
+							now - _lastMenuGamepadNavigationTime < kRA2MenuGamepadNavigationDebounceMs)
+						return true;
+					_lastMenuGamepadNavigationTime = now;
+				}
+
 				Common::Event syntheticEvent = Common::Event();
 				syntheticEvent.type = Common::EVENT_KEYDOWN;
 				syntheticEvent.kbd.keycode = keycode;
@@ -669,7 +877,40 @@ bool InsaneRebel2::notifyEvent(const Common::Event &event) {
 		// consumed (and returned true for) the cases they handle.
 	}
 
+	const bool gameplayMenuTrigger = (event.type == Common::EVENT_MAINMENU) ||
+		(event.type == Common::EVENT_KEYDOWN && event.kbd.keycode == Common::KEYCODE_ESCAPE);
+	if (gameplayMenuTrigger && _gameState == kStateGameplay && _lastGameplayMenuCloseTime != 0) {
+		const uint32 elapsedSinceMenuClose = _vm->_system->getMillis() - _lastGameplayMenuCloseTime;
+		if (elapsedSinceMenuClose < 500) {
+			debug("Rebel2: Ignoring repeated gameplay menu trigger (%u ms)", elapsedSinceMenuClose);
+			return true;
+		}
+	}
+
+	if (event.type == Common::EVENT_MAINMENU && splayer &&
+			_gameState == kStateGameplay && _rebelHandler != 0) {
+		debug("Rebel2: Main menu action during gameplay - opening ScummVM menu");
+		openGameplayMainMenu(splayer);
+		return true;
+	}
+
 	if (event.type == Common::EVENT_KEYDOWN) {
+		// Gamepad buttons mapped to ESC can lose their key-up while the GMM is open,
+		// leaving the key repeat source to synthesize another ESC immediately after
+		// returning to gameplay. Treat repeats as part of the original press.
+		if (event.kbd.keycode == Common::KEYCODE_ESCAPE && event.kbdRepeat) {
+			debug("Rebel2: Ignoring repeated ESC keydown");
+			return true;
+		}
+		if (_menuInputActive && event.kbdRepeat &&
+				(event.kbd.keycode == Common::KEYCODE_UP ||
+				 event.kbd.keycode == Common::KEYCODE_DOWN ||
+				 event.kbd.keycode == Common::KEYCODE_LEFT ||
+				 event.kbd.keycode == Common::KEYCODE_RIGHT)) {
+			debug("Rebel2: Ignoring repeated menu direction keydown");
+			return true;
+		}
+
 		// When paused during gameplay, ANY key unpauses (FUN_405A21 line 360-365).
 		// ESC additionally opens the ScummVM menu (original: quit key exits level).
 		if (splayer && splayer->_paused && _gameState == kStateGameplay) {
@@ -682,9 +923,7 @@ bool InsaneRebel2::notifyEvent(const Common::Event &event) {
 			splayer->unpause();
 			if (event.kbd.keycode == Common::KEYCODE_ESCAPE && _rebelHandler != 0) {
 				debug("Rebel2: ESC during pause - opening ScummVM menu");
-				splayer->pause();
-				_vm->openMainMenuDialog();
-				splayer->unpause();
+				openGameplayMainMenu(splayer);
 			}
 			return true;
 		}
@@ -709,12 +948,7 @@ bool InsaneRebel2::notifyEvent(const Common::Event &event) {
 				} else if (_gameState == kStateGameplay && _rebelHandler != 0) {
 					// During active gameplay (handler != 0): pause and open ScummVM menu.
 					debug("Rebel2: ESC pressed during gameplay - opening ScummVM menu");
-					bool wasPaused = splayer->_paused;
-					if (!wasPaused)
-						splayer->pause();
-					_vm->openMainMenuDialog();
-					if (!wasPaused)
-						splayer->unpause();
+					openGameplayMainMenu(splayer);
 				} else {
 					// During cutscenes/intros/mission briefings: skip video
 					debug("Rebel2: ESC pressed - skipping video");
@@ -762,12 +996,16 @@ bool InsaneRebel2::notifyEvent(const Common::Event &event) {
 	switch (event.type) {
 	case Common::EVENT_KEYDOWN:
 	case Common::EVENT_LBUTTONDOWN:
-	case Common::EVENT_MOUSEMOVE:
 	case Common::EVENT_QUIT:
 	case Common::EVENT_RETURN_TO_LAUNCHER:
 		// Queue these events for processing in processMenuInput()
 		_menuEventQueue.push(event);
 		break;
+	case Common::EVENT_MOUSEMOVE:
+		if (_lastMenuGamepadNavigationTime == 0 ||
+				_vm->_system->getMillis() - _lastMenuGamepadNavigationTime >= kRA2MenuGamepadMouseSuppressMs)
+			_menuEventQueue.push(event);
+		break;
 	default:
 		break;
 	}
diff --git a/engines/scumm/insane/rebel2/rebel.h b/engines/scumm/insane/rebel2/rebel.h
index fefd80a6f96..926c23d69ab 100644
--- a/engines/scumm/insane/rebel2/rebel.h
+++ b/engines/scumm/insane/rebel2/rebel.h
@@ -510,6 +510,9 @@ public:
 	// recenter the reticle (the cursor is locked ~center during gameplay, so a gamepad
 	// "click" lands at center). See notifyEvent()/updateGameplayAimFromGamepad().
 	bool _gamepadAimActive;
+	uint32 _lastGameplayMenuCloseTime;
+	uint32 _lastMenuGamepadNavigationTime;
+	void openGameplayMainMenu(SmushPlayer *splayer);
 	bool isBitSet(int n) override;
 	void setBit(int n) override;
 
diff --git a/engines/scumm/metaengine.cpp b/engines/scumm/metaengine.cpp
index 93e61776248..c43f81ea35d 100644
--- a/engines/scumm/metaengine.cpp
+++ b/engines/scumm/metaengine.cpp
@@ -1019,6 +1019,18 @@ Common::KeymapArray ScummMetaEngine::initKeymaps(const char *target) const {
 	Common::String gameId = ConfMan.get("gameid", target);
 	Action *act;
 
+	if (gameId == "rebel2") {
+		for (uint i = 0; i < keymaps.size(); ++i) {
+			if (keymaps[i]->getId() == "engine-default") {
+				delete keymaps.remove_at(i);
+				Keymap *engineKeyMap = new Keymap(Keymap::kKeymapTypeGame, "engine-default", _("Default game keymappings"));
+				engineKeyMap->setPartialMatchAllowed(false);
+				keymaps.insert_at(i, engineKeyMap);
+				break;
+			}
+		}
+	}
+
 	if (gameId == "ft") {
 		Keymap *insaneKeymap = new Keymap(Keymap::kKeymapTypeGame, insaneKeymapId, "SCUMM - Bike Fights");
 
@@ -1191,24 +1203,28 @@ Common::KeymapArray ScummMetaEngine::initKeymaps(const char *target) const {
 		act->setCustomBackendActionAxisEvent(kScummBackendActionRebel2AxisUp);
 		act->addDefaultInputMapping("JOY_LEFT_STICK_Y-");
 		act->addDefaultInputMapping("JOY_RIGHT_STICK_Y-");
+		act->addDefaultInputMapping("JOY_HAT_Y-");
 		rebel2Keymap->addAction(act);
 
 		act = new Action("RA2STICKDOWN", _("Stick down"));
 		act->setCustomBackendActionAxisEvent(kScummBackendActionRebel2AxisDown);
 		act->addDefaultInputMapping("JOY_LEFT_STICK_Y+");
 		act->addDefaultInputMapping("JOY_RIGHT_STICK_Y+");
+		act->addDefaultInputMapping("JOY_HAT_Y+");
 		rebel2Keymap->addAction(act);
 
 		act = new Action("RA2STICKLEFT", _("Stick left"));
 		act->setCustomBackendActionAxisEvent(kScummBackendActionRebel2AxisLeft);
 		act->addDefaultInputMapping("JOY_LEFT_STICK_X-");
 		act->addDefaultInputMapping("JOY_RIGHT_STICK_X-");
+		act->addDefaultInputMapping("JOY_HAT_X-");
 		rebel2Keymap->addAction(act);
 
 		act = new Action("RA2STICKRIGHT", _("Stick right"));
 		act->setCustomBackendActionAxisEvent(kScummBackendActionRebel2AxisRight);
 		act->addDefaultInputMapping("JOY_LEFT_STICK_X+");
 		act->addDefaultInputMapping("JOY_RIGHT_STICK_X+");
+		act->addDefaultInputMapping("JOY_HAT_X+");
 		rebel2Keymap->addAction(act);
 
 		act = new Action("RA2FIRE", _("Fire / select"));
@@ -1218,12 +1234,20 @@ Common::KeymapArray ScummMetaEngine::initKeymaps(const char *target) const {
 
 		act = new Action("RA2COVER", _("Cover / back"));
 		act->setCustomEngineActionEvent(kScummActionInsaneSwitch);
+		act->addDefaultInputMapping("JOY_X");
+		act->addDefaultInputMapping("JOY_Y");
+		rebel2Keymap->addAction(act);
+
+		act = new Action("RA2SKIP", _("Skip / back"));
+		act->setCustomEngineActionEvent(kScummActionInsaneSkip);
 		act->addDefaultInputMapping("JOY_B");
+		act->addDefaultInputMapping("JOY_RIGHT_TRIGGER");
+		act->addDefaultInputMapping("AC_BACK");
 		rebel2Keymap->addAction(act);
 
 		act = new Action("RA2BACK", _("Skip / menu"));
-		act->setKeyEvent(KeyState(KEYCODE_ESCAPE, ASCII_ESCAPE));
-		act->addDefaultInputMapping("JOY_Y");
+		act->setCustomEngineActionEvent(kScummActionInsaneBack);
+		act->addDefaultInputMapping("ESCAPE");
 		act->addDefaultInputMapping("JOY_START");
 		rebel2Keymap->addAction(act);
 
diff --git a/engines/scumm/scumm.h b/engines/scumm/scumm.h
index 94ebcfc4a7a..ae7b4bb7cd1 100644
--- a/engines/scumm/scumm.h
+++ b/engines/scumm/scumm.h
@@ -499,6 +499,8 @@ enum ScummAction {
 	kScummActionInsaneAttack,
 	kScummActionInsaneSwitch,
 	kScummActionInsaneCheat,
+	kScummActionInsaneBack,
+	kScummActionInsaneSkip,
 
 	kScummActionCount
 };



From noreply at scummvm.org  Mon Jun  1 16:27:26 2026
From: noreply at scummvm.org (ScummVM-Translations)
Date: Mon,  1 Jun 2026 16:27:26 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 c03bb4f729c9ae5223e084ee90ceb3906df6ec2e
Message-ID: <20260601162726.84AA27005BB@ron.scummvm.net>

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

Summary:
c03bb4f729 I18N: Update translations templates


Commit: c03bb4f729c9ae5223e084ee90ceb3906df6ec2e
    https://github.com/scummvm/scummvm/commit/c03bb4f729c9ae5223e084ee90ceb3906df6ec2e
Author: Weblate (noreply at weblate.org)
Date: 2026-06-01T16:27:20Z

Commit Message:
I18N: Update translations templates

Changed paths:
    po/scummvm.pot


diff --git a/po/scummvm.pot b/po/scummvm.pot
index a0615fac668..27bbdefa188 100644
--- a/po/scummvm.pot
+++ b/po/scummvm.pot
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2026.2.1git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 14:35+0000\n"
+"POT-Creation-Date: 2026-06-01 16:27+0000\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL at ADDRESS>\n"
 "Language-Team: LANGUAGE <LL at li.org>\n"
@@ -1014,7 +1014,7 @@ msgstr ""
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1064
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1076
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1035,7 +1035,7 @@ msgstr ""
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1031
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1043
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1056,7 +1056,7 @@ msgstr ""
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1044
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1056
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1076,7 +1076,7 @@ msgstr ""
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1051
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1063
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3589,6 +3589,7 @@ msgid "Game not implemented"
 msgstr ""
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
+#: engines/scumm/metaengine.cpp:1026
 msgid "Default game keymappings"
 msgstr ""
 
@@ -6795,7 +6796,7 @@ msgstr ""
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1090 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1102 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -7442,7 +7443,7 @@ msgstr ""
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1077
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1089
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -12392,84 +12393,88 @@ msgstr ""
 msgid "All levels will be available without requiring passwords"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1025
+#: engines/scumm/metaengine.cpp:1037
 msgid "Down left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1038
+#: engines/scumm/metaengine.cpp:1050
 msgid "Down right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1058
+#: engines/scumm/metaengine.cpp:1070
 msgid "Up left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1071
+#: engines/scumm/metaengine.cpp:1083
 msgid "Up right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1083
+#: engines/scumm/metaengine.cpp:1095
 msgid "Switch weapon"
 msgstr ""
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1097
+#: engines/scumm/metaengine.cpp:1109
 msgid "Win the bike fight cheat"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1106
+#: engines/scumm/metaengine.cpp:1118
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1108 engines/scumm/metaengine.cpp:1170
+#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1182
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1113 engines/scumm/metaengine.cpp:1175
+#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1187
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1118 engines/scumm/metaengine.cpp:1180
+#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1192
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1123 engines/scumm/metaengine.cpp:1185
+#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1197
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1128 engines/scumm/metaengine.cpp:1190
+#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
 msgid "Stick up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1134 engines/scumm/metaengine.cpp:1196
+#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1209
 msgid "Stick down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1216
 msgid "Stick left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1208
+#: engines/scumm/metaengine.cpp:1158 engines/scumm/metaengine.cpp:1223
 msgid "Stick right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1214
+#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1230
 msgid "Fire / select"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1157
+#: engines/scumm/metaengine.cpp:1169
 msgid "Back / skip"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1168
+#: engines/scumm/metaengine.cpp:1180
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1219
+#: engines/scumm/metaengine.cpp:1235
 msgid "Cover / back"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1224
+#: engines/scumm/metaengine.cpp:1241
+msgid "Skip / back"
+msgstr ""
+
+#: engines/scumm/metaengine.cpp:1248
 msgid "Skip / menu"
 msgstr ""
 



From noreply at scummvm.org  Mon Jun  1 16:28:00 2026
From: noreply at scummvm.org (ScummVM-Translations)
Date: Mon,  1 Jun 2026 16:28:00 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 0a62c03c18eb7cc033b8906d7bf5bf7c41de2ed1
Message-ID: <20260601162800.D8DC5700591@ron.scummvm.net>

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

Summary:
0a62c03c18 I18N: Update translation files


Commit: 0a62c03c18eb7cc033b8906d7bf5bf7c41de2ed1
    https://github.com/scummvm/scummvm/commit/0a62c03c18eb7cc033b8906d7bf5bf7c41de2ed1
Author: Weblate (noreply at weblate.org)
Date: 2026-06-01T16:27:40Z

Commit Message:
I18N: Update translation files

Updated by "Update PO files to match POT (msgmerge)" hook in Weblate.

Translation: ScummVM/scummvm
Translate-URL: https://translations.scummvm.org/projects/scummvm/scummvm/

Changed paths:
    po/ar.po
    po/be-tarask.po
    po/be_BY.po
    po/ca_ES.po
    po/cs_CZ.po
    po/da.po
    po/de_DE.po
    po/el.po
    po/es_ES.po
    po/eu.po
    po/fi_FI.po
    po/fr_FR.po
    po/gl_ES.po
    po/he.po
    po/hi.po
    po/hu_HU.po
    po/it_IT.po
    po/ja.po
    po/ka.po
    po/ko.po
    po/nb_NO.po
    po/nl_NL.po
    po/nn_NO.po
    po/pl_PL.po
    po/pt_BR.po
    po/pt_PT.po
    po/ro.po
    po/ru_RU.po
    po/sv_SE.po
    po/tr.po
    po/uk_UA.po
    po/zh.po
    po/zh_Hans.po
    po/zh_Hant.po


diff --git a/po/ar.po b/po/ar.po
index fc404c6b896..ff95d404548 100644
--- a/po/ar.po
+++ b/po/ar.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 14:35+0000\n"
+"POT-Creation-Date: 2026-06-01 16:27+0000\n"
 "PO-Revision-Date: 2026-02-24 22:26+0000\n"
 "Last-Translator: Mohamed Shaaban <mohmedbashamedo at hotmail.com>\n"
 "Language-Team: Arabic <https://translations.scummvm.org/projects/scummvm/"
@@ -1060,7 +1060,7 @@ msgstr "??????????????"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1064
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1076
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1081,7 +1081,7 @@ msgstr "????????"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1031
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1043
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1102,7 +1102,7 @@ msgstr "????????"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1044
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1056
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1122,7 +1122,7 @@ msgstr "????????"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1051
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1063
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3912,6 +3912,7 @@ msgid "Game not implemented"
 msgstr "?????????? ???????????? ?????? ??????????"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
+#: engines/scumm/metaengine.cpp:1026
 #, fuzzy
 #| msgid "Default game keymap"
 msgid "Default game keymappings"
@@ -7458,7 +7459,7 @@ msgstr "?????????? ????????????"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1090 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1102 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -8224,7 +8225,7 @@ msgstr "??????????????"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1077
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1089
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -14173,106 +14174,112 @@ msgstr ""
 msgid "All levels will be available without requiring passwords"
 msgstr "?????????? ???????? ?????????????????? ?????????? ????????."
 
-#: engines/scumm/metaengine.cpp:1025
+#: engines/scumm/metaengine.cpp:1037
 #, fuzzy
 #| msgid "Down Left"
 msgid "Down left"
 msgstr "???????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1038
+#: engines/scumm/metaengine.cpp:1050
 #, fuzzy
 #| msgid "Down Right"
 msgid "Down right"
 msgstr "???????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1058
+#: engines/scumm/metaengine.cpp:1070
 #, fuzzy
 #| msgid "Upper left item"
 msgid "Up left"
 msgstr "???????????? ???????????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1071
+#: engines/scumm/metaengine.cpp:1083
 #, fuzzy
 #| msgid "Up Right"
 msgid "Up right"
 msgstr "???????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1083
+#: engines/scumm/metaengine.cpp:1095
 msgid "Switch weapon"
 msgstr "?????????? ????????????"
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1097
+#: engines/scumm/metaengine.cpp:1109
 msgid "Win the bike fight cheat"
 msgstr "?????? ???????? ???? ?????????? ??????????????"
 
-#: engines/scumm/metaengine.cpp:1106
+#: engines/scumm/metaengine.cpp:1118
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1108 engines/scumm/metaengine.cpp:1170
+#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1182
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1113 engines/scumm/metaengine.cpp:1175
+#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1187
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1118 engines/scumm/metaengine.cpp:1180
+#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1192
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1123 engines/scumm/metaengine.cpp:1185
+#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1197
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1128 engines/scumm/metaengine.cpp:1190
+#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "??????????"
 
-#: engines/scumm/metaengine.cpp:1134 engines/scumm/metaengine.cpp:1196
+#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1209
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "?????? ???????????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1216
 #, fuzzy
 #| msgid "Slide Left"
 msgid "Stick left"
 msgstr "?????????????? ?????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1208
+#: engines/scumm/metaengine.cpp:1158 engines/scumm/metaengine.cpp:1223
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick right"
 msgstr "???????????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1214
+#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1230
 #, fuzzy
 #| msgid "Move left"
 msgid "Fire / select"
 msgstr "???????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1157
+#: engines/scumm/metaengine.cpp:1169
 #, fuzzy
 #| msgid "Backend"
 msgid "Back / skip"
 msgstr "??????????????"
 
-#: engines/scumm/metaengine.cpp:1168
+#: engines/scumm/metaengine.cpp:1180
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1219
+#: engines/scumm/metaengine.cpp:1235
 #, fuzzy
 #| msgid "Move Back"
 msgid "Cover / back"
 msgstr "???????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1224
+#: engines/scumm/metaengine.cpp:1241
+#, fuzzy
+#| msgid "Skip dialogue"
+msgid "Skip / back"
+msgstr "???????? ????????????"
+
+#: engines/scumm/metaengine.cpp:1248
 #, fuzzy
 #| msgid "Open main menu"
 msgid "Skip / menu"
diff --git a/po/be-tarask.po b/po/be-tarask.po
index b44ab3754d2..c6fbc78df20 100644
--- a/po/be-tarask.po
+++ b/po/be-tarask.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 14:35+0000\n"
+"POT-Creation-Date: 2026-06-01 16:27+0000\n"
 "PO-Revision-Date: 2023-02-26 15:01+0000\n"
 "Last-Translator: Kastu?? K. <oakum2334 at gmail.com>\n"
 "Language-Team: Belarusian (Tara??kievica) <https://translations.scummvm.org/"
@@ -1071,7 +1071,7 @@ msgstr "????????????????????"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1064
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1076
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1092,7 +1092,7 @@ msgstr "??????????"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1031
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1043
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1113,7 +1113,7 @@ msgstr "????????"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1044
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1056
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1133,7 +1133,7 @@ msgstr "????????????"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1051
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1063
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3868,6 +3868,7 @@ msgid "Game not implemented"
 msgstr "ID ???????????? ???? ??????????????????????????????"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
+#: engines/scumm/metaengine.cpp:1026
 #, fuzzy
 #| msgid "Default game keymap"
 msgid "Default game keymappings"
@@ -7400,7 +7401,7 @@ msgstr "???????????????????? ?????????? ??????"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1090 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1102 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -8171,7 +8172,7 @@ msgstr "????????????????????"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1077
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1089
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -14102,104 +14103,110 @@ msgstr ""
 msgid "All levels will be available without requiring passwords"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1025
+#: engines/scumm/metaengine.cpp:1037
 #, fuzzy
 msgid "Down left"
 msgstr "?????????????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1038
+#: engines/scumm/metaengine.cpp:1050
 #, fuzzy
 msgid "Down right"
 msgstr "?????????????? ??????????????"
 
-#: engines/scumm/metaengine.cpp:1058
+#: engines/scumm/metaengine.cpp:1070
 #, fuzzy
 #| msgid "Upper left item"
 msgid "Up left"
 msgstr "???????????? ?????????? ??????"
 
-#: engines/scumm/metaengine.cpp:1071
+#: engines/scumm/metaengine.cpp:1083
 #, fuzzy
 msgid "Up right"
 msgstr "??????????????"
 
-#: engines/scumm/metaengine.cpp:1083
+#: engines/scumm/metaengine.cpp:1095
 #, fuzzy
 msgid "Switch weapon"
 msgstr "????????????????????"
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1097
+#: engines/scumm/metaengine.cpp:1109
 msgid "Win the bike fight cheat"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1106
+#: engines/scumm/metaengine.cpp:1118
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1108 engines/scumm/metaengine.cpp:1170
+#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1182
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1113 engines/scumm/metaengine.cpp:1175
+#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1187
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1118 engines/scumm/metaengine.cpp:1180
+#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1192
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1123 engines/scumm/metaengine.cpp:1185
+#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1197
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1128 engines/scumm/metaengine.cpp:1190
+#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "??????????????"
 
-#: engines/scumm/metaengine.cpp:1134 engines/scumm/metaengine.cpp:1196
+#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1209
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "?????????????????????????? ????????"
 
-#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1216
 #, fuzzy
 #| msgid "Slide Left"
 msgid "Stick left"
 msgstr "?????????????????????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1208
+#: engines/scumm/metaengine.cpp:1158 engines/scumm/metaengine.cpp:1223
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick right"
 msgstr "?????????????????????? ??????????????"
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1214
+#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1230
 #, fuzzy
 #| msgid "Move left"
 msgid "Fire / select"
 msgstr "?????????????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1157
+#: engines/scumm/metaengine.cpp:1169
 #, fuzzy
 #| msgid "Backend"
 msgid "Back / skip"
 msgstr "????????????"
 
-#: engines/scumm/metaengine.cpp:1168
+#: engines/scumm/metaengine.cpp:1180
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1219
+#: engines/scumm/metaengine.cpp:1235
 #, fuzzy
 #| msgid "Move Back"
 msgid "Cover / back"
 msgstr "?????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1224
+#: engines/scumm/metaengine.cpp:1241
+#, fuzzy
+#| msgid "Skip dialogue"
+msgid "Skip / back"
+msgstr "?????????????????????? ????????????"
+
+#: engines/scumm/metaengine.cpp:1248
 #, fuzzy
 #| msgid "Open main menu"
 msgid "Skip / menu"
diff --git a/po/be_BY.po b/po/be_BY.po
index 9187b491e79..990799fbf2b 100644
--- a/po/be_BY.po
+++ b/po/be_BY.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 14:35+0000\n"
+"POT-Creation-Date: 2026-06-01 16:27+0000\n"
 "PO-Revision-Date: 2021-03-21 08:24+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Belarusian <https://translations.scummvm.org/projects/scummvm/"
@@ -1074,7 +1074,7 @@ msgstr "??????????????????????????"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1064
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1076
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1095,7 +1095,7 @@ msgstr "??????????"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1031
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1043
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1116,7 +1116,7 @@ msgstr "????????"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1044
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1056
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1136,7 +1136,7 @@ msgstr "????????????"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1051
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1063
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3873,6 +3873,7 @@ msgid "Game not implemented"
 msgstr "Game id ???? ??????????????????????????"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
+#: engines/scumm/metaengine.cpp:1026
 #, fuzzy
 #| msgid "Default game keymap"
 msgid "Default game keymappings"
@@ -7381,7 +7382,7 @@ msgstr "?????????????????????? ??????"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1090 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1102 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -8136,7 +8137,7 @@ msgstr "??????????????????????????"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1077
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1089
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -13912,104 +13913,109 @@ msgstr ""
 msgid "All levels will be available without requiring passwords"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1025
+#: engines/scumm/metaengine.cpp:1037
 #, fuzzy
 msgid "Down left"
 msgstr "?????????????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1038
+#: engines/scumm/metaengine.cpp:1050
 #, fuzzy
 msgid "Down right"
 msgstr "?????????????? ??????????????"
 
-#: engines/scumm/metaengine.cpp:1058
+#: engines/scumm/metaengine.cpp:1070
 #, fuzzy
 #| msgid "Upper left item"
 msgid "Up left"
 msgstr "???????????? ???????? ??????????????"
 
-#: engines/scumm/metaengine.cpp:1071
+#: engines/scumm/metaengine.cpp:1083
 #, fuzzy
 msgid "Up right"
 msgstr "??????????????"
 
-#: engines/scumm/metaengine.cpp:1083
+#: engines/scumm/metaengine.cpp:1095
 #, fuzzy
 msgid "Switch weapon"
 msgstr "?????????? ??????????:"
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1097
+#: engines/scumm/metaengine.cpp:1109
 msgid "Win the bike fight cheat"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1106
+#: engines/scumm/metaengine.cpp:1118
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1108 engines/scumm/metaengine.cpp:1170
+#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1182
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1113 engines/scumm/metaengine.cpp:1175
+#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1187
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1118 engines/scumm/metaengine.cpp:1180
+#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1192
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1123 engines/scumm/metaengine.cpp:1185
+#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1197
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1128 engines/scumm/metaengine.cpp:1190
+#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "??????????????"
 
-#: engines/scumm/metaengine.cpp:1134 engines/scumm/metaengine.cpp:1196
+#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1209
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "???????????????????? ????????"
 
-#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1216
 #, fuzzy
 #| msgid "Slide Left"
 msgid "Stick left"
 msgstr "???????????????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1208
+#: engines/scumm/metaengine.cpp:1158 engines/scumm/metaengine.cpp:1223
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick right"
 msgstr "???????????????? ??????????????"
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1214
+#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1230
 #, fuzzy
 #| msgid "Move left"
 msgid "Fire / select"
 msgstr "?????????????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1157
+#: engines/scumm/metaengine.cpp:1169
 #, fuzzy
 #| msgid "Joy Back"
 msgid "Back / skip"
 msgstr "???????????????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1168
+#: engines/scumm/metaengine.cpp:1180
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1219
+#: engines/scumm/metaengine.cpp:1235
 #, fuzzy
 #| msgid "Move Back"
 msgid "Cover / back"
 msgstr "???????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1224
+#: engines/scumm/metaengine.cpp:1241
+#, fuzzy
+msgid "Skip / back"
+msgstr "???????????????????? ??????????"
+
+#: engines/scumm/metaengine.cpp:1248
 #, fuzzy
 #| msgid "Open main menu"
 msgid "Skip / menu"
diff --git a/po/ca_ES.po b/po/ca_ES.po
index 83fb44112df..afc2bd754e8 100644
--- a/po/ca_ES.po
+++ b/po/ca_ES.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.6.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 14:35+0000\n"
+"POT-Creation-Date: 2026-06-01 16:27+0000\n"
 "PO-Revision-Date: 2026-05-26 04:22+0000\n"
 "Last-Translator: Xaviu <xaviu at xaviu.com>\n"
 "Language-Team: Catalan <https://translations.scummvm.org/projects/scummvm/"
@@ -1057,7 +1057,7 @@ msgstr "Interaccionar"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1064
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1076
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1078,7 +1078,7 @@ msgstr "Amunt"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1031
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1043
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1099,7 +1099,7 @@ msgstr "Avall"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1044
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1056
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1119,7 +1119,7 @@ msgstr "Esquerra"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1051
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1063
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3796,6 +3796,7 @@ msgid "Game not implemented"
 msgstr "Joc no implementat"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
+#: engines/scumm/metaengine.cpp:1026
 msgid "Default game keymappings"
 msgstr "Assignaci?? de tecles per defecte del joc"
 
@@ -7143,7 +7144,7 @@ msgstr "Activa / desactiva el combat"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1090 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1102 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -7811,7 +7812,7 @@ msgstr "Parla / Interactua"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1077
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1089
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -12985,98 +12986,104 @@ msgstr ""
 msgid "All levels will be available without requiring passwords"
 msgstr "Tots els nivells estaran disponibles per jugar."
 
-#: engines/scumm/metaengine.cpp:1025
+#: engines/scumm/metaengine.cpp:1037
 msgid "Down left"
 msgstr "A baix a l'esquerra"
 
-#: engines/scumm/metaengine.cpp:1038
+#: engines/scumm/metaengine.cpp:1050
 msgid "Down right"
 msgstr "A baix a la dreta"
 
-#: engines/scumm/metaengine.cpp:1058
+#: engines/scumm/metaengine.cpp:1070
 msgid "Up left"
 msgstr "Amunt a l'esquerra"
 
-#: engines/scumm/metaengine.cpp:1071
+#: engines/scumm/metaengine.cpp:1083
 msgid "Up right"
 msgstr "Amunt a la dreta"
 
-#: engines/scumm/metaengine.cpp:1083
+#: engines/scumm/metaengine.cpp:1095
 msgid "Switch weapon"
 msgstr "Canvia d'arma"
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1097
+#: engines/scumm/metaengine.cpp:1109
 msgid "Win the bike fight cheat"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1106
+#: engines/scumm/metaengine.cpp:1118
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1108 engines/scumm/metaengine.cpp:1170
+#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1182
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1113 engines/scumm/metaengine.cpp:1175
+#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1187
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1118 engines/scumm/metaengine.cpp:1180
+#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1192
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1123 engines/scumm/metaengine.cpp:1185
+#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1197
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1128 engines/scumm/metaengine.cpp:1190
+#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "Recull"
 
-#: engines/scumm/metaengine.cpp:1134 engines/scumm/metaengine.cpp:1196
+#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1209
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "Despla??ament avall"
 
-#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1216
 #, fuzzy
 #| msgid "Slide left"
 msgid "Stick left"
 msgstr "Llisca cap a l'esquerra"
 
-#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1208
+#: engines/scumm/metaengine.cpp:1158 engines/scumm/metaengine.cpp:1223
 #, fuzzy
 #| msgid "Slide right"
 msgid "Stick right"
 msgstr "Llisca cap a la dreta"
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1214
+#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1230
 #, fuzzy
 #| msgid "Move / Select"
 msgid "Fire / select"
 msgstr "Mou / Selecciona"
 
-#: engines/scumm/metaengine.cpp:1157
+#: engines/scumm/metaengine.cpp:1169
 #, fuzzy
 #| msgid "Backspace"
 msgid "Back / skip"
 msgstr "Retroc??s"
 
-#: engines/scumm/metaengine.cpp:1168
+#: engines/scumm/metaengine.cpp:1180
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1219
+#: engines/scumm/metaengine.cpp:1235
 #, fuzzy
 #| msgid "Move back"
 msgid "Cover / back"
 msgstr "Mou enrere"
 
-#: engines/scumm/metaengine.cpp:1224
+#: engines/scumm/metaengine.cpp:1241
+#, fuzzy
+#| msgid "Skip walk"
+msgid "Skip / back"
+msgstr "Omet el despla??ament"
+
+#: engines/scumm/metaengine.cpp:1248
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu"
diff --git a/po/cs_CZ.po b/po/cs_CZ.po
index 8d7660ce8b6..5078ad7843f 100644
--- a/po/cs_CZ.po
+++ b/po/cs_CZ.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.7.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 14:35+0000\n"
+"POT-Creation-Date: 2026-06-01 16:27+0000\n"
 "PO-Revision-Date: 2026-02-09 20:22+0000\n"
 "Last-Translator: AsciiWolf <mail at asciiwolf.com>\n"
 "Language-Team: Czech <https://translations.scummvm.org/projects/scummvm/"
@@ -1074,7 +1074,7 @@ msgstr "Interakce"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1064
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1076
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1095,7 +1095,7 @@ msgstr "Nahoru"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1031
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1043
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1116,7 +1116,7 @@ msgstr "Dol??"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1044
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1056
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1136,7 +1136,7 @@ msgstr "Doleva"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1051
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1063
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3861,6 +3861,7 @@ msgid "Game not implemented"
 msgstr "Id hry nen?? podporov??no"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
+#: engines/scumm/metaengine.cpp:1026
 #, fuzzy
 #| msgid "Default game keymap"
 msgid "Default game keymappings"
@@ -7335,7 +7336,7 @@ msgstr "Povolit zachycov??n?? my??i"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1090 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1102 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -8080,7 +8081,7 @@ msgstr "Interakce"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1077
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1089
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -13722,101 +13723,106 @@ msgstr ""
 msgid "All levels will be available without requiring passwords"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1025
+#: engines/scumm/metaengine.cpp:1037
 #, fuzzy
 msgid "Down left"
 msgstr "Oto??it se doleva"
 
-#: engines/scumm/metaengine.cpp:1038
+#: engines/scumm/metaengine.cpp:1050
 #, fuzzy
 msgid "Down right"
 msgstr "Oto??it se doprava"
 
-#: engines/scumm/metaengine.cpp:1058
+#: engines/scumm/metaengine.cpp:1070
 #, fuzzy
 #| msgid "Upper left item"
 msgid "Up left"
 msgstr "Polo??ka vlevo naho??e"
 
-#: engines/scumm/metaengine.cpp:1071
+#: engines/scumm/metaengine.cpp:1083
 #, fuzzy
 msgid "Up right"
 msgstr "Doprava"
 
-#: engines/scumm/metaengine.cpp:1083
+#: engines/scumm/metaengine.cpp:1095
 #, fuzzy
 msgid "Switch weapon"
 msgstr "M??n??n?? postav:"
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1097
+#: engines/scumm/metaengine.cpp:1109
 msgid "Win the bike fight cheat"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1106
+#: engines/scumm/metaengine.cpp:1118
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1108 engines/scumm/metaengine.cpp:1170
+#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1182
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1113 engines/scumm/metaengine.cpp:1175
+#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1187
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1118 engines/scumm/metaengine.cpp:1180
+#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1192
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1123 engines/scumm/metaengine.cpp:1185
+#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1197
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1128 engines/scumm/metaengine.cpp:1190
+#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "Sebrat"
 
-#: engines/scumm/metaengine.cpp:1134 engines/scumm/metaengine.cpp:1196
+#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1209
 #, fuzzy
 msgid "Stick down"
 msgstr "Posunout seznam dolu"
 
-#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1216
 #, fuzzy
 #| msgid "Slide Left"
 msgid "Stick left"
 msgstr "P??esunout se Doleva"
 
-#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1208
+#: engines/scumm/metaengine.cpp:1158 engines/scumm/metaengine.cpp:1223
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick right"
 msgstr "P??esunout se Doprava"
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1214
+#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1230
 #, fuzzy
 msgid "Fire / select"
 msgstr "P??esunout se doleva"
 
-#: engines/scumm/metaengine.cpp:1157
+#: engines/scumm/metaengine.cpp:1169
 #, fuzzy
 msgid "Back / skip"
 msgstr "Vzad"
 
-#: engines/scumm/metaengine.cpp:1168
+#: engines/scumm/metaengine.cpp:1180
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1219
+#: engines/scumm/metaengine.cpp:1235
 #, fuzzy
 #| msgid "Move Back"
 msgid "Cover / back"
 msgstr "Vzad"
 
-#: engines/scumm/metaengine.cpp:1224
+#: engines/scumm/metaengine.cpp:1241
+#, fuzzy
+msgid "Skip / back"
+msgstr "P??esko??it ????dek"
+
+#: engines/scumm/metaengine.cpp:1248
 #, fuzzy
 msgid "Skip / menu"
 msgstr "~N~??vrat do Spou??t????e"
diff --git a/po/da.po b/po/da.po
index 49dd91ad47b..4b3ebfe9c92 100644
--- a/po/da.po
+++ b/po/da.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 14:35+0000\n"
+"POT-Creation-Date: 2026-06-01 16:27+0000\n"
 "PO-Revision-Date: 2023-09-07 17:04+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Danish <https://translations.scummvm.org/projects/scummvm/"
@@ -1077,7 +1077,7 @@ msgstr "interagere"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1064
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1076
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1098,7 +1098,7 @@ msgstr "Op"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1031
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1043
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1119,7 +1119,7 @@ msgstr "Ned"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1044
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1056
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1139,7 +1139,7 @@ msgstr "Venstre"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1051
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1063
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3872,6 +3872,7 @@ msgid "Game not implemented"
 msgstr "Spil-id ikke underst??ttet"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
+#: engines/scumm/metaengine.cpp:1026
 #, fuzzy
 msgid "Default game keymappings"
 msgstr "~I~ndstillinger ..."
@@ -7362,7 +7363,7 @@ msgstr "Museindfangning til/fra"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1090 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1102 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -8107,7 +8108,7 @@ msgstr "interagere"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1077
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1089
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -13813,102 +13814,107 @@ msgstr ""
 msgid "All levels will be available without requiring passwords"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1025
+#: engines/scumm/metaengine.cpp:1037
 #, fuzzy
 msgid "Down left"
 msgstr "Drej til venstre"
 
-#: engines/scumm/metaengine.cpp:1038
+#: engines/scumm/metaengine.cpp:1050
 #, fuzzy
 msgid "Down right"
 msgstr "Drej til h??jre"
 
-#: engines/scumm/metaengine.cpp:1058
+#: engines/scumm/metaengine.cpp:1070
 #, fuzzy
 #| msgid "Upper left item"
 msgid "Up left"
 msgstr "??verste venstre punkt"
 
-#: engines/scumm/metaengine.cpp:1071
+#: engines/scumm/metaengine.cpp:1083
 #, fuzzy
 msgid "Up right"
 msgstr "H??jre"
 
-#: engines/scumm/metaengine.cpp:1083
+#: engines/scumm/metaengine.cpp:1095
 #, fuzzy
 msgid "Switch weapon"
 msgstr "Skifter karaktere:"
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1097
+#: engines/scumm/metaengine.cpp:1109
 msgid "Win the bike fight cheat"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1106
+#: engines/scumm/metaengine.cpp:1118
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1108 engines/scumm/metaengine.cpp:1170
+#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1182
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1113 engines/scumm/metaengine.cpp:1175
+#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1187
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1118 engines/scumm/metaengine.cpp:1180
+#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1192
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1123 engines/scumm/metaengine.cpp:1185
+#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1197
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1128 engines/scumm/metaengine.cpp:1190
+#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "Tag op"
 
-#: engines/scumm/metaengine.cpp:1134 engines/scumm/metaengine.cpp:1196
+#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1209
 #, fuzzy
 msgid "Stick down"
 msgstr "Rul liste ned"
 
-#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1216
 #, fuzzy
 #| msgid "Slide Left"
 msgid "Stick left"
 msgstr "Flyt til venstre"
 
-#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1208
+#: engines/scumm/metaengine.cpp:1158 engines/scumm/metaengine.cpp:1223
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick right"
 msgstr "Flyt til h??jre"
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1214
+#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1230
 #, fuzzy
 msgid "Fire / select"
 msgstr "Flyt til venstre"
 
-#: engines/scumm/metaengine.cpp:1157
+#: engines/scumm/metaengine.cpp:1169
 #, fuzzy
 #| msgid "Backend"
 msgid "Back / skip"
 msgstr "Backend"
 
-#: engines/scumm/metaengine.cpp:1168
+#: engines/scumm/metaengine.cpp:1180
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1219
+#: engines/scumm/metaengine.cpp:1235
 #, fuzzy
 #| msgid "Move Back"
 msgid "Cover / back"
 msgstr "Flyt bagud"
 
-#: engines/scumm/metaengine.cpp:1224
+#: engines/scumm/metaengine.cpp:1241
+#, fuzzy
+msgid "Skip / back"
+msgstr "Spring linje over"
+
+#: engines/scumm/metaengine.cpp:1248
 #, fuzzy
 msgid "Skip / menu"
 msgstr "~R~etur til oversigt"
diff --git a/po/de_DE.po b/po/de_DE.po
index 969bd0696d8..69df9f05da7 100644
--- a/po/de_DE.po
+++ b/po/de_DE.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.10.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 14:35+0000\n"
+"POT-Creation-Date: 2026-06-01 16:27+0000\n"
 "PO-Revision-Date: 2026-05-05 16:31+0000\n"
 "Last-Translator: Lothar Serra Mari <lothar.serramari at scummvm.org>\n"
 "Language-Team: German <https://translations.scummvm.org/projects/scummvm/"
@@ -1064,7 +1064,7 @@ msgstr "Interagieren"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1064
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1076
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1085,7 +1085,7 @@ msgstr "Hoch"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1031
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1043
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1106,7 +1106,7 @@ msgstr "Runter"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1044
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1056
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1126,7 +1126,7 @@ msgstr "Links"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1051
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1063
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3929,6 +3929,7 @@ msgid "Game not implemented"
 msgstr "Spiel nicht implementiert"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
+#: engines/scumm/metaengine.cpp:1026
 msgid "Default game keymappings"
 msgstr "Standard-Tastenbelegung f??r Spiele"
 
@@ -7626,7 +7627,7 @@ msgstr "Kampf umschalten"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1090 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1102 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -8293,7 +8294,7 @@ msgstr "Sprechen / Interagieren"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1077
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1089
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -13716,98 +13717,104 @@ msgstr ""
 msgid "All levels will be available without requiring passwords"
 msgstr "Alle Level k??nnen direkt gespielt werden."
 
-#: engines/scumm/metaengine.cpp:1025
+#: engines/scumm/metaengine.cpp:1037
 msgid "Down left"
 msgstr "Unten links"
 
-#: engines/scumm/metaengine.cpp:1038
+#: engines/scumm/metaengine.cpp:1050
 msgid "Down right"
 msgstr "Unten rechts"
 
-#: engines/scumm/metaengine.cpp:1058
+#: engines/scumm/metaengine.cpp:1070
 msgid "Up left"
 msgstr "Oben links"
 
-#: engines/scumm/metaengine.cpp:1071
+#: engines/scumm/metaengine.cpp:1083
 msgid "Up right"
 msgstr "Oben rechts"
 
-#: engines/scumm/metaengine.cpp:1083
+#: engines/scumm/metaengine.cpp:1095
 msgid "Switch weapon"
 msgstr "Waffe wechseln"
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1097
+#: engines/scumm/metaengine.cpp:1109
 msgid "Win the bike fight cheat"
 msgstr "Cheat: Bike-Kampf gewinnen"
 
-#: engines/scumm/metaengine.cpp:1106
+#: engines/scumm/metaengine.cpp:1118
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1108 engines/scumm/metaengine.cpp:1170
+#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1182
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1113 engines/scumm/metaengine.cpp:1175
+#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1187
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1118 engines/scumm/metaengine.cpp:1180
+#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1192
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1123 engines/scumm/metaengine.cpp:1185
+#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1197
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1128 engines/scumm/metaengine.cpp:1190
+#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "Nimm"
 
-#: engines/scumm/metaengine.cpp:1134 engines/scumm/metaengine.cpp:1196
+#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1209
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "Nach unten scrollen"
 
-#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1216
 #, fuzzy
 #| msgid "Slide left"
 msgid "Stick left"
 msgstr "Slide nach links"
 
-#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1208
+#: engines/scumm/metaengine.cpp:1158 engines/scumm/metaengine.cpp:1223
 #, fuzzy
 #| msgid "Slide right"
 msgid "Stick right"
 msgstr "Slide nach rechts"
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1214
+#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1230
 #, fuzzy
 #| msgid "Move / Select"
 msgid "Fire / select"
 msgstr "Bewegen / Ausw??hlen"
 
-#: engines/scumm/metaengine.cpp:1157
+#: engines/scumm/metaengine.cpp:1169
 #, fuzzy
 #| msgid "Backspace"
 msgid "Back / skip"
 msgstr "R??cktaste"
 
-#: engines/scumm/metaengine.cpp:1168
+#: engines/scumm/metaengine.cpp:1180
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1219
+#: engines/scumm/metaengine.cpp:1235
 #, fuzzy
 #| msgid "Move back"
 msgid "Cover / back"
 msgstr "Zur??ckgehen"
 
-#: engines/scumm/metaengine.cpp:1224
+#: engines/scumm/metaengine.cpp:1241
+#, fuzzy
+#| msgid "Skip walk"
+msgid "Skip / back"
+msgstr "Spaziergang ??berspringen"
+
+#: engines/scumm/metaengine.cpp:1248
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu"
diff --git a/po/el.po b/po/el.po
index 4d225495b85..675fc343881 100644
--- a/po/el.po
+++ b/po/el.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.10.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 14:35+0000\n"
+"POT-Creation-Date: 2026-06-01 16:27+0000\n"
 "PO-Revision-Date: 2026-04-23 00:22+0000\n"
 "Last-Translator: Antoniou Athanasios <a.antoniou79 at gmail.com>\n"
 "Language-Team: Greek <https://translations.scummvm.org/projects/scummvm/"
@@ -1067,7 +1067,7 @@ msgstr "??????????????????????????"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1064
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1076
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1088,7 +1088,7 @@ msgstr "????????"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1031
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1043
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1109,7 +1109,7 @@ msgstr "????????"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1044
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1056
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1129,7 +1129,7 @@ msgstr "????????????????"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1051
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1063
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -4000,6 +4000,7 @@ msgid "Game not implemented"
 msgstr "???? ???????????????? ?????? ???????? ????????????????????"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
+#: engines/scumm/metaengine.cpp:1026
 msgid "Default game keymappings"
 msgstr "?????????????? ???????????????????????????? ???????????????? ????????????????????"
 
@@ -7754,7 +7755,7 @@ msgstr "???????????????? ?????????????????????? ?????? ????????"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1090 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1102 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -8428,7 +8429,7 @@ msgstr "???????? / ??????????????????????????"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1077
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1089
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -13871,98 +13872,104 @@ msgstr ""
 msgid "All levels will be available without requiring passwords"
 msgstr "?????? ???? ?????????????? ???? ???????????? ?????????????????? ?????? ????????????????."
 
-#: engines/scumm/metaengine.cpp:1025
+#: engines/scumm/metaengine.cpp:1037
 msgid "Down left"
 msgstr "???????? ????????????????"
 
-#: engines/scumm/metaengine.cpp:1038
+#: engines/scumm/metaengine.cpp:1050
 msgid "Down right"
 msgstr "???????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1058
+#: engines/scumm/metaengine.cpp:1070
 msgid "Up left"
 msgstr "???????? ????????????????"
 
-#: engines/scumm/metaengine.cpp:1071
+#: engines/scumm/metaengine.cpp:1083
 msgid "Up right"
 msgstr "???????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1083
+#: engines/scumm/metaengine.cpp:1095
 msgid "Switch weapon"
 msgstr "???????????? ??????????"
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1097
+#: engines/scumm/metaengine.cpp:1109
 msgid "Win the bike fight cheat"
 msgstr "?????????? ?????? ???????? ?????? ???????? ???? ?????? ??????????????"
 
-#: engines/scumm/metaengine.cpp:1106
+#: engines/scumm/metaengine.cpp:1118
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1108 engines/scumm/metaengine.cpp:1170
+#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1182
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1113 engines/scumm/metaengine.cpp:1175
+#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1187
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1118 engines/scumm/metaengine.cpp:1180
+#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1192
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1123 engines/scumm/metaengine.cpp:1185
+#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1197
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1128 engines/scumm/metaengine.cpp:1190
+#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "????????????"
 
-#: engines/scumm/metaengine.cpp:1134 engines/scumm/metaengine.cpp:1196
+#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1209
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "???????????? ????????"
 
-#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1216
 #, fuzzy
 #| msgid "Slide left"
 msgid "Stick left"
 msgstr "???????????????? ????????????????"
 
-#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1208
+#: engines/scumm/metaengine.cpp:1158 engines/scumm/metaengine.cpp:1223
 #, fuzzy
 #| msgid "Slide right"
 msgid "Stick right"
 msgstr "???????????????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1214
+#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1230
 #, fuzzy
 #| msgid "Move / Select"
 msgid "Fire / select"
 msgstr "???????????? / ??????????????"
 
-#: engines/scumm/metaengine.cpp:1157
+#: engines/scumm/metaengine.cpp:1169
 #, fuzzy
 #| msgid "Backspace"
 msgid "Back / skip"
 msgstr "Backspace"
 
-#: engines/scumm/metaengine.cpp:1168
+#: engines/scumm/metaengine.cpp:1180
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1219
+#: engines/scumm/metaengine.cpp:1235
 #, fuzzy
 #| msgid "Move Back"
 msgid "Cover / back"
 msgstr "?????????????????? ????????"
 
-#: engines/scumm/metaengine.cpp:1224
+#: engines/scumm/metaengine.cpp:1241
+#, fuzzy
+#| msgid "Skip walk"
+msgid "Skip / back"
+msgstr "?????????????????? ??????????????????"
+
+#: engines/scumm/metaengine.cpp:1248
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu"
diff --git a/po/es_ES.po b/po/es_ES.po
index cfe179bacf1..2b1a9155039 100644
--- a/po/es_ES.po
+++ b/po/es_ES.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.4.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 14:35+0000\n"
+"POT-Creation-Date: 2026-06-01 16:27+0000\n"
 "PO-Revision-Date: 2026-06-01 09:35+0000\n"
 "Last-Translator: IlDucci <pajaroloco_2 at hotmail.com>\n"
 "Language-Team: Spanish <https://translations.scummvm.org/projects/scummvm/"
@@ -1052,7 +1052,7 @@ msgstr "Interactuar"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1064
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1076
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1073,7 +1073,7 @@ msgstr "Arriba"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1031
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1043
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1094,7 +1094,7 @@ msgstr "Abajo"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1044
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1056
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1114,7 +1114,7 @@ msgstr "Izquierda"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1051
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1063
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3932,6 +3932,7 @@ msgid "Game not implemented"
 msgstr "Juego no implementado"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
+#: engines/scumm/metaengine.cpp:1026
 msgid "Default game keymappings"
 msgstr "Asignaciones predet. del juego"
 
@@ -7641,7 +7642,7 @@ msgstr "Entrar/Salir del modo de combate"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1090 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1102 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -8310,7 +8311,7 @@ msgstr "Hablar/Interactuar"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1077
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1089
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -13689,84 +13690,90 @@ msgstr ""
 msgid "All levels will be available without requiring passwords"
 msgstr "Desbloquea todos los niveles sin necesitar contrase??as"
 
-#: engines/scumm/metaengine.cpp:1025
+#: engines/scumm/metaengine.cpp:1037
 msgid "Down left"
 msgstr "Bajar a la izquierda"
 
-#: engines/scumm/metaengine.cpp:1038
+#: engines/scumm/metaengine.cpp:1050
 msgid "Down right"
 msgstr "Bajar a la derecha"
 
-#: engines/scumm/metaengine.cpp:1058
+#: engines/scumm/metaengine.cpp:1070
 msgid "Up left"
 msgstr "Subir a la izquierda"
 
-#: engines/scumm/metaengine.cpp:1071
+#: engines/scumm/metaengine.cpp:1083
 msgid "Up right"
 msgstr "Subir a la derecha"
 
-#: engines/scumm/metaengine.cpp:1083
+#: engines/scumm/metaengine.cpp:1095
 msgid "Switch weapon"
 msgstr "Cambiar de arma"
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1097
+#: engines/scumm/metaengine.cpp:1109
 msgid "Win the bike fight cheat"
 msgstr "Truco para ganar pelea de motos"
 
-#: engines/scumm/metaengine.cpp:1106
+#: engines/scumm/metaengine.cpp:1118
 msgid "Rebel Assault controls"
 msgstr "Controles de Rebel Assault"
 
-#: engines/scumm/metaengine.cpp:1108 engines/scumm/metaengine.cpp:1170
+#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1182
 msgid "Aim up / menu up"
 msgstr "Apuntar arriba/Subir men??"
 
-#: engines/scumm/metaengine.cpp:1113 engines/scumm/metaengine.cpp:1175
+#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1187
 msgid "Aim down / menu down"
 msgstr "Apuntar abajo/Bajar men??"
 
-#: engines/scumm/metaengine.cpp:1118 engines/scumm/metaengine.cpp:1180
+#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1192
 msgid "Aim left / menu left"
 msgstr "Apuntar a izda./Men?? a izda."
 
-#: engines/scumm/metaengine.cpp:1123 engines/scumm/metaengine.cpp:1185
+#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1197
 msgid "Aim right / menu right"
 msgstr "Apuntar a dcha./Men?? a dcha."
 
-#: engines/scumm/metaengine.cpp:1128 engines/scumm/metaengine.cpp:1190
+#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
 msgid "Stick up"
 msgstr "Stick hacia arriba"
 
-#: engines/scumm/metaengine.cpp:1134 engines/scumm/metaengine.cpp:1196
+#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1209
 msgid "Stick down"
 msgstr "Stick hacia abajo"
 
-#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1216
 msgid "Stick left"
 msgstr "Stick hacia la izquierda"
 
-#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1208
+#: engines/scumm/metaengine.cpp:1158 engines/scumm/metaengine.cpp:1223
 msgid "Stick right"
 msgstr "Stick hacia la derecha"
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1214
+#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1230
 msgid "Fire / select"
 msgstr "Disparar/Seleccionar"
 
-#: engines/scumm/metaengine.cpp:1157
+#: engines/scumm/metaengine.cpp:1169
 msgid "Back / skip"
 msgstr "Volver/Omitir"
 
-#: engines/scumm/metaengine.cpp:1168
+#: engines/scumm/metaengine.cpp:1180
 msgid "Rebel Assault II controls"
 msgstr "Controles de Rebel Assault II"
 
-#: engines/scumm/metaengine.cpp:1219
+#: engines/scumm/metaengine.cpp:1235
 msgid "Cover / back"
 msgstr "Cubrirse/Volver"
 
-#: engines/scumm/metaengine.cpp:1224
+#: engines/scumm/metaengine.cpp:1241
+#, fuzzy
+#| msgid "Skip walk"
+msgid "Skip / back"
+msgstr "Omitir desplazamiento"
+
+#: engines/scumm/metaengine.cpp:1248
 msgid "Skip / menu"
 msgstr "Omitir/Men??"
 
diff --git a/po/eu.po b/po/eu.po
index 455147ba79c..bd87d7eae86 100644
--- a/po/eu.po
+++ b/po/eu.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.5.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 14:35+0000\n"
+"POT-Creation-Date: 2026-06-01 16:27+0000\n"
 "PO-Revision-Date: 2024-02-05 18:42+0000\n"
 "Last-Translator: Josu Igoa <josuigoa at ni.eus>\n"
 "Language-Team: Basque <https://translations.scummvm.org/projects/scummvm/"
@@ -1044,7 +1044,7 @@ msgstr "Elkarreragin"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1064
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1076
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1065,7 +1065,7 @@ msgstr "Gora"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1031
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1043
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1086,7 +1086,7 @@ msgstr "Behera"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1044
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1056
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1106,7 +1106,7 @@ msgstr "Ezker"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1051
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1063
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3898,6 +3898,7 @@ msgid "Game not implemented"
 msgstr "Jokoaren ID ez-onartua"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
+#: engines/scumm/metaengine.cpp:1026
 #, fuzzy
 msgid "Default game keymappings"
 msgstr "~A~ukerak..."
@@ -7408,7 +7409,7 @@ msgstr "Mututu/Ez-mututu"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1090 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1102 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -8155,7 +8156,7 @@ msgstr "Elkarreragin"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1077
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1089
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -13780,102 +13781,107 @@ msgstr ""
 msgid "All levels will be available without requiring passwords"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1025
+#: engines/scumm/metaengine.cpp:1037
 #, fuzzy
 msgid "Down left"
 msgstr "Ezkerrera biratu"
 
-#: engines/scumm/metaengine.cpp:1038
+#: engines/scumm/metaengine.cpp:1050
 #, fuzzy
 msgid "Down right"
 msgstr "Eskuinera biratu"
 
-#: engines/scumm/metaengine.cpp:1058
+#: engines/scumm/metaengine.cpp:1070
 #, fuzzy
 #| msgid "Upper left item"
 msgid "Up left"
 msgstr "Goiko ezkerreko objektua"
 
-#: engines/scumm/metaengine.cpp:1071
+#: engines/scumm/metaengine.cpp:1083
 #, fuzzy
 msgid "Up right"
 msgstr "Eskuin"
 
-#: engines/scumm/metaengine.cpp:1083
+#: engines/scumm/metaengine.cpp:1095
 #, fuzzy
 msgid "Switch weapon"
 msgstr "Pertsonaia aldatu:"
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1097
+#: engines/scumm/metaengine.cpp:1109
 msgid "Win the bike fight cheat"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1106
+#: engines/scumm/metaengine.cpp:1118
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1108 engines/scumm/metaengine.cpp:1170
+#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1182
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1113 engines/scumm/metaengine.cpp:1175
+#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1187
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1118 engines/scumm/metaengine.cpp:1180
+#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1192
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1123 engines/scumm/metaengine.cpp:1185
+#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1197
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1128 engines/scumm/metaengine.cpp:1190
+#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "Jaso"
 
-#: engines/scumm/metaengine.cpp:1134 engines/scumm/metaengine.cpp:1196
+#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1209
 #, fuzzy
 msgid "Stick down"
 msgstr "Behera"
 
-#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1216
 #, fuzzy
 #| msgid "Slide Left"
 msgid "Stick left"
 msgstr "Ezkerrera irristatu"
 
-#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1208
+#: engines/scumm/metaengine.cpp:1158 engines/scumm/metaengine.cpp:1223
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick right"
 msgstr "Eskuinera irristatu"
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1214
+#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1230
 #, fuzzy
 msgid "Fire / select"
 msgstr "Ezkerrera irristatu"
 
-#: engines/scumm/metaengine.cpp:1157
+#: engines/scumm/metaengine.cpp:1169
 #, fuzzy
 #| msgid "Backspace"
 msgid "Back / skip"
 msgstr "Ezabatu"
 
-#: engines/scumm/metaengine.cpp:1168
+#: engines/scumm/metaengine.cpp:1180
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1219
+#: engines/scumm/metaengine.cpp:1235
 #, fuzzy
 #| msgid "Move Back"
 msgid "Cover / back"
 msgstr "Atzera mugitu"
 
-#: engines/scumm/metaengine.cpp:1224
+#: engines/scumm/metaengine.cpp:1241
+#, fuzzy
+msgid "Skip / back"
+msgstr "Lerroa saltatu"
+
+#: engines/scumm/metaengine.cpp:1248
 #, fuzzy
 msgid "Skip / menu"
 msgstr "It~z~uli abiarazlera"
diff --git a/po/fi_FI.po b/po/fi_FI.po
index bfe0a4b6747..a3b3b5b5e4b 100644
--- a/po/fi_FI.po
+++ b/po/fi_FI.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.6.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 14:35+0000\n"
+"POT-Creation-Date: 2026-06-01 16:27+0000\n"
 "PO-Revision-Date: 2026-02-07 15:22+0000\n"
 "Last-Translator: Timo Mikkolainen <tmikkola at gmail.com>\n"
 "Language-Team: Finnish <https://translations.scummvm.org/projects/scummvm/"
@@ -1052,7 +1052,7 @@ msgstr "K??yt??"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1064
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1076
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1073,7 +1073,7 @@ msgstr "Yl??s"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1031
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1043
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1094,7 +1094,7 @@ msgstr "Alas"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1044
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1056
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1114,7 +1114,7 @@ msgstr "Vasen"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1051
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1063
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3950,6 +3950,7 @@ msgid "Game not implemented"
 msgstr "Pelill?? ei ole toteutusta"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
+#: engines/scumm/metaengine.cpp:1026
 #, fuzzy
 #| msgid "Default game keymap"
 msgid "Default game keymappings"
@@ -7724,7 +7725,7 @@ msgstr "Taistelumoodi p????lle / pois"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1090 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1102 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -8422,7 +8423,7 @@ msgstr "Puhu / Vuorovaikuta"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1077
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1089
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -13916,98 +13917,104 @@ msgstr ""
 msgid "All levels will be available without requiring passwords"
 msgstr "Kaikki tasot ovat pelattavissa."
 
-#: engines/scumm/metaengine.cpp:1025
+#: engines/scumm/metaengine.cpp:1037
 msgid "Down left"
 msgstr "Alavasemmalle"
 
-#: engines/scumm/metaengine.cpp:1038
+#: engines/scumm/metaengine.cpp:1050
 msgid "Down right"
 msgstr "Alaoikealle"
 
-#: engines/scumm/metaengine.cpp:1058
+#: engines/scumm/metaengine.cpp:1070
 msgid "Up left"
 msgstr "Yl??vasemmalle"
 
-#: engines/scumm/metaengine.cpp:1071
+#: engines/scumm/metaengine.cpp:1083
 msgid "Up right"
 msgstr "Yl??oikealle"
 
-#: engines/scumm/metaengine.cpp:1083
+#: engines/scumm/metaengine.cpp:1095
 msgid "Switch weapon"
 msgstr "Vaihda asetta"
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1097
+#: engines/scumm/metaengine.cpp:1109
 msgid "Win the bike fight cheat"
 msgstr "Voita moottoripy??r??taistelu-huijaus"
 
-#: engines/scumm/metaengine.cpp:1106
+#: engines/scumm/metaengine.cpp:1118
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1108 engines/scumm/metaengine.cpp:1170
+#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1182
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1113 engines/scumm/metaengine.cpp:1175
+#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1187
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1118 engines/scumm/metaengine.cpp:1180
+#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1192
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1123 engines/scumm/metaengine.cpp:1185
+#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1197
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1128 engines/scumm/metaengine.cpp:1190
+#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "Ota"
 
-#: engines/scumm/metaengine.cpp:1134 engines/scumm/metaengine.cpp:1196
+#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1209
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "Vierit?? alas"
 
-#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1216
 #, fuzzy
 #| msgid "Slide left"
 msgid "Stick left"
 msgstr "Liu'u vasemmalle"
 
-#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1208
+#: engines/scumm/metaengine.cpp:1158 engines/scumm/metaengine.cpp:1223
 #, fuzzy
 #| msgid "Slide right"
 msgid "Stick right"
 msgstr "Liu'u oikealle"
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1214
+#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1230
 #, fuzzy
 #| msgid "Move / Select"
 msgid "Fire / select"
 msgstr "Liiku / Valitse"
 
-#: engines/scumm/metaengine.cpp:1157
+#: engines/scumm/metaengine.cpp:1169
 #, fuzzy
 #| msgid "Backspace"
 msgid "Back / skip"
 msgstr "Askelpalautin"
 
-#: engines/scumm/metaengine.cpp:1168
+#: engines/scumm/metaengine.cpp:1180
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1219
+#: engines/scumm/metaengine.cpp:1235
 #, fuzzy
 #| msgid "Move back"
 msgid "Cover / back"
 msgstr "Liiku taaksep??in"
 
-#: engines/scumm/metaengine.cpp:1224
+#: engines/scumm/metaengine.cpp:1241
+#, fuzzy
+#| msgid "Skip walk"
+msgid "Skip / back"
+msgstr "Ohita k??vely"
+
+#: engines/scumm/metaengine.cpp:1248
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu"
diff --git a/po/fr_FR.po b/po/fr_FR.po
index f106cacfcda..ec931d56349 100644
--- a/po/fr_FR.po
+++ b/po/fr_FR.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 14:35+0000\n"
+"POT-Creation-Date: 2026-06-01 16:27+0000\n"
 "PO-Revision-Date: 2026-06-01 09:35+0000\n"
 "Last-Translator: Purple T <ZEONK at hotmail.com>\n"
 "Language-Team: French <https://translations.scummvm.org/projects/scummvm/"
@@ -1063,7 +1063,7 @@ msgstr "Interagir"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1064
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1076
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1084,7 +1084,7 @@ msgstr "Haut"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1031
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1043
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1105,7 +1105,7 @@ msgstr "Bas"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1044
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1056
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1125,7 +1125,7 @@ msgstr "Gauche"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1051
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1063
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3955,6 +3955,7 @@ msgid "Game not implemented"
 msgstr "Jeu non impl??ment??"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
+#: engines/scumm/metaengine.cpp:1026
 msgid "Default game keymappings"
 msgstr "Conf. de touches par d??faut"
 
@@ -7646,7 +7647,7 @@ msgstr "Act. / D??sact. combat"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1090 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1102 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -8310,7 +8311,7 @@ msgstr "Parler / Interagir"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1077
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1089
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -13714,84 +13715,90 @@ msgstr "Lance le jeu en 640x400 haute r??solution au lieu de 320x200"
 msgid "All levels will be available without requiring passwords"
 msgstr "Tous les niveaux seront disponibles sans demander de mots de passe"
 
-#: engines/scumm/metaengine.cpp:1025
+#: engines/scumm/metaengine.cpp:1037
 msgid "Down left"
 msgstr "Bas gauche"
 
-#: engines/scumm/metaengine.cpp:1038
+#: engines/scumm/metaengine.cpp:1050
 msgid "Down right"
 msgstr "Bas droite"
 
-#: engines/scumm/metaengine.cpp:1058
+#: engines/scumm/metaengine.cpp:1070
 msgid "Up left"
 msgstr "Haut gauche"
 
-#: engines/scumm/metaengine.cpp:1071
+#: engines/scumm/metaengine.cpp:1083
 msgid "Up right"
 msgstr "Haut droite"
 
-#: engines/scumm/metaengine.cpp:1083
+#: engines/scumm/metaengine.cpp:1095
 msgid "Switch weapon"
 msgstr "Changer d'arme"
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1097
+#: engines/scumm/metaengine.cpp:1109
 msgid "Win the bike fight cheat"
 msgstr "Gagner les bagarres en b??cane (cheat)"
 
-#: engines/scumm/metaengine.cpp:1106
+#: engines/scumm/metaengine.cpp:1118
 msgid "Rebel Assault controls"
 msgstr "Contr??les Rebel Assault"
 
-#: engines/scumm/metaengine.cpp:1108 engines/scumm/metaengine.cpp:1170
+#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1182
 msgid "Aim up / menu up"
 msgstr "Vis??e haut / Menu haut"
 
-#: engines/scumm/metaengine.cpp:1113 engines/scumm/metaengine.cpp:1175
+#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1187
 msgid "Aim down / menu down"
 msgstr "Vis??e bas / Menu bas"
 
-#: engines/scumm/metaengine.cpp:1118 engines/scumm/metaengine.cpp:1180
+#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1192
 msgid "Aim left / menu left"
 msgstr "Vis??e gauche / menu gauche"
 
-#: engines/scumm/metaengine.cpp:1123 engines/scumm/metaengine.cpp:1185
+#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1197
 msgid "Aim right / menu right"
 msgstr "Vis??e droite / Menu droite"
 
-#: engines/scumm/metaengine.cpp:1128 engines/scumm/metaengine.cpp:1190
+#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
 msgid "Stick up"
 msgstr "Manche haut"
 
-#: engines/scumm/metaengine.cpp:1134 engines/scumm/metaengine.cpp:1196
+#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1209
 msgid "Stick down"
 msgstr "Manche bas"
 
-#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1216
 msgid "Stick left"
 msgstr "Manche gauche"
 
-#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1208
+#: engines/scumm/metaengine.cpp:1158 engines/scumm/metaengine.cpp:1223
 msgid "Stick right"
 msgstr "Manche droite"
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1214
+#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1230
 msgid "Fire / select"
 msgstr "Tirer / S??lectionner"
 
-#: engines/scumm/metaengine.cpp:1157
+#: engines/scumm/metaengine.cpp:1169
 msgid "Back / skip"
 msgstr "Retour / passer"
 
-#: engines/scumm/metaengine.cpp:1168
+#: engines/scumm/metaengine.cpp:1180
 msgid "Rebel Assault II controls"
 msgstr "Contr??les Rebel Assault II"
 
-#: engines/scumm/metaengine.cpp:1219
+#: engines/scumm/metaengine.cpp:1235
 msgid "Cover / back"
 msgstr "Couverture / retour"
 
-#: engines/scumm/metaengine.cpp:1224
+#: engines/scumm/metaengine.cpp:1241
+#, fuzzy
+#| msgid "Skip walk"
+msgid "Skip / back"
+msgstr "Passer le d??placement"
+
+#: engines/scumm/metaengine.cpp:1248
 msgid "Skip / menu"
 msgstr "Passer / menu"
 
diff --git a/po/gl_ES.po b/po/gl_ES.po
index bdf9f428f07..76ff944e273 100644
--- a/po/gl_ES.po
+++ b/po/gl_ES.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 14:35+0000\n"
+"POT-Creation-Date: 2026-06-01 16:27+0000\n"
 "PO-Revision-Date: 2020-09-26 21:13+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Galician <https://translations.scummvm.org/projects/scummvm/"
@@ -1070,7 +1070,7 @@ msgstr "Interpolaci??n:"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1064
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1076
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1091,7 +1091,7 @@ msgstr "Arriba"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1031
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1043
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1112,7 +1112,7 @@ msgstr "Abaixo"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1044
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1056
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1132,7 +1132,7 @@ msgstr "Esquerda"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1051
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1063
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3859,6 +3859,7 @@ msgid "Game not implemented"
 msgstr "ID de xogo non compatible"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
+#: engines/scumm/metaengine.cpp:1026
 #, fuzzy
 msgid "Default game keymappings"
 msgstr "~O~pci??ns..."
@@ -7341,7 +7342,7 @@ msgstr "Activar/desactivar captura de rato"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1090 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1102 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -8087,7 +8088,7 @@ msgstr "Interpolaci??n:"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1077
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1089
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -13736,101 +13737,106 @@ msgstr ""
 msgid "All levels will be available without requiring passwords"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1025
+#: engines/scumm/metaengine.cpp:1037
 #, fuzzy
 msgid "Down left"
 msgstr "Xirar ?? esquerda"
 
-#: engines/scumm/metaengine.cpp:1038
+#: engines/scumm/metaengine.cpp:1050
 #, fuzzy
 msgid "Down right"
 msgstr "Xirar ?? dereita"
 
-#: engines/scumm/metaengine.cpp:1058
+#: engines/scumm/metaengine.cpp:1070
 #, fuzzy
 #| msgid "Upper left item"
 msgid "Up left"
 msgstr "Obxecto esquerdo arriba"
 
-#: engines/scumm/metaengine.cpp:1071
+#: engines/scumm/metaengine.cpp:1083
 #, fuzzy
 msgid "Up right"
 msgstr "Dereita"
 
-#: engines/scumm/metaengine.cpp:1083
+#: engines/scumm/metaengine.cpp:1095
 #, fuzzy
 msgid "Switch weapon"
 msgstr "Cambiar personaxes:"
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1097
+#: engines/scumm/metaengine.cpp:1109
 msgid "Win the bike fight cheat"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1106
+#: engines/scumm/metaengine.cpp:1118
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1108 engines/scumm/metaengine.cpp:1170
+#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1182
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1113 engines/scumm/metaengine.cpp:1175
+#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1187
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1118 engines/scumm/metaengine.cpp:1180
+#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1192
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1123 engines/scumm/metaengine.cpp:1185
+#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1197
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1128 engines/scumm/metaengine.cpp:1190
+#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "Coller"
 
-#: engines/scumm/metaengine.cpp:1134 engines/scumm/metaengine.cpp:1196
+#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1209
 #, fuzzy
 msgid "Stick down"
 msgstr "Baixar lista"
 
-#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1216
 #, fuzzy
 #| msgid "Slide Left"
 msgid "Stick left"
 msgstr "Esvarar ?? esquerda"
 
-#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1208
+#: engines/scumm/metaengine.cpp:1158 engines/scumm/metaengine.cpp:1223
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick right"
 msgstr "Esvarar ?? dereita"
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1214
+#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1230
 #, fuzzy
 msgid "Fire / select"
 msgstr "Mover ?? esquerda"
 
-#: engines/scumm/metaengine.cpp:1157
+#: engines/scumm/metaengine.cpp:1169
 #, fuzzy
 msgid "Back / skip"
 msgstr "Mover cara atr??s"
 
-#: engines/scumm/metaengine.cpp:1168
+#: engines/scumm/metaengine.cpp:1180
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1219
+#: engines/scumm/metaengine.cpp:1235
 #, fuzzy
 #| msgid "Move Back"
 msgid "Cover / back"
 msgstr "Mover cara atr??s"
 
-#: engines/scumm/metaengine.cpp:1224
+#: engines/scumm/metaengine.cpp:1241
+#, fuzzy
+msgid "Skip / back"
+msgstr "Omitir li??a"
+
+#: engines/scumm/metaengine.cpp:1248
 #, fuzzy
 msgid "Skip / menu"
 msgstr "~V~olver ao Iniciador"
diff --git a/po/he.po b/po/he.po
index 5561c8404a5..7016c58febe 100644
--- a/po/he.po
+++ b/po/he.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.1.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 14:35+0000\n"
+"POT-Creation-Date: 2026-06-01 16:27+0000\n"
 "PO-Revision-Date: 2025-04-09 20:18+0000\n"
 "Last-Translator: Niv Baehr <bloop93 at gmail.com>\n"
 "Language-Team: Hebrew <https://translations.scummvm.org/projects/scummvm/"
@@ -1033,7 +1033,7 @@ msgstr "??????????"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1064
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1076
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1054,7 +1054,7 @@ msgstr "??????????"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1031
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1043
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1075,7 +1075,7 @@ msgstr "????????"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1044
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1056
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1095,7 +1095,7 @@ msgstr "??????????"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1051
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1063
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3807,6 +3807,7 @@ msgid "Game not implemented"
 msgstr "???????? ?????????? ???????? ????????"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
+#: engines/scumm/metaengine.cpp:1026
 #, fuzzy
 #| msgid "Default game keymap"
 msgid "Default game keymappings"
@@ -7324,7 +7325,7 @@ msgstr "?????????? ??????????"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1090 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1102 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -8086,7 +8087,7 @@ msgstr "????????????????????"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1077
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1089
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -13958,103 +13959,109 @@ msgstr ""
 msgid "All levels will be available without requiring passwords"
 msgstr "???? ???????????? ???????? ???????????? ??????????."
 
-#: engines/scumm/metaengine.cpp:1025
+#: engines/scumm/metaengine.cpp:1037
 #, fuzzy
 msgid "Down left"
 msgstr "???????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1038
+#: engines/scumm/metaengine.cpp:1050
 #, fuzzy
 msgid "Down right"
 msgstr "?????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1058
+#: engines/scumm/metaengine.cpp:1070
 #, fuzzy
 #| msgid "Upper left item"
 msgid "Up left"
 msgstr "???????? ?????????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1071
+#: engines/scumm/metaengine.cpp:1083
 #, fuzzy
 msgid "Up right"
 msgstr "??????????"
 
-#: engines/scumm/metaengine.cpp:1083
+#: engines/scumm/metaengine.cpp:1095
 msgid "Switch weapon"
 msgstr "?????????? ??????"
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1097
+#: engines/scumm/metaengine.cpp:1109
 msgid "Win the bike fight cheat"
 msgstr "?????????? ?????????????? ???????? ????????????????"
 
-#: engines/scumm/metaengine.cpp:1106
+#: engines/scumm/metaengine.cpp:1118
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1108 engines/scumm/metaengine.cpp:1170
+#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1182
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1113 engines/scumm/metaengine.cpp:1175
+#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1187
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1118 engines/scumm/metaengine.cpp:1180
+#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1192
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1123 engines/scumm/metaengine.cpp:1185
+#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1197
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1128 engines/scumm/metaengine.cpp:1190
+#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "??????????"
 
-#: engines/scumm/metaengine.cpp:1134 engines/scumm/metaengine.cpp:1196
+#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1209
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "?????????? ??????"
 
-#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1216
 #, fuzzy
 #| msgid "Slide Left"
 msgid "Stick left"
 msgstr "???????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1208
+#: engines/scumm/metaengine.cpp:1158 engines/scumm/metaengine.cpp:1223
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick right"
 msgstr "???????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1214
+#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1230
 #, fuzzy
 #| msgid "Move left"
 msgid "Fire / select"
 msgstr "?????????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1157
+#: engines/scumm/metaengine.cpp:1169
 #, fuzzy
 #| msgid "Backend"
 msgid "Back / skip"
 msgstr "??????????"
 
-#: engines/scumm/metaengine.cpp:1168
+#: engines/scumm/metaengine.cpp:1180
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1219
+#: engines/scumm/metaengine.cpp:1235
 #, fuzzy
 #| msgid "Move Back"
 msgid "Cover / back"
 msgstr "??????????"
 
-#: engines/scumm/metaengine.cpp:1224
+#: engines/scumm/metaengine.cpp:1241
+#, fuzzy
+#| msgid "Skip dialogue"
+msgid "Skip / back"
+msgstr "?????????? ???? ????????"
+
+#: engines/scumm/metaengine.cpp:1248
 #, fuzzy
 #| msgid "Open main menu"
 msgid "Skip / menu"
diff --git a/po/hi.po b/po/hi.po
index afe12a7051d..91e6b41a508 100644
--- a/po/hi.po
+++ b/po/hi.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 14:35+0000\n"
+"POT-Creation-Date: 2026-06-01 16:27+0000\n"
 "PO-Revision-Date: 2026-05-12 22:22+0000\n"
 "Last-Translator: KARTIK SAXENA <kartiksaxena2005 at gmail.com>\n"
 "Language-Team: Hindi <https://translations.scummvm.org/projects/scummvm/"
@@ -1042,7 +1042,7 @@ msgstr ""
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1064
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1076
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1063,7 +1063,7 @@ msgstr "?????????"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1031
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1043
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1084,7 +1084,7 @@ msgstr "????????????"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1044
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1056
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1104,7 +1104,7 @@ msgstr "????????????"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1051
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1063
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3734,6 +3734,7 @@ msgid "Game not implemented"
 msgstr ""
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
+#: engines/scumm/metaengine.cpp:1026
 #, fuzzy
 #| msgid "Game Options"
 msgid "Default game keymappings"
@@ -7058,7 +7059,7 @@ msgstr "???????????????????????? ?????????"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1090 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1102 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -7779,7 +7780,7 @@ msgstr "???????????? ??????????????? ????????????????????????"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1077
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1089
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -13213,103 +13214,109 @@ msgstr ""
 msgid "All levels will be available without requiring passwords"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1025
+#: engines/scumm/metaengine.cpp:1037
 #, fuzzy
 #| msgid "Down Left"
 msgid "Down left"
 msgstr "???????????? ??????????????????"
 
-#: engines/scumm/metaengine.cpp:1038
+#: engines/scumm/metaengine.cpp:1050
 #, fuzzy
 #| msgid "Down Right"
 msgid "Down right"
 msgstr "??????????????? ??????????????????"
 
-#: engines/scumm/metaengine.cpp:1058
+#: engines/scumm/metaengine.cpp:1070
 #, fuzzy
 #| msgid "Move Left"
 msgid "Up left"
 msgstr "???????????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1071
+#: engines/scumm/metaengine.cpp:1083
 #, fuzzy
 #| msgid "Up Right"
 msgid "Up right"
 msgstr "????????????"
 
-#: engines/scumm/metaengine.cpp:1083
+#: engines/scumm/metaengine.cpp:1095
 msgid "Switch weapon"
 msgstr ""
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1097
+#: engines/scumm/metaengine.cpp:1109
 msgid "Win the bike fight cheat"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1106
+#: engines/scumm/metaengine.cpp:1118
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1108 engines/scumm/metaengine.cpp:1170
+#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1182
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1113 engines/scumm/metaengine.cpp:1175
+#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1187
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1118 engines/scumm/metaengine.cpp:1180
+#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1192
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1123 engines/scumm/metaengine.cpp:1185
+#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1197
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1128 engines/scumm/metaengine.cpp:1190
+#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
 msgid "Stick up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1134 engines/scumm/metaengine.cpp:1196
+#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1209
 #, fuzzy
 msgid "Stick down"
 msgstr "???????????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1216
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick left"
 msgstr "?????????????????? ?????????????????? ?????????"
 
-#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1208
+#: engines/scumm/metaengine.cpp:1158 engines/scumm/metaengine.cpp:1223
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick right"
 msgstr "?????????????????? ?????????????????? ?????????"
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1214
+#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1230
 #, fuzzy
 #| msgid "Move Left"
 msgid "Fire / select"
 msgstr "???????????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1157
+#: engines/scumm/metaengine.cpp:1169
 #, fuzzy
 #| msgid "Backspace"
 msgid "Back / skip"
 msgstr "????????????????????????"
 
-#: engines/scumm/metaengine.cpp:1168
+#: engines/scumm/metaengine.cpp:1180
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1219
+#: engines/scumm/metaengine.cpp:1235
 #, fuzzy
 #| msgid "Move Forward"
 msgid "Cover / back"
 msgstr "????????? ?????????"
 
-#: engines/scumm/metaengine.cpp:1224
+#: engines/scumm/metaengine.cpp:1241
+#, fuzzy
+#| msgid "Show hidden files"
+msgid "Skip / back"
+msgstr "???????????? ????????? ?????????????????? ?????????????????????"
+
+#: engines/scumm/metaengine.cpp:1248
 #, fuzzy
 #| msgid "Skip autosave"
 msgid "Skip / menu"
diff --git a/po/hu_HU.po b/po/hu_HU.po
index 243e4072ead..1573018cec3 100644
--- a/po/hu_HU.po
+++ b/po/hu_HU.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 14:35+0000\n"
+"POT-Creation-Date: 2026-06-01 16:27+0000\n"
 "PO-Revision-Date: 2025-03-07 00:18+0000\n"
 "Last-Translator: Hov??nszki Tam??s <gahex68379 at lofiey.com>\n"
 "Language-Team: Hungarian <https://translations.scummvm.org/projects/scummvm/"
@@ -1053,7 +1053,7 @@ msgstr "Interakci??"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1064
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1076
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1074,7 +1074,7 @@ msgstr "Fel"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1031
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1043
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1095,7 +1095,7 @@ msgstr "Le"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1044
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1056
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1115,7 +1115,7 @@ msgstr "Bal"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1051
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1063
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3961,6 +3961,7 @@ msgid "Game not implemented"
 msgstr "A j??t??k m??g nem ker??lt implement??l??sra"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
+#: engines/scumm/metaengine.cpp:1026
 #, fuzzy
 #| msgid "Default game keymap"
 msgid "Default game keymappings"
@@ -7843,7 +7844,7 @@ msgstr "Harcmodor V??lt??s"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1090 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1102 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -8605,7 +8606,7 @@ msgstr "Besz??l??s/Interakci??"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1077
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1089
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -14415,106 +14416,112 @@ msgstr ""
 msgid "All levels will be available without requiring passwords"
 msgstr "Minden p??lya el??rhet??v?? v??lik a j??t??kos sz??m??ra."
 
-#: engines/scumm/metaengine.cpp:1025
+#: engines/scumm/metaengine.cpp:1037
 #, fuzzy
 #| msgid "Down Left"
 msgid "Down left"
 msgstr "Balra Le"
 
-#: engines/scumm/metaengine.cpp:1038
+#: engines/scumm/metaengine.cpp:1050
 #, fuzzy
 #| msgid "Down Right"
 msgid "Down right"
 msgstr "Jobbra Le"
 
-#: engines/scumm/metaengine.cpp:1058
+#: engines/scumm/metaengine.cpp:1070
 #, fuzzy
 #| msgid "Upper left item"
 msgid "Up left"
 msgstr "Bal fels?? t??rgy"
 
-#: engines/scumm/metaengine.cpp:1071
+#: engines/scumm/metaengine.cpp:1083
 #, fuzzy
 #| msgid "Up Right"
 msgid "Up right"
 msgstr "Jobbra Fel"
 
-#: engines/scumm/metaengine.cpp:1083
+#: engines/scumm/metaengine.cpp:1095
 msgid "Switch weapon"
 msgstr "Fegyver v??lt??s"
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1097
+#: engines/scumm/metaengine.cpp:1109
 msgid "Win the bike fight cheat"
 msgstr "Csal??s a motoros harc megnyer??s??hez"
 
-#: engines/scumm/metaengine.cpp:1106
+#: engines/scumm/metaengine.cpp:1118
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1108 engines/scumm/metaengine.cpp:1170
+#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1182
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1113 engines/scumm/metaengine.cpp:1175
+#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1187
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1118 engines/scumm/metaengine.cpp:1180
+#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1192
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1123 engines/scumm/metaengine.cpp:1185
+#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1197
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1128 engines/scumm/metaengine.cpp:1190
+#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "Felvesz"
 
-#: engines/scumm/metaengine.cpp:1134 engines/scumm/metaengine.cpp:1196
+#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1209
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "G??rget??s LE"
 
-#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1216
 #, fuzzy
 #| msgid "Slide Left"
 msgid "Stick left"
 msgstr "Cs??sz??s balra"
 
-#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1208
+#: engines/scumm/metaengine.cpp:1158 engines/scumm/metaengine.cpp:1223
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick right"
 msgstr "Cs??sz??s jobbra"
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1214
+#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1230
 #, fuzzy
 #| msgid "Move left"
 msgid "Fire / select"
 msgstr "Mozg??s balra"
 
-#: engines/scumm/metaengine.cpp:1157
+#: engines/scumm/metaengine.cpp:1169
 #, fuzzy
 #| msgid "Backspace"
 msgid "Back / skip"
 msgstr "T??rl??sgomb"
 
-#: engines/scumm/metaengine.cpp:1168
+#: engines/scumm/metaengine.cpp:1180
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1219
+#: engines/scumm/metaengine.cpp:1235
 #, fuzzy
 #| msgid "Move Back"
 msgid "Cover / back"
 msgstr "Mozg??s h??tra"
 
-#: engines/scumm/metaengine.cpp:1224
+#: engines/scumm/metaengine.cpp:1241
+#, fuzzy
+#| msgid "Skip dialogue"
+msgid "Skip / back"
+msgstr "Dial??gus ??tugr??sa"
+
+#: engines/scumm/metaengine.cpp:1248
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu"
diff --git a/po/it_IT.po b/po/it_IT.po
index f68a94966b4..71144ac81a9 100644
--- a/po/it_IT.po
+++ b/po/it_IT.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 14:35+0000\n"
+"POT-Creation-Date: 2026-06-01 16:27+0000\n"
 "PO-Revision-Date: 2026-05-25 15:48+0000\n"
 "Last-Translator: Walter Agazzi <walter.agazzi at protonmail.com>\n"
 "Language-Team: Italian <https://translations.scummvm.org/projects/scummvm/"
@@ -1056,7 +1056,7 @@ msgstr "Interazione"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1064
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1076
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1077,7 +1077,7 @@ msgstr "Su"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1031
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1043
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1098,7 +1098,7 @@ msgstr "Gi??"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1044
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1056
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1118,7 +1118,7 @@ msgstr "Sinistra"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1051
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1063
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3923,6 +3923,7 @@ msgid "Game not implemented"
 msgstr "Gioco non implementato"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
+#: engines/scumm/metaengine.cpp:1026
 msgid "Default game keymappings"
 msgstr "Mappa tasti predefinita"
 
@@ -7624,7 +7625,7 @@ msgstr "Attiva / Disattiva modalit?? combattimento"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1090 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1102 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -8286,7 +8287,7 @@ msgstr "Parlare / Interazione"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1077
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1089
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -13664,98 +13665,104 @@ msgstr ""
 msgid "All levels will be available without requiring passwords"
 msgstr "Permette l'accesso a qualsiasi livello del gioco."
 
-#: engines/scumm/metaengine.cpp:1025
+#: engines/scumm/metaengine.cpp:1037
 msgid "Down left"
 msgstr "Gi?? sinistra"
 
-#: engines/scumm/metaengine.cpp:1038
+#: engines/scumm/metaengine.cpp:1050
 msgid "Down right"
 msgstr "Gi?? destra"
 
-#: engines/scumm/metaengine.cpp:1058
+#: engines/scumm/metaengine.cpp:1070
 msgid "Up left"
 msgstr "Su sinistra"
 
-#: engines/scumm/metaengine.cpp:1071
+#: engines/scumm/metaengine.cpp:1083
 msgid "Up right"
 msgstr "Su destra"
 
-#: engines/scumm/metaengine.cpp:1083
+#: engines/scumm/metaengine.cpp:1095
 msgid "Switch weapon"
 msgstr "Cambia arma"
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1097
+#: engines/scumm/metaengine.cpp:1109
 msgid "Win the bike fight cheat"
 msgstr "Cheat: Vinci combattimento in moto"
 
-#: engines/scumm/metaengine.cpp:1106
+#: engines/scumm/metaengine.cpp:1118
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1108 engines/scumm/metaengine.cpp:1170
+#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1182
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1113 engines/scumm/metaengine.cpp:1175
+#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1187
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1118 engines/scumm/metaengine.cpp:1180
+#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1192
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1123 engines/scumm/metaengine.cpp:1185
+#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1197
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1128 engines/scumm/metaengine.cpp:1190
+#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "Raccogli"
 
-#: engines/scumm/metaengine.cpp:1134 engines/scumm/metaengine.cpp:1196
+#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1209
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "Scorri verso il basso"
 
-#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1216
 #, fuzzy
 #| msgid "Slide left"
 msgid "Stick left"
 msgstr "Scorri a sinistra"
 
-#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1208
+#: engines/scumm/metaengine.cpp:1158 engines/scumm/metaengine.cpp:1223
 #, fuzzy
 #| msgid "Slide right"
 msgid "Stick right"
 msgstr "Scorri a destra"
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1214
+#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1230
 #, fuzzy
 #| msgid "Move / Select"
 msgid "Fire / select"
 msgstr "Spostamento / Selezione"
 
-#: engines/scumm/metaengine.cpp:1157
+#: engines/scumm/metaengine.cpp:1169
 #, fuzzy
 #| msgid "Backspace"
 msgid "Back / skip"
 msgstr "Backspace"
 
-#: engines/scumm/metaengine.cpp:1168
+#: engines/scumm/metaengine.cpp:1180
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1219
+#: engines/scumm/metaengine.cpp:1235
 #, fuzzy
 #| msgid "Move back"
 msgid "Cover / back"
 msgstr "Arretra"
 
-#: engines/scumm/metaengine.cpp:1224
+#: engines/scumm/metaengine.cpp:1241
+#, fuzzy
+#| msgid "Skip walk"
+msgid "Skip / back"
+msgstr "Salta spostamento"
+
+#: engines/scumm/metaengine.cpp:1248
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu"
diff --git a/po/ja.po b/po/ja.po
index 970b70f2e39..7297140f23c 100644
--- a/po/ja.po
+++ b/po/ja.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 14:35+0000\n"
+"POT-Creation-Date: 2026-06-01 16:27+0000\n"
 "PO-Revision-Date: 2026-04-26 07:22+0000\n"
 "Last-Translator: VAN-Gluon <van.gluonp at gmail.com>\n"
 "Language-Team: Japanese <https://translations.scummvm.org/projects/scummvm/"
@@ -1042,7 +1042,7 @@ msgstr "??????"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1064
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1076
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1063,7 +1063,7 @@ msgstr "???"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1031
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1043
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1084,7 +1084,7 @@ msgstr "???"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1044
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1056
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1104,7 +1104,7 @@ msgstr "???"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1051
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1063
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3781,6 +3781,7 @@ msgid "Game not implemented"
 msgstr "?????????????????????"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
+#: engines/scumm/metaengine.cpp:1026
 msgid "Default game keymappings"
 msgstr "?????????????????????(???????????????)"
 
@@ -7473,7 +7474,7 @@ msgstr "??????????????????"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1090 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1102 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -8234,7 +8235,7 @@ msgstr "??????/??????"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1077
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1089
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -14059,106 +14060,112 @@ msgstr ""
 msgid "All levels will be available without requiring passwords"
 msgstr "?????????????????????????????????????????????"
 
-#: engines/scumm/metaengine.cpp:1025
+#: engines/scumm/metaengine.cpp:1037
 #, fuzzy
 #| msgid "Down Left"
 msgid "Down left"
 msgstr "??????"
 
-#: engines/scumm/metaengine.cpp:1038
+#: engines/scumm/metaengine.cpp:1050
 #, fuzzy
 #| msgid "Down Right"
 msgid "Down right"
 msgstr "??????"
 
-#: engines/scumm/metaengine.cpp:1058
+#: engines/scumm/metaengine.cpp:1070
 #, fuzzy
 #| msgid "Upper left item"
 msgid "Up left"
 msgstr "?????????????????????"
 
-#: engines/scumm/metaengine.cpp:1071
+#: engines/scumm/metaengine.cpp:1083
 #, fuzzy
 #| msgid "Up Right"
 msgid "Up right"
 msgstr "??????"
 
-#: engines/scumm/metaengine.cpp:1083
+#: engines/scumm/metaengine.cpp:1095
 msgid "Switch weapon"
 msgstr "?????????????????????"
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1097
+#: engines/scumm/metaengine.cpp:1109
 msgid "Win the bike fight cheat"
 msgstr "????????????????????????"
 
-#: engines/scumm/metaengine.cpp:1106
+#: engines/scumm/metaengine.cpp:1118
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1108 engines/scumm/metaengine.cpp:1170
+#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1182
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1113 engines/scumm/metaengine.cpp:1175
+#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1187
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1118 engines/scumm/metaengine.cpp:1180
+#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1192
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1123 engines/scumm/metaengine.cpp:1185
+#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1197
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1128 engines/scumm/metaengine.cpp:1190
+#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "??????"
 
-#: engines/scumm/metaengine.cpp:1134 engines/scumm/metaengine.cpp:1196
+#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1209
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "?????????????????????"
 
-#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1216
 #, fuzzy
 #| msgid "Slide Left"
 msgid "Stick left"
 msgstr "??????????????????"
 
-#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1208
+#: engines/scumm/metaengine.cpp:1158 engines/scumm/metaengine.cpp:1223
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick right"
 msgstr "??????????????????"
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1214
+#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1230
 #, fuzzy
 #| msgid "Move left"
 msgid "Fire / select"
 msgstr "????????????"
 
-#: engines/scumm/metaengine.cpp:1157
+#: engines/scumm/metaengine.cpp:1169
 #, fuzzy
 #| msgid "Backspace"
 msgid "Back / skip"
 msgstr "?????????????????????"
 
-#: engines/scumm/metaengine.cpp:1168
+#: engines/scumm/metaengine.cpp:1180
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1219
+#: engines/scumm/metaengine.cpp:1235
 #, fuzzy
 #| msgid "Move Back"
 msgid "Cover / back"
 msgstr "??????"
 
-#: engines/scumm/metaengine.cpp:1224
+#: engines/scumm/metaengine.cpp:1241
+#, fuzzy
+#| msgid "Skip dialogue"
+msgid "Skip / back"
+msgstr "??????????????????????????????"
+
+#: engines/scumm/metaengine.cpp:1248
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu"
diff --git a/po/ka.po b/po/ka.po
index d8b60230e34..e5b24edc455 100644
--- a/po/ka.po
+++ b/po/ka.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 14:35+0000\n"
+"POT-Creation-Date: 2026-06-01 16:27+0000\n"
 "PO-Revision-Date: 2026-05-09 12:22+0000\n"
 "Last-Translator: NorwayFun <temuri.doghonadze at gmail.com>\n"
 "Language-Team: Georgian <https://translations.scummvm.org/projects/scummvm/"
@@ -1021,7 +1021,7 @@ msgstr "??????????????????????????????"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1064
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1076
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1042,7 +1042,7 @@ msgstr "?????????????????????"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1031
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1043
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1063,7 +1063,7 @@ msgstr "??????????????????"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1044
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1056
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1083,7 +1083,7 @@ msgstr "????????????????????????"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1051
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1063
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3606,6 +3606,7 @@ msgid "Game not implemented"
 msgstr "?????????????????? ????????????????????????????????????????????? ????????????"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
+#: engines/scumm/metaengine.cpp:1026
 msgid "Default game keymappings"
 msgstr "???????????????????????????????????? ??????????????????????????? ??????????????????????????? ??????????????????"
 
@@ -6829,7 +6830,7 @@ msgstr "????????????????????? ????????????????????????"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1090 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1102 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -7476,7 +7477,7 @@ msgstr "????????????????????? / ??????????????????????????????"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1077
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1089
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -12432,98 +12433,104 @@ msgstr ""
 msgid "All levels will be available without requiring passwords"
 msgstr "??????????????????????????? ??????????????????????????????????????? ?????????????????? ??????????????? ????????????."
 
-#: engines/scumm/metaengine.cpp:1025
+#: engines/scumm/metaengine.cpp:1037
 msgid "Down left"
 msgstr "??????????????? ????????????????????????"
 
-#: engines/scumm/metaengine.cpp:1038
+#: engines/scumm/metaengine.cpp:1050
 msgid "Down right"
 msgstr "??????????????? ????????????????????????"
 
-#: engines/scumm/metaengine.cpp:1058
+#: engines/scumm/metaengine.cpp:1070
 msgid "Up left"
 msgstr "???????????? ????????????????????????"
 
-#: engines/scumm/metaengine.cpp:1071
+#: engines/scumm/metaengine.cpp:1083
 msgid "Up right"
 msgstr "??????????????? ????????????????????????"
 
-#: engines/scumm/metaengine.cpp:1083
+#: engines/scumm/metaengine.cpp:1095
 msgid "Switch weapon"
 msgstr "????????????????????? ????????????????????????"
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1097
+#: engines/scumm/metaengine.cpp:1109
 msgid "Win the bike fight cheat"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1106
+#: engines/scumm/metaengine.cpp:1118
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1108 engines/scumm/metaengine.cpp:1170
+#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1182
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1113 engines/scumm/metaengine.cpp:1175
+#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1187
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1118 engines/scumm/metaengine.cpp:1180
+#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1192
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1123 engines/scumm/metaengine.cpp:1185
+#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1197
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1128 engines/scumm/metaengine.cpp:1190
+#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "???????????????"
 
-#: engines/scumm/metaengine.cpp:1134 engines/scumm/metaengine.cpp:1196
+#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1209
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "??????????????? ????????????????????????"
 
-#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1216
 #, fuzzy
 #| msgid "Slide left"
 msgid "Stick left"
 msgstr "?????????????????????????????? ????????????????????????"
 
-#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1208
+#: engines/scumm/metaengine.cpp:1158 engines/scumm/metaengine.cpp:1223
 #, fuzzy
 #| msgid "Slide right"
 msgid "Stick right"
 msgstr "?????????????????????????????? ????????????????????????"
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1214
+#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1230
 #, fuzzy
 #| msgid "Move / Select"
 msgid "Fire / select"
 msgstr "???????????????????????????????????? / ????????????????????????"
 
-#: engines/scumm/metaengine.cpp:1157
+#: engines/scumm/metaengine.cpp:1169
 #, fuzzy
 #| msgid "Backspace"
 msgid "Back / skip"
 msgstr "Backspace"
 
-#: engines/scumm/metaengine.cpp:1168
+#: engines/scumm/metaengine.cpp:1180
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1219
+#: engines/scumm/metaengine.cpp:1235
 #, fuzzy
 #| msgid "Move back"
 msgid "Cover / back"
 msgstr "???????????? ????????????????????????"
 
-#: engines/scumm/metaengine.cpp:1224
+#: engines/scumm/metaengine.cpp:1241
+#, fuzzy
+#| msgid "Skip walk"
+msgid "Skip / back"
+msgstr "???????????????????????? ??????????????????????????????"
+
+#: engines/scumm/metaengine.cpp:1248
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu"
diff --git a/po/ko.po b/po/ko.po
index 8cb0e52511d..34c13fda2d8 100644
--- a/po/ko.po
+++ b/po/ko.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 14:35+0000\n"
+"POT-Creation-Date: 2026-06-01 16:27+0000\n"
 "PO-Revision-Date: 2026-05-19 20:22+0000\n"
 "Last-Translator: Hoseok Seo <ddinghoya at gmail.com>\n"
 "Language-Team: Korean <https://translations.scummvm.org/projects/scummvm/"
@@ -1044,7 +1044,7 @@ msgstr "??????"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1064
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1076
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1065,7 +1065,7 @@ msgstr "??????"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1031
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1043
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1086,7 +1086,7 @@ msgstr "?????????"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1044
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1056
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1106,7 +1106,7 @@ msgstr "??????"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1051
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1063
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3835,6 +3835,7 @@ msgid "Game not implemented"
 msgstr "????????? ???????????? ?????????"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
+#: engines/scumm/metaengine.cpp:1026
 msgid "Default game keymappings"
 msgstr "?????? ?????? ?????????"
 
@@ -7443,7 +7444,7 @@ msgstr "?????? ??????"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1090 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1102 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -8105,7 +8106,7 @@ msgstr "?????? / ??????"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1077
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1089
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -13344,98 +13345,104 @@ msgstr ""
 msgid "All levels will be available without requiring passwords"
 msgstr "?????? ???????????? ???????????? ??? ????????????."
 
-#: engines/scumm/metaengine.cpp:1025
+#: engines/scumm/metaengine.cpp:1037
 msgid "Down left"
 msgstr "?????? ??????"
 
-#: engines/scumm/metaengine.cpp:1038
+#: engines/scumm/metaengine.cpp:1050
 msgid "Down right"
 msgstr "????????? ??????"
 
-#: engines/scumm/metaengine.cpp:1058
+#: engines/scumm/metaengine.cpp:1070
 msgid "Up left"
 msgstr "?????? ??????"
 
-#: engines/scumm/metaengine.cpp:1071
+#: engines/scumm/metaengine.cpp:1083
 msgid "Up right"
 msgstr "????????? ??????"
 
-#: engines/scumm/metaengine.cpp:1083
+#: engines/scumm/metaengine.cpp:1095
 msgid "Switch weapon"
 msgstr "?????? ??????"
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1097
+#: engines/scumm/metaengine.cpp:1109
 msgid "Win the bike fight cheat"
 msgstr "????????? ???????????? ???????????? ??????"
 
-#: engines/scumm/metaengine.cpp:1106
+#: engines/scumm/metaengine.cpp:1118
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1108 engines/scumm/metaengine.cpp:1170
+#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1182
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1113 engines/scumm/metaengine.cpp:1175
+#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1187
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1118 engines/scumm/metaengine.cpp:1180
+#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1192
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1123 engines/scumm/metaengine.cpp:1185
+#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1197
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1128 engines/scumm/metaengine.cpp:1190
+#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "??????"
 
-#: engines/scumm/metaengine.cpp:1134 engines/scumm/metaengine.cpp:1196
+#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1209
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "????????? ?????????"
 
-#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1216
 #, fuzzy
 #| msgid "Slide left"
 msgid "Stick left"
 msgstr "???????????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1208
+#: engines/scumm/metaengine.cpp:1158 engines/scumm/metaengine.cpp:1223
 #, fuzzy
 #| msgid "Slide right"
 msgid "Stick right"
 msgstr "????????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1214
+#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1230
 #, fuzzy
 #| msgid "Move / Select"
 msgid "Fire / select"
 msgstr "?????? / ??????"
 
-#: engines/scumm/metaengine.cpp:1157
+#: engines/scumm/metaengine.cpp:1169
 #, fuzzy
 #| msgid "Backspace"
 msgid "Back / skip"
 msgstr "Backspace ???"
 
-#: engines/scumm/metaengine.cpp:1168
+#: engines/scumm/metaengine.cpp:1180
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1219
+#: engines/scumm/metaengine.cpp:1235
 #, fuzzy
 #| msgid "Move back"
 msgid "Cover / back"
 msgstr "?????? ??????"
 
-#: engines/scumm/metaengine.cpp:1224
+#: engines/scumm/metaengine.cpp:1241
+#, fuzzy
+#| msgid "Skip walk"
+msgid "Skip / back"
+msgstr "?????? ????????????"
+
+#: engines/scumm/metaengine.cpp:1248
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu"
diff --git a/po/nb_NO.po b/po/nb_NO.po
index 96ddc99c1bd..5c1e44f423e 100644
--- a/po/nb_NO.po
+++ b/po/nb_NO.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 14:35+0000\n"
+"POT-Creation-Date: 2026-06-01 16:27+0000\n"
 "PO-Revision-Date: 2026-02-07 15:22+0000\n"
 "Last-Translator: Einar Johan Tr??an S??m??en <einarjohants at gmail.com>\n"
 "Language-Team: Norwegian Bokm??l <https://translations.scummvm.org/projects/"
@@ -1052,7 +1052,7 @@ msgstr "Samhandle"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1064
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1076
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1073,7 +1073,7 @@ msgstr "Opp"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1031
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1043
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1094,7 +1094,7 @@ msgstr "Ned"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1044
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1056
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1114,7 +1114,7 @@ msgstr "Venstre"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1051
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1063
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3963,6 +3963,7 @@ msgid "Game not implemented"
 msgstr "Spillet er ikke implementert"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
+#: engines/scumm/metaengine.cpp:1026
 #, fuzzy
 #| msgid "Default game keymap"
 msgid "Default game keymappings"
@@ -7909,7 +7910,7 @@ msgstr "Skru kampmodus av/p??"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1090 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1102 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -8677,7 +8678,7 @@ msgstr "Konverser/Interager"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1077
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1089
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -14506,106 +14507,112 @@ msgstr ""
 msgid "All levels will be available without requiring passwords"
 msgstr "Alle niv??er ??pnes for spilling."
 
-#: engines/scumm/metaengine.cpp:1025
+#: engines/scumm/metaengine.cpp:1037
 #, fuzzy
 #| msgid "Down Left"
 msgid "Down left"
 msgstr "Ned til venstre"
 
-#: engines/scumm/metaengine.cpp:1038
+#: engines/scumm/metaengine.cpp:1050
 #, fuzzy
 #| msgid "Down Right"
 msgid "Down right"
 msgstr "Ned til h??yre"
 
-#: engines/scumm/metaengine.cpp:1058
+#: engines/scumm/metaengine.cpp:1070
 #, fuzzy
 #| msgid "Upper left item"
 msgid "Up left"
 msgstr "??vre venstre gjenstand"
 
-#: engines/scumm/metaengine.cpp:1071
+#: engines/scumm/metaengine.cpp:1083
 #, fuzzy
 #| msgid "Up Right"
 msgid "Up right"
 msgstr "Opp til h??yre"
 
-#: engines/scumm/metaengine.cpp:1083
+#: engines/scumm/metaengine.cpp:1095
 msgid "Switch weapon"
 msgstr "Bytte v??pen"
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1097
+#: engines/scumm/metaengine.cpp:1109
 msgid "Win the bike fight cheat"
 msgstr "Juksefunksjon for ?? vinne motorsykkelkamper"
 
-#: engines/scumm/metaengine.cpp:1106
+#: engines/scumm/metaengine.cpp:1118
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1108 engines/scumm/metaengine.cpp:1170
+#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1182
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1113 engines/scumm/metaengine.cpp:1175
+#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1187
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1118 engines/scumm/metaengine.cpp:1180
+#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1192
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1123 engines/scumm/metaengine.cpp:1185
+#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1197
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1128 engines/scumm/metaengine.cpp:1190
+#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "Plukk opp"
 
-#: engines/scumm/metaengine.cpp:1134 engines/scumm/metaengine.cpp:1196
+#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1209
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "Bla ned"
 
-#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1216
 #, fuzzy
 #| msgid "Slide Left"
 msgid "Stick left"
 msgstr "Skli mot Venstre"
 
-#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1208
+#: engines/scumm/metaengine.cpp:1158 engines/scumm/metaengine.cpp:1223
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick right"
 msgstr "Skli mot H??yre"
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1214
+#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1230
 #, fuzzy
 #| msgid "Move left"
 msgid "Fire / select"
 msgstr "G?? til venstre"
 
-#: engines/scumm/metaengine.cpp:1157
+#: engines/scumm/metaengine.cpp:1169
 #, fuzzy
 #| msgid "Backspace"
 msgid "Back / skip"
 msgstr "Rettetast"
 
-#: engines/scumm/metaengine.cpp:1168
+#: engines/scumm/metaengine.cpp:1180
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1219
+#: engines/scumm/metaengine.cpp:1235
 #, fuzzy
 #| msgid "Move Back"
 msgid "Cover / back"
 msgstr "Beveg Bakover"
 
-#: engines/scumm/metaengine.cpp:1224
+#: engines/scumm/metaengine.cpp:1241
+#, fuzzy
+#| msgid "Skip dialogue"
+msgid "Skip / back"
+msgstr "Hopp over dialog"
+
+#: engines/scumm/metaengine.cpp:1248
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu"
diff --git a/po/nl_NL.po b/po/nl_NL.po
index 87e9a1bffed..a4e841441b2 100644
--- a/po/nl_NL.po
+++ b/po/nl_NL.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.9.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 14:35+0000\n"
+"POT-Creation-Date: 2026-06-01 16:27+0000\n"
 "PO-Revision-Date: 2026-02-11 14:23+0000\n"
 "Last-Translator: SecularSteve <fairfull.playing at gmail.com>\n"
 "Language-Team: Dutch <https://translations.scummvm.org/projects/scummvm/"
@@ -1053,7 +1053,7 @@ msgstr "Interactie"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1064
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1076
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1074,7 +1074,7 @@ msgstr "Omhoog"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1031
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1043
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1095,7 +1095,7 @@ msgstr "Omlaag"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1044
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1056
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1115,7 +1115,7 @@ msgstr "Links"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1051
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1063
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3896,6 +3896,7 @@ msgid "Game not implemented"
 msgstr "Spel niet ge??mplementeerd"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
+#: engines/scumm/metaengine.cpp:1026
 msgid "Default game keymappings"
 msgstr "Standaard speltoetsenkoppelingen"
 
@@ -7551,7 +7552,7 @@ msgstr "Schakel gevecht in/uit"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1090 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1102 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -8250,7 +8251,7 @@ msgstr "Praten / Interactie"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1077
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1089
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -13707,98 +13708,104 @@ msgstr ""
 msgid "All levels will be available without requiring passwords"
 msgstr "Alle levels kunnen dan gespeeld worden."
 
-#: engines/scumm/metaengine.cpp:1025
+#: engines/scumm/metaengine.cpp:1037
 msgid "Down left"
 msgstr "Linksonder"
 
-#: engines/scumm/metaengine.cpp:1038
+#: engines/scumm/metaengine.cpp:1050
 msgid "Down right"
 msgstr "Rechtsonder"
 
-#: engines/scumm/metaengine.cpp:1058
+#: engines/scumm/metaengine.cpp:1070
 msgid "Up left"
 msgstr "Linksboven"
 
-#: engines/scumm/metaengine.cpp:1071
+#: engines/scumm/metaengine.cpp:1083
 msgid "Up right"
 msgstr "Rechtop"
 
-#: engines/scumm/metaengine.cpp:1083
+#: engines/scumm/metaengine.cpp:1095
 msgid "Switch weapon"
 msgstr "Wissel wapen"
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1097
+#: engines/scumm/metaengine.cpp:1109
 msgid "Win the bike fight cheat"
 msgstr "Win de fietsstrijd-cheat"
 
-#: engines/scumm/metaengine.cpp:1106
+#: engines/scumm/metaengine.cpp:1118
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1108 engines/scumm/metaengine.cpp:1170
+#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1182
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1113 engines/scumm/metaengine.cpp:1175
+#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1187
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1118 engines/scumm/metaengine.cpp:1180
+#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1192
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1123 engines/scumm/metaengine.cpp:1185
+#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1197
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1128 engines/scumm/metaengine.cpp:1190
+#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "Pak op"
 
-#: engines/scumm/metaengine.cpp:1134 engines/scumm/metaengine.cpp:1196
+#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1209
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "Scroll omlaag"
 
-#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1216
 #, fuzzy
 #| msgid "Slide left"
 msgid "Stick left"
 msgstr "Schuif naar link"
 
-#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1208
+#: engines/scumm/metaengine.cpp:1158 engines/scumm/metaengine.cpp:1223
 #, fuzzy
 #| msgid "Slide right"
 msgid "Stick right"
 msgstr "Schuif naar rechts"
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1214
+#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1230
 #, fuzzy
 #| msgid "Move / Select"
 msgid "Fire / select"
 msgstr "Verplaatsen / Selecteren"
 
-#: engines/scumm/metaengine.cpp:1157
+#: engines/scumm/metaengine.cpp:1169
 #, fuzzy
 #| msgid "Backspace"
 msgid "Back / skip"
 msgstr "Backspace"
 
-#: engines/scumm/metaengine.cpp:1168
+#: engines/scumm/metaengine.cpp:1180
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1219
+#: engines/scumm/metaengine.cpp:1235
 #, fuzzy
 #| msgid "Move back"
 msgid "Cover / back"
 msgstr "Teruggaan"
 
-#: engines/scumm/metaengine.cpp:1224
+#: engines/scumm/metaengine.cpp:1241
+#, fuzzy
+#| msgid "Skip walk"
+msgid "Skip / back"
+msgstr "Sla wandeling over"
+
+#: engines/scumm/metaengine.cpp:1248
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu"
diff --git a/po/nn_NO.po b/po/nn_NO.po
index 14d7272b2dc..2200c0b7291 100644
--- a/po/nn_NO.po
+++ b/po/nn_NO.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 14:35+0000\n"
+"POT-Creation-Date: 2026-06-01 16:27+0000\n"
 "PO-Revision-Date: 2023-12-14 14:28+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Norwegian Nynorsk <https://translations.scummvm.org/projects/"
@@ -1057,7 +1057,7 @@ msgstr "Interager"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1064
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1076
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1078,7 +1078,7 @@ msgstr "Opp"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1031
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1043
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1099,7 +1099,7 @@ msgstr "Ned"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1044
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1056
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1119,7 +1119,7 @@ msgstr "Venstre"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1051
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1063
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3822,6 +3822,7 @@ msgid "Game not implemented"
 msgstr "Spel ID ikkje st??tta"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
+#: engines/scumm/metaengine.cpp:1026
 #, fuzzy
 #| msgid "Game Options"
 msgid "Default game keymappings"
@@ -7287,7 +7288,7 @@ msgstr "Veksle musl??sing"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1090 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1102 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -8035,7 +8036,7 @@ msgstr "Interpolering:"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1077
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1089
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -13633,102 +13634,107 @@ msgstr ""
 msgid "All levels will be available without requiring passwords"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1025
+#: engines/scumm/metaengine.cpp:1037
 #, fuzzy
 msgid "Down left"
 msgstr "Snu til Venstre"
 
-#: engines/scumm/metaengine.cpp:1038
+#: engines/scumm/metaengine.cpp:1050
 #, fuzzy
 msgid "Down right"
 msgstr "Snu til H??yre"
 
-#: engines/scumm/metaengine.cpp:1058
+#: engines/scumm/metaengine.cpp:1070
 #, fuzzy
 #| msgid "Upper left item"
 msgid "Up left"
 msgstr "??vre venstre gjenstand"
 
-#: engines/scumm/metaengine.cpp:1071
+#: engines/scumm/metaengine.cpp:1083
 #, fuzzy
 msgid "Up right"
 msgstr "H??gre"
 
-#: engines/scumm/metaengine.cpp:1083
+#: engines/scumm/metaengine.cpp:1095
 #, fuzzy
 msgid "Switch weapon"
 msgstr "Veksle karakterar:"
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1097
+#: engines/scumm/metaengine.cpp:1109
 msgid "Win the bike fight cheat"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1106
+#: engines/scumm/metaengine.cpp:1118
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1108 engines/scumm/metaengine.cpp:1170
+#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1182
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1113 engines/scumm/metaengine.cpp:1175
+#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1187
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1118 engines/scumm/metaengine.cpp:1180
+#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1192
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1123 engines/scumm/metaengine.cpp:1185
+#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1197
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1128 engines/scumm/metaengine.cpp:1190
+#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "Plukk opp"
 
-#: engines/scumm/metaengine.cpp:1134 engines/scumm/metaengine.cpp:1196
+#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1209
 #, fuzzy
 msgid "Stick down"
 msgstr "Bla liste ned"
 
-#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1216
 #, fuzzy
 #| msgid "Slide Left"
 msgid "Stick left"
 msgstr "Skli til Venstre"
 
-#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1208
+#: engines/scumm/metaengine.cpp:1158 engines/scumm/metaengine.cpp:1223
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick right"
 msgstr "Skli til H??yre"
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1214
+#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1230
 #, fuzzy
 msgid "Fire / select"
 msgstr "Skli til Venstre"
 
-#: engines/scumm/metaengine.cpp:1157
+#: engines/scumm/metaengine.cpp:1169
 #, fuzzy
 #| msgid "Backspace"
 msgid "Back / skip"
 msgstr "Tilbaketast"
 
-#: engines/scumm/metaengine.cpp:1168
+#: engines/scumm/metaengine.cpp:1180
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1219
+#: engines/scumm/metaengine.cpp:1235
 #, fuzzy
 #| msgid "Move Back"
 msgid "Cover / back"
 msgstr "Beveg Bakover"
 
-#: engines/scumm/metaengine.cpp:1224
+#: engines/scumm/metaengine.cpp:1241
+#, fuzzy
+msgid "Skip / back"
+msgstr "Hopp over linje"
+
+#: engines/scumm/metaengine.cpp:1248
 #, fuzzy
 msgid "Skip / menu"
 msgstr "Tilbake til Oppsta~r~tar"
diff --git a/po/pl_PL.po b/po/pl_PL.po
index eace59066f2..3792f1b7140 100644
--- a/po/pl_PL.po
+++ b/po/pl_PL.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 14:35+0000\n"
+"POT-Creation-Date: 2026-06-01 16:27+0000\n"
 "PO-Revision-Date: 2026-04-17 00:22+0000\n"
 "Last-Translator: SkiffPL <skiff at windowslive.com>\n"
 "Language-Team: Polish <https://translations.scummvm.org/projects/scummvm/"
@@ -1054,7 +1054,7 @@ msgstr "Interakcja"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1064
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1076
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1075,7 +1075,7 @@ msgstr "W g??r??"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1031
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1043
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1096,7 +1096,7 @@ msgstr "W d????"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1044
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1056
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1116,7 +1116,7 @@ msgstr "W lewo"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1051
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1063
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3887,6 +3887,7 @@ msgid "Game not implemented"
 msgstr "Gra nie jest obs??ugiwana"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
+#: engines/scumm/metaengine.cpp:1026
 msgid "Default game keymappings"
 msgstr "Domy??lne mapowanie klawiatury"
 
@@ -7565,7 +7566,7 @@ msgstr "Prze????cz tryb walki"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1090 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1102 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -8233,7 +8234,7 @@ msgstr "Rozmowa/interakcja"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1077
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1089
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -13886,98 +13887,104 @@ msgstr ""
 msgid "All levels will be available without requiring passwords"
 msgstr "Wszystkie poziomy gry b??d?? dost??pne."
 
-#: engines/scumm/metaengine.cpp:1025
+#: engines/scumm/metaengine.cpp:1037
 msgid "Down left"
 msgstr "W d???? w lewo"
 
-#: engines/scumm/metaengine.cpp:1038
+#: engines/scumm/metaengine.cpp:1050
 msgid "Down right"
 msgstr "W d???? w prawo"
 
-#: engines/scumm/metaengine.cpp:1058
+#: engines/scumm/metaengine.cpp:1070
 msgid "Up left"
 msgstr "W g??r?? na lewo"
 
-#: engines/scumm/metaengine.cpp:1071
+#: engines/scumm/metaengine.cpp:1083
 msgid "Up right"
 msgstr "W g??r?? w prawo"
 
-#: engines/scumm/metaengine.cpp:1083
+#: engines/scumm/metaengine.cpp:1095
 msgid "Switch weapon"
 msgstr "Zmie?? bro??"
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1097
+#: engines/scumm/metaengine.cpp:1109
 msgid "Win the bike fight cheat"
 msgstr "Cheat 'wygraj walk?? na motocyklu'"
 
-#: engines/scumm/metaengine.cpp:1106
+#: engines/scumm/metaengine.cpp:1118
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1108 engines/scumm/metaengine.cpp:1170
+#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1182
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1113 engines/scumm/metaengine.cpp:1175
+#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1187
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1118 engines/scumm/metaengine.cpp:1180
+#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1192
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1123 engines/scumm/metaengine.cpp:1185
+#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1197
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1128 engines/scumm/metaengine.cpp:1190
+#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "Podnie??"
 
-#: engines/scumm/metaengine.cpp:1134 engines/scumm/metaengine.cpp:1196
+#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1209
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "Przewi?? w d????"
 
-#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1216
 #, fuzzy
 #| msgid "Slide left"
 msgid "Stick left"
 msgstr "??lizg w lewo"
 
-#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1208
+#: engines/scumm/metaengine.cpp:1158 engines/scumm/metaengine.cpp:1223
 #, fuzzy
 #| msgid "Slide right"
 msgid "Stick right"
 msgstr "??lizg w prawo"
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1214
+#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1230
 #, fuzzy
 #| msgid "Move / Select"
 msgid "Fire / select"
 msgstr "Ruch / Zaznacz"
 
-#: engines/scumm/metaengine.cpp:1157
+#: engines/scumm/metaengine.cpp:1169
 #, fuzzy
 #| msgid "Backspace"
 msgid "Back / skip"
 msgstr "Backspace"
 
-#: engines/scumm/metaengine.cpp:1168
+#: engines/scumm/metaengine.cpp:1180
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1219
+#: engines/scumm/metaengine.cpp:1235
 #, fuzzy
 #| msgid "Move back"
 msgid "Cover / back"
 msgstr "Ruch do ty??u"
 
-#: engines/scumm/metaengine.cpp:1224
+#: engines/scumm/metaengine.cpp:1241
+#, fuzzy
+#| msgid "Skip dialog"
+msgid "Skip / back"
+msgstr "Pomi?? dialog"
+
+#: engines/scumm/metaengine.cpp:1248
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu"
diff --git a/po/pt_BR.po b/po/pt_BR.po
index a35db613a10..84a52105dad 100644
--- a/po/pt_BR.po
+++ b/po/pt_BR.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 14:35+0000\n"
+"POT-Creation-Date: 2026-06-01 16:27+0000\n"
 "PO-Revision-Date: 2024-11-16 02:13+0000\n"
 "Last-Translator: Marcel Souza Lemes <marcosoutsider at gmail.com>\n"
 "Language-Team: Portuguese (Brazil) <https://translations.scummvm.org/"
@@ -1059,7 +1059,7 @@ msgstr "Interagir"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1064
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1076
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1080,7 +1080,7 @@ msgstr "Cima"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1031
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1043
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1101,7 +1101,7 @@ msgstr "Baixo"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1044
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1056
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1121,7 +1121,7 @@ msgstr "Esquerda"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1051
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1063
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3981,6 +3981,7 @@ msgid "Game not implemented"
 msgstr "Jogo n??o implementado"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
+#: engines/scumm/metaengine.cpp:1026
 #, fuzzy
 #| msgid "Default game keymap"
 msgid "Default game keymappings"
@@ -7880,7 +7881,7 @@ msgstr "Alternar Modo de Combate"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1090 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1102 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -8644,7 +8645,7 @@ msgstr "Falar/Interagir"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1077
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1089
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -14478,106 +14479,112 @@ msgstr ""
 msgid "All levels will be available without requiring passwords"
 msgstr "Todos os n??veis estar??o dispon??veis para jogar."
 
-#: engines/scumm/metaengine.cpp:1025
+#: engines/scumm/metaengine.cpp:1037
 #, fuzzy
 #| msgid "Down Left"
 msgid "Down left"
 msgstr "Para Baixo ?? Esquerda"
 
-#: engines/scumm/metaengine.cpp:1038
+#: engines/scumm/metaengine.cpp:1050
 #, fuzzy
 #| msgid "Down Right"
 msgid "Down right"
 msgstr "Para Baixo ?? Direita"
 
-#: engines/scumm/metaengine.cpp:1058
+#: engines/scumm/metaengine.cpp:1070
 #, fuzzy
 #| msgid "Upper left item"
 msgid "Up left"
 msgstr "Item da esquerda superior"
 
-#: engines/scumm/metaengine.cpp:1071
+#: engines/scumm/metaengine.cpp:1083
 #, fuzzy
 #| msgid "Up Right"
 msgid "Up right"
 msgstr "Para Cima ?? Direita"
 
-#: engines/scumm/metaengine.cpp:1083
+#: engines/scumm/metaengine.cpp:1095
 msgid "Switch weapon"
 msgstr "Trocar arma"
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1097
+#: engines/scumm/metaengine.cpp:1109
 msgid "Win the bike fight cheat"
 msgstr "Trapa??a para vencer a briga de motos"
 
-#: engines/scumm/metaengine.cpp:1106
+#: engines/scumm/metaengine.cpp:1118
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1108 engines/scumm/metaengine.cpp:1170
+#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1182
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1113 engines/scumm/metaengine.cpp:1175
+#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1187
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1118 engines/scumm/metaengine.cpp:1180
+#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1192
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1123 engines/scumm/metaengine.cpp:1185
+#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1197
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1128 engines/scumm/metaengine.cpp:1190
+#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "Pegar"
 
-#: engines/scumm/metaengine.cpp:1134 engines/scumm/metaengine.cpp:1196
+#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1209
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "Rolar para baixo"
 
-#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1216
 #, fuzzy
 #| msgid "Slide Left"
 msgid "Stick left"
 msgstr "Deslizar para Esquerda"
 
-#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1208
+#: engines/scumm/metaengine.cpp:1158 engines/scumm/metaengine.cpp:1223
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick right"
 msgstr "Deslizar para Direita"
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1214
+#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1230
 #, fuzzy
 #| msgid "Move left"
 msgid "Fire / select"
 msgstr "Mover para esquerda"
 
-#: engines/scumm/metaengine.cpp:1157
+#: engines/scumm/metaengine.cpp:1169
 #, fuzzy
 #| msgid "Backspace"
 msgid "Back / skip"
 msgstr "Retroceder"
 
-#: engines/scumm/metaengine.cpp:1168
+#: engines/scumm/metaengine.cpp:1180
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1219
+#: engines/scumm/metaengine.cpp:1235
 #, fuzzy
 #| msgid "Move Back"
 msgid "Cover / back"
 msgstr "Mover para Tr??s"
 
-#: engines/scumm/metaengine.cpp:1224
+#: engines/scumm/metaengine.cpp:1241
+#, fuzzy
+#| msgid "Skip dialogue"
+msgid "Skip / back"
+msgstr "Pular di??logo"
+
+#: engines/scumm/metaengine.cpp:1248
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu"
diff --git a/po/pt_PT.po b/po/pt_PT.po
index e66ef2ce01a..37675c9f6df 100644
--- a/po/pt_PT.po
+++ b/po/pt_PT.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.10.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 14:35+0000\n"
+"POT-Creation-Date: 2026-06-01 16:27+0000\n"
 "PO-Revision-Date: 2022-06-05 14:09+0000\n"
 "Last-Translator: Daniel <supsuper at gmail.com>\n"
 "Language-Team: Portuguese (Portugal) <https://translations.scummvm.org/"
@@ -1072,7 +1072,7 @@ msgstr "Interagir"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1064
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1076
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1093,7 +1093,7 @@ msgstr "Cima"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1031
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1043
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1114,7 +1114,7 @@ msgstr "Baixo"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1044
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1056
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1134,7 +1134,7 @@ msgstr "Esquerda"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1051
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1063
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3855,6 +3855,7 @@ msgid "Game not implemented"
 msgstr "ID de jogo n??o suportado"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
+#: engines/scumm/metaengine.cpp:1026
 #, fuzzy
 #| msgid "Default game keymap"
 msgid "Default game keymappings"
@@ -7319,7 +7320,7 @@ msgstr "Ativar/desativar combate"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1090 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1102 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -8065,7 +8066,7 @@ msgstr "Interagir"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1077
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1089
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -13755,104 +13756,110 @@ msgstr ""
 msgid "All levels will be available without requiring passwords"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1025
+#: engines/scumm/metaengine.cpp:1037
 #, fuzzy
 msgid "Down left"
 msgstr "Girar ?? esquerda"
 
-#: engines/scumm/metaengine.cpp:1038
+#: engines/scumm/metaengine.cpp:1050
 #, fuzzy
 msgid "Down right"
 msgstr "Girar ?? direita"
 
-#: engines/scumm/metaengine.cpp:1058
+#: engines/scumm/metaengine.cpp:1070
 #, fuzzy
 #| msgid "Move left"
 msgid "Up left"
 msgstr "Mover para a esquerda"
 
-#: engines/scumm/metaengine.cpp:1071
+#: engines/scumm/metaengine.cpp:1083
 #, fuzzy
 msgid "Up right"
 msgstr "Direita"
 
-#: engines/scumm/metaengine.cpp:1083
+#: engines/scumm/metaengine.cpp:1095
 #, fuzzy
 msgid "Switch weapon"
 msgstr "Trocar ecr??s"
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1097
+#: engines/scumm/metaengine.cpp:1109
 msgid "Win the bike fight cheat"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1106
+#: engines/scumm/metaengine.cpp:1118
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1108 engines/scumm/metaengine.cpp:1170
+#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1182
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1113 engines/scumm/metaengine.cpp:1175
+#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1187
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1118 engines/scumm/metaengine.cpp:1180
+#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1192
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1123 engines/scumm/metaengine.cpp:1185
+#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1197
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1128 engines/scumm/metaengine.cpp:1190
+#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
 #, fuzzy
 #| msgid "Left Stick X"
 msgid "Stick up"
 msgstr "Anal??gico Esquerdo X"
 
-#: engines/scumm/metaengine.cpp:1134 engines/scumm/metaengine.cpp:1196
+#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1209
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "Deslizar para baixo"
 
-#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1216
 #, fuzzy
 #| msgid "Slide Left"
 msgid "Stick left"
 msgstr "Deslizar ?? esquerda"
 
-#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1208
+#: engines/scumm/metaengine.cpp:1158 engines/scumm/metaengine.cpp:1223
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick right"
 msgstr "Deslizar ?? direita"
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1214
+#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1230
 #, fuzzy
 #| msgid "Move left"
 msgid "Fire / select"
 msgstr "Mover para a esquerda"
 
-#: engines/scumm/metaengine.cpp:1157
+#: engines/scumm/metaengine.cpp:1169
 #, fuzzy
 #| msgid "Backend"
 msgid "Back / skip"
 msgstr "Sistema"
 
-#: engines/scumm/metaengine.cpp:1168
+#: engines/scumm/metaengine.cpp:1180
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1219
+#: engines/scumm/metaengine.cpp:1235
 #, fuzzy
 #| msgid "Move Back"
 msgid "Cover / back"
 msgstr "Retroceder"
 
-#: engines/scumm/metaengine.cpp:1224
+#: engines/scumm/metaengine.cpp:1241
+#, fuzzy
+#| msgid "Skip dialogue"
+msgid "Skip / back"
+msgstr "Ignorar frase"
+
+#: engines/scumm/metaengine.cpp:1248
 #, fuzzy
 msgid "Skip / menu"
 msgstr "~M~enu principal"
diff --git a/po/ro.po b/po/ro.po
index f1f1b3187ee..6bcec3d1781 100644
--- a/po/ro.po
+++ b/po/ro.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.10.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 14:35+0000\n"
+"POT-Creation-Date: 2026-06-01 16:27+0000\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: Automatically generated\n"
 "Language-Team: none\n"
@@ -1013,7 +1013,7 @@ msgstr ""
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1064
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1076
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1034,7 +1034,7 @@ msgstr ""
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1031
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1043
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1055,7 +1055,7 @@ msgstr ""
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1044
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1056
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1075,7 +1075,7 @@ msgstr ""
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1051
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1063
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3588,6 +3588,7 @@ msgid "Game not implemented"
 msgstr ""
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
+#: engines/scumm/metaengine.cpp:1026
 msgid "Default game keymappings"
 msgstr ""
 
@@ -6794,7 +6795,7 @@ msgstr ""
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1090 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1102 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -7441,7 +7442,7 @@ msgstr ""
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1077
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1089
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -12391,84 +12392,88 @@ msgstr ""
 msgid "All levels will be available without requiring passwords"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1025
+#: engines/scumm/metaengine.cpp:1037
 msgid "Down left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1038
+#: engines/scumm/metaengine.cpp:1050
 msgid "Down right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1058
+#: engines/scumm/metaengine.cpp:1070
 msgid "Up left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1071
+#: engines/scumm/metaengine.cpp:1083
 msgid "Up right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1083
+#: engines/scumm/metaengine.cpp:1095
 msgid "Switch weapon"
 msgstr ""
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1097
+#: engines/scumm/metaengine.cpp:1109
 msgid "Win the bike fight cheat"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1106
+#: engines/scumm/metaengine.cpp:1118
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1108 engines/scumm/metaengine.cpp:1170
+#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1182
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1113 engines/scumm/metaengine.cpp:1175
+#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1187
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1118 engines/scumm/metaengine.cpp:1180
+#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1192
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1123 engines/scumm/metaengine.cpp:1185
+#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1197
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1128 engines/scumm/metaengine.cpp:1190
+#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
 msgid "Stick up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1134 engines/scumm/metaengine.cpp:1196
+#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1209
 msgid "Stick down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1216
 msgid "Stick left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1208
+#: engines/scumm/metaengine.cpp:1158 engines/scumm/metaengine.cpp:1223
 msgid "Stick right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1214
+#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1230
 msgid "Fire / select"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1157
+#: engines/scumm/metaengine.cpp:1169
 msgid "Back / skip"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1168
+#: engines/scumm/metaengine.cpp:1180
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1219
+#: engines/scumm/metaengine.cpp:1235
 msgid "Cover / back"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1224
+#: engines/scumm/metaengine.cpp:1241
+msgid "Skip / back"
+msgstr ""
+
+#: engines/scumm/metaengine.cpp:1248
 msgid "Skip / menu"
 msgstr ""
 
diff --git a/po/ru_RU.po b/po/ru_RU.po
index 04732a46262..58794bf2592 100644
--- a/po/ru_RU.po
+++ b/po/ru_RU.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.8.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 14:35+0000\n"
+"POT-Creation-Date: 2026-06-01 16:27+0000\n"
 "PO-Revision-Date: 2026-05-06 08:54+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Russian <https://translations.scummvm.org/projects/scummvm/"
@@ -1051,7 +1051,7 @@ msgstr "????????????????????????????"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1064
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1076
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1072,7 +1072,7 @@ msgstr "??????????"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1031
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1043
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1093,7 +1093,7 @@ msgstr "????????"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1044
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1056
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1113,7 +1113,7 @@ msgstr "??????????"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1051
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1063
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3896,6 +3896,7 @@ msgid "Game not implemented"
 msgstr "???????? ???? ??????????????????????"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
+#: engines/scumm/metaengine.cpp:1026
 msgid "Default game keymappings"
 msgstr "?????????????????? ???????????? ???????? ???? ??????????????????"
 
@@ -7565,7 +7566,7 @@ msgstr "?????????????????????? ??????"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1090 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1102 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -8228,7 +8229,7 @@ msgstr "???????????????? / ??????????????????????????????????"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1077
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1089
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -13555,98 +13556,104 @@ msgstr ""
 msgid "All levels will be available without requiring passwords"
 msgstr "?????? ???????????? ?????????? ???????????????? ?????? ????????."
 
-#: engines/scumm/metaengine.cpp:1025
+#: engines/scumm/metaengine.cpp:1037
 msgid "Down left"
 msgstr "????????-??????????"
 
-#: engines/scumm/metaengine.cpp:1038
+#: engines/scumm/metaengine.cpp:1050
 msgid "Down right"
 msgstr "????????-????????????"
 
-#: engines/scumm/metaengine.cpp:1058
+#: engines/scumm/metaengine.cpp:1070
 msgid "Up left"
 msgstr "??????????-??????????"
 
-#: engines/scumm/metaengine.cpp:1071
+#: engines/scumm/metaengine.cpp:1083
 msgid "Up right"
 msgstr "??????????-????????????"
 
-#: engines/scumm/metaengine.cpp:1083
+#: engines/scumm/metaengine.cpp:1095
 msgid "Switch weapon"
 msgstr "?????????????????????? ????????????"
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1097
+#: engines/scumm/metaengine.cpp:1109
 msgid "Win the bike fight cheat"
 msgstr "?????? ?????? ???????????????? ???????? ???? ????????????"
 
-#: engines/scumm/metaengine.cpp:1106
+#: engines/scumm/metaengine.cpp:1118
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1108 engines/scumm/metaengine.cpp:1170
+#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1182
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1113 engines/scumm/metaengine.cpp:1175
+#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1187
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1118 engines/scumm/metaengine.cpp:1180
+#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1192
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1123 engines/scumm/metaengine.cpp:1185
+#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1197
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1128 engines/scumm/metaengine.cpp:1190
+#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "??????????????"
 
-#: engines/scumm/metaengine.cpp:1134 engines/scumm/metaengine.cpp:1196
+#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1209
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "???????????????????? ????????"
 
-#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1216
 #, fuzzy
 #| msgid "Slide left"
 msgid "Stick left"
 msgstr "?????????????????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1208
+#: engines/scumm/metaengine.cpp:1158 engines/scumm/metaengine.cpp:1223
 #, fuzzy
 #| msgid "Slide right"
 msgid "Stick right"
 msgstr "?????????????????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1214
+#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1230
 #, fuzzy
 #| msgid "Move / Select"
 msgid "Fire / select"
 msgstr "???????? / ??????????????"
 
-#: engines/scumm/metaengine.cpp:1157
+#: engines/scumm/metaengine.cpp:1169
 #, fuzzy
 #| msgid "Backspace"
 msgid "Back / skip"
 msgstr "Backspace"
 
-#: engines/scumm/metaengine.cpp:1168
+#: engines/scumm/metaengine.cpp:1180
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1219
+#: engines/scumm/metaengine.cpp:1235
 #, fuzzy
 #| msgid "Move back"
 msgid "Cover / back"
 msgstr "???????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1224
+#: engines/scumm/metaengine.cpp:1241
+#, fuzzy
+#| msgid "Skip walk"
+msgid "Skip / back"
+msgstr "???????????????????? ????????????"
+
+#: engines/scumm/metaengine.cpp:1248
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu"
diff --git a/po/sv_SE.po b/po/sv_SE.po
index 48b00d26343..8fb162004d5 100644
--- a/po/sv_SE.po
+++ b/po/sv_SE.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.5.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 14:35+0000\n"
+"POT-Creation-Date: 2026-06-01 16:27+0000\n"
 "PO-Revision-Date: 2026-04-19 13:22+0000\n"
 "Last-Translator: Daniel Nylander <po at danielnylander.se>\n"
 "Language-Team: Swedish <https://translations.scummvm.org/projects/scummvm/"
@@ -1053,7 +1053,7 @@ msgstr "Interagera"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1064
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1076
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1074,7 +1074,7 @@ msgstr "Upp"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1031
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1043
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1095,7 +1095,7 @@ msgstr "Ner"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1044
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1056
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1115,7 +1115,7 @@ msgstr "V??nster"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1051
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1063
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3886,6 +3886,7 @@ msgid "Game not implemented"
 msgstr "Spelet ??r inte implementerat"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
+#: engines/scumm/metaengine.cpp:1026
 msgid "Default game keymappings"
 msgstr "Standardinst??llningar f??r tangentbordskommandon i spelet"
 
@@ -7540,7 +7541,7 @@ msgstr "V??xla strid"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1090 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1102 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -8208,7 +8209,7 @@ msgstr "Prata/Interagera"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1077
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1089
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -13604,98 +13605,104 @@ msgstr ""
 msgid "All levels will be available without requiring passwords"
 msgstr "Alla niv??er kommer att finnas tillg??ngliga att spela."
 
-#: engines/scumm/metaengine.cpp:1025
+#: engines/scumm/metaengine.cpp:1037
 msgid "Down left"
 msgstr "Ned??t v??nster"
 
-#: engines/scumm/metaengine.cpp:1038
+#: engines/scumm/metaengine.cpp:1050
 msgid "Down right"
 msgstr "Ned??t h??ger"
 
-#: engines/scumm/metaengine.cpp:1058
+#: engines/scumm/metaengine.cpp:1070
 msgid "Up left"
 msgstr "Upp??t v??nster"
 
-#: engines/scumm/metaengine.cpp:1071
+#: engines/scumm/metaengine.cpp:1083
 msgid "Up right"
 msgstr "Upp??t h??ger"
 
-#: engines/scumm/metaengine.cpp:1083
+#: engines/scumm/metaengine.cpp:1095
 msgid "Switch weapon"
 msgstr "Byt vapen"
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1097
+#: engines/scumm/metaengine.cpp:1109
 msgid "Win the bike fight cheat"
 msgstr "Vinn cykelkampsfusket"
 
-#: engines/scumm/metaengine.cpp:1106
+#: engines/scumm/metaengine.cpp:1118
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1108 engines/scumm/metaengine.cpp:1170
+#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1182
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1113 engines/scumm/metaengine.cpp:1175
+#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1187
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1118 engines/scumm/metaengine.cpp:1180
+#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1192
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1123 engines/scumm/metaengine.cpp:1185
+#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1197
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1128 engines/scumm/metaengine.cpp:1190
+#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "Ta"
 
-#: engines/scumm/metaengine.cpp:1134 engines/scumm/metaengine.cpp:1196
+#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1209
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "Bl??ddra ned??t"
 
-#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1216
 #, fuzzy
 #| msgid "Slide left"
 msgid "Stick left"
 msgstr "Glid v??nster"
 
-#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1208
+#: engines/scumm/metaengine.cpp:1158 engines/scumm/metaengine.cpp:1223
 #, fuzzy
 #| msgid "Slide right"
 msgid "Stick right"
 msgstr "Glid h??ger"
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1214
+#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1230
 #, fuzzy
 #| msgid "Move / Select"
 msgid "Fire / select"
 msgstr "Flytta / V??lj"
 
-#: engines/scumm/metaengine.cpp:1157
+#: engines/scumm/metaengine.cpp:1169
 #, fuzzy
 #| msgid "Backspace"
 msgid "Back / skip"
 msgstr "Backsteg"
 
-#: engines/scumm/metaengine.cpp:1168
+#: engines/scumm/metaengine.cpp:1180
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1219
+#: engines/scumm/metaengine.cpp:1235
 #, fuzzy
 #| msgid "Move back"
 msgid "Cover / back"
 msgstr "Stega bak??t"
 
-#: engines/scumm/metaengine.cpp:1224
+#: engines/scumm/metaengine.cpp:1241
+#, fuzzy
+#| msgid "Skip walk"
+msgid "Skip / back"
+msgstr "Hoppa ??ver g??ng"
+
+#: engines/scumm/metaengine.cpp:1248
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu"
diff --git a/po/tr.po b/po/tr.po
index fb298fe5471..f56b28e8f0a 100644
--- a/po/tr.po
+++ b/po/tr.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 14:35+0000\n"
+"POT-Creation-Date: 2026-06-01 16:27+0000\n"
 "PO-Revision-Date: 2024-10-10 12:33+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Turkish <https://translations.scummvm.org/projects/scummvm/"
@@ -1057,7 +1057,7 @@ msgstr "Etkile??im"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1064
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1076
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1078,7 +1078,7 @@ msgstr "Yukar??"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1031
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1043
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1099,7 +1099,7 @@ msgstr "A??a????"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1044
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1056
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1119,7 +1119,7 @@ msgstr "Sol"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1051
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1063
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3832,6 +3832,7 @@ msgid "Game not implemented"
 msgstr "Oyun ID si desteklenmiyor"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
+#: engines/scumm/metaengine.cpp:1026
 #, fuzzy
 msgid "Default game keymappings"
 msgstr "A~y~arlar..."
@@ -7193,7 +7194,7 @@ msgstr "Altyaz??lar"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1090 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1102 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -7903,7 +7904,7 @@ msgstr "Etkile??im"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1077
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1089
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -13260,97 +13261,103 @@ msgstr ""
 msgid "All levels will be available without requiring passwords"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1025
+#: engines/scumm/metaengine.cpp:1037
 #, fuzzy
 msgid "Down left"
 msgstr "Sol"
 
-#: engines/scumm/metaengine.cpp:1038
+#: engines/scumm/metaengine.cpp:1050
 #, fuzzy
 msgid "Down right"
 msgstr "Sa??"
 
-#: engines/scumm/metaengine.cpp:1058
+#: engines/scumm/metaengine.cpp:1070
 #, fuzzy
 msgid "Up left"
 msgstr "Sol"
 
-#: engines/scumm/metaengine.cpp:1071
+#: engines/scumm/metaengine.cpp:1083
 #, fuzzy
 msgid "Up right"
 msgstr "Sa??"
 
-#: engines/scumm/metaengine.cpp:1083
+#: engines/scumm/metaengine.cpp:1095
 #, fuzzy
 msgid "Switch weapon"
 msgstr "Oyunu se??"
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1097
+#: engines/scumm/metaengine.cpp:1109
 msgid "Win the bike fight cheat"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1106
+#: engines/scumm/metaengine.cpp:1118
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1108 engines/scumm/metaengine.cpp:1170
+#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1182
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1113 engines/scumm/metaengine.cpp:1175
+#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1187
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1118 engines/scumm/metaengine.cpp:1180
+#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1192
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1123 engines/scumm/metaengine.cpp:1185
+#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1197
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1128 engines/scumm/metaengine.cpp:1190
+#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
 msgid "Stick up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1134 engines/scumm/metaengine.cpp:1196
+#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1209
 #, fuzzy
 msgid "Stick down"
 msgstr "A??a????"
 
-#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1216
 #, fuzzy
 msgid "Stick left"
 msgstr "Sa??"
 
-#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1208
+#: engines/scumm/metaengine.cpp:1158 engines/scumm/metaengine.cpp:1223
 #, fuzzy
 msgid "Stick right"
 msgstr "Sa??"
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1214
+#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1230
 #, fuzzy
 msgid "Fire / select"
 msgstr "Sol"
 
-#: engines/scumm/metaengine.cpp:1157
+#: engines/scumm/metaengine.cpp:1169
 #, fuzzy
 #| msgid "Backend"
 msgid "Back / skip"
 msgstr "Altyap??"
 
-#: engines/scumm/metaengine.cpp:1168
+#: engines/scumm/metaengine.cpp:1180
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1219
+#: engines/scumm/metaengine.cpp:1235
 #, fuzzy
 #| msgid "Backend"
 msgid "Cover / back"
 msgstr "Altyap??"
 
-#: engines/scumm/metaengine.cpp:1224
+#: engines/scumm/metaengine.cpp:1241
+#, fuzzy
+#| msgid "Show hidden files"
+msgid "Skip / back"
+msgstr "Gizli dosyalar?? g??ster"
+
+#: engines/scumm/metaengine.cpp:1248
 #, fuzzy
 msgid "Skip / menu"
 msgstr "??ptal"
diff --git a/po/uk_UA.po b/po/uk_UA.po
index f5a32f3b53b..fd2796a5a99 100644
--- a/po/uk_UA.po
+++ b/po/uk_UA.po
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.9.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 14:35+0000\n"
+"POT-Creation-Date: 2026-06-01 16:27+0000\n"
 "PO-Revision-Date: 2026-05-06 08:54+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Ukrainian <https://translations.scummvm.org/projects/scummvm/"
@@ -1055,7 +1055,7 @@ msgstr "??????????????????"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1064
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1076
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1076,7 +1076,7 @@ msgstr "????????????"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1031
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1043
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1097,7 +1097,7 @@ msgstr "????????????"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1044
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1056
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1117,7 +1117,7 @@ msgstr "????????????"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1051
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1063
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3890,6 +3890,7 @@ msgid "Game not implemented"
 msgstr "?????? ???? ??????????????????????"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
+#: engines/scumm/metaengine.cpp:1026
 msgid "Default game keymappings"
 msgstr "???????? ???????????? ?????? ???? ??????????????????????"
 
@@ -7545,7 +7546,7 @@ msgstr "???????????????????? ?????????? ??????"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1090 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1102 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -8209,7 +8210,7 @@ msgstr "???????????????? / ??????????????????????"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1077
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1089
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -13514,98 +13515,104 @@ msgstr ""
 msgid "All levels will be available without requiring passwords"
 msgstr "?????? ?????????? ???????????? ???????????????? ?????? ??????."
 
-#: engines/scumm/metaengine.cpp:1025
+#: engines/scumm/metaengine.cpp:1037
 msgid "Down left"
 msgstr "????????????-????????????"
 
-#: engines/scumm/metaengine.cpp:1038
+#: engines/scumm/metaengine.cpp:1050
 msgid "Down right"
 msgstr "????????????-??????????????"
 
-#: engines/scumm/metaengine.cpp:1058
+#: engines/scumm/metaengine.cpp:1070
 msgid "Up left"
 msgstr "????????????-????????????"
 
-#: engines/scumm/metaengine.cpp:1071
+#: engines/scumm/metaengine.cpp:1083
 msgid "Up right"
 msgstr "????????????-??????????????"
 
-#: engines/scumm/metaengine.cpp:1083
+#: engines/scumm/metaengine.cpp:1095
 msgid "Switch weapon"
 msgstr "???????????????????? ??????????"
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1097
+#: engines/scumm/metaengine.cpp:1109
 msgid "Win the bike fight cheat"
 msgstr "?????? ???????????????????? ?????? ???? ??????????????"
 
-#: engines/scumm/metaengine.cpp:1106
+#: engines/scumm/metaengine.cpp:1118
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1108 engines/scumm/metaengine.cpp:1170
+#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1182
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1113 engines/scumm/metaengine.cpp:1175
+#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1187
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1118 engines/scumm/metaengine.cpp:1180
+#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1192
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1123 engines/scumm/metaengine.cpp:1185
+#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1197
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1128 engines/scumm/metaengine.cpp:1190
+#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "??????????????????"
 
-#: engines/scumm/metaengine.cpp:1134 engines/scumm/metaengine.cpp:1196
+#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1209
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "?????????????????????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1216
 #, fuzzy
 #| msgid "Slide left"
 msgid "Stick left"
 msgstr "???????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1208
+#: engines/scumm/metaengine.cpp:1158 engines/scumm/metaengine.cpp:1223
 #, fuzzy
 #| msgid "Slide right"
 msgid "Stick right"
 msgstr "???????? ??????????????"
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1214
+#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1230
 #, fuzzy
 #| msgid "Move / Select"
 msgid "Fire / select"
 msgstr "???????????????? / ??????????????"
 
-#: engines/scumm/metaengine.cpp:1157
+#: engines/scumm/metaengine.cpp:1169
 #, fuzzy
 #| msgid "Backspace"
 msgid "Back / skip"
 msgstr "Backspace"
 
-#: engines/scumm/metaengine.cpp:1168
+#: engines/scumm/metaengine.cpp:1180
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1219
+#: engines/scumm/metaengine.cpp:1235
 #, fuzzy
 #| msgid "Move back"
 msgid "Cover / back"
 msgstr "???????????????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1224
+#: engines/scumm/metaengine.cpp:1241
+#, fuzzy
+#| msgid "Skip walk"
+msgid "Skip / back"
+msgstr "???????????????????? ??????????????"
+
+#: engines/scumm/metaengine.cpp:1248
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu"
diff --git a/po/zh.po b/po/zh.po
index e83a026f05d..df0d00a1345 100644
--- a/po/zh.po
+++ b/po/zh.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 14:35+0000\n"
+"POT-Creation-Date: 2026-06-01 16:27+0000\n"
 "PO-Revision-Date: 2024-05-14 12:42+0000\n"
 "Last-Translator: ?????? <clonewith at qq.com>\n"
 "Language-Team: Chinese <https://translations.scummvm.org/projects/scummvm/"
@@ -1033,7 +1033,7 @@ msgstr "??????"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1064
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1076
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1054,7 +1054,7 @@ msgstr "??????"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1031
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1043
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1075,7 +1075,7 @@ msgstr "??????"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1044
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1056
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1095,7 +1095,7 @@ msgstr "??????"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1051
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1063
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3645,6 +3645,7 @@ msgid "Game not implemented"
 msgstr ""
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
+#: engines/scumm/metaengine.cpp:1026
 #, fuzzy
 #| msgid "Game Options"
 msgid "Default game keymappings"
@@ -6953,7 +6954,7 @@ msgstr "??????????????????"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1090 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1102 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -7674,7 +7675,7 @@ msgstr "??????"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1077
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1089
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -13085,96 +13086,102 @@ msgstr ""
 msgid "All levels will be available without requiring passwords"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1025
+#: engines/scumm/metaengine.cpp:1037
 #, fuzzy
 msgid "Down left"
 msgstr "??????"
 
-#: engines/scumm/metaengine.cpp:1038
+#: engines/scumm/metaengine.cpp:1050
 #, fuzzy
 msgid "Down right"
 msgstr "??????"
 
-#: engines/scumm/metaengine.cpp:1058
+#: engines/scumm/metaengine.cpp:1070
 #, fuzzy
 msgid "Up left"
 msgstr "??????"
 
-#: engines/scumm/metaengine.cpp:1071
+#: engines/scumm/metaengine.cpp:1083
 #, fuzzy
 msgid "Up right"
 msgstr "??????"
 
-#: engines/scumm/metaengine.cpp:1083
+#: engines/scumm/metaengine.cpp:1095
 msgid "Switch weapon"
 msgstr ""
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1097
+#: engines/scumm/metaengine.cpp:1109
 msgid "Win the bike fight cheat"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1106
+#: engines/scumm/metaengine.cpp:1118
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1108 engines/scumm/metaengine.cpp:1170
+#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1182
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1113 engines/scumm/metaengine.cpp:1175
+#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1187
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1118 engines/scumm/metaengine.cpp:1180
+#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1192
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1123 engines/scumm/metaengine.cpp:1185
+#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1197
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1128 engines/scumm/metaengine.cpp:1190
+#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
 msgid "Stick up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1134 engines/scumm/metaengine.cpp:1196
+#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1209
 #, fuzzy
 msgid "Stick down"
 msgstr "??????"
 
-#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1216
 #, fuzzy
 msgid "Stick left"
 msgstr "??????"
 
-#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1208
+#: engines/scumm/metaengine.cpp:1158 engines/scumm/metaengine.cpp:1223
 #, fuzzy
 msgid "Stick right"
 msgstr "??????"
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1214
+#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1230
 #, fuzzy
 msgid "Fire / select"
 msgstr "??????"
 
-#: engines/scumm/metaengine.cpp:1157
+#: engines/scumm/metaengine.cpp:1169
 #, fuzzy
 #| msgid "Backspace"
 msgid "Back / skip"
 msgstr "??????"
 
-#: engines/scumm/metaengine.cpp:1168
+#: engines/scumm/metaengine.cpp:1180
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1219
+#: engines/scumm/metaengine.cpp:1235
 #, fuzzy
 #| msgid "Backend"
 msgid "Cover / back"
 msgstr "??????"
 
-#: engines/scumm/metaengine.cpp:1224
+#: engines/scumm/metaengine.cpp:1241
+#, fuzzy
+#| msgid "Show hidden files"
+msgid "Skip / back"
+msgstr "?????????????????????"
+
+#: engines/scumm/metaengine.cpp:1248
 #, fuzzy
 #| msgid "Skip autosave"
 msgid "Skip / menu"
diff --git a/po/zh_Hans.po b/po/zh_Hans.po
index 6ae91f8ee85..4b0015f64f2 100644
--- a/po/zh_Hans.po
+++ b/po/zh_Hans.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.9.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 14:35+0000\n"
+"POT-Creation-Date: 2026-06-01 16:27+0000\n"
 "PO-Revision-Date: 2024-12-01 22:18+0000\n"
 "Last-Translator: ?????? <clonewith at qq.com>\n"
 "Language-Team: Chinese (Simplified Han script) <https://"
@@ -1034,7 +1034,7 @@ msgstr "??????"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1064
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1076
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1055,7 +1055,7 @@ msgstr "??????"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1031
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1043
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1076,7 +1076,7 @@ msgstr "??????"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1044
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1056
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1096,7 +1096,7 @@ msgstr "??????"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1051
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1063
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3846,6 +3846,7 @@ msgid "Game not implemented"
 msgstr "???????????????"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
+#: engines/scumm/metaengine.cpp:1026
 #, fuzzy
 #| msgid "Default game keymap"
 msgid "Default game keymappings"
@@ -7628,7 +7629,7 @@ msgstr "????????????"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1090 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1102 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -8383,7 +8384,7 @@ msgstr "??????/??????"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1077
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1089
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -14056,106 +14057,112 @@ msgstr ""
 msgid "All levels will be available without requiring passwords"
 msgstr "??????????????????????????????"
 
-#: engines/scumm/metaengine.cpp:1025
+#: engines/scumm/metaengine.cpp:1037
 #, fuzzy
 #| msgid "Down Left"
 msgid "Down left"
 msgstr "??????"
 
-#: engines/scumm/metaengine.cpp:1038
+#: engines/scumm/metaengine.cpp:1050
 #, fuzzy
 #| msgid "Down Right"
 msgid "Down right"
 msgstr "??????"
 
-#: engines/scumm/metaengine.cpp:1058
+#: engines/scumm/metaengine.cpp:1070
 #, fuzzy
 #| msgid "Upper left item"
 msgid "Up left"
 msgstr "???????????????"
 
-#: engines/scumm/metaengine.cpp:1071
+#: engines/scumm/metaengine.cpp:1083
 #, fuzzy
 #| msgid "Up Right"
 msgid "Up right"
 msgstr "??????"
 
-#: engines/scumm/metaengine.cpp:1083
+#: engines/scumm/metaengine.cpp:1095
 msgid "Switch weapon"
 msgstr "????????????"
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1097
+#: engines/scumm/metaengine.cpp:1109
 msgid "Win the bike fight cheat"
 msgstr "???????????????????????????"
 
-#: engines/scumm/metaengine.cpp:1106
+#: engines/scumm/metaengine.cpp:1118
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1108 engines/scumm/metaengine.cpp:1170
+#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1182
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1113 engines/scumm/metaengine.cpp:1175
+#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1187
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1118 engines/scumm/metaengine.cpp:1180
+#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1192
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1123 engines/scumm/metaengine.cpp:1185
+#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1197
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1128 engines/scumm/metaengine.cpp:1190
+#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "??????"
 
-#: engines/scumm/metaengine.cpp:1134 engines/scumm/metaengine.cpp:1196
+#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1209
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "????????????"
 
-#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1216
 #, fuzzy
 #| msgid "Slide Left"
 msgid "Stick left"
 msgstr "????????????"
 
-#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1208
+#: engines/scumm/metaengine.cpp:1158 engines/scumm/metaengine.cpp:1223
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick right"
 msgstr "????????????"
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1214
+#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1230
 #, fuzzy
 #| msgid "Move left"
 msgid "Fire / select"
 msgstr "????????????"
 
-#: engines/scumm/metaengine.cpp:1157
+#: engines/scumm/metaengine.cpp:1169
 #, fuzzy
 #| msgid "Backspace"
 msgid "Back / skip"
 msgstr "??????"
 
-#: engines/scumm/metaengine.cpp:1168
+#: engines/scumm/metaengine.cpp:1180
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1219
+#: engines/scumm/metaengine.cpp:1235
 #, fuzzy
 #| msgid "Move Back"
 msgid "Cover / back"
 msgstr "????????????"
 
-#: engines/scumm/metaengine.cpp:1224
+#: engines/scumm/metaengine.cpp:1241
+#, fuzzy
+#| msgid "Skip dialogue"
+msgid "Skip / back"
+msgstr "????????????"
+
+#: engines/scumm/metaengine.cpp:1248
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu"
diff --git a/po/zh_Hant.po b/po/zh_Hant.po
index d979bd21073..86089e77ef8 100644
--- a/po/zh_Hant.po
+++ b/po/zh_Hant.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.9.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 14:35+0000\n"
+"POT-Creation-Date: 2026-06-01 16:27+0000\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: Automatically generated\n"
 "Language-Team: none\n"
@@ -1014,7 +1014,7 @@ msgstr ""
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1064
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1076
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1035,7 +1035,7 @@ msgstr ""
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1031
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1043
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1056,7 +1056,7 @@ msgstr ""
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1044
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1056
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1076,7 +1076,7 @@ msgstr ""
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1051
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1063
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3589,6 +3589,7 @@ msgid "Game not implemented"
 msgstr ""
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
+#: engines/scumm/metaengine.cpp:1026
 msgid "Default game keymappings"
 msgstr ""
 
@@ -6795,7 +6796,7 @@ msgstr ""
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1090 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1102 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -7442,7 +7443,7 @@ msgstr ""
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1077
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1089
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -12392,84 +12393,88 @@ msgstr ""
 msgid "All levels will be available without requiring passwords"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1025
+#: engines/scumm/metaengine.cpp:1037
 msgid "Down left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1038
+#: engines/scumm/metaengine.cpp:1050
 msgid "Down right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1058
+#: engines/scumm/metaengine.cpp:1070
 msgid "Up left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1071
+#: engines/scumm/metaengine.cpp:1083
 msgid "Up right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1083
+#: engines/scumm/metaengine.cpp:1095
 msgid "Switch weapon"
 msgstr ""
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1097
+#: engines/scumm/metaengine.cpp:1109
 msgid "Win the bike fight cheat"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1106
+#: engines/scumm/metaengine.cpp:1118
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1108 engines/scumm/metaengine.cpp:1170
+#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1182
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1113 engines/scumm/metaengine.cpp:1175
+#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1187
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1118 engines/scumm/metaengine.cpp:1180
+#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1192
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1123 engines/scumm/metaengine.cpp:1185
+#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1197
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1128 engines/scumm/metaengine.cpp:1190
+#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
 msgid "Stick up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1134 engines/scumm/metaengine.cpp:1196
+#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1209
 msgid "Stick down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1216
 msgid "Stick left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1208
+#: engines/scumm/metaengine.cpp:1158 engines/scumm/metaengine.cpp:1223
 msgid "Stick right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1214
+#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1230
 msgid "Fire / select"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1157
+#: engines/scumm/metaengine.cpp:1169
 msgid "Back / skip"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1168
+#: engines/scumm/metaengine.cpp:1180
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1219
+#: engines/scumm/metaengine.cpp:1235
 msgid "Cover / back"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1224
+#: engines/scumm/metaengine.cpp:1241
+msgid "Skip / back"
+msgstr ""
+
+#: engines/scumm/metaengine.cpp:1248
 msgid "Skip / menu"
 msgstr ""
 



From noreply at scummvm.org  Mon Jun  1 18:06:37 2026
From: noreply at scummvm.org (sev-)
Date: Mon,  1 Jun 2026 18:06:37 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 bbc58f2b47eaca226b43cf7932429fe2fb603833
Message-ID: <20260601180637.685C6700591@ron.scummvm.net>

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

Summary:
0da70bcde1 DIRECTOR: Draw directToStage video in trails mode
682cd5f167 DIRECTOR: Fix invalid pointer in TextCastMember::writeSTXTResource
37fb0a2719 DIRECTOR: Add array guardrails in Movie::loadFileInfo
298b520c93 DIRECTOR: Manage all Archive lifetimes with SharePtr
53ac0b7ff3 DIRECTOR: Add Xtra + quirks for Pink Gear Collection
5085f88e77 DIRECTOR: Add guardrail to Symbol::operator==
9c211c8ff8 DIRECTOR: Improve accuracy of b_dontPassEvent
ff813b5efc DIRECTOR: Clear window modal flag in destructor
bbc58f2b47 DIRECTOR: Comment out crash-inducing movie export debug code


Commit: 0da70bcde1f0eb652afe66c1b5295bae102112b9
    https://github.com/scummvm/scummvm/commit/0da70bcde1f0eb652afe66c1b5295bae102112b9
Author: Scott Percival (code at moral.net.au)
Date: 2026-06-01T20:06:27+02:00

Commit Message:
DIRECTOR: Draw directToStage video in trails mode

Fixes rendering of title movie in Mean City.

Changed paths:
    engines/director/channel.cpp


diff --git a/engines/director/channel.cpp b/engines/director/channel.cpp
index 6e4bf08d985..2d5b3936faa 100644
--- a/engines/director/channel.cpp
+++ b/engines/director/channel.cpp
@@ -757,7 +757,9 @@ bool Channel::updateWidget() {
 }
 
 bool Channel::isTrail() {
-	return _sprite->_trails;
+	return _sprite->_trails || (_sprite->_cast &&
+			(_sprite->_cast->_type == kCastDigitalVideo) &&
+			(((DigitalVideoCastMember *)_sprite->_cast)->_directToStage));
 }
 
 int Channel::getMouseChar(int x, int y) {


Commit: 682cd5f167b2cfaecd39147b9e361ded492e4409
    https://github.com/scummvm/scummvm/commit/682cd5f167b2cfaecd39147b9e361ded492e4409
Author: Scott Percival (code at moral.net.au)
Date: 2026-06-01T20:06:27+02:00

Commit Message:
DIRECTOR: Fix invalid pointer in TextCastMember::writeSTXTResource

Changed paths:
    engines/director/castmember/text.cpp


diff --git a/engines/director/castmember/text.cpp b/engines/director/castmember/text.cpp
index 368843d0dca..35ed292e400 100644
--- a/engines/director/castmember/text.cpp
+++ b/engines/director/castmember/text.cpp
@@ -1046,7 +1046,8 @@ uint32 TextCastMember::writeSTXTResource(Common::SeekableWriteStream *writeStrea
 				// Ignoring height and ascent for now from FontStyle
 				uint16 temp;
 				style.formatStartOffset = pIndex;
-				const Common::u32char_type_t *s = _ftext.substr(it, 22).c_str();
+				Common::U32String data = _ftext.substr(it, 22);
+				const Common::u32char_type_t *s = data.c_str();
 
 				s = Graphics::readHex(&style.fontId, s, 4);
 				s = Graphics::readHex(&temp, s, 2);


Commit: 37fb0a2719e52951a6603a7d529d0e82165c7545
    https://github.com/scummvm/scummvm/commit/37fb0a2719e52951a6603a7d529d0e82165c7545
Author: Scott Percival (code at moral.net.au)
Date: 2026-06-01T20:06:27+02:00

Commit Message:
DIRECTOR: Add array guardrails in Movie::loadFileInfo

Changed paths:
    engines/director/movie.cpp


diff --git a/engines/director/movie.cpp b/engines/director/movie.cpp
index 0d644f9146f..edea2f6e335 100644
--- a/engines/director/movie.cpp
+++ b/engines/director/movie.cpp
@@ -369,7 +369,8 @@ void Movie::loadFileInfo(Common::SeekableReadStreamEndian &stream) {
 	_allowOutdatedLingo = (fileInfo.flags & kMovieFlagAllowOutdatedLingo) != 0;
 	_remapPalettesWhenNeeded = (fileInfo.flags & kMovieFlagRemapPalettesWhenNeeded) != 0;
 
-	_script = fileInfo.strings[0].readString(false);
+	if (fileInfo.strings.size() >= 1)
+		_script = fileInfo.strings[0].readString(false);
 
 	if (!_script.empty() && ConfMan.getBool("dump_scripts"))
 		_cast->dumpScript(_script.c_str(), kMovieScript, 0);
@@ -377,12 +378,15 @@ void Movie::loadFileInfo(Common::SeekableReadStreamEndian &stream) {
 	if (!_script.empty())
 		_cast->_lingoArchive->addCode(_script, kMovieScript, 0, nullptr, kLPPTrimGarbage);
 
-	_changedBy = fileInfo.strings[1].readString();
-	_createdBy = fileInfo.strings[2].readString();
-	_origDirectory = fileInfo.strings[3].readString();
+	if (fileInfo.strings.size() >= 2)
+		_changedBy = fileInfo.strings[1].readString();
+	if (fileInfo.strings.size() >= 3)
+		_createdBy = fileInfo.strings[2].readString();
+	if (fileInfo.strings.size() >= 4)
+		_origDirectory = fileInfo.strings[3].readString();
 
 	uint16 preload = 0;
-	if (fileInfo.strings[4].len) {
+	if ((fileInfo.strings.size() >= 5) && fileInfo.strings[4].len) {
 		if (stream.isBE())
 			preload = READ_BE_INT16(fileInfo.strings[4].data);
 		else


Commit: 298b520c933c3cafb9a2e3ce9be83e72f2548ca5
    https://github.com/scummvm/scummvm/commit/298b520c933c3cafb9a2e3ce9be83e72f2548ca5
Author: Scott Percival (code at moral.net.au)
Date: 2026-06-01T20:06:27+02:00

Commit Message:
DIRECTOR: Manage all Archive lifetimes with SharePtr

For various reasons we need to keep Archives around across movie
changes and multiple windows (e.g. shared casts). Easiest way to deal
with the lifetimes is to reference count them, and the easiest way to
reference count them is to use SharedPtr.

Fixes missing toolbox lower window in Opera Fatal.

Changed paths:
    engines/director/cast.cpp
    engines/director/cast.h
    engines/director/castmember/palette.cpp
    engines/director/cursor.cpp
    engines/director/debugger.cpp
    engines/director/debugger/dt-lists.cpp
    engines/director/director.cpp
    engines/director/director.h
    engines/director/lingo/lingo-builtins.cpp
    engines/director/movie.cpp
    engines/director/movie.h
    engines/director/resource.cpp
    engines/director/sound.cpp
    engines/director/tests.cpp
    engines/director/window.cpp


diff --git a/engines/director/cast.cpp b/engines/director/cast.cpp
index 6739fd58afe..ef9b52d450d 100644
--- a/engines/director/cast.cpp
+++ b/engines/director/cast.cpp
@@ -303,7 +303,7 @@ bool Cast::eraseCastMember(int castId) {
 	return false;
 }
 
-void Cast::setArchive(Archive *archive) {
+void Cast::setArchive(Common::SharedPtr<Archive> archive) {
 	_castArchive = archive;
 
 	if (archive->hasResource(MKTAG('M', 'C', 'N', 'M'), 0)) {
diff --git a/engines/director/cast.h b/engines/director/cast.h
index 5eeef4221ea..6005bdd3562 100644
--- a/engines/director/cast.h
+++ b/engines/director/cast.h
@@ -90,8 +90,8 @@ public:
 	~Cast();
 
 	void loadArchive();
-	void setArchive(Archive *archive);
-	Archive *getArchive() const { return _castArchive; };
+	void setArchive(Common::SharedPtr<Archive> archive);
+	Common::SharedPtr<Archive> getArchive() const { return _castArchive; };
 	Common::String getMacName() const { return _macName; }
 	Common::String getCastName() const { return _castName; }
 	void setCastName(const Common::String &name) { _castName = name; }
@@ -158,7 +158,7 @@ private:
 	uint32 computeChecksum();
 
 public:
-	Archive *_castArchive;
+	Common::SharedPtr<Archive> _castArchive;
 	Common::Platform _platform;
 	uint16 _castLibID;
 	uint16 _libResourceId;
diff --git a/engines/director/castmember/palette.cpp b/engines/director/castmember/palette.cpp
index 83bb0892abd..0a59f067cd5 100644
--- a/engines/director/castmember/palette.cpp
+++ b/engines/director/castmember/palette.cpp
@@ -114,7 +114,7 @@ void PaletteCastMember::load() {
 	if (paletteId) {
 
 		uint32 tag = MKTAG('C', 'L', 'U', 'T');
-		Archive *arch = _cast->getArchive();
+		Archive *arch = _cast->getArchive().get();
 		if (arch->hasResource(tag, paletteId)) {
 			Common::SeekableReadStreamEndian *pal = arch->getResource(MKTAG('C', 'L', 'U', 'T'), paletteId);
 			debugC(2, kDebugImages, "PaletteCastMember::load(): linking palette id %d to cast index %d", paletteId, _castId);
diff --git a/engines/director/cursor.cpp b/engines/director/cursor.cpp
index 30fc64ea802..0738a2c35ac 100644
--- a/engines/director/cursor.cpp
+++ b/engines/director/cursor.cpp
@@ -215,13 +215,13 @@ void Cursor::readFromResource(Datum resourceId) {
 
 		Cast *cast = g_director->getCurrentMovie()->getCast();
 		if (cast && cast->getArchive()) {
-			readSuccessful = readFromArchive(cast->getArchive(), resourceId.asInt());
+			readSuccessful = readFromArchive(cast->getArchive().get(), resourceId.asInt());
 			if (readSuccessful)
 				break;
 		}
 
 		for (auto &it : g_director->_allOpenResFiles) {
-			readSuccessful = readFromArchive(g_director->_allSeenResFiles[it], resourceId.asInt());
+			readSuccessful = readFromArchive(g_director->_allSeenResFiles[it].get(), resourceId.asInt());
 			if (readSuccessful)
 				break;
 		}
@@ -229,7 +229,7 @@ void Cursor::readFromResource(Datum resourceId) {
 		// Cursors can be located in the main archive, which may not
 		// be in _allOpenResFiles
 		if (!readSuccessful && g_director->getPlatform() == Common::kPlatformMacintosh) {
-			readSuccessful = readFromArchive(g_director->getMainArchive(), resourceId.asInt());
+			readSuccessful = readFromArchive(g_director->getMainArchive().get(), resourceId.asInt());
 		}
 
 		// TODO: figure out where to read custom cursor in windows platform
diff --git a/engines/director/debugger.cpp b/engines/director/debugger.cpp
index 48223247e48..a7d346ac083 100644
--- a/engines/director/debugger.cpp
+++ b/engines/director/debugger.cpp
@@ -273,7 +273,7 @@ bool Debugger::cmdVersion(int argc, const char **argv) {
 bool Debugger::cmdInfo(int argc, const char **argv) {
 	Movie *movie = g_director->getCurrentMovie();
 	Score *score = movie->getScore();
-	Archive *archive = movie->getArchive();
+	Archive *archive = movie->getArchive().get();
 	Cast *cast = movie->getCast();
 	debugPrintf("Movie path: %s\n", archive->getPathName().toString(g_director->_dirSeparator).c_str());
 	debugPrintf("Movie file size: %d\n", archive->getFileSize());
diff --git a/engines/director/debugger/dt-lists.cpp b/engines/director/debugger/dt-lists.cpp
index f6eb6018138..a88aecd4615 100644
--- a/engines/director/debugger/dt-lists.cpp
+++ b/engines/director/debugger/dt-lists.cpp
@@ -297,7 +297,7 @@ void showArchive() {
 			ImGui::BeginChild("ChildL", ImVec2(ImGui::GetContentRegionAvail().x * 0.3f, ImGui::GetContentRegionAvail().y), ImGuiChildFlags_None);
 
 			for (auto &it : g_director->_allSeenResFiles) {
-				Archive *archive = it._value;
+				Archive *archive = it._value.get();
 
 				if (ImGui::TreeNode(archive->getPathName().toString().c_str())) {
 					Common::Array<uint32> typeList = archive->getResourceTypeList();
diff --git a/engines/director/director.cpp b/engines/director/director.cpp
index e8de3dbd040..fdc65339d5f 100644
--- a/engines/director/director.cpp
+++ b/engines/director/director.cpp
@@ -181,9 +181,7 @@ DirectorEngine::~DirectorEngine() {
 		_currentWindow = nullptr;
 	}
 	delete _wm;
-	for (auto &it : _allSeenResFiles) {
-		delete it._value;
-	}
+	_allSeenResFiles.clear();
 	for (uint i = 0; i < _winCursor.size(); i++)
 		delete _winCursor[i];
 
diff --git a/engines/director/director.h b/engines/director/director.h
index 0d4d2fbbe2a..6c83e8a7110 100644
--- a/engines/director/director.h
+++ b/engines/director/director.h
@@ -186,8 +186,8 @@ public:
 	void setCursorWindow(Window *window) { _cursorWindow = window; }
 	Movie *getCurrentMovie() const;
 	void setCurrentMovie(Movie *movie);
-	Archive *getMainArchive() const { return _mainArchive; }
-	void setMainArchive(Archive *archive) { _mainArchive = archive; }
+	Common::SharedPtr<Archive> getMainArchive() const { return _mainArchive; }
+	void setMainArchive(Common::SharedPtr<Archive> archive) { _mainArchive = archive; }
 	Common::String getCurrentPath() const;
 	Common::String getCurrentAbsolutePath();
 	Common::Path getStartupPath() const;
@@ -230,7 +230,7 @@ public:
 	Common::CodePage getPlatformEncoding();
 
 	Archive *createArchive();
-	Archive *openArchive(const Common::Path &movie);
+	Common::SharedPtr<Archive> openArchive(const Common::Path &movie);
 	void addArchiveToOpenList(const Common::Path &path);
 	Archive *loadEXE(const Common::Path &movie);
 	Archive *loadEXEv3(Common::SeekableReadStream *stream);
@@ -271,15 +271,15 @@ public:
 	bool _centerStage;
 	char _dirSeparator;
 	bool _fixStageSize;
-	Archive *_mainArchive;
+	Common::SharedPtr<Archive> _mainArchive;
 	Common::Rect _fixStageRect;
 	Common::List<Common::String> _extraSearchPath;
 	bool _emulateMultiButtonMouse;
 
 	// Owner of all Archive objects.
-	Common::HashMap<Common::Path, Archive *, Common::Path::IgnoreCaseAndMac_Hash, Common::Path::IgnoreCaseAndMac_EqualTo> _allSeenResFiles;
+	Common::HashMap<Common::Path, Common::SharedPtr<Archive>, Common::Path::IgnoreCaseAndMac_Hash, Common::Path::IgnoreCaseAndMac_EqualTo> _allSeenResFiles;
 	// Handles to resource files that were opened by OpenResFile.
-	Common::HashMap<Common::Path, Archive *, Common::Path::IgnoreCaseAndMac_Hash, Common::Path::IgnoreCaseAndMac_EqualTo> _openResFiles;
+	Common::HashMap<Common::Path, Common::SharedPtr<Archive>, Common::Path::IgnoreCaseAndMac_Hash, Common::Path::IgnoreCaseAndMac_EqualTo> _openResFiles;
 	// List of all currently open resource files
 	Common::List<Common::Path> _allOpenResFiles;
 
diff --git a/engines/director/lingo/lingo-builtins.cpp b/engines/director/lingo/lingo-builtins.cpp
index de210df178c..98924a725a6 100644
--- a/engines/director/lingo/lingo-builtins.cpp
+++ b/engines/director/lingo/lingo-builtins.cpp
@@ -1806,13 +1806,11 @@ void LB::b_openResFile(int nargs) {
  	}
 
 	if (!g_director->_allSeenResFiles.contains(resPath)) {
-		MacArchive *arch = new MacArchive();
+		Common::SharedPtr<Archive> arch(new MacArchive());
 		if (arch->openFile(findPath(resPath))) {
 			g_director->_openResFiles.setVal(resPath, arch);
 			g_director->_allSeenResFiles.setVal(resPath, arch);
 			g_director->addArchiveToOpenList(resPath);
-		} else {
-			delete arch;
 		}
 	}
 }
diff --git a/engines/director/movie.cpp b/engines/director/movie.cpp
index edea2f6e335..218e12f5906 100644
--- a/engines/director/movie.cpp
+++ b/engines/director/movie.cpp
@@ -117,7 +117,7 @@ Movie::~Movie() {
 	delete _score;
 }
 
-void Movie::setArchive(Archive *archive) {
+void Movie::setArchive(Common::SharedPtr<Archive> archive) {
 	_movieArchive = archive;
 
 	if (archive->hasResource(MKTAG('M', 'C', 'N', 'M'), 0)) {
@@ -169,7 +169,7 @@ void Movie::loadCastLibMapping(Common::SeekableReadStreamEndian &stream) {
 		uint16 libResourceId = stream.readUint16();
 		uint16 libId = i + 1;
 		debugC(5, kDebugLoading, "Movie::loadCastLibMapping: name: %s, path: %s, minMember: %d, maxMember: %d, libResourceId: %d, libId: %d", utf8ToPrintable(name).c_str(), utf8ToPrintable(path).c_str(), minMember, maxMember, libResourceId, libId);
-		Archive *castArchive = _movieArchive;
+		Common::SharedPtr<Archive> castArchive = _movieArchive;
 		bool isExternal = !path.empty();
 		if (isExternal) {
 			Common::Path archivePath = findMoviePath(path);
@@ -424,7 +424,7 @@ void Movie::clearSharedCast() {
 void Movie::loadSharedCastsFrom(Common::Path &filename) {
 	clearSharedCast();
 
-	Archive *sharedCast = _vm->openArchive(filename);
+	Common::SharedPtr<Archive> sharedCast = _vm->openArchive(filename);
 
 	if (!sharedCast) {
 		warning("loadSharedCastsFrom(): No shared cast %s", filename.toString().c_str());
@@ -442,8 +442,8 @@ void Movie::loadSharedCastsFrom(Common::Path &filename) {
 	_sharedCast->loadArchive();
 }
 
-Archive *Movie::loadExternalCastFrom(Common::Path &filename) {
-	Archive *externalCast = nullptr;
+Common::SharedPtr<Archive> Movie::loadExternalCastFrom(Common::Path &filename) {
+	Common::SharedPtr<Archive> externalCast = nullptr;
 	externalCast = _vm->openArchive(filename);
 
 	if (!externalCast) {
@@ -468,7 +468,7 @@ bool Movie::loadCastLibFrom(uint16 libId, Common::Path &filename) {
 		}
 	}
 
-	Archive *castArchive = loadExternalCastFrom(filename);
+	Common::SharedPtr<Archive> castArchive = loadExternalCastFrom(filename);
 	if (!castArchive) {
 		return false;
 	}
diff --git a/engines/director/movie.h b/engines/director/movie.h
index 5640c6d6f64..5aee2c30f2c 100644
--- a/engines/director/movie.h
+++ b/engines/director/movie.h
@@ -98,8 +98,8 @@ public:
 
 	void loadCastLibMapping(Common::SeekableReadStreamEndian &stream);
 	bool loadArchive();
-	void setArchive(Archive *archive);
-	Archive *getArchive() const { return _movieArchive; };
+	void setArchive(Common::SharedPtr<Archive> archive);
+	Common::SharedPtr<Archive> getArchive() const { return _movieArchive; };
 	Common::String getMacName() const { return _macName; }
 	Window *getWindow() const { return _window; }
 	DirectorEngine *getVM() const { return _vm; }
@@ -112,7 +112,7 @@ public:
 
 	void clearSharedCast();
 	void loadSharedCastsFrom(Common::Path &filename);
-	Archive *loadExternalCastFrom(Common::Path &filename);
+	Common::SharedPtr<Archive> loadExternalCastFrom(Common::Path &filename);
 	bool loadCastLibFrom(uint16 libId, Common::Path &filename);
 
 	CastMember *getCastMember(CastMemberID memberID);
@@ -153,7 +153,7 @@ private:
 	void queueSpriteEvent(Common::Queue<LingoEvent> &queue, LEvent event, int eventId, int spriteId);
 
 public:
-	Archive *_movieArchive;
+	Common::SharedPtr<Archive> _movieArchive;
 	uint16 _version;
 	Common::Platform _platform;
 	Common::Rect _movieRect;
diff --git a/engines/director/resource.cpp b/engines/director/resource.cpp
index 5dade0314fb..ca9e4961ffb 100644
--- a/engines/director/resource.cpp
+++ b/engines/director/resource.cpp
@@ -67,14 +67,14 @@ Common::Error Window::loadInitialMovie() {
 	}
 	loadINIStream();
 	Common::Path path = findPath(movie);
-	Archive *mainArchive = g_director->openArchive(path);
+	Common::SharedPtr<Archive> mainArchive = g_director->openArchive(path);
 
 	if (!mainArchive) {
 		warning("Window::loadInitialMovie: Cannot open main movie");
 		return Common::kNoGameDataFoundError;
 	}
 	g_director->setMainArchive(mainArchive);
-	probeResources(mainArchive);
+	probeResources(mainArchive.get());
 
 	// Load multiple-resources based executable file (Projector)
 	Common::String rawEXE = _vm->getRawEXEName();
@@ -163,8 +163,8 @@ void Window::probeResources(Archive *archive) {
 		}
 
 		if (archive->hasResource(MKTAG('S', 'T', 'R', '#'), 0)) {
-			if (_currentMovie)
-				_currentMovie->setArchive(archive);
+			//if (_currentMovie)
+			//	_currentMovie->setArchive(archive);
 
 			Common::SeekableReadStreamEndian *name = archive->getResource(MKTAG('S', 'T', 'R', '#'), 0);
 			int num = name->readUint16();
@@ -186,9 +186,9 @@ void Window::probeResources(Archive *archive) {
 					_currentMovie = nullptr;
 				}
 
-				Archive *subMovie = g_director->openArchive(moviePath);
+				Common::SharedPtr<Archive> subMovie = g_director->openArchive(moviePath);
 				if (subMovie) {
-					probeResources(subMovie);
+					probeResources(subMovie.get());
 				}
 			} else {
 				warning("Window::probeResources: Couldn't find score with name: %s", sname.c_str());
@@ -244,7 +244,7 @@ void DirectorEngine::addArchiveToOpenList(const Common::Path &path) {
 	_allOpenResFiles.push_front(path);
 }
 
-Archive *DirectorEngine::openArchive(const Common::Path &path) {
+Common::SharedPtr<Archive> DirectorEngine::openArchive(const Common::Path &path) {
 	debug(1, "DirectorEngine::openArchive(\"%s\")", path.toString().c_str());
 
 	// If the archive is already open, don't reopen it;
@@ -269,11 +269,12 @@ Archive *DirectorEngine::openArchive(const Common::Path &path) {
 		}
 	}
 	result->setPathName(path);
-	_allSeenResFiles.setVal(path, result);
+	Common::SharedPtr<Archive> arch(result);
+	_allSeenResFiles.setVal(path, arch);
 
 	addArchiveToOpenList(path);
 
-	return result;
+	return arch;
 }
 
 void Window::loadINIStream() {
diff --git a/engines/director/sound.cpp b/engines/director/sound.cpp
index b8b03a25698..47672584184 100644
--- a/engines/director/sound.cpp
+++ b/engines/director/sound.cpp
@@ -400,7 +400,7 @@ void DirectorSound::loadSampleSounds(uint type) {
 		for (uint j = 0; j < idList.size(); j++) {
 			if (static_cast<uint>(idList[j] & 0xFF) == type) {
 				id = idList[j];
-				archive = g_director->_allSeenResFiles[it];
+				archive = g_director->_allSeenResFiles[it].get();
 				break;
 			}
 		}
@@ -637,7 +637,7 @@ void DirectorSound::playFPlaySound() {
 	for (auto &it : g_director->_allOpenResFiles) {
 		id = g_director->_allSeenResFiles[it]->findResourceID(tag, sndName, true);
 		if (id != 0xFFFF) {
-			archive = g_director->_allSeenResFiles[it];
+			archive = g_director->_allSeenResFiles[it].get();
 			break;
 		}
 	}
diff --git a/engines/director/tests.cpp b/engines/director/tests.cpp
index cfb6d5aacab..ad7ea478d5a 100644
--- a/engines/director/tests.cpp
+++ b/engines/director/tests.cpp
@@ -193,7 +193,7 @@ Common::HashMap<Common::String, Movie *> *Window::scanMovies(const Common::Path
 			}
 
 			warning("name: %s", i->getName().c_str());
-			Archive *arc = _vm->openArchive(i->getPathInArchive());
+			Common::SharedPtr<Archive> arc = _vm->openArchive(i->getPathInArchive());
 			Movie *m = new Movie(this);
 			m->setArchive(arc);
 			nameMap->setVal(m->getMacName(), m);
@@ -324,7 +324,7 @@ void Window::runTests() {
 
 	initGraphics(640, 480);
 
-	Archive *mainArchive = new RIFXArchive();
+	Common::SharedPtr<Archive> mainArchive(new RIFXArchive());
 	g_director->setMainArchive(mainArchive);
 	g_director->_allSeenResFiles.setVal("test.dir", mainArchive);
 	if (!mainArchive->openStream(stream, 0)) {
diff --git a/engines/director/window.cpp b/engines/director/window.cpp
index c6af79c18f9..9602f3e4e6f 100644
--- a/engines/director/window.cpp
+++ b/engines/director/window.cpp
@@ -561,7 +561,6 @@ void Window::loadNewSharedCast(Cast *previousSharedCast) {
 
 		g_director->_allSeenResFiles.erase(previousSharedCastPath);
 		g_director->_allOpenResFiles.remove(previousSharedCastPath);
-		delete previousSharedCast->_castArchive;
 		delete previousSharedCast;
 	} else {
 		debug(0, "@@   No previous shared cast");
@@ -600,14 +599,14 @@ bool Window::loadNextMovie() {
 		_currentMovie = nullptr;
 	}
 
-	Archive *mov = g_director->openArchive(archivePath);
+	Common::SharedPtr<Archive> mov = g_director->openArchive(archivePath);
 
 	_nextMovie.movie.clear(); // Clearing it, so we will not attempt to load again
 
 	if (!mov)
 		return false;
 
-	probeResources(mov);
+	probeResources(mov.get());
 
 	// Artificial delay for games that expect slow media, e.g. Spaceship Warlock
 	if (g_director->_loadSlowdownFactor && !debugChannelSet(-1, kDebugFast)) {


Commit: 53ac0b7ff382b1bf95e3c3e12d27b4ffb3267238
    https://github.com/scummvm/scummvm/commit/53ac0b7ff382b1bf95e3c3e12d27b4ffb3267238
Author: Scott Percival (code at moral.net.au)
Date: 2026-06-01T20:06:27+02:00

Commit Message:
DIRECTOR: Add Xtra + quirks for Pink Gear Collection

Changed paths:
  A engines/director/lingo/xtras/g/getdir.cpp
  A engines/director/lingo/xtras/g/getdir.h
    engines/director/game-quirks.cpp
    engines/director/lingo/lingo-object.cpp
    engines/director/module.mk


diff --git a/engines/director/game-quirks.cpp b/engines/director/game-quirks.cpp
index ecd6081eff5..26601391c4e 100644
--- a/engines/director/game-quirks.cpp
+++ b/engines/director/game-quirks.cpp
@@ -312,6 +312,10 @@ const struct Quirk {
 	{ "puppetmotel", Common::kPlatformWindows, &quirkForceFileIOXtra },
 	{ "puppetmotel", Common::kPlatformMacintosh, &quirkForceFileIOXtra },
 
+	// Pink Gear Collection uses the Xtra version, but loads it manually with openXLib.
+	{ "pinkgear", Common::kPlatformWindows, &quirkForceFileIOXtra },
+	{ "pinkgear", Common::kPlatformMacintosh, &quirkForceFileIOXtra },
+
 	// Stay Tooned is D5, but expects the XObject version to be used.
 	{ "staytooned", Common::kPlatformWindows, &quirkForceFileIOXObj },
 	{ "staytooned", Common::kPlatformMacintosh, &quirkForceFileIOXObj },
diff --git a/engines/director/lingo/lingo-object.cpp b/engines/director/lingo/lingo-object.cpp
index 40ca497c2d0..5f6193c1e23 100644
--- a/engines/director/lingo/lingo-object.cpp
+++ b/engines/director/lingo/lingo-object.cpp
@@ -156,6 +156,7 @@
 #include "director/lingo/xtras/d/directsound.h"
 #include "director/lingo/xtras/d/displayres.h"
 #include "director/lingo/xtras/f/filextra.h"
+#include "director/lingo/xtras/g/getdir.h"
 #include "director/lingo/xtras/k/keypoll.h"
 #include "director/lingo/xtras/m/masterapp.h"
 #include "director/lingo/xtras/m/mui.h"
@@ -297,6 +298,7 @@ static const struct XLibProto {
 	XLIBDEF(FlushXObj,			kXObj,			300),	// D3
 	XLIBDEF(FPlayXObj,			kXObj,			200),	// D2
 	XLIBDEF(GenUtilsXObj,		kXObj,			400),	// D4
+	XLIBDEF(GetDirXtra,			kXtraObj,					500),	// D5
 	XLIBDEF(GetScreenRectsXFCN,	kXObj,			300),	// D3
 	XLIBDEF(GetScreenSizeXFCN,	kXObj,			300),	// D3
 	XLIBDEF(GetSoundInLevelXObj,kXObj,			400),	// D4
diff --git a/engines/director/lingo/xtras/g/getdir.cpp b/engines/director/lingo/xtras/g/getdir.cpp
new file mode 100644
index 00000000000..431be2a627d
--- /dev/null
+++ b/engines/director/lingo/xtras/g/getdir.cpp
@@ -0,0 +1,118 @@
+/* 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/system.h"
+
+#include "director/director.h"
+#include "director/lingo/lingo.h"
+#include "director/lingo/lingo-object.h"
+#include "director/lingo/lingo-utils.h"
+#include "director/lingo/xtras/g/getdir.h"
+
+/**************************************************
+ *
+ * USED IN:
+ * pinkgear
+ *
+ **************************************************/
+
+/*
+-- xtra GetDir
+new object me
+forget object me
+-- GetDirXtra --
+GetDirOne object me, int repeat -- GetDir n times using default system GetDir
+* GetDirTwo -- global level GetDir method: GetDirs once
++ GetDirThree  object xtraRef -- xtra level version of above method
+* GetWindowsDir -- return path to windows directory
+* GetSystemDir -- return path to windows system directory
+
+ */
+
+namespace Director {
+
+const char *GetDirXtra::xlibName = "GetDir";
+const XlibFileDesc GetDirXtra::fileNames[] = {
+	{ "getdir",   nullptr },
+	{ nullptr,        nullptr },
+};
+
+static MethodProto xlibMethods[] = {
+	{ "new",				GetDirXtra::m_new,		 0, 0,	500 },
+	{ "forget",				GetDirXtra::m_forget,		 0, 0,	500 },
+	{ "GetDirOne",				GetDirXtra::m_GetDirOne,		 1, 1,	500 },
+	{ "GetDirThree",				GetDirXtra::m_GetDirThree,		 0, 0,	500 },
+	{ nullptr, nullptr, 0, 0, 0 }
+};
+
+static BuiltinProto xlibBuiltins[] = {
+	{ "GetDirTwo", GetDirXtra::m_GetDirTwo, 0, 0, 500, HBLTIN },
+	{ "GetWindowsDir", GetDirXtra::m_GetWindowsDir, 0, 0, 500, HBLTIN },
+	{ "GetSystemDir", GetDirXtra::m_GetSystemDir, 0, 0, 500, HBLTIN },
+	{ nullptr, nullptr, 0, 0, 0, VOIDSYM }
+};
+
+GetDirXtraObject::GetDirXtraObject(ObjectType ObjectType) :Object<GetDirXtraObject>("GetDir") {
+	_objType = ObjectType;
+}
+
+bool GetDirXtraObject::hasProp(const Common::String &propName) {
+	return (propName == "name");
+}
+
+Datum GetDirXtraObject::getProp(const Common::String &propName) {
+	if (propName == "name")
+		return Datum(GetDirXtra::xlibName);
+	warning("GetDirXtra::getProp: unknown property '%s'", propName.c_str());
+	return Datum();
+}
+
+void GetDirXtra::open(ObjectType type, const Common::Path &path) {
+    GetDirXtraObject::initMethods(xlibMethods);
+    GetDirXtraObject *xobj = new GetDirXtraObject(type);
+    if (type == kXtraObj) {
+        g_lingo->_openXtras.push_back(xlibName);
+		g_lingo->_openXtraObjects.push_back(xobj);
+	}
+    g_lingo->exposeXObject(xlibName, xobj);
+    g_lingo->initBuiltIns(xlibBuiltins);
+}
+
+void GetDirXtra::close(ObjectType type) {
+    GetDirXtraObject::cleanupMethods();
+    g_lingo->_globalvars[xlibName] = Datum();
+
+}
+
+void GetDirXtra::m_new(int nargs) {
+	g_lingo->printSTUBWithArglist("GetDirXtra::m_new", nargs);
+	g_lingo->dropStack(nargs);
+	g_lingo->push(g_lingo->_state->me);
+}
+
+XOBJSTUB(GetDirXtra::m_forget, 0)
+XOBJSTUB(GetDirXtra::m_GetDirOne, 0)
+XOBJSTUB(GetDirXtra::m_GetDirTwo, 0)
+XOBJSTUB(GetDirXtra::m_GetDirThree, 0)
+XOBJSTUB(GetDirXtra::m_GetWindowsDir, Datum("C:\\"))
+XOBJSTUB(GetDirXtra::m_GetSystemDir, Datum("@:"))
+
+}
diff --git a/engines/director/lingo/xtras/g/getdir.h b/engines/director/lingo/xtras/g/getdir.h
new file mode 100644
index 00000000000..a75db96d1c6
--- /dev/null
+++ b/engines/director/lingo/xtras/g/getdir.h
@@ -0,0 +1,55 @@
+/* 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 DIRECTOR_LINGO_XTRAS_G_GETDIR_H
+#define DIRECTOR_LINGO_XTRAS_G_GETDIR_H
+
+namespace Director {
+
+class GetDirXtraObject : public Object<GetDirXtraObject> {
+public:
+	GetDirXtraObject(ObjectType objType);
+
+	bool hasProp(const Common::String &propName) override;
+	Datum getProp(const Common::String &propName) override;
+};
+
+namespace GetDirXtra {
+
+extern const char *xlibName;
+extern const XlibFileDesc fileNames[];
+
+void open(ObjectType type, const Common::Path &path);
+void close(ObjectType type);
+
+void m_new(int nargs);
+void m_forget(int nargs);
+void m_GetDirOne(int nargs);
+void m_GetDirTwo(int nargs);
+void m_GetDirThree(int nargs);
+void m_GetWindowsDir(int nargs);
+void m_GetSystemDir(int nargs);
+
+} // End of namespace GetDirXtra
+
+} // End of namespace Director
+
+#endif
diff --git a/engines/director/module.mk b/engines/director/module.mk
index 0f70a1b434d..2b460e50677 100644
--- a/engines/director/module.mk
+++ b/engines/director/module.mk
@@ -191,6 +191,7 @@ MODULE_OBJS = \
 	lingo/xtras/k/keypoll.o \
 	lingo/xtras/m/masterapp.o \
 	lingo/xtras/m/mui.o \
+	lingo/xtras/g/getdir.o \
 	lingo/xtras/o/openurl.o \
 	lingo/xtras/o/oscheck.o \
 	lingo/xtras/q/qtvrxtra.o \


Commit: 5085f88e77957525a7fdd82f5e91c6502ae81be4
    https://github.com/scummvm/scummvm/commit/5085f88e77957525a7fdd82f5e91c6502ae81be4
Author: Scott Percival (code at moral.net.au)
Date: 2026-06-01T20:06:27+02:00

Commit Message:
DIRECTOR: Add guardrail to Symbol::operator==

Changed paths:
    engines/director/lingo/lingo.cpp


diff --git a/engines/director/lingo/lingo.cpp b/engines/director/lingo/lingo.cpp
index b6327fc6b09..db54113b73d 100644
--- a/engines/director/lingo/lingo.cpp
+++ b/engines/director/lingo/lingo.cpp
@@ -109,6 +109,10 @@ Symbol& Symbol::operator=(const Symbol &s) {
 }
 
 bool Symbol::operator==(Symbol &s) const {
+	if ((s.type == VOIDSYM) && (type == VOIDSYM))
+		return true;
+	if ((!name || !s.name))
+		return false;
 	return ctx == s.ctx && (name->equalsIgnoreCase(*s.name));
 }
 


Commit: 9c211c8ff8913a84f18e728dedf78ed167c35a30
    https://github.com/scummvm/scummvm/commit/9c211c8ff8913a84f18e728dedf78ed167c35a30
Author: Scott Percival (code at moral.net.au)
Date: 2026-06-01T20:06:27+02:00

Commit Message:
DIRECTOR: Improve accuracy of b_dontPassEvent

When fetching system input events, Director seems to process them
immediately (if possible) and use the value of the _passEvent flag
to determine if other widgets get the event. This means you can e.g.
set up a keyDownScript which calls dontPassEvent if Enter is pressed,
and the selected textbox won't receive that key.

If the system event queue is being accessed while another script is
executing, buffer the event for later like we did before.

Fixes overflowing the name textbox in Pink Gear Collection.

Changed paths:
    engines/director/castmember/text.cpp
    engines/director/director.cpp
    engines/director/director.h
    engines/director/events.cpp
    engines/director/lingo/lingo-events.cpp
    engines/director/lingo/lingo.cpp
    engines/director/movie.h
    engines/director/score.cpp
    engines/director/transitions.cpp
    engines/director/window.cpp
    engines/director/window.h
    graphics/macgui/macwindow.cpp


diff --git a/engines/director/castmember/text.cpp b/engines/director/castmember/text.cpp
index 35ed292e400..06806dd8ecf 100644
--- a/engines/director/castmember/text.cpp
+++ b/engines/director/castmember/text.cpp
@@ -293,7 +293,7 @@ void TextCastMember::importStxt(const Stxt *stxt) {
 }
 
 bool textWindowCallback(Graphics::WindowClick click, Common::Event &event, void *ptr) {
-	return g_director->getCurrentMovie()->processEvent(event);
+	return g_director->getCurrentMovie()->processSysEvent(event);
 }
 
 Graphics::MacWidget *TextCastMember::createWindowOrWidget(Common::Rect &bbox, Common::Rect dims, Graphics::MacFont *macFont) {
diff --git a/engines/director/director.cpp b/engines/director/director.cpp
index fdc65339d5f..bd7afa3ea54 100644
--- a/engines/director/director.cpp
+++ b/engines/director/director.cpp
@@ -358,7 +358,7 @@ Common::Error DirectorEngine::run() {
 
 	while (loop) {
 		if (_stage->getCurrentMovie())
-			processEvents();
+			processSysEvents();
 
 		setCurrentWindow(_stage);
 		g_lingo->switchStateFromWindow();
diff --git a/engines/director/director.h b/engines/director/director.h
index 6c83e8a7110..b994db9b77d 100644
--- a/engines/director/director.h
+++ b/engines/director/director.h
@@ -244,7 +244,7 @@ public:
 
 	// events.cpp
 	bool pollEvent(Common::Event &event);
-	bool processEvents(bool captureClick = false, bool skipWindowManager = false);
+	bool processSysEvents(bool captureClick = false, bool skipWindowManager = false);
 	void processEventQUIT();
 	int getMacTicks();
 	Common::Array<Common::Event> _injectedEvents;
diff --git a/engines/director/events.cpp b/engines/director/events.cpp
index f0146900278..59db793449a 100644
--- a/engines/director/events.cpp
+++ b/engines/director/events.cpp
@@ -52,7 +52,7 @@ bool DirectorEngine::pollEvent(Common::Event &event) {
 	return g_system->getEventManager()->pollEvent(event);
 }
 
-bool DirectorEngine::processEvents(bool captureClick, bool skipWindowManager) {
+bool DirectorEngine::processSysEvents(bool captureClick, bool skipWindowManager) {
 	debugC(9, kDebugEvents, "\n@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
 	debugC(9, kDebugEvents, "@@@@   Processing events");
 	debugC(9, kDebugEvents, "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n");
@@ -133,16 +133,25 @@ void DirectorEngine::processEventQUIT() {
 	}
 }
 
-bool Window::processEvent(Common::Event &event) {
+bool Window::processSysEvent(Common::Event &event) {
 	bool flag = false;
 
-	if (_currentMovie && _currentMovie->processEvent(event))
-		flag = true;
+	if (g_lingo) {
+		Window *prev = g_director->getCurrentWindow();
+		g_director->setCurrentWindow(this);
+		g_lingo->switchStateFromWindow();
+
+		if (_currentMovie && _currentMovie->processSysEvent(event))
+			flag = true;
+
+		g_director->setCurrentWindow(prev);
+		g_lingo->switchStateFromWindow();
+	}
 
 	return flag;
 }
 
-bool Movie::processEvent(Common::Event &event) {
+bool Movie::processSysEvent(Common::Event &event) {
 	// When in GUI message box is being shown, movie may record clicking on the message box as a movie event
 	// Make sure that these events (mouseUp, mouseDown) are not recorded in the movie
 	if (_inGuiMessageBox) {
@@ -151,10 +160,11 @@ bool Movie::processEvent(Common::Event &event) {
 
 	Score *sc = getScore();
 	if (sc->getCurrentFrameNum() > sc->getFramesNum()) {
-		warning("processEvents: request to access frame %d of %d", sc->getCurrentFrameNum(), sc->getFramesNum());
+		warning("Movie::processSysEvent: request to access frame %d of %d", sc->getCurrentFrameNum(), sc->getFramesNum());
 		return false;
 	}
 	uint16 spriteId = 0;
+	bool result = true;
 
 	if (event.mouse != Common::Point(-1, -1)) {
 		if (g_director->getVersion() < 400)
@@ -235,7 +245,7 @@ bool Movie::processEvent(Common::Event &event) {
 				}
 			}
 		}
-		return true;
+		return result;
 
 	case Common::EVENT_LBUTTONDOWN:
 	case Common::EVENT_RBUTTONDOWN:
@@ -248,7 +258,7 @@ bool Movie::processEvent(Common::Event &event) {
 
 			if (g_director->getVersion() >= 600) {
 				if (_lastClickedSpriteId && _lastClickedSpriteId != spriteId) {
-					queueInputEvent(kEventMouseUpOutSide, _lastClickedSpriteId, pos);
+					processInputEvent(kEventMouseUpOutSide, _lastClickedSpriteId, pos);
 				}
 			}
 
@@ -276,24 +286,24 @@ bool Movie::processEvent(Common::Event &event) {
 				}
 			}
 
-			debugC(3, kDebugEvents, "Movie::processEvent(): Button Down @(%d, %d), movie '%s'", pos.x, pos.y, _macName.c_str());
-			queueInputEvent(ev, 0, pos);
+			debugC(3, kDebugEvents, "Movie::processSysEvent(): Button Down @(%d, %d), movie '%s'", pos.x, pos.y, _macName.c_str());
+			result = processInputEvent(ev, 0, pos);
 
 			// D5 has special behavior here
 			if (g_director->getVersion() >= 500 && g_director->getVersion() < 600) {
 				if (_lastClickedSpriteId)
-					queueInputEvent(kEventMouseEnter, _lastClickedSpriteId, pos);
+					processInputEvent(kEventMouseEnter, _lastClickedSpriteId, pos);
 			}
 		}
 
-		return true;
+		return result;
 
 	case Common::EVENT_LBUTTONUP:
 	case Common::EVENT_RBUTTONUP:
 		{
 			pos = event.mouse;
 
-			debugC(3, kDebugEvents, "Movie::processEvent(): Button Up @(%d, %d), movie '%s'", pos.x, pos.y, _macName.c_str());
+			debugC(3, kDebugEvents, "Movie::processSysEvent(): Button Up @(%d, %d), movie '%s'", pos.x, pos.y, _macName.c_str());
 
 			LEvent ev = kEventMouseUp;
 			// In D5 and up, right mouse clicks don't trigger the mouseUp handler.
@@ -311,17 +321,17 @@ bool Movie::processEvent(Common::Event &event) {
 				}
 			}
 
-			queueInputEvent(ev, 0, pos);
+			result = processInputEvent(ev, 0, pos);
 
 			// D5 has special behavior here
 			if (g_director->getVersion() >= 500 && g_director->getVersion() < 600) {
 				if (spriteId)
-					queueInputEvent(kEventMouseLeave, spriteId, pos);
+					processInputEvent(kEventMouseLeave, spriteId, pos);
 			}
 
 			sc->renderCursor(pos);
 		}
-		return true;
+		return result;
 
 	case Common::EVENT_KEYDOWN:
 		_keyCode = _vm->_KeyCodes.contains(event.kbd.keycode) ? _vm->_KeyCodes[event.kbd.keycode] : 0;
@@ -361,14 +371,14 @@ bool Movie::processEvent(Common::Event &event) {
 		if (_timeOutKeyDown)
 			_lastTimeOut = _lastEventTime;
 
-		queueInputEvent(kEventKeyDown, sc->getSpriteIDOfActiveWidget());
+		result = processInputEvent(kEventKeyDown, sc->getSpriteIDOfActiveWidget());
 		g_director->loadSlowdownCooloff();
-		return true;
+		return result;
 
 	case Common::EVENT_KEYUP:
-		queueInputEvent(kEventKeyUp, sc->getSpriteIDOfActiveWidget());
+		result = processInputEvent(kEventKeyUp, sc->getSpriteIDOfActiveWidget());
 		_keyFlags = event.kbd.flags;
-		return true;
+		return result;
 
 	default:
 		break;
@@ -421,7 +431,7 @@ bool Window::processWMEvent(Graphics::WindowClick click, Common::Event &event) {
 		break;
 	}
 
-	flag |= processEvent(event);
+	flag |= processSysEvent(event);
 	return flag;
 }
 
diff --git a/engines/director/lingo/lingo-events.cpp b/engines/director/lingo/lingo-events.cpp
index a412dff3397..544ed07ac2e 100644
--- a/engines/director/lingo/lingo-events.cpp
+++ b/engines/director/lingo/lingo-events.cpp
@@ -611,6 +611,22 @@ void Movie::queueInputEvent(LEvent event, int targetId, Common::Point pos) {
 	queueEvent(_inputEventQueue, event, targetId, pos);
 }
 
+
+bool Movie::processInputEvent(LEvent event, int targetId, Common::Point pos) {
+	if (!_lingo->_state->callstack.empty()) {
+		// We're in the middle of executing something else, queue input event for later
+		queueInputEvent(event, targetId, pos);
+		return true;
+	}
+	// Try and process event inline
+	Common::Queue<LingoEvent> queue;
+	queueEvent(queue, event, targetId, pos);
+	_vm->setCurrentWindow(this->getWindow());
+	_lingo->processEvents(queue, true);
+	return _lingo->_passEvent;
+}
+
+
 void Movie::processEvent(LEvent event, int targetId) {
 	Common::Queue<LingoEvent> queue;
 	queueEvent(queue, event, targetId);
diff --git a/engines/director/lingo/lingo.cpp b/engines/director/lingo/lingo.cpp
index db54113b73d..d11bbe941ad 100644
--- a/engines/director/lingo/lingo.cpp
+++ b/engines/director/lingo/lingo.cpp
@@ -651,7 +651,7 @@ bool Lingo::execute(int targetFrame) {
 
 		// process events every so often
 		if (localCounter > 0 && localCounter % 100 == 0) {
-			_vm->processEvents();
+			_vm->processSysEvents();
 			// Also process update widgets!
 			Movie *movie = g_director->getCurrentMovie();
 			Score *score = movie->getScore();
diff --git a/engines/director/movie.h b/engines/director/movie.h
index 5aee2c30f2c..773c60262f6 100644
--- a/engines/director/movie.h
+++ b/engines/director/movie.h
@@ -135,7 +135,7 @@ public:
 	Symbol getHandler(const Common::String &name, uint16 castLibHint = 0);
 
 	// lingo/lingo-events.cpp
-	bool processEvent(Common::Event &event);
+	bool processSysEvent(Common::Event &event);
 	void broadcastEvent(LEvent event);
 
 	// lingo/lingo-events.cpp
@@ -143,6 +143,7 @@ public:
 	void resolveScriptEvent(LingoEvent &event);
 	void processEvent(LEvent event, int targetId = 0);
 	void queueInputEvent(LEvent event, int targetId = 0, Common::Point pos = Common::Point(-1, -1));
+	bool processInputEvent(LEvent event, int targetId = 0, Common::Point pos = Common::Point(-1, -1));
 
 	Common::String formatMovieInfo();
 
diff --git a/engines/director/score.cpp b/engines/director/score.cpp
index d922b220003..1465d21b7e6 100644
--- a/engines/director/score.cpp
+++ b/engines/director/score.cpp
@@ -124,7 +124,7 @@ void Score::setPuppetTempo(int16 puppetTempo) {
 }
 
 CastMemberID Score::getCurrentPalette() {
-	return g_director->_lastPalette;
+	return _vm->_lastPalette;
 }
 
 bool Score::processImmediateFrameScript(Common::String s, int id) {
@@ -353,11 +353,11 @@ void Score::step() {
 	update();
 
 	if (debugChannelSet(-1, kDebugFewFramesOnly) || debugChannelSet(-1, kDebugScreenshot)) {
-		warning("Score::startLoop(): ran frame %0d", g_director->_framesRan);
-		g_director->_framesRan++;
+		warning("Score::startLoop(): ran frame %0d", _vm->_framesRan);
+		_vm->_framesRan++;
 	}
 
-	if (debugChannelSet(-1, kDebugFewFramesOnly) && g_director->_framesRan > kFewFamesMaxCounter) {
+	if (debugChannelSet(-1, kDebugFewFramesOnly) && _vm->_framesRan > kFewFamesMaxCounter) {
 		warning("Score::startLoop(): exiting due to debug few frames only");
 		_playState = kPlayStopped;
 		return;
@@ -545,8 +545,8 @@ void Score::updateNextFrameTime() {
 			} else if (tempo <= 120) {
 				// FPS
 				_currentFrameRate = tempo;
-				if (g_director->_fpsLimit)
-					_currentFrameRate = MIN(g_director->_fpsLimit, _currentFrameRate);
+				if (_vm->_fpsLimit)
+					_currentFrameRate = MIN(_vm->_fpsLimit, _currentFrameRate);
 				_nextFrameTime = g_system->getMillis() + 1000.0 / (float)_currentFrameRate;
 				debugC(5, kDebugEvents, "Score::updateNextFrameTime(): setting _nextFrameTime to %d based on a framerate of %d", _nextFrameTime, _currentFrameRate);
 			} else {
@@ -600,8 +600,8 @@ void Score::updateNextFrameTime() {
 			} else if (tempo == 246) {
 				// FPS
 				_currentFrameRate = tempoCuePoint;
-				if (g_director->_fpsLimit)
-					_currentFrameRate = MIN(g_director->_fpsLimit, _currentFrameRate);
+				if (_vm->_fpsLimit)
+					_currentFrameRate = MIN(_vm->_fpsLimit, _currentFrameRate);
 				_nextFrameTime = g_system->getMillis() + 1000.0 / (float)_currentFrameRate;
 				debugC(5, kDebugEvents, "Score::updateNextFrameTime(): setting _nextFrameTime to %d based on a framerate of %d", _nextFrameTime, _currentFrameRate);
 			} else {
@@ -853,7 +853,7 @@ void Score::renderFrame(uint16 frameId, RenderMode mode, bool sound1Changed, boo
 		incrementFilmLoops();
 		_window->render();
 		_skipTransition = false;
-	} else if (g_director->_playbackPaused) {
+	} else if (_vm->_playbackPaused) {
 		updateSprites(mode);
 		incrementFilmLoops();
 		_window->render();
@@ -1012,8 +1012,8 @@ bool Score::renderPrePaletteCycle(RenderMode mode) {
 		if (debugChannelSet(-1, kDebugFast))
 			frameRate = 30;
 
-		if (g_director->_fpsLimit)
-			frameRate = MIN((int)g_director->_fpsLimit, frameRate);
+		if (_vm->_fpsLimit)
+			frameRate = MIN((int)_vm->_fpsLimit, frameRate);
 
 		int frameDelay = 1000 / 60;
 		int fadeFrames = kFadeColorFrames[frameRate - 1];
@@ -1023,8 +1023,8 @@ bool Score::renderPrePaletteCycle(RenderMode mode) {
 
 		// Copy the current palette into the snapshot buffer
 		memset(_paletteSnapshotBuffer, 0, 768);
-		memcpy(_paletteSnapshotBuffer, g_director->getPalette(), g_director->getPaletteColorCount() * 3);
-		PaletteV4 *destPal = g_director->getPalette(currentPalette);
+		memcpy(_paletteSnapshotBuffer, _vm->getPalette(), _vm->getPaletteColorCount() * 3);
+		PaletteV4 *destPal = _vm->getPalette(currentPalette);
 		if (!destPal) {
 			warning("Unable to fetch palette %s", currentPalette.asString().c_str());
 			return false;
@@ -1033,7 +1033,7 @@ bool Score::renderPrePaletteCycle(RenderMode mode) {
 		if (_currentFrame->_mainChannels.palette.normal) {
 			// If the target palette ID is the same as the previous palette ID,
 			// a normal fade is a no-op.
-			if (_currentFrame->_mainChannels.palette.paletteId == g_director->_lastPalette) {
+			if (_currentFrame->_mainChannels.palette.paletteId == _vm->_lastPalette) {
 				return false;
 			}
 
@@ -1049,20 +1049,20 @@ bool Score::renderPrePaletteCycle(RenderMode mode) {
 					i + 1,
 					fadeFrames
 				);
-				g_director->setPalette(calcPal, 256);
-				g_director->draw();
+				_vm->setPalette(calcPal, 256);
+				_vm->draw();
 				if (_activeFade) {
 					_activeFade = _soundManager->fadeChannels();
 				}
 				// On click, stop loop and reset palette
-				if (_vm->processEvents(true)) {
+				if (_vm->processSysEvents(true)) {
 					debugC(2, kDebugImages, "Score::renderPrePaletteCycle(): interrupted, setting palette to %s", currentPalette.asString().c_str());
-					g_director->setPalette(currentPalette);
+					_vm->setPalette(currentPalette);
 					return true;
 				}
 				uint32 endTime = g_system->getMillis();
 				int diff = (int)frameDelay - (int)(endTime - startTime);
-				g_director->delayMillis(MAX(0, diff));
+				_vm->delayMillis(MAX(0, diff));
 			}
 
 		} else {
@@ -1092,20 +1092,20 @@ bool Score::renderPrePaletteCycle(RenderMode mode) {
 					i + 1,
 					fadeFrames
 				);
-				g_director->setPalette(calcPal, 256);
-				g_director->draw();
+				_vm->setPalette(calcPal, 256);
+				_vm->draw();
 				if (_activeFade) {
 					_activeFade = _soundManager->fadeChannels();
 				}
 				// On click, stop loop and reset palette
-				if (_vm->processEvents(true)) {
+				if (_vm->processSysEvents(true)) {
 					debugC(2, kDebugImages, "Score::renderPrePaletteCycle(): interrupted, setting palette to %s", currentPalette.asString().c_str());
-					g_director->setPalette(currentPalette);
+					_vm->setPalette(currentPalette);
 					return true;
 				}
 				uint32 endTime = g_system->getMillis();
 				int diff = (int)frameDelay - (int)(endTime - startTime);
-				g_director->delayMillis(MAX(0, diff));
+				_vm->delayMillis(MAX(0, diff));
 			}
 		}
 	}
@@ -1120,19 +1120,19 @@ void Score::setLastPalette() {
 	CastMemberID currentPalette = _currentFrame->_mainChannels.palette.paletteId;
 	// Director allows you to use palette IDs for cast members
 	// that have long since been erased. Check all of them.
-	if (!g_director->hasPalette(currentPalette))
+	if (!_vm->hasPalette(currentPalette))
 		currentPalette = CastMemberID();
 	// Palette not specified in the frame
 	if (currentPalette.isNull()) {
 		// Use the score cached palette ID
 		isCachedPalette = true;
 		currentPalette = _currentFrame->_mainChannels.scoreCachedPaletteId;
-		if (!g_director->hasPalette(currentPalette))
+		if (!_vm->hasPalette(currentPalette))
 			currentPalette = CastMemberID();
 		// The cached ID is created before the cast gets loaded; if it's zero,
 		// this corresponds to the movie default palette.
 		if (currentPalette.isNull()) {
-			currentPalette = g_director->getCurrentMovie()->_defaultPalette;
+			currentPalette = _vm->getCurrentMovie()->_defaultPalette;
 		}
 		// If for whatever reason this doesn't resolve, abort.
 		if (currentPalette.isNull())
@@ -1141,17 +1141,17 @@ void Score::setLastPalette() {
 
 	// If the palette is defined in the frame and doesn't match
 	// the current one, set it
-	bool paletteChanged = (currentPalette != g_director->_lastPalette) && (!currentPalette.isNull());
+	bool paletteChanged = (currentPalette != _vm->_lastPalette) && (!currentPalette.isNull());
 	if (paletteChanged) {
 		debugC(2, kDebugImages, "Score::setLastPalette(): palette changed to %s, from %s", currentPalette.asString().c_str(), isCachedPalette ? "cache" :"frame");
-		g_director->_lastPalette = currentPalette;
+		_vm->_lastPalette = currentPalette;
 		_paletteTransitionIndex = 0;
 
 		// Switch to a new palette immediately if:
 		// - this is color cycling mode, or
 		// - the cached palette ID is different (i.e. we jumped in the score)
 		if (_currentFrame->_mainChannels.palette.colorCycling || isCachedPalette)
-			g_director->setPalette(g_director->_lastPalette);
+			_vm->setPalette(_vm->_lastPalette);
 	}
 
 }
@@ -1184,8 +1184,8 @@ void Score::renderPaletteCycle(RenderMode mode) {
 		return;
 
 	// Apply the global FPS limit if required
-	if (g_director->_fpsLimit)
-		speed = MIN((int)g_director->_fpsLimit, speed);
+	if (_vm->_fpsLimit)
+		speed = MIN((int)_vm->_fpsLimit, speed);
 
 	if (debugChannelSet(-1, kDebugFast))
 		speed = 30;
@@ -1200,12 +1200,12 @@ void Score::renderPaletteCycle(RenderMode mode) {
 		if (_currentFrame->_mainChannels.palette.overTime) {
 			// Do a single color step in one frame transition
 			debugC(2, kDebugImages, "Score::renderPaletteCycle(): color cycle palette %s, from colors %d to %d, by 1 frame", currentPalette.asString().c_str(), firstColor, lastColor);
-			g_director->shiftPalette(firstColor, lastColor, false);
-			g_director->draw();
+			_vm->shiftPalette(firstColor, lastColor, false);
+			_vm->draw();
 		} else {
 			// Short circuit for few frames renderer
 			if (debugChannelSet(-1, kDebugFast)) {
-				g_director->setPalette(currentPalette);
+				_vm->setPalette(currentPalette);
 				return;
 			}
 
@@ -1215,43 +1215,43 @@ void Score::renderPaletteCycle(RenderMode mode) {
 			for (int i = 0; i < _currentFrame->_mainChannels.palette.cycleCount; i++) {
 				for (int j = 0; j < steps; j++) {
 					uint32 startTime = g_system->getMillis();
-					g_director->shiftPalette(firstColor, lastColor, false);
-					g_director->draw();
+					_vm->shiftPalette(firstColor, lastColor, false);
+					_vm->draw();
 					if (_activeFade) {
 						_activeFade = _soundManager->fadeChannels();
 					}
 					// On click, stop loop and reset palette
-					if (_vm->processEvents(true)) {
-						g_director->setPalette(currentPalette);
+					if (_vm->processSysEvents(true)) {
+						_vm->setPalette(currentPalette);
 						return;
 					}
 					uint32 endTime = g_system->getMillis();
 					int diff = (int)delay - (int)(endTime - startTime);
-					g_director->delayMillis(MAX(0, diff));
+					_vm->delayMillis(MAX(0, diff));
 				}
 				if (_currentFrame->_mainChannels.palette.autoReverse) {
 					for (int j = 0; j < steps; j++) {
 						uint32 startTime = g_system->getMillis();
-						g_director->shiftPalette(firstColor, lastColor, true);
-						g_director->draw();
+						_vm->shiftPalette(firstColor, lastColor, true);
+						_vm->draw();
 						if (_activeFade) {
 							_activeFade = _soundManager->fadeChannels();
 						}
 						// On click, stop loop and reset palette
-						if (_vm->processEvents(true)) {
-							g_director->setPalette(currentPalette);
+						if (_vm->processSysEvents(true)) {
+							_vm->setPalette(currentPalette);
 							return;
 						}
 						uint32 endTime = g_system->getMillis();
 						int diff = (int)delay - (int)(endTime - startTime);
-						g_director->delayMillis(MAX(0, diff));
+						_vm->delayMillis(MAX(0, diff));
 					}
 				}
 			}
 		}
 	} else {
 		// Transition from the current palette to a new palette
-		PaletteV4 *destPal = g_director->getPalette(currentPalette);
+		PaletteV4 *destPal = _vm->getPalette(currentPalette);
 		if (!destPal) {
 			warning("Score::renderPaletteCycle(): no match for palette id %s", currentPalette.asString().c_str());
 			return;
@@ -1264,7 +1264,7 @@ void Score::renderPaletteCycle(RenderMode mode) {
 			if (_paletteTransitionIndex == 0) {
 				// Copy the current palette into the snapshot buffer
 				memset(_paletteSnapshotBuffer, 0, 768);
-				memcpy(_paletteSnapshotBuffer, g_director->getPalette(), g_director->getPaletteColorCount() * 3);
+				memcpy(_paletteSnapshotBuffer, _vm->getPalette(), _vm->getPaletteColorCount() * 3);
 				debugC(2, kDebugImages, "Score::renderPaletteCycle(): fading palette to %s over %d frames", currentPalette.asString().c_str(), frameCount);
 			}
 
@@ -1312,14 +1312,14 @@ void Score::renderPaletteCycle(RenderMode mode) {
 					);
 				}
 			}
-			g_director->setPalette(calcPal, 256);
+			_vm->setPalette(calcPal, 256);
 			_paletteTransitionIndex++;
 			_paletteTransitionIndex %= frameCount;
 		} else {
 			// Short circuit for fast renderer
 			if (debugChannelSet(-1, kDebugFast)) {
 				debugC(2, kDebugImages, "Score::renderPaletteCycle(): setting palette to %s", currentPalette.asString().c_str());
-				g_director->setPalette(currentPalette);
+				_vm->setPalette(currentPalette);
 				return;
 			}
 
@@ -1342,8 +1342,8 @@ void Score::renderPaletteCycle(RenderMode mode) {
 				if (debugChannelSet(-1, kDebugFast))
 					frameRate = 30;
 
-				if (g_director->_fpsLimit)
-					frameRate = MIN((int)g_director->_fpsLimit, frameRate);
+				if (_vm->_fpsLimit)
+					frameRate = MIN((int)_vm->_fpsLimit, frameRate);
 
 				int frameDelay = 1000 / 60;
 				int fadeFrames = kFadeColorFrames[frameRate - 1];
@@ -1351,22 +1351,22 @@ void Score::renderPaletteCycle(RenderMode mode) {
 					fadeFrames = kFadeColorFramesD5[frameRate - 1];
 
 				// Wait for a fixed time
-				g_director->setPalette(fadePal, 256);
-				g_director->draw();
+				_vm->setPalette(fadePal, 256);
+				_vm->draw();
 				for (int i = 0; i < kFadeColorWait; i++) {
 					uint32 startTime = g_system->getMillis();
 					if (_activeFade) {
 						_activeFade = _soundManager->fadeChannels();
 					}
 					// On click, stop loop and reset palette
-					if (_vm->processEvents(true)) {
+					if (_vm->processSysEvents(true)) {
 						debugC(2, kDebugImages, "Score::renderPaletteCycle(): interrupted, setting palette to %s", currentPalette.asString().c_str());
-						g_director->setPalette(currentPalette);
+						_vm->setPalette(currentPalette);
 						return;
 					}
 					uint32 endTime = g_system->getMillis();
 					int diff = (int)frameDelay - (int)(endTime - startTime);
-					g_director->delayMillis(MAX(0, diff));
+					_vm->delayMillis(MAX(0, diff));
 				}
 
 				debugC(2, kDebugImages, "Score::renderPaletteCycle(): fading palette to %s over %d frames", currentPalette.asString().c_str(), fadeFrames);
@@ -1380,20 +1380,20 @@ void Score::renderPaletteCycle(RenderMode mode) {
 						i + 1,
 						fadeFrames
 					);
-					g_director->setPalette(calcPal, 256);
-					g_director->draw();
+					_vm->setPalette(calcPal, 256);
+					_vm->draw();
 					if (_activeFade) {
 						_activeFade = _soundManager->fadeChannels();
 					}
 					// On click, stop loop and reset palette
-					if (_vm->processEvents(true)) {
+					if (_vm->processSysEvents(true)) {
 						debugC(2, kDebugImages, "Score::renderPaletteCycle(): interrupted, setting palette to %s", currentPalette.asString().c_str());
-						g_director->setPalette(currentPalette);
+						_vm->setPalette(currentPalette);
 						return;
 					}
 					uint32 endTime = g_system->getMillis();
 					int diff = (int)frameDelay - (int)(endTime - startTime);
-					g_director->delayMillis(MAX(0, diff));
+					_vm->delayMillis(MAX(0, diff));
 				}
 
 			}
@@ -1435,8 +1435,8 @@ void Score::renderCursor(Common::Point pos, bool forceUpdate) {
 
 			// try to use the cursor read from exe file.
 			// currently, we are using mac arrow to represent custom win cursor since we didn't find where it stores. So i comment it out here.
-//			if (g_director->getPlatform() == Common::kPlatformWindows && _channels[spriteId]->_cursor._cursorType == Graphics::kMacCursorCustom)
-//				_vm->_wm->replaceCursor(_channels[spriteId]->_cursor._cursorType, g_director->_winCursor[_channels[spriteId]->_cursor._cursorResId]);
+//			if (_vm->getPlatform() == Common::kPlatformWindows && _channels[spriteId]->_cursor._cursorType == Graphics::kMacCursorCustom)
+//				_vm->_wm->replaceCursor(_channels[spriteId]->_cursor._cursorType, _vm->_winCursor[_channels[spriteId]->_cursor._cursorResId]);
 			_vm->_wm->replaceCursor(_channels[spriteId]->_cursor._cursorType, &_channels[spriteId]->_cursor);
 			_currentCursor = _channels[spriteId]->_cursor.getRef();
 			return;
@@ -1531,9 +1531,9 @@ void Score::screenShot() {
 	Graphics::Surface *newSurface = rawSurface.convertTo(requiredFormat_4byte, _vm->getPalette());
 
 	Common::String currentPath = _vm->getCurrentPath().c_str();
-	Common::replace(currentPath, Common::String(g_director->_dirSeparator), "-"); // exclude dir separator from screenshot filename prefix
+	Common::replace(currentPath, Common::String(_vm->_dirSeparator), "-"); // exclude dir separator from screenshot filename prefix
 	Common::String prefix = Common::String::format("%s%s", currentPath.c_str(), Common::punycode_encodefilename(_movie->getMacName()).c_str());
-	Common::Path filename = dumpScriptName(prefix.c_str(), kMovieScript, g_director->_framesRan, "png");
+	Common::Path filename = dumpScriptName(prefix.c_str(), kMovieScript, _vm->_framesRan, "png");
 
 	const char *buildNumber = getenv("BUILD_NUMBER");
 
@@ -1543,7 +1543,7 @@ void Score::screenShot() {
 		// ./dumps/theapartment/25/xn--Main Menu-zd0e-19.png
 
 		Common::Path buildDir(Common::String::format("%s/%s", ConfMan.get("screenshotpath").c_str(),
-			g_director->getTargetName().c_str()), '/');
+			_vm->getTargetName().c_str()), '/');
 
 		// We run for the first time, let's check if we had the directory previously
 		if (_previousBuildBotBuild == -1) {
@@ -1559,7 +1559,7 @@ void Score::screenShot() {
 
 		// Now we try to find any previous dump
 		while (prevbuild > 0) {
-			filename = buildDir.join(Common::Path(Common::String::format("%d/%s-%d.png", prevbuild, prefix.c_str(), g_director->_framesRan), '/'));
+			filename = buildDir.join(Common::Path(Common::String::format("%d/%s-%d.png", prevbuild, prefix.c_str(), _vm->_framesRan), '/'));
 
 			Common::FSNode fs(filename);
 
@@ -1594,7 +1594,7 @@ void Score::screenShot() {
 		// the screenshot was different from the previous one.
 		//
 		// Regenerate file name with the correct build number
-		filename = buildDir.join(Common::Path(Common::String::format("%s/%s-%d.png", buildNumber, prefix.c_str(), g_director->_framesRan), '/'));
+		filename = buildDir.join(Common::Path(Common::String::format("%s/%s-%d.png", buildNumber, prefix.c_str(), _vm->_framesRan), '/'));
 	}
 
 	Common::DumpFile screenshotFile;
@@ -1613,7 +1613,7 @@ void Score::screenShot() {
 }
 
 uint16 Score::getSpriteIDOfActiveWidget() {
-	Graphics::MacWidget *active = g_director->_wm->getActiveWidget();
+	Graphics::MacWidget *active = _vm->_wm->getActiveWidget();
 	if (!active)
 		return 0;
 	for (int i = _channels.size() - 1; i >= 0; i--) {
@@ -2404,16 +2404,16 @@ void Score::loadActions(Common::SeekableReadStreamEndian &stream) {
 Common::String Score::formatChannelInfo() {
 	Frame &frame = *_currentFrame;
 	Common::String result;
-	CastMemberID defaultPalette = g_director->getCurrentMovie()->_defaultPalette;
+	CastMemberID defaultPalette = _vm->getCurrentMovie()->_defaultPalette;
 	result += Common::String::format("TMPO:   tempo: %d, skipFrameFlag: %d, blend: %d, currentFPS: %d\n",
 		frame._mainChannels.tempo, frame._mainChannels.skipFrameFlag, frame._mainChannels.blend, _currentFrameRate);
 	if (!frame._mainChannels.palette.paletteId.isNull()) {
 		result += Common::String::format("PAL:    paletteId: %s, firstColor: %d, lastColor: %d, flags: %d, cycleCount: %d, speed: %d, frameCount: %d, fade: %d, delay: %d, style: %d, currentId: %s, defaultId: %s\n",
 			frame._mainChannels.palette.paletteId.asString().c_str(), frame._mainChannels.palette.firstColor, frame._mainChannels.palette.lastColor, frame._mainChannels.palette.flags,
 			frame._mainChannels.palette.cycleCount, frame._mainChannels.palette.speed, frame._mainChannels.palette.frameCount,
-			frame._mainChannels.palette.fade, frame._mainChannels.palette.delay, frame._mainChannels.palette.style, g_director->_lastPalette.asString().c_str(), defaultPalette.asString().c_str());
+			frame._mainChannels.palette.fade, frame._mainChannels.palette.delay, frame._mainChannels.palette.style, _vm->_lastPalette.asString().c_str(), defaultPalette.asString().c_str());
 	} else {
-		result += Common::String::format("PAL:    paletteId: 000, currentId: %s, defaultId: %s\n", g_director->_lastPalette.asString().c_str(), defaultPalette.asString().c_str());
+		result += Common::String::format("PAL:    paletteId: 000, currentId: %s, defaultId: %s\n", _vm->_lastPalette.asString().c_str(), defaultPalette.asString().c_str());
 	}
 	result += Common::String::format("TRAN:   transType: %d, transDuration: %d, transChunkSize: %d\n",
 		frame._mainChannels.transType, frame._mainChannels.transDuration, frame._mainChannels.transChunkSize);
diff --git a/engines/director/transitions.cpp b/engines/director/transitions.cpp
index 46894c6a869..c7955b75e19 100644
--- a/engines/director/transitions.cpp
+++ b/engines/director/transitions.cpp
@@ -566,7 +566,7 @@ void Window::playTransition(uint frame, RenderMode mode, uint16 transDuration, u
 
 		composeSurface->blitFrom(*blitFrom, rfrom, Common::Point(rto.left, rto.top));
 
-		if (_vm->processEvents(true)) {
+		if (_vm->processSysEvents(true)) {
 			exitTransition(t, &nextFrame, clipRect);
 			break;
 		}
@@ -781,7 +781,7 @@ void Window::dissolveTrans(TransParams &t, Common::Rect &clipRect, Graphics::Man
 
 		g_lingo->executePerFrameHook(t.frame, i + 1, false);
 
-		if (_vm->processEvents(true)) {
+		if (_vm->processSysEvents(true)) {
 			exitTransition(t, nextFrame, clipRect);
 			break;
 		}
@@ -907,7 +907,7 @@ void Window::dissolvePatternsTrans(TransParams &t, Common::Rect &clipRect, Graph
 
 		g_lingo->executePerFrameHook(t.frame, i + 1, false);
 
-		if (_vm->processEvents(true)) {
+		if (_vm->processSysEvents(true)) {
 			exitTransition(t, nextFrame, clipRect);
 			break;
 		}
@@ -1092,7 +1092,7 @@ void Window::transMultiPass(TransParams &t, Common::Rect &clipRect, Graphics::Ma
 		debugC(6, kDebugImages, "Window::transMultiPass(): delaying for %d", diff);
 		g_director->delayMillis(diff);
 
-		if (_vm->processEvents(true)) {
+		if (_vm->processSysEvents(true)) {
 			exitTransition(t, nextFrame, clipRect);
 			break;
 		}
@@ -1146,7 +1146,7 @@ void Window::transZoom(TransParams &t, Common::Rect &clipRect, Graphics::Managed
 		r.setWidth(t.xStepSize * i * 2 / TSTEP_FRAC);
 		r.moveTo(clipRect.left + w / 2 - t.xStepSize * i / TSTEP_FRAC, clipRect.top + h / 2 - t.yStepSize * i / TSTEP_FRAC);
 
-		if (_vm->processEvents(true)) {
+		if (_vm->processSysEvents(true)) {
 			exitTransition(t, nextFrame, clipRect);
 			break;
 		}
diff --git a/engines/director/window.cpp b/engines/director/window.cpp
index 9602f3e4e6f..6f9d18be1c8 100644
--- a/engines/director/window.cpp
+++ b/engines/director/window.cpp
@@ -617,7 +617,7 @@ bool Window::loadNextMovie() {
 			while (delay != 0) {
 				uint32 dec = MIN((uint32)10, delay);
 				// Skip delay if mouse is clicked
-				if (g_director->processEvents(true, true)) {
+				if (g_director->processSysEvents(true, true)) {
 					g_director->loadSlowdownCooloff();
 					break;
 				}
diff --git a/engines/director/window.h b/engines/director/window.h
index 5c0bc63a505..0139e0cb962 100644
--- a/engines/director/window.h
+++ b/engines/director/window.h
@@ -187,7 +187,7 @@ public:
 	static void inkBlitFrom(Channel *channel, Common::Rect destRect, Graphics::ManagedSurface *blitTo = nullptr);
 
 	// events.cpp
-	bool processEvent(Common::Event &event);
+	bool processSysEvent(Common::Event &event);
 	bool processWMEvent(Graphics::WindowClick click, Common::Event &event);
 	void sendWindowEvent(LEvent event);
 
diff --git a/graphics/macgui/macwindow.cpp b/graphics/macgui/macwindow.cpp
index 4199132057c..2823c889644 100644
--- a/graphics/macgui/macwindow.cpp
+++ b/graphics/macgui/macwindow.cpp
@@ -618,7 +618,7 @@ bool MacWindow::processEvent(Common::Event &event) {
 			return result;
 
 		if (_wm->getActiveWidget())
-			return _wm->getActiveWidget()->processEvent(event) || result;
+			return result && _wm->getActiveWidget()->processEvent(event);
 
 		return result;
 


Commit: ff813b5efcca39de6b1d01c390f6a166eb2abd61
    https://github.com/scummvm/scummvm/commit/ff813b5efcca39de6b1d01c390f6a166eb2abd61
Author: Scott Percival (code at moral.net.au)
Date: 2026-06-01T20:06:27+02:00

Commit Message:
DIRECTOR: Clear window modal flag in destructor

Fixes option screen freeze after saving in Opera Fatal.

Changed paths:
    engines/director/window.cpp


diff --git a/engines/director/window.cpp b/engines/director/window.cpp
index 6f9d18be1c8..295d262fb48 100644
--- a/engines/director/window.cpp
+++ b/engines/director/window.cpp
@@ -91,6 +91,10 @@ Window::~Window() {
 		delete _frozenLingoStates[i];
 	if (_puppetTransition)
 		delete _puppetTransition;
+	if (_isModal) {
+		_wm->setLockedWidget(nullptr);
+		_isModal = false;
+	}
 	g_director->_wm->removeWindow(_window);
 	g_director->_wm->removeMarked();
 	_window = nullptr;


Commit: bbc58f2b47eaca226b43cf7932429fe2fb603833
    https://github.com/scummvm/scummvm/commit/bbc58f2b47eaca226b43cf7932429fe2fb603833
Author: Scott Percival (code at moral.net.au)
Date: 2026-06-01T20:06:27+02:00

Commit Message:
DIRECTOR: Comment out crash-inducing movie export debug code

Changed paths:
    engines/director/cast.cpp
    engines/director/castmember/filmloop.cpp
    engines/director/castmember/palette.cpp
    engines/director/castmember/text.cpp


diff --git a/engines/director/cast.cpp b/engines/director/cast.cpp
index ef9b52d450d..1915d5d1558 100644
--- a/engines/director/cast.cpp
+++ b/engines/director/cast.cpp
@@ -694,7 +694,8 @@ void Cast::saveConfig(Common::SeekableWriteStream *writeStream, uint32 offset) {
 		writeStream->writeSint16BE(_windowDragRegionMaskId.member);
 	}
 
-	if (debugChannelSet(7, kDebugSaving)) {
+	// FIXME: can't dereference SeekableWriteStream
+	/*if (debugChannelSet(7, kDebugSaving)) {
 		// Adding +8 because the stream doesn't include the header and the entry for the size itself
 		byte *dumpData = (byte *)calloc(configSize + 8, sizeof(byte));
 
@@ -708,7 +709,7 @@ void Cast::saveConfig(Common::SeekableWriteStream *writeStream, uint32 offset) {
 		dumpFile("ConfigData", 0, MKTAG('V', 'W', 'C', 'F'), dumpData, configSize + 8);
 		free(dumpData);
 		delete dumpStream;
-	}
+	}*/
 
 }
 
@@ -950,7 +951,8 @@ void Cast::saveCastData(Common::SeekableWriteStream *writeStream, Resource *res)
 
 	debugC(5, kDebugSaving, "Cast::saveCastData()::Saving 'CASt' resource, id: %d, size: %d, type: %s", id, castSize, castType2str(type));
 
-	if (debugChannelSet(7, kDebugSaving)) {
+	// FIXME: can't dereference SeekableWriteStream
+	/*if (debugChannelSet(7, kDebugSaving)) {
 		byte *dumpData = (byte *)calloc(castSize + 8, sizeof(byte));
 		Common::SeekableMemoryWriteStream *dumpStream = new Common::SeekableMemoryWriteStream(dumpData, castSize + 8);
 
@@ -962,7 +964,7 @@ void Cast::saveCastData(Common::SeekableWriteStream *writeStream, Resource *res)
 		dumpFile(castType2str(type), res->index, MKTAG('C', 'A', 'S', 't'), dumpData, castSize + 8);
 		free(dumpData);
 		delete dumpStream;
-	}
+	}*/
 }
 
 void Cast::writeCastInfo(Common::SeekableWriteStream *writeStream, uint32 castId) {
diff --git a/engines/director/castmember/filmloop.cpp b/engines/director/castmember/filmloop.cpp
index 1ca1764d6c3..ca01dfd5440 100644
--- a/engines/director/castmember/filmloop.cpp
+++ b/engines/director/castmember/filmloop.cpp
@@ -360,7 +360,8 @@ void FilmLoopCastMember::writeSCVWResource(Common::SeekableWriteStream *writeStr
 
 	}
 
-	if (debugChannelSet(7, kDebugSaving)) {
+	// FIXME: can't dereference SeekableWriteStream
+	/*if (debugChannelSet(7, kDebugSaving)) {
 		// Adding +8 because the stream doesn't include the header and the entry for the size itself
 		byte *dumpData = (byte *)calloc(filmloopSize + 8, sizeof(byte));
 
@@ -374,7 +375,7 @@ void FilmLoopCastMember::writeSCVWResource(Common::SeekableWriteStream *writeStr
 		dumpFile("FilmLoopData", 0, MKTAG('V', 'W', 'C', 'F'), dumpData, filmloopSize);
 		free(dumpData);
 		delete dumpStream;
-	}
+	}*/
 }
 
 uint32 FilmLoopCastMember::getSCVWResourceSize() {
diff --git a/engines/director/castmember/palette.cpp b/engines/director/castmember/palette.cpp
index 0a59f067cd5..6994cff3edb 100644
--- a/engines/director/castmember/palette.cpp
+++ b/engines/director/castmember/palette.cpp
@@ -183,7 +183,8 @@ void PaletteCastMember::writePaletteData(Common::SeekableWriteStream *writeStrea
 		writeStream->writeUint16BE(pal[3 * i + 2] << 8);
 	}
 
-	if (debugChannelSet(7, kDebugSaving)) {
+	// FIXME: can't dereference SeekableWriteStream
+	/* if (debugChannelSet(7, kDebugSaving)) {
 		byte *dumpData = nullptr;
 		dumpData = (byte *)calloc(castSize, sizeof(byte));
 		auto dumpStream = new Common::SeekableMemoryWriteStream(dumpData, castSize + 8);
@@ -196,7 +197,7 @@ void PaletteCastMember::writePaletteData(Common::SeekableWriteStream *writeStrea
 		dumpFile("PaletteData", _castId, MKTAG('C', 'L', 'U', 'T'), dumpData, castSize);
 		free(dumpData);
 		delete dumpStream;
-	}
+	}*/
 }
 
 }	// End of namespace Director
diff --git a/engines/director/castmember/text.cpp b/engines/director/castmember/text.cpp
index 06806dd8ecf..7fd5a312104 100644
--- a/engines/director/castmember/text.cpp
+++ b/engines/director/castmember/text.cpp
@@ -1085,7 +1085,8 @@ uint32 TextCastMember::writeSTXTResource(Common::SeekableWriteStream *writeStrea
 	writeStream->writeString(rawText);
 	writeStream->seek(currentPos);
 
-	if (debugChannelSet(7, kDebugSaving)) {
+	// FIXME: can't dereference SeekableWriteStream
+	/*if (debugChannelSet(7, kDebugSaving)) {
 		byte *dumpData = nullptr;
 		dumpData = (byte *)calloc(stxtSize, sizeof(byte));
 		Common::MemoryWriteStream *dumpStream = new Common::SeekableMemoryWriteStream(dumpData, stxtSize);
@@ -1098,7 +1099,7 @@ uint32 TextCastMember::writeSTXTResource(Common::SeekableWriteStream *writeStrea
 		dumpFile("TextData", _castId, MKTAG('S', 'T', 'X', 'T'), dumpData, getSTXTResourceSize() + 8);
 		free(dumpData);
 		delete dumpStream;
-	}
+	}*/
 
 	return stxtSize + 8;
 }



From noreply at scummvm.org  Mon Jun  1 18:22:57 2026
From: noreply at scummvm.org (ScummVM-Translations)
Date: Mon,  1 Jun 2026 18:22:57 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 bf976b80237c0972392efaed1c05f5afe26e6d5b
Message-ID: <20260601182257.3BABF700591@ron.scummvm.net>

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

Summary:
bf976b8023 I18N: Update translation (French)


Commit: bf976b80237c0972392efaed1c05f5afe26e6d5b
    https://github.com/scummvm/scummvm/commit/bf976b80237c0972392efaed1c05f5afe26e6d5b
Author: Purple T (ZEONK at hotmail.com)
Date: 2026-06-01T18:22:49Z

Commit Message:
I18N: Update translation (French)

Currently translated at 99.9% (3202 of 3203 strings)

Changed paths:
    po/fr_FR.po


diff --git a/po/fr_FR.po b/po/fr_FR.po
index ec931d56349..b380ea97812 100644
--- a/po/fr_FR.po
+++ b/po/fr_FR.po
@@ -8,7 +8,7 @@ msgstr ""
 "Project-Id-Version: ScummVM 1.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
 "POT-Creation-Date: 2026-06-01 16:27+0000\n"
-"PO-Revision-Date: 2026-06-01 09:35+0000\n"
+"PO-Revision-Date: 2026-06-01 18:22+0000\n"
 "Last-Translator: Purple T <ZEONK at hotmail.com>\n"
 "Language-Team: French <https://translations.scummvm.org/projects/scummvm/"
 "scummvm/fr/>\n"
@@ -13742,7 +13742,7 @@ msgstr "Gagner les bagarres en b??cane (cheat)"
 
 #: engines/scumm/metaengine.cpp:1118
 msgid "Rebel Assault controls"
-msgstr "Contr??les Rebel Assault"
+msgstr "Contr??les de Rebel Assault"
 
 #: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1182
 msgid "Aim up / menu up"
@@ -13786,7 +13786,7 @@ msgstr "Retour / passer"
 
 #: engines/scumm/metaengine.cpp:1180
 msgid "Rebel Assault II controls"
-msgstr "Contr??les Rebel Assault II"
+msgstr "Contr??les de Rebel Assault II"
 
 #: engines/scumm/metaengine.cpp:1235
 msgid "Cover / back"



From noreply at scummvm.org  Mon Jun  1 19:59:10 2026
From: noreply at scummvm.org (neuromancer)
Date: Mon,  1 Jun 2026 19:59:10 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 90490c2792c55d85cbda11825683eca383eceecc
Message-ID: <20260601195910.56E5F700591@ron.scummvm.net>

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

Summary:
bf3a892f5f SCUMM: RA2: improved input handling
5b40d53e96 SCUMM: RA2: fixed a bug in the duplicate/delete pilot code
4dd584d879 SCUMM: RA: make sure music is not interrupted
90490c2792 SCUMM: RA1: improve menu navigation using gamepad


Commit: bf3a892f5fe68a8bda3cc3728ac7da6637a11637
    https://github.com/scummvm/scummvm/commit/bf3a892f5fe68a8bda3cc3728ac7da6637a11637
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-01T21:58:55+02:00

Commit Message:
SCUMM: RA2: improved input handling

Changed paths:
    engines/scumm/insane/rebel2/iact.cpp
    engines/scumm/insane/rebel2/rebel.cpp


diff --git a/engines/scumm/insane/rebel2/iact.cpp b/engines/scumm/insane/rebel2/iact.cpp
index fe30e70a2dc..6fb4ffec57e 100644
--- a/engines/scumm/insane/rebel2/iact.cpp
+++ b/engines/scumm/insane/rebel2/iact.cpp
@@ -801,11 +801,14 @@ void InsaneRebel2::handleOpcode6Handler7(Common::SeekableReadStream &b, int16 pa
 	//   -> velocity history averaging -> physics delta (clamped +/-12/frame)
 	//   -> position clamping -> corridor collision -> perspective offsets
 	//
-	// Level data table (DAT_0047e0e8 + level*0x242 + difficulty*0x22):
-	//   offset 0: smoothing param (>>4 +1 = window size)
+	// Level data table (DAT_0047e0e8 + difficulty*0x242 + levelType*0x22):
 	//   offset 2: Y speed          offset 4: X speed (levelSpeed)
-	//   offset 6: wind multiplier  offset 14: corridor damage
-	// We don't have the actual level data, so we use calibrated defaults.
+	//   offset 6: wind multiplier
+	// Our extracted difficulty table starts at DAT_0047e0f0, so for Handler 7
+	// level types these fields map to lift/slide/drift of the preceding row.
+	const int flightParamIndex = CLIP(_rebelLevelType - 1, 0, 16);
+	const LevelDifficultyParams &flightParams =
+		kDifficultyTable[CLIP(_difficulty, 0, 5)][flightParamIndex];
 
 	// Step 1: Mouse input as offset from screen center.
 	// DAT_0047a7e0 = mouseX - 160, DAT_0047a7e2 = mouseY - 100.
@@ -836,7 +839,6 @@ void InsaneRebel2::handleOpcode6Handler7(Common::SeekableReadStream &b, int16 pa
 	}
 	_velocityHistory[0] = scaledInputX;
 
-	// Window size = (levelData[0] >> 4) + 1. Calibrated default: 5.
 	const int smoothWindow = 5;
 	int velSum = 0;
 	for (int i = 0; i < smoothWindow; i++) {
@@ -845,8 +847,7 @@ void InsaneRebel2::handleOpcode6Handler7(Common::SeekableReadStream &b, int16 pa
 	_smoothedVelocity = (int16)(velSum / smoothWindow);  // DAT_0044370c
 
 	// Step 4: Wind history (lines 158-173).
-	// Wind multiplier comes from level data[6]. Without data, use 0 (no wind).
-	const int16 windMult = 0;
+	const int16 windMult = flightParams.driftRate;
 	int windSumX = 0, windSumY = 0;
 	for (int i = 14; i > 0; i--) {
 		_windHistoryX[i] = _windHistoryX[i - 1];
@@ -863,12 +864,8 @@ void InsaneRebel2::handleOpcode6Handler7(Common::SeekableReadStream &b, int16 pa
 	int16 windEffectY = (int16)((windMult * (windSumY + _windParamY)) / 15);
 
 	// Step 5: Position delta (lines 174-242).
-	// levelSpeed (offset 4): calibrated so max velocity (127) -> delta 12.
-	//   8 = (speed * 127) >> 9 -> speed around 32
-	// levelYSpeed (offset 2): calibrated so max input (127) -> delta ~6.
-	//   6 = (speed * 127) >> 10 -> speed around 48
-	const int16 levelSpeed = 32;
-	const int16 levelYSpeed = 48;
+	const int16 levelSpeed = flightParams.slideRate;
+	const int16 levelYSpeed = flightParams.liftRate;
 	int16 absSmoothVel = ABS(_smoothedVelocity);
 	int16 positionDeltaX;
 
diff --git a/engines/scumm/insane/rebel2/rebel.cpp b/engines/scumm/insane/rebel2/rebel.cpp
index 4c363df1be4..501186e152b 100644
--- a/engines/scumm/insane/rebel2/rebel.cpp
+++ b/engines/scumm/insane/rebel2/rebel.cpp
@@ -1596,7 +1596,12 @@ Common::Point InsaneRebel2::getGameplayAimPoint() {
 	// Pure getter (queried many times per frame): the aim/reticle follows the virtual
 	// mouse position. Directional controls pan that position incrementally once per frame
 	// via updateGameplayAimFromGamepad(), rather than snapping the reticle to a screen edge.
-	return Common::Point(_vm->_mouse.x, _vm->_mouse.y);
+	int y = _vm->_mouse.y;
+	if (_optControlsFlipped) {
+		// Original DAT_0047a7fe reverses only the up/down gameplay axis.
+		y = CLIP<int>(200 - y, 0, 199);
+	}
+	return Common::Point(_vm->_mouse.x, y);
 }
 
 // Apply the user's configured analog deadzone so a resting stick reports no


Commit: 5b40d53e96ed8200127fcbfea77a98ac16285d9f
    https://github.com/scummvm/scummvm/commit/5b40d53e96ed8200127fcbfea77a98ac16285d9f
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-01T21:58:55+02:00

Commit Message:
SCUMM: RA2: fixed a bug in the duplicate/delete pilot code

Changed paths:
    engines/scumm/insane/rebel2/menu.cpp
    engines/scumm/insane/rebel2/rebel.h


diff --git a/engines/scumm/insane/rebel2/menu.cpp b/engines/scumm/insane/rebel2/menu.cpp
index 8dfdd0cac36..5a7221e3ad1 100644
--- a/engines/scumm/insane/rebel2/menu.cpp
+++ b/engines/scumm/insane/rebel2/menu.cpp
@@ -1239,6 +1239,32 @@ int InsaneRebel2::runLevelSelect() {
 			continue;
 		}
 
+		// --- Pilot operation submenu completed ---
+		if (_pilotMenuMode == kPilotModeCopySelect || _pilotMenuMode == kPilotModeDeleteSelect) {
+			int pilotIndex = _levelSelection;
+			if (pilotIndex >= 0 && pilotIndex < _numPilots) {
+				if (_pilotMenuMode == kPilotModeCopySelect) {
+					copyPilot(pilotIndex);
+					savePilots();
+					_levelSelection = pilotIndex;
+					debug("Rebel2: Copied pilot %d, now %d pilots", pilotIndex, _numPilots);
+				} else {
+					deletePilot(pilotIndex);
+					savePilots();
+					if (_numPilots > 0) {
+						_levelSelection = (pilotIndex != 0) ? pilotIndex - 1 : 0;
+					} else {
+						_levelSelection = 0;
+					}
+					debug("Rebel2: Deleted pilot %d, %d remaining", pilotIndex, _numPilots);
+				}
+			}
+			_pilotMenuMode = kPilotModeSelect;
+			_levelItemCount = _numPilots + 4;
+			_gameState = kStatePilotSelect;
+			continue;
+		}
+
 		// --- Normal pilot menu selection ---
 		debug("Rebel2: Pilot selection: %d (numPilots=%d)", _levelSelection, _numPilots);
 
@@ -1270,27 +1296,22 @@ int InsaneRebel2::runLevelSelect() {
 			continue;
 
 		} else if (_levelSelection == _numPilots + 1) {
-			// COPY PILOT
+			// COPY PILOT - original opens a second menu to select the source pilot.
 			if (_numPilots > 0 && _numPilots < kMaxPilots) {
-				// Copy first pilot (slot 0) by default ??? original swaps with selected
-				int srcIdx = (_levelSelection > 0 && _levelSelection <= _numPilots) ? _levelSelection - 1 : 0;
-				copyPilot(srcIdx);
-				savePilots();
-				_levelItemCount = _numPilots + 4;
-				debug("Rebel2: Copied pilot %d, now %d pilots", srcIdx, _numPilots);
+				_pilotMenuMode = kPilotModeCopySelect;
+				_levelSelection = 0;
+				_levelItemCount = _numPilots;
+				debug("Rebel2: COPY PILOT - selecting source");
 			}
 			continue;
 
 		} else if (_levelSelection == _numPilots + 2) {
-			// DELETE PILOT
+			// DELETE PILOT - original opens a second menu to select the target pilot.
 			if (_numPilots > 0) {
-				// Delete the first pilot (slot 0) ??? original has confirm sub-flow
-				deletePilot(0);
-				savePilots();
-				_levelItemCount = _numPilots + 4;
-				if (_levelSelection >= _levelItemCount)
-					_levelSelection = _levelItemCount - 1;
-				debug("Rebel2: Deleted pilot, %d remaining", _numPilots);
+				_pilotMenuMode = kPilotModeDeleteSelect;
+				_levelSelection = 0;
+				_levelItemCount = _numPilots;
+				debug("Rebel2: DELETE PILOT - selecting target");
 			}
 			continue;
 
@@ -1306,8 +1327,8 @@ int InsaneRebel2::runLevelSelect() {
 }
 
 int InsaneRebel2::processLevelSelectInput() {
-	// Process input for pilot selection and difficulty submenu
-	// Handles kPilotModeSelect, kPilotModeNameInput, and kStateDifficultySelect
+	// Process input for pilot selection and difficulty submenu.
+	// Handles pilot list, pilot operation submenus, name input, and difficulty.
 	// Returns: -1 = no action, 0+ = item selected
 
 	int result = -1;
@@ -1364,8 +1385,13 @@ int InsaneRebel2::processLevelSelectInput() {
 
 	// Normal menu navigation (pilot select or difficulty submenu)
 	bool isDifficultyMode = (_gameState == kStateDifficultySelect);
+	bool isPilotOperationMode =
+		(_pilotMenuMode == kPilotModeCopySelect || _pilotMenuMode == kPilotModeDeleteSelect);
 	int &selection = isDifficultyMode ? _difficultySelection : _levelSelection;
-	int itemCount = isDifficultyMode ? 6 : _levelItemCount;
+	int itemCount = isDifficultyMode ? 6 : (isPilotOperationMode ? _numPilots : _levelItemCount);
+	if (itemCount <= 0)
+		return -1;
+
 	const int itemBaseY = itemCount * -5 + 0x68;
 	const int itemSpacing = 10;
 
@@ -1398,6 +1424,11 @@ int InsaneRebel2::processLevelSelectInput() {
 			case Common::KEYCODE_ESCAPE:
 				if (isDifficultyMode) {
 					_gameState = kStatePilotSelect;
+				} else if (isPilotOperationMode) {
+					bool wasCopyMode = (_pilotMenuMode == kPilotModeCopySelect);
+					_pilotMenuMode = kPilotModeSelect;
+					_levelItemCount = _numPilots + 4;
+					_levelSelection = _numPilots + (wasCopyMode ? 1 : 2);
 				} else {
 					result = _levelItemCount - 1;  // Last item = MAIN MENU
 				}
@@ -1437,6 +1468,11 @@ int InsaneRebel2::processLevelSelectInput() {
 		case Common::EVENT_RETURN_TO_LAUNCHER:
 			if (isDifficultyMode) {
 				_gameState = kStatePilotSelect;
+			} else if (isPilotOperationMode) {
+				_pilotMenuMode = kPilotModeSelect;
+				_levelItemCount = _numPilots + 4;
+				_levelSelection = _levelItemCount - 1;
+				result = _levelSelection;
 			} else {
 				result = _levelItemCount - 1;
 			}
@@ -1480,6 +1516,30 @@ void InsaneRebel2::drawLevelSelectOverlay(byte *renderBitmap, int pitch, int wid
 		return;
 	}
 
+	if (_pilotMenuMode == kPilotModeCopySelect || _pilotMenuMode == kPilotModeDeleteSelect) {
+		Common::String pilotNameStrs[kMaxPilots];
+		for (int i = 0; i < _numPilots; i++) {
+			pilotNameStrs[i] = Common::String::format("^f01^c005%s^f00", _pilots[i].name);
+		}
+
+		const char *pilotItems[kMaxPilots + 1];
+		int idx = 0;
+		pilotItems[idx++] = splayer->getString(_pilotMenuMode == kPilotModeCopySelect ? 28 : 27);
+
+		for (int i = 0; i < _numPilots; i++) {
+			pilotItems[idx++] = pilotNameStrs[i].c_str();
+		}
+
+		for (int i = 0; i < idx; i++) {
+			if (!pilotItems[i] || !pilotItems[i][0]) {
+				pilotItems[i] = "";
+			}
+		}
+
+		drawMenuItems(renderBitmap, pitch, width, height, pilotItems, _numPilots, _levelSelection);
+		return;
+	}
+
 	// -------------------------------------------------------------------
 	// Pilot menu - FUN_0041f5ae(0, &DAT_00457768, N+4, 0)
 	// -------------------------------------------------------------------
diff --git a/engines/scumm/insane/rebel2/rebel.h b/engines/scumm/insane/rebel2/rebel.h
index 926c23d69ab..6f24d48a69f 100644
--- a/engines/scumm/insane/rebel2/rebel.h
+++ b/engines/scumm/insane/rebel2/rebel.h
@@ -296,7 +296,9 @@ public:
 	enum PilotMenuMode {
 		kPilotModeSelect = 0,     // Normal pilot list selection
 		kPilotModeNameInput = 1,  // Typing a new pilot name
-		kPilotModeDifficulty = 2  // Difficulty submenu
+		kPilotModeDifficulty = 2, // Difficulty submenu
+		kPilotModeCopySelect = 3, // Selecting source pilot to copy
+		kPilotModeDeleteSelect = 4 // Selecting pilot to delete
 	};
 	PilotMenuMode _pilotMenuMode;
 	Common::String _pilotNameInput;      // Current name being typed


Commit: 4dd584d879196b8cc1c5306280820abbdcd60f4a
    https://github.com/scummvm/scummvm/commit/4dd584d879196b8cc1c5306280820abbdcd60f4a
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-01T21:58:55+02:00

Commit Message:
SCUMM: RA: make sure music is not interrupted

Changed paths:
    engines/scumm/insane/rebel/rebel_audio.cpp
    engines/scumm/insane/rebel/rebel_audio.h
    engines/scumm/insane/rebel1/iact.cpp
    engines/scumm/insane/rebel1/rebel.cpp
    engines/scumm/insane/rebel1/rebel.h
    engines/scumm/insane/rebel1/runlevels.cpp


diff --git a/engines/scumm/insane/rebel/rebel_audio.cpp b/engines/scumm/insane/rebel/rebel_audio.cpp
index b06a58fd884..07204d4806d 100644
--- a/engines/scumm/insane/rebel/rebel_audio.cpp
+++ b/engines/scumm/insane/rebel/rebel_audio.cpp
@@ -87,10 +87,203 @@ void RebelAudio::queueData(int trackIdx, const uint8 *data, int32 size, int volu
 	_vm->_mixer->setChannelBalance(_handles[trackIdx], scaledPan);
 }
 
+bool RebelAudio::processAudioCodes(SmushPlayer *player, int idx, int32 &tmpFeedSize, int &mixVolume) {
+	uint8 *code, *buf, subcode, value;
+	int chunk;
+
+	while (tmpFeedSize) {
+		code = player->_smushDispatch[idx].headerPtr;
+
+		switch (code[0]) {
+		case SAUD_OP_INIT:
+			player->_smushDispatch[idx].audioLength = 0;
+			buf = player->_smushDispatch[idx].headerPtr;
+			player->_smushDispatch[idx].audioRemaining = READ_BE_UINT32(buf + 2);
+			player->_smushDispatch[idx].currentOffset = READ_BE_UINT32(buf + 6);
+			player->_smushDispatch[idx].sampleRate = player->_smushAudioSampleRate;
+			player->_smushDispatch[idx].headerPtr += player->_smushDispatch[idx].headerPtr[1] + 2;
+			if (player->_smushDispatch[idx].audioRemaining < player->_smushTracks[idx].availableSize + (player->_smushTracks[idx].availableSize >= player->_smushTracks[idx].sdatSize ? 0 : 15000) - player->_smushTracks[idx].dataSize) {
+				chunk = player->_smushTracks[idx].availableSize - player->_smushTracks[idx].dataSize - player->_smushDispatch[idx].audioRemaining + 15000;
+				if (chunk > player->_smushDispatch[idx].currentOffset) {
+					player->_smushTracks[idx].state = TRK_STATE_INACTIVE;
+					player->_smushTracks[idx].groupId = GRP_MASTER;
+					tmpFeedSize = 0;
+					break;
+				}
+
+				player->_smushDispatch[idx].audioRemaining += chunk;
+				player->_smushDispatch[idx].currentOffset -= chunk;
+			}
+			break;
+
+		case SAUD_OP_UPDATE_HEADER:
+		case SAUD_OP_COMPARE_GT:
+		case SAUD_OP_COMPARE_LT:
+		case SAUD_OP_COMPARE_EQ:
+		case SAUD_OP_COMPARE_NE:
+			subcode = code[4];
+			switch (subcode) {
+			case SAUD_VALUEID_ALL_VOLS:
+				value = player->_smushTrackVols[0];
+				break;
+			case SAUD_VALUEID_TRK_VOL:
+				value = player->_smushTracks[idx].volume;
+				break;
+			case SAUD_VALUEID_TRK_PAN:
+				value = player->_smushTracks[idx].pan;
+				break;
+			default:
+				value = player->_smushAudioTable[subcode];
+				break;
+			}
+
+			switch (code[0]) {
+			case SAUD_OP_UPDATE_HEADER:
+				value = value || (subcode == 0);
+				break;
+			case SAUD_OP_COMPARE_GT:
+				value = value > code[5];
+				break;
+			case SAUD_OP_COMPARE_LT:
+				value = value < code[5];
+				break;
+			case SAUD_OP_COMPARE_EQ:
+				value = value == code[5];
+				break;
+			case SAUD_OP_COMPARE_NE:
+				value = value != code[5];
+				break;
+			default:
+				break;
+			}
+
+			if (!value) {
+				player->_smushDispatch[idx].headerPtr = &code[code[1] + 2];
+			} else {
+				// Rebel Assault SAUD branch targets are signed relative displacements.
+				player->_smushDispatch[idx].headerPtr = code + READ_BE_INT16(&code[2]);
+			}
+			break;
+
+		case SAUD_OP_SET_PARAM:
+			switch (code[2]) {
+			case SAUD_VALUEID_ALL_VOLS:
+				player->_smushTrackVols[0] = code[3];
+				break;
+			case SAUD_VALUEID_TRK_VOL:
+				player->_smushTracks[idx].volume = code[3];
+				mixVolume = (player->_smushTrackVols[0] * player->_smushTracks[idx].volume) / 127;
+
+				if ((player->_smushTracks[idx].flags & TRK_TYPE_MASK) == IS_BKG_MUSIC && player->isChanActive(CHN_SPEECH))
+					mixVolume = (mixVolume * player->_gainReductionMultiplier) >> 8;
+				break;
+			case SAUD_VALUEID_TRK_PAN:
+				player->_smushTracks[idx].pan = code[3];
+				break;
+			default:
+				player->_smushAudioTable[code[2]] = code[3];
+				break;
+			}
+			player->_smushDispatch[idx].headerPtr = &code[code[1] + 2];
+			break;
+
+		case SAUD_OP_INCR_PARAM:
+			switch (code[2]) {
+			case SAUD_VALUEID_ALL_VOLS:
+				player->_smushTrackVols[0] += code[3];
+				break;
+			case SAUD_VALUEID_TRK_VOL:
+				player->_smushTracks[idx].volume += code[3];
+				break;
+			case SAUD_VALUEID_TRK_PAN:
+				player->_smushTracks[idx].pan += code[3];
+				break;
+			default:
+				player->_smushAudioTable[code[2]] += code[3];
+				break;
+			}
+			player->_smushDispatch[idx].headerPtr = &code[code[1] + 2];
+			break;
+
+		case SAUD_OP_SET_OFFSET:
+			player->_smushDispatch[idx].audioLength = 0;
+			buf = player->_smushDispatch[idx].headerPtr;
+			player->_smushDispatch[idx].audioRemaining = READ_BE_UINT32(buf + 2);
+			player->_smushDispatch[idx].currentOffset = READ_BE_UINT32(buf + 6);
+			player->_smushDispatch[idx].sampleRate = player->_smushAudioSampleRate;
+
+			player->_smushDispatch[idx].headerPtr += player->_smushDispatch[idx].headerPtr[1] + 2;
+			if (player->_smushDispatch[idx].audioRemaining < player->_smushTracks[idx].availableSize + (player->_smushTracks[idx].availableSize >= player->_smushTracks[idx].sdatSize ? 0 : 15000) - player->_smushTracks[idx].dataSize) {
+				chunk = player->_smushTracks[idx].availableSize - player->_smushTracks[idx].dataSize - player->_smushDispatch[idx].audioRemaining + 15000;
+				if (chunk > player->_smushDispatch[idx].currentOffset) {
+					player->_smushTracks[idx].state = TRK_STATE_INACTIVE;
+					player->_smushTracks[idx].groupId = GRP_MASTER;
+					tmpFeedSize = 0;
+					break;
+				}
+
+				player->_smushDispatch[idx].audioRemaining += chunk;
+				player->_smushDispatch[idx].currentOffset -= chunk;
+			}
+			break;
+
+		case SAUD_OP_SET_LENGTH:
+			if (!player->_smushDispatch[idx].audioLength) {
+				player->_smushDispatch[idx].audioLength = READ_BE_UINT32(&code[6]);
+				player->_smushDispatch[idx].elapsedAudio = 0;
+			}
+
+			buf = player->_smushDispatch[idx].headerPtr;
+			player->_smushDispatch[idx].audioRemaining = player->_smushDispatch[idx].elapsedAudio + READ_BE_UINT32(buf + 2);
+
+			player->_smushDispatch[idx].currentOffset = READ_BE_UINT32(buf + 14);
+			if (player->_smushDispatch[idx].currentOffset > player->_smushDispatch[idx].audioLength)
+				player->_smushDispatch[idx].currentOffset = player->_smushDispatch[idx].audioLength;
+
+			player->_smushDispatch[idx].sampleRate = player->_smushAudioSampleRate;
+
+			player->_smushDispatch[idx].audioLength -= player->_smushDispatch[idx].currentOffset;
+			player->_smushDispatch[idx].elapsedAudio += player->_smushDispatch[idx].currentOffset;
+
+			if (player->_smushDispatch[idx].audioLength) {
+				player->_smushDispatch[idx].headerPtr = &code[code[1] + 2];
+			} else {
+				player->_smushDispatch[idx].headerPtr = code + READ_BE_INT16(&code[18]);
+			}
+
+			if (player->_smushDispatch[idx].audioRemaining >= player->_smushTracks[idx].availableSize + (player->_smushTracks[idx].availableSize >= player->_smushTracks[idx].sdatSize ? 0 : 15000) - player->_smushTracks[idx].dataSize) {
+				chunk = player->_smushTracks[idx].availableSize - player->_smushTracks[idx].dataSize - player->_smushDispatch[idx].audioRemaining + 15000;
+				if (chunk > player->_smushDispatch[idx].currentOffset) {
+					player->_smushTracks[idx].state = TRK_STATE_INACTIVE;
+					player->_smushTracks[idx].groupId = GRP_MASTER;
+					tmpFeedSize = 0;
+				} else {
+					player->_smushDispatch[idx].audioRemaining += chunk;
+					player->_smushDispatch[idx].currentOffset -= chunk;
+				}
+			}
+			break;
+
+		default:
+			player->_smushTracks[idx].state = TRK_STATE_INACTIVE;
+			player->_smushTracks[idx].groupId = GRP_MASTER;
+			tmpFeedSize = 0;
+		}
+
+		if (player->_smushDispatch[idx].currentOffset > 0)
+			return false;
+	}
+
+	return true;
+}
+
 void RebelAudio::processFrame(SmushPlayer *player, int16 feedSize) {
 	if (!player)
 		return;
 
+	if (player->_paused)
+		return;
+
 	if (player->_smushTracksNeedInit) {
 		player->_smushTracksNeedInit = false;
 		for (int i = 0; i < SMUSH_MAX_TRACKS; i++) {
@@ -176,6 +369,7 @@ void RebelAudio::processFrame(SmushPlayer *player, int16 feedSize) {
 						if (!dispatch.dataBuf || offset < 0 || offset + mixInFrameCount > dispatch.dataSize)
 							break;
 
+						track.state = TRK_STATE_PLAYING;
 						queueData(i, &dispatch.dataBuf[offset], mixInFrameCount, mixVolume, track.pan);
 
 						dispatch.currentOffset -= mixInFrameCount;
@@ -191,9 +385,7 @@ void RebelAudio::processFrame(SmushPlayer *player, int16 feedSize) {
 				}
 
 				if (dispatch.currentOffset <= 0) {
-					if (!player->processAudioCodes(i, tmpFeedSize, mixVolume))
-						break;
-					if (dispatch.currentOffset <= 0)
+					if (processAudioCodes(player, i, tmpFeedSize, mixVolume) && tmpFeedSize <= 0)
 						break;
 				} else if (tmpFeedSize <= 0) {
 					break;
diff --git a/engines/scumm/insane/rebel/rebel_audio.h b/engines/scumm/insane/rebel/rebel_audio.h
index c20dace1e5e..10e0548bf71 100644
--- a/engines/scumm/insane/rebel/rebel_audio.h
+++ b/engines/scumm/insane/rebel/rebel_audio.h
@@ -48,6 +48,8 @@ public:
 private:
 	static const int kMaxTracks = 4;
 
+	bool processAudioCodes(SmushPlayer *player, int idx, int32 &tmpFeedSize, int &mixVolume);
+
 	ScummEngine_v7 *_vm;
 	Audio::QueuingAudioStream *_streams[kMaxTracks];
 	Audio::SoundHandle _handles[kMaxTracks];
diff --git a/engines/scumm/insane/rebel1/iact.cpp b/engines/scumm/insane/rebel1/iact.cpp
index f3d2a507789..db4c4593dc9 100644
--- a/engines/scumm/insane/rebel1/iact.cpp
+++ b/engines/scumm/insane/rebel1/iact.cpp
@@ -1222,6 +1222,7 @@ void InsaneRebel1::updateShipPhysics() {
 	if (_pathBranchEnabled && _gameCounter >= kPathBranchCounter) {
 		if (_shipPosX > kRA1CenterX) {
 			_rightPathSelected = true;
+			preserveInteractiveVideoAudioState();
 			_vm->_smushVideoShouldFinish = true;
 			debugC(DEBUG_INSANE, "RA1: Right path selected (counter=%d, shipX=%d)", _gameCounter, _shipPosX);
 		} else {
diff --git a/engines/scumm/insane/rebel1/rebel.cpp b/engines/scumm/insane/rebel1/rebel.cpp
index 8341fa0c1cd..f8efc33a1ea 100644
--- a/engines/scumm/insane/rebel1/rebel.cpp
+++ b/engines/scumm/insane/rebel1/rebel.cpp
@@ -313,6 +313,10 @@ InsaneRebel1::InsaneRebel1(ScummEngine_v7 *scumm) : Insane(), _vm(scumm) {
 	_hudDirtyFlag = 0;
 	_maxChapterUnlocked = 0;
 	_interactiveVideoActive = false;
+	_restoreInteractiveVideoAudioState = false;
+	memset(_savedInteractiveVideoTrackState, 0, sizeof(_savedInteractiveVideoTrackState));
+	memset(_savedInteractiveVideoTrackGroupId, 0, sizeof(_savedInteractiveVideoTrackGroupId));
+	_savedInteractiveVideoTrackCount = 0;
 	_gameCounter = 0;
 	_pathBranchEnabled = false;
 	_rightPathSelected = false;
diff --git a/engines/scumm/insane/rebel1/rebel.h b/engines/scumm/insane/rebel1/rebel.h
index 0b7d2da299b..83e8381390c 100644
--- a/engines/scumm/insane/rebel1/rebel.h
+++ b/engines/scumm/insane/rebel1/rebel.h
@@ -199,6 +199,8 @@ private:
 	// Play interactive gameplay video (with ship physics + HUD)
 	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();
@@ -488,6 +490,10 @@ 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/runlevels.cpp b/engines/scumm/insane/rebel1/runlevels.cpp
index f1d67c8e49b..b1e4303c7f4 100644
--- a/engines/scumm/insane/rebel1/runlevels.cpp
+++ b/engines/scumm/insane/rebel1/runlevels.cpp
@@ -1552,6 +1552,42 @@ void InsaneRebel1::resetInteractiveVideoAudio() {
 	initAudio(sampleRate);
 }
 
+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];
+	}
+}
+
 void InsaneRebel1::setupInteractiveVideoState(int32 startFrame) {
 	const bool level7RouteSplice = (_currentLevel == 6 && _levelRouteIndex > 0);
 	const bool resumingRoute = startFrame > 0;
@@ -1658,6 +1694,7 @@ void InsaneRebel1::releaseInteractiveVideoInput() {
 
 void InsaneRebel1::playInteractiveVideoFile(const char *filename, int32 videoOffset, int32 videoStartFrame) {
 	_vm->_splayer->play(filename, 12, videoOffset, videoStartFrame);
+	restoreInteractiveVideoAudioState();
 	_interactiveVideoActive = false;
 }
 
@@ -1669,8 +1706,10 @@ void InsaneRebel1::playInteractiveVideo(const char *filename, int32 startFrame)
 
 	int32 videoStartFrame = 0;
 	int32 videoOffset = 0;
+	const bool preserveRuntimeState = (startFrame > 0) || (_currentLevel == 6 && _levelRouteIndex > 0);
 
-	resetInteractiveVideoAudio();
+	if (!preserveRuntimeState)
+		resetInteractiveVideoAudio();
 	setupInteractiveVideoState(startFrame);
 	resolveSeek(filename, startFrame, videoOffset, videoStartFrame);
 	captureInteractiveVideoInput();


Commit: 90490c2792c55d85cbda11825683eca383eceecc
    https://github.com/scummvm/scummvm/commit/90490c2792c55d85cbda11825683eca383eceecc
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-01T21:58:55+02:00

Commit Message:
SCUMM: RA1: improve menu navigation using gamepad

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


diff --git a/engines/scumm/insane/rebel1/menu.cpp b/engines/scumm/insane/rebel1/menu.cpp
index 0dbc31baf07..68bbd4aae9d 100644
--- a/engines/scumm/insane/rebel1/menu.cpp
+++ b/engines/scumm/insane/rebel1/menu.cpp
@@ -91,6 +91,10 @@ static const char *getRebel1ActionName(Common::CustomEventType customType) {
 		return "attack";
 	case kScummActionInsaneSwitch:
 		return "switch";
+	case kScummActionInsaneBack:
+		return "back";
+	case kScummActionInsaneSkip:
+		return "skip";
 	default:
 		return "other";
 	}
@@ -171,6 +175,8 @@ static RA1MenuCommand getRebel1MenuCommandFromAction(ScummAction action) {
 		return kRA1MenuCommandRight;
 	case kScummActionInsaneAttack:
 		return kRA1MenuCommandAccept;
+	case kScummActionInsaneSkip:
+		return kRA1MenuCommandCancel;
 	default:
 		return kRA1MenuCommandNone;
 	}
@@ -326,6 +332,10 @@ bool InsaneRebel1::handleTextEntryAction(ScummAction action) {
 	case kScummActionInsaneAttack:
 		selectTextEntryChar();
 		return true;
+	case kScummActionInsaneSwitch:
+	case kScummActionInsaneSkip:
+		finishTextEntry(true);
+		return true;
 	default:
 		return false;
 	}
@@ -482,6 +492,12 @@ bool InsaneRebel1::handleControllerMenuAction(ScummAction action) {
 	if (_textEntryActive)
 		return handleTextEntryAction(action);
 
+	if (action == kScummActionInsaneSwitch || action == kScummActionInsaneSkip) {
+		if (_levelSelectActive || _optionsActive)
+			return handleMenuCommand(kRA1MenuCommandCancel);
+		return true;
+	}
+
 	return handleMenuCommand(getRebel1MenuCommandFromAction(action));
 }
 
@@ -692,7 +708,24 @@ bool InsaneRebel1::notifyEvent(const Common::Event &event) {
 			_activeInputSource = kInputSourceJoystickDigital;
 		}
 
-		if (_highScoresActive && pressed && event.customType == kScummActionInsaneAttack) {
+		if (event.customType == kScummActionInsaneBack) {
+			if (!pressed)
+				return true;
+			if (_player) {
+				const bool wasPaused = _player->_paused;
+				if (!wasPaused)
+					_player->pause();
+				_vm->openMainMenuDialog();
+				if (!wasPaused)
+					_player->unpause();
+			}
+			return true;
+		}
+
+		if (_highScoresActive && pressed &&
+			(event.customType == kScummActionInsaneAttack ||
+			 event.customType == kScummActionInsaneSwitch ||
+			 event.customType == kScummActionInsaneSkip)) {
 			_vm->_smushVideoShouldFinish = true;
 			return true;
 		}
@@ -700,6 +733,16 @@ bool InsaneRebel1::notifyEvent(const Common::Event &event) {
 		if (pressed && handleControllerMenuAction((ScummAction)event.customType))
 			return true;
 
+		if (event.customType == kScummActionInsaneSkip) {
+			if (!pressed)
+				return true;
+			if (!_interactiveVideoActive) {
+				_vm->_smushVideoShouldFinish = true;
+				return true;
+			}
+			return true;
+		}
+
 		if (_interactiveVideoActive && !_menuActive && event.customType == kScummActionInsaneAttack) {
 			_playerFired = pressed;
 			return true;
@@ -732,14 +775,30 @@ bool InsaneRebel1::notifyEvent(const Common::Event &event) {
 		return true;
 	}
 
-	if (event.type == Common::EVENT_MAINMENU && _interactiveVideoActive && !_menuActive) {
+	if (event.type == Common::EVENT_MAINMENU) {
+		if (_menuActive || _highScoresActive)
+			return true;
+
 		const uint32 now = _vm->_system->getMillis();
 		const uint32 elapsedSinceAxis = _lastJoystickAxisEventTime ? now - _lastJoystickAxisEventTime : 0xffffffffu;
-		debugC(DEBUG_INSANE, "RA1 input mainmenu-event: gameplay=1 elapsedSinceAxis=%u storedAxis=(%d,%d)",
-			elapsedSinceAxis, _joystickAxisX, _joystickAxisY);
-		if (elapsedSinceAxis <= kRA1JoystickAxisEscGuardMs) {
-			debugC(DEBUG_INSANE, "RA1 input ignored mainmenu event after recent joystick axis movement (%u ms)", elapsedSinceAxis);
-			return true;
+
+		if (_interactiveVideoActive && !_menuActive) {
+			debugC(DEBUG_INSANE, "RA1 input mainmenu-event: gameplay=1 elapsedSinceAxis=%u storedAxis=(%d,%d)",
+				elapsedSinceAxis, _joystickAxisX, _joystickAxisY);
+			if (elapsedSinceAxis <= kRA1JoystickAxisEscGuardMs) {
+				debugC(DEBUG_INSANE, "RA1 input ignored mainmenu event after recent joystick axis movement (%u ms)", elapsedSinceAxis);
+				return true;
+			}
+
+			if (_player) {
+				const bool wasPaused = _player->_paused;
+				if (!wasPaused)
+					_player->pause();
+				_vm->openMainMenuDialog();
+				if (!wasPaused)
+					_player->unpause();
+				return true;
+			}
 		}
 	}
 
diff --git a/engines/scumm/metaengine.cpp b/engines/scumm/metaengine.cpp
index c43f81ea35d..d0e91a67b89 100644
--- a/engines/scumm/metaengine.cpp
+++ b/engines/scumm/metaengine.cpp
@@ -1019,7 +1019,7 @@ Common::KeymapArray ScummMetaEngine::initKeymaps(const char *target) const {
 	Common::String gameId = ConfMan.get("gameid", target);
 	Action *act;
 
-	if (gameId == "rebel2") {
+	if (gameId == "rebel1" || gameId == "rebel2") {
 		for (uint i = 0; i < keymaps.size(); ++i) {
 			if (keymaps[i]->getId() == "engine-default") {
 				delete keymaps.remove_at(i);
@@ -1141,24 +1141,28 @@ Common::KeymapArray ScummMetaEngine::initKeymaps(const char *target) const {
 		act->setCustomBackendActionAxisEvent(kScummBackendActionRebel1AxisUp);
 		act->addDefaultInputMapping("JOY_LEFT_STICK_Y-");
 		act->addDefaultInputMapping("JOY_RIGHT_STICK_Y-");
+		act->addDefaultInputMapping("JOY_HAT_Y-");
 		rebel1Keymap->addAction(act);
 
 		act = new Action("RA1STICKDOWN", _("Stick down"));
 		act->setCustomBackendActionAxisEvent(kScummBackendActionRebel1AxisDown);
 		act->addDefaultInputMapping("JOY_LEFT_STICK_Y+");
 		act->addDefaultInputMapping("JOY_RIGHT_STICK_Y+");
+		act->addDefaultInputMapping("JOY_HAT_Y+");
 		rebel1Keymap->addAction(act);
 
 		act = new Action("RA1STICKLEFT", _("Stick left"));
 		act->setCustomBackendActionAxisEvent(kScummBackendActionRebel1AxisLeft);
 		act->addDefaultInputMapping("JOY_LEFT_STICK_X-");
 		act->addDefaultInputMapping("JOY_RIGHT_STICK_X-");
+		act->addDefaultInputMapping("JOY_HAT_X-");
 		rebel1Keymap->addAction(act);
 
 		act = new Action("RA1STICKRIGHT", _("Stick right"));
 		act->setCustomBackendActionAxisEvent(kScummBackendActionRebel1AxisRight);
 		act->addDefaultInputMapping("JOY_LEFT_STICK_X+");
 		act->addDefaultInputMapping("JOY_RIGHT_STICK_X+");
+		act->addDefaultInputMapping("JOY_HAT_X+");
 		rebel1Keymap->addAction(act);
 
 		act = new Action("RA1FIRE", _("Fire / select"));
@@ -1166,11 +1170,21 @@ Common::KeymapArray ScummMetaEngine::initKeymaps(const char *target) const {
 		act->addDefaultInputMapping("JOY_A");
 		rebel1Keymap->addAction(act);
 
-		act = new Action("RA1BACK", _("Back / skip"));
-		act->setKeyEvent(KeyState(KEYCODE_ESCAPE, ASCII_ESCAPE));
+		act = new Action("RA1CANCEL", _("Menu back"));
+		act->setCustomEngineActionEvent(kScummActionInsaneSwitch);
+		act->addDefaultInputMapping("JOY_X");
+		rebel1Keymap->addAction(act);
+
+		act = new Action("RA1SKIP", _("Skip / menu back"));
+		act->setCustomEngineActionEvent(kScummActionInsaneSkip);
 		act->addDefaultInputMapping("JOY_B");
-		act->addDefaultInputMapping("JOY_Y");
+		rebel1Keymap->addAction(act);
+
+		act = new Action("RA1BACK", _("Menu"));
+		act->setCustomEngineActionEvent(kScummActionInsaneBack);
+		act->addDefaultInputMapping("ESCAPE");
 		act->addDefaultInputMapping("JOY_START");
+		act->addDefaultInputMapping("AC_BACK");
 		rebel1Keymap->addAction(act);
 
 		keymaps.push_back(rebel1Keymap);



From noreply at scummvm.org  Mon Jun  1 19:59:14 2026
From: noreply at scummvm.org (ScummVM-Translations)
Date: Mon,  1 Jun 2026 19:59:14 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 9bb4d8a01d77c3af8019157d9611e67b30c0dd40
Message-ID: <20260601195914.5EA9F7005C2@ron.scummvm.net>

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

Summary:
9bb4d8a01d I18N: Update translations templates


Commit: 9bb4d8a01d77c3af8019157d9611e67b30c0dd40
    https://github.com/scummvm/scummvm/commit/9bb4d8a01d77c3af8019157d9611e67b30c0dd40
Author: Weblate (noreply at weblate.org)
Date: 2026-06-01T19:59:09Z

Commit Message:
I18N: Update translations templates

Changed paths:
    po/scummvm.pot


diff --git a/po/scummvm.pot b/po/scummvm.pot
index 27bbdefa188..215bae7510f 100644
--- a/po/scummvm.pot
+++ b/po/scummvm.pot
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2026.2.1git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 16:27+0000\n"
+"POT-Creation-Date: 2026-06-01 19:59+0000\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL at ADDRESS>\n"
 "Language-Team: LANGUAGE <LL at li.org>\n"
@@ -5435,7 +5435,7 @@ msgstr ""
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1183 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr ""
 
@@ -12422,59 +12422,63 @@ msgstr ""
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1182
+#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1196
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1187
+#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1201
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1192
+#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1206
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1197
+#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1211
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
+#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1216
 msgid "Stick up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1209
+#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
 msgid "Stick down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1216
+#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1230
 msgid "Stick left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1158 engines/scumm/metaengine.cpp:1223
+#: engines/scumm/metaengine.cpp:1161 engines/scumm/metaengine.cpp:1237
 msgid "Stick right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1230
+#: engines/scumm/metaengine.cpp:1168 engines/scumm/metaengine.cpp:1244
 msgid "Fire / select"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1169
-msgid "Back / skip"
+#: engines/scumm/metaengine.cpp:1173
+msgid "Menu back"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1180
+#: engines/scumm/metaengine.cpp:1178
+msgid "Skip / menu back"
+msgstr ""
+
+#: engines/scumm/metaengine.cpp:1194
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1235
+#: engines/scumm/metaengine.cpp:1249
 msgid "Cover / back"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1241
+#: engines/scumm/metaengine.cpp:1255
 msgid "Skip / back"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1248
+#: engines/scumm/metaengine.cpp:1262
 msgid "Skip / menu"
 msgstr ""
 



From noreply at scummvm.org  Mon Jun  1 19:59:46 2026
From: noreply at scummvm.org (ScummVM-Translations)
Date: Mon,  1 Jun 2026 19:59:46 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 374d6ca71eee98f88e9236e043ea1d2419bed7da
Message-ID: <20260601195946.E980C700591@ron.scummvm.net>

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

Summary:
374d6ca71e I18N: Update translation files


Commit: 374d6ca71eee98f88e9236e043ea1d2419bed7da
    https://github.com/scummvm/scummvm/commit/374d6ca71eee98f88e9236e043ea1d2419bed7da
Author: Weblate (noreply at weblate.org)
Date: 2026-06-01T19:59:27Z

Commit Message:
I18N: Update translation files

Updated by "Update PO files to match POT (msgmerge)" hook in Weblate.

Translation: ScummVM/scummvm
Translate-URL: https://translations.scummvm.org/projects/scummvm/scummvm/

Changed paths:
    po/ar.po
    po/be-tarask.po
    po/be_BY.po
    po/ca_ES.po
    po/cs_CZ.po
    po/da.po
    po/de_DE.po
    po/el.po
    po/es_ES.po
    po/eu.po
    po/fi_FI.po
    po/fr_FR.po
    po/gl_ES.po
    po/he.po
    po/hi.po
    po/hu_HU.po
    po/it_IT.po
    po/ja.po
    po/ka.po
    po/ko.po
    po/nb_NO.po
    po/nl_NL.po
    po/nn_NO.po
    po/pl_PL.po
    po/pt_BR.po
    po/pt_PT.po
    po/ro.po
    po/ru_RU.po
    po/sv_SE.po
    po/tr.po
    po/uk_UA.po
    po/zh.po
    po/zh_Hans.po
    po/zh_Hant.po


diff --git a/po/ar.po b/po/ar.po
index ff95d404548..a589349aec0 100644
--- a/po/ar.po
+++ b/po/ar.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 16:27+0000\n"
+"POT-Creation-Date: 2026-06-01 19:59+0000\n"
 "PO-Revision-Date: 2026-02-24 22:26+0000\n"
 "Last-Translator: Mohamed Shaaban <mohmedbashamedo at hotmail.com>\n"
 "Language-Team: Arabic <https://translations.scummvm.org/projects/scummvm/"
@@ -5935,7 +5935,7 @@ msgstr "R2"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1183 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "??????????????"
 
@@ -14211,75 +14211,81 @@ msgstr "?????? ???????? ???? ?????????? ??????????????"
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1182
+#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1196
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1187
+#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1201
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1192
+#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1206
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1197
+#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1211
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
+#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1216
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "??????????"
 
-#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1209
+#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "?????? ???????????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1216
+#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1230
 #, fuzzy
 #| msgid "Slide Left"
 msgid "Stick left"
 msgstr "?????????????? ?????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1158 engines/scumm/metaengine.cpp:1223
+#: engines/scumm/metaengine.cpp:1161 engines/scumm/metaengine.cpp:1237
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick right"
 msgstr "???????????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1230
+#: engines/scumm/metaengine.cpp:1168 engines/scumm/metaengine.cpp:1244
 #, fuzzy
 #| msgid "Move left"
 msgid "Fire / select"
 msgstr "???????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1169
+#: engines/scumm/metaengine.cpp:1173
 #, fuzzy
-#| msgid "Backend"
-msgid "Back / skip"
-msgstr "??????????????"
+#| msgid "Move Back"
+msgid "Menu back"
+msgstr "???????? ????????????"
+
+#: engines/scumm/metaengine.cpp:1178
+#, fuzzy
+#| msgid "Open main menu"
+msgid "Skip / menu back"
+msgstr "???????? ?????????????? ????????????????"
 
-#: engines/scumm/metaengine.cpp:1180
+#: engines/scumm/metaengine.cpp:1194
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1235
+#: engines/scumm/metaengine.cpp:1249
 #, fuzzy
 #| msgid "Move Back"
 msgid "Cover / back"
 msgstr "???????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1241
+#: engines/scumm/metaengine.cpp:1255
 #, fuzzy
 #| msgid "Skip dialogue"
 msgid "Skip / back"
 msgstr "???????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1248
+#: engines/scumm/metaengine.cpp:1262
 #, fuzzy
 #| msgid "Open main menu"
 msgid "Skip / menu"
@@ -18382,6 +18388,11 @@ msgid ""
 "supported. Continue launching game?"
 msgstr ""
 
+#, fuzzy
+#~| msgid "Backend"
+#~ msgid "Back / skip"
+#~ msgstr "??????????????"
+
 #, fuzzy
 #~| msgid "No"
 #~ msgid "N"
diff --git a/po/be-tarask.po b/po/be-tarask.po
index c6fbc78df20..b31f3712274 100644
--- a/po/be-tarask.po
+++ b/po/be-tarask.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 16:27+0000\n"
+"POT-Creation-Date: 2026-06-01 19:59+0000\n"
 "PO-Revision-Date: 2023-02-26 15:01+0000\n"
 "Last-Translator: Kastu?? K. <oakum2334 at gmail.com>\n"
 "Language-Team: Belarusian (Tara??kievica) <https://translations.scummvm.org/"
@@ -5868,7 +5868,7 @@ msgstr "R2"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1183 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "????????"
 
@@ -14138,75 +14138,81 @@ msgstr ""
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1182
+#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1196
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1187
+#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1201
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1192
+#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1206
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1197
+#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1211
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
+#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1216
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "??????????????"
 
-#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1209
+#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "?????????????????????????? ????????"
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1216
+#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1230
 #, fuzzy
 #| msgid "Slide Left"
 msgid "Stick left"
 msgstr "?????????????????????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1158 engines/scumm/metaengine.cpp:1223
+#: engines/scumm/metaengine.cpp:1161 engines/scumm/metaengine.cpp:1237
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick right"
 msgstr "?????????????????????? ??????????????"
 
-#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1230
+#: engines/scumm/metaengine.cpp:1168 engines/scumm/metaengine.cpp:1244
 #, fuzzy
 #| msgid "Move left"
 msgid "Fire / select"
 msgstr "?????????????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1169
+#: engines/scumm/metaengine.cpp:1173
 #, fuzzy
-#| msgid "Backend"
-msgid "Back / skip"
-msgstr "????????????"
+#| msgid "Move Back"
+msgid "Menu back"
+msgstr "?????? ??????????"
+
+#: engines/scumm/metaengine.cpp:1178
+#, fuzzy
+#| msgid "Open main menu"
+msgid "Skip / menu back"
+msgstr "?????????????? ???????????????? ????????"
 
-#: engines/scumm/metaengine.cpp:1180
+#: engines/scumm/metaengine.cpp:1194
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1235
+#: engines/scumm/metaengine.cpp:1249
 #, fuzzy
 #| msgid "Move Back"
 msgid "Cover / back"
 msgstr "?????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1241
+#: engines/scumm/metaengine.cpp:1255
 #, fuzzy
 #| msgid "Skip dialogue"
 msgid "Skip / back"
 msgstr "?????????????????????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1248
+#: engines/scumm/metaengine.cpp:1262
 #, fuzzy
 #| msgid "Open main menu"
 msgid "Skip / menu"
@@ -18297,6 +18303,11 @@ msgid ""
 "supported. Continue launching game?"
 msgstr ""
 
+#, fuzzy
+#~| msgid "Backend"
+#~ msgid "Back / skip"
+#~ msgstr "????????????"
+
 #, fuzzy
 #~| msgid "No"
 #~ msgid "N"
diff --git a/po/be_BY.po b/po/be_BY.po
index 990799fbf2b..350b1463732 100644
--- a/po/be_BY.po
+++ b/po/be_BY.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 16:27+0000\n"
+"POT-Creation-Date: 2026-06-01 19:59+0000\n"
 "PO-Revision-Date: 2021-03-21 08:24+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Belarusian <https://translations.scummvm.org/projects/scummvm/"
@@ -5877,7 +5877,7 @@ msgstr "R2"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1183 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "????????"
 
@@ -13948,74 +13948,80 @@ msgstr ""
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1182
+#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1196
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1187
+#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1201
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1192
+#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1206
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1197
+#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1211
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
+#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1216
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "??????????????"
 
-#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1209
+#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "???????????????????? ????????"
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1216
+#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1230
 #, fuzzy
 #| msgid "Slide Left"
 msgid "Stick left"
 msgstr "???????????????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1158 engines/scumm/metaengine.cpp:1223
+#: engines/scumm/metaengine.cpp:1161 engines/scumm/metaengine.cpp:1237
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick right"
 msgstr "???????????????? ??????????????"
 
-#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1230
+#: engines/scumm/metaengine.cpp:1168 engines/scumm/metaengine.cpp:1244
 #, fuzzy
 #| msgid "Move left"
 msgid "Fire / select"
 msgstr "?????????????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1169
+#: engines/scumm/metaengine.cpp:1173
 #, fuzzy
-#| msgid "Joy Back"
-msgid "Back / skip"
-msgstr "???????????????? ??????????"
+#| msgid "Move Back"
+msgid "Menu back"
+msgstr "???????? ??????????"
+
+#: engines/scumm/metaengine.cpp:1178
+#, fuzzy
+#| msgid "Open main menu"
+msgid "Skip / menu back"
+msgstr "?????????????? ???????????????? ????????"
 
-#: engines/scumm/metaengine.cpp:1180
+#: engines/scumm/metaengine.cpp:1194
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1235
+#: engines/scumm/metaengine.cpp:1249
 #, fuzzy
 #| msgid "Move Back"
 msgid "Cover / back"
 msgstr "???????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1241
+#: engines/scumm/metaengine.cpp:1255
 #, fuzzy
 msgid "Skip / back"
 msgstr "???????????????????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1248
+#: engines/scumm/metaengine.cpp:1262
 #, fuzzy
 #| msgid "Open main menu"
 msgid "Skip / menu"
@@ -18008,6 +18014,11 @@ msgid ""
 "supported. Continue launching game?"
 msgstr ""
 
+#, fuzzy
+#~| msgid "Joy Back"
+#~ msgid "Back / skip"
+#~ msgstr "???????????????? ??????????"
+
 #, fuzzy
 #~| msgid "No"
 #~ msgid "N"
diff --git a/po/ca_ES.po b/po/ca_ES.po
index afc2bd754e8..332122576aa 100644
--- a/po/ca_ES.po
+++ b/po/ca_ES.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.6.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 16:27+0000\n"
+"POT-Creation-Date: 2026-06-01 19:59+0000\n"
 "PO-Revision-Date: 2026-05-26 04:22+0000\n"
 "Last-Translator: Xaviu <xaviu at xaviu.com>\n"
 "Language-Team: Catalan <https://translations.scummvm.org/projects/scummvm/"
@@ -5679,7 +5679,7 @@ msgstr "R2"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1183 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "Men??"
 
@@ -13015,75 +13015,81 @@ msgstr ""
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1182
+#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1196
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1187
+#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1201
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1192
+#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1206
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1197
+#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1211
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
+#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1216
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "Recull"
 
-#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1209
+#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "Despla??ament avall"
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1216
+#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1230
 #, fuzzy
 #| msgid "Slide left"
 msgid "Stick left"
 msgstr "Llisca cap a l'esquerra"
 
-#: engines/scumm/metaengine.cpp:1158 engines/scumm/metaengine.cpp:1223
+#: engines/scumm/metaengine.cpp:1161 engines/scumm/metaengine.cpp:1237
 #, fuzzy
 #| msgid "Slide right"
 msgid "Stick right"
 msgstr "Llisca cap a la dreta"
 
-#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1230
+#: engines/scumm/metaengine.cpp:1168 engines/scumm/metaengine.cpp:1244
 #, fuzzy
 #| msgid "Move / Select"
 msgid "Fire / select"
 msgstr "Mou / Selecciona"
 
-#: engines/scumm/metaengine.cpp:1169
+#: engines/scumm/metaengine.cpp:1173
 #, fuzzy
-#| msgid "Backspace"
-msgid "Back / skip"
-msgstr "Retroc??s"
+#| msgid "Move back"
+msgid "Menu back"
+msgstr "Mou enrere"
 
-#: engines/scumm/metaengine.cpp:1180
+#: engines/scumm/metaengine.cpp:1178
+#, fuzzy
+#| msgid "Skip main menu"
+msgid "Skip / menu back"
+msgstr "Omet el men?? principal"
+
+#: engines/scumm/metaengine.cpp:1194
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1235
+#: engines/scumm/metaengine.cpp:1249
 #, fuzzy
 #| msgid "Move back"
 msgid "Cover / back"
 msgstr "Mou enrere"
 
-#: engines/scumm/metaengine.cpp:1241
+#: engines/scumm/metaengine.cpp:1255
 #, fuzzy
 #| msgid "Skip walk"
 msgid "Skip / back"
 msgstr "Omet el despla??ament"
 
-#: engines/scumm/metaengine.cpp:1248
+#: engines/scumm/metaengine.cpp:1262
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu"
@@ -16546,6 +16552,11 @@ msgid ""
 "supported. Continue launching game?"
 msgstr ""
 
+#, fuzzy
+#~| msgid "Backspace"
+#~ msgid "Back / skip"
+#~ msgstr "Retroc??s"
+
 #, fuzzy
 #~| msgid "No"
 #~ msgid "N"
diff --git a/po/cs_CZ.po b/po/cs_CZ.po
index 5078ad7843f..a891995e094 100644
--- a/po/cs_CZ.po
+++ b/po/cs_CZ.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.7.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 16:27+0000\n"
+"POT-Creation-Date: 2026-06-01 19:59+0000\n"
 "PO-Revision-Date: 2026-02-09 20:22+0000\n"
 "Last-Translator: AsciiWolf <mail at asciiwolf.com>\n"
 "Language-Team: Czech <https://translations.scummvm.org/projects/scummvm/"
@@ -5845,7 +5845,7 @@ msgstr ""
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1183 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "Menu"
 
@@ -13758,71 +13758,77 @@ msgstr ""
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1182
+#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1196
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1187
+#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1201
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1192
+#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1206
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1197
+#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1211
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
+#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1216
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "Sebrat"
 
-#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1209
+#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
 #, fuzzy
 msgid "Stick down"
 msgstr "Posunout seznam dolu"
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1216
+#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1230
 #, fuzzy
 #| msgid "Slide Left"
 msgid "Stick left"
 msgstr "P??esunout se Doleva"
 
-#: engines/scumm/metaengine.cpp:1158 engines/scumm/metaengine.cpp:1223
+#: engines/scumm/metaengine.cpp:1161 engines/scumm/metaengine.cpp:1237
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick right"
 msgstr "P??esunout se Doprava"
 
-#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1230
+#: engines/scumm/metaengine.cpp:1168 engines/scumm/metaengine.cpp:1244
 #, fuzzy
 msgid "Fire / select"
 msgstr "P??esunout se doleva"
 
-#: engines/scumm/metaengine.cpp:1169
+#: engines/scumm/metaengine.cpp:1173
 #, fuzzy
-msgid "Back / skip"
+#| msgid "Move Back"
+msgid "Menu back"
 msgstr "Vzad"
 
-#: engines/scumm/metaengine.cpp:1180
+#: engines/scumm/metaengine.cpp:1178
+#, fuzzy
+msgid "Skip / menu back"
+msgstr "~N~??vrat do Spou??t????e"
+
+#: engines/scumm/metaengine.cpp:1194
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1235
+#: engines/scumm/metaengine.cpp:1249
 #, fuzzy
 #| msgid "Move Back"
 msgid "Cover / back"
 msgstr "Vzad"
 
-#: engines/scumm/metaengine.cpp:1241
+#: engines/scumm/metaengine.cpp:1255
 #, fuzzy
 msgid "Skip / back"
 msgstr "P??esko??it ????dek"
 
-#: engines/scumm/metaengine.cpp:1248
+#: engines/scumm/metaengine.cpp:1262
 #, fuzzy
 msgid "Skip / menu"
 msgstr "~N~??vrat do Spou??t????e"
@@ -17759,6 +17765,10 @@ msgid ""
 "supported. Continue launching game?"
 msgstr ""
 
+#, fuzzy
+#~ msgid "Back / skip"
+#~ msgstr "Vzad"
+
 #, fuzzy
 #~| msgid "No"
 #~ msgid "N"
diff --git a/po/da.po b/po/da.po
index 4b3ebfe9c92..a83bc0b942a 100644
--- a/po/da.po
+++ b/po/da.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 16:27+0000\n"
+"POT-Creation-Date: 2026-06-01 19:59+0000\n"
 "PO-Revision-Date: 2023-09-07 17:04+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Danish <https://translations.scummvm.org/projects/scummvm/"
@@ -5864,7 +5864,7 @@ msgstr ""
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1183 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "Menu"
 
@@ -13849,72 +13849,77 @@ msgstr ""
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1182
+#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1196
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1187
+#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1201
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1192
+#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1206
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1197
+#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1211
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
+#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1216
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "Tag op"
 
-#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1209
+#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
 #, fuzzy
 msgid "Stick down"
 msgstr "Rul liste ned"
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1216
+#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1230
 #, fuzzy
 #| msgid "Slide Left"
 msgid "Stick left"
 msgstr "Flyt til venstre"
 
-#: engines/scumm/metaengine.cpp:1158 engines/scumm/metaengine.cpp:1223
+#: engines/scumm/metaengine.cpp:1161 engines/scumm/metaengine.cpp:1237
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick right"
 msgstr "Flyt til h??jre"
 
-#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1230
+#: engines/scumm/metaengine.cpp:1168 engines/scumm/metaengine.cpp:1244
 #, fuzzy
 msgid "Fire / select"
 msgstr "Flyt til venstre"
 
-#: engines/scumm/metaengine.cpp:1169
+#: engines/scumm/metaengine.cpp:1173
 #, fuzzy
-#| msgid "Backend"
-msgid "Back / skip"
-msgstr "Backend"
+#| msgid "Move Back"
+msgid "Menu back"
+msgstr "Flyt bagud"
 
-#: engines/scumm/metaengine.cpp:1180
+#: engines/scumm/metaengine.cpp:1178
+#, fuzzy
+msgid "Skip / menu back"
+msgstr "~R~etur til oversigt"
+
+#: engines/scumm/metaengine.cpp:1194
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1235
+#: engines/scumm/metaengine.cpp:1249
 #, fuzzy
 #| msgid "Move Back"
 msgid "Cover / back"
 msgstr "Flyt bagud"
 
-#: engines/scumm/metaengine.cpp:1241
+#: engines/scumm/metaengine.cpp:1255
 #, fuzzy
 msgid "Skip / back"
 msgstr "Spring linje over"
 
-#: engines/scumm/metaengine.cpp:1248
+#: engines/scumm/metaengine.cpp:1262
 #, fuzzy
 msgid "Skip / menu"
 msgstr "~R~etur til oversigt"
@@ -17883,6 +17888,11 @@ msgid ""
 "supported. Continue launching game?"
 msgstr ""
 
+#, fuzzy
+#~| msgid "Backend"
+#~ msgid "Back / skip"
+#~ msgstr "Backend"
+
 #, fuzzy
 #~| msgid "No"
 #~ msgid "N"
diff --git a/po/de_DE.po b/po/de_DE.po
index 69df9f05da7..94854784316 100644
--- a/po/de_DE.po
+++ b/po/de_DE.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.10.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 16:27+0000\n"
+"POT-Creation-Date: 2026-06-01 19:59+0000\n"
 "PO-Revision-Date: 2026-05-05 16:31+0000\n"
 "Last-Translator: Lothar Serra Mari <lothar.serramari at scummvm.org>\n"
 "Language-Team: German <https://translations.scummvm.org/projects/scummvm/"
@@ -6144,7 +6144,7 @@ msgstr "R2"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1183 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "Men??"
 
@@ -13746,75 +13746,81 @@ msgstr "Cheat: Bike-Kampf gewinnen"
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1182
+#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1196
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1187
+#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1201
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1192
+#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1206
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1197
+#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1211
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
+#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1216
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "Nimm"
 
-#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1209
+#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "Nach unten scrollen"
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1216
+#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1230
 #, fuzzy
 #| msgid "Slide left"
 msgid "Stick left"
 msgstr "Slide nach links"
 
-#: engines/scumm/metaengine.cpp:1158 engines/scumm/metaengine.cpp:1223
+#: engines/scumm/metaengine.cpp:1161 engines/scumm/metaengine.cpp:1237
 #, fuzzy
 #| msgid "Slide right"
 msgid "Stick right"
 msgstr "Slide nach rechts"
 
-#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1230
+#: engines/scumm/metaengine.cpp:1168 engines/scumm/metaengine.cpp:1244
 #, fuzzy
 #| msgid "Move / Select"
 msgid "Fire / select"
 msgstr "Bewegen / Ausw??hlen"
 
-#: engines/scumm/metaengine.cpp:1169
+#: engines/scumm/metaengine.cpp:1173
 #, fuzzy
-#| msgid "Backspace"
-msgid "Back / skip"
-msgstr "R??cktaste"
+#| msgid "Move back"
+msgid "Menu back"
+msgstr "Zur??ckgehen"
 
-#: engines/scumm/metaengine.cpp:1180
+#: engines/scumm/metaengine.cpp:1178
+#, fuzzy
+#| msgid "Skip main menu"
+msgid "Skip / menu back"
+msgstr "Hauptmen?? ??berspringen"
+
+#: engines/scumm/metaengine.cpp:1194
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1235
+#: engines/scumm/metaengine.cpp:1249
 #, fuzzy
 #| msgid "Move back"
 msgid "Cover / back"
 msgstr "Zur??ckgehen"
 
-#: engines/scumm/metaengine.cpp:1241
+#: engines/scumm/metaengine.cpp:1255
 #, fuzzy
 #| msgid "Skip walk"
 msgid "Skip / back"
 msgstr "Spaziergang ??berspringen"
 
-#: engines/scumm/metaengine.cpp:1248
+#: engines/scumm/metaengine.cpp:1262
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu"
@@ -17410,6 +17416,11 @@ msgstr ""
 "Untertiteln gel??st werden, sofern diese unterst??tzt werden. M??chten Sie das "
 "Spiel trotzdem starten?"
 
+#, fuzzy
+#~| msgid "Backspace"
+#~ msgid "Back / skip"
+#~ msgstr "R??cktaste"
+
 #, fuzzy
 #~| msgid "No"
 #~ msgid "N"
diff --git a/po/el.po b/po/el.po
index 675fc343881..56be69d69f8 100644
--- a/po/el.po
+++ b/po/el.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.10.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 16:27+0000\n"
+"POT-Creation-Date: 2026-06-01 19:59+0000\n"
 "PO-Revision-Date: 2026-04-23 00:22+0000\n"
 "Last-Translator: Antoniou Athanasios <a.antoniou79 at gmail.com>\n"
 "Language-Team: Greek <https://translations.scummvm.org/projects/scummvm/"
@@ -6268,7 +6268,7 @@ msgstr "R2"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1183 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "??????????"
 
@@ -13901,75 +13901,81 @@ msgstr "?????????? ?????? ???????? ?????? ???????? ???? ?????? ??????????????"
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1182
+#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1196
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1187
+#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1201
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1192
+#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1206
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1197
+#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1211
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
+#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1216
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "????????????"
 
-#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1209
+#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "???????????? ????????"
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1216
+#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1230
 #, fuzzy
 #| msgid "Slide left"
 msgid "Stick left"
 msgstr "???????????????? ????????????????"
 
-#: engines/scumm/metaengine.cpp:1158 engines/scumm/metaengine.cpp:1223
+#: engines/scumm/metaengine.cpp:1161 engines/scumm/metaengine.cpp:1237
 #, fuzzy
 #| msgid "Slide right"
 msgid "Stick right"
 msgstr "???????????????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1230
+#: engines/scumm/metaengine.cpp:1168 engines/scumm/metaengine.cpp:1244
 #, fuzzy
 #| msgid "Move / Select"
 msgid "Fire / select"
 msgstr "???????????? / ??????????????"
 
-#: engines/scumm/metaengine.cpp:1169
+#: engines/scumm/metaengine.cpp:1173
 #, fuzzy
-#| msgid "Backspace"
-msgid "Back / skip"
-msgstr "Backspace"
+#| msgid "Move Back"
+msgid "Menu back"
+msgstr "?????????????????? ????????"
 
-#: engines/scumm/metaengine.cpp:1180
+#: engines/scumm/metaengine.cpp:1178
+#, fuzzy
+#| msgid "Skip main menu"
+msgid "Skip / menu back"
+msgstr "?????????????????? ?????????????????? ??????????"
+
+#: engines/scumm/metaengine.cpp:1194
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1235
+#: engines/scumm/metaengine.cpp:1249
 #, fuzzy
 #| msgid "Move Back"
 msgid "Cover / back"
 msgstr "?????????????????? ????????"
 
-#: engines/scumm/metaengine.cpp:1241
+#: engines/scumm/metaengine.cpp:1255
 #, fuzzy
 #| msgid "Skip walk"
 msgid "Skip / back"
 msgstr "?????????????????? ??????????????????"
 
-#: engines/scumm/metaengine.cpp:1248
+#: engines/scumm/metaengine.cpp:1262
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu"
@@ -17698,6 +17704,11 @@ msgstr ""
 "???? ?????????????????? ???? ?????????? ?????????????????? , ???? ????????????????????????????. ???? ???????????????????? ?????? "
 "???????????????? ?????? ????????????????????;"
 
+#, fuzzy
+#~| msgid "Backspace"
+#~ msgid "Back / skip"
+#~ msgstr "Backspace"
+
 #, fuzzy
 #~| msgid "No"
 #~ msgid "N"
diff --git a/po/es_ES.po b/po/es_ES.po
index 2b1a9155039..14e22562e6e 100644
--- a/po/es_ES.po
+++ b/po/es_ES.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.4.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 16:27+0000\n"
+"POT-Creation-Date: 2026-06-01 19:59+0000\n"
 "PO-Revision-Date: 2026-06-01 09:35+0000\n"
 "Last-Translator: IlDucci <pajaroloco_2 at hotmail.com>\n"
 "Language-Team: Spanish <https://translations.scummvm.org/projects/scummvm/"
@@ -6166,7 +6166,7 @@ msgstr "R2"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1183 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "Men??"
 
@@ -13719,61 +13719,69 @@ msgstr "Truco para ganar pelea de motos"
 msgid "Rebel Assault controls"
 msgstr "Controles de Rebel Assault"
 
-#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1182
+#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1196
 msgid "Aim up / menu up"
 msgstr "Apuntar arriba/Subir men??"
 
-#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1187
+#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1201
 msgid "Aim down / menu down"
 msgstr "Apuntar abajo/Bajar men??"
 
-#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1192
+#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1206
 msgid "Aim left / menu left"
 msgstr "Apuntar a izda./Men?? a izda."
 
-#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1197
+#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1211
 msgid "Aim right / menu right"
 msgstr "Apuntar a dcha./Men?? a dcha."
 
-#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
+#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1216
 msgid "Stick up"
 msgstr "Stick hacia arriba"
 
-#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1209
+#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
 msgid "Stick down"
 msgstr "Stick hacia abajo"
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1216
+#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1230
 msgid "Stick left"
 msgstr "Stick hacia la izquierda"
 
-#: engines/scumm/metaengine.cpp:1158 engines/scumm/metaengine.cpp:1223
+#: engines/scumm/metaengine.cpp:1161 engines/scumm/metaengine.cpp:1237
 msgid "Stick right"
 msgstr "Stick hacia la derecha"
 
-#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1230
+#: engines/scumm/metaengine.cpp:1168 engines/scumm/metaengine.cpp:1244
 msgid "Fire / select"
 msgstr "Disparar/Seleccionar"
 
-#: engines/scumm/metaengine.cpp:1169
-msgid "Back / skip"
-msgstr "Volver/Omitir"
+#: engines/scumm/metaengine.cpp:1173
+#, fuzzy
+#| msgid "Move back"
+msgid "Menu back"
+msgstr "Retroceder"
+
+#: engines/scumm/metaengine.cpp:1178
+#, fuzzy
+#| msgid "Skip / menu"
+msgid "Skip / menu back"
+msgstr "Omitir/Men??"
 
-#: engines/scumm/metaengine.cpp:1180
+#: engines/scumm/metaengine.cpp:1194
 msgid "Rebel Assault II controls"
 msgstr "Controles de Rebel Assault II"
 
-#: engines/scumm/metaengine.cpp:1235
+#: engines/scumm/metaengine.cpp:1249
 msgid "Cover / back"
 msgstr "Cubrirse/Volver"
 
-#: engines/scumm/metaengine.cpp:1241
+#: engines/scumm/metaengine.cpp:1255
 #, fuzzy
 #| msgid "Skip walk"
 msgid "Skip / back"
 msgstr "Omitir desplazamiento"
 
-#: engines/scumm/metaengine.cpp:1248
+#: engines/scumm/metaengine.cpp:1262
 msgid "Skip / menu"
 msgstr "Omitir/Men??"
 
@@ -17348,6 +17356,9 @@ msgstr ""
 "se podr??an resolver estos acertijos con subt??tulos, si est??n disponibles. "
 "??Quieres seguir ejecutando el juego?"
 
+#~ msgid "Back / skip"
+#~ msgstr "Volver/Omitir"
+
 #~ msgid "N"
 #~ msgstr "N"
 
diff --git a/po/eu.po b/po/eu.po
index bd87d7eae86..f31251c114d 100644
--- a/po/eu.po
+++ b/po/eu.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.5.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 16:27+0000\n"
+"POT-Creation-Date: 2026-06-01 19:59+0000\n"
 "PO-Revision-Date: 2024-02-05 18:42+0000\n"
 "Last-Translator: Josu Igoa <josuigoa at ni.eus>\n"
 "Language-Team: Basque <https://translations.scummvm.org/projects/scummvm/"
@@ -5919,7 +5919,7 @@ msgstr ""
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1183 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "Menua"
 
@@ -13816,72 +13816,77 @@ msgstr ""
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1182
+#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1196
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1187
+#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1201
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1192
+#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1206
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1197
+#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1211
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
+#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1216
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "Jaso"
 
-#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1209
+#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
 #, fuzzy
 msgid "Stick down"
 msgstr "Behera"
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1216
+#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1230
 #, fuzzy
 #| msgid "Slide Left"
 msgid "Stick left"
 msgstr "Ezkerrera irristatu"
 
-#: engines/scumm/metaengine.cpp:1158 engines/scumm/metaengine.cpp:1223
+#: engines/scumm/metaengine.cpp:1161 engines/scumm/metaengine.cpp:1237
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick right"
 msgstr "Eskuinera irristatu"
 
-#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1230
+#: engines/scumm/metaengine.cpp:1168 engines/scumm/metaengine.cpp:1244
 #, fuzzy
 msgid "Fire / select"
 msgstr "Ezkerrera irristatu"
 
-#: engines/scumm/metaengine.cpp:1169
+#: engines/scumm/metaengine.cpp:1173
 #, fuzzy
-#| msgid "Backspace"
-msgid "Back / skip"
-msgstr "Ezabatu"
+#| msgid "Move Back"
+msgid "Menu back"
+msgstr "Atzera mugitu"
 
-#: engines/scumm/metaengine.cpp:1180
+#: engines/scumm/metaengine.cpp:1178
+#, fuzzy
+msgid "Skip / menu back"
+msgstr "It~z~uli abiarazlera"
+
+#: engines/scumm/metaengine.cpp:1194
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1235
+#: engines/scumm/metaengine.cpp:1249
 #, fuzzy
 #| msgid "Move Back"
 msgid "Cover / back"
 msgstr "Atzera mugitu"
 
-#: engines/scumm/metaengine.cpp:1241
+#: engines/scumm/metaengine.cpp:1255
 #, fuzzy
 msgid "Skip / back"
 msgstr "Lerroa saltatu"
 
-#: engines/scumm/metaengine.cpp:1248
+#: engines/scumm/metaengine.cpp:1262
 #, fuzzy
 msgid "Skip / menu"
 msgstr "It~z~uli abiarazlera"
@@ -17855,6 +17860,11 @@ msgid ""
 "supported. Continue launching game?"
 msgstr ""
 
+#, fuzzy
+#~| msgid "Backspace"
+#~ msgid "Back / skip"
+#~ msgstr "Ezabatu"
+
 #, fuzzy
 #~| msgid "No"
 #~ msgid "N"
diff --git a/po/fi_FI.po b/po/fi_FI.po
index a3b3b5b5e4b..89b868c2071 100644
--- a/po/fi_FI.po
+++ b/po/fi_FI.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.6.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 16:27+0000\n"
+"POT-Creation-Date: 2026-06-01 19:59+0000\n"
 "PO-Revision-Date: 2026-02-07 15:22+0000\n"
 "Last-Translator: Timo Mikkolainen <tmikkola at gmail.com>\n"
 "Language-Team: Finnish <https://translations.scummvm.org/projects/scummvm/"
@@ -6263,7 +6263,7 @@ msgstr "R2"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1183 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "Valikko"
 
@@ -13946,75 +13946,81 @@ msgstr "Voita moottoripy??r??taistelu-huijaus"
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1182
+#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1196
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1187
+#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1201
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1192
+#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1206
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1197
+#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1211
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
+#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1216
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "Ota"
 
-#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1209
+#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "Vierit?? alas"
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1216
+#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1230
 #, fuzzy
 #| msgid "Slide left"
 msgid "Stick left"
 msgstr "Liu'u vasemmalle"
 
-#: engines/scumm/metaengine.cpp:1158 engines/scumm/metaengine.cpp:1223
+#: engines/scumm/metaengine.cpp:1161 engines/scumm/metaengine.cpp:1237
 #, fuzzy
 #| msgid "Slide right"
 msgid "Stick right"
 msgstr "Liu'u oikealle"
 
-#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1230
+#: engines/scumm/metaengine.cpp:1168 engines/scumm/metaengine.cpp:1244
 #, fuzzy
 #| msgid "Move / Select"
 msgid "Fire / select"
 msgstr "Liiku / Valitse"
 
-#: engines/scumm/metaengine.cpp:1169
+#: engines/scumm/metaengine.cpp:1173
 #, fuzzy
-#| msgid "Backspace"
-msgid "Back / skip"
-msgstr "Askelpalautin"
+#| msgid "Move back"
+msgid "Menu back"
+msgstr "Liiku taaksep??in"
 
-#: engines/scumm/metaengine.cpp:1180
+#: engines/scumm/metaengine.cpp:1178
+#, fuzzy
+#| msgid "Skip main menu"
+msgid "Skip / menu back"
+msgstr "Ohita p????valikko"
+
+#: engines/scumm/metaengine.cpp:1194
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1235
+#: engines/scumm/metaengine.cpp:1249
 #, fuzzy
 #| msgid "Move back"
 msgid "Cover / back"
 msgstr "Liiku taaksep??in"
 
-#: engines/scumm/metaengine.cpp:1241
+#: engines/scumm/metaengine.cpp:1255
 #, fuzzy
 #| msgid "Skip walk"
 msgid "Skip / back"
 msgstr "Ohita k??vely"
 
-#: engines/scumm/metaengine.cpp:1248
+#: engines/scumm/metaengine.cpp:1262
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu"
@@ -17726,6 +17732,11 @@ msgid ""
 "supported. Continue launching game?"
 msgstr ""
 
+#, fuzzy
+#~| msgid "Backspace"
+#~ msgid "Back / skip"
+#~ msgstr "Askelpalautin"
+
 #, fuzzy
 #~| msgid "No"
 #~ msgid "N"
diff --git a/po/fr_FR.po b/po/fr_FR.po
index b380ea97812..fe3bffefbe6 100644
--- a/po/fr_FR.po
+++ b/po/fr_FR.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 16:27+0000\n"
+"POT-Creation-Date: 2026-06-01 19:59+0000\n"
 "PO-Revision-Date: 2026-06-01 18:22+0000\n"
 "Last-Translator: Purple T <ZEONK at hotmail.com>\n"
 "Language-Team: French <https://translations.scummvm.org/projects/scummvm/"
@@ -6181,7 +6181,7 @@ msgstr "R2"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1183 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "Menu"
 
@@ -13744,61 +13744,69 @@ msgstr "Gagner les bagarres en b??cane (cheat)"
 msgid "Rebel Assault controls"
 msgstr "Contr??les de Rebel Assault"
 
-#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1182
+#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1196
 msgid "Aim up / menu up"
 msgstr "Vis??e haut / Menu haut"
 
-#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1187
+#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1201
 msgid "Aim down / menu down"
 msgstr "Vis??e bas / Menu bas"
 
-#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1192
+#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1206
 msgid "Aim left / menu left"
 msgstr "Vis??e gauche / menu gauche"
 
-#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1197
+#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1211
 msgid "Aim right / menu right"
 msgstr "Vis??e droite / Menu droite"
 
-#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
+#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1216
 msgid "Stick up"
 msgstr "Manche haut"
 
-#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1209
+#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
 msgid "Stick down"
 msgstr "Manche bas"
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1216
+#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1230
 msgid "Stick left"
 msgstr "Manche gauche"
 
-#: engines/scumm/metaengine.cpp:1158 engines/scumm/metaengine.cpp:1223
+#: engines/scumm/metaengine.cpp:1161 engines/scumm/metaengine.cpp:1237
 msgid "Stick right"
 msgstr "Manche droite"
 
-#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1230
+#: engines/scumm/metaengine.cpp:1168 engines/scumm/metaengine.cpp:1244
 msgid "Fire / select"
 msgstr "Tirer / S??lectionner"
 
-#: engines/scumm/metaengine.cpp:1169
-msgid "Back / skip"
-msgstr "Retour / passer"
+#: engines/scumm/metaengine.cpp:1173
+#, fuzzy
+#| msgid "Move back"
+msgid "Menu back"
+msgstr "Reculer"
+
+#: engines/scumm/metaengine.cpp:1178
+#, fuzzy
+#| msgid "Skip / menu"
+msgid "Skip / menu back"
+msgstr "Passer / menu"
 
-#: engines/scumm/metaengine.cpp:1180
+#: engines/scumm/metaengine.cpp:1194
 msgid "Rebel Assault II controls"
 msgstr "Contr??les de Rebel Assault II"
 
-#: engines/scumm/metaengine.cpp:1235
+#: engines/scumm/metaengine.cpp:1249
 msgid "Cover / back"
 msgstr "Couverture / retour"
 
-#: engines/scumm/metaengine.cpp:1241
+#: engines/scumm/metaengine.cpp:1255
 #, fuzzy
 #| msgid "Skip walk"
 msgid "Skip / back"
 msgstr "Passer le d??placement"
 
-#: engines/scumm/metaengine.cpp:1248
+#: engines/scumm/metaengine.cpp:1262
 msgid "Skip / menu"
 msgstr "Passer / menu"
 
@@ -17398,6 +17406,9 @@ msgstr ""
 "r??solues.  Ces puzzles peuvent ??galement ??tre r??solus ?? l'aide de sous-"
 "titres, s'ils sont pris en charge. Continuer ?? lancer le jeu ?"
 
+#~ msgid "Back / skip"
+#~ msgstr "Retour / passer"
+
 #~ msgid "N"
 #~ msgstr "N"
 
diff --git a/po/gl_ES.po b/po/gl_ES.po
index 76ff944e273..07af72c00dc 100644
--- a/po/gl_ES.po
+++ b/po/gl_ES.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 16:27+0000\n"
+"POT-Creation-Date: 2026-06-01 19:59+0000\n"
 "PO-Revision-Date: 2020-09-26 21:13+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Galician <https://translations.scummvm.org/projects/scummvm/"
@@ -5853,7 +5853,7 @@ msgstr ""
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1183 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "Men??"
 
@@ -13772,71 +13772,77 @@ msgstr ""
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1182
+#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1196
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1187
+#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1201
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1192
+#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1206
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1197
+#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1211
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
+#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1216
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "Coller"
 
-#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1209
+#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
 #, fuzzy
 msgid "Stick down"
 msgstr "Baixar lista"
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1216
+#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1230
 #, fuzzy
 #| msgid "Slide Left"
 msgid "Stick left"
 msgstr "Esvarar ?? esquerda"
 
-#: engines/scumm/metaengine.cpp:1158 engines/scumm/metaengine.cpp:1223
+#: engines/scumm/metaengine.cpp:1161 engines/scumm/metaengine.cpp:1237
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick right"
 msgstr "Esvarar ?? dereita"
 
-#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1230
+#: engines/scumm/metaengine.cpp:1168 engines/scumm/metaengine.cpp:1244
 #, fuzzy
 msgid "Fire / select"
 msgstr "Mover ?? esquerda"
 
-#: engines/scumm/metaengine.cpp:1169
+#: engines/scumm/metaengine.cpp:1173
 #, fuzzy
-msgid "Back / skip"
+#| msgid "Move Back"
+msgid "Menu back"
 msgstr "Mover cara atr??s"
 
-#: engines/scumm/metaengine.cpp:1180
+#: engines/scumm/metaengine.cpp:1178
+#, fuzzy
+msgid "Skip / menu back"
+msgstr "~V~olver ao Iniciador"
+
+#: engines/scumm/metaengine.cpp:1194
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1235
+#: engines/scumm/metaengine.cpp:1249
 #, fuzzy
 #| msgid "Move Back"
 msgid "Cover / back"
 msgstr "Mover cara atr??s"
 
-#: engines/scumm/metaengine.cpp:1241
+#: engines/scumm/metaengine.cpp:1255
 #, fuzzy
 msgid "Skip / back"
 msgstr "Omitir li??a"
 
-#: engines/scumm/metaengine.cpp:1248
+#: engines/scumm/metaengine.cpp:1262
 #, fuzzy
 msgid "Skip / menu"
 msgstr "~V~olver ao Iniciador"
@@ -17764,6 +17770,10 @@ msgid ""
 "supported. Continue launching game?"
 msgstr ""
 
+#, fuzzy
+#~ msgid "Back / skip"
+#~ msgstr "Mover cara atr??s"
+
 #, fuzzy
 #~| msgid "No"
 #~ msgid "N"
diff --git a/po/he.po b/po/he.po
index 7016c58febe..6482aba2da2 100644
--- a/po/he.po
+++ b/po/he.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.1.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 16:27+0000\n"
+"POT-Creation-Date: 2026-06-01 19:59+0000\n"
 "PO-Revision-Date: 2025-04-09 20:18+0000\n"
 "Last-Translator: Niv Baehr <bloop93 at gmail.com>\n"
 "Language-Team: Hebrew <https://translations.scummvm.org/projects/scummvm/"
@@ -5811,7 +5811,7 @@ msgstr "R2"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1183 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "??????????"
 
@@ -13993,75 +13993,81 @@ msgstr "?????????? ?????????????? ???????? ????????????????"
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1182
+#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1196
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1187
+#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1201
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1192
+#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1206
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1197
+#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1211
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
+#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1216
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "??????????"
 
-#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1209
+#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "?????????? ??????"
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1216
+#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1230
 #, fuzzy
 #| msgid "Slide Left"
 msgid "Stick left"
 msgstr "???????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1158 engines/scumm/metaengine.cpp:1223
+#: engines/scumm/metaengine.cpp:1161 engines/scumm/metaengine.cpp:1237
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick right"
 msgstr "???????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1230
+#: engines/scumm/metaengine.cpp:1168 engines/scumm/metaengine.cpp:1244
 #, fuzzy
 #| msgid "Move left"
 msgid "Fire / select"
 msgstr "?????????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1169
+#: engines/scumm/metaengine.cpp:1173
 #, fuzzy
-#| msgid "Backend"
-msgid "Back / skip"
-msgstr "??????????"
+#| msgid "Move Back"
+msgid "Menu back"
+msgstr "??????????"
+
+#: engines/scumm/metaengine.cpp:1178
+#, fuzzy
+#| msgid "Open main menu"
+msgid "Skip / menu back"
+msgstr "?????????? ?????????? ????????"
 
-#: engines/scumm/metaengine.cpp:1180
+#: engines/scumm/metaengine.cpp:1194
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1235
+#: engines/scumm/metaengine.cpp:1249
 #, fuzzy
 #| msgid "Move Back"
 msgid "Cover / back"
 msgstr "??????????"
 
-#: engines/scumm/metaengine.cpp:1241
+#: engines/scumm/metaengine.cpp:1255
 #, fuzzy
 #| msgid "Skip dialogue"
 msgid "Skip / back"
 msgstr "?????????? ???? ????????"
 
-#: engines/scumm/metaengine.cpp:1248
+#: engines/scumm/metaengine.cpp:1262
 #, fuzzy
 #| msgid "Open main menu"
 msgid "Skip / menu"
@@ -18159,6 +18165,11 @@ msgid ""
 "supported. Continue launching game?"
 msgstr ""
 
+#, fuzzy
+#~| msgid "Backend"
+#~ msgid "Back / skip"
+#~ msgstr "??????????"
+
 #, fuzzy
 #~| msgid "No"
 #~ msgid "N"
diff --git a/po/hi.po b/po/hi.po
index 91e6b41a508..dcd15442bbc 100644
--- a/po/hi.po
+++ b/po/hi.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 16:27+0000\n"
+"POT-Creation-Date: 2026-06-01 19:59+0000\n"
 "PO-Revision-Date: 2026-05-12 22:22+0000\n"
 "Last-Translator: KARTIK SAXENA <kartiksaxena2005 at gmail.com>\n"
 "Language-Team: Hindi <https://translations.scummvm.org/projects/scummvm/"
@@ -5638,7 +5638,7 @@ msgstr ""
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1183 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr ""
 
@@ -13251,72 +13251,78 @@ msgstr ""
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1182
+#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1196
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1187
+#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1201
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1192
+#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1206
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1197
+#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1211
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
+#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1216
 msgid "Stick up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1209
+#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
 #, fuzzy
 msgid "Stick down"
 msgstr "???????????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1216
+#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1230
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick left"
 msgstr "?????????????????? ?????????????????? ?????????"
 
-#: engines/scumm/metaengine.cpp:1158 engines/scumm/metaengine.cpp:1223
+#: engines/scumm/metaengine.cpp:1161 engines/scumm/metaengine.cpp:1237
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick right"
 msgstr "?????????????????? ?????????????????? ?????????"
 
-#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1230
+#: engines/scumm/metaengine.cpp:1168 engines/scumm/metaengine.cpp:1244
 #, fuzzy
 #| msgid "Move Left"
 msgid "Fire / select"
 msgstr "???????????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1169
+#: engines/scumm/metaengine.cpp:1173
 #, fuzzy
-#| msgid "Backspace"
-msgid "Back / skip"
-msgstr "????????????????????????"
+#| msgid "Move Forward"
+msgid "Menu back"
+msgstr "????????? ?????????"
+
+#: engines/scumm/metaengine.cpp:1178
+#, fuzzy
+#| msgid "Skip autosave"
+msgid "Skip / menu back"
+msgstr "?????????????????? ??????????????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1180
+#: engines/scumm/metaengine.cpp:1194
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1235
+#: engines/scumm/metaengine.cpp:1249
 #, fuzzy
 #| msgid "Move Forward"
 msgid "Cover / back"
 msgstr "????????? ?????????"
 
-#: engines/scumm/metaengine.cpp:1241
+#: engines/scumm/metaengine.cpp:1255
 #, fuzzy
 #| msgid "Show hidden files"
 msgid "Skip / back"
 msgstr "???????????? ????????? ?????????????????? ?????????????????????"
 
-#: engines/scumm/metaengine.cpp:1248
+#: engines/scumm/metaengine.cpp:1262
 #, fuzzy
 #| msgid "Skip autosave"
 msgid "Skip / menu"
@@ -17153,6 +17159,11 @@ msgid ""
 "supported. Continue launching game?"
 msgstr ""
 
+#, fuzzy
+#~| msgid "Backspace"
+#~ msgid "Back / skip"
+#~ msgstr "????????????????????????"
+
 #, fuzzy
 #~| msgid "No"
 #~ msgid "N"
diff --git a/po/hu_HU.po b/po/hu_HU.po
index 1573018cec3..39a4aea7ffd 100644
--- a/po/hu_HU.po
+++ b/po/hu_HU.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 16:27+0000\n"
+"POT-Creation-Date: 2026-06-01 19:59+0000\n"
 "PO-Revision-Date: 2025-03-07 00:18+0000\n"
 "Last-Translator: Hov??nszki Tam??s <gahex68379 at lofiey.com>\n"
 "Language-Team: Hungarian <https://translations.scummvm.org/projects/scummvm/"
@@ -6351,7 +6351,7 @@ msgstr "R2"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1183 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "Men??"
 
@@ -14453,75 +14453,81 @@ msgstr "Csal??s a motoros harc megnyer??s??hez"
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1182
+#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1196
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1187
+#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1201
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1192
+#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1206
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1197
+#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1211
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
+#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1216
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "Felvesz"
 
-#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1209
+#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "G??rget??s LE"
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1216
+#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1230
 #, fuzzy
 #| msgid "Slide Left"
 msgid "Stick left"
 msgstr "Cs??sz??s balra"
 
-#: engines/scumm/metaengine.cpp:1158 engines/scumm/metaengine.cpp:1223
+#: engines/scumm/metaengine.cpp:1161 engines/scumm/metaengine.cpp:1237
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick right"
 msgstr "Cs??sz??s jobbra"
 
-#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1230
+#: engines/scumm/metaengine.cpp:1168 engines/scumm/metaengine.cpp:1244
 #, fuzzy
 #| msgid "Move left"
 msgid "Fire / select"
 msgstr "Mozg??s balra"
 
-#: engines/scumm/metaengine.cpp:1169
+#: engines/scumm/metaengine.cpp:1173
 #, fuzzy
-#| msgid "Backspace"
-msgid "Back / skip"
-msgstr "T??rl??sgomb"
+#| msgid "Move Back"
+msgid "Menu back"
+msgstr "Mozg??s h??tra"
+
+#: engines/scumm/metaengine.cpp:1178
+#, fuzzy
+#| msgid "Skip main menu"
+msgid "Skip / menu back"
+msgstr "F?? men?? ??tugr??sa"
 
-#: engines/scumm/metaengine.cpp:1180
+#: engines/scumm/metaengine.cpp:1194
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1235
+#: engines/scumm/metaengine.cpp:1249
 #, fuzzy
 #| msgid "Move Back"
 msgid "Cover / back"
 msgstr "Mozg??s h??tra"
 
-#: engines/scumm/metaengine.cpp:1241
+#: engines/scumm/metaengine.cpp:1255
 #, fuzzy
 #| msgid "Skip dialogue"
 msgid "Skip / back"
 msgstr "Dial??gus ??tugr??sa"
 
-#: engines/scumm/metaengine.cpp:1248
+#: engines/scumm/metaengine.cpp:1262
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu"
@@ -18587,6 +18593,11 @@ msgid ""
 "supported. Continue launching game?"
 msgstr ""
 
+#, fuzzy
+#~| msgid "Backspace"
+#~ msgid "Back / skip"
+#~ msgstr "T??rl??sgomb"
+
 #, fuzzy
 #~| msgid "No"
 #~ msgid "N"
diff --git a/po/it_IT.po b/po/it_IT.po
index 71144ac81a9..573871a85b5 100644
--- a/po/it_IT.po
+++ b/po/it_IT.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 16:27+0000\n"
+"POT-Creation-Date: 2026-06-01 19:59+0000\n"
 "PO-Revision-Date: 2026-05-25 15:48+0000\n"
 "Last-Translator: Walter Agazzi <walter.agazzi at protonmail.com>\n"
 "Language-Team: Italian <https://translations.scummvm.org/projects/scummvm/"
@@ -6148,7 +6148,7 @@ msgstr "R2"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1183 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "Menu"
 
@@ -13694,75 +13694,81 @@ msgstr "Cheat: Vinci combattimento in moto"
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1182
+#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1196
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1187
+#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1201
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1192
+#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1206
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1197
+#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1211
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
+#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1216
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "Raccogli"
 
-#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1209
+#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "Scorri verso il basso"
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1216
+#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1230
 #, fuzzy
 #| msgid "Slide left"
 msgid "Stick left"
 msgstr "Scorri a sinistra"
 
-#: engines/scumm/metaengine.cpp:1158 engines/scumm/metaengine.cpp:1223
+#: engines/scumm/metaengine.cpp:1161 engines/scumm/metaengine.cpp:1237
 #, fuzzy
 #| msgid "Slide right"
 msgid "Stick right"
 msgstr "Scorri a destra"
 
-#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1230
+#: engines/scumm/metaengine.cpp:1168 engines/scumm/metaengine.cpp:1244
 #, fuzzy
 #| msgid "Move / Select"
 msgid "Fire / select"
 msgstr "Spostamento / Selezione"
 
-#: engines/scumm/metaengine.cpp:1169
+#: engines/scumm/metaengine.cpp:1173
 #, fuzzy
-#| msgid "Backspace"
-msgid "Back / skip"
-msgstr "Backspace"
+#| msgid "Move back"
+msgid "Menu back"
+msgstr "Arretra"
 
-#: engines/scumm/metaengine.cpp:1180
+#: engines/scumm/metaengine.cpp:1178
+#, fuzzy
+#| msgid "Skip main menu"
+msgid "Skip / menu back"
+msgstr "Salta men?? principale"
+
+#: engines/scumm/metaengine.cpp:1194
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1235
+#: engines/scumm/metaengine.cpp:1249
 #, fuzzy
 #| msgid "Move back"
 msgid "Cover / back"
 msgstr "Arretra"
 
-#: engines/scumm/metaengine.cpp:1241
+#: engines/scumm/metaengine.cpp:1255
 #, fuzzy
 #| msgid "Skip walk"
 msgid "Skip / back"
 msgstr "Salta spostamento"
 
-#: engines/scumm/metaengine.cpp:1248
+#: engines/scumm/metaengine.cpp:1262
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu"
@@ -17347,6 +17353,11 @@ msgstr ""
 "poterli risolvere. In alternativa, i puzzle possono essere risolti usando i "
 "sottotitoli, se supportati. Desideri continuare ad avviare il gioco?"
 
+#, fuzzy
+#~| msgid "Backspace"
+#~ msgid "Back / skip"
+#~ msgstr "Backspace"
+
 #, fuzzy
 #~| msgid "No"
 #~ msgid "N"
diff --git a/po/ja.po b/po/ja.po
index 7297140f23c..0c790856226 100644
--- a/po/ja.po
+++ b/po/ja.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 16:27+0000\n"
+"POT-Creation-Date: 2026-06-01 19:59+0000\n"
 "PO-Revision-Date: 2026-04-26 07:22+0000\n"
 "Last-Translator: VAN-Gluon <van.gluonp at gmail.com>\n"
 "Language-Team: Japanese <https://translations.scummvm.org/projects/scummvm/"
@@ -6002,7 +6002,7 @@ msgstr "R2"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1183 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "????????????"
 
@@ -14097,75 +14097,81 @@ msgstr "????????????????????????"
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1182
+#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1196
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1187
+#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1201
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1192
+#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1206
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1197
+#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1211
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
+#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1216
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "??????"
 
-#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1209
+#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "?????????????????????"
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1216
+#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1230
 #, fuzzy
 #| msgid "Slide Left"
 msgid "Stick left"
 msgstr "??????????????????"
 
-#: engines/scumm/metaengine.cpp:1158 engines/scumm/metaengine.cpp:1223
+#: engines/scumm/metaengine.cpp:1161 engines/scumm/metaengine.cpp:1237
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick right"
 msgstr "??????????????????"
 
-#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1230
+#: engines/scumm/metaengine.cpp:1168 engines/scumm/metaengine.cpp:1244
 #, fuzzy
 #| msgid "Move left"
 msgid "Fire / select"
 msgstr "????????????"
 
-#: engines/scumm/metaengine.cpp:1169
+#: engines/scumm/metaengine.cpp:1173
 #, fuzzy
-#| msgid "Backspace"
-msgid "Back / skip"
-msgstr "?????????????????????"
+#| msgid "Move Back"
+msgid "Menu back"
+msgstr "??????"
+
+#: engines/scumm/metaengine.cpp:1178
+#, fuzzy
+#| msgid "Skip main menu"
+msgid "Skip / menu back"
+msgstr "????????????????????????????????????"
 
-#: engines/scumm/metaengine.cpp:1180
+#: engines/scumm/metaengine.cpp:1194
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1235
+#: engines/scumm/metaengine.cpp:1249
 #, fuzzy
 #| msgid "Move Back"
 msgid "Cover / back"
 msgstr "??????"
 
-#: engines/scumm/metaengine.cpp:1241
+#: engines/scumm/metaengine.cpp:1255
 #, fuzzy
 #| msgid "Skip dialogue"
 msgid "Skip / back"
 msgstr "??????????????????????????????"
 
-#: engines/scumm/metaengine.cpp:1248
+#: engines/scumm/metaengine.cpp:1262
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu"
@@ -18240,6 +18246,11 @@ msgid ""
 "supported. Continue launching game?"
 msgstr ""
 
+#, fuzzy
+#~| msgid "Backspace"
+#~ msgid "Back / skip"
+#~ msgstr "?????????????????????"
+
 #, fuzzy
 #~| msgid "No"
 #~ msgid "N"
diff --git a/po/ka.po b/po/ka.po
index e5b24edc455..a9881fbb316 100644
--- a/po/ka.po
+++ b/po/ka.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 16:27+0000\n"
+"POT-Creation-Date: 2026-06-01 19:59+0000\n"
 "PO-Revision-Date: 2026-05-09 12:22+0000\n"
 "Last-Translator: NorwayFun <temuri.doghonadze at gmail.com>\n"
 "Language-Team: Georgian <https://translations.scummvm.org/projects/scummvm/"
@@ -5463,7 +5463,7 @@ msgstr "R2"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1183 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "???????????????"
 
@@ -12462,75 +12462,81 @@ msgstr ""
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1182
+#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1196
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1187
+#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1201
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1192
+#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1206
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1197
+#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1211
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
+#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1216
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "???????????????"
 
-#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1209
+#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "??????????????? ????????????????????????"
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1216
+#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1230
 #, fuzzy
 #| msgid "Slide left"
 msgid "Stick left"
 msgstr "?????????????????????????????? ????????????????????????"
 
-#: engines/scumm/metaengine.cpp:1158 engines/scumm/metaengine.cpp:1223
+#: engines/scumm/metaengine.cpp:1161 engines/scumm/metaengine.cpp:1237
 #, fuzzy
 #| msgid "Slide right"
 msgid "Stick right"
 msgstr "?????????????????????????????? ????????????????????????"
 
-#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1230
+#: engines/scumm/metaengine.cpp:1168 engines/scumm/metaengine.cpp:1244
 #, fuzzy
 #| msgid "Move / Select"
 msgid "Fire / select"
 msgstr "???????????????????????????????????? / ????????????????????????"
 
-#: engines/scumm/metaengine.cpp:1169
+#: engines/scumm/metaengine.cpp:1173
 #, fuzzy
-#| msgid "Backspace"
-msgid "Back / skip"
-msgstr "Backspace"
+#| msgid "Move back"
+msgid "Menu back"
+msgstr "???????????? ????????????????????????"
 
-#: engines/scumm/metaengine.cpp:1180
+#: engines/scumm/metaengine.cpp:1178
+#, fuzzy
+#| msgid "Skip main menu"
+msgid "Skip / menu back"
+msgstr "????????????????????? ?????????????????? ??????????????????????????????"
+
+#: engines/scumm/metaengine.cpp:1194
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1235
+#: engines/scumm/metaengine.cpp:1249
 #, fuzzy
 #| msgid "Move back"
 msgid "Cover / back"
 msgstr "???????????? ????????????????????????"
 
-#: engines/scumm/metaengine.cpp:1241
+#: engines/scumm/metaengine.cpp:1255
 #, fuzzy
 #| msgid "Skip walk"
 msgid "Skip / back"
 msgstr "???????????????????????? ??????????????????????????????"
 
-#: engines/scumm/metaengine.cpp:1248
+#: engines/scumm/metaengine.cpp:1262
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu"
@@ -15929,6 +15935,11 @@ msgid ""
 "supported. Continue launching game?"
 msgstr ""
 
+#, fuzzy
+#~| msgid "Backspace"
+#~ msgid "Back / skip"
+#~ msgstr "Backspace"
+
 #, fuzzy
 #~| msgid "No"
 #~ msgid "N"
diff --git a/po/ko.po b/po/ko.po
index 34c13fda2d8..2be315c018c 100644
--- a/po/ko.po
+++ b/po/ko.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 16:27+0000\n"
+"POT-Creation-Date: 2026-06-01 19:59+0000\n"
 "PO-Revision-Date: 2026-05-19 20:22+0000\n"
 "Last-Translator: Hoseok Seo <ddinghoya at gmail.com>\n"
 "Language-Team: Korean <https://translations.scummvm.org/projects/scummvm/"
@@ -6013,7 +6013,7 @@ msgstr "R2 ??????"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1183 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "??????"
 
@@ -13374,75 +13374,81 @@ msgstr "????????? ???????????? ???????????? ??????"
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1182
+#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1196
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1187
+#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1201
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1192
+#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1206
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1197
+#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1211
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
+#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1216
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "??????"
 
-#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1209
+#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "????????? ?????????"
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1216
+#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1230
 #, fuzzy
 #| msgid "Slide left"
 msgid "Stick left"
 msgstr "???????????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1158 engines/scumm/metaengine.cpp:1223
+#: engines/scumm/metaengine.cpp:1161 engines/scumm/metaengine.cpp:1237
 #, fuzzy
 #| msgid "Slide right"
 msgid "Stick right"
 msgstr "????????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1230
+#: engines/scumm/metaengine.cpp:1168 engines/scumm/metaengine.cpp:1244
 #, fuzzy
 #| msgid "Move / Select"
 msgid "Fire / select"
 msgstr "?????? / ??????"
 
-#: engines/scumm/metaengine.cpp:1169
+#: engines/scumm/metaengine.cpp:1173
 #, fuzzy
-#| msgid "Backspace"
-msgid "Back / skip"
-msgstr "Backspace ???"
+#| msgid "Move back"
+msgid "Menu back"
+msgstr "?????? ??????"
 
-#: engines/scumm/metaengine.cpp:1180
+#: engines/scumm/metaengine.cpp:1178
+#, fuzzy
+#| msgid "Skip main menu"
+msgid "Skip / menu back"
+msgstr "?????? ?????? ????????????"
+
+#: engines/scumm/metaengine.cpp:1194
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1235
+#: engines/scumm/metaengine.cpp:1249
 #, fuzzy
 #| msgid "Move back"
 msgid "Cover / back"
 msgstr "?????? ??????"
 
-#: engines/scumm/metaengine.cpp:1241
+#: engines/scumm/metaengine.cpp:1255
 #, fuzzy
 #| msgid "Skip walk"
 msgid "Skip / back"
 msgstr "?????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1248
+#: engines/scumm/metaengine.cpp:1262
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu"
@@ -16956,6 +16962,11 @@ msgstr ""
 "MIDI ???????????? ????????? ?????? ?????? ????????? ???????????? ????????????.  ????????? ????????? ??????"
 "?????? ?????? ????????? ???????????? ????????? ??? ????????????. ????????? ?????? ????????????????"
 
+#, fuzzy
+#~| msgid "Backspace"
+#~ msgid "Back / skip"
+#~ msgstr "Backspace ???"
+
 #, fuzzy
 #~| msgid "No"
 #~ msgid "N"
diff --git a/po/nb_NO.po b/po/nb_NO.po
index 5c1e44f423e..874a2eec80b 100644
--- a/po/nb_NO.po
+++ b/po/nb_NO.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 16:27+0000\n"
+"POT-Creation-Date: 2026-06-01 19:59+0000\n"
 "PO-Revision-Date: 2026-02-07 15:22+0000\n"
 "Last-Translator: Einar Johan Tr??an S??m??en <einarjohants at gmail.com>\n"
 "Language-Team: Norwegian Bokm??l <https://translations.scummvm.org/projects/"
@@ -6412,7 +6412,7 @@ msgstr "R2"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1183 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "Meny"
 
@@ -14544,75 +14544,81 @@ msgstr "Juksefunksjon for ?? vinne motorsykkelkamper"
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1182
+#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1196
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1187
+#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1201
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1192
+#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1206
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1197
+#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1211
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
+#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1216
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "Plukk opp"
 
-#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1209
+#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "Bla ned"
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1216
+#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1230
 #, fuzzy
 #| msgid "Slide Left"
 msgid "Stick left"
 msgstr "Skli mot Venstre"
 
-#: engines/scumm/metaengine.cpp:1158 engines/scumm/metaengine.cpp:1223
+#: engines/scumm/metaengine.cpp:1161 engines/scumm/metaengine.cpp:1237
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick right"
 msgstr "Skli mot H??yre"
 
-#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1230
+#: engines/scumm/metaengine.cpp:1168 engines/scumm/metaengine.cpp:1244
 #, fuzzy
 #| msgid "Move left"
 msgid "Fire / select"
 msgstr "G?? til venstre"
 
-#: engines/scumm/metaengine.cpp:1169
+#: engines/scumm/metaengine.cpp:1173
 #, fuzzy
-#| msgid "Backspace"
-msgid "Back / skip"
-msgstr "Rettetast"
+#| msgid "Move Back"
+msgid "Menu back"
+msgstr "Beveg Bakover"
+
+#: engines/scumm/metaengine.cpp:1178
+#, fuzzy
+#| msgid "Skip main menu"
+msgid "Skip / menu back"
+msgstr "Hopp over hovedmeny"
 
-#: engines/scumm/metaengine.cpp:1180
+#: engines/scumm/metaengine.cpp:1194
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1235
+#: engines/scumm/metaengine.cpp:1249
 #, fuzzy
 #| msgid "Move Back"
 msgid "Cover / back"
 msgstr "Beveg Bakover"
 
-#: engines/scumm/metaengine.cpp:1241
+#: engines/scumm/metaengine.cpp:1255
 #, fuzzy
 #| msgid "Skip dialogue"
 msgid "Skip / back"
 msgstr "Hopp over dialog"
 
-#: engines/scumm/metaengine.cpp:1248
+#: engines/scumm/metaengine.cpp:1262
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu"
@@ -18763,6 +18769,11 @@ msgid ""
 "supported. Continue launching game?"
 msgstr ""
 
+#, fuzzy
+#~| msgid "Backspace"
+#~ msgid "Back / skip"
+#~ msgstr "Rettetast"
+
 #, fuzzy
 #~| msgid "No"
 #~ msgid "N"
diff --git a/po/nl_NL.po b/po/nl_NL.po
index a4e841441b2..9b7e4e66f9e 100644
--- a/po/nl_NL.po
+++ b/po/nl_NL.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.9.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 16:27+0000\n"
+"POT-Creation-Date: 2026-06-01 19:59+0000\n"
 "PO-Revision-Date: 2026-02-11 14:23+0000\n"
 "Last-Translator: SecularSteve <fairfull.playing at gmail.com>\n"
 "Language-Team: Dutch <https://translations.scummvm.org/projects/scummvm/"
@@ -6087,7 +6087,7 @@ msgstr "R2"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1183 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "Menu"
 
@@ -13737,75 +13737,81 @@ msgstr "Win de fietsstrijd-cheat"
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1182
+#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1196
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1187
+#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1201
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1192
+#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1206
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1197
+#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1211
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
+#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1216
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "Pak op"
 
-#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1209
+#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "Scroll omlaag"
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1216
+#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1230
 #, fuzzy
 #| msgid "Slide left"
 msgid "Stick left"
 msgstr "Schuif naar link"
 
-#: engines/scumm/metaengine.cpp:1158 engines/scumm/metaengine.cpp:1223
+#: engines/scumm/metaengine.cpp:1161 engines/scumm/metaengine.cpp:1237
 #, fuzzy
 #| msgid "Slide right"
 msgid "Stick right"
 msgstr "Schuif naar rechts"
 
-#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1230
+#: engines/scumm/metaengine.cpp:1168 engines/scumm/metaengine.cpp:1244
 #, fuzzy
 #| msgid "Move / Select"
 msgid "Fire / select"
 msgstr "Verplaatsen / Selecteren"
 
-#: engines/scumm/metaengine.cpp:1169
+#: engines/scumm/metaengine.cpp:1173
 #, fuzzy
-#| msgid "Backspace"
-msgid "Back / skip"
-msgstr "Backspace"
+#| msgid "Move back"
+msgid "Menu back"
+msgstr "Teruggaan"
 
-#: engines/scumm/metaengine.cpp:1180
+#: engines/scumm/metaengine.cpp:1178
+#, fuzzy
+#| msgid "Skip main menu"
+msgid "Skip / menu back"
+msgstr "Hoofdmenu overslaan"
+
+#: engines/scumm/metaengine.cpp:1194
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1235
+#: engines/scumm/metaengine.cpp:1249
 #, fuzzy
 #| msgid "Move back"
 msgid "Cover / back"
 msgstr "Teruggaan"
 
-#: engines/scumm/metaengine.cpp:1241
+#: engines/scumm/metaengine.cpp:1255
 #, fuzzy
 #| msgid "Skip walk"
 msgid "Skip / back"
 msgstr "Sla wandeling over"
 
-#: engines/scumm/metaengine.cpp:1248
+#: engines/scumm/metaengine.cpp:1262
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu"
@@ -17406,6 +17412,11 @@ msgstr ""
 "behulp van ondertitels, indien ondersteund. Doorgaan met het starten van het "
 "spel?"
 
+#, fuzzy
+#~| msgid "Backspace"
+#~ msgid "Back / skip"
+#~ msgstr "Backspace"
+
 #, fuzzy
 #~| msgid "No"
 #~ msgid "N"
diff --git a/po/nn_NO.po b/po/nn_NO.po
index 2200c0b7291..7b09e82786f 100644
--- a/po/nn_NO.po
+++ b/po/nn_NO.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 16:27+0000\n"
+"POT-Creation-Date: 2026-06-01 19:59+0000\n"
 "PO-Revision-Date: 2023-12-14 14:28+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Norwegian Nynorsk <https://translations.scummvm.org/projects/"
@@ -5814,7 +5814,7 @@ msgstr ""
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1183 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "Meny"
 
@@ -13669,72 +13669,77 @@ msgstr ""
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1182
+#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1196
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1187
+#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1201
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1192
+#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1206
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1197
+#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1211
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
+#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1216
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "Plukk opp"
 
-#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1209
+#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
 #, fuzzy
 msgid "Stick down"
 msgstr "Bla liste ned"
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1216
+#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1230
 #, fuzzy
 #| msgid "Slide Left"
 msgid "Stick left"
 msgstr "Skli til Venstre"
 
-#: engines/scumm/metaengine.cpp:1158 engines/scumm/metaengine.cpp:1223
+#: engines/scumm/metaengine.cpp:1161 engines/scumm/metaengine.cpp:1237
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick right"
 msgstr "Skli til H??yre"
 
-#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1230
+#: engines/scumm/metaengine.cpp:1168 engines/scumm/metaengine.cpp:1244
 #, fuzzy
 msgid "Fire / select"
 msgstr "Skli til Venstre"
 
-#: engines/scumm/metaengine.cpp:1169
+#: engines/scumm/metaengine.cpp:1173
 #, fuzzy
-#| msgid "Backspace"
-msgid "Back / skip"
-msgstr "Tilbaketast"
+#| msgid "Move Back"
+msgid "Menu back"
+msgstr "Beveg Bakover"
 
-#: engines/scumm/metaengine.cpp:1180
+#: engines/scumm/metaengine.cpp:1178
+#, fuzzy
+msgid "Skip / menu back"
+msgstr "Tilbake til Oppsta~r~tar"
+
+#: engines/scumm/metaengine.cpp:1194
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1235
+#: engines/scumm/metaengine.cpp:1249
 #, fuzzy
 #| msgid "Move Back"
 msgid "Cover / back"
 msgstr "Beveg Bakover"
 
-#: engines/scumm/metaengine.cpp:1241
+#: engines/scumm/metaengine.cpp:1255
 #, fuzzy
 msgid "Skip / back"
 msgstr "Hopp over linje"
 
-#: engines/scumm/metaengine.cpp:1248
+#: engines/scumm/metaengine.cpp:1262
 #, fuzzy
 msgid "Skip / menu"
 msgstr "Tilbake til Oppsta~r~tar"
@@ -17685,6 +17690,11 @@ msgid ""
 "supported. Continue launching game?"
 msgstr ""
 
+#, fuzzy
+#~| msgid "Backspace"
+#~ msgid "Back / skip"
+#~ msgstr "Tilbaketast"
+
 #, fuzzy
 #~| msgid "No"
 #~ msgid "N"
diff --git a/po/pl_PL.po b/po/pl_PL.po
index 3792f1b7140..31cc9786123 100644
--- a/po/pl_PL.po
+++ b/po/pl_PL.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 16:27+0000\n"
+"POT-Creation-Date: 2026-06-01 19:59+0000\n"
 "PO-Revision-Date: 2026-04-17 00:22+0000\n"
 "Last-Translator: SkiffPL <skiff at windowslive.com>\n"
 "Language-Team: Polish <https://translations.scummvm.org/projects/scummvm/"
@@ -6099,7 +6099,7 @@ msgstr "R2"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1183 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "Menu"
 
@@ -13916,75 +13916,81 @@ msgstr "Cheat 'wygraj walk?? na motocyklu'"
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1182
+#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1196
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1187
+#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1201
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1192
+#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1206
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1197
+#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1211
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
+#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1216
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "Podnie??"
 
-#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1209
+#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "Przewi?? w d????"
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1216
+#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1230
 #, fuzzy
 #| msgid "Slide left"
 msgid "Stick left"
 msgstr "??lizg w lewo"
 
-#: engines/scumm/metaengine.cpp:1158 engines/scumm/metaengine.cpp:1223
+#: engines/scumm/metaengine.cpp:1161 engines/scumm/metaengine.cpp:1237
 #, fuzzy
 #| msgid "Slide right"
 msgid "Stick right"
 msgstr "??lizg w prawo"
 
-#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1230
+#: engines/scumm/metaengine.cpp:1168 engines/scumm/metaengine.cpp:1244
 #, fuzzy
 #| msgid "Move / Select"
 msgid "Fire / select"
 msgstr "Ruch / Zaznacz"
 
-#: engines/scumm/metaengine.cpp:1169
+#: engines/scumm/metaengine.cpp:1173
 #, fuzzy
-#| msgid "Backspace"
-msgid "Back / skip"
-msgstr "Backspace"
+#| msgid "Move back"
+msgid "Menu back"
+msgstr "Ruch do ty??u"
 
-#: engines/scumm/metaengine.cpp:1180
+#: engines/scumm/metaengine.cpp:1178
+#, fuzzy
+#| msgid "Skip main menu"
+msgid "Skip / menu back"
+msgstr "Pomijaj menu g????wne"
+
+#: engines/scumm/metaengine.cpp:1194
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1235
+#: engines/scumm/metaengine.cpp:1249
 #, fuzzy
 #| msgid "Move back"
 msgid "Cover / back"
 msgstr "Ruch do ty??u"
 
-#: engines/scumm/metaengine.cpp:1241
+#: engines/scumm/metaengine.cpp:1255
 #, fuzzy
 #| msgid "Skip dialog"
 msgid "Skip / back"
 msgstr "Pomi?? dialog"
 
-#: engines/scumm/metaengine.cpp:1248
+#: engines/scumm/metaengine.cpp:1262
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu"
@@ -17661,6 +17667,11 @@ msgstr ""
 "rozwi??zania. Te ??amig????wki mo??na r??wnie?? rozwi??za?? za pomoc?? napis??w, je??li "
 "s?? obs??ugiwane. Kontynuowa?? uruchamianie gry?"
 
+#, fuzzy
+#~| msgid "Backspace"
+#~ msgid "Back / skip"
+#~ msgstr "Backspace"
+
 #, fuzzy
 #~| msgid "No"
 #~ msgid "N"
diff --git a/po/pt_BR.po b/po/pt_BR.po
index 84a52105dad..0103943b107 100644
--- a/po/pt_BR.po
+++ b/po/pt_BR.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 16:27+0000\n"
+"POT-Creation-Date: 2026-06-01 19:59+0000\n"
 "PO-Revision-Date: 2024-11-16 02:13+0000\n"
 "Last-Translator: Marcel Souza Lemes <marcosoutsider at gmail.com>\n"
 "Language-Team: Portuguese (Brazil) <https://translations.scummvm.org/"
@@ -6376,7 +6376,7 @@ msgstr "R2"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1183 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "Menu"
 
@@ -14516,75 +14516,81 @@ msgstr "Trapa??a para vencer a briga de motos"
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1182
+#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1196
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1187
+#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1201
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1192
+#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1206
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1197
+#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1211
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
+#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1216
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "Pegar"
 
-#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1209
+#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "Rolar para baixo"
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1216
+#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1230
 #, fuzzy
 #| msgid "Slide Left"
 msgid "Stick left"
 msgstr "Deslizar para Esquerda"
 
-#: engines/scumm/metaengine.cpp:1158 engines/scumm/metaengine.cpp:1223
+#: engines/scumm/metaengine.cpp:1161 engines/scumm/metaengine.cpp:1237
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick right"
 msgstr "Deslizar para Direita"
 
-#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1230
+#: engines/scumm/metaengine.cpp:1168 engines/scumm/metaengine.cpp:1244
 #, fuzzy
 #| msgid "Move left"
 msgid "Fire / select"
 msgstr "Mover para esquerda"
 
-#: engines/scumm/metaengine.cpp:1169
+#: engines/scumm/metaengine.cpp:1173
 #, fuzzy
-#| msgid "Backspace"
-msgid "Back / skip"
-msgstr "Retroceder"
+#| msgid "Move Back"
+msgid "Menu back"
+msgstr "Mover para Tr??s"
+
+#: engines/scumm/metaengine.cpp:1178
+#, fuzzy
+#| msgid "Skip main menu"
+msgid "Skip / menu back"
+msgstr "Omitir menu principal"
 
-#: engines/scumm/metaengine.cpp:1180
+#: engines/scumm/metaengine.cpp:1194
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1235
+#: engines/scumm/metaengine.cpp:1249
 #, fuzzy
 #| msgid "Move Back"
 msgid "Cover / back"
 msgstr "Mover para Tr??s"
 
-#: engines/scumm/metaengine.cpp:1241
+#: engines/scumm/metaengine.cpp:1255
 #, fuzzy
 #| msgid "Skip dialogue"
 msgid "Skip / back"
 msgstr "Pular di??logo"
 
-#: engines/scumm/metaengine.cpp:1248
+#: engines/scumm/metaengine.cpp:1262
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu"
@@ -18681,6 +18687,11 @@ msgid ""
 "supported. Continue launching game?"
 msgstr ""
 
+#, fuzzy
+#~| msgid "Backspace"
+#~ msgid "Back / skip"
+#~ msgstr "Retroceder"
+
 #, fuzzy
 #~| msgid "No"
 #~ msgid "N"
diff --git a/po/pt_PT.po b/po/pt_PT.po
index 37675c9f6df..e813ef531c0 100644
--- a/po/pt_PT.po
+++ b/po/pt_PT.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.10.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 16:27+0000\n"
+"POT-Creation-Date: 2026-06-01 19:59+0000\n"
 "PO-Revision-Date: 2022-06-05 14:09+0000\n"
 "Last-Translator: Daniel <supsuper at gmail.com>\n"
 "Language-Team: Portuguese (Portugal) <https://translations.scummvm.org/"
@@ -5838,7 +5838,7 @@ msgstr "R2"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1183 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "Menu"
 
@@ -13791,75 +13791,80 @@ msgstr ""
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1182
+#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1196
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1187
+#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1201
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1192
+#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1206
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1197
+#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1211
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
+#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1216
 #, fuzzy
 #| msgid "Left Stick X"
 msgid "Stick up"
 msgstr "Anal??gico Esquerdo X"
 
-#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1209
+#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "Deslizar para baixo"
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1216
+#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1230
 #, fuzzy
 #| msgid "Slide Left"
 msgid "Stick left"
 msgstr "Deslizar ?? esquerda"
 
-#: engines/scumm/metaengine.cpp:1158 engines/scumm/metaengine.cpp:1223
+#: engines/scumm/metaengine.cpp:1161 engines/scumm/metaengine.cpp:1237
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick right"
 msgstr "Deslizar ?? direita"
 
-#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1230
+#: engines/scumm/metaengine.cpp:1168 engines/scumm/metaengine.cpp:1244
 #, fuzzy
 #| msgid "Move left"
 msgid "Fire / select"
 msgstr "Mover para a esquerda"
 
-#: engines/scumm/metaengine.cpp:1169
+#: engines/scumm/metaengine.cpp:1173
 #, fuzzy
-#| msgid "Backend"
-msgid "Back / skip"
-msgstr "Sistema"
+#| msgid "Move Back"
+msgid "Menu back"
+msgstr "Retroceder"
 
-#: engines/scumm/metaengine.cpp:1180
+#: engines/scumm/metaengine.cpp:1178
+#, fuzzy
+msgid "Skip / menu back"
+msgstr "~M~enu principal"
+
+#: engines/scumm/metaengine.cpp:1194
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1235
+#: engines/scumm/metaengine.cpp:1249
 #, fuzzy
 #| msgid "Move Back"
 msgid "Cover / back"
 msgstr "Retroceder"
 
-#: engines/scumm/metaengine.cpp:1241
+#: engines/scumm/metaengine.cpp:1255
 #, fuzzy
 #| msgid "Skip dialogue"
 msgid "Skip / back"
 msgstr "Ignorar frase"
 
-#: engines/scumm/metaengine.cpp:1248
+#: engines/scumm/metaengine.cpp:1262
 #, fuzzy
 msgid "Skip / menu"
 msgstr "~M~enu principal"
@@ -17838,6 +17843,11 @@ msgid ""
 "supported. Continue launching game?"
 msgstr ""
 
+#, fuzzy
+#~| msgid "Backend"
+#~ msgid "Back / skip"
+#~ msgstr "Sistema"
+
 #, fuzzy
 #~| msgid "No"
 #~ msgid "N"
diff --git a/po/ro.po b/po/ro.po
index 6bcec3d1781..cecab5cd6d8 100644
--- a/po/ro.po
+++ b/po/ro.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.10.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 16:27+0000\n"
+"POT-Creation-Date: 2026-06-01 19:59+0000\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: Automatically generated\n"
 "Language-Team: none\n"
@@ -5434,7 +5434,7 @@ msgstr ""
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1183 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr ""
 
@@ -12421,59 +12421,63 @@ msgstr ""
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1182
+#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1196
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1187
+#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1201
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1192
+#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1206
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1197
+#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1211
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
+#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1216
 msgid "Stick up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1209
+#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
 msgid "Stick down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1216
+#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1230
 msgid "Stick left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1158 engines/scumm/metaengine.cpp:1223
+#: engines/scumm/metaengine.cpp:1161 engines/scumm/metaengine.cpp:1237
 msgid "Stick right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1230
+#: engines/scumm/metaengine.cpp:1168 engines/scumm/metaengine.cpp:1244
 msgid "Fire / select"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1169
-msgid "Back / skip"
+#: engines/scumm/metaengine.cpp:1173
+msgid "Menu back"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1180
+#: engines/scumm/metaengine.cpp:1178
+msgid "Skip / menu back"
+msgstr ""
+
+#: engines/scumm/metaengine.cpp:1194
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1235
+#: engines/scumm/metaengine.cpp:1249
 msgid "Cover / back"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1241
+#: engines/scumm/metaengine.cpp:1255
 msgid "Skip / back"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1248
+#: engines/scumm/metaengine.cpp:1262
 msgid "Skip / menu"
 msgstr ""
 
diff --git a/po/ru_RU.po b/po/ru_RU.po
index 58794bf2592..fb5a10125cc 100644
--- a/po/ru_RU.po
+++ b/po/ru_RU.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.8.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 16:27+0000\n"
+"POT-Creation-Date: 2026-06-01 19:59+0000\n"
 "PO-Revision-Date: 2026-05-06 08:54+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Russian <https://translations.scummvm.org/projects/scummvm/"
@@ -6109,7 +6109,7 @@ msgstr "R2"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1183 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "????????"
 
@@ -13585,75 +13585,81 @@ msgstr "?????? ?????? ???????????????? ???????? ???? ????????????"
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1182
+#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1196
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1187
+#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1201
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1192
+#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1206
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1197
+#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1211
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
+#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1216
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "??????????????"
 
-#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1209
+#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "???????????????????? ????????"
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1216
+#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1230
 #, fuzzy
 #| msgid "Slide left"
 msgid "Stick left"
 msgstr "?????????????????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1158 engines/scumm/metaengine.cpp:1223
+#: engines/scumm/metaengine.cpp:1161 engines/scumm/metaengine.cpp:1237
 #, fuzzy
 #| msgid "Slide right"
 msgid "Stick right"
 msgstr "?????????????????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1230
+#: engines/scumm/metaengine.cpp:1168 engines/scumm/metaengine.cpp:1244
 #, fuzzy
 #| msgid "Move / Select"
 msgid "Fire / select"
 msgstr "???????? / ??????????????"
 
-#: engines/scumm/metaengine.cpp:1169
+#: engines/scumm/metaengine.cpp:1173
 #, fuzzy
-#| msgid "Backspace"
-msgid "Back / skip"
-msgstr "Backspace"
+#| msgid "Move back"
+msgid "Menu back"
+msgstr "???????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1180
+#: engines/scumm/metaengine.cpp:1178
+#, fuzzy
+#| msgid "Skip main menu"
+msgid "Skip / menu back"
+msgstr "???????????????????? ?????????????? ????????"
+
+#: engines/scumm/metaengine.cpp:1194
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1235
+#: engines/scumm/metaengine.cpp:1249
 #, fuzzy
 #| msgid "Move back"
 msgid "Cover / back"
 msgstr "???????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1241
+#: engines/scumm/metaengine.cpp:1255
 #, fuzzy
 #| msgid "Skip walk"
 msgid "Skip / back"
 msgstr "???????????????????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1248
+#: engines/scumm/metaengine.cpp:1262
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu"
@@ -17196,6 +17202,11 @@ msgstr ""
 "MIDI-??????????. ?????? ?????????????????????? ?????????? ?????????? ???????????? ?? ?????????????? ??????????????????, ???????? ?????? "
 "????????????????????????????. ???????????????????? ???????????? ?????????"
 
+#, fuzzy
+#~| msgid "Backspace"
+#~ msgid "Back / skip"
+#~ msgstr "Backspace"
+
 #, fuzzy
 #~| msgid "No"
 #~ msgid "N"
diff --git a/po/sv_SE.po b/po/sv_SE.po
index 8fb162004d5..8ed86b8f91d 100644
--- a/po/sv_SE.po
+++ b/po/sv_SE.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.5.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 16:27+0000\n"
+"POT-Creation-Date: 2026-06-01 19:59+0000\n"
 "PO-Revision-Date: 2026-04-19 13:22+0000\n"
 "Last-Translator: Daniel Nylander <po at danielnylander.se>\n"
 "Language-Team: Swedish <https://translations.scummvm.org/projects/scummvm/"
@@ -6086,7 +6086,7 @@ msgstr "R2"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1183 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "Meny"
 
@@ -13634,75 +13634,81 @@ msgstr "Vinn cykelkampsfusket"
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1182
+#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1196
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1187
+#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1201
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1192
+#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1206
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1197
+#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1211
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
+#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1216
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "Ta"
 
-#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1209
+#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "Bl??ddra ned??t"
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1216
+#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1230
 #, fuzzy
 #| msgid "Slide left"
 msgid "Stick left"
 msgstr "Glid v??nster"
 
-#: engines/scumm/metaengine.cpp:1158 engines/scumm/metaengine.cpp:1223
+#: engines/scumm/metaengine.cpp:1161 engines/scumm/metaengine.cpp:1237
 #, fuzzy
 #| msgid "Slide right"
 msgid "Stick right"
 msgstr "Glid h??ger"
 
-#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1230
+#: engines/scumm/metaengine.cpp:1168 engines/scumm/metaengine.cpp:1244
 #, fuzzy
 #| msgid "Move / Select"
 msgid "Fire / select"
 msgstr "Flytta / V??lj"
 
-#: engines/scumm/metaengine.cpp:1169
+#: engines/scumm/metaengine.cpp:1173
 #, fuzzy
-#| msgid "Backspace"
-msgid "Back / skip"
-msgstr "Backsteg"
+#| msgid "Move back"
+msgid "Menu back"
+msgstr "Stega bak??t"
 
-#: engines/scumm/metaengine.cpp:1180
+#: engines/scumm/metaengine.cpp:1178
+#, fuzzy
+#| msgid "Skip main menu"
+msgid "Skip / menu back"
+msgstr "Hoppa ??ver huvudmeny"
+
+#: engines/scumm/metaengine.cpp:1194
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1235
+#: engines/scumm/metaengine.cpp:1249
 #, fuzzy
 #| msgid "Move back"
 msgid "Cover / back"
 msgstr "Stega bak??t"
 
-#: engines/scumm/metaengine.cpp:1241
+#: engines/scumm/metaengine.cpp:1255
 #, fuzzy
 #| msgid "Skip walk"
 msgid "Skip / back"
 msgstr "Hoppa ??ver g??ng"
 
-#: engines/scumm/metaengine.cpp:1248
+#: engines/scumm/metaengine.cpp:1262
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu"
@@ -17231,6 +17237,11 @@ msgstr ""
 "l??sas. Dessa pussel kan alternativt l??sas med hj??lp av undertexter, om detta "
 "st??ds. Vill du forts??tta spela?"
 
+#, fuzzy
+#~| msgid "Backspace"
+#~ msgid "Back / skip"
+#~ msgstr "Backsteg"
+
 #, fuzzy
 #~| msgid "No"
 #~ msgid "N"
diff --git a/po/tr.po b/po/tr.po
index f56b28e8f0a..33240fadecb 100644
--- a/po/tr.po
+++ b/po/tr.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 16:27+0000\n"
+"POT-Creation-Date: 2026-06-01 19:59+0000\n"
 "PO-Revision-Date: 2024-10-10 12:33+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Turkish <https://translations.scummvm.org/projects/scummvm/"
@@ -5762,7 +5762,7 @@ msgstr ""
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1183 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr ""
 
@@ -13295,69 +13295,74 @@ msgstr ""
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1182
+#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1196
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1187
+#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1201
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1192
+#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1206
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1197
+#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1211
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
+#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1216
 msgid "Stick up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1209
+#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
 #, fuzzy
 msgid "Stick down"
 msgstr "A??a????"
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1216
+#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1230
 #, fuzzy
 msgid "Stick left"
 msgstr "Sa??"
 
-#: engines/scumm/metaengine.cpp:1158 engines/scumm/metaengine.cpp:1223
+#: engines/scumm/metaengine.cpp:1161 engines/scumm/metaengine.cpp:1237
 #, fuzzy
 msgid "Stick right"
 msgstr "Sa??"
 
-#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1230
+#: engines/scumm/metaengine.cpp:1168 engines/scumm/metaengine.cpp:1244
 #, fuzzy
 msgid "Fire / select"
 msgstr "Sol"
 
-#: engines/scumm/metaengine.cpp:1169
+#: engines/scumm/metaengine.cpp:1173
 #, fuzzy
 #| msgid "Backend"
-msgid "Back / skip"
+msgid "Menu back"
 msgstr "Altyap??"
 
-#: engines/scumm/metaengine.cpp:1180
+#: engines/scumm/metaengine.cpp:1178
+#, fuzzy
+msgid "Skip / menu back"
+msgstr "??ptal"
+
+#: engines/scumm/metaengine.cpp:1194
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1235
+#: engines/scumm/metaengine.cpp:1249
 #, fuzzy
 #| msgid "Backend"
 msgid "Cover / back"
 msgstr "Altyap??"
 
-#: engines/scumm/metaengine.cpp:1241
+#: engines/scumm/metaengine.cpp:1255
 #, fuzzy
 #| msgid "Show hidden files"
 msgid "Skip / back"
 msgstr "Gizli dosyalar?? g??ster"
 
-#: engines/scumm/metaengine.cpp:1248
+#: engines/scumm/metaengine.cpp:1262
 #, fuzzy
 msgid "Skip / menu"
 msgstr "??ptal"
@@ -17089,6 +17094,11 @@ msgid ""
 "supported. Continue launching game?"
 msgstr ""
 
+#, fuzzy
+#~| msgid "Backend"
+#~ msgid "Back / skip"
+#~ msgstr "Altyap??"
+
 #, fuzzy
 #~| msgid "No"
 #~ msgid "N"
diff --git a/po/uk_UA.po b/po/uk_UA.po
index fd2796a5a99..b37e891eed6 100644
--- a/po/uk_UA.po
+++ b/po/uk_UA.po
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.9.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 16:27+0000\n"
+"POT-Creation-Date: 2026-06-01 19:59+0000\n"
 "PO-Revision-Date: 2026-05-06 08:54+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Ukrainian <https://translations.scummvm.org/projects/scummvm/"
@@ -6095,7 +6095,7 @@ msgstr "R2"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1183 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "????????"
 
@@ -13544,75 +13544,81 @@ msgstr "?????? ???????????????????? ?????? ???? ??????????????"
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1182
+#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1196
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1187
+#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1201
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1192
+#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1206
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1197
+#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1211
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
+#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1216
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "??????????????????"
 
-#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1209
+#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "?????????????????????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1216
+#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1230
 #, fuzzy
 #| msgid "Slide left"
 msgid "Stick left"
 msgstr "???????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1158 engines/scumm/metaengine.cpp:1223
+#: engines/scumm/metaengine.cpp:1161 engines/scumm/metaengine.cpp:1237
 #, fuzzy
 #| msgid "Slide right"
 msgid "Stick right"
 msgstr "???????? ??????????????"
 
-#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1230
+#: engines/scumm/metaengine.cpp:1168 engines/scumm/metaengine.cpp:1244
 #, fuzzy
 #| msgid "Move / Select"
 msgid "Fire / select"
 msgstr "???????????????? / ??????????????"
 
-#: engines/scumm/metaengine.cpp:1169
+#: engines/scumm/metaengine.cpp:1173
 #, fuzzy
-#| msgid "Backspace"
-msgid "Back / skip"
-msgstr "Backspace"
+#| msgid "Move back"
+msgid "Menu back"
+msgstr "???????????????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1180
+#: engines/scumm/metaengine.cpp:1178
+#, fuzzy
+#| msgid "Skip main menu"
+msgid "Skip / menu back"
+msgstr "???????????????????? ?????????????? ????????"
+
+#: engines/scumm/metaengine.cpp:1194
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1235
+#: engines/scumm/metaengine.cpp:1249
 #, fuzzy
 #| msgid "Move back"
 msgid "Cover / back"
 msgstr "???????????????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1241
+#: engines/scumm/metaengine.cpp:1255
 #, fuzzy
 #| msgid "Skip walk"
 msgid "Skip / back"
 msgstr "???????????????????? ??????????????"
 
-#: engines/scumm/metaengine.cpp:1248
+#: engines/scumm/metaengine.cpp:1262
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu"
@@ -17145,6 +17151,11 @@ msgstr ""
 "??????????. ???? ?????????????????????? ?????????? ?????????? ????????'?????????? ???? ?????????????????? ??????????????????, ???????? "
 "???????? ??????????????????????????. ???????????????????? ???????????? ???????"
 
+#, fuzzy
+#~| msgid "Backspace"
+#~ msgid "Back / skip"
+#~ msgstr "Backspace"
+
 #, fuzzy
 #~| msgid "No"
 #~ msgid "N"
diff --git a/po/zh.po b/po/zh.po
index df0d00a1345..42752c952f8 100644
--- a/po/zh.po
+++ b/po/zh.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 16:27+0000\n"
+"POT-Creation-Date: 2026-06-01 19:59+0000\n"
 "PO-Revision-Date: 2024-05-14 12:42+0000\n"
 "Last-Translator: ?????? <clonewith at qq.com>\n"
 "Language-Team: Chinese <https://translations.scummvm.org/projects/scummvm/"
@@ -5543,7 +5543,7 @@ msgstr ""
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1183 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr ""
 
@@ -13119,69 +13119,75 @@ msgstr ""
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1182
+#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1196
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1187
+#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1201
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1192
+#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1206
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1197
+#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1211
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
+#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1216
 msgid "Stick up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1209
+#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
 #, fuzzy
 msgid "Stick down"
 msgstr "??????"
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1216
+#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1230
 #, fuzzy
 msgid "Stick left"
 msgstr "??????"
 
-#: engines/scumm/metaengine.cpp:1158 engines/scumm/metaengine.cpp:1223
+#: engines/scumm/metaengine.cpp:1161 engines/scumm/metaengine.cpp:1237
 #, fuzzy
 msgid "Stick right"
 msgstr "??????"
 
-#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1230
+#: engines/scumm/metaengine.cpp:1168 engines/scumm/metaengine.cpp:1244
 #, fuzzy
 msgid "Fire / select"
 msgstr "??????"
 
-#: engines/scumm/metaengine.cpp:1169
+#: engines/scumm/metaengine.cpp:1173
 #, fuzzy
-#| msgid "Backspace"
-msgid "Back / skip"
-msgstr "??????"
+#| msgid "Backend"
+msgid "Menu back"
+msgstr "??????"
+
+#: engines/scumm/metaengine.cpp:1178
+#, fuzzy
+#| msgid "Skip autosave"
+msgid "Skip / menu back"
+msgstr "??????????????????"
 
-#: engines/scumm/metaengine.cpp:1180
+#: engines/scumm/metaengine.cpp:1194
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1235
+#: engines/scumm/metaengine.cpp:1249
 #, fuzzy
 #| msgid "Backend"
 msgid "Cover / back"
 msgstr "??????"
 
-#: engines/scumm/metaengine.cpp:1241
+#: engines/scumm/metaengine.cpp:1255
 #, fuzzy
 #| msgid "Show hidden files"
 msgid "Skip / back"
 msgstr "?????????????????????"
 
-#: engines/scumm/metaengine.cpp:1248
+#: engines/scumm/metaengine.cpp:1262
 #, fuzzy
 #| msgid "Skip autosave"
 msgid "Skip / menu"
@@ -16977,6 +16983,11 @@ msgid ""
 "supported. Continue launching game?"
 msgstr ""
 
+#, fuzzy
+#~| msgid "Backspace"
+#~ msgid "Back / skip"
+#~ msgstr "??????"
+
 #, fuzzy
 #~| msgid "No"
 #~ msgid "N"
diff --git a/po/zh_Hans.po b/po/zh_Hans.po
index 4b0015f64f2..edf32c38e60 100644
--- a/po/zh_Hans.po
+++ b/po/zh_Hans.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.9.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 16:27+0000\n"
+"POT-Creation-Date: 2026-06-01 19:59+0000\n"
 "PO-Revision-Date: 2024-12-01 22:18+0000\n"
 "Last-Translator: ?????? <clonewith at qq.com>\n"
 "Language-Team: Chinese (Simplified Han script) <https://"
@@ -6179,7 +6179,7 @@ msgstr "R2"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1183 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "??????"
 
@@ -14094,75 +14094,81 @@ msgstr "???????????????????????????"
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1182
+#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1196
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1187
+#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1201
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1192
+#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1206
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1197
+#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1211
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
+#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1216
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "??????"
 
-#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1209
+#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "????????????"
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1216
+#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1230
 #, fuzzy
 #| msgid "Slide Left"
 msgid "Stick left"
 msgstr "????????????"
 
-#: engines/scumm/metaengine.cpp:1158 engines/scumm/metaengine.cpp:1223
+#: engines/scumm/metaengine.cpp:1161 engines/scumm/metaengine.cpp:1237
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick right"
 msgstr "????????????"
 
-#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1230
+#: engines/scumm/metaengine.cpp:1168 engines/scumm/metaengine.cpp:1244
 #, fuzzy
 #| msgid "Move left"
 msgid "Fire / select"
 msgstr "????????????"
 
-#: engines/scumm/metaengine.cpp:1169
+#: engines/scumm/metaengine.cpp:1173
 #, fuzzy
-#| msgid "Backspace"
-msgid "Back / skip"
-msgstr "??????"
+#| msgid "Move Back"
+msgid "Menu back"
+msgstr "????????????"
+
+#: engines/scumm/metaengine.cpp:1178
+#, fuzzy
+#| msgid "Skip main menu"
+msgid "Skip / menu back"
+msgstr "???????????????"
 
-#: engines/scumm/metaengine.cpp:1180
+#: engines/scumm/metaengine.cpp:1194
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1235
+#: engines/scumm/metaengine.cpp:1249
 #, fuzzy
 #| msgid "Move Back"
 msgid "Cover / back"
 msgstr "????????????"
 
-#: engines/scumm/metaengine.cpp:1241
+#: engines/scumm/metaengine.cpp:1255
 #, fuzzy
 #| msgid "Skip dialogue"
 msgid "Skip / back"
 msgstr "????????????"
 
-#: engines/scumm/metaengine.cpp:1248
+#: engines/scumm/metaengine.cpp:1262
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu"
@@ -18180,6 +18186,11 @@ msgid ""
 "supported. Continue launching game?"
 msgstr ""
 
+#, fuzzy
+#~| msgid "Backspace"
+#~ msgid "Back / skip"
+#~ msgstr "??????"
+
 #, fuzzy
 #~| msgid "No"
 #~ msgid "N"
diff --git a/po/zh_Hant.po b/po/zh_Hant.po
index 86089e77ef8..f355f3e614c 100644
--- a/po/zh_Hant.po
+++ b/po/zh_Hant.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.9.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 16:27+0000\n"
+"POT-Creation-Date: 2026-06-01 19:59+0000\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: Automatically generated\n"
 "Language-Team: none\n"
@@ -5435,7 +5435,7 @@ msgstr ""
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1183 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr ""
 
@@ -12422,59 +12422,63 @@ msgstr ""
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1182
+#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1196
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1187
+#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1201
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1192
+#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1206
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1197
+#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1211
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1202
+#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1216
 msgid "Stick up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1146 engines/scumm/metaengine.cpp:1209
+#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
 msgid "Stick down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1216
+#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1230
 msgid "Stick left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1158 engines/scumm/metaengine.cpp:1223
+#: engines/scumm/metaengine.cpp:1161 engines/scumm/metaengine.cpp:1237
 msgid "Stick right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1230
+#: engines/scumm/metaengine.cpp:1168 engines/scumm/metaengine.cpp:1244
 msgid "Fire / select"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1169
-msgid "Back / skip"
+#: engines/scumm/metaengine.cpp:1173
+msgid "Menu back"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1180
+#: engines/scumm/metaengine.cpp:1178
+msgid "Skip / menu back"
+msgstr ""
+
+#: engines/scumm/metaengine.cpp:1194
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1235
+#: engines/scumm/metaengine.cpp:1249
 msgid "Cover / back"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1241
+#: engines/scumm/metaengine.cpp:1255
 msgid "Skip / back"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1248
+#: engines/scumm/metaengine.cpp:1262
 msgid "Skip / menu"
 msgstr ""
 



From noreply at scummvm.org  Mon Jun  1 21:39:04 2026
From: noreply at scummvm.org (neuromancer)
Date: Mon,  1 Jun 2026 21:39:04 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 ec9b91d1e9634f6115de7e474d119de410a4635e
Message-ID: <20260601213904.7C3B3700591@ron.scummvm.net>

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:
361a7b6f5c SCUMM: RA2: show virtual keyboard when entering text
ec9b91d1e9 SCUMM: RA1: option to play all the levels


Commit: 361a7b6f5c2926b00ce7458a3f76bf49ce15696e
    https://github.com/scummvm/scummvm/commit/361a7b6f5c2926b00ce7458a3f76bf49ce15696e
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-01T23:38:46+02:00

Commit Message:
SCUMM: RA2: show virtual keyboard when entering text

Changed paths:
    engines/scumm/insane/rebel2/menu.cpp
    engines/scumm/insane/rebel2/rebel.cpp
    engines/scumm/insane/rebel2/rebel.h


diff --git a/engines/scumm/insane/rebel2/menu.cpp b/engines/scumm/insane/rebel2/menu.cpp
index 5a7221e3ad1..20bd7ef213c 100644
--- a/engines/scumm/insane/rebel2/menu.cpp
+++ b/engines/scumm/insane/rebel2/menu.cpp
@@ -50,6 +50,35 @@ void InsaneRebel2::resetMenu() {
 	_menuInactivityTimer = 0;
 	_menuRepeatDelay = 0;
 	_menuSelectionConfirmed = false;
+	setVirtualKeyboardVisible(false);
+}
+
+bool InsaneRebel2::isMenuTextInputActive() const {
+	if (!_menuInputActive)
+		return false;
+
+	if (_gameState == kStatePilotSelect && _pilotMenuMode == kPilotModeNameInput)
+		return true;
+
+	return _gameState == kStateChapterSelect &&
+	       _chapterSelection >= 0 &&
+	       _chapterSelection < 16 &&
+	       !_chapterUnlocked[_chapterSelection];
+}
+
+void InsaneRebel2::setVirtualKeyboardVisible(bool visible) {
+	if (!_vm->_system->hasFeature(OSystem::kFeatureVirtualKeyboard))
+		return;
+
+	if (_virtualKeyboardActive == visible)
+		return;
+
+	_vm->_system->setFeatureState(OSystem::kFeatureVirtualKeyboard, visible);
+	_virtualKeyboardActive = visible;
+}
+
+void InsaneRebel2::updateMenuVirtualKeyboard() {
+	setVirtualKeyboardVisible(isMenuTextInputActive());
 }
 
 // unlockAllChapters -- Debug mode unlock (FUN_00415CF8 lines 60-71, DAT_0047ab34=='d').
@@ -716,6 +745,7 @@ int InsaneRebel2::runChapterSelect() {
 	_passwordInput = "";
 	_menuRepeatDelay = 0;
 	_gameState = kStateChapterSelect;
+	updateMenuVirtualKeyboard();
 
 	SmushPlayer *splayer = ((ScummEngine_v7 *)_vm)->_splayer;
 
@@ -753,6 +783,7 @@ int InsaneRebel2::runChapterSelect() {
 		splayer->play("OPEN/O_LEVEL.SAN", 12);
 
 		if (_vm->shouldQuit()) {
+			setVirtualKeyboardVisible(false);
 			_menuInputActive = false;
 			return kChapterSelectQuit;
 		}
@@ -771,6 +802,7 @@ int InsaneRebel2::runChapterSelect() {
 		if (_chapterSelection == 16) {
 			// BACK selected (index 16 = 17th item)
 			debug("Rebel2: BACK to main menu selected");
+			setVirtualKeyboardVisible(false);
 			_menuInputActive = false;
 			return kChapterSelectBack;
 		}
@@ -780,6 +812,7 @@ int InsaneRebel2::runChapterSelect() {
 				// Unlocked chapter ??? play it
 				_selectedChapter = _chapterSelection;
 				debug("Rebel2: Chapter %d selected (unlocked)", _selectedChapter + 1);
+				setVirtualKeyboardVisible(false);
 				_menuInputActive = false;
 				return kChapterSelectPlay;
 			}
@@ -801,6 +834,7 @@ int InsaneRebel2::runChapterSelect() {
 					// Update iactBit for video preview (original jumps to LAB_00415d88)
 					setBit(16 - _chapterSelection);
 					_passwordInput.clear();
+					updateMenuVirtualKeyboard();
 					debug("Rebel2: Chapter %d unlocked via password", _chapterSelection + 1);
 					continue;  // Re-render with updated unlock state
 				}
@@ -811,6 +845,7 @@ int InsaneRebel2::runChapterSelect() {
 		}
 	}
 
+	setVirtualKeyboardVisible(false);
 	_menuInputActive = false;
 	return kChapterSelectQuit;
 }
@@ -838,6 +873,7 @@ int InsaneRebel2::processChapterSelectInput() {
 				_passwordInput.clear();
 				// Update preview offset (FUN_00425170: Y = selected * -50 + 75)
 				_previewOffsetY = _chapterSelection * -50 + 75;
+				updateMenuVirtualKeyboard();
 				debug("ChapterSelect: Selection changed to %d (UP) offsetY=%d", _chapterSelection, _previewOffsetY);
 				break;
 
@@ -850,6 +886,7 @@ int InsaneRebel2::processChapterSelectInput() {
 				_passwordInput.clear();
 				// Update preview offset (FUN_00425170: Y = selected * -50 + 75)
 				_previewOffsetY = _chapterSelection * -50 + 75;
+				updateMenuVirtualKeyboard();
 				debug("ChapterSelect: Selection changed to %d (DOWN) offsetY=%d", _chapterSelection, _previewOffsetY);
 				break;
 
@@ -863,6 +900,7 @@ int InsaneRebel2::processChapterSelectInput() {
 
 			case Common::KEYCODE_ESCAPE:
 				// ESC = Back to main menu (same as selecting BACK)
+				setVirtualKeyboardVisible(false);
 				result = 16;  // BACK index
 				debug("ChapterSelect: ESC pressed - back to menu");
 				break;
@@ -897,6 +935,7 @@ int InsaneRebel2::processChapterSelectInput() {
 					if (event.mouse.y >= itemY - 1 && event.mouse.y < itemY + 9) {
 						_chapterSelection = i;
 						_previewOffsetY = _chapterSelection * -50 + 75;
+						updateMenuVirtualKeyboard();
 						result = _chapterSelection;
 						debug("ChapterSelect: Item %d selected (mouse)", _chapterSelection);
 						break;
@@ -918,6 +957,7 @@ int InsaneRebel2::processChapterSelectInput() {
 						if (i != _chapterSelection) {
 							_chapterSelection = i;
 							_previewOffsetY = _chapterSelection * -50 + 75;
+							updateMenuVirtualKeyboard();
 							debug(5, "ChapterSelect: Hover changed to %d", _chapterSelection);
 						}
 						break;
@@ -1183,6 +1223,7 @@ int InsaneRebel2::runLevelSelect() {
 	_pilotMenuMode = kPilotModeSelect;
 	_pilotNameInput = "";
 	_pilotEditIndex = -1;
+	updateMenuVirtualKeyboard();
 
 	SmushPlayer *splayer = ((ScummEngine_v7 *)_vm)->_splayer;
 
@@ -1195,6 +1236,7 @@ int InsaneRebel2::runLevelSelect() {
 		splayer->play(menuVideo.c_str(), 12);
 
 		if (_vm->shouldQuit()) {
+			setVirtualKeyboardVisible(false);
 			_menuInputActive = false;
 			return kLevelSelectQuit;
 		}
@@ -1233,6 +1275,7 @@ int InsaneRebel2::runLevelSelect() {
 				Common::strlcpy(_pilots[_pilotEditIndex].name, _pilotNameInput.c_str(),
 				                sizeof(_pilots[_pilotEditIndex].name));
 			}
+			setVirtualKeyboardVisible(false);
 			_pilotMenuMode = kPilotModeDifficulty;
 			_gameState = kStateDifficultySelect;
 			_difficultySelection = 2;
@@ -1280,6 +1323,7 @@ int InsaneRebel2::runLevelSelect() {
 
 			debug("Rebel2: Pilot '%s' selected (slot %d, difficulty %d)",
 			      _pilots[_activePilot].name, _activePilot, _difficulty);
+			setVirtualKeyboardVisible(false);
 			_menuInputActive = false;
 			return kLevelSelectPlay;
 
@@ -1291,6 +1335,7 @@ int InsaneRebel2::runLevelSelect() {
 				_pilotNameInput = "";
 				_pilotMenuMode = kPilotModeNameInput;
 				_levelItemCount = _numPilots + 4;
+				updateMenuVirtualKeyboard();
 				debug("Rebel2: NEW PILOT - entering name for slot %d", newIdx);
 			}
 			continue;
@@ -1317,11 +1362,13 @@ int InsaneRebel2::runLevelSelect() {
 
 		} else if (_levelSelection == _numPilots + 3) {
 			// RETURN TO MAIN MENU
+			setVirtualKeyboardVisible(false);
 			_menuInputActive = false;
 			return kLevelSelectBack;
 		}
 	}
 
+	setVirtualKeyboardVisible(false);
 	_menuInputActive = false;
 	return kLevelSelectQuit;
 }
@@ -1343,6 +1390,7 @@ int InsaneRebel2::processLevelSelectInput() {
 				    event.kbd.keycode == Common::KEYCODE_KP_ENTER) {
 					// Confirm name ??? signal back to runLevelSelect()
 					if (_pilotNameInput.size() > 0) {
+						setVirtualKeyboardVisible(false);
 						_menuSelectionConfirmed = true;
 						_vm->_smushVideoShouldFinish = true;
 						debug("PilotName: confirmed '%s'", _pilotNameInput.c_str());
@@ -1354,6 +1402,7 @@ int InsaneRebel2::processLevelSelectInput() {
 					}
 					_pilotMenuMode = kPilotModeSelect;
 					_levelItemCount = _numPilots + 4;
+					updateMenuVirtualKeyboard();
 					debug("PilotName: cancelled");
 				} else if (event.kbd.keycode == Common::KEYCODE_BACKSPACE) {
 					// Backspace ??? remove last character
@@ -1378,6 +1427,7 @@ int InsaneRebel2::processLevelSelectInput() {
 				}
 				_pilotMenuMode = kPilotModeSelect;
 				_levelItemCount = _numPilots + 4;
+				updateMenuVirtualKeyboard();
 			}
 		}
 		return -1;
diff --git a/engines/scumm/insane/rebel2/rebel.cpp b/engines/scumm/insane/rebel2/rebel.cpp
index 501186e152b..bfec3000e6a 100644
--- a/engines/scumm/insane/rebel2/rebel.cpp
+++ b/engines/scumm/insane/rebel2/rebel.cpp
@@ -505,6 +505,7 @@ InsaneRebel2::InsaneRebel2(ScummEngine_v7 *scumm) {
 
 	// Initialize menu input capture system
 	_menuInputActive = false;
+	_virtualKeyboardActive = false;
 
 	// Analog stick state for gamepad aiming (mirrors RA1's analog model).
 	// Ingested from EVENT_CUSTOM_BACKEND_ACTION_AXIS, read with a deadzone and
@@ -526,6 +527,8 @@ InsaneRebel2::InsaneRebel2(ScummEngine_v7 *scumm) {
 
 
 InsaneRebel2::~InsaneRebel2() {
+	setVirtualKeyboardVisible(false);
+
 	// Unregister EventObserver
 	_vm->_system->getEventManager()->getEventDispatcher()->unregisterObserver(this);
 
@@ -877,6 +880,11 @@ bool InsaneRebel2::notifyEvent(const Common::Event &event) {
 		// consumed (and returned true for) the cases they handle.
 	}
 
+	if (_menuInputActive && isMenuTextInputActive() && event.type == Common::EVENT_KEYDOWN) {
+		_menuEventQueue.push(event);
+		return true;
+	}
+
 	const bool gameplayMenuTrigger = (event.type == Common::EVENT_MAINMENU) ||
 		(event.type == Common::EVENT_KEYDOWN && event.kbd.keycode == Common::KEYCODE_ESCAPE);
 	if (gameplayMenuTrigger && _gameState == kStateGameplay && _lastGameplayMenuCloseTime != 0) {
diff --git a/engines/scumm/insane/rebel2/rebel.h b/engines/scumm/insane/rebel2/rebel.h
index 6f24d48a69f..0477679bd45 100644
--- a/engines/scumm/insane/rebel2/rebel.h
+++ b/engines/scumm/insane/rebel2/rebel.h
@@ -57,6 +57,7 @@ public:
 	// Menu input event queue - events are captured by notifyEvent() and processed by processMenuInput()
 	Common::Queue<Common::Event> _menuEventQueue;
 	bool _menuInputActive;  // True when we're capturing menu input events
+	bool _virtualKeyboardActive;
 
 	// ---------------------------------------------------------------------------
 	// Menu System
@@ -125,6 +126,9 @@ public:
 
 	// Reset menu state for fresh start
 	void resetMenu();
+	bool isMenuTextInputActive() const;
+	void setVirtualKeyboardVisible(bool visible);
+	void updateMenuVirtualKeyboard();
 
 	// ---------------------------------------------------------------------------
 	// Chapter Selection Screen (FUN_00415CF8)


Commit: ec9b91d1e9634f6115de7e474d119de410a4635e
    https://github.com/scummvm/scummvm/commit/ec9b91d1e9634f6115de7e474d119de410a4635e
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-01T23:38:46+02:00

Commit Message:
SCUMM: RA1: option to play all the levels

Changed paths:
    engines/scumm/detection.h
    engines/scumm/detection_tables.h
    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/detection.h b/engines/scumm/detection.h
index db1da1ba8df..cd6dbfa9d43 100644
--- a/engines/scumm/detection.h
+++ b/engines/scumm/detection.h
@@ -40,6 +40,7 @@ namespace Scumm {
 #define GAMEOPTION_TTS                                       GUIO_GAMEOPTIONS9
 #define GAMEOPTION_REBEL2_HIRES                              GUIO_GAMEOPTIONS10
 #define GAMEOPTION_REBEL2_UNLOCK_ALL                         GUIO_GAMEOPTIONS11
+#define GAMEOPTION_REBEL1_UNLOCK_ALL                         GUIO_GAMEOPTIONS12
 
 /**
  * Descriptor of a specific SCUMM game. Used internally to store
diff --git a/engines/scumm/detection_tables.h b/engines/scumm/detection_tables.h
index b1bcb6e8b46..9c2260772c4 100644
--- a/engines/scumm/detection_tables.h
+++ b/engines/scumm/detection_tables.h
@@ -228,7 +228,7 @@ static const GameSettings gameVariantsTable[] = {
 	{"ft",   "Remastered", 0, GID_FT,  7, 0, MDT_NONE, GF_DOUBLEFINE_PAK, UNK, GUIO5(GUIO_NOMIDI, GAMEOPTION_ENHANCEMENTS, GAMEOPTION_ORIGINALGUI, GAMEOPTION_LOWLATENCYAUDIO, GAMEOPTION_TTS)},
 	{"ft",   "Demo", 0, GID_FT,  7, 0, MDT_NONE, GF_DEMO, UNK, GUIO4(GUIO_NOMIDI, GAMEOPTION_ORIGINALGUI, GAMEOPTION_LOWLATENCYAUDIO, GAMEOPTION_TTS)},
 
-	{"rebel1", "", 0, GID_REBEL1, 7, 0, MDT_NONE, 0, Common::kPlatformDOS, GUIO1(GUIO_NOMIDI)},
+	{"rebel1", "", 0, GID_REBEL1, 7, 0, MDT_NONE, 0, Common::kPlatformDOS, GUIO2(GUIO_NOMIDI, GAMEOPTION_REBEL1_UNLOCK_ALL)},
 
 	{"rebel2", "", 0, GID_REBEL2, 7, 0, MDT_NONE, 0, Common::kPlatformDOS, GUIO3(GUIO_NOMIDI, GAMEOPTION_REBEL2_HIRES, GAMEOPTION_REBEL2_UNLOCK_ALL)},
 	{"rebel2", "Demo", 0, GID_REBEL2, 7, 0, MDT_NONE, GF_DEMO, Common::kPlatformDOS, GUIO3(GUIO_NOMIDI, GAMEOPTION_REBEL2_HIRES, GAMEOPTION_REBEL2_UNLOCK_ALL)},
diff --git a/engines/scumm/insane/rebel1/menu.cpp b/engines/scumm/insane/rebel1/menu.cpp
index 68bbd4aae9d..2de991ea72b 100644
--- a/engines/scumm/insane/rebel1/menu.cpp
+++ b/engines/scumm/insane/rebel1/menu.cpp
@@ -262,6 +262,18 @@ static void drawRebel1MenuFrame(byte *dst, int pitch, int width, int height, int
 	}
 }
 
+int InsaneRebel1::getMainMenuItemCount() const {
+	return _unlockAllLevels ? kRA1MainMenuItemCount : kRA1MainMenuItemCount - 1;
+}
+
+int InsaneRebel1::getMainMenuResultForSelection(int selection) const {
+	if (selection < 0 || selection >= getMainMenuItemCount())
+		return kRA1MainMenuItemCount;
+	if (_unlockAllLevels || selection < 3)
+		return selection + 1;
+	return selection + 2;
+}
+
 void InsaneRebel1::beginTextEntry(bool passcodeMode) {
 	_textEntryActive = true;
 	_textEntryPasscodeMode = passcodeMode;
@@ -456,15 +468,17 @@ bool InsaneRebel1::handleMenuCommand(RA1MenuCommand command) {
 		}
 	}
 
+	const int mainMenuItemCount = getMainMenuItemCount();
+
 	switch (command) {
 	case kRA1MenuCommandUp:
-		_menuSelection = (_menuSelection + kRA1MainMenuItemCount - 1) % kRA1MainMenuItemCount;
+		_menuSelection = (_menuSelection + mainMenuItemCount - 1) % mainMenuItemCount;
 		return true;
 	case kRA1MenuCommandDown:
-		_menuSelection = (_menuSelection + 1) % kRA1MainMenuItemCount;
+		_menuSelection = (_menuSelection + 1) % mainMenuItemCount;
 		return true;
 	case kRA1MenuCommandCancel:
-		_menuSelection = kRA1MainMenuItemCount - 1;
+		_menuSelection = mainMenuItemCount - 1;
 		// fall through
 	case kRA1MenuCommandAccept:
 		_menuConfirmed = true;
@@ -475,11 +489,15 @@ bool InsaneRebel1::handleMenuCommand(RA1MenuCommand command) {
 	case kRA1MenuCommandSelect3:
 	case kRA1MenuCommandSelect4:
 	case kRA1MenuCommandSelect5:
-	case kRA1MenuCommandSelect6:
-		_menuSelection = command - kRA1MenuCommandSelect1;
+	case kRA1MenuCommandSelect6: {
+		const int selection = command - kRA1MenuCommandSelect1;
+		if (selection >= mainMenuItemCount)
+			return false;
+		_menuSelection = selection;
 		_menuConfirmed = true;
 		_vm->_smushVideoShouldFinish = true;
 		return true;
+	}
 	default:
 		return false;
 	}
@@ -563,7 +581,8 @@ bool InsaneRebel1::handleMenuMouse(const Common::Event &event) {
 		}
 	} else {
 		selection = &_menuSelection;
-		for (int i = 0; i < kRA1MainMenuItemCount; i++) {
+		const int mainMenuItemCount = getMainMenuItemCount();
+		for (int i = 0; i < mainMenuItemCount; i++) {
 			const int frameY = (i + 1) * kRA1MenuRowH + kRA1MainMenuFrameYBase;
 			if (mx >= kRA1MenuFrameX && mx < kRA1MenuFrameX + kRA1MenuFrameW &&
 				my >= frameY && my < frameY + kRA1MenuFrameH) {
@@ -962,7 +981,7 @@ void InsaneRebel1::renderLevelSelectOverlay(byte *dst, int pitch, int width, int
 
 void InsaneRebel1::renderMainMenuItems(byte *dst, int pitch, int width, int height) {
 	// --- Main menu ---
-	const char *kMenuItems[kRA1MainMenuItemCount] = {
+	const char *const kMenuItems[kRA1MainMenuItemCount] = {
 		"START NEW GAME",
 		"GAME OPTIONS",
 		"ENTER PASSCODE",
@@ -970,6 +989,15 @@ void InsaneRebel1::renderMainMenuItems(byte *dst, int pitch, int width, int heig
 		"CONTINUE DEMO",
 		"EXIT TO DOS"
 	};
+	const char *const kMenuItemsLocked[kRA1MainMenuItemCount - 1] = {
+		"START NEW GAME",
+		"GAME OPTIONS",
+		"ENTER PASSCODE",
+		"CONTINUE DEMO",
+		"EXIT TO DOS"
+	};
+	const char *const *menuItems = _unlockAllLevels ? kMenuItems : kMenuItemsLocked;
+	const int mainMenuItemCount = getMainMenuItemCount();
 
 	// Center title
 	const int titleW = getFontBankStringWidth("MAIN MENU");
@@ -977,12 +1005,12 @@ void InsaneRebel1::renderMainMenuItems(byte *dst, int pitch, int width, int heig
 	drawMenuTitleText(dst, pitch, width, height, titleX, 30, "MAIN MENU");
 
 	// Draw menu items centered horizontally
-	for (int i = 0; i < kRA1MainMenuItemCount; i++) {
-		const int textW = getMenuTalkTextWidth(kMenuItems[i]);
+	for (int i = 0; i < mainMenuItemCount; i++) {
+		const int textW = getMenuTalkTextWidth(menuItems[i]);
 		const int textX = getRebel1MenuCenteredX(textW);
 		const int y = 0x3c + i * kRA1MenuRowH;
 
-		drawMenuTalkText(dst, pitch, width, height, textX, y, kMenuItems[i]);
+		drawMenuTalkText(dst, pitch, width, height, textX, y, menuItems[i]);
 
 		if (i == _menuSelection)
 			drawRebel1MenuFrame(dst, pitch, width, height,
@@ -1086,7 +1114,7 @@ int InsaneRebel1::runMainMenu() {
 			return kRA1MainMenuItemCount;
 
 		if (_menuConfirmed)
-			return _menuSelection + 1;
+			return getMainMenuResultForSelection(_menuSelection);
 	}
 
 	return kRA1MainMenuItemCount;
@@ -1197,6 +1225,9 @@ void InsaneRebel1::runOptionsMenu() {
 }
 
 int InsaneRebel1::runLevelSelectMenu() {
+	if (!_unlockAllLevels)
+		return 0;
+
 	_levelSelectSel = CLIP(_startLevel - 1, 0, kRA1NumLevels - 1);
 	_levelSelectActive = true;
 
diff --git a/engines/scumm/insane/rebel1/rebel.cpp b/engines/scumm/insane/rebel1/rebel.cpp
index f8efc33a1ea..d4b1a7929bc 100644
--- a/engines/scumm/insane/rebel1/rebel.cpp
+++ b/engines/scumm/insane/rebel1/rebel.cpp
@@ -312,6 +312,7 @@ InsaneRebel1::InsaneRebel1(ScummEngine_v7 *scumm) : Insane(), _vm(scumm) {
 	_hudRenderFlag = 0;
 	_hudDirtyFlag = 0;
 	_maxChapterUnlocked = 0;
+	_unlockAllLevels = ConfMan.getBool("rebel1_unlock_all");
 	_interactiveVideoActive = false;
 	_restoreInteractiveVideoAudioState = false;
 	memset(_savedInteractiveVideoTrackState, 0, sizeof(_savedInteractiveVideoTrackState));
diff --git a/engines/scumm/insane/rebel1/rebel.h b/engines/scumm/insane/rebel1/rebel.h
index 83e8381390c..eab04823b21 100644
--- a/engines/scumm/insane/rebel1/rebel.h
+++ b/engines/scumm/insane/rebel1/rebel.h
@@ -482,6 +482,7 @@ private:
 	byte _hudRenderFlag;         // 0x7600: 0xFF when HUD should render (set by combat mode handlers)
 	byte _hudDirtyFlag;          // 0x7601: 0xFF after HUD redraw (set by renderHUD)
 	int16 _maxChapterUnlocked;   // 0x7730: highest unlocked passcode slot (0=none)
+	bool _unlockAllLevels;       // ScummVM option: expose level select without passcodes
 
 	static const int16 kMaxHealth = 98;
 	static const int16 kDeathTimerInit = 30;
@@ -543,6 +544,8 @@ private:
 	bool handleMenuMouse(const Common::Event &event);
 	bool handleTextEntryAction(ScummAction action);
 	bool handleTextEntryKey(const Common::Event &event);
+	int getMainMenuItemCount() const;
+	int getMainMenuResultForSelection(int selection) const;
 	void playMenuBackground();
 	bool runTextEntryMenuLoop();
 	void beginTextEntry(bool passcodeMode);
@@ -551,7 +554,7 @@ private:
 	const char *getChapterCompletePassword(int passwordIndex) const;
 	bool _menuActive;
 	bool _menuConfirmed;
-	int _menuSelection; // 0..5 maps to return values 1..6
+	int _menuSelection; // Visible main-menu row; mapped to return values by getMainMenuResultForSelection().
 	int _menuFrameCounter;
 
 	// Options submenu state ??? RunGameOptionsMenu (0x14B42)
diff --git a/engines/scumm/metaengine.cpp b/engines/scumm/metaengine.cpp
index d0e91a67b89..7c7a5d0396d 100644
--- a/engines/scumm/metaengine.cpp
+++ b/engines/scumm/metaengine.cpp
@@ -896,6 +896,15 @@ static const ExtraGuiOption enableRebel2UnlockAll = {
 	0
 };
 
+const ExtraGuiOption enableRebel1UnlockAll = {
+	_s("Unlock all levels"),
+	_s("All levels will be available without requiring passwords"),
+	"rebel1_unlock_all",
+	false,
+	0,
+	0
+};
+
 const ExtraGuiOptions ScummMetaEngine::getExtraGuiOptions(const Common::String &target) const {
 	ExtraGuiOptions options;
 	// Query the GUI options
@@ -943,6 +952,9 @@ const ExtraGuiOptions ScummMetaEngine::getExtraGuiOptions(const Common::String &
 	if (target.empty() || guiOptions.contains(GAMEOPTION_REBEL2_UNLOCK_ALL)) {
 		options.push_back(enableRebel2UnlockAll);
 	}
+	if (target.empty() || guiOptions.contains(GAMEOPTION_REBEL1_UNLOCK_ALL)) {
+		options.push_back(enableRebel1UnlockAll);
+	}
 	if (target.empty() || gameid == "comi") {
 		options.push_back(comiObjectLabelsOption);
 



From noreply at scummvm.org  Mon Jun  1 21:39:08 2026
From: noreply at scummvm.org (ScummVM-Translations)
Date: Mon,  1 Jun 2026 21:39:08 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 a5ee7105186fe47c49afc46d2cab4854f6bfdc03
Message-ID: <20260601213908.DAEC8700591@ron.scummvm.net>

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

Summary:
a5ee710518 I18N: Update translations templates


Commit: a5ee7105186fe47c49afc46d2cab4854f6bfdc03
    https://github.com/scummvm/scummvm/commit/a5ee7105186fe47c49afc46d2cab4854f6bfdc03
Author: Weblate (noreply at weblate.org)
Date: 2026-06-01T21:39:04Z

Commit Message:
I18N: Update translations templates

Changed paths:
    po/scummvm.pot


diff --git a/po/scummvm.pot b/po/scummvm.pot
index 215bae7510f..536bfa2e247 100644
--- a/po/scummvm.pot
+++ b/po/scummvm.pot
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2026.2.1git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 19:59+0000\n"
+"POT-Creation-Date: 2026-06-01 21:39+0000\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL at ADDRESS>\n"
 "Language-Team: LANGUAGE <LL at li.org>\n"
@@ -1014,7 +1014,7 @@ msgstr ""
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1076
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1088
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1035,7 +1035,7 @@ msgstr ""
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1043
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1055
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1056,7 +1056,7 @@ msgstr ""
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1056
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1068
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1076,7 +1076,7 @@ msgstr ""
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1063
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1075
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3589,7 +3589,7 @@ msgid "Game not implemented"
 msgstr ""
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
-#: engines/scumm/metaengine.cpp:1026
+#: engines/scumm/metaengine.cpp:1038
 msgid "Default game keymappings"
 msgstr ""
 
@@ -5435,7 +5435,7 @@ msgstr ""
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1183 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1195 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr ""
 
@@ -6796,7 +6796,7 @@ msgstr ""
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1102 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1114 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -7443,7 +7443,7 @@ msgstr ""
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1089
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1101
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -9330,6 +9330,7 @@ msgid "Player ammo will never decrease."
 msgstr ""
 
 #: engines/hypno/metaengine.cpp:70 engines/scumm/metaengine.cpp:891
+#: engines/scumm/metaengine.cpp:900
 msgid "Unlock all levels"
 msgstr ""
 
@@ -12389,96 +12390,96 @@ msgstr ""
 msgid "Run the game in 640x400 high resolution mode instead of 320x200"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:892
+#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:901
 msgid "All levels will be available without requiring passwords"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1037
+#: engines/scumm/metaengine.cpp:1049
 msgid "Down left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1050
+#: engines/scumm/metaengine.cpp:1062
 msgid "Down right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1070
+#: engines/scumm/metaengine.cpp:1082
 msgid "Up left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1083
+#: engines/scumm/metaengine.cpp:1095
 msgid "Up right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1095
+#: engines/scumm/metaengine.cpp:1107
 msgid "Switch weapon"
 msgstr ""
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1109
+#: engines/scumm/metaengine.cpp:1121
 msgid "Win the bike fight cheat"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1118
+#: engines/scumm/metaengine.cpp:1130
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1196
+#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1208
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1201
+#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1213
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1206
+#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1218
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1211
+#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1216
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1228
 msgid "Stick up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
+#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1235
 msgid "Stick down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1230
+#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1242
 msgid "Stick left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1161 engines/scumm/metaengine.cpp:1237
+#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1249
 msgid "Stick right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1168 engines/scumm/metaengine.cpp:1244
+#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1256
 msgid "Fire / select"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1173
+#: engines/scumm/metaengine.cpp:1185
 msgid "Menu back"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1178
+#: engines/scumm/metaengine.cpp:1190
 msgid "Skip / menu back"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1194
+#: engines/scumm/metaengine.cpp:1206
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1249
+#: engines/scumm/metaengine.cpp:1261
 msgid "Cover / back"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1255
+#: engines/scumm/metaengine.cpp:1267
 msgid "Skip / back"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1262
+#: engines/scumm/metaengine.cpp:1274
 msgid "Skip / menu"
 msgstr ""
 



From noreply at scummvm.org  Mon Jun  1 21:39:40 2026
From: noreply at scummvm.org (ScummVM-Translations)
Date: Mon,  1 Jun 2026 21:39:40 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 c6499005845fba8c4823e4795f1c74a25e0d17dc
Message-ID: <20260601213940.1AEE1700591@ron.scummvm.net>

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

Summary:
c649900584 I18N: Update translation files


Commit: c6499005845fba8c4823e4795f1c74a25e0d17dc
    https://github.com/scummvm/scummvm/commit/c6499005845fba8c4823e4795f1c74a25e0d17dc
Author: Weblate (noreply at weblate.org)
Date: 2026-06-01T21:39:21Z

Commit Message:
I18N: Update translation files

Updated by "Update PO files to match POT (msgmerge)" hook in Weblate.

Translation: ScummVM/scummvm
Translate-URL: https://translations.scummvm.org/projects/scummvm/scummvm/

Changed paths:
    po/ar.po
    po/be-tarask.po
    po/be_BY.po
    po/ca_ES.po
    po/cs_CZ.po
    po/da.po
    po/de_DE.po
    po/el.po
    po/es_ES.po
    po/eu.po
    po/fi_FI.po
    po/fr_FR.po
    po/gl_ES.po
    po/he.po
    po/hi.po
    po/hu_HU.po
    po/it_IT.po
    po/ja.po
    po/ka.po
    po/ko.po
    po/nb_NO.po
    po/nl_NL.po
    po/nn_NO.po
    po/pl_PL.po
    po/pt_BR.po
    po/pt_PT.po
    po/ro.po
    po/ru_RU.po
    po/sv_SE.po
    po/tr.po
    po/uk_UA.po
    po/zh.po
    po/zh_Hans.po
    po/zh_Hant.po


diff --git a/po/ar.po b/po/ar.po
index a589349aec0..76c832ef410 100644
--- a/po/ar.po
+++ b/po/ar.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 19:59+0000\n"
+"POT-Creation-Date: 2026-06-01 21:39+0000\n"
 "PO-Revision-Date: 2026-02-24 22:26+0000\n"
 "Last-Translator: Mohamed Shaaban <mohmedbashamedo at hotmail.com>\n"
 "Language-Team: Arabic <https://translations.scummvm.org/projects/scummvm/"
@@ -1060,7 +1060,7 @@ msgstr "??????????????"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1076
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1088
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1081,7 +1081,7 @@ msgstr "????????"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1043
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1055
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1102,7 +1102,7 @@ msgstr "????????"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1056
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1068
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1122,7 +1122,7 @@ msgstr "????????"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1063
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1075
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3912,7 +3912,7 @@ msgid "Game not implemented"
 msgstr "?????????? ???????????? ?????? ??????????"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
-#: engines/scumm/metaengine.cpp:1026
+#: engines/scumm/metaengine.cpp:1038
 #, fuzzy
 #| msgid "Default game keymap"
 msgid "Default game keymappings"
@@ -5935,7 +5935,7 @@ msgstr "R2"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1183 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1195 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "??????????????"
 
@@ -7459,7 +7459,7 @@ msgstr "?????????? ????????????"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1102 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1114 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -8225,7 +8225,7 @@ msgstr "??????????????"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1089
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1101
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -10538,6 +10538,7 @@ msgid "Player ammo will never decrease."
 msgstr "???? ?????????? ?????????? ???????????? ??????????."
 
 #: engines/hypno/metaengine.cpp:70 engines/scumm/metaengine.cpp:891
+#: engines/scumm/metaengine.cpp:900
 msgid "Unlock all levels"
 msgstr "???????? ???????? ??????????????????"
 
@@ -14168,124 +14169,124 @@ msgstr "?????????? ?????? ??????????"
 msgid "Run the game in 640x400 high resolution mode instead of 320x200"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:892
+#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:901
 #, fuzzy
 #| msgid "All levels will be available to play."
 msgid "All levels will be available without requiring passwords"
 msgstr "?????????? ???????? ?????????????????? ?????????? ????????."
 
-#: engines/scumm/metaengine.cpp:1037
+#: engines/scumm/metaengine.cpp:1049
 #, fuzzy
 #| msgid "Down Left"
 msgid "Down left"
 msgstr "???????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1050
+#: engines/scumm/metaengine.cpp:1062
 #, fuzzy
 #| msgid "Down Right"
 msgid "Down right"
 msgstr "???????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1070
+#: engines/scumm/metaengine.cpp:1082
 #, fuzzy
 #| msgid "Upper left item"
 msgid "Up left"
 msgstr "???????????? ???????????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1083
+#: engines/scumm/metaengine.cpp:1095
 #, fuzzy
 #| msgid "Up Right"
 msgid "Up right"
 msgstr "???????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1095
+#: engines/scumm/metaengine.cpp:1107
 msgid "Switch weapon"
 msgstr "?????????? ????????????"
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1109
+#: engines/scumm/metaengine.cpp:1121
 msgid "Win the bike fight cheat"
 msgstr "?????? ???????? ???? ?????????? ??????????????"
 
-#: engines/scumm/metaengine.cpp:1118
+#: engines/scumm/metaengine.cpp:1130
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1196
+#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1208
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1201
+#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1213
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1206
+#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1218
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1211
+#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1216
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1228
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "??????????"
 
-#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
+#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1235
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "?????? ???????????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1230
+#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1242
 #, fuzzy
 #| msgid "Slide Left"
 msgid "Stick left"
 msgstr "?????????????? ?????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1161 engines/scumm/metaengine.cpp:1237
+#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1249
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick right"
 msgstr "???????????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1168 engines/scumm/metaengine.cpp:1244
+#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1256
 #, fuzzy
 #| msgid "Move left"
 msgid "Fire / select"
 msgstr "???????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1173
+#: engines/scumm/metaengine.cpp:1185
 #, fuzzy
 #| msgid "Move Back"
 msgid "Menu back"
 msgstr "???????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1178
+#: engines/scumm/metaengine.cpp:1190
 #, fuzzy
 #| msgid "Open main menu"
 msgid "Skip / menu back"
 msgstr "???????? ?????????????? ????????????????"
 
-#: engines/scumm/metaengine.cpp:1194
+#: engines/scumm/metaengine.cpp:1206
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1249
+#: engines/scumm/metaengine.cpp:1261
 #, fuzzy
 #| msgid "Move Back"
 msgid "Cover / back"
 msgstr "???????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1255
+#: engines/scumm/metaengine.cpp:1267
 #, fuzzy
 #| msgid "Skip dialogue"
 msgid "Skip / back"
 msgstr "???????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1262
+#: engines/scumm/metaengine.cpp:1274
 #, fuzzy
 #| msgid "Open main menu"
 msgid "Skip / menu"
diff --git a/po/be-tarask.po b/po/be-tarask.po
index b31f3712274..fdcfa60956d 100644
--- a/po/be-tarask.po
+++ b/po/be-tarask.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 19:59+0000\n"
+"POT-Creation-Date: 2026-06-01 21:39+0000\n"
 "PO-Revision-Date: 2023-02-26 15:01+0000\n"
 "Last-Translator: Kastu?? K. <oakum2334 at gmail.com>\n"
 "Language-Team: Belarusian (Tara??kievica) <https://translations.scummvm.org/"
@@ -1071,7 +1071,7 @@ msgstr "????????????????????"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1076
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1088
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1092,7 +1092,7 @@ msgstr "??????????"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1043
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1055
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1113,7 +1113,7 @@ msgstr "????????"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1056
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1068
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1133,7 +1133,7 @@ msgstr "????????????"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1063
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1075
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3868,7 +3868,7 @@ msgid "Game not implemented"
 msgstr "ID ???????????? ???? ??????????????????????????????"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
-#: engines/scumm/metaengine.cpp:1026
+#: engines/scumm/metaengine.cpp:1038
 #, fuzzy
 #| msgid "Default game keymap"
 msgid "Default game keymappings"
@@ -5868,7 +5868,7 @@ msgstr "R2"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1183 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1195 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "????????"
 
@@ -7401,7 +7401,7 @@ msgstr "???????????????????? ?????????? ??????"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1102 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1114 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -8172,7 +8172,7 @@ msgstr "????????????????????"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1089
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1101
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -10493,6 +10493,7 @@ msgid "Player ammo will never decrease."
 msgstr "?????????? ???????????? ???????????? ???? ????????????????????."
 
 #: engines/hypno/metaengine.cpp:70 engines/scumm/metaengine.cpp:891
+#: engines/scumm/metaengine.cpp:900
 msgid "Unlock all levels"
 msgstr ""
 
@@ -14099,120 +14100,120 @@ msgstr "?????????????? ???????????????????????? ??????????????????"
 msgid "Run the game in 640x400 high resolution mode instead of 320x200"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:892
+#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:901
 msgid "All levels will be available without requiring passwords"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1037
+#: engines/scumm/metaengine.cpp:1049
 #, fuzzy
 msgid "Down left"
 msgstr "?????????????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1050
+#: engines/scumm/metaengine.cpp:1062
 #, fuzzy
 msgid "Down right"
 msgstr "?????????????? ??????????????"
 
-#: engines/scumm/metaengine.cpp:1070
+#: engines/scumm/metaengine.cpp:1082
 #, fuzzy
 #| msgid "Upper left item"
 msgid "Up left"
 msgstr "???????????? ?????????? ??????"
 
-#: engines/scumm/metaengine.cpp:1083
+#: engines/scumm/metaengine.cpp:1095
 #, fuzzy
 msgid "Up right"
 msgstr "??????????????"
 
-#: engines/scumm/metaengine.cpp:1095
+#: engines/scumm/metaengine.cpp:1107
 #, fuzzy
 msgid "Switch weapon"
 msgstr "????????????????????"
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1109
+#: engines/scumm/metaengine.cpp:1121
 msgid "Win the bike fight cheat"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1118
+#: engines/scumm/metaengine.cpp:1130
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1196
+#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1208
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1201
+#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1213
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1206
+#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1218
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1211
+#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1216
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1228
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "??????????????"
 
-#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
+#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1235
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "?????????????????????????? ????????"
 
-#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1230
+#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1242
 #, fuzzy
 #| msgid "Slide Left"
 msgid "Stick left"
 msgstr "?????????????????????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1161 engines/scumm/metaengine.cpp:1237
+#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1249
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick right"
 msgstr "?????????????????????? ??????????????"
 
-#: engines/scumm/metaengine.cpp:1168 engines/scumm/metaengine.cpp:1244
+#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1256
 #, fuzzy
 #| msgid "Move left"
 msgid "Fire / select"
 msgstr "?????????????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1173
+#: engines/scumm/metaengine.cpp:1185
 #, fuzzy
 #| msgid "Move Back"
 msgid "Menu back"
 msgstr "?????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1178
+#: engines/scumm/metaengine.cpp:1190
 #, fuzzy
 #| msgid "Open main menu"
 msgid "Skip / menu back"
 msgstr "?????????????? ???????????????? ????????"
 
-#: engines/scumm/metaengine.cpp:1194
+#: engines/scumm/metaengine.cpp:1206
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1249
+#: engines/scumm/metaengine.cpp:1261
 #, fuzzy
 #| msgid "Move Back"
 msgid "Cover / back"
 msgstr "?????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1255
+#: engines/scumm/metaengine.cpp:1267
 #, fuzzy
 #| msgid "Skip dialogue"
 msgid "Skip / back"
 msgstr "?????????????????????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1262
+#: engines/scumm/metaengine.cpp:1274
 #, fuzzy
 #| msgid "Open main menu"
 msgid "Skip / menu"
diff --git a/po/be_BY.po b/po/be_BY.po
index 350b1463732..8384c99d89b 100644
--- a/po/be_BY.po
+++ b/po/be_BY.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 19:59+0000\n"
+"POT-Creation-Date: 2026-06-01 21:39+0000\n"
 "PO-Revision-Date: 2021-03-21 08:24+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Belarusian <https://translations.scummvm.org/projects/scummvm/"
@@ -1074,7 +1074,7 @@ msgstr "??????????????????????????"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1076
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1088
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1095,7 +1095,7 @@ msgstr "??????????"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1043
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1055
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1116,7 +1116,7 @@ msgstr "????????"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1056
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1068
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1136,7 +1136,7 @@ msgstr "????????????"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1063
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1075
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3873,7 +3873,7 @@ msgid "Game not implemented"
 msgstr "Game id ???? ??????????????????????????"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
-#: engines/scumm/metaengine.cpp:1026
+#: engines/scumm/metaengine.cpp:1038
 #, fuzzy
 #| msgid "Default game keymap"
 msgid "Default game keymappings"
@@ -5877,7 +5877,7 @@ msgstr "R2"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1183 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1195 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "????????"
 
@@ -7382,7 +7382,7 @@ msgstr "?????????????????????? ??????"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1102 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1114 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -8137,7 +8137,7 @@ msgstr "??????????????????????????"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1089
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1101
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -10392,6 +10392,7 @@ msgid "Player ammo will never decrease."
 msgstr ""
 
 #: engines/hypno/metaengine.cpp:70 engines/scumm/metaengine.cpp:891
+#: engines/scumm/metaengine.cpp:900
 msgid "Unlock all levels"
 msgstr ""
 
@@ -13909,119 +13910,119 @@ msgstr "???????????????? ?????????????????????????? ?????????????? ????????????
 msgid "Run the game in 640x400 high resolution mode instead of 320x200"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:892
+#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:901
 msgid "All levels will be available without requiring passwords"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1037
+#: engines/scumm/metaengine.cpp:1049
 #, fuzzy
 msgid "Down left"
 msgstr "?????????????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1050
+#: engines/scumm/metaengine.cpp:1062
 #, fuzzy
 msgid "Down right"
 msgstr "?????????????? ??????????????"
 
-#: engines/scumm/metaengine.cpp:1070
+#: engines/scumm/metaengine.cpp:1082
 #, fuzzy
 #| msgid "Upper left item"
 msgid "Up left"
 msgstr "???????????? ???????? ??????????????"
 
-#: engines/scumm/metaengine.cpp:1083
+#: engines/scumm/metaengine.cpp:1095
 #, fuzzy
 msgid "Up right"
 msgstr "??????????????"
 
-#: engines/scumm/metaengine.cpp:1095
+#: engines/scumm/metaengine.cpp:1107
 #, fuzzy
 msgid "Switch weapon"
 msgstr "?????????? ??????????:"
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1109
+#: engines/scumm/metaengine.cpp:1121
 msgid "Win the bike fight cheat"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1118
+#: engines/scumm/metaengine.cpp:1130
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1196
+#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1208
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1201
+#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1213
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1206
+#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1218
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1211
+#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1216
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1228
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "??????????????"
 
-#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
+#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1235
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "???????????????????? ????????"
 
-#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1230
+#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1242
 #, fuzzy
 #| msgid "Slide Left"
 msgid "Stick left"
 msgstr "???????????????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1161 engines/scumm/metaengine.cpp:1237
+#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1249
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick right"
 msgstr "???????????????? ??????????????"
 
-#: engines/scumm/metaengine.cpp:1168 engines/scumm/metaengine.cpp:1244
+#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1256
 #, fuzzy
 #| msgid "Move left"
 msgid "Fire / select"
 msgstr "?????????????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1173
+#: engines/scumm/metaengine.cpp:1185
 #, fuzzy
 #| msgid "Move Back"
 msgid "Menu back"
 msgstr "???????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1178
+#: engines/scumm/metaengine.cpp:1190
 #, fuzzy
 #| msgid "Open main menu"
 msgid "Skip / menu back"
 msgstr "?????????????? ???????????????? ????????"
 
-#: engines/scumm/metaengine.cpp:1194
+#: engines/scumm/metaengine.cpp:1206
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1249
+#: engines/scumm/metaengine.cpp:1261
 #, fuzzy
 #| msgid "Move Back"
 msgid "Cover / back"
 msgstr "???????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1255
+#: engines/scumm/metaengine.cpp:1267
 #, fuzzy
 msgid "Skip / back"
 msgstr "???????????????????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1262
+#: engines/scumm/metaengine.cpp:1274
 #, fuzzy
 #| msgid "Open main menu"
 msgid "Skip / menu"
diff --git a/po/ca_ES.po b/po/ca_ES.po
index 332122576aa..63c6170015a 100644
--- a/po/ca_ES.po
+++ b/po/ca_ES.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.6.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 19:59+0000\n"
+"POT-Creation-Date: 2026-06-01 21:39+0000\n"
 "PO-Revision-Date: 2026-05-26 04:22+0000\n"
 "Last-Translator: Xaviu <xaviu at xaviu.com>\n"
 "Language-Team: Catalan <https://translations.scummvm.org/projects/scummvm/"
@@ -1057,7 +1057,7 @@ msgstr "Interaccionar"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1076
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1088
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1078,7 +1078,7 @@ msgstr "Amunt"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1043
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1055
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1099,7 +1099,7 @@ msgstr "Avall"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1056
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1068
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1119,7 +1119,7 @@ msgstr "Esquerra"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1063
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1075
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3796,7 +3796,7 @@ msgid "Game not implemented"
 msgstr "Joc no implementat"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
-#: engines/scumm/metaengine.cpp:1026
+#: engines/scumm/metaengine.cpp:1038
 msgid "Default game keymappings"
 msgstr "Assignaci?? de tecles per defecte del joc"
 
@@ -5679,7 +5679,7 @@ msgstr "R2"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1183 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1195 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "Men??"
 
@@ -7144,7 +7144,7 @@ msgstr "Activa / desactiva el combat"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1102 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1114 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -7812,7 +7812,7 @@ msgstr "Parla / Interactua"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1089
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1101
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -9781,6 +9781,7 @@ msgid "Player ammo will never decrease."
 msgstr "La munici?? del jugador mai disminuir??."
 
 #: engines/hypno/metaengine.cpp:70 engines/scumm/metaengine.cpp:891
+#: engines/scumm/metaengine.cpp:900
 msgid "Unlock all levels"
 msgstr "Desbloqueja tots els nivells"
 
@@ -12980,116 +12981,116 @@ msgstr "Alta resoluci?? activada"
 msgid "Run the game in 640x400 high resolution mode instead of 320x200"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:892
+#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:901
 #, fuzzy
 #| msgid "All levels will be available to play."
 msgid "All levels will be available without requiring passwords"
 msgstr "Tots els nivells estaran disponibles per jugar."
 
-#: engines/scumm/metaengine.cpp:1037
+#: engines/scumm/metaengine.cpp:1049
 msgid "Down left"
 msgstr "A baix a l'esquerra"
 
-#: engines/scumm/metaengine.cpp:1050
+#: engines/scumm/metaengine.cpp:1062
 msgid "Down right"
 msgstr "A baix a la dreta"
 
-#: engines/scumm/metaengine.cpp:1070
+#: engines/scumm/metaengine.cpp:1082
 msgid "Up left"
 msgstr "Amunt a l'esquerra"
 
-#: engines/scumm/metaengine.cpp:1083
+#: engines/scumm/metaengine.cpp:1095
 msgid "Up right"
 msgstr "Amunt a la dreta"
 
-#: engines/scumm/metaengine.cpp:1095
+#: engines/scumm/metaengine.cpp:1107
 msgid "Switch weapon"
 msgstr "Canvia d'arma"
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1109
+#: engines/scumm/metaengine.cpp:1121
 msgid "Win the bike fight cheat"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1118
+#: engines/scumm/metaengine.cpp:1130
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1196
+#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1208
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1201
+#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1213
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1206
+#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1218
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1211
+#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1216
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1228
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "Recull"
 
-#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
+#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1235
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "Despla??ament avall"
 
-#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1230
+#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1242
 #, fuzzy
 #| msgid "Slide left"
 msgid "Stick left"
 msgstr "Llisca cap a l'esquerra"
 
-#: engines/scumm/metaengine.cpp:1161 engines/scumm/metaengine.cpp:1237
+#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1249
 #, fuzzy
 #| msgid "Slide right"
 msgid "Stick right"
 msgstr "Llisca cap a la dreta"
 
-#: engines/scumm/metaengine.cpp:1168 engines/scumm/metaengine.cpp:1244
+#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1256
 #, fuzzy
 #| msgid "Move / Select"
 msgid "Fire / select"
 msgstr "Mou / Selecciona"
 
-#: engines/scumm/metaengine.cpp:1173
+#: engines/scumm/metaengine.cpp:1185
 #, fuzzy
 #| msgid "Move back"
 msgid "Menu back"
 msgstr "Mou enrere"
 
-#: engines/scumm/metaengine.cpp:1178
+#: engines/scumm/metaengine.cpp:1190
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu back"
 msgstr "Omet el men?? principal"
 
-#: engines/scumm/metaengine.cpp:1194
+#: engines/scumm/metaengine.cpp:1206
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1249
+#: engines/scumm/metaengine.cpp:1261
 #, fuzzy
 #| msgid "Move back"
 msgid "Cover / back"
 msgstr "Mou enrere"
 
-#: engines/scumm/metaengine.cpp:1255
+#: engines/scumm/metaengine.cpp:1267
 #, fuzzy
 #| msgid "Skip walk"
 msgid "Skip / back"
 msgstr "Omet el despla??ament"
 
-#: engines/scumm/metaengine.cpp:1262
+#: engines/scumm/metaengine.cpp:1274
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu"
diff --git a/po/cs_CZ.po b/po/cs_CZ.po
index a891995e094..5d877108c06 100644
--- a/po/cs_CZ.po
+++ b/po/cs_CZ.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.7.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 19:59+0000\n"
+"POT-Creation-Date: 2026-06-01 21:39+0000\n"
 "PO-Revision-Date: 2026-02-09 20:22+0000\n"
 "Last-Translator: AsciiWolf <mail at asciiwolf.com>\n"
 "Language-Team: Czech <https://translations.scummvm.org/projects/scummvm/"
@@ -1074,7 +1074,7 @@ msgstr "Interakce"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1076
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1088
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1095,7 +1095,7 @@ msgstr "Nahoru"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1043
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1055
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1116,7 +1116,7 @@ msgstr "Dol??"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1056
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1068
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1136,7 +1136,7 @@ msgstr "Doleva"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1063
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1075
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3861,7 +3861,7 @@ msgid "Game not implemented"
 msgstr "Id hry nen?? podporov??no"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
-#: engines/scumm/metaengine.cpp:1026
+#: engines/scumm/metaengine.cpp:1038
 #, fuzzy
 #| msgid "Default game keymap"
 msgid "Default game keymappings"
@@ -5845,7 +5845,7 @@ msgstr ""
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1183 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1195 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "Menu"
 
@@ -7336,7 +7336,7 @@ msgstr "Povolit zachycov??n?? my??i"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1102 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1114 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -8081,7 +8081,7 @@ msgstr "Interakce"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1089
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1101
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -10281,6 +10281,7 @@ msgid "Player ammo will never decrease."
 msgstr ""
 
 #: engines/hypno/metaengine.cpp:70 engines/scumm/metaengine.cpp:891
+#: engines/scumm/metaengine.cpp:900
 msgid "Unlock all levels"
 msgstr ""
 
@@ -13719,116 +13720,116 @@ msgstr "Povolit grafiku ve vysok??m rozli??en??"
 msgid "Run the game in 640x400 high resolution mode instead of 320x200"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:892
+#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:901
 msgid "All levels will be available without requiring passwords"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1037
+#: engines/scumm/metaengine.cpp:1049
 #, fuzzy
 msgid "Down left"
 msgstr "Oto??it se doleva"
 
-#: engines/scumm/metaengine.cpp:1050
+#: engines/scumm/metaengine.cpp:1062
 #, fuzzy
 msgid "Down right"
 msgstr "Oto??it se doprava"
 
-#: engines/scumm/metaengine.cpp:1070
+#: engines/scumm/metaengine.cpp:1082
 #, fuzzy
 #| msgid "Upper left item"
 msgid "Up left"
 msgstr "Polo??ka vlevo naho??e"
 
-#: engines/scumm/metaengine.cpp:1083
+#: engines/scumm/metaengine.cpp:1095
 #, fuzzy
 msgid "Up right"
 msgstr "Doprava"
 
-#: engines/scumm/metaengine.cpp:1095
+#: engines/scumm/metaengine.cpp:1107
 #, fuzzy
 msgid "Switch weapon"
 msgstr "M??n??n?? postav:"
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1109
+#: engines/scumm/metaengine.cpp:1121
 msgid "Win the bike fight cheat"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1118
+#: engines/scumm/metaengine.cpp:1130
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1196
+#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1208
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1201
+#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1213
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1206
+#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1218
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1211
+#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1216
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1228
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "Sebrat"
 
-#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
+#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1235
 #, fuzzy
 msgid "Stick down"
 msgstr "Posunout seznam dolu"
 
-#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1230
+#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1242
 #, fuzzy
 #| msgid "Slide Left"
 msgid "Stick left"
 msgstr "P??esunout se Doleva"
 
-#: engines/scumm/metaengine.cpp:1161 engines/scumm/metaengine.cpp:1237
+#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1249
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick right"
 msgstr "P??esunout se Doprava"
 
-#: engines/scumm/metaengine.cpp:1168 engines/scumm/metaengine.cpp:1244
+#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1256
 #, fuzzy
 msgid "Fire / select"
 msgstr "P??esunout se doleva"
 
-#: engines/scumm/metaengine.cpp:1173
+#: engines/scumm/metaengine.cpp:1185
 #, fuzzy
 #| msgid "Move Back"
 msgid "Menu back"
 msgstr "Vzad"
 
-#: engines/scumm/metaengine.cpp:1178
+#: engines/scumm/metaengine.cpp:1190
 #, fuzzy
 msgid "Skip / menu back"
 msgstr "~N~??vrat do Spou??t????e"
 
-#: engines/scumm/metaengine.cpp:1194
+#: engines/scumm/metaengine.cpp:1206
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1249
+#: engines/scumm/metaengine.cpp:1261
 #, fuzzy
 #| msgid "Move Back"
 msgid "Cover / back"
 msgstr "Vzad"
 
-#: engines/scumm/metaengine.cpp:1255
+#: engines/scumm/metaengine.cpp:1267
 #, fuzzy
 msgid "Skip / back"
 msgstr "P??esko??it ????dek"
 
-#: engines/scumm/metaengine.cpp:1262
+#: engines/scumm/metaengine.cpp:1274
 #, fuzzy
 msgid "Skip / menu"
 msgstr "~N~??vrat do Spou??t????e"
diff --git a/po/da.po b/po/da.po
index a83bc0b942a..3b22733e0a9 100644
--- a/po/da.po
+++ b/po/da.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 19:59+0000\n"
+"POT-Creation-Date: 2026-06-01 21:39+0000\n"
 "PO-Revision-Date: 2023-09-07 17:04+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Danish <https://translations.scummvm.org/projects/scummvm/"
@@ -1077,7 +1077,7 @@ msgstr "interagere"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1076
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1088
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1098,7 +1098,7 @@ msgstr "Op"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1043
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1055
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1119,7 +1119,7 @@ msgstr "Ned"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1056
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1068
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1139,7 +1139,7 @@ msgstr "Venstre"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1063
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1075
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3872,7 +3872,7 @@ msgid "Game not implemented"
 msgstr "Spil-id ikke underst??ttet"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
-#: engines/scumm/metaengine.cpp:1026
+#: engines/scumm/metaengine.cpp:1038
 #, fuzzy
 msgid "Default game keymappings"
 msgstr "~I~ndstillinger ..."
@@ -5864,7 +5864,7 @@ msgstr ""
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1183 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1195 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "Menu"
 
@@ -7363,7 +7363,7 @@ msgstr "Museindfangning til/fra"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1102 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1114 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -8108,7 +8108,7 @@ msgstr "interagere"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1089
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1101
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -10302,6 +10302,7 @@ msgid "Player ammo will never decrease."
 msgstr ""
 
 #: engines/hypno/metaengine.cpp:70 engines/scumm/metaengine.cpp:891
+#: engines/scumm/metaengine.cpp:900
 msgid "Unlock all levels"
 msgstr ""
 
@@ -13810,116 +13811,116 @@ msgstr "Aktiv??r grafik i h??j opl??sning"
 msgid "Run the game in 640x400 high resolution mode instead of 320x200"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:892
+#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:901
 msgid "All levels will be available without requiring passwords"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1037
+#: engines/scumm/metaengine.cpp:1049
 #, fuzzy
 msgid "Down left"
 msgstr "Drej til venstre"
 
-#: engines/scumm/metaengine.cpp:1050
+#: engines/scumm/metaengine.cpp:1062
 #, fuzzy
 msgid "Down right"
 msgstr "Drej til h??jre"
 
-#: engines/scumm/metaengine.cpp:1070
+#: engines/scumm/metaengine.cpp:1082
 #, fuzzy
 #| msgid "Upper left item"
 msgid "Up left"
 msgstr "??verste venstre punkt"
 
-#: engines/scumm/metaengine.cpp:1083
+#: engines/scumm/metaengine.cpp:1095
 #, fuzzy
 msgid "Up right"
 msgstr "H??jre"
 
-#: engines/scumm/metaengine.cpp:1095
+#: engines/scumm/metaengine.cpp:1107
 #, fuzzy
 msgid "Switch weapon"
 msgstr "Skifter karaktere:"
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1109
+#: engines/scumm/metaengine.cpp:1121
 msgid "Win the bike fight cheat"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1118
+#: engines/scumm/metaengine.cpp:1130
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1196
+#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1208
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1201
+#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1213
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1206
+#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1218
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1211
+#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1216
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1228
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "Tag op"
 
-#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
+#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1235
 #, fuzzy
 msgid "Stick down"
 msgstr "Rul liste ned"
 
-#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1230
+#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1242
 #, fuzzy
 #| msgid "Slide Left"
 msgid "Stick left"
 msgstr "Flyt til venstre"
 
-#: engines/scumm/metaengine.cpp:1161 engines/scumm/metaengine.cpp:1237
+#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1249
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick right"
 msgstr "Flyt til h??jre"
 
-#: engines/scumm/metaengine.cpp:1168 engines/scumm/metaengine.cpp:1244
+#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1256
 #, fuzzy
 msgid "Fire / select"
 msgstr "Flyt til venstre"
 
-#: engines/scumm/metaengine.cpp:1173
+#: engines/scumm/metaengine.cpp:1185
 #, fuzzy
 #| msgid "Move Back"
 msgid "Menu back"
 msgstr "Flyt bagud"
 
-#: engines/scumm/metaengine.cpp:1178
+#: engines/scumm/metaengine.cpp:1190
 #, fuzzy
 msgid "Skip / menu back"
 msgstr "~R~etur til oversigt"
 
-#: engines/scumm/metaengine.cpp:1194
+#: engines/scumm/metaengine.cpp:1206
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1249
+#: engines/scumm/metaengine.cpp:1261
 #, fuzzy
 #| msgid "Move Back"
 msgid "Cover / back"
 msgstr "Flyt bagud"
 
-#: engines/scumm/metaengine.cpp:1255
+#: engines/scumm/metaengine.cpp:1267
 #, fuzzy
 msgid "Skip / back"
 msgstr "Spring linje over"
 
-#: engines/scumm/metaengine.cpp:1262
+#: engines/scumm/metaengine.cpp:1274
 #, fuzzy
 msgid "Skip / menu"
 msgstr "~R~etur til oversigt"
diff --git a/po/de_DE.po b/po/de_DE.po
index 94854784316..6320dfbaea2 100644
--- a/po/de_DE.po
+++ b/po/de_DE.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.10.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 19:59+0000\n"
+"POT-Creation-Date: 2026-06-01 21:39+0000\n"
 "PO-Revision-Date: 2026-05-05 16:31+0000\n"
 "Last-Translator: Lothar Serra Mari <lothar.serramari at scummvm.org>\n"
 "Language-Team: German <https://translations.scummvm.org/projects/scummvm/"
@@ -1064,7 +1064,7 @@ msgstr "Interagieren"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1076
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1088
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1085,7 +1085,7 @@ msgstr "Hoch"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1043
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1055
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1106,7 +1106,7 @@ msgstr "Runter"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1056
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1068
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1126,7 +1126,7 @@ msgstr "Links"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1063
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1075
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3929,7 +3929,7 @@ msgid "Game not implemented"
 msgstr "Spiel nicht implementiert"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
-#: engines/scumm/metaengine.cpp:1026
+#: engines/scumm/metaengine.cpp:1038
 msgid "Default game keymappings"
 msgstr "Standard-Tastenbelegung f??r Spiele"
 
@@ -6144,7 +6144,7 @@ msgstr "R2"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1183 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1195 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "Men??"
 
@@ -7627,7 +7627,7 @@ msgstr "Kampf umschalten"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1102 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1114 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -8294,7 +8294,7 @@ msgstr "Sprechen / Interagieren"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1089
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1101
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -10296,6 +10296,7 @@ msgid "Player ammo will never decrease."
 msgstr "Die Munition des Spielers wird sich nie verringern."
 
 #: engines/hypno/metaengine.cpp:70 engines/scumm/metaengine.cpp:891
+#: engines/scumm/metaengine.cpp:900
 msgid "Unlock all levels"
 msgstr "Alle Level freischalten"
 
@@ -13711,116 +13712,116 @@ msgstr "Hohe Aufl??sung eingeschaltet"
 msgid "Run the game in 640x400 high resolution mode instead of 320x200"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:892
+#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:901
 #, fuzzy
 #| msgid "All levels will be available to play."
 msgid "All levels will be available without requiring passwords"
 msgstr "Alle Level k??nnen direkt gespielt werden."
 
-#: engines/scumm/metaengine.cpp:1037
+#: engines/scumm/metaengine.cpp:1049
 msgid "Down left"
 msgstr "Unten links"
 
-#: engines/scumm/metaengine.cpp:1050
+#: engines/scumm/metaengine.cpp:1062
 msgid "Down right"
 msgstr "Unten rechts"
 
-#: engines/scumm/metaengine.cpp:1070
+#: engines/scumm/metaengine.cpp:1082
 msgid "Up left"
 msgstr "Oben links"
 
-#: engines/scumm/metaengine.cpp:1083
+#: engines/scumm/metaengine.cpp:1095
 msgid "Up right"
 msgstr "Oben rechts"
 
-#: engines/scumm/metaengine.cpp:1095
+#: engines/scumm/metaengine.cpp:1107
 msgid "Switch weapon"
 msgstr "Waffe wechseln"
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1109
+#: engines/scumm/metaengine.cpp:1121
 msgid "Win the bike fight cheat"
 msgstr "Cheat: Bike-Kampf gewinnen"
 
-#: engines/scumm/metaengine.cpp:1118
+#: engines/scumm/metaengine.cpp:1130
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1196
+#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1208
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1201
+#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1213
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1206
+#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1218
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1211
+#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1216
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1228
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "Nimm"
 
-#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
+#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1235
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "Nach unten scrollen"
 
-#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1230
+#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1242
 #, fuzzy
 #| msgid "Slide left"
 msgid "Stick left"
 msgstr "Slide nach links"
 
-#: engines/scumm/metaengine.cpp:1161 engines/scumm/metaengine.cpp:1237
+#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1249
 #, fuzzy
 #| msgid "Slide right"
 msgid "Stick right"
 msgstr "Slide nach rechts"
 
-#: engines/scumm/metaengine.cpp:1168 engines/scumm/metaengine.cpp:1244
+#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1256
 #, fuzzy
 #| msgid "Move / Select"
 msgid "Fire / select"
 msgstr "Bewegen / Ausw??hlen"
 
-#: engines/scumm/metaengine.cpp:1173
+#: engines/scumm/metaengine.cpp:1185
 #, fuzzy
 #| msgid "Move back"
 msgid "Menu back"
 msgstr "Zur??ckgehen"
 
-#: engines/scumm/metaengine.cpp:1178
+#: engines/scumm/metaengine.cpp:1190
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu back"
 msgstr "Hauptmen?? ??berspringen"
 
-#: engines/scumm/metaengine.cpp:1194
+#: engines/scumm/metaengine.cpp:1206
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1249
+#: engines/scumm/metaengine.cpp:1261
 #, fuzzy
 #| msgid "Move back"
 msgid "Cover / back"
 msgstr "Zur??ckgehen"
 
-#: engines/scumm/metaengine.cpp:1255
+#: engines/scumm/metaengine.cpp:1267
 #, fuzzy
 #| msgid "Skip walk"
 msgid "Skip / back"
 msgstr "Spaziergang ??berspringen"
 
-#: engines/scumm/metaengine.cpp:1262
+#: engines/scumm/metaengine.cpp:1274
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu"
diff --git a/po/el.po b/po/el.po
index 56be69d69f8..2af8184d969 100644
--- a/po/el.po
+++ b/po/el.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.10.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 19:59+0000\n"
+"POT-Creation-Date: 2026-06-01 21:39+0000\n"
 "PO-Revision-Date: 2026-04-23 00:22+0000\n"
 "Last-Translator: Antoniou Athanasios <a.antoniou79 at gmail.com>\n"
 "Language-Team: Greek <https://translations.scummvm.org/projects/scummvm/"
@@ -1067,7 +1067,7 @@ msgstr "??????????????????????????"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1076
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1088
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1088,7 +1088,7 @@ msgstr "????????"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1043
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1055
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1109,7 +1109,7 @@ msgstr "????????"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1056
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1068
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1129,7 +1129,7 @@ msgstr "????????????????"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1063
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1075
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -4000,7 +4000,7 @@ msgid "Game not implemented"
 msgstr "???? ???????????????? ?????? ???????? ????????????????????"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
-#: engines/scumm/metaengine.cpp:1026
+#: engines/scumm/metaengine.cpp:1038
 msgid "Default game keymappings"
 msgstr "?????????????? ???????????????????????????? ???????????????? ????????????????????"
 
@@ -6268,7 +6268,7 @@ msgstr "R2"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1183 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1195 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "??????????"
 
@@ -7755,7 +7755,7 @@ msgstr "???????????????? ?????????????????????? ?????? ????????"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1102 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1114 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -8429,7 +8429,7 @@ msgstr "???????? / ??????????????????????????"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1089
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1101
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -10425,6 +10425,7 @@ msgid "Player ammo will never decrease."
 msgstr "???? ???????????????????? ?????? ???????????? ?????? ???? ???????????????? ????????."
 
 #: engines/hypno/metaengine.cpp:70 engines/scumm/metaengine.cpp:891
+#: engines/scumm/metaengine.cpp:900
 msgid "Unlock all levels"
 msgstr "???????????????????? ???????? ?????? ????????????????"
 
@@ -13866,116 +13867,116 @@ msgstr "???????????????????????? ???????????? ????????????????"
 msgid "Run the game in 640x400 high resolution mode instead of 320x200"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:892
+#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:901
 #, fuzzy
 #| msgid "All levels will be available to play."
 msgid "All levels will be available without requiring passwords"
 msgstr "?????? ???? ?????????????? ???? ???????????? ?????????????????? ?????? ????????????????."
 
-#: engines/scumm/metaengine.cpp:1037
+#: engines/scumm/metaengine.cpp:1049
 msgid "Down left"
 msgstr "???????? ????????????????"
 
-#: engines/scumm/metaengine.cpp:1050
+#: engines/scumm/metaengine.cpp:1062
 msgid "Down right"
 msgstr "???????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1070
+#: engines/scumm/metaengine.cpp:1082
 msgid "Up left"
 msgstr "???????? ????????????????"
 
-#: engines/scumm/metaengine.cpp:1083
+#: engines/scumm/metaengine.cpp:1095
 msgid "Up right"
 msgstr "???????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1095
+#: engines/scumm/metaengine.cpp:1107
 msgid "Switch weapon"
 msgstr "???????????? ??????????"
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1109
+#: engines/scumm/metaengine.cpp:1121
 msgid "Win the bike fight cheat"
 msgstr "?????????? ?????? ???????? ?????? ???????? ???? ?????? ??????????????"
 
-#: engines/scumm/metaengine.cpp:1118
+#: engines/scumm/metaengine.cpp:1130
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1196
+#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1208
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1201
+#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1213
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1206
+#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1218
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1211
+#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1216
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1228
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "????????????"
 
-#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
+#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1235
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "???????????? ????????"
 
-#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1230
+#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1242
 #, fuzzy
 #| msgid "Slide left"
 msgid "Stick left"
 msgstr "???????????????? ????????????????"
 
-#: engines/scumm/metaengine.cpp:1161 engines/scumm/metaengine.cpp:1237
+#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1249
 #, fuzzy
 #| msgid "Slide right"
 msgid "Stick right"
 msgstr "???????????????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1168 engines/scumm/metaengine.cpp:1244
+#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1256
 #, fuzzy
 #| msgid "Move / Select"
 msgid "Fire / select"
 msgstr "???????????? / ??????????????"
 
-#: engines/scumm/metaengine.cpp:1173
+#: engines/scumm/metaengine.cpp:1185
 #, fuzzy
 #| msgid "Move Back"
 msgid "Menu back"
 msgstr "?????????????????? ????????"
 
-#: engines/scumm/metaengine.cpp:1178
+#: engines/scumm/metaengine.cpp:1190
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu back"
 msgstr "?????????????????? ?????????????????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1194
+#: engines/scumm/metaengine.cpp:1206
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1249
+#: engines/scumm/metaengine.cpp:1261
 #, fuzzy
 #| msgid "Move Back"
 msgid "Cover / back"
 msgstr "?????????????????? ????????"
 
-#: engines/scumm/metaengine.cpp:1255
+#: engines/scumm/metaengine.cpp:1267
 #, fuzzy
 #| msgid "Skip walk"
 msgid "Skip / back"
 msgstr "?????????????????? ??????????????????"
 
-#: engines/scumm/metaengine.cpp:1262
+#: engines/scumm/metaengine.cpp:1274
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu"
diff --git a/po/es_ES.po b/po/es_ES.po
index 14e22562e6e..660466ddddc 100644
--- a/po/es_ES.po
+++ b/po/es_ES.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.4.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 19:59+0000\n"
+"POT-Creation-Date: 2026-06-01 21:39+0000\n"
 "PO-Revision-Date: 2026-06-01 09:35+0000\n"
 "Last-Translator: IlDucci <pajaroloco_2 at hotmail.com>\n"
 "Language-Team: Spanish <https://translations.scummvm.org/projects/scummvm/"
@@ -1052,7 +1052,7 @@ msgstr "Interactuar"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1076
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1088
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1073,7 +1073,7 @@ msgstr "Arriba"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1043
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1055
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1094,7 +1094,7 @@ msgstr "Abajo"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1056
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1068
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1114,7 +1114,7 @@ msgstr "Izquierda"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1063
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1075
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3932,7 +3932,7 @@ msgid "Game not implemented"
 msgstr "Juego no implementado"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
-#: engines/scumm/metaengine.cpp:1026
+#: engines/scumm/metaengine.cpp:1038
 msgid "Default game keymappings"
 msgstr "Asignaciones predet. del juego"
 
@@ -6166,7 +6166,7 @@ msgstr "R2"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1183 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1195 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "Men??"
 
@@ -7642,7 +7642,7 @@ msgstr "Entrar/Salir del modo de combate"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1102 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1114 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -8311,7 +8311,7 @@ msgstr "Hablar/Interactuar"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1089
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1101
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -10302,6 +10302,7 @@ msgid "Player ammo will never decrease."
 msgstr "La munici??n del jugador no se reducir?? nunca."
 
 #: engines/hypno/metaengine.cpp:70 engines/scumm/metaengine.cpp:891
+#: engines/scumm/metaengine.cpp:900
 msgid "Unlock all levels"
 msgstr "Desbloquear todos los niveles"
 
@@ -13686,102 +13687,102 @@ msgstr ""
 "Muestra el juego en el modo de alta resoluci??n a 640??400 p??xeles, en vez de "
 "a 320??200"
 
-#: engines/scumm/metaengine.cpp:892
+#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:901
 msgid "All levels will be available without requiring passwords"
 msgstr "Desbloquea todos los niveles sin necesitar contrase??as"
 
-#: engines/scumm/metaengine.cpp:1037
+#: engines/scumm/metaengine.cpp:1049
 msgid "Down left"
 msgstr "Bajar a la izquierda"
 
-#: engines/scumm/metaengine.cpp:1050
+#: engines/scumm/metaengine.cpp:1062
 msgid "Down right"
 msgstr "Bajar a la derecha"
 
-#: engines/scumm/metaengine.cpp:1070
+#: engines/scumm/metaengine.cpp:1082
 msgid "Up left"
 msgstr "Subir a la izquierda"
 
-#: engines/scumm/metaengine.cpp:1083
+#: engines/scumm/metaengine.cpp:1095
 msgid "Up right"
 msgstr "Subir a la derecha"
 
-#: engines/scumm/metaengine.cpp:1095
+#: engines/scumm/metaengine.cpp:1107
 msgid "Switch weapon"
 msgstr "Cambiar de arma"
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1109
+#: engines/scumm/metaengine.cpp:1121
 msgid "Win the bike fight cheat"
 msgstr "Truco para ganar pelea de motos"
 
-#: engines/scumm/metaengine.cpp:1118
+#: engines/scumm/metaengine.cpp:1130
 msgid "Rebel Assault controls"
 msgstr "Controles de Rebel Assault"
 
-#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1196
+#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1208
 msgid "Aim up / menu up"
 msgstr "Apuntar arriba/Subir men??"
 
-#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1201
+#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1213
 msgid "Aim down / menu down"
 msgstr "Apuntar abajo/Bajar men??"
 
-#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1206
+#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1218
 msgid "Aim left / menu left"
 msgstr "Apuntar a izda./Men?? a izda."
 
-#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1211
+#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
 msgid "Aim right / menu right"
 msgstr "Apuntar a dcha./Men?? a dcha."
 
-#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1216
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1228
 msgid "Stick up"
 msgstr "Stick hacia arriba"
 
-#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
+#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1235
 msgid "Stick down"
 msgstr "Stick hacia abajo"
 
-#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1230
+#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1242
 msgid "Stick left"
 msgstr "Stick hacia la izquierda"
 
-#: engines/scumm/metaengine.cpp:1161 engines/scumm/metaengine.cpp:1237
+#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1249
 msgid "Stick right"
 msgstr "Stick hacia la derecha"
 
-#: engines/scumm/metaengine.cpp:1168 engines/scumm/metaengine.cpp:1244
+#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1256
 msgid "Fire / select"
 msgstr "Disparar/Seleccionar"
 
-#: engines/scumm/metaengine.cpp:1173
+#: engines/scumm/metaengine.cpp:1185
 #, fuzzy
 #| msgid "Move back"
 msgid "Menu back"
 msgstr "Retroceder"
 
-#: engines/scumm/metaengine.cpp:1178
+#: engines/scumm/metaengine.cpp:1190
 #, fuzzy
 #| msgid "Skip / menu"
 msgid "Skip / menu back"
 msgstr "Omitir/Men??"
 
-#: engines/scumm/metaengine.cpp:1194
+#: engines/scumm/metaengine.cpp:1206
 msgid "Rebel Assault II controls"
 msgstr "Controles de Rebel Assault II"
 
-#: engines/scumm/metaengine.cpp:1249
+#: engines/scumm/metaengine.cpp:1261
 msgid "Cover / back"
 msgstr "Cubrirse/Volver"
 
-#: engines/scumm/metaengine.cpp:1255
+#: engines/scumm/metaengine.cpp:1267
 #, fuzzy
 #| msgid "Skip walk"
 msgid "Skip / back"
 msgstr "Omitir desplazamiento"
 
-#: engines/scumm/metaengine.cpp:1262
+#: engines/scumm/metaengine.cpp:1274
 msgid "Skip / menu"
 msgstr "Omitir/Men??"
 
diff --git a/po/eu.po b/po/eu.po
index f31251c114d..cd5f3e5b6ba 100644
--- a/po/eu.po
+++ b/po/eu.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.5.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 19:59+0000\n"
+"POT-Creation-Date: 2026-06-01 21:39+0000\n"
 "PO-Revision-Date: 2024-02-05 18:42+0000\n"
 "Last-Translator: Josu Igoa <josuigoa at ni.eus>\n"
 "Language-Team: Basque <https://translations.scummvm.org/projects/scummvm/"
@@ -1044,7 +1044,7 @@ msgstr "Elkarreragin"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1076
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1088
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1065,7 +1065,7 @@ msgstr "Gora"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1043
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1055
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1086,7 +1086,7 @@ msgstr "Behera"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1056
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1068
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1106,7 +1106,7 @@ msgstr "Ezker"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1063
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1075
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3898,7 +3898,7 @@ msgid "Game not implemented"
 msgstr "Jokoaren ID ez-onartua"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
-#: engines/scumm/metaengine.cpp:1026
+#: engines/scumm/metaengine.cpp:1038
 #, fuzzy
 msgid "Default game keymappings"
 msgstr "~A~ukerak..."
@@ -5919,7 +5919,7 @@ msgstr ""
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1183 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1195 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "Menua"
 
@@ -7409,7 +7409,7 @@ msgstr "Mututu/Ez-mututu"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1102 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1114 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -8156,7 +8156,7 @@ msgstr "Elkarreragin"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1089
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1101
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -10374,6 +10374,7 @@ msgid "Player ammo will never decrease."
 msgstr ""
 
 #: engines/hypno/metaengine.cpp:70 engines/scumm/metaengine.cpp:891
+#: engines/scumm/metaengine.cpp:900
 msgid "Unlock all levels"
 msgstr ""
 
@@ -13777,116 +13778,116 @@ msgstr "Gaitu erresoluzio altuko grafikoak"
 msgid "Run the game in 640x400 high resolution mode instead of 320x200"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:892
+#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:901
 msgid "All levels will be available without requiring passwords"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1037
+#: engines/scumm/metaengine.cpp:1049
 #, fuzzy
 msgid "Down left"
 msgstr "Ezkerrera biratu"
 
-#: engines/scumm/metaengine.cpp:1050
+#: engines/scumm/metaengine.cpp:1062
 #, fuzzy
 msgid "Down right"
 msgstr "Eskuinera biratu"
 
-#: engines/scumm/metaengine.cpp:1070
+#: engines/scumm/metaengine.cpp:1082
 #, fuzzy
 #| msgid "Upper left item"
 msgid "Up left"
 msgstr "Goiko ezkerreko objektua"
 
-#: engines/scumm/metaengine.cpp:1083
+#: engines/scumm/metaengine.cpp:1095
 #, fuzzy
 msgid "Up right"
 msgstr "Eskuin"
 
-#: engines/scumm/metaengine.cpp:1095
+#: engines/scumm/metaengine.cpp:1107
 #, fuzzy
 msgid "Switch weapon"
 msgstr "Pertsonaia aldatu:"
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1109
+#: engines/scumm/metaengine.cpp:1121
 msgid "Win the bike fight cheat"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1118
+#: engines/scumm/metaengine.cpp:1130
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1196
+#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1208
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1201
+#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1213
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1206
+#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1218
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1211
+#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1216
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1228
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "Jaso"
 
-#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
+#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1235
 #, fuzzy
 msgid "Stick down"
 msgstr "Behera"
 
-#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1230
+#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1242
 #, fuzzy
 #| msgid "Slide Left"
 msgid "Stick left"
 msgstr "Ezkerrera irristatu"
 
-#: engines/scumm/metaengine.cpp:1161 engines/scumm/metaengine.cpp:1237
+#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1249
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick right"
 msgstr "Eskuinera irristatu"
 
-#: engines/scumm/metaengine.cpp:1168 engines/scumm/metaengine.cpp:1244
+#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1256
 #, fuzzy
 msgid "Fire / select"
 msgstr "Ezkerrera irristatu"
 
-#: engines/scumm/metaengine.cpp:1173
+#: engines/scumm/metaengine.cpp:1185
 #, fuzzy
 #| msgid "Move Back"
 msgid "Menu back"
 msgstr "Atzera mugitu"
 
-#: engines/scumm/metaengine.cpp:1178
+#: engines/scumm/metaengine.cpp:1190
 #, fuzzy
 msgid "Skip / menu back"
 msgstr "It~z~uli abiarazlera"
 
-#: engines/scumm/metaengine.cpp:1194
+#: engines/scumm/metaengine.cpp:1206
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1249
+#: engines/scumm/metaengine.cpp:1261
 #, fuzzy
 #| msgid "Move Back"
 msgid "Cover / back"
 msgstr "Atzera mugitu"
 
-#: engines/scumm/metaengine.cpp:1255
+#: engines/scumm/metaengine.cpp:1267
 #, fuzzy
 msgid "Skip / back"
 msgstr "Lerroa saltatu"
 
-#: engines/scumm/metaengine.cpp:1262
+#: engines/scumm/metaengine.cpp:1274
 #, fuzzy
 msgid "Skip / menu"
 msgstr "It~z~uli abiarazlera"
diff --git a/po/fi_FI.po b/po/fi_FI.po
index 89b868c2071..0c1e6bb9085 100644
--- a/po/fi_FI.po
+++ b/po/fi_FI.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.6.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 19:59+0000\n"
+"POT-Creation-Date: 2026-06-01 21:39+0000\n"
 "PO-Revision-Date: 2026-02-07 15:22+0000\n"
 "Last-Translator: Timo Mikkolainen <tmikkola at gmail.com>\n"
 "Language-Team: Finnish <https://translations.scummvm.org/projects/scummvm/"
@@ -1052,7 +1052,7 @@ msgstr "K??yt??"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1076
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1088
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1073,7 +1073,7 @@ msgstr "Yl??s"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1043
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1055
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1094,7 +1094,7 @@ msgstr "Alas"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1056
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1068
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1114,7 +1114,7 @@ msgstr "Vasen"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1063
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1075
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3950,7 +3950,7 @@ msgid "Game not implemented"
 msgstr "Pelill?? ei ole toteutusta"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
-#: engines/scumm/metaengine.cpp:1026
+#: engines/scumm/metaengine.cpp:1038
 #, fuzzy
 #| msgid "Default game keymap"
 msgid "Default game keymappings"
@@ -6263,7 +6263,7 @@ msgstr "R2"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1183 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1195 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "Valikko"
 
@@ -7725,7 +7725,7 @@ msgstr "Taistelumoodi p????lle / pois"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1102 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1114 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -8423,7 +8423,7 @@ msgstr "Puhu / Vuorovaikuta"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1089
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1101
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -10498,6 +10498,7 @@ msgid "Player ammo will never decrease."
 msgstr "Pelaajan ammukset eiv??t v??hene."
 
 #: engines/hypno/metaengine.cpp:70 engines/scumm/metaengine.cpp:891
+#: engines/scumm/metaengine.cpp:900
 msgid "Unlock all levels"
 msgstr "Avaa kaikki tasot"
 
@@ -13911,116 +13912,116 @@ msgstr "K??yt?? korkeaa resoluutiota"
 msgid "Run the game in 640x400 high resolution mode instead of 320x200"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:892
+#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:901
 #, fuzzy
 #| msgid "All levels will be available to play."
 msgid "All levels will be available without requiring passwords"
 msgstr "Kaikki tasot ovat pelattavissa."
 
-#: engines/scumm/metaengine.cpp:1037
+#: engines/scumm/metaengine.cpp:1049
 msgid "Down left"
 msgstr "Alavasemmalle"
 
-#: engines/scumm/metaengine.cpp:1050
+#: engines/scumm/metaengine.cpp:1062
 msgid "Down right"
 msgstr "Alaoikealle"
 
-#: engines/scumm/metaengine.cpp:1070
+#: engines/scumm/metaengine.cpp:1082
 msgid "Up left"
 msgstr "Yl??vasemmalle"
 
-#: engines/scumm/metaengine.cpp:1083
+#: engines/scumm/metaengine.cpp:1095
 msgid "Up right"
 msgstr "Yl??oikealle"
 
-#: engines/scumm/metaengine.cpp:1095
+#: engines/scumm/metaengine.cpp:1107
 msgid "Switch weapon"
 msgstr "Vaihda asetta"
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1109
+#: engines/scumm/metaengine.cpp:1121
 msgid "Win the bike fight cheat"
 msgstr "Voita moottoripy??r??taistelu-huijaus"
 
-#: engines/scumm/metaengine.cpp:1118
+#: engines/scumm/metaengine.cpp:1130
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1196
+#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1208
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1201
+#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1213
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1206
+#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1218
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1211
+#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1216
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1228
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "Ota"
 
-#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
+#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1235
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "Vierit?? alas"
 
-#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1230
+#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1242
 #, fuzzy
 #| msgid "Slide left"
 msgid "Stick left"
 msgstr "Liu'u vasemmalle"
 
-#: engines/scumm/metaengine.cpp:1161 engines/scumm/metaengine.cpp:1237
+#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1249
 #, fuzzy
 #| msgid "Slide right"
 msgid "Stick right"
 msgstr "Liu'u oikealle"
 
-#: engines/scumm/metaengine.cpp:1168 engines/scumm/metaengine.cpp:1244
+#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1256
 #, fuzzy
 #| msgid "Move / Select"
 msgid "Fire / select"
 msgstr "Liiku / Valitse"
 
-#: engines/scumm/metaengine.cpp:1173
+#: engines/scumm/metaengine.cpp:1185
 #, fuzzy
 #| msgid "Move back"
 msgid "Menu back"
 msgstr "Liiku taaksep??in"
 
-#: engines/scumm/metaengine.cpp:1178
+#: engines/scumm/metaengine.cpp:1190
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu back"
 msgstr "Ohita p????valikko"
 
-#: engines/scumm/metaengine.cpp:1194
+#: engines/scumm/metaengine.cpp:1206
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1249
+#: engines/scumm/metaengine.cpp:1261
 #, fuzzy
 #| msgid "Move back"
 msgid "Cover / back"
 msgstr "Liiku taaksep??in"
 
-#: engines/scumm/metaengine.cpp:1255
+#: engines/scumm/metaengine.cpp:1267
 #, fuzzy
 #| msgid "Skip walk"
 msgid "Skip / back"
 msgstr "Ohita k??vely"
 
-#: engines/scumm/metaengine.cpp:1262
+#: engines/scumm/metaengine.cpp:1274
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu"
diff --git a/po/fr_FR.po b/po/fr_FR.po
index fe3bffefbe6..9460dfbbe7c 100644
--- a/po/fr_FR.po
+++ b/po/fr_FR.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 19:59+0000\n"
+"POT-Creation-Date: 2026-06-01 21:39+0000\n"
 "PO-Revision-Date: 2026-06-01 18:22+0000\n"
 "Last-Translator: Purple T <ZEONK at hotmail.com>\n"
 "Language-Team: French <https://translations.scummvm.org/projects/scummvm/"
@@ -1063,7 +1063,7 @@ msgstr "Interagir"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1076
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1088
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1084,7 +1084,7 @@ msgstr "Haut"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1043
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1055
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1105,7 +1105,7 @@ msgstr "Bas"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1056
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1068
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1125,7 +1125,7 @@ msgstr "Gauche"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1063
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1075
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3955,7 +3955,7 @@ msgid "Game not implemented"
 msgstr "Jeu non impl??ment??"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
-#: engines/scumm/metaengine.cpp:1026
+#: engines/scumm/metaengine.cpp:1038
 msgid "Default game keymappings"
 msgstr "Conf. de touches par d??faut"
 
@@ -6181,7 +6181,7 @@ msgstr "R2"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1183 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1195 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "Menu"
 
@@ -7647,7 +7647,7 @@ msgstr "Act. / D??sact. combat"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1102 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1114 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -8311,7 +8311,7 @@ msgstr "Parler / Interagir"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1089
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1101
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -10303,6 +10303,7 @@ msgid "Player ammo will never decrease."
 msgstr "La quantit?? de munitions ne baissera jamais."
 
 #: engines/hypno/metaengine.cpp:70 engines/scumm/metaengine.cpp:891
+#: engines/scumm/metaengine.cpp:900
 msgid "Unlock all levels"
 msgstr "D??bloquer tous les niveaux"
 
@@ -13711,102 +13712,102 @@ msgstr "Mode Haute r??solution"
 msgid "Run the game in 640x400 high resolution mode instead of 320x200"
 msgstr "Lance le jeu en 640x400 haute r??solution au lieu de 320x200"
 
-#: engines/scumm/metaengine.cpp:892
+#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:901
 msgid "All levels will be available without requiring passwords"
 msgstr "Tous les niveaux seront disponibles sans demander de mots de passe"
 
-#: engines/scumm/metaengine.cpp:1037
+#: engines/scumm/metaengine.cpp:1049
 msgid "Down left"
 msgstr "Bas gauche"
 
-#: engines/scumm/metaengine.cpp:1050
+#: engines/scumm/metaengine.cpp:1062
 msgid "Down right"
 msgstr "Bas droite"
 
-#: engines/scumm/metaengine.cpp:1070
+#: engines/scumm/metaengine.cpp:1082
 msgid "Up left"
 msgstr "Haut gauche"
 
-#: engines/scumm/metaengine.cpp:1083
+#: engines/scumm/metaengine.cpp:1095
 msgid "Up right"
 msgstr "Haut droite"
 
-#: engines/scumm/metaengine.cpp:1095
+#: engines/scumm/metaengine.cpp:1107
 msgid "Switch weapon"
 msgstr "Changer d'arme"
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1109
+#: engines/scumm/metaengine.cpp:1121
 msgid "Win the bike fight cheat"
 msgstr "Gagner les bagarres en b??cane (cheat)"
 
-#: engines/scumm/metaengine.cpp:1118
+#: engines/scumm/metaengine.cpp:1130
 msgid "Rebel Assault controls"
 msgstr "Contr??les de Rebel Assault"
 
-#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1196
+#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1208
 msgid "Aim up / menu up"
 msgstr "Vis??e haut / Menu haut"
 
-#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1201
+#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1213
 msgid "Aim down / menu down"
 msgstr "Vis??e bas / Menu bas"
 
-#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1206
+#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1218
 msgid "Aim left / menu left"
 msgstr "Vis??e gauche / menu gauche"
 
-#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1211
+#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
 msgid "Aim right / menu right"
 msgstr "Vis??e droite / Menu droite"
 
-#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1216
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1228
 msgid "Stick up"
 msgstr "Manche haut"
 
-#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
+#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1235
 msgid "Stick down"
 msgstr "Manche bas"
 
-#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1230
+#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1242
 msgid "Stick left"
 msgstr "Manche gauche"
 
-#: engines/scumm/metaengine.cpp:1161 engines/scumm/metaengine.cpp:1237
+#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1249
 msgid "Stick right"
 msgstr "Manche droite"
 
-#: engines/scumm/metaengine.cpp:1168 engines/scumm/metaengine.cpp:1244
+#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1256
 msgid "Fire / select"
 msgstr "Tirer / S??lectionner"
 
-#: engines/scumm/metaengine.cpp:1173
+#: engines/scumm/metaengine.cpp:1185
 #, fuzzy
 #| msgid "Move back"
 msgid "Menu back"
 msgstr "Reculer"
 
-#: engines/scumm/metaengine.cpp:1178
+#: engines/scumm/metaengine.cpp:1190
 #, fuzzy
 #| msgid "Skip / menu"
 msgid "Skip / menu back"
 msgstr "Passer / menu"
 
-#: engines/scumm/metaengine.cpp:1194
+#: engines/scumm/metaengine.cpp:1206
 msgid "Rebel Assault II controls"
 msgstr "Contr??les de Rebel Assault II"
 
-#: engines/scumm/metaengine.cpp:1249
+#: engines/scumm/metaengine.cpp:1261
 msgid "Cover / back"
 msgstr "Couverture / retour"
 
-#: engines/scumm/metaengine.cpp:1255
+#: engines/scumm/metaengine.cpp:1267
 #, fuzzy
 #| msgid "Skip walk"
 msgid "Skip / back"
 msgstr "Passer le d??placement"
 
-#: engines/scumm/metaengine.cpp:1262
+#: engines/scumm/metaengine.cpp:1274
 msgid "Skip / menu"
 msgstr "Passer / menu"
 
diff --git a/po/gl_ES.po b/po/gl_ES.po
index 07af72c00dc..1affcaa145d 100644
--- a/po/gl_ES.po
+++ b/po/gl_ES.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 19:59+0000\n"
+"POT-Creation-Date: 2026-06-01 21:39+0000\n"
 "PO-Revision-Date: 2020-09-26 21:13+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Galician <https://translations.scummvm.org/projects/scummvm/"
@@ -1070,7 +1070,7 @@ msgstr "Interpolaci??n:"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1076
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1088
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1091,7 +1091,7 @@ msgstr "Arriba"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1043
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1055
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1112,7 +1112,7 @@ msgstr "Abaixo"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1056
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1068
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1132,7 +1132,7 @@ msgstr "Esquerda"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1063
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1075
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3859,7 +3859,7 @@ msgid "Game not implemented"
 msgstr "ID de xogo non compatible"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
-#: engines/scumm/metaengine.cpp:1026
+#: engines/scumm/metaengine.cpp:1038
 #, fuzzy
 msgid "Default game keymappings"
 msgstr "~O~pci??ns..."
@@ -5853,7 +5853,7 @@ msgstr ""
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1183 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1195 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "Men??"
 
@@ -7342,7 +7342,7 @@ msgstr "Activar/desactivar captura de rato"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1102 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1114 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -8088,7 +8088,7 @@ msgstr "Interpolaci??n:"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1089
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1101
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -10281,6 +10281,7 @@ msgid "Player ammo will never decrease."
 msgstr ""
 
 #: engines/hypno/metaengine.cpp:70 engines/scumm/metaengine.cpp:891
+#: engines/scumm/metaengine.cpp:900
 msgid "Unlock all levels"
 msgstr ""
 
@@ -13733,116 +13734,116 @@ msgstr "Activar gr??ficos de alta resoluci??n"
 msgid "Run the game in 640x400 high resolution mode instead of 320x200"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:892
+#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:901
 msgid "All levels will be available without requiring passwords"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1037
+#: engines/scumm/metaengine.cpp:1049
 #, fuzzy
 msgid "Down left"
 msgstr "Xirar ?? esquerda"
 
-#: engines/scumm/metaengine.cpp:1050
+#: engines/scumm/metaengine.cpp:1062
 #, fuzzy
 msgid "Down right"
 msgstr "Xirar ?? dereita"
 
-#: engines/scumm/metaengine.cpp:1070
+#: engines/scumm/metaengine.cpp:1082
 #, fuzzy
 #| msgid "Upper left item"
 msgid "Up left"
 msgstr "Obxecto esquerdo arriba"
 
-#: engines/scumm/metaengine.cpp:1083
+#: engines/scumm/metaengine.cpp:1095
 #, fuzzy
 msgid "Up right"
 msgstr "Dereita"
 
-#: engines/scumm/metaengine.cpp:1095
+#: engines/scumm/metaengine.cpp:1107
 #, fuzzy
 msgid "Switch weapon"
 msgstr "Cambiar personaxes:"
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1109
+#: engines/scumm/metaengine.cpp:1121
 msgid "Win the bike fight cheat"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1118
+#: engines/scumm/metaengine.cpp:1130
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1196
+#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1208
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1201
+#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1213
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1206
+#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1218
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1211
+#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1216
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1228
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "Coller"
 
-#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
+#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1235
 #, fuzzy
 msgid "Stick down"
 msgstr "Baixar lista"
 
-#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1230
+#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1242
 #, fuzzy
 #| msgid "Slide Left"
 msgid "Stick left"
 msgstr "Esvarar ?? esquerda"
 
-#: engines/scumm/metaengine.cpp:1161 engines/scumm/metaengine.cpp:1237
+#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1249
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick right"
 msgstr "Esvarar ?? dereita"
 
-#: engines/scumm/metaengine.cpp:1168 engines/scumm/metaengine.cpp:1244
+#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1256
 #, fuzzy
 msgid "Fire / select"
 msgstr "Mover ?? esquerda"
 
-#: engines/scumm/metaengine.cpp:1173
+#: engines/scumm/metaengine.cpp:1185
 #, fuzzy
 #| msgid "Move Back"
 msgid "Menu back"
 msgstr "Mover cara atr??s"
 
-#: engines/scumm/metaengine.cpp:1178
+#: engines/scumm/metaengine.cpp:1190
 #, fuzzy
 msgid "Skip / menu back"
 msgstr "~V~olver ao Iniciador"
 
-#: engines/scumm/metaengine.cpp:1194
+#: engines/scumm/metaengine.cpp:1206
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1249
+#: engines/scumm/metaengine.cpp:1261
 #, fuzzy
 #| msgid "Move Back"
 msgid "Cover / back"
 msgstr "Mover cara atr??s"
 
-#: engines/scumm/metaengine.cpp:1255
+#: engines/scumm/metaengine.cpp:1267
 #, fuzzy
 msgid "Skip / back"
 msgstr "Omitir li??a"
 
-#: engines/scumm/metaengine.cpp:1262
+#: engines/scumm/metaengine.cpp:1274
 #, fuzzy
 msgid "Skip / menu"
 msgstr "~V~olver ao Iniciador"
diff --git a/po/he.po b/po/he.po
index 6482aba2da2..d8d215cbfc5 100644
--- a/po/he.po
+++ b/po/he.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.1.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 19:59+0000\n"
+"POT-Creation-Date: 2026-06-01 21:39+0000\n"
 "PO-Revision-Date: 2025-04-09 20:18+0000\n"
 "Last-Translator: Niv Baehr <bloop93 at gmail.com>\n"
 "Language-Team: Hebrew <https://translations.scummvm.org/projects/scummvm/"
@@ -1033,7 +1033,7 @@ msgstr "??????????"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1076
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1088
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1054,7 +1054,7 @@ msgstr "??????????"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1043
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1055
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1075,7 +1075,7 @@ msgstr "????????"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1056
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1068
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1095,7 +1095,7 @@ msgstr "??????????"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1063
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1075
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3807,7 +3807,7 @@ msgid "Game not implemented"
 msgstr "???????? ?????????? ???????? ????????"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
-#: engines/scumm/metaengine.cpp:1026
+#: engines/scumm/metaengine.cpp:1038
 #, fuzzy
 #| msgid "Default game keymap"
 msgid "Default game keymappings"
@@ -5811,7 +5811,7 @@ msgstr "R2"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1183 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1195 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "??????????"
 
@@ -7325,7 +7325,7 @@ msgstr "?????????? ??????????"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1102 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1114 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -8087,7 +8087,7 @@ msgstr "????????????????????"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1089
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1101
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -10392,6 +10392,7 @@ msgid "Player ammo will never decrease."
 msgstr "???????????? ?????????? ???? ???????? ??????????."
 
 #: engines/hypno/metaengine.cpp:70 engines/scumm/metaengine.cpp:891
+#: engines/scumm/metaengine.cpp:900
 msgid "Unlock all levels"
 msgstr "?????????? ???? ????????????"
 
@@ -13953,121 +13954,121 @@ msgstr "?????????? ?????????? ??????????"
 msgid "Run the game in 640x400 high resolution mode instead of 320x200"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:892
+#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:901
 #, fuzzy
 #| msgid "All levels will be available to play."
 msgid "All levels will be available without requiring passwords"
 msgstr "???? ???????????? ???????? ???????????? ??????????."
 
-#: engines/scumm/metaengine.cpp:1037
+#: engines/scumm/metaengine.cpp:1049
 #, fuzzy
 msgid "Down left"
 msgstr "???????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1050
+#: engines/scumm/metaengine.cpp:1062
 #, fuzzy
 msgid "Down right"
 msgstr "?????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1070
+#: engines/scumm/metaengine.cpp:1082
 #, fuzzy
 #| msgid "Upper left item"
 msgid "Up left"
 msgstr "???????? ?????????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1083
+#: engines/scumm/metaengine.cpp:1095
 #, fuzzy
 msgid "Up right"
 msgstr "??????????"
 
-#: engines/scumm/metaengine.cpp:1095
+#: engines/scumm/metaengine.cpp:1107
 msgid "Switch weapon"
 msgstr "?????????? ??????"
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1109
+#: engines/scumm/metaengine.cpp:1121
 msgid "Win the bike fight cheat"
 msgstr "?????????? ?????????????? ???????? ????????????????"
 
-#: engines/scumm/metaengine.cpp:1118
+#: engines/scumm/metaengine.cpp:1130
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1196
+#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1208
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1201
+#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1213
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1206
+#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1218
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1211
+#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1216
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1228
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "??????????"
 
-#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
+#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1235
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "?????????? ??????"
 
-#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1230
+#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1242
 #, fuzzy
 #| msgid "Slide Left"
 msgid "Stick left"
 msgstr "???????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1161 engines/scumm/metaengine.cpp:1237
+#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1249
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick right"
 msgstr "???????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1168 engines/scumm/metaengine.cpp:1244
+#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1256
 #, fuzzy
 #| msgid "Move left"
 msgid "Fire / select"
 msgstr "?????????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1173
+#: engines/scumm/metaengine.cpp:1185
 #, fuzzy
 #| msgid "Move Back"
 msgid "Menu back"
 msgstr "??????????"
 
-#: engines/scumm/metaengine.cpp:1178
+#: engines/scumm/metaengine.cpp:1190
 #, fuzzy
 #| msgid "Open main menu"
 msgid "Skip / menu back"
 msgstr "?????????? ?????????? ????????"
 
-#: engines/scumm/metaengine.cpp:1194
+#: engines/scumm/metaengine.cpp:1206
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1249
+#: engines/scumm/metaengine.cpp:1261
 #, fuzzy
 #| msgid "Move Back"
 msgid "Cover / back"
 msgstr "??????????"
 
-#: engines/scumm/metaengine.cpp:1255
+#: engines/scumm/metaengine.cpp:1267
 #, fuzzy
 #| msgid "Skip dialogue"
 msgid "Skip / back"
 msgstr "?????????? ???? ????????"
 
-#: engines/scumm/metaengine.cpp:1262
+#: engines/scumm/metaengine.cpp:1274
 #, fuzzy
 #| msgid "Open main menu"
 msgid "Skip / menu"
diff --git a/po/hi.po b/po/hi.po
index dcd15442bbc..d084ba77f95 100644
--- a/po/hi.po
+++ b/po/hi.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 19:59+0000\n"
+"POT-Creation-Date: 2026-06-01 21:39+0000\n"
 "PO-Revision-Date: 2026-05-12 22:22+0000\n"
 "Last-Translator: KARTIK SAXENA <kartiksaxena2005 at gmail.com>\n"
 "Language-Team: Hindi <https://translations.scummvm.org/projects/scummvm/"
@@ -1042,7 +1042,7 @@ msgstr ""
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1076
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1088
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1063,7 +1063,7 @@ msgstr "?????????"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1043
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1055
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1084,7 +1084,7 @@ msgstr "????????????"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1056
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1068
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1104,7 +1104,7 @@ msgstr "????????????"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1063
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1075
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3734,7 +3734,7 @@ msgid "Game not implemented"
 msgstr ""
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
-#: engines/scumm/metaengine.cpp:1026
+#: engines/scumm/metaengine.cpp:1038
 #, fuzzy
 #| msgid "Game Options"
 msgid "Default game keymappings"
@@ -5638,7 +5638,7 @@ msgstr ""
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1183 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1195 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr ""
 
@@ -7059,7 +7059,7 @@ msgstr "???????????????????????? ?????????"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1102 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1114 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -7780,7 +7780,7 @@ msgstr "???????????? ??????????????? ????????????????????????"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1089
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1101
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -9906,6 +9906,7 @@ msgid "Player ammo will never decrease."
 msgstr ""
 
 #: engines/hypno/metaengine.cpp:70 engines/scumm/metaengine.cpp:891
+#: engines/scumm/metaengine.cpp:900
 msgid "Unlock all levels"
 msgstr ""
 
@@ -13210,119 +13211,119 @@ msgstr ""
 msgid "Run the game in 640x400 high resolution mode instead of 320x200"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:892
+#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:901
 msgid "All levels will be available without requiring passwords"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1037
+#: engines/scumm/metaengine.cpp:1049
 #, fuzzy
 #| msgid "Down Left"
 msgid "Down left"
 msgstr "???????????? ??????????????????"
 
-#: engines/scumm/metaengine.cpp:1050
+#: engines/scumm/metaengine.cpp:1062
 #, fuzzy
 #| msgid "Down Right"
 msgid "Down right"
 msgstr "??????????????? ??????????????????"
 
-#: engines/scumm/metaengine.cpp:1070
+#: engines/scumm/metaengine.cpp:1082
 #, fuzzy
 #| msgid "Move Left"
 msgid "Up left"
 msgstr "???????????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1083
+#: engines/scumm/metaengine.cpp:1095
 #, fuzzy
 #| msgid "Up Right"
 msgid "Up right"
 msgstr "????????????"
 
-#: engines/scumm/metaengine.cpp:1095
+#: engines/scumm/metaengine.cpp:1107
 msgid "Switch weapon"
 msgstr ""
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1109
+#: engines/scumm/metaengine.cpp:1121
 msgid "Win the bike fight cheat"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1118
+#: engines/scumm/metaengine.cpp:1130
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1196
+#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1208
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1201
+#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1213
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1206
+#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1218
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1211
+#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1216
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1228
 msgid "Stick up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
+#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1235
 #, fuzzy
 msgid "Stick down"
 msgstr "???????????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1230
+#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1242
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick left"
 msgstr "?????????????????? ?????????????????? ?????????"
 
-#: engines/scumm/metaengine.cpp:1161 engines/scumm/metaengine.cpp:1237
+#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1249
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick right"
 msgstr "?????????????????? ?????????????????? ?????????"
 
-#: engines/scumm/metaengine.cpp:1168 engines/scumm/metaengine.cpp:1244
+#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1256
 #, fuzzy
 #| msgid "Move Left"
 msgid "Fire / select"
 msgstr "???????????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1173
+#: engines/scumm/metaengine.cpp:1185
 #, fuzzy
 #| msgid "Move Forward"
 msgid "Menu back"
 msgstr "????????? ?????????"
 
-#: engines/scumm/metaengine.cpp:1178
+#: engines/scumm/metaengine.cpp:1190
 #, fuzzy
 #| msgid "Skip autosave"
 msgid "Skip / menu back"
 msgstr "?????????????????? ??????????????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1194
+#: engines/scumm/metaengine.cpp:1206
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1249
+#: engines/scumm/metaengine.cpp:1261
 #, fuzzy
 #| msgid "Move Forward"
 msgid "Cover / back"
 msgstr "????????? ?????????"
 
-#: engines/scumm/metaengine.cpp:1255
+#: engines/scumm/metaengine.cpp:1267
 #, fuzzy
 #| msgid "Show hidden files"
 msgid "Skip / back"
 msgstr "???????????? ????????? ?????????????????? ?????????????????????"
 
-#: engines/scumm/metaengine.cpp:1262
+#: engines/scumm/metaengine.cpp:1274
 #, fuzzy
 #| msgid "Skip autosave"
 msgid "Skip / menu"
diff --git a/po/hu_HU.po b/po/hu_HU.po
index 39a4aea7ffd..c4b82145e5e 100644
--- a/po/hu_HU.po
+++ b/po/hu_HU.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 19:59+0000\n"
+"POT-Creation-Date: 2026-06-01 21:39+0000\n"
 "PO-Revision-Date: 2025-03-07 00:18+0000\n"
 "Last-Translator: Hov??nszki Tam??s <gahex68379 at lofiey.com>\n"
 "Language-Team: Hungarian <https://translations.scummvm.org/projects/scummvm/"
@@ -1053,7 +1053,7 @@ msgstr "Interakci??"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1076
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1088
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1074,7 +1074,7 @@ msgstr "Fel"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1043
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1055
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1095,7 +1095,7 @@ msgstr "Le"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1056
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1068
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1115,7 +1115,7 @@ msgstr "Bal"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1063
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1075
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3961,7 +3961,7 @@ msgid "Game not implemented"
 msgstr "A j??t??k m??g nem ker??lt implement??l??sra"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
-#: engines/scumm/metaengine.cpp:1026
+#: engines/scumm/metaengine.cpp:1038
 #, fuzzy
 #| msgid "Default game keymap"
 msgid "Default game keymappings"
@@ -6351,7 +6351,7 @@ msgstr "R2"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1183 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1195 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "Men??"
 
@@ -7844,7 +7844,7 @@ msgstr "Harcmodor V??lt??s"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1102 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1114 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -8606,7 +8606,7 @@ msgstr "Besz??l??s/Interakci??"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1089
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1101
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -10848,6 +10848,7 @@ msgid "Player ammo will never decrease."
 msgstr "A j??t??kos l??szere soha nem fog cs??kkenni."
 
 #: engines/hypno/metaengine.cpp:70 engines/scumm/metaengine.cpp:891
+#: engines/scumm/metaengine.cpp:900
 msgid "Unlock all levels"
 msgstr "Minden p??lya el??rhet??v??"
 
@@ -14410,124 +14411,124 @@ msgstr "Magas felbont??s be"
 msgid "Run the game in 640x400 high resolution mode instead of 320x200"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:892
+#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:901
 #, fuzzy
 #| msgid "All levels will be available to play."
 msgid "All levels will be available without requiring passwords"
 msgstr "Minden p??lya el??rhet??v?? v??lik a j??t??kos sz??m??ra."
 
-#: engines/scumm/metaengine.cpp:1037
+#: engines/scumm/metaengine.cpp:1049
 #, fuzzy
 #| msgid "Down Left"
 msgid "Down left"
 msgstr "Balra Le"
 
-#: engines/scumm/metaengine.cpp:1050
+#: engines/scumm/metaengine.cpp:1062
 #, fuzzy
 #| msgid "Down Right"
 msgid "Down right"
 msgstr "Jobbra Le"
 
-#: engines/scumm/metaengine.cpp:1070
+#: engines/scumm/metaengine.cpp:1082
 #, fuzzy
 #| msgid "Upper left item"
 msgid "Up left"
 msgstr "Bal fels?? t??rgy"
 
-#: engines/scumm/metaengine.cpp:1083
+#: engines/scumm/metaengine.cpp:1095
 #, fuzzy
 #| msgid "Up Right"
 msgid "Up right"
 msgstr "Jobbra Fel"
 
-#: engines/scumm/metaengine.cpp:1095
+#: engines/scumm/metaengine.cpp:1107
 msgid "Switch weapon"
 msgstr "Fegyver v??lt??s"
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1109
+#: engines/scumm/metaengine.cpp:1121
 msgid "Win the bike fight cheat"
 msgstr "Csal??s a motoros harc megnyer??s??hez"
 
-#: engines/scumm/metaengine.cpp:1118
+#: engines/scumm/metaengine.cpp:1130
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1196
+#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1208
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1201
+#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1213
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1206
+#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1218
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1211
+#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1216
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1228
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "Felvesz"
 
-#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
+#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1235
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "G??rget??s LE"
 
-#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1230
+#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1242
 #, fuzzy
 #| msgid "Slide Left"
 msgid "Stick left"
 msgstr "Cs??sz??s balra"
 
-#: engines/scumm/metaengine.cpp:1161 engines/scumm/metaengine.cpp:1237
+#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1249
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick right"
 msgstr "Cs??sz??s jobbra"
 
-#: engines/scumm/metaengine.cpp:1168 engines/scumm/metaengine.cpp:1244
+#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1256
 #, fuzzy
 #| msgid "Move left"
 msgid "Fire / select"
 msgstr "Mozg??s balra"
 
-#: engines/scumm/metaengine.cpp:1173
+#: engines/scumm/metaengine.cpp:1185
 #, fuzzy
 #| msgid "Move Back"
 msgid "Menu back"
 msgstr "Mozg??s h??tra"
 
-#: engines/scumm/metaengine.cpp:1178
+#: engines/scumm/metaengine.cpp:1190
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu back"
 msgstr "F?? men?? ??tugr??sa"
 
-#: engines/scumm/metaengine.cpp:1194
+#: engines/scumm/metaengine.cpp:1206
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1249
+#: engines/scumm/metaengine.cpp:1261
 #, fuzzy
 #| msgid "Move Back"
 msgid "Cover / back"
 msgstr "Mozg??s h??tra"
 
-#: engines/scumm/metaengine.cpp:1255
+#: engines/scumm/metaengine.cpp:1267
 #, fuzzy
 #| msgid "Skip dialogue"
 msgid "Skip / back"
 msgstr "Dial??gus ??tugr??sa"
 
-#: engines/scumm/metaengine.cpp:1262
+#: engines/scumm/metaengine.cpp:1274
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu"
diff --git a/po/it_IT.po b/po/it_IT.po
index 573871a85b5..0c23f55eb1b 100644
--- a/po/it_IT.po
+++ b/po/it_IT.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 19:59+0000\n"
+"POT-Creation-Date: 2026-06-01 21:39+0000\n"
 "PO-Revision-Date: 2026-05-25 15:48+0000\n"
 "Last-Translator: Walter Agazzi <walter.agazzi at protonmail.com>\n"
 "Language-Team: Italian <https://translations.scummvm.org/projects/scummvm/"
@@ -1056,7 +1056,7 @@ msgstr "Interazione"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1076
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1088
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1077,7 +1077,7 @@ msgstr "Su"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1043
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1055
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1098,7 +1098,7 @@ msgstr "Gi??"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1056
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1068
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1118,7 +1118,7 @@ msgstr "Sinistra"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1063
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1075
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3923,7 +3923,7 @@ msgid "Game not implemented"
 msgstr "Gioco non implementato"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
-#: engines/scumm/metaengine.cpp:1026
+#: engines/scumm/metaengine.cpp:1038
 msgid "Default game keymappings"
 msgstr "Mappa tasti predefinita"
 
@@ -6148,7 +6148,7 @@ msgstr "R2"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1183 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1195 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "Menu"
 
@@ -7625,7 +7625,7 @@ msgstr "Attiva / Disattiva modalit?? combattimento"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1102 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1114 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -8287,7 +8287,7 @@ msgstr "Parlare / Interazione"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1089
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1101
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -10268,6 +10268,7 @@ msgid "Player ammo will never decrease."
 msgstr "Il giocatore non consumer?? mai munizioni."
 
 #: engines/hypno/metaengine.cpp:70 engines/scumm/metaengine.cpp:891
+#: engines/scumm/metaengine.cpp:900
 msgid "Unlock all levels"
 msgstr "Sblocca tutti i livelli"
 
@@ -13659,116 +13660,116 @@ msgstr "Alta risoluzione attivata"
 msgid "Run the game in 640x400 high resolution mode instead of 320x200"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:892
+#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:901
 #, fuzzy
 #| msgid "All levels will be available to play."
 msgid "All levels will be available without requiring passwords"
 msgstr "Permette l'accesso a qualsiasi livello del gioco."
 
-#: engines/scumm/metaengine.cpp:1037
+#: engines/scumm/metaengine.cpp:1049
 msgid "Down left"
 msgstr "Gi?? sinistra"
 
-#: engines/scumm/metaengine.cpp:1050
+#: engines/scumm/metaengine.cpp:1062
 msgid "Down right"
 msgstr "Gi?? destra"
 
-#: engines/scumm/metaengine.cpp:1070
+#: engines/scumm/metaengine.cpp:1082
 msgid "Up left"
 msgstr "Su sinistra"
 
-#: engines/scumm/metaengine.cpp:1083
+#: engines/scumm/metaengine.cpp:1095
 msgid "Up right"
 msgstr "Su destra"
 
-#: engines/scumm/metaengine.cpp:1095
+#: engines/scumm/metaengine.cpp:1107
 msgid "Switch weapon"
 msgstr "Cambia arma"
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1109
+#: engines/scumm/metaengine.cpp:1121
 msgid "Win the bike fight cheat"
 msgstr "Cheat: Vinci combattimento in moto"
 
-#: engines/scumm/metaengine.cpp:1118
+#: engines/scumm/metaengine.cpp:1130
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1196
+#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1208
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1201
+#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1213
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1206
+#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1218
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1211
+#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1216
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1228
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "Raccogli"
 
-#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
+#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1235
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "Scorri verso il basso"
 
-#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1230
+#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1242
 #, fuzzy
 #| msgid "Slide left"
 msgid "Stick left"
 msgstr "Scorri a sinistra"
 
-#: engines/scumm/metaengine.cpp:1161 engines/scumm/metaengine.cpp:1237
+#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1249
 #, fuzzy
 #| msgid "Slide right"
 msgid "Stick right"
 msgstr "Scorri a destra"
 
-#: engines/scumm/metaengine.cpp:1168 engines/scumm/metaengine.cpp:1244
+#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1256
 #, fuzzy
 #| msgid "Move / Select"
 msgid "Fire / select"
 msgstr "Spostamento / Selezione"
 
-#: engines/scumm/metaengine.cpp:1173
+#: engines/scumm/metaengine.cpp:1185
 #, fuzzy
 #| msgid "Move back"
 msgid "Menu back"
 msgstr "Arretra"
 
-#: engines/scumm/metaengine.cpp:1178
+#: engines/scumm/metaengine.cpp:1190
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu back"
 msgstr "Salta men?? principale"
 
-#: engines/scumm/metaengine.cpp:1194
+#: engines/scumm/metaengine.cpp:1206
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1249
+#: engines/scumm/metaengine.cpp:1261
 #, fuzzy
 #| msgid "Move back"
 msgid "Cover / back"
 msgstr "Arretra"
 
-#: engines/scumm/metaengine.cpp:1255
+#: engines/scumm/metaengine.cpp:1267
 #, fuzzy
 #| msgid "Skip walk"
 msgid "Skip / back"
 msgstr "Salta spostamento"
 
-#: engines/scumm/metaengine.cpp:1262
+#: engines/scumm/metaengine.cpp:1274
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu"
diff --git a/po/ja.po b/po/ja.po
index 0c790856226..f99219eaa3c 100644
--- a/po/ja.po
+++ b/po/ja.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 19:59+0000\n"
+"POT-Creation-Date: 2026-06-01 21:39+0000\n"
 "PO-Revision-Date: 2026-04-26 07:22+0000\n"
 "Last-Translator: VAN-Gluon <van.gluonp at gmail.com>\n"
 "Language-Team: Japanese <https://translations.scummvm.org/projects/scummvm/"
@@ -1042,7 +1042,7 @@ msgstr "??????"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1076
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1088
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1063,7 +1063,7 @@ msgstr "???"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1043
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1055
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1084,7 +1084,7 @@ msgstr "???"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1056
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1068
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1104,7 +1104,7 @@ msgstr "???"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1063
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1075
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3781,7 +3781,7 @@ msgid "Game not implemented"
 msgstr "?????????????????????"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
-#: engines/scumm/metaengine.cpp:1026
+#: engines/scumm/metaengine.cpp:1038
 msgid "Default game keymappings"
 msgstr "?????????????????????(???????????????)"
 
@@ -6002,7 +6002,7 @@ msgstr "R2"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1183 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1195 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "????????????"
 
@@ -7474,7 +7474,7 @@ msgstr "??????????????????"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1102 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1114 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -8235,7 +8235,7 @@ msgstr "??????/??????"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1089
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1101
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -10489,6 +10489,7 @@ msgid "Player ammo will never decrease."
 msgstr "?????????????????????????????????????????????"
 
 #: engines/hypno/metaengine.cpp:70 engines/scumm/metaengine.cpp:891
+#: engines/scumm/metaengine.cpp:900
 msgid "Unlock all levels"
 msgstr "??????????????????"
 
@@ -14054,124 +14055,124 @@ msgstr "????????????ON"
 msgid "Run the game in 640x400 high resolution mode instead of 320x200"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:892
+#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:901
 #, fuzzy
 #| msgid "All levels will be available to play."
 msgid "All levels will be available without requiring passwords"
 msgstr "?????????????????????????????????????????????"
 
-#: engines/scumm/metaengine.cpp:1037
+#: engines/scumm/metaengine.cpp:1049
 #, fuzzy
 #| msgid "Down Left"
 msgid "Down left"
 msgstr "??????"
 
-#: engines/scumm/metaengine.cpp:1050
+#: engines/scumm/metaengine.cpp:1062
 #, fuzzy
 #| msgid "Down Right"
 msgid "Down right"
 msgstr "??????"
 
-#: engines/scumm/metaengine.cpp:1070
+#: engines/scumm/metaengine.cpp:1082
 #, fuzzy
 #| msgid "Upper left item"
 msgid "Up left"
 msgstr "?????????????????????"
 
-#: engines/scumm/metaengine.cpp:1083
+#: engines/scumm/metaengine.cpp:1095
 #, fuzzy
 #| msgid "Up Right"
 msgid "Up right"
 msgstr "??????"
 
-#: engines/scumm/metaengine.cpp:1095
+#: engines/scumm/metaengine.cpp:1107
 msgid "Switch weapon"
 msgstr "?????????????????????"
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1109
+#: engines/scumm/metaengine.cpp:1121
 msgid "Win the bike fight cheat"
 msgstr "????????????????????????"
 
-#: engines/scumm/metaengine.cpp:1118
+#: engines/scumm/metaengine.cpp:1130
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1196
+#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1208
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1201
+#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1213
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1206
+#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1218
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1211
+#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1216
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1228
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "??????"
 
-#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
+#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1235
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "?????????????????????"
 
-#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1230
+#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1242
 #, fuzzy
 #| msgid "Slide Left"
 msgid "Stick left"
 msgstr "??????????????????"
 
-#: engines/scumm/metaengine.cpp:1161 engines/scumm/metaengine.cpp:1237
+#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1249
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick right"
 msgstr "??????????????????"
 
-#: engines/scumm/metaengine.cpp:1168 engines/scumm/metaengine.cpp:1244
+#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1256
 #, fuzzy
 #| msgid "Move left"
 msgid "Fire / select"
 msgstr "????????????"
 
-#: engines/scumm/metaengine.cpp:1173
+#: engines/scumm/metaengine.cpp:1185
 #, fuzzy
 #| msgid "Move Back"
 msgid "Menu back"
 msgstr "??????"
 
-#: engines/scumm/metaengine.cpp:1178
+#: engines/scumm/metaengine.cpp:1190
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu back"
 msgstr "????????????????????????????????????"
 
-#: engines/scumm/metaengine.cpp:1194
+#: engines/scumm/metaengine.cpp:1206
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1249
+#: engines/scumm/metaengine.cpp:1261
 #, fuzzy
 #| msgid "Move Back"
 msgid "Cover / back"
 msgstr "??????"
 
-#: engines/scumm/metaengine.cpp:1255
+#: engines/scumm/metaengine.cpp:1267
 #, fuzzy
 #| msgid "Skip dialogue"
 msgid "Skip / back"
 msgstr "??????????????????????????????"
 
-#: engines/scumm/metaengine.cpp:1262
+#: engines/scumm/metaengine.cpp:1274
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu"
diff --git a/po/ka.po b/po/ka.po
index a9881fbb316..5a8100cbbdd 100644
--- a/po/ka.po
+++ b/po/ka.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 19:59+0000\n"
+"POT-Creation-Date: 2026-06-01 21:39+0000\n"
 "PO-Revision-Date: 2026-05-09 12:22+0000\n"
 "Last-Translator: NorwayFun <temuri.doghonadze at gmail.com>\n"
 "Language-Team: Georgian <https://translations.scummvm.org/projects/scummvm/"
@@ -1021,7 +1021,7 @@ msgstr "??????????????????????????????"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1076
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1088
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1042,7 +1042,7 @@ msgstr "?????????????????????"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1043
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1055
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1063,7 +1063,7 @@ msgstr "??????????????????"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1056
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1068
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1083,7 +1083,7 @@ msgstr "????????????????????????"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1063
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1075
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3606,7 +3606,7 @@ msgid "Game not implemented"
 msgstr "?????????????????? ????????????????????????????????????????????? ????????????"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
-#: engines/scumm/metaengine.cpp:1026
+#: engines/scumm/metaengine.cpp:1038
 msgid "Default game keymappings"
 msgstr "???????????????????????????????????? ??????????????????????????? ??????????????????????????? ??????????????????"
 
@@ -5463,7 +5463,7 @@ msgstr "R2"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1183 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1195 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "???????????????"
 
@@ -6830,7 +6830,7 @@ msgstr "????????????????????? ????????????????????????"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1102 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1114 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -7477,7 +7477,7 @@ msgstr "????????????????????? / ??????????????????????????????"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1089
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1101
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -9366,6 +9366,7 @@ msgid "Player ammo will never decrease."
 msgstr ""
 
 #: engines/hypno/metaengine.cpp:70 engines/scumm/metaengine.cpp:891
+#: engines/scumm/metaengine.cpp:900
 msgid "Unlock all levels"
 msgstr "??????????????? ??????????????? ???????????????????????????"
 
@@ -12427,116 +12428,116 @@ msgstr "?????????????????? ????????????????????????????????? ??????????????????"
 msgid "Run the game in 640x400 high resolution mode instead of 320x200"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:892
+#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:901
 #, fuzzy
 #| msgid "All levels will be available to play."
 msgid "All levels will be available without requiring passwords"
 msgstr "??????????????????????????? ??????????????????????????????????????? ?????????????????? ??????????????? ????????????."
 
-#: engines/scumm/metaengine.cpp:1037
+#: engines/scumm/metaengine.cpp:1049
 msgid "Down left"
 msgstr "??????????????? ????????????????????????"
 
-#: engines/scumm/metaengine.cpp:1050
+#: engines/scumm/metaengine.cpp:1062
 msgid "Down right"
 msgstr "??????????????? ????????????????????????"
 
-#: engines/scumm/metaengine.cpp:1070
+#: engines/scumm/metaengine.cpp:1082
 msgid "Up left"
 msgstr "???????????? ????????????????????????"
 
-#: engines/scumm/metaengine.cpp:1083
+#: engines/scumm/metaengine.cpp:1095
 msgid "Up right"
 msgstr "??????????????? ????????????????????????"
 
-#: engines/scumm/metaengine.cpp:1095
+#: engines/scumm/metaengine.cpp:1107
 msgid "Switch weapon"
 msgstr "????????????????????? ????????????????????????"
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1109
+#: engines/scumm/metaengine.cpp:1121
 msgid "Win the bike fight cheat"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1118
+#: engines/scumm/metaengine.cpp:1130
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1196
+#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1208
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1201
+#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1213
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1206
+#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1218
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1211
+#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1216
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1228
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "???????????????"
 
-#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
+#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1235
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "??????????????? ????????????????????????"
 
-#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1230
+#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1242
 #, fuzzy
 #| msgid "Slide left"
 msgid "Stick left"
 msgstr "?????????????????????????????? ????????????????????????"
 
-#: engines/scumm/metaengine.cpp:1161 engines/scumm/metaengine.cpp:1237
+#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1249
 #, fuzzy
 #| msgid "Slide right"
 msgid "Stick right"
 msgstr "?????????????????????????????? ????????????????????????"
 
-#: engines/scumm/metaengine.cpp:1168 engines/scumm/metaengine.cpp:1244
+#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1256
 #, fuzzy
 #| msgid "Move / Select"
 msgid "Fire / select"
 msgstr "???????????????????????????????????? / ????????????????????????"
 
-#: engines/scumm/metaengine.cpp:1173
+#: engines/scumm/metaengine.cpp:1185
 #, fuzzy
 #| msgid "Move back"
 msgid "Menu back"
 msgstr "???????????? ????????????????????????"
 
-#: engines/scumm/metaengine.cpp:1178
+#: engines/scumm/metaengine.cpp:1190
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu back"
 msgstr "????????????????????? ?????????????????? ??????????????????????????????"
 
-#: engines/scumm/metaengine.cpp:1194
+#: engines/scumm/metaengine.cpp:1206
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1249
+#: engines/scumm/metaengine.cpp:1261
 #, fuzzy
 #| msgid "Move back"
 msgid "Cover / back"
 msgstr "???????????? ????????????????????????"
 
-#: engines/scumm/metaengine.cpp:1255
+#: engines/scumm/metaengine.cpp:1267
 #, fuzzy
 #| msgid "Skip walk"
 msgid "Skip / back"
 msgstr "???????????????????????? ??????????????????????????????"
 
-#: engines/scumm/metaengine.cpp:1262
+#: engines/scumm/metaengine.cpp:1274
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu"
diff --git a/po/ko.po b/po/ko.po
index 2be315c018c..45cc119277b 100644
--- a/po/ko.po
+++ b/po/ko.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 19:59+0000\n"
+"POT-Creation-Date: 2026-06-01 21:39+0000\n"
 "PO-Revision-Date: 2026-05-19 20:22+0000\n"
 "Last-Translator: Hoseok Seo <ddinghoya at gmail.com>\n"
 "Language-Team: Korean <https://translations.scummvm.org/projects/scummvm/"
@@ -1044,7 +1044,7 @@ msgstr "??????"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1076
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1088
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1065,7 +1065,7 @@ msgstr "??????"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1043
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1055
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1086,7 +1086,7 @@ msgstr "?????????"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1056
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1068
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1106,7 +1106,7 @@ msgstr "??????"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1063
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1075
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3835,7 +3835,7 @@ msgid "Game not implemented"
 msgstr "????????? ???????????? ?????????"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
-#: engines/scumm/metaengine.cpp:1026
+#: engines/scumm/metaengine.cpp:1038
 msgid "Default game keymappings"
 msgstr "?????? ?????? ?????????"
 
@@ -6013,7 +6013,7 @@ msgstr "R2 ??????"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1183 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1195 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "??????"
 
@@ -7444,7 +7444,7 @@ msgstr "?????? ??????"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1102 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1114 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -8106,7 +8106,7 @@ msgstr "?????? / ??????"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1089
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1101
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -10056,6 +10056,7 @@ msgid "Player ammo will never decrease."
 msgstr "??????????????? ????????? ???????????? ????????????."
 
 #: engines/hypno/metaengine.cpp:70 engines/scumm/metaengine.cpp:891
+#: engines/scumm/metaengine.cpp:900
 msgid "Unlock all levels"
 msgstr "?????? ?????? ?????? ??????"
 
@@ -13339,116 +13340,116 @@ msgstr "???????????? ???"
 msgid "Run the game in 640x400 high resolution mode instead of 320x200"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:892
+#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:901
 #, fuzzy
 #| msgid "All levels will be available to play."
 msgid "All levels will be available without requiring passwords"
 msgstr "?????? ???????????? ???????????? ??? ????????????."
 
-#: engines/scumm/metaengine.cpp:1037
+#: engines/scumm/metaengine.cpp:1049
 msgid "Down left"
 msgstr "?????? ??????"
 
-#: engines/scumm/metaengine.cpp:1050
+#: engines/scumm/metaengine.cpp:1062
 msgid "Down right"
 msgstr "????????? ??????"
 
-#: engines/scumm/metaengine.cpp:1070
+#: engines/scumm/metaengine.cpp:1082
 msgid "Up left"
 msgstr "?????? ??????"
 
-#: engines/scumm/metaengine.cpp:1083
+#: engines/scumm/metaengine.cpp:1095
 msgid "Up right"
 msgstr "????????? ??????"
 
-#: engines/scumm/metaengine.cpp:1095
+#: engines/scumm/metaengine.cpp:1107
 msgid "Switch weapon"
 msgstr "?????? ??????"
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1109
+#: engines/scumm/metaengine.cpp:1121
 msgid "Win the bike fight cheat"
 msgstr "????????? ???????????? ???????????? ??????"
 
-#: engines/scumm/metaengine.cpp:1118
+#: engines/scumm/metaengine.cpp:1130
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1196
+#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1208
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1201
+#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1213
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1206
+#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1218
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1211
+#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1216
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1228
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "??????"
 
-#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
+#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1235
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "????????? ?????????"
 
-#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1230
+#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1242
 #, fuzzy
 #| msgid "Slide left"
 msgid "Stick left"
 msgstr "???????????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1161 engines/scumm/metaengine.cpp:1237
+#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1249
 #, fuzzy
 #| msgid "Slide right"
 msgid "Stick right"
 msgstr "????????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1168 engines/scumm/metaengine.cpp:1244
+#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1256
 #, fuzzy
 #| msgid "Move / Select"
 msgid "Fire / select"
 msgstr "?????? / ??????"
 
-#: engines/scumm/metaengine.cpp:1173
+#: engines/scumm/metaengine.cpp:1185
 #, fuzzy
 #| msgid "Move back"
 msgid "Menu back"
 msgstr "?????? ??????"
 
-#: engines/scumm/metaengine.cpp:1178
+#: engines/scumm/metaengine.cpp:1190
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu back"
 msgstr "?????? ?????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1194
+#: engines/scumm/metaengine.cpp:1206
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1249
+#: engines/scumm/metaengine.cpp:1261
 #, fuzzy
 #| msgid "Move back"
 msgid "Cover / back"
 msgstr "?????? ??????"
 
-#: engines/scumm/metaengine.cpp:1255
+#: engines/scumm/metaengine.cpp:1267
 #, fuzzy
 #| msgid "Skip walk"
 msgid "Skip / back"
 msgstr "?????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1262
+#: engines/scumm/metaengine.cpp:1274
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu"
diff --git a/po/nb_NO.po b/po/nb_NO.po
index 874a2eec80b..c400a547a2d 100644
--- a/po/nb_NO.po
+++ b/po/nb_NO.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 19:59+0000\n"
+"POT-Creation-Date: 2026-06-01 21:39+0000\n"
 "PO-Revision-Date: 2026-02-07 15:22+0000\n"
 "Last-Translator: Einar Johan Tr??an S??m??en <einarjohants at gmail.com>\n"
 "Language-Team: Norwegian Bokm??l <https://translations.scummvm.org/projects/"
@@ -1052,7 +1052,7 @@ msgstr "Samhandle"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1076
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1088
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1073,7 +1073,7 @@ msgstr "Opp"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1043
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1055
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1094,7 +1094,7 @@ msgstr "Ned"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1056
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1068
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1114,7 +1114,7 @@ msgstr "Venstre"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1063
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1075
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3963,7 +3963,7 @@ msgid "Game not implemented"
 msgstr "Spillet er ikke implementert"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
-#: engines/scumm/metaengine.cpp:1026
+#: engines/scumm/metaengine.cpp:1038
 #, fuzzy
 #| msgid "Default game keymap"
 msgid "Default game keymappings"
@@ -6412,7 +6412,7 @@ msgstr "R2"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1183 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1195 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "Meny"
 
@@ -7910,7 +7910,7 @@ msgstr "Skru kampmodus av/p??"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1102 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1114 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -8678,7 +8678,7 @@ msgstr "Konverser/Interager"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1089
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1101
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -10930,6 +10930,7 @@ msgid "Player ammo will never decrease."
 msgstr "Spillerens ammunisjon minsker aldri."
 
 #: engines/hypno/metaengine.cpp:70 engines/scumm/metaengine.cpp:891
+#: engines/scumm/metaengine.cpp:900
 msgid "Unlock all levels"
 msgstr "L??ser opp alle niv??er"
 
@@ -14501,124 +14502,124 @@ msgstr "H??y oppl??sning p??"
 msgid "Run the game in 640x400 high resolution mode instead of 320x200"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:892
+#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:901
 #, fuzzy
 #| msgid "All levels will be available to play."
 msgid "All levels will be available without requiring passwords"
 msgstr "Alle niv??er ??pnes for spilling."
 
-#: engines/scumm/metaengine.cpp:1037
+#: engines/scumm/metaengine.cpp:1049
 #, fuzzy
 #| msgid "Down Left"
 msgid "Down left"
 msgstr "Ned til venstre"
 
-#: engines/scumm/metaengine.cpp:1050
+#: engines/scumm/metaengine.cpp:1062
 #, fuzzy
 #| msgid "Down Right"
 msgid "Down right"
 msgstr "Ned til h??yre"
 
-#: engines/scumm/metaengine.cpp:1070
+#: engines/scumm/metaengine.cpp:1082
 #, fuzzy
 #| msgid "Upper left item"
 msgid "Up left"
 msgstr "??vre venstre gjenstand"
 
-#: engines/scumm/metaengine.cpp:1083
+#: engines/scumm/metaengine.cpp:1095
 #, fuzzy
 #| msgid "Up Right"
 msgid "Up right"
 msgstr "Opp til h??yre"
 
-#: engines/scumm/metaengine.cpp:1095
+#: engines/scumm/metaengine.cpp:1107
 msgid "Switch weapon"
 msgstr "Bytte v??pen"
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1109
+#: engines/scumm/metaengine.cpp:1121
 msgid "Win the bike fight cheat"
 msgstr "Juksefunksjon for ?? vinne motorsykkelkamper"
 
-#: engines/scumm/metaengine.cpp:1118
+#: engines/scumm/metaengine.cpp:1130
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1196
+#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1208
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1201
+#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1213
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1206
+#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1218
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1211
+#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1216
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1228
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "Plukk opp"
 
-#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
+#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1235
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "Bla ned"
 
-#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1230
+#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1242
 #, fuzzy
 #| msgid "Slide Left"
 msgid "Stick left"
 msgstr "Skli mot Venstre"
 
-#: engines/scumm/metaengine.cpp:1161 engines/scumm/metaengine.cpp:1237
+#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1249
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick right"
 msgstr "Skli mot H??yre"
 
-#: engines/scumm/metaengine.cpp:1168 engines/scumm/metaengine.cpp:1244
+#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1256
 #, fuzzy
 #| msgid "Move left"
 msgid "Fire / select"
 msgstr "G?? til venstre"
 
-#: engines/scumm/metaengine.cpp:1173
+#: engines/scumm/metaengine.cpp:1185
 #, fuzzy
 #| msgid "Move Back"
 msgid "Menu back"
 msgstr "Beveg Bakover"
 
-#: engines/scumm/metaengine.cpp:1178
+#: engines/scumm/metaengine.cpp:1190
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu back"
 msgstr "Hopp over hovedmeny"
 
-#: engines/scumm/metaengine.cpp:1194
+#: engines/scumm/metaengine.cpp:1206
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1249
+#: engines/scumm/metaengine.cpp:1261
 #, fuzzy
 #| msgid "Move Back"
 msgid "Cover / back"
 msgstr "Beveg Bakover"
 
-#: engines/scumm/metaengine.cpp:1255
+#: engines/scumm/metaengine.cpp:1267
 #, fuzzy
 #| msgid "Skip dialogue"
 msgid "Skip / back"
 msgstr "Hopp over dialog"
 
-#: engines/scumm/metaengine.cpp:1262
+#: engines/scumm/metaengine.cpp:1274
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu"
diff --git a/po/nl_NL.po b/po/nl_NL.po
index 9b7e4e66f9e..8cfbbaecb6a 100644
--- a/po/nl_NL.po
+++ b/po/nl_NL.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.9.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 19:59+0000\n"
+"POT-Creation-Date: 2026-06-01 21:39+0000\n"
 "PO-Revision-Date: 2026-02-11 14:23+0000\n"
 "Last-Translator: SecularSteve <fairfull.playing at gmail.com>\n"
 "Language-Team: Dutch <https://translations.scummvm.org/projects/scummvm/"
@@ -1053,7 +1053,7 @@ msgstr "Interactie"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1076
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1088
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1074,7 +1074,7 @@ msgstr "Omhoog"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1043
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1055
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1095,7 +1095,7 @@ msgstr "Omlaag"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1056
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1068
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1115,7 +1115,7 @@ msgstr "Links"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1063
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1075
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3896,7 +3896,7 @@ msgid "Game not implemented"
 msgstr "Spel niet ge??mplementeerd"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
-#: engines/scumm/metaengine.cpp:1026
+#: engines/scumm/metaengine.cpp:1038
 msgid "Default game keymappings"
 msgstr "Standaard speltoetsenkoppelingen"
 
@@ -6087,7 +6087,7 @@ msgstr "R2"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1183 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1195 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "Menu"
 
@@ -7552,7 +7552,7 @@ msgstr "Schakel gevecht in/uit"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1102 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1114 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -8251,7 +8251,7 @@ msgstr "Praten / Interactie"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1089
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1101
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -10317,6 +10317,7 @@ msgid "Player ammo will never decrease."
 msgstr "Munitie van speler zal niet verminderen."
 
 #: engines/hypno/metaengine.cpp:70 engines/scumm/metaengine.cpp:891
+#: engines/scumm/metaengine.cpp:900
 msgid "Unlock all levels"
 msgstr "Geef alle levels vrij"
 
@@ -13702,116 +13703,116 @@ msgstr "Hoge resolutie aan"
 msgid "Run the game in 640x400 high resolution mode instead of 320x200"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:892
+#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:901
 #, fuzzy
 #| msgid "All levels will be available to play."
 msgid "All levels will be available without requiring passwords"
 msgstr "Alle levels kunnen dan gespeeld worden."
 
-#: engines/scumm/metaengine.cpp:1037
+#: engines/scumm/metaengine.cpp:1049
 msgid "Down left"
 msgstr "Linksonder"
 
-#: engines/scumm/metaengine.cpp:1050
+#: engines/scumm/metaengine.cpp:1062
 msgid "Down right"
 msgstr "Rechtsonder"
 
-#: engines/scumm/metaengine.cpp:1070
+#: engines/scumm/metaengine.cpp:1082
 msgid "Up left"
 msgstr "Linksboven"
 
-#: engines/scumm/metaengine.cpp:1083
+#: engines/scumm/metaengine.cpp:1095
 msgid "Up right"
 msgstr "Rechtop"
 
-#: engines/scumm/metaengine.cpp:1095
+#: engines/scumm/metaengine.cpp:1107
 msgid "Switch weapon"
 msgstr "Wissel wapen"
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1109
+#: engines/scumm/metaengine.cpp:1121
 msgid "Win the bike fight cheat"
 msgstr "Win de fietsstrijd-cheat"
 
-#: engines/scumm/metaengine.cpp:1118
+#: engines/scumm/metaengine.cpp:1130
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1196
+#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1208
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1201
+#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1213
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1206
+#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1218
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1211
+#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1216
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1228
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "Pak op"
 
-#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
+#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1235
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "Scroll omlaag"
 
-#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1230
+#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1242
 #, fuzzy
 #| msgid "Slide left"
 msgid "Stick left"
 msgstr "Schuif naar link"
 
-#: engines/scumm/metaengine.cpp:1161 engines/scumm/metaengine.cpp:1237
+#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1249
 #, fuzzy
 #| msgid "Slide right"
 msgid "Stick right"
 msgstr "Schuif naar rechts"
 
-#: engines/scumm/metaengine.cpp:1168 engines/scumm/metaengine.cpp:1244
+#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1256
 #, fuzzy
 #| msgid "Move / Select"
 msgid "Fire / select"
 msgstr "Verplaatsen / Selecteren"
 
-#: engines/scumm/metaengine.cpp:1173
+#: engines/scumm/metaengine.cpp:1185
 #, fuzzy
 #| msgid "Move back"
 msgid "Menu back"
 msgstr "Teruggaan"
 
-#: engines/scumm/metaengine.cpp:1178
+#: engines/scumm/metaengine.cpp:1190
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu back"
 msgstr "Hoofdmenu overslaan"
 
-#: engines/scumm/metaengine.cpp:1194
+#: engines/scumm/metaengine.cpp:1206
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1249
+#: engines/scumm/metaengine.cpp:1261
 #, fuzzy
 #| msgid "Move back"
 msgid "Cover / back"
 msgstr "Teruggaan"
 
-#: engines/scumm/metaengine.cpp:1255
+#: engines/scumm/metaengine.cpp:1267
 #, fuzzy
 #| msgid "Skip walk"
 msgid "Skip / back"
 msgstr "Sla wandeling over"
 
-#: engines/scumm/metaengine.cpp:1262
+#: engines/scumm/metaengine.cpp:1274
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu"
diff --git a/po/nn_NO.po b/po/nn_NO.po
index 7b09e82786f..1dc2e56767d 100644
--- a/po/nn_NO.po
+++ b/po/nn_NO.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 19:59+0000\n"
+"POT-Creation-Date: 2026-06-01 21:39+0000\n"
 "PO-Revision-Date: 2023-12-14 14:28+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Norwegian Nynorsk <https://translations.scummvm.org/projects/"
@@ -1057,7 +1057,7 @@ msgstr "Interager"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1076
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1088
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1078,7 +1078,7 @@ msgstr "Opp"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1043
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1055
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1099,7 +1099,7 @@ msgstr "Ned"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1056
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1068
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1119,7 +1119,7 @@ msgstr "Venstre"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1063
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1075
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3822,7 +3822,7 @@ msgid "Game not implemented"
 msgstr "Spel ID ikkje st??tta"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
-#: engines/scumm/metaengine.cpp:1026
+#: engines/scumm/metaengine.cpp:1038
 #, fuzzy
 #| msgid "Game Options"
 msgid "Default game keymappings"
@@ -5814,7 +5814,7 @@ msgstr ""
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1183 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1195 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "Meny"
 
@@ -7288,7 +7288,7 @@ msgstr "Veksle musl??sing"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1102 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1114 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -8036,7 +8036,7 @@ msgstr "Interpolering:"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1089
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1101
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -10250,6 +10250,7 @@ msgid "Player ammo will never decrease."
 msgstr ""
 
 #: engines/hypno/metaengine.cpp:70 engines/scumm/metaengine.cpp:891
+#: engines/scumm/metaengine.cpp:900
 msgid "Unlock all levels"
 msgstr ""
 
@@ -13630,116 +13631,116 @@ msgstr "Nytt h??goppl??yseleg grafikk"
 msgid "Run the game in 640x400 high resolution mode instead of 320x200"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:892
+#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:901
 msgid "All levels will be available without requiring passwords"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1037
+#: engines/scumm/metaengine.cpp:1049
 #, fuzzy
 msgid "Down left"
 msgstr "Snu til Venstre"
 
-#: engines/scumm/metaengine.cpp:1050
+#: engines/scumm/metaengine.cpp:1062
 #, fuzzy
 msgid "Down right"
 msgstr "Snu til H??yre"
 
-#: engines/scumm/metaengine.cpp:1070
+#: engines/scumm/metaengine.cpp:1082
 #, fuzzy
 #| msgid "Upper left item"
 msgid "Up left"
 msgstr "??vre venstre gjenstand"
 
-#: engines/scumm/metaengine.cpp:1083
+#: engines/scumm/metaengine.cpp:1095
 #, fuzzy
 msgid "Up right"
 msgstr "H??gre"
 
-#: engines/scumm/metaengine.cpp:1095
+#: engines/scumm/metaengine.cpp:1107
 #, fuzzy
 msgid "Switch weapon"
 msgstr "Veksle karakterar:"
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1109
+#: engines/scumm/metaengine.cpp:1121
 msgid "Win the bike fight cheat"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1118
+#: engines/scumm/metaengine.cpp:1130
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1196
+#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1208
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1201
+#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1213
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1206
+#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1218
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1211
+#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1216
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1228
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "Plukk opp"
 
-#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
+#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1235
 #, fuzzy
 msgid "Stick down"
 msgstr "Bla liste ned"
 
-#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1230
+#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1242
 #, fuzzy
 #| msgid "Slide Left"
 msgid "Stick left"
 msgstr "Skli til Venstre"
 
-#: engines/scumm/metaengine.cpp:1161 engines/scumm/metaengine.cpp:1237
+#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1249
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick right"
 msgstr "Skli til H??yre"
 
-#: engines/scumm/metaengine.cpp:1168 engines/scumm/metaengine.cpp:1244
+#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1256
 #, fuzzy
 msgid "Fire / select"
 msgstr "Skli til Venstre"
 
-#: engines/scumm/metaengine.cpp:1173
+#: engines/scumm/metaengine.cpp:1185
 #, fuzzy
 #| msgid "Move Back"
 msgid "Menu back"
 msgstr "Beveg Bakover"
 
-#: engines/scumm/metaengine.cpp:1178
+#: engines/scumm/metaengine.cpp:1190
 #, fuzzy
 msgid "Skip / menu back"
 msgstr "Tilbake til Oppsta~r~tar"
 
-#: engines/scumm/metaengine.cpp:1194
+#: engines/scumm/metaengine.cpp:1206
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1249
+#: engines/scumm/metaengine.cpp:1261
 #, fuzzy
 #| msgid "Move Back"
 msgid "Cover / back"
 msgstr "Beveg Bakover"
 
-#: engines/scumm/metaengine.cpp:1255
+#: engines/scumm/metaengine.cpp:1267
 #, fuzzy
 msgid "Skip / back"
 msgstr "Hopp over linje"
 
-#: engines/scumm/metaengine.cpp:1262
+#: engines/scumm/metaengine.cpp:1274
 #, fuzzy
 msgid "Skip / menu"
 msgstr "Tilbake til Oppsta~r~tar"
diff --git a/po/pl_PL.po b/po/pl_PL.po
index 31cc9786123..d6c06687c4c 100644
--- a/po/pl_PL.po
+++ b/po/pl_PL.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 19:59+0000\n"
+"POT-Creation-Date: 2026-06-01 21:39+0000\n"
 "PO-Revision-Date: 2026-04-17 00:22+0000\n"
 "Last-Translator: SkiffPL <skiff at windowslive.com>\n"
 "Language-Team: Polish <https://translations.scummvm.org/projects/scummvm/"
@@ -1054,7 +1054,7 @@ msgstr "Interakcja"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1076
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1088
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1075,7 +1075,7 @@ msgstr "W g??r??"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1043
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1055
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1096,7 +1096,7 @@ msgstr "W d????"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1056
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1068
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1116,7 +1116,7 @@ msgstr "W lewo"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1063
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1075
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3887,7 +3887,7 @@ msgid "Game not implemented"
 msgstr "Gra nie jest obs??ugiwana"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
-#: engines/scumm/metaengine.cpp:1026
+#: engines/scumm/metaengine.cpp:1038
 msgid "Default game keymappings"
 msgstr "Domy??lne mapowanie klawiatury"
 
@@ -6099,7 +6099,7 @@ msgstr "R2"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1183 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1195 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "Menu"
 
@@ -7566,7 +7566,7 @@ msgstr "Prze????cz tryb walki"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1102 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1114 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -8234,7 +8234,7 @@ msgstr "Rozmowa/interakcja"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1089
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1101
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -10427,6 +10427,7 @@ msgid "Player ammo will never decrease."
 msgstr "Amunicja gracza nigdy si?? nie zmniejszy."
 
 #: engines/hypno/metaengine.cpp:70 engines/scumm/metaengine.cpp:891
+#: engines/scumm/metaengine.cpp:900
 msgid "Unlock all levels"
 msgstr "Odblokuj wszystkie poziomy"
 
@@ -13881,116 +13882,116 @@ msgstr "Wysoka rozdzielczo???? w????czona"
 msgid "Run the game in 640x400 high resolution mode instead of 320x200"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:892
+#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:901
 #, fuzzy
 #| msgid "All levels will be available to play."
 msgid "All levels will be available without requiring passwords"
 msgstr "Wszystkie poziomy gry b??d?? dost??pne."
 
-#: engines/scumm/metaengine.cpp:1037
+#: engines/scumm/metaengine.cpp:1049
 msgid "Down left"
 msgstr "W d???? w lewo"
 
-#: engines/scumm/metaengine.cpp:1050
+#: engines/scumm/metaengine.cpp:1062
 msgid "Down right"
 msgstr "W d???? w prawo"
 
-#: engines/scumm/metaengine.cpp:1070
+#: engines/scumm/metaengine.cpp:1082
 msgid "Up left"
 msgstr "W g??r?? na lewo"
 
-#: engines/scumm/metaengine.cpp:1083
+#: engines/scumm/metaengine.cpp:1095
 msgid "Up right"
 msgstr "W g??r?? w prawo"
 
-#: engines/scumm/metaengine.cpp:1095
+#: engines/scumm/metaengine.cpp:1107
 msgid "Switch weapon"
 msgstr "Zmie?? bro??"
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1109
+#: engines/scumm/metaengine.cpp:1121
 msgid "Win the bike fight cheat"
 msgstr "Cheat 'wygraj walk?? na motocyklu'"
 
-#: engines/scumm/metaengine.cpp:1118
+#: engines/scumm/metaengine.cpp:1130
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1196
+#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1208
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1201
+#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1213
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1206
+#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1218
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1211
+#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1216
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1228
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "Podnie??"
 
-#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
+#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1235
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "Przewi?? w d????"
 
-#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1230
+#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1242
 #, fuzzy
 #| msgid "Slide left"
 msgid "Stick left"
 msgstr "??lizg w lewo"
 
-#: engines/scumm/metaengine.cpp:1161 engines/scumm/metaengine.cpp:1237
+#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1249
 #, fuzzy
 #| msgid "Slide right"
 msgid "Stick right"
 msgstr "??lizg w prawo"
 
-#: engines/scumm/metaengine.cpp:1168 engines/scumm/metaengine.cpp:1244
+#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1256
 #, fuzzy
 #| msgid "Move / Select"
 msgid "Fire / select"
 msgstr "Ruch / Zaznacz"
 
-#: engines/scumm/metaengine.cpp:1173
+#: engines/scumm/metaengine.cpp:1185
 #, fuzzy
 #| msgid "Move back"
 msgid "Menu back"
 msgstr "Ruch do ty??u"
 
-#: engines/scumm/metaengine.cpp:1178
+#: engines/scumm/metaengine.cpp:1190
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu back"
 msgstr "Pomijaj menu g????wne"
 
-#: engines/scumm/metaengine.cpp:1194
+#: engines/scumm/metaengine.cpp:1206
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1249
+#: engines/scumm/metaengine.cpp:1261
 #, fuzzy
 #| msgid "Move back"
 msgid "Cover / back"
 msgstr "Ruch do ty??u"
 
-#: engines/scumm/metaengine.cpp:1255
+#: engines/scumm/metaengine.cpp:1267
 #, fuzzy
 #| msgid "Skip dialog"
 msgid "Skip / back"
 msgstr "Pomi?? dialog"
 
-#: engines/scumm/metaengine.cpp:1262
+#: engines/scumm/metaengine.cpp:1274
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu"
diff --git a/po/pt_BR.po b/po/pt_BR.po
index 0103943b107..13523cf72cc 100644
--- a/po/pt_BR.po
+++ b/po/pt_BR.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 19:59+0000\n"
+"POT-Creation-Date: 2026-06-01 21:39+0000\n"
 "PO-Revision-Date: 2024-11-16 02:13+0000\n"
 "Last-Translator: Marcel Souza Lemes <marcosoutsider at gmail.com>\n"
 "Language-Team: Portuguese (Brazil) <https://translations.scummvm.org/"
@@ -1059,7 +1059,7 @@ msgstr "Interagir"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1076
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1088
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1080,7 +1080,7 @@ msgstr "Cima"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1043
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1055
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1101,7 +1101,7 @@ msgstr "Baixo"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1056
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1068
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1121,7 +1121,7 @@ msgstr "Esquerda"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1063
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1075
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3981,7 +3981,7 @@ msgid "Game not implemented"
 msgstr "Jogo n??o implementado"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
-#: engines/scumm/metaengine.cpp:1026
+#: engines/scumm/metaengine.cpp:1038
 #, fuzzy
 #| msgid "Default game keymap"
 msgid "Default game keymappings"
@@ -6376,7 +6376,7 @@ msgstr "R2"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1183 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1195 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "Menu"
 
@@ -7881,7 +7881,7 @@ msgstr "Alternar Modo de Combate"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1102 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1114 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -8645,7 +8645,7 @@ msgstr "Falar/Interagir"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1089
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1101
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -10889,6 +10889,7 @@ msgid "Player ammo will never decrease."
 msgstr "A muni????o do jogador nunca diminuir??."
 
 #: engines/hypno/metaengine.cpp:70 engines/scumm/metaengine.cpp:891
+#: engines/scumm/metaengine.cpp:900
 msgid "Unlock all levels"
 msgstr "Desbloquear todos os n??veis"
 
@@ -14473,124 +14474,124 @@ msgstr "Alta resolu????o ativada"
 msgid "Run the game in 640x400 high resolution mode instead of 320x200"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:892
+#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:901
 #, fuzzy
 #| msgid "All levels will be available to play."
 msgid "All levels will be available without requiring passwords"
 msgstr "Todos os n??veis estar??o dispon??veis para jogar."
 
-#: engines/scumm/metaengine.cpp:1037
+#: engines/scumm/metaengine.cpp:1049
 #, fuzzy
 #| msgid "Down Left"
 msgid "Down left"
 msgstr "Para Baixo ?? Esquerda"
 
-#: engines/scumm/metaengine.cpp:1050
+#: engines/scumm/metaengine.cpp:1062
 #, fuzzy
 #| msgid "Down Right"
 msgid "Down right"
 msgstr "Para Baixo ?? Direita"
 
-#: engines/scumm/metaengine.cpp:1070
+#: engines/scumm/metaengine.cpp:1082
 #, fuzzy
 #| msgid "Upper left item"
 msgid "Up left"
 msgstr "Item da esquerda superior"
 
-#: engines/scumm/metaengine.cpp:1083
+#: engines/scumm/metaengine.cpp:1095
 #, fuzzy
 #| msgid "Up Right"
 msgid "Up right"
 msgstr "Para Cima ?? Direita"
 
-#: engines/scumm/metaengine.cpp:1095
+#: engines/scumm/metaengine.cpp:1107
 msgid "Switch weapon"
 msgstr "Trocar arma"
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1109
+#: engines/scumm/metaengine.cpp:1121
 msgid "Win the bike fight cheat"
 msgstr "Trapa??a para vencer a briga de motos"
 
-#: engines/scumm/metaengine.cpp:1118
+#: engines/scumm/metaengine.cpp:1130
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1196
+#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1208
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1201
+#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1213
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1206
+#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1218
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1211
+#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1216
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1228
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "Pegar"
 
-#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
+#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1235
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "Rolar para baixo"
 
-#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1230
+#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1242
 #, fuzzy
 #| msgid "Slide Left"
 msgid "Stick left"
 msgstr "Deslizar para Esquerda"
 
-#: engines/scumm/metaengine.cpp:1161 engines/scumm/metaengine.cpp:1237
+#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1249
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick right"
 msgstr "Deslizar para Direita"
 
-#: engines/scumm/metaengine.cpp:1168 engines/scumm/metaengine.cpp:1244
+#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1256
 #, fuzzy
 #| msgid "Move left"
 msgid "Fire / select"
 msgstr "Mover para esquerda"
 
-#: engines/scumm/metaengine.cpp:1173
+#: engines/scumm/metaengine.cpp:1185
 #, fuzzy
 #| msgid "Move Back"
 msgid "Menu back"
 msgstr "Mover para Tr??s"
 
-#: engines/scumm/metaengine.cpp:1178
+#: engines/scumm/metaengine.cpp:1190
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu back"
 msgstr "Omitir menu principal"
 
-#: engines/scumm/metaengine.cpp:1194
+#: engines/scumm/metaengine.cpp:1206
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1249
+#: engines/scumm/metaengine.cpp:1261
 #, fuzzy
 #| msgid "Move Back"
 msgid "Cover / back"
 msgstr "Mover para Tr??s"
 
-#: engines/scumm/metaengine.cpp:1255
+#: engines/scumm/metaengine.cpp:1267
 #, fuzzy
 #| msgid "Skip dialogue"
 msgid "Skip / back"
 msgstr "Pular di??logo"
 
-#: engines/scumm/metaengine.cpp:1262
+#: engines/scumm/metaengine.cpp:1274
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu"
diff --git a/po/pt_PT.po b/po/pt_PT.po
index e813ef531c0..4027efe9572 100644
--- a/po/pt_PT.po
+++ b/po/pt_PT.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.10.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 19:59+0000\n"
+"POT-Creation-Date: 2026-06-01 21:39+0000\n"
 "PO-Revision-Date: 2022-06-05 14:09+0000\n"
 "Last-Translator: Daniel <supsuper at gmail.com>\n"
 "Language-Team: Portuguese (Portugal) <https://translations.scummvm.org/"
@@ -1072,7 +1072,7 @@ msgstr "Interagir"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1076
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1088
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1093,7 +1093,7 @@ msgstr "Cima"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1043
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1055
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1114,7 +1114,7 @@ msgstr "Baixo"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1056
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1068
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1134,7 +1134,7 @@ msgstr "Esquerda"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1063
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1075
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3855,7 +3855,7 @@ msgid "Game not implemented"
 msgstr "ID de jogo n??o suportado"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
-#: engines/scumm/metaengine.cpp:1026
+#: engines/scumm/metaengine.cpp:1038
 #, fuzzy
 #| msgid "Default game keymap"
 msgid "Default game keymappings"
@@ -5838,7 +5838,7 @@ msgstr "R2"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1183 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1195 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "Menu"
 
@@ -7320,7 +7320,7 @@ msgstr "Ativar/desativar combate"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1102 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1114 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -8066,7 +8066,7 @@ msgstr "Interagir"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1089
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1101
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -10299,6 +10299,7 @@ msgid "Player ammo will never decrease."
 msgstr ""
 
 #: engines/hypno/metaengine.cpp:70 engines/scumm/metaengine.cpp:891
+#: engines/scumm/metaengine.cpp:900
 msgid "Unlock all levels"
 msgstr ""
 
@@ -13752,119 +13753,119 @@ msgstr "Alta resolu????o ativada"
 msgid "Run the game in 640x400 high resolution mode instead of 320x200"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:892
+#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:901
 msgid "All levels will be available without requiring passwords"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1037
+#: engines/scumm/metaengine.cpp:1049
 #, fuzzy
 msgid "Down left"
 msgstr "Girar ?? esquerda"
 
-#: engines/scumm/metaengine.cpp:1050
+#: engines/scumm/metaengine.cpp:1062
 #, fuzzy
 msgid "Down right"
 msgstr "Girar ?? direita"
 
-#: engines/scumm/metaengine.cpp:1070
+#: engines/scumm/metaengine.cpp:1082
 #, fuzzy
 #| msgid "Move left"
 msgid "Up left"
 msgstr "Mover para a esquerda"
 
-#: engines/scumm/metaengine.cpp:1083
+#: engines/scumm/metaengine.cpp:1095
 #, fuzzy
 msgid "Up right"
 msgstr "Direita"
 
-#: engines/scumm/metaengine.cpp:1095
+#: engines/scumm/metaengine.cpp:1107
 #, fuzzy
 msgid "Switch weapon"
 msgstr "Trocar ecr??s"
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1109
+#: engines/scumm/metaengine.cpp:1121
 msgid "Win the bike fight cheat"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1118
+#: engines/scumm/metaengine.cpp:1130
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1196
+#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1208
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1201
+#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1213
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1206
+#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1218
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1211
+#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1216
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1228
 #, fuzzy
 #| msgid "Left Stick X"
 msgid "Stick up"
 msgstr "Anal??gico Esquerdo X"
 
-#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
+#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1235
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "Deslizar para baixo"
 
-#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1230
+#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1242
 #, fuzzy
 #| msgid "Slide Left"
 msgid "Stick left"
 msgstr "Deslizar ?? esquerda"
 
-#: engines/scumm/metaengine.cpp:1161 engines/scumm/metaengine.cpp:1237
+#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1249
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick right"
 msgstr "Deslizar ?? direita"
 
-#: engines/scumm/metaengine.cpp:1168 engines/scumm/metaengine.cpp:1244
+#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1256
 #, fuzzy
 #| msgid "Move left"
 msgid "Fire / select"
 msgstr "Mover para a esquerda"
 
-#: engines/scumm/metaengine.cpp:1173
+#: engines/scumm/metaengine.cpp:1185
 #, fuzzy
 #| msgid "Move Back"
 msgid "Menu back"
 msgstr "Retroceder"
 
-#: engines/scumm/metaengine.cpp:1178
+#: engines/scumm/metaengine.cpp:1190
 #, fuzzy
 msgid "Skip / menu back"
 msgstr "~M~enu principal"
 
-#: engines/scumm/metaengine.cpp:1194
+#: engines/scumm/metaengine.cpp:1206
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1249
+#: engines/scumm/metaengine.cpp:1261
 #, fuzzy
 #| msgid "Move Back"
 msgid "Cover / back"
 msgstr "Retroceder"
 
-#: engines/scumm/metaengine.cpp:1255
+#: engines/scumm/metaengine.cpp:1267
 #, fuzzy
 #| msgid "Skip dialogue"
 msgid "Skip / back"
 msgstr "Ignorar frase"
 
-#: engines/scumm/metaengine.cpp:1262
+#: engines/scumm/metaengine.cpp:1274
 #, fuzzy
 msgid "Skip / menu"
 msgstr "~M~enu principal"
diff --git a/po/ro.po b/po/ro.po
index cecab5cd6d8..e85e4e1fb47 100644
--- a/po/ro.po
+++ b/po/ro.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.10.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 19:59+0000\n"
+"POT-Creation-Date: 2026-06-01 21:39+0000\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: Automatically generated\n"
 "Language-Team: none\n"
@@ -1013,7 +1013,7 @@ msgstr ""
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1076
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1088
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1034,7 +1034,7 @@ msgstr ""
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1043
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1055
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1055,7 +1055,7 @@ msgstr ""
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1056
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1068
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1075,7 +1075,7 @@ msgstr ""
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1063
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1075
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3588,7 +3588,7 @@ msgid "Game not implemented"
 msgstr ""
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
-#: engines/scumm/metaengine.cpp:1026
+#: engines/scumm/metaengine.cpp:1038
 msgid "Default game keymappings"
 msgstr ""
 
@@ -5434,7 +5434,7 @@ msgstr ""
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1183 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1195 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr ""
 
@@ -6795,7 +6795,7 @@ msgstr ""
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1102 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1114 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -7442,7 +7442,7 @@ msgstr ""
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1089
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1101
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -9329,6 +9329,7 @@ msgid "Player ammo will never decrease."
 msgstr ""
 
 #: engines/hypno/metaengine.cpp:70 engines/scumm/metaengine.cpp:891
+#: engines/scumm/metaengine.cpp:900
 msgid "Unlock all levels"
 msgstr ""
 
@@ -12388,96 +12389,96 @@ msgstr ""
 msgid "Run the game in 640x400 high resolution mode instead of 320x200"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:892
+#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:901
 msgid "All levels will be available without requiring passwords"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1037
+#: engines/scumm/metaengine.cpp:1049
 msgid "Down left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1050
+#: engines/scumm/metaengine.cpp:1062
 msgid "Down right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1070
+#: engines/scumm/metaengine.cpp:1082
 msgid "Up left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1083
+#: engines/scumm/metaengine.cpp:1095
 msgid "Up right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1095
+#: engines/scumm/metaengine.cpp:1107
 msgid "Switch weapon"
 msgstr ""
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1109
+#: engines/scumm/metaengine.cpp:1121
 msgid "Win the bike fight cheat"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1118
+#: engines/scumm/metaengine.cpp:1130
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1196
+#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1208
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1201
+#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1213
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1206
+#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1218
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1211
+#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1216
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1228
 msgid "Stick up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
+#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1235
 msgid "Stick down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1230
+#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1242
 msgid "Stick left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1161 engines/scumm/metaengine.cpp:1237
+#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1249
 msgid "Stick right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1168 engines/scumm/metaengine.cpp:1244
+#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1256
 msgid "Fire / select"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1173
+#: engines/scumm/metaengine.cpp:1185
 msgid "Menu back"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1178
+#: engines/scumm/metaengine.cpp:1190
 msgid "Skip / menu back"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1194
+#: engines/scumm/metaengine.cpp:1206
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1249
+#: engines/scumm/metaengine.cpp:1261
 msgid "Cover / back"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1255
+#: engines/scumm/metaengine.cpp:1267
 msgid "Skip / back"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1262
+#: engines/scumm/metaengine.cpp:1274
 msgid "Skip / menu"
 msgstr ""
 
diff --git a/po/ru_RU.po b/po/ru_RU.po
index fb5a10125cc..9f59bb880c8 100644
--- a/po/ru_RU.po
+++ b/po/ru_RU.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.8.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 19:59+0000\n"
+"POT-Creation-Date: 2026-06-01 21:39+0000\n"
 "PO-Revision-Date: 2026-05-06 08:54+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Russian <https://translations.scummvm.org/projects/scummvm/"
@@ -1051,7 +1051,7 @@ msgstr "????????????????????????????"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1076
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1088
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1072,7 +1072,7 @@ msgstr "??????????"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1043
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1055
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1093,7 +1093,7 @@ msgstr "????????"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1056
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1068
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1113,7 +1113,7 @@ msgstr "??????????"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1063
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1075
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3896,7 +3896,7 @@ msgid "Game not implemented"
 msgstr "???????? ???? ??????????????????????"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
-#: engines/scumm/metaengine.cpp:1026
+#: engines/scumm/metaengine.cpp:1038
 msgid "Default game keymappings"
 msgstr "?????????????????? ???????????? ???????? ???? ??????????????????"
 
@@ -6109,7 +6109,7 @@ msgstr "R2"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1183 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1195 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "????????"
 
@@ -7566,7 +7566,7 @@ msgstr "?????????????????????? ??????"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1102 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1114 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -8229,7 +8229,7 @@ msgstr "???????????????? / ??????????????????????????????????"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1089
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1101
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -10197,6 +10197,7 @@ msgid "Player ammo will never decrease."
 msgstr "???????????????????? ???????????? ?????????????? ???? ??????????????????????."
 
 #: engines/hypno/metaengine.cpp:70 engines/scumm/metaengine.cpp:891
+#: engines/scumm/metaengine.cpp:900
 msgid "Unlock all levels"
 msgstr "???????????????????????????? ?????? ????????????"
 
@@ -13550,116 +13551,116 @@ msgstr "?????????????? ???????????????????? ????????????????"
 msgid "Run the game in 640x400 high resolution mode instead of 320x200"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:892
+#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:901
 #, fuzzy
 #| msgid "All levels will be available to play."
 msgid "All levels will be available without requiring passwords"
 msgstr "?????? ???????????? ?????????? ???????????????? ?????? ????????."
 
-#: engines/scumm/metaengine.cpp:1037
+#: engines/scumm/metaengine.cpp:1049
 msgid "Down left"
 msgstr "????????-??????????"
 
-#: engines/scumm/metaengine.cpp:1050
+#: engines/scumm/metaengine.cpp:1062
 msgid "Down right"
 msgstr "????????-????????????"
 
-#: engines/scumm/metaengine.cpp:1070
+#: engines/scumm/metaengine.cpp:1082
 msgid "Up left"
 msgstr "??????????-??????????"
 
-#: engines/scumm/metaengine.cpp:1083
+#: engines/scumm/metaengine.cpp:1095
 msgid "Up right"
 msgstr "??????????-????????????"
 
-#: engines/scumm/metaengine.cpp:1095
+#: engines/scumm/metaengine.cpp:1107
 msgid "Switch weapon"
 msgstr "?????????????????????? ????????????"
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1109
+#: engines/scumm/metaengine.cpp:1121
 msgid "Win the bike fight cheat"
 msgstr "?????? ?????? ???????????????? ???????? ???? ????????????"
 
-#: engines/scumm/metaengine.cpp:1118
+#: engines/scumm/metaengine.cpp:1130
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1196
+#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1208
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1201
+#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1213
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1206
+#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1218
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1211
+#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1216
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1228
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "??????????????"
 
-#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
+#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1235
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "???????????????????? ????????"
 
-#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1230
+#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1242
 #, fuzzy
 #| msgid "Slide left"
 msgid "Stick left"
 msgstr "?????????????????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1161 engines/scumm/metaengine.cpp:1237
+#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1249
 #, fuzzy
 #| msgid "Slide right"
 msgid "Stick right"
 msgstr "?????????????????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1168 engines/scumm/metaengine.cpp:1244
+#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1256
 #, fuzzy
 #| msgid "Move / Select"
 msgid "Fire / select"
 msgstr "???????? / ??????????????"
 
-#: engines/scumm/metaengine.cpp:1173
+#: engines/scumm/metaengine.cpp:1185
 #, fuzzy
 #| msgid "Move back"
 msgid "Menu back"
 msgstr "???????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1178
+#: engines/scumm/metaengine.cpp:1190
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu back"
 msgstr "???????????????????? ?????????????? ????????"
 
-#: engines/scumm/metaengine.cpp:1194
+#: engines/scumm/metaengine.cpp:1206
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1249
+#: engines/scumm/metaengine.cpp:1261
 #, fuzzy
 #| msgid "Move back"
 msgid "Cover / back"
 msgstr "???????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1255
+#: engines/scumm/metaengine.cpp:1267
 #, fuzzy
 #| msgid "Skip walk"
 msgid "Skip / back"
 msgstr "???????????????????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1262
+#: engines/scumm/metaengine.cpp:1274
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu"
diff --git a/po/sv_SE.po b/po/sv_SE.po
index 8ed86b8f91d..db388799f40 100644
--- a/po/sv_SE.po
+++ b/po/sv_SE.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.5.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 19:59+0000\n"
+"POT-Creation-Date: 2026-06-01 21:39+0000\n"
 "PO-Revision-Date: 2026-04-19 13:22+0000\n"
 "Last-Translator: Daniel Nylander <po at danielnylander.se>\n"
 "Language-Team: Swedish <https://translations.scummvm.org/projects/scummvm/"
@@ -1053,7 +1053,7 @@ msgstr "Interagera"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1076
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1088
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1074,7 +1074,7 @@ msgstr "Upp"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1043
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1055
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1095,7 +1095,7 @@ msgstr "Ner"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1056
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1068
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1115,7 +1115,7 @@ msgstr "V??nster"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1063
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1075
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3886,7 +3886,7 @@ msgid "Game not implemented"
 msgstr "Spelet ??r inte implementerat"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
-#: engines/scumm/metaengine.cpp:1026
+#: engines/scumm/metaengine.cpp:1038
 msgid "Default game keymappings"
 msgstr "Standardinst??llningar f??r tangentbordskommandon i spelet"
 
@@ -6086,7 +6086,7 @@ msgstr "R2"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1183 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1195 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "Meny"
 
@@ -7541,7 +7541,7 @@ msgstr "V??xla strid"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1102 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1114 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -8209,7 +8209,7 @@ msgstr "Prata/Interagera"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1089
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1101
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -10238,6 +10238,7 @@ msgid "Player ammo will never decrease."
 msgstr "Spelarens ammunition kommer aldrig att minska."
 
 #: engines/hypno/metaengine.cpp:70 engines/scumm/metaengine.cpp:891
+#: engines/scumm/metaengine.cpp:900
 msgid "Unlock all levels"
 msgstr "L??s upp alla niv??er"
 
@@ -13599,116 +13600,116 @@ msgstr "H??guppl??st grafik p??"
 msgid "Run the game in 640x400 high resolution mode instead of 320x200"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:892
+#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:901
 #, fuzzy
 #| msgid "All levels will be available to play."
 msgid "All levels will be available without requiring passwords"
 msgstr "Alla niv??er kommer att finnas tillg??ngliga att spela."
 
-#: engines/scumm/metaengine.cpp:1037
+#: engines/scumm/metaengine.cpp:1049
 msgid "Down left"
 msgstr "Ned??t v??nster"
 
-#: engines/scumm/metaengine.cpp:1050
+#: engines/scumm/metaengine.cpp:1062
 msgid "Down right"
 msgstr "Ned??t h??ger"
 
-#: engines/scumm/metaengine.cpp:1070
+#: engines/scumm/metaengine.cpp:1082
 msgid "Up left"
 msgstr "Upp??t v??nster"
 
-#: engines/scumm/metaengine.cpp:1083
+#: engines/scumm/metaengine.cpp:1095
 msgid "Up right"
 msgstr "Upp??t h??ger"
 
-#: engines/scumm/metaengine.cpp:1095
+#: engines/scumm/metaengine.cpp:1107
 msgid "Switch weapon"
 msgstr "Byt vapen"
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1109
+#: engines/scumm/metaengine.cpp:1121
 msgid "Win the bike fight cheat"
 msgstr "Vinn cykelkampsfusket"
 
-#: engines/scumm/metaengine.cpp:1118
+#: engines/scumm/metaengine.cpp:1130
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1196
+#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1208
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1201
+#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1213
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1206
+#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1218
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1211
+#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1216
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1228
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "Ta"
 
-#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
+#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1235
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "Bl??ddra ned??t"
 
-#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1230
+#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1242
 #, fuzzy
 #| msgid "Slide left"
 msgid "Stick left"
 msgstr "Glid v??nster"
 
-#: engines/scumm/metaengine.cpp:1161 engines/scumm/metaengine.cpp:1237
+#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1249
 #, fuzzy
 #| msgid "Slide right"
 msgid "Stick right"
 msgstr "Glid h??ger"
 
-#: engines/scumm/metaengine.cpp:1168 engines/scumm/metaengine.cpp:1244
+#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1256
 #, fuzzy
 #| msgid "Move / Select"
 msgid "Fire / select"
 msgstr "Flytta / V??lj"
 
-#: engines/scumm/metaengine.cpp:1173
+#: engines/scumm/metaengine.cpp:1185
 #, fuzzy
 #| msgid "Move back"
 msgid "Menu back"
 msgstr "Stega bak??t"
 
-#: engines/scumm/metaengine.cpp:1178
+#: engines/scumm/metaengine.cpp:1190
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu back"
 msgstr "Hoppa ??ver huvudmeny"
 
-#: engines/scumm/metaengine.cpp:1194
+#: engines/scumm/metaengine.cpp:1206
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1249
+#: engines/scumm/metaengine.cpp:1261
 #, fuzzy
 #| msgid "Move back"
 msgid "Cover / back"
 msgstr "Stega bak??t"
 
-#: engines/scumm/metaengine.cpp:1255
+#: engines/scumm/metaengine.cpp:1267
 #, fuzzy
 #| msgid "Skip walk"
 msgid "Skip / back"
 msgstr "Hoppa ??ver g??ng"
 
-#: engines/scumm/metaengine.cpp:1262
+#: engines/scumm/metaengine.cpp:1274
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu"
diff --git a/po/tr.po b/po/tr.po
index 33240fadecb..a78c906b6c0 100644
--- a/po/tr.po
+++ b/po/tr.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 19:59+0000\n"
+"POT-Creation-Date: 2026-06-01 21:39+0000\n"
 "PO-Revision-Date: 2024-10-10 12:33+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Turkish <https://translations.scummvm.org/projects/scummvm/"
@@ -1057,7 +1057,7 @@ msgstr "Etkile??im"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1076
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1088
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1078,7 +1078,7 @@ msgstr "Yukar??"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1043
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1055
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1099,7 +1099,7 @@ msgstr "A??a????"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1056
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1068
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1119,7 +1119,7 @@ msgstr "Sol"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1063
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1075
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3832,7 +3832,7 @@ msgid "Game not implemented"
 msgstr "Oyun ID si desteklenmiyor"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
-#: engines/scumm/metaengine.cpp:1026
+#: engines/scumm/metaengine.cpp:1038
 #, fuzzy
 msgid "Default game keymappings"
 msgstr "A~y~arlar..."
@@ -5762,7 +5762,7 @@ msgstr ""
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1183 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1195 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr ""
 
@@ -7194,7 +7194,7 @@ msgstr "Altyaz??lar"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1102 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1114 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -7904,7 +7904,7 @@ msgstr "Etkile??im"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1089
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1101
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -9981,6 +9981,7 @@ msgid "Player ammo will never decrease."
 msgstr ""
 
 #: engines/hypno/metaengine.cpp:70 engines/scumm/metaengine.cpp:891
+#: engines/scumm/metaengine.cpp:900
 msgid "Unlock all levels"
 msgstr ""
 
@@ -13257,112 +13258,112 @@ msgstr ""
 msgid "Run the game in 640x400 high resolution mode instead of 320x200"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:892
+#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:901
 msgid "All levels will be available without requiring passwords"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1037
+#: engines/scumm/metaengine.cpp:1049
 #, fuzzy
 msgid "Down left"
 msgstr "Sol"
 
-#: engines/scumm/metaengine.cpp:1050
+#: engines/scumm/metaengine.cpp:1062
 #, fuzzy
 msgid "Down right"
 msgstr "Sa??"
 
-#: engines/scumm/metaengine.cpp:1070
+#: engines/scumm/metaengine.cpp:1082
 #, fuzzy
 msgid "Up left"
 msgstr "Sol"
 
-#: engines/scumm/metaengine.cpp:1083
+#: engines/scumm/metaengine.cpp:1095
 #, fuzzy
 msgid "Up right"
 msgstr "Sa??"
 
-#: engines/scumm/metaengine.cpp:1095
+#: engines/scumm/metaengine.cpp:1107
 #, fuzzy
 msgid "Switch weapon"
 msgstr "Oyunu se??"
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1109
+#: engines/scumm/metaengine.cpp:1121
 msgid "Win the bike fight cheat"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1118
+#: engines/scumm/metaengine.cpp:1130
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1196
+#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1208
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1201
+#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1213
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1206
+#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1218
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1211
+#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1216
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1228
 msgid "Stick up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
+#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1235
 #, fuzzy
 msgid "Stick down"
 msgstr "A??a????"
 
-#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1230
+#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1242
 #, fuzzy
 msgid "Stick left"
 msgstr "Sa??"
 
-#: engines/scumm/metaengine.cpp:1161 engines/scumm/metaengine.cpp:1237
+#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1249
 #, fuzzy
 msgid "Stick right"
 msgstr "Sa??"
 
-#: engines/scumm/metaengine.cpp:1168 engines/scumm/metaengine.cpp:1244
+#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1256
 #, fuzzy
 msgid "Fire / select"
 msgstr "Sol"
 
-#: engines/scumm/metaengine.cpp:1173
+#: engines/scumm/metaengine.cpp:1185
 #, fuzzy
 #| msgid "Backend"
 msgid "Menu back"
 msgstr "Altyap??"
 
-#: engines/scumm/metaengine.cpp:1178
+#: engines/scumm/metaengine.cpp:1190
 #, fuzzy
 msgid "Skip / menu back"
 msgstr "??ptal"
 
-#: engines/scumm/metaengine.cpp:1194
+#: engines/scumm/metaengine.cpp:1206
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1249
+#: engines/scumm/metaengine.cpp:1261
 #, fuzzy
 #| msgid "Backend"
 msgid "Cover / back"
 msgstr "Altyap??"
 
-#: engines/scumm/metaengine.cpp:1255
+#: engines/scumm/metaengine.cpp:1267
 #, fuzzy
 #| msgid "Show hidden files"
 msgid "Skip / back"
 msgstr "Gizli dosyalar?? g??ster"
 
-#: engines/scumm/metaengine.cpp:1262
+#: engines/scumm/metaengine.cpp:1274
 #, fuzzy
 msgid "Skip / menu"
 msgstr "??ptal"
diff --git a/po/uk_UA.po b/po/uk_UA.po
index b37e891eed6..b6078449f44 100644
--- a/po/uk_UA.po
+++ b/po/uk_UA.po
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.9.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 19:59+0000\n"
+"POT-Creation-Date: 2026-06-01 21:39+0000\n"
 "PO-Revision-Date: 2026-05-06 08:54+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Ukrainian <https://translations.scummvm.org/projects/scummvm/"
@@ -1055,7 +1055,7 @@ msgstr "??????????????????"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1076
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1088
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1076,7 +1076,7 @@ msgstr "????????????"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1043
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1055
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1097,7 +1097,7 @@ msgstr "????????????"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1056
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1068
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1117,7 +1117,7 @@ msgstr "????????????"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1063
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1075
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3890,7 +3890,7 @@ msgid "Game not implemented"
 msgstr "?????? ???? ??????????????????????"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
-#: engines/scumm/metaengine.cpp:1026
+#: engines/scumm/metaengine.cpp:1038
 msgid "Default game keymappings"
 msgstr "???????? ???????????? ?????? ???? ??????????????????????"
 
@@ -6095,7 +6095,7 @@ msgstr "R2"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1183 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1195 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "????????"
 
@@ -7546,7 +7546,7 @@ msgstr "???????????????????? ?????????? ??????"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1102 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1114 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -8210,7 +8210,7 @@ msgstr "???????????????? / ??????????????????????"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1089
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1101
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -10176,6 +10176,7 @@ msgid "Player ammo will never decrease."
 msgstr "???????????????????? ???????????? ???????????? ???? ??????????????????????."
 
 #: engines/hypno/metaengine.cpp:70 engines/scumm/metaengine.cpp:891
+#: engines/scumm/metaengine.cpp:900
 msgid "Unlock all levels"
 msgstr "???????????????????????? ?????? ??????????"
 
@@ -13509,116 +13510,116 @@ msgstr "???????????? ???????????????????????? ??????????????????"
 msgid "Run the game in 640x400 high resolution mode instead of 320x200"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:892
+#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:901
 #, fuzzy
 #| msgid "All levels will be available to play."
 msgid "All levels will be available without requiring passwords"
 msgstr "?????? ?????????? ???????????? ???????????????? ?????? ??????."
 
-#: engines/scumm/metaengine.cpp:1037
+#: engines/scumm/metaengine.cpp:1049
 msgid "Down left"
 msgstr "????????????-????????????"
 
-#: engines/scumm/metaengine.cpp:1050
+#: engines/scumm/metaengine.cpp:1062
 msgid "Down right"
 msgstr "????????????-??????????????"
 
-#: engines/scumm/metaengine.cpp:1070
+#: engines/scumm/metaengine.cpp:1082
 msgid "Up left"
 msgstr "????????????-????????????"
 
-#: engines/scumm/metaengine.cpp:1083
+#: engines/scumm/metaengine.cpp:1095
 msgid "Up right"
 msgstr "????????????-??????????????"
 
-#: engines/scumm/metaengine.cpp:1095
+#: engines/scumm/metaengine.cpp:1107
 msgid "Switch weapon"
 msgstr "???????????????????? ??????????"
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1109
+#: engines/scumm/metaengine.cpp:1121
 msgid "Win the bike fight cheat"
 msgstr "?????? ???????????????????? ?????? ???? ??????????????"
 
-#: engines/scumm/metaengine.cpp:1118
+#: engines/scumm/metaengine.cpp:1130
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1196
+#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1208
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1201
+#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1213
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1206
+#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1218
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1211
+#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1216
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1228
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "??????????????????"
 
-#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
+#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1235
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "?????????????????????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1230
+#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1242
 #, fuzzy
 #| msgid "Slide left"
 msgid "Stick left"
 msgstr "???????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1161 engines/scumm/metaengine.cpp:1237
+#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1249
 #, fuzzy
 #| msgid "Slide right"
 msgid "Stick right"
 msgstr "???????? ??????????????"
 
-#: engines/scumm/metaengine.cpp:1168 engines/scumm/metaengine.cpp:1244
+#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1256
 #, fuzzy
 #| msgid "Move / Select"
 msgid "Fire / select"
 msgstr "???????????????? / ??????????????"
 
-#: engines/scumm/metaengine.cpp:1173
+#: engines/scumm/metaengine.cpp:1185
 #, fuzzy
 #| msgid "Move back"
 msgid "Menu back"
 msgstr "???????????????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1178
+#: engines/scumm/metaengine.cpp:1190
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu back"
 msgstr "???????????????????? ?????????????? ????????"
 
-#: engines/scumm/metaengine.cpp:1194
+#: engines/scumm/metaengine.cpp:1206
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1249
+#: engines/scumm/metaengine.cpp:1261
 #, fuzzy
 #| msgid "Move back"
 msgid "Cover / back"
 msgstr "???????????????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1255
+#: engines/scumm/metaengine.cpp:1267
 #, fuzzy
 #| msgid "Skip walk"
 msgid "Skip / back"
 msgstr "???????????????????? ??????????????"
 
-#: engines/scumm/metaengine.cpp:1262
+#: engines/scumm/metaengine.cpp:1274
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu"
diff --git a/po/zh.po b/po/zh.po
index 42752c952f8..6e9917a4016 100644
--- a/po/zh.po
+++ b/po/zh.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 19:59+0000\n"
+"POT-Creation-Date: 2026-06-01 21:39+0000\n"
 "PO-Revision-Date: 2024-05-14 12:42+0000\n"
 "Last-Translator: ?????? <clonewith at qq.com>\n"
 "Language-Team: Chinese <https://translations.scummvm.org/projects/scummvm/"
@@ -1033,7 +1033,7 @@ msgstr "??????"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1076
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1088
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1054,7 +1054,7 @@ msgstr "??????"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1043
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1055
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1075,7 +1075,7 @@ msgstr "??????"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1056
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1068
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1095,7 +1095,7 @@ msgstr "??????"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1063
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1075
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3645,7 +3645,7 @@ msgid "Game not implemented"
 msgstr ""
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
-#: engines/scumm/metaengine.cpp:1026
+#: engines/scumm/metaengine.cpp:1038
 #, fuzzy
 #| msgid "Game Options"
 msgid "Default game keymappings"
@@ -5543,7 +5543,7 @@ msgstr ""
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1183 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1195 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr ""
 
@@ -6954,7 +6954,7 @@ msgstr "??????????????????"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1102 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1114 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -7675,7 +7675,7 @@ msgstr "??????"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1089
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1101
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -9795,6 +9795,7 @@ msgid "Player ammo will never decrease."
 msgstr ""
 
 #: engines/hypno/metaengine.cpp:70 engines/scumm/metaengine.cpp:891
+#: engines/scumm/metaengine.cpp:900
 msgid "Unlock all levels"
 msgstr ""
 
@@ -13082,112 +13083,112 @@ msgstr ""
 msgid "Run the game in 640x400 high resolution mode instead of 320x200"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:892
+#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:901
 msgid "All levels will be available without requiring passwords"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1037
+#: engines/scumm/metaengine.cpp:1049
 #, fuzzy
 msgid "Down left"
 msgstr "??????"
 
-#: engines/scumm/metaengine.cpp:1050
+#: engines/scumm/metaengine.cpp:1062
 #, fuzzy
 msgid "Down right"
 msgstr "??????"
 
-#: engines/scumm/metaengine.cpp:1070
+#: engines/scumm/metaengine.cpp:1082
 #, fuzzy
 msgid "Up left"
 msgstr "??????"
 
-#: engines/scumm/metaengine.cpp:1083
+#: engines/scumm/metaengine.cpp:1095
 #, fuzzy
 msgid "Up right"
 msgstr "??????"
 
-#: engines/scumm/metaengine.cpp:1095
+#: engines/scumm/metaengine.cpp:1107
 msgid "Switch weapon"
 msgstr ""
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1109
+#: engines/scumm/metaengine.cpp:1121
 msgid "Win the bike fight cheat"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1118
+#: engines/scumm/metaengine.cpp:1130
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1196
+#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1208
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1201
+#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1213
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1206
+#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1218
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1211
+#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1216
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1228
 msgid "Stick up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
+#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1235
 #, fuzzy
 msgid "Stick down"
 msgstr "??????"
 
-#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1230
+#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1242
 #, fuzzy
 msgid "Stick left"
 msgstr "??????"
 
-#: engines/scumm/metaengine.cpp:1161 engines/scumm/metaengine.cpp:1237
+#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1249
 #, fuzzy
 msgid "Stick right"
 msgstr "??????"
 
-#: engines/scumm/metaengine.cpp:1168 engines/scumm/metaengine.cpp:1244
+#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1256
 #, fuzzy
 msgid "Fire / select"
 msgstr "??????"
 
-#: engines/scumm/metaengine.cpp:1173
+#: engines/scumm/metaengine.cpp:1185
 #, fuzzy
 #| msgid "Backend"
 msgid "Menu back"
 msgstr "??????"
 
-#: engines/scumm/metaengine.cpp:1178
+#: engines/scumm/metaengine.cpp:1190
 #, fuzzy
 #| msgid "Skip autosave"
 msgid "Skip / menu back"
 msgstr "??????????????????"
 
-#: engines/scumm/metaengine.cpp:1194
+#: engines/scumm/metaengine.cpp:1206
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1249
+#: engines/scumm/metaengine.cpp:1261
 #, fuzzy
 #| msgid "Backend"
 msgid "Cover / back"
 msgstr "??????"
 
-#: engines/scumm/metaengine.cpp:1255
+#: engines/scumm/metaengine.cpp:1267
 #, fuzzy
 #| msgid "Show hidden files"
 msgid "Skip / back"
 msgstr "?????????????????????"
 
-#: engines/scumm/metaengine.cpp:1262
+#: engines/scumm/metaengine.cpp:1274
 #, fuzzy
 #| msgid "Skip autosave"
 msgid "Skip / menu"
diff --git a/po/zh_Hans.po b/po/zh_Hans.po
index edf32c38e60..9a65a1c001d 100644
--- a/po/zh_Hans.po
+++ b/po/zh_Hans.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.9.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 19:59+0000\n"
+"POT-Creation-Date: 2026-06-01 21:39+0000\n"
 "PO-Revision-Date: 2024-12-01 22:18+0000\n"
 "Last-Translator: ?????? <clonewith at qq.com>\n"
 "Language-Team: Chinese (Simplified Han script) <https://"
@@ -1034,7 +1034,7 @@ msgstr "??????"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1076
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1088
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1055,7 +1055,7 @@ msgstr "??????"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1043
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1055
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1076,7 +1076,7 @@ msgstr "??????"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1056
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1068
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1096,7 +1096,7 @@ msgstr "??????"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1063
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1075
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3846,7 +3846,7 @@ msgid "Game not implemented"
 msgstr "???????????????"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
-#: engines/scumm/metaengine.cpp:1026
+#: engines/scumm/metaengine.cpp:1038
 #, fuzzy
 #| msgid "Default game keymap"
 msgid "Default game keymappings"
@@ -6179,7 +6179,7 @@ msgstr "R2"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1183 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1195 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "??????"
 
@@ -7629,7 +7629,7 @@ msgstr "????????????"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1102 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1114 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -8384,7 +8384,7 @@ msgstr "??????/??????"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1089
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1101
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -10607,6 +10607,7 @@ msgid "Player ammo will never decrease."
 msgstr "???????????????????????????"
 
 #: engines/hypno/metaengine.cpp:70 engines/scumm/metaengine.cpp:891
+#: engines/scumm/metaengine.cpp:900
 msgid "Unlock all levels"
 msgstr "??????????????????"
 
@@ -14051,124 +14052,124 @@ msgstr "??????????????????"
 msgid "Run the game in 640x400 high resolution mode instead of 320x200"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:892
+#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:901
 #, fuzzy
 #| msgid "All levels will be available to play."
 msgid "All levels will be available without requiring passwords"
 msgstr "??????????????????????????????"
 
-#: engines/scumm/metaengine.cpp:1037
+#: engines/scumm/metaengine.cpp:1049
 #, fuzzy
 #| msgid "Down Left"
 msgid "Down left"
 msgstr "??????"
 
-#: engines/scumm/metaengine.cpp:1050
+#: engines/scumm/metaengine.cpp:1062
 #, fuzzy
 #| msgid "Down Right"
 msgid "Down right"
 msgstr "??????"
 
-#: engines/scumm/metaengine.cpp:1070
+#: engines/scumm/metaengine.cpp:1082
 #, fuzzy
 #| msgid "Upper left item"
 msgid "Up left"
 msgstr "???????????????"
 
-#: engines/scumm/metaengine.cpp:1083
+#: engines/scumm/metaengine.cpp:1095
 #, fuzzy
 #| msgid "Up Right"
 msgid "Up right"
 msgstr "??????"
 
-#: engines/scumm/metaengine.cpp:1095
+#: engines/scumm/metaengine.cpp:1107
 msgid "Switch weapon"
 msgstr "????????????"
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1109
+#: engines/scumm/metaengine.cpp:1121
 msgid "Win the bike fight cheat"
 msgstr "???????????????????????????"
 
-#: engines/scumm/metaengine.cpp:1118
+#: engines/scumm/metaengine.cpp:1130
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1196
+#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1208
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1201
+#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1213
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1206
+#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1218
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1211
+#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1216
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1228
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "??????"
 
-#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
+#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1235
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "????????????"
 
-#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1230
+#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1242
 #, fuzzy
 #| msgid "Slide Left"
 msgid "Stick left"
 msgstr "????????????"
 
-#: engines/scumm/metaengine.cpp:1161 engines/scumm/metaengine.cpp:1237
+#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1249
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick right"
 msgstr "????????????"
 
-#: engines/scumm/metaengine.cpp:1168 engines/scumm/metaengine.cpp:1244
+#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1256
 #, fuzzy
 #| msgid "Move left"
 msgid "Fire / select"
 msgstr "????????????"
 
-#: engines/scumm/metaengine.cpp:1173
+#: engines/scumm/metaengine.cpp:1185
 #, fuzzy
 #| msgid "Move Back"
 msgid "Menu back"
 msgstr "????????????"
 
-#: engines/scumm/metaengine.cpp:1178
+#: engines/scumm/metaengine.cpp:1190
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu back"
 msgstr "???????????????"
 
-#: engines/scumm/metaengine.cpp:1194
+#: engines/scumm/metaengine.cpp:1206
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1249
+#: engines/scumm/metaengine.cpp:1261
 #, fuzzy
 #| msgid "Move Back"
 msgid "Cover / back"
 msgstr "????????????"
 
-#: engines/scumm/metaengine.cpp:1255
+#: engines/scumm/metaengine.cpp:1267
 #, fuzzy
 #| msgid "Skip dialogue"
 msgid "Skip / back"
 msgstr "????????????"
 
-#: engines/scumm/metaengine.cpp:1262
+#: engines/scumm/metaengine.cpp:1274
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu"
diff --git a/po/zh_Hant.po b/po/zh_Hant.po
index f355f3e614c..c74930b3fae 100644
--- a/po/zh_Hant.po
+++ b/po/zh_Hant.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.9.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 19:59+0000\n"
+"POT-Creation-Date: 2026-06-01 21:39+0000\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: Automatically generated\n"
 "Language-Team: none\n"
@@ -1014,7 +1014,7 @@ msgstr ""
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1076
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1088
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1035,7 +1035,7 @@ msgstr ""
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1043
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1055
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1056,7 +1056,7 @@ msgstr ""
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1056
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1068
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1076,7 +1076,7 @@ msgstr ""
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1063
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1075
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3589,7 +3589,7 @@ msgid "Game not implemented"
 msgstr ""
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
-#: engines/scumm/metaengine.cpp:1026
+#: engines/scumm/metaengine.cpp:1038
 msgid "Default game keymappings"
 msgstr ""
 
@@ -5435,7 +5435,7 @@ msgstr ""
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1183 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1195 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr ""
 
@@ -6796,7 +6796,7 @@ msgstr ""
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1102 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1114 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -7443,7 +7443,7 @@ msgstr ""
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1089
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1101
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -9330,6 +9330,7 @@ msgid "Player ammo will never decrease."
 msgstr ""
 
 #: engines/hypno/metaengine.cpp:70 engines/scumm/metaengine.cpp:891
+#: engines/scumm/metaengine.cpp:900
 msgid "Unlock all levels"
 msgstr ""
 
@@ -12389,96 +12390,96 @@ msgstr ""
 msgid "Run the game in 640x400 high resolution mode instead of 320x200"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:892
+#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:901
 msgid "All levels will be available without requiring passwords"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1037
+#: engines/scumm/metaengine.cpp:1049
 msgid "Down left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1050
+#: engines/scumm/metaengine.cpp:1062
 msgid "Down right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1070
+#: engines/scumm/metaengine.cpp:1082
 msgid "Up left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1083
+#: engines/scumm/metaengine.cpp:1095
 msgid "Up right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1095
+#: engines/scumm/metaengine.cpp:1107
 msgid "Switch weapon"
 msgstr ""
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1109
+#: engines/scumm/metaengine.cpp:1121
 msgid "Win the bike fight cheat"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1118
+#: engines/scumm/metaengine.cpp:1130
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1120 engines/scumm/metaengine.cpp:1196
+#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1208
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1125 engines/scumm/metaengine.cpp:1201
+#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1213
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1130 engines/scumm/metaengine.cpp:1206
+#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1218
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1135 engines/scumm/metaengine.cpp:1211
+#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1140 engines/scumm/metaengine.cpp:1216
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1228
 msgid "Stick up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
+#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1235
 msgid "Stick down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1230
+#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1242
 msgid "Stick left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1161 engines/scumm/metaengine.cpp:1237
+#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1249
 msgid "Stick right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1168 engines/scumm/metaengine.cpp:1244
+#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1256
 msgid "Fire / select"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1173
+#: engines/scumm/metaengine.cpp:1185
 msgid "Menu back"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1178
+#: engines/scumm/metaengine.cpp:1190
 msgid "Skip / menu back"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1194
+#: engines/scumm/metaengine.cpp:1206
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1249
+#: engines/scumm/metaengine.cpp:1261
 msgid "Cover / back"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1255
+#: engines/scumm/metaengine.cpp:1267
 msgid "Skip / back"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1262
+#: engines/scumm/metaengine.cpp:1274
 msgid "Skip / menu"
 msgstr ""
 



From noreply at scummvm.org  Mon Jun  1 22:14:31 2026
From: noreply at scummvm.org (tag2015)
Date: Mon,  1 Jun 2026 22:14:31 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm-icons master ->
 a8949137434a982d5c45662a00ec0438d410ac65
Message-ID: <20260601221431.50F85700591@ron.scummvm.net>

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

Summary:
a894913743 ICONS: Added entries to TOC


Commit: a8949137434a982d5c45662a00ec0438d410ac65
    https://github.com/scummvm/scummvm-icons/commit/a8949137434a982d5c45662a00ec0438d410ac65
Author: Walter Agazzi (walter.agazzi at protonmail.com)
Date: 2026-06-02T00:14:28+02:00

Commit Message:
ICONS: Added entries to TOC

Updated entries for Access, ALG, BOLT, PhoenixVR, Nancy

Changed paths:
    TOC.md


diff --git a/TOC.md b/TOC.md
index 8eaabf8..2e08d85 100644
--- a/TOC.md
+++ b/TOC.md
@@ -5,6 +5,7 @@
 | ??? | example | example             | Example Entry                                                                | Your Name Here
 | ??? | access | amazon              | Amazon: Guardians of Eden                                                    | Tucky27, GandalfTheWhite80 |
 | ??? | access | martian          | Martian Memorandum                                                             | GandalfTheWhite80 |
+| ?????? | access | nocropolis         | Noctropolis                                                                | |
 | ??? | adl | hires0              | Hi-Res Adventure #0: Mission Asteroid                                        | Tucky27, GandalfTheWhite80 |
 | ??? | adl | hires1              | Hi-Res Adventure #1: Mystery House                                           | rehail |
 | ??? | adl | hires2              | Hi-Res Adventure #2: Wizard and the Princess                                 | TrivialBalderdash |
@@ -283,6 +284,13 @@
 | ??? | ags | zidjourney          | Zid Journey	                                                               | tag2015, GandalfTheWhite80 |
 | ??? | ags | zniwadventure       | Zniw Adventure	                                                           | tag2015, GandalfTheWhite80 |
 | ??? | alcachofa | aventuradecine   | Mortadelo y Filem??n: Una Aventura de Cine                                | GandalfTheWhite80 |
+| ?????? | alg | cpatrol            | Crime Patrol                                                                 | |
+| ?????? | alg | dwars              | Crime Patrol 2: Drug Wars                                                    | |
+| ?????? | alg | johnroc            | Who Shot Johnny Rock?                                                        | |
+| ?????? | alg | lbhunter           | The Last Bounty Hunter                                                       | |
+| ?????? | alg | maddog             | Mad Dog McCree                                                               | |
+| ?????? | alg | maddog2            | Mad Dog II: The Lost Gold                                                    | |
+| ?????? | alg | spirates           | Space Pirates                                                                | |
 | ??? | asylum | asylum          | Sanitarium                                                                   | JenniBee, GandalfTheWhite80 |
 | ??? | avalanche | avalanche           | Lord Avalot d'Argent                                                         | JenniBee, GandalfTheWhite80 |
 | ??? | awe | anotherworld        | Another World/Out of This World                                               | GandalfTheWhite80 |
@@ -291,6 +299,7 @@
 | ??? | bbvs | bbvs                | Beavis and Butt-head in Virtual Stupidity                                    | JenniBee, GandalfTheWhite80 |
 | ??? | bladerunner | bladerunner         | Blade Runner                                                                 | NekoFever, DavidAriyan |
 | ??? | bladerunner | bladerunner-final   | Blade Runner                                                                 | NekoFever, DavidAriyan, GandalfTheWhite80 |
+| ?????? | bolt | carnival           | Cartoon Carnival                                                            | |
 | ??? | buried | buried              | The Journeyman Project 2: Buried in Time                                     | GandalfTheWhite80 |
 | ??? | cge | soltys              | Soltys                                                                       | JenniBee, GandalfTheWhite80 |
 | ??? | cge2 | sfinx               | Sfinx                                                                        | JenniBee, GandalfTheWhite80 |
@@ -573,6 +582,8 @@
 | ??? | nancy | nancy5              | Nancy Drew: The Final Scene                                                  | GandalfTheWhite80 |
 | ??? | nancy | nancy6              | Nancy Drew: Secret of the Scarlet Hand                                       | GandalfTheWhite80 |
 | ??? | nancy | nancy7              | Nancy Drew: Ghost Dogs of Moon Lake                                          | GandalfTheWhite80 |
+| ?????? | nancy | nancy8              | Nancy Drew: The Haunted Carousel                                             | |
+| ?????? | nancy | nancy9              | Nancy Drew: Danger on Deception Island                                       | |
 | ??? | nancy | vampirediaries      | The Vampire Diaries                                                          | GandalfTheWhite80 |
 | ??? | neverhood | neverhood           | The Neverhood Chronicles                                                     | JenniBee, GandalfTheWhite80 |
 | ??? | ngi | fullpipe            | Full Pipe                                                                    | [App Store page](https://apps.apple.com/app/full-pipe-puzzle-adventure/id900071823), GandalfTheWhite80 |
@@ -580,6 +591,9 @@
 | ??? | pegasus | pegasus             | The Journeyman Project: Pegasus Prime                                        | JenniBee, GandalfTheWhite80 |
 | ??? | petka | petka1              | Red Comrades 1: Save the Galaxy                                              | GandalfTheWhite80 |
 | ??? | petka | petka2              | Red Comrades 2: For the Great Justice                                        | GandalfTheWhite80 |
+| ?????? | phoenixvr | amerzone        | Amerzone: The Explorer's Legacy                                             | |
+| ?????? | phoenixvr | lochness        | The Cameron Files: The Secret at Loch Ness                                 | |
+| ?????? | phoenixvr | necrono         | Necronomicon: The Dawning of Darkness                                      | |
 | ??? | pink | peril                | The Pink Panther: Passport to Peril                                        | tag2015, GandalfTheWhite80 |
 | ??? | pink | pokus                | The Pink Panther: Hokus Pokus Pink                                         | Tucky27 |
 | ??? | plumbers | plumbers            | Plumbers Don't Wear Ties!                                                    | nightm4re94, GandalfTheWhite80 |



From noreply at scummvm.org  Mon Jun  1 23:18:27 2026
From: noreply at scummvm.org (bluegr)
Date: Mon,  1 Jun 2026 23:18:27 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 1fd05cea96680bd707a00daef1a02f8df480eeb5
Message-ID: <20260601231827.945DA700591@ron.scummvm.net>

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:
2a05646354 NANCY: More work on the Nancy10+ taskbar buttons
d68decbb6d NANCY: Add workaround for hotspots in the Feeding Frenzy game in Nancy9
10d1caf9d7 NANCY: Fix item cursor when hovering over some hotspots in Nancy10+
24dda66e77 NANCY: Disable global font kerning, as the engine has its own kerning
1fd05cea96 NANCY: More work on cellphone search link records for Nancy10+


Commit: 2a05646354759539765b4679131601fc02a89467
    https://github.com/scummvm/scummvm/commit/2a05646354759539765b4679131601fc02a89467
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-06-02T02:18:10+03:00

Commit Message:
NANCY: More work on the Nancy10+ taskbar buttons

- Buttons are now enabled/disabled correctly
- Added (still unused) functionality for button notifications
- More work on the ControUIItems AR, which toggles the taskbar buttons

Changed paths:
    engines/nancy/action/miscrecords.cpp
    engines/nancy/action/miscrecords.h
    engines/nancy/state/scene.cpp
    engines/nancy/ui/taskbar.cpp
    engines/nancy/ui/taskbar.h


diff --git a/engines/nancy/action/miscrecords.cpp b/engines/nancy/action/miscrecords.cpp
index 5c373c50fb5..b7bab4de98f 100644
--- a/engines/nancy/action/miscrecords.cpp
+++ b/engines/nancy/action/miscrecords.cpp
@@ -191,17 +191,65 @@ void FrameTextBox::execute() {
 
 void ControlUIItems::readData(Common::SeekableReadStream &stream) {
 	_uiButton = stream.readUint16LE();
-	_flagA = stream.readByte();
+	_autoOpenOrBadgeSound = stream.readByte();
 	_flagB  = stream.readByte();
-	_scene1 = stream.readSint16LE();
-	_scene2 = stream.readSint16LE();
+	_startScene = stream.readSint16LE();
+	_endScene = stream.readSint16LE();
 }
 
 void ControlUIItems::execute() {
-	// TODO: finish this
-
-	NancySceneState.getTaskbar()->toggleButton(_uiButton, _flagA != 0);
-	debug("ControlUIItems: UIButton=%d, flagA=%d, flagB=%d, scene1=%d, scene2=%d", _uiButton, _flagA, _flagB, _scene1, _scene2);
+	// Value 1 auto-opens the popup selected by _uiButton. For the cell
+	// phone, _startScene (when set) is the scene to jump to once it opens,
+	// which places a call that starts a conversation there.
+	if (_autoOpenOrBadgeSound == 1) {
+		switch (_uiButton) {
+		case kUITypeInventory:
+			NancySceneState.getInventoryPopup().open();
+			break;
+		case kUITypeNotebook:
+			NancySceneState.getNotebookPopup().open();
+			break;
+		case kUITypeCellphone:
+			NancySceneState.getCellPhonePopup().open();
+
+			if (_startScene != (int16)kNoScene) {
+				SceneChangeDescription scene;
+				scene.sceneID = _startScene;
+				scene.frameID = 0;
+				scene.verticalOffset = 0;
+				// The destination scene's sound carries the conversation audio.
+				scene.continueSceneSound = kLoadSceneSound;
+
+				// Pushed so AR 128 in the conversation scene can return here.
+				NancySceneState.pushScene();
+				NancySceneState.changeScene(scene);
+			}
+			break;
+		default:
+			break;
+		}
+	} else {
+		// Otherwise this AR toggles whether the button is disabled while the
+		// player is in scene range [_startScene, _endScene]. _flagB != 0 sets
+		// the toggle (a _startScene of 9997 means "from scene 0", with the
+		// range capped at 9997); _flagB == 0 clears it once a bound is 9999
+		// (kNoScene). The _autoOpenOrBadgeSound value selects the button's
+		// click sound in the original (not yet implemented).
+		UI::Taskbar *taskbar = NancySceneState.getTaskbar();
+		if (taskbar) {
+			if (_flagB != 0) {
+				int16 start = _startScene;
+				int16 end = _endScene;
+				if (_startScene == 9997) {
+					start = 0;
+					end = 9997;
+				}
+				taskbar->setDisabledRange(_uiButton, start, end);
+			} else if (_startScene == (int16)kNoScene || _endScene == (int16)kNoScene) {
+				taskbar->clearButtonOverride(_uiButton);
+			}
+		}
+	}
 
 	finishExecution();
 }
diff --git a/engines/nancy/action/miscrecords.h b/engines/nancy/action/miscrecords.h
index ed6cd179032..427bb7e5b1c 100644
--- a/engines/nancy/action/miscrecords.h
+++ b/engines/nancy/action/miscrecords.h
@@ -148,10 +148,10 @@ public:
 	void execute() override;
 
 	uint16 _uiButton = 0;
-	byte _flagA = 0;    // 0, 1 or 10
+	byte _autoOpenOrBadgeSound = 0; // 1 = auto-open popup; 0/10 = notification-badge click-sound selector
 	byte _flagB = 0;    // 0 = clear, 1 = enable+remember scene
-	int16 _scene1 = 0;  // start scene id (9999 = none)
-	int16 _scene2 = 0;  // end scene id (9999 = none)
+	int16 _startScene = 0; // start scene id (9999 = none); also the auto-open cell phone's call target
+	int16 _endScene = 0;   // end scene id (9999 = none)
 
 protected:
 	Common::String getRecordTypeName() const override { return "ControlUIItems"; }
diff --git a/engines/nancy/state/scene.cpp b/engines/nancy/state/scene.cpp
index 3cf4d222c41..02cefa63196 100644
--- a/engines/nancy/state/scene.cpp
+++ b/engines/nancy/state/scene.cpp
@@ -1059,11 +1059,9 @@ void Scene::load(bool fromSaveFile) {
 		_flags.sceneCounts.getOrCreateVal(_sceneState.currentScene.sceneID)++;
 	}
 
-	// Enable all task buttons
-	// TODO: This should be done elsewhere
+	// Re-evaluate taskbar notification states against the new scene.
 	if (g_nancy->getGameType() >= kGameTypeNancy10) {
-		for (int i = 0; i < 5; ++i)
-			_taskbar->toggleButton(i, true);
+		_taskbar->updateNotificationStates(_sceneState.currentScene.sceneID);
 	}
 
 	delete sceneIFF;
diff --git a/engines/nancy/ui/taskbar.cpp b/engines/nancy/ui/taskbar.cpp
index 995c1d0798e..d266c2a62bd 100644
--- a/engines/nancy/ui/taskbar.cpp
+++ b/engines/nancy/ui/taskbar.cpp
@@ -34,9 +34,11 @@ namespace UI {
 Taskbar::Taskbar() :
 		RenderObject(7),
 		_hoveredButton(-1),
-		_clickedButton(-1) {
+		_clickedButton(-1),
+		_currentScene(-1) {
 	for (uint i = 0; i < TASK::kNumButtons; ++i) {
 		_buttonStates[i] = kButtonIdle;
+		_enabled[i] = true;
 	}
 }
 
@@ -72,9 +74,14 @@ void Taskbar::drawButton(uint index, ButtonState state) {
 	auto *taskData = GetEngineData(TASK);
 	assert(taskData);
 
-	const UIButtonRecord &btn = taskData->buttons[index].button;
+	const TASK::ButtonRecord &rec = taskData->buttons[index];
+	const UIButtonRecord &btn = rec.button;
 
-	Common::Rect src = btn.sourceRects[state];
+	// The notification sprite lives outside the standard sourceRects
+	// array, in its own per-button rect.
+	Common::Rect src = (state == kButtonNotification)
+		? rec.notificationSrcRect
+		: btn.sourceRects[state];
 	if (src.isEmpty())
 		src = btn.sourceRects[kButtonIdle];
 	if (src.isEmpty())
@@ -96,8 +103,86 @@ void Taskbar::drawButton(uint index, ButtonState state) {
 	_needsRedraw = true;
 }
 
+Taskbar::ButtonState Taskbar::restingState(uint index) const {
+	if (index >= TASK::kNumButtons) {
+		return kButtonIdle;
+	}
+	if (!_enabled[index]) {
+		return kButtonDisabled;
+	}
+	const ButtonOverride &o = _overrides[index];
+	if (o.active && _currentScene >= o.startScene && _currentScene <= o.endScene) {
+		return o.state;
+	}
+	return kButtonIdle;
+}
+
+bool Taskbar::isButtonActive(uint index) const {
+	return _enabled[index] && restingState(index) != kButtonDisabled;
+}
+
 void Taskbar::toggleButton(uint index, bool enabled) {
-	drawButton(index, enabled ? kButtonIdle : kButtonDisabled);
+	if (index >= TASK::kNumButtons) {
+		return;
+	}
+	_enabled[index] = enabled;
+	if ((int)index != _hoveredButton) {
+		drawButton(index, restingState(index));
+	}
+}
+
+void Taskbar::setNotification(uint buttonIndex, int16 startScene, int16 endScene) {
+	if (buttonIndex >= TASK::kNumButtons) {
+		return;
+	}
+	_overrides[buttonIndex].active = true;
+	_overrides[buttonIndex].state = kButtonNotification;
+	_overrides[buttonIndex].startScene = startScene;
+	_overrides[buttonIndex].endScene = endScene;
+
+	// Re-render this button immediately unless the player is currently
+	// hovering it (hover takes priority over the override sprite).
+	if ((int)buttonIndex != _hoveredButton) {
+		drawButton(buttonIndex, restingState(buttonIndex));
+	}
+}
+
+void Taskbar::setDisabledRange(uint buttonIndex, int16 startScene, int16 endScene) {
+	if (buttonIndex >= TASK::kNumButtons) {
+		return;
+	}
+	_overrides[buttonIndex].active = true;
+	_overrides[buttonIndex].state = kButtonDisabled;
+	_overrides[buttonIndex].startScene = startScene;
+	_overrides[buttonIndex].endScene = endScene;
+
+	if ((int)buttonIndex != _hoveredButton) {
+		drawButton(buttonIndex, restingState(buttonIndex));
+	}
+}
+
+void Taskbar::clearButtonOverride(uint buttonIndex) {
+	if (buttonIndex >= TASK::kNumButtons) {
+		return;
+	}
+	_overrides[buttonIndex].active = false;
+
+	if ((int)buttonIndex != _hoveredButton) {
+		drawButton(buttonIndex, restingState(buttonIndex));
+	}
+}
+
+void Taskbar::updateNotificationStates(int16 currentSceneID) {
+	_currentScene = currentSceneID;
+	for (uint i = 0; i < TASK::kNumButtons; ++i) {
+		if ((int)i == _hoveredButton) {
+			continue;
+		}
+		const ButtonState desired = restingState(i);
+		if (_buttonStates[i] != desired) {
+			drawButton(i, desired);
+		}
+	}
 }
 
 void Taskbar::handleInput(NancyInput &input) {
@@ -108,18 +193,18 @@ void Taskbar::handleInput(NancyInput &input) {
 
 	int newHovered = -1;
 	for (uint i = 0; i < TASK::kNumButtons; ++i) {
-		if (taskData->buttons[i].button.destRect.contains(input.mousePos) && _buttonStates[i] != kButtonDisabled) {
+		if (isButtonActive(i) && taskData->buttons[i].button.destRect.contains(input.mousePos)) {
 			newHovered = i;
 			break;
 		}
 	}
 
-	// Update hover graphic on enter/exit. Always revert the previously-
-	// hovered button (even from kButtonPressed) so it never gets stuck
-	// after the cursor leaves.
+	// Update hover graphic on enter/exit. The previously-hovered button
+	// returns to its resting sprite (idle or notification) so it doesn't
+	// get stuck in hover/pressed after the cursor leaves.
 	if (newHovered != _hoveredButton) {
 		if (_hoveredButton != -1) {
-			drawButton(_hoveredButton, kButtonIdle);
+			drawButton(_hoveredButton, restingState(_hoveredButton));
 		}
 		if (newHovered != -1) {
 			drawButton(newHovered, kButtonHover);
@@ -141,6 +226,12 @@ void Taskbar::handleInput(NancyInput &input) {
 	} else if (input.input & NancyInput::kLeftMouseButtonUp) {
 		// Mouse released over the button: trigger the click action and
 		// snap the sprite back to hover (the cursor is still over it).
+		// Acknowledging the click also clears a pending notification
+		// for this button, so re-opening the popup doesn't keep blinking.
+		// A scene-ranged disable override is left intact.
+		if (_overrides[newHovered].state == kButtonNotification) {
+			_overrides[newHovered].active = false;
+		}
 		drawButton(newHovered, kButtonHover);
 		_clickedButton = newHovered;
 
diff --git a/engines/nancy/ui/taskbar.h b/engines/nancy/ui/taskbar.h
index d3cfcf7cb65..f8b1d92dd22 100644
--- a/engines/nancy/ui/taskbar.h
+++ b/engines/nancy/ui/taskbar.h
@@ -42,27 +42,61 @@ public:
 	void registerGraphics() override;
 	void handleInput(NancyInput &input);
 
+	// Enable / disable a taskbar button. A disabled button is rendered
+	// in its disabled sprite and ignores clicks.
 	void toggleButton(uint index, bool enabled);
 
+	// Configure a per-button override that is active only while the player
+	// is in a scene whose ID falls in [startScene, endScene]. The button
+	// renders in its notification (badge) sprite, or its disabled sprite,
+	// for that range; outside it reverts to idle.
+	// setDisabledRange is driven by AR 29 (ControlUIItems, _flagB != 0).
+	// setNotification renders the badge sprite; the source that should drive
+	// it has not been identified yet (it is NOT ControlUIItems).
+	void setNotification(uint buttonIndex, int16 startScene, int16 endScene);
+	void setDisabledRange(uint buttonIndex, int16 startScene, int16 endScene);
+	void clearButtonOverride(uint buttonIndex);
+
+	// Re-evaluate which buttons should currently show their override
+	// sprite. Call after a scene change so the range check kicks in.
+	void updateNotificationStates(int16 currentSceneID);
+
 	// Returns the index of the button that was clicked this frame, or -1
 	// if none. Cleared on the next call to handleInput().
 	int getClickedButton() const { return _clickedButton; }
 
 private:
 	enum ButtonState {
-		kButtonIdle     = 0,
-		kButtonHover    = 1,
-		kButtonPressed  = 2,
-		kButtonDisabled = 3
+		kButtonIdle         = 0,
+		kButtonHover        = 1,
+		kButtonPressed      = 2,
+		kButtonDisabled     = 3,   // not clickable
+		kButtonNotification = 4    // popup has new content (badge sprite)
+	};
+
+	// A scene-ranged sprite override for one button. While active and the
+	// current scene is within [startScene, endScene] the button renders in
+	// `state` (kButtonNotification or kButtonDisabled).
+	struct ButtonOverride {
+		bool active = false;
+		ButtonState state = kButtonIdle;
+		int16 startScene = -1;
+		int16 endScene = -1;
 	};
 
 	void drawButton(uint index, ButtonState state);
+	ButtonState restingState(uint index) const;
+	// True when the button currently accepts hover/click (not disabled).
+	bool isButtonActive(uint index) const;
 
 	Graphics::ManagedSurface _backgroundImage; // TASK::imageName (e.g. "Frame")
 	Graphics::ManagedSurface _buttonImage;     // buttons' primaryImageName (e.g. "UIShared_OVL")
 	int _hoveredButton;
 	int _clickedButton;
+	int16 _currentScene;
+	bool _enabled[5];
 	ButtonState _buttonStates[5];
+	ButtonOverride _overrides[5];
 };
 
 } // End of namespace UI


Commit: d68decbb6d65b724185b3bf988edb5d121194f05
    https://github.com/scummvm/scummvm/commit/d68decbb6d65b724185b3bf988edb5d121194f05
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-06-02T02:18:12+03:00

Commit Message:
NANCY: Add workaround for hotspots in the Feeding Frenzy game in Nancy9

Fix #16792

Changed paths:
    engines/nancy/action/interactivevideo.cpp


diff --git a/engines/nancy/action/interactivevideo.cpp b/engines/nancy/action/interactivevideo.cpp
index 3ee0cd42bd4..b50851bf8e8 100644
--- a/engines/nancy/action/interactivevideo.cpp
+++ b/engines/nancy/action/interactivevideo.cpp
@@ -55,6 +55,19 @@ void InteractiveVideo::readData(Common::SeekableReadStream &stream) {
 
 	readFilename(*ivFile, _videoName);
 
+	// WORKAROUND: In Nancy 9, the Feeding Frenzy mini-game plays 6 videos (the whales that pop up)
+	// with a normal arrow cursor. In such cases, the cursor manager reverts to the default arrow
+	// cursor, if an item is held (which, in this case is a fish). We replace these cursors with a
+	// normal one, which allows for the held item to be visible. Making this a workaround for that
+	// scene, since a change in that part of the cursor code would require a full regression test in
+	// all supported games. Fixes bug #16792.
+	const uint16 sceneId = NancySceneState.getSceneInfo().sceneID;
+	if (g_nancy->getGameType() == kGameTypeNancy9 && (sceneId == 2992 || sceneId == 2995 || sceneId == 2996)) {
+		if (_videoName.toString().contains("WhaleFeed") && _cursors[0] == CursorManager::kNormalArrow) {
+			_cursors[0] = CursorManager::kNormal;
+		}
+	}
+
 	uint32 numFrames = ivFile->readUint32LE();
 	_frames.resize(numFrames);
 	for (uint i = 0; i < numFrames; ++i) {


Commit: 10d1caf9d70dd6ae8210cebd2817c154616fb93e
    https://github.com/scummvm/scummvm/commit/10d1caf9d70dd6ae8210cebd2817c154616fb93e
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-06-02T02:18:13+03:00

Commit Message:
NANCY: Fix item cursor when hovering over some hotspots in Nancy10+

Fixes the cursor when hovering the key over the rolltop in Nancy10

Changed paths:
    engines/nancy/action/datarecords.cpp
    engines/nancy/action/datarecords.h


diff --git a/engines/nancy/action/datarecords.cpp b/engines/nancy/action/datarecords.cpp
index 65f2009ce91..6610f500be0 100644
--- a/engines/nancy/action/datarecords.cpp
+++ b/engines/nancy/action/datarecords.cpp
@@ -361,6 +361,12 @@ void EventFlagsMultiHS::readData(Common::SeekableReadStream &stream) {
 	}
 }
 
+CursorManager::CursorType EventFlagsMultiHS::getHoverCursor() const {
+	if (g_nancy->getGameType() >= kGameTypeNancy10 && NancySceneState.getHeldItem() >= 0)
+		return CursorManager::kHotspot;
+	return _hoverCursor;
+}
+
 void EventFlagsMultiHS::execute() {
 	switch (_state) {
 	case kBegin:
diff --git a/engines/nancy/action/datarecords.h b/engines/nancy/action/datarecords.h
index d8a5700ab42..d200227d2f6 100644
--- a/engines/nancy/action/datarecords.h
+++ b/engines/nancy/action/datarecords.h
@@ -120,7 +120,7 @@ public:
 	void readData(Common::SeekableReadStream &stream) override;
 	void execute() override;
 
-	CursorManager::CursorType getHoverCursor() const override { return _hoverCursor; }
+	CursorManager::CursorType getHoverCursor() const override;
 
 	CursorManager::CursorType _hoverCursor = CursorManager::kHotspot;
 	Common::Array<HotspotDescription> _hotspots;


Commit: 24dda66e7783fad9d73b5f71dbfe73cea2e0b05b
    https://github.com/scummvm/scummvm/commit/24dda66e7783fad9d73b5f71dbfe73cea2e0b05b
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-06-02T02:18:14+03:00

Commit Message:
NANCY: Disable global font kerning, as the engine has its own kerning

Changed paths:
    engines/nancy/font.cpp
    engines/nancy/font.h


diff --git a/engines/nancy/font.cpp b/engines/nancy/font.cpp
index 13ce11d4ace..2235f9cc837 100644
--- a/engines/nancy/font.cpp
+++ b/engines/nancy/font.cpp
@@ -51,7 +51,7 @@ void Font::read(Common::SeekableReadStream &stream) {
 	_color1CoordsOffset.y = stream.readUint32LE();
 
 	_spaceWidth = stream.readUint16LE();
-	_charSpace = stream.readSint16LE() - 1; // Account for the added pixel in readRect
+	_charSpace = stream.readSint16LE();
 
 	_uppercaseOffset					= stream.readUint16LE();
 	_lowercaseOffset					= stream.readUint16LE();
diff --git a/engines/nancy/font.h b/engines/nancy/font.h
index 9ebab817312..c3d73477efe 100644
--- a/engines/nancy/font.h
+++ b/engines/nancy/font.h
@@ -46,7 +46,7 @@ public:
 	int getFontHeight() const override { return _fontHeight - 1; }
 	int getMaxCharWidth() const override { return _maxCharWidth; }
 	int getCharWidth(uint32 chr) const override;
-	int getKerningOffset(uint32 left, uint32 right) const override { return 1; }
+	int getKerningOffset(uint32 left, uint32 right) const override { return 0; }
 
 	void drawChar(Graphics::Surface *dst, uint32 chr, int x, int y, uint32 color) const override;
 


Commit: 1fd05cea96680bd707a00daef1a02f8df480eeb5
    https://github.com/scummvm/scummvm/commit/1fd05cea96680bd707a00daef1a02f8df480eeb5
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-06-02T02:18:16+03:00

Commit Message:
NANCY: More work on cellphone search link records for Nancy10+

Changed paths:
    engines/nancy/action/miscrecords.cpp
    engines/nancy/action/miscrecords.h


diff --git a/engines/nancy/action/miscrecords.cpp b/engines/nancy/action/miscrecords.cpp
index b7bab4de98f..1c45bbd347f 100644
--- a/engines/nancy/action/miscrecords.cpp
+++ b/engines/nancy/action/miscrecords.cpp
@@ -275,14 +275,12 @@ void AddSearchLink::readData(Common::SeekableReadStream &stream) {
 
 	_extra  = stream.readSint16LE();
 	_flag = stream.readSint16LE();
-	_scene = stream.readSint16LE();
+	_eventFlag = stream.readSint16LE();
 }
 
 void AddSearchLink::execute() {
-	// TODO: finish this
-
-	debug("AddSearchLink: mode=%d, key=%s, value=%s, extra=%d, scene1=%d, scene2=%d", _mode, _key.c_str(), _value.c_str(), _extra, _flag, _scene);
-
+	//NancySceneState.getCellPhonePopup().addSearchLink(
+	//	_mode, _key, _value, _extra, _flag, _eventFlag);
 	finishExecution();
 }
 
diff --git a/engines/nancy/action/miscrecords.h b/engines/nancy/action/miscrecords.h
index 427bb7e5b1c..b5dd38cc958 100644
--- a/engines/nancy/action/miscrecords.h
+++ b/engines/nancy/action/miscrecords.h
@@ -178,11 +178,11 @@ public:
 	void execute() override;
 
 	int16 _mode = 0;
-	Common::String _key;
-	Common::String _value;
-	int16 _extra = 0;
-	int16 _flag = 0;
-	int16 _scene = 0;
+	Common::String _key;          // CVTX key for the list row text (both modes)
+	Common::String _value;        // body CVTX key (mode 0/email); unused for mode 1
+	int16 _extra = 0;             // page index (mode 1); unused for mode 0
+	int16 _flag = 0;              // stored but unused by the original; reserved
+	int16 _eventFlag = 0;         // event-flag index set when the entry is opened
 
 protected:
 	Common::String getRecordTypeName() const override { return "AddSearchLink"; }



From noreply at scummvm.org  Tue Jun  2 07:11:35 2026
From: noreply at scummvm.org (dreammaster)
Date: Tue,  2 Jun 2026 07:11:35 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 6290b52667e3450312277be40f845544babc3bdf
Message-ID: <20260602071135.7F924700573@ron.scummvm.net>

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

Summary:
e9d76282d9 MADS: DRAGONSPHERE: Workaround for conv_reset invalid indexes
6ef0ceed55 MADS: DRAGONSPHERE: Room 614 conv fixes
b3112015d3 MADS: DRAGONSPHERE: Properly reset room 614 wizard death timer
6290b52667 MADS: DRAGONSPHERE: Correctly show item that Polystone is mimicing


Commit: e9d76282d9e20f53c8553b0d98ceb73543e86a6d
    https://github.com/scummvm/scummvm/commit/e9d76282d9e20f53c8553b0d98ceb73543e86a6d
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-06-02T17:11:22+10:00

Commit Message:
MADS: DRAGONSPHERE: Workaround for conv_reset invalid indexes

Changed paths:
    engines/mads/madsv2/core/conv.cpp


diff --git a/engines/mads/madsv2/core/conv.cpp b/engines/mads/madsv2/core/conv.cpp
index f4801ebfd76..dcb59344a44 100644
--- a/engines/mads/madsv2/core/conv.cpp
+++ b/engines/mads/madsv2/core/conv.cpp
@@ -1689,13 +1689,16 @@ done:
 }
 
 void conv_reset(int id) {
-	if (conv_slots[id]) {
+	// WORKAROUND: Only allowing freeing by slot within the allowed range
+	if (id < CONV_MAX_DATA && conv_slots[id]) {
 		mem_free(conv_data[id]);
 		mem_free(conv[id]);
 		conv_data[id] = nullptr;
 		conv[id] = nullptr;
 		conv_slots[id] = 0;
 	}
+
+	conv_indexes[id] = 0;
 }
 
 int conv_append(Common::WriteStream *handle) {


Commit: 6ef0ceed5512e9bb5f9b3037e90e69e0d4861f77
    https://github.com/scummvm/scummvm/commit/6ef0ceed5512e9bb5f9b3037e90e69e0d4861f77
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-06-02T17:11:22+10:00

Commit Message:
MADS: DRAGONSPHERE: Room 614 conv fixes

Changed paths:
    engines/mads/madsv2/dragonsphere/rooms/room614.cpp


diff --git a/engines/mads/madsv2/dragonsphere/rooms/room614.cpp b/engines/mads/madsv2/dragonsphere/rooms/room614.cpp
index 7c3ff5dfd87..9832f762e39 100644
--- a/engines/mads/madsv2/dragonsphere/rooms/room614.cpp
+++ b/engines/mads/madsv2/dragonsphere/rooms/room614.cpp
@@ -1117,11 +1117,6 @@ static void process_conv_32() {
 		local->wiz_ice_action = KILL_PID;
 		break;
 
-	case conv032_pid_death_b_b:
-		conv_abort();
-		local->wiz_float_action = KILL_PID;
-		break;
-
 	case 7:
 		*conv_my_next_start = 8;
 		conv_abort();
@@ -1136,13 +1131,25 @@ static void process_conv_32() {
 		local->wiz_float_action = WAITING_FOR_STONE;
 		break;
 
-	case 17:
-		*conv_my_next_start = 18;
+	case conv032_pain_b_b:
+		*conv_my_next_start = conv032_pain;
 		conv_abort();
 		local->activate_timer = true;
 		player.commands_allowed = true;
 		break;
 
+	case 17:
+		*conv_my_next_start = 18;
+		conv_abort();
+		local->wiz_ice_action = WAITING_FOR_STONE;
+		local->wiz_float_action = WAITING_FOR_STONE;
+		break;
+
+	case conv032_pid_death_b_b:
+		conv_abort();
+		local->wiz_float_action = KILL_PID;
+		break;
+
 	case 22:
 		*conv_my_next_start = 23;
 		conv_abort();
@@ -1150,11 +1157,19 @@ static void process_conv_32() {
 		local->wiz_float_action = WAITING_FOR_STONE;
 		break;
 
-	case conv032_pain_b_b:
-		*conv_my_next_start = conv032_pain;
+	case 25:
+		*conv_my_next_start = 26;
 		conv_abort();
+		local->death_timer = 0;
+		local->clock = 0;
+		local->doofus = true;
 		local->activate_timer = true;
-		player.commands_allowed = true;
+		player.commands_allowed = false;
+		local->wiz_ice_action = BACK_TO_CAMERA;
+		local->wiz_float_action = BACK_TO_CAMERA;
+		break;
+
+	default:
 		break;
 	}
 


Commit: b3112015d3a7a7282b32104610c56d4205a6fb90
    https://github.com/scummvm/scummvm/commit/b3112015d3a7a7282b32104610c56d4205a6fb90
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-06-02T17:11:22+10:00

Commit Message:
MADS: DRAGONSPHERE: Properly reset room 614 wizard death timer

Changed paths:
    engines/mads/madsv2/dragonsphere/rooms/room614.cpp


diff --git a/engines/mads/madsv2/dragonsphere/rooms/room614.cpp b/engines/mads/madsv2/dragonsphere/rooms/room614.cpp
index 9832f762e39..d0cabe2fed7 100644
--- a/engines/mads/madsv2/dragonsphere/rooms/room614.cpp
+++ b/engines/mads/madsv2/dragonsphere/rooms/room614.cpp
@@ -1135,6 +1135,7 @@ static void process_conv_32() {
 		*conv_my_next_start = conv032_pain;
 		conv_abort();
 		local->activate_timer = true;
+		local->death_timer = 0;
 		player.commands_allowed = true;
 		break;
 
@@ -1160,10 +1161,10 @@ static void process_conv_32() {
 	case 25:
 		*conv_my_next_start = 26;
 		conv_abort();
-		local->death_timer = 0;
 		local->clock = 0;
 		local->doofus = true;
 		local->activate_timer = true;
+		local->death_timer = 0;
 		player.commands_allowed = false;
 		local->wiz_ice_action = BACK_TO_CAMERA;
 		local->wiz_float_action = BACK_TO_CAMERA;


Commit: 6290b52667e3450312277be40f845544babc3bdf
    https://github.com/scummvm/scummvm/commit/6290b52667e3450312277be40f845544babc3bdf
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-06-02T17:11:23+10:00

Commit Message:
MADS: DRAGONSPHERE: Correctly show item that Polystone is mimicing

Changed paths:
    engines/mads/madsv2/core/inter.cpp


diff --git a/engines/mads/madsv2/core/inter.cpp b/engines/mads/madsv2/core/inter.cpp
index d67ce4ecc7d..16a0b0bbdd5 100644
--- a/engines/mads/madsv2/core/inter.cpp
+++ b/engines/mads/madsv2/core/inter.cpp
@@ -42,10 +42,13 @@
 #include "mads/madsv2/core/video.h"
 #include "mads/madsv2/core/matte.h"
 #include "mads/madsv2/core/game.h"
+#include "mads/madsv2/core/global.h"
 #include "mads/madsv2/core/imath.h"
 #include "mads/madsv2/core/env.h"
 #include "mads/madsv2/core/error.h"
 #include "mads/madsv2/forest/extra.h"
+#include "mads/madsv2/dragonsphere/mads/inventory.h"
+#include "mads/madsv2/dragonsphere/global.h"
 #include "mads/madsv2/engine.h"
 
 namespace MADS {
@@ -1939,7 +1942,13 @@ void inter_spin_object(int object_id) {
 	if (inter_object_routine == NULL) {
 		Common::strcpy_s(temp_buf, "*OB");
 
-		env_catint(temp_buf, object_id, 3);
+		// Special handling for the Polystone in Dragonsphere, which can mimic other items
+		int objectId = object_id;
+		if (g_engine->getGameID() == GType_Dragonsphere && object_id == Dragonsphere::polystone &&
+			global[Dragonsphere::object_imitated] != -1)
+			objectId = global[Dragonsphere::object_imitated];
+
+		env_catint(temp_buf, objectId, 3);
 
 		Common::strcat_s(temp_buf, "I");
 



From noreply at scummvm.org  Tue Jun  2 10:21:58 2026
From: noreply at scummvm.org (neuromancer)
Date: Tue,  2 Jun 2026 10:21:58 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 964b4850bffa3b9ef541edf6620f79248c53388c
Message-ID: <20260602102158.6ED88700573@ron.scummvm.net>

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:
ec72a8167f SCUMM: RA2: clear buffer after each frame in level 5
964b4850bf SCUMM: RA2: better collision detection for level 4


Commit: ec72a8167fa1392a9bee6d4ba4485f6b09f8569c
    https://github.com/scummvm/scummvm/commit/ec72a8167fa1392a9bee6d4ba4485f6b09f8569c
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-02T12:20:56+02:00

Commit Message:
SCUMM: RA2: clear buffer after each frame in level 5

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


diff --git a/engines/scumm/insane/rebel2/runlevels.cpp b/engines/scumm/insane/rebel2/runlevels.cpp
index 2d054d0c888..63167338abb 100644
--- a/engines/scumm/insane/rebel2/runlevels.cpp
+++ b/engines/scumm/insane/rebel2/runlevels.cpp
@@ -767,8 +767,9 @@ int InsaneRebel2::runLevel5() {
 		clearBit(0);
 
 		// Play gameplay (05PLAY.SAN)
+		// Original: FUN_0041f4d0("05PLAY.SAN", 8, -1, -1, 0)
 		debug("Rebel2: Level 5 gameplay");
-		if (!playLevelSegment("LEV05/05PLAY.SAN", 0x28))
+		if (!playLevelSegment("LEV05/05PLAY.SAN", 0x08))
 			return kLevelQuit;
 
 		if (_playerShield > 0) {
diff --git a/engines/scumm/smush/rebel/smush_player_ra2.cpp b/engines/scumm/smush/rebel/smush_player_ra2.cpp
index 87fb6db2eef..5f22d5dc428 100644
--- a/engines/scumm/smush/rebel/smush_player_ra2.cpp
+++ b/engines/scumm/smush/rebel/smush_player_ra2.cpp
@@ -87,6 +87,8 @@ void SmushPlayerRebel2::initGamePlayerFields() {
 	_lastLoadChunkIdx = -1;
 	_totalLoadChunks = 0;
 	_ra2FrameSourceSkipY = 0;
+	_ra2FrameObjectSurfaceWidth = 0;
+	_ra2FrameObjectSurfaceHeight = 0;
 	_scrollX = 0;
 	_scrollY = 0;
 }
@@ -115,14 +117,14 @@ void SmushPlayerRebel2::initGameVideoState() {
 
 	// Handle background preservation between videos:
 	// - Cinematic videos (flags 0x20) clear the buffer for a fresh start
-	// - Gameplay videos (flags 0x28) preserve the existing screen content
+	// - Gameplay videos (flags 0x08) preserve the existing screen content
 	if (_dst != nullptr) {
 		VirtScreen *vs = &_vm->_virtscr[kMainVirtScreen];
 		if ((_curVideoFlags & 0x08) == 0) {
 			// Cinematic mode (flags 0x20) - clear buffer for fresh video
 			memset(_dst, 0, vs->w * vs->h);
 		}
-		// Gameplay mode (flags 0x28): no-op, the existing screen content is preserved.
+		// Gameplay mode: no-op, the existing screen content is preserved.
 	}
 }
 
@@ -173,6 +175,8 @@ bool SmushPlayerRebel2::handleGameFetch(int32 subSize, Common::SeekableReadStrea
 		debugC(DEBUG_SMUSH, "SmushPlayerRebel2::handleGameFetch FTCH: Re-decoding stored FOBJ codec=%d pos=(%d,%d) size=%dx%d dataSize=%d",
 			_storedFobjCodec, _storedFobjLeft, _storedFobjTop,
 			_storedFobjWidth, _storedFobjHeight, _storedFobjDataSize);
+		ra2PrepareFrameObjectSurface(_storedFobjLeft, _storedFobjTop,
+			_storedFobjWidth, _storedFobjHeight);
 		decodeFrameObject(_storedFobjCodec, _storedFobjData,
 			_storedFobjLeft, _storedFobjTop,
 			_storedFobjWidth, _storedFobjHeight,
@@ -562,18 +566,40 @@ void SmushPlayerRebel2::ra2HandleTextResource(const char *str, int fontId, int c
  * RA2-specific buffer selection for non-standard FOBJ dimensions.
  * Returns true when the dimensions are valid and updates _dst, _width, _height as needed.
  */
+void SmushPlayerRebel2::ra2PrepareFrameObjectSurface(int left, int top, int width, int height) {
+	_ra2FrameObjectSurfaceWidth = width;
+	_ra2FrameObjectSurfaceHeight = height;
+
+	const int64 right = (int64)left + width;
+	const int64 bottom = (int64)top + height;
+	if (right > _ra2FrameObjectSurfaceWidth && right <= INT_MAX)
+		_ra2FrameObjectSurfaceWidth = (int)right;
+	if (bottom > _ra2FrameObjectSurfaceHeight && bottom <= INT_MAX)
+		_ra2FrameObjectSurfaceHeight = (int)bottom;
+}
+
 bool SmushPlayerRebel2::ra2SelectFrameBuffer(int width, int height) {
 	// Rebel2 uses a special buffer for all non-matching frames.
+	// Oversized FOBJ chunks are clipped against the original 424x260 surface.
 	// Level 1: First frame is 424x260 (background), small sprites reuse same buffer
 	// Level 2: Uses virtual screen directly (handled below when _specialBuffer stays null)
-	const int64 bufSize64 = (int64)width * height;
-	if (width <= 0 || height <= 0 || bufSize64 > INT_MAX) {
+	const int screenSize = _vm->_screenWidth * _vm->_screenHeight;
+	const int64 fobjSize64 = (int64)width * height;
+	int surfaceWidth = width;
+	int surfaceHeight = height;
+
+	if (fobjSize64 > screenSize) {
+		surfaceWidth = MAX(surfaceWidth, _ra2FrameObjectSurfaceWidth);
+		surfaceHeight = MAX(surfaceHeight, _ra2FrameObjectSurfaceHeight);
+	}
+
+	const int64 bufSize64 = (int64)surfaceWidth * surfaceHeight;
+	if (width <= 0 || height <= 0 || surfaceWidth <= 0 || surfaceHeight <= 0 || bufSize64 > INT_MAX) {
 		debugC(DEBUG_SMUSH, "SmushPlayerRebel2::ra2SelectFrameBuffer: Skipping invalid FOBJ dimensions %dx%d", width, height);
 		return false;
 	}
 
 	const int bufSize = (int)bufSize64;
-	const int screenSize = _vm->_screenWidth * _vm->_screenHeight;
 	if (bufSize > screenSize) {
 		// Frame is larger than screen - need special buffer
 		if (_specialBuffer == nullptr || bufSize > _specialBufferSize) {
@@ -586,18 +612,20 @@ bool SmushPlayerRebel2::ra2SelectFrameBuffer(int width, int height) {
 			free(_specialBuffer);
 			_specialBuffer = newSpecialBuffer;
 			_specialBufferSize = bufSize;
-			_width = width;
-			_height = height;
+			_width = surfaceWidth;
+			_height = surfaceHeight;
 			// Zero-fill the new buffer to avoid garbage in areas not written by FOBJ codec
 			memset(_specialBuffer, 0, bufSize);
-			debugC(DEBUG_SMUSH, "SmushPlayerRebel2::ra2SelectFrameBuffer: Allocated new _specialBuffer %dx%d (%d bytes)", width, height, bufSize);
+			debugC(DEBUG_SMUSH, "SmushPlayerRebel2::ra2SelectFrameBuffer: Allocated new _specialBuffer %dx%d for FOBJ %dx%d (%d bytes)",
+				surfaceWidth, surfaceHeight, width, height, bufSize);
 		}
 	}
 
 	if (bufSize > screenSize &&
 	    _specialBuffer != nullptr && _specialBufferSize >= bufSize) {
 		_dst = _specialBuffer;
-		debugC(DEBUG_SMUSH, "SmushPlayerRebel2::ra2SelectFrameBuffer: Using _specialBuffer for oversized FOBJ %dx%d", width, height);
+		debugC(DEBUG_SMUSH, "SmushPlayerRebel2::ra2SelectFrameBuffer: Using _specialBuffer %dx%d for oversized FOBJ %dx%d",
+			_width, _height, width, height);
 	} else {
 		if (_specialBuffer == nullptr) {
 			VirtScreen *vs = &_vm->_virtscr[kMainVirtScreen];
@@ -702,6 +730,7 @@ void SmushPlayerRebel2::ra2HandleGost(int32 subSize, Common::SeekableReadStream
 	// Priority bits (0x2000/0x4000/0x6000) are currently not modeled in
 	// ScummVM's SMUSH decoders. Coordinate-correct re-decode restores expected
 	// RA2 chapter preview behavior.
+	ra2PrepareFrameObjectSurface(left, top, _lastFobjWidth, _lastFobjHeight);
 	decodeFrameObject(_lastFobjCodec, _lastFobjData, left, top,
 		_lastFobjWidth, _lastFobjHeight, _lastFobjDataSize);
 }
@@ -761,6 +790,8 @@ bool SmushPlayerRebel2::handleGameStoreFrame() {
 }
 
 void SmushPlayerRebel2::handleGameFrameObjectPre(int codec, int left, int top, int width, int height, int dataSize) {
+	ra2PrepareFrameObjectSurface(left, top, width, height);
+
 	debugC(DEBUG_SMUSH, "SmushPlayerRebel2::handleGameFrameObjectPre FOBJ: frame=%d codec=%d pos=(%d,%d) size=%dx%d dataSize=%d storeFrame=%d _width=%d _height=%d",
 		_frame, codec, left, top, width, height, dataSize, _storeFrame, _width, _height);
 }
@@ -775,6 +806,23 @@ void SmushPlayerRebel2::handleGameFrameObjectPost(int codec, const byte *data, i
 
 void SmushPlayerRebel2::handleGameFrameStart() {
 	_hasFrameFobjForGost = false;
+
+	// FUN_00424d70 clears the target buffer before decoding a frame
+	// unless playback flags contain 0x20. Codec 21 frames in levels like
+	// LEV05/05PLAY.SAN only contain non-zero literals, so stale skipped pixels
+	// must not survive from the previous frame.
+	if ((_curVideoFlags & 0x20) == 0 && _dst != nullptr) {
+		int clearSize = 0;
+		if (_dst == _specialBuffer && _width > 0 && _height > 0) {
+			const int64 size64 = (int64)_width * _height;
+			if (size64 <= INT_MAX && size64 <= _specialBufferSize)
+				clearSize = (int)size64;
+		} else {
+			clearSize = _vm->_screenWidth * _vm->_screenHeight;
+		}
+		if (clearSize > 0)
+			memset(_dst, 0, clearSize);
+	}
 }
 
 bool SmushPlayerRebel2::handleGameSkipChunk(uint32 subType, int32 subSize, Common::SeekableReadStream &b) {
diff --git a/engines/scumm/smush/rebel/smush_player_ra2.h b/engines/scumm/smush/rebel/smush_player_ra2.h
index 084a727fb3d..c10a544cb76 100644
--- a/engines/scumm/smush/rebel/smush_player_ra2.h
+++ b/engines/scumm/smush/rebel/smush_player_ra2.h
@@ -66,6 +66,7 @@ private:
 	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);
+	void ra2PrepareFrameObjectSurface(int left, int top, int width, int height);
 	bool ra2SelectFrameBuffer(int width, int height);
 	bool ra2DecodeCodec(int codec, const uint8 *src, int left, int top,
 						int width, int height, int pitch, int dataSize);
@@ -82,6 +83,8 @@ private:
 	int16 _lastLoadChunkIdx;
 	int16 _totalLoadChunks;
 	int _ra2FrameSourceSkipY;
+	int _ra2FrameObjectSurfaceWidth;
+	int _ra2FrameObjectSurfaceHeight;
 };
 
 } // End of namespace Scumm


Commit: 964b4850bffa3b9ef541edf6620f79248c53388c
    https://github.com/scummvm/scummvm/commit/964b4850bffa3b9ef541edf6620f79248c53388c
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-02T12:20:56+02:00

Commit Message:
SCUMM: RA2: better collision detection for level 4

Changed paths:
    engines/scumm/insane/rebel2/iact.cpp
    engines/scumm/insane/rebel2/rebel.h
    engines/scumm/insane/rebel2/render.cpp
    engines/scumm/insane/rebel2/runlevels.cpp


diff --git a/engines/scumm/insane/rebel2/iact.cpp b/engines/scumm/insane/rebel2/iact.cpp
index 6fb4ffec57e..36dbc54a38e 100644
--- a/engines/scumm/insane/rebel2/iact.cpp
+++ b/engines/scumm/insane/rebel2/iact.cpp
@@ -810,13 +810,11 @@ void InsaneRebel2::handleOpcode6Handler7(Common::SeekableReadStream &b, int16 pa
 	const LevelDifficultyParams &flightParams =
 		kDifficultyTable[CLIP(_difficulty, 0, 5)][flightParamIndex];
 
-	// Step 1: Mouse input as offset from screen center.
+	// Step 1: Raw mouse input as offset from screen center.
 	// DAT_0047a7e0 = mouseX - 160, DAT_0047a7e2 = mouseY - 100.
-	// _vm->_mouse.x/y are in virtual screen coords (0-319, 0-199)
-	// consistent with handler 8 which uses _vm->_mouse.x directly.
-	Common::Point aimPos = getGameplayAimPoint();
-	int16 inputX = (int16)(aimPos.x - 160);  // DAT_0047a7e0
-	int16 inputY = (int16)(aimPos.y - 100);  // DAT_0047a7e2
+	// Handler 7 applies DAT_0047a7fe to its local vertical input after clamping.
+	int16 inputX = (int16)(_vm->_mouse.x - 160);  // DAT_0047a7e0
+	int16 inputY = (int16)(_vm->_mouse.y - 100);  // DAT_0047a7e2
 
 	// Clamp: mouse mode uses [-160, 160] for X, [-127, 127] for Y (lines 55-70).
 	if (inputX > 160)
@@ -831,7 +829,7 @@ void InsaneRebel2::handleOpcode6Handler7(Common::SeekableReadStream &b, int16 pa
 	// Step 2: Scale to [-127, 127] (lines 82-84).
 	// Mouse mode: scaledInputX = (DAT_0047a7e0 * 0x7f) / 0xa0.
 	int16 scaledInputX = (int16)((inputX * 127) / 160);
-	int16 scaledInputY = inputY;  // Y already in [-127, 127]
+	int16 scaledInputY = _optControlsFlipped ? (int16)-inputY : inputY;  // local_14
 
 	// Step 3: Velocity history + smoothed average (lines 141-157).
 	for (int i = 24; i > 0; i--) {
diff --git a/engines/scumm/insane/rebel2/rebel.h b/engines/scumm/insane/rebel2/rebel.h
index 0477679bd45..73b03abc07a 100644
--- a/engines/scumm/insane/rebel2/rebel.h
+++ b/engines/scumm/insane/rebel2/rebel.h
@@ -432,6 +432,7 @@ public:
 	void resetLevelAttemptState(int initialPhase);
 	void resetLevelPhaseState(bool clearEnemies);
 	void resetLevelWaveState();
+	void resetHandler7FlightState();
 
 	// Random number helper (emulates FUN_004233a0)
 	int getRandomVariant(int max);
diff --git a/engines/scumm/insane/rebel2/render.cpp b/engines/scumm/insane/rebel2/render.cpp
index c93f1c24aa0..194333b6a0a 100644
--- a/engines/scumm/insane/rebel2/render.cpp
+++ b/engines/scumm/insane/rebel2/render.cpp
@@ -1549,11 +1549,12 @@ void InsaneRebel2::checkCollisionZones() {
 		return;
 
 	// Calculate aim position in centered coordinates.
-	// Original: local_10 = mouseOffset + 0xa0, then smoothed and clamped to [-0x34..0x34]
-	// Simplified mapping: mouse 0..320 ??? [-52..52], mouse 0..200 ??? [-45..45]
-	Common::Point aimPos = getGameplayAimPoint();
-	int16 aimX = (int16)((aimPos.x - 160) * 52 / 160);
-	int16 aimY = (int16)((100 - aimPos.y) * 45 / 100);
+	// Handler 0x26 applies the mouse-mode vertical inversion before DAT_0047a7fe
+	// (FUN_407FCB lines 108-123), so this must not use getGameplayAimPoint().
+	const int rawX = _vm->_mouse.x - 160;
+	const int rawY = _vm->_mouse.y - 100;
+	int16 aimX = (int16)(rawX * 52 / 160);
+	int16 aimY = (int16)((_optControlsFlipped ? -rawY : rawY) * 45 / 100);
 
 	// Clamp to original ranges (DAT_0047a7fc < 1 path)
 	if (aimX > 0x34)
diff --git a/engines/scumm/insane/rebel2/runlevels.cpp b/engines/scumm/insane/rebel2/runlevels.cpp
index 63167338abb..24c88ce2394 100644
--- a/engines/scumm/insane/rebel2/runlevels.cpp
+++ b/engines/scumm/insane/rebel2/runlevels.cpp
@@ -294,6 +294,41 @@ void InsaneRebel2::resetLevelWaveState() {
 	_rebelWaveState = 0;
 }
 
+void InsaneRebel2::resetHandler7FlightState() {
+	_hitCooldown = 0;
+	_flyShipScreenX = 0xd4;
+	_flyShipScreenY = 0x82;
+	_smoothedVelocity = 0;
+	_verticalInput = 0;
+	_flyOverlayRepeatCount = 0;
+	_viewShift = 0;
+	_perspectiveX = 0;
+	_perspectiveY = 0;
+	_windParamX = 0;
+	_windParamY = 0;
+	_corridorLeftX = 0;
+	_corridorTopY = 0;
+	_corridorRightX = 0x1a8;
+	_corridorBottomY = 0x104;
+	_facingRight = false;
+	_spaceShotDirection = 0;
+
+	memset(_velocityHistory, 0, sizeof(_velocityHistory));
+	memset(_windHistoryX, 0, sizeof(_windHistoryX));
+	memset(_windHistoryY, 0, sizeof(_windHistoryY));
+
+	for (int i = 0; i < 2; i++) {
+		_spaceShots[i].counter = 0;
+		_spaceShots[i].targetX = 0;
+		_spaceShots[i].targetY = 0;
+		_spaceShots[i].leftGunX = 0;
+		_spaceShots[i].leftGunY = 0;
+		_spaceShots[i].rightGunX = 0;
+		_spaceShots[i].rightGunY = 0;
+		_spaceShots[i].variant = 0;
+	}
+}
+
 // ---------------------------------------------------------------------------
 // Level 2 Handler - FUN_00418063
 // Multiple parts with P1/P2/P3 subdirectories
@@ -600,6 +635,7 @@ int InsaneRebel2::runLevel3() {
 
 		// Reset bit table before gameplay starts - FUN_00423880 calls FUN_00423a00(0)
 		clearBit(0);
+		resetHandler7FlightState();
 
 		// Play phase 1 gameplay (03PLAY1.SAN)
 		debug("Rebel2: Level 3 Phase 1");
@@ -651,6 +687,7 @@ int InsaneRebel2::runLevel3() {
 
 		// Reset bit table before gameplay starts
 		clearBit(0);
+		resetHandler7FlightState();
 
 		// Play phase 2 gameplay (03PLAY2.SAN)
 		debug("Rebel2: Level 3 Phase 2");



From noreply at scummvm.org  Tue Jun  2 10:35:49 2026
From: noreply at scummvm.org (mduggan)
Date: Tue,  2 Jun 2026 10:35:49 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 480c02a26aad14270a4acd50a7417f84a38b670c
Message-ID: <20260602103549.949A6700573@ron.scummvm.net>

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

Summary:
480c02a26a ACCESS: Add keyboard support for commands


Commit: 480c02a26aad14270a4acd50a7417f84a38b670c
    https://github.com/scummvm/scummvm/commit/480c02a26aad14270a4acd50a7417f84a38b670c
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2026-06-02T20:35:33+10:00

Commit Message:
ACCESS: Add keyboard support for commands

Avoid having to go through the menu.  Also fixes the menu usage and mouse
cursor slightly.

Changed paths:
    engines/access/noctropolis/noctropolis_inventory.cpp
    engines/access/noctropolis/noctropolis_room.cpp
    engines/access/polygon.cpp


diff --git a/engines/access/noctropolis/noctropolis_inventory.cpp b/engines/access/noctropolis/noctropolis_inventory.cpp
index e4af7990a6f..98b780cafc6 100644
--- a/engines/access/noctropolis/noctropolis_inventory.cpp
+++ b/engines/access/noctropolis/noctropolis_inventory.cpp
@@ -49,6 +49,8 @@ int NoctropolisInventory::displayInv() {
 
 	setUseItem(-1);
 
+	_vm->_events->setCursor(CURSOR_ARROW);
+
 	if (!_keepInventoryPosition) {
 		int16 warpMouseX, warpMouseY;
 		_inventoryBase.x = _vm->_events->clipMouseCenter(_vm->_events->_mousePos.x, 252, 640, warpMouseX);
@@ -84,6 +86,10 @@ int NoctropolisInventory::displayInv() {
 		int x = _vm->_events->_mousePos.x - _inventoryBase.x - 23;
 		int y = _vm->_events->_mousePos.y - _inventoryBase.y - 26;
 
+		Common::CustomEventType action;
+		if (_vm->_events->getAction(action) && action == kActionSkip)
+			break;
+
 		if (prevTopItemIndex != _topItemIndex) {
 
 			int slotIndex = 0, slotCount = 20, skipIndex = 0;
diff --git a/engines/access/noctropolis/noctropolis_room.cpp b/engines/access/noctropolis/noctropolis_room.cpp
index eecec716f0f..e75a26b183c 100644
--- a/engines/access/noctropolis/noctropolis_room.cpp
+++ b/engines/access/noctropolis/noctropolis_room.cpp
@@ -201,12 +201,20 @@ void NoctropolisRoom::doCommands() {
 	if (_vm->_events->_interfaceOff) {
 		_vm->_events->setNormalCursor(CURSOR_DARK_ANKH);
 	} else {
+		Common::CustomEventType action;
 		if (_vm->_events->_rightButton) {
 			_vm->_events->debounceRight();
 			roomMenu();
+		} else if (_vm->_events->getAction(action)) {
+			const AccessActionCode *actionCodes = _vm->getActionCodes();
+			for (int i = 0; actionCodes[i]._action != kActionNone; ++i) {
+				if (actionCodes[i]._action == action) {
+					handleCommand(actionCodes[i]._code);
+					break;
+				}
+			}
 		}
 
-		// TODO: Check keyboard commands
 		Common::Point pt = _vm->_events->calcRawMouse();
 		int hotspotIndex = -1;
 
@@ -357,6 +365,7 @@ int NoctropolisRoom::validateBox(int boxId) {
 
 
 void NoctropolisRoom::roomMenu() {
+	_vm->_events->setCursor(CURSOR_ARROW);
 	// Move the mouse to the centre of the menu
 	// see NoctRoomMenu::setPositionFromMouse.
 	Common::Point mousePt = _vm->_events->getMousePos();
diff --git a/engines/access/polygon.cpp b/engines/access/polygon.cpp
index 2884537206e..6b99f374a71 100644
--- a/engines/access/polygon.cpp
+++ b/engines/access/polygon.cpp
@@ -30,7 +30,8 @@ bool pointInside(const Common::Point * const points, int16 x, int16 y) {
 	uint j = 0;
 	while (points[j] != LISTEND)
 		j++;
-	for (uint k = 0; points[k].x >= 0 && points[k].y >= 0; k++) {
+	j--;
+	for (uint k = 0; points[k] != LISTEND; k++) {
 		if ((points[k].y <= y && y < points[j].y) ||
 			(points[j].y <= y && y < points[k].y)) {
 			if (x < (points[j].x - points[k].x) * (y - points[k].y) /



From noreply at scummvm.org  Tue Jun  2 11:22:54 2026
From: noreply at scummvm.org (ScummVM-Translations)
Date: Tue,  2 Jun 2026 11:22:54 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 d0bb28428d5e4acac54be65cf320f6614e858026
Message-ID: <20260602112254.F299F700573@ron.scummvm.net>

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

Summary:
d0bb28428d I18N: Update translation (French)


Commit: d0bb28428d5e4acac54be65cf320f6614e858026
    https://github.com/scummvm/scummvm/commit/d0bb28428d5e4acac54be65cf320f6614e858026
Author: Purple T (ZEONK at hotmail.com)
Date: 2026-06-02T11:22:49Z

Commit Message:
I18N: Update translation (French)

Currently translated at 100.0% (3204 of 3204 strings)

Changed paths:
    po/fr_FR.po


diff --git a/po/fr_FR.po b/po/fr_FR.po
index 9460dfbbe7c..df67f0d6b32 100644
--- a/po/fr_FR.po
+++ b/po/fr_FR.po
@@ -8,7 +8,7 @@ msgstr ""
 "Project-Id-Version: ScummVM 1.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
 "POT-Creation-Date: 2026-06-01 21:39+0000\n"
-"PO-Revision-Date: 2026-06-01 18:22+0000\n"
+"PO-Revision-Date: 2026-06-02 11:22+0000\n"
 "Last-Translator: Purple T <ZEONK at hotmail.com>\n"
 "Language-Team: French <https://translations.scummvm.org/projects/scummvm/"
 "scummvm/fr/>\n"
@@ -13782,16 +13782,12 @@ msgid "Fire / select"
 msgstr "Tirer / S??lectionner"
 
 #: engines/scumm/metaengine.cpp:1185
-#, fuzzy
-#| msgid "Move back"
 msgid "Menu back"
-msgstr "Reculer"
+msgstr "Menu retour"
 
 #: engines/scumm/metaengine.cpp:1190
-#, fuzzy
-#| msgid "Skip / menu"
 msgid "Skip / menu back"
-msgstr "Passer / menu"
+msgstr "Passer / menu retour"
 
 #: engines/scumm/metaengine.cpp:1206
 msgid "Rebel Assault II controls"
@@ -13802,10 +13798,8 @@ msgid "Cover / back"
 msgstr "Couverture / retour"
 
 #: engines/scumm/metaengine.cpp:1267
-#, fuzzy
-#| msgid "Skip walk"
 msgid "Skip / back"
-msgstr "Passer le d??placement"
+msgstr "Passer / retour"
 
 #: engines/scumm/metaengine.cpp:1274
 msgid "Skip / menu"



From noreply at scummvm.org  Tue Jun  2 13:56:07 2026
From: noreply at scummvm.org (neuromancer)
Date: Tue,  2 Jun 2026 13:56:07 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 904144411f57b697948e7083177cee23507cbe50
Message-ID: <20260602135607.C86C8700573@ron.scummvm.net>

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

Summary:
904144411f SCUMM: RA: add more RA1 hashes


Commit: 904144411f57b697948e7083177cee23507cbe50
    https://github.com/scummvm/scummvm/commit/904144411f57b697948e7083177cee23507cbe50
Author: Manuel Lauss (manuel.lauss at gmail.com)
Date: 2026-06-02T15:56:02+02:00

Commit Message:
SCUMM: RA: add more RA1 hashes

Add hashes for the V1.3 and V1.7 DOS EXEs, found in GOG Release.

Changed paths:
    devtools/scumm-md5.txt


diff --git a/devtools/scumm-md5.txt b/devtools/scumm-md5.txt
index 11029af684a..7c86cfa8e56 100644
--- a/devtools/scumm-md5.txt
+++ b/devtools/scumm-md5.txt
@@ -448,7 +448,9 @@ ft	Full Throttle
 	8ead2c01c48c2e3d15d6a89b86cd84ab	19697	he	DOS	-	-	-	Hebrew fan translation
 
 rebel1	Star Wars: Rebel Assault
-	b9ac90e4411da1b6f7dd075cc4b03c2a	214435	en	DOS	-	-	-	-
+	b9ac90e4411da1b6f7dd075cc4b03c2a	214435	en	DOS	-	v1.0	-	-
+	e210a57b756473be705d24b9378a8233	212383	en	DOS	-	v1.3	-	-
+	6bb0fe2d7816c5528c773f2898e4e0ff	212915	en	DOS	-	v1.7	-	-	
 
 rebel2	Star Wars: Rebel Assault II: The Hidden Empire
 	6b73a08c535c0544785d73ff812908a0	9430	en	DOS	-	-	-	-



From noreply at scummvm.org  Tue Jun  2 16:30:05 2026
From: noreply at scummvm.org (neuromancer)
Date: Tue,  2 Jun 2026 16:30:05 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 135ed69a93619f80f86227f916cc58997bd06c26
Message-ID: <20260602163005.ABA15700573@ron.scummvm.net>

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:
bfd9911cd8 SCUMM: RA2: better gamepad support for level 4
62fbbbba89 SCUMM: RA: sync subtitle support on real time with the ScummVM settings
135ed69a93 SCUMM: RA1: make sure touchpad and desktop users can skip cutscenes


Commit: bfd9911cd848f69d8e22dab04155e308cd07a366
    https://github.com/scummvm/scummvm/commit/bfd9911cd848f69d8e22dab04155e308cd07a366
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-02T18:29:45+02:00

Commit Message:
SCUMM: RA2: better gamepad support for level 4

Changed paths:
    engines/scumm/insane/rebel2/rebel.cpp


diff --git a/engines/scumm/insane/rebel2/rebel.cpp b/engines/scumm/insane/rebel2/rebel.cpp
index bfec3000e6a..f570cda54ca 100644
--- a/engines/scumm/insane/rebel2/rebel.cpp
+++ b/engines/scumm/insane/rebel2/rebel.cpp
@@ -602,11 +602,14 @@ bool InsaneRebel2::notifyEvent(const Common::Event &event) {
 	// every shot. We observe before ScummEngine (priority 1 > kEventManPriority), so
 	// dropping the event here prevents the clobber. Firing is driven by the
 	// kScummActionInsaneAttack action, not the pointer, so this does not affect shots.
-	// A genuine mouse/touch motion (nonzero relative delta) hands control back.
+	// A genuine mouse/touch motion (nonzero relative delta) normally hands control
+	// back; handler 0x26 keeps ownership until its gamepad reticle returns to center.
 	if (_gamepadAimActive && _gameState == kStateGameplay && !_menuInputActive) {
 		switch (event.type) {
 		case Common::EVENT_MOUSEMOVE:
 			if (event.relMouse.x != 0 || event.relMouse.y != 0) {
+				if (_rebelHandler == 0x26)
+					return true;
 				_gamepadAimActive = false; // real pointer motion takes over
 				break;
 			}
@@ -1623,35 +1626,71 @@ void InsaneRebel2::updateGameplayAimFromGamepad() {
 	if (_menuInputActive || _gameState != kStateGameplay)
 		return;
 
-	// The analog stick still reports the original joystick-style centered axis range
-	// [-127,127], while the dpad follows the original keyboard handler's normal
-	// movement step (FUN_405822: 3 units, or 5 with the DOS fast modifier).
-	int deltaX = 0;
-	int deltaY = 0;
-	bool activeGamepadAim = false;
-
 	const int dpadX = (_vm->getActionState(kScummActionInsaneRight) ? 1 : 0) -
 	                  (_vm->getActionState(kScummActionInsaneLeft) ? 1 : 0);
 	const int dpadY = (_vm->getActionState(kScummActionInsaneDown) ? 1 : 0) -
 	                  (_vm->getActionState(kScummActionInsaneUp) ? 1 : 0);
 
-	if (dpadX || dpadY) {
+	const int16 ax = applyRebel2AnalogDeadzone(_joystickAxisX);
+	const int16 ay = applyRebel2AnalogDeadzone(_joystickAxisY);
+	const int velX = CLIP<int>((int)ax * 127 / Common::JOYAXIS_MAX, -127, 127);
+	const int velY = CLIP<int>((int)ay * 127 / Common::JOYAXIS_MAX, -127, 127);
+
+	int deltaX = 0;
+	int deltaY = 0;
+	bool activeGamepadAim = false;
+
+	if (_rebelHandler == 0x26) {
+		// Retail RA2 maps joystick axes into a small centered handler 0x26 reticle box.
+		// ScummVM deliberately exposes the full 320x200 mouse aim range for gamepads
+		// too, but preserves the original joystick feel: curved response for precise
+		// center aiming, and automatic return to screen center when the stick is released.
+		int axisX = 0;
+		int axisY = 0;
+		if (dpadX || dpadY) {
+			axisX = dpadX * 127;
+			axisY = dpadY * 127;
+		} else {
+			axisX = velX;
+			axisY = velY;
+		}
+
+		if (axisX || axisY || _gamepadAimActive) {
+			const int centerX = 160;
+			const int centerY = 100;
+			const int absAxisX = ABS(axisX);
+			const int absAxisY = ABS(axisY);
+			const int curvedX = (absAxisX * absAxisX + 126) / 127;
+			const int curvedY = (absAxisY * absAxisY + 126) / 127;
+			const int targetX = axisX < 0 ?
+				centerX - curvedX * centerX / 127 :
+				centerX + curvedX * (319 - centerX) / 127;
+			const int targetY = axisY < 0 ?
+				centerY - curvedY * centerY / 127 :
+				centerY + curvedY * (199 - centerY) / 127;
+			const int maxStep = (axisX || axisY) ? 14 : 10;
+			const int distX = targetX - _vm->_mouse.x;
+			const int distY = targetY - _vm->_mouse.y;
+
+			if (distX || distY) {
+				deltaX = CLIP<int>(distX, -maxStep, maxStep);
+				deltaY = CLIP<int>(distY, -maxStep, maxStep);
+				activeGamepadAim = true;
+			} else {
+				_gamepadAimActive = (axisX || axisY);
+				return;
+			}
+		}
+	} else if (dpadX || dpadY) {
 		const int kOriginalDigitalStep = 3;
 		deltaX = dpadX * kOriginalDigitalStep;
 		deltaY = dpadY * kOriginalDigitalStep;
 		activeGamepadAim = true;
-	} else {
-		const int16 ax = applyRebel2AnalogDeadzone(_joystickAxisX);
-		const int16 ay = applyRebel2AnalogDeadzone(_joystickAxisY);
-		const int velX = CLIP<int>((int)ax * 127 / Common::JOYAXIS_MAX, -127, 127);
-		const int velY = CLIP<int>((int)ay * 127 / Common::JOYAXIS_MAX, -127, 127);
-
-		if (velX || velY) {
-			const int kAnalogMaxStep = 8;
-			deltaX = velX * kAnalogMaxStep / 127;
-			deltaY = velY * kAnalogMaxStep / 127;
-			activeGamepadAim = true;
-		}
+	} else if (velX || velY) {
+		const int kAnalogMaxStep = 8;
+		deltaX = velX * kAnalogMaxStep / 127;
+		deltaY = velY * kAnalogMaxStep / 127;
+		activeGamepadAim = true;
 	}
 
 	if (!activeGamepadAim)


Commit: 62fbbbba895d32abcd879424cbb079abba481fb7
    https://github.com/scummvm/scummvm/commit/62fbbbba895d32abcd879424cbb079abba481fb7
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-02T18:29:45+02:00

Commit Message:
SCUMM: RA: sync subtitle support on real time with the ScummVM settings

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


diff --git a/engines/scumm/insane/rebel1/menu.cpp b/engines/scumm/insane/rebel1/menu.cpp
index 2de991ea72b..56d9d11c8cb 100644
--- a/engines/scumm/insane/rebel1/menu.cpp
+++ b/engines/scumm/insane/rebel1/menu.cpp
@@ -900,6 +900,8 @@ void InsaneRebel1::renderHighScoresOverlay(byte *dst, int pitch, int width, int
 
 void InsaneRebel1::renderOptionsOverlay(byte *dst, int pitch, int width, int height) {
 	// --- Options submenu (matching original RunGameOptionsMenu) ---
+	_optTextEnabled = ConfMan.getBool("subtitles");
+
 	const char *kDiffNames[3] = { "EASY", "NORMAL", "HARD" };
 
 	const int titleW = getFontBankStringWidth("GAME OPTIONS");
@@ -1203,7 +1205,7 @@ void InsaneRebel1::runOptionsMenu() {
 				_vm->_mixer->muteSoundType(Audio::Mixer::kSpeechSoundType, !_optSfxEnabled);
 				break;
 			case 4: // Toggle dialogue text
-				_optTextEnabled = !_optTextEnabled;
+				_optTextEnabled = !ConfMan.getBool("subtitles");
 				ConfMan.setBool("subtitles", _optTextEnabled);
 				break;
 			case 5: // Toggle enhanced/original input style
diff --git a/engines/scumm/insane/rebel2/menu.cpp b/engines/scumm/insane/rebel2/menu.cpp
index 20bd7ef213c..f785cd7159c 100644
--- a/engines/scumm/insane/rebel2/menu.cpp
+++ b/engines/scumm/insane/rebel2/menu.cpp
@@ -1907,7 +1907,7 @@ int InsaneRebel2::processOptionsInput() {
 					_vm->_mixer->muteSoundType(Audio::Mixer::kSpeechSoundType, !_optVoicesEnabled);
 					break;
 				case 3:  // Text toggle
-					_optTextEnabled = !_optTextEnabled;
+					_optTextEnabled = !ConfMan.getBool("subtitles");
 					ConfMan.setBool("subtitles", _optTextEnabled);
 					break;
 				case 4:  // Controls toggle
@@ -1961,6 +1961,8 @@ void InsaneRebel2::drawOptionsOverlay(byte *renderBitmap, int pitch, int width,
 	if (!splayer)
 		return;
 
+	_optTextEnabled = ConfMan.getBool("subtitles");
+
 	// Build items array from TRS strings based on current toggle states
 	// TRS 89: title, 90/91: music, 92/93: sfx, 94/95: voices,
 	// 96/97: text, 98/99: controls, 100/101: rapid fire, 103: volume, 107: back


Commit: 135ed69a93619f80f86227f916cc58997bd06c26
    https://github.com/scummvm/scummvm/commit/135ed69a93619f80f86227f916cc58997bd06c26
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-02T18:29:45+02:00

Commit Message:
SCUMM: RA1: make sure touchpad and desktop users can skip cutscenes

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


diff --git a/engines/scumm/insane/rebel1/iact.cpp b/engines/scumm/insane/rebel1/iact.cpp
index db4c4593dc9..b5d28d028d7 100644
--- a/engines/scumm/insane/rebel1/iact.cpp
+++ b/engines/scumm/insane/rebel1/iact.cpp
@@ -857,20 +857,12 @@ void InsaneRebel1::preprocessMouseAxes(int16 &inputX, int16 &inputY, bool *usedJ
 		return;
 	}
 
-	// On touchscreen devices aiming comes from the on-screen gamepad (joystick), never the
-	// absolute touch/cursor position; and we must not recenter the system cursor
-	// (smush_warpMouse below), which would inject spurious motion and drift the reticle.
-	if (isTouchscreenActive()) {
-		inputX = 0;
-		inputY = 0;
-		_mouseVirtualValid = false;
-		return;
-	}
-
 	int16 logicalX = (int16)CLIP<int>(_vm->_mouse.x, 0, 319);
 	int16 logicalY = (int16)CLIP<int>(_vm->_mouse.y, 0, 199);
 
-	if (_optEnhancedControls) {
+	// Touchscreens cannot support the DOS recenter/warp model, so use the
+	// non-warping absolute axis path even when original input style is selected.
+	if (_optEnhancedControls || isTouchscreenActive()) {
 		_mouseVirtualValid = false;
 		_mouseBiasLatch = false;
 		_mouseBiasX = 0;
diff --git a/engines/scumm/insane/rebel1/menu.cpp b/engines/scumm/insane/rebel1/menu.cpp
index 56d9d11c8cb..4a88a5e9b92 100644
--- a/engines/scumm/insane/rebel1/menu.cpp
+++ b/engines/scumm/insane/rebel1/menu.cpp
@@ -612,12 +612,21 @@ bool InsaneRebel1::notifyEvent(const Common::Event &event) {
 	if (_vm->isPaused())
 		return false;
 
-	// On touchscreen devices, finger drags arrive as mouse-move events; do not let them
-	// hijack RA1 into absolute-cursor aiming (aiming there comes from the on-screen gamepad).
-	if (event.type == Common::EVENT_MOUSEMOVE && !_mouseRecentering && !isTouchscreenActive()) {
-		_activeInputSource = kInputSourceMouse;
+	if (isTouchscreenActive() && !_interactiveVideoActive && !_menuActive &&
+			(event.type == Common::EVENT_LBUTTONDOWN || event.type == Common::EVENT_LBUTTONUP)) {
+		_vm->_smushVideoShouldFinish = true;
+		return true;
 	}
 
+	if (event.type == Common::EVENT_MOUSEMOVE && !_mouseRecentering)
+		_activeInputSource = kInputSourceMouse;
+
+	// Android direct touch reports taps as left-clicks. Treat those as mouse input
+	// during gameplay so tap-to-aim-and-fire works even if a joystick event was last.
+	if (isTouchscreenActive() && _interactiveVideoActive && !_menuActive &&
+			event.type == Common::EVENT_LBUTTONDOWN && !_mouseRecentering)
+		_activeInputSource = kInputSourceMouse;
+
 	// ScummVM-exclusive feature: mouse navigation/clicking of the RA1 front-end menus.
 	if (handleMenuMouse(event))
 		return true;
@@ -730,6 +739,10 @@ bool InsaneRebel1::notifyEvent(const Common::Event &event) {
 		if (event.customType == kScummActionInsaneBack) {
 			if (!pressed)
 				return true;
+			if (_player && !_interactiveVideoActive && !_menuActive) {
+				_vm->_smushVideoShouldFinish = true;
+				return true;
+			}
 			if (_player) {
 				const bool wasPaused = _player->_paused;
 				if (!wasPaused)
@@ -749,6 +762,14 @@ bool InsaneRebel1::notifyEvent(const Common::Event &event) {
 			return true;
 		}
 
+		if (isTouchscreenActive() && !_interactiveVideoActive && !_menuActive && pressed &&
+				(event.customType == kScummActionInsaneAttack ||
+				 event.customType == kScummActionInsaneSwitch ||
+				 event.customType == kScummActionInsaneSkip)) {
+			_vm->_smushVideoShouldFinish = true;
+			return true;
+		}
+
 		if (pressed && handleControllerMenuAction((ScummAction)event.customType))
 			return true;
 
@@ -778,10 +799,14 @@ bool InsaneRebel1::notifyEvent(const Common::Event &event) {
 	// Shooting: mouse button during interactive gameplay ??? FUN_1CCA0 (0x1CCA0)
 	if (_interactiveVideoActive && !_menuActive) {
 		if (event.type == Common::EVENT_LBUTTONDOWN) {
+			_vm->_mouse.x = event.mouse.x;
+			_vm->_mouse.y = event.mouse.y;
 			_playerFired = true;
 			return true;
 		}
 		if (event.type == Common::EVENT_LBUTTONUP) {
+			_vm->_mouse.x = event.mouse.x;
+			_vm->_mouse.y = event.mouse.y;
 			_playerFired = false;
 			return true;
 		}
@@ -795,6 +820,11 @@ bool InsaneRebel1::notifyEvent(const Common::Event &event) {
 	}
 
 	if (event.type == Common::EVENT_MAINMENU) {
+		if (_player && !_interactiveVideoActive && !_menuActive) {
+			_vm->_smushVideoShouldFinish = true;
+			return true;
+		}
+
 		if (_menuActive || _highScoresActive)
 			return true;
 
diff --git a/engines/scumm/insane/rebel1/rebel.h b/engines/scumm/insane/rebel1/rebel.h
index eab04823b21..6081ec18aae 100644
--- a/engines/scumm/insane/rebel1/rebel.h
+++ b/engines/scumm/insane/rebel1/rebel.h
@@ -105,9 +105,9 @@ public:
 
 	void handleGameChunk(int32 subSize, Common::SeekableReadStream &b);
 	bool isInteractiveVideoActive() const { return _interactiveVideoActive; }
-	// True on touchscreen devices (e.g. Android). RA1 then aims from the on-screen gamepad
-	// joystick instead of the DOS absolute-mouse model, and skips cursor warping/locking,
-	// which otherwise inject spurious motion that drifts the reticle and on-screen buttons.
+	// True on touchscreen devices (e.g. Android). RA1 skips DOS-style cursor
+	// warping/locking there; direct touch uses absolute aiming, while on-screen
+	// gamepad/joystick input continues through the joystick path.
 	bool isTouchscreenActive() const;
 	void setFrameHasGameChunk(bool hasGameChunk) { _frameHasGameChunk = hasGameChunk; }
 	int getCurrentLevel() const { return _currentLevel; }
diff --git a/engines/scumm/insane/rebel1/runlevels.cpp b/engines/scumm/insane/rebel1/runlevels.cpp
index b1e4303c7f4..1a61b7ff307 100644
--- a/engines/scumm/insane/rebel1/runlevels.cpp
+++ b/engines/scumm/insane/rebel1/runlevels.cpp
@@ -1672,9 +1672,9 @@ void InsaneRebel1::captureInteractiveVideoInput() {
 	// Level 7 route splices happen inside one original gameplay loop, so keep
 	// the current input state instead of recentering between route clips.
 	if (!level7RouteSplice) {
-		// On touchscreen devices the DOS recenter-the-cursor aiming model does not apply
-		// (aiming uses the on-screen gamepad); warping/locking the system mouse there only
-		// injects spurious motion that drifts the reticle and on-screen buttons.
+		// On touchscreen devices the DOS recenter-the-cursor aiming model does not apply;
+		// warping/locking the system mouse there injects spurious motion that drifts
+		// direct touch aiming and on-screen controls.
 		if (!isTouchscreenActive())
 			smush_warpMouse(160, 100, -1);
 		_mouseVirtualRawX = 0x140;
diff --git a/engines/scumm/metaengine.cpp b/engines/scumm/metaengine.cpp
index 7c7a5d0396d..7a059404df5 100644
--- a/engines/scumm/metaengine.cpp
+++ b/engines/scumm/metaengine.cpp
@@ -1179,6 +1179,7 @@ Common::KeymapArray ScummMetaEngine::initKeymaps(const char *target) const {
 
 		act = new Action("RA1FIRE", _("Fire / select"));
 		act->setCustomEngineActionEvent(kScummActionInsaneAttack);
+		act->addDefaultInputMapping("MOUSE_LEFT");
 		act->addDefaultInputMapping("JOY_A");
 		rebel1Keymap->addAction(act);
 



From noreply at scummvm.org  Tue Jun  2 16:30:10 2026
From: noreply at scummvm.org (ScummVM-Translations)
Date: Tue,  2 Jun 2026 16:30:10 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 8b824e9e581bb27f70aa01b925d5584bcf3e5b47
Message-ID: <20260602163010.88D407005C2@ron.scummvm.net>

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

Summary:
8b824e9e58 I18N: Update translations templates


Commit: 8b824e9e581bb27f70aa01b925d5584bcf3e5b47
    https://github.com/scummvm/scummvm/commit/8b824e9e581bb27f70aa01b925d5584bcf3e5b47
Author: Weblate (noreply at weblate.org)
Date: 2026-06-02T16:30:05Z

Commit Message:
I18N: Update translations templates

Changed paths:
    po/scummvm.pot


diff --git a/po/scummvm.pot b/po/scummvm.pot
index 536bfa2e247..39f58b09516 100644
--- a/po/scummvm.pot
+++ b/po/scummvm.pot
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2026.2.1git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 21:39+0000\n"
+"POT-Creation-Date: 2026-06-02 16:30+0000\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL at ADDRESS>\n"
 "Language-Team: LANGUAGE <LL at li.org>\n"
@@ -5435,7 +5435,7 @@ msgstr ""
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1195 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1196 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr ""
 
@@ -12423,63 +12423,63 @@ msgstr ""
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1208
+#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1209
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1213
+#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1214
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1218
+#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1219
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
+#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1224
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1228
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1229
 msgid "Stick up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1235
+#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
 msgid "Stick down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1242
+#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1243
 msgid "Stick left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1249
+#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1250
 msgid "Stick right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1256
+#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1257
 msgid "Fire / select"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1185
+#: engines/scumm/metaengine.cpp:1186
 msgid "Menu back"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1190
+#: engines/scumm/metaengine.cpp:1191
 msgid "Skip / menu back"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1206
+#: engines/scumm/metaengine.cpp:1207
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1261
+#: engines/scumm/metaengine.cpp:1262
 msgid "Cover / back"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1267
+#: engines/scumm/metaengine.cpp:1268
 msgid "Skip / back"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1274
+#: engines/scumm/metaengine.cpp:1275
 msgid "Skip / menu"
 msgstr ""
 



From noreply at scummvm.org  Tue Jun  2 16:30:41 2026
From: noreply at scummvm.org (ScummVM-Translations)
Date: Tue,  2 Jun 2026 16:30:41 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 b18af7a493926ad1418486e44824b8ef0d641e20
Message-ID: <20260602163041.6A790700573@ron.scummvm.net>

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

Summary:
b18af7a493 I18N: Update translation files


Commit: b18af7a493926ad1418486e44824b8ef0d641e20
    https://github.com/scummvm/scummvm/commit/b18af7a493926ad1418486e44824b8ef0d641e20
Author: Weblate (noreply at weblate.org)
Date: 2026-06-02T16:30:22Z

Commit Message:
I18N: Update translation files

Updated by "Update PO files to match POT (msgmerge)" hook in Weblate.

Translation: ScummVM/scummvm
Translate-URL: https://translations.scummvm.org/projects/scummvm/scummvm/

Changed paths:
    po/ar.po
    po/be-tarask.po
    po/be_BY.po
    po/ca_ES.po
    po/cs_CZ.po
    po/da.po
    po/de_DE.po
    po/el.po
    po/es_ES.po
    po/eu.po
    po/fi_FI.po
    po/fr_FR.po
    po/gl_ES.po
    po/he.po
    po/hi.po
    po/hu_HU.po
    po/it_IT.po
    po/ja.po
    po/ka.po
    po/ko.po
    po/nb_NO.po
    po/nl_NL.po
    po/nn_NO.po
    po/pl_PL.po
    po/pt_BR.po
    po/pt_PT.po
    po/ro.po
    po/ru_RU.po
    po/sv_SE.po
    po/tr.po
    po/uk_UA.po
    po/zh.po
    po/zh_Hans.po
    po/zh_Hant.po


diff --git a/po/ar.po b/po/ar.po
index 76c832ef410..7ea7d9f9c68 100644
--- a/po/ar.po
+++ b/po/ar.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 21:39+0000\n"
+"POT-Creation-Date: 2026-06-02 16:30+0000\n"
 "PO-Revision-Date: 2026-02-24 22:26+0000\n"
 "Last-Translator: Mohamed Shaaban <mohmedbashamedo at hotmail.com>\n"
 "Language-Team: Arabic <https://translations.scummvm.org/projects/scummvm/"
@@ -5935,7 +5935,7 @@ msgstr "R2"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1195 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1196 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "??????????????"
 
@@ -14212,81 +14212,81 @@ msgstr "?????? ???????? ???? ?????????? ??????????????"
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1208
+#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1209
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1213
+#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1214
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1218
+#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1219
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
+#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1224
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1228
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1229
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "??????????"
 
-#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1235
+#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "?????? ???????????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1242
+#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1243
 #, fuzzy
 #| msgid "Slide Left"
 msgid "Stick left"
 msgstr "?????????????? ?????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1249
+#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1250
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick right"
 msgstr "???????????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1256
+#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1257
 #, fuzzy
 #| msgid "Move left"
 msgid "Fire / select"
 msgstr "???????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1185
+#: engines/scumm/metaengine.cpp:1186
 #, fuzzy
 #| msgid "Move Back"
 msgid "Menu back"
 msgstr "???????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1190
+#: engines/scumm/metaengine.cpp:1191
 #, fuzzy
 #| msgid "Open main menu"
 msgid "Skip / menu back"
 msgstr "???????? ?????????????? ????????????????"
 
-#: engines/scumm/metaengine.cpp:1206
+#: engines/scumm/metaengine.cpp:1207
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1261
+#: engines/scumm/metaengine.cpp:1262
 #, fuzzy
 #| msgid "Move Back"
 msgid "Cover / back"
 msgstr "???????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1267
+#: engines/scumm/metaengine.cpp:1268
 #, fuzzy
 #| msgid "Skip dialogue"
 msgid "Skip / back"
 msgstr "???????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1274
+#: engines/scumm/metaengine.cpp:1275
 #, fuzzy
 #| msgid "Open main menu"
 msgid "Skip / menu"
diff --git a/po/be-tarask.po b/po/be-tarask.po
index fdcfa60956d..e9671fbec29 100644
--- a/po/be-tarask.po
+++ b/po/be-tarask.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 21:39+0000\n"
+"POT-Creation-Date: 2026-06-02 16:30+0000\n"
 "PO-Revision-Date: 2023-02-26 15:01+0000\n"
 "Last-Translator: Kastu?? K. <oakum2334 at gmail.com>\n"
 "Language-Team: Belarusian (Tara??kievica) <https://translations.scummvm.org/"
@@ -5868,7 +5868,7 @@ msgstr "R2"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1195 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1196 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "????????"
 
@@ -14139,81 +14139,81 @@ msgstr ""
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1208
+#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1209
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1213
+#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1214
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1218
+#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1219
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
+#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1224
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1228
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1229
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "??????????????"
 
-#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1235
+#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "?????????????????????????? ????????"
 
-#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1242
+#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1243
 #, fuzzy
 #| msgid "Slide Left"
 msgid "Stick left"
 msgstr "?????????????????????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1249
+#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1250
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick right"
 msgstr "?????????????????????? ??????????????"
 
-#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1256
+#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1257
 #, fuzzy
 #| msgid "Move left"
 msgid "Fire / select"
 msgstr "?????????????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1185
+#: engines/scumm/metaengine.cpp:1186
 #, fuzzy
 #| msgid "Move Back"
 msgid "Menu back"
 msgstr "?????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1190
+#: engines/scumm/metaengine.cpp:1191
 #, fuzzy
 #| msgid "Open main menu"
 msgid "Skip / menu back"
 msgstr "?????????????? ???????????????? ????????"
 
-#: engines/scumm/metaengine.cpp:1206
+#: engines/scumm/metaengine.cpp:1207
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1261
+#: engines/scumm/metaengine.cpp:1262
 #, fuzzy
 #| msgid "Move Back"
 msgid "Cover / back"
 msgstr "?????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1267
+#: engines/scumm/metaengine.cpp:1268
 #, fuzzy
 #| msgid "Skip dialogue"
 msgid "Skip / back"
 msgstr "?????????????????????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1274
+#: engines/scumm/metaengine.cpp:1275
 #, fuzzy
 #| msgid "Open main menu"
 msgid "Skip / menu"
diff --git a/po/be_BY.po b/po/be_BY.po
index 8384c99d89b..f90466c98c0 100644
--- a/po/be_BY.po
+++ b/po/be_BY.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 21:39+0000\n"
+"POT-Creation-Date: 2026-06-02 16:30+0000\n"
 "PO-Revision-Date: 2021-03-21 08:24+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Belarusian <https://translations.scummvm.org/projects/scummvm/"
@@ -5877,7 +5877,7 @@ msgstr "R2"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1195 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1196 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "????????"
 
@@ -13949,80 +13949,80 @@ msgstr ""
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1208
+#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1209
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1213
+#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1214
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1218
+#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1219
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
+#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1224
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1228
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1229
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "??????????????"
 
-#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1235
+#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "???????????????????? ????????"
 
-#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1242
+#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1243
 #, fuzzy
 #| msgid "Slide Left"
 msgid "Stick left"
 msgstr "???????????????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1249
+#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1250
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick right"
 msgstr "???????????????? ??????????????"
 
-#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1256
+#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1257
 #, fuzzy
 #| msgid "Move left"
 msgid "Fire / select"
 msgstr "?????????????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1185
+#: engines/scumm/metaengine.cpp:1186
 #, fuzzy
 #| msgid "Move Back"
 msgid "Menu back"
 msgstr "???????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1190
+#: engines/scumm/metaengine.cpp:1191
 #, fuzzy
 #| msgid "Open main menu"
 msgid "Skip / menu back"
 msgstr "?????????????? ???????????????? ????????"
 
-#: engines/scumm/metaengine.cpp:1206
+#: engines/scumm/metaengine.cpp:1207
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1261
+#: engines/scumm/metaengine.cpp:1262
 #, fuzzy
 #| msgid "Move Back"
 msgid "Cover / back"
 msgstr "???????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1267
+#: engines/scumm/metaengine.cpp:1268
 #, fuzzy
 msgid "Skip / back"
 msgstr "???????????????????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1274
+#: engines/scumm/metaengine.cpp:1275
 #, fuzzy
 #| msgid "Open main menu"
 msgid "Skip / menu"
diff --git a/po/ca_ES.po b/po/ca_ES.po
index 63c6170015a..47a90cc7dbd 100644
--- a/po/ca_ES.po
+++ b/po/ca_ES.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.6.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 21:39+0000\n"
+"POT-Creation-Date: 2026-06-02 16:30+0000\n"
 "PO-Revision-Date: 2026-05-26 04:22+0000\n"
 "Last-Translator: Xaviu <xaviu at xaviu.com>\n"
 "Language-Team: Catalan <https://translations.scummvm.org/projects/scummvm/"
@@ -5679,7 +5679,7 @@ msgstr "R2"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1195 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1196 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "Men??"
 
@@ -13016,81 +13016,81 @@ msgstr ""
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1208
+#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1209
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1213
+#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1214
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1218
+#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1219
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
+#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1224
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1228
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1229
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "Recull"
 
-#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1235
+#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "Despla??ament avall"
 
-#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1242
+#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1243
 #, fuzzy
 #| msgid "Slide left"
 msgid "Stick left"
 msgstr "Llisca cap a l'esquerra"
 
-#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1249
+#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1250
 #, fuzzy
 #| msgid "Slide right"
 msgid "Stick right"
 msgstr "Llisca cap a la dreta"
 
-#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1256
+#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1257
 #, fuzzy
 #| msgid "Move / Select"
 msgid "Fire / select"
 msgstr "Mou / Selecciona"
 
-#: engines/scumm/metaengine.cpp:1185
+#: engines/scumm/metaengine.cpp:1186
 #, fuzzy
 #| msgid "Move back"
 msgid "Menu back"
 msgstr "Mou enrere"
 
-#: engines/scumm/metaengine.cpp:1190
+#: engines/scumm/metaengine.cpp:1191
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu back"
 msgstr "Omet el men?? principal"
 
-#: engines/scumm/metaengine.cpp:1206
+#: engines/scumm/metaengine.cpp:1207
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1261
+#: engines/scumm/metaengine.cpp:1262
 #, fuzzy
 #| msgid "Move back"
 msgid "Cover / back"
 msgstr "Mou enrere"
 
-#: engines/scumm/metaengine.cpp:1267
+#: engines/scumm/metaengine.cpp:1268
 #, fuzzy
 #| msgid "Skip walk"
 msgid "Skip / back"
 msgstr "Omet el despla??ament"
 
-#: engines/scumm/metaengine.cpp:1274
+#: engines/scumm/metaengine.cpp:1275
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu"
diff --git a/po/cs_CZ.po b/po/cs_CZ.po
index 5d877108c06..5c8dc00d9d3 100644
--- a/po/cs_CZ.po
+++ b/po/cs_CZ.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.7.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 21:39+0000\n"
+"POT-Creation-Date: 2026-06-02 16:30+0000\n"
 "PO-Revision-Date: 2026-02-09 20:22+0000\n"
 "Last-Translator: AsciiWolf <mail at asciiwolf.com>\n"
 "Language-Team: Czech <https://translations.scummvm.org/projects/scummvm/"
@@ -5845,7 +5845,7 @@ msgstr ""
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1195 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1196 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "Menu"
 
@@ -13759,77 +13759,77 @@ msgstr ""
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1208
+#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1209
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1213
+#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1214
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1218
+#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1219
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
+#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1224
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1228
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1229
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "Sebrat"
 
-#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1235
+#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
 #, fuzzy
 msgid "Stick down"
 msgstr "Posunout seznam dolu"
 
-#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1242
+#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1243
 #, fuzzy
 #| msgid "Slide Left"
 msgid "Stick left"
 msgstr "P??esunout se Doleva"
 
-#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1249
+#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1250
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick right"
 msgstr "P??esunout se Doprava"
 
-#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1256
+#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1257
 #, fuzzy
 msgid "Fire / select"
 msgstr "P??esunout se doleva"
 
-#: engines/scumm/metaengine.cpp:1185
+#: engines/scumm/metaengine.cpp:1186
 #, fuzzy
 #| msgid "Move Back"
 msgid "Menu back"
 msgstr "Vzad"
 
-#: engines/scumm/metaengine.cpp:1190
+#: engines/scumm/metaengine.cpp:1191
 #, fuzzy
 msgid "Skip / menu back"
 msgstr "~N~??vrat do Spou??t????e"
 
-#: engines/scumm/metaengine.cpp:1206
+#: engines/scumm/metaengine.cpp:1207
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1261
+#: engines/scumm/metaengine.cpp:1262
 #, fuzzy
 #| msgid "Move Back"
 msgid "Cover / back"
 msgstr "Vzad"
 
-#: engines/scumm/metaengine.cpp:1267
+#: engines/scumm/metaengine.cpp:1268
 #, fuzzy
 msgid "Skip / back"
 msgstr "P??esko??it ????dek"
 
-#: engines/scumm/metaengine.cpp:1274
+#: engines/scumm/metaengine.cpp:1275
 #, fuzzy
 msgid "Skip / menu"
 msgstr "~N~??vrat do Spou??t????e"
diff --git a/po/da.po b/po/da.po
index 3b22733e0a9..fee6f201f6b 100644
--- a/po/da.po
+++ b/po/da.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 21:39+0000\n"
+"POT-Creation-Date: 2026-06-02 16:30+0000\n"
 "PO-Revision-Date: 2023-09-07 17:04+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Danish <https://translations.scummvm.org/projects/scummvm/"
@@ -5864,7 +5864,7 @@ msgstr ""
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1195 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1196 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "Menu"
 
@@ -13850,77 +13850,77 @@ msgstr ""
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1208
+#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1209
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1213
+#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1214
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1218
+#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1219
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
+#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1224
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1228
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1229
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "Tag op"
 
-#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1235
+#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
 #, fuzzy
 msgid "Stick down"
 msgstr "Rul liste ned"
 
-#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1242
+#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1243
 #, fuzzy
 #| msgid "Slide Left"
 msgid "Stick left"
 msgstr "Flyt til venstre"
 
-#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1249
+#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1250
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick right"
 msgstr "Flyt til h??jre"
 
-#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1256
+#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1257
 #, fuzzy
 msgid "Fire / select"
 msgstr "Flyt til venstre"
 
-#: engines/scumm/metaengine.cpp:1185
+#: engines/scumm/metaengine.cpp:1186
 #, fuzzy
 #| msgid "Move Back"
 msgid "Menu back"
 msgstr "Flyt bagud"
 
-#: engines/scumm/metaengine.cpp:1190
+#: engines/scumm/metaengine.cpp:1191
 #, fuzzy
 msgid "Skip / menu back"
 msgstr "~R~etur til oversigt"
 
-#: engines/scumm/metaengine.cpp:1206
+#: engines/scumm/metaengine.cpp:1207
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1261
+#: engines/scumm/metaengine.cpp:1262
 #, fuzzy
 #| msgid "Move Back"
 msgid "Cover / back"
 msgstr "Flyt bagud"
 
-#: engines/scumm/metaengine.cpp:1267
+#: engines/scumm/metaengine.cpp:1268
 #, fuzzy
 msgid "Skip / back"
 msgstr "Spring linje over"
 
-#: engines/scumm/metaengine.cpp:1274
+#: engines/scumm/metaengine.cpp:1275
 #, fuzzy
 msgid "Skip / menu"
 msgstr "~R~etur til oversigt"
diff --git a/po/de_DE.po b/po/de_DE.po
index 6320dfbaea2..bbc728776d1 100644
--- a/po/de_DE.po
+++ b/po/de_DE.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.10.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 21:39+0000\n"
+"POT-Creation-Date: 2026-06-02 16:30+0000\n"
 "PO-Revision-Date: 2026-05-05 16:31+0000\n"
 "Last-Translator: Lothar Serra Mari <lothar.serramari at scummvm.org>\n"
 "Language-Team: German <https://translations.scummvm.org/projects/scummvm/"
@@ -6144,7 +6144,7 @@ msgstr "R2"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1195 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1196 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "Men??"
 
@@ -13747,81 +13747,81 @@ msgstr "Cheat: Bike-Kampf gewinnen"
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1208
+#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1209
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1213
+#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1214
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1218
+#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1219
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
+#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1224
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1228
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1229
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "Nimm"
 
-#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1235
+#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "Nach unten scrollen"
 
-#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1242
+#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1243
 #, fuzzy
 #| msgid "Slide left"
 msgid "Stick left"
 msgstr "Slide nach links"
 
-#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1249
+#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1250
 #, fuzzy
 #| msgid "Slide right"
 msgid "Stick right"
 msgstr "Slide nach rechts"
 
-#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1256
+#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1257
 #, fuzzy
 #| msgid "Move / Select"
 msgid "Fire / select"
 msgstr "Bewegen / Ausw??hlen"
 
-#: engines/scumm/metaengine.cpp:1185
+#: engines/scumm/metaengine.cpp:1186
 #, fuzzy
 #| msgid "Move back"
 msgid "Menu back"
 msgstr "Zur??ckgehen"
 
-#: engines/scumm/metaengine.cpp:1190
+#: engines/scumm/metaengine.cpp:1191
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu back"
 msgstr "Hauptmen?? ??berspringen"
 
-#: engines/scumm/metaengine.cpp:1206
+#: engines/scumm/metaengine.cpp:1207
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1261
+#: engines/scumm/metaengine.cpp:1262
 #, fuzzy
 #| msgid "Move back"
 msgid "Cover / back"
 msgstr "Zur??ckgehen"
 
-#: engines/scumm/metaengine.cpp:1267
+#: engines/scumm/metaengine.cpp:1268
 #, fuzzy
 #| msgid "Skip walk"
 msgid "Skip / back"
 msgstr "Spaziergang ??berspringen"
 
-#: engines/scumm/metaengine.cpp:1274
+#: engines/scumm/metaengine.cpp:1275
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu"
diff --git a/po/el.po b/po/el.po
index 2af8184d969..ea1fa3af49b 100644
--- a/po/el.po
+++ b/po/el.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.10.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 21:39+0000\n"
+"POT-Creation-Date: 2026-06-02 16:30+0000\n"
 "PO-Revision-Date: 2026-04-23 00:22+0000\n"
 "Last-Translator: Antoniou Athanasios <a.antoniou79 at gmail.com>\n"
 "Language-Team: Greek <https://translations.scummvm.org/projects/scummvm/"
@@ -6268,7 +6268,7 @@ msgstr "R2"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1195 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1196 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "??????????"
 
@@ -13902,81 +13902,81 @@ msgstr "?????????? ?????? ???????? ?????? ???????? ???? ?????? ??????????????"
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1208
+#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1209
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1213
+#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1214
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1218
+#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1219
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
+#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1224
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1228
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1229
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "????????????"
 
-#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1235
+#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "???????????? ????????"
 
-#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1242
+#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1243
 #, fuzzy
 #| msgid "Slide left"
 msgid "Stick left"
 msgstr "???????????????? ????????????????"
 
-#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1249
+#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1250
 #, fuzzy
 #| msgid "Slide right"
 msgid "Stick right"
 msgstr "???????????????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1256
+#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1257
 #, fuzzy
 #| msgid "Move / Select"
 msgid "Fire / select"
 msgstr "???????????? / ??????????????"
 
-#: engines/scumm/metaengine.cpp:1185
+#: engines/scumm/metaengine.cpp:1186
 #, fuzzy
 #| msgid "Move Back"
 msgid "Menu back"
 msgstr "?????????????????? ????????"
 
-#: engines/scumm/metaengine.cpp:1190
+#: engines/scumm/metaengine.cpp:1191
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu back"
 msgstr "?????????????????? ?????????????????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1206
+#: engines/scumm/metaengine.cpp:1207
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1261
+#: engines/scumm/metaengine.cpp:1262
 #, fuzzy
 #| msgid "Move Back"
 msgid "Cover / back"
 msgstr "?????????????????? ????????"
 
-#: engines/scumm/metaengine.cpp:1267
+#: engines/scumm/metaengine.cpp:1268
 #, fuzzy
 #| msgid "Skip walk"
 msgid "Skip / back"
 msgstr "?????????????????? ??????????????????"
 
-#: engines/scumm/metaengine.cpp:1274
+#: engines/scumm/metaengine.cpp:1275
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu"
diff --git a/po/es_ES.po b/po/es_ES.po
index 660466ddddc..86af8371604 100644
--- a/po/es_ES.po
+++ b/po/es_ES.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.4.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 21:39+0000\n"
+"POT-Creation-Date: 2026-06-02 16:30+0000\n"
 "PO-Revision-Date: 2026-06-01 09:35+0000\n"
 "Last-Translator: IlDucci <pajaroloco_2 at hotmail.com>\n"
 "Language-Team: Spanish <https://translations.scummvm.org/projects/scummvm/"
@@ -6166,7 +6166,7 @@ msgstr "R2"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1195 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1196 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "Men??"
 
@@ -13720,69 +13720,69 @@ msgstr "Truco para ganar pelea de motos"
 msgid "Rebel Assault controls"
 msgstr "Controles de Rebel Assault"
 
-#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1208
+#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1209
 msgid "Aim up / menu up"
 msgstr "Apuntar arriba/Subir men??"
 
-#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1213
+#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1214
 msgid "Aim down / menu down"
 msgstr "Apuntar abajo/Bajar men??"
 
-#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1218
+#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1219
 msgid "Aim left / menu left"
 msgstr "Apuntar a izda./Men?? a izda."
 
-#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
+#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1224
 msgid "Aim right / menu right"
 msgstr "Apuntar a dcha./Men?? a dcha."
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1228
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1229
 msgid "Stick up"
 msgstr "Stick hacia arriba"
 
-#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1235
+#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
 msgid "Stick down"
 msgstr "Stick hacia abajo"
 
-#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1242
+#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1243
 msgid "Stick left"
 msgstr "Stick hacia la izquierda"
 
-#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1249
+#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1250
 msgid "Stick right"
 msgstr "Stick hacia la derecha"
 
-#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1256
+#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1257
 msgid "Fire / select"
 msgstr "Disparar/Seleccionar"
 
-#: engines/scumm/metaengine.cpp:1185
+#: engines/scumm/metaengine.cpp:1186
 #, fuzzy
 #| msgid "Move back"
 msgid "Menu back"
 msgstr "Retroceder"
 
-#: engines/scumm/metaengine.cpp:1190
+#: engines/scumm/metaengine.cpp:1191
 #, fuzzy
 #| msgid "Skip / menu"
 msgid "Skip / menu back"
 msgstr "Omitir/Men??"
 
-#: engines/scumm/metaengine.cpp:1206
+#: engines/scumm/metaengine.cpp:1207
 msgid "Rebel Assault II controls"
 msgstr "Controles de Rebel Assault II"
 
-#: engines/scumm/metaengine.cpp:1261
+#: engines/scumm/metaengine.cpp:1262
 msgid "Cover / back"
 msgstr "Cubrirse/Volver"
 
-#: engines/scumm/metaengine.cpp:1267
+#: engines/scumm/metaengine.cpp:1268
 #, fuzzy
 #| msgid "Skip walk"
 msgid "Skip / back"
 msgstr "Omitir desplazamiento"
 
-#: engines/scumm/metaengine.cpp:1274
+#: engines/scumm/metaengine.cpp:1275
 msgid "Skip / menu"
 msgstr "Omitir/Men??"
 
diff --git a/po/eu.po b/po/eu.po
index cd5f3e5b6ba..785467323e9 100644
--- a/po/eu.po
+++ b/po/eu.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.5.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 21:39+0000\n"
+"POT-Creation-Date: 2026-06-02 16:30+0000\n"
 "PO-Revision-Date: 2024-02-05 18:42+0000\n"
 "Last-Translator: Josu Igoa <josuigoa at ni.eus>\n"
 "Language-Team: Basque <https://translations.scummvm.org/projects/scummvm/"
@@ -5919,7 +5919,7 @@ msgstr ""
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1195 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1196 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "Menua"
 
@@ -13817,77 +13817,77 @@ msgstr ""
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1208
+#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1209
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1213
+#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1214
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1218
+#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1219
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
+#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1224
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1228
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1229
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "Jaso"
 
-#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1235
+#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
 #, fuzzy
 msgid "Stick down"
 msgstr "Behera"
 
-#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1242
+#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1243
 #, fuzzy
 #| msgid "Slide Left"
 msgid "Stick left"
 msgstr "Ezkerrera irristatu"
 
-#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1249
+#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1250
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick right"
 msgstr "Eskuinera irristatu"
 
-#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1256
+#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1257
 #, fuzzy
 msgid "Fire / select"
 msgstr "Ezkerrera irristatu"
 
-#: engines/scumm/metaengine.cpp:1185
+#: engines/scumm/metaengine.cpp:1186
 #, fuzzy
 #| msgid "Move Back"
 msgid "Menu back"
 msgstr "Atzera mugitu"
 
-#: engines/scumm/metaengine.cpp:1190
+#: engines/scumm/metaengine.cpp:1191
 #, fuzzy
 msgid "Skip / menu back"
 msgstr "It~z~uli abiarazlera"
 
-#: engines/scumm/metaengine.cpp:1206
+#: engines/scumm/metaengine.cpp:1207
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1261
+#: engines/scumm/metaengine.cpp:1262
 #, fuzzy
 #| msgid "Move Back"
 msgid "Cover / back"
 msgstr "Atzera mugitu"
 
-#: engines/scumm/metaengine.cpp:1267
+#: engines/scumm/metaengine.cpp:1268
 #, fuzzy
 msgid "Skip / back"
 msgstr "Lerroa saltatu"
 
-#: engines/scumm/metaengine.cpp:1274
+#: engines/scumm/metaengine.cpp:1275
 #, fuzzy
 msgid "Skip / menu"
 msgstr "It~z~uli abiarazlera"
diff --git a/po/fi_FI.po b/po/fi_FI.po
index 0c1e6bb9085..2981df062e2 100644
--- a/po/fi_FI.po
+++ b/po/fi_FI.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.6.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 21:39+0000\n"
+"POT-Creation-Date: 2026-06-02 16:30+0000\n"
 "PO-Revision-Date: 2026-02-07 15:22+0000\n"
 "Last-Translator: Timo Mikkolainen <tmikkola at gmail.com>\n"
 "Language-Team: Finnish <https://translations.scummvm.org/projects/scummvm/"
@@ -6263,7 +6263,7 @@ msgstr "R2"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1195 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1196 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "Valikko"
 
@@ -13947,81 +13947,81 @@ msgstr "Voita moottoripy??r??taistelu-huijaus"
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1208
+#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1209
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1213
+#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1214
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1218
+#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1219
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
+#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1224
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1228
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1229
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "Ota"
 
-#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1235
+#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "Vierit?? alas"
 
-#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1242
+#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1243
 #, fuzzy
 #| msgid "Slide left"
 msgid "Stick left"
 msgstr "Liu'u vasemmalle"
 
-#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1249
+#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1250
 #, fuzzy
 #| msgid "Slide right"
 msgid "Stick right"
 msgstr "Liu'u oikealle"
 
-#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1256
+#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1257
 #, fuzzy
 #| msgid "Move / Select"
 msgid "Fire / select"
 msgstr "Liiku / Valitse"
 
-#: engines/scumm/metaengine.cpp:1185
+#: engines/scumm/metaengine.cpp:1186
 #, fuzzy
 #| msgid "Move back"
 msgid "Menu back"
 msgstr "Liiku taaksep??in"
 
-#: engines/scumm/metaengine.cpp:1190
+#: engines/scumm/metaengine.cpp:1191
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu back"
 msgstr "Ohita p????valikko"
 
-#: engines/scumm/metaengine.cpp:1206
+#: engines/scumm/metaengine.cpp:1207
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1261
+#: engines/scumm/metaengine.cpp:1262
 #, fuzzy
 #| msgid "Move back"
 msgid "Cover / back"
 msgstr "Liiku taaksep??in"
 
-#: engines/scumm/metaengine.cpp:1267
+#: engines/scumm/metaengine.cpp:1268
 #, fuzzy
 #| msgid "Skip walk"
 msgid "Skip / back"
 msgstr "Ohita k??vely"
 
-#: engines/scumm/metaengine.cpp:1274
+#: engines/scumm/metaengine.cpp:1275
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu"
diff --git a/po/fr_FR.po b/po/fr_FR.po
index df67f0d6b32..61f91eb431e 100644
--- a/po/fr_FR.po
+++ b/po/fr_FR.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 21:39+0000\n"
+"POT-Creation-Date: 2026-06-02 16:30+0000\n"
 "PO-Revision-Date: 2026-06-02 11:22+0000\n"
 "Last-Translator: Purple T <ZEONK at hotmail.com>\n"
 "Language-Team: French <https://translations.scummvm.org/projects/scummvm/"
@@ -6181,7 +6181,7 @@ msgstr "R2"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1195 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1196 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "Menu"
 
@@ -13745,63 +13745,63 @@ msgstr "Gagner les bagarres en b??cane (cheat)"
 msgid "Rebel Assault controls"
 msgstr "Contr??les de Rebel Assault"
 
-#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1208
+#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1209
 msgid "Aim up / menu up"
 msgstr "Vis??e haut / Menu haut"
 
-#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1213
+#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1214
 msgid "Aim down / menu down"
 msgstr "Vis??e bas / Menu bas"
 
-#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1218
+#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1219
 msgid "Aim left / menu left"
 msgstr "Vis??e gauche / menu gauche"
 
-#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
+#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1224
 msgid "Aim right / menu right"
 msgstr "Vis??e droite / Menu droite"
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1228
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1229
 msgid "Stick up"
 msgstr "Manche haut"
 
-#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1235
+#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
 msgid "Stick down"
 msgstr "Manche bas"
 
-#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1242
+#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1243
 msgid "Stick left"
 msgstr "Manche gauche"
 
-#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1249
+#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1250
 msgid "Stick right"
 msgstr "Manche droite"
 
-#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1256
+#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1257
 msgid "Fire / select"
 msgstr "Tirer / S??lectionner"
 
-#: engines/scumm/metaengine.cpp:1185
+#: engines/scumm/metaengine.cpp:1186
 msgid "Menu back"
 msgstr "Menu retour"
 
-#: engines/scumm/metaengine.cpp:1190
+#: engines/scumm/metaengine.cpp:1191
 msgid "Skip / menu back"
 msgstr "Passer / menu retour"
 
-#: engines/scumm/metaengine.cpp:1206
+#: engines/scumm/metaengine.cpp:1207
 msgid "Rebel Assault II controls"
 msgstr "Contr??les de Rebel Assault II"
 
-#: engines/scumm/metaengine.cpp:1261
+#: engines/scumm/metaengine.cpp:1262
 msgid "Cover / back"
 msgstr "Couverture / retour"
 
-#: engines/scumm/metaengine.cpp:1267
+#: engines/scumm/metaengine.cpp:1268
 msgid "Skip / back"
 msgstr "Passer / retour"
 
-#: engines/scumm/metaengine.cpp:1274
+#: engines/scumm/metaengine.cpp:1275
 msgid "Skip / menu"
 msgstr "Passer / menu"
 
diff --git a/po/gl_ES.po b/po/gl_ES.po
index 1affcaa145d..43cd0f685d7 100644
--- a/po/gl_ES.po
+++ b/po/gl_ES.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 21:39+0000\n"
+"POT-Creation-Date: 2026-06-02 16:30+0000\n"
 "PO-Revision-Date: 2020-09-26 21:13+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Galician <https://translations.scummvm.org/projects/scummvm/"
@@ -5853,7 +5853,7 @@ msgstr ""
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1195 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1196 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "Men??"
 
@@ -13773,77 +13773,77 @@ msgstr ""
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1208
+#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1209
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1213
+#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1214
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1218
+#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1219
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
+#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1224
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1228
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1229
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "Coller"
 
-#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1235
+#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
 #, fuzzy
 msgid "Stick down"
 msgstr "Baixar lista"
 
-#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1242
+#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1243
 #, fuzzy
 #| msgid "Slide Left"
 msgid "Stick left"
 msgstr "Esvarar ?? esquerda"
 
-#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1249
+#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1250
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick right"
 msgstr "Esvarar ?? dereita"
 
-#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1256
+#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1257
 #, fuzzy
 msgid "Fire / select"
 msgstr "Mover ?? esquerda"
 
-#: engines/scumm/metaengine.cpp:1185
+#: engines/scumm/metaengine.cpp:1186
 #, fuzzy
 #| msgid "Move Back"
 msgid "Menu back"
 msgstr "Mover cara atr??s"
 
-#: engines/scumm/metaengine.cpp:1190
+#: engines/scumm/metaengine.cpp:1191
 #, fuzzy
 msgid "Skip / menu back"
 msgstr "~V~olver ao Iniciador"
 
-#: engines/scumm/metaengine.cpp:1206
+#: engines/scumm/metaengine.cpp:1207
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1261
+#: engines/scumm/metaengine.cpp:1262
 #, fuzzy
 #| msgid "Move Back"
 msgid "Cover / back"
 msgstr "Mover cara atr??s"
 
-#: engines/scumm/metaengine.cpp:1267
+#: engines/scumm/metaengine.cpp:1268
 #, fuzzy
 msgid "Skip / back"
 msgstr "Omitir li??a"
 
-#: engines/scumm/metaengine.cpp:1274
+#: engines/scumm/metaengine.cpp:1275
 #, fuzzy
 msgid "Skip / menu"
 msgstr "~V~olver ao Iniciador"
diff --git a/po/he.po b/po/he.po
index d8d215cbfc5..69a1cbfdea2 100644
--- a/po/he.po
+++ b/po/he.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.1.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 21:39+0000\n"
+"POT-Creation-Date: 2026-06-02 16:30+0000\n"
 "PO-Revision-Date: 2025-04-09 20:18+0000\n"
 "Last-Translator: Niv Baehr <bloop93 at gmail.com>\n"
 "Language-Team: Hebrew <https://translations.scummvm.org/projects/scummvm/"
@@ -5811,7 +5811,7 @@ msgstr "R2"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1195 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1196 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "??????????"
 
@@ -13994,81 +13994,81 @@ msgstr "?????????? ?????????????? ???????? ????????????????"
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1208
+#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1209
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1213
+#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1214
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1218
+#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1219
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
+#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1224
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1228
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1229
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "??????????"
 
-#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1235
+#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "?????????? ??????"
 
-#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1242
+#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1243
 #, fuzzy
 #| msgid "Slide Left"
 msgid "Stick left"
 msgstr "???????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1249
+#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1250
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick right"
 msgstr "???????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1256
+#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1257
 #, fuzzy
 #| msgid "Move left"
 msgid "Fire / select"
 msgstr "?????????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1185
+#: engines/scumm/metaengine.cpp:1186
 #, fuzzy
 #| msgid "Move Back"
 msgid "Menu back"
 msgstr "??????????"
 
-#: engines/scumm/metaengine.cpp:1190
+#: engines/scumm/metaengine.cpp:1191
 #, fuzzy
 #| msgid "Open main menu"
 msgid "Skip / menu back"
 msgstr "?????????? ?????????? ????????"
 
-#: engines/scumm/metaengine.cpp:1206
+#: engines/scumm/metaengine.cpp:1207
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1261
+#: engines/scumm/metaengine.cpp:1262
 #, fuzzy
 #| msgid "Move Back"
 msgid "Cover / back"
 msgstr "??????????"
 
-#: engines/scumm/metaengine.cpp:1267
+#: engines/scumm/metaengine.cpp:1268
 #, fuzzy
 #| msgid "Skip dialogue"
 msgid "Skip / back"
 msgstr "?????????? ???? ????????"
 
-#: engines/scumm/metaengine.cpp:1274
+#: engines/scumm/metaengine.cpp:1275
 #, fuzzy
 #| msgid "Open main menu"
 msgid "Skip / menu"
diff --git a/po/hi.po b/po/hi.po
index d084ba77f95..f67615fec24 100644
--- a/po/hi.po
+++ b/po/hi.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 21:39+0000\n"
+"POT-Creation-Date: 2026-06-02 16:30+0000\n"
 "PO-Revision-Date: 2026-05-12 22:22+0000\n"
 "Last-Translator: KARTIK SAXENA <kartiksaxena2005 at gmail.com>\n"
 "Language-Team: Hindi <https://translations.scummvm.org/projects/scummvm/"
@@ -5638,7 +5638,7 @@ msgstr ""
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1195 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1196 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr ""
 
@@ -13252,78 +13252,78 @@ msgstr ""
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1208
+#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1209
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1213
+#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1214
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1218
+#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1219
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
+#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1224
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1228
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1229
 msgid "Stick up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1235
+#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
 #, fuzzy
 msgid "Stick down"
 msgstr "???????????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1242
+#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1243
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick left"
 msgstr "?????????????????? ?????????????????? ?????????"
 
-#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1249
+#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1250
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick right"
 msgstr "?????????????????? ?????????????????? ?????????"
 
-#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1256
+#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1257
 #, fuzzy
 #| msgid "Move Left"
 msgid "Fire / select"
 msgstr "???????????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1185
+#: engines/scumm/metaengine.cpp:1186
 #, fuzzy
 #| msgid "Move Forward"
 msgid "Menu back"
 msgstr "????????? ?????????"
 
-#: engines/scumm/metaengine.cpp:1190
+#: engines/scumm/metaengine.cpp:1191
 #, fuzzy
 #| msgid "Skip autosave"
 msgid "Skip / menu back"
 msgstr "?????????????????? ??????????????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1206
+#: engines/scumm/metaengine.cpp:1207
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1261
+#: engines/scumm/metaengine.cpp:1262
 #, fuzzy
 #| msgid "Move Forward"
 msgid "Cover / back"
 msgstr "????????? ?????????"
 
-#: engines/scumm/metaengine.cpp:1267
+#: engines/scumm/metaengine.cpp:1268
 #, fuzzy
 #| msgid "Show hidden files"
 msgid "Skip / back"
 msgstr "???????????? ????????? ?????????????????? ?????????????????????"
 
-#: engines/scumm/metaengine.cpp:1274
+#: engines/scumm/metaengine.cpp:1275
 #, fuzzy
 #| msgid "Skip autosave"
 msgid "Skip / menu"
diff --git a/po/hu_HU.po b/po/hu_HU.po
index c4b82145e5e..8079cad5db9 100644
--- a/po/hu_HU.po
+++ b/po/hu_HU.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 21:39+0000\n"
+"POT-Creation-Date: 2026-06-02 16:30+0000\n"
 "PO-Revision-Date: 2025-03-07 00:18+0000\n"
 "Last-Translator: Hov??nszki Tam??s <gahex68379 at lofiey.com>\n"
 "Language-Team: Hungarian <https://translations.scummvm.org/projects/scummvm/"
@@ -6351,7 +6351,7 @@ msgstr "R2"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1195 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1196 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "Men??"
 
@@ -14454,81 +14454,81 @@ msgstr "Csal??s a motoros harc megnyer??s??hez"
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1208
+#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1209
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1213
+#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1214
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1218
+#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1219
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
+#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1224
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1228
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1229
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "Felvesz"
 
-#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1235
+#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "G??rget??s LE"
 
-#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1242
+#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1243
 #, fuzzy
 #| msgid "Slide Left"
 msgid "Stick left"
 msgstr "Cs??sz??s balra"
 
-#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1249
+#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1250
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick right"
 msgstr "Cs??sz??s jobbra"
 
-#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1256
+#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1257
 #, fuzzy
 #| msgid "Move left"
 msgid "Fire / select"
 msgstr "Mozg??s balra"
 
-#: engines/scumm/metaengine.cpp:1185
+#: engines/scumm/metaengine.cpp:1186
 #, fuzzy
 #| msgid "Move Back"
 msgid "Menu back"
 msgstr "Mozg??s h??tra"
 
-#: engines/scumm/metaengine.cpp:1190
+#: engines/scumm/metaengine.cpp:1191
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu back"
 msgstr "F?? men?? ??tugr??sa"
 
-#: engines/scumm/metaengine.cpp:1206
+#: engines/scumm/metaengine.cpp:1207
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1261
+#: engines/scumm/metaengine.cpp:1262
 #, fuzzy
 #| msgid "Move Back"
 msgid "Cover / back"
 msgstr "Mozg??s h??tra"
 
-#: engines/scumm/metaengine.cpp:1267
+#: engines/scumm/metaengine.cpp:1268
 #, fuzzy
 #| msgid "Skip dialogue"
 msgid "Skip / back"
 msgstr "Dial??gus ??tugr??sa"
 
-#: engines/scumm/metaengine.cpp:1274
+#: engines/scumm/metaengine.cpp:1275
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu"
diff --git a/po/it_IT.po b/po/it_IT.po
index 0c23f55eb1b..89450d90eab 100644
--- a/po/it_IT.po
+++ b/po/it_IT.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 21:39+0000\n"
+"POT-Creation-Date: 2026-06-02 16:30+0000\n"
 "PO-Revision-Date: 2026-05-25 15:48+0000\n"
 "Last-Translator: Walter Agazzi <walter.agazzi at protonmail.com>\n"
 "Language-Team: Italian <https://translations.scummvm.org/projects/scummvm/"
@@ -6148,7 +6148,7 @@ msgstr "R2"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1195 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1196 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "Menu"
 
@@ -13695,81 +13695,81 @@ msgstr "Cheat: Vinci combattimento in moto"
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1208
+#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1209
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1213
+#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1214
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1218
+#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1219
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
+#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1224
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1228
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1229
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "Raccogli"
 
-#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1235
+#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "Scorri verso il basso"
 
-#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1242
+#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1243
 #, fuzzy
 #| msgid "Slide left"
 msgid "Stick left"
 msgstr "Scorri a sinistra"
 
-#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1249
+#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1250
 #, fuzzy
 #| msgid "Slide right"
 msgid "Stick right"
 msgstr "Scorri a destra"
 
-#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1256
+#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1257
 #, fuzzy
 #| msgid "Move / Select"
 msgid "Fire / select"
 msgstr "Spostamento / Selezione"
 
-#: engines/scumm/metaengine.cpp:1185
+#: engines/scumm/metaengine.cpp:1186
 #, fuzzy
 #| msgid "Move back"
 msgid "Menu back"
 msgstr "Arretra"
 
-#: engines/scumm/metaengine.cpp:1190
+#: engines/scumm/metaengine.cpp:1191
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu back"
 msgstr "Salta men?? principale"
 
-#: engines/scumm/metaengine.cpp:1206
+#: engines/scumm/metaengine.cpp:1207
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1261
+#: engines/scumm/metaengine.cpp:1262
 #, fuzzy
 #| msgid "Move back"
 msgid "Cover / back"
 msgstr "Arretra"
 
-#: engines/scumm/metaengine.cpp:1267
+#: engines/scumm/metaengine.cpp:1268
 #, fuzzy
 #| msgid "Skip walk"
 msgid "Skip / back"
 msgstr "Salta spostamento"
 
-#: engines/scumm/metaengine.cpp:1274
+#: engines/scumm/metaengine.cpp:1275
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu"
diff --git a/po/ja.po b/po/ja.po
index f99219eaa3c..1c3f46c85a7 100644
--- a/po/ja.po
+++ b/po/ja.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 21:39+0000\n"
+"POT-Creation-Date: 2026-06-02 16:30+0000\n"
 "PO-Revision-Date: 2026-04-26 07:22+0000\n"
 "Last-Translator: VAN-Gluon <van.gluonp at gmail.com>\n"
 "Language-Team: Japanese <https://translations.scummvm.org/projects/scummvm/"
@@ -6002,7 +6002,7 @@ msgstr "R2"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1195 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1196 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "????????????"
 
@@ -14098,81 +14098,81 @@ msgstr "????????????????????????"
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1208
+#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1209
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1213
+#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1214
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1218
+#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1219
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
+#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1224
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1228
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1229
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "??????"
 
-#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1235
+#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "?????????????????????"
 
-#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1242
+#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1243
 #, fuzzy
 #| msgid "Slide Left"
 msgid "Stick left"
 msgstr "??????????????????"
 
-#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1249
+#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1250
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick right"
 msgstr "??????????????????"
 
-#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1256
+#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1257
 #, fuzzy
 #| msgid "Move left"
 msgid "Fire / select"
 msgstr "????????????"
 
-#: engines/scumm/metaengine.cpp:1185
+#: engines/scumm/metaengine.cpp:1186
 #, fuzzy
 #| msgid "Move Back"
 msgid "Menu back"
 msgstr "??????"
 
-#: engines/scumm/metaengine.cpp:1190
+#: engines/scumm/metaengine.cpp:1191
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu back"
 msgstr "????????????????????????????????????"
 
-#: engines/scumm/metaengine.cpp:1206
+#: engines/scumm/metaengine.cpp:1207
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1261
+#: engines/scumm/metaengine.cpp:1262
 #, fuzzy
 #| msgid "Move Back"
 msgid "Cover / back"
 msgstr "??????"
 
-#: engines/scumm/metaengine.cpp:1267
+#: engines/scumm/metaengine.cpp:1268
 #, fuzzy
 #| msgid "Skip dialogue"
 msgid "Skip / back"
 msgstr "??????????????????????????????"
 
-#: engines/scumm/metaengine.cpp:1274
+#: engines/scumm/metaengine.cpp:1275
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu"
diff --git a/po/ka.po b/po/ka.po
index 5a8100cbbdd..7cc01fb514f 100644
--- a/po/ka.po
+++ b/po/ka.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 21:39+0000\n"
+"POT-Creation-Date: 2026-06-02 16:30+0000\n"
 "PO-Revision-Date: 2026-05-09 12:22+0000\n"
 "Last-Translator: NorwayFun <temuri.doghonadze at gmail.com>\n"
 "Language-Team: Georgian <https://translations.scummvm.org/projects/scummvm/"
@@ -5463,7 +5463,7 @@ msgstr "R2"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1195 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1196 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "???????????????"
 
@@ -12463,81 +12463,81 @@ msgstr ""
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1208
+#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1209
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1213
+#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1214
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1218
+#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1219
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
+#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1224
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1228
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1229
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "???????????????"
 
-#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1235
+#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "??????????????? ????????????????????????"
 
-#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1242
+#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1243
 #, fuzzy
 #| msgid "Slide left"
 msgid "Stick left"
 msgstr "?????????????????????????????? ????????????????????????"
 
-#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1249
+#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1250
 #, fuzzy
 #| msgid "Slide right"
 msgid "Stick right"
 msgstr "?????????????????????????????? ????????????????????????"
 
-#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1256
+#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1257
 #, fuzzy
 #| msgid "Move / Select"
 msgid "Fire / select"
 msgstr "???????????????????????????????????? / ????????????????????????"
 
-#: engines/scumm/metaengine.cpp:1185
+#: engines/scumm/metaengine.cpp:1186
 #, fuzzy
 #| msgid "Move back"
 msgid "Menu back"
 msgstr "???????????? ????????????????????????"
 
-#: engines/scumm/metaengine.cpp:1190
+#: engines/scumm/metaengine.cpp:1191
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu back"
 msgstr "????????????????????? ?????????????????? ??????????????????????????????"
 
-#: engines/scumm/metaengine.cpp:1206
+#: engines/scumm/metaengine.cpp:1207
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1261
+#: engines/scumm/metaengine.cpp:1262
 #, fuzzy
 #| msgid "Move back"
 msgid "Cover / back"
 msgstr "???????????? ????????????????????????"
 
-#: engines/scumm/metaengine.cpp:1267
+#: engines/scumm/metaengine.cpp:1268
 #, fuzzy
 #| msgid "Skip walk"
 msgid "Skip / back"
 msgstr "???????????????????????? ??????????????????????????????"
 
-#: engines/scumm/metaengine.cpp:1274
+#: engines/scumm/metaengine.cpp:1275
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu"
diff --git a/po/ko.po b/po/ko.po
index 45cc119277b..da31e3c4d63 100644
--- a/po/ko.po
+++ b/po/ko.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 21:39+0000\n"
+"POT-Creation-Date: 2026-06-02 16:30+0000\n"
 "PO-Revision-Date: 2026-05-19 20:22+0000\n"
 "Last-Translator: Hoseok Seo <ddinghoya at gmail.com>\n"
 "Language-Team: Korean <https://translations.scummvm.org/projects/scummvm/"
@@ -6013,7 +6013,7 @@ msgstr "R2 ??????"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1195 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1196 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "??????"
 
@@ -13375,81 +13375,81 @@ msgstr "????????? ???????????? ???????????? ??????"
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1208
+#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1209
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1213
+#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1214
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1218
+#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1219
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
+#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1224
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1228
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1229
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "??????"
 
-#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1235
+#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "????????? ?????????"
 
-#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1242
+#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1243
 #, fuzzy
 #| msgid "Slide left"
 msgid "Stick left"
 msgstr "???????????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1249
+#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1250
 #, fuzzy
 #| msgid "Slide right"
 msgid "Stick right"
 msgstr "????????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1256
+#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1257
 #, fuzzy
 #| msgid "Move / Select"
 msgid "Fire / select"
 msgstr "?????? / ??????"
 
-#: engines/scumm/metaengine.cpp:1185
+#: engines/scumm/metaengine.cpp:1186
 #, fuzzy
 #| msgid "Move back"
 msgid "Menu back"
 msgstr "?????? ??????"
 
-#: engines/scumm/metaengine.cpp:1190
+#: engines/scumm/metaengine.cpp:1191
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu back"
 msgstr "?????? ?????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1206
+#: engines/scumm/metaengine.cpp:1207
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1261
+#: engines/scumm/metaengine.cpp:1262
 #, fuzzy
 #| msgid "Move back"
 msgid "Cover / back"
 msgstr "?????? ??????"
 
-#: engines/scumm/metaengine.cpp:1267
+#: engines/scumm/metaengine.cpp:1268
 #, fuzzy
 #| msgid "Skip walk"
 msgid "Skip / back"
 msgstr "?????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1274
+#: engines/scumm/metaengine.cpp:1275
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu"
diff --git a/po/nb_NO.po b/po/nb_NO.po
index c400a547a2d..4195d4be314 100644
--- a/po/nb_NO.po
+++ b/po/nb_NO.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 21:39+0000\n"
+"POT-Creation-Date: 2026-06-02 16:30+0000\n"
 "PO-Revision-Date: 2026-02-07 15:22+0000\n"
 "Last-Translator: Einar Johan Tr??an S??m??en <einarjohants at gmail.com>\n"
 "Language-Team: Norwegian Bokm??l <https://translations.scummvm.org/projects/"
@@ -6412,7 +6412,7 @@ msgstr "R2"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1195 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1196 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "Meny"
 
@@ -14545,81 +14545,81 @@ msgstr "Juksefunksjon for ?? vinne motorsykkelkamper"
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1208
+#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1209
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1213
+#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1214
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1218
+#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1219
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
+#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1224
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1228
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1229
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "Plukk opp"
 
-#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1235
+#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "Bla ned"
 
-#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1242
+#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1243
 #, fuzzy
 #| msgid "Slide Left"
 msgid "Stick left"
 msgstr "Skli mot Venstre"
 
-#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1249
+#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1250
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick right"
 msgstr "Skli mot H??yre"
 
-#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1256
+#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1257
 #, fuzzy
 #| msgid "Move left"
 msgid "Fire / select"
 msgstr "G?? til venstre"
 
-#: engines/scumm/metaengine.cpp:1185
+#: engines/scumm/metaengine.cpp:1186
 #, fuzzy
 #| msgid "Move Back"
 msgid "Menu back"
 msgstr "Beveg Bakover"
 
-#: engines/scumm/metaengine.cpp:1190
+#: engines/scumm/metaengine.cpp:1191
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu back"
 msgstr "Hopp over hovedmeny"
 
-#: engines/scumm/metaengine.cpp:1206
+#: engines/scumm/metaengine.cpp:1207
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1261
+#: engines/scumm/metaengine.cpp:1262
 #, fuzzy
 #| msgid "Move Back"
 msgid "Cover / back"
 msgstr "Beveg Bakover"
 
-#: engines/scumm/metaengine.cpp:1267
+#: engines/scumm/metaengine.cpp:1268
 #, fuzzy
 #| msgid "Skip dialogue"
 msgid "Skip / back"
 msgstr "Hopp over dialog"
 
-#: engines/scumm/metaengine.cpp:1274
+#: engines/scumm/metaengine.cpp:1275
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu"
diff --git a/po/nl_NL.po b/po/nl_NL.po
index 8cfbbaecb6a..72a318554f9 100644
--- a/po/nl_NL.po
+++ b/po/nl_NL.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.9.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 21:39+0000\n"
+"POT-Creation-Date: 2026-06-02 16:30+0000\n"
 "PO-Revision-Date: 2026-02-11 14:23+0000\n"
 "Last-Translator: SecularSteve <fairfull.playing at gmail.com>\n"
 "Language-Team: Dutch <https://translations.scummvm.org/projects/scummvm/"
@@ -6087,7 +6087,7 @@ msgstr "R2"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1195 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1196 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "Menu"
 
@@ -13738,81 +13738,81 @@ msgstr "Win de fietsstrijd-cheat"
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1208
+#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1209
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1213
+#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1214
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1218
+#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1219
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
+#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1224
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1228
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1229
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "Pak op"
 
-#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1235
+#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "Scroll omlaag"
 
-#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1242
+#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1243
 #, fuzzy
 #| msgid "Slide left"
 msgid "Stick left"
 msgstr "Schuif naar link"
 
-#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1249
+#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1250
 #, fuzzy
 #| msgid "Slide right"
 msgid "Stick right"
 msgstr "Schuif naar rechts"
 
-#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1256
+#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1257
 #, fuzzy
 #| msgid "Move / Select"
 msgid "Fire / select"
 msgstr "Verplaatsen / Selecteren"
 
-#: engines/scumm/metaengine.cpp:1185
+#: engines/scumm/metaengine.cpp:1186
 #, fuzzy
 #| msgid "Move back"
 msgid "Menu back"
 msgstr "Teruggaan"
 
-#: engines/scumm/metaengine.cpp:1190
+#: engines/scumm/metaengine.cpp:1191
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu back"
 msgstr "Hoofdmenu overslaan"
 
-#: engines/scumm/metaengine.cpp:1206
+#: engines/scumm/metaengine.cpp:1207
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1261
+#: engines/scumm/metaengine.cpp:1262
 #, fuzzy
 #| msgid "Move back"
 msgid "Cover / back"
 msgstr "Teruggaan"
 
-#: engines/scumm/metaengine.cpp:1267
+#: engines/scumm/metaengine.cpp:1268
 #, fuzzy
 #| msgid "Skip walk"
 msgid "Skip / back"
 msgstr "Sla wandeling over"
 
-#: engines/scumm/metaengine.cpp:1274
+#: engines/scumm/metaengine.cpp:1275
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu"
diff --git a/po/nn_NO.po b/po/nn_NO.po
index 1dc2e56767d..120aa9731d5 100644
--- a/po/nn_NO.po
+++ b/po/nn_NO.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 21:39+0000\n"
+"POT-Creation-Date: 2026-06-02 16:30+0000\n"
 "PO-Revision-Date: 2023-12-14 14:28+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Norwegian Nynorsk <https://translations.scummvm.org/projects/"
@@ -5814,7 +5814,7 @@ msgstr ""
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1195 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1196 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "Meny"
 
@@ -13670,77 +13670,77 @@ msgstr ""
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1208
+#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1209
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1213
+#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1214
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1218
+#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1219
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
+#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1224
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1228
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1229
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "Plukk opp"
 
-#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1235
+#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
 #, fuzzy
 msgid "Stick down"
 msgstr "Bla liste ned"
 
-#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1242
+#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1243
 #, fuzzy
 #| msgid "Slide Left"
 msgid "Stick left"
 msgstr "Skli til Venstre"
 
-#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1249
+#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1250
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick right"
 msgstr "Skli til H??yre"
 
-#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1256
+#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1257
 #, fuzzy
 msgid "Fire / select"
 msgstr "Skli til Venstre"
 
-#: engines/scumm/metaengine.cpp:1185
+#: engines/scumm/metaengine.cpp:1186
 #, fuzzy
 #| msgid "Move Back"
 msgid "Menu back"
 msgstr "Beveg Bakover"
 
-#: engines/scumm/metaengine.cpp:1190
+#: engines/scumm/metaengine.cpp:1191
 #, fuzzy
 msgid "Skip / menu back"
 msgstr "Tilbake til Oppsta~r~tar"
 
-#: engines/scumm/metaengine.cpp:1206
+#: engines/scumm/metaengine.cpp:1207
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1261
+#: engines/scumm/metaengine.cpp:1262
 #, fuzzy
 #| msgid "Move Back"
 msgid "Cover / back"
 msgstr "Beveg Bakover"
 
-#: engines/scumm/metaengine.cpp:1267
+#: engines/scumm/metaengine.cpp:1268
 #, fuzzy
 msgid "Skip / back"
 msgstr "Hopp over linje"
 
-#: engines/scumm/metaengine.cpp:1274
+#: engines/scumm/metaengine.cpp:1275
 #, fuzzy
 msgid "Skip / menu"
 msgstr "Tilbake til Oppsta~r~tar"
diff --git a/po/pl_PL.po b/po/pl_PL.po
index d6c06687c4c..429ada8042d 100644
--- a/po/pl_PL.po
+++ b/po/pl_PL.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 21:39+0000\n"
+"POT-Creation-Date: 2026-06-02 16:30+0000\n"
 "PO-Revision-Date: 2026-04-17 00:22+0000\n"
 "Last-Translator: SkiffPL <skiff at windowslive.com>\n"
 "Language-Team: Polish <https://translations.scummvm.org/projects/scummvm/"
@@ -6099,7 +6099,7 @@ msgstr "R2"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1195 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1196 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "Menu"
 
@@ -13917,81 +13917,81 @@ msgstr "Cheat 'wygraj walk?? na motocyklu'"
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1208
+#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1209
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1213
+#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1214
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1218
+#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1219
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
+#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1224
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1228
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1229
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "Podnie??"
 
-#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1235
+#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "Przewi?? w d????"
 
-#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1242
+#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1243
 #, fuzzy
 #| msgid "Slide left"
 msgid "Stick left"
 msgstr "??lizg w lewo"
 
-#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1249
+#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1250
 #, fuzzy
 #| msgid "Slide right"
 msgid "Stick right"
 msgstr "??lizg w prawo"
 
-#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1256
+#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1257
 #, fuzzy
 #| msgid "Move / Select"
 msgid "Fire / select"
 msgstr "Ruch / Zaznacz"
 
-#: engines/scumm/metaengine.cpp:1185
+#: engines/scumm/metaengine.cpp:1186
 #, fuzzy
 #| msgid "Move back"
 msgid "Menu back"
 msgstr "Ruch do ty??u"
 
-#: engines/scumm/metaengine.cpp:1190
+#: engines/scumm/metaengine.cpp:1191
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu back"
 msgstr "Pomijaj menu g????wne"
 
-#: engines/scumm/metaengine.cpp:1206
+#: engines/scumm/metaengine.cpp:1207
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1261
+#: engines/scumm/metaengine.cpp:1262
 #, fuzzy
 #| msgid "Move back"
 msgid "Cover / back"
 msgstr "Ruch do ty??u"
 
-#: engines/scumm/metaengine.cpp:1267
+#: engines/scumm/metaengine.cpp:1268
 #, fuzzy
 #| msgid "Skip dialog"
 msgid "Skip / back"
 msgstr "Pomi?? dialog"
 
-#: engines/scumm/metaengine.cpp:1274
+#: engines/scumm/metaengine.cpp:1275
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu"
diff --git a/po/pt_BR.po b/po/pt_BR.po
index 13523cf72cc..d99d1145715 100644
--- a/po/pt_BR.po
+++ b/po/pt_BR.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 21:39+0000\n"
+"POT-Creation-Date: 2026-06-02 16:30+0000\n"
 "PO-Revision-Date: 2024-11-16 02:13+0000\n"
 "Last-Translator: Marcel Souza Lemes <marcosoutsider at gmail.com>\n"
 "Language-Team: Portuguese (Brazil) <https://translations.scummvm.org/"
@@ -6376,7 +6376,7 @@ msgstr "R2"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1195 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1196 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "Menu"
 
@@ -14517,81 +14517,81 @@ msgstr "Trapa??a para vencer a briga de motos"
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1208
+#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1209
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1213
+#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1214
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1218
+#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1219
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
+#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1224
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1228
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1229
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "Pegar"
 
-#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1235
+#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "Rolar para baixo"
 
-#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1242
+#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1243
 #, fuzzy
 #| msgid "Slide Left"
 msgid "Stick left"
 msgstr "Deslizar para Esquerda"
 
-#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1249
+#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1250
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick right"
 msgstr "Deslizar para Direita"
 
-#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1256
+#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1257
 #, fuzzy
 #| msgid "Move left"
 msgid "Fire / select"
 msgstr "Mover para esquerda"
 
-#: engines/scumm/metaengine.cpp:1185
+#: engines/scumm/metaengine.cpp:1186
 #, fuzzy
 #| msgid "Move Back"
 msgid "Menu back"
 msgstr "Mover para Tr??s"
 
-#: engines/scumm/metaengine.cpp:1190
+#: engines/scumm/metaengine.cpp:1191
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu back"
 msgstr "Omitir menu principal"
 
-#: engines/scumm/metaengine.cpp:1206
+#: engines/scumm/metaengine.cpp:1207
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1261
+#: engines/scumm/metaengine.cpp:1262
 #, fuzzy
 #| msgid "Move Back"
 msgid "Cover / back"
 msgstr "Mover para Tr??s"
 
-#: engines/scumm/metaengine.cpp:1267
+#: engines/scumm/metaengine.cpp:1268
 #, fuzzy
 #| msgid "Skip dialogue"
 msgid "Skip / back"
 msgstr "Pular di??logo"
 
-#: engines/scumm/metaengine.cpp:1274
+#: engines/scumm/metaengine.cpp:1275
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu"
diff --git a/po/pt_PT.po b/po/pt_PT.po
index 4027efe9572..9132e8e0ba6 100644
--- a/po/pt_PT.po
+++ b/po/pt_PT.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.10.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 21:39+0000\n"
+"POT-Creation-Date: 2026-06-02 16:30+0000\n"
 "PO-Revision-Date: 2022-06-05 14:09+0000\n"
 "Last-Translator: Daniel <supsuper at gmail.com>\n"
 "Language-Team: Portuguese (Portugal) <https://translations.scummvm.org/"
@@ -5838,7 +5838,7 @@ msgstr "R2"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1195 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1196 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "Menu"
 
@@ -13792,80 +13792,80 @@ msgstr ""
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1208
+#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1209
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1213
+#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1214
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1218
+#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1219
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
+#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1224
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1228
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1229
 #, fuzzy
 #| msgid "Left Stick X"
 msgid "Stick up"
 msgstr "Anal??gico Esquerdo X"
 
-#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1235
+#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "Deslizar para baixo"
 
-#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1242
+#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1243
 #, fuzzy
 #| msgid "Slide Left"
 msgid "Stick left"
 msgstr "Deslizar ?? esquerda"
 
-#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1249
+#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1250
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick right"
 msgstr "Deslizar ?? direita"
 
-#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1256
+#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1257
 #, fuzzy
 #| msgid "Move left"
 msgid "Fire / select"
 msgstr "Mover para a esquerda"
 
-#: engines/scumm/metaengine.cpp:1185
+#: engines/scumm/metaengine.cpp:1186
 #, fuzzy
 #| msgid "Move Back"
 msgid "Menu back"
 msgstr "Retroceder"
 
-#: engines/scumm/metaengine.cpp:1190
+#: engines/scumm/metaengine.cpp:1191
 #, fuzzy
 msgid "Skip / menu back"
 msgstr "~M~enu principal"
 
-#: engines/scumm/metaengine.cpp:1206
+#: engines/scumm/metaengine.cpp:1207
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1261
+#: engines/scumm/metaengine.cpp:1262
 #, fuzzy
 #| msgid "Move Back"
 msgid "Cover / back"
 msgstr "Retroceder"
 
-#: engines/scumm/metaengine.cpp:1267
+#: engines/scumm/metaengine.cpp:1268
 #, fuzzy
 #| msgid "Skip dialogue"
 msgid "Skip / back"
 msgstr "Ignorar frase"
 
-#: engines/scumm/metaengine.cpp:1274
+#: engines/scumm/metaengine.cpp:1275
 #, fuzzy
 msgid "Skip / menu"
 msgstr "~M~enu principal"
diff --git a/po/ro.po b/po/ro.po
index e85e4e1fb47..c832db79dd6 100644
--- a/po/ro.po
+++ b/po/ro.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.10.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 21:39+0000\n"
+"POT-Creation-Date: 2026-06-02 16:30+0000\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: Automatically generated\n"
 "Language-Team: none\n"
@@ -5434,7 +5434,7 @@ msgstr ""
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1195 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1196 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr ""
 
@@ -12422,63 +12422,63 @@ msgstr ""
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1208
+#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1209
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1213
+#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1214
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1218
+#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1219
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
+#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1224
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1228
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1229
 msgid "Stick up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1235
+#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
 msgid "Stick down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1242
+#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1243
 msgid "Stick left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1249
+#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1250
 msgid "Stick right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1256
+#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1257
 msgid "Fire / select"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1185
+#: engines/scumm/metaengine.cpp:1186
 msgid "Menu back"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1190
+#: engines/scumm/metaengine.cpp:1191
 msgid "Skip / menu back"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1206
+#: engines/scumm/metaengine.cpp:1207
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1261
+#: engines/scumm/metaengine.cpp:1262
 msgid "Cover / back"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1267
+#: engines/scumm/metaengine.cpp:1268
 msgid "Skip / back"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1274
+#: engines/scumm/metaengine.cpp:1275
 msgid "Skip / menu"
 msgstr ""
 
diff --git a/po/ru_RU.po b/po/ru_RU.po
index 9f59bb880c8..0d6017ab83f 100644
--- a/po/ru_RU.po
+++ b/po/ru_RU.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.8.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 21:39+0000\n"
+"POT-Creation-Date: 2026-06-02 16:30+0000\n"
 "PO-Revision-Date: 2026-05-06 08:54+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Russian <https://translations.scummvm.org/projects/scummvm/"
@@ -6109,7 +6109,7 @@ msgstr "R2"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1195 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1196 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "????????"
 
@@ -13586,81 +13586,81 @@ msgstr "?????? ?????? ???????????????? ???????? ???? ????????????"
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1208
+#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1209
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1213
+#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1214
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1218
+#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1219
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
+#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1224
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1228
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1229
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "??????????????"
 
-#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1235
+#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "???????????????????? ????????"
 
-#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1242
+#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1243
 #, fuzzy
 #| msgid "Slide left"
 msgid "Stick left"
 msgstr "?????????????????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1249
+#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1250
 #, fuzzy
 #| msgid "Slide right"
 msgid "Stick right"
 msgstr "?????????????????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1256
+#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1257
 #, fuzzy
 #| msgid "Move / Select"
 msgid "Fire / select"
 msgstr "???????? / ??????????????"
 
-#: engines/scumm/metaengine.cpp:1185
+#: engines/scumm/metaengine.cpp:1186
 #, fuzzy
 #| msgid "Move back"
 msgid "Menu back"
 msgstr "???????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1190
+#: engines/scumm/metaengine.cpp:1191
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu back"
 msgstr "???????????????????? ?????????????? ????????"
 
-#: engines/scumm/metaengine.cpp:1206
+#: engines/scumm/metaengine.cpp:1207
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1261
+#: engines/scumm/metaengine.cpp:1262
 #, fuzzy
 #| msgid "Move back"
 msgid "Cover / back"
 msgstr "???????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1267
+#: engines/scumm/metaengine.cpp:1268
 #, fuzzy
 #| msgid "Skip walk"
 msgid "Skip / back"
 msgstr "???????????????????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1274
+#: engines/scumm/metaengine.cpp:1275
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu"
diff --git a/po/sv_SE.po b/po/sv_SE.po
index db388799f40..99c218c4c4e 100644
--- a/po/sv_SE.po
+++ b/po/sv_SE.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.5.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 21:39+0000\n"
+"POT-Creation-Date: 2026-06-02 16:30+0000\n"
 "PO-Revision-Date: 2026-04-19 13:22+0000\n"
 "Last-Translator: Daniel Nylander <po at danielnylander.se>\n"
 "Language-Team: Swedish <https://translations.scummvm.org/projects/scummvm/"
@@ -6086,7 +6086,7 @@ msgstr "R2"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1195 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1196 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "Meny"
 
@@ -13635,81 +13635,81 @@ msgstr "Vinn cykelkampsfusket"
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1208
+#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1209
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1213
+#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1214
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1218
+#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1219
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
+#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1224
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1228
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1229
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "Ta"
 
-#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1235
+#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "Bl??ddra ned??t"
 
-#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1242
+#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1243
 #, fuzzy
 #| msgid "Slide left"
 msgid "Stick left"
 msgstr "Glid v??nster"
 
-#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1249
+#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1250
 #, fuzzy
 #| msgid "Slide right"
 msgid "Stick right"
 msgstr "Glid h??ger"
 
-#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1256
+#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1257
 #, fuzzy
 #| msgid "Move / Select"
 msgid "Fire / select"
 msgstr "Flytta / V??lj"
 
-#: engines/scumm/metaengine.cpp:1185
+#: engines/scumm/metaengine.cpp:1186
 #, fuzzy
 #| msgid "Move back"
 msgid "Menu back"
 msgstr "Stega bak??t"
 
-#: engines/scumm/metaengine.cpp:1190
+#: engines/scumm/metaengine.cpp:1191
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu back"
 msgstr "Hoppa ??ver huvudmeny"
 
-#: engines/scumm/metaengine.cpp:1206
+#: engines/scumm/metaengine.cpp:1207
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1261
+#: engines/scumm/metaengine.cpp:1262
 #, fuzzy
 #| msgid "Move back"
 msgid "Cover / back"
 msgstr "Stega bak??t"
 
-#: engines/scumm/metaengine.cpp:1267
+#: engines/scumm/metaengine.cpp:1268
 #, fuzzy
 #| msgid "Skip walk"
 msgid "Skip / back"
 msgstr "Hoppa ??ver g??ng"
 
-#: engines/scumm/metaengine.cpp:1274
+#: engines/scumm/metaengine.cpp:1275
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu"
diff --git a/po/tr.po b/po/tr.po
index a78c906b6c0..d91a8e24d23 100644
--- a/po/tr.po
+++ b/po/tr.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 21:39+0000\n"
+"POT-Creation-Date: 2026-06-02 16:30+0000\n"
 "PO-Revision-Date: 2024-10-10 12:33+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Turkish <https://translations.scummvm.org/projects/scummvm/"
@@ -5762,7 +5762,7 @@ msgstr ""
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1195 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1196 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr ""
 
@@ -13296,74 +13296,74 @@ msgstr ""
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1208
+#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1209
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1213
+#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1214
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1218
+#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1219
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
+#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1224
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1228
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1229
 msgid "Stick up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1235
+#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
 #, fuzzy
 msgid "Stick down"
 msgstr "A??a????"
 
-#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1242
+#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1243
 #, fuzzy
 msgid "Stick left"
 msgstr "Sa??"
 
-#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1249
+#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1250
 #, fuzzy
 msgid "Stick right"
 msgstr "Sa??"
 
-#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1256
+#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1257
 #, fuzzy
 msgid "Fire / select"
 msgstr "Sol"
 
-#: engines/scumm/metaengine.cpp:1185
+#: engines/scumm/metaengine.cpp:1186
 #, fuzzy
 #| msgid "Backend"
 msgid "Menu back"
 msgstr "Altyap??"
 
-#: engines/scumm/metaengine.cpp:1190
+#: engines/scumm/metaengine.cpp:1191
 #, fuzzy
 msgid "Skip / menu back"
 msgstr "??ptal"
 
-#: engines/scumm/metaengine.cpp:1206
+#: engines/scumm/metaengine.cpp:1207
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1261
+#: engines/scumm/metaengine.cpp:1262
 #, fuzzy
 #| msgid "Backend"
 msgid "Cover / back"
 msgstr "Altyap??"
 
-#: engines/scumm/metaengine.cpp:1267
+#: engines/scumm/metaengine.cpp:1268
 #, fuzzy
 #| msgid "Show hidden files"
 msgid "Skip / back"
 msgstr "Gizli dosyalar?? g??ster"
 
-#: engines/scumm/metaengine.cpp:1274
+#: engines/scumm/metaengine.cpp:1275
 #, fuzzy
 msgid "Skip / menu"
 msgstr "??ptal"
diff --git a/po/uk_UA.po b/po/uk_UA.po
index b6078449f44..ee3fb2d2495 100644
--- a/po/uk_UA.po
+++ b/po/uk_UA.po
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.9.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 21:39+0000\n"
+"POT-Creation-Date: 2026-06-02 16:30+0000\n"
 "PO-Revision-Date: 2026-05-06 08:54+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Ukrainian <https://translations.scummvm.org/projects/scummvm/"
@@ -6095,7 +6095,7 @@ msgstr "R2"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1195 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1196 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "????????"
 
@@ -13545,81 +13545,81 @@ msgstr "?????? ???????????????????? ?????? ???? ??????????????"
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1208
+#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1209
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1213
+#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1214
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1218
+#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1219
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
+#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1224
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1228
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1229
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "??????????????????"
 
-#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1235
+#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "?????????????????????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1242
+#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1243
 #, fuzzy
 #| msgid "Slide left"
 msgid "Stick left"
 msgstr "???????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1249
+#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1250
 #, fuzzy
 #| msgid "Slide right"
 msgid "Stick right"
 msgstr "???????? ??????????????"
 
-#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1256
+#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1257
 #, fuzzy
 #| msgid "Move / Select"
 msgid "Fire / select"
 msgstr "???????????????? / ??????????????"
 
-#: engines/scumm/metaengine.cpp:1185
+#: engines/scumm/metaengine.cpp:1186
 #, fuzzy
 #| msgid "Move back"
 msgid "Menu back"
 msgstr "???????????????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1190
+#: engines/scumm/metaengine.cpp:1191
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu back"
 msgstr "???????????????????? ?????????????? ????????"
 
-#: engines/scumm/metaengine.cpp:1206
+#: engines/scumm/metaengine.cpp:1207
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1261
+#: engines/scumm/metaengine.cpp:1262
 #, fuzzy
 #| msgid "Move back"
 msgid "Cover / back"
 msgstr "???????????????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1267
+#: engines/scumm/metaengine.cpp:1268
 #, fuzzy
 #| msgid "Skip walk"
 msgid "Skip / back"
 msgstr "???????????????????? ??????????????"
 
-#: engines/scumm/metaengine.cpp:1274
+#: engines/scumm/metaengine.cpp:1275
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu"
diff --git a/po/zh.po b/po/zh.po
index 6e9917a4016..dbef50a6a93 100644
--- a/po/zh.po
+++ b/po/zh.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 21:39+0000\n"
+"POT-Creation-Date: 2026-06-02 16:30+0000\n"
 "PO-Revision-Date: 2024-05-14 12:42+0000\n"
 "Last-Translator: ?????? <clonewith at qq.com>\n"
 "Language-Team: Chinese <https://translations.scummvm.org/projects/scummvm/"
@@ -5543,7 +5543,7 @@ msgstr ""
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1195 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1196 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr ""
 
@@ -13120,75 +13120,75 @@ msgstr ""
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1208
+#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1209
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1213
+#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1214
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1218
+#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1219
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
+#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1224
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1228
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1229
 msgid "Stick up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1235
+#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
 #, fuzzy
 msgid "Stick down"
 msgstr "??????"
 
-#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1242
+#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1243
 #, fuzzy
 msgid "Stick left"
 msgstr "??????"
 
-#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1249
+#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1250
 #, fuzzy
 msgid "Stick right"
 msgstr "??????"
 
-#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1256
+#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1257
 #, fuzzy
 msgid "Fire / select"
 msgstr "??????"
 
-#: engines/scumm/metaengine.cpp:1185
+#: engines/scumm/metaengine.cpp:1186
 #, fuzzy
 #| msgid "Backend"
 msgid "Menu back"
 msgstr "??????"
 
-#: engines/scumm/metaengine.cpp:1190
+#: engines/scumm/metaengine.cpp:1191
 #, fuzzy
 #| msgid "Skip autosave"
 msgid "Skip / menu back"
 msgstr "??????????????????"
 
-#: engines/scumm/metaengine.cpp:1206
+#: engines/scumm/metaengine.cpp:1207
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1261
+#: engines/scumm/metaengine.cpp:1262
 #, fuzzy
 #| msgid "Backend"
 msgid "Cover / back"
 msgstr "??????"
 
-#: engines/scumm/metaengine.cpp:1267
+#: engines/scumm/metaengine.cpp:1268
 #, fuzzy
 #| msgid "Show hidden files"
 msgid "Skip / back"
 msgstr "?????????????????????"
 
-#: engines/scumm/metaengine.cpp:1274
+#: engines/scumm/metaengine.cpp:1275
 #, fuzzy
 #| msgid "Skip autosave"
 msgid "Skip / menu"
diff --git a/po/zh_Hans.po b/po/zh_Hans.po
index 9a65a1c001d..7b97e247c63 100644
--- a/po/zh_Hans.po
+++ b/po/zh_Hans.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.9.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 21:39+0000\n"
+"POT-Creation-Date: 2026-06-02 16:30+0000\n"
 "PO-Revision-Date: 2024-12-01 22:18+0000\n"
 "Last-Translator: ?????? <clonewith at qq.com>\n"
 "Language-Team: Chinese (Simplified Han script) <https://"
@@ -6179,7 +6179,7 @@ msgstr "R2"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1195 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1196 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "??????"
 
@@ -14095,81 +14095,81 @@ msgstr "???????????????????????????"
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1208
+#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1209
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1213
+#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1214
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1218
+#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1219
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
+#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1224
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1228
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1229
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "??????"
 
-#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1235
+#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "????????????"
 
-#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1242
+#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1243
 #, fuzzy
 #| msgid "Slide Left"
 msgid "Stick left"
 msgstr "????????????"
 
-#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1249
+#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1250
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick right"
 msgstr "????????????"
 
-#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1256
+#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1257
 #, fuzzy
 #| msgid "Move left"
 msgid "Fire / select"
 msgstr "????????????"
 
-#: engines/scumm/metaengine.cpp:1185
+#: engines/scumm/metaengine.cpp:1186
 #, fuzzy
 #| msgid "Move Back"
 msgid "Menu back"
 msgstr "????????????"
 
-#: engines/scumm/metaengine.cpp:1190
+#: engines/scumm/metaengine.cpp:1191
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu back"
 msgstr "???????????????"
 
-#: engines/scumm/metaengine.cpp:1206
+#: engines/scumm/metaengine.cpp:1207
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1261
+#: engines/scumm/metaengine.cpp:1262
 #, fuzzy
 #| msgid "Move Back"
 msgid "Cover / back"
 msgstr "????????????"
 
-#: engines/scumm/metaengine.cpp:1267
+#: engines/scumm/metaengine.cpp:1268
 #, fuzzy
 #| msgid "Skip dialogue"
 msgid "Skip / back"
 msgstr "????????????"
 
-#: engines/scumm/metaengine.cpp:1274
+#: engines/scumm/metaengine.cpp:1275
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu"
diff --git a/po/zh_Hant.po b/po/zh_Hant.po
index c74930b3fae..05944cc1c4d 100644
--- a/po/zh_Hant.po
+++ b/po/zh_Hant.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.9.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-01 21:39+0000\n"
+"POT-Creation-Date: 2026-06-02 16:30+0000\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: Automatically generated\n"
 "Language-Team: none\n"
@@ -5435,7 +5435,7 @@ msgstr ""
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1195 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1196 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr ""
 
@@ -12423,63 +12423,63 @@ msgstr ""
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1208
+#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1209
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1213
+#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1214
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1218
+#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1219
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1223
+#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1224
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1228
+#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1229
 msgid "Stick up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1235
+#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
 msgid "Stick down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1242
+#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1243
 msgid "Stick left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1249
+#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1250
 msgid "Stick right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1256
+#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1257
 msgid "Fire / select"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1185
+#: engines/scumm/metaengine.cpp:1186
 msgid "Menu back"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1190
+#: engines/scumm/metaengine.cpp:1191
 msgid "Skip / menu back"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1206
+#: engines/scumm/metaengine.cpp:1207
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1261
+#: engines/scumm/metaengine.cpp:1262
 msgid "Cover / back"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1267
+#: engines/scumm/metaengine.cpp:1268
 msgid "Skip / back"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1274
+#: engines/scumm/metaengine.cpp:1275
 msgid "Skip / menu"
 msgstr ""
 



From noreply at scummvm.org  Tue Jun  2 17:25:34 2026
From: noreply at scummvm.org (neuromancer)
Date: Tue,  2 Jun 2026 17:25:34 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 fc697e4f41e629e2bb9c86868190194e4af0b319
Message-ID: <20260602172534.5C993700573@ron.scummvm.net>

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:
50860fbcf7 SCUMM: RA2: defensive check for paused game
fc697e4f41 SCUMM: RA2: better rendering of shooting in level 10


Commit: 50860fbcf70a619a40a42e00c3c5315f8973b4e3
    https://github.com/scummvm/scummvm/commit/50860fbcf70a619a40a42e00c3c5315f8973b4e3
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-02T19:25:14+02:00

Commit Message:
SCUMM: RA2: defensive check for paused game

Changed paths:
    engines/scumm/insane/rebel2/rebel.cpp


diff --git a/engines/scumm/insane/rebel2/rebel.cpp b/engines/scumm/insane/rebel2/rebel.cpp
index f570cda54ca..e0be2b591c6 100644
--- a/engines/scumm/insane/rebel2/rebel.cpp
+++ b/engines/scumm/insane/rebel2/rebel.cpp
@@ -594,6 +594,13 @@ void InsaneRebel2::openGameplayMainMenu(SmushPlayer *splayer) {
 bool InsaneRebel2::notifyEvent(const Common::Event &event) {
 	SmushPlayer *splayer = ((ScummEngine_v7 *)_vm)->_splayer;
 
+	// Global ScummVM dialogs pause the engine while their modal event loop runs.
+	// Do not consume those events as RA2 input: a key seen here would otherwise
+	// trip RA2's "any key unpauses gameplay" path while the Smush player must
+	// remain paused for the dialog/focus interval.
+	if (_vm->isPaused())
+		return false;
+
 	// Keep the gamepad reticle authoritative against stray pointer events. During
 	// gameplay the cursor is locked ~screen-center (levels.cpp lockMouse), so any
 	// spurious pointer event ??? e.g. a gamepad "fire" surfacing as a button-down ???


Commit: fc697e4f41e629e2bb9c86868190194e4af0b319
    https://github.com/scummvm/scummvm/commit/fc697e4f41e629e2bb9c86868190194e4af0b319
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-02T19:25:14+02:00

Commit Message:
SCUMM: RA2: better rendering of shooting in level 10

Changed paths:
    engines/scumm/insane/rebel2/iact.cpp
    engines/scumm/insane/rebel2/rebel.cpp
    engines/scumm/insane/rebel2/rebel.h
    engines/scumm/insane/rebel2/render.cpp
    engines/scumm/insane/rebel2/runlevels.cpp


diff --git a/engines/scumm/insane/rebel2/iact.cpp b/engines/scumm/insane/rebel2/iact.cpp
index 36dbc54a38e..fef48209da6 100644
--- a/engines/scumm/insane/rebel2/iact.cpp
+++ b/engines/scumm/insane/rebel2/iact.cpp
@@ -1571,6 +1571,23 @@ bool InsaneRebel2::loadOpcode8Handler7FlySprites(Common::SeekableReadStream &b,
 	return false;
 }
 
+// ScummVM refactor helper for opcode 8 Handler 7 shot table loading, not a separate retail function.
+bool InsaneRebel2::loadOpcode8Handler7ShotTable(Common::SeekableReadStream &b, int64 startPos, int64 remaining, int16 par4) {
+	// FUN_0040c3cc case 6:
+	//   par4=12 -> FUN_0040fcfa(text, DAT_004437c2, DAT_00443808)
+	//   par4=13 -> FUN_0040fcfa(text, DAT_0044384e, DAT_00443894)
+	if (_rebelHandler != 7 || (par4 != 12 && par4 != 13))
+		return false;
+
+	if (loadHandler7ShotTable(b, startPos, remaining, par4)) {
+		b.seek(startPos);
+		return true;
+	}
+
+	b.seek(startPos);
+	return false;
+}
+
 // ScummVM refactor helper for opcode 8 edge table loading, not a separate retail function.
 bool InsaneRebel2::loadOpcode8EdgeTable(Common::SeekableReadStream &b, int64 startPos, int64 remaining, int16 par4) {
 	// Edge Blend Table Loading (par4 == 1000)
@@ -1800,6 +1817,9 @@ void InsaneRebel2::iactRebel2Opcode8(byte *renderBitmap, Common::SeekableReadStr
 	if (loadOpcode8Handler7FlySprites(b, startPos, remaining, par4))
 		return;
 
+	if (loadOpcode8Handler7ShotTable(b, startPos, remaining, par4))
+		return;
+
 	if (loadOpcode8EdgeTable(b, startPos, remaining, par4))
 		return;
 
@@ -1900,6 +1920,91 @@ bool InsaneRebel2::loadHandler25ShotOriginTable(Common::SeekableReadStream &b, i
 	return true;
 }
 
+// loadHandler7ShotTable -- Parse handler 7 laser muzzle coordinate pairs from IACT payload.
+bool InsaneRebel2::loadHandler7ShotTable(Common::SeekableReadStream &b, int64 startPos, int64 remaining, int16 par4) {
+	// Retail FUN_0040fcfa parses 35 "%hd %hd" pairs from offset +18 in the IACT
+	// chunk into two parallel 35-entry tables. These tables are BSS globals in
+	// the EXE, so their values only exist in the SAN/IACT stream.
+	if (remaining < 12)
+		return false;
+
+	int64 savedPos = b.pos();
+	b.seek(startPos + 6);
+	uint32 textSize = b.readUint32LE();
+
+	int64 textPos = startPos + 10;
+	int64 maxAvail = remaining - 10;
+	if (maxAvail <= 0) {
+		b.seek(savedPos);
+		return false;
+	}
+
+	int64 bytesToRead = maxAvail;
+	if (textSize > 0 && (int64)textSize <= maxAvail)
+		bytesToRead = textSize;
+
+	char *buf = new char[(size_t)bytesToRead + 1];
+	b.seek(textPos);
+	b.read((byte *)buf, bytesToRead);
+	buf[bytesToRead] = '\0';
+
+	int16 vals[70];
+	int count = 0;
+	const char *p = buf;
+	const char *end = buf + bytesToRead;
+	while (p < end && count < 70) {
+		while (p < end && *p != '-' && *p != '+' && !Common::isDigit(*p))
+			++p;
+		if (p >= end)
+			break;
+
+		int sign = 1;
+		if (*p == '-' || *p == '+') {
+			if (*p == '-')
+				sign = -1;
+			++p;
+		}
+
+		if (p >= end || !Common::isDigit(*p))
+			continue;
+
+		int value = 0;
+		while (p < end && Common::isDigit(*p)) {
+			value = value * 10 + (*p - '0');
+			if (value > 32768)
+				value = 32768;
+			++p;
+		}
+
+		vals[count++] = (int16)CLIP<int>(sign * value, -32768, 32767);
+	}
+
+	delete[] buf;
+	b.seek(savedPos);
+
+	if (count < 70) {
+		debug("Rebel2 Opcode 8: Handler7 par4=%d shot table parse failed (count=%d)", par4, count);
+		return false;
+	}
+
+	int16 *tableX = (par4 == 12) ? _flyLeftGunX : _flyRightGunX;
+	int16 *tableY = (par4 == 12) ? _flyLeftGunY : _flyRightGunY;
+	for (int i = 0; i < 35; ++i) {
+		tableX[i] = vals[i * 2];
+		tableY[i] = vals[i * 2 + 1];
+	}
+
+	if (par4 == 12)
+		_flyLeftGunTableLoaded = true;
+	else
+		_flyRightGunTableLoaded = true;
+
+	debug("Rebel2 Opcode 8: Loaded Handler7 %s gun table idx0=(%d,%d) idx17=(%d,%d) idx34=(%d,%d)",
+		(par4 == 12) ? "left" : "right",
+		tableX[0], tableY[0], tableX[17], tableY[17], tableX[34], tableY[34]);
+	return true;
+}
+
 // ---------------------------------------------------------------------------
 // Opcode 8 Helper Functions
 // ---------------------------------------------------------------------------
diff --git a/engines/scumm/insane/rebel2/rebel.cpp b/engines/scumm/insane/rebel2/rebel.cpp
index e0be2b591c6..c76a47b1067 100644
--- a/engines/scumm/insane/rebel2/rebel.cpp
+++ b/engines/scumm/insane/rebel2/rebel.cpp
@@ -347,6 +347,12 @@ InsaneRebel2::InsaneRebel2(ScummEngine_v7 *scumm) {
 		_spaceShots[i].variant = 0;
 	}
 	_spaceShotDirection = 0;
+	memset(_flyLeftGunX, 0, sizeof(_flyLeftGunX));
+	memset(_flyLeftGunY, 0, sizeof(_flyLeftGunY));
+	memset(_flyRightGunX, 0, sizeof(_flyRightGunX));
+	memset(_flyRightGunY, 0, sizeof(_flyRightGunY));
+	_flyLeftGunTableLoaded = false;
+	_flyRightGunTableLoaded = false;
 
 	for (i = 0; i < 16; i++) {
 		_rebelEmbeddedHud[i].pixels = nullptr;
@@ -1474,7 +1480,7 @@ int32 InsaneRebel2::processMouse() {
 	// - Other gameplay handlers fire while button is held; slot counters still rate-limit.
 	bool triggerShot = (_rebelHandler == 25) ? (leftPressed && !leftWasPressed) : leftPressed;
 	if (triggerShot && isShootingAllowed()) {
-		Common::Point mousePos = getGameplayAimPoint();
+		Common::Point mousePos = (_rebelHandler == 7) ? getHandler7ShotTargetPoint() : getGameplayAimPoint();
 		debug("Rebel2 Click: Mouse=(%d,%d) Enemies=%d",
 			mousePos.x, mousePos.y, _enemies.size());
 
@@ -1482,7 +1488,11 @@ int32 InsaneRebel2::processMouse() {
 		spawnShot(mousePos.x, mousePos.y);
 
 		// Calculate world position for hit testing
-		Common::Point worldMousePos(mousePos.x + _viewX, mousePos.y + _viewY);
+		Common::Point worldMousePos = mousePos;
+		if (_rebelHandler != 7) {
+			worldMousePos.x += _viewX;
+			worldMousePos.y += _viewY;
+		}
 
 		// Check for hit on any active enemy
 		Common::List<enemy>::iterator it;
diff --git a/engines/scumm/insane/rebel2/rebel.h b/engines/scumm/insane/rebel2/rebel.h
index 73b03abc07a..6ccc021848f 100644
--- a/engines/scumm/insane/rebel2/rebel.h
+++ b/engines/scumm/insane/rebel2/rebel.h
@@ -544,6 +544,7 @@ public:
 	void scanOpcode6EmbeddedAnim(byte *renderBitmap, Common::SeekableReadStream &b, int32 chunkSize, int16 par4);
 	void iactRebel2Opcode8(byte *renderBitmap, Common::SeekableReadStream &b, int32 chunkSize, int16 par2, int16 par3, int16 par4);
 	bool loadOpcode8Handler7FlySprites(Common::SeekableReadStream &b, int64 startPos, int64 remaining, int16 par4);
+	bool loadOpcode8Handler7ShotTable(Common::SeekableReadStream &b, int64 startPos, int64 remaining, int16 par4);
 	bool loadOpcode8EdgeTable(Common::SeekableReadStream &b, int64 startPos, int64 remaining, int16 par4);
 	bool loadOpcode8AuxSfx(Common::SeekableReadStream &b, int64 startPos, int64 remaining, int16 par4);
 	bool loadOpcode8ShotOriginTable(Common::SeekableReadStream &b, int64 startPos, int64 remaining, int16 par4);
@@ -607,6 +608,9 @@ public:
 
 	// Update target lock state and draw crosshair/reticle
 	void renderCrosshair(byte *renderBitmap, int pitch, int width, int height);
+	Common::Point getHandler7ShipDrawPoint();
+	Common::Point getHandler7ProjectedPoint();
+	Common::Point getHandler7ShotTargetPoint();
 
 	// Reset enemy active flags and collision zones at frame end
 	void frameEndCleanup();
@@ -618,6 +622,7 @@ public:
 
 	// Load Handler 7 FLY NUT sprites from IACT data
 	bool loadHandler7FlySprites(Common::SeekableReadStream &b, int64 remaining, int16 par4);
+	bool loadHandler7ShotTable(Common::SeekableReadStream &b, int64 startPos, int64 remaining, int16 par4);
 
 	// Load turret HUD overlay NUT from ANIM data
 	bool loadTurretHudOverlay(byte *animData, int32 size, int16 par3);
@@ -985,6 +990,12 @@ public:
 	};
 	SpaceShot _spaceShots[2];
 	int16 _spaceShotDirection;  // DAT_0044374e - ship direction for gun lookup
+	int16 _flyLeftGunX[35];     // DAT_004437c2 - handler 7 left muzzle X table
+	int16 _flyLeftGunY[35];     // DAT_00443808 - handler 7 left muzzle Y table
+	int16 _flyRightGunX[35];    // DAT_0044384e - handler 7 right muzzle X table
+	int16 _flyRightGunY[35];    // DAT_00443894 - handler 7 right muzzle Y table
+	bool _flyLeftGunTableLoaded;
+	bool _flyRightGunTableLoaded;
 
 	// Handler-specific shot spawning
 	void spawnTurretShot(int x, int y);    // Handler 0x26
diff --git a/engines/scumm/insane/rebel2/render.cpp b/engines/scumm/insane/rebel2/render.cpp
index 194333b6a0a..9302279fbbd 100644
--- a/engines/scumm/insane/rebel2/render.cpp
+++ b/engines/scumm/insane/rebel2/render.cpp
@@ -494,6 +494,39 @@ void InsaneRebel2::spawnHandler25Shot(int x, int y) {
 	}
 }
 
+Common::Point InsaneRebel2::getHandler7ShipDrawPoint() {
+	int shipCenterX = (_flyShipScreenX - 0xd4) + _perspectiveX + 160 + _viewX;
+	int shipCenterY = (_flyShipScreenY - 0x82) + _perspectiveY + 100 + _viewY;
+
+	if (!_flyShipSprite || _flyShipSprite->getNumChars() <= 0)
+		return Common::Point(shipCenterX - 0xd4, shipCenterY - 0x82);
+
+	int spriteIndex = CLIP<int>(_shipDirectionIndex, 0, _flyShipSprite->getNumChars() - 1);
+	return Common::Point(shipCenterX - _flyShipSprite->getCharWidth(spriteIndex) / 2,
+	                     shipCenterY - _flyShipSprite->getCharHeight(spriteIndex) / 2);
+}
+
+Common::Point InsaneRebel2::getHandler7ProjectedPoint() {
+	int viewTilt = (_viewShift * 5) / 128;
+	int xSkew = (viewTilt * 9) / 4;
+	int ySkew = viewTilt * 5;
+	int relX = _flyShipScreenX - (_perspectiveX + 0xd4);
+	int relY = _flyShipScreenY - (_perspectiveY + 0x82);
+	int projectedX = (xSkew * relY) / 0x55 + relX + 0xa0 + _viewX;
+	int projectedY = relY + 0x55 - (ySkew * relX) / 0xa0 + _viewY;
+
+	return Common::Point(projectedX, projectedY);
+}
+
+Common::Point InsaneRebel2::getHandler7ShotTargetPoint() {
+	// Retail handler 7 targets the projected ship/crosshair point computed in
+	// FUN_0040d836; it does not use the generic mouse position for combat shots.
+	Common::Point projected = getHandler7ProjectedPoint();
+
+	return Common::Point(projected.x + _smoothedVelocity / 2,
+	                     projected.y + ABS(_smoothedVelocity) / 4 - _verticalInput / 2 - 0x28);
+}
+
 // spawnSpaceShot -- Handler 7 space combat shot spawn (FUN_40D836).
 void InsaneRebel2::spawnSpaceShot(int x, int y) {
 	for (int i = 0; i < 2; i++) {
@@ -502,21 +535,30 @@ void InsaneRebel2::spawnSpaceShot(int x, int y) {
 			playSfx(6, 127, 0);
 
 			_spaceShots[i].counter = getShotMaxDuration();
-			_spaceShots[i].targetX = x;  // Screen coords
-			_spaceShots[i].targetY = y;
-
-			// Calculate gun positions from direction-based lookup tables
-			// In the original, these come from tables indexed by _shipDirectionIndex
-			// DAT_004437c2/DAT_00443808 for left gun, DAT_0044384e/DAT_00443894 for right gun
-			// Use simplified positions relative to the ship.
-			int shipScreenX = 160 + ((_shipPosX - 160) >> 3);
-			int shipScreenY = 105 + ((_shipPosY - 40) >> 2);
-
-			// Gun offsets (approximate from disassembly)
-			_spaceShots[i].leftGunX = shipScreenX - 28;
-			_spaceShots[i].leftGunY = shipScreenY + 10;
-			_spaceShots[i].rightGunX = shipScreenX + 28;
-			_spaceShots[i].rightGunY = shipScreenY + 10;
+			Common::Point projected = getHandler7ProjectedPoint();
+			Common::Point target = getHandler7ShotTargetPoint();
+			int tableIndex = CLIP<int>(_shipDirectionIndex, 0, 34);
+
+			_spaceShots[i].targetX = target.x;
+			_spaceShots[i].targetY = target.y;
+
+			// FUN_0040d836 uses muzzle tables loaded by FUN_0040fcfa from opcode
+			// 8 par4=12/13. Values are centered FLY coordinates, adjusted by
+			// the projected ship point before drawing the line.
+			if (_flyLeftGunTableLoaded) {
+				_spaceShots[i].leftGunX = projected.x + _flyLeftGunX[tableIndex] - 0xd4;
+				_spaceShots[i].leftGunY = projected.y + _flyLeftGunY[tableIndex] - 0x82;
+			} else {
+				_spaceShots[i].leftGunX = projected.x - 28;
+				_spaceShots[i].leftGunY = projected.y + 10;
+			}
+			if (_flyRightGunTableLoaded) {
+				_spaceShots[i].rightGunX = projected.x + _flyRightGunX[tableIndex] - 0xd4;
+				_spaceShots[i].rightGunY = projected.y + _flyRightGunY[tableIndex] - 0x82;
+			} else {
+				_spaceShots[i].rightGunX = projected.x + 28;
+				_spaceShots[i].rightGunY = projected.y + 10;
+			}
 			_spaceShots[i].variant = _spaceShotDirection;
 			break;
 		}
@@ -975,34 +1017,6 @@ void InsaneRebel2::initLaserTexture(NutRenderer *nut, int spriteIdx) {
 
 	debug("Rebel2: Initialized laser texture %dx%d from sprite %d (xoff=%d yoff=%d src=%dx%d)",
 	      texWidth, texHeight, spriteIdx, srcXOff, srcYOff, srcWidth, srcHeight);
-
-	// Diagnostic: dump texture pixel stats to verify data is loaded correctly
-	if (_laserTexture.pixels && texWidth > 0 && texHeight > 0) {
-		int third = texWidth / 3;
-		int band1 = 0, band2 = 0, band3 = 0;
-		for (int row = 0; row < texHeight; row++) {
-			for (int col = 0; col < texWidth; col++) {
-				if (_laserTexture.pixels[row * texWidth + col] != 0) {
-					if (col < third)
-						band1++;
-					else if (col < third * 2)
-						band2++;
-					else
-						band3++;
-				}
-			}
-		}
-		debug("Rebel2: Texture non-zero pixels by band: [0-%d]=%d  [%d-%d]=%d  [%d-%d]=%d",
-			third - 1, band1, third, third * 2 - 1, band2, third * 2, texWidth - 1, band3);
-
-		// Dump first row hex (first 64 bytes)
-		Common::String hexRow;
-		int dumpLen = MIN(texWidth, (int16)64);
-		for (int col = 0; col < dumpLen; col++) {
-			hexRow += Common::String::format("%02x ", _laserTexture.pixels[col]);
-		}
-		debug("Rebel2: Texture row 0 (first %d): %s", dumpLen, hexRow.c_str());
-	}
 }
 
 // freeLaserTexture -- Emulates FUN_0040BBD1.
@@ -3052,9 +3066,9 @@ void InsaneRebel2::renderHandler7Ship(byte *renderBitmap, int pitch, int width,
 	if (spriteIndex >= numSprites)
 		spriteIndex = numSprites - 1;
 
-	// Simplified FUN_41C720-like transform to current render buffer coordinates.
-	int shipCenterX = (_flyShipScreenX - 0xd4) + _perspectiveX + 160 + _viewX;
-	int shipCenterY = (_flyShipScreenY - 0x82) + _perspectiveY + 100 + _viewY;
+	Common::Point shipDraw = getHandler7ShipDrawPoint();
+	int shipCenterX = shipDraw.x + 0xd4;
+	int shipCenterY = shipDraw.y + 0x82;
 
 	// FUN_40D836 lines 108-136: FLY002 proximity cues near corridor danger.
 	if (_flyLaserSprite && _flyLaserSprite->getNumChars() > 0) {
@@ -3103,11 +3117,8 @@ void InsaneRebel2::renderHandler7Ship(byte *renderBitmap, int pitch, int width,
 		}
 	}
 
-	// Center the sprite on the position
-	int spriteW = _flyShipSprite->getCharWidth(spriteIndex);
-	int spriteH = _flyShipSprite->getCharHeight(spriteIndex);
-	int drawX = shipCenterX - spriteW / 2;
-	int drawY = shipCenterY - spriteH / 2;
+	int drawX = shipDraw.x;
+	int drawY = shipDraw.y;
 
 	renderNutSprite(renderBitmap, pitch, width, height, drawX, drawY, _flyShipSprite, spriteIndex);
 
@@ -4035,8 +4046,12 @@ void InsaneRebel2::renderCrosshair(byte *renderBitmap, int pitch, int width, int
 	// Update target lock state and draw crosshair/reticle
 
 	// Target lock detection (DAT_00443676 equivalent)
-	Common::Point aimPos = getGameplayAimPoint();
-	Common::Point worldMousePos(aimPos.x + _viewX, aimPos.y + _viewY);
+	Common::Point aimPos = (_rebelHandler == 7) ? getHandler7ShotTargetPoint() : getGameplayAimPoint();
+	Common::Point worldMousePos = aimPos;
+	if (_rebelHandler != 7) {
+		worldMousePos.x += _viewX;
+		worldMousePos.y += _viewY;
+	}
 	bool targetLocked = false;
 
 	for (Common::List<enemy>::iterator it = _enemies.begin(); it != _enemies.end(); ++it) {
@@ -4083,8 +4098,12 @@ void InsaneRebel2::renderCrosshair(byte *renderBitmap, int pitch, int width, int
 		int ch = _smush_iconsNut->getCharHeight(reticleIndex);
 
 		// Calculate crosshair position
-		int crosshairX = aimPos.x - cw / 2 + _viewX;
-		int crosshairY = aimPos.y - ch / 2 + _viewY;
+		int crosshairX = aimPos.x - cw / 2;
+		int crosshairY = aimPos.y - ch / 2;
+		if (_rebelHandler != 7) {
+			crosshairX += _viewX;
+			crosshairY += _viewY;
+		}
 
 		// Handler 25 (0x19): Add view offset to crosshair position
 		// From FUN_41DB5E lines 198-199: X = DAT_00457914 + DAT_0045790c, Y = DAT_00457916 + DAT_0045790e
diff --git a/engines/scumm/insane/rebel2/runlevels.cpp b/engines/scumm/insane/rebel2/runlevels.cpp
index 24c88ce2394..ba3abf80298 100644
--- a/engines/scumm/insane/rebel2/runlevels.cpp
+++ b/engines/scumm/insane/rebel2/runlevels.cpp
@@ -312,6 +312,12 @@ void InsaneRebel2::resetHandler7FlightState() {
 	_corridorBottomY = 0x104;
 	_facingRight = false;
 	_spaceShotDirection = 0;
+	memset(_flyLeftGunX, 0, sizeof(_flyLeftGunX));
+	memset(_flyLeftGunY, 0, sizeof(_flyLeftGunY));
+	memset(_flyRightGunX, 0, sizeof(_flyRightGunX));
+	memset(_flyRightGunY, 0, sizeof(_flyRightGunY));
+	_flyLeftGunTableLoaded = false;
+	_flyRightGunTableLoaded = false;
 
 	memset(_velocityHistory, 0, sizeof(_velocityHistory));
 	memset(_windHistoryX, 0, sizeof(_windHistoryX));



From noreply at scummvm.org  Tue Jun  2 17:26:26 2026
From: noreply at scummvm.org (neuromancer)
Date: Tue,  2 Jun 2026 17:26:26 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 2e395838d9a5e9eb5f579e1ceb7bc4318149c408
Message-ID: <20260602172626.E43D5700573@ron.scummvm.net>

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

Summary:
2e395838d9 SCUMM: RA: Default framerate is 15fps


Commit: 2e395838d9a5e9eb5f579e1ceb7bc4318149c408
    https://github.com/scummvm/scummvm/commit/2e395838d9a5e9eb5f579e1ceb7bc4318149c408
Author: Manuel Lauss (manuel.lauss at gmail.com)
Date: 2026-06-02T19:26:22+02:00

Commit Message:
SCUMM: RA: Default framerate is 15fps

The default frame rate for all RA1 ANM and RA2 SAN files is 15fps.

Changed paths:
    engines/scumm/insane/rebel1/runlevels.cpp
    engines/scumm/insane/rebel2/levels.cpp
    engines/scumm/insane/rebel2/menu.cpp
    engines/scumm/insane/rebel2/runlevels.cpp


diff --git a/engines/scumm/insane/rebel1/runlevels.cpp b/engines/scumm/insane/rebel1/runlevels.cpp
index 1a61b7ff307..3623029abfb 100644
--- a/engines/scumm/insane/rebel1/runlevels.cpp
+++ b/engines/scumm/insane/rebel1/runlevels.cpp
@@ -159,7 +159,7 @@ void InsaneRebel1::playCinematic(const char *filename, int32 startFrame) {
 	splayer->setCurVideoFlags(0x28);  // Cinematic mode + buffer preserve
 	splayer->setFastForwardFromFrame(0);
 	splayer->setFastForwardToFrame(startFrame > 0 ? startFrame : 0);
-	splayer->play(filename, 12);
+	splayer->play(filename, 15);
 
 	// Level-title text is only meant for the intro cinematic that armed it.
 	// Clear it even when the movie ended through ESC, so it cannot leak into
@@ -1693,7 +1693,7 @@ void InsaneRebel1::releaseInteractiveVideoInput() {
 }
 
 void InsaneRebel1::playInteractiveVideoFile(const char *filename, int32 videoOffset, int32 videoStartFrame) {
-	_vm->_splayer->play(filename, 12, videoOffset, videoStartFrame);
+	_vm->_splayer->play(filename, 15, videoOffset, videoStartFrame);
 	restoreInteractiveVideoAudioState();
 	_interactiveVideoActive = false;
 }
diff --git a/engines/scumm/insane/rebel2/levels.cpp b/engines/scumm/insane/rebel2/levels.cpp
index ad3f32077be..63d49134bba 100644
--- a/engines/scumm/insane/rebel2/levels.cpp
+++ b/engines/scumm/insane/rebel2/levels.cpp
@@ -59,7 +59,7 @@ void InsaneRebel2::runGame() {
 
 	// Demo: just play the demo video and return
 	if (_vm->_game.features & GF_DEMO) {
-		splayer->play("OPEN/O_DEMO.SAN", 12);
+		splayer->play("OPEN/O_DEMO.SAN", 15);
 		return;
 	}
 
@@ -147,7 +147,7 @@ void InsaneRebel2::playIntroSequence() {
 	// Original: FUN_0041f4d0("OPEN/O_OPEN_A.SAN", 0x28, 0xffff, 0xffff, 0)
 	debug("Rebel2: Playing main intro (O_OPEN_A.SAN)");
 	splayer->setCurVideoFlags(0x28);
-	splayer->play("OPEN/O_OPEN_A.SAN", 12);
+	splayer->play("OPEN/O_OPEN_A.SAN", 15);
 
 	if (_vm->shouldQuit())
 		return;
@@ -157,7 +157,7 @@ void InsaneRebel2::playIntroSequence() {
 	// We play unconditionally (matches "Continue Intro" menu behavior)
 	debug("Rebel2: Playing additional intro (O_OPEN_B.SAN)");
 	splayer->setCurVideoFlags(0x28);
-	splayer->play("OPEN/O_OPEN_B.SAN", 12);
+	splayer->play("OPEN/O_OPEN_B.SAN", 15);
 }
 
 // playMissionBriefing -- Mission briefing screen (FUN_00415CF8).
@@ -167,7 +167,7 @@ void InsaneRebel2::playMissionBriefing() {
 
 	SmushPlayer *splayer = ((ScummEngine_v7 *)_vm)->_splayer;
 	splayer->setCurVideoFlags(0x08);  // Briefing mode flag
-	splayer->play("OPEN/O_LEVEL.SAN", 12);
+	splayer->play("OPEN/O_LEVEL.SAN", 15);
 }
 
 // playCinematic -- Play a cinematic/cutscene video.
@@ -179,7 +179,7 @@ void InsaneRebel2::playCinematic(const char *filename) {
 
 	SmushPlayer *splayer = ((ScummEngine_v7 *)_vm)->_splayer;
 	splayer->setCurVideoFlags(0x28);  // Cinematic mode + buffer preserve (0x20 | 0x08)
-	splayer->play(filename, 12);
+	splayer->play(filename, 15);
 }
 
 // playVideoWithText -- Video with progressive text overlay (FUN_004171c5).
@@ -201,7 +201,7 @@ void InsaneRebel2::playVideoWithText(const char *filename, int textID, int textX
 
 	SmushPlayer *splayer = ((ScummEngine_v7 *)_vm)->_splayer;
 	splayer->setCurVideoFlags(0x28);
-	splayer->play(filename, 12);
+	splayer->play(filename, 15);
 
 	_textOverlayActive = false;
 }
@@ -270,7 +270,7 @@ void InsaneRebel2::playLevelEnd(int levelId) {
 	SmushPlayer *splayer = ((ScummEngine_v7 *)_vm)->_splayer;
 	// Original: FUN_00417327 adds | 8, so flags = 0x20 | 0x08 = 0x28
 	splayer->setCurVideoFlags(0x28);
-	splayer->play(filename.c_str(), 12);
+	splayer->play(filename.c_str(), 15);
 }
 
 // playLevelRetry -- Retry prompt video (LEVXX/XXRETRY.SAN, FUN_00417168).
@@ -288,7 +288,7 @@ void InsaneRebel2::playLevelRetry(int levelId) {
 	SmushPlayer *splayer = ((ScummEngine_v7 *)_vm)->_splayer;
 	// Original: FUN_00417168 adds | 8, so flags = 0x20 | 0x08 = 0x28
 	splayer->setCurVideoFlags(0x28);
-	splayer->play(filename.c_str(), 12);
+	splayer->play(filename.c_str(), 15);
 }
 
 // playLevelGameOver -- Game over video (FUN_00417ab2).
@@ -306,7 +306,7 @@ void InsaneRebel2::playLevelGameOver(int levelId) {
 	SmushPlayer *splayer = ((ScummEngine_v7 *)_vm)->_splayer;
 	// Original: FUN_00417ab2 adds | 8, so flags = 0x20 | 0x08 = 0x28
 	splayer->setCurVideoFlags(0x28);
-	splayer->play(filename.c_str(), 12);
+	splayer->play(filename.c_str(), 15);
 }
 
 // playEndingSequence -- Finale + credits + epilogue (FUN_0041bbe8).
@@ -363,7 +363,7 @@ void InsaneRebel2::playCreditsSequence() {
 
 	SmushPlayer *splayer = ((ScummEngine_v7 *)_vm)->_splayer;
 	splayer->setCurVideoFlags(0x20);
-	splayer->play("OPEN/O_CREDIT.SAN", 12);
+	splayer->play("OPEN/O_CREDIT.SAN", 15);
 }
 
 void InsaneRebel2::centerGameplayAim() {
@@ -697,7 +697,7 @@ void InsaneRebel2::playLevelDeathVariant(int levelId, int phase, int frame) {
 	SmushPlayer *splayer = ((ScummEngine_v7 *)_vm)->_splayer;
 	// Original: FUN_00417168 adds | 8, so flags = 0x20 | 0x08 = 0x28
 	splayer->setCurVideoFlags(0x28);
-	splayer->play(filename.c_str(), 12);
+	splayer->play(filename.c_str(), 15);
 }
 
 // playLevelRetryVariant -- Phase-specific retry video.
@@ -723,7 +723,7 @@ void InsaneRebel2::playLevelRetryVariant(int levelId, int phase) {
 	SmushPlayer *splayer = ((ScummEngine_v7 *)_vm)->_splayer;
 	// Original: FUN_00417168 adds | 8, so flags = 0x20 | 0x08 = 0x28
 	splayer->setCurVideoFlags(0x28);
-	splayer->play(filename.c_str(), 12);
+	splayer->play(filename.c_str(), 15);
 }
 
 
diff --git a/engines/scumm/insane/rebel2/menu.cpp b/engines/scumm/insane/rebel2/menu.cpp
index f785cd7159c..83a5f178ebb 100644
--- a/engines/scumm/insane/rebel2/menu.cpp
+++ b/engines/scumm/insane/rebel2/menu.cpp
@@ -622,7 +622,7 @@ int InsaneRebel2::runMainMenu() {
 		// Play the menu video
 		// Input is processed in procPostRendering during playback
 		// When user confirms selection, _vm->_smushVideoShouldFinish is set
-		splayer->play(menuVideo.c_str(), 12);
+		splayer->play(menuVideo.c_str(), 15);
 
 		// Check for quit
 		if (_vm->shouldQuit()) {
@@ -678,9 +678,9 @@ int InsaneRebel2::runMainMenu() {
 			_menuInputActive = false;
 			// Play intro sequence again (O_OPEN_A/B)
 			splayer->setCurVideoFlags(0x20);
-			splayer->play("OPEN/O_OPEN_A.SAN", 12);
+			splayer->play("OPEN/O_OPEN_A.SAN", 15);
 			if (!_vm->shouldQuit()) {
-				splayer->play("OPEN/O_OPEN_B.SAN", 12);
+				splayer->play("OPEN/O_OPEN_B.SAN", 15);
 			}
 			// Restore menu state
 			_gameState = kStateMainMenu;
@@ -697,7 +697,7 @@ int InsaneRebel2::runMainMenu() {
 			_gameState = kStateCredits;
 			_menuInputActive = false;
 			splayer->setCurVideoFlags(0x20);
-			splayer->play("OPEN/O_CREDIT.SAN", 12);
+			splayer->play("OPEN/O_CREDIT.SAN", 15);
 			_gameState = kStateMainMenu;
 			_menuInputActive = true;
 			// Returns 1 in original -> stays at stage 1 (main menu)
@@ -780,7 +780,7 @@ int InsaneRebel2::runChapterSelect() {
 		splayer->setCurVideoFlags(0x28);
 
 		// Play O_LEVEL.SAN ??? preview thumbnails are rendered by FOBJ offset
-		splayer->play("OPEN/O_LEVEL.SAN", 12);
+		splayer->play("OPEN/O_LEVEL.SAN", 15);
 
 		if (_vm->shouldQuit()) {
 			setVirtualKeyboardVisible(false);
@@ -1233,7 +1233,7 @@ int InsaneRebel2::runLevelSelect() {
 
 		Common::String menuVideo = getRandomMenuVideo();
 		splayer->setCurVideoFlags(0x20);
-		splayer->play(menuVideo.c_str(), 12);
+		splayer->play(menuVideo.c_str(), 15);
 
 		if (_vm->shouldQuit()) {
 			setVirtualKeyboardVisible(false);
@@ -1732,7 +1732,7 @@ void InsaneRebel2::showTopPilots() {
 
 	Common::String menuVideo = getRandomMenuVideo();
 	splayer->setCurVideoFlags(0x20);
-	splayer->play(menuVideo.c_str(), 12);
+	splayer->play(menuVideo.c_str(), 15);
 
 	_gameState = kStateMainMenu;
 	_menuInputActive = true;
@@ -1839,7 +1839,7 @@ void InsaneRebel2::showOptionsMenu() {
 
 		Common::String menuVideo = getRandomMenuVideo();
 		splayer->setCurVideoFlags(0x20);
-		splayer->play(menuVideo.c_str(), 12);
+		splayer->play(menuVideo.c_str(), 15);
 	}
 
 	_gameState = kStateMainMenu;
diff --git a/engines/scumm/insane/rebel2/runlevels.cpp b/engines/scumm/insane/rebel2/runlevels.cpp
index ba3abf80298..a71a39285c5 100644
--- a/engines/scumm/insane/rebel2/runlevels.cpp
+++ b/engines/scumm/insane/rebel2/runlevels.cpp
@@ -198,7 +198,7 @@ bool InsaneRebel2::playLevelSegment(const char *filename, uint16 flags, bool rec
 		centerGameplayAim();
 
 	splayer->setCurVideoFlags(flags);
-	splayer->play(filename, 12);
+	splayer->play(filename, 15);
 	if (recordFrame)
 		_deathFrame = splayer->_frame;
 	return !_vm->shouldQuit();



From noreply at scummvm.org  Tue Jun  2 17:56:27 2026
From: noreply at scummvm.org (bluegr)
Date: Tue,  2 Jun 2026 17:56:27 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 ac54d7b9b7a8b56d82eccf021d9ac4ff61769828
Message-ID: <20260602175627.36ADC700573@ron.scummvm.net>

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

Summary:
ac54d7b9b7 NANCY: Add detection for Nancy 10 variants


Commit: ac54d7b9b7a8b56d82eccf021d9ac4ff61769828
    https://github.com/scummvm/scummvm/commit/ac54d7b9b7a8b56d82eccf021d9ac4ff61769828
Author: eientei95 (einstein95 at users.noreply.github.com)
Date: 2026-06-02T20:56:23+03:00

Commit Message:
NANCY: Add detection for Nancy 10 variants

Changed paths:
    engines/nancy/detection_tables.h


diff --git a/engines/nancy/detection_tables.h b/engines/nancy/detection_tables.h
index e5285668c7d..01d7935a32c 100644
--- a/engines/nancy/detection_tables.h
+++ b/engines/nancy/detection_tables.h
@@ -727,6 +727,39 @@ static const NancyGameDescription gameDescriptions[] = {
 		},
 		kGameTypeNancy10
 	},
+	{ // v1.01, MD5 by eientei95
+		{
+			"nancy10", nullptr,
+			{
+				{ "data1.hdr", 0, "f8a023c14d04ffdae08f1a0bb8a1fd2a", 394956 },
+				{ "data1.cab", 0, "829ab79c4a5c49f918b7c22186eb7365", 7510894 },
+				{ "data2.cab", 0, "a4af0fb92bd2d91d5ec7b2f9233a7355", 150997862 },
+				{ "ciftree.dat", 0, "270133c5e89b8538caef08041598d049", 29413095 }, // ciftree is outside of cabfiles
+				AD_LISTEND
+			},
+			Common::EN_ANY,
+			Common::kPlatformWindows,
+			ADGF_UNSTABLE | ADGF_DROPPLATFORM | GF_COMPRESSED,
+			NANCY8_GUIOPTIONS
+		},
+		kGameTypeNancy10
+	},
+	{ // v1.01 from Nancy Drew: Ultimate Dare, MD5 by eientei95
+		{
+			"nancy10", nullptr,
+			{
+				{ "data1.hdr", 0, "0f9223c701af38398f101e4c29c71d27", 392284 },
+				{ "data1.cab", 0, "752ccf625189520d8221eafd5c896845", 7513583 },
+				{ "data2.cab", 0, "8b481aac1b07838bdb4d578a8aeb9676", 574477161 },
+				AD_LISTEND
+			},
+			Common::EN_ANY,
+			Common::kPlatformWindows,
+			ADGF_UNSTABLE | ADGF_DROPPLATFORM,
+			NANCY8_GUIOPTIONS
+		},
+		kGameTypeNancy10
+	},
 	{
 		{
 			"nancy11", nullptr,



From noreply at scummvm.org  Tue Jun  2 18:30:49 2026
From: noreply at scummvm.org (neuromancer)
Date: Tue,  2 Jun 2026 18:30:49 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 f6426933dcd09b5d36dcd2e7218d143c8fc2ac69
Message-ID: <20260602183049.9F490700573@ron.scummvm.net>

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

Summary:
f6426933dc SCUMM: RA: support female voice output


Commit: f6426933dcd09b5d36dcd2e7218d143c8fc2ac69
    https://github.com/scummvm/scummvm/commit/f6426933dcd09b5d36dcd2e7218d143c8fc2ac69
Author: Manuel Lauss (manuel.lauss at gmail.com)
Date: 2026-06-02T20:30:45+02:00

Commit Message:
SCUMM: RA: support female voice output

smushAudioTable[4] (sou_hooks[] in the original source) indicates which
gender voice track to play.

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 4a88a5e9b92..af7cb44fdaa 100644
--- a/engines/scumm/insane/rebel1/menu.cpp
+++ b/engines/scumm/insane/rebel1/menu.cpp
@@ -1224,6 +1224,7 @@ void InsaneRebel1::runOptionsMenu() {
 				return;
 			case 1: // Toggle Rookie One gender
 				_optRookieOneFemale = !_optRookieOneFemale;
+				_player->_smushAudioTable[4] = _optRookieOneFemale ? 1 : 0;
 				break;
 			case 2: // Toggle music
 				_optMusicEnabled = !_optMusicEnabled;



From noreply at scummvm.org  Tue Jun  2 21:20:42 2026
From: noreply at scummvm.org (neuromancer)
Date: Tue,  2 Jun 2026 21:20:42 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 97329525d686722b34136e391dc7503e1745c35d
Message-ID: <20260602212042.BF241700573@ron.scummvm.net>

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

Summary:
97329525d6 SCUMM: RA2: avoid crash in level 11


Commit: 97329525d686722b34136e391dc7503e1745c35d
    https://github.com/scummvm/scummvm/commit/97329525d686722b34136e391dc7503e1745c35d
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-02T23:20:28+02:00

Commit Message:
SCUMM: RA2: avoid crash in level 11

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


diff --git a/engines/scumm/insane/rebel2/iact.cpp b/engines/scumm/insane/rebel2/iact.cpp
index fef48209da6..af30f749a81 100644
--- a/engines/scumm/insane/rebel2/iact.cpp
+++ b/engines/scumm/insane/rebel2/iact.cpp
@@ -2358,16 +2358,19 @@ bool InsaneRebel2::loadLevel2Background(byte *animData, int32 size, byte *render
 			debug("Rebel2 loadLevel2Background: Found FOBJ: codec=%d pos=(%d,%d) size=%dx%d",
 				codec, fobjX, fobjY, fobjW, fobjH);
 
-			// Decode codec 3 (RLE) into background buffer.
-			// Use smushDecodeRLEOpaque to write ALL colors including color 0 (black).
+			// Decode codec 3 (RLE) into the original 320x200 background buffer.
+			// FUN_0041CADB/FUN_00401234 draw these resources into a fixed buffer
+			// and clip them there; Level 11 backgrounds extend past the right edge.
 			if (codec == 3 && fobjX >= 0 && fobjY >= 0 && fobjW > 0 && fobjH > 0 &&
-					fobjX + fobjW <= 320 && fobjY + fobjH <= 200 && stream.pos() < subDataEnd) {
+					fobjX < 320 && fobjY < 200 && stream.pos() < subDataEnd) {
+				int drawW = MIN<int>(fobjW, 320 - fobjX);
+				int drawH = MIN<int>(fobjH, 200 - fobjY);
 				const byte *rleData = animData + stream.pos();
-				smushDecodeRLEOpaque(_level2Background, rleData, fobjX, fobjY, fobjW, fobjH, 320,
+				smushDecodeRLEOpaque(_level2Background, rleData, fobjX, fobjY, drawW, drawH, 320,
 					(int)(subDataEnd - stream.pos()));
 
-				debug("Rebel2 loadLevel2Background: Decoded Level 2 background (%dx%d at %d,%d)",
-					fobjW, fobjH, fobjX, fobjY);
+				debug("Rebel2 loadLevel2Background: Decoded Level 2 background (%dx%d at %d,%d, clipped to %dx%d)",
+					fobjW, fobjH, fobjX, fobjY, drawW, drawH);
 				_level2BackgroundLoaded = true;
 				foundBackground = true;
 
diff --git a/engines/scumm/smush/rebel/smush_player_ra2.cpp b/engines/scumm/smush/rebel/smush_player_ra2.cpp
index 5f22d5dc428..e24b8f56d3c 100644
--- a/engines/scumm/smush/rebel/smush_player_ra2.cpp
+++ b/engines/scumm/smush/rebel/smush_player_ra2.cpp
@@ -115,6 +115,11 @@ void SmushPlayerRebel2::initGameVideoState() {
 	// Since play() resets _palDirtyMin/Max, the palette would never be pushed otherwise.
 	setDirtyColors(0, 255);
 
+	// SmushPlayer::init() resets _dst to the virtual screen. Use matching
+	// screen dimensions until AHDR/FOBJ selects a larger gameplay surface.
+	_width = _vm->_screenWidth;
+	_height = _vm->_screenHeight;
+
 	// Handle background preservation between videos:
 	// - Cinematic videos (flags 0x20) clear the buffer for a fresh start
 	// - Gameplay videos (flags 0x08) preserve the existing screen content
@@ -441,10 +446,9 @@ void SmushPlayerRebel2::ra2HandleDeltaPalette(int32 subSize, Common::SeekableRea
 	setDirtyColors(0, 255);
 }
 
-/**
- * RA2-specific handleAnimHeader fixup: when AHDR reports 0x0 dimensions,
- * use screen dimensions instead.
- */
+// RA2-specific AHDR handling. AHDR metadata can describe the original SMUSH
+// backing bitmap, but active low-res gameplay dimensions are selected later by
+// IACT/FOBJ state; keep the virtual-screen pitch safe until then.
 bool SmushPlayerRebel2::handleGameAnimHeader(byte *headerContent) {
 	int width = READ_LE_UINT16(&headerContent[4]);
 	int height = READ_LE_UINT16(&headerContent[6]);
@@ -453,10 +457,22 @@ bool SmushPlayerRebel2::handleGameAnimHeader(byte *headerContent) {
 		_width = _vm->_screenWidth;
 		_height = _vm->_screenHeight;
 		debugC(DEBUG_SMUSH, "SmushPlayerRebel2::handleGameAnimHeader: RA2 AHDR has 0x0 dims - using screen size %dx%d", _width, _height);
+	} else if (width != _vm->_screenWidth || height != _vm->_screenHeight) {
+		// FUN_00407FCB/FUN_0040C3CC update the active frame descriptor from
+		// IACT opcode 6. In ScummVM's low-res path the descriptor remains
+		// 320x200 and perspective is applied through FUN_00424510-equivalent
+		// FOBJ offsets. Do not let AHDR alone change pitch while _dst still
+		// points at the virtual screen; FOBJ selection will allocate a larger
+		// surface when an actual oversized frame object is decoded.
+		_width = _vm->_screenWidth;
+		_height = _vm->_screenHeight;
+		debugC(DEBUG_SMUSH, "SmushPlayerRebel2::handleGameAnimHeader: RA2 AHDR %dx%d - using screen size until FOBJ selects a surface",
+			width, height);
 	} else {
 		_width = width;
 		_height = height;
 	}
+
 	return true;
 }
 
@@ -757,9 +773,24 @@ bool SmushPlayerRebel2::handleGameFrameBufferSelect(int codec, int width, int he
 
 bool SmushPlayerRebel2::handleGameDimensionOverride(int codec, int width, int height) {
 	if ((height != _vm->_screenHeight) || (width != _vm->_screenWidth)) {
-		// RA2: preserve _width/_height set during buffer allocation
-		return true;
+		if (_insane != nullptr) {
+			InsaneRebel2 *rebel2 = static_cast<InsaneRebel2 *>(_insane);
+			if (rebel2->getHandler() != 0) {
+				// RA2 gameplay preserves the dimensions selected by the
+				// current frame target instead of the individual FOBJ.
+				return true;
+			}
+		}
+
+		if (_dst == _specialBuffer && _specialBuffer != nullptr) {
+			return true;
+		}
+
+		// Handler-zero videos are menus/cinematics. Let the base player restore
+		// normal screen dimensions so menu overlays do not inherit gameplay pitch.
+		return false;
 	}
+
 	return false;
 }
 



From noreply at scummvm.org  Wed Jun  3 01:49:03 2026
From: noreply at scummvm.org (dreammaster)
Date: Wed,  3 Jun 2026 01:49:03 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 6ce747fcd673c7726d0b62bb627b6ecc808f5061
Message-ID: <20260603014903.DC8CD700591@ron.scummvm.net>

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:
b010511697 MADS: DRAGONSPHERE: Fix leaving Hightower mountaintop
4ac77c3df6 MADS: DRAGONSPHERE: Implement conv code to suppress dialogs for room 504 poem
6ce747fcd6 MADS: DRAGONSPHERE: Fixed ending of rose recitation


Commit: b010511697a780a3a990f35dcb906157b260eaa3
    https://github.com/scummvm/scummvm/commit/b010511697a780a3a990f35dcb906157b260eaa3
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-06-03T11:48:49+10:00

Commit Message:
MADS: DRAGONSPHERE: Fix leaving Hightower mountaintop

Changed paths:
    engines/mads/madsv2/dragonsphere/mads/words.h


diff --git a/engines/mads/madsv2/dragonsphere/mads/words.h b/engines/mads/madsv2/dragonsphere/mads/words.h
index a28f8c38afa..8b2c963cb2d 100644
--- a/engines/mads/madsv2/dragonsphere/mads/words.h
+++ b/engines/mads/madsv2/dragonsphere/mads/words.h
@@ -478,7 +478,7 @@ enum {
 	words_Slathan_ni_Patan     = 453,
 	words_sick                 = 454,
 	words_path_around_tower    = 455,
-	words_path_to_Hightower    = 456,
+	words_path_to_Hightower1   = 456,
 	words_spirit_plane         = 457,
 	words_spirit_tree          = 458,
 	words_walk                 = 459,
@@ -593,6 +593,7 @@ enum {
 	words_robe                 = 569,
 	words_cedar_chest          = 570,
 	words_Dragonsphere         = 571,
+	words_path_to_Hightower    = 572,
 	words_Caliph               = 573,
 };
 


Commit: 4ac77c3df6545c8854efa8a47a4a714667139e0b
    https://github.com/scummvm/scummvm/commit/4ac77c3df6545c8854efa8a47a4a714667139e0b
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-06-03T11:48:49+10:00

Commit Message:
MADS: DRAGONSPHERE: Implement conv code to suppress dialogs for room 504 poem

Changed paths:
    engines/mads/madsv2/core/conv.cpp


diff --git a/engines/mads/madsv2/core/conv.cpp b/engines/mads/madsv2/core/conv.cpp
index dcb59344a44..5b280c99ada 100644
--- a/engines/mads/madsv2/core/conv.cpp
+++ b/engines/mads/madsv2/core/conv.cpp
@@ -581,45 +581,58 @@ static void conv_generate_text(Conv *convIn, ConvData * /*convData*/,
 	int  person = conv_control.person_speaking;
 	char textBuf[512];
 	Box *savedBox;
+	bool showBox = conv_show_boxes;
 
-	// Redirect popup operations to the conversation-private box so that the
-	// bounds recorded by popup_draw can be used later by conv_purge_any_popup.
-	savedBox = box;
-	box = &conv_box;
+	// TODO: Hook this up to ScummVM subtitles - original disabled reply boxes if in voice only mode
+	//if (speech_system_active && speech_on && speechCount > 0 && !show_subtitles) showBox = false;
 
-	// Size and position the popup from the current speaker's slot data.
-	int horiz_pieces = popup_estimate_pieces(conv_control.width[person]);
-	popup_create(horiz_pieces,
-	             conv_control.x[person],
-	             conv_control.y[person]);
-
-	// Attach the speaker portrait icon if a series was loaded for this slot.
-	// The icon_id is the 1-based frame stored in speaker_frame.
-	// center=0: left-aligned within the popup box.
-	if (conv_control.speaker_series[person] >= 0) {
-		popup_add_icon(series_list[conv_control.speaker_series[person]],
-		               conv_control.speaker_frame[person],
-		               0);
-	}
+	if (showBox) {
+		// Redirect popup operations to the conversation-private box so that the
+		// bounds recorded by popup_draw can be used later by conv_purge_any_popup.
+		savedBox = box;
+		box = &conv_box;
+
+		// Size and position the popup from the current speaker's slot data.
+		int horiz_pieces = popup_estimate_pieces(conv_control.width[person]);
+		popup_create(horiz_pieces,
+			conv_control.x[person],
+			conv_control.y[person]);
+
+		// Attach the speaker portrait icon if a series was loaded for this slot.
+		// The icon_id is the 1-based frame stored in speaker_frame.
+		// center=0: left-aligned within the popup box.
+		if (conv_control.speaker_series[person] >= 0) {
+			popup_add_icon(series_list[conv_control.speaker_series[person]],
+				conv_control.speaker_frame[person],
+				0);
+		}
 
-	// Copy the dialog string to a local mutable buffer, strip trailing
-	// whitespace (the raw text pool can have padding bytes), then write it.
-	Common::strlcpy(textBuf, conv_string(convIn, textIdx), sizeof(textBuf));
-	string_trim(textBuf);
-	popup_write_string(textBuf);
+		// Copy the dialog string to a local mutable buffer, strip trailing
+		// whitespace (the raw text pool can have padding bytes), then write it.
+		Common::strlcpy(textBuf, conv_string(convIn, textIdx), sizeof(textBuf));
+		string_trim(textBuf);
+		popup_write_string(textBuf);
 
-	// Render the popup, saving the underlying screen region.
-	popup_draw(true, true);
+		// Render the popup, saving the underlying screen region.
+		popup_draw(true, true);
 
-	// Restore the caller's box and record that a conversation popup is live.
-	box = savedBox;
+		// Restore the caller's box
+		box = savedBox;
+	}
+
+	// Record that a conversation popup is live.
 	conv_control.popup_is_up  = -1;
-	conv_control.popup_clock  = kernel.clock + conv_control.popup_duration;
 
 	// Play the associated speech audio when the speech system is on.
 	if (speech_system_active && speech_on && speechCount > 0) {
 		speech_play(convIn->speech_file, speechList[0]);
 	}
+
+	if (showBox)
+		conv_control.popup_clock = kernel.clock + conv_control.popup_duration;
+	else
+		// TODO: If speech is allowed with no box showing, this needs to allow time for the speech duration
+		conv_control.popup_clock = kernel.clock;
 }
 
 static int conv_next_node() {
@@ -1323,43 +1336,63 @@ static void conv_generate_message(Conv *convIn, ConvData *convData,
 	int messageId;
 	int lineStart, lineCount;
 	char tempString[256];
+	bool showBox = conv_show_boxes;
+
+	// TODO: Hook this up to ScummVM subtitles - original disabled reply boxes if in voice only mode
+	//if (speech_system_active && speech_on && speechCount > 0 && !show_subtitles) showBox = false;
 
-	if (msgListSize != 0) {
-		personSpeaking = conv_control.person_speaking;
-		if (!popup_create(popup_estimate_pieces(conv_control.width[personSpeaking]),
+	if (showBox) {
+		if (msgListSize != 0) {
+			personSpeaking = conv_control.person_speaking;
+			if (!popup_create(popup_estimate_pieces(conv_control.width[personSpeaking]),
 				conv_control.x[personSpeaking], conv_control.y[personSpeaking])) {
-			if (conv_control.speaker_series[personSpeaking] >= 0) {
-				popup_add_icon(series_list[conv_control.speaker_series[personSpeaking]],
-					conv_control.speaker_frame[personSpeaking], 0);
-			}
+				if (conv_control.speaker_series[personSpeaking] >= 0) {
+					popup_add_icon(series_list[conv_control.speaker_series[personSpeaking]],
+						conv_control.speaker_frame[personSpeaking], 0);
+				}
 
-			for (int msgIndex = 0; msgIndex < msgListSize; ++msgIndex) {
-				messageId = msgList[msgIndex];
-				lineStart = convIn->messages[messageId].lineStart;
-				lineCount = convIn->messages[messageId].lineCount;
+				for (int msgIndex = 0; msgIndex < msgListSize; ++msgIndex) {
+					messageId = msgList[msgIndex];
+					lineStart = convIn->messages[messageId].lineStart;
+					lineCount = convIn->messages[messageId].lineCount;
 
-				for (int lineCtr = 0; lineCtr < lineCount; ++lineCtr) {
-					Common::strcpy_s(tempString, conv_string(convIn, lineStart + lineCtr));
-					string_trim(tempString);
-					popup_write_string(tempString);
+					for (int lineCtr = 0; lineCtr < lineCount; ++lineCtr) {
+						Common::strcpy_s(tempString, conv_string(convIn, lineStart + lineCtr));
+						string_trim(tempString);
+						popup_write_string(tempString);
+					}
 				}
-			}
 
-			popup_next_line();
-			if (!popup_draw(-1, -1)) {
-				conv_control.popup_is_up = -1;
-				conv_control.popup_clock = kernel.clock + conv_control.popup_duration;
+				popup_next_line();
+				if (!popup_draw(-1, -1)) {
+					conv_control.popup_is_up = -1;
+					conv_control.popup_clock = kernel.clock + conv_control.popup_duration;
 
-				if (speech_system_active && speech_on) {
-					if (voiceListSize != 0) {
-						speech_play(convIn->speech_file, *voiceList);
+					if (speech_system_active && speech_on) {
+						if (voiceListSize != 0) {
+							speech_play(convIn->speech_file, *voiceList);
+						}
 					}
 				}
 			}
 		}
+
+		box = priorBox;
 	}
 
-	box = priorBox;
+	// Record that a conversation popup is live.
+	conv_control.popup_is_up = -1;
+
+	// Play the associated speech audio when the speech system is on.
+	if (speech_system_active && speech_on && voiceListSize > 0) {
+		speech_play(convIn->speech_file, voiceList[0]);
+	}
+
+	if (showBox)
+		conv_control.popup_clock = kernel.clock + conv_control.popup_duration;
+	else
+		// TODO: If speech is allowed with no box showing, this needs to allow time for the speech duration
+		conv_control.popup_clock = kernel.clock;
 }
 
 // ---------------------------------------------------------------------------


Commit: 6ce747fcd673c7726d0b62bb627b6ecc808f5061
    https://github.com/scummvm/scummvm/commit/6ce747fcd673c7726d0b62bb627b6ecc808f5061
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-06-03T11:48:50+10:00

Commit Message:
MADS: DRAGONSPHERE: Fixed ending of rose recitation

Changed paths:
    engines/mads/madsv2/dragonsphere/mads/conv.h
    engines/mads/madsv2/dragonsphere/rooms/room504.cpp


diff --git a/engines/mads/madsv2/dragonsphere/mads/conv.h b/engines/mads/madsv2/dragonsphere/mads/conv.h
index 46ef64ef0b4..173e1cc728f 100644
--- a/engines/mads/madsv2/dragonsphere/mads/conv.h
+++ b/engines/mads/madsv2/dragonsphere/mads/conv.h
@@ -401,7 +401,7 @@ enum {
 	conv042_seventeen_restart= 89,
 	conv042_seventeen_abort  = 90,
 
-	conv042_exit_b_b         = 93,
+	conv042_exit_b_b         = 92,
 	conv042_later_b_b        = 94,
 	conv042_whoops_b_b       = 96,
 };
diff --git a/engines/mads/madsv2/dragonsphere/rooms/room504.cpp b/engines/mads/madsv2/dragonsphere/rooms/room504.cpp
index a174bb1c9bd..88f7ec4bcdc 100644
--- a/engines/mads/madsv2/dragonsphere/rooms/room504.cpp
+++ b/engines/mads/madsv2/dragonsphere/rooms/room504.cpp
@@ -1360,12 +1360,13 @@ static void process_conv_poem() {
 		conv_abort();
 		me_trig_flag = true;
 
-		if ((game.difficulty == EASY_MODE && local->got_it == 14) ||
-		    (local->got_it == 17)) {
+		if ((game.difficulty == EASY_MODE && local->got_it == 14) || (local->got_it == 17)) {
+			// Successfully completed the poem
 			kernel_timing_trigger(HALF_SECOND, ROOM_504_END_POEM);
 			global[player_score] += 8;
 
 		} else {
+			// Player screwed it up
 			local->working_on_line = 1;
 			local->line_1[0] = '\0';
 			local->line_2[0] = '\0';



From noreply at scummvm.org  Wed Jun  3 02:26:47 2026
From: noreply at scummvm.org (dreammaster)
Date: Wed,  3 Jun 2026 02:26:47 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 d20b7e1a5acb7ce1a7ef00bada635da8897a53f8
Message-ID: <20260603022647.9FD4E700591@ron.scummvm.net>

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

Summary:
d20b7e1a5a MADS: DRAGONSPHERE: Conv fixes for healing Llania


Commit: d20b7e1a5acb7ce1a7ef00bada635da8897a53f8
    https://github.com/scummvm/scummvm/commit/d20b7e1a5acb7ce1a7ef00bada635da8897a53f8
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-06-03T12:26:40+10:00

Commit Message:
MADS: DRAGONSPHERE: Conv fixes for healing Llania

Changed paths:
    engines/mads/madsv2/core/conv.cpp


diff --git a/engines/mads/madsv2/core/conv.cpp b/engines/mads/madsv2/core/conv.cpp
index 5b280c99ada..13e327046bb 100644
--- a/engines/mads/madsv2/core/conv.cpp
+++ b/engines/mads/madsv2/core/conv.cpp
@@ -583,6 +583,8 @@ static void conv_generate_text(Conv *convIn, ConvData * /*convData*/,
 	Box *savedBox;
 	bool showBox = conv_show_boxes;
 
+	conv_control.has_text = true;
+
 	// TODO: Hook this up to ScummVM subtitles - original disabled reply boxes if in voice only mode
 	//if (speech_system_active && speech_on && speechCount > 0 && !show_subtitles) showBox = false;
 
@@ -1338,6 +1340,10 @@ static void conv_generate_message(Conv *convIn, ConvData *convData,
 	char tempString[256];
 	bool showBox = conv_show_boxes;
 
+	conv_control.has_text = false;
+	if (msgListSize == 0)
+		goto done;
+
 	// TODO: Hook this up to ScummVM subtitles - original disabled reply boxes if in voice only mode
 	//if (speech_system_active && speech_on && speechCount > 0 && !show_subtitles) showBox = false;
 
@@ -1376,8 +1382,6 @@ static void conv_generate_message(Conv *convIn, ConvData *convData,
 				}
 			}
 		}
-
-		box = priorBox;
 	}
 
 	// Record that a conversation popup is live.
@@ -1393,6 +1397,9 @@ static void conv_generate_message(Conv *convIn, ConvData *convData,
 	else
 		// TODO: If speech is allowed with no box showing, this needs to allow time for the speech duration
 		conv_control.popup_clock = kernel.clock;
+
+done:
+	box = priorBox;
 }
 
 // ---------------------------------------------------------------------------



From noreply at scummvm.org  Wed Jun  3 07:57:26 2026
From: noreply at scummvm.org (neuromancer)
Date: Wed,  3 Jun 2026 07:57:26 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 3dc821daaecb64a36ee648daafb3170f0cd750ff
Message-ID: <20260603075726.D6B5C700591@ron.scummvm.net>

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:
651c56dba8 SCUMM: RA1: better collision/shoot detection in level 1 part 2
87bf21fc04 SCUMM: RA2: fixed corruption in L1 -> L2
3dc821daae SCUMM: RA2: implement arrow hints to avoid collisions


Commit: 651c56dba8d022cad40c19d75f69ef6de4c68997
    https://github.com/scummvm/scummvm/commit/651c56dba8d022cad40c19d75f69ef6de4c68997
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-03T08:41:45+02:00

Commit Message:
SCUMM: RA1: better collision/shoot detection in level 1 part 2

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


diff --git a/engines/scumm/insane/rebel1/iact.cpp b/engines/scumm/insane/rebel1/iact.cpp
index b5d28d028d7..fb8e22f4b00 100644
--- a/engines/scumm/insane/rebel1/iact.cpp
+++ b/engines/scumm/insane/rebel1/iact.cpp
@@ -102,6 +102,21 @@ inline bool ra1DispatcherHudOnlyWhenDisabled(uint32 opcode) {
 	}
 }
 
+bool ra1TargetCursorUsesProjection(uint16 opcode) {
+	switch (opcode) {
+	// FOBJ target chunks can precede the paired 0x0A GAME chunk, leaving 0x08
+	// as the active opcode in turret sections such as L1PLAY2.
+	case 0x08:
+	case 0x09:
+	case 0x0A:
+	case 0x0B:
+	case 0x1A:
+		return true;
+	default:
+		return false;
+	}
+}
+
 inline bool isLevel2DamageLatch(uint16 code) {
 	switch (code) {
 	case 0x0003:
@@ -1108,8 +1123,9 @@ void InsaneRebel1::updateShipPhysics() {
 
 	// FUN_1DEB5 updates the curve table via FUN_22549 after SetCameraOffset.
 	// The full DOS path blends a few roll-history terms; use the current roll
-	// accumulator so side-looking still bends the gameplay projection.
-	rebuildProjectionTable(CLIP<int16>((int16)(-(_rollAccum >> 7)), -0x1A, 0x1A), 0x1A);
+	// accumulator so side-looking still bends the gameplay projection. DOS
+	// negates before the arithmetic shift.
+	rebuildProjectionTable((int16)CLIP<int32>((-_rollAccum) >> 7, -0x1A, 0x1A), 0x1A);
 
 	// --- Step 8: Direction sprite index (FUN_1DEB5 LAB_1e23e) ---
 	// Horizontal component from _74CA (rollAccum):
@@ -1272,9 +1288,9 @@ void InsaneRebel1::updateTurretShipDirection(int16 offsetY) {
 
 void InsaneRebel1::getCollisionShipCenter(int16 &x, int16 &y) const {
 	// Original 0x0D/0x0E collision compares script zones transformed by
-	// FUN_2248C against the source-buffer ship center (base center +
-	// g_shipOffset). Keep this in the same 384x242 space; the final viewport
-	// crop is only a presentation step.
+	// FUN_223FE against the gameplay-window ship center (base center +
+	// g_shipOffset). This is DOS screen space; render overlays add the viewport
+	// offset separately when drawing into ScummVM's larger source buffer.
 	//
 	// In Level 1 part 2, HandleGameOp0A_TurretVariant reuses _shipPos for the
 	// targeting cursor, so collision must read the movement accumulator instead.
@@ -1409,7 +1425,8 @@ void InsaneRebel1::updateTurretPhysics() {
 
 	// FUN_1E6A7 rebuilds the side-look curve with a shallower table than the
 	// main flight handler, derived directly from roll.
-	rebuildProjectionTable((int16)(-(_rollAccum >> 9)), 0x0D);
+	// DOS negates before the arithmetic shift.
+	rebuildProjectionTable((int16)((-_rollAccum) >> 9), 0x0D);
 
 	// Regeneration + survival bonus via FUN_1BB0E call in this path.
 	if ((_frameCounter & 0x1F) == 0) {
@@ -2090,10 +2107,10 @@ void InsaneRebel1::handleGameOpcode0DCorridor(int32 subSize, Common::SeekableRea
 
 	int16 centerX = corridorLeft + corridorWidth / 2;
 	int16 centerY = corridorTop + corridorHeight / 2;
-	// DOS FUN_1C54D calls FUN_2248C here, which adds the current
-	// camera offset to the scripted rectangle center before testing it
-	// against the source-buffer ship center.
-	unprojectGameplayPoint(centerX, centerY);
+	// DOS FUN_1C54D calls FUN_223FE here, which projects the scripted
+	// rectangle center into gameplay-window space before testing it against the
+	// ship center.
+	projectGameplayPoint(centerX, centerY);
 
 	_corridorLeftX = centerX - corridorWidth / 2;
 	_corridorTopY = centerY - corridorHeight / 2;
@@ -2165,8 +2182,8 @@ void InsaneRebel1::handleGameOpcode0EZone(int32 subSize, Common::SeekableReadStr
 
 	int16 centerX = zoneLeft + zoneWidth / 2;
 	int16 centerY = zoneTop + zoneHeight / 2;
-	// Same transform as opcode 0x0D/FUN_1C54D.
-	unprojectGameplayPoint(centerX, centerY);
+	// Same gameplay-window FUN_223FE transform as opcode 0x0D/FUN_1C54D.
+	projectGameplayPoint(centerX, centerY);
 
 	zoneLeft = centerX - zoneWidth / 2;
 	zoneTop = centerY - zoneHeight / 2;
@@ -2410,16 +2427,17 @@ void InsaneRebel1::processShot() {
 }
 
 // checkTargetHit ??? FUN_1C0EF (0x1C0EF). AABB target detection with snap tolerance.
-// The original compares target bounds against the cursor after UnprojectScreenPoint()
-// because g_shipPos is a screen-space cursor. Most handlers in this port draw the
-// cursor into the 384x242 source buffer before the viewport crop, so their cursor is
-// already in the same target space as raw FOBJ bounds. Opcode 0x0B remains screen-space
-// and still needs the original project/unproject conversion.
+// The original compares raw FOBJ bounds against the cursor after
+// UnprojectScreenPoint(). Keep that separate from 0x0D/0x0E collision, which projects
+// zones into gameplay-window screen space before comparing against the ship center.
 void InsaneRebel1::checkTargetHit(int16 targetIdx, int16 left, int16 top, int16 right, int16 bottom) {
 	int16 snap = _tuning.snap;
-	const bool screenSpaceCursor = (getEffectiveGameOpcode() == 0x0B);
-	int16 curX = getGameplayCursorX();
-	int16 curY = getGameplayCursorY();
+	const uint16 effectiveOpcode = getEffectiveGameOpcode();
+	const bool screenSpaceCursor = ra1TargetCursorUsesProjection(effectiveOpcode);
+	const int16 screenCursorX = getGameplayCursorX();
+	const int16 screenCursorY = getGameplayCursorY();
+	int16 curX = screenCursorX;
+	int16 curY = screenCursorY;
 	if (screenSpaceCursor)
 		unprojectGameplayPoint(curX, curY);
 	const int slot = _targetCount;
@@ -2447,6 +2465,11 @@ void InsaneRebel1::checkTargetHit(int16 targetIdx, int16 left, int16 top, int16
 		if (slot < kMaxTargetBoxes)
 			_targetBoxVariant[slot] = CLIP<int16>((int16)(_targetBoxVariant[slot] + 3), 0, 5);
 
+		debugC(DEBUG_INSANE, "RA1 target near: opcode=0x%02x target=%d raw=[%d,%d]-[%d,%d] cursorScreen=(%d,%d) cursorTest=(%d,%d) snap=%d prox=%d view=(%d,%d)",
+			effectiveOpcode, targetIdx, left, top, right, bottom,
+			screenCursorX, screenCursorY, curX, curY, snap, _targetProximity,
+			_perspectiveX, _perspectiveY);
+
 		// Check tight lock: cursor within target + snap (no extra margin)
 		if (curX > left - snap && curX < right + snap &&
 			curY > top - snap && curY < bottom + snap) {
diff --git a/engines/scumm/insane/rebel1/render.cpp b/engines/scumm/insane/rebel1/render.cpp
index ac5c801a121..f428e495c39 100644
--- a/engines/scumm/insane/rebel1/render.cpp
+++ b/engines/scumm/insane/rebel1/render.cpp
@@ -31,9 +31,8 @@ inline int ra1OverlayViewOffsetX(const InsaneRebel1 *rebel1) {
 	if (!rebel1 || !rebel1->isInteractiveVideoActive())
 		return 0;
 
-	// In opcode 0x0B (FUN_1CDA7), marker/shot coordinates are in the gameplay
-	// window. Under ScummVM's FUN_224FD crop emulation, shift them into the
-	// 384-wide source buffer so they stay aligned after the source-window crop.
+	// Opcode 0x0B target/GOST markers are handled as raw/projected coordinates
+	// in their callers. Keep this helper scoped to that legacy path.
 	return (rebel1->getEffectiveGameOpcode() == 0x0B) ? rebel1->getPerspectiveX() : 0;
 }
 
@@ -44,6 +43,42 @@ inline int ra1OverlayViewOffsetY(const InsaneRebel1 *rebel1) {
 	return (rebel1->getEffectiveGameOpcode() == 0x0B) ? rebel1->getPerspectiveY() : 0;
 }
 
+int ra1GameplayWindowOffsetX(const InsaneRebel1 *rebel1) {
+	if (!rebel1 || !rebel1->isInteractiveVideoActive())
+		return 0;
+
+	// Ship/cursor/shot coordinates are in DOS's 320x200 gameplay window. Under
+	// ScummVM's FUN_224FD crop emulation, shift them into the 384x242 source
+	// buffer so the final source-window crop presents them at the same screen
+	// position DOS used for gameplay and collision.
+	switch (rebel1->getEffectiveGameOpcode()) {
+	case 0x07:
+	case 0x08:
+	case 0x09:
+	case 0x0A:
+	case 0x0B:
+		return rebel1->getPerspectiveX();
+	default:
+		return 0;
+	}
+}
+
+int ra1GameplayWindowOffsetY(const InsaneRebel1 *rebel1) {
+	if (!rebel1 || !rebel1->isInteractiveVideoActive())
+		return 0;
+
+	switch (rebel1->getEffectiveGameOpcode()) {
+	case 0x07:
+	case 0x08:
+	case 0x09:
+	case 0x0A:
+	case 0x0B:
+		return rebel1->getPerspectiveY();
+	default:
+		return 0;
+	}
+}
+
 void drawBankString(const RA1SpriteBank &bank, byte *dst, int pitch, int width, int height,
 	int x, int y, const char *text) {
 	if (!dst || !text || bank.numSprites <= 0)
@@ -840,8 +875,8 @@ void InsaneRebel1::renderTargetBoxes(byte *dst, int pitch, int width, int height
 void InsaneRebel1::renderTargeting(byte *dst, int pitch, int width, int height) {
 	const char kRA1TorpedoIndicator[] = "<<d";
 	const RA1SpriteBank &markerBank = (_techFontBank.numSprites > 0) ? _techFontBank : _hudFontBank;
-	const int overlayX = ra1OverlayViewOffsetX(this);
-	const int overlayY = ra1OverlayViewOffsetY(this);
+	const int overlayX = ra1GameplayWindowOffsetX(this);
+	const int overlayY = ra1GameplayWindowOffsetY(this);
 	if (markerBank.numSprites > 0) {
 		// FUN_1CB22 can switch marker sets via DAT_75FF bit 1.
 		// Baseline RA1 targeting uses '^' and animation e..h.
@@ -1155,21 +1190,21 @@ void InsaneRebel1::renderLaserShots(byte *dst, int pitch, int width, int height)
 		0, -3, -19, -24, -30, -28, -30, -29, -20, -5
 	};
 	const int spritesPerSet = 5;
-	const int overlayX = ra1OverlayViewOffsetX(this);
-	const int overlayY = ra1OverlayViewOffsetY(this);
+	const int overlayX = ra1GameplayWindowOffsetX(this);
+	const int overlayY = ra1GameplayWindowOffsetY(this);
 	const int leftStartX = 0;
 	const int rightStartX = 0x13F; // 319
 	const uint16 effectiveOpcode = getEffectiveGameOpcode();
 	const bool onFootMode = (effectiveOpcode == 0x19 || effectiveOpcode == 0x1A);
 	const bool turretMode = (effectiveOpcode == 0x08 || effectiveOpcode == 0x0A);
 	const bool flightVariantMode = (effectiveOpcode == 0x09);
-	int shipBaseX = _shipPosX;
-	int shipBaseY = flightVariantMode ? _shipPosY : (overlayY + _shipPosY);
+	int shipBaseX = overlayX + _shipPosX;
+	int shipBaseY = overlayY + _shipPosY;
 	if (turretMode) {
 		int16 centerX, centerY;
 		getTurretShipCenter(centerX, centerY);
-		shipBaseX = centerX;
-		shipBaseY = centerY;
+		shipBaseX = overlayX + centerX;
+		shipBaseY = overlayY + centerY;
 	}
 
 	for (int i = 0; i < kMaxShotSlots; i++) {
@@ -1589,8 +1624,8 @@ void InsaneRebel1::renderShip(byte *dst, int pitch, int width, int height) {
 		shipScreenY = centerY;
 	}
 
-	int drawX = shipScreenX - spr.width / 2;
-	int drawY = shipScreenY - spr.height / 2;
+	int drawX = ra1GameplayWindowOffsetX(this) + shipScreenX - spr.width / 2;
+	int drawY = ra1GameplayWindowOffsetY(this) + shipScreenY - spr.height / 2;
 
 	renderSprite(dst, pitch, width, height, drawX, drawY, spr);
 }
@@ -1601,8 +1636,8 @@ void InsaneRebel1::renderExplosions(byte *dst, int pitch, int width, int height)
 	if (_bangBank.numSprites <= 0)
 		return;
 
-	const int overlayX = ra1OverlayViewOffsetX(this);
-	const int overlayY = ra1OverlayViewOffsetY(this);
+	const int overlayX = ra1GameplayWindowOffsetX(this);
+	const int overlayY = ra1GameplayWindowOffsetY(this);
 	// In 0x08/0x0A turret handlers, explosion anchors use the ship center, not
 	// the targeting cursor stored in _shipPos. Flight handlers already keep the
 	// ship center in _shipPos.
@@ -1612,8 +1647,8 @@ void InsaneRebel1::renderExplosions(byte *dst, int pitch, int width, int height)
 	if (effectiveOpcode == 0x08 || effectiveOpcode == 0x0A) {
 		int16 centerX, centerY;
 		getTurretShipCenter(centerX, centerY);
-		shipScreenX = centerX;
-		shipScreenY = centerY;
+		shipScreenX = overlayX + centerX;
+		shipScreenY = overlayY + centerY;
 	}
 
 	// --- Death shake explosions (FUN_1DEB5 LAB_1e0e3) ---


Commit: 87bf21fc0426da2da7817f87072d774f8ee656c3
    https://github.com/scummvm/scummvm/commit/87bf21fc0426da2da7817f87072d774f8ee656c3
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-03T09:18:52+02:00

Commit Message:
SCUMM: RA2: fixed corruption in L1 -> L2

Changed paths:
    engines/scumm/insane/rebel2/runlevels.cpp


diff --git a/engines/scumm/insane/rebel2/runlevels.cpp b/engines/scumm/insane/rebel2/runlevels.cpp
index a71a39285c5..77f5c4cbdb0 100644
--- a/engines/scumm/insane/rebel2/runlevels.cpp
+++ b/engines/scumm/insane/rebel2/runlevels.cpp
@@ -269,9 +269,8 @@ void InsaneRebel2::resetLevelPhaseState(bool clearEnemies) {
 	_grd002Sprite = nullptr;
 	_grdShotOriginTableLoaded = false;
 
-	static const int handler25FrameSlots[] = { 4, 6, 7, 10, 12, 13 };
-	for (uint i = 0; i < ARRAYSIZE(handler25FrameSlots); ++i) {
-		EmbeddedSanFrame &frame = _rebelEmbeddedHud[handler25FrameSlots[i]];
+	for (uint i = 0; i < ARRAYSIZE(_rebelEmbeddedHud); ++i) {
+		EmbeddedSanFrame &frame = _rebelEmbeddedHud[i];
 		free(frame.pixels);
 		frame.pixels = nullptr;
 		frame.width = 0;


Commit: 3dc821daaecb64a36ee648daafb3170f0cd750ff
    https://github.com/scummvm/scummvm/commit/3dc821daaecb64a36ee648daafb3170f0cd750ff
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-03T09:53:01+02:00

Commit Message:
SCUMM: RA2: implement arrow hints to avoid collisions

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


diff --git a/engines/scumm/insane/rebel2/rebel.h b/engines/scumm/insane/rebel2/rebel.h
index 6ccc021848f..c4ed62eb146 100644
--- a/engines/scumm/insane/rebel2/rebel.h
+++ b/engines/scumm/insane/rebel2/rebel.h
@@ -836,10 +836,10 @@ public:
 	// Reset collision zone counters (called at end of frame)
 	void resetCollisionZones();
 
-	// Per-frame collision checking against registered zones (FUN_4092D9 first loop)
+	// Per-frame collision checking against registered zones (FUN_4092D9)
 	// Tests aim/ship position against primary zone quadrilaterals
 	// Applies collision damage from DAT_0047e0f6 when inside obstacle zone
-	void checkCollisionZones();
+	void checkCollisionZones(byte *renderBitmap, int pitch, int width, int height, int32 curFrame);
 
 	// Handler 7 collision system (FUN_40E35E)
 	// Mode 0/2: Obstacle collision using secondary zones ??? inside quad = hit
diff --git a/engines/scumm/insane/rebel2/render.cpp b/engines/scumm/insane/rebel2/render.cpp
index 9302279fbbd..e5a373e253f 100644
--- a/engines/scumm/insane/rebel2/render.cpp
+++ b/engines/scumm/insane/rebel2/render.cpp
@@ -39,6 +39,13 @@ namespace Scumm {
 extern void smushDecodeRLE(byte *dst, const byte *src, int left, int top, int width, int height, int pitch);
 extern void smushDecodeUncompressed(byte *dst, const byte *src, int left, int top, int width, int height, int pitch);
 
+int getRebel2IndicatorScale(int width, int height) {
+	// Retail only doubles these anchors in true high-res mode (DAT_0047a808 >= 2).
+	// RA2's 424x260 low-res gameplay buffer is still displayed through a 320x200
+	// viewport, so it uses the original 320x200 indicator coordinates.
+	return (width >= 640 || height >= 400) ? 2 : 1;
+}
+
 static bool isValidEmbeddedFrame(const InsaneRebel2::EmbeddedSanFrame &frame) {
 	return frame.valid && frame.pixels && frame.width > 0 && frame.height > 0;
 }
@@ -1545,8 +1552,8 @@ void InsaneRebel2::resetCollisionZones() {
 //
 // checkCollisionZones -- Per-frame collision test against primary zones (FUN_4092D9).
 //
-void InsaneRebel2::checkCollisionZones() {
-	// Per-frame collision checking ??? FUN_4092D9 first loop (lines 39-202).
+void InsaneRebel2::checkCollisionZones(byte *renderBitmap, int pitch, int width, int height, int32 curFrame) {
+	// Per-frame collision checking ??? FUN_4092D9.
 	// Tests aim/ship position against primary collision zone quadrilaterals.
 	//
 	// Original coordinate system:
@@ -1580,6 +1587,12 @@ void InsaneRebel2::checkCollisionZones() {
 	if (aimY < -0x2d)
 		aimY = -0x2d;
 
+	LevelDifficultyParams dparams = getDifficultyParams();
+	const bool showDirectionalWarnings = (dparams.flags & 8) != 0;
+	const bool showGenericWarnings = !showDirectionalWarnings && ((dparams.flags & 0x10) != 0);
+	const bool warningFrame = ((curFrame & 2) != 0) && _smush_iconsNut && (showDirectionalWarnings || showGenericWarnings);
+	const int indicatorScale = getRebel2IndicatorScale(width, height);
+
 	for (int i = 0; i < _primaryZoneCount; i++) {
 		CollisionZone &zone = _primaryZones[i];
 		if (!zone.active)
@@ -1590,11 +1603,6 @@ void InsaneRebel2::checkCollisionZones() {
 		if (zone.filterValue >= 1000)
 			continue;
 
-		// Frame check: field2 - 1 == field1
-		// Original: sVar2 + -1 == (int)sVar1
-		if (zone.field2 - 1 != zone.field1)
-			continue;
-
 		// Center zone vertices by subtracting buffer center (0xD4=212, 0x82=130)
 		// Original: sVar4 = x1 - 0xD4, sVar8 = y1 - 0x82, etc.
 		int cx1 = zone.x1 - 0xD4;
@@ -1606,64 +1614,94 @@ void InsaneRebel2::checkCollisionZones() {
 		int cx4 = zone.x4 - 0xD4;
 		int cy4 = zone.y4 - 0x82;
 
-		// Point-in-quadrilateral test ??? FUN_4092D9 lines 119-128
-		// Tests if aim position is OUTSIDE the safe corridor (= collision with obstacle).
-		// Original uses 4 edge interpolation tests connected by OR (any failure = collision).
-		//
-		// Edge 1: interpolate Y along top edge (v1???v2) at aim X position
-		//   if aimY < interpolated Y ??? outside top edge ??? collision
-		// Edge 2: interpolate Y along bottom edge (v4???v3) at aim X position
-		//   if interpolated Y < aimY ??? outside bottom edge ??? collision
-		// Edge 3: interpolate X along left edge (v1???v4) at aim Y position
-		//   if aimX < interpolated X ??? outside left edge ??? collision
-		// Edge 4: interpolate X along right edge (v2???v3) at aim Y position
-		//   if interpolated X < aimX ??? outside right edge ??? collision
-		bool collision = false;
-
-		// Avoid division by zero for degenerate edges
-		if (cx2 != cx1) {
-			int interpY1 = ((aimX - cx1) * (cy2 - cy1)) / (cx2 - cx1) + cy1;
-			if (aimY < interpY1)
-				collision = true;
-		}
-		if (!collision && cx3 != cx4) {
-			int interpY2 = ((aimX - cx4) * (cy3 - cy4)) / (cx3 - cx4) + cy4;
-			if (interpY2 < aimY)
-				collision = true;
-		}
-		if (!collision && cy4 != cy1) {
-			int interpX1 = ((aimY - cy1) * (cx4 - cx1)) / (cy4 - cy1) + cx1;
-			if (aimX < interpX1)
-				collision = true;
-		}
-		if (!collision && cy3 != cy2) {
-			int interpX2 = ((aimY - cy2) * (cx3 - cx2)) / (cy3 - cy2) + cx2;
-			if (interpX2 < aimX)
-				collision = true;
-		}
+		// Frame check: field2 - 1 == field1
+		// Original: sVar2 + -1 == (int)sVar1
+		if (zone.field2 - 1 == zone.field1) {
+			// Point-in-quadrilateral test ??? FUN_4092D9 lines 119-128
+			// Tests if aim position is OUTSIDE the safe corridor (= collision with obstacle).
+			// Original uses 4 edge interpolation tests connected by OR (any failure = collision).
+			//
+			// Edge 1: interpolate Y along top edge (v1???v2) at aim X position
+			//   if aimY < interpolated Y ??? outside top edge ??? collision
+			// Edge 2: interpolate Y along bottom edge (v4???v3) at aim X position
+			//   if interpolated Y < aimY ??? outside bottom edge ??? collision
+			// Edge 3: interpolate X along left edge (v1???v4) at aim Y position
+			//   if aimX < interpolated X ??? outside left edge ??? collision
+			// Edge 4: interpolate X along right edge (v2???v3) at aim Y position
+			//   if interpolated X < aimX ??? outside right edge ??? collision
+			bool collision = false;
+
+			// Avoid division by zero for degenerate edges
+			if (cx2 != cx1) {
+				int interpY1 = ((aimX - cx1) * (cy2 - cy1)) / (cx2 - cx1) + cy1;
+				if (aimY < interpY1)
+					collision = true;
+			}
+			if (!collision && cx3 != cx4) {
+				int interpY2 = ((aimX - cx4) * (cy3 - cy4)) / (cx3 - cx4) + cy4;
+				if (interpY2 < aimY)
+					collision = true;
+			}
+			if (!collision && cy4 != cy1) {
+				int interpX1 = ((aimY - cy1) * (cx4 - cx1)) / (cy4 - cy1) + cx1;
+				if (aimX < interpX1)
+					collision = true;
+			}
+			if (!collision && cy3 != cy2) {
+				int interpX2 = ((aimY - cy2) * (cx3 - cx2)) / (cy3 - cy2) + cx2;
+				if (interpX2 < aimX)
+					collision = true;
+			}
 
-		if (collision) {
-			// Collision detected ??? apply damage from collision damage table
-			// Original: DAT_0047a7ec += DAT_0047e0f6[chapter * 0x242 + level * 0x22]
-			LevelDifficultyParams params = getDifficultyParams();
-			int collisionDamage = (params.dodgeDamage >= 0) ? params.dodgeDamage : 0;
-
-			if (!_rebelInvulnerable) {
-				_playerDamage += collisionDamage;
-				if (_playerDamage > 255)
-					_playerDamage = 255;
-				debug("Rebel2: COLLISION damage! zone=%d aim=(%d,%d) damage=%d total=%d",
-					i, aimX, aimY, collisionDamage, _playerDamage);
+			if (collision) {
+				// Collision detected ??? apply damage from collision damage table
+				// Original: DAT_0047a7ec += DAT_0047e0f6[chapter * 0x242 + level * 0x22]
+				int collisionDamage = (dparams.dodgeDamage >= 0) ? dparams.dodgeDamage : 0;
+
+				if (!_rebelInvulnerable) {
+					_playerDamage += collisionDamage;
+					if (_playerDamage > 255)
+						_playerDamage = 255;
+					debug("Rebel2: COLLISION damage! zone=%d aim=(%d,%d) damage=%d total=%d",
+						i, aimX, aimY, collisionDamage, _playerDamage);
+				}
+				// Visual effect ??? FUN_00420515 (palette flash)
+				initDamageFlash();
+				// TODO: FUN_0041189e sound based on collision direction
+			} else {
+				// Safely passed ??? award score bonus
+				// Original: FUN_0041bf8d(DAT_0047e100[levelIdx])
+				if (dparams.dodgePoints > 0) {
+					addScore(dparams.dodgePoints);
+				}
 			}
-			// Visual effect ??? FUN_00420515 (palette flash)
-			initDamageFlash();
-			// TODO: FUN_0041189e sound based on collision direction
-		} else {
-			// Safely passed ??? award score bonus
-			// Original: FUN_0041bf8d(DAT_0047e100[levelIdx])
-			LevelDifficultyParams scoreParams = getDifficultyParams();
-			if (scoreParams.dodgePoints > 0) {
-				addScore(scoreParams.dodgePoints);
+		} else if (warningFrame && zone.field2 - 0x0c < zone.field1) {
+			// FUN_4092D9 near-collision indicators. Easy mode (flags bit 0x08)
+			// draws directional arrows from cockpit icon slots 0x2a..0x2d.
+			// Novice mode (flags bit 0x10) draws a generic indicator from slot 0x36.
+			const int iconChars = _smush_iconsNut->getNumChars();
+			if (showDirectionalWarnings) {
+				int avgX = (cx1 + cx2 + cx3 + cx4) / 4;
+				int avgY = (cy1 + cy2 + cy3 + cy4) / 4;
+
+				if (avgX >= 0x15 && iconChars > 0x2d) {
+					renderNutSprite(renderBitmap, pitch, width, height,
+						0xd7 * indicatorScale + _viewX, 0x55 * indicatorScale + _viewY, _smush_iconsNut, 0x2d);
+				} else if (avgX < -0x14 && iconChars > 0x2c) {
+					renderNutSprite(renderBitmap, pitch, width, height,
+						0x69 * indicatorScale + _viewX, 0x55 * indicatorScale + _viewY, _smush_iconsNut, 0x2c);
+				}
+
+				if (avgY >= 0x15 && iconChars > 0x2b) {
+					renderNutSprite(renderBitmap, pitch, width, height,
+						0xa0 * indicatorScale + _viewX, 0x82 * indicatorScale + _viewY, _smush_iconsNut, 0x2b);
+				} else if (avgY < -0x14 && iconChars > 0x2a) {
+					renderNutSprite(renderBitmap, pitch, width, height,
+						0xa0 * indicatorScale + _viewX, 0x28 * indicatorScale + _viewY, _smush_iconsNut, 0x2a);
+				}
+			} else if (showGenericWarnings && iconChars > 0x36) {
+				renderNutSprite(renderBitmap, pitch, width, height,
+					0xa0 * indicatorScale + _viewX, 0x1e * indicatorScale + _viewY, _smush_iconsNut, 0x36);
 			}
 		}
 	}
@@ -1925,7 +1963,7 @@ void InsaneRebel2::renderHandler7WarningCues(byte *renderBitmap, int pitch, int
 	// Note: These are cue sprites (often perceived as "shadows"), not the aiming reticle.
 	LevelDifficultyParams dparams = getDifficultyParams();
 	if ((curFrame & 2) != 0 && (dparams.flags & 8) != 0 && _smush_iconsNut) {
-		int scale = (_vm->_screenWidth > 320 || _vm->_screenHeight > 200) ? 2 : 1;
+		int scale = getRebel2IndicatorScale(width, height);
 
 		if ((warningMask & 1) != 0 && _smush_iconsNut->getNumChars() > 0x2d) {
 			renderNutSprite(renderBitmap, pitch, width, height,
@@ -2340,7 +2378,7 @@ void InsaneRebel2::checkGameplayPostRenderCollisions(byte *renderBitmap, int pit
 	//   Mode 1/3: PRIMARY zones (0x0D) - wall/boundary per-edge with push-back
 	//   Uses ship position in raw buffer coords, hit cooldown, directional damage.
 	if (_rebelHandler == 0x26) {
-		checkCollisionZones();
+		checkCollisionZones(renderBitmap, pitch, width, height, curFrame);
 	} else if (_rebelHandler == 7) {
 		checkHandler7CollisionZones(renderBitmap, pitch, width, height, curFrame);
 	}



From noreply at scummvm.org  Wed Jun  3 08:30:31 2026
From: noreply at scummvm.org (ScummVM-Translations)
Date: Wed,  3 Jun 2026 08:30:31 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 01c6d7ea723cd682c4e6f212dcda8cb4a17688ad
Message-ID: <20260603083031.7DB5C700591@ron.scummvm.net>

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

Summary:
01c6d7ea72 I18N: Update translation (Spanish)


Commit: 01c6d7ea723cd682c4e6f212dcda8cb4a17688ad
    https://github.com/scummvm/scummvm/commit/01c6d7ea723cd682c4e6f212dcda8cb4a17688ad
Author: IlDucci (pajaroloco_2 at hotmail.com)
Date: 2026-06-03T08:30:24Z

Commit Message:
I18N: Update translation (Spanish)

Currently translated at 100.0% (3204 of 3204 strings)

Changed paths:
    po/es_ES.po


diff --git a/po/es_ES.po b/po/es_ES.po
index 86af8371604..23e6f9ee56c 100644
--- a/po/es_ES.po
+++ b/po/es_ES.po
@@ -8,7 +8,7 @@ msgstr ""
 "Project-Id-Version: ScummVM 1.4.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
 "POT-Creation-Date: 2026-06-02 16:30+0000\n"
-"PO-Revision-Date: 2026-06-01 09:35+0000\n"
+"PO-Revision-Date: 2026-06-03 08:30+0000\n"
 "Last-Translator: IlDucci <pajaroloco_2 at hotmail.com>\n"
 "Language-Team: Spanish <https://translations.scummvm.org/projects/scummvm/"
 "scummvm/es/>\n"
@@ -13757,16 +13757,12 @@ msgid "Fire / select"
 msgstr "Disparar/Seleccionar"
 
 #: engines/scumm/metaengine.cpp:1186
-#, fuzzy
-#| msgid "Move back"
 msgid "Menu back"
-msgstr "Retroceder"
+msgstr "Volver (men??s)"
 
 #: engines/scumm/metaengine.cpp:1191
-#, fuzzy
-#| msgid "Skip / menu"
 msgid "Skip / menu back"
-msgstr "Omitir/Men??"
+msgstr "Omitir/Volver (men??s)"
 
 #: engines/scumm/metaengine.cpp:1207
 msgid "Rebel Assault II controls"
@@ -13777,10 +13773,8 @@ msgid "Cover / back"
 msgstr "Cubrirse/Volver"
 
 #: engines/scumm/metaengine.cpp:1268
-#, fuzzy
-#| msgid "Skip walk"
 msgid "Skip / back"
-msgstr "Omitir desplazamiento"
+msgstr "Omitir/Volver"
 
 #: engines/scumm/metaengine.cpp:1275
 msgid "Skip / menu"



From noreply at scummvm.org  Wed Jun  3 09:51:10 2026
From: noreply at scummvm.org (neuromancer)
Date: Wed,  3 Jun 2026 09:51:10 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 97ae70740c160c27b96a5af516b549a879fada94
Message-ID: <20260603095110.5A5FA700591@ron.scummvm.net>

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

Summary:
97ae70740c SCUMM: RA: add codec4/5 post processing


Commit: 97ae70740c160c27b96a5af516b549a879fada94
    https://github.com/scummvm/scummvm/commit/97ae70740c160c27b96a5af516b549a879fada94
Author: Manuel Lauss (manuel.lauss at gmail.com)
Date: 2026-06-03T11:51:05+02:00

Commit Message:
SCUMM: RA: add codec4/5 post processing

Adds the post-processing found in the high-quality implementations
of codec4/5/33/34 of RA1 DOS:  It reduces the inherent "blocky" look
of these codecs.

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


diff --git a/engines/scumm/smush/rebel/codec_ra1.cpp b/engines/scumm/smush/rebel/codec_ra1.cpp
index e9c0a0ba9e9..3620dea06df 100644
--- a/engines/scumm/smush/rebel/codec_ra1.cpp
+++ b/engines/scumm/smush/rebel/codec_ra1.cpp
@@ -317,6 +317,26 @@ void smushDecodeRA1Block(byte *dst, const byte *src, int left, int top, int widt
 							*(dst + yo * pitch + xo) = *gs;
 					}
 			}
+
+			/* post processing of the hiquality implementations of codec4/5,
+			 * see e.g. ASSAULT.EXE 121e8 - 12242
+			 */
+			if (x <= 0 || y <= 0 || (x + 4) >= mx || (y + 4) >= my)
+				continue;		/* skip unreachable edges */
+			const uint32 dstoff = y * pitch + x;
+			if (s_ra1C4Param & 0x80) {
+				for (int k = 0; k < 4; k++)
+					*(dst + dstoff + k) = ((*(dst + dstoff + k) + *(dst + dstoff + k - pitch)) >> 1) | 0x80;
+				*(dst + dstoff + 1 * pitch) = ((*(dst + dstoff + 1 * pitch) + *(dst + dstoff + 1 * pitch - 1)) >> 1) | 0x80;
+				*(dst + dstoff + 2 * pitch) = ((*(dst + dstoff + 2 * pitch) + *(dst + dstoff + 2 * pitch - 1)) >> 1) | 0x80;
+				*(dst + dstoff + 3 * pitch) = ((*(dst + dstoff + 3 * pitch) + *(dst + dstoff + 3 * pitch - 1)) >> 1) | 0x80;
+			} else {
+				for (int k = 0; k < 4; k++)
+					*(dst + dstoff + k) = ((*(dst + dstoff + k) + *(dst + dstoff + k - pitch)) >> 1) & 0x7f;
+				*(dst + dstoff + 1 * pitch) = ((*(dst + dstoff + 1 * pitch) + *(dst + dstoff + 1 * pitch - 1)) >> 1) & 0x7f;
+				*(dst + dstoff + 2 * pitch) = ((*(dst + dstoff + 2 * pitch) + *(dst + dstoff + 2 * pitch - 1)) >> 1) & 0x7f;
+				*(dst + dstoff + 3 * pitch) = ((*(dst + dstoff + 3 * pitch) + *(dst + dstoff + 3 * pitch - 1)) >> 1) & 0x7f;
+			}
 		}
 	}
 }



From noreply at scummvm.org  Wed Jun  3 09:53:20 2026
From: noreply at scummvm.org (neuromancer)
Date: Wed,  3 Jun 2026 09:53:20 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 3218ea2566218a25815f64c3b08679dd8959da71
Message-ID: <20260603095320.3D7EF700591@ron.scummvm.net>

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

Summary:
3218ea2566 SCUMM: RA2: refined controls for level 1


Commit: 3218ea2566218a25815f64c3b08679dd8959da71
    https://github.com/scummvm/scummvm/commit/3218ea2566218a25815f64c3b08679dd8959da71
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-03T11:53:12+02:00

Commit Message:
SCUMM: RA2: refined controls for level 1

Changed paths:
    engines/scumm/insane/rebel2/rebel.cpp


diff --git a/engines/scumm/insane/rebel2/rebel.cpp b/engines/scumm/insane/rebel2/rebel.cpp
index c76a47b1067..d8dfa9bc0b0 100644
--- a/engines/scumm/insane/rebel2/rebel.cpp
+++ b/engines/scumm/insane/rebel2/rebel.cpp
@@ -616,12 +616,13 @@ bool InsaneRebel2::notifyEvent(const Common::Event &event) {
 	// dropping the event here prevents the clobber. Firing is driven by the
 	// kScummActionInsaneAttack action, not the pointer, so this does not affect shots.
 	// A genuine mouse/touch motion (nonzero relative delta) normally hands control
-	// back; handler 0x26 keeps ownership until its gamepad reticle returns to center.
+	// back; handler 0x26 keeps ownership until its gamepad reticle returns to center,
+	// except for Level 1's relative gamepad aiming where the reticle deliberately holds.
 	if (_gamepadAimActive && _gameState == kStateGameplay && !_menuInputActive) {
 		switch (event.type) {
 		case Common::EVENT_MOUSEMOVE:
 			if (event.relMouse.x != 0 || event.relMouse.y != 0) {
-				if (_rebelHandler == 0x26)
+				if (_rebelHandler == 0x26 && _selectedLevel != 1)
 					return true;
 				_gamepadAimActive = false; // real pointer motion takes over
 				break;
@@ -1657,7 +1658,23 @@ void InsaneRebel2::updateGameplayAimFromGamepad() {
 	int deltaY = 0;
 	bool activeGamepadAim = false;
 
-	if (_rebelHandler == 0x26) {
+	if (_rebelHandler == 0x26 && _selectedLevel == 1) {
+		// Level 1 plays best with the older mouse-like gamepad behavior from
+		// ec305dee371/0025c4e1086: pan the reticle directly and leave it where
+		// the player releases the stick. Later handler 0x26 levels keep the
+		// original-style centered mapping for obstacle avoidance.
+		if (dpadX || dpadY) {
+			const int kLevel1DigitalStep = 3;
+			deltaX = dpadX * kLevel1DigitalStep;
+			deltaY = dpadY * kLevel1DigitalStep;
+			activeGamepadAim = true;
+		} else if (velX || velY) {
+			const int kLevel1AnalogMaxStep = 8;
+			deltaX = velX * kLevel1AnalogMaxStep / 127;
+			deltaY = velY * kLevel1AnalogMaxStep / 127;
+			activeGamepadAim = true;
+		}
+	} else if (_rebelHandler == 0x26) {
 		// Retail RA2 maps joystick axes into a small centered handler 0x26 reticle box.
 		// ScummVM deliberately exposes the full 320x200 mouse aim range for gamepads
 		// too, but preserves the original joystick feel: curved response for precise



From noreply at scummvm.org  Wed Jun  3 11:17:46 2026
From: noreply at scummvm.org (dreammaster)
Date: Wed,  3 Jun 2026 11:17:46 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 20f6cc3eae81a825e7f3ea077839a06deda2b04e
Message-ID: <20260603111746.3CEBC700591@ron.scummvm.net>

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:
fb9409e7c9 MADS: DRAGONSPHERE: Parser cleanup in room 116
20f6cc3eae MADS: DRAGONSPHERE: Fixes for rescuing king from ice


Commit: fb9409e7c924c53a6fcc0232afaa45647a910627
    https://github.com/scummvm/scummvm/commit/fb9409e7c924c53a6fcc0232afaa45647a910627
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-06-03T21:17:36+10:00

Commit Message:
MADS: DRAGONSPHERE: Parser cleanup in room 116

Changed paths:
    engines/mads/madsv2/dragonsphere/mads/conv.h
    engines/mads/madsv2/dragonsphere/rooms/room116.cpp
    engines/mads/madsv2/dragonsphere/rooms/room405.cpp


diff --git a/engines/mads/madsv2/dragonsphere/mads/conv.h b/engines/mads/madsv2/dragonsphere/mads/conv.h
index 173e1cc728f..559a9d968f8 100644
--- a/engines/mads/madsv2/dragonsphere/mads/conv.h
+++ b/engines/mads/madsv2/dragonsphere/mads/conv.h
@@ -417,10 +417,10 @@ enum {
 };
 
 enum {
-	conv044_repeater         =  0,
-	conv044_greet            =  2,
-	conv044_leave_b_b        =  7,
-	conv044_enter_b_b        =  9
+	conv044_greet            =  0,
+	conv044_repeater         =  2,
+	conv044_enter_b_b        =  7,
+	conv044_leave_b_b        =  9
 };
 
 enum {
diff --git a/engines/mads/madsv2/dragonsphere/rooms/room116.cpp b/engines/mads/madsv2/dragonsphere/rooms/room116.cpp
index 32927ceac90..d346f210b46 100644
--- a/engines/mads/madsv2/dragonsphere/rooms/room116.cpp
+++ b/engines/mads/madsv2/dragonsphere/rooms/room116.cpp
@@ -391,15 +391,15 @@ static void room_116_daemon() {
 }
 
 static void room_116_pre_parser() {
-	if (!player_parse(words_look, 0) &&
-	    !player_parse(words_push, words_king, 0) &&
-	    !player_parse(7, words_king, words_cave_floor, 0) &&
-	    !player_parse(words_pull, words_king, 0) &&
-	    !player_parse(words_take, words_king, 0) &&
-	    !player_parse(words_walk_to, words_king, 0) &&
+	if (!player_said_1(look) &&
+	    !player_said_2(push, king) &&
+	    !player_said_3(put, king, cave_floor) &&
+	    !player_said_2(pull, king) &&
+	    !player_said_2(take, king) &&
+	    !player_said_2(walk_to, king) &&
 	    (local->bear_status == FIRST_TIME_BEAR || local->bear_status == IS_A_BEAR_AGAIN) &&
 	    global[king_status] == KING_CAPTIVE &&
-	    (player.need_to_walk || player_parse(words_revert, 0))) {
+	    (player.need_to_walk || player_said_1(revert))) {
 
 		switch (kernel.trigger) {
 		case 0:
@@ -415,7 +415,7 @@ static void room_116_pre_parser() {
 			local->bear_status = IS_PID_AGAIN;
 			player.walker_visible = true;
 			player.commands_allowed = true;
-			if (!player_parse(words_revert, 0)) {
+			if (!player_said_1(revert)) {
 				player.ready_to_walk = true;
 			}
 			kernel_synch(KERNEL_PLAYER, 0, KERNEL_SERIES, seq[fx_bear_morph]);
@@ -423,7 +423,7 @@ static void room_116_pre_parser() {
 		}
 	}
 
-	if (player_parse(words_invoke_power_of, words_crystal_ball, 0)) {
+	if (player_said_2(invoke_power_of, crystal_ball)) {
 		if (local->bear_status == HAS_NEVER_BEEN_A_BEAR ||
 		    local->bear_status == IS_PID_AGAIN) {
 			if (global[king_status] == KING_CAPTIVE ||
@@ -466,7 +466,7 @@ static void room_116_parser() {
 		goto handled;
 	}
 
-	if (player_parse(words_revert, 0) &&
+	if (player_said_1(revert) &&
 	    (local->bear_status == FIRST_TIME_BEAR || local->bear_status == IS_A_BEAR_AGAIN)) {
 		goto handled;
 	}
@@ -476,7 +476,7 @@ static void room_116_parser() {
 		goto handled;
 	}
 
-	if (player_parse(words_talk_to, words_king, 0)) {
+	if (player_said_2(talk_to, king)) {
 		if (global[king_status] == KING_CAPTIVE) {
 			text_show(11626);
 		} else {
@@ -487,9 +487,9 @@ static void room_116_parser() {
 		goto handled;
 	}
 
-	if (player_parse(words_walk_through, words_door_to_north, 0) ||
-	    player_parse(words_open, words_door_to_north, 0) ||
-	    player_parse(words_pull, words_door_to_north, 0)) {
+	if (player_said_2(walk_through, door_to_north) ||
+	    player_said_2(open, door_to_north) ||
+	    player_said_2(pull, door_to_north)) {
 		switch (kernel.trigger) {
 		case 0:
 			player.commands_allowed = false;
@@ -555,9 +555,9 @@ static void room_116_parser() {
 		goto handled;
 	}
 
-	if (player_parse(words_walk_through, words_doorway_to_south, 0) ||
-			player_parse(words_open, words_doorway_to_south, 0) ||
-			player_parse(words_pull, words_doorway_to_south, 0)) {
+	if (player_said_2(walk_through, doorway_to_south) ||
+			player_said_2(open, doorway_to_south) ||
+			player_said_2(pull, doorway_to_south)) {
 		if (global[king_status] == KING_WITH_SOUL && !global[king_is_in_stairwell]) {
 			conv_run(CONVERSATION_WITH_KING);
 			conv_export_value(global[king_status] == KING_WITH_SOUL ? 1 : 0);
@@ -569,16 +569,16 @@ static void room_116_parser() {
 	}
 
 	// Can't shift into seal/snake while a bear
-	if ((player_parse(words_shift_into_seal, 0) || player_parse(words_shift_into_snake, 0)) &&
+	if ((player_said_1(shift_into_seal) || player_said_1(shift_into_snake)) &&
 	    (local->bear_status == FIRST_TIME_BEAR || local->bear_status == IS_A_BEAR_AGAIN)) {
 		text_show(990);
 		goto handled;
 	}
 
 	// Shift into bear / invoke crystal ball (bear transformation)
-	if (player_parse(words_shift_into_bear, 0) ||
-	    player_parse(words_invoke_power_of, words_crystal_ball, 0)) {
-		if (player_parse(words_shift_into_bear, 0) &&
+	if (player_said_1(shift_into_bear) ||
+	    player_said_2(invoke_power_of, crystal_ball)) {
+		if (player_said_1(shift_into_bear) &&
 				(local->bear_status == FIRST_TIME_BEAR || local->bear_status == IS_A_BEAR_AGAIN)) {
 			text_show(11629);
 			goto handled;
@@ -592,7 +592,7 @@ static void room_116_parser() {
 				break;
 
 			case 1:
-				if (player_parse(words_invoke_power_of, 0))
+				if (player_said_1(invoke_power_of))
 					sound_play(N_InvokeCrystalBall);
 
 				player.walker_visible = false;
@@ -606,7 +606,7 @@ static void room_116_parser() {
 			case 2:
 				local->temp = seq[fx_bear_morph];
 
-				if (player_parse(words_crystal_ball, 0)) {
+				if (player_said_1(crystal_ball)) {
 					aa[fx_open_door] = kernel_run_animation(kernel_name('a', -1), 0);
 					local->anim_2_running = true;
 					global[crystal_ball_dead] = true;
@@ -636,11 +636,11 @@ static void room_116_parser() {
 	}
 
 	// Bear grabs king / gives soul to king
-	if ((player_parse(words_push, words_king, 0) ||
-		player_parse(words_pull, words_king, 0) ||
-		player_parse(words_take, words_king, 0) ||
-		player_parse(words_put, words_king, words_cave_floor)) &&
-		global[king_status] == KING_CAPTIVE) {
+	if ((player_said_2(push, king) ||
+			player_said_2(pull, king) ||
+			player_said_2(take, king) ||
+			player_said_3(put, king, cave_floor)) &&
+			global[king_status] == KING_CAPTIVE) {
 		if (local->bear_status == HAS_NEVER_BEEN_A_BEAR) {
 			kernel_seq_delete(seq[fx_bear_morph]);
 			player.commands_allowed = false;
@@ -678,16 +678,16 @@ static void room_116_parser() {
 		goto handled;
 	}
 
-	if (player_parse(words_give, 82, words_king) ||
-			player_parse(words_invoke_power_of, words_crystal_ball) ||
-			player_parse(words_put, 82, words_king))  {
-		if ((player_parse(words_give, 82, words_king) || player_parse(words_put, 82, words_king)) &&
+	if (player_said_3(give, soul_egg, king) ||
+			player_said_2(invoke_power_of, crystal_ball) ||
+			player_said_3(put, soul_egg, king))  {
+		if ((player_said_3(give, soul_egg, king) || player_said_3(put, soul_egg, king)) &&
 				global[king_status] == KING_CAPTIVE) {
 			text_show(11630);
 		} else if (global[king_status] == KING_WITHOUT_SOUL) {
 			switch (kernel.trigger) {
 			case 0:
-				if (player_parse(words_crystal_ball, 0)) {
+				if (player_said_1(crystal_ball)) {
 					sound_play(N_InvokeCrystalBall);
 					kernel_timing_trigger(1, 3);
 				} else {
@@ -698,7 +698,7 @@ static void room_116_parser() {
 				break;
 
 			case 1:
-				if (!player_parse(words_crystal_ball, 0)) {
+				if (!player_said_1(crystal_ball)) {
 					player.walker_visible = false;
 					seq[fx_give_soul] = kernel_seq_pingpong(ss[fx_give_soul], -1, 8, 0, 0, 2);
 					kernel_seq_player(seq[fx_give_soul], -1);
@@ -717,7 +717,7 @@ static void room_116_parser() {
 				break;
 
 			case 3:
-				if (player_parse(109, 0)) {
+				if (player_said_1(crystal_ball)) {
 					text_show(11618);
 					inter_move_object(crystal_ball, NOWHERE);
 					text_show(970);
@@ -738,7 +738,7 @@ static void room_116_parser() {
 				break;
 
 			case 6:
-				if (!player_parse(crystal_ball, 0)) {
+				if (!player_said_1(crystal_ball)) {
 					inter_move_object(soul_egg, NOWHERE);
 					text_show(11614);
 				}
@@ -756,7 +756,7 @@ static void room_116_parser() {
 		goto handled;
 	}
 
-	if (player_parse(words_invoke, words_signet_ring, 0)) {
+	if (player_said_2(invoke, signet_ring)) {
 		if (global[king_status] == KING_WITH_SOUL && !global[king_is_in_stairwell]) {
 			conv_run(CONVERSATION_WITH_KING);
 			conv_export_value(global[king_status] == KING_WITH_SOUL ? 1 : 0);
@@ -769,73 +769,73 @@ static void room_116_parser() {
 		goto handled;
 	}
 
-	if (player_parse(words_look, 0) || player_parse(words_look_at, 0)) {
-		if (player_parse(words_cave_floor, 0)) {
+	if (player_said_1(look) || player_said_1(look_at)) {
+		if (player_said_1(cave_floor)) {
 			text_show(11602);
 			goto handled;
-		} else if (player_parse(words_cave_wall, 0)) {
+		} else if (player_said_1(cave_wall)) {
 			text_show(11603);
 			goto handled;
-		} else if (player_parse(229, 0)) {
+		} else if (player_said_1(pedestal)) {
 			text_show(global[king_status] == KING_CAPTIVE ? 11604 : 11609);
 			goto handled;
-		} else if (player_parse(words_door_to_north, 0)) {
+		} else if (player_said_1(door_to_north)) {
 			text_show(11605);
 			goto handled;
-		} else if (player_parse(words_doorway_to_south, 0)) {
+		} else if (player_said_1(doorway_to_south)) {
 			text_show(11606);
 			goto handled;
-		} else if (player_parse(words_king, 0) && global[king_status] == KING_CAPTIVE) {
+		} else if (player_said_1(king) && global[king_status] == KING_CAPTIVE) {
 			text_show(11607);
 			goto handled;
-		} else if (player_parse(words_king, 0) && global[king_status] == KING_OFF_ICE) {
+		} else if (player_said_1(king) && global[king_status] == KING_OFF_ICE) {
 			text_show(11610);
 			goto handled;
-		} else if (player_parse(words_king, 0) && conv_control.running != 13) {
+		} else if (player_said_1(king) && conv_control.running != 13) {
 			text_show(11611);
 			goto handled;
 		}
 	}
 
-	if (player_parse(words_gaze_into, words_crystal_ball, 0) && global[king_status] == KING_CAPTIVE) {
+	if (player_said_2(gaze_into, crystal_ball) && global[king_status] == KING_CAPTIVE) {
 		text_show(11615);
 		goto handled;
 	}
-	if (player_parse(words_gaze_into, words_crystal_ball, 0) && global[king_status] == KING_WITHOUT_SOUL) {
+	if (player_said_2(gaze_into, crystal_ball) && global[king_status] == KING_WITHOUT_SOUL) {
 		text_show(11616);
 		goto handled;
 	}
 
-	if (player_parse(words_close, words_door_to_north, 0)) {
+	if (player_said_2(close, door_to_north)) {
 		text_show(42);
 		goto handled;
 	}
 
-	if ((local->bear_status != FIRST_TIME_BEAR || local->bear_status != IS_A_BEAR_AGAIN) &&
-			!player_parse(words_look, 0) &&
-			!player_parse(words_take, 0) &&
-			!player_parse(words_push, 0) &&
-			!player_parse(words_open, 0) &&
-			!player_parse(words_put, 0) &&
-			!player_parse(words_talk_to, 0) &&
-			!player_parse(words_give, 0) &&
-			!player_parse(words_pull, 0) &&
-			!player_parse(words_close, 0) &&
-			!player_parse(words_throw, 0) &&
-			!player_parse(words_swim_to, 0) &&
-			!player_parse(words_swim_towards, 0)) {
+	if ((local->bear_status == FIRST_TIME_BEAR || local->bear_status == IS_A_BEAR_AGAIN) &&
+			!player_said_1(look) &&
+			!player_said_1(take) &&
+			!player_said_1(push) &&
+			!player_said_1(open) &&
+			!player_said_1(put) &&
+			!player_said_1(talk_to) &&
+			!player_said_1(give) &&
+			!player_said_1(pull) &&
+			!player_said_1(close) &&
+			!player_said_1(throw) &&
+			!player_said_1(swim_to) &&
+			!player_said_1(swim_towards)) {
 		text_show(32);
 		goto handled;
 	}
 
-	if (player_parse(words_heal, words_king) && global[king_status] != KING_WITH_SOUL) {
+	if (player_said_2(heal, king) && global[king_status] != KING_WITH_SOUL) {
 		text_show(11622);
 		goto handled;
 	}
 
-	if (player_parse(words_sword, words_attack, words_king, 0) ||
-			player_parse(words_sword, words_carve_up, words_king, 0) ||
-			player_parse(words_sword, words_thrust, words_king, 0)) {
+	if (player_said_3(sword, attack, king) ||
+			player_said_3(sword, carve_up, king) ||
+			player_said_3(sword, thrust, king)) {
 		if (global[king_status] == KING_CAPTIVE) {
 			text_show(11621);
 			goto handled;
diff --git a/engines/mads/madsv2/dragonsphere/rooms/room405.cpp b/engines/mads/madsv2/dragonsphere/rooms/room405.cpp
index 6282984f07d..8984a6cc692 100644
--- a/engines/mads/madsv2/dragonsphere/rooms/room405.cpp
+++ b/engines/mads/madsv2/dragonsphere/rooms/room405.cpp
@@ -1078,18 +1078,18 @@ static void process_conv_pid_guard() {
 	int me_trig_flag   = false;
 
 	switch (player_verb) {
-	case conv044_leave_b_b:
-		*conv_my_next_start = conv044_greet;
+	case conv044_enter_b_b:
+		*conv_my_next_start = conv044_repeater;
 		conv_abort();
-		local->pid_tent_action = LEAVE;
+		local->guard_action = OPEN_TENT;
 		you_trig_flag = true;
-		me_trig_flag  = true;
+		me_trig_flag = true;
 		break;
 
-	case conv044_enter_b_b:
-		*conv_my_next_start = conv044_repeater;
+	case conv044_leave_b_b:
+		*conv_my_next_start = conv044_greet;
 		conv_abort();
-		local->guard_action = OPEN_TENT;
+		local->pid_tent_action = LEAVE;
 		you_trig_flag = true;
 		me_trig_flag  = true;
 		break;


Commit: 20f6cc3eae81a825e7f3ea077839a06deda2b04e
    https://github.com/scummvm/scummvm/commit/20f6cc3eae81a825e7f3ea077839a06deda2b04e
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-06-03T21:17:36+10:00

Commit Message:
MADS: DRAGONSPHERE: Fixes for rescuing king from ice

Changed paths:
    engines/mads/madsv2/core/kernel.cpp
    engines/mads/madsv2/dragonsphere/rooms/room116.cpp


diff --git a/engines/mads/madsv2/core/kernel.cpp b/engines/mads/madsv2/core/kernel.cpp
index 2b30b0f0f28..401776fde6e 100644
--- a/engines/mads/madsv2/core/kernel.cpp
+++ b/engines/mads/madsv2/core/kernel.cpp
@@ -1805,15 +1805,14 @@ static void kernel_process_animation(int handle, int asynchronous) {
 
 				seg_id = image_list[id].segment_id;
 
-				// image_list[id].segment_id += KERNEL_SEGMENT_ANIMATION;
 				image_list[id].segment_id = (byte)(KERNEL_SEGMENT_ANIMATION + handle);
-				image_list[id].flags = series_list[image_list[id].series_id]->delta_series ? IMAGE_DELTA : IMAGE_UPDATE;
-				// if (kernel_anim[handle].anim->misc_any_packed) {
-				// if (image_list[id].series_id == (byte)kernel_anim[handle].anim->series_id[kernel_anim[handle].anim->misc_packed_series]) {
-				// series_id = image_list[id].series_id;
-				// sprite_data_load (series_list[series_id], image_list[id].sprite_id, series_list[series_id]->arena);
-				// }
-				// }
+				if (image_list[id].series_id < (SERIES_LIST_SIZE + SERIES_BONUS_SIZE) && series_list[image_list[id].series_id])
+					image_list[id].flags = series_list[image_list[id].series_id]->delta_series ? IMAGE_DELTA : IMAGE_UPDATE;
+				else
+					// WORKAROUND: For invalid series_id. This at least happens when rescuing the king from ice
+					// in Dragonsphere whilst in bear form - the series_id == 0xff
+					image_list[id].flags = IMAGE_ERASE;
+
 				if (hot >= 0) {
 					kernel_hot_check(hot, id, seg_id);
 				}
diff --git a/engines/mads/madsv2/dragonsphere/rooms/room116.cpp b/engines/mads/madsv2/dragonsphere/rooms/room116.cpp
index d346f210b46..347a79d569e 100644
--- a/engines/mads/madsv2/dragonsphere/rooms/room116.cpp
+++ b/engines/mads/madsv2/dragonsphere/rooms/room116.cpp
@@ -406,7 +406,7 @@ static void room_116_pre_parser() {
 			player.commands_allowed = false;
 			player.ready_to_walk = false;
 			kernel_seq_delete(seq[fx_bear_morph]);
-			seq[fx_bear_morph] = kernel_seq_backward(ss[fx_bear_morph], false, 8, 1, 0, 0);
+			seq[fx_bear_morph] = kernel_seq_backward(ss[fx_bear_morph], false, 8, 0, 0, 1);
 			kernel_seq_depth(seq[fx_bear_morph], 4);
 			kernel_seq_trigger(seq[fx_bear_morph], KERNEL_TRIGGER_EXPIRE, 0, 1);
 			break;
@@ -502,7 +502,7 @@ static void room_116_parser() {
 			}
 
 			player.walker_visible = false;
-			seq[fx_open_door] = kernel_seq_pingpong(ss[fx_open_door], false, 8, 2, 0, 0);
+			seq[fx_open_door] = kernel_seq_pingpong(ss[fx_open_door], false, 8, 0, 0, 2);
 			kernel_seq_player(seq[fx_open_door], -1);
 			kernel_seq_trigger(seq[fx_open_door], KERNEL_TRIGGER_SPRITE, 2, 1);
 			kernel_seq_trigger(seq[fx_open_door], KERNEL_TRIGGER_EXPIRE, 0, 3);
@@ -511,7 +511,7 @@ static void room_116_parser() {
 		case 1:
 			kernel_seq_delete(seq[fx_door]);
 			sound_play(N_DoorOpens);
-			seq[fx_open_door] = kernel_seq_forward(ss[fx_door], false, 7, 0, 0, 0);
+			seq[fx_open_door] = kernel_seq_forward(ss[fx_door], false, 7, 0, 0, 1);
 			kernel_seq_depth(seq[fx_door], 14);
 			kernel_seq_trigger(seq[fx_door], KERNEL_TRIGGER_EXPIRE, 0, 2);
 			break;
@@ -529,7 +529,7 @@ static void room_116_parser() {
 		case 4:
 			player_walk(START_X_ROOM_119, START_Y_ROOM_119, FACING_EAST);
 
-			if (global[king_status] == KING_WITH_SOUL && global[king_is_in_stairwell])
+			if (global[king_status] != KING_WITH_SOUL || global[king_is_in_stairwell])
 				player_walk_trigger(5);
 			break;
 		case 5:
@@ -637,11 +637,11 @@ static void room_116_parser() {
 
 	// Bear grabs king / gives soul to king
 	if ((player_said_2(push, king) ||
-			player_said_2(pull, king) ||
-			player_said_2(take, king) ||
-			player_said_3(put, king, cave_floor)) &&
-			global[king_status] == KING_CAPTIVE) {
-		if (local->bear_status == HAS_NEVER_BEEN_A_BEAR) {
+				player_said_2(pull, king) ||
+				player_said_2(take, king) ||
+				player_said_3(put, king, cave_floor)) &&
+				global[king_status] == KING_CAPTIVE) {
+		if (local->bear_status != HAS_NEVER_BEEN_A_BEAR) {
 			kernel_seq_delete(seq[fx_bear_morph]);
 			player.commands_allowed = false;
 			local->anim_2_running = true;
@@ -658,12 +658,12 @@ static void room_116_parser() {
 				player.walker_visible = false;
 				seq[fx_open_door] = kernel_seq_pingpong(ss[fx_open_door], 0, 9, 0, 0, 2);
 				kernel_seq_player(seq[fx_open_door], -1);
-				kernel_seq_trigger(seq[fx_open_door], 0, 0, 8);
+				kernel_seq_trigger(seq[fx_open_door], 0, 0, 2);
 				break;
 
 			case 2:
 				player.walker_visible = true;
-				kernel_synch(2, 0, 1, seq[fx_open_door]);
+				kernel_synch(KERNEL_PLAYER, 0, KERNEL_SERIES, seq[fx_open_door]);
 				kernel_timing_trigger(12, 3);
 				sound_play(N_GrabKing);
 				break;



From noreply at scummvm.org  Wed Jun  3 11:46:49 2026
From: noreply at scummvm.org (kelmer44)
Date: Wed,  3 Jun 2026 11:46:49 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 bff97c6edc65f984485531335c8fa494ffc8d98d
Message-ID: <20260603114649.C6B1D700591@ron.scummvm.net>

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

Summary:
bff97c6edc PELROCK: Fix remaining PVS-Studio issues. PVS-Studio V557, V575, V769, V701


Commit: bff97c6edc65f984485531335c8fa494ffc8d98d
    https://github.com/scummvm/scummvm/commit/bff97c6edc65f984485531335c8fa494ffc8d98d
Author: kelmer (kelmer at gmail.com)
Date: 2026-06-03T13:46:42+02:00

Commit Message:
PELROCK: Fix remaining PVS-Studio issues. PVS-Studio V557, V575, V769, V701

Changed paths:
    engines/pelrock/pelrock.cpp
    engines/pelrock/sound.cpp
    engines/pelrock/util.cpp


diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 6299c1daf93..de64f32b278 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -2127,7 +2127,7 @@ void PelrockEngine::endingScene() {
 			idleAnim.animData[0] = new byte[sprite->stride];
 			extractSingleFrame(legsAnimData, idleAnim.animData[0], 0, sprite->w, sprite->h);
 			sprite->animData[0] = idleAnim;
-			sprite->animData[1] = mainAnim;
+			sprite->animData[1] = mainAnim; //-V557
 		} else {
 			sprite->animData[0] = mainAnim;
 		}
diff --git a/engines/pelrock/sound.cpp b/engines/pelrock/sound.cpp
index 2ea43433f2a..940a1336a24 100644
--- a/engines/pelrock/sound.cpp
+++ b/engines/pelrock/sound.cpp
@@ -184,6 +184,7 @@ int SoundManager::playSound(SonidoFile sound, int channel, int loopCount) {
 	sonidosFile.read(data, sound.size);
 	sonidosFile.close();
 
+	assert(data);
 	SoundFormat format = detectFormat(data, sound.size);
 	uint32 sampleRate = getSampleRate(data, format);
 	Audio::SeekableAudioStream *stream = nullptr;
@@ -203,7 +204,6 @@ int SoundManager::playSound(SonidoFile sound, int channel, int loopCount) {
 		uint32 pcmSize = sound.size - headerSize;
 		byte *pcmData = (byte *)malloc(pcmSize);
 		assert(pcmData);
-		assert(data);
 		memcpy(pcmData, data + headerSize, pcmSize);
 		free(data);
 
diff --git a/engines/pelrock/util.cpp b/engines/pelrock/util.cpp
index b9cbfef8ba9..2af0fd7f944 100644
--- a/engines/pelrock/util.cpp
+++ b/engines/pelrock/util.cpp
@@ -189,7 +189,12 @@ void readUntilBuda(Common::SeekableReadStream *stream, uint32 startPos, byte *&b
 		byte b = stream->readByte();
 		if (pos + 1 > bufferSize) {
 			bufferSize *= 2;
-			buffer = (byte *)realloc(buffer, bufferSize);
+			byte *tmp = (byte *)realloc(buffer, bufferSize);
+			if (!tmp) {
+				free(buffer);
+				error("realloc failed in util.cpp");
+			}
+			buffer = tmp;
 		}
 		buffer[pos++] = b;
 



From noreply at scummvm.org  Wed Jun  3 12:33:49 2026
From: noreply at scummvm.org (neuromancer)
Date: Wed,  3 Jun 2026 12:33:49 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 ddfbc182ef404711cc508543e10a687038825dab
Message-ID: <20260603123349.7EEF0700591@ron.scummvm.net>

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:
a3cd1a9917 SCUMM: RA2: avoid incorrect palettes in transition frames
bfe931de30 SCUMM: RA2: correctly center input when a new gameplay section starts
ddfbc182ef SCUMM: RA2: refactor nutcracker/font code to isolate it from the rest of the scumm games


Commit: a3cd1a99175cef34d0830c125f79cf40405cd047
    https://github.com/scummvm/scummvm/commit/a3cd1a99175cef34d0830c125f79cf40405cd047
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-03T14:31:53+02:00

Commit Message:
SCUMM: RA2: avoid incorrect palettes in transition frames

Changed paths:
    engines/scumm/insane/rebel2/iact.cpp
    engines/scumm/insane/rebel2/rebel.h
    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/iact.cpp b/engines/scumm/insane/rebel2/iact.cpp
index af30f749a81..cb34ed8fffb 100644
--- a/engines/scumm/insane/rebel2/iact.cpp
+++ b/engines/scumm/insane/rebel2/iact.cpp
@@ -1236,43 +1236,9 @@ void InsaneRebel2::handleOpcode6Handler25(byte *renderBitmap, Common::SeekableRe
 		_player->_fobjOffsetY = _rebelViewOffsetY;
 	}
 
-	// Draw corridor overlay OPAQUELY (FUN_00428A10 in original, line 216).
+	// Draw corridor overlay opaquely (FUN_00428a10 in original, line 216).
 	// This wipes previous frame content so codec 23 delta skip regions show clean corridor.
-	if (renderBitmap) {
-		EmbeddedSanFrame &corridorOverlay = _rebelEmbeddedHud[4];
-		if (corridorOverlay.valid && corridorOverlay.pixels) {
-			int pitch = (_player && _player->_width > 0) ? _player->_width : 320;
-			int bufHeight = (_player && _player->_height > 0) ? _player->_height : 200;
-
-			int srcOffsetX = 0;
-			int srcOffsetY = 0;
-			int destX = _rebelViewOffsetX;
-			int destY = _rebelViewOffsetY;
-			int drawWidth = corridorOverlay.width;
-			int drawHeight = corridorOverlay.height;
-
-			if (destX < 0) { srcOffsetX = -destX; drawWidth -= srcOffsetX; destX = 0; }
-			if (destY < 0) { srcOffsetY = -destY; drawHeight -= srcOffsetY; destY = 0; }
-			if (destX + drawWidth > pitch)
-				drawWidth = pitch - destX;
-			if (destY + drawHeight > bufHeight)
-				drawHeight = bufHeight - destY;
-			if (drawWidth > corridorOverlay.width - srcOffsetX)
-				drawWidth = corridorOverlay.width - srcOffsetX;
-			if (drawHeight > corridorOverlay.height - srcOffsetY)
-				drawHeight = corridorOverlay.height - srcOffsetY;
-
-			if (drawWidth > 0 && drawHeight > 0) {
-				for (int y = 0; y < drawHeight; y++) {
-					memcpy(renderBitmap + (destY + y) * pitch + destX,
-						   corridorOverlay.pixels + (srcOffsetY + y) * corridorOverlay.width + srcOffsetX,
-						   drawWidth);
-				}
-			}
-			debug("Rebel2 Opcode 6: Corridor overlay drawn at (%d,%d) size(%d,%d)",
-				_rebelViewOffsetX, _rebelViewOffsetY, corridorOverlay.width, corridorOverlay.height);
-		}
-	}
+	drawHandler25CorridorOverlay(renderBitmap);
 }
 
 // ScummVM refactor helper for opcode 6 Handler 0x26, not a separate retail function.
diff --git a/engines/scumm/insane/rebel2/rebel.h b/engines/scumm/insane/rebel2/rebel.h
index c4ed62eb146..c51f4dfa214 100644
--- a/engines/scumm/insane/rebel2/rebel.h
+++ b/engines/scumm/insane/rebel2/rebel.h
@@ -765,6 +765,7 @@ public:
 	// Render a decoded embedded frame to the video buffer
 	// Handles transparency (color 0 and 231) and boundary checks
 	void renderEmbeddedFrame(byte *renderBitmap, const EmbeddedSanFrame &frame, int userId);
+	void drawHandler25CorridorOverlay(byte *renderBitmap);
 
 	int16 _rebelLinks[512][3]; // Dependency links: Slot 0 (Disable on death), Slot 1/2 (Enable on death)
 	void clearBit(int n);
diff --git a/engines/scumm/insane/rebel2/render.cpp b/engines/scumm/insane/rebel2/render.cpp
index e5a373e253f..890a19c9166 100644
--- a/engines/scumm/insane/rebel2/render.cpp
+++ b/engines/scumm/insane/rebel2/render.cpp
@@ -132,13 +132,18 @@ void InsaneRebel2::renderEmbeddedFrame(byte *renderBitmap, const EmbeddedSanFram
 	                          _rebelHandler == 0x26 || _rebelHandler == 0x19);
 
 	// Handler 25 overlays:
-	// - userId 4 (corridor overlay): Draw during procPostRendering at view offset, NOT immediately
+	// - userId 4 (corridor overlay): draw immediately at the current view offset.
+	//   FUN_0041cadb case 6/par4=4 decodes DAT_00482268, then calls
+	//   FUN_00428a10(param_1, 0, DAT_0045790c, DAT_0045790e, DAT_00482268).
 	// - userId 6, 7 (static overlays): Draw immediately (they don't move)
+	if (_rebelHandler == 0x19 && userId == 4) {
+		drawHandler25CorridorOverlay(renderBitmap);
+		return;
+	}
 	if (_rebelHandler == 0x19 && (userId == 6 || userId == 7)) {
 		skipImmediateDraw = false;
 		debug("Rebel2: Handler 25 static overlay userId=%d - forcing immediate draw", userId);
 	}
-	// userId 4 should NOT draw immediately - it will be drawn at view offset each frame
 
 	if (!frame.valid || !renderBitmap || skipImmediateDraw) {
 		if (skipImmediateDraw && frame.valid) {
@@ -156,6 +161,56 @@ void InsaneRebel2::renderEmbeddedFrame(byte *renderBitmap, const EmbeddedSanFram
 	debug("Rebel2: Rendered embedded HUD %d at (%d,%d)", userId, frame.renderX, frame.renderY);
 }
 
+void InsaneRebel2::drawHandler25CorridorOverlay(byte *renderBitmap) {
+	if (!renderBitmap)
+		return;
+
+	EmbeddedSanFrame &corridorOverlay = _rebelEmbeddedHud[4];
+	if (!isValidEmbeddedFrame(corridorOverlay))
+		return;
+
+	int pitch = (_player && _player->_width > 0) ? _player->_width : 320;
+	int bufHeight = (_player && _player->_height > 0) ? _player->_height : 200;
+
+	int srcOffsetX = 0;
+	int srcOffsetY = 0;
+	int destX = _rebelViewOffsetX;
+	int destY = _rebelViewOffsetY;
+	int drawWidth = corridorOverlay.width;
+	int drawHeight = corridorOverlay.height;
+
+	if (destX < 0) {
+		srcOffsetX = -destX;
+		drawWidth -= srcOffsetX;
+		destX = 0;
+	}
+	if (destY < 0) {
+		srcOffsetY = -destY;
+		drawHeight -= srcOffsetY;
+		destY = 0;
+	}
+	if (destX + drawWidth > pitch)
+		drawWidth = pitch - destX;
+	if (destY + drawHeight > bufHeight)
+		drawHeight = bufHeight - destY;
+	if (drawWidth > corridorOverlay.width - srcOffsetX)
+		drawWidth = corridorOverlay.width - srcOffsetX;
+	if (drawHeight > corridorOverlay.height - srcOffsetY)
+		drawHeight = corridorOverlay.height - srcOffsetY;
+
+	if (drawWidth <= 0 || drawHeight <= 0)
+		return;
+
+	for (int y = 0; y < drawHeight; y++) {
+		memcpy(renderBitmap + (destY + y) * pitch + destX,
+			   corridorOverlay.pixels + (srcOffsetY + y) * corridorOverlay.width + srcOffsetX,
+			   drawWidth);
+	}
+
+	debug("Rebel2 Handler25: Corridor overlay drawn at (%d,%d) size(%d,%d)",
+		_rebelViewOffsetX, _rebelViewOffsetY, corridorOverlay.width, corridorOverlay.height);
+}
+
 //
 // loadEmbeddedSan -- Decode an embedded SAN (ANIM/FOBJ) from IACT opcode 8 data.
 //
@@ -2866,10 +2921,9 @@ void InsaneRebel2::renderEmbeddedHudOverlays(byte *renderBitmap, int pitch, int
 		if (!isValidEmbeddedFrame(frame))
 			continue;
 
-		// Handler 25: Skip slot 4 (corridor overlay) in post-rendering.
-		// The corridor is a full background image (no color 0 transparent center).
-		// Drawing it here would cover enemies. It's already drawn in procPreRendering
-		// with transparency to preserve frame persistence for codec 23 delta.
+		// Handler 25: skip slot 4 (corridor overlay) in post-rendering.
+		// FUN_0041cadb draws it opaquely from opcode 6 and immediately after
+		// loading par4=4; drawing it here would cover enemies.
 		if (_rebelHandler == 25 && hudSlot == 4) {
 			continue;
 		}
diff --git a/engines/scumm/smush/rebel/smush_player_ra2.cpp b/engines/scumm/smush/rebel/smush_player_ra2.cpp
index e24b8f56d3c..58d1c3fdf18 100644
--- a/engines/scumm/smush/rebel/smush_player_ra2.cpp
+++ b/engines/scumm/smush/rebel/smush_player_ra2.cpp
@@ -89,6 +89,7 @@ void SmushPlayerRebel2::initGamePlayerFields() {
 	_ra2FrameSourceSkipY = 0;
 	_ra2FrameObjectSurfaceWidth = 0;
 	_ra2FrameObjectSurfaceHeight = 0;
+	_ra2PendingAnimHeaderPalette = false;
 	_scrollX = 0;
 	_scrollY = 0;
 }
@@ -106,10 +107,12 @@ void SmushPlayerRebel2::destroyGamePlayerFields() {
 
 /**
  * RA2-specific initialization in SmushPlayer::init().
- * Re-pushes the SMUSH palette (videos without NPAL inherit from previous),
- * and handles background preservation between cinematic and gameplay videos.
+ * Re-pushes the SMUSH palette for videos that inherit it from the previous video,
+ * and initializes the screen target state before AHDR/FOBJ selects dimensions.
  */
 void SmushPlayerRebel2::initGameVideoState() {
+	_ra2PendingAnimHeaderPalette = false;
+
 	// Re-push the SMUSH palette to the system. Videos like O_LEVEL.SAN
 	// have no NPAL chunk and inherit the palette from the previous video.
 	// Since play() resets _palDirtyMin/Max, the palette would never be pushed otherwise.
@@ -120,16 +123,15 @@ void SmushPlayerRebel2::initGameVideoState() {
 	_width = _vm->_screenWidth;
 	_height = _vm->_screenHeight;
 
-	// Handle background preservation between videos:
-	// - Cinematic videos (flags 0x20) clear the buffer for a fresh start
-	// - Gameplay videos (flags 0x08) preserve the existing screen content
+	// Keep ScummVM's virtual screen consistent with FUN_00424d70: bit 0x20
+	// controls per-frame clearing. Videos that clear every frame can also start
+	// from a cleared target; videos with bit 0x20 set preserve until their first
+	// decoded frame overwrites or composes over it.
 	if (_dst != nullptr) {
 		VirtScreen *vs = &_vm->_virtscr[kMainVirtScreen];
-		if ((_curVideoFlags & 0x08) == 0) {
-			// Cinematic mode (flags 0x20) - clear buffer for fresh video
+		if ((_curVideoFlags & 0x20) == 0) {
 			memset(_dst, 0, vs->w * vs->h);
 		}
-		// Gameplay mode: no-op, the existing screen content is preserved.
 	}
 }
 
@@ -397,8 +399,9 @@ void SmushPlayerRebel2::adjustGamePalette() {
 }
 
 bool SmushPlayerRebel2::shouldLoadAnimHeaderPalette() const {
-	// Original RA2 AHDR handler skips the embedded palette when video flag 0x400 is set.
-	return (_curVideoFlags & 0x400) == 0;
+	// RA2 copies the AHDR palette in handleGameAnimHeader() so it can match
+	// FUN_00424640/FUN_00424540 timing without changing the generic player.
+	return false;
 }
 
 void SmushPlayerRebel2::ra2HandleDeltaPalette(int32 subSize, Common::SeekableReadStream &b) {
@@ -450,6 +453,20 @@ void SmushPlayerRebel2::ra2HandleDeltaPalette(int32 subSize, Common::SeekableRea
 // backing bitmap, but active low-res gameplay dimensions are selected later by
 // IACT/FOBJ state; keep the virtual-screen pitch safe until then.
 bool SmushPlayerRebel2::handleGameAnimHeader(byte *headerContent) {
+	if (!_skipPalette && (_curVideoFlags & 0x400) == 0) {
+		memcpy(_pal, headerContent + 6, sizeof(_pal));
+		adjustGamePalette();
+
+		// initGameVideoState() marks the previous palette dirty for videos that
+		// inherit it. AHDR replaces _pal before the first FRME, so clear that
+		// inherited dirty range and delay the new AHDR palette until frame start.
+		// Original RA2 marks it dirty from FUN_00424640, then applies it from
+		// FUN_00424540 after FUN_00424d70 has processed a frame.
+		_palDirtyMin = 256;
+		_palDirtyMax = -1;
+		_ra2PendingAnimHeaderPalette = true;
+	}
+
 	int width = READ_LE_UINT16(&headerContent[4]);
 	int height = READ_LE_UINT16(&headerContent[6]);
 
@@ -838,6 +855,11 @@ void SmushPlayerRebel2::handleGameFrameObjectPost(int codec, const byte *data, i
 void SmushPlayerRebel2::handleGameFrameStart() {
 	_hasFrameFobjForGost = false;
 
+	if (_ra2PendingAnimHeaderPalette) {
+		setDirtyColors(0, 255);
+		_ra2PendingAnimHeaderPalette = false;
+	}
+
 	// FUN_00424d70 clears the target buffer before decoding a frame
 	// unless playback flags contain 0x20. Codec 21 frames in levels like
 	// LEV05/05PLAY.SAN only contain non-zero literals, so stale skipped pixels
diff --git a/engines/scumm/smush/rebel/smush_player_ra2.h b/engines/scumm/smush/rebel/smush_player_ra2.h
index c10a544cb76..415401af885 100644
--- a/engines/scumm/smush/rebel/smush_player_ra2.h
+++ b/engines/scumm/smush/rebel/smush_player_ra2.h
@@ -85,6 +85,7 @@ private:
 	int _ra2FrameSourceSkipY;
 	int _ra2FrameObjectSurfaceWidth;
 	int _ra2FrameObjectSurfaceHeight;
+	bool _ra2PendingAnimHeaderPalette;
 };
 
 } // End of namespace Scumm


Commit: bfe931de30a8d0c3d36a9c9c490a1828a400812d
    https://github.com/scummvm/scummvm/commit/bfe931de30a8d0c3d36a9c9c490a1828a400812d
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-03T14:31:53+02:00

Commit Message:
SCUMM: RA2: correctly center input when a new gameplay section starts

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


diff --git a/engines/scumm/insane/rebel2/levels.cpp b/engines/scumm/insane/rebel2/levels.cpp
index 63d49134bba..8b010dca334 100644
--- a/engines/scumm/insane/rebel2/levels.cpp
+++ b/engines/scumm/insane/rebel2/levels.cpp
@@ -19,6 +19,7 @@
  *
  */
 
+#include "common/events.h"
 #include "common/system.h"
 
 #include "graphics/cursorman.h"
@@ -34,6 +35,15 @@ namespace Scumm {
 static const int kRebel2GameplayAimCenterX = 160;
 static const int kRebel2GameplayAimCenterY = 100;
 
+static void purgeRebel2GameplayInputEvents(Common::EventManager *eventMan) {
+	if (!eventMan)
+		return;
+
+	eventMan->getEventDispatcher()->clearEvents();
+	eventMan->purgeMouseEvents();
+	eventMan->purgeKeyboardEvents();
+}
+
 // ---------------------------------------------------------------------------
 // Level Loading System
 // ---------------------------------------------------------------------------
@@ -174,6 +184,7 @@ void InsaneRebel2::playMissionBriefing() {
 // Resets handler to 0 (no HUD) and sets flags to 0x28 (cinematic + buffer preserve).
 // All wrapper functions (FUN_00417168/4171c5/417ab2/417327) add | 8 before calling FUN_0041f4d0.
 void InsaneRebel2::playCinematic(const char *filename) {
+	_gameplaySectionActive = false;
 	_rebelHandler = 0;
 	_rebelStatusBarSprite = 0;  // No status bar during cinematics
 
@@ -188,6 +199,7 @@ void InsaneRebel2::playCinematic(const char *filename) {
 void InsaneRebel2::playVideoWithText(const char *filename, int textID, int textX, int textY,
                                      int fadeInFrame, int fadeOutFrame) {
 
+	_gameplaySectionActive = false;
 	_rebelHandler = 0;
 	_rebelStatusBarSprite = 0;
 
@@ -258,6 +270,7 @@ void InsaneRebel2::playLevelBegin(int levelId) {
 // playLevelEnd -- Level completion video (FUN_00417327).
 void InsaneRebel2::playLevelEnd(int levelId) {
 
+	_gameplaySectionActive = false;
 	_rebelHandler = 0;
 	_rebelStatusBarSprite = 0;  // No status bar during end cinematic
 
@@ -276,6 +289,7 @@ void InsaneRebel2::playLevelEnd(int levelId) {
 // playLevelRetry -- Retry prompt video (LEVXX/XXRETRY.SAN, FUN_00417168).
 void InsaneRebel2::playLevelRetry(int levelId) {
 
+	_gameplaySectionActive = false;
 	_rebelHandler = 0;
 	_rebelStatusBarSprite = 0;  // Reset for retry - will be set by IACT opcode 6 if needed
 
@@ -294,6 +308,7 @@ void InsaneRebel2::playLevelRetry(int levelId) {
 // playLevelGameOver -- Game over video (FUN_00417ab2).
 void InsaneRebel2::playLevelGameOver(int levelId) {
 
+	_gameplaySectionActive = false;
 	_rebelHandler = 0;
 	_rebelStatusBarSprite = 0;  // No status bar during game over cinematic
 
@@ -367,6 +382,9 @@ void InsaneRebel2::playCreditsSequence() {
 }
 
 void InsaneRebel2::centerGameplayAim() {
+	Common::EventManager *eventMan = _vm->_system->getEventManager();
+	purgeRebel2GameplayInputEvents(eventMan);
+
 	_vm->_mouse.x = kRebel2GameplayAimCenterX;
 	_vm->_mouse.y = kRebel2GameplayAimCenterY;
 
@@ -375,6 +393,7 @@ void InsaneRebel2::centerGameplayAim() {
 	_gamepadAimActive = false;
 
 	smush_warpMouse(kRebel2GameplayAimCenterX, kRebel2GameplayAimCenterY, -1);
+	purgeRebel2GameplayInputEvents(eventMan);
 }
 
 // runLevel -- Main level dispatcher, calls per-level handlers.
@@ -414,7 +433,7 @@ int InsaneRebel2::runLevel(int levelId) {
 	// The original hides the cursor (ShowCursor(0)) and relies on Windows confining
 	// the mouse to the game window. Without locking, the cursor can escape the
 	// ScummVM window making the ship uncontrollable.
-	centerGameplayAim();
+	_gameplaySectionActive = false;
 	CursorMan.showMouse(false);
 	g_system->lockMouse(true);
 
@@ -677,6 +696,7 @@ Common::String InsaneRebel2::selectDeathVideoVariant(int levelId, int phase, int
 // playLevelDeathVariant -- Death video with variant selection.
 void InsaneRebel2::playLevelDeathVariant(int levelId, int phase, int frame) {
 
+	_gameplaySectionActive = false;
 	_rebelHandler = 0;
 	_rebelStatusBarSprite = 0;  // No status bar during death cinematic
 
@@ -703,6 +723,7 @@ void InsaneRebel2::playLevelDeathVariant(int levelId, int phase, int frame) {
 // playLevelRetryVariant -- Phase-specific retry video.
 void InsaneRebel2::playLevelRetryVariant(int levelId, int phase) {
 
+	_gameplaySectionActive = false;
 	_rebelHandler = 0;
 	_rebelStatusBarSprite = 0;  // Reset for retry - will be set by IACT opcode 6 if needed
 
diff --git a/engines/scumm/insane/rebel2/rebel.cpp b/engines/scumm/insane/rebel2/rebel.cpp
index d8dfa9bc0b0..1e5f241014c 100644
--- a/engines/scumm/insane/rebel2/rebel.cpp
+++ b/engines/scumm/insane/rebel2/rebel.cpp
@@ -519,6 +519,7 @@ InsaneRebel2::InsaneRebel2(ScummEngine_v7 *scumm) {
 	_joystickAxisX = 0;
 	_joystickAxisY = 0;
 	_gamepadAimActive = false;
+	_gameplaySectionActive = false;
 	_lastGameplayMenuCloseTime = 0;
 	_lastMenuGamepadNavigationTime = 0;
 
diff --git a/engines/scumm/insane/rebel2/rebel.h b/engines/scumm/insane/rebel2/rebel.h
index c51f4dfa214..d833df9de23 100644
--- a/engines/scumm/insane/rebel2/rebel.h
+++ b/engines/scumm/insane/rebel2/rebel.h
@@ -468,6 +468,8 @@ public:
 
 	// Reset gameplay aim to the original centered mouse/joystick baseline.
 	void centerGameplayAim();
+	// Tracks consecutive recorded gameplay SANs so wave-loop videos do not recenter aim.
+	bool _gameplaySectionActive;
 
 	// 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 77f5c4cbdb0..63dc995ce41 100644
--- a/engines/scumm/insane/rebel2/runlevels.cpp
+++ b/engines/scumm/insane/rebel2/runlevels.cpp
@@ -191,11 +191,15 @@ InsaneRebel2::WaveEndResult InsaneRebel2::processWaveEnd(int16 mask, int16 *budg
 bool InsaneRebel2::playLevelSegment(const char *filename, uint16 flags, bool recordFrame) {
 	SmushPlayer *splayer = ((ScummEngine_v7 *)_vm)->_splayer;
 
-	// Retail reset helpers clear the centered input axes before gameplay starts.
-	// Do the same for playable segments, but keep seamless continuation videos
-	// (0x40, e.g. 13PLAY_B) from snapping the aim point mid-sequence.
-	if (recordFrame && (flags & 0x08) != 0 && (flags & 0x40) == 0)
-		centerGameplayAim();
+	const bool isRecordedGameplay = recordFrame && (flags & 0x08) != 0;
+	if (isRecordedGameplay) {
+		// Center only at the section boundary; looped wave videos are continuations.
+		if (!_gameplaySectionActive && (flags & 0x40) == 0)
+			centerGameplayAim();
+		_gameplaySectionActive = true;
+	} else {
+		_gameplaySectionActive = false;
+	}
 
 	splayer->setCurVideoFlags(flags);
 	splayer->play(filename, 15);


Commit: ddfbc182ef404711cc508543e10a687038825dab
    https://github.com/scummvm/scummvm/commit/ddfbc182ef404711cc508543e10a687038825dab
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-03T14:31:53+02:00

Commit Message:
SCUMM: RA2: refactor nutcracker/font code to isolate it from the rest of the scumm games

Changed paths:
  A engines/scumm/smush/rebel/font_rebel2.cpp
  A engines/scumm/smush/rebel/font_rebel2.h
    engines/scumm/insane/rebel2/iact.cpp
    engines/scumm/insane/rebel2/menu.cpp
    engines/scumm/insane/rebel2/rebel.cpp
    engines/scumm/insane/rebel2/rebel.h
    engines/scumm/insane/rebel2/render.cpp
    engines/scumm/module.mk
    engines/scumm/nut_renderer.cpp
    engines/scumm/smush/rebel/smush_multi_font.cpp
    engines/scumm/smush/rebel/smush_multi_font.h


diff --git a/engines/scumm/insane/rebel2/iact.cpp b/engines/scumm/insane/rebel2/iact.cpp
index cb34ed8fffb..c7d1bf5ca9f 100644
--- a/engines/scumm/insane/rebel2/iact.cpp
+++ b/engines/scumm/insane/rebel2/iact.cpp
@@ -27,8 +27,8 @@
 #include "scumm/scumm_v7.h"
 
 #include "scumm/smush/smush_player.h"
-#include "scumm/smush/smush_font.h"
 #include "scumm/smush/rebel/codec_ra2.h"
+#include "scumm/smush/rebel/font_rebel2.h"
 
 #include "scumm/insane/rebel2/rebel.h"
 
@@ -2588,12 +2588,16 @@ void InsaneRebel2::iactRebel2Opcode9(byte *renderBitmap, Common::SeekableReadStr
 	// "subtitles" setting and the in-game TEXT toggle (same ConfMan key). The chunk is
 	// still fully parsed above so stream consumption is unaffected.
 	if (ConfMan.getBool("subtitles")) {
+		Rebel2FontSet fontSet;
+		fontSet.numFonts = 1;
+		fontSet.fonts[0] = _rebelMsgFont;
+
 		if (textFlags & 0x04) {
 			// Word-wrapped text
-			_rebelMsgFont->drawStringWrap(convertedText, renderBitmap, clipRect, posX, posY, textColor, styleFlags);
+			drawRebel2StringWrap(fontSet, convertedText, dstIdx, renderBitmap, clipRect, posX, posY, width, textColor, styleFlags);
 		} else {
 			// Single-line text
-			_rebelMsgFont->drawString(convertedText, renderBitmap, clipRect, posX, posY, textColor, styleFlags);
+			drawRebel2String(fontSet, convertedText, dstIdx, renderBitmap, clipRect, posX, posY, width, textColor, styleFlags);
 		}
 	}
 
diff --git a/engines/scumm/insane/rebel2/menu.cpp b/engines/scumm/insane/rebel2/menu.cpp
index 83a5f178ebb..b238e163ce7 100644
--- a/engines/scumm/insane/rebel2/menu.cpp
+++ b/engines/scumm/insane/rebel2/menu.cpp
@@ -33,7 +33,7 @@
 #include "scumm/scumm_v7.h"
 
 #include "scumm/smush/smush_player.h"
-#include "scumm/smush/smush_font.h"
+#include "scumm/smush/rebel/font_rebel2.h"
 #include "scumm/smush/rebel/smush_multi_font.h"
 
 #include "scumm/insane/rebel2/rebel.h"
@@ -416,8 +416,7 @@ void InsaneRebel2::drawMenuString(byte *renderBitmap, const char *str, int x, in
 		if (!curFont || c >= curFont->getNumChars()) continue;
 		int charW = curFont->getCharWidth(c);
 		if (x >= 0 && y >= 0 && charW > 0)
-			curFont->drawCharV7(renderBitmap, clipRect, x, y, pitch, curColor,
-			                    kStyleAlignLeft, c, false, false);
+			drawRebel2Char(curFont, renderBitmap, clipRect, x, y, pitch, curColor, c);
 		x += charW;
 	}
 }
diff --git a/engines/scumm/insane/rebel2/rebel.cpp b/engines/scumm/insane/rebel2/rebel.cpp
index 1e5f241014c..eef134ae7aa 100644
--- a/engines/scumm/insane/rebel2/rebel.cpp
+++ b/engines/scumm/insane/rebel2/rebel.cpp
@@ -41,7 +41,7 @@
 #include "scumm/imuse_digi/dimuse_engine.h"
 
 #include "scumm/smush/smush_player.h"
-#include "scumm/smush/smush_font.h"
+#include "scumm/smush/rebel/font_rebel2.h"
 
 #include "scumm/insane/rebel2/rebel.h"
 
@@ -140,7 +140,7 @@ InsaneRebel2::InsaneRebel2(ScummEngine_v7 *scumm) {
 	_smush_cockpitNut = new NutRenderer(_vm, "SYSTM/DISPFONT.NUT");
 
 	// Load DIHIFONT.NUT for in-video messages/subtitles (Opcode 9)
-	_rebelMsgFont = new SmushFont(_vm, "SYSTM/DIHIFONT.NUT", true);
+	_rebelMsgFont = makeRebel2Font(_vm, "SYSTM/DIHIFONT.NUT");
 
 	// Load menu system fonts (from info.md - FUN_403BD0 lines 302-348)
 	// In low resolution mode, fonts are loaded as a linked list:
@@ -148,9 +148,9 @@ InsaneRebel2::InsaneRebel2(ScummEngine_v7 *scumm) {
 	//   Font 1 (^f01): SMALFONT.NUT - Small font for format code switching
 	//   Font 2 (^f02): TITLFONT.NUT - Title font
 	//   Font 3 (^f03): POVFONT.NUT - POV font (not loaded here)
-	_smush_talkfontNut = new NutRenderer(_vm, "SYSTM/TALKFONT.NUT");
-	_smush_smalfontNut = new NutRenderer(_vm, "SYSTM/SMALFONT.NUT");
-	_smush_titlefontNut = new NutRenderer(_vm, "SYSTM/TITLFONT.NUT");
+	_smush_talkfontNut = makeRebel2Font(_vm, "SYSTM/TALKFONT.NUT");
+	_smush_smalfontNut = makeRebel2Font(_vm, "SYSTM/SMALFONT.NUT");
+	_smush_titlefontNut = makeRebel2Font(_vm, "SYSTM/TITLFONT.NUT");
 
 	_pauseOverlayActive = false;
 	memset(_savedPausePalette, 0, sizeof(_savedPausePalette));
diff --git a/engines/scumm/insane/rebel2/rebel.h b/engines/scumm/insane/rebel2/rebel.h
index d833df9de23..45ed0628a66 100644
--- a/engines/scumm/insane/rebel2/rebel.h
+++ b/engines/scumm/insane/rebel2/rebel.h
@@ -483,7 +483,7 @@ public:
 	NutRenderer *_smush_cockpitNut;
 
 	// Font used for opcode 9 text/subtitle rendering (DIHIFONT / TALKFONT)
-	SmushFont *_rebelMsgFont;
+	NutRenderer *_rebelMsgFont;
 
 	// Menu system fonts (from info.md - FUN_403BD0 font loading)
 	// Low resolution mode font list (stored in DAT_00485058 linked list):
diff --git a/engines/scumm/insane/rebel2/render.cpp b/engines/scumm/insane/rebel2/render.cpp
index 890a19c9166..5724492124b 100644
--- a/engines/scumm/insane/rebel2/render.cpp
+++ b/engines/scumm/insane/rebel2/render.cpp
@@ -28,8 +28,8 @@
 #include "scumm/scumm_v7.h"
 
 #include "scumm/smush/smush_player.h"
-#include "scumm/smush/smush_font.h"
 #include "scumm/smush/rebel/codec_ra2.h"
+#include "scumm/smush/rebel/font_rebel2.h"
 
 #include "scumm/insane/rebel2/rebel.h"
 
@@ -2806,8 +2806,7 @@ void InsaneRebel2::renderTextOverlay(byte *renderBitmap, int pitch, int width, i
 				}
 				int charW = curFont->getCharWidth(c);
 				if (drawX >= 0 && drawY >= 0 && charW > 0) {
-					curFont->drawCharV7(renderBitmap, clipRect, drawX, drawY,
-					                    pitch, curColor, kStyleAlignLeft, c, false, false);
+					drawRebel2Char(curFont, renderBitmap, clipRect, drawX, drawY, pitch, curColor, c);
 				}
 				drawX += charW;
 				lineCharsDrawn++;
diff --git a/engines/scumm/module.mk b/engines/scumm/module.mk
index e7fe13f0263..4f42d417fdc 100644
--- a/engines/scumm/module.mk
+++ b/engines/scumm/module.mk
@@ -156,6 +156,7 @@ MODULE_OBJS += \
 	smush/smush_player.o \
 	smush/rebel/codec_ra1.o \
 	smush/rebel/codec_ra2.o \
+	smush/rebel/font_rebel2.o \
 	smush/rebel/smush_multi_font.o \
 	smush/rebel/smush_player_rebel.o \
 	smush/rebel/smush_player_ra1.o \
diff --git a/engines/scumm/nut_renderer.cpp b/engines/scumm/nut_renderer.cpp
index 5fa5819a613..85cfef2cffd 100644
--- a/engines/scumm/nut_renderer.cpp
+++ b/engines/scumm/nut_renderer.cpp
@@ -196,12 +196,7 @@ void NutRenderer::loadFont(const char *filename) {
 		// If characters have transparency, then bytes just get skipped and
 		// so there may appear some garbage. That's why we have to fill it
 		// with a default color first.
-		//
-		// For codec 44: standard SCUMM v7/v8 fonts use value 2 as the
-		// transparent color. But RA2 codec 44 fonts use value 2 as an actual
-		// glyph color (medium body shade), so we must use 0 instead to avoid
-		// making those pixels invisible during rendering.
-		if (codec == 44 && _vm->_game.id != GID_REBEL2) {
+		if (codec == 44) {
 			memset(_chars[l].src, kSmush44TransparentColor, _chars[l].width * _chars[l].height);
 			_chars[l].transparency = kSmush44TransparentColor;
 		} else {
@@ -315,9 +310,7 @@ void NutRenderer::loadFontFromData(const byte *data, int32 dataSize) {
 
 		decodedPtr += (_chars[l].width * _chars[l].height);
 
-		// Same transparency logic as loadFont: RA2 codec 44 fonts use
-		// value 2 as a glyph color, not as transparency.
-		if (codec == 44 && _vm->_game.id != GID_REBEL2) {
+		if (codec == 44) {
 			memset(_chars[l].src, kSmush44TransparentColor, _chars[l].width * _chars[l].height);
 			_chars[l].transparency = kSmush44TransparentColor;
 		} else {
@@ -438,26 +431,7 @@ int NutRenderer::drawCharV7(byte *buffer, Common::Rect &clipRect, int x, int y,
 	int clipWdth = (_chars[chr].width - width);
 	char color = (col != -1) ? col : 1;
 
-	if (_vm->_game.id == GID_REBEL2) {
-		// RA2 codec 44 font rendering, matching the original behavior:
-		//   - Pixel value 1 ??? remapped to the caller's text color
-		//   - Other non-transparent values ??? used as-is (palette indices)
-		// The font's pixel layout: value 4 = black outline (38% of pixels),
-		// value 1 = body (11%, remapped to color), value 3 = gray AA (2%).
-		// The SMUSH video palette reserves indices 0-4 for text rendering:
-		//   0=(0,0,0), 1=(255,255,255), 2=(188,188,188), 3=(128,128,128), 4=(0,0,0).
-		for (int j = minY; j < height; j++) {
-			for (int i = minX; i < width; i++) {
-				int8 value = *src++;
-				if (value == 1)
-					dst[i] = color;
-				else if (value != _chars[chr].transparency)
-					dst[i] = value;
-			}
-			src += clipWdth;
-			dst += pitch;
-		}
-	} else if (_vm->_game.version == 7) {
+	if (_vm->_game.version == 7) {
 		if (hardcodedColors) {
 			for (int j = minY; j < height; j++) {
 				for (int i = minX; i < width; i++) {
diff --git a/engines/scumm/smush/rebel/font_rebel2.cpp b/engines/scumm/smush/rebel/font_rebel2.cpp
new file mode 100644
index 00000000000..d83fbef6585
--- /dev/null
+++ b/engines/scumm/smush/rebel/font_rebel2.cpp
@@ -0,0 +1,504 @@
+/* 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 "scumm/smush/rebel/font_rebel2.h"
+
+#include "common/endian.h"
+#include "common/util.h"
+
+#include "scumm/file.h"
+#include "scumm/nut_renderer.h"
+#include "scumm/scumm.h"
+
+namespace Scumm {
+
+enum {
+	kRebel2MaxStrings = 80
+};
+
+class Rebel2NutRenderer : public NutRenderer {
+public:
+	Rebel2NutRenderer(ScummEngine *vm, const char *filename) : NutRenderer(vm, nullptr) {
+		loadRebel2Font(filename);
+	}
+
+private:
+	void codec44(byte *dst, const byte *src, int width, int height, int pitch);
+	void loadRebel2Font(const char *filename);
+};
+
+void Rebel2NutRenderer::codec44(byte *dst, const byte *src, int width, int height, int pitch) {
+	while (height--) {
+		byte *dstPtrNext = dst + pitch;
+		const byte *srcPtrNext = src + 2 + READ_LE_UINT16(src);
+		src += 2;
+		int len = width;
+		do {
+			int offs = READ_LE_UINT16(src); src += 2;
+			dst += offs;
+			len -= offs;
+			if (len <= 0)
+				break;
+
+			int w = READ_LE_UINT16(src) + 1; src += 2;
+			len -= w;
+			if (len < 0)
+				w += len;
+
+			while (w--) {
+				byte value = *src++;
+				*dst++ = (value == 0xff) ? 0 : value;
+			}
+		} while (len > 0);
+		dst = dstPtrNext;
+		src = srcPtrNext;
+	}
+}
+
+void Rebel2NutRenderer::loadRebel2Font(const char *filename) {
+	ScummFile *file = _vm->instantiateScummFile();
+
+	_vm->openFile(*file, filename);
+	if (!file->isOpen())
+		error("Rebel2NutRenderer::loadRebel2Font() Can't open font file: %s", filename);
+
+	uint32 tag = file->readUint32BE();
+	if (tag != MKTAG('A','N','I','M'))
+		error("Rebel2NutRenderer::loadRebel2Font() there is no ANIM chunk in font header");
+
+	uint32 length = file->readUint32BE();
+	byte *dataSrc = new byte[length];
+	file->read(dataSrc, length);
+	file->close();
+	delete file;
+
+	if (READ_BE_UINT32(dataSrc) != MKTAG('A','H','D','R'))
+		error("Rebel2NutRenderer::loadRebel2Font() there is no AHDR chunk in font header");
+
+	_numChars = READ_LE_UINT16(dataSrc + 10);
+	if (_numChars > ARRAYSIZE(_chars)) {
+		warning("Rebel2NutRenderer::loadRebel2Font(%s) numChars (%d) exceeds max, clamping", filename, _numChars);
+		_numChars = ARRAYSIZE(_chars);
+	}
+
+	uint32 offset = 0;
+	uint32 decodedLength = 0;
+	int l;
+
+	for (l = 0; l < _numChars; l++) {
+		if (offset + 8 > length) {
+			warning("Rebel2NutRenderer::loadRebel2Font(%s) truncated before char %d (offset %x), clamping", filename, l, offset);
+			break;
+		}
+		uint32 chunkSize = READ_BE_UINT32(dataSrc + offset + 4);
+		uint64 nextOffset = (uint64)offset + chunkSize + 16 + (chunkSize & 1);
+		if (nextOffset + 18 > length) {
+			warning("Rebel2NutRenderer::loadRebel2Font(%s) font chunk exceeds file at char %d (offset %x), clamping", filename, l, offset);
+			break;
+		}
+		offset = (uint32)nextOffset;
+		int width = READ_LE_UINT16(dataSrc + offset + 14);
+		_fontHeight = READ_LE_UINT16(dataSrc + offset + 16);
+		decodedLength += width * _fontHeight;
+	}
+
+	if (l < _numChars)
+		_numChars = l;
+
+	if (_numChars <= 0 || decodedLength == 0)
+		error("Rebel2NutRenderer::loadRebel2Font(%s) no decodable characters", filename);
+
+	debug(1, "Rebel2NutRenderer::loadRebel2Font('%s') - decodedLength = %d", filename, decodedLength);
+
+	_decodedData = new byte[decodedLength];
+	byte *decodedPtr = _decodedData;
+
+	offset = 0;
+	for (l = 0; l < _numChars; l++) {
+		if (offset + 8 > length) {
+			warning("Rebel2NutRenderer::loadRebel2Font(%s) invalid font chunk header %d (offset %x), stopping decode", filename, l, offset);
+			break;
+		}
+		uint32 chunkSize = READ_BE_UINT32(dataSrc + offset + 4);
+		uint64 nextOffset = (uint64)offset + chunkSize + 8 + (chunkSize & 1);
+		if (nextOffset + 8 > length) {
+			warning("Rebel2NutRenderer::loadRebel2Font(%s) FRME chunk exceeds file %d (offset %x), stopping decode", filename, l, offset);
+			break;
+		}
+		offset = (uint32)nextOffset;
+		if (READ_BE_UINT32(dataSrc + offset) != MKTAG('F','R','M','E')) {
+			warning("Rebel2NutRenderer::loadRebel2Font(%s) no FRME chunk %d (offset %x), stopping decode", filename, l, offset);
+			break;
+		}
+		offset += 8;
+		if (offset + 22 > length) {
+			warning("Rebel2NutRenderer::loadRebel2Font(%s) FOBJ chunk exceeds file %d (offset %x), stopping decode", filename, l, offset);
+			break;
+		}
+		if (READ_BE_UINT32(dataSrc + offset) != MKTAG('F','O','B','J')) {
+			warning("Rebel2NutRenderer::loadRebel2Font(%s) no FOBJ chunk in FRME chunk %d (offset %x), stopping decode", filename, l, offset);
+			break;
+		}
+
+		int codec = READ_LE_UINT16(dataSrc + offset + 8);
+		_chars[l].xoffs = READ_LE_INT16(dataSrc + offset + 10);
+		_chars[l].yoffs = READ_LE_INT16(dataSrc + offset + 12);
+		_chars[l].width = READ_LE_UINT16(dataSrc + offset + 14);
+		_chars[l].height = READ_LE_UINT16(dataSrc + offset + 16);
+		_chars[l].src = decodedPtr;
+
+		decodedPtr += (_chars[l].width * _chars[l].height);
+
+		memset(_chars[l].src, kDefaultTransparentColor, _chars[l].width * _chars[l].height);
+		_chars[l].transparency = kDefaultTransparentColor;
+
+		const uint8 *fobjptr = dataSrc + offset + 22;
+		switch (codec) {
+		case 1:
+			codec1(_chars[l].src, fobjptr, _chars[l].width, _chars[l].height, _chars[l].width);
+			break;
+		case 21:
+			codec21(_chars[l].src, fobjptr, _chars[l].width, _chars[l].height, _chars[l].width);
+			break;
+		case 44:
+			codec44(_chars[l].src, fobjptr, _chars[l].width, _chars[l].height, _chars[l].width);
+			break;
+		default:
+			error("Rebel2NutRenderer::loadRebel2Font: unknown codec: %d", codec);
+		}
+	}
+
+	delete[] dataSrc;
+}
+
+NutRenderer *makeRebel2Font(ScummEngine *vm, const char *filename) {
+	return new Rebel2NutRenderer(vm, filename);
+}
+
+Rebel2FontSet::Rebel2FontSet() : numFonts(0), defaultFont(0) {
+	memset(fonts, 0, sizeof(fonts));
+}
+
+NutRenderer *Rebel2FontSet::getFont(int id) const {
+	if (numFonts <= 0)
+		return nullptr;
+
+	if (id < 0 || id >= numFonts || fonts[id] == nullptr)
+		id = defaultFont;
+
+	if (id < 0 || id >= numFonts)
+		return nullptr;
+
+	return fonts[id] ? fonts[id] : fonts[0];
+}
+
+static bool parseRebel2FormatCode(const char *str, uint len, uint &pos, int &fontId, int16 &color) {
+	if (pos + 1 >= len || str[pos] != '^')
+		return false;
+
+	const char code = str[pos + 1];
+	if (code == '^') {
+		pos++;
+		return false;
+	}
+
+	if (code == 'f') {
+		pos += 2;
+		int value = 0;
+		while (pos < len && str[pos] >= '0' && str[pos] <= '9') {
+			value = value * 10 + str[pos] - '0';
+			pos++;
+		}
+		fontId = value;
+		return true;
+	}
+
+	if (code == 'c') {
+		pos += 2;
+		int value = 0;
+		while (pos < len && str[pos] >= '0' && str[pos] <= '9') {
+			value = value * 10 + str[pos] - '0';
+			pos++;
+		}
+		color = value;
+		return true;
+	}
+
+	if (code == 'l') {
+		pos += 2;
+		return true;
+	}
+
+	return false;
+}
+
+int drawRebel2Char(NutRenderer *font, byte *buffer, Common::Rect &clipRect, int x, int y,
+		int pitch, int16 col, byte chr) {
+	if (!font || chr >= font->getNumChars())
+		return 0;
+
+	const int charWidth = font->getCharWidth(chr);
+	const int charHeight = font->getCharHeight(chr);
+	int width = MIN(charWidth, clipRect.right - x);
+	int height = MIN(charHeight, clipRect.bottom - y);
+	const int minX = x < clipRect.left ? clipRect.left - x : 0;
+	const int minY = y < clipRect.top ? clipRect.top - y : 0;
+
+	if (width <= 0 || height <= 0)
+		return 0;
+
+	width -= minX;
+	height -= minY;
+	if (width <= 0 || height <= 0)
+		return 0;
+
+	const byte *src = font->getCharData(chr) + minY * charWidth + minX;
+	byte *dst = buffer + pitch * (y + minY) + x + minX;
+	const int color = (col != -1) ? col : 1;
+
+	for (int row = 0; row < height; row++) {
+		for (int column = 0; column < width; column++) {
+			const byte value = src[column];
+			if (value != 0 && value != 0xff)
+				dst[column] = (byte)(value + color - 1);
+		}
+		src += charWidth;
+		dst += pitch;
+	}
+
+	return MIN(charWidth, clipRect.right - x);
+}
+
+int getRebel2StringWidth(const Rebel2FontSet &fontSet, const char *str, uint len) {
+	int width = 0;
+	int fontId = fontSet.defaultFont;
+	int16 color = 0;
+
+	for (uint pos = 0; pos < len;) {
+		if (parseRebel2FormatCode(str, len, pos, fontId, color))
+			continue;
+
+		const byte chr = (byte)str[pos++];
+		if (chr == '\n' || chr == '\r')
+			continue;
+
+		NutRenderer *font = fontSet.getFont(fontId);
+		if (font && chr < font->getNumChars())
+			width += font->getCharWidth(chr);
+	}
+
+	return width;
+}
+
+int getRebel2StringHeight(const Rebel2FontSet &fontSet, const char *str, uint len) {
+	int height = 0;
+	int lineHeight = 0;
+	int fontId = fontSet.defaultFont;
+	int16 color = 0;
+
+	for (uint pos = 0; pos < len;) {
+		if (parseRebel2FormatCode(str, len, pos, fontId, color))
+			continue;
+
+		const byte chr = (byte)str[pos++];
+		if (chr == '\n') {
+			NutRenderer *font = fontSet.getFont(fontId);
+			height += lineHeight ? lineHeight : (font ? font->getFontHeight() : 0);
+			lineHeight = 0;
+		} else if (chr != '\r') {
+			NutRenderer *font = fontSet.getFont(fontId);
+			if (font && chr < font->getNumChars())
+				lineHeight = MAX<int>(lineHeight, font->getCharHeight(chr));
+		}
+	}
+
+	NutRenderer *font = fontSet.getFont(fontId);
+	return height + (lineHeight ? lineHeight : (font ? font->getFontHeight() : 0));
+}
+
+static void drawRebel2Substring(const Rebel2FontSet &fontSet, const char *str, uint len,
+		byte *buffer, Common::Rect &clipRect, int x, int y, int pitch,
+		int &fontId, int16 &color) {
+	for (uint pos = 0; pos < len;) {
+		if (parseRebel2FormatCode(str, len, pos, fontId, color))
+			continue;
+
+		const byte chr = (byte)str[pos++];
+		if (chr == '\n' || chr == '\r')
+			continue;
+
+		NutRenderer *font = fontSet.getFont(fontId);
+		x += drawRebel2Char(font, buffer, clipRect, x, y, pitch, color, chr);
+	}
+}
+
+void drawRebel2String(const Rebel2FontSet &fontSet, const char *str, uint len, byte *buffer,
+		Common::Rect &clipRect, int x, int y, int pitch, int16 col, TextStyleFlags flags) {
+	if (!str || !buffer)
+		return;
+
+	int lineStart = 0;
+	int maxWidth = 0;
+	int yStart = y;
+	int fontId = fontSet.defaultFont;
+	int16 color = col;
+
+	for (uint pos = 0; pos <= len; pos++) {
+		if (pos < len && str[pos] != '\n')
+			continue;
+
+		const int lineLen = pos - lineStart;
+		const int lineWidth = getRebel2StringWidth(fontSet, str + lineStart, lineLen);
+		const int lineHeight = getRebel2StringHeight(fontSet, str + lineStart, lineLen);
+		maxWidth = MAX(maxWidth, lineWidth);
+
+		int drawX = x;
+		if (flags & kStyleAlignCenter)
+			drawX = x - lineWidth / 2;
+		else if (flags & kStyleAlignRight)
+			drawX = x - lineWidth;
+
+		drawRebel2Substring(fontSet, str + lineStart, lineLen, buffer, clipRect, drawX, y, pitch, fontId, color);
+		y += lineHeight;
+		lineStart = pos + 1;
+	}
+
+	clipRect.left = MAX<int>(0, (flags & kStyleAlignCenter) ? x - maxWidth / 2 : ((flags & kStyleAlignRight) ? x - maxWidth : x));
+	clipRect.right = MIN<int>(clipRect.right, clipRect.left + maxWidth);
+	clipRect.top = yStart;
+	clipRect.bottom = y;
+}
+
+void drawRebel2StringWrap(const Rebel2FontSet &fontSet, const char *str, uint len, byte *buffer,
+		Common::Rect &clipRect, int x, int y, int pitch, int16 col, TextStyleFlags flags) {
+	if (!str || !buffer)
+		return;
+
+	int16 substrByteLength[kRebel2MaxStrings];
+	int16 substrWidths[kRebel2MaxStrings];
+	int16 substrStart[kRebel2MaxStrings];
+	memset(substrByteLength, 0, sizeof(substrByteLength));
+	memset(substrWidths, 0, sizeof(substrWidths));
+	memset(substrStart, 0, sizeof(substrStart));
+
+	int16 numSubstrings = 0;
+	int curWidth = 0;
+	int curPos = -1;
+	int maxWidth = 0;
+	int height = 0;
+	int lastSubstrHeight = 0;
+
+	while (curPos < (int)len) {
+		int textStart = curPos + 1;
+		while (textStart < (int)len && str[textStart] == ' ')
+			textStart++;
+
+		const int separatorWidth = curPos > 0 ? getRebel2StringWidth(fontSet, str + curPos, textStart - curPos) : 0;
+
+		int nextSeparatorPos = textStart;
+		while (nextSeparatorPos < (int)len && str[nextSeparatorPos] != ' ' && str[nextSeparatorPos] != '\n')
+			nextSeparatorPos++;
+
+		const int wordWidth = getRebel2StringWidth(fontSet, str + textStart, nextSeparatorPos - textStart);
+		int newWidth = curWidth + separatorWidth + wordWidth;
+
+		if (curWidth && newWidth > clipRect.width()) {
+			if (numSubstrings < kRebel2MaxStrings) {
+				substrWidths[numSubstrings] = curWidth;
+				substrByteLength[numSubstrings] = curPos - substrStart[numSubstrings];
+				numSubstrings++;
+			}
+			newWidth = wordWidth;
+			substrStart[numSubstrings] = textStart;
+		}
+		curWidth = newWidth;
+
+		curPos = nextSeparatorPos;
+		if (curPos >= (int)len || str[curPos] == '\n') {
+			if (numSubstrings < kRebel2MaxStrings) {
+				substrWidths[numSubstrings] = curWidth;
+				substrByteLength[numSubstrings] = curPos - substrStart[numSubstrings];
+				numSubstrings++;
+				if (numSubstrings < kRebel2MaxStrings)
+					substrStart[numSubstrings] = curPos + 1;
+			}
+			curWidth = 0;
+		}
+	}
+
+	if (curWidth && numSubstrings < kRebel2MaxStrings) {
+		substrWidths[numSubstrings] = curWidth;
+		substrByteLength[numSubstrings] = curPos - substrStart[numSubstrings];
+		numSubstrings++;
+	}
+
+	for (int i = 0; i < numSubstrings; i++) {
+		maxWidth = MAX<int>(maxWidth, substrWidths[i]);
+		lastSubstrHeight = substrByteLength[i] > 0 ? getRebel2StringHeight(fontSet, str + substrStart[i], substrByteLength[i]) : 0;
+		height += lastSubstrHeight;
+	}
+
+	const int clipHeight = height + lastSubstrHeight / 2;
+	if (y > clipRect.bottom - clipHeight)
+		y = clipRect.bottom - clipHeight;
+	if (y < clipRect.top)
+		y = clipRect.top;
+
+	if (flags & kStyleAlignCenter) {
+		if (x + (maxWidth >> 1) > clipRect.right)
+			x = clipRect.right - (maxWidth >> 1);
+		if (x - (maxWidth >> 1) < clipRect.left)
+			x = clipRect.left + (maxWidth >> 1);
+	} else if (flags & kStyleAlignRight) {
+		if (x > clipRect.right)
+			x = clipRect.right;
+		if (x < clipRect.left + maxWidth)
+			x = clipRect.left + maxWidth;
+	} else {
+		if (x > clipRect.right - maxWidth)
+			x = clipRect.right - maxWidth;
+		if (x < clipRect.left)
+			x = clipRect.left;
+	}
+
+	const int yStart = y;
+	int fontId = fontSet.defaultFont;
+	int16 color = col;
+
+	for (int i = 0; i < numSubstrings; i++) {
+		int drawX = x;
+		if (flags & kStyleAlignCenter)
+			drawX = x - substrWidths[i] / 2;
+		else if (flags & kStyleAlignRight)
+			drawX = x - substrWidths[i];
+
+		const int lineLen = substrByteLength[i] > 0 ? substrByteLength[i] : 0;
+		drawRebel2Substring(fontSet, str + substrStart[i], lineLen, buffer, clipRect, drawX, y, pitch, fontId, color);
+		y += getRebel2StringHeight(fontSet, str + substrStart[i], lineLen);
+	}
+
+	clipRect.left = MAX<int>(0, (flags & kStyleAlignCenter) ? x - maxWidth / 2 : ((flags & kStyleAlignRight) ? x - maxWidth : x));
+	clipRect.right = MIN<int>(clipRect.right, clipRect.left + maxWidth);
+	clipRect.top = yStart;
+	clipRect.bottom = y;
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/smush/rebel/font_rebel2.h b/engines/scumm/smush/rebel/font_rebel2.h
new file mode 100644
index 00000000000..2a5ae2f7ae7
--- /dev/null
+++ b/engines/scumm/smush/rebel/font_rebel2.h
@@ -0,0 +1,60 @@
+/* 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_SMUSH_REBEL_FONT_REBEL2_H
+#define SCUMM_SMUSH_REBEL_FONT_REBEL2_H
+
+#include "common/scummsys.h"
+#include "common/rect.h"
+
+#include "scumm/charset_v7.h"
+
+namespace Scumm {
+
+class ScummEngine;
+class NutRenderer;
+
+struct Rebel2FontSet {
+	enum {
+		kMaxFonts = 5
+	};
+
+	NutRenderer *fonts[kMaxFonts];
+	int numFonts;
+	int defaultFont;
+
+	Rebel2FontSet();
+	NutRenderer *getFont(int id) const;
+};
+
+NutRenderer *makeRebel2Font(ScummEngine *vm, const char *filename);
+int drawRebel2Char(NutRenderer *font, byte *buffer, Common::Rect &clipRect, int x, int y,
+		int pitch, int16 col, byte chr);
+int getRebel2StringWidth(const Rebel2FontSet &fontSet, const char *str, uint len);
+int getRebel2StringHeight(const Rebel2FontSet &fontSet, const char *str, uint len);
+void drawRebel2String(const Rebel2FontSet &fontSet, const char *str, uint len, byte *buffer,
+		Common::Rect &clipRect, int x, int y, int pitch, int16 col, TextStyleFlags flags);
+void drawRebel2StringWrap(const Rebel2FontSet &fontSet, const char *str, uint len, byte *buffer,
+		Common::Rect &clipRect, int x, int y, int pitch, int16 col, TextStyleFlags flags);
+
+} // End of namespace Scumm
+
+#endif
diff --git a/engines/scumm/smush/rebel/smush_multi_font.cpp b/engines/scumm/smush/rebel/smush_multi_font.cpp
index 9c4205b7a65..47193e58a6e 100644
--- a/engines/scumm/smush/rebel/smush_multi_font.cpp
+++ b/engines/scumm/smush/rebel/smush_multi_font.cpp
@@ -28,10 +28,13 @@ namespace Scumm {
 
 SmushMultiFont::SmushMultiFont(ScummEngine *vm, SmushPlayer *player, bool useOriginalColors)
 	: _vm(vm), _player(player), _currentFont(0), _defaultFont(0), _hardcodedFontColors(useOriginalColors) {
+	memset(_rebel2Fonts, 0, sizeof(_rebel2Fonts));
 	_textRenderer = new TextRenderer_v7(vm, this);
 }
 
 SmushMultiFont::~SmushMultiFont() {
+	for (int i = 0; i < ARRAYSIZE(_rebel2Fonts); i++)
+		delete _rebel2Fonts[i];
 	delete _textRenderer;
 }
 
@@ -47,20 +50,54 @@ NutRenderer *SmushMultiFont::getCurrentFont() const {
 	return const_cast<SmushMultiFont*>(this)->getFont(_currentFont);
 }
 
+Rebel2FontSet SmushMultiFont::getRebel2FontSet() {
+	static const char *ra2Fonts[] = {
+		"SYSTM/TALKFONT.NUT",
+		"SYSTM/SMALFONT.NUT",
+		"SYSTM/TITLFONT.NUT",
+		"SYSTM/POVFONT.NUT"
+	};
+
+	Rebel2FontSet fontSet;
+	fontSet.numFonts = ARRAYSIZE(ra2Fonts);
+	fontSet.defaultFont = CLIP<int>(_defaultFont, 0, fontSet.numFonts - 1);
+	for (int i = 0; i < fontSet.numFonts; i++) {
+		if (!_rebel2Fonts[i])
+			_rebel2Fonts[i] = makeRebel2Font(_vm, ra2Fonts[i]);
+		fontSet.fonts[i] = _rebel2Fonts[i];
+	}
+	return fontSet;
+}
+
 void SmushMultiFont::drawString(const char *str, byte *buffer, Common::Rect &clipRect, int x, int y, int16 col, TextStyleFlags flags) {
 	// Reset to default font before drawing
 	_currentFont = _defaultFont;
+	if (_vm->_game.id == GID_REBEL2) {
+		Rebel2FontSet fontSet = getRebel2FontSet();
+		drawRebel2String(fontSet, str, strlen(str), buffer, clipRect, x, y, _vm->_screenWidth, col, flags);
+		return;
+	}
 	_textRenderer->drawString(str, buffer, clipRect, x, y, _vm->_screenWidth, col, flags);
 }
 
 void SmushMultiFont::drawString(const char *str, byte *buffer, Common::Rect &clipRect, int x, int y, int pitch, int16 col, TextStyleFlags flags) {
 	_currentFont = _defaultFont;
+	if (_vm->_game.id == GID_REBEL2) {
+		Rebel2FontSet fontSet = getRebel2FontSet();
+		drawRebel2String(fontSet, str, strlen(str), buffer, clipRect, x, y, pitch, col, flags);
+		return;
+	}
 	_textRenderer->drawString(str, buffer, clipRect, x, y, pitch, col, flags);
 }
 
 void SmushMultiFont::drawStringWrap(const char *str, byte *buffer, Common::Rect &clipRect, int x, int y, int16 col, TextStyleFlags flags) {
 	// Reset to default font before drawing
 	_currentFont = _defaultFont;
+	if (_vm->_game.id == GID_REBEL2) {
+		Rebel2FontSet fontSet = getRebel2FontSet();
+		drawRebel2StringWrap(fontSet, str, strlen(str), buffer, clipRect, x, y, _vm->_screenWidth, col, flags);
+		return;
+	}
 	_textRenderer->drawStringWrap(str, buffer, clipRect, x, y, _vm->_screenWidth, col, flags);
 }
 
@@ -80,6 +117,11 @@ int SmushMultiFont::draw2byte(byte *buffer, Common::Rect &clipRect, int x, int y
 }
 
 int SmushMultiFont::drawCharV7(byte *buffer, Common::Rect &clipRect, int x, int y, int pitch, int16 col, TextStyleFlags flags, byte chr) {
+	if (_vm->_game.id == GID_REBEL2) {
+		Rebel2FontSet fontSet = getRebel2FontSet();
+		return drawRebel2Char(fontSet.getFont(_currentFont), buffer, clipRect, x, y, pitch, col, chr);
+	}
+
 	NutRenderer *font = getCurrentFont();
 	if (!font)
 		return 0;
diff --git a/engines/scumm/smush/rebel/smush_multi_font.h b/engines/scumm/smush/rebel/smush_multi_font.h
index 6c57acbe57f..1f7fba58487 100644
--- a/engines/scumm/smush/rebel/smush_multi_font.h
+++ b/engines/scumm/smush/rebel/smush_multi_font.h
@@ -25,6 +25,7 @@
 #include "common/scummsys.h"
 #include "scumm/nut_renderer.h"
 #include "scumm/scumm.h"
+#include "scumm/smush/rebel/font_rebel2.h"
 #include "scumm/string_v7.h"
 
 namespace Scumm {
@@ -70,10 +71,12 @@ public:
 private:
 	NutRenderer *getFont(int id);
 	NutRenderer *getCurrentFont() const;
+	Rebel2FontSet getRebel2FontSet();
 
 	ScummEngine *_vm;
 	SmushPlayer *_player;
 	TextRenderer_v7 *_textRenderer;
+	NutRenderer *_rebel2Fonts[Rebel2FontSet::kMaxFonts];
 
 	int _currentFont;
 	int _defaultFont;



From noreply at scummvm.org  Wed Jun  3 13:56:44 2026
From: noreply at scummvm.org (neuromancer)
Date: Wed,  3 Jun 2026 13:56:44 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 b6fdfa0e2e422a90b46cb9ee5c7e641fb476dc65
Message-ID: <20260603135644.5BC40700591@ron.scummvm.net>

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:
7a5821d116 SCUMM: RA2: missing line in chapter titles
a53dad284d SCUMM: RA2: refined controls for level 5
b6fdfa0e2e SCUMM: RA2: correctly decode audio tracks on 22hz


Commit: 7a5821d1160dc5752854db2d931e782a6c3fb770
    https://github.com/scummvm/scummvm/commit/7a5821d1160dc5752854db2d931e782a6c3fb770
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-03T14:39:18+02:00

Commit Message:
SCUMM: RA2: missing line in chapter titles

Changed paths:
    engines/scumm/insane/rebel2/render.cpp


diff --git a/engines/scumm/insane/rebel2/render.cpp b/engines/scumm/insane/rebel2/render.cpp
index 5724492124b..db4780badeb 100644
--- a/engines/scumm/insane/rebel2/render.cpp
+++ b/engines/scumm/insane/rebel2/render.cpp
@@ -2739,16 +2739,26 @@ void InsaneRebel2::renderTextOverlay(byte *renderBitmap, int pitch, int width, i
 		return fc == -2;
 	};
 
-	// The TRS parser joins multi-line strings with spaces (stripping \n//),
-	// so " ^f" marks where a line break was in the original TRS file.
 	// Split into lines, then render each centered at textX (FUN_004341a0).
+	// Older RA2 text loading joined multi-line strings with spaces, leaving
+	// " ^f" as the separator; current TRES loading preserves real newlines.
 	Common::Array<Common::String> lines;
 	{
 		Common::String cur;
 		const char *s = text;
 		while (*s) {
+			if (*s == '\n' || *s == '\r') {
+				if (!cur.empty())
+					lines.push_back(cur);
+				cur.clear();
+				if (*s == '\r' && s[1] == '\n')
+					s++;
+				s++;
+				continue;
+			}
 			if (*s == ' ' && s[1] == '^' && s[2] == 'f') {
-				lines.push_back(cur);
+				if (!cur.empty())
+					lines.push_back(cur);
 				cur.clear();
 				s++; // skip the space, keep ^f for the next line
 				continue;


Commit: a53dad284d71c190f4ab548e37e49ff2a30cbf65
    https://github.com/scummvm/scummvm/commit/a53dad284d71c190f4ab548e37e49ff2a30cbf65
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-03T14:57:17+02:00

Commit Message:
SCUMM: RA2: refined controls for level 5

Changed paths:
    engines/scumm/insane/rebel2/rebel.cpp


diff --git a/engines/scumm/insane/rebel2/rebel.cpp b/engines/scumm/insane/rebel2/rebel.cpp
index eef134ae7aa..080f47e9165 100644
--- a/engines/scumm/insane/rebel2/rebel.cpp
+++ b/engines/scumm/insane/rebel2/rebel.cpp
@@ -55,6 +55,10 @@ const int kRA2MenuAxisThreshold = Common::JOYAXIS_MAX / 5;
 const uint32 kRA2MenuGamepadNavigationDebounceMs = 250;
 const uint32 kRA2MenuGamepadMouseSuppressMs = 250;
 
+bool rebel2UsesRelativeGamepadAim(int selectedLevel) {
+	return selectedLevel == 1 || selectedLevel == 5;
+}
+
 bool isRebel2RawMenuAxis(int axis) {
 	return axis == Common::JOYSTICK_AXIS_LEFT_STICK_X ||
 	       axis == Common::JOYSTICK_AXIS_LEFT_STICK_Y ||
@@ -618,12 +622,12 @@ bool InsaneRebel2::notifyEvent(const Common::Event &event) {
 	// kScummActionInsaneAttack action, not the pointer, so this does not affect shots.
 	// A genuine mouse/touch motion (nonzero relative delta) normally hands control
 	// back; handler 0x26 keeps ownership until its gamepad reticle returns to center,
-	// except for Level 1's relative gamepad aiming where the reticle deliberately holds.
+	// except for relative gamepad aiming levels where the reticle deliberately holds.
 	if (_gamepadAimActive && _gameState == kStateGameplay && !_menuInputActive) {
 		switch (event.type) {
 		case Common::EVENT_MOUSEMOVE:
 			if (event.relMouse.x != 0 || event.relMouse.y != 0) {
-				if (_rebelHandler == 0x26 && _selectedLevel != 1)
+				if (_rebelHandler == 0x26 && !rebel2UsesRelativeGamepadAim(_selectedLevel))
 					return true;
 				_gamepadAimActive = false; // real pointer motion takes over
 				break;
@@ -1636,7 +1640,7 @@ Common::Point InsaneRebel2::getGameplayAimPoint() {
 
 // Apply the user's configured analog deadzone so a resting stick reports no
 // motion. Mirrors RA1's applyRebel1AnalogDeadzone (iact.cpp).
-static inline int16 applyRebel2AnalogDeadzone(int16 axisValue) {
+int16 applyRebel2AnalogDeadzone(int16 axisValue) {
 	const int deadZone = MAX(0, ConfMan.getInt("joystick_deadzone")) * 1000;
 	return (ABS((int)axisValue) <= deadZone) ? 0 : axisValue;
 }
@@ -1659,8 +1663,8 @@ void InsaneRebel2::updateGameplayAimFromGamepad() {
 	int deltaY = 0;
 	bool activeGamepadAim = false;
 
-	if (_rebelHandler == 0x26 && _selectedLevel == 1) {
-		// Level 1 plays best with the older mouse-like gamepad behavior from
+	if (_rebelHandler == 0x26 && rebel2UsesRelativeGamepadAim(_selectedLevel)) {
+		// Levels 1 and 5 play best with the older mouse-like gamepad behavior from
 		// ec305dee371/0025c4e1086: pan the reticle directly and leave it where
 		// the player releases the stick. Later handler 0x26 levels keep the
 		// original-style centered mapping for obstacle avoidance.


Commit: b6fdfa0e2e422a90b46cb9ee5c7e641fb476dc65
    https://github.com/scummvm/scummvm/commit/b6fdfa0e2e422a90b46cb9ee5c7e641fb476dc65
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-03T15:56:25+02:00

Commit Message:
SCUMM: RA2: correctly decode audio tracks on 22hz

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


diff --git a/engines/scumm/insane/rebel/rebel_audio.cpp b/engines/scumm/insane/rebel/rebel_audio.cpp
index 07204d4806d..6c1f85d4bf2 100644
--- a/engines/scumm/insane/rebel/rebel_audio.cpp
+++ b/engines/scumm/insane/rebel/rebel_audio.cpp
@@ -61,10 +61,12 @@ void RebelAudio::terminate() {
 	}
 }
 
-void RebelAudio::queueData(int trackIdx, const uint8 *data, int32 size, int volume, int pan) {
+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;
 
+	const int sourceRate = sampleRate > 0 ? sampleRate : _sampleRate;
+
 	if (!_streams[trackIdx]) {
 		debug(1, "RebelAudio: Creating audio stream for track %d at %d Hz", trackIdx, _sampleRate);
 		_streams[trackIdx] = Audio::makeQueuingAudioStream(_sampleRate, false);
@@ -74,12 +76,29 @@ void RebelAudio::queueData(int trackIdx, const uint8 *data, int32 size, int volu
 			DisposeAfterUse::NO);
 	}
 
-	byte *audioCopy = (byte *)malloc(size);
+	int32 queueSize = size;
+	if (sourceRate != _sampleRate) {
+		queueSize = ((int64)size * _sampleRate + sourceRate / 2) / sourceRate;
+		if (queueSize <= 0)
+			queueSize = 1;
+	}
+
+	byte *audioCopy = (byte *)malloc(queueSize);
 	if (!audioCopy)
 		return;
-	memcpy(audioCopy, data, size);
 
-	_streams[trackIdx]->queueBuffer(audioCopy, size, DisposeAfterUse::YES, Audio::FLAG_UNSIGNED);
+	if (sourceRate == _sampleRate) {
+		memcpy(audioCopy, data, queueSize);
+	} else {
+		for (int32 i = 0; i < queueSize; i++) {
+			int32 srcPos = ((int64)i * sourceRate) / _sampleRate;
+			if (srcPos >= size)
+				srcPos = size - 1;
+			audioCopy[i] = data[srcPos];
+		}
+	}
+
+	_streams[trackIdx]->queueBuffer(audioCopy, queueSize, DisposeAfterUse::YES, Audio::FLAG_UNSIGNED);
 
 	const int scaledVolume = (volume * Audio::Mixer::kMaxChannelVolume) / 127;
 	const int scaledPan = (pan * 127) / 128;
@@ -210,7 +229,7 @@ bool RebelAudio::processAudioCodes(SmushPlayer *player, int idx, int32 &tmpFeedS
 			buf = player->_smushDispatch[idx].headerPtr;
 			player->_smushDispatch[idx].audioRemaining = READ_BE_UINT32(buf + 2);
 			player->_smushDispatch[idx].currentOffset = READ_BE_UINT32(buf + 6);
-			player->_smushDispatch[idx].sampleRate = player->_smushAudioSampleRate;
+			player->_smushDispatch[idx].sampleRate = READ_BE_UINT32(buf + 10);
 
 			player->_smushDispatch[idx].headerPtr += player->_smushDispatch[idx].headerPtr[1] + 2;
 			if (player->_smushDispatch[idx].audioRemaining < player->_smushTracks[idx].availableSize + (player->_smushTracks[idx].availableSize >= player->_smushTracks[idx].sdatSize ? 0 : 15000) - player->_smushTracks[idx].dataSize) {
@@ -240,7 +259,7 @@ bool RebelAudio::processAudioCodes(SmushPlayer *player, int idx, int32 &tmpFeedS
 			if (player->_smushDispatch[idx].currentOffset > player->_smushDispatch[idx].audioLength)
 				player->_smushDispatch[idx].currentOffset = player->_smushDispatch[idx].audioLength;
 
-			player->_smushDispatch[idx].sampleRate = player->_smushAudioSampleRate;
+			player->_smushDispatch[idx].sampleRate = READ_BE_UINT32(buf + 10);
 
 			player->_smushDispatch[idx].audioLength -= player->_smushDispatch[idx].currentOffset;
 			player->_smushDispatch[idx].elapsedAudio += player->_smushDispatch[idx].currentOffset;
@@ -349,7 +368,8 @@ void RebelAudio::processFrame(SmushPlayer *player, int16 feedSize) {
 					int32 offset = dispatch.audioRemaining % dispatch.dataSize;
 
 					if (dispatch.sampleRate > 0 && player->_smushAudioSampleRate > 0) {
-						int32 maxFrames = dispatch.sampleRate * tmpFeedSize / player->_smushAudioSampleRate;
+						int32 maxFrames = ((int64)dispatch.sampleRate * tmpFeedSize +
+							player->_smushAudioSampleRate - 1) / player->_smushAudioSampleRate;
 						if (mixInFrameCount > maxFrames)
 							mixInFrameCount = maxFrames;
 					}
@@ -370,13 +390,14 @@ void RebelAudio::processFrame(SmushPlayer *player, int16 feedSize) {
 							break;
 
 						track.state = TRK_STATE_PLAYING;
-						queueData(i, &dispatch.dataBuf[offset], mixInFrameCount, mixVolume, track.pan);
+						queueData(i, &dispatch.dataBuf[offset], mixInFrameCount, mixVolume, track.pan, dispatch.sampleRate);
 
 						dispatch.currentOffset -= mixInFrameCount;
 						dispatch.audioRemaining += mixInFrameCount;
 
 						if (dispatch.sampleRate > 0) {
-							int32 consumedFeed = mixInFrameCount * player->_smushAudioSampleRate / dispatch.sampleRate;
+							int32 consumedFeed = ((int64)mixInFrameCount * player->_smushAudioSampleRate +
+								dispatch.sampleRate - 1) / dispatch.sampleRate;
 							tmpFeedSize -= consumedFeed;
 						} else {
 							tmpFeedSize -= mixInFrameCount;
diff --git a/engines/scumm/insane/rebel/rebel_audio.h b/engines/scumm/insane/rebel/rebel_audio.h
index 10e0548bf71..a91ef4f28dc 100644
--- a/engines/scumm/insane/rebel/rebel_audio.h
+++ b/engines/scumm/insane/rebel/rebel_audio.h
@@ -42,7 +42,7 @@ public:
 	void terminate();
 	int sampleRate() const { return _sampleRate; }
 
-	void queueData(int trackIdx, const uint8 *data, int32 size, int volume, int pan);
+	void queueData(int trackIdx, const uint8 *data, int32 size, int volume, int pan, int sampleRate = 0);
 	void processFrame(SmushPlayer *player, int16 feedSize);
 
 private:
diff --git a/engines/scumm/insane/rebel2/rebel.cpp b/engines/scumm/insane/rebel2/rebel.cpp
index 080f47e9165..0585d558df7 100644
--- a/engines/scumm/insane/rebel2/rebel.cpp
+++ b/engines/scumm/insane/rebel2/rebel.cpp
@@ -420,8 +420,9 @@ InsaneRebel2::InsaneRebel2(ScummEngine_v7 *scumm) {
 	_hudOverlayNut = nullptr;    // DAT_0047fe78 - Primary HUD overlay (GRD files, animated)
 	_hudOverlay2Nut = nullptr;   // DAT_0047fe80 - Secondary HUD overlay
 
-	// Initialize audio system for RA2 (since we don't use iMUSE)
-	initAudio(11025);  // RA2 audio is 11025 Hz, not 22050 Hz
+	// Initialize audio system for RA2 (since we don't use iMUSE).
+	// Individual SMUSH audio blocks carry their own source rate.
+	initAudio(11025);
 
 	// Initialize and load sound effects (SYSTM/*.SAD files)
 	for (i = 0; i < kRA2NumSfx; i++) {
diff --git a/engines/scumm/smush/rebel/smush_player_ra2.cpp b/engines/scumm/smush/rebel/smush_player_ra2.cpp
index 58d1c3fdf18..232ab3d9c92 100644
--- a/engines/scumm/smush/rebel/smush_player_ra2.cpp
+++ b/engines/scumm/smush/rebel/smush_player_ra2.cpp
@@ -772,8 +772,8 @@ void SmushPlayerRebel2::ra2HandleGost(int32 subSize, Common::SeekableReadStream
  * RA2 per-frame audio processing.
  */
 void SmushPlayerRebel2::handleGameParseNextFrame() {
-	// Call processDispatches directly since RA2 has no iMUSE
-	// 11025 Hz / 12 fps = ~918 samples per frame
+	// Call processDispatches directly since RA2 has no iMUSE.
+	// This is the mixer cadence; SAUD opcodes provide per-block source rates.
 	processDispatches(_smushAudioSampleRate / 12);
 }
 



From noreply at scummvm.org  Wed Jun  3 14:46:45 2026
From: noreply at scummvm.org (neuromancer)
Date: Wed,  3 Jun 2026 14:46:45 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 faebc8510ac195db05db3f5b88da549262ee7b5c
Message-ID: <20260603144645.DF79D700591@ron.scummvm.net>

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:
b0f8595f44 SCUMM: RA2: fixed some coloring issue in certain fonts
bdb923e0a3 SCUMM: RA2: ESC should bring the scummvm menu instead of sending enter
faebc8510a SCUMM: RA2: refined controls for level 14


Commit: b0f8595f4414952706e225ae313767680a37b24e
    https://github.com/scummvm/scummvm/commit/b0f8595f4414952706e225ae313767680a37b24e
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-03T16:23:44+02:00

Commit Message:
SCUMM: RA2: fixed some coloring issue in certain fonts

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


diff --git a/engines/scumm/smush/rebel/smush_player_ra2.cpp b/engines/scumm/smush/rebel/smush_player_ra2.cpp
index 232ab3d9c92..64f483df637 100644
--- a/engines/scumm/smush/rebel/smush_player_ra2.cpp
+++ b/engines/scumm/smush/rebel/smush_player_ra2.cpp
@@ -195,6 +195,82 @@ bool SmushPlayerRebel2::handleGameFetch(int32 subSize, Common::SeekableReadStrea
 	return true;
 }
 
+bool SmushPlayerRebel2::handleGameTextResource(uint32 subType, int32 subSize, Common::SeekableReadStream &b) {
+	if (subType != MKTAG('T','E','X','T') && subType != MKTAG('T','R','E','S'))
+		return false;
+
+	if (subSize < 16) {
+		b.skip(subSize);
+		return true;
+	}
+
+	int posX = b.readSint16LE();
+	int posY = b.readSint16LE();
+	int flags = b.readSint16LE();
+	int left = b.readSint16LE();
+	int top = b.readSint16LE();
+	int width = b.readSint16LE();
+	int height = b.readSint16LE();
+	b.readUint16LE();
+
+	const char *str = nullptr;
+	char *text = nullptr;
+	int consumed = 16;
+
+	if (subType == MKTAG('T','E','X','T')) {
+		const int textSize = subSize - consumed;
+		if (textSize > 0) {
+			text = (char *)malloc(textSize + 1);
+			if (!text) {
+				b.skip(textSize);
+				return true;
+			}
+			b.read(text, textSize);
+			text[textSize] = 0;
+			consumed += textSize;
+			str = text;
+		}
+	} else if (subSize >= consumed + 2) {
+		int stringId = b.readUint16LE();
+		consumed += 2;
+		debugC(DEBUG_SMUSH, "SmushPlayerRebel2::handleGameTextResource: TRES string_id=%d pos=(%d,%d) flags=0x%x clip=(%d,%d,%d,%d) _strings=%p",
+			stringId, posX, posY, flags, left, top, width, height, (void *)_strings);
+		if (_strings)
+			str = _strings->get(stringId);
+	}
+
+	if (consumed < subSize)
+		b.skip(subSize - consumed);
+
+	if (!str) {
+		free(text);
+		return true;
+	}
+
+	int color = 1;
+	int fontId = 0;
+
+	while (str[0] == '^') {
+		if (str[1] == 'f' && Common::isDigit(str[2]) && Common::isDigit(str[3])) {
+			fontId = (str[2] - '0') * 10 + str[3] - '0';
+			str += 4;
+		} else if (str[1] == 'c' && Common::isDigit(str[2]) &&
+				Common::isDigit(str[3]) && Common::isDigit(str[4])) {
+			color = (str[2] - '0') * 100 + (str[3] - '0') * 10 + str[4] - '0';
+			str += 5;
+		} else {
+			break;
+		}
+	}
+
+	TextStyleFlags flg = (TextStyleFlags)(flags & 7);
+	if (ConfMan.getBool("subtitles"))
+		ra2HandleTextResource(str, fontId, color, posX, posY, left, top, width, height, flg);
+
+	free(text);
+	return true;
+}
+
 /**
  * RA2-specific text rendering using SmushMultiFont for inline font switching.
  */
diff --git a/engines/scumm/smush/rebel/smush_player_ra2.h b/engines/scumm/smush/rebel/smush_player_ra2.h
index 415401af885..5f4a33dbae8 100644
--- a/engines/scumm/smush/rebel/smush_player_ra2.h
+++ b/engines/scumm/smush/rebel/smush_player_ra2.h
@@ -38,6 +38,7 @@ protected:
 	void releaseGameVideoState() override;
 	bool shouldPreserveFrameBuffer() const override { return true; }
 	bool handleGameFetch(int32 subSize, Common::SeekableReadStream &b) override;
+	bool handleGameTextResource(uint32 subType, int32 subSize, Common::SeekableReadStream &b) override;
 	bool handleGameTextRendering(const char *str, int fontId, int color, int pos_x, int pos_y, int left, int top, int width, int height, TextStyleFlags flg) override;
 	bool shouldAlwaysShowSubtitles() const override { return true; }
 	SmushFont *getGameFont(int font) override;


Commit: bdb923e0a34e5ed89eb21c21df2a6506676ac224
    https://github.com/scummvm/scummvm/commit/bdb923e0a34e5ed89eb21c21df2a6506676ac224
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-03T16:34:31+02:00

Commit Message:
SCUMM: RA2: ESC should bring the scummvm menu instead of sending enter

Changed paths:
    engines/scumm/insane/rebel2/menu.cpp
    engines/scumm/insane/rebel2/rebel.cpp
    engines/scumm/insane/rebel2/rebel.h


diff --git a/engines/scumm/insane/rebel2/menu.cpp b/engines/scumm/insane/rebel2/menu.cpp
index b238e163ce7..f82fe30354e 100644
--- a/engines/scumm/insane/rebel2/menu.cpp
+++ b/engines/scumm/insane/rebel2/menu.cpp
@@ -114,7 +114,8 @@ Common::String InsaneRebel2::getRandomMenuVideo() {
 //
 // Returns -1 (no action) or a 0-based selected menu item.
 // Events captured by notifyEvent() before ScummEngine consumes them.
-// Keyboard: Up=0x148, Down=0x150, Enter=0x0d, ESC=0x1b.
+// Keyboard: Up=0x148, Down=0x150, Enter=0x0d.
+// Physical ESC is handled by notifyEvent() and opens the ScummVM menu.
 // Mouse mode (DAT_0047a806 == 1): Y position maps to selection.
 //
 int InsaneRebel2::processMenuInput() {
@@ -167,9 +168,9 @@ int InsaneRebel2::processMenuInput() {
 				break;
 
 			case Common::KEYCODE_ESCAPE:
-				// ESC - Quit (last item) - emulates key code 0x1b
+				// Synthetic custom back action - quit/back (last item)
 				result = _menuItemCount - 1;  // Select quit option
-				debug("Menu: ESC pressed - selecting quit (item %d)", result);
+				debug("Menu: Back action - selecting quit (item %d)", result);
 				break;
 
 			default:
@@ -898,10 +899,10 @@ int InsaneRebel2::processChapterSelectInput() {
 				break;
 
 			case Common::KEYCODE_ESCAPE:
-				// ESC = Back to main menu (same as selecting BACK)
+				// Synthetic custom back action (same as selecting BACK)
 				setVirtualKeyboardVisible(false);
 				result = 16;  // BACK index
-				debug("ChapterSelect: ESC pressed - back to menu");
+				debug("ChapterSelect: Back action - back to menu");
 				break;
 
 			case Common::KEYCODE_BACKSPACE:
@@ -1395,7 +1396,7 @@ int InsaneRebel2::processLevelSelectInput() {
 						debug("PilotName: confirmed '%s'", _pilotNameInput.c_str());
 					}
 				} else if (event.kbd.keycode == Common::KEYCODE_ESCAPE) {
-					// Cancel name entry ??? delete the pilot slot we created
+					// Synthetic custom back action - cancel name entry
 					if (_pilotEditIndex >= 0 && _pilotEditIndex < _numPilots) {
 						deletePilot(_pilotEditIndex);
 					}
diff --git a/engines/scumm/insane/rebel2/rebel.cpp b/engines/scumm/insane/rebel2/rebel.cpp
index 0585d558df7..471d2c96b98 100644
--- a/engines/scumm/insane/rebel2/rebel.cpp
+++ b/engines/scumm/insane/rebel2/rebel.cpp
@@ -97,6 +97,15 @@ bool isRebel2MenuDirectionKey(Common::KeyCode keycode) {
 	       keycode == Common::KEYCODE_RIGHT;
 }
 
+bool isRebel2MenuState(InsaneRebel2::GameState state) {
+	return state == InsaneRebel2::kStateMainMenu ||
+	       state == InsaneRebel2::kStatePilotSelect ||
+	       state == InsaneRebel2::kStateDifficultySelect ||
+	       state == InsaneRebel2::kStateChapterSelect ||
+	       state == InsaneRebel2::kStateOptions ||
+	       state == InsaneRebel2::kStateTopPilots;
+}
+
 InsaneRebel2::InsaneRebel2(ScummEngine_v7 *scumm) {
 	_vm = scumm;
 
@@ -600,6 +609,17 @@ void InsaneRebel2::openGameplayMainMenu(SmushPlayer *splayer) {
 	_lastGameplayMenuCloseTime = _vm->_system->getMillis();
 }
 
+void InsaneRebel2::openMenuMainMenu(SmushPlayer *splayer) {
+	if (splayer && !splayer->_paused) {
+		splayer->pause();
+		_vm->openMainMenuDialog();
+		splayer->unpause();
+		return;
+	}
+
+	_vm->openMainMenuDialog();
+}
+
 // 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.
@@ -752,12 +772,7 @@ bool InsaneRebel2::notifyEvent(const Common::Event &event) {
 	if (event.type == Common::EVENT_CUSTOM_ENGINE_ACTION_START ||
 		event.type == Common::EVENT_CUSTOM_ENGINE_ACTION_END) {
 		const bool pressed = (event.type == Common::EVENT_CUSTOM_ENGINE_ACTION_START);
-		const bool menuState = _gameState == kStateMainMenu ||
-		                       _gameState == kStatePilotSelect ||
-		                       _gameState == kStateDifficultySelect ||
-		                       _gameState == kStateChapterSelect ||
-		                       _gameState == kStateOptions ||
-		                       _gameState == kStateTopPilots;
+		const bool menuState = isRebel2MenuState(_gameState);
 
 		if (event.customType == kScummActionInsaneSkip) {
 			if (!pressed)
@@ -787,11 +802,8 @@ bool InsaneRebel2::notifyEvent(const Common::Event &event) {
 				return true;
 
 			if (_menuInputActive && menuState) {
-				Common::Event syntheticEvent = Common::Event();
-				syntheticEvent.type = Common::EVENT_KEYDOWN;
-				syntheticEvent.kbd.keycode = Common::KEYCODE_ESCAPE;
-				syntheticEvent.kbd.ascii = Common::ASCII_ESCAPE;
-				_menuEventQueue.push(syntheticEvent);
+				debug("Rebel2: Back/menu action in menu - opening ScummVM menu");
+				openMenuMainMenu(splayer);
 				return true;
 			}
 
@@ -903,6 +915,19 @@ bool InsaneRebel2::notifyEvent(const Common::Event &event) {
 		// consumed (and returned true for) the cases they handle.
 	}
 
+	if (_menuInputActive && isRebel2MenuState(_gameState) &&
+			(event.type == Common::EVENT_MAINMENU ||
+			 (event.type == Common::EVENT_KEYDOWN && event.kbd.keycode == Common::KEYCODE_ESCAPE))) {
+		if (event.type == Common::EVENT_KEYDOWN && event.kbdRepeat) {
+			debug("Rebel2: Ignoring repeated ESC keydown in menu");
+			return true;
+		}
+
+		debug("Rebel2: Opening ScummVM menu from menu state");
+		openMenuMainMenu(splayer);
+		return true;
+	}
+
 	if (_menuInputActive && isMenuTextInputActive() && event.type == Common::EVENT_KEYDOWN) {
 		_menuEventQueue.push(event);
 		return true;
@@ -962,21 +987,11 @@ bool InsaneRebel2::notifyEvent(const Common::Event &event) {
 		switch (event.kbd.keycode) {
 		case Common::KEYCODE_ESCAPE:
 			// ESC handling depends on game state:
-			// - In menus: Select quit option and confirm
+			// - In menus: Open the ScummVM menu (handled above)
 			// - During gameplay: Pause and open ScummVM menu
 			// - During cutscenes/intros: Skip video
 			if (splayer) {
-				if (_menuInputActive && (_gameState == kStateMainMenu ||
-				                          _gameState == kStatePilotSelect ||
-				                          _gameState == kStateDifficultySelect ||
-				                          _gameState == kStateChapterSelect)) {
-					// In menu mode: Select quit option and confirm selection
-					// This emulates the assembly behavior from FUN_0041f5ae
-					_menuSelection = _menuItemCount - 1;  // Select last item (quit/back)
-					_menuSelectionConfirmed = true;
-					debug("Rebel2: ESC pressed in menu - selecting quit (item %d)", _menuSelection);
-					_vm->_smushVideoShouldFinish = true;
-				} else if (_gameState == kStateGameplay && _rebelHandler != 0) {
+				if (_gameState == kStateGameplay && _rebelHandler != 0) {
 					// During active gameplay (handler != 0): pause and open ScummVM menu.
 					debug("Rebel2: ESC pressed during gameplay - opening ScummVM menu");
 					openGameplayMainMenu(splayer);
diff --git a/engines/scumm/insane/rebel2/rebel.h b/engines/scumm/insane/rebel2/rebel.h
index 45ed0628a66..f2d5cdb3f78 100644
--- a/engines/scumm/insane/rebel2/rebel.h
+++ b/engines/scumm/insane/rebel2/rebel.h
@@ -522,6 +522,7 @@ public:
 	uint32 _lastGameplayMenuCloseTime;
 	uint32 _lastMenuGamepadNavigationTime;
 	void openGameplayMainMenu(SmushPlayer *splayer);
+	void openMenuMainMenu(SmushPlayer *splayer);
 	bool isBitSet(int n) override;
 	void setBit(int n) override;
 


Commit: faebc8510ac195db05db3f5b88da549262ee7b5c
    https://github.com/scummvm/scummvm/commit/faebc8510ac195db05db3f5b88da549262ee7b5c
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-03T16:46:28+02:00

Commit Message:
SCUMM: RA2: refined controls for level 14

Changed paths:
    engines/scumm/insane/rebel2/rebel.cpp


diff --git a/engines/scumm/insane/rebel2/rebel.cpp b/engines/scumm/insane/rebel2/rebel.cpp
index 471d2c96b98..a777dc8353e 100644
--- a/engines/scumm/insane/rebel2/rebel.cpp
+++ b/engines/scumm/insane/rebel2/rebel.cpp
@@ -56,7 +56,7 @@ const uint32 kRA2MenuGamepadNavigationDebounceMs = 250;
 const uint32 kRA2MenuGamepadMouseSuppressMs = 250;
 
 bool rebel2UsesRelativeGamepadAim(int selectedLevel) {
-	return selectedLevel == 1 || selectedLevel == 5;
+	return selectedLevel == 1 || selectedLevel == 5 || selectedLevel == 14;
 }
 
 bool isRebel2RawMenuAxis(int axis) {
@@ -1680,7 +1680,7 @@ void InsaneRebel2::updateGameplayAimFromGamepad() {
 	bool activeGamepadAim = false;
 
 	if (_rebelHandler == 0x26 && rebel2UsesRelativeGamepadAim(_selectedLevel)) {
-		// Levels 1 and 5 play best with the older mouse-like gamepad behavior from
+		// Levels 1, 5, and 14 play best with the older mouse-like gamepad behavior from
 		// ec305dee371/0025c4e1086: pan the reticle directly and leave it where
 		// the player releases the stick. Later handler 0x26 levels keep the
 		// original-style centered mapping for obstacle avoidance.



From noreply at scummvm.org  Wed Jun  3 16:31:17 2026
From: noreply at scummvm.org (mgerhardy)
Date: Wed,  3 Jun 2026 16:31:17 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm-tools master ->
 d06a5562375ce5d27ad6846df1310c566a215856
Message-ID: <20260603163117.D49337005B3@ron.scummvm.net>

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

Summary:
c184c4f8ab MACS2: renamed some opcodes to match names in ghidra
88af2bbcef MACS2: updated the decompiler to also support json output and unified with the imgui decompiler in scummvm itself
fadaad3fbf MASC2: started to export json scene data, too
d06a556237 MACS2: this is a shadowmap


Commit: c184c4f8abe5ee8ddf61afd145f243b3638cf15e
    https://github.com/scummvm/scummvm-tools/commit/c184c4f8abe5ee8ddf61afd145f243b3638cf15e
Author: Martin Gerhardy (martin.gerhardy at gmail.com)
Date: 2026-05-31T17:41:31+02:00

Commit Message:
MACS2: renamed some opcodes to match names in ghidra

Changed paths:
    engines/macs2/demacs2.cpp


diff --git a/engines/macs2/demacs2.cpp b/engines/macs2/demacs2.cpp
index 099ec06e..96cc2bca 100644
--- a/engines/macs2/demacs2.cpp
+++ b/engines/macs2/demacs2.cpp
@@ -21,10 +21,10 @@
 
 /* MACS2 Script decompiler */
 
-#include <stdio.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
+#include <cstdio>
+#include <cstdint>
+#include <cstdlib>
+#include <cstring>
 #include <vector>
 #include <string>
 
@@ -337,11 +337,11 @@ static void disassemble() {
 			printf("subtract %s, %s\n", a.c_str(), b.c_str());
 			break;
 		}
-		case 0x26: { // setAnimSlot
+		case 0x26: { // loadSpecialAnim
 			std::string obj = formatValue();
-			formatValue(); // unused
+			std::string decodeFlag = formatValue(); // decode/enable flag (runtime +0x182)
 			uint8_t animIdx = readByte();
-			printf("setAnimSlot obj=%s anim=%u\n", obj.c_str(), animIdx);
+			printf("loadSpecialAnim obj=%s decode=%s anim=%u\n", obj.c_str(), decodeFlag.c_str(), animIdx);
 			break;
 		}
 		case 0x27: { // setMaxAnimFrame
@@ -403,9 +403,9 @@ static void disassemble() {
 			printf("nop30\n");
 			break;
 		}
-		case 0x31: { // setMusicVolume
+		case 0x31: { // setVolume
 			std::string vol = formatValue();
-			printf("setMusicVolume %s\n", vol.c_str());
+			printf("setVolume %s\n", vol.c_str());
 			break;
 		}
 		case 0x32: { // setClickable


Commit: 88af2bbcef51c400c3c6f85e0c62be4a994e170b
    https://github.com/scummvm/scummvm-tools/commit/88af2bbcef51c400c3c6f85e0c62be4a994e170b
Author: Martin Gerhardy (martin.gerhardy at gmail.com)
Date: 2026-05-31T17:45:00+02:00

Commit Message:
MACS2: updated the decompiler to also support json output and unified with the imgui decompiler in scummvm itself

Changed paths:
    engines/macs2/demacs2.cpp


diff --git a/engines/macs2/demacs2.cpp b/engines/macs2/demacs2.cpp
index 96cc2bca..8898d093 100644
--- a/engines/macs2/demacs2.cpp
+++ b/engines/macs2/demacs2.cpp
@@ -21,19 +21,20 @@
 
 /* MACS2 Script decompiler */
 
-#include <cstdio>
 #include <cstdint>
+#include <cstdio>
 #include <cstdlib>
 #include <cstring>
-#include <vector>
 #include <string>
+#include <vector>
 
 static uint8_t *scriptData = nullptr;
 static uint32_t scriptSize = 0;
 static uint32_t pos = 0;
 
 static uint8_t readByte() {
-	if (pos >= scriptSize) return 0;
+	if (pos >= scriptSize)
+		return 0;
 	return scriptData[pos++];
 }
 
@@ -43,6 +44,41 @@ static uint16_t readWord() {
 	return (uint16_t)hi << 8 | lo;
 }
 
+static const struct {
+	uint16_t id;
+	const char *name;
+} kSpecialNames[] = {
+	{0x01, "interactedUse"},
+	{0x02, "interactedLook"},
+	{0x03, "interactedTalk"},
+	{0x04, "areaAtActor"},
+	{0x0B, "isRepeatRun"},
+	{0x0D, "dialogueResult"},
+	{0x23, "pathWalkable"},
+	{0x24, "actorX"},
+	{0x25, "actorY"},
+	{0x26, "isSceneInit"},
+	{0x27, "areaRepeatRun"},
+	{0x28, "invCheck"},
+	{0x29, "animBlobRange"},
+	{0x2A, "invCombine"},
+	{0x2B, "invAction"},
+	{0x2C, "interactedMap"},
+	{0x2D, "curScene"},
+	{0x2E, "const2"},
+	{0x2F, "prevScene"},
+	{0x30, "musicActive"},
+	{0x31, "soundActive"},
+};
+
+static const char *getSpecialName(uint16_t val) {
+	for (const auto &s : kSpecialNames) {
+		if (s.id == val)
+			return s.name;
+	}
+	return nullptr;
+}
+
 static std::string formatValue() {
 	uint8_t type = readByte();
 	uint16_t val = readWord();
@@ -51,531 +87,682 @@ static std::string formatValue() {
 		snprintf(buf, sizeof(buf), "%u", val);
 		return buf;
 	} else if (type == 0xFF) {
-		switch (val) {
-		case 0x01: return "interactedUse";
-		case 0x02: return "interactedLook";
-		case 0x03: return "interactedTalk";
-		case 0x04: return "areaAtActor";
-		case 0x0B: return "isRepeatRun";
-		case 0x0D: return "dialogueResult";
-		case 0x23: return "pathWalkable";
-		case 0x24: return "actorX";
-		case 0x25: return "actorY";
-		case 0x26: return "isSceneInit";
-		case 0x28: return "invCheck";
-		case 0x2A: return "invCombine";
-		case 0x2B: return "invAction";
-		case 0x2D: return "curScene";
-		case 0x2F: return "prevScene";
-		default:
+		const char *name = getSpecialName(val);
+		if (name)
+			return name;
+		if (val >= 0x0E && val <= 0x22) {
 			char buf[32];
-			snprintf(buf, sizeof(buf), "special[0x%02x]", val);
+			snprintf(buf, sizeof(buf), "%u", val - 0x0D);
 			return buf;
 		}
+		char buf[32];
+		snprintf(buf, sizeof(buf), "special[0x%02x]", val);
+		return buf;
 	}
 	char buf[32];
 	snprintf(buf, sizeof(buf), "var[%u]", val);
 	return buf;
 }
 
+static const char *getOpcodeName(uint8_t opcode) {
+	switch (opcode) {
+	case 0x01:
+		return "setVar";
+	case 0x02:
+		return "setVarOr";
+	case 0x03:
+		return "ifFalse";
+	case 0x04:
+		return "ifTrue";
+	case 0x05:
+		return "compare";
+	case 0x06:
+		return "ifInteraction";
+	case 0x07:
+		return "endIf";
+	case 0x08:
+		return "else";
+	case 0x09:
+		return "nop09";
+	case 0x0A:
+		return "printStringLeft";
+	case 0x0B:
+		return "moveObject";
+	case 0x0C:
+		return "changeScene";
+	case 0x0D:
+		return "showDialogue";
+	case 0x0E:
+		return "changeAnimation";
+	case 0x0F:
+		return "frameWait";
+	case 0x10:
+		return "walkToPosition";
+	case 0x11:
+		return "waitForWalk";
+	case 0x12:
+		return "setPathfinding";
+	case 0x13:
+		return "skipUntil14";
+	case 0x14:
+		return "skipWord";
+	case 0x15:
+		return "clearDialogueChoices";
+	case 0x16:
+		return "addDialogueChoice";
+	case 0x17:
+		return "showDialogueChoice";
+	case 0x18:
+		return "dismissPanel";
+	case 0x19:
+		return "walkToAndPickup";
+	case 0x1A:
+		return "setPickupFrames";
+	case 0x1B:
+		return "setupObject";
+	case 0x1C:
+		return "setSkippable";
+	case 0x1D:
+		return "clearSkippable";
+	case 0x1E:
+		return "playAnimation";
+	case 0x1F:
+		return "pathWalkable = testPathfinding";
+	case 0x20:
+		return "setYOffset";
+	case 0x21:
+		return "setMotion";
+	case 0x22:
+		return "setOrientation";
+	case 0x23:
+		return "moveToPosition";
+	case 0x24:
+		return "addValues";
+	case 0x25:
+		return "subValues";
+	case 0x26:
+		return "loadSpecialAnim";
+	case 0x27:
+		return "setDirection";
+	case 0x28:
+		return "stopAnimation";
+	case 0x29:
+		return "openInventory";
+	case 0x2A:
+		return "loadObjectAnim";
+	case 0x2B:
+		return "checkObjectData";
+	case 0x2C:
+		return "invCheck = checkInventory";
+	case 0x2D:
+		return "setSnapToTarget";
+	case 0x2E:
+		return "animRangeTest = testSceneAnimFrame";
+	case 0x2F:
+		return "animRangeTest = testObjectAnimFrame";
+	case 0x30:
+		return "printStringRight";
+	case 0x31:
+		return "setVolume";
+	case 0x32:
+		return "setObjectClickable";
+	case 0x33:
+		return "setObjectVisible";
+	case 0x34:
+		return "setHotspotOverride";
+	case 0x35:
+		return "setObjectBounds";
+	case 0x36:
+		return "dismissAllPanels";
+	case 0x37:
+		return "resetToSceneScript";
+	case 0x38:
+		return "loadOverlayFont";
+	case 0x39:
+		return "endOverlayText";
+	case 0x3A:
+		return "addOverlayTextEntry";
+	case 0x3B:
+		return "clearOverlayText";
+	case 0x3C:
+		return "fadeToBlack";
+	case 0x3D:
+		return "fadeFromBlack";
+	case 0x3E:
+		return "loadPcmSound";
+	case 0x3F:
+		return "freePcmSound";
+	case 0x40:
+		return "playPcmSound";
+	case 0x41:
+		return "waitForSound";
+	case 0x42:
+		return "stopPcmSound";
+	case 0x43:
+		return "loadMusicSlot";
+	case 0x44:
+		return "playMusicSlot";
+	case 0x45:
+		return "stopMusicSlot";
+	case 0x46:
+		return "freeMusicSlot";
+	case 0x47:
+		return "waitForMusic";
+	case 0x48:
+		return "getObjectX";
+	case 0x49:
+		return "getObjectY";
+	case 0x4A:
+		return "getObjectField8";
+	case 0x4B:
+		return "getObjectOrientation";
+	case 0x4C:
+		return "clearActorInventory";
+	case 0x4D:
+		return "setPathfindingRemap";
+	case 0x4E:
+		return "waitForAdlib";
+	default:
+		return "???";
+	}
+}
+
 static const char *cmpOpName(uint8_t op) {
 	switch (op) {
-	case 0x01: return "==";
-	case 0x02: return "!=";
-	case 0x03: return "<";
-	case 0x04: return ">";
-	case 0x05: return "<=";
-	case 0x06: return ">=";
-	default: return "?";
+	case 0x01:
+		return "==";
+	case 0x02:
+		return "!=";
+	case 0x03:
+		return "<";
+	case 0x04:
+		return ">";
+	case 0x05:
+		return "<=";
+	case 0x06:
+		return ">=";
+	default:
+		return "?";
 	}
 }
 
-static void disassemble() {
+struct DecompiledLine {
+	uint32_t offset;
+	uint8_t opcode;
+	int indent;
+	std::string params;
+};
+
+static std::string decodeParams(uint8_t opcode, uint32_t endPos, int &indent) {
+	std::string result;
+
+	char buf[256];
+	switch (opcode) {
+	case 0x01:
+	case 0x02: {
+		readByte();
+		uint16_t varIdx = readWord();
+		std::string val = formatValue();
+		snprintf(buf, sizeof(buf), " var[%u] = %s", varIdx, val.c_str());
+		result = buf;
+		break;
+	}
+	case 0x03:
+	case 0x04: {
+		std::string val = formatValue();
+		snprintf(buf, sizeof(buf), " (%s)", val.c_str());
+		result = buf;
+		indent++;
+		break;
+	}
+	case 0x05: {
+		uint8_t cmpOp = readByte();
+		std::string a = formatValue();
+		std::string b = formatValue();
+		snprintf(buf, sizeof(buf), " (%s %s %s)", a.c_str(), cmpOpName(cmpOp), b.c_str());
+		result = buf;
+		indent++;
+		break;
+	}
+	case 0x06: {
+		uint8_t subOp = readByte();
+		std::string i = formatValue();
+		std::string a = formatValue();
+		std::string b = formatValue();
+		snprintf(buf, sizeof(buf), " %s(%s, %s, %s)", subOp == 2 ? "NOT " : "", i.c_str(), a.c_str(), b.c_str());
+		result = buf;
+		indent++;
+		break;
+	}
+	case 0x08: {
+		indent++;
+		break;
+	}
+	case 0x0A: {
+		std::string x = formatValue();
+		std::string y = formatValue();
+		uint16_t strOffset = readWord();
+		uint16_t numLines = readWord();
+		snprintf(buf, sizeof(buf), " pos=(%s,%s) str=%u lines=%u", x.c_str(), y.c_str(), strOffset, numLines);
+		result = buf;
+		break;
+	}
+	case 0x0B: {
+		std::string obj = formatValue();
+		std::string scene = formatValue();
+		std::string x = formatValue();
+		std::string y = formatValue();
+		snprintf(buf, sizeof(buf), " obj=%s scene=%s pos=(%s,%s)", obj.c_str(), scene.c_str(), x.c_str(), y.c_str());
+		result = buf;
+		break;
+	}
+	case 0x0C: {
+		std::string scene = formatValue();
+		std::string mode = formatValue();
+		std::string speed = formatValue();
+		snprintf(buf, sizeof(buf), " scene=%s mode=%s speed=%s", scene.c_str(), mode.c_str(), speed.c_str());
+		result = buf;
+		break;
+	}
+	case 0x0D: {
+		std::string obj = formatValue();
+		std::string x = formatValue();
+		std::string y = formatValue();
+		std::string side = formatValue();
+		uint16_t strOffset = readWord();
+		uint16_t numLines = readWord();
+		snprintf(buf, sizeof(buf), " obj=%s pos=(%s,%s) side=%s str=%u lines=%u",
+				 obj.c_str(), x.c_str(), y.c_str(), side.c_str(), strOffset, numLines);
+		result = buf;
+		break;
+	}
+	case 0x0F:
+	case 0x31:
+	case 0x3C:
+	case 0x3D: {
+		std::string val = formatValue();
+		result = " " + val;
+		break;
+	}
+	case 0x10: {
+		std::string obj = formatValue();
+		std::string x = formatValue();
+		std::string y = formatValue();
+		snprintf(buf, sizeof(buf), " obj=%s pos=(%s,%s)", obj.c_str(), x.c_str(), y.c_str());
+		result = buf;
+		break;
+	}
+	case 0x11:
+	case 0x47: {
+		std::string obj = formatValue();
+		result = " obj=" + obj;
+		break;
+	}
+	case 0x12: {
+		std::string area = formatValue();
+		std::string active = formatValue();
+		std::string val = formatValue();
+		snprintf(buf, sizeof(buf), " area=%s active=%s val=%s", area.c_str(), active.c_str(), val.c_str());
+		result = buf;
+		break;
+	}
+	case 0x14: {
+		uint16_t w = readWord();
+		snprintf(buf, sizeof(buf), " 0x%04x", w);
+		result = buf;
+		break;
+	}
+	case 0x16: {
+		std::string idx = formatValue();
+		uint16_t strOffset = readWord();
+		uint16_t numLines = readWord();
+		snprintf(buf, sizeof(buf), " idx=%s str=%u lines=%u", idx.c_str(), strOffset, numLines);
+		result = buf;
+		break;
+	}
+	case 0x17: {
+		std::string obj = formatValue();
+		std::string x = formatValue();
+		std::string y = formatValue();
+		std::string side = formatValue();
+		snprintf(buf, sizeof(buf), " obj=%s pos=(%s,%s) side=%s", obj.c_str(), x.c_str(), y.c_str(), side.c_str());
+		result = buf;
+		break;
+	}
+	case 0x19: {
+		std::string actor = formatValue();
+		std::string obj = formatValue();
+		snprintf(buf, sizeof(buf), " actor=%s obj=%s", actor.c_str(), obj.c_str());
+		result = buf;
+		break;
+	}
+	case 0x1A: {
+		std::string obj = formatValue();
+		std::string start = formatValue();
+		std::string end = formatValue();
+		snprintf(buf, sizeof(buf), " obj=%s start=%s end=%s", obj.c_str(), start.c_str(), end.c_str());
+		result = buf;
+		break;
+	}
+	case 0x1B: {
+		std::string obj = formatValue();
+		std::string slot = formatValue();
+		std::string speed = formatValue();
+		snprintf(buf, sizeof(buf), " obj=%s slot=%s speed=%s", obj.c_str(), slot.c_str(), speed.c_str());
+		result = buf;
+		break;
+	}
+	case 0x1E: {
+		std::string obj = formatValue();
+		std::string slot = formatValue();
+		std::string frame = formatValue();
+		snprintf(buf, sizeof(buf), " obj=%s slot=%s frame=%s", obj.c_str(), slot.c_str(), frame.c_str());
+		result = buf;
+		break;
+	}
+	case 0x1F: {
+		std::string obj = formatValue();
+		std::string x = formatValue();
+		std::string y = formatValue();
+		snprintf(buf, sizeof(buf), " obj=%s pos=(%s,%s)", obj.c_str(), x.c_str(), y.c_str());
+		result = buf;
+		break;
+	}
+	case 0x20: {
+		std::string obj = formatValue();
+		std::string offset = formatValue();
+		snprintf(buf, sizeof(buf), " obj=%s offset=%s", obj.c_str(), offset.c_str());
+		result = buf;
+		break;
+	}
+	case 0x21: {
+		std::string obj = formatValue();
+		std::string target = formatValue();
+		std::string delta = formatValue();
+		std::string dist = formatValue();
+		snprintf(buf, sizeof(buf), " obj=%s target=%s delta=%s dist=%s", obj.c_str(), target.c_str(), delta.c_str(), dist.c_str());
+		result = buf;
+		break;
+	}
+	case 0x22: {
+		std::string obj = formatValue();
+		std::string anim = formatValue();
+		snprintf(buf, sizeof(buf), " obj=%s anim=%s", obj.c_str(), anim.c_str());
+		result = buf;
+		break;
+	}
+	case 0x23: {
+		std::string obj = formatValue();
+		std::string x = formatValue();
+		std::string y = formatValue();
+		std::string voff = formatValue();
+		snprintf(buf, sizeof(buf), " obj=%s pos=(%s,%s) voff=%s", obj.c_str(), x.c_str(), y.c_str(), voff.c_str());
+		result = buf;
+		break;
+	}
+	case 0x24:
+	case 0x25: {
+		std::string a = formatValue();
+		std::string b = formatValue();
+		const char *op = (opcode == 0x24) ? "+" : "-";
+		snprintf(buf, sizeof(buf), " %s = %s %s %s", a.c_str(), a.c_str(), op, b.c_str());
+		result = buf;
+		break;
+	}
+	case 0x26: {
+		std::string obj = formatValue();
+		std::string decodeFlag = formatValue();
+		uint8_t animIdx = readByte();
+		snprintf(buf, sizeof(buf), " obj=%s decode=%s anim=%u", obj.c_str(), decodeFlag.c_str(), animIdx);
+		result = buf;
+		break;
+	}
+	case 0x27: {
+		std::string obj = formatValue();
+		std::string maxFrame = formatValue();
+		snprintf(buf, sizeof(buf), " obj=%s maxFrame=%s", obj.c_str(), maxFrame.c_str());
+		result = buf;
+		break;
+	}
+	case 0x29: {
+		std::string obj = formatValue();
+		result = " obj=" + obj;
+		break;
+	}
+	case 0x2A: {
+		std::string obj = formatValue();
+		std::string slot = formatValue();
+		std::string decode = formatValue();
+		uint8_t idx = readByte();
+		snprintf(buf, sizeof(buf), " obj=%s slot=%s decode=%s idx=%u", obj.c_str(), slot.c_str(), decode.c_str(), idx);
+		result = buf;
+		break;
+	}
+	case 0x2B: {
+		std::string obj = formatValue();
+		result = " obj=" + obj;
+		break;
+	}
+	case 0x2C:
+	case 0x32:
+	case 0x33:
+	case 0x2D: {
+		std::string obj = formatValue();
+		std::string val = formatValue();
+		snprintf(buf, sizeof(buf), " obj=%s val=%s", obj.c_str(), val.c_str());
+		result = buf;
+		break;
+	}
+	case 0x2E: {
+		std::string anim = formatValue();
+		std::string lo = formatValue();
+		std::string hi = formatValue();
+		snprintf(buf, sizeof(buf), " anim=%s range=[%s,%s]", anim.c_str(), lo.c_str(), hi.c_str());
+		result = buf;
+		break;
+	}
+	case 0x2F: {
+		std::string obj = formatValue();
+		std::string slot = formatValue();
+		std::string lo = formatValue();
+		std::string hi = formatValue();
+		snprintf(buf, sizeof(buf), " obj=%s slot=%s range=[%s,%s]", obj.c_str(), slot.c_str(), lo.c_str(), hi.c_str());
+		result = buf;
+		break;
+	}
+	case 0x34:
+	case 0x4D: {
+		std::string a = formatValue();
+		std::string b = formatValue();
+		snprintf(buf, sizeof(buf), " %s -> %s", a.c_str(), b.c_str());
+		result = buf;
+		break;
+	}
+	case 0x35: {
+		std::string obj = formatValue();
+		std::string parent = formatValue();
+		std::string a = formatValue();
+		std::string b = formatValue();
+		std::string c = formatValue();
+		snprintf(buf, sizeof(buf), " obj=%s parent=%s (%s,%s,%s)", obj.c_str(), parent.c_str(), a.c_str(), b.c_str(), c.c_str());
+		result = buf;
+		break;
+	}
+	case 0x38:
+	case 0x3E: {
+		uint8_t resIdx = readByte();
+		snprintf(buf, sizeof(buf), " res=%u", resIdx);
+		result = buf;
+		break;
+	}
+	case 0x3A: {
+		std::string x = formatValue();
+		std::string y = formatValue();
+		std::string align = formatValue();
+		uint16_t strOffset = readWord();
+		uint16_t entryType = readWord();
+		snprintf(buf, sizeof(buf), " pos=(%s,%s) align=%s str=%u type=%u", x.c_str(), y.c_str(), align.c_str(), strOffset, entryType);
+		result = buf;
+		break;
+	}
+	case 0x43: {
+		std::string slot = formatValue();
+		uint8_t resIdx = readByte();
+		snprintf(buf, sizeof(buf), " slot=%s res=%u", slot.c_str(), resIdx);
+		result = buf;
+		break;
+	}
+	case 0x44:
+	case 0x45: {
+		std::string slot = formatValue();
+		std::string a = formatValue();
+		std::string b = formatValue();
+		snprintf(buf, sizeof(buf), " slot=%s %s %s", slot.c_str(), a.c_str(), b.c_str());
+		result = buf;
+		break;
+	}
+	case 0x46: {
+		std::string slot = formatValue();
+		result = " slot=" + slot;
+		break;
+	}
+	case 0x48:
+	case 0x49:
+	case 0x4A:
+	case 0x4B: {
+		std::string obj = formatValue();
+		if (pos + 3 <= endPos) {
+			readByte();
+			uint16_t varIdx = readWord();
+			snprintf(buf, sizeof(buf), " var[%u] = obj=%s", varIdx, obj.c_str());
+			result = buf;
+		} else {
+			result = " obj=" + obj;
+		}
+		break;
+	}
+	case 0x07:
+	case 0x09:
+	case 0x0E:
+	case 0x13:
+	case 0x15:
+	case 0x18:
+	case 0x1C:
+	case 0x1D:
+	case 0x28:
+	case 0x30:
+	case 0x36:
+	case 0x37:
+	case 0x39:
+	case 0x3B:
+	case 0x3F:
+	case 0x40:
+	case 0x41:
+	case 0x42:
+	case 0x4C:
+	case 0x4E:
+		break;
+	default: {
+		uint8_t length = (uint8_t)(endPos - pos);
+		if (length > 0 && length <= 30) {
+			result = " [";
+			uint32_t dataPos = pos;
+			for (uint8_t i = 0; i < length && dataPos < scriptSize; i++, dataPos++) {
+				if (i)
+					result += " ";
+				snprintf(buf, sizeof(buf), "%02x", scriptData[dataPos]);
+				result += buf;
+			}
+			result += "]";
+		}
+		break;
+	}
+	}
+	return result;
+}
+
+static std::vector<DecompiledLine> decompileToLines() {
+	std::vector<DecompiledLine> lines;
 	int indent = 0;
 
 	while (pos < scriptSize - 1) {
 		uint32_t instrAddr = pos;
 		uint8_t opcode = readByte();
-		if (opcode == 0x00) continue;
+		if (opcode == 0x00)
+			continue;
 
 		uint8_t length = readByte();
 		uint32_t endPos = pos + length;
 
-		// Adjust indent for block-end opcodes
-		if (opcode == 0x07 && indent > 0) indent--;
-		if (opcode == 0x08 && indent > 0) indent--;
+		if (opcode == 0x07 && indent > 0)
+			indent--;
+		if (opcode == 0x08 && indent > 0)
+			indent--;
 
-		for (int i = 0; i < indent; i++) printf("  ");
-		printf("%04x: ", instrAddr);
+		DecompiledLine line;
+		line.offset = instrAddr;
+		line.opcode = opcode;
+		line.indent = indent;
+		line.params = decodeParams(opcode, endPos, indent);
+		lines.push_back(line);
 
-		switch (opcode) {
-		case 0x01: { // setVar
-			readByte();
-			uint16_t varIdx = readWord();
-			std::string val = formatValue();
-			printf("setVar var[%u] = %s\n", varIdx, val.c_str());
-			break;
-		}
-		case 0x02: { // setVarOr
-			readByte();
-			uint16_t varIdx = readWord();
-			std::string val = formatValue();
-			printf("setVarOr var[%u] = %s\n", varIdx, val.c_str());
-			break;
-		}
-		case 0x03: { // ifTrue
-			std::string val = formatValue();
-			printf("ifTrue (%s)\n", val.c_str());
-			indent++;
-			break;
-		}
-		case 0x04: { // ifFalse
-			std::string val = formatValue();
-			printf("ifFalse (%s)\n", val.c_str());
-			indent++;
-			break;
-		}
-		case 0x05: { // compare
-			uint8_t cmpOp = readByte();
-			std::string a = formatValue();
-			std::string b = formatValue();
-			printf("compare (%s %s %s)\n", a.c_str(), cmpOpName(cmpOp), b.c_str());
-			indent++;
-			break;
-		}
-		case 0x06: { // ifInteraction
-			uint8_t subOp = readByte();
-			std::string i = formatValue();
-			std::string a = formatValue();
-			std::string b = formatValue();
-			printf("ifInteraction %s(%s, %s, %s)\n", subOp == 2 ? "NOT " : "", i.c_str(), a.c_str(), b.c_str());
-			indent++;
-			break;
-		}
-		case 0x07: { // endIf
-			printf("endIf\n");
-			break;
-		}
-		case 0x08: { // else
-			printf("else\n");
-			indent++;
-			break;
-		}
-		case 0x0A: { // printString
-			std::string x = formatValue();
-			std::string y = formatValue();
-			uint16_t strOffset = readWord();
-			uint16_t numLines = readWord();
-			printf("printString pos=(%s, %s) strOffset=%u numLines=%u\n", x.c_str(), y.c_str(), strOffset, numLines);
-			break;
-		}
-		case 0x0B: { // moveObject
-			std::string obj = formatValue();
-			std::string scene = formatValue();
-			std::string x = formatValue();
-			std::string y = formatValue();
-			printf("moveObject obj=%s scene=%s pos=(%s, %s)\n", obj.c_str(), scene.c_str(), x.c_str(), y.c_str());
-			break;
-		}
-		case 0x0C: { // changeScene
-			std::string scene = formatValue();
-			std::string mode = formatValue();
-			std::string speed = formatValue();
-			printf("changeScene scene=%s mode=%s speed=%s\n", scene.c_str(), mode.c_str(), speed.c_str());
-			break;
-		}
-		case 0x0D: { // showDialogue
-			std::string obj = formatValue();
-			std::string x = formatValue();
-			std::string y = formatValue();
-			std::string side = formatValue();
-			uint16_t strOffset = readWord();
-			uint16_t numLines = readWord();
-			printf("showDialogue obj=%s pos=(%s, %s) side=%s strOffset=%u numLines=%u\n",
-				   obj.c_str(), x.c_str(), y.c_str(), side.c_str(), strOffset, numLines);
-			break;
-		}
-		case 0x0E: { // changeAnim
-			printf("changeAnim\n");
-			break;
-		}
-		case 0x0F: { // frameWait
-			std::string val = formatValue();
-			printf("frameWait %s\n", val.c_str());
-			break;
-		}
-		case 0x10: { // walkTo
-			std::string obj = formatValue();
-			std::string x = formatValue();
-			std::string y = formatValue();
-			printf("walkTo obj=%s pos=(%s, %s)\n", obj.c_str(), x.c_str(), y.c_str());
-			break;
-		}
-		case 0x11: { // waitForWalk
-			std::string obj = formatValue();
-			printf("waitForWalk obj=%s\n", obj.c_str());
-			break;
-		}
-		case 0x12: { // setPathOverride
-			std::string area = formatValue();
-			std::string active = formatValue();
-			std::string val = formatValue();
-			printf("setPathOverride area=%s active=%s val=%s\n", area.c_str(), active.c_str(), val.c_str());
-			break;
-		}
-		case 0x13: { // loadAnim
-			printf("loadAnim\n");
-			break;
-		}
-		case 0x14: { // skipWord
-			uint16_t w = readWord();
-			printf("skipWord 0x%04x\n", w);
-			break;
-		}
-		case 0x15: { // clearDialogueChoices
-			printf("clearDialogueChoices\n");
-			break;
-		}
-		case 0x16: { // addDialogueChoice
-			std::string idx = formatValue();
-			uint16_t strOffset = readWord();
-			uint16_t numLines = readWord();
-			printf("addDialogueChoice idx=%s strOffset=%u numLines=%u\n", idx.c_str(), strOffset, numLines);
-			break;
-		}
-		case 0x17: { // showDialogueChoice
-			std::string obj = formatValue();
-			std::string x = formatValue();
-			std::string y = formatValue();
-			std::string side = formatValue();
-			printf("showDialogueChoice obj=%s pos=(%s, %s) side=%s\n", obj.c_str(), x.c_str(), y.c_str(), side.c_str());
-			break;
-		}
-		case 0x18: { // dismissPanel
-			printf("dismissPanel\n");
-			break;
-		}
-		case 0x19: { // walkToAndPickup
-			std::string actor = formatValue();
-			std::string obj = formatValue();
-			printf("walkToAndPickup actor=%s obj=%s\n", actor.c_str(), obj.c_str());
-			break;
-		}
-		case 0x1A: { // setPickupFrames
-			std::string obj = formatValue();
-			std::string start = formatValue();
-			std::string end = formatValue();
-			printf("setPickupFrames obj=%s start=%s end=%s\n", obj.c_str(), start.c_str(), end.c_str());
-			break;
-		}
-		case 0x1B: { // setAnimSpeed
-			std::string obj = formatValue();
-			std::string slot = formatValue();
-			std::string speed = formatValue();
-			printf("setAnimSpeed obj=%s slot=%s speed=%s\n", obj.c_str(), slot.c_str(), speed.c_str());
-			break;
-		}
-		case 0x1C: { // setSkippable
-			printf("setSkippable\n");
-			break;
-		}
-		case 0x1D: { // clearSkippable
-			printf("clearSkippable\n");
-			break;
-		}
-		case 0x1E: { // loadAnimBlob
-			std::string obj = formatValue();
-			std::string slot = formatValue();
-			std::string frame = formatValue();
-			printf("loadAnimBlob obj=%s slot=%s frame=%s\n", obj.c_str(), slot.c_str(), frame.c_str());
-			break;
-		}
-		case 0x1F: { // setPosition
-			std::string obj = formatValue();
-			std::string x = formatValue();
-			std::string y = formatValue();
-			printf("setPosition obj=%s pos=(%s, %s)\n", obj.c_str(), x.c_str(), y.c_str());
-			break;
-		}
-		case 0x20: { // setVerticalOffset
-			std::string obj = formatValue();
-			std::string offset = formatValue();
-			printf("setVerticalOffset obj=%s offset=%s\n", obj.c_str(), offset.c_str());
-			break;
-		}
-		case 0x21: { // setMotion
-			std::string obj = formatValue();
-			std::string target = formatValue();
-			std::string delta = formatValue();
-			std::string dist = formatValue();
-			printf("setMotion obj=%s target=%s delta=%s dist=%s\n", obj.c_str(), target.c_str(), delta.c_str(), dist.c_str());
-			break;
-		}
-		case 0x22: { // setAnimIndex
-			std::string obj = formatValue();
-			std::string anim = formatValue();
-			printf("setAnimIndex obj=%s anim=%s\n", obj.c_str(), anim.c_str());
-			break;
-		}
-		case 0x23: { // moveToPosition
-			std::string obj = formatValue();
-			std::string x = formatValue();
-			std::string y = formatValue();
-			std::string voff = formatValue();
-			printf("moveToPosition obj=%s pos=(%s, %s) voff=%s\n", obj.c_str(), x.c_str(), y.c_str(), voff.c_str());
-			break;
-		}
-		case 0x24: { // add
-			std::string a = formatValue();
-			std::string b = formatValue();
-			printf("add %s, %s\n", a.c_str(), b.c_str());
-			break;
-		}
-		case 0x25: { // subtract
-			std::string a = formatValue();
-			std::string b = formatValue();
-			printf("subtract %s, %s\n", a.c_str(), b.c_str());
-			break;
-		}
-		case 0x26: { // loadSpecialAnim
-			std::string obj = formatValue();
-			std::string decodeFlag = formatValue(); // decode/enable flag (runtime +0x182)
-			uint8_t animIdx = readByte();
-			printf("loadSpecialAnim obj=%s decode=%s anim=%u\n", obj.c_str(), decodeFlag.c_str(), animIdx);
-			break;
-		}
-		case 0x27: { // setMaxAnimFrame
-			std::string obj = formatValue();
-			std::string maxFrame = formatValue();
-			printf("setMaxAnimFrame obj=%s maxFrame=%s\n", obj.c_str(), maxFrame.c_str());
-			break;
-		}
-		case 0x28: { // nop28
-			printf("nop28\n");
-			break;
-		}
-		case 0x29: { // loadSong
-			std::string obj = formatValue();
-			printf("loadSong obj=%s\n", obj.c_str());
-			break;
-		}
-		case 0x2A: { // loadAnimFromScene
-			std::string obj = formatValue();
-			std::string slot = formatValue();
-			std::string decode = formatValue();
-			uint8_t idx = readByte();
-			printf("loadAnimFromScene obj=%s slot=%s decode=%s idx=%u\n", obj.c_str(), slot.c_str(), decode.c_str(), idx);
-			break;
-		}
-		case 0x2B: { // setShading
-			std::string obj = formatValue();
-			printf("setShading obj=%s\n", obj.c_str());
-			break;
-		}
-		case 0x2C: { // setParent
-			std::string obj = formatValue();
-			std::string val = formatValue();
-			printf("setParent obj=%s val=%s\n", obj.c_str(), val.c_str());
-			break;
-		}
-		case 0x2D: { // setScaling
-			std::string obj = formatValue();
-			std::string val = formatValue();
-			printf("setScaling obj=%s val=%s\n", obj.c_str(), val.c_str());
-			break;
-		}
-		case 0x2E: { // checkAnimRange
-			std::string anim = formatValue();
-			std::string lo = formatValue();
-			std::string hi = formatValue();
-			printf("checkAnimRange anim=%s range=[%s, %s]\n", anim.c_str(), lo.c_str(), hi.c_str());
-			break;
-		}
-		case 0x2F: { // checkBlobRange
-			std::string obj = formatValue();
-			std::string slot = formatValue();
-			std::string lo = formatValue();
-			std::string hi = formatValue();
-			printf("checkBlobRange obj=%s slot=%s range=[%s, %s]\n", obj.c_str(), slot.c_str(), lo.c_str(), hi.c_str());
-			break;
-		}
-		case 0x30: { // nop30
-			printf("nop30\n");
-			break;
-		}
-		case 0x31: { // setVolume
-			std::string vol = formatValue();
-			printf("setVolume %s\n", vol.c_str());
-			break;
-		}
-		case 0x32: { // setClickable
-			std::string obj = formatValue();
-			std::string val = formatValue();
-			printf("setClickable obj=%s val=%s\n", obj.c_str(), val.c_str());
-			break;
-		}
-		case 0x33: { // setVisible
-			std::string obj = formatValue();
-			std::string val = formatValue();
-			printf("setVisible obj=%s val=%s\n", obj.c_str(), val.c_str());
-			break;
-		}
-		case 0x34: { // setHotspotRemap
-			std::string a = formatValue();
-			std::string b = formatValue();
-			printf("setHotspotRemap %s -> %s\n", a.c_str(), b.c_str());
-			break;
-		}
-		case 0x35: { // setBoundsAttach
-			std::string obj = formatValue();
-			std::string parent = formatValue();
-			std::string a = formatValue();
-			std::string b = formatValue();
-			std::string c = formatValue();
-			printf("setBoundsAttach obj=%s parent=%s (%s, %s, %s)\n",
-				   obj.c_str(), parent.c_str(), a.c_str(), b.c_str(), c.c_str());
-			break;
-		}
-		case 0x36: { // dismissAllPanels
-			printf("dismissAllPanels\n");
-			break;
-		}
-		case 0x37: { // resetScript
-			printf("resetScript\n");
-			break;
-		}
-		case 0x38: { // loadOverlayFont
-			uint8_t resIdx = readByte();
-			printf("loadOverlayFont res=%u\n", resIdx);
-			break;
-		}
-		case 0x39: { // endOverlayText
-			printf("endOverlayText\n");
-			break;
-		}
-		case 0x3A: { // addOverlayEntry
-			std::string x = formatValue();
-			std::string y = formatValue();
-			std::string align = formatValue();
-			uint16_t strOffset = readWord();
-			uint16_t entryType = readWord();
-			printf("addOverlayEntry pos=(%s, %s) align=%s str=%u type=%u\n",
-				   x.c_str(), y.c_str(), align.c_str(), strOffset, entryType);
-			break;
-		}
-		case 0x3B: { // clearOverlayEntries
-			printf("clearOverlayEntries\n");
-			break;
-		}
-		case 0x3C: { // fadeToBlack
-			std::string speed = formatValue();
-			printf("fadeToBlack %s\n", speed.c_str());
-			break;
-		}
-		case 0x3D: { // fadeFromBlack
-			std::string speed = formatValue();
-			printf("fadeFromBlack %s\n", speed.c_str());
-			break;
-		}
-		case 0x3E: { // loadSoundRes
-			uint8_t resIdx = readByte();
-			printf("loadSoundRes res=%u\n", resIdx);
-			break;
-		}
-		case 0x3F: { // clearSound
-			printf("clearSound\n");
-			break;
-		}
-		case 0x40: { // playSound
-			printf("playSound\n");
-			break;
-		}
-		case 0x41: { // waitForSound
-			printf("waitForSound\n");
-			break;
-		}
-		case 0x42: { // stopSound
-			printf("stopSound\n");
-			break;
-		}
-		case 0x43: { // loadMusicSlot
-			std::string slot = formatValue();
-			uint8_t resIdx = readByte();
-			printf("loadMusicSlot slot=%s res=%u\n", slot.c_str(), resIdx);
-			break;
-		}
-		case 0x44: { // playMusic
-			std::string slot = formatValue();
-			std::string a = formatValue();
-			std::string b = formatValue();
-			printf("playMusic slot=%s %s %s\n", slot.c_str(), a.c_str(), b.c_str());
-			break;
-		}
-		case 0x45: { // stopMusic
-			std::string slot = formatValue();
-			std::string a = formatValue();
-			std::string b = formatValue();
-			printf("stopMusic slot=%s %s %s\n", slot.c_str(), a.c_str(), b.c_str());
-			break;
-		}
-		case 0x46: { // freeMusic
-			std::string slot = formatValue();
-			printf("freeMusic slot=%s\n", slot.c_str());
-			break;
-		}
-		case 0x47: { // waitForMusic
-			printf("waitForMusic\n");
-			break;
-		}
-		case 0x48: { // getObjectX
-			std::string obj = formatValue();
-			printf("getObjectX obj=%s\n", obj.c_str());
-			break;
-		}
-		case 0x49: { // getObjectY
-			std::string obj = formatValue();
-			printf("getObjectY obj=%s\n", obj.c_str());
-			break;
-		}
-		case 0x4A: { // getObjectField
-			std::string obj = formatValue();
-			printf("getObjectField obj=%s\n", obj.c_str());
-			break;
-		}
-		case 0x4B: { // getObjectOrient
-			std::string obj = formatValue();
-			printf("getObjectOrient obj=%s\n", obj.c_str());
+		if (pos != endPos)
+			pos = endPos;
+	}
+	return lines;
+}
+
+static void disassemble() {
+	auto lines = decompileToLines();
+	for (const auto &l : lines) {
+		for (int i = 0; i < l.indent; i++)
+			printf("  ");
+		printf("%04x: %s%s\n", l.offset, getOpcodeName(l.opcode), l.params.c_str());
+	}
+}
+
+static std::string jsonEscape(const std::string &s) {
+	std::string out;
+	for (char c : s) {
+		switch (c) {
+		case '"':
+			out += "\\\"";
 			break;
-		}
-		case 0x4C: { // clearActorItems
-			printf("clearActorItems\n");
+		case '\\':
+			out += "\\\\";
 			break;
-		}
-		case 0x4D: { // setAreaRemap
-			std::string a = formatValue();
-			std::string b = formatValue();
-			printf("setAreaRemap %s -> %s\n", a.c_str(), b.c_str());
+		case '\n':
+			out += "\\n";
 			break;
-		}
-		case 0x4E: { // waitForAdlib
-			printf("waitForAdlib\n");
+		case '\t':
+			out += "\\t";
 			break;
-		}
-		default: {
-			printf("??? opcode=0x%02x len=%u\n", opcode, length);
+		default:
+			out += c;
 			break;
 		}
-		}
+	}
+	return out;
+}
 
-		if (pos != endPos) {
-			pos = endPos;
-		}
+static void disassembleJson(int sceneIndex) {
+	auto lines = decompileToLines();
+	printf("  {\"scene\": %d, \"size\": %u, \"instructions\": [\n", sceneIndex, scriptSize);
+	for (size_t i = 0; i < lines.size(); i++) {
+		const auto &l = lines[i];
+		std::string text = std::string(getOpcodeName(l.opcode)) + l.params;
+		printf("    {\"offset\": %u, \"opcode\": %u, \"name\": \"%s\", \"indent\": %d, \"text\": \"%s\"}%s\n",
+			   l.offset, l.opcode, jsonEscape(getOpcodeName(l.opcode)).c_str(),
+			   l.indent, jsonEscape(text).c_str(),
+			   (i + 1 < lines.size()) ? "," : "");
 	}
+	printf("  ]}");
 }
 
 static void printHelp(const char *bin) {
 	printf("MACS2 Script Decompiler\n\n");
-	printf("Usage: %s <game_data_file> [scene_index]\n\n", bin);
+	printf("Usage: %s [--json] <game_data_file> [scene_index]\n\n", bin);
+	printf("  --json          - Output as JSON\n");
 	printf("  game_data_file  - The main game data file\n");
 	printf("  scene_index     - Scene number to decompile (1-based)\n");
 	printf("                    If omitted, decompiles all scenes\n\n");
@@ -587,17 +774,24 @@ static bool loadSceneScript(FILE *f, uint16_t sceneIndex) {
 	fseek(f, offset, SEEK_SET);
 
 	uint32_t sceneDataOffset;
-	if (fread(&sceneDataOffset, 4, 1, f) != 1) return false;
+	if (fread(&sceneDataOffset, 4, 1, f) != 1)
+		return false;
+
+	if (sceneDataOffset == 0)
+		return false;
 
 	fseek(f, sceneDataOffset + 0x80, SEEK_SET);
 
 	uint16_t size;
-	if (fread(&size, 2, 1, f) != 1) return false;
+	if (fread(&size, 2, 1, f) != 1)
+		return false;
 
-	if (size == 0) return false;
+	if (size == 0)
+		return false;
 
 	scriptData = (uint8_t *)malloc(size);
-	if (!scriptData) return false;
+	if (!scriptData)
+		return false;
 
 	if (fread(scriptData, 1, size, f) != size) {
 		free(scriptData);
@@ -616,53 +810,71 @@ int main(int argc, char **argv) {
 		return 1;
 	}
 
-	FILE *f = fopen(argv[1], "rb");
+	bool jsonMode = false;
+	int argIdx = 1;
+	if (!strcmp(argv[argIdx], "--json")) {
+		jsonMode = true;
+		argIdx++;
+	}
+
+	if (argIdx >= argc) {
+		printHelp(argv[0]);
+		return 1;
+	}
+
+	FILE *f = fopen(argv[argIdx], "rb");
 	if (!f) {
-		fprintf(stderr, "Error: Cannot open file '%s'\n", argv[1]);
+		fprintf(stderr, "Error: Cannot open file '%s'\n", argv[argIdx]);
 		return 1;
 	}
+	argIdx++;
 
 	int startScene = -1;
 	int endScene = -1;
 
-	if (argc >= 3) {
-		startScene = atoi(argv[2]);
+	if (argIdx < argc) {
+		startScene = atoi(argv[argIdx]);
 		endScene = startScene;
 	} else {
 		startScene = 1;
 		endScene = 512;
 	}
 
-	printf("; MACS2 Script Runtime Variables (type 0xFF, read-only)\n");
-	printf("; These are computed by the engine at read-time, not stored in script data.\n");
-	printf("; interactedUse    (FF:01) Object ID interacted with via Use/UseInventory cursor\n");
-	printf("; interactedLook   (FF:02) Object ID interacted with via Look cursor\n");
-	printf("; interactedTalk   (FF:03) Object ID interacted with via Talk cursor\n");
-	printf("; areaAtActor      (FF:04) Area ID at protagonist's current position\n");
-	printf("; isRepeatRun      (FF:0B) 1 during repeat/object script pass, 0 otherwise\n");
-	printf("; dialogueResult   (FF:0D) Index of last chosen dialogue option\n");
-	printf("; pathWalkable     (FF:23) 1 if last setPosition path test succeeded\n");
-	printf("; actorX           (FF:24) Protagonist X position\n");
-	printf("; actorY           (FF:25) Protagonist Y position\n");
-	printf("; isSceneInit      (FF:26) 1 during scene initialization pass\n");
-	printf("; invCheck         (FF:28) 1 if last setParent inventory check matched\n");
-	printf("; invCombine       (FF:2A) 1 if inventory combine pending (no UI open)\n");
-	printf("; invAction        (FF:2B) 1 if inventory action pending (no UI open)\n");
-	printf("; curScene         (FF:2D) Current scene index\n");
-	printf("; prevScene        (FF:2F) Previous scene index\n");
-	printf(";\n");
-	printf("; Script variables: var[N] (read/write, all zeroed on scene load)\n");
-	printf("; Object IDs in moveObject/etc.: raw value - 0x400 = object index\n");
-	printf(";\n\n");
-
-	for (int scene = startScene; scene <= endScene; scene++) {
-		if (loadSceneScript(f, (uint16_t)scene)) {
-			printf("=== Scene %d (size: %u bytes) ===\n", scene, scriptSize);
-			disassemble();
-			printf("\n");
-			free(scriptData);
-			scriptData = nullptr;
-			scriptSize = 0;
+	if (jsonMode) {
+		printf("{\"scenes\": [\n");
+		bool first = true;
+		for (int scene = startScene; scene <= endScene; scene++) {
+			if (loadSceneScript(f, (uint16_t)scene)) {
+				if (!first)
+					printf(",\n");
+				first = false;
+				disassembleJson(scene);
+				free(scriptData);
+				scriptData = nullptr;
+				scriptSize = 0;
+			}
+		}
+		printf("\n]}\n");
+	} else {
+		printf("; MACS2 Script Runtime Variables (type 0xFF, read-only)\n");
+		printf("; These are computed by the engine at read-time, not stored in script data.\n");
+		for (const auto &s : kSpecialNames) {
+			printf("; %-18s (FF:%02x)\n", s.name, s.id);
+		}
+		printf(";\n");
+		printf("; Script variables: var[N] (read/write, all zeroed on scene load)\n");
+		printf("; Object IDs in moveObject/etc.: raw value - 0x400 = object index\n");
+		printf(";\n\n");
+
+		for (int scene = startScene; scene <= endScene; scene++) {
+			if (loadSceneScript(f, (uint16_t)scene)) {
+				printf("=== Scene %d (size: %u bytes) ===\n", scene, scriptSize);
+				disassemble();
+				printf("\n");
+				free(scriptData);
+				scriptData = nullptr;
+				scriptSize = 0;
+			}
 		}
 	}
 


Commit: fadaad3fbf935207e9f37a2865ac07a00f0e43bf
    https://github.com/scummvm/scummvm-tools/commit/fadaad3fbf935207e9f37a2865ac07a00f0e43bf
Author: Martin Gerhardy (martin.gerhardy at gmail.com)
Date: 2026-05-31T18:08:08+02:00

Commit Message:
MASC2: started to export json scene data, too

Changed paths:
    engines/macs2/extract_macs2.cpp


diff --git a/engines/macs2/extract_macs2.cpp b/engines/macs2/extract_macs2.cpp
index 5a6c3f0d..dd10941e 100644
--- a/engines/macs2/extract_macs2.cpp
+++ b/engines/macs2/extract_macs2.cpp
@@ -21,8 +21,8 @@
 
 /* MACS2 Resource Extractor - extracts images, sounds, music, and strings */
 
-#include <stdio.h>
 #include <stdint.h>
+#include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <string>
@@ -68,7 +68,8 @@ static bool decodeRLEImage(uint32_t offset, uint8_t *pixels) {
 	fseek(resFile, offset, SEEK_SET);
 	for (int y = 0; y < 200; y++) {
 		uint16_t length = readU16(resFile);
-		if (length == 0 || length > 960) return false;
+		if (length == 0 || length > 960)
+			return false;
 		std::vector<uint8_t> rowData(length);
 		fread(rowData.data(), 1, length, resFile);
 		int remaining = 320;
@@ -81,7 +82,8 @@ static bool decodeRLEImage(uint32_t offset, uint8_t *pixels) {
 				pixels[y * 320 + x++] = val;
 				remaining--;
 			} else {
-				if (ptr + 2 > end) break;
+				if (ptr + 2 > end)
+					break;
 				uint8_t runLen = *ptr++;
 				uint8_t runVal = *ptr++;
 				for (int i = 0; i < runLen && remaining > 0; i++) {
@@ -97,7 +99,10 @@ static bool decodeRLEImage(uint32_t offset, uint8_t *pixels) {
 // Write a BMP file (8-bit indexed)
 static void writeBMP(const char *path, const uint8_t *pixels, const uint8_t *palette) {
 	FILE *f = fopen(path, "wb");
-	if (!f) { fprintf(stderr, "Cannot write %s\n", path); return; }
+	if (!f) {
+		fprintf(stderr, "Cannot write %s\n", path);
+		return;
+	}
 
 	// BMP header
 	uint32_t imageSize = 320 * 200;
@@ -106,21 +111,27 @@ static void writeBMP(const char *path, const uint8_t *pixels, const uint8_t *pal
 	uint32_t fileSize = dataOffset + imageSize;
 
 	// File header
-	fputc('B', f); fputc('M', f);
+	fputc('B', f);
+	fputc('M', f);
 	uint8_t hdr[12] = {};
-	hdr[0] = fileSize & 0xFF; hdr[1] = (fileSize >> 8) & 0xFF;
-	hdr[2] = (fileSize >> 16) & 0xFF; hdr[3] = (fileSize >> 24) & 0xFF;
-	hdr[8] = dataOffset & 0xFF; hdr[9] = (dataOffset >> 8) & 0xFF;
-	hdr[10] = (dataOffset >> 16) & 0xFF; hdr[11] = (dataOffset >> 24) & 0xFF;
+	hdr[0] = fileSize & 0xFF;
+	hdr[1] = (fileSize >> 8) & 0xFF;
+	hdr[2] = (fileSize >> 16) & 0xFF;
+	hdr[3] = (fileSize >> 24) & 0xFF;
+	hdr[8] = dataOffset & 0xFF;
+	hdr[9] = (dataOffset >> 8) & 0xFF;
+	hdr[10] = (dataOffset >> 16) & 0xFF;
+	hdr[11] = (dataOffset >> 24) & 0xFF;
 	fwrite(hdr, 1, 12, f);
 
 	// DIB header (BITMAPINFOHEADER)
 	uint8_t dib[40] = {};
 	dib[0] = 40; // header size
-	dib[4] = 0x40; dib[5] = 0x01; // width = 320
+	dib[4] = 0x40;
+	dib[5] = 0x01; // width = 320
 	dib[8] = 0xC8; // height = 200 (positive = bottom-up)
-	dib[12] = 1; // planes
-	dib[14] = 8; // bpp
+	dib[12] = 1;   // planes
+	dib[14] = 8;   // bpp
 	fwrite(dib, 1, 40, f);
 
 	// Palette (BGRA)
@@ -141,13 +152,18 @@ static void writeBMP(const char *path, const uint8_t *pixels, const uint8_t *pal
 	printf("  Wrote %s\n", path);
 }
 
+// Forward declaration
+static bool decodeRLEMap(FILE *f, uint8_t *pixels);
+
 // Extract background image for a scene
 static void extractImage(uint16_t sceneIndex, const char *outDir) {
 	uint32_t bgOffset = getSceneBgOffset(sceneIndex);
-	if (bgOffset == 0) return;
+	if (bgOffset == 0)
+		return;
 
 	uint8_t pixels[320 * 200];
-	if (!decodeRLEImage(bgOffset, pixels)) return;
+	if (!decodeRLEImage(bgOffset, pixels))
+		return;
 
 	// Palette follows immediately after the image
 	uint8_t palette[768];
@@ -156,12 +172,26 @@ static void extractImage(uint16_t sceneIndex, const char *outDir) {
 	char path[512];
 	snprintf(path, sizeof(path), "%s/scene_%03d.bmp", outDir, sceneIndex);
 	writeBMP(path, pixels, palette);
+
+	// Export the 4 maps as BMP using the scene's own palette (same as imgui debugger)
+	fseek(resFile, 256, SEEK_CUR); // shading table
+	fseek(resFile, 3, SEEK_CUR);   // 3 unknown bytes
+
+	const char *mapNames[] = {"depth", "pathfinding", "unknown", "hotspot"};
+	for (int m = 0; m < 4; m++) {
+		uint8_t mapPixels[320 * 200];
+		if (!decodeRLEMap(resFile, mapPixels))
+			break;
+		snprintf(path, sizeof(path), "%s/scene_%03d_%s.bmp", outDir, sceneIndex, mapNames[m]);
+		writeBMP(path, mapPixels, palette);
+	}
 }
 
 // Extract indexed resources (sounds/music) for a scene
 static void extractResources(uint16_t sceneIndex, const char *outDir, const char *prefix) {
 	uint32_t dataOffset = getSceneDataOffset(sceneIndex);
-	if (dataOffset == 0) return;
+	if (dataOffset == 0)
+		return;
 
 	fseek(resFile, dataOffset, SEEK_SET);
 	uint32_t resourceTable[32]; // 0x80 / 4 = 32 entries
@@ -170,10 +200,12 @@ static void extractResources(uint16_t sceneIndex, const char *outDir, const char
 	}
 
 	for (int i = 0; i < 32; i++) {
-		if (resourceTable[i] == 0) continue;
+		if (resourceTable[i] == 0)
+			continue;
 		fseek(resFile, resourceTable[i], SEEK_SET);
 		uint32_t size = readU32(resFile);
-		if (size == 0 || size > 0x100000) continue;
+		if (size == 0 || size > 0x100000)
+			continue;
 
 		std::vector<uint8_t> data(size);
 		fread(data.data(), 1, size, resFile);
@@ -204,11 +236,13 @@ static std::string decryptString(const uint8_t *data, uint16_t length) {
 // Extract strings for a scene
 static void extractStrings(uint16_t sceneIndex, const char *outDir) {
 	uint32_t strOffset = getSceneStringsOffset(sceneIndex);
-	if (strOffset == 0) return;
+	if (strOffset == 0)
+		return;
 
 	fseek(resFile, strOffset, SEEK_SET);
 	uint16_t totalSize = readU16(resFile);
-	if (totalSize == 0) return;
+	if (totalSize == 0)
+		return;
 
 	std::vector<uint8_t> strData(totalSize);
 	fread(strData.data(), 1, totalSize, resFile);
@@ -216,7 +250,8 @@ static void extractStrings(uint16_t sceneIndex, const char *outDir) {
 	char path[512];
 	snprintf(path, sizeof(path), "%s/strings_scene%03d.txt", outDir, sceneIndex);
 	FILE *out = fopen(path, "w");
-	if (!out) return;
+	if (!out)
+		return;
 
 	fprintf(out, "; Scene %d strings\n", sceneIndex);
 	fprintf(out, "; Total data size: %u bytes\n\n", totalSize);
@@ -227,7 +262,8 @@ static void extractStrings(uint16_t sceneIndex, const char *outDir) {
 	while (pos + 2 <= totalSize) {
 		uint16_t len = strData[pos] | (strData[pos + 1] << 8);
 		pos += 2;
-		if (len == 0 || pos + len > totalSize) break;
+		if (len == 0 || pos + len > totalSize)
+			break;
 		std::string decoded = decryptString(strData.data() + pos, len);
 		fprintf(out, "[%d] (offset=%u) %s\n", stringIndex, (unsigned)(pos - 2), decoded.c_str());
 		pos += len;
@@ -246,14 +282,238 @@ static void mkdirp(const char *path) {
 #endif
 }
 
+// Skip an RLE-compressed 320x200 image in the file (advance past it without decoding)
+static void skipRLEImage(FILE *f) {
+	for (int y = 0; y < 200; y++) {
+		uint16_t length = readU16(f);
+		fseek(f, length, SEEK_CUR);
+	}
+}
+
+// Decode an RLE 320x200 image into a buffer
+static bool decodeRLEMap(FILE *f, uint8_t *pixels) {
+	for (int y = 0; y < 200; y++) {
+		uint16_t length = readU16(f);
+		if (length == 0 || length > 960)
+			return false;
+		std::vector<uint8_t> rowData(length);
+		fread(rowData.data(), 1, length, f);
+		int remaining = 320;
+		int x = 0;
+		uint8_t *ptr = rowData.data();
+		uint8_t *end = ptr + length;
+		while (remaining > 0 && ptr < end) {
+			uint8_t val = *ptr++;
+			if (val != 0xF0) {
+				pixels[y * 320 + x++] = val;
+				remaining--;
+			} else {
+				if (ptr + 2 > end)
+					break;
+				uint8_t runLen = *ptr++;
+				uint8_t runVal = *ptr++;
+				for (int i = 0; i < runLen && remaining > 0; i++) {
+					pixels[y * 320 + x++] = runVal;
+					remaining--;
+				}
+			}
+		}
+	}
+	return true;
+}
+
+static void writeRawFile(const char *path, const uint8_t *data, size_t size) {
+	FILE *f = fopen(path, "wb");
+	if (f) {
+		fwrite(data, 1, size, f);
+		fclose(f);
+	}
+}
+
+// Extract comprehensive scene data as JSON + binary map files
+static void extractSceneData(uint16_t sceneIndex, const char *outDir) {
+	uint32_t bgOffset = getSceneBgOffset(sceneIndex);
+	if (bgOffset == 0)
+		return;
+
+	uint32_t dataOffset = getSceneDataOffset(sceneIndex);
+	uint32_t stringsOffset = getSceneStringsOffset(sceneIndex);
+
+	fseek(resFile, bgOffset, SEEK_SET);
+	skipRLEImage(resFile); // background image
+
+	// Palette (768 bytes, 6-bit VGA)
+	uint8_t palette[768];
+	fread(palette, 1, 768, resFile);
+
+	// Shading table (256 bytes)
+	uint8_t shadingTable[256];
+	fread(shadingTable, 1, 256, resFile);
+
+	// 3 unknown bytes
+	uint8_t unknownBytes[3];
+	fread(unknownBytes, 1, 3, resFile);
+
+	// 4 RLE maps (each 320x200)
+	uint8_t depthMap[320 * 200];
+	uint8_t pathfindingMap[320 * 200];
+	uint8_t unknownMap[320 * 200];
+	uint8_t hotspotMap[320 * 200];
+	decodeRLEMap(resFile, depthMap);
+	decodeRLEMap(resFile, pathfindingMap);
+	decodeRLEMap(resFile, unknownMap);
+	decodeRLEMap(resFile, hotspotMap);
+
+	// Write map binary files
+	char path[512];
+	snprintf(path, sizeof(path), "%s/scene%03d_depth.bin", outDir, sceneIndex);
+	writeRawFile(path, depthMap, sizeof(depthMap));
+	snprintf(path, sizeof(path), "%s/scene%03d_pathfinding.bin", outDir, sceneIndex);
+	writeRawFile(path, pathfindingMap, sizeof(pathfindingMap));
+	snprintf(path, sizeof(path), "%s/scene%03d_unknown.bin", outDir, sceneIndex);
+	writeRawFile(path, unknownMap, sizeof(unknownMap));
+	snprintf(path, sizeof(path), "%s/scene%03d_hotspot.bin", outDir, sceneIndex);
+	writeRawFile(path, hotspotMap, sizeof(hotspotMap));
+
+	// Pathfinding nodes: 16 entries ?? 10 bytes
+	struct PathNode {
+		uint16_t x, y;
+		uint8_t adj[4];
+		uint16_t numConnections;
+	} nodes[16];
+	for (int i = 0; i < 16; i++) {
+		nodes[i].x = readU16(resFile);
+		nodes[i].y = readU16(resFile);
+		fread(nodes[i].adj, 1, 4, resFile);
+		nodes[i].numConnections = readU16(resFile);
+	}
+
+	uint16_t numHotspots = readU16(resFile);
+
+	// Hotspot color table: 16 uint16 entries (maps pixel value to object ID)
+	uint16_t hotspotColors[16];
+	for (int i = 0; i < 16; i++)
+		hotspotColors[i] = readU16(resFile);
+
+	// Resource offsets from data block (32 dwords = special anim file offsets)
+	uint32_t resourceOffsets[32] = {};
+	uint32_t mapImageOffset = 0;
+	if (dataOffset != 0) {
+		fseek(resFile, dataOffset, SEEK_SET);
+		for (int i = 0; i < 32; i++)
+			resourceOffsets[i] = readU32(resFile);
+		// Map image offset at data block +0x3C0
+		fseek(resFile, dataOffset + 0x3C0, SEEK_SET);
+		mapImageOffset = readU32(resFile);
+	}
+
+	// Script size
+	uint16_t scriptSize = 0;
+	if (dataOffset != 0) {
+		fseek(resFile, dataOffset + 0x80, SEEK_SET);
+		scriptSize = readU16(resFile);
+	}
+
+	// Strings size
+	uint16_t stringsSize = 0;
+	if (stringsOffset != 0) {
+		fseek(resFile, stringsOffset, SEEK_SET);
+		stringsSize = readU16(resFile);
+	}
+
+	// Write JSON
+	snprintf(path, sizeof(path), "%s/scenedata_%03d.json", outDir, sceneIndex);
+	FILE *out = fopen(path, "w");
+	if (!out)
+		return;
+
+	fprintf(out, "{\n");
+	fprintf(out, "  \"scene\": %d,\n", sceneIndex);
+	fprintf(out, "  \"offsets\": {\n");
+	fprintf(out, "    \"background\": %u,\n", bgOffset);
+	fprintf(out, "    \"data\": %u,\n", dataOffset);
+	fprintf(out, "    \"strings\": %u,\n", stringsOffset);
+	fprintf(out, "    \"mapImage\": %u\n", mapImageOffset);
+	fprintf(out, "  },\n");
+	fprintf(out, "  \"scriptSize\": %u,\n", scriptSize);
+	fprintf(out, "  \"stringsSize\": %u,\n", stringsSize);
+
+	// Palette
+	fprintf(out, "  \"palette\": [");
+	for (int i = 0; i < 256; i++) {
+		if (i > 0)
+			fprintf(out, ",");
+		if (i % 8 == 0)
+			fprintf(out, "\n    ");
+		fprintf(out, "[%u,%u,%u]", palette[i * 3], palette[i * 3 + 1], palette[i * 3 + 2]);
+	}
+	fprintf(out, "\n  ],\n");
+
+	// Shading table
+	fprintf(out, "  \"shadingTable\": [");
+	for (int i = 0; i < 256; i++) {
+		if (i > 0)
+			fprintf(out, ",");
+		if (i % 32 == 0)
+			fprintf(out, "\n    ");
+		fprintf(out, "%u", shadingTable[i]);
+	}
+	fprintf(out, "\n  ],\n");
+
+	fprintf(out, "  \"unknownBytes\": [%u, %u, %u],\n", unknownBytes[0], unknownBytes[1], unknownBytes[2]);
+
+	// Pathfinding nodes
+	fprintf(out, "  \"pathfindingNodes\": [\n");
+	for (int i = 0; i < 16; i++) {
+		fprintf(out, "    {\"index\": %d, \"x\": %u, \"y\": %u, \"adjacent\": [", i, nodes[i].x, nodes[i].y);
+		for (uint16_t j = 0; j < nodes[i].numConnections && j < 4; j++) {
+			if (j > 0)
+				fprintf(out, ", ");
+			fprintf(out, "%u", nodes[i].adj[j]);
+		}
+		fprintf(out, "]}%s\n", i < 15 ? "," : "");
+	}
+	fprintf(out, "  ],\n");
+
+	fprintf(out, "  \"numHotspots\": %u,\n", numHotspots);
+	fprintf(out, "  \"hotspotColorTable\": [");
+	for (int i = 0; i < 16; i++) {
+		if (i > 0)
+			fprintf(out, ", ");
+		fprintf(out, "%u", hotspotColors[i]);
+	}
+	fprintf(out, "],\n");
+
+	// Resource offsets (special animation file offsets)
+	fprintf(out, "  \"resourceOffsets\": [");
+	for (int i = 0; i < 32; i++) {
+		if (i > 0)
+			fprintf(out, ", ");
+		fprintf(out, "%u", resourceOffsets[i]);
+	}
+	fprintf(out, "],\n");
+
+	// Map file references
+	fprintf(out, "  \"maps\": {\n");
+	fprintf(out, "    \"depth\": \"scene%03d_depth.bin\",\n", sceneIndex);
+	fprintf(out, "    \"pathfinding\": \"scene%03d_pathfinding.bin\",\n", sceneIndex);
+	fprintf(out, "    \"unknown\": \"scene%03d_unknown.bin\",\n", sceneIndex);
+	fprintf(out, "    \"hotspot\": \"scene%03d_hotspot.bin\"\n", sceneIndex);
+	fprintf(out, "  }\n");
+	fprintf(out, "}\n");
+	fclose(out);
+	printf("  Wrote %s + 4 map files\n", path);
+}
+
 static void printHelp(const char *bin) {
 	printf("MACS2 Resource Extractor\n\n");
 	printf("Usage: %s <mode> <game_data_file> <output_dir> [scene_index]\n\n", bin);
 	printf("Modes:\n");
-	printf("  images   - Extract background images as BMP files\n");
-	printf("  sounds   - Extract sound/music resource blobs\n");
-	printf("  strings  - Extract and decrypt text strings\n");
-	printf("  all      - Extract everything\n");
+	printf("  images    - Extract background images as BMP files\n");
+	printf("  sounds    - Extract sound/music resource blobs\n");
+	printf("  strings   - Extract and decrypt text strings\n");
+	printf("  scenedata - Extract scene metadata as JSON (pathfinding, hotspots, walk params)\n");
+	printf("  all       - Extract everything\n");
 	printf("\n");
 	printf("If scene_index is omitted, extracts from all scenes.\n");
 }
@@ -284,6 +544,7 @@ int main(int argc, char **argv) {
 	bool doImages = !strcmp(mode, "images") || !strcmp(mode, "all");
 	bool doSounds = !strcmp(mode, "sounds") || !strcmp(mode, "all");
 	bool doStrings = !strcmp(mode, "strings") || !strcmp(mode, "all");
+	bool doSceneData = !strcmp(mode, "scenedata") || !strcmp(mode, "all");
 
 	for (int scene = startScene; scene <= endScene; scene++) {
 		bool hasData = false;
@@ -291,7 +552,10 @@ int main(int argc, char **argv) {
 		if (doImages) {
 			uint32_t bgOff = getSceneBgOffset(scene);
 			if (bgOff != 0) {
-				if (!hasData) { printf("Scene %d:\n", scene); hasData = true; }
+				if (!hasData) {
+					printf("Scene %d:\n", scene);
+					hasData = true;
+				}
 				extractImage(scene, outDir);
 			}
 		}
@@ -299,7 +563,10 @@ int main(int argc, char **argv) {
 		if (doSounds) {
 			uint32_t dataOff = getSceneDataOffset(scene);
 			if (dataOff != 0) {
-				if (!hasData) { printf("Scene %d:\n", scene); hasData = true; }
+				if (!hasData) {
+					printf("Scene %d:\n", scene);
+					hasData = true;
+				}
 				extractResources(scene, outDir, "res");
 			}
 		}
@@ -307,10 +574,24 @@ int main(int argc, char **argv) {
 		if (doStrings) {
 			uint32_t strOff = getSceneStringsOffset(scene);
 			if (strOff != 0) {
-				if (!hasData) { printf("Scene %d:\n", scene); hasData = true; }
+				if (!hasData) {
+					printf("Scene %d:\n", scene);
+					hasData = true;
+				}
 				extractStrings(scene, outDir);
 			}
 		}
+
+		if (doSceneData) {
+			uint32_t bgOff = getSceneBgOffset(scene);
+			if (bgOff != 0) {
+				if (!hasData) {
+					printf("Scene %d:\n", scene);
+					hasData = true;
+				}
+				extractSceneData(scene, outDir);
+			}
+		}
 	}
 
 	fclose(resFile);


Commit: d06a5562375ce5d27ad6846df1310c566a215856
    https://github.com/scummvm/scummvm-tools/commit/d06a5562375ce5d27ad6846df1310c566a215856
Author: Martin Gerhardy (martin.gerhardy at gmail.com)
Date: 2026-06-03T18:30:41+02:00

Commit Message:
MACS2: this is a shadowmap

Changed paths:
    engines/macs2/extract_macs2.cpp


diff --git a/engines/macs2/extract_macs2.cpp b/engines/macs2/extract_macs2.cpp
index dd10941e..9a9b4a1b 100644
--- a/engines/macs2/extract_macs2.cpp
+++ b/engines/macs2/extract_macs2.cpp
@@ -177,7 +177,7 @@ static void extractImage(uint16_t sceneIndex, const char *outDir) {
 	fseek(resFile, 256, SEEK_CUR); // shading table
 	fseek(resFile, 3, SEEK_CUR);   // 3 unknown bytes
 
-	const char *mapNames[] = {"depth", "pathfinding", "unknown", "hotspot"};
+	const char *mapNames[] = {"depth", "pathfinding", "shadow", "hotspot"};
 	for (int m = 0; m < 4; m++) {
 		uint8_t mapPixels[320 * 200];
 		if (!decodeRLEMap(resFile, mapPixels))
@@ -357,11 +357,11 @@ static void extractSceneData(uint16_t sceneIndex, const char *outDir) {
 	// 4 RLE maps (each 320x200)
 	uint8_t depthMap[320 * 200];
 	uint8_t pathfindingMap[320 * 200];
-	uint8_t unknownMap[320 * 200];
+	uint8_t shadowMap[320 * 200];
 	uint8_t hotspotMap[320 * 200];
 	decodeRLEMap(resFile, depthMap);
 	decodeRLEMap(resFile, pathfindingMap);
-	decodeRLEMap(resFile, unknownMap);
+	decodeRLEMap(resFile, shadowMap);
 	decodeRLEMap(resFile, hotspotMap);
 
 	// Write map binary files
@@ -370,8 +370,8 @@ static void extractSceneData(uint16_t sceneIndex, const char *outDir) {
 	writeRawFile(path, depthMap, sizeof(depthMap));
 	snprintf(path, sizeof(path), "%s/scene%03d_pathfinding.bin", outDir, sceneIndex);
 	writeRawFile(path, pathfindingMap, sizeof(pathfindingMap));
-	snprintf(path, sizeof(path), "%s/scene%03d_unknown.bin", outDir, sceneIndex);
-	writeRawFile(path, unknownMap, sizeof(unknownMap));
+	snprintf(path, sizeof(path), "%s/scene%03d_shadow.bin", outDir, sceneIndex);
+	writeRawFile(path, shadowMap, sizeof(shadowMap));
 	snprintf(path, sizeof(path), "%s/scene%03d_hotspot.bin", outDir, sceneIndex);
 	writeRawFile(path, hotspotMap, sizeof(hotspotMap));
 
@@ -497,7 +497,7 @@ static void extractSceneData(uint16_t sceneIndex, const char *outDir) {
 	fprintf(out, "  \"maps\": {\n");
 	fprintf(out, "    \"depth\": \"scene%03d_depth.bin\",\n", sceneIndex);
 	fprintf(out, "    \"pathfinding\": \"scene%03d_pathfinding.bin\",\n", sceneIndex);
-	fprintf(out, "    \"unknown\": \"scene%03d_unknown.bin\",\n", sceneIndex);
+	fprintf(out, "    \"shadow\": \"scene%03d_shadow.bin\",\n", sceneIndex);
 	fprintf(out, "    \"hotspot\": \"scene%03d_hotspot.bin\"\n", sceneIndex);
 	fprintf(out, "  }\n");
 	fprintf(out, "}\n");



From noreply at scummvm.org  Wed Jun  3 16:38:13 2026
From: noreply at scummvm.org (mgerhardy)
Date: Wed,  3 Jun 2026 16:38:13 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm-tools master ->
 3c30c78a80119b5c389902e7be9e386e3ea481bd
Message-ID: <20260603163813.8AFDA7005BC@ron.scummvm.net>

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

Summary:
3c30c78a80 MACS2: looks like the header inclusion fix got lost...


Commit: 3c30c78a80119b5c389902e7be9e386e3ea481bd
    https://github.com/scummvm/scummvm-tools/commit/3c30c78a80119b5c389902e7be9e386e3ea481bd
Author: Martin Gerhardy (martin.gerhardy at gmail.com)
Date: 2026-06-03T18:38:06+02:00

Commit Message:
MACS2: looks like the header inclusion fix got lost...

Changed paths:
    engines/macs2/demacs2.cpp


diff --git a/engines/macs2/demacs2.cpp b/engines/macs2/demacs2.cpp
index 8898d093..4fd9a0db 100644
--- a/engines/macs2/demacs2.cpp
+++ b/engines/macs2/demacs2.cpp
@@ -21,10 +21,10 @@
 
 /* MACS2 Script decompiler */
 
-#include <cstdint>
-#include <cstdio>
-#include <cstdlib>
-#include <cstring>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
 #include <string>
 #include <vector>
 



From noreply at scummvm.org  Wed Jun  3 17:38:05 2026
From: noreply at scummvm.org (neuromancer)
Date: Wed,  3 Jun 2026 17:38:05 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 e0985e843eeb5e4e9cb075300c5f71036fc754f8
Message-ID: <20260603173805.57FC97005BC@ron.scummvm.net>

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:
4de0e53e91 SCUMM: RA2: improve rendering of level 12
d2be31c84a SCUMM: RA2: make level 12 progress until the end
5905f3934c SCUMM: RA2: proper shooting in level 12
3f6b1ed8e4 SCUMM: RA2: render overlay in level 12
e0985e843e SCUMM: RA2: removed some useless statics


Commit: 4de0e53e915325440cccd4fe3179ab313c3b3e78
    https://github.com/scummvm/scummvm/commit/4de0e53e915325440cccd4fe3179ab313c3b3e78
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-03T16:59:37+02:00

Commit Message:
SCUMM: RA2: improve rendering of level 12

Changed paths:
    engines/scumm/insane/rebel2/iact.cpp
    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/iact.cpp b/engines/scumm/insane/rebel2/iact.cpp
index c7d1bf5ca9f..906160a537e 100644
--- a/engines/scumm/insane/rebel2/iact.cpp
+++ b/engines/scumm/insane/rebel2/iact.cpp
@@ -86,7 +86,7 @@ void InsaneRebel2::procPreRendering(byte *renderBitmap) {
 	// This is called at the start of handleFrame(), before any FOBJ chunks are processed.
 	//
 	// IMPORTANT: Only restore when the render buffer pitch matches the background pitch (320).
-	// Levels like Level 12 (Sewers) use oversized buffers (640x260) where FOBJ/FETCH handles
+	// Levels like Level 12 (Sewers) use oversized buffers (424x260) where FOBJ/FETCH handles
 	// background restoration. Copying the 320-wide background into a 640-wide buffer with
 	// hardcoded pitch=320 would corrupt the corridor rendering.
 	if (_rebelHandler == 8 && _level2BackgroundLoaded && _level2Background && renderBitmap) {
diff --git a/engines/scumm/insane/rebel2/render.cpp b/engines/scumm/insane/rebel2/render.cpp
index db4780badeb..afabee2e76a 100644
--- a/engines/scumm/insane/rebel2/render.cpp
+++ b/engines/scumm/insane/rebel2/render.cpp
@@ -3340,7 +3340,7 @@ void InsaneRebel2::renderHandler25ShipPre(byte *renderBitmap, int pitch, int wid
 		return;
 
 	// CRITICAL: Clip height to 180 (0xb4) + viewport Y to avoid drawing over status bar.
-	// For oversized buffers (e.g., Level 12's 640x260), the status bar is at
+	// For oversized buffers (e.g., Level 12's 424x260), the status bar is at
 	// Y = 180 + _viewY in buffer coordinates.
 	int renderHeight = MIN(height, 180 + _viewY);
 
@@ -3378,7 +3378,7 @@ void InsaneRebel2::renderHandler25ShipPre(byte *renderBitmap, int pitch, int wid
 
 		// Add viewport offset so sprite follows the visible area.
 		// For 320x200 buffers (Level 2), _viewX/_viewY are 0 ??? no change.
-		// For oversized buffers (Level 12's 640x260), the viewport scrolls
+		// For oversized buffers (Level 12's 424x260), the viewport scrolls
 		// and sprites must be drawn at the correct position within it.
 		int drawX = _rebelViewOffset2X + spriteXOffset + _viewX;
 		int drawY = _rebelViewOffset2Y + spriteYOffset + _viewY;
diff --git a/engines/scumm/smush/rebel/smush_player_ra2.cpp b/engines/scumm/smush/rebel/smush_player_ra2.cpp
index 64f483df637..7389e22422c 100644
--- a/engines/scumm/smush/rebel/smush_player_ra2.cpp
+++ b/engines/scumm/smush/rebel/smush_player_ra2.cpp
@@ -41,6 +41,10 @@
 
 namespace Scumm {
 
+bool isRebel2FullFrameDeltaCodec(int codec) {
+	return codec == SMUSH_CODEC_DELTA_BLOCKS || codec == SMUSH_CODEC_DELTA_GLYPHS;
+}
+
 // ---------------------------------------------------------------------------
 // SmushPlayerRebel2 ??? construction / destruction
 // ---------------------------------------------------------------------------
@@ -687,17 +691,16 @@ void SmushPlayerRebel2::ra2PrepareFrameObjectSurface(int left, int top, int widt
 		_ra2FrameObjectSurfaceHeight = (int)bottom;
 }
 
-bool SmushPlayerRebel2::ra2SelectFrameBuffer(int width, int height) {
+bool SmushPlayerRebel2::ra2SelectFrameBuffer(int codec, int width, int height) {
 	// Rebel2 uses a special buffer for all non-matching frames.
-	// Oversized FOBJ chunks are clipped against the original 424x260 surface.
-	// Level 1: First frame is 424x260 (background), small sprites reuse same buffer
-	// Level 2: Uses virtual screen directly (handled below when _specialBuffer stays null)
+	// Oversized full-frame delta codecs decode into a tightly packed surface
+	// with their own width as pitch. Other codecs honor left/top and pitch.
 	const int screenSize = _vm->_screenWidth * _vm->_screenHeight;
 	const int64 fobjSize64 = (int64)width * height;
 	int surfaceWidth = width;
 	int surfaceHeight = height;
 
-	if (fobjSize64 > screenSize) {
+	if (fobjSize64 > screenSize && !isRebel2FullFrameDeltaCodec(codec)) {
 		surfaceWidth = MAX(surfaceWidth, _ra2FrameObjectSurfaceWidth);
 		surfaceHeight = MAX(surfaceHeight, _ra2FrameObjectSurfaceHeight);
 	}
@@ -721,13 +724,13 @@ bool SmushPlayerRebel2::ra2SelectFrameBuffer(int width, int height) {
 			free(_specialBuffer);
 			_specialBuffer = newSpecialBuffer;
 			_specialBufferSize = bufSize;
-			_width = surfaceWidth;
-			_height = surfaceHeight;
 			// Zero-fill the new buffer to avoid garbage in areas not written by FOBJ codec
 			memset(_specialBuffer, 0, bufSize);
 			debugC(DEBUG_SMUSH, "SmushPlayerRebel2::ra2SelectFrameBuffer: Allocated new _specialBuffer %dx%d for FOBJ %dx%d (%d bytes)",
 				surfaceWidth, surfaceHeight, width, height, bufSize);
 		}
+		_width = surfaceWidth;
+		_height = surfaceHeight;
 	}
 
 	if (bufSize > screenSize &&
@@ -859,7 +862,7 @@ void SmushPlayerRebel2::handleGameParseNextFrame() {
 
 bool SmushPlayerRebel2::handleGameFrameBufferSelect(int codec, int width, int height) {
 	if ((height != _vm->_screenHeight) || (width != _vm->_screenWidth)) {
-		return ra2SelectFrameBuffer(width, height);
+		return ra2SelectFrameBuffer(codec, width, height);
 	}
 	return false;
 }
diff --git a/engines/scumm/smush/rebel/smush_player_ra2.h b/engines/scumm/smush/rebel/smush_player_ra2.h
index 5f4a33dbae8..20c20c55a29 100644
--- a/engines/scumm/smush/rebel/smush_player_ra2.h
+++ b/engines/scumm/smush/rebel/smush_player_ra2.h
@@ -68,7 +68,7 @@ private:
 							   int pos_x, int pos_y, int left, int top,
 							   int width, int height, TextStyleFlags flg);
 	void ra2PrepareFrameObjectSurface(int left, int top, int width, int height);
-	bool ra2SelectFrameBuffer(int width, int height);
+	bool ra2SelectFrameBuffer(int codec, int width, int height);
 	bool ra2DecodeCodec(int codec, const uint8 *src, int left, int top,
 						int width, int height, int pitch, int dataSize);
 	void ra2HandleDeltaPalette(int32 subSize, Common::SeekableReadStream &b);


Commit: d2be31c84aeb11e949ed8ce7c71e4ec06bb78d6d
    https://github.com/scummvm/scummvm/commit/d2be31c84aeb11e949ed8ce7c71e4ec06bb78d6d
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-03T17:39:38+02:00

Commit Message:
SCUMM: RA2: make level 12 progress until the end

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


diff --git a/engines/scumm/insane/rebel2/iact.cpp b/engines/scumm/insane/rebel2/iact.cpp
index 906160a537e..15e1a040343 100644
--- a/engines/scumm/insane/rebel2/iact.cpp
+++ b/engines/scumm/insane/rebel2/iact.cpp
@@ -602,22 +602,30 @@ void InsaneRebel2::updateOpcode6Handler(int16 par2) {
 }
 
 // ScummVM refactor helper for opcode 6 Handler 8, not a separate retail function.
-void InsaneRebel2::handleOpcode6Handler8(int16 par3, int16 par4) {
+void InsaneRebel2::handleOpcode6Handler8(Common::SeekableReadStream &b, int16 par4) {
 	// Handler 8 specific logic (third-person on foot) - FUN_00401234 case 4.
-	// Set ship level mode (DAT_0043e000 = par3)
-	_shipLevelMode = par3;
+	// DAT_0043e000 = local_14[3], which maps to the IACT header's par4/userId.
+	_shipLevelMode = par4;
 
-	// If par4 == 1, enable status bar and re-render laser texture (FUN_0040bb87)
-	if (par4 == 1) {
+	// local_14[4] is the first body word after the 8-byte IACT header.
+	int16 bodyStatusFlag = 0;
+	if (b.pos() + 2 <= b.size()) {
+		int64 savedPos = b.pos();
+		bodyStatusFlag = b.readSint16LE();
+		b.seek(savedPos);
+	}
+
+	// If local_14[4] == 1, enable status bar and re-render laser texture (FUN_0040bb87)
+	if (bodyStatusFlag == 1) {
 		_rebelStatusBarSprite = 5;
 		if (_smush_iconsNut && _smush_iconsNut->getNumChars() > 5) {
 			initLaserTexture(_smush_iconsNut, 5);
 		}
 	}
 
-	// Reset state when shipLevelMode != 0 && par4 == 1 (FUN_401234 lines 97-103)
+	// Reset state when shipLevelMode != 0 && local_14[4] == 1 (FUN_00401234 lines 97-103)
 	// Guard with _rebelOp6Initialized: runs once per wave video, not per frame.
-	if (_shipLevelMode != 0 && par4 == 1 && !_rebelOp6Initialized) {
+	if (_shipLevelMode != 0 && bodyStatusFlag == 1 && !_rebelOp6Initialized) {
 		clearBit(0);
 		for (int i = 0; i < 512; i++) {
 			_rebelLinks[i][0] = 0;
@@ -758,8 +766,8 @@ void InsaneRebel2::handleOpcode6Handler8(int16 par3, int16 par4) {
 			_vm->getActionState(kScummActionInsaneAttack);
 	}
 
-	debug("Rebel2 Opcode 6 (Handler 8): mode=%d range=%d shipPos=(%d,%d) target=(%d,%d) firing=%d dir=(%d,%d,%d)",
-		_shipLevelMode, _movementRangeLimit, _shipPosX, _shipPosY, _shipTargetX, _shipTargetY, _shipFiring,
+	debug("Rebel2 Opcode 6 (Handler 8): mode=%d bodyFlag=%d range=%d shipPos=(%d,%d) target=(%d,%d) firing=%d dir=(%d,%d,%d)",
+		_shipLevelMode, bodyStatusFlag, _movementRangeLimit, _shipPosX, _shipPosY, _shipTargetX, _shipTargetY, _shipFiring,
 		_shipDirectionH, _shipDirectionV, _shipDirectionIndex);
 }
 
@@ -1465,20 +1473,20 @@ void InsaneRebel2::scanOpcode6EmbeddedAnim(byte *renderBitmap, Common::SeekableR
 // iactRebel2Opcode6 -- Level setup / mode switch (FUN_41CADB case 4)
 //
 // Per-wave initialization: clears bit table, resets link tables, configures
-// handler mode (ship/turret/corridor), and loads collision zones. Called once
-// per wave video on frame 0.
+// handler mode (ship/turret/corridor), and loads collision zones. The original
+// gates the reset with handler-specific IACT fields, not always frame 0.
 //
 void InsaneRebel2::iactRebel2Opcode6(byte *renderBitmap, Common::SeekableReadStream &b, int32 chunkSize, int16 par2, int16 par3, int16 par4) {
 	// Opcode 6: Level setup / mode switch
 	// Based on FUN_41CADB case 4 (switch on *local_14 - 2 == 4, meaning opcode 6)
 	//
 	// For Handler 8 (third-person on foot) - FUN_00401234 case 4:
-	// - par3 sets ship level mode (DAT_0043e000)
-	// - par4 == 1 triggers status bar display and state reset
+	// - par4 sets ship level mode (DAT_0043e000)
+	// - first body word == 1 triggers status bar display and state reset
 	// - Updates ship position based on mouse input
 	//
 	// For Handler 0x26/0x19 (turret/FPS):
-	// - Same par4 == 1 behavior
+	// - Handler-specific status/reset word
 	// - Different view offset calculations
 
 	debug("Rebel2 IACT Opcode 6: par2=%d par3=%d par4=%d", par2, par3, par4);
@@ -1486,7 +1494,7 @@ void InsaneRebel2::iactRebel2Opcode6(byte *renderBitmap, Common::SeekableReadStr
 	updateOpcode6Handler(par2);
 
 	if (_rebelHandler == 8) {
-		handleOpcode6Handler8(par3, par4);
+		handleOpcode6Handler8(b, par4);
 		return;
 	}
 
diff --git a/engines/scumm/insane/rebel2/rebel.h b/engines/scumm/insane/rebel2/rebel.h
index f2d5cdb3f78..8dc6ceb75d0 100644
--- a/engines/scumm/insane/rebel2/rebel.h
+++ b/engines/scumm/insane/rebel2/rebel.h
@@ -538,7 +538,7 @@ public:
 	void iactRebel2Opcode3(Common::SeekableReadStream &b, int16 par2, int16 par3, int16 par4);
 	void iactRebel2Opcode6(byte *renderBitmap, Common::SeekableReadStream &b, int32 chunkSize, int16 par2, int16 par3, int16 par4);
 	void updateOpcode6Handler(int16 par2);
-	void handleOpcode6Handler8(int16 par3, int16 par4);
+	void handleOpcode6Handler8(Common::SeekableReadStream &b, int16 par4);
 	void handleOpcode6Handler7(Common::SeekableReadStream &b, int16 par4);
 	void handleOpcode6Handler25(byte *renderBitmap, Common::SeekableReadStream &b, int16 par2, int16 par3, int16 par4);
 	void handleOpcode6Turret(Common::SeekableReadStream &b, int16 par4);
@@ -1063,7 +1063,7 @@ public:
 	int16 _shipTargetY;              // DAT_0043e004 - Target Y
 
 	// Level mode for handler 8 (different from _rebelLevelType)
-	// Set by opcode 6 par3, affects ship rendering behavior
+	// Set by opcode 6 par4, affects ship rendering behavior
 	// Mode 0/1/3: "Shooting" - full movement range (127)
 	// Mode 2: "Covered" - restricted movement (41) - behind cover
 	// Mode 4: "Autopilot" - no shooting, scripted movement
diff --git a/engines/scumm/smush/rebel/smush_player_ra2.cpp b/engines/scumm/smush/rebel/smush_player_ra2.cpp
index 7389e22422c..3b0d660c7fb 100644
--- a/engines/scumm/smush/rebel/smush_player_ra2.cpp
+++ b/engines/scumm/smush/rebel/smush_player_ra2.cpp
@@ -41,6 +41,9 @@
 
 namespace Scumm {
 
+// FUN_00423880 is initialized at startup with a 400000-byte preload buffer.
+constexpr int kRebel2LoadBufferSize = 400000;
+
 bool isRebel2FullFrameDeltaCodec(int codec) {
 	return codec == SMUSH_CODEC_DELTA_BLOCKS || codec == SMUSH_CODEC_DELTA_GLYPHS;
 }
@@ -89,7 +92,7 @@ void SmushPlayerRebel2::initGamePlayerFields() {
 	_loadBufferOffset = 0;
 	_loadReadOffset = 8;  // Original starts reading at offset 8 (skips header)
 	_lastLoadChunkIdx = -1;
-	_totalLoadChunks = 0;
+	_loadStreamId = 0;
 	_ra2FrameSourceSkipY = 0;
 	_ra2FrameObjectSurfaceWidth = 0;
 	_ra2FrameObjectSurfaceHeight = 0;
@@ -141,19 +144,16 @@ void SmushPlayerRebel2::initGameVideoState() {
 
 /**
  * RA2-specific cleanup in SmushPlayer::release().
- * Frees stored FOBJ data but preserves _frameBuffer across videos.
+ * Preserves the stored FOBJ and _frameBuffer across videos.
  */
 void SmushPlayerRebel2::releaseGameVideoState() {
-	free(_storedFobjData);
-	_storedFobjData = nullptr;
-	_storedFobjDataSize = 0;
-	_storedFobjParm2 = 0;
 	free(_lastFobjData);
 	_lastFobjData = nullptr;
 	_lastFobjDataSize = 0;
 	_hasFrameFobjForGost = false;
-	// Preserve _frameBuffer across videos so that gameplay videos (which have no
-	// background FOBJ) can use the stored background from the previous BEG video.
+	// FUN_00423880 allocates the STOR overlay buffer once for the SMUSH subsystem,
+	// and FUN_004246d0 replays the last stored FOBJ even after a new SAN starts.
+	// Level 12 wave segments depend on this when 12P01_B.SAN starts with FTCH.
 }
 
 /**
@@ -585,7 +585,7 @@ void SmushPlayerRebel2::handleGameLoad(int32 subSize, Common::SeekableReadStream
  * Handle LOAD chunk for Rebel Assault 2.
  *
  * LOAD chunks stream embedded resource data across multiple frames.
- * The data is accumulated in a buffer and consumed by the audio system.
+ * FUN_00424450 treats word 0 as the stream id and word 1 as the chunk index.
  */
 void SmushPlayerRebel2::handleLoad(int32 subSize, Common::SeekableReadStream &b) {
 	debugC(DEBUG_SMUSH, "SmushPlayerRebel2::handleLoad()");
@@ -595,26 +595,24 @@ void SmushPlayerRebel2::handleLoad(int32 subSize, Common::SeekableReadStream &b)
 		return;
 	}
 
-	int16 totalChunks = b.readUint16LE();
+	int16 streamId = b.readUint16LE();
 	int16 chunkIndex = b.readUint16LE();
 	b.skip(6);  // Unknown/padding
 
 	int32 dataSize = subSize - 10;
 
-	debugC(DEBUG_SMUSH, "SmushPlayerRebel2::handleLoad: chunk %d/%d, dataSize=%d, bufferOffset=%d",
-		chunkIndex, totalChunks, dataSize, _loadBufferOffset);
+	debugC(DEBUG_SMUSH, "SmushPlayerRebel2::handleLoad: stream=%d chunk=%d dataSize=%d bufferOffset=%d",
+		streamId, chunkIndex, dataSize, _loadBufferOffset);
 
-	// First chunk in sequence - reset buffer state
 	if (chunkIndex == 0) {
 		_loadBufferOffset = 0;
 		_loadReadOffset = 8;
 		_lastLoadChunkIdx = -1;
-		_totalLoadChunks = totalChunks;
+		_loadStreamId = streamId;
 
-		int32 estimatedSize = totalChunks * 600;
-		if (_loadBuffer == nullptr || _loadBufferSize < estimatedSize) {
+		if (_loadBuffer == nullptr || _loadBufferSize < kRebel2LoadBufferSize) {
 			free(_loadBuffer);
-			_loadBufferSize = estimatedSize;
+			_loadBufferSize = kRebel2LoadBufferSize;
 			_loadBuffer = (byte *)malloc(_loadBufferSize);
 			if (_loadBuffer == nullptr) {
 				warning("SmushPlayerRebel2::handleLoad: Failed to allocate %d bytes for LOAD buffer",
@@ -627,31 +625,23 @@ void SmushPlayerRebel2::handleLoad(int32 subSize, Common::SeekableReadStream &b)
 		}
 	}
 
-	// Check sequential order
 	if (_lastLoadChunkIdx + 1 != chunkIndex) {
 		debugC(DEBUG_SMUSH, "SmushPlayerRebel2::handleLoad: Non-sequential chunk %d (expected %d), skipping",
 			chunkIndex, _lastLoadChunkIdx + 1);
 		return;
 	}
 
-	// Check buffer capacity
-	if (_loadBuffer == nullptr || _loadBufferOffset + dataSize >= _loadBufferSize) {
+	if (_loadBuffer == nullptr || _loadBufferOffset + subSize >= _loadBufferSize) {
 		warning("SmushPlayerRebel2::handleLoad: Buffer overflow - offset=%d size=%d limit=%d",
-			_loadBufferOffset, dataSize, _loadBufferSize);
+			_loadBufferOffset, subSize, _loadBufferSize);
 		return;
 	}
 
-	// Copy data to buffer
 	b.read(_loadBuffer + _loadBufferOffset, dataSize);
 	_loadBufferOffset += dataSize;
 	_lastLoadChunkIdx = chunkIndex;
 
 	debugC(DEBUG_SMUSH, "SmushPlayerRebel2::handleLoad: Accumulated %d bytes total", _loadBufferOffset);
-
-	if (chunkIndex == totalChunks - 1) {
-		debugC(DEBUG_SMUSH, "SmushPlayerRebel2::handleLoad: Sequence complete - %d chunks, %d bytes total",
-			totalChunks, _loadBufferOffset);
-	}
 }
 
 /**
diff --git a/engines/scumm/smush/rebel/smush_player_ra2.h b/engines/scumm/smush/rebel/smush_player_ra2.h
index 20c20c55a29..35af253e0f6 100644
--- a/engines/scumm/smush/rebel/smush_player_ra2.h
+++ b/engines/scumm/smush/rebel/smush_player_ra2.h
@@ -82,7 +82,7 @@ private:
 	int32 _loadBufferOffset;
 	int32 _loadReadOffset;
 	int16 _lastLoadChunkIdx;
-	int16 _totalLoadChunks;
+	int16 _loadStreamId;
 	int _ra2FrameSourceSkipY;
 	int _ra2FrameObjectSurfaceWidth;
 	int _ra2FrameObjectSurfaceHeight;


Commit: 5905f3934ccee38d0a2d1348619e51c55deefece
    https://github.com/scummvm/scummvm/commit/5905f3934ccee38d0a2d1348619e51c55deefece
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-03T17:58:24+02:00

Commit Message:
SCUMM: RA2: proper shooting in level 12

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


diff --git a/engines/scumm/insane/rebel2/iact.cpp b/engines/scumm/insane/rebel2/iact.cpp
index 15e1a040343..60c194e6a0d 100644
--- a/engines/scumm/insane/rebel2/iact.cpp
+++ b/engines/scumm/insane/rebel2/iact.cpp
@@ -2151,8 +2151,8 @@ bool InsaneRebel2::loadHandler8ShipSprites(byte *animData, int32 size, int16 par
 	// par4 values (from IACT data offset +6, NOT par3 which is always 0):
 	//   1: POV001 - Primary ship sprite (DAT_0047e010 / _shipSprite)
 	//   3: POV004 - Secondary ship sprite (DAT_0047e028 / _shipSprite2)
-	//   6: POV002 - Ship overlay 1 (DAT_0047e020 / _shipOverlay1)
-	//   7: POV003 - Ship overlay 2 (DAT_0047e018 / _shipOverlay2)
+	//   6: POV002 - Shot impact overlay (DAT_0047e020 / _shipOverlay1)
+	//   7: POV003 - Shot impact overlay (DAT_0047e018 / _shipOverlay2)
 
 	if (!animData || size <= 0) {
 		return false;
@@ -2183,11 +2183,11 @@ bool InsaneRebel2::loadHandler8ShipSprites(byte *animData, int32 size, int16 par
 		delete _shipSprite2;
 		_shipSprite2 = newNut;
 		break;
-	case 6:  // POV002 - Ship overlay 1
+	case 6:  // POV002 - Shot impact overlay
 		delete _shipOverlay1;
 		_shipOverlay1 = newNut;
 		break;
-	case 7:  // POV003 - Ship overlay 2
+	case 7:  // POV003 - Shot impact overlay
 		delete _shipOverlay2;
 		_shipOverlay2 = newNut;
 		break;
diff --git a/engines/scumm/insane/rebel2/rebel.cpp b/engines/scumm/insane/rebel2/rebel.cpp
index a777dc8353e..a3acf396b74 100644
--- a/engines/scumm/insane/rebel2/rebel.cpp
+++ b/engines/scumm/insane/rebel2/rebel.cpp
@@ -347,6 +347,13 @@ InsaneRebel2::InsaneRebel2(ScummEngine_v7 *scumm) {
 		_vehicleShots[i].targetX = 0;
 		_vehicleShots[i].targetY = 0;
 	}
+	for (i = 0; i < 7; i++) {
+		_vehicleShotImpacts[i].counter = 0;
+		_vehicleShotImpacts[i].x = 0;
+		_vehicleShotImpacts[i].y = 0;
+		_vehicleShotImpacts[i].spriteIndex = 0;
+	}
+	_vehicleShotImpactIndex = 0;
 
 	// Initialize Handler 7 Space shot system (FUN_40FADF)
 	for (i = 0; i < 2; i++) {
@@ -1501,10 +1508,25 @@ int32 InsaneRebel2::processMouse() {
 	// - Handler 25 keeps edge-triggered clicks due cover-toggle/sticky input semantics.
 	// - Other gameplay handlers fire while button is held; slot counters still rate-limit.
 	bool triggerShot = (_rebelHandler == 25) ? (leftPressed && !leftWasPressed) : leftPressed;
-	if (triggerShot && isShootingAllowed()) {
-		Common::Point mousePos = (_rebelHandler == 7) ? getHandler7ShotTargetPoint() : getGameplayAimPoint();
-		debug("Rebel2 Click: Mouse=(%d,%d) Enemies=%d",
-			mousePos.x, mousePos.y, _enemies.size());
+	bool canShoot = isShootingAllowed();
+	if (_rebelHandler == 8) {
+		// FUN_00401CCF uses the same per-frame fire bit both to spawn shots and
+		// to choose the POV gun sprite. Keep the sprite driven by the event-manager
+		// input that processMouse() uses during SMUSH playback.
+		_shipFiring = triggerShot && canShoot;
+	}
+	if (triggerShot && canShoot) {
+		Common::Point mousePos;
+		if (_rebelHandler == 7) {
+			mousePos = getHandler7ShotTargetPoint();
+		} else if (_rebelHandler == 8) {
+			mousePos = getHandler8ShotTargetPoint();
+		} else {
+			mousePos = getGameplayAimPoint();
+		}
+		Common::Point gameplayAim = getGameplayAimPoint();
+		debug("Rebel2 Click: Mouse=(%d,%d) Target=(%d,%d) Enemies=%d",
+			gameplayAim.x, gameplayAim.y, mousePos.x, mousePos.y, _enemies.size());
 
 		// Spawn visual shot immediately
 		spawnShot(mousePos.x, mousePos.y);
diff --git a/engines/scumm/insane/rebel2/rebel.h b/engines/scumm/insane/rebel2/rebel.h
index 8dc6ceb75d0..54489204056 100644
--- a/engines/scumm/insane/rebel2/rebel.h
+++ b/engines/scumm/insane/rebel2/rebel.h
@@ -596,6 +596,7 @@ public:
 
 	// Draw Handler 8 ship sprite (third-person on foot - POV sprites)
 	void renderHandler8Ship(byte *renderBitmap, int pitch, int width, int height);
+	void renderVehicleShotImpacts(byte *renderBitmap, int pitch, int width, int height); // FUN_402DA8 (Handler 8)
 
 	// Draw fallback ship using embedded HUD frame
 	void renderFallbackShip(byte *renderBitmap, int pitch, int width, int height);
@@ -614,6 +615,7 @@ public:
 	Common::Point getHandler7ShipDrawPoint();
 	Common::Point getHandler7ProjectedPoint();
 	Common::Point getHandler7ShotTargetPoint();
+	Common::Point getHandler8ShotTargetPoint();
 
 	// Reset enemy active flags and collision zones at frame end
 	void frameEndCleanup();
@@ -969,6 +971,21 @@ public:
 	};
 	VehicleShot _vehicleShots[2];
 
+	// Handler 8 shot impact animation state (FUN_402DA8 / FUN_402ED0)
+	// DAT_0043f81a[7]: impact duration counter
+	// DAT_0043f828[7]: impact world X
+	// DAT_0043f836[7]: impact world Y
+	// DAT_0043f844[7]: impact sprite index from DAT_0047e030 background mask
+	// DAT_0043f852: ring-buffer index
+	struct VehicleShotImpact {
+		int16 counter;
+		int16 x;
+		int16 y;
+		int16 spriteIndex;
+	};
+	VehicleShotImpact _vehicleShotImpacts[7];
+	int16 _vehicleShotImpactIndex;
+
 	// ---------------------------------------------------------------------------
 	// Handler 7 Third-Person Ship Shot System
 	// ---------------------------------------------------------------------------
@@ -1035,16 +1052,16 @@ public:
 	// Based on FUN_00401234 and FUN_00401ccf disassembly:
 	// - DAT_0047e010: Primary ship sprite (POV001, subcase 1)
 	// - DAT_0047e028: Secondary ship sprite (POV004, subcase 3)
-	// - DAT_0047e020: Additional overlay (POV002, subcase 6)
-	// - DAT_0047e018: Additional overlay (POV003, subcase 7)
+	// - DAT_0047e020: Shot impact overlay (POV002, subcase 6)
+	// - DAT_0047e018: Shot impact overlay (POV003, subcase 7)
 	// - DAT_0043e006: Ship X position (raw, needs conversion for display)
 	// - DAT_0043e008: Ship Y position (raw, needs conversion for display)
-	// - DAT_0043e000: Level mode from opcode 6 par3
+	// - DAT_0043e000: Level mode from opcode 6 par4
 
 	NutRenderer *_shipSprite;        // DAT_0047e010 - Primary ship NUT
 	NutRenderer *_shipSprite2;       // DAT_0047e028 - Secondary ship NUT
-	NutRenderer *_shipOverlay1;      // DAT_0047e020 - Additional overlay
-	NutRenderer *_shipOverlay2;      // DAT_0047e018 - Additional overlay
+	NutRenderer *_shipOverlay1;      // DAT_0047e020 - Shot impact overlay
+	NutRenderer *_shipOverlay2;      // DAT_0047e018 - Shot impact overlay
 
 	// Level 2 background buffer (DAT_0047e030)
 	// Loaded from IACT opcode 8, par4=5 - contains 320x200 background image
diff --git a/engines/scumm/insane/rebel2/render.cpp b/engines/scumm/insane/rebel2/render.cpp
index afabee2e76a..97d32232cc0 100644
--- a/engines/scumm/insane/rebel2/render.cpp
+++ b/engines/scumm/insane/rebel2/render.cpp
@@ -589,6 +589,13 @@ Common::Point InsaneRebel2::getHandler7ShotTargetPoint() {
 	                     projected.y + ABS(_smoothedVelocity) / 4 - _verticalInput / 2 - 0x28);
 }
 
+Common::Point InsaneRebel2::getHandler8ShotTargetPoint() {
+	// Retail handler 8 stores and draws the shot target from the damped ship
+	// position in FUN_00401ccf, not from the current mouse/analog aim point.
+	return Common::Point(((_shipPosX - 0xa0) >> 3) + 0xa0,
+	                     ((_shipPosY - 0x28) >> 2) + 0x69);
+}
+
 // spawnSpaceShot -- Handler 7 space combat shot spawn (FUN_40D836).
 void InsaneRebel2::spawnSpaceShot(int x, int y) {
 	for (int i = 0; i < 2; i++) {
@@ -2484,6 +2491,7 @@ void InsaneRebel2::renderGameplayPostFrame(byte *renderBitmap, int pitch, int wi
 		_flyShipSprite ? _flyShipSprite->getNumChars() : 0);
 
 	renderHandler7Ship(renderBitmap, pitch, width, height);
+	renderVehicleShotImpacts(renderBitmap, pitch, width, height);
 	renderHandler8Ship(renderBitmap, pitch, width, height);
 	renderFallbackShip(renderBitmap, pitch, width, height);
 
@@ -3295,29 +3303,6 @@ void InsaneRebel2::renderHandler8Ship(byte *renderBitmap, int pitch, int width,
 		}
 	}
 
-	// Also render ship overlays (POV002 / POV003) if loaded.
-	// These are weapon/character overlays loaded via IACT opcode 8 par4=6,7.
-	if (_shipOverlay1) {
-		int overlayIdx = _shipFiring ? 1 : 0;
-		if (overlayIdx >= _shipOverlay1->getNumChars())
-			overlayIdx = 0;
-		int16 ovlXOff = _shipOverlay1->getCharXOffset(overlayIdx);
-		int16 ovlYOff = _shipOverlay1->getCharYOffset(overlayIdx);
-		int ovlX = displayOffsetX + ovlXOff + _viewX;
-		int ovlY = displayOffsetY + ovlYOff + _viewY;
-		renderNutSprite(renderBitmap, pitch, width, height, ovlX, ovlY, _shipOverlay1, overlayIdx);
-	}
-	if (_shipOverlay2) {
-		int overlayIdx = _shipFiring ? 1 : 0;
-		if (overlayIdx >= _shipOverlay2->getNumChars())
-			overlayIdx = 0;
-		int16 ovlXOff = _shipOverlay2->getCharXOffset(overlayIdx);
-		int16 ovlYOff = _shipOverlay2->getCharYOffset(overlayIdx);
-		int ovlX = displayOffsetX + ovlXOff + _viewX;
-		int ovlY = displayOffsetY + ovlYOff + _viewY;
-		renderNutSprite(renderBitmap, pitch, width, height, ovlX, ovlY, _shipOverlay2, overlayIdx);
-	}
-
 	int sprW = _shipSprite->getCharWidth(spriteIndex);
 	int sprH = _shipSprite->getCharHeight(spriteIndex);
 	debug("Rebel2 Handler8: Ship at (%d,%d) raw(%d,%d) offset(%d,%d) nutOff(%d,%d) size(%d,%d) bottom=%d view(%d,%d) sprite=%d/%d",
@@ -3326,6 +3311,41 @@ void InsaneRebel2::renderHandler8Ship(byte *renderBitmap, int pitch, int width,
 		_viewX, _viewY, spriteIndex, numSprites);
 }
 
+// renderVehicleShotImpacts -- Handler 8 shot impact sprites (FUN_402DA8).
+void InsaneRebel2::renderVehicleShotImpacts(byte *renderBitmap, int pitch, int width, int height) {
+	if (_rebelHandler != 8)
+		return;
+
+	for (int i = 0; i < 7; i++) {
+		VehicleShotImpact &impact = _vehicleShotImpacts[i];
+		if (impact.counter <= 0)
+			continue;
+
+		int drawX = impact.x - _shipPosX + _viewX;
+		int drawY = impact.y - _shipPosY + _viewY;
+
+		// Original draws DAT_0047e020 repeatedly based on remaining life, then
+		// DAT_0047e018 once, both using the sampled background-mask sprite index.
+		if (_shipOverlay1 && impact.spriteIndex >= 0 && impact.spriteIndex < _shipOverlay1->getNumChars()) {
+			int spriteX = drawX + _shipOverlay1->getCharXOffset(impact.spriteIndex) -
+				_shipOverlay1->getCharWidth(impact.spriteIndex) / 2;
+			int spriteY = drawY + _shipOverlay1->getCharYOffset(impact.spriteIndex) -
+				_shipOverlay1->getCharHeight(impact.spriteIndex) / 2;
+			for (int repeat = 0; repeat <= (impact.counter >> 2); repeat++)
+				renderNutSprite(renderBitmap, pitch, width, height, spriteX, spriteY, _shipOverlay1, impact.spriteIndex);
+		}
+		if (_shipOverlay2 && impact.spriteIndex >= 0 && impact.spriteIndex < _shipOverlay2->getNumChars()) {
+			int spriteX = drawX + _shipOverlay2->getCharXOffset(impact.spriteIndex) -
+				_shipOverlay2->getCharWidth(impact.spriteIndex) / 2;
+			int spriteY = drawY + _shipOverlay2->getCharYOffset(impact.spriteIndex) -
+				_shipOverlay2->getCharHeight(impact.spriteIndex) / 2;
+			renderNutSprite(renderBitmap, pitch, width, height, spriteX, spriteY, _shipOverlay2, impact.spriteIndex);
+		}
+
+		impact.counter--;
+	}
+}
+
 // Handler 25: Draw GRD001 (wall/cockpit overlay) in procPostRendering.
 // renderHandler25ShipPre -- Handler 25 GRD001 pre-rendering (FUN_0041DB5E lines 202-221).
 // GRD sprites drawn AFTER FOBJ enemies, before GRD002. Mode-based clipping:
@@ -4045,6 +4065,32 @@ void InsaneRebel2::renderVehicleLaserShots(byte *renderBitmap, int pitch, int wi
 			progress, maxDuration, 20, 8, 4);
 
 		_vehicleShots[i].counter--;
+
+		// FUN_402ED0 samples DAT_0047e030 after drawing the beam. Non-zero
+		// mask pixels select the POV002/POV003 impact sprite index rendered by
+		// FUN_402DA8 on following frames. Mode 2 suppresses these impacts.
+		if (_shipLevelMode != 2 && _level2BackgroundLoaded && _level2Background) {
+			int impactX = ((_shipPosX - 160) >> 3) + _shipPosX + 160;
+			int impactY = ((_shipPosY - 40) >> 2) + _shipPosY + 105;
+			int maskX = impactX - 160;
+			int maskY = impactY - 30;
+
+			if (impactX > 160 && impactX < 480 &&
+					maskX >= 0 && maskX < 320 && maskY >= 0 && maskY < 200) {
+				byte spriteIndex = _level2Background[maskY * 320 + maskX];
+				if (spriteIndex != 0) {
+					VehicleShotImpact &impact = _vehicleShotImpacts[_vehicleShotImpactIndex];
+					_vehicleShotImpactIndex++;
+					if (_vehicleShotImpactIndex >= 7)
+						_vehicleShotImpactIndex = 0;
+
+					impact.counter = 12;
+					impact.x = impactX;
+					impact.y = impactY;
+					impact.spriteIndex = spriteIndex;
+				}
+			}
+		}
 	}
 }
 
@@ -4130,7 +4176,7 @@ void InsaneRebel2::renderHandler25LaserShots(byte *renderBitmap, int pitch, int
 	}
 }
 
-// renderCrosshair -- Draw crosshair/reticle at mouse position.
+// renderCrosshair -- Draw crosshair/reticle at the current handler's aim point.
 void InsaneRebel2::renderCrosshair(byte *renderBitmap, int pitch, int width, int height) {
 	// From FUN_0040d836 (Handler 7) line 167-168: crosshair only drawn when DAT_004437c0 == 2
 	// Don't draw crosshair when shooting is disabled (flight-only segments)
@@ -4147,7 +4193,14 @@ void InsaneRebel2::renderCrosshair(byte *renderBitmap, int pitch, int width, int
 	// Update target lock state and draw crosshair/reticle
 
 	// Target lock detection (DAT_00443676 equivalent)
-	Common::Point aimPos = (_rebelHandler == 7) ? getHandler7ShotTargetPoint() : getGameplayAimPoint();
+	Common::Point aimPos;
+	if (_rebelHandler == 7) {
+		aimPos = getHandler7ShotTargetPoint();
+	} else if (_rebelHandler == 8) {
+		aimPos = getHandler8ShotTargetPoint();
+	} else {
+		aimPos = getGameplayAimPoint();
+	}
 	Common::Point worldMousePos = aimPos;
 	if (_rebelHandler != 7) {
 		worldMousePos.x += _viewX;
@@ -4199,8 +4252,8 @@ void InsaneRebel2::renderCrosshair(byte *renderBitmap, int pitch, int width, int
 		int ch = _smush_iconsNut->getCharHeight(reticleIndex);
 
 		// Calculate crosshair position
-		int crosshairX = aimPos.x - cw / 2;
-		int crosshairY = aimPos.y - ch / 2;
+		int crosshairX = aimPos.x;
+		int crosshairY = aimPos.y;
 		if (_rebelHandler != 7) {
 			crosshairX += _viewX;
 			crosshairY += _viewY;
@@ -4213,6 +4266,10 @@ void InsaneRebel2::renderCrosshair(byte *renderBitmap, int pitch, int width, int
 			crosshairY += _rebelViewOffsetY;
 		}
 
+		// FUN_004236e0 with flags=2 applies the NUT frame offsets, then centers.
+		crosshairX += _smush_iconsNut->getCharXOffset(reticleIndex) - cw / 2;
+		crosshairY += _smush_iconsNut->getCharYOffset(reticleIndex) - ch / 2;
+
 		renderNutSprite(renderBitmap, pitch, width, height,
 			crosshairX, crosshairY,
 			_smush_iconsNut, reticleIndex);


Commit: 3f6b1ed8e45ffcd86415bf13754c70eb33749f7b
    https://github.com/scummvm/scummvm/commit/3f6b1ed8e45ffcd86415bf13754c70eb33749f7b
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-03T19:34:28+02:00

Commit Message:
SCUMM: RA2: render overlay in level 12

Changed paths:
    engines/scumm/insane/rebel2/iact.cpp
    engines/scumm/insane/rebel2/rebel.cpp
    engines/scumm/insane/rebel2/rebel.h
    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/iact.cpp b/engines/scumm/insane/rebel2/iact.cpp
index 60c194e6a0d..854fcf100df 100644
--- a/engines/scumm/insane/rebel2/iact.cpp
+++ b/engines/scumm/insane/rebel2/iact.cpp
@@ -713,6 +713,15 @@ void InsaneRebel2::handleOpcode6Handler8(Common::SeekableReadStream &b, int16 pa
 			_shipPosY = (newY < _shipTargetY) ? _shipTargetY : newY;
 		}
 
+		// FUN_00401234 calls FUN_00424510(-DAT_0043e006, -DAT_0043e008)
+		// after updating the handler-8 camera. This shifts subsequent FOBJ
+		// decoding into screen coordinates; FUN_00401CCF then draws HUD and
+		// weapon sprites without a separate final-buffer scroll.
+		if (_player) {
+			_player->_fobjOffsetX = -_shipPosX;
+			_player->_fobjOffsetY = -_shipPosY;
+		}
+
 		// Calculate ship direction indices for sprite selection
 		// Map mouse position to 5x7 direction grid (like Handler 7)
 		int16 mouseX = aimPos.x;
diff --git a/engines/scumm/insane/rebel2/rebel.cpp b/engines/scumm/insane/rebel2/rebel.cpp
index a3acf396b74..4d125c67016 100644
--- a/engines/scumm/insane/rebel2/rebel.cpp
+++ b/engines/scumm/insane/rebel2/rebel.cpp
@@ -160,10 +160,11 @@ InsaneRebel2::InsaneRebel2(ScummEngine_v7 *scumm) {
 	//   Font 0 (^f00): TALKFONT.NUT - Default menu font (DAT_00485058)
 	//   Font 1 (^f01): SMALFONT.NUT - Small font for format code switching
 	//   Font 2 (^f02): TITLFONT.NUT - Title font
-	//   Font 3 (^f03): POVFONT.NUT - POV font (not loaded here)
+	//   Font 3 (^f03): POVFONT.NUT - POV font
 	_smush_talkfontNut = makeRebel2Font(_vm, "SYSTM/TALKFONT.NUT");
 	_smush_smalfontNut = makeRebel2Font(_vm, "SYSTM/SMALFONT.NUT");
 	_smush_titlefontNut = makeRebel2Font(_vm, "SYSTM/TITLFONT.NUT");
+	_smush_povfontNut = makeRebel2Font(_vm, "SYSTM/POVFONT.NUT");
 
 	_pauseOverlayActive = false;
 	memset(_savedPausePalette, 0, sizeof(_savedPausePalette));
@@ -543,6 +544,9 @@ InsaneRebel2::InsaneRebel2(ScummEngine_v7 *scumm) {
 	_gameplaySectionActive = false;
 	_lastGameplayMenuCloseTime = 0;
 	_lastMenuGamepadNavigationTime = 0;
+	_handler8HudGlyph = '#';
+	_handler8HudMessageTimer = 0;
+	_handler8HudMessageIndex = 0;
 
 	// Initialize level state tracking for multi-phase levels
 	_currentPhase = 1;
@@ -566,6 +570,7 @@ InsaneRebel2::~InsaneRebel2() {
 	delete _smush_talkfontNut;
 	delete _smush_smalfontNut;
 	delete _smush_titlefontNut;
+	delete _smush_povfontNut;
 
 	// Clean up Handler 8 ship sprites
 	delete _shipSprite;
@@ -1533,7 +1538,10 @@ int32 InsaneRebel2::processMouse() {
 
 		// Calculate world position for hit testing
 		Common::Point worldMousePos = mousePos;
-		if (_rebelHandler != 7) {
+		if (_rebelHandler == 8) {
+			worldMousePos.x += _shipPosX;
+			worldMousePos.y += _shipPosY;
+		} else if (_rebelHandler != 7) {
 			worldMousePos.x += _viewX;
 			worldMousePos.y += _viewY;
 		}
@@ -1634,7 +1642,8 @@ int32 InsaneRebel2::processMouse() {
 				// Play enemy death sound.
 				// Pan based on enemy center X position: (screenX - 160) mapped to [-127,127]
 				{
-					int enemyCenterX = (it->rect.left + it->rect.right) / 2 - _viewX;
+					int cameraX = (_rebelHandler == 8) ? _shipPosX : _viewX;
+					int enemyCenterX = (it->rect.left + it->rect.right) / 2 - cameraX;
 					int sfxPan = CLIP((enemyCenterX - 160) * 127 / 160, -127, 127);
 					if (_rebelHandler == 8 && it->type >= 1 && it->type <= 4) {
 						// Handler 8 soldier types 1-4: play from auxiliary buffer 0
diff --git a/engines/scumm/insane/rebel2/rebel.h b/engines/scumm/insane/rebel2/rebel.h
index 54489204056..645596603c4 100644
--- a/engines/scumm/insane/rebel2/rebel.h
+++ b/engines/scumm/insane/rebel2/rebel.h
@@ -494,6 +494,7 @@ public:
 	NutRenderer *_smush_talkfontNut;   // Font 0 - primary menu font (DAT_00485058)
 	NutRenderer *_smush_smalfontNut;   // Font 1 - small font for ^f01 switching
 	NutRenderer *_smush_titlefontNut;  // Font 2 - title font
+	NutRenderer *_smush_povfontNut;    // Font 3 - POV font for Handler 8 overlay
 
 	// Saved palette for pause overlay restoration (FUN_405A21)
 	byte _savedPausePalette[768];
@@ -612,6 +613,7 @@ public:
 
 	// Update target lock state and draw crosshair/reticle
 	void renderCrosshair(byte *renderBitmap, int pitch, int width, int height);
+	void renderHandler8PovOverlay(byte *renderBitmap, int pitch, int width, int height);
 	Common::Point getHandler7ShipDrawPoint();
 	Common::Point getHandler7ProjectedPoint();
 	Common::Point getHandler7ShotTargetPoint();
@@ -1086,6 +1088,9 @@ public:
 	// Mode 4: "Autopilot" - no shooting, scripted movement
 	// Mode 5: "Cutscene" - ship not rendered
 	int16 _shipLevelMode;            // DAT_0043e000
+	char _handler8HudGlyph;          // DAT_0047e048
+	int16 _handler8HudMessageTimer;  // DAT_0047e040
+	int16 _handler8HudMessageIndex;  // DAT_0047e044
 
 	// Movement range limiter for Handler 8 (Level 2 covered/shooting states)
 	// Controls horizontal movement range: 127 for shooting, 41 for covered
diff --git a/engines/scumm/insane/rebel2/render.cpp b/engines/scumm/insane/rebel2/render.cpp
index 97d32232cc0..9bdb7e7583a 100644
--- a/engines/scumm/insane/rebel2/render.cpp
+++ b/engines/scumm/insane/rebel2/render.cpp
@@ -63,6 +63,66 @@ static int countEmbeddedFramePixels(const InsaneRebel2::EmbeddedSanFrame &frame)
 	return count;
 }
 
+static bool parseRebel2TextOverlayFormat(const char *&str, NutRenderer *&curFont, int &curColor,
+		NutRenderer **fonts, int numFonts, NutRenderer *defaultFont) {
+	int fontId = InsaneRebel2::parseFormatCode(str, curColor);
+	if (fontId >= 0) {
+		curFont = (fontId < numFonts && fonts[fontId]) ? fonts[fontId] : defaultFont;
+		return true;
+	}
+
+	return fontId == -2;
+}
+
+static const char *getHandler8PovOverlayString(int id) {
+	switch (id) {
+	case 200:
+		return "^f03^c248 MODE  ^c005 INFARED";
+	case 201:
+		return "^f03^c248 SCANNING";
+	case 202:
+		return "^f03^c005 VHL EMM %lx";
+	case 203:
+		return "^f03^c005 SCOOBYNESS FACTOR %lx";
+	case 204:
+		return "^f03^c005 USELESS COORDINATES %lx %lx";
+	case 205:
+		return "^f03^c248 Buy Afterlife";
+	case 206:
+		return "^f03^c248 No Homework.. You must fight the bear";
+	case 207:
+		return "^f03^c248 What is that watermelon doing there?";
+	case 208:
+		return "^f03^c248 I just like to say taboo";
+	case 209:
+		return "^f03^c248 How about them Bears";
+	case 210:
+		return "^f03^c248 There is cause to be optimistic -HAL";
+	case 211:
+		return "^f03^c248 Little Darth is between his legs -LISA";
+	default:
+		return nullptr;
+	}
+}
+
+static int getHandler8PovOverlayRandom(ScummEngine_v7 *vm, int max) {
+	if (max == 0)
+		return 0;
+	if (max < 0)
+		return -vm->_rnd.getRandomNumber(-max - 1);
+	return vm->_rnd.getRandomNumber(max - 1);
+}
+
+static void drawHandler8PovOverlayText(const Rebel2FontSet &fontSet, byte *renderBitmap,
+		int pitch, int width, int height, const char *str, int x, int y, int16 color,
+		TextStyleFlags flags) {
+	if (!str)
+		return;
+
+	Common::Rect clipRect(0, 0, width, height);
+	drawRebel2String(fontSet, str, strlen(str), renderBitmap, clipRect, x, y, pitch, color, flags);
+}
+
 static void blitEmbeddedFrameRegion(byte *renderBitmap, int pitch, int clipWidth, int clipHeight,
 		const InsaneRebel2::EmbeddedSanFrame &frame, int destX, int destY,
 		int srcX, int srcY, int drawWidth, int drawHeight) {
@@ -462,8 +522,8 @@ void InsaneRebel2::spawnVehicleShot(int x, int y) {
 			// FUN_0041189e(6, local_c + 1, 0x7f, 0, 0) ??? HBLAST.SAD
 			playSfx(6, 127, 0);
 			_vehicleShots[i].counter = getShotMaxDuration();
-			_vehicleShots[i].targetX = x + _viewX;
-			_vehicleShots[i].targetY = y + _viewY;
+			_vehicleShots[i].targetX = x;
+			_vehicleShots[i].targetY = y;
 			break;
 		}
 	}
@@ -2187,6 +2247,16 @@ void InsaneRebel2::renderNutSpriteMirrored(byte *dst, int pitch, int width, int
 
 // updatePostRenderScroll -- Set SmushPlayer scroll offsets for the current frame.
 void InsaneRebel2::updatePostRenderScroll(int width, int height) {
+	if (_rebelHandler == 8) {
+		// Handler 8 follows FUN_00401234/FUN_00401CCF: the camera is applied
+		// through FUN_00424510 before FOBJ decoding, not by scrolling the final
+		// buffer copied by FUN_00424540.
+		_viewX = 0;
+		_viewY = 0;
+		_player->setScrollOffset(0, 0);
+		return;
+	}
+
 	// Rebel Assault 2 uses a buffer larger (424x260) than screen (320x200).
 	// Map mouse X (0-320) to Scroll X (0-104), and Y (0-200) to Scroll Y (0-60).
 	int maxScrollX = width - _vm->_screenWidth;
@@ -2529,6 +2599,9 @@ void InsaneRebel2::renderGameplayPostFrame(byte *renderBitmap, int pitch, int wi
 	// Crosshair/reticle (FUN_004089ab, FUN_0040d836).
 	renderCrosshair(renderBitmap, pitch, width, height);
 
+	// Handler 8 POV text overlay (FUN_00401CCF).
+	renderHandler8PovOverlay(renderBitmap, pitch, width, height);
+
 	// HUD score/lives rendering (FUN_0041c012).
 	renderScoreHUD(renderBitmap, pitch, width, height, statusBarY);
 
@@ -2740,13 +2813,6 @@ void InsaneRebel2::renderTextOverlay(byte *renderBitmap, int pitch, int width, i
 
 	Common::Rect clipRect(0, 0, width, height);
 
-	// Wrapper around shared parseFormatCode that also updates curFont
-	auto parseFormat = [&](const char *&s, NutRenderer *&curFont, int &curColor) {
-		int fc = parseFormatCode(s, curColor);
-		if (fc >= 0) { curFont = (fonts[fc] ? fonts[fc] : defaultFont); return true; }
-		return fc == -2;
-	};
-
 	// Split into lines, then render each centered at textX (FUN_004341a0).
 	// Older RA2 text loading joined multi-line strings with spaces, leaving
 	// " ^f" as the separator; current TRES loading preserves real newlines.
@@ -2793,7 +2859,7 @@ void InsaneRebel2::renderTextOverlay(byte *renderBitmap, int pitch, int width, i
 			NutRenderer *mFont = defaultFont;
 			int mColor = 1;
 			while (s < lineEnd && (visCount + lineVisCount) < displayLen) {
-				if (parseFormat(s, mFont, mColor))
+				if (parseRebel2TextOverlayFormat(s, mFont, mColor, fonts, ARRAYSIZE(fonts), defaultFont))
 					continue;
 				lineFont = mFont;
 				byte c = (byte)*s++;
@@ -2813,7 +2879,7 @@ void InsaneRebel2::renderTextOverlay(byte *renderBitmap, int pitch, int width, i
 			NutRenderer *curFont = defaultFont;
 			int curColor = 1;
 			while (s < lineEnd && (visCount + lineCharsDrawn) < displayLen) {
-				if (parseFormat(s, curFont, curColor))
+				if (parseRebel2TextOverlayFormat(s, curFont, curColor, fonts, ARRAYSIZE(fonts), defaultFont))
 					continue;
 				byte c = (byte)*s++;
 				if (c >= 'a' && c <= 'z')
@@ -3285,8 +3351,8 @@ void InsaneRebel2::renderHandler8Ship(byte *renderBitmap, int pitch, int width,
 	// (centered for Level 2/11 third-person, bottom for Level 12 FPS).
 	int16 spriteXOffset = _shipSprite->getCharXOffset(spriteIndex);
 	int16 spriteYOffset = _shipSprite->getCharYOffset(spriteIndex);
-	int drawX = displayOffsetX + spriteXOffset + _viewX;
-	int drawY = displayOffsetY + spriteYOffset + _viewY;
+	int drawX = displayOffsetX + spriteXOffset;
+	int drawY = displayOffsetY + spriteYOffset;
 
 	renderNutSprite(renderBitmap, pitch, width, height, drawX, drawY, _shipSprite, spriteIndex);
 
@@ -3297,8 +3363,8 @@ void InsaneRebel2::renderHandler8Ship(byte *renderBitmap, int pitch, int width,
 		if (shadowIndex < _shipSprite2->getNumChars()) {
 			int16 shadowXOff = _shipSprite2->getCharXOffset(shadowIndex);
 			int16 shadowYOff = _shipSprite2->getCharYOffset(shadowIndex);
-			int shadowX = displayOffsetX + shadowXOff + _viewX;
-			int shadowY = displayOffsetY + shadowYOff + _viewY;
+			int shadowX = displayOffsetX + shadowXOff;
+			int shadowY = displayOffsetY + shadowYOff;
 			renderNutSprite(renderBitmap, pitch, width, height, shadowX, shadowY, _shipSprite2, shadowIndex);
 		}
 	}
@@ -3307,7 +3373,7 @@ void InsaneRebel2::renderHandler8Ship(byte *renderBitmap, int pitch, int width,
 	int sprH = _shipSprite->getCharHeight(spriteIndex);
 	debug("Rebel2 Handler8: Ship at (%d,%d) raw(%d,%d) offset(%d,%d) nutOff(%d,%d) size(%d,%d) bottom=%d view(%d,%d) sprite=%d/%d",
 		drawX, drawY, _shipPosX, _shipPosY, displayOffsetX, displayOffsetY,
-		spriteXOffset, spriteYOffset, sprW, sprH, drawY + sprH - _viewY,
+		spriteXOffset, spriteYOffset, sprW, sprH, drawY + sprH,
 		_viewX, _viewY, spriteIndex, numSprites);
 }
 
@@ -3321,8 +3387,8 @@ void InsaneRebel2::renderVehicleShotImpacts(byte *renderBitmap, int pitch, int w
 		if (impact.counter <= 0)
 			continue;
 
-		int drawX = impact.x - _shipPosX + _viewX;
-		int drawY = impact.y - _shipPosY + _viewY;
+		int drawX = impact.x - _shipPosX;
+		int drawY = impact.y - _shipPosY;
 
 		// Original draws DAT_0047e020 repeatedly based on remaining life, then
 		// DAT_0047e018 once, both using the sampled background-mask sprite index.
@@ -3791,7 +3857,7 @@ void InsaneRebel2::renderTurretExplosions(byte *renderBitmap, int pitch, int wid
 }
 
 // renderVehicleExplosions -- Handler 8 on-foot explosion rendering (FUN_402696).
-// Position: world coords minus camera offset (DAT_0043e006/08 = _viewX/_viewY).
+// Position: world coords minus camera offset (DAT_0043e006/08 = _shipPosX/_shipPosY).
 // Scale thresholds: <11, <21. No secondary NUT.
 void InsaneRebel2::renderVehicleExplosions(byte *renderBitmap, int pitch, int width, int height) {
 	if (!_smush_iconsNut)
@@ -3802,8 +3868,8 @@ void InsaneRebel2::renderVehicleExplosions(byte *renderBitmap, int pitch, int wi
 			continue;
 
 		// FUN_402696 line 22-23: screenX = worldX - DAT_0043e006, screenY = worldY - DAT_0043e008
-		int screenX = _explosions[i].x - _viewX;
-		int screenY = _explosions[i].y - _viewY;
+		int screenX = _explosions[i].x - _shipPosX;
+		int screenY = _explosions[i].y - _shipPosY;
 		renderExplosionFrame(renderBitmap, pitch, width, height, _explosions[i],
 			screenX, screenY, kExplosionAdvanceAfterDraw);
 	}
@@ -4060,8 +4126,8 @@ void InsaneRebel2::renderVehicleLaserShots(byte *renderBitmap, int pitch, int wi
 		// From FUN_402ED0: widthScale=0x14(20), heightScale=8, thickness=4
 		// Parameters: gunX, gunY -> shipScreenX, shipScreenY (NOT the stored target!)
 		drawLaserBeam(renderBitmap, pitch, width, height,
-			gunX + _viewX, gunY + _viewY,
-			shipScreenX + _viewX, shipScreenY + _viewY,
+			gunX, gunY,
+			shipScreenX, shipScreenY,
 			progress, maxDuration, 20, 8, 4);
 
 		_vehicleShots[i].counter--;
@@ -4176,6 +4242,69 @@ void InsaneRebel2::renderHandler25LaserShots(byte *renderBitmap, int pitch, int
 	}
 }
 
+// renderHandler8PovOverlay -- Draw Level 12 POV text overlay (FUN_00401CCF).
+void InsaneRebel2::renderHandler8PovOverlay(byte *renderBitmap, int pitch, int width, int height) {
+	if (_rebelHandler != 8 || !renderBitmap || !_smush_talkfontNut || !_smush_povfontNut)
+		return;
+
+	Rebel2FontSet fontSet;
+	fontSet.numFonts = 4;
+	fontSet.defaultFont = 0;
+	fontSet.fonts[0] = _smush_talkfontNut;
+	fontSet.fonts[1] = _smush_smalfontNut ? _smush_smalfontNut : _smush_talkfontNut;
+	fontSet.fonts[2] = _smush_titlefontNut ? _smush_titlefontNut : _smush_talkfontNut;
+	fontSet.fonts[3] = _smush_povfontNut;
+
+	ScummEngine_v7 *vm = (ScummEngine_v7 *)_vm;
+
+	// Original updates DAT_0047e048 with random(5)+0x23 when random(20)==0.
+	if (getHandler8PovOverlayRandom(vm, 20) == 0)
+		_handler8HudGlyph = (char)(getHandler8PovOverlayRandom(vm, 5) + 0x23);
+
+	drawHandler8PovOverlayText(fontSet, renderBitmap, pitch, width, height,
+		getHandler8PovOverlayString(200), 10, 5, 1, kStyleAlignLeft);
+
+	char buffer[128];
+	Common::sprintf_s(buffer, "^f03&#$%c", _handler8HudGlyph);
+	drawHandler8PovOverlayText(fontSet, renderBitmap, pitch, width, height,
+		buffer, 10, 150, 248, kStyleAlignLeft);
+
+	const char *text = getHandler8PovOverlayString(203);
+	if (text) {
+		Common::sprintf_s(buffer, text,
+			(unsigned long)(uint32)(int32)getHandler8PovOverlayRandom(vm, 20000));
+		drawHandler8PovOverlayText(fontSet, renderBitmap, pitch, width, height,
+			buffer, 10, 170, 1, kStyleAlignLeft);
+	}
+
+	text = getHandler8PovOverlayString(202);
+	if (text) {
+		Common::sprintf_s(buffer, text,
+			(unsigned long)(uint32)(int32)getHandler8PovOverlayRandom(vm, -0xd50));
+		drawHandler8PovOverlayText(fontSet, renderBitmap, pitch, width, height,
+			buffer, 220, 160, 1, kStyleAlignLeft);
+	}
+
+	text = getHandler8PovOverlayString(204);
+	if (text) {
+		Common::sprintf_s(buffer, text, (unsigned long)(uint32)(int32)(int16)_shipPosX,
+			(unsigned long)(uint32)(int32)(int16)_shipPosY);
+		drawHandler8PovOverlayText(fontSet, renderBitmap, pitch, width, height,
+			buffer, 220, 170, 1, kStyleAlignLeft);
+	}
+
+	if (_handler8HudMessageTimer == 0) {
+		_handler8HudMessageIndex = getHandler8PovOverlayRandom(vm, 100);
+		if (_handler8HudMessageIndex < 7)
+			_handler8HudMessageTimer = 0x32;
+	} else {
+		_handler8HudMessageTimer--;
+		drawHandler8PovOverlayText(fontSet, renderBitmap, pitch, width, height,
+			getHandler8PovOverlayString(_handler8HudMessageIndex + 0xcd),
+			200, 5, 1, kStyleAlignCenter);
+	}
+}
+
 // renderCrosshair -- Draw crosshair/reticle at the current handler's aim point.
 void InsaneRebel2::renderCrosshair(byte *renderBitmap, int pitch, int width, int height) {
 	// From FUN_0040d836 (Handler 7) line 167-168: crosshair only drawn when DAT_004437c0 == 2
@@ -4202,7 +4331,10 @@ void InsaneRebel2::renderCrosshair(byte *renderBitmap, int pitch, int width, int
 		aimPos = getGameplayAimPoint();
 	}
 	Common::Point worldMousePos = aimPos;
-	if (_rebelHandler != 7) {
+	if (_rebelHandler == 8) {
+		worldMousePos.x += _shipPosX;
+		worldMousePos.y += _shipPosY;
+	} else if (_rebelHandler != 7) {
 		worldMousePos.x += _viewX;
 		worldMousePos.y += _viewY;
 	}
diff --git a/engines/scumm/smush/rebel/smush_player_ra2.cpp b/engines/scumm/smush/rebel/smush_player_ra2.cpp
index 3b0d660c7fb..2c43508df0d 100644
--- a/engines/scumm/smush/rebel/smush_player_ra2.cpp
+++ b/engines/scumm/smush/rebel/smush_player_ra2.cpp
@@ -43,11 +43,52 @@ namespace Scumm {
 
 // FUN_00423880 is initialized at startup with a 400000-byte preload buffer.
 constexpr int kRebel2LoadBufferSize = 400000;
+constexpr int kRebel2GameplaySurfaceWidth = 0x1a8;
+constexpr int kRebel2GameplaySurfaceHeight = 0x104;
 
 bool isRebel2FullFrameDeltaCodec(int codec) {
 	return codec == SMUSH_CODEC_DELTA_BLOCKS || codec == SMUSH_CODEC_DELTA_GLYPHS;
 }
 
+bool isRebel2GameplayActive(Insane *insane) {
+	if (insane == nullptr)
+		return false;
+
+	return static_cast<InsaneRebel2 *>(insane)->getHandler() != 0;
+}
+
+void smushDecodeRA2Uncompressed(byte *dst, const byte *src, int left, int top,
+		int width, int height, int dstPitch, int srcPitch, int dataSize,
+		int srcSkipX, int srcSkipY) {
+	if (dst == nullptr || src == nullptr || width <= 0 || height <= 0 ||
+			dstPitch <= 0 || srcPitch <= 0) {
+		return;
+	}
+
+	const int64 srcOffset = (int64)srcSkipY * srcPitch + srcSkipX;
+	if (srcOffset < 0 || (dataSize > 0 && srcOffset >= dataSize))
+		return;
+
+	if (dataSize > 0) {
+		const int64 bytesAfterOffset = (int64)dataSize - srcOffset;
+		if (bytesAfterOffset < width)
+			return;
+
+		const int maxRows = (int)((bytesAfterOffset - width) / srcPitch) + 1;
+		height = MIN(height, maxRows);
+		if (height <= 0)
+			return;
+	}
+
+	src += srcOffset;
+	dst += top * dstPitch + left;
+	while (height-- > 0) {
+		memcpy(dst, src, width);
+		src += srcPitch;
+		dst += dstPitch;
+	}
+}
+
 // ---------------------------------------------------------------------------
 // SmushPlayerRebel2 ??? construction / destruction
 // ---------------------------------------------------------------------------
@@ -93,7 +134,9 @@ void SmushPlayerRebel2::initGamePlayerFields() {
 	_loadReadOffset = 8;  // Original starts reading at offset 8 (skips header)
 	_lastLoadChunkIdx = -1;
 	_loadStreamId = 0;
+	_ra2FrameSourceSkipX = 0;
 	_ra2FrameSourceSkipY = 0;
+	_ra2FrameObjectOriginalWidth = 0;
 	_ra2FrameObjectSurfaceWidth = 0;
 	_ra2FrameObjectSurfaceHeight = 0;
 	_ra2PendingAnimHeaderPalette = false;
@@ -670,6 +713,7 @@ void SmushPlayerRebel2::ra2HandleTextResource(const char *str, int fontId, int c
  * Returns true when the dimensions are valid and updates _dst, _width, _height as needed.
  */
 void SmushPlayerRebel2::ra2PrepareFrameObjectSurface(int left, int top, int width, int height) {
+	_ra2FrameObjectOriginalWidth = width;
 	_ra2FrameObjectSurfaceWidth = width;
 	_ra2FrameObjectSurfaceHeight = height;
 
@@ -682,15 +726,23 @@ void SmushPlayerRebel2::ra2PrepareFrameObjectSurface(int left, int top, int widt
 }
 
 bool SmushPlayerRebel2::ra2SelectFrameBuffer(int codec, int width, int height) {
-	// Rebel2 uses a special buffer for all non-matching frames.
-	// Oversized full-frame delta codecs decode into a tightly packed surface
-	// with their own width as pitch. Other codecs honor left/top and pitch.
+	// Rebel2 allocates the low-res gameplay target as 424x260 (FUN_00424730).
+	// Use that target once an oversized gameplay FOBJ appears, then keep small
+	// overlay FOBJ chunks compositing into it. Pure 320x200 gameplay videos keep
+	// drawing their small FOBJ chunks directly onto the main screen.
 	const int screenSize = _vm->_screenWidth * _vm->_screenHeight;
 	const int64 fobjSize64 = (int64)width * height;
 	int surfaceWidth = width;
 	int surfaceHeight = height;
 
-	if (fobjSize64 > screenSize && !isRebel2FullFrameDeltaCodec(codec)) {
+	const bool useGameplaySurface = isRebel2GameplayActive(_insane) &&
+		!isRebel2FullFrameDeltaCodec(codec) &&
+		(fobjSize64 > screenSize || _specialBuffer != nullptr);
+
+	if (useGameplaySurface) {
+		surfaceWidth = kRebel2GameplaySurfaceWidth;
+		surfaceHeight = kRebel2GameplaySurfaceHeight;
+	} else if (fobjSize64 > screenSize && !isRebel2FullFrameDeltaCodec(codec)) {
 		surfaceWidth = MAX(surfaceWidth, _ra2FrameObjectSurfaceWidth);
 		surfaceHeight = MAX(surfaceHeight, _ra2FrameObjectSurfaceHeight);
 	}
@@ -752,6 +804,12 @@ bool SmushPlayerRebel2::ra2SelectFrameBuffer(int codec, int width, int height) {
 bool SmushPlayerRebel2::ra2DecodeCodec(int codec, const uint8 *src, int left, int top,
 								 int width, int height, int pitch, int dataSize) {
 	switch (codec) {
+	case SMUSH_CODEC_UNCOMPRESSED: {
+		const int sourcePitch = (_ra2FrameObjectOriginalWidth > 0) ? _ra2FrameObjectOriginalWidth : width;
+		smushDecodeRA2Uncompressed(_dst, src, left, top, width, height, pitch, sourcePitch,
+			dataSize, _ra2FrameSourceSkipX, _ra2FrameSourceSkipY);
+		return true;
+	}
 	case SMUSH_CODEC_LINE_UPDATE:
 	case SMUSH_CODEC_LINE_UPDATE2: {
 		const uint8 *adjustedSrc = smushSkipRLELines(src, dataSize, _ra2FrameSourceSkipY);
@@ -882,13 +940,23 @@ bool SmushPlayerRebel2::handleGameDimensionOverride(int codec, int width, int he
 
 bool SmushPlayerRebel2::handleGameAdjustCoords(int codec, int &left, int &top, int &width, int &height, int pitch, int *srcSkipY) {
 	int sourceSkipY = 0;
+	const int adjustedLeft = left + _fobjOffsetX;
+	_ra2FrameSourceSkipX = (adjustedLeft < 0) ? -adjustedLeft : 0;
 	_ra2FrameSourceSkipY = 0;
 	adjustFrameCoords(left, top, width, height, pitch, &sourceSkipY);
 	if (codec == SMUSH_CODEC_LINE_UPDATE || codec == SMUSH_CODEC_LINE_UPDATE2 ||
-			codec == SMUSH_CODEC_SKIP_RLE || codec == SMUSH_CODEC_RA2_BOMP) {
+			codec == SMUSH_CODEC_SKIP_RLE || codec == SMUSH_CODEC_RA2_BOMP ||
+			codec == SMUSH_CODEC_UNCOMPRESSED) {
 		_ra2FrameSourceSkipY = sourceSkipY;
 		if (srcSkipY)
 			*srcSkipY = 0;
+	} else if (isRebel2FullFrameDeltaCodec(codec)) {
+		// Codec 37/47 streams are full-frame delta streams, not row-prefixed
+		// RLE data. The original FOBJ dispatcher applies FUN_00424510 offsets
+		// to the destination coordinates passed to FUN_0042cba0; it does not
+		// advance the compressed source by clipped scanlines.
+		if (srcSkipY)
+			*srcSkipY = 0;
 	} else if (srcSkipY) {
 		*srcSkipY = sourceSkipY;
 	}
diff --git a/engines/scumm/smush/rebel/smush_player_ra2.h b/engines/scumm/smush/rebel/smush_player_ra2.h
index 35af253e0f6..87c64a12431 100644
--- a/engines/scumm/smush/rebel/smush_player_ra2.h
+++ b/engines/scumm/smush/rebel/smush_player_ra2.h
@@ -83,7 +83,9 @@ private:
 	int32 _loadReadOffset;
 	int16 _lastLoadChunkIdx;
 	int16 _loadStreamId;
+	int _ra2FrameSourceSkipX;
 	int _ra2FrameSourceSkipY;
+	int _ra2FrameObjectOriginalWidth;
 	int _ra2FrameObjectSurfaceWidth;
 	int _ra2FrameObjectSurfaceHeight;
 	bool _ra2PendingAnimHeaderPalette;


Commit: e0985e843eeb5e4e9cb075300c5f71036fc754f8
    https://github.com/scummvm/scummvm/commit/e0985e843eeb5e4e9cb075300c5f71036fc754f8
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-03T19:37:44+02:00

Commit Message:
SCUMM: RA2: removed some useless statics

Changed paths:
    engines/scumm/insane/rebel2/render.cpp


diff --git a/engines/scumm/insane/rebel2/render.cpp b/engines/scumm/insane/rebel2/render.cpp
index 9bdb7e7583a..5eae883192b 100644
--- a/engines/scumm/insane/rebel2/render.cpp
+++ b/engines/scumm/insane/rebel2/render.cpp
@@ -63,7 +63,7 @@ static int countEmbeddedFramePixels(const InsaneRebel2::EmbeddedSanFrame &frame)
 	return count;
 }
 
-static bool parseRebel2TextOverlayFormat(const char *&str, NutRenderer *&curFont, int &curColor,
+bool parseRebel2TextOverlayFormat(const char *&str, NutRenderer *&curFont, int &curColor,
 		NutRenderer **fonts, int numFonts, NutRenderer *defaultFont) {
 	int fontId = InsaneRebel2::parseFormatCode(str, curColor);
 	if (fontId >= 0) {
@@ -74,7 +74,7 @@ static bool parseRebel2TextOverlayFormat(const char *&str, NutRenderer *&curFont
 	return fontId == -2;
 }
 
-static const char *getHandler8PovOverlayString(int id) {
+const char *getHandler8PovOverlayString(int id) {
 	switch (id) {
 	case 200:
 		return "^f03^c248 MODE  ^c005 INFARED";
@@ -105,7 +105,7 @@ static const char *getHandler8PovOverlayString(int id) {
 	}
 }
 
-static int getHandler8PovOverlayRandom(ScummEngine_v7 *vm, int max) {
+int getHandler8PovOverlayRandom(ScummEngine_v7 *vm, int max) {
 	if (max == 0)
 		return 0;
 	if (max < 0)
@@ -113,7 +113,7 @@ static int getHandler8PovOverlayRandom(ScummEngine_v7 *vm, int max) {
 	return vm->_rnd.getRandomNumber(max - 1);
 }
 
-static void drawHandler8PovOverlayText(const Rebel2FontSet &fontSet, byte *renderBitmap,
+void drawHandler8PovOverlayText(const Rebel2FontSet &fontSet, byte *renderBitmap,
 		int pitch, int width, int height, const char *str, int x, int y, int16 color,
 		TextStyleFlags flags) {
 	if (!str)



From noreply at scummvm.org  Wed Jun  3 19:38:20 2026
From: noreply at scummvm.org (neuromancer)
Date: Wed,  3 Jun 2026 19:38:20 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 7bc544c5b6b15ec14cebf60328a7a31d2f916ab7
Message-ID: <20260603193821.0383C7005BC@ron.scummvm.net>

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

Summary:
7bc544c5b6 SCUMM: RA2: implemented special monitor effect in level 12


Commit: 7bc544c5b6b15ec14cebf60328a7a31d2f916ab7
    https://github.com/scummvm/scummvm/commit/7bc544c5b6b15ec14cebf60328a7a31d2f916ab7
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-03T21:37:41+02:00

Commit Message:
SCUMM: RA2: implemented special monitor effect in level 12

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


diff --git a/engines/scumm/insane/rebel2/rebel.h b/engines/scumm/insane/rebel2/rebel.h
index 645596603c4..913e4465656 100644
--- a/engines/scumm/insane/rebel2/rebel.h
+++ b/engines/scumm/insane/rebel2/rebel.h
@@ -613,6 +613,7 @@ public:
 
 	// Update target lock state and draw crosshair/reticle
 	void renderCrosshair(byte *renderBitmap, int pitch, int width, int height);
+	void renderHandler8MonitorEffect(byte *renderBitmap, int pitch, int width, int height);
 	void renderHandler8PovOverlay(byte *renderBitmap, int pitch, int width, int height);
 	Common::Point getHandler7ShipDrawPoint();
 	Common::Point getHandler7ProjectedPoint();
diff --git a/engines/scumm/insane/rebel2/render.cpp b/engines/scumm/insane/rebel2/render.cpp
index 5eae883192b..1f821621e41 100644
--- a/engines/scumm/insane/rebel2/render.cpp
+++ b/engines/scumm/insane/rebel2/render.cpp
@@ -2599,6 +2599,9 @@ void InsaneRebel2::renderGameplayPostFrame(byte *renderBitmap, int pitch, int wi
 	// Crosshair/reticle (FUN_004089ab, FUN_0040d836).
 	renderCrosshair(renderBitmap, pitch, width, height);
 
+	// Handler 8 monitor scanline effect (FUN_0041C6EC/FUN_0041C6C3).
+	renderHandler8MonitorEffect(renderBitmap, pitch, width, height);
+
 	// Handler 8 POV text overlay (FUN_00401CCF).
 	renderHandler8PovOverlay(renderBitmap, pitch, width, height);
 
@@ -4242,6 +4245,40 @@ void InsaneRebel2::renderHandler25LaserShots(byte *renderBitmap, int pitch, int
 	}
 }
 
+// renderHandler8MonitorEffect -- Level 12 POV monitor scanline effect.
+void InsaneRebel2::renderHandler8MonitorEffect(byte *renderBitmap, int pitch, int width, int height) {
+	if (_rebelHandler != 8 || !renderBitmap)
+		return;
+
+	const bool highRes = (width >= 640 || height >= 400);
+	const int effectWidth = MIN<int>(MIN<int>(width, pitch), highRes ? 640 : 320);
+	const int effectHeight = MIN<int>(height, highRes ? 360 : 180);
+	if (effectWidth <= 0 || effectHeight <= 1)
+		return;
+
+	if (highRes) {
+		// FUN_0041C6C3/FUN_00413EC2: fill every other gameplay row with color 4.
+		for (int y = 1; y < effectHeight; y += 2) {
+			byte *row = renderBitmap + y * pitch;
+			memset(row, 4, effectWidth);
+		}
+		return;
+	}
+
+	if (_rebelDetailMode <= 0)
+		return;
+
+	// FUN_0041C6EC/FUN_00413EFC: remap every other gameplay row through
+	// FUN_00410721()+0x400. ScummVM uses the primary edge table, matching
+	// DAT_0047a81c == 0.
+	const byte *monitorTable = _edgeTable + 0x400;
+	for (int y = 1; y < effectHeight; y += 2) {
+		byte *row = renderBitmap + y * pitch;
+		for (int x = 0; x < effectWidth; x++)
+			row[x] = monitorTable[row[x]];
+	}
+}
+
 // renderHandler8PovOverlay -- Draw Level 12 POV text overlay (FUN_00401CCF).
 void InsaneRebel2::renderHandler8PovOverlay(byte *renderBitmap, int pitch, int width, int height) {
 	if (_rebelHandler != 8 || !renderBitmap || !_smush_talkfontNut || !_smush_povfontNut)



From noreply at scummvm.org  Wed Jun  3 21:12:28 2026
From: noreply at scummvm.org (sev-)
Date: Wed,  3 Jun 2026 21:12:28 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 d33776efd2c1c24223211398d1dcc11cbc27d926
Message-ID: <20260603211228.C22937005BC@ron.scummvm.net>

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:
922426f320 DIRECTOR: DT: Cast window now loads shared cast
0ea5066c06 DIRECTOR: DT: fix crash and add script viewing from cast details
d33776efd2 DIRECTOR: DT: fix null dereferences, OOB accesses, and memory leaks


Commit: 922426f320ab585c04a890dd0869757cb3bc9818
    https://github.com/scummvm/scummvm/commit/922426f320ab585c04a890dd0869757cb3bc9818
Author: ramyak-sharma (ramyaksharma1 at gmail.com)
Date: 2026-06-03T23:12:22+02:00

Commit Message:
DIRECTOR: DT: Cast window now loads shared cast

Changed paths:
    engines/director/debugger/dt-cast.cpp


diff --git a/engines/director/debugger/dt-cast.cpp b/engines/director/debugger/dt-cast.cpp
index 9994701e4cd..029bb37b033 100644
--- a/engines/director/debugger/dt-cast.cpp
+++ b/engines/director/debugger/dt-cast.cpp
@@ -109,6 +109,93 @@ Common::String getDisplayName(CastMember *castMember) {
 	return Common::String::format("%u", castMember->getID());
 }
 
+void drawCastRow(Cast* cast) {
+	assert(cast);
+	for (auto castMember : *cast->_loadedCast) {
+		castMember._value->load();
+
+		Common::String name(getDisplayName(castMember._value));
+		if (!_state->_cast._nameFilter.PassFilter(name.c_str()))
+			continue;
+		if ((castMember._value->_type != kCastTypeAny) &&
+				!(_state->_cast._typeFilter & (1 << (int)castMember._value->_type)))
+			continue;
+
+		ImGui::TableNextRow();
+
+		// Make the entire row selectable/clickable
+		ImGui::TableSetColumnIndex(0);
+		if (ImGui::Selectable(
+			Common::String::format("##row%d", castMember._key).c_str(),
+			false,
+			ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowOverlap,
+			ImVec2(0, 32.f) // match row height
+		)) {
+			castMember._value->load();
+			_state->_castDetails._castMember = castMember._value;
+			_state->_w.castDetails = true;
+		}
+		ImGui::SameLine();
+
+		ImGui::Text("%s %s", toIcon(castMember._value->_type), name.c_str());
+
+		ImGui::TableNextColumn();
+		ImGui::Text("%d", castMember._key);
+
+		ImGui::TableNextColumn();
+		if (castMember._value->_type == CastType::kCastLingoScript) {
+			ScriptCastMember *scriptMember = (ScriptCastMember *)castMember._value;
+			ImGui::Text("%s", toString(scriptMember->_scriptType));
+		}
+		ImGui::TableNextColumn();
+		ImGui::Text("%s", toString(castMember._value->_type));
+
+		ImGui::TableNextColumn();
+		float columnWidth = ImGui::GetColumnWidth();
+
+		ImGuiImage imgID = {};
+		switch (castMember._value->_type) {
+		case kCastBitmap:
+			{
+				imgID = getImageID(castMember._value);
+				if (imgID.id) {
+					float offsetX = (columnWidth - 32.f) * 0.5f;
+					ImGui::SetCursorPosX(ImGui::GetCursorPosX() + offsetX);
+					showImage(imgID, name.c_str(), 32.f);
+				}
+			}
+			break;
+
+		case kCastText:
+		case kCastRichText:
+		case kCastButton:
+			{
+				imgID = getTextID(castMember._value);
+				if (imgID.id) {
+					float offsetX = (columnWidth - 32.f) * 0.5f;
+					ImGui::SetCursorPosX(ImGui::GetCursorPosX() + offsetX);
+					showImage(imgID, name.c_str(), 32.f);
+				}
+			}
+			break;
+
+		case kCastShape:
+			{
+				imgID = getShapeID(castMember._value);
+				if (imgID.id) {
+					float offsetX = (columnWidth - 32.f) * 0.5f;
+					ImGui::SetCursorPosX(ImGui::GetCursorPosX() + offsetX);
+					showImage(imgID, name.c_str(), 32.f);
+				}
+			}
+			break;
+		default:
+			break;
+		}
+	}
+
+}
+
 void showCast() {
 	if (!_state->_w.cast)
 		return;
@@ -164,89 +251,13 @@ void showCast() {
 					Cast *cast = it._value;
 					if (!cast->_loadedCast)
 						continue;
+					drawCastRow(cast);
+				}
 
-					for (auto castMember : *cast->_loadedCast) {
-						castMember._value->load();
-
-						Common::String name(getDisplayName(castMember._value));
-						if (!_state->_cast._nameFilter.PassFilter(name.c_str()))
-							continue;
-						if ((castMember._value->_type != kCastTypeAny) && !(_state->_cast._typeFilter & (1 << (int)castMember._value->_type)))
-							continue;
-
-						ImGui::TableNextRow();
-
-						// Make the entire row selectable/clickable
-						ImGui::TableSetColumnIndex(0);
-						if (ImGui::Selectable(
-							Common::String::format("##row%d", castMember._key).c_str(),
-							false,
-							ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowOverlap,
-							ImVec2(0, 32.f) // match row height
-						)) {
-							castMember._value->load();
-							_state->_castDetails._castMember = castMember._value;
-							_state->_w.castDetails = true;
-						}
-						ImGui::SameLine();
-
-						ImGui::Text("%s %s", toIcon(castMember._value->_type), name.c_str());
-
-						ImGui::TableNextColumn();
-						ImGui::Text("%d", castMember._key);
-
-						ImGui::TableNextColumn();
-						if (castMember._value->_type == CastType::kCastLingoScript) {
-							ScriptCastMember *scriptMember = (ScriptCastMember *)castMember._value;
-							ImGui::Text("%s", toString(scriptMember->_scriptType));
-						}
-						ImGui::TableNextColumn();
-						ImGui::Text("%s", toString(castMember._value->_type));
-
-						ImGui::TableNextColumn();
-						float columnWidth = ImGui::GetColumnWidth();
-
-						ImGuiImage imgID = {};
-						switch (castMember._value->_type) {
-						case kCastBitmap:
-							{
-								imgID = getImageID(castMember._value);
-								if (imgID.id) {
-									float offsetX = (columnWidth - 32.f) * 0.5f;
-									ImGui::SetCursorPosX(ImGui::GetCursorPosX() + offsetX);
-									showImage(imgID, name.c_str(), 32.f);
-								}
-							}
-							break;
-
-						case kCastText:
-						case kCastRichText:
-						case kCastButton:
-							{
-								imgID = getTextID(castMember._value);
-								if (imgID.id) {
-									float offsetX = (columnWidth - 32.f) * 0.5f;
-									ImGui::SetCursorPosX(ImGui::GetCursorPosX() + offsetX);
-									showImage(imgID, name.c_str(), 32.f);
-								}
-							}
-							break;
+				Cast *sharedCast = movie->getSharedCast();
+				if(sharedCast && sharedCast->_loadedCast)
+					drawCastRow(sharedCast);
 
-						case kCastShape:
-							{
-								imgID = getShapeID(castMember._value);
-								if (imgID.id) {
-									float offsetX = (columnWidth - 32.f) * 0.5f;
-									ImGui::SetCursorPosX(ImGui::GetCursorPosX() + offsetX);
-									showImage(imgID, name.c_str(), 32.f);
-								}
-							}
-							break;
-						default:
-							break;
-						}
-					}
-				}
 				ImGui::EndTable();
 			}
 		} else {


Commit: 0ea5066c062a20b52f3f3882eb42d680502548b2
    https://github.com/scummvm/scummvm/commit/0ea5066c062a20b52f3f3882eb42d680502548b2
Author: ramyak-sharma (ramyaksharma1 at gmail.com)
Date: 2026-06-03T23:12:22+02:00

Commit Message:
DIRECTOR: DT: fix crash and add script viewing from cast details

Changed paths:
    engines/director/debugger/debugtools.cpp
    engines/director/debugger/dt-castdetails.cpp


diff --git a/engines/director/debugger/debugtools.cpp b/engines/director/debugger/debugtools.cpp
index 733d3c04d9f..f9bb8840a3d 100644
--- a/engines/director/debugger/debugtools.cpp
+++ b/engines/director/debugger/debugtools.cpp
@@ -127,7 +127,7 @@ ImGuiScript toImGuiScript(ScriptType scriptType, CastMemberID id, const Common::
 
 ScriptContext *getScriptContext(CastMemberID id) {
 	const Director::Movie *movie = g_director->getCurrentMovie();;
-	const Cast *cast = movie->getCasts()->getVal(id.castLib);
+	const Cast *cast;
 
 	if (id.castLib == SHARED_CAST_LIB)
 		cast = movie->getSharedCast();
diff --git a/engines/director/debugger/dt-castdetails.cpp b/engines/director/debugger/dt-castdetails.cpp
index d275b5e460c..bd0939442bc 100644
--- a/engines/director/debugger/dt-castdetails.cpp
+++ b/engines/director/debugger/dt-castdetails.cpp
@@ -38,6 +38,7 @@
 #include "director/score.h"
 #include "director/sound.h"
 
+#include "director/movie.h"
 #include "director/types.h"
 
 namespace Director {
@@ -544,10 +545,16 @@ void drawBaseCMprops(CastMember *member) {
 				ImGui::Text("scriptText");
 				ImGui::TableSetColumnIndex(1);
 				if (info && !info->script.empty()) {
-					ImGui::TextColored(ImVec4(0.7f, 0.7f, 1.0f, 1.0f), "...");
+					CastMemberID scriptCastId(member->getID(), cast->_castLibID);
+					bool hasCastScript = g_director->getCurrentMovie()->getScriptContext(kCastScript, scriptCastId) != nullptr;
+					ImGui::TextColored(hasCastScript ? ImVec4(0.4f, 0.8f, 0.4f, 1.0f) : ImVec4(0.7f, 0.7f, 1.0f, 1.0f), "...");
 					if (ImGui::IsItemHovered()) {
-						ImGui::SetTooltip("%s", info->script.c_str());
+						ImGui::SetTooltip(hasCastScript ? "%s\n\n(click to open script)" : "%s", info->script.c_str());
+						if (hasCastScript)
+							ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
 					}
+					if (hasCastScript && ImGui::IsItemClicked(0))
+						displayScriptRef(scriptCastId);
 				} else {
 					ImGui::TextColored(ImVec4(0.5f, 0.5f, 0.5f, 1.0f), "...");
 				}


Commit: d33776efd2c1c24223211398d1dcc11cbc27d926
    https://github.com/scummvm/scummvm/commit/d33776efd2c1c24223211398d1dcc11cbc27d926
Author: ramyak-sharma (ramyaksharma1 at gmail.com)
Date: 2026-06-03T23:12:22+02:00

Commit Message:
DIRECTOR: DT: fix null dereferences, OOB accesses, and memory leaks

Changed paths:
    engines/director/debugger/debugtools.cpp
    engines/director/debugger/dt-castdetails.cpp
    engines/director/debugger/dt-controlpanel.cpp
    engines/director/debugger/dt-internal.h
    engines/director/debugger/dt-save-state.cpp
    engines/director/debugger/dt-score.cpp
    engines/director/debugger/dt-script-d2.cpp
    engines/director/debugger/dt-script-d4.cpp
    engines/director/debugger/dt-scripts.cpp


diff --git a/engines/director/debugger/debugtools.cpp b/engines/director/debugger/debugtools.cpp
index f9bb8840a3d..6512c3e3eaf 100644
--- a/engines/director/debugger/debugtools.cpp
+++ b/engines/director/debugger/debugtools.cpp
@@ -82,13 +82,16 @@ const LingoDec::Handler *getHandler(CastMemberID id, const Common::String &handl
 	if (id.castLib == SHARED_CAST_LIB)
 		return getHandler(movie->getSharedCast(), id, handlerId);
 
-	const Cast *cast = movie->getCasts()->getVal(id.castLib);
+	const Cast *cast = movie->getCasts()->getValOrDefault(id.castLib, nullptr);
 
 	const LingoDec::Handler *handler = getHandler(cast, id, handlerId);
 	if (handler)
 		return handler;
 
-	return getHandler(movie->getSharedCast(), id, handlerId);
+	Cast *sharedCast = movie->getSharedCast();
+	if (!sharedCast)
+		return nullptr;
+	return getHandler(sharedCast, id, handlerId);
 }
 
 ImGuiScript toImGuiScript(ScriptType scriptType, CastMemberID id, const Common::String &handlerId) {
@@ -101,7 +104,11 @@ ImGuiScript toImGuiScript(ScriptType scriptType, CastMemberID id, const Common::
 	if (!handler) {
 		const ScriptContext *ctx;
 		if (id.castLib == SHARED_CAST_LIB) {
-			ctx = g_director->getCurrentMovie()->getSharedCast()->_lingoArchive->getScriptContext(scriptType, id.member);
+			// null guard
+			Cast *sharedCast = g_director->getCurrentMovie()->getSharedCast();
+			if (!sharedCast)
+				return result;
+			ctx = sharedCast->_lingoArchive->getScriptContext(scriptType, id.member);
 		} else {
 			ctx = g_director->getCurrentMovie()->getScriptContext(scriptType, id);
 		}
@@ -144,10 +151,17 @@ ScriptContext *getScriptContext(CastMemberID id) {
 
 ScriptContext *getScriptContext(uint32 nameIndex, CastMemberID id, Common::String handlerName) {
 	Movie *movie = g_director->getCurrentMovie();
-	Cast *cast = movie->getCasts()->getVal(id.castLib);
+	Cast *cast;
+	if (id.castLib == SHARED_CAST_LIB)
+		cast = movie->getSharedCast();
+	else
+		cast = movie->getCasts()->getValOrDefault(id.castLib, nullptr);
+
+	if (!cast)
+		return nullptr;
 
 	// If the name at nameIndex is not the same as handler name, means its a local script (in the same Lscr resource)
-	if (cast && cast->_lingoArchive->names[nameIndex] != handlerName) {
+	if (cast->_lingoArchive->names[nameIndex] != handlerName) {
 		return cast->_lingoArchive->findScriptContext(id.member);
 	}
 
@@ -299,6 +313,8 @@ static void setToolTipImage(const ImGuiImage &image, const char *name) {
 }
 
 void showImage(const ImGuiImage &image, const char *name, float thumbnailSize) {
+	if (!image.width || !image.height)
+		return;
 	ImVec2 size;
 	if (image.width > image.height) {
 		size = {thumbnailSize - 2, (thumbnailSize - 2) * image.height / image.width};
@@ -320,6 +336,8 @@ void showImage(const ImGuiImage &image, const char *name, float thumbnailSize) {
 }
 
 void showImageWrappedBorder(const ImGuiImage &image, const char *name, float imageSize) {
+	if (!image.width || !image.height)
+		return;
 	ImVec2 size;
 	if (image.width > image.height) {
 		size = {imageSize, imageSize * image.height / image.width};
@@ -424,8 +442,11 @@ ImGuiImage getTextID(CastMember *castMember) {
 	Graphics::MacWidget *widget = castMember->createWidget(bbox, channel, kTextSprite);
 	Graphics::Surface surface;
 
-	if (!widget || !widget->getSurface() || !widget->getSurface()->getPixels())
-      return {};
+	if (!widget || !widget->getSurface() || !widget->getSurface()->getPixels()) {
+		delete channel;
+		delete sprite;
+		return {};
+	}
 
 	surface.copyFrom(*widget->getSurface());
 
diff --git a/engines/director/debugger/dt-castdetails.cpp b/engines/director/debugger/dt-castdetails.cpp
index bd0939442bc..1999dddddfa 100644
--- a/engines/director/debugger/dt-castdetails.cpp
+++ b/engines/director/debugger/dt-castdetails.cpp
@@ -300,7 +300,7 @@ void showImageViewer() {
 			}
 
 			// text tab
-			if (!state->cachedNormalized.empty() && ImGui::BeginTabItem("Text")) {
+			if (!state->cachedNormalized.empty() && state->buffer && ImGui::BeginTabItem("Text")) {
 
 				ImGui::BeginChild("##textPanel", ImGui::GetContentRegionAvail(), false,
 					ImGuiWindowFlags_HorizontalScrollbar);
diff --git a/engines/director/debugger/dt-controlpanel.cpp b/engines/director/debugger/dt-controlpanel.cpp
index d0fd862aec2..094dc8d97bc 100644
--- a/engines/director/debugger/dt-controlpanel.cpp
+++ b/engines/director/debugger/dt-controlpanel.cpp
@@ -34,7 +34,7 @@ static uint32 getLineFromPC() {
 	ScriptData *scriptData = &_state->_functions._windowScriptData.getOrCreateVal(g_director->getCurrentWindow());
 
 	const uint pc = g_lingo->_state->pc;
-	if (scriptData->_scripts.empty())
+	if (scriptData->_scripts.empty() || scriptData->_current >= scriptData->_scripts.size())
 		return 0;
 	const Common::Array<uint> &offsets = scriptData->_scripts[scriptData->_current].startOffsets;
 	for (uint i = 0; i < offsets.size(); i++) {
@@ -128,7 +128,12 @@ void showControlPanel() {
 	ImGui::SetNextWindowSize(ImVec2(200, 103), ImGuiCond_FirstUseEver);
 
 	if (ImGui::Begin("Control Panel", &_state->_w.controlPanel, ImGuiWindowFlags_NoDocking)) {
+		// null guard
 		Movie *movie = g_director->getCurrentMovie();
+		if (!movie) {
+			ImGui::End();
+			return;
+		}
 		Score *score = movie->getScore();
 		ImDrawList *dl = ImGui::GetWindowDrawList();
 
diff --git a/engines/director/debugger/dt-internal.h b/engines/director/debugger/dt-internal.h
index cf5e1d0c981..998f4795286 100644
--- a/engines/director/debugger/dt-internal.h
+++ b/engines/director/debugger/dt-internal.h
@@ -224,7 +224,7 @@ typedef struct ImGuiState {
 		Common::HashMap<Window *, ScriptData> _windowScriptData;
 	} _functions;
 	struct {
-		CastMember *_castMember;
+		CastMember *_castMember = nullptr;
 		Common::HashMap<CastMember *, int> _filmLoopCurrentFrame;
 	} _castDetails;
 
diff --git a/engines/director/debugger/dt-save-state.cpp b/engines/director/debugger/dt-save-state.cpp
index 0180968d6ee..e8088fc94e0 100644
--- a/engines/director/debugger/dt-save-state.cpp
+++ b/engines/director/debugger/dt-save-state.cpp
@@ -140,6 +140,16 @@ void loadSavedState() {
 	debugC(7, kDebugImGui, "ImGui::loaded state: %s", saved->stringify(true).c_str());
 
 	// Load open/closed window flags
+	if (!saved->asObject()["Windows"] || !saved->asObject()["Window Settings"] ||
+			!saved->asObject()["Log"] || !saved->asObject()["ScoreWindow"] ||
+			!saved->asObject()["ChannelsWindow"] || !saved->asObject()["CastWindow"] ||
+			!saved->asObject()["IgnoreMouse"] || !saved->asObject()["EnableMultiViewport"]) {
+		warning("ImGui::loadSavedState(): save file is missing required fields, ignoring");
+		free(data);
+		delete saved;
+		delete savedState;
+		return;
+	}
 	int64 openFlags = saved->asObject()["Windows"]->asIntegerNumber();
 	Common::Array<WindowFlag> windows = getWindowFlags();
 
@@ -157,8 +167,8 @@ void loadSavedState() {
 	}
 
 	// Load window settings
-	const char *windowSettings = saved->asObject()["Window Settings"]->asString().c_str();
-	ImGui::LoadIniSettingsFromMemory(windowSettings);
+	Common::String windowSettingsStr = saved->asObject()["Window Settings"]->asString();
+	ImGui::LoadIniSettingsFromMemory(windowSettingsStr.c_str());
 
 	// Load the log
 	Common::JSONArray log = saved->asObject()["Log"]->asArray();
diff --git a/engines/director/debugger/dt-score.cpp b/engines/director/debugger/dt-score.cpp
index ae11f63cbc9..fcc05364f08 100644
--- a/engines/director/debugger/dt-score.cpp
+++ b/engines/director/debugger/dt-score.cpp
@@ -150,6 +150,9 @@ static void buildContinuationData(Window *window) {
 	Score *score = window->getCurrentMovie()->getScore();
 	uint numFrames = score->_scoreCache.size();
 
+	if (numFrames == 0)
+		return;
+
 	uint numChannels = score->_scoreCache[0]->_sprites.size();
 	_state->_continuationData.resize(numChannels);
 
@@ -391,7 +394,9 @@ static void drawSpriteInspector(Score *score, Cast *cast, uint numFrames) {
 		CastMember *castMember = nullptr;
 		bool shape = false;
 
-		if (_state->_selectedScoreCast.frame != -1 && !_state->_selectedScoreCast.isMainChannel)
+		if (_state->_selectedScoreCast.frame != -1 && !_state->_selectedScoreCast.isMainChannel
+				&& _state->_selectedScoreCast.frame < (int)score->_scoreCache.size()
+				&& _state->_selectedScoreCast.channel < (int)score->_scoreCache[_state->_selectedScoreCast.frame]->_sprites.size())
 			sprite = score->_scoreCache[_state->_selectedScoreCast.frame]->_sprites[_state->_selectedScoreCast.channel];
 
 		if (sprite) {
@@ -975,6 +980,8 @@ static void drawMainChannelGrid(ImDrawList *dl, ImVec2 startPos, Score *score) {
 				case kChScript: // open script in script editor
 					if (mc.actionId.member) {
 						ScriptContext *ctx = getScriptContext(mc.actionId);
+						if (!ctx)
+							break;
 						for (auto &handler : ctx->_functionHandlers) {
 							ImGuiScript script = toImGuiScript(kScoreScript, mc.actionId, handler._key);
 							script.byteOffsets = ctx->_functionByteOffsets[script.handlerId];
@@ -1138,6 +1145,11 @@ void showScore() {
 
 		buildContinuationData(selectedWindow);
 
+		if (!selectedWindow->getCurrentMovie()) {
+			ImGui::Text("No movie loaded");
+			ImGui::End();
+			return;
+		}
 		Score *score = selectedWindow->getCurrentMovie()->getScore();
 		uint numFrames = score->_scoreCache.size();
 		Cast *cast = selectedWindow->getCurrentMovie()->getCast();
@@ -1191,6 +1203,11 @@ void showChannels() {
 	if (ImGui::Begin("Channels", &_state->_w.channels)) {
 		Window *selectedWindow = windowListCombo(&_state->_scoreWindow);
 
+		if (!selectedWindow->getCurrentMovie()) {
+			ImGui::Text("No movie loaded");
+			ImGui::End();
+			return;
+		}
 		Score *score = selectedWindow->getCurrentMovie()->getScore();
 		const Frame &frame = *score->_currentFrame;
 
@@ -1410,8 +1427,10 @@ void showChannels() {
 						if (sprite._spriteListIdx) {
 							Common::MemoryReadStreamEndian *stream = score->getSpriteDetailsStream(sprite._spriteListIdx + 2);
 							Common::String name;
-							if (stream)
+							if (stream) {
 								name = stream->readPascalString();
+								delete stream;
+							}
 							ImGui::Text("%s", name.c_str());
 						} else {
 							ImGui::Text(" ");
diff --git a/engines/director/debugger/dt-script-d2.cpp b/engines/director/debugger/dt-script-d2.cpp
index 2e07fb5c042..6e732985fde 100644
--- a/engines/director/debugger/dt-script-d2.cpp
+++ b/engines/director/debugger/dt-script-d2.cpp
@@ -48,7 +48,8 @@ public:
 		Common::Array<CFrame *> &callstack = g_lingo->_state->callstack;
 		if (!callstack.empty()) {
 			CFrame *head = callstack[callstack.size() - 1];
-			_isScriptInDebug = (head->sp.ctx->_id == script.id.member) && (*head->sp.name == script.handlerId);
+			if (head->sp.ctx)
+				_isScriptInDebug = (head->sp.ctx->_id == script.id.member) && (*head->sp.name == script.handlerId);
 		}
 		_script.startOffsets.clear();
 	}
diff --git a/engines/director/debugger/dt-script-d4.cpp b/engines/director/debugger/dt-script-d4.cpp
index 5d7cc2d1627..e47661be2a5 100644
--- a/engines/director/debugger/dt-script-d4.cpp
+++ b/engines/director/debugger/dt-script-d4.cpp
@@ -41,7 +41,8 @@ public:
 		Common::Array<CFrame *> &callstack = g_lingo->_state->callstack;
 		if (!callstack.empty()) {
 			CFrame *head = callstack[callstack.size() - 1];
-			_isScriptInDebug = (head->sp.ctx->_id == script.id.member) && (*head->sp.name == script.handlerId);
+			if (head->sp.ctx)
+				_isScriptInDebug = (head->sp.ctx->_id == script.id.member) && (*head->sp.name == script.handlerId);
 		}
 		_script.startOffsets.clear();
 	}
@@ -1128,6 +1129,8 @@ private:
 
 	void renderLine(uint p) {
 		bool showCurrentStatement = false;
+		if (_script.byteOffsets.empty())
+			return;
 		p = MIN(p, _script.byteOffsets.size() - 1);
 		uint pc = _script.byteOffsets[p];
 		_script.startOffsets.push_back(pc);
diff --git a/engines/director/debugger/dt-scripts.cpp b/engines/director/debugger/dt-scripts.cpp
index 1690e10f034..402f96376cf 100644
--- a/engines/director/debugger/dt-scripts.cpp
+++ b/engines/director/debugger/dt-scripts.cpp
@@ -130,7 +130,8 @@ static void renderCallStack(uint pc) {
 
 		if (frame->sp.type != VOIDSYM) {
 			stackFrame = Common::String::format("#%d ", i);
-			stackFrame += Common::String::format("%d ", frame->sp.ctx->_scriptId);
+			if (frame->sp.ctx)
+				stackFrame += Common::String::format("%d ", frame->sp.ctx->_scriptId);
 
 			if (frame->sp.ctx && frame->sp.ctx->_id != -1) {
 				stackFrame += Common::String::format("(%d): ", frame->sp.ctx->_id);
@@ -155,6 +156,8 @@ static void renderCallStack(uint pc) {
 		if (ImGui::Selectable(stackFrame.c_str())) {
 			CFrame *head = callstack[callstack.size() - i - 1];
 			ScriptContext *scriptContext = head->sp.ctx;
+			if (!scriptContext || !movie->getCast())
+				continue;
 			int castLibID = movie->getCast()->_castLibID;
 			int castId = head->sp.ctx->_id;
 			bool childScript = false;
@@ -183,13 +186,13 @@ static bool showScriptCast(CastMemberID &id) {
 	bool closed = true;
 
 	if (ImGui::Begin(wName.c_str(), &closed)) {
-		Cast *cast = g_director->getCurrentMovie()->getCasts()->getVal(id.castLib);
+		Cast *cast = g_director->getCurrentMovie()->getCasts()->getValOrDefault(id.castLib, nullptr);
 		ScriptContext *ctx = g_director->getCurrentMovie()->getScriptContext(kScoreScript, id);
 
 		if (ctx) {
 			for (auto &handler : ctx->_functionHandlers)
 				renderCastScript(handler._value);
-		} else if (cast->_lingoArchive->factoryContexts.contains(id.member)) {
+		} else if (cast && cast->_lingoArchive->factoryContexts.contains(id.member)) {
 			for (auto &it : *cast->_lingoArchive->factoryContexts.getVal(id.member)) {
 				for (auto &handler : it._value->_functionHandlers)
 					renderCastScript(handler._value);
@@ -272,11 +275,13 @@ void showHandlers() {
 		return;
 	}
 
+	Common::Array<uint32> toClose;
 	for (auto handler : _state->_openHandlers) {
-		if (!showHandler(handler._value)) {
-			_state->_openHandlers.erase(handler._value.id.member);
-		}
+		if (!showHandler(handler._value))
+			toClose.push_back(handler._value.id.member);
 	}
+	for (uint32 key : toClose)
+		_state->_openHandlers.erase(key);
 }
 
 static void updateCurrentScript() {
@@ -291,6 +296,8 @@ static void updateCurrentScript() {
 	CFrame *head = callstack[callstack.size() - 1];
 	const Director::Movie *movie = g_director->getCurrentMovie();
 	ScriptContext *scriptContext = head->sp.ctx;
+	if (!scriptContext || !movie->getCast())
+		return;
 	int castLibID = movie->getCast()->_castLibID;
 	int castId = head->sp.ctx->_id;
 	bool childScript = false;
@@ -573,7 +580,7 @@ void showExecutionContext() {
 		ScriptData *scriptData = &_state->_functions._windowScriptData.getOrCreateVal(stage);
 		updateCurrentScript();
 
-		if (scriptData->_showScript) {
+		if (scriptData->_showScript && !scriptData->_scripts.empty() && scriptData->_current < scriptData->_scripts.size()) {
 			// disable highlighting while rendering scripts in Execution Context
 			bool oldSuppress = _state->_dbg._suppressHighlight;
 			_state->_dbg._suppressHighlight = true;
@@ -693,7 +700,7 @@ void showExecutionContext() {
 			scriptData = &_state->_functions._windowScriptData.getOrCreateVal(window);
 			updateCurrentScript();
 
-			if (scriptData->_showScript) {
+			if (scriptData->_showScript && !scriptData->_scripts.empty() && scriptData->_current < scriptData->_scripts.size()) {
 				bool oldSuppress = _state->_dbg._suppressHighlight;
 				_state->_dbg._suppressHighlight = true;
 				ImGuiScript &current = scriptData->_scripts[scriptData->_current];



From noreply at scummvm.org  Thu Jun  4 00:09:31 2026
From: noreply at scummvm.org (bluegr)
Date: Thu,  4 Jun 2026 00:09:31 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 f82acefa44e55675ad03b153eb0b2331f4ef7069
Message-ID: <20260604000931.26E14700573@ron.scummvm.net>

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

Summary:
48a4be0a38 NANCY: Update some action records for Nancy10+
a16aefca4e NANCY: Implement MagnetMazePuzzle for Nancy10
48cb27af63 NANCY: Implement taskbar button notifications for Nancy10+
cbd22cfc0a NANCY: Implement the rest of the cellphone functionality for Nancy10+
650c5cf32a NANCY: Add new MagnetMazePuzzle to module.mk
dfc12fce98 NANCY: More work on the Nancy10+ textbox
5d21ea0320 NANCY: Remove superfluous parentheses
1a68dd3f18 NANCY: Correct reading of TBOX chunk data for Nancy10+
f82acefa44 NANCY: Update inactive animated overlays


Commit: 48a4be0a388c520fc65d3256aef333d14837ee16
    https://github.com/scummvm/scummvm/commit/48a4be0a388c520fc65d3256aef333d14837ee16
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-06-04T03:08:55+03:00

Commit Message:
NANCY: Update some action records for Nancy10+

Changed paths:
    engines/nancy/action/arfactory.cpp


diff --git a/engines/nancy/action/arfactory.cpp b/engines/nancy/action/arfactory.cpp
index 849380711b4..26b25508099 100644
--- a/engines/nancy/action/arfactory.cpp
+++ b/engines/nancy/action/arfactory.cpp
@@ -104,7 +104,7 @@ ActionRecord *ActionManager::createActionRecord(uint16 type, Common::SeekableRea
 		if (g_nancy->getGameType() <= kGameTypeNancy9)
 			return new HotMultiframeMultiSceneChange();
 		else
-			return new Hot1FrSceneChange(CursorManager::kExit); // TODO: cursor
+			return new Hot1FrSceneChange(CursorManager::kHotspot);
 	case 14:
 		return new Hot1FrSceneChange(CursorManager::kExit);
 	case 15:
@@ -241,7 +241,10 @@ ActionRecord *ActionManager::createActionRecord(uint16 type, Common::SeekableRea
 	case 66:
 		return new TableIndexPlaySound();
 	case 67:
-		return new TableIndexSetValueHS();
+		if (g_nancy->getGameType() >= kGameTypeNancy10)
+			return new Autotext();		// Moved from 61 in Nancy 10
+		else
+			return new TableIndexSetValueHS();
 	case 68:
 		return new TextScroll(false);
 	case 70:


Commit: a16aefca4ebc6d1a4e2d2a46a3a12dac6be49b1e
    https://github.com/scummvm/scummvm/commit/a16aefca4ebc6d1a4e2d2a46a3a12dac6be49b1e
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-06-04T03:08:59+03:00

Commit Message:
NANCY: Implement MagnetMazePuzzle for Nancy10

This is a puzzle with magnets, where the player needs to move four
metal pieces within a maze into its center

Changed paths:
  A engines/nancy/action/puzzle/magnetmazepuzzle.cpp
  A engines/nancy/action/puzzle/magnetmazepuzzle.h
    engines/nancy/action/arfactory.cpp
    engines/nancy/puzzledata.cpp
    engines/nancy/puzzledata.h


diff --git a/engines/nancy/action/arfactory.cpp b/engines/nancy/action/arfactory.cpp
index 26b25508099..ac65b5f58fa 100644
--- a/engines/nancy/action/arfactory.cpp
+++ b/engines/nancy/action/arfactory.cpp
@@ -47,6 +47,7 @@
 #include "engines/nancy/action/puzzle/matchpuzzle.h"
 #include "engines/nancy/action/puzzle/hamradiopuzzle.h"
 #include "engines/nancy/action/puzzle/leverpuzzle.h"
+#include "engines/nancy/action/puzzle/magnetmazepuzzle.h"
 #include "engines/nancy/action/puzzle/mazechasepuzzle.h"
 #include "engines/nancy/action/puzzle/memorypuzzle.h"
 #include "engines/nancy/action/puzzle/mouselightpuzzle.h"
@@ -472,7 +473,7 @@ ActionRecord *ActionManager::createActionRecord(uint16 type, Common::SeekableRea
 	case 241:
 		return new DotConnectPuzzle();
 	case 242:
-		// return new MagnetMazePuzzle();
+		return new MagnetMazePuzzle();
 	case 243:
 		return new BeadPuzzle();
 	case 244:
diff --git a/engines/nancy/action/puzzle/magnetmazepuzzle.cpp b/engines/nancy/action/puzzle/magnetmazepuzzle.cpp
new file mode 100644
index 00000000000..710eb2f5263
--- /dev/null
+++ b/engines/nancy/action/puzzle/magnetmazepuzzle.cpp
@@ -0,0 +1,420 @@
+/* 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 "engines/nancy/nancy.h"
+#include "engines/nancy/graphics.h"
+#include "engines/nancy/resource.h"
+#include "engines/nancy/sound.h"
+#include "engines/nancy/input.h"
+#include "engines/nancy/util.h"
+
+#include "engines/nancy/state/scene.h"
+#include "engines/nancy/puzzledata.h"
+#include "engines/nancy/action/puzzle/magnetmazepuzzle.h"
+
+namespace Nancy {
+namespace Action {
+
+void MagnetMazePuzzle::readData(Common::SeekableReadStream &stream) {
+	const int64 start = stream.pos();
+
+	readFilename(stream, _boardImageName);
+	readFilename(stream, _mazeImageName);
+
+	for (int i = 0; i < kNumOverlays; ++i)
+		readRect(stream, _overlaySrcRects[i]);
+	for (int i = 0; i < kNumOverlays; ++i)
+		readRect(stream, _overlayDestRects[i]);
+
+	_overlayTransparent = (stream.readByte() != 0);
+	_hideOverlays       = (stream.readByte() != 0);
+
+	_requiredItem = stream.readSint16LE();
+	stream.skip(1); // hit-test mode flag (unused in this port)
+
+	for (int i = 0; i < kNumMagnets; ++i)
+		readRect(stream, _magnetSrcRects[i]);
+	for (int i = 0; i < kNumMagnets; ++i)
+		readRect(stream, _magnetHomeRects[i]);
+	for (int i = 0; i < kNumMagnets; ++i)
+		readRect(stream, _magnetTargetRects[i]);
+
+	// CUIButton block at AR+0x1c7 (239 bytes). Internal layout: 15-byte
+	// shared-art name, 4 ?? 16-byte state src rects at +0x46, hot rect at +0x96,
+	// click sound at +0xbe. Only the hot rect is needed for hit-testing.
+	stream.seek(start + 0x1c7 + 0x96);
+	readRect(stream, _resetButtonHotspot);
+
+	// Skip the decorative highlight RGB; read the wall RGB used for collision.
+	stream.seek(start + 0x2b9);
+	_wallColorR = stream.readByte();
+	_wallColorG = stream.readByte();
+	_wallColorB = stream.readByte();
+
+	stream.seek(start + 0x2bc);
+
+	_pickupSound.readNormal(stream);
+	stream.seek(start + 0x372);
+	_placeSound.readNormal(stream);
+	stream.seek(start + 0x428);
+	_resetSound.readNormal(stream);
+	stream.seek(start + 0x459);
+	_bumpSound.readNormal(stream);
+
+	stream.seek(start + 0x48a);
+	_winScene.readData(stream);
+	stream.seek(start + 0x4a0);
+	_winFlag.label = stream.readSint16LE();
+	_winFlag.flag  = stream.readByte();
+	_winDelaySec   = stream.readUint16LE();
+	_winSound.readNormal(stream);
+
+	stream.seek(start + 0x4d6);
+	_cancelScene.readData(stream);
+	stream.seek(start + 0x4ec);
+	_cancelFlag.label = stream.readSint16LE();
+	_cancelFlag.flag  = stream.readByte();
+
+	readRect(stream, _exitHotspot);
+}
+
+void MagnetMazePuzzle::resetMagnets() {
+	for (int i = 0; i < kNumMagnets; ++i) {
+		_magnetPos[i]    = _magnetHomeRects[i];
+		_magnetLocked[i] = false;
+	}
+	_heldMagnet = -1;
+}
+
+void MagnetMazePuzzle::init() {
+	Common::Rect vpBounds = NancySceneState.getViewport().getBounds();
+	_drawSurface.create(vpBounds.width(), vpBounds.height(),
+		g_nancy->_graphics->getInputPixelFormat());
+	_drawSurface.clear(g_nancy->_graphics->getTransColor());
+	setTransparent(true);
+	setVisible(true);
+	moveTo(vpBounds);
+
+	g_nancy->_resource->loadImage(_boardImageName, _boardImage);
+	_boardImage.setTransparentColor(_drawSurface.getTransparentColor());
+
+	g_nancy->_resource->loadImage(_mazeImageName, _mazeImage);
+
+	MagnetMazePuzzleData *mmd = (MagnetMazePuzzleData *)NancySceneState.getPuzzleData(MagnetMazePuzzleData::getTag());
+	bool restored = false;
+	if (mmd && mmd->magnetState.size() >= kNumMagnets * 5) {
+		const Common::Array<int16> &state = mmd->magnetState;
+		for (int i = 0; i < kNumMagnets; ++i) {
+			_magnetPos[i].left   = state[i * 5 + 0];
+			_magnetPos[i].top    = state[i * 5 + 1];
+			_magnetPos[i].right  = state[i * 5 + 2];
+			_magnetPos[i].bottom = state[i * 5 + 3];
+			_magnetLocked[i]     = (state[i * 5 + 4] != 0);
+		}
+		_heldMagnet = -1;
+		restored = true;
+	}
+	if (!restored) {
+		resetMagnets();
+		persistState();
+	}
+
+	_isSolved = false;
+	_subState = kPlaying;
+
+	redraw();
+}
+
+void MagnetMazePuzzle::execute() {
+	switch (_state) {
+	case kBegin:
+		init();
+		registerGraphics();
+		_state = kRun;
+		// fall through
+
+	case kRun:
+		switch (_subState) {
+		case kPlaying:
+			break;
+		case kWaitWinDelay:
+			if (g_system->getMillis() >= _winDelayEndTime) {
+				if (_winSound.name != "NO SOUND") {
+					g_nancy->_sound->loadSound(_winSound);
+					g_nancy->_sound->playSound(_winSound);
+					_subState = kWaitWinSound;
+				} else {
+					_subState = kExitToWin;
+				}
+			}
+			break;
+		case kWaitWinSound:
+			if (!g_nancy->_sound->isSoundPlaying(_winSound)) {
+				g_nancy->_sound->stopSound(_winSound);
+				_subState = kExitToWin;
+			}
+			break;
+		case kExitToWin:
+		case kExitToCancel:
+			_state = kActionTrigger;
+			break;
+		}
+		break;
+
+	case kActionTrigger:
+		g_nancy->_sound->stopSound(_pickupSound);
+		g_nancy->_sound->stopSound(_placeSound);
+		g_nancy->_sound->stopSound(_resetSound);
+		g_nancy->_sound->stopSound(_bumpSound);
+		g_nancy->_sound->stopSound(_winSound);
+		if (_subState == kExitToWin) {
+			MagnetMazePuzzleData *mmd = (MagnetMazePuzzleData *)NancySceneState.getPuzzleData(MagnetMazePuzzleData::getTag());
+			if (mmd)
+				mmd->magnetState.clear();
+			if (_winFlag.label != -1)
+				NancySceneState.setEventFlag(_winFlag);
+			if (_winScene.sceneID != kNoScene)
+				NancySceneState.changeScene(_winScene);
+		} else {
+			if (_cancelFlag.label != -1)
+				NancySceneState.setEventFlag(_cancelFlag);
+			if (_cancelScene.sceneID != kNoScene)
+				NancySceneState.changeScene(_cancelScene);
+		}
+		finishExecution();
+		break;
+	}
+}
+
+bool MagnetMazePuzzle::collidesAt(const Common::Rect &r) const {
+	if (_mazeImage.empty() || _mazeImage.w == 0 || _mazeImage.h == 0)
+		return false;
+
+	const int mazeW = _mazeImage.w;
+	const int mazeH = _mazeImage.h;
+	int x0 = MAX<int>(0, r.left);
+	int y0 = MAX<int>(0, r.top);
+	int x1 = MIN<int>(mazeW, r.right);
+	int y1 = MIN<int>(mazeH, r.bottom);
+
+	// Anything that walks off the maze image counts as a wall.
+	if (x0 >= x1 || y0 >= y1)
+		return true;
+	if (r.left < 0 || r.top < 0 || r.right > mazeW || r.bottom > mazeH)
+		return true;
+
+	const uint32 wall = _mazeImage.format.RGBToColor(_wallColorR, _wallColorG, _wallColorB);
+	for (int y = y0; y < y1; ++y) {
+		for (int x = x0; x < x1; ++x) {
+			if (_mazeImage.getPixel(x, y) == wall)
+				return true;
+		}
+	}
+	return false;
+}
+
+void MagnetMazePuzzle::stepMagnetToward(Common::Rect &cur, const Common::Rect &target) const {
+	int dx = target.left - cur.left;
+	int dy = target.top - cur.top;
+
+	// Single-pixel walk toward the target with axis-only fallback so a
+	// diagonal blocked by a wall can still slide along it.
+	while (dx != 0 || dy != 0) {
+		const int sx = (dx > 0) ? 1 : (dx < 0 ? -1 : 0);
+		const int sy = (dy > 0) ? 1 : (dy < 0 ? -1 : 0);
+
+		if (sx != 0 && sy != 0) {
+			Common::Rect cand(cur.left + sx, cur.top + sy, cur.right + sx, cur.bottom + sy);
+			if (!collidesAt(cand)) {
+				cur = cand;
+				dx -= sx;
+				dy -= sy;
+				continue;
+			}
+		}
+		if (sx != 0) {
+			Common::Rect cand(cur.left + sx, cur.top, cur.right + sx, cur.bottom);
+			if (!collidesAt(cand)) {
+				cur = cand;
+				dx -= sx;
+				continue;
+			}
+		}
+		if (sy != 0) {
+			Common::Rect cand(cur.left, cur.top + sy, cur.right, cur.bottom + sy);
+			if (!collidesAt(cand)) {
+				cur = cand;
+				dy -= sy;
+				continue;
+			}
+		}
+		break; // boxed in on every axis
+	}
+}
+
+void MagnetMazePuzzle::snapMagnet(int idx) {
+	const Common::Rect &tgt = _magnetTargetRects[idx];
+	const Common::Rect &src = _magnetHomeRects[idx];
+	int x = tgt.left + (tgt.width()  - src.width())  / 2;
+	int y = tgt.top  + (tgt.height() - src.height()) / 2;
+	_magnetPos[idx] = Common::Rect(x, y, x + src.width(), y + src.height());
+	_magnetLocked[idx] = true;
+}
+
+void MagnetMazePuzzle::handleInput(NancyInput &input) {
+	if (_state != kRun || _subState != kPlaying)
+		return;
+
+	Common::Rect vpScreen = NancySceneState.getViewport().getScreenPosition();
+	Common::Point mouseVP = input.mousePos - Common::Point(vpScreen.left, vpScreen.top);
+
+	if (_heldMagnet != -1 && _heldDrawPos != mouseVP) {
+		_heldDrawPos = mouseVP;
+		const Common::Rect &src = _magnetHomeRects[_heldMagnet];
+		int w = src.width();
+		int h = src.height();
+		Common::Rect target(mouseVP.x - w / 2, mouseVP.y - h / 2,
+		                    mouseVP.x - w / 2 + w, mouseVP.y - h / 2 + h);
+		Common::Rect prev = _magnetPos[_heldMagnet];
+		stepMagnetToward(_magnetPos[_heldMagnet], target);
+		if (_magnetPos[_heldMagnet] != prev)
+			redraw();
+	}
+
+	if (!_exitHotspot.isEmpty() && _exitHotspot.contains(mouseVP)) {
+		g_nancy->_cursor->setCursorType(g_nancy->_cursor->_puzzleExitCursor);
+		if (input.input & NancyInput::kLeftMouseButtonUp)
+			_subState = kExitToCancel;
+		return;
+	}
+
+	if (!_resetButtonHotspot.isEmpty() && _resetButtonHotspot.contains(mouseVP)) {
+		g_nancy->_cursor->setCursorType(CursorManager::kHotspot);
+		if (input.input & NancyInput::kLeftMouseButtonUp) {
+			resetMagnets();
+			if (_resetSound.name != "NO SOUND") {
+				g_nancy->_sound->loadSound(_resetSound);
+				g_nancy->_sound->playSound(_resetSound);
+			}
+			persistState();
+			redraw();
+		}
+		return;
+	}
+
+	if (_heldMagnet == -1) {
+		for (int i = 0; i < kNumMagnets; ++i) {
+			if (_magnetLocked[i])
+				continue;
+			if (!_magnetPos[i].contains(mouseVP))
+				continue;
+			g_nancy->_cursor->setCursorType(CursorManager::kHotspot);
+			if (input.input & NancyInput::kLeftMouseButtonUp) {
+				if (_requiredItem != -1 && NancySceneState.getHeldItem() != _requiredItem) {
+					if (!_cantPlayed[i]) {
+						NancySceneState.playItemCantSound(_requiredItem);
+						_cantPlayed[i] = true;
+					}
+					return;
+				}
+				_heldMagnet = i;
+				_heldDrawPos = mouseVP;
+				if (_pickupSound.name != "NO SOUND") {
+					g_nancy->_sound->loadSound(_pickupSound);
+					g_nancy->_sound->playSound(_pickupSound);
+				}
+			}
+			return;
+		}
+		return;
+	}
+
+	g_nancy->_cursor->setCursorType(CursorManager::kDragHand);
+	if (!(input.input & NancyInput::kLeftMouseButtonUp))
+		return;
+
+	const Common::Rect &tgt = _magnetTargetRects[_heldMagnet];
+	if (_magnetPos[_heldMagnet].intersects(tgt)) {
+		snapMagnet(_heldMagnet);
+		if (_placeSound.name != "NO SOUND") {
+			g_nancy->_sound->loadSound(_placeSound);
+			g_nancy->_sound->playSound(_placeSound);
+		}
+	}
+
+	persistState();
+
+	_heldMagnet = -1;
+	checkSolved();
+	redraw();
+}
+
+void MagnetMazePuzzle::persistState() {
+	MagnetMazePuzzleData *mmd = (MagnetMazePuzzleData *)NancySceneState.getPuzzleData(MagnetMazePuzzleData::getTag());
+	if (!mmd)
+		return;
+	Common::Array<int16> &state = mmd->magnetState;
+	state.clear();
+	for (int i = 0; i < kNumMagnets; ++i) {
+		state.push_back((int16)_magnetPos[i].left);
+		state.push_back((int16)_magnetPos[i].top);
+		state.push_back((int16)_magnetPos[i].right);
+		state.push_back((int16)_magnetPos[i].bottom);
+		state.push_back(_magnetLocked[i] ? 1 : 0);
+	}
+}
+
+void MagnetMazePuzzle::checkSolved() {
+	for (int i = 0; i < kNumMagnets; ++i) {
+		if (!_magnetLocked[i])
+			return;
+	}
+	_isSolved = true;
+	_subState = kWaitWinDelay;
+	_winDelayEndTime = g_system->getMillis() + (uint32)_winDelaySec * 1000;
+}
+
+void MagnetMazePuzzle::redraw() {
+	_drawSurface.clear(_drawSurface.getTransparentColor());
+
+	for (int i = 0; i < kNumMagnets; ++i) {
+		const Common::Rect &src = _magnetSrcRects[i];
+		const Common::Rect &dst = _magnetPos[i];
+		if (src.isEmpty() || dst.isEmpty())
+			continue;
+		_drawSurface.blitFrom(_boardImage, src, Common::Point(dst.left, dst.top));
+	}
+
+	if (!_hideOverlays) {
+		for (int i = 0; i < kNumOverlays; ++i) {
+			const Common::Rect &src = _overlaySrcRects[i];
+			const Common::Rect &dst = _overlayDestRects[i];
+			if (src.isEmpty() || dst.isEmpty())
+				continue;
+			_drawSurface.blitFrom(_boardImage, src, Common::Point(dst.left, dst.top));
+		}
+	}
+
+	_needsRedraw = true;
+}
+
+} // End of namespace Action
+} // End of namespace Nancy
diff --git a/engines/nancy/action/puzzle/magnetmazepuzzle.h b/engines/nancy/action/puzzle/magnetmazepuzzle.h
new file mode 100644
index 00000000000..0cd4f266941
--- /dev/null
+++ b/engines/nancy/action/puzzle/magnetmazepuzzle.h
@@ -0,0 +1,134 @@
+/* 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 NANCY_ACTION_MAGNETMAZEPUZZLE_H
+#define NANCY_ACTION_MAGNETMAZEPUZZLE_H
+
+#include "engines/nancy/action/actionrecord.h"
+#include "engines/nancy/commontypes.h"
+
+namespace Nancy {
+namespace Action {
+
+// Four magnet pieces, four targets on a maze board. The player picks a piece
+// from its home slot and drops it onto its target zone; an overlap snaps the
+// piece to a fixed position and locks it. Reset button returns all pieces
+// home. Wins when every piece is locked into its target.
+// Called in Nancy10 from scenes 3280 (normal mode, with overlaid pieces) and
+// 3281 (easy mode, without overlaid pieces, triggered as a cheat mode when
+// visiting the puzzle multiple times).
+class MagnetMazePuzzle : public RenderActionRecord {
+public:
+	MagnetMazePuzzle() : RenderActionRecord(7) {}
+	virtual ~MagnetMazePuzzle() {}
+
+	void init() override;
+
+	void readData(Common::SeekableReadStream &stream) override;
+	void execute() override;
+	void handleInput(NancyInput &input) override;
+
+	bool isViewportRelative() const override { return true; }
+
+protected:
+	Common::String getRecordTypeName() const override { return "MagnetMazePuzzle"; }
+
+	static const int kNumMagnets  = 4;
+	static const int kNumOverlays = 6;
+
+	// File data
+
+	Common::Path _boardImageName;
+	Common::Path _mazeImageName;
+
+	Common::Rect _overlaySrcRects[kNumOverlays];
+	Common::Rect _overlayDestRects[kNumOverlays];
+
+	bool _overlayTransparent = false;
+	bool _hideOverlays       = false;
+
+	Common::Rect _magnetSrcRects[kNumMagnets];
+	Common::Rect _magnetHomeRects[kNumMagnets];
+	Common::Rect _magnetTargetRects[kNumMagnets];
+
+	int16 _requiredItem = -1;
+
+	Common::Rect _resetButtonHotspot;
+
+	SoundDescription _pickupSound;
+	SoundDescription _placeSound;
+	SoundDescription _resetSound;
+	SoundDescription _bumpSound;
+
+	SceneChangeDescription _winScene;
+	FlagDescription        _winFlag;
+	uint16                 _winDelaySec = 0;
+	SoundDescription       _winSound;
+
+	SceneChangeDescription _cancelScene;
+	FlagDescription        _cancelFlag;
+
+	Common::Rect _exitHotspot;
+
+	// Runtime state
+
+	enum SubState {
+		kPlaying = 0,
+		kWaitWinDelay,
+		kWaitWinSound,
+		kExitToWin,
+		kExitToCancel
+	};
+
+	SubState _subState = kPlaying;
+
+	Common::Rect _magnetPos[kNumMagnets];
+	bool _magnetLocked[kNumMagnets] = {};
+
+	int  _heldMagnet  = -1;
+	bool _isSolved    = false;
+	bool _cantPlayed[kNumMagnets] = {};
+	uint32 _winDelayEndTime = 0;
+
+	Common::Point _heldDrawPos;
+
+	Graphics::ManagedSurface _boardImage;
+	Graphics::ManagedSurface _mazeImage; // wall mask for pixel-collision tests
+
+	// RGB of the maze image's "wall" pixel color; a magnet rect overlapping
+	// any pixel of this color in the maze image is treated as colliding.
+	byte _wallColorR = 0;
+	byte _wallColorG = 0;
+	byte _wallColorB = 0;
+
+	void resetMagnets();
+	void snapMagnet(int idx);
+	void persistState();
+	void redraw();
+	void checkSolved();
+	bool collidesAt(const Common::Rect &r) const;
+	void stepMagnetToward(Common::Rect &cur, const Common::Rect &target) const;
+};
+
+} // End of namespace Action
+} // End of namespace Nancy
+
+#endif // NANCY_ACTION_MAGNETMAZEPUZZLE_H
diff --git a/engines/nancy/puzzledata.cpp b/engines/nancy/puzzledata.cpp
index bb1bc8f3579..7de27e562bc 100644
--- a/engines/nancy/puzzledata.cpp
+++ b/engines/nancy/puzzledata.cpp
@@ -230,6 +230,10 @@ void SortPuzzleData::synchronize(Common::Serializer &ser) {
 	syncInt16Array(ser, solvedState);
 }
 
+void MagnetMazePuzzleData::synchronize(Common::Serializer &ser) {
+	syncInt16Array(ser, magnetState);
+}
+
 void GridMapPuzzleData::synchronize(Common::Serializer &ser) {
 	syncInt16Array(ser, itemState);
 }
@@ -349,6 +353,8 @@ PuzzleData *makePuzzleData(const uint32 tag) {
 		return new BeadPuzzleData();
 	case SortPuzzleData::getTag():
 		return new SortPuzzleData();
+	case MagnetMazePuzzleData::getTag():
+		return new MagnetMazePuzzleData();
 	case GridMapPuzzleData::getTag():
 		return new GridMapPuzzleData();
 	case JournalData::getTag():
diff --git a/engines/nancy/puzzledata.h b/engines/nancy/puzzledata.h
index 88fb64fb7dc..bfcdbbcb6f2 100644
--- a/engines/nancy/puzzledata.h
+++ b/engines/nancy/puzzledata.h
@@ -140,6 +140,19 @@ struct SortPuzzleData : public PuzzleData {
 	Common::Array<int16> solvedState;
 };
 
+// Per-magnet (left, top, right, bottom, locked) packed as 5 int16s. The
+// puzzle's two scenes (3280, 3281) are the same puzzle with the same data,
+// so a single flat array suffices.
+struct MagnetMazePuzzleData : public PuzzleData {
+	MagnetMazePuzzleData() {}
+	virtual ~MagnetMazePuzzleData() {}
+
+	static constexpr uint32 getTag() { return MKTAG('M', 'M', 'A', 'Z'); }
+	virtual void synchronize(Common::Serializer &ser);
+
+	Common::Array<int16> magnetState;
+};
+
 // Per-item (inMap, inItems, mapRow, mapCol, itemsRow, itemsCol) packed as
 // 6 int16s.
 struct GridMapPuzzleData : public PuzzleData {


Commit: 48cb27af63a8f588cf11d518609d382afa8139d6
    https://github.com/scummvm/scummvm/commit/48cb27af63a8f588cf11d518609d382afa8139d6
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-06-04T03:09:03+03:00

Commit Message:
NANCY: Implement taskbar button notifications for Nancy10+

Changed paths:
    engines/nancy/action/datarecords.cpp
    engines/nancy/action/miscrecords.cpp
    engines/nancy/state/scene.cpp
    engines/nancy/ui/cellphonepopup.cpp
    engines/nancy/ui/inventorypopup.cpp
    engines/nancy/ui/notebookpopup.cpp
    engines/nancy/ui/taskbar.cpp
    engines/nancy/ui/taskbar.h


diff --git a/engines/nancy/action/datarecords.cpp b/engines/nancy/action/datarecords.cpp
index 6610f500be0..0f6bf6bcd68 100644
--- a/engines/nancy/action/datarecords.cpp
+++ b/engines/nancy/action/datarecords.cpp
@@ -26,6 +26,8 @@
 
 #include "engines/nancy/state/scene.h"
 
+#include "engines/nancy/ui/taskbar.h"
+
 namespace Nancy {
 namespace Action {
 
@@ -480,6 +482,18 @@ void ModifyListEntry::execute() {
 		NancySceneState.getNotebookPopup().refreshContent();
 	}
 
+	// Nancy 10+: raise the notebook notification badge on the taskbar when
+	// a new entry is added to one of the tracked lists.
+	if (_type == kAdd && g_nancy->getGameType() >= kGameTypeNancy10) {
+		if (UI::Taskbar *taskbar = NancySceneState.getTaskbar()) {
+			if (_surfaceID == 4) {
+				taskbar->setNotification(kTaskButtonNotebook, 0);
+			} else if (_surfaceID == 3) {
+				taskbar->setNotification(kTaskButtonNotebook, 1);
+			}
+		}
+	}
+
 	finishExecution();
 }
 
diff --git a/engines/nancy/action/miscrecords.cpp b/engines/nancy/action/miscrecords.cpp
index 1c45bbd347f..9dca812d0e4 100644
--- a/engines/nancy/action/miscrecords.cpp
+++ b/engines/nancy/action/miscrecords.cpp
@@ -281,6 +281,13 @@ void AddSearchLink::readData(Common::SeekableReadStream &stream) {
 void AddSearchLink::execute() {
 	//NancySceneState.getCellPhonePopup().addSearchLink(
 	//	_mode, _key, _value, _extra, _flag, _eventFlag);
+
+	// Cellphone taskbar badge: mode 0 = new email (sub-cat 1), mode != 0
+	// = new web search topic (sub-cat 2).
+	if (UI::Taskbar *taskbar = NancySceneState.getTaskbar()) {
+		taskbar->setNotification(kTaskButtonCellphone, _mode == 0 ? 1 : 2);
+	}
+
 	finishExecution();
 }
 
@@ -315,6 +322,12 @@ void ChangeCellPhoneInfo::readData(Common::SeekableReadStream &stream) {
 
 void ChangeCellPhoneInfo::execute() {
 	NancySceneState.getCellPhonePopup().upsertContact(_contact);
+
+	// Cellphone taskbar badge: a new/updated contact triggers sub-cat 0.
+	if (UI::Taskbar *taskbar = NancySceneState.getTaskbar()) {
+		taskbar->setNotification(kTaskButtonCellphone, 0);
+	}
+
 	finishExecution();
 }
 
diff --git a/engines/nancy/state/scene.cpp b/engines/nancy/state/scene.cpp
index 02cefa63196..2a0352af58d 100644
--- a/engines/nancy/state/scene.cpp
+++ b/engines/nancy/state/scene.cpp
@@ -349,8 +349,12 @@ void Scene::addItemToInventory(int16 id) {
 
 		if (g_nancy->getGameType() <= kGameTypeNancy9) {
 			_inventoryBox.addItem(id);
-		} else if (_inventoryPopup.isOpen()) {
-			_inventoryPopup.refreshGrid();
+		} else {
+			if (_inventoryPopup.isOpen()) {
+				_inventoryPopup.refreshGrid();
+			} else if (_taskbar) {
+				_taskbar->setNotification(kTaskButtonInventory, 0);
+			}
 		}
 	}
 }
diff --git a/engines/nancy/ui/cellphonepopup.cpp b/engines/nancy/ui/cellphonepopup.cpp
index 0de5e463f4e..bcea4ea19fb 100644
--- a/engines/nancy/ui/cellphonepopup.cpp
+++ b/engines/nancy/ui/cellphonepopup.cpp
@@ -30,6 +30,8 @@
 
 #include "engines/nancy/state/scene.h"
 
+#include "engines/nancy/ui/taskbar.h"
+
 #include "engines/nancy/ui/cellphonepopup.h"
 
 namespace Nancy {
@@ -184,6 +186,8 @@ void CellPhonePopup::open() {
 	drawScreenContent();
 	setVisible(true);
 
+	NancySceneState.getTaskbar()->clearAllNotifications(kTaskButtonCellphone);
+
 	if (!_uiclData->header.sounds[0].name.empty()) {
 		g_nancy->_sound->loadSound(_uiclData->header.sounds[0]);
 		g_nancy->_sound->playSound(_uiclData->header.sounds[0]);
diff --git a/engines/nancy/ui/inventorypopup.cpp b/engines/nancy/ui/inventorypopup.cpp
index eb24e8693b9..ddada1f2ef2 100644
--- a/engines/nancy/ui/inventorypopup.cpp
+++ b/engines/nancy/ui/inventorypopup.cpp
@@ -29,6 +29,7 @@
 #include "engines/nancy/state/scene.h"
 
 #include "engines/nancy/ui/inventorypopup.h"
+#include "engines/nancy/ui/taskbar.h"
 
 namespace Nancy {
 namespace UI {
@@ -86,6 +87,8 @@ void InventoryPopup::open() {
 
 	setVisible(true);
 
+	NancySceneState.getTaskbar()->clearAllNotifications(kTaskButtonInventory);
+
 	if (!_uiivData->header.sounds[0].name.empty()) {
 		g_nancy->_sound->loadSound(_uiivData->header.sounds[0]);
 		g_nancy->_sound->playSound(_uiivData->header.sounds[0]);
diff --git a/engines/nancy/ui/notebookpopup.cpp b/engines/nancy/ui/notebookpopup.cpp
index 2c3c658975d..50d79fb3842 100644
--- a/engines/nancy/ui/notebookpopup.cpp
+++ b/engines/nancy/ui/notebookpopup.cpp
@@ -30,6 +30,8 @@
 
 #include "engines/nancy/state/scene.h"
 
+#include "engines/nancy/ui/taskbar.h"
+
 #include "engines/nancy/ui/notebookpopup.h"
 
 namespace Nancy {
@@ -112,6 +114,8 @@ void NotebookPopup::open() {
 
 	setVisible(true);
 
+	NancySceneState.getTaskbar()->clearAllNotifications(kTaskButtonNotebook);
+
 	// JournalData entries may have changed since the last open (added by
 	// ModifyListEntry, marked complete, etc.) ??? re-render content.
 	refreshContent();
diff --git a/engines/nancy/ui/taskbar.cpp b/engines/nancy/ui/taskbar.cpp
index d266c2a62bd..abd2cb1ce66 100644
--- a/engines/nancy/ui/taskbar.cpp
+++ b/engines/nancy/ui/taskbar.cpp
@@ -39,6 +39,9 @@ Taskbar::Taskbar() :
 	for (uint i = 0; i < TASK::kNumButtons; ++i) {
 		_buttonStates[i] = kButtonIdle;
 		_enabled[i] = true;
+		for (uint s = 0; s < kNumNotificationSubCategories; ++s) {
+			_notifications[i][s] = false;
+		}
 	}
 }
 
@@ -110,9 +113,16 @@ Taskbar::ButtonState Taskbar::restingState(uint index) const {
 	if (!_enabled[index]) {
 		return kButtonDisabled;
 	}
+	// Disable override (ControlUIItems) takes precedence over the badge ???
+	// FUN_004d51c3 only draws the notify sprite when state != 3.
 	const ButtonOverride &o = _overrides[index];
 	if (o.active && _currentScene >= o.startScene && _currentScene <= o.endScene) {
-		return o.state;
+		return kButtonDisabled;
+	}
+	for (uint s = 0; s < kNumNotificationSubCategories; ++s) {
+		if (_notifications[index][s]) {
+			return kButtonNotification;
+		}
 	}
 	return kButtonIdle;
 }
@@ -131,17 +141,36 @@ void Taskbar::toggleButton(uint index, bool enabled) {
 	}
 }
 
-void Taskbar::setNotification(uint buttonIndex, int16 startScene, int16 endScene) {
+void Taskbar::setNotification(uint buttonIndex, uint subCategory) {
+	if (buttonIndex >= TASK::kNumButtons || subCategory >= kNumNotificationSubCategories) {
+		return;
+	}
+	_notifications[buttonIndex][subCategory] = true;
+
+	if ((int)buttonIndex != _hoveredButton) {
+		drawButton(buttonIndex, restingState(buttonIndex));
+	}
+}
+
+void Taskbar::clearNotification(uint buttonIndex, uint subCategory) {
+	if (buttonIndex >= TASK::kNumButtons || subCategory >= kNumNotificationSubCategories) {
+		return;
+	}
+	_notifications[buttonIndex][subCategory] = false;
+
+	if ((int)buttonIndex != _hoveredButton) {
+		drawButton(buttonIndex, restingState(buttonIndex));
+	}
+}
+
+void Taskbar::clearAllNotifications(uint buttonIndex) {
 	if (buttonIndex >= TASK::kNumButtons) {
 		return;
 	}
-	_overrides[buttonIndex].active = true;
-	_overrides[buttonIndex].state = kButtonNotification;
-	_overrides[buttonIndex].startScene = startScene;
-	_overrides[buttonIndex].endScene = endScene;
+	for (uint s = 0; s < kNumNotificationSubCategories; ++s) {
+		_notifications[buttonIndex][s] = false;
+	}
 
-	// Re-render this button immediately unless the player is currently
-	// hovering it (hover takes priority over the override sprite).
 	if ((int)buttonIndex != _hoveredButton) {
 		drawButton(buttonIndex, restingState(buttonIndex));
 	}
@@ -152,7 +181,6 @@ void Taskbar::setDisabledRange(uint buttonIndex, int16 startScene, int16 endScen
 		return;
 	}
 	_overrides[buttonIndex].active = true;
-	_overrides[buttonIndex].state = kButtonDisabled;
 	_overrides[buttonIndex].startScene = startScene;
 	_overrides[buttonIndex].endScene = endScene;
 
@@ -226,11 +254,10 @@ void Taskbar::handleInput(NancyInput &input) {
 	} else if (input.input & NancyInput::kLeftMouseButtonUp) {
 		// Mouse released over the button: trigger the click action and
 		// snap the sprite back to hover (the cursor is still over it).
-		// Acknowledging the click also clears a pending notification
-		// for this button, so re-opening the popup doesn't keep blinking.
-		// A scene-ranged disable override is left intact.
-		if (_overrides[newHovered].state == kButtonNotification) {
-			_overrides[newHovered].active = false;
+		// Acknowledging the click also clears any pending notifications
+		// for this button ??? the popup will read them on entry.
+		for (uint s = 0; s < kNumNotificationSubCategories; ++s) {
+			_notifications[newHovered][s] = false;
 		}
 		drawButton(newHovered, kButtonHover);
 		_clickedButton = newHovered;
diff --git a/engines/nancy/ui/taskbar.h b/engines/nancy/ui/taskbar.h
index f8b1d92dd22..3838ed4f3a6 100644
--- a/engines/nancy/ui/taskbar.h
+++ b/engines/nancy/ui/taskbar.h
@@ -46,17 +46,20 @@ public:
 	// in its disabled sprite and ignores clicks.
 	void toggleButton(uint index, bool enabled);
 
-	// Configure a per-button override that is active only while the player
-	// is in a scene whose ID falls in [startScene, endScene]. The button
-	// renders in its notification (badge) sprite, or its disabled sprite,
-	// for that range; outside it reverts to idle.
-	// setDisabledRange is driven by AR 29 (ControlUIItems, _flagB != 0).
-	// setNotification renders the badge sprite; the source that should drive
-	// it has not been identified yet (it is NOT ControlUIItems).
-	void setNotification(uint buttonIndex, int16 startScene, int16 endScene);
+	// Disable override: keep the disabled sprite active while the current
+	// scene is in [startScene, endScene]. Driven by AR 29 (ControlUIItems,
+	// _flagB != 0).
 	void setDisabledRange(uint buttonIndex, int16 startScene, int16 endScene);
 	void clearButtonOverride(uint buttonIndex);
 
+	// Notification badge: each button has up to 3 independent notification
+	// sub-categories. The badge shows when any sub-category is set; it
+	// persists across scene changes until cleared (or all sub-cats are
+	// cleared individually). Disabled state takes precedence over the badge.
+	void setNotification(uint buttonIndex, uint subCategory);
+	void clearNotification(uint buttonIndex, uint subCategory);
+	void clearAllNotifications(uint buttonIndex);
+
 	// Re-evaluate which buttons should currently show their override
 	// sprite. Call after a scene change so the range check kicks in.
 	void updateNotificationStates(int16 currentSceneID);
@@ -74,16 +77,17 @@ private:
 		kButtonNotification = 4    // popup has new content (badge sprite)
 	};
 
-	// A scene-ranged sprite override for one button. While active and the
+	// A scene-ranged disable override for one button. While active and the
 	// current scene is within [startScene, endScene] the button renders in
-	// `state` (kButtonNotification or kButtonDisabled).
+	// the disabled state.
 	struct ButtonOverride {
 		bool active = false;
-		ButtonState state = kButtonIdle;
 		int16 startScene = -1;
 		int16 endScene = -1;
 	};
 
+	static const uint kNumNotificationSubCategories = 3;
+
 	void drawButton(uint index, ButtonState state);
 	ButtonState restingState(uint index) const;
 	// True when the button currently accepts hover/click (not disabled).
@@ -97,6 +101,7 @@ private:
 	bool _enabled[5];
 	ButtonState _buttonStates[5];
 	ButtonOverride _overrides[5];
+	bool _notifications[5][kNumNotificationSubCategories];
 };
 
 } // End of namespace UI


Commit: cbd22cfc0a8a7b0a3941fc1b83c98b7bc9c1141b
    https://github.com/scummvm/scummvm/commit/cbd22cfc0a8a7b0a3941fc1b83c98b7bc9c1141b
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-06-04T03:09:08+03:00

Commit Message:
NANCY: Implement the rest of the cellphone functionality for Nancy10+

This *breaks* existing Nancy10+ saved games

- Implemented online email/web screen
- Implemented web search list
- Implemented message list
- Implemented full-text view of emails / web pages

Changed paths:
    engines/nancy/action/miscrecords.cpp
    engines/nancy/enginedata.cpp
    engines/nancy/enginedata.h
    engines/nancy/puzzledata.cpp
    engines/nancy/puzzledata.h
    engines/nancy/ui/cellphonepopup.cpp
    engines/nancy/ui/cellphonepopup.h


diff --git a/engines/nancy/action/miscrecords.cpp b/engines/nancy/action/miscrecords.cpp
index 9dca812d0e4..35af709604a 100644
--- a/engines/nancy/action/miscrecords.cpp
+++ b/engines/nancy/action/miscrecords.cpp
@@ -279,8 +279,8 @@ void AddSearchLink::readData(Common::SeekableReadStream &stream) {
 }
 
 void AddSearchLink::execute() {
-	//NancySceneState.getCellPhonePopup().addSearchLink(
-	//	_mode, _key, _value, _extra, _flag, _eventFlag);
+	NancySceneState.getCellPhonePopup().addSearchLink(
+		_mode, _key, _value, _extra, _flag, _eventFlag);
 
 	// Cellphone taskbar badge: mode 0 = new email (sub-cat 1), mode != 0
 	// = new web search topic (sub-cat 2).
diff --git a/engines/nancy/enginedata.cpp b/engines/nancy/enginedata.cpp
index 2c029526ba6..c4df40aa807 100644
--- a/engines/nancy/enginedata.cpp
+++ b/engines/nancy/enginedata.cpp
@@ -938,9 +938,12 @@ UICL::UICL(Common::SeekableReadStream *chunkStream) : EngineData(chunkStream) {
 
 	readFilename(*chunkStream, overlayImageName);
 
-	// Skip shared UIButton template - the sub-fields data is read
-	// separately for each button record below.
-	chunkStream->skip(206);
+	// Shared UIButton template (206 bytes); its sub-fields are read
+	// separately per button below. The last 49 bytes of the block are
+	// the call-sound template (channel / volume / loops for the ring,
+	// pickup and invalid-number cues).
+	chunkStream->skip(157);
+	callSoundTemplate.readNormal(*chunkStream);
 
 	for (uint i = 0; i < kNumDialPadSlots; ++i) {
 		readRect(*chunkStream, dialPadSlots[i].srcRect);
@@ -974,13 +977,13 @@ UICL::UICL(Common::SeekableReadStream *chunkStream) : EngineData(chunkStream) {
 	readRect(*chunkStream, dirLabel.srcRect);
 	readRect(*chunkStream, dirLabel.destRect);
 
-	// Call/hang-up widget (3 rects).
-	readRect(*chunkStream, callButton.srcRectIdle);
-	readRect(*chunkStream, callButton.srcRectPressed);
-	readRect(*chunkStream, callButton.destRect);
+	// Help "?" button widget (3 rects).
+	readRect(*chunkStream, helpButton.srcRectIdle);
+	readRect(*chunkStream, helpButton.srcRectPressed);
+	readRect(*chunkStream, helpButton.destRect);
 
 	// Screen-content sprite block
-	readFilename(*chunkStream, phoneUseSound);
+	readFilename(*chunkStream, helpTextKey);
 	readRect(*chunkStream, signalSpriteSrc);
 	readRect(*chunkStream, signalSpriteSrcAlt);
 	readRect(*chunkStream, signalSpriteDest);
diff --git a/engines/nancy/enginedata.h b/engines/nancy/enginedata.h
index c4310365ae0..23483060220 100644
--- a/engines/nancy/enginedata.h
+++ b/engines/nancy/enginedata.h
@@ -612,6 +612,12 @@ struct UICL : public EngineData {
 
 	UIPopupHeader header;
 	Common::Path overlayImageName;
+
+	// Template the call-flow sounds (ring / pickup / invalid) are played
+	// through. Only the name is swapped per call; channel / volume /
+	// loops come from here.
+	SoundDescription callSoundTemplate;
+
 	DialPadSlot dialPadSlots[kNumDialPadSlots];
 
 	// Screen-frame and label rects
@@ -625,10 +631,12 @@ struct UICL : public EngineData {
 	SrcDestRectPair webLabel;
 	SrcDestRectPair dirLabel;
 
-	ThreeRectWidget callButton;
+	// Help "?" button (original button index 15). The visible Talk/Call key
+	// is dial-pad slot 12, not a separate widget.
+	ThreeRectWidget helpButton;
 
 	// Screen-content sprite block
-	Common::Path phoneUseSound;
+	Common::String helpTextKey;               // CVTX key for the help page text
 	Common::Rect signalSpriteSrc;
 	Common::Rect signalSpriteSrcAlt;
 	Common::Rect signalSpriteDest;
diff --git a/engines/nancy/puzzledata.cpp b/engines/nancy/puzzledata.cpp
index 7de27e562bc..a11dbc379cd 100644
--- a/engines/nancy/puzzledata.cpp
+++ b/engines/nancy/puzzledata.cpp
@@ -331,6 +331,25 @@ void CellPhoneData::synchronize(Common::Serializer &ser) {
 
 		ser.syncBytes(c.unknownSuffix, sizeof(c.unknownSuffix));
 	}
+
+	syncLinkArray(ser, emailMessages);
+	syncLinkArray(ser, searchLinks);
+}
+
+void CellPhoneData::syncLinkArray(Common::Serializer &ser, Common::Array<LinkEntry> &arr) {
+	uint16 n = (uint16)arr.size();
+	ser.syncAsUint16LE(n);
+	if (ser.isLoading()) {
+		arr.resize(n);
+	}
+	for (uint16 i = 0; i < n; ++i) {
+		ser.syncString(arr[i].key);
+		ser.syncString(arr[i].value);
+		ser.syncAsSint16LE(arr[i].extra);
+		ser.syncAsSint16LE(arr[i].flag);
+		ser.syncAsSint16LE(arr[i].eventFlag);
+		ser.syncAsByte(arr[i].read);
+	}
 }
 
 PuzzleData *makePuzzleData(const uint32 tag) {
diff --git a/engines/nancy/puzzledata.h b/engines/nancy/puzzledata.h
index bfcdbbcb6f2..5932ec92c0e 100644
--- a/engines/nancy/puzzledata.h
+++ b/engines/nancy/puzzledata.h
@@ -218,9 +218,19 @@ struct TableData : public PuzzleData {
 	Common::Array<float> comboValues;
 };
 
-// Nancy 10+ cellphone state mutated by the ChangeCellPhoneInfo and
-// SetCellPhoneBatteryAndSignal action records, persisted between saves.
+// Nancy 10+ cellphone state mutated by the ChangeCellPhoneInfo,
+// SetCellPhoneBatteryAndSignal and AddSearchLink action records,
+// persisted between saves.
 struct CellPhoneData : public PuzzleData {
+	struct LinkEntry {
+		Common::String key;       // CVTX key whose looked-up text is shown in the list
+		Common::String value;     // CVTX key for the body (email only); unused for search
+		int16 extra = 0;          // search mode: page index (mode-1 only); unused for email
+		int16 flag = -1;          // stored by the AR but unused by the original; reserved
+		int16 eventFlag = -1;     // event-flag index set when the entry is opened
+		bool read = false;        // email only: set once the message is opened
+	};
+
 	CellPhoneData() {}
 	virtual ~CellPhoneData() {}
 
@@ -233,6 +243,15 @@ struct CellPhoneData : public PuzzleData {
 	// the UICL chunk; we then own it as runtime data.
 	bool seeded = false;
 	Common::Array<UICL::Contact> contacts;
+
+	// Populated by AR 131 (AddSearchLink). Mode 0 ??? emailMessages (each
+	// with a body-text CVTX key + read flag); any non-zero mode ???
+	// searchLinks (web search topics).
+	Common::Array<LinkEntry> emailMessages;
+	Common::Array<LinkEntry> searchLinks;
+
+private:
+	void syncLinkArray(Common::Serializer &ser, Common::Array<LinkEntry> &arr);
 };
 
 PuzzleData *makePuzzleData(const uint32 tag);
diff --git a/engines/nancy/ui/cellphonepopup.cpp b/engines/nancy/ui/cellphonepopup.cpp
index bcea4ea19fb..60e3f429414 100644
--- a/engines/nancy/ui/cellphonepopup.cpp
+++ b/engines/nancy/ui/cellphonepopup.cpp
@@ -28,6 +28,8 @@
 #include "engines/nancy/resource.h"
 #include "engines/nancy/sound.h"
 
+#include "engines/nancy/misc/hypertext.h"
+
 #include "engines/nancy/state/scene.h"
 
 #include "engines/nancy/ui/taskbar.h"
@@ -37,6 +39,38 @@
 namespace Nancy {
 namespace UI {
 
+// Renders the engine's hypertext markup (colour / formatting tags) into a
+// scratch surface, which the content view then blits into the LCD. Thin
+// wrapper that exposes HypertextParser's protected rendering entry points.
+class CellPhoneHypertext : public Misc::HypertextParser {
+public:
+	// Expose the inherited per-page image hooks so the popup can register
+	// the UIBW image table before render() is called.
+	using Misc::HypertextParser::setImageName;
+	using Misc::HypertextParser::addImage;
+
+	void render(uint width, uint height, uint32 transColor,
+				const Common::String &text, uint fontID) {
+		initSurfaces(width, height, g_nancy->_graphics->getInputPixelFormat(),
+						transColor, transColor);
+		_fullSurface.setTransparentColor(transColor);
+		addTextLine(text);
+
+		const Font *font = g_nancy->_graphics->getFont(fontID);
+		const TBOX *tbox = GetEngineData(TBOX);
+		Common::Rect textBounds(0, 0, (int16)width, (int16)height);
+		const uint d = font ? (font->getFontHeight() + 1) / 2 + 1 : 0;
+		textBounds.left += d;
+		textBounds.top += d + 1;
+		const int leftOffset = tbox ? (int)tbox->leftOffset - textBounds.left : 0;
+		drawAllText(textBounds, (uint)MAX(0, leftOffset), fontID, fontID);
+	}
+
+	const Graphics::ManagedSurface &surface() const { return _fullSurface; }
+	uint16 textHeight() const { return _drawnTextHeight; }
+	const Common::Array<Common::Rect> &hotspots() const { return _hotspots; }
+};
+
 // Chunk destRects are raw screen coords; subtract _screenPosition.topLeft
 // to get popup-local. srcRects are atlas-image coords for _spritesImage
 // and pass through unchanged.
@@ -136,6 +170,43 @@ void CellPhonePopup::setBatteryLow(bool low) {
 	}
 }
 
+void CellPhonePopup::addSearchLink(int16 mode, const Common::String &key,
+									const Common::String &value, int16 extra,
+									int16 flag, int16 eventFlag) {
+	CellPhoneData *cellData = (CellPhoneData *)NancySceneState.getPuzzleData(CellPhoneData::getTag());
+	if (!cellData) {
+		return;
+	}
+
+	// Original (AddSearchLink @ 004dac11) branches on `mode == 0` (email)
+	// vs anything else (search) ??? not specifically mode == 1.
+	const bool isSearch = (mode != 0);
+	Common::Array<CellPhoneData::LinkEntry> &list =
+		isSearch ? cellData->searchLinks : cellData->emailMessages;
+
+	// Skip duplicates (matched by key) so re-running the scene doesn't
+	// pile up the same entries.
+	for (uint i = 0; i < list.size(); ++i) {
+		if (list[i].key.equalsIgnoreCase(key)) {
+			return;
+		}
+	}
+
+	CellPhoneData::LinkEntry e;
+	e.key = key;
+	e.value = value;
+	e.extra = extra;
+	e.flag = flag;
+	e.eventFlag = eventFlag;
+	list.push_back(e);
+
+	if (_isVisible &&
+			((isSearch && _screenState == kWebList) ||
+			 (!isSearch && _screenState == kEmailList))) {
+		drawScreenContent();
+	}
+}
+
 void CellPhonePopup::upsertContact(const UICL::Contact &c) {
 	// Match against the 11-byte dial pattern (prefix[2..12]). If an entry
 	// already carries that pattern, overwrite it; otherwise append.
@@ -221,6 +292,10 @@ void CellPhonePopup::updateGraphics() {
 	case kWelcome:
 	case kDialing:
 	case kDirectory:
+	case kOnlineHub:
+	case kWebList:
+	case kEmailList:
+	case kContentView:
 		break;
 
 	case kPlaceCall:
@@ -297,10 +372,18 @@ void CellPhonePopup::updateGraphics() {
 // --------------------------------------------------------------------
 
 void CellPhonePopup::drawChrome() {
-	_drawSurface.blitFrom(_overlayImage, _uiclData->header.normalSrcRect,
-							Common::Point(0, 0));
+	// The chrome image holds two layouts side-by-side: the normal
+	// phone-with-keypad and a zoomed-in "full screen" variant with the
+	// keypad hidden. fullEmptyScreenSrc (chunk+0x10b5) points at the
+	// latter; the original swaps to it for browser/list/email-content
+	// modes so the LCD can extend down into the keypad area.
+	const Common::Rect &chromeSrc =
+		isZoomedChromeState() && !_uiclData->fullEmptyScreenSrc.isEmpty()
+			? _uiclData->fullEmptyScreenSrc
+			: _uiclData->header.normalSrcRect;
+	_drawSurface.blitFrom(_overlayImage, chromeSrc, Common::Point(0, 0));
 	drawCloseButton(_closeButtonHovered ? 1 : 0);
-	drawCallButtonState(0);
+	drawHelpButton(0);
 	_needsRedraw = true;
 }
 
@@ -343,9 +426,50 @@ void CellPhonePopup::drawScreenContent() {
 		break;
 
 	case kDirectory:
+		drawHeading(_uiclData->dirHeading);
 		drawDirectoryList();
 		drawDirectoryArrows();
 		break;
+
+	case kOnlineHub: {
+		drawHeading(_uiclData->onlineHeading);
+		// Email / Web option buttons (subButtons 3 and 4) sit inside the LCD.
+		const UICL::ThreeRectWidget &emailBtn = _uiclData->subButtons[3];
+		const UICL::ThreeRectWidget &webBtn   = _uiclData->subButtons[4];
+		const Common::Point chunkOrigin(_screenPosition.left, _screenPosition.top);
+		if (!emailBtn.srcRectIdle.isEmpty() && !emailBtn.destRect.isEmpty()) {
+			_drawSurface.blitFrom(_spritesImage, emailBtn.srcRectIdle,
+					Common::Point(emailBtn.destRect.left - chunkOrigin.x,
+									emailBtn.destRect.top - chunkOrigin.y));
+		}
+		if (!webBtn.srcRectIdle.isEmpty() && !webBtn.destRect.isEmpty()) {
+			_drawSurface.blitFrom(_spritesImage, webBtn.srcRectIdle,
+					Common::Point(webBtn.destRect.left - chunkOrigin.x,
+									webBtn.destRect.top - chunkOrigin.y));
+		}
+		break;
+	}
+
+	case kWebList:
+		// Web search-results list (AR-131 mode 1).
+		drawHeading(_uiclData->searchHeading);
+		drawLinkList();
+		drawDirectoryArrows();
+		break;
+
+	case kEmailList:
+		drawHeading(_uiclData->emailHeading);
+		drawLinkList();
+		drawDirectoryArrows();
+		break;
+
+	case kContentView:
+		if (_contentHeading) {
+			drawHeading(*_contentHeading);
+		}
+		drawContentView();
+		drawDirectoryArrows();
+		break;
 	}
 
 	_needsRedraw = true;
@@ -427,12 +551,19 @@ void CellPhonePopup::drawConnectedLabel() {
 }
 
 void CellPhonePopup::drawConnectingSprite() {
-	if (_uiclData->connectingSpriteSrc.isEmpty() || _uiclData->connectingSpriteDest.isEmpty()) {
+	// Invalid-number states swap in the alternate connecting sprite
+	// (the "try again" / red-light variant).
+	const bool useAlt = (_screenState == kInvalidNumber || _screenState == kWaitInvalid) &&
+						!_uiclData->connectingSpriteSrcAlt.isEmpty();
+	const Common::Rect &src = useAlt
+		? _uiclData->connectingSpriteSrcAlt
+		: _uiclData->connectingSpriteSrc;
+	if (src.isEmpty() || _uiclData->connectingSpriteDest.isEmpty()) {
 		return;
 	}
 
 	const Common::Point chunkOrigin(_screenPosition.left, _screenPosition.top);
-	_drawSurface.blitFrom(_spritesImage, _uiclData->connectingSpriteSrc,
+	_drawSurface.blitFrom(_spritesImage, src,
 							Common::Point(_uiclData->connectingSpriteDest.left - chunkOrigin.x,
 											_uiclData->connectingSpriteDest.top - chunkOrigin.y));
 }
@@ -454,23 +585,23 @@ void CellPhonePopup::drawDialedNumber() {
 						_screenPosition.width() - x, 0);
 }
 
-void CellPhonePopup::drawCallButtonState(uint state) {
-	const UICL::ThreeRectWidget &cb = _uiclData->callButton;
-	if (cb.destRect.isEmpty()) {
+void CellPhonePopup::drawHelpButton(uint state) {
+	const UICL::ThreeRectWidget &hb = _uiclData->helpButton;
+	if (hb.destRect.isEmpty()) {
 		return;
 	}
 
-	const Common::Rect &src = (state == 1 && !cb.srcRectPressed.isEmpty())
-								? cb.srcRectPressed
-								: cb.srcRectIdle;
+	const Common::Rect &src = (state == 1 && !hb.srcRectPressed.isEmpty())
+								? hb.srcRectPressed
+								: hb.srcRectIdle;
 	if (src.isEmpty()) {
 		return;
 	}
 
 	const Common::Point chunkOrigin(_screenPosition.left, _screenPosition.top);
 	_drawSurface.blitFrom(_spritesImage, src,
-							Common::Point(cb.destRect.left - chunkOrigin.x,
-											cb.destRect.top - chunkOrigin.y));
+							Common::Point(hb.destRect.left - chunkOrigin.x,
+											hb.destRect.top - chunkOrigin.y));
 }
 
 void CellPhonePopup::drawCloseButton(uint state) {
@@ -521,16 +652,243 @@ void CellPhonePopup::drawStatusLabels() {
 	}
 }
 
-void CellPhonePopup::drawDirHeading() {
-	const UICL::SrcDestRectPair &dh = _uiclData->dirHeading;
-	if (dh.srcRect.isEmpty() || dh.destRect.isEmpty()) {
+void CellPhonePopup::drawHeading(const UICL::SrcDestRectPair &heading) {
+	if (heading.srcRect.isEmpty() || heading.destRect.isEmpty()) {
 		return;
 	}
-
 	const Common::Point chunkOrigin(_screenPosition.left, _screenPosition.top);
-	_drawSurface.blitFrom(_spritesImage, dh.srcRect,
-							Common::Point(dh.destRect.left - chunkOrigin.x,
-											dh.destRect.top - chunkOrigin.y));
+	_drawSurface.blitFrom(_spritesImage, heading.srcRect,
+							Common::Point(heading.destRect.left - chunkOrigin.x,
+											heading.destRect.top - chunkOrigin.y));
+}
+
+Common::Array<uint> CellPhonePopup::listVisibleIndices() const {
+	Common::Array<uint> out;
+	const CellPhoneData *cellData = (const CellPhoneData *)NancySceneState.getPuzzleData(CellPhoneData::getTag());
+	if (!cellData) {
+		return out;
+	}
+
+	if (_screenState == kWebList) {
+		for (uint i = 0; i < cellData->searchLinks.size(); ++i) {
+			out.push_back(i);
+		}
+	} else if (_screenState == kEmailList) {
+		// "Old Email Only" (no-signal) hides messages not yet read.
+		for (uint i = 0; i < cellData->emailMessages.size(); ++i) {
+			if (!_noSignal || cellData->emailMessages[i].read) {
+				out.push_back(i);
+			}
+		}
+	}
+	return out;
+}
+
+void CellPhonePopup::drawLinkList() {
+	const CellPhoneData *cellData = (const CellPhoneData *)NancySceneState.getPuzzleData(CellPhoneData::getTag());
+	if (!cellData) {
+		return;
+	}
+	const Common::Array<CellPhoneData::LinkEntry> &list =
+		_screenState == kWebList ? cellData->searchLinks : cellData->emailMessages;
+	const Common::Array<uint> visible = listVisibleIndices();
+	if (visible.empty()) {
+		return;
+	}
+
+	const Font *font = g_nancy->_graphics->getFont(_uiclData->fontId2);
+	if (!font) {
+		return;
+	}
+
+	const CVTX *autotext = (const CVTX *)g_nancy->getEngineData("AUTOTEXT");
+
+	const uint titleRows = listTitleRows();
+	const uint totalRows = maxDirectoryRows();
+	const uint maxEntries = totalRows > titleRows ? totalRows - titleRows : 0;
+	for (uint visibleRow = 0;
+			visibleRow < maxEntries && _directoryScroll + visibleRow < visible.size();
+			++visibleRow) {
+		const uint absolute = visible[_directoryScroll + visibleRow];
+		const Common::Rect rowRect = directoryRowRect(titleRows + visibleRow);
+
+		// In email mode, prefix each row with the unread / selected icon.
+		int textX = rowRect.left;
+		if (_screenState == kEmailList) {
+			const Common::Rect &icon = (visibleRow == _directorySelection &&
+										!_uiclData->emailIconSelected.isEmpty())
+				? _uiclData->emailIconSelected
+				: _uiclData->emailIconUnread;
+			if (!icon.isEmpty()) {
+				const int iconX = MAX(0, rowRect.left - icon.width() - 2);
+				_drawSurface.blitFrom(_spritesImage, icon,
+										Common::Point(iconX, rowRect.top));
+				textX = MAX(textX, iconX + icon.width() + 2);
+			}
+		}
+
+		Common::String lookupKey = list[absolute].key;
+		lookupKey.toUppercase();
+		Common::String rowText = (autotext && autotext->texts.contains(lookupKey))
+			? autotext->texts[lookupKey]
+			: lookupKey;
+		// Single-line draw ??? drop every <n> markup so they don't render as
+		// literal "<n>" glyphs and crowd the row.
+		while (rowText.contains("<n>")) {
+			rowText.erase(rowText.find("<n>"), 3);
+		}
+
+		font->drawString(&_drawSurface, rowText,
+							textX, rowRect.top,
+							rowRect.right - textX, 0);
+	}
+}
+
+void CellPhonePopup::openContentView(const Common::String &key, const UICL::SrcDestRectPair &heading) {
+	_contentReturnState = _screenState;
+	_contentHeading = &heading;
+	_contentKey = key;
+	_contentKey.toUppercase();
+	_contentScroll = 0;
+	enterScreenState(kContentView);
+}
+
+void CellPhonePopup::openBrowserHome() {
+	// Web shows the navigable topic list (clickable rows + arrow-key
+	// selection); clicking a topic opens its article in the content view.
+	enterScreenState(kWebList);
+}
+
+void CellPhonePopup::drawContentView() {
+	if (_contentKey.empty()) {
+		return;
+	}
+
+	const CVTX *autotext = (const CVTX *)g_nancy->getEngineData("AUTOTEXT");
+	if (!autotext || !autotext->texts.contains(_contentKey)) {
+		return;
+	}
+
+	const Font *font = g_nancy->_graphics->getFont(_uiclData->fontId2);
+	if (!font) {
+		return;
+	}
+
+	// Content view runs under the zoomed-in chrome (drawChrome blits
+	// fullEmptyScreenSrc for kContentView), so the keypad is no longer
+	// visible underneath and we can render into the larger LCD area
+	// that emailListContainer / FUN_004dae28 define.
+	const Common::Rect &ws =
+		_uiclData->emailListContainer.isEmpty()
+			? _uiclData->welcomeScreen.destRect
+			: _uiclData->emailListContainer;
+	const int lcdLeft = ws.left - _screenPosition.left;
+	const int lcdTop  = ws.top  - _screenPosition.top;
+	const int lcdW    = ws.width();
+	const int lcdH    = ws.height();
+	const int textTop = 22;                 // clear the heading sprite
+	const int viewH   = MAX(0, lcdH - textTop);
+	const int rowH    = MAX(font->getFontHeight() + 1, 12);
+
+	// Render the engine's hypertext markup into a tall scratch surface,
+	// then blit a vertically-scrolled window of it into the LCD.
+	const Common::String renderText = autotext->texts[_contentKey];
+
+	// Find this page in the UIBW chunk (browser pages only); its hotspot
+	// records are the per-page image table the article references.
+	const UIBW *browserData = nullptr;
+	int pageIdx = -1;
+	if (_contentHeading == &_uiclData->browserHeading) {
+		browserData = GetEngineData(UIBW);
+		if (browserData) {
+			for (uint p = 0; p < browserData->pages.size(); ++p) {
+				Common::String pageKey = browserData->pages[p].imageName.toString();
+				pageKey.toUppercase();
+				if (pageKey == _contentKey) {
+					pageIdx = (int)p;
+					break;
+				}
+			}
+		}
+	}
+
+	// Parse the <H>...<L> regions out of the body before rendering ??? each
+	// becomes a clickable in-page hyperlink. The text between the markers
+	// is used as the target article CVTX key.
+	_contentHotspotTargets.clear();
+	{
+		uint32 cursor = 0;
+		while (cursor < renderText.size()) {
+			const uint32 hStart = renderText.find("<H>", cursor);
+			if (hStart >= renderText.size()) {
+				break;
+			}
+			const uint32 linkTextStart = hStart + 3;
+			const uint32 lStart = renderText.find("<L>", linkTextStart);
+			if (lStart >= renderText.size()) {
+				break;
+			}
+			Common::String linkText = renderText.substr(linkTextStart, lStart - linkTextStart);
+			linkText.toUppercase();
+			_contentHotspotTargets.push_back(linkText);
+			cursor = lStart + 3;
+		}
+	}
+
+	CellPhoneHypertext ht;
+	if (pageIdx >= 0 && !browserData->pages[pageIdx].hotspots.empty()) {
+		// UIBW hotspots are misnamed ??? they're per-page image records
+		// (id = line in the rendered text, rect = source in the atlas).
+		ht.setImageName(browserData->imageName);
+		for (uint i = 0; i < browserData->pages[pageIdx].hotspots.size(); ++i) {
+			const UIBW::Hotspot &h = browserData->pages[pageIdx].hotspots[i];
+			ht.addImage(h.id, h.rect);
+		}
+	}
+	const uint32 trans = g_nancy->_graphics->getTransColor();
+	ht.render(lcdW, 2000, trans, renderText, _uiclData->fontId2);
+
+	// Clamp scroll to the rendered text height.
+	const int textH = ht.textHeight();
+	const int maxScrollPx = MAX(0, textH - viewH);
+	const int maxScroll = maxScrollPx / rowH;
+	if ((int)_contentScroll > maxScroll) {
+		_contentScroll = maxScroll;
+	}
+
+	const int srcTop = (int)_contentScroll * rowH;
+	Common::Rect srcRect(0, srcTop, lcdW, srcTop + viewH);
+	srcRect.clip(Common::Rect(ht.surface().w, ht.surface().h));
+	if (srcRect.isEmpty()) {
+		_contentHotspots.clear();
+		return;
+	}
+
+	_drawSurface.blitFrom(ht.surface(), srcRect,
+							Common::Point(lcdLeft, lcdTop + textTop));
+
+	// Translate the parser's hotspots (surface coords) into popup-local
+	// coords for the current scroll. Drop any that aren't fully visible
+	// inside the LCD window so we don't fire on partially-clipped links.
+	_contentHotspots.clear();
+	const Common::Array<Common::Rect> &surfaceHs = ht.hotspots();
+	const uint linkCount = MIN(surfaceHs.size(), _contentHotspotTargets.size());
+	for (uint i = 0; i < linkCount; ++i) {
+		Common::Rect r = surfaceHs[i];
+		r.translate(lcdLeft, lcdTop + textTop - srcTop);
+		const Common::Rect lcdClip(lcdLeft, lcdTop + textTop,
+									lcdLeft + lcdW, lcdTop + textTop + viewH);
+		Common::Rect clipped = r.findIntersectingRect(lcdClip);
+		if (!clipped.isEmpty()) {
+			_contentHotspots.push_back(clipped);
+		} else {
+			_contentHotspots.push_back(Common::Rect());
+		}
+	}
+	// Resize so indices align even if some links were clipped to empty.
+	if (_contentHotspotTargets.size() > linkCount) {
+		_contentHotspotTargets.resize(linkCount);
+	}
 }
 
 void CellPhonePopup::drawDirectoryList() {
@@ -602,10 +960,24 @@ void CellPhonePopup::drawBackLabel() {
 											back.destRect.top - chunkOrigin.y));
 }
 
+const UICL::ThreeRectWidget &CellPhonePopup::scrollUpButton() const {
+	// Directory uses subButtons[1]; search / email / browser content all
+	// use subButtons[5] (which sits above the taller list LCD area).
+	return _screenState == kDirectory
+		? _uiclData->subButtons[1]
+		: _uiclData->subButtons[5];
+}
+
+const UICL::ThreeRectWidget &CellPhonePopup::scrollDownButton() const {
+	return _screenState == kDirectory
+		? _uiclData->subButtons[2]
+		: _uiclData->subButtons[6];
+}
+
 void CellPhonePopup::drawDirectoryArrows() {
 	// Up/down scroll arrows are not in the chrome image; blit on every redraw.
-	const UICL::ThreeRectWidget &up = _uiclData->subButtons[1];
-	const UICL::ThreeRectWidget &down = _uiclData->subButtons[2];
+	const UICL::ThreeRectWidget &up = scrollUpButton();
+	const UICL::ThreeRectWidget &down = scrollDownButton();
 
 	const Common::Point chunkOrigin(_screenPosition.left, _screenPosition.top);
 
@@ -620,18 +992,25 @@ void CellPhonePopup::drawDirectoryArrows() {
 												down.destRect.top - chunkOrigin.y));
 	}
 
-	// Selection indicator next to the active row.
+	// Selection indicator (dirArrowSrc sprite) at the dirCursorSrc column,
+	// stepped down by the active entry's layout row ??? only meaningful in
+	// directory mode; list and content modes have their own arrow logic
+	// (or none at all) and a stray cursor sprite paints over their LCD.
+	if (_screenState != kDirectory) {
+		return;
+	}
 	const Common::Rect &arrowSrc = _uiclData->dirArrowSrc;
-	if (arrowSrc.isEmpty()) {
+	const Common::Rect &cursor = _uiclData->dirCursorSrc;
+	if (arrowSrc.isEmpty() || cursor.isEmpty()) {
 		return;
 	}
-	const uint maxRows = maxDirectoryRows();
-	if (_directorySelection >= maxRows) {
+	const uint titleRows = listTitleRows();
+	const uint selRow = titleRows + _directorySelection;
+	if (selRow >= maxDirectoryRows()) {
 		return;
 	}
-	const Common::Rect rowRect = directoryRowRect(_directorySelection);
-	const int arrowX = MAX(0, rowRect.left - arrowSrc.width() - 2);
-	const int arrowY = rowRect.top;
+	const int arrowX = cursor.left - _screenPosition.left;
+	const int arrowY = cursor.top - _screenPosition.top + (int)selRow * rowPitch();
 	_drawSurface.blitFrom(_spritesImage, arrowSrc,
 							Common::Point(arrowX, arrowY));
 }
@@ -667,14 +1046,10 @@ bool CellPhonePopup::playSoundIfPresent(const Common::Path &soundName) {
 		return false;
 	}
 
+	// Play through the chunk's call-sound template: channel / volume /
+	// loops come from it, only the filename varies per cue.
+	_callSound = _uiclData->callSoundTemplate;
 	_callSound.name = nameStr;
-	if (_callSound.channelID == 0) {
-		// TODO: should come from the per-call channel slot in the chunk.
-		_callSound.channelID = 28;
-	}
-	_callSound.volume = 100;
-	_callSound.numLoops = 1;
-	_callSound.playCommands = 1;
 
 	g_nancy->_sound->loadSound(_callSound);
 	g_nancy->_sound->playSound(_callSound);
@@ -758,48 +1133,62 @@ void CellPhonePopup::triggerContactCallSceneChange(uint contactIndex) {
 // Directory helpers
 // --------------------------------------------------------------------
 
-uint CellPhonePopup::maxDirectoryRows() const {
-	const Font *font = g_nancy->_graphics->getFont(_uiclData->fontId2);
-	if (!font) {
-		return 0;
+int CellPhonePopup::rowPitch() const {
+	// Original (FUN_004d8476): pitch = dirCursorSrc.height + 8.
+	const Common::Rect &cursor = _uiclData->dirCursorSrc;
+	if (!cursor.isEmpty()) {
+		return cursor.height() + 8;
 	}
+	return MAX(_uiclData->dirArrowSrc.height() + 4, 14);
+}
 
-	const Common::Rect &arrow = _uiclData->dirArrowSrc;
-	if (arrow.isEmpty()) {
-		return 0;
+int CellPhonePopup::rowTopScreen() const {
+	// First row's Y (screen). Original anchors on dirCursorSrc.top - 5.
+	const Common::Rect &cursor = _uiclData->dirCursorSrc;
+	if (!cursor.isEmpty()) {
+		return cursor.top - 5;
 	}
+	return _uiclData->welcomeScreen.destRect.top + 22;
+}
 
-	const int rowH = MAX(arrow.height() + 4, 14);
-	const Common::Rect &ws = _uiclData->welcomeScreen.destRect;
-	const int firstRowOffset = 22;
-	const int lcdH = MAX(0, ws.height() - firstRowOffset);
-	return MAX<int>(1, lcdH / rowH);
+uint CellPhonePopup::maxDirectoryRows() const {
+	const int pitch = rowPitch();
+	if (pitch <= 0) {
+		return 0;
+	}
+	const int yLimit = _uiclData->welcomeScreen.destRect.bottom;
+	int y = rowTopScreen();
+	uint count = 0;
+	while (y + pitch < yLimit) {
+		++count;
+		y += pitch;
+	}
+	return count;
 }
 
 Common::Rect CellPhonePopup::directoryRowRect(uint visibleIndex) const {
-	const Font *font = g_nancy->_graphics->getFont(_uiclData->fontId2);
-	if (!font) {
-		return Common::Rect();
+	const Common::Rect &cursor = _uiclData->dirCursorSrc;
+	const Common::Rect &ws = _uiclData->welcomeScreen.destRect;
+	const int pitch = rowPitch();
+
+	// Row text spans from just right of the arrow cursor to a margin
+	// inside the LCD's right edge (the -30 the original applies).
+	int xLeftScreen, xRightScreen;
+	if (!cursor.isEmpty()) {
+		xLeftScreen  = cursor.right + 5;
+		xRightScreen = ws.right - 30;
+	} else {
+		const Common::Rect &arrow = _uiclData->dirArrowSrc;
+		xLeftScreen  = ws.left + arrow.width() + 4;
+		xRightScreen = ws.right - 2;
 	}
 
-	const Common::Rect &arrow = _uiclData->dirArrowSrc;
-	const int rowH = MAX(arrow.height() + 4, 14);
-	const Common::Rect &ws = _uiclData->welcomeScreen.destRect;
-	const int lcdLeft = ws.left - _screenPosition.left;
-	const int lcdTop  = ws.top  - _screenPosition.top;
-	const int lcdWidth = ws.width();
-	const int firstRowOffset = 22;
-
-	const int x = lcdLeft + arrow.width() + 4;
-	const int y = lcdTop + firstRowOffset + (int)visibleIndex * rowH;
-	const int width = MAX(0, lcdWidth - (arrow.width() + 4) - 2);
-	// Clamp to the LCD so a row rect can't leak onto the keypad below.
-	const int lcdBottom = lcdTop + ws.height();
-	const int rowBottom = MIN(y + rowH, lcdBottom);
-	if (rowBottom <= y) {
-		return Common::Rect();
-	}
-	return Common::Rect(x, y, x + width, rowBottom);
+	const int yTopScreen = rowTopScreen() + (int)visibleIndex * pitch;
+
+	const int x = xLeftScreen - _screenPosition.left;
+	const int y = yTopScreen - _screenPosition.top;
+	const int right = xRightScreen - _screenPosition.left;
+	return Common::Rect(x, y, MAX(right, x), y + pitch);
 }
 
 bool CellPhonePopup::isContactVisible(const UICL::Contact &c) const {
@@ -813,6 +1202,20 @@ bool CellPhonePopup::isContactVisible(const UICL::Contact &c) const {
 	return NancySceneState.getEventFlag((int16)flag, g_nancy->_true);
 }
 
+Common::Rect CellPhonePopup::hubEmailRect() const {
+	// subButtons[3] is the upper LCD option button (Email).
+	Common::Rect r = _uiclData->subButtons[3].destRect;
+	r.translate(-_screenPosition.left, -_screenPosition.top);
+	return r;
+}
+
+Common::Rect CellPhonePopup::hubWebRect() const {
+	// subButtons[4] is the lower LCD option button (Web).
+	Common::Rect r = _uiclData->subButtons[4].destRect;
+	r.translate(-_screenPosition.left, -_screenPosition.top);
+	return r;
+}
+
 Common::Rect CellPhonePopup::backLabelHitRect() const {
 	// Back overlays the Web/Dir label area. Returns popup-local coordinates.
 	Common::Rect hit;
@@ -868,14 +1271,28 @@ int CellPhonePopup::contactIndexForVisibleRow(uint visibleRow) const {
 	return -1;
 }
 
+uint CellPhonePopup::currentListEntryCount() const {
+	switch (_screenState) {
+	case kDirectory:
+		return deduplicatedContactCount();
+	case kWebList:
+	case kEmailList:
+		return listVisibleIndices().size();
+	default:
+		return 0;
+	}
+}
+
 void CellPhonePopup::moveDirectorySelection(int delta) {
-	if (_screenState != kDirectory || delta == 0) {
+	if (delta == 0) {
 		return;
 	}
 
-	const uint total = deduplicatedContactCount();
-	const uint maxRows = maxDirectoryRows();
-	if (total == 0 || maxRows == 0) {
+	const uint total = currentListEntryCount();
+	const uint totalRows = maxDirectoryRows();
+	const uint titleRows = listTitleRows();
+	const uint pageRows = totalRows > titleRows ? totalRows - titleRows : 0;
+	if (total == 0 || pageRows == 0) {
 		return;
 	}
 
@@ -883,11 +1300,7 @@ void CellPhonePopup::moveDirectorySelection(int delta) {
 
 	if (delta < 0) {
 		const uint dec = (uint)(-delta);
-		if (dec >= absolute) {
-			absolute = 0;
-		} else {
-			absolute -= dec;
-		}
+		absolute = dec >= absolute ? 0 : absolute - dec;
 	} else {
 		absolute += (uint)delta;
 		if (absolute >= total) {
@@ -898,8 +1311,8 @@ void CellPhonePopup::moveDirectorySelection(int delta) {
 	if (absolute < _directoryScroll) {
 		_directoryScroll = absolute;
 		_directorySelection = 0;
-	} else if (absolute >= _directoryScroll + maxRows) {
-		_directorySelection = maxRows - 1;
+	} else if (absolute >= _directoryScroll + pageRows) {
+		_directorySelection = pageRows - 1;
 		_directoryScroll = absolute - _directorySelection;
 	} else {
 		_directorySelection = absolute - _directoryScroll;
@@ -1020,9 +1433,22 @@ void CellPhonePopup::handleInput(NancyInput &input) {
 
 	const Common::Point chunkMouse = mouseToChunkCoords(input.mousePos);
 
+	// Help "?" button: opens the help page in the content view. Reachable
+	// from any interactive state (except when already showing it).
+	if (!_uiclData->helpButton.destRect.isEmpty() && !_uiclData->helpTextKey.empty() &&
+			!(_screenState == kContentView && _contentKey == _uiclData->helpTextKey) &&
+			_uiclData->helpButton.destRect.contains(chunkMouse)) {
+		g_nancy->_cursor->setCursorType(CursorManager::kHotspotArrow);
+		if (input.input & NancyInput::kLeftMouseButtonUp) {
+			openContentView(_uiclData->helpTextKey, _uiclData->helpHeading);
+			input.eatMouseInput();
+			return;
+		}
+	}
+
 	if (_screenState == kDirectory) {
-		const Common::Rect &upDst = _uiclData->subButtons[1].destRect;
-		const Common::Rect &downDst = _uiclData->subButtons[2].destRect;
+		const Common::Rect &upDst = scrollUpButton().destRect;
+		const Common::Rect &downDst = scrollDownButton().destRect;
 
 		// Up/down move the selection; scrolling kicks in at page edges.
 		if (upDst.contains(chunkMouse)) {
@@ -1077,11 +1503,206 @@ void CellPhonePopup::handleInput(NancyInput &input) {
 		// can override the directory by starting a fresh dial.
 	}
 
+	// Online hub: two labels ??? Email and Web ??? plus the Back hotspot.
+	if (_screenState == kOnlineHub) {
+		const Common::Point popupMouse(chunkMouse.x - _screenPosition.left,
+										chunkMouse.y - _screenPosition.top);
+		const Common::Rect emailR = hubEmailRect();
+		const Common::Rect webR   = hubWebRect();
+		const Common::Rect backHit = backLabelHitRect();
+
+		if (emailR.contains(popupMouse)) {
+			g_nancy->_cursor->setCursorType(CursorManager::kHotspotArrow);
+			if (input.input & NancyInput::kLeftMouseButtonUp) {
+				_directoryScroll = 0;
+				_directorySelection = 0;
+				enterScreenState(kEmailList);
+				input.eatMouseInput();
+				return;
+			}
+		} else if (webR.contains(popupMouse)) {
+			g_nancy->_cursor->setCursorType(CursorManager::kHotspotArrow);
+			if (input.input & NancyInput::kLeftMouseButtonUp) {
+				_directoryScroll = 0;
+				_directorySelection = 0;
+				openBrowserHome();
+				input.eatMouseInput();
+				return;
+			}
+		} else if (!backHit.isEmpty() && backHit.contains(popupMouse)) {
+			g_nancy->_cursor->setCursorType(CursorManager::kHotspotArrow);
+			if (input.input & NancyInput::kLeftMouseButtonUp) {
+				enterScreenState(kWelcome);
+				input.eatMouseInput();
+				return;
+			}
+		}
+	}
+
+	// Link-list modes (web search results / email messages). Up/down +
+	// Back behave like in directory mode; row clicks navigate to the
+	// link's scene and set its event flag.
+	if (isLinkListMode()) {
+		const Common::Rect &upDst = scrollUpButton().destRect;
+		const Common::Rect &downDst = scrollDownButton().destRect;
+
+		if (upDst.contains(chunkMouse)) {
+			g_nancy->_cursor->setCursorType(CursorManager::kHotspotArrow);
+			if (input.input & NancyInput::kLeftMouseButtonUp) {
+				moveDirectorySelection(-1);
+				input.eatMouseInput();
+				return;
+			}
+		} else if (downDst.contains(chunkMouse)) {
+			g_nancy->_cursor->setCursorType(CursorManager::kHotspotArrow);
+			if (input.input & NancyInput::kLeftMouseButtonUp) {
+				moveDirectorySelection(+1);
+				input.eatMouseInput();
+				return;
+			}
+		}
+
+		const Common::Rect backHit = backLabelHitRect();
+		const Common::Point popupMouse(chunkMouse.x - _screenPosition.left,
+										chunkMouse.y - _screenPosition.top);
+		const bool overUpDown =
+			upDst.contains(chunkMouse) || downDst.contains(chunkMouse);
+		if (!overUpDown && !backHit.isEmpty() && backHit.contains(popupMouse)) {
+			g_nancy->_cursor->setCursorType(CursorManager::kHotspotArrow);
+			if (input.input & NancyInput::kLeftMouseButtonUp) {
+				_directoryScroll = 0;
+				_directorySelection = 0;
+				enterScreenState(kOnlineHub);
+				input.eatMouseInput();
+				return;
+			}
+		}
+
+		const uint row = directoryRowAt(chunkMouse);
+		const uint titleRows = listTitleRows();
+		if (row != (uint)-1 && row >= titleRows) {
+			const uint entryRow = row - titleRows;
+			CellPhoneData *cellData = (CellPhoneData *)NancySceneState.getPuzzleData(CellPhoneData::getTag());
+			Common::Array<CellPhoneData::LinkEntry> *list = nullptr;
+			if (cellData) {
+				list = (_screenState == kWebList) ? &cellData->searchLinks
+												  : &cellData->emailMessages;
+			}
+			// Map the visible row through the active filter to a real index.
+			const Common::Array<uint> visible = listVisibleIndices();
+			const uint visIdx = _directoryScroll + entryRow;
+			if (list && visIdx < visible.size()) {
+				const uint absolute = visible[visIdx];
+				g_nancy->_cursor->setCursorType(CursorManager::kHotspotArrow);
+				if (input.input & NancyInput::kLeftMouseButtonUp) {
+					// Move the selection indicator to the clicked row, then
+					// act on the entry.
+					_directorySelection = entryRow;
+					CellPhoneData::LinkEntry &e = (*list)[absolute];
+					// Original sets the event flag when an entry is opened;
+					// it does not scene-change from a list click.
+					if (e.eventFlag != -1) {
+						NancySceneState.setEventFlag(e.eventFlag, g_nancy->_true);
+					}
+					if (_screenState == kEmailList && !e.value.empty()) {
+						// Email: open the message body and mark as read.
+						e.read = true;
+						openContentView(e.value, _uiclData->emailHeading);
+					} else if (_screenState == kWebList) {
+						// AR-131 mode-1 stores a browser-page INDEX in
+						// `extra`; the page body lives in the UIBW chunk
+						// (UrlPage.imageName is actually the body CVTX
+						// key, despite the field name). Fall back to
+						// `value`/`key` if the index is out of range.
+						Common::String articleKey;
+						const UIBW *browserData = GetEngineData(UIBW);
+						if (browserData && e.extra >= 0 &&
+								(uint)e.extra < browserData->pages.size()) {
+							articleKey = browserData->pages[e.extra].imageName.toString();
+						}
+						if (articleKey.empty()) {
+							articleKey = e.value.empty() ? e.key : e.value;
+						}
+						openContentView(articleKey, _uiclData->browserHeading);
+					} else {
+						drawScreenContent();
+					}
+					input.eatMouseInput();
+					return;
+				}
+			}
+		}
+	}
+
+	// Content view (single email / page text). Up/down scroll the text;
+	// Back returns to the list it was opened from.
+	if (_screenState == kContentView) {
+		// In-page hyperlinks first so they take priority over any
+		// overlapping fallthrough hit (e.g. the back hotspot).
+		const Common::Point popupMouseLink(chunkMouse.x - _screenPosition.left,
+											chunkMouse.y - _screenPosition.top);
+		for (uint i = 0; i < _contentHotspots.size(); ++i) {
+			if (_contentHotspots[i].isEmpty()) {
+				continue;
+			}
+			if (_contentHotspots[i].contains(popupMouseLink)) {
+				g_nancy->_cursor->setCursorType(CursorManager::kHotspotArrow);
+				if (input.input & NancyInput::kLeftMouseButtonUp) {
+					if (i < _contentHotspotTargets.size() &&
+							!_contentHotspotTargets[i].empty()) {
+						openContentView(_contentHotspotTargets[i],
+										_uiclData->browserHeading);
+					}
+					input.eatMouseInput();
+					return;
+				}
+				break;
+			}
+		}
+
+		const Common::Rect &upDst = scrollUpButton().destRect;
+		const Common::Rect &downDst = scrollDownButton().destRect;
+
+		if (upDst.contains(chunkMouse)) {
+			g_nancy->_cursor->setCursorType(CursorManager::kHotspotArrow);
+			if (input.input & NancyInput::kLeftMouseButtonUp) {
+				if (_contentScroll > 0) {
+					--_contentScroll;
+					drawScreenContent();
+				}
+				input.eatMouseInput();
+				return;
+			}
+		} else if (downDst.contains(chunkMouse)) {
+			g_nancy->_cursor->setCursorType(CursorManager::kHotspotArrow);
+			if (input.input & NancyInput::kLeftMouseButtonUp) {
+				++_contentScroll;
+				drawScreenContent();
+				input.eatMouseInput();
+				return;
+			}
+		}
+
+		const Common::Rect backHit = backLabelHitRect();
+		const Common::Point popupMouse(chunkMouse.x - _screenPosition.left,
+										chunkMouse.y - _screenPosition.top);
+		const bool overUpDown =
+			upDst.contains(chunkMouse) || downDst.contains(chunkMouse);
+		if (!overUpDown && !backHit.isEmpty() && backHit.contains(popupMouse)) {
+			g_nancy->_cursor->setCursorType(CursorManager::kHotspotArrow);
+			if (input.input & NancyInput::kLeftMouseButtonUp) {
+				_contentKey.clear();
+				_contentHeading = nullptr;
+				enterScreenState(_contentReturnState);
+				input.eatMouseInput();
+				return;
+			}
+		}
+	}
+
 	// Call/talk button. Checked before the dial-pad loop so an overlapping
-	// slot can't eat it. The keypad Talk key is slot 12; the callButton
-	// widget covers a different region.
-	if (_uiclData->callButton.destRect.contains(chunkMouse) ||
-			_uiclData->dialPadSlots[12].destRect.contains(chunkMouse)) {
+	// slot can't eat it. The Talk key is dial-pad slot 12.
+	if (_uiclData->dialPadSlots[12].destRect.contains(chunkMouse)) {
 		g_nancy->_cursor->setCursorType(CursorManager::kHotspotArrow);
 
 		if (input.input & NancyInput::kLeftMouseButtonUp) {
@@ -1131,10 +1752,22 @@ void CellPhonePopup::handleInput(NancyInput &input) {
 			}
 
 			if (newHovered < 10) {
-				if (_screenState == kDirectory) {
+				if (_screenState == kDirectory || isLinkListMode()) {
 					_dialedNumber.clear();
 				}
 				appendDigit((byte)newHovered);
+			} else if (newHovered == 13) {
+				// Online toggle: opens the Email/Web hub.
+				if (isOnlineMode()) {
+					_directoryScroll = 0;
+					_directorySelection = 0;
+					enterScreenState(kWelcome);
+				} else {
+					_dialedNumber.clear();
+					_directoryScroll = 0;
+					_directorySelection = 0;
+					enterScreenState(kOnlineHub);
+				}
 			} else if (newHovered == 14) {
 				if (_screenState == kDirectory) {
 					_directoryScroll = 0;
diff --git a/engines/nancy/ui/cellphonepopup.h b/engines/nancy/ui/cellphonepopup.h
index 884795c0e9a..9a0353003bd 100644
--- a/engines/nancy/ui/cellphonepopup.h
+++ b/engines/nancy/ui/cellphonepopup.h
@@ -59,6 +59,12 @@ public:
 	// Used by AR 130 to add/modify entries at runtime.
 	void upsertContact(const UICL::Contact &c);
 
+	// Append an entry to the search/email results list (mode 0) or the
+	// web bookmarks list (mode 1). Driven by AR 131 (AddSearchLink).
+	void addSearchLink(int16 mode, const Common::String &key,
+						const Common::String &value, int16 extra,
+						int16 flag, int16 eventFlag);
+
 private:
 	enum ScreenState : int {
 		kWelcome          = 0,
@@ -70,7 +76,11 @@ private:
 		kConnected        = 6,
 		kInvalidNumber    = 7,
 		kWaitInvalid      = 8,
-		kDirectory        = 9
+		kDirectory        = 9,
+		kOnlineHub        = 10,  // Online heading + Email / Web sub-buttons
+		kWebList          = 11,  // web search-results list (AR-131 mode 1)
+		kEmailList        = 12,  // email message list (AR-131 mode 0)
+		kContentView      = 13   // full-text view of a single email / page
 	};
 
 	void drawChrome();
@@ -82,15 +92,40 @@ private:
 	void drawConnectedLabel();
 	void drawConnectingSprite();
 	void drawDialedNumber();
-	void drawCallButtonState(uint state);
+	void drawHelpButton(uint state);
 	void drawCloseButton(uint state);
 	void drawStatusLabels();
 	void drawDirectoryList();
-	void drawDirHeading();
 	void drawDirectoryArrows();
 	void drawWelcomeScreen();
 	void drawBackLabel();
 
+	// Generic list renderer used by web / email modes.
+	void drawLinkList();
+	// Blit a heading sprite (e.g. emailHeading) at its chunk dest. The
+	// heading sits in the title-bar strip above the LCD content.
+	void drawHeading(const UICL::SrcDestRectPair &heading);
+	// Render the opened entry's body text in the LCD area, word-wrapped.
+	void drawContentView();
+	// Enter the content view for a list entry whose AUTOTEXT key is `key`.
+	void openContentView(const Common::String &key, const UICL::SrcDestRectPair &heading);
+	// Web button: open the first url entry as the browser home page (page 0).
+	void openBrowserHome();
+
+	// Up/down scroll buttons differ by mode: subButtons[1]/[2] for the
+	// directory's narrower list area, [5]/[6] for the taller search /
+	// email / browser-content LCD area.
+	const UICL::ThreeRectWidget &scrollUpButton() const;
+	const UICL::ThreeRectWidget &scrollDownButton() const;
+
+	// True when the current screen uses the zoomed-in (no-keypad)
+	// chrome variant: search list, email list, and content view.
+	bool isZoomedChromeState() const {
+		return _screenState == kWebList ||
+				_screenState == kEmailList ||
+				_screenState == kContentView;
+	}
+
 	void resetDialPad();
 	void enterScreenState(ScreenState newState);
 	void appendDigit(byte slotIndex);
@@ -102,10 +137,30 @@ private:
 	uint maxDirectoryRows() const;
 	uint directoryRowAt(const Common::Point &chunkMouse) const;
 	Common::Rect directoryRowRect(uint visibleIndex) const;
+	// Row pitch and first-row Y (screen coords) from the layout data.
+	int rowPitch() const;
+	int rowTopScreen() const;
+	bool isLinkListMode() const { return _screenState == kWebList || _screenState == kEmailList; }
+	bool isOnlineMode() const { return _screenState == kOnlineHub || isLinkListMode(); }
+
+	// Section headings live in the phone's title-bar strip above the LCD,
+	// so they don't consume a list row. Kept as a hook in case a game
+	// needs an in-LCD title row.
+	uint listTitleRows() const { return 0; }
+
+	// Layout for the two clickable labels on the Online hub.
+	Common::Rect hubEmailRect() const;
+	Common::Rect hubWebRect() const;
 	void startCallToContact(uint contactIndex);
 	// Visible (deduplicated) row -> raw contact index, or -1.
 	int contactIndexForVisibleRow(uint visibleRow) const;
 	uint deduplicatedContactCount() const;
+	// Entry count for whichever list the popup is currently showing.
+	uint currentListEntryCount() const;
+	// Absolute indices into the current list's backing array that pass
+	// the active filter. Email applies the "Old Email Only" filter
+	// (no-signal -> read messages only); web shows everything.
+	Common::Array<uint> listVisibleIndices() const;
 	// True when the contact's visibility flag is currently unlocked.
 	bool isContactVisible(const UICL::Contact &c) const;
 	// Popup-local rect of the Back hotspot in directory mode.
@@ -144,6 +199,17 @@ private:
 	uint _directoryScroll = 0;
 	uint _directorySelection = 0;
 
+	// Content-view (single email / page) state.
+	ScreenState _contentReturnState = kOnlineHub;
+	const UICL::SrcDestRectPair *_contentHeading = nullptr;
+	Common::String _contentKey;
+	uint _contentScroll = 0;
+
+	// In-page hyperlinks: rects (popup-local, recomputed every draw) and
+	// the target CVTX key parsed from each <H>...<L> region of the body.
+	Common::Array<Common::Rect> _contentHotspots;
+	Common::Array<Common::String> _contentHotspotTargets;
+
 	bool _noSignal = false;
 	bool _batteryLow = false;
 };


Commit: 650c5cf32a8ed8c788a734686ad424c231cd648d
    https://github.com/scummvm/scummvm/commit/650c5cf32a8ed8c788a734686ad424c231cd648d
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-06-04T03:09:10+03:00

Commit Message:
NANCY: Add new MagnetMazePuzzle to module.mk

Changed paths:
    engines/nancy/module.mk


diff --git a/engines/nancy/module.mk b/engines/nancy/module.mk
index 83f82d210bd..af38b27b11e 100644
--- a/engines/nancy/module.mk
+++ b/engines/nancy/module.mk
@@ -29,6 +29,7 @@ MODULE_OBJS = \
   action/puzzle/gridmappuzzle.o \
   action/puzzle/hamradiopuzzle.o \
   action/puzzle/leverpuzzle.o \
+  action/puzzle/magnetmazepuzzle.o \
   action/puzzle/mazechasepuzzle.o \
   action/puzzle/matchpuzzle.o \
   action/puzzle/memorypuzzle.o \


Commit: dfc12fce980b878a476547516ec84d0abb83c428
    https://github.com/scummvm/scummvm/commit/dfc12fce980b878a476547516ec84d0abb83c428
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-06-04T03:09:12+03:00

Commit Message:
NANCY: More work on the Nancy10+ textbox

Changed paths:
    engines/nancy/action/miscrecords.cpp
    engines/nancy/state/scene.cpp
    engines/nancy/ui/textbox.cpp
    engines/nancy/ui/textbox.h


diff --git a/engines/nancy/action/miscrecords.cpp b/engines/nancy/action/miscrecords.cpp
index 35af709604a..cacb72796aa 100644
--- a/engines/nancy/action/miscrecords.cpp
+++ b/engines/nancy/action/miscrecords.cpp
@@ -186,6 +186,19 @@ void FrameTextBox::execute() {
 	if (!_text.empty()) {
 		tb.addTextLine(_text);
 	}
+
+	// Variant 74 (case 0x4a in ProcessActionRecords) opens the full-width
+	// textbox overlay that covers the taskbar buttons; the original arms a
+	// 15-second timer (DAT_005a7a7d = GetTickCount + 15000) that drops it
+	// back to the closed strip. Variant 75 (case 0x4b) is the legacy
+	// closed/strip path. Variant 81 first appears in Nancy 11; tentatively
+	// route it like 74 until its real semantics are confirmed.
+	if (_variant == kVariant74 || _variant == kVariant81) {
+		tb.setFullMode(true);
+	} else {
+		tb.setFullMode(false);
+	}
+
 	finishExecution();
 }
 
diff --git a/engines/nancy/state/scene.cpp b/engines/nancy/state/scene.cpp
index 2a0352af58d..cb27b1b43cf 100644
--- a/engines/nancy/state/scene.cpp
+++ b/engines/nancy/state/scene.cpp
@@ -1216,9 +1216,11 @@ void Scene::handleInput() {
 
 	_actionManager.handleInput(input);
 
-	// Menu/help are disabled when a movie is active
+	// Menu/help are disabled when a movie is active. The taskbar is also
+	// skipped while the textbox is in open mode (it visually covers the
+	// buttons, so they should not receive hover/clicks).
 	if (!_activeMovie) {
-		if (_taskbar) {
+		if (_taskbar && !_textbox.isFullMode()) {
 			_taskbar->handleInput(input);
 
 			int clicked = _taskbar->getClickedButton();
diff --git a/engines/nancy/ui/textbox.cpp b/engines/nancy/ui/textbox.cpp
index 19e04f70dce..e7e2d7efe40 100644
--- a/engines/nancy/ui/textbox.cpp
+++ b/engines/nancy/ui/textbox.cpp
@@ -41,7 +41,9 @@ Textbox::Textbox() :
 		_scrollbarPos(0),
 		_highlightRObj(g_nancy->getGameType() >= kGameTypeNancy10 ? 11 : 7),
 		_fontIDOverride(-1),
-		_autoClearTime(0) {}
+		_autoClearTime(0),
+		_isFullMode(false),
+		_fullModeCloseTime(0) {}
 
 Textbox::~Textbox() {
 	delete _scrollbar;
@@ -55,30 +57,40 @@ void Textbox::init() {
 		auto *bsum = GetEngineData(BSUM);
 		assert(bsum);
 
-		Common::Rect textRect = bsum->textboxScreenPosition;
-
-		// Clip the bottom of the text strip to sit above the taskbar.
+		// The bsum rect spans the full taskbar strip (open mode); the
+		// closed mode clips at the taskbar's top edge so the buttons stay
+		// visible. We keep both rects so setFullMode() can toggle between
+		// them without reallocating.
+		_openRect = bsum->textboxScreenPosition;
+		_closedRect = _openRect;
 		const TASK *taskData = GetEngineData(TASK);
-		if (taskData && taskData->dstRect.top > textRect.top &&
-				taskData->dstRect.top < textRect.bottom) {
-			textRect.bottom = taskData->dstRect.top;
+		if (taskData && taskData->dstRect.top > _closedRect.top &&
+				taskData->dstRect.top < _closedRect.bottom) {
+			_closedRect.bottom = taskData->dstRect.top;
 		}
 
-		moveTo(textRect);
-		_highlightRObj.moveTo(textRect);
-
-		// No scrolling for now: the surface is sized to exactly the
-		// visible text rect, so overflow simply clips at the bottom.
-		initSurfaces(textRect.width(), textRect.height(),
+		// Size the backing surface to the OPEN rect so toggling never
+		// reallocates. The visible slice is constrained via _drawSurface.
+		initSurfaces(_openRect.width(), _openRect.height(),
 			g_nancy->_graphics->getScreenPixelFormat(),
 			tbox->textBackground, tbox->highlightTextBackground);
 
-		Common::Rect outerBoundingBox = _screenPosition;
-		outerBoundingBox.moveTo(0, 0);
-		_drawSurface.create(_fullSurface, outerBoundingBox);
+		_isFullMode = false;
+		applyDisplayMode();
 
 		RenderObject::init();
 
+		// Nancy 11+ scrollbar for the OPEN-mode text overflow. Nancy 10
+		// has no scrollbar in either state; left intentionally nullptr.
+		if (g_nancy->getGameType() >= kGameTypeNancy11
+				&& tbox->scrollbarSrcBounds.width() > 0) {
+			_scrollbar = new Scrollbar(11,
+				tbox->scrollbarSrcBounds,
+				tbox->scrollbarDefaultPos,
+				tbox->scrollbarMaxScroll - tbox->scrollbarDefaultPos.y);
+			_scrollbar->init();
+		}
+
 		setVisible(false);
 		return;
 	}
@@ -117,6 +129,13 @@ void Textbox::updateGraphics() {
 	if (_autoClearTime && g_nancy->getTotalPlayTime() > _autoClearTime)
 		clear();
 
+	// Nancy 10+ open-mode auto-close timer (mirrors the DAT_005a7a7d
+	// check in case 0x4a of ProcessActionRecords).
+	if (_isFullMode && _fullModeCloseTime &&
+			g_nancy->getTotalPlayTime() > _fullModeCloseTime) {
+		setFullMode(false);
+	}
+
 	if (_needsTextRedraw)
 		drawTextbox();
 
@@ -129,6 +148,48 @@ void Textbox::updateGraphics() {
 	RenderObject::updateGraphics();
 }
 
+void Textbox::applyDisplayMode() {
+	if (g_nancy->getGameType() < kGameTypeNancy10) {
+		return;
+	}
+
+	const Common::Rect &target = _isFullMode ? _openRect : _closedRect;
+	moveTo(target);
+	_highlightRObj.moveTo(target);
+
+	Common::Rect outerBoundingBox = target;
+	outerBoundingBox.moveTo(0, 0);
+	_drawSurface.create(_fullSurface, outerBoundingBox);
+
+	// Nancy 11+ scrollbar only makes sense in open mode; hide it in
+	// closed mode regardless of game version.
+	if (_scrollbar) {
+		_scrollbar->setVisible(_isFullMode);
+	}
+
+	_needsTextRedraw = true;
+	_needsRedraw = true;
+}
+
+void Textbox::setFullMode(bool open, uint32 timeoutMs) {
+	if (g_nancy->getGameType() < kGameTypeNancy10) {
+		return;
+	}
+	if (_isFullMode == open) {
+		// Re-arm the timer on a repeat open call.
+		if (open && timeoutMs) {
+			_fullModeCloseTime = g_nancy->getTotalPlayTime() + timeoutMs;
+		}
+		return;
+	}
+
+	_isFullMode = open;
+	_fullModeCloseTime = (open && timeoutMs)
+			? g_nancy->getTotalPlayTime() + timeoutMs
+			: 0;
+	applyDisplayMode();
+}
+
 void Textbox::handleInput(NancyInput &input) {
 	if (_scrollbar)
 		_scrollbar->handleInput(input);
@@ -207,9 +268,15 @@ void Textbox::clear() {
 		_autoClearTime = 0;
 
 		// Nancy 10+: the text strip overlaps the taskbar buttons, so
-		// hide it whenever it has no content to show.
-		if (g_nancy->getGameType() >= kGameTypeNancy10)
+		// hide it whenever it has no content to show. Also drop the
+		// open-mode overlay so the next entry starts fresh in closed
+		// mode unless a kVariant74 AR re-opens it.
+		if (g_nancy->getGameType() >= kGameTypeNancy10) {
+			if (_isFullMode) {
+				setFullMode(false);
+			}
 			setVisible(false);
+		}
 	}
 }
 
diff --git a/engines/nancy/ui/textbox.h b/engines/nancy/ui/textbox.h
index ed6b92417a2..c4db8b68f12 100644
--- a/engines/nancy/ui/textbox.h
+++ b/engines/nancy/ui/textbox.h
@@ -51,9 +51,22 @@ public:
 	void addTextLine(const Common::String &text, uint32 autoClearTime = 0);
 	void setOverrideFont(const uint fontID);
 
+	// Nancy 10+ open/full mode. When `open` is true the textbox extends
+	// down to cover the taskbar buttons; `timeoutMs` schedules an automatic
+	// return to closed mode Passing 0 disables the auto-close (the caller is
+	// then responsible for closing). No-op for pre-Nancy 10 games.
+	void setFullMode(bool open, uint32 timeoutMs = 15000);
+	bool isFullMode() const { return _isFullMode; }
+
 private:
+	enum DisplayMode {
+		kModeClosed = 0,
+		kModeOpen   = 1
+	};
+
 	uint16 getInnerHeight() const;
 	void onScrollbarMove();
+	void applyDisplayMode();
 
 	RenderObject _highlightRObj;
 	Scrollbar *_scrollbar;
@@ -62,6 +75,14 @@ private:
 
 	uint32 _autoClearTime;
 	int _fontIDOverride;
+
+	// Nancy 10+ open/closed strip geometry. `_closedRect` is the small
+	// taskbar-clipped strip; `_openRect` is the full bsum textbox area
+	// (extends through the taskbar zone).
+	bool _isFullMode;
+	uint32 _fullModeCloseTime;
+	Common::Rect _closedRect;
+	Common::Rect _openRect;
 };
 
 } // End of namespace UI


Commit: 5d21ea03202b0cd39d3c5d9f11894f7137d24bda
    https://github.com/scummvm/scummvm/commit/5d21ea03202b0cd39d3c5d9f11894f7137d24bda
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-06-04T03:09:15+03:00

Commit Message:
NANCY: Remove superfluous parentheses

Changed paths:
    engines/nancy/action/overlay.cpp


diff --git a/engines/nancy/action/overlay.cpp b/engines/nancy/action/overlay.cpp
index 0af996c2a7e..e291d3940b6 100644
--- a/engines/nancy/action/overlay.cpp
+++ b/engines/nancy/action/overlay.cpp
@@ -157,9 +157,9 @@ void Overlay::execute() {
 			// Wait until sound stops (if present)
 			if (!g_nancy->_sound->isSoundPlaying(_sound)) {
 				// Check if we're at the last frame
-				if ((_currentFrame == _loopLastFrame) && (_playDirection == kPlayOverlayForward) && (_loop == kPlayOverlayOnce)) {
+				if (_currentFrame == _loopLastFrame && _playDirection == kPlayOverlayForward && _loop == kPlayOverlayOnce) {
 					shouldTrigger = true;
-				} else if ((_currentFrame == _loopFirstFrame) && (_playDirection == kPlayOverlayReverse) && (_loop == kPlayOverlayOnce)) {
+				} else if (_currentFrame == _loopFirstFrame && _playDirection == kPlayOverlayReverse && _loop == kPlayOverlayOnce) {
 					shouldTrigger = true;
 				}
 			}


Commit: 1a68dd3f18072ac97810d90ab574bb7c0e2228e2
    https://github.com/scummvm/scummvm/commit/1a68dd3f18072ac97810d90ab574bb7c0e2228e2
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-06-04T03:09:16+03:00

Commit Message:
NANCY: Correct reading of TBOX chunk data for Nancy10+

Changed paths:
    engines/nancy/enginedata.cpp


diff --git a/engines/nancy/enginedata.cpp b/engines/nancy/enginedata.cpp
index c4df40aa807..482e0c69890 100644
--- a/engines/nancy/enginedata.cpp
+++ b/engines/nancy/enginedata.cpp
@@ -278,10 +278,13 @@ TBOX::TBOX(Common::SeekableReadStream *chunkStream) : EngineData(chunkStream) {
 	defaultTextColor = chunkStream->readUint16LE();
 
 	if (g_nancy->getGameType() >= kGameTypeNancy10) {
-		// Nancy 10+ moved the conversation font IDs to a later position;
-		// the 4 bytes here are unrelated, so skip them.
-		chunkStream->skip(4);
-	} else if (g_nancy->getGameType() >= kGameTypeNancy2) {
+		chunkStream->skip(2);	// line-start X cursor
+		chunkStream->skip(2);	// bottom margin
+		chunkStream->skip(2);	// unused
+		chunkStream->skip(2);	// initial color (we use the AR for this)
+	}
+
+	if (g_nancy->getGameType() >= kGameTypeNancy2) {
 		conversationFontID = chunkStream->readUint16LE();
 		highlightConversationFontID = chunkStream->readUint16LE();
 	} else {
@@ -292,12 +295,6 @@ TBOX::TBOX(Common::SeekableReadStream *chunkStream) : EngineData(chunkStream) {
 	tabWidth = chunkStream->readUint16LE();
 	pageScrollPercent = chunkStream->readUint16LE(); // Not implemented yet
 
-	if (g_nancy->getGameType() >= kGameTypeNancy10) {
-		conversationFontID = chunkStream->readUint16LE();
-		highlightConversationFontID = chunkStream->readUint16LE();
-		chunkStream->skip(4); // 2 unknown uint16 fields (values: 4, 75 in nancy10)
-	}
-
 	Graphics::PixelFormat format = g_nancy->_graphics->getInputPixelFormat();
 	if (g_nancy->getGameType() >= kGameTypeNancy2) {
 		byte r, g, b;


Commit: f82acefa44e55675ad03b153eb0b2331f4ef7069
    https://github.com/scummvm/scummvm/commit/f82acefa44e55675ad03b153eb0b2331f4ef7069
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-06-04T03:09:18+03:00

Commit Message:
NANCY: Update inactive animated overlays

In Nancy8, when meeting Miles for the first time, there's an animation
of his flashing lights that stops when he's done talking. After this,
his animated overlay stays inactive and is no longer updated. Update
the overlay in such cases, to adapt it to the current viewport frame.

Fix #16694 and part of #16723

Changed paths:
    engines/nancy/action/overlay.cpp
    engines/nancy/action/overlay.h


diff --git a/engines/nancy/action/overlay.cpp b/engines/nancy/action/overlay.cpp
index e291d3940b6..f4da96d9787 100644
--- a/engines/nancy/action/overlay.cpp
+++ b/engines/nancy/action/overlay.cpp
@@ -73,6 +73,26 @@ void Overlay::handleInput(NancyInput &input) {
 	}
 }
 
+void Overlay::updateGraphics() {
+	// Update inactive animated overlays
+	if (!_isActive && _state == kRun && !_blitDescriptions.empty() && _overlayType == kPlayOverlayAnimated) {
+		uint16 newFrame = NancySceneState.getSceneInfo().frameID;
+		if (_currentViewportFrame == newFrame)
+			return;
+
+		_currentViewportFrame = (int16)newFrame;
+		setVisible(false);
+
+		for (auto &blit : _blitDescriptions) {
+			if (_currentViewportFrame == blit.frameID) {
+				moveTo(blit.dest);
+				setVisible(true);
+				break;
+			}
+		}
+	}
+}
+
 void Overlay::readData(Common::SeekableReadStream &stream) {
 	Common::Serializer ser(&stream, nullptr);
 	ser.setVersion(g_nancy->getGameType());
diff --git a/engines/nancy/action/overlay.h b/engines/nancy/action/overlay.h
index 3ae78e8f549..888f666ab1d 100644
--- a/engines/nancy/action/overlay.h
+++ b/engines/nancy/action/overlay.h
@@ -48,6 +48,7 @@ public:
 
 	void init() override;
 	void handleInput(NancyInput &input) override;
+	void updateGraphics() override;
 
 	void readData(Common::SeekableReadStream &stream) override;
 	void execute() override;



From noreply at scummvm.org  Thu Jun  4 01:35:23 2026
From: noreply at scummvm.org (dreammaster)
Date: Thu,  4 Jun 2026 01:35:23 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 83fe65b2782b28788bbc56ddb8a337895b1ccacd
Message-ID: <20260604013523.28C73700573@ron.scummvm.net>

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:
50471cb1ca MADS: DRAGONSPHERE: Disable interface immediately on conv start
83fe65b278 MADS: DRAGONSPHERE: Fix reaeching castle at endgame


Commit: 50471cb1ca4e6705f1fd7681872b592aac82181b
    https://github.com/scummvm/scummvm/commit/50471cb1ca4e6705f1fd7681872b592aac82181b
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-06-04T11:35:14+10:00

Commit Message:
MADS: DRAGONSPHERE: Disable interface immediately on conv start

Changed paths:
    engines/mads/madsv2/core/conv.cpp
    engines/mads/madsv2/core/inter.cpp


diff --git a/engines/mads/madsv2/core/conv.cpp b/engines/mads/madsv2/core/conv.cpp
index 13e327046bb..e418a5bc6fb 100644
--- a/engines/mads/madsv2/core/conv.cpp
+++ b/engines/mads/madsv2/core/conv.cpp
@@ -38,7 +38,7 @@
 #include "mads/madsv2/core/mem.h"
 #include "mads/madsv2/core/popup.h"
 #include "mads/madsv2/core/speech.h"
-#include "mads/mads.h"
+#include "mads/madsv2/engine.h"
 
 namespace MADS {
 namespace MADSV2 {
@@ -1312,6 +1312,11 @@ void conv_run(int convId) {
 		kernel_new_palette();
 
 	player.commands_allowed = 0;
+
+	if (g_engine->getGameID() == GType_Dragonsphere) {
+		kernel_init_dialog();
+		kernel_set_interface_mode(INTER_CONVERSATION);
+	}
 }
 
 // ---------------------------------------------------------------------------
diff --git a/engines/mads/madsv2/core/inter.cpp b/engines/mads/madsv2/core/inter.cpp
index 16a0b0bbdd5..246b40aac58 100644
--- a/engines/mads/madsv2/core/inter.cpp
+++ b/engines/mads/madsv2/core/inter.cpp
@@ -1928,16 +1928,15 @@ void inter_spin_object(int object_id) {
 
 	// Update to live screen chunk of interface that selected object
 	// is on (it has been deleted in list by now)
-	if (kernel_mode == KERNEL_ACTIVE_CODE) {
-		video_update(&scr_inter, OUAF_OBJECT_X, 0,
-			OUAF_OBJECT_X, 156,
-			59, 44);
+	if (kernel_mode == KERNEL_ACTIVE_CODE && g_engine->getGameID() == GType_Forest) {
+		video_update(&scr_inter, OUAF_OBJECT_X, 0, OUAF_OBJECT_X, 156, 59, 44);
 	}
 
-	if (!inter_spinning_objects) goto done;
+	if (!inter_spinning_objects)
+		goto done;
 
-	if (inter_input_mode == INTER_BUILDING_SENTENCES ||
-		inter_input_mode == INTER_LIMITED_SENTENCES) inter_screen_update();
+	if (inter_input_mode == INTER_BUILDING_SENTENCES || inter_input_mode == INTER_LIMITED_SENTENCES)
+		inter_screen_update();
 
 	if (inter_object_routine == NULL) {
 		Common::strcpy_s(temp_buf, "*OB");


Commit: 83fe65b2782b28788bbc56ddb8a337895b1ccacd
    https://github.com/scummvm/scummvm/commit/83fe65b2782b28788bbc56ddb8a337895b1ccacd
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-06-04T11:35:14+10:00

Commit Message:
MADS: DRAGONSPHERE: Fix reaeching castle at endgame

Changed paths:
    engines/mads/madsv2/dragonsphere/rooms/room104.cpp
    engines/mads/madsv2/dragonsphere/rooms/room116.cpp


diff --git a/engines/mads/madsv2/dragonsphere/rooms/room104.cpp b/engines/mads/madsv2/dragonsphere/rooms/room104.cpp
index cb455e2362a..a515489d043 100644
--- a/engines/mads/madsv2/dragonsphere/rooms/room104.cpp
+++ b/engines/mads/madsv2/dragonsphere/rooms/room104.cpp
@@ -201,7 +201,7 @@ static void room_104_init() {
 	}
 
 	// Flip MacMorn hotspot off to prevent palette fragmentation
-	kernel_flip_hotspot(350, false);  // words_MacMorn
+	kernel_flip_hotspot(words_MacMorn, false);
 
 	// Zero runtime state on fresh room entry (not when restoring a saved game)
 	if (previous_room != KERNEL_RESTORING_GAME) {
@@ -378,7 +378,7 @@ static void room_104_init() {
 			kernel_dynamic_anim(id, aa[2], 0);
 		}
 
-		kernel_flip_hotspot(350, true);   // words_MacMorn ??? visible now
+		kernel_flip_hotspot(words_MacMorn, true);
 
 		{
 			int16 id = kernel_add_dynamic(291, 13, SYNTAX_MASC_NOT_PROPER,
diff --git a/engines/mads/madsv2/dragonsphere/rooms/room116.cpp b/engines/mads/madsv2/dragonsphere/rooms/room116.cpp
index 347a79d569e..78da0968ace 100644
--- a/engines/mads/madsv2/dragonsphere/rooms/room116.cpp
+++ b/engines/mads/madsv2/dragonsphere/rooms/room116.cpp
@@ -497,7 +497,7 @@ static void room_116_parser() {
 			if (global[king_status] == KING_WITH_SOUL && !global[king_is_in_stairwell]) {
 				kernel_abort_animation(aa[1]);
 				local->anim_1_running = false;
-				aa[0] = kernel_run_animation(kernel_name('B', -1), 0);
+				aa[0] = kernel_run_animation(kernel_name('B', -1), 5);
 				local->animation_running = KING_THRU_DOOR;
 			}
 



From noreply at scummvm.org  Thu Jun  4 07:22:56 2026
From: noreply at scummvm.org (ScummVM-Translations)
Date: Thu,  4 Jun 2026 07:22:56 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 897ab8912e3d78b4e510bb0133b31ee38ef154db
Message-ID: <20260604072256.BCFF7700573@ron.scummvm.net>

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

Summary:
897ab8912e I18N: Update translation (Japanese)


Commit: 897ab8912e3d78b4e510bb0133b31ee38ef154db
    https://github.com/scummvm/scummvm/commit/897ab8912e3d78b4e510bb0133b31ee38ef154db
Author: VAN-Gluon (van.gluonp at gmail.com)
Date: 2026-06-04T07:22:49Z

Commit Message:
I18N: Update translation (Japanese)

Currently translated at 70.4% (2258 of 3204 strings)

Changed paths:
    po/ja.po


diff --git a/po/ja.po b/po/ja.po
index 1c3f46c85a7..52fb857b493 100644
--- a/po/ja.po
+++ b/po/ja.po
@@ -8,7 +8,7 @@ msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
 "POT-Creation-Date: 2026-06-02 16:30+0000\n"
-"PO-Revision-Date: 2026-04-26 07:22+0000\n"
+"PO-Revision-Date: 2026-06-04 07:22+0000\n"
 "Last-Translator: VAN-Gluon <van.gluonp at gmail.com>\n"
 "Language-Team: Japanese <https://translations.scummvm.org/projects/scummvm/"
 "scummvm/ja/>\n"
@@ -6768,8 +6768,6 @@ msgid "Game keymappings"
 msgstr "???????????????????????????"
 
 #: engines/agos/metaengine.cpp:155 engines/lure/metaengine.cpp:172
-#, fuzzy
-#| msgid "Yes/No keymapping"
 msgid "Yes/No keymappings"
 msgstr "Yes / No???????????????"
 
@@ -6787,17 +6785,13 @@ msgstr "BGM??????????????????"
 
 #: engines/agos/metaengine.cpp:194 engines/sherlock/metaengine.cpp:687
 #: engines/ultima/nuvie/metaengine.cpp:101 engines/vcruise/metaengine.cpp:201
-#, fuzzy
-#| msgid "Toggle mute"
 msgid "Toggle music"
-msgstr "???????????????"
+msgstr "BGM?????????"
 
 #: engines/agos/metaengine.cpp:199 engines/sherlock/metaengine.cpp:702
 #: engines/vcruise/metaengine.cpp:206
-#, fuzzy
-#| msgid "Toggle sound effect on/off"
 msgid "Toggle sound effects"
-msgstr "SE ON/OFF?????????"
+msgstr "SE?????????"
 
 #. I18N: Speeds up the game to twice its normal speed
 #: engines/agos/metaengine.cpp:204 engines/queen/metaengine.cpp:192
@@ -6843,10 +6837,8 @@ msgid "Turn right"
 msgstr "??????"
 
 #: engines/agos/metaengine.cpp:241
-#, fuzzy
-#| msgid "Toggle fast mode"
 msgid "Toggle fight mode"
-msgstr "??????????????????????????????"
+msgstr "?????????????????????"
 
 #: engines/agos/metaengine.cpp:250
 msgid "Text speed - Fast"
@@ -6865,10 +6857,8 @@ msgid "Show objects to interact"
 msgstr "???????????????????????????????????????"
 
 #: engines/agos/metaengine.cpp:272
-#, fuzzy
-#| msgid "Toggle background sounds on/off"
 msgid "Toggle background sounds"
-msgstr "????????? ON/OFF?????????"
+msgstr "??????????????????"
 
 #. I18N: Characters are game actors
 #: engines/agos/metaengine.cpp:281
@@ -6876,10 +6866,8 @@ msgid "Switch characters"
 msgstr "????????????????????????"
 
 #: engines/agos/metaengine.cpp:287
-#, fuzzy
-#| msgid "Toggle hitbox names on/off"
 msgid "Toggle hitbox names"
-msgstr "????????? ON/OFF?????????"
+msgstr "????????????????????????"
 
 #: engines/agos/metaengine.cpp:294
 msgid "Switches between speech only and combined speech and subtitles"
@@ -6997,15 +6985,9 @@ msgstr ""
 "????????????????????????"
 
 #: engines/ags/ags.cpp:157
-#, fuzzy
-#| msgid ""
-#| "The selected game uses a pre-2.5 version of the AGS engine, which is not "
-#| "supported."
 msgid ""
 "The selected game uses version 4 of the AGS engine, which is not supported."
-msgstr ""
-"?????????????????????Scumm VM????????????????????????????????????????????????2.5?????????AGS???????????????"
-"????????????????????????"
+msgstr "Scumm VM????????????????????????????????????4???AGS???????????????????????????????????????"
 
 #: engines/ags/ags.cpp:165
 msgid ""
@@ -7089,16 +7071,12 @@ msgid "Old OpenGL detected, some graphical errors will occur."
 msgstr "??????OpenGL??????????????????????????????????????????????????????"
 
 #: engines/alcachofa/metaengine.cpp:40
-#, fuzzy
-#| msgid "Music Quality:"
 msgid "High Quality"
-msgstr "?????????"
+msgstr "?????????"
 
 #: engines/alcachofa/metaengine.cpp:41
-#, fuzzy
-#| msgid "Toggle sound effect on/off"
 msgid "Toggles some optional graphical effects"
-msgstr "SE ON/OFF?????????"
+msgstr "?????????????????????????????????????????????"
 
 #: engines/alcachofa/metaengine.cpp:51
 msgid "32 Bits"
@@ -7109,10 +7087,8 @@ msgid "Whether to render the game in 16-bit color"
 msgstr "????????????16bit??????????????????????????????"
 
 #: engines/alcachofa/metaengine.cpp:62
-#, fuzzy
-#| msgid "Filter graphics"
 msgid "Filter textures"
-msgstr "?????????????????????"
+msgstr "?????????????????????????????????"
 
 #: engines/alcachofa/metaengine.cpp:63
 msgid "Whether textures should be linearly filtered"
@@ -7192,10 +7168,8 @@ msgid "Open character inventory"
 msgstr "???????????????????????????????????????"
 
 #: engines/asylum/metaengine.cpp:226
-#, fuzzy
-#| msgid "Toggle animation on/off"
 msgid "Toggle animation"
-msgstr "????????? ON/OFF?????????"
+msgstr "????????????????????????"
 
 #: engines/asylum/metaengine.cpp:232
 msgid "Return to gameplay"



From noreply at scummvm.org  Thu Jun  4 07:51:14 2026
From: noreply at scummvm.org (kelmer44)
Date: Thu,  4 Jun 2026 07:51:14 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 fa9c5e6b5473f9a4ddec3e9bcb03a7bd0cbf8fa1
Message-ID: <20260604075114.A1CA3700573@ron.scummvm.net>

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

Summary:
fa9c5e6b54 PELROCK: Mark the game as TESTING, build by default.


Commit: fa9c5e6b5473f9a4ddec3e9bcb03a7bd0cbf8fa1
    https://github.com/scummvm/scummvm/commit/fa9c5e6b5473f9a4ddec3e9bcb03a7bd0cbf8fa1
Author: kelmer (kelmer at gmail.com)
Date: 2026-06-04T09:51:06+02:00

Commit Message:
PELROCK: Mark the game as TESTING, build by default.

Changed paths:
    engines/pelrock/configure.engine
    engines/pelrock/detection_tables.h


diff --git a/engines/pelrock/configure.engine b/engines/pelrock/configure.engine
index a4ac40cb140..df86a2957c3 100644
--- a/engines/pelrock/configure.engine
+++ b/engines/pelrock/configure.engine
@@ -1,3 +1,3 @@
 # This file is included from the main "configure" script
 # add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps] [components]
-add_engine pelrock "Pelrock" no "" "" "" ""
+add_engine pelrock "Pelrock" yes "" "" "" ""
diff --git a/engines/pelrock/detection_tables.h b/engines/pelrock/detection_tables.h
index 87467325e96..103e74e38c1 100644
--- a/engines/pelrock/detection_tables.h
+++ b/engines/pelrock/detection_tables.h
@@ -37,7 +37,7 @@ const ADGameDescription gameDescriptions[] = {
 		),
 		Common::ES_ESP,
 		Common::kPlatformDOS,
-		ADGF_UNSTABLE,
+		ADGF_TESTING,
 		GUIO4(GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_ALTERNATE_TIMING, GAMEOPTION_PLAY_INTRO, GAMEOPTION_DISABLE_SCREENSAVER)
 	},
 



From noreply at scummvm.org  Thu Jun  4 10:43:50 2026
From: noreply at scummvm.org (dreammaster)
Date: Thu,  4 Jun 2026 10:43:50 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 c236ac5c292572adac8cec2bed83b5e61600055f
Message-ID: <20260604104350.998D9700573@ron.scummvm.net>

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:
ea1b1e96fb MM: MM1: Fix stale view close handling
3ab2933001 MM: MM1: Use DOS spell result formatting
c236ac5c29 MM: MM1: Fix targeted spell casting crash


Commit: ea1b1e96fb6a93f43aeadf52b51df788008365ec
    https://github.com/scummvm/scummvm/commit/ea1b1e96fb6a93f43aeadf52b51df788008365ec
Author: Scorp (scorp at mrs.mn)
Date: 2026-06-04T20:43:44+10:00

Commit Message:
MM: MM1: Fix stale view close handling

Changed paths:
    engines/mm/mm1/events.cpp


diff --git a/engines/mm/mm1/events.cpp b/engines/mm/mm1/events.cpp
index 6f5499f112f..197e6823d54 100644
--- a/engines/mm/mm1/events.cpp
+++ b/engines/mm/mm1/events.cpp
@@ -134,7 +134,7 @@ void Events::replaceView(UIElement *ui, bool replaceAllViews) {
 }
 
 void Events::replaceView(const Common::String &name, bool replaceAllViews) {
-	replaceView(findView(name));
+	replaceView(findView(name), replaceAllViews);
 }
 
 void Events::addView(UIElement *ui) {
@@ -272,8 +272,10 @@ void UIElement::focus() {
 }
 
 void UIElement::close() {
-	assert(g_engine->focusedView() == this);
-	g_engine->popView();
+	if (g_events->focusedView() != this)
+		return;
+
+	g_events->popView();
 }
 
 bool UIElement::isFocused() const {


Commit: 3ab2933001b44af4dddee6f7eee09738f34afd71
    https://github.com/scummvm/scummvm/commit/3ab2933001b44af4dddee6f7eee09738f34afd71
Author: Scorp (scorp at mrs.mn)
Date: 2026-06-04T20:43:44+10:00

Commit Message:
MM: MM1: Use DOS spell result formatting

Changed paths:
    engines/mm/mm1/game/combat.cpp
    engines/mm/mm1/game/spell_casting.cpp
    engines/mm/mm1/game/spell_casting.h
    engines/mm/mm1/views/spells/cast_spell.cpp
    engines/mm/mm1/views/spells/spell_view.cpp


diff --git a/engines/mm/mm1/game/combat.cpp b/engines/mm/mm1/game/combat.cpp
index 7a25411c34c..3da28dc6cfa 100644
--- a/engines/mm/mm1/game/combat.cpp
+++ b/engines/mm/mm1/game/combat.cpp
@@ -20,6 +20,7 @@
  */
 
 #include "mm/mm1/game/combat.h"
+#include "mm/mm1/game/spell_casting.h"
 #include "mm/mm1/globals.h"
 #include "mm/mm1/mm1.h"
 #include "mm/mm1/sound.h"
@@ -1107,8 +1108,7 @@ void Combat::resetDestMonster() {
 void Combat::spellFailed() {
 	g_globals->_combatParty[_currentChar]->_checked = true;
 
-	SoundMessage msg(10, 2, Common::String::format("*** %s ***",
-		STRING["spells.failed"].c_str()));
+	SoundMessage msg(10, 2, SpellCasting::spellResultMessage(STRING["spells.failed"]));
 	msg._delaySeconds = 3;
 	displaySpellResult(msg);
 }
diff --git a/engines/mm/mm1/game/spell_casting.cpp b/engines/mm/mm1/game/spell_casting.cpp
index b4d1d43e56d..98c880bc699 100644
--- a/engines/mm/mm1/game/spell_casting.cpp
+++ b/engines/mm/mm1/game/spell_casting.cpp
@@ -179,7 +179,7 @@ Common::String SpellCasting::getSpellError() const {
 	}
 
 	if (!isInCombat())
-		msg = Common::String::format("*** %s ***", msg.c_str());
+		msg = spellResultMessage(msg);
 	return msg;
 }
 
@@ -187,6 +187,10 @@ bool SpellCasting::isInCombat() const {
 	return g_events->isPresent("Combat");
 }
 
+Common::String SpellCasting::spellResultMessage(const Common::String &msg) {
+	return Common::String::format("*** %s ***", msg.c_str());
+}
+
 } // namespace Game
 } // namespace MM1
 } // namespace MM
diff --git a/engines/mm/mm1/game/spell_casting.h b/engines/mm/mm1/game/spell_casting.h
index d4a53e8bae0..340b955aca3 100644
--- a/engines/mm/mm1/game/spell_casting.h
+++ b/engines/mm/mm1/game/spell_casting.h
@@ -98,6 +98,11 @@ public:
 	 * Returns the error message
 	 */
 	Common::String getSpellError() const;
+
+	/**
+	 * Returns a spell result message formatted as in the DOS executable.
+	 */
+	static Common::String spellResultMessage(const Common::String &msg);
 };
 
 } // namespace Game
diff --git a/engines/mm/mm1/views/spells/cast_spell.cpp b/engines/mm/mm1/views/spells/cast_spell.cpp
index b64c8d6dc61..bdd54165b0b 100644
--- a/engines/mm/mm1/views/spells/cast_spell.cpp
+++ b/engines/mm/mm1/views/spells/cast_spell.cpp
@@ -241,16 +241,20 @@ void CastSpell::spellDone() {
 }
 
 void CastSpell::spellDone(const Common::String &msg, int xp) {
+	Common::String result = msg.hasPrefix("*** ") ? msg :
+		spellResultMessage(msg);
+	xp = 20 - (result.size() / 2);
+
 	if (isInCombat()) {
 		close();
 
-		GameMessage gameMsg("SPELL_RESULT", msg);
+		GameMessage gameMsg("SPELL_RESULT", result);
 		gameMsg._value = xp;
 		g_events->focusedView()->send(gameMsg);
 
 	} else {
 		Sound::sound(SOUND_2);
-		_spellResult = msg;
+		_spellResult = result;
 		_spellResultX = xp;
 		setState(ENDING);
 	}
diff --git a/engines/mm/mm1/views/spells/spell_view.cpp b/engines/mm/mm1/views/spells/spell_view.cpp
index ca74c73f98c..efdc79a27a8 100644
--- a/engines/mm/mm1/views/spells/spell_view.cpp
+++ b/engines/mm/mm1/views/spells/spell_view.cpp
@@ -20,6 +20,7 @@
  */
 
 #include "mm/mm1/views/spells/spell_view.h"
+#include "mm/mm1/game/spell_casting.h"
 #include "mm/mm1/globals.h"
 #include "mm/mm1/sound.h"
 
@@ -31,7 +32,7 @@ namespace Spells {
 void SpellView::spellFailed() {
 	// Spell failed
 	clearSurface();
-	writeString(10, 2, STRING["spells.failed"]);
+	writeString(10, 2, Game::SpellCasting::spellResultMessage(STRING["spells.failed"]));
 
 	Sound::sound(SOUND_2);
 	delaySeconds(3);
@@ -39,7 +40,7 @@ void SpellView::spellFailed() {
 
 void SpellView::spellDone() {
 	clearSurface();
-	writeString(14, 2, STRING["spells.done"]);
+	writeString(14, 2, Game::SpellCasting::spellResultMessage(STRING["spells.done"]));
 	g_globals->_party.updateAC();
 
 	Sound::sound(SOUND_2);


Commit: c236ac5c292572adac8cec2bed83b5e61600055f
    https://github.com/scummvm/scummvm/commit/c236ac5c292572adac8cec2bed83b5e61600055f
Author: Scorp (scorp at mrs.mn)
Date: 2026-06-04T20:43:44+10:00

Commit Message:
MM: MM1: Fix targeted spell casting crash

Changed paths:
    engines/mm/mm1/views/spells/cast_spell.cpp


diff --git a/engines/mm/mm1/views/spells/cast_spell.cpp b/engines/mm/mm1/views/spells/cast_spell.cpp
index bdd54165b0b..7b76203e934 100644
--- a/engines/mm/mm1/views/spells/cast_spell.cpp
+++ b/engines/mm/mm1/views/spells/cast_spell.cpp
@@ -183,7 +183,8 @@ bool CastSpell::msgAction(const ActionMessage &msg) {
 
 	} else if (msg._action == KEYBIND_SELECT) {
 		// Time to execute the spell
-		performSpell();
+		if (_state == PRESS_ENTER)
+			performSpell();
 
 	} else if (_state == SELECT_CHAR &&
 		msg._action >= KEYBIND_VIEW_PARTY1 &&



From noreply at scummvm.org  Thu Jun  4 11:42:04 2026
From: noreply at scummvm.org (dreammaster)
Date: Thu,  4 Jun 2026 11:42:04 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 40e784859a8db62f09f8d445c59982ff1b43fe6e
Message-ID: <20260604114204.87C80700573@ron.scummvm.net>

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:
7cce546f42 MADS: FOREST: Support functions for room 101 init and daemon
40e784859a MADS: FOREST: Implemented remainder of room 101


Commit: 7cce546f42b3d9bb4e6c55f604c9e17f56eefe4c
    https://github.com/scummvm/scummvm/commit/7cce546f42b3d9bb4e6c55f604c9e17f56eefe4c
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-06-04T21:41:31+10:00

Commit Message:
MADS: FOREST: Support functions for room 101 init and daemon

Changed paths:
    engines/mads/madsv2/core/digi.cpp
    engines/mads/madsv2/core/digi.h
    engines/mads/madsv2/core/mouse.cpp
    engines/mads/madsv2/core/mouse.h
    engines/mads/madsv2/forest/global.cpp
    engines/mads/madsv2/forest/global.h
    engines/mads/madsv2/forest/rooms/room101.cpp


diff --git a/engines/mads/madsv2/core/digi.cpp b/engines/mads/madsv2/core/digi.cpp
index 49cbb8c55c4..bc683eb315b 100644
--- a/engines/mads/madsv2/core/digi.cpp
+++ b/engines/mads/madsv2/core/digi.cpp
@@ -25,7 +25,10 @@
 namespace MADS {
 namespace MADSV2 {
 
+bool digi_flag1;
+
 void digi_install() {
+	digi_flag1 = false;
 }
 
 void digi_uninstall() {
diff --git a/engines/mads/madsv2/core/digi.h b/engines/mads/madsv2/core/digi.h
index 69cb647d13e..e8795ec3db6 100644
--- a/engines/mads/madsv2/core/digi.h
+++ b/engines/mads/madsv2/core/digi.h
@@ -27,6 +27,8 @@
 namespace MADS {
 namespace MADSV2 {
 
+extern bool digi_flag1;
+
 extern void digi_install();
 extern void digi_play(const char *name, int slot);
 extern void digi_play_build(int room, char thing, int num, int slot);
diff --git a/engines/mads/madsv2/core/mouse.cpp b/engines/mads/madsv2/core/mouse.cpp
index e8cb0d6bf0f..83d11e75f15 100644
--- a/engines/mads/madsv2/core/mouse.cpp
+++ b/engines/mads/madsv2/core/mouse.cpp
@@ -45,6 +45,7 @@ int mouse_old_x = 0;
 int mouse_old_y = 0;
 long mouse_clock = 0;
 byte mouse_showing = 0;
+byte mouse_hidden = true;
 int mouse_video_mode = 0;
 
 
@@ -56,10 +57,15 @@ int mouse_init(int driverflag, int videomode) {
 void mouse_show() {
 	if (!CursorMan.isVisible())
 		CursorMan.showMouse(true);
+
+	mouse_showing = true;
+	mouse_hidden = false;
 }
 
 void mouse_hide() {
 	CursorMan.showMouse(false);
+	mouse_showing = false;
+	mouse_hidden = true;
 }
 
 void mouse_force(int x, int y) {
diff --git a/engines/mads/madsv2/core/mouse.h b/engines/mads/madsv2/core/mouse.h
index 59a3a97e298..7faf48725d7 100644
--- a/engines/mads/madsv2/core/mouse.h
+++ b/engines/mads/madsv2/core/mouse.h
@@ -38,6 +38,7 @@ extern word mouse_driver;       /* True if mouse driver is currently active  */
 extern word mouse_known_mode;   /* True if mouse in a 320x200 graphics mode  */
 extern int  mouse_video_mode;   /* Rom BIOS video mode # of current cursor   */
 extern byte mouse_showing;      /* Mouse cursor showing status (0 = show)    */
+extern byte mouse_hidden;
 
 extern int mouse_button;        /* Last button pressed (0 = left, 1 = right) */
 extern int mouse_status;        /* Button status flags                       */
diff --git a/engines/mads/madsv2/forest/global.cpp b/engines/mads/madsv2/forest/global.cpp
index 449173192f0..eeb31fdb4ee 100644
--- a/engines/mads/madsv2/forest/global.cpp
+++ b/engines/mads/madsv2/forest/global.cpp
@@ -23,6 +23,7 @@
 #include "mads/madsv2/core/digi.h"
 #include "mads/madsv2/core/error.h"
 #include "mads/madsv2/core/game.h"
+#include "mads/madsv2/core/imath.h"
 #include "mads/madsv2/core/kernel.h"
 #include "mads/madsv2/core/midi.h"
 #include "mads/madsv2/forest/global.h"
@@ -167,6 +168,704 @@ void sync_room(Common::Serializer &s) {
 	}
 }
 
+static void global_anim1_1(int arg_0, int arg_2, int16 *arg_4) {
+	if (kernel_anim[arg_0].frame == *arg_4)
+		return;
+
+	*arg_4 = kernel_anim[arg_0].frame;
+	int16 var_4 = -1;
+
+	if (arg_2 == 0) {
+		var_4 = 0;
+	} else {
+		switch (*arg_4) {
+		case 1:
+			if (global[g133] == 1)
+				var_4 = 0;
+			break;
+
+		case 2:
+		case 17:
+			if (global[g133] == 0) {
+				var_4 = 1;
+				global[g134]++;
+				if (global[g134] > 17) {
+					var_4 = imath_random(1, 2);
+					global[g134] = 0;
+				}
+			} else if (global[g133] == 1) {
+				var_4 = 1;
+				global[g136] = -1;
+			}
+			break;
+
+		case 6:
+		case 8:
+		case 13:
+			if (global[g133] == 0) {
+				global[g134]++;
+				if (imath_random(5, 10) >= global[g134]) {
+					if (*arg_4 == 8)
+						var_4 = 6;
+					else
+						var_4 = *arg_4 - 1;
+				} else {
+					int16 pick = imath_random(1, 4) - 1;
+					if (pick == 0)      var_4 = 5;
+					else if (pick == 1) var_4 = 7;
+					else if (pick == 2) var_4 = 8;
+					else if (pick == 3) var_4 = 13;
+					if (*arg_4 == 13)
+						var_4 = 7;
+					global[g134] = 0;
+				}
+			} else if (global[g133] == 1) {
+				var_4 = 13;
+			}
+			break;
+
+		case 10:
+			if (global[g133] == 0) {
+				global[g134]++;
+				if (imath_random(5, 10) >= global[g134]) {
+					var_4 = *arg_4 - 1;
+				} else {
+					var_4 = imath_random(9, 10);
+					global[g134] = 0;
+				}
+			} else if (global[g133] == 1) {
+				var_4 = 10;
+			}
+			break;
+
+		default:
+			break;
+		}
+	}
+
+	if (var_4 >= 0) {
+		kernel_reset_animation(arg_0, var_4);
+		*arg_4 = var_4;
+	}
+}
+
+static void global_anim1_2(int arg_0, int arg_2, int16 *arg_4) {
+	if (kernel_anim[arg_0].frame == *arg_4)
+		return;
+
+	*arg_4 = kernel_anim[arg_0].frame;
+	int16 var_2 = -1;
+
+	if (arg_2 == 0) {
+		var_2 = 0;
+	} else {
+		switch (*arg_4) {
+		case 1:
+			if (global[g133] == 1)
+				var_2 = 0;
+			break;
+
+		case 2:
+		case 20:
+			if (global[g133] == 0) {
+				var_2 = 1;
+				global[g134]++;
+				if (global[g134] > 17) {
+					var_2 = imath_random(1, 2);
+					global[g134] = 0;
+				}
+			} else if (global[g133] == 1) {
+				var_2 = 1;
+				global[g136] = -1;
+			}
+			break;
+
+		case 7:
+		case 8:
+		case 9:
+		case 10:
+		case 11:
+		case 12:
+			if (global[g133] == 0) {
+				global[g134]++;
+				if (global[g134] > 17) {
+					var_2 = imath_random(7, 13);
+					if (var_2 == 13)
+						var_2 = 14;
+					global[g134] = 0;
+				} else {
+					var_2 = *arg_4 - 1;
+				}
+			} else if (global[g133] == 1) {
+				var_2 = 14;
+			}
+			break;
+
+		case 14:
+			var_2 = 7;
+			global[g134] = 0;
+			break;
+
+		default:
+			break;
+		}
+	}
+
+	if (var_2 >= 0) {
+		kernel_reset_animation(arg_0, var_2);
+		*arg_4 = var_2;
+	}
+}
+
+static void global_anim1_3(int arg_0, int arg_2, int16 *arg_4) {
+	if (kernel_anim[arg_0].frame == *arg_4)
+		return;
+
+	*arg_4 = kernel_anim[arg_0].frame;
+	int16 var_2 = -1;
+
+	if (arg_2 == 0) {
+		var_2 = 0;
+	} else {
+		switch (*arg_4) {
+		case 1:
+			if (global[g133] == 1)
+				var_2 = 0;
+			break;
+
+		case 2:
+		case 10:
+			if (global[g133] == 0) {
+				var_2 = 1;
+				global[g134]++;
+				if (global[g134] > 17) {
+					var_2 = imath_random(1, 2);
+					global[g134] = 0;
+				}
+			} else if (global[g133] == 1) {
+				var_2 = 1;
+				global[g136] = -1;
+			}
+			break;
+
+		case 5:
+		case 7:
+			if (global[g133] == 0) {
+				global[g134]++;
+				if (imath_random(5, 10) < global[g134]) {
+					var_2 = 7;
+					global[g134] = 0;
+				}
+			} else if (global[g133] == 1) {
+				var_2 = 7;
+			}
+			break;
+
+		default:
+			break;
+		}
+	}
+
+	if (var_2 >= 0) {
+		kernel_reset_animation(arg_0, var_2);
+		*arg_4 = var_2;
+	}
+}
+
+static void global_anim1_4(int arg_0, int arg_2, int16 *arg_4) {
+	if (kernel_anim[arg_0].frame == *arg_4)
+		return;
+
+	*arg_4 = kernel_anim[arg_0].frame;
+	int16 var_2 = -1;
+
+	if (arg_2 == 0) {
+		var_2 = 0;
+	} else {
+		switch (*arg_4) {
+		case 1:
+			if (global[g133] == 1)
+				var_2 = 0;
+			break;
+
+		case 2:
+		case 3:
+		case 4:
+			if (global[g133] == 0) {
+				global[g134]++;
+				if (imath_random(7, 15) >= global[g134]) {
+					var_2 = *arg_4 - 1;
+				} else {
+					var_2 = imath_random(1, 3);
+					global[g134] = 0;
+				}
+			} else if (global[g133] == 1) {
+				var_2 = 4;
+			}
+			break;
+
+		case 5:
+			var_2 = 4;
+			global[g136] = -1;
+			break;
+
+		default:
+			break;
+		}
+	}
+
+	if (var_2 >= 0) {
+		kernel_reset_animation(arg_0, var_2);
+		*arg_4 = var_2;
+	}
+}
+
+static void global_anim1_5(int arg_0, int arg_2, int16 *arg_4) {
+	if (kernel_anim[arg_0].frame == *arg_4)
+		return;
+
+	*arg_4 = kernel_anim[arg_0].frame;
+	int16 var_2 = -1;
+
+	if (arg_2 == 0) {
+		var_2 = 0;
+	} else {
+		switch (*arg_4) {
+		case 1:
+			if (global[g133] == 1)
+				var_2 = 0;
+			break;
+
+		case 2:
+		case 3:
+			if (global[g133] == 0) {
+				global[g134]++;
+				if (imath_random(7, 15) >= global[g134]) {
+					var_2 = *arg_4 - 1;
+				} else {
+					var_2 = imath_random(1, 2);
+					global[g134] = 0;
+				}
+			} else if (global[g133] == 1) {
+				var_2 = 3;
+			}
+			break;
+
+		case 4:
+			var_2 = 3;
+			global[g136] = -1;
+			break;
+
+		default:
+			break;
+		}
+	}
+
+	if (var_2 >= 0) {
+		kernel_reset_animation(arg_0, var_2);
+		*arg_4 = var_2;
+	}
+}
+
+void global_anim1(int arg_0, int arg_2, int arg_4, int16 *arg_6) {
+	if (global[g150]) {
+		global[g135] = -1;
+		global[g145] = -1;
+	}
+
+	if (global[g135])
+		global[g133] = 1;
+
+	if (global[g150]) {
+		if (global[g146] && global[g136]) {
+			global[g146] = 0;
+			global[g136] = 0;
+			global[g145] = 0;
+			global[g135] = 0;
+			global[g150] = 0;
+			kernel_timing_trigger(1, 26);
+		}
+	} else {
+		if (global[g135] && global[g136]) {
+			global[g136] = 0;
+			global[g135] = 0;
+			kernel_timing_trigger(1, 24);
+		}
+	}
+
+	switch (arg_0) {
+	case 1:
+	case 3:
+		global_anim1_1(arg_2, arg_4, arg_6);
+		break;
+	case 2:
+		global_anim1_2(arg_2, arg_4, arg_6);
+		break;
+	case 4:
+	case 6:
+		global_anim1_3(arg_2, arg_4, arg_6);
+		break;
+	case 7:
+	case 9:
+		global_anim1_4(arg_2, arg_4, arg_6);
+		break;
+	case 8:
+		global_anim1_5(arg_2, arg_4, arg_6);
+
+		break;
+	default:
+		break;
+	}
+}
+
+static void global_anim2_1(int arg_0, int arg_2, int16 *arg_4);
+static void global_anim2_2(int arg_0, int arg_2, int16 *arg_4);
+static void global_anim2_3(int arg_0, int arg_2, int16 *arg_4);
+static void global_anim2_4(int arg_0, int arg_2, int16 *arg_4);
+static void global_anim2_5(int arg_0, int arg_2, int16 *arg_4);
+
+void global_anim2(int arg_0, int arg_2, int arg_4, int16 *arg_6) {
+	if (global[g150]) {
+		global[g135] = -1;
+		global[g145] = -1;
+	}
+
+	if (global[g145])
+		global[g143] = 1;
+
+	if (global[g150]) {
+		if (global[g146] && global[g136]) {
+			global[g146] = 0;
+			global[g136] = 0;
+			global[g145] = 0;
+			global[g135] = 0;
+			global[g150] = 0;
+			kernel_timing_trigger(1, 26);
+		}
+	} else {
+		if (global[g145] && global[g146]) {
+			global[g146] = 0;
+			global[g145] = 0;
+			kernel_timing_trigger(1, 25);
+		}
+	}
+
+	switch (arg_0) {
+	case 1:
+	case 3:
+		global_anim2_1(arg_2, arg_4, arg_6);
+		break;
+	case 2:
+		global_anim2_2(arg_2, arg_4, arg_6);
+		break;
+	case 4:
+	case 6:
+		global_anim2_3(arg_2, arg_4, arg_6);
+		break;
+	case 7:
+	case 9:
+		global_anim2_4(arg_2, arg_4, arg_6);
+		break;
+	case 8:
+		global_anim2_5(arg_2, arg_4, arg_6);
+		break;
+	default:
+		break;
+	}
+}
+
+static void global_anim2_1(int arg_0, int arg_2, int16 *arg_4) {
+	if (kernel_anim[arg_0].frame == *arg_4)
+		return;
+
+	*arg_4 = kernel_anim[arg_0].frame;
+	int16 var_4 = -1;
+
+	if (arg_2 == 0) {
+		var_4 = 0;
+	} else {
+		switch (*arg_4) {
+		case 1:
+			if (global[g143] == 1)
+				var_4 = 0;
+			break;
+
+		case 2:
+		case 3:
+		case 11:
+			if (global[g143] == 0) {
+				global[g144]++;
+				if (imath_random(10, 17) >= global[g144]) {
+					if (*arg_4 == 11)
+						var_4 = 1;
+					else
+						var_4 = *arg_4 - 1;
+				} else {
+					var_4 = imath_random(1, 3);
+					global[g144] = 0;
+				}
+			} else if (global[g143] == 1) {
+				var_4 = 11;
+			}
+			break;
+
+		case 5:
+		case 6:
+		case 8:
+			if (global[g143] == 0) {
+				global[g144]++;
+				if (imath_random(1, 3) >= global[g144]) {
+					if (*arg_4 == 8)
+						var_4 = 6;
+					else if (*arg_4 == 5)
+						var_4 = 5;
+					else
+						var_4 = *arg_4 - 1;
+				} else {
+					int16 pick = imath_random(1, 3) - 1;
+					if (pick == 0)      var_4 = 5;
+					else if (pick == 1) var_4 = 6;
+					else if (pick == 2) var_4 = 8;
+					global[g144] = 0;
+				}
+			} else if (global[g143] == 1) {
+				global[g144] = 0;
+				var_4 = 8;
+			}
+			break;
+
+		case 10:
+			global[g144] = 0;
+			break;
+
+		case 12:
+			var_4 = 11;
+			global[g146] = -1;
+			break;
+
+		default:
+			break;
+		}
+	}
+
+	if (var_4 >= 0) {
+		kernel_reset_animation(arg_0, var_4);
+		*arg_4 = var_4;
+	}
+}
+
+static void global_anim2_2(int arg_0, int arg_2, int16 *arg_4) {
+	if (kernel_anim[arg_0].frame == *arg_4)
+		return;
+
+	*arg_4 = kernel_anim[arg_0].frame;
+	int16 var_2 = -1;
+
+	if (arg_2 == 0) {
+		var_2 = 0;
+	} else {
+		switch (*arg_4) {
+		case 1:
+			if (global[g143] == 1)
+				var_2 = 0;
+			break;
+
+		case 2:
+		case 15:
+			if (global[g143] == 0) {
+				var_2 = 1;
+				global[g144]++;
+				if (global[g144] > 17) {
+					var_2 = imath_random(1, 2);
+					global[g144] = 0;
+				}
+			} else if (global[g143] == 1) {
+				var_2 = 15;
+			}
+			break;
+
+		case 7:
+			digi_play_build_ii('_', 2, 3);
+			break;
+
+		case 8:
+		case 9:
+		case 10:
+		case 11:
+			if (global[g143] == 0) {
+				global[g144]++;
+				if (imath_random(5, 10) >= global[g144]) {
+					var_2 = imath_random(8, 10);
+				} else {
+					var_2 = imath_random(8, 11);
+					global[g144] = 0;
+				}
+			} else if (global[g143] == 1) {
+				var_2 = 11;
+			}
+			break;
+
+		case 16:
+			var_2 = 15;
+			global[g146] = -1;
+			break;
+
+		default:
+			break;
+		}
+	}
+
+	if (var_2 >= 0) {
+		kernel_reset_animation(arg_0, var_2);
+		*arg_4 = var_2;
+	}
+}
+
+static void global_anim2_3(int arg_0, int arg_2, int16 *arg_4) {
+	if (kernel_anim[arg_0].frame == *arg_4)
+		return;
+
+	*arg_4 = kernel_anim[arg_0].frame;
+	int16 var_2 = -1;
+
+	if (arg_2 == 0) {
+		var_2 = 0;
+	} else {
+		switch (*arg_4) {
+		case 1:
+			if (global[g143] == 1)
+				var_2 = 0;
+			break;
+
+		case 2:
+		case 8:
+			if (global[g143] == 0) {
+				var_2 = 1;
+				global[g144]++;
+				if (imath_random(15, 20) < global[g144]) {
+					var_2 = imath_random(1, 2);
+					global[g144] = 0;
+				}
+			} else if (global[g143] == 1) {
+				var_2 = 8;
+			}
+			break;
+
+		case 9:
+			var_2 = 8;
+			global[g146] = -1;
+			break;
+
+		default:
+			break;
+		}
+	}
+
+	if (var_2 >= 0) {
+		kernel_reset_animation(arg_0, var_2);
+		*arg_4 = var_2;
+	}
+}
+
+static void global_anim2_4(int arg_0, int arg_2, int16 *arg_4) {
+	if (kernel_anim[arg_0].frame == *arg_4)
+		return;
+
+	*arg_4 = kernel_anim[arg_0].frame;
+	int16 var_2 = -1;
+
+	if (arg_2 == 0) {
+		var_2 = 0;
+	} else {
+		switch (*arg_4) {
+		case 1:
+			if (global[g143] == 1)
+				var_2 = 0;
+			break;
+
+		case 2:
+		case 7:
+			if (global[g143] == 0) {
+				global[g144]++;
+				if (imath_random(7, 15) >= global[g144]) {
+					var_2 = 1;
+				} else {
+					var_2 = imath_random(1, 2);
+					global[g144] = 0;
+				}
+			} else if (global[g143] == 1) {
+				var_2 = 7;
+			}
+			break;
+
+		case 5:
+			if (imath_random(1, 2) == 1)
+				var_2 = 3;
+			break;
+
+		case 8:
+			var_2 = 7;
+			global[g146] = -1;
+			break;
+
+		default:
+			break;
+		}
+	}
+
+	if (var_2 >= 0) {
+		kernel_reset_animation(arg_0, var_2);
+		*arg_4 = var_2;
+	}
+}
+
+static void global_anim2_5(int arg_0, int arg_2, int16 *arg_4) {
+	if (kernel_anim[arg_0].frame == *arg_4)
+		return;
+
+	*arg_4 = kernel_anim[arg_0].frame;
+	int16 var_2 = -1;
+
+	if (arg_2 == 0) {
+		var_2 = 0;
+	} else {
+		switch (*arg_4) {
+		case 1:
+			if (global[g143] == 1)
+				var_2 = 0;
+			break;
+
+		case 2:
+		case 3:
+			if (global[g143] == 0) {
+				global[g144]++;
+				if (imath_random(7, 15) >= global[g144]) {
+					var_2 = *arg_4 - 1;
+				} else {
+					var_2 = imath_random(1, 2);
+					global[g144] = 0;
+				}
+			} else if (global[g143] == 1) {
+				var_2 = 3;
+			}
+			break;
+
+		case 4:
+			var_2 = 3;
+			global[g146] = -1;
+			break;
+
+		default:
+			break;
+		}
+	}
+
+	if (var_2 >= 0) {
+		kernel_reset_animation(arg_0, var_2);
+		*arg_4 = var_2;
+	}
+}
+
 void global_digi_play(int num) {
 	// TODO
 	warning("TODO: global_digi_play");
diff --git a/engines/mads/madsv2/forest/global.h b/engines/mads/madsv2/forest/global.h
index 52f035909e7..286505cc4d0 100644
--- a/engines/mads/madsv2/forest/global.h
+++ b/engines/mads/madsv2/forest/global.h
@@ -33,7 +33,7 @@ enum {
 	walker_timing             =   0,   /* Stop walker timing (long) */
 	walker_timing_2           =   1,
 	inventory_is_displayed    =   2,   /* T if inventory is being displayed */
-	g003                      =   3,
+	player_hyperwalked        =   3,
 	player_selected_object    =   4,
 	walker_converse_state     =   5,
 	walker_converse_now       =   6,
@@ -248,6 +248,8 @@ extern int16 flags[40];
 extern void global_section_constructor();
 extern void sync_room(Common::Serializer &s);
 
+extern void global_anim1(int arg_0, int arg_2, int arg_4, int16 *arg_6);
+extern void global_anim2(int arg_0, int arg_2, int arg_4, int16 *arg_6);
 extern void global_digi_play(int num);
 extern void global_daemon_code();
 
diff --git a/engines/mads/madsv2/forest/rooms/room101.cpp b/engines/mads/madsv2/forest/rooms/room101.cpp
index b1c5c7e0b9b..b7a5928c463 100644
--- a/engines/mads/madsv2/forest/rooms/room101.cpp
+++ b/engines/mads/madsv2/forest/rooms/room101.cpp
@@ -30,6 +30,7 @@
 #include "mads/madsv2/core/mouse.h"
 #include "mads/madsv2/core/object.h"
 #include "mads/madsv2/core/player.h"
+#include "mads/madsv2/core/sprite.h"
 #include "mads/madsv2/core/text.h"
 #include "mads/madsv2/forest/mads/inventory.h"
 #include "mads/madsv2/forest/mads/sounds.h"
@@ -81,9 +82,10 @@ struct Scratch {
 
 static Scratch scratch;
 
-#define ss    scratch.sprite
-#define seq   scratch.sequence
-#define aa    scratch.animation
+#define ss     scratch.sprite
+#define seq    scratch.sequence
+#define aa     scratch.animation
+#define aainfo scratch.array1
 
 static void room_101_init1() {
 	global[g009] = -1;
@@ -288,8 +290,358 @@ static void room_101_init() {
 	}
 }
 
+static void room_101_anim1() {
+	int16 frame = kernel_anim[aa[6]].frame;
+	if (frame == aainfo[6]._val2)
+		return;
+	aainfo[6]._val2 = frame;
+
+	switch (scratch._ac) {
+	case 2:
+		if (frame == 1) {
+			aainfo[6]._val3 = 10;
+			digi_play_build(101, 'b', 1, 1);
+			scratch._aa = 1;
+			return;
+		}
+		if (frame == 6) {
+			if (aainfo[6]._val3 != 10) return;
+			aainfo[6]._val2 = 2;
+			kernel_reset_animation(aa[6], 2);
+			return;
+		}
+		if (frame == 10) {
+			if (aainfo[6]._val3 != 9) return;
+			aainfo[6]._val2 = 6;
+			kernel_reset_animation(aa[6], 6);
+			return;
+		}
+		if (frame == 14) {
+			if (aainfo[6]._val3 != 12) return;
+			aainfo[6]._val2 = 13;
+			kernel_reset_animation(aa[6], 13);
+		}
+		return;
+
+	case 4:
+		if (frame == 10) {
+			aainfo[6]._val3 = 9;
+			digi_play_build(101, 'r', 2, 1);
+			scratch._aa = 10;
+			return;
+		}
+		if (frame == 15) {
+			if (aainfo[6]._val3 != 9) return;
+			aainfo[6]._val2 = 11;
+			kernel_reset_animation(aa[6], 11);
+		}
+		return;
+
+	case 6:
+		if (frame == 7) {
+			aainfo[6]._val3 = 11;
+			digi_play_build(101, 'e', 1, 1);
+			scratch._aa = 20;
+			return;
+		}
+		if (frame == 12) {
+			if (aainfo[6]._val3 != 11) return;
+			aainfo[6]._val2 = 8;
+			kernel_reset_animation(aa[6], 8);
+		}
+		return;
+
+	case 9:
+		if (frame == 28) {
+			aainfo[6]._val3 = 10;
+			digi_play_build(101, 'b', 3, 1);
+			scratch._aa = 30;
+			return;
+		}
+		if (frame == 30) {
+			if (aainfo[6]._val3 != 10) return;
+			aainfo[6]._val2 = 29;
+			kernel_reset_animation(aa[6], 29);
+		}
+		return;
+
+	case 10:
+		if (frame == 88) {
+			aainfo[6]._val3 = 10;
+			digi_play_build(101, 'b', 4, 1);
+			scratch._aa = 40;
+			return;
+		}
+		if (frame == 94) {
+			if (aainfo[6]._val3 != 10) return;
+			aainfo[6]._val2 = 90;
+			kernel_reset_animation(aa[6], 90);
+		}
+		return;
+	}
+}
+
+static void room_101_anim2() {
+	int16 frame = kernel_anim[aa[7]].frame;
+	if (frame == aainfo[7]._val2)
+		return;
+	aainfo[7]._val2 = frame;
+	if (frame != 18) return;
+	if (aainfo[7]._val3 != 7) return;
+	aainfo[7]._val2 = 6;
+	kernel_reset_animation(aa[7], 6);
+}
+
+static void room_101_anim3() {
+	int16 frame = kernel_anim[aa[0]].frame;
+	if (frame != aainfo[0]._val2)
+		aainfo[0]._val2 = frame;
+	if (global[player_hyperwalked] != -1) return;
+	int16 resetFrame = scratch._9c - 1;
+	aainfo[0]._val2 = resetFrame;
+	kernel_reset_animation(aa[0], resetFrame);
+}
+
+static void room_101_anim4() {
+	int16 frame = kernel_anim[aa[4]].frame;
+	if (frame == aainfo[4]._val2)
+		return;
+	aainfo[4]._val2 = frame;
+
+	switch (aainfo[4]._val3) {
+	case 13:
+		if (frame == 37)
+			digi_play_build(101, '_', 1, 2);
+		return;
+
+	case 14:
+		if (frame == 26) {
+			digi_play_build(101, '_', 1, 2);
+			return;
+		}
+		if (frame > 26) return;
+		if (frame == 2) {
+			digi_play_build(101, 'e', 2, 1);
+			scratch._aa = 50;
+			return;
+		}
+		if (frame == 7) {
+			if (scratch._aa != 50) return;
+			aainfo[4]._val2 = 3;
+			kernel_reset_animation(aa[4], 3);
+			return;
+		}
+		if (frame == 9) {
+			if (scratch._aa != 50) return;
+			aainfo[4]._val2 = 8;
+			kernel_reset_animation(aa[4], 8);
+			return;
+		}
+		if (frame == 18) {
+			if (scratch._aa != 51) return;
+			aainfo[4]._val2 = 14;
+			kernel_reset_animation(aa[4], 14);
+		}
+		return;
+
+	case 15:
+		if (frame == 24) {
+			digi_play_build(101, 'e', 4, 1);
+			scratch._aa = 0;
+			return;
+		}
+		if (frame == 41)
+			digi_play_build(101, '_', 1, 2);
+		return;
+	}
+}
+
+static void room_101_anim5() {
+	int16 frame = kernel_anim[aa[2]].frame;
+	if (frame != aainfo[2]._val2)
+		aainfo[2]._val2 = frame;
+}
+
+static void room_101_anim6() {
+	int16 frame = kernel_anim[aa[1]].frame;
+	if (frame != aainfo[1]._val2)
+		aainfo[1]._val2 = frame;
+}
+
+static void room_101_anim7() {
+	int16 frame = kernel_anim[aa[5]].frame;
+	if (frame == aainfo[5]._val2)
+		return;
+	aainfo[5]._val2 = frame;
+	if (frame == 2) {
+		if (scratch._ae != 4) return;
+		digi_initial_volume(100);
+		digi_play_build(101, '_', 500, 2);
+		global[g009] = 0;
+		return;
+	}
+	if (frame == 78) {
+		if (scratch._ae != 5) return;
+		kernel_abort_animation(aa[5]);
+		aainfo[5]._val1 = 0;
+		player.x = 96;
+		player.y = 140;
+		player.facing = 3;
+		player.walker_visible = -1;
+		player.commands_allowed = -1;
+		mouse_show();
+		kernel_synch(4, 2, 0, 0);
+	}
+}
+
+static void room_101_anim8() {
+	int16 frame = kernel_anim[aa[8]].frame;
+	if (frame == aainfo[8]._val2)
+		return;
+	aainfo[8]._val2 = frame;
+	if (frame != 9) {
+		if (frame != 81) return;
+		dont_frag_the_palette();
+		kernel_abort_animation(aa[8]);
+		aainfo[8]._val1 = 0;
+		stop_speech_on_run_animation = false;
+		aa[9] = kernel_run_animation(kernel_name('I', 2), 0);
+		aainfo[9]._val1 = -1;
+		kernel_synch(3, 3, aa[9], aa[8]);
+	}
+	kernel_timing_trigger(1, 113);
+}
+
+static void room_101_anim9() {
+	int16 frame = kernel_anim[aa[9]].frame;
+	if (frame != aainfo[9]._val2) {
+		aainfo[9]._val2 = frame;
+		if (frame == 99) {
+			digi_play_build(101, 'r', 3, 1);
+			scratch._b4 = 99;
+		} else if (frame < 99) {
+			if (frame == 1) {
+				digi_play_build(101, 'c', 1, 1);
+				scratch._b4 = 1;
+			} else if (frame == 67) {
+				digi_play_build(101, 'e', 5, 1);
+				scratch._b4 = 67;
+			}
+		} else {
+			if (frame == 135) {
+				digi_play_build(101, 'm', 1, 1);
+				scratch._b4 = 135;
+			} else if (frame == 163) {
+				digi_play_build(101, 'b', 6, 1);
+				scratch._b4 = 163;
+			} else if (frame == 205) {
+				dont_frag_the_palette();
+				kernel_abort_animation(aa[9]);
+				aainfo[9]._val1 = 0;
+				stop_speech_on_run_animation = false;
+				aa[10] = kernel_run_animation(kernel_name('I', 3), 0);
+				aainfo[10]._val1 = -1;
+				kernel_synch(3, 3, aa[10], aa[9]);
+				kernel_timing_trigger(1, 113);
+			}
+		}
+	}
+
+	if (kernel.trigger != 7 && kernel.trigger != 28) return;
+	int16 b4 = scratch._b4;
+	if (b4 == 1 || b4 == 67 || b4 == 99 || b4 == 135 || b4 == 163)
+		scratch._b4 = -1;
+}
+
+static void room_101_anim10() {
+	int16 frame = kernel_anim[aa[10]].frame;
+	if (frame != aainfo[10]._val2) {
+		aainfo[10]._val2 = frame;
+		if (frame == 90) {
+			stop_speech_on_run_animation = true;
+			kernel_abort_animation(aa[10]);
+			aainfo[10]._val1 = 0;
+			new_room = 106;
+		} else if (frame < 90) {
+			if (frame == 35) {
+				scratch._b4 = 35;
+			} else if (frame < 35) {
+				if (frame == 5)
+					global_digi_play(11);
+				else if (frame == 30) {
+					digi_play_build(101, '_', 3, 1);
+					scratch._b4 = 30;
+				}
+			} else {
+				if (frame == 47) {
+					digi_play_build(101, 'b', 7, 1);
+					scratch._b4 = 47;
+				} else if (frame == 70) {
+					digi_play_build(101, 'e', 6, 1);
+					scratch._b4 = 70;
+				} else if (frame == 81) {
+					digi_play_build(101, '_', 4, 1);
+					scratch._b4 = 81;
+				}
+			}
+		}
+	}
+
+	if (kernel.trigger != 7 && kernel.trigger != 28) return;
+	int16 b4 = scratch._b4;
+	if (b4 > 81) return;
+	if (b4 == 30 || b4 == 35 || b4 == 47 || b4 == 70 || b4 == 81)
+		scratch._b4 = -1;
+}
+
 static void room_101_daemon() {
-	// TODO
+	if (flags[0] < 0 && global[player_hyperwalked]) {
+		flags[0] = 0;
+		flags[1] = 0;
+		flags[2] = 0;
+		flags[3] = 0;
+		new_room = 904;
+	}
+
+	switch (kernel.trigger) {
+	case 113:
+		digi_initial_volume(90);
+		digi_play_build(101, '_', 5, 3);
+		digi_flag1 = true;
+		break;
+	}
+
+	if (kernel.trigger == 9 && flags[0] != -3)
+		scratch._b0 = 0;
+
+	if (scratch._b0 == 0 && flags[0] != -3) {
+		if (imath_random(1, 150) == 1) {
+			digi_initial_volume(25);
+			digi_play_build(104, '_', 1, 3);
+			scratch._b0 = -1;
+		}
+	}
+
+	if (flags[0] != -3) {
+		global_anim1(1, scratch._a4, global[g131], &global[g132]);   // TODO: identify global_anim
+		global_anim2(3, scratch._a6, global[g141], &global[g142]); // TODO: identify global_anim_2
+		if (mouse_hidden != 0)
+			mouse_show();
+	}
+
+	if (aainfo[6]._val1)  room_101_anim1();
+	if (aainfo[7]._val1)  room_101_anim2();
+	if (aainfo[0]._val1)  room_101_anim3();
+	if (aainfo[4]._val1)  room_101_anim4();
+	if (aainfo[2]._val1)  room_101_anim5();
+	if (aainfo[1]._val1)  room_101_anim6();
+	if (aainfo[5]._val1)  room_101_anim7();
+	if (aainfo[8]._val1)  room_101_anim8();
+	if (aainfo[9]._val1)  room_101_anim9();
+	if (aainfo[10]._val1) room_101_anim10();
+
+	if (scratch._b2)
+		kernel_random_frame(scratch._a8, &global[g151], global[g154]);
 }
 
 static void room_101_pre_parser() {


Commit: 40e784859a8db62f09f8d445c59982ff1b43fe6e
    https://github.com/scummvm/scummvm/commit/40e784859a8db62f09f8d445c59982ff1b43fe6e
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-06-04T21:41:31+10:00

Commit Message:
MADS: FOREST: Implemented remainder of room 101

Changed paths:
    engines/mads/madsv2/forest/journal.cpp
    engines/mads/madsv2/forest/journal.h
    engines/mads/madsv2/forest/rooms/room101.cpp


diff --git a/engines/mads/madsv2/forest/journal.cpp b/engines/mads/madsv2/forest/journal.cpp
index b86344bbc25..07ee1c45009 100644
--- a/engines/mads/madsv2/forest/journal.cpp
+++ b/engines/mads/madsv2/forest/journal.cpp
@@ -37,7 +37,7 @@ void open_journal() {
 	// TODO
 }
 
-void close_journal() {
+void close_journal(int mode) {
 	// TODO
 }
 
diff --git a/engines/mads/madsv2/forest/journal.h b/engines/mads/madsv2/forest/journal.h
index 1e2f97fb3ed..c0bf195f389 100644
--- a/engines/mads/madsv2/forest/journal.h
+++ b/engines/mads/madsv2/forest/journal.h
@@ -31,7 +31,7 @@ namespace Forest {
 extern void display_journal();
 extern void leave_journal();
 extern void open_journal();
-extern void close_journal();
+extern void close_journal(int mode);
 
 } // namespace Forest
 } // namespace MADSV2
diff --git a/engines/mads/madsv2/forest/rooms/room101.cpp b/engines/mads/madsv2/forest/rooms/room101.cpp
index b7a5928c463..85d59475940 100644
--- a/engines/mads/madsv2/forest/rooms/room101.cpp
+++ b/engines/mads/madsv2/forest/rooms/room101.cpp
@@ -36,6 +36,7 @@
 #include "mads/madsv2/forest/mads/sounds.h"
 #include "mads/madsv2/forest/mads/words.h"
 #include "mads/madsv2/forest/global.h"
+#include "mads/madsv2/forest/journal.h"
 #include "mads/madsv2/forest/rooms/section1.h"
 #include "mads/madsv2/forest/rooms/room101.h"
 
@@ -490,7 +491,7 @@ static void room_101_anim7() {
 		player.walker_visible = -1;
 		player.commands_allowed = -1;
 		mouse_show();
-		kernel_synch(4, 2, 0, 0);
+		kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
 	}
 }
 
@@ -507,7 +508,7 @@ static void room_101_anim8() {
 		stop_speech_on_run_animation = false;
 		aa[9] = kernel_run_animation(kernel_name('I', 2), 0);
 		aainfo[9]._val1 = -1;
-		kernel_synch(3, 3, aa[9], aa[8]);
+		kernel_synch(KERNEL_ANIM, aa[9], KERNEL_ANIM, aa[8]);
 	}
 	kernel_timing_trigger(1, 113);
 }
@@ -541,7 +542,7 @@ static void room_101_anim9() {
 				stop_speech_on_run_animation = false;
 				aa[10] = kernel_run_animation(kernel_name('I', 3), 0);
 				aainfo[10]._val1 = -1;
-				kernel_synch(3, 3, aa[10], aa[9]);
+				kernel_synch(KERNEL_ANIM, aa[10], KERNEL_ANIM, aa[9]);
 				kernel_timing_trigger(1, 113);
 			}
 		}
@@ -604,11 +605,398 @@ static void room_101_daemon() {
 	}
 
 	switch (kernel.trigger) {
+	case 7:
+		switch (scratch._aa) {
+		case 1:
+			aainfo[6]._val2 = 13;
+			kernel_reset_animation(aa[6], 13);
+			aainfo[6]._val3 = 12;
+			kernel_timing_trigger(25, 7);
+			scratch._aa++;
+			break;
+		case 2:
+			aainfo[6]._val2 = 6;
+			kernel_reset_animation(aa[6], 6);
+			aainfo[6]._val3 = 9;
+			digi_play_build(101, 'r', 1, 1);
+			scratch._aa++;
+			break;
+		case 3:
+			aainfo[6]._val2 = 13;
+			kernel_reset_animation(aa[6], 13);
+			aainfo[6]._val3 = 12;
+			kernel_timing_trigger(30, 106);
+			break;
+		case 4:
+			aainfo[6]._val2 = 12;
+			kernel_reset_animation(aa[6], 12);
+			aainfo[6]._val3 = 0;
+			scratch._aa++;
+			break;
+		case 10:
+			aainfo[6]._val2 = 15;
+			kernel_reset_animation(aa[6], 15);
+			aainfo[6]._val3 = 0;
+			scratch._aa++;
+			break;
+		case 20:
+			aainfo[6]._val2 = 13;
+			kernel_reset_animation(aa[6], 13);
+			aainfo[6]._val3 = 0;
+			scratch._aa++;
+			break;
+		case 29:
+			global[walker_converse_state] = 0;
+			close_journal(3);
+			player.commands_allowed = true;
+			break;
+		case 30:
+			aainfo[6]._val2 = 31;
+			kernel_reset_animation(aa[6], 31);
+			aainfo[6]._val3 = 0;
+			scratch._aa++;
+			break;
+		case 40:
+			aainfo[6]._val2 = 95;
+			kernel_reset_animation(aa[6], 95);
+			aainfo[6]._val3 = 0;
+			scratch._aa++;
+			break;
+		case 49:
+			kernel_abort_animation(scratch._a8);
+			scratch._b2 = 0;
+			kernel_reset_animation(scratch._a4, 0);
+			kernel_reset_animation(scratch._a6, 0);
+			global[g133] = 1;
+			global[g143] = 1;
+			inter_move_object(0, 2);
+			kernel_seq_delete(seq[2]);
+			kernel_flip_hotspot(105, 0);
+			aa[4] = kernel_run_animation(kernel_name('R', 5), 103);
+			aainfo[4]._val1 = -1;
+			aainfo[4]._val3 = 14;
+			scratch._a2 = 4;
+			kernel_synch(KERNEL_ANIM, scratch._a4, KERNEL_ANIM, aa[4]);
+			break;
+		case 50:
+			aainfo[4]._val2 = 8;
+			kernel_reset_animation(aa[4], 8);
+			kernel_timing_trigger(45, 107);
+			break;
+		case 51:
+			aainfo[4]._val2 = 19;
+			kernel_reset_animation(aa[4], 19);
+			break;
+		case 59:
+			kernel_abort_animation(scratch._a8);
+			scratch._b2 = 0;
+			kernel_reset_animation(scratch._a4, 0);
+			kernel_reset_animation(scratch._a6, 0);
+			global[g133] = 1;
+			global[g143] = 1;
+			inter_move_object(11, 2);
+			kernel_seq_delete(seq[0]);
+			kernel_flip_hotspot(164, 0);
+			aa[4] = kernel_run_animation(kernel_name('R', 6), 103);
+			aainfo[4]._val1 = -1;
+			aainfo[4]._val3 = 15;
+			scratch._a2 = 4;
+			kernel_synch(KERNEL_ANIM, scratch._a4, KERNEL_ANIM, aa[4]);
+			break;
+		case 69:
+			global[g154] = 0;
+			kernel_abort_animation(scratch._a8);
+			player.walker_visible = -1;
+			scratch._b2 = 0;
+			kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
+			kernel_timing_trigger(45, 109);
+			break;
+		default:
+			break;
+		}
+		break;
+
+	case 26:
+		switch (scratch._9a) {
+		case 15:
+			scratch._aa = 59;
+			global[g154] = 2;
+			player.commands_allowed = 0;
+			scratch._a8 = kernel_run_animation_talk('b', 7, 0);
+			kernel_position_anim(scratch._a8, player.x, player.y, player.scale, player.depth);
+			scratch._b2 = -1;
+			kernel_synch(KERNEL_ANIM, scratch._a8, KERNEL_PLAYER, 0);
+			digi_play_build_ii('b', 2, 1);
+			break;
+		case 14:
+			scratch._aa = 49;
+			global[g154] = 2;
+			player.commands_allowed = 0;
+			player.walker_visible = 0;
+			scratch._a8 = kernel_run_animation_talk('b', 6, 0);
+			kernel_position_anim(scratch._a8, player.x, player.y, player.scale, player.depth);
+			scratch._b2 = -1;
+			kernel_synch(KERNEL_ANIM, scratch._a8, KERNEL_PLAYER, 0);
+			digi_play_build_ii('b', 1, 1);
+			break;
+
+		case 13:
+			scratch._aa = 69;
+			player.commands_allowed = 0;
+			player.walker_visible = 0;
+			kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
+			global[g154] = 2;
+			scratch._a8 = kernel_run_animation_talk('b', 4, 0);
+			kernel_position_anim(scratch._a8, player.x, player.y, player.scale, player.depth);
+			scratch._b2 = -1;
+			kernel_synch(KERNEL_ANIM, scratch._a8, KERNEL_PLAYER, 0);
+			digi_play_build_ii('b', 2, 1);
+			break;
+
+		default:
+			break;
+		}
+		break;
+
+	case 24:
+		if (scratch._96 == 3) {
+			aa[1] = kernel_run_animation(kernel_name('E', 1), 102);
+			aainfo[1]._val1 = -1;
+			scratch._a0 = 1;
+			kernel_reset_animation(scratch._a4, 0);
+			kernel_synch(KERNEL_ANIM, aa[1], KERNEL_ANIM, scratch._a4);
+			global[g133] = 1;
+		}
+		break;
+
+	case 25:
+		if (scratch._98 == 2) {
+			aa[2] = kernel_run_animation(kernel_name('R', 1), 101);
+			aainfo[2]._val1 = -1;
+			scratch._9e = 2;
+			kernel_reset_animation(scratch._a6, 0);
+			kernel_synch(KERNEL_ANIM, aa[2], KERNEL_ANIM, scratch._a6);
+			global[g143] = 1;
+		}
+		break;
+
+	case 104:
+		switch (scratch._ac) {
+		case 1:
+			kernel_abort_animation(aa[6]);
+			aa[6] = kernel_run_animation(kernel_name('b', 2), 104);
+			aainfo[6]._val1 = -1;
+			scratch._9c = 1;
+			scratch._ac++;
+			break;
+		case 2:
+			kernel_abort_animation(aa[6]);
+			aa[6] = kernel_run_animation(kernel_name('b', 3), 104);
+			aainfo[6]._val1 = -1;
+			scratch._9c = 1;
+			scratch._ac++;
+			break;
+		case 3:
+			kernel_abort_animation(aa[6]);
+			aa[6] = kernel_run_animation(kernel_name('r', 1), 104);
+			aainfo[6]._val1 = -1;
+			scratch._9c = 1;
+			scratch._ac++;
+			break;
+		case 4:
+			kernel_abort_animation(aa[6]);
+			aa[7] = kernel_run_animation(kernel_name('r', 2), 105);
+			aainfo[7]._val1 = -1;
+			aainfo[7]._val3 = 7;
+			scratch._9c = 1;
+			aa[6] = kernel_run_animation(kernel_name('e', 1), 104);
+			aainfo[6]._val1 = -1;
+			scratch._9c = 1;
+			scratch._ac++;
+			break;
+		case 5:
+			kernel_seq_delete(seq[3]);
+			kernel_flip_hotspot(110, 0);
+			kernel_abort_animation(aa[6]);
+			aa[6] = kernel_run_animation(kernel_name('e', 2), 104);
+			aainfo[6]._val1 = -1;
+			scratch._9c = 1;
+			scratch._ac++;
+			break;
+		case 6:
+			kernel_abort_animation(aa[6]);
+			global[g131] = -1;
+			kernel_reset_animation(scratch._a4, 1);
+			kernel_synch(KERNEL_ANIM, scratch._a4, KERNEL_NOW, 0);
+			global[g133] = 0;
+			aa[6] = kernel_run_animation(kernel_name('b', 4), 104);
+			aainfo[6]._val1 = -1;
+			scratch._9c = 1;
+			scratch._ac++;
+			break;
+		case 7:
+			kernel_abort_animation(aa[6]);
+			aa[6] = kernel_run_animation(kernel_name('b', 5), 104);
+			aainfo[6]._val1 = -1;
+			scratch._9c = 1;
+			scratch._ac++;
+			break;
+		case 8:
+			kernel_abort_animation(aa[6]);
+			aa[6] = kernel_run_animation(kernel_name('b', 6), 104);
+			aainfo[6]._val1 = -1;
+			scratch._9c = 1;
+			scratch._ac++;
+			break;
+		case 9:
+			kernel_abort_animation(aa[6]);
+			aa[6] = kernel_run_animation(kernel_name('b', 9), 104);
+			aainfo[6]._val1 = -1;
+			scratch._9c = 1;
+			scratch._ac++;
+			break;
+		case 10:
+			aainfo[7]._val3 = 8;
+			player.walker_visible = -1;
+			kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
+			flags[0] = 2;
+			flags[1] = 0;
+			flags[2] = 0;
+			flags[3] = 0;
+			break;
+		default:
+			break;
+		}
+		break;
+
+	case 103:
+		kernel_abort_animation(aa[4]);
+		aainfo[4]._val1 = 0;
+		global[g131] = -1;
+		global[g141] = -1;
+		kernel_reset_animation(scratch._a4, 1);
+		kernel_synch(KERNEL_ANIM, scratch._a4, KERNEL_NOW, 0);
+		kernel_reset_animation(scratch._a6, 1);
+		kernel_synch(KERNEL_ANIM, scratch._a6, KERNEL_NOW, 0);
+		player.walker_visible = -1;
+		global[g133] = 0;
+		global[g143] = 0;
+		kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
+		player.commands_allowed = -1;
+		break;
+
+	case 107:
+		aainfo[4]._val2 = 13;
+		kernel_reset_animation(aa[4], 13);
+		digi_play_build(101, 'B', 5, 1);
+		scratch._aa++;
+		break;
+
+	case 111:
+		switch (scratch._ae) {
+		case 1:
+			kernel_abort_animation(aa[5]);
+			aa[5] = kernel_run_animation(kernel_name('b', 7), 111);
+			scratch._ae = 2;
+			break;
+		case 2:
+			kernel_abort_animation(aa[5]);
+			aainfo[5]._val1 = 0;
+			new_room = 205;
+			break;
+		case 4:
+			kernel_abort_animation(aa[5]);
+			aa[5] = kernel_run_animation(kernel_name('b', 9), 111);
+			scratch._ae = 5;
+			break;
+		default:
+			break;
+		}
+		break;
+
+	case 109:
+		kernel_reset_animation(scratch._a4, 0);
+		kernel_reset_animation(scratch._a6, 0);
+		global[g133] = 1;
+		global[g143] = 1;
+		inter_move_object(8, 2);
+		kernel_seq_delete(seq[1]);
+		kernel_flip_hotspot(154, 0);
+		aa[4] = kernel_run_animation(kernel_name('R', 4), 103);
+		aainfo[4]._val1 = -1;
+		aainfo[4]._val3 = 13;
+		scratch._a2 = 4;
+		kernel_synch(KERNEL_ANIM, scratch._a4, KERNEL_ANIM, aa[4]);
+		digi_play_build(101, 'e', 3, 1);
+		scratch._aa = 0;
+		break;
+
+	case 106:
+		aainfo[6]._val2 = 3;
+		kernel_reset_animation(aa[6], 3);
+		aainfo[6]._val3 = 10;
+		digi_play_build(101, 'b', 2, 1);
+		scratch._aa++;
+		break;
+
+	case 105:
+		if (aainfo[7]._val3 == 0) {
+			aainfo[7]._val1 = 0;
+			global[g141] = -1;
+			kernel_reset_animation(scratch._a6, 1);
+			kernel_synch(KERNEL_ANIM, scratch._a6, KERNEL_NOW, 0);
+			global[g143] = 0;
+			global[g009] = 0;
+			player.commands_allowed = -1;
+		} else if (aainfo[7]._val3 == 8) {
+			kernel_abort_animation(aa[7]);
+			aa[7] = kernel_run_animation(kernel_name('r', 3), 105);
+			aainfo[7]._val1 = -1;
+			scratch._9c = 1;
+			aainfo[7]._val3 = 0;
+		}
+		break;
+
+	case 100:
+		kernel_abort_animation(aa[0]);
+		aainfo[0]._val1 = 0;
+		global[g131] = -1;
+		global[g141] = -1;
+		kernel_reset_animation(scratch._a4, 1);
+		kernel_synch(KERNEL_ANIM, scratch._a4, KERNEL_NOW, 0);
+		kernel_reset_animation(scratch._a6, 1);
+		kernel_synch(KERNEL_ANIM, scratch._a6, KERNEL_NOW, 0);
+		player.walker_visible = -1;
+		global[g133] = 0;
+		global[g143] = 0;
+		kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
+		player.commands_allowed = -1;
+		break;
+
+	case 101:
+		kernel_abort_animation(aa[scratch._9e]);
+		aainfo[scratch._9e]._val1 = 0;
+		kernel_reset_animation(scratch._a6, 1);
+		kernel_synch(KERNEL_ANIM, scratch._a6, KERNEL_NOW, 0);
+		global[g143] = 0;
+		player.commands_allowed = -1;
+		break;
+
+	case 102:
+		kernel_abort_animation(aa[scratch._a0]);
+		aainfo[scratch._a0]._val1 = 0;
+		kernel_reset_animation(scratch._a4, 1);
+		kernel_synch(KERNEL_ANIM, scratch._a4, KERNEL_NOW, 0);
+		global[g133] = 0;
+		player.commands_allowed = -1;
+		break;
+
 	case 113:
 		digi_initial_volume(90);
 		digi_play_build(101, '_', 5, 3);
 		digi_flag1 = true;
 		break;
+
 	}
 
 	if (kernel.trigger == 9 && flags[0] != -3)
@@ -623,8 +1011,8 @@ static void room_101_daemon() {
 	}
 
 	if (flags[0] != -3) {
-		global_anim1(1, scratch._a4, global[g131], &global[g132]);   // TODO: identify global_anim
-		global_anim2(3, scratch._a6, global[g141], &global[g142]); // TODO: identify global_anim_2
+		global_anim1(1, scratch._a4, global[g131], &global[g132]);
+		global_anim2(3, scratch._a6, global[g141], &global[g142]);
 		if (mouse_hidden != 0)
 			mouse_show();
 	}
@@ -710,9 +1098,27 @@ done:
 }
 
 void room_101_synchronize(Common::Serializer &s) {
-	for (int16 &v : scratch.sprite)    s.syncAsSint16LE(v);
-	for (int16 &v : scratch.sequence)  s.syncAsSint16LE(v);
-	for (auto &v : scratch.array1)     v.synchronize(s);
+	for (int16 &v : scratch.sprite)     s.syncAsSint16LE(v);
+	for (int16 &v : scratch.sequence)   s.syncAsSint16LE(v);
+	for (int16 &v : scratch.animation)  s.syncAsSint16LE(v);
+	for (auto  &v : scratch.array1)     v.synchronize(s);
+	s.syncAsSint16LE(scratch._96);
+	s.syncAsSint16LE(scratch._98);
+	s.syncAsSint16LE(scratch._9a);
+	s.syncAsSint16LE(scratch._9c);
+	s.syncAsSint16LE(scratch._9e);
+	s.syncAsSint16LE(scratch._a0);
+	s.syncAsSint16LE(scratch._a2);
+	s.syncAsSint16LE(scratch._a4);
+	s.syncAsSint16LE(scratch._a6);
+	s.syncAsSint16LE(scratch._a8);
+	s.syncAsSint16LE(scratch._aa);
+	s.syncAsSint16LE(scratch._ac);
+	s.syncAsSint16LE(scratch._ae);
+	s.syncAsSint16LE(scratch._b0);
+	s.syncAsSint16LE(scratch._b2);
+	s.syncAsSint16LE(scratch._b4);
+	s.syncAsSint16LE(scratch._b6);
 }
 
 void room_101_preload() {



From noreply at scummvm.org  Thu Jun  4 12:16:10 2026
From: noreply at scummvm.org (dreammaster)
Date: Thu,  4 Jun 2026 12:16:10 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 2e866819b8974c4f9b26cf41a9c9953a29e5ac8b
Message-ID: <20260604121610.4E1F4700573@ron.scummvm.net>

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

Summary:
2e866819b8 MADS: FOREST: Implemented room 103


Commit: 2e866819b8974c4f9b26cf41a9c9953a29e5ac8b
    https://github.com/scummvm/scummvm/commit/2e866819b8974c4f9b26cf41a9c9953a29e5ac8b
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-06-04T22:14:53+10:00

Commit Message:
MADS: FOREST: Implemented room 103

Changed paths:
    engines/mads/madsv2/forest/global.h
    engines/mads/madsv2/forest/rooms/room101.cpp
    engines/mads/madsv2/forest/rooms/room103.cpp


diff --git a/engines/mads/madsv2/forest/global.h b/engines/mads/madsv2/forest/global.h
index 286505cc4d0..4213e666761 100644
--- a/engines/mads/madsv2/forest/global.h
+++ b/engines/mads/madsv2/forest/global.h
@@ -243,6 +243,17 @@ enum {
 	g210                      = 210
 };
 
+struct AnimationInfo {
+	int16 _val1;
+	int16 _val2;
+	int16 _val3;
+	int16 _val4;
+
+	void synchronize(Common::Serializer &s) {
+		s.syncMultipleLE(_val1, _val2, _val3, _val4);
+	}
+};
+
 extern int16 flags[40];
 
 extern void global_section_constructor();
diff --git a/engines/mads/madsv2/forest/rooms/room101.cpp b/engines/mads/madsv2/forest/rooms/room101.cpp
index 85d59475940..ff30057be70 100644
--- a/engines/mads/madsv2/forest/rooms/room101.cpp
+++ b/engines/mads/madsv2/forest/rooms/room101.cpp
@@ -45,23 +45,12 @@ namespace MADSV2 {
 namespace Forest {
 namespace Rooms {
 
-struct Struct8 {
-	int16 _val1;
-	int16 _val2;
-	int16 _val3;
-	int16 _val4;
-
-	void synchronize(Common::Serializer &s) {
-		s.syncMultipleLE(_val1, _val2, _val3, _val4);
-	}
-};
-
 struct Scratch {
 	// Tentative field definitions
 	int16 sprite[10];       // Offset 00h
 	int16 sequence[10];     // Offset 14h
 	int16 animation[11];	// Offset 28h
-	Struct8 array1[11];		// Offset 3eh
+	AnimationInfo animation_info[11];	// Offset 3eh
 	int16 _96;				// Offset 96h
 	int16 _98;				// Offset 98h
 	int16 _9a;				// Offset 9ah
@@ -86,7 +75,7 @@ static Scratch scratch;
 #define ss     scratch.sprite
 #define seq    scratch.sequence
 #define aa     scratch.animation
-#define aainfo scratch.array1
+#define aainfo scratch.animation_info
 
 static void room_101_init1() {
 	global[g009] = -1;
@@ -96,15 +85,15 @@ static void room_101_init1() {
 	player.commands_allowed = false;
 	mouse_hide();
 
-	for (auto &v : scratch.array1) {
+	for (auto &v : aainfo) {
 		v._val1 = 0;
 		v._val2 = -1;
 	}
 
 	stop_speech_on_run_animation = false;
 	aa[8] = kernel_run_animation(kernel_name('I', 1), 0);
-	scratch.array1[8]._val1 = -1;
-	scratch.array1[8]._val2 = 8;
+	aainfo[8]._val1 = -1;
+	aainfo[8]._val2 = 8;
 	kernel_reset_animation(aa[8], 8);
 }
 
@@ -136,7 +125,7 @@ static void room_101_init2() {
 	}
 
 	aa[6] = kernel_run_animation(kernel_name('b', 1), 104);
-	scratch.array1[6]._val1 = -1;
+	aainfo[6]._val1 = -1;
 	scratch._9c = 1;
 	scratch._ac = 1;	
 }
@@ -173,14 +162,14 @@ static void room_101_init3() {
 
 	if (previous_room == 104) {
 		aa[0] = kernel_run_animation(kernel_name('y', 1), 100);
-		scratch.array1[0]._val1 = -1;
+		aainfo[0]._val1 = -1;
 		scratch._9c = 57;
 		return;
 	}
 
 	if (previous_room == 106) {
 		aa[0] = kernel_run_animation(kernel_name('y', 2), 100);
-		scratch.array1[0]._val1 = -1;
+		aainfo[0]._val1 = -1;
 		scratch._9c = 77;
 		return;
 	}
@@ -208,7 +197,7 @@ static void room_101_init3() {
 		player.commands_allowed = 0;
 		player.walker_visible = 0;
 		global[g009] = 0;
-		scratch.array1[5]._val1 = -1;
+		aainfo[5]._val1 = -1;
 		aa[5] = kernel_run_animation(kernel_name('b', 8), 111);
 		scratch._ae = 4;
 		return;
@@ -234,7 +223,7 @@ static void room_101_init() {
 			player.commands_allowed = false;
 		}
 
-		for (Struct8 &s : scratch.array1) {
+		for (auto &s : aainfo) {
 			s._val1 = 0;
 			s._val2 = 1;
 			s._val3 = s._val4 = 0;
@@ -1084,7 +1073,7 @@ static void room_101_parser() {
 		player.commands_allowed = 0;
 		player.walker_visible = 0;
 		kernel.trigger_setup_mode = 1;
-		scratch.array1[5]._val1 = -1;
+		aainfo[5]._val1 = -1;
 		aa[5] = kernel_run_animation(kernel_name('b', 5), 111);
 		scratch._ae = 1;
 		goto handled;
@@ -1101,7 +1090,7 @@ void room_101_synchronize(Common::Serializer &s) {
 	for (int16 &v : scratch.sprite)     s.syncAsSint16LE(v);
 	for (int16 &v : scratch.sequence)   s.syncAsSint16LE(v);
 	for (int16 &v : scratch.animation)  s.syncAsSint16LE(v);
-	for (auto  &v : scratch.array1)     v.synchronize(s);
+	for (auto  &v : scratch.animation_info) v.synchronize(s);
 	s.syncAsSint16LE(scratch._96);
 	s.syncAsSint16LE(scratch._98);
 	s.syncAsSint16LE(scratch._9a);
diff --git a/engines/mads/madsv2/forest/rooms/room103.cpp b/engines/mads/madsv2/forest/rooms/room103.cpp
index a019225a50d..d8883f18883 100644
--- a/engines/mads/madsv2/forest/rooms/room103.cpp
+++ b/engines/mads/madsv2/forest/rooms/room103.cpp
@@ -20,11 +20,15 @@
  */
 
 #include "mads/madsv2/core/conv.h"
+#include "mads/madsv2/core/digi.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/imath.h"
 #include "mads/madsv2/core/inter.h"
 #include "mads/madsv2/core/kernel.h"
+#include "mads/madsv2/core/matte.h"
+#include "mads/madsv2/core/midi.h"
 #include "mads/madsv2/core/sound.h"
+#include "mads/madsv2/core/sprite.h"
 #include "mads/madsv2/core/text.h"
 #include "mads/madsv2/forest/mads/inventory.h"
 #include "mads/madsv2/forest/mads/sounds.h"
@@ -39,14 +43,15 @@ namespace Forest {
 namespace Rooms {
 
 struct Scratch {
-	int16 sprite[15];       /* Sprite series handles */
-	int16 sequence[15];     /* Sequence handles      */
-	int16 animation[4];     /* Animation handles     */
+	int16 sprite[10];       /* Sprite series handles */
+	int16 sequence[10];     /* Sequence handles      */
+	int16 animation[10];     /* Animation handles     */
+	AnimationInfo animation_info[10];
 
-	int16 dragon_frame;     /* frame animation is on */
-
-	int16 done_with_conv;   /* T if done with conv   */
-	int16 prev_room;
+	int16 _8c;
+	int16 _8e;
+	int16 _90;
+	int16 _92;
 };
 
 static Scratch scratch;
@@ -55,24 +60,361 @@ static Scratch scratch;
 #define ss    local->sprite
 #define seq   local->sequence
 #define aa    local->animation
-
+#define aainfo scratch.animation_info
 
 static void room_103_init() {
+	scratch._90 = -1;
+	scratch._92 = -1;
+
+	for (int count = 0; count < 10; count++) {
+		aainfo[count]._val1 = 0;
+		aainfo[count]._val2 = 1;
+		aainfo[count]._val3 = 0;
+		aainfo[count]._val4 = 0;
+	}
+
+	global[player_score] = -1;
+	global[g009] = -1;
+	viewing_at_y = 22;
+	player.walker_visible = 0;
+	player.commands_allowed = 0;
+
+	for (int count = 0; count < 10; count++) {
+		aainfo[count]._val1 = 0;
+		aainfo[count]._val2 = -1;
+	}
+
+	aa[0] = kernel_run_animation(kernel_name('F', 1), 0);
+	aainfo[0]._val1 = -1;
+}
+
+static void room_103_anim1() {
+	if (kernel_anim[aa[0]].frame != aainfo[0]._val2) {
+		int16 frame = kernel_anim[aa[0]].frame;
+		aainfo[0]._val2 = frame;
+
+		if (frame == 1) {
+			digi_initial_volume(20);
+			digi_play_build(101, '_', 5, 3);
+			scratch._92 = 1;
+			digi_flag1 = true;
+		} else if (frame == 273) {
+			kernel_abort_animation(aa[0]);
+			aainfo[0]._val1 = 0;
+			stop_speech_on_run_animation = false;
+			aa[1] = kernel_run_animation(kernel_name('F', 2), 0);
+			aainfo[1]._val1 = -1;
+			kernel_synch(KERNEL_ANIM, aa[1], KERNEL_ANIM, aa[0]);
+		}
+	}
+
+	if (kernel.trigger == 8 || kernel.trigger == 40) {
+		if (scratch._92 == 1) {
+			digi_initial_volume(20);
+			scratch._92 = 1;
+			digi_play_build(101, '_', 5, 3);
+			digi_flag1 = true;
+		}
+	}
+}
+
+static void room_103_anim2() {
+	if (kernel_anim[aa[1]].frame != aainfo[1]._val2) {
+		int16 frame = kernel_anim[aa[1]].frame;
+		aainfo[1]._val2 = frame;
+
+		if (frame == 165) {
+			digi_flag1 = false;
+		} else if (frame > 165) {
+			if (frame == 186) {
+				aainfo[1]._val3 = 11;
+				digi_play_build(103, 'e', 1, 1);
+				scratch._90 = 186;
+			} else if (frame == 195) {
+				if (aainfo[1]._val3 == 11) {
+					aainfo[1]._val2 = 186;
+					kernel_reset_animation(aa[1], 186);
+				}
+			} else if (frame == 196) {
+				dont_frag_the_palette();
+				kernel_abort_animation(aa[1]);
+				aainfo[1]._val1 = 0;
+				stop_speech_on_run_animation = false;
+				aa[2] = kernel_run_animation(kernel_name('F', 3), 0);
+				aainfo[2]._val1 = -1;
+				kernel_reset_animation(aa[2], 3);
+				kernel_synch(KERNEL_ANIM, aa[2], KERNEL_ANIM, aa[1]);
+			}
+		} else {
+			if (frame == 1) {
+				digi_initial_volume(50);
+				digi_play_build(101, '_', 5, 3);
+				scratch._92 = 1;
+				digi_flag1 = true;
+			} else if (frame == 160) {
+				digi_initial_volume(60);
+				digi_play_build(103, '_', 1, 1);
+				scratch._90 = 160;
+				digi_flag1 = true;
+			}
+		}
+	}
+
+	if (kernel.trigger == 7 || kernel.trigger == 40) {
+		if (scratch._90 == 160) {
+			scratch._90 = -1;
+		} else if (scratch._90 == 186) {
+			scratch._90 = -1;
+			aainfo[1]._val3 = 13;
+			aainfo[1]._val2 = 195;
+			kernel_reset_animation(aa[1], 195);
+		}
+	}
+
+	if (kernel.trigger == 8) {
+		if (scratch._92 == 1) {
+			digi_initial_volume(90);
+			scratch._92++;
+			digi_play_build(101, '_', 5, 3);
+			digi_flag1 = true;
+		}
+	}
+}
+
+static void room_103_anim3() {
+	if (kernel_anim[aa[2]].frame != aainfo[2]._val2) {
+		int16 frame = kernel_anim[aa[2]].frame;
+		aainfo[2]._val2 = frame;
+
+		switch (frame) {
+		case 29:
+			aainfo[2]._val3 = 12;
+			digi_play_build(103, 'r', 1, 1);
+			scratch._90 = 29;
+			break;
+		case 38:
+			if (aainfo[2]._val3 == 12) {
+				aainfo[2]._val2 = 29;
+				kernel_reset_animation(aa[2], 29);
+			}
+			break;
+		case 44:
+			aainfo[2]._val3 = 10;
+			digi_play_build(103, 'b', 1, 1);
+			scratch._90 = 44;
+			break;
+		case 50:
+			if (aainfo[2]._val3 == 10) {
+				aainfo[2]._val2 = 44;
+				kernel_reset_animation(aa[2], 44);
+			}
+			break;
+		case 54:
+			aainfo[2]._val3 = 12;
+			digi_play_build(103, 'r', 2, 1);
+			scratch._90 = 54;
+			break;
+		case 60:
+			if (aainfo[2]._val3 == 12) {
+				aainfo[2]._val2 = 54;
+				kernel_reset_animation(aa[2], 54);
+			}
+			break;
+		case 72:
+			aainfo[2]._val3 = 14;
+			digi_play_build(103, 'm', 1, 1);
+			scratch._90 = 72;
+			break;
+		case 81:
+			if (aainfo[2]._val3 == 14) {
+				aainfo[2]._val2 = 72;
+				kernel_reset_animation(aa[2], 72);
+			}
+			break;
+		case 85:
+			aainfo[2]._val3 = 14;
+			digi_play_build(103, 'm', 2, 1);
+			scratch._90 = 85;
+			break;
+		case 91:
+			if (aainfo[2]._val3 == 14) {
+				aainfo[2]._val2 = 85;
+				kernel_reset_animation(aa[2], 85);
+			}
+			break;
+		case 109:
+			aainfo[2]._val3 = 14;
+			digi_play_build(103, 'm', 3, 1);
+			scratch._90 = 109;
+			break;
+		case 113:
+			if (aainfo[2]._val3 == 14) {
+				aainfo[2]._val2 = 109;
+				kernel_reset_animation(aa[2], 109);
+			}
+			break;
+		case 120:
+			digi_play_build(104, '_', 2, 2);
+			scratch._92 = 120;
+			break;
+		case 140:
+			digi_play_build(103, '_', 3, 2);
+			scratch._92 = 140;
+			break;
+		case 166:
+			dont_frag_the_palette();
+			kernel_abort_animation(aa[2]);
+			aainfo[2]._val1 = 0;
+			stop_speech_on_run_animation = false;
+			aa[3] = kernel_run_animation(kernel_name('F', 4), 0);
+			aainfo[3]._val1 = -1;
+			kernel_synch(KERNEL_ANIM, aa[3], KERNEL_ANIM, aa[2]);
+			break;
+		}
+	}
+
+	if (kernel.trigger == 7 || kernel.trigger == 40) {
+		switch (scratch._90) {
+		case 29:
+			scratch._90 = -1;
+			aainfo[2]._val3 = 13;
+			aainfo[2]._val2 = 40;
+			kernel_reset_animation(aa[2], 40);
+			break;
+		case 44:
+			scratch._90 = -1;
+			aainfo[2]._val3 = 13;
+			aainfo[2]._val2 = 50;
+			kernel_reset_animation(aa[2], 50);
+			break;
+		case 54:
+			scratch._90 = -1;
+			aainfo[2]._val3 = 13;
+			aainfo[2]._val2 = 60;
+			kernel_reset_animation(aa[2], 60);
+			break;
+		case 72:
+			scratch._90 = -1;
+			aainfo[2]._val3 = 13;
+			aainfo[2]._val2 = 81;
+			kernel_reset_animation(aa[2], 81);
+			break;
+		case 85:
+			scratch._90 = -1;
+			global[player_score] = 0;
+			midi_stop();
+			aainfo[2]._val3 = 13;
+			aainfo[2]._val2 = 92;
+			kernel_reset_animation(aa[2], 92);
+			break;
+		case 109:
+			scratch._90 = -1;
+			aainfo[2]._val3 = 13;
+			aainfo[2]._val2 = 113;
+			kernel_reset_animation(aa[2], 113);
+			break;
+		}
+	}
+
+	if (kernel.trigger == 8) {
+		scratch._92 = -1;
+	}
+}
+
+static void room_103_anim4() {
+	if (kernel_anim[aa[3]].frame != aainfo[3]._val2) {
+		int16 frame = kernel_anim[aa[3]].frame;
+		aainfo[3]._val2 = frame;
+
+		if (frame == 50) {
+			stop_speech_on_run_animation = true;
+			kernel_abort_animation(aa[3]);
+			aainfo[3]._val1 = 0;
+			new_room = 104;
+		} else if (frame < 50) {
+			switch (frame) {
+			case 16:
+				global_digi_play(8);
+				aainfo[3]._val3 = 15;
+				digi_play_build(103, 'c', 1, 1);
+				scratch._90 = 16;
+				break;
+			case 21:
+				if (aainfo[3]._val3 == 15) {
+					aainfo[3]._val2 = 16;
+					kernel_reset_animation(aa[3], 16);
+				}
+				break;
+			case 37:
+				aainfo[3]._val3 = 15;
+				digi_play_build(103, 'c', 2, 1);
+				scratch._90 = 37;
+				break;
+			case 40:
+				if (aainfo[3]._val3 == 15) {
+					aainfo[3]._val2 = 37;
+					kernel_reset_animation(aa[3], 37);
+				}
+				break;
+			}
+		}
+	}
+
+	if (kernel.trigger == 7 || kernel.trigger == 40) {
+		if (scratch._90 == 16) {
+			scratch._90 = -1;
+			aainfo[3]._val3 = 13;
+			aainfo[3]._val2 = 21;
+			kernel_reset_animation(aa[3], 21);
+		} else if (scratch._90 == 37) {
+			scratch._90 = -1;
+			aainfo[3]._val3 = 13;
+			aainfo[3]._val2 = 40;
+			kernel_reset_animation(aa[3], 40);
+		} else {
+			scratch._90 = -1;
+		}
+	}
+
+	if (kernel.trigger == 8) {
+		scratch._92 = -1;
+	}
 }
 
 static void room_103_daemon() {
+	if (flags[0] < 0 && global[player_hyperwalked] == -1) {
+		flags[0] = 0;
+		flags[1] = 0;
+		flags[2] = 0;
+		flags[3] = 0;
+		new_room = 904;
+	}
+
+	if (aainfo[0]._val1 != 0) room_103_anim1();
+	if (aainfo[1]._val1 != 0) room_103_anim2();
+	if (aainfo[2]._val1 != 0) room_103_anim3();
+	if (aainfo[3]._val1 != 0) room_103_anim4();
 }
 
 static void room_103_pre_parser() {
+	if (player_parse(13, 15, 0))
+		player.walk_off_edge_to_room = 101;
 }
 
+
 static void room_103_parser() {
+	// No implementation
 }
 
 void room_103_synchronize(Common::Serializer &s) {
-	for (int16 &v : scratch.sprite)    s.syncAsSint16LE(v);
-	for (int16 &v : scratch.sequence)  s.syncAsSint16LE(v);
-	for (int16 &v : scratch.animation) s.syncAsSint16LE(v);
+	for (int16 &v : scratch.sprite)         s.syncAsSint16LE(v);
+	for (int16 &v : scratch.sequence)       s.syncAsSint16LE(v);
+	for (int16 &v : scratch.animation)      s.syncAsSint16LE(v);
+	for (AnimationInfo &ai : scratch.animation_info) ai.synchronize(s);
+	s.syncAsSint16LE(scratch._8c);
+	s.syncAsSint16LE(scratch._8e);
+	s.syncAsSint16LE(scratch._90);
+	s.syncAsSint16LE(scratch._92);
 }
 
 void room_103_preload() {



From noreply at scummvm.org  Thu Jun  4 12:55:14 2026
From: noreply at scummvm.org (neuromancer)
Date: Thu,  4 Jun 2026 12:55:14 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 63b9827058128e9fc2990ceca8b30dbb47b7b0b5
Message-ID: <20260604125514.306E0700573@ron.scummvm.net>

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:
81517766f8 SCUMM: RA1: properly invert Y axis for some levels
04abb43098 SCUMM: RA1: virtual keyboard support for text inputs
63b9827058 SCUMM: RA1: better gamepad controls for some levels


Commit: 81517766f874af7248778667af06b524b1f8eff8
    https://github.com/scummvm/scummvm/commit/81517766f874af7248778667af06b524b1f8eff8
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-04T14:54:00+02:00

Commit Message:
SCUMM: RA1: properly invert Y axis for some levels

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


diff --git a/engines/scumm/insane/rebel1/iact.cpp b/engines/scumm/insane/rebel1/iact.cpp
index fb8e22f4b00..eed59ccdb22 100644
--- a/engines/scumm/insane/rebel1/iact.cpp
+++ b/engines/scumm/insane/rebel1/iact.cpp
@@ -807,6 +807,25 @@ void InsaneRebel1::updateFlightVariantCursor() {
 // jump latch from FUN_231BE, plus FUN_23115's DOS mouse recenter behavior.
 // Enhanced controls are ScummVM-only: they bypass the original recentering state
 // and expose a stable absolute centered mouse axis instead.
+bool InsaneRebel1::shouldInvertTouchYSettingForCurrentLevel() const {
+	if (!isTouchscreenActive())
+		return false;
+
+	switch (_currentLevel) {
+	case 1:  // Level 2
+	case 3:  // Level 4
+	case 7:  // Level 8
+	case 8:  // Level 9
+	case 9:  // Level 10
+	case 11: // Level 12
+	case 13: // Level 14
+	case 14: // Level 15
+		return true;
+	default:
+		return false;
+	}
+}
+
 void InsaneRebel1::preprocessMouseAxes(int16 &inputX, int16 &inputY, bool *usedJoystick) {
 	if (usedJoystick)
 		*usedJoystick = false;
@@ -888,7 +907,12 @@ void InsaneRebel1::preprocessMouseAxes(int16 &inputX, int16 &inputY, bool *usedJ
 		inputX = (int16)CLIP<int32>(((int32)(logicalX - kRA1CenterX) * 127) / kRA1CenterX, -127, 127);
 		inputY = (int16)CLIP<int32>(((int32)(logicalY - kRA1CenterY) * 127) / kRA1CenterY, -127, 127);
 
-		if (_optControlsYFlip)
+		// These handlers negate DAT_756E internally, so direct touch needs the
+		// opposite baseline while the menu option still toggles the result.
+		bool flipY = _optControlsYFlip;
+		if (shouldInvertTouchYSettingForCurrentLevel())
+			flipY = !flipY;
+		if (flipY)
 			inputY = -inputY;
 
 		return;
diff --git a/engines/scumm/insane/rebel1/rebel.h b/engines/scumm/insane/rebel1/rebel.h
index 6081ec18aae..e08e41245a8 100644
--- a/engines/scumm/insane/rebel1/rebel.h
+++ b/engines/scumm/insane/rebel1/rebel.h
@@ -221,6 +221,7 @@ private:
 	void updateTurretPhysics();
 	void updateTurretShipDirection(int16 offsetY);
 	void getCollisionShipCenter(int16 &x, int16 &y) const;
+	bool shouldInvertTouchYSettingForCurrentLevel() const;
 	void preprocessMouseAxes(int16 &inputX, int16 &inputY, bool *usedJoystick = nullptr);
 	void rebuildProjectionTable(int16 curveStep, int16 curveExtent);
 	void resetProjectionTable();


Commit: 04abb430982dee4a771a8281fb9f237862ae0983
    https://github.com/scummvm/scummvm/commit/04abb430982dee4a771a8281fb9f237862ae0983
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-04T14:54:00+02:00

Commit Message:
SCUMM: RA1: virtual keyboard support for text inputs

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


diff --git a/engines/scumm/insane/rebel1/menu.cpp b/engines/scumm/insane/rebel1/menu.cpp
index af7cb44fdaa..15d07ef18c2 100644
--- a/engines/scumm/insane/rebel1/menu.cpp
+++ b/engines/scumm/insane/rebel1/menu.cpp
@@ -274,6 +274,17 @@ int InsaneRebel1::getMainMenuResultForSelection(int selection) const {
 	return selection + 2;
 }
 
+void InsaneRebel1::setVirtualKeyboardVisible(bool visible) {
+	if (!_vm->_system->hasFeature(OSystem::kFeatureVirtualKeyboard))
+		return;
+
+	if (_virtualKeyboardActive == visible)
+		return;
+
+	_vm->_system->setFeatureState(OSystem::kFeatureVirtualKeyboard, visible);
+	_virtualKeyboardActive = visible;
+}
+
 void InsaneRebel1::beginTextEntry(bool passcodeMode) {
 	_textEntryActive = true;
 	_textEntryPasscodeMode = passcodeMode;
@@ -292,6 +303,8 @@ void InsaneRebel1::beginTextEntry(bool passcodeMode) {
 			_textEntryPickerIndex = 1;
 		}
 	}
+
+	setVirtualKeyboardVisible(true);
 }
 
 void InsaneRebel1::finishTextEntry(bool canceled) {
@@ -299,6 +312,7 @@ void InsaneRebel1::finishTextEntry(bool canceled) {
 	if (!canceled)
 		_textEntryDone = true;
 	_textEntryActive = false;
+	setVirtualKeyboardVisible(false);
 	_vm->_smushVideoShouldFinish = true;
 }
 
@@ -1134,6 +1148,7 @@ bool InsaneRebel1::runTextEntryMenuLoop() {
 	while (!shouldAbortGameFlow() && !_textEntryDone && !_textEntryCanceled)
 		playMenuBackground();
 
+	setVirtualKeyboardVisible(false);
 	return !shouldAbortGameFlow() && !_textEntryCanceled;
 }
 
diff --git a/engines/scumm/insane/rebel1/rebel.cpp b/engines/scumm/insane/rebel1/rebel.cpp
index d4b1a7929bc..59a87b0f136 100644
--- a/engines/scumm/insane/rebel1/rebel.cpp
+++ b/engines/scumm/insane/rebel1/rebel.cpp
@@ -378,6 +378,7 @@ InsaneRebel1::InsaneRebel1(ScummEngine_v7 *scumm) : Insane(), _vm(scumm) {
 	_textEntryPasscodeMode = false;
 	_textEntryDone = false;
 	_textEntryCanceled = false;
+	_virtualKeyboardActive = false;
 	_textEntryPickerIndex = 0;
 	_textEntryPickerOffsetX = 0;
 	_textEntryMaxChars = 0;
diff --git a/engines/scumm/insane/rebel1/rebel.h b/engines/scumm/insane/rebel1/rebel.h
index e08e41245a8..5264989c70c 100644
--- a/engines/scumm/insane/rebel1/rebel.h
+++ b/engines/scumm/insane/rebel1/rebel.h
@@ -549,6 +549,7 @@ private:
 	int getMainMenuResultForSelection(int selection) const;
 	void playMenuBackground();
 	bool runTextEntryMenuLoop();
+	void setVirtualKeyboardVisible(bool visible);
 	void beginTextEntry(bool passcodeMode);
 	void finishTextEntry(bool canceled);
 	void selectTextEntryChar();
@@ -599,6 +600,7 @@ private:
 	bool _textEntryPasscodeMode;
 	bool _textEntryDone;
 	bool _textEntryCanceled;
+	bool _virtualKeyboardActive;
 	int _textEntryPickerIndex;
 	int _textEntryPickerOffsetX;
 	int _textEntryMaxChars;


Commit: 63b9827058128e9fc2990ceca8b30dbb47b7b0b5
    https://github.com/scummvm/scummvm/commit/63b9827058128e9fc2990ceca8b30dbb47b7b0b5
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-04T14:54:00+02:00

Commit Message:
SCUMM: RA1: better gamepad controls for some levels

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/insane/rebel1/runlevels.cpp


diff --git a/engines/scumm/insane/rebel1/iact.cpp b/engines/scumm/insane/rebel1/iact.cpp
index eed59ccdb22..8baa313f60f 100644
--- a/engines/scumm/insane/rebel1/iact.cpp
+++ b/engines/scumm/insane/rebel1/iact.cpp
@@ -34,9 +34,10 @@ inline int16 applyRebel1AnalogDeadzone(int16 axisValue) {
 	return (ABS(axis) <= deadZone) ? 0 : axisValue;
 }
 
-inline int16 smoothRebel1Op0BAnalogInput(int16 inputValue, int16 &filteredValue, int16 axisMax) {
+inline int16 smoothRebel1Op0BAnalogInput(int16 inputValue, int16 &filteredValue,
+		int16 axisMax, int responseDivisor) {
 	const int delta = (int)inputValue - (int)filteredValue;
-	int step = delta / 10;
+	int step = delta / responseDivisor;
 
 	if (step == 0 && delta != 0)
 		step = (delta > 0) ? 1 : -1;
@@ -45,6 +46,29 @@ inline int16 smoothRebel1Op0BAnalogInput(int16 inputValue, int16 &filteredValue,
 	return filteredValue;
 }
 
+int16 shapeRebel1Op0BGamepadAxis(int16 inputValue, int16 axisMax) {
+	const int axis = CLIP<int>(inputValue, -axisMax, axisMax);
+	const int absAxis = ABS(axis);
+	const int precisionRadius = MAX<int>(1, (axisMax * 2) / 5);
+	const int precisionOutput = MAX<int>(1, precisionRadius / 3);
+	int shaped = 0;
+
+	if (absAxis <= precisionRadius) {
+		shaped = (absAxis * precisionOutput + precisionRadius / 2) / precisionRadius;
+	} else {
+		const int outerInput = absAxis - precisionRadius;
+		const int outerInputRange = axisMax - precisionRadius;
+		const int outerOutputRange = axisMax - precisionOutput;
+		const int linear = outerInput * outerOutputRange / outerInputRange;
+		const int accel = (outerInput * outerInput * outerOutputRange +
+			(outerInputRange * outerInputRange) / 2) /
+			(outerInputRange * outerInputRange);
+		shaped = precisionOutput + (linear + 2 * accel) / 3;
+	}
+
+	return axis < 0 ? -shaped : shaped;
+}
+
 const int16 kRA1Op09AimXScale[5] = { 0, 44, 88, 128, 165 };
 const int16 kRA1Op09AimYScale[5] = { 256, 252, 240, 221, 196 };
 const int kRA1DosMouseCenterX = 0x140;
@@ -807,10 +831,7 @@ void InsaneRebel1::updateFlightVariantCursor() {
 // jump latch from FUN_231BE, plus FUN_23115's DOS mouse recenter behavior.
 // Enhanced controls are ScummVM-only: they bypass the original recentering state
 // and expose a stable absolute centered mouse axis instead.
-bool InsaneRebel1::shouldInvertTouchYSettingForCurrentLevel() const {
-	if (!isTouchscreenActive())
-		return false;
-
+bool InsaneRebel1::isOp0BReticleControlLevel() const {
 	switch (_currentLevel) {
 	case 1:  // Level 2
 	case 3:  // Level 4
@@ -826,6 +847,84 @@ bool InsaneRebel1::shouldInvertTouchYSettingForCurrentLevel() const {
 	}
 }
 
+bool InsaneRebel1::shouldInvertTouchYSettingForCurrentLevel() const {
+	return isTouchscreenActive() && isOp0BReticleControlLevel();
+}
+
+bool InsaneRebel1::usesRelativeGamepadAimForCurrentLevel() const {
+	return _optEnhancedControls &&
+		(_currentLevel == 3 ||
+		 (_currentLevel == 4 && _levelGameplayPhase == 2) ||
+		 _currentLevel == 9 ||
+		 _currentLevel == 11 ||
+		 _currentLevel == 13);
+}
+
+void InsaneRebel1::resetRelativeGamepadAim() {
+	_gamepadAimAxisX = 0;
+	_gamepadAimAxisY = 0;
+	_gamepadAimActive = false;
+}
+
+bool InsaneRebel1::updateRelativeGamepadAim(int16 &inputX, int16 &inputY, bool *usedJoystick) {
+	if (!usesRelativeGamepadAimForCurrentLevel())
+		return false;
+
+	const int dpadX =
+		(_vm->getActionState(kScummActionInsaneRight) ? 1 : 0) -
+		(_vm->getActionState(kScummActionInsaneLeft) ? 1 : 0);
+	int dpadY =
+		(_vm->getActionState(kScummActionInsaneUp) ? 1 : 0) -
+		(_vm->getActionState(kScummActionInsaneDown) ? 1 : 0);
+
+	const int16 analogAxisX = applyRebel1AnalogDeadzone(_joystickAxisX);
+	const int16 analogAxisY = applyRebel1AnalogDeadzone(_joystickAxisY);
+	const int analogX = CLIP<int32>(((int32)analogAxisX * 127) / Common::JOYAXIS_MAX, -127, 127);
+	int analogY = CLIP<int32>((-(int32)analogAxisY * 100) / Common::JOYAXIS_MAX, -100, 100);
+
+	if (_optControlsYFlip) {
+		dpadY = -dpadY;
+		analogY = -analogY;
+	}
+
+	int deltaX = 0;
+	int deltaY = 0;
+	bool activeGamepadAim = false;
+
+	if (dpadX || dpadY) {
+		const int kDigitalAimStep = 3;
+		deltaX = dpadX * kDigitalAimStep;
+		deltaY = dpadY * kDigitalAimStep;
+		activeGamepadAim = true;
+	} else if (analogX || analogY) {
+		const int kAnalogAimMaxStep = 8;
+		deltaX = analogX * kAnalogAimMaxStep / 127;
+		deltaY = analogY * kAnalogAimMaxStep / 100;
+		activeGamepadAim = true;
+	}
+
+	if (activeGamepadAim) {
+		if (!_gamepadAimActive) {
+			_gamepadAimAxisX = CLIP<int16>(_avgInputX, -127, 127);
+			_gamepadAimAxisY = CLIP<int16>((int16)-_avgInputY, -100, 100);
+		}
+		_gamepadAimAxisX = CLIP<int16>((int16)(_gamepadAimAxisX + deltaX), -127, 127);
+		_gamepadAimAxisY = CLIP<int16>((int16)(_gamepadAimAxisY + deltaY), -100, 100);
+		_gamepadAimActive = true;
+	}
+
+	if (!_gamepadAimActive)
+		return false;
+
+	if (usedJoystick)
+		*usedJoystick = true;
+	_activeInputSource = kInputSourceJoystickRelative;
+	_mouseVirtualValid = false;
+	inputX = _gamepadAimAxisX;
+	inputY = _gamepadAimAxisY;
+	return true;
+}
+
 void InsaneRebel1::preprocessMouseAxes(int16 &inputX, int16 &inputY, bool *usedJoystick) {
 	if (usedJoystick)
 		*usedJoystick = false;
@@ -833,6 +932,9 @@ void InsaneRebel1::preprocessMouseAxes(int16 &inputX, int16 &inputY, bool *usedJ
 	if (_mouseRecentering)
 		return;
 
+	if (updateRelativeGamepadAim(inputX, inputY, usedJoystick))
+		return;
+
 	const int16 analogAxisX = applyRebel1AnalogDeadzone(_joystickAxisX);
 	const int16 analogAxisY = applyRebel1AnalogDeadzone(_joystickAxisY);
 	const int joyX =
@@ -1643,14 +1745,25 @@ void InsaneRebel1::updateGameOp0BPhysics() {
 		inputSourceName = "joystick-analog";
 	else if (_activeInputSource == kInputSourceJoystickDigital)
 		inputSourceName = "joystick-dpad";
+	else if (_activeInputSource == kInputSourceJoystickRelative)
+		inputSourceName = "joystick-relative";
 
-	if (usedJoystick && _optEnhancedControls) {
+	const bool relativeGamepadReticle = usedJoystick && _activeInputSource == kInputSourceJoystickRelative;
+	const bool preciseGamepadReticle = usedJoystick && _optEnhancedControls &&
+		isOp0BReticleControlLevel() && !relativeGamepadReticle;
+
+	if (usedJoystick && _optEnhancedControls && !relativeGamepadReticle) {
 		// The 0x0B first-person handler is shared by multiple RA1 stages. Smooth
-		// analog stick input over time so these sections keep full reach without
-		// feeling hyper-sensitive, while leaving mouse behavior untouched.
+		// analog stick input over time and shape the affected gamepad levels with
+		// a low-gain linear center plus an accelerating outer range.
 		if (op0BAnalogSmoothing) {
-			inputX = smoothRebel1Op0BAnalogInput(inputX, _level2JoystickFilteredX, 127);
-			inputY = smoothRebel1Op0BAnalogInput(inputY, _level2JoystickFilteredY, 100);
+			if (preciseGamepadReticle) {
+				inputX = shapeRebel1Op0BGamepadAxis(inputX, 127);
+				inputY = shapeRebel1Op0BGamepadAxis(inputY, 100);
+			}
+			const int analogRampDivisor = preciseGamepadReticle ? 20 : 10;
+			inputX = smoothRebel1Op0BAnalogInput(inputX, _level2JoystickFilteredX, 127, analogRampDivisor);
+			inputY = smoothRebel1Op0BAnalogInput(inputY, _level2JoystickFilteredY, 100, analogRampDivisor);
 		} else {
 			_level2JoystickFilteredX = 0;
 			_level2JoystickFilteredY = 0;
@@ -1663,11 +1776,12 @@ void InsaneRebel1::updateGameOp0BPhysics() {
 	}
 	_inputAxisDeltaX = inputX;
 
-	debugC(DEBUG_INSANE, "RA1 GAME 0x0B input: frame=%d source=%s controls=%s window=%d view=(%d,%d) health=%d prevFlags=0x%02x axis=(%d,%d) mouse=(%d,%d) actions(L,R,U,D)=(%d,%d,%d,%d) raw=(%d,%d) final=(%d,%d) level=%d opcode=0x%X",
+	debugC(DEBUG_INSANE, "RA1 GAME 0x0B input: frame=%d source=%s controls=%s window=%d precisionPad=%d view=(%d,%d) health=%d prevFlags=0x%02x axis=(%d,%d) mouse=(%d,%d) actions(L,R,U,D)=(%d,%d,%d,%d) raw=(%d,%d) final=(%d,%d) level=%d opcode=0x%X",
 		_gameCounter,
 		inputSourceName,
 		_optEnhancedControls ? "enhanced" : "original",
 		gameOp0BSmoothWindow,
+		preciseGamepadReticle ? 1 : 0,
 		_perspectiveX, _perspectiveY,
 		_health, _prevDamageFlags,
 		_joystickAxisX, _joystickAxisY,
@@ -2031,6 +2145,7 @@ void InsaneRebel1::handleGameOpcode5EReset(uint32 param1) {
 	_mouseVirtualValid = false;
 	_level2JoystickFilteredX = 0;
 	_level2JoystickFilteredY = 0;
+	resetRelativeGamepadAim();
 
 	_playerFired = false;
 	_fireCooldown = 0;
diff --git a/engines/scumm/insane/rebel1/menu.cpp b/engines/scumm/insane/rebel1/menu.cpp
index 15d07ef18c2..7677cf0d985 100644
--- a/engines/scumm/insane/rebel1/menu.cpp
+++ b/engines/scumm/insane/rebel1/menu.cpp
@@ -632,8 +632,13 @@ bool InsaneRebel1::notifyEvent(const Common::Event &event) {
 		return true;
 	}
 
-	if (event.type == Common::EVENT_MOUSEMOVE && !_mouseRecentering)
+	if (event.type == Common::EVENT_MOUSEMOVE && !_mouseRecentering) {
+		if (_gamepadAimActive && usesRelativeGamepadAimForCurrentLevel() &&
+				_interactiveVideoActive && !_menuActive &&
+				(event.relMouse.x != 0 || event.relMouse.y != 0))
+			_gamepadAimActive = false;
 		_activeInputSource = kInputSourceMouse;
+	}
 
 	// Android direct touch reports taps as left-clicks. Treat those as mouse input
 	// during gameplay so tap-to-aim-and-fire works even if a joystick event was last.
diff --git a/engines/scumm/insane/rebel1/rebel.cpp b/engines/scumm/insane/rebel1/rebel.cpp
index 59a87b0f136..248b9ae29c5 100644
--- a/engines/scumm/insane/rebel1/rebel.cpp
+++ b/engines/scumm/insane/rebel1/rebel.cpp
@@ -276,6 +276,9 @@ InsaneRebel1::InsaneRebel1(ScummEngine_v7 *scumm) : Insane(), _vm(scumm) {
 	_lastJoystickAxisEventTime = 0;
 	_level2JoystickFilteredX = 0;
 	_level2JoystickFilteredY = 0;
+	_gamepadAimAxisX = 0;
+	_gamepadAimAxisY = 0;
+	_gamepadAimActive = false;
 	_activeInputSource = kInputSourceMouse;
 
 	_currentLevel = 0;
diff --git a/engines/scumm/insane/rebel1/rebel.h b/engines/scumm/insane/rebel1/rebel.h
index 5264989c70c..4d1478e66ae 100644
--- a/engines/scumm/insane/rebel1/rebel.h
+++ b/engines/scumm/insane/rebel1/rebel.h
@@ -221,7 +221,11 @@ private:
 	void updateTurretPhysics();
 	void updateTurretShipDirection(int16 offsetY);
 	void getCollisionShipCenter(int16 &x, int16 &y) const;
+	bool isOp0BReticleControlLevel() const;
 	bool shouldInvertTouchYSettingForCurrentLevel() const;
+	bool usesRelativeGamepadAimForCurrentLevel() const;
+	void resetRelativeGamepadAim();
+	bool updateRelativeGamepadAim(int16 &inputX, int16 &inputY, bool *usedJoystick);
 	void preprocessMouseAxes(int16 &inputX, int16 &inputY, bool *usedJoystick = nullptr);
 	void rebuildProjectionTable(int16 curveStep, int16 curveExtent);
 	void resetProjectionTable();
@@ -364,10 +368,14 @@ private:
 	uint32 _lastJoystickAxisEventTime;
 	int16 _level2JoystickFilteredX; // Smoothed Level 2 analog X input
 	int16 _level2JoystickFilteredY; // Smoothed Level 2 analog Y input
+	int16 _gamepadAimAxisX; // Relative gamepad 0x0B aim in preprocessed input space
+	int16 _gamepadAimAxisY;
+	bool _gamepadAimActive;
 	enum InputSource {
 		kInputSourceMouse,
 		kInputSourceJoystickAnalog,
-		kInputSourceJoystickDigital
+		kInputSourceJoystickDigital,
+		kInputSourceJoystickRelative
 	};
 	InputSource _activeInputSource;
 
diff --git a/engines/scumm/insane/rebel1/runlevels.cpp b/engines/scumm/insane/rebel1/runlevels.cpp
index 3623029abfb..1b7c05a8a0a 100644
--- a/engines/scumm/insane/rebel1/runlevels.cpp
+++ b/engines/scumm/insane/rebel1/runlevels.cpp
@@ -246,6 +246,7 @@ void InsaneRebel1::resetLevelInputHistory(bool resetAxisDeltaX) {
 		_inputAxisDeltaX = 0;
 	_avgInputX = 0;
 	_avgInputY = 0;
+	resetRelativeGamepadAim();
 }
 
 void InsaneRebel1::resetLevelAttemptState(int16 flyControlMode, int16 gameplayPhase,
@@ -1603,6 +1604,7 @@ void InsaneRebel1::setupInteractiveVideoState(int32 startFrame) {
 	if (!preserveRuntimeState) {
 		_onFootInitialized = false;  // Reset so each segment triggers counter==0 init
 		resetFrameObjectState();
+		resetRelativeGamepadAim();
 	}
 	_vm->_smushVideoShouldFinish = false;
 	// Route resumes stay in the same gameplay flow in the original executable.



From noreply at scummvm.org  Thu Jun  4 15:12:23 2026
From: noreply at scummvm.org (neuromancer)
Date: Thu,  4 Jun 2026 15:12:23 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 d9fc895bf5e6e9c43ae00274ae88053d317af2d4
Message-ID: <20260604151223.2A3F5700573@ron.scummvm.net>

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:
31a97081b0 SCUMM: RA1: proper indicator rendering for level 8
6748744720 SCUMM: RA1: proper winning condition for level 8
0d51b3e9c2 SCUMM: RA1: adding more missing wining conditions
b820b7a630 SCUMM: RA1: improve shift + S to skip some sequences that have wining conditions
d9fc895bf5 SCUMM: RA1: added missing overlay in level 5


Commit: 31a97081b053069f9b6437c87f199444617ec61e
    https://github.com/scummvm/scummvm/commit/31a97081b053069f9b6437c87f199444617ec61e
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-04T15:19:15+02:00

Commit Message:
SCUMM: RA1: proper indicator rendering for level 8

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


diff --git a/engines/scumm/smush/rebel/smush_player_ra1.cpp b/engines/scumm/smush/rebel/smush_player_ra1.cpp
index aa213b17a65..880eb987be9 100644
--- a/engines/scumm/smush/rebel/smush_player_ra1.cpp
+++ b/engines/scumm/smush/rebel/smush_player_ra1.cpp
@@ -241,11 +241,13 @@ bool SmushPlayerRebel1::handleGameFetch(int32 subSize, Common::SeekableReadStrea
 			if (rebel1->isInteractiveVideoActive()) {
 				const uint16 gameOp = rebel1->getActiveGameOpcode();
 				const bool fullWidthStoredPatch = (_storedFobjWidth == _vm->_screenWidth);
-				// 0x0B (asteroid/surface) and 0x19/0x1A (on-foot) use SetCameraOffset
-				// directly ??? no projection-based FTCH placement. Level 4 phase 2
-				// also stores a full-width screen-space patch (320x180) that DOS
-				// restores without the centered 1/4 projection warp.
-				if (fullWidthStoredPatch || gameOp == 0x0B ||
+				const bool level8WalkerPatch = (gameOp == 0x0B && rebel1->getCurrentLevel() == 7);
+				// Keep the direct viewport placement used by the existing 0x0B
+				// compatibility path, except for Level 8's walker cockpit patch.
+				// DOS FTCH goes through FUN_28D0A, which sets DispatchFobjCodec
+				// flag 0x800; Level 8 depends on that quarter-projection so the
+				// cockpit and RunLevel8Flow indicators move together.
+				if (fullWidthStoredPatch || (gameOp == 0x0B && !level8WalkerPatch) ||
 						gameOp == 0x19 || gameOp == 0x1A) {
 					left += _ra1ViewportOffsetX;
 					top += _ra1ViewportOffsetY;


Commit: 67487447204cab06b657be2eb19eaa42a7d56781
    https://github.com/scummvm/scummvm/commit/67487447204cab06b657be2eb19eaa42a7d56781
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-04T15:30:13+02:00

Commit Message:
SCUMM: RA1: proper winning condition for level 8

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


diff --git a/engines/scumm/insane/rebel1/iact.cpp b/engines/scumm/insane/rebel1/iact.cpp
index 8baa313f60f..48f1ff485ee 100644
--- a/engines/scumm/insane/rebel1/iact.cpp
+++ b/engines/scumm/insane/rebel1/iact.cpp
@@ -2110,6 +2110,8 @@ void InsaneRebel1::handleGameOpcode5EReset(uint32 param1) {
 		return;
 	}
 
+	const int16 walkerReplayKillCount = (_walkerRoundReplay && _currentLevel == 7) ? _killCount : 0;
+
 	_damageFlags = 0;
 	_prevDamageFlags = 0;
 	_damageCooldown = 0;
@@ -2164,6 +2166,8 @@ void InsaneRebel1::handleGameOpcode5EReset(uint32 param1) {
 	_gostSlotIdx = 0;
 	_killCount = 0;
 	_lastHitTarget = 0;
+	if (_walkerRoundReplay && _currentLevel == 7)
+		_killCount = walkerReplayKillCount;
 
 	// Field1 == 0 corresponds to baseline recenter behavior in the original.
 	if ((int32)param1 == 0) {
diff --git a/engines/scumm/insane/rebel1/rebel.cpp b/engines/scumm/insane/rebel1/rebel.cpp
index 248b9ae29c5..cdbf2d37e32 100644
--- a/engines/scumm/insane/rebel1/rebel.cpp
+++ b/engines/scumm/insane/rebel1/rebel.cpp
@@ -418,6 +418,7 @@ InsaneRebel1::InsaneRebel1(ScummEngine_v7 *scumm) : Insane(), _vm(scumm) {
 	_walkerHealth = 100;
 	_walkerTimer = 0;
 	_walkerBranchChoice = 0;
+	_walkerRoundReplay = false;
 	resetFrameObjectState();
 
 	if (loadRA1Nut("SYS/TALKFONT.NUT", _hudFontBank)) {
diff --git a/engines/scumm/insane/rebel1/rebel.h b/engines/scumm/insane/rebel1/rebel.h
index 4d1478e66ae..23570cc8d13 100644
--- a/engines/scumm/insane/rebel1/rebel.h
+++ b/engines/scumm/insane/rebel1/rebel.h
@@ -694,6 +694,7 @@ private:
 	int16 _walkerHealth;     // Walker health percentage (0-100), init=100
 	int16 _walkerTimer;      // Attack window countdown (100???0)
 	int16 _walkerBranchChoice; // Directional choice: 0=none, 1=left, 2=right
+	bool _walkerRoundReplay; // Preserve walker damage across a replayed L8 route
 
 	// Attack window frame numbers per route (3 routes ?? 3 windows)
 	// Route 0: 2588/1709/262, Route 1: 2323/1444/-2, Route 2: 877/-2/-2
diff --git a/engines/scumm/insane/rebel1/runlevels.cpp b/engines/scumm/insane/rebel1/runlevels.cpp
index 1b7c05a8a0a..35a01e30779 100644
--- a/engines/scumm/insane/rebel1/runlevels.cpp
+++ b/engines/scumm/insane/rebel1/runlevels.cpp
@@ -821,15 +821,19 @@ bool InsaneRebel1::runLevel8() {
 		_walkerHealth = 100;
 		_walkerTimer = 0;
 		_walkerBranchChoice = 0;
+		_walkerRoundReplay = false;
 
 		int route = 0;
 		int32 routeStartFrame = 0;
+		bool replayingRound = false;
 		while (!shouldAbortGameFlow()) {
 			_levelRouteIndex = route;
 			_pendingRouteIndex = -1;
 			_pendingRouteStartFrame = routeStartFrame;
 			_pendingRouteCutoverFrame = -1;
+			_walkerRoundReplay = replayingRound;
 			playInteractiveVideo(kLevel8Routes[route], routeStartFrame);
+			_walkerRoundReplay = false;
 			if (shouldAbortGameFlow())
 				return false;
 
@@ -839,23 +843,39 @@ bool InsaneRebel1::runLevel8() {
 			if (_walkerHealth <= 0)
 				break;
 
-			if (_pendingRouteIndex < 0 || _pendingRouteIndex == route)
-				break;
+			if (_pendingRouteIndex >= 0 && _pendingRouteIndex != route) {
+				// RunLevel8Flow uses PlayAnmFile(..., g_frameCounter, 1, -1)
+				// when it branches from one walker route to another. That wrapper
+				// keeps the current ANM alive for seven more gameplay frames, then
+				// opens the destination route while preserving the active state.
+				routeStartFrame = _pendingRouteStartFrame;
+				route = _pendingRouteIndex;
+				replayingRound = false;
+				continue;
+			}
 
-			// RunLevel8Flow uses PlayAnmFile(..., g_frameCounter, 1, -1)
-			// when it branches from one walker route to another. That wrapper
-			// keeps the current ANM alive for seven more gameplay frames, then
-			// opens the destination route while preserving the active state.
-			routeStartFrame = _pendingRouteStartFrame;
-			route = _pendingRouteIndex;
+			// RunLevel8Flow keeps pumping the active route while the walker
+			// shield register is nonzero. ScummVM's blocking SMUSH play returns
+			// when one route pass ends, so explicitly replay that route and
+			// preserve the accumulated walker damage.
+			debugC(DEBUG_INSANE, "RA1 L8 replaying route=%d walkerHealth=%d killCount=%d",
+				route, (int)_walkerHealth, (int)_killCount);
+			routeStartFrame = 0;
+			_walkerTimer = 0;
+			_walkerBranchChoice = 0;
+			replayingRound = true;
 		}
 
 		_levelRouteIndex = -1;
 		_pendingRouteIndex = -1;
 		_pendingRouteStartFrame = 0;
 		_pendingRouteCutoverFrame = -1;
+		_walkerRoundReplay = false;
 
-		if (_health >= 0) {
+		if (shouldAbortGameFlow())
+			return false;
+
+		if (_health >= 0 && _walkerHealth <= 0) {
 			playChapterCompleteCinematic("LVL8/L8END.ANM", 8, 0x5F, 5);
 			return !shouldAbortGameFlow();
 		}


Commit: 0d51b3e9c2a701daa9c0bb3f22d13c9da0eb1a1b
    https://github.com/scummvm/scummvm/commit/0d51b3e9c2a701daa9c0bb3f22d13c9da0eb1a1b
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-04T16:38:32+02:00

Commit Message:
SCUMM: RA1: adding more missing wining conditions

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


diff --git a/engines/scumm/insane/rebel1/rebel.cpp b/engines/scumm/insane/rebel1/rebel.cpp
index cdbf2d37e32..4cfee698554 100644
--- a/engines/scumm/insane/rebel1/rebel.cpp
+++ b/engines/scumm/insane/rebel1/rebel.cpp
@@ -317,6 +317,7 @@ InsaneRebel1::InsaneRebel1(ScummEngine_v7 *scumm) : Insane(), _vm(scumm) {
 	_maxChapterUnlocked = 0;
 	_unlockAllLevels = ConfMan.getBool("rebel1_unlock_all");
 	_interactiveVideoActive = false;
+	_preserveInteractiveRuntimeState = false;
 	_restoreInteractiveVideoAudioState = false;
 	memset(_savedInteractiveVideoTrackState, 0, sizeof(_savedInteractiveVideoTrackState));
 	memset(_savedInteractiveVideoTrackGroupId, 0, sizeof(_savedInteractiveVideoTrackGroupId));
diff --git a/engines/scumm/insane/rebel1/rebel.h b/engines/scumm/insane/rebel1/rebel.h
index 23570cc8d13..8ff100263bc 100644
--- a/engines/scumm/insane/rebel1/rebel.h
+++ b/engines/scumm/insane/rebel1/rebel.h
@@ -521,6 +521,7 @@ private:
 
 	// True only while an interactive gameplay SMUSH is running.
 	bool _interactiveVideoActive;
+	bool _preserveInteractiveRuntimeState;
 
 	// 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 35a01e30779..7f4e7ce0d41 100644
--- a/engines/scumm/insane/rebel1/runlevels.cpp
+++ b/engines/scumm/insane/rebel1/runlevels.cpp
@@ -551,13 +551,35 @@ bool InsaneRebel1::runLevel4() {
 		_shieldGenHitsA = 0;
 		_shieldGenHitsB = 0;
 		_levelGameplayPhase = 1;
-		playInteractiveVideo("LVL4/L4PLAY1.ANM");
+		bool shieldGeneratorsDestroyed = false;
+		bool replayingShieldGeneratorPhase = false;
+		while (!shouldAbortGameFlow()) {
+			_preserveInteractiveRuntimeState = replayingShieldGeneratorPhase;
+			playInteractiveVideo("LVL4/L4PLAY1.ANM");
+			_preserveInteractiveRuntimeState = false;
+			if (shouldAbortGameFlow())
+				return false;
+
+			if (_health < 0)
+				break;
+
+			shieldGeneratorsDestroyed =
+				_protectedTargetA == 0 && _protectedTargetB == 0 &&
+				_shieldGenHitsA > 0x30 + 0x3C && _shieldGenHitsB > 0x30;
+			if (shieldGeneratorsDestroyed)
+				break;
+
+			debugC(DEBUG_INSANE, "RA1 L4 replaying shield-generator phase: hits=(%d,%d) protected=(%d,%d)",
+				(int)_shieldGenHitsA, (int)_shieldGenHitsB,
+				(int)_protectedTargetA, (int)_protectedTargetB);
+			replayingShieldGeneratorPhase = true;
+		}
 		_protectedTargetA = 0;
 		_protectedTargetB = 0;
 		if (shouldAbortGameFlow())
 			return false;
 
-		if (_health >= 0) {
+		if (_health >= 0 && shieldGeneratorsDestroyed) {
 			// Phase 2: torpedo run. The DOS loop enables torpedo mode at frame
 			// 0x3E and exits early as soon as killCount becomes nonzero.
 			_activeGameOpcode = 0;
@@ -572,7 +594,7 @@ bool InsaneRebel1::runLevel4() {
 				return false;
 		}
 
-		if (_health >= 0) {
+		if (_health >= 0 && shieldGeneratorsDestroyed) {
 			// L4END1 = torpedo hit, L4END2 = torpedo missed
 			const bool torpedoHit = (_killCount != 0);
 			playChapterCompleteCinematic(torpedoHit ? "LVL4/L4END1.ANM" : "LVL4/L4END2.ANM",
@@ -1290,11 +1312,31 @@ bool InsaneRebel1::runLevel14() {
 		_level14SuccessFrames = 0;
 
 		// Phase 1: targeting surface cannons
-		playInteractiveVideo("LVL14/L14PLAY.ANM");
+		bool level14Phase1Complete = false;
+		bool replayingLevel14Phase1 = false;
+		while (!shouldAbortGameFlow()) {
+			_preserveInteractiveRuntimeState = replayingLevel14Phase1;
+			playInteractiveVideo("LVL14/L14PLAY.ANM");
+			_preserveInteractiveRuntimeState = false;
+			if (shouldAbortGameFlow())
+				return false;
+
+			if (_health < 0)
+				break;
+
+			level14Phase1Complete = _level14SuccessFrames >= 0x3C;
+			if (level14Phase1Complete)
+				break;
+
+			debugC(DEBUG_INSANE, "RA1 L14 replaying phase 1: successFrames=%d targetsDestroyed=%d",
+				(int)_level14SuccessFrames, areLevel14Phase1TargetsDestroyed() ? 1 : 0);
+			replayingLevel14Phase1 = true;
+		}
 		if (shouldAbortGameFlow())
 			return false;
 
-		if (_health >= 0) {
+		bool level14Phase2Complete = false;
+		if (_health >= 0 && level14Phase1Complete) {
 			// Phase 2: exhaust port approach
 			_activeGameOpcode = 0;
 			_gameLatch5D = 0;
@@ -1308,25 +1350,48 @@ bool InsaneRebel1::runLevel14() {
 			_level14Play2BSpliced = false;
 			_level14Play2BSpliceFrame = 0;
 
-			playInteractiveVideo("LVL14/L14PLAY2.ANM");
-			if (shouldAbortGameFlow())
-				return false;
-
-			if (_health >= 0 && _level14Play2BSplicePending) {
-				const int32 spliceFrame = _level14Play2BSpliceFrame;
-				_level14Play2BSplicePending = false;
-
-				// DOS queues L14PLY2B from the L14PLAY2 loop with startFrame
-				// equal to L14PLAY2's old timeline frame. L14PLY2B itself is the
-				// continuation clip, so the port starts it from frame 0 but uses
-				// the non-zero frame argument to preserve gameplay/video state.
-				playInteractiveVideo("LVL14/L14PLY2B.ANM", spliceFrame);
+			const char *level14Phase2Video = "LVL14/L14PLAY2.ANM";
+			bool replayingLevel14Phase2 = false;
+			while (!shouldAbortGameFlow()) {
+				_preserveInteractiveRuntimeState = replayingLevel14Phase2;
+				playInteractiveVideo(level14Phase2Video);
+				_preserveInteractiveRuntimeState = false;
 				if (shouldAbortGameFlow())
 					return false;
+
+				if (_health < 0)
+					break;
+
+				if (_level14Play2BSplicePending) {
+					const int32 spliceFrame = _level14Play2BSpliceFrame;
+					_level14Play2BSplicePending = false;
+					level14Phase2Video = "LVL14/L14PLY2B.ANM";
+
+					// DOS queues L14PLY2B from the L14PLAY2 loop with startFrame
+					// equal to L14PLAY2's old timeline frame. L14PLY2B itself is the
+					// continuation clip, so the port starts it from frame 0 but uses
+					// the non-zero frame argument to preserve gameplay/video state.
+					playInteractiveVideo(level14Phase2Video, spliceFrame);
+					if (shouldAbortGameFlow())
+						return false;
+					if (_health < 0)
+						break;
+				}
+
+				level14Phase2Complete = _level14SuccessFrames >= 0x3C;
+				if (level14Phase2Complete)
+					break;
+
+				debugC(DEBUG_INSANE, "RA1 L14 replaying phase 2: video=%s successFrames=%d targetsDestroyed=%d",
+					level14Phase2Video, (int)_level14SuccessFrames,
+					areLevel14Phase2TargetsDestroyed() ? 1 : 0);
+				replayingLevel14Phase2 = true;
 			}
+			if (shouldAbortGameFlow())
+				return false;
 		}
 
-		if (_health >= 0) {
+		if (_health >= 0 && level14Phase2Complete) {
 			playChapterCompleteCinematic("LVL14/L14END.ANM", 14, 0x69, 5,
 				nullptr, nullptr, 0, nullptr, nullptr, 0, _difficulty + 10);
 			return !shouldAbortGameFlow();
@@ -1612,8 +1677,8 @@ void InsaneRebel1::restoreInteractiveVideoAudioState() {
 void InsaneRebel1::setupInteractiveVideoState(int32 startFrame) {
 	const bool level7RouteSplice = (_currentLevel == 6 && _levelRouteIndex > 0);
 	const bool resumingRoute = startFrame > 0;
-	const bool preserveRuntimeState = resumingRoute || level7RouteSplice;
-	const bool preserveVideoState = resumingRoute && !level7RouteSplice;
+	const bool preserveRuntimeState = _preserveInteractiveRuntimeState || resumingRoute || level7RouteSplice;
+	const bool preserveVideoState = !_preserveInteractiveRuntimeState && resumingRoute && !level7RouteSplice;
 
 	SmushPlayer *splayer = _vm->_splayer;
 	_player = splayer;
@@ -1689,11 +1754,12 @@ 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;
 
 	// Center mouse, hide system cursor (we draw our own), lock mouse to window.
-	// Level 7 route splices happen inside one original gameplay loop, so keep
-	// the current input state instead of recentering between route clips.
-	if (!level7RouteSplice) {
+	// Some replays happen inside one original gameplay loop, so keep the current
+	// input state instead of recentering between route clips.
+	if (!preserveInputState) {
 		// On touchscreen devices the DOS recenter-the-cursor aiming model does not apply;
 		// warping/locking the system mouse there injects spurious motion that drifts
 		// direct touch aiming and on-screen controls.
@@ -1723,12 +1789,15 @@ void InsaneRebel1::playInteractiveVideoFile(const char *filename, int32 videoOff
 // Play interactive gameplay video (with ship physics + HUD).
 void InsaneRebel1::playInteractiveVideo(const char *filename, int32 startFrame) {
 	debugC(DEBUG_INSANE, "InsaneRebel1::playInteractiveVideo('%s', startFrame=%d)", filename, startFrame);
-	if (shouldAbortGameFlow())
+	if (shouldAbortGameFlow()) {
+		_preserveInteractiveRuntimeState = false;
 		return;
+	}
 
 	int32 videoStartFrame = 0;
 	int32 videoOffset = 0;
-	const bool preserveRuntimeState = (startFrame > 0) || (_currentLevel == 6 && _levelRouteIndex > 0);
+	const bool preserveRuntimeState = _preserveInteractiveRuntimeState ||
+		(startFrame > 0) || (_currentLevel == 6 && _levelRouteIndex > 0);
 
 	if (!preserveRuntimeState)
 		resetInteractiveVideoAudio();
@@ -1737,6 +1806,7 @@ void InsaneRebel1::playInteractiveVideo(const char *filename, int32 startFrame)
 	captureInteractiveVideoInput();
 	playInteractiveVideoFile(filename, videoOffset, videoStartFrame);
 	releaseInteractiveVideoInput();
+	_preserveInteractiveRuntimeState = false;
 }
 
 } // End of namespace Scumm


Commit: b820b7a63075dad02d735a88ac729b5cf73d130d
    https://github.com/scummvm/scummvm/commit/b820b7a63075dad02d735a88ac729b5cf73d130d
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-04T16:51:26+02:00

Commit Message:
SCUMM: RA1: improve shift + S to skip some sequences that have wining conditions

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


diff --git a/engines/scumm/insane/rebel1/menu.cpp b/engines/scumm/insane/rebel1/menu.cpp
index 7677cf0d985..45eb5a1d6e5 100644
--- a/engines/scumm/insane/rebel1/menu.cpp
+++ b/engines/scumm/insane/rebel1/menu.cpp
@@ -895,6 +895,7 @@ bool InsaneRebel1::notifyEvent(const Common::Event &event) {
 		if (_interactiveVideoActive && !_menuActive &&
 			event.kbd.keycode == Common::KEYCODE_s &&
 			event.kbd.hasFlags(Common::KBD_SHIFT)) {
+			_interactiveVideoCheatSkipped = true;
 			_vm->_smushVideoShouldFinish = true;
 			return true;
 		}
diff --git a/engines/scumm/insane/rebel1/rebel.cpp b/engines/scumm/insane/rebel1/rebel.cpp
index 4cfee698554..4fd79f1c7eb 100644
--- a/engines/scumm/insane/rebel1/rebel.cpp
+++ b/engines/scumm/insane/rebel1/rebel.cpp
@@ -318,6 +318,7 @@ InsaneRebel1::InsaneRebel1(ScummEngine_v7 *scumm) : Insane(), _vm(scumm) {
 	_unlockAllLevels = ConfMan.getBool("rebel1_unlock_all");
 	_interactiveVideoActive = false;
 	_preserveInteractiveRuntimeState = false;
+	_interactiveVideoCheatSkipped = false;
 	_restoreInteractiveVideoAudioState = false;
 	memset(_savedInteractiveVideoTrackState, 0, sizeof(_savedInteractiveVideoTrackState));
 	memset(_savedInteractiveVideoTrackGroupId, 0, sizeof(_savedInteractiveVideoTrackGroupId));
diff --git a/engines/scumm/insane/rebel1/rebel.h b/engines/scumm/insane/rebel1/rebel.h
index 8ff100263bc..f74d6dd5d69 100644
--- a/engines/scumm/insane/rebel1/rebel.h
+++ b/engines/scumm/insane/rebel1/rebel.h
@@ -522,6 +522,7 @@ private:
 	// True only while an interactive gameplay SMUSH is running.
 	bool _interactiveVideoActive;
 	bool _preserveInteractiveRuntimeState;
+	bool _interactiveVideoCheatSkipped;
 
 	// 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 7f4e7ce0d41..215fe75864f 100644
--- a/engines/scumm/insane/rebel1/runlevels.cpp
+++ b/engines/scumm/insane/rebel1/runlevels.cpp
@@ -412,7 +412,7 @@ bool InsaneRebel1::runLevel1() {
 				if (_health < 0)
 					break;
 
-				if (_killCount > 4) {
+				if (_killCount > 4 || _interactiveVideoCheatSkipped) {
 					const char *pathText = _rightPathSelected ? "Path Taken: Hard" : "Path Taken: Easy";
 					const int pathBonus = _rightPathSelected ? _tuning.bonus * 3 : 0;
 					char accuracyText[80];
@@ -563,9 +563,9 @@ bool InsaneRebel1::runLevel4() {
 			if (_health < 0)
 				break;
 
-			shieldGeneratorsDestroyed =
-				_protectedTargetA == 0 && _protectedTargetB == 0 &&
-				_shieldGenHitsA > 0x30 + 0x3C && _shieldGenHitsB > 0x30;
+			shieldGeneratorsDestroyed = _interactiveVideoCheatSkipped ||
+				(_protectedTargetA == 0 && _protectedTargetB == 0 &&
+				_shieldGenHitsA > 0x30 + 0x3C && _shieldGenHitsB > 0x30);
 			if (shieldGeneratorsDestroyed)
 				break;
 
@@ -640,7 +640,7 @@ bool InsaneRebel1::runLevel5() {
 			return false;
 		}
 
-		if (_killCount <= 2) {
+		if (_killCount <= 2 && !_interactiveVideoCheatSkipped) {
 			if (_lives > 0) {
 				_lives--;
 				playCinematic("LVL5/L5RETRY.ANM");
@@ -862,6 +862,11 @@ bool InsaneRebel1::runLevel8() {
 			if (_health < 0)
 				break;
 
+			if (_interactiveVideoCheatSkipped) {
+				_walkerHealth = 0;
+				break;
+			}
+
 			if (_walkerHealth <= 0)
 				break;
 
@@ -941,6 +946,8 @@ bool InsaneRebel1::runLevel9() {
 			playInteractiveVideo(filename);
 			if (shouldAbortGameFlow() || _health < 0)
 				return -1;
+			if (_interactiveVideoCheatSkipped)
+				return (_shipPosX < kRA1CenterX) ? 0 : 1;
 			if (_killCount > 0)
 				return (_shipPosX < kRA1CenterX) ? 0 : 1;
 
@@ -1011,7 +1018,7 @@ bool InsaneRebel1::runLevel9() {
 			if (_health < 0)
 				break;
 
-			if (_killCount < 15) {
+			if (_killCount < 15 && !_interactiveVideoCheatSkipped) {
 				playInteractiveVideo("LVL9/L9PLAY3B.ANM");
 				if (shouldAbortGameFlow())
 					return false;
@@ -1164,7 +1171,7 @@ bool InsaneRebel1::runLevel11() {
 				break;
 
 			// Original: killCount > 4 means pass
-			if (_killCount > 4)
+			if (_killCount > 4 || _interactiveVideoCheatSkipped)
 				break;
 
 			// Not enough kills ??? retry
@@ -1223,7 +1230,7 @@ bool InsaneRebel1::runLevel12() {
 			if (shouldAbortGameFlow())
 				return false;
 
-			if (_levelGameplayPhase == 1) {
+			if (_levelGameplayPhase == 1 && !_interactiveVideoCheatSkipped) {
 				playCinematic("LVL12/L12RETRY.ANM");
 				if (shouldAbortGameFlow())
 					return false;
@@ -1324,7 +1331,7 @@ bool InsaneRebel1::runLevel14() {
 			if (_health < 0)
 				break;
 
-			level14Phase1Complete = _level14SuccessFrames >= 0x3C;
+			level14Phase1Complete = _interactiveVideoCheatSkipped || _level14SuccessFrames >= 0x3C;
 			if (level14Phase1Complete)
 				break;
 
@@ -1362,6 +1369,11 @@ bool InsaneRebel1::runLevel14() {
 				if (_health < 0)
 					break;
 
+				if (_interactiveVideoCheatSkipped) {
+					level14Phase2Complete = true;
+					break;
+				}
+
 				if (_level14Play2BSplicePending) {
 					const int32 spliceFrame = _level14Play2BSpliceFrame;
 					_level14Play2BSplicePending = false;
@@ -1378,7 +1390,7 @@ bool InsaneRebel1::runLevel14() {
 						break;
 				}
 
-				level14Phase2Complete = _level14SuccessFrames >= 0x3C;
+				level14Phase2Complete = _interactiveVideoCheatSkipped || _level14SuccessFrames >= 0x3C;
 				if (level14Phase2Complete)
 					break;
 
@@ -1454,7 +1466,7 @@ bool InsaneRebel1::runLevel15() {
 				return false;
 		}
 
-		if (_health >= 0 && !_torpedoFired) {
+		if (_health >= 0 && !_torpedoFired && !_interactiveVideoCheatSkipped) {
 			debugC(DEBUG_INSANE, "InsaneRebel1::runLevel15: Level 15 torpedo run ended without exhaust-port hit");
 			return false;
 		}
@@ -1791,9 +1803,11 @@ void InsaneRebel1::playInteractiveVideo(const char *filename, int32 startFrame)
 	debugC(DEBUG_INSANE, "InsaneRebel1::playInteractiveVideo('%s', startFrame=%d)", filename, startFrame);
 	if (shouldAbortGameFlow()) {
 		_preserveInteractiveRuntimeState = false;
+		_interactiveVideoCheatSkipped = false;
 		return;
 	}
 
+	_interactiveVideoCheatSkipped = false;
 	int32 videoStartFrame = 0;
 	int32 videoOffset = 0;
 	const bool preserveRuntimeState = _preserveInteractiveRuntimeState ||


Commit: d9fc895bf5e6e9c43ae00274ae88053d317af2d4
    https://github.com/scummvm/scummvm/commit/d9fc895bf5e6e9c43ae00274ae88053d317af2d4
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-04T17:11:24+02:00

Commit Message:
SCUMM: RA1: added missing overlay in level 5

Changed paths:
    engines/scumm/insane/rebel1/rebel.h
    engines/scumm/insane/rebel1/render.cpp
    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 f74d6dd5d69..26dc27c6014 100644
--- a/engines/scumm/insane/rebel1/rebel.h
+++ b/engines/scumm/insane/rebel1/rebel.h
@@ -111,6 +111,7 @@ public:
 	bool isTouchscreenActive() const;
 	void setFrameHasGameChunk(bool hasGameChunk) { _frameHasGameChunk = hasGameChunk; }
 	int getCurrentLevel() const { return _currentLevel; }
+	int getLevelGameplayPhase() const { return _levelGameplayPhase; }
 	uint16 getActiveGameOpcode() const { return _activeGameOpcode; }
 	uint16 getEffectiveGameOpcode() const;
 	bool hasFrameGameOpcode(uint16 opcode) const {
@@ -245,6 +246,7 @@ private:
 		bool drawTargetBoxes);
 	void handleLevel14Play2BSplice(int32 curFrame, int32 maxFrame);
 	void renderLevel7RouteOverlays(byte *dst, int pitch, int width, int height);
+	void renderLevel5Part2Overlay(byte *dst, int pitch, int width, int height, int32 curFrame);
 	void renderLevel11HitsOverlay(byte *dst, int pitch, int width, int height);
 	void resetEnemyShotSlots();
 	void renderLevel13EnemyShots(byte *dst, int pitch, int width, int height);
diff --git a/engines/scumm/insane/rebel1/render.cpp b/engines/scumm/insane/rebel1/render.cpp
index f428e495c39..1a17b10eb9f 100644
--- a/engines/scumm/insane/rebel1/render.cpp
+++ b/engines/scumm/insane/rebel1/render.cpp
@@ -797,6 +797,9 @@ void InsaneRebel1::procPostRendering(byte *renderBitmap, int32 codecparam, int32
 	renderExplosions(renderBitmap, pitch, width, height);
 	handleLevel14Play2BSplice(curFrame, maxFrame);
 
+	if (_currentLevel == 4 && _levelGameplayPhase == 2)
+		renderLevel5Part2Overlay(renderBitmap, pitch, width, height, curFrame);
+
 	if (_currentLevel == 10)
 		renderLevel11HitsOverlay(renderBitmap, pitch, width, height);
 
@@ -1026,6 +1029,35 @@ void InsaneRebel1::renderLevel11HitsOverlay(byte *dst, int pitch, int width, int
 	drawFontBankString(dst, pitch, width, height, 0x119, 0x16, hitsStr);
 }
 
+// renderLevel5Part2Overlay ??? RunLevel5Flow (0x176D0-0x1777E) draws the
+// part-2 instruction reveal, then switches to the live target count.
+void InsaneRebel1::renderLevel5Part2Overlay(byte *dst, int pitch, int width, int height, int32 curFrame) {
+	if (_hudFontBank.numSprites <= 0 && _techFontBank.numSprites <= 0)
+		return;
+
+	const int viewportX = _player ? ra1Player()->_ra1ViewportOffsetX : 0;
+	const int viewportY = _player ? ra1Player()->_ra1ViewportOffsetY : 0;
+	int16 overlayX = 0x88;
+	int16 overlayY = 0x8C;
+	projectGameplayPoint(overlayX, overlayY);
+	overlayX = (int16)(0x88 - ((overlayX - 0x88) >> 2));
+	overlayY = (int16)(0x8C - ((overlayY - 0x8C) >> 2));
+
+	if (curFrame < 0x32) {
+		const char instructionText[] = "<<SHOOT TARGETS FOR BONUS";
+		const int revealChars = CLIP<int>((int)curFrame, 0, (int)sizeof(instructionText) - 1);
+		char revealedText[sizeof(instructionText)];
+		Common::strlcpy(revealedText, instructionText, revealChars + 1);
+		drawFontBankString(dst, pitch, width, height,
+			viewportX + overlayX - 0x27, viewportY + overlayY, revealedText);
+	} else {
+		char hitsStr[16];
+		Common::sprintf_s(hitsStr, "<<HITS: %d", (int)_killCount);
+		drawFontBankString(dst, pitch, width, height,
+			viewportX + overlayX, viewportY + overlayY, hitsStr);
+	}
+}
+
 void InsaneRebel1::resetEnemyShotSlots() {
 	memset(_enemyShotSlots, 0, sizeof(_enemyShotSlots));
 }
diff --git a/engines/scumm/smush/rebel/smush_player_ra1.cpp b/engines/scumm/smush/rebel/smush_player_ra1.cpp
index 880eb987be9..19add73b0d5 100644
--- a/engines/scumm/smush/rebel/smush_player_ra1.cpp
+++ b/engines/scumm/smush/rebel/smush_player_ra1.cpp
@@ -241,13 +241,16 @@ bool SmushPlayerRebel1::handleGameFetch(int32 subSize, Common::SeekableReadStrea
 			if (rebel1->isInteractiveVideoActive()) {
 				const uint16 gameOp = rebel1->getActiveGameOpcode();
 				const bool fullWidthStoredPatch = (_storedFobjWidth == _vm->_screenWidth);
-				const bool level8WalkerPatch = (gameOp == 0x0B && rebel1->getCurrentLevel() == 7);
+				const bool projectedCockpitPatch = (gameOp == 0x0B &&
+					((rebel1->getCurrentLevel() == 4 && rebel1->getLevelGameplayPhase() == 2) ||
+						rebel1->getCurrentLevel() == 7));
 				// Keep the direct viewport placement used by the existing 0x0B
-				// compatibility path, except for Level 8's walker cockpit patch.
+				// compatibility path, except for the Level 5 part 2 and Level 8
+				// cockpit patches.
 				// DOS FTCH goes through FUN_28D0A, which sets DispatchFobjCodec
-				// flag 0x800; Level 8 depends on that quarter-projection so the
-				// cockpit and RunLevel8Flow indicators move together.
-				if (fullWidthStoredPatch || (gameOp == 0x0B && !level8WalkerPatch) ||
+				// flag 0x800; these cockpit patches depend on that quarter-projection
+				// so the cockpit and flow-script indicators move together.
+				if (fullWidthStoredPatch || (gameOp == 0x0B && !projectedCockpitPatch) ||
 						gameOp == 0x19 || gameOp == 0x1A) {
 					left += _ra1ViewportOffsetX;
 					top += _ra1ViewportOffsetY;



From noreply at scummvm.org  Thu Jun  4 17:01:09 2026
From: noreply at scummvm.org (neuromancer)
Date: Thu,  4 Jun 2026 17:01:09 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 12c0e7e2cd6348b67c2faafb0dc3edce1f5dc83c
Message-ID: <20260604170109.D2AD0700573@ron.scummvm.net>

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:
f0bd302f43 SCUMM: RA1: added missing overlay in level 1 (part 2)
1138603703 SCUMM: RA1: fixed ordering of rendering for indicators, shoots, overlays and cockpit
12c0e7e2cd SCUMM: RA1: correctly render the opening line in the intro


Commit: f0bd302f430d8a741f44258e7de27dc1506e0aa3
    https://github.com/scummvm/scummvm/commit/f0bd302f430d8a741f44258e7de27dc1506e0aa3
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-04T17:22:54+02:00

Commit Message:
SCUMM: RA1: added missing overlay in level 1 (part 2)

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


diff --git a/engines/scumm/insane/rebel1/rebel.h b/engines/scumm/insane/rebel1/rebel.h
index 26dc27c6014..5ed007bb059 100644
--- a/engines/scumm/insane/rebel1/rebel.h
+++ b/engines/scumm/insane/rebel1/rebel.h
@@ -247,7 +247,8 @@ private:
 	void handleLevel14Play2BSplice(int32 curFrame, int32 maxFrame);
 	void renderLevel7RouteOverlays(byte *dst, int pitch, int width, int height);
 	void renderLevel5Part2Overlay(byte *dst, int pitch, int width, int height, int32 curFrame);
-	void renderLevel11HitsOverlay(byte *dst, int pitch, int width, int height);
+	void renderLevelHitsOverlay(byte *dst, int pitch, int width, int height, int y,
+		bool screenSpace);
 	void resetEnemyShotSlots();
 	void renderLevel13EnemyShots(byte *dst, int pitch, int width, int height);
 	void getTurretShipCenter(int16 &x, int16 &y) const;
diff --git a/engines/scumm/insane/rebel1/render.cpp b/engines/scumm/insane/rebel1/render.cpp
index 1a17b10eb9f..e52794dde7d 100644
--- a/engines/scumm/insane/rebel1/render.cpp
+++ b/engines/scumm/insane/rebel1/render.cpp
@@ -800,8 +800,11 @@ void InsaneRebel1::procPostRendering(byte *renderBitmap, int32 codecparam, int32
 	if (_currentLevel == 4 && _levelGameplayPhase == 2)
 		renderLevel5Part2Overlay(renderBitmap, pitch, width, height, curFrame);
 
+	if (_currentLevel == 0 && _flyControlMode == 2)
+		renderLevelHitsOverlay(renderBitmap, pitch, width, height, 0x04, true);
+
 	if (_currentLevel == 10)
-		renderLevel11HitsOverlay(renderBitmap, pitch, width, height);
+		renderLevelHitsOverlay(renderBitmap, pitch, width, height, 0x16, false);
 
 	// Level 8 (Imperial Walkers) ??? walker-specific state update + UI overlay.
 	// In the original, RunLevel8Flow runs the walker logic inline in the per-frame
@@ -1017,16 +1020,23 @@ void InsaneRebel1::renderGostSlots(byte *dst, int pitch, int width, int height)
 	}
 }
 
-// renderLevel11HitsOverlay ??? RunLevel11Flow (0x19F9F) calls FormatAndDrawText
-// each L11PLAY frame with "<<HITS %02d" at (0x119, 0x16). This helper is a
-// ScummVM-side extraction; the original keeps the draw call inline in the loop.
-void InsaneRebel1::renderLevel11HitsOverlay(byte *dst, int pitch, int width, int height) {
+// renderLevelHitsOverlay ??? RunLevel1Flow (0x16421-0x16438) and RunLevel11Flow
+// (0x1A07A-0x1A090) call FormatAndDrawText with "<<HITS %02d" at x=0x119.
+void InsaneRebel1::renderLevelHitsOverlay(byte *dst, int pitch, int width, int height, int y,
+		bool screenSpace) {
 	if (_hudFontBank.numSprites <= 0 && _techFontBank.numSprites <= 0)
 		return;
 
+	int drawX = 0x119;
+	int drawY = y;
+	if (screenSpace && _player) {
+		drawX += ra1Player()->_ra1ViewportOffsetX;
+		drawY += ra1Player()->_ra1ViewportOffsetY;
+	}
+
 	char hitsStr[16];
 	Common::sprintf_s(hitsStr, "<<HITS %02d", (int)_killCount);
-	drawFontBankString(dst, pitch, width, height, 0x119, 0x16, hitsStr);
+	drawFontBankString(dst, pitch, width, height, drawX, drawY, hitsStr);
 }
 
 // renderLevel5Part2Overlay ??? RunLevel5Flow (0x176D0-0x1777E) draws the


Commit: 113860370310e60708cdf42a127f50a851c9b9bd
    https://github.com/scummvm/scummvm/commit/113860370310e60708cdf42a127f50a851c9b9bd
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-04T17:59:33+02:00

Commit Message:
SCUMM: RA1: fixed ordering of rendering for indicators, shoots, overlays and cockpit

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/smush/rebel/smush_player_ra1.cpp


diff --git a/engines/scumm/insane/rebel1/iact.cpp b/engines/scumm/insane/rebel1/iact.cpp
index 48f1ff485ee..71106907a37 100644
--- a/engines/scumm/insane/rebel1/iact.cpp
+++ b/engines/scumm/insane/rebel1/iact.cpp
@@ -2433,7 +2433,8 @@ void InsaneRebel1::handleGameCounterOpcode(uint32 opcode, int32 subSize, Common:
 
 // handleGameChunk ??? FUN_1BE1B (0x1BE1B). Central GAME opcode dispatcher.
 // Reads 7x32-bit BE integers from GAME chunk, routes to per-opcode handlers.
-void InsaneRebel1::handleGameChunk(int32 subSize, Common::SeekableReadStream &b) {
+void InsaneRebel1::handleGameChunk(int32 subSize, Common::SeekableReadStream &b,
+		byte *renderBitmap, int width, int height) {
 	if (subSize < 8)
 		return;
 
@@ -2484,6 +2485,7 @@ void InsaneRebel1::handleGameChunk(int32 subSize, Common::SeekableReadStream &b)
 
 	case 0x0B:
 		handleGameOpcode0BFirstPerson(subSize, b, param1);
+		renderGameOp0BOverlayDuringChunk(renderBitmap, width, width, height);
 		break;
 
 	case 0x5A:
diff --git a/engines/scumm/insane/rebel1/rebel.cpp b/engines/scumm/insane/rebel1/rebel.cpp
index 4fd79f1c7eb..eb886e24bae 100644
--- a/engines/scumm/insane/rebel1/rebel.cpp
+++ b/engines/scumm/insane/rebel1/rebel.cpp
@@ -295,6 +295,7 @@ InsaneRebel1::InsaneRebel1(ScummEngine_v7 *scumm) : Insane(), _vm(scumm) {
 	_frameHasGameChunk = false;
 	_frameDispatchFlags = 0;
 	_gameOp0BPhysicsUpdatedThisFrame = false;
+	_gameOp0BOverlayRenderedThisFrame = false;
 
 	_health = kMaxHealth;
 	_lives = 3;
diff --git a/engines/scumm/insane/rebel1/rebel.h b/engines/scumm/insane/rebel1/rebel.h
index 5ed007bb059..1db04a43a5b 100644
--- a/engines/scumm/insane/rebel1/rebel.h
+++ b/engines/scumm/insane/rebel1/rebel.h
@@ -103,7 +103,8 @@ public:
 		int16 par1, int16 par2, int16 par3, int16 par4) override;
 	void procSKIP(int32 subSize, Common::SeekableReadStream &b) override;
 
-	void handleGameChunk(int32 subSize, Common::SeekableReadStream &b);
+	void handleGameChunk(int32 subSize, Common::SeekableReadStream &b,
+		byte *renderBitmap = nullptr, int width = 0, int height = 0);
 	bool isInteractiveVideoActive() const { return _interactiveVideoActive; }
 	// True on touchscreen devices (e.g. Android). RA1 skips DOS-style cursor
 	// warping/locking there; direct touch uses absolute aiming, while on-screen
@@ -243,7 +244,8 @@ private:
 							  int16 centerX, int16 centerY, int16 frame);
 	void renderLaserShots(byte *dst, int pitch, int width, int height);
 	void renderShotOverlayPipeline(byte *dst, int pitch, int width, int height,
-		bool drawTargetBoxes);
+		bool drawTargetBoxes, bool drawTargeting = true);
+	void renderGameOp0BOverlayDuringChunk(byte *dst, int pitch, int width, int height);
 	void handleLevel14Play2BSplice(int32 curFrame, int32 maxFrame);
 	void renderLevel7RouteOverlays(byte *dst, int pitch, int width, int height);
 	void renderLevel5Part2Overlay(byte *dst, int pitch, int width, int height, int32 curFrame);
@@ -447,6 +449,7 @@ private:
 	bool _frameHasGameChunk;
 	uint16 _frameDispatchFlags;
 	bool _gameOp0BPhysicsUpdatedThisFrame;
+	bool _gameOp0BOverlayRenderedThisFrame;
 
 	// Difficulty (0=easy, 1=normal, 2=hard) ??? matches original DAT_22BC
 	int _difficulty;
diff --git a/engines/scumm/insane/rebel1/render.cpp b/engines/scumm/insane/rebel1/render.cpp
index e52794dde7d..44b888c2cd1 100644
--- a/engines/scumm/insane/rebel1/render.cpp
+++ b/engines/scumm/insane/rebel1/render.cpp
@@ -524,6 +524,7 @@ void InsaneRebel1::procPreRendering(byte *renderBitmap) {
 	_frameDispatchFlags = 0;
 	_hudRenderFlag = 0;
 	_gameOp0BPhysicsUpdatedThisFrame = false;
+	_gameOp0BOverlayRenderedThisFrame = false;
 	_turretFrameShipCenterValid = false;
 	_frameObjectHitRevealPending = false;
 
@@ -630,6 +631,8 @@ void InsaneRebel1::procPostRendering(byte *renderBitmap, int32 codecparam, int32
 		(allowImplicitGameplayMode &&
 			(_activeGameOpcode == 0x07 || _activeGameOpcode == 0x09));
 	bool shotOverlayHandled = false;
+	bool drawShipAfterGameplayOverlays = false;
+	bool drawGameOp0BTargetingAfterFetch = false;
 	if (gameOp0BMode) {
 		// GAME 0x0B scrolling cockpit/surface handler ??? FUN_1CDA7.
 		if (!_gameOp0BPhysicsUpdatedThisFrame) {
@@ -647,6 +650,9 @@ void InsaneRebel1::procPostRendering(byte *renderBitmap, int32 codecparam, int32
 			_vm->_smushVideoShouldFinish = true;
 			return;
 		}
+
+		shotOverlayHandled = _gameOp0BOverlayRenderedThisFrame;
+		drawGameOp0BTargetingAfterFetch = _gameOp0BOverlayRenderedThisFrame;
 	} else if (onFootMode) {
 		// On-foot handler ??? opcodes 0x19/0x1A (Level 9 Stormtroopers)
 		if (_currentLevel == 8 && onFootAimMode && !onFootSequenceMode && _killCount > 0) {
@@ -721,9 +727,9 @@ void InsaneRebel1::procPostRendering(byte *renderBitmap, int32 codecparam, int32
 			return;
 		}
 
-		// Ship sprite is present in both flight (0x07 family) and 0x08 turret path.
-		if (flightMode || turretMode)
-			renderShip(renderBitmap, pitch, width, height);
+		// The paired GAME 0x09/0x0A handlers draw target boxes, shots and
+		// reticles before the 0x07/0x08 ship/cockpit pass.
+		drawShipAfterGameplayOverlays = (flightMode || turretMode);
 	}
 
 	// GAME handlers in the original update FUN_224FD during the same frame that
@@ -795,6 +801,13 @@ void InsaneRebel1::procPostRendering(byte *renderBitmap, int32 codecparam, int32
 		renderLevel7RouteOverlays(renderBitmap, pitch, width, height);
 
 	renderExplosions(renderBitmap, pitch, width, height);
+
+	if (drawShipAfterGameplayOverlays)
+		renderShip(renderBitmap, pitch, width, height);
+
+	if (drawGameOp0BTargetingAfterFetch)
+		renderTargeting(renderBitmap, pitch, width, height);
+
 	handleLevel14Play2BSplice(curFrame, maxFrame);
 
 	if (_currentLevel == 4 && _levelGameplayPhase == 2)
@@ -823,9 +836,11 @@ void InsaneRebel1::procPostRendering(byte *renderBitmap, int32 codecparam, int32
 
 // ScummVM helper that groups the common shot/lock overlay calls used by the
 // original GAME handlers. GAME 0x1A uses the same pipeline without the target
-// box draw; 0x09/0x0A/0x0B include DrawTargetIndicators first.
+// box draw; 0x09/0x0A/0x0B include DrawTargetIndicators first. GAME 0x0B
+// defers FUN_1CB22 targeting until post-render so FTCH can restore cockpit
+// pixels over shots/boxes while the lock/fire indicator remains visible.
 void InsaneRebel1::renderShotOverlayPipeline(byte *dst, int pitch, int width, int height,
-		bool drawTargetBoxes) {
+		bool drawTargetBoxes, bool drawTargeting) {
 	if (drawTargetBoxes) {
 		renderTargetBoxes(dst, pitch, width, height);
 	} else {
@@ -842,7 +857,16 @@ void InsaneRebel1::renderShotOverlayPipeline(byte *dst, int pitch, int width, in
 			_shotSlots[i].timer--;
 	}
 	renderGostSlots(dst, pitch, width, height);
-	renderTargeting(dst, pitch, width, height);
+	if (drawTargeting)
+		renderTargeting(dst, pitch, width, height);
+}
+
+void InsaneRebel1::renderGameOp0BOverlayDuringChunk(byte *dst, int pitch, int width, int height) {
+	if (_gameOp0BOverlayRenderedThisFrame || !dst || width <= 0 || height <= 0 || _health < 0)
+		return;
+
+	renderShotOverlayPipeline(dst, pitch, width, height, true, false);
+	_gameOp0BOverlayRenderedThisFrame = true;
 }
 
 // renderTargetBoxes ??? FUN_1C940 (0x1C940). Per-target green box overlays.
diff --git a/engines/scumm/smush/rebel/smush_player_ra1.cpp b/engines/scumm/smush/rebel/smush_player_ra1.cpp
index 19add73b0d5..d3343270d9a 100644
--- a/engines/scumm/smush/rebel/smush_player_ra1.cpp
+++ b/engines/scumm/smush/rebel/smush_player_ra1.cpp
@@ -740,7 +740,7 @@ void SmushPlayerRebel1::ra1HandleFrameAudioChunk(int32 subSize, Common::Seekable
 void SmushPlayerRebel1::ra1HandleGameFrameChunk(int32 subSize, Common::SeekableReadStream &b, bool fastForwarding) {
 	if (!fastForwarding && _insane) {
 		InsaneRebel1 *rebel1 = (InsaneRebel1 *)_insane;
-		rebel1->handleGameChunk(subSize, b);
+		rebel1->handleGameChunk(subSize, b, _dst, _width, _height);
 	}
 }
 


Commit: 12c0e7e2cd6348b67c2faafb0dc3edce1f5dc83c
    https://github.com/scummvm/scummvm/commit/12c0e7e2cd6348b67c2faafb0dc3edce1f5dc83c
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-04T19:00:49+02:00

Commit Message:
SCUMM: RA1: correctly render the opening line in the intro

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


diff --git a/engines/scumm/insane/rebel1/runlevels.cpp b/engines/scumm/insane/rebel1/runlevels.cpp
index 215fe75864f..8ef89b85847 100644
--- a/engines/scumm/insane/rebel1/runlevels.cpp
+++ b/engines/scumm/insane/rebel1/runlevels.cpp
@@ -144,7 +144,6 @@ static int calculateThresholdBonus(int kills, int perfectThreshold, int perKillT
 // ---------------------------------------------------------------------------
 
 // Play a passive cinematic (no game callback, skippable).
-// Reuses RA2's pattern: reset handler, set cinematic flags, play video.
 // startFrame > 0: fast-forward (decode without display/audio) to that frame.
 void InsaneRebel1::playCinematic(const char *filename, int32 startFrame) {
 	debugC(DEBUG_INSANE, "InsaneRebel1::playCinematic('%s', startFrame=%d)", filename, startFrame);
@@ -156,7 +155,7 @@ void InsaneRebel1::playCinematic(const char *filename, int32 startFrame) {
 	restoreScreenFlashPalette();
 	_interactiveVideoActive = false;
 	_vm->_smushVideoShouldFinish = false;
-	splayer->setCurVideoFlags(0x28);  // Cinematic mode + buffer preserve
+	splayer->setCurVideoFlags(0x420);
 	splayer->setFastForwardFromFrame(0);
 	splayer->setFastForwardToFrame(startFrame > 0 ? startFrame : 0);
 	splayer->play(filename, 15);
@@ -320,7 +319,6 @@ void InsaneRebel1::playIntroSequence() {
 	playCinematic("OPEN/O1LOGO.ANM");
 	if (shouldAbortGameFlow())
 		return;
-	clearVideoBuffer();
 
 	// Star Wars opening crawl (original: PUSH 0x5800, CALL FUN_1BA32)
 	playCinematic("OPEN/O1OPEN.ANM");
diff --git a/engines/scumm/smush/rebel/smush_player_ra1.cpp b/engines/scumm/smush/rebel/smush_player_ra1.cpp
index d3343270d9a..54d4f029003 100644
--- a/engines/scumm/smush/rebel/smush_player_ra1.cpp
+++ b/engines/scumm/smush/rebel/smush_player_ra1.cpp
@@ -41,6 +41,8 @@
 namespace Scumm {
 
 enum {
+	kRA1DecodeWidth = 384,
+	kRA1DecodeHeight = 242,
 	kRA1PresentationBorder = 4,
 	kRA1PresentationScreenWidth = 320,
 	kRA1PresentationScreenHeight = 200,
@@ -326,12 +328,17 @@ bool SmushPlayerRebel1::handleGameTextResource(uint32 subType, int32 subSize, Co
 	if (subType != MKTAG('T','E','X','T'))
 		return false;
 
-	// RA1 dialogue subtitles. Only render them when subtitles are enabled ??? this honors
-	// both ScummVM's global "subtitles" setting and the in-game DIALOGUE TEXT toggle
-	// (which writes the same ConfMan key). Still return true so the base handler doesn't
-	// try to parse RA1's custom TEXT format. Querying ConfMan per chunk also lets the
-	// setting take effect mid-video.
-	if (ConfMan.getBool("subtitles"))
+	bool forceText = false;
+	if (subSize > 8) {
+		const int64 textStart = b.pos();
+		b.seek(textStart + 8, SEEK_SET);
+		forceText = (b.readByte() == '.');
+		b.seek(textStart, SEEK_SET);
+	}
+
+	// Original FUN_1FDBC draws RA1 TEXT when the text starts with '.' regardless
+	// of the DIALOGUE TEXT option. O1OPEN uses that path for the opening lines.
+	if (forceText || ConfMan.getBool("subtitles"))
 		ra1HandleText(subSize, b);
 	return true;
 }
@@ -479,9 +486,9 @@ void SmushPlayerRebel1::ra1HandleFade(int32 subSize, Common::SeekableReadStream
 
 bool SmushPlayerRebel1::handleGameAnimHeader(byte *headerContent) {
 	(void)headerContent;
-	_width = 0;
-	_height = 0;
-	const int bufSize = 384 * 242;
+	_width = kRA1DecodeWidth;
+	_height = kRA1DecodeHeight;
+	const int bufSize = kRA1DecodeWidth * kRA1DecodeHeight;
 	if (_specialBuffer == nullptr || bufSize > _specialBufferSize) {
 		free(_specialBuffer);
 		_specialBuffer = (byte *)calloc(bufSize, 1);
@@ -499,7 +506,7 @@ void SmushPlayerRebel1::handleGameParseNextFrame() {
 
 bool SmushPlayerRebel1::handleGameFrameBufferSelect(int codec, int width, int height) {
 	// RA1 sub-fullscreen frames render into _specialBuffer at their (left, top) offset position.
-	int bufSize = 384 * 242;
+	int bufSize = kRA1DecodeWidth * kRA1DecodeHeight;
 	if (_specialBuffer == nullptr || bufSize > _specialBufferSize) {
 		free(_specialBuffer);
 		_specialBuffer = (byte *)calloc(bufSize, 1);
@@ -512,10 +519,8 @@ bool SmushPlayerRebel1::handleGameFrameBufferSelect(int codec, int width, int he
 bool SmushPlayerRebel1::handleGameDimensionOverride(int codec, int width, int height) {
 	if (_dst == _specialBuffer) {
 		// RA1: sub-fullscreen FOBJs should not override the 384x242 dimensions.
-		if (_width == 0 || _height == 0) {
-			_width = 384;
-			_height = 242;
-		}
+		_width = kRA1DecodeWidth;
+		_height = kRA1DecodeHeight;
 		return true;
 	}
 	return false;



From noreply at scummvm.org  Thu Jun  4 19:01:04 2026
From: noreply at scummvm.org (ScummVM-Translations)
Date: Thu,  4 Jun 2026 19:01:04 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 b115874f45ffa0ee3e4c970b453e45ce814d5aa8
Message-ID: <20260604190104.6580F700573@ron.scummvm.net>

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

Summary:
b115874f45 I18N: Update translation (Italian)


Commit: b115874f45ffa0ee3e4c970b453e45ce814d5aa8
    https://github.com/scummvm/scummvm/commit/b115874f45ffa0ee3e4c970b453e45ce814d5aa8
Author: Walter Agazzi (walter.agazzi at protonmail.com)
Date: 2026-06-04T19:00:57Z

Commit Message:
I18N: Update translation (Italian)

Currently translated at 100.0% (3204 of 3204 strings)

Changed paths:
    po/it_IT.po


diff --git a/po/it_IT.po b/po/it_IT.po
index 89450d90eab..e71e95d2f95 100644
--- a/po/it_IT.po
+++ b/po/it_IT.po
@@ -8,7 +8,7 @@ msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
 "POT-Creation-Date: 2026-06-02 16:30+0000\n"
-"PO-Revision-Date: 2026-05-25 15:48+0000\n"
+"PO-Revision-Date: 2026-06-04 19:00+0000\n"
 "Last-Translator: Walter Agazzi <walter.agazzi at protonmail.com>\n"
 "Language-Team: Italian <https://translations.scummvm.org/projects/scummvm/"
 "scummvm/it/>\n"
@@ -13650,21 +13650,17 @@ msgid "Enable ambience sounds."
 msgstr "Abilita gli effetti sonori ambientali."
 
 #: engines/scumm/metaengine.cpp:882
-#, fuzzy
-#| msgctxt "Options menu"
-#| msgid "High resolution on"
 msgid "High resolution mode"
-msgstr "Alta risoluzione attivata"
+msgstr "Modalit?? ad alta risoluzione"
 
 #: engines/scumm/metaengine.cpp:883
 msgid "Run the game in 640x400 high resolution mode instead of 320x200"
-msgstr ""
+msgstr "Esegue il gioco in alta risoluzione (640x400), anzich?? 320x200"
 
 #: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:901
-#, fuzzy
-#| msgid "All levels will be available to play."
 msgid "All levels will be available without requiring passwords"
-msgstr "Permette l'accesso a qualsiasi livello del gioco."
+msgstr ""
+"Permette l'accesso a qualsiasi livello del gioco senza richiedere password"
 
 #: engines/scumm/metaengine.cpp:1049
 msgid "Down left"
@@ -13693,87 +13689,67 @@ msgstr "Cheat: Vinci combattimento in moto"
 
 #: engines/scumm/metaengine.cpp:1130
 msgid "Rebel Assault controls"
-msgstr ""
+msgstr "Controlli di Rebel Assault"
 
 #: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1209
 msgid "Aim up / menu up"
-msgstr ""
+msgstr "Mira verso l'alto / men?? su"
 
 #: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1214
 msgid "Aim down / menu down"
-msgstr ""
+msgstr "Mira verso il basso / men?? gi??"
 
 #: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1219
 msgid "Aim left / menu left"
-msgstr ""
+msgstr "Mira a sinistra / men?? sinistra"
 
 #: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1224
 msgid "Aim right / menu right"
-msgstr ""
+msgstr "Mira a destra / men?? destra"
 
 #: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1229
-#, fuzzy
-#| msgid "Pick up"
 msgid "Stick up"
-msgstr "Raccogli"
+msgstr "Joystick su"
 
 #: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
-#, fuzzy
-#| msgid "Scroll down"
 msgid "Stick down"
-msgstr "Scorri verso il basso"
+msgstr "Joystick gi??"
 
 #: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1243
-#, fuzzy
-#| msgid "Slide left"
 msgid "Stick left"
-msgstr "Scorri a sinistra"
+msgstr "Joystick sinistra"
 
 #: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1250
-#, fuzzy
-#| msgid "Slide right"
 msgid "Stick right"
-msgstr "Scorri a destra"
+msgstr "Joystick destra"
 
 #: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1257
-#, fuzzy
-#| msgid "Move / Select"
 msgid "Fire / select"
-msgstr "Spostamento / Selezione"
+msgstr "Spara / seleziona"
 
 #: engines/scumm/metaengine.cpp:1186
-#, fuzzy
-#| msgid "Move back"
 msgid "Menu back"
-msgstr "Arretra"
+msgstr "Indietro (men??)"
 
 #: engines/scumm/metaengine.cpp:1191
-#, fuzzy
-#| msgid "Skip main menu"
 msgid "Skip / menu back"
-msgstr "Salta men?? principale"
+msgstr "Salta / indietro (men??)"
 
 #: engines/scumm/metaengine.cpp:1207
 msgid "Rebel Assault II controls"
-msgstr ""
+msgstr "Controlli di Rebel Assault II"
 
 #: engines/scumm/metaengine.cpp:1262
-#, fuzzy
-#| msgid "Move back"
 msgid "Cover / back"
-msgstr "Arretra"
+msgstr "Copertura / indietro"
 
 #: engines/scumm/metaengine.cpp:1268
-#, fuzzy
-#| msgid "Skip walk"
 msgid "Skip / back"
-msgstr "Salta spostamento"
+msgstr "Salta / indietro"
 
 #: engines/scumm/metaengine.cpp:1275
-#, fuzzy
-#| msgid "Skip main menu"
 msgid "Skip / menu"
-msgstr "Salta men?? principale"
+msgstr "Salta / men??"
 
 #: engines/scumm/saveload.cpp:1990
 #, c-format



From noreply at scummvm.org  Thu Jun  4 20:27:56 2026
From: noreply at scummvm.org (sev-)
Date: Thu,  4 Jun 2026 20:27:56 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 579b31bd6b94ede2bf4e4f508e97882816e303ff
Message-ID: <20260604202756.705EB700573@ron.scummvm.net>

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

Summary:
5869634b2f DM: Correct type from int16 to uint16. PVS-Studio V1112
368e672ae7 DM: Correct triggerLocalEffect to check effCell. PVS-Studio V547
9366cf83ba DM: Add explicit casts to fix sign-compare warnings. PVS-Studio V1112
a1cac12ce6 DM: Fix champion rename state restore bug. PVS-Studio V519
c00e4c78b2 DM: Prevent array overrun when backspacing in renaming champion. PVS-Studio V557
a70e9eab03 DM: Change decodeText parameter type to int16. PVS-Studio V1016
81dab0d58a DM: Use correct size of _portrait in memset. PVS-Studio V1086
14f536d233 DM: Make variables static to prevent stack-use-after-scope. PVS-Studio V506
f489c0b4cf DM: Initialize members in constructor. PVS-Studio V730
c57c5ef12a DM: Fix possible array underrun (original bug). PVS-Studio V557
81bfab4ebf DM: Remove redundant footprint check in setSquareAspect. PVS-Studio V560
a303636f89 DM: Fix mouse button status update on button release. PVS-Studio V547
75cb68ec06 DM: Prevent potential uint16 underflow. PVS-Studio V658
2d47d540d8 DM: Add boundary check in mouseProcessCommands125To128_clickOnChampionIcon. PVS-Studio V1051
8935d35fe2 DM: Fix dangling pointer by declaring projectileCoordinates at function scope. PVS-Stduio V507
c3be4eb33e DM: Fix double assignment mistake. PVS-Studio V519
c2f2ad6b1c DM: Fix out-of-bounds pointer arithmetic. PVS-Studio V557
999255e9c3 DM: Refactor to avoid compound assignment in condition. PVS-Studio V1019
938f11c228 DM: Consolidate duplicate case branches. PVS-Studio V1037
1d481b111f DM: Prevent _aspect array underrun. PVS-Studio V557
800a7cb451 DM: Removed redundant condition check. PVS-Studio V560
aa9931f2dc DM: Correct the order of parameters to avoid confusion. PVS-Studio V764
ece35f516d DM: Disable copy constructor for LZWdecompressor. PVS-Studio V690
78406b76af DM: Use size_t for string length in DialogMan. PVS-Studio V1029
7fa36f1373 DM: Fix double assignment mistake. PVS-Studio V519
7a6c02a11e DM: Use int16 cast to suppress enum range warnings. PVS-Studio V1016
6e79884984 DM: Fix strict alignment warning in processTimeline. PVS-Studio V1032
579b31bd6b DM: Refactor self assignment. PVS-Studio V1048


Commit: 5869634b2f4caa4731f974efcd628da298af4485
    https://github.com/scummvm/scummvm/commit/5869634b2f4caa4731f974efcd628da298af4485
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-06-04T22:27:39+02:00

Commit Message:
DM: Correct type from int16 to uint16. PVS-Studio V1112

Changed paths:
    engines/dm/inventory.cpp


diff --git a/engines/dm/inventory.cpp b/engines/dm/inventory.cpp
index a1b3a6bd16d..39cea8cb2fc 100644
--- a/engines/dm/inventory.cpp
+++ b/engines/dm/inventory.cpp
@@ -997,7 +997,7 @@ void InventoryMan::clickOnMouth() {
 		case kDMPotionTypeVi: {
 			uint16 healWoundIterationCount = MAX(1, (((Potion *)junkData)->getPower() / 42));
 			curChampion->_currHealth += curChampion->_maxHealth / counter;
-			int16 wounds = curChampion->_wounds;
+			uint16 wounds = curChampion->_wounds;
 			if (wounds) { /* If the champion is wounded */
 				counter = 10;
 				do {


Commit: 368e672ae79582f6d011eb2695a4e661c5aa8b4a
    https://github.com/scummvm/scummvm/commit/368e672ae79582f6d011eb2695a4e661c5aa8b4a
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-06-04T22:27:39+02:00

Commit Message:
DM: Correct triggerLocalEffect to check effCell. PVS-Studio V547

It checked localEffect != kDMSensorEffectNone instead of the correct effCell != kDMCellAny, as in the original reversed code.

Changed paths:
    engines/dm/movesens.cpp


diff --git a/engines/dm/movesens.cpp b/engines/dm/movesens.cpp
index 6a2ddc3bc3c..349cf3a32f1 100644
--- a/engines/dm/movesens.cpp
+++ b/engines/dm/movesens.cpp
@@ -937,7 +937,7 @@ void MovesensMan::triggerEffect(Sensor *sensor, SensorEffect effect, int16 mapX,
 
 void MovesensMan::triggerLocalEffect(SensorEffect localEffect, int16 effX, int16 effY, int16 effCell) {
 	if (localEffect == kDMSensorEffectAddExperience) {
-		addSkillExperience(kDMSkillSteal, 300, localEffect != kDMSensorEffectNone);
+		addSkillExperience(kDMSkillSteal, 300, effCell != kDMCellAny);
 		return;
 	}
 	_sensorRotationEffect = localEffect;


Commit: 9366cf83baa0db104abfd893850a7915666459b7
    https://github.com/scummvm/scummvm/commit/9366cf83baa0db104abfd893850a7915666459b7
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-06-04T22:27:39+02:00

Commit Message:
DM: Add explicit casts to fix sign-compare warnings. PVS-Studio V1112

Changed paths:
    engines/dm/movesens.cpp


diff --git a/engines/dm/movesens.cpp b/engines/dm/movesens.cpp
index 349cf3a32f1..5333dcbc9f3 100644
--- a/engines/dm/movesens.cpp
+++ b/engines/dm/movesens.cpp
@@ -972,14 +972,14 @@ void MovesensMan::processRotationEffect() {
 		{
 			Thing firstSensorThing = dungeon.getSquareFirstThing(_sensorRotationEffMapX, _sensorRotationEffMapY);
 			while ((firstSensorThing.getType() != kDMThingTypeSensor)
-				|| ((_sensorRotationEffCell != kDMCellAny) && (firstSensorThing.getCell() != _sensorRotationEffCell))) {
+				|| ((_sensorRotationEffCell != kDMCellAny) && ((int16)firstSensorThing.getCell() != _sensorRotationEffCell))) {
 				firstSensorThing = dungeon.getNextThing(firstSensorThing);
 			}
 			Sensor *firstSensor = (Sensor *)dungeon.getThingData(firstSensorThing);
 			Thing lastSensorThing = firstSensor->getNextThing();
 			while ((lastSensorThing != _vm->_thingEndOfList)
 				&& ((lastSensorThing.getType() != kDMThingTypeSensor)
-				|| ((_sensorRotationEffCell != kDMCellAny) && (lastSensorThing.getCell() != _sensorRotationEffCell)))) {
+				|| ((_sensorRotationEffCell != kDMCellAny) && ((int16)lastSensorThing.getCell() != _sensorRotationEffCell)))) {
 				lastSensorThing = dungeon.getNextThing(lastSensorThing);
 			}
 			if (lastSensorThing == _vm->_thingEndOfList)
@@ -988,7 +988,7 @@ void MovesensMan::processRotationEffect() {
 			Sensor *lastSensor = (Sensor *)dungeon.getThingData(lastSensorThing);
 			lastSensorThing = dungeon.getNextThing(lastSensorThing);
 			while (((lastSensorThing != _vm->_thingEndOfList) && (lastSensorThing.getType() == kDMThingTypeSensor))) {
-				if ((_sensorRotationEffCell == kDMCellAny) || (lastSensorThing.getCell() == _sensorRotationEffCell))
+				if ((_sensorRotationEffCell == kDMCellAny) || ((int16)lastSensorThing.getCell() == _sensorRotationEffCell))
 					lastSensor = (Sensor *)dungeon.getThingData(lastSensorThing);
 				lastSensorThing = dungeon.getNextThing(lastSensorThing);
 			}
@@ -1017,7 +1017,7 @@ Thing MovesensMan::getObjectOfTypeInCell(int16 mapX, int16 mapY, int16 cell, int
 	DungeonMan &dungeon = *_vm->_dungeonMan;
 	Thing curThing = dungeon.getSquareFirstObject(mapX, mapY);
 	while (curThing != _vm->_thingEndOfList) {
-		if ((_vm->_objectMan->getObjectType(curThing) == objectType) && ((cell == kDMCellAny) || (curThing.getCell() == cell)))
+		if ((_vm->_objectMan->getObjectType(curThing) == objectType) && ((cell == kDMCellAny) || ((int16)curThing.getCell() == cell)))
 			return curThing;
 
 		curThing = dungeon.getNextThing(curThing);


Commit: a1cac12ce6b83d3cb45b89efced741e072cd926a
    https://github.com/scummvm/scummvm/commit/a1cac12ce6b83d3cb45b89efced741e072cd926a
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-06-04T22:27:39+02:00

Commit Message:
DM: Fix champion rename state restore bug. PVS-Studio V519

Actaul game bug as mentioned in the reversed code.

Changed paths:
    engines/dm/champion.cpp


diff --git a/engines/dm/champion.cpp b/engines/dm/champion.cpp
index 436a529dae2..5e8a9d431a6 100644
--- a/engines/dm/champion.cpp
+++ b/engines/dm/champion.cpp
@@ -2530,11 +2530,11 @@ void ChampionMan::renameChampion(Champion *champ) {
 						return;
 					}
 
+					Common::strcpy_s(champ->_name, championNameBackupString);
 					if (renamedChampionStringMode == kDMRenameChampionTitle)
 						renamedChampionString = champ->_title;
-
-					Common::strcpy_s(champ->_name, championNameBackupString);
-					renamedChampionString = champ->_name;
+					else
+						renamedChampionString = champ->_name;
 					curCharacterIndex = characterIndexBackup;
 				} else {
 					if ((mousePos.x >= 107) && (mousePos.x <= 175) && (mousePos.y >= 147) && (mousePos.y <= 155)) { /* Coordinates of 'BACKSPACE' button */


Commit: c00e4c78b2f934b5ea3254bd90661c4e747c4c40
    https://github.com/scummvm/scummvm/commit/c00e4c78b2f934b5ea3254bd90661c4e747c4c40
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-06-04T22:27:39+02:00

Commit Message:
DM: Prevent array overrun when backspacing in renaming champion. PVS-Studio V557

Actaul game bug as mentioned in the reversed code.

Changed paths:
    engines/dm/champion.cpp


diff --git a/engines/dm/champion.cpp b/engines/dm/champion.cpp
index 5e8a9d431a6..3fcc103dd6a 100644
--- a/engines/dm/champion.cpp
+++ b/engines/dm/champion.cpp
@@ -2613,7 +2613,8 @@ void ChampionMan::renameChampion(Champion *champ) {
 			}
 			if (curCharacterIndex == 0) {
 				renamedChampionString = champ->_name;
-				curCharacterIndex = strlen(renamedChampionString) - 1;
+				int16 nameLen = (int16)strlen(renamedChampionString);
+				curCharacterIndex = nameLen > 0 ? nameLen - 1 : 0;
 				renamedChampionStringMode = kDMRenameChampionName;
 				textPosX = 177 + (curCharacterIndex * 6);
 				textPosY = 91;


Commit: a70e9eab0300b02058009d2cd712235d551da012
    https://github.com/scummvm/scummvm/commit/a70e9eab0300b02058009d2cd712235d551da012
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-06-04T22:27:39+02:00

Commit Message:
DM: Change decodeText parameter type to int16. PVS-Studio V1016

Changed paths:
    engines/dm/champion.cpp
    engines/dm/dm.cpp
    engines/dm/dungeonman.cpp
    engines/dm/dungeonman.h
    engines/dm/inventory.cpp


diff --git a/engines/dm/champion.cpp b/engines/dm/champion.cpp
index 3fcc103dd6a..bddd8ce1dfd 100644
--- a/engines/dm/champion.cpp
+++ b/engines/dm/champion.cpp
@@ -1948,7 +1948,7 @@ void ChampionMan::addCandidateChampionToParty(uint16 championPortraitIndex) {
 	char L0807_ac_DecodedChampionText[77];
 	char *decodedStringPtr = L0807_ac_DecodedChampionText;
 	dungeon.decodeText(decodedStringPtr, sizeof(L0807_ac_DecodedChampionText),
-			curThing, (TextType)(kDMTextTypeScroll | kDMMaskDecodeEvenIfInvisible));
+			curThing, (int16)(kDMTextTypeScroll | kDMMaskDecodeEvenIfInvisible));
 
 	uint16 charIdx = 0;
 	char tmpChar;
diff --git a/engines/dm/dm.cpp b/engines/dm/dm.cpp
index ef0eef93003..2858317b967 100644
--- a/engines/dm/dm.cpp
+++ b/engines/dm/dm.cpp
@@ -1001,7 +1001,7 @@ void DMEngine::fuseSequence() {
 		for (int16 idx = 0; idx < maxCount; idx++) {
 			char decodedString[200];
 			_dungeonMan->decodeText(decodedString, sizeof(decodedString),
-					textStringThings[idx], (TextType)(kDMTextTypeMessage | kDMMaskDecodeEvenIfInvisible));
+					textStringThings[idx], (int16)(kDMTextTypeMessage | kDMMaskDecodeEvenIfInvisible));
 			if (decodedString[1] == textFirstChar) {
 				_textMan->clearAllRows();
 				decodedString[1] = '\n'; /* New line */
diff --git a/engines/dm/dungeonman.cpp b/engines/dm/dungeonman.cpp
index f6e6ea312a8..1ad005738d5 100644
--- a/engines/dm/dungeonman.cpp
+++ b/engines/dm/dungeonman.cpp
@@ -1047,7 +1047,7 @@ Thing DungeonMan::getNextThing(Thing thing) {
 	return Thing(getThingData(thing)[0]);
 }
 
-void DungeonMan::decodeText(char *destString, size_t maxSize, Thing thing, TextType type) {
+void DungeonMan::decodeText(char *destString, size_t maxSize, Thing thing, int16 type) {
 	static char messageAndScrollEscReplacementStrings[32][8] = { // @ G0255_aac_Graphic559_MessageAndScrollEscapeReplacementStrings
 		{'x',   0,   0,   0, 0, 0, 0, 0}, /* Atari ST Version 1.0 1987-12-08 1987-12-11 1.1 1.2EN 1.2GE: { '?',  0,  0,  0, 0, 0, 0, 0 }, */
 		{'y',   0,   0,   0, 0, 0, 0, 0}, /* Atari ST Version 1.0 1987-12-08 1987-12-11 1.1 1.2EN 1.2GE: { '!',  0,  0,  0, 0, 0, 0, 0 }, */
@@ -1131,7 +1131,7 @@ void DungeonMan::decodeText(char *destString, size_t maxSize, Thing thing, TextT
 
 	TextString textString(_thingData[kDMstringTypeText] + thing.getIndex() * _thingDataWordCount[kDMstringTypeText]);
 	if ((textString.isVisible()) || (type & kDMMaskDecodeEvenIfInvisible)) {
-		type = (TextType)(type & ~kDMMaskDecodeEvenIfInvisible);
+		type &= ~kDMMaskDecodeEvenIfInvisible;
 		char sepChar;
 		if (type == kDMTextTypeMessage) {
 			*destString++ = '\n';
diff --git a/engines/dm/dungeonman.h b/engines/dm/dungeonman.h
index 59df5773c2a..9c8666a60b4 100644
--- a/engines/dm/dungeonman.h
+++ b/engines/dm/dungeonman.h
@@ -641,7 +641,7 @@ public:
 		return Square(getRelSquare(dir, stepsForward, stepsRight, posX, posY)).getType();
 	} // @ F0153_DUNGEON_GetRelativeSquareType
 	void setSquareAspect(uint16 *aspectArray, Direction dir, int16 mapX, int16 mapY); // @ F0172_DUNGEON_SetSquareAspect
-	void decodeText(char *destString, size_t maxSize, Thing thing, TextType type); // F0168_DUNGEON_DecodeText
+	void decodeText(char *destString, size_t maxSize, Thing thing, int16 type); // F0168_DUNGEON_DecodeText
 	Thing getUnusedThing(uint16 thingType); // @ F0166_DUNGEON_GetUnusedThing
 
 	uint16 getObjectWeight(Thing thing); // @ F0140_DUNGEON_GetObjectWeight
diff --git a/engines/dm/inventory.cpp b/engines/dm/inventory.cpp
index 39cea8cb2fc..47fec4a496a 100644
--- a/engines/dm/inventory.cpp
+++ b/engines/dm/inventory.cpp
@@ -317,7 +317,7 @@ void InventoryMan::drawPanelScroll(Scroll *scroll) {
 
 	char stringFirstLine[300];
 	_vm->_dungeonMan->decodeText(stringFirstLine, sizeof(stringFirstLine),
-			Thing(scroll->getTextStringThingIndex()), (TextType)(kDMTextTypeScroll | kDMMaskDecodeEvenIfInvisible));
+			Thing(scroll->getTextStringThingIndex()), (int16)(kDMTextTypeScroll | kDMMaskDecodeEvenIfInvisible));
 	char *charRed = stringFirstLine;
 	while (*charRed && (*charRed != '\n'))
 		charRed++;


Commit: 81dab0d58a46680e5bfb47173748880aecbf7058
    https://github.com/scummvm/scummvm/commit/81dab0d58a46680e5bfb47173748880aecbf7058
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-06-04T22:27:39+02:00

Commit Message:
DM: Use correct size of _portrait in memset. PVS-Studio V1086

Changed paths:
    engines/dm/champion.cpp


diff --git a/engines/dm/champion.cpp b/engines/dm/champion.cpp
index bddd8ce1dfd..88afc8598d7 100644
--- a/engines/dm/champion.cpp
+++ b/engines/dm/champion.cpp
@@ -59,7 +59,7 @@ void Champion::resetToZero() {
 	_directionMaximumDamageReceived = _maximumDamageReceived = _poisonEventCount = _enableActionEventIndex = 0;
 	_hideDamageReceivedIndex = _currHealth = _maxHealth = _currStamina = _maxStamina = _currMana = _maxMana = 0;
 	_actionDefense = _food = _water = _load = _shieldDefense = 0;
-	memset(_portrait, 0, 464);
+	memset(_portrait, 0, sizeof(_portrait));
 }
 
 void Champion::setWoundsFlag(ChampionWound flag, bool value) {


Commit: 14f536d2332e050f40b6e98799a79146598e9907
    https://github.com/scummvm/scummvm/commit/14f536d2332e050f40b6e98799a79146598e9907
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-06-04T22:27:39+02:00

Commit Message:
DM: Make variables static to prevent stack-use-after-scope. PVS-Studio V506

Changed paths:
    engines/dm/dm.cpp


diff --git a/engines/dm/dm.cpp b/engines/dm/dm.cpp
index 2858317b967..f14cb2e6b78 100644
--- a/engines/dm/dm.cpp
+++ b/engines/dm/dm.cpp
@@ -757,9 +757,9 @@ void DMEngine::drawEntrance() {
 	_dungeonMan->_currMapHeight = 5;
 	_dungeonMan->_currMapData = microDungeonCurrentMapData;
 
-	Map map; // uninitialized, won't be used
+	static Map map; // uninitialized, won't be used
 	_dungeonMan->_currMap = &map;
-	Square microDungeonSquares[25];
+	static Square microDungeonSquares[25];
 	for (uint16 i = 0; i < 25; ++i)
 		microDungeonSquares[i] = Square(kDMElementTypeWall, 0);
 


Commit: f489c0b4cf36f7696d50aa20da993292fa4a3a85
    https://github.com/scummvm/scummvm/commit/f489c0b4cf36f7696d50aa20da993292fa4a3a85
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-06-04T22:27:39+02:00

Commit Message:
DM: Initialize members in constructor. PVS-Studio V730

Changed paths:
    engines/dm/champion.h
    engines/dm/dm.cpp


diff --git a/engines/dm/champion.h b/engines/dm/champion.h
index 254c9b6590d..18a798b572f 100644
--- a/engines/dm/champion.h
+++ b/engines/dm/champion.h
@@ -462,7 +462,7 @@ public:
 
 class Spell {
 public:
-	Spell() {}
+	Spell() : _symbols(0), _baseRequiredSkillLevel(0), _skillIndex(0), _attributes(0) {}
 	Spell(int32 symbols, byte baseSkillReq, byte skillIndex, uint16 attributes)
 	: _symbols(symbols), _baseRequiredSkillLevel(baseSkillReq), _skillIndex(skillIndex), _attributes(attributes) {}
 
diff --git a/engines/dm/dm.cpp b/engines/dm/dm.cpp
index f14cb2e6b78..12d3fabd50a 100644
--- a/engines/dm/dm.cpp
+++ b/engines/dm/dm.cpp
@@ -123,7 +123,8 @@ DMEngine::DMEngine(OSystem *syst, const DMADGameDescription *desc) :
 			_thingExplFireBall(0xFF80), _thingExplSlime(0xFF81), _thingExplLightningBolt(0xFF82),
 			_thingExplHarmNonMaterial(0xFF83), _thingExplOpenDoor(0xFF84), _thingExplPoisonBolt(0xFF86),
 			_thingExplPoisonCloud(0xFF87), _thingExplSmoke(0xFFA8), _thingExplFluxcage(0xFFB2),
-			_thingExplRebirthStep1(0xFFE4), _thingExplRebirthStep2(0xFFE5), _thingParty(0xFFFF)
+			_thingExplRebirthStep1(0xFFE4), _thingExplRebirthStep2(0xFFE5), _thingParty(0xFFFF),
+			_dirIntoStepCountEast(), _dirIntoStepCountNorth()
 	{
 	// register random source
 	_rnd = new Common::RandomSource("dm");


Commit: c57c5ef12ae3eddaaf4c4c3e2bce8c9bd787cf5c
    https://github.com/scummvm/scummvm/commit/c57c5ef12ae3eddaaf4c4c3e2bce8c9bd787cf5c
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-06-04T22:27:39+02:00

Commit Message:
DM: Fix possible array underrun (original bug). PVS-Studio V557

Changed paths:
    engines/dm/dungeonman.cpp


diff --git a/engines/dm/dungeonman.cpp b/engines/dm/dungeonman.cpp
index 1ad005738d5..b58e6234d17 100644
--- a/engines/dm/dungeonman.cpp
+++ b/engines/dm/dungeonman.cpp
@@ -1388,7 +1388,10 @@ int16 DungeonMan::getProjectileAspect(Thing thing) {
 			return -projAspOrd;
 	}
 
-	return _objectInfos[getObjectInfoIndex(thing)]._objectAspectIndex;
+	int16 infoIndex = getObjectInfoIndex(thing);
+	if (infoIndex < 0 || infoIndex >= 180)
+		return 0;
+	return _objectInfos[infoIndex]._objectAspectIndex;
 }
 
 int16 DungeonMan::getLocationAfterLevelChange(int16 mapIndex, int16 levelDelta, int16 *mapX, int16 *mapY) {


Commit: 81bfab4ebfb469f5386880716e9c6574bcd9ef22
    https://github.com/scummvm/scummvm/commit/81bfab4ebfb469f5386880716e9c6574bcd9ef22
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-06-04T22:27:39+02:00

Commit Message:
DM: Remove redundant footprint check in setSquareAspect. PVS-Studio V560

Changed paths:
    engines/dm/dungeonman.cpp


diff --git a/engines/dm/dungeonman.cpp b/engines/dm/dungeonman.cpp
index b58e6234d17..111f7480863 100644
--- a/engines/dm/dungeonman.cpp
+++ b/engines/dm/dungeonman.cpp
@@ -989,7 +989,7 @@ T0172010_ClosedFakeWall:
 		while ((curThing != _vm->_thingEndOfList) && (curThing.getType() <= kDMThingTypeSensor))
 			curThing = getNextThing(curThing);
 
-		if (AL0307_uc_FootprintsAllowed && (AL0307_uc_ScentOrdinal = championMan.getScentOrdinal(mapX, mapY)) && (--AL0307_uc_ScentOrdinal >= championMan._party._firstScentIndex) && (AL0307_uc_ScentOrdinal < championMan._party._lastScentIndex))
+		if ((AL0307_uc_ScentOrdinal = championMan.getScentOrdinal(mapX, mapY)) && (--AL0307_uc_ScentOrdinal >= championMan._party._firstScentIndex) && (AL0307_uc_ScentOrdinal < championMan._party._lastScentIndex))
 			setFlag(aspectArray[kDMSquareAspectFloorOrn], kDMMaskFootprints);
 		break;
 	default:


Commit: a303636f891b5f6c51fa20f5c51aec9ab5828342
    https://github.com/scummvm/scummvm/commit/a303636f891b5f6c51fa20f5c51aec9ab5828342
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-06-04T22:27:39+02:00

Commit Message:
DM: Fix mouse button status update on button release. PVS-Studio V547

Changed paths:
    engines/dm/eventman.cpp


diff --git a/engines/dm/eventman.cpp b/engines/dm/eventman.cpp
index 7515380d0df..b5f31737419 100644
--- a/engines/dm/eventman.cpp
+++ b/engines/dm/eventman.cpp
@@ -656,7 +656,7 @@ Common::EventType EventManager::processInput(Common::Event *grabKey, Common::Eve
 		}
 		case Common::EVENT_LBUTTONUP:
 		case Common::EVENT_RBUTTONUP: {
-			MouseButton button = (event.type == Common::EVENT_LBUTTONDOWN) ? kDMMouseButtonLeft : kDMMouseButtonRight;
+			MouseButton button = (event.type == Common::EVENT_LBUTTONUP) ? kDMMouseButtonLeft : kDMMouseButtonRight;
 			_mouseButtonStatus &= ~button;
 			resetPressingEyeOrMouth();
 			break;


Commit: 75cb68ec06983e4271c8d61115e57bfe41963e4e
    https://github.com/scummvm/scummvm/commit/75cb68ec06983e4271c8d61115e57bfe41963e4e
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-06-04T22:27:39+02:00

Commit Message:
DM: Prevent potential uint16 underflow. PVS-Studio V658

Changed paths:
    engines/dm/eventman.cpp


diff --git a/engines/dm/eventman.cpp b/engines/dm/eventman.cpp
index b5f31737419..6eb810ec35e 100644
--- a/engines/dm/eventman.cpp
+++ b/engines/dm/eventman.cpp
@@ -1611,7 +1611,7 @@ void EventManager::commandProcessType111To115_ClickInActionArea(int16 posX, int1
 			if (mouseCommand == kDMCommandClickInActionAreaPass) {
 				commandHighlightBoxEnable(285, 319, 77, 83);
 				_vm->_menuMan->didClickTriggerAction(-1);
-			} else if ((mouseCommand - kDMCommandClickInActionAreaPass) <= _vm->_menuMan->_actionCount) {
+			} else if (mouseCommand > kDMCommandClickInActionAreaPass && (mouseCommand - kDMCommandClickInActionAreaPass) <= _vm->_menuMan->_actionCount) {
 				if (mouseCommand == kDMCommandClickInActionAreaAction0)
 					commandHighlightBoxEnable(234, 318, 86, 96);
 				else if (mouseCommand == kDMCommandClickInActionAreaAction1)


Commit: 2d47d540d8281e24856c5ca5a5d03795f0bbce13
    https://github.com/scummvm/scummvm/commit/2d47d540d8281e24856c5ca5a5d03795f0bbce13
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-06-04T22:27:39+02:00

Commit Message:
DM: Add boundary check in mouseProcessCommands125To128_clickOnChampionIcon. PVS-Studio V1051

Changed paths:
    engines/dm/eventman.cpp


diff --git a/engines/dm/eventman.cpp b/engines/dm/eventman.cpp
index 6eb810ec35e..b5561a7d08d 100644
--- a/engines/dm/eventman.cpp
+++ b/engines/dm/eventman.cpp
@@ -1496,7 +1496,7 @@ void EventManager::mouseProcessCommands125To128_clickOnChampionIcon(uint16 champ
 		uint16 championIconIndex = _vm->ordinalToIndex(_useChampionIconOrdinalAsMousePointerBitmap);
 		_useChampionIconOrdinalAsMousePointerBitmap = _vm->indexToOrdinal(kDMChampionNone);
 		int16 championCellIndex = _vm->_championMan->getIndexInCell(_vm->normalizeModulo4(championIconIndex + _vm->_dungeonMan->_partyDir));
-		if (championIconIndex == champIconIndex) {
+		if (championCellIndex >= 0 && championIconIndex == champIconIndex) {
 			setFlag(_vm->_championMan->_champions[championCellIndex]._attributes, kDMAttributeIcon);
 			_vm->_championMan->drawChampionState((ChampionIndex)championCellIndex);
 		} else {


Commit: 8935d35fe2aa6e044c074d2014401dd79d3a842d
    https://github.com/scummvm/scummvm/commit/8935d35fe2aa6e044c074d2014401dd79d3a842d
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-06-04T22:27:39+02:00

Commit Message:
DM: Fix dangling pointer by declaring projectileCoordinates at function scope. PVS-Stduio V507

Matches with reversed source code

Changed paths:
    engines/dm/gfx.cpp


diff --git a/engines/dm/gfx.cpp b/engines/dm/gfx.cpp
index 13c9c31a5c5..38691e4b664 100644
--- a/engines/dm/gfx.cpp
+++ b/engines/dm/gfx.cpp
@@ -2955,6 +2955,7 @@ void DisplayMan::drawObjectsCreaturesProjectilesExplosions(Thing thingParam, Dir
 	ObjectAspect *objectAspect;
 	uint32 remainingViewCellOrdinalsToProcess;
 	byte *coordinateSet;
+	byte projectileCoordinates[2];
 	int16 derivedBitmapIndex = -1;
 	bool L0135_B_DrawAlcoveObjects;
 	int16 byteWidth;
@@ -3726,7 +3727,6 @@ T0115129_DrawProjectiles:
 					blitToBitmap(bitmapRedBanana, _bitmapViewport, boxByteGreen, AL_4_xPos, 0, getNormalizedByteWidth(byteWidth), k112_byteWidthViewport, kDMColorFlesh, heightRedEagle, k136_heightViewport);
 				} else { /* Positive value: projectile aspect is the index of a OBJECT_ASPECT */
 					useAlcoveObjectImage = false;
-					byte projectileCoordinates[2];
 					projectileCoordinates[0] = projectilePosX;
 					projectileCoordinates[1] = 47;
 					coordinateSet = projectileCoordinates;


Commit: c3be4eb33e1e0dd01a7af08ee27817446870efe4
    https://github.com/scummvm/scummvm/commit/c3be4eb33e1e0dd01a7af08ee27817446870efe4
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-06-04T22:27:39+02:00

Commit Message:
DM: Fix double assignment mistake. PVS-Studio V519

Changed paths:
    engines/dm/dungeonman.cpp
    engines/dm/gfx.cpp


diff --git a/engines/dm/dungeonman.cpp b/engines/dm/dungeonman.cpp
index 111f7480863..b6d50a144e1 100644
--- a/engines/dm/dungeonman.cpp
+++ b/engines/dm/dungeonman.cpp
@@ -415,7 +415,6 @@ DungeonMan::DungeonMan(DMEngine *dmEngine) : _vm(dmEngine) {
 	_dungeonColumCount = 0;
 	_dungeonMapsFirstColumnIndex = nullptr;
 
-	_dungeonColumCount = 0;
 	_dungeonColumnsCumulativeSquareThingCount = nullptr;
 	_squareFirstThings = nullptr;
 	_dungeonTextData = nullptr;
diff --git a/engines/dm/gfx.cpp b/engines/dm/gfx.cpp
index 38691e4b664..ce94179213f 100644
--- a/engines/dm/gfx.cpp
+++ b/engines/dm/gfx.cpp
@@ -71,7 +71,6 @@ DisplayMan::DisplayMan(DMEngine *dmEngine) : _vm(dmEngine) {
 	_bitmapCompressedByteCount = nullptr;
 	_bitmapDecompressedByteCount = nullptr;
 	_packedBitmaps = nullptr;
-	_bitmaps = nullptr;
 	_tmpBitmap = nullptr;
 	_bitmapFloor = nullptr;
 	_bitmapCeiling = nullptr;
@@ -118,8 +117,6 @@ DisplayMan::DisplayMan(DMEngine *dmEngine) : _vm(dmEngine) {
 	_inscriptionThing = _vm->_thingNone;
 	_useByteBoxCoordinates = false;
 
-	_bitmapCeiling = nullptr;
-	_bitmapFloor = nullptr;
 	_bitmapWallSetD3L2 = nullptr;
 	_bitmapWallSetD3R2 = nullptr;
 	_bitmapWallSetD3LCR = nullptr;


Commit: c2f2ad6b1ce9bdc08bd0a682b72f0b38de8858c7
    https://github.com/scummvm/scummvm/commit/c2f2ad6b1ce9bdc08bd0a682b72f0b38de8858c7
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-06-04T22:27:39+02:00

Commit Message:
DM: Fix out-of-bounds pointer arithmetic. PVS-Studio V557

Changed paths:
    engines/dm/gfx.cpp


diff --git a/engines/dm/gfx.cpp b/engines/dm/gfx.cpp
index ce94179213f..15156df6a0e 100644
--- a/engines/dm/gfx.cpp
+++ b/engines/dm/gfx.cpp
@@ -2817,7 +2817,8 @@ bool DisplayMan::isDrawnWallOrnAnAlcove(int16 wallOrnOrd, ViewWall viewWallIndex
 			else if (viewWallIndex == kDMViewWallD2RFront)
 				coordinateSetOffset = -6;
 		}
-		blitPosX = (ornCoordSet + coordinateSetOffset)[1] - (ornCoordSet + coordinateSetOffset)[0];
+		byte *coords = _wallOrnamentCoordSets[wallOrnamentCoordinateSetIndex][viewWallIndex + (coordinateSetOffset / 6)];
+		blitPosX = coords[1] - coords[0];
 		wallOrnamentIndex = kDMDerivedBitmapFirstWallOrnament + (wallOrnamentIndex << 2) + wallOrnDerivedBitmapIndexIncrement[viewWallIndex];
 		if (!isDerivedBitmapInCache(wallOrnamentIndex)) {
 			byte *blitBitmap = getNativeBitmapOrGraphic(ornNativeBitmapIndex);


Commit: 999255e9c3c07110febdf31330a59668d1ab11e8
    https://github.com/scummvm/scummvm/commit/999255e9c3c07110febdf31330a59668d1ab11e8
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-06-04T22:27:39+02:00

Commit Message:
DM: Refactor to avoid compound assignment in condition. PVS-Studio V1019

Changed paths:
    engines/dm/gfx.cpp


diff --git a/engines/dm/gfx.cpp b/engines/dm/gfx.cpp
index 15156df6a0e..ab6ed119264 100644
--- a/engines/dm/gfx.cpp
+++ b/engines/dm/gfx.cpp
@@ -1247,7 +1247,9 @@ void DisplayMan::drawFloorOrnament(uint16 floorOrnOrdinal, ViewFloor viewFloorIn
 		return;
 
 	bool drawFootprints = (getFlag(floorOrnOrdinal, kDMMaskFootprints) ? true : false);
-	if (!drawFootprints || clearFlag(floorOrnOrdinal, kDMMaskFootprints)) {
+	if (drawFootprints)
+		clearFlag(floorOrnOrdinal, kDMMaskFootprints);
+	if (!drawFootprints || floorOrnOrdinal) {
 		floorOrnOrdinal--;
 		uint16 floorOrnIndex = floorOrnOrdinal;
 		int16 nativeBitmapIndex = _currMapFloorOrnInfo[floorOrnIndex].nativeIndice


Commit: 938f11c228a548475eb7a4890a01821fa90aff7b
    https://github.com/scummvm/scummvm/commit/938f11c228a548475eb7a4890a01821fa90aff7b
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-06-04T22:27:39+02:00

Commit Message:
DM: Consolidate duplicate case branches. PVS-Studio V1037

Changed paths:
    engines/dm/gfx.cpp


diff --git a/engines/dm/gfx.cpp b/engines/dm/gfx.cpp
index ab6ed119264..b15fd5bbe1e 100644
--- a/engines/dm/gfx.cpp
+++ b/engines/dm/gfx.cpp
@@ -1944,6 +1944,8 @@ void DisplayMan::drawSquareD1L(Direction dir, int16 posX, int16 posY) {
 			drawFloorPitOrStairsBitmap(_stairsNativeBitmapIndexDownSideD1L, frameStairsDownSideD1L);
 		// fall through
 	case kDMElementTypeDoorSide:
+	case kDMElementTypeTeleporter:
+	case kDMElementTypeCorridor:
 		order = kDMCellOrderBackRightFrontRight;
 		/* BUG0_64 Floor ornaments are drawn over open pits. There is no check to prevent drawing floor ornaments over open pits */
 		drawFloorOrnament(squareAspect[kDMSquareAspectFloorOrn], kDMViewFloorD1L);
@@ -1960,13 +1962,6 @@ void DisplayMan::drawSquareD1L(Direction dir, int16 posX, int16 posY) {
 	case kDMElementTypePit:
 		drawFloorPitOrStairsBitmap(squareAspect[kDMSquareAspectPitInvisible] ? kDMGraphicIdxFloorPitInvisibleD1L : kDMGraphicIdxFloorPitD1L, frameFloorPitD1L);
 		// fall through
-	case kDMElementTypeTeleporter:
-	case kDMElementTypeCorridor:
-		order = kDMCellOrderBackRightFrontRight;
-		 /* BUG0_64 Floor ornaments are drawn over open pits. There is no check to prevent drawing floor ornaments over open pits */
-		drawFloorOrnament(squareAspect[kDMSquareAspectFloorOrn], kDMViewFloorD1L);
-		drawCeilingPit(kDMGraphicIdxCeilingPitD1L, &frameCeilingPitD1L, posX, posY, false);
-		break;
 	default:
 		skip = true;
 		break;
@@ -2029,6 +2024,8 @@ void DisplayMan::drawSquareD1R(Direction dir, int16 posX, int16 posY) {
 
 		// fall through
 	case kDMElementTypeDoorSide:
+	case kDMElementTypeTeleporter:
+	case kDMElementTypeCorridor:
 		order = kDMCellOrderBackLeftFrontLeft;
 		drawFloorOrnament(squareAspect[kDMSquareAspectFloorOrn], kDMViewFloorD1R); /* BUG0_64 Floor ornaments are drawn over open pits. There is no check to prevent drawing floor ornaments over open pits */
 		drawCeilingPit(kDMGraphicIdxCeilingPitD1L, &frameCeilingPitD1R, posX, posY, true);
@@ -2045,12 +2042,6 @@ void DisplayMan::drawSquareD1R(Direction dir, int16 posX, int16 posY) {
 		drawFloorPitOrStairsBitmapFlippedHorizontally(squareAspect[kDMSquareAspectPitInvisible] ? kDMGraphicIdxFloorPitInvisibleD1L
 														   : kDMGraphicIdxFloorPitD1L, frameFloorPitD1R);
 		// fall through
-	case kDMElementTypeTeleporter:
-	case kDMElementTypeCorridor:
-		order = kDMCellOrderBackLeftFrontLeft;
-		drawFloorOrnament(squareAspect[kDMSquareAspectFloorOrn], kDMViewFloorD1R); /* BUG0_64 Floor ornaments are drawn over open pits. There is no check to prevent drawing floor ornaments over open pits */
-		drawCeilingPit(kDMGraphicIdxCeilingPitD1L, &frameCeilingPitD1R, posX, posY, true);
-		break;
 	default:
 		skip = true;
 		break;


Commit: 1d481b111ff232eb79f72453c09f2ea1ea447031
    https://github.com/scummvm/scummvm/commit/1d481b111ff232eb79f72453c09f2ea1ea447031
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-06-04T22:27:39+02:00

Commit Message:
DM: Prevent _aspect array underrun. PVS-Studio V557

Changed paths:
    engines/dm/group.cpp


diff --git a/engines/dm/group.cpp b/engines/dm/group.cpp
index 84bda38c9db..1f39a4a39d2 100644
--- a/engines/dm/group.cpp
+++ b/engines/dm/group.cpp
@@ -753,7 +753,8 @@ T0209005_AddEventAndReturn:
 		}
 		if (AL0447_i_Behavior == kDMBehaviorAttack) {
 			AL0446_i_CreatureAspectIndex = eventType - kDMEventTypeUpdateAspectCreature0; /* Value -1 for event 32, meaning aspect will be updated for all creatures in the group */
-			nextAspectUpdateTime = getCreatureAspectUpdateTime(activeGroup, AL0446_i_CreatureAspectIndex, getFlag(activeGroup->_aspect[AL0446_i_CreatureAspectIndex], kDMAspectMaskActiveGroupIsAttacking));
+			bool isAttacking = (AL0446_i_CreatureAspectIndex >= 0 && AL0446_i_CreatureAspectIndex < 4) ? getFlag(activeGroup->_aspect[AL0446_i_CreatureAspectIndex], kDMAspectMaskActiveGroupIsAttacking) : false;
+			nextAspectUpdateTime = getCreatureAspectUpdateTime(activeGroup, AL0446_i_CreatureAspectIndex, isAttacking);
 			goto T0209136;
 		}
 		if ((AL0450_i_DistanceXToParty > 3) || (AL0451_i_DistanceYToParty > 3)) {


Commit: 800a7cb451d2cec351ed287a572f8875b2ff31bd
    https://github.com/scummvm/scummvm/commit/800a7cb451d2cec351ed287a572f8875b2ff31bd
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-06-04T22:27:39+02:00

Commit Message:
DM: Removed redundant condition check. PVS-Studio V560

Changed paths:
    engines/dm/group.cpp


diff --git a/engines/dm/group.cpp b/engines/dm/group.cpp
index 1f39a4a39d2..744abdc2190 100644
--- a/engines/dm/group.cpp
+++ b/engines/dm/group.cpp
@@ -745,7 +745,8 @@ T0209005_AddEventAndReturn:
 			if ((AL0447_i_Behavior != kDMBehaviorAttack) && (AL0447_i_Behavior != kDMBehaviorFlee)) {
 				if (_vm->getDistance(dungeon._partyMapX, dungeon._partyMapY, eventMapX, eventMapY) <= 1)
 					goto T0209044_SetBehavior6_Attack;
-				if (((AL0447_i_Behavior == kDMBehaviorWander) || (AL0447_i_Behavior == kDMBehaviorUnknown3)) && (AL0447_i_Behavior != kDMBehaviorApproach)) /* BUG0_00 Useless code. Behavior cannot be 3 because this value is never used. Moreover, the second condition in the && is redundant (if the value is 0 or 3, it cannot be 7). The actual condition is: if (AL0447_i_Behavior == k0_behavior_WANDER) */
+				// if (((AL0447_i_Behavior == kDMBehaviorWander) || (AL0447_i_Behavior == kDMBehaviorUnknown3)) && (AL0447_i_Behavior != kDMBehaviorApproach)) /* BUG0_00 Useless code. Behavior cannot be 3 because this value is never used. Moreover, the second condition in the && is redundant (if the value is 0 or 3, it cannot be 7). The actual condition is: if (AL0447_i_Behavior == k0_behavior_WANDER) */
+				if ((AL0447_i_Behavior == kDMBehaviorWander) || (AL0447_i_Behavior == kDMBehaviorUnknown3))
 					goto T0209054_SetBehavior7_Approach;
 			}
 			activeGroup->_targetMapX = dungeon._partyMapX;


Commit: aa9931f2dc2a398b7d8598cbddba4df3b4f6aee1
    https://github.com/scummvm/scummvm/commit/aa9931f2dc2a398b7d8598cbddba4df3b4f6aee1
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-06-04T22:27:39+02:00

Commit Message:
DM: Correct the order of parameters to avoid confusion. PVS-Studio V764

Changed paths:
    engines/dm/group.cpp
    engines/dm/group.h


diff --git a/engines/dm/group.cpp b/engines/dm/group.cpp
index 744abdc2190..9536aad2338 100644
--- a/engines/dm/group.cpp
+++ b/engines/dm/group.cpp
@@ -1400,7 +1400,7 @@ void GroupMan::addGroupEvent(TimelineEvent *event, uint32 time) {
 	_vm->_timeline->addEventGetEventIndex(event);
 }
 
-int16 GroupMan::getSmelledPartyPrimaryDirOrdinal(CreatureInfo *creatureInfo, int16 mapY, int16 mapX) {
+int16 GroupMan::getSmelledPartyPrimaryDirOrdinal(CreatureInfo *creatureInfo, int16 mapX, int16 mapY) {
 	uint16 smellRange = creatureInfo->getSmellRange();
 	if (!smellRange)
 		return 0;
@@ -1408,14 +1408,14 @@ int16 GroupMan::getSmelledPartyPrimaryDirOrdinal(CreatureInfo *creatureInfo, int
 	ChampionMan &championMan = *_vm->_championMan;
 	DungeonMan &dungeon = *_vm->_dungeonMan;
 
-	if ((((smellRange + 1) >> 1) >= _currGroupDistanceToParty) && getDistanceBetweenUnblockedSquares(mapY, mapX, dungeon._partyMapX, dungeon._partyMapY, &GroupMan::isSmellPartyBlocked)) {
+	if ((((smellRange + 1) >> 1) >= _currGroupDistanceToParty) && getDistanceBetweenUnblockedSquares(mapX, mapY, dungeon._partyMapX, dungeon._partyMapY, &GroupMan::isSmellPartyBlocked)) {
 		_vm->_projexpl->_secondaryDirToOrFromParty = _currGroupSecondaryDirToParty;
 		return _vm->indexToOrdinal(_currGroupPrimaryDirToParty);
 	}
 
-	int16 scentOrdinal = championMan.getScentOrdinal(mapY, mapX);
+	int16 scentOrdinal = championMan.getScentOrdinal(mapX, mapY);
 	if (scentOrdinal && ((championMan._party._scentStrengths[_vm->ordinalToIndex(scentOrdinal)] + _vm->getRandomNumber(4)) > (30 - (smellRange << 1)))) { /* If there is a fresh enough party scent on the group square */
-		return _vm->indexToOrdinal(getDirsWhereDestIsVisibleFromSource(mapY, mapX, championMan._party._scents[scentOrdinal].getMapX(), championMan._party._scents[scentOrdinal].getMapY()));
+		return _vm->indexToOrdinal(getDirsWhereDestIsVisibleFromSource(mapX, mapY, championMan._party._scents[scentOrdinal].getMapX(), championMan._party._scents[scentOrdinal].getMapY()));
 	}
 	return 0;
 }
diff --git a/engines/dm/group.h b/engines/dm/group.h
index cc96c8d8c70..3af34b631b2 100644
--- a/engines/dm/group.h
+++ b/engines/dm/group.h
@@ -218,7 +218,7 @@ public:
 										   bool isAttacking); // @ F0179_GROUP_GetCreatureAspectUpdateTime
 	void setGroupDirection(ActiveGroup *activeGroup, int16 dir, int16 creatureIndex, bool twoHalfSquareSizedCreatures); // @ F0205_GROUP_SetDirection
 	void addGroupEvent(TimelineEvent *event, uint32 time); // @ F0208_GROUP_AddEvent
-	int16 getSmelledPartyPrimaryDirOrdinal(CreatureInfo *creatureInfo, int16 mapY, int16 mapX); // @ F0201_GROUP_GetSmelledPartyPrimaryDirectionOrdinal
+	int16 getSmelledPartyPrimaryDirOrdinal(CreatureInfo *creatureInfo, int16 mapX, int16 mapY); // @ F0201_GROUP_GetSmelledPartyPrimaryDirectionOrdinal
 	bool isSmellPartyBlocked(uint16 mapX, uint16 mapY); // @ F0198_GROUP_IsSmellPartyBlocked
 	int16 getFirstPossibleMovementDirOrdinal(CreatureInfo *info, int16 mapX, int16 mapY,
 												  bool allowMovementOverImaginaryPitsAndFakeWalls); // @ F0203_GROUP_GetFirstPossibleMovementDirectionOrdinal


Commit: ece35f516dfeb46c86e2d32966121f156598b291
    https://github.com/scummvm/scummvm/commit/ece35f516dfeb46c86e2d32966121f156598b291
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-06-04T22:27:39+02:00

Commit Message:
DM: Disable copy constructor for LZWdecompressor. PVS-Studio V690

Changed paths:
    engines/dm/lzw.h


diff --git a/engines/dm/lzw.h b/engines/dm/lzw.h
index 6d040d0d86f..3d2ab7d8014 100644
--- a/engines/dm/lzw.h
+++ b/engines/dm/lzw.h
@@ -56,6 +56,7 @@ class LZWdecompressor {
 
 	int16 getNextInputCode(Common::MemoryReadStream &stream, int32 *inputByteCount);
 	void outputCharacter(byte character, byte **out);
+	LZWdecompressor(const LZWdecompressor &other); // no implementation on purpose
 	void operator=(const LZWdecompressor&); // deleted
 public:
 	LZWdecompressor();


Commit: 78406b76af080090783957b18d13fd7e549a9331
    https://github.com/scummvm/scummvm/commit/78406b76af080090783957b18d13fd7e549a9331
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-06-04T22:27:39+02:00

Commit Message:
DM: Use size_t for string length in DialogMan. PVS-Studio V1029

Changed paths:
    engines/dm/dialog.cpp
    engines/dm/text.cpp


diff --git a/engines/dm/dialog.cpp b/engines/dm/dialog.cpp
index 839eaed1c02..db3d83ffd2f 100644
--- a/engines/dm/dialog.cpp
+++ b/engines/dm/dialog.cpp
@@ -148,12 +148,12 @@ void DialogMan::printCenteredChoice(byte *bitmap, const char *str, int16 posX, i
 }
 
 bool DialogMan::isMessageOnTwoLines(const char *str, char *part1, char *part2) {
-	uint16 strLength = strlen(str);
+	size_t strLength = strlen(str);
 	if (strLength <= 30)
 		return false;
 
 	Common::strcpy_s(part1, 70, str);
-	uint16 splitPosition = strLength >> 1;
+	size_t splitPosition = strLength >> 1;
 	while ((splitPosition < strLength) && (part1[splitPosition] != ' '))
 		splitPosition++;
 
diff --git a/engines/dm/text.cpp b/engines/dm/text.cpp
index 3563a8d8e86..4ac98ec21dd 100644
--- a/engines/dm/text.cpp
+++ b/engines/dm/text.cpp
@@ -56,7 +56,7 @@ void TextMan::printTextToBitmap(byte *destBitmap, uint16 destByteWidth, int16 de
 
 	uint16 destPixelWidth = destByteWidth * 2;
 
-	uint16 textLength = strlen(text);
+	size_t textLength = strlen(text);
 	uint16 nextX = destX;
 	uint16 nextY = destY;
 	byte *srcBitmap = _vm->_displayMan->getNativeBitmapOrGraphic(kDMGraphicIdxFont);
@@ -153,7 +153,7 @@ void TextMan::createNewRow() {
 }
 
 void TextMan::printString(Color color, const char* string) {
-	int16 stringLength = strlen(string);
+	size_t stringLength = strlen(string);
 	if (isTextScrolling(&_textScroller, false))
 		printToLogicalScreen(_messageAreaCursorColumn * 6, (_messageAreaCursorRow * 7 - 1) + 177, color, kDMColorBlack, string);
 	else {


Commit: 7fa36f1373d18ae49b9f843e6008c9dbf1ca9fa2
    https://github.com/scummvm/scummvm/commit/7fa36f1373d18ae49b9f843e6008c9dbf1ca9fa2
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-06-04T22:27:39+02:00

Commit Message:
DM: Fix double assignment mistake. PVS-Studio V519

Changed paths:
    engines/dm/group.cpp
    engines/dm/timeline.cpp


diff --git a/engines/dm/group.cpp b/engines/dm/group.cpp
index 9536aad2338..d767950ddad 100644
--- a/engines/dm/group.cpp
+++ b/engines/dm/group.cpp
@@ -1983,7 +1983,6 @@ void GroupMan::fluxCageAction(int16 mapX, int16 mapY) {
 	newEvent._Cu._slot = unusedThing.toUint16();
 	newEvent._Bu._location._mapX = mapX;
 	newEvent._Bu._location._mapY = mapY;
-	newEvent._Bu._location._mapY = mapY;
 	_vm->_timeline->addEventGetEventIndex(&newEvent);
 	int16 fluxcageCount;
 	if (isLordChaosOnSquare(mapX, mapY - 1)) {
diff --git a/engines/dm/timeline.cpp b/engines/dm/timeline.cpp
index 324de237264..bfd7df8baed 100644
--- a/engines/dm/timeline.cpp
+++ b/engines/dm/timeline.cpp
@@ -760,7 +760,6 @@ void Timeline::processEventSquareCorridor(TimelineEvent *event) {
 						newEvent._priority = 0;
 						newEvent._Bu._location._mapX = mapX;
 						newEvent._Bu._location._mapY = mapY;
-						newEvent._Bu._location._mapY = mapY;
 						addEventGetEventIndex(&newEvent);
 					}
 				}


Commit: 7a6c02a11e22badd3e8f22979e187f42e1ef64be
    https://github.com/scummvm/scummvm/commit/7a6c02a11e22badd3e8f22979e187f42e1ef64be
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-06-04T22:27:39+02:00

Commit Message:
DM: Use int16 cast to suppress enum range warnings. PVS-Studio V1016

Changed paths:
    engines/dm/gfx.cpp
    engines/dm/gfx.h
    engines/dm/timeline.cpp


diff --git a/engines/dm/gfx.cpp b/engines/dm/gfx.cpp
index b15fd5bbe1e..140ef36070e 100644
--- a/engines/dm/gfx.cpp
+++ b/engines/dm/gfx.cpp
@@ -1061,14 +1061,14 @@ void DisplayMan::fillBoxBitmap(byte *destBitmap, Box &box, Color color, int16 by
 }
 
 void DisplayMan::blitBoxFilledWithMaskedBitmap(byte *src, byte *dest, byte *mask, byte *tmp, Box& box,
-											   int16 lastUnitIndex, int16 firstUnitIndex, int16 destByteWidth, Color transparent,
+											   int16 lastUnitIndex, int16 firstUnitIndex, int16 destByteWidth, int16 transparent,
 											   int16 xPos, int16 yPos, int16 destHeight, int16 height2) {
 
 	// FIXME: does not produce the same effect as the original
 
 	byte nextUnitIndex = firstUnitIndex;
 	bool useMask = !(transparent & k0x0080_BlitDoNotUseMask);
-	transparent = (Color)(transparent & ~(k0x0080_BlitDoNotUseMask)); // clear flag 0x0080
+	transparent &= ~k0x0080_BlitDoNotUseMask; // clear flag 0x0080
 	for (byte next_y = box._rect.top; next_y <= box._rect.bottom; next_y++) { // '<=' for inclusive boundaries
 		for (byte next_x = box._rect.left; next_x <= box._rect.right; next_x++) { // '<=' for inclusive boundaries
 			byte *nextDestPixel = dest + next_y * destByteWidth * 2 + next_x;
@@ -2912,7 +2912,7 @@ void DisplayMan::drawField(FieldAspect *fieldAspect, Box& box) {
 	byte *bitmap = getNativeBitmapOrGraphic(kDMGraphicIdxFieldTeleporter + fieldAspect->_nativeBitmapRelativeIndex);
 	blitBoxFilledWithMaskedBitmap(bitmap, _bitmapViewport, bitmapMask, getDerivedBitmap(kDMDerivedBitmapViewport), box,
 									   _vm->getRandomNumber(2) + fieldAspect->_baseStartUnitIndex, _vm->getRandomNumber(32), k112_byteWidthViewport,
-									   (Color)fieldAspect->_transparentColor, fieldAspect->_xPos, 0, 136, fieldAspect->_bitplaneWordCount);
+									   fieldAspect->_transparentColor, fieldAspect->_xPos, 0, 136, fieldAspect->_bitplaneWordCount);
 	addDerivedBitmap(kDMDerivedBitmapViewport);
 	releaseBlock(kDMDerivedBitmapViewport | 0x8000);
 }
@@ -3796,7 +3796,7 @@ T0115171_BackFromT0115015_DrawProjectileAsObject:;
 					blitToBitmapShrinkWithPalChange(bitmapRedBanana, _tmpBitmap, 48, 32, 48, 32, _palChangeSmoke);
 					bitmapRedBanana = _tmpBitmap;
 				}
-				blitBoxFilledWithMaskedBitmap(bitmapRedBanana, _bitmapViewport, nullptr, getDerivedBitmap(kDMDerivedBitmapViewport), boxExplosionPatternD0C, _vm->getRandomNumber(4) + 87, _vm->getRandomNumber(64), k112_byteWidthViewport, Color(k0x0080_BlitDoNotUseMask | kDMColorFlesh), 0, 0, 136, 93);
+				blitBoxFilledWithMaskedBitmap(bitmapRedBanana, _bitmapViewport, nullptr, getDerivedBitmap(kDMDerivedBitmapViewport), boxExplosionPatternD0C, _vm->getRandomNumber(4) + 87, _vm->getRandomNumber(64), k112_byteWidthViewport, k0x0080_BlitDoNotUseMask | kDMColorFlesh, 0, 0, 136, 93);
 				addDerivedBitmap(kDMDerivedBitmapViewport);
 				warning("DISABLED CODE: f480_releaseBlock in drawObjectsCreaturesProjectilesExplosions");
 				//f480_releaseBlock(k0_DerivedBitmapViewport | 0x8000);
diff --git a/engines/dm/gfx.h b/engines/dm/gfx.h
index 4e734110113..34c84ae2f5f 100644
--- a/engines/dm/gfx.h
+++ b/engines/dm/gfx.h
@@ -742,7 +742,7 @@ public:
 						   uint16 destByteWidth, Color transparent, int16 srcHeight, int16 destHight); // @ F0132_VIDEO_Blit
 	 /* Expects inclusive boundaries in box */
 	void blitBoxFilledWithMaskedBitmap(byte *src, byte *dest, byte *mask, byte *tmp, Box &box, int16 lastUnitIndex,
-											int16 firstUnitIndex, int16 destByteWidth, Color transparent,
+											int16 firstUnitIndex, int16 destByteWidth, int16 transparent,
 											int16 xPos, int16 yPos, int16 destHeight, int16 height2); // @ F0133_VIDEO_BlitBoxFilledWithMaskedBitmap
 		 // this function takes pixel widths
 	void blitToBitmapShrinkWithPalChange(byte *srcBitmap, byte *destBitmap,
diff --git a/engines/dm/timeline.cpp b/engines/dm/timeline.cpp
index bfd7df8baed..0d966077f28 100644
--- a/engines/dm/timeline.cpp
+++ b/engines/dm/timeline.cpp
@@ -417,8 +417,10 @@ void Timeline::processEventDoorAnimation(TimelineEvent *event) {
 				if (_vm->_groupMan->getDamageAllCreaturesOutcome((Group *)_vm->_dungeonMan->getThingData(groupThing), mapX, mapY, 5, true) != kDMKillOutcomeAllCreaturesInGroup)
 					_vm->_groupMan->processEvents29to41(mapX, mapY, kDMEventTypeCreateReactionDangerOnSquare, 0);
 
-				int16 nextState = doorState - 1;
-				doorState = (doorState == kDMDoorStateOpen) ? kDMDoorStateOpen : (DoorState) nextState;
+				int16 nextState = (int16)doorState - 1;
+				if (nextState < 0)
+					nextState = 0;
+				doorState = (DoorState)nextState;
 				curSquare->setDoorState(doorState);
 				_vm->_sound->requestPlay(kDMSoundIndexWoodenThudAttackTrolinAntmanStoneGolem, mapX, mapY, kDMSoundModePlayIfPrioritized);
 				event->_mapTime++;
@@ -433,9 +435,13 @@ void Timeline::processEventDoorAnimation(TimelineEvent *event) {
 	if ((sensorEffect == kDMSensorEffectClear) && (doorState == kDMDoorStateClosed))
 		return;
 
-	int16 nextDoorEffect = doorState + 1;
-	int16 prevDoorEffect = doorState - 1;
-	doorState = (DoorState) ((sensorEffect == kDMSensorEffectSet) ? prevDoorEffect : nextDoorEffect);
+	int16 nextDoorStateVal = (sensorEffect == kDMSensorEffectSet) ? (int16)doorState - 1 : (int16)doorState + 1;
+	if (nextDoorStateVal < 0) {
+		nextDoorStateVal = 0;
+	} else if (nextDoorStateVal > 4) {
+		nextDoorStateVal = 4;
+	}
+	doorState = (DoorState)nextDoorStateVal;
 	curSquare->setDoorState(doorState);
 	_vm->_sound->requestPlay(kDMSoundIndexDoorRattle, mapX, mapY, kDMSoundModePlayIfPrioritized);
 


Commit: 6e79884984913087fddb709dab5c9fff4a6b53c9
    https://github.com/scummvm/scummvm/commit/6e79884984913087fddb709dab5c9fff4a6b53c9
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-06-04T22:27:39+02:00

Commit Message:
DM: Fix strict alignment warning in processTimeline. PVS-Studio V1032

Changed paths:
    engines/dm/timeline.cpp


diff --git a/engines/dm/timeline.cpp b/engines/dm/timeline.cpp
index 0d966077f28..43e8510ff9c 100644
--- a/engines/dm/timeline.cpp
+++ b/engines/dm/timeline.cpp
@@ -308,8 +308,8 @@ void Timeline::processTimeline() {
 			case kDMEventTypeRemoveFluxcage:
 				if (!_vm->_gameWon) {
 					_vm->_dungeonMan->unlinkThingFromList(Thing(newEvent._Cu._slot), Thing(0), newEvent._Bu._location._mapX, newEvent._Bu._location._mapY);
-					curEvent = (TimelineEvent *)_vm->_dungeonMan->getThingData(Thing(newEvent._Cu._slot));
-					((Explosion *)curEvent)->setNextThing(_vm->_thingNone);
+					Explosion *explosion = (Explosion *)_vm->_dungeonMan->getThingData(Thing(newEvent._Cu._slot));
+					explosion->setNextThing(_vm->_thingNone);
 				}
 				break;
 			case kDMEventTypeEnableChampionAction:


Commit: 579b31bd6b94ede2bf4e4f508e97882816e303ff
    https://github.com/scummvm/scummvm/commit/579b31bd6b94ede2bf4e4f508e97882816e303ff
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-06-04T22:27:39+02:00

Commit Message:
DM: Refactor self assignment. PVS-Studio V1048

Changed paths:
    engines/dm/gfx.cpp
    engines/dm/lzw.cpp
    engines/dm/menus.cpp
    engines/dm/movesens.cpp
    engines/dm/timeline.cpp


diff --git a/engines/dm/gfx.cpp b/engines/dm/gfx.cpp
index 140ef36070e..a74c0146eed 100644
--- a/engines/dm/gfx.cpp
+++ b/engines/dm/gfx.cpp
@@ -2830,7 +2830,6 @@ bool DisplayMan::isDrawnWallOrnAnAlcove(int16 wallOrnOrd, ViewWall viewWallIndex
 	}
 	byte byteFrame[6];
 	if (isInscription) {
-		byte *blitBitmap = ornCoordSet;
 		byte *inscrString2 = inscriptionString;
 		int16 unreadableTextLineCount = 0;
 		do {
@@ -2839,7 +2838,6 @@ bool DisplayMan::isDrawnWallOrnAnAlcove(int16 wallOrnOrd, ViewWall viewWallIndex
 			}
 			unreadableTextLineCount++;
 		} while (*inscrString2++ != 129); /* Hexadecimal: 0x81 (Megamax C does not support hexadecimal character constants) */
-		ornCoordSet = blitBitmap;
 		if (unreadableTextLineCount < 4) {
 			for (uint16 i = 0; i < 6; ++i)
 				byteFrame[i] = ornCoordSet[i];
@@ -3341,7 +3339,6 @@ T0115015_DrawProjectileAsObject:
 						AL_6_box->_rect.top = MIN(AL_6_box->_rect.top, boxByteGreen._rect.top);
 						AL_6_box->_rect.bottom = MAX(AL_6_box->_rect.bottom, boxByteGreen._rect.bottom);
 					}
-					bitmapRedBanana = bitmapGreenAnt;
 					dungeon._pileTopObject[AL_2_viewCell] = thingParam; /* The object is at the top of the pile */
 				}
 				blitToBitmap(bitmapRedBanana, _bitmapViewport, boxByteGreen, AL_4_xPos, 0, getNormalizedByteWidth(byteWidth), k112_byteWidthViewport, kDMColorFlesh, heightRedEagle, k136_heightViewport);
diff --git a/engines/dm/lzw.cpp b/engines/dm/lzw.cpp
index 6360478a5a1..6cab6d8b42e 100644
--- a/engines/dm/lzw.cpp
+++ b/engines/dm/lzw.cpp
@@ -131,7 +131,7 @@ int32 LZWdecompressor::decompress(Common::MemoryReadStream &inStream, int32 inpu
 	byte *reversedDecodedStringEnd = reversedDecodedStringStart = _tempBuffer;
 	byte *originalOut = out;
 	_repetitionEnabled = false;
-	_codeBitCount = 9;
+	// _codeBitCount = 9;
 	_dictFlushed = false;
 	_currentMaximumCode = (1 << (_codeBitCount = 9)) - 1;
 	for (int16 code = 255; code >= 0; code--) {
diff --git a/engines/dm/menus.cpp b/engines/dm/menus.cpp
index 7fca0f08134..795e5575c5c 100644
--- a/engines/dm/menus.cpp
+++ b/engines/dm/menus.cpp
@@ -1294,7 +1294,6 @@ bool MenuMan::isActionPerformed(uint16 champIndex, int16 actionIndex) {
 				curChampion->_currMana = 0;
 
 			setFlag(curChampion->_attributes, kDMAttributeStatistics);
-			actionPerformed = true;
 		}
 		}
 		break;
diff --git a/engines/dm/movesens.cpp b/engines/dm/movesens.cpp
index 5333dcbc9f3..d8f82df7e87 100644
--- a/engines/dm/movesens.cpp
+++ b/engines/dm/movesens.cpp
@@ -308,7 +308,6 @@ bool MovesensMan::getMoveResult(Thing thing, int16 mapX, int16 mapY, int16 destM
 			} else {
 				if ((destinationSquareType == (int)kDMElementTypePit) && !thingLevitates && getFlag(destinationSquareData, kDMSquareMaskPitOpen) && !getFlag(destinationSquareData, kDMSquareMaskPitImaginary)) {
 					if (drawDungeonViewWhileFalling && !_useRopeToClimbDownPit) {
-						drawDungeonViewWhileFalling = true;
 						if (traversedPitCount) {
 							dungeon.setCurrentMapAndPartyMap(mapIndexDestination);
 							display.loadCurrentMapGraphics();
diff --git a/engines/dm/timeline.cpp b/engines/dm/timeline.cpp
index 43e8510ff9c..16757915538 100644
--- a/engines/dm/timeline.cpp
+++ b/engines/dm/timeline.cpp
@@ -352,7 +352,7 @@ void Timeline::processTimeline() {
 				break;
 			case kDMEventTypePoisonChampion: {
 				uint16 championIndex = newEvent._priority;
-				_vm->_championMan->_champions[championIndex = newEvent._priority]._poisonEventCount--;
+				_vm->_championMan->_champions[championIndex]._poisonEventCount--;
 				_vm->_championMan->championPoison(championIndex, newEvent._Bu._attack);
 				}
 				break;



From noreply at scummvm.org  Thu Jun  4 20:29:03 2026
From: noreply at scummvm.org (neuromancer)
Date: Thu,  4 Jun 2026 20:29:03 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 b19adc3e6f14d2bc49d62e56951a0e76d4c2eb09
Message-ID: <20260604202903.93CA4700573@ron.scummvm.net>

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

Summary:
b19adc3e6f SCUMM: RA1: allow torpedo shooting in some levels


Commit: b19adc3e6f14d2bc49d62e56951a0e76d4c2eb09
    https://github.com/scummvm/scummvm/commit/b19adc3e6f14d2bc49d62e56951a0e76d4c2eb09
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-04T22:28:40+02:00

Commit Message:
SCUMM: RA1: allow torpedo shooting in some levels

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


diff --git a/engines/scumm/insane/rebel1/iact.cpp b/engines/scumm/insane/rebel1/iact.cpp
index 71106907a37..7065bdc8a8b 100644
--- a/engines/scumm/insane/rebel1/iact.cpp
+++ b/engines/scumm/insane/rebel1/iact.cpp
@@ -1869,10 +1869,10 @@ void InsaneRebel1::updateGameOp0BPhysics() {
 			_vm->_smushVideoShouldFinish = true;
 	}
 
-	// Level 4 Phase 2: enable torpedo mode at frame 0x3E and finish as
-	// soon as the torpedo registers a hit. The DOS loop exits on killCount.
+	// Level 4 Phase 2: enable torpedo mode at frontend/movie frame 0x3E and
+	// finish as soon as the torpedo registers a hit. The DOS loop exits on killCount.
 	if (_currentLevel == 3 && _levelGameplayPhase == 2) {
-		if (_frameCounter == 0x3E)
+		if (_currentSmushFrame == 0x3E)
 			_gameplayFlags75ff |= 2;
 		if (_killCount > 0)
 			_vm->_smushVideoShouldFinish = true;
@@ -1912,11 +1912,11 @@ void InsaneRebel1::updateGameOp0BPhysics() {
 			_vm->_smushVideoShouldFinish = true;
 	}
 
-	// Level 15 Phase 2: enable torpedo at frame 0x18A, expose the protected
-	// target IDs used by the original flow, and finish when
+	// Level 15 Phase 2: enable torpedo at frontend/movie frame 0x18A, expose
+	// the protected target IDs used by the original flow, and finish when
 	// g_gameplayPhaseFlags & 2 becomes set.
 	if (_currentLevel == 14 && _levelGameplayPhase == 2) {
-		if (_frameCounter == 0x18A) {
+		if (_currentSmushFrame == 0x18A) {
 			// Original writes the 16-bit flags word: g_hudDisableFlags |= 0x210.
 			// Low byte bit 0x10 suppresses normal hit feedback in the torpedo phase;
 			// high byte bit 0x02 switches targeting/shot rendering to torpedoes.
diff --git a/engines/scumm/insane/rebel1/rebel.cpp b/engines/scumm/insane/rebel1/rebel.cpp
index eb886e24bae..c61b0c8aa65 100644
--- a/engines/scumm/insane/rebel1/rebel.cpp
+++ b/engines/scumm/insane/rebel1/rebel.cpp
@@ -309,6 +309,7 @@ InsaneRebel1::InsaneRebel1(ScummEngine_v7 *scumm) : Insane(), _vm(scumm) {
 	_deathTimer = 0;
 	_screenFlash = 0;
 	_frameCounter = 0;
+	_currentSmushFrame = 0;
 	_screenShakeEnabled = false;
 	memset(_screenFlashBasePalette, 0, sizeof(_screenFlashBasePalette));
 	_screenFlashBasePaletteValid = false;
@@ -474,6 +475,10 @@ InsaneRebel1::InsaneRebel1(ScummEngine_v7 *scumm) : Insane(), _vm(scumm) {
 	_vm->_system->getEventManager()->getEventDispatcher()->registerObserver(this, 1, false);
 }
 
+void InsaneRebel1::setCurrentSmushFrame(int32 frame) {
+	_currentSmushFrame = frame;
+}
+
 InsaneRebel1::~InsaneRebel1() {
 	_vm->_system->getEventManager()->getEventDispatcher()->unregisterObserver(this);
 	terminateAudio();
diff --git a/engines/scumm/insane/rebel1/rebel.h b/engines/scumm/insane/rebel1/rebel.h
index 1db04a43a5b..781d916bc74 100644
--- a/engines/scumm/insane/rebel1/rebel.h
+++ b/engines/scumm/insane/rebel1/rebel.h
@@ -111,6 +111,7 @@ public:
 	// gamepad/joystick input continues through the joystick path.
 	bool isTouchscreenActive() const;
 	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; }
@@ -490,6 +491,7 @@ private:
 	int16 _deathTimer;           // 0x756A: death animation countdown (30 on death)
 	int16 _screenFlash;          // 0x7736: screen flash timer on hit
 	uint32 _frameCounter;        // Gameplay handler frame accumulator
+	int32 _currentSmushFrame;    // Current frontend/movie frame (original DAT_7740)
 	bool _screenShakeEnabled;    // 0x41AC: when true, SetCameraOffset adds ??2 random jitter
 	byte _screenFlashBasePalette[0x300];
 	bool _screenFlashBasePaletteValid;
diff --git a/engines/scumm/insane/rebel1/runlevels.cpp b/engines/scumm/insane/rebel1/runlevels.cpp
index 8ef89b85847..11499e20973 100644
--- a/engines/scumm/insane/rebel1/runlevels.cpp
+++ b/engines/scumm/insane/rebel1/runlevels.cpp
@@ -210,6 +210,7 @@ void InsaneRebel1::resetLevelDamageState() {
 
 void InsaneRebel1::resetLevelFrameState() {
 	_frameCounter = 0;
+	_currentSmushFrame = 0;
 	_gameCounter = 0;
 	_activeGameOpcode = 0;
 	_gameLatch5D = 0;
@@ -578,8 +579,8 @@ bool InsaneRebel1::runLevel4() {
 			return false;
 
 		if (_health >= 0 && shieldGeneratorsDestroyed) {
-			// Phase 2: torpedo run. The DOS loop enables torpedo mode at frame
-			// 0x3E and exits early as soon as killCount becomes nonzero.
+			// Phase 2: torpedo run. The DOS loop enables torpedo mode at
+			// frontend/movie frame 0x3E and exits early as soon as killCount becomes nonzero.
 			_activeGameOpcode = 0;
 			_gameLatch5D = 0;
 			_gameLatch5F = 0;
@@ -1448,7 +1449,7 @@ bool InsaneRebel1::runLevel15() {
 				return false;
 
 			// Phase 2: final approach and torpedo shot. The DOS flow enables
-			// torpedo mode at frame 0x18A and completes only after object-state
+			// torpedo mode at frontend/movie frame 0x18A and completes only after object-state
 			// bit 0x7602 & 2 is set by the exhaust-port hit.
 			_activeGameOpcode = 0;
 			_gameLatch5D = 0;
diff --git a/engines/scumm/smush/rebel/smush_player_ra1.cpp b/engines/scumm/smush/rebel/smush_player_ra1.cpp
index 54d4f029003..9b24d8248fa 100644
--- a/engines/scumm/smush/rebel/smush_player_ra1.cpp
+++ b/engines/scumm/smush/rebel/smush_player_ra1.cpp
@@ -904,6 +904,7 @@ void SmushPlayerRebel1::handleFrame(int32 frameSize, Common::SeekableReadStream
 	bool preserveFrameHistory = false;
 	if (_insane) {
 		InsaneRebel1 *rebel1 = static_cast<InsaneRebel1 *>(_insane);
+		rebel1->setCurrentSmushFrame(_frame);
 		bool interactive = rebel1->isInteractiveVideoActive();
 		const bool frameHasGameChunk = interactive && ra1FrameHasGameChunk(b, frameSize);
 		rebel1->setFrameHasGameChunk(frameHasGameChunk);



From noreply at scummvm.org  Thu Jun  4 21:55:39 2026
From: noreply at scummvm.org (bluegr)
Date: Thu,  4 Jun 2026 21:55:39 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 f6a0670535c883b082cff7e7fd5afac6b0c8bce8
Message-ID: <20260604215539.0ED6E700573@ron.scummvm.net>

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

Summary:
8f77ad0b07 NANCY: Add stubs for new Action Records in Nancy11
b77da6a8af NANCY: Fix SecondaryMovie/SecondaryVideo playing for Nancy11
c7aaf642b6 NANCY: Set the correct ID for no response for Nancy11
5a91983cb4 NANCY: Adapt SetPlayerClock puzzle for Nancy11
c8d1e4a19c NANCY: Add info for the SCTB (ScrollableTextBox) chunk in Nancy11
f6a0670535 NANCY: Disable the text box for Nancy11, for now


Commit: 8f77ad0b072241997025ffb94e013c97a2f5d0fa
    https://github.com/scummvm/scummvm/commit/8f77ad0b072241997025ffb94e013c97a2f5d0fa
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-06-05T00:55:21+03:00

Commit Message:
NANCY: Add stubs for new Action Records in Nancy11

Changed paths:
    engines/nancy/action/arfactory.cpp


diff --git a/engines/nancy/action/arfactory.cpp b/engines/nancy/action/arfactory.cpp
index ac65b5f58fa..d89f822b735 100644
--- a/engines/nancy/action/arfactory.cpp
+++ b/engines/nancy/action/arfactory.cpp
@@ -178,9 +178,21 @@ ActionRecord *ActionManager::createActionRecord(uint16 type, Common::SeekableRea
 	case 29:
 		// Nancy 10+
 		return new ControlUIItems();
+	case 30:	// Nancy11
+		warning("StopPlayerScrolling");	// TODO
+		return nullptr;
+	case 31:	// Nancy11
+		warning("StartPlayerScrolling");	// TODO
+		return nullptr;
 	case 32:
 		// Nancy 10+
 		return new UIPopupPrepScene();
+	case 45:	// Nancy11
+		warning("PlayRandomMovie");	// TODO
+		return nullptr;
+	case 46:	// Nancy11
+		warning("PlayRandomMovieControl");	// TODO
+		return nullptr;
 	case 40:
 		if (g_nancy->getGameType() <= kGameTypeNancy1)
 			return new LightningOn();	// Only used in TVD
@@ -248,6 +260,9 @@ ActionRecord *ActionManager::createActionRecord(uint16 type, Common::SeekableRea
 			return new TableIndexSetValueHS();
 	case 68:
 		return new TextScroll(false);
+	case 69:	// Nancy11
+		warning("TimerControl");	// TODO
+		return nullptr;
 	case 70:
 		return new TextScroll(true); // AutotextEntryList
 	case 71:
@@ -271,8 +286,12 @@ ActionRecord *ActionManager::createActionRecord(uint16 type, Common::SeekableRea
 		return new SetValueCombo();
 	case 79:
 		return new ValueTest();
-	//case 81: // Nancy 11+
-	//	return nullptr;	// TODO
+	case 81:	// Nancy11
+		//warning("AutotextTextBoxWrite");	// TODO
+		return nullptr;
+	case 96:	// Nancy11
+		warning("UnknownAR96");	// TODO
+		return nullptr;
 	case 97:
 		return new EventFlags(true);
 	case 98:
@@ -354,6 +373,9 @@ ActionRecord *ActionManager::createActionRecord(uint16 type, Common::SeekableRea
 		return new AddSearchLink();
 	case 140:
 		return new SetVolume();
+	case 147:	// Nancy11
+		warning("FadeSoundToSilence");	// TODO
+		return nullptr;
 	case 148:
 		// MakeScreenFile - seems to save a cropped image of the screen in a bitmap file?
 		// TODO: Used in Nancy 9, sand castle puzzle
@@ -379,6 +401,9 @@ ActionRecord *ActionManager::createActionRecord(uint16 type, Common::SeekableRea
 		return new StopSound();
 	case 155:
 		return new StopSound(); // StopAndUnloadSound, but we always unload
+	case 156:	// Nancy11
+		warning("Update3DSound");	// TODO
+		return nullptr;
 	case 157:
 		return new PlaySoundCC();
 	case 158:
@@ -479,10 +504,12 @@ ActionRecord *ActionManager::createActionRecord(uint16 type, Common::SeekableRea
 	case 244:
 		return new GridMapPuzzle();
 	// -- Nancy 11 and up --
-	case 245:
-		// return new TypingQuizPuzzle();
-	case 246:
-		// return new MatchPuzzle();
+	case 245:	// Nancy11
+		warning("TypingQuizPuzzle");	// TODO
+		return nullptr;
+	case 246:	// Nancy11
+		warning("MatchPuzzle246");	// TODO
+		return nullptr;
 	default:
 		warning("Unknown action record type %d", type);
 		return nullptr;


Commit: b77da6a8af27eee246f53004ec23c5c36c4db605
    https://github.com/scummvm/scummvm/commit/b77da6a8af27eee246f53004ec23c5c36c4db605
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-06-05T00:55:22+03:00

Commit Message:
NANCY: Fix SecondaryMovie/SecondaryVideo playing for Nancy11

Changed paths:
    engines/nancy/action/secondarymovie.cpp
    engines/nancy/commontypes.cpp


diff --git a/engines/nancy/action/secondarymovie.cpp b/engines/nancy/action/secondarymovie.cpp
index d6421d8ef40..499d7b07791 100644
--- a/engines/nancy/action/secondarymovie.cpp
+++ b/engines/nancy/action/secondarymovie.cpp
@@ -56,6 +56,8 @@ void PlaySecondaryMovie::readData(Common::SeekableReadStream &stream) {
 	ser.syncAsUint16LE(_videoType, kGameTypeNancy7);
 	ser.skip(2, kGameTypeVampire, kGameTypeNancy9); // videoPlaySource
 	ser.syncAsUint16LE(_videoFormat);
+	if (g_nancy->getGameType() >= kGameTypeNancy11)
+		_videoFormat = kLargeVideoFormat;
 	ser.skip(4, kGameTypeVampire, kGameTypeVampire); // paletteStart, paletteSize
 	ser.skip(2, kGameTypeVampire, kGameTypeNancy9);  // hasBitmapOverlaySurface
 	ser.skip(2, kGameTypeVampire, kGameTypeNancy9);  // VIDEO_STOP_RENDERING, VIDEO_CONTINUE_RENDERING
diff --git a/engines/nancy/commontypes.cpp b/engines/nancy/commontypes.cpp
index 2a2fd87e8eb..512502da263 100644
--- a/engines/nancy/commontypes.cpp
+++ b/engines/nancy/commontypes.cpp
@@ -126,7 +126,8 @@ void SecondaryVideoDescription::readData(Common::SeekableReadStream &stream) {
 	frameID = stream.readUint16LE();
 	readRect(stream, srcRect);
 	readRect(stream, destRect);
-	stream.skip(0x20);
+	if (g_nancy->getGameType() <= kGameTypeNancy10)
+		stream.skip(0x20);
 }
 
 void SoundEffectDescription::readData(Common::SeekableReadStream &stream) {


Commit: c7aaf642b60e74c3e22298068f8e2ff3531c20ff
    https://github.com/scummvm/scummvm/commit/c7aaf642b60e74c3e22298068f8e2ff3531c20ff
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-06-05T00:55:23+03:00

Commit Message:
NANCY: Set the correct ID for no response for Nancy11

Changed paths:
    engines/nancy/action/conversation.cpp


diff --git a/engines/nancy/action/conversation.cpp b/engines/nancy/action/conversation.cpp
index 6ece9bbf0ff..64ad001d71e 100644
--- a/engines/nancy/action/conversation.cpp
+++ b/engines/nancy/action/conversation.cpp
@@ -41,7 +41,7 @@ namespace Action {
 ConversationSound::ConversationSound() :
 		RenderActionRecord(8),
 		_noResponse(g_nancy->getGameType() <= kGameTypeNancy2 ||
-					g_nancy->getGameType() >= kGameTypeNancy10 ? 10 : 20),
+					g_nancy->getGameType() == kGameTypeNancy10 ? 10 : 20),
 		_hasDrawnTextbox(false),
 		_pickedResponse(-1) {
 	_conditionalResponseCharacterID = _noResponse;


Commit: 5a91983cb4a048c3a51effa067a5c709cc81b835
    https://github.com/scummvm/scummvm/commit/5a91983cb4a048c3a51effa067a5c709cc81b835
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-06-05T00:55:25+03:00

Commit Message:
NANCY: Adapt SetPlayerClock puzzle for Nancy11

Changed paths:
    engines/nancy/action/puzzle/setplayerclock.cpp


diff --git a/engines/nancy/action/puzzle/setplayerclock.cpp b/engines/nancy/action/puzzle/setplayerclock.cpp
index 8d921f85cca..969479887ed 100644
--- a/engines/nancy/action/puzzle/setplayerclock.cpp
+++ b/engines/nancy/action/puzzle/setplayerclock.cpp
@@ -82,6 +82,10 @@ void SetPlayerClock::readData(Common::SeekableReadStream &stream) {
 
 	stream.skip(2);
 
+	if (g_nancy->getGameType() >= kGameTypeNancy11) {
+		stream.skip(2);
+	}
+
 	_buttonSound.readNormal(stream);
 	_alarmSetScene.readData(stream);
 	_alarmSoundDelay = stream.readUint16LE();


Commit: c8d1e4a19c2fd1394d577993f7c90f7508d08301
    https://github.com/scummvm/scummvm/commit/c8d1e4a19c2fd1394d577993f7c90f7508d08301
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-06-05T00:55:25+03:00

Commit Message:
NANCY: Add info for the SCTB (ScrollableTextBox) chunk in Nancy11

Changed paths:
    engines/nancy/enginedata.h


diff --git a/engines/nancy/enginedata.h b/engines/nancy/enginedata.h
index 23483060220..11d29695d9d 100644
--- a/engines/nancy/enginedata.h
+++ b/engines/nancy/enginedata.h
@@ -506,7 +506,9 @@ struct SHUI : public EngineData {
 	Common::Array<Common::Rect> _sliderRects;	// Slider rects
 };
 
-// Scheduled talk (?) UI. Introduced in Nancy 11
+// ScrollTextBox - introduced in Nancy 11. Configuration for the scrollable
+// text-box UI used for long textbox content (e.g. journal / scheduled-talk
+// panels) that needs to scroll within a fixed frame.
 struct SCTB : public EngineData {
 	SCTB(Common::SeekableReadStream *chunkStream);
 


Commit: f6a0670535c883b082cff7e7fd5afac6b0c8bce8
    https://github.com/scummvm/scummvm/commit/f6a0670535c883b082cff7e7fd5afac6b0c8bce8
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-06-05T00:55:28+03:00

Commit Message:
NANCY: Disable the text box for Nancy11, for now

It has differences in the implementation compared to Nancy10, so it
renders incorrectly as a big red box, so disable it for now

Changed paths:
    engines/nancy/ui/textbox.cpp


diff --git a/engines/nancy/ui/textbox.cpp b/engines/nancy/ui/textbox.cpp
index e7e2d7efe40..9126feb4f38 100644
--- a/engines/nancy/ui/textbox.cpp
+++ b/engines/nancy/ui/textbox.cpp
@@ -118,6 +118,11 @@ void Textbox::init() {
 }
 
 void Textbox::registerGraphics() {
+	if (g_nancy->getGameType() >= kGameTypeNancy11) {
+		// TODO: Textbox for Nancy11+
+		return;
+	}
+
 	RenderObject::registerGraphics();
 	if (_scrollbar)
 		_scrollbar->registerGraphics();



From noreply at scummvm.org  Thu Jun  4 23:27:19 2026
From: noreply at scummvm.org (mduggan)
Date: Thu,  4 Jun 2026 23:27:19 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 96305a88435d59fcd3e85480f4c1dee428d66ea6
Message-ID: <20260604232719.98684700573@ron.scummvm.net>

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

Summary:
96305a8843 ACCESS: JANITORIAL: Ignore some PVS Studio false-positives


Commit: 96305a88435d59fcd3e85480f4c1dee428d66ea6
    https://github.com/scummvm/scummvm/commit/96305a88435d59fcd3e85480f4c1dee428d66ea6
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2026-06-05T08:44:48+10:00

Commit Message:
ACCESS: JANITORIAL: Ignore some PVS Studio false-positives

Changed paths:
    engines/access/martian/martian_game.cpp
    engines/access/noctropolis/noctropolis_game.cpp
    engines/access/noctropolis/noctropolis_resources.cpp
    engines/access/scripts.cpp


diff --git a/engines/access/martian/martian_game.cpp b/engines/access/martian/martian_game.cpp
index a71bd2d40b0..65c8a81e9a5 100644
--- a/engines/access/martian/martian_game.cpp
+++ b/engines/access/martian/martian_game.cpp
@@ -40,7 +40,8 @@ _creditsStream(nullptr)
 MartianEngine::~MartianEngine() {
 	_skipStart = false;
 	_creditsStream = nullptr;
-}
+	// _video will be deleted in the parent
+} // -V773
 
 void MartianEngine::initObjects() {
 	_inventory = new MartianInventory(this);
diff --git a/engines/access/noctropolis/noctropolis_game.cpp b/engines/access/noctropolis/noctropolis_game.cpp
index 1024d7ccdeb..8e040bad117 100644
--- a/engines/access/noctropolis/noctropolis_game.cpp
+++ b/engines/access/noctropolis/noctropolis_game.cpp
@@ -46,7 +46,7 @@ _stilFlag2(_flags[234])
 NoctropolisEngine::~NoctropolisEngine() {
 	delete _invScript;
 	// _midi will be deleted in parent.
-}
+} //-V773
 
 void NoctropolisEngine::initObjects() {
 	_room = new NoctropolisRoom(this);
diff --git a/engines/access/noctropolis/noctropolis_resources.cpp b/engines/access/noctropolis/noctropolis_resources.cpp
index f07c94b4a56..43739415fd3 100644
--- a/engines/access/noctropolis/noctropolis_resources.cpp
+++ b/engines/access/noctropolis/noctropolis_resources.cpp
@@ -1820,14 +1820,14 @@ static constexpr const char *INVNAMES[4][76] = {
 {" ", "      MORE ITEMS", "      MORE ITEMS", "      FENCE SPIRE", "     SILVER TOKEN", "      GOLD TOKEN", "       NOTICES", "     TICKET STUB", "    DIVORCE PAPERS", "       POSTCARD", "    BANK STATEMENT", "    LOTTERY LETTER", "       NEWSPAPER", "     BUTTER KNIFE", "         WIRE", "       DETONATOR", "PLASTIC EXPLOSIVE STATUE", "        CHALICE", "       COFFIN KEY", "         BILLS", "     SCRAP OF LACE", "         NOTE", "         DIARY", "   LIQUIDARK GRENADE", "       NEWSPAPERS", "   DARKSHEER HISTORY", "      GLASS CUTTER", "     SHARD OF GLASS", "       CLIPBOARD", "     ELEVATOR PASS", "         CLOCK", "  QUICKGROW SOLUTION", "         BONE", "     SACK OF SEEDS", "    THROWING KNIVES", "      MAGIC WAND", "      SCREWDRIVER", "       LOG BOOK", "        OIL CAN", "          LENS", "       MAKEUP KIT", "        JEWELRY", "        HAMMER", "        CHISEL", "    BUTCHER KNIFE", "      MEAT HOOK", "        BEAKER", "         ACID", "     VALVE WHEEL", "         ROPE", "        RE-BAR", "         NET", "         RAT", "         FORK", "         MATCH", "         PAINT", "      PAINT BRUSH", "        PEPPER", "      DENTAL PICK", "         SPEAR", "       EGG SACK", "       DIAMOND", "        COMIC", "          WEB", "   WROUGHT IRON SHAFT", "   CHALICE WITH WATER", "      BROKEN CHAIR", "       SAUSAGE", "        POSTER", "        BRICK", "      NOCTROGLYPH", "       DUCT TAPE", "         BROOM", "        OIL LAMP", "     BROOM AND HOOK", "    ROPE AND SAUSAGE"},
 {" ", "      AUTRES OBJETS", "      AUTRES OBJETS", "      MONTANT DE GRILLE", "     PIECE D'ARGENT", "      PIECE D'OR", "       AVERTISSEMENTS", "     SOUCHE DE TICKET", "    PAPIERS DE DIVORCE", "    CARTE POSTALE", "    RELEVE BANCAIRE", "    LETTRE DU CONCOURS", "       JOURNAL", "     COUTEAU A BEURRE", "         CABLE", "       DETONATEUR", "STATUETTE EXPLOSIVE", "        CALICE", "   CLEF DE CERCUEIL", "      FACTURES", " MORCEAU DE DENTELLE", "       MESSAGE", "      JOURNAL", "  GRENADE DE LIQUIDARK", "       JOURNAL", " HISTOIRE DE DARKSHEER", "      DIAMANT A VITRES", "     ECLAT DE VERRE", "       BLOC-NOTES", "  CARTE MAGNETIQUE", "         REVEIL", "  ENGRAIS MIRACLE", "         MACHOIRE", "     SAC DE GRAINES", "    COUTEAUX", "  BAGUETTE MAGIQUE", "     TOURNEVIS", "     JOURNAL", "       HUILE", "       LENTILLE", " TROUSSE DE MAQUILLAGE", "      BIJOUX", "      MARTEAU", "       BURIN", "   COUTEAU DE BOUCHER", "     CROCHET A VIANDE", "        FIOLE", "         ACIDE", "     VANNE", "         CORDE", "      FER A BETON", "        FILET", "         RAT", "      FOURCHETTE", "      ALLUMETTE", "         PEINTURE", "      PINCEAU", "        POIVRE", "      CURE-DENTS", "        LANCE", "       OEUFS", "       DIAMANT", "   BANDE DESSINEE", "        TOILE", "   TIGE D'ACIER ROUILLEE", "   CALICE PLEIN D'EAU", "      CHAISE CASSEE", "       SAUCISSE", "       AFFICHE", "        BRIQUE", "      NOCTROGLYPHE", "       TEFLON", "         BALAI", "      LAMPE A HUILE", "     BALAI ET CROCHET", "    CORDE ET SAUCISSE"},
 {" ", "      MAS OBJETOS", "      MAS OBJETOS", "  HIERRO DE VERJA", "  MONEDA PLATEADA", "   MONEDA DORADA", "         NOTAS", "     COMPROBANTE", " DEMANDA DE DIVORCIO", "         POSTAL", "   CARTA DEL BANCO", " CARTA DE LA LOTERIA", "      PERIODICO", " CUCHILLO MANTEQUILLA", "      ALAMBRE", "       DETONADOR", "ESTATUA EXPLOSIVA", "        CALIZ", "   LLAVE DE ATAUD", "      FACTURAS", "  TIRA DE ENCAJE", "         NOTA", "         DIARIO", "  GRANADA DE LIQUIOSCURO", "      PERIODICOS", "  TRATADO DE TINIEBLAS", "  CORTADOR DE CRISTAL", "   PEDAZO DE CRISTAL", "     PORTAPAPELES", "  PASE DE ELEVADOR", "         RELOJ", " SOLUCION DE CRECIMIENTO", "        HUESO", "        SEMILLAS", " CUCHILLOS ARROJADIZOS", "     VARA MAGICA", "    DESTORNILLADOR", "       REGISTRO", "  LATA DE ACEITE", "         LENTE", "      MAQUILLAJE", "          JOYAS", "      MARTILLO", "        CINCEL", " CUCHILLO DE CARNE", "  GANCHO DE CARNE", "         JARRA", "        ACIDO", " RUEDA DE VALVULA", "         SOGA", "      PALANCA", "         RED", "        RATA", "      TENEDOR", "       CERILLA", "      PINTURA", "        BROCHA", "      PIMIENTA", "     PICO DENTAL", "         LANZA", "  SACO DE HUEVOS", "       DIAMANTE", "        COMIC", "         TELA", "   ASTA DE HIERRO", "   CALIZ CON AGUA", "     SILLA ROTA", "    SALCHICHA", "        CARTEL", "     LADRILLO", "      NOCTROGLIFO", " CINTA ADHESIVA", "        ESCOBA", " LAMPARA DE ACEITE", "  ESCOBA CON GANCHO", "  SOGA CON SALCHICHA"},
-{" ", "  WEITERE GEGENST\x8E""NDE", "  WEITERE GEGENST\x8E""NDE", "      ZAUNSTREBE", "   SILBERNES SYMBOL", "   GOLDENES SYMBOL", "       MAHNUNGEN", "     LOSABSCHNITT", "  SCHEIDUNGSPAPIERE", "      POSTKARTE", "     KONTOAUSZUG", "   WETTBEWERBSBRIEF", "       ZEITUNG", "    BUTTERMESSER", "        KABEL", "       Z\x9A""NDER", "STATUE AUS PLASTIKSPRENGSTOFF", "       KELCH", "   SARGSCHL\x9A""SSEL", "     RECHNUNGEN", "       SPITZE", "       ZETTEL", "      TAGEBUCH", "   DUSTER\x99""L-GRANATE", "      ZEITUNG", "DIE GESCHICHTE VON DARKSHEER", "   GLASSCHNEIDER", "      SCHERBE", "     KLEMMBRETT", "     MAGNETKARTE", "       WECKER", "  WUCHSBESCHLEUNIGER", "       KNOCHEN", "     SAMENBEUTEL", "      WURFMESSER", "      ZAUBERSTAB", "   SCHRAUBENDREHER", "       LOGBUCH", "      \x99""LK\x8E""NNCHEN", "        LINSE", "     SCHMINKSET", "        SCHMUCK", "        HAMMER", "        MEISSEL", "        MESSER", "    FLEISCHERHAKEN", "       MESSBECHER", "        S\x8E""URE", "    VENTILSTELLRAD", "         SEIL", "     MONIEREISEN", "         NETZ", "        RATTE", "        GABEL", "     STREICHHOLZ", "         LACK", "        PINSEL", "        PFEFFER", "      ZAHNSTOCHER", "         SPEER", "       EIERSACK", "        DIAMANT", "        COMIC", "         NETZ", "  SCHMIEDEEISERNE STANGE", "    KELCH MIT WASSER", "      STUHLTR\x9A""MMER", "       W\x9A""RSTCHEN", "        PLAKAT", "        ZIEGEL", "      NOCTROGLYPH", "      ISOLIERBAND", "         BESEN", "        \x99""LLAMPE", "     STIEL MIT HAKEN", "    SEIL MIT W\x9A""RSTCHEN"},
+{" ", "  WEITERE GEGENST\x8E""NDE", "  WEITERE GEGENST\x8E""NDE", "      ZAUNSTREBE", "   SILBERNES SYMBOL", "   GOLDENES SYMBOL", "       MAHNUNGEN", "     LOSABSCHNITT", "  SCHEIDUNGSPAPIERE", "      POSTKARTE", "     KONTOAUSZUG", "   WETTBEWERBSBRIEF", "       ZEITUNG", "    BUTTERMESSER", "        KABEL", "       Z\x9A""NDER", "STATUE AUS PLASTIKSPRENGSTOFF", "       KELCH", "   SARGSCHL\x9A""SSEL", "     RECHNUNGEN", "       SPITZE", "       ZETTEL", "      TAGEBUCH", "   DUSTER\x99""L-GRANATE", "      ZEITUNG", "DIE GESCHICHTE VON DARKSHEER", "   GLASSCHNEIDER", "      SCHERBE", "     KLEMMBRETT", "     MAGNETKARTE", "       WECKER", "  WUCHSBESCHLEUNIGER", "       KNOCHEN", "     SAMENBEUTEL", "      WURFMESSER", "      ZAUBERSTAB", "   SCHRAUBENDREHER", "       LOGBUCH", "      \x99""LK\x8E""NNCHEN", "        LINSE", "     SCHMINKSET", "        SCHMUCK", "        HAMMER", "        MEISSEL", "        MESSER", "    FLEISCHERHAKEN", "       MESSBECHER", "        S\x8E""URE", "    VENTILSTELLRAD", "         SEIL", "     MONIEREISEN", "         NETZ", "        RATTE", "        GABEL", "     STREICHHOLZ", "         LACK", "        PINSEL", "        PFEFFER", "      ZAHNSTOCHER", "         SPEER", "       EIERSACK", "        DIAMANT", "        COMIC", "         NETZ", "  SCHMIEDEEISERNE STANGE", "    KELCH MIT WASSER", "      STUHLTR\x9A""MMER", "       W\x9A""RSTCHEN", "        PLAKAT", "        ZIEGEL", "      NOCTROGLYPH", "      ISOLIERBAND", "         BESEN", "        \x99""LLAMPE", "     STIEL MIT HAKEN", "    SEIL MIT W\x9A""RSTCHEN"}, //-V653
 };
 
 static constexpr const char *ASKITEMS[4][33] = {
 {"      NOCTROPOLIS", "       DARKSHEER", "       STILETTO", "    FATHER DESMOND", "       SUCCUBUS", "        CYGNUS", "      SAM JENKINS", "       MS. SHOTO", "      GREENTHUMB", "        TOPHAT", "        BUTCHER", "    MASTER MACABRE", "       DREALMER", "         FLUX", "    SUNSPIRE TOWER", "      LIQUIDARK", "      SHADESKIN", "      SHADOWLAIR", "     OBSERVATORY", "      CATHEDRAL", "     BEN HUTCHINS", "      MAUSOLEUM", "CENTRAL PARK GREENHOUSE", "      WHISPERMAN", "      OPERA- NEX", "      OPERAHOUSE", "     SUBTERRAINIA", "       CREATURE", "        WARDS", "    PETER BORNICK", "  INCARNATE/DYNATEK", "      JIM DRAKE", "      DISASTERS"},
 {"      NOCTROPOLIS", "       DARKSHEER", "       STILETTO", "    PERE DESMOND", "       SUCCUBE", "        CYGNE", "      SAM JENKINS", "       MME SHOTO", "      MAINVERTE", "        TOPHAT", "        BOUCHER", "    MAITRE MACABRE", "       DREALMER", "         FLUX", "    TOUR SUNSPIRE", "      LIQUIDARK", "    PEAU D'OMBRE", "    ANTRE D'OMBRE", "     OBSERVATOIRE", "      CATHEDRALE", "     BEN HUTCHINS", "      MAUSOLEE", " SERRE DE CENTRAL PARK", "      CHUCHOTEUR", "      OPERA-NEX", "         OPERA", "     SUBTERRAINIA", "       CREATURE", "      PUPILLES", "    PETER BORNICK", "  INCARNATE/DYNATEK", "      JIM DRAKE", "     CATASTROPHES"},
 {"      NOCTROPOLIS", "       TENEBROSO", "       STILETTO", "     PADRE DESMOND", "       SUCCUBUS", "        CYGNUS", "      SAM JENKINS", "      SRA. SHOTO", "       DEDOVERDE", "          DIVA", "      CARNICERO", "   MAESTRO MACABRO", "       MORFEICO", "         FLUX", "       TORRE SOLAR", "    LIQUIOSCURO", "   PIEL SOMBRIA", "   CUEVA SOMBRIA", "    OBSERVATORIO", "       CATEDRAL", "     BEN HUTCHINS", "       MAUSOLEO", "        INVERNADERO", "      WHISPERMAN", "      OPERA- NEX", "           OPERA", "      SUBTERRANIA", "       CRIATURA", "   GUARDIANES", "    PETER BORNICK", " ENCARNADAS/DYNATEK", "      JIM DRAKE", "      DESASTRES"},
-{"      NOCTROPOLIS", "       DARKSHEER", "       STILETTO", "    VATER DESMOND", "       SUCCUBUS", "        CYGNUS", "      SAM JENKINS", "      MISS SHOTO", "     GR\x9A""ND\x8E""UMLING", "  MISS ZYLINDERCHEN", "        METZGER", "    DOKTOR MAKABER", "       TR\x8E""UMLER", "         FLUX", "     SONNENNADEL", "       DUSTER\x99""L", "     FINSTERHAUT", "    SCHATTENHORT", "    OBSERVATORIUM", "         DOM", "    BEN HUTCHINS", "     MAUSOLEUM", "CENTRAL PARK TREIBHAUS", "     FL\x9A""STERMANN", "      OPER-NEX", "      OPERNHAUS", "    SUBTERRAINIA", "      GESCH\x99""PF", "      DIE WEHR", "    PETER BORNICK", "  INCARNATE/DYNATEK", "      JIM DRAKE", "    KATASTROPHEN"},
+{"      NOCTROPOLIS", "       DARKSHEER", "       STILETTO", "    VATER DESMOND", "       SUCCUBUS", "        CYGNUS", "      SAM JENKINS", "      MISS SHOTO", "     GR\x9A""ND\x8E""UMLING", "  MISS ZYLINDERCHEN", "        METZGER", "    DOKTOR MAKABER", "       TR\x8E""UMLER", "         FLUX", "     SONNENNADEL", "       DUSTER\x99""L", "     FINSTERHAUT", "    SCHATTENHORT", "    OBSERVATORIUM", "         DOM", "    BEN HUTCHINS", "     MAUSOLEUM", "CENTRAL PARK TREIBHAUS", "     FL\x9A""STERMANN", "      OPER-NEX", "      OPERNHAUS", "    SUBTERRAINIA", "      GESCH\x99""PF", "      DIE WEHR", "    PETER BORNICK", "  INCARNATE/DYNATEK", "      JIM DRAKE", "    KATASTROPHEN"}, //-V653
 };
 
 
@@ -2077,7 +2077,7 @@ static constexpr const char *NOCT_GENERAL_MESSAGES[4][10] = {
 	"\x80""a ne s'ouvre pas.",
 	"\x80""a ne bouge pas.",
 	"Vous ne pouvez pas prendre \x87""a.",
-	"Vous n'obtenez aucune r\x82""ponse.",
+	"Vous n'obtenez aucune r\x82""ponse.", //-V653
 	"\x80""a ne marche pas.",
 	"Vous ne pouvez pas aller l\x85.",
 	"\x80""a ne marche pas.",
diff --git a/engines/access/scripts.cpp b/engines/access/scripts.cpp
index 00280b0edb8..1d9ed34f795 100644
--- a/engines/access/scripts.cpp
+++ b/engines/access/scripts.cpp
@@ -214,7 +214,8 @@ void Scripts::charLoop() {
 	bool endFlag = _endFlag;
 	_continuenceFlag = false;
 
-	int64 pos = _data->pos();
+	// Data should never be null here, but might be after running script.
+	int64 pos = _data->pos(); // -V595
 
 	_sequence = ROOM_SCRIPT;
 	searchForSequence();
@@ -289,7 +290,8 @@ int Scripts::executeScript() {
 	_endFlag = false;
 	_returnCode = 0;
 
-	debugC(1, kDebugScripts, "** Start script execution (at %d/%d bytes) **", (int)_data->pos(), (int)_data->size());
+	// Data should never be null here but may be after running script
+	debugC(1, kDebugScripts, "** Start script execution (at %d/%d bytes) **", (int)_data->pos(), (int)_data->size()); // -V595
 	do {
 		// Get next command, skipping over script start start if it's being pointed to
 		while ((_scriptCommand = _data->readByte()) == SCRIPT_START_BYTE)



From noreply at scummvm.org  Fri Jun  5 04:05:53 2026
From: noreply at scummvm.org (aquadran)
Date: Fri,  5 Jun 2026 04:05:53 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 cfb2cf80bb6df0dcce41a875999ddbc65c257e41
Message-ID: <20260605040553.06E947005BC@ron.scummvm.net>

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

Summary:
cfb2cf80bb NEWS: Mention PS3 release packages as separate modules/executables


Commit: cfb2cf80bb6df0dcce41a875999ddbc65c257e41
    https://github.com/scummvm/scummvm/commit/cfb2cf80bb6df0dcce41a875999ddbc65c257e41
Author: Pawe?? Ko??odziejski (aquadran at gmail.com)
Date: 2026-06-05T06:05:40+02:00

Commit Message:
NEWS: Mention PS3 release packages as separate modules/executables

Changed paths:
    NEWS.md


diff --git a/NEWS.md b/NEWS.md
index 7ffbbccbedc..b4ce092bdd7 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -57,6 +57,9 @@ For a more comprehensive changelog of the latest experimental code, see:
    - Fixed holomap model rendering.
    - Disable autosaves while driving a vehicle.
 
+ PS3 port:
+   - Added support for running ScummVM engines as separate modules/executables.
+     This saves about 90 MB of RAM. It is enabled only for release packages.
 
 #### 2026.2.0 "Railmonicon" (2026-03-28)
 



From noreply at scummvm.org  Fri Jun  5 07:00:33 2026
From: noreply at scummvm.org (dreammaster)
Date: Fri,  5 Jun 2026 07:00:33 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 bb0f0a1564c834e22266c7e84c4f56aa72c91f78
Message-ID: <20260605070033.B864E7005BC@ron.scummvm.net>

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

Summary:
bb0f0a1564 MADS: FOREST: Implemented room 104


Commit: bb0f0a1564c834e22266c7e84c4f56aa72c91f78
    https://github.com/scummvm/scummvm/commit/bb0f0a1564c834e22266c7e84c4f56aa72c91f78
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-06-05T17:00:18+10:00

Commit Message:
MADS: FOREST: Implemented room 104

Changed paths:
    engines/mads/madsv2/core/kernel.h
    engines/mads/madsv2/core/speech.h
    engines/mads/madsv2/forest/rooms/room101.cpp
    engines/mads/madsv2/forest/rooms/room103.cpp
    engines/mads/madsv2/forest/rooms/room104.cpp


diff --git a/engines/mads/madsv2/core/kernel.h b/engines/mads/madsv2/core/kernel.h
index 9fc707b9f72..48aa7a90512 100644
--- a/engines/mads/madsv2/core/kernel.h
+++ b/engines/mads/madsv2/core/kernel.h
@@ -23,6 +23,7 @@
 #define MADS_CORE_KERNEL_H
 
 #include "common/serializer.h"
+#include "common/textconsole.h"
 #include "mads/madsv2/core/general.h"
 #include "mads/madsv2/core/anim.h"
 #include "mads/madsv2/core/color.h"
@@ -630,6 +631,10 @@ extern void kernel_random_frame(int handle, int16 *frame, int mode);
 extern void kernel_translate_anim(int handle, int delta_x, int delta_y, int delta_scale);
 extern void kernel_position_anim(int handle, int x, int y, int scale, int depth);
 
+inline void kernel_save_game(const char *buf) {
+	error("TODO: refactor to ScummVM savegame code");
+}
+
 } // namespace MADSV2
 } // namespace MADS
 
diff --git a/engines/mads/madsv2/core/speech.h b/engines/mads/madsv2/core/speech.h
index f9fd283c326..0e393185566 100644
--- a/engines/mads/madsv2/core/speech.h
+++ b/engines/mads/madsv2/core/speech.h
@@ -47,6 +47,10 @@ extern int speech_status();
 
 extern void init_speech();
 
+inline void speech_ems_play(const char *resName, int id, bool /*memFlag?*/) {
+	speech_play(resName, id);
+}
+
 } // namespace MADSV2
 } // namespace MADS
 
diff --git a/engines/mads/madsv2/forest/rooms/room101.cpp b/engines/mads/madsv2/forest/rooms/room101.cpp
index ff30057be70..156824d8b41 100644
--- a/engines/mads/madsv2/forest/rooms/room101.cpp
+++ b/engines/mads/madsv2/forest/rooms/room101.cpp
@@ -1116,8 +1116,12 @@ void room_101_preload() {
 	room_parser_code_pointer = room_101_parser;
 	room_daemon_code_pointer = room_101_daemon;
 
+	if (flags[0] == -4)
+		global[g016] = -1;
+
 	section_1_walker();
 	section_1_interface();
+	player.walker_must_reload = true;
 }
 
 } // namespace Rooms
diff --git a/engines/mads/madsv2/forest/rooms/room103.cpp b/engines/mads/madsv2/forest/rooms/room103.cpp
index d8883f18883..7ec53fc9625 100644
--- a/engines/mads/madsv2/forest/rooms/room103.cpp
+++ b/engines/mads/madsv2/forest/rooms/room103.cpp
@@ -423,8 +423,14 @@ void room_103_preload() {
 	room_parser_code_pointer = room_103_parser;
 	room_daemon_code_pointer = room_103_daemon;
 
-	section_1_walker();
-	section_1_interface();
+	if (flags[1] == -4)
+		global[g016] = -1;
+	if (previous_room != 199) {
+		section_1_walker();
+		section_1_interface();
+	}
+
+	player.walker_must_reload = true;
 }
 
 } // namespace Rooms
diff --git a/engines/mads/madsv2/forest/rooms/room104.cpp b/engines/mads/madsv2/forest/rooms/room104.cpp
index 680889df50d..d435bb44d8f 100644
--- a/engines/mads/madsv2/forest/rooms/room104.cpp
+++ b/engines/mads/madsv2/forest/rooms/room104.cpp
@@ -1,4 +1,4 @@
-???/* ScummVM - Graphic Adventure Engine
+/* 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
@@ -20,16 +20,22 @@
  */
 
 #include "mads/madsv2/core/conv.h"
+#include "mads/madsv2/core/digi.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/imath.h"
 #include "mads/madsv2/core/inter.h"
 #include "mads/madsv2/core/kernel.h"
+#include "mads/madsv2/core/matte.h"
+#include "mads/madsv2/core/midi.h"
+#include "mads/madsv2/core/mouse.h"
+#include "mads/madsv2/core/player.h"
 #include "mads/madsv2/core/sound.h"
 #include "mads/madsv2/core/text.h"
 #include "mads/madsv2/forest/mads/inventory.h"
 #include "mads/madsv2/forest/mads/sounds.h"
 #include "mads/madsv2/forest/mads/words.h"
 #include "mads/madsv2/forest/global.h"
+#include "mads/madsv2/forest/journal.h"
 #include "mads/madsv2/forest/rooms/section1.h"
 #include "mads/madsv2/forest/rooms/room104.h"
 
@@ -39,14 +45,24 @@ namespace Forest {
 namespace Rooms {
 
 struct Scratch {
-	int16 sprite[15];       /* Sprite series handles */
-	int16 sequence[15];     /* Sequence handles      */
-	int16 animation[4];     /* Animation handles     */
+	int16 sprite[10];       /* Sprite series handles */
+	int16 sequence[10];     /* Sequence handles      */
+	int16 animation[10];    /* Animation handles     */
+	AnimationInfo animation_info[10];
 
-	int16 dragon_frame;     /* frame animation is on */
-
-	int16 done_with_conv;   /* T if done with conv   */
-	int16 prev_room;
+	int16 _8c;
+	int16 _8e;
+	int16 _90;
+	int16 _92;
+	int16 _94;
+	int16 _96;
+	int16 _98;
+	int16 _9a;
+	int16 _9c;
+	int16 _9e;
+	int16 _a0;
+	int16 _a2;
+	int16 _a4;
 };
 
 static Scratch scratch;
@@ -55,34 +71,912 @@ static Scratch scratch;
 #define ss    local->sprite
 #define seq   local->sequence
 #define aa    local->animation
+#define aainfo scratch.animation_info
 
+static void room_104_init1();
+static void room_104_init2();
+static void room_104_init3();
+static void room_104_anim1();
+static void room_104_anim2();
+static void room_104_anim3();
+static void room_104_anim4();
+static void room_104_anim5();
+static void room_104_anim6();
+static void room_104_anim7();
+static void room_104_anim8();
 
 static void room_104_init() {
+	scratch._a0 = -1;
+	global[player_score] = 0;
+
+	for (int count = 0; count < 10; count++) {
+		aainfo[count]._val1 = 0;
+		aainfo[count]._val2 = 1;
+		aainfo[count]._val3 = 0;
+		aainfo[count]._val4 = 0;
+	}
+
+	if (previous_room != -2 && previous_room != 199) {
+		player.walker_visible = 0;
+		player.commands_allowed = 0;
+		if (flags[4] != 3)
+			flags[4]++;
+	}
+
+	switch (flags[4]) {
+	case -3:
+		if (previous_room == 107) { flags[4] = 5; room_104_init2(); return; }
+		room_104_init1();
+		return;
+	case -2:
+		if (previous_room == 107) { flags[4] = 5; room_104_init2(); return; }
+		flags[4] = -3;
+		room_104_init1();
+		return;
+	case -1:
+		if (previous_room == 107) { flags[4] = 5; room_104_init2(); return; }
+		flags[4] = -3;
+		room_104_init1();
+		return;
+	case 1:
+		if (previous_room == 107) { flags[4] = 5; room_104_init2(); return; }
+		room_104_init3();
+		return;
+	case 2:
+		if (previous_room == 107) { flags[4] = 5; room_104_init2(); return; }
+		room_104_init3();
+		return;
+	case 3:
+		if (previous_room != 107) { room_104_init3(); return; }
+		flags[4] = 5;
+		room_104_init2();
+		return;
+	case 5:
+		if (previous_room == 107) flags[4] = 5;
+		room_104_init2();
+		return;
+	case 6:
+		if (previous_room == 107) { flags[4] = 5; room_104_init2(); return; }
+		room_104_init3();
+		return;
+	case 7:
+		if (previous_room == 107) { flags[4] = 5; room_104_init2(); return; }
+		room_104_init3();
+		return;
+	default:
+		room_104_init3();
+		return;
+	}
+}
+
+static void room_104_init1() {
+	global[player_score] = 0;
+	global[g009] = -1;
+	global_digi_play(8);
+	viewing_at_y = 22;
+	player.walker_visible = 0;
+	player.commands_allowed = 0;
+	mouse_hide();
+
+	for (int count = 0; count < 10; count++) {
+		aainfo[count]._val1 = 0;
+		aainfo[count]._val2 = -1;
+	}
+
+	aa[6] = kernel_run_animation(kernel_name('s', 1), 0);
+	aainfo[6]._val1 = -1;
+}
+
+static void room_104_init2() {
+	global[player_score] = 0;
+	global[g009] = 0;
+	midi_stop();
+	viewing_at_y = 22;
+	player.walker_visible = 0;
+	player.commands_allowed = 0;
+	mouse_hide();
+
+	for (int count = 0; count < 10; count++) {
+		aainfo[count]._val1 = 0;
+		aainfo[count]._val2 = -1;
+	}
+
+	aa[7] = kernel_run_animation(kernel_name('T', 1), 0);
+	aainfo[7]._val1 = -1;
+}
+
+static void room_104_init3() {
+	global[g009] = -1;
+	global_digi_play(8);
+	global[g131] = 0;
+	global[g141] = 0;
+
+	scratch._9c = kernel_run_animation_disp('r', 4, 0);
+	kernel_position_anim(scratch._9c, 265, 111, 87, 14);
+	scratch._9a = kernel_run_animation_disp('e', 4, 0);
+	kernel_position_anim(scratch._9a, 248, 137, 94, 3);
+
+	aa[4] = kernel_run_animation(kernel_name('C', 1), 0);
+	aainfo[4]._val1 = -1;
+	aa[5] = kernel_run_animation(kernel_name('M', 1), 0);
+	aainfo[5]._val1 = -1;
+
+	if (previous_room != -2) {
+		player.x = 214;
+		player.y = 142;
+		player.facing = 4;
+	}
+
+	if (previous_room == -2) {
+		if (global[g101] != 0) return;
+		global[g131] = -1;
+		global[g141] = -1;
+		kernel_reset_animation(scratch._9a, 2);
+		kernel_reset_animation(scratch._9c, 2);
+		global[g133] = 0;
+		global[g143] = 0;
+		return;
+	} else if (previous_room == 101) {
+		scratch._a0 = 0;
+		aa[0] = kernel_run_animation(kernel_name('y', 1), 100);
+		aainfo[0]._val1 = -1;
+		scratch._92 = 31;
+		return;
+	} else if (previous_room == 199) {
+		global[g131] = -1;
+		global[g141] = -1;
+		kernel_reset_animation(scratch._9a, 2);
+		kernel_reset_animation(scratch._9c, 2);
+		global[g133] = 0;
+		global[g143] = 0;
+		restore_player();
+		player.commands_allowed = -1;
+		player.walker_visible = -1;
+	} else {
+		if (global[g101] != 0) return;
+		global[g131] = -1;
+		global[g141] = -1;
+		kernel_reset_animation(scratch._9a, 2);
+		kernel_reset_animation(scratch._9c, 2);
+		global[g133] = 0;
+		global[g143] = 0;
+		player.commands_allowed = -1;
+		player.walker_visible = -1;
+	}
+}
+
+static void room_104_anim1() {
+	if (kernel_anim[aa[6]].frame != aainfo[6]._val2) {
+		int16 frame = kernel_anim[aa[6]].frame;
+		aainfo[6]._val2 = frame;
+		scratch._a0 = 0;
+
+		switch (frame) {
+		case 1:
+			aainfo[6]._val3 = 7;
+			digi_play_build(104, 'c', 1, 1);
+			scratch._a2 = 1;
+			break;
+		case 5:
+			digi_initial_volume(40);
+			digi_play_build(104, '_', 2, 2);
+			scratch._a4 = 5;
+			break;
+		case 32:
+			if (aainfo[6]._val3 == 7) {
+				aainfo[6]._val2 = 13;
+				kernel_reset_animation(aa[6], 13);
+			} else if (aainfo[6]._val3 == 9) {
+				aainfo[6]._val2 = 31;
+				kernel_reset_animation(aa[6], 31);
+			}
+			break;
+		case 33:
+			aainfo[6]._val3 = 8;
+			digi_play_build(104, 'e', 1, 1);
+			scratch._a2 = 33;
+			break;
+		case 42:
+			if (aainfo[6]._val3 == 8) {
+				aainfo[6]._val2 = 33;
+				kernel_reset_animation(aa[6], 33);
+			}
+			break;
+		case 43:
+			digi_initial_volume(40);
+			digi_play_build(104, '_', 2, 2);
+			scratch._a4 = 43;
+			break;
+		case 45:
+			if (aainfo[6]._val3 == 9) {
+				aainfo[6]._val2 = 44;
+				kernel_reset_animation(aa[6], 44);
+			}
+			break;
+		case 46:
+			aainfo[6]._val3 = 7;
+			digi_play_build(104, 'c', 2, 1);
+			scratch._a2 = 46;
+			break;
+		case 69:
+			if (aainfo[6]._val3 == 7) {
+				aainfo[6]._val2 = 46;
+				kernel_reset_animation(aa[6], 46);
+			}
+			break;
+		case 71:
+			if (aainfo[6]._val3 == 9) {
+				aainfo[6]._val2 = 70;
+				kernel_reset_animation(aa[6], 70);
+			}
+			break;
+		case 75:
+			digi_initial_volume(40);
+			digi_play_build(104, '_', 2, 2);
+			scratch._a4 = 75;
+			break;
+		case 82:
+			digi_play_build(101, '_', 1, 1);
+			scratch._a4 = 82;
+			break;
+		case 95:
+			if (global[g023] != 0) {
+				new_room = 101;
+				global[g023] = 0;
+			} else {
+				new_room = 904;
+			}
+			break;
+		}
+	}
+
+	if (kernel.trigger == 7 || kernel.trigger == 28) {
+		switch (scratch._a2) {
+		case 1:
+			kernel_timing_trigger(30, 28);
+			aainfo[6]._val3 = 9;
+			scratch._a2 = 2;
+			break;
+		case 2:
+			aainfo[6]._val3 = 10;
+			aainfo[6]._val2 = 31;
+			kernel_reset_animation(aa[6], 31);
+			scratch._a2 = -1;
+			break;
+		case 32:
+			kernel_timing_trigger(30, 28);
+			aainfo[6]._val3 = 9;
+			scratch._a2 = 33;
+			break;
+		case 33:
+			aainfo[6]._val3 = 10;
+			aainfo[6]._val2 = 42;
+			kernel_reset_animation(aa[6], 42);
+			scratch._a2 = -1;
+			break;
+		case 46:
+			kernel_timing_trigger(30, 28);
+			aainfo[6]._val3 = 9;
+			scratch._a2 = 47;
+			break;
+		case 47:
+			aainfo[6]._val3 = 7;
+			aainfo[6]._val2 = 13;
+			digi_play_build(104, 'c', 3, 1);
+			kernel_reset_animation(aa[6], aainfo[6]._val2);
+			scratch._a2 = 48;
+			break;
+		case 48:
+			kernel_timing_trigger(30, 28);
+			aainfo[6]._val3 = 9;
+			scratch._a2 = 49;
+			break;
+		case 49:
+			aainfo[6]._val3 = 10;
+			aainfo[6]._val2 = 69;
+			kernel_reset_animation(aa[6], 69);
+			scratch._a2 = -1;
+			break;
+		}
+	}
+
+	if (kernel.trigger == 8) {
+		if (scratch._a4 == 5 || scratch._a4 == 43 || scratch._a4 == 75)
+			scratch._a4 = -1;
+	}
+}
+
+static void room_104_anim2() {
+	if (kernel_anim[aa[7]].frame != aainfo[7]._val2) {
+		int16 frame = kernel_anim[aa[7]].frame;
+		aainfo[7]._val2 = frame;
+		scratch._a0 = 0;
+
+		switch (frame) {
+		case 1:
+			digi_initial_volume(40);
+			digi_play_build(104, '_', 2, 2);
+			scratch._a4 = 5;
+			break;
+		case 14:
+			aainfo[7]._val3 = 7;
+			digi_play_build(104, 'c', 4, 1);
+			scratch._a2 = 20;
+			break;
+		case 30:
+			aainfo[7]._val3 = 8;
+			digi_play_build(104, 'e', 2, 1);
+			scratch._a2 = 30;
+			break;
+		case 31: case 32: case 33: case 34: case 35:
+			if (aainfo[7]._val3 == 8) {
+				aainfo[7]._val2 = imath_random(30, 34);
+				kernel_reset_animation(aa[7], aainfo[7]._val2);
+			} else {
+				aainfo[7]._val2 = 38;
+				kernel_reset_animation(aa[7], 38);
+			}
+			break;
+		case 39:
+			if (aainfo[7]._val3 == 9) {
+				aainfo[7]._val2 = 38;
+				kernel_reset_animation(aa[7], 38);
+			}
+			break;
+		case 40:
+			aainfo[7]._val3 = 11;
+			digi_play_build(104, 'b', 1, 1);
+			scratch._a2 = 40;
+			break;
+		case 41: case 42: case 43: case 44: case 45:
+			if (aainfo[7]._val3 == 11) {
+				aainfo[7]._val2 = imath_random(40, 44);
+				kernel_reset_animation(aa[7], aainfo[7]._val2);
+			} else {
+				aainfo[7]._val2 = 45;
+				kernel_reset_animation(aa[7], 45);
+			}
+			break;
+		case 49:
+			if (aainfo[7]._val3 == 9) {
+				aainfo[7]._val2 = 48;
+				kernel_reset_animation(aa[7], 48);
+			}
+			break;
+		case 50:
+			aainfo[7]._val3 = 7;
+			digi_play_build(104, 'c', 5, 1);
+			scratch._a2 = 50;
+			break;
+		case 51: case 52: case 53: case 54: case 55:
+			if (aainfo[7]._val3 == 7) {
+				aainfo[7]._val2 = imath_random(50, 54);
+				kernel_reset_animation(aa[7], aainfo[7]._val2);
+			} else {
+				aainfo[7]._val2 = 55;
+				kernel_reset_animation(aa[7], 55);
+			}
+			break;
+		case 59:
+			if (aainfo[7]._val3 == 9) {
+				aainfo[7]._val2 = 58;
+				kernel_reset_animation(aa[7], 58);
+			}
+			break;
+		case 64:
+			dont_frag_the_palette();
+			kernel_abort_animation(aa[7]);
+			aainfo[7]._val1 = 0;
+			stop_speech_on_run_animation = false;
+			aa[8] = kernel_run_animation(kernel_name('T', 2), 0);
+			aainfo[8]._val1 = -1;
+			kernel_synch(KERNEL_ANIM, aa[7], KERNEL_ANIM, aa[8]);
+			break;
+		}
+	}
+
+	if (kernel.trigger == 7 || kernel.trigger == 28) {
+		switch (scratch._a2) {
+		case 20:
+			aainfo[7]._val3 = 10;
+			scratch._a2 = -1;
+			break;
+		case 30:
+			kernel_timing_trigger(2, 28);
+			aainfo[7]._val3 = 9;
+			scratch._a2 = 31;
+			break;
+		case 31:
+			aainfo[7]._val3 = 10;
+			aainfo[7]._val2 = 38;
+			kernel_reset_animation(aa[7], 38);
+			scratch._a2 = -1;
+			break;
+		case 40:
+			kernel_timing_trigger(5, 28);
+			aainfo[7]._val3 = 9;
+			scratch._a2 = 41;
+			break;
+		case 41:
+			aainfo[7]._val3 = 10;
+			aainfo[7]._val2 = 45;
+			kernel_reset_animation(aa[7], 45);
+			scratch._a2 = -1;
+			break;
+		case 50:
+			kernel_timing_trigger(5, 28);
+			aainfo[7]._val3 = 9;
+			scratch._a2 = 51;
+			break;
+		case 51:
+			aainfo[7]._val3 = 10;
+			aainfo[7]._val2 = 55;
+			kernel_reset_animation(aa[7], 55);
+			scratch._a2 = -1;
+			break;
+		}
+	}
+
+	if (kernel.trigger == 8) {
+		if (scratch._a4 == 5)
+			scratch._a4 = -1;
+	}
+}
+
+static void room_104_anim3() {
+	if (kernel_anim[aa[8]].frame != aainfo[8]._val2) {
+		int16 frame = kernel_anim[aa[8]].frame;
+		aainfo[8]._val2 = frame;
+		scratch._a0 = 0;
+
+		switch (frame) {
+		case 1:
+			digi_initial_volume(40);
+			digi_play_build(104, '_', 5, 2);
+			scratch._a4 = 1;
+			break;
+		case 41:
+			aainfo[8]._val3 = 7;
+			digi_play_build(104, 'c', 6, 1);
+			scratch._a2 = 41;
+			break;
+		case 45:
+			if (aainfo[8]._val3 == 7) {
+				aainfo[8]._val2 = 41;
+				kernel_reset_animation(aa[8], 41);
+			}
+			break;
+		case 47:
+			if (aainfo[8]._val3 == 9) {
+				aainfo[8]._val2 = 46;
+				kernel_reset_animation(aa[8], 46);
+			}
+			break;
+		case 48:
+			digi_play_build(104, '_', 750, 2);
+			scratch._a4 = 48;
+			break;
+		case 59:
+			aainfo[8]._val3 = 12;
+			digi_play_build(104, 'm', 1, 1);
+			scratch._a2 = 59;
+			break;
+		case 69:
+			if (aainfo[8]._val3 == 12) {
+				aainfo[8]._val2 = 60;
+				kernel_reset_animation(aa[8], 60);
+			}
+			break;
+		case 74:
+			if (aainfo[8]._val3 == 9) {
+				aainfo[8]._val2 = 73;
+				kernel_reset_animation(aa[8], 73);
+			}
+			break;
+		case 75:
+			aainfo[8]._val3 = 11;
+			digi_play_build(104, 'b', 2, 1);
+			scratch._a2 = 75;
+			break;
+		case 79:
+			if (aainfo[8]._val3 == 11) {
+				aainfo[8]._val2 = 75;
+				kernel_reset_animation(aa[8], 75);
+			}
+			break;
+		case 83:
+			if (aainfo[8]._val3 == 9) {
+				aainfo[8]._val2 = 82;
+				kernel_reset_animation(aa[8], 82);
+			}
+			break;
+		case 84:
+			aainfo[8]._val3 = 8;
+			digi_play_build(104, 'e', 3, 1);
+			scratch._a2 = 84;
+			global_digi_play(14);
+			break;
+		case 89:
+			if (aainfo[8]._val3 == 8) {
+				aainfo[8]._val2 = 84;
+				kernel_reset_animation(aa[8], 84);
+			}
+			break;
+		case 94:
+			new_room = 510;
+			break;
+		}
+	}
+
+	if (kernel.trigger == 7 || kernel.trigger == 28) {
+		switch (scratch._a2) {
+		case 41:
+			kernel_timing_trigger(30, 28);
+			aainfo[8]._val3 = 9;
+			scratch._a2 = 42;
+			break;
+		case 42:
+			aainfo[8]._val3 = 10;
+			aainfo[8]._val2 = 45;
+			kernel_reset_animation(aa[8], 45);
+			scratch._a2 = -1;
+			break;
+		case 59:
+			kernel_timing_trigger(30, 28);
+			aainfo[8]._val3 = 9;
+			scratch._a2 = 60;
+			break;
+		case 60:
+			aainfo[8]._val3 = 10;
+			aainfo[8]._val2 = 70;
+			kernel_reset_animation(aa[8], 70);
+			scratch._a2 = -1;
+			break;
+		case 75:
+			kernel_timing_trigger(30, 28);
+			aainfo[8]._val3 = 9;
+			scratch._a2 = 76;
+			break;
+		case 76:
+			aainfo[8]._val3 = 10;
+			aainfo[8]._val2 = 79;
+			kernel_reset_animation(aa[8], 79);
+			scratch._a2 = -1;
+			break;
+		case 84:
+			kernel_timing_trigger(30, 28);
+			aainfo[8]._val3 = 9;
+			scratch._a2 = 85;
+			break;
+		case 85:
+			aainfo[8]._val3 = 10;
+			aainfo[8]._val2 = 89;
+			kernel_reset_animation(aa[8], 89);
+			scratch._a2 = -1;
+			break;
+		}
+	}
+
+	if (kernel.trigger == 8) {
+		if (scratch._a4 == 1 || scratch._a4 == 48)
+			scratch._a4 = -1;
+	}
+}
+
+static void room_104_anim4() {
+	int16 var_2 = -1;
+
+	if (kernel_anim[aa[4]].frame != aainfo[4]._val2) {
+		int16 frame = kernel_anim[aa[4]].frame;
+		aainfo[4]._val2 = frame;
+		aainfo[4]._val4++;
+
+		if (frame == 7) {
+			var_2 = 0;
+		} else if (frame < 7) {
+			if (frame == 1) {
+				int r1 = imath_random(10, 40);
+				if (r1 >= aainfo[4]._val4) {
+					var_2 = 0;
+				} else {
+					aainfo[4]._val4 = 0;
+					int r2 = imath_random(1, 100);
+					var_2 = (r2 > 35) ? 2 : 1;
+					if (scratch._a0 != 0)
+						digi_play_build(104, '_', 1, 2);
+				}
+			} else if (frame == 2) {
+				int r = imath_random(1, 2);
+				if (r >= aainfo[4]._val4) {
+					var_2 = 1;
+				} else {
+					aainfo[4]._val4 = 0;
+					var_2 = 0;
+				}
+			}
+		}
+	}
+
+	if (var_2 >= 0) {
+		aainfo[4]._val2 = var_2;
+		kernel_reset_animation(aa[4], var_2);
+	}
+}
+
+static void room_104_anim5() {
+	int16 var_2 = -1;
+
+	if (kernel_anim[aa[5]].frame != aainfo[5]._val2) {
+		int16 frame = kernel_anim[aa[5]].frame;
+		aainfo[5]._val2 = frame;
+		aainfo[5]._val4++;
+
+		if (frame == 12 || frame == 5) {
+			var_2 = 0;
+		} else if (frame < 12 && frame == 1) {
+			if (scratch._a0 == 0) {
+				var_2 = 0;
+			} else {
+				int r1 = imath_random(8, 12);
+				if (r1 >= aainfo[5]._val4) {
+					var_2 = 0;
+				} else {
+					aainfo[5]._val4 = 0;
+					int r2 = imath_random(1, 100);
+					if (r2 > 20) {
+						var_2 = 5;
+					} else {
+						var_2 = 1;
+						digi_play_build(104, '_', 2, 2);
+					}
+				}
+			}
+		}
+	}
+
+	if (var_2 >= 0) {
+		aainfo[5]._val2 = var_2;
+		kernel_reset_animation(aa[5], var_2);
+	}
+}
+
+static void room_104_anim6() {
+	if (kernel_anim[aa[0]].frame != aainfo[0]._val2)
+		aainfo[0]._val2 = kernel_anim[aa[0]].frame;
+
+	if (global[player_hyperwalked] == -1) {
+		aainfo[0]._val2 = scratch._92 - 1;
+		kernel_reset_animation(aa[0], aainfo[0]._val2);
+	}
+}
+
+static void room_104_anim7() {
+	if (kernel_anim[aa[2]].frame != aainfo[2]._val2)
+		aainfo[2]._val2 = kernel_anim[aa[2]].frame;
+	int16 frame = aainfo[2]._val2;
+
+	if (global[player_hyperwalked] == -1) {
+		if (frame <= 23) {
+			aainfo[2]._val2 = 23;
+			kernel_reset_animation(aa[2], 23);
+			kernel_synch(KERNEL_ANIM, aa[2], KERNEL_NOW, 0);
+		} else {
+			aainfo[2]._val2 = 44;
+			kernel_reset_animation(aa[2], 44);
+			kernel_synch(KERNEL_ANIM, aa[2], KERNEL_NOW, 0);
+		}
+	} else if (frame == 45) {
+		aainfo[2]._val2 = 44;
+		kernel_reset_animation(aa[2], 44);
+		kernel_synch(KERNEL_ANIM, aa[2], KERNEL_NOW, 0);
+	}
+}
+
+static void room_104_anim8() {
+	if (kernel_anim[aa[1]].frame != aainfo[1]._val2)
+		aainfo[1]._val2 = kernel_anim[aa[1]].frame;
+	int16 frame = aainfo[1]._val2;
+
+	if (global[player_hyperwalked] == -1) {
+		if (frame <= 23) {
+			aainfo[1]._val2 = 23;
+			kernel_reset_animation(aa[1], 23);
+			kernel_synch(KERNEL_ANIM, aa[1], KERNEL_NOW, 0);
+		} else {
+			aainfo[1]._val2 = 44;
+			kernel_reset_animation(aa[1], 44);
+			kernel_synch(KERNEL_ANIM, aa[1], KERNEL_NOW, 0);
+		}
+	} else if (frame == 45) {
+		aainfo[1]._val2 = 44;
+		kernel_reset_animation(aa[1], 44);
+		kernel_synch(KERNEL_ANIM, aa[1], KERNEL_NOW, 0);
+	}
 }
 
 static void room_104_daemon() {
+	if (flags[0] < 0 && global[player_hyperwalked] == -1) {
+		flags[0] = 0;
+		flags[1] = 0;
+		flags[2] = 0;
+		flags[3] = 0;
+		new_room = 904;
+	}
+
+	if (global[g101] != 0 && global[player_hyperwalked] == -1) {
+		game_save_name(0);
+		kernel_save_game(save_game_buf);
+		new_room = 904;
+	}
+
+	switch (kernel.trigger) {
+	case 7:
+		if (global[walker_converse_state] != 0) {
+			global[walker_converse_state] = 0;
+			close_journal(3);
+			player.commands_allowed = -1;
+		}
+		break;
+
+	case 24:
+		if (scratch._8c == 3) {
+			aa[1] = kernel_run_animation(kernel_name('e', 1), 102);
+			aainfo[1]._val1 = -1;
+			scratch._96 = 1;
+			kernel_reset_animation(scratch._9a, 0);
+			kernel_synch(KERNEL_ANIM, aa[1], KERNEL_ANIM, scratch._9a);
+			global[g133] = 1;
+		} else {
+			speech_ems_play("d1", kernel.trigger, scratch._8c);
+		}
+		break;
+
+	case 25:
+		if (scratch._8e == 2) {
+			aa[2] = kernel_run_animation(kernel_name('R', 1), 101);
+			aainfo[2]._val1 = -1;
+			scratch._94 = 2;
+			kernel_reset_animation(scratch._9c, 0);
+			kernel_synch(KERNEL_ANIM, aa[2], KERNEL_ANIM, scratch._9c);
+			global[g143] = 1;
+		}
+		break;
+
+	case 26:
+		kernel_reset_animation(scratch._9a, 0);
+		kernel_reset_animation(scratch._9c, 0);
+		global[g133] = 1;
+		global[g143] = 1;
+		break;
+
+	case 100:
+		kernel_abort_animation(aa[0]);
+		// TODO: word_79954 = 0;
+		global[g131] = -1;
+		global[g141] = -1;
+		kernel_reset_animation(scratch._9a, 1);
+		kernel_synch(KERNEL_ANIM, scratch._9a, KERNEL_NOW, 0);
+		kernel_reset_animation(scratch._9c, 1);
+		kernel_synch(KERNEL_ANIM, scratch._9c, KERNEL_NOW, 0);
+		player.walker_visible = -1;
+		global[g133] = 0;
+		global[g143] = 0;
+		kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
+		player.commands_allowed = -1;
+		scratch._a0 = -1;
+		break;
+
+	case 101:
+		kernel_abort_animation(aa[scratch._94]);
+		aainfo[scratch._94]._val1 = 0;
+		kernel_reset_animation(scratch._9c, 1);
+		kernel_synch(KERNEL_ANIM, scratch._9c, KERNEL_NOW, 0);
+		global[g143] = 0;
+		player.commands_allowed = -1;
+		break;
+
+	case 102:
+		kernel_abort_animation(aa[scratch._96]);
+		aainfo[scratch._96]._val1 = 0;
+		kernel_reset_animation(scratch._9a, 1);
+		kernel_synch(KERNEL_ANIM, scratch._9a, KERNEL_NOW, 0);
+		global[g133] = 0;
+		player.commands_allowed = -1;
+		break;
+	}
+
+	if (flags[4] != -3 && flags[4] != 5) {
+		global_anim1(4, scratch._9a, global[g131], &global[g132]);
+		global_anim2(4, scratch._9c, global[g141], &global[g142]);
+	}
+
+	if (aainfo[0]._val1 != 0) room_104_anim6();
+	if (aainfo[2]._val1 != 0) room_104_anim7();
+	if (aainfo[1]._val1 != 0) room_104_anim8();
+	if (aainfo[4]._val1 != 0) room_104_anim4();
+	if (aainfo[5]._val1 != 0) room_104_anim5();
+	if (aainfo[6]._val1 != 0) room_104_anim1();
+	if (aainfo[7]._val1 != 0) room_104_anim2();
+	if (aainfo[8]._val1 != 0) room_104_anim3();
 }
 
 static void room_104_pre_parser() {
+	if (player_parse(13, 15, 0)) {
+		global[g009] = 0;
+		midi_stop();
+		player.walk_off_edge_to_room = 101;
+	}
 }
 
 static void room_104_parser() {
+	if (global[walker_converse_state] != 0) {
+		player.commands_allowed = 0;
+		digi_play_build_ii('c', 1, 1);
+		goto handled;
+	}
+
+	if (global[player_selected_object] >= 0) {
+		goto handled;
+	}
+
+	if (player_parse(114, 116, 0)) {
+		player.commands_allowed = 0;
+		global[g145] = -1;
+		scratch._8e = 2;
+		goto handled;
+	}
+
+	if (player_parse(126, 99, 0)) {
+		player.commands_allowed = 0;
+		global[g135] = -1;
+		scratch._8c = 3;
+		goto handled;
+	}
+
+	if (player_parse(78, 119, 0)) {
+		player.commands_allowed = 0;
+		global[g135] = -1;
+		scratch._8c = 3;
+		goto handled;
+	}
+
+	goto done;
+handled:
+	player.command_ready = false;
+done:
+	;
 }
 
 void room_104_synchronize(Common::Serializer &s) {
-	for (int16 &v : scratch.sprite)    s.syncAsSint16LE(v);
-	for (int16 &v : scratch.sequence)  s.syncAsSint16LE(v);
-	for (int16 &v : scratch.animation) s.syncAsSint16LE(v);
+	for (int16 &v : scratch.sprite)         s.syncAsSint16LE(v);
+	for (int16 &v : scratch.sequence)       s.syncAsSint16LE(v);
+	for (int16 &v : scratch.animation)      s.syncAsSint16LE(v);
+	for (AnimationInfo &ai : scratch.animation_info) ai.synchronize(s);
+	s.syncAsSint16LE(scratch._8c);
+	s.syncAsSint16LE(scratch._8e);
+	s.syncAsSint16LE(scratch._90);
+	s.syncAsSint16LE(scratch._92);
+	s.syncAsSint16LE(scratch._94);
+	s.syncAsSint16LE(scratch._96);
+	s.syncAsSint16LE(scratch._98);
+	s.syncAsSint16LE(scratch._9a);
+	s.syncAsSint16LE(scratch._9c);
+	s.syncAsSint16LE(scratch._9e);
+	s.syncAsSint16LE(scratch._a0);
+	s.syncAsSint16LE(scratch._a2);
+	s.syncAsSint16LE(scratch._a4);
 }
 
 void room_104_preload() {
-	room_init_code_pointer = room_104_init;
+	room_init_code_pointer       = room_104_init;
 	room_pre_parser_code_pointer = room_104_pre_parser;
-	room_parser_code_pointer = room_104_parser;
-	room_daemon_code_pointer = room_104_daemon;
+	room_parser_code_pointer     = room_104_parser;
+	room_daemon_code_pointer     = room_104_daemon;
 
+	global[g016] = (global[g101] || room_id == 103) ? -1 : 0;
 	section_1_walker();
 	section_1_interface();
+	player.walker_must_reload = true;
 }
 
 } // namespace Rooms



From noreply at scummvm.org  Fri Jun  5 09:58:57 2026
From: noreply at scummvm.org (dreammaster)
Date: Fri,  5 Jun 2026 09:58:57 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 3d2e748fd9d59839334f4e1f092e0db2bd668eae
Message-ID: <20260605095857.D9B837005BC@ron.scummvm.net>

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

Summary:
3d2e748fd9 MADS: FOREST: Impelemented room 106


Commit: 3d2e748fd9d59839334f4e1f092e0db2bd668eae
    https://github.com/scummvm/scummvm/commit/3d2e748fd9d59839334f4e1f092e0db2bd668eae
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-06-05T19:58:49+10:00

Commit Message:
MADS: FOREST: Impelemented room 106

Changed paths:
    engines/mads/madsv2/core/digi.cpp
    engines/mads/madsv2/core/digi.h
    engines/mads/madsv2/forest/rooms/room106.cpp


diff --git a/engines/mads/madsv2/core/digi.cpp b/engines/mads/madsv2/core/digi.cpp
index bc683eb315b..a5ede99202d 100644
--- a/engines/mads/madsv2/core/digi.cpp
+++ b/engines/mads/madsv2/core/digi.cpp
@@ -26,6 +26,7 @@ namespace MADS {
 namespace MADSV2 {
 
 bool digi_flag1;
+int digi_val1;
 
 void digi_install() {
 	digi_flag1 = false;
diff --git a/engines/mads/madsv2/core/digi.h b/engines/mads/madsv2/core/digi.h
index e8795ec3db6..8e11848d5ef 100644
--- a/engines/mads/madsv2/core/digi.h
+++ b/engines/mads/madsv2/core/digi.h
@@ -28,6 +28,7 @@ namespace MADS {
 namespace MADSV2 {
 
 extern bool digi_flag1;
+extern int digi_val1;
 
 extern void digi_install();
 extern void digi_play(const char *name, int slot);
diff --git a/engines/mads/madsv2/forest/rooms/room106.cpp b/engines/mads/madsv2/forest/rooms/room106.cpp
index 46646593123..27b9ff43c0c 100644
--- a/engines/mads/madsv2/forest/rooms/room106.cpp
+++ b/engines/mads/madsv2/forest/rooms/room106.cpp
@@ -20,16 +20,22 @@
  */
 
 #include "mads/madsv2/core/conv.h"
+#include "mads/madsv2/core/digi.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/imath.h"
 #include "mads/madsv2/core/inter.h"
 #include "mads/madsv2/core/kernel.h"
+#include "mads/madsv2/core/matte.h"
+#include "mads/madsv2/core/midi.h"
+#include "mads/madsv2/core/object.h"
+#include "mads/madsv2/core/player.h"
 #include "mads/madsv2/core/sound.h"
 #include "mads/madsv2/core/text.h"
 #include "mads/madsv2/forest/mads/inventory.h"
 #include "mads/madsv2/forest/mads/sounds.h"
 #include "mads/madsv2/forest/mads/words.h"
 #include "mads/madsv2/forest/global.h"
+#include "mads/madsv2/forest/journal.h"
 #include "mads/madsv2/forest/rooms/section1.h"
 #include "mads/madsv2/forest/rooms/room106.h"
 
@@ -39,14 +45,28 @@ namespace Forest {
 namespace Rooms {
 
 struct Scratch {
-	int16 sprite[15];       /* Sprite series handles */
-	int16 sequence[15];     /* Sequence handles      */
-	int16 animation[4];     /* Animation handles     */
-
-	int16 dragon_frame;     /* frame animation is on */
-
-	int16 done_with_conv;   /* T if done with conv   */
-	int16 prev_room;
+	int16 sprite[10];            /* 0x00 ??? sprite series handles  */
+	int16 sequence[10];          /* 0x14 ??? sequence handles       */
+	int16 animation[10];         /* 0x28 ??? animation handles      */
+	AnimationInfo animation_info[10]; /* 0x3C */
+	int16 _8c;
+	int16 _8e;
+	int16 _90;
+	int16 _92;
+	int16 _94;
+	int16 _96;
+	int16 _98;
+	int16 _9a;
+	int16 _9c;
+	int16 _9e;
+	int16 _a0;
+	int16 _a2;
+	int16 _a4;
+	int16 _a6;
+	int16 _a8;
+	int16 _aa;                   /* 0xAA ??? initialised to 0      */
+	int16 _ac;
+	int16 _ae;                   /* 0xAE ??? initialised to -1     */
 };
 
 static Scratch scratch;
@@ -55,24 +75,741 @@ static Scratch scratch;
 #define ss    local->sprite
 #define seq   local->sequence
 #define aa    local->animation
+#define aainfo scratch.animation_info
 
+static void room_106_init1();
+static void room_106_init2();
+static void room_106_anim1();
+static void room_106_anim2();
+static void room_106_anim3();
+static void room_106_anim4();
+static void room_106_anim5();
+static void room_106_anim6();
+static void room_106_anim7();
+static void room_106_anim8();
+static void room_106_anim9();
+static void room_106_anim10();
 
 static void room_106_init() {
+	scratch._aa = 0;
+	scratch._ae = -1;
+
+	if (global[g022] != 0)
+		kernel_flip_hotspot(84, 0);
+
+	if (object_is_here(5)) {
+		ss[0] = kernel_load_series(kernel_name('p', 2), 0);
+		seq[0] = kernel_seq_stamp(ss[0], false, KERNEL_FIRST);
+		kernel_seq_depth(seq[0], 10);
+		kernel_seq_loc(seq[0], 235, 99);
+		kernel_seq_scale(seq[0], 58);
+	} else {
+		kernel_flip_hotspot(125, 0);
+	}
+
+	if (previous_room != KERNEL_RESTORING_GAME) {
+		if (previous_room != 199) {
+			player.walker_visible = 0;
+			player.commands_allowed = 0;
+		}
+
+		for (int16 i = 0; i < 10; i++) {
+			aainfo[i]._val1 = 0;
+			aainfo[i]._val2 = 1;
+			aainfo[i]._val3 = 0;
+			aainfo[i]._val4 = 0;
+		}
+
+		if (previous_room != 199 && flags[3] != 3)
+			flags[3]++;
+	}
+
+	switch (flags[3]) {
+	case -3:
+		room_106_init1();
+		break;
+	case -2:
+		flags[3] = 1;
+		room_106_init2();
+		break;
+	default:
+		room_106_init2();
+		break;
+	}
+}
+
+static void room_106_init1() {
+	global[player_score] = -1;
+	global[g009] = -1;
+	viewing_at_y = 22;
+	player.walker_visible = 0;
+	player.commands_allowed = 0;
+
+	for (int16 i = 0; i < 10; i++) {
+		aainfo[i]._val1 = 0;
+		aainfo[i]._val2 = -1;
+	}
+
+	aa[9] = kernel_run_animation(kernel_name('I', 1), 0);
+	aainfo[9]._val1 = -1;
+}
+
+static void room_106_init2() {
+	midi_stop();
+	global[player_score] = -1;
+	global[g131] = 0;
+	global[g141] = 0;
+
+	scratch._9c = kernel_run_animation_disp('r', 1, 0);
+	kernel_position_anim(scratch._9c, 242, 84, 52, 11);
+	scratch._9a = kernel_run_animation_disp('e', 2, 0);
+	kernel_position_anim(scratch._9a, 277, 92, 55, 9);
+
+	if (previous_room != KERNEL_LAST) {
+		player.x = 259;
+		player.y = 98;
+		player.facing = 3;
+	}
+
+	switch (previous_room) {
+	case KERNEL_LAST:
+		global[g131] = -1;
+		global[g141] = -1;
+		kernel_reset_animation(scratch._9a, 2);
+		kernel_reset_animation(scratch._9c, 2);
+		global[g133] = 0;
+		global[g143] = 0;
+		return;
+	case 101:
+		aa[0] = kernel_run_animation(kernel_name('y', 1), 100);
+		aainfo[0]._val1 = -1;
+		scratch._92 = 95;
+		return;
+	case 199:
+		global[g131] = -1;
+		global[g141] = -1;
+		kernel_reset_animation(scratch._9a, 2);
+		kernel_reset_animation(scratch._9c, 2);
+		global[g133] = 0;
+		global[g143] = 0;
+		restore_player();
+		player.commands_allowed = -1;
+		player.walker_visible = -1;
+		return;
+	case 201:
+		aa[0] = kernel_run_animation(kernel_name('y', 2), 100);
+		aainfo[0]._val1 = -1;
+		scratch._92 = 44;
+		return;
+	case 210:
+		aa[1] = kernel_run_animation("*RM106Y31", 0);
+		aainfo[1]._val1 = -1;
+		scratch._92 = 54;
+		return;
+	default:
+		global[g131] = -1;
+		global[g141] = -1;
+		kernel_reset_animation(scratch._9a, 2);
+		kernel_reset_animation(scratch._9c, 2);
+		global[g133] = 0;
+		global[g143] = 0;
+		player.commands_allowed = -1;
+		player.walker_visible = -1;
+		return;
+	}
+}
+
+static void room_106_anim1() {
+	int16 frame = kernel_anim[aa[9]].frame;
+	if (frame != aainfo[9]._val2) {
+		aainfo[9]._val2 = frame;
+		if (frame == 121) {
+			new_room = 103;
+		} else if (frame < 121) {
+			if (frame == 61) {
+				digi_play_build(106, 'r', 1, 1);
+				scratch._ae = 61;
+			} else if (frame < 61) {
+				if (frame == 1) {
+					kernel_timing_trigger(1, 109);
+				} else if (frame == 4) {
+					digi_play_build(106, 'm', 1, 1);
+					scratch._ae = 4;
+				} else if (frame == 54) {
+					digi_play_build(101, '_', 4, 1);
+					scratch._ae = 54;
+				}
+			} else { // 62..120
+				if (frame == 75) {
+					digi_stop(3);
+				} else if (frame == 83) {
+					digi_play_build(106, 'm', 2, 1);
+					scratch._ae = 83;
+				} else if (frame == 114) {
+					digi_play_build(106, 'm', 3, 1);
+					scratch._ae = 114;
+				}
+			}
+		}
+	}
+
+	if (kernel.trigger != 7 && kernel.trigger != 28)
+		return;
+	if (digi_val1 != 0)
+		kernel_timing_trigger(1, 109);
+	if (scratch._ae == 114) { scratch._ae = -1; return; }
+	if (scratch._ae > 114)  return;
+	if (scratch._ae == 4 || scratch._ae == 54 || scratch._ae == 61 || scratch._ae == 83)
+		scratch._ae = -1;
+}
+
+static void room_106_anim2() {
+	int16 frame = kernel_anim[aa[6]].frame;
+	if (frame == aainfo[6]._val2)
+		return;
+	aainfo[6]._val2 = frame;
+	if (scratch._a2 == 1) {
+		if (frame == 81) {
+			digi_play_build_ii('e', 5, 1);
+			scratch._ae = 81;
+		}
+	} else if (scratch._a2 == 2) {
+		if (frame == 75) {
+			digi_play_build_ii('e', 1, 1);
+			scratch._ae = 75;
+		}
+	}
+}
+
+static void room_106_anim3() {
+	int16 frame = kernel_anim[aa[0]].frame;
+	if (frame != aainfo[0]._val2)
+		aainfo[0]._val2 = frame;
+	if (global[player_hyperwalked] == -1) {
+		aainfo[0]._val2 = scratch._92 - 1;
+		kernel_reset_animation(aa[0], aainfo[0]._val2);
+	}
+}
+
+static void room_106_anim4() {
+	int16 frame = kernel_anim[aa[7]].frame;
+	if (frame != aainfo[7]._val2) {
+		aainfo[7]._val2 = frame;
+		if (frame == 53) {
+			if (aainfo[7]._val3 == 10) {
+				aainfo[7]._val2 = 48;
+				kernel_reset_animation(aa[7], 48);
+			}
+		} else if (frame < 53) {
+			if (frame == 7) {
+				if (aainfo[7]._val3 == 13) {
+					aainfo[7]._val2 = 5;
+					kernel_reset_animation(aa[7], 5);
+				}
+			} else if (frame < 7) {
+				if (frame == 1) {
+					aainfo[7]._val3 = 10;
+					digi_play_build(106, 'b', 1, 1);
+					scratch._ae = 101;
+				} else if (frame == 4) {
+					if (aainfo[7]._val3 == 10) {
+						aainfo[7]._val2 = 2;
+						kernel_reset_animation(aa[7], 2);
+					}
+				}
+			} else { // 8..52
+				if (frame == 12) {
+					if (aainfo[7]._val3 == 11) {
+						aainfo[7]._val2 = 8;
+						kernel_reset_animation(aa[7], 8);
+					}
+				} else if (frame == 19) {
+					aainfo[7]._val3 = 10;
+					global[g154] = 2;
+					digi_play_build(106, 'b', 2, 1);
+					scratch._ae = 105;
+					aainfo[7]._val2 = 2;
+					kernel_reset_animation(aa[7], 2);
+				}
+			}
+		}
+	}
+
+	if (kernel.trigger != 7 && kernel.trigger != 28)
+		return;
+
+	switch (scratch._ae) {
+	case 101:
+		global[g154] = 0;
+		kernel_timing_trigger(30, 28);
+		aainfo[7]._val3 = 13;
+		aainfo[7]._val2 = 5;
+		kernel_reset_animation(aa[7], 5);
+		scratch._ae = 102;
+		return;
+	case 102:
+		global[g156] = 2;
+		aainfo[7]._val3 = 11;
+		aainfo[7]._val2 = 8;
+		kernel_reset_animation(aa[7], 8);
+		digi_play_build(106, 'e', 1, 1);
+		scratch._ae = 103;
+		return;
+	case 103:
+		global[g156] = 0;
+		aainfo[7]._val3 = 13;
+		aainfo[7]._val2 = 13;
+		kernel_reset_animation(aa[7], 13);
+		return;
+	case 105:
+		global[g009] = 0;
+		global_digi_play(4);
+		global[g154] = 0;
+		aainfo[7]._val3 = 13;
+		aainfo[7]._val2 = 20;
+		kernel_reset_animation(aa[7], 20);
+		return;
+	}
+}
+
+static void room_106_anim5() {
+	int16 frame = kernel_anim[aa[8]].frame;
+	if (frame != aainfo[8]._val2) {
+		aainfo[8]._val2 = frame;
+		if (scratch._ac == 2) {
+			if (frame == 44) {
+				aainfo[8]._val3 = 11;
+				digi_play_build(106, 'e', 2, 1);
+				scratch._ae = 501;
+			} else if (frame == 49) {
+				if (aainfo[8]._val3 == 11) {
+					aainfo[8]._val2 = 47;
+					kernel_reset_animation(aa[8], 47);
+				}
+			}
+		}
+	}
+	if (kernel.trigger != 7 && kernel.trigger != 28)
+		return;
+	if (scratch._ae == 501) {
+		global[g156] = 0;
+		aainfo[8]._val3 = 13;
+		aainfo[8]._val2 = 51;
+		kernel_reset_animation(aa[8], 51);
+	}
+}
+
+static void room_106_anim6() {
+	int16 frame = kernel_anim[aa[1]].frame;
+	if (frame != aainfo[1]._val2)
+		aainfo[1]._val2 = frame;
+
+	if (global[player_hyperwalked] != -1 && aainfo[1]._val2 != scratch._92)
+		return;
+
+	kernel_abort_animation(aa[1]);
+	aainfo[1]._val1 = 0;
+	aa[2] = kernel_run_animation("*RM106Y32", 100);
+	aainfo[2]._val1 = -1;
+	scratch._92 = 134;
+	aainfo[2]._val2 = (global[player_hyperwalked] == -1) ? 133 : 54;
+	kernel_reset_animation(aa[2], aainfo[2]._val2);
+}
+
+static void room_106_anim7() {
+	int16 frame = kernel_anim[aa[2]].frame;
+	if (frame != aainfo[2]._val2)
+		aainfo[2]._val2 = frame;
+	if (global[player_hyperwalked] == -1) {
+		aainfo[2]._val2 = scratch._92 - 1;
+		kernel_reset_animation(aa[2], aainfo[2]._val2);
+	}
+}
+
+static void room_106_anim8() {
+	int16 frame = kernel_anim[aa[3]].frame;
+	if (frame != aainfo[3]._val2)
+		aainfo[3]._val2 = frame;
+	if (global[player_hyperwalked] == -1) {
+		aainfo[3]._val2 = scratch._92 - 1;
+		kernel_reset_animation(aa[3], aainfo[3]._val2);
+	}
+}
+
+static void room_106_anim9() {
+	int16 frame = kernel_anim[aa[5]].frame;
+	if (frame == aainfo[5]._val2)
+		return;
+	aainfo[5]._val2 = frame;
+	if (frame == 13)
+		digi_play_build(101, '_', 1, 2);
+}
+
+static void room_106_anim10() {
+	int16 frame = kernel_anim[aa[4]].frame;
+	if (frame != aainfo[4]._val2)
+		aainfo[4]._val2 = frame;
+
+	if (global[player_hyperwalked] == -1 && frame <= 23) {
+		aainfo[4]._val2 = 23;
+		kernel_reset_animation(aa[4], 23);
+		kernel_synch(KERNEL_ANIM, aa[4], KERNEL_NOW, 0);
+		return;
+	}
+	if (global[player_hyperwalked] == -1 || frame == 45) {
+		aainfo[4]._val2 = 44;
+		kernel_reset_animation(aa[4], 44);
+		kernel_synch(KERNEL_ANIM, aa[4], KERNEL_NOW, 0);
+	}
 }
 
 static void room_106_daemon() {
+	if (flags[0] < 0 && global[player_hyperwalked] == -1) {
+		flags[0] = flags[1] = flags[2] = flags[3] = 0;
+		new_room = 904;
+	}
+
+	switch (kernel.trigger) {
+	case 7:
+		if (global[walker_converse_state] != 0) {
+			global[walker_converse_state] = 0;
+			close_journal(3);
+			player.commands_allowed = -1;
+		} else if (scratch._ae == 75) {
+			kernel_timing_trigger(30, 106);
+		} else if (scratch._ae == 81) {
+			kernel_timing_trigger(30, 105);
+		} else if (scratch._ae == 89) {
+			kernel_abort_animation(scratch._a6);
+			scratch._ae = -1;
+			global[g154] = 0;
+			scratch._aa = 0;
+			kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
+			player.walker_visible = -1;
+			global[g150] = -1;
+			scratch._90 = 8;
+		} else if (scratch._ae == 99) {
+			kernel_abort_animation(scratch._a6);
+			scratch._ae = -1;
+			scratch._aa = 0;
+			kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
+			player.walker_visible = -1;
+			global[g150] = -1;
+			scratch._90 = 7;
+		} else if (scratch._ae == 1000) {
+			global[g156] = 0;
+			kernel_seq_delete(seq[0]);
+			kernel_reset_animation(scratch._9c, 0);
+			global[g143] = 1;
+			kernel_synch(KERNEL_ANIM, scratch._9c, KERNEL_NOW, 0);
+			aa[5] = kernel_run_animation(kernel_name('R', 1), 107);
+			aainfo[5]._val1 = -1;
+			kernel_synch(KERNEL_ANIM, aa[5], KERNEL_NOW, 0);
+		}
+		break;
+	case 24:
+		if (scratch._a2 == 1)
+			aa[6] = kernel_run_animation(kernel_name('E', 1), 102);
+		else if (scratch._a2 == 2)
+			aa[6] = kernel_run_animation(kernel_name('M', 1), 102);
+		else
+			break;
+		aainfo[6]._val1 = -1;
+		kernel_reset_animation(scratch._9a, 0);
+		kernel_synch(KERNEL_ANIM, aa[6], KERNEL_ANIM, scratch._9a);
+		global[g133] = 1;
+		break;
+	case 25:
+		if (scratch._8e == 2) {
+			aa[5] = kernel_run_animation(kernel_name('R', 1), 101);
+			aainfo[5]._val1 = -1;
+			scratch._94 = 5;
+			kernel_reset_animation(scratch._9c, 0);
+			kernel_synch(KERNEL_ANIM, aa[5], KERNEL_ANIM, scratch._9c);
+			global[g143] = 1;
+		}
+		break;
+	case 26:
+		if (scratch._90 == 6) {
+			kernel_reset_animation(scratch._9a, 0);
+			kernel_reset_animation(scratch._9c, 0);
+			global[g133] = 1;
+			global[g143] = 1;
+			aa[3] = kernel_run_animation(kernel_name('Z', 3), 103);
+			aainfo[3]._val1 = -1;
+			scratch._98 = 3;
+			scratch._92 = 101;
+			kernel_synch(KERNEL_ANIM, scratch._9a, KERNEL_ANIM, aa[3]);
+		} else if (scratch._90 == 7) {
+			kernel_reset_animation(scratch._9a, 0);
+			global[g133] = 1;
+			scratch._a8 = kernel_run_animation_talk('e', 4, 0);
+			kernel_position_anim(scratch._a8, 277, 92, 55, 9);
+			global[g156] = 2;
+			kernel_synch(KERNEL_ANIM, scratch._a8, KERNEL_ANIM, scratch._9a);
+			digi_play_build(106, 'E', 3, 1);
+			scratch._ae = 1000;
+			scratch._aa = -1;
+		} else if (scratch._90 == 8) {
+			kernel_reset_animation(scratch._9a, 0);
+			kernel_reset_animation(scratch._9c, 0);
+			global[g133] = 1;
+			global[g143] = 1;
+			aa[8] = kernel_run_animation(kernel_name('x', 1), 108);
+			aainfo[8]._val1 = -1;
+			scratch._ac = 1;
+			kernel_synch(KERNEL_ANIM, scratch._9a, KERNEL_ANIM, aa[8]);
+		}
+		break;
+	case 100:
+		kernel_abort_animation(aa[0]);
+		aainfo[0]._val1 = 0;
+		if (flags[3] == 1) {
+			aa[7] = kernel_run_animation(kernel_name('t', 1), 104);
+			aainfo[7]._val1 = -1;
+		} else {
+			global[g131] = -1;
+			global[g141] = -1;
+			kernel_reset_animation(scratch._9a, 1);
+			kernel_synch(KERNEL_ANIM, scratch._9a, KERNEL_NOW, 0);
+			kernel_reset_animation(scratch._9c, 1);
+			kernel_synch(KERNEL_ANIM, scratch._9c, KERNEL_NOW, 0);
+			player.walker_visible = -1;
+			global[g133] = 0;
+			global[g143] = 0;
+			kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
+			player.commands_allowed = -1;
+		}
+		break;
+	case 101:
+		kernel_abort_animation(aa[scratch._94]);
+		aainfo[scratch._94]._val1 = 0;
+		kernel_reset_animation(scratch._9c, 1);
+		kernel_synch(KERNEL_ANIM, scratch._9c, KERNEL_NOW, 0);
+		global[g143] = 0;
+		player.commands_allowed = -1;
+		break;
+	case 102:
+		kernel_abort_animation(aa[6]);
+		aainfo[6]._val1 = 0;
+		kernel_reset_animation(scratch._9a, 1);
+		kernel_synch(KERNEL_ANIM, scratch._9a, KERNEL_NOW, 0);
+		global[g133] = 0;
+		player.commands_allowed = -1;
+		break;
+	case 103:
+		if (scratch._90 == 6) {
+			global[g021] = -1;
+			new_room = 210;
+		}
+		break;
+	case 104:
+		kernel_abort_animation(aa[7]);
+		aainfo[7]._val1 = 0;
+		global[g131] = -1;
+		global[g141] = -1;
+		kernel_reset_animation(scratch._9a, 1);
+		kernel_synch(KERNEL_ANIM, scratch._9a, KERNEL_NOW, 0);
+		kernel_reset_animation(scratch._9c, 1);
+		kernel_synch(KERNEL_ANIM, scratch._9c, KERNEL_NOW, 0);
+		player.walker_visible = -1;
+		global[g133] = 0;
+		global[g143] = 0;
+		kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
+		player.commands_allowed = -1;
+		break;
+	case 105:
+	case 106:
+		digi_play_build_ii('e', 10, 1);
+		scratch._ae = -1;
+		break;
+	case 107:
+		kernel_abort_animation(aa[5]);
+		aainfo[5]._val1 = 0;
+		kernel_reset_animation(scratch._9c, 1);
+		kernel_synch(KERNEL_ANIM, scratch._9c, KERNEL_NOW, 0);
+		global[g143] = 0;
+		kernel_abort_animation(scratch._a8);
+		scratch._aa = 0;
+		kernel_reset_animation(scratch._9a, 1);
+		kernel_synch(KERNEL_ANIM, scratch._9a, KERNEL_NOW, 0);
+		global[g133] = 0;
+		kernel_flip_hotspot(125, 0);
+		inter_move_object(5, 2);
+		player.commands_allowed = -1;
+		break;
+	case 108:
+		if (scratch._ac == 1) {
+			kernel_abort_animation(aa[8]);
+			aa[8] = kernel_run_animation(kernel_name('x', 2), 108);
+			kernel_synch(KERNEL_ANIM, scratch._9a, KERNEL_ANIM, aa[8]);
+			scratch._ac = 2;
+		} else if (scratch._ac == 2) {
+			kernel_abort_animation(aa[8]);
+			aa[8] = kernel_run_animation(kernel_name('x', 3), 108);
+			kernel_synch(KERNEL_ANIM, scratch._9a, KERNEL_ANIM, aa[8]);
+			scratch._ac = 3;
+		} else if (scratch._ac == 3) {
+			kernel_abort_animation(aa[8]);
+			aainfo[8]._val1 = 0;
+			global[g131] = -1;
+			global[g141] = -1;
+			kernel_reset_animation(scratch._9a, 1);
+			kernel_synch(KERNEL_ANIM, scratch._9a, KERNEL_NOW, 0);
+			kernel_reset_animation(scratch._9c, 1);
+			kernel_synch(KERNEL_ANIM, scratch._9c, KERNEL_NOW, 0);
+			player.commands_allowed = -1;
+			global[g133] = 0;
+			global[g143] = 0;
+			scratch._ae = 0;
+			scratch._ac = 0;
+		}
+		break;
+	case 109:
+		digi_initial_volume(50);
+		digi_play_build(101, '_', 5, 3);
+		digi_flag1 = true;
+		break;
+	}
+
+	if (flags[3] != -3) {
+		global_anim1(2, scratch._9a, global[g131], &global[g132]);
+		global_anim2(1, scratch._9c, global[g141], &global[g142]);
+	}
+	if (aainfo[0]._val1 != 0)  room_106_anim3();
+	if (aainfo[1]._val1 != 0)  room_106_anim6();
+	if (aainfo[2]._val1 != 0)  room_106_anim7();
+	if (aainfo[3]._val1 != 0)  room_106_anim8();
+	if (aainfo[5]._val1 != 0)  room_106_anim9();
+	if (aainfo[4]._val1 != 0)  room_106_anim10();
+	if (aainfo[6]._val1 != 0)  room_106_anim2();
+	if (aainfo[9]._val1 != 0)  room_106_anim1();
+	if (aainfo[7]._val1 != 0)  room_106_anim4();
+	if (aainfo[8]._val1 != 0)  room_106_anim5();
+
+	if (scratch._aa != 0) {
+		kernel_random_frame(scratch._a6, &global[g151], global[g154]);
+		if (scratch._ae != 99 && scratch._ae != 89) {
+			kernel_random_frame(scratch._a4, &global[g152], global[g155]);
+			kernel_random_frame(scratch._a8, &global[g153], global[g156]);
+		}
+	}
 }
 
 static void room_106_pre_parser() {
+	if (player_parse(13, 21, 0))
+		player.walk_off_edge_to_room = 201;
 }
 
 static void room_106_parser() {
+	if (global[walker_converse_state] != 0) {
+		player.commands_allowed = 0;
+		digi_play_build_ii('c', 1, 1);
+		player.command_ready = 0;
+		return;
+	}
+
+	if (player_parse(118, 0)) {
+		global[g135] = -1;
+		scratch._a2 = 2;
+		player.commands_allowed = 0;
+	}
+
+	if (player_parse(131, 0)) {
+		global[g135] = -1;
+		scratch._a2 = 1;
+		player.commands_allowed = 0;
+	}
+
+	if (player_parse(125, 0)) {
+		global[g154] = 2;
+		player.commands_allowed = 0;
+		player.walker_visible = 0;
+		scratch._a6 = kernel_run_animation_talk('b', 3, 0);
+		kernel_position_anim(scratch._a6, player.x, player.y, player.scale, player.depth);
+		kernel_synch(KERNEL_ANIM, scratch._a6, KERNEL_PLAYER, 0);
+		digi_play_build_ii('b', 3, 1);
+		scratch._ae = 99;
+		scratch._aa = -1;
+		player.command_ready = 0;
+		return;
+	}
+
+	if (player_parse(13, 15, 0)) {
+		new_room = 101;
+		player.command_ready = 0;
+		return;
+	}
+
+	if (player_parse(27, 84, 0) && global[g022] == 0) {
+		global[g154] = 2;
+		player.commands_allowed = 0;
+		player.walker_visible = 0;
+		scratch._a6 = kernel_run_animation_talk('b', 9, 0);
+		kernel_position_anim(scratch._a6, player.x, player.y, player.scale, player.depth);
+		kernel_synch(KERNEL_ANIM, scratch._a6, KERNEL_PLAYER, 0);
+		digi_play_build_ii('b', 1, 1);
+		scratch._ae = 89;
+		scratch._aa = -1;
+		kernel_flip_hotspot(84, 0);
+		global[g022] = -1;
+		player.command_ready = 0;
+		return;
+	}
+
+	if (player_parse(13, 27, 0)) {
+		global[g150] = -1;
+		scratch._90 = 6;
+		player.commands_allowed = 0;
+		player.command_ready = 0;
+		return;
+	}
+
+	if (global[player_selected_object] >= 0) {
+		player.command_ready = 0;
+		return;
+	}
+
+	if (player_parse(114, 116, 0)) {
+		player.commands_allowed = 0;
+		global[g145] = -1;
+		scratch._8e = 2;
+		player.command_ready = 0;
+		return;
+	}
+
+	if (player_parse(126, 99, 0) || player_parse(78, 119, 0)) {
+		player.commands_allowed = 0;
+		global[g135] = -1;
+		scratch._8c = 3;
+		player.command_ready = 0;
+		return;
+	}
 }
 
 void room_106_synchronize(Common::Serializer &s) {
-	for (int16 &v : scratch.sprite)    s.syncAsSint16LE(v);
-	for (int16 &v : scratch.sequence)  s.syncAsSint16LE(v);
-	for (int16 &v : scratch.animation) s.syncAsSint16LE(v);
+	for (int16 &v : scratch.sprite)         s.syncAsSint16LE(v);
+	for (int16 &v : scratch.sequence)       s.syncAsSint16LE(v);
+	for (int16 &v : scratch.animation)      s.syncAsSint16LE(v);
+	for (AnimationInfo &ai : scratch.animation_info) ai.synchronize(s);
+	s.syncAsSint16LE(scratch._8c);
+	s.syncAsSint16LE(scratch._8e);
+	s.syncAsSint16LE(scratch._90);
+	s.syncAsSint16LE(scratch._92);
+	s.syncAsSint16LE(scratch._94);
+	s.syncAsSint16LE(scratch._96);
+	s.syncAsSint16LE(scratch._98);
+	s.syncAsSint16LE(scratch._9a);
+	s.syncAsSint16LE(scratch._9c);
+	s.syncAsSint16LE(scratch._9e);
+	s.syncAsSint16LE(scratch._a0);
+	s.syncAsSint16LE(scratch._a2);
+	s.syncAsSint16LE(scratch._a4);
+	s.syncAsSint16LE(scratch._a6);
+	s.syncAsSint16LE(scratch._a8);
+	s.syncAsSint16LE(scratch._aa);
+	s.syncAsSint16LE(scratch._ac);
+	s.syncAsSint16LE(scratch._ae);
 }
 
 void room_106_preload() {
@@ -81,8 +818,12 @@ void room_106_preload() {
 	room_parser_code_pointer = room_106_parser;
 	room_daemon_code_pointer = room_106_daemon;
 
+	if (flags[3] == -4)
+		global[g016] = -1;
+
 	section_1_walker();
 	section_1_interface();
+	player.walker_must_reload = true;
 }
 
 } // namespace Rooms



From noreply at scummvm.org  Fri Jun  5 10:12:45 2026
From: noreply at scummvm.org (ScummVM git)
Date: Fri,  5 Jun 2026 10:12:45 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm-web
 dependabot/composer/patch-releases-d9a64d47d3 ->
 273df83806b7851ea67539f00f39ce4c5804391f
Message-ID: <20260605101245.E10CA7005BC@ron.scummvm.net>

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

Summary:
273df83806 Bump matomo/device-detector


Commit: 273df83806b7851ea67539f00f39ce4c5804391f
    https://github.com/scummvm/scummvm-web/commit/273df83806b7851ea67539f00f39ce4c5804391f
Author: dependabot[bot] (49699333+dependabot[bot]@users.noreply.github.com)
Date: 2026-06-05T10:12:41Z

Commit Message:
Bump matomo/device-detector

Bumps the patch-releases group with 1 update in the / directory: [matomo/device-detector](https://github.com/matomo-org/device-detector).


Updates `matomo/device-detector` from 6.5.0 to 6.5.1
- [Release notes](https://github.com/matomo-org/device-detector/releases)
- [Commits](https://github.com/matomo-org/device-detector/compare/6.5.0...6.5.1)

---
updated-dependencies:
- dependency-name: matomo/device-detector
  dependency-version: 6.5.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: patch-releases
...

Signed-off-by: dependabot[bot] <support at github.com>

Changed paths:
    composer.lock


diff --git a/composer.lock b/composer.lock
index e05e1cb7..9b69cd84 100644
--- a/composer.lock
+++ b/composer.lock
@@ -600,16 +600,16 @@
         },
         {
             "name": "matomo/device-detector",
-            "version": "6.5.0",
+            "version": "6.5.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/matomo-org/device-detector.git",
-                "reference": "e0fff2309dad83eb3cfb2564e524be715cfcf3cf"
+                "reference": "f30457500c6be4c80c8830466f8a746724779fd0"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/matomo-org/device-detector/zipball/e0fff2309dad83eb3cfb2564e524be715cfcf3cf",
-                "reference": "e0fff2309dad83eb3cfb2564e524be715cfcf3cf",
+                "url": "https://api.github.com/repos/matomo-org/device-detector/zipball/f30457500c6be4c80c8830466f8a746724779fd0",
+                "reference": "f30457500c6be4c80c8830466f8a746724779fd0",
                 "shasum": ""
             },
             "require": {
@@ -623,11 +623,11 @@
                 "matthiasmullie/scrapbook": "^1.4.7",
                 "mayflower/mo4-coding-standard": "^v9.0.0",
                 "phpstan/phpstan": "^1.10.44",
-                "phpunit/phpunit": "^8.5.2 || ^9 || ^10 || ^11 || ^12",
+                "phpunit/phpunit": "^8.5.2 || ^9 || ^10 || ^11 || ^12 || ^13",
                 "psr/cache": "^1.0.1 || ^2.0 || ^3.0",
                 "psr/simple-cache": "^1.0.1 || ^2.0 || ^3.0",
                 "slevomat/coding-standard": "<8.16.0",
-                "symfony/yaml": "^5.1.7"
+                "symfony/yaml": "^5.4 || ^6.4 || ^7.4 || ^8.0"
             },
             "suggest": {
                 "ext-yaml": "Necessary for using the Pecl YAML parser"
@@ -663,9 +663,9 @@
                 "forum": "https://forum.matomo.org/",
                 "issues": "https://github.com/matomo-org/device-detector/issues",
                 "source": "https://github.com/matomo-org/matomo",
-                "wiki": "https://dev.matomo.org/"
+                "wiki": "https://developer.matomo.org/"
             },
-            "time": "2026-01-21T07:53:39+00:00"
+            "time": "2026-05-27T09:53:26+00:00"
         },
         {
             "name": "mustangostang/spyc",
@@ -3031,5 +3031,5 @@
     "platform-overrides": {
         "php": "8.2.0"
     },
-    "plugin-api-version": "2.6.0"
+    "plugin-api-version": "2.9.0"
 }



From noreply at scummvm.org  Fri Jun  5 10:22:57 2026
From: noreply at scummvm.org (ScummVM-Translations)
Date: Fri,  5 Jun 2026 10:22:57 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 d9e60ffb26b53c34d14866d0cfc624545dfa61d5
Message-ID: <20260605102257.49EFC7005BC@ron.scummvm.net>

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

Summary:
d9e60ffb26 I18N: Update translation (Georgian)


Commit: d9e60ffb26b53c34d14866d0cfc624545dfa61d5
    https://github.com/scummvm/scummvm/commit/d9e60ffb26b53c34d14866d0cfc624545dfa61d5
Author: NorwayFun (temuri.doghonadze at gmail.com)
Date: 2026-06-05T10:22:49Z

Commit Message:
I18N: Update translation (Georgian)

Currently translated at 71.9% (2306 of 3204 strings)

Changed paths:
    po/ka.po


diff --git a/po/ka.po b/po/ka.po
index 7cc01fb514f..d7d680b6089 100644
--- a/po/ka.po
+++ b/po/ka.po
@@ -8,7 +8,7 @@ msgstr ""
 "Project-Id-Version: ScummVM 2.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
 "POT-Creation-Date: 2026-06-02 16:30+0000\n"
-"PO-Revision-Date: 2026-05-09 12:22+0000\n"
+"PO-Revision-Date: 2026-06-05 10:22+0000\n"
 "Last-Translator: NorwayFun <temuri.doghonadze at gmail.com>\n"
 "Language-Team: Georgian <https://translations.scummvm.org/projects/scummvm/"
 "scummvm/ka/>\n"
@@ -8285,10 +8285,8 @@ msgstr "????????????????????????????????? ???????????????"
 
 #. I18N: Toggles the red/blue stereoscopic 3D effect (anaglyph glasses).
 #: engines/freescape/movement.cpp:119
-#, fuzzy
-#| msgid "Toggle resizable window"
 msgid "Toggle red/blue 3D"
-msgstr "???????????????????????????????????? ????????????????????? ????????????????????????"
+msgstr "??????????????????/??????????????? 3D-?????? ????????????????????????"
 
 #: engines/freescape/games/castle/castle.cpp:463
 msgid "Select prince"
@@ -12419,20 +12417,16 @@ msgid "Enable ambience sounds."
 msgstr "????????????????????? ?????????????????? ??????????????????."
 
 #: engines/scumm/metaengine.cpp:882
-#, fuzzy
-#| msgid "Enable high resolution"
 msgid "High resolution mode"
-msgstr "?????????????????? ????????????????????????????????? ??????????????????"
+msgstr "?????????????????? ????????????????????????????????? ??????????????????"
 
 #: engines/scumm/metaengine.cpp:883
 msgid "Run the game in 640x400 high resolution mode instead of 320x200"
 msgstr ""
 
 #: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:901
-#, fuzzy
-#| msgid "All levels will be available to play."
 msgid "All levels will be available without requiring passwords"
-msgstr "??????????????????????????? ??????????????????????????????????????? ?????????????????? ??????????????? ????????????."
+msgstr "??????????????????????????? ??????????????????????????????????????? ?????????????????? ??????????????? ???????????? ????????????????????? ??????????????????????????? ??????????????????"
 
 #: engines/scumm/metaengine.cpp:1049
 msgid "Down left"
@@ -12480,46 +12474,32 @@ msgid "Aim right / menu right"
 msgstr ""
 
 #: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1229
-#, fuzzy
-#| msgid "Pick up"
 msgid "Stick up"
 msgstr "???????????????"
 
 #: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
-#, fuzzy
-#| msgid "Scroll down"
 msgid "Stick down"
-msgstr "??????????????? ????????????????????????"
+msgstr "????????????????????????"
 
 #: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1243
-#, fuzzy
-#| msgid "Slide left"
 msgid "Stick left"
-msgstr "?????????????????????????????? ????????????????????????"
+msgstr "??????????????????????????? ????????????????????????"
 
 #: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1250
-#, fuzzy
-#| msgid "Slide right"
 msgid "Stick right"
-msgstr "?????????????????????????????? ????????????????????????"
+msgstr "??????????????????????????? ????????????????????????"
 
 #: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1257
-#, fuzzy
-#| msgid "Move / Select"
 msgid "Fire / select"
-msgstr "???????????????????????????????????? / ????????????????????????"
+msgstr "?????????????????? / ????????????????????????"
 
 #: engines/scumm/metaengine.cpp:1186
-#, fuzzy
-#| msgid "Move back"
 msgid "Menu back"
-msgstr "???????????? ????????????????????????"
+msgstr "??????????????? ????????????"
 
 #: engines/scumm/metaengine.cpp:1191
-#, fuzzy
-#| msgid "Skip main menu"
 msgid "Skip / menu back"
-msgstr "????????????????????? ?????????????????? ??????????????????????????????"
+msgstr "?????????????????????????????? / ??????????????? ????????????"
 
 #: engines/scumm/metaengine.cpp:1207
 msgid "Rebel Assault II controls"



From noreply at scummvm.org  Fri Jun  5 10:52:03 2026
From: noreply at scummvm.org (kelmer44)
Date: Fri,  5 Jun 2026 10:52:03 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 0a5fc7038b1f3dbca276d17b400f323d5be2940a
Message-ID: <20260605105203.EDD507005BC@ron.scummvm.net>

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

Summary:
0a5fc7038b NEWS: Add entry for Alfred Pelrock


Commit: 0a5fc7038b1f3dbca276d17b400f323d5be2940a
    https://github.com/scummvm/scummvm/commit/0a5fc7038b1f3dbca276d17b400f323d5be2940a
Author: kelmer (kelmer at gmail.com)
Date: 2026-06-05T12:51:55+02:00

Commit Message:
NEWS: Add entry for Alfred Pelrock

Changed paths:
    NEWS.md


diff --git a/NEWS.md b/NEWS.md
index b4ce092bdd7..33396fcaf27 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -7,6 +7,7 @@ For a more comprehensive changelog of the latest experimental code, see:
    - Added support for Nancy Drew: The Haunted Carousel.
    - Added support for Nancy Drew: Danger on Deception Island.
    - Added support for Cartoon Carnival.
+   - Added support for Alfred Pelrock: En Busca de un Sue??o
 
  General:
    - Highlight clickable links in the GUI with a cursor change.



From noreply at scummvm.org  Fri Jun  5 10:54:57 2026
From: noreply at scummvm.org (dreammaster)
Date: Fri,  5 Jun 2026 10:54:57 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 f157eaacdc564b45043341feaa6c820636136409
Message-ID: <20260605105457.B27047005BC@ron.scummvm.net>

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:
c55ec411de MADS: FOREST: Implemented room 107
f157eaacdc MADS: FOREST: Implemented room 199


Commit: c55ec411deb5780469ebd6a7fbd991f7c61cc1eb
    https://github.com/scummvm/scummvm/commit/c55ec411deb5780469ebd6a7fbd991f7c61cc1eb
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-06-05T20:54:49+10:00

Commit Message:
MADS: FOREST: Implemented room 107

Changed paths:
    engines/mads/madsv2/forest/rooms/room107.cpp


diff --git a/engines/mads/madsv2/forest/rooms/room107.cpp b/engines/mads/madsv2/forest/rooms/room107.cpp
index 4e4075d4524..a8c4aa9a173 100644
--- a/engines/mads/madsv2/forest/rooms/room107.cpp
+++ b/engines/mads/madsv2/forest/rooms/room107.cpp
@@ -19,16 +19,12 @@
  *
  */
 
-#include "mads/madsv2/core/conv.h"
+#include "mads/madsv2/core/digi.h"
 #include "mads/madsv2/core/game.h"
-#include "mads/madsv2/core/imath.h"
-#include "mads/madsv2/core/inter.h"
 #include "mads/madsv2/core/kernel.h"
-#include "mads/madsv2/core/sound.h"
-#include "mads/madsv2/core/text.h"
-#include "mads/madsv2/forest/mads/inventory.h"
-#include "mads/madsv2/forest/mads/sounds.h"
-#include "mads/madsv2/forest/mads/words.h"
+#include "mads/madsv2/core/matte.h"
+#include "mads/madsv2/core/mouse.h"
+#include "mads/madsv2/core/player.h"
 #include "mads/madsv2/forest/global.h"
 #include "mads/madsv2/forest/rooms/section1.h"
 #include "mads/madsv2/forest/rooms/room107.h"
@@ -39,14 +35,12 @@ namespace Forest {
 namespace Rooms {
 
 struct Scratch {
-	int16 sprite[15];       /* Sprite series handles */
-	int16 sequence[15];     /* Sequence handles      */
-	int16 animation[4];     /* Animation handles     */
-
-	int16 dragon_frame;     /* frame animation is on */
-
-	int16 done_with_conv;   /* T if done with conv   */
-	int16 prev_room;
+	int16 sprite[10];            /* 0x00 ??? sprite series handles  */
+	int16 sequence[10];          /* 0x14 ??? sequence handles       */
+	int16 animation[10];         /* 0x28 ??? animation handles      */
+	AnimationInfo animation_info[10]; /* 0x3C */
+	int16 _8c;                        /* 0x8C */
+	int16 _8e;                        /* 0x8C */
 };
 
 static Scratch scratch;
@@ -55,12 +49,99 @@ static Scratch scratch;
 #define ss    local->sprite
 #define seq   local->sequence
 #define aa    local->animation
+#define aainfo scratch.animation_info
 
+static void room_107_anim1();
 
 static void room_107_init() {
+	flags[8] = 5;
+
+	ss[0] = kernel_load_series(kernel_name('b', 1), 0);
+	seq[0] = kernel_seq_stamp(ss[0], false, KERNEL_FIRST);
+	kernel_seq_depth(seq[0], 1);
+	kernel_seq_loc(seq[0], 274, 147);
+	kernel_seq_scale(seq[0], 100);
+
+	ss[1] = kernel_load_series(kernel_name('e', 1), 0);
+	seq[1] = kernel_seq_stamp(ss[1], false, KERNEL_FIRST);
+	kernel_seq_depth(seq[1], 1);
+	kernel_seq_loc(seq[1], 167, 115);
+	kernel_seq_scale(seq[1], 100);
+
+	ss[2] = kernel_load_series(kernel_name('r', 1), 0);
+	seq[2] = kernel_seq_stamp(ss[2], false, KERNEL_FIRST);
+	kernel_seq_depth(seq[2], 1);
+	kernel_seq_loc(seq[2], 75, 149);
+	kernel_seq_scale(seq[2], 100);
+
+	viewing_at_y = 22;
+	global[player_score] = 0;
+	global[g009] = 0;
+	player.walker_visible = 0;
+	player.commands_allowed = 0;
+	mouse_hide();
+
+	for (int16 i = 0; i < 10; i++) {
+		aainfo[i]._val1 = 0;
+		aainfo[i]._val2 = -1;
+	}
+
+	kernel_timing_trigger(5, 100);
+}
+
+static void room_107_anim1() {
+	int16 prev_frame = aainfo[0]._val2;
+	int16 frame = kernel_anim[aa[0]].frame;
+
+	if (frame != prev_frame) {
+		aainfo[0]._val2 = frame;
+
+		if (frame == 30) {
+			new_room = 104;
+		} else if (frame < 30) {
+			if (frame == 1) {
+				kernel_seq_delete(seq[0]);
+				kernel_seq_delete(seq[1]);
+				kernel_seq_delete(seq[2]);
+			} else if (frame == 21) {
+				aainfo[0]._val3 = 1;
+				digi_play_build(107, 'b', 1, 1);
+				scratch._8e = 21;
+			} else if (frame == 26) {
+				if (aainfo[0]._val3 == 1) {
+					aainfo[0]._val2 = 21;
+					kernel_reset_animation(aa[0], 21);
+				}
+			}
+		}
+	}
+
+	if (kernel.trigger == 7 || kernel.trigger == 28) {
+		if (scratch._8e == 21) {
+			aainfo[0]._val3 = 2;
+			scratch._8e = -1;
+			global_digi_play(4);
+			aainfo[0]._val2 = 26;
+			kernel_reset_animation(aa[0], 26);
+		}
+	}
 }
 
 static void room_107_daemon() {
+	if (global[player_hyperwalked] == -1) {
+		game_save_name(0);
+		kernel_save_game(save_game_buf);
+		new_room = 904;
+	}
+
+	if (kernel.trigger == 100) {
+		aa[0] = kernel_run_animation(kernel_name('T', 1), 0);
+		aainfo[0]._val1 = -1;
+		scratch._8c = 30;
+	}
+
+	if (aainfo[0]._val1 != 0)
+		room_107_anim1();
 }
 
 static void room_107_pre_parser() {
@@ -73,6 +154,8 @@ void room_107_synchronize(Common::Serializer &s) {
 	for (int16 &v : scratch.sprite)    s.syncAsSint16LE(v);
 	for (int16 &v : scratch.sequence)  s.syncAsSint16LE(v);
 	for (int16 &v : scratch.animation) s.syncAsSint16LE(v);
+	for (AnimationInfo &ai : scratch.animation_info) ai.synchronize(s);
+	s.syncAsSint16LE(scratch._8c);
 }
 
 void room_107_preload() {
@@ -83,6 +166,7 @@ void room_107_preload() {
 
 	section_1_walker();
 	section_1_interface();
+	player.walker_must_reload = true;
 }
 
 } // namespace Rooms


Commit: f157eaacdc564b45043341feaa6c820636136409
    https://github.com/scummvm/scummvm/commit/f157eaacdc564b45043341feaa6c820636136409
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-06-05T20:54:50+10:00

Commit Message:
MADS: FOREST: Implemented room 199

Changed paths:
    engines/mads/madsv2/forest/rooms/room199.cpp


diff --git a/engines/mads/madsv2/forest/rooms/room199.cpp b/engines/mads/madsv2/forest/rooms/room199.cpp
index 1ad9e804b96..beffb90cfe8 100644
--- a/engines/mads/madsv2/forest/rooms/room199.cpp
+++ b/engines/mads/madsv2/forest/rooms/room199.cpp
@@ -19,17 +19,14 @@
  *
  */
 
-#include "mads/madsv2/core/conv.h"
+#include "mads/madsv2/core/digi.h"
 #include "mads/madsv2/core/game.h"
-#include "mads/madsv2/core/imath.h"
-#include "mads/madsv2/core/inter.h"
+#include "mads/madsv2/core/global.h"
 #include "mads/madsv2/core/kernel.h"
-#include "mads/madsv2/core/sound.h"
-#include "mads/madsv2/core/text.h"
-#include "mads/madsv2/forest/mads/inventory.h"
-#include "mads/madsv2/forest/mads/sounds.h"
-#include "mads/madsv2/forest/mads/words.h"
+#include "mads/madsv2/core/matte.h"
+#include "mads/madsv2/core/player.h"
 #include "mads/madsv2/forest/global.h"
+#include "mads/madsv2/forest/journal.h"
 #include "mads/madsv2/forest/rooms/section1.h"
 #include "mads/madsv2/forest/rooms/room199.h"
 
@@ -39,14 +36,10 @@ namespace Forest {
 namespace Rooms {
 
 struct Scratch {
-	int16 sprite[15];       /* Sprite series handles */
-	int16 sequence[15];     /* Sequence handles      */
-	int16 animation[4];     /* Animation handles     */
-
-	int16 dragon_frame;     /* frame animation is on */
-
-	int16 done_with_conv;   /* T if done with conv   */
-	int16 prev_room;
+	int16 sprite[10];            /* 0x00 ??? sprite series handles  */
+	int16 sequence[10];          /* 0x14 ??? sequence handles       */
+	int16 animation[10];         /* 0x28 ??? animation handles      */
+	AnimationInfo animation_info[10]; /* 0x3C */
 };
 
 static Scratch scratch;
@@ -55,24 +48,517 @@ static Scratch scratch;
 #define ss    local->sprite
 #define seq   local->sequence
 #define aa    local->animation
+#define aainfo scratch.animation_info
 
 
+static void room_199_init1() {
+	if (flags[15] >= 1) {
+		ss[1] = kernel_load_series("*rm199n2", 0); seq[7] = kernel_seq_stamp(ss[1], false, 1); kernel_seq_depth(seq[7], 5);
+	}
+	if (flags[16] >= 1) {
+		ss[2] = kernel_load_series("*rm199n3", 0); seq[8] = kernel_seq_stamp(ss[2], false, 1); kernel_seq_depth(seq[8], 5);
+	}
+	if (flags[17] >= 1 || flags[24] >= 1) {
+		ss[3] = kernel_load_series("*rm199n5", 0); seq[9] = kernel_seq_stamp(ss[3], false, 1); kernel_seq_depth(seq[9], 5);
+	}
+	if (flags[18] >= 1) {
+		ss[4] = kernel_load_series("*rm199n4", 0); aa[0] = kernel_seq_stamp(ss[4], false, 1); kernel_seq_depth(aa[0], 5);
+	}
+	if (flags[19] >= 1) {
+		ss[5] = kernel_load_series("*rm199n7", 0); aa[1] = kernel_seq_stamp(ss[5], false, 1); kernel_seq_depth(aa[1], 5);
+	}
+	if (flags[20] >= 1) {
+		ss[6] = kernel_load_series("*rm199n8", 0); aa[2] = kernel_seq_stamp(ss[6], false, 1); kernel_seq_depth(aa[2], 5);
+	}
+	if (flags[21] >= 1 || flags[25] >= 1) {
+		ss[7] = kernel_load_series("*rm199n6", 0); aa[3] = kernel_seq_stamp(ss[7], false, 1); kernel_seq_depth(aa[3], 5);
+	}
+	if (flags[22] >= 1) {
+		ss[8] = kernel_load_series("*rm199n9", 0); aa[4] = kernel_seq_stamp(ss[8], false, 1); kernel_seq_depth(aa[4], 5);
+	}
+	if (flags[26] >= 1 || flags[31] >= 1) {
+		ss[9] = kernel_load_series("*rm199n10", 0); aa[5] = kernel_seq_stamp(ss[9], false, 1); kernel_seq_depth(aa[5], 5);
+	}
+	if (flags[27] >= 1) {
+		seq[0] = kernel_load_series("*rm199n11", 0); aa[6] = kernel_seq_stamp(seq[0], false, 1); kernel_seq_depth(aa[6], 5);
+	}
+	if (flags[28] >= 1) {
+		seq[1] = kernel_load_series("*rm199n12", 0); aa[7] = kernel_seq_stamp(seq[1], false, 1); kernel_seq_depth(aa[7], 5);
+	}
+	if (flags[29] >= 1) {
+		seq[2] = kernel_load_series("*rm199n13", 0); aa[8] = kernel_seq_stamp(seq[2], false, 1); kernel_seq_depth(aa[8], 5);
+	}
+	if (flags[30] >= 1) {
+		seq[3] = kernel_load_series("*rm199n14", 0); aa[9] = kernel_seq_stamp(seq[3], false, 1); kernel_seq_depth(aa[9], 5);
+	}
+	if (flags[32] >= 1) {
+		seq[4] = kernel_load_series("*rm199n15", 0); aainfo[0]._val1 = kernel_seq_stamp(seq[4], false, 1); kernel_seq_depth(aainfo[0]._val1, 5);
+	}
+	if (flags[33] >= 1) {
+		seq[5] = kernel_load_series("*rm199n16", 0); aainfo[0]._val2 = kernel_seq_stamp(seq[5], false, 1); kernel_seq_depth(aainfo[0]._val2, 5);
+	}
+}
+
 static void room_199_init() {
+	global[player_score] = 0;
+	global[g009] = -1;
+	global_digi_play(7);
+
+	aainfo[2]._val3 = -1;
+	aainfo[2]._val4 = 0;
+	aainfo[3]._val1 = 0;
+	aainfo[3]._val2 = -1;
+
+	if (previous_room == 304 || previous_room == 402) {
+		if (global[g070] != 0) {
+			aainfo[2]._val2 = 1;
+			ss[1] = kernel_load_series("*rm199z1", 0);
+			kernel_flip_hotspot(99, false);
+			kernel_flip_hotspot(174, false);
+			int16 slot = aainfo[2]._val2;
+			seq[6 + slot] = kernel_seq_stamp(ss[slot], false, 1);
+			kernel_seq_depth(seq[6 + slot], 1);
+			aainfo[2]._val1 = slot;
+			player.commands_allowed = 0;
+			kernel_timing_trigger(300, 111);
+			return;
+		}
+		room_199_init1();
+		aainfo[2]._val2 = 0;
+		ss[0] = kernel_load_series("*rm199n1", 0);
+	} else if (previous_room == 503) {
+		aainfo[2]._val2 = 3;
+		ss[3] = kernel_load_series("*rm199v1", 0);
+		kernel_flip_hotspot(99, false);
+		kernel_flip_hotspot(174, false);
+		int16 slot = aainfo[2]._val2;
+		seq[6 + slot] = kernel_seq_stamp(ss[slot], false, 1);
+		kernel_seq_depth(seq[6 + slot], 5);
+		aainfo[2]._val1 = slot;
+		player.commands_allowed = 0;
+		kernel_timing_trigger(2, 112);
+		return;
+	} else {
+		room_199_init1();
+		aainfo[2]._val2 = 0;
+		ss[0] = kernel_load_series("*rm199n1", 0);
+	}
+
+	kernel_flip_hotspot(99, false);
+	kernel_flip_hotspot(174, false);
+	int16 slot = aainfo[2]._val2;
+	seq[6 + slot] = kernel_seq_stamp(ss[slot], false, 1);
+	kernel_seq_depth(seq[6 + slot], 1);
+	aainfo[2]._val1 = slot;
+}
+
+static int room_199_anim1() {
+	if (!player_has(6))  return 0;
+	if (!player_has(12)) return 0;
+	if (!player_has(0))  return 0;
+	if (!player_has(2))  return 0;
+	if (!player_has(14)) return 0;
+	if (!player_has(4))  return 0;
+	if (!player_has(8))  return 0;
+	if (!player_has(10)) return 0;
+	if (!player_has(9))  return 0;
+	if (!player_has(11)) return 0;
+	if (!player_has(7))  return 0;
+	return 1;
+}
+
+static void room_199_anim2() {
+	if (kernel.trigger == 7)
+		kernel_timing_trigger(30, 28);
+	if (kernel.trigger != 28)
+		return;
+
+	switch (aainfo[2]._val3) {
+	case 1:
+		if (!player_has(14)) digi_play_build(521, 'e', 6, 1);
+		else kernel_timing_trigger(2, 28);
+		aainfo[2]._val3 = 2; return;
+	case 2:
+		if (!player_has(11)) digi_play_build(521, 'e', 10, 1);
+		else kernel_timing_trigger(2, 28);
+		aainfo[2]._val3 = 3; return;
+	case 3:
+		if (!player_has(12)) digi_play_build(521, 'e', 3, 1);
+		else kernel_timing_trigger(2, 28);
+		aainfo[2]._val3 = 4; return;
+	case 4:
+		if (!player_has(10)) digi_play_build(521, 'e', 8, 1);
+		else kernel_timing_trigger(2, 28);
+		aainfo[2]._val3 = 5; return;
+	case 5:
+		if (!player_has(9)) digi_play_build(521, 'e', 9, 1);
+		else kernel_timing_trigger(2, 28);
+		aainfo[2]._val3 = 6; return;
+	case 6:
+		if (!player_has(8)) digi_play_build(521, 'e', 20, 1);
+		else kernel_timing_trigger(2, 28);
+		aainfo[2]._val3 = 7; return;
+	case 7:
+		if (!player_has(2)) digi_play_build(521, 'e', 5, 1);
+		else kernel_timing_trigger(2, 28);
+		aainfo[2]._val3 = 8; return;
+	case 8:
+		if (!player_has(6)) digi_play_build(521, 'e', 2, 1);
+		else kernel_timing_trigger(2, 28);
+		aainfo[2]._val3 = 9; return;
+	case 9:
+		if (!player_has(4)) digi_play_build(521, 'e', 7, 1);
+		else kernel_timing_trigger(2, 28);
+		aainfo[2]._val3 = 10; return;
+	case 10:
+		if (!player_has(0)) digi_play_build(521, 'e', 4, 1);
+		else kernel_timing_trigger(2, 28);
+		aainfo[2]._val3 = 11; return;
+	case 11:
+		if (!player_has(7)) digi_play_build(521, 'e', 21, 1);
+		else kernel_timing_trigger(2, 28);
+		aainfo[2]._val3 = 12; return;
+	case 12:
+		digi_play_build(521, 'b', 1, 1);
+		aainfo[2]._val3 = 20; return;
+	case 14:
+		aainfo[2]._val4 = 0;
+		aainfo[2]._val3 = -1;
+		aainfo[3]._val1 = 0;
+		kernel_timing_trigger(2, 113);
+		return;
+	default:
+		return;
+	}
+}
+
+static void room_199_anim3() {
+	switch (aainfo[2]._val3) {
+	case 1:  digi_play_build(521, 'e', 6, 1);  break;
+	case 2:  digi_play_build(521, 'e', 10, 1); break;
+	case 3:  digi_play_build(521, 'e', 3, 1);  break;
+	case 4:  digi_play_build(521, 'e', 8, 1);  break;
+	case 5:  digi_play_build(521, 'e', 9, 1);  break;
+	case 6:  digi_play_build(521, 'e', 20, 1); break;
+	case 7:  digi_play_build(521, 'e', 5, 1);  break;
+	case 8:  digi_play_build(521, 'e', 2, 1);  break;
+	case 9:  digi_play_build(521, 'e', 7, 1);  break;
+	case 10: digi_play_build(521, 'e', 4, 1);  break;
+	case 11: digi_play_build(521, 'e', 21, 1); break;
+	default: break;
+	}
+}
+
+static void room_199_anim4() {
+	if (kernel.trigger == 7) {
+		kernel_timing_trigger(15, 28);
+		aainfo[2]._val3++;
+	}
+	if (kernel.trigger != 28)
+		return;
+
+	switch (aainfo[2]._val3) {
+	case 1:
+		if (player_has(14)) {
+			ss[8] = kernel_load_series("*ob014i", 0);
+			aa[4] = kernel_seq_stamp(ss[8], false, 1);
+			kernel_seq_depth(aa[4], 1);
+			kernel_seq_loc(aa[4], 213, 42);
+			kernel_seq_scale(aa[4], 88);
+		}
+		kernel_timing_trigger(15, 115); return;
+	case 2:
+		if (player_has(11)) {
+			seq[3] = kernel_load_series("*ob011i", 0);
+			aa[9] = kernel_seq_stamp(seq[3], false, 1);
+			kernel_seq_depth(aa[9], 1);
+			kernel_seq_loc(aa[9], 250, 41);
+		}
+		kernel_timing_trigger(15, 115); return;
+	case 3:
+		if (player_has(12)) {
+			ss[5] = kernel_load_series("*ob012i", 0);
+			aa[1] = kernel_seq_stamp(ss[5], false, 1);
+			kernel_seq_depth(aa[1], 1);
+			kernel_seq_loc(aa[1], 287, 39);
+			kernel_seq_scale(aa[1], 82);
+		}
+		kernel_timing_trigger(15, 115); return;
+	case 4:
+		if (player_has(10)) {
+			seq[1] = kernel_load_series("*ob010i", 0);
+			aa[7] = kernel_seq_stamp(seq[1], false, 1);
+			kernel_seq_depth(aa[7], 1);
+			kernel_seq_loc(aa[7], 213, 73);
+			kernel_seq_scale(aa[7], 79);
+		}
+		kernel_timing_trigger(15, 115); return;
+	case 5:
+		if (player_has(9)) {
+			seq[2] = kernel_load_series("*ob009i", 0);
+			aa[8] = kernel_seq_stamp(seq[2], false, 1);
+			kernel_seq_depth(aa[8], 1);
+			kernel_seq_loc(aa[8], 248, 73);
+			kernel_seq_scale(aa[8], 82);
+		}
+		kernel_timing_trigger(15, 115); return;
+	case 6:
+		if (player_has(8)) {
+			seq[0] = kernel_load_series("*ob008i", 0);
+			aa[6] = kernel_seq_stamp(seq[0], false, 1);
+			kernel_seq_depth(aa[6], 1);
+			kernel_seq_loc(aa[6], 287, 72);
+			kernel_seq_scale(aa[6], 82);
+		}
+		kernel_timing_trigger(15, 115); return;
+	case 7:
+		if (player_has(2)) {
+			ss[7] = kernel_load_series("*ob002i", 0);
+			aa[3] = kernel_seq_stamp(ss[7], false, 1);
+			kernel_seq_depth(aa[3], 1);
+			kernel_seq_loc(aa[3], 212, 108);
+			kernel_seq_scale(aa[3], 82);
+		}
+		kernel_timing_trigger(15, 115); return;
+	case 8:
+		if (player_has(6)) {
+			ss[4] = kernel_load_series("*ob006i", 0);
+			aa[0] = kernel_seq_stamp(ss[4], false, 1);
+			kernel_seq_depth(aa[0], 1);
+			kernel_seq_loc(aa[0], 249, 105);
+			kernel_seq_scale(aa[0], 82);
+		}
+		kernel_timing_trigger(15, 115); return;
+	case 9:
+		if (player_has(4)) {
+			ss[9] = kernel_load_series("*ob004i", 0);
+			aa[5] = kernel_seq_stamp(ss[9], false, 1);
+			kernel_seq_depth(aa[5], 1);
+			kernel_seq_loc(aa[5], 285, 108);
+			kernel_seq_scale(aa[5], 82);
+		}
+		kernel_timing_trigger(15, 115); return;
+	case 10:
+		if (player_has(0)) {
+			ss[6] = kernel_load_series("*ob000i", 0);
+			aa[2] = kernel_seq_stamp(ss[6], false, 1);
+			kernel_seq_depth(aa[2], 1);
+			kernel_seq_loc(aa[2], 230, 142);
+			kernel_seq_scale(aa[2], 82);
+		}
+		kernel_timing_trigger(15, 115); return;
+	case 11:
+		if (player_has(7)) {
+			seq[4] = kernel_load_series("*ob007i", 0);
+			aainfo[0]._val1 = kernel_seq_stamp(seq[4], false, 1);
+			kernel_seq_depth(aainfo[0]._val1, 1);
+			kernel_seq_loc(aainfo[0]._val1, 270, 144);
+			kernel_seq_scale(aainfo[0]._val1, 85);
+		}
+		kernel_timing_trigger(15, 115); return;
+	case 12:
+		aainfo[2]._val4 = 0;
+		aainfo[2]._val3 = -1;
+		kernel_timing_trigger(30, 114);
+		return;
+	default:
+		return;
+	}
 }
 
 static void room_199_daemon() {
+	int16 trig = kernel.trigger;
+
+	if (trig == 7) {
+		if (aainfo[3]._val2 == 11) {
+			aainfo[3]._val2 = -1;
+			kernel_timing_trigger(30, 113);
+		}
+	} else if (trig == 111) {
+		global[g070] = 0;
+		leave_journal();
+		player.commands_allowed = -1;
+	} else if (trig == 112) {
+		aainfo[2]._val3 = 0;
+		aainfo[2]._val4 = -1;
+		digi_play_build(521, 'e', 1, 1);
+	} else if (trig == 113) {
+		leave_journal();
+		player.commands_allowed = -1;
+	} else if (trig == 114) {
+		int result = room_199_anim1();
+		global[g100] = result;
+		if (result) {
+			digi_play_build(521, 'e', 11, 1);
+			aainfo[3]._val2 = 11;
+		} else {
+			aainfo[2]._val3 = 1;
+			aainfo[3]._val1 = -1;
+			digi_play_build(521, 'e', 12, 1);
+			room_199_anim2();
+		}
+	} else if (trig == 115) {
+		room_199_anim3();
+	}
+
+	if (aainfo[2]._val4 != 0)
+		room_199_anim4();
+	if (aainfo[3]._val1 != 0)
+		room_199_anim2();
 }
 
 static void room_199_pre_parser() {
+	// No implementation
+}
+
+static void room_199_parser2() {
+	if (flags[33] >= 1) {
+		kernel_seq_delete(aainfo[0]._val2); matte_deallocate_series(seq[5], -1);
+	}
+	if (flags[32] >= 1) {
+		kernel_seq_delete(aainfo[0]._val1); matte_deallocate_series(seq[4], -1);
+	}
+	if (flags[30] >= 1) {
+		kernel_seq_delete(aa[9]);            matte_deallocate_series(seq[3], -1);
+	}
+	if (flags[29] >= 1) {
+		kernel_seq_delete(aa[8]);            matte_deallocate_series(seq[2], -1);
+	}
+	if (flags[28] >= 1) {
+		kernel_seq_delete(aa[7]);            matte_deallocate_series(seq[1], -1);
+	}
+	if (flags[27] >= 1) {
+		kernel_seq_delete(aa[6]);            matte_deallocate_series(seq[0], -1);
+	}
+	if (flags[26] >= 1 || flags[31] >= 1) {
+		kernel_seq_delete(aa[5]); matte_deallocate_series(ss[9], -1);
+	}
+	if (flags[22] >= 1) {
+		kernel_seq_delete(aa[4]);            matte_deallocate_series(ss[8], -1);
+	}
+	if (flags[21] >= 1 || flags[25] >= 1) {
+		kernel_seq_delete(aa[3]); matte_deallocate_series(ss[7], -1);
+	}
+	if (flags[20] >= 1) {
+		kernel_seq_delete(aa[2]);            matte_deallocate_series(ss[6], -1);
+	}
+	if (flags[19] >= 1) {
+		kernel_seq_delete(aa[1]);            matte_deallocate_series(ss[5], -1);
+	}
+	if (flags[18] >= 1) {
+		kernel_seq_delete(aa[0]);            matte_deallocate_series(ss[4], -1);
+	}
+	if (flags[17] >= 1 || flags[24] >= 1) {
+		kernel_seq_delete(seq[9]); matte_deallocate_series(ss[3], -1);
+	}
+	if (flags[16] >= 1) {
+		kernel_seq_delete(seq[8]);           matte_deallocate_series(ss[2], -1);
+	}
+	if (flags[15] >= 1) {
+		kernel_seq_delete(seq[7]);           matte_deallocate_series(ss[1], -1);
+	}
+}
+
+static void room_199_parser1() {
+	if (aainfo[2]._val1 == 2)
+		kernel_flip_hotspot(99, 0);
+	if (aainfo[2]._val1 == 3)
+		kernel_flip_hotspot(174, 0);
+
+	int16 old_slot = aainfo[2]._val1;
+	kernel_seq_delete(seq[6 + old_slot]);
+	matte_deallocate_series(ss[old_slot], -1);
+
+	if (aainfo[2]._val1 == 0)
+		room_199_parser2();
+
+	int16 new_slot = aainfo[2]._val2;
+	switch (new_slot) {
+	case 0:
+		room_199_init1();
+		ss[new_slot] = kernel_load_series("*rm199n1", 0);
+		break;
+	case 1:
+		ss[new_slot] = kernel_load_series("*rm199z1", 0);
+		break;
+	case 2:
+		ss[new_slot] = kernel_load_series("*rm199i1", 0);
+		kernel_flip_hotspot(99, -1);
+		break;
+	case 3:
+		ss[new_slot] = kernel_load_series("*rm199v1", 0);
+		kernel_flip_hotspot(174, -1);
+		break;
+	}
+
+	seq[6 + new_slot] = kernel_seq_stamp(ss[new_slot], false, 1);
+	kernel_seq_depth(seq[6 + new_slot], 1);
+	aainfo[2]._val1 = aainfo[2]._val2;
+	digi_play_build_ii('_', 1, 2);
 }
 
 static void room_199_parser() {
+	if (player_parse(174, 121, 0)) { digi_play_build(521, 'e', 6, 2);  goto handled; }
+	if (player_parse(174, 164, 0)) { digi_play_build(521, 'e', 10, 2); goto handled; }
+	if (player_parse(174, 112, 0)) { digi_play_build(521, 'e', 3, 2);  goto handled; }
+	if (player_parse(174, 163, 0)) { digi_play_build(521, 'e', 8, 2);  goto handled; }
+	if (player_parse(174, 158, 0)) { digi_play_build(521, 'e', 9, 2);  goto handled; }
+	if (player_parse(174, 154, 0)) { digi_play_build(521, 'e', 20, 2); goto handled; }
+	if (player_parse(174, 96, 0))  { digi_play_build(521, 'e', 5, 2);  goto handled; }
+	if (player_parse(174, 132, 0)) { digi_play_build(521, 'e', 2, 2);  goto handled; }
+	if (player_parse(174, 113, 0)) { digi_play_build(521, 'e', 7, 2);  goto handled; }
+	if (player_parse(174, 105, 0)) { digi_play_build(521, 'e', 4, 2);  goto handled; }
+	if (player_parse(174, 145, 0)) { digi_play_build(521, 'e', 21, 2); goto handled; }
+
+	if (player_parse(99, 82, 0))  { digi_play_build_ii('e', 9, 2);  goto handled; }
+	if (player_parse(99, 131, 0)) { digi_play_build_ii('e', 5, 2);  goto handled; }
+	if (player_parse(99, 142, 0)) { digi_play_build_ii('e', 3, 2);  goto handled; }
+	if (player_parse(99, 146, 0)) { digi_play_build_ii('e', 6, 2);  goto handled; }
+	if (player_parse(99, 150, 0)) { digi_play_build_ii('e', 8, 2);  goto handled; }
+	if (player_parse(99, 95, 0))  { digi_play_build_ii('e', 11, 2); goto handled; }
+	if (player_parse(99, 118, 0)) { digi_play_build_ii('e', 1, 2);  goto handled; }
+	if (player_parse(99, 103, 0)) { digi_play_build_ii('e', 4, 2);  goto handled; }
+	if (player_parse(99, 83, 0))  { digi_play_build_ii('e', 7, 2);  goto handled; }
+	if (player_parse(99, 172, 0)) { digi_play_build_ii('e', 12, 2); goto handled; }
+	if (player_parse(99, 77, 0))  { digi_play_build_ii('e', 2, 2);  goto handled; }
+
+	if (player_parse(94, 0)) {
+		leave_journal();
+		goto handled;
+	}
+
+	if (player_parse(156, 0)) {
+		if (aainfo[2]._val1 != 3) {
+			aainfo[2]._val1 = aainfo[2]._val2;
+			aainfo[2]._val2++;
+		}
+		if (aainfo[2]._val2 != aainfo[2]._val1)
+			room_199_parser1();
+		goto handled;
+	}
+
+	if (player_parse(90, 0)) {
+		if (aainfo[2]._val1 != 0) {
+			aainfo[2]._val1 = aainfo[2]._val2;
+			aainfo[2]._val2--;
+		}
+		if (aainfo[2]._val2 != aainfo[2]._val1)
+			room_199_parser1();
+		goto handled;
+	}
+
+	goto done;
+handled:
+	player.command_ready = false;
+done:
+	;
 }
 
 void room_199_synchronize(Common::Serializer &s) {
 	for (int16 &v : scratch.sprite)    s.syncAsSint16LE(v);
 	for (int16 &v : scratch.sequence)  s.syncAsSint16LE(v);
 	for (int16 &v : scratch.animation) s.syncAsSint16LE(v);
+	for (AnimationInfo &ai : scratch.animation_info) ai.synchronize(s);
 }
 
 void room_199_preload() {
@@ -81,6 +567,8 @@ void room_199_preload() {
 	room_parser_code_pointer = room_199_parser;
 	room_daemon_code_pointer = room_199_daemon;
 
+	global[g016] = -1;
+	player.walker_visible = false;
 	section_1_walker();
 	section_1_interface();
 }



From noreply at scummvm.org  Fri Jun  5 11:04:15 2026
From: noreply at scummvm.org (dreammaster)
Date: Fri,  5 Jun 2026 11:04:15 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 8e1d46c4731998ed39faabb31b76d319dd7de41f
Message-ID: <20260605110415.277E57005BC@ron.scummvm.net>

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

Summary:
8e1d46c473 MADS: NEBULAR: Fix bug #16821 interrupting main menu animation


Commit: 8e1d46c4731998ed39faabb31b76d319dd7de41f
    https://github.com/scummvm/scummvm/commit/8e1d46c4731998ed39faabb31b76d319dd7de41f
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-06-05T21:04:08+10:00

Commit Message:
MADS: NEBULAR: Fix bug #16821 interrupting main menu animation

Changed paths:
    engines/mads/nebular/menu_nebular.cpp


diff --git a/engines/mads/nebular/menu_nebular.cpp b/engines/mads/nebular/menu_nebular.cpp
index a2e777d71bb..daba7cf741c 100644
--- a/engines/mads/nebular/menu_nebular.cpp
+++ b/engines/mads/nebular/menu_nebular.cpp
@@ -136,6 +136,7 @@ void MainMenu::doFrame() {
 			addSpriteSlot();
 		}
 
+		_vm->_screen->markAllDirty();
 		_vm->_events->showCursor();
 		showBonusItems();
 	} else {



From noreply at scummvm.org  Fri Jun  5 11:08:34 2026
From: noreply at scummvm.org (kelmer44)
Date: Fri,  5 Jun 2026 11:08:34 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 4ae5f60a53daaf76909a255398b1419ab9d15124
Message-ID: <20260605110834.793867005BC@ron.scummvm.net>

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

Summary:
4ae5f60a53 PELROCK: Add alternate paths for intro files


Commit: 4ae5f60a53daaf76909a255398b1419ab9d15124
    https://github.com/scummvm/scummvm/commit/4ae5f60a53daaf76909a255398b1419ab9d15124
Author: kelmer (kelmer at gmail.com)
Date: 2026-06-05T13:08:26+02:00

Commit Message:
PELROCK: Add alternate paths for intro files

Changed paths:
    engines/pelrock/video.cpp


diff --git a/engines/pelrock/video.cpp b/engines/pelrock/video.cpp
index e506d936974..2fed22ef082 100644
--- a/engines/pelrock/video.cpp
+++ b/engines/pelrock/video.cpp
@@ -39,7 +39,7 @@ VideoManager::VideoManager(
 	SoundManager *sound) : _screen(screen), _events(events), _chrono(chrono), _largeFont(largeFont), _dialog(dialog), _sound(sound) {
 	_videoSurface.create(640, 400, Graphics::PixelFormat::createFormatCLUT8());
 	_textSurface.create(640, 400, Graphics::PixelFormat::createFormatCLUT8());
-	if (!_introSndFile.open("introsnd.dat")) {
+	if (!_introSndFile.open("introsnd.dat") && !_introSndFile.open("DATA/introsnd.dat")) {
 		error("VideoManager::VideoManager(): Could not open introsnd.dat");
 	}
 }
@@ -53,7 +53,7 @@ VideoManager::~VideoManager() {
 void VideoManager::playIntro() {
 	initMetadata();
 	Common::File videoFile;
-	if (!videoFile.open("ESCENAX.SSN")) {
+	if (!videoFile.open("ESCENAX.SSN") && !videoFile.open("DATA/ESCENAX.SSN")) {
 		error("VideoManager::playIntro(): Could not open ESCENAX.SSN");
 		return;
 	}
@@ -264,7 +264,7 @@ void VideoManager::presentFrame() {
 
 void VideoManager::initMetadata() {
 	Common::File metadataFile;
-	if (!metadataFile.open("ESCENAX.SCR")) {
+	if (!metadataFile.open("ESCENAX.SCR") && !metadataFile.open("DATA/ESCENAX.SCR")) {
 		error("VideoManager::initMetadata(): Could not open ESCENAX.SCR");
 		return;
 	}



From noreply at scummvm.org  Fri Jun  5 11:24:56 2026
From: noreply at scummvm.org (neuromancer)
Date: Fri,  5 Jun 2026 11:24:56 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 0befed2f78418f3d30d171d00cabd9ba3b3f656f
Message-ID: <20260605112456.5D0387005BC@ron.scummvm.net>

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

Summary:
4031c5786f COLONY: mouse is less sensitive when turning
efe59024d9 COLONY: do not crash when calling glDepthRangef in desktop
a731f96b70 COLONY: added missing sounds
e3981b3175 COLONY: refined mac b&w interface and refactored some related code
9b0fb92042 COLONY: fixed xor mode in shader rendering
d541fc5d0e COLONY: complete rendering of mountain parallax
0befed2f78 COLONY: make sure the battle coordinates are normalized before starting


Commit: 4031c5786f456add3413cc8071ed1fc9ac6ce718
    https://github.com/scummvm/scummvm/commit/4031c5786f456add3413cc8071ed1fc9ac6ce718
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-05T13:24:41+02:00

Commit Message:
COLONY: mouse is less sensitive when turning

Changed paths:
    engines/colony/colony.cpp
    engines/colony/colony.h


diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index 2b736f884ab..3748d3d8dda 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -53,6 +53,9 @@ namespace Colony {
 
 namespace {
 
+const float kMouseLookSensitivity = 0.25f;
+const float kKeyboardTurnSpeed = 30.0f; // angle units per second
+
 class OwnedCursor final : public Graphics::Cursor {
 public:
 	OwnedCursor(uint16 width, uint16 height, uint16 hotspotX, uint16 hotspotY, byte keyColor,
@@ -139,7 +142,7 @@ ColonyEngine::ColonyEngine(OSystem *syst, const ADGameDescription *gd) : Engine(
 	_height = 350;
 	_centerX = _width / 2;
 	_centerY = _height / 2;
-	_mouseSensitivity = 1;
+	_mouseSensitivity = kMouseLookSensitivity;
 	_mouseLocked = false;
 	_mousePos = Common::Point(_centerX, _centerY);
 	_showDashBoard = true;
@@ -841,6 +844,7 @@ Common::Error ColonyEngine::run() {
 	updateMouseCapture(true);
 
 	int mouseDX = 0, mouseDY = 0;
+	float mouseLookAccumX = 0.0f, mouseLookAccumY = 0.0f;
 	bool mouseMoved = false;
 	uint32 lastMoveTick = _system->getMillis();
 	uint32 lastColonyTick = lastMoveTick;
@@ -930,6 +934,7 @@ Common::Error ColonyEngine::run() {
 					updateMouseCapture(true);
 					if (_mouseLocked) {
 						mouseDX = mouseDY = 0;
+						mouseLookAccumX = mouseLookAccumY = 0.0f;
 						mouseMoved = false;
 					}
 				}
@@ -995,6 +1000,7 @@ Common::Error ColonyEngine::run() {
 						updateMouseCapture(true);
 						if (_mouseLocked) {
 							mouseDX = mouseDY = 0;
+							mouseLookAccumX = mouseLookAccumY = 0.0f;
 							mouseMoved = false;
 						}
 					}
@@ -1009,6 +1015,9 @@ Common::Error ColonyEngine::run() {
 					openMainMenuDialog();
 					_gfx->computeScreenViewport();
 					updateMouseCapture(true);
+					mouseDX = mouseDY = 0;
+					mouseLookAccumX = mouseLookAccumY = 0.0f;
+					mouseMoved = false;
 					break;
 				default:
 					break;
@@ -1073,13 +1082,27 @@ Common::Error ColonyEngine::run() {
 
 		if (mouseMoved && _mouseLocked) {
 			if (mouseDX != 0) {
-				_me.look = (uint8)((int)_me.look - (mouseDX * _mouseSensitivity));
-				// In battle mode, body always faces look direction
-				if (_gameMode == kModeBattle)
-					_me.ang = _me.look;
+				mouseLookAccumX -= mouseDX * _mouseSensitivity;
+				const int lookDelta = (int)mouseLookAccumX;
+				if (lookDelta != 0) {
+					mouseLookAccumX -= lookDelta;
+					_me.look = (uint8)((int)_me.look + lookDelta);
+					// In battle mode, body always faces look direction
+					if (_gameMode == kModeBattle)
+						_me.ang = _me.look;
+				}
 			}
-			if (mouseDY != 0 && !_fl) {
-				_me.lookY = (int8)CLIP<int>((int)_me.lookY - (mouseDY * _mouseSensitivity), -64, 64);
+			if (mouseDY != 0) {
+				if (!_fl) {
+					mouseLookAccumY -= mouseDY * _mouseSensitivity;
+					const int lookYDelta = (int)mouseLookAccumY;
+					if (lookYDelta != 0) {
+						mouseLookAccumY -= lookYDelta;
+						_me.lookY = (int8)CLIP<int>((int)_me.lookY + lookYDelta, -64, 64);
+					}
+				} else {
+					mouseLookAccumY = 0.0f;
+				}
 			}
 			// Warp back to center and purge remaining mouse events
 			// to prevent the warp from generating phantom deltas (Freescape pattern)
@@ -1145,7 +1168,7 @@ Common::Error ColonyEngine::run() {
 			}
 
 			if (_rotateLeft || _rotateRight) {
-				const float rotSpeed = (float)(1 << (_speedShift - 1)) * 15.0f;
+				const float rotSpeed = kKeyboardTurnSpeed;
 				_rotAccum += (_rotateLeft ? rotSpeed : -rotSpeed) * dt;
 				const int rint = (int)_rotAccum;
 				_rotAccum -= rint;
diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index 5e41698308a..c933b6ec992 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -507,7 +507,7 @@ private:
 	int _cost[256];
 	int _centerX, _centerY;
 	int _width, _height;
-	int _mouseSensitivity;
+	float _mouseSensitivity;
 	bool _mouseLocked;
 	bool _soundOn = true;
 	bool _showDashBoard;


Commit: efe59024d91aa031c0d1369530498dc86e03a968
    https://github.com/scummvm/scummvm/commit/efe59024d91aa031c0d1369530498dc86e03a968
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-05T13:24:41+02:00

Commit Message:
COLONY: do not crash when calling glDepthRangef in desktop

Changed paths:
    engines/colony/renderer_opengl_shaders.cpp


diff --git a/engines/colony/renderer_opengl_shaders.cpp b/engines/colony/renderer_opengl_shaders.cpp
index eab77613daa..0639d28f5da 100644
--- a/engines/colony/renderer_opengl_shaders.cpp
+++ b/engines/colony/renderer_opengl_shaders.cpp
@@ -106,6 +106,7 @@ private:
 	void applyLineWidth(GLenum mode);
 
 	void uploadSolid3D(const float *positions, int vertCount);
+	void setGLDepthRange(float nearVal, float farVal);
 	// allowStipple=true on the fill pass picks up _stippleActive; lines
 	// must always render unstippled (matches the fixed-function path,
 	// which only stipples GL_QUADS / GL_POLYGON, never lines).
@@ -692,8 +693,18 @@ void OpenGLShaderRenderer::setDepthState(bool testEnabled, bool writeEnabled) {
 	glDepthMask(writeEnabled ? GL_TRUE : GL_FALSE);
 }
 
-void OpenGLShaderRenderer::setDepthRange(float nearVal, float farVal) {
+void OpenGLShaderRenderer::setGLDepthRange(float nearVal, float farVal) {
+#if USE_FORCED_GLES || USE_FORCED_GLES2
 	glDepthRangef(nearVal, farVal);
+#else
+	// glDepthRangef is only core in desktop OpenGL 4.1+. The double-precision
+	// entry point is core since OpenGL 1.0, so use it for desktop GL contexts.
+	glDepthRange(nearVal, farVal);
+#endif
+}
+
+void OpenGLShaderRenderer::setDepthRange(float nearVal, float farVal) {
+	setGLDepthRange(nearVal, farVal);
 }
 
 void OpenGLShaderRenderer::begin3D(int camX, int camY, int camZ, int angle, int angleY,
@@ -767,7 +778,7 @@ void OpenGLShaderRenderer::begin3D(int camX, int camY, int camZ, int angle, int
 void OpenGLShaderRenderer::end3D() {
 	glDisable(GL_DEPTH_TEST);
 	glDepthMask(GL_TRUE);
-	glDepthRangef(0.0f, 1.0f);
+	setGLDepthRange(0.0f, 1.0f);
 	glDisable(GL_SCISSOR_TEST);
 
 	// Restore the 2D viewport so subsequent overlay draws (dashboard, menu,


Commit: a731f96b706d0dd933c9d0d811ce86bd858294b5
    https://github.com/scummvm/scummvm/commit/a731f96b706d0dd933c9d0d811ce86bd858294b5
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-05T13:24:41+02:00

Commit Message:
COLONY: added missing sounds

Changed paths:
    engines/colony/animation.cpp
    engines/colony/colony.cpp
    engines/colony/colony.h
    engines/colony/movement.cpp


diff --git a/engines/colony/animation.cpp b/engines/colony/animation.cpp
index 43efac8971c..180ae0df359 100644
--- a/engines/colony/animation.cpp
+++ b/engines/colony/animation.cpp
@@ -657,10 +657,9 @@ void ColonyEngine::playAnimation() {
 	_system->getEventManager()->purgeMouseEvents();
 	_system->getEventManager()->purgeKeyboardEvents();
 
-	// Suppress collision sound on the first few wall hits after animation exit.
+	// Suppress collision sound on the first wall hit after animation exit.
 	// The player is at a door/wall boundary and held movement keys will
-	// immediately trigger checkwall collisions that play kBang ??? which sounds
-	// like a spurious gunshot. The flag auto-clears on the first successful move.
+	// immediately trigger a collision sound. The flag auto-clears in cCommand().
 	_suppressCollisionSound = true;
 
 	deleteAnimation();
@@ -1254,6 +1253,9 @@ void ColonyEngine::handleTeleshowClick(int item) {
 }
 
 void ColonyEngine::handleKeypadClick(int item) {
+	if (item > 0 && item <= 12)
+		_sound->play(Sound::kDit);
+
 	if (item >= 1 && item <= 10) {
 		for (int i = 5; i >= 1; i--)
 			_animDisplay[i] = _animDisplay[i - 1];
diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index 3748d3d8dda..c6b5c121211 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -690,6 +690,7 @@ void ColonyEngine::startNewGame() {
 	_orbit = 0;
 	_allGrow = false;
 	_suppressCollisionSound = false;
+	_lastCollisionSoundTime = 0;
 	_action0 = 0;
 	_action1 = 0;
 	_creature = 0;
@@ -977,14 +978,17 @@ Common::Error ColonyEngine::run() {
 					_me.lookY = 0;
 					break;
 				case kActionToggleDashboard:
+					_sound->play(Sound::kDit);
 					_showDashBoard = !_showDashBoard;
 					break;
 				case kActionToggleWireframe:
+					_sound->play(Sound::kDit);
 					_wireframe = !_wireframe;
 					debugC(1, kColonyDebugRender, "Polyfill: %s", _wireframe ? "off (wireframe)" : "on (filled)");
 					break;
 				case kActionToggleFullscreen:
 					if (_macMenu) {
+						_sound->play(Sound::kDit);
 						_fullscreen = !_fullscreen;
 						_menuBarHeight = _fullscreen ? 0 : 20;
 						updateViewportLayout();
diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index c933b6ec992..84a8d14ad0e 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -558,6 +558,7 @@ private:
 	uint32 _lastHotfootTime = 0;  // Time-gate for HOTFOOT damage (~8fps)
 	uint32 _lastAnimUpdate = 0;
 	uint32 _lastWarningChimeTime = 0;
+	uint32 _lastCollisionSoundTime = 0;
 	int _action0 = 0, _action1 = 0;
 	int _creature = 0;
 	bool _allGrow = false;
@@ -815,6 +816,7 @@ private:
 	Common::Rect readRect(Common::SeekableReadStreamEndian &file);
 	int whichSprite(const Common::Point &p);
 	void handleAnimationClick(int item);
+	void playCollisionSound();
 	void handleDeskClick(int item);
 	void handleVanityClick(int item);
 	void handleSlidesClick(int item);
diff --git a/engines/colony/movement.cpp b/engines/colony/movement.cpp
index 9e7cfcbd87f..84c40706357 100644
--- a/engines/colony/movement.cpp
+++ b/engines/colony/movement.cpp
@@ -411,8 +411,6 @@ int ColonyEngine::checkwall(int xnew, int ynew, Locate *pobject) {
 					return r;
 			}
 			debugC(1, kColonyDebugMove, "Collision South at x=%d y=%d", pobject->xindex, yind2);
-			if (!_suppressCollisionSound)
-				_sound->play(Sound::kBang);
 			return -1;
 
 		}
@@ -427,8 +425,6 @@ int ColonyEngine::checkwall(int xnew, int ynew, Locate *pobject) {
 				return r;
 		}
 		debugC(1, kColonyDebugMove, "Collision North at x=%d y=%d", pobject->xindex, pobject->yindex);
-		if (!_suppressCollisionSound)
-			_sound->play(Sound::kBang);
 		return -1;
 
 	}
@@ -445,8 +441,6 @@ int ColonyEngine::checkwall(int xnew, int ynew, Locate *pobject) {
 					return r;
 			}
 			debugC(1, kColonyDebugMove, "Collision East at x=%d y=%d", xind2, pobject->yindex);
-			if (!_suppressCollisionSound)
-				_sound->play(Sound::kBang);
 			return -1;
 
 		}
@@ -461,8 +455,6 @@ int ColonyEngine::checkwall(int xnew, int ynew, Locate *pobject) {
 				return r;
 		}
 		debugC(1, kColonyDebugMove, "Collision West at x=%d y=%d", pobject->xindex, pobject->yindex);
-		if (!_suppressCollisionSound)
-			_sound->play(Sound::kBang);
 		return -1;
 
 	}
@@ -1128,13 +1120,33 @@ void ColonyEngine::checkCenter() {
 	}
 }
 
+void ColonyEngine::playCollisionSound() {
+	if (_suppressCollisionSound)
+		return;
+
+	const uint32 now = _system->getMillis();
+	if (_lastCollisionSoundTime != 0 && now - _lastCollisionSoundTime < 175)
+		return;
+
+	_lastCollisionSoundTime = now;
+	_sound->play(Sound::kBonk);
+}
+
 void ColonyEngine::cCommand(int xnew, int ynew, bool allowInteraction) {
 	if (_me.xindex >= 0 && _me.xindex < 32 && _me.yindex >= 0 && _me.yindex < 32)
 		_robotArray[_me.xindex][_me.yindex] = 0;
 
+	const int oldXIndex = _me.xindex;
+	const int oldYIndex = _me.yindex;
+	const bool sameCellAttempt = ((xnew >> 8) == oldXIndex && (ynew >> 8) == oldYIndex);
 	const int robot = checkwall(xnew, ynew, &_me);
 	if (robot > 0 && allowInteraction)
 		interactWithObject(robot);
+	else if (robot)
+		playCollisionSound();
+	else if (sameCellAttempt && _me.xindex == oldXIndex && _me.yindex == oldYIndex &&
+			(_me.xloc != xnew || _me.yloc != ynew))
+		playCollisionSound();
 
 	if (_me.xindex >= 0 && _me.xindex < 32 && _me.yindex >= 0 && _me.yindex < 32)
 		_robotArray[_me.xindex][_me.yindex] = kMeNum;


Commit: e3981b3175121858b0b3fdac1ae674ae61eb9dd8
    https://github.com/scummvm/scummvm/commit/e3981b3175121858b0b3fdac1ae674ae61eb9dd8
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-05T13:24:41+02:00

Commit Message:
COLONY: refined mac b&w interface and refactored some related code

Changed paths:
    engines/colony/animation.cpp
    engines/colony/colony.cpp
    engines/colony/colony.h
    engines/colony/interaction.cpp
    engines/colony/intro.cpp
    engines/colony/movement.cpp
    engines/colony/render.cpp
    engines/colony/render_features.cpp
    engines/colony/render_objects.cpp
    engines/colony/ui.cpp


diff --git a/engines/colony/animation.cpp b/engines/colony/animation.cpp
index 180ae0df359..e04f5c6629b 100644
--- a/engines/colony/animation.cpp
+++ b/engines/colony/animation.cpp
@@ -398,7 +398,7 @@ void ColonyEngine::playAnimation() {
 	_system->lockMouse(false);
 	warpMouseLogical(_centerX, _centerY);
 	const char *cursorName = "default arrow cursor";
-	if (_renderMode == Common::kRenderMacintosh && _macArrowCursor) {
+	if (isMacRenderMode() && _macArrowCursor) {
 		cursorName = "Mac arrow cursor";
 		CursorMan.replaceCursor(_macArrowCursor);
 	} else {
@@ -713,7 +713,7 @@ void ColonyEngine::drawAnimation() {
 	ox = (ox / 8) * 8;
 	int oy = _screenR.top + (_screenR.height() - 264) / 2;
 
-	const bool useColor = (_hasMacColors && _renderMode == Common::kRenderMacintosh
+	const bool useColor = (isMacColorMode()
 		&& !_animBMColors.empty());
 
 	// Fill background patterns (416x264 area).
@@ -728,7 +728,7 @@ void ColonyEngine::drawAnimation() {
 	// did 416*264 = 109,824 individual setPixel calls per drawAnimation, each
 	// issuing its own glBegin/glEnd; on the OpenGL backend that took tens of
 	// milliseconds per frame and starved the cursor of refreshes.
-	const int patternMode = useColor ? 2 : (_renderMode == Common::kRenderMacintosh ? 1 : 0);
+	const int patternMode = useColor ? 2 : (isMacRenderMode() ? 1 : 0);
 	uint32 topColor = 0, botColor = 0;
 	if (useColor) {
 		const bool powered = (_corePower[_coreIndex] > 0);
@@ -833,7 +833,7 @@ void ColonyEngine::drawComplexSprite(int index, int ox, int oy) {
 
 	// Resolve fill color from BMColor[index+2] (ganimate.c DrawlSprite).
 	uint32 fillColor = 0xFFFFFFFF; // B&W default: white
-	const bool useColor = (_hasMacColors && _renderMode == Common::kRenderMacintosh
+	const bool useColor = (isMacColorMode()
 		&& !_animBMColors.empty());
 	if (useColor) {
 		int bmIdx = index + 2;
@@ -857,8 +857,8 @@ void ColonyEngine::drawAnimationImage(Image *img, Image *mask, int x, int y, uin
 	//   fg bit=0   -> BackColor (fillColor from BMColor)
 	// Mac B&W: same ??? fg bit=1 is black (0), fg bit=0 is white (15).
 	// DOS MetaWINDOW: OPPOSITE ??? fg bit=1 is white (15), fg bit=0 is black (0).
-	const bool useColor = (_hasMacColors && _renderMode == Common::kRenderMacintosh);
-	const bool isMacMode = (_renderMode == Common::kRenderMacintosh);
+	const bool useColor = isMacColorMode();
+	const bool isMacMode = isMacRenderMode();
 
 	// Pixels written into the alpha-keyed RGBA cache. mask=0 ??? alpha 0
 	// (transparent), so drawSurface's alpha-blend skips them naturally.
diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index c6b5c121211..abdeed7de21 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -166,7 +166,7 @@ ColonyEngine::ColonyEngine(OSystem *syst, const ADGameDescription *gd) : Engine(
 			_renderMode = Common::kRenderEGA;
 	}
 
-	_wireframe = (_renderMode != Common::kRenderMacintosh);
+	_wireframe = !isMacRenderMode();
 	_fullscreen = false;
 	_speedShift = 2; // DOS default: speedshift=1, but 2 feels better with our frame rate
 	_moveForward = false;
@@ -487,7 +487,7 @@ void ColonyEngine::updateMouseCapture(bool recenter) {
 
 	int cursorMode = 0;
 
-	if (!_mouseLocked && _renderMode == Common::kRenderMacintosh && _wm) {
+	if (!_mouseLocked && isMacRenderMode() && _wm) {
 		if (_macCrossCursor) {
 			cursorMode = 1;
 			_wm->replaceCursor(Graphics::kMacCursorCustom, _macCrossCursor);
@@ -585,7 +585,7 @@ void ColonyEngine::handleMenuAction(int action) {
 }
 
 void ColonyEngine::initMacMenus() {
-	if (_renderMode != Common::kRenderMacintosh) {
+	if (!isMacRenderMode()) {
 		_menuBarHeight = 0;
 		return;
 	}
@@ -775,7 +775,7 @@ Common::Error ColonyEngine::run() {
 	// Original Mac Colony: rScreen capped at 640x480 (inits.c lines 111-112).
 	// DOS EGA: 640x350 with non-square pixels displayed at 4:3.
 	// Mac uses square pixels at native 640x480.
-	if (_renderMode == Common::kRenderMacintosh) {
+	if (isMacRenderMode()) {
 		_width = 640;
 		_height = 480;
 	} else {
@@ -791,6 +791,7 @@ Common::Error ColonyEngine::run() {
 	if (!_gfx)
 		return Common::kUserCanceled;
 
+	loadMacColors();
 	updateViewportLayout();
 	const Graphics::PixelFormat format = _system->getScreenFormat();
 	debugC(1, kColonyDebugRender, "Screen format: %d bytesPerPixel. Actual size: %dx%d", format.bytesPerPixel, _width, _height);
@@ -814,8 +815,7 @@ Common::Error ColonyEngine::run() {
 		pal[i * 3 + 2] = i;
 	}
 
-	loadMacColors();
-	if (_hasMacColors) {
+	if (isMacColorMode()) {
 		for (int i = 0; i < 145; i++) {
 			pal[(100 + i) * 3 + 0] = _macColors[i].fg[0] >> 8;
 			pal[(100 + i) * 3 + 1] = _macColors[i].fg[1] >> 8;
diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index 84a8d14ad0e..b4be8d94fb9 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -451,6 +451,8 @@ public:
 	Common::Platform getPlatform() const { return _gameDescription->platform; }
 	bool isSoundEnabled() const { return _soundOn; }
 	const Graphics::Surface *getSavedScreen() const { return _savedScreen; }
+	bool isMacRenderMode() const { return _renderMode == Common::kRenderMacintosh || _renderMode == Common::kRenderMacintoshBW; }
+	bool isMacColorMode() const { return _renderMode == Common::kRenderMacintosh && _hasMacColors; }
 
 	void initTrig();
 	void loadMacColors();
diff --git a/engines/colony/interaction.cpp b/engines/colony/interaction.cpp
index ca543bca7e0..3f791c564d4 100644
--- a/engines/colony/interaction.cpp
+++ b/engines/colony/interaction.cpp
@@ -475,7 +475,7 @@ void ColonyEngine::destroyRobot(int num) {
 // bottom-left and bottom-right corners of the viewport converging at the aim
 // point, plus a small filled oval at center. Simulates a rifle-barrel perspective.
 void ColonyEngine::doShootCircles(int cx, int cy) {
-	const bool isMac = (_renderMode == Common::kRenderMacintosh);
+	const bool isMac = isMacRenderMode();
 
 	if (isMac) {
 		// Mac shoot.c CShoot(): patXor diagonal lines radiating from aim point
@@ -541,7 +541,7 @@ void ColonyEngine::doShootCircles(int cx, int cy) {
 // SHOOT.C doBurnHole(): expanding concentric random rays in blue/yellow/white
 // when a robot is hit. Creates an "explosion" effect at the hit location.
 void ColonyEngine::doBurnHole(int cx, int cy, int radius) {
-	const bool isMac = (_renderMode == Common::kRenderMacintosh);
+	const bool isMac = isMacRenderMode();
 
 	if (isMac) {
 		// Mac: InvertOval at robot bounds
@@ -605,7 +605,7 @@ void ColonyEngine::meGetShot() {
 	if (vw <= 0 || vh <= 0)
 		return;
 
-	const bool isMac = (_renderMode == Common::kRenderMacintosh);
+	const bool isMac = isMacRenderMode();
 
 	if (isMac) {
 		// Mac shoot.c: InvertRect(&Clip) ??? full viewport flash
diff --git a/engines/colony/intro.cpp b/engines/colony/intro.cpp
index 78a93b8b985..e345cf30a5f 100644
--- a/engines/colony/intro.cpp
+++ b/engines/colony/intro.cpp
@@ -140,7 +140,7 @@ public:
 };
 
 int ColonyEngine::runMacEndgameDialog(const Common::String &message) {
-	if (_renderMode != Common::kRenderMacintosh || !_wm || !_menuSurface || !_gfx)
+	if (!isMacRenderMode() || !_wm || !_menuSurface || !_gfx)
 		return Graphics::kMacDialogQuitRequested;
 
 	if (_macMenu && _wm->isMenuActive())
@@ -502,7 +502,7 @@ bool ColonyEngine::scrollInfo(const Graphics::Font *macFont) {
 	// Set up gradient palette entries (200-213) for story text
 	// Mac original: tColor.blue starts at 0xFFFF and decreases by 4096 per visible line
 	// B&W Mac: white gradient instead of blue
-	const bool bwMac = (macFont && !_hasMacColors);
+	const bool bwMac = (macFont && !isMacColorMode());
 	byte pal[14 * 3]; // storyLength entries
 	memset(pal, 0, sizeof(pal));
 	for (int i = 0; i < storyLength; i++) {
@@ -968,7 +968,7 @@ bool ColonyEngine::timeSquare(const Common::String &str, const Graphics::Font *m
 
 	int centery = _height / 2 - 10;
 
-	const bool bwMac = (macFont && !_hasMacColors);
+	const bool bwMac = (macFont && !isMacColorMode());
 	const bool macStyle = (macFont != nullptr);
 	const uint32 grayIndex = 160;
 	const uint32 textIndex = 176;
@@ -1214,7 +1214,7 @@ void ColonyEngine::terminateGame(bool blowup) {
 	_centerX = savedCenterX;
 	_centerY = savedCenterY;
 
-	if (_renderMode == Common::kRenderMacintosh) {
+	if (isMacRenderMode()) {
 		while (!shouldQuit()) {
 			switch (runMacEndgameDialog(_("You have been terminated."))) {
 			case 0:
diff --git a/engines/colony/movement.cpp b/engines/colony/movement.cpp
index 84c40706357..08649bf0b99 100644
--- a/engines/colony/movement.cpp
+++ b/engines/colony/movement.cpp
@@ -786,7 +786,7 @@ int ColonyEngine::tryPassThroughFeature(int fromX, int fromY, int direction, Loc
 
 void ColonyEngine::playTunnelAirlockEffect() {
 	const Common::Rect effectRect(0, _menuBarHeight, _width, _height);
-	const bool macColor = (_renderMode == Common::kRenderMacintosh && _hasMacColors);
+	const bool macColor = isMacColorMode();
 	const int tunnelColor = 24; // c_tunnel
 	const uint32 fillFg = macColor ? packMacColor(_macColors[tunnelColor].fg) : 0;
 	const uint32 fillBg = macColor ? packMacColor(_macColors[tunnelColor].bg) : 0;
@@ -883,7 +883,7 @@ void ColonyEngine::playTunnelEffect(bool falling) {
 	// Original TUNNEL.C: falling into the reactor reuses the tunnel renderer
 	// with the falling flag set, which removes the tracks and shortens the run.
 	const Common::Rect effectRect(0, _menuBarHeight, _width, _height);
-	const bool macColor = (_renderMode == Common::kRenderMacintosh && _hasMacColors);
+	const bool macColor = isMacColorMode();
 	const int tunnelColor = 24; // c_tunnel
 	const int tunnelFrames = falling ? 10 : 49;
 	const uint32 fillFg = macColor ? packMacColor(_macColors[tunnelColor].fg) : 0;
diff --git a/engines/colony/render.cpp b/engines/colony/render.cpp
index d40a6de5edf..32cf7e59df8 100644
--- a/engines/colony/render.cpp
+++ b/engines/colony/render.cpp
@@ -522,7 +522,7 @@ void ColonyEngine::draw3DPrism(Thing &obj, const PrismPartDef &def, bool useLook
 				continue;
 			}
 
-			if (_renderMode == Common::kRenderMacintosh && _hasMacColors) {
+			if (isMacColorMode()) {
 				// Mac color rendering: follows SuperPoly() from calcrobo.c:429-505.
 				int pattern;
 				uint32 fg;
@@ -574,7 +574,7 @@ void ColonyEngine::draw3DPrism(Thing &obj, const PrismPartDef &def, bool useLook
 					_gfx->setStippleData(nullptr);
 				}
 			} else if (lit) {
-				if (_renderMode == Common::kRenderMacintosh) {
+				if (isMacRenderMode()) {
 					// Mac B&W: stipple dither pattern fill + black outline
 					int pattern;
 					if (colorIdx == kColorCorridorWall) {
@@ -632,10 +632,10 @@ void ColonyEngine::draw3DLeaf(const Thing &obj, const PrismPartDef &def) {
 	const bool lit = (_corePower[_coreIndex] > 0);
 	// Mac color: c_plant bg; Mac B&W: black; EGA: green; unlit: white/black
 	uint32 color;
-	if (_renderMode == Common::kRenderMacintosh && _hasMacColors) {
+	if (isMacColorMode()) {
 		color = lit ? packMacColor(_macColors[63].bg) : 0xFF000000; // c_plant
 	} else {
-		color = lit ? (_renderMode == Common::kRenderMacintosh ? 0 : 2) : 15;
+		color = lit ? (isMacRenderMode() ? 0 : 2) : 15;
 	}
 
 	for (int i = 0; i < def.surfaceCount; i++) {
@@ -740,7 +740,7 @@ void ColonyEngine::draw3DSphere(Thing &obj, int pt0x, int pt0y, int pt0z,
 		}
 	}
 
-	if (_renderMode == Common::kRenderMacintosh && _hasMacColors) {
+	if (isMacColorMode()) {
 		// Mac color: map fillColor to Mac color index and use RGB
 		// fillColor is an ObjColor enum value passed by the caller
 		const int fillIdx = mapObjColorToMacColor((int)fillColor, _level);
@@ -760,7 +760,7 @@ void ColonyEngine::draw3DSphere(Thing &obj, int pt0x, int pt0y, int pt0z,
 		if (stipple)
 			_gfx->setStippleData(nullptr);
 	} else if (lit) {
-		if (_renderMode == Common::kRenderMacintosh) {
+		if (isMacRenderMode()) {
 			int pattern = lookupMacPattern((int)fillColor, _level);
 			if (pattern == kPatternClear)
 				pattern = kPatternGray;
@@ -872,11 +872,11 @@ void ColonyEngine::renderCorridor3D() {
 	computeVisibleCells();
 
 	bool lit = (_corePower[_coreIndex] > 0);
-	bool macMode = (_renderMode == Common::kRenderMacintosh);
+	bool macMode = isMacRenderMode();
 
 	uint32 wallFill, wallLine, floorColor, ceilColor;
 
-	if (macMode && _hasMacColors) {
+	if (isMacColorMode()) {
 		if (lit) {
 			// Mac Display(): wallColor = cColor[c_char0+level-1].f (level-specific color).
 			// SuperPoly(c_lwall) uses wallColor as fill, giving all walls the level tint.
@@ -928,7 +928,7 @@ void ColonyEngine::renderCorridor3D() {
 	// Mac color mode: original corridor renderer only showed ceiling edges at wall
 	// boundaries (via 2D perspective), not a full-map grid. Wall tops from draw3DWall
 	// already provide the ceiling lines where walls exist.
-	if (!(macMode && _hasMacColors)) {
+	if (!isMacColorMode()) {
 		for (int i = 0; i <= 32; i++) {
 			float d = i * 256.0f;
 			float maxD = 32.0f * 256.0f;
diff --git a/engines/colony/render_features.cpp b/engines/colony/render_features.cpp
index 8534a898db8..ec211480f47 100644
--- a/engines/colony/render_features.cpp
+++ b/engines/colony/render_features.cpp
@@ -252,8 +252,8 @@ void ColonyEngine::wallChar(const float corners[4][3], uint8 cnum) {
 	if (!data || data[0] == 0)
 		return;
 
-	const bool macMode = (_renderMode == Common::kRenderMacintosh);
-	const bool macColors = (macMode && _hasMacColors);
+	const bool macMode = isMacRenderMode();
+	const bool macColors = isMacColorMode();
 	const uint32 fillColor = macColors ? packMacColor(_macColors[8 + _level - 1].bg) : 0;
 	const uint32 lineColor = macColors ? (uint32)0xFF000000 : 0;
 
@@ -354,7 +354,7 @@ void ColonyEngine::wallChar(const float corners[4][3], uint8 cnum) {
 	}
 
 	if (macMode) {
-		const uint32 wallFill = _hasMacColors
+		const uint32 wallFill = macColors
 			? packMacColor(_macColors[8 + _level - 1].fg)
 			: (uint32)255;
 		_gfx->setWireframe(true, wallFill);
@@ -393,8 +393,8 @@ void ColonyEngine::drawCellFeature3D(int cellX, int cellY) {
 	bool lit = (_corePower[_coreIndex] > 0);
 	uint32 holeColor = lit ? 0 : 7;
 
-	const bool macMode = (_renderMode == Common::kRenderMacintosh);
-	const bool macColors = (macMode && _hasMacColors);
+	const bool macMode = isMacRenderMode();
+	const bool macColors = isMacColorMode();
 
 	// Helper lambda: draw a filled hole polygon with Mac color or B&W fallback
 	auto drawHolePoly = [&](const float *u, const float *v, int cnt, int macIdx) {
@@ -484,8 +484,8 @@ void ColonyEngine::drawWallFeature3D(int cellX, int cellY, int direction) {
 
 	float corners[4][3];
 	getWallFace3D(cellX, cellY, direction, corners);
-	const bool macMode = (_renderMode == Common::kRenderMacintosh);
-	const bool macColors = (_renderMode == Common::kRenderMacintosh && _hasMacColors);
+	const bool macMode = isMacRenderMode();
+	const bool macColors = isMacColorMode();
 	const bool lit = (_corePower[_coreIndex] > 0);
 	const uint32 wallFeatureFill = macColors
 		? packMacColor(lit ? _macColors[8 + _level - 1].fg : _macColors[6].bg)
@@ -653,7 +653,7 @@ void ColonyEngine::drawWallFeature3D(int cellX, int cellY, int direction) {
 		const uint32 col = macColors ? (uint32)0xFF000000 : 0; // vBLACK
 
 		// Mac: fill entire wall face (c_upstairs)
-		if (_renderMode == Common::kRenderMacintosh) {
+		if (isMacRenderMode()) {
 			float uf[4] = {0.0f, 1.0f, 1.0f, 0.0f};
 			float vf2[4] = {0.0f, 0.0f, 1.0f, 1.0f};
 			if (macColors) {
@@ -726,7 +726,7 @@ void ColonyEngine::drawWallFeature3D(int cellX, int cellY, int direction) {
 		const uint32 col = macColors ? (uint32)0xFF000000 : 0; // vBLACK
 
 		// Mac: fill entire wall face (c_dnstairs)
-		if (_renderMode == Common::kRenderMacintosh) {
+		if (isMacRenderMode()) {
 			float uf[4] = {0.0f, 1.0f, 1.0f, 0.0f};
 			float vf2[4] = {0.0f, 0.0f, 1.0f, 1.0f};
 			if (macColors) {
@@ -830,7 +830,7 @@ void ColonyEngine::drawWallFeature3D(int cellX, int cellY, int direction) {
 		// Tunnel: hexagonal opening from Grid (0,0 0,5 1,6 5,6 6,5 6,0)
 		const float uT[6] = { 0.0f,    0.0f,    1/6.0f,  5/6.0f,  1.0f,    1.0f };
 		const float vT[6] = { 0.0f,    0.750f,  0.875f,  0.875f,  0.750f,  0.0f };
-		if (_renderMode == Common::kRenderMacintosh) {
+		if (isMacRenderMode()) {
 			if (macColors) {
 				macFillPoly(uT, vT, 6, 24); // c_tunnel
 			} else {
@@ -894,7 +894,7 @@ void ColonyEngine::drawWallFeature3D(int cellX, int cellY, int direction) {
 		// map[1..4] = pattern ID per band (0=WHITE, 1=LTGRAY, 2=GRAY, 3=DKGRAY, 4=BLACK).
 		// Values >= 5 trigger animation: color = (map[i+1] + _displayCount) % 5.
 		// Band 0 (top): v=0.75..1.0, Band 1: v=0.5..0.75, Band 2: v=0.25..0.5, Band 3: v=0..0.25.
-		if (_renderMode == Common::kRenderMacintosh) {
+		if (isMacRenderMode()) {
 			if (macColors) {
 				// Mac drawColor: map[i+1] selects color (0???c_color0..3???c_color3, 4???BLACK).
 				// Values >= 5: animated = (map[i+1] + _displayCount) % 5.
diff --git a/engines/colony/render_objects.cpp b/engines/colony/render_objects.cpp
index a13f4076449..a93feac780c 100644
--- a/engines/colony/render_objects.cpp
+++ b/engines/colony/render_objects.cpp
@@ -1271,7 +1271,7 @@ void ColonyEngine::drawPrismOval3D(Thing &thing, const PrismPartDef &def, bool u
 		pz[i] = centerZ + ca * axisHZ + sa * axisVZ;
 	}
 
-	if (_renderMode == Common::kRenderMacintosh && _hasMacColors) {
+	if (isMacColorMode()) {
 		const int macColorIdx = mapEyeOverlayColorToMacColor(fillColorIdx, _level);
 		int pattern = _macColors[macColorIdx].pattern;
 		uint32 fg = packEyeOverlayMacColor(_macColors[macColorIdx].fg);
@@ -1288,7 +1288,7 @@ void ColonyEngine::drawPrismOval3D(Thing &thing, const PrismPartDef &def, bool u
 		if (stipple)
 			_gfx->setStippleData(nullptr);
 	} else if (lit) {
-		if (_renderMode == Common::kRenderMacintosh) {
+		if (isMacRenderMode()) {
 			int pattern = mapEyeOverlayColorToMacPattern(fillColorIdx);
 			if (pattern == kPatternClear)
 				return;
@@ -1375,13 +1375,13 @@ bool ColonyEngine::drawStaticObjectPrisms3D(Thing &obj) {
 		_gfx->setDepthRange(0.0f, 1.0f);
 		break;
 	case kObjFWall:
-		if (_renderMode == Common::kRenderMacintosh)
+		if (isMacRenderMode())
 			draw3DPrism(obj, kFWallPart, false, kColorCorridorWall, true, true);
 		else
 			draw3DPrism(obj, kFWallPart, false, -1, true, true);
 		break;
 	case kObjCWall:
-		if (_renderMode == Common::kRenderMacintosh)
+		if (isMacRenderMode())
 			draw3DPrism(obj, kCWallPart, false, kColorCorridorWall, true, true);
 		else
 			draw3DPrism(obj, kCWallPart, false, -1, true, true);
@@ -1715,7 +1715,7 @@ bool ColonyEngine::drawStaticObjectPrisms3D(Thing &obj) {
 			Thing &nearEye = leftFirst ? rightEye : leftEye;
 			const PrismPartDef &farWing = leftFirst ? kQLWingDef : kQRWingDef;
 			const PrismPartDef &nearWing = leftFirst ? kQRWingDef : kQLWingDef;
-			const int wingColor = (_renderMode != Common::kRenderMacintosh && _level == 7) ? kColorQueenWingRed : kColorClear;
+			const int wingColor = (!isMacRenderMode() && _level == 7) ? kColorQueenWingRed : kColorClear;
 
 			// DOS draweyes(): queen eyeball is RED fill + WHITE outline
 			// (hardcoded, not from color table). Iris is GREEN.
diff --git a/engines/colony/ui.cpp b/engines/colony/ui.cpp
index e73ce82bc1c..5b0cf0516d2 100644
--- a/engines/colony/ui.cpp
+++ b/engines/colony/ui.cpp
@@ -161,18 +161,16 @@ bool drawMacTextPopup(Graphics::MacWindowManager *wm, Renderer *gfx,
 	return true;
 }
 
-// Load a PICT resource from the Mac resource fork, returning a new RGB surface.
-// Try Color Colony first (has color dashboard PICTs), then fall back to B&W Colony.
+// Load a dashboard PICT resource from the Mac resource fork, returning a new
+// RGB surface. The companion Color Colony resource fork contains the complete
+// dashboard PICT set, so prefer it when available and fall back to the base app.
 // Caller owns the returned surface. Returns nullptr on failure.
 Graphics::Surface *ColonyEngine::loadPictSurface(int resID) {
 	Common::SeekableReadStream *pictStream = nullptr;
 
-	// Try Color Colony resource fork first
-	if (_colorResMan && _colorResMan->hasResFork()) {
+	if (_colorResMan && _colorResMan->hasResFork())
 		pictStream = _colorResMan->getResource(MKTAG('P', 'I', 'C', 'T'), (int16)resID);
-	}
 
-	// Fall back to B&W Colony resource fork
 	if (!pictStream && _resMan && (_resMan->isMacFile() || _resMan->hasResFork())) {
 		pictStream = _resMan->getResource(MKTAG('P', 'I', 'C', 'T'), (int16)resID);
 	}
@@ -235,8 +233,7 @@ Graphics::Surface *ColonyEngine::loadPictSurface(int resID) {
 
 // Draw a PICT surface at a specific destination position. The surface was
 // created by loadPictSurface() in the exact ARGB layout the renderer's
-// drawSurface() consumes, so this is a single textured-quad blit ??? no need
-// to walk pixels.
+// drawSurface() consumes, so this is a single textured-quad blit.
 void ColonyEngine::drawPictAt(Graphics::Surface *surf, int destX, int destY) {
 	if (!surf)
 		return;
@@ -252,7 +249,7 @@ void ColonyEngine::updateViewportLayout() {
 		return Common::Rect(left, top, right, bottom);
 	};
 
-	const bool isMac = (_renderMode == Common::kRenderMacintosh);
+	const bool isMac = isMacRenderMode();
 
 	// Original IBM_INIT.C: pix_per_Qinch = pixResX/4, pixResY/4
 	// MetaWINDOW EGA 640x350: pixResX=96, pixResY=72 ??? pQx=24, pQy=18
@@ -293,12 +290,35 @@ void ColonyEngine::updateViewportLayout() {
 	if (isMac) {
 		// Original Mac layout from inits.c/compass.c/power.c:
 		// screenR.left = 96  sidebar is 96px wide.
-		// Two floating windows centered in sidebar over gray desktop.
+		// Two floating windows over gray desktop.
 		// moveWindow: compRect = (0,0, 2*CCENTER, 3*CCENTER) = (0,0, 70, 105)
 		//   floorRect (minimap) = (8,8)-(62,62)  54x54 inside moveWindow
 		//   compass dish below at (19,66)-(51,98), needle center at (35,82)
-		// infoWindow: sized from PICT resource, positioned above moveWindow
+		// infoWindow: original WIND 10930 rect (9,26)-(79,189).
 		const int CCENTER = 35;
+		auto loadWindowRect = [this, &makeSafeRect](int resID, const Common::Rect &fallback) {
+			Common::SeekableReadStream *windStream = nullptr;
+			if (_resMan && (_resMan->isMacFile() || _resMan->hasResFork()))
+				windStream = _resMan->getResource(MKTAG('W', 'I', 'N', 'D'), (int16)resID);
+			if (!windStream && _colorResMan && _colorResMan->hasResFork())
+				windStream = _colorResMan->getResource(MKTAG('W', 'I', 'N', 'D'), (int16)resID);
+
+			if (!windStream)
+				return fallback;
+
+			Common::Rect r = fallback;
+			if (windStream->size() >= 8) {
+				const int top = windStream->readSint16BE();
+				const int left = windStream->readSint16BE();
+				const int bottom = windStream->readSint16BE();
+				const int right = windStream->readSint16BE();
+				r = makeSafeRect(left, top, right, bottom);
+			}
+			delete windStream;
+			return r;
+		};
+
+		const Common::Rect infoWindow = loadWindowRect(10930, Common::Rect(9, 26, 79, 189));
 
 		// Load PICT surfaces (cached after first load)
 		if (!_pictCompass)
@@ -312,23 +332,24 @@ void ColonyEngine::updateViewportLayout() {
 			if (_armor > 0)
 				wantID = -32755;
 			else
-				wantID = _hasMacColors ? -32761 : -32752;
+				wantID = isMacColorMode() ? -32761 : -32752;
 			_pictPower = loadPictSurface(wantID);
 			if (!_pictPower && _armor > 0 && wantID != -32755)
 				_pictPower = loadPictSurface(-32755);
 			_pictPowerID = _pictPower ? wantID : 0;
 		}
 
-		// moveWindow dimensions from original constants
+		// Keep the ScummVM dashboard's bottom moveWindow placement. The original
+		// Mac window resource is for a compact 512x342 desktop and should not
+		// move this panel upward on our larger Mac render surface.
 		const int moveW = 2 * CCENTER; // 70
 		const int moveH = 3 * CCENTER; // 105
-		const int infoW = _pictPower ? _pictPower->w : moveW;
-		const int infoH = _pictPower ? _pictPower->h : moveH;
+		const int infoW = infoWindow.width();
+		const int infoH = infoWindow.height();
 
-		// Center panels horizontally in sidebar
 		const int centerX = dashWidth / 2;
 
-		// Position moveWindow at the bottom of the sidebar
+		// Position moveWindow at the bottom of the sidebar.
 		const int moveLeft = MAX(0, centerX - moveW / 2);
 		const int moveTop = _height - pad - moveH;
 
@@ -340,11 +361,12 @@ void ColonyEngine::updateViewportLayout() {
 		// _compassRect = entire moveWindow (used for compass dish drawing)
 		_compassRect = makeSafeRect(moveLeft, moveTop, moveLeft + moveW, moveTop + moveH);
 
-		// Position infoWindow below the Mac menu bar.
-		// Original PICT is drawn at (-2,-2) in window-local coords, so offset
-		// the panel by 2px to prevent the PICT from overlapping the menu bar.
-		const int infoLeft = MAX(0, centerX - infoW / 2);
-		const int infoTop = menuTop + pad;
+		int infoLeft = infoWindow.left;
+		int infoTop = infoWindow.top;
+		if (infoLeft + infoW > dashWidth)
+			infoLeft = MAX(0, dashWidth - infoW);
+		if (infoTop + infoH > _height)
+			infoTop = MAX(menuTop, _height - pad - infoH);
 		_powerRect = makeSafeRect(infoLeft, infoTop, infoLeft + infoW, infoTop + infoH);
 	} else {
 		// DASHBOAR.C RCompass(): compOval before shrink
@@ -428,7 +450,7 @@ void ColonyEngine::drawDashboardStep1() {
 	if (_dashBoardRect.width() <= 0 || _dashBoardRect.height() <= 0)
 		return;
 
-	const bool isMac = (_renderMode == Common::kRenderMacintosh);
+	const bool isMac = isMacRenderMode();
 
 	if (isMac) {
 		drawDashboardMac();
@@ -521,7 +543,7 @@ void ColonyEngine::drawDashboardStep1() {
 // Original Mac had two floating windows (infoWindow + moveWindow) over gray desktop.
 
 void ColonyEngine::drawDashboardMac() {
-	const bool macColor = _hasMacColors;
+	const bool macColor = isMacColorMode();
 	const uint32 colBlack = packRGB(0, 0, 0);
 	const uint32 colWhite = packRGB(255, 255, 255);
 	const uint32 colWinBg = macColor ? packMacColor(_macColors[7].bg) : colWhite;
@@ -580,27 +602,44 @@ void ColonyEngine::drawDashboardMac() {
 		// In the original B&W game, GetPicture(-32752) returns null when !armor,
 		// so DrawPicture is a no-op ??? the window just shows white fill.
 		if (_pictPower)
-			drawPictAt(_pictPower, _powerRect.left - 2, _powerRect.top - 2);
+			drawPictAt(_pictPower, _powerRect.left + 1, _powerRect.top + 1);
+
+		if (!macColor) {
+			// Match the B&W Window Manager shadow as pixels. The shadow is
+			// staggered over the checkerboard desktop; a solid rect is too wide.
+			_gfx->fillRect(Common::Rect(_powerRect.right, _powerRect.top - 1,
+				_powerRect.right + 1, _powerRect.bottom + 3), colBlack);
+			_gfx->fillRect(Common::Rect(_powerRect.right + 1, _powerRect.top,
+				_powerRect.right + 2, _powerRect.bottom + 4), colBlack);
+			_gfx->fillRect(Common::Rect(_powerRect.right + 2, _powerRect.top - 1,
+				_powerRect.right + 3, _powerRect.top), colBlack);
+			_gfx->fillRect(Common::Rect(_powerRect.right + 2, _powerRect.top + 1,
+				_powerRect.right + 3, _powerRect.bottom + 3), colBlack);
+			_gfx->fillRect(Common::Rect(_powerRect.left - 2, _powerRect.bottom,
+				_powerRect.right + 3, _powerRect.bottom + 1), colBlack);
+			_gfx->fillRect(Common::Rect(_powerRect.left + 1, _powerRect.bottom + 1,
+				_powerRect.right + 4, _powerRect.bottom + 2), colBlack);
+			_gfx->fillRect(Common::Rect(_powerRect.left, _powerRect.bottom + 2,
+				_powerRect.right + 3, _powerRect.bottom + 3), colBlack);
+		}
 
 		// Blue bars only when armored (power.c: if(armor) { ... ForeColor(blueColor) ... })
 		if (_armor > 0 && _pictPower) {
-			// power.c info rect adjustments: left+=3,right+=3,top-=3,bottom-=3, then ++/--
-			// Net effect: info is adjusted relative to PICT position
-			const int infoLeft = _powerRect.left - 2 + 2;  // -2 (PICT offset) +3-1 = 0, +2 net
-			const int infoBottom = _powerRect.top - 2 + _pictPower->h - 2; // PICT bottom adjusted
-			const int bot = infoBottom - 27; // power.c: bot = info.bottom - 27
+			// power.c draws the bars with QuickDraw MoveTo/LineTo after shifting
+			// the PICT rect. GL line rasterization does not cover the same pixels,
+			// so draw the observed 18x2 bar strips explicitly.
+			const int infoLeft = _powerRect.left - 1;
+			const int bot = _powerRect.bottom - 27; // power.c: bot = info.bottom - 27
 
 			for (int i = 0; i < 3; i++) {
-				// power.c: lft = 3 + info.left + i*23
+				// Reference B&W frame: columns start at x 10, 33, 56; bars are
+				// inset by 2 pixels on each side: x 12..29, 35..52, 58..75.
 				const int lft = 3 + infoLeft + i * 23;
 				for (int j = 0; j < ePower[i] && j < 20; j++) {
 					const int ln = bot - 3 * j;
 					if (ln <= _powerRect.top)
 						break;
-					// power.c: MoveTo(lft+1,ln); LineTo(lft+16,ln);  16px wide, 2 lines
-					_gfx->drawLine(lft + 1, ln, lft + 16, ln, colBlue);
-					if (ln - 1 > _powerRect.top)
-						_gfx->drawLine(lft + 1, ln - 1, lft + 16, ln - 1, colBlue);
+					_gfx->fillRect(Common::Rect(lft + 1, ln - 1, lft + 19, ln + 1), colBlue);
 				}
 			}
 		}
@@ -694,7 +733,7 @@ void ColonyEngine::drawMiniMap(uint32 lineColor) {
 			_gfx->drawLine(x1, y1, x2, y2, color);
 	};
 
-	const bool isMac = (_renderMode == Common::kRenderMacintosh);
+	const bool isMac = isMacRenderMode();
 
 	int lExt, sExt, xloc, yloc, ccenterx, ccentery;
 	if (isMac) {
@@ -921,8 +960,8 @@ void ColonyEngine::drawAutomap() {
 		return;
 
 	const int lv = _level - 1;
-	const bool isMac = (_renderMode == Common::kRenderMacintosh);
-	const bool macColor = isMac && _hasMacColors;
+	const bool isMac = isMacRenderMode();
+	const bool macColor = isMacColorMode();
 
 	const Common::Rect vp(0, _menuBarHeight, _width, _height);
 	const int vpW = vp.width();
@@ -1014,7 +1053,7 @@ void ColonyEngine::drawForkliftOverlay() {
 	const int w = _screenR.width();
 	const int h = _screenR.height();
 	const int cx = w / 2;
-	const uint32 color = (_renderMode == Common::kRenderMacintosh) ? packRGB(0, 0, 0) : 0;
+	const uint32 color = isMacRenderMode() ? packRGB(0, 0, 0) : 0;
 
 	const int tx2 = cx >> 2;  // centerX/4
 	const int tx1 = cx >> 1;  // centerX/2
@@ -1031,7 +1070,7 @@ void ColonyEngine::drawCrosshair() {
 	if (!_crosshair || _screenR.width() <= 0 || _screenR.height() <= 0)
 		return;
 
-	const bool isMac = (_renderMode == Common::kRenderMacintosh);
+	const bool isMac = isMacRenderMode();
 	if (isMac && _cursorShoot && !_mouseLocked && _weapons > 0)
 		return;
 
@@ -1039,7 +1078,7 @@ void ColonyEngine::drawCrosshair() {
 	if (isMac) {
 		// Mac: black when powered, gray when no weapons, white when armed but no power
 		// B&W: no gray, so powered=black, else white
-		if (_hasMacColors)
+		if (isMacColorMode())
 			color = (_corePower[_coreIndex] > 0) ? packRGB(0, 0, 0)
 				: (_weapons > 0) ? packRGB(255, 255, 255) : packRGB(128, 128, 128);
 		else
@@ -1109,8 +1148,8 @@ void ColonyEngine::printMessage(const char *text[], bool hold) {
 		numLines++;
 	}
 
-	if (_renderMode == Common::kRenderMacintosh && drawMacTextPopup(_wm, _gfx,
-			_width, _height, _centerX, _centerY, lines, Graphics::kTextAlignCenter, _hasMacColors)) {
+	if (isMacRenderMode() && drawMacTextPopup(_wm, _gfx,
+			_width, _height, _centerX, _centerY, lines, Graphics::kTextAlignCenter, isMacColorMode())) {
 		if (hold)
 			waitForInput();
 		return;
@@ -1226,13 +1265,13 @@ void ColonyEngine::doText(int entry, int center) {
 	if (maxlines > (int)lineArray.size())
 		maxlines = lineArray.size();
 
-	if (_renderMode == Common::kRenderMacintosh) {
+	if (isMacRenderMode()) {
 		Common::Array<Common::String> popupLines;
 		for (int i = 0; i < maxlines; ++i)
 			popupLines.push_back(lineArray[i]);
 		popupLines.push_back((int)lineArray.size() > maxlines ? kmore : kpress);
 		if (drawMacTextPopup(_wm, _gfx, _width, _height, _centerX, _centerY, popupLines,
-				center == 1 ? Graphics::kTextAlignCenter : Graphics::kTextAlignLeft, _hasMacColors)) {
+				center == 1 ? Graphics::kTextAlignCenter : Graphics::kTextAlignLeft, isMacColorMode())) {
 			waitForInput();
 			delete[] page;
 			return;


Commit: 9b0fb9204204cb43b003148c5f8740c682c8519a
    https://github.com/scummvm/scummvm/commit/9b0fb9204204cb43b003148c5f8740c682c8519a
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-05T13:24:41+02:00

Commit Message:
COLONY: fixed xor mode in shader rendering

Changed paths:
    engines/colony/renderer_opengl_shaders.cpp


diff --git a/engines/colony/renderer_opengl_shaders.cpp b/engines/colony/renderer_opengl_shaders.cpp
index 0639d28f5da..9315a5c8f9b 100644
--- a/engines/colony/renderer_opengl_shaders.cpp
+++ b/engines/colony/renderer_opengl_shaders.cpp
@@ -45,8 +45,6 @@
 namespace Colony {
 
 // Phase 3: 2D primitives + the 3D corridor draw path are programmable.
-// XOR mode and polygon stipple are intentionally left stubbed ??? see the
-// renderer audit for why those are deferred.
 class OpenGLShaderRenderer : public Renderer {
 public:
 	OpenGLShaderRenderer(OSystem *system, int width, int height);
@@ -77,7 +75,7 @@ public:
 
 	void copyToScreen() override;
 	void setWireframe(bool enable, int64_t fillColor) override;
-	void setXorMode(bool enable) override {}
+	void setXorMode(bool enable) override;
 	void setStippleData(const byte *data) override;
 	void setMacColors(uint32 fg, uint32 bg) override;
 	void setDepthState(bool testEnabled, bool writeEnabled) override;
@@ -147,6 +145,7 @@ private:
 
 	bool _wireframe = true;
 	int64_t _wireframeFillColor = 0; // -1 = no fill, else color (palette idx or ARGB)
+	bool _xorMode = false;
 
 	// Stipple state. The shader emulates glPolygonStipple via a 128-int
 	// uniform array (Freescape pattern, GLES2-safe). _stippleActive is
@@ -348,6 +347,17 @@ void OpenGLShaderRenderer::drawSolid(GLenum mode, const float *positions, int ve
 		const float rgba[4]) {
 	if (vertCount <= 0)
 		return;
+	if (_xorMode) {
+		// QuickDraw's patXor effects are drawn with white mask pixels in the
+		// reimplementation. This blend state turns those pixels into
+		// 1 - destination, which matches the visible Mac B&W shoot/invert flash
+		// behavior and works on shader renderers without GL logic ops.
+		glEnable(GL_BLEND);
+		glBlendEquation(GL_FUNC_ADD);
+		glBlendFuncSeparate(GL_ONE_MINUS_DST_COLOR, GL_ZERO, GL_ZERO, GL_ONE);
+	} else {
+		glDisable(GL_BLEND);
+	}
 	uploadSolid(positions, vertCount);
 	// "projection" is set once by uploadProjectionUniform() ??? it persists in
 	// the program object across draws. Shader stays bound between draws so
@@ -665,6 +675,12 @@ void OpenGLShaderRenderer::setWireframe(bool enable, int64_t fillColor) {
 	_wireframeFillColor = fillColor;
 }
 
+void OpenGLShaderRenderer::setXorMode(bool enable) {
+	_xorMode = enable;
+	if (!enable)
+		glDisable(GL_BLEND);
+}
+
 void OpenGLShaderRenderer::setStippleData(const byte *data) {
 	const bool nowActive = (data != nullptr);
 	if (nowActive) {


Commit: d541fc5d0e6d2d7385cd4a036d8229de5d948840
    https://github.com/scummvm/scummvm/commit/d541fc5d0e6d2d7385cd4a036d8229de5d948840
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-05T13:24:41+02:00

Commit Message:
COLONY: complete rendering of mountain parallax

Changed paths:
    engines/colony/battle.cpp


diff --git a/engines/colony/battle.cpp b/engines/colony/battle.cpp
index 5ec5031274b..6dd4f82cf06 100644
--- a/engines/colony/battle.cpp
+++ b/engines/colony/battle.cpp
@@ -458,8 +458,9 @@ void ColonyEngine::battleInit() {
 	_projon = false;
 	_pcount = 0;
 
-	// Mountain parallax
-	_battledx = _width / 59;
+	// Mountain parallax. battleBackdrop() recomputes this once the viewport
+	// layout is final; keep a sane initial value for freshly initialized state.
+	_battledx = MAX<int>(1, _screenR.width() / 59);
 
 	// Generate mountain height profile (smoothed random)
 	int temp[257];
@@ -548,7 +549,13 @@ void ColonyEngine::battleBackdrop() {
 	// Mountain silhouette
 	uint32 mtColor = 0xFF606060;
 	uint8 ang = _me.look;
-	int xloc = -_battledx;
+	// Original battle.c draws the mountain profile under ClipRect(&Clip).
+	// Align the first sample to the active viewport, not to logical x=0
+	// where the Mac dashboard/sidebar lives in ScummVM.
+	_battledx = MAX<int>(1, _screenR.width() / 59);
+	if (!isMacRenderMode())
+		_battledx++;
+	int xloc = _screenR.left - _battledx;
 	if (ang & 0x01) {
 		xloc += _battledx;
 		ang--;
@@ -569,7 +576,12 @@ void ColonyEngine::battleBackdrop() {
 			sunon = true;
 		}
 		int curY = horizonY - _mountains[ang];
-		_gfx->drawLine(prevX, prevY, xloc, curY, mtColor);
+		int x1 = prevX;
+		int y1 = prevY;
+		int x2 = xloc;
+		int y2 = curY;
+		if (clipLineToRect(x1, y1, x2, y2, _screenR))
+			_gfx->drawLine(x1, y1, x2, y2, mtColor);
 		prevX = xloc;
 		prevY = curY;
 	}


Commit: 0befed2f78418f3d30d171d00cabd9ba3b3f656f
    https://github.com/scummvm/scummvm/commit/0befed2f78418f3d30d171d00cabd9ba3b3f656f
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-05T13:24:41+02:00

Commit Message:
COLONY: make sure the battle coordinates are normalized before starting

Changed paths:
    engines/colony/battle.cpp
    engines/colony/colony.h
    engines/colony/movement.cpp
    engines/colony/savegame.cpp


diff --git a/engines/colony/battle.cpp b/engines/colony/battle.cpp
index 6dd4f82cf06..cc870b5d272 100644
--- a/engines/colony/battle.cpp
+++ b/engines/colony/battle.cpp
@@ -515,6 +515,16 @@ void ColonyEngine::battleSet() {
 	}
 }
 
+void ColonyEngine::normalizeBattlePlayerPosition() {
+	// The original stored battle coordinates in 16-bit ints. Airlock map
+	// targets such as 253<<8 wrap to negative world coordinates there; keep
+	// the same representation before battle culling and camera math run.
+	_me.xloc = battleNormalizeCoord(_me.xloc);
+	_me.yloc = battleNormalizeCoord(_me.yloc);
+	_me.xindex = wrapBattleCoord(_me.xloc) >> 8;
+	_me.yindex = wrapBattleCoord(_me.yloc) >> 8;
+}
+
 // =====================================================================
 // battleBackdrop: Draw 2D sky gradient and ground fill.
 // Called before 3D rendering begins.
@@ -1187,6 +1197,7 @@ void ColonyEngine::battleProjCommand(int xcheck, int ycheck) {
 // Called from the main loop when _gameMode == kModeBattle.
 // =====================================================================
 void ColonyEngine::renderBattle() {
+	normalizeBattlePlayerPosition();
 	_battleMaxP = 0;
 
 	// Phase 1: 2D backdrop (sky gradient, mountains, sun) follows camera pitch
diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index b4be8d94fb9..f3de2a2e2df 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -693,6 +693,7 @@ private:
 	void battleInit();
 	void battleSet();
 	void battleThink();
+	void normalizeBattlePlayerPosition();
 	void enterColonyFromBattle(int mapNum, int xloc, int yloc);
 	void battleCommand(int xnew, int ynew);
 	void battleShoot();
diff --git a/engines/colony/movement.cpp b/engines/colony/movement.cpp
index 08649bf0b99..fd550ab45ed 100644
--- a/engines/colony/movement.cpp
+++ b/engines/colony/movement.cpp
@@ -599,6 +599,7 @@ int ColonyEngine::goToDestination(const uint8 *map, Locate *pobject) {
 		_me.yloc = targetY << 8;
 		_me.xindex = targetX;
 		_me.yindex = targetY;
+		normalizeBattlePlayerPosition();
 		return 2;
 	}
 
diff --git a/engines/colony/savegame.cpp b/engines/colony/savegame.cpp
index 8d7df774b93..1f423247887 100644
--- a/engines/colony/savegame.cpp
+++ b/engines/colony/savegame.cpp
@@ -581,6 +581,8 @@ Common::Error ColonyEngine::loadGameStream(Common::SeekableReadStream *stream) {
 	_lastWarningChimeTime = 0;
 	_battledx = _width / 59;
 	updateViewportLayout();
+	if (_gameMode == kModeBattle)
+		normalizeBattlePlayerPosition();
 
 	return Common::kNoError;
 }



From noreply at scummvm.org  Fri Jun  5 11:31:36 2026
From: noreply at scummvm.org (lotharsm)
Date: Fri,  5 Jun 2026 11:31:36 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 33fd20f4835d4603f4b49ceb2ecaa1c9bdbbeb6f
Message-ID: <20260605113136.8204E7005BC@ron.scummvm.net>

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

Summary:
33fd20f483 NEWS: Update German NEWS file


Commit: 33fd20f4835d4603f4b49ceb2ecaa1c9bdbbeb6f
    https://github.com/scummvm/scummvm/commit/33fd20f4835d4603f4b49ceb2ecaa1c9bdbbeb6f
Author: Lothar Serra Mari (mail at serra.me)
Date: 2026-06-05T13:31:24+02:00

Commit Message:
NEWS: Update German NEWS file

Changed paths:
    doc/de/NEUES.md


diff --git a/doc/de/NEUES.md b/doc/de/NEUES.md
index 247212376bd..d3daa25f76d 100644
--- a/doc/de/NEUES.md
+++ b/doc/de/NEUES.md
@@ -7,6 +7,7 @@ Umfangreichere Informationen ??ber die ??nderungen des aktuellen Codes findest D
    - Unterst??tzung f??r Nancy Drew: The Haunted Carousel hinzugef??gt.
    - Unterst??tzung f??r Nancy Drew: Danger on Deception Island hinzugef??gt.
    - Unterst??tzung f??r Cartoon Carnival hinzugef??gt.
+   - Unterst??tzung f??r Alfred Pelrock: En Busca de un Sue??o hinzugef??gt.
 
  Allgemein:
    - Klickbare Links in der Benutzeroberfl??che werden jetzt durch einen ver??nderten Cursor hervorgehoben.
@@ -61,6 +62,9 @@ Umfangreichere Informationen ??ber die ??nderungen des aktuellen Codes findest D
    - Darstellung des Holomap-Modells korrigiert.
    - Automatische Speicherung beim Fahren eines Fahrzeugs deaktiviert.
 
+ PS3-Portierung:
+   - ScummVM-Engines k??nnen jetzt als separate Module/Programmdateien ausgef??hrt werden.
+     Dadurch werden etwa 90 MB RAM eingespart. Die Option wird nur in Release-Paketen aktiviert.
 
 #### 2026.2.0 "Railmonicon" (28.03.2026)
 



From noreply at scummvm.org  Fri Jun  5 19:36:44 2026
From: noreply at scummvm.org (neuromancer)
Date: Fri,  5 Jun 2026 19:36:44 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 e997e181e40ae1b9469c26f5591ee1137dfc0e0d
Message-ID: <20260605193644.15B067005BC@ron.scummvm.net>

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

Summary:
4f7951f86e SCUMM: RA1: center mouse at the start and default to easy dificulty
07cc2c95cc SCUMM: RA: improved mouse input for both game with more effective centering when they start
25c4577531 COLONY: better rendering of enemies
114ad8c194 COLONY: more fluent animations for grow/shrink enemies
8ff586d3dd COLONY: better detection for aiming enemies
e997e181e4 COLONY: better detection collision detection with static objects


Commit: 4f7951f86edbfbc2e8289bf35d71468861dff7d5
    https://github.com/scummvm/scummvm/commit/4f7951f86edbfbc2e8289bf35d71468861dff7d5
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-05T16:17:57+02:00

Commit Message:
SCUMM: RA1: center mouse at the start and default to easy dificulty

Changed paths:
    engines/scumm/insane/rebel1/rebel.cpp
    engines/scumm/insane/rebel1/runlevels.cpp


diff --git a/engines/scumm/insane/rebel1/rebel.cpp b/engines/scumm/insane/rebel1/rebel.cpp
index c61b0c8aa65..828fb601216 100644
--- a/engines/scumm/insane/rebel1/rebel.cpp
+++ b/engines/scumm/insane/rebel1/rebel.cpp
@@ -238,8 +238,8 @@ InsaneRebel1::InsaneRebel1(ScummEngine_v7 *scumm) : Insane(), _vm(scumm) {
 	_turretFrameShipCenterValid = false;
 	_driftParam = 0;
 
-	// Original startup initializes the options difficulty/tuning index to 1.
-	_difficulty = 1;
+	// Start new games on the least punishing original tuning by default.
+	_difficulty = 0;
 	loadTuningForLevel(0);
 
 	_perspectiveX = 0;
diff --git a/engines/scumm/insane/rebel1/runlevels.cpp b/engines/scumm/insane/rebel1/runlevels.cpp
index 11499e20973..09f0ff68cd9 100644
--- a/engines/scumm/insane/rebel1/runlevels.cpp
+++ b/engines/scumm/insane/rebel1/runlevels.cpp
@@ -1774,8 +1774,11 @@ void InsaneRebel1::captureInteractiveVideoInput() {
 		// On touchscreen devices the DOS recenter-the-cursor aiming model does not apply;
 		// warping/locking the system mouse there injects spurious motion that drifts
 		// direct touch aiming and on-screen controls.
-		if (!isTouchscreenActive())
+		if (!isTouchscreenActive()) {
+			_vm->_mouse.x = kRA1CenterX;
+			_vm->_mouse.y = kRA1CenterY;
 			smush_warpMouse(160, 100, -1);
+		}
 		_mouseVirtualRawX = 0x140;
 		_mouseVirtualRawY = 100;
 		_mouseVirtualPrevLogicalX = kRA1CenterX;


Commit: 07cc2c95cc0800c8201ecbb516326894f5231d8f
    https://github.com/scummvm/scummvm/commit/07cc2c95cc0800c8201ecbb516326894f5231d8f
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-05T16:17:57+02:00

Commit Message:
SCUMM: RA: improved mouse input for both game with more effective centering when they start

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/insane/rebel1/runlevels.cpp
    engines/scumm/insane/rebel2/iact.cpp
    engines/scumm/insane/rebel2/levels.cpp
    engines/scumm/insane/rebel2/rebel.cpp
    engines/scumm/insane/rebel2/rebel.h


diff --git a/engines/scumm/insane/rebel1/iact.cpp b/engines/scumm/insane/rebel1/iact.cpp
index 7065bdc8a8b..4ae3706e857 100644
--- a/engines/scumm/insane/rebel1/iact.cpp
+++ b/engines/scumm/insane/rebel1/iact.cpp
@@ -46,6 +46,10 @@ inline int16 smoothRebel1Op0BAnalogInput(int16 inputValue, int16 &filteredValue,
 	return filteredValue;
 }
 
+inline int16 scaleRebel1CenteredMouseAxis(int16 value, int16 center, int axisMax) {
+	return (int16)CLIP<int32>(((int32)(value - center) * axisMax) / center, -axisMax, axisMax);
+}
+
 int16 shapeRebel1Op0BGamepadAxis(int16 inputValue, int16 axisMax) {
 	const int axis = CLIP<int>(inputValue, -axisMax, axisMax);
 	const int absAxis = ABS(axis);
@@ -77,6 +81,10 @@ const int kRA1DosMouseSafeLeft = 0x0AA;
 const int kRA1DosMouseSafeRight = 0x1D6;
 const int kRA1DosMouseSafeTop = 0x32;
 const int kRA1DosMouseSafeBottom = 0x96;
+const int kRA1DosFlightMouseMaxX = kRA1DosMouseCenterX >> 2;
+const int kRA1DosFlightMouseMaxY = kRA1DosMouseCenterY >> 1;
+const int kRA1EnhancedFlightDirectMaxX = (kRA1DosFlightMouseMaxX * 4) / 5;
+const int kRA1EnhancedFlightDirectMaxY = (kRA1DosFlightMouseMaxY * 4) / 5;
 
 // Level 15 final approach 0x5D damage/event codes consumed by
 // RunLevel1GameLoop. The latch stores the raw GAME parameter; no translation is
@@ -950,8 +958,9 @@ void InsaneRebel1::preprocessMouseAxes(int16 &inputX, int16 &inputY, bool *usedJ
 		if (usedJoystick)
 			*usedJoystick = true;
 
-		inputX = joyX * 127;
-		inputY = joyY * 127;
+		const bool enhancedFlightInput = _optEnhancedControls && getEffectiveGameOpcode() == 0x07;
+		inputX = joyX * (enhancedFlightInput ? kRA1EnhancedFlightDirectMaxX : 127);
+		inputY = joyY * (enhancedFlightInput ? kRA1EnhancedFlightDirectMaxY : 127);
 
 		if (_optControlsYFlip)
 			inputY = -inputY;
@@ -965,8 +974,11 @@ void InsaneRebel1::preprocessMouseAxes(int16 &inputX, int16 &inputY, bool *usedJ
 			*usedJoystick = true;
 
 		if (analogAxisX != 0 || analogAxisY != 0) {
-			inputX = CLIP<int32>(((int32)analogAxisX * 127) / Common::JOYAXIS_MAX, -127, 127);
-			inputY = CLIP<int32>(((int32)analogAxisY * 127) / Common::JOYAXIS_MAX, -127, 127);
+			const bool enhancedFlightInput = _optEnhancedControls && getEffectiveGameOpcode() == 0x07;
+			const int axisMaxX = enhancedFlightInput ? kRA1EnhancedFlightDirectMaxX : 127;
+			const int axisMaxY = enhancedFlightInput ? kRA1EnhancedFlightDirectMaxY : 127;
+			inputX = CLIP<int32>(((int32)analogAxisX * axisMaxX) / Common::JOYAXIS_MAX, -axisMaxX, axisMaxX);
+			inputY = CLIP<int32>(((int32)analogAxisY * axisMaxY) / Common::JOYAXIS_MAX, -axisMaxY, axisMaxY);
 		} else {
 			inputX = 0;
 			inputY = 0;
@@ -1006,8 +1018,15 @@ void InsaneRebel1::preprocessMouseAxes(int16 &inputX, int16 &inputY, bool *usedJ
 		_mousePrevBiasX = 0;
 		_mousePrevBiasY = 0;
 
-		inputX = (int16)CLIP<int32>(((int32)(logicalX - kRA1CenterX) * 127) / kRA1CenterX, -127, 127);
-		inputY = (int16)CLIP<int32>(((int32)(logicalY - kRA1CenterY) * 127) / kRA1CenterY, -127, 127);
+		if (getEffectiveGameOpcode() == 0x07) {
+			// Direct flight input lacks the DOS recenter path's offset decay, so keep
+			// held-edge steering below the raw DOS full-deflection bias.
+			inputX = scaleRebel1CenteredMouseAxis(logicalX, kRA1CenterX, kRA1EnhancedFlightDirectMaxX);
+			inputY = scaleRebel1CenteredMouseAxis(logicalY, kRA1CenterY, kRA1EnhancedFlightDirectMaxY);
+		} else {
+			inputX = (int16)CLIP<int32>(((int32)(logicalX - kRA1CenterX) * 127) / kRA1CenterX, -127, 127);
+			inputY = (int16)CLIP<int32>(((int32)(logicalY - kRA1CenterY) * 127) / kRA1CenterY, -127, 127);
+		}
 
 		// These handlers negate DAT_756E internally, so direct touch needs the
 		// opposite baseline while the menu option still toggles the result.
@@ -1053,9 +1072,7 @@ void InsaneRebel1::preprocessMouseAxes(int16 &inputX, int16 &inputY, bool *usedJ
 		_mouseVirtualRawY = rawY;
 		_mouseVirtualPrevLogicalX = kRA1CenterX;
 		_mouseVirtualPrevLogicalY = kRA1CenterY;
-		_vm->_mouse.x = kRA1CenterX;
-		_vm->_mouse.y = kRA1CenterY;
-		smush_warpMouse(kRA1CenterX, kRA1CenterY, -1);
+		warpGameplayMouseNow(kRA1CenterX, kRA1CenterY);
 		debug(2, "RA1 original input virtual recenter: offset=(%d,%d) mouse=(%d,%d)",
 			_mouseOffsetX, _mouseOffsetY, logicalX, logicalY);
 	}
diff --git a/engines/scumm/insane/rebel1/menu.cpp b/engines/scumm/insane/rebel1/menu.cpp
index 45eb5a1d6e5..9b7f374073e 100644
--- a/engines/scumm/insane/rebel1/menu.cpp
+++ b/engines/scumm/insane/rebel1/menu.cpp
@@ -54,6 +54,12 @@ const int kRA1LevelSelectLeftX      = 20;
 const int kRA1LevelSelectRightX     = 170;
 const int kRA1LevelSelectColW       = 130;
 const uint32 kRA1JoystickAxisEscGuardMs = 250;
+const int kRA1GameplayMouseSettleJumpThreshold = 40;
+const int kRA1GameplayMouseSettleRelativeThreshold = 40;
+const int kRA1GameplayMouseSettleEdgeMargin = 16;
+const int kRA1GameplayMouseMaxX = 319;
+const int kRA1GameplayMouseMaxY = 199;
+const uint32 kRA1GameplayMouseSettleExtendMs = 1000;
 // Original picker traversal uses fixed max indices, not strlen(): passcodes
 // stop at 0x1d and high-score names stop at 0x37.
 const char kRA1TextEntryPickerChars[] = "^`_ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
@@ -632,6 +638,42 @@ bool InsaneRebel1::notifyEvent(const Common::Event &event) {
 		return true;
 	}
 
+	if (_interactiveVideoActive && !_menuActive && !isTouchscreenActive() &&
+			getEffectiveGameOpcode() == 0x07 &&
+			event.type == Common::EVENT_MOUSEMOVE && _gameplayMouseSettleUntil != 0) {
+		const uint32 now = _vm->_system->getMillis();
+		if (now < _gameplayMouseSettleUntil) {
+			const int jumpX = event.mouse.x - _vm->_mouse.x;
+			const int jumpY = event.mouse.y - _vm->_mouse.y;
+			const bool largeAbsoluteJump =
+				ABS(jumpX) >= kRA1GameplayMouseSettleJumpThreshold ||
+				ABS(jumpY) >= kRA1GameplayMouseSettleJumpThreshold;
+			const bool smallRelativeMove =
+				ABS((int)event.relMouse.x) < kRA1GameplayMouseSettleRelativeThreshold &&
+				ABS((int)event.relMouse.y) < kRA1GameplayMouseSettleRelativeThreshold;
+			const bool nearWindowEdge =
+				event.mouse.x <= kRA1GameplayMouseSettleEdgeMargin ||
+				event.mouse.x >= kRA1GameplayMouseMaxX - kRA1GameplayMouseSettleEdgeMargin ||
+				event.mouse.y <= kRA1GameplayMouseSettleEdgeMargin ||
+				event.mouse.y >= kRA1GameplayMouseMaxY - kRA1GameplayMouseSettleEdgeMargin;
+
+			if (largeAbsoluteJump && smallRelativeMove && nearWindowEdge) {
+				const int recenterX = _vm->_mouse.x;
+				const int recenterY = _vm->_mouse.y;
+				_gameplayMouseSettleUntil = now + kRA1GameplayMouseSettleExtendMs;
+				warpGameplayMouseNow(recenterX, recenterY);
+
+				debugC(DEBUG_INSANE, "RA1 mouse settle: suppress pos=(%d,%d) rel=(%d,%d) current=(%d,%d) until=%u opcode=0x%X",
+					event.mouse.x, event.mouse.y, event.relMouse.x, event.relMouse.y,
+					_vm->_mouse.x, _vm->_mouse.y, _gameplayMouseSettleUntil,
+					getEffectiveGameOpcode());
+				return true;
+			}
+		}
+
+		_gameplayMouseSettleUntil = 0;
+	}
+
 	if (event.type == Common::EVENT_MOUSEMOVE && !_mouseRecentering) {
 		if (_gamepadAimActive && usesRelativeGamepadAimForCurrentLevel() &&
 				_interactiveVideoActive && !_menuActive &&
diff --git a/engines/scumm/insane/rebel1/rebel.cpp b/engines/scumm/insane/rebel1/rebel.cpp
index 828fb601216..004840569e1 100644
--- a/engines/scumm/insane/rebel1/rebel.cpp
+++ b/engines/scumm/insane/rebel1/rebel.cpp
@@ -279,6 +279,7 @@ InsaneRebel1::InsaneRebel1(ScummEngine_v7 *scumm) : Insane(), _vm(scumm) {
 	_gamepadAimAxisX = 0;
 	_gamepadAimAxisY = 0;
 	_gamepadAimActive = false;
+	_gameplayMouseSettleUntil = 0;
 	_activeInputSource = kInputSourceMouse;
 
 	_currentLevel = 0;
@@ -479,6 +480,20 @@ void InsaneRebel1::setCurrentSmushFrame(int32 frame) {
 	_currentSmushFrame = frame;
 }
 
+void InsaneRebel1::warpGameplayMouseNow(int x, int y) {
+	Common::EventManager *eventMan = _vm->_system->getEventManager();
+	if (eventMan)
+		eventMan->purgeMouseEvents();
+
+	_vm->_mouse.x = x;
+	_vm->_mouse.y = y;
+	_vm->_system->warpMouse(_vm->_macScreen ? x * 2 : x,
+		_vm->_macScreen ? y * 2 + 2 * _vm->_macScreenDrawOffset : y);
+
+	if (eventMan)
+		eventMan->purgeMouseEvents();
+}
+
 InsaneRebel1::~InsaneRebel1() {
 	_vm->_system->getEventManager()->getEventDispatcher()->unregisterObserver(this);
 	terminateAudio();
diff --git a/engines/scumm/insane/rebel1/rebel.h b/engines/scumm/insane/rebel1/rebel.h
index 781d916bc74..6622e884873 100644
--- a/engines/scumm/insane/rebel1/rebel.h
+++ b/engines/scumm/insane/rebel1/rebel.h
@@ -119,6 +119,7 @@ public:
 	bool hasFrameGameOpcode(uint16 opcode) const {
 		return opcode < 32 && (_frameGameOpcodeMask & (1u << opcode)) != 0;
 	}
+	void warpGameplayMouseNow(int x, int y);
 	int16 getPerspectiveX() const { return _perspectiveX; }
 	int16 getPerspectiveY() const { return _perspectiveY; }
 	void projectGameplayPoint(int16 &x, int16 &y) const;
@@ -377,6 +378,8 @@ private:
 	int16 _gamepadAimAxisX; // Relative gamepad 0x0B aim in preprocessed input space
 	int16 _gamepadAimAxisY;
 	bool _gamepadAimActive;
+	// Suppress delayed lock/warp mousemove artifacts after centering interactive gameplay.
+	uint32 _gameplayMouseSettleUntil;
 	enum InputSource {
 		kInputSourceMouse,
 		kInputSourceJoystickAnalog,
diff --git a/engines/scumm/insane/rebel1/runlevels.cpp b/engines/scumm/insane/rebel1/runlevels.cpp
index 09f0ff68cd9..29792d1ad45 100644
--- a/engines/scumm/insane/rebel1/runlevels.cpp
+++ b/engines/scumm/insane/rebel1/runlevels.cpp
@@ -33,6 +33,8 @@
 
 namespace Scumm {
 
+static const uint32 kRA1GameplayMouseSettleMs = 1000;
+
 int32 findAnimFrameChunkOffset(ScummEngine_v7 *vm, const char *filename, int32 targetFrame) {
 	if (targetFrame <= 0)
 		return 0;
@@ -1771,26 +1773,34 @@ void InsaneRebel1::captureInteractiveVideoInput() {
 	// Some replays happen inside one original gameplay loop, so keep the current
 	// input state instead of recentering between route clips.
 	if (!preserveInputState) {
+		_gameplayMouseSettleUntil = 0;
 		// On touchscreen devices the DOS recenter-the-cursor aiming model does not apply;
 		// warping/locking the system mouse there injects spurious motion that drifts
 		// direct touch aiming and on-screen controls.
 		if (!isTouchscreenActive()) {
-			_vm->_mouse.x = kRA1CenterX;
-			_vm->_mouse.y = kRA1CenterY;
-			smush_warpMouse(160, 100, -1);
+			warpGameplayMouseNow(kRA1CenterX, kRA1CenterY);
+			_gameplayMouseSettleUntil = _vm->_system->getMillis() + kRA1GameplayMouseSettleMs;
 		}
 		_mouseVirtualRawX = 0x140;
 		_mouseVirtualRawY = 100;
 		_mouseVirtualPrevLogicalX = kRA1CenterX;
 		_mouseVirtualPrevLogicalY = kRA1CenterY;
 		_mouseVirtualValid = false;
+	} else {
+		_gameplayMouseSettleUntil = 0;
 	}
 	CursorMan.showMouse(false);
 	if (!isTouchscreenActive())
 		g_system->lockMouse(true);
+
+	debugC(DEBUG_INSANE, "RA1 centerGameplayAim: mouse=(%d,%d) joystick=(%d,%d) gamepadAim=%d settleUntil=%u preserve=%d",
+		_vm->_mouse.x, _vm->_mouse.y, _joystickAxisX, _joystickAxisY,
+		_gamepadAimActive ? 1 : 0, _gameplayMouseSettleUntil,
+		preserveInputState ? 1 : 0);
 }
 
 void InsaneRebel1::releaseInteractiveVideoInput() {
+	_gameplayMouseSettleUntil = 0;
 	g_system->lockMouse(false);
 }
 
diff --git a/engines/scumm/insane/rebel2/iact.cpp b/engines/scumm/insane/rebel2/iact.cpp
index 854fcf100df..8122855a018 100644
--- a/engines/scumm/insane/rebel2/iact.cpp
+++ b/engines/scumm/insane/rebel2/iact.cpp
@@ -34,6 +34,9 @@
 
 namespace Scumm {
 
+const int kRA2Handler7DirectInputNumerator = 4;
+const int kRA2Handler7DirectInputDenominator = 5;
+
 static bool readLevel2BackgroundChunkHeader(Common::SeekableReadStream &stream, int64 containerEnd, const char *context,
 		uint32 &tag, uint32 &chunkSize, int64 &dataEnd, int64 &nextChunkPos) {
 	const int64 headerPos = stream.pos();
@@ -830,8 +833,10 @@ void InsaneRebel2::handleOpcode6Handler7(Common::SeekableReadStream &b, int16 pa
 	// Step 1: Raw mouse input as offset from screen center.
 	// DAT_0047a7e0 = mouseX - 160, DAT_0047a7e2 = mouseY - 100.
 	// Handler 7 applies DAT_0047a7fe to its local vertical input after clamping.
-	int16 inputX = (int16)(_vm->_mouse.x - 160);  // DAT_0047a7e0
-	int16 inputY = (int16)(_vm->_mouse.y - 100);  // DAT_0047a7e2
+	const int16 mouseX = _vm->_mouse.x;
+	const int16 mouseY = _vm->_mouse.y;
+	int16 inputX = (int16)(mouseX - 160);  // DAT_0047a7e0
+	int16 inputY = (int16)(mouseY - 100);  // DAT_0047a7e2
 
 	// Clamp: mouse mode uses [-160, 160] for X, [-127, 127] for Y (lines 55-70).
 	if (inputX > 160)
@@ -848,6 +853,14 @@ void InsaneRebel2::handleOpcode6Handler7(Common::SeekableReadStream &b, int16 pa
 	int16 scaledInputX = (int16)((inputX * 127) / 160);
 	int16 scaledInputY = _optControlsFlipped ? (int16)-inputY : inputY;  // local_14
 
+	// ScummVM direct mouse/touch/gamepad aiming can hold the cursor at an edge
+	// indefinitely. Keep this sensitivity concession local to Handler 7
+	// third-person ship steering.
+	scaledInputX = (int16)((scaledInputX * kRA2Handler7DirectInputNumerator) /
+		kRA2Handler7DirectInputDenominator);
+	scaledInputY = (int16)((scaledInputY * kRA2Handler7DirectInputNumerator) /
+		kRA2Handler7DirectInputDenominator);
+
 	// Step 3: Velocity history + smoothed average (lines 141-157).
 	for (int i = 24; i > 0; i--) {
 		_velocityHistory[i] = _velocityHistory[i - 1];
@@ -1063,7 +1076,9 @@ void InsaneRebel2::handleOpcode6Handler7(Common::SeekableReadStream &b, int16 pa
 		((_vm->VAR(_vm->VAR_LEFTBTN_HOLD) != 0) ||
 		 _vm->getActionState(kScummActionInsaneAttack));
 
-	debug("Rebel2 H7: pos=(%d,%d) vel=%d vIn=%d dx=%d dir=%d mode=%d",
+	debugC(DEBUG_INSANE, "Rebel2 H7: mouse=(%d,%d) raw=(%d,%d) scaled=(%d,%d) pos=(%d,%d) "
+		"vel=%d vIn=%d dx=%d dir=%d mode=%d",
+		mouseX, mouseY, inputX, inputY, scaledInputX, scaledInputY,
 		_flyShipScreenX, _flyShipScreenY, _smoothedVelocity,
 		_verticalInput, positionDeltaX, _shipDirectionIndex, _flyControlMode);
 }
diff --git a/engines/scumm/insane/rebel2/levels.cpp b/engines/scumm/insane/rebel2/levels.cpp
index 8b010dca334..062ec934395 100644
--- a/engines/scumm/insane/rebel2/levels.cpp
+++ b/engines/scumm/insane/rebel2/levels.cpp
@@ -34,6 +34,7 @@ namespace Scumm {
 
 static const int kRebel2GameplayAimCenterX = 160;
 static const int kRebel2GameplayAimCenterY = 100;
+static const uint32 kRebel2GameplayMouseSettleMs = 1000;
 
 static void purgeRebel2GameplayInputEvents(Common::EventManager *eventMan) {
 	if (!eventMan)
@@ -385,15 +386,32 @@ void InsaneRebel2::centerGameplayAim() {
 	Common::EventManager *eventMan = _vm->_system->getEventManager();
 	purgeRebel2GameplayInputEvents(eventMan);
 
-	_vm->_mouse.x = kRebel2GameplayAimCenterX;
-	_vm->_mouse.y = kRebel2GameplayAimCenterY;
-
 	_joystickAxisX = 0;
 	_joystickAxisY = 0;
 	_gamepadAimActive = false;
 
-	smush_warpMouse(kRebel2GameplayAimCenterX, kRebel2GameplayAimCenterY, -1);
+	warpGameplayMouseNow(kRebel2GameplayAimCenterX, kRebel2GameplayAimCenterY);
 	purgeRebel2GameplayInputEvents(eventMan);
+	_gameplayMouseSettleUntil = _vm->_system->getMillis() + kRebel2GameplayMouseSettleMs;
+
+	debugC(DEBUG_INSANE, "Rebel2 centerGameplayAim: mouse=(%d,%d) joystick=(%d,%d) gamepadAim=%d settleUntil=%u",
+		_vm->_mouse.x, _vm->_mouse.y,
+		_joystickAxisX, _joystickAxisY, _gamepadAimActive ? 1 : 0,
+		_gameplayMouseSettleUntil);
+}
+
+void InsaneRebel2::warpGameplayMouseNow(int x, int y) {
+	Common::EventManager *eventMan = _vm->_system->getEventManager();
+	if (eventMan)
+		eventMan->purgeMouseEvents();
+
+	_vm->_mouse.x = x;
+	_vm->_mouse.y = y;
+	_vm->_system->warpMouse(_vm->_macScreen ? x * 2 : x,
+		_vm->_macScreen ? y * 2 + 2 * _vm->_macScreenDrawOffset : y);
+
+	if (eventMan)
+		eventMan->purgeMouseEvents();
 }
 
 // runLevel -- Main level dispatcher, calls per-level handlers.
diff --git a/engines/scumm/insane/rebel2/rebel.cpp b/engines/scumm/insane/rebel2/rebel.cpp
index 4d125c67016..ae1f86099ac 100644
--- a/engines/scumm/insane/rebel2/rebel.cpp
+++ b/engines/scumm/insane/rebel2/rebel.cpp
@@ -54,6 +54,12 @@ namespace Scumm {
 const int kRA2MenuAxisThreshold = Common::JOYAXIS_MAX / 5;
 const uint32 kRA2MenuGamepadNavigationDebounceMs = 250;
 const uint32 kRA2MenuGamepadMouseSuppressMs = 250;
+const int kRA2Handler7MouseSettleJumpThreshold = 40;
+const int kRA2Handler7MouseSettleRelativeThreshold = 40;
+const int kRA2Handler7MouseSettleEdgeMargin = 16;
+const int kRA2GameplayMouseMaxX = 319;
+const int kRA2GameplayMouseMaxY = 199;
+const uint32 kRA2Handler7MouseSettleExtendMs = 1000;
 
 bool rebel2UsesRelativeGamepadAim(int selectedLevel) {
 	return selectedLevel == 1 || selectedLevel == 5 || selectedLevel == 14;
@@ -542,6 +548,7 @@ InsaneRebel2::InsaneRebel2(ScummEngine_v7 *scumm) {
 	_joystickAxisY = 0;
 	_gamepadAimActive = false;
 	_gameplaySectionActive = false;
+	_gameplayMouseSettleUntil = 0;
 	_lastGameplayMenuCloseTime = 0;
 	_lastMenuGamepadNavigationTime = 0;
 	_handler8HudGlyph = '#';
@@ -645,6 +652,46 @@ bool InsaneRebel2::notifyEvent(const Common::Event &event) {
 	if (_vm->isPaused())
 		return false;
 
+	if (_gameState == kStateGameplay && _rebelHandler == 7 &&
+			event.type == Common::EVENT_MOUSEMOVE) {
+		if (_gameplayMouseSettleUntil != 0) {
+			const uint32 now = _vm->_system->getMillis();
+			if (now < _gameplayMouseSettleUntil) {
+				const int jumpX = event.mouse.x - _vm->_mouse.x;
+				const int jumpY = event.mouse.y - _vm->_mouse.y;
+				const bool largeAbsoluteJump =
+					ABS(jumpX) >= kRA2Handler7MouseSettleJumpThreshold ||
+					ABS(jumpY) >= kRA2Handler7MouseSettleJumpThreshold;
+				const bool smallRelativeMove =
+					ABS((int)event.relMouse.x) < kRA2Handler7MouseSettleRelativeThreshold &&
+					ABS((int)event.relMouse.y) < kRA2Handler7MouseSettleRelativeThreshold;
+				const bool nearWindowEdge =
+					event.mouse.x <= kRA2Handler7MouseSettleEdgeMargin ||
+					event.mouse.x >= kRA2GameplayMouseMaxX - kRA2Handler7MouseSettleEdgeMargin ||
+					event.mouse.y <= kRA2Handler7MouseSettleEdgeMargin ||
+					event.mouse.y >= kRA2GameplayMouseMaxY - kRA2Handler7MouseSettleEdgeMargin;
+
+				if (largeAbsoluteJump && smallRelativeMove && nearWindowEdge) {
+					const int recenterX = _vm->_mouse.x;
+					const int recenterY = _vm->_mouse.y;
+					_gameplayMouseSettleUntil = now + kRA2Handler7MouseSettleExtendMs;
+					warpGameplayMouseNow(recenterX, recenterY);
+
+					debugC(DEBUG_INSANE, "Rebel2 H7 mouse settle: suppress pos=(%d,%d) rel=(%d,%d) current=(%d,%d) until=%u",
+						event.mouse.x, event.mouse.y, event.relMouse.x, event.relMouse.y,
+						_vm->_mouse.x, _vm->_mouse.y, _gameplayMouseSettleUntil);
+					return true;
+				}
+			}
+
+			_gameplayMouseSettleUntil = 0;
+		}
+
+		debugC(DEBUG_INSANE, "Rebel2 H7 mouse event: pos=(%d,%d) rel=(%d,%d) gamepadAim=%d menuInput=%d",
+			event.mouse.x, event.mouse.y, event.relMouse.x, event.relMouse.y,
+			_gamepadAimActive ? 1 : 0, _menuInputActive ? 1 : 0);
+	}
+
 	// Keep the gamepad reticle authoritative against stray pointer events. During
 	// gameplay the cursor is locked ~screen-center (levels.cpp lockMouse), so any
 	// spurious pointer event ??? e.g. a gamepad "fire" surfacing as a button-down ???
diff --git a/engines/scumm/insane/rebel2/rebel.h b/engines/scumm/insane/rebel2/rebel.h
index 913e4465656..41cdec1c5be 100644
--- a/engines/scumm/insane/rebel2/rebel.h
+++ b/engines/scumm/insane/rebel2/rebel.h
@@ -509,6 +509,7 @@ public:
 	// (on-screen/physical gamepad dpad, keyboard arrows) instead of snapping it to a screen
 	// edge. Call once per frame; getGameplayAimPoint() stays a pure getter.
 	void updateGameplayAimFromGamepad();
+	void warpGameplayMouseNow(int x, int y);
 
 	// Analog stick state, ingested from EVENT_CUSTOM_BACKEND_ACTION_AXIS in notifyEvent()
 	// and read (with a deadzone) by updateGameplayAimFromGamepad(). Signed; 0 = centered.
@@ -520,6 +521,8 @@ public:
 	// recenter the reticle (the cursor is locked ~center during gameplay, so a gamepad
 	// "click" lands at center). See notifyEvent()/updateGameplayAimFromGamepad().
 	bool _gamepadAimActive;
+	// Suppress delayed lock/warp mousemove artifacts after centering Handler 7 gameplay.
+	uint32 _gameplayMouseSettleUntil;
 	uint32 _lastGameplayMenuCloseTime;
 	uint32 _lastMenuGamepadNavigationTime;
 	void openGameplayMainMenu(SmushPlayer *splayer);


Commit: 25c4577531182786f743f4d7599aec8d7db1f7cc
    https://github.com/scummvm/scummvm/commit/25c4577531182786f743f4d7599aec8d7db1f7cc
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-05T17:50:51+02:00

Commit Message:
COLONY: better rendering of enemies

Changed paths:
    engines/colony/animation.cpp
    engines/colony/render_objects.cpp


diff --git a/engines/colony/animation.cpp b/engines/colony/animation.cpp
index e04f5c6629b..98fdba9fb25 100644
--- a/engines/colony/animation.cpp
+++ b/engines/colony/animation.cpp
@@ -610,12 +610,18 @@ void ColonyEngine::playAnimation() {
 					needsDraw = true;
 				}
 			} else if (event.type == Common::EVENT_KEYDOWN) {
+				if (event.kbd.keycode == Common::KEYCODE_RETURN) {
+					// Original Mac AnimControl(): Return closes the animation window.
+					_animationRunning = false;
+					continue;
+				}
+
 				int item = 0;
 				if (event.kbd.keycode >= Common::KEYCODE_0 && event.kbd.keycode <= Common::KEYCODE_9) {
 					item = 1 + (event.kbd.keycode - Common::KEYCODE_0);
 				} else if (event.kbd.keycode >= Common::KEYCODE_KP0 && event.kbd.keycode <= Common::KEYCODE_KP9) {
 					item = 1 + (event.kbd.keycode - Common::KEYCODE_KP0);
-				} else if (event.kbd.keycode == Common::KEYCODE_RETURN || event.kbd.keycode == Common::KEYCODE_KP_ENTER) {
+				} else if (event.kbd.keycode == Common::KEYCODE_KP_ENTER) {
 					item = 12; // Enter
 				} else if (event.kbd.keycode == Common::KEYCODE_BACKSPACE || event.kbd.keycode == Common::KEYCODE_DELETE) {
 					item = 11; // Clear
diff --git a/engines/colony/render_objects.cpp b/engines/colony/render_objects.cpp
index a93feac780c..60cbf8bfde0 100644
--- a/engines/colony/render_objects.cpp
+++ b/engines/colony/render_objects.cpp
@@ -43,6 +43,8 @@ int mapEyeOverlayColorToMacColor(int colorIdx, int level) {
 	case kColorIris:         return 35; // c_iris
 	case kColorEyeIris:      return 32; // c_eye
 	case kColorMiniEyeIris:  return 33; // c_meye
+	case kColorDroneEye:     return 51; // c_edrone
+	case kColorSoldierEye:   return 53; // c_esoldier
 	case kColorQueenEye:     return 49; // c_equeen
 	default:                 return 6;  // c_dwall fallback
 	}
@@ -58,6 +60,8 @@ uint8 mapEyeOverlayColorToDOSFill(int colorIdx, int level) {
 	case kColorIris:
 	case kColorEyeIris:
 	case kColorMiniEyeIris:
+	case kColorDroneEye:
+	case kColorSoldierEye:
 	case kColorQueenEye:
 		return 1;
 	default:
@@ -79,6 +83,8 @@ int mapEyeOverlayColorToMacPattern(int colorIdx) {
 	case kColorIris:
 	case kColorEyeIris:
 	case kColorMiniEyeIris:
+	case kColorDroneEye:
+	case kColorSoldierEye:
 	case kColorQueenEye: // fallthrough: explicitly grouped with default
 	default:
 		return kPatternGray;
@@ -1100,7 +1106,7 @@ const int kDAbdomenSurf[8][8] = {
 	{kColorDrone, 3, 5, 2, 1, 5, 0, 0}, {kColorDrone, 3, 5, 3, 2, 5, 0, 0},
 	{kColorDrone, 3, 5, 4, 3, 5, 0, 0}, {kColorDrone, 3, 5, 1, 4, 5, 0, 0}
 };
-// Drone static pincers (llPincer/rrPincer)
+// Base jaw points used by the Soldier pincer animation.
 const int kDLLPincerPts[4][3] = {
 	{0, 0, 130}, {50, -2, 130}, {35, -20, 140}, {35, -20, 120}
 };
@@ -1115,25 +1121,8 @@ const int kDRPincerSurf[4][8] = {
 	{kColorClaw1, 3, 0, 1, 2, 0, 0, 0}, {kColorClaw1, 3, 0, 3, 1, 0, 0, 0},
 	{kColorClaw2, 3, 0, 2, 3, 0, 0, 0}, {kColorClaw2, 3, 1, 3, 2, 1, 0, 0}
 };
-// Drone eyes
-const int kDLEyePts[3][3] = {
-	{60, 0, 150}, {60, 50, 130}, {60, 25, 150}
-};
-const int kDLEyeSurf[2][8] = {
-	{kColorDroneEye, 3, 0, 1, 2, 0, 0, 0}, {kColorDroneEye, 3, 0, 2, 1, 0, 0, 0}
-};
-const int kDREyePts[3][3] = {
-	{60, 0, 150}, {60, -50, 130}, {60, -25, 150}
-};
-const int kDREyeSurf[2][8] = {
-	{kColorDroneEye, 3, 0, 1, 2, 0, 0, 0}, {kColorDroneEye, 3, 0, 2, 1, 0, 0, 0}
-};
 
 const Colony::ColonyEngine::PrismPartDef kDAbdomenDef = {6, kDAbdomenPts, 8, kDAbdomenSurf};
-const Colony::ColonyEngine::PrismPartDef kDLLPincerDef = {4, kDLLPincerPts, 4, kDLPincerSurf};
-const Colony::ColonyEngine::PrismPartDef kDRRPincerDef = {4, kDRRPincerPts, 4, kDRPincerSurf};
-const Colony::ColonyEngine::PrismPartDef kDLEyeDef = {3, kDLEyePts, 2, kDLEyeSurf};
-const Colony::ColonyEngine::PrismPartDef kDREyeDef = {3, kDREyePts, 2, kDREyeSurf};
 
 // --- Snoop (type 20) ---
 const int kSnoopAbdomenPts[4][3] = {
@@ -1173,6 +1162,142 @@ void resetObjectBounds(const Common::Rect &screenR, Locate &loc) {
 	loc.zmx = screenR.top;
 }
 
+void mergeObjectBounds(Locate &dst, const Locate &src) {
+	dst.xmn = MIN(dst.xmn, src.xmn);
+	dst.xmx = MAX(dst.xmx, src.xmx);
+	dst.zmn = MIN(dst.zmn, src.zmn);
+	dst.zmx = MAX(dst.zmx, src.zmx);
+}
+
+bool prismSurfaceVisible(const Common::Rect &screenR, const Colony::Thing &thing,
+		const Colony::ColonyEngine::PrismPartDef &def, int surfaceIndex,
+		uint8 cameraLook, int8 cameraLookY, int cameraX, int cameraY,
+		const int *sint, const int *cost) {
+	return isProjectedPrismSurfaceVisible(screenR, thing, def, false, surfaceIndex,
+		cameraLook, cameraLookY, cameraX, cameraY, sint, cost);
+}
+
+int droneBodyOrder(const Common::Rect &screenR, const Colony::Thing &thing,
+		uint8 cameraLook, int8 cameraLookY, int cameraX, int cameraY,
+		const int *sint, const int *cost) {
+	const bool v0 = prismSurfaceVisible(screenR, thing, kDAbdomenDef, 0, cameraLook, cameraLookY, cameraX, cameraY, sint, cost);
+	const bool v1 = prismSurfaceVisible(screenR, thing, kDAbdomenDef, 1, cameraLook, cameraLookY, cameraX, cameraY, sint, cost);
+	const bool v2 = prismSurfaceVisible(screenR, thing, kDAbdomenDef, 2, cameraLook, cameraLookY, cameraX, cameraY, sint, cost);
+	const bool v3 = prismSurfaceVisible(screenR, thing, kDAbdomenDef, 3, cameraLook, cameraLookY, cameraX, cameraY, sint, cost);
+
+	int body = 1;
+	if (v0 && v1)
+		body = 1;
+	if (v2 && v3)
+		body = 1;
+	if (v0 && v3)
+		body = 0;
+	if (v1 && v2)
+		body = 2;
+	return body;
+}
+
+int queenBodyOrder(const Common::Rect &screenR, const Colony::Thing &thing,
+		uint8 cameraLook, int8 cameraLookY, int cameraX, int cameraY,
+		const int *sint, const int *cost) {
+	const bool v0 = prismSurfaceVisible(screenR, thing, kQAbdomenDef, 0, cameraLook, cameraLookY, cameraX, cameraY, sint, cost);
+	const bool v1 = prismSurfaceVisible(screenR, thing, kQAbdomenDef, 1, cameraLook, cameraLookY, cameraX, cameraY, sint, cost);
+	const bool v4 = prismSurfaceVisible(screenR, thing, kQAbdomenDef, 4, cameraLook, cameraLookY, cameraX, cameraY, sint, cost);
+	const bool v5 = prismSurfaceVisible(screenR, thing, kQAbdomenDef, 5, cameraLook, cameraLookY, cameraX, cameraY, sint, cost);
+
+	int body = 1;
+	if (v0 && v4)
+		body = 1;
+	if (v1 && v5)
+		body = 1;
+	if (v0 && v1)
+		body = 0;
+	if (v4 && v5)
+		body = 2;
+	return body;
+}
+
+float prismPointForwardDepth(const Colony::Thing &thing, const Colony::ColonyEngine::PrismPartDef &def, int pointIndex,
+		uint8 cameraLook, int cameraX, int cameraY, const int *sint, const int *cost) {
+	if (pointIndex < 0 || pointIndex >= def.pointCount)
+		return 0.0f;
+
+	const uint8 objAng = thing.where.ang + 32;
+	const int32 rotCos = cost[objAng];
+	const int32 rotSin = sint[objAng];
+	const int ox = def.points[pointIndex][0];
+	const int oy = def.points[pointIndex][1];
+	const int32 rx = ((int32)ox * rotCos - (int32)oy * rotSin) >> 7;
+	const int32 ry = ((int32)ox * rotSin + (int32)oy * rotCos) >> 7;
+	const float dx = (float)(rx + thing.where.xloc - cameraX);
+	const float dy = (float)(ry + thing.where.yloc - cameraY);
+
+	return dx * (cost[cameraLook] / 128.0f) + dy * (sint[cameraLook] / 128.0f);
+}
+
+struct EnemyEyePair {
+	Thing left;
+	Thing right;
+	bool leftFirst;
+};
+
+int projectedEnemyEyeDiameter(const Common::Rect &screenR, const Colony::Thing &eye,
+		uint8 cameraLook, int8 cameraLookY, int cameraX, int cameraY,
+		const int *sint, const int *cost) {
+	int bottomX = 0;
+	int bottomY = 0;
+	int centerX = 0;
+	int centerY = 0;
+	if (!projectCorridorPointRaw(screenR, cameraLook, cameraLookY, sint, cost, cameraX, cameraY,
+			(float)eye.where.xloc, (float)eye.where.yloc, 130.0f - 160.0f, bottomX, bottomY)) {
+		return -1;
+	}
+	if (!projectCorridorPointRaw(screenR, cameraLook, cameraLookY, sint, cost, cameraX, cameraY,
+			(float)eye.where.xloc, (float)eye.where.yloc, 155.0f - 160.0f, centerX, centerY)) {
+		return -1;
+	}
+	return ABS(bottomY - centerY);
+}
+
+EnemyEyePair buildEnemyEyePair(const Common::Rect &screenR, const Colony::Thing &obj,
+		uint8 cameraLook, int8 cameraLookY, int cameraX, int cameraY,
+		const int *sint, const int *cost) {
+	EnemyEyePair eyes;
+	eyes.left = obj;
+	eyes.right = obj;
+
+	const int32 s1 = sint[obj.where.ang] >> 1;
+	const int32 c1 = cost[obj.where.ang] >> 1;
+	const int32 s2 = s1 >> 1;
+	const int32 c2 = c1 >> 1;
+	const int32 eyeBaseX = obj.where.xloc + c1;
+	const int32 eyeBaseY = obj.where.yloc + s1;
+
+	eyes.left.where.xloc = (int)(eyeBaseX - s2);
+	eyes.left.where.yloc = (int)(eyeBaseY + c2);
+	resetObjectBounds(screenR, eyes.left.where);
+
+	eyes.right.where.xloc = (int)(eyeBaseX + s2);
+	eyes.right.where.yloc = (int)(eyeBaseY - c2);
+	resetObjectBounds(screenR, eyes.right.where);
+
+	const int leftDiameter = projectedEnemyEyeDiameter(screenR, eyes.left, cameraLook, cameraLookY, cameraX, cameraY, sint, cost);
+	const int rightDiameter = projectedEnemyEyeDiameter(screenR, eyes.right, cameraLook, cameraLookY, cameraX, cameraY, sint, cost);
+	if (leftDiameter >= 0 && rightDiameter >= 0) {
+		eyes.leftFirst = leftDiameter < rightDiameter;
+	} else {
+		const int32 leftDx = eyes.left.where.xloc - cameraX;
+		const int32 leftDy = eyes.left.where.yloc - cameraY;
+		const int32 rightDx = eyes.right.where.xloc - cameraX;
+		const int32 rightDy = eyes.right.where.yloc - cameraY;
+		const int64 leftDist = (int64)leftDx * leftDx + (int64)leftDy * leftDy;
+		const int64 rightDist = (int64)rightDx * rightDx + (int64)rightDy * rightDy;
+		eyes.leftFirst = leftDist >= rightDist;
+	}
+
+	return eyes;
+}
+
 void ColonyEngine::drawStaticObjects() {
 	for (uint i = 0; i < _objects.size(); i++) {
 		Thing &obj = _objects[i];
@@ -1689,101 +1814,98 @@ bool ColonyEngine::drawStaticObjectPrisms3D(Thing &obj) {
 		break;
 	case kRobQueen:
 		{
-			const int32 s1 = _sint[obj.where.ang] >> 1;
-			const int32 c1 = _cost[obj.where.ang] >> 1;
-			const int32 s2 = s1 >> 1;
-			const int32 c2 = c1 >> 1;
-			const int32 eyeBaseX = obj.where.xloc + c1;
-			const int32 eyeBaseY = obj.where.yloc + s1;
-
-			Thing leftEye = obj;
-			leftEye.where.xloc = (int)(eyeBaseX - s2);
-			leftEye.where.yloc = (int)(eyeBaseY + c2);
-			resetObjectBounds(_screenR, leftEye.where);
-
-			Thing rightEye = obj;
-			rightEye.where.xloc = (int)(eyeBaseX + s2);
-			rightEye.where.yloc = (int)(eyeBaseY - c2);
-			resetObjectBounds(_screenR, rightEye.where);
-
-			const int32 leftDist = (leftEye.where.xloc - _me.xloc) * (leftEye.where.xloc - _me.xloc) +
-				(leftEye.where.yloc - _me.yloc) * (leftEye.where.yloc - _me.yloc);
-			const int32 rightDist = (rightEye.where.xloc - _me.xloc) * (rightEye.where.xloc - _me.xloc) +
-				(rightEye.where.yloc - _me.yloc) * (rightEye.where.yloc - _me.yloc);
-			const bool leftFirst = leftDist >= rightDist;
-			Thing &farEye = leftFirst ? leftEye : rightEye;
-			Thing &nearEye = leftFirst ? rightEye : leftEye;
-			const PrismPartDef &farWing = leftFirst ? kQLWingDef : kQRWingDef;
-			const PrismPartDef &nearWing = leftFirst ? kQRWingDef : kQLWingDef;
+			EnemyEyePair eyes = buildEnemyEyePair(_screenR, obj, _me.look, _me.lookY, _me.xloc, _me.yloc, _sint, _cost);
+			Thing &firstEye = eyes.leftFirst ? eyes.left : eyes.right;
+			Thing &secondEye = eyes.leftFirst ? eyes.right : eyes.left;
+			const PrismPartDef &firstWing = eyes.leftFirst ? kQLWingDef : kQRWingDef;
+			const PrismPartDef &secondWing = eyes.leftFirst ? kQRWingDef : kQLWingDef;
 			const int wingColor = (!isMacRenderMode() && _level == 7) ? kColorQueenWingRed : kColorClear;
-
-			// DOS draweyes(): queen eyeball is RED fill + WHITE outline
-			// (hardcoded, not from color table). Iris is GREEN.
-			const int queenEyeballColor = 5; // vRED (EGA index 4 = kColorRed... use raw 5 = cRED index)
-			draw3DPrism(obj, farWing, false, wingColor, true, true);
-			draw3DSphere(farEye, 0, 0, 130, 0, 0, 155, queenEyeballColor, kColorBlack, true);
-			drawEyeOverlays3D(farEye, kQIrisDef, -1, kQPupilDef, pupilColor, true);
-			if (farEye.where.xmn < obj.where.xmn)
-				obj.where.xmn = farEye.where.xmn;
-			if (farEye.where.xmx > obj.where.xmx)
-				obj.where.xmx = farEye.where.xmx;
-			if (farEye.where.zmn < obj.where.zmn)
-				obj.where.zmn = farEye.where.zmn;
-			if (farEye.where.zmx > obj.where.zmx)
-				obj.where.zmx = farEye.where.zmx;
-
-			draw3DPrism(obj, kQThoraxDef, false, -1, true, false);
-			draw3DPrism(obj, kQAbdomenDef, false, -1, true, false);
-
-			draw3DPrism(obj, nearWing, false, wingColor, true, true);
-			draw3DSphere(nearEye, 0, 0, 130, 0, 0, 155, queenEyeballColor, kColorBlack, true);
-			drawEyeOverlays3D(nearEye, kQIrisDef, -1, kQPupilDef, pupilColor, true);
-			if (nearEye.where.xmn < obj.where.xmn)
-				obj.where.xmn = nearEye.where.xmn;
-			if (nearEye.where.xmx > obj.where.xmx)
-				obj.where.xmx = nearEye.where.xmx;
-			if (nearEye.where.zmn < obj.where.zmn)
-				obj.where.zmn = nearEye.where.zmn;
-			if (nearEye.where.zmx > obj.where.zmx)
-				obj.where.zmx = nearEye.where.zmx;
+			const int enemyEyeballColor = isMacRenderMode() ? eyeballColor : 5;
+			int layer = 12;
+			auto setNextDepthRange = [&]() {
+				_gfx->setDepthRange((layer--) * 0.002f, 1.0f);
+			};
+
+			auto drawEye = [&](Thing &eye) {
+				draw3DSphere(eye, 0, 0, 130, 0, 0, 155, enemyEyeballColor, kColorBlack, true);
+				drawEyeOverlays3D(eye, kQIrisDef, kColorQueenEye, kQPupilDef, pupilColor, true);
+				mergeObjectBounds(obj.where, eye.where);
+			};
+			auto drawWingEye = [&](Thing &eye, const PrismPartDef &wing) {
+				setNextDepthRange();
+				draw3DPrism(obj, wing, false, wingColor, true, true);
+				setNextDepthRange();
+				drawEye(eye);
+			};
+			auto drawThorax = [&]() {
+				setNextDepthRange();
+				draw3DPrism(obj, kQThoraxDef, false, -1, true, false);
+			};
+			auto drawAbdomen = [&]() {
+				setNextDepthRange();
+				draw3DPrism(obj, kQAbdomenDef, false, -1, true, false);
+			};
+
+			const int body = queenBodyOrder(_screenR, obj, _me.look, _me.lookY, _me.xloc, _me.yloc, _sint, _cost);
+			if (body == 0) {
+				drawThorax();
+				drawAbdomen();
+			}
+			drawWingEye(firstEye, firstWing);
+			if (body == 1) {
+				const bool abdomenBackVisible = prismSurfaceVisible(_screenR, obj, kQAbdomenDef, 8,
+					_me.look, _me.lookY, _me.xloc, _me.yloc, _sint, _cost);
+				if (abdomenBackVisible) {
+					drawAbdomen();
+					drawThorax();
+				} else {
+					drawThorax();
+					drawAbdomen();
+				}
+			}
+			drawWingEye(secondEye, secondWing);
+			if (body == 2) {
+				drawAbdomen();
+				drawThorax();
+			}
+			_gfx->setDepthRange(0.0f, 1.0f);
 		}
 		break;
 	case kRobDrone:
 		{
-			// DOS DRONE.C: body + seteyes()/draweyes() ??? same two-eye
-			// system as Queen, positioned at offsets from body center.
-			const int32 s1 = _sint[obj.where.ang] >> 1;
-			const int32 c1 = _cost[obj.where.ang] >> 1;
-			const int32 s2 = s1 >> 1;
-			const int32 c2 = c1 >> 1;
-			const int32 eyeBaseX = obj.where.xloc + c1;
-			const int32 eyeBaseY = obj.where.yloc + s1;
-
-			Thing leftEye = obj;
-			leftEye.where.xloc = (int)(eyeBaseX - s2);
-			leftEye.where.yloc = (int)(eyeBaseY + c2);
-			resetObjectBounds(_screenR, leftEye.where);
-			Thing rightEye = obj;
-			rightEye.where.xloc = (int)(eyeBaseX + s2);
-			rightEye.where.yloc = (int)(eyeBaseY - c2);
-			resetObjectBounds(_screenR, rightEye.where);
-
-			// DOS draweyes(): shared with Queen ??? RED eyeball, GREEN iris
-			draw3DPrism(obj, kDAbdomenDef, false, -1, true, false);
-			draw3DPrism(obj, kDLLPincerDef, false, -1, true, false);
-			draw3DPrism(obj, kDRRPincerDef, false, -1, true, false);
-			draw3DPrism(obj, kDLEyeDef, false, -1, true, false);
-			draw3DPrism(obj, kDREyeDef, false, -1, true, false);
-			draw3DSphere(leftEye, 0, 0, 130, 0, 0, 155, 5, kColorBlack, true);
-			drawEyeOverlays3D(leftEye, kQIrisDef, -1, kQPupilDef, pupilColor, true);
-			draw3DSphere(rightEye, 0, 0, 130, 0, 0, 155, 5, kColorBlack, true);
-			drawEyeOverlays3D(rightEye, kQIrisDef, -1, kQPupilDef, pupilColor, true);
+			EnemyEyePair eyes = buildEnemyEyePair(_screenR, obj, _me.look, _me.lookY, _me.xloc, _me.yloc, _sint, _cost);
+			Thing &firstEye = eyes.leftFirst ? eyes.left : eyes.right;
+			Thing &secondEye = eyes.leftFirst ? eyes.right : eyes.left;
+			const int enemyEyeballColor = isMacRenderMode() ? eyeballColor : 5;
+			int layer = 6;
+			auto setNextDepthRange = [&]() {
+				_gfx->setDepthRange((layer--) * 0.002f, 1.0f);
+			};
+			auto drawBody = [&]() {
+				setNextDepthRange();
+				draw3DPrism(obj, kDAbdomenDef, false, -1, true, false);
+			};
+			auto drawEye = [&](Thing &eye) {
+				setNextDepthRange();
+				draw3DSphere(eye, 0, 0, 130, 0, 0, 155, enemyEyeballColor, kColorBlack, true);
+				drawEyeOverlays3D(eye, kQIrisDef, kColorDroneEye, kQPupilDef, pupilColor, true);
+				mergeObjectBounds(obj.where, eye.where);
+			};
+
+			const int body = droneBodyOrder(_screenR, obj, _me.look, _me.lookY, _me.xloc, _me.yloc, _sint, _cost);
+			if (body == 0)
+				drawBody();
+			drawEye(firstEye);
+			if (body == 1)
+				drawBody();
+			drawEye(secondEye);
+			if (body == 2)
+				drawBody();
+			_gfx->setDepthRange(0.0f, 1.0f);
 		}
 		break;
 	case kRobSoldier:
 		{
-			// DOS DRONE.C MakeSoldier(): body + animated pincers +
-			// seteyes()/draweyes() ??? same two-eye system as Queen.
 			int leftPincerPts[4][3];
 			int rightPincerPts[4][3];
 			const int lookAmount = (obj.where.lookx < 0) ? -obj.where.lookx : obj.where.lookx;
@@ -1800,32 +1922,63 @@ bool ColonyEngine::drawStaticObjectPrisms3D(Thing &obj) {
 			const PrismPartDef leftPincerDef = {4, leftPincerPts, 4, kDLPincerSurf};
 			const PrismPartDef rightPincerDef = {4, rightPincerPts, 4, kDRPincerSurf};
 
-			const int32 s1 = _sint[obj.where.ang] >> 1;
-			const int32 c1 = _cost[obj.where.ang] >> 1;
-			const int32 s2 = s1 >> 1;
-			const int32 c2 = c1 >> 1;
-			const int32 eyeBaseX = obj.where.xloc + c1;
-			const int32 eyeBaseY = obj.where.yloc + s1;
-
-			Thing leftEye = obj;
-			leftEye.where.xloc = (int)(eyeBaseX - s2);
-			leftEye.where.yloc = (int)(eyeBaseY + c2);
-			resetObjectBounds(_screenR, leftEye.where);
-			Thing rightEye = obj;
-			rightEye.where.xloc = (int)(eyeBaseX + s2);
-			rightEye.where.yloc = (int)(eyeBaseY - c2);
-			resetObjectBounds(_screenR, rightEye.where);
-
-			draw3DPrism(obj, kDAbdomenDef, false, kColorSoldierBody, true, false);
-			draw3DPrism(obj, leftPincerDef, false, -1, true, false);
-			draw3DPrism(obj, rightPincerDef, false, -1, true, false);
-			// DOS draweyes(): shared with Queen ??? RED eyeball, GREEN iris
-			draw3DPrism(obj, kDLEyeDef, false, kColorSoldierEye, true, false);
-			draw3DPrism(obj, kDREyeDef, false, kColorSoldierEye, true, false);
-			draw3DSphere(leftEye, 0, 0, 130, 0, 0, 155, 5, kColorBlack, true);
-			drawEyeOverlays3D(leftEye, kQIrisDef, -1, kQPupilDef, pupilColor, true);
-			draw3DSphere(rightEye, 0, 0, 130, 0, 0, 155, 5, kColorBlack, true);
-			drawEyeOverlays3D(rightEye, kQIrisDef, -1, kQPupilDef, pupilColor, true);
+			EnemyEyePair eyes = buildEnemyEyePair(_screenR, obj, _me.look, _me.lookY, _me.xloc, _me.yloc, _sint, _cost);
+			Thing &firstEye = eyes.leftFirst ? eyes.left : eyes.right;
+			Thing &secondEye = eyes.leftFirst ? eyes.right : eyes.left;
+			const int enemyEyeballColor = isMacRenderMode() ? eyeballColor : 5;
+			const float leftPincerDepth = prismPointForwardDepth(obj, leftPincerDef, 1, _me.look, _me.xloc, _me.yloc, _sint, _cost);
+			const float rightPincerDepth = prismPointForwardDepth(obj, rightPincerDef, 1, _me.look, _me.xloc, _me.yloc, _sint, _cost);
+			int layer = 8;
+			auto setNextDepthRange = [&]() {
+				_gfx->setDepthRange((layer--) * 0.002f, 1.0f);
+			};
+			auto drawBody = [&]() {
+				setNextDepthRange();
+				draw3DPrism(obj, kDAbdomenDef, false, kColorSoldierBody, true, false);
+			};
+			auto drawEye = [&](Thing &eye) {
+				setNextDepthRange();
+				draw3DSphere(eye, 0, 0, 130, 0, 0, 155, enemyEyeballColor, kColorBlack, true);
+				drawEyeOverlays3D(eye, kQIrisDef, kColorSoldierEye, kQPupilDef, pupilColor, true);
+				mergeObjectBounds(obj.where, eye.where);
+			};
+			auto drawPincers = [&]() {
+				if (leftPincerDepth < rightPincerDepth) {
+					setNextDepthRange();
+					draw3DPrism(obj, rightPincerDef, false, -1, true, false);
+					setNextDepthRange();
+					draw3DPrism(obj, leftPincerDef, false, -1, true, false);
+				} else {
+					setNextDepthRange();
+					draw3DPrism(obj, leftPincerDef, false, -1, true, false);
+					setNextDepthRange();
+					draw3DPrism(obj, rightPincerDef, false, -1, true, false);
+				}
+			};
+
+			const int body = droneBodyOrder(_screenR, obj, _me.look, _me.lookY, _me.xloc, _me.yloc, _sint, _cost);
+			switch (body) {
+			case 0:
+				drawBody();
+				drawEye(firstEye);
+				drawEye(secondEye);
+				drawPincers();
+				break;
+			case 1:
+				drawEye(firstEye);
+				drawBody();
+				drawPincers();
+				drawEye(secondEye);
+				break;
+			case 2:
+			default:
+				drawEye(firstEye);
+				drawEye(secondEye);
+				drawPincers();
+				drawBody();
+				break;
+			}
+			_gfx->setDepthRange(0.0f, 1.0f);
 		}
 		break;
 	case kRobSnoop:


Commit: 114ad8c194f440fc166840900be0b5c1cced3962
    https://github.com/scummvm/scummvm/commit/114ad8c194f440fc166840900be0b5c1cced3962
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-05T19:28:08+02:00

Commit Message:
COLONY: more fluent animations for grow/shrink enemies

Changed paths:
    engines/colony/colony.cpp
    engines/colony/colony.h
    engines/colony/render_objects.cpp


diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index abdeed7de21..c0f53f56991 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -849,6 +849,7 @@ Common::Error ColonyEngine::run() {
 	bool mouseMoved = false;
 	uint32 lastMoveTick = _system->getMillis();
 	uint32 lastColonyTick = lastMoveTick;
+	_lastColonyThinkTime = lastColonyTick;
 	uint32 lastBattleTick = lastMoveTick;
 	uint32 lastCenterTick = lastMoveTick;
 	while (!shouldQuit()) {
@@ -876,6 +877,7 @@ Common::Error ColonyEngine::run() {
 		// 125ms (~8fps) matches the original balance for robot aggression.
 		if (_gameMode == kModeColony && now - lastColonyTick >= 125) {
 			lastColonyTick = now;
+			_lastColonyThinkTime = now;
 			cThink();
 		}
 
diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index f3de2a2e2df..fa50a5435c8 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -557,6 +557,7 @@ private:
 	uint32 _blackoutColor = 0;
 	uint32 _lastClickTime = 0;
 	uint32 _displayCount = 0; // Frame counter for COLOR wall animation (Mac: count)
+	uint32 _lastColonyThinkTime = 0; // Last 125ms CThink tick, for render-only interpolation
 	uint32 _lastHotfootTime = 0;  // Time-gate for HOTFOOT damage (~8fps)
 	uint32 _lastAnimUpdate = 0;
 	uint32 _lastWarningChimeTime = 0;
@@ -654,6 +655,10 @@ private:
 	void drawPrismOval3D(Thing &thing, const PrismPartDef &def, bool useLook, int colorOverride, bool forceVisible = false);
 	void drawEyeOverlays3D(Thing &thing, const PrismPartDef &irisDef, int irisColorOverride,
 		const PrismPartDef &pupilDef, int pupilColorOverride, bool useLook);
+	float growRenderTickFraction() const;
+	bool drawInterpolatedGrowRobot(Thing &obj, int eyeballColor, int pupilColor);
+	void drawInterpolatedGrowPrism(Thing &obj, const PrismPartDef &fromDef, const PrismPartDef &toDef, float progress);
+	void drawInterpolatedGrowEye(Thing &obj, int fromStage, int toStage, float progress, int eyeballColor, int pupilColor);
 	bool drawStaticObjectPrisms3D(Thing &obj);
 	void initRobots();
 	void renderCorridor3D();
diff --git a/engines/colony/render_objects.cpp b/engines/colony/render_objects.cpp
index 60cbf8bfde0..b5d55332ac9 100644
--- a/engines/colony/render_objects.cpp
+++ b/engines/colony/render_objects.cpp
@@ -1452,6 +1452,180 @@ void ColonyEngine::drawEyeOverlays3D(Thing &thing, const PrismPartDef &irisDef,
 	drawPrismOval3D(thing, pupilDef, useLook, pupilColorOverride, true);
 }
 
+namespace {
+
+int interpolatedRobotPoint(int from, int to, float progress) {
+	return (int)roundf((float)from + ((float)to - (float)from) * progress);
+}
+
+bool robotGrowShapeAndStage(int type, int &shape, int &stage) {
+	if (type < kRobEye || type > kRobMUPyramid)
+		return false;
+
+	shape = (type - kRobEye) & 3;
+	stage = (type - kRobEye) >> 2;
+	return true;
+}
+
+const ColonyEngine::PrismPartDef *growPrismDefForStage(int shape, int stage) {
+	switch (shape) {
+	case 1:
+		switch (stage) {
+		case 0: return &kPyramidBodyDef;
+		case 1: return &kFPyramidBodyDef;
+		case 2: return &kSPyramidBodyDef;
+		case 3: return &kMPyramidBodyDef;
+		default: return nullptr;
+		}
+	case 2:
+		switch (stage) {
+		case 0: return &kCubeBodyDef;
+		case 1: return &kFCubeBodyDef;
+		case 2: return &kSCubeBodyDef;
+		case 3: return &kMCubeBodyDef;
+		default: return nullptr;
+		}
+	case 3:
+		switch (stage) {
+		case 0: return &kUPyramidBodyDef;
+		case 1: return &kFUPyramidBodyDef;
+		case 2: return &kSUPyramidBodyDef;
+		case 3: return &kMUPyramidBodyDef;
+		default: return nullptr;
+		}
+	default:
+		return nullptr;
+	}
+}
+
+const ColonyEngine::PrismPartDef &growEyeIrisDefForStage(int stage) {
+	switch (stage) {
+	case 0: return kEyeIrisDef;
+	case 1: return kFEyeIrisDef;
+	case 2: return kSEyeIrisDef;
+	case 3:
+	default:
+		return kMEyeIrisDef;
+	}
+}
+
+const ColonyEngine::PrismPartDef &growEyePupilDefForStage(int stage) {
+	switch (stage) {
+	case 0: return kEyePupilDef;
+	case 1: return kFEyePupilDef;
+	case 2: return kSEyePupilDef;
+	case 3:
+	default:
+		return kMEyePupilDef;
+	}
+}
+
+} // namespace
+
+float ColonyEngine::growRenderTickFraction() const {
+	const uint32 kColonyThinkIntervalMs = 125;
+	const uint32 now = _system->getMillis();
+
+	if (_lastColonyThinkTime == 0 || now <= _lastColonyThinkTime)
+		return 0.0f;
+
+	const uint32 elapsed = now - _lastColonyThinkTime;
+	if (elapsed >= kColonyThinkIntervalMs)
+		return 1.0f;
+
+	return (float)elapsed / (float)kColonyThinkIntervalMs;
+}
+
+void ColonyEngine::drawInterpolatedGrowPrism(Thing &obj, const PrismPartDef &fromDef, const PrismPartDef &toDef, float progress) {
+	if (fromDef.pointCount != toDef.pointCount || fromDef.surfaceCount != toDef.surfaceCount)
+		return;
+
+	int points[8][3];
+	assert(fromDef.pointCount <= ARRAYSIZE(points));
+	for (int i = 0; i < fromDef.pointCount; ++i) {
+		points[i][0] = interpolatedRobotPoint(fromDef.points[i][0], toDef.points[i][0], progress);
+		points[i][1] = interpolatedRobotPoint(fromDef.points[i][1], toDef.points[i][1], progress);
+		points[i][2] = interpolatedRobotPoint(fromDef.points[i][2], toDef.points[i][2], progress);
+	}
+
+	const PrismPartDef def = {fromDef.pointCount, points, fromDef.surfaceCount, fromDef.surfaces};
+	draw3DPrism(obj, def, false, -1, true, false);
+}
+
+void ColonyEngine::drawInterpolatedGrowEye(Thing &obj, int fromStage, int toStage, float progress, int eyeballColor, int pupilColor) {
+	const int sphereZ[4][2] = {
+		{100, 200},
+		{0, 100},
+		{0, 50},
+		{0, 25}
+	};
+	int irisPoints[4][3];
+	int pupilPoints[4][3];
+
+	fromStage = CLIP(fromStage, 0, 3);
+	toStage = CLIP(toStage, 0, 3);
+
+	const int z0 = interpolatedRobotPoint(sphereZ[fromStage][0], sphereZ[toStage][0], progress);
+	const int z1 = interpolatedRobotPoint(sphereZ[fromStage][1], sphereZ[toStage][1], progress);
+	draw3DSphere(obj, 0, 0, z0, 0, 0, z1, eyeballColor, kColorBlack, true);
+
+	const PrismPartDef &fromIris = growEyeIrisDefForStage(fromStage);
+	const PrismPartDef &toIris = growEyeIrisDefForStage(toStage);
+	const PrismPartDef &fromPupil = growEyePupilDefForStage(fromStage);
+	const PrismPartDef &toPupil = growEyePupilDefForStage(toStage);
+	for (int i = 0; i < 4; ++i) {
+		irisPoints[i][0] = interpolatedRobotPoint(fromIris.points[i][0], toIris.points[i][0], progress);
+		irisPoints[i][1] = interpolatedRobotPoint(fromIris.points[i][1], toIris.points[i][1], progress);
+		irisPoints[i][2] = interpolatedRobotPoint(fromIris.points[i][2], toIris.points[i][2], progress);
+		pupilPoints[i][0] = interpolatedRobotPoint(fromPupil.points[i][0], toPupil.points[i][0], progress);
+		pupilPoints[i][1] = interpolatedRobotPoint(fromPupil.points[i][1], toPupil.points[i][1], progress);
+		pupilPoints[i][2] = interpolatedRobotPoint(fromPupil.points[i][2], toPupil.points[i][2], progress);
+	}
+
+	const PrismPartDef irisDef = {4, irisPoints, fromIris.surfaceCount, fromIris.surfaces};
+	const PrismPartDef pupilDef = {4, pupilPoints, fromPupil.surfaceCount, fromPupil.surfaces};
+	const int irisColor = (toStage == 3 && progress >= 0.5f) ? kColorMiniEyeIris : -1;
+	drawEyeOverlays3D(obj, irisDef, irisColor, pupilDef, pupilColor, false);
+}
+
+bool ColonyEngine::drawInterpolatedGrowRobot(Thing &obj, int eyeballColor, int pupilColor) {
+	int shape = 0;
+	int stage = 0;
+	if (obj.grow == 0 || !robotGrowShapeAndStage(obj.type, shape, stage))
+		return false;
+
+	int targetStage = stage;
+	if (obj.grow > 0) {
+		if (stage <= 0 || stage >= 3)
+			return false;
+		targetStage = stage - 1;
+	} else {
+		if (stage <= 0 || stage >= 3)
+			return false;
+		targetStage = stage + 1;
+	}
+
+	const int count = CLIP(obj.count, 0, 3);
+	float progress = ((float)count + growRenderTickFraction()) * 0.25f;
+	if (progress < 0.0f)
+		progress = 0.0f;
+	if (progress > 1.0f)
+		progress = 1.0f;
+
+	if (shape == 0) {
+		drawInterpolatedGrowEye(obj, stage, targetStage, progress, eyeballColor, pupilColor);
+		return true;
+	}
+
+	const PrismPartDef *fromDef = growPrismDefForStage(shape, stage);
+	const PrismPartDef *toDef = growPrismDefForStage(shape, targetStage);
+	if (!fromDef || !toDef)
+		return false;
+
+	drawInterpolatedGrowPrism(obj, *fromDef, *toDef, progress);
+	return true;
+}
+
 bool ColonyEngine::drawStaticObjectPrisms3D(Thing &obj) {
 	const int eyeballColor = (_level == 1 || _level == 7) ? kColorPupil : kColorEyeball;
 	const int pupilColor = (_level == 1 || _level == 7) ? kColorEyeball : kColorPupil;
@@ -1774,42 +1948,66 @@ bool ColonyEngine::drawStaticObjectPrisms3D(Thing &obj) {
 		_gfx->setDepthRange(0.0f, 1.0f);
 		break;
 	case kRobFEye:
+		if (drawInterpolatedGrowRobot(obj, eyeballColor, pupilColor))
+			break;
 		draw3DSphere(obj, 0, 0, 0, 0, 0, 100, eyeballColor, kColorBlack, true);
 		drawEyeOverlays3D(obj, kFEyeIrisDef, -1, kFEyePupilDef, pupilColor, false);
 		break;
 	case kRobFPyramid:
+		if (drawInterpolatedGrowRobot(obj, eyeballColor, pupilColor))
+			break;
 		draw3DPrism(obj, kFPyramidBodyDef, false, -1, true, false);
 		break;
 	case kRobFCube:
+		if (drawInterpolatedGrowRobot(obj, eyeballColor, pupilColor))
+			break;
 		draw3DPrism(obj, kFCubeBodyDef, false, -1, true, false);
 		break;
 	case kRobFUPyramid:
+		if (drawInterpolatedGrowRobot(obj, eyeballColor, pupilColor))
+			break;
 		draw3DPrism(obj, kFUPyramidBodyDef, false, -1, true, false);
 		break;
 	case kRobSEye:
+		if (drawInterpolatedGrowRobot(obj, eyeballColor, pupilColor))
+			break;
 		draw3DSphere(obj, 0, 0, 0, 0, 0, 50, eyeballColor, kColorBlack, true);
 		drawEyeOverlays3D(obj, kSEyeIrisDef, -1, kSEyePupilDef, pupilColor, false);
 		break;
 	case kRobSPyramid:
+		if (drawInterpolatedGrowRobot(obj, eyeballColor, pupilColor))
+			break;
 		draw3DPrism(obj, kSPyramidBodyDef, false, -1, true, false);
 		break;
 	case kRobSCube:
+		if (drawInterpolatedGrowRobot(obj, eyeballColor, pupilColor))
+			break;
 		draw3DPrism(obj, kSCubeBodyDef, false, -1, true, false);
 		break;
 	case kRobSUPyramid:
+		if (drawInterpolatedGrowRobot(obj, eyeballColor, pupilColor))
+			break;
 		draw3DPrism(obj, kSUPyramidBodyDef, false, -1, true, false);
 		break;
 	case kRobMEye:
+		if (drawInterpolatedGrowRobot(obj, eyeballColor, pupilColor))
+			break;
 		draw3DSphere(obj, 0, 0, 0, 0, 0, 25, eyeballColor, kColorBlack, true);
 		drawEyeOverlays3D(obj, kMEyeIrisDef, kColorMiniEyeIris, kMEyePupilDef, pupilColor, false);
 		break;
 	case kRobMPyramid:
+		if (drawInterpolatedGrowRobot(obj, eyeballColor, pupilColor))
+			break;
 		draw3DPrism(obj, kMPyramidBodyDef, false, -1, true, false);
 		break;
 	case kRobMCube:
+		if (drawInterpolatedGrowRobot(obj, eyeballColor, pupilColor))
+			break;
 		draw3DPrism(obj, kMCubeBodyDef, false, -1, true, false);
 		break;
 	case kRobMUPyramid:
+		if (drawInterpolatedGrowRobot(obj, eyeballColor, pupilColor))
+			break;
 		draw3DPrism(obj, kMUPyramidBodyDef, false, -1, true, false);
 		break;
 	case kRobQueen:


Commit: 8ff586d3ddef50330ac7903bac42326b4b846bc6
    https://github.com/scummvm/scummvm/commit/8ff586d3ddef50330ac7903bac42326b4b846bc6
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-05T19:42:52+02:00

Commit Message:
COLONY: better detection for aiming enemies

Changed paths:
    engines/colony/colony.h
    engines/colony/interaction.cpp
    engines/colony/render_objects.cpp


diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index fa50a5435c8..9650a513ad9 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -689,6 +689,10 @@ private:
 	// shoot.c: shooting and power management
 	void setPower(int p0, int p1, int p2);
 	void cShoot();
+	bool isShootableRobotType(int type) const;
+	bool isShotBlockingObjectType(int type) const;
+	int findAimedObject(const Common::Point &aim, bool *isBlocker = nullptr, int *targetDist = nullptr) const;
+	bool hasAimedRobotTarget() const;
 	void destroyRobot(int num);
 	void doShootCircles(int cx, int cy);
 	void doBurnHole(int cx, int cy, int radius);
diff --git a/engines/colony/interaction.cpp b/engines/colony/interaction.cpp
index 3f791c564d4..f786284222a 100644
--- a/engines/colony/interaction.cpp
+++ b/engines/colony/interaction.cpp
@@ -342,6 +342,62 @@ void ColonyEngine::setPower(int p0, int p1, int p2) {
 	}
 }
 
+bool ColonyEngine::isShootableRobotType(int type) const {
+	return (type >= kRobEye && type <= kRobUPyramid) ||
+		type == kRobQueen || type == kRobDrone || type == kRobSoldier;
+}
+
+bool ColonyEngine::isShotBlockingObjectType(int type) const {
+	return type == kObjForkLift || type == kObjTeleport || type == kObjPToilet ||
+		type == kObjBox2 || type == kObjReactor || type == kObjScreen;
+}
+
+int ColonyEngine::findAimedObject(const Common::Point &aim, bool *isBlocker, int *targetDist) const {
+	int bestIdx = -1;
+	int bestDist = INT_MAX;
+	bool bestIsBlocker = false;
+
+	for (uint i = 0; i < _objects.size(); i++) {
+		const Thing &obj = _objects[i];
+		if (!obj.alive)
+			continue;
+
+		if (obj.where.xmn > obj.where.xmx || obj.where.zmn > obj.where.zmx)
+			continue;
+		if (obj.where.xmn > aim.x || obj.where.xmx < aim.x ||
+			obj.where.zmn > aim.y || obj.where.zmx < aim.y)
+			continue;
+
+		const int type = obj.type;
+		const bool isRobot = isShootableRobotType(type);
+		const bool isBlockerObject = isShotBlockingObjectType(type);
+		if (!isRobot && !isBlockerObject)
+			continue;
+
+		const int dx = obj.where.xloc - _me.xloc;
+		const int dy = obj.where.yloc - _me.yloc;
+		const int dist = (int)sqrtf((float)(dx * dx + dy * dy));
+
+		if (dist < bestDist) {
+			bestDist = dist;
+			bestIdx = (int)i + 1; // 1-based object index
+			bestIsBlocker = isBlockerObject;
+		}
+	}
+
+	if (isBlocker)
+		*isBlocker = bestIsBlocker;
+	if (targetDist)
+		*targetDist = bestDist;
+	return bestIdx;
+}
+
+bool ColonyEngine::hasAimedRobotTarget() const {
+	bool blocker = false;
+	const int target = findAimedObject(getAimPoint(), &blocker);
+	return target > 0 && !blocker && isShootableRobotType(_objects[target - 1].type);
+}
+
 // shoot.c CShoot(): player fires weapon at the cursor or screen center.
 // Original hit detection uses the rendered object bounds on screen.
 void ColonyEngine::cShoot() {
@@ -368,39 +424,9 @@ void ColonyEngine::cShoot() {
 	// Drain weapons power: -(1 << level) per shot
 	setPower(-(1 << _level), 0, 0);
 
-	int bestIdx = -1;
-	int bestDist = INT_MAX;
 	bool bestIsBlocker = false;
-
-	for (uint i = 0; i < _objects.size(); i++) {
-		const Thing &obj = _objects[i];
-		if (!obj.alive)
-			continue;
-
-		if (obj.where.xmn > obj.where.xmx || obj.where.zmn > obj.where.zmx)
-			continue;
-		if (obj.where.xmn > cx || obj.where.xmx < cx ||
-			obj.where.zmn > cy || obj.where.zmx < cy)
-			continue;
-
-		int t = obj.type;
-		bool isRobot = (t >= kRobEye && t <= kRobUPyramid) ||
-			t == kRobQueen || t == kRobDrone || t == kRobSoldier;
-		bool blocksShot = (t == kObjForkLift || t == kObjTeleport || t == kObjPToilet ||
-			t == kObjBox2 || t == kObjReactor || t == kObjScreen);
-		if (!isRobot && !blocksShot)
-			continue;
-
-		int dx = obj.where.xloc - _me.xloc;
-		int dy = obj.where.yloc - _me.yloc;
-		int dist = (int)sqrtf((float)(dx * dx + dy * dy));
-
-		if (dist < bestDist) {
-			bestDist = dist;
-			bestIdx = (int)i + 1; // 1-based robot index
-			bestIsBlocker = blocksShot;
-		}
-	}
+	int bestDist = INT_MAX;
+	const int bestIdx = findAimedObject(aim, &bestIsBlocker, &bestDist);
 
 	if (bestIdx > 0 && !bestIsBlocker) {
 		const Thing &target = _objects[bestIdx - 1];
diff --git a/engines/colony/render_objects.cpp b/engines/colony/render_objects.cpp
index b5d55332ac9..5d49db97a8e 100644
--- a/engines/colony/render_objects.cpp
+++ b/engines/colony/render_objects.cpp
@@ -1309,15 +1309,11 @@ void ColonyEngine::drawStaticObjects() {
 		if (ox < 0 || ox >= 32 || oy < 0 || oy >= 32 || !_visibleCell[ox][oy])
 			continue;
 		drawStaticObjectPrisms3D(obj);
-		// MAKEROBO.C: if shootable robot straddles centerX, set insight
-		// (narrows crosshair brackets to indicate a target is in the line of fire)
-		int t = obj.type;
-		if ((t >= kRobEye && t <= kRobUPyramid) ||
-			(t >= kRobQueen && t <= kRobSoldier)) {
-			if (obj.where.xmn < _centerX && obj.where.xmx > _centerX)
-				_insight = true;
-		}
 	}
+
+	// Use the same rendered-bounds target selection as cShoot() so the
+	// narrowed crosshair only appears for a shot that would select a robot.
+	_insight = _weapons > 0 && hasAimedRobotTarget();
 }
 
 void ColonyEngine::drawPrismOval3D(Thing &thing, const PrismPartDef &def, bool useLook, int colorOverride, bool forceVisible) {


Commit: e997e181e40ae1b9469c26f5591ee1137dfc0e0d
    https://github.com/scummvm/scummvm/commit/e997e181e40ae1b9469c26f5591ee1137dfc0e0d
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-05T21:36:22+02:00

Commit Message:
COLONY: better detection collision detection with static objects

Changed paths:
    engines/colony/colony.cpp
    engines/colony/colony.h
    engines/colony/interaction.cpp
    engines/colony/map.cpp
    engines/colony/movement.cpp
    engines/colony/render_objects.cpp
    engines/colony/savegame.cpp
    engines/colony/sound.cpp


diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index c0f53f56991..df1733b3671 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -51,8 +51,6 @@
 
 namespace Colony {
 
-namespace {
-
 const float kMouseLookSensitivity = 0.25f;
 const float kKeyboardTurnSpeed = 30.0f; // angle units per second
 
@@ -131,8 +129,6 @@ Graphics::Cursor *cloneAndScaleCursor(const Graphics::Cursor &src, int scale) {
 		src.getPaletteStartIndex(), src.getPaletteCount(), scale);
 }
 
-} // anonymous namespace
-
 ColonyEngine::ColonyEngine(OSystem *syst, const ADGameDescription *gd) : Engine(syst), _gameDescription(gd), _randomSource("colony") {
 	_level = 0;
 	_robotNum = 0;
diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index 9650a513ad9..15678f332a5 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -466,6 +466,9 @@ public:
 	int checkwallMoveTo(int xnew, int ynew, int xind2, int yind2, Locate *pobject, uint8 trailCode);
 	int checkwallTryFeature(int xnew, int ynew, int xind2, int yind2, Locate *pobject, int dir);
 	int checkwall(int xnew, int ynew, Locate *pobject);
+	void clearPlayerCellMarker();
+	void setPlayerCellMarker();
+	bool playerIntersectsObjectFootprint(const Thing &obj, int xloc, int yloc) const;
 	void cCommand(int xnew, int ynew, bool allowInteraction);
 	bool scrollInfo(const Graphics::Font *macFont = nullptr);
 	bool checkSkipRequested();
@@ -668,7 +671,7 @@ private:
 	void getWallFace3D(int cellX, int cellY, int direction, float corners[4][3]);
 	void getCellFace3D(int cellX, int cellY, bool ceiling, float corners[4][3]);
 
-	int occupiedObjectAt(int x, int y, const Locate *pobject);
+	int occupiedObjectAt(int xnew, int ynew, int x, int y, const Locate *pobject);
 	void interactWithObject(int objNum);
 
 	// Convert a mouse coord delivered by the event manager into engine
diff --git a/engines/colony/interaction.cpp b/engines/colony/interaction.cpp
index f786284222a..57709656628 100644
--- a/engines/colony/interaction.cpp
+++ b/engines/colony/interaction.cpp
@@ -175,10 +175,10 @@ void ColonyEngine::interactWithObject(int objNum) {
 		_me.xloc = (targetX << 8) + 128;
 		_me.yloc = (targetY << 8) + 128;
 		_me.ang = _me.look;
-		if (oldX >= 0 && oldX < 32 && oldY >= 0 && oldY < 32)
+		if (oldX >= 0 && oldX < 32 && oldY >= 0 && oldY < 32 &&
+				_robotArray[oldX][oldY] == kMeNum)
 			_robotArray[oldX][oldY] = 0;
-		if (_me.xindex >= 0 && _me.xindex < 32 && _me.yindex >= 0 && _me.yindex < 32)
-			_robotArray[_me.xindex][_me.yindex] = kMeNum;
+		setPlayerCellMarker();
 		break;
 	}
 	case kObjDrawer:
diff --git a/engines/colony/map.cpp b/engines/colony/map.cpp
index 538cd08c28d..e488ed58ac5 100644
--- a/engines/colony/map.cpp
+++ b/engines/colony/map.cpp
@@ -164,8 +164,7 @@ void ColonyEngine::loadMap(int mnum) {
 	doPatch();  // apply object relocations from patch table
 	initRobots();  // spawn robot objects for this level
 
-	if (_me.xindex >= 0 && _me.xindex < 32 && _me.yindex >= 0 && _me.yindex < 32)
-		_robotArray[_me.xindex][_me.yindex] = kMeNum;
+	setPlayerCellMarker();
 	debugC(1, kColonyDebugMap, "Successfully loaded map %d (objects: %d)", mnum, (int)_objects.size());
 }
 
diff --git a/engines/colony/movement.cpp b/engines/colony/movement.cpp
index fd550ab45ed..85179c9cdf8 100644
--- a/engines/colony/movement.cpp
+++ b/engines/colony/movement.cpp
@@ -151,6 +151,118 @@ int tunnelClipCode(const Common::Rect &rect, int x, int y) {
 	return code;
 }
 
+struct ObjectFootprint {
+	int centerX;
+	int centerY;
+	int halfX;
+	int halfY;
+};
+
+bool objectFootprintForType(int type, ObjectFootprint &fp) {
+	fp.centerX = 0;
+	fp.centerY = 0;
+	fp.halfX = 128;
+	fp.halfY = 128;
+
+	switch (type) {
+	case kObjPlant:
+		fp.halfX = 45;
+		fp.halfY = 45;
+		return true;
+	case kObjCChair:
+		fp.halfX = 55;
+		fp.halfY = 65;
+		return true;
+	case kObjChair:
+		fp.centerX = -15;
+		fp.halfX = 70;
+		fp.halfY = 75;
+		return true;
+	case kObjCouch:
+		fp.centerX = -15;
+		fp.halfX = 80;
+		fp.halfY = 128;
+		return true;
+	case kObjTV:
+		fp.halfX = 35;
+		fp.halfY = 65;
+		return true;
+	case kObjScreen:
+		fp.halfX = 20;
+		fp.halfY = 70;
+		return true;
+	case kObjConsole:
+		fp.centerX = -55;
+		fp.halfX = 55;
+		fp.halfY = 75;
+		return true;
+	case kObjDrawer:
+		fp.centerX = -40;
+		fp.halfX = 45;
+		fp.halfY = 75;
+		return true;
+	case kObjTub:
+		fp.centerX = -64;
+		fp.halfX = 70;
+		fp.halfY = 128;
+		return true;
+	case kObjSink:
+		fp.centerX = -90;
+		fp.halfX = 45;
+		fp.halfY = 60;
+		return true;
+	case kObjToilet:
+		fp.centerX = -75;
+		fp.halfX = 60;
+		fp.halfY = 50;
+		return true;
+	case kObjBench:
+		fp.halfX = 65;
+		fp.halfY = 128;
+		return true;
+	case kObjCBench:
+		fp.centerX = 35;
+		fp.centerY = -35;
+		fp.halfX = 100;
+		fp.halfY = 100;
+		return true;
+	case kObjProjector:
+		fp.halfX = 60;
+		fp.halfY = 60;
+		return true;
+	case kObjPowerSuit:
+		fp.halfX = 120;
+		fp.halfY = 120;
+		return true;
+	case kObjBox1:
+	case kObjBox2:
+		fp.halfX = 55;
+		fp.halfY = 55;
+		return true;
+	case kObjForkLift:
+		fp.halfX = 100;
+		fp.halfY = 120;
+		return true;
+	case kObjCryo:
+		fp.halfX = 128;
+		fp.halfY = 75;
+		return true;
+	case kObjTeleport:
+		fp.halfX = 105;
+		fp.halfY = 105;
+		return true;
+	case kObjDesk:
+	case kObjBed:
+	case kObjBBed:
+	case kObjTable:
+	case kObjReactor:
+	case kObjPToilet:
+		return true;
+	default:
+		return false;
+	}
+}
+
 void drawTunnelLine(Renderer *gfx, const Common::Rect &rect, int x1, int y1, int x2, int y2, uint32 color) {
 	if (rect.isEmpty())
 		return;
@@ -208,7 +320,44 @@ void drawTunnelLine(Renderer *gfx, const Common::Rect &rect, int x1, int y1, int
 	}
 }
 
-int ColonyEngine::occupiedObjectAt(int x, int y, const Locate *pobject) {
+void ColonyEngine::clearPlayerCellMarker() {
+	if (_me.xindex >= 0 && _me.xindex < 32 && _me.yindex >= 0 && _me.yindex < 32 &&
+			_robotArray[_me.xindex][_me.yindex] == kMeNum)
+		_robotArray[_me.xindex][_me.yindex] = 0;
+}
+
+void ColonyEngine::setPlayerCellMarker() {
+	if (_me.xindex < 0 || _me.xindex >= 32 || _me.yindex < 0 || _me.yindex >= 32)
+		return;
+
+	const int rnum = _robotArray[_me.xindex][_me.yindex];
+	if (rnum > 0 && rnum != kMeNum && rnum <= (int)_objects.size()) {
+		const Thing &obj = _objects[rnum - 1];
+		if (obj.alive && obj.where.xindex == _me.xindex && obj.where.yindex == _me.yindex)
+			return;
+	}
+
+	_robotArray[_me.xindex][_me.yindex] = kMeNum;
+}
+
+bool ColonyEngine::playerIntersectsObjectFootprint(const Thing &obj, int xloc, int yloc) const {
+	ObjectFootprint fp;
+	if (!objectFootprintForType(obj.type, fp))
+		return true;
+
+	const int kPlayerObjectPad = 32;
+	const int objAng = (obj.where.ang + 32) & 0xFF;
+	const uint8 invAng = (uint8)(0 - objAng);
+	const int wx = xloc - obj.where.xloc;
+	const int wy = yloc - obj.where.yloc;
+	const int lx = (int)(((int32)wx * _cost[invAng] - (int32)wy * _sint[invAng]) >> 7) - fp.centerX;
+	const int ly = (int)(((int32)wx * _sint[invAng] + (int32)wy * _cost[invAng]) >> 7) - fp.centerY;
+
+	return ABS(lx) <= fp.halfX + kPlayerObjectPad &&
+		ABS(ly) <= fp.halfY + kPlayerObjectPad;
+}
+
+int ColonyEngine::occupiedObjectAt(int xnew, int ynew, int x, int y, const Locate *pobject) {
 	if (x < 0 || x >= 32 || y < 0 || y >= 32)
 		return -1;
 	const int rnum = _robotArray[x][y];
@@ -216,6 +365,12 @@ int ColonyEngine::occupiedObjectAt(int x, int y, const Locate *pobject) {
 		return 0;
 	if (pobject == &_me && rnum <= (int)_objects.size()) {
 		Thing &obj = _objects[rnum - 1];
+		// Only the player gets tight static-object footprints. Robots,
+		// snoops, and scan rays keep full-cell blocking so enemies cannot
+		// enter or path through cells occupied by objects.
+		if (obj.type > kBaseObject && obj.type != kObjCWall && obj.type != kObjFWall &&
+				!playerIntersectsObjectFootprint(obj, xnew, ynew))
+			return 0;
 		if (obj.type <= kBaseObject)
 			obj.where.look = obj.where.ang = _me.ang + 128;
 	}
@@ -320,7 +475,7 @@ void ColonyEngine::clampToDiagonalWalls(Locate *p) {
 }
 
 int ColonyEngine::checkwallMoveTo(int xnew, int ynew, int xind2, int yind2, Locate *pobject, uint8 trailCode) {
-	const int rnum = occupiedObjectAt(xind2, yind2, pobject);
+	const int rnum = occupiedObjectAt(xnew, ynew, xind2, yind2, pobject);
 	if (rnum)
 		return rnum;
 	if (trailCode != 0 && pobject->type == kMeNum &&
@@ -345,7 +500,7 @@ int ColonyEngine::checkwallTryFeature(int xnew, int ynew, int xind2, int yind2,
 	if (r == 2)
 		return 0; // teleported  position already updated by the feature
 	if (r == 1) {
-		const int rnum = occupiedObjectAt(xind2, yind2, pobject);
+		const int rnum = occupiedObjectAt(xnew, ynew, xind2, yind2, pobject);
 		if (rnum) {
 			const bool showDoorText = (pobject == &_me && feature &&
 				(feature[0] == kWallFeatureDoor || feature[0] == kWallFeatureAirlock));
@@ -392,11 +547,18 @@ int ColonyEngine::checkwall(int xnew, int ynew, Locate *pobject) {
 
 	if (xind2 == pobject->xindex) {
 		if (yind2 == pobject->yindex) {
+			if (pobject == &_me) {
+				const int rnum = occupiedObjectAt(xnew, ynew, xind2, yind2, pobject);
+				if (rnum)
+					return rnum;
+			}
 			pobject->dx = xnew - pobject->xloc;
 			pobject->dy = ynew - pobject->yloc;
 			pobject->xloc = xnew;
 			pobject->yloc = ynew;
 			clampToWalls(pobject);
+			if (pobject == &_me)
+				clampToDiagonalWalls(pobject);
 			return 0;
 		}
 
@@ -588,9 +750,7 @@ int ColonyEngine::goToDestination(const uint8 *map, Locate *pobject) {
 			return 0;
 		}
 
-		if (_me.xindex >= 0 && _me.xindex < 32 &&
-			_me.yindex >= 0 && _me.yindex < 32)
-			_robotArray[_me.xindex][_me.yindex] = 0;
+		clearPlayerCellMarker();
 
 		_gameMode = kModeBattle;
 		_projon = false;
@@ -1041,7 +1201,7 @@ void ColonyEngine::fallThroughHole() {
 		if (targetMap == 0 && _robotArray[targetX][targetY] != 0)
 			return;
 
-		_robotArray[_me.xindex][_me.yindex] = 0;
+		clearPlayerCellMarker();
 
 		// Preserve sub-cell offset (DOS: xmod = xloc - (xindex<<8))
 		int xmod = _me.xloc - (_me.xindex << 8);
@@ -1051,7 +1211,7 @@ void ColonyEngine::fallThroughHole() {
 		_me.yloc = (targetY << 8) + ymod;
 		_me.yindex = targetY;
 
-		_robotArray[targetX][targetY] = kMeNum;
+		setPlayerCellMarker();
 	}
 
 	// DOS: if(map) load_mapnum(map, TRUE)  always reload when map != 0
@@ -1134,8 +1294,7 @@ void ColonyEngine::playCollisionSound() {
 }
 
 void ColonyEngine::cCommand(int xnew, int ynew, bool allowInteraction) {
-	if (_me.xindex >= 0 && _me.xindex < 32 && _me.yindex >= 0 && _me.yindex < 32)
-		_robotArray[_me.xindex][_me.yindex] = 0;
+	clearPlayerCellMarker();
 
 	const int oldXIndex = _me.xindex;
 	const int oldYIndex = _me.yindex;
@@ -1149,8 +1308,7 @@ void ColonyEngine::cCommand(int xnew, int ynew, bool allowInteraction) {
 			(_me.xloc != xnew || _me.yloc != ynew))
 		playCollisionSound();
 
-	if (_me.xindex >= 0 && _me.xindex < 32 && _me.yindex >= 0 && _me.yindex < 32)
-		_robotArray[_me.xindex][_me.yindex] = kMeNum;
+	setPlayerCellMarker();
 
 	_suppressCollisionSound = false;
 }
diff --git a/engines/colony/render_objects.cpp b/engines/colony/render_objects.cpp
index 5d49db97a8e..afa5e4458e4 100644
--- a/engines/colony/render_objects.cpp
+++ b/engines/colony/render_objects.cpp
@@ -1448,8 +1448,6 @@ void ColonyEngine::drawEyeOverlays3D(Thing &thing, const PrismPartDef &irisDef,
 	drawPrismOval3D(thing, pupilDef, useLook, pupilColorOverride, true);
 }
 
-namespace {
-
 int interpolatedRobotPoint(int from, int to, float progress) {
 	return (int)roundf((float)from + ((float)to - (float)from) * progress);
 }
@@ -1516,8 +1514,6 @@ const ColonyEngine::PrismPartDef &growEyePupilDefForStage(int stage) {
 	}
 }
 
-} // namespace
-
 float ColonyEngine::growRenderTickFraction() const {
 	const uint32 kColonyThinkIntervalMs = 125;
 	const uint32 now = _system->getMillis();
diff --git a/engines/colony/savegame.cpp b/engines/colony/savegame.cpp
index 1f423247887..f3117164866 100644
--- a/engines/colony/savegame.cpp
+++ b/engines/colony/savegame.cpp
@@ -28,8 +28,6 @@
 
 namespace Colony {
 
-namespace {
-
 const uint32 kSaveVersion = 1;
 const uint32 kMaxSaveObjects = 4096;
 const uint32 kMaxSavePatches = 100;
@@ -274,8 +272,6 @@ bool findInvalidActiveObjectSlot(const Common::Array<Thing> &objects, uint32 &in
 	return false;
 }
 
-} // anonymous namespace
-
 bool ColonyEngine::hasFeature(EngineFeature f) const {
 	return f == kSupportsReturnToLauncher ||
 		f == kSupportsLoadingDuringRuntime ||
diff --git a/engines/colony/sound.cpp b/engines/colony/sound.cpp
index 9ab0459219d..d72708bbe05 100644
--- a/engines/colony/sound.cpp
+++ b/engines/colony/sound.cpp
@@ -34,8 +34,6 @@
 
 namespace Colony {
 
-namespace {
-
 struct MelodyStep {
 	uint32 divider;
 	uint8 ticks;
@@ -99,8 +97,6 @@ const int kBeamMeRamp1Steps = 20;
 const int kBeamMeRamp2Steps = 20;
 const int kBeamMeRamp3Steps = 80;
 
-} // namespace
-
 Sound::Sound(ColonyEngine *vm) : _vm(vm), _resMan(nullptr), _appResMan(nullptr) {
 	_speaker = new Audio::PCSpeaker();
 	_speaker->init();



From noreply at scummvm.org  Fri Jun  5 19:36:47 2026
From: noreply at scummvm.org (ScummVM-Translations)
Date: Fri,  5 Jun 2026 19:36:47 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 8416080d4ff5eb4e10e1e8715354af781c871b45
Message-ID: <20260605193647.26BA77005CE@ron.scummvm.net>

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

Summary:
8416080d4f I18N: Update translations templates


Commit: 8416080d4ff5eb4e10e1e8715354af781c871b45
    https://github.com/scummvm/scummvm/commit/8416080d4ff5eb4e10e1e8715354af781c871b45
Author: Weblate (noreply at weblate.org)
Date: 2026-06-05T19:36:43Z

Commit Message:
I18N: Update translations templates

Changed paths:
    po/scummvm.pot


diff --git a/po/scummvm.pot b/po/scummvm.pot
index 39f58b09516..446b4597ee8 100644
--- a/po/scummvm.pot
+++ b/po/scummvm.pot
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2026.2.1git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-02 16:30+0000\n"
+"POT-Creation-Date: 2026-06-05 19:36+0000\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL at ADDRESS>\n"
 "Language-Team: LANGUAGE <LL at li.org>\n"
@@ -7428,11 +7428,11 @@ msgstr ""
 msgid "Fire weapon"
 msgstr ""
 
-#: engines/colony/savegame.cpp:295
+#: engines/colony/savegame.cpp:291
 msgid "Saving is only available in first-person view."
 msgstr ""
 
-#: engines/colony/savegame.cpp:304
+#: engines/colony/savegame.cpp:300
 msgid "Loading is only available in first-person view."
 msgstr ""
 



From noreply at scummvm.org  Fri Jun  5 19:37:18 2026
From: noreply at scummvm.org (ScummVM-Translations)
Date: Fri,  5 Jun 2026 19:37:18 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 70b79506ef410e0c53f98d749ea3b9b6acc5e98f
Message-ID: <20260605193718.C30F87005BC@ron.scummvm.net>

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

Summary:
70b79506ef I18N: Update translation files


Commit: 70b79506ef410e0c53f98d749ea3b9b6acc5e98f
    https://github.com/scummvm/scummvm/commit/70b79506ef410e0c53f98d749ea3b9b6acc5e98f
Author: Weblate (noreply at weblate.org)
Date: 2026-06-05T19:36:59Z

Commit Message:
I18N: Update translation files

Updated by "Update PO files to match POT (msgmerge)" hook in Weblate.

Translation: ScummVM/scummvm
Translate-URL: https://translations.scummvm.org/projects/scummvm/scummvm/

Changed paths:
    po/ar.po
    po/be-tarask.po
    po/be_BY.po
    po/ca_ES.po
    po/cs_CZ.po
    po/da.po
    po/de_DE.po
    po/el.po
    po/es_ES.po
    po/eu.po
    po/fi_FI.po
    po/fr_FR.po
    po/gl_ES.po
    po/he.po
    po/hi.po
    po/hu_HU.po
    po/it_IT.po
    po/ja.po
    po/ka.po
    po/ko.po
    po/nb_NO.po
    po/nl_NL.po
    po/nn_NO.po
    po/pl_PL.po
    po/pt_BR.po
    po/pt_PT.po
    po/ro.po
    po/ru_RU.po
    po/sv_SE.po
    po/tr.po
    po/uk_UA.po
    po/zh.po
    po/zh_Hans.po
    po/zh_Hant.po


diff --git a/po/ar.po b/po/ar.po
index 7ea7d9f9c68..f9d72237cbd 100644
--- a/po/ar.po
+++ b/po/ar.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-02 16:30+0000\n"
+"POT-Creation-Date: 2026-06-05 19:36+0000\n"
 "PO-Revision-Date: 2026-02-24 22:26+0000\n"
 "Last-Translator: Mohamed Shaaban <mohmedbashamedo at hotmail.com>\n"
 "Language-Team: Arabic <https://translations.scummvm.org/projects/scummvm/"
@@ -8208,11 +8208,11 @@ msgstr "?????????????? / ????????"
 msgid "Fire weapon"
 msgstr "?????????? ????????????"
 
-#: engines/colony/savegame.cpp:295
+#: engines/colony/savegame.cpp:291
 msgid "Saving is only available in first-person view."
 msgstr ""
 
-#: engines/colony/savegame.cpp:304
+#: engines/colony/savegame.cpp:300
 msgid "Loading is only available in first-person view."
 msgstr ""
 
diff --git a/po/be-tarask.po b/po/be-tarask.po
index e9671fbec29..b91d6f80bd5 100644
--- a/po/be-tarask.po
+++ b/po/be-tarask.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-02 16:30+0000\n"
+"POT-Creation-Date: 2026-06-05 19:36+0000\n"
 "PO-Revision-Date: 2023-02-26 15:01+0000\n"
 "Last-Translator: Kastu?? K. <oakum2334 at gmail.com>\n"
 "Language-Team: Belarusian (Tara??kievica) <https://translations.scummvm.org/"
@@ -8155,11 +8155,11 @@ msgstr "???????? / ??????????????????????"
 msgid "Fire weapon"
 msgstr "?????????????????? ????????"
 
-#: engines/colony/savegame.cpp:295
+#: engines/colony/savegame.cpp:291
 msgid "Saving is only available in first-person view."
 msgstr ""
 
-#: engines/colony/savegame.cpp:304
+#: engines/colony/savegame.cpp:300
 msgid "Loading is only available in first-person view."
 msgstr ""
 
diff --git a/po/be_BY.po b/po/be_BY.po
index f90466c98c0..e68cf8a142d 100644
--- a/po/be_BY.po
+++ b/po/be_BY.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-02 16:30+0000\n"
+"POT-Creation-Date: 2026-06-05 19:36+0000\n"
 "PO-Revision-Date: 2021-03-21 08:24+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Belarusian <https://translations.scummvm.org/projects/scummvm/"
@@ -8120,11 +8120,11 @@ msgstr "???????????????????? ??????????"
 msgid "Fire weapon"
 msgstr "?????????????????? ??????????????"
 
-#: engines/colony/savegame.cpp:295
+#: engines/colony/savegame.cpp:291
 msgid "Saving is only available in first-person view."
 msgstr ""
 
-#: engines/colony/savegame.cpp:304
+#: engines/colony/savegame.cpp:300
 msgid "Loading is only available in first-person view."
 msgstr ""
 
diff --git a/po/ca_ES.po b/po/ca_ES.po
index 47a90cc7dbd..eb0d116a83d 100644
--- a/po/ca_ES.po
+++ b/po/ca_ES.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.6.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-02 16:30+0000\n"
+"POT-Creation-Date: 2026-06-05 19:36+0000\n"
 "PO-Revision-Date: 2026-05-26 04:22+0000\n"
 "Last-Translator: Xaviu <xaviu at xaviu.com>\n"
 "Language-Team: Catalan <https://translations.scummvm.org/projects/scummvm/"
@@ -7796,11 +7796,11 @@ msgstr "Men?? / Omet la introducci??"
 msgid "Fire weapon"
 msgstr "Dispara l'arma"
 
-#: engines/colony/savegame.cpp:295
+#: engines/colony/savegame.cpp:291
 msgid "Saving is only available in first-person view."
 msgstr "Desar la partida nom??s est?? disponible a la vista en primera persona."
 
-#: engines/colony/savegame.cpp:304
+#: engines/colony/savegame.cpp:300
 msgid "Loading is only available in first-person view."
 msgstr ""
 "Carregar la partida nom??s est?? disponible a la vista en primera persona."
diff --git a/po/cs_CZ.po b/po/cs_CZ.po
index 5c8dc00d9d3..a683f820fe0 100644
--- a/po/cs_CZ.po
+++ b/po/cs_CZ.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.7.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-02 16:30+0000\n"
+"POT-Creation-Date: 2026-06-05 19:36+0000\n"
 "PO-Revision-Date: 2026-02-09 20:22+0000\n"
 "Last-Translator: AsciiWolf <mail at asciiwolf.com>\n"
 "Language-Team: Czech <https://translations.scummvm.org/projects/scummvm/"
@@ -8064,11 +8064,11 @@ msgstr "P??esko??it ????dek"
 msgid "Fire weapon"
 msgstr "~P~??edchoz??"
 
-#: engines/colony/savegame.cpp:295
+#: engines/colony/savegame.cpp:291
 msgid "Saving is only available in first-person view."
 msgstr ""
 
-#: engines/colony/savegame.cpp:304
+#: engines/colony/savegame.cpp:300
 msgid "Loading is only available in first-person view."
 msgstr ""
 
diff --git a/po/da.po b/po/da.po
index fee6f201f6b..e15a6e42b2e 100644
--- a/po/da.po
+++ b/po/da.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-02 16:30+0000\n"
+"POT-Creation-Date: 2026-06-05 19:36+0000\n"
 "PO-Revision-Date: 2023-09-07 17:04+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Danish <https://translations.scummvm.org/projects/scummvm/"
@@ -8091,11 +8091,11 @@ msgstr "Spring linje over"
 msgid "Fire weapon"
 msgstr "Fo~r~rige"
 
-#: engines/colony/savegame.cpp:295
+#: engines/colony/savegame.cpp:291
 msgid "Saving is only available in first-person view."
 msgstr ""
 
-#: engines/colony/savegame.cpp:304
+#: engines/colony/savegame.cpp:300
 msgid "Loading is only available in first-person view."
 msgstr ""
 
diff --git a/po/de_DE.po b/po/de_DE.po
index bbc728776d1..cd60796f34d 100644
--- a/po/de_DE.po
+++ b/po/de_DE.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.10.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-02 16:30+0000\n"
+"POT-Creation-Date: 2026-06-05 19:36+0000\n"
 "PO-Revision-Date: 2026-05-05 16:31+0000\n"
 "Last-Translator: Lothar Serra Mari <lothar.serramari at scummvm.org>\n"
 "Language-Team: German <https://translations.scummvm.org/projects/scummvm/"
@@ -8279,11 +8279,11 @@ msgstr "Men?? / Intro ??berspringen"
 msgid "Fire weapon"
 msgstr "Feuerwaffe"
 
-#: engines/colony/savegame.cpp:295
+#: engines/colony/savegame.cpp:291
 msgid "Saving is only available in first-person view."
 msgstr "Speichern wird nur in der first-person-Ansicht unterst??tzt."
 
-#: engines/colony/savegame.cpp:304
+#: engines/colony/savegame.cpp:300
 msgid "Loading is only available in first-person view."
 msgstr "Laden wird nur in der first-person-Ansicht unterst??tzt."
 
diff --git a/po/el.po b/po/el.po
index ea1fa3af49b..ced05bfc4ee 100644
--- a/po/el.po
+++ b/po/el.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.10.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-02 16:30+0000\n"
+"POT-Creation-Date: 2026-06-05 19:36+0000\n"
 "PO-Revision-Date: 2026-04-23 00:22+0000\n"
 "Last-Translator: Antoniou Athanasios <a.antoniou79 at gmail.com>\n"
 "Language-Team: Greek <https://translations.scummvm.org/projects/scummvm/"
@@ -8414,11 +8414,11 @@ msgstr "?????????? / ??????????????????"
 msgid "Fire weapon"
 msgstr "????????????????????????"
 
-#: engines/colony/savegame.cpp:295
+#: engines/colony/savegame.cpp:291
 msgid "Saving is only available in first-person view."
 msgstr "?? ???????????????????? ?????????? ?????????????????? ???????? ???? ???????????? ???????????? ????????????????."
 
-#: engines/colony/savegame.cpp:304
+#: engines/colony/savegame.cpp:300
 msgid "Loading is only available in first-person view."
 msgstr "?? ?????????????? ?????????? ?????????????????? ???????? ???? ???????????? ???????????? ????????????????."
 
diff --git a/po/es_ES.po b/po/es_ES.po
index 23e6f9ee56c..b319f78df70 100644
--- a/po/es_ES.po
+++ b/po/es_ES.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.4.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-02 16:30+0000\n"
+"POT-Creation-Date: 2026-06-05 19:36+0000\n"
 "PO-Revision-Date: 2026-06-03 08:30+0000\n"
 "Last-Translator: IlDucci <pajaroloco_2 at hotmail.com>\n"
 "Language-Team: Spanish <https://translations.scummvm.org/projects/scummvm/"
@@ -8296,11 +8296,11 @@ msgstr "Men??/Omitir introducci??n"
 msgid "Fire weapon"
 msgstr "Disparar arma"
 
-#: engines/colony/savegame.cpp:295
+#: engines/colony/savegame.cpp:291
 msgid "Saving is only available in first-person view."
 msgstr "Solo se puede guardar en la vista en primera persona."
 
-#: engines/colony/savegame.cpp:304
+#: engines/colony/savegame.cpp:300
 msgid "Loading is only available in first-person view."
 msgstr "Solo se puede cargar en la vista en primera persona."
 
diff --git a/po/eu.po b/po/eu.po
index 785467323e9..6570181c9cc 100644
--- a/po/eu.po
+++ b/po/eu.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.5.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-02 16:30+0000\n"
+"POT-Creation-Date: 2026-06-05 19:36+0000\n"
 "PO-Revision-Date: 2024-02-05 18:42+0000\n"
 "Last-Translator: Josu Igoa <josuigoa at ni.eus>\n"
 "Language-Team: Basque <https://translations.scummvm.org/projects/scummvm/"
@@ -8139,11 +8139,11 @@ msgstr "Lerroa saltatu"
 msgid "Fire weapon"
 msgstr "~A~urrekoa"
 
-#: engines/colony/savegame.cpp:295
+#: engines/colony/savegame.cpp:291
 msgid "Saving is only available in first-person view."
 msgstr ""
 
-#: engines/colony/savegame.cpp:304
+#: engines/colony/savegame.cpp:300
 msgid "Loading is only available in first-person view."
 msgstr ""
 
diff --git a/po/fi_FI.po b/po/fi_FI.po
index 2981df062e2..110d08bb059 100644
--- a/po/fi_FI.po
+++ b/po/fi_FI.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.6.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-02 16:30+0000\n"
+"POT-Creation-Date: 2026-06-05 19:36+0000\n"
 "PO-Revision-Date: 2026-02-07 15:22+0000\n"
 "Last-Translator: Timo Mikkolainen <tmikkola at gmail.com>\n"
 "Language-Team: Finnish <https://translations.scummvm.org/projects/scummvm/"
@@ -8408,11 +8408,11 @@ msgstr "Valikko / Ohita"
 msgid "Fire weapon"
 msgstr "Laukaise ase"
 
-#: engines/colony/savegame.cpp:295
+#: engines/colony/savegame.cpp:291
 msgid "Saving is only available in first-person view."
 msgstr ""
 
-#: engines/colony/savegame.cpp:304
+#: engines/colony/savegame.cpp:300
 msgid "Loading is only available in first-person view."
 msgstr ""
 
diff --git a/po/fr_FR.po b/po/fr_FR.po
index 61f91eb431e..e9f158876ff 100644
--- a/po/fr_FR.po
+++ b/po/fr_FR.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-02 16:30+0000\n"
+"POT-Creation-Date: 2026-06-05 19:36+0000\n"
 "PO-Revision-Date: 2026-06-02 11:22+0000\n"
 "Last-Translator: Purple T <ZEONK at hotmail.com>\n"
 "Language-Team: French <https://translations.scummvm.org/projects/scummvm/"
@@ -8296,11 +8296,11 @@ msgstr "Menu / Passer l???intro"
 msgid "Fire weapon"
 msgstr "Tirer avec l'arme"
 
-#: engines/colony/savegame.cpp:295
+#: engines/colony/savegame.cpp:291
 msgid "Saving is only available in first-person view."
 msgstr "La sauvegarde n'est disponible qu'en vue ?? la premi??re personne."
 
-#: engines/colony/savegame.cpp:304
+#: engines/colony/savegame.cpp:300
 msgid "Loading is only available in first-person view."
 msgstr "Le chargement n'est disponible qu'en vue ?? la premi??re personne."
 
diff --git a/po/gl_ES.po b/po/gl_ES.po
index 43cd0f685d7..f16d355a456 100644
--- a/po/gl_ES.po
+++ b/po/gl_ES.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-02 16:30+0000\n"
+"POT-Creation-Date: 2026-06-05 19:36+0000\n"
 "PO-Revision-Date: 2020-09-26 21:13+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Galician <https://translations.scummvm.org/projects/scummvm/"
@@ -8072,11 +8072,11 @@ msgstr "Omitir li??a"
 msgid "Fire weapon"
 msgstr "~A~nterior"
 
-#: engines/colony/savegame.cpp:295
+#: engines/colony/savegame.cpp:291
 msgid "Saving is only available in first-person view."
 msgstr ""
 
-#: engines/colony/savegame.cpp:304
+#: engines/colony/savegame.cpp:300
 msgid "Loading is only available in first-person view."
 msgstr ""
 
diff --git a/po/he.po b/po/he.po
index 69a1cbfdea2..d5ab7be8303 100644
--- a/po/he.po
+++ b/po/he.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.1.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-02 16:30+0000\n"
+"POT-Creation-Date: 2026-06-05 19:36+0000\n"
 "PO-Revision-Date: 2025-04-09 20:18+0000\n"
 "Last-Translator: Niv Baehr <bloop93 at gmail.com>\n"
 "Language-Team: Hebrew <https://translations.scummvm.org/projects/scummvm/"
@@ -8070,11 +8070,11 @@ msgstr "?????????? / ??????????"
 msgid "Fire weapon"
 msgstr "?????????? ????????"
 
-#: engines/colony/savegame.cpp:295
+#: engines/colony/savegame.cpp:291
 msgid "Saving is only available in first-person view."
 msgstr ""
 
-#: engines/colony/savegame.cpp:304
+#: engines/colony/savegame.cpp:300
 msgid "Loading is only available in first-person view."
 msgstr ""
 
diff --git a/po/hi.po b/po/hi.po
index f67615fec24..3a3a3d281c2 100644
--- a/po/hi.po
+++ b/po/hi.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-02 16:30+0000\n"
+"POT-Creation-Date: 2026-06-05 19:36+0000\n"
 "PO-Revision-Date: 2026-05-12 22:22+0000\n"
 "Last-Translator: KARTIK SAXENA <kartiksaxena2005 at gmail.com>\n"
 "Language-Team: Hindi <https://translations.scummvm.org/projects/scummvm/"
@@ -7763,11 +7763,11 @@ msgstr "???????????? ????????? files ??????????????????"
 msgid "Fire weapon"
 msgstr ""
 
-#: engines/colony/savegame.cpp:295
+#: engines/colony/savegame.cpp:291
 msgid "Saving is only available in first-person view."
 msgstr ""
 
-#: engines/colony/savegame.cpp:304
+#: engines/colony/savegame.cpp:300
 msgid "Loading is only available in first-person view."
 msgstr ""
 
diff --git a/po/hu_HU.po b/po/hu_HU.po
index 8079cad5db9..ffed1dc018f 100644
--- a/po/hu_HU.po
+++ b/po/hu_HU.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-02 16:30+0000\n"
+"POT-Creation-Date: 2026-06-05 19:36+0000\n"
 "PO-Revision-Date: 2025-03-07 00:18+0000\n"
 "Last-Translator: Hov??nszki Tam??s <gahex68379 at lofiey.com>\n"
 "Language-Team: Hungarian <https://translations.scummvm.org/projects/scummvm/"
@@ -8589,11 +8589,11 @@ msgstr "Men?? / Ugr??s"
 msgid "Fire weapon"
 msgstr "K??vetkez?? m??velet"
 
-#: engines/colony/savegame.cpp:295
+#: engines/colony/savegame.cpp:291
 msgid "Saving is only available in first-person view."
 msgstr ""
 
-#: engines/colony/savegame.cpp:304
+#: engines/colony/savegame.cpp:300
 msgid "Loading is only available in first-person view."
 msgstr ""
 
diff --git a/po/it_IT.po b/po/it_IT.po
index e71e95d2f95..783149531b6 100644
--- a/po/it_IT.po
+++ b/po/it_IT.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-02 16:30+0000\n"
+"POT-Creation-Date: 2026-06-05 19:36+0000\n"
 "PO-Revision-Date: 2026-06-04 19:00+0000\n"
 "Last-Translator: Walter Agazzi <walter.agazzi at protonmail.com>\n"
 "Language-Team: Italian <https://translations.scummvm.org/projects/scummvm/"
@@ -8272,11 +8272,11 @@ msgstr "Men?? / Salta introduzione"
 msgid "Fire weapon"
 msgstr "Spara con l'arma"
 
-#: engines/colony/savegame.cpp:295
+#: engines/colony/savegame.cpp:291
 msgid "Saving is only available in first-person view."
 msgstr "Il salvataggio ?? possibile solo nella visuale in prima persona."
 
-#: engines/colony/savegame.cpp:304
+#: engines/colony/savegame.cpp:300
 msgid "Loading is only available in first-person view."
 msgstr "Il caricamento ?? possibile solo nella visuale in prima persona."
 
diff --git a/po/ja.po b/po/ja.po
index 52fb857b493..63dca5e9668 100644
--- a/po/ja.po
+++ b/po/ja.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-02 16:30+0000\n"
+"POT-Creation-Date: 2026-06-05 19:36+0000\n"
 "PO-Revision-Date: 2026-06-04 07:22+0000\n"
 "Last-Translator: VAN-Gluon <van.gluonp at gmail.com>\n"
 "Language-Team: Japanese <https://translations.scummvm.org/projects/scummvm/"
@@ -8192,11 +8192,11 @@ msgstr "????????????/????????????"
 msgid "Fire weapon"
 msgstr "????????????"
 
-#: engines/colony/savegame.cpp:295
+#: engines/colony/savegame.cpp:291
 msgid "Saving is only available in first-person view."
 msgstr ""
 
-#: engines/colony/savegame.cpp:304
+#: engines/colony/savegame.cpp:300
 msgid "Loading is only available in first-person view."
 msgstr ""
 
diff --git a/po/ka.po b/po/ka.po
index d7d680b6089..02b57ee8312 100644
--- a/po/ka.po
+++ b/po/ka.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-02 16:30+0000\n"
+"POT-Creation-Date: 2026-06-05 19:36+0000\n"
 "PO-Revision-Date: 2026-06-05 10:22+0000\n"
 "Last-Translator: NorwayFun <temuri.doghonadze at gmail.com>\n"
 "Language-Team: Georgian <https://translations.scummvm.org/projects/scummvm/"
@@ -7462,11 +7462,11 @@ msgstr "??????????????? / ?????????????????? ??????????????????????????????"
 msgid "Fire weapon"
 msgstr "????????????????????? ???????????????"
 
-#: engines/colony/savegame.cpp:295
+#: engines/colony/savegame.cpp:291
 msgid "Saving is only available in first-person view."
 msgstr ""
 
-#: engines/colony/savegame.cpp:304
+#: engines/colony/savegame.cpp:300
 msgid "Loading is only available in first-person view."
 msgstr ""
 
diff --git a/po/ko.po b/po/ko.po
index da31e3c4d63..4b2eaf55058 100644
--- a/po/ko.po
+++ b/po/ko.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-02 16:30+0000\n"
+"POT-Creation-Date: 2026-06-05 19:36+0000\n"
 "PO-Revision-Date: 2026-05-19 20:22+0000\n"
 "Last-Translator: Hoseok Seo <ddinghoya at gmail.com>\n"
 "Language-Team: Korean <https://translations.scummvm.org/projects/scummvm/"
@@ -8091,11 +8091,11 @@ msgstr "?????? / ????????? ????????????"
 msgid "Fire weapon"
 msgstr "?????? ??????"
 
-#: engines/colony/savegame.cpp:295
+#: engines/colony/savegame.cpp:291
 msgid "Saving is only available in first-person view."
 msgstr "?????? ????????? 1?????? ??????????????? ????????? ??? ????????????."
 
-#: engines/colony/savegame.cpp:304
+#: engines/colony/savegame.cpp:300
 msgid "Loading is only available in first-person view."
 msgstr "????????? 1?????? ??????????????? ???????????????."
 
diff --git a/po/nb_NO.po b/po/nb_NO.po
index 4195d4be314..67b3b235810 100644
--- a/po/nb_NO.po
+++ b/po/nb_NO.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-02 16:30+0000\n"
+"POT-Creation-Date: 2026-06-05 19:36+0000\n"
 "PO-Revision-Date: 2026-02-07 15:22+0000\n"
 "Last-Translator: Einar Johan Tr??an S??m??en <einarjohants at gmail.com>\n"
 "Language-Team: Norwegian Bokm??l <https://translations.scummvm.org/projects/"
@@ -8661,11 +8661,11 @@ msgstr "Meny / Hopp over"
 msgid "Fire weapon"
 msgstr "Neste handling"
 
-#: engines/colony/savegame.cpp:295
+#: engines/colony/savegame.cpp:291
 msgid "Saving is only available in first-person view."
 msgstr ""
 
-#: engines/colony/savegame.cpp:304
+#: engines/colony/savegame.cpp:300
 msgid "Loading is only available in first-person view."
 msgstr ""
 
diff --git a/po/nl_NL.po b/po/nl_NL.po
index 72a318554f9..edc7f61491c 100644
--- a/po/nl_NL.po
+++ b/po/nl_NL.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.9.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-02 16:30+0000\n"
+"POT-Creation-Date: 2026-06-05 19:36+0000\n"
 "PO-Revision-Date: 2026-02-11 14:23+0000\n"
 "Last-Translator: SecularSteve <fairfull.playing at gmail.com>\n"
 "Language-Team: Dutch <https://translations.scummvm.org/projects/scummvm/"
@@ -8236,11 +8236,11 @@ msgstr "Menu / Overslaan"
 msgid "Fire weapon"
 msgstr "Vuurwapen"
 
-#: engines/colony/savegame.cpp:295
+#: engines/colony/savegame.cpp:291
 msgid "Saving is only available in first-person view."
 msgstr ""
 
-#: engines/colony/savegame.cpp:304
+#: engines/colony/savegame.cpp:300
 msgid "Loading is only available in first-person view."
 msgstr ""
 
diff --git a/po/nn_NO.po b/po/nn_NO.po
index 120aa9731d5..0b22ebc43a9 100644
--- a/po/nn_NO.po
+++ b/po/nn_NO.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-02 16:30+0000\n"
+"POT-Creation-Date: 2026-06-05 19:36+0000\n"
 "PO-Revision-Date: 2023-12-14 14:28+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Norwegian Nynorsk <https://translations.scummvm.org/projects/"
@@ -8020,11 +8020,11 @@ msgstr "Hopp over linje"
 msgid "Fire weapon"
 msgstr "~F~orrige"
 
-#: engines/colony/savegame.cpp:295
+#: engines/colony/savegame.cpp:291
 msgid "Saving is only available in first-person view."
 msgstr ""
 
-#: engines/colony/savegame.cpp:304
+#: engines/colony/savegame.cpp:300
 msgid "Loading is only available in first-person view."
 msgstr ""
 
diff --git a/po/pl_PL.po b/po/pl_PL.po
index 429ada8042d..a2edc01050a 100644
--- a/po/pl_PL.po
+++ b/po/pl_PL.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-02 16:30+0000\n"
+"POT-Creation-Date: 2026-06-05 19:36+0000\n"
 "PO-Revision-Date: 2026-04-17 00:22+0000\n"
 "Last-Translator: SkiffPL <skiff at windowslive.com>\n"
 "Language-Team: Polish <https://translations.scummvm.org/projects/scummvm/"
@@ -8219,11 +8219,11 @@ msgstr "Menu / Pomi??"
 msgid "Fire weapon"
 msgstr "Wystrzel z broni"
 
-#: engines/colony/savegame.cpp:295
+#: engines/colony/savegame.cpp:291
 msgid "Saving is only available in first-person view."
 msgstr "Zapisywanie jest mo??liwe tylko w widoku pierwszej osoby."
 
-#: engines/colony/savegame.cpp:304
+#: engines/colony/savegame.cpp:300
 msgid "Loading is only available in first-person view."
 msgstr "Wczytywanie jest mo??liwe tylko w widoku pierwszej osoby."
 
diff --git a/po/pt_BR.po b/po/pt_BR.po
index d99d1145715..304bf314279 100644
--- a/po/pt_BR.po
+++ b/po/pt_BR.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-02 16:30+0000\n"
+"POT-Creation-Date: 2026-06-05 19:36+0000\n"
 "PO-Revision-Date: 2024-11-16 02:13+0000\n"
 "Last-Translator: Marcel Souza Lemes <marcosoutsider at gmail.com>\n"
 "Language-Team: Portuguese (Brazil) <https://translations.scummvm.org/"
@@ -8628,11 +8628,11 @@ msgstr "Menu / Pular"
 msgid "Fire weapon"
 msgstr "Pr??xima a????o"
 
-#: engines/colony/savegame.cpp:295
+#: engines/colony/savegame.cpp:291
 msgid "Saving is only available in first-person view."
 msgstr ""
 
-#: engines/colony/savegame.cpp:304
+#: engines/colony/savegame.cpp:300
 msgid "Loading is only available in first-person view."
 msgstr ""
 
diff --git a/po/pt_PT.po b/po/pt_PT.po
index 9132e8e0ba6..e527af1d283 100644
--- a/po/pt_PT.po
+++ b/po/pt_PT.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.10.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-02 16:30+0000\n"
+"POT-Creation-Date: 2026-06-05 19:36+0000\n"
 "PO-Revision-Date: 2022-06-05 14:09+0000\n"
 "Last-Translator: Daniel <supsuper at gmail.com>\n"
 "Language-Team: Portuguese (Portugal) <https://translations.scummvm.org/"
@@ -8049,11 +8049,11 @@ msgstr "Ignorar linha"
 msgid "Fire weapon"
 msgstr "A????o seguinte"
 
-#: engines/colony/savegame.cpp:295
+#: engines/colony/savegame.cpp:291
 msgid "Saving is only available in first-person view."
 msgstr ""
 
-#: engines/colony/savegame.cpp:304
+#: engines/colony/savegame.cpp:300
 msgid "Loading is only available in first-person view."
 msgstr ""
 
diff --git a/po/ro.po b/po/ro.po
index c832db79dd6..5c8968bb42d 100644
--- a/po/ro.po
+++ b/po/ro.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.10.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-02 16:30+0000\n"
+"POT-Creation-Date: 2026-06-05 19:36+0000\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: Automatically generated\n"
 "Language-Team: none\n"
@@ -7427,11 +7427,11 @@ msgstr ""
 msgid "Fire weapon"
 msgstr ""
 
-#: engines/colony/savegame.cpp:295
+#: engines/colony/savegame.cpp:291
 msgid "Saving is only available in first-person view."
 msgstr ""
 
-#: engines/colony/savegame.cpp:304
+#: engines/colony/savegame.cpp:300
 msgid "Loading is only available in first-person view."
 msgstr ""
 
diff --git a/po/ru_RU.po b/po/ru_RU.po
index 0d6017ab83f..bb4bf4a1a2e 100644
--- a/po/ru_RU.po
+++ b/po/ru_RU.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.8.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-02 16:30+0000\n"
+"POT-Creation-Date: 2026-06-05 19:36+0000\n"
 "PO-Revision-Date: 2026-05-06 08:54+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Russian <https://translations.scummvm.org/projects/scummvm/"
@@ -8214,11 +8214,11 @@ msgstr "???????? / ???????????????????? ????????????????"
 msgid "Fire weapon"
 msgstr "???????????????????? ???? ????????????"
 
-#: engines/colony/savegame.cpp:295
+#: engines/colony/savegame.cpp:291
 msgid "Saving is only available in first-person view."
 msgstr "???????????????????? ???????? ???????????????? ???????????? ?? ???????????? ???? ?????????????? ????????."
 
-#: engines/colony/savegame.cpp:304
+#: engines/colony/savegame.cpp:300
 msgid "Loading is only available in first-person view."
 msgstr "???????????????? ???????????????? ???????????? ?? ???????????? ???? ?????????????? ????????."
 
diff --git a/po/sv_SE.po b/po/sv_SE.po
index 99c218c4c4e..ec01f977f15 100644
--- a/po/sv_SE.po
+++ b/po/sv_SE.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.5.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-02 16:30+0000\n"
+"POT-Creation-Date: 2026-06-05 19:36+0000\n"
 "PO-Revision-Date: 2026-04-19 13:22+0000\n"
 "Last-Translator: Daniel Nylander <po at danielnylander.se>\n"
 "Language-Team: Swedish <https://translations.scummvm.org/projects/scummvm/"
@@ -8194,11 +8194,11 @@ msgstr "Meny / Hoppa ??ver"
 msgid "Fire weapon"
 msgstr "Avfyra vapen"
 
-#: engines/colony/savegame.cpp:295
+#: engines/colony/savegame.cpp:291
 msgid "Saving is only available in first-person view."
 msgstr ""
 
-#: engines/colony/savegame.cpp:304
+#: engines/colony/savegame.cpp:300
 msgid "Loading is only available in first-person view."
 msgstr ""
 
diff --git a/po/tr.po b/po/tr.po
index d91a8e24d23..31b3bac8aab 100644
--- a/po/tr.po
+++ b/po/tr.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-02 16:30+0000\n"
+"POT-Creation-Date: 2026-06-05 19:36+0000\n"
 "PO-Revision-Date: 2024-10-10 12:33+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Turkish <https://translations.scummvm.org/projects/scummvm/"
@@ -7887,11 +7887,11 @@ msgstr "Gizli dosyalar?? g??ster"
 msgid "Fire weapon"
 msgstr "Oyunu se??"
 
-#: engines/colony/savegame.cpp:295
+#: engines/colony/savegame.cpp:291
 msgid "Saving is only available in first-person view."
 msgstr ""
 
-#: engines/colony/savegame.cpp:304
+#: engines/colony/savegame.cpp:300
 msgid "Loading is only available in first-person view."
 msgstr ""
 
diff --git a/po/uk_UA.po b/po/uk_UA.po
index ee3fb2d2495..8381eea590a 100644
--- a/po/uk_UA.po
+++ b/po/uk_UA.po
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.9.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-02 16:30+0000\n"
+"POT-Creation-Date: 2026-06-05 19:36+0000\n"
 "PO-Revision-Date: 2026-05-06 08:54+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Ukrainian <https://translations.scummvm.org/projects/scummvm/"
@@ -8195,11 +8195,11 @@ msgstr "???????? / ???????????????????? ????????????????"
 msgid "Fire weapon"
 msgstr "???????????????????? ???? ??????????"
 
-#: engines/colony/savegame.cpp:295
+#: engines/colony/savegame.cpp:291
 msgid "Saving is only available in first-person view."
 msgstr "???????????????????? ???????????????? ???????? ?? ???????????? ?????? ???????????? ??????????."
 
-#: engines/colony/savegame.cpp:304
+#: engines/colony/savegame.cpp:300
 msgid "Loading is only available in first-person view."
 msgstr "???????????????????????? ???????????????? ???????? ?? ???????????? ?????? ???????????? ??????????."
 
diff --git a/po/zh.po b/po/zh.po
index dbef50a6a93..c8e7cc04c97 100644
--- a/po/zh.po
+++ b/po/zh.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-02 16:30+0000\n"
+"POT-Creation-Date: 2026-06-05 19:36+0000\n"
 "PO-Revision-Date: 2024-05-14 12:42+0000\n"
 "Last-Translator: ?????? <clonewith at qq.com>\n"
 "Language-Team: Chinese <https://translations.scummvm.org/projects/scummvm/"
@@ -7658,11 +7658,11 @@ msgstr "????????????"
 msgid "Fire weapon"
 msgstr ""
 
-#: engines/colony/savegame.cpp:295
+#: engines/colony/savegame.cpp:291
 msgid "Saving is only available in first-person view."
 msgstr ""
 
-#: engines/colony/savegame.cpp:304
+#: engines/colony/savegame.cpp:300
 msgid "Loading is only available in first-person view."
 msgstr ""
 
diff --git a/po/zh_Hans.po b/po/zh_Hans.po
index 7b97e247c63..12797bfdb8f 100644
--- a/po/zh_Hans.po
+++ b/po/zh_Hans.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.9.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-02 16:30+0000\n"
+"POT-Creation-Date: 2026-06-05 19:36+0000\n"
 "PO-Revision-Date: 2024-12-01 22:18+0000\n"
 "Last-Translator: ?????? <clonewith at qq.com>\n"
 "Language-Team: Chinese (Simplified Han script) <https://"
@@ -8367,11 +8367,11 @@ msgstr "?????? / ??????"
 msgid "Fire weapon"
 msgstr "???????????????"
 
-#: engines/colony/savegame.cpp:295
+#: engines/colony/savegame.cpp:291
 msgid "Saving is only available in first-person view."
 msgstr ""
 
-#: engines/colony/savegame.cpp:304
+#: engines/colony/savegame.cpp:300
 msgid "Loading is only available in first-person view."
 msgstr ""
 
diff --git a/po/zh_Hant.po b/po/zh_Hant.po
index 05944cc1c4d..eebb929a279 100644
--- a/po/zh_Hant.po
+++ b/po/zh_Hant.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.9.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-02 16:30+0000\n"
+"POT-Creation-Date: 2026-06-05 19:36+0000\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: Automatically generated\n"
 "Language-Team: none\n"
@@ -7428,11 +7428,11 @@ msgstr ""
 msgid "Fire weapon"
 msgstr ""
 
-#: engines/colony/savegame.cpp:295
+#: engines/colony/savegame.cpp:291
 msgid "Saving is only available in first-person view."
 msgstr ""
 
-#: engines/colony/savegame.cpp:304
+#: engines/colony/savegame.cpp:300
 msgid "Loading is only available in first-person view."
 msgstr ""
 



From noreply at scummvm.org  Fri Jun  5 22:08:23 2026
From: noreply at scummvm.org (bluegr)
Date: Fri,  5 Jun 2026 22:08:23 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 91386e917060da3059e46a5c93317a50fc1e7b13
Message-ID: <20260605220823.7E8847005BC@ron.scummvm.net>

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:
a70c81f574 NANCY: Better search for case-insensitive convo texts in Nancy10+
3e03d26189 NANCY: Fix restoring the textbox after loading a saved game in Nancy10
91386e9170 NANCY: Add changes to onebuildpuzzle for Nancy10


Commit: a70c81f5745545d00e452c946869e7160fee6517
    https://github.com/scummvm/scummvm/commit/a70c81f5745545d00e452c946869e7160fee6517
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-06-06T01:08:08+03:00

Commit Message:
NANCY: Better search for case-insensitive convo texts in Nancy10+

Changed paths:
    engines/nancy/action/conversation.cpp


diff --git a/engines/nancy/action/conversation.cpp b/engines/nancy/action/conversation.cpp
index 64ad001d71e..5989b549e1f 100644
--- a/engines/nancy/action/conversation.cpp
+++ b/engines/nancy/action/conversation.cpp
@@ -185,13 +185,23 @@ void ConversationSound::readTerseCaptionText(Common::SeekableReadStream &stream)
 	const CVTX *convo = (const CVTX *)g_nancy->getEngineData("CONVO");
 	assert(convo);
 
-	// WORKAROUND: Return an empty string for captions that aren't found.
-	// Happens with some conversations in Nancy10 (e.g. when calling the Rawleys).
 	if (convo->texts.contains(key)) {
 		_text = convo->texts[key];
 	} else {
-		warning("Convo key not found: %s", key.c_str());
-		_text = "";
+		// Nancy10+ searched convo texts in a key insensitive way, but
+		// the possible permutations involve mainly the last character
+		// being upper or lower case, so just try that before giving up.
+		if (key[key.size() - 1] == toupper(key[key.size() - 1]))
+			key[key.size() - 1] = tolower(key[key.size() - 1]);
+		else
+			key[key.size() - 1] = toupper(key[key.size() - 1]);
+
+		if (convo->texts.contains(key)) {
+			_text = convo->texts[key];
+		} else {
+			warning("Convo key not found: %s", key.c_str());
+			_text = "";
+		}
 	}
 }
 


Commit: 3e03d2618964798f96c77bfbd48bd670559d6b7e
    https://github.com/scummvm/scummvm/commit/3e03d2618964798f96c77bfbd48bd670559d6b7e
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-06-06T01:08:09+03:00

Commit Message:
NANCY: Fix restoring the textbox after loading a saved game in Nancy10

Changed paths:
    engines/nancy/state/scene.cpp
    engines/nancy/ui/textbox.cpp


diff --git a/engines/nancy/state/scene.cpp b/engines/nancy/state/scene.cpp
index cb27b1b43cf..975ed3f5f37 100644
--- a/engines/nancy/state/scene.cpp
+++ b/engines/nancy/state/scene.cpp
@@ -1378,7 +1378,7 @@ void Scene::clearSceneData() {
 		_lightning->endLightning();
 	}
 
-	if (_textbox.hasBeenDrawn()) {
+	if (_textbox.hasBeenDrawn() || g_nancy->getGameType() >= kGameTypeNancy10) {
 		// Improvement: the dog portrait scenes in nancy7 queue a piece of text,
 		// then immediately change the scene. This makes the text disappear instantly;
 		// instead, we check if the textbox has been drawn, and don't clear it if it hasn't.
diff --git a/engines/nancy/ui/textbox.cpp b/engines/nancy/ui/textbox.cpp
index 9126feb4f38..a8248564fe3 100644
--- a/engines/nancy/ui/textbox.cpp
+++ b/engines/nancy/ui/textbox.cpp
@@ -258,11 +258,11 @@ void Textbox::drawTextbox() {
 
 	HypertextParser::drawAllText(textBounds, 0, baseFontID, highlightFontID);
 
-	setVisible(true);
+	setVisible(g_nancy->getGameType() <= kGameTypeNancy9 || _isFullMode);
 }
 
 void Textbox::clear() {
-	if (_textLines.size()) {
+	if (!_textLines.empty() || g_nancy->getGameType() >= kGameTypeNancy10) {
 		HypertextParser::clear();
 		if (_scrollbar) {
 			_scrollbar->resetPosition();


Commit: 91386e917060da3059e46a5c93317a50fc1e7b13
    https://github.com/scummvm/scummvm/commit/91386e917060da3059e46a5c93317a50fc1e7b13
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-06-06T01:08:13+03:00

Commit Message:
NANCY: Add changes to onebuildpuzzle for Nancy10

Fixes the egg basket jigsaw puzzle in Nancy10

Changed paths:
    engines/nancy/action/puzzle/onebuildpuzzle.cpp
    engines/nancy/action/puzzle/onebuildpuzzle.h


diff --git a/engines/nancy/action/puzzle/onebuildpuzzle.cpp b/engines/nancy/action/puzzle/onebuildpuzzle.cpp
index a49b1adfb4d..c8313c81e5b 100644
--- a/engines/nancy/action/puzzle/onebuildpuzzle.cpp
+++ b/engines/nancy/action/puzzle/onebuildpuzzle.cpp
@@ -91,6 +91,8 @@ void OneBuildPuzzle::registerGraphics() {
 }
 
 void OneBuildPuzzle::readData(Common::SeekableReadStream &stream) {
+	const bool isNancy10 = g_nancy->getGameType() >= kGameTypeNancy10;
+
 	readFilename(stream, _imageName);
 
 	_numPieces = stream.readUint16LE();
@@ -98,33 +100,66 @@ void OneBuildPuzzle::readData(Common::SeekableReadStream &stream) {
 	_canRotateAll = stream.readByte();
 	stream.skip(6); // rotationMode, zoneHeight, zoneWidth, mouse-clamping flag
 	_slotTolerance = stream.readSint16LE();
+
+	if (isNancy10) {
+		// TODO: purpose of this duplicate placement-order block is unknown.
+		_legacyOrderedFlag = stream.readByte() != 0;
+		_legacyPlacementOrder.resize(20);
+		for (uint i = 0; i < 20; ++i)
+			_legacyPlacementOrder[i] = stream.readSint16LE();
+	}
+
 	_orderedPlacement = stream.readByte();
 
 	_placementOrder.resize(20);
 	for (uint i = 0; i < 20; ++i)
 		_placementOrder[i] = stream.readSint16LE();
 
+	// Nancy 10 piece records add an alternative source rect at the front.
+	const uint pieceSize = isNancy10 ? 66 : 50;
+
 	_pieces.resize(_numPieces);
 	for (uint i = 0; i < 20; ++i) {
-		if (i < _numPieces) {
-			Piece &p = _pieces[i];
+		if (i >= _numPieces) {
+			stream.skip(pieceSize);
+			continue;
+		}
+
+		Piece &p = _pieces[i];
+		if (isNancy10) {
+			// Fall back to the alt rect when the primary one is empty.
+			Common::Rect altSrc;
+			readRect(stream, altSrc);
 			readRect(stream, p.srcRect);
-			readRect(stream, p.slotRect);
-			readRect(stream, p.homeRect);
-			p.defaultRotation = stream.readByte();
-			p.isPreRotated = stream.readByte();
+			if (p.srcRect.isEmpty())
+				p.srcRect = altSrc;
 		} else {
-			stream.skip(50);
+			readRect(stream, p.srcRect);
 		}
+		readRect(stream, p.slotRect);
+		readRect(stream, p.homeRect);
+		p.defaultRotation = stream.readByte();
+		p.isPreRotated = stream.readByte();
+	}
+
+	if (isNancy10) {
+		stream.skip(32); // TODO: 32 post-piece bytes, layout undecoded.
+		readFilename(stream, _extraSoundName);
+		readRect(stream, _animRectA);
+		readRect(stream, _animRectB);
+		for (uint i = 0; i < 6; ++i)
+			_animLayout[i] = stream.readSint16LE();
+		_animSound1.readNormal(stream);
+		_animSound2.readNormal(stream);
 	}
 
-	_pickupSound.readNormal(stream);  // +0x43e: played when rotating a placed piece
-	_rotateSound.readNormal(stream);  // +0x46f: played when picking up an unplaced piece
-	_dropSound.readNormal(stream);    // +0x4a0: played when dropping a piece
+	_pickupSound.readNormal(stream);
+	_rotateSound.readNormal(stream);
+	_dropSound.readNormal(stream);
 	readFilename(stream, _dropAlt1Filename);
 	readFilename(stream, _dropAlt2Filename);
 
-	_goodPlacementSound.readNormal(stream); // +0x513
+	_goodPlacementSound.readNormal(stream);
 	readFilename(stream, _goodAlt1Filename);
 	readFilename(stream, _goodAlt2Filename);
 
@@ -145,7 +180,7 @@ void OneBuildPuzzle::readData(Common::SeekableReadStream &stream) {
 	for (uint i = 0; i < 3; ++i)
 		readFilename(stream, unusedKey);
 
-	_badPlacementSound.readNormal(stream);  // +0x841
+	_badPlacementSound.readNormal(stream);
 	readFilename(stream, _badAlt1Filename);
 	readFilename(stream, _badAlt2Filename);
 
@@ -159,7 +194,7 @@ void OneBuildPuzzle::readData(Common::SeekableReadStream &stream) {
 	for (uint i = 0; i < 3; ++i)
 		readFilename(stream, unusedKey);
 
-	stream.skip(4); // unknown bytes at +0xb6f
+	stream.skip(4); // TODO: 4 bytes before solveScene, unknown.
 	_solveScene.readData(stream);
 	_completionSound.readNormal(stream);
 	readFilename(stream, unusedKey);
diff --git a/engines/nancy/action/puzzle/onebuildpuzzle.h b/engines/nancy/action/puzzle/onebuildpuzzle.h
index 5e7db6f8c0b..81f2d6f7c49 100644
--- a/engines/nancy/action/puzzle/onebuildpuzzle.h
+++ b/engines/nancy/action/puzzle/onebuildpuzzle.h
@@ -84,6 +84,22 @@ protected:
 	bool _orderedPlacement = false; // Pieces must be placed in a specific order
 	Common::Array<int16> _placementOrder; // 1-indexed piece IDs in required placement order
 
+	// --- Nancy 10 additions ---
+
+	// TODO: runtime role unknown; parsed for round-trip but not consumed.
+	bool _legacyOrderedFlag = false;
+	Common::Array<int16> _legacyPlacementOrder;
+
+	// Filename only (no SoundDescription metadata).
+	Common::String _extraSoundName;
+
+	// Completion sprite-sheet animation; TODO: not wired up.
+	Common::Rect _animRectA;
+	Common::Rect _animRectB;
+	int16 _animLayout[6] = {}; // cols, framesPerStep, baseX, baseY, spacing, totalRows
+	SoundDescription _animSound1;
+	SoundDescription _animSound2;
+
 	Common::Array<Piece> _pieces;
 
 	SoundDescription _pickupSound;



From noreply at scummvm.org  Fri Jun  5 23:36:09 2026
From: noreply at scummvm.org (sev-)
Date: Fri,  5 Jun 2026 23:36:09 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 a3db1e931532b897896180f50e395ceb80406841
Message-ID: <20260605233609.A45AA7005BC@ron.scummvm.net>

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:
1f18790b79 DIRECTOR: Clarify the `field` keyword
a3db1e9315 DIRECTOR: XLIBS: Fix path resolving in MapNavigatorXObj


Commit: 1f18790b7927952351a067d84930b34fe787f72b
    https://github.com/scummvm/scummvm/commit/1f18790b7927952351a067d84930b34fe787f72b
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2026-06-06T01:12:57+02:00

Commit Message:
DIRECTOR: Clarify the `field` keyword

It is meant to refer only to the text cast members. Without this
restriction, we try loading first cast member with the matching name.

This fixes startup in jewels1, where a script cast member and a text
cast member both have name "nullMouseUpScript" in "CIPHER/NAVLOAD.DXR"

Changed paths:
    engines/director/lingo/lingo-the.cpp


diff --git a/engines/director/lingo/lingo-the.cpp b/engines/director/lingo/lingo-the.cpp
index c1ac922028f..211dfe71ada 100644
--- a/engines/director/lingo/lingo-the.cpp
+++ b/engines/director/lingo/lingo-the.cpp
@@ -2314,7 +2314,7 @@ Datum Lingo::getTheField(Datum &id1, int field) {
 		return d;
 	}
 
-	CastMemberID id = id1.asMemberID();
+	CastMemberID id = id1.asMemberID(kCastText);
 
 	CastMember *member = movie->getCastMember(id);
 	if (!member) {


Commit: a3db1e931532b897896180f50e395ceb80406841
    https://github.com/scummvm/scummvm/commit/a3db1e931532b897896180f50e395ceb80406841
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2026-06-06T01:27:34+02:00

Commit Message:
DIRECTOR: XLIBS: Fix path resolving in MapNavigatorXObj

Fixes the XObj instantiation in jewels1

Changed paths:
    engines/director/lingo/xlibs/m/mapnavigatorxobj.cpp


diff --git a/engines/director/lingo/xlibs/m/mapnavigatorxobj.cpp b/engines/director/lingo/xlibs/m/mapnavigatorxobj.cpp
index c6e8381a5d6..48056929149 100644
--- a/engines/director/lingo/xlibs/m/mapnavigatorxobj.cpp
+++ b/engines/director/lingo/xlibs/m/mapnavigatorxobj.cpp
@@ -145,7 +145,9 @@ void MapNavigatorXObj::m_new(int nargs) {
 	me->_filename = g_lingo->pop().asString();
 	Common::File file;
 
-	if (!file.open(Common::Path(me->_filename))) {
+	Common::Path resPath = findPath(me->_filename, true, true, false);
+
+	if (!file.open(resPath)) {
 		warning("MapNavigatorXObj::m_new(): Cannot open file %s", me->_filename.c_str());
 		g_lingo->push(g_lingo->_state->me);
 		return;



From noreply at scummvm.org  Fri Jun  5 23:44:39 2026
From: noreply at scummvm.org (mduggan)
Date: Fri,  5 Jun 2026 23:44:39 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 420ff8f9d539706854ecb577334f5e35b9c28395
Message-ID: <20260605234439.807C37005BC@ron.scummvm.net>

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:
c6003f8599 ACCESS: Mark Noctropolis ready for testing
420ff8f9d5 NEWS: Add Noctropolis support and other ACCESS fixes


Commit: c6003f859938774c639acb2bd5f12172ee067ad2
    https://github.com/scummvm/scummvm/commit/c6003f859938774c639acb2bd5f12172ee067ad2
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2026-06-06T09:43:01+10:00

Commit Message:
ACCESS: Mark Noctropolis ready for testing

Changed paths:
    engines/access/detection_tables.h


diff --git a/engines/access/detection_tables.h b/engines/access/detection_tables.h
index b1ea7341ab2..8ea38e57609 100644
--- a/engines/access/detection_tables.h
+++ b/engines/access/detection_tables.h
@@ -138,7 +138,7 @@ static const AccessGameDescription gameDescriptions[] = {
 			AD_ENTRY1s("demo/scene01.ap", "3a154bf58e10cd7ace14cab1bf5adf4a", 145796),
 			Common::EN_ANY,
 			Common::kPlatformWindows,
-			ADGF_UNSTABLE | ADGF_DEMO,
+			ADGF_TESTING | ADGF_DEMO,
 			GUIO1(GUIO_NONE)
 		},
 		kGameNoctropolis,
@@ -146,14 +146,14 @@ static const AccessGameDescription gameDescriptions[] = {
 	},
 
 	{
-		// Noctropolis (Sum from GOG Windows and macOS versions)
+		// Noctropolis - sum of this file is the same in all known verisons
 		{
 			"noctropolis",
 			nullptr,
 			AD_ENTRY1s("dark/scene01.ap", "3a154bf58e10cd7ace14cab1bf5adf4a", 147954),
 			Common::EN_ANY,
 			Common::kPlatformWindows,
-			ADGF_UNSTABLE,
+			ADGF_TESTING,
 			GUIO1(GUIO_NONE)
 		},
 		kGameNoctropolis,


Commit: 420ff8f9d539706854ecb577334f5e35b9c28395
    https://github.com/scummvm/scummvm/commit/420ff8f9d539706854ecb577334f5e35b9c28395
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2026-06-06T09:43:28+10:00

Commit Message:
NEWS: Add Noctropolis support and other ACCESS fixes

Changed paths:
    NEWS.md


diff --git a/NEWS.md b/NEWS.md
index 33396fcaf27..25658b24a49 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -7,13 +7,17 @@ For a more comprehensive changelog of the latest experimental code, see:
    - Added support for Nancy Drew: The Haunted Carousel.
    - Added support for Nancy Drew: Danger on Deception Island.
    - Added support for Cartoon Carnival.
-   - Added support for Alfred Pelrock: En Busca de un Sue??o
+   - Added support for Alfred Pelrock: En Busca de un Sue??o.
+   - Added support for Noctropolis.
 
  General:
    - Highlight clickable links in the GUI with a cursor change.
    - Fixed window size when switching out of fullscreen mode on 3D engines.
    - Implemented NULL OPL driver for lower-base systems.
 
+ ACCESS:
+   - Fixed various script issues in Martian Memorandum.
+
  AGOS:
    - Added support for language files used by Amiga and Macintosh
      versions of Simon2.



From noreply at scummvm.org  Fri Jun  5 23:48:46 2026
From: noreply at scummvm.org (sev-)
Date: Fri,  5 Jun 2026 23:48:46 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 3f37431a182ca2c9c3c207691320a535844ed970
Message-ID: <20260605234846.88E287005BC@ron.scummvm.net>

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

Summary:
3c6f276984 DIRECTOR: Add myfolder xlib
431cb84973 DIRECTOR: Add getuinfo xlib
52a4c8a3aa DIRECTOR: Fix set scriptText updating wrong script type slot
3f37431a18 DIRECTOR: Remove now unneeded elroy hits the pavement quirk


Commit: 3c6f276984f90668cdee4e7f7f7f09d014303aac
    https://github.com/scummvm/scummvm/commit/3c6f276984f90668cdee4e7f7f7f09d014303aac
Author: Zachary Berry (zberry92 at gmail.com)
Date: 2026-06-06T01:48:41+02:00

Commit Message:
DIRECTOR: Add myfolder xlib

Changed paths:
  A engines/director/lingo/xlibs/m/myfolder.cpp
  A engines/director/lingo/xlibs/m/myfolder.h
    engines/director/lingo/lingo-object.cpp
    engines/director/module.mk


diff --git a/engines/director/lingo/lingo-object.cpp b/engines/director/lingo/lingo-object.cpp
index 5f6193c1e23..69ba4c7e3b8 100644
--- a/engines/director/lingo/lingo-object.cpp
+++ b/engines/director/lingo/lingo-object.cpp
@@ -103,6 +103,7 @@
 #include "director/lingo/xlibs/m/msfile.h"
 #include "director/lingo/xlibs/m/mystisle.h"
 #include "director/lingo/xlibs/m/mazexobj.h"
+#include "director/lingo/xlibs/m/myfolder.h"
 #include "director/lingo/xlibs/o/openbleedwindowxcmd.h"
 #include "director/lingo/xlibs/o/orthoplayxobj.h"
 #include "director/lingo/xlibs/p/paco.h"
@@ -331,6 +332,7 @@ static const struct XLibProto {
 	XLIBDEF(MovUtilsXObj,		kXObj,			400),	// D4
 	XLIBDEF(MSFile,             kXObj,          400),   // D4
 	XLIBDEF(MuiXtra,			kXtraObj,					500),	// D5
+	XLIBDEF(MyFolderXObj,			kXObj,					400),	// D4
 	XLIBDEF(MystIsleXObj,		kXObj,			400),	// D4
 	XLIBDEF(OSCheckXtra,		kXtraObj,		400),	// D4
 	XLIBDEF(OpenBleedWindowXCMD,kXObj,			300),	// D3
diff --git a/engines/director/lingo/xlibs/m/myfolder.cpp b/engines/director/lingo/xlibs/m/myfolder.cpp
new file mode 100644
index 00000000000..82667cb063e
--- /dev/null
+++ b/engines/director/lingo/xlibs/m/myfolder.cpp
@@ -0,0 +1,107 @@
+/* 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/system.h"
+
+#include "director/director.h"
+#include "director/lingo/lingo.h"
+#include "director/lingo/lingo-object.h"
+#include "director/lingo/lingo-utils.h"
+#include "director/lingo/xlibs/m/myfolder.h"
+
+/**************************************************
+ *
+ * USED IN:
+ * dimensionq
+ *
+ **************************************************/
+
+/*
+-- MyFolder XObject. Windows version. (c) 1995 Mark Carolan
+--MyFolder
+I      mNew                --Creates a new instance of the XObject
+X      mDispose            --Disposes of XObject instance
+SS     mDo, folderName     --Returns pathname as a string
+-- If folderName is empty string, returns path to preferences directory.
+-- If folderName is supplied, folder folderName is created in preferences.
+-- If folder folderName already exists in preferences, full pathname is returned.
+-- Check for errors by looking for non-string return value or string starting with: <Error
+-- Shareware. $US15 per title gets you Mac & Win version plus a good night's sleep.
+-- Free use for demos. Contact Mark_Carolan at aapda.com.au Compuserve 100242,1154
+ */
+
+namespace Director {
+
+const char *MyFolderXObj::xlibName = "MyFolder";
+const XlibFileDesc MyFolderXObj::fileNames[] = {
+	{ "MYFOLDER",   nullptr },
+	{ nullptr,        nullptr },
+};
+
+static MethodProto xlibMethods[] = {
+	{ "new",				MyFolderXObj::m_new,		 0, 0,	400 },
+	{ "dispose",				MyFolderXObj::m_dispose,		 0, 0,	400 },
+	{ "do",				MyFolderXObj::m_do,		 1, 1,	400 },
+
+	{ nullptr, nullptr, 0, 0, 0 }
+};
+
+static BuiltinProto xlibBuiltins[] = {
+	{ "MyFolder", MyFolderXObj::m_myFolder, 1, 1, 400, HBLTIN },
+	{ nullptr, nullptr, 0, 0, 0, VOIDSYM }
+};
+
+MyFolderXObject::MyFolderXObject(ObjectType ObjectType) :Object<MyFolderXObject>("MyFolder") {
+	_objType = ObjectType;
+}
+
+void MyFolderXObj::open(ObjectType type, const Common::Path &path) {
+    MyFolderXObject::initMethods(xlibMethods);
+    MyFolderXObject *xobj = new MyFolderXObject(type);
+    if (type == kXtraObj) {
+        g_lingo->_openXtras.push_back(xlibName);
+		g_lingo->_openXtraObjects.push_back(xobj);
+	}
+    g_lingo->exposeXObject(xlibName, xobj);
+    g_lingo->initBuiltIns(xlibBuiltins);
+}
+
+void MyFolderXObj::close(ObjectType type) {
+    MyFolderXObject::cleanupMethods();
+    g_lingo->_globalvars[xlibName] = Datum();
+
+}
+
+void MyFolderXObj::m_new(int nargs) {
+	g_lingo->printSTUBWithArglist("MyFolderXObj::m_new", nargs);
+	g_lingo->dropStack(nargs);
+	g_lingo->push(g_lingo->_state->me);
+}
+
+void MyFolderXObj::m_myFolder(int nargs) {
+	g_lingo->dropStack(nargs);
+	g_lingo->push(Datum(""));
+}
+
+XOBJSTUBNR(MyFolderXObj::m_dispose)
+XOBJSTUB(MyFolderXObj::m_do, "")
+
+}
diff --git a/engines/director/lingo/xlibs/m/myfolder.h b/engines/director/lingo/xlibs/m/myfolder.h
new file mode 100644
index 00000000000..bfdfe8f9af5
--- /dev/null
+++ b/engines/director/lingo/xlibs/m/myfolder.h
@@ -0,0 +1,49 @@
+/* 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 DIRECTOR_LINGO_XLIBS_M_MYFOLDER_H
+#define DIRECTOR_LINGO_XLIBS_M_MYFOLDER_H
+
+namespace Director {
+
+class MyFolderXObject : public Object<MyFolderXObject> {
+public:
+	MyFolderXObject(ObjectType objType);
+};
+
+namespace MyFolderXObj {
+
+extern const char *xlibName;
+extern const XlibFileDesc fileNames[];
+
+void open(ObjectType type, const Common::Path &path);
+void close(ObjectType type);
+
+void m_new(int nargs);
+void m_dispose(int nargs);
+void m_do(int nargs);
+void m_myFolder(int nargs);
+
+} // End of namespace MyFolderXObj
+
+} // End of namespace Director
+
+#endif
diff --git a/engines/director/module.mk b/engines/director/module.mk
index 2b460e50677..d6d1a7c7b66 100644
--- a/engines/director/module.mk
+++ b/engines/director/module.mk
@@ -135,6 +135,7 @@ MODULE_OBJS = \
 	lingo/xlibs/m/mystisle.o \
 	lingo/xlibs/b/blockthedrawingxobj.o \
 	lingo/xlibs/m/mazexobj.o \
+	lingo/xlibs/m/myfolder.o \
 	lingo/xlibs/o/openbleedwindowxcmd.o \
  	lingo/xlibs/p/playsoundmoviexobj.o \
  	lingo/xlibs/s/savenrestorexobj.o \


Commit: 431cb84973de361ac6263c9ca66d9f33be4247c1
    https://github.com/scummvm/scummvm/commit/431cb84973de361ac6263c9ca66d9f33be4247c1
Author: Zachary Berry (zberry92 at gmail.com)
Date: 2026-06-06T01:48:41+02:00

Commit Message:
DIRECTOR: Add getuinfo xlib

Changed paths:
  A engines/director/lingo/xlibs/g/getuinfo.cpp
  A engines/director/lingo/xlibs/g/getuinfo.h
    engines/director/lingo/lingo-object.cpp
    engines/director/lingo/xlibs/m/myfolder.cpp
    engines/director/module.mk


diff --git a/engines/director/lingo/lingo-object.cpp b/engines/director/lingo/lingo-object.cpp
index 69ba4c7e3b8..b1002a6d565 100644
--- a/engines/director/lingo/lingo-object.cpp
+++ b/engines/director/lingo/lingo-object.cpp
@@ -77,6 +77,7 @@
 #include "director/lingo/xlibs/g/getscreensizexfcn.h"
 #include "director/lingo/xlibs/g/getsoundinlevel.h"
 #include "director/lingo/xlibs/g/gpid.h"
+#include "director/lingo/xlibs/g/getuinfo.h"
 #include "director/lingo/xlibs/h/henry.h"
 #include "director/lingo/xlibs/h/hitmap.h"
 #include "director/lingo/xlibs/i/inixobj.h"
@@ -303,6 +304,7 @@ static const struct XLibProto {
 	XLIBDEF(GetScreenRectsXFCN,	kXObj,			300),	// D3
 	XLIBDEF(GetScreenSizeXFCN,	kXObj,			300),	// D3
 	XLIBDEF(GetSoundInLevelXObj,kXObj,			400),	// D4
+	XLIBDEF(GetUInfoXObj,			kXObj,					400),	// D4
 	XLIBDEF(GpidXObj,			kXObj,			400),	// D4
 	XLIBDEF(HenryXObj,			kXObj,			400),	// D4
 	XLIBDEF(HitMap,				kXObj,			400),	// D4
diff --git a/engines/director/lingo/xlibs/g/getuinfo.cpp b/engines/director/lingo/xlibs/g/getuinfo.cpp
new file mode 100644
index 00000000000..9c6e5d9ff4c
--- /dev/null
+++ b/engines/director/lingo/xlibs/g/getuinfo.cpp
@@ -0,0 +1,120 @@
+/* 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/system.h"
+
+#include "director/director.h"
+#include "director/movie.h"
+#include "director/lingo/lingo.h"
+#include "director/lingo/lingo-object.h"
+#include "director/lingo/xlibs/g/getuinfo.h"
+
+/**************************************************
+ *
+ * USED IN:
+ * Elroy Hits the Pavement
+ *
+ **************************************************/
+
+/*
+-- Penworks XObject Utilities
+--GetUInfo
+I      mNew                -- Creates a new instance of the XObject
+X      mDispose            -- Disposes of XObject instance
+I      mLButtonDown        -- Returns TRUE if LEFT mouse button is down
+I      mRButtonDown        -- Returns TRUE if RIGHT mouse button is down
+I      mKeyPressed         -- Returns the virtual keycode if a key is pressed
+ */
+
+namespace Director {
+
+const char *GetUInfoXObj::xlibName = "GetUInfo";
+const XlibFileDesc GetUInfoXObj::fileNames[] = {
+	{ "GETUINFO",   nullptr },
+	{ nullptr,        nullptr },
+};
+
+static MethodProto xlibMethods[] = {
+	{ "new",				GetUInfoXObj::m_new,		 0, 0,	400 },
+	{ "dispose",				GetUInfoXObj::m_dispose,		 0, 0,	400 },
+	{ "lButtonDown",				GetUInfoXObj::m_lButtonDown,		 0, 0,	400 },
+	{ "rButtonDown",				GetUInfoXObj::m_rButtonDown,		 0, 0,	400 },
+	{ "keyPressed",				GetUInfoXObj::m_keyPressed,		 0, 0,	400 },
+
+	{ nullptr, nullptr, 0, 0, 0 }
+};
+
+static BuiltinProto xlibBuiltins[] = {
+
+	{ nullptr, nullptr, 0, 0, 0, VOIDSYM }
+};
+
+GetUInfoXObject::GetUInfoXObject(ObjectType ObjectType) :Object<GetUInfoXObject>("GetUInfo") {
+	_objType = ObjectType;
+}
+
+void GetUInfoXObj::open(ObjectType type, const Common::Path &path) {
+    GetUInfoXObject::initMethods(xlibMethods);
+    GetUInfoXObject *xobj = new GetUInfoXObject(type);
+    if (type == kXtraObj) {
+        g_lingo->_openXtras.push_back(xlibName);
+		g_lingo->_openXtraObjects.push_back(xobj);
+	}
+    g_lingo->exposeXObject(xlibName, xobj);
+    g_lingo->initBuiltIns(xlibBuiltins);
+}
+
+void GetUInfoXObj::close(ObjectType type) {
+    GetUInfoXObject::cleanupMethods();
+    g_lingo->_globalvars[xlibName] = Datum();
+
+}
+
+void GetUInfoXObj::m_new(int nargs) {
+	g_lingo->printSTUBWithArglist("GetUInfoXObj::m_new", nargs);
+	g_lingo->dropStack(nargs);
+	g_lingo->push(g_lingo->_state->me);
+}
+
+void GetUInfoXObj::m_dispose(int nargs) {
+	g_lingo->_state->me.u.obj->dispose();
+	g_lingo->push(g_lingo->_state->me);
+}
+
+void GetUInfoXObj::m_lButtonDown(int nargs) {
+	g_lingo->dropStack(nargs);
+	int state = g_system->getEventManager()->getButtonState() & (1 << Common::MOUSE_BUTTON_LEFT) ? 1 : 0;
+	g_lingo->push(Datum(state));
+}
+
+void GetUInfoXObj::m_rButtonDown(int nargs) {
+	g_lingo->dropStack(nargs);
+	int state = g_system->getEventManager()->getButtonState() & (1 << Common::MOUSE_BUTTON_RIGHT) ? 1 : 0;
+	g_lingo->push(Datum(state));
+}
+
+void GetUInfoXObj::m_keyPressed(int nargs) {
+	g_lingo->dropStack(nargs);
+	Movie *movie = g_director->getCurrentMovie();
+	g_lingo->push(Datum(movie ? movie->_keyCode : 0));
+}
+
+}
diff --git a/engines/director/lingo/xlibs/g/getuinfo.h b/engines/director/lingo/xlibs/g/getuinfo.h
new file mode 100644
index 00000000000..c3c05176b17
--- /dev/null
+++ b/engines/director/lingo/xlibs/g/getuinfo.h
@@ -0,0 +1,50 @@
+/* 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 DIRECTOR_LINGO_XLIBS_G_GETUINFO_H
+#define DIRECTOR_LINGO_XLIBS_G_GETUINFO_H
+
+namespace Director {
+
+class GetUInfoXObject : public Object<GetUInfoXObject> {
+public:
+	GetUInfoXObject(ObjectType objType);
+};
+
+namespace GetUInfoXObj {
+
+extern const char *xlibName;
+extern const XlibFileDesc fileNames[];
+
+void open(ObjectType type, const Common::Path &path);
+void close(ObjectType type);
+
+void m_new(int nargs);
+void m_dispose(int nargs);
+void m_lButtonDown(int nargs);
+void m_rButtonDown(int nargs);
+void m_keyPressed(int nargs);
+
+} // End of namespace GetUInfoXObj
+
+} // End of namespace Director
+
+#endif
diff --git a/engines/director/lingo/xlibs/m/myfolder.cpp b/engines/director/lingo/xlibs/m/myfolder.cpp
index 82667cb063e..303a3b4e1a1 100644
--- a/engines/director/lingo/xlibs/m/myfolder.cpp
+++ b/engines/director/lingo/xlibs/m/myfolder.cpp
@@ -101,7 +101,10 @@ void MyFolderXObj::m_myFolder(int nargs) {
 	g_lingo->push(Datum(""));
 }
 
-XOBJSTUBNR(MyFolderXObj::m_dispose)
+void MyFolderXObj::m_dispose(int nargs) {
+	g_lingo->_state->me.u.obj->dispose();
+	g_lingo->push(g_lingo->_state->me);
+}
 XOBJSTUB(MyFolderXObj::m_do, "")
 
 }
diff --git a/engines/director/module.mk b/engines/director/module.mk
index d6d1a7c7b66..154fa7fbbc7 100644
--- a/engines/director/module.mk
+++ b/engines/director/module.mk
@@ -136,6 +136,7 @@ MODULE_OBJS = \
 	lingo/xlibs/b/blockthedrawingxobj.o \
 	lingo/xlibs/m/mazexobj.o \
 	lingo/xlibs/m/myfolder.o \
+	lingo/xlibs/g/getuinfo.o \
 	lingo/xlibs/o/openbleedwindowxcmd.o \
  	lingo/xlibs/p/playsoundmoviexobj.o \
  	lingo/xlibs/s/savenrestorexobj.o \


Commit: 52a4c8a3aa48ee6094539e5b92c607bacfa30347
    https://github.com/scummvm/scummvm/commit/52a4c8a3aa48ee6094539e5b92c607bacfa30347
Author: Zachary Berry (zberry92 at gmail.com)
Date: 2026-06-06T01:48:41+02:00

Commit Message:
DIRECTOR: Fix set scriptText updating wrong script type slot

set the scriptText of cast X was always calling replaceCode with
kCastScript, but sprite event dispatch looks up kScoreScript.

For behavior cast members (_scriptType == kScoreScript), the
injection landed in the wrong slot and was never executed on click.
Fix by using the cast member's actual _scriptType when replacing.

Changed paths:
    engines/director/castmember/castmember.cpp


diff --git a/engines/director/castmember/castmember.cpp b/engines/director/castmember/castmember.cpp
index 02aa2d72521..e1da80eab3e 100644
--- a/engines/director/castmember/castmember.cpp
+++ b/engines/director/castmember/castmember.cpp
@@ -26,6 +26,7 @@
 #include "director/cast.h"
 #include "director/movie.h"
 #include "director/castmember/castmember.h"
+#include "director/castmember/script.h"
 #include "director/lingo/lingo-the.h"
 #include "director/util.h"
 
@@ -321,7 +322,12 @@ void CastMember::setField(int field, const Datum &d) {
 			warning("CastMember::setField(): CastMember info for %d not found", _castId);
 			return;
 		}
-		_cast->_lingoArchive->replaceCode(*d.u.s, kCastScript, _castId);
+		{
+			ScriptType scriptType = kCastScript;
+			if (_type == kCastLingoScript)
+				scriptType = static_cast<ScriptCastMember *>(this)->_scriptType;
+			_cast->_lingoArchive->replaceCode(*d.u.s, scriptType, _castId);
+		}
 		castInfo->script = d.asString();
 		return;
 	case kTheWidth:


Commit: 3f37431a182ca2c9c3c207691320a535844ed970
    https://github.com/scummvm/scummvm/commit/3f37431a182ca2c9c3c207691320a535844ed970
Author: Zachary Berry (zberry92 at gmail.com)
Date: 2026-06-06T01:48:41+02:00

Commit Message:
DIRECTOR: Remove now unneeded elroy hits the pavement quirk

Changed paths:
    engines/director/lingo/lingo-patcher.cpp


diff --git a/engines/director/lingo/lingo-patcher.cpp b/engines/director/lingo/lingo-patcher.cpp
index f83bf587adf..b1ce786036f 100644
--- a/engines/director/lingo/lingo-patcher.cpp
+++ b/engines/director/lingo/lingo-patcher.cpp
@@ -397,14 +397,6 @@ on GetCDLetter tagFile, discNumber\r\
 end \r\
 ";
 
-/* Elroy Hits the Pavement has a missing mouseUp script for clicking on the map when
- * you game over in the gangster's hideout. */
-const char *const elroypaveMapFix = " \
-on mouseUp\r\
-  handleMapClick()\r\
-end \r\
-";
-
 /* Frankenstein: Through The Eyes Of The Monster uses a projector FRANKIE.EXE, which calls an
  * identically-named submovie FRANKIE.DIR. For now we can work around this mess by referring to
  * the full "path" of the embedded submovie so path detection doesn't collide with FRANKIE.EXE.
@@ -561,10 +553,6 @@ struct ScriptHandlerPatch {
 	{"kyoto", nullptr, kPlatformWindows, "ck_data\\opening\\shared.dxr", kMovieScript, 802, DEFAULT_CAST_LIB, &kyotoTextEntryFix},
 	{"kyoto", nullptr, kPlatformWindows, "ck_data\\rajoumon\\shared.dxr", kMovieScript, 840, DEFAULT_CAST_LIB, &kyotoTextEntryFix},
 	{"kyoto", nullptr, kPlatformWindows, "ck_data\\rokudou\\shared.dxr", kMovieScript, 846, DEFAULT_CAST_LIB, &kyotoTextEntryFix},
-	{"elroypave", nullptr, kPlatformWindows, "P04\\P04HAZ\\ENDING.DXR", kScoreScript, 27, DEFAULT_CAST_LIB, &elroypaveMapFix},
-	{"elroypave", nullptr, kPlatformWindows, "P04\\P04HAZ\\ENDING.DXR", kScoreScript, 29, DEFAULT_CAST_LIB, &elroypaveMapFix},
-	{"elroypave", nullptr, kPlatformMacintosh, "P04:p04Haz:ending.Dxr", kScoreScript, 27, DEFAULT_CAST_LIB, &elroypaveMapFix},
-	{"elroypave", nullptr, kPlatformMacintosh, "P04:p04Haz:ending.Dxr", kScoreScript, 29, DEFAULT_CAST_LIB, &elroypaveMapFix},
 	{"vnc", nullptr, kPlatformWindows, "VNC\\VNC.EXE", kMovieScript, 57, DEFAULT_CAST_LIB, &vncSkipDetection},
 	{"vnc", nullptr, kPlatformWindows, "VNC2\\SHARED.DXR", kMovieScript, 1248, DEFAULT_CAST_LIB, &vncEnableCheats},
 	{"vnc", nullptr, kPlatformWindows, "VNC\\Shared.DXR", kMovieScript, 1562, DEFAULT_CAST_LIB, &vncFixIntro},



From noreply at scummvm.org  Fri Jun  5 23:50:21 2026
From: noreply at scummvm.org (sev-)
Date: Fri,  5 Jun 2026 23:50:21 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 ad01fa472ba1cf628dfa0df3a739ab9a182d7a4b
Message-ID: <20260605235021.72CC67005BC@ron.scummvm.net>

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

Summary:
ad01fa472b DIRECTOR: JANITORIAL: Sort module.mk files


Commit: ad01fa472ba1cf628dfa0df3a739ab9a182d7a4b
    https://github.com/scummvm/scummvm/commit/ad01fa472ba1cf628dfa0df3a739ab9a182d7a4b
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2026-06-06T01:49:38+02:00

Commit Message:
DIRECTOR: JANITORIAL: Sort module.mk files

Changed paths:
    engines/director/module.mk


diff --git a/engines/director/module.mk b/engines/director/module.mk
index 154fa7fbbc7..0b12b8920b5 100644
--- a/engines/director/module.mk
+++ b/engines/director/module.mk
@@ -190,10 +190,10 @@ MODULE_OBJS = \
 	lingo/xtras/d/directsound.o \
 	lingo/xtras/d/displayres.o \
 	lingo/xtras/f/filextra.o \
+	lingo/xtras/g/getdir.o \
 	lingo/xtras/k/keypoll.o \
 	lingo/xtras/m/masterapp.o \
 	lingo/xtras/m/mui.o \
-	lingo/xtras/g/getdir.o \
 	lingo/xtras/o/openurl.o \
 	lingo/xtras/o/oscheck.o \
 	lingo/xtras/q/qtvrxtra.o \



From noreply at scummvm.org  Sat Jun  6 00:11:55 2026
From: noreply at scummvm.org (bluegr)
Date: Sat,  6 Jun 2026 00:11:55 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 b4973e1e0fbb75eb4f76e2a354e3369f7ac1d92d
Message-ID: <20260606001155.7C104700573@ron.scummvm.net>

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:
bc89beb05e NANCY: Allow loading from the GMM before starting a new scene
b4973e1e0f NANCY: Add changes to mazechasepuzzle for Nancy10


Commit: bc89beb05e71599f8e88b3c3410ca909d925afd3
    https://github.com/scummvm/scummvm/commit/bc89beb05e71599f8e88b3c3410ca909d925afd3
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-06-06T03:11:44+03:00

Commit Message:
NANCY: Allow loading from the GMM before starting a new scene

Fix #16741

Changed paths:
    engines/nancy/nancy.cpp
    engines/nancy/state/scene.cpp


diff --git a/engines/nancy/nancy.cpp b/engines/nancy/nancy.cpp
index f965a26249d..243c95591d9 100644
--- a/engines/nancy/nancy.cpp
+++ b/engines/nancy/nancy.cpp
@@ -137,15 +137,15 @@ Common::Error NancyEngine::saveGameStream(Common::WriteStream *stream, bool isAu
 }
 
 bool NancyEngine::canLoadGameStateCurrently(Common::U32String *msg) {
-	return canSaveGameStateCurrently();
+	return NancySceneState.getActiveConversation() == nullptr &&
+		   NancySceneState.getActiveMovie() == nullptr &&
+		   !NancySceneState.isRunningAd();
 }
 
 bool NancyEngine::canSaveGameStateCurrently(Common::U32String *msg) {
 	return State::Scene::hasInstance() &&
 		   NancySceneState.getState() == State::Scene::kRun &&
-			NancySceneState.getActiveConversation() == nullptr &&
-			NancySceneState.getActiveMovie() == nullptr &&
-			!NancySceneState.isRunningAd();
+		   canLoadGameStateCurrently();
 }
 
 void NancyEngine::secondChance() {
diff --git a/engines/nancy/state/scene.cpp b/engines/nancy/state/scene.cpp
index 975ed3f5f37..f3f856c11ce 100644
--- a/engines/nancy/state/scene.cpp
+++ b/engines/nancy/state/scene.cpp
@@ -662,6 +662,9 @@ void Scene::registerGraphics() {
 }
 
 void Scene::synchronize(Common::Serializer &ser) {
+	if (_flags.eventFlags.empty())
+		init();
+
 	ser.syncAsUint16LE(_sceneState.currentScene.sceneID);
 	ser.syncAsUint16LE(_sceneState.currentScene.frameID);
 	ser.syncAsUint16LE(_sceneState.currentScene.verticalOffset);


Commit: b4973e1e0fbb75eb4f76e2a354e3369f7ac1d92d
    https://github.com/scummvm/scummvm/commit/b4973e1e0fbb75eb4f76e2a354e3369f7ac1d92d
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-06-06T03:11:45+03:00

Commit Message:
NANCY: Add changes to mazechasepuzzle for Nancy10

Fixes the "Run for Cover" game in Mary's Gifts

Changed paths:
    engines/nancy/action/puzzle/mazechasepuzzle.cpp


diff --git a/engines/nancy/action/puzzle/mazechasepuzzle.cpp b/engines/nancy/action/puzzle/mazechasepuzzle.cpp
index d4c1ae9165c..ae9cef80e36 100644
--- a/engines/nancy/action/puzzle/mazechasepuzzle.cpp
+++ b/engines/nancy/action/puzzle/mazechasepuzzle.cpp
@@ -144,6 +144,8 @@ void MazeChasePuzzle::updateGraphics() {
 }
 
 void MazeChasePuzzle::readData(Common::SeekableReadStream &stream) {
+	const bool isNancy10 = g_nancy->getGameType() >= kGameTypeNancy10;
+
 	readFilename(stream, _imageName);
 
 	uint width = stream.readUint16LE();
@@ -153,6 +155,10 @@ void MazeChasePuzzle::readData(Common::SeekableReadStream &stream) {
 	_exitPos.x = stream.readUint16LE();
 	_exitPos.y = stream.readUint16LE();
 
+	if (isNancy10) {
+		stream.skip(1); // TODO: unknown byte before the grid.
+	}
+
 	_grid.resize(height, Common::Array<uint16>(width));
 	for (uint y = 0; y < height; ++y) {
 		for (uint x = 0; x < width; ++x) {
@@ -168,6 +174,14 @@ void MazeChasePuzzle::readData(Common::SeekableReadStream &stream) {
 		_startLocations[i].y = stream.readUint16LE();
 	}
 
+	if (isNancy10) {
+		// Fixed 3 slots (player + up to 2 enemies); skip the unused tail.
+		const uint kMaxSlots = 3;
+		const uint used = numEnemies + 1;
+		if (used < kMaxSlots)
+			stream.skip((kMaxSlots - used) * 4);
+	}
+
 	readRect(stream, _playerSrc);
 	readRect(stream, _enemySrc);
 	readRect(stream, _verticalWallSrc);



From noreply at scummvm.org  Sat Jun  6 03:43:38 2026
From: noreply at scummvm.org (moralrecordings)
Date: Sat,  6 Jun 2026 03:43:38 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 a2a8e168107b4a05a799188e6f4752d7650dc224
Message-ID: <20260606034338.4F628700573@ron.scummvm.net>

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

Summary:
a2a8e16810 DIRECTOR: LINGO: Remove error from b_member


Commit: a2a8e168107b4a05a799188e6f4752d7650dc224
    https://github.com/scummvm/scummvm/commit/a2a8e168107b4a05a799188e6f4752d7650dc224
Author: Scott Percival (code at moral.net.au)
Date: 2026-06-06T11:42:59+08:00

Commit Message:
DIRECTOR: LINGO: Remove error from b_member

In practice, it seems in D5 that calling "member X of castLib Y", where
X is out of range, is fine. Certain operations such as "put member X of castLib Y"
are what causes a Lingo error. This is a bug fixed in D6 and
later.

There are D5 games which require the ability to create out of range
CastMemberID objects, so even if we wanted to replicate the bug this
would be the wrong place.

Changed paths:
    engines/director/lingo/lingo-builtins.cpp


diff --git a/engines/director/lingo/lingo-builtins.cpp b/engines/director/lingo/lingo-builtins.cpp
index 98924a725a6..510e5762ddf 100644
--- a/engines/director/lingo/lingo-builtins.cpp
+++ b/engines/director/lingo/lingo-builtins.cpp
@@ -4065,7 +4065,7 @@ void LB::b_member(int nargs) {
 	if (res.member > g_lingo->getMembersNum(res.castLib)) {
 		// D6 and up does not error on non-existing cast members
 		if (g_director->getVersion() < 600) {
-			g_lingo->lingoError("b_member: Cast member ID out of range");
+			debugC(5, kDebugLingoExec, "b_member: Cast member ID %s out of range! In real D5 this would cause Lingo errors for some use cases", res.asString().c_str());
 		}
 	}
 	g_lingo->push(res);



From noreply at scummvm.org  Sat Jun  6 07:55:03 2026
From: noreply at scummvm.org (lotharsm)
Date: Sat,  6 Jun 2026 07:55:03 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 419dde28cf570fc67b15cfe10100066b6beed8f6
Message-ID: <20260606075503.84CA7700573@ron.scummvm.net>

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

Summary:
419dde28cf NEWS: Update German NEWS file


Commit: 419dde28cf570fc67b15cfe10100066b6beed8f6
    https://github.com/scummvm/scummvm/commit/419dde28cf570fc67b15cfe10100066b6beed8f6
Author: Lothar Serra Mari (mail at serra.me)
Date: 2026-06-06T09:54:47+02:00

Commit Message:
NEWS: Update German NEWS file

Changed paths:
    doc/de/NEUES.md


diff --git a/doc/de/NEUES.md b/doc/de/NEUES.md
index d3daa25f76d..ed9278c9525 100644
--- a/doc/de/NEUES.md
+++ b/doc/de/NEUES.md
@@ -8,12 +8,16 @@ Umfangreichere Informationen ??ber die ??nderungen des aktuellen Codes findest D
    - Unterst??tzung f??r Nancy Drew: Danger on Deception Island hinzugef??gt.
    - Unterst??tzung f??r Cartoon Carnival hinzugef??gt.
    - Unterst??tzung f??r Alfred Pelrock: En Busca de un Sue??o hinzugef??gt.
+   - Unterst??tzung f??r Noctropolis hinzugef??gt.
 
  Allgemein:
    - Klickbare Links in der Benutzeroberfl??che werden jetzt durch einen ver??nderten Cursor hervorgehoben.
    - Die Fenstergr????e wird jetzt korrekt wiederhergestellt, wenn in 3D-Engines von Vollbild zur??ck zum Fenster-Modus gewechselt wird.
    - NULL OPL-Treiber f??r schw??chere Systeme implementiert.
 
+ ACCESS:
+   - Mehrere Skript-Fehler in Martian Memorandum behoben.
+
  AGOS:
    - Unterst??tzung f??r die von der Amiga- und Macintosh-Versionen von Simon2 verwendeten
      Sprachdateien hinzugef??gt.



From noreply at scummvm.org  Sat Jun  6 08:04:16 2026
From: noreply at scummvm.org (neuromancer)
Date: Sat,  6 Jun 2026 08:04:16 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 9bbfdda5adc030de46ed381ecb331aceda288f77
Message-ID: <20260606080416.5F5F9700573@ron.scummvm.net>

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:
390947c5b4 SCUMM: RA: restore palette when the player is hit
c741d304f3 SCUMM: RA1: simplify controls using 3DO controls
9bbfdda5ad SCUMM: RA1: added rapid fire following 3do controls


Commit: 390947c5b4e34b2f0920ad8f15f693d39e651feb
    https://github.com/scummvm/scummvm/commit/390947c5b4e34b2f0920ad8f15f693d39e651feb
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-06T10:04:04+02:00

Commit Message:
SCUMM: RA: restore palette when the player is hit

Changed paths:
    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/render.cpp
    engines/scumm/insane/rebel2/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/runlevels.cpp b/engines/scumm/insane/rebel1/runlevels.cpp
index 29792d1ad45..cb6dd67e766 100644
--- a/engines/scumm/insane/rebel1/runlevels.cpp
+++ b/engines/scumm/insane/rebel1/runlevels.cpp
@@ -1806,6 +1806,7 @@ void InsaneRebel1::releaseInteractiveVideoInput() {
 
 void InsaneRebel1::playInteractiveVideoFile(const char *filename, int32 videoOffset, int32 videoStartFrame) {
 	_vm->_splayer->play(filename, 15, videoOffset, videoStartFrame);
+	restoreScreenFlashPalette();
 	restoreInteractiveVideoAudioState();
 	_interactiveVideoActive = false;
 }
diff --git a/engines/scumm/insane/rebel2/levels.cpp b/engines/scumm/insane/rebel2/levels.cpp
index 062ec934395..f7fd926727e 100644
--- a/engines/scumm/insane/rebel2/levels.cpp
+++ b/engines/scumm/insane/rebel2/levels.cpp
@@ -185,6 +185,7 @@ void InsaneRebel2::playMissionBriefing() {
 // Resets handler to 0 (no HUD) and sets flags to 0x28 (cinematic + buffer preserve).
 // All wrapper functions (FUN_00417168/4171c5/417ab2/417327) add | 8 before calling FUN_0041f4d0.
 void InsaneRebel2::playCinematic(const char *filename) {
+	restoreDamageFlashPalette();
 	_gameplaySectionActive = false;
 	_rebelHandler = 0;
 	_rebelStatusBarSprite = 0;  // No status bar during cinematics
@@ -200,6 +201,7 @@ void InsaneRebel2::playCinematic(const char *filename) {
 void InsaneRebel2::playVideoWithText(const char *filename, int textID, int textX, int textY,
                                      int fadeInFrame, int fadeOutFrame) {
 
+	restoreDamageFlashPalette();
 	_gameplaySectionActive = false;
 	_rebelHandler = 0;
 	_rebelStatusBarSprite = 0;
@@ -271,6 +273,7 @@ void InsaneRebel2::playLevelBegin(int levelId) {
 // playLevelEnd -- Level completion video (FUN_00417327).
 void InsaneRebel2::playLevelEnd(int levelId) {
 
+	restoreDamageFlashPalette();
 	_gameplaySectionActive = false;
 	_rebelHandler = 0;
 	_rebelStatusBarSprite = 0;  // No status bar during end cinematic
@@ -290,6 +293,7 @@ void InsaneRebel2::playLevelEnd(int levelId) {
 // playLevelRetry -- Retry prompt video (LEVXX/XXRETRY.SAN, FUN_00417168).
 void InsaneRebel2::playLevelRetry(int levelId) {
 
+	restoreDamageFlashPalette();
 	_gameplaySectionActive = false;
 	_rebelHandler = 0;
 	_rebelStatusBarSprite = 0;  // Reset for retry - will be set by IACT opcode 6 if needed
@@ -309,6 +313,7 @@ void InsaneRebel2::playLevelRetry(int levelId) {
 // playLevelGameOver -- Game over video (FUN_00417ab2).
 void InsaneRebel2::playLevelGameOver(int levelId) {
 
+	restoreDamageFlashPalette();
 	_gameplaySectionActive = false;
 	_rebelHandler = 0;
 	_rebelStatusBarSprite = 0;  // No status bar during game over cinematic
@@ -714,6 +719,7 @@ Common::String InsaneRebel2::selectDeathVideoVariant(int levelId, int phase, int
 // playLevelDeathVariant -- Death video with variant selection.
 void InsaneRebel2::playLevelDeathVariant(int levelId, int phase, int frame) {
 
+	restoreDamageFlashPalette();
 	_gameplaySectionActive = false;
 	_rebelHandler = 0;
 	_rebelStatusBarSprite = 0;  // No status bar during death cinematic
@@ -741,6 +747,7 @@ void InsaneRebel2::playLevelDeathVariant(int levelId, int phase, int frame) {
 // playLevelRetryVariant -- Phase-specific retry video.
 void InsaneRebel2::playLevelRetryVariant(int levelId, int phase) {
 
+	restoreDamageFlashPalette();
 	_gameplaySectionActive = false;
 	_rebelHandler = 0;
 	_rebelStatusBarSprite = 0;  // Reset for retry - will be set by IACT opcode 6 if needed
diff --git a/engines/scumm/insane/rebel2/rebel.cpp b/engines/scumm/insane/rebel2/rebel.cpp
index ae1f86099ac..213e0820411 100644
--- a/engines/scumm/insane/rebel2/rebel.cpp
+++ b/engines/scumm/insane/rebel2/rebel.cpp
@@ -193,6 +193,8 @@ InsaneRebel2::InsaneRebel2(ScummEngine_v7 *scumm) {
 	_damageHighFlashCounter = 0;
 	_damageShakeCounter = 0;
 	memset(_damageSavedPalette, 0, sizeof(_damageSavedPalette));
+	memset(_damageRestorePalette, 0, sizeof(_damageRestorePalette));
+	_damageRestorePaletteValid = false;
 
 	// Text overlay state (FUN_004171c5 chapter title rendering)
 	_textOverlayActive = false;
diff --git a/engines/scumm/insane/rebel2/rebel.h b/engines/scumm/insane/rebel2/rebel.h
index 41cdec1c5be..b0bf580ba9b 100644
--- a/engines/scumm/insane/rebel2/rebel.h
+++ b/engines/scumm/insane/rebel2/rebel.h
@@ -900,11 +900,14 @@ public:
 	void updateDamageFlashPalette();     // FUN_00420562
 	void updateDamageEffect(byte *renderBitmap, int pitch, int width, int height); // FUN_00420754
 	void resetDamageFlash();             // FUN_00420501
+	void restoreDamageFlashPalette();
 
 	int16 _damageFlashCounter;           // DAT_00482404 - palette flash countdown (0..5)
 	int16 _damageHighFlashCounter;       // DAT_00482408 - high-damage red flash (0..16)
 	int16 _damageShakeCounter;           // DAT_0048240c - screen shake countdown (0..10)
 	byte _damageSavedPalette[0x300];     // DAT_00459990 - palette snapshot before flash
+	byte _damageRestorePalette[0x300];   // ScummVM boundary restore snapshot
+	bool _damageRestorePaletteValid;
 
 	// Rebel per-level counters / flags mapped from retail globals
 	bool _rebelOp6Initialized; // Guard: opcode 6 init block (clearBit/links/wave) runs once per video
diff --git a/engines/scumm/insane/rebel2/render.cpp b/engines/scumm/insane/rebel2/render.cpp
index 1f821621e41..c0a7e3ab4d7 100644
--- a/engines/scumm/insane/rebel2/render.cpp
+++ b/engines/scumm/insane/rebel2/render.cpp
@@ -2667,8 +2667,27 @@ void InsaneRebel2::resetDamageFlash() {
 	_damageFlashCounter = 0;
 }
 
+void InsaneRebel2::restoreDamageFlashPalette() {
+	if (_player) {
+		if (_damageRestorePaletteValid)
+			_player->setPalette(_damageRestorePalette);
+		else if (_damageFlashCounter != 0 || _damageHighFlashCounter != 0)
+			_player->setPalette(_damageSavedPalette);
+	}
+
+	_damageFlashCounter = 0;
+	_damageHighFlashCounter = 0;
+	_damageShakeCounter = 0;
+	_damageRestorePaletteValid = false;
+}
+
 // initDamageFlash -- Save palette and initiate 5-frame flash (FUN_00420515).
 void InsaneRebel2::initDamageFlash() {
+	if (!_damageRestorePaletteValid && _player) {
+		memcpy(_damageRestorePalette, _player->_pal, 0x300);
+		_damageRestorePaletteValid = true;
+	}
+
 	if (_damageFlashCounter == 0) {
 		// Save current SMUSH palette before modifying it
 		memcpy(_damageSavedPalette, _player->_pal, 0x300);
@@ -2698,6 +2717,10 @@ void InsaneRebel2::updateDamageFlashPalette() {
 		_damageHighFlashCounter = 0;
 	} else {
 		if (_damageHighFlashCounter == 0) {
+			if (!_damageRestorePaletteValid) {
+				memcpy(_damageRestorePalette, _player->_pal, 0x300);
+				_damageRestorePaletteValid = true;
+			}
 			// Save palette on first frame of high-damage mode
 			memcpy(_damageSavedPalette, _player->_pal, 0x300);
 		}
@@ -2719,6 +2742,8 @@ void InsaneRebel2::updateDamageFlashPalette() {
 					modPal[i] = 0xFF - (((0xFF - _damageSavedPalette[i]) * blend) >> 4);
 				}
 				_player->setPalette(modPal);
+				if (_damageFlashCounter == 0 && _damageHighFlashCounter == 0)
+					_damageRestorePaletteValid = false;
 			}
 		}
 	} else {
diff --git a/engines/scumm/insane/rebel2/runlevels.cpp b/engines/scumm/insane/rebel2/runlevels.cpp
index 63dc995ce41..1dd66088685 100644
--- a/engines/scumm/insane/rebel2/runlevels.cpp
+++ b/engines/scumm/insane/rebel2/runlevels.cpp
@@ -205,6 +205,7 @@ bool InsaneRebel2::playLevelSegment(const char *filename, uint16 flags, bool rec
 	splayer->play(filename, 15);
 	if (recordFrame)
 		_deathFrame = splayer->_frame;
+	restoreDamageFlashPalette();
 	return !_vm->shouldQuit();
 }
 
diff --git a/engines/scumm/smush/rebel/smush_player_ra1.cpp b/engines/scumm/smush/rebel/smush_player_ra1.cpp
index 9b24d8248fa..d6208e7ad8c 100644
--- a/engines/scumm/smush/rebel/smush_player_ra1.cpp
+++ b/engines/scumm/smush/rebel/smush_player_ra1.cpp
@@ -187,6 +187,13 @@ void SmushPlayerRebel1::resetGameVideoState() {
 	_ra1UseFadeFrame = false;
 }
 
+void SmushPlayerRebel1::initGameVideoState() {
+	// Some RA1 ANMs inherit the current SMUSH palette. SmushPlayer::play()
+	// clears the dirty range before init(), so re-push the palette that was
+	// restored or inherited before this video starts.
+	setDirtyColors(0, 255);
+}
+
 void SmushPlayerRebel1::releaseGameVideoState() {
 	free(_storedFobjData);
 	_storedFobjData = nullptr;
diff --git a/engines/scumm/smush/rebel/smush_player_ra1.h b/engines/scumm/smush/rebel/smush_player_ra1.h
index 2188e206c62..be70db7b950 100644
--- a/engines/scumm/smush/rebel/smush_player_ra1.h
+++ b/engines/scumm/smush/rebel/smush_player_ra1.h
@@ -36,6 +36,7 @@ protected:
 	void initGamePlayerFields() override;
 	void destroyGamePlayerFields() override;
 	void resetGameVideoState() override;
+	void initGameVideoState() override;
 	void releaseGameVideoState() override;
 	bool handleGameFetch(int32 subSize, Common::SeekableReadStream &b) override;
 	bool handleGameTextResource(uint32 subType, int32 subSize, Common::SeekableReadStream &b) override;


Commit: c741d304f3e00c9bb36f4c36d4a1627c9c767434
    https://github.com/scummvm/scummvm/commit/c741d304f3e00c9bb36f4c36d4a1627c9c767434
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-06T10:04:04+02:00

Commit Message:
SCUMM: RA1: simplify 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/insane/rebel1/runlevels.cpp


diff --git a/engines/scumm/insane/rebel1/iact.cpp b/engines/scumm/insane/rebel1/iact.cpp
index 4ae3706e857..2a2501402e6 100644
--- a/engines/scumm/insane/rebel1/iact.cpp
+++ b/engines/scumm/insane/rebel1/iact.cpp
@@ -50,6 +50,14 @@ inline int16 scaleRebel1CenteredMouseAxis(int16 value, int16 center, int axisMax
 	return (int16)CLIP<int32>(((int32)(value - center) * axisMax) / center, -axisMax, axisMax);
 }
 
+inline int16 stepRebel1Op0BReticleAxis(int axisValue) {
+	if (axisValue >= 0)
+		return (int16)(axisValue >> 4);
+
+	// The 3DO ARM code integrates the standard control-pad path with ASR #4.
+	return (int16)-((-axisValue + 15) >> 4);
+}
+
 int16 shapeRebel1Op0BGamepadAxis(int16 inputValue, int16 axisMax) {
 	const int axis = CLIP<int>(inputValue, -axisMax, axisMax);
 	const int absAxis = ABS(axis);
@@ -75,16 +83,11 @@ int16 shapeRebel1Op0BGamepadAxis(int16 inputValue, int16 axisMax) {
 
 const int16 kRA1Op09AimXScale[5] = { 0, 44, 88, 128, 165 };
 const int16 kRA1Op09AimYScale[5] = { 256, 252, 240, 221, 196 };
-const int kRA1DosMouseCenterX = 0x140;
-const int kRA1DosMouseCenterY = 100;
-const int kRA1DosMouseSafeLeft = 0x0AA;
-const int kRA1DosMouseSafeRight = 0x1D6;
-const int kRA1DosMouseSafeTop = 0x32;
-const int kRA1DosMouseSafeBottom = 0x96;
-const int kRA1DosFlightMouseMaxX = kRA1DosMouseCenterX >> 2;
-const int kRA1DosFlightMouseMaxY = kRA1DosMouseCenterY >> 1;
-const int kRA1EnhancedFlightDirectMaxX = (kRA1DosFlightMouseMaxX * 4) / 5;
-const int kRA1EnhancedFlightDirectMaxY = (kRA1DosFlightMouseMaxY * 4) / 5;
+const int kRA1CenteredAxisMax = 127;
+const int kRA1Op0BVerticalAxisMax = 100;
+const int kRA1EnhancedFlightDirectMaxX = 64;
+const int kRA1EnhancedFlightDirectMaxY = 40;
+const int kRA1ControlPadAxisStep = 0x1E;
 
 // Level 15 final approach 0x5D damage/event codes consumed by
 // RunLevel1GameLoop. The latch stores the raw GAME parameter; no translation is
@@ -834,11 +837,10 @@ void InsaneRebel1::updateFlightVariantCursor() {
 }
 
 // preprocessMouseAxes ??? FUN_231BE (0x231BE) centered-axis output law, adapted to
-// ScummVM's absolute 320x200 mouse space.
-// Original-input mode preserves the DOS bias/offset persistence and one-frame
-// jump latch from FUN_231BE, plus FUN_23115's DOS mouse recenter behavior.
-// Enhanced controls are ScummVM-only: they bypass the original recentering state
-// and expose a stable absolute centered mouse axis instead.
+// ScummVM's absolute 320x200 mouse space. The old DOS virtual-mouse/recenter
+// control path is intentionally not used. For opcode 0x0B, gamepad input uses
+// the 3DO standard-pad reticle model: axis samples move the reticle, and
+// releasing the pad holds the last reticle position.
 bool InsaneRebel1::isOp0BReticleControlLevel() const {
 	switch (_currentLevel) {
 	case 1:  // Level 2
@@ -859,23 +861,18 @@ bool InsaneRebel1::shouldInvertTouchYSettingForCurrentLevel() const {
 	return isTouchscreenActive() && isOp0BReticleControlLevel();
 }
 
-bool InsaneRebel1::usesRelativeGamepadAimForCurrentLevel() const {
-	return _optEnhancedControls &&
-		(_currentLevel == 3 ||
-		 (_currentLevel == 4 && _levelGameplayPhase == 2) ||
-		 _currentLevel == 9 ||
-		 _currentLevel == 11 ||
-		 _currentLevel == 13);
+bool InsaneRebel1::usesGamepadReticleAimForCurrentFrame() const {
+	return getEffectiveGameOpcode() == 0x0B;
 }
 
-void InsaneRebel1::resetRelativeGamepadAim() {
+void InsaneRebel1::resetGamepadReticleAim() {
 	_gamepadAimAxisX = 0;
 	_gamepadAimAxisY = 0;
 	_gamepadAimActive = false;
 }
 
-bool InsaneRebel1::updateRelativeGamepadAim(int16 &inputX, int16 &inputY, bool *usedJoystick) {
-	if (!usesRelativeGamepadAimForCurrentLevel())
+bool InsaneRebel1::updateGamepadReticleAim(int16 &inputX, int16 &inputY, bool *usedJoystick) {
+	if (!usesGamepadReticleAimForCurrentFrame())
 		return false;
 
 	const int dpadX =
@@ -887,8 +884,10 @@ bool InsaneRebel1::updateRelativeGamepadAim(int16 &inputX, int16 &inputY, bool *
 
 	const int16 analogAxisX = applyRebel1AnalogDeadzone(_joystickAxisX);
 	const int16 analogAxisY = applyRebel1AnalogDeadzone(_joystickAxisY);
-	const int analogX = CLIP<int32>(((int32)analogAxisX * 127) / Common::JOYAXIS_MAX, -127, 127);
-	int analogY = CLIP<int32>((-(int32)analogAxisY * 100) / Common::JOYAXIS_MAX, -100, 100);
+	const int analogX = CLIP<int32>(((int32)analogAxisX * kRA1CenteredAxisMax) / Common::JOYAXIS_MAX,
+		-kRA1CenteredAxisMax, kRA1CenteredAxisMax);
+	int analogY = CLIP<int32>((-(int32)analogAxisY * kRA1Op0BVerticalAxisMax) / Common::JOYAXIS_MAX,
+		-kRA1Op0BVerticalAxisMax, kRA1Op0BVerticalAxisMax);
 
 	if (_optControlsYFlip) {
 		dpadY = -dpadY;
@@ -900,24 +899,22 @@ bool InsaneRebel1::updateRelativeGamepadAim(int16 &inputX, int16 &inputY, bool *
 	bool activeGamepadAim = false;
 
 	if (dpadX || dpadY) {
-		const int kDigitalAimStep = 3;
-		deltaX = dpadX * kDigitalAimStep;
-		deltaY = dpadY * kDigitalAimStep;
+		deltaX = stepRebel1Op0BReticleAxis(dpadX * kRA1ControlPadAxisStep);
+		deltaY = stepRebel1Op0BReticleAxis(dpadY * kRA1ControlPadAxisStep);
 		activeGamepadAim = true;
 	} else if (analogX || analogY) {
-		const int kAnalogAimMaxStep = 8;
-		deltaX = analogX * kAnalogAimMaxStep / 127;
-		deltaY = analogY * kAnalogAimMaxStep / 100;
+		deltaX = stepRebel1Op0BReticleAxis(analogX);
+		deltaY = stepRebel1Op0BReticleAxis(analogY);
 		activeGamepadAim = true;
 	}
 
 	if (activeGamepadAim) {
 		if (!_gamepadAimActive) {
-			_gamepadAimAxisX = CLIP<int16>(_avgInputX, -127, 127);
-			_gamepadAimAxisY = CLIP<int16>((int16)-_avgInputY, -100, 100);
+			_gamepadAimAxisX = CLIP<int16>(_avgInputX, -kRA1CenteredAxisMax, kRA1CenteredAxisMax);
+			_gamepadAimAxisY = CLIP<int16>((int16)-_avgInputY, -kRA1Op0BVerticalAxisMax, kRA1Op0BVerticalAxisMax);
 		}
-		_gamepadAimAxisX = CLIP<int16>((int16)(_gamepadAimAxisX + deltaX), -127, 127);
-		_gamepadAimAxisY = CLIP<int16>((int16)(_gamepadAimAxisY + deltaY), -100, 100);
+		_gamepadAimAxisX = CLIP<int16>((int16)(_gamepadAimAxisX + deltaX), -kRA1CenteredAxisMax, kRA1CenteredAxisMax);
+		_gamepadAimAxisY = CLIP<int16>((int16)(_gamepadAimAxisY + deltaY), -kRA1Op0BVerticalAxisMax, kRA1Op0BVerticalAxisMax);
 		_gamepadAimActive = true;
 	}
 
@@ -926,8 +923,7 @@ bool InsaneRebel1::updateRelativeGamepadAim(int16 &inputX, int16 &inputY, bool *
 
 	if (usedJoystick)
 		*usedJoystick = true;
-	_activeInputSource = kInputSourceJoystickRelative;
-	_mouseVirtualValid = false;
+	_activeInputSource = kInputSourceJoystickReticle;
 	inputX = _gamepadAimAxisX;
 	inputY = _gamepadAimAxisY;
 	return true;
@@ -937,11 +933,10 @@ void InsaneRebel1::preprocessMouseAxes(int16 &inputX, int16 &inputY, bool *usedJ
 	if (usedJoystick)
 		*usedJoystick = false;
 
-	if (_mouseRecentering)
+	if (updateGamepadReticleAim(inputX, inputY, usedJoystick))
 		return;
 
-	if (updateRelativeGamepadAim(inputX, inputY, usedJoystick))
-		return;
+	const bool directFlightInput = getEffectiveGameOpcode() == 0x07;
 
 	const int16 analogAxisX = applyRebel1AnalogDeadzone(_joystickAxisX);
 	const int16 analogAxisY = applyRebel1AnalogDeadzone(_joystickAxisY);
@@ -958,14 +953,12 @@ void InsaneRebel1::preprocessMouseAxes(int16 &inputX, int16 &inputY, bool *usedJ
 		if (usedJoystick)
 			*usedJoystick = true;
 
-		const bool enhancedFlightInput = _optEnhancedControls && getEffectiveGameOpcode() == 0x07;
-		inputX = joyX * (enhancedFlightInput ? kRA1EnhancedFlightDirectMaxX : 127);
-		inputY = joyY * (enhancedFlightInput ? kRA1EnhancedFlightDirectMaxY : 127);
+		inputX = joyX * kRA1ControlPadAxisStep;
+		inputY = joyY * kRA1ControlPadAxisStep;
 
 		if (_optControlsYFlip)
 			inputY = -inputY;
 
-		_mouseVirtualValid = false;
 		return;
 	}
 
@@ -974,9 +967,8 @@ void InsaneRebel1::preprocessMouseAxes(int16 &inputX, int16 &inputY, bool *usedJ
 			*usedJoystick = true;
 
 		if (analogAxisX != 0 || analogAxisY != 0) {
-			const bool enhancedFlightInput = _optEnhancedControls && getEffectiveGameOpcode() == 0x07;
-			const int axisMaxX = enhancedFlightInput ? kRA1EnhancedFlightDirectMaxX : 127;
-			const int axisMaxY = enhancedFlightInput ? kRA1EnhancedFlightDirectMaxY : 127;
+			const int axisMaxX = directFlightInput ? kRA1EnhancedFlightDirectMaxX : kRA1CenteredAxisMax;
+			const int axisMaxY = directFlightInput ? kRA1EnhancedFlightDirectMaxY : kRA1CenteredAxisMax;
 			inputX = CLIP<int32>(((int32)analogAxisX * axisMaxX) / Common::JOYAXIS_MAX, -axisMaxX, axisMaxX);
 			inputY = CLIP<int32>(((int32)analogAxisY * axisMaxY) / Common::JOYAXIS_MAX, -axisMaxY, axisMaxY);
 		} else {
@@ -987,7 +979,6 @@ void InsaneRebel1::preprocessMouseAxes(int16 &inputX, int16 &inputY, bool *usedJ
 		if (_optControlsYFlip)
 			inputY = -inputY;
 
-		_mouseVirtualValid = false;
 		return;
 	}
 
@@ -1001,156 +992,26 @@ void InsaneRebel1::preprocessMouseAxes(int16 &inputX, int16 &inputY, bool *usedJ
 		if (_optControlsYFlip)
 			inputY = -inputY;
 
-		_mouseVirtualValid = false;
 		return;
 	}
 
 	int16 logicalX = (int16)CLIP<int>(_vm->_mouse.x, 0, 319);
 	int16 logicalY = (int16)CLIP<int>(_vm->_mouse.y, 0, 199);
 
-	// Touchscreens cannot support the DOS recenter/warp model, so use the
-	// non-warping absolute axis path even when original input style is selected.
-	if (_optEnhancedControls || isTouchscreenActive()) {
-		_mouseVirtualValid = false;
-		_mouseBiasLatch = false;
-		_mouseBiasX = 0;
-		_mouseBiasY = 0;
-		_mousePrevBiasX = 0;
-		_mousePrevBiasY = 0;
-
-		if (getEffectiveGameOpcode() == 0x07) {
-			// Direct flight input lacks the DOS recenter path's offset decay, so keep
-			// held-edge steering below the raw DOS full-deflection bias.
-			inputX = scaleRebel1CenteredMouseAxis(logicalX, kRA1CenterX, kRA1EnhancedFlightDirectMaxX);
-			inputY = scaleRebel1CenteredMouseAxis(logicalY, kRA1CenterY, kRA1EnhancedFlightDirectMaxY);
-		} else {
-			inputX = (int16)CLIP<int32>(((int32)(logicalX - kRA1CenterX) * 127) / kRA1CenterX, -127, 127);
-			inputY = (int16)CLIP<int32>(((int32)(logicalY - kRA1CenterY) * 127) / kRA1CenterY, -127, 127);
-		}
-
-		// These handlers negate DAT_756E internally, so direct touch needs the
-		// opposite baseline while the menu option still toggles the result.
-		bool flipY = _optControlsYFlip;
-		if (shouldInvertTouchYSettingForCurrentLevel())
-			flipY = !flipY;
-		if (flipY)
-			inputY = -inputY;
-
-		return;
-	}
-
-	int16 rawX = (int16)(logicalX << 1);
-	int16 rawY = logicalY;
-
-	if (!_mouseVirtualValid) {
-		_mouseVirtualRawX = rawX;
-		_mouseVirtualRawY = rawY;
-		_mouseVirtualValid = true;
+	if (directFlightInput) {
+		inputX = scaleRebel1CenteredMouseAxis(logicalX, kRA1CenterX, kRA1EnhancedFlightDirectMaxX);
+		inputY = scaleRebel1CenteredMouseAxis(logicalY, kRA1CenterY, kRA1EnhancedFlightDirectMaxY);
 	} else {
-		_mouseVirtualRawX = (int16)CLIP<int>(
-			_mouseVirtualRawX + ((logicalX - _mouseVirtualPrevLogicalX) << 1),
-			-32768, 32767);
-		_mouseVirtualRawY = (int16)CLIP<int>(
-			_mouseVirtualRawY + (logicalY - _mouseVirtualPrevLogicalY),
-			-32768, 32767);
-	}
-	_mouseVirtualPrevLogicalX = logicalX;
-	_mouseVirtualPrevLogicalY = logicalY;
-
-	rawX = _mouseVirtualRawX;
-	rawY = _mouseVirtualRawY;
-
-	if (rawX < kRA1DosMouseSafeLeft || rawX > kRA1DosMouseSafeRight ||
-		rawY < kRA1DosMouseSafeTop || rawY > kRA1DosMouseSafeBottom) {
-		_mouseOffsetX = (int16)CLIP<int>(
-			(int)_mouseOffsetX + rawX - kRA1DosMouseCenterX, -32768, 32767);
-		_mouseOffsetY = (int16)CLIP<int>(
-			(int)_mouseOffsetY + rawY - kRA1DosMouseCenterY, -32768, 32767);
-		rawX = kRA1DosMouseCenterX;
-		rawY = kRA1DosMouseCenterY;
-		_mouseVirtualRawX = rawX;
-		_mouseVirtualRawY = rawY;
-		_mouseVirtualPrevLogicalX = kRA1CenterX;
-		_mouseVirtualPrevLogicalY = kRA1CenterY;
-		warpGameplayMouseNow(kRA1CenterX, kRA1CenterY);
-		debug(2, "RA1 original input virtual recenter: offset=(%d,%d) mouse=(%d,%d)",
-			_mouseOffsetX, _mouseOffsetY, logicalX, logicalY);
-	}
-
-	int16 biasX = (int16)((rawX + _mouseOffsetX - kRA1DosMouseCenterX) >> 2);
-	int16 biasY = (int16)((rawY + _mouseOffsetY - kRA1DosMouseCenterY) >> 1);
-
-	if (biasY < 0x65) {
-		const bool largeJump =
-			(_mousePrevBiasX + 0x14 < biasX) ||
-			(_mousePrevBiasY + 0x14 < biasY) ||
-			(biasX < _mousePrevBiasX - 0x14) ||
-			(biasY < _mousePrevBiasY - 0x14);
-		if (largeJump) {
-			if (!_mouseBiasLatch) {
-				biasX = _mousePrevBiasX;
-				biasY = _mousePrevBiasY;
-				_mouseBiasLatch = true;
-			}
-		} else {
-			_mouseBiasLatch = false;
-		}
-	} else {
-		biasX = _mousePrevBiasX;
-		biasY = _mousePrevBiasY;
-		_mouseBiasLatch = true;
-	}
-
-	_mouseBiasX = biasX;
-	_mouseBiasY = biasY;
-	_mousePrevBiasX = biasX;
-	_mousePrevBiasY = biasY;
-
-	int accumX = rawX + _mouseOffsetX;
-	if (accumX < 0xC0)
-		_mouseOffsetX = (int16)(0xC0 - rawX);
-	else if (accumX > 0x1C0)
-		_mouseOffsetX = (int16)(0x1C0 - rawX);
-
-	int accumY = rawY + _mouseOffsetY;
-	if (accumY < -0x1C)
-		_mouseOffsetY = (int16)(-0x1C - rawY);
-	else if (accumY > 0xE4)
-		_mouseOffsetY = (int16)(0xE4 - rawY);
-
-	accumX = rawX + _mouseOffsetX;
-	if (accumX < 0x145) {
-		if (accumX < 0x142) {
-			if (accumX < 0x13C)
-				_mouseOffsetX += 4;
-			else if (accumX < 0x13F)
-				_mouseOffsetX += 1;
-		} else {
-			_mouseOffsetX -= 1;
-		}
-	} else {
-		_mouseOffsetX -= 4;
+		inputX = (int16)CLIP<int32>(((int32)(logicalX - kRA1CenterX) * kRA1CenteredAxisMax) / kRA1CenterX,
+			-kRA1CenteredAxisMax, kRA1CenteredAxisMax);
+		inputY = (int16)CLIP<int32>(((int32)(logicalY - kRA1CenterY) * kRA1CenteredAxisMax) / kRA1CenterY,
+			-kRA1CenteredAxisMax, kRA1CenteredAxisMax);
 	}
 
-	accumY = rawY + _mouseOffsetY;
-	if (accumY < 0x69) {
-		if (accumY < 0x66) {
-			if (accumY < 0x60)
-				_mouseOffsetY += 4;
-			else if (accumY < 99)
-				_mouseOffsetY += 1;
-		} else {
-			_mouseOffsetY -= 1;
-		}
-	} else {
-		_mouseOffsetY -= 4;
-	}
-
-	inputX = CLIP<int16>(biasX, -0xA0, 0xA0);
-	inputY = CLIP<int16>(biasY, -127, 127);
-
-	// Controls Y-flip option (DAT_22be in original)
-	if (_optControlsYFlip)
+	bool flipY = _optControlsYFlip;
+	if (shouldInvertTouchYSettingForCurrentLevel())
+		flipY = !flipY;
+	if (flipY)
 		inputY = -inputY;
 }
 
@@ -1194,8 +1055,8 @@ void InsaneRebel1::updateShipPhysics() {
 	preprocessMouseAxes(inputX, inputY, &usedJoystick);
 	const int16 rawInputX = inputX;
 	const int16 rawInputY = inputY;
-	inputX = CLIP<int16>(inputX, -127, 127);
-	inputY = CLIP<int16>(inputY, -127, 127);
+	inputX = CLIP<int16>(inputX, -kRA1CenteredAxisMax, kRA1CenteredAxisMax);
+	inputY = CLIP<int16>(inputY, -kRA1CenteredAxisMax, kRA1CenteredAxisMax);
 	const char *inputSourceName = "mouse";
 	if (_activeInputSource == kInputSourceJoystickAnalog)
 		inputSourceName = "joystick-analog";
@@ -1214,8 +1075,7 @@ void InsaneRebel1::updateShipPhysics() {
 
 	// --- Step 4: Position accumulator deltas ---
 	// X delta: drift + slide coupling - cross-coupling
-	const bool originalTurbulence = !_optEnhancedControls;
-	int32 rng = originalTurbulence ? (int32)_vm->_rnd.getRandomNumber(199) : 100;  // RandScaleByte(200), centered at 100
+	int32 rng = 100;  // RandScaleByte(200), centered at 100
 	int32 crossTermX;
 	if (_liftSmooth < 0)
 		crossTermX = ((int32)_tuning.lift * _liftSmooth * _rollAccum) >> 11;
@@ -1293,7 +1153,7 @@ void InsaneRebel1::updateShipPhysics() {
 
 	debugC(DEBUG_INSANE, "RA1 ship input: frame=%d source=%s controls=%s turbulence=%d usedJoystick=%d raw=(%d,%d) clipped=(%d,%d) storedAxis=(%d,%d) actionState(L,R,U,D)=(%d,%d,%d,%d) roll=%d lift=%d pos=(%d,%d) view=(%d,%d) dir=%d level=%d mode=%d opcode=0x%X",
 		_gameCounter, inputSourceName,
-		_optEnhancedControls ? "enhanced" : "original", originalTurbulence, usedJoystick,
+		"enhanced", 0, usedJoystick,
 		rawInputX, rawInputY, inputX, inputY,
 		_joystickAxisX, _joystickAxisY,
 		_vm->getActionState(kScummActionInsaneLeft),
@@ -1521,12 +1381,12 @@ void InsaneRebel1::updateTurretPhysics() {
 		int16 inputY = 0;
 		bool usedJoystick = false;
 		preprocessMouseAxes(inputX, inputY, &usedJoystick);
-		inputX = CLIP<int16>(inputX, -127, 127);
-		inputY = CLIP<int16>(inputY, -127, 127);
+		inputX = CLIP<int16>(inputX, -kRA1CenteredAxisMax, kRA1CenteredAxisMax);
+		inputY = CLIP<int16>(inputY, -kRA1CenteredAxisMax, kRA1CenteredAxisMax);
 		const int16 rawInputX = inputX;
 		const int16 rawInputY = inputY;
 
-		if (usedJoystick && _optEnhancedControls && _flyControlMode == 2) {
+		if (usedJoystick && _flyControlMode == 2) {
 			// ScummVM-only concession for Level 1 part 2. The original 0x08 handler
 			// uses raw axes directly; do not damp Level 13's surface controls.
 			inputX /= 2;
@@ -1535,7 +1395,7 @@ void InsaneRebel1::updateTurretPhysics() {
 
 		debugC(DEBUG_INSANE, "RA1 turret input: source=%s controls=%s mouse=(%d,%d) actions(L,R,U,D)=(%d,%d,%d,%d) raw=(%d,%d) final=(%d,%d) level=%d mode=%d opcode=0x%X",
 			usedJoystick ? "joystick-actions" : "mouse-path",
-			_optEnhancedControls ? "enhanced" : "original",
+			"enhanced",
 			_vm->_mouse.x, _vm->_mouse.y,
 			_vm->getActionState(kScummActionInsaneLeft),
 			_vm->getActionState(kScummActionInsaneRight),
@@ -1627,9 +1487,9 @@ void InsaneRebel1::updateScreenFlashPalette() {
 // Ship position = averaged input + center offset.
 // Viewport = second history buffer for smooth camera scrolling.
 void InsaneRebel1::updateGameOp0BPhysics() {
-	// Original FUN_1CDA7 uses the full 10-sample history. Enhanced controls keep
-	// the same pipeline but average fewer samples for responsiveness.
-	const int gameOp0BSmoothWindow = _optEnhancedControls ? 2 : kInputHistorySize;
+	// Enhanced controls keep the original 0x0B pipeline but average fewer
+	// samples for responsiveness.
+	const int gameOp0BSmoothWindow = 2;
 
 	// RA1 FUN_1B297-style per-frame latches for 0x0B sections:
 	//   0x5D latch 0xFFFF -> bit 0x40 (scripted obstacle/contact)
@@ -1752,7 +1612,7 @@ void InsaneRebel1::updateGameOp0BPhysics() {
 	bool usedJoystick = false;
 	preprocessMouseAxes(inputX, inputY, &usedJoystick);
 	inputX = CLIP<int16>(inputX, -0xA0, 0xA0);
-	inputY = CLIP<int16>(inputY, -100, 100);
+	inputY = CLIP<int16>(inputY, -kRA1Op0BVerticalAxisMax, kRA1Op0BVerticalAxisMax);
 	const int16 rawInputX = inputX;
 	const int16 rawInputY = inputY;
 	const bool op0BAnalogSmoothing = (_activeInputSource == kInputSourceJoystickAnalog);
@@ -1762,25 +1622,25 @@ void InsaneRebel1::updateGameOp0BPhysics() {
 		inputSourceName = "joystick-analog";
 	else if (_activeInputSource == kInputSourceJoystickDigital)
 		inputSourceName = "joystick-dpad";
-	else if (_activeInputSource == kInputSourceJoystickRelative)
-		inputSourceName = "joystick-relative";
+	else if (_activeInputSource == kInputSourceJoystickReticle)
+		inputSourceName = "joystick-reticle";
 
-	const bool relativeGamepadReticle = usedJoystick && _activeInputSource == kInputSourceJoystickRelative;
-	const bool preciseGamepadReticle = usedJoystick && _optEnhancedControls &&
-		isOp0BReticleControlLevel() && !relativeGamepadReticle;
+	const bool relativeGamepadReticle = usedJoystick && _activeInputSource == kInputSourceJoystickReticle;
+	const bool preciseGamepadReticle = usedJoystick && isOp0BReticleControlLevel() &&
+		!relativeGamepadReticle;
 
-	if (usedJoystick && _optEnhancedControls && !relativeGamepadReticle) {
+	if (usedJoystick && !relativeGamepadReticle) {
 		// The 0x0B first-person handler is shared by multiple RA1 stages. Smooth
 		// analog stick input over time and shape the affected gamepad levels with
 		// a low-gain linear center plus an accelerating outer range.
 		if (op0BAnalogSmoothing) {
 			if (preciseGamepadReticle) {
-				inputX = shapeRebel1Op0BGamepadAxis(inputX, 127);
-				inputY = shapeRebel1Op0BGamepadAxis(inputY, 100);
+				inputX = shapeRebel1Op0BGamepadAxis(inputX, kRA1CenteredAxisMax);
+				inputY = shapeRebel1Op0BGamepadAxis(inputY, kRA1Op0BVerticalAxisMax);
 			}
 			const int analogRampDivisor = preciseGamepadReticle ? 20 : 10;
-			inputX = smoothRebel1Op0BAnalogInput(inputX, _level2JoystickFilteredX, 127, analogRampDivisor);
-			inputY = smoothRebel1Op0BAnalogInput(inputY, _level2JoystickFilteredY, 100, analogRampDivisor);
+			inputX = smoothRebel1Op0BAnalogInput(inputX, _level2JoystickFilteredX, kRA1CenteredAxisMax, analogRampDivisor);
+			inputY = smoothRebel1Op0BAnalogInput(inputY, _level2JoystickFilteredY, kRA1Op0BVerticalAxisMax, analogRampDivisor);
 		} else {
 			_level2JoystickFilteredX = 0;
 			_level2JoystickFilteredY = 0;
@@ -1796,7 +1656,7 @@ void InsaneRebel1::updateGameOp0BPhysics() {
 	debugC(DEBUG_INSANE, "RA1 GAME 0x0B input: frame=%d source=%s controls=%s window=%d precisionPad=%d view=(%d,%d) health=%d prevFlags=0x%02x axis=(%d,%d) mouse=(%d,%d) actions(L,R,U,D)=(%d,%d,%d,%d) raw=(%d,%d) final=(%d,%d) level=%d opcode=0x%X",
 		_gameCounter,
 		inputSourceName,
-		_optEnhancedControls ? "enhanced" : "original",
+		"enhanced",
 		gameOp0BSmoothWindow,
 		preciseGamepadReticle ? 1 : 0,
 		_perspectiveX, _perspectiveY,
@@ -2149,22 +2009,9 @@ void InsaneRebel1::handleGameOpcode5EReset(uint32 param1) {
 	_inputAxisDeltaX = 0;
 	_avgInputX = 0;
 	_avgInputY = 0;
-	_mouseOffsetX = 0;
-	_mouseOffsetY = 0;
-	_mouseBiasX = 0;
-	_mouseBiasY = 0;
-	_mousePrevBiasX = 0;
-	_mousePrevBiasY = 0;
-	_mouseBiasLatch = false;
-	_mouseRecentering = false;
-	_mouseVirtualRawX = kRA1DosMouseCenterX;
-	_mouseVirtualRawY = kRA1DosMouseCenterY;
-	_mouseVirtualPrevLogicalX = kRA1CenterX;
-	_mouseVirtualPrevLogicalY = kRA1CenterY;
-	_mouseVirtualValid = false;
 	_level2JoystickFilteredX = 0;
 	_level2JoystickFilteredY = 0;
-	resetRelativeGamepadAim();
+	resetGamepadReticleAim();
 
 	_playerFired = false;
 	_fireCooldown = 0;
diff --git a/engines/scumm/insane/rebel1/menu.cpp b/engines/scumm/insane/rebel1/menu.cpp
index 9b7f374073e..02be2e39935 100644
--- a/engines/scumm/insane/rebel1/menu.cpp
+++ b/engines/scumm/insane/rebel1/menu.cpp
@@ -469,11 +469,11 @@ bool InsaneRebel1::handleMenuCommand(RA1MenuCommand command) {
 			_optionsSel = (_optionsSel + 1) % kOptionsItemCount;
 			return true;
 		case kRA1MenuCommandLeft:
-			if (_optionsSel == 7)
+			if (_optionsSel == 6)
 				setRebel1Volume(_vm, _optVolume, -5);
 			return true;
 		case kRA1MenuCommandRight:
-			if (_optionsSel == 7)
+			if (_optionsSel == 6)
 				setRebel1Volume(_vm, _optVolume, 5);
 			return true;
 		case kRA1MenuCommandCancel:
@@ -674,8 +674,8 @@ bool InsaneRebel1::notifyEvent(const Common::Event &event) {
 		_gameplayMouseSettleUntil = 0;
 	}
 
-	if (event.type == Common::EVENT_MOUSEMOVE && !_mouseRecentering) {
-		if (_gamepadAimActive && usesRelativeGamepadAimForCurrentLevel() &&
+	if (event.type == Common::EVENT_MOUSEMOVE) {
+		if (_gamepadAimActive && usesGamepadReticleAimForCurrentFrame() &&
 				_interactiveVideoActive && !_menuActive &&
 				(event.relMouse.x != 0 || event.relMouse.y != 0))
 			_gamepadAimActive = false;
@@ -685,7 +685,7 @@ bool InsaneRebel1::notifyEvent(const Common::Event &event) {
 	// Android direct touch reports taps as left-clicks. Treat those as mouse input
 	// during gameplay so tap-to-aim-and-fire works even if a joystick event was last.
 	if (isTouchscreenActive() && _interactiveVideoActive && !_menuActive &&
-			event.type == Common::EVENT_LBUTTONDOWN && !_mouseRecentering)
+			event.type == Common::EVENT_LBUTTONDOWN)
 		_activeInputSource = kInputSourceMouse;
 
 	// ScummVM-exclusive feature: mouse navigation/clicking of the RA1 front-end menus.
@@ -1009,8 +1009,7 @@ void InsaneRebel1::renderOptionsOverlay(byte *dst, int pitch, int width, int hei
 		_optRookieOneFemale ? "ROOKIE1 IS FEMALE" : "ROOKIE1 IS MALE",
 		_optMusicEnabled  ? "MUSIC IS ON"             : "MUSIC IS OFF",
 		_optSfxEnabled    ? "SFX AND VOICE ARE ON"    : "SFX AND VOICE ARE OFF",
-		_optTextEnabled   ? "DIALOGUE TEXT IS ON"      : "DIALOGUE TEXT IS OFF",
-		_optEnhancedControls ? "INPUT STYLE ENHANCED" : "INPUT STYLE ORIGINAL",
+		_optTextEnabled   ? "DIALOGUE TEXT IS ON"     : "DIALOGUE TEXT IS OFF",
 		_optControlsYFlip ? "Y AXIS IS INVERTED"      : "Y AXIS IS NORMAL",
 		volLine,
 		diffLine
@@ -1302,15 +1301,12 @@ void InsaneRebel1::runOptionsMenu() {
 				_optTextEnabled = !ConfMan.getBool("subtitles");
 				ConfMan.setBool("subtitles", _optTextEnabled);
 				break;
-			case 5: // Toggle enhanced/original input style
-				_optEnhancedControls = !_optEnhancedControls;
-				break;
-			case 6: // Toggle Y-flip controls
+			case 5: // Toggle Y-flip controls
 				_optControlsYFlip = !_optControlsYFlip;
 				break;
-			case 7: // Volume ??? adjusted via left/right in notifyEvent
+			case 6: // Volume ??? adjusted via left/right in notifyEvent
 				break;
-			case 8: // Cycle difficulty
+			case 7: // Cycle difficulty
 				_difficulty = (_difficulty + 1) % 3;
 				loadTuningForLevel(0);
 				break;
diff --git a/engines/scumm/insane/rebel1/rebel.cpp b/engines/scumm/insane/rebel1/rebel.cpp
index 004840569e1..034d8cb2bfc 100644
--- a/engines/scumm/insane/rebel1/rebel.cpp
+++ b/engines/scumm/insane/rebel1/rebel.cpp
@@ -258,19 +258,6 @@ InsaneRebel1::InsaneRebel1(ScummEngine_v7 *scumm) : Insane(), _vm(scumm) {
 	_inputAxisDeltaX = 0;
 	_avgInputX = 0;
 	_avgInputY = 0;
-	_mouseOffsetX = 0;
-	_mouseOffsetY = 0;
-	_mouseBiasX = 0;
-	_mouseBiasY = 0;
-	_mousePrevBiasX = 0;
-	_mousePrevBiasY = 0;
-	_mouseBiasLatch = false;
-	_mouseRecentering = false;
-	_mouseVirtualRawX = 0x140;
-	_mouseVirtualRawY = 100;
-	_mouseVirtualPrevLogicalX = kRA1CenterX;
-	_mouseVirtualPrevLogicalY = kRA1CenterY;
-	_mouseVirtualValid = false;
 	_joystickAxisX = 0;
 	_joystickAxisY = 0;
 	_lastJoystickAxisEventTime = 0;
@@ -365,7 +352,6 @@ InsaneRebel1::InsaneRebel1(ScummEngine_v7 *scumm) : Insane(), _vm(scumm) {
 	// game and the in-game DIALOGUE TEXT menu label reflect it. The menu toggle writes the
 	// same "subtitles" key, and ra1HandleText() gates rendering on it.
 	_optTextEnabled = ConfMan.getBool("subtitles");
-	_optEnhancedControls = true;
 	_optControlsYFlip = false;
 	_optVolume = _vm->_mixer->getVolumeForSoundType(Audio::Mixer::kPlainSoundType) * 127 / Audio::Mixer::kMaxChannelVolume;
 
diff --git a/engines/scumm/insane/rebel1/rebel.h b/engines/scumm/insane/rebel1/rebel.h
index 6622e884873..4d3675ee358 100644
--- a/engines/scumm/insane/rebel1/rebel.h
+++ b/engines/scumm/insane/rebel1/rebel.h
@@ -227,9 +227,9 @@ private:
 	void getCollisionShipCenter(int16 &x, int16 &y) const;
 	bool isOp0BReticleControlLevel() const;
 	bool shouldInvertTouchYSettingForCurrentLevel() const;
-	bool usesRelativeGamepadAimForCurrentLevel() const;
-	void resetRelativeGamepadAim();
-	bool updateRelativeGamepadAim(int16 &inputX, int16 &inputY, bool *usedJoystick);
+	bool usesGamepadReticleAimForCurrentFrame() const;
+	void resetGamepadReticleAim();
+	bool updateGamepadReticleAim(int16 &inputX, int16 &inputY, bool *usedJoystick);
 	void preprocessMouseAxes(int16 &inputX, int16 &inputY, bool *usedJoystick = nullptr);
 	void rebuildProjectionTable(int16 curveStep, int16 curveExtent);
 	void resetProjectionTable();
@@ -357,19 +357,6 @@ private:
 	int16 _inputAxisDeltaX; // Current 0x0B horizontal input sample, before history averaging
 	int16 _avgInputX;    // smoothed horizontal input (clamped to [-0xA0, 0xA0])
 	int16 _avgInputY;    // smoothed vertical input (clamped to [-0x46, 0x41])
-	int16 _mouseOffsetX; // 0x9762-style accumulated recenter offset in DOS 640-space
-	int16 _mouseOffsetY; // 0x9760-style accumulated recenter offset in DOS 200-space
-	int16 _mouseBiasX;   // 0x9774: current preprocessed horizontal bias
-	int16 _mouseBiasY;   // 0x9772: current preprocessed vertical bias
-	int16 _mousePrevBiasX; // 0x9770: previous-frame biasX
-	int16 _mousePrevBiasY; // 0x976E: previous-frame biasY
-	bool _mouseBiasLatch;  // 0x4486: one-frame large-jump latch
-	bool _mouseRecentering; // 0x976D: suppress recursive updates during warp
-	int16 _mouseVirtualRawX; // Virtual DOS mouse X used by original-input recentering
-	int16 _mouseVirtualRawY; // Virtual DOS mouse Y used by original-input recentering
-	int16 _mouseVirtualPrevLogicalX;
-	int16 _mouseVirtualPrevLogicalY;
-	bool _mouseVirtualValid;
 	int16 _joystickAxisX;   // Rebel-specific left-stick X captured from keymapper axis events
 	int16 _joystickAxisY;   // Rebel-specific left-stick Y captured from keymapper axis events
 	uint32 _lastJoystickAxisEventTime;
@@ -384,7 +371,7 @@ private:
 		kInputSourceMouse,
 		kInputSourceJoystickAnalog,
 		kInputSourceJoystickDigital,
-		kInputSourceJoystickRelative
+		kInputSourceJoystickReticle
 	};
 	InputSource _activeInputSource;
 
@@ -581,9 +568,9 @@ private:
 	int _menuFrameCounter;
 
 	// Options submenu state ??? RunGameOptionsMenu (0x14B42)
-	static const int kOptionsItemCount = 9;
+	static const int kOptionsItemCount = 8;
 	bool _optionsActive;     // True when showing options instead of main menu
-	int _optionsSel;         // 0..8 selected option row
+	int _optionsSel;         // 0..7 selected option row
 	bool _levelSelectActive; // True when showing level-select submenu
 	int _levelSelectSel;     // 0=Level1 ... N-1=Back
 	int _startLevel;         // 1-based start level for "Start New Game"
@@ -593,7 +580,6 @@ private:
 	bool _optMusicEnabled;    // DAT_22b7: music on/off
 	bool _optSfxEnabled;      // DAT_22b8: sfx+voice on/off
 	bool _optTextEnabled;     // DAT_22b9: dialogue text on/off
-	bool _optEnhancedControls; // ScummVM option: current responsive controls vs original control law
 	bool _optControlsYFlip;   // DAT_22be: Y-axis inversion
 	int  _optVolume;          // DAT_22c1: master volume 0..127
 
diff --git a/engines/scumm/insane/rebel1/runlevels.cpp b/engines/scumm/insane/rebel1/runlevels.cpp
index cb6dd67e766..2bd976587ad 100644
--- a/engines/scumm/insane/rebel1/runlevels.cpp
+++ b/engines/scumm/insane/rebel1/runlevels.cpp
@@ -248,7 +248,7 @@ void InsaneRebel1::resetLevelInputHistory(bool resetAxisDeltaX) {
 		_inputAxisDeltaX = 0;
 	_avgInputX = 0;
 	_avgInputY = 0;
-	resetRelativeGamepadAim();
+	resetGamepadReticleAim();
 }
 
 void InsaneRebel1::resetLevelAttemptState(int16 flyControlMode, int16 gameplayPhase,
@@ -1702,7 +1702,7 @@ void InsaneRebel1::setupInteractiveVideoState(int32 startFrame) {
 	if (!preserveRuntimeState) {
 		_onFootInitialized = false;  // Reset so each segment triggers counter==0 init
 		resetFrameObjectState();
-		resetRelativeGamepadAim();
+		resetGamepadReticleAim();
 	}
 	_vm->_smushVideoShouldFinish = false;
 	// Route resumes stay in the same gameplay flow in the original executable.
@@ -1781,11 +1781,6 @@ void InsaneRebel1::captureInteractiveVideoInput() {
 			warpGameplayMouseNow(kRA1CenterX, kRA1CenterY);
 			_gameplayMouseSettleUntil = _vm->_system->getMillis() + kRA1GameplayMouseSettleMs;
 		}
-		_mouseVirtualRawX = 0x140;
-		_mouseVirtualRawY = 100;
-		_mouseVirtualPrevLogicalX = kRA1CenterX;
-		_mouseVirtualPrevLogicalY = kRA1CenterY;
-		_mouseVirtualValid = false;
 	} else {
 		_gameplayMouseSettleUntil = 0;
 	}


Commit: 9bbfdda5adc030de46ed381ecb331aceda288f77
    https://github.com/scummvm/scummvm/commit/9bbfdda5adc030de46ed381ecb331aceda288f77
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-06T10:04:04+02:00

Commit Message:
SCUMM: RA1: added rapid fire following 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


diff --git a/engines/scumm/insane/rebel1/iact.cpp b/engines/scumm/insane/rebel1/iact.cpp
index 2a2501402e6..f4c68f3f53d 100644
--- a/engines/scumm/insane/rebel1/iact.cpp
+++ b/engines/scumm/insane/rebel1/iact.cpp
@@ -2015,6 +2015,7 @@ void InsaneRebel1::handleGameOpcode5EReset(uint32 param1) {
 
 	_playerFired = false;
 	_fireCooldown = 0;
+	_rapidFirePhase = 0;
 	memset(_shotSlots, 0, sizeof(_shotSlots));
 	_shotAlternator = 0;
 	_shotSideToggle = false;
@@ -2373,9 +2374,22 @@ void InsaneRebel1::handleGameChunk(int32 subSize, Common::SeekableReadStream &b,
 // processShot ??? FUN_1CCA0 (0x1CCA0). Spawns shot into explosion slot when fired.
 // Called once per frame during interactive rendering.
 void InsaneRebel1::processShot() {
-	if (!_playerFired || _fireCooldown != 0)
+	if (_optRapidFire) {
+		// 3DO FUN_0000c3a4 advances this before testing fire state; a fresh press
+		// fires immediately, while held repeats are gated to phase 0.
+		_rapidFirePhase++;
+		if (_rapidFirePhase > 2)
+			_rapidFirePhase = 0;
+	}
+
+	if (!_playerFired)
 		return;
 
+	if (_fireCooldown != 0) {
+		if (!_optRapidFire || _rapidFirePhase != 0)
+			return;
+	}
+
 	// On-foot mode: only spawn when in aiming stance (dirIndex 11-19) or flags force it.
 	// Original: if (((10 < g_shipDirIndex) && (g_shipDirIndex < 0x14)) || ((DAT_000075fe & 8) != 0))
 	const uint16 effectiveOpcode = getEffectiveGameOpcode();
diff --git a/engines/scumm/insane/rebel1/menu.cpp b/engines/scumm/insane/rebel1/menu.cpp
index 02be2e39935..a6ac4ebd4bc 100644
--- a/engines/scumm/insane/rebel1/menu.cpp
+++ b/engines/scumm/insane/rebel1/menu.cpp
@@ -469,11 +469,11 @@ bool InsaneRebel1::handleMenuCommand(RA1MenuCommand command) {
 			_optionsSel = (_optionsSel + 1) % kOptionsItemCount;
 			return true;
 		case kRA1MenuCommandLeft:
-			if (_optionsSel == 6)
+			if (_optionsSel == 7)
 				setRebel1Volume(_vm, _optVolume, -5);
 			return true;
 		case kRA1MenuCommandRight:
-			if (_optionsSel == 6)
+			if (_optionsSel == 7)
 				setRebel1Volume(_vm, _optVolume, 5);
 			return true;
 		case kRA1MenuCommandCancel:
@@ -1011,6 +1011,7 @@ void InsaneRebel1::renderOptionsOverlay(byte *dst, int pitch, int width, int hei
 		_optSfxEnabled    ? "SFX AND VOICE ARE ON"    : "SFX AND VOICE ARE OFF",
 		_optTextEnabled   ? "DIALOGUE TEXT IS ON"     : "DIALOGUE TEXT IS OFF",
 		_optControlsYFlip ? "Y AXIS IS INVERTED"      : "Y AXIS IS NORMAL",
+		_optRapidFire     ? "RAPID FIRE IS ON"        : "RAPID FIRE IS OFF",
 		volLine,
 		diffLine
 	};
@@ -1304,9 +1305,12 @@ void InsaneRebel1::runOptionsMenu() {
 			case 5: // Toggle Y-flip controls
 				_optControlsYFlip = !_optControlsYFlip;
 				break;
-			case 6: // Volume ??? adjusted via left/right in notifyEvent
+			case 6: // Toggle held-fire shooting
+				_optRapidFire = !_optRapidFire;
 				break;
-			case 7: // Cycle difficulty
+			case 7: // Volume ??? adjusted via left/right in notifyEvent
+				break;
+			case 8: // Cycle difficulty
 				_difficulty = (_difficulty + 1) % 3;
 				loadTuningForLevel(0);
 				break;
diff --git a/engines/scumm/insane/rebel1/rebel.cpp b/engines/scumm/insane/rebel1/rebel.cpp
index 034d8cb2bfc..eb41ddadbe6 100644
--- a/engines/scumm/insane/rebel1/rebel.cpp
+++ b/engines/scumm/insane/rebel1/rebel.cpp
@@ -353,6 +353,7 @@ InsaneRebel1::InsaneRebel1(ScummEngine_v7 *scumm) : Insane(), _vm(scumm) {
 	// same "subtitles" key, and ra1HandleText() gates rendering on it.
 	_optTextEnabled = ConfMan.getBool("subtitles");
 	_optControlsYFlip = false;
+	_optRapidFire = true;
 	_optVolume = _vm->_mixer->getVolumeForSoundType(Audio::Mixer::kPlainSoundType) * 127 / Audio::Mixer::kMaxChannelVolume;
 
 	// Default high scores ??? from DS:0x1D0/0x298/0x2C0
@@ -382,6 +383,7 @@ InsaneRebel1::InsaneRebel1(ScummEngine_v7 *scumm) : Insane(), _vm(scumm) {
 	// Shooting/targeting state
 	_playerFired = false;
 	_fireCooldown = 0;
+	_rapidFirePhase = 0;
 	_gameplayFlags75fe = 0;
 	_gameplayFlags75ff = 0;
 	memset(_shotSlots, 0, sizeof(_shotSlots));
diff --git a/engines/scumm/insane/rebel1/rebel.h b/engines/scumm/insane/rebel1/rebel.h
index 4d3675ee358..6a0ad752c99 100644
--- a/engines/scumm/insane/rebel1/rebel.h
+++ b/engines/scumm/insane/rebel1/rebel.h
@@ -568,9 +568,9 @@ private:
 	int _menuFrameCounter;
 
 	// Options submenu state ??? RunGameOptionsMenu (0x14B42)
-	static const int kOptionsItemCount = 8;
+	static const int kOptionsItemCount = 9;
 	bool _optionsActive;     // True when showing options instead of main menu
-	int _optionsSel;         // 0..7 selected option row
+	int _optionsSel;         // 0..8 selected option row
 	bool _levelSelectActive; // True when showing level-select submenu
 	int _levelSelectSel;     // 0=Level1 ... N-1=Back
 	int _startLevel;         // 1-based start level for "Start New Game"
@@ -581,6 +581,7 @@ private:
 	bool _optSfxEnabled;      // DAT_22b8: sfx+voice on/off
 	bool _optTextEnabled;     // DAT_22b9: dialogue text on/off
 	bool _optControlsYFlip;   // DAT_22be: Y-axis inversion
+	bool _optRapidFire;       // ScummVM option: held fire keeps shooting
 	int  _optVolume;          // DAT_22c1: master volume 0..127
 
 	// High scores / TOP PILOTS display ??? data at DS:0x1D0
@@ -616,7 +617,8 @@ private:
 
 	// Shooting state ??? FUN_1CCA0 (0x1CCA0)
 	bool _playerFired;       // 0x7570: current fire-button state
-	int16 _fireCooldown;     // 0x757C: previous-frame fire-button state (edge gate)
+	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
 	uint16 _gameplayFlags75ff; // 0x75FF: targeting / shot-style flags
 



From noreply at scummvm.org  Sat Jun  6 09:41:35 2026
From: noreply at scummvm.org (bluegr)
Date: Sat,  6 Jun 2026 09:41:35 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 09764a18ed56986582f47a929b3477f988f97827
Message-ID: <20260606094135.C9536700573@ron.scummvm.net>

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:
07a5fb57b8 NANCY: Use a helper method for case insensitive key matching in Nancy10
09764a18ed NANCY: Fix making phone calls within a closeup scene in Nancy10


Commit: 07a5fb57b8740ac3197b91217c243772801bbc55
    https://github.com/scummvm/scummvm/commit/07a5fb57b8740ac3197b91217c243772801bbc55
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-06-06T12:41:24+03:00

Commit Message:
NANCY: Use a helper method for case insensitive key matching in Nancy10

Allows us to remove relevant hacks. Fixes viewing the trunk in Mary's
Gifts.

Changed paths:
    engines/nancy/action/conversation.cpp
    engines/nancy/action/miscrecords.cpp
    engines/nancy/action/soundrecords.cpp
    engines/nancy/ui/cellphonepopup.cpp
    engines/nancy/ui/notebookpopup.cpp
    engines/nancy/util.cpp
    engines/nancy/util.h


diff --git a/engines/nancy/action/conversation.cpp b/engines/nancy/action/conversation.cpp
index 5989b549e1f..4c4a4a609e8 100644
--- a/engines/nancy/action/conversation.cpp
+++ b/engines/nancy/action/conversation.cpp
@@ -185,24 +185,7 @@ void ConversationSound::readTerseCaptionText(Common::SeekableReadStream &stream)
 	const CVTX *convo = (const CVTX *)g_nancy->getEngineData("CONVO");
 	assert(convo);
 
-	if (convo->texts.contains(key)) {
-		_text = convo->texts[key];
-	} else {
-		// Nancy10+ searched convo texts in a key insensitive way, but
-		// the possible permutations involve mainly the last character
-		// being upper or lower case, so just try that before giving up.
-		if (key[key.size() - 1] == toupper(key[key.size() - 1]))
-			key[key.size() - 1] = tolower(key[key.size() - 1]);
-		else
-			key[key.size() - 1] = toupper(key[key.size() - 1]);
-
-		if (convo->texts.contains(key)) {
-			_text = convo->texts[key];
-		} else {
-			warning("Convo key not found: %s", key.c_str());
-			_text = "";
-		}
-	}
+	_text = getTextFromCaseInsensitiveKey(convo->texts, key);
 }
 
 void ConversationSound::readTerseResponseText(Common::SeekableReadStream &stream, ResponseStruct &response) {
@@ -212,7 +195,7 @@ void ConversationSound::readTerseResponseText(Common::SeekableReadStream &stream
 	const CVTX *convo = (const CVTX *)g_nancy->getEngineData("CONVO");
 	assert(convo);
 
-	response.text = convo->texts[key];
+	response.text = getTextFromCaseInsensitiveKey(convo->texts, key);
 }
 
 void ConversationSound::execute() {
diff --git a/engines/nancy/action/miscrecords.cpp b/engines/nancy/action/miscrecords.cpp
index cacb72796aa..259b1320083 100644
--- a/engines/nancy/action/miscrecords.cpp
+++ b/engines/nancy/action/miscrecords.cpp
@@ -116,7 +116,7 @@ void TextBoxWrite::readData(Common::SeekableReadStream &stream) {
 		const CVTX *autotext = (const CVTX *)g_nancy->getEngineData("AUTOTEXT");
 		assert(autotext);
 
-		_text = autotext->texts[stringID];
+		_text = getTextFromCaseInsensitiveKey(autotext->texts, stringID);
 	} else {
 		char *buf = new char[size];
 		stream.read(buf, size);
@@ -159,10 +159,7 @@ void FrameTextBox::readData(Common::SeekableReadStream &stream) {
 		const CVTX *autotext = (const CVTX *)g_nancy->getEngineData("AUTOTEXT");
 		assert(autotext);
 
-		// TODO: we probably ought to be doing something more robust here
-		// to detect missing keys, but for now just return an empty string
-		// if the key isn't found.
-		_text = autotext->texts.getValOrDefault(stringID, "");
+		_text = getTextFromCaseInsensitiveKey(autotext->texts, stringID);
 	} else if (size > 0) {
 		char *buf = new char[size];
 		stream.read(buf, size);
diff --git a/engines/nancy/action/soundrecords.cpp b/engines/nancy/action/soundrecords.cpp
index c65cfd4ede6..c27435c3a5d 100644
--- a/engines/nancy/action/soundrecords.cpp
+++ b/engines/nancy/action/soundrecords.cpp
@@ -138,7 +138,7 @@ void PlaySoundCC::readCCText(Common::SeekableReadStream &stream, Common::String
 		const CVTX *autotext = (const CVTX *)g_nancy->getEngineData("AUTOTEXT");
 		assert(autotext);
 
-		out = autotext->texts[key];
+		out = getTextFromCaseInsensitiveKey(autotext->texts, key);
 	}
 }
 
diff --git a/engines/nancy/ui/cellphonepopup.cpp b/engines/nancy/ui/cellphonepopup.cpp
index 60e3f429414..577068cbb6f 100644
--- a/engines/nancy/ui/cellphonepopup.cpp
+++ b/engines/nancy/ui/cellphonepopup.cpp
@@ -728,10 +728,8 @@ void CellPhonePopup::drawLinkList() {
 		}
 
 		Common::String lookupKey = list[absolute].key;
-		lookupKey.toUppercase();
-		Common::String rowText = (autotext && autotext->texts.contains(lookupKey))
-			? autotext->texts[lookupKey]
-			: lookupKey;
+		Common::String rowText = getTextFromCaseInsensitiveKey(autotext->texts, lookupKey);
+
 		// Single-line draw ??? drop every <n> markup so they don't render as
 		// literal "<n>" glyphs and crowd the row.
 		while (rowText.contains("<n>")) {
@@ -765,9 +763,6 @@ void CellPhonePopup::drawContentView() {
 	}
 
 	const CVTX *autotext = (const CVTX *)g_nancy->getEngineData("AUTOTEXT");
-	if (!autotext || !autotext->texts.contains(_contentKey)) {
-		return;
-	}
 
 	const Font *font = g_nancy->_graphics->getFont(_uiclData->fontId2);
 	if (!font) {
@@ -792,7 +787,7 @@ void CellPhonePopup::drawContentView() {
 
 	// Render the engine's hypertext markup into a tall scratch surface,
 	// then blit a vertically-scrolled window of it into the LCD.
-	const Common::String renderText = autotext->texts[_contentKey];
+	const Common::String renderText = getTextFromCaseInsensitiveKey(autotext->texts, _contentKey);
 
 	// Find this page in the UIBW chunk (browser pages only); its hotspot
 	// records are the per-page image table the article references.
diff --git a/engines/nancy/ui/notebookpopup.cpp b/engines/nancy/ui/notebookpopup.cpp
index 50d79fb3842..73037a51983 100644
--- a/engines/nancy/ui/notebookpopup.cpp
+++ b/engines/nancy/ui/notebookpopup.cpp
@@ -441,8 +441,6 @@ void NotebookPopup::buildTextLines() {
 		return;
 
 	const CVTX *autotext = (const CVTX *)g_nancy->getEngineData("AUTOTEXT");
-	if (!autotext)
-		return;
 
 	// Senior-detective Tasks page: hide the to-do list and show the
 	// AUTOTEXT placeholder body instead.
@@ -459,13 +457,8 @@ void NotebookPopup::buildTextLines() {
 
 	const Common::Array<JournalData::Entry> &entries = journalData->journalEntries[surfaceID];
 	for (uint i = 0; i < entries.size(); ++i) {
-		const Common::String &stringID = entries[i].stringID;
-		if (!autotext->texts.contains(stringID))
-			continue;
-
-		Common::String body = autotext->texts[stringID];
-		if (body.empty())
-			continue;
+		Common::String stringID = entries[i].stringID;
+		Common::String body = getTextFromCaseInsensitiveKey(autotext->texts, stringID);
 
 		// Tasks are prefixed with a checkbox showing completion state.
 		// mark % 10 == 8 means "complete".
diff --git a/engines/nancy/util.cpp b/engines/nancy/util.cpp
index 971d90a6df4..6c9518635f1 100644
--- a/engines/nancy/util.cpp
+++ b/engines/nancy/util.cpp
@@ -344,6 +344,31 @@ void assembleTextLine(char *rawCaption, Common::String &output, uint size) {
 	}
 }
 
+Common::String getTextFromCaseInsensitiveKey(Common::HashMap<Common::String, Common::String> texts, Common::String &key) {
+	if (texts.contains(key)) {
+		return texts[key];
+	} else {
+		// Nancy10+ searched keyed texts in a key insensitive way, but
+		// the possible permutations involve mainly the last character
+		// being upper or lower case, so just try that before giving up.
+		if (key[key.size() - 1] == toupper(key[key.size() - 1]))
+			key[key.size() - 1] = tolower(key[key.size() - 1]);
+		else
+			key[key.size() - 1] = toupper(key[key.size() - 1]);
+
+		if (texts.contains(key))
+			return texts[key];
+
+		key.toUppercase();
+
+		if (texts.contains(key))
+			return texts[key];
+	}
+
+	warning("Key not found: %s", key.c_str());
+	return "";
+}
+
 bool DeferredLoader::load(uint32 endTime) {
 	uint32 loopStartTime = g_system->getMillis();
 	uint32 loopTime = 0; // Stores the loop that took the longest time to complete
diff --git a/engines/nancy/util.h b/engines/nancy/util.h
index 3dc6ab1eaf5..0977792e16f 100644
--- a/engines/nancy/util.h
+++ b/engines/nancy/util.h
@@ -22,6 +22,7 @@
 #define NANCY_UTIL_H
 
 #include "common/array.h"
+#include "common/hashmap.h"
 #include "common/path.h"
 #include "common/rect.h"
 #include "common/serializer.h"
@@ -64,6 +65,8 @@ void readUISlider(Common::SeekableReadStream &stream, UISliderRecord &dst);
 void readUIPopupHeader(Common::SeekableReadStream &stream, UIPopupHeader &dst);
 void readUIButtonSlot(Common::SeekableReadStream &stream, UIButtonSlot &dst);
 
+Common::String getTextFromCaseInsensitiveKey(Common::HashMap<Common::String, Common::String> texts, Common::String &key);
+
 // Abstract base class used for loading data that would take too much time in a single frame
 class DeferredLoader {
 public:


Commit: 09764a18ed56986582f47a929b3477f988f97827
    https://github.com/scummvm/scummvm/commit/09764a18ed56986582f47a929b3477f988f97827
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-06-06T12:41:26+03:00

Commit Message:
NANCY: Fix making phone calls within a closeup scene in Nancy10

The call phone's scene pushing/popping mechanism could clobber the
global scene stack. Add safeguards, so that doesn't happen.

Fixes making phone calls within a closeup scene, e.g. when calling
Charleena Purcell while looking at a closeup of the trunk in Mary's
Gifts.

Changed paths:
    engines/nancy/action/miscrecords.cpp
    engines/nancy/ui/cellphonepopup.cpp
    engines/nancy/ui/cellphonepopup.h


diff --git a/engines/nancy/action/miscrecords.cpp b/engines/nancy/action/miscrecords.cpp
index 259b1320083..f3054b3afbe 100644
--- a/engines/nancy/action/miscrecords.cpp
+++ b/engines/nancy/action/miscrecords.cpp
@@ -219,8 +219,9 @@ void ControlUIItems::execute() {
 		case kUITypeNotebook:
 			NancySceneState.getNotebookPopup().open();
 			break;
-		case kUITypeCellphone:
-			NancySceneState.getCellPhonePopup().open();
+		case kUITypeCellphone: {
+			UI::CellPhonePopup &phone = NancySceneState.getCellPhonePopup();
+			phone.open();
 
 			if (_startScene != (int16)kNoScene) {
 				SceneChangeDescription scene;
@@ -230,11 +231,13 @@ void ControlUIItems::execute() {
 				// The destination scene's sound carries the conversation audio.
 				scene.continueSceneSound = kLoadSceneSound;
 
-				// Pushed so AR 128 in the conversation scene can return here.
-				NancySceneState.pushScene();
+				// Save the pre-call scene on the popup so AR 128 returns
+				// there without touching the global push slot.
+				phone.setReturnScene(NancySceneState.getSceneInfo());
 				NancySceneState.changeScene(scene);
 			}
 			break;
+		}
 		default:
 			break;
 		}
@@ -346,14 +349,22 @@ void CellPhonePopCellSceneFromStack::readData(Common::SeekableReadStream &stream
 }
 
 void CellPhonePopCellSceneFromStack::execute() {
-	if (_sceneChange.sceneID == kNoScene) {
-		NancySceneState.popScene(false);
-	} else {
+	UI::CellPhonePopup &phone = NancySceneState.getCellPhonePopup();
+
+	if (_sceneChange.sceneID != kNoScene) {
 		NancySceneState.changeScene(_sceneChange);
+	} else {
+		// Restore the pre-call scene if one was saved. If there's no saved
+		// scene (e.g. the conversation was entered without a phone call),
+		// do nothing ??? popping the global scene stack here would clobber
+		// closeup / inventory pushes that have nothing to do with the phone.
+		SceneChangeDescription returnScene;
+		if (phone.consumeReturnScene(returnScene))
+			NancySceneState.changeScene(returnScene);
 	}
 
 	// Conversation is over; take the phone down.
-	NancySceneState.getCellPhonePopup().close();
+	phone.close();
 
 	finishExecution();
 }
diff --git a/engines/nancy/ui/cellphonepopup.cpp b/engines/nancy/ui/cellphonepopup.cpp
index 577068cbb6f..d9aab409861 100644
--- a/engines/nancy/ui/cellphonepopup.cpp
+++ b/engines/nancy/ui/cellphonepopup.cpp
@@ -1116,14 +1116,28 @@ void CellPhonePopup::triggerContactCallSceneChange(uint contactIndex) {
 										eventFlagValue ? g_nancy->_true : g_nancy->_false);
 	}
 
-	// Pushed so AR 128 in the conversation scene can return the player here.
-	NancySceneState.pushScene();
+	// Save the pre-call scene on the popup so AR 128 can return there
+	// without clobbering the global push slot (used by closeups, etc).
+	setReturnScene(NancySceneState.getSceneInfo());
 
 	NancySceneState.changeScene(scene);
 
 	// Phone stays on screen through the conversation; AR 128 closes it.
 }
 
+void CellPhonePopup::setReturnScene(const SceneChangeDescription &scene) {
+	_returnScene = scene;
+	_hasReturnScene = true;
+}
+
+bool CellPhonePopup::consumeReturnScene(SceneChangeDescription &out) {
+	if (!_hasReturnScene)
+		return false;
+	out = _returnScene;
+	_hasReturnScene = false;
+	return true;
+}
+
 // --------------------------------------------------------------------
 // Directory helpers
 // --------------------------------------------------------------------
diff --git a/engines/nancy/ui/cellphonepopup.h b/engines/nancy/ui/cellphonepopup.h
index 9a0353003bd..0536c135a7f 100644
--- a/engines/nancy/ui/cellphonepopup.h
+++ b/engines/nancy/ui/cellphonepopup.h
@@ -65,6 +65,12 @@ public:
 						const Common::String &value, int16 extra,
 						int16 flag, int16 eventFlag);
 
+	// Phone-call return scene. Set before jumping into a conversation scene
+	// so AR 128 (CellPhonePopCellSceneFromStack) can return there without
+	// trampling the global push slot used by closeups/inventory views.
+	void setReturnScene(const SceneChangeDescription &scene);
+	bool consumeReturnScene(SceneChangeDescription &out);
+
 private:
 	enum ScreenState : int {
 		kWelcome          = 0,
@@ -212,6 +218,9 @@ private:
 
 	bool _noSignal = false;
 	bool _batteryLow = false;
+
+	SceneChangeDescription _returnScene;
+	bool _hasReturnScene = false;
 };
 
 } // End of namespace UI



From noreply at scummvm.org  Sat Jun  6 11:22:12 2026
From: noreply at scummvm.org (athrxx)
Date: Sat,  6 Jun 2026 11:22:12 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 0e1d41cfee4651055b105075f47132a1c88ce06e
Message-ID: <20260606112212.64CEE700573@ron.scummvm.net>

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

Summary:
0e1d41cfee SCUMM: fix scroll transition for dithered EGA mode


Commit: 0e1d41cfee4651055b105075f47132a1c88ce06e
    https://github.com/scummvm/scummvm/commit/0e1d41cfee4651055b105075f47132a1c88ce06e
Author: athrxx (athrxx at scummvm.org)
Date: 2026-06-06T13:21:04+02:00

Commit Message:
SCUMM: fix scroll transition for dithered EGA mode

Changed paths:
    engines/scumm/gfx.cpp





From noreply at scummvm.org  Sat Jun  6 11:26:25 2026
From: noreply at scummvm.org (neuromancer)
Date: Sat,  6 Jun 2026 11:26:25 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 ee8a663ea20b92fdb3ae1fbef457e8998cb4c60b
Message-ID: <20260606112625.504C1700573@ron.scummvm.net>

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 \



From noreply at scummvm.org  Sat Jun  6 11:26:28 2026
From: noreply at scummvm.org (ScummVM-Translations)
Date: Sat,  6 Jun 2026 11:26:28 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 3a4d8f5a93eac850caf672ed03e0cdf17a2a9741
Message-ID: <20260606112628.E34247005CE@ron.scummvm.net>

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

Summary:
3a4d8f5a93 I18N: Update translations templates


Commit: 3a4d8f5a93eac850caf672ed03e0cdf17a2a9741
    https://github.com/scummvm/scummvm/commit/3a4d8f5a93eac850caf672ed03e0cdf17a2a9741
Author: Weblate (noreply at weblate.org)
Date: 2026-06-06T11:26:24Z

Commit Message:
I18N: Update translations templates

Changed paths:
    po/scummvm.pot


diff --git a/po/scummvm.pot b/po/scummvm.pot
index 446b4597ee8..a316b4c30c2 100644
--- a/po/scummvm.pot
+++ b/po/scummvm.pot
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2026.2.1git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-05 19:36+0000\n"
+"POT-Creation-Date: 2026-06-06 11:26+0000\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL at ADDRESS>\n"
 "Language-Team: LANGUAGE <LL at li.org>\n"
@@ -12460,7 +12460,7 @@ msgid "Fire / select"
 msgstr ""
 
 #: engines/scumm/metaengine.cpp:1186
-msgid "Menu back"
+msgid "Walk / menu back"
 msgstr ""
 
 #: engines/scumm/metaengine.cpp:1191



From noreply at scummvm.org  Sat Jun  6 11:27:00 2026
From: noreply at scummvm.org (ScummVM-Translations)
Date: Sat,  6 Jun 2026 11:27:00 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 0a120e915a13daedcc539a920948b05ef889f5f1
Message-ID: <20260606112700.B12C9700573@ron.scummvm.net>

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

Summary:
0a120e915a I18N: Update translation files


Commit: 0a120e915a13daedcc539a920948b05ef889f5f1
    https://github.com/scummvm/scummvm/commit/0a120e915a13daedcc539a920948b05ef889f5f1
Author: Weblate (noreply at weblate.org)
Date: 2026-06-06T11:26:41Z

Commit Message:
I18N: Update translation files

Updated by "Update PO files to match POT (msgmerge)" hook in Weblate.

Translation: ScummVM/scummvm
Translate-URL: https://translations.scummvm.org/projects/scummvm/scummvm/

Changed paths:
    po/ar.po
    po/be-tarask.po
    po/be_BY.po
    po/ca_ES.po
    po/cs_CZ.po
    po/da.po
    po/de_DE.po
    po/el.po
    po/es_ES.po
    po/eu.po
    po/fi_FI.po
    po/fr_FR.po
    po/gl_ES.po
    po/he.po
    po/hi.po
    po/hu_HU.po
    po/it_IT.po
    po/ja.po
    po/ka.po
    po/ko.po
    po/nb_NO.po
    po/nl_NL.po
    po/nn_NO.po
    po/pl_PL.po
    po/pt_BR.po
    po/pt_PT.po
    po/ro.po
    po/ru_RU.po
    po/sv_SE.po
    po/tr.po
    po/uk_UA.po
    po/zh.po
    po/zh_Hans.po
    po/zh_Hant.po


diff --git a/po/ar.po b/po/ar.po
index f9d72237cbd..49e5b161910 100644
--- a/po/ar.po
+++ b/po/ar.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-05 19:36+0000\n"
+"POT-Creation-Date: 2026-06-06 11:26+0000\n"
 "PO-Revision-Date: 2026-02-24 22:26+0000\n"
 "Last-Translator: Mohamed Shaaban <mohmedbashamedo at hotmail.com>\n"
 "Language-Team: Arabic <https://translations.scummvm.org/projects/scummvm/"
@@ -14260,9 +14260,9 @@ msgstr "???????? ??????????"
 
 #: engines/scumm/metaengine.cpp:1186
 #, fuzzy
-#| msgid "Move Back"
-msgid "Menu back"
-msgstr "???????? ????????????"
+#| msgid "Open main menu"
+msgid "Walk / menu back"
+msgstr "???????? ?????????????? ????????????????"
 
 #: engines/scumm/metaengine.cpp:1191
 #, fuzzy
@@ -18389,6 +18389,11 @@ msgid ""
 "supported. Continue launching game?"
 msgstr ""
 
+#, fuzzy
+#~| msgid "Move Back"
+#~ msgid "Menu back"
+#~ msgstr "???????? ????????????"
+
 #, fuzzy
 #~| msgid "Backend"
 #~ msgid "Back / skip"
diff --git a/po/be-tarask.po b/po/be-tarask.po
index b91d6f80bd5..12945442165 100644
--- a/po/be-tarask.po
+++ b/po/be-tarask.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-05 19:36+0000\n"
+"POT-Creation-Date: 2026-06-06 11:26+0000\n"
 "PO-Revision-Date: 2023-02-26 15:01+0000\n"
 "Last-Translator: Kastu?? K. <oakum2334 at gmail.com>\n"
 "Language-Team: Belarusian (Tara??kievica) <https://translations.scummvm.org/"
@@ -14187,9 +14187,9 @@ msgstr "?????????????? ????????????"
 
 #: engines/scumm/metaengine.cpp:1186
 #, fuzzy
-#| msgid "Move Back"
-msgid "Menu back"
-msgstr "?????? ??????????"
+#| msgid "Open main menu"
+msgid "Walk / menu back"
+msgstr "?????????????? ???????????????? ????????"
 
 #: engines/scumm/metaengine.cpp:1191
 #, fuzzy
@@ -18304,6 +18304,11 @@ msgid ""
 "supported. Continue launching game?"
 msgstr ""
 
+#, fuzzy
+#~| msgid "Move Back"
+#~ msgid "Menu back"
+#~ msgstr "?????? ??????????"
+
 #, fuzzy
 #~| msgid "Backend"
 #~ msgid "Back / skip"
diff --git a/po/be_BY.po b/po/be_BY.po
index e68cf8a142d..c1de8f156ac 100644
--- a/po/be_BY.po
+++ b/po/be_BY.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-05 19:36+0000\n"
+"POT-Creation-Date: 2026-06-06 11:26+0000\n"
 "PO-Revision-Date: 2021-03-21 08:24+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Belarusian <https://translations.scummvm.org/projects/scummvm/"
@@ -13997,9 +13997,9 @@ msgstr "?????????????? ????????????"
 
 #: engines/scumm/metaengine.cpp:1186
 #, fuzzy
-#| msgid "Move Back"
-msgid "Menu back"
-msgstr "???????? ??????????"
+#| msgid "Open main menu"
+msgid "Walk / menu back"
+msgstr "?????????????? ???????????????? ????????"
 
 #: engines/scumm/metaengine.cpp:1191
 #, fuzzy
@@ -18015,6 +18015,11 @@ msgid ""
 "supported. Continue launching game?"
 msgstr ""
 
+#, fuzzy
+#~| msgid "Move Back"
+#~ msgid "Menu back"
+#~ msgstr "???????? ??????????"
+
 #, fuzzy
 #~| msgid "Joy Back"
 #~ msgid "Back / skip"
diff --git a/po/ca_ES.po b/po/ca_ES.po
index eb0d116a83d..455f5b1aee4 100644
--- a/po/ca_ES.po
+++ b/po/ca_ES.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.6.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-05 19:36+0000\n"
+"POT-Creation-Date: 2026-06-06 11:26+0000\n"
 "PO-Revision-Date: 2026-05-26 04:22+0000\n"
 "Last-Translator: Xaviu <xaviu at xaviu.com>\n"
 "Language-Team: Catalan <https://translations.scummvm.org/projects/scummvm/"
@@ -13064,9 +13064,9 @@ msgstr "Mou / Selecciona"
 
 #: engines/scumm/metaengine.cpp:1186
 #, fuzzy
-#| msgid "Move back"
-msgid "Menu back"
-msgstr "Mou enrere"
+#| msgid "Skip main menu"
+msgid "Walk / menu back"
+msgstr "Omet el men?? principal"
 
 #: engines/scumm/metaengine.cpp:1191
 #, fuzzy
@@ -16553,6 +16553,11 @@ msgid ""
 "supported. Continue launching game?"
 msgstr ""
 
+#, fuzzy
+#~| msgid "Move back"
+#~ msgid "Menu back"
+#~ msgstr "Mou enrere"
+
 #, fuzzy
 #~| msgid "Backspace"
 #~ msgid "Back / skip"
diff --git a/po/cs_CZ.po b/po/cs_CZ.po
index a683f820fe0..cfbbdb780e5 100644
--- a/po/cs_CZ.po
+++ b/po/cs_CZ.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.7.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-05 19:36+0000\n"
+"POT-Creation-Date: 2026-06-06 11:26+0000\n"
 "PO-Revision-Date: 2026-02-09 20:22+0000\n"
 "Last-Translator: AsciiWolf <mail at asciiwolf.com>\n"
 "Language-Team: Czech <https://translations.scummvm.org/projects/scummvm/"
@@ -13805,9 +13805,8 @@ msgstr "P??esunout se doleva"
 
 #: engines/scumm/metaengine.cpp:1186
 #, fuzzy
-#| msgid "Move Back"
-msgid "Menu back"
-msgstr "Vzad"
+msgid "Walk / menu back"
+msgstr "~N~??vrat do Spou??t????e"
 
 #: engines/scumm/metaengine.cpp:1191
 #, fuzzy
@@ -17766,6 +17765,11 @@ msgid ""
 "supported. Continue launching game?"
 msgstr ""
 
+#, fuzzy
+#~| msgid "Move Back"
+#~ msgid "Menu back"
+#~ msgstr "Vzad"
+
 #, fuzzy
 #~ msgid "Back / skip"
 #~ msgstr "Vzad"
diff --git a/po/da.po b/po/da.po
index e15a6e42b2e..3929298f883 100644
--- a/po/da.po
+++ b/po/da.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-05 19:36+0000\n"
+"POT-Creation-Date: 2026-06-06 11:26+0000\n"
 "PO-Revision-Date: 2023-09-07 17:04+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Danish <https://translations.scummvm.org/projects/scummvm/"
@@ -13896,9 +13896,8 @@ msgstr "Flyt til venstre"
 
 #: engines/scumm/metaengine.cpp:1186
 #, fuzzy
-#| msgid "Move Back"
-msgid "Menu back"
-msgstr "Flyt bagud"
+msgid "Walk / menu back"
+msgstr "~R~etur til oversigt"
 
 #: engines/scumm/metaengine.cpp:1191
 #, fuzzy
@@ -17889,6 +17888,11 @@ msgid ""
 "supported. Continue launching game?"
 msgstr ""
 
+#, fuzzy
+#~| msgid "Move Back"
+#~ msgid "Menu back"
+#~ msgstr "Flyt bagud"
+
 #, fuzzy
 #~| msgid "Backend"
 #~ msgid "Back / skip"
diff --git a/po/de_DE.po b/po/de_DE.po
index cd60796f34d..3472d71570d 100644
--- a/po/de_DE.po
+++ b/po/de_DE.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.10.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-05 19:36+0000\n"
+"POT-Creation-Date: 2026-06-06 11:26+0000\n"
 "PO-Revision-Date: 2026-05-05 16:31+0000\n"
 "Last-Translator: Lothar Serra Mari <lothar.serramari at scummvm.org>\n"
 "Language-Team: German <https://translations.scummvm.org/projects/scummvm/"
@@ -13795,9 +13795,9 @@ msgstr "Bewegen / Ausw??hlen"
 
 #: engines/scumm/metaengine.cpp:1186
 #, fuzzy
-#| msgid "Move back"
-msgid "Menu back"
-msgstr "Zur??ckgehen"
+#| msgid "Skip main menu"
+msgid "Walk / menu back"
+msgstr "Hauptmen?? ??berspringen"
 
 #: engines/scumm/metaengine.cpp:1191
 #, fuzzy
@@ -17417,6 +17417,11 @@ msgstr ""
 "Untertiteln gel??st werden, sofern diese unterst??tzt werden. M??chten Sie das "
 "Spiel trotzdem starten?"
 
+#, fuzzy
+#~| msgid "Move back"
+#~ msgid "Menu back"
+#~ msgstr "Zur??ckgehen"
+
 #, fuzzy
 #~| msgid "Backspace"
 #~ msgid "Back / skip"
diff --git a/po/el.po b/po/el.po
index ced05bfc4ee..094cc35897c 100644
--- a/po/el.po
+++ b/po/el.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.10.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-05 19:36+0000\n"
+"POT-Creation-Date: 2026-06-06 11:26+0000\n"
 "PO-Revision-Date: 2026-04-23 00:22+0000\n"
 "Last-Translator: Antoniou Athanasios <a.antoniou79 at gmail.com>\n"
 "Language-Team: Greek <https://translations.scummvm.org/projects/scummvm/"
@@ -13950,9 +13950,9 @@ msgstr "???????????? / ??????????????"
 
 #: engines/scumm/metaengine.cpp:1186
 #, fuzzy
-#| msgid "Move Back"
-msgid "Menu back"
-msgstr "?????????????????? ????????"
+#| msgid "Skip main menu"
+msgid "Walk / menu back"
+msgstr "?????????????????? ?????????????????? ??????????"
 
 #: engines/scumm/metaengine.cpp:1191
 #, fuzzy
@@ -17705,6 +17705,11 @@ msgstr ""
 "???? ?????????????????? ???? ?????????? ?????????????????? , ???? ????????????????????????????. ???? ???????????????????? ?????? "
 "???????????????? ?????? ????????????????????;"
 
+#, fuzzy
+#~| msgid "Move Back"
+#~ msgid "Menu back"
+#~ msgstr "?????????????????? ????????"
+
 #, fuzzy
 #~| msgid "Backspace"
 #~ msgid "Back / skip"
diff --git a/po/es_ES.po b/po/es_ES.po
index b319f78df70..2f377329644 100644
--- a/po/es_ES.po
+++ b/po/es_ES.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.4.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-05 19:36+0000\n"
+"POT-Creation-Date: 2026-06-06 11:26+0000\n"
 "PO-Revision-Date: 2026-06-03 08:30+0000\n"
 "Last-Translator: IlDucci <pajaroloco_2 at hotmail.com>\n"
 "Language-Team: Spanish <https://translations.scummvm.org/projects/scummvm/"
@@ -13757,8 +13757,10 @@ msgid "Fire / select"
 msgstr "Disparar/Seleccionar"
 
 #: engines/scumm/metaengine.cpp:1186
-msgid "Menu back"
-msgstr "Volver (men??s)"
+#, fuzzy
+#| msgid "Skip / menu back"
+msgid "Walk / menu back"
+msgstr "Omitir/Volver (men??s)"
 
 #: engines/scumm/metaengine.cpp:1191
 msgid "Skip / menu back"
@@ -17351,6 +17353,9 @@ msgstr ""
 "se podr??an resolver estos acertijos con subt??tulos, si est??n disponibles. "
 "??Quieres seguir ejecutando el juego?"
 
+#~ msgid "Menu back"
+#~ msgstr "Volver (men??s)"
+
 #~ msgid "Back / skip"
 #~ msgstr "Volver/Omitir"
 
diff --git a/po/eu.po b/po/eu.po
index 6570181c9cc..50b277da5ee 100644
--- a/po/eu.po
+++ b/po/eu.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.5.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-05 19:36+0000\n"
+"POT-Creation-Date: 2026-06-06 11:26+0000\n"
 "PO-Revision-Date: 2024-02-05 18:42+0000\n"
 "Last-Translator: Josu Igoa <josuigoa at ni.eus>\n"
 "Language-Team: Basque <https://translations.scummvm.org/projects/scummvm/"
@@ -13863,9 +13863,8 @@ msgstr "Ezkerrera irristatu"
 
 #: engines/scumm/metaengine.cpp:1186
 #, fuzzy
-#| msgid "Move Back"
-msgid "Menu back"
-msgstr "Atzera mugitu"
+msgid "Walk / menu back"
+msgstr "It~z~uli abiarazlera"
 
 #: engines/scumm/metaengine.cpp:1191
 #, fuzzy
@@ -17861,6 +17860,11 @@ msgid ""
 "supported. Continue launching game?"
 msgstr ""
 
+#, fuzzy
+#~| msgid "Move Back"
+#~ msgid "Menu back"
+#~ msgstr "Atzera mugitu"
+
 #, fuzzy
 #~| msgid "Backspace"
 #~ msgid "Back / skip"
diff --git a/po/fi_FI.po b/po/fi_FI.po
index 110d08bb059..daa57d5207d 100644
--- a/po/fi_FI.po
+++ b/po/fi_FI.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.6.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-05 19:36+0000\n"
+"POT-Creation-Date: 2026-06-06 11:26+0000\n"
 "PO-Revision-Date: 2026-02-07 15:22+0000\n"
 "Last-Translator: Timo Mikkolainen <tmikkola at gmail.com>\n"
 "Language-Team: Finnish <https://translations.scummvm.org/projects/scummvm/"
@@ -13995,9 +13995,9 @@ msgstr "Liiku / Valitse"
 
 #: engines/scumm/metaengine.cpp:1186
 #, fuzzy
-#| msgid "Move back"
-msgid "Menu back"
-msgstr "Liiku taaksep??in"
+#| msgid "Skip main menu"
+msgid "Walk / menu back"
+msgstr "Ohita p????valikko"
 
 #: engines/scumm/metaengine.cpp:1191
 #, fuzzy
@@ -17733,6 +17733,11 @@ msgid ""
 "supported. Continue launching game?"
 msgstr ""
 
+#, fuzzy
+#~| msgid "Move back"
+#~ msgid "Menu back"
+#~ msgstr "Liiku taaksep??in"
+
 #, fuzzy
 #~| msgid "Backspace"
 #~ msgid "Back / skip"
diff --git a/po/fr_FR.po b/po/fr_FR.po
index e9f158876ff..09f4daeae01 100644
--- a/po/fr_FR.po
+++ b/po/fr_FR.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-05 19:36+0000\n"
+"POT-Creation-Date: 2026-06-06 11:26+0000\n"
 "PO-Revision-Date: 2026-06-02 11:22+0000\n"
 "Last-Translator: Purple T <ZEONK at hotmail.com>\n"
 "Language-Team: French <https://translations.scummvm.org/projects/scummvm/"
@@ -13782,8 +13782,10 @@ msgid "Fire / select"
 msgstr "Tirer / S??lectionner"
 
 #: engines/scumm/metaengine.cpp:1186
-msgid "Menu back"
-msgstr "Menu retour"
+#, fuzzy
+#| msgid "Skip / menu back"
+msgid "Walk / menu back"
+msgstr "Passer / menu retour"
 
 #: engines/scumm/metaengine.cpp:1191
 msgid "Skip / menu back"
@@ -17401,6 +17403,9 @@ msgstr ""
 "r??solues.  Ces puzzles peuvent ??galement ??tre r??solus ?? l'aide de sous-"
 "titres, s'ils sont pris en charge. Continuer ?? lancer le jeu ?"
 
+#~ msgid "Menu back"
+#~ msgstr "Menu retour"
+
 #~ msgid "Back / skip"
 #~ msgstr "Retour / passer"
 
diff --git a/po/gl_ES.po b/po/gl_ES.po
index f16d355a456..2212038d996 100644
--- a/po/gl_ES.po
+++ b/po/gl_ES.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-05 19:36+0000\n"
+"POT-Creation-Date: 2026-06-06 11:26+0000\n"
 "PO-Revision-Date: 2020-09-26 21:13+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Galician <https://translations.scummvm.org/projects/scummvm/"
@@ -13819,9 +13819,8 @@ msgstr "Mover ?? esquerda"
 
 #: engines/scumm/metaengine.cpp:1186
 #, fuzzy
-#| msgid "Move Back"
-msgid "Menu back"
-msgstr "Mover cara atr??s"
+msgid "Walk / menu back"
+msgstr "~V~olver ao Iniciador"
 
 #: engines/scumm/metaengine.cpp:1191
 #, fuzzy
@@ -17771,6 +17770,11 @@ msgid ""
 "supported. Continue launching game?"
 msgstr ""
 
+#, fuzzy
+#~| msgid "Move Back"
+#~ msgid "Menu back"
+#~ msgstr "Mover cara atr??s"
+
 #, fuzzy
 #~ msgid "Back / skip"
 #~ msgstr "Mover cara atr??s"
diff --git a/po/he.po b/po/he.po
index d5ab7be8303..1ed13aa192a 100644
--- a/po/he.po
+++ b/po/he.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.1.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-05 19:36+0000\n"
+"POT-Creation-Date: 2026-06-06 11:26+0000\n"
 "PO-Revision-Date: 2025-04-09 20:18+0000\n"
 "Last-Translator: Niv Baehr <bloop93 at gmail.com>\n"
 "Language-Team: Hebrew <https://translations.scummvm.org/projects/scummvm/"
@@ -14042,9 +14042,9 @@ msgstr "?????????? ??????????"
 
 #: engines/scumm/metaengine.cpp:1186
 #, fuzzy
-#| msgid "Move Back"
-msgid "Menu back"
-msgstr "??????????"
+#| msgid "Open main menu"
+msgid "Walk / menu back"
+msgstr "?????????? ?????????? ????????"
 
 #: engines/scumm/metaengine.cpp:1191
 #, fuzzy
@@ -18166,6 +18166,11 @@ msgid ""
 "supported. Continue launching game?"
 msgstr ""
 
+#, fuzzy
+#~| msgid "Move Back"
+#~ msgid "Menu back"
+#~ msgstr "??????????"
+
 #, fuzzy
 #~| msgid "Backend"
 #~ msgid "Back / skip"
diff --git a/po/hi.po b/po/hi.po
index 3a3a3d281c2..11d46e9d9d4 100644
--- a/po/hi.po
+++ b/po/hi.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-05 19:36+0000\n"
+"POT-Creation-Date: 2026-06-06 11:26+0000\n"
 "PO-Revision-Date: 2026-05-12 22:22+0000\n"
 "Last-Translator: KARTIK SAXENA <kartiksaxena2005 at gmail.com>\n"
 "Language-Team: Hindi <https://translations.scummvm.org/projects/scummvm/"
@@ -13297,9 +13297,9 @@ msgstr "???????????? ????????????"
 
 #: engines/scumm/metaengine.cpp:1186
 #, fuzzy
-#| msgid "Move Forward"
-msgid "Menu back"
-msgstr "????????? ?????????"
+#| msgid "Skip autosave"
+msgid "Walk / menu back"
+msgstr "?????????????????? ??????????????? ????????????"
 
 #: engines/scumm/metaengine.cpp:1191
 #, fuzzy
@@ -17160,6 +17160,11 @@ msgid ""
 "supported. Continue launching game?"
 msgstr ""
 
+#, fuzzy
+#~| msgid "Move Forward"
+#~ msgid "Menu back"
+#~ msgstr "????????? ?????????"
+
 #, fuzzy
 #~| msgid "Backspace"
 #~ msgid "Back / skip"
diff --git a/po/hu_HU.po b/po/hu_HU.po
index ffed1dc018f..c559a14e895 100644
--- a/po/hu_HU.po
+++ b/po/hu_HU.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-05 19:36+0000\n"
+"POT-Creation-Date: 2026-06-06 11:26+0000\n"
 "PO-Revision-Date: 2025-03-07 00:18+0000\n"
 "Last-Translator: Hov??nszki Tam??s <gahex68379 at lofiey.com>\n"
 "Language-Team: Hungarian <https://translations.scummvm.org/projects/scummvm/"
@@ -14502,9 +14502,9 @@ msgstr "Mozg??s balra"
 
 #: engines/scumm/metaengine.cpp:1186
 #, fuzzy
-#| msgid "Move Back"
-msgid "Menu back"
-msgstr "Mozg??s h??tra"
+#| msgid "Skip main menu"
+msgid "Walk / menu back"
+msgstr "F?? men?? ??tugr??sa"
 
 #: engines/scumm/metaengine.cpp:1191
 #, fuzzy
@@ -18594,6 +18594,11 @@ msgid ""
 "supported. Continue launching game?"
 msgstr ""
 
+#, fuzzy
+#~| msgid "Move Back"
+#~ msgid "Menu back"
+#~ msgstr "Mozg??s h??tra"
+
 #, fuzzy
 #~| msgid "Backspace"
 #~ msgid "Back / skip"
diff --git a/po/it_IT.po b/po/it_IT.po
index 783149531b6..ceb532b26c9 100644
--- a/po/it_IT.po
+++ b/po/it_IT.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-05 19:36+0000\n"
+"POT-Creation-Date: 2026-06-06 11:26+0000\n"
 "PO-Revision-Date: 2026-06-04 19:00+0000\n"
 "Last-Translator: Walter Agazzi <walter.agazzi at protonmail.com>\n"
 "Language-Team: Italian <https://translations.scummvm.org/projects/scummvm/"
@@ -13728,8 +13728,10 @@ msgid "Fire / select"
 msgstr "Spara / seleziona"
 
 #: engines/scumm/metaengine.cpp:1186
-msgid "Menu back"
-msgstr "Indietro (men??)"
+#, fuzzy
+#| msgid "Skip / menu back"
+msgid "Walk / menu back"
+msgstr "Salta / indietro (men??)"
 
 #: engines/scumm/metaengine.cpp:1191
 msgid "Skip / menu back"
@@ -17330,6 +17332,9 @@ msgstr ""
 "poterli risolvere. In alternativa, i puzzle possono essere risolti usando i "
 "sottotitoli, se supportati. Desideri continuare ad avviare il gioco?"
 
+#~ msgid "Menu back"
+#~ msgstr "Indietro (men??)"
+
 #, fuzzy
 #~| msgid "Backspace"
 #~ msgid "Back / skip"
diff --git a/po/ja.po b/po/ja.po
index 63dca5e9668..7b0afd958b3 100644
--- a/po/ja.po
+++ b/po/ja.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-05 19:36+0000\n"
+"POT-Creation-Date: 2026-06-06 11:26+0000\n"
 "PO-Revision-Date: 2026-06-04 07:22+0000\n"
 "Last-Translator: VAN-Gluon <van.gluonp at gmail.com>\n"
 "Language-Team: Japanese <https://translations.scummvm.org/projects/scummvm/"
@@ -14120,9 +14120,9 @@ msgstr "????????????"
 
 #: engines/scumm/metaengine.cpp:1186
 #, fuzzy
-#| msgid "Move Back"
-msgid "Menu back"
-msgstr "??????"
+#| msgid "Skip main menu"
+msgid "Walk / menu back"
+msgstr "????????????????????????????????????"
 
 #: engines/scumm/metaengine.cpp:1191
 #, fuzzy
@@ -18221,6 +18221,11 @@ msgid ""
 "supported. Continue launching game?"
 msgstr ""
 
+#, fuzzy
+#~| msgid "Move Back"
+#~ msgid "Menu back"
+#~ msgstr "??????"
+
 #, fuzzy
 #~| msgid "Backspace"
 #~ msgid "Back / skip"
diff --git a/po/ka.po b/po/ka.po
index 02b57ee8312..e9982a65682 100644
--- a/po/ka.po
+++ b/po/ka.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-05 19:36+0000\n"
+"POT-Creation-Date: 2026-06-06 11:26+0000\n"
 "PO-Revision-Date: 2026-06-05 10:22+0000\n"
 "Last-Translator: NorwayFun <temuri.doghonadze at gmail.com>\n"
 "Language-Team: Georgian <https://translations.scummvm.org/projects/scummvm/"
@@ -12494,8 +12494,10 @@ msgid "Fire / select"
 msgstr "?????????????????? / ????????????????????????"
 
 #: engines/scumm/metaengine.cpp:1186
-msgid "Menu back"
-msgstr "??????????????? ????????????"
+#, fuzzy
+#| msgid "Skip / menu back"
+msgid "Walk / menu back"
+msgstr "?????????????????????????????? / ??????????????? ????????????"
 
 #: engines/scumm/metaengine.cpp:1191
 msgid "Skip / menu back"
@@ -15916,6 +15918,9 @@ msgid ""
 "supported. Continue launching game?"
 msgstr ""
 
+#~ msgid "Menu back"
+#~ msgstr "??????????????? ????????????"
+
 #, fuzzy
 #~| msgid "Backspace"
 #~ msgid "Back / skip"
diff --git a/po/ko.po b/po/ko.po
index 4b2eaf55058..fbff096317e 100644
--- a/po/ko.po
+++ b/po/ko.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-05 19:36+0000\n"
+"POT-Creation-Date: 2026-06-06 11:26+0000\n"
 "PO-Revision-Date: 2026-05-19 20:22+0000\n"
 "Last-Translator: Hoseok Seo <ddinghoya at gmail.com>\n"
 "Language-Team: Korean <https://translations.scummvm.org/projects/scummvm/"
@@ -13423,9 +13423,9 @@ msgstr "?????? / ??????"
 
 #: engines/scumm/metaengine.cpp:1186
 #, fuzzy
-#| msgid "Move back"
-msgid "Menu back"
-msgstr "?????? ??????"
+#| msgid "Skip main menu"
+msgid "Walk / menu back"
+msgstr "?????? ?????? ????????????"
 
 #: engines/scumm/metaengine.cpp:1191
 #, fuzzy
@@ -16963,6 +16963,11 @@ msgstr ""
 "MIDI ???????????? ????????? ?????? ?????? ????????? ???????????? ????????????.  ????????? ????????? ??????"
 "?????? ?????? ????????? ???????????? ????????? ??? ????????????. ????????? ?????? ????????????????"
 
+#, fuzzy
+#~| msgid "Move back"
+#~ msgid "Menu back"
+#~ msgstr "?????? ??????"
+
 #, fuzzy
 #~| msgid "Backspace"
 #~ msgid "Back / skip"
diff --git a/po/nb_NO.po b/po/nb_NO.po
index 67b3b235810..2816787779f 100644
--- a/po/nb_NO.po
+++ b/po/nb_NO.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-05 19:36+0000\n"
+"POT-Creation-Date: 2026-06-06 11:26+0000\n"
 "PO-Revision-Date: 2026-02-07 15:22+0000\n"
 "Last-Translator: Einar Johan Tr??an S??m??en <einarjohants at gmail.com>\n"
 "Language-Team: Norwegian Bokm??l <https://translations.scummvm.org/projects/"
@@ -14593,9 +14593,9 @@ msgstr "G?? til venstre"
 
 #: engines/scumm/metaengine.cpp:1186
 #, fuzzy
-#| msgid "Move Back"
-msgid "Menu back"
-msgstr "Beveg Bakover"
+#| msgid "Skip main menu"
+msgid "Walk / menu back"
+msgstr "Hopp over hovedmeny"
 
 #: engines/scumm/metaengine.cpp:1191
 #, fuzzy
@@ -18770,6 +18770,11 @@ msgid ""
 "supported. Continue launching game?"
 msgstr ""
 
+#, fuzzy
+#~| msgid "Move Back"
+#~ msgid "Menu back"
+#~ msgstr "Beveg Bakover"
+
 #, fuzzy
 #~| msgid "Backspace"
 #~ msgid "Back / skip"
diff --git a/po/nl_NL.po b/po/nl_NL.po
index edc7f61491c..1ea3795dc78 100644
--- a/po/nl_NL.po
+++ b/po/nl_NL.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.9.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-05 19:36+0000\n"
+"POT-Creation-Date: 2026-06-06 11:26+0000\n"
 "PO-Revision-Date: 2026-02-11 14:23+0000\n"
 "Last-Translator: SecularSteve <fairfull.playing at gmail.com>\n"
 "Language-Team: Dutch <https://translations.scummvm.org/projects/scummvm/"
@@ -13786,9 +13786,9 @@ msgstr "Verplaatsen / Selecteren"
 
 #: engines/scumm/metaengine.cpp:1186
 #, fuzzy
-#| msgid "Move back"
-msgid "Menu back"
-msgstr "Teruggaan"
+#| msgid "Skip main menu"
+msgid "Walk / menu back"
+msgstr "Hoofdmenu overslaan"
 
 #: engines/scumm/metaengine.cpp:1191
 #, fuzzy
@@ -17413,6 +17413,11 @@ msgstr ""
 "behulp van ondertitels, indien ondersteund. Doorgaan met het starten van het "
 "spel?"
 
+#, fuzzy
+#~| msgid "Move back"
+#~ msgid "Menu back"
+#~ msgstr "Teruggaan"
+
 #, fuzzy
 #~| msgid "Backspace"
 #~ msgid "Back / skip"
diff --git a/po/nn_NO.po b/po/nn_NO.po
index 0b22ebc43a9..657c4c577bb 100644
--- a/po/nn_NO.po
+++ b/po/nn_NO.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-05 19:36+0000\n"
+"POT-Creation-Date: 2026-06-06 11:26+0000\n"
 "PO-Revision-Date: 2023-12-14 14:28+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Norwegian Nynorsk <https://translations.scummvm.org/projects/"
@@ -13716,9 +13716,8 @@ msgstr "Skli til Venstre"
 
 #: engines/scumm/metaengine.cpp:1186
 #, fuzzy
-#| msgid "Move Back"
-msgid "Menu back"
-msgstr "Beveg Bakover"
+msgid "Walk / menu back"
+msgstr "Tilbake til Oppsta~r~tar"
 
 #: engines/scumm/metaengine.cpp:1191
 #, fuzzy
@@ -17691,6 +17690,11 @@ msgid ""
 "supported. Continue launching game?"
 msgstr ""
 
+#, fuzzy
+#~| msgid "Move Back"
+#~ msgid "Menu back"
+#~ msgstr "Beveg Bakover"
+
 #, fuzzy
 #~| msgid "Backspace"
 #~ msgid "Back / skip"
diff --git a/po/pl_PL.po b/po/pl_PL.po
index a2edc01050a..c790a0f499e 100644
--- a/po/pl_PL.po
+++ b/po/pl_PL.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-05 19:36+0000\n"
+"POT-Creation-Date: 2026-06-06 11:26+0000\n"
 "PO-Revision-Date: 2026-04-17 00:22+0000\n"
 "Last-Translator: SkiffPL <skiff at windowslive.com>\n"
 "Language-Team: Polish <https://translations.scummvm.org/projects/scummvm/"
@@ -13965,9 +13965,9 @@ msgstr "Ruch / Zaznacz"
 
 #: engines/scumm/metaengine.cpp:1186
 #, fuzzy
-#| msgid "Move back"
-msgid "Menu back"
-msgstr "Ruch do ty??u"
+#| msgid "Skip main menu"
+msgid "Walk / menu back"
+msgstr "Pomijaj menu g????wne"
 
 #: engines/scumm/metaengine.cpp:1191
 #, fuzzy
@@ -17668,6 +17668,11 @@ msgstr ""
 "rozwi??zania. Te ??amig????wki mo??na r??wnie?? rozwi??za?? za pomoc?? napis??w, je??li "
 "s?? obs??ugiwane. Kontynuowa?? uruchamianie gry?"
 
+#, fuzzy
+#~| msgid "Move back"
+#~ msgid "Menu back"
+#~ msgstr "Ruch do ty??u"
+
 #, fuzzy
 #~| msgid "Backspace"
 #~ msgid "Back / skip"
diff --git a/po/pt_BR.po b/po/pt_BR.po
index 304bf314279..91ecef4ce23 100644
--- a/po/pt_BR.po
+++ b/po/pt_BR.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-05 19:36+0000\n"
+"POT-Creation-Date: 2026-06-06 11:26+0000\n"
 "PO-Revision-Date: 2024-11-16 02:13+0000\n"
 "Last-Translator: Marcel Souza Lemes <marcosoutsider at gmail.com>\n"
 "Language-Team: Portuguese (Brazil) <https://translations.scummvm.org/"
@@ -14565,9 +14565,9 @@ msgstr "Mover para esquerda"
 
 #: engines/scumm/metaengine.cpp:1186
 #, fuzzy
-#| msgid "Move Back"
-msgid "Menu back"
-msgstr "Mover para Tr??s"
+#| msgid "Skip main menu"
+msgid "Walk / menu back"
+msgstr "Omitir menu principal"
 
 #: engines/scumm/metaengine.cpp:1191
 #, fuzzy
@@ -18688,6 +18688,11 @@ msgid ""
 "supported. Continue launching game?"
 msgstr ""
 
+#, fuzzy
+#~| msgid "Move Back"
+#~ msgid "Menu back"
+#~ msgstr "Mover para Tr??s"
+
 #, fuzzy
 #~| msgid "Backspace"
 #~ msgid "Back / skip"
diff --git a/po/pt_PT.po b/po/pt_PT.po
index e527af1d283..c038eba1e80 100644
--- a/po/pt_PT.po
+++ b/po/pt_PT.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.10.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-05 19:36+0000\n"
+"POT-Creation-Date: 2026-06-06 11:26+0000\n"
 "PO-Revision-Date: 2022-06-05 14:09+0000\n"
 "Last-Translator: Daniel <supsuper at gmail.com>\n"
 "Language-Team: Portuguese (Portugal) <https://translations.scummvm.org/"
@@ -13840,9 +13840,8 @@ msgstr "Mover para a esquerda"
 
 #: engines/scumm/metaengine.cpp:1186
 #, fuzzy
-#| msgid "Move Back"
-msgid "Menu back"
-msgstr "Retroceder"
+msgid "Walk / menu back"
+msgstr "~M~enu principal"
 
 #: engines/scumm/metaengine.cpp:1191
 #, fuzzy
@@ -17844,6 +17843,11 @@ msgid ""
 "supported. Continue launching game?"
 msgstr ""
 
+#, fuzzy
+#~| msgid "Move Back"
+#~ msgid "Menu back"
+#~ msgstr "Retroceder"
+
 #, fuzzy
 #~| msgid "Backend"
 #~ msgid "Back / skip"
diff --git a/po/ro.po b/po/ro.po
index 5c8968bb42d..8609f3c0cba 100644
--- a/po/ro.po
+++ b/po/ro.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.10.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-05 19:36+0000\n"
+"POT-Creation-Date: 2026-06-06 11:26+0000\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: Automatically generated\n"
 "Language-Team: none\n"
@@ -12459,7 +12459,7 @@ msgid "Fire / select"
 msgstr ""
 
 #: engines/scumm/metaengine.cpp:1186
-msgid "Menu back"
+msgid "Walk / menu back"
 msgstr ""
 
 #: engines/scumm/metaengine.cpp:1191
diff --git a/po/ru_RU.po b/po/ru_RU.po
index bb4bf4a1a2e..6118daf1595 100644
--- a/po/ru_RU.po
+++ b/po/ru_RU.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.8.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-05 19:36+0000\n"
+"POT-Creation-Date: 2026-06-06 11:26+0000\n"
 "PO-Revision-Date: 2026-05-06 08:54+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Russian <https://translations.scummvm.org/projects/scummvm/"
@@ -13634,9 +13634,9 @@ msgstr "???????? / ??????????????"
 
 #: engines/scumm/metaengine.cpp:1186
 #, fuzzy
-#| msgid "Move back"
-msgid "Menu back"
-msgstr "???????? ??????????"
+#| msgid "Skip main menu"
+msgid "Walk / menu back"
+msgstr "???????????????????? ?????????????? ????????"
 
 #: engines/scumm/metaengine.cpp:1191
 #, fuzzy
@@ -17203,6 +17203,11 @@ msgstr ""
 "MIDI-??????????. ?????? ?????????????????????? ?????????? ?????????? ???????????? ?? ?????????????? ??????????????????, ???????? ?????? "
 "????????????????????????????. ???????????????????? ???????????? ?????????"
 
+#, fuzzy
+#~| msgid "Move back"
+#~ msgid "Menu back"
+#~ msgstr "???????? ??????????"
+
 #, fuzzy
 #~| msgid "Backspace"
 #~ msgid "Back / skip"
diff --git a/po/sv_SE.po b/po/sv_SE.po
index ec01f977f15..f00f664f6d2 100644
--- a/po/sv_SE.po
+++ b/po/sv_SE.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.5.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-05 19:36+0000\n"
+"POT-Creation-Date: 2026-06-06 11:26+0000\n"
 "PO-Revision-Date: 2026-04-19 13:22+0000\n"
 "Last-Translator: Daniel Nylander <po at danielnylander.se>\n"
 "Language-Team: Swedish <https://translations.scummvm.org/projects/scummvm/"
@@ -13683,9 +13683,9 @@ msgstr "Flytta / V??lj"
 
 #: engines/scumm/metaengine.cpp:1186
 #, fuzzy
-#| msgid "Move back"
-msgid "Menu back"
-msgstr "Stega bak??t"
+#| msgid "Skip main menu"
+msgid "Walk / menu back"
+msgstr "Hoppa ??ver huvudmeny"
 
 #: engines/scumm/metaengine.cpp:1191
 #, fuzzy
@@ -17238,6 +17238,11 @@ msgstr ""
 "l??sas. Dessa pussel kan alternativt l??sas med hj??lp av undertexter, om detta "
 "st??ds. Vill du forts??tta spela?"
 
+#, fuzzy
+#~| msgid "Move back"
+#~ msgid "Menu back"
+#~ msgstr "Stega bak??t"
+
 #, fuzzy
 #~| msgid "Backspace"
 #~ msgid "Back / skip"
diff --git a/po/tr.po b/po/tr.po
index 31b3bac8aab..9ffbb03cd1c 100644
--- a/po/tr.po
+++ b/po/tr.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-05 19:36+0000\n"
+"POT-Creation-Date: 2026-06-06 11:26+0000\n"
 "PO-Revision-Date: 2024-10-10 12:33+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Turkish <https://translations.scummvm.org/projects/scummvm/"
@@ -13338,9 +13338,8 @@ msgstr "Sol"
 
 #: engines/scumm/metaengine.cpp:1186
 #, fuzzy
-#| msgid "Backend"
-msgid "Menu back"
-msgstr "Altyap??"
+msgid "Walk / menu back"
+msgstr "??ptal"
 
 #: engines/scumm/metaengine.cpp:1191
 #, fuzzy
@@ -17095,6 +17094,11 @@ msgid ""
 "supported. Continue launching game?"
 msgstr ""
 
+#, fuzzy
+#~| msgid "Backend"
+#~ msgid "Menu back"
+#~ msgstr "Altyap??"
+
 #, fuzzy
 #~| msgid "Backend"
 #~ msgid "Back / skip"
diff --git a/po/uk_UA.po b/po/uk_UA.po
index 8381eea590a..0863ac82e01 100644
--- a/po/uk_UA.po
+++ b/po/uk_UA.po
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.9.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-05 19:36+0000\n"
+"POT-Creation-Date: 2026-06-06 11:26+0000\n"
 "PO-Revision-Date: 2026-05-06 08:54+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Ukrainian <https://translations.scummvm.org/projects/scummvm/"
@@ -13593,9 +13593,9 @@ msgstr "???????????????? / ??????????????"
 
 #: engines/scumm/metaengine.cpp:1186
 #, fuzzy
-#| msgid "Move back"
-msgid "Menu back"
-msgstr "???????????????? ??????????"
+#| msgid "Skip main menu"
+msgid "Walk / menu back"
+msgstr "???????????????????? ?????????????? ????????"
 
 #: engines/scumm/metaengine.cpp:1191
 #, fuzzy
@@ -17152,6 +17152,11 @@ msgstr ""
 "??????????. ???? ?????????????????????? ?????????? ?????????? ????????'?????????? ???? ?????????????????? ??????????????????, ???????? "
 "???????? ??????????????????????????. ???????????????????? ???????????? ???????"
 
+#, fuzzy
+#~| msgid "Move back"
+#~ msgid "Menu back"
+#~ msgstr "???????????????? ??????????"
+
 #, fuzzy
 #~| msgid "Backspace"
 #~ msgid "Back / skip"
diff --git a/po/zh.po b/po/zh.po
index c8e7cc04c97..05e0bbecd8b 100644
--- a/po/zh.po
+++ b/po/zh.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-05 19:36+0000\n"
+"POT-Creation-Date: 2026-06-06 11:26+0000\n"
 "PO-Revision-Date: 2024-05-14 12:42+0000\n"
 "Last-Translator: ?????? <clonewith at qq.com>\n"
 "Language-Team: Chinese <https://translations.scummvm.org/projects/scummvm/"
@@ -13162,9 +13162,9 @@ msgstr "??????"
 
 #: engines/scumm/metaengine.cpp:1186
 #, fuzzy
-#| msgid "Backend"
-msgid "Menu back"
-msgstr "??????"
+#| msgid "Skip autosave"
+msgid "Walk / menu back"
+msgstr "??????????????????"
 
 #: engines/scumm/metaengine.cpp:1191
 #, fuzzy
@@ -16984,6 +16984,11 @@ msgid ""
 "supported. Continue launching game?"
 msgstr ""
 
+#, fuzzy
+#~| msgid "Backend"
+#~ msgid "Menu back"
+#~ msgstr "??????"
+
 #, fuzzy
 #~| msgid "Backspace"
 #~ msgid "Back / skip"
diff --git a/po/zh_Hans.po b/po/zh_Hans.po
index 12797bfdb8f..9baf5253dff 100644
--- a/po/zh_Hans.po
+++ b/po/zh_Hans.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.9.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-05 19:36+0000\n"
+"POT-Creation-Date: 2026-06-06 11:26+0000\n"
 "PO-Revision-Date: 2024-12-01 22:18+0000\n"
 "Last-Translator: ?????? <clonewith at qq.com>\n"
 "Language-Team: Chinese (Simplified Han script) <https://"
@@ -14143,9 +14143,9 @@ msgstr "????????????"
 
 #: engines/scumm/metaengine.cpp:1186
 #, fuzzy
-#| msgid "Move Back"
-msgid "Menu back"
-msgstr "????????????"
+#| msgid "Skip main menu"
+msgid "Walk / menu back"
+msgstr "???????????????"
 
 #: engines/scumm/metaengine.cpp:1191
 #, fuzzy
@@ -18187,6 +18187,11 @@ msgid ""
 "supported. Continue launching game?"
 msgstr ""
 
+#, fuzzy
+#~| msgid "Move Back"
+#~ msgid "Menu back"
+#~ msgstr "????????????"
+
 #, fuzzy
 #~| msgid "Backspace"
 #~ msgid "Back / skip"
diff --git a/po/zh_Hant.po b/po/zh_Hant.po
index eebb929a279..8af6799126d 100644
--- a/po/zh_Hant.po
+++ b/po/zh_Hant.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.9.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-05 19:36+0000\n"
+"POT-Creation-Date: 2026-06-06 11:26+0000\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: Automatically generated\n"
 "Language-Team: none\n"
@@ -12460,7 +12460,7 @@ msgid "Fire / select"
 msgstr ""
 
 #: engines/scumm/metaengine.cpp:1186
-msgid "Menu back"
+msgid "Walk / menu back"
 msgstr ""
 
 #: engines/scumm/metaengine.cpp:1191



From noreply at scummvm.org  Sat Jun  6 11:38:01 2026
From: noreply at scummvm.org (dreammaster)
Date: Sat,  6 Jun 2026 11:38:01 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 f6c38ae6fbf0110e20e4e6eb6a867f767a9e1000
Message-ID: <20260606113801.6DEC8700573@ron.scummvm.net>

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:
601fc79921 MM: MM1: Add classic PC speaker sounds
30eb8f2e15 MM: MM1: Update classic PC speaker playback
f6c38ae6fb MM: MM1: Add classic sound test command


Commit: 601fc79921b3a4a6c34af80bdc561dd9c6f618ea
    https://github.com/scummvm/scummvm/commit/601fc79921b3a4a6c34af80bdc561dd9c6f618ea
Author: Scorp (scorp at mrs.mn)
Date: 2026-06-06T21:37:57+10:00

Commit Message:
MM: MM1: Add classic PC speaker sounds

Changed paths:
  A engines/mm/shared/classic/pc_speaker.cpp
  A engines/mm/shared/classic/pc_speaker.h
    engines/mm/mm1/game/combat.cpp
    engines/mm/mm1/game/encounter.cpp
    engines/mm/mm1/sound.cpp
    engines/mm/mm1/sound.h
    engines/mm/mm1/views/combat.cpp
    engines/mm/mm1/views/interactions/hacker.cpp
    engines/mm/mm1/views/interactions/inspectron.cpp
    engines/mm/mm1/views/interactions/lord_ironfist.cpp
    engines/mm/mm1/views/interactions/prisoners.cpp
    engines/mm/mm1/views/interactions/prisoners.h
    engines/mm/mm1/views/search.cpp
    engines/mm/mm1/views/title.cpp
    engines/mm/module.mk


diff --git a/engines/mm/mm1/game/combat.cpp b/engines/mm/mm1/game/combat.cpp
index 3da28dc6cfa..b12442f46d6 100644
--- a/engines/mm/mm1/game/combat.cpp
+++ b/engines/mm/mm1/game/combat.cpp
@@ -900,6 +900,7 @@ void Combat::updateMonsterStatus() {
 	if (val <= 0) {
 		_monsterP->_hp = 0;
 		_monsterP->_status = MONFLAG_DEAD;
+		Sound::sound2(SOUND_9);
 
 	} else {
 		_monsterP->_hp = val;
@@ -1107,6 +1108,7 @@ void Combat::resetDestMonster() {
 
 void Combat::spellFailed() {
 	g_globals->_combatParty[_currentChar]->_checked = true;
+	Sound::sound(SOUND_2);
 
 	SoundMessage msg(10, 2, SpellCasting::spellResultMessage(STRING["spells.failed"]));
 	msg._delaySeconds = 3;
diff --git a/engines/mm/mm1/game/encounter.cpp b/engines/mm/mm1/game/encounter.cpp
index e6bdc19aaba..91520da80c0 100644
--- a/engines/mm/mm1/game/encounter.cpp
+++ b/engines/mm/mm1/game/encounter.cpp
@@ -25,6 +25,7 @@
 #include "mm/mm1/events.h"
 #include "mm/mm1/globals.h"
 #include "mm/mm1/mm1.h"
+#include "mm/mm1/sound.h"
 
 namespace MM {
 namespace MM1 {
@@ -34,6 +35,8 @@ void Encounter::execute() {
 	if (!g_globals->_encountersOn)
 		return;
 
+	Sound::sound2(SOUND_1);
+
 	Maps::Map &map = *g_maps->_currentMap;
 	int comp, maxRand, maxVal;
 	const Monster *monsterP;
diff --git a/engines/mm/mm1/sound.cpp b/engines/mm/mm1/sound.cpp
index 4a747e2cd54..a846c71e382 100644
--- a/engines/mm/mm1/sound.cpp
+++ b/engines/mm/mm1/sound.cpp
@@ -19,13 +19,117 @@
  *
  */
 
-#include "common/textconsole.h"
+#include "common/system.h"
 #include "mm/mm1/sound.h"
 #include "mm/mm1/mm1.h"
 
 namespace MM {
 namespace MM1 {
 
+static const uint32 kBiosTimerTickMicros = 54925;
+
+static const Shared::Classic::PitSequenceEntry kMM1Sound0[] = {
+	{ 1, 0x0a98 }, { 1, 0x0970 }, { 1, 0x08e8 }, { 1, 0x07ef },
+	{ 14, 0x0712 }, { 2, 0x0000 }, { 6, 0x0712 }, { 2, 0x0000 },
+	{ 6, 0x0712 }, { 1, 0x0000 }, { 1, 0x0e24 }, { 1, 0x0a98 },
+	{ 6, 0x08e8 }, { 2, 0x0000 }, { 4, 0x0a98 }, { 4, 0x0970 },
+	{ 4, 0x08e8 }, { 3, 0x07ef }, { 1, 0x0000 }, { 3, 0x0712 },
+	{ 1, 0x0000 }, { 2, 0x06ac }, { 1, 0x08e8 }, { 1, 0x0712 },
+	{ 8, 0x05f2 }, { 4, 0x06ac }, { 4, 0x0712 }, { 8, 0x06ac },
+	{ 4, 0x0712 }, { 2, 0x07ef }, { 1, 0x12e0 }, { 1, 0x0b3a },
+	{ 12, 0x0712 }, { 4, 0x06ac }, { 4, 0x0712 }, { 3, 0x07ef },
+	{ 1, 0x0000 }, { 3, 0x08e8 }, { 1, 0x0000 }, { 3, 0x0970 },
+	{ 1, 0x0000 }, { 14, 0x0a98 }, { 2, 0x0000 }, { 6, 0x0e24 },
+	{ 2, 0x0000 }, { 5, 0x0a98 }, { 1, 0x0000 }, { 1, 0x0e24 },
+	{ 1, 0x0be4 }, { 3, 0x08e8 }, { 1, 0x0000 }, { 3, 0x0be4 },
+	{ 1, 0x0000 }, { 2, 0x08e8 }, { 2, 0x0970 }, { 2, 0x08e8 },
+	{ 2, 0x07ef }, { 14, 0x0712 }, { 2, 0x0000 }, { 2, 0x06ac },
+	{ 2, 0x0712 }, { 2, 0x07ef }, { 2, 0x08e8 }, { 2, 0x0712 },
+	{ 2, 0x07ef }, { 2, 0x08e8 }, { 2, 0x0970 }, { 2, 0x07ef },
+	{ 2, 0x08e8 }, { 2, 0x0970 }, { 2, 0x0a98 }, { 2, 0x08e8 },
+	{ 2, 0x0970 }, { 2, 0x0a98 }, { 2, 0x0b3a }, { 4, 0x06ac },
+	{ 5, 0x0712 }, { 6, 0x07ef }, { 7, 0x08e8 }, { 2, 0x0e24 },
+	{ 3, 0x0a98 }, { 32, 0x0868 }, { 4, 0x0000 }, { 0, 0x0000 }
+};
+
+static const Shared::Classic::PitSequenceEntry kMM1Sound1[] = {
+	{ 2, 0x0be4 }, { 4, 0x0000 }, { 1, 0x08e8 }, { 1, 0x0000 },
+	{ 8, 0x05f2 }, { 0, 0x0000 }
+};
+
+static const Shared::Classic::PitSequenceEntry kMM1Sound2[] = {
+	{ 2, 0x0be4 }, { 4, 0x0000 }, { 1, 0x0be4 }, { 1, 0x0000 },
+	{ 1, 0x0be4 }, { 1, 0x0000 }, { 1, 0x0be4 }, { 1, 0x0000 },
+	{ 12, 0x07ef }, { 0, 0x0000 }
+};
+
+static const Shared::Classic::PitSequenceEntry kMM1Sound3[] = {
+	{ 1, 0x11d1 }, { 1, 0x0be4 }, { 3, 0x0712 }, { 6, 0x0000 },
+	{ 2, 0x0712 }, { 1, 0x0000 }, { 12, 0x0712 }, { 6, 0x0000 },
+	{ 3, 0x0712 }, { 3, 0x0000 }, { 2, 0x06ac }, { 2, 0x0000 },
+	{ 2, 0x0be4 }, { 2, 0x0000 }, { 2, 0x06ac }, { 1, 0x11d1 },
+	{ 1, 0x0be4 }, { 3, 0x0712 }, { 6, 0x0000 }, { 2, 0x0712 },
+	{ 1, 0x0000 }, { 12, 0x0712 }, { 6, 0x0000 }, { 3, 0x0712 },
+	{ 2, 0x0000 }, { 1, 0x17c8 }, { 1, 0x0be4 }, { 2, 0x07ef },
+	{ 2, 0x0000 }, { 2, 0x08e8 }, { 2, 0x0000 }, { 2, 0x07ef },
+	{ 2, 0x0000 }, { 1, 0x0e24 }, { 1, 0x0be4 }, { 3, 0x08e8 },
+	{ 6, 0x0000 }, { 2, 0x0be4 }, { 1, 0x0000 }, { 1, 0x8e84 },
+	{ 1, 0x5f1e }, { 1, 0x4742 }, { 1, 0x2f8f }, { 1, 0x23a2 },
+	{ 1, 0x17c8 }, { 1, 0x11d1 }, { 1, 0x0e24 }, { 1, 0x0be4 },
+	{ 12, 0x08e8 }, { 0, 0x0000 }
+};
+
+static const Shared::Classic::PitSequenceEntry kMM1Sound4[] = {
+	{ 16, 0x06ac }, { 3, 0x0000 }, { 4, 0x0868 }, { 4, 0x08e8 },
+	{ 4, 0x0970 }, { 16, 0x08e8 }, { 3, 0x0000 }, { 4, 0x0b3a },
+	{ 4, 0x0be4 }, { 4, 0x0b3a }, { 24, 0x0d59 }, { 0, 0x0000 }
+};
+
+static const Shared::Classic::PitSequenceEntry kMM1Sound5[] = {
+	{ 2, 0x8e84 }, { 2, 0x5f1e }, { 2, 0x4742 }, { 2, 0x2f8f },
+	{ 2, 0x23a2 }, { 2, 0x17c8 }, { 2, 0x11d1 }, { 2, 0x0be4 },
+	{ 2, 0x08e8 }, { 2, 0x05f2 }, { 2, 0x0474 }, { 2, 0x0389 },
+	{ 0, 0x0000 }
+};
+
+static const Shared::Classic::PitSequenceEntry kMM1Sound6[] = {
+	{ 6, 0x03e8 }, { 0, 0x0000 }
+};
+
+static const Shared::Classic::PitSequenceEntry kMM1Sound7[] = {
+	{ 8, 0x4e20 }, { 0, 0x0000 }
+};
+
+static const Shared::Classic::PitSequenceEntry kMM1Sound8[] = {
+	{ 8, 0x0b3a }, { 8, 0x0be4 }, { 8, 0x0c98 }, { 8, 0x0be4 },
+	{ 2, 0x0000 }, { 16, 0x0d59 }, { 0, 0x0000 }
+};
+
+static const Shared::Classic::PitSequenceEntry kMM1Sound9[] = {
+	{ 5, 0x08e8 }, { 1, 0x0000 }, { 1, 0x07ef }, { 1, 0x0000 },
+	{ 1, 0x0be4 }, { 1, 0x0000 }, { 1, 0x07ef }, { 1, 0x0000 },
+	{ 12, 0x0712 }, { 0, 0x0000 }
+};
+
+Sound::Sound(Audio::Mixer *mixer) : Shared::Xeen::Sound(mixer) {
+	_speaker.init();
+}
+
+void Sound::playSequence(const Shared::Classic::PitSequenceEntry *sequence, bool append, bool restart) {
+	if (!g_engine->_sound || !g_engine->_sound->_fxOn)
+		return;
+	if (g_engine->isEnhanced())
+		return;
+
+	if (restart)
+		g_engine->_sound->_speaker.stop();
+
+	if (!append && !restart && g_engine->_sound->_speaker.isPlaying())
+		return;
+
+	g_engine->_sound->_speaker.playPitSequence(sequence, kBiosTimerTickMicros, append);
+}
+
 void Sound::sound(SoundId soundNum) {
 	if (g_engine->isEnhanced()) {
 		if (soundNum == SOUND_1) {
@@ -34,15 +138,60 @@ void Sound::sound(SoundId soundNum) {
 		}
 	}
 
-	warning("TODO: sound %d", (int)soundNum);
+	if (!g_engine->_sound || !g_engine->_sound->_fxOn)
+		return;
+
+	switch (soundNum) {
+	case SOUND_1:
+		playSequence(kMM1Sound7, false, true);
+		break;
+	case SOUND_2:
+		playSequence(kMM1Sound6, false, true);
+		break;
+	case SOUND_3:
+		playSequence(kMM1Sound6, false, true);
+		playSequence(kMM1Sound6, true);
+		playSequence(kMM1Sound6, true);
+		break;
+	default:
+		break;
+	}
 }
 
 void Sound::sound2(SoundId soundNum) {
-	warning("TODO: sound2 %d", (int)soundNum);
+	switch (soundNum) {
+	case SOUND_TITLE:
+		playSequence(kMM1Sound0, true);
+		break;
+	case SOUND_1:
+		playSequence(kMM1Sound1, true);
+		break;
+	case SOUND_2:
+		playSequence(kMM1Sound2, true);
+		break;
+	case SOUND_3:
+		playSequence(kMM1Sound3, true);
+		break;
+	case SOUND_4:
+		playSequence(kMM1Sound4, true);
+		break;
+	case SOUND_5:
+		playSequence(kMM1Sound5, true);
+		break;
+	case SOUND_8:
+		playSequence(kMM1Sound8, true);
+		break;
+	case SOUND_9:
+		playSequence(kMM1Sound9);
+		break;
+	default:
+		break;
+	}
 }
 
 void Sound::stopSound() {
-	warning("TODO: stopSound");
+	if (g_engine->_sound)
+		g_engine->_sound->_speaker.stop();
 }
 
 } // namespace MM1
diff --git a/engines/mm/mm1/sound.h b/engines/mm/mm1/sound.h
index fb4c306085a..59e924ae728 100644
--- a/engines/mm/mm1/sound.h
+++ b/engines/mm/mm1/sound.h
@@ -23,18 +23,26 @@
 #define MM_MM1_SOUND_H
 
 #include "mm/shared/xeen/sound.h"
+#include "mm/shared/classic/pc_speaker.h"
 
 namespace MM {
 namespace MM1 {
 
 enum SoundId {
+	SOUND_TITLE = 0,
 	SOUND_1 = 1, SOUND_2 = 2, SOUND_3 = 3, SOUND_4 = 4,
 	SOUND_5 = 5, SOUND_8 = 8, SOUND_9 = 9
 };
 
 class Sound : public Shared::Xeen::Sound {
+private:
+	Shared::Classic::PcSpeaker _speaker;
+
+	static void playSequence(const Shared::Classic::PitSequenceEntry *sequence, bool append = false, bool restart = false);
+
 public:
-	Sound(Audio::Mixer *mixer) : Shared::Xeen::Sound(mixer) {}
+	Sound(Audio::Mixer *mixer);
+	~Sound() override {}
 
 	static void sound(SoundId soundNum);
 	static void sound2(SoundId soundNum);
diff --git a/engines/mm/mm1/views/combat.cpp b/engines/mm/mm1/views/combat.cpp
index b753a6968d2..9f1f5388897 100644
--- a/engines/mm/mm1/views/combat.cpp
+++ b/engines/mm/mm1/views/combat.cpp
@@ -91,6 +91,7 @@ bool Combat::msgGame(const GameMessage &msg) {
 		_spellResult._lines.clear();
 		_spellResult._lines.push_back(Line(msg._value, 1, msg._stringValue));
 		_spellResult._delaySeconds = 3;
+		Sound::sound(SOUND_2);
 
 		setMode(SPELL_RESULT);
 		return true;
diff --git a/engines/mm/mm1/views/interactions/hacker.cpp b/engines/mm/mm1/views/interactions/hacker.cpp
index a8fe1310947..5cf257dd9b0 100644
--- a/engines/mm/mm1/views/interactions/hacker.cpp
+++ b/engines/mm/mm1/views/interactions/hacker.cpp
@@ -43,6 +43,7 @@ bool Hacker::msgGame(const GameMessage &msg) {
 	if (_canAccept) {
 		// Show the view
 		Sound::sound(SOUND_2);
+		Sound::sound2(SOUND_2);
 		addView();
 
 	} else {
@@ -58,6 +59,7 @@ bool Hacker::msgGame(const GameMessage &msg) {
 		g_maps->_mapPos.x--;
 		map.redrawGame();
 
+		Sound::sound2(SOUND_2);
 		send(SoundMessage(
 			0, 1, STRING["maps.map36.hacker1"],
 			0, 2, line
diff --git a/engines/mm/mm1/views/interactions/inspectron.cpp b/engines/mm/mm1/views/interactions/inspectron.cpp
index 4abe339748e..d18251f51e3 100644
--- a/engines/mm/mm1/views/interactions/inspectron.cpp
+++ b/engines/mm/mm1/views/interactions/inspectron.cpp
@@ -46,6 +46,7 @@ bool Inspectron::msgGame(const GameMessage &msg) {
 	if (_canAccept) {
 		// Open the view for display
 		Sound::sound(SOUND_2);
+		Sound::sound2(SOUND_2);
 		addView();
 
 	} else {
@@ -63,6 +64,7 @@ bool Inspectron::msgGame(const GameMessage &msg) {
 		g_maps->_mapPos.y++;
 		map.redrawGame();
 
+		Sound::sound2(SOUND_2);
 		send(SoundMessage(
 			0, 1, STRING["maps.map35.inspectron1"],
 			0, 2, line
diff --git a/engines/mm/mm1/views/interactions/lord_ironfist.cpp b/engines/mm/mm1/views/interactions/lord_ironfist.cpp
index 987da0f36ed..427f07cb957 100644
--- a/engines/mm/mm1/views/interactions/lord_ironfist.cpp
+++ b/engines/mm/mm1/views/interactions/lord_ironfist.cpp
@@ -37,6 +37,7 @@ bool LordIronfist::msgFocus(const FocusMessage &msg) {
 	g_globals->_currCharacter = &g_globals->_party[0];
 	_canAccept = !g_globals->_currCharacter->_quest;
 	Sound::sound(SOUND_2);
+	Sound::sound2(SOUND_2);
 
 	return TextView::msgFocus(msg);
 }
@@ -46,7 +47,6 @@ void LordIronfist::draw() {
 	clearSurface();
 
 	if (_canAccept) {
-		Sound::sound2(SOUND_2);
 		writeString(0, 1, STRING["maps.map43.ironfist1"]);
 		writeString(0, 2, STRING["maps.map43.ironfist2"]);
 
diff --git a/engines/mm/mm1/views/interactions/prisoners.cpp b/engines/mm/mm1/views/interactions/prisoners.cpp
index b2e079b7e5b..3d7b30eed17 100644
--- a/engines/mm/mm1/views/interactions/prisoners.cpp
+++ b/engines/mm/mm1/views/interactions/prisoners.cpp
@@ -46,6 +46,11 @@ void Prisoner::draw() {
 	writeString(STRING["maps.prisoners.options3"]);
 }
 
+bool Prisoner::msgFocus(const FocusMessage &msg) {
+	Sound::sound2(SOUND_2);
+	return TextView::msgFocus(msg);
+}
+
 bool Prisoner::msgKeypress(const KeypressMessage &msg) {
 	if (endDelay())
 		return true;
diff --git a/engines/mm/mm1/views/interactions/prisoners.h b/engines/mm/mm1/views/interactions/prisoners.h
index 63fbe26abf1..b037fff3eea 100644
--- a/engines/mm/mm1/views/interactions/prisoners.h
+++ b/engines/mm/mm1/views/interactions/prisoners.h
@@ -44,6 +44,7 @@ public:
 	virtual ~Prisoner() {}
 
 	void draw() override;
+	bool msgFocus(const FocusMessage &msg) override;
 	bool msgKeypress(const KeypressMessage &msg) override;
 	void timeout() override;
 };
diff --git a/engines/mm/mm1/views/search.cpp b/engines/mm/mm1/views/search.cpp
index 19946c44922..21ada2243e5 100644
--- a/engines/mm/mm1/views/search.cpp
+++ b/engines/mm/mm1/views/search.cpp
@@ -421,6 +421,7 @@ void Search::drawItem() {
 				c._name,
 				item->_name.c_str()
 			));
+			Sound::sound2(SOUND_5);
 
 			if (treasure.hasItems()) {
 				delaySeconds(2);
diff --git a/engines/mm/mm1/views/title.cpp b/engines/mm/mm1/views/title.cpp
index 39211cef572..dd9be716574 100644
--- a/engines/mm/mm1/views/title.cpp
+++ b/engines/mm/mm1/views/title.cpp
@@ -68,10 +68,16 @@ bool Title::msgFocus(const FocusMessage &msg) {
 	_screenNum = -1;
 	_fadeIndex = 0;
 
+	if (!g_engine->isEnhanced())
+		Sound::sound2(SOUND_TITLE);
+
 	return true;
 }
 
 bool Title::msgUnfocus(const UnfocusMessage & msg) {
+	if (!g_engine->isEnhanced())
+		Sound::stopSound();
+
 	for (int i = 0; i < SCREENS_COUNT; ++i)
 		_screens[i].clear();
 
diff --git a/engines/mm/module.mk b/engines/mm/module.mk
index 5f69cbaaaf4..cc6c4b28fd4 100644
--- a/engines/mm/module.mk
+++ b/engines/mm/module.mk
@@ -4,6 +4,7 @@ MODULE_OBJS := \
 	metaengine.o \
 	mm.o \
 	shared/utils/bitmap_font.o \
+	shared/classic/pc_speaker.o \
 	shared/utils/strings.o \
 	shared/utils/strings_data.o \
 	shared/utils/xeen_font.o \
diff --git a/engines/mm/shared/classic/pc_speaker.cpp b/engines/mm/shared/classic/pc_speaker.cpp
new file mode 100644
index 00000000000..1834be138ec
--- /dev/null
+++ b/engines/mm/shared/classic/pc_speaker.cpp
@@ -0,0 +1,101 @@
+/* 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 "audio/softsynth/pcspk.h"
+#include "mm/shared/classic/pc_speaker.h"
+
+namespace MM {
+namespace Shared {
+namespace Classic {
+
+static const float kPitBaseFrequency = 1193182.0f;
+
+PcSpeaker::PcSpeaker() {
+	_speaker = new Audio::PCSpeaker();
+}
+
+PcSpeaker::~PcSpeaker() {
+	delete _speaker;
+}
+
+bool PcSpeaker::init() {
+	_ready = _speaker->init();
+	return _ready;
+}
+
+void PcSpeaker::stop() {
+	if (_ready)
+		_speaker->stop();
+}
+
+bool PcSpeaker::isPlaying() const {
+	return _ready && _speaker->isPlaying();
+}
+
+void PcSpeaker::playTone(int frequency, int32 lengthMs) {
+	if (_ready)
+		_speaker->play(Audio::PCSpeaker::kWaveFormSquare, frequency, lengthMs);
+}
+
+void PcSpeaker::queueTone(float frequency, uint32 lengthUs) {
+	if (_ready)
+		_speaker->playQueue(Audio::PCSpeaker::kWaveFormSquare, frequency, lengthUs);
+}
+
+void PcSpeaker::queueSilence(uint32 lengthUs) {
+	if (_ready)
+		_speaker->playQueue(Audio::PCSpeaker::kWaveFormSilence, 0.0f, lengthUs);
+}
+
+void PcSpeaker::playPitSequence(const PitSequenceEntry *sequence, uint32 tickMicros, bool append) {
+	if (!_ready || !sequence)
+		return;
+
+	if (!append)
+		stop();
+
+	for (const PitSequenceEntry *entry = sequence; entry->durationTicks != 0; ++entry) {
+		const uint32 lengthUs = entry->durationTicks * tickMicros;
+		if (entry->pitDivisor == 0) {
+			queueSilence(lengthUs);
+		} else {
+			queueTone(kPitBaseFrequency / entry->pitDivisor, lengthUs);
+		}
+	}
+}
+
+void PcSpeaker::playFrequencySequence(const FrequencySequenceEntry *sequence) {
+	if (!_ready || !sequence)
+		return;
+
+	stop();
+
+	for (const FrequencySequenceEntry *entry = sequence; entry->lengthUs != 0; ++entry) {
+		if (entry->frequency == 0.0f)
+			queueSilence(entry->lengthUs);
+		else
+			queueTone(entry->frequency, entry->lengthUs);
+	}
+}
+
+} // namespace Classic
+} // namespace Shared
+} // namespace MM
diff --git a/engines/mm/shared/classic/pc_speaker.h b/engines/mm/shared/classic/pc_speaker.h
new file mode 100644
index 00000000000..6f7f89f20f1
--- /dev/null
+++ b/engines/mm/shared/classic/pc_speaker.h
@@ -0,0 +1,69 @@
+/* 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 MM_SHARED_CLASSIC_PC_SPEAKER_H
+#define MM_SHARED_CLASSIC_PC_SPEAKER_H
+
+#include "common/scummsys.h"
+
+namespace Audio {
+class PCSpeaker;
+}
+
+namespace MM {
+namespace Shared {
+namespace Classic {
+
+struct PitSequenceEntry {
+	uint16 durationTicks;
+	uint16 pitDivisor;
+};
+
+struct FrequencySequenceEntry {
+	uint32 lengthUs;
+	float frequency;
+};
+
+class PcSpeaker {
+private:
+	Audio::PCSpeaker *_speaker = nullptr;
+	bool _ready = false;
+
+public:
+	PcSpeaker();
+	~PcSpeaker();
+
+	bool init();
+	void stop();
+	bool isPlaying() const;
+
+	void playTone(int frequency, int32 lengthMs);
+	void queueTone(float frequency, uint32 lengthUs);
+	void queueSilence(uint32 lengthUs);
+	void playPitSequence(const PitSequenceEntry *sequence, uint32 tickMicros, bool append = false);
+	void playFrequencySequence(const FrequencySequenceEntry *sequence);
+};
+
+} // namespace Classic
+} // namespace Shared
+} // namespace MM
+
+#endif


Commit: 30eb8f2e15b229fb21fe8056748ac59ce7a8cb98
    https://github.com/scummvm/scummvm/commit/30eb8f2e15b229fb21fe8056748ac59ce7a8cb98
Author: Scorp (scorp at mrs.mn)
Date: 2026-06-06T21:37:57+10:00

Commit Message:
MM: MM1: Update classic PC speaker playback

Changed paths:
    engines/mm/mm1/events.cpp
    engines/mm/mm1/sound.cpp
    engines/mm/mm1/sound.h
    engines/mm/shared/classic/pc_speaker.cpp
    engines/mm/shared/classic/pc_speaker.h


diff --git a/engines/mm/mm1/events.cpp b/engines/mm/mm1/events.cpp
index 197e6823d54..91ffb1a673f 100644
--- a/engines/mm/mm1/events.cpp
+++ b/engines/mm/mm1/events.cpp
@@ -24,6 +24,7 @@
 #include "mm/mm1/events.h"
 #include "mm/mm1/mm1.h"
 #include "mm/mm1/gfx/gfx.h"
+#include "mm/mm1/sound.h"
 #include "mm/mm1/views/dialogs.h"
 #include "mm/mm1/views_enh/dialogs.h"
 
@@ -74,6 +75,7 @@ void Events::runGame() {
 		if ((currTime = g_system->getMillis()) >= nextFrameTime) {
 			nextFrameTime = currTime + FRAME_DELAY;
 			tick();
+			Sound::update();
 			drawElements();
 			_screen->update();
 		}
diff --git a/engines/mm/mm1/sound.cpp b/engines/mm/mm1/sound.cpp
index a846c71e382..1e50695144f 100644
--- a/engines/mm/mm1/sound.cpp
+++ b/engines/mm/mm1/sound.cpp
@@ -115,7 +115,7 @@ Sound::Sound(Audio::Mixer *mixer) : Shared::Xeen::Sound(mixer) {
 	_speaker.init();
 }
 
-void Sound::playSequence(const Shared::Classic::PitSequenceEntry *sequence, bool append, bool restart) {
+void Sound::playSequence(const Shared::Classic::PitSequenceEntry *sequence, bool append, bool restart, bool loop) {
 	if (!g_engine->_sound || !g_engine->_sound->_fxOn)
 		return;
 	if (g_engine->isEnhanced())
@@ -127,7 +127,7 @@ void Sound::playSequence(const Shared::Classic::PitSequenceEntry *sequence, bool
 	if (!append && !restart && g_engine->_sound->_speaker.isPlaying())
 		return;
 
-	g_engine->_sound->_speaker.playPitSequence(sequence, kBiosTimerTickMicros, append);
+	g_engine->_sound->_speaker.playPitSequence(sequence, kBiosTimerTickMicros, append, loop);
 }
 
 void Sound::sound(SoundId soundNum) {
@@ -161,10 +161,10 @@ void Sound::sound(SoundId soundNum) {
 void Sound::sound2(SoundId soundNum) {
 	switch (soundNum) {
 	case SOUND_TITLE:
-		playSequence(kMM1Sound0, true);
+		playSequence(kMM1Sound0, false, false, true);
 		break;
 	case SOUND_1:
-		playSequence(kMM1Sound1, true);
+		playSequence(kMM1Sound1, false, true);
 		break;
 	case SOUND_2:
 		playSequence(kMM1Sound2, true);
@@ -189,9 +189,15 @@ void Sound::sound2(SoundId soundNum) {
 	}
 }
 
-void Sound::stopSound() {
+void Sound::update() {
 	if (g_engine->_sound)
+		g_engine->_sound->_speaker.update();
+}
+
+void Sound::stopSound() {
+	if (g_engine->_sound) {
 		g_engine->_sound->_speaker.stop();
+	}
 }
 
 } // namespace MM1
diff --git a/engines/mm/mm1/sound.h b/engines/mm/mm1/sound.h
index 59e924ae728..e03decdc8fd 100644
--- a/engines/mm/mm1/sound.h
+++ b/engines/mm/mm1/sound.h
@@ -38,7 +38,7 @@ class Sound : public Shared::Xeen::Sound {
 private:
 	Shared::Classic::PcSpeaker _speaker;
 
-	static void playSequence(const Shared::Classic::PitSequenceEntry *sequence, bool append = false, bool restart = false);
+	static void playSequence(const Shared::Classic::PitSequenceEntry *sequence, bool append = false, bool restart = false, bool loop = false);
 
 public:
 	Sound(Audio::Mixer *mixer);
@@ -46,6 +46,7 @@ public:
 
 	static void sound(SoundId soundNum);
 	static void sound2(SoundId soundNum);
+	static void update();
 	static void stopSound();
 };
 
diff --git a/engines/mm/shared/classic/pc_speaker.cpp b/engines/mm/shared/classic/pc_speaker.cpp
index 1834be138ec..018c9232f59 100644
--- a/engines/mm/shared/classic/pc_speaker.cpp
+++ b/engines/mm/shared/classic/pc_speaker.cpp
@@ -42,6 +42,9 @@ bool PcSpeaker::init() {
 }
 
 void PcSpeaker::stop() {
+	_loopingPitSequence = nullptr;
+	_loopingPitTickMicros = 0;
+
 	if (_ready)
 		_speaker->stop();
 }
@@ -65,13 +68,7 @@ void PcSpeaker::queueSilence(uint32 lengthUs) {
 		_speaker->playQueue(Audio::PCSpeaker::kWaveFormSilence, 0.0f, lengthUs);
 }
 
-void PcSpeaker::playPitSequence(const PitSequenceEntry *sequence, uint32 tickMicros, bool append) {
-	if (!_ready || !sequence)
-		return;
-
-	if (!append)
-		stop();
-
+void PcSpeaker::queuePitSequence(const PitSequenceEntry *sequence, uint32 tickMicros) {
 	for (const PitSequenceEntry *entry = sequence; entry->durationTicks != 0; ++entry) {
 		const uint32 lengthUs = entry->durationTicks * tickMicros;
 		if (entry->pitDivisor == 0) {
@@ -82,6 +79,18 @@ void PcSpeaker::playPitSequence(const PitSequenceEntry *sequence, uint32 tickMic
 	}
 }
 
+void PcSpeaker::playPitSequence(const PitSequenceEntry *sequence, uint32 tickMicros, bool append, bool loop) {
+	if (!_ready || !sequence)
+		return;
+
+	if (!append)
+		stop();
+
+	_loopingPitSequence = loop ? sequence : nullptr;
+	_loopingPitTickMicros = loop ? tickMicros : 0;
+	queuePitSequence(sequence, tickMicros);
+}
+
 void PcSpeaker::playFrequencySequence(const FrequencySequenceEntry *sequence) {
 	if (!_ready || !sequence)
 		return;
@@ -96,6 +105,11 @@ void PcSpeaker::playFrequencySequence(const FrequencySequenceEntry *sequence) {
 	}
 }
 
+void PcSpeaker::update() {
+	if (_loopingPitSequence && !isPlaying())
+		queuePitSequence(_loopingPitSequence, _loopingPitTickMicros);
+}
+
 } // namespace Classic
 } // namespace Shared
 } // namespace MM
diff --git a/engines/mm/shared/classic/pc_speaker.h b/engines/mm/shared/classic/pc_speaker.h
index 6f7f89f20f1..9a558d701f6 100644
--- a/engines/mm/shared/classic/pc_speaker.h
+++ b/engines/mm/shared/classic/pc_speaker.h
@@ -45,8 +45,12 @@ struct FrequencySequenceEntry {
 class PcSpeaker {
 private:
 	Audio::PCSpeaker *_speaker = nullptr;
+	const PitSequenceEntry *_loopingPitSequence = nullptr;
+	uint32 _loopingPitTickMicros = 0;
 	bool _ready = false;
 
+	void queuePitSequence(const PitSequenceEntry *sequence, uint32 tickMicros);
+
 public:
 	PcSpeaker();
 	~PcSpeaker();
@@ -58,8 +62,9 @@ public:
 	void playTone(int frequency, int32 lengthMs);
 	void queueTone(float frequency, uint32 lengthUs);
 	void queueSilence(uint32 lengthUs);
-	void playPitSequence(const PitSequenceEntry *sequence, uint32 tickMicros, bool append = false);
+	void playPitSequence(const PitSequenceEntry *sequence, uint32 tickMicros, bool append = false, bool loop = false);
 	void playFrequencySequence(const FrequencySequenceEntry *sequence);
+	void update();
 };
 
 } // namespace Classic


Commit: f6c38ae6fbf0110e20e4e6eb6a867f767a9e1000
    https://github.com/scummvm/scummvm/commit/f6c38ae6fbf0110e20e4e6eb6a867f767a9e1000
Author: Scorp (scorp at mrs.mn)
Date: 2026-06-06T21:37:57+10:00

Commit Message:
MM: MM1: Add classic sound test command

Changed paths:
    engines/mm/mm1/console.cpp
    engines/mm/mm1/console.h


diff --git a/engines/mm/mm1/console.cpp b/engines/mm/mm1/console.cpp
index 78e36c45977..958f1999027 100644
--- a/engines/mm/mm1/console.cpp
+++ b/engines/mm/mm1/console.cpp
@@ -22,15 +22,371 @@
 #include "common/file.h"
 #include "common/savefile.h"
 #include "common/system.h"
+#include "common/util.h"
 #include "mm/shared/utils/strings.h"
 #include "mm/mm1/console.h"
 #include "mm/mm1/globals.h"
 #include "mm/mm1/events.h"
 #include "mm/mm1/game/spells_party.h"
+#include "mm/mm1/messages.h"
+#include "mm/mm1/sound.h"
 
 namespace MM {
 namespace MM1 {
 
+namespace {
+
+const char *const SOUND_TESTS[] = {
+	"wall",
+	"message",
+	"triple",
+	"encounter",
+	"combat",
+	"search",
+	"cast",
+	"death"
+};
+
+const SoundId SOUND_TEST_SOUND_IDS[] = {
+	SOUND_1, SOUND_2, SOUND_3
+};
+
+const SoundId SOUND_TEST_SOUND2_IDS[] = {
+	SOUND_TITLE, SOUND_1, SOUND_2, SOUND_3, SOUND_4, SOUND_5, SOUND_8, SOUND_9
+};
+
+class SoundTestRunner;
+SoundTestRunner *g_soundTestRunner = nullptr;
+
+void faceDirection(Maps::DirMask dir) {
+	Maps::Maps &maps = g_globals->_maps;
+
+	for (int i = 0; i < 4 && maps._forwardMask != dir; ++i)
+		maps.turnLeft();
+}
+
+bool stageWallBump() {
+	Maps::Maps &maps = g_globals->_maps;
+	maps.loadTown(Maps::SORPIGAL);
+	g_globals->_intangible = false;
+
+	static const Maps::DirMask DIRS[] = {
+		Maps::DIRMASK_N, Maps::DIRMASK_E, Maps::DIRMASK_S, Maps::DIRMASK_W
+	};
+
+	for (int y = 0; y < MAP_H; ++y) {
+		for (int x = 0; x < MAP_W; ++x) {
+			const uint offset = y * MAP_W + x;
+			if (maps._currentMap->_states[offset] & Maps::CELL_SPECIAL)
+				continue;
+
+			for (uint i = 0; i < ARRAYSIZE(DIRS); ++i) {
+				const Maps::DirMask dir = DIRS[i];
+				if (!(maps._currentMap->_walls[offset] & dir) ||
+						!(maps._currentMap->_states[offset] & 0x55 & dir))
+					continue;
+
+				maps._mapPos = Common::Point(x, y);
+				faceDirection(dir);
+				maps._mapOffset = offset;
+				maps._currentWalls = maps._currentMap->_walls[offset];
+				maps._currentState = maps._currentMap->_states[offset];
+				g_events->send("View", ActionMessage(KEYBIND_FORWARDS));
+				return true;
+			}
+		}
+	}
+
+	return false;
+}
+
+Character *findSpellTester() {
+	for (uint i = 0; i < g_globals->_party.size(); ++i) {
+		Character &c = g_globals->_party[i];
+		if (c._class == CLERIC || c._class == SORCERER || c._class == ARCHER)
+			return &c;
+	}
+
+	return g_globals->_party.empty() ? nullptr : &g_globals->_party[0];
+}
+
+void stageSpellDone() {
+	Character *c = findSpellTester();
+	if (!c)
+		return;
+
+	g_globals->_currCharacter = c;
+	c->_class = SORCERER;
+	c->_spellLevel = 7;
+	c->_sp = 100;
+	c->_gems = 100;
+
+	int spellIndex = Console::getSpellIndex(c, 1, 5);
+	g_events->send("CastSpell", GameMessage("SPELL", spellIndex));
+}
+
+void stageSearchTreasure() {
+	g_globals->_treasure.clear();
+	g_globals->_treasure._container = CLOTH_SACK;
+	g_globals->_treasure._trapType = 2;
+	g_globals->_treasure.setGold(6);
+	g_globals->_treasure.setGems(0);
+	g_events->send("Search", GameMessage("SHOW"));
+}
+
+void stageEncounter() {
+	if (g_globals->_party.empty()) {
+		warning("MM1 sound test: encounter requires an active party");
+		return;
+	}
+
+	Game::Encounter &enc = g_globals->_encounters;
+	const bool encountersOn = g_globals->_encountersOn;
+	g_globals->_encountersOn = true;
+
+	enc.clearMonsters();
+	enc.addMonster(1, 1);
+	enc._manual = true;
+	enc._levelIndex = 80;
+	enc._encounterType = Game::FORCE_SURPRISED;
+	enc.execute();
+
+	g_globals->_encountersOn = encountersOn;
+}
+
+void stageTripleBeepSpecial() {
+	g_events->send("GameMessages", InfoMessage(
+		0, 1, STRING["maps.map13.snake_pit"],
+		0, 2, STRING["maps.map13.levitation2"]
+	));
+	Sound::sound(SOUND_3);
+}
+
+void stageCombatKill() {
+	Character *c = g_globals->_party.empty() ? nullptr : &g_globals->_party[0];
+	if (!c)
+		return;
+
+	c->_class = KNIGHT;
+	c->_condition = FINE;
+	c->_level = 50;
+	c->_hp = c->_hpCurrent = c->_hpMax = 200;
+	c->_might = 50;
+	c->_accuracy = 50;
+	c->_speed = 50;
+	c->_physicalAttr = 50;
+	g_globals->_currCharacter = c;
+
+	stageEncounter();
+	g_events->send("Combat", GameMessage("COMBAT"));
+}
+
+bool runSoundTestCase(const Common::String &testName) {
+	if (!scumm_stricmp(testName.c_str(), "wall")) {
+		return stageWallBump();
+
+	} else if (!scumm_stricmp(testName.c_str(), "message")) {
+		g_events->send("GameMessages", SoundMessage(STRING["dialogs.search.nothing"]));
+		return true;
+
+	} else if (!scumm_stricmp(testName.c_str(), "triple")) {
+		stageTripleBeepSpecial();
+		return true;
+
+	} else if (!scumm_stricmp(testName.c_str(), "encounter")) {
+		stageEncounter();
+		return true;
+
+	} else if (!scumm_stricmp(testName.c_str(), "combat")) {
+		stageCombatKill();
+		return true;
+
+	} else if (!scumm_stricmp(testName.c_str(), "search")) {
+		stageSearchTreasure();
+		return true;
+
+	} else if (!scumm_stricmp(testName.c_str(), "death") ||
+			!scumm_stricmp(testName.c_str(), "dead")) {
+		g_events->addView("Dead");
+		return true;
+
+	} else if (!scumm_stricmp(testName.c_str(), "cast")) {
+		stageSpellDone();
+		return true;
+	}
+
+	return false;
+}
+
+bool parseSoundId(const char *str, SoundId &soundId) {
+	char *end = nullptr;
+	const long value = strtol(str, &end, 10);
+
+	if (!str[0] || *end)
+		return false;
+
+	switch (value) {
+	case SOUND_TITLE:
+	case SOUND_1:
+	case SOUND_2:
+	case SOUND_3:
+	case SOUND_4:
+	case SOUND_5:
+	case SOUND_8:
+	case SOUND_9:
+		soundId = (SoundId)value;
+		return true;
+	default:
+		return false;
+	}
+}
+
+bool isSoundIdSupported(const SoundId *ids, uint count, SoundId soundId) {
+	for (uint i = 0; i < count; ++i) {
+		if (ids[i] == soundId)
+			return true;
+	}
+
+	return false;
+}
+
+bool runDirectSoundTest(const Common::String &routine, SoundId soundId) {
+	if (!scumm_stricmp(routine.c_str(), "sound")) {
+		if (!isSoundIdSupported(SOUND_TEST_SOUND_IDS, ARRAYSIZE(SOUND_TEST_SOUND_IDS), soundId))
+			return false;
+
+		Sound::sound(soundId);
+		return true;
+	}
+
+	if (!scumm_stricmp(routine.c_str(), "sound2")) {
+		if (!isSoundIdSupported(SOUND_TEST_SOUND2_IDS, ARRAYSIZE(SOUND_TEST_SOUND2_IDS), soundId))
+			return false;
+
+		Sound::sound2(soundId);
+		return true;
+	}
+
+	return false;
+}
+
+bool runDirectSoundTestAlias(const Common::String &testName) {
+	const char *name = testName.c_str();
+	SoundId soundId;
+
+	if (!strncmp(name, "sound2-", 7) && parseSoundId(name + 7, soundId))
+		return runDirectSoundTest("sound2", soundId);
+
+	if (!strncmp(name, "sound-", 6) && parseSoundId(name + 6, soundId))
+		return runDirectSoundTest("sound", soundId);
+
+	return false;
+}
+
+void soundTestPromptAccepted();
+
+class SoundTestRunner : public UIElement {
+private:
+	enum State {
+		ST_INACTIVE,
+		ST_PROMPT,
+		ST_RUN,
+		ST_WAIT
+	};
+
+	State _state = ST_INACTIVE;
+	uint _testIndex = 0;
+	uint32 _waitUntil = 0;
+	bool _promptOpen = false;
+	bool _promptAccepted = false;
+
+	void nextTest() {
+		++_testIndex;
+		_promptOpen = false;
+		_promptAccepted = false;
+
+		if (_testIndex >= ARRAYSIZE(SOUND_TESTS)) {
+			_state = ST_INACTIVE;
+			close();
+		} else {
+			_state = ST_PROMPT;
+		}
+	}
+
+	uint32 waitMillisForTest(const Common::String &testName) const {
+		if (!scumm_stricmp(testName.c_str(), "encounter"))
+			return 5000;
+		if (!scumm_stricmp(testName.c_str(), "combat"))
+			return 8000;
+		if (!scumm_stricmp(testName.c_str(), "cast"))
+			return 3000;
+		if (!scumm_stricmp(testName.c_str(), "search"))
+			return 3000;
+		return 2000;
+	}
+
+public:
+	SoundTestRunner(UIElement *owner) : UIElement("SoundTestRunner", owner) {}
+
+	void start() {
+		_state = ST_PROMPT;
+		_testIndex = 0;
+		_promptOpen = false;
+		_promptAccepted = false;
+		_waitUntil = 0;
+	}
+
+	void promptAccepted() {
+		_promptAccepted = true;
+	}
+
+	bool tick() override {
+		if (_state == ST_INACTIVE)
+			return false;
+
+		const Common::String testName = SOUND_TESTS[_testIndex];
+
+		switch (_state) {
+		case ST_PROMPT:
+			if (!_promptOpen) {
+				_promptOpen = true;
+				g_events->send("GameMessages", InfoMessage(
+					0, 3,
+					Common::String::format("proceed with %s test", testName.c_str()),
+					soundTestPromptAccepted));
+				return true;
+			}
+
+			if (_promptAccepted)
+				_state = ST_RUN;
+			return true;
+
+		case ST_RUN:
+			runSoundTestCase(testName);
+			_waitUntil = g_system->getMillis() + waitMillisForTest(testName);
+			_state = ST_WAIT;
+			return true;
+
+		case ST_WAIT:
+			if (g_system->getMillis() >= _waitUntil)
+				nextTest();
+			return true;
+
+		default:
+			break;
+		}
+
+		return false;
+	}
+};
+
+void soundTestPromptAccepted() {
+	if (g_soundTestRunner)
+		g_soundTestRunner->promptAccepted();
+}
+
+} // namespace
+
 Console::Console() : GUI::Debugger() {
 	registerCmd("dump_map", WRAP_METHOD(Console, cmdDumpMap));
 	registerCmd("dump_monsters", WRAP_METHOD(Console, cmdDumpMonsters));
@@ -47,6 +403,28 @@ Console::Console() : GUI::Debugger() {
 	registerCmd("specials", WRAP_METHOD(Console, cmdSpecials));
 	registerCmd("special", WRAP_METHOD(Console, cmdSpecial));
 	registerCmd("view", WRAP_METHOD(Console, cmdView));
+	registerCmd("sound", WRAP_METHOD(Console, cmdSoundTest));
+}
+
+Console::~Console() {
+	if (g_soundTestRunner == _soundTestRunner)
+		g_soundTestRunner = nullptr;
+	delete _soundTestRunner;
+}
+
+void Console::postEnter() {
+	GUI::Debugger::postEnter();
+
+	if (_pendingSoundTest) {
+		_pendingSoundTest = false;
+		if (!_soundTestRunner) {
+			UIElement *game = g_events->findView("Game");
+			assert(game);
+			_soundTestRunner = new SoundTestRunner(game);
+			g_soundTestRunner = static_cast<SoundTestRunner *>(_soundTestRunner);
+		}
+		static_cast<SoundTestRunner *>(_soundTestRunner)->start();
+	}
 }
 
 bool Console::cmdDumpMap(int argc, const char **argv) {
@@ -489,5 +867,49 @@ bool Console::cmdView(int argc, const char **argv) {
 	}
 }
 
+bool Console::cmdSoundTest(int argc, const char **argv) {
+	Common::String testName;
+	SoundId soundId;
+
+	if (argc > 1 && !scumm_stricmp(argv[1], "list")) {
+		debugPrintf("sound [test|list|wall|message|triple|encounter|combat|search|cast|death]\n");
+		debugPrintf("sound sound <1|2|3>\n");
+		debugPrintf("sound sound2 <0|1|2|3|4|5|8|9>\n");
+		debugPrintf("  sound <name> stages one gameplay event for classic sound testing.\n");
+		debugPrintf("  sound test runs the staged checks interactively.\n");
+		debugPrintf("  sound sound <id> calls the original sound(id) routine directly.\n");
+		debugPrintf("  sound sound2 <id> calls the original sound2(id) routine directly.\n");
+		for (uint i = 0; i < ARRAYSIZE(SOUND_TESTS); ++i)
+			debugPrintf("  %u: %s\n", i + 1, SOUND_TESTS[i]);
+		for (uint i = 0; i < ARRAYSIZE(SOUND_TEST_SOUND_IDS); ++i)
+			debugPrintf("  sound-%d\n", SOUND_TEST_SOUND_IDS[i]);
+		for (uint i = 0; i < ARRAYSIZE(SOUND_TEST_SOUND2_IDS); ++i)
+			debugPrintf("  sound2-%d\n", SOUND_TEST_SOUND2_IDS[i]);
+		return true;
+	}
+
+	if (argc == 1 || !scumm_stricmp(argv[1], "test")) {
+		_pendingSoundTest = true;
+		return false;
+	}
+
+	if (argc == 3 && parseSoundId(argv[2], soundId)) {
+		if (runDirectSoundTest(argv[1], soundId))
+			return false;
+
+		debugPrintf("Sound routine '%s' does not support id %d\n", argv[1], soundId);
+		debugPrintf("Use 'sound list' to show known tests.\n");
+		return true;
+	}
+
+	testName = argv[1];
+	if (runDirectSoundTestAlias(testName) || runSoundTestCase(testName))
+		return false;
+
+	debugPrintf("Unknown sound test '%s'\n", argv[1]);
+	debugPrintf("Use 'sound list' to show known tests.\n");
+	return true;
+}
+
 } // namespace MM1
 } // namespace MM
diff --git a/engines/mm/mm1/console.h b/engines/mm/mm1/console.h
index 5c180eaff44..05d9a7b7375 100644
--- a/engines/mm/mm1/console.h
+++ b/engines/mm/mm1/console.h
@@ -28,8 +28,16 @@
 namespace MM {
 namespace MM1 {
 
+class UIElement;
+
 class Console : public GUI::Debugger, public MM1::Game::SpellCasting {
+private:
+	bool _pendingSoundTest = false;
+	UIElement *_soundTestRunner = nullptr;
+
 protected:
+	void postEnter() override;
+
 	/**
 	 * Used to dump a map's code and data
 	 */
@@ -105,9 +113,14 @@ protected:
 	 */
 	bool cmdDumpRoster(int argc, const char **argv);
 
+	/**
+	 * Stages gameplay events used to test classic sounds
+	 */
+	bool cmdSoundTest(int argc, const char **argv);
+
 public:
 	Console();
-	~Console() override {}
+	~Console() override;
 };
 
 } // namespace MM1



From noreply at scummvm.org  Sat Jun  6 11:38:54 2026
From: noreply at scummvm.org (dreammaster)
Date: Sat,  6 Jun 2026 11:38:54 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 5502b6690f1f52b13e29f19ea39c6f25746438b2
Message-ID: <20260606113854.11709700573@ron.scummvm.net>

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

Summary:
5502b6690f MM: MM1: Fix classic combat target crashes


Commit: 5502b6690f1f52b13e29f19ea39c6f25746438b2
    https://github.com/scummvm/scummvm/commit/5502b6690f1f52b13e29f19ea39c6f25746438b2
Author: Scorp (scorp at mrs.mn)
Date: 2026-06-06T21:38:50+10:00

Commit Message:
MM: MM1: Fix classic combat target crashes

Changed paths:
    engines/mm/mm1/game/combat.cpp
    engines/mm/mm1/views/combat.cpp


diff --git a/engines/mm/mm1/game/combat.cpp b/engines/mm/mm1/game/combat.cpp
index b12442f46d6..cb04d480564 100644
--- a/engines/mm/mm1/game/combat.cpp
+++ b/engines/mm/mm1/game/combat.cpp
@@ -673,7 +673,7 @@ void Combat::checkMonsterActions() {
 	_monsterP->_counterFlags--;
 
 	// Pick a random character to shoot at
-	int charNum = getRandomNumber(g_globals->_party.size()) - 1;
+	int charNum = CLIP<int>(getRandomNumber(g_globals->_party.size()) - 1, 0, (int)g_globals->_party.size() - 1);
 	Character &c = g_globals->_party[charNum];
 	g_globals->_currCharacter = &c;
 
diff --git a/engines/mm/mm1/views/combat.cpp b/engines/mm/mm1/views/combat.cpp
index 9f1f5388897..1240991a755 100644
--- a/engines/mm/mm1/views/combat.cpp
+++ b/engines/mm/mm1/views/combat.cpp
@@ -190,6 +190,9 @@ void Combat::draw() {
 }
 
 void Combat::timeout() {
+	if (!isFocused())
+		return;
+
 	switch (_mode) {
 	case NEXT_ROUND:
 		nextRound2();



From noreply at scummvm.org  Sat Jun  6 12:21:11 2026
From: noreply at scummvm.org (neuromancer)
Date: Sat,  6 Jun 2026 12:21:11 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 ccd5a4ecac9740d11e2095da0c26dbe16a0d8746
Message-ID: <20260606122111.5A8E3700573@ron.scummvm.net>

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:
aa8ea34af2 SCUMM: RA1: padding in level names in level selection menu
9bfbce7dd0 SCUMM: RA1: implement damage in L9
ccd5a4ecac SCUMM: RA1: better controls for L9


Commit: aa8ea34af20cbed6d6c16c9ad30afade426f2080
    https://github.com/scummvm/scummvm/commit/aa8ea34af20cbed6d6c16c9ad30afade426f2080
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-06T14:03:54+02:00

Commit Message:
SCUMM: RA1: padding in level names in level selection menu

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 7a67f39b3ac..f0613e5c408 100644
--- a/engines/scumm/insane/rebel1/menu.cpp
+++ b/engines/scumm/insane/rebel1/menu.cpp
@@ -1077,39 +1077,45 @@ void InsaneRebel1::renderLevelSelectOverlay(byte *dst, int pitch, int width, int
 	const int titleW = getFontBankStringWidth("LEVEL SELECT");
 	drawMenuTitleText(dst, pitch, width, height, getRebel1MenuCenteredX(titleW), 15, "LEVEL SELECT");
 
-	const char *kLevelItems[kRA1LevelSelectItemCount] = {
-		" 1 TRAINING",
-		" 2 ASTEROIDS",
-		" 3 KOLAADOR",
-		" 4 STAR DESTR",
-		" 5 TATOOINE",
-		" 6 AST CHASE",
-		" 7 PROBES",
-		" 8 WALKERS",
-		" 9 TROOPERS",
-		"10 TRANSPORT",
-		"11 YAVIN",
-		"12 TIE ATK",
+	const char *const kLevelItems[kRA1NumLevels] = {
+		"1  TRAINING  ",
+		"2  ASTEROIDS ",
+		"3  KOLAADOR  ",
+		"4  STAR DESTR",
+		"5  TATOOINE  ",
+		"6  AST CHASE ",
+		"7  PROBES    ",
+		"8  WALKERS   ",
+		"9  TROOPERS  ",
+		"10 TRANSPORT ",
+		"11 YAVIN     ",
+		"12 TIE ATK   ",
 		"13 DS SURFACE",
-		"14 CANNON",
-		"15 DS TRENCH",
-		"BACK"
+		"14 CANNON    ",
+		"15 DS TRENCH "
 	};
+	const char *const kBackItem = "BACK";
 
 	const int menuY = 0x2d;
 	const int leftFrameX = kRA1LevelSelectLeftX;
 	const int rightFrameX = kRA1LevelSelectRightX;
 	const int columnW = kRA1LevelSelectColW;
+	int levelTextW = 0;
+
+	for (int i = 0; i < kRA1NumLevels; i++)
+		levelTextW = MAX(levelTextW, getMenuTalkTextWidth(kLevelItems[i]));
 
 	for (int i = 0; i < kRA1LevelSelectItemCount; i++) {
 		const int col = i / kRA1LevelSelectRowsPerCol;
 		const int row = i % kRA1LevelSelectRowsPerCol;
 		const int frameX = (col == 0) ? leftFrameX : rightFrameX;
 		const int y = menuY + row * kRA1MenuRowH;
-		const int textW = getMenuTalkTextWidth(kLevelItems[i]);
+		const bool levelItem = i < kRA1NumLevels;
+		const char *text = levelItem ? kLevelItems[i] : kBackItem;
+		const int textW = levelItem ? levelTextW : getMenuTalkTextWidth(text);
 		const int textX = frameX + (columnW - textW) / 2;
 
-		drawMenuTalkText(dst, pitch, width, height, textX, y, kLevelItems[i]);
+		drawMenuTalkText(dst, pitch, width, height, textX, y, text);
 
 		if (i == _levelSelectSel)
 			drawRebel1MenuFrame(dst, pitch, width, height,


Commit: 9bfbce7dd0fdca768cf334b41fd473ceb827485d
    https://github.com/scummvm/scummvm/commit/9bfbce7dd0fdca768cf334b41fd473ceb827485d
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-06T14:03:54+02:00

Commit Message:
SCUMM: RA1: implement damage in L9

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 41232a893bd..614338dd842 100644
--- a/engines/scumm/insane/rebel1/iact.cpp
+++ b/engines/scumm/insane/rebel1/iact.cpp
@@ -1907,9 +1907,11 @@ void InsaneRebel1::updateOnFootSequence() {
 			_shipDirIndex = 4;  // Walk left
 	}
 
-	// --- Scripted damage latches ??? damageFlags (matching FUN_1B297 pattern) ---
-	// GAME 0x5D/0x5F set latches; convert to damage flags before the check.
-	if (_gameLatch5D == 0xFFFF)
+	// --- Scripted damage latches ??? damageFlags ---
+	// L9 on-foot trooper shots use ordinary 0x5D event ids, gated by the
+	// 0x5D object bitmask handler, then consumed by FUN_1ED95 through 0x74D4.
+	// The ship loops keep narrower level-specific 0x5D damage rules.
+	if (_gameLatch5D != 0)
 		_damageFlags |= 0x40;
 	if (_gameLatch5F != 0 &&
 		_vm->_rnd.getRandomNumber((uint16)(_gameLatch5F - 1)) == 0)
@@ -1919,6 +1921,7 @@ void InsaneRebel1::updateOnFootSequence() {
 	// On-foot damage uses the same heavy-damage tuning byte as ship shot/collision
 	// damage in the original, not the miss penalty.
 	if (_damageFlags != 0 && _damageCooldown == 0 && _health >= 0 && _deathTimer < 1) {
+		const int16 oldHealth = _health;
 		_health -= _tuning.shot;
 		if (_health < 0) {
 			_deathTimer = 15;
@@ -1928,6 +1931,8 @@ void InsaneRebel1::updateOnFootSequence() {
 		_damageCooldown = 3;
 		playSfx(kSfxBoom, 127, 0);
 		_screenFlash = 5;
+		debugC(DEBUG_INSANE, "RA1 on-foot player hit: frame=%u latch=%u flags=0x%02x health=%d->%d",
+			(unsigned)(uint16)_gameCounter, (unsigned)_gameLatch5D, _damageFlags, oldHealth, _health);
 	}
 }
 


Commit: ccd5a4ecac9740d11e2095da0c26dbe16a0d8746
    https://github.com/scummvm/scummvm/commit/ccd5a4ecac9740d11e2095da0c26dbe16a0d8746
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-06T14:03:54+02:00

Commit Message:
SCUMM: RA1: better controls for L9

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


diff --git a/engines/scumm/insane/rebel1/iact.cpp b/engines/scumm/insane/rebel1/iact.cpp
index 614338dd842..61b7a30e28e 100644
--- a/engines/scumm/insane/rebel1/iact.cpp
+++ b/engines/scumm/insane/rebel1/iact.cpp
@@ -88,6 +88,14 @@ const int kRA1Op0BVerticalAxisMax = 100;
 const int kRA1EnhancedFlightDirectMaxX = 64;
 const int kRA1EnhancedFlightDirectMaxY = 40;
 const int kRA1ControlPadAxisStep = 0x1E;
+const int16 kOnFootCenterX = 0xA3;  // g_perspectiveX in HandleGameOp19
+const int16 kOnFootCenterY = 0x82;  // g_perspectiveY in HandleGameOp19
+const int16 kOnFootCursorBaseY = kOnFootCenterY - 0x32;
+const int16 kOnFootGamepadStep = 7;
+const int16 kOnFootCursorMinX = 0;
+const int16 kOnFootCursorMaxX = 319;
+const int16 kOnFootCursorMinY = 0;
+const int16 kOnFootCursorMaxY = 199;
 
 // Level 15 final approach 0x5D damage/event codes consumed by
 // RunLevel1GameLoop. The latch stores the raw GAME parameter; no translation is
@@ -1837,9 +1845,6 @@ bool InsaneRebel1::isTorpedoModeActive() const {
 //   g_perspectiveX/Y  = crosshair center (on-foot targeting)
 // Our _perspectiveX/_perspectiveY maps to the camera offset (DAT_000041a0/41a2).
 // The crosshair center (0xA3, 0x82) is a separate constant for on-foot mode.
-const int16 kOnFootCenterX = 0xA3;  // g_perspectiveX in HandleGameOp19
-const int16 kOnFootCenterY = 0x82;  // g_perspectiveY in HandleGameOp19
-
 // Port split matching HandleGameOp19_OnFootSequence. The helper name is new to
 // this implementation; the original code dispatches the opcode handler directly.
 void InsaneRebel1::initOnFootSequence() {
@@ -1861,6 +1866,74 @@ void InsaneRebel1::initOnFootSequence() {
 	}
 }
 
+void InsaneRebel1::preprocessOnFootAim(int16 &inputX, int16 &inputY, bool *usedJoystick) {
+	if (usedJoystick)
+		*usedJoystick = false;
+
+	const int dpadX =
+		(_vm->getActionState(kScummActionInsaneRight) ? 1 : 0) -
+		(_vm->getActionState(kScummActionInsaneLeft) ? 1 : 0);
+	int dpadY =
+		(_vm->getActionState(kScummActionInsaneDown) ? 1 : 0) -
+		(_vm->getActionState(kScummActionInsaneUp) ? 1 : 0);
+
+	const int16 analogAxisX = applyRebel1AnalogDeadzone(_joystickAxisX);
+	int16 analogAxisY = applyRebel1AnalogDeadzone(_joystickAxisY);
+	if (_optControlsYFlip) {
+		dpadY = -dpadY;
+		analogAxisY = (int16)-analogAxisY;
+	}
+
+	int deltaX = 0;
+	int deltaY = 0;
+	bool activeGamepadAim = false;
+
+	if (dpadX || dpadY) {
+		deltaX = dpadX * kOnFootGamepadStep;
+		deltaY = dpadY * kOnFootGamepadStep;
+		activeGamepadAim = true;
+	} else if (_activeInputSource == kInputSourceJoystickAnalog && (analogAxisX || analogAxisY)) {
+		const int analogX = CLIP<int32>(((int32)analogAxisX * kRA1CenteredAxisMax) / Common::JOYAXIS_MAX,
+			-kRA1CenteredAxisMax, kRA1CenteredAxisMax);
+		const int analogY = CLIP<int32>(((int32)analogAxisY * kRA1CenteredAxisMax) / Common::JOYAXIS_MAX,
+			-kRA1CenteredAxisMax, kRA1CenteredAxisMax);
+		deltaX = stepRebel1Op0BReticleAxis(analogX);
+		deltaY = stepRebel1Op0BReticleAxis(analogY);
+		activeGamepadAim = (deltaX != 0 || deltaY != 0);
+	}
+
+	if (activeGamepadAim) {
+		if (!_gamepadAimActive) {
+			_gamepadAimAxisX = CLIP<int16>(_shipPosX, kOnFootCursorMinX, kOnFootCursorMaxX);
+			_gamepadAimAxisY = CLIP<int16>(_shipPosY, kOnFootCursorMinY, kOnFootCursorMaxY);
+		}
+
+		_gamepadAimAxisX = CLIP<int16>((int16)(_gamepadAimAxisX + deltaX), kOnFootCursorMinX, kOnFootCursorMaxX);
+		_gamepadAimAxisY = CLIP<int16>((int16)(_gamepadAimAxisY + deltaY), kOnFootCursorMinY, kOnFootCursorMaxY);
+		_gamepadAimActive = true;
+
+		if (usedJoystick)
+			*usedJoystick = true;
+		inputX = (int16)(_gamepadAimAxisX - kOnFootCenterX);
+		inputY = (int16)(_gamepadAimAxisY - kOnFootCursorBaseY);
+		return;
+	}
+
+	if (_gamepadAimActive && _activeInputSource != kInputSourceMouse) {
+		if (usedJoystick)
+			*usedJoystick = true;
+		inputX = (int16)(_gamepadAimAxisX - kOnFootCenterX);
+		inputY = (int16)(_gamepadAimAxisY - kOnFootCursorBaseY);
+		return;
+	}
+
+	_gamepadAimActive = false;
+	const int16 logicalX = (int16)CLIP<int>(_vm->_mouse.x, kOnFootCursorMinX, kOnFootCursorMaxX);
+	const int16 logicalY = (int16)CLIP<int>(_vm->_mouse.y, kOnFootCursorMinY, kOnFootCursorMaxY);
+	inputX = (int16)(logicalX - kOnFootCenterX);
+	inputY = (int16)(logicalY - kOnFootCursorBaseY);
+}
+
 // Port split matching HandleGameOp19_OnFootSequence. The helper name is new to
 // this implementation; the original code dispatches the opcode handler directly.
 void InsaneRebel1::updateOnFootSequence() {
@@ -1899,8 +1972,10 @@ void InsaneRebel1::updateOnFootSequence() {
 	} else {
 		// 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);
+		int16 inputX = (int16)(_shipPosX - kOnFootCenterX);
+		int16 inputY = (int16)(_shipPosY - kOnFootCursorBaseY);
+		if (!hasFrameGameOpcode(0x1A))
+			preprocessOnFootAim(inputX, inputY);
 		if (inputX > 0x1E && _onFootCharX < 0x73)
 			_shipDirIndex = 6;  // Walk right
 		else if (inputX < -0x1E && _onFootCharX > -0x73)
@@ -1940,13 +2015,13 @@ void InsaneRebel1::updateOnFootSequence() {
 // this implementation; the original code dispatches the opcode handler directly.
 void InsaneRebel1::updateOnFootAimVariant() {
 	// --- 0x1A: Crosshair positioning (HandleGameOp1A_OnFootVariant) ---
-	// shipPosX/Y = mouse_input + crosshair_center + character_offset
+	// DOS used virtual-mouse axes relative to the character offset. ScummVM's
+	// mouse and gamepad reticle are screen-space controls so the cursor remains
+	// able to cross the whole playfield while Luke is standing at either side.
 	int16 inputX = 0, inputY = 0;
-	preprocessMouseAxes(inputX, inputY);
-	inputX = CLIP<int16>(inputX, -100, 100);
-	int16 inputYNeg = CLIP<int16>((int16)(-inputY), -0x4B, 0x0F);
-	_shipPosX = inputX + kOnFootCenterX + _onFootCharX;
-	_shipPosY = inputYNeg + kOnFootCenterY + _onFootCharY - 0x32;
+	preprocessOnFootAim(inputX, inputY);
+	_shipPosX = CLIP<int16>((int16)(inputX + kOnFootCenterX), kOnFootCursorMinX, kOnFootCursorMaxX);
+	_shipPosY = CLIP<int16>((int16)(inputY + kOnFootCursorBaseY), kOnFootCursorMinY, kOnFootCursorMaxY);
 }
 
 void InsaneRebel1::finishOnFootFrame() {
diff --git a/engines/scumm/insane/rebel1/rebel.h b/engines/scumm/insane/rebel1/rebel.h
index b191854248b..041a528e978 100644
--- a/engines/scumm/insane/rebel1/rebel.h
+++ b/engines/scumm/insane/rebel1/rebel.h
@@ -386,6 +386,7 @@ private:
 
 	// 0x19/0x1A on-foot handler (Level 9 Stormtroopers)
 	void initOnFootSequence();
+	void preprocessOnFootAim(int16 &inputX, int16 &inputY, bool *usedJoystick = nullptr);
 	void updateOnFootSequence();
 	void updateOnFootAimVariant();
 	void finishOnFootFrame();



From noreply at scummvm.org  Sat Jun  6 13:34:46 2026
From: noreply at scummvm.org (neuromancer)
Date: Sat,  6 Jun 2026 13:34:46 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 27da4fc2a56a6582fd1c410a62cbd76c98e96691
Message-ID: <20260606133446.68B0C700573@ron.scummvm.net>

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

Summary:
72481cb710 SCUMM: RA1: fix perspective for L11 UI
512a0ea6fa SCUMM: RA1: less permissive target detection
daf7775b63 SCUMM: RA1: make L11 much easier to control with mouse
27da4fc2a5 SCUMM: RA1: reduce crazy oscilation when using the mouse in some levels


Commit: 72481cb7106579b7f5d4303ee50300a2b8dfe04e
    https://github.com/scummvm/scummvm/commit/72481cb7106579b7f5d4303ee50300a2b8dfe04e
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-06T14:28:34+02:00

Commit Message:
SCUMM: RA1: fix perspective for L11 UI

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


diff --git a/engines/scumm/insane/rebel1/render.cpp b/engines/scumm/insane/rebel1/render.cpp
index 44b888c2cd1..aafe827bde7 100644
--- a/engines/scumm/insane/rebel1/render.cpp
+++ b/engines/scumm/insane/rebel1/render.cpp
@@ -817,7 +817,7 @@ void InsaneRebel1::procPostRendering(byte *renderBitmap, int32 codecparam, int32
 		renderLevelHitsOverlay(renderBitmap, pitch, width, height, 0x04, true);
 
 	if (_currentLevel == 10)
-		renderLevelHitsOverlay(renderBitmap, pitch, width, height, 0x16, false);
+		renderLevelHitsOverlay(renderBitmap, pitch, width, height, 0x16, true);
 
 	// Level 8 (Imperial Walkers) ??? walker-specific state update + UI overlay.
 	// In the original, RunLevel8Flow runs the walker logic inline in the per-frame


Commit: 512a0ea6fa9b904f26ccbbacca88bf344bd5e04c
    https://github.com/scummvm/scummvm/commit/512a0ea6fa9b904f26ccbbacca88bf344bd5e04c
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-06T15:03:27+02:00

Commit Message:
SCUMM: RA1: less permissive target detection

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


diff --git a/engines/scumm/insane/rebel1/iact.cpp b/engines/scumm/insane/rebel1/iact.cpp
index 61b7a30e28e..8346b350b59 100644
--- a/engines/scumm/insane/rebel1/iact.cpp
+++ b/engines/scumm/insane/rebel1/iact.cpp
@@ -145,6 +145,25 @@ inline bool ra1DispatcherHudOnlyWhenDisabled(uint32 opcode) {
 	}
 }
 
+inline uint16 ra1PrioritizedFrameOpcode(uint32 opcodeMask) {
+	if (opcodeMask & (1u << 0x1A))
+		return 0x1A;
+	if (opcodeMask & (1u << 0x19))
+		return 0x19;
+	if (opcodeMask & (1u << 0x0B))
+		return 0x0B;
+	if (opcodeMask & (1u << 0x0A))
+		return 0x0A;
+	if (opcodeMask & (1u << 0x09))
+		return 0x09;
+	if (opcodeMask & (1u << 0x08))
+		return 0x08;
+	if (opcodeMask & (1u << 0x07))
+		return 0x07;
+
+	return 0;
+}
+
 bool ra1TargetCursorUsesProjection(uint16 opcode) {
 	switch (opcode) {
 	// FOBJ target chunks can precede the paired 0x0A GAME chunk, leaving 0x08
@@ -783,34 +802,39 @@ void InsaneRebel1::unprojectGameplayPoint(int16 &x, int16 &y) const {
 }
 
 uint16 InsaneRebel1::getEffectiveGameOpcode() const {
-	if (hasFrameGameOpcode(0x1A))
-		return 0x1A;
-	if (hasFrameGameOpcode(0x19))
-		return 0x19;
-	if (hasFrameGameOpcode(0x0B))
-		return 0x0B;
-	if (hasFrameGameOpcode(0x0A))
-		return 0x0A;
-	if (hasFrameGameOpcode(0x09))
-		return 0x09;
-	if (hasFrameGameOpcode(0x08))
-		return 0x08;
-	if (hasFrameGameOpcode(0x07))
-		return 0x07;
+	const uint16 frameOpcode = ra1PrioritizedFrameOpcode(_frameGameOpcodeMask);
+	if (frameOpcode != 0)
+		return frameOpcode;
+
+	return _activeGameOpcode;
+}
+
+uint16 InsaneRebel1::getTargetHitGameOpcode() const {
+	const uint16 frameOpcode = ra1PrioritizedFrameOpcode(_frameGameOpcodeMask | _frameGameOpcodeHintMask);
+	if (frameOpcode != 0)
+		return frameOpcode;
 
 	return _activeGameOpcode;
 }
 
+int16 InsaneRebel1::getGameplayCursorX(uint16 opcode) const {
+	return (opcode == 0x09) ? _flightAimX : _shipPosX;
+}
+
 int16 InsaneRebel1::getGameplayCursorX() const {
-	return (getEffectiveGameOpcode() == 0x09) ? _flightAimX : _shipPosX;
+	return getGameplayCursorX(getEffectiveGameOpcode());
+}
+
+int16 InsaneRebel1::getGameplayCursorY(uint16 opcode) const {
+	return (opcode == 0x09) ? _flightAimY : _shipPosY;
 }
 
 int16 InsaneRebel1::getGameplayCursorY() const {
-	return (getEffectiveGameOpcode() == 0x09) ? _flightAimY : _shipPosY;
+	return getGameplayCursorY(getEffectiveGameOpcode());
 }
 
-void InsaneRebel1::setGameplayCursor(int16 x, int16 y) {
-	if (getEffectiveGameOpcode() == 0x09) {
+void InsaneRebel1::setGameplayCursor(uint16 opcode, int16 x, int16 y) {
+	if (opcode == 0x09) {
 		_flightAimX = x;
 		_flightAimY = y;
 	} else {
@@ -819,6 +843,10 @@ void InsaneRebel1::setGameplayCursor(int16 x, int16 y) {
 	}
 }
 
+void InsaneRebel1::setGameplayCursor(int16 x, int16 y) {
+	setGameplayCursor(getEffectiveGameOpcode(), x, y);
+}
+
 void InsaneRebel1::updateFlightVariantCursor() {
 	if (getEffectiveGameOpcode() != 0x09)
 		return;
@@ -2537,10 +2565,10 @@ void InsaneRebel1::processShot() {
 // zones into gameplay-window screen space before comparing against the ship center.
 void InsaneRebel1::checkTargetHit(int16 targetIdx, int16 left, int16 top, int16 right, int16 bottom) {
 	int16 snap = _tuning.snap;
-	const uint16 effectiveOpcode = getEffectiveGameOpcode();
+	const uint16 effectiveOpcode = getTargetHitGameOpcode();
 	const bool screenSpaceCursor = ra1TargetCursorUsesProjection(effectiveOpcode);
-	const int16 screenCursorX = getGameplayCursorX();
-	const int16 screenCursorY = getGameplayCursorY();
+	const int16 screenCursorX = getGameplayCursorX(effectiveOpcode);
+	const int16 screenCursorY = getGameplayCursorY(effectiveOpcode);
 	int16 curX = screenCursorX;
 	int16 curY = screenCursorY;
 	if (screenSpaceCursor)
@@ -2584,7 +2612,7 @@ void InsaneRebel1::checkTargetHit(int16 targetIdx, int16 left, int16 top, int16
 				int16 snappedY = (top + bottom) / 2;
 				if (screenSpaceCursor)
 					projectGameplayPoint(snappedX, snappedY);
-				setGameplayCursor(snappedX, snappedY);
+				setGameplayCursor(effectiveOpcode, snappedX, snappedY);
 			}
 
 			// DOS uses g_recentKillObjectIdPlus1 as a frame-wide latch. Once one
diff --git a/engines/scumm/insane/rebel1/rebel.cpp b/engines/scumm/insane/rebel1/rebel.cpp
index 4327c5e1703..4e981793ec0 100644
--- a/engines/scumm/insane/rebel1/rebel.cpp
+++ b/engines/scumm/insane/rebel1/rebel.cpp
@@ -279,6 +279,7 @@ InsaneRebel1::InsaneRebel1(ScummEngine_v7 *scumm) : Insane(), _vm(scumm) {
 	_turretEmitterRightX = 0;
 	_turretEmitterRightY = 0;
 	_activeGameOpcode = 0;
+	_frameGameOpcodeHintMask = 0;
 	_frameGameOpcodeMask = 0;
 	_frameHasGameChunk = false;
 	_frameDispatchFlags = 0;
diff --git a/engines/scumm/insane/rebel1/rebel.h b/engines/scumm/insane/rebel1/rebel.h
index 041a528e978..a110ab1e295 100644
--- a/engines/scumm/insane/rebel1/rebel.h
+++ b/engines/scumm/insane/rebel1/rebel.h
@@ -117,9 +117,11 @@ public:
 	int getLevelGameplayPhase() const { return _levelGameplayPhase; }
 	uint16 getActiveGameOpcode() const { return _activeGameOpcode; }
 	uint16 getEffectiveGameOpcode() const;
+	uint16 getTargetHitGameOpcode() const;
 	bool hasFrameGameOpcode(uint16 opcode) const {
 		return opcode < 32 && (_frameGameOpcodeMask & (1u << opcode)) != 0;
 	}
+	void setFrameGameOpcodeHintMask(uint32 opcodeMask) { _frameGameOpcodeHintMask = opcodeMask; }
 	void warpGameplayMouseNow(int x, int y);
 	int16 getPerspectiveX() const { return _perspectiveX; }
 	int16 getPerspectiveY() const { return _perspectiveY; }
@@ -127,7 +129,10 @@ public:
 	void unprojectGameplayPoint(int16 &x, int16 &y) const;
 	int16 getGameplayCursorX() const;
 	int16 getGameplayCursorY() const;
+	int16 getGameplayCursorX(uint16 opcode) const;
+	int16 getGameplayCursorY(uint16 opcode) const;
 	void setGameplayCursor(int16 x, int16 y);
+	void setGameplayCursor(uint16 opcode, int16 x, int16 y);
 	void updateFlightVariantCursor();
 	bool handleFrameObjectTarget(int16 objectId, int16 left, int16 top, int16 width, int16 height,
 		int codec, uint8 &ra1Param);
@@ -441,6 +446,8 @@ private:
 	// Last per-frame GAME opcode observed in the current playback stream.
 	// Kept for legacy call sites; frame-accurate dispatch uses _frameGameOpcodeMask.
 	uint16 _activeGameOpcode;
+	// GAME opcodes pre-scanned from the current frame before FOBJ dispatch.
+	uint32 _frameGameOpcodeHintMask;
 	uint32 _frameGameOpcodeMask;
 	bool _frameHasGameChunk;
 	uint16 _frameDispatchFlags;
diff --git a/engines/scumm/insane/rebel1/runlevels.cpp b/engines/scumm/insane/rebel1/runlevels.cpp
index 92962e9b931..02b741f8a74 100644
--- a/engines/scumm/insane/rebel1/runlevels.cpp
+++ b/engines/scumm/insane/rebel1/runlevels.cpp
@@ -220,6 +220,7 @@ void InsaneRebel1::resetLevelFrameState() {
 	_currentSmushFrame = 0;
 	_gameCounter = 0;
 	_activeGameOpcode = 0;
+	_frameGameOpcodeHintMask = 0;
 	_gameLatch5D = 0;
 	_gameLatch5F = 0;
 }
diff --git a/engines/scumm/smush/rebel/smush_player_ra1.cpp b/engines/scumm/smush/rebel/smush_player_ra1.cpp
index c2a9fa8bdbb..694e947c881 100644
--- a/engines/scumm/smush/rebel/smush_player_ra1.cpp
+++ b/engines/scumm/smush/rebel/smush_player_ra1.cpp
@@ -717,18 +717,24 @@ void SmushPlayerRebel1::handleFrameObject(int32 subSize, Common::SeekableReadStr
 // handleFrame override ??? RA1 frame parsing with alignment, OBJ chunks, clean frame
 // ---------------------------------------------------------------------------
 
-static bool ra1FrameHasGameChunk(Common::SeekableReadStream &b, int32 frameSize) {
+static bool ra1ScanFrameGameChunks(Common::SeekableReadStream &b, int32 frameSize, uint32 &opcodeMask) {
+	opcodeMask = 0;
 	const int64 frameStart = b.pos();
 	int32 remaining = frameSize;
 	RA1FrameChunkIterator chunks(b, remaining);
 	RA1AnimChunk chunk;
+	bool hasGameChunk = false;
 	while (chunks.next(chunk)) {
 		if (chunk.tag == MKTAG('F', 'R', 'M', 'E'))
 			break;
 		if (chunk.tag == MKTAG('G', 'A', 'M', 'E') ||
 				chunk.tag == MKTAG('G', 'A', 'M', '2')) {
-			b.seek(frameStart, SEEK_SET);
-			return true;
+			hasGameChunk = true;
+			if (chunks.fits(chunk) && chunk.size >= 4) {
+				const uint32 opcode = b.readUint32BE();
+				if (opcode < 32)
+					opcodeMask |= (1u << opcode);
+			}
 		}
 
 		if (!chunks.fits(chunk))
@@ -737,7 +743,7 @@ static bool ra1FrameHasGameChunk(Common::SeekableReadStream &b, int32 frameSize)
 	}
 
 	b.seek(frameStart, SEEK_SET);
-	return false;
+	return hasGameChunk;
 }
 
 void SmushPlayerRebel1::ra1HandleFrameAudioChunk(int32 subSize, Common::SeekableReadStream &b) {
@@ -922,8 +928,11 @@ void SmushPlayerRebel1::handleFrame(int32 frameSize, Common::SeekableReadStream
 		InsaneRebel1 *rebel1 = static_cast<InsaneRebel1 *>(_insane);
 		rebel1->setCurrentSmushFrame(_frame);
 		bool interactive = rebel1->isInteractiveVideoActive();
-		const bool frameHasGameChunk = interactive && ra1FrameHasGameChunk(b, frameSize);
+		uint32 frameGameOpcodeHintMask = 0;
+		const bool frameHasGameChunk = interactive &&
+			ra1ScanFrameGameChunks(b, frameSize, frameGameOpcodeHintMask);
 		rebel1->setFrameHasGameChunk(frameHasGameChunk);
+		rebel1->setFrameGameOpcodeHintMask(frameGameOpcodeHintMask);
 		const uint16 activeOpcode = rebel1->getActiveGameOpcode();
 		bool forceClear = interactive &&
 			(activeOpcode == 0x0B ||


Commit: daf7775b633de7894716a1f477051951d3f639b4
    https://github.com/scummvm/scummvm/commit/daf7775b633de7894716a1f477051951d3f639b4
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-06T15:13:27+02:00

Commit Message:
SCUMM: RA1: make L11 much easier to control with mouse

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 8346b350b59..2b9b4a70847 100644
--- a/engines/scumm/insane/rebel1/iact.cpp
+++ b/engines/scumm/insane/rebel1/iact.cpp
@@ -87,6 +87,7 @@ const int kRA1CenteredAxisMax = 127;
 const int kRA1Op0BVerticalAxisMax = 100;
 const int kRA1EnhancedFlightDirectMaxX = 64;
 const int kRA1EnhancedFlightDirectMaxY = 40;
+const int kRA1Op09MouseRollTargetScale = 16;
 const int kRA1ControlPadAxisStep = 0x1E;
 const int16 kOnFootCenterX = 0xA3;  // g_perspectiveX in HandleGameOp19
 const int16 kOnFootCenterY = 0x82;  // g_perspectiveY in HandleGameOp19
@@ -972,7 +973,9 @@ void InsaneRebel1::preprocessMouseAxes(int16 &inputX, int16 &inputY, bool *usedJ
 	if (updateGamepadReticleAim(inputX, inputY, usedJoystick))
 		return;
 
-	const bool directFlightInput = getEffectiveGameOpcode() == 0x07;
+	const uint16 effectiveOpcode = getEffectiveGameOpcode();
+	const bool directFlightInput = effectiveOpcode == 0x07;
+	const bool flightVariantInput = effectiveOpcode == 0x09;
 
 	const int16 analogAxisX = applyRebel1AnalogDeadzone(_joystickAxisX);
 	const int16 analogAxisY = applyRebel1AnalogDeadzone(_joystickAxisY);
@@ -1034,7 +1037,7 @@ void InsaneRebel1::preprocessMouseAxes(int16 &inputX, int16 &inputY, bool *usedJ
 	int16 logicalX = (int16)CLIP<int>(_vm->_mouse.x, 0, 319);
 	int16 logicalY = (int16)CLIP<int>(_vm->_mouse.y, 0, 199);
 
-	if (directFlightInput) {
+	if (directFlightInput || flightVariantInput) {
 		inputX = scaleRebel1CenteredMouseAxis(logicalX, kRA1CenterX, kRA1EnhancedFlightDirectMaxX);
 		inputY = scaleRebel1CenteredMouseAxis(logicalY, kRA1CenterY, kRA1EnhancedFlightDirectMaxY);
 	} else {
@@ -1085,6 +1088,7 @@ void InsaneRebel1::updateShipPhysics() {
 	// --- Step 1: Gameplay axes from FUN_231BE ---
 	// HandleGameOp07_ShipFlight consumes the preprocessed axes in DAT_756C/756E,
 	// not raw mouse coordinates. Reuse the same centered-axis law here.
+	const uint16 effectiveOpcode = getEffectiveGameOpcode();
 	int16 inputX = 0;
 	int16 inputY = 0;
 	bool usedJoystick = false;
@@ -1100,8 +1104,15 @@ void InsaneRebel1::updateShipPhysics() {
 		inputSourceName = "joystick-dpad";
 
 	// --- Step 2: Roll accumulator (_74CA) ---
-	// Normal mode: accumulate; mode 0x10: snap to input
-	_rollAccum += (_tuning.roll * (int32)inputX) >> 5;
+	// Normal mode: accumulate. For ScummVM's absolute mouse in GAME 0x09, steer
+	// toward a bounded roll target so holding the cursor off center does not
+	// continue accelerating the ship until it clamps.
+	if (effectiveOpcode == 0x09 && _activeInputSource == kInputSourceMouse && !usedJoystick) {
+		const int32 targetRoll = (int32)inputX * kRA1Op09MouseRollTargetScale;
+		_rollAccum += (targetRoll - _rollAccum) >> 2;
+	} else {
+		_rollAccum += (_tuning.roll * (int32)inputX) >> 5;
+	}
 	_rollAccum = CLIP<int32>(_rollAccum, -0x47F, 0x47F);
 
 	// --- Step 3: Vertical smoothing (_74CE) ---


Commit: 27da4fc2a56a6582fd1c410a62cbd76c98e96691
    https://github.com/scummvm/scummvm/commit/27da4fc2a56a6582fd1c410a62cbd76c98e96691
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-06T15:34:32+02:00

Commit Message:
SCUMM: RA1: reduce crazy oscilation when using the mouse in 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 2b9b4a70847..9c96d37650a 100644
--- a/engines/scumm/insane/rebel1/iact.cpp
+++ b/engines/scumm/insane/rebel1/iact.cpp
@@ -87,7 +87,7 @@ const int kRA1CenteredAxisMax = 127;
 const int kRA1Op0BVerticalAxisMax = 100;
 const int kRA1EnhancedFlightDirectMaxX = 64;
 const int kRA1EnhancedFlightDirectMaxY = 40;
-const int kRA1Op09MouseRollTargetScale = 16;
+const int kRA1MouseFlightRollTargetScale = 16;
 const int kRA1ControlPadAxisStep = 0x1E;
 const int16 kOnFootCenterX = 0xA3;  // g_perspectiveX in HandleGameOp19
 const int16 kOnFootCenterY = 0x82;  // g_perspectiveY in HandleGameOp19
@@ -1104,11 +1104,12 @@ void InsaneRebel1::updateShipPhysics() {
 		inputSourceName = "joystick-dpad";
 
 	// --- Step 2: Roll accumulator (_74CA) ---
-	// Normal mode: accumulate. For ScummVM's absolute mouse in GAME 0x09, steer
-	// toward a bounded roll target so holding the cursor off center does not
-	// continue accelerating the ship until it clamps.
-	if (effectiveOpcode == 0x09 && _activeInputSource == kInputSourceMouse && !usedJoystick) {
-		const int32 targetRoll = (int32)inputX * kRA1Op09MouseRollTargetScale;
+	// Normal mode: accumulate. For ScummVM's absolute mouse in flight handlers,
+	// steer toward a bounded roll target so holding the cursor off center does
+	// not continue accelerating the ship until it clamps.
+	if ((effectiveOpcode == 0x07 || effectiveOpcode == 0x09) &&
+			_activeInputSource == kInputSourceMouse && !usedJoystick) {
+		const int32 targetRoll = (int32)inputX * kRA1MouseFlightRollTargetScale;
 		_rollAccum += (targetRoll - _rollAccum) >> 2;
 	} else {
 		_rollAccum += (_tuning.roll * (int32)inputX) >> 5;



From noreply at scummvm.org  Sat Jun  6 15:34:24 2026
From: noreply at scummvm.org (neuromancer)
Date: Sat,  6 Jun 2026 15:34:24 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 b22dba7b28bbc47c128048915fa9a7b12db931e5
Message-ID: <20260606153424.5110A700573@ron.scummvm.net>

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:
84dfb4fc43 COMMON: Allow adding files to SpectrumTapeArchive
b22dba7b28 FREESCAPE: add ZX Spectrum tape support


Commit: 84dfb4fc433293629240ef9938396b16e7079f64
    https://github.com/scummvm/scummvm/commit/84dfb4fc433293629240ef9938396b16e7079f64
Author: Shadow Maker (shm at vtrd.in)
Date: 2026-06-06T17:34:19+02:00

Commit Message:
COMMON: Allow adding files to SpectrumTapeArchive

Changed paths:
    common/formats/spectrum_tape.cpp
    common/formats/spectrum_tape.h


diff --git a/common/formats/spectrum_tape.cpp b/common/formats/spectrum_tape.cpp
index 175146cbd3b..d18a460c5ba 100644
--- a/common/formats/spectrum_tape.cpp
+++ b/common/formats/spectrum_tape.cpp
@@ -471,15 +471,19 @@ SpectrumTapeArchive::SpectrumTapeArchive(const SpectrumTapeBlocks &blocks) {
 		if (!getTapBody(block, body, &flag) || flag == 0x00 || body.empty())
 			continue;
 
-		ArchiveFile entry;
-		entry.name = Path(getTapeFileName(pendingHeader, body, fileIndex++), Path::kNoSeparator);
-		entry.data = body;
-		_files.push_back(entry);
+		addFile(Path(getTapeFileName(pendingHeader, body, fileIndex++), Path::kNoSeparator), body);
 
 		pendingHeader.valid = false;
 	}
 }
 
+void SpectrumTapeArchive::addFile(const Path &path, const Array<byte> &data) {
+	ArchiveFile entry;
+	entry.name = path;
+	entry.data = data;
+	_files.push_back(entry);
+}
+
 bool SpectrumTapeArchive::hasFile(const Path &path) const {
 	for (const ArchiveFile &file : _files) {
 		if (file.name.equalsIgnoreCase(path))
diff --git a/common/formats/spectrum_tape.h b/common/formats/spectrum_tape.h
index ebb61d4c3ac..f72dccfa24e 100644
--- a/common/formats/spectrum_tape.h
+++ b/common/formats/spectrum_tape.h
@@ -42,8 +42,11 @@ bool parseSpectrumTape(SeekableReadStream &stream, SpectrumTapeBlocks &blocks);
 
 class SpectrumTapeArchive : public Archive {
 public:
+	SpectrumTapeArchive() {}
 	SpectrumTapeArchive(const SpectrumTapeBlocks &blocks);
 
+	void addFile(const Path &path, const Array<byte> &data);
+
 	bool hasFile(const Path &path) const override;
 	int listMembers(ArchiveMemberList &list) const override;
 	const ArchiveMemberPtr getMember(const Path &path) const override;


Commit: b22dba7b28bbc47c128048915fa9a7b12db931e5
    https://github.com/scummvm/scummvm/commit/b22dba7b28bbc47c128048915fa9a7b12db931e5
Author: Shadow Maker (shm at vtrd.in)
Date: 2026-06-06T17:34:19+02:00

Commit Message:
FREESCAPE: add ZX Spectrum tape support

Changed paths:
  A engines/freescape/zx_tape.cpp
  A engines/freescape/zx_tape.h
    engines/freescape/assets.cpp
    engines/freescape/detection.cpp
    engines/freescape/games/eclipse/zx.cpp
    engines/freescape/module.mk


diff --git a/engines/freescape/assets.cpp b/engines/freescape/assets.cpp
index c02332fb6bb..c76219cedee 100644
--- a/engines/freescape/assets.cpp
+++ b/engines/freescape/assets.cpp
@@ -23,10 +23,13 @@
 // available at https://github.com/TomHarte/Phantasma/ (MIT)
 
 #include "common/file.h"
+#include "common/archive.h"
+#include "common/config-manager.h"
 #include "common/compression/unzip.h"
 #include "image/bmp.h"
 
 #include "freescape/freescape.h"
+#include "freescape/zx_tape.h"
 
 namespace Freescape {
 
@@ -125,6 +128,9 @@ void FreescapeEngine::loadDataBundle() {
 	if (versionData != expectedVersion)
 		error("Unexpected version number for freescape.dat: expecting '%s' but found '%s'", expectedVersion.c_str(), versionData);
 	free(versionData);
+
+	if (Common::Archive *archive = makeZxSpectrumTapeArchive(*_gameDescription, ConfMan.getPath("path")))
+		SearchMan.add("freescape-zx-tape", archive, 10);
 }
 
 Graphics::Surface *FreescapeEngine::loadBundledImage(const Common::String &name, bool appendRenderMode) {
diff --git a/engines/freescape/detection.cpp b/engines/freescape/detection.cpp
index afba0909dbf..b6db9b61304 100644
--- a/engines/freescape/detection.cpp
+++ b/engines/freescape/detection.cpp
@@ -24,6 +24,10 @@
 
 #include "freescape/games/driller/driller.h"
 #include "freescape/games/eclipse/eclipse.h"
+#include "freescape/zx_tape.h"
+
+#include "common/config-manager.h"
+#include "common/file.h"
 
 namespace Freescape {
 
@@ -613,7 +617,7 @@ const ADGameDescription gameDescriptions[] = {
 	{
 		"totaleclipse2",
 		"",
-		AD_ENTRY1s("totaleclipse.zx.data", "5e80cb6a518d5ab2192b845801b1a32e", 35661),
+		AD_ENTRY1s("totaleclipse2.zx.data", "5e80cb6a518d5ab2192b845801b1a32e", 35661),
 		Common::EN_ANY,
 		Common::kPlatformZX,
 		ADGF_NO_FLAGS,
@@ -1263,9 +1267,70 @@ public:
 	const DebugChannelDef *getDebugChannels() const override {
 		return debugFlagList;
 	}
+	Common::Error identifyGame(DetectedGame &game, const void **descriptor) override;
+	DetectedGames detectGames(const Common::FSList &fslist, uint32 skipADFlags, bool skipIncomplete) override;
 	DetectedGame toDetectedGame(const ADDetectedGame &adGame, ADDetectedGameExtraInfo *extraInfo) const override;
+
+private:
+	ADDetectedGames detectZxTapeGames(const Common::FSList &fslist, uint32 skipADFlags) const;
 };
 
+Common::Error FreescapeMetaEngineDetection::identifyGame(DetectedGame &game, const void **descriptor) {
+	Common::Error err = AdvancedMetaEngineDetection<ADGameDescription>::identifyGame(game, descriptor);
+	if (err.getCode() == Common::kNoError)
+		return err;
+
+	Common::Path path = ConfMan.hasKey("path") ? ConfMan.getPath("path") : Common::Path(".");
+	Common::FSNode dir(path);
+	Common::FSList files;
+	if (!dir.isDirectory() || !dir.getChildren(files, Common::FSNode::kListAll) || files.empty())
+		return err;
+
+	Common::String gameId = ConfMan.get("gameid");
+	ADDetectedGames tapeGames = detectZxTapeGames(files, 0);
+	for (const ADDetectedGame &tapeGame : tapeGames) {
+		if (tapeGame.desc->gameId == gameId) {
+			game = toDetectedGame(tapeGame, nullptr);
+			*descriptor = new ADDynamicGameDescription<ADGameDescription>(tapeGame.desc);
+			return Common::kNoError;
+		}
+	}
+
+	return err;
+}
+
+DetectedGames FreescapeMetaEngineDetection::detectGames(const Common::FSList &fslist, uint32 skipADFlags, bool skipIncomplete) {
+	DetectedGames detectedGames = AdvancedMetaEngineDetection::detectGames(fslist, skipADFlags, skipIncomplete);
+
+	ADDetectedGames tapeGames = detectZxTapeGames(fslist, skipADFlags);
+	for (const ADDetectedGame &game : tapeGames)
+		detectedGames.push_back(toDetectedGame(game, nullptr));
+
+	return detectedGames;
+}
+
+ADDetectedGames FreescapeMetaEngineDetection::detectZxTapeGames(const Common::FSList &fslist, uint32 skipADFlags) const {
+	ADDetectedGames detectedGames;
+
+	for (const Common::FSNode &node : fslist) {
+		Common::File file;
+		Common::String name = node.getName();
+		if ((name.hasSuffixIgnoreCase(".tap") || name.hasSuffixIgnoreCase(".tzx")) && file.open(node)) {
+			for (const ADGameDescription *desc = Freescape::gameDescriptions; desc->gameId; ++desc) {
+				if (!(desc->flags & skipADFlags) && desc->platform == Common::kPlatformZX) {
+					file.seek(0);
+					Freescape::ZxTapeFileList files;
+					if (Freescape::extractZxSpectrumTapeFiles(file, desc->gameId, files) &&
+							Freescape::matchZxSpectrumTapeFiles(files, *desc, _md5Bytes))
+						detectedGames.push_back(ADDetectedGame(desc));
+				}
+			}
+		}
+	}
+
+	return detectedGames;
+}
+
 DetectedGame FreescapeMetaEngineDetection::toDetectedGame(const ADDetectedGame &adGame, ADDetectedGameExtraInfo *extraInfo) const {
 	DetectedGame game = AdvancedMetaEngineDetection::toDetectedGame(adGame);
 
diff --git a/engines/freescape/games/eclipse/zx.cpp b/engines/freescape/games/eclipse/zx.cpp
index 103e11d3760..dabb21644bc 100644
--- a/engines/freescape/games/eclipse/zx.cpp
+++ b/engines/freescape/games/eclipse/zx.cpp
@@ -86,24 +86,28 @@ void EclipseEngine::loadHeartFramesZX(Common::SeekableReadStream *file, int rest
 
 void EclipseEngine::loadAssetsZXFullGame() {
 	Common::File file;
+	const char *prefix = isEclipse2() ? "totaleclipse2" : "totaleclipse";
+	Common::Path titleFile(Common::String::format("%s.zx.title", prefix));
+	Common::Path borderFile(Common::String::format("%s.zx.border", prefix));
+	Common::Path dataFile(Common::String::format("%s.zx.data", prefix));
 
-	file.open("totaleclipse.zx.title");
+	file.open(titleFile);
 	if (file.isOpen()) {
 		_title = loadAndConvertScrImage(&file);
 	} else
-		error("Unable to find totaleclipse.zx.title");
+		error("Unable to find %s", titleFile.toString().c_str());
 
 	file.close();
-	file.open("totaleclipse.zx.border");
+	file.open(borderFile);
 	if (file.isOpen()) {
 		_border = loadAndConvertScrImage(&file);
 	} else
-		error("Unable to find totaleclipse.zx.border");
+		error("Unable to find %s", borderFile.toString().c_str());
 	file.close();
 
-	file.open("totaleclipse.zx.data");
+	file.open(dataFile);
 	if (!file.isOpen())
-		error("Failed to open totaleclipse.zx.data");
+		error("Failed to open %s", dataFile.toString().c_str());
 
 	if (isEclipse2()) {
 		loadMessagesFixedSize(&file, 0x2ac, 16, 30);
diff --git a/engines/freescape/module.mk b/engines/freescape/module.mk
index a88d1003f37..57fcd93b830 100644
--- a/engines/freescape/module.mk
+++ b/engines/freescape/module.mk
@@ -72,7 +72,8 @@ MODULE_OBJS := \
 	sound/zx.o \
 	ui.o \
 	unpack.o \
-	wb.o
+	wb.o \
+	zx_tape.o
 
 ifdef USE_TINYGL
 MODULE_OBJS += \
@@ -105,4 +106,5 @@ endif
 include $(srcdir)/rules.mk
 
 # Detection objects
-DETECT_OBJS += $(MODULE)/detection.o
+DETECT_OBJS += $(MODULE)/detection.o \
+	$(MODULE)/zx_tape.o
diff --git a/engines/freescape/zx_tape.cpp b/engines/freescape/zx_tape.cpp
new file mode 100644
index 00000000000..3190231fab1
--- /dev/null
+++ b/engines/freescape/zx_tape.cpp
@@ -0,0 +1,121 @@
+/* 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 "freescape/zx_tape.h"
+
+#include "common/file.h"
+#include "common/formats/spectrum_tape.h"
+#include "common/fs.h"
+#include "common/md5.h"
+#include "common/memstream.h"
+#include "engines/advancedDetector.h"
+
+namespace Freescape {
+
+bool extractZxSpectrumTapeFiles(Common::SeekableReadStream &stream, const char *prefix, ZxTapeFileList &files) {
+	files.clear();
+
+	Common::SpectrumTapeBlocks blocks;
+	if (!Common::parseSpectrumTape(stream, blocks))
+		return false;
+
+	Common::Array<byte> title;
+	Common::Array<byte> border;
+	Common::Array<byte> data;
+
+	for (const Common::SpectrumTapeBlock &block : blocks) {
+		if (block.tap.size() >= 2) {
+			Common::Array<byte> body;
+			body.assign(block.tap.begin() + 1, block.tap.end() - 1);
+			if (body.size() == 6912) {
+				title = border;
+				border = body;
+			} else if (body.size() >= data.size()) {
+				data = body;
+			}
+		}
+	}
+
+	const struct {
+		const char *suffix;
+		const Common::Array<byte> &payload;
+	} outputs[] = {
+		{ "title", title },
+		{ "border", border },
+		{ "data", data }
+	};
+	for (uint i = 0; i < ARRAYSIZE(outputs); ++i) {
+		if (!outputs[i].payload.empty()) {
+			ZxTapeFile file;
+			file.name = Common::Path(Common::String::format("%s.zx.%s", prefix, outputs[i].suffix), Common::Path::kNoSeparator);
+			file.data = outputs[i].payload;
+			files.push_back(file);
+		}
+	}
+
+	return !data.empty();
+}
+
+bool matchZxSpectrumTapeFiles(const ZxTapeFileList &files, const ADGameDescription &desc, uint md5Bytes) {
+	bool matched = desc.platform == Common::kPlatformZX;
+
+	for (const ADGameFileDescription *fileDesc = desc.filesDescriptions; matched && fileDesc->fileName; ++fileDesc) {
+		bool fileMatched = false;
+		Common::Path fileName(fileDesc->fileName, Common::Path::kNoSeparator);
+		for (uint i = 0; !fileMatched && i < files.size(); ++i) {
+			fileMatched = files[i].name.equalsIgnoreCase(fileName) &&
+				(fileDesc->fileSize == AD_NO_SIZE || fileDesc->fileSize == files[i].data.size());
+			if (fileMatched && fileDesc->md5) {
+				Common::MemoryReadStream stream(files[i].data.data(), files[i].data.size());
+				fileMatched = Common::computeStreamMD5AsString(stream, md5Bytes) == fileDesc->md5;
+			}
+		}
+		matched = fileMatched;
+	}
+
+	return matched;
+}
+
+Common::Archive *makeZxSpectrumTapeArchive(const ADGameDescription &desc, const Common::Path &gamePath) {
+	Common::Archive *archive = nullptr;
+
+	Common::FSList files;
+	if (desc.platform == Common::kPlatformZX && Common::FSNode(gamePath).getChildren(files, Common::FSNode::kListFilesOnly)) {
+		for (const Common::FSNode &node : files) {
+			Common::File file;
+			Common::String name = node.getName();
+			if ((name.hasSuffixIgnoreCase(".tap") || name.hasSuffixIgnoreCase(".tzx")) && file.open(node)) {
+				ZxTapeFileList tapeFiles;
+				if (extractZxSpectrumTapeFiles(file, desc.gameId, tapeFiles) && matchZxSpectrumTapeFiles(tapeFiles, desc)) {
+					Common::SpectrumTapeArchive *tapeArchive = new Common::SpectrumTapeArchive();
+					for (const ZxTapeFile &tapeFile : tapeFiles)
+						tapeArchive->addFile(tapeFile.name, tapeFile.data);
+					archive = tapeArchive;
+					break;
+				}
+			}
+		}
+	}
+
+	return archive;
+}
+
+} // End of namespace Freescape
diff --git a/engines/freescape/zx_tape.h b/engines/freescape/zx_tape.h
new file mode 100644
index 00000000000..166775ee7d2
--- /dev/null
+++ b/engines/freescape/zx_tape.h
@@ -0,0 +1,51 @@
+/* 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 FREESCAPE_ZX_TAPE_H
+#define FREESCAPE_ZX_TAPE_H
+
+#include "common/array.h"
+#include "common/path.h"
+
+namespace Common {
+class Archive;
+class FSNode;
+class SeekableReadStream;
+}
+
+struct ADGameDescription;
+
+namespace Freescape {
+
+struct ZxTapeFile {
+	Common::Path name;
+	Common::Array<byte> data;
+};
+
+typedef Common::Array<ZxTapeFile> ZxTapeFileList;
+
+bool extractZxSpectrumTapeFiles(Common::SeekableReadStream &stream, const char *prefix, ZxTapeFileList &files);
+bool matchZxSpectrumTapeFiles(const ZxTapeFileList &files, const ADGameDescription &desc, uint md5Bytes = 5000);
+Common::Archive *makeZxSpectrumTapeArchive(const ADGameDescription &desc, const Common::Path &gamePath);
+
+} // End of namespace Freescape
+
+#endif



From noreply at scummvm.org  Sat Jun  6 16:01:55 2026
From: noreply at scummvm.org (lephilousophe)
Date: Sat,  6 Jun 2026 16:01:55 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm-web master ->
 7d37aed331b2fe4c56e874519b8e9030ffc6a593
Message-ID: <20260606160155.D83A4700573@ron.scummvm.net>

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

Summary:
7d37aed331 Bump matomo/device-detector


Commit: 7d37aed331b2fe4c56e874519b8e9030ffc6a593
    https://github.com/scummvm/scummvm-web/commit/7d37aed331b2fe4c56e874519b8e9030ffc6a593
Author: dependabot[bot] (49699333+dependabot[bot]@users.noreply.github.com)
Date: 2026-06-06T18:01:51+02:00

Commit Message:
Bump matomo/device-detector

Bumps the patch-releases group with 1 update in the / directory: [matomo/device-detector](https://github.com/matomo-org/device-detector).


Updates `matomo/device-detector` from 6.5.0 to 6.5.1
- [Release notes](https://github.com/matomo-org/device-detector/releases)
- [Commits](https://github.com/matomo-org/device-detector/compare/6.5.0...6.5.1)

---
updated-dependencies:
- dependency-name: matomo/device-detector
  dependency-version: 6.5.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: patch-releases
...

Signed-off-by: dependabot[bot] <support at github.com>

Changed paths:
    composer.lock


diff --git a/composer.lock b/composer.lock
index e05e1cb7..9b69cd84 100644
--- a/composer.lock
+++ b/composer.lock
@@ -600,16 +600,16 @@
         },
         {
             "name": "matomo/device-detector",
-            "version": "6.5.0",
+            "version": "6.5.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/matomo-org/device-detector.git",
-                "reference": "e0fff2309dad83eb3cfb2564e524be715cfcf3cf"
+                "reference": "f30457500c6be4c80c8830466f8a746724779fd0"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/matomo-org/device-detector/zipball/e0fff2309dad83eb3cfb2564e524be715cfcf3cf",
-                "reference": "e0fff2309dad83eb3cfb2564e524be715cfcf3cf",
+                "url": "https://api.github.com/repos/matomo-org/device-detector/zipball/f30457500c6be4c80c8830466f8a746724779fd0",
+                "reference": "f30457500c6be4c80c8830466f8a746724779fd0",
                 "shasum": ""
             },
             "require": {
@@ -623,11 +623,11 @@
                 "matthiasmullie/scrapbook": "^1.4.7",
                 "mayflower/mo4-coding-standard": "^v9.0.0",
                 "phpstan/phpstan": "^1.10.44",
-                "phpunit/phpunit": "^8.5.2 || ^9 || ^10 || ^11 || ^12",
+                "phpunit/phpunit": "^8.5.2 || ^9 || ^10 || ^11 || ^12 || ^13",
                 "psr/cache": "^1.0.1 || ^2.0 || ^3.0",
                 "psr/simple-cache": "^1.0.1 || ^2.0 || ^3.0",
                 "slevomat/coding-standard": "<8.16.0",
-                "symfony/yaml": "^5.1.7"
+                "symfony/yaml": "^5.4 || ^6.4 || ^7.4 || ^8.0"
             },
             "suggest": {
                 "ext-yaml": "Necessary for using the Pecl YAML parser"
@@ -663,9 +663,9 @@
                 "forum": "https://forum.matomo.org/",
                 "issues": "https://github.com/matomo-org/device-detector/issues",
                 "source": "https://github.com/matomo-org/matomo",
-                "wiki": "https://dev.matomo.org/"
+                "wiki": "https://developer.matomo.org/"
             },
-            "time": "2026-01-21T07:53:39+00:00"
+            "time": "2026-05-27T09:53:26+00:00"
         },
         {
             "name": "mustangostang/spyc",
@@ -3031,5 +3031,5 @@
     "platform-overrides": {
         "php": "8.2.0"
     },
-    "plugin-api-version": "2.6.0"
+    "plugin-api-version": "2.9.0"
 }



From noreply at scummvm.org  Sat Jun  6 16:02:24 2026
From: noreply at scummvm.org (lephilousophe)
Date: Sat,  6 Jun 2026 16:02:24 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm-web master ->
 16df4c235a80cedf24a68b4cb661df887622bc0c
Message-ID: <20260606160224.76A9F700573@ron.scummvm.net>

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

Summary:
16df4c235a Bump sass from 1.99.0 to 1.100.0


Commit: 16df4c235a80cedf24a68b4cb661df887622bc0c
    https://github.com/scummvm/scummvm-web/commit/16df4c235a80cedf24a68b4cb661df887622bc0c
Author: dependabot[bot] (49699333+dependabot[bot]@users.noreply.github.com)
Date: 2026-06-06T18:02:21+02:00

Commit Message:
Bump sass from 1.99.0 to 1.100.0

Bumps [sass](https://github.com/sass/dart-sass) from 1.99.0 to 1.100.0.
- [Release notes](https://github.com/sass/dart-sass/releases)
- [Changelog](https://github.com/sass/dart-sass/blob/main/CHANGELOG.md)
- [Commits](https://github.com/sass/dart-sass/compare/1.99.0...1.100.0)

---
updated-dependencies:
- dependency-name: sass
  dependency-version: 1.100.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support at github.com>

Changed paths:
    package-lock.json
    package.json


diff --git a/package-lock.json b/package-lock.json
index b9258ae9..ac46eac8 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -11,7 +11,7 @@
       "dependencies": {
         "autoprefixer": "^10.5.0",
         "postcss": "^8.5.14",
-        "sass": "^1.99.0"
+        "sass": "^1.100.0"
       }
     },
     "node_modules/@parcel/watcher": {
@@ -412,15 +412,15 @@
       "license": "CC-BY-4.0"
     },
     "node_modules/chokidar": {
-      "version": "4.0.3",
-      "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz",
-      "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==",
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-5.0.0.tgz",
+      "integrity": "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==",
       "license": "MIT",
       "dependencies": {
-        "readdirp": "^4.0.1"
+        "readdirp": "^5.0.0"
       },
       "engines": {
-        "node": ">= 14.16.0"
+        "node": ">= 20.19.0"
       },
       "funding": {
         "url": "https://paulmillr.com/funding/"
@@ -578,12 +578,12 @@
       "license": "MIT"
     },
     "node_modules/readdirp": {
-      "version": "4.1.2",
-      "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz",
-      "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==",
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-5.0.0.tgz",
+      "integrity": "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==",
       "license": "MIT",
       "engines": {
-        "node": ">= 14.18.0"
+        "node": ">= 20.19.0"
       },
       "funding": {
         "type": "individual",
@@ -591,12 +591,12 @@
       }
     },
     "node_modules/sass": {
-      "version": "1.99.0",
-      "resolved": "https://registry.npmjs.org/sass/-/sass-1.99.0.tgz",
-      "integrity": "sha512-kgW13M54DUB7IsIRM5LvJkNlpH+WhMpooUcaWGFARkF1Tc82v9mIWkCbCYf+MBvpIUBSeSOTilpZjEPr2VYE6Q==",
+      "version": "1.100.0",
+      "resolved": "https://registry.npmjs.org/sass/-/sass-1.100.0.tgz",
+      "integrity": "sha512-B5j0rYMlinhhOo9tjQebMVVn0TfyXAF+wB3b2ggZUuJ/is/Y+7+JGjirAMxHZ9Z3hIP98NPfamlAkBHa1lAaXQ==",
       "license": "MIT",
       "dependencies": {
-        "chokidar": "^4.0.0",
+        "chokidar": "^5.0.0",
         "immutable": "^5.1.5",
         "source-map-js": ">=0.6.2 <2.0.0"
       },
@@ -604,7 +604,7 @@
         "sass": "sass.js"
       },
       "engines": {
-        "node": ">=14.0.0"
+        "node": ">=20.19.0"
       },
       "optionalDependencies": {
         "@parcel/watcher": "^2.4.1"
diff --git a/package.json b/package.json
index 92b519d2..b28c581b 100644
--- a/package.json
+++ b/package.json
@@ -5,7 +5,7 @@
   "dependencies": {
     "autoprefixer": "^10.5.0",
     "postcss": "^8.5.14",
-    "sass": "^1.99.0"
+    "sass": "^1.100.0"
   },
   "scripts": {
     "postinstall": "(cd dumper-companion && npm ci --omit=dev && npm run build) && node build.js",



From noreply at scummvm.org  Sat Jun  6 16:02:41 2026
From: noreply at scummvm.org (lephilousophe)
Date: Sat,  6 Jun 2026 16:02:41 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm-web master ->
 33d3310bd06e5ff4858b8e517be65c71917ac38c
Message-ID: <20260606160241.8BF75700573@ron.scummvm.net>

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

Summary:
33d3310bd0 Bump webpack from 5.106.2 to 5.107.1 in /dumper-companion


Commit: 33d3310bd06e5ff4858b8e517be65c71917ac38c
    https://github.com/scummvm/scummvm-web/commit/33d3310bd06e5ff4858b8e517be65c71917ac38c
Author: dependabot[bot] (49699333+dependabot[bot]@users.noreply.github.com)
Date: 2026-06-06T18:02:38+02:00

Commit Message:
Bump webpack from 5.106.2 to 5.107.1 in /dumper-companion

Bumps [webpack](https://github.com/webpack/webpack) from 5.106.2 to 5.107.1.
- [Release notes](https://github.com/webpack/webpack/releases)
- [Changelog](https://github.com/webpack/webpack/blob/main/CHANGELOG.md)
- [Commits](https://github.com/webpack/webpack/compare/v5.106.2...v5.107.1)

---
updated-dependencies:
- dependency-name: webpack
  dependency-version: 5.107.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support at github.com>

Changed paths:
    dumper-companion/package-lock.json
    dumper-companion/package.json


diff --git a/dumper-companion/package-lock.json b/dumper-companion/package-lock.json
index 973a74bd..1b5a3cfe 100644
--- a/dumper-companion/package-lock.json
+++ b/dumper-companion/package-lock.json
@@ -13,7 +13,7 @@
         "preact": "^10.29.1",
         "punycode": "^2.3.1",
         "ts-loader": "^9.5.7",
-        "webpack": "^5.106.2",
+        "webpack": "^5.107.1",
         "webpack-cli": "^7.0.2"
       },
       "devDependencies": {
@@ -683,26 +683,6 @@
         "eslint": "^9.0.0 || ^10.0.0"
       }
     },
-    "node_modules/@types/eslint": {
-      "version": "9.6.1",
-      "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz",
-      "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==",
-      "license": "MIT",
-      "dependencies": {
-        "@types/estree": "*",
-        "@types/json-schema": "*"
-      }
-    },
-    "node_modules/@types/eslint-scope": {
-      "version": "3.7.7",
-      "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz",
-      "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==",
-      "license": "MIT",
-      "dependencies": {
-        "@types/eslint": "*",
-        "@types/estree": "*"
-      }
-    },
     "node_modules/@types/estree": {
       "version": "1.0.9",
       "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz",
@@ -1889,9 +1869,9 @@
       "license": "ISC"
     },
     "node_modules/enhanced-resolve": {
-      "version": "5.21.2",
-      "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.21.2.tgz",
-      "integrity": "sha512-xe9vQb5kReirPUxgQrXA3ihgbCqssmTiM7cOZ+Gzu+VeGWgpV98lLZvp0dl4yriyAePcewxGUs9UpKD8PET9KQ==",
+      "version": "5.22.0",
+      "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.22.0.tgz",
+      "integrity": "sha512-xYcDWrpELkFzz9SpZ3PlI6Eu6eD93Yf0WLDRxikGhWJ3MAir2SNZTIVCVZqZ/NUyx8AdMc2gT9C0gPiw18kG+A==",
       "license": "MIT",
       "dependencies": {
         "graceful-fs": "^4.2.4",
@@ -5005,12 +4985,11 @@
       }
     },
     "node_modules/webpack": {
-      "version": "5.106.2",
-      "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.106.2.tgz",
-      "integrity": "sha512-wGN3qcrBQIFmQ/c0AiOAQBvrZ5lmY8vbbMv4Mxfgzqd/B6+9pXtLo73WuS1dSGXM5QYY3hZnIbvx+K1xxe6FyA==",
+      "version": "5.107.1",
+      "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.107.1.tgz",
+      "integrity": "sha512-mvdIWxj/H6QsfgDdH9djne3a5dYcmEmtsXGESkypaGN5jXjF/b+9KDlmTDQ2TKlFUeA2fI9Y65kihD30JOdB+Q==",
       "license": "MIT",
       "dependencies": {
-        "@types/eslint-scope": "^3.7.7",
         "@types/estree": "^1.0.8",
         "@types/json-schema": "^7.0.15",
         "@webassemblyjs/ast": "^1.14.1",
@@ -5020,20 +4999,20 @@
         "acorn-import-phases": "^1.0.3",
         "browserslist": "^4.28.1",
         "chrome-trace-event": "^1.0.2",
-        "enhanced-resolve": "^5.20.0",
-        "es-module-lexer": "^2.0.0",
+        "enhanced-resolve": "^5.21.4",
+        "es-module-lexer": "^2.1.0",
         "eslint-scope": "5.1.1",
         "events": "^3.2.0",
         "glob-to-regexp": "^0.4.1",
         "graceful-fs": "^4.2.11",
-        "loader-runner": "^4.3.1",
+        "loader-runner": "^4.3.2",
         "mime-db": "^1.54.0",
         "neo-async": "^2.6.2",
         "schema-utils": "^4.3.3",
         "tapable": "^2.3.0",
-        "terser-webpack-plugin": "^5.3.17",
+        "terser-webpack-plugin": "^5.5.0",
         "watchpack": "^2.5.1",
-        "webpack-sources": "^3.3.4"
+        "webpack-sources": "^3.4.1"
       },
       "bin": {
         "webpack": "bin/webpack.js"
diff --git a/dumper-companion/package.json b/dumper-companion/package.json
index 0321cb32..90e97964 100644
--- a/dumper-companion/package.json
+++ b/dumper-companion/package.json
@@ -12,7 +12,7 @@
     "punycode": "^2.3.1",
     "@types/punycode": "^2.1.4",
     "ts-loader": "^9.5.7",
-    "webpack": "^5.106.2",
+    "webpack": "^5.107.1",
     "webpack-cli": "^7.0.2"
   },
   "devDependencies": {



From noreply at scummvm.org  Sat Jun  6 16:04:55 2026
From: noreply at scummvm.org (ScummVM git)
Date: Sat,  6 Jun 2026 16:04:55 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm-web
 dependabot/npm_and_yarn/patch-releases-7601ed2b6b ->
 f2146bcbe5df4a116204579caaf5f52d5caf8a3d
Message-ID: <20260606160455.0FA9D700573@ron.scummvm.net>

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

Summary:
f2146bcbe5 Bump the patch-releases group across 2 directories with 4 updates


Commit: f2146bcbe5df4a116204579caaf5f52d5caf8a3d
    https://github.com/scummvm/scummvm-web/commit/f2146bcbe5df4a116204579caaf5f52d5caf8a3d
Author: dependabot[bot] (49699333+dependabot[bot]@users.noreply.github.com)
Date: 2026-06-06T16:04:52Z

Commit Message:
Bump the patch-releases group across 2 directories with 4 updates

Bumps the patch-releases group with 1 update in the / directory: [postcss](https://github.com/postcss/postcss).
Bumps the patch-releases group with 3 updates in the /dumper-companion directory: [preact](https://github.com/preactjs/preact), [webpack](https://github.com/webpack/webpack) and [webpack-cli](https://github.com/webpack/webpack-cli).


Updates `postcss` from 8.5.14 to 8.5.15
- [Release notes](https://github.com/postcss/postcss/releases)
- [Changelog](https://github.com/postcss/postcss/blob/main/CHANGELOG.md)
- [Commits](https://github.com/postcss/postcss/compare/8.5.14...8.5.15)

Updates `preact` from 10.29.1 to 10.29.2
- [Release notes](https://github.com/preactjs/preact/releases)
- [Commits](https://github.com/preactjs/preact/compare/10.29.1...10.29.2)

Updates `webpack` from 5.107.1 to 5.107.2
- [Release notes](https://github.com/webpack/webpack/releases)
- [Changelog](https://github.com/webpack/webpack/blob/main/CHANGELOG.md)
- [Commits](https://github.com/webpack/webpack/compare/v5.107.1...v5.107.2)

Updates `webpack-cli` from 7.0.2 to 7.0.3
- [Release notes](https://github.com/webpack/webpack-cli/releases)
- [Changelog](https://github.com/webpack/webpack-cli/blob/main/CHANGELOG.md)
- [Commits](https://github.com/webpack/webpack-cli/compare/webpack-cli at 7.0.2...webpack-cli@7.0.3)

---
updated-dependencies:
- dependency-name: postcss
  dependency-version: 8.5.15
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: patch-releases
- dependency-name: preact
  dependency-version: 10.29.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: patch-releases
- dependency-name: webpack
  dependency-version: 5.107.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: patch-releases
- dependency-name: webpack-cli
  dependency-version: 7.0.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: patch-releases
...

Signed-off-by: dependabot[bot] <support at github.com>

Changed paths:
    dumper-companion/package-lock.json
    dumper-companion/package.json
    package-lock.json
    package.json


diff --git a/dumper-companion/package-lock.json b/dumper-companion/package-lock.json
index 1b5a3cfe..6a4c8b3a 100644
--- a/dumper-companion/package-lock.json
+++ b/dumper-companion/package-lock.json
@@ -10,11 +10,11 @@
       "dependencies": {
         "@types/punycode": "^2.1.4",
         "@zip.js/zip.js": "^2.8.26",
-        "preact": "^10.29.1",
+        "preact": "^10.29.2",
         "punycode": "^2.3.1",
         "ts-loader": "^9.5.7",
-        "webpack": "^5.107.1",
-        "webpack-cli": "^7.0.2"
+        "webpack": "^5.107.2",
+        "webpack-cli": "^7.0.3"
       },
       "devDependencies": {
         "@eslint/js": "^9.39.4",
@@ -2422,15 +2422,6 @@
       ],
       "license": "BSD-3-Clause"
     },
-    "node_modules/fastest-levenshtein": {
-      "version": "1.0.16",
-      "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz",
-      "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==",
-      "license": "MIT",
-      "engines": {
-        "node": ">= 4.9.1"
-      }
-    },
     "node_modules/fdir": {
       "version": "6.5.0",
       "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
@@ -3971,9 +3962,9 @@
       }
     },
     "node_modules/preact": {
-      "version": "10.29.1",
-      "resolved": "https://registry.npmjs.org/preact/-/preact-10.29.1.tgz",
-      "integrity": "sha512-gQCLc/vWroE8lIpleXtdJhTFDogTdZG9AjMUpVkDf2iTCNwYNWA+u16dL41TqUDJO4gm2IgrcMv3uTpjd4Pwmg==",
+      "version": "10.29.2",
+      "resolved": "https://registry.npmjs.org/preact/-/preact-10.29.2.tgz",
+      "integrity": "sha512-7tNmwg/7mzzAoB/8kSg6Hl37JraAZw3Z3A0JSY7VXlZwo82Xn0G7wKbNNs2qoF4ZEEsQGTwDAroNdqKs1ofJxQ==",
       "license": "MIT",
       "funding": {
         "type": "opencollective",
@@ -4985,9 +4976,9 @@
       }
     },
     "node_modules/webpack": {
-      "version": "5.107.1",
-      "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.107.1.tgz",
-      "integrity": "sha512-mvdIWxj/H6QsfgDdH9djne3a5dYcmEmtsXGESkypaGN5jXjF/b+9KDlmTDQ2TKlFUeA2fI9Y65kihD30JOdB+Q==",
+      "version": "5.107.2",
+      "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.107.2.tgz",
+      "integrity": "sha512-v7RhXaJbpMlV0D7hC7lb2EbnxkoeUqf9qhKr6lozx3Q48pmFrqqNRmZFUEGmi7pSwm6fCQ2H1IjvCkHqdpVdjQ==",
       "license": "MIT",
       "dependencies": {
         "@types/estree": "^1.0.8",
@@ -4999,7 +4990,7 @@
         "acorn-import-phases": "^1.0.3",
         "browserslist": "^4.28.1",
         "chrome-trace-event": "^1.0.2",
-        "enhanced-resolve": "^5.21.4",
+        "enhanced-resolve": "^5.22.0",
         "es-module-lexer": "^2.1.0",
         "eslint-scope": "5.1.1",
         "events": "^3.2.0",
@@ -5012,7 +5003,7 @@
         "tapable": "^2.3.0",
         "terser-webpack-plugin": "^5.5.0",
         "watchpack": "^2.5.1",
-        "webpack-sources": "^3.4.1"
+        "webpack-sources": "^3.5.0"
       },
       "bin": {
         "webpack": "bin/webpack.js"
@@ -5031,16 +5022,15 @@
       }
     },
     "node_modules/webpack-cli": {
-      "version": "7.0.2",
-      "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-7.0.2.tgz",
-      "integrity": "sha512-dB0R4T+C/8YuvM+fabdvil6QE44/ChDXikV5lOOkrUeCkW5hTJv2pGLE3keh+D5hjYw8icBaJkZzpFoaHV4T+g==",
+      "version": "7.0.3",
+      "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-7.0.3.tgz",
+      "integrity": "sha512-2E2C6A1e2El7791zQgTH7LPIuwLjRliow9OHS/qlJc9pwhZlCoL/uiwqd/1WSlXT83wJfmfDbkcqHXuXoPJZ3g==",
       "license": "MIT",
       "dependencies": {
-        "@discoveryjs/json-ext": "^1.0.0",
+        "@discoveryjs/json-ext": "^1.1.0",
         "commander": "^14.0.3",
         "cross-spawn": "^7.0.6",
         "envinfo": "^7.14.0",
-        "fastest-levenshtein": "^1.0.12",
         "import-local": "^3.0.2",
         "interpret": "^3.1.1",
         "rechoir": "^0.8.0",
@@ -5094,9 +5084,9 @@
       }
     },
     "node_modules/webpack-sources": {
-      "version": "3.4.1",
-      "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.4.1.tgz",
-      "integrity": "sha512-eACpxRN02yaawnt+uUNIF7Qje6A9zArxBbcAJjK1PK3S9Ycg5jIuJ8pW4q8EMnwNZCEGltcjkRx1QzOxOkKD8A==",
+      "version": "3.5.0",
+      "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.5.0.tgz",
+      "integrity": "sha512-HPuy+uuoTCaaoEoI1LQ3JN9+vrPBvEesnnX1jADHy728cHSMlq4wUc4afYqahq2B1mhQVZxCXOkNTnXltr+2vQ==",
       "license": "MIT",
       "engines": {
         "node": ">=10.13.0"
diff --git a/dumper-companion/package.json b/dumper-companion/package.json
index 90e97964..ad3d9ed0 100644
--- a/dumper-companion/package.json
+++ b/dumper-companion/package.json
@@ -8,12 +8,12 @@
   },
   "dependencies": {
     "@zip.js/zip.js": "^2.8.26",
-    "preact": "^10.29.1",
+    "preact": "^10.29.2",
     "punycode": "^2.3.1",
     "@types/punycode": "^2.1.4",
     "ts-loader": "^9.5.7",
-    "webpack": "^5.107.1",
-    "webpack-cli": "^7.0.2"
+    "webpack": "^5.107.2",
+    "webpack-cli": "^7.0.3"
   },
   "devDependencies": {
     "@eslint/js": "^9.39.4",
diff --git a/package-lock.json b/package-lock.json
index ac46eac8..e9483602 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -10,7 +10,7 @@
       "hasInstallScript": true,
       "dependencies": {
         "autoprefixer": "^10.5.0",
-        "postcss": "^8.5.14",
+        "postcss": "^8.5.15",
         "sass": "^1.100.0"
       }
     },
@@ -544,9 +544,9 @@
       }
     },
     "node_modules/postcss": {
-      "version": "8.5.14",
-      "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.14.tgz",
-      "integrity": "sha512-SoSL4+OSEtR99LHFZQiJLkT59C5B1amGO1NzTwj7TT1qCUgUO6hxOvzkOYxD+vMrXBM3XJIKzokoERdqQq/Zmg==",
+      "version": "8.5.15",
+      "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz",
+      "integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==",
       "funding": [
         {
           "type": "opencollective",
@@ -563,7 +563,7 @@
       ],
       "license": "MIT",
       "dependencies": {
-        "nanoid": "^3.3.11",
+        "nanoid": "^3.3.12",
         "picocolors": "^1.1.1",
         "source-map-js": "^1.2.1"
       },
diff --git a/package.json b/package.json
index b28c581b..2ac1ed45 100644
--- a/package.json
+++ b/package.json
@@ -4,7 +4,7 @@
   "version": "1.0.0",
   "dependencies": {
     "autoprefixer": "^10.5.0",
-    "postcss": "^8.5.14",
+    "postcss": "^8.5.15",
     "sass": "^1.100.0"
   },
   "scripts": {



From noreply at scummvm.org  Sat Jun  6 16:05:06 2026
From: noreply at scummvm.org (ScummVM git)
Date: Sat,  6 Jun 2026 16:05:06 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm-web
 dependabot/npm_and_yarn/dumper-companion/eslint-72480bc8cd ->
 6304a72f722f6a8db63c5fda7b7a5f12d8d9d16c
Message-ID: <20260606160506.AFA47700573@ron.scummvm.net>

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

Summary:
6304a72f72 Bump the eslint group across 1 directory with 3 updates


Commit: 6304a72f722f6a8db63c5fda7b7a5f12d8d9d16c
    https://github.com/scummvm/scummvm-web/commit/6304a72f722f6a8db63c5fda7b7a5f12d8d9d16c
Author: dependabot[bot] (49699333+dependabot[bot]@users.noreply.github.com)
Date: 2026-06-06T16:05:03Z

Commit Message:
Bump the eslint group across 1 directory with 3 updates

Bumps the eslint group with 3 updates in the /dumper-companion directory: [@eslint/js](https://github.com/eslint/eslint/tree/HEAD/packages/js), [eslint](https://github.com/eslint/eslint) and [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint).


Updates `@eslint/js` from 9.39.4 to 10.0.1
- [Release notes](https://github.com/eslint/eslint/releases)
- [Commits](https://github.com/eslint/eslint/commits/v10.0.1/packages/js)

Updates `eslint` from 9.39.4 to 10.4.1
- [Release notes](https://github.com/eslint/eslint/releases)
- [Commits](https://github.com/eslint/eslint/compare/v9.39.4...v10.4.1)

Updates `typescript-eslint` from 8.59.2 to 8.60.1
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/typescript-eslint/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.60.1/packages/typescript-eslint)

---
updated-dependencies:
- dependency-name: "@eslint/js"
  dependency-version: 10.0.1
  dependency-type: direct:development
  update-type: version-update:semver-major
  dependency-group: eslint
- dependency-name: eslint
  dependency-version: 10.4.0
  dependency-type: direct:development
  update-type: version-update:semver-major
  dependency-group: eslint
- dependency-name: typescript-eslint
  dependency-version: 8.59.4
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: eslint
...

Signed-off-by: dependabot[bot] <support at github.com>

Changed paths:
    dumper-companion/package-lock.json
    dumper-companion/package.json


diff --git a/dumper-companion/package-lock.json b/dumper-companion/package-lock.json
index 1b5a3cfe..7d25c98c 100644
--- a/dumper-companion/package-lock.json
+++ b/dumper-companion/package-lock.json
@@ -17,12 +17,12 @@
         "webpack-cli": "^7.0.2"
       },
       "devDependencies": {
-        "@eslint/js": "^9.39.4",
+        "@eslint/js": "^10.0.1",
         "@stylistic/eslint-plugin": "^5.10.0",
-        "eslint": "^9.39.4",
+        "eslint": "^10.4.1",
         "eslint-config-preact": "^2.0.0",
         "globals": "^17.5.0",
-        "typescript-eslint": "^8.58.2"
+        "typescript-eslint": "^8.60.1"
       }
     },
     "node_modules/@babel/code-frame": {
@@ -81,35 +81,6 @@
         "url": "https://opencollective.com/babel"
       }
     },
-    "node_modules/@babel/eslint-parser": {
-      "version": "7.28.6",
-      "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.28.6.tgz",
-      "integrity": "sha512-QGmsKi2PBO/MHSQk+AAgA9R6OHQr+VqnniFE0eMWZcVcfBZoA2dKn2hUsl3Csg/Plt9opRUWdY7//VXsrIlEiA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@nicolo-ribaudo/eslint-scope-5-internals": "5.1.1-v1",
-        "eslint-visitor-keys": "^2.1.0",
-        "semver": "^6.3.1"
-      },
-      "engines": {
-        "node": "^10.13.0 || ^12.13.0 || >=14.0.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.11.0",
-        "eslint": "^7.5.0 || ^8.0.0 || ^9.0.0"
-      }
-    },
-    "node_modules/@babel/eslint-parser/node_modules/eslint-visitor-keys": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz",
-      "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==",
-      "dev": true,
-      "license": "Apache-2.0",
-      "engines": {
-        "node": ">=10"
-      }
-    },
     "node_modules/@babel/generator": {
       "version": "7.29.1",
       "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz",
@@ -385,118 +356,89 @@
       }
     },
     "node_modules/@eslint/config-array": {
-      "version": "0.21.2",
-      "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.2.tgz",
-      "integrity": "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==",
+      "version": "0.23.5",
+      "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.23.5.tgz",
+      "integrity": "sha512-Y3kKLvC1dvTOT+oGlqNQ1XLqK6D1HU2YXPc52NmAlJZbMMWDzGYXMiPRJ8TYD39muD/OTjlZmNJ4ib7dvSrMBA==",
       "dev": true,
       "license": "Apache-2.0",
       "dependencies": {
-        "@eslint/object-schema": "^2.1.7",
+        "@eslint/object-schema": "^3.0.5",
         "debug": "^4.3.1",
-        "minimatch": "^3.1.5"
+        "minimatch": "^10.2.4"
       },
       "engines": {
-        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+        "node": "^20.19.0 || ^22.13.0 || >=24"
       }
     },
     "node_modules/@eslint/config-helpers": {
-      "version": "0.4.2",
-      "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz",
-      "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==",
+      "version": "0.6.0",
+      "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.6.0.tgz",
+      "integrity": "sha512-ii6Bw9jJ2zi2cWA2Z+9/QZ/+3DX6kwaV5Q986D/CdP3Lap3w/pgQZ373FV7byY/i7L4IRH/G43I5dz1ClsCbpA==",
       "dev": true,
       "license": "Apache-2.0",
       "dependencies": {
-        "@eslint/core": "^0.17.0"
+        "@eslint/core": "^1.2.1"
       },
       "engines": {
-        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+        "node": "^20.19.0 || ^22.13.0 || >=24"
       }
     },
     "node_modules/@eslint/core": {
-      "version": "0.17.0",
-      "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz",
-      "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==",
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/@eslint/core/-/core-1.2.1.tgz",
+      "integrity": "sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ==",
       "dev": true,
       "license": "Apache-2.0",
       "dependencies": {
         "@types/json-schema": "^7.0.15"
       },
       "engines": {
-        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
-      }
-    },
-    "node_modules/@eslint/eslintrc": {
-      "version": "3.3.5",
-      "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.5.tgz",
-      "integrity": "sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "ajv": "^6.14.0",
-        "debug": "^4.3.2",
-        "espree": "^10.0.1",
-        "globals": "^14.0.0",
-        "ignore": "^5.2.0",
-        "import-fresh": "^3.2.1",
-        "js-yaml": "^4.1.1",
-        "minimatch": "^3.1.5",
-        "strip-json-comments": "^3.1.1"
-      },
-      "engines": {
-        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
-      },
-      "funding": {
-        "url": "https://opencollective.com/eslint"
-      }
-    },
-    "node_modules/@eslint/eslintrc/node_modules/globals": {
-      "version": "14.0.0",
-      "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz",
-      "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=18"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
+        "node": "^20.19.0 || ^22.13.0 || >=24"
       }
     },
     "node_modules/@eslint/js": {
-      "version": "9.39.4",
-      "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.4.tgz",
-      "integrity": "sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==",
+      "version": "10.0.1",
+      "resolved": "https://registry.npmjs.org/@eslint/js/-/js-10.0.1.tgz",
+      "integrity": "sha512-zeR9k5pd4gxjZ0abRoIaxdc7I3nDktoXZk2qOv9gCNWx3mVwEn32VRhyLaRsDiJjTs0xq/T8mfPtyuXu7GWBcA==",
       "dev": true,
       "license": "MIT",
       "engines": {
-        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+        "node": "^20.19.0 || ^22.13.0 || >=24"
       },
       "funding": {
         "url": "https://eslint.org/donate"
+      },
+      "peerDependencies": {
+        "eslint": "^10.0.0"
+      },
+      "peerDependenciesMeta": {
+        "eslint": {
+          "optional": true
+        }
       }
     },
     "node_modules/@eslint/object-schema": {
-      "version": "2.1.7",
-      "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz",
-      "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==",
+      "version": "3.0.5",
+      "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-3.0.5.tgz",
+      "integrity": "sha512-vqTaUEgxzm+YDSdElad6PiRoX4t8VGDjCtt05zn4nU810UIx/uNEV7/lZJ6KwFThKZOzOxzXy48da+No7HZaMw==",
       "dev": true,
       "license": "Apache-2.0",
       "engines": {
-        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+        "node": "^20.19.0 || ^22.13.0 || >=24"
       }
     },
     "node_modules/@eslint/plugin-kit": {
-      "version": "0.4.1",
-      "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz",
-      "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==",
+      "version": "0.7.2",
+      "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.7.2.tgz",
+      "integrity": "sha512-+CNAzxglkrpNf/kKywqQfk74QjtceuOE7Qm+AF8miRvPF/wmmK5+OJOgVh3AVTT3RP2mH3+FOaxlE5v72owk0A==",
       "dev": true,
       "license": "Apache-2.0",
       "dependencies": {
-        "@eslint/core": "^0.17.0",
+        "@eslint/core": "^1.2.1",
         "levn": "^0.4.1"
       },
       "engines": {
-        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+        "node": "^20.19.0 || ^22.13.0 || >=24"
       }
     },
     "node_modules/@humanfs/core": {
@@ -683,6 +625,13 @@
         "eslint": "^9.0.0 || ^10.0.0"
       }
     },
+    "node_modules/@types/esrecurse": {
+      "version": "4.3.1",
+      "resolved": "https://registry.npmjs.org/@types/esrecurse/-/esrecurse-4.3.1.tgz",
+      "integrity": "sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==",
+      "dev": true,
+      "license": "MIT"
+    },
     "node_modules/@types/estree": {
       "version": "1.0.9",
       "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz",
@@ -711,17 +660,17 @@
       "license": "MIT"
     },
     "node_modules/@typescript-eslint/eslint-plugin": {
-      "version": "8.59.2",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.59.2.tgz",
-      "integrity": "sha512-j/bwmkBvHUtPNxzuWe5z6BEk3q54YRyGlBXkSsmfoih7zNrBvl5A9A98anlp/7JbyZcWIJ8KXo/3Tq/DjFLtuQ==",
+      "version": "8.60.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.60.1.tgz",
+      "integrity": "sha512-JQ4S5GB0tfjO8BuJ4fcX+HodkzJjYBV+7OJ+wLygaX7OGQ7FudyHL4NSCA6ob+w3Yn+5MkKIozOwQhXeM7opVg==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
         "@eslint-community/regexpp": "^4.12.2",
-        "@typescript-eslint/scope-manager": "8.59.2",
-        "@typescript-eslint/type-utils": "8.59.2",
-        "@typescript-eslint/utils": "8.59.2",
-        "@typescript-eslint/visitor-keys": "8.59.2",
+        "@typescript-eslint/scope-manager": "8.60.1",
+        "@typescript-eslint/type-utils": "8.60.1",
+        "@typescript-eslint/utils": "8.60.1",
+        "@typescript-eslint/visitor-keys": "8.60.1",
         "ignore": "^7.0.5",
         "natural-compare": "^1.4.0",
         "ts-api-utils": "^2.5.0"
@@ -734,7 +683,7 @@
         "url": "https://opencollective.com/typescript-eslint"
       },
       "peerDependencies": {
-        "@typescript-eslint/parser": "^8.59.2",
+        "@typescript-eslint/parser": "^8.60.1",
         "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
         "typescript": ">=4.8.4 <6.1.0"
       }
@@ -750,16 +699,16 @@
       }
     },
     "node_modules/@typescript-eslint/parser": {
-      "version": "8.59.2",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.59.2.tgz",
-      "integrity": "sha512-plR3pp6D+SSUn1HM7xvSkx12/DhoHInI2YF35KAcVFNZvlC0gtrWqx7Qq1oH2Ssgi0vlFRCTbP+DZc7B9+TtsQ==",
+      "version": "8.60.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.60.1.tgz",
+      "integrity": "sha512-A0M6ua6H252bVjPvvtSgl2QA4+ET9S5Mtkb2GDyTxIhH/C4qDItT7RQNO5PhMC6NXGYXOR9dIalcDDgBKT7oFA==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@typescript-eslint/scope-manager": "8.59.2",
-        "@typescript-eslint/types": "8.59.2",
-        "@typescript-eslint/typescript-estree": "8.59.2",
-        "@typescript-eslint/visitor-keys": "8.59.2",
+        "@typescript-eslint/scope-manager": "8.60.1",
+        "@typescript-eslint/types": "8.60.1",
+        "@typescript-eslint/typescript-estree": "8.60.1",
+        "@typescript-eslint/visitor-keys": "8.60.1",
         "debug": "^4.4.3"
       },
       "engines": {
@@ -775,14 +724,14 @@
       }
     },
     "node_modules/@typescript-eslint/project-service": {
-      "version": "8.59.2",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.59.2.tgz",
-      "integrity": "sha512-+2hqvEkeyf/0FBor67duF0Ll7Ot8jyKzDQOSrxazF/danillRq2DwR9dLptsXpoZQqxE1UisSmoZewrlPas9Vw==",
+      "version": "8.60.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.60.1.tgz",
+      "integrity": "sha512-eXkTH2bxmXlqD1RnOPmLZ9ZM9D3VwSx04JOwBnP9RQ+yUA5a2Mu7SfW8uaV2Aon53NJzZlZYuX7tn91Izf+xaw==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@typescript-eslint/tsconfig-utils": "^8.59.2",
-        "@typescript-eslint/types": "^8.59.2",
+        "@typescript-eslint/tsconfig-utils": "^8.60.1",
+        "@typescript-eslint/types": "^8.60.1",
         "debug": "^4.4.3"
       },
       "engines": {
@@ -797,14 +746,14 @@
       }
     },
     "node_modules/@typescript-eslint/scope-manager": {
-      "version": "8.59.2",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.59.2.tgz",
-      "integrity": "sha512-JzfyEpEtOU89CcFSwyNS3mu4MLvLSXqnmX05+aKBDM+TdR5jzcGOEBwxwGNxrEQ7p/z6kK2WyioCGBf2zZBnvg==",
+      "version": "8.60.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.60.1.tgz",
+      "integrity": "sha512-gvI5OQoptnxQnchOirukCuQ55svJSTuD/4k5+pC267xyBtYry748R9/c3tYUzb/iE6RZfllRz2lVulLCHkTm4w==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@typescript-eslint/types": "8.59.2",
-        "@typescript-eslint/visitor-keys": "8.59.2"
+        "@typescript-eslint/types": "8.60.1",
+        "@typescript-eslint/visitor-keys": "8.60.1"
       },
       "engines": {
         "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -815,9 +764,9 @@
       }
     },
     "node_modules/@typescript-eslint/tsconfig-utils": {
-      "version": "8.59.2",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.59.2.tgz",
-      "integrity": "sha512-BKK4alN7oi4C/zv4VqHQ+uRU+lTa6JGIZ7s1juw7b3RHo9OfKB+bKX3u0iVZetdsUCBBkSbdWbarJbmN0fTeSw==",
+      "version": "8.60.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.60.1.tgz",
+      "integrity": "sha512-nh8w4qAteiKuZu3pSSzG/yGKpw0OlkrKnzFmbVRenKaD4qc+7i1GrmZaLVkr8rk4uipiPGMOW4YsM6WmKZ5CvA==",
       "dev": true,
       "license": "MIT",
       "engines": {
@@ -832,15 +781,15 @@
       }
     },
     "node_modules/@typescript-eslint/type-utils": {
-      "version": "8.59.2",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.59.2.tgz",
-      "integrity": "sha512-nhqaj1nmTdVVl/BP5omXNRGO38jn5iosis2vbdmupF2txCf8ylWT8lx+JlvMYYVqzGVKtjojUFoQ3JRWK+mfzQ==",
+      "version": "8.60.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.60.1.tgz",
+      "integrity": "sha512-sdwTrpjosW7ANQYJ39ZBF1ZyEMEGVB2UsikrserVM/30a/F1dTLnu9bGxEdosugyu5caigjLrR2qiD11asjI1A==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@typescript-eslint/types": "8.59.2",
-        "@typescript-eslint/typescript-estree": "8.59.2",
-        "@typescript-eslint/utils": "8.59.2",
+        "@typescript-eslint/types": "8.60.1",
+        "@typescript-eslint/typescript-estree": "8.60.1",
+        "@typescript-eslint/utils": "8.60.1",
         "debug": "^4.4.3",
         "ts-api-utils": "^2.5.0"
       },
@@ -857,9 +806,9 @@
       }
     },
     "node_modules/@typescript-eslint/types": {
-      "version": "8.59.2",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.59.2.tgz",
-      "integrity": "sha512-e82GVOE8Ps3E++Egvb6Y3Dw0S10u8NkQ9KXmtRhCWJJ8kDhOJTvtMAWnFL16kB1583goCWXsr0NieKCZMs2/0Q==",
+      "version": "8.60.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.60.1.tgz",
+      "integrity": "sha512-4h0tY8ppCkdCzcrl2YM5M3my0xsE1Tf8om3owEu5oPWmXwkKRmk0j0LGDzYBGUcAlesEbxBhazqu/K4cu3Ug7w==",
       "dev": true,
       "license": "MIT",
       "engines": {
@@ -871,16 +820,16 @@
       }
     },
     "node_modules/@typescript-eslint/typescript-estree": {
-      "version": "8.59.2",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.59.2.tgz",
-      "integrity": "sha512-o0XPGNwcWw+FIwStOWn+BwBuEmL6QXP0rsvAFg7ET1dey1Nr6Wb1ac8p5HEsK0ygO/6mUxlk+YWQD9xcb/nnXg==",
+      "version": "8.60.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.60.1.tgz",
+      "integrity": "sha512-alpRkfG8hlVE5kdJW2GkfgDgXxold3e8e4l6EnmhRmRLbekgAPCCGDVD++sABy9FcgPFroq+uFcCSM1vR57Cew==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@typescript-eslint/project-service": "8.59.2",
-        "@typescript-eslint/tsconfig-utils": "8.59.2",
-        "@typescript-eslint/types": "8.59.2",
-        "@typescript-eslint/visitor-keys": "8.59.2",
+        "@typescript-eslint/project-service": "8.60.1",
+        "@typescript-eslint/tsconfig-utils": "8.60.1",
+        "@typescript-eslint/types": "8.60.1",
+        "@typescript-eslint/visitor-keys": "8.60.1",
         "debug": "^4.4.3",
         "minimatch": "^10.2.2",
         "semver": "^7.7.3",
@@ -898,49 +847,10 @@
         "typescript": ">=4.8.4 <6.1.0"
       }
     },
-    "node_modules/@typescript-eslint/typescript-estree/node_modules/balanced-match": {
-      "version": "4.0.4",
-      "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
-      "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": "18 || 20 || >=22"
-      }
-    },
-    "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": {
-      "version": "5.0.6",
-      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz",
-      "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "balanced-match": "^4.0.2"
-      },
-      "engines": {
-        "node": "18 || 20 || >=22"
-      }
-    },
-    "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": {
-      "version": "10.2.5",
-      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz",
-      "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==",
-      "dev": true,
-      "license": "BlueOak-1.0.0",
-      "dependencies": {
-        "brace-expansion": "^5.0.5"
-      },
-      "engines": {
-        "node": "18 || 20 || >=22"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/isaacs"
-      }
-    },
     "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": {
-      "version": "7.7.4",
-      "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
-      "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
+      "version": "7.8.2",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.2.tgz",
+      "integrity": "sha512-c8jsqUZm3omBOI66G90z1Dyw5z622G8oLG+omfsHBJf3CWQTlOcwOjvOG6wtiNfW6anKm/eA39LMwMtMez2TiQ==",
       "dev": true,
       "license": "ISC",
       "bin": {
@@ -951,16 +861,16 @@
       }
     },
     "node_modules/@typescript-eslint/utils": {
-      "version": "8.59.2",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.59.2.tgz",
-      "integrity": "sha512-Juw3EinkXqjaffxz6roowvV7GZT/kET5vSKKZT6upl5TXdWkLkYmNPXwDDL2Vkt2DPn0nODIS4egC/0AGxKo/Q==",
+      "version": "8.60.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.60.1.tgz",
+      "integrity": "sha512-h2MPBLoNtjc3qZWfY3Tl51yPorQ2McHn8pJfcMNTcIvrrZrr90Ykffit0yjrPFWQcRcUxzH20+6OcVdW4yHtUg==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
         "@eslint-community/eslint-utils": "^4.9.1",
-        "@typescript-eslint/scope-manager": "8.59.2",
-        "@typescript-eslint/types": "8.59.2",
-        "@typescript-eslint/typescript-estree": "8.59.2"
+        "@typescript-eslint/scope-manager": "8.60.1",
+        "@typescript-eslint/types": "8.60.1",
+        "@typescript-eslint/typescript-estree": "8.60.1"
       },
       "engines": {
         "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -975,13 +885,13 @@
       }
     },
     "node_modules/@typescript-eslint/visitor-keys": {
-      "version": "8.59.2",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.59.2.tgz",
-      "integrity": "sha512-NwjLUnGy8/Zfx23fl50tRC8rYaYnM52xNRYFAXvmiil9yh1+K6aRVQMnzW6gQB/1DLgWt977lYQn7C+wtgXZiA==",
+      "version": "8.60.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.60.1.tgz",
+      "integrity": "sha512-EbGRQg4FhrmwLodl+t3JNAnXHWVr9Vp+Zl1QBZVPY4ByfkzIT8cX3K6QWODHtkIZqqJVEWvhHSx3v5PDHsaQag==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@typescript-eslint/types": "8.59.2",
+        "@typescript-eslint/types": "8.60.1",
         "eslint-visitor-keys": "^5.0.0"
       },
       "engines": {
@@ -1279,13 +1189,6 @@
         "url": "https://github.com/chalk/ansi-styles?sponsor=1"
       }
     },
-    "node_modules/argparse": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
-      "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
-      "dev": true,
-      "license": "Python-2.0"
-    },
     "node_modules/array-buffer-byte-length": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz",
@@ -1468,11 +1371,14 @@
       }
     },
     "node_modules/balanced-match": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
-      "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+      "version": "4.0.4",
+      "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
+      "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==",
       "dev": true,
-      "license": "MIT"
+      "license": "MIT",
+      "engines": {
+        "node": "18 || 20 || >=22"
+      }
     },
     "node_modules/baseline-browser-mapping": {
       "version": "2.10.28",
@@ -1487,14 +1393,16 @@
       }
     },
     "node_modules/brace-expansion": {
-      "version": "1.1.14",
-      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz",
-      "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==",
+      "version": "5.0.6",
+      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz",
+      "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "balanced-match": "^1.0.0",
-        "concat-map": "0.0.1"
+        "balanced-match": "^4.0.2"
+      },
+      "engines": {
+        "node": "18 || 20 || >=22"
       }
     },
     "node_modules/braces": {
@@ -1598,16 +1506,6 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
-    "node_modules/callsites": {
-      "version": "3.1.0",
-      "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
-      "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=6"
-      }
-    },
     "node_modules/caniuse-lite": {
       "version": "1.0.30001792",
       "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001792.tgz",
@@ -2016,9 +1914,9 @@
       "license": "MIT"
     },
     "node_modules/es-object-atoms": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
-      "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz",
+      "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
@@ -2098,33 +1996,30 @@
       }
     },
     "node_modules/eslint": {
-      "version": "9.39.4",
-      "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.4.tgz",
-      "integrity": "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==",
+      "version": "10.4.1",
+      "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.4.1.tgz",
+      "integrity": "sha512-AyIKhnOBuOAdueD7RB3xB+YeAWScb9jHsJBgH2Hcde8InP5JYhqrRR6iTMHyTEwgENK54Cp44e4v8BwNhsuHuw==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
         "@eslint-community/eslint-utils": "^4.8.0",
-        "@eslint-community/regexpp": "^4.12.1",
-        "@eslint/config-array": "^0.21.2",
-        "@eslint/config-helpers": "^0.4.2",
-        "@eslint/core": "^0.17.0",
-        "@eslint/eslintrc": "^3.3.5",
-        "@eslint/js": "9.39.4",
-        "@eslint/plugin-kit": "^0.4.1",
+        "@eslint-community/regexpp": "^4.12.2",
+        "@eslint/config-array": "^0.23.5",
+        "@eslint/config-helpers": "^0.6.0",
+        "@eslint/core": "^1.2.1",
+        "@eslint/plugin-kit": "^0.7.2",
         "@humanfs/node": "^0.16.6",
         "@humanwhocodes/module-importer": "^1.0.1",
         "@humanwhocodes/retry": "^0.4.2",
         "@types/estree": "^1.0.6",
         "ajv": "^6.14.0",
-        "chalk": "^4.0.0",
         "cross-spawn": "^7.0.6",
         "debug": "^4.3.2",
         "escape-string-regexp": "^4.0.0",
-        "eslint-scope": "^8.4.0",
-        "eslint-visitor-keys": "^4.2.1",
-        "espree": "^10.4.0",
-        "esquery": "^1.5.0",
+        "eslint-scope": "^9.1.2",
+        "eslint-visitor-keys": "^5.0.1",
+        "espree": "^11.2.0",
+        "esquery": "^1.7.0",
         "esutils": "^2.0.2",
         "fast-deep-equal": "^3.1.3",
         "file-entry-cache": "^8.0.0",
@@ -2134,8 +2029,7 @@
         "imurmurhash": "^0.1.4",
         "is-glob": "^4.0.0",
         "json-stable-stringify-without-jsonify": "^1.0.1",
-        "lodash.merge": "^4.6.2",
-        "minimatch": "^3.1.5",
+        "minimatch": "^10.2.4",
         "natural-compare": "^1.4.0",
         "optionator": "^0.9.3"
       },
@@ -2143,7 +2037,7 @@
         "eslint": "bin/eslint.js"
       },
       "engines": {
-        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+        "node": "^20.19.0 || ^22.13.0 || >=24"
       },
       "funding": {
         "url": "https://eslint.org/donate"
@@ -2178,68 +2072,57 @@
         "eslint": "^8.57.1 || ^9.0.0"
       }
     },
-    "node_modules/eslint-config-preact/node_modules/globals": {
-      "version": "16.5.0",
-      "resolved": "https://registry.npmjs.org/globals/-/globals-16.5.0.tgz",
-      "integrity": "sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=18"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/eslint-plugin-compat": {
-      "version": "6.2.1",
-      "resolved": "https://registry.npmjs.org/eslint-plugin-compat/-/eslint-plugin-compat-6.2.1.tgz",
-      "integrity": "sha512-gLKqUH+lQcCL+HzsROUjBDvakc5Zaga51Y4ZAkPCXc41pzKBfyluqTr2j8zOx8QQQb7zyglu1LVoL5aSNWf2SQ==",
+    "node_modules/eslint-config-preact/node_modules/@babel/eslint-parser": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.29.7.tgz",
+      "integrity": "sha512-zxt+UJTOMKvUt3yOg+D58MLuz334pHp93qifMFcjIIO+9hN6t+ufw2gi7vDPMpxvfnHRR+3VVXvIjineCcgyXw==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@mdn/browser-compat-data": "^6.1.1",
-        "ast-metadata-inferer": "^0.8.1",
-        "browserslist": "^4.25.2",
-        "find-up": "^5.0.0",
-        "globals": "^15.7.0",
-        "lodash.memoize": "^4.1.2",
-        "semver": "^7.6.2"
+        "@nicolo-ribaudo/eslint-scope-5-internals": "5.1.1-v1",
+        "eslint-visitor-keys": "^2.1.0",
+        "semver": "^6.3.1"
       },
       "engines": {
-        "node": ">=18.x"
+        "node": "^10.13.0 || ^12.13.0 || >=14.0.0"
       },
       "peerDependencies": {
-        "eslint": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0"
+        "@babel/core": "^7.11.0",
+        "eslint": "^7.5.0 || ^8.0.0 || ^9.0.0"
       }
     },
-    "node_modules/eslint-plugin-compat/node_modules/globals": {
-      "version": "15.15.0",
-      "resolved": "https://registry.npmjs.org/globals/-/globals-15.15.0.tgz",
-      "integrity": "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==",
+    "node_modules/eslint-config-preact/node_modules/@eslint/js": {
+      "version": "9.39.4",
+      "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.4.tgz",
+      "integrity": "sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==",
       "dev": true,
       "license": "MIT",
       "engines": {
-        "node": ">=18"
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
       },
       "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
+        "url": "https://eslint.org/donate"
       }
     },
-    "node_modules/eslint-plugin-compat/node_modules/semver": {
-      "version": "7.7.4",
-      "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
-      "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
+    "node_modules/eslint-config-preact/node_modules/balanced-match": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+      "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
       "dev": true,
-      "license": "ISC",
-      "bin": {
-        "semver": "bin/semver.js"
-      },
-      "engines": {
-        "node": ">=10"
+      "license": "MIT"
+    },
+    "node_modules/eslint-config-preact/node_modules/brace-expansion": {
+      "version": "1.1.15",
+      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz",
+      "integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "balanced-match": "^1.0.0",
+        "concat-map": "0.0.1"
       }
     },
-    "node_modules/eslint-plugin-react": {
+    "node_modules/eslint-config-preact/node_modules/eslint-plugin-react": {
       "version": "7.37.5",
       "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.5.tgz",
       "integrity": "sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==",
@@ -2272,7 +2155,7 @@
         "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7"
       }
     },
-    "node_modules/eslint-plugin-react-hooks": {
+    "node_modules/eslint-config-preact/node_modules/eslint-plugin-react-hooks": {
       "version": "5.2.0",
       "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.2.0.tgz",
       "integrity": "sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==",
@@ -2285,18 +2168,104 @@
         "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0"
       }
     },
+    "node_modules/eslint-config-preact/node_modules/eslint-visitor-keys": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz",
+      "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/eslint-config-preact/node_modules/globals": {
+      "version": "16.5.0",
+      "resolved": "https://registry.npmjs.org/globals/-/globals-16.5.0.tgz",
+      "integrity": "sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/eslint-config-preact/node_modules/minimatch": {
+      "version": "3.1.5",
+      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
+      "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "brace-expansion": "^1.1.7"
+      },
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/eslint-plugin-compat": {
+      "version": "6.2.1",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-compat/-/eslint-plugin-compat-6.2.1.tgz",
+      "integrity": "sha512-gLKqUH+lQcCL+HzsROUjBDvakc5Zaga51Y4ZAkPCXc41pzKBfyluqTr2j8zOx8QQQb7zyglu1LVoL5aSNWf2SQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@mdn/browser-compat-data": "^6.1.1",
+        "ast-metadata-inferer": "^0.8.1",
+        "browserslist": "^4.25.2",
+        "find-up": "^5.0.0",
+        "globals": "^15.7.0",
+        "lodash.memoize": "^4.1.2",
+        "semver": "^7.6.2"
+      },
+      "engines": {
+        "node": ">=18.x"
+      },
+      "peerDependencies": {
+        "eslint": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0"
+      }
+    },
+    "node_modules/eslint-plugin-compat/node_modules/globals": {
+      "version": "15.15.0",
+      "resolved": "https://registry.npmjs.org/globals/-/globals-15.15.0.tgz",
+      "integrity": "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/eslint-plugin-compat/node_modules/semver": {
+      "version": "7.7.4",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
+      "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
+      "dev": true,
+      "license": "ISC",
+      "bin": {
+        "semver": "bin/semver.js"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
     "node_modules/eslint-scope": {
-      "version": "8.4.0",
-      "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz",
-      "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==",
+      "version": "9.1.2",
+      "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-9.1.2.tgz",
+      "integrity": "sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==",
       "dev": true,
       "license": "BSD-2-Clause",
       "dependencies": {
+        "@types/esrecurse": "^4.3.1",
+        "@types/estree": "^1.0.8",
         "esrecurse": "^4.3.0",
         "estraverse": "^5.2.0"
       },
       "engines": {
-        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+        "node": "^20.19.0 || ^22.13.0 || >=24"
       },
       "funding": {
         "url": "https://opencollective.com/eslint"
@@ -2315,6 +2284,37 @@
         "url": "https://opencollective.com/eslint"
       }
     },
+    "node_modules/eslint/node_modules/eslint-visitor-keys": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz",
+      "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "engines": {
+        "node": "^20.19.0 || ^22.13.0 || >=24"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint"
+      }
+    },
+    "node_modules/eslint/node_modules/espree": {
+      "version": "11.2.0",
+      "resolved": "https://registry.npmjs.org/espree/-/espree-11.2.0.tgz",
+      "integrity": "sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "dependencies": {
+        "acorn": "^8.16.0",
+        "acorn-jsx": "^5.3.2",
+        "eslint-visitor-keys": "^5.0.1"
+      },
+      "engines": {
+        "node": "^20.19.0 || ^22.13.0 || >=24"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint"
+      }
+    },
     "node_modules/espree": {
       "version": "10.4.0",
       "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz",
@@ -2824,23 +2824,6 @@
         "node": ">= 4"
       }
     },
-    "node_modules/import-fresh": {
-      "version": "3.3.1",
-      "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
-      "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "parent-module": "^1.0.0",
-        "resolve-from": "^4.0.0"
-      },
-      "engines": {
-        "node": ">=6"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
     "node_modules/import-local": {
       "version": "3.2.0",
       "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz",
@@ -3372,19 +3355,6 @@
       "dev": true,
       "license": "MIT"
     },
-    "node_modules/js-yaml": {
-      "version": "4.1.1",
-      "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
-      "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "argparse": "^2.0.1"
-      },
-      "bin": {
-        "js-yaml": "bin/js-yaml.js"
-      }
-    },
     "node_modules/jsesc": {
       "version": "3.1.0",
       "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",
@@ -3517,13 +3487,6 @@
       "dev": true,
       "license": "MIT"
     },
-    "node_modules/lodash.merge": {
-      "version": "4.6.2",
-      "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
-      "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
-      "dev": true,
-      "license": "MIT"
-    },
     "node_modules/loose-envify": {
       "version": "1.4.0",
       "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
@@ -3598,16 +3561,19 @@
       }
     },
     "node_modules/minimatch": {
-      "version": "3.1.5",
-      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
-      "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
+      "version": "10.2.5",
+      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz",
+      "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==",
       "dev": true,
-      "license": "ISC",
+      "license": "BlueOak-1.0.0",
       "dependencies": {
-        "brace-expansion": "^1.1.7"
+        "brace-expansion": "^5.0.5"
       },
       "engines": {
-        "node": "*"
+        "node": "18 || 20 || >=22"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
       }
     },
     "node_modules/ms": {
@@ -3840,19 +3806,6 @@
         "node": ">=6"
       }
     },
-    "node_modules/parent-module": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
-      "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "callsites": "^3.0.0"
-      },
-      "engines": {
-        "node": ">=6"
-      }
-    },
     "node_modules/path-exists": {
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
@@ -4105,14 +4058,14 @@
       }
     },
     "node_modules/resolve": {
-      "version": "2.0.0-next.6",
-      "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.6.tgz",
-      "integrity": "sha512-3JmVl5hMGtJ3kMmB3zi3DL25KfkCEyy3Tw7Gmw7z5w8M9WlwoPFnIvwChzu1+cF3iaK3sp18hhPz8ANeimdJfA==",
+      "version": "2.0.0-next.7",
+      "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.7.tgz",
+      "integrity": "sha512-tqt+NBWwyaMgw3zDsnygx4CByWjQEJHOPMdslYhppaQSJUtL/D4JO9CcBBlhPoI8lz9oJIDXkwXfhF4aWqP8xQ==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
         "es-errors": "^1.3.0",
-        "is-core-module": "^2.16.1",
+        "is-core-module": "^2.16.2",
         "node-exports-info": "^1.6.0",
         "object-keys": "^1.1.1",
         "path-parse": "^1.0.7",
@@ -4149,16 +4102,6 @@
         "node": ">=8"
       }
     },
-    "node_modules/resolve-from": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
-      "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=4"
-      }
-    },
     "node_modules/safe-array-concat": {
       "version": "1.1.4",
       "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.4.tgz",
@@ -4517,19 +4460,20 @@
       }
     },
     "node_modules/string.prototype.trim": {
-      "version": "1.2.10",
-      "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz",
-      "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==",
+      "version": "1.2.11",
+      "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.11.tgz",
+      "integrity": "sha512-PwvK7BU+CMTJGYQCTZb5RWXIML92lftJLhQz1tBzgKiqGxJaMlBAa48POXaNAC2s4y8jr3EFqrkF9+44neS46w==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "call-bind": "^1.0.8",
-        "call-bound": "^1.0.2",
+        "call-bind": "^1.0.9",
+        "call-bound": "^1.0.4",
         "define-data-property": "^1.1.4",
         "define-properties": "^1.2.1",
-        "es-abstract": "^1.23.5",
-        "es-object-atoms": "^1.0.0",
-        "has-property-descriptors": "^1.0.2"
+        "es-abstract": "^1.24.2",
+        "es-object-atoms": "^1.1.2",
+        "has-property-descriptors": "^1.0.2",
+        "safe-regex-test": "^1.1.0"
       },
       "engines": {
         "node": ">= 0.4"
@@ -4539,16 +4483,16 @@
       }
     },
     "node_modules/string.prototype.trimend": {
-      "version": "1.0.9",
-      "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz",
-      "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==",
+      "version": "1.0.10",
+      "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.10.tgz",
+      "integrity": "sha512-2+3aDAOmPTmuFwjDnmJG2ctEkQKVki7vOSqaxkv42Mowj1V6PnvuwFCRrR5lChUux1TBskPjfkeTOhqczDMxTw==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "call-bind": "^1.0.8",
-        "call-bound": "^1.0.2",
+        "call-bind": "^1.0.9",
+        "call-bound": "^1.0.4",
         "define-properties": "^1.2.1",
-        "es-object-atoms": "^1.0.0"
+        "es-object-atoms": "^1.1.2"
       },
       "engines": {
         "node": ">= 0.4"
@@ -4575,19 +4519,6 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
-    "node_modules/strip-json-comments": {
-      "version": "3.1.1",
-      "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
-      "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=8"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
     "node_modules/supports-color": {
       "version": "7.2.0",
       "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
@@ -4704,9 +4635,9 @@
       }
     },
     "node_modules/tinyglobby": {
-      "version": "0.2.16",
-      "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz",
-      "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==",
+      "version": "0.2.17",
+      "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz",
+      "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
@@ -4848,18 +4779,18 @@
       }
     },
     "node_modules/typed-array-length": {
-      "version": "1.0.7",
-      "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz",
-      "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==",
+      "version": "1.0.8",
+      "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.8.tgz",
+      "integrity": "sha512-phPGCwqr2+Qo0fwniCE8e4pKnGu/yFb5nD5Y8bf0EEeiI5GklnACYA9GFy/DrAeRrKHXvHn+1SUsOWgJp6RO+g==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "call-bind": "^1.0.7",
-        "for-each": "^0.3.3",
-        "gopd": "^1.0.1",
-        "is-typed-array": "^1.1.13",
-        "possible-typed-array-names": "^1.0.0",
-        "reflect.getprototypeof": "^1.0.6"
+        "call-bind": "^1.0.9",
+        "for-each": "^0.3.5",
+        "gopd": "^1.2.0",
+        "is-typed-array": "^1.1.15",
+        "possible-typed-array-names": "^1.1.0",
+        "reflect.getprototypeof": "^1.0.10"
       },
       "engines": {
         "node": ">= 0.4"
@@ -4883,16 +4814,16 @@
       }
     },
     "node_modules/typescript-eslint": {
-      "version": "8.59.2",
-      "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.59.2.tgz",
-      "integrity": "sha512-pJw051uomb3ZeCzGTpRb8RbEqB5Y4WWet8gl/GcTlU35BSx0PVdZ86/bqkQCyKKuraVQEK7r6kBHQXF+fBhkoQ==",
+      "version": "8.60.1",
+      "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.60.1.tgz",
+      "integrity": "sha512-6m5hkkRAp8lKvhVpcprAIn5KkehQEh+47oHH2VGnExEh7dhNxXlg6GPAOIu6TxbVQxhebrJDvjl3020ooiWCMA==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@typescript-eslint/eslint-plugin": "8.59.2",
-        "@typescript-eslint/parser": "8.59.2",
-        "@typescript-eslint/typescript-estree": "8.59.2",
-        "@typescript-eslint/utils": "8.59.2"
+        "@typescript-eslint/eslint-plugin": "8.60.1",
+        "@typescript-eslint/parser": "8.60.1",
+        "@typescript-eslint/typescript-estree": "8.60.1",
+        "@typescript-eslint/utils": "8.60.1"
       },
       "engines": {
         "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -5207,14 +5138,14 @@
       }
     },
     "node_modules/which-typed-array": {
-      "version": "1.1.20",
-      "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.20.tgz",
-      "integrity": "sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==",
+      "version": "1.1.22",
+      "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.22.tgz",
+      "integrity": "sha512-fvO4ExWMFsqyhG3AiPAObMuY1lxaqgYcxbc49CNdWDDECOJNgQyvsOWVwbZc+qf3rzRtxojBK+CMEv0Ld5CYpw==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
         "available-typed-arrays": "^1.0.7",
-        "call-bind": "^1.0.8",
+        "call-bind": "^1.0.9",
         "call-bound": "^1.0.4",
         "for-each": "^0.3.5",
         "get-proto": "^1.0.1",
diff --git a/dumper-companion/package.json b/dumper-companion/package.json
index 90e97964..aa653f22 100644
--- a/dumper-companion/package.json
+++ b/dumper-companion/package.json
@@ -16,11 +16,11 @@
     "webpack-cli": "^7.0.2"
   },
   "devDependencies": {
-    "@eslint/js": "^9.39.4",
+    "@eslint/js": "^10.0.1",
     "@stylistic/eslint-plugin": "^5.10.0",
-    "eslint": "^9.39.4",
+    "eslint": "^10.4.1",
     "eslint-config-preact": "^2.0.0",
     "globals": "^17.5.0",
-    "typescript-eslint": "^8.58.2"
+    "typescript-eslint": "^8.60.1"
   }
 }



From noreply at scummvm.org  Sat Jun  6 16:13:30 2026
From: noreply at scummvm.org (lephilousophe)
Date: Sat,  6 Jun 2026 16:13:30 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm-web master ->
 1fb9cda3a12ee839c95fa3101a1501bc37e204e6
Message-ID: <20260606161330.63911700573@ron.scummvm.net>

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

Summary:
1fb9cda3a1 WEB: Update versions


Commit: 1fb9cda3a12ee839c95fa3101a1501bc37e204e6
    https://github.com/scummvm/scummvm-web/commit/1fb9cda3a12ee839c95fa3101a1501bc37e204e6
Author: Le Philousophe (lephilousophe at users.noreply.github.com)
Date: 2026-06-06T18:12:46+02:00

Commit Message:
WEB: Update versions

Changed paths:
    composer.json
    composer.lock
    dumper-companion/package-lock.json
    package-lock.json


diff --git a/composer.json b/composer.json
index ea5b9871..3b42aa56 100644
--- a/composer.json
+++ b/composer.json
@@ -12,17 +12,17 @@
   "require": {
     "php": ">=8.2.0",
     "ext-intl": "*",
-    "smarty/smarty": "^5.5",
-    "ezyang/htmlpurifier": "^4.18",
+    "smarty/smarty": "^5.8",
+    "ezyang/htmlpurifier": "^4.19",
     "altorouter/altorouter": "^2.0",
-    "matomo/device-detector": "^6.4",
+    "matomo/device-detector": "^6.5",
     "spatie/yaml-front-matter": "^2.1",
-    "erusev/parsedown": "^1.7",
+    "erusev/parsedown": "^1.8",
     "phpfastcache/phpfastcache": "^9.2",
-    "league/csv": "^9.24",
+    "league/csv": "^9.28",
     "symfony/yaml": "^7.3",
-    "predis/predis": "^3.2",
-    "guzzlehttp/guzzle": "^7.9",
+    "predis/predis": "^3.5",
+    "guzzlehttp/guzzle": "^7.11",
     "propel/propel": "~2.0 at dev"
   },
   "config": {
@@ -31,7 +31,7 @@
     }
   },
   "require-dev": {
-    "phpstan/phpstan": "^2.1",
+    "phpstan/phpstan": "^2.2",
     "squizlabs/php_codesniffer": "^4.0"
   },
   "suggest": {
diff --git a/composer.lock b/composer.lock
index 9b69cd84..3116c27c 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
         "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
         "This file is @generated automatically"
     ],
-    "content-hash": "455b4bc6c108a62971ac2a6e90e0c606",
+    "content-hash": "f2c309a97912f13704b2180df3b2c3f5",
     "packages": [
         {
             "name": "altorouter/altorouter",
@@ -183,25 +183,26 @@
         },
         {
             "name": "guzzlehttp/guzzle",
-            "version": "7.10.0",
+            "version": "7.11.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/guzzle/guzzle.git",
-                "reference": "b51ac707cfa420b7bfd4e4d5e510ba8008e822b4"
+                "reference": "c987f8ce84b8434fa430795eca0f3430663da72b"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/guzzle/guzzle/zipball/b51ac707cfa420b7bfd4e4d5e510ba8008e822b4",
-                "reference": "b51ac707cfa420b7bfd4e4d5e510ba8008e822b4",
+                "url": "https://api.github.com/repos/guzzle/guzzle/zipball/c987f8ce84b8434fa430795eca0f3430663da72b",
+                "reference": "c987f8ce84b8434fa430795eca0f3430663da72b",
                 "shasum": ""
             },
             "require": {
                 "ext-json": "*",
-                "guzzlehttp/promises": "^2.3",
-                "guzzlehttp/psr7": "^2.8",
+                "guzzlehttp/promises": "^2.5",
+                "guzzlehttp/psr7": "^2.11",
                 "php": "^7.2.5 || ^8.0",
                 "psr/http-client": "^1.0",
-                "symfony/deprecation-contracts": "^2.2 || ^3.0"
+                "symfony/deprecation-contracts": "^2.5 || ^3.0",
+                "symfony/polyfill-php80": "^1.24"
             },
             "provide": {
                 "psr/http-client-implementation": "1.0"
@@ -210,8 +211,9 @@
                 "bamarni/composer-bin-plugin": "^1.8.2",
                 "ext-curl": "*",
                 "guzzle/client-integration-tests": "3.0.2",
+                "guzzlehttp/test-server": "^0.4",
                 "php-http/message-factory": "^1.1",
-                "phpunit/phpunit": "^8.5.39 || ^9.6.20",
+                "phpunit/phpunit": "^8.5.52 || ^9.6.34",
                 "psr/log": "^1.1 || ^2.0 || ^3.0"
             },
             "suggest": {
@@ -289,7 +291,7 @@
             ],
             "support": {
                 "issues": "https://github.com/guzzle/guzzle/issues",
-                "source": "https://github.com/guzzle/guzzle/tree/7.10.0"
+                "source": "https://github.com/guzzle/guzzle/tree/7.11.0"
             },
             "funding": [
                 {
@@ -305,28 +307,29 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2025-08-23T22:36:01+00:00"
+            "time": "2026-06-02T12:40:51+00:00"
         },
         {
             "name": "guzzlehttp/promises",
-            "version": "2.3.0",
+            "version": "2.5.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/guzzle/promises.git",
-                "reference": "481557b130ef3790cf82b713667b43030dc9c957"
+                "reference": "4360e982f87f5f258bf872d094647791db2f4c8e"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/guzzle/promises/zipball/481557b130ef3790cf82b713667b43030dc9c957",
-                "reference": "481557b130ef3790cf82b713667b43030dc9c957",
+                "url": "https://api.github.com/repos/guzzle/promises/zipball/4360e982f87f5f258bf872d094647791db2f4c8e",
+                "reference": "4360e982f87f5f258bf872d094647791db2f4c8e",
                 "shasum": ""
             },
             "require": {
-                "php": "^7.2.5 || ^8.0"
+                "php": "^7.2.5 || ^8.0",
+                "symfony/deprecation-contracts": "^2.5 || ^3.0"
             },
             "require-dev": {
                 "bamarni/composer-bin-plugin": "^1.8.2",
-                "phpunit/phpunit": "^8.5.44 || ^9.6.25"
+                "phpunit/phpunit": "^8.5.52 || ^9.6.34"
             },
             "type": "library",
             "extra": {
@@ -372,7 +375,7 @@
             ],
             "support": {
                 "issues": "https://github.com/guzzle/promises/issues",
-                "source": "https://github.com/guzzle/promises/tree/2.3.0"
+                "source": "https://github.com/guzzle/promises/tree/2.5.0"
             },
             "funding": [
                 {
@@ -388,27 +391,29 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2025-08-22T14:34:08+00:00"
+            "time": "2026-06-02T12:23:43+00:00"
         },
         {
             "name": "guzzlehttp/psr7",
-            "version": "2.9.0",
+            "version": "2.11.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/guzzle/psr7.git",
-                "reference": "7d0ed42f28e42d61352a7a79de682e5e67fec884"
+                "reference": "bbb5e61349fa5cb822b3e87842b951088b76b81f"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/guzzle/psr7/zipball/7d0ed42f28e42d61352a7a79de682e5e67fec884",
-                "reference": "7d0ed42f28e42d61352a7a79de682e5e67fec884",
+                "url": "https://api.github.com/repos/guzzle/psr7/zipball/bbb5e61349fa5cb822b3e87842b951088b76b81f",
+                "reference": "bbb5e61349fa5cb822b3e87842b951088b76b81f",
                 "shasum": ""
             },
             "require": {
                 "php": "^7.2.5 || ^8.0",
                 "psr/http-factory": "^1.0",
                 "psr/http-message": "^1.1 || ^2.0",
-                "ralouphie/getallheaders": "^3.0"
+                "ralouphie/getallheaders": "^3.0",
+                "symfony/deprecation-contracts": "^2.5 || ^3.0",
+                "symfony/polyfill-php80": "^1.24"
             },
             "provide": {
                 "psr/http-factory-implementation": "1.0",
@@ -416,9 +421,9 @@
             },
             "require-dev": {
                 "bamarni/composer-bin-plugin": "^1.8.2",
-                "http-interop/http-factory-tests": "0.9.0",
+                "http-interop/http-factory-tests": "1.1.0",
                 "jshttp/mime-db": "1.54.0.1",
-                "phpunit/phpunit": "^8.5.44 || ^9.6.25"
+                "phpunit/phpunit": "^8.5.52 || ^9.6.34"
             },
             "suggest": {
                 "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses"
@@ -489,7 +494,7 @@
             ],
             "support": {
                 "issues": "https://github.com/guzzle/psr7/issues",
-                "source": "https://github.com/guzzle/psr7/tree/2.9.0"
+                "source": "https://github.com/guzzle/psr7/tree/2.11.0"
             },
             "funding": [
                 {
@@ -505,7 +510,7 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2026-03-10T16:41:02+00:00"
+            "time": "2026-06-02T12:30:48+00:00"
         },
         {
             "name": "league/csv",
@@ -855,16 +860,16 @@
         },
         {
             "name": "predis/predis",
-            "version": "v3.4.2",
+            "version": "v3.5.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/predis/predis.git",
-                "reference": "2033429520d8997a7815a2485f56abe6d2d0e075"
+                "reference": "8cc4319c06924c8ff0c5c7eec4243a19e3be32f1"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/predis/predis/zipball/2033429520d8997a7815a2485f56abe6d2d0e075",
-                "reference": "2033429520d8997a7815a2485f56abe6d2d0e075",
+                "url": "https://api.github.com/repos/predis/predis/zipball/8cc4319c06924c8ff0c5c7eec4243a19e3be32f1",
+                "reference": "8cc4319c06924c8ff0c5c7eec4243a19e3be32f1",
                 "shasum": ""
             },
             "require": {
@@ -906,7 +911,7 @@
             ],
             "support": {
                 "issues": "https://github.com/predis/predis/issues",
-                "source": "https://github.com/predis/predis/tree/v3.4.2"
+                "source": "https://github.com/predis/predis/tree/v3.5.0"
             },
             "funding": [
                 {
@@ -914,7 +919,7 @@
                     "type": "github"
                 }
             ],
-            "time": "2026-03-09T20:33:04+00:00"
+            "time": "2026-06-02T19:25:56+00:00"
         },
         {
             "name": "propel/propel",
@@ -1621,16 +1626,16 @@
         },
         {
             "name": "symfony/console",
-            "version": "v7.4.9",
+            "version": "v7.4.13",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/console.git",
-                "reference": "d7d2b64a45a89d607865927b176fa51c33ddbb58"
+                "reference": "85095d2573eaefaf35e40b9513a9bf09f72cd217"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/console/zipball/d7d2b64a45a89d607865927b176fa51c33ddbb58",
-                "reference": "d7d2b64a45a89d607865927b176fa51c33ddbb58",
+                "url": "https://api.github.com/repos/symfony/console/zipball/85095d2573eaefaf35e40b9513a9bf09f72cd217",
+                "reference": "85095d2573eaefaf35e40b9513a9bf09f72cd217",
                 "shasum": ""
             },
             "require": {
@@ -1695,7 +1700,7 @@
                 "terminal"
             ],
             "support": {
-                "source": "https://github.com/symfony/console/tree/v7.4.9"
+                "source": "https://github.com/symfony/console/tree/v7.4.13"
             },
             "funding": [
                 {
@@ -1715,7 +1720,7 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2026-04-22T15:21:55+00:00"
+            "time": "2026-05-24T08:56:14+00:00"
         },
         {
             "name": "symfony/deprecation-contracts",
@@ -1790,16 +1795,16 @@
         },
         {
             "name": "symfony/filesystem",
-            "version": "v7.4.9",
+            "version": "v7.4.11",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/filesystem.git",
-                "reference": "dcd8f96bcdc0f128ec406c765cc066c6035d1be3"
+                "reference": "d721ea61b4a5fba8c5b6e7c1feda19efea144b50"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/filesystem/zipball/dcd8f96bcdc0f128ec406c765cc066c6035d1be3",
-                "reference": "dcd8f96bcdc0f128ec406c765cc066c6035d1be3",
+                "url": "https://api.github.com/repos/symfony/filesystem/zipball/d721ea61b4a5fba8c5b6e7c1feda19efea144b50",
+                "reference": "d721ea61b4a5fba8c5b6e7c1feda19efea144b50",
                 "shasum": ""
             },
             "require": {
@@ -1836,7 +1841,7 @@
             "description": "Provides basic utilities for the filesystem",
             "homepage": "https://symfony.com",
             "support": {
-                "source": "https://github.com/symfony/filesystem/tree/v7.4.9"
+                "source": "https://github.com/symfony/filesystem/tree/v7.4.11"
             },
             "funding": [
                 {
@@ -1856,7 +1861,7 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2026-04-18T13:18:21+00:00"
+            "time": "2026-05-11T16:38:44+00:00"
         },
         {
             "name": "symfony/finder",
@@ -2011,16 +2016,16 @@
         },
         {
             "name": "symfony/polyfill-intl-grapheme",
-            "version": "v1.37.0",
+            "version": "v1.38.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/polyfill-intl-grapheme.git",
-                "reference": "4864388bfbd3001ce88e234fab652acd91fdc57e"
+                "reference": "e9247d281d694a5120554d9afaf54e070e88a603"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/4864388bfbd3001ce88e234fab652acd91fdc57e",
-                "reference": "4864388bfbd3001ce88e234fab652acd91fdc57e",
+                "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/e9247d281d694a5120554d9afaf54e070e88a603",
+                "reference": "e9247d281d694a5120554d9afaf54e070e88a603",
                 "shasum": ""
             },
             "require": {
@@ -2069,7 +2074,7 @@
                 "shim"
             ],
             "support": {
-                "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.37.0"
+                "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.38.1"
             },
             "funding": [
                 {
@@ -2089,20 +2094,20 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2026-04-26T13:13:48+00:00"
+            "time": "2026-05-26T05:58:03+00:00"
         },
         {
             "name": "symfony/polyfill-intl-normalizer",
-            "version": "v1.37.0",
+            "version": "v1.38.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/polyfill-intl-normalizer.git",
-                "reference": "3833d7255cc303546435cb650316bff708a1c75c"
+                "reference": "2d446c214bdbe5b71bde5011b060a05fece3ae6b"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/3833d7255cc303546435cb650316bff708a1c75c",
-                "reference": "3833d7255cc303546435cb650316bff708a1c75c",
+                "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/2d446c214bdbe5b71bde5011b060a05fece3ae6b",
+                "reference": "2d446c214bdbe5b71bde5011b060a05fece3ae6b",
                 "shasum": ""
             },
             "require": {
@@ -2154,7 +2159,7 @@
                 "shim"
             ],
             "support": {
-                "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.37.0"
+                "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.38.0"
             },
             "funding": [
                 {
@@ -2174,20 +2179,20 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2024-09-09T11:45:10+00:00"
+            "time": "2026-05-25T13:48:31+00:00"
         },
         {
             "name": "symfony/polyfill-mbstring",
-            "version": "v1.37.0",
+            "version": "v1.38.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/polyfill-mbstring.git",
-                "reference": "6a21eb99c6973357967f6ce3708cd55a6bec6315"
+                "reference": "14c5439eec4ccff081ac14eca2dc57feb2a66d92"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6a21eb99c6973357967f6ce3708cd55a6bec6315",
-                "reference": "6a21eb99c6973357967f6ce3708cd55a6bec6315",
+                "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/14c5439eec4ccff081ac14eca2dc57feb2a66d92",
+                "reference": "14c5439eec4ccff081ac14eca2dc57feb2a66d92",
                 "shasum": ""
             },
             "require": {
@@ -2239,7 +2244,7 @@
                 "shim"
             ],
             "support": {
-                "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.37.0"
+                "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.38.1"
             },
             "funding": [
                 {
@@ -2259,20 +2264,104 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2026-04-10T17:25:58+00:00"
+            "time": "2026-05-26T12:51:13+00:00"
         },
         {
-            "name": "symfony/polyfill-php83",
+            "name": "symfony/polyfill-php80",
             "version": "v1.37.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/polyfill-php80.git",
+                "reference": "dfb55726c3a76ea3b6459fcfda1ec2d80a682411"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/dfb55726c3a76ea3b6459fcfda1ec2d80a682411",
+                "reference": "dfb55726c3a76ea3b6459fcfda1ec2d80a682411",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.2"
+            },
+            "type": "library",
+            "extra": {
+                "thanks": {
+                    "url": "https://github.com/symfony/polyfill",
+                    "name": "symfony/polyfill"
+                }
+            },
+            "autoload": {
+                "files": [
+                    "bootstrap.php"
+                ],
+                "psr-4": {
+                    "Symfony\\Polyfill\\Php80\\": ""
+                },
+                "classmap": [
+                    "Resources/stubs"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Ion Bazan",
+                    "email": "ion.bazan at gmail.com"
+                },
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "p at tchwork.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "compatibility",
+                "polyfill",
+                "portable",
+                "shim"
+            ],
+            "support": {
+                "source": "https://github.com/symfony/polyfill-php80/tree/v1.37.0"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://github.com/nicolas-grekas",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2026-04-10T16:19:22+00:00"
+        },
+        {
+            "name": "symfony/polyfill-php83",
+            "version": "v1.38.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/polyfill-php83.git",
-                "reference": "3600c2cb22399e25bb226e4a135ce91eeb2a6149"
+                "reference": "8339098cae28673c15cce00d80734af0453054e2"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/3600c2cb22399e25bb226e4a135ce91eeb2a6149",
-                "reference": "3600c2cb22399e25bb226e4a135ce91eeb2a6149",
+                "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/8339098cae28673c15cce00d80734af0453054e2",
+                "reference": "8339098cae28673c15cce00d80734af0453054e2",
                 "shasum": ""
             },
             "require": {
@@ -2319,7 +2408,7 @@
                 "shim"
             ],
             "support": {
-                "source": "https://github.com/symfony/polyfill-php83/tree/v1.37.0"
+                "source": "https://github.com/symfony/polyfill-php83/tree/v1.38.1"
             },
             "funding": [
                 {
@@ -2339,7 +2428,7 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2026-04-10T17:25:58+00:00"
+            "time": "2026-05-26T12:51:13+00:00"
         },
         {
             "name": "symfony/service-contracts",
@@ -2430,16 +2519,16 @@
         },
         {
             "name": "symfony/string",
-            "version": "v7.4.8",
+            "version": "v7.4.13",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/string.git",
-                "reference": "114ac57257d75df748eda23dd003878080b8e688"
+                "reference": "961683010db3b27ec6ebcd7308e6e1ee8fa7ffde"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/string/zipball/114ac57257d75df748eda23dd003878080b8e688",
-                "reference": "114ac57257d75df748eda23dd003878080b8e688",
+                "url": "https://api.github.com/repos/symfony/string/zipball/961683010db3b27ec6ebcd7308e6e1ee8fa7ffde",
+                "reference": "961683010db3b27ec6ebcd7308e6e1ee8fa7ffde",
                 "shasum": ""
             },
             "require": {
@@ -2497,7 +2586,7 @@
                 "utf8"
             ],
             "support": {
-                "source": "https://github.com/symfony/string/tree/v7.4.8"
+                "source": "https://github.com/symfony/string/tree/v7.4.13"
             },
             "funding": [
                 {
@@ -2517,7 +2606,7 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2026-03-24T13:12:05+00:00"
+            "time": "2026-05-23T15:23:29+00:00"
         },
         {
             "name": "symfony/translation",
@@ -2807,16 +2896,16 @@
         },
         {
             "name": "symfony/yaml",
-            "version": "v7.4.10",
+            "version": "v7.4.13",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/yaml.git",
-                "reference": "c660d6538545a3e8e65a5621ee3d7a6d352892c7"
+                "reference": "a7ec3b1156faf8815db7683ec7c1e7338e6f977c"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/yaml/zipball/c660d6538545a3e8e65a5621ee3d7a6d352892c7",
-                "reference": "c660d6538545a3e8e65a5621ee3d7a6d352892c7",
+                "url": "https://api.github.com/repos/symfony/yaml/zipball/a7ec3b1156faf8815db7683ec7c1e7338e6f977c",
+                "reference": "a7ec3b1156faf8815db7683ec7c1e7338e6f977c",
                 "shasum": ""
             },
             "require": {
@@ -2859,7 +2948,7 @@
             "description": "Loads and dumps YAML files",
             "homepage": "https://symfony.com",
             "support": {
-                "source": "https://github.com/symfony/yaml/tree/v7.4.10"
+                "source": "https://github.com/symfony/yaml/tree/v7.4.13"
             },
             "funding": [
                 {
@@ -2879,17 +2968,17 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2026-05-05T08:01:55+00:00"
+            "time": "2026-05-25T06:06:12+00:00"
         }
     ],
     "packages-dev": [
         {
             "name": "phpstan/phpstan",
-            "version": "2.1.54",
+            "version": "2.2.2",
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/phpstan/phpstan/zipball/8be50c3992107dc837b17da4d140fbbdf9a5c5bd",
-                "reference": "8be50c3992107dc837b17da4d140fbbdf9a5c5bd",
+                "url": "https://api.github.com/repos/phpstan/phpstan/zipball/e5cc34d491a90e79c216d824f60fe21fd4d93bd6",
+                "reference": "e5cc34d491a90e79c216d824f60fe21fd4d93bd6",
                 "shasum": ""
             },
             "require": {
@@ -2912,6 +3001,17 @@
             "license": [
                 "MIT"
             ],
+            "authors": [
+                {
+                    "name": "Ond??ej Mirtes"
+                },
+                {
+                    "name": "Markus Staab"
+                },
+                {
+                    "name": "Vincent Langlet"
+                }
+            ],
             "description": "PHPStan - PHP Static Analysis Tool",
             "keywords": [
                 "dev",
@@ -2934,7 +3034,7 @@
                     "type": "github"
                 }
             ],
-            "time": "2026-04-29T13:31:09+00:00"
+            "time": "2026-06-05T09:00:01+00:00"
         },
         {
             "name": "squizlabs/php_codesniffer",
@@ -3031,5 +3131,5 @@
     "platform-overrides": {
         "php": "8.2.0"
     },
-    "plugin-api-version": "2.9.0"
+    "plugin-api-version": "2.6.0"
 }
diff --git a/dumper-companion/package-lock.json b/dumper-companion/package-lock.json
index 1b5a3cfe..e7324deb 100644
--- a/dumper-companion/package-lock.json
+++ b/dumper-companion/package-lock.json
@@ -26,13 +26,13 @@
       }
     },
     "node_modules/@babel/code-frame": {
-      "version": "7.29.0",
-      "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz",
-      "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==",
+      "version": "7.29.7",
+      "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz",
+      "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@babel/helper-validator-identifier": "^7.28.5",
+        "@babel/helper-validator-identifier": "^7.29.7",
         "js-tokens": "^4.0.0",
         "picocolors": "^1.1.1"
       },
@@ -41,9 +41,9 @@
       }
     },
     "node_modules/@babel/compat-data": {
-      "version": "7.29.3",
-      "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.3.tgz",
-      "integrity": "sha512-LIVqM46zQWZhj17qA8wb4nW/ixr2y1Nw+r1etiAWgRM6U1IqP+LNhL1yg440jYZR72jCWcWbLWzIosH+uP1fqg==",
+      "version": "7.29.7",
+      "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz",
+      "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==",
       "dev": true,
       "license": "MIT",
       "engines": {
@@ -51,21 +51,21 @@
       }
     },
     "node_modules/@babel/core": {
-      "version": "7.29.0",
-      "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz",
-      "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==",
+      "version": "7.29.7",
+      "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz",
+      "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@babel/code-frame": "^7.29.0",
-        "@babel/generator": "^7.29.0",
-        "@babel/helper-compilation-targets": "^7.28.6",
-        "@babel/helper-module-transforms": "^7.28.6",
-        "@babel/helpers": "^7.28.6",
-        "@babel/parser": "^7.29.0",
-        "@babel/template": "^7.28.6",
-        "@babel/traverse": "^7.29.0",
-        "@babel/types": "^7.29.0",
+        "@babel/code-frame": "^7.29.7",
+        "@babel/generator": "^7.29.7",
+        "@babel/helper-compilation-targets": "^7.29.7",
+        "@babel/helper-module-transforms": "^7.29.7",
+        "@babel/helpers": "^7.29.7",
+        "@babel/parser": "^7.29.7",
+        "@babel/template": "^7.29.7",
+        "@babel/traverse": "^7.29.7",
+        "@babel/types": "^7.29.7",
         "@jridgewell/remapping": "^2.3.5",
         "convert-source-map": "^2.0.0",
         "debug": "^4.1.0",
@@ -82,9 +82,9 @@
       }
     },
     "node_modules/@babel/eslint-parser": {
-      "version": "7.28.6",
-      "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.28.6.tgz",
-      "integrity": "sha512-QGmsKi2PBO/MHSQk+AAgA9R6OHQr+VqnniFE0eMWZcVcfBZoA2dKn2hUsl3Csg/Plt9opRUWdY7//VXsrIlEiA==",
+      "version": "7.29.7",
+      "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.29.7.tgz",
+      "integrity": "sha512-zxt+UJTOMKvUt3yOg+D58MLuz334pHp93qifMFcjIIO+9hN6t+ufw2gi7vDPMpxvfnHRR+3VVXvIjineCcgyXw==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
@@ -111,14 +111,14 @@
       }
     },
     "node_modules/@babel/generator": {
-      "version": "7.29.1",
-      "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz",
-      "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==",
+      "version": "7.29.7",
+      "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.7.tgz",
+      "integrity": "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@babel/parser": "^7.29.0",
-        "@babel/types": "^7.29.0",
+        "@babel/parser": "^7.29.7",
+        "@babel/types": "^7.29.7",
         "@jridgewell/gen-mapping": "^0.3.12",
         "@jridgewell/trace-mapping": "^0.3.28",
         "jsesc": "^3.0.2"
@@ -128,14 +128,14 @@
       }
     },
     "node_modules/@babel/helper-compilation-targets": {
-      "version": "7.28.6",
-      "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz",
-      "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==",
+      "version": "7.29.7",
+      "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz",
+      "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@babel/compat-data": "^7.28.6",
-        "@babel/helper-validator-option": "^7.27.1",
+        "@babel/compat-data": "^7.29.7",
+        "@babel/helper-validator-option": "^7.29.7",
         "browserslist": "^4.24.0",
         "lru-cache": "^5.1.1",
         "semver": "^6.3.1"
@@ -145,9 +145,9 @@
       }
     },
     "node_modules/@babel/helper-globals": {
-      "version": "7.28.0",
-      "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz",
-      "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==",
+      "version": "7.29.7",
+      "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz",
+      "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==",
       "dev": true,
       "license": "MIT",
       "engines": {
@@ -155,29 +155,29 @@
       }
     },
     "node_modules/@babel/helper-module-imports": {
-      "version": "7.28.6",
-      "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz",
-      "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==",
+      "version": "7.29.7",
+      "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz",
+      "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@babel/traverse": "^7.28.6",
-        "@babel/types": "^7.28.6"
+        "@babel/traverse": "^7.29.7",
+        "@babel/types": "^7.29.7"
       },
       "engines": {
         "node": ">=6.9.0"
       }
     },
     "node_modules/@babel/helper-module-transforms": {
-      "version": "7.28.6",
-      "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz",
-      "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==",
+      "version": "7.29.7",
+      "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz",
+      "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@babel/helper-module-imports": "^7.28.6",
-        "@babel/helper-validator-identifier": "^7.28.5",
-        "@babel/traverse": "^7.28.6"
+        "@babel/helper-module-imports": "^7.29.7",
+        "@babel/helper-validator-identifier": "^7.29.7",
+        "@babel/traverse": "^7.29.7"
       },
       "engines": {
         "node": ">=6.9.0"
@@ -187,9 +187,9 @@
       }
     },
     "node_modules/@babel/helper-plugin-utils": {
-      "version": "7.28.6",
-      "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz",
-      "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==",
+      "version": "7.29.7",
+      "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.29.7.tgz",
+      "integrity": "sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==",
       "dev": true,
       "license": "MIT",
       "engines": {
@@ -197,9 +197,9 @@
       }
     },
     "node_modules/@babel/helper-string-parser": {
-      "version": "7.27.1",
-      "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
-      "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==",
+      "version": "7.29.7",
+      "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz",
+      "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==",
       "dev": true,
       "license": "MIT",
       "engines": {
@@ -207,9 +207,9 @@
       }
     },
     "node_modules/@babel/helper-validator-identifier": {
-      "version": "7.28.5",
-      "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz",
-      "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==",
+      "version": "7.29.7",
+      "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz",
+      "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==",
       "dev": true,
       "license": "MIT",
       "engines": {
@@ -217,9 +217,9 @@
       }
     },
     "node_modules/@babel/helper-validator-option": {
-      "version": "7.27.1",
-      "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz",
-      "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==",
+      "version": "7.29.7",
+      "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz",
+      "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==",
       "dev": true,
       "license": "MIT",
       "engines": {
@@ -227,27 +227,27 @@
       }
     },
     "node_modules/@babel/helpers": {
-      "version": "7.29.2",
-      "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.2.tgz",
-      "integrity": "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==",
+      "version": "7.29.7",
+      "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.7.tgz",
+      "integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@babel/template": "^7.28.6",
-        "@babel/types": "^7.29.0"
+        "@babel/template": "^7.29.7",
+        "@babel/types": "^7.29.7"
       },
       "engines": {
         "node": ">=6.9.0"
       }
     },
     "node_modules/@babel/parser": {
-      "version": "7.29.3",
-      "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.3.tgz",
-      "integrity": "sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA==",
+      "version": "7.29.7",
+      "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.7.tgz",
+      "integrity": "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@babel/types": "^7.29.0"
+        "@babel/types": "^7.29.7"
       },
       "bin": {
         "parser": "bin/babel-parser.js"
@@ -270,13 +270,13 @@
       }
     },
     "node_modules/@babel/plugin-syntax-jsx": {
-      "version": "7.28.6",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.28.6.tgz",
-      "integrity": "sha512-wgEmr06G6sIpqr8YDwA2dSRTE3bJ+V0IfpzfSY3Lfgd7YWOaAdlykvJi13ZKBt8cZHfgH1IXN+CL656W3uUa4w==",
+      "version": "7.29.7",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.29.7.tgz",
+      "integrity": "sha512-TSu8+mHCoEaaCDEZ0I3+6mvTBYR4PCxQwf2z9/r5Tbztv6NaLR3B9thGTTxX2WGuGHJqRiAbKPeGTJ5XWXVg6A==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@babel/helper-plugin-utils": "^7.28.6"
+        "@babel/helper-plugin-utils": "^7.29.7"
       },
       "engines": {
         "node": ">=6.9.0"
@@ -286,33 +286,33 @@
       }
     },
     "node_modules/@babel/template": {
-      "version": "7.28.6",
-      "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz",
-      "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==",
+      "version": "7.29.7",
+      "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz",
+      "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@babel/code-frame": "^7.28.6",
-        "@babel/parser": "^7.28.6",
-        "@babel/types": "^7.28.6"
+        "@babel/code-frame": "^7.29.7",
+        "@babel/parser": "^7.29.7",
+        "@babel/types": "^7.29.7"
       },
       "engines": {
         "node": ">=6.9.0"
       }
     },
     "node_modules/@babel/traverse": {
-      "version": "7.29.0",
-      "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz",
-      "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==",
+      "version": "7.29.7",
+      "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.7.tgz",
+      "integrity": "sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@babel/code-frame": "^7.29.0",
-        "@babel/generator": "^7.29.0",
-        "@babel/helper-globals": "^7.28.0",
-        "@babel/parser": "^7.29.0",
-        "@babel/template": "^7.28.6",
-        "@babel/types": "^7.29.0",
+        "@babel/code-frame": "^7.29.7",
+        "@babel/generator": "^7.29.7",
+        "@babel/helper-globals": "^7.29.7",
+        "@babel/parser": "^7.29.7",
+        "@babel/template": "^7.29.7",
+        "@babel/types": "^7.29.7",
         "debug": "^4.3.1"
       },
       "engines": {
@@ -320,14 +320,14 @@
       }
     },
     "node_modules/@babel/types": {
-      "version": "7.29.0",
-      "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz",
-      "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==",
+      "version": "7.29.7",
+      "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.7.tgz",
+      "integrity": "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@babel/helper-string-parser": "^7.27.1",
-        "@babel/helper-validator-identifier": "^7.28.5"
+        "@babel/helper-string-parser": "^7.29.7",
+        "@babel/helper-validator-identifier": "^7.29.7"
       },
       "engines": {
         "node": ">=6.9.0"
@@ -696,12 +696,12 @@
       "license": "MIT"
     },
     "node_modules/@types/node": {
-      "version": "25.6.2",
-      "resolved": "https://registry.npmjs.org/@types/node/-/node-25.6.2.tgz",
-      "integrity": "sha512-sokuT28dxf9JT5Kady1fsXOvI4HVpjZa95NKT5y9PNTIrs2AsobR4GFAA90ZG8M+nxVRLysCXsVj6eGC7Vbrlw==",
+      "version": "25.9.2",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-25.9.2.tgz",
+      "integrity": "sha512-G05zqtJhcDLb8uslf5EjCxXg9G1KQxiV8OS0R26IC//Eoyitzqe8z37I7cqvnZlrlSfgocQRfSn/AHBZJJFyGw==",
       "license": "MIT",
       "dependencies": {
-        "undici-types": "~7.19.0"
+        "undici-types": ">=7.24.0 <7.24.7"
       }
     },
     "node_modules/@types/punycode": {
@@ -711,17 +711,17 @@
       "license": "MIT"
     },
     "node_modules/@typescript-eslint/eslint-plugin": {
-      "version": "8.59.2",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.59.2.tgz",
-      "integrity": "sha512-j/bwmkBvHUtPNxzuWe5z6BEk3q54YRyGlBXkSsmfoih7zNrBvl5A9A98anlp/7JbyZcWIJ8KXo/3Tq/DjFLtuQ==",
+      "version": "8.60.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.60.1.tgz",
+      "integrity": "sha512-JQ4S5GB0tfjO8BuJ4fcX+HodkzJjYBV+7OJ+wLygaX7OGQ7FudyHL4NSCA6ob+w3Yn+5MkKIozOwQhXeM7opVg==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
         "@eslint-community/regexpp": "^4.12.2",
-        "@typescript-eslint/scope-manager": "8.59.2",
-        "@typescript-eslint/type-utils": "8.59.2",
-        "@typescript-eslint/utils": "8.59.2",
-        "@typescript-eslint/visitor-keys": "8.59.2",
+        "@typescript-eslint/scope-manager": "8.60.1",
+        "@typescript-eslint/type-utils": "8.60.1",
+        "@typescript-eslint/utils": "8.60.1",
+        "@typescript-eslint/visitor-keys": "8.60.1",
         "ignore": "^7.0.5",
         "natural-compare": "^1.4.0",
         "ts-api-utils": "^2.5.0"
@@ -734,7 +734,7 @@
         "url": "https://opencollective.com/typescript-eslint"
       },
       "peerDependencies": {
-        "@typescript-eslint/parser": "^8.59.2",
+        "@typescript-eslint/parser": "^8.60.1",
         "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
         "typescript": ">=4.8.4 <6.1.0"
       }
@@ -750,16 +750,16 @@
       }
     },
     "node_modules/@typescript-eslint/parser": {
-      "version": "8.59.2",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.59.2.tgz",
-      "integrity": "sha512-plR3pp6D+SSUn1HM7xvSkx12/DhoHInI2YF35KAcVFNZvlC0gtrWqx7Qq1oH2Ssgi0vlFRCTbP+DZc7B9+TtsQ==",
+      "version": "8.60.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.60.1.tgz",
+      "integrity": "sha512-A0M6ua6H252bVjPvvtSgl2QA4+ET9S5Mtkb2GDyTxIhH/C4qDItT7RQNO5PhMC6NXGYXOR9dIalcDDgBKT7oFA==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@typescript-eslint/scope-manager": "8.59.2",
-        "@typescript-eslint/types": "8.59.2",
-        "@typescript-eslint/typescript-estree": "8.59.2",
-        "@typescript-eslint/visitor-keys": "8.59.2",
+        "@typescript-eslint/scope-manager": "8.60.1",
+        "@typescript-eslint/types": "8.60.1",
+        "@typescript-eslint/typescript-estree": "8.60.1",
+        "@typescript-eslint/visitor-keys": "8.60.1",
         "debug": "^4.4.3"
       },
       "engines": {
@@ -775,14 +775,14 @@
       }
     },
     "node_modules/@typescript-eslint/project-service": {
-      "version": "8.59.2",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.59.2.tgz",
-      "integrity": "sha512-+2hqvEkeyf/0FBor67duF0Ll7Ot8jyKzDQOSrxazF/danillRq2DwR9dLptsXpoZQqxE1UisSmoZewrlPas9Vw==",
+      "version": "8.60.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.60.1.tgz",
+      "integrity": "sha512-eXkTH2bxmXlqD1RnOPmLZ9ZM9D3VwSx04JOwBnP9RQ+yUA5a2Mu7SfW8uaV2Aon53NJzZlZYuX7tn91Izf+xaw==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@typescript-eslint/tsconfig-utils": "^8.59.2",
-        "@typescript-eslint/types": "^8.59.2",
+        "@typescript-eslint/tsconfig-utils": "^8.60.1",
+        "@typescript-eslint/types": "^8.60.1",
         "debug": "^4.4.3"
       },
       "engines": {
@@ -797,14 +797,14 @@
       }
     },
     "node_modules/@typescript-eslint/scope-manager": {
-      "version": "8.59.2",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.59.2.tgz",
-      "integrity": "sha512-JzfyEpEtOU89CcFSwyNS3mu4MLvLSXqnmX05+aKBDM+TdR5jzcGOEBwxwGNxrEQ7p/z6kK2WyioCGBf2zZBnvg==",
+      "version": "8.60.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.60.1.tgz",
+      "integrity": "sha512-gvI5OQoptnxQnchOirukCuQ55svJSTuD/4k5+pC267xyBtYry748R9/c3tYUzb/iE6RZfllRz2lVulLCHkTm4w==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@typescript-eslint/types": "8.59.2",
-        "@typescript-eslint/visitor-keys": "8.59.2"
+        "@typescript-eslint/types": "8.60.1",
+        "@typescript-eslint/visitor-keys": "8.60.1"
       },
       "engines": {
         "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -815,9 +815,9 @@
       }
     },
     "node_modules/@typescript-eslint/tsconfig-utils": {
-      "version": "8.59.2",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.59.2.tgz",
-      "integrity": "sha512-BKK4alN7oi4C/zv4VqHQ+uRU+lTa6JGIZ7s1juw7b3RHo9OfKB+bKX3u0iVZetdsUCBBkSbdWbarJbmN0fTeSw==",
+      "version": "8.60.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.60.1.tgz",
+      "integrity": "sha512-nh8w4qAteiKuZu3pSSzG/yGKpw0OlkrKnzFmbVRenKaD4qc+7i1GrmZaLVkr8rk4uipiPGMOW4YsM6WmKZ5CvA==",
       "dev": true,
       "license": "MIT",
       "engines": {
@@ -832,15 +832,15 @@
       }
     },
     "node_modules/@typescript-eslint/type-utils": {
-      "version": "8.59.2",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.59.2.tgz",
-      "integrity": "sha512-nhqaj1nmTdVVl/BP5omXNRGO38jn5iosis2vbdmupF2txCf8ylWT8lx+JlvMYYVqzGVKtjojUFoQ3JRWK+mfzQ==",
+      "version": "8.60.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.60.1.tgz",
+      "integrity": "sha512-sdwTrpjosW7ANQYJ39ZBF1ZyEMEGVB2UsikrserVM/30a/F1dTLnu9bGxEdosugyu5caigjLrR2qiD11asjI1A==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@typescript-eslint/types": "8.59.2",
-        "@typescript-eslint/typescript-estree": "8.59.2",
-        "@typescript-eslint/utils": "8.59.2",
+        "@typescript-eslint/types": "8.60.1",
+        "@typescript-eslint/typescript-estree": "8.60.1",
+        "@typescript-eslint/utils": "8.60.1",
         "debug": "^4.4.3",
         "ts-api-utils": "^2.5.0"
       },
@@ -857,9 +857,9 @@
       }
     },
     "node_modules/@typescript-eslint/types": {
-      "version": "8.59.2",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.59.2.tgz",
-      "integrity": "sha512-e82GVOE8Ps3E++Egvb6Y3Dw0S10u8NkQ9KXmtRhCWJJ8kDhOJTvtMAWnFL16kB1583goCWXsr0NieKCZMs2/0Q==",
+      "version": "8.60.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.60.1.tgz",
+      "integrity": "sha512-4h0tY8ppCkdCzcrl2YM5M3my0xsE1Tf8om3owEu5oPWmXwkKRmk0j0LGDzYBGUcAlesEbxBhazqu/K4cu3Ug7w==",
       "dev": true,
       "license": "MIT",
       "engines": {
@@ -871,16 +871,16 @@
       }
     },
     "node_modules/@typescript-eslint/typescript-estree": {
-      "version": "8.59.2",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.59.2.tgz",
-      "integrity": "sha512-o0XPGNwcWw+FIwStOWn+BwBuEmL6QXP0rsvAFg7ET1dey1Nr6Wb1ac8p5HEsK0ygO/6mUxlk+YWQD9xcb/nnXg==",
+      "version": "8.60.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.60.1.tgz",
+      "integrity": "sha512-alpRkfG8hlVE5kdJW2GkfgDgXxold3e8e4l6EnmhRmRLbekgAPCCGDVD++sABy9FcgPFroq+uFcCSM1vR57Cew==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@typescript-eslint/project-service": "8.59.2",
-        "@typescript-eslint/tsconfig-utils": "8.59.2",
-        "@typescript-eslint/types": "8.59.2",
-        "@typescript-eslint/visitor-keys": "8.59.2",
+        "@typescript-eslint/project-service": "8.60.1",
+        "@typescript-eslint/tsconfig-utils": "8.60.1",
+        "@typescript-eslint/types": "8.60.1",
+        "@typescript-eslint/visitor-keys": "8.60.1",
         "debug": "^4.4.3",
         "minimatch": "^10.2.2",
         "semver": "^7.7.3",
@@ -938,9 +938,9 @@
       }
     },
     "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": {
-      "version": "7.7.4",
-      "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
-      "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
+      "version": "7.8.2",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.2.tgz",
+      "integrity": "sha512-c8jsqUZm3omBOI66G90z1Dyw5z622G8oLG+omfsHBJf3CWQTlOcwOjvOG6wtiNfW6anKm/eA39LMwMtMez2TiQ==",
       "dev": true,
       "license": "ISC",
       "bin": {
@@ -951,16 +951,16 @@
       }
     },
     "node_modules/@typescript-eslint/utils": {
-      "version": "8.59.2",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.59.2.tgz",
-      "integrity": "sha512-Juw3EinkXqjaffxz6roowvV7GZT/kET5vSKKZT6upl5TXdWkLkYmNPXwDDL2Vkt2DPn0nODIS4egC/0AGxKo/Q==",
+      "version": "8.60.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.60.1.tgz",
+      "integrity": "sha512-h2MPBLoNtjc3qZWfY3Tl51yPorQ2McHn8pJfcMNTcIvrrZrr90Ykffit0yjrPFWQcRcUxzH20+6OcVdW4yHtUg==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
         "@eslint-community/eslint-utils": "^4.9.1",
-        "@typescript-eslint/scope-manager": "8.59.2",
-        "@typescript-eslint/types": "8.59.2",
-        "@typescript-eslint/typescript-estree": "8.59.2"
+        "@typescript-eslint/scope-manager": "8.60.1",
+        "@typescript-eslint/types": "8.60.1",
+        "@typescript-eslint/typescript-estree": "8.60.1"
       },
       "engines": {
         "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -975,13 +975,13 @@
       }
     },
     "node_modules/@typescript-eslint/visitor-keys": {
-      "version": "8.59.2",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.59.2.tgz",
-      "integrity": "sha512-NwjLUnGy8/Zfx23fl50tRC8rYaYnM52xNRYFAXvmiil9yh1+K6aRVQMnzW6gQB/1DLgWt977lYQn7C+wtgXZiA==",
+      "version": "8.60.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.60.1.tgz",
+      "integrity": "sha512-EbGRQg4FhrmwLodl+t3JNAnXHWVr9Vp+Zl1QBZVPY4ByfkzIT8cX3K6QWODHtkIZqqJVEWvhHSx3v5PDHsaQag==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@typescript-eslint/types": "8.59.2",
+        "@typescript-eslint/types": "8.60.1",
         "eslint-visitor-keys": "^5.0.0"
       },
       "engines": {
@@ -1475,9 +1475,9 @@
       "license": "MIT"
     },
     "node_modules/baseline-browser-mapping": {
-      "version": "2.10.28",
-      "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.28.tgz",
-      "integrity": "sha512-Ic44hnOtFIgravCunj1ifSoQPSUrkNiJuH9Mf6jr2jjoA74icqV8wU0KuadXeOR8zuIJMOoTv0GuQjZ9ZYNMeA==",
+      "version": "2.10.34",
+      "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.34.tgz",
+      "integrity": "sha512-IMDedajPifLnHNY0X9n8hKxRTQ6/eTHwr5bDo04WnuqxyKw6LYtQywCuuqPZwhl3aBXMvQpJov42GLCwRRdQzw==",
       "license": "Apache-2.0",
       "bin": {
         "baseline-browser-mapping": "dist/cli.cjs"
@@ -1487,9 +1487,9 @@
       }
     },
     "node_modules/brace-expansion": {
-      "version": "1.1.14",
-      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz",
-      "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==",
+      "version": "1.1.15",
+      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz",
+      "integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
@@ -1609,9 +1609,9 @@
       }
     },
     "node_modules/caniuse-lite": {
-      "version": "1.0.30001792",
-      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001792.tgz",
-      "integrity": "sha512-hVLMUZFgR4JJ6ACt1uEESvQN1/dBVqPAKY0hgrV70eN3391K6juAfTjKZLKvOMsx8PxA7gsY1/tLMMTcfFLLpw==",
+      "version": "1.0.30001797",
+      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001797.tgz",
+      "integrity": "sha512-l8xKG+gwAIExZGl9FrF7KUwuOmk6wbEPC9Xoy/RtnWv1XG0Q4LFlagaLpUv3Kiza3W/wm27zy0yWJEieYKAP6w==",
       "funding": [
         {
           "type": "opencollective",
@@ -1863,15 +1863,15 @@
       }
     },
     "node_modules/electron-to-chromium": {
-      "version": "1.5.352",
-      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.352.tgz",
-      "integrity": "sha512-9wHk8x6dyuimoe18EdiDPWKExNdxYqo4fn4FwOVVper6RxT3cmpBwBkWWfSOCYJjQdIco/nPhJhNLmn4Ufg1Yg==",
+      "version": "1.5.368",
+      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.368.tgz",
+      "integrity": "sha512-7RckJJK4uESJF9PxvfMWd3TGqIiieUTG4HxnKaKuIpGbcr+r2ZEB3g2gAhCP3Fqm42vJSzLfgab9eva/C4/XVw==",
       "license": "ISC"
     },
     "node_modules/enhanced-resolve": {
-      "version": "5.22.0",
-      "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.22.0.tgz",
-      "integrity": "sha512-xYcDWrpELkFzz9SpZ3PlI6Eu6eD93Yf0WLDRxikGhWJ3MAir2SNZTIVCVZqZ/NUyx8AdMc2gT9C0gPiw18kG+A==",
+      "version": "5.23.0",
+      "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.23.0.tgz",
+      "integrity": "sha512-yJN/BOOLxcOW2aQgeif9mSnaUB8KtvmMMp56oA1kx1CRfBKbhZm2pJ+NBY+3eOboHxix8lfjWpHE0Ei5U8RbSA==",
       "license": "MIT",
       "dependencies": {
         "graceful-fs": "^4.2.4",
@@ -2016,9 +2016,9 @@
       "license": "MIT"
     },
     "node_modules/es-object-atoms": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
-      "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz",
+      "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
@@ -2227,9 +2227,9 @@
       }
     },
     "node_modules/eslint-plugin-compat/node_modules/semver": {
-      "version": "7.7.4",
-      "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
-      "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
+      "version": "7.8.2",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.2.tgz",
+      "integrity": "sha512-c8jsqUZm3omBOI66G90z1Dyw5z622G8oLG+omfsHBJf3CWQTlOcwOjvOG6wtiNfW6anKm/eA39LMwMtMez2TiQ==",
       "dev": true,
       "license": "ISC",
       "bin": {
@@ -2422,15 +2422,6 @@
       ],
       "license": "BSD-3-Clause"
     },
-    "node_modules/fastest-levenshtein": {
-      "version": "1.0.16",
-      "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz",
-      "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==",
-      "license": "MIT",
-      "engines": {
-        "node": ">= 4.9.1"
-      }
-    },
     "node_modules/fdir": {
       "version": "6.5.0",
       "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
@@ -2803,9 +2794,9 @@
       }
     },
     "node_modules/hasown": {
-      "version": "2.0.3",
-      "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.3.tgz",
-      "integrity": "sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==",
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz",
+      "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==",
       "license": "MIT",
       "dependencies": {
         "function-bind": "^1.1.2"
@@ -3373,10 +3364,20 @@
       "license": "MIT"
     },
     "node_modules/js-yaml": {
-      "version": "4.1.1",
-      "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
-      "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
+      "version": "4.2.0",
+      "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.2.0.tgz",
+      "integrity": "sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==",
       "dev": true,
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/puzrin"
+        },
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/nodeca"
+        }
+      ],
       "license": "MIT",
       "dependencies": {
         "argparse": "^2.0.1"
@@ -3650,10 +3651,13 @@
       }
     },
     "node_modules/node-releases": {
-      "version": "2.0.38",
-      "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.38.tgz",
-      "integrity": "sha512-3qT/88Y3FbH/Kx4szpQQ4HzUbVrHPKTLVpVocKiLfoYvw9XSGOX2FmD2d6DrXbVYyAQTF2HeF6My8jmzx7/CRw==",
-      "license": "MIT"
+      "version": "2.0.47",
+      "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.47.tgz",
+      "integrity": "sha512-Uzmd6LXpouKo8EUK68IjH4+E01w/hXyV3R3g/geCJo+rXLNfh1xucB+LOzYEOQPSiUK3h/xZf0cQGcSsmyL2Og==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=18"
+      }
     },
     "node_modules/object-assign": {
       "version": "4.1.1",
@@ -3971,9 +3975,9 @@
       }
     },
     "node_modules/preact": {
-      "version": "10.29.1",
-      "resolved": "https://registry.npmjs.org/preact/-/preact-10.29.1.tgz",
-      "integrity": "sha512-gQCLc/vWroE8lIpleXtdJhTFDogTdZG9AjMUpVkDf2iTCNwYNWA+u16dL41TqUDJO4gm2IgrcMv3uTpjd4Pwmg==",
+      "version": "10.29.2",
+      "resolved": "https://registry.npmjs.org/preact/-/preact-10.29.2.tgz",
+      "integrity": "sha512-7tNmwg/7mzzAoB/8kSg6Hl37JraAZw3Z3A0JSY7VXlZwo82Xn0G7wKbNNs2qoF4ZEEsQGTwDAroNdqKs1ofJxQ==",
       "license": "MIT",
       "funding": {
         "type": "opencollective",
@@ -4105,14 +4109,14 @@
       }
     },
     "node_modules/resolve": {
-      "version": "2.0.0-next.6",
-      "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.6.tgz",
-      "integrity": "sha512-3JmVl5hMGtJ3kMmB3zi3DL25KfkCEyy3Tw7Gmw7z5w8M9WlwoPFnIvwChzu1+cF3iaK3sp18hhPz8ANeimdJfA==",
+      "version": "2.0.0-next.7",
+      "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.7.tgz",
+      "integrity": "sha512-tqt+NBWwyaMgw3zDsnygx4CByWjQEJHOPMdslYhppaQSJUtL/D4JO9CcBBlhPoI8lz9oJIDXkwXfhF4aWqP8xQ==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
         "es-errors": "^1.3.0",
-        "is-core-module": "^2.16.1",
+        "is-core-module": "^2.16.2",
         "node-exports-info": "^1.6.0",
         "object-keys": "^1.1.1",
         "path-parse": "^1.0.7",
@@ -4517,19 +4521,20 @@
       }
     },
     "node_modules/string.prototype.trim": {
-      "version": "1.2.10",
-      "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz",
-      "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==",
+      "version": "1.2.11",
+      "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.11.tgz",
+      "integrity": "sha512-PwvK7BU+CMTJGYQCTZb5RWXIML92lftJLhQz1tBzgKiqGxJaMlBAa48POXaNAC2s4y8jr3EFqrkF9+44neS46w==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "call-bind": "^1.0.8",
-        "call-bound": "^1.0.2",
+        "call-bind": "^1.0.9",
+        "call-bound": "^1.0.4",
         "define-data-property": "^1.1.4",
         "define-properties": "^1.2.1",
-        "es-abstract": "^1.23.5",
-        "es-object-atoms": "^1.0.0",
-        "has-property-descriptors": "^1.0.2"
+        "es-abstract": "^1.24.2",
+        "es-object-atoms": "^1.1.2",
+        "has-property-descriptors": "^1.0.2",
+        "safe-regex-test": "^1.1.0"
       },
       "engines": {
         "node": ">= 0.4"
@@ -4539,16 +4544,16 @@
       }
     },
     "node_modules/string.prototype.trimend": {
-      "version": "1.0.9",
-      "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz",
-      "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==",
+      "version": "1.0.10",
+      "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.10.tgz",
+      "integrity": "sha512-2+3aDAOmPTmuFwjDnmJG2ctEkQKVki7vOSqaxkv42Mowj1V6PnvuwFCRrR5lChUux1TBskPjfkeTOhqczDMxTw==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "call-bind": "^1.0.8",
-        "call-bound": "^1.0.2",
+        "call-bind": "^1.0.9",
+        "call-bound": "^1.0.4",
         "define-properties": "^1.2.1",
-        "es-object-atoms": "^1.0.0"
+        "es-object-atoms": "^1.1.2"
       },
       "engines": {
         "node": ">= 0.4"
@@ -4626,9 +4631,9 @@
       }
     },
     "node_modules/terser": {
-      "version": "5.47.1",
-      "resolved": "https://registry.npmjs.org/terser/-/terser-5.47.1.tgz",
-      "integrity": "sha512-tPbLXTI6ohPASb/1YViL428oEHu6/qv1OxqYnfaonVCFHqx4+wCd95pHrQWsL5X4pl90CTyW9piSAsS2L0VoMw==",
+      "version": "5.48.0",
+      "resolved": "https://registry.npmjs.org/terser/-/terser-5.48.0.tgz",
+      "integrity": "sha512-J/9An6vs9Us6wKRriSFXBWdRZapREHqFzdNUKk0pmu804EMR6dr6winwo7e5JDxN4xahxQsuysyYFwlwj4XN/Q==",
       "license": "BSD-2-Clause",
       "dependencies": {
         "@jridgewell/source-map": "^0.3.3",
@@ -4644,9 +4649,9 @@
       }
     },
     "node_modules/terser-webpack-plugin": {
-      "version": "5.6.0",
-      "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.6.0.tgz",
-      "integrity": "sha512-Eum+5ajkaOhf5KbM26osvv21kLD7BaGqQ1UA4Ami4arYwylmGUQTgHFpHDdmJod1q4QXa66p0to/FBKID+J1vA==",
+      "version": "5.6.1",
+      "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.6.1.tgz",
+      "integrity": "sha512-201R5j+sJpK8nFWwKVyNfZot8FaJbLZDq5evriVzbV1wDtSXDjRUDRfJzHpAaxFDMEhsZL1QkeqM61wgsS3KaQ==",
       "license": "MIT",
       "dependencies": {
         "@jridgewell/trace-mapping": "^0.3.25",
@@ -4704,9 +4709,9 @@
       }
     },
     "node_modules/tinyglobby": {
-      "version": "0.2.16",
-      "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz",
-      "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==",
+      "version": "0.2.17",
+      "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz",
+      "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
@@ -4746,9 +4751,9 @@
       }
     },
     "node_modules/ts-loader": {
-      "version": "9.5.7",
-      "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.5.7.tgz",
-      "integrity": "sha512-/ZNrKgA3K3PtpMYOC71EeMWIloGw3IYEa5/t1cyz2r5/PyUwTXGzYJvcD3kfUvmhlfpz1rhV8B2O6IVTQ0avsg==",
+      "version": "9.6.0",
+      "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.6.0.tgz",
+      "integrity": "sha512-dsJO0S+T7grTDWTc4a0nTygXGjKncVUpx8Y+af8EvI/D5WgTJby5UEk5eoMCB9EcLQmnvitqh99MqtjtHgAwFQ==",
       "license": "MIT",
       "dependencies": {
         "chalk": "^4.1.0",
@@ -4761,14 +4766,20 @@
         "node": ">=12.0.0"
       },
       "peerDependencies": {
+        "loader-utils": "*",
         "typescript": "*",
-        "webpack": "^5.0.0"
+        "webpack": "^4.0.0 || ^5.0.0"
+      },
+      "peerDependenciesMeta": {
+        "loader-utils": {
+          "optional": true
+        }
       }
     },
     "node_modules/ts-loader/node_modules/semver": {
-      "version": "7.7.4",
-      "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
-      "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
+      "version": "7.8.2",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.2.tgz",
+      "integrity": "sha512-c8jsqUZm3omBOI66G90z1Dyw5z622G8oLG+omfsHBJf3CWQTlOcwOjvOG6wtiNfW6anKm/eA39LMwMtMez2TiQ==",
       "license": "ISC",
       "bin": {
         "semver": "bin/semver.js"
@@ -4848,18 +4859,18 @@
       }
     },
     "node_modules/typed-array-length": {
-      "version": "1.0.7",
-      "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz",
-      "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==",
+      "version": "1.0.8",
+      "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.8.tgz",
+      "integrity": "sha512-phPGCwqr2+Qo0fwniCE8e4pKnGu/yFb5nD5Y8bf0EEeiI5GklnACYA9GFy/DrAeRrKHXvHn+1SUsOWgJp6RO+g==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "call-bind": "^1.0.7",
-        "for-each": "^0.3.3",
-        "gopd": "^1.0.1",
-        "is-typed-array": "^1.1.13",
-        "possible-typed-array-names": "^1.0.0",
-        "reflect.getprototypeof": "^1.0.6"
+        "call-bind": "^1.0.9",
+        "for-each": "^0.3.5",
+        "gopd": "^1.2.0",
+        "is-typed-array": "^1.1.15",
+        "possible-typed-array-names": "^1.1.0",
+        "reflect.getprototypeof": "^1.0.10"
       },
       "engines": {
         "node": ">= 0.4"
@@ -4883,16 +4894,16 @@
       }
     },
     "node_modules/typescript-eslint": {
-      "version": "8.59.2",
-      "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.59.2.tgz",
-      "integrity": "sha512-pJw051uomb3ZeCzGTpRb8RbEqB5Y4WWet8gl/GcTlU35BSx0PVdZ86/bqkQCyKKuraVQEK7r6kBHQXF+fBhkoQ==",
+      "version": "8.60.1",
+      "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.60.1.tgz",
+      "integrity": "sha512-6m5hkkRAp8lKvhVpcprAIn5KkehQEh+47oHH2VGnExEh7dhNxXlg6GPAOIu6TxbVQxhebrJDvjl3020ooiWCMA==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@typescript-eslint/eslint-plugin": "8.59.2",
-        "@typescript-eslint/parser": "8.59.2",
-        "@typescript-eslint/typescript-estree": "8.59.2",
-        "@typescript-eslint/utils": "8.59.2"
+        "@typescript-eslint/eslint-plugin": "8.60.1",
+        "@typescript-eslint/parser": "8.60.1",
+        "@typescript-eslint/typescript-estree": "8.60.1",
+        "@typescript-eslint/utils": "8.60.1"
       },
       "engines": {
         "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -4926,9 +4937,9 @@
       }
     },
     "node_modules/undici-types": {
-      "version": "7.19.2",
-      "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.19.2.tgz",
-      "integrity": "sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg==",
+      "version": "7.24.6",
+      "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.24.6.tgz",
+      "integrity": "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==",
       "license": "MIT"
     },
     "node_modules/update-browserslist-db": {
@@ -4985,9 +4996,9 @@
       }
     },
     "node_modules/webpack": {
-      "version": "5.107.1",
-      "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.107.1.tgz",
-      "integrity": "sha512-mvdIWxj/H6QsfgDdH9djne3a5dYcmEmtsXGESkypaGN5jXjF/b+9KDlmTDQ2TKlFUeA2fI9Y65kihD30JOdB+Q==",
+      "version": "5.107.2",
+      "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.107.2.tgz",
+      "integrity": "sha512-v7RhXaJbpMlV0D7hC7lb2EbnxkoeUqf9qhKr6lozx3Q48pmFrqqNRmZFUEGmi7pSwm6fCQ2H1IjvCkHqdpVdjQ==",
       "license": "MIT",
       "dependencies": {
         "@types/estree": "^1.0.8",
@@ -4999,7 +5010,7 @@
         "acorn-import-phases": "^1.0.3",
         "browserslist": "^4.28.1",
         "chrome-trace-event": "^1.0.2",
-        "enhanced-resolve": "^5.21.4",
+        "enhanced-resolve": "^5.22.0",
         "es-module-lexer": "^2.1.0",
         "eslint-scope": "5.1.1",
         "events": "^3.2.0",
@@ -5012,7 +5023,7 @@
         "tapable": "^2.3.0",
         "terser-webpack-plugin": "^5.5.0",
         "watchpack": "^2.5.1",
-        "webpack-sources": "^3.4.1"
+        "webpack-sources": "^3.5.0"
       },
       "bin": {
         "webpack": "bin/webpack.js"
@@ -5031,16 +5042,15 @@
       }
     },
     "node_modules/webpack-cli": {
-      "version": "7.0.2",
-      "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-7.0.2.tgz",
-      "integrity": "sha512-dB0R4T+C/8YuvM+fabdvil6QE44/ChDXikV5lOOkrUeCkW5hTJv2pGLE3keh+D5hjYw8icBaJkZzpFoaHV4T+g==",
+      "version": "7.0.3",
+      "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-7.0.3.tgz",
+      "integrity": "sha512-2E2C6A1e2El7791zQgTH7LPIuwLjRliow9OHS/qlJc9pwhZlCoL/uiwqd/1WSlXT83wJfmfDbkcqHXuXoPJZ3g==",
       "license": "MIT",
       "dependencies": {
-        "@discoveryjs/json-ext": "^1.0.0",
+        "@discoveryjs/json-ext": "^1.1.0",
         "commander": "^14.0.3",
         "cross-spawn": "^7.0.6",
         "envinfo": "^7.14.0",
-        "fastest-levenshtein": "^1.0.12",
         "import-local": "^3.0.2",
         "interpret": "^3.1.1",
         "rechoir": "^0.8.0",
@@ -5094,9 +5104,9 @@
       }
     },
     "node_modules/webpack-sources": {
-      "version": "3.4.1",
-      "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.4.1.tgz",
-      "integrity": "sha512-eACpxRN02yaawnt+uUNIF7Qje6A9zArxBbcAJjK1PK3S9Ycg5jIuJ8pW4q8EMnwNZCEGltcjkRx1QzOxOkKD8A==",
+      "version": "3.5.0",
+      "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.5.0.tgz",
+      "integrity": "sha512-HPuy+uuoTCaaoEoI1LQ3JN9+vrPBvEesnnX1jADHy728cHSMlq4wUc4afYqahq2B1mhQVZxCXOkNTnXltr+2vQ==",
       "license": "MIT",
       "engines": {
         "node": ">=10.13.0"
@@ -5207,14 +5217,14 @@
       }
     },
     "node_modules/which-typed-array": {
-      "version": "1.1.20",
-      "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.20.tgz",
-      "integrity": "sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==",
+      "version": "1.1.22",
+      "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.22.tgz",
+      "integrity": "sha512-fvO4ExWMFsqyhG3AiPAObMuY1lxaqgYcxbc49CNdWDDECOJNgQyvsOWVwbZc+qf3rzRtxojBK+CMEv0Ld5CYpw==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
         "available-typed-arrays": "^1.0.7",
-        "call-bind": "^1.0.8",
+        "call-bind": "^1.0.9",
         "call-bound": "^1.0.4",
         "for-each": "^0.3.5",
         "get-proto": "^1.0.1",
diff --git a/package-lock.json b/package-lock.json
index ac46eac8..17dc5924 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -347,9 +347,9 @@
       }
     },
     "node_modules/baseline-browser-mapping": {
-      "version": "2.10.28",
-      "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.28.tgz",
-      "integrity": "sha512-Ic44hnOtFIgravCunj1ifSoQPSUrkNiJuH9Mf6jr2jjoA74icqV8wU0KuadXeOR8zuIJMOoTv0GuQjZ9ZYNMeA==",
+      "version": "2.10.34",
+      "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.34.tgz",
+      "integrity": "sha512-IMDedajPifLnHNY0X9n8hKxRTQ6/eTHwr5bDo04WnuqxyKw6LYtQywCuuqPZwhl3aBXMvQpJov42GLCwRRdQzw==",
       "license": "Apache-2.0",
       "bin": {
         "baseline-browser-mapping": "dist/cli.cjs"
@@ -392,9 +392,9 @@
       }
     },
     "node_modules/caniuse-lite": {
-      "version": "1.0.30001792",
-      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001792.tgz",
-      "integrity": "sha512-hVLMUZFgR4JJ6ACt1uEESvQN1/dBVqPAKY0hgrV70eN3391K6juAfTjKZLKvOMsx8PxA7gsY1/tLMMTcfFLLpw==",
+      "version": "1.0.30001797",
+      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001797.tgz",
+      "integrity": "sha512-l8xKG+gwAIExZGl9FrF7KUwuOmk6wbEPC9Xoy/RtnWv1XG0Q4LFlagaLpUv3Kiza3W/wm27zy0yWJEieYKAP6w==",
       "funding": [
         {
           "type": "opencollective",
@@ -437,9 +437,9 @@
       }
     },
     "node_modules/electron-to-chromium": {
-      "version": "1.5.352",
-      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.352.tgz",
-      "integrity": "sha512-9wHk8x6dyuimoe18EdiDPWKExNdxYqo4fn4FwOVVper6RxT3cmpBwBkWWfSOCYJjQdIco/nPhJhNLmn4Ufg1Yg==",
+      "version": "1.5.368",
+      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.368.tgz",
+      "integrity": "sha512-7RckJJK4uESJF9PxvfMWd3TGqIiieUTG4HxnKaKuIpGbcr+r2ZEB3g2gAhCP3Fqm42vJSzLfgab9eva/C4/XVw==",
       "license": "ISC"
     },
     "node_modules/escalade": {
@@ -465,9 +465,9 @@
       }
     },
     "node_modules/immutable": {
-      "version": "5.1.5",
-      "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.5.tgz",
-      "integrity": "sha512-t7xcm2siw+hlUM68I+UEOK+z84RzmN59as9DZ7P1l0994DKUWV7UXBMQZVxaoMSRQ+PBZbHCOoBt7a2wxOMt+A==",
+      "version": "5.1.6",
+      "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.6.tgz",
+      "integrity": "sha512-q1swsS8K7L8usSHuOqF2TAoCCkonYz0SG38wLAggaa4Wml70zixIvt2ql4coQ2C2B3hTjltJry4r6bULwgAXLQ==",
       "license": "MIT"
     },
     "node_modules/is-extglob": {
@@ -519,10 +519,13 @@
       "optional": true
     },
     "node_modules/node-releases": {
-      "version": "2.0.38",
-      "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.38.tgz",
-      "integrity": "sha512-3qT/88Y3FbH/Kx4szpQQ4HzUbVrHPKTLVpVocKiLfoYvw9XSGOX2FmD2d6DrXbVYyAQTF2HeF6My8jmzx7/CRw==",
-      "license": "MIT"
+      "version": "2.0.47",
+      "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.47.tgz",
+      "integrity": "sha512-Uzmd6LXpouKo8EUK68IjH4+E01w/hXyV3R3g/geCJo+rXLNfh1xucB+LOzYEOQPSiUK3h/xZf0cQGcSsmyL2Og==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=18"
+      }
     },
     "node_modules/picocolors": {
       "version": "1.1.1",
@@ -544,9 +547,9 @@
       }
     },
     "node_modules/postcss": {
-      "version": "8.5.14",
-      "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.14.tgz",
-      "integrity": "sha512-SoSL4+OSEtR99LHFZQiJLkT59C5B1amGO1NzTwj7TT1qCUgUO6hxOvzkOYxD+vMrXBM3XJIKzokoERdqQq/Zmg==",
+      "version": "8.5.15",
+      "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz",
+      "integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==",
       "funding": [
         {
           "type": "opencollective",
@@ -563,7 +566,7 @@
       ],
       "license": "MIT",
       "dependencies": {
-        "nanoid": "^3.3.11",
+        "nanoid": "^3.3.12",
         "picocolors": "^1.1.1",
         "source-map-js": "^1.2.1"
       },



From noreply at scummvm.org  Sat Jun  6 16:16:08 2026
From: noreply at scummvm.org (ScummVM git)
Date: Sat,  6 Jun 2026 16:16:08 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm-web
 dependabot/npm_and_yarn/dumper-companion/eslint-e715b53a58 ->
 dad6d24c892a0f8e31e55434a1034e86fc0911ac
Message-ID: <20260606161608.8640D700573@ron.scummvm.net>

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

Summary:
dad6d24c89 Bump the eslint group across 1 directory with 2 updates


Commit: dad6d24c892a0f8e31e55434a1034e86fc0911ac
    https://github.com/scummvm/scummvm-web/commit/dad6d24c892a0f8e31e55434a1034e86fc0911ac
Author: dependabot[bot] (49699333+dependabot[bot]@users.noreply.github.com)
Date: 2026-06-06T16:16:05Z

Commit Message:
Bump the eslint group across 1 directory with 2 updates

Bumps the eslint group with 2 updates in the /dumper-companion directory: [@eslint/js](https://github.com/eslint/eslint/tree/HEAD/packages/js) and [eslint](https://github.com/eslint/eslint).


Updates `@eslint/js` from 9.39.4 to 10.0.1
- [Release notes](https://github.com/eslint/eslint/releases)
- [Commits](https://github.com/eslint/eslint/commits/v10.0.1/packages/js)

Updates `eslint` from 9.39.4 to 10.4.1
- [Release notes](https://github.com/eslint/eslint/releases)
- [Commits](https://github.com/eslint/eslint/compare/v9.39.4...v10.4.1)

---
updated-dependencies:
- dependency-name: "@eslint/js"
  dependency-version: 10.0.1
  dependency-type: direct:development
  update-type: version-update:semver-major
  dependency-group: eslint
- dependency-name: eslint
  dependency-version: 10.4.1
  dependency-type: direct:development
  update-type: version-update:semver-major
  dependency-group: eslint
...

Signed-off-by: dependabot[bot] <support at github.com>

Changed paths:
    dumper-companion/package-lock.json
    dumper-companion/package.json


diff --git a/dumper-companion/package-lock.json b/dumper-companion/package-lock.json
index e7324deb..a160a25e 100644
--- a/dumper-companion/package-lock.json
+++ b/dumper-companion/package-lock.json
@@ -17,9 +17,9 @@
         "webpack-cli": "^7.0.2"
       },
       "devDependencies": {
-        "@eslint/js": "^9.39.4",
+        "@eslint/js": "^10.0.1",
         "@stylistic/eslint-plugin": "^5.10.0",
-        "eslint": "^9.39.4",
+        "eslint": "^10.4.1",
         "eslint-config-preact": "^2.0.0",
         "globals": "^17.5.0",
         "typescript-eslint": "^8.58.2"
@@ -81,35 +81,6 @@
         "url": "https://opencollective.com/babel"
       }
     },
-    "node_modules/@babel/eslint-parser": {
-      "version": "7.29.7",
-      "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.29.7.tgz",
-      "integrity": "sha512-zxt+UJTOMKvUt3yOg+D58MLuz334pHp93qifMFcjIIO+9hN6t+ufw2gi7vDPMpxvfnHRR+3VVXvIjineCcgyXw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@nicolo-ribaudo/eslint-scope-5-internals": "5.1.1-v1",
-        "eslint-visitor-keys": "^2.1.0",
-        "semver": "^6.3.1"
-      },
-      "engines": {
-        "node": "^10.13.0 || ^12.13.0 || >=14.0.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.11.0",
-        "eslint": "^7.5.0 || ^8.0.0 || ^9.0.0"
-      }
-    },
-    "node_modules/@babel/eslint-parser/node_modules/eslint-visitor-keys": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz",
-      "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==",
-      "dev": true,
-      "license": "Apache-2.0",
-      "engines": {
-        "node": ">=10"
-      }
-    },
     "node_modules/@babel/generator": {
       "version": "7.29.7",
       "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.7.tgz",
@@ -385,118 +356,89 @@
       }
     },
     "node_modules/@eslint/config-array": {
-      "version": "0.21.2",
-      "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.2.tgz",
-      "integrity": "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==",
+      "version": "0.23.5",
+      "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.23.5.tgz",
+      "integrity": "sha512-Y3kKLvC1dvTOT+oGlqNQ1XLqK6D1HU2YXPc52NmAlJZbMMWDzGYXMiPRJ8TYD39muD/OTjlZmNJ4ib7dvSrMBA==",
       "dev": true,
       "license": "Apache-2.0",
       "dependencies": {
-        "@eslint/object-schema": "^2.1.7",
+        "@eslint/object-schema": "^3.0.5",
         "debug": "^4.3.1",
-        "minimatch": "^3.1.5"
+        "minimatch": "^10.2.4"
       },
       "engines": {
-        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+        "node": "^20.19.0 || ^22.13.0 || >=24"
       }
     },
     "node_modules/@eslint/config-helpers": {
-      "version": "0.4.2",
-      "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz",
-      "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==",
+      "version": "0.6.0",
+      "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.6.0.tgz",
+      "integrity": "sha512-ii6Bw9jJ2zi2cWA2Z+9/QZ/+3DX6kwaV5Q986D/CdP3Lap3w/pgQZ373FV7byY/i7L4IRH/G43I5dz1ClsCbpA==",
       "dev": true,
       "license": "Apache-2.0",
       "dependencies": {
-        "@eslint/core": "^0.17.0"
+        "@eslint/core": "^1.2.1"
       },
       "engines": {
-        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+        "node": "^20.19.0 || ^22.13.0 || >=24"
       }
     },
     "node_modules/@eslint/core": {
-      "version": "0.17.0",
-      "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz",
-      "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==",
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/@eslint/core/-/core-1.2.1.tgz",
+      "integrity": "sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ==",
       "dev": true,
       "license": "Apache-2.0",
       "dependencies": {
         "@types/json-schema": "^7.0.15"
       },
       "engines": {
-        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
-      }
-    },
-    "node_modules/@eslint/eslintrc": {
-      "version": "3.3.5",
-      "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.5.tgz",
-      "integrity": "sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "ajv": "^6.14.0",
-        "debug": "^4.3.2",
-        "espree": "^10.0.1",
-        "globals": "^14.0.0",
-        "ignore": "^5.2.0",
-        "import-fresh": "^3.2.1",
-        "js-yaml": "^4.1.1",
-        "minimatch": "^3.1.5",
-        "strip-json-comments": "^3.1.1"
-      },
-      "engines": {
-        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
-      },
-      "funding": {
-        "url": "https://opencollective.com/eslint"
-      }
-    },
-    "node_modules/@eslint/eslintrc/node_modules/globals": {
-      "version": "14.0.0",
-      "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz",
-      "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=18"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
+        "node": "^20.19.0 || ^22.13.0 || >=24"
       }
     },
     "node_modules/@eslint/js": {
-      "version": "9.39.4",
-      "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.4.tgz",
-      "integrity": "sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==",
+      "version": "10.0.1",
+      "resolved": "https://registry.npmjs.org/@eslint/js/-/js-10.0.1.tgz",
+      "integrity": "sha512-zeR9k5pd4gxjZ0abRoIaxdc7I3nDktoXZk2qOv9gCNWx3mVwEn32VRhyLaRsDiJjTs0xq/T8mfPtyuXu7GWBcA==",
       "dev": true,
       "license": "MIT",
       "engines": {
-        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+        "node": "^20.19.0 || ^22.13.0 || >=24"
       },
       "funding": {
         "url": "https://eslint.org/donate"
+      },
+      "peerDependencies": {
+        "eslint": "^10.0.0"
+      },
+      "peerDependenciesMeta": {
+        "eslint": {
+          "optional": true
+        }
       }
     },
     "node_modules/@eslint/object-schema": {
-      "version": "2.1.7",
-      "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz",
-      "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==",
+      "version": "3.0.5",
+      "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-3.0.5.tgz",
+      "integrity": "sha512-vqTaUEgxzm+YDSdElad6PiRoX4t8VGDjCtt05zn4nU810UIx/uNEV7/lZJ6KwFThKZOzOxzXy48da+No7HZaMw==",
       "dev": true,
       "license": "Apache-2.0",
       "engines": {
-        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+        "node": "^20.19.0 || ^22.13.0 || >=24"
       }
     },
     "node_modules/@eslint/plugin-kit": {
-      "version": "0.4.1",
-      "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz",
-      "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==",
+      "version": "0.7.2",
+      "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.7.2.tgz",
+      "integrity": "sha512-+CNAzxglkrpNf/kKywqQfk74QjtceuOE7Qm+AF8miRvPF/wmmK5+OJOgVh3AVTT3RP2mH3+FOaxlE5v72owk0A==",
       "dev": true,
       "license": "Apache-2.0",
       "dependencies": {
-        "@eslint/core": "^0.17.0",
+        "@eslint/core": "^1.2.1",
         "levn": "^0.4.1"
       },
       "engines": {
-        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+        "node": "^20.19.0 || ^22.13.0 || >=24"
       }
     },
     "node_modules/@humanfs/core": {
@@ -683,6 +625,13 @@
         "eslint": "^9.0.0 || ^10.0.0"
       }
     },
+    "node_modules/@types/esrecurse": {
+      "version": "4.3.1",
+      "resolved": "https://registry.npmjs.org/@types/esrecurse/-/esrecurse-4.3.1.tgz",
+      "integrity": "sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==",
+      "dev": true,
+      "license": "MIT"
+    },
     "node_modules/@types/estree": {
       "version": "1.0.9",
       "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz",
@@ -898,45 +847,6 @@
         "typescript": ">=4.8.4 <6.1.0"
       }
     },
-    "node_modules/@typescript-eslint/typescript-estree/node_modules/balanced-match": {
-      "version": "4.0.4",
-      "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
-      "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": "18 || 20 || >=22"
-      }
-    },
-    "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": {
-      "version": "5.0.6",
-      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz",
-      "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "balanced-match": "^4.0.2"
-      },
-      "engines": {
-        "node": "18 || 20 || >=22"
-      }
-    },
-    "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": {
-      "version": "10.2.5",
-      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz",
-      "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==",
-      "dev": true,
-      "license": "BlueOak-1.0.0",
-      "dependencies": {
-        "brace-expansion": "^5.0.5"
-      },
-      "engines": {
-        "node": "18 || 20 || >=22"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/isaacs"
-      }
-    },
     "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": {
       "version": "7.8.2",
       "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.2.tgz",
@@ -1279,13 +1189,6 @@
         "url": "https://github.com/chalk/ansi-styles?sponsor=1"
       }
     },
-    "node_modules/argparse": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
-      "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
-      "dev": true,
-      "license": "Python-2.0"
-    },
     "node_modules/array-buffer-byte-length": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz",
@@ -1468,11 +1371,14 @@
       }
     },
     "node_modules/balanced-match": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
-      "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+      "version": "4.0.4",
+      "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
+      "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==",
       "dev": true,
-      "license": "MIT"
+      "license": "MIT",
+      "engines": {
+        "node": "18 || 20 || >=22"
+      }
     },
     "node_modules/baseline-browser-mapping": {
       "version": "2.10.34",
@@ -1487,14 +1393,16 @@
       }
     },
     "node_modules/brace-expansion": {
-      "version": "1.1.15",
-      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz",
-      "integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==",
+      "version": "5.0.6",
+      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz",
+      "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "balanced-match": "^1.0.0",
-        "concat-map": "0.0.1"
+        "balanced-match": "^4.0.2"
+      },
+      "engines": {
+        "node": "18 || 20 || >=22"
       }
     },
     "node_modules/braces": {
@@ -1598,16 +1506,6 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
-    "node_modules/callsites": {
-      "version": "3.1.0",
-      "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
-      "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=6"
-      }
-    },
     "node_modules/caniuse-lite": {
       "version": "1.0.30001797",
       "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001797.tgz",
@@ -2098,33 +1996,30 @@
       }
     },
     "node_modules/eslint": {
-      "version": "9.39.4",
-      "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.4.tgz",
-      "integrity": "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==",
+      "version": "10.4.1",
+      "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.4.1.tgz",
+      "integrity": "sha512-AyIKhnOBuOAdueD7RB3xB+YeAWScb9jHsJBgH2Hcde8InP5JYhqrRR6iTMHyTEwgENK54Cp44e4v8BwNhsuHuw==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
         "@eslint-community/eslint-utils": "^4.8.0",
-        "@eslint-community/regexpp": "^4.12.1",
-        "@eslint/config-array": "^0.21.2",
-        "@eslint/config-helpers": "^0.4.2",
-        "@eslint/core": "^0.17.0",
-        "@eslint/eslintrc": "^3.3.5",
-        "@eslint/js": "9.39.4",
-        "@eslint/plugin-kit": "^0.4.1",
+        "@eslint-community/regexpp": "^4.12.2",
+        "@eslint/config-array": "^0.23.5",
+        "@eslint/config-helpers": "^0.6.0",
+        "@eslint/core": "^1.2.1",
+        "@eslint/plugin-kit": "^0.7.2",
         "@humanfs/node": "^0.16.6",
         "@humanwhocodes/module-importer": "^1.0.1",
         "@humanwhocodes/retry": "^0.4.2",
         "@types/estree": "^1.0.6",
         "ajv": "^6.14.0",
-        "chalk": "^4.0.0",
         "cross-spawn": "^7.0.6",
         "debug": "^4.3.2",
         "escape-string-regexp": "^4.0.0",
-        "eslint-scope": "^8.4.0",
-        "eslint-visitor-keys": "^4.2.1",
-        "espree": "^10.4.0",
-        "esquery": "^1.5.0",
+        "eslint-scope": "^9.1.2",
+        "eslint-visitor-keys": "^5.0.1",
+        "espree": "^11.2.0",
+        "esquery": "^1.7.0",
         "esutils": "^2.0.2",
         "fast-deep-equal": "^3.1.3",
         "file-entry-cache": "^8.0.0",
@@ -2134,8 +2029,7 @@
         "imurmurhash": "^0.1.4",
         "is-glob": "^4.0.0",
         "json-stable-stringify-without-jsonify": "^1.0.1",
-        "lodash.merge": "^4.6.2",
-        "minimatch": "^3.1.5",
+        "minimatch": "^10.2.4",
         "natural-compare": "^1.4.0",
         "optionator": "^0.9.3"
       },
@@ -2143,7 +2037,7 @@
         "eslint": "bin/eslint.js"
       },
       "engines": {
-        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+        "node": "^20.19.0 || ^22.13.0 || >=24"
       },
       "funding": {
         "url": "https://eslint.org/donate"
@@ -2178,68 +2072,57 @@
         "eslint": "^8.57.1 || ^9.0.0"
       }
     },
-    "node_modules/eslint-config-preact/node_modules/globals": {
-      "version": "16.5.0",
-      "resolved": "https://registry.npmjs.org/globals/-/globals-16.5.0.tgz",
-      "integrity": "sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=18"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/eslint-plugin-compat": {
-      "version": "6.2.1",
-      "resolved": "https://registry.npmjs.org/eslint-plugin-compat/-/eslint-plugin-compat-6.2.1.tgz",
-      "integrity": "sha512-gLKqUH+lQcCL+HzsROUjBDvakc5Zaga51Y4ZAkPCXc41pzKBfyluqTr2j8zOx8QQQb7zyglu1LVoL5aSNWf2SQ==",
+    "node_modules/eslint-config-preact/node_modules/@babel/eslint-parser": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.29.7.tgz",
+      "integrity": "sha512-zxt+UJTOMKvUt3yOg+D58MLuz334pHp93qifMFcjIIO+9hN6t+ufw2gi7vDPMpxvfnHRR+3VVXvIjineCcgyXw==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@mdn/browser-compat-data": "^6.1.1",
-        "ast-metadata-inferer": "^0.8.1",
-        "browserslist": "^4.25.2",
-        "find-up": "^5.0.0",
-        "globals": "^15.7.0",
-        "lodash.memoize": "^4.1.2",
-        "semver": "^7.6.2"
+        "@nicolo-ribaudo/eslint-scope-5-internals": "5.1.1-v1",
+        "eslint-visitor-keys": "^2.1.0",
+        "semver": "^6.3.1"
       },
       "engines": {
-        "node": ">=18.x"
+        "node": "^10.13.0 || ^12.13.0 || >=14.0.0"
       },
       "peerDependencies": {
-        "eslint": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0"
+        "@babel/core": "^7.11.0",
+        "eslint": "^7.5.0 || ^8.0.0 || ^9.0.0"
       }
     },
-    "node_modules/eslint-plugin-compat/node_modules/globals": {
-      "version": "15.15.0",
-      "resolved": "https://registry.npmjs.org/globals/-/globals-15.15.0.tgz",
-      "integrity": "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==",
+    "node_modules/eslint-config-preact/node_modules/@eslint/js": {
+      "version": "9.39.4",
+      "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.4.tgz",
+      "integrity": "sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==",
       "dev": true,
       "license": "MIT",
       "engines": {
-        "node": ">=18"
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
       },
       "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
+        "url": "https://eslint.org/donate"
       }
     },
-    "node_modules/eslint-plugin-compat/node_modules/semver": {
-      "version": "7.8.2",
-      "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.2.tgz",
-      "integrity": "sha512-c8jsqUZm3omBOI66G90z1Dyw5z622G8oLG+omfsHBJf3CWQTlOcwOjvOG6wtiNfW6anKm/eA39LMwMtMez2TiQ==",
+    "node_modules/eslint-config-preact/node_modules/balanced-match": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+      "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
       "dev": true,
-      "license": "ISC",
-      "bin": {
-        "semver": "bin/semver.js"
-      },
-      "engines": {
-        "node": ">=10"
+      "license": "MIT"
+    },
+    "node_modules/eslint-config-preact/node_modules/brace-expansion": {
+      "version": "1.1.15",
+      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz",
+      "integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "balanced-match": "^1.0.0",
+        "concat-map": "0.0.1"
       }
     },
-    "node_modules/eslint-plugin-react": {
+    "node_modules/eslint-config-preact/node_modules/eslint-plugin-react": {
       "version": "7.37.5",
       "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.5.tgz",
       "integrity": "sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==",
@@ -2272,7 +2155,7 @@
         "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7"
       }
     },
-    "node_modules/eslint-plugin-react-hooks": {
+    "node_modules/eslint-config-preact/node_modules/eslint-plugin-react-hooks": {
       "version": "5.2.0",
       "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.2.0.tgz",
       "integrity": "sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==",
@@ -2285,18 +2168,104 @@
         "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0"
       }
     },
+    "node_modules/eslint-config-preact/node_modules/eslint-visitor-keys": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz",
+      "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/eslint-config-preact/node_modules/globals": {
+      "version": "16.5.0",
+      "resolved": "https://registry.npmjs.org/globals/-/globals-16.5.0.tgz",
+      "integrity": "sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/eslint-config-preact/node_modules/minimatch": {
+      "version": "3.1.5",
+      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
+      "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "brace-expansion": "^1.1.7"
+      },
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/eslint-plugin-compat": {
+      "version": "6.2.1",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-compat/-/eslint-plugin-compat-6.2.1.tgz",
+      "integrity": "sha512-gLKqUH+lQcCL+HzsROUjBDvakc5Zaga51Y4ZAkPCXc41pzKBfyluqTr2j8zOx8QQQb7zyglu1LVoL5aSNWf2SQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@mdn/browser-compat-data": "^6.1.1",
+        "ast-metadata-inferer": "^0.8.1",
+        "browserslist": "^4.25.2",
+        "find-up": "^5.0.0",
+        "globals": "^15.7.0",
+        "lodash.memoize": "^4.1.2",
+        "semver": "^7.6.2"
+      },
+      "engines": {
+        "node": ">=18.x"
+      },
+      "peerDependencies": {
+        "eslint": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0"
+      }
+    },
+    "node_modules/eslint-plugin-compat/node_modules/globals": {
+      "version": "15.15.0",
+      "resolved": "https://registry.npmjs.org/globals/-/globals-15.15.0.tgz",
+      "integrity": "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/eslint-plugin-compat/node_modules/semver": {
+      "version": "7.8.2",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.2.tgz",
+      "integrity": "sha512-c8jsqUZm3omBOI66G90z1Dyw5z622G8oLG+omfsHBJf3CWQTlOcwOjvOG6wtiNfW6anKm/eA39LMwMtMez2TiQ==",
+      "dev": true,
+      "license": "ISC",
+      "bin": {
+        "semver": "bin/semver.js"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
     "node_modules/eslint-scope": {
-      "version": "8.4.0",
-      "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz",
-      "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==",
+      "version": "9.1.2",
+      "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-9.1.2.tgz",
+      "integrity": "sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==",
       "dev": true,
       "license": "BSD-2-Clause",
       "dependencies": {
+        "@types/esrecurse": "^4.3.1",
+        "@types/estree": "^1.0.8",
         "esrecurse": "^4.3.0",
         "estraverse": "^5.2.0"
       },
       "engines": {
-        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+        "node": "^20.19.0 || ^22.13.0 || >=24"
       },
       "funding": {
         "url": "https://opencollective.com/eslint"
@@ -2315,6 +2284,37 @@
         "url": "https://opencollective.com/eslint"
       }
     },
+    "node_modules/eslint/node_modules/eslint-visitor-keys": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz",
+      "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "engines": {
+        "node": "^20.19.0 || ^22.13.0 || >=24"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint"
+      }
+    },
+    "node_modules/eslint/node_modules/espree": {
+      "version": "11.2.0",
+      "resolved": "https://registry.npmjs.org/espree/-/espree-11.2.0.tgz",
+      "integrity": "sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "dependencies": {
+        "acorn": "^8.16.0",
+        "acorn-jsx": "^5.3.2",
+        "eslint-visitor-keys": "^5.0.1"
+      },
+      "engines": {
+        "node": "^20.19.0 || ^22.13.0 || >=24"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint"
+      }
+    },
     "node_modules/espree": {
       "version": "10.4.0",
       "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz",
@@ -2815,23 +2815,6 @@
         "node": ">= 4"
       }
     },
-    "node_modules/import-fresh": {
-      "version": "3.3.1",
-      "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
-      "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "parent-module": "^1.0.0",
-        "resolve-from": "^4.0.0"
-      },
-      "engines": {
-        "node": ">=6"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
     "node_modules/import-local": {
       "version": "3.2.0",
       "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz",
@@ -3363,29 +3346,6 @@
       "dev": true,
       "license": "MIT"
     },
-    "node_modules/js-yaml": {
-      "version": "4.2.0",
-      "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.2.0.tgz",
-      "integrity": "sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==",
-      "dev": true,
-      "funding": [
-        {
-          "type": "github",
-          "url": "https://github.com/sponsors/puzrin"
-        },
-        {
-          "type": "github",
-          "url": "https://github.com/sponsors/nodeca"
-        }
-      ],
-      "license": "MIT",
-      "dependencies": {
-        "argparse": "^2.0.1"
-      },
-      "bin": {
-        "js-yaml": "bin/js-yaml.js"
-      }
-    },
     "node_modules/jsesc": {
       "version": "3.1.0",
       "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",
@@ -3518,13 +3478,6 @@
       "dev": true,
       "license": "MIT"
     },
-    "node_modules/lodash.merge": {
-      "version": "4.6.2",
-      "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
-      "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
-      "dev": true,
-      "license": "MIT"
-    },
     "node_modules/loose-envify": {
       "version": "1.4.0",
       "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
@@ -3599,16 +3552,19 @@
       }
     },
     "node_modules/minimatch": {
-      "version": "3.1.5",
-      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
-      "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
+      "version": "10.2.5",
+      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz",
+      "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==",
       "dev": true,
-      "license": "ISC",
+      "license": "BlueOak-1.0.0",
       "dependencies": {
-        "brace-expansion": "^1.1.7"
+        "brace-expansion": "^5.0.5"
       },
       "engines": {
-        "node": "*"
+        "node": "18 || 20 || >=22"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
       }
     },
     "node_modules/ms": {
@@ -3844,19 +3800,6 @@
         "node": ">=6"
       }
     },
-    "node_modules/parent-module": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
-      "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "callsites": "^3.0.0"
-      },
-      "engines": {
-        "node": ">=6"
-      }
-    },
     "node_modules/path-exists": {
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
@@ -4153,16 +4096,6 @@
         "node": ">=8"
       }
     },
-    "node_modules/resolve-from": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
-      "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=4"
-      }
-    },
     "node_modules/safe-array-concat": {
       "version": "1.1.4",
       "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.4.tgz",
@@ -4580,19 +4513,6 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
-    "node_modules/strip-json-comments": {
-      "version": "3.1.1",
-      "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
-      "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=8"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
     "node_modules/supports-color": {
       "version": "7.2.0",
       "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
diff --git a/dumper-companion/package.json b/dumper-companion/package.json
index 90e97964..289af7cd 100644
--- a/dumper-companion/package.json
+++ b/dumper-companion/package.json
@@ -16,9 +16,9 @@
     "webpack-cli": "^7.0.2"
   },
   "devDependencies": {
-    "@eslint/js": "^9.39.4",
+    "@eslint/js": "^10.0.1",
     "@stylistic/eslint-plugin": "^5.10.0",
-    "eslint": "^9.39.4",
+    "eslint": "^10.4.1",
     "eslint-config-preact": "^2.0.0",
     "globals": "^17.5.0",
     "typescript-eslint": "^8.58.2"



From noreply at scummvm.org  Sat Jun  6 16:27:35 2026
From: noreply at scummvm.org (lephilousophe)
Date: Sat,  6 Jun 2026 16:27:35 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm-web master ->
 552125cf9f6e2ee89e821569811774fab03f0032
Message-ID: <20260606162735.825EE700573@ron.scummvm.net>

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

Summary:
552125cf9f WEB: Add Dumper Companion link in the main menu


Commit: 552125cf9f6e2ee89e821569811774fab03f0032
    https://github.com/scummvm/scummvm-web/commit/552125cf9f6e2ee89e821569811774fab03f0032
Author: Le Philousophe (lephilousophe at users.noreply.github.com)
Date: 2026-06-06T18:27:01+02:00

Commit Message:
WEB: Add Dumper Companion link in the main menu

Changed paths:
    data/en/menus.yaml
    data/en/strings.json


diff --git a/data/en/menus.yaml b/data/en/menus.yaml
index c1f0c93e..4b649b41 100644
--- a/data/en/menus.yaml
+++ b/data/en/menus.yaml
@@ -17,6 +17,8 @@
       href: /demos/director
     - name: '{#menuMainBlogs#}'
       href: 'https://planet.scummvm.org'
+    - name: '{#menuMainDumper#}'
+      href: /dumper-companion
 - name: '{#menuHeaderDocumentation#}'
   class: menu-docs
   links:
diff --git a/data/en/strings.json b/data/en/strings.json
index ca56eb14..262297ba 100644
--- a/data/en/strings.json
+++ b/data/en/strings.json
@@ -150,6 +150,7 @@
   "menuMainDownloads": "Downloads",
   "menuMainFreewareGames": "Freeware Games & Addons",
   "menuMainBlogs": "ScummVM Blogs",
+  "menuMainDumper": "Dumper Companion",
   "menuHeaderDocumentation": "Documentation",
   "menuDocumentationFaq": "F.A.Q.",
   "menuDocumentationDocumentation": "Documentation",



From noreply at scummvm.org  Sat Jun  6 16:29:11 2026
From: noreply at scummvm.org (lotharsm)
Date: Sat,  6 Jun 2026 16:29:11 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm-web master ->
 0d439af6ebfaefacb84109c97f1802abba0d627a
Message-ID: <20260606162911.1B4AF7005CC@ron.scummvm.net>

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

Summary:
0d439af6eb DATA: Update data files


Commit: 0d439af6ebfaefacb84109c97f1802abba0d627a
    https://github.com/scummvm/scummvm-web/commit/0d439af6ebfaefacb84109c97f1802abba0d627a
Author: lephilousophe (lephilousophe at gmail.com)
Date: 2026-06-06T16:29:06Z

Commit Message:
DATA: Update data files

Changed paths:
    data/en/compatibility.yaml
    data/en/engines.yaml
    data/en/games.yaml


diff --git a/data/en/compatibility.yaml b/data/en/compatibility.yaml
index 9451b7c8..2f5c7bb5 100644
--- a/data/en/compatibility.yaml
+++ b/data/en/compatibility.yaml
@@ -5781,3 +5781,17 @@
     since_version: DEV
     stable_platforms: win
     unstable_platforms: ''
+-
+    id: 'bolt:carnival'
+    support: excellent
+    notes: '- Only Windows/Macintosh version is supported'
+    since_version: DEV
+    stable_platforms: 'win,mac'
+    unstable_platforms: ''
+-
+    id: 'access:noctropolis'
+    support: good
+    notes: '- Only EN language currently supported'
+    since_version: DEV
+    stable_platforms: win
+    unstable_platforms: ''
diff --git a/data/en/engines.yaml b/data/en/engines.yaml
index 177d9d6f..f68e9e70 100644
--- a/data/en/engines.yaml
+++ b/data/en/engines.yaml
@@ -69,6 +69,11 @@
     name: 'Blade Runner'
     alt_name: ''
     enabled: true
+-
+    id: bolt
+    name: BOLT
+    alt_name: 'Funhouse Engine'
+    enabled: true
 -
     id: buried
     name: 'The Journeyman Project 2: Buried in Time'
diff --git a/data/en/games.yaml b/data/en/games.yaml
index 168d030d..2da77d08 100644
--- a/data/en/games.yaml
+++ b/data/en/games.yaml
@@ -27,6 +27,20 @@
     datafiles: Martian_Memorandum
     wikipedia_page: Martian_Memorandum
     series_id: texmurphy
+-
+    id: 'access:noctropolis'
+    name: Noctropolis
+    engine_id: access
+    company_id: access
+    year: '1994'
+    moby_id: '1511'
+    steam_id: '377070'
+    gog_id: noctropolis
+    zoom_id: ''
+    additional_stores: ''
+    datafiles: ''
+    wikipedia_page: Noctropolis
+    series_id: ''
 -
     id: 'adl:hires0'
     name: 'Hi-Res Adventure #0: Mission Asteroid'
@@ -6453,6 +6467,20 @@
     datafiles: Blade_Runner
     wikipedia_page: Blade_Runner_(1997_video_game)
     series_id: ''
+-
+    id: 'bolt:carnival'
+    name: 'Cartoon Carnival'
+    engine_id: bolt
+    company_id: funhouse
+    year: '1993'
+    moby_id: '40084'
+    steam_id: ''
+    gog_id: ''
+    zoom_id: ''
+    additional_stores: ''
+    datafiles: Cartoon_Carnival
+    wikipedia_page: ''
+    series_id: ''
 -
     id: 'buried:buried'
     name: 'The Journeyman Project 2: Buried in Time'



From noreply at scummvm.org  Sat Jun  6 18:20:51 2026
From: noreply at scummvm.org (lephilousophe)
Date: Sat,  6 Jun 2026 18:20:51 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm-web master ->
 f011a0a6a2420a497efd30812c01281b60bbdfb4
Message-ID: <20260606182051.504D87005CC@ron.scummvm.net>

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

Summary:
f011a0a6a2 DUMPER-COMPANION: Add support for hybrid ISO/HFS discs


Commit: f011a0a6a2420a497efd30812c01281b60bbdfb4
    https://github.com/scummvm/scummvm-web/commit/f011a0a6a2420a497efd30812c01281b60bbdfb4
Author: Le Philousophe (lephilousophe at users.noreply.github.com)
Date: 2026-06-06T20:19:58+02:00

Commit Message:
DUMPER-COMPANION: Add support for hybrid ISO/HFS discs

Only dump the HFS partition, though.

Changed paths:
    dumper-companion/src/hfs/main.ts


diff --git a/dumper-companion/src/hfs/main.ts b/dumper-companion/src/hfs/main.ts
index f6055418..fafc7ea0 100644
--- a/dumper-companion/src/hfs/main.ts
+++ b/dumper-companion/src/hfs/main.ts
@@ -30,7 +30,7 @@
 import * as bitmanip from './bitmanip';
 import * as btree from './btree';
 import { AbstractFolder, FileOrFolder, MacFile, MacFolder } from './directory';
-import { charCode, joinBytes, bytes } from '../util';
+import { joinBytes, bytes, bytesToString } from '../util';
 import struct from '../struct';
 
 
@@ -66,28 +66,78 @@ export class Volume extends AbstractFolder {
         this.name = 'Untitled';
     }
 
-    findMagicNumber(from_volume: Uint8Array): number {
-        // Image data starts at 0x54 in DiskCopy 4.2 format https://wiki.68kmla.org/DiskCopy_4.2_format_specification
+    probePartition(from_volume: Uint8Array): Uint8Array|null {
+        // Search for partition first
+        for (let partition_num = 1; ; partition_num++) {
+            // Apple Partition Map
+            if (from_volume.length < partition_num*512+1 || from_volume[partition_num*512] != 0x50 || from_volume[partition_num*512+1] != 0x4D) { // Searching PM
+                break;
+            }
+
+            const [num_partitions, partition_start, partition_size] = struct<[number, number, number]>('>III').unpack_from(from_volume.subarray(partition_num*512+4, partition_num*512+16));
+            const partition_type = bytesToString(from_volume.subarray(partition_num*512+48, partition_num*512+80));
+            if (partition_type.startsWith("Apple_HFS\x00")) {
+                // First HFS partition: take it
+                return from_volume.subarray(partition_start*512, (partition_start+partition_size)*512);
+            }
 
-        for (const startOffset of [0x0, 0x54]) {
-            for (let i = startOffset; i < from_volume.length; i += 512) {
-                if (from_volume[i+1024] === charCode('B') && from_volume[i+1024+1] === charCode('D')) {
-                    return i;
+            if (partition_num >= num_partitions) {
+                break;
+            }
+        }
+
+        // No partition
+        return null;
+    }
+
+    probeStart(from_volume: Uint8Array): Uint8Array|null {
+        for (let i = 0; i + 1024 + 1 < from_volume.length; i += 512) {
+            if (from_volume[i+1024] === 0x42 && from_volume[i+1024+1] === 0x44) { // Searching BD
+                if (i == 0) {
+                    return from_volume;
                 }
+                return from_volume.subarray(i);
+            }
+        }
+
+        return null;
+    }
+
+    probe(from_volume: Uint8Array): Uint8Array|null {
+        // Image data starts at 0x54 in DiskCopy 4.2 format https://wiki.68kmla.org/DiskCopy_4.2_format_specification
+        // 0x52-0x53 bytes have 01 00 value
+        const maybeDC42 = from_volume.length >= 0x54 && from_volume[0x52] == 0x01 && from_volume[0x53] == 0x00;
+
+        if (maybeDC42) {
+            const dc42 = from_volume.subarray(0x54);
+
+            // Try to find a partition first with DC42 offset
+            const partitioned = this.probePartition(dc42);
+            if (partitioned !== null) {
+                return this.probeStart(partitioned);
             }
+            // Try looking for start using DC42 offset then
+            const start = this.probeStart(dc42);
+            if (start !== null) {
+                return start;
+            }
+
+            // If it failed, follow on with RAW images
         }
 
-        return -1;
+        const partitioned = this.probePartition(from_volume);
+        if (partitioned !== null) {
+            return this.probeStart(partitioned);
+        }
+
+        return this.probeStart(from_volume);
     }
 
     read(from_volume: Uint8Array): void {
-        const magicOffset = this.findMagicNumber(from_volume);
-        if (magicOffset < 0) {
+        const volume = this.probe(from_volume);
+        if (volume === null) {
             throw new Error('Magic number not found in image');
         }
-        if (magicOffset > 0) {
-            from_volume = from_volume.subarray(magicOffset);
-        }
 
         /* eslint-disable @typescript-eslint/no-unused-vars */
         const [drSigWord, drCrDate, drLsMod, drAtrb, drNmFls,
@@ -103,7 +153,7 @@ export class Volume extends AbstractFolder {
                 number, number, number, number, number, number,
                 Uint8Array, number, number, number,
                 number, Uint8Array,
-                number, Uint8Array]>('>2sLLHHHHHLLHLH28pLHLLLHLL32sHHHL12sL12s').unpack_from(from_volume, 1024);
+                number, Uint8Array]>('>2sLLHHHHHLLHLH28pLHLLLHLL32sHHHL12sL12s').unpack_from(volume, 1024);
         /* eslint-enable @typescript-eslint/no-unused-vars */
 
         this.crdate = drCrDate;
@@ -113,7 +163,7 @@ export class Volume extends AbstractFolder {
         const block2offset = (block: number) => 512*drAlBlSt + drAlBlkSiz*block;
         const getextents = (extents: [number, number][]) => joinBytes(
             extents.map(
-                ([firstblk, blkcnt]: [number, number]) => from_volume.subarray(
+                ([firstblk, blkcnt]: [number, number]) => volume.subarray(
                     block2offset(firstblk),
                     block2offset(firstblk+blkcnt)
                 )



From noreply at scummvm.org  Sat Jun  6 19:01:46 2026
From: noreply at scummvm.org (neuromancer)
Date: Sat,  6 Jun 2026 19:01:46 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 084f0d6376b59d2d8aaf55b1f6390d88cbcc30a2
Message-ID: <20260606190146.259A97005CC@ron.scummvm.net>

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:
80a7a31357 SCUMM: RA2: small corruption fixed in some levels
4865d17161 SCUMM: RA2: fixed corruption from L2 to L3
084f0d6376 SCUMM: RA2: reduce oscilation when using the mouse as input


Commit: 80a7a313577987c3c5bd1b91522ecaf61abd9f70
    https://github.com/scummvm/scummvm/commit/80a7a313577987c3c5bd1b91522ecaf61abd9f70
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-06T20:45:51+02:00

Commit Message:
SCUMM: RA2: small corruption fixed in some levels

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


diff --git a/engines/scumm/insane/rebel2/rebel.cpp b/engines/scumm/insane/rebel2/rebel.cpp
index 7b4781a1ffa..7b8cb21dee8 100644
--- a/engines/scumm/insane/rebel2/rebel.cpp
+++ b/engines/scumm/insane/rebel2/rebel.cpp
@@ -174,6 +174,8 @@ InsaneRebel2::InsaneRebel2(ScummEngine_v7 *scumm) {
 
 	_pauseOverlayActive = false;
 	memset(_savedPausePalette, 0, sizeof(_savedPausePalette));
+	memset(_rebelEmbeddedCodec45Palette, 0, sizeof(_rebelEmbeddedCodec45Palette));
+	memset(_rebelEmbeddedCodec45Lookup, 0, sizeof(_rebelEmbeddedCodec45Lookup));
 
 	_enemies.clear();
 	_rebelHandler = 0;  // Not set yet - will be set by IACT opcode 6
@@ -1102,6 +1104,20 @@ bool InsaneRebel2::notifyEvent(const Common::Event &event) {
 			}
 			break;
 
+		case Common::KEYCODE_d:
+			// Debug shortcut: Shift+D forces the current gameplay section to end in death.
+			if (splayer &&
+			    _gameState == kStateGameplay &&
+			    _rebelHandler != 0 &&
+			    event.kbd.hasFlags(Common::KBD_SHIFT)) {
+				_playerDamage = 255;
+				_playerShield = 0;
+				debug("Rebel2: Shift+D pressed - forcing player death");
+				_vm->_smushVideoShouldFinish = true;
+				return true;  // Consume the event
+			}
+			break;
+
 		default:
 			break;
 		}
diff --git a/engines/scumm/insane/rebel2/rebel.h b/engines/scumm/insane/rebel2/rebel.h
index 099add0d6cd..e8fe499b865 100644
--- a/engines/scumm/insane/rebel2/rebel.h
+++ b/engines/scumm/insane/rebel2/rebel.h
@@ -772,6 +772,8 @@ public:
 	};
 
 	EmbeddedSanFrame _rebelEmbeddedHud[16];  // HUD overlay slots (userId 0-15)
+	byte _rebelEmbeddedCodec45Palette[0x300];
+	byte _rebelEmbeddedCodec45Lookup[0x8000];
 
 	// Load and decode an embedded SAN animation from IACT chunk data
 	// userId: HUD slot (1-4), animData: raw ANIM data, size: data size, renderBitmap: current frame buffer
diff --git a/engines/scumm/insane/rebel2/render.cpp b/engines/scumm/insane/rebel2/render.cpp
index c0a7e3ab4d7..1c986483361 100644
--- a/engines/scumm/insane/rebel2/render.cpp
+++ b/engines/scumm/insane/rebel2/render.cpp
@@ -403,8 +403,9 @@ void InsaneRebel2::loadEmbeddedSan(int userId, byte *animData, int32 size, byte
 								frame.valid = true;
 								debug("Rebel2: Decoded embedded HUD (codec %d/line update): %dx%d", codec, width, height);
 							} else if (codec == 45) {
-								// Codec 45: RA2-specific BOMP RLE (FUN_0042B5F0)
-								smushDecodeRA2Bomp(frame.pixels, fobjData, 0, 0, width, height, width, dataSize);
+								// Codec 45: blur/wipe mask (FUN_0042B460 -> FUN_0042B530 -> FUN_0042DDF0)
+								smushDecodeRA2Blur(frame.pixels, fobjData, 0, 0, width, height, width, dataSize,
+									_rebelEmbeddedCodec45Palette, _rebelEmbeddedCodec45Lookup);
 								frame.valid = true;
 							} else if (codec == 23) {
 								// Codec 23: Skip/copy with embedded RLE (FUN_0042BBF0)
diff --git a/engines/scumm/smush/rebel/codec_ra2.cpp b/engines/scumm/smush/rebel/codec_ra2.cpp
index 44f454f88b4..e528d51f8a9 100644
--- a/engines/scumm/smush/rebel/codec_ra2.cpp
+++ b/engines/scumm/smush/rebel/codec_ra2.cpp
@@ -225,134 +225,99 @@ void smushDecodeSkipRLE(byte *dst, const byte *src, int left, int top, int width
 	}
 }
 
-/**
- * Codec 45: RA2-specific BOMP RLE with variable header
- * Used for embedded ANIM frames, particularly small animation elements.
- * Has a variable-length header (commonly 6 bytes starting with 01 FE).
- * Note: For overlay sprites, color 0 is treated as transparent.
- */
-void smushDecodeRA2Bomp(byte *dst, const byte *src, int left, int top, int width, int height, int pitch, int dataSize) {
-	dst += top * pitch + left;
-
-	// Detect header pattern and find RLE data start
-	int headerSkip = 0;
-	bool foundValidOffset = false;
-
-	// Check for common 6-byte header pattern: 01 FE XX XX XX XX
-	if (dataSize > 6 && src[0] == 0x01 && src[1] == 0xFE) {
-		headerSkip = 6;
-		foundValidOffset = true;
-	} else {
-		// Probe offsets to find valid RLE start
-		// Valid start should have reasonable line size values
-		for (int testOffset = 0; testOffset <= 6 && testOffset + 2 <= dataSize; testOffset += 2) {
-			int testLineSize = READ_LE_INT16(src + testOffset);
-			// A valid line size should be positive and reasonable for the width
-			if (testLineSize > 0 && testLineSize <= width * 2 && testLineSize < dataSize - testOffset) {
-				// Further validation: try to count valid line sizes
-				int linesTest = 0;
-				const byte *testPtr = src + testOffset;
-				bool validSum = true;
-
-				while (linesTest < height && testPtr + 2 <= src + dataSize) {
-					int ls = READ_LE_INT16(testPtr);
-					if (ls <= 0 || ls > width * 2) {
-						validSum = false;
-						break;
-					}
-					testPtr += ls + 2;
-					linesTest++;
-				}
-
-				if (validSum && linesTest >= height - 1) {
-					headerSkip = testOffset;
-					foundValidOffset = true;
-					break;
-				}
-			}
+bool smushPrepareRA2BlurData(const byte *src, int dataSize, byte *palette, byte *lookup, const byte *&maskData, int &maskSize) {
+	maskData = nullptr;
+	maskSize = 0;
+
+	if (src == nullptr || dataSize < 6 || palette == nullptr || lookup == nullptr)
+		return false;
+
+	// FUN_0042B530 only processes this codec body when byte +4 is 1.
+	if (src[4] != 1)
+		return false;
+
+	const int tableMode = READ_LE_INT16(src + 2);
+	maskData = src + 6;
+	maskSize = dataSize - 6;
+
+	if (tableMode == 0) {
+		if (dataSize < 0x306)
+			return false;
+
+		memcpy(palette, src + 6, 0x300);
+
+		const byte *p = src + 0x306;
+		int remaining = dataSize - 0x306;
+		int lookupIndex = 0;
+		while (lookupIndex < 0x8000 && remaining >= 2) {
+			int count = p[0];
+			const byte color = p[1];
+			p += 2;
+			remaining -= 2;
+
+			while (count-- > 0 && lookupIndex < 0x8000)
+				lookup[lookupIndex++] = color;
 		}
-	}
 
-	if (!foundValidOffset) {
-		warning("smushDecodeRA2Bomp: Codec 45 couldn't find valid RLE offset, using offset 0");
-	}
+		if (lookupIndex < 0x8000)
+			return false;
 
-	src += headerSkip;
-	const byte *dataEnd = src + (dataSize - headerSkip);
+		maskData = p;
+		maskSize = remaining;
+	}
 
-	// Check first value to determine per-line vs continuous mode
-	int firstVal = (src + 2 <= dataEnd) ? READ_LE_INT16(src) : 0;
-	bool perLineMode = (firstVal > 0 && firstVal <= width * 2);
+	return maskSize > 3;
+}
 
-	if (perLineMode) {
-		// Per-line RLE with 2-byte size headers
-		for (int row = 0; row < height && src < dataEnd; row++) {
-			int lineSize = READ_LE_INT16(src);
-			src += 2;
-			if (lineSize <= 0 || lineSize > (int)(dataEnd - src))
-				break;
+/**
+ * Codec 45: RA2 blur/wipe mask.
+ * Original path: FUN_0042B460 -> FUN_0042B530 -> FUN_0042DDF0.
+ */
+void smushDecodeRA2Blur(byte *dst, const byte *src, int left, int top, int dstWidth, int dstHeight, int pitch, int dataSize, byte *palette, byte *lookup) {
+	const byte *maskData = nullptr;
+	int maskSize = 0;
+	if (dst == nullptr || dstWidth <= 2 || dstHeight <= 2 || pitch <= 0 ||
+			!smushPrepareRA2BlurData(src, dataSize, palette, lookup, maskData, maskSize))
+		return;
 
-			const byte *lineEnd = src + lineSize;
-			byte *rowDst = dst + row * pitch;
-			int x = 0;
-
-			while (src < lineEnd && x < width) {
-				byte ctrl = *src++;
-				int count = (ctrl >> 1) + 1;
-
-				if (ctrl & 1) {
-					// RLE fill - color 0 is transparent for overlay sprites
-					byte color = (src < lineEnd) ? *src++ : 0;
-					if (color != 0) {
-						int num = (count > width - x) ? width - x : count;
-						memset(rowDst + x, color, num);
-					}
-					x += count;
-					if (x > width)
-						x = width;
-				} else {
-					// Literal copy - color 0 is transparent for overlay sprites
-					for (int i = 0; i < count && x < width && src < lineEnd; i++) {
-						byte color = *src++;
-						if (color != 0)
-							rowDst[x] = color;
-						x++;
-					}
-				}
-			}
-			src = lineEnd;
-		}
-	} else {
-		// Continuous BOMP RLE (no per-line headers)
-		for (int row = 0; row < height && src < dataEnd; row++) {
-			byte *rowDst = dst + row * pitch;
-			int x = 0;
-
-			while (x < width && src < dataEnd) {
-				byte ctrl = *src++;
-				int count = (ctrl >> 1) + 1;
-
-				if (ctrl & 1) {
-					// RLE fill - color 0 is transparent for overlay sprites
-					byte color = (src < dataEnd) ? *src++ : 0;
-					if (color != 0) {
-						int num = (count > width - x) ? width - x : count;
-						memset(rowDst + x, color, num);
-					}
-					x += count;
-					if (x > width)
-						x = width;
-				} else {
-					// Literal copy - color 0 is transparent for overlay sprites
-					for (int i = 0; i < count && x < width && src < dataEnd; i++) {
-						byte color = *src++;
-						if (color != 0)
-							rowDst[x] = color;
-						x++;
-					}
-				}
+	int x = left;
+	int y = top;
+
+	while (maskSize > 3) {
+		const int dx = READ_LE_INT16(maskData);
+		const int dy = maskData[2];
+		const int count = maskData[3];
+		maskData += 4;
+		maskSize -= 4;
+
+		x += dx;
+		y += dy;
+
+		for (int i = 0; i <= count; ++i) {
+			if (x > 0 && y > 0 && x < dstWidth - 1) {
+				if (y >= dstHeight - 1)
+					return;
+
+				byte *pixel = dst + y * pitch + x;
+				const byte leftColor = pixel[-1];
+				const byte rightColor = pixel[1];
+				const byte topColor = pixel[-pitch];
+				const byte bottomColor = pixel[pitch];
+
+				const int red = palette[leftColor * 3] + palette[rightColor * 3] +
+					palette[topColor * 3] + palette[bottomColor * 3];
+				const int green = palette[leftColor * 3 + 1] + palette[rightColor * 3 + 1] +
+					palette[topColor * 3 + 1] + palette[bottomColor * 3 + 1];
+				const int blue = palette[leftColor * 3 + 2] + palette[rightColor * 3 + 2] +
+					palette[topColor * 3 + 2] + palette[bottomColor * 3 + 2];
+
+				const int lookupIndex = ((red << 5) & 0x7c00) + (green & 0x3e0) + (blue >> 5);
+				*pixel = lookup[lookupIndex & 0x7fff];
 			}
+
+			++x;
 		}
+		--x;
 	}
 }
 
diff --git a/engines/scumm/smush/rebel/codec_ra2.h b/engines/scumm/smush/rebel/codec_ra2.h
index 1aa8d2e86d0..b89797c1a0a 100644
--- a/engines/scumm/smush/rebel/codec_ra2.h
+++ b/engines/scumm/smush/rebel/codec_ra2.h
@@ -29,7 +29,7 @@ namespace Scumm {
 void smushDecodeRLEOpaque(byte *dst, const byte *src, int left, int top, int width, int height, int pitch, int dataSize);
 void smushDecodeLineUpdate(byte *dst, const byte *src, int left, int top, int width, int height, int pitch, int dataSize);
 void smushDecodeSkipRLE(byte *dst, const byte *src, int left, int top, int width, int height, int pitch, int dataSize);
-void smushDecodeRA2Bomp(byte *dst, const byte *src, int left, int top, int width, int height, int pitch, int dataSize);
+void smushDecodeRA2Blur(byte *dst, const byte *src, int left, int top, int dstWidth, int dstHeight, int pitch, int dataSize, byte *palette, byte *lookup);
 const byte *smushSkipRLELines(const byte *src, int &dataSize, int lines);
 
 } // End of namespace Scumm
diff --git a/engines/scumm/smush/rebel/smush_player_ra2.cpp b/engines/scumm/smush/rebel/smush_player_ra2.cpp
index 2c43508df0d..b24ea74678c 100644
--- a/engines/scumm/smush/rebel/smush_player_ra2.cpp
+++ b/engines/scumm/smush/rebel/smush_player_ra2.cpp
@@ -140,6 +140,8 @@ void SmushPlayerRebel2::initGamePlayerFields() {
 	_ra2FrameObjectSurfaceWidth = 0;
 	_ra2FrameObjectSurfaceHeight = 0;
 	_ra2PendingAnimHeaderPalette = false;
+	memset(_ra2Codec45Palette, 0, sizeof(_ra2Codec45Palette));
+	memset(_ra2Codec45Lookup, 0, sizeof(_ra2Codec45Lookup));
 	_scrollX = 0;
 	_scrollY = 0;
 }
@@ -726,6 +728,18 @@ void SmushPlayerRebel2::ra2PrepareFrameObjectSurface(int left, int top, int widt
 }
 
 bool SmushPlayerRebel2::ra2SelectFrameBuffer(int codec, int width, int height) {
+	if (codec == SMUSH_CODEC_RA2_BOMP) {
+		if (_specialBuffer != nullptr) {
+			_dst = _specialBuffer;
+			debugC(DEBUG_SMUSH, "SmushPlayerRebel2::ra2SelectFrameBuffer: Using _specialBuffer for codec 45 mask");
+		} else {
+			VirtScreen *vs = &_vm->_virtscr[kMainVirtScreen];
+			_dst = vs->getPixels(0, 0);
+			debugC(DEBUG_SMUSH, "SmushPlayerRebel2::ra2SelectFrameBuffer: Using virtual screen for codec 45 mask");
+		}
+		return true;
+	}
+
 	// Rebel2 allocates the low-res gameplay target as 424x260 (FUN_00424730).
 	// Use that target once an oversized gameplay FOBJ appears, then keep small
 	// overlay FOBJ chunks compositing into it. Pure 320x200 gameplay videos keep
@@ -802,8 +816,20 @@ bool SmushPlayerRebel2::ra2SelectFrameBuffer(int codec, int width, int height) {
  * Returns true if the codec was handled, false for standard codecs.
  */
 bool SmushPlayerRebel2::ra2DecodeCodec(int codec, const uint8 *src, int left, int top,
-								 int width, int height, int pitch, int dataSize) {
+									 int width, int height, int pitch, int dataSize) {
 	switch (codec) {
+	case SMUSH_CODEC_RLE_ALT:
+		// RA2 codec 3 dispatches directly to FUN_0042CAA0, the opaque RLE path.
+		smushDecodeRLEOpaque(_dst, src, left, top, width, height, pitch, dataSize);
+		return true;
+	case SMUSH_CODEC_RLE:
+		if ((_curVideoFlags & 0x100) != 0) {
+			// Original RA2 RLE dispatch selects the opaque renderer only when
+			// render flag 0x100 is set. Cinematic wipe masks use transparent 0.
+			smushDecodeRLEOpaque(_dst, src, left, top, width, height, pitch, dataSize);
+			return true;
+		}
+		return false;
 	case SMUSH_CODEC_UNCOMPRESSED: {
 		const int sourcePitch = (_ra2FrameObjectOriginalWidth > 0) ? _ra2FrameObjectOriginalWidth : width;
 		smushDecodeRA2Uncompressed(_dst, src, left, top, width, height, pitch, sourcePitch,
@@ -822,8 +848,8 @@ bool SmushPlayerRebel2::ra2DecodeCodec(int codec, const uint8 *src, int left, in
 		return true;
 	}
 	case SMUSH_CODEC_RA2_BOMP: {
-		const uint8 *adjustedSrc = smushSkipRLELines(src, dataSize, _ra2FrameSourceSkipY);
-		smushDecodeRA2Bomp(_dst, adjustedSrc, left, top, width, height, pitch, dataSize);
+		smushDecodeRA2Blur(_dst, src, left, top, pitch, _height, pitch, dataSize,
+			_ra2Codec45Palette, _ra2Codec45Lookup);
 		return true;
 	}
 	default:
@@ -943,10 +969,18 @@ bool SmushPlayerRebel2::handleGameAdjustCoords(int codec, int &left, int &top, i
 	const int adjustedLeft = left + _fobjOffsetX;
 	_ra2FrameSourceSkipX = (adjustedLeft < 0) ? -adjustedLeft : 0;
 	_ra2FrameSourceSkipY = 0;
+
+	if (codec == SMUSH_CODEC_RA2_BOMP) {
+		left = adjustedLeft;
+		top += _fobjOffsetY;
+		if (srcSkipY)
+			*srcSkipY = 0;
+		return true;
+	}
+
 	adjustFrameCoords(left, top, width, height, pitch, &sourceSkipY);
 	if (codec == SMUSH_CODEC_LINE_UPDATE || codec == SMUSH_CODEC_LINE_UPDATE2 ||
-			codec == SMUSH_CODEC_SKIP_RLE || codec == SMUSH_CODEC_RA2_BOMP ||
-			codec == SMUSH_CODEC_UNCOMPRESSED) {
+			codec == SMUSH_CODEC_SKIP_RLE || codec == SMUSH_CODEC_UNCOMPRESSED) {
 		_ra2FrameSourceSkipY = sourceSkipY;
 		if (srcSkipY)
 			*srcSkipY = 0;
diff --git a/engines/scumm/smush/rebel/smush_player_ra2.h b/engines/scumm/smush/rebel/smush_player_ra2.h
index 87c64a12431..ac4700919e8 100644
--- a/engines/scumm/smush/rebel/smush_player_ra2.h
+++ b/engines/scumm/smush/rebel/smush_player_ra2.h
@@ -89,6 +89,8 @@ private:
 	int _ra2FrameObjectSurfaceWidth;
 	int _ra2FrameObjectSurfaceHeight;
 	bool _ra2PendingAnimHeaderPalette;
+	byte _ra2Codec45Palette[0x300];
+	byte _ra2Codec45Lookup[0x8000];
 };
 
 } // End of namespace Scumm


Commit: 4865d1716190ff623e2da2fba5f14881a66df031
    https://github.com/scummvm/scummvm/commit/4865d1716190ff623e2da2fba5f14881a66df031
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-06T20:45:51+02:00

Commit Message:
SCUMM: RA2: fixed corruption from L2 to L3

Changed paths:
    engines/scumm/insane/rebel2/iact.cpp
    engines/scumm/insane/rebel2/rebel.h
    engines/scumm/insane/rebel2/runlevels.cpp
    engines/scumm/smush/rebel/font_rebel2.cpp
    engines/scumm/smush/rebel/font_rebel2.h


diff --git a/engines/scumm/insane/rebel2/iact.cpp b/engines/scumm/insane/rebel2/iact.cpp
index 8122855a018..bbde2d14589 100644
--- a/engines/scumm/insane/rebel2/iact.cpp
+++ b/engines/scumm/insane/rebel2/iact.cpp
@@ -2079,10 +2079,9 @@ bool InsaneRebel2::loadHandler7FlySprites(Common::SeekableReadStream &b, int64 r
 		}
 	}
 
-	// Load as NUT
-	NutRenderer *newNut = new NutRenderer(_vm);
-	newNut->loadFontFromData(nutData, bytesRead);
-	if (newNut->getNumChars() <= 0) {
+	// Load as a Rebel2 embedded sprite ANIM.
+	NutRenderer *newNut = makeRebel2SpriteFromData(_vm, nutData, bytesRead);
+	if (!newNut || newNut->getNumChars() <= 0) {
 		debug("Rebel2 loadHandler7FlySprites: NUT load failed for par4=%d", par4);
 		delete newNut;
 		free(nutData);
@@ -2145,8 +2144,7 @@ bool InsaneRebel2::loadTurretHudOverlay(byte *animData, int32 size, int16 par3)
 		return false;  // Not a turret HUD slot
 	}
 
-	NutRenderer *newNut = new NutRenderer(_vm);
-	newNut->loadFontFromData(animData, size);
+	NutRenderer *newNut = makeRebel2SpriteFromData(_vm, animData, size);
 	if (!newNut || newNut->getNumChars() <= 0) {
 		debug("Rebel2 loadTurretHudOverlay: NUT load failed for par3=%d", par3);
 		delete newNut;
@@ -2187,8 +2185,7 @@ bool InsaneRebel2::loadHandler8ShipSprites(byte *animData, int32 size, int16 par
 		return false;
 	}
 
-	NutRenderer *newNut = new NutRenderer(_vm);
-	newNut->loadFontFromData(animData, size);
+	NutRenderer *newNut = makeRebel2SpriteFromData(_vm, animData, size);
 	if (!newNut || newNut->getNumChars() <= 0) {
 		debug("Rebel2 loadHandler8ShipSprites: NUT load failed for par4=%d", par4);
 		delete newNut;
@@ -2239,8 +2236,7 @@ bool InsaneRebel2::loadHandler25GrdSprites(byte *animData, int32 size, int16 par
 		return false;
 	}
 
-	NutRenderer *newNut = new NutRenderer(_vm);
-	newNut->loadFontFromData(animData, size);
+	NutRenderer *newNut = makeRebel2SpriteFromData(_vm, animData, size);
 	if (!newNut || newNut->getNumChars() <= 0) {
 		debug("Rebel2 loadHandler25GrdSprites: NUT load failed for par4=%d", par4);
 		delete newNut;
diff --git a/engines/scumm/insane/rebel2/rebel.h b/engines/scumm/insane/rebel2/rebel.h
index e8fe499b865..765ab1f4b22 100644
--- a/engines/scumm/insane/rebel2/rebel.h
+++ b/engines/scumm/insane/rebel2/rebel.h
@@ -434,6 +434,7 @@ public:
 	bool handleLevelDeath(int levelId, int phase, const char *deathVideo, const char *retryVideo, int &levelResult);
 	void resetLevelAttemptState(int initialPhase);
 	void resetLevelPhaseState(bool clearEnemies);
+	void clearEmbeddedHudFrames();
 	void resetLevelWaveState();
 	void resetHandler7FlightState();
 
diff --git a/engines/scumm/insane/rebel2/runlevels.cpp b/engines/scumm/insane/rebel2/runlevels.cpp
index 55a0e046ee1..ede78f398b2 100644
--- a/engines/scumm/insane/rebel2/runlevels.cpp
+++ b/engines/scumm/insane/rebel2/runlevels.cpp
@@ -278,6 +278,17 @@ void InsaneRebel2::resetLevelPhaseState(bool clearEnemies) {
 	_grd002Sprite = nullptr;
 	_grdShotOriginTableLoaded = false;
 
+	clearEmbeddedHudFrames();
+
+	free(_level2Background);
+	_level2Background = nullptr;
+	_level2BackgroundLoaded = false;
+
+	if (clearEnemies)
+		_enemies.clear();
+}
+
+void InsaneRebel2::clearEmbeddedHudFrames() {
 	for (uint i = 0; i < ARRAYSIZE(_rebelEmbeddedHud); ++i) {
 		EmbeddedSanFrame &frame = _rebelEmbeddedHud[i];
 		free(frame.pixels);
@@ -288,13 +299,6 @@ void InsaneRebel2::resetLevelPhaseState(bool clearEnemies) {
 		frame.renderY = 0;
 		frame.valid = false;
 	}
-
-	free(_level2Background);
-	_level2Background = nullptr;
-	_level2BackgroundLoaded = false;
-
-	if (clearEnemies)
-		_enemies.clear();
 }
 
 void InsaneRebel2::resetLevelWaveState() {
@@ -647,6 +651,8 @@ int InsaneRebel2::runLevel3() {
 		_playerDamage = 0;
 		_currentPhase = 1;
 
+		clearEmbeddedHudFrames();
+
 		// Reset bit table before gameplay starts - FUN_00423880 calls FUN_00423a00(0)
 		clearBit(0);
 		resetHandler7FlightState();
@@ -699,6 +705,8 @@ int InsaneRebel2::runLevel3() {
 		_playerDamage = 0;
 		_playerScore = phase1Score;
 
+		clearEmbeddedHudFrames();
+
 		// Reset bit table before gameplay starts
 		clearBit(0);
 		resetHandler7FlightState();
diff --git a/engines/scumm/smush/rebel/font_rebel2.cpp b/engines/scumm/smush/rebel/font_rebel2.cpp
index d83fbef6585..a9f69d0cbdb 100644
--- a/engines/scumm/smush/rebel/font_rebel2.cpp
+++ b/engines/scumm/smush/rebel/font_rebel2.cpp
@@ -27,22 +27,50 @@
 #include "scumm/file.h"
 #include "scumm/nut_renderer.h"
 #include "scumm/scumm.h"
+#include "scumm/smush/rebel/codec_ra2.h"
 
 namespace Scumm {
 
 enum {
-	kRebel2MaxStrings = 80
+	kRebel2MaxStrings = 80,
+	kRebel2MaxSpritePixels = 16 * 1024 * 1024,
+	kRebel2MaxDecodedSpriteBytes = 32 * 1024 * 1024
 };
 
+struct Rebel2NutFrameInfo {
+	Rebel2NutFrameInfo() : codec(0), xoffs(0), yoffs(0), width(0), height(0), data(nullptr), dataSize(0) {}
+
+	int codec;
+	int16 xoffs;
+	int16 yoffs;
+	uint16 width;
+	uint16 height;
+	const byte *data;
+	int32 dataSize;
+};
+
+static void decodeRebel2RawSprite(byte *dst, const byte *src, int width, int height, int dataSize) {
+	const int32 totalSize = width * height;
+	const int32 copySize = MIN<int32>(totalSize, dataSize);
+	if (copySize > 0)
+		memcpy(dst, src, copySize);
+}
+
 class Rebel2NutRenderer : public NutRenderer {
 public:
 	Rebel2NutRenderer(ScummEngine *vm, const char *filename) : NutRenderer(vm, nullptr) {
 		loadRebel2Font(filename);
 	}
 
+	Rebel2NutRenderer(ScummEngine *vm, const byte *data, int32 dataSize) : NutRenderer(vm, nullptr) {
+		loadRebel2SpriteFromData(data, dataSize);
+	}
+
 private:
 	void codec44(byte *dst, const byte *src, int width, int height, int pitch);
 	void loadRebel2Font(const char *filename);
+	void loadRebel2SpriteFromData(const byte *data, int32 dataSize);
+	void decodeRebel2Frame(byte *dst, const Rebel2NutFrameInfo &frame, byte *codec45Palette, byte *codec45Lookup);
 };
 
 void Rebel2NutRenderer::codec44(byte *dst, const byte *src, int width, int height, int pitch) {
@@ -189,10 +217,176 @@ void Rebel2NutRenderer::loadRebel2Font(const char *filename) {
 	delete[] dataSrc;
 }
 
+void Rebel2NutRenderer::decodeRebel2Frame(byte *dst, const Rebel2NutFrameInfo &frame, byte *codec45Palette, byte *codec45Lookup) {
+	switch (frame.codec) {
+	case 1:
+	case 3:
+		codec1(dst, frame.data, frame.width, frame.height, frame.width);
+		break;
+	case 20:
+		decodeRebel2RawSprite(dst, frame.data, frame.width, frame.height, frame.dataSize);
+		break;
+	case 21:
+		smushDecodeLineUpdate(dst, frame.data, 0, 0, frame.width, frame.height, frame.width, frame.dataSize);
+		break;
+	case 23:
+		smushDecodeSkipRLE(dst, frame.data, 0, 0, frame.width, frame.height, frame.width, frame.dataSize);
+		break;
+	case 44:
+		codec44(dst, frame.data, frame.width, frame.height, frame.width);
+		break;
+	case 45:
+		smushDecodeRA2Blur(dst, frame.data, 0, 0, frame.width, frame.height, frame.width, frame.dataSize,
+			codec45Palette, codec45Lookup);
+		break;
+	default:
+		warning("Rebel2NutRenderer::loadRebel2SpriteFromData: unknown codec: %d", frame.codec);
+		break;
+	}
+}
+
+void Rebel2NutRenderer::loadRebel2SpriteFromData(const byte *data, int32 dataSize) {
+	if (!data || dataSize < 16) {
+		warning("Rebel2NutRenderer::loadRebel2SpriteFromData: data too small (%d bytes)", dataSize);
+		return;
+	}
+
+	if (READ_BE_UINT32(data) != MKTAG('A','N','I','M')) {
+		warning("Rebel2NutRenderer::loadRebel2SpriteFromData: no ANIM chunk");
+		return;
+	}
+
+	uint32 length = READ_BE_UINT32(data + 4);
+	if (length > (uint32)(dataSize - 8)) {
+		warning("Rebel2NutRenderer::loadRebel2SpriteFromData: ANIM size (%u) exceeds data size (%d)", length, dataSize);
+		length = dataSize - 8;
+	}
+
+	const int64 animEnd = 8 + (int64)length;
+	if (animEnd < 16 || READ_BE_UINT32(data + 8) != MKTAG('A','H','D','R')) {
+		warning("Rebel2NutRenderer::loadRebel2SpriteFromData: no AHDR chunk in font data");
+		return;
+	}
+
+	int declaredChars = READ_LE_UINT16(data + 18);
+	if (declaredChars > (int)ARRAYSIZE(_chars)) {
+		warning("Rebel2NutRenderer::loadRebel2SpriteFromData: numChars (%d) exceeds max, clamping", declaredChars);
+		declaredChars = ARRAYSIZE(_chars);
+	}
+
+	Rebel2NutFrameInfo frames[ARRAYSIZE(_chars)];
+	uint64 decodedLength = 0;
+	int frameCount = 0;
+
+	for (int64 offset = 8; offset + 8 <= animEnd && frameCount < declaredChars;) {
+		const uint32 tag = READ_BE_UINT32(data + offset);
+		const uint32 chunkSize = READ_BE_UINT32(data + offset + 4);
+		const int64 chunkDataStart = offset + 8;
+		if ((int64)chunkSize > animEnd - chunkDataStart) {
+			warning("Rebel2NutRenderer::loadRebel2SpriteFromData: truncated chunk 0x%08x at offset %llx", tag, (long long)offset);
+			break;
+		}
+
+		const int64 chunkDataEnd = chunkDataStart + chunkSize;
+		int64 nextChunk = chunkDataEnd + (chunkSize & 1);
+		if (nextChunk > animEnd)
+			nextChunk = chunkDataEnd;
+
+		if (tag == MKTAG('F','R','M','E')) {
+			Rebel2NutFrameInfo &frame = frames[frameCount];
+			if (chunkSize >= 8 && chunkDataStart + 8 <= chunkDataEnd &&
+					READ_BE_UINT32(data + chunkDataStart) == MKTAG('F','O','B','J')) {
+				const uint32 fobjSize = READ_BE_UINT32(data + chunkDataStart + 4);
+				const int64 fobjDataStart = chunkDataStart + 8;
+				const int64 fobjDataEnd = fobjDataStart + fobjSize;
+
+				if (fobjSize >= 14 && fobjDataEnd <= chunkDataEnd) {
+					frame.codec = READ_LE_UINT16(data + fobjDataStart);
+					frame.xoffs = READ_LE_INT16(data + fobjDataStart + 2);
+					frame.yoffs = READ_LE_INT16(data + fobjDataStart + 4);
+					frame.width = READ_LE_UINT16(data + fobjDataStart + 6);
+					frame.height = READ_LE_UINT16(data + fobjDataStart + 8);
+					frame.data = data + fobjDataStart + 14;
+					frame.dataSize = fobjSize - 14;
+
+					const uint64 pixels = (uint64)frame.width * frame.height;
+					if (pixels == 0) {
+						frame.width = 0;
+						frame.height = 0;
+						frame.data = nullptr;
+						frame.dataSize = 0;
+					} else if (pixels > kRebel2MaxSpritePixels || decodedLength + pixels > kRebel2MaxDecodedSpriteBytes) {
+						warning("Rebel2NutRenderer::loadRebel2SpriteFromData: invalid sprite dimensions %ux%u at frame %d",
+							frame.width, frame.height, frameCount);
+						frame.width = 0;
+						frame.height = 0;
+						frame.data = nullptr;
+						frame.dataSize = 0;
+					} else {
+						decodedLength += pixels;
+					}
+				}
+			}
+			frameCount++;
+		}
+
+		offset = nextChunk;
+	}
+
+	_numChars = frameCount;
+	if (_numChars <= 0) {
+		warning("Rebel2NutRenderer::loadRebel2SpriteFromData: no decodable frames");
+		return;
+	}
+
+	delete[] _decodedData;
+	_decodedData = decodedLength ? new byte[(uint32)decodedLength] : nullptr;
+	memset(_chars, 0, sizeof(_chars));
+	_fontHeight = 0;
+
+	byte *decodedPtr = _decodedData;
+	byte codec45Palette[0x300];
+	byte codec45Lookup[0x8000];
+	memset(codec45Palette, 0, sizeof(codec45Palette));
+	memset(codec45Lookup, 0, sizeof(codec45Lookup));
+
+	for (int i = 0; i < _numChars; i++) {
+		const Rebel2NutFrameInfo &frame = frames[i];
+		_chars[i].xoffs = frame.xoffs;
+		_chars[i].yoffs = frame.yoffs;
+		_chars[i].width = frame.width;
+		_chars[i].height = frame.height;
+		_chars[i].transparency = kDefaultTransparentColor;
+
+		if (frame.width == 0 || frame.height == 0 || frame.data == nullptr)
+			continue;
+
+		const uint32 pixels = frame.width * frame.height;
+		_chars[i].src = decodedPtr;
+		decodedPtr += pixels;
+		_fontHeight = MAX<int>(_fontHeight, frame.height);
+
+		memset(_chars[i].src, kDefaultTransparentColor, pixels);
+		decodeRebel2Frame(_chars[i].src, frame, codec45Palette, codec45Lookup);
+	}
+
+	debug(1, "Rebel2NutRenderer::loadRebel2SpriteFromData() - numChars=%d decodedLength=%u", _numChars, (uint32)decodedLength);
+}
+
 NutRenderer *makeRebel2Font(ScummEngine *vm, const char *filename) {
 	return new Rebel2NutRenderer(vm, filename);
 }
 
+NutRenderer *makeRebel2SpriteFromData(ScummEngine *vm, const byte *data, int32 dataSize) {
+	Rebel2NutRenderer *renderer = new Rebel2NutRenderer(vm, data, dataSize);
+	if (renderer->getNumChars() <= 0) {
+		delete renderer;
+		return nullptr;
+	}
+
+	return renderer;
+}
+
 Rebel2FontSet::Rebel2FontSet() : numFonts(0), defaultFont(0) {
 	memset(fonts, 0, sizeof(fonts));
 }
diff --git a/engines/scumm/smush/rebel/font_rebel2.h b/engines/scumm/smush/rebel/font_rebel2.h
index 2a5ae2f7ae7..1276e1c13a3 100644
--- a/engines/scumm/smush/rebel/font_rebel2.h
+++ b/engines/scumm/smush/rebel/font_rebel2.h
@@ -46,6 +46,7 @@ struct Rebel2FontSet {
 };
 
 NutRenderer *makeRebel2Font(ScummEngine *vm, const char *filename);
+NutRenderer *makeRebel2SpriteFromData(ScummEngine *vm, const byte *data, int32 dataSize);
 int drawRebel2Char(NutRenderer *font, byte *buffer, Common::Rect &clipRect, int x, int y,
 		int pitch, int16 col, byte chr);
 int getRebel2StringWidth(const Rebel2FontSet &fontSet, const char *str, uint len);


Commit: 084f0d6376b59d2d8aaf55b1f6390d88cbcc30a2
    https://github.com/scummvm/scummvm/commit/084f0d6376b59d2d8aaf55b1f6390d88cbcc30a2
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-06T20:56:53+02:00

Commit Message:
SCUMM: RA2: reduce oscilation when using the mouse as input

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


diff --git a/engines/scumm/insane/rebel2/iact.cpp b/engines/scumm/insane/rebel2/iact.cpp
index bbde2d14589..8572dd26a67 100644
--- a/engines/scumm/insane/rebel2/iact.cpp
+++ b/engines/scumm/insane/rebel2/iact.cpp
@@ -36,6 +36,7 @@ namespace Scumm {
 
 const int kRA2Handler7DirectInputNumerator = 4;
 const int kRA2Handler7DirectInputDenominator = 5;
+const int kRA2Handler7MouseTargetRangeX = 0xc0;
 
 static bool readLevel2BackgroundChunkHeader(Common::SeekableReadStream &stream, int64 containerEnd, const char *context,
 		uint32 &tag, uint32 &chunkSize, int64 &dataEnd, int64 &nextChunkPos) {
@@ -860,6 +861,12 @@ void InsaneRebel2::handleOpcode6Handler7(Common::SeekableReadStream &b, int16 pa
 		kRA2Handler7DirectInputDenominator);
 	scaledInputY = (int16)((scaledInputY * kRA2Handler7DirectInputNumerator) /
 		kRA2Handler7DirectInputDenominator);
+	const bool useMouseFlightTarget = !_gamepadAimActive;
+	int16 mouseFlightTargetX = _flyShipScreenX;
+	if (useMouseFlightTarget) {
+		mouseFlightTargetX = (int16)(0xd4 + (scaledInputX * kRA2Handler7MouseTargetRangeX) / 127);
+		mouseFlightTargetX = CLIP<int16>(mouseFlightTargetX, 0x14, 0x194);
+	}
 
 	// Step 3: Velocity history + smoothed average (lines 141-157).
 	for (int i = 24; i > 0; i--) {
@@ -920,6 +927,16 @@ void InsaneRebel2::handleOpcode6Handler7(Common::SeekableReadStream &b, int16 pa
 	if (positionDeltaX > 11)
 		positionDeltaX = 12;
 
+	// Retail integrates relative flight axes. ScummVM's real mouse is an
+	// absolute position, so steer toward a bounded position target instead of
+	// letting a held off-center cursor keep pushing the ship until it bounces.
+	if (useMouseFlightTarget) {
+		int targetDeltaX = mouseFlightTargetX - _flyShipScreenX;
+		positionDeltaX = (int16)CLIP<int>(targetDeltaX / 4, -12, 12);
+		if (positionDeltaX == 0 && targetDeltaX != 0)
+			positionDeltaX = (targetDeltaX < 0) ? -1 : 1;
+	}
+
 	// Apply X delta (line 193 / 237).
 	_flyShipScreenX += positionDeltaX;
 
@@ -1076,11 +1093,12 @@ void InsaneRebel2::handleOpcode6Handler7(Common::SeekableReadStream &b, int16 pa
 		((_vm->VAR(_vm->VAR_LEFTBTN_HOLD) != 0) ||
 		 _vm->getActionState(kScummActionInsaneAttack));
 
-	debugC(DEBUG_INSANE, "Rebel2 H7: mouse=(%d,%d) raw=(%d,%d) scaled=(%d,%d) pos=(%d,%d) "
-		"vel=%d vIn=%d dx=%d dir=%d mode=%d",
+	debugC(DEBUG_INSANE, "Rebel2 H7: mouse=(%d,%d) raw=(%d,%d) scaled=(%d,%d) targetX=%d pos=(%d,%d) "
+		"vel=%d vIn=%d dx=%d dir=%d mode=%d mouseTarget=%d",
 		mouseX, mouseY, inputX, inputY, scaledInputX, scaledInputY,
-		_flyShipScreenX, _flyShipScreenY, _smoothedVelocity,
-		_verticalInput, positionDeltaX, _shipDirectionIndex, _flyControlMode);
+		mouseFlightTargetX, _flyShipScreenX, _flyShipScreenY, _smoothedVelocity,
+		_verticalInput, positionDeltaX, _shipDirectionIndex, _flyControlMode,
+		useMouseFlightTarget ? 1 : 0);
 }
 
 // ScummVM refactor helper for opcode 6 Handler 25, not a separate retail function.



From noreply at scummvm.org  Sat Jun  6 22:05:53 2026
From: noreply at scummvm.org (mduggan)
Date: Sat,  6 Jun 2026 22:05:53 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 b320897787316d387e12eebb26d8cf5bb9e0b5de
Message-ID: <20260606220553.9CCFB7005CC@ron.scummvm.net>

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:
b8b6bca7cd ACCESS: Make video destructors explicit PVS-Studio 1053
8a219cf382 ACCESS: Add detection enum values for other games
b320897787 ACCESS: Remove impossible branch PVS-Studio V547


Commit: b8b6bca7cd555fecdb8e54456b012a307c50b82a
    https://github.com/scummvm/scummvm/commit/b8b6bca7cd555fecdb8e54456b012a307c50b82a
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2026-06-07T07:40:40+10:00

Commit Message:
ACCESS: Make video destructors explicit PVS-Studio 1053

Changed paths:
    engines/access/video.cpp
    engines/access/video.h


diff --git a/engines/access/video.cpp b/engines/access/video.cpp
index 343774a4185..56979a14830 100644
--- a/engines/access/video.cpp
+++ b/engines/access/video.cpp
@@ -32,7 +32,6 @@ _vidSurface(nullptr) {
 }
 
 VideoPlayer::~VideoPlayer() {
-	closeVideo();
 }
 
 void VideoPlayer::setVideo(BaseSurface *vidSurface, const Common::Point &pt, const Common::Path &filename, int rate) {
@@ -91,6 +90,9 @@ VideoPlayer_v1::VideoPlayer_v1(AccessEngine *vm) : VideoPlayer(vm) {
 	_header._flags = VIDEOFLAG_NONE;
 }
 
+VideoPlayer_v1::~VideoPlayer_v1() {
+	VideoPlayer_v1::closeVideo();
+}
 
 void VideoPlayer_v1::setVideo(const Common::Point &pt) {
 	_vidSurface->_orgX1 = pt.x;
@@ -253,6 +255,10 @@ _frame(nullptr), _nextFrameTime(0), _setPal(setPal), _startMs(0), _drawBorder(fa
 	_header._flags = VIDEOFLAG_NONE;
 }
 
+VideoPlayer_v2::~VideoPlayer_v2() {
+	VideoPlayer_v2::closeVideo();
+}
+
 void VideoPlayer_v2::setVideo(const Common::Point &pt) {
 	_header._id = _videoData->_stream->readUint32LE();
 	_header._version = _videoData->_stream->readByte();
diff --git a/engines/access/video.h b/engines/access/video.h
index 11afc220fb4..d3b92e7657c 100644
--- a/engines/access/video.h
+++ b/engines/access/video.h
@@ -111,6 +111,8 @@ protected:
 public:
 	VideoPlayer_v1(AccessEngine *vm);
 
+	~VideoPlayer_v1();
+
 	void playVideo() override;
 
 	void copyVideo() override;
@@ -160,6 +162,8 @@ protected:
 public:
 	VideoPlayer_v2(AccessEngine *vm, bool setPal = false);
 
+	~VideoPlayer_v2();
+
 	void playVideo() override;
 
 	void copyVideo() override {};


Commit: 8a219cf382a6c2c020e5b0731c6a0033f87fbfae
    https://github.com/scummvm/scummvm/commit/8a219cf382a6c2c020e5b0731c6a0033f87fbfae
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2026-06-07T07:40:43+10:00

Commit Message:
ACCESS: Add detection enum values for other games

We don't have table entries or support for these games yet, but it would be
nice to add support in the future.  Add some placeholder enum values for now.

Changed paths:
    engines/access/detection.h


diff --git a/engines/access/detection.h b/engines/access/detection.h
index 6c9f93aa63f..5857932b478 100644
--- a/engines/access/detection.h
+++ b/engines/access/detection.h
@@ -29,7 +29,9 @@ namespace Access {
 enum AccessGameType {
 	kGameAmazon = 1,
 	kGameMartianMemorandum = 2,
-	kGameNoctropolis = 3
+	kGameNoctropolis = 3,
+	kGameCountdown = 4,
+	kGameSynnergist = 5,
 };
 
 struct AccessGameDescription {


Commit: b320897787316d387e12eebb26d8cf5bb9e0b5de
    https://github.com/scummvm/scummvm/commit/b320897787316d387e12eebb26d8cf5bb9e0b5de
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2026-06-07T08:00:15+10:00

Commit Message:
ACCESS: Remove impossible branch PVS-Studio V547

This time read was added initially using the original header in PR7324, but
that PR was then updated to use the standard header format, so there should be
no saves in the wild with an older header but version > 1.  Even if there were
they would be rejected by the version check on line 617.

Changed paths:
    engines/access/access.cpp


diff --git a/engines/access/access.cpp b/engines/access/access.cpp
index 4cfe81b9beb..9fb4964b70a 100644
--- a/engines/access/access.cpp
+++ b/engines/access/access.cpp
@@ -635,10 +635,6 @@ bool AccessEngine::readSavegameHeader(Common::InSaveFile *in, AccessSavegameHead
 	// Read Totalframes
 	header._totalFrames = in->readUint32LE();
 
-	// Read the Total PlayTime (if available)
-	if (header._version > 1)
-		header._totalPlayTime = in->readUint32LE();
-
 	return true;
 }
 



From noreply at scummvm.org  Sat Jun  6 22:22:54 2026
From: noreply at scummvm.org (dreammaster)
Date: Sat,  6 Jun 2026 22:22:54 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 6878d3b44d85b9c0c5459150487c5b303f0219ce
Message-ID: <20260606222254.F3DF37005CC@ron.scummvm.net>

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:
3a3d38bc9f MADS: FOREST: Implemented room 201
8d948d35db MADS: FOREST: Cleanup Scratch structs for pending rooms
6878d3b44d MADS: FOREST: Implemented room 203


Commit: 3a3d38bc9f82e6f7ea3ff233a0fe6e96a35bdfa5
    https://github.com/scummvm/scummvm/commit/3a3d38bc9f82e6f7ea3ff233a0fe6e96a35bdfa5
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-06-07T08:21:16+10:00

Commit Message:
MADS: FOREST: Implemented room 201

Changed paths:
    engines/mads/madsv2/forest/rooms/room201.cpp


diff --git a/engines/mads/madsv2/forest/rooms/room201.cpp b/engines/mads/madsv2/forest/rooms/room201.cpp
index 9fc82b87e5f..c34633d1139 100644
--- a/engines/mads/madsv2/forest/rooms/room201.cpp
+++ b/engines/mads/madsv2/forest/rooms/room201.cpp
@@ -1,4 +1,4 @@
-???/* ScummVM - Graphic Adventure Engine
+/* 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
@@ -19,18 +19,15 @@
  *
  */
 
-#include "mads/madsv2/core/conv.h"
+#include "mads/madsv2/core/digi.h"
 #include "mads/madsv2/core/game.h"
-#include "mads/madsv2/core/imath.h"
 #include "mads/madsv2/core/inter.h"
 #include "mads/madsv2/core/kernel.h"
-#include "mads/madsv2/core/sound.h"
-#include "mads/madsv2/core/text.h"
-#include "mads/madsv2/forest/mads/inventory.h"
-#include "mads/madsv2/forest/mads/sounds.h"
-#include "mads/madsv2/forest/mads/words.h"
+#include "mads/madsv2/core/object.h"
+#include "mads/madsv2/core/player.h"
 #include "mads/madsv2/forest/global.h"
-#include "mads/madsv2/forest/rooms/section1.h"
+#include "mads/madsv2/forest/journal.h"
+#include "mads/madsv2/forest/rooms/section2.h"
 #include "mads/madsv2/forest/rooms/room201.h"
 
 namespace MADS {
@@ -39,50 +36,663 @@ namespace Forest {
 namespace Rooms {
 
 struct Scratch {
-	int16 sprite[15];       /* Sprite series handles */
-	int16 sequence[15];     /* Sequence handles      */
-	int16 animation[4];     /* Animation handles     */
-
-	int16 dragon_frame;     /* frame animation is on */
-
-	int16 done_with_conv;   /* T if done with conv   */
-	int16 prev_room;
+	int16 sprite[10];                /* 0x00 ??? sprite series handles  */
+	int16 sequence[10];              /* 0x14 ??? sequence handles       */
+	int16 animation[10];             /* 0x28 ??? animation handles      */
+	AnimationInfo animation_info[10];/* 0x3C */
+	int16 _8c;                       /* 0x8C */
+	int16 _8e;                       /* 0x8E */
+	int16 _pad90;                    /* 0x90 */
+	int16 _92;                       /* 0x92 */
+	int16 _94;                       /* 0x94 ??? animation slot index for 'R' anim */
+	int16 _96;                       /* 0x96 ??? animation slot index for 'F' anim */
+	int16 _98;                       /* 0x98 */
+	int16 _9a;                       /* 0x9A ??? animation handle */
+	int16 _9c;                       /* 0x9C ??? animation handle */
+	int16 _pad9e;                    /* 0x9E */
+	int16 _a0;                       /* 0xA0 ??? talk animation handle */
+	int16 _a2;                       /* 0xA2 ??? animation handle for random-frame walk */
+	int16 _a4;                       /* 0xA4 */
+	int16 _a6;                       /* 0xA6 */
+	int16 _a8;                       /* 0xA8 */
+	int16 _aa;                       /* 0xAA ??? cached frame for anim5 */
+	int16 _ac;                       /* 0xAC */
+	int16 _ae;                       /* 0xAE ??? cached frame for anim6 */
+	int16 _b0;                       /* 0xB0 */
 };
 
 static Scratch scratch;
 
-#define local (&scratch)
-#define ss    local->sprite
-#define seq   local->sequence
-#define aa    local->animation
+#define local  (&scratch)
+#define ss     local->sprite
+#define seq    local->sequence
+#define aa     local->animation
+#define aainfo scratch.animation_info
+
+
+static void room_201_init1() {
+	global[player_score] = -1;
+	global[g009] = -1;
+	global_digi_play(11);
+	global[g131] = 0;
+	global[g141] = 0;
+
+	if (object_is_here(12)) {
+		ss[0] = kernel_load_series(kernel_name('p', 2), 0);
+		seq[0] = kernel_seq_stamp(ss[0], false, KERNEL_FIRST);
+		kernel_seq_depth(seq[0], 2);
+		kernel_seq_loc(seq[0], 204, 153);
+		kernel_seq_scale(seq[0], 100);
+	} else {
+		kernel_flip_hotspot(93, false);
+	}
+
+	if (object_is_here(7)) {
+		ss[1] = kernel_load_series(kernel_name('p', 1), 0);
+		seq[1] = kernel_seq_stamp(ss[1], false, KERNEL_FIRST);
+		kernel_seq_depth(seq[1], 10);
+		kernel_seq_loc(seq[1], 46, 103);
+		kernel_seq_scale(seq[1], 69);
+	} else {
+		kernel_flip_hotspot(145, false);
+	}
+
+	scratch._9c = kernel_run_animation_disp('r', 1, 0);
+	kernel_position_anim(scratch._9c, 208, 116, 77, 7);
+	scratch._9a = kernel_run_animation_disp('e', 1, 0);
+	kernel_position_anim(scratch._9a, 186, 115, 76, 7);
+
+	if (previous_room != KERNEL_RESTORING_GAME) {
+		player.x = 155;
+		player.y = 115;
+		player.facing = 3;
+	}
+
+	switch (previous_room) {
+	case KERNEL_RESTORING_GAME:
+		global[g131] = -1;
+		global[g141] = -1;
+		kernel_reset_animation(scratch._9a, 2);
+		kernel_reset_animation(scratch._9c, 2);
+		global[g133] = 0;
+		global[g143] = 0;
+		return;
+	case 106:
+		aa[0] = kernel_run_animation(kernel_name('y', 1), 100);
+		aainfo[0]._val1 = -1;
+		scratch._92 = 65;
+		return;
+	case 203:
+		aa[0] = kernel_run_animation(kernel_name('y', 2), 100);
+		aainfo[0]._val1 = -1;
+		scratch._92 = 75;
+		return;
+	case 199:
+		global[g131] = -1;
+		global[g141] = -1;
+		kernel_reset_animation(scratch._9a, 2);
+		kernel_reset_animation(scratch._9c, 2);
+		global[g133] = 0;
+		global[g143] = 0;
+		restore_player();
+		player.commands_allowed = -1;
+		player.walker_visible = -1;
+		return;
+	default:
+		global[g131] = -1;
+		global[g141] = -1;
+		kernel_reset_animation(scratch._9a, 2);
+		kernel_reset_animation(scratch._9c, 2);
+		global[g133] = 0;
+		global[g143] = 0;
+		player.commands_allowed = -1;
+		player.walker_visible = -1;
+		return;
+	}
+}
+
+static void room_201_anim1() {
+	if (kernel_anim[aa[0]].frame != aainfo[0]._val2)
+		aainfo[0]._val2 = kernel_anim[aa[0]].frame;
+
+	if (global[player_hyperwalked] != -1)
+		return;
+
+	aainfo[0]._val2 = scratch._92 - 1;
+	kernel_reset_animation(aa[0], scratch._92 - 1);
+}
+
+static void room_201_anim2() {
+	if (kernel_anim[aa[0]].frame != aainfo[0]._val2)
+		aainfo[0]._val2 = kernel_anim[aa[0]].frame;
+
+	if (scratch._a4 != 0)
+		kernel_random_frame(scratch._a2, &global[g153], global[g156]);
+
+	if (scratch._a4 == 0)
+		return;
+
+	if (kernel.trigger != 7 && kernel.trigger != 28)
+		return;
+
+	switch (scratch._a4) {
+	case 1:
+		global[g156] = 0;
+		kernel_timing_trigger(10, 28);
+		scratch._a4 = 2;
+		return;
+	case 2:
+		kernel_abort_animation(scratch._a2);
+		scratch._a4 = -1;
+		player.commands_allowed = -1;
+		player.walker_visible = -1;
+		aainfo[1]._val1 = 0;
+		kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
+		global[g131] = -1;
+		kernel_reset_animation(scratch._9a, 1);
+		kernel_synch(KERNEL_ANIM, scratch._9a, KERNEL_NOW, 0);
+		global[g133] = 0;
+		global[g143] = 0;
+		global[g017] = -1;
+		return;
+	default:
+		return;
+	}
+}
+
+static void room_201_anim3() {
+	if (kernel_anim[aa[3]].frame != aainfo[3]._val2)
+		aainfo[3]._val2 = kernel_anim[aa[3]].frame;
 
+	if (global[player_hyperwalked] == -1 && aainfo[3]._val2 <= 23) {
+		aainfo[3]._val2 = 23;
+		kernel_reset_animation(aa[3], 23);
+		kernel_synch(KERNEL_ANIM, aa[3], KERNEL_NOW, 0);
+		return;
+	}
+
+	if (global[player_hyperwalked] == -1 || aainfo[3]._val2 == 45) {
+		aainfo[3]._val2 = 44;
+		kernel_reset_animation(aa[3], 44);
+		kernel_synch(KERNEL_ANIM, aa[3], KERNEL_NOW, 0);
+	}
+}
+
+static void room_201_anim4() {
+	if (kernel_anim[aa[4]].frame != aainfo[4]._val2) {
+		aainfo[4]._val2 = kernel_anim[aa[4]].frame;
+		int16 frame = aainfo[4]._val2;
+
+		if (scratch._a8 == 1) {
+			if (frame == 10) {
+				if (aainfo[4]._val3 == 10) {
+					aainfo[4]._val2 = 9;
+					kernel_reset_animation(aa[4], 9);
+				}
+			} else if (frame <= 10) {
+				if (frame == 4) {
+					aainfo[4]._val3 = 9;
+					digi_play_build_ii('e', 2, 1);
+					scratch._a4 = 28;
+				} else if (frame == 8) {
+					if (aainfo[4]._val3 == 9) {
+						aainfo[4]._val2 = 4;
+						kernel_reset_animation(aa[4], 4);
+					}
+				}
+			}
+		} else if (scratch._a8 == 2) {
+			if (frame == 13) {
+				if (aainfo[4]._val3 == 10) {
+					aainfo[4]._val2 = 11;
+					kernel_reset_animation(aa[4], 11);
+				}
+			} else if (frame <= 13) {
+				if (frame == 5) {
+					aainfo[4]._val3 = 9;
+					digi_play_build_ii('e', 4, 1);
+					scratch._a4 = 450;
+				} else if (frame == 10) {
+					if (aainfo[4]._val3 == 9) {
+						aainfo[4]._val2 = 5;
+						kernel_reset_animation(aa[4], 5);
+					}
+				}
+			}
+		}
+	}
+
+	if (kernel.trigger != 7 && kernel.trigger != 28)
+		return;
+
+	switch (scratch._a4) {
+	case 28:
+		kernel_timing_trigger(30, 28);
+		aainfo[4]._val3 = 10;
+		scratch._a4 = 29;
+		return;
+	case 29:
+		aainfo[4]._val3 = 9;
+		aainfo[4]._val2 = 4;
+		kernel_reset_animation(aa[4], 4);
+		digi_play_build_ii('e', 10, 1);
+		scratch._a4 = 30;
+		return;
+	case 30:
+		aainfo[4]._val3 = 11;
+		aainfo[4]._val2 = 9;
+		kernel_reset_animation(aa[4], 9);
+		scratch._a4 = -1;
+		return;
+	case 450:
+		kernel_timing_trigger(30, 28);
+		aainfo[4]._val3 = 10;
+		scratch._a4 = 451;
+		return;
+	case 451:
+		aainfo[4]._val3 = 9;
+		aainfo[4]._val2 = 5;
+		kernel_reset_animation(aa[4], 5);
+		digi_play_build_ii('e', 10, 1);
+		scratch._a4 = 452;
+		return;
+	case 452:
+		aainfo[4]._val3 = 11;
+		aainfo[4]._val2 = 10;
+		kernel_reset_animation(aa[4], 10);
+		scratch._a4 = -1;
+		return;
+	default:
+		return;
+	}
+}
+
+static void room_201_anim5() {
+	if (kernel_anim[aa[3]].frame == scratch._aa)
+		return;
+
+	int16 var_2 = -1;
+	scratch._aa = kernel_anim[aa[3]].frame;
+
+	if (scratch._aa == 23)
+		digi_play_build(101, '_', 1, 2);
+
+	if (var_2 >= 0) {
+		kernel_reset_animation(aa[3], var_2);
+		scratch._aa = var_2;
+	}
+}
+
+static void room_201_anim6() {
+	if (kernel_anim[aa[3]].frame == scratch._ae)
+		return;
+
+	int16 var_2 = -1;
+	scratch._ae = kernel_anim[aa[3]].frame;
+
+	if (scratch._ae == 40)
+		digi_play_build(101, '_', 1, 2);
+
+	if (var_2 >= 0) {
+		kernel_reset_animation(aa[3], var_2);
+		scratch._ae = var_2;
+	}
+}
 
 static void room_201_init() {
+	scratch._a6 = 0;
+	scratch._a4 = -1;
+
+	if (previous_room != KERNEL_RESTORING_GAME && previous_room != 199) {
+		player.walker_visible = 0;
+		player.commands_allowed = 0;
+	}
+
+	scratch._ac = 0;
+	scratch._b0 = 0;
+	for (int i = 0; i < 10; i++) {
+		aainfo[i]._val1 = 0;
+		aainfo[i]._val2 = 1;
+		aainfo[i]._val3 = 0;
+		aainfo[i]._val4 = 0;
+	}
+
+	if (previous_room != 199 && flags[7] != 3)
+		flags[7]++;
+
+	if (flags[7] == -3)
+		flags[7] = 1;
+	room_201_init1();
 }
 
 static void room_201_daemon() {
+	int16 trigger = kernel.trigger;
+
+	if (trigger == 106) {
+		kernel_abort_animation(aa[3]);
+		kernel_reset_animation(scratch._9c, 1);
+		scratch._b0 = 0;
+		kernel_synch(KERNEL_ANIM, scratch._9c, KERNEL_NOW, 0);
+		global[g143] = 0;
+		kernel_abort_animation(scratch._a2);
+		scratch._a6 = 0;
+		scratch._a4 = -1;
+		kernel_reset_animation(scratch._9a, 1);
+		kernel_synch(KERNEL_ANIM, scratch._9a, KERNEL_NOW, 0);
+		global[g133] = 0;
+		kernel_flip_hotspot(145, false);
+		inter_move_object(7, 2);
+		player.commands_allowed = -1;
+	} else if (trigger == 26) {
+		if (scratch._a8 == 1) {
+			kernel_reset_animation(scratch._9a, 0);
+			global[g133] = 1;
+			scratch._a2 = kernel_run_animation_talk('e', 2, 0);
+			kernel_position_anim(scratch._a2, 186, 115, 76, 7);
+			global[g156] = 2;
+			kernel_synch(KERNEL_ANIM, scratch._a2, KERNEL_ANIM, scratch._9a);
+			digi_play_build(201, 'E', 2, 1);
+			scratch._a4 = 1000;
+			scratch._a6 = -1;
+		} else if (scratch._a8 == 2) {
+			kernel_reset_animation(scratch._9a, 0);
+			global[g133] = 1;
+			scratch._a2 = kernel_run_animation_talk('e', 1, 0);
+			kernel_position_anim(scratch._a2, 186, 115, 76, 7);
+			global[g156] = 2;
+			kernel_synch(KERNEL_ANIM, scratch._a2, KERNEL_ANIM, scratch._9a);
+			digi_play_build(201, 'E', 3, 1);
+			scratch._a4 = 2000;
+			scratch._a6 = -1;
+		}
+	} else if (trigger > 26) {
+		if (trigger == 100) {
+			kernel_abort_animation(aa[0]);
+			aainfo[0]._val1 = 0;
+			if (flags[7] == 1) {
+				kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
+				global[g141] = -1;
+				global[g017] = 0;
+				global[g154] = 0;
+				global[g143] = 0;
+				kernel_reset_animation(scratch._9c, 1);
+				kernel_synch(KERNEL_ANIM, scratch._9c, KERNEL_NOW, 0);
+				kernel_synch(KERNEL_ANIM, scratch._9a, KERNEL_NOW, 0);
+				global[g156] = 2;
+				global[g131] = -1;
+				player.walker_visible = -1;
+				aainfo[1]._val1 = -1;
+				global[g133] = 1;
+				scratch._a2 = kernel_run_animation_talk('e', 1, 0);
+				kernel_position_anim(scratch._a2, 186, 115, 76, 7);
+				kernel_synch(KERNEL_ANIM, scratch._a2, KERNEL_ANIM, scratch._9a);
+				kernel_synch(KERNEL_ANIM, scratch._a0, KERNEL_PLAYER, 0);
+				digi_play_build(201, 'e', 1, 1);
+				scratch._a4 = 1;
+			} else {
+				global[g131] = -1;
+				global[g141] = -1;
+				kernel_reset_animation(scratch._9a, 1);
+				kernel_synch(KERNEL_ANIM, scratch._9a, KERNEL_NOW, 0);
+				kernel_reset_animation(scratch._9c, 1);
+				kernel_synch(KERNEL_ANIM, scratch._9c, KERNEL_NOW, 0);
+				player.walker_visible = -1;
+				global[g133] = 0;
+				global[g143] = 0;
+				kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
+				player.commands_allowed = -1;
+			}
+		} else if (trigger == 101) {
+			kernel_abort_animation(aa[scratch._94]);
+			aainfo[scratch._94]._val1 = 0;
+			kernel_reset_animation(scratch._9c, 1);
+			kernel_synch(KERNEL_ANIM, scratch._9c, KERNEL_NOW, 0);
+			global[g143] = 0;
+			player.commands_allowed = -1;
+		} else if (trigger == 102) {
+			kernel_abort_animation(aa[scratch._96]);
+			aainfo[scratch._96]._val1 = 0;
+			kernel_reset_animation(scratch._9a, 1);
+			kernel_synch(KERNEL_ANIM, scratch._9a, KERNEL_NOW, 0);
+			global[g133] = 0;
+			player.commands_allowed = -1;
+		} else if (trigger == 105) {
+			kernel_abort_animation(aa[3]);
+			kernel_reset_animation(scratch._9c, 1);
+			scratch._ac = 0;
+			kernel_synch(KERNEL_ANIM, scratch._9c, KERNEL_NOW, 0);
+			global[g143] = 0;
+			kernel_abort_animation(scratch._a2);
+			scratch._a6 = 0;
+			scratch._a4 = -1;
+			kernel_reset_animation(scratch._9a, 1);
+			kernel_synch(KERNEL_ANIM, scratch._9a, KERNEL_NOW, 0);
+			global[g133] = 0;
+			kernel_flip_hotspot(93, false);
+			inter_move_object(12, 2);
+			player.commands_allowed = -1;
+		}
+	} else if (trigger == 7) {
+		if (global[walker_converse_state] != 0) {
+			global[walker_converse_state] = 0;
+			close_journal(3);
+			player.commands_allowed = -1;
+		} else {
+			switch (scratch._a4) {
+			case 40:
+				kernel_abort_animation(scratch._a0);
+				scratch._a4 = -1;
+				scratch._a6 = 0;
+				kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
+				player.walker_visible = -1;
+				global[g150] = -1;
+				scratch._a8 = 1;
+				break;
+			case 41:
+				kernel_abort_animation(scratch._a0);
+				scratch._a4 = -1;
+				scratch._a6 = 0;
+				kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
+				player.walker_visible = -1;
+				global[g150] = -1;
+				scratch._a8 = 2;
+				break;
+			case 1000:
+				global[g156] = 0;
+				kernel_seq_delete(seq[0]);
+				kernel_reset_animation(scratch._9c, 0);
+				global[g143] = 1;
+				kernel_synch(KERNEL_ANIM, scratch._9c, KERNEL_NOW, 0);
+				aa[3] = kernel_run_animation(kernel_name('t', 1), 105);
+				scratch._ac = -1;
+				kernel_synch(KERNEL_ANIM, aa[3], KERNEL_NOW, 0);
+				break;
+			case 2000:
+				global[g156] = 0;
+				kernel_seq_delete(seq[1]);
+				kernel_reset_animation(scratch._9c, 0);
+				global[g143] = 1;
+				kernel_synch(KERNEL_ANIM, scratch._9c, KERNEL_NOW, 0);
+				aa[3] = kernel_run_animation(kernel_name('t', 2), 106);
+				scratch._b0 = -1;
+				kernel_synch(KERNEL_ANIM, aa[3], KERNEL_NOW, 0);
+				break;
+			default:
+				break;
+			}
+		}
+	} else if (trigger == 24) {
+		if (scratch._8c == 5) {
+			if (scratch._a8 == 1) {
+				aa[4] = kernel_run_animation(kernel_name('F', 1), 102);
+				aainfo[4]._val1 = -1;
+				scratch._96 = 4;
+				kernel_reset_animation(scratch._9a, 0);
+				kernel_synch(KERNEL_ANIM, aa[4], KERNEL_ANIM, scratch._9a);
+				global[g133] = 1;
+			} else if (scratch._a8 == 2) {
+				aa[4] = kernel_run_animation(kernel_name('F', 2), 102);
+				aainfo[4]._val1 = -1;
+				scratch._96 = 4;
+				kernel_reset_animation(scratch._9a, 0);
+				kernel_synch(KERNEL_ANIM, aa[4], KERNEL_ANIM, scratch._9a);
+				global[g133] = 1;
+			}
+		}
+	} else if (trigger == 25) {
+		if (scratch._8e == 2) {
+			aa[3] = kernel_run_animation(kernel_name('R', 1), 101);
+			aainfo[3]._val1 = -1;
+			scratch._94 = 3;
+			kernel_reset_animation(scratch._9c, 0);
+			kernel_synch(KERNEL_ANIM, aa[3], KERNEL_ANIM, scratch._9c);
+			global[g143] = 1;
+		}
+	}
+
+	if (scratch._a6 != 0) {
+		if (scratch._a4 == 40 || scratch._a4 == 41)
+			kernel_random_frame(scratch._a0, &global[g151], global[g154]);
+		else if (scratch._a4 == 1 || scratch._a4 == 2 || scratch._a4 == 1000 || scratch._a4 == 2000)
+			kernel_random_frame(scratch._a2, &global[g153], global[g156]);
+	}
+
+	global_anim1(1, scratch._9a, global[g131], &global[g132]);
+	global_anim2(1, scratch._9c, global[g141], &global[g142]);
+
+	if (aainfo[0]._val1 != 0) room_201_anim1();
+	if (aainfo[3]._val1 != 0) room_201_anim3();
+	if (aainfo[4]._val1 != 0) room_201_anim4();
+	if (aainfo[1]._val1 != 0) room_201_anim2();
+	if (scratch._ac != 0) room_201_anim5();
+	if (scratch._b0 != 0) room_201_anim6();
 }
 
 static void room_201_pre_parser() {
+	if (player_parse(13, 17, 0))
+		player.walk_off_edge_to_room = 106;
+	if (player_parse(13, 23, 0))
+		player.walk_off_edge_to_room = 203;
 }
 
 static void room_201_parser() {
+	if (global[walker_converse_state] != 0) {
+		player.commands_allowed = 0;
+		digi_play_build_ii('c', 1, 1);
+		player.command_ready = false;
+		return;
+	}
+
+	if (player_parse(93, 0)) {
+		global[g154] = 2;
+		player.commands_allowed = 0;
+		player.walker_visible = 0;
+		scratch._a0 = kernel_run_animation_talk(98, 3, 0);
+		kernel_position_anim(scratch._a0, player.x, player.y, player.scale, player.depth);
+		kernel_synch(KERNEL_ANIM, scratch._a0, KERNEL_PLAYER, 0);
+		digi_play_build_ii('b', 3, 1);
+		scratch._a4 = 40;
+		scratch._a6 = -1;
+		player.command_ready = false;
+		return;
+	}
+
+	if (player_parse(145, 0)) {
+		global[g154] = 2;
+		player.commands_allowed = 0;
+		player.walker_visible = 0;
+		scratch._a0 = kernel_run_animation_talk(98, 4, 0);
+		kernel_position_anim(scratch._a0, player.x, player.y, player.scale, player.depth);
+		kernel_synch(KERNEL_ANIM, scratch._a0, KERNEL_PLAYER, 0);
+		digi_play_build_ii('b', 3, 1);
+		scratch._a4 = 41;
+		scratch._a6 = -1;
+		player.command_ready = false;
+		return;
+	}
+
+	if (player_parse(114, 77, 0)) {
+		player.commands_allowed = 0;
+		global[g135] = -1;
+		scratch._8c = 5;
+		scratch._a8 = 1;
+		player.command_ready = false;
+		return;
+	}
+
+	if (player_parse(114, 103, 0)) {
+		player.commands_allowed = 0;
+		global[g135] = -1;
+		scratch._8c = 5;
+		scratch._a8 = 2;
+		player.command_ready = false;
+		return;
+	}
+
+	if (global[player_selected_object] >= 0) {
+		player.command_ready = false;
+		return;
+	}
+
+	if (player_parse(114, 116, 0)) {
+		player.commands_allowed = 0;
+		global[g145] = -1;
+		scratch._8e = 2;
+		player.command_ready = false;
+		return;
+	}
+
+	if (player_parse(99, 0)) {
+		player.commands_allowed = 0;
+		global[g135] = -1;
+		scratch._8c = 3;
+		player.command_ready = false;
+		return;
+	}
+
+	if (player_parse(78, 119, 0)) {
+		player.commands_allowed = 0;
+		global[g135] = -1;
+		scratch._8c = 3;
+		player.command_ready = false;
+		return;
+	}
 }
 
 void room_201_synchronize(Common::Serializer &s) {
 	for (int16 &v : scratch.sprite)    s.syncAsSint16LE(v);
 	for (int16 &v : scratch.sequence)  s.syncAsSint16LE(v);
 	for (int16 &v : scratch.animation) s.syncAsSint16LE(v);
+	for (AnimationInfo &ai : scratch.animation_info) ai.synchronize(s);
+	s.syncAsSint16LE(scratch._8c);
+	s.syncAsSint16LE(scratch._8e);
+	s.syncAsSint16LE(scratch._pad90);
+	s.syncAsSint16LE(scratch._92);
+	s.syncAsSint16LE(scratch._94);
+	s.syncAsSint16LE(scratch._96);
+	s.syncAsSint16LE(scratch._98);
+	s.syncAsSint16LE(scratch._9a);
+	s.syncAsSint16LE(scratch._9c);
+	s.syncAsSint16LE(scratch._pad9e);
+	s.syncAsSint16LE(scratch._a0);
+	s.syncAsSint16LE(scratch._a2);
+	s.syncAsSint16LE(scratch._a4);
+	s.syncAsSint16LE(scratch._a6);
+	s.syncAsSint16LE(scratch._a8);
+	s.syncAsSint16LE(scratch._aa);
+	s.syncAsSint16LE(scratch._ac);
+	s.syncAsSint16LE(scratch._ae);
+	s.syncAsSint16LE(scratch._b0);
 }
 
 void room_201_preload() {
-	room_init_code_pointer = room_201_init;
+	room_init_code_pointer       = room_201_init;
 	room_pre_parser_code_pointer = room_201_pre_parser;
-	room_parser_code_pointer = room_201_parser;
-	room_daemon_code_pointer = room_201_daemon;
-
-	section_1_walker();
-	section_1_interface();
+	room_parser_code_pointer     = room_201_parser;
+	room_daemon_code_pointer     = room_201_daemon;
+	section_2_walker();
+	section_2_interface();
+	player.walker_must_reload = true;
 }
 
 } // namespace Rooms


Commit: 8d948d35db2b0c4453458c3d2a8039ce5b6be477
    https://github.com/scummvm/scummvm/commit/8d948d35db2b0c4453458c3d2a8039ce5b6be477
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-06-07T08:21:17+10:00

Commit Message:
MADS: FOREST: Cleanup Scratch structs for pending rooms

Changed paths:
    engines/mads/madsv2/forest/rooms/room101.cpp
    engines/mads/madsv2/forest/rooms/room203.cpp
    engines/mads/madsv2/forest/rooms/room204.cpp
    engines/mads/madsv2/forest/rooms/room205.cpp
    engines/mads/madsv2/forest/rooms/room210.cpp
    engines/mads/madsv2/forest/rooms/room211.cpp
    engines/mads/madsv2/forest/rooms/room220.cpp
    engines/mads/madsv2/forest/rooms/room221.cpp
    engines/mads/madsv2/forest/rooms/room301.cpp
    engines/mads/madsv2/forest/rooms/room302.cpp
    engines/mads/madsv2/forest/rooms/room303.cpp
    engines/mads/madsv2/forest/rooms/room304.cpp
    engines/mads/madsv2/forest/rooms/room305.cpp
    engines/mads/madsv2/forest/rooms/room306.cpp
    engines/mads/madsv2/forest/rooms/room307.cpp
    engines/mads/madsv2/forest/rooms/room308.cpp
    engines/mads/madsv2/forest/rooms/room321.cpp
    engines/mads/madsv2/forest/rooms/room322.cpp
    engines/mads/madsv2/forest/rooms/room401.cpp
    engines/mads/madsv2/forest/rooms/room402.cpp
    engines/mads/madsv2/forest/rooms/room403.cpp
    engines/mads/madsv2/forest/rooms/room404.cpp
    engines/mads/madsv2/forest/rooms/room405.cpp
    engines/mads/madsv2/forest/rooms/room420.cpp
    engines/mads/madsv2/forest/rooms/room501.cpp
    engines/mads/madsv2/forest/rooms/room503.cpp
    engines/mads/madsv2/forest/rooms/room509.cpp
    engines/mads/madsv2/forest/rooms/room510.cpp
    engines/mads/madsv2/forest/rooms/room520.cpp
    engines/mads/madsv2/forest/rooms/room903.cpp


diff --git a/engines/mads/madsv2/forest/rooms/room101.cpp b/engines/mads/madsv2/forest/rooms/room101.cpp
index 156824d8b41..e37b7788ccc 100644
--- a/engines/mads/madsv2/forest/rooms/room101.cpp
+++ b/engines/mads/madsv2/forest/rooms/room101.cpp
@@ -77,6 +77,7 @@ static Scratch scratch;
 #define aa     scratch.animation
 #define aainfo scratch.animation_info
 
+
 static void room_101_init1() {
 	global[g009] = -1;
 	viewing_at_y = 22;
diff --git a/engines/mads/madsv2/forest/rooms/room203.cpp b/engines/mads/madsv2/forest/rooms/room203.cpp
index 7dba244347d..e4dd299ad51 100644
--- a/engines/mads/madsv2/forest/rooms/room203.cpp
+++ b/engines/mads/madsv2/forest/rooms/room203.cpp
@@ -39,14 +39,10 @@ namespace Forest {
 namespace Rooms {
 
 struct Scratch {
-	int16 sprite[15];       /* Sprite series handles */
-	int16 sequence[15];     /* Sequence handles      */
-	int16 animation[4];     /* Animation handles     */
-
-	int16 dragon_frame;     /* frame animation is on */
-
-	int16 done_with_conv;   /* T if done with conv   */
-	int16 prev_room;
+	int16 sprite[10];       /* Sprite series handles */
+	int16 sequence[10];     /* Sequence handles      */
+	int16 animation[10];     /* Animation handles     */
+	AnimationInfo animation_info[10];
 };
 
 static Scratch scratch;
@@ -55,7 +51,7 @@ static Scratch scratch;
 #define ss    local->sprite
 #define seq   local->sequence
 #define aa    local->animation
-
+#define aainfo scratch.animation_info
 
 static void room_203_init() {
 }
diff --git a/engines/mads/madsv2/forest/rooms/room204.cpp b/engines/mads/madsv2/forest/rooms/room204.cpp
index a3265ab6f87..8ab1e7fbcd2 100644
--- a/engines/mads/madsv2/forest/rooms/room204.cpp
+++ b/engines/mads/madsv2/forest/rooms/room204.cpp
@@ -39,14 +39,10 @@ namespace Forest {
 namespace Rooms {
 
 struct Scratch {
-	int16 sprite[15];       /* Sprite series handles */
-	int16 sequence[15];     /* Sequence handles      */
-	int16 animation[4];     /* Animation handles     */
-
-	int16 dragon_frame;     /* frame animation is on */
-
-	int16 done_with_conv;   /* T if done with conv   */
-	int16 prev_room;
+	int16 sprite[10];       /* Sprite series handles */
+	int16 sequence[10];     /* Sequence handles      */
+	int16 animation[10];     /* Animation handles     */
+	AnimationInfo animation_info[10];
 };
 
 static Scratch scratch;
@@ -55,6 +51,7 @@ static Scratch scratch;
 #define ss    local->sprite
 #define seq   local->sequence
 #define aa    local->animation
+#define aainfo scratch.animation_info
 
 
 static void room_204_init() {
diff --git a/engines/mads/madsv2/forest/rooms/room205.cpp b/engines/mads/madsv2/forest/rooms/room205.cpp
index f0ba29ce313..0da27750430 100644
--- a/engines/mads/madsv2/forest/rooms/room205.cpp
+++ b/engines/mads/madsv2/forest/rooms/room205.cpp
@@ -39,14 +39,10 @@ namespace Forest {
 namespace Rooms {
 
 struct Scratch {
-	int16 sprite[15];       /* Sprite series handles */
-	int16 sequence[15];     /* Sequence handles      */
-	int16 animation[4];     /* Animation handles     */
-
-	int16 dragon_frame;     /* frame animation is on */
-
-	int16 done_with_conv;   /* T if done with conv   */
-	int16 prev_room;
+	int16 sprite[10];       /* Sprite series handles */
+	int16 sequence[10];     /* Sequence handles      */
+	int16 animation[10];     /* Animation handles     */
+	AnimationInfo animation_info[10];
 };
 
 static Scratch scratch;
@@ -55,6 +51,7 @@ static Scratch scratch;
 #define ss    local->sprite
 #define seq   local->sequence
 #define aa    local->animation
+#define aainfo scratch.animation_info
 
 
 static void room_205_init() {
diff --git a/engines/mads/madsv2/forest/rooms/room210.cpp b/engines/mads/madsv2/forest/rooms/room210.cpp
index ae17ec3faae..235c69f63df 100644
--- a/engines/mads/madsv2/forest/rooms/room210.cpp
+++ b/engines/mads/madsv2/forest/rooms/room210.cpp
@@ -39,14 +39,10 @@ namespace Forest {
 namespace Rooms {
 
 struct Scratch {
-	int16 sprite[15];       /* Sprite series handles */
-	int16 sequence[15];     /* Sequence handles      */
-	int16 animation[4];     /* Animation handles     */
-
-	int16 dragon_frame;     /* frame animation is on */
-
-	int16 done_with_conv;   /* T if done with conv   */
-	int16 prev_room;
+	int16 sprite[10];       /* Sprite series handles */
+	int16 sequence[10];     /* Sequence handles      */
+	int16 animation[10];     /* Animation handles     */
+	AnimationInfo animation_info[10];
 };
 
 static Scratch scratch;
@@ -55,6 +51,7 @@ static Scratch scratch;
 #define ss    local->sprite
 #define seq   local->sequence
 #define aa    local->animation
+#define aainfo scratch.animation_info
 
 
 static void room_210_init() {
diff --git a/engines/mads/madsv2/forest/rooms/room211.cpp b/engines/mads/madsv2/forest/rooms/room211.cpp
index 8173536b2dd..69b91185a8d 100644
--- a/engines/mads/madsv2/forest/rooms/room211.cpp
+++ b/engines/mads/madsv2/forest/rooms/room211.cpp
@@ -39,14 +39,10 @@ namespace Forest {
 namespace Rooms {
 
 struct Scratch {
-	int16 sprite[15];       /* Sprite series handles */
-	int16 sequence[15];     /* Sequence handles      */
-	int16 animation[4];     /* Animation handles     */
-
-	int16 dragon_frame;     /* frame animation is on */
-
-	int16 done_with_conv;   /* T if done with conv   */
-	int16 prev_room;
+	int16 sprite[10];       /* Sprite series handles */
+	int16 sequence[10];     /* Sequence handles      */
+	int16 animation[10];     /* Animation handles     */
+	AnimationInfo animation_info[10];
 };
 
 static Scratch scratch;
@@ -55,6 +51,7 @@ static Scratch scratch;
 #define ss    local->sprite
 #define seq   local->sequence
 #define aa    local->animation
+#define aainfo scratch.animation_info
 
 
 static void room_211_init() {
diff --git a/engines/mads/madsv2/forest/rooms/room220.cpp b/engines/mads/madsv2/forest/rooms/room220.cpp
index 83679c727bc..643c4b96747 100644
--- a/engines/mads/madsv2/forest/rooms/room220.cpp
+++ b/engines/mads/madsv2/forest/rooms/room220.cpp
@@ -39,14 +39,10 @@ namespace Forest {
 namespace Rooms {
 
 struct Scratch {
-	int16 sprite[15];       /* Sprite series handles */
-	int16 sequence[15];     /* Sequence handles      */
-	int16 animation[4];     /* Animation handles     */
-
-	int16 dragon_frame;     /* frame animation is on */
-
-	int16 done_with_conv;   /* T if done with conv   */
-	int16 prev_room;
+	int16 sprite[10];       /* Sprite series handles */
+	int16 sequence[10];     /* Sequence handles      */
+	int16 animation[10];     /* Animation handles     */
+	AnimationInfo animation_info[10];
 };
 
 static Scratch scratch;
@@ -55,6 +51,7 @@ static Scratch scratch;
 #define ss    local->sprite
 #define seq   local->sequence
 #define aa    local->animation
+#define aainfo scratch.animation_info
 
 
 static void room_220_init() {
diff --git a/engines/mads/madsv2/forest/rooms/room221.cpp b/engines/mads/madsv2/forest/rooms/room221.cpp
index 81bb0747d0a..74145af6669 100644
--- a/engines/mads/madsv2/forest/rooms/room221.cpp
+++ b/engines/mads/madsv2/forest/rooms/room221.cpp
@@ -39,14 +39,10 @@ namespace Forest {
 namespace Rooms {
 
 struct Scratch {
-	int16 sprite[15];       /* Sprite series handles */
-	int16 sequence[15];     /* Sequence handles      */
-	int16 animation[4];     /* Animation handles     */
-
-	int16 dragon_frame;     /* frame animation is on */
-
-	int16 done_with_conv;   /* T if done with conv   */
-	int16 prev_room;
+	int16 sprite[10];       /* Sprite series handles */
+	int16 sequence[10];     /* Sequence handles      */
+	int16 animation[10];     /* Animation handles     */
+	AnimationInfo animation_info[10];
 };
 
 static Scratch scratch;
@@ -55,6 +51,7 @@ static Scratch scratch;
 #define ss    local->sprite
 #define seq   local->sequence
 #define aa    local->animation
+#define aainfo scratch.animation_info
 
 
 static void room_221_init() {
diff --git a/engines/mads/madsv2/forest/rooms/room301.cpp b/engines/mads/madsv2/forest/rooms/room301.cpp
index bc879fa1355..85f0c82eade 100644
--- a/engines/mads/madsv2/forest/rooms/room301.cpp
+++ b/engines/mads/madsv2/forest/rooms/room301.cpp
@@ -39,14 +39,10 @@ namespace Forest {
 namespace Rooms {
 
 struct Scratch {
-	int16 sprite[15];       /* Sprite series handles */
-	int16 sequence[15];     /* Sequence handles      */
-	int16 animation[4];     /* Animation handles     */
-
-	int16 dragon_frame;     /* frame animation is on */
-
-	int16 done_with_conv;   /* T if done with conv   */
-	int16 prev_room;
+	int16 sprite[10];       /* Sprite series handles */
+	int16 sequence[10];     /* Sequence handles      */
+	int16 animation[10];     /* Animation handles     */
+	AnimationInfo animation_info[10];
 };
 
 static Scratch scratch;
@@ -55,6 +51,7 @@ static Scratch scratch;
 #define ss    local->sprite
 #define seq   local->sequence
 #define aa    local->animation
+#define aainfo scratch.animation_info
 
 
 static void room_301_init() {
diff --git a/engines/mads/madsv2/forest/rooms/room302.cpp b/engines/mads/madsv2/forest/rooms/room302.cpp
index fe16ebcf227..82a6e09a042 100644
--- a/engines/mads/madsv2/forest/rooms/room302.cpp
+++ b/engines/mads/madsv2/forest/rooms/room302.cpp
@@ -39,14 +39,10 @@ namespace Forest {
 namespace Rooms {
 
 struct Scratch {
-	int16 sprite[15];       /* Sprite series handles */
-	int16 sequence[15];     /* Sequence handles      */
-	int16 animation[4];     /* Animation handles     */
-
-	int16 dragon_frame;     /* frame animation is on */
-
-	int16 done_with_conv;   /* T if done with conv   */
-	int16 prev_room;
+	int16 sprite[10];       /* Sprite series handles */
+	int16 sequence[10];     /* Sequence handles      */
+	int16 animation[10];     /* Animation handles     */
+	AnimationInfo animation_info[10];
 };
 
 static Scratch scratch;
@@ -55,6 +51,7 @@ static Scratch scratch;
 #define ss    local->sprite
 #define seq   local->sequence
 #define aa    local->animation
+#define aainfo scratch.animation_info
 
 
 static void room_302_init() {
diff --git a/engines/mads/madsv2/forest/rooms/room303.cpp b/engines/mads/madsv2/forest/rooms/room303.cpp
index 6d476f668ff..abb32340a3c 100644
--- a/engines/mads/madsv2/forest/rooms/room303.cpp
+++ b/engines/mads/madsv2/forest/rooms/room303.cpp
@@ -39,14 +39,10 @@ namespace Forest {
 namespace Rooms {
 
 struct Scratch {
-	int16 sprite[15];       /* Sprite series handles */
-	int16 sequence[15];     /* Sequence handles      */
-	int16 animation[4];     /* Animation handles     */
-
-	int16 dragon_frame;     /* frame animation is on */
-
-	int16 done_with_conv;   /* T if done with conv   */
-	int16 prev_room;
+	int16 sprite[10];       /* Sprite series handles */
+	int16 sequence[10];     /* Sequence handles      */
+	int16 animation[10];     /* Animation handles     */
+	AnimationInfo animation_info[10];
 };
 
 static Scratch scratch;
@@ -55,6 +51,7 @@ static Scratch scratch;
 #define ss    local->sprite
 #define seq   local->sequence
 #define aa    local->animation
+#define aainfo scratch.animation_info
 
 
 static void room_303_init() {
diff --git a/engines/mads/madsv2/forest/rooms/room304.cpp b/engines/mads/madsv2/forest/rooms/room304.cpp
index 105c64b1714..0cbdeae59d8 100644
--- a/engines/mads/madsv2/forest/rooms/room304.cpp
+++ b/engines/mads/madsv2/forest/rooms/room304.cpp
@@ -39,14 +39,10 @@ namespace Forest {
 namespace Rooms {
 
 struct Scratch {
-	int16 sprite[15];       /* Sprite series handles */
-	int16 sequence[15];     /* Sequence handles      */
-	int16 animation[4];     /* Animation handles     */
-
-	int16 dragon_frame;     /* frame animation is on */
-
-	int16 done_with_conv;   /* T if done with conv   */
-	int16 prev_room;
+	int16 sprite[10];       /* Sprite series handles */
+	int16 sequence[10];     /* Sequence handles      */
+	int16 animation[10];     /* Animation handles     */
+	AnimationInfo animation_info[10];
 };
 
 static Scratch scratch;
@@ -55,6 +51,7 @@ static Scratch scratch;
 #define ss    local->sprite
 #define seq   local->sequence
 #define aa    local->animation
+#define aainfo scratch.animation_info
 
 
 static void room_304_init() {
diff --git a/engines/mads/madsv2/forest/rooms/room305.cpp b/engines/mads/madsv2/forest/rooms/room305.cpp
index b789bcd6474..a3369107f8d 100644
--- a/engines/mads/madsv2/forest/rooms/room305.cpp
+++ b/engines/mads/madsv2/forest/rooms/room305.cpp
@@ -39,14 +39,10 @@ namespace Forest {
 namespace Rooms {
 
 struct Scratch {
-	int16 sprite[15];       /* Sprite series handles */
-	int16 sequence[15];     /* Sequence handles      */
-	int16 animation[4];     /* Animation handles     */
-
-	int16 dragon_frame;     /* frame animation is on */
-
-	int16 done_with_conv;   /* T if done with conv   */
-	int16 prev_room;
+	int16 sprite[10];       /* Sprite series handles */
+	int16 sequence[10];     /* Sequence handles      */
+	int16 animation[10];     /* Animation handles     */
+	AnimationInfo animation_info[10];
 };
 
 static Scratch scratch;
@@ -55,6 +51,7 @@ static Scratch scratch;
 #define ss    local->sprite
 #define seq   local->sequence
 #define aa    local->animation
+#define aainfo scratch.animation_info
 
 
 static void room_305_init() {
diff --git a/engines/mads/madsv2/forest/rooms/room306.cpp b/engines/mads/madsv2/forest/rooms/room306.cpp
index 27fbacdc6cb..44c11f19ac9 100644
--- a/engines/mads/madsv2/forest/rooms/room306.cpp
+++ b/engines/mads/madsv2/forest/rooms/room306.cpp
@@ -39,14 +39,10 @@ namespace Forest {
 namespace Rooms {
 
 struct Scratch {
-	int16 sprite[15];       /* Sprite series handles */
-	int16 sequence[15];     /* Sequence handles      */
-	int16 animation[4];     /* Animation handles     */
-
-	int16 dragon_frame;     /* frame animation is on */
-
-	int16 done_with_conv;   /* T if done with conv   */
-	int16 prev_room;
+	int16 sprite[10];       /* Sprite series handles */
+	int16 sequence[10];     /* Sequence handles      */
+	int16 animation[10];     /* Animation handles     */
+	AnimationInfo animation_info[10];
 };
 
 static Scratch scratch;
@@ -55,6 +51,7 @@ static Scratch scratch;
 #define ss    local->sprite
 #define seq   local->sequence
 #define aa    local->animation
+#define aainfo scratch.animation_info
 
 
 static void room_306_init() {
diff --git a/engines/mads/madsv2/forest/rooms/room307.cpp b/engines/mads/madsv2/forest/rooms/room307.cpp
index 409ed029d8b..04f7f2dd846 100644
--- a/engines/mads/madsv2/forest/rooms/room307.cpp
+++ b/engines/mads/madsv2/forest/rooms/room307.cpp
@@ -39,14 +39,10 @@ namespace Forest {
 namespace Rooms {
 
 struct Scratch {
-	int16 sprite[15];       /* Sprite series handles */
-	int16 sequence[15];     /* Sequence handles      */
-	int16 animation[4];     /* Animation handles     */
-
-	int16 dragon_frame;     /* frame animation is on */
-
-	int16 done_with_conv;   /* T if done with conv   */
-	int16 prev_room;
+	int16 sprite[10];       /* Sprite series handles */
+	int16 sequence[10];     /* Sequence handles      */
+	int16 animation[10];     /* Animation handles     */
+	AnimationInfo animation_info[10];
 };
 
 static Scratch scratch;
@@ -55,6 +51,7 @@ static Scratch scratch;
 #define ss    local->sprite
 #define seq   local->sequence
 #define aa    local->animation
+#define aainfo scratch.animation_info
 
 
 static void room_307_init() {
diff --git a/engines/mads/madsv2/forest/rooms/room308.cpp b/engines/mads/madsv2/forest/rooms/room308.cpp
index 8555f6dbd6d..6be0433fca1 100644
--- a/engines/mads/madsv2/forest/rooms/room308.cpp
+++ b/engines/mads/madsv2/forest/rooms/room308.cpp
@@ -39,14 +39,10 @@ namespace Forest {
 namespace Rooms {
 
 struct Scratch {
-	int16 sprite[15];       /* Sprite series handles */
-	int16 sequence[15];     /* Sequence handles      */
-	int16 animation[4];     /* Animation handles     */
-
-	int16 dragon_frame;     /* frame animation is on */
-
-	int16 done_with_conv;   /* T if done with conv   */
-	int16 prev_room;
+	int16 sprite[10];       /* Sprite series handles */
+	int16 sequence[10];     /* Sequence handles      */
+	int16 animation[10];     /* Animation handles     */
+	AnimationInfo animation_info[10];
 };
 
 static Scratch scratch;
@@ -55,6 +51,7 @@ static Scratch scratch;
 #define ss    local->sprite
 #define seq   local->sequence
 #define aa    local->animation
+#define aainfo scratch.animation_info
 
 
 static void room_308_init() {
diff --git a/engines/mads/madsv2/forest/rooms/room321.cpp b/engines/mads/madsv2/forest/rooms/room321.cpp
index 7325c714f9f..ff3ef4df085 100644
--- a/engines/mads/madsv2/forest/rooms/room321.cpp
+++ b/engines/mads/madsv2/forest/rooms/room321.cpp
@@ -39,14 +39,10 @@ namespace Forest {
 namespace Rooms {
 
 struct Scratch {
-	int16 sprite[15];       /* Sprite series handles */
-	int16 sequence[15];     /* Sequence handles      */
-	int16 animation[4];     /* Animation handles     */
-
-	int16 dragon_frame;     /* frame animation is on */
-
-	int16 done_with_conv;   /* T if done with conv   */
-	int16 prev_room;
+	int16 sprite[10];       /* Sprite series handles */
+	int16 sequence[10];     /* Sequence handles      */
+	int16 animation[10];     /* Animation handles     */
+	AnimationInfo animation_info[10];
 };
 
 static Scratch scratch;
@@ -55,6 +51,7 @@ static Scratch scratch;
 #define ss    local->sprite
 #define seq   local->sequence
 #define aa    local->animation
+#define aainfo scratch.animation_info
 
 
 static void room_321_init() {
diff --git a/engines/mads/madsv2/forest/rooms/room322.cpp b/engines/mads/madsv2/forest/rooms/room322.cpp
index 07c0a80e45c..8f1777d3c83 100644
--- a/engines/mads/madsv2/forest/rooms/room322.cpp
+++ b/engines/mads/madsv2/forest/rooms/room322.cpp
@@ -39,14 +39,10 @@ namespace Forest {
 namespace Rooms {
 
 struct Scratch {
-	int16 sprite[15];       /* Sprite series handles */
-	int16 sequence[15];     /* Sequence handles      */
-	int16 animation[4];     /* Animation handles     */
-
-	int16 dragon_frame;     /* frame animation is on */
-
-	int16 done_with_conv;   /* T if done with conv   */
-	int16 prev_room;
+	int16 sprite[10];       /* Sprite series handles */
+	int16 sequence[10];     /* Sequence handles      */
+	int16 animation[10];     /* Animation handles     */
+	AnimationInfo animation_info[10];
 };
 
 static Scratch scratch;
@@ -55,6 +51,7 @@ static Scratch scratch;
 #define ss    local->sprite
 #define seq   local->sequence
 #define aa    local->animation
+#define aainfo scratch.animation_info
 
 
 static void room_322_init() {
diff --git a/engines/mads/madsv2/forest/rooms/room401.cpp b/engines/mads/madsv2/forest/rooms/room401.cpp
index 626d2f7cb9e..4ea88c25dec 100644
--- a/engines/mads/madsv2/forest/rooms/room401.cpp
+++ b/engines/mads/madsv2/forest/rooms/room401.cpp
@@ -39,14 +39,10 @@ namespace Forest {
 namespace Rooms {
 
 struct Scratch {
-	int16 sprite[15];       /* Sprite series handles */
-	int16 sequence[15];     /* Sequence handles      */
-	int16 animation[4];     /* Animation handles     */
-
-	int16 dragon_frame;     /* frame animation is on */
-
-	int16 done_with_conv;   /* T if done with conv   */
-	int16 prev_room;
+	int16 sprite[10];       /* Sprite series handles */
+	int16 sequence[10];     /* Sequence handles      */
+	int16 animation[10];     /* Animation handles     */
+	AnimationInfo animation_info[10];
 };
 
 static Scratch scratch;
@@ -55,6 +51,7 @@ static Scratch scratch;
 #define ss    local->sprite
 #define seq   local->sequence
 #define aa    local->animation
+#define aainfo scratch.animation_info
 
 
 static void room_401_init() {
diff --git a/engines/mads/madsv2/forest/rooms/room402.cpp b/engines/mads/madsv2/forest/rooms/room402.cpp
index 34273aa93ce..99e263ca4e3 100644
--- a/engines/mads/madsv2/forest/rooms/room402.cpp
+++ b/engines/mads/madsv2/forest/rooms/room402.cpp
@@ -39,14 +39,10 @@ namespace Forest {
 namespace Rooms {
 
 struct Scratch {
-	int16 sprite[15];       /* Sprite series handles */
-	int16 sequence[15];     /* Sequence handles      */
-	int16 animation[4];     /* Animation handles     */
-
-	int16 dragon_frame;     /* frame animation is on */
-
-	int16 done_with_conv;   /* T if done with conv   */
-	int16 prev_room;
+	int16 sprite[10];       /* Sprite series handles */
+	int16 sequence[10];     /* Sequence handles      */
+	int16 animation[10];     /* Animation handles     */
+	AnimationInfo animation_info[10];
 };
 
 static Scratch scratch;
@@ -55,6 +51,7 @@ static Scratch scratch;
 #define ss    local->sprite
 #define seq   local->sequence
 #define aa    local->animation
+#define aainfo scratch.animation_info
 
 
 static void room_402_init() {
diff --git a/engines/mads/madsv2/forest/rooms/room403.cpp b/engines/mads/madsv2/forest/rooms/room403.cpp
index 35bcb54e03f..c9e44095b24 100644
--- a/engines/mads/madsv2/forest/rooms/room403.cpp
+++ b/engines/mads/madsv2/forest/rooms/room403.cpp
@@ -39,14 +39,10 @@ namespace Forest {
 namespace Rooms {
 
 struct Scratch {
-	int16 sprite[15];       /* Sprite series handles */
-	int16 sequence[15];     /* Sequence handles      */
-	int16 animation[4];     /* Animation handles     */
-
-	int16 dragon_frame;     /* frame animation is on */
-
-	int16 done_with_conv;   /* T if done with conv   */
-	int16 prev_room;
+	int16 sprite[10];       /* Sprite series handles */
+	int16 sequence[10];     /* Sequence handles      */
+	int16 animation[10];     /* Animation handles     */
+	AnimationInfo animation_info[10];
 };
 
 static Scratch scratch;
@@ -55,6 +51,7 @@ static Scratch scratch;
 #define ss    local->sprite
 #define seq   local->sequence
 #define aa    local->animation
+#define aainfo scratch.animation_info
 
 
 static void room_403_init() {
diff --git a/engines/mads/madsv2/forest/rooms/room404.cpp b/engines/mads/madsv2/forest/rooms/room404.cpp
index a0e1dff1e99..24d2b042ea2 100644
--- a/engines/mads/madsv2/forest/rooms/room404.cpp
+++ b/engines/mads/madsv2/forest/rooms/room404.cpp
@@ -39,14 +39,10 @@ namespace Forest {
 namespace Rooms {
 
 struct Scratch {
-	int16 sprite[15];       /* Sprite series handles */
-	int16 sequence[15];     /* Sequence handles      */
-	int16 animation[4];     /* Animation handles     */
-
-	int16 dragon_frame;     /* frame animation is on */
-
-	int16 done_with_conv;   /* T if done with conv   */
-	int16 prev_room;
+	int16 sprite[10];       /* Sprite series handles */
+	int16 sequence[10];     /* Sequence handles      */
+	int16 animation[10];     /* Animation handles     */
+	AnimationInfo animation_info[10];
 };
 
 static Scratch scratch;
@@ -55,6 +51,7 @@ static Scratch scratch;
 #define ss    local->sprite
 #define seq   local->sequence
 #define aa    local->animation
+#define aainfo scratch.animation_info
 
 
 static void room_404_init() {
diff --git a/engines/mads/madsv2/forest/rooms/room405.cpp b/engines/mads/madsv2/forest/rooms/room405.cpp
index 794e67c6d6e..282231b5cf3 100644
--- a/engines/mads/madsv2/forest/rooms/room405.cpp
+++ b/engines/mads/madsv2/forest/rooms/room405.cpp
@@ -39,14 +39,10 @@ namespace Forest {
 namespace Rooms {
 
 struct Scratch {
-	int16 sprite[15];       /* Sprite series handles */
-	int16 sequence[15];     /* Sequence handles      */
-	int16 animation[4];     /* Animation handles     */
-
-	int16 dragon_frame;     /* frame animation is on */
-
-	int16 done_with_conv;   /* T if done with conv   */
-	int16 prev_room;
+	int16 sprite[10];       /* Sprite series handles */
+	int16 sequence[10];     /* Sequence handles      */
+	int16 animation[10];     /* Animation handles     */
+	AnimationInfo animation_info[10];
 };
 
 static Scratch scratch;
@@ -55,6 +51,7 @@ static Scratch scratch;
 #define ss    local->sprite
 #define seq   local->sequence
 #define aa    local->animation
+#define aainfo scratch.animation_info
 
 
 static void room_405_init() {
diff --git a/engines/mads/madsv2/forest/rooms/room420.cpp b/engines/mads/madsv2/forest/rooms/room420.cpp
index e3ccd7685e7..9cda90a1583 100644
--- a/engines/mads/madsv2/forest/rooms/room420.cpp
+++ b/engines/mads/madsv2/forest/rooms/room420.cpp
@@ -39,14 +39,10 @@ namespace Forest {
 namespace Rooms {
 
 struct Scratch {
-	int16 sprite[15];       /* Sprite series handles */
-	int16 sequence[15];     /* Sequence handles      */
-	int16 animation[4];     /* Animation handles     */
-
-	int16 dragon_frame;     /* frame animation is on */
-
-	int16 done_with_conv;   /* T if done with conv   */
-	int16 prev_room;
+	int16 sprite[10];       /* Sprite series handles */
+	int16 sequence[10];     /* Sequence handles      */
+	int16 animation[10];     /* Animation handles     */
+	AnimationInfo animation_info[10];
 };
 
 static Scratch scratch;
@@ -55,6 +51,7 @@ static Scratch scratch;
 #define ss    local->sprite
 #define seq   local->sequence
 #define aa    local->animation
+#define aainfo scratch.animation_info
 
 
 static void room_420_init() {
diff --git a/engines/mads/madsv2/forest/rooms/room501.cpp b/engines/mads/madsv2/forest/rooms/room501.cpp
index c2075289b60..0856cadf4e6 100644
--- a/engines/mads/madsv2/forest/rooms/room501.cpp
+++ b/engines/mads/madsv2/forest/rooms/room501.cpp
@@ -39,14 +39,10 @@ namespace Forest {
 namespace Rooms {
 
 struct Scratch {
-	int16 sprite[15];       /* Sprite series handles */
-	int16 sequence[15];     /* Sequence handles      */
-	int16 animation[4];     /* Animation handles     */
-
-	int16 dragon_frame;     /* frame animation is on */
-
-	int16 done_with_conv;   /* T if done with conv   */
-	int16 prev_room;
+	int16 sprite[10];       /* Sprite series handles */
+	int16 sequence[10];     /* Sequence handles      */
+	int16 animation[10];     /* Animation handles     */
+	AnimationInfo animation_info[10];
 };
 
 static Scratch scratch;
@@ -55,6 +51,7 @@ static Scratch scratch;
 #define ss    local->sprite
 #define seq   local->sequence
 #define aa    local->animation
+#define aainfo scratch.animation_info
 
 
 static void room_501_init() {
diff --git a/engines/mads/madsv2/forest/rooms/room503.cpp b/engines/mads/madsv2/forest/rooms/room503.cpp
index 0c66b325ff4..5ecd14289d0 100644
--- a/engines/mads/madsv2/forest/rooms/room503.cpp
+++ b/engines/mads/madsv2/forest/rooms/room503.cpp
@@ -39,14 +39,10 @@ namespace Forest {
 namespace Rooms {
 
 struct Scratch {
-	int16 sprite[15];       /* Sprite series handles */
-	int16 sequence[15];     /* Sequence handles      */
-	int16 animation[4];     /* Animation handles     */
-
-	int16 dragon_frame;     /* frame animation is on */
-
-	int16 done_with_conv;   /* T if done with conv   */
-	int16 prev_room;
+	int16 sprite[10];       /* Sprite series handles */
+	int16 sequence[10];     /* Sequence handles      */
+	int16 animation[10];     /* Animation handles     */
+	AnimationInfo animation_info[10];
 };
 
 static Scratch scratch;
@@ -55,6 +51,7 @@ static Scratch scratch;
 #define ss    local->sprite
 #define seq   local->sequence
 #define aa    local->animation
+#define aainfo scratch.animation_info
 
 
 static void room_503_init() {
diff --git a/engines/mads/madsv2/forest/rooms/room509.cpp b/engines/mads/madsv2/forest/rooms/room509.cpp
index 50881aa9434..b91d93105db 100644
--- a/engines/mads/madsv2/forest/rooms/room509.cpp
+++ b/engines/mads/madsv2/forest/rooms/room509.cpp
@@ -39,14 +39,10 @@ namespace Forest {
 namespace Rooms {
 
 struct Scratch {
-	int16 sprite[15];       /* Sprite series handles */
-	int16 sequence[15];     /* Sequence handles      */
-	int16 animation[4];     /* Animation handles     */
-
-	int16 dragon_frame;     /* frame animation is on */
-
-	int16 done_with_conv;   /* T if done with conv   */
-	int16 prev_room;
+	int16 sprite[10];       /* Sprite series handles */
+	int16 sequence[10];     /* Sequence handles      */
+	int16 animation[10];     /* Animation handles     */
+	AnimationInfo animation_info[10];
 };
 
 static Scratch scratch;
@@ -55,6 +51,7 @@ static Scratch scratch;
 #define ss    local->sprite
 #define seq   local->sequence
 #define aa    local->animation
+#define aainfo scratch.animation_info
 
 
 static void room_509_init() {
diff --git a/engines/mads/madsv2/forest/rooms/room510.cpp b/engines/mads/madsv2/forest/rooms/room510.cpp
index 815c6ed21e3..ec39479b552 100644
--- a/engines/mads/madsv2/forest/rooms/room510.cpp
+++ b/engines/mads/madsv2/forest/rooms/room510.cpp
@@ -39,14 +39,10 @@ namespace Forest {
 namespace Rooms {
 
 struct Scratch {
-	int16 sprite[15];       /* Sprite series handles */
-	int16 sequence[15];     /* Sequence handles      */
-	int16 animation[4];     /* Animation handles     */
-
-	int16 dragon_frame;     /* frame animation is on */
-
-	int16 done_with_conv;   /* T if done with conv   */
-	int16 prev_room;
+	int16 sprite[10];       /* Sprite series handles */
+	int16 sequence[10];     /* Sequence handles      */
+	int16 animation[10];     /* Animation handles     */
+	AnimationInfo animation_info[10];
 };
 
 static Scratch scratch;
@@ -55,6 +51,7 @@ static Scratch scratch;
 #define ss    local->sprite
 #define seq   local->sequence
 #define aa    local->animation
+#define aainfo scratch.animation_info
 
 
 static void room_510_init() {
diff --git a/engines/mads/madsv2/forest/rooms/room520.cpp b/engines/mads/madsv2/forest/rooms/room520.cpp
index f907dd7d515..3413c1f4a33 100644
--- a/engines/mads/madsv2/forest/rooms/room520.cpp
+++ b/engines/mads/madsv2/forest/rooms/room520.cpp
@@ -39,14 +39,10 @@ namespace Forest {
 namespace Rooms {
 
 struct Scratch {
-	int16 sprite[15];       /* Sprite series handles */
-	int16 sequence[15];     /* Sequence handles      */
-	int16 animation[4];     /* Animation handles     */
-
-	int16 dragon_frame;     /* frame animation is on */
-
-	int16 done_with_conv;   /* T if done with conv   */
-	int16 prev_room;
+	int16 sprite[10];       /* Sprite series handles */
+	int16 sequence[10];     /* Sequence handles      */
+	int16 animation[10];     /* Animation handles     */
+	AnimationInfo animation_info[10];
 };
 
 static Scratch scratch;
@@ -55,6 +51,7 @@ static Scratch scratch;
 #define ss    local->sprite
 #define seq   local->sequence
 #define aa    local->animation
+#define aainfo scratch.animation_info
 
 
 static void room_520_init() {
diff --git a/engines/mads/madsv2/forest/rooms/room903.cpp b/engines/mads/madsv2/forest/rooms/room903.cpp
index a080b8ecef5..affb572a887 100644
--- a/engines/mads/madsv2/forest/rooms/room903.cpp
+++ b/engines/mads/madsv2/forest/rooms/room903.cpp
@@ -37,11 +37,6 @@ namespace MADSV2 {
 namespace Forest {
 namespace Rooms {
 
-#define local (&scratch)
-#define ss    local->sprite
-#define seq   local->sequence
-#define aa    local->animation
-
 /* Triggers */
 #define TRIGGER0 100
 #define TRIGGER1 101


Commit: 6878d3b44d85b9c0c5459150487c5b303f0219ce
    https://github.com/scummvm/scummvm/commit/6878d3b44d85b9c0c5459150487c5b303f0219ce
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-06-07T08:21:18+10:00

Commit Message:
MADS: FOREST: Implemented room 203

Changed paths:
    engines/mads/madsv2/forest/extra.cpp
    engines/mads/madsv2/forest/extra.h
    engines/mads/madsv2/forest/global.cpp
    engines/mads/madsv2/forest/global.h
    engines/mads/madsv2/forest/rooms/room203.cpp


diff --git a/engines/mads/madsv2/forest/extra.cpp b/engines/mads/madsv2/forest/extra.cpp
index e135a5355f2..b222cf4d01d 100644
--- a/engines/mads/madsv2/forest/extra.cpp
+++ b/engines/mads/madsv2/forest/extra.cpp
@@ -34,6 +34,10 @@ namespace MADS {
 namespace MADSV2 {
 namespace Forest {
 
+void display_interface() {
+	error("TODO: display_interface");
+}
+
 void fly_on_screen(int flying_object) {
 	error("TODO: fly_on_screen");
 }
diff --git a/engines/mads/madsv2/forest/extra.h b/engines/mads/madsv2/forest/extra.h
index e4cac25e94f..8639da8addb 100644
--- a/engines/mads/madsv2/forest/extra.h
+++ b/engines/mads/madsv2/forest/extra.h
@@ -48,6 +48,7 @@ namespace Forest {
 extern void fly_on_screen(int flying_object);
 extern void fly_off_screen(int flying_object);
 
+extern void display_interface();
 extern void display_inventory(void);
 extern void solve_me_selected(void);
 extern void door_selected(void);
diff --git a/engines/mads/madsv2/forest/global.cpp b/engines/mads/madsv2/forest/global.cpp
index eeb31fdb4ee..22f546ca4f5 100644
--- a/engines/mads/madsv2/forest/global.cpp
+++ b/engines/mads/madsv2/forest/global.cpp
@@ -34,6 +34,7 @@ namespace MADSV2 {
 namespace Forest {
 
 int16 flags[40];
+bool room_203_flag;
 
 namespace Rooms {
 
diff --git a/engines/mads/madsv2/forest/global.h b/engines/mads/madsv2/forest/global.h
index 4213e666761..b602faf2352 100644
--- a/engines/mads/madsv2/forest/global.h
+++ b/engines/mads/madsv2/forest/global.h
@@ -255,6 +255,7 @@ struct AnimationInfo {
 };
 
 extern int16 flags[40];
+extern bool room_203_flag;
 
 extern void global_section_constructor();
 extern void sync_room(Common::Serializer &s);
diff --git a/engines/mads/madsv2/forest/rooms/room203.cpp b/engines/mads/madsv2/forest/rooms/room203.cpp
index e4dd299ad51..e9a0d88dedf 100644
--- a/engines/mads/madsv2/forest/rooms/room203.cpp
+++ b/engines/mads/madsv2/forest/rooms/room203.cpp
@@ -1,4 +1,4 @@
-???/* ScummVM - Graphic Adventure Engine
+/* 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
@@ -19,18 +19,16 @@
  *
  */
 
-#include "mads/madsv2/core/conv.h"
+#include "mads/madsv2/core/digi.h"
 #include "mads/madsv2/core/game.h"
-#include "mads/madsv2/core/imath.h"
 #include "mads/madsv2/core/inter.h"
 #include "mads/madsv2/core/kernel.h"
-#include "mads/madsv2/core/sound.h"
-#include "mads/madsv2/core/text.h"
-#include "mads/madsv2/forest/mads/inventory.h"
-#include "mads/madsv2/forest/mads/sounds.h"
-#include "mads/madsv2/forest/mads/words.h"
+#include "mads/madsv2/core/object.h"
+#include "mads/madsv2/core/player.h"
+#include "mads/madsv2/forest/extra.h"
 #include "mads/madsv2/forest/global.h"
-#include "mads/madsv2/forest/rooms/section1.h"
+#include "mads/madsv2/forest/journal.h"
+#include "mads/madsv2/forest/rooms/section2.h"
 #include "mads/madsv2/forest/rooms/room203.h"
 
 namespace MADS {
@@ -39,46 +37,1045 @@ namespace Forest {
 namespace Rooms {
 
 struct Scratch {
-	int16 sprite[10];       /* Sprite series handles */
-	int16 sequence[10];     /* Sequence handles      */
-	int16 animation[10];     /* Animation handles     */
-	AnimationInfo animation_info[10];
+	int16 sprite[10];                /* 0x00 ??? sprite series handles  */
+	int16 sequence[10];              /* 0x14 ??? sequence handles       */
+	int16 animation[10];             /* 0x28 ??? animation handles      */
+	AnimationInfo animation_info[10];/* 0x3C */
+	int16 _8c;                       /* 0x8C */
+	int16 _8e;                       /* 0x8E */
+	int16 _90;                       /* 0x90 */
+	int16 _92;                       /* 0x92 */
+	int16 _94;                       /* 0x94 */
+	int16 _96;                       /* 0x96 */
+	int16 _98;                       /* 0x98 */
+	int16 _9a;                       /* 0x9A */
+	int16 _9c;                       /* 0x9C */
+	int16 _9e;                       /* 0x9E ??? animation handle */
+	int16 _a0;                       /* 0xA0 ??? animation handle */
+	int16 _a2;                       /* 0xA2 ??? talk animation handle */
+	int16 _a4;                       /* 0xA4 */
+	int16 _a6;                       /* 0xA6 */
+	int16 _a8;                       /* 0xA8 */
+	int16 _paa;                      /* 0xAA */
+	int16 _ac;                       /* 0xAC */
+	int16 _ae;                       /* 0xAE */
+	int16 _b0;                       /* 0xB0 */
+	int16 _b2;                       /* 0xB2 */
+	int16 _b4;                       /* 0xB4 */
 };
 
 static Scratch scratch;
 
-#define local (&scratch)
-#define ss    local->sprite
-#define seq   local->sequence
-#define aa    local->animation
+#define local  (&scratch)
+#define ss     local->sprite
+#define seq    local->sequence
+#define aa     local->animation
 #define aainfo scratch.animation_info
 
+
+static void room_203_init1() {
+	global[g131] = 0;
+	global[g141] = 0;
+
+	if (global[g046] != 0) {
+		object_set_quality(5, 0, -1);
+		object_set_quality(8, 0, -1);
+		scratch._a0 = kernel_run_animation_disp('r', 1, 0);
+		kernel_position_anim(scratch._a0, 132, 133, 88, 6);
+		scratch._9e = kernel_run_animation_disp('e', 3, 0);
+		kernel_position_anim(scratch._9e, 105, 137, 90, 5);
+		player.x = 91;
+		player.y = 124;
+		player.facing = 3;
+	} else {
+		object_set_quality(5, 0, 0);
+		object_set_quality(8, 0, 0);
+		room_203_flag = 0;
+		scratch._a0 = kernel_run_animation_disp('r', 9, 0);
+		kernel_position_anim(scratch._a0, 110, 153, 98, 2);
+		scratch._9e = kernel_run_animation_disp('e', 9, 0);
+		kernel_position_anim(scratch._9e, 86, 148, 96, 3);
+		player.x = 70;
+		player.y = 130;
+		player.facing = 9;
+	}
+
+	if (previous_room == 201) {
+		aa[0] = kernel_run_animation(kernel_name('y', 1), 101);
+		aainfo[0]._val1 = -1;
+		scratch._9a = 11;
+		scratch._92 = 60;
+		if (flags[8] == 1) {
+			aa[4] = kernel_run_animation(kernel_name('w', 1), 105);
+			aainfo[4]._val1 = -1;
+			aainfo[4]._val2 = 0;
+			scratch._9c = 60;
+		}
+		return;
+	} else if (previous_room == 204) {
+		aa[0] = kernel_run_animation(kernel_name('y', 2), 101);
+		aainfo[0]._val1 = -1;
+		scratch._9a = 11;
+		scratch._92 = 99;
+		return;
+	} else if (previous_room == 210) {
+		aa[0] = kernel_run_animation(kernel_name('y', 3), 101);
+		aainfo[0]._val1 = -1;
+		scratch._9a = 11;
+		scratch._92 = 74;
+		global_digi_play(11);
+		scratch._a6 = 5;
+		return;
+	}
+
+	if (previous_room == 199)
+		restore_player();
+
+	global[g131] = -1;
+	global[g141] = -1;
+	kernel_reset_animation(scratch._9e, 2);
+	kernel_reset_animation(scratch._a0, 2);
+	kernel_synch(KERNEL_ANIM, scratch._9e, KERNEL_NOW, 0);
+	kernel_synch(KERNEL_ANIM, scratch._a0, KERNEL_NOW, 0);
+	global[g133] = 0;
+	global[g143] = 0;
+
+	if (global[g046] == 0) {
+		global_digi_play(15);
+		scratch._a6 = 2;
+		aa[4] = kernel_run_animation(kernel_name('w', 4), 105);
+		aainfo[4]._val1 = -1;
+		aainfo[4]._val2 = 0;
+		scratch._9c = 63;
+		player.walker_visible = -1;
+		kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
+		player.commands_allowed = -1;
+		kernel_flip_hotspot(168, -1);
+		kernel_flip_hotspot(170, -1);
+		kernel_flip_hotspot(169, -1);
+
+		if (global[g049] != 0) {
+			aa[3] = kernel_run_animation(kernel_name('a', 1), 104);
+			scratch._98 = 30;
+			aainfo[3]._val1 = -1;
+			aainfo[3]._val2 = 25;
+			kernel_reset_animation(aa[3], 25);
+			global[g049] = -1;
+			player.walker_visible = 0;
+			player.commands_allowed = 0;
+			kernel_synch(KERNEL_ANIM, aa[3], KERNEL_NOW, 0);
+		}
+
+		kernel_flip_hotspot(119, global[g047] == 0 ? 1 : 0);
+		kernel_flip_hotspot(27, global[g047]);
+	} else {
+		global_digi_play(11);
+		scratch._a6 = 5;
+	}
+}
+
 static void room_203_init() {
+	scratch._b4 = 0;
+	scratch._ac = 0;
+	scratch._ae = 0;
+	scratch._a8 = 0;
+
+	if (previous_room != KERNEL_RESTORING_GAME && previous_room != 199 && global[g050] == 0)
+		global[g046] = 0;
+
+	scratch._a6 = 5;
+	global[player_score] = -1;
+	global[g009] = -1;
+	kernel_flip_hotspot(65, false);
+	kernel_flip_hotspot(66, false);
+	kernel_flip_hotspot(168, false);
+	kernel_flip_hotspot(170, false);
+	kernel_flip_hotspot(169, false);
+
+	ss[0] = kernel_load_series(kernel_name('p', 5), 0);
+	ss[1] = kernel_load_series(kernel_name('p', 6), 0);
+
+	if (global[g047] != 0) {
+		kernel_flip_hotspot(119, false);
+		kernel_flip_hotspot(27, -1);
+		seq[1] = kernel_seq_stamp(ss[1], false, KERNEL_FIRST);
+		kernel_seq_depth(seq[1], 15);
+		kernel_seq_loc(seq[1], 128, 74);
+		kernel_seq_scale(seq[1], 100);
+	} else {
+		kernel_flip_hotspot(119, -1);
+		kernel_flip_hotspot(27, false);
+		seq[0] = kernel_seq_stamp(ss[0], false, KERNEL_FIRST);
+		kernel_seq_depth(seq[0], 15);
+		kernel_seq_loc(seq[0], 129, 79);
+		kernel_seq_scale(seq[0], 100);
+	}
+
+	if (previous_room != KERNEL_RESTORING_GAME) {
+		if (previous_room != 199) {
+			player.walker_visible = 0;
+			player.commands_allowed = 0;
+		}
+
+		for (int i = 0; i < 10; i++) {
+			aainfo[i]._val1 = 0;
+			aainfo[i]._val2 = 1;
+			aainfo[i]._val3 = 0;
+			aainfo[i]._val4 = 0;
+		}
+
+		if (previous_room != 199 && flags[8] != 3)
+			flags[8]++;
+	}
+
+	room_203_init1();
 }
 
 static void room_203_daemon() {
+	switch (kernel.trigger) {
+	case 7:
+		switch (local->_a4) {
+		case 11:
+			kernel_timing_trigger(40, 106);
+			aainfo[0]._val2 = 9;
+			kernel_reset_animation(aa[0], 9);
+			break;
+
+		case 12:
+			kernel_timing_trigger(40, 106);
+			aainfo[0]._val2 = 33;
+			kernel_reset_animation(aa[0], 33);
+			break;
+
+		case 13:
+			aainfo[0]._val2 = 28;
+			kernel_reset_animation(aa[0], 28);
+			break;
+
+		case 14:
+		case 15:
+			aainfo[1]._val2 = 11;
+			kernel_reset_animation(aa[1], 11);
+			break;
+
+		case 16:
+			aainfo[0]._val2 = 12;
+			kernel_reset_animation(aa[0], 12);
+			break;
+
+		case 17:
+			aainfo[0]._val2 = 57;
+			kernel_reset_animation(aa[0], 57);
+			break;
+
+		case 21:
+			kernel_timing_trigger(40, 106);
+			aainfo[0]._val2 = 17;
+			kernel_reset_animation(aa[0], 17);
+			break;
+
+		case 22:
+			dont_frag_the_palette();
+			kernel_abort_animation(aa[4]);
+			aainfo[4]._val1 = 0;
+
+			aa[4] = kernel_run_animation(kernel_name('w', 3), 105);
+			aainfo[4]._val1 = -1;
+			aainfo[4]._val2 = 0;
+			local->_9c = 62;
+			aainfo[0]._val2 = 41;
+			kernel_reset_animation(aa[0], 41);
+			break;
+
+		case 23:
+			kernel_timing_trigger(40, 106);
+			aainfo[0]._val2 = 23;
+			kernel_reset_animation(aa[0], 23);
+			break;
+
+		case 24:
+			kernel_timing_trigger(40, 106);
+			aainfo[2]._val2 = 6;
+			kernel_reset_animation(aa[2], 6);
+			break;
+
+		case 25:
+			aainfo[2]._val2 = 20;
+			kernel_reset_animation(aa[2], 20);
+			break;
+
+		case 26:
+			aainfo[2]._val2 = 10;
+			kernel_reset_animation(aa[2], 10);
+			break;
+
+		case 30:
+			dont_frag_the_palette();
+			kernel_abort_animation(local->_a2);
+			player.walker_visible = true;
+			local->_b2 = 0;
+			global[g154] = 0;
+			kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
+			global[g150] = -1;
+			local->_90 = global[g046] ? 16 : 15;
+
+		case 31:
+			kernel_timing_trigger(40, 106);
+			aainfo[0]._val2 = 25;
+			kernel_reset_animation(aa[0], 25);
+			break;
+
+		case 32:
+			aainfo[0]._val2 = 5;
+			kernel_reset_animation(aa[0], 5);
+			break;
+
+		case 33:
+			aainfo[3]._val2 = 3;
+			kernel_reset_animation(aa[3], 3);
+			break;
+
+		case 34:
+		case 35:
+			aainfo[3]._val2 = 39;
+			kernel_reset_animation(aa[3], 39);
+			break;
+
+		case 36:
+			kernel_timing_trigger(40, 106);
+			aainfo[3]._val2 = 60;
+			kernel_reset_animation(aa[3], 60);
+			break;
+
+		case 38:
+		case 39:
+			aainfo[3]._val2 = 63;
+			kernel_reset_animation(aa[3], 63);
+			break;
+
+		case 51:
+			kernel_timing_trigger(40, 106);
+			aainfo[0]._val2 = 24;
+			kernel_reset_animation(aa[0], 24);
+			break;
+
+		case 52:
+			global[walker_converse_state] = 0;
+			close_journal(3);
+			player.commands_allowed = true;
+			local->_a4 = 0;
+			break;
+
+		default:
+			break;
+		}
+		break;
+
+	case 24:
+		switch (local->_8c) {
+		case 50:
+			aa[1] = kernel_run_animation(kernel_name('Z', 1), 103);
+			aainfo[1]._val1 = -1;
+			aainfo[1]._val2 = 0;
+			local->_96 = 50;
+			player.commands_allowed = false;
+			player.walker_visible = false;
+			kernel_reset_animation(local->_9e, 0);
+
+			global[g131] = 0;
+			kernel_synch(KERNEL_ANIM, aa[1], KERNEL_ANIM, local->_9e);
+			kernel_synch(KERNEL_ANIM, 0, KERNEL_PLAYER, aa[1]);
+			global[g133] = 1;
+			break;
+
+		case 51:
+			aa[1] = kernel_run_animation(kernel_name('e', 4), 103);
+			aainfo[1]._val1 = -1;
+			aainfo[1]._val2 = 0;
+			local->_96 = 52;
+			player.commands_allowed = false;
+			player.walker_visible = false;
+			kernel_reset_animation(local->_9e, 0);
+
+			global[g131] = 0;
+			kernel_synch(KERNEL_ANIM, aa[1], KERNEL_ANIM, local->_9e);
+			kernel_synch(KERNEL_ANIM, 0, KERNEL_PLAYER, aa[1]);
+			global[g133] = 1;
+			break;
+
+		}
+		break;
+
+	case 25:
+		player.commands_allowed = false;
+
+		switch (local->_8e) {
+		case 41:
+			aa[2] = kernel_run_animation(kernel_name('R', 3), 102);
+			aainfo[2]._val1 = -1;
+			aainfo[2]._val2 = 0;
+			local->_94 = 41;
+			local->_a8 = 0;
+			kernel_reset_animation(local->_a0, 0);
+			kernel_synch(KERNEL_ANIM, aa[2], KERNEL_ANIM, local->_a0);
+			global[g143] = 1;
+			break;
+
+		case 42:
+			aa[2] = kernel_run_animation(kernel_name('R', 1), 102);
+			aainfo[2]._val1 = -1;
+			aainfo[2]._val2 = 0;
+			local->_94 = 42;
+			kernel_reset_animation(local->_a0, 0);
+			kernel_synch(KERNEL_ANIM, aa[2], KERNEL_ANIM, local->_a0);
+			global[g143] = 1;
+			break;
+
+		case 43:
+			aa[2] = kernel_run_animation(kernel_name('R', 2), 102);
+			aainfo[2]._val1 = -1;
+			aainfo[2]._val2 = 0;
+			local->_94 = 43;
+			kernel_reset_animation(local->_a0, 0);
+			kernel_synch(KERNEL_ANIM, aa[2], KERNEL_ANIM, local->_a0);
+			global[g143] = 1;
+			break;
+
+		default:
+			break;
+		}
+		break;
+
+	case 26:
+		kernel_reset_animation(local->_9e, 0);
+		kernel_synch(KERNEL_ANIM, aa[local->_9e], KERNEL_NOW, 0);
+		global[g133] = 1;
+		global[g131] = 0;
+
+		if (local->_90 != 55) {
+			kernel_reset_animation(local->_a0, 0);
+			kernel_synch(KERNEL_ANIM, aa[local->_a0], KERNEL_NOW, 0);
+			global[g143] = 1;
+			global[g141] = 0;
+		}
+
+		player.walker_visible = false;
+		kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
+		player.commands_allowed = false;
+
+		switch (local->_90) {
+		case 14:
+			aa[0] = kernel_run_animation(kernel_name('x', 4), 101);
+			aainfo[0]._val1 = -1;
+			aainfo[0]._val2 = 0;
+			kernel_synch(KERNEL_ANIM, aa[0], KERNEL_NOW, 0);
+			local->_9a = 14;
+			break;
+
+		case 15:
+			dont_frag_the_palette();
+			kernel_abort_animation(aa[4]);
+			aainfo[4]._val1 = 0;
+			aa[0] = kernel_run_animation(kernel_name('z', 2), 101);
+			aainfo[0]._val1 = -1;
+			aainfo[0]._val2 = 0;
+			kernel_synch(KERNEL_ANIM, aa[0], KERNEL_NOW, 0);
+			local->_9a = 15;
+			break;
+
+		case 16:
+			player.walker_visible = true;
+			kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
+			aa[0] = kernel_run_animation(kernel_name('m', 1), 101);
+			aainfo[0]._val1 = -1;
+			aainfo[0]._val2 = 0;
+			kernel_synch(KERNEL_ANIM, aa[0], KERNEL_NOW, 0);
+			local->_9a = 16;
+			kernel_seq_delete(seq[0]);
+			break;
+
+		case 55:
+			global[g143] = 0;
+			kernel_reset_animation(local->_a0, 1);
+			kernel_synch(KERNEL_ANIM, aa[local->_a0], KERNEL_NOW, 0);
+			aa[1] = kernel_run_animation(kernel_name('e', 1), 103);
+			kernel_synch(KERNEL_ANIM, aa[1], KERNEL_NOW, 0);
+			aainfo[1]._val1 = -1;
+			aainfo[1]._val2 = 0;
+			local->_a8 = 0;
+			break;
+
+		default:
+			break;
+		}
+		break;
+
+	case 101:
+		dont_frag_the_palette();
+		kernel_abort_animation(aa[0]);
+		aainfo[0]._val1 = 0;
+
+		switch (local->_9a) {
+		case 11:
+			if (global[g046]) {
+				global[g131] = -1;
+				global[g141] = -1;
+				kernel_reset_animation(local->_9e, 1);
+				kernel_reset_animation(local->_a0, 1);
+				player.walker_visible = true;
+				global[g133] = 0;
+				global[g143] = 0;
+				kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
+				kernel_synch(KERNEL_ANIM, local->_9e, KERNEL_NOW, 0);
+				kernel_synch(KERNEL_ANIM, local->_a0, KERNEL_NOW, 0);
+				player.commands_allowed = true;
+			} else {
+				if (local->_a6 != 1) {
+					global_digi_play(10);
+					local->_a6 = 1;
+				}
+
+				aa[0] = kernel_run_animation(kernel_name('x', 2), 101);
+				aainfo[0]._val1 = -1;
+				aainfo[0]._val2 = 0;
+				local->_9a = 12;
+
+				if (flags[8] == 1) {
+					aa[4] = kernel_run_animation(kernel_name('w', 2), 105);
+					aainfo[4]._val1 = -1;
+					aainfo[4]._val2 = 0;
+					local->_9c = 61;
+				} else {
+					aa[4] = kernel_run_animation(kernel_name('w', 3), 105);
+					aainfo[4]._val1 = -1;
+					aainfo[4]._val2 = 0;
+					local->_9c = 62;
+					aainfo[0]._val2 = 41;
+					kernel_reset_animation(aa[0], 41);
+				}
+			}
+			break;
+
+		case 14:
+			dont_frag_the_palette();
+			kernel_abort_animation(local->_9e);
+			dont_frag_the_palette();
+			kernel_abort_animation(local->_a0);
+			local->_a0 = kernel_run_animation_disp('r', 1, 0);
+			kernel_position_anim(local->_a0, 132, 133, 88, 6);
+			local->_9e = kernel_run_animation_disp('e', 3, 0);
+			kernel_position_anim(local->_9e, 105, 137, 90, 5);
+			close_journal(3);
+			display_interface();
+			global[g046] = -1;
+
+			if (local->_ac)
+				global[g050] = -1;
+
+			kernel_flip_hotspot(168, false);
+			kernel_flip_hotspot(170, false);
+			kernel_flip_hotspot(169, false);
+
+			global[g131] = -1;
+			global[g141] = -1;
+			kernel_reset_animation(local->_9e, 1);
+			kernel_reset_animation(local->_a0, 1);
+			kernel_synch(KERNEL_ANIM, local->_9e, KERNEL_NOW, 0);
+			kernel_synch(KERNEL_ANIM, local->_a0, KERNEL_NOW, 0);
+			global[g133] = 0;
+			global[g143] = 0;
+
+			player.walker_visible = false;
+			player_demand_facing(3);
+			player_demand_location(124, 91);
+			kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
+			player.commands_allowed = true;
+			player.walker_visible = true;
+			room_203_flag = true;
+			break;
+
+		case 15:
+			new_room = 201;
+			break;
+
+		case 16:
+			global[g131] = -1;
+			global[g141] = -1;
+			kernel_reset_animation(local->_9e, 1);
+			kernel_reset_animation(local->_a0, 1);
+			kernel_synch(KERNEL_ANIM, local->_9e, KERNEL_NOW, 0);
+			kernel_synch(KERNEL_ANIM, local->_a0, KERNEL_NOW, 0);
+			global[g133] = 0;
+			global[g143] = 0;
+
+			kernel_flip_hotspot(27, true);
+			seq[1] = kernel_seq_stamp(ss[1], true, -1);
+			kernel_seq_depth(seq[1], 15);
+			kernel_seq_loc(seq[1], 128, 74);
+			kernel_seq_scale(seq[1], 100);
+			player.commands_allowed = true;
+			break;
+
+		default:
+			break;
+		}
+		break;
+
+	case 102:
+		dont_frag_the_palette();
+		kernel_abort_animation(aa[2]);
+		aainfo[2]._val1 = 0;
+		aainfo[2]._val2 = 0;
+
+		switch (local->_8e) {
+		case 41:
+			dont_frag_the_palette();
+			kernel_abort_animation(aa[4]);
+			aainfo[4]._val1 = 0;
+			aa[4] = kernel_run_animation(kernel_name('w', 5), 105);
+			aainfo[4]._val1 = -1;
+			aainfo[4]._val2 = 0;
+			local->_9c = 64;
+			kernel_synch(KERNEL_ANIM, aa[4], KERNEL_NOW, 0);
+			kernel_reset_animation(local->_a0, 1);
+			kernel_synch(KERNEL_ANIM, local->_a0, KERNEL_NOW, 0);
+			global[g143] = 0;
+			display_interface();
+			break;
+
+		case 42:
+			global[g141] = -1;
+			kernel_reset_animation(local->_a0, 1);
+			kernel_synch(KERNEL_ANIM, local->_a0, KERNEL_NOW, 0);
+			global[g143] = 0;
+			kernel_timing_trigger(1, 26);
+			local->_90 = 14;
+			break;
+
+		case 43:
+			global[g141] = -1;
+			kernel_reset_animation(local->_a0, 1);
+			kernel_synch(KERNEL_ANIM, local->_a0, KERNEL_NOW, 0);
+			global[g143] = 0;
+			player.commands_allowed = true;
+			break;
+
+		default:
+			break;
+		}
+		break;
+
+	case 103:
+		dont_frag_the_palette();
+		kernel_abort_animation(aa[1]);
+		aainfo[1]._val1 = 0;
+		aainfo[1]._val2 = 0;
+
+		switch (local->_96) {
+		case 50:
+			global[g040] = -1;
+			new_room = 210;
+			break;
+
+		case 52:
+			aa[1] = kernel_run_animation(kernel_name('e', 1), 103);
+			kernel_synch(KERNEL_ANIM, aa[1], KERNEL_NOW, 0);
+			aainfo[1]._val1 = -1;
+			aainfo[1]._val2 = 0;
+			local->_96 = 53;
+			local->_a8 = 0;
+			break;
+
+		case 53:
+			aa[1] = kernel_run_animation(kernel_name('e', 2), 103);
+			aainfo[1]._val1 = -1;
+			aainfo[1]._val2 = 0;
+			aainfo[1]._val4 = 0;
+			aainfo[4]._val2 = 17;
+			kernel_reset_animation(aa[4], 0);
+			kernel_synch(KERNEL_ANIM, aa[4], KERNEL_NOW, 0);
+			local->_96 = local->_ac ? 55 : 54;
+			break;
+
+		case 56:
+			if (local->_ac) {
+				global[g131] = -1;
+				kernel_reset_animation(local->_9e, 1);
+				kernel_synch(KERNEL_ANIM, local->_9e, KERNEL_NOW, 0);
+				global[g133] = 0;
+			} else {
+				aa[1] = kernel_run_animation(kernel_name('e', 4), 103);
+				kernel_synch(KERNEL_ANIM, aa[1], KERNEL_NOW, 0);
+				aainfo[1]._val1 = -1;
+				aainfo[1]._val2 = 0;
+				local->_96 = 57;
+			}
+			break;
+
+		case 57:
+			global[g131] = -1;
+			kernel_reset_animation(local->_9e, 1);
+			kernel_synch(KERNEL_ANIM, local->_9e, KERNEL_NOW, 0);
+			global[g133] = 0;
+			kernel_timing_trigger(1, 26);
+			local->_90 = 14;
+			break;
+
+		default:
+			local->_96 = 0;
+			break;
+		}
+		break;
+
+	case 104:
+		dont_frag_the_palette();
+		kernel_abort_animation(aa[3]);
+		aainfo[3]._val1 = 0;
+
+		if (local->_98 == 33) {
+			player.walker_visible = true;
+			kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
+			global[g150] = -1;
+			local->_90 = 14;
+			local->_98 = 0;
+			local->_94 = 0;
+			local->_96 = 0;
+		}
+		break;
+
+	case 105:
+		dont_frag_the_palette();
+		kernel_abort_animation(aa[4]);
+		aainfo[4]._val1 = 0;
+
+		if (local->_9c == 64) {
+			if (local->_94 == 41 && local->_ac == 0) {
+				global[g145] = -1;
+				local->_8e = 42;
+			}
+
+			if (local->_96 == 54) {
+				dont_frag_the_palette();
+				kernel_abort_animation(aa[5]);
+				aainfo[5]._val1 = 0;
+				aa[1] = kernel_run_animation(kernel_name('e', 3), 103);
+				aainfo[1]._val1 = -1;
+				aainfo[1]._val2 = 0;
+				local->_96 = 56;
+			}
+
+			object_set_quality(5, -1, -1);
+			object_set_quality(8, -1, -1);
+
+			if (local->_ac) {
+				dont_frag_the_palette();
+				kernel_abort_animation(aa[5]);
+				aainfo[5]._val1 = 0;
+				aa[1] = kernel_run_animation(kernel_name('e', 3), 103);
+				aainfo[1]._val1 = -1;
+				aainfo[1]._val2 = 0;
+				local->_96 = 56;
+			}
+			break;
+		}
+		break;
+
+	case 106:
+		switch (local->_a4) {
+		case 23:
+			digi_play_build(203, 'W', 1, 1);
+			local->_a4 = 51;
+			aainfo[4]._val2 = 1;
+			kernel_reset_animation(aa[4], 1);
+			break;
+
+		case 24:
+			aainfo[2]._val2 = 10;
+			kernel_reset_animation(aa[2], 10);
+			break;
+
+		case 31:
+			aainfo[0]._val2 = 27;
+			kernel_reset_animation(aa[0], 27);
+			break;
+
+		case 36:
+			aainfo[3]._val2 = 57;
+			kernel_reset_animation(aa[3], 57);
+			digi_play_build(203, 'b', 8, 1);
+			local->_a4 = 38;
+			break;
+
+		case 34:
+			aainfo[0]._val2 = 11;
+			kernel_reset_animation(aa[0], 11);
+			break;
+
+		case 35:
+			aainfo[0]._val2 = 35;
+			kernel_reset_animation(aa[0], 35);
+			break;
+
+		case 44:
+			aainfo[0]._val2 = 19;
+			kernel_reset_animation(aa[0], 19);
+			break;
+
+		default:
+			break;
+		}
+		break;
+
+	case 107:
+		player.commands_allowed = false;
+		break;
+
+	default:
+		break;
+	}
 }
 
 static void room_203_pre_parser() {
+	if (global[g046] == 0) {
+		player.need_to_walk = 0;
+		return;
+	}
+	if (player_parse(13, 21, 0))
+		player.walk_off_edge_to_room = 201;
+	if (player_parse(13, 24, 0))
+		player.walk_off_edge_to_room = 204;
 }
 
 static void room_203_parser() {
+	if (player_parse(13, 27, 0)) {
+		if (global[g046] != 0) {
+			global[g135] = -1;
+			scratch._8c = 50;
+		} else {
+			player.commands_allowed = 0;
+			global[g150] = -1;
+			scratch._90 = 15;
+		}
+		player.command_ready = false;
+		return;
+	}
+
+	if (player_parse(13, 21, 0)) {
+		if (global[g046] == 0) {
+			player.commands_allowed = 0;
+			global[g150] = -1;
+			scratch._90 = 15;
+		}
+		player.command_ready = false;
+		return;
+	}
+
+	if (player_parse(13, 24, 0) && global[g046] == 0) {
+		player.commands_allowed = 0;
+		global[g150] = -1;
+		scratch._90 = 15;
+	}
+
+	if (global[walker_converse_state] != 0) {
+		if (global[g046] == 0) {
+			player.commands_allowed = 0;
+			kernel.trigger_setup_mode = KERNEL_TRIGGER_DAEMON;
+			kernel_timing_trigger(1, 107);
+			scratch._a8 = 0;
+			if (global[g049] != 0) {
+				inter_spin_object(8);
+				aainfo[3]._val2 = 28;
+				kernel_reset_animation(aa[3], 28);
+				global[g049] = 0;
+			} else {
+				global[g135] = -1;
+				scratch._8c = 51;
+			}
+		} else {
+			player.commands_allowed = 0;
+			digi_play_build_ii('c', 1, 1);
+			scratch._a4 = 52;
+		}
+		player.command_ready = false;
+		return;
+	}
+
+	if (global[player_selected_object] >= 0) {
+		if (global[player_selected_object] == 5) {
+			if (global[g046] != 0) {
+				// TODO: display_interface()
+			} else {
+				player.commands_allowed = 0;
+				kernel.trigger_setup_mode = KERNEL_TRIGGER_DAEMON;
+				kernel_timing_trigger(1, 107);
+				if (global[g049] != 0) {
+					aainfo[3]._val2 = 41;
+					kernel_reset_animation(aa[3], 41);
+					global[g049] = 0;
+					scratch._ae = -1;
+				} else {
+					global[g145] = -1;
+					scratch._8e = 41;
+					scratch._a8 = 0;
+				}
+			}
+		} else if (global[player_selected_object] == 8) {
+			if (global[g046] != 0) {
+				// TODO: display_interface()
+			} else {
+				player.commands_allowed = 0;
+				kernel.trigger_setup_mode = KERNEL_TRIGGER_DAEMON;
+				kernel_timing_trigger(1, 107);
+				if (global[g049] != 0) {
+					scratch._ac = 0;
+					aainfo[3]._val2 = 28;
+					kernel_reset_animation(aa[3], 28);
+					global[g049] = 0;
+				} else {
+					kernel.trigger_setup_mode = KERNEL_TRIGGER_DAEMON;
+					aa[3] = kernel_run_animation(kernel_name('a', 2), 104);
+					aainfo[3]._val1 = -1;
+					aainfo[3]._val2 = 0;
+					scratch._98 = 31;
+					player.walker_visible = 0;
+					kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
+				}
+			}
+		}
+		player.command_ready = false;
+		return;
+	}
+
+	if (player_parse(78, 119, 0)) {
+		player.commands_allowed = 0;
+		if (global[g046] == 0) {
+			global[g150] = -1;
+			scratch._90 = 15;
+		} else {
+			global[g154] = 2;
+			player.walker_visible = 0;
+			scratch._a2 = kernel_run_animation_talk('b', 7, 0);
+			kernel_position_anim(scratch._a2, player.x, player.y, player.scale, player.depth);
+			scratch._b4 = -1;
+			kernel_synch(KERNEL_ANIM, scratch._a2, KERNEL_PLAYER, 0);
+			digi_play_build_ii('b', 1, 1);
+			scratch._a4 = 30;
+		}
+		player.command_ready = false;
+		return;
+	}
+
+	if (player_parse(78, 168, 0)) {
+		if (player_has(5) && player_has(8)) {
+			kernel.trigger_setup_mode = KERNEL_TRIGGER_DAEMON;
+			aa[3] = kernel_run_animation(kernel_name('a', 1), 104);
+			scratch._98 = 30;
+			aainfo[3]._val1 = -1;
+			global[g049] = -1;
+			aainfo[3]._val2 = 0;
+			player.walker_visible = 0;
+			player.commands_allowed = 0;
+			kernel_synch(KERNEL_ANIM, aa[3], KERNEL_NOW, 0);
+		} else if (player_has(8)) {
+			kernel.trigger_setup_mode = KERNEL_TRIGGER_DAEMON;
+			aa[3] = kernel_run_animation(kernel_name('a', 2), 104);
+			aainfo[3]._val1 = -1;
+			aainfo[3]._val2 = 0;
+			scratch._98 = 31;
+			player.walker_visible = 0;
+			kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
+			player.commands_allowed = 0;
+		} else {
+			player.commands_allowed = 0;
+			global[g150] = -1;
+			scratch._90 = 15;
+		}
+		player.command_ready = false;
+		return;
+	}
+
+	if (player_parse(78, 169, 0)) {
+		player.commands_allowed = 0;
+		global[g135] = -1;
+		scratch._8c = 51;
+		player.command_ready = false;
+		return;
+	}
+
+	if (player_parse(78, 170, 0)) {
+		player.commands_allowed = 0;
+		global[g145] = -1;
+		scratch._8e = 43;
+		player.command_ready = false;
+		return;
+	}
+
+	if (player_parse(78, 65, 0)) {
+		player.commands_allowed = 0;
+		aainfo[3]._val2 = 28;
+		kernel_reset_animation(aa[3], 28);
+		global[g049] = 0;
+		player.command_ready = false;
+		return;
+	}
+
+	if (player_parse(78, 66, 0)) {
+		player.commands_allowed = 0;
+		aainfo[3]._val2 = 33;
+		kernel_reset_animation(aa[3], 33);
+		global[g049] = 0;
+		player.command_ready = false;
+		return;
+	}
+
+	if (global[g049] == 0)
+		return;
+
+	if (player_parse(13, 106, 0)) {
+		player.commands_allowed = 0;
+		aainfo[3]._val2 = 41;
+		kernel_reset_animation(aa[3], 41);
+		global[g049] = 0;
+		player.command_ready = false;
+	}
 }
 
 void room_203_synchronize(Common::Serializer &s) {
 	for (int16 &v : scratch.sprite)    s.syncAsSint16LE(v);
 	for (int16 &v : scratch.sequence)  s.syncAsSint16LE(v);
 	for (int16 &v : scratch.animation) s.syncAsSint16LE(v);
+	for (AnimationInfo &ai : scratch.animation_info) ai.synchronize(s);
+	s.syncAsSint16LE(scratch._8c);
+	s.syncAsSint16LE(scratch._8e);
+	s.syncAsSint16LE(scratch._90);
+	s.syncAsSint16LE(scratch._92);
+	s.syncAsSint16LE(scratch._94);
+	s.syncAsSint16LE(scratch._96);
+	s.syncAsSint16LE(scratch._98);
+	s.syncAsSint16LE(scratch._9a);
+	s.syncAsSint16LE(scratch._9c);
+	s.syncAsSint16LE(scratch._9e);
+	s.syncAsSint16LE(scratch._a0);
+	s.syncAsSint16LE(scratch._a2);
+	s.syncAsSint16LE(scratch._a4);
+	s.syncAsSint16LE(scratch._a6);
+	s.syncAsSint16LE(scratch._a8);
+	s.syncAsSint16LE(scratch._paa);
+	s.syncAsSint16LE(scratch._ac);
+	s.syncAsSint16LE(scratch._ae);
+	s.syncAsSint16LE(scratch._b0);
+	s.syncAsSint16LE(scratch._b2);
+	s.syncAsSint16LE(scratch._b4);
 }
 
 void room_203_preload() {
-	room_init_code_pointer = room_203_init;
+	room_init_code_pointer       = room_203_init;
 	room_pre_parser_code_pointer = room_203_pre_parser;
-	room_parser_code_pointer = room_203_parser;
-	room_daemon_code_pointer = room_203_daemon;
-
-	section_1_walker();
-	section_1_interface();
+	room_parser_code_pointer     = room_203_parser;
+	room_daemon_code_pointer     = room_203_daemon;
+	section_2_walker();
+	section_2_interface();
+	player.walker_must_reload = true;
 }
 
 } // namespace Rooms



From noreply at scummvm.org  Sun Jun  7 04:16:06 2026
From: noreply at scummvm.org (dreammaster)
Date: Sun,  7 Jun 2026 04:16:06 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 1db23e2d9741ae3c20326d2ced9d0fb4ce88b675
Message-ID: <20260607041606.D215A7005BC@ron.scummvm.net>

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:
f3af6e23b9 MADS: FOREST: Implemented missing bits of room 203, AnimationInfo fields rename
1db23e2d97 MADS: FOREST: Implemented room 204, other rooms preload functions


Commit: f3af6e23b941da28fc7529796088bf19634119e3
    https://github.com/scummvm/scummvm/commit/f3af6e23b941da28fc7529796088bf19634119e3
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-06-07T13:32:52+10:00

Commit Message:
MADS: FOREST: Implemented missing bits of room 203, AnimationInfo fields rename

Changed paths:
    engines/mads/madsv2/forest/global.h
    engines/mads/madsv2/forest/rooms/room101.cpp
    engines/mads/madsv2/forest/rooms/room103.cpp
    engines/mads/madsv2/forest/rooms/room104.cpp
    engines/mads/madsv2/forest/rooms/room106.cpp
    engines/mads/madsv2/forest/rooms/room107.cpp
    engines/mads/madsv2/forest/rooms/room199.cpp
    engines/mads/madsv2/forest/rooms/room201.cpp
    engines/mads/madsv2/forest/rooms/room203.cpp
    engines/mads/madsv2/forest/rooms/room204.cpp


diff --git a/engines/mads/madsv2/forest/global.h b/engines/mads/madsv2/forest/global.h
index b602faf2352..75b09e04960 100644
--- a/engines/mads/madsv2/forest/global.h
+++ b/engines/mads/madsv2/forest/global.h
@@ -244,13 +244,13 @@ enum {
 };
 
 struct AnimationInfo {
-	int16 _val1;
-	int16 _val2;
+	int16 _active;
+	int16 _frame;
 	int16 _val3;
 	int16 _val4;
 
 	void synchronize(Common::Serializer &s) {
-		s.syncMultipleLE(_val1, _val2, _val3, _val4);
+		s.syncMultipleLE(_active, _frame, _val3, _val4);
 	}
 };
 
diff --git a/engines/mads/madsv2/forest/rooms/room101.cpp b/engines/mads/madsv2/forest/rooms/room101.cpp
index e37b7788ccc..101cc01d88d 100644
--- a/engines/mads/madsv2/forest/rooms/room101.cpp
+++ b/engines/mads/madsv2/forest/rooms/room101.cpp
@@ -87,14 +87,14 @@ static void room_101_init1() {
 	mouse_hide();
 
 	for (auto &v : aainfo) {
-		v._val1 = 0;
-		v._val2 = -1;
+		v._active = 0;
+		v._frame = -1;
 	}
 
 	stop_speech_on_run_animation = false;
 	aa[8] = kernel_run_animation(kernel_name('I', 1), 0);
-	aainfo[8]._val1 = -1;
-	aainfo[8]._val2 = 8;
+	aainfo[8]._active = -1;
+	aainfo[8]._frame = 8;
 	kernel_reset_animation(aa[8], 8);
 }
 
@@ -126,7 +126,7 @@ static void room_101_init2() {
 	}
 
 	aa[6] = kernel_run_animation(kernel_name('b', 1), 104);
-	aainfo[6]._val1 = -1;
+	aainfo[6]._active = -1;
 	scratch._9c = 1;
 	scratch._ac = 1;	
 }
@@ -163,14 +163,14 @@ static void room_101_init3() {
 
 	if (previous_room == 104) {
 		aa[0] = kernel_run_animation(kernel_name('y', 1), 100);
-		aainfo[0]._val1 = -1;
+		aainfo[0]._active = -1;
 		scratch._9c = 57;
 		return;
 	}
 
 	if (previous_room == 106) {
 		aa[0] = kernel_run_animation(kernel_name('y', 2), 100);
-		aainfo[0]._val1 = -1;
+		aainfo[0]._active = -1;
 		scratch._9c = 77;
 		return;
 	}
@@ -198,7 +198,7 @@ static void room_101_init3() {
 		player.commands_allowed = 0;
 		player.walker_visible = 0;
 		global[g009] = 0;
-		aainfo[5]._val1 = -1;
+		aainfo[5]._active = -1;
 		aa[5] = kernel_run_animation(kernel_name('b', 8), 111);
 		scratch._ae = 4;
 		return;
@@ -225,8 +225,8 @@ static void room_101_init() {
 		}
 
 		for (auto &s : aainfo) {
-			s._val1 = 0;
-			s._val2 = 1;
+			s._active = 0;
+			s._frame = 1;
 			s._val3 = s._val4 = 0;
 		}
 
@@ -283,9 +283,9 @@ static void room_101_init() {
 
 static void room_101_anim1() {
 	int16 frame = kernel_anim[aa[6]].frame;
-	if (frame == aainfo[6]._val2)
+	if (frame == aainfo[6]._frame)
 		return;
-	aainfo[6]._val2 = frame;
+	aainfo[6]._frame = frame;
 
 	switch (scratch._ac) {
 	case 2:
@@ -297,19 +297,19 @@ static void room_101_anim1() {
 		}
 		if (frame == 6) {
 			if (aainfo[6]._val3 != 10) return;
-			aainfo[6]._val2 = 2;
+			aainfo[6]._frame = 2;
 			kernel_reset_animation(aa[6], 2);
 			return;
 		}
 		if (frame == 10) {
 			if (aainfo[6]._val3 != 9) return;
-			aainfo[6]._val2 = 6;
+			aainfo[6]._frame = 6;
 			kernel_reset_animation(aa[6], 6);
 			return;
 		}
 		if (frame == 14) {
 			if (aainfo[6]._val3 != 12) return;
-			aainfo[6]._val2 = 13;
+			aainfo[6]._frame = 13;
 			kernel_reset_animation(aa[6], 13);
 		}
 		return;
@@ -323,7 +323,7 @@ static void room_101_anim1() {
 		}
 		if (frame == 15) {
 			if (aainfo[6]._val3 != 9) return;
-			aainfo[6]._val2 = 11;
+			aainfo[6]._frame = 11;
 			kernel_reset_animation(aa[6], 11);
 		}
 		return;
@@ -337,7 +337,7 @@ static void room_101_anim1() {
 		}
 		if (frame == 12) {
 			if (aainfo[6]._val3 != 11) return;
-			aainfo[6]._val2 = 8;
+			aainfo[6]._frame = 8;
 			kernel_reset_animation(aa[6], 8);
 		}
 		return;
@@ -351,7 +351,7 @@ static void room_101_anim1() {
 		}
 		if (frame == 30) {
 			if (aainfo[6]._val3 != 10) return;
-			aainfo[6]._val2 = 29;
+			aainfo[6]._frame = 29;
 			kernel_reset_animation(aa[6], 29);
 		}
 		return;
@@ -365,7 +365,7 @@ static void room_101_anim1() {
 		}
 		if (frame == 94) {
 			if (aainfo[6]._val3 != 10) return;
-			aainfo[6]._val2 = 90;
+			aainfo[6]._frame = 90;
 			kernel_reset_animation(aa[6], 90);
 		}
 		return;
@@ -374,30 +374,30 @@ static void room_101_anim1() {
 
 static void room_101_anim2() {
 	int16 frame = kernel_anim[aa[7]].frame;
-	if (frame == aainfo[7]._val2)
+	if (frame == aainfo[7]._frame)
 		return;
-	aainfo[7]._val2 = frame;
+	aainfo[7]._frame = frame;
 	if (frame != 18) return;
 	if (aainfo[7]._val3 != 7) return;
-	aainfo[7]._val2 = 6;
+	aainfo[7]._frame = 6;
 	kernel_reset_animation(aa[7], 6);
 }
 
 static void room_101_anim3() {
 	int16 frame = kernel_anim[aa[0]].frame;
-	if (frame != aainfo[0]._val2)
-		aainfo[0]._val2 = frame;
+	if (frame != aainfo[0]._frame)
+		aainfo[0]._frame = frame;
 	if (global[player_hyperwalked] != -1) return;
 	int16 resetFrame = scratch._9c - 1;
-	aainfo[0]._val2 = resetFrame;
+	aainfo[0]._frame = resetFrame;
 	kernel_reset_animation(aa[0], resetFrame);
 }
 
 static void room_101_anim4() {
 	int16 frame = kernel_anim[aa[4]].frame;
-	if (frame == aainfo[4]._val2)
+	if (frame == aainfo[4]._frame)
 		return;
-	aainfo[4]._val2 = frame;
+	aainfo[4]._frame = frame;
 
 	switch (aainfo[4]._val3) {
 	case 13:
@@ -418,19 +418,19 @@ static void room_101_anim4() {
 		}
 		if (frame == 7) {
 			if (scratch._aa != 50) return;
-			aainfo[4]._val2 = 3;
+			aainfo[4]._frame = 3;
 			kernel_reset_animation(aa[4], 3);
 			return;
 		}
 		if (frame == 9) {
 			if (scratch._aa != 50) return;
-			aainfo[4]._val2 = 8;
+			aainfo[4]._frame = 8;
 			kernel_reset_animation(aa[4], 8);
 			return;
 		}
 		if (frame == 18) {
 			if (scratch._aa != 51) return;
-			aainfo[4]._val2 = 14;
+			aainfo[4]._frame = 14;
 			kernel_reset_animation(aa[4], 14);
 		}
 		return;
@@ -449,21 +449,21 @@ static void room_101_anim4() {
 
 static void room_101_anim5() {
 	int16 frame = kernel_anim[aa[2]].frame;
-	if (frame != aainfo[2]._val2)
-		aainfo[2]._val2 = frame;
+	if (frame != aainfo[2]._frame)
+		aainfo[2]._frame = frame;
 }
 
 static void room_101_anim6() {
 	int16 frame = kernel_anim[aa[1]].frame;
-	if (frame != aainfo[1]._val2)
-		aainfo[1]._val2 = frame;
+	if (frame != aainfo[1]._frame)
+		aainfo[1]._frame = frame;
 }
 
 static void room_101_anim7() {
 	int16 frame = kernel_anim[aa[5]].frame;
-	if (frame == aainfo[5]._val2)
+	if (frame == aainfo[5]._frame)
 		return;
-	aainfo[5]._val2 = frame;
+	aainfo[5]._frame = frame;
 	if (frame == 2) {
 		if (scratch._ae != 4) return;
 		digi_initial_volume(100);
@@ -474,7 +474,7 @@ static void room_101_anim7() {
 	if (frame == 78) {
 		if (scratch._ae != 5) return;
 		kernel_abort_animation(aa[5]);
-		aainfo[5]._val1 = 0;
+		aainfo[5]._active = 0;
 		player.x = 96;
 		player.y = 140;
 		player.facing = 3;
@@ -487,17 +487,17 @@ static void room_101_anim7() {
 
 static void room_101_anim8() {
 	int16 frame = kernel_anim[aa[8]].frame;
-	if (frame == aainfo[8]._val2)
+	if (frame == aainfo[8]._frame)
 		return;
-	aainfo[8]._val2 = frame;
+	aainfo[8]._frame = frame;
 	if (frame != 9) {
 		if (frame != 81) return;
 		dont_frag_the_palette();
 		kernel_abort_animation(aa[8]);
-		aainfo[8]._val1 = 0;
+		aainfo[8]._active = 0;
 		stop_speech_on_run_animation = false;
 		aa[9] = kernel_run_animation(kernel_name('I', 2), 0);
-		aainfo[9]._val1 = -1;
+		aainfo[9]._active = -1;
 		kernel_synch(KERNEL_ANIM, aa[9], KERNEL_ANIM, aa[8]);
 	}
 	kernel_timing_trigger(1, 113);
@@ -505,8 +505,8 @@ static void room_101_anim8() {
 
 static void room_101_anim9() {
 	int16 frame = kernel_anim[aa[9]].frame;
-	if (frame != aainfo[9]._val2) {
-		aainfo[9]._val2 = frame;
+	if (frame != aainfo[9]._frame) {
+		aainfo[9]._frame = frame;
 		if (frame == 99) {
 			digi_play_build(101, 'r', 3, 1);
 			scratch._b4 = 99;
@@ -528,10 +528,10 @@ static void room_101_anim9() {
 			} else if (frame == 205) {
 				dont_frag_the_palette();
 				kernel_abort_animation(aa[9]);
-				aainfo[9]._val1 = 0;
+				aainfo[9]._active = 0;
 				stop_speech_on_run_animation = false;
 				aa[10] = kernel_run_animation(kernel_name('I', 3), 0);
-				aainfo[10]._val1 = -1;
+				aainfo[10]._active = -1;
 				kernel_synch(KERNEL_ANIM, aa[10], KERNEL_ANIM, aa[9]);
 				kernel_timing_trigger(1, 113);
 			}
@@ -546,12 +546,12 @@ static void room_101_anim9() {
 
 static void room_101_anim10() {
 	int16 frame = kernel_anim[aa[10]].frame;
-	if (frame != aainfo[10]._val2) {
-		aainfo[10]._val2 = frame;
+	if (frame != aainfo[10]._frame) {
+		aainfo[10]._frame = frame;
 		if (frame == 90) {
 			stop_speech_on_run_animation = true;
 			kernel_abort_animation(aa[10]);
-			aainfo[10]._val1 = 0;
+			aainfo[10]._active = 0;
 			new_room = 106;
 		} else if (frame < 90) {
 			if (frame == 35) {
@@ -598,39 +598,39 @@ static void room_101_daemon() {
 	case 7:
 		switch (scratch._aa) {
 		case 1:
-			aainfo[6]._val2 = 13;
+			aainfo[6]._frame = 13;
 			kernel_reset_animation(aa[6], 13);
 			aainfo[6]._val3 = 12;
 			kernel_timing_trigger(25, 7);
 			scratch._aa++;
 			break;
 		case 2:
-			aainfo[6]._val2 = 6;
+			aainfo[6]._frame = 6;
 			kernel_reset_animation(aa[6], 6);
 			aainfo[6]._val3 = 9;
 			digi_play_build(101, 'r', 1, 1);
 			scratch._aa++;
 			break;
 		case 3:
-			aainfo[6]._val2 = 13;
+			aainfo[6]._frame = 13;
 			kernel_reset_animation(aa[6], 13);
 			aainfo[6]._val3 = 12;
 			kernel_timing_trigger(30, 106);
 			break;
 		case 4:
-			aainfo[6]._val2 = 12;
+			aainfo[6]._frame = 12;
 			kernel_reset_animation(aa[6], 12);
 			aainfo[6]._val3 = 0;
 			scratch._aa++;
 			break;
 		case 10:
-			aainfo[6]._val2 = 15;
+			aainfo[6]._frame = 15;
 			kernel_reset_animation(aa[6], 15);
 			aainfo[6]._val3 = 0;
 			scratch._aa++;
 			break;
 		case 20:
-			aainfo[6]._val2 = 13;
+			aainfo[6]._frame = 13;
 			kernel_reset_animation(aa[6], 13);
 			aainfo[6]._val3 = 0;
 			scratch._aa++;
@@ -641,13 +641,13 @@ static void room_101_daemon() {
 			player.commands_allowed = true;
 			break;
 		case 30:
-			aainfo[6]._val2 = 31;
+			aainfo[6]._frame = 31;
 			kernel_reset_animation(aa[6], 31);
 			aainfo[6]._val3 = 0;
 			scratch._aa++;
 			break;
 		case 40:
-			aainfo[6]._val2 = 95;
+			aainfo[6]._frame = 95;
 			kernel_reset_animation(aa[6], 95);
 			aainfo[6]._val3 = 0;
 			scratch._aa++;
@@ -663,18 +663,18 @@ static void room_101_daemon() {
 			kernel_seq_delete(seq[2]);
 			kernel_flip_hotspot(105, 0);
 			aa[4] = kernel_run_animation(kernel_name('R', 5), 103);
-			aainfo[4]._val1 = -1;
+			aainfo[4]._active = -1;
 			aainfo[4]._val3 = 14;
 			scratch._a2 = 4;
 			kernel_synch(KERNEL_ANIM, scratch._a4, KERNEL_ANIM, aa[4]);
 			break;
 		case 50:
-			aainfo[4]._val2 = 8;
+			aainfo[4]._frame = 8;
 			kernel_reset_animation(aa[4], 8);
 			kernel_timing_trigger(45, 107);
 			break;
 		case 51:
-			aainfo[4]._val2 = 19;
+			aainfo[4]._frame = 19;
 			kernel_reset_animation(aa[4], 19);
 			break;
 		case 59:
@@ -688,7 +688,7 @@ static void room_101_daemon() {
 			kernel_seq_delete(seq[0]);
 			kernel_flip_hotspot(164, 0);
 			aa[4] = kernel_run_animation(kernel_name('R', 6), 103);
-			aainfo[4]._val1 = -1;
+			aainfo[4]._active = -1;
 			aainfo[4]._val3 = 15;
 			scratch._a2 = 4;
 			kernel_synch(KERNEL_ANIM, scratch._a4, KERNEL_ANIM, aa[4]);
@@ -751,7 +751,7 @@ static void room_101_daemon() {
 	case 24:
 		if (scratch._96 == 3) {
 			aa[1] = kernel_run_animation(kernel_name('E', 1), 102);
-			aainfo[1]._val1 = -1;
+			aainfo[1]._active = -1;
 			scratch._a0 = 1;
 			kernel_reset_animation(scratch._a4, 0);
 			kernel_synch(KERNEL_ANIM, aa[1], KERNEL_ANIM, scratch._a4);
@@ -762,7 +762,7 @@ static void room_101_daemon() {
 	case 25:
 		if (scratch._98 == 2) {
 			aa[2] = kernel_run_animation(kernel_name('R', 1), 101);
-			aainfo[2]._val1 = -1;
+			aainfo[2]._active = -1;
 			scratch._9e = 2;
 			kernel_reset_animation(scratch._a6, 0);
 			kernel_synch(KERNEL_ANIM, aa[2], KERNEL_ANIM, scratch._a6);
@@ -775,32 +775,32 @@ static void room_101_daemon() {
 		case 1:
 			kernel_abort_animation(aa[6]);
 			aa[6] = kernel_run_animation(kernel_name('b', 2), 104);
-			aainfo[6]._val1 = -1;
+			aainfo[6]._active = -1;
 			scratch._9c = 1;
 			scratch._ac++;
 			break;
 		case 2:
 			kernel_abort_animation(aa[6]);
 			aa[6] = kernel_run_animation(kernel_name('b', 3), 104);
-			aainfo[6]._val1 = -1;
+			aainfo[6]._active = -1;
 			scratch._9c = 1;
 			scratch._ac++;
 			break;
 		case 3:
 			kernel_abort_animation(aa[6]);
 			aa[6] = kernel_run_animation(kernel_name('r', 1), 104);
-			aainfo[6]._val1 = -1;
+			aainfo[6]._active = -1;
 			scratch._9c = 1;
 			scratch._ac++;
 			break;
 		case 4:
 			kernel_abort_animation(aa[6]);
 			aa[7] = kernel_run_animation(kernel_name('r', 2), 105);
-			aainfo[7]._val1 = -1;
+			aainfo[7]._active = -1;
 			aainfo[7]._val3 = 7;
 			scratch._9c = 1;
 			aa[6] = kernel_run_animation(kernel_name('e', 1), 104);
-			aainfo[6]._val1 = -1;
+			aainfo[6]._active = -1;
 			scratch._9c = 1;
 			scratch._ac++;
 			break;
@@ -809,7 +809,7 @@ static void room_101_daemon() {
 			kernel_flip_hotspot(110, 0);
 			kernel_abort_animation(aa[6]);
 			aa[6] = kernel_run_animation(kernel_name('e', 2), 104);
-			aainfo[6]._val1 = -1;
+			aainfo[6]._active = -1;
 			scratch._9c = 1;
 			scratch._ac++;
 			break;
@@ -820,28 +820,28 @@ static void room_101_daemon() {
 			kernel_synch(KERNEL_ANIM, scratch._a4, KERNEL_NOW, 0);
 			global[g133] = 0;
 			aa[6] = kernel_run_animation(kernel_name('b', 4), 104);
-			aainfo[6]._val1 = -1;
+			aainfo[6]._active = -1;
 			scratch._9c = 1;
 			scratch._ac++;
 			break;
 		case 7:
 			kernel_abort_animation(aa[6]);
 			aa[6] = kernel_run_animation(kernel_name('b', 5), 104);
-			aainfo[6]._val1 = -1;
+			aainfo[6]._active = -1;
 			scratch._9c = 1;
 			scratch._ac++;
 			break;
 		case 8:
 			kernel_abort_animation(aa[6]);
 			aa[6] = kernel_run_animation(kernel_name('b', 6), 104);
-			aainfo[6]._val1 = -1;
+			aainfo[6]._active = -1;
 			scratch._9c = 1;
 			scratch._ac++;
 			break;
 		case 9:
 			kernel_abort_animation(aa[6]);
 			aa[6] = kernel_run_animation(kernel_name('b', 9), 104);
-			aainfo[6]._val1 = -1;
+			aainfo[6]._active = -1;
 			scratch._9c = 1;
 			scratch._ac++;
 			break;
@@ -861,7 +861,7 @@ static void room_101_daemon() {
 
 	case 103:
 		kernel_abort_animation(aa[4]);
-		aainfo[4]._val1 = 0;
+		aainfo[4]._active = 0;
 		global[g131] = -1;
 		global[g141] = -1;
 		kernel_reset_animation(scratch._a4, 1);
@@ -876,7 +876,7 @@ static void room_101_daemon() {
 		break;
 
 	case 107:
-		aainfo[4]._val2 = 13;
+		aainfo[4]._frame = 13;
 		kernel_reset_animation(aa[4], 13);
 		digi_play_build(101, 'B', 5, 1);
 		scratch._aa++;
@@ -891,7 +891,7 @@ static void room_101_daemon() {
 			break;
 		case 2:
 			kernel_abort_animation(aa[5]);
-			aainfo[5]._val1 = 0;
+			aainfo[5]._active = 0;
 			new_room = 205;
 			break;
 		case 4:
@@ -913,7 +913,7 @@ static void room_101_daemon() {
 		kernel_seq_delete(seq[1]);
 		kernel_flip_hotspot(154, 0);
 		aa[4] = kernel_run_animation(kernel_name('R', 4), 103);
-		aainfo[4]._val1 = -1;
+		aainfo[4]._active = -1;
 		aainfo[4]._val3 = 13;
 		scratch._a2 = 4;
 		kernel_synch(KERNEL_ANIM, scratch._a4, KERNEL_ANIM, aa[4]);
@@ -922,7 +922,7 @@ static void room_101_daemon() {
 		break;
 
 	case 106:
-		aainfo[6]._val2 = 3;
+		aainfo[6]._frame = 3;
 		kernel_reset_animation(aa[6], 3);
 		aainfo[6]._val3 = 10;
 		digi_play_build(101, 'b', 2, 1);
@@ -931,7 +931,7 @@ static void room_101_daemon() {
 
 	case 105:
 		if (aainfo[7]._val3 == 0) {
-			aainfo[7]._val1 = 0;
+			aainfo[7]._active = 0;
 			global[g141] = -1;
 			kernel_reset_animation(scratch._a6, 1);
 			kernel_synch(KERNEL_ANIM, scratch._a6, KERNEL_NOW, 0);
@@ -941,7 +941,7 @@ static void room_101_daemon() {
 		} else if (aainfo[7]._val3 == 8) {
 			kernel_abort_animation(aa[7]);
 			aa[7] = kernel_run_animation(kernel_name('r', 3), 105);
-			aainfo[7]._val1 = -1;
+			aainfo[7]._active = -1;
 			scratch._9c = 1;
 			aainfo[7]._val3 = 0;
 		}
@@ -949,7 +949,7 @@ static void room_101_daemon() {
 
 	case 100:
 		kernel_abort_animation(aa[0]);
-		aainfo[0]._val1 = 0;
+		aainfo[0]._active = 0;
 		global[g131] = -1;
 		global[g141] = -1;
 		kernel_reset_animation(scratch._a4, 1);
@@ -965,7 +965,7 @@ static void room_101_daemon() {
 
 	case 101:
 		kernel_abort_animation(aa[scratch._9e]);
-		aainfo[scratch._9e]._val1 = 0;
+		aainfo[scratch._9e]._active = 0;
 		kernel_reset_animation(scratch._a6, 1);
 		kernel_synch(KERNEL_ANIM, scratch._a6, KERNEL_NOW, 0);
 		global[g143] = 0;
@@ -974,7 +974,7 @@ static void room_101_daemon() {
 
 	case 102:
 		kernel_abort_animation(aa[scratch._a0]);
-		aainfo[scratch._a0]._val1 = 0;
+		aainfo[scratch._a0]._active = 0;
 		kernel_reset_animation(scratch._a4, 1);
 		kernel_synch(KERNEL_ANIM, scratch._a4, KERNEL_NOW, 0);
 		global[g133] = 0;
@@ -1007,16 +1007,16 @@ static void room_101_daemon() {
 			mouse_show();
 	}
 
-	if (aainfo[6]._val1)  room_101_anim1();
-	if (aainfo[7]._val1)  room_101_anim2();
-	if (aainfo[0]._val1)  room_101_anim3();
-	if (aainfo[4]._val1)  room_101_anim4();
-	if (aainfo[2]._val1)  room_101_anim5();
-	if (aainfo[1]._val1)  room_101_anim6();
-	if (aainfo[5]._val1)  room_101_anim7();
-	if (aainfo[8]._val1)  room_101_anim8();
-	if (aainfo[9]._val1)  room_101_anim9();
-	if (aainfo[10]._val1) room_101_anim10();
+	if (aainfo[6]._active)  room_101_anim1();
+	if (aainfo[7]._active)  room_101_anim2();
+	if (aainfo[0]._active)  room_101_anim3();
+	if (aainfo[4]._active)  room_101_anim4();
+	if (aainfo[2]._active)  room_101_anim5();
+	if (aainfo[1]._active)  room_101_anim6();
+	if (aainfo[5]._active)  room_101_anim7();
+	if (aainfo[8]._active)  room_101_anim8();
+	if (aainfo[9]._active)  room_101_anim9();
+	if (aainfo[10]._active) room_101_anim10();
 
 	if (scratch._b2)
 		kernel_random_frame(scratch._a8, &global[g151], global[g154]);
@@ -1074,7 +1074,7 @@ static void room_101_parser() {
 		player.commands_allowed = 0;
 		player.walker_visible = 0;
 		kernel.trigger_setup_mode = 1;
-		aainfo[5]._val1 = -1;
+		aainfo[5]._active = -1;
 		aa[5] = kernel_run_animation(kernel_name('b', 5), 111);
 		scratch._ae = 1;
 		goto handled;
diff --git a/engines/mads/madsv2/forest/rooms/room103.cpp b/engines/mads/madsv2/forest/rooms/room103.cpp
index 7ec53fc9625..d12e5461d0f 100644
--- a/engines/mads/madsv2/forest/rooms/room103.cpp
+++ b/engines/mads/madsv2/forest/rooms/room103.cpp
@@ -67,8 +67,8 @@ static void room_103_init() {
 	scratch._92 = -1;
 
 	for (int count = 0; count < 10; count++) {
-		aainfo[count]._val1 = 0;
-		aainfo[count]._val2 = 1;
+		aainfo[count]._active = 0;
+		aainfo[count]._frame = 1;
 		aainfo[count]._val3 = 0;
 		aainfo[count]._val4 = 0;
 	}
@@ -80,18 +80,18 @@ static void room_103_init() {
 	player.commands_allowed = 0;
 
 	for (int count = 0; count < 10; count++) {
-		aainfo[count]._val1 = 0;
-		aainfo[count]._val2 = -1;
+		aainfo[count]._active = 0;
+		aainfo[count]._frame = -1;
 	}
 
 	aa[0] = kernel_run_animation(kernel_name('F', 1), 0);
-	aainfo[0]._val1 = -1;
+	aainfo[0]._active = -1;
 }
 
 static void room_103_anim1() {
-	if (kernel_anim[aa[0]].frame != aainfo[0]._val2) {
+	if (kernel_anim[aa[0]].frame != aainfo[0]._frame) {
 		int16 frame = kernel_anim[aa[0]].frame;
-		aainfo[0]._val2 = frame;
+		aainfo[0]._frame = frame;
 
 		if (frame == 1) {
 			digi_initial_volume(20);
@@ -100,10 +100,10 @@ static void room_103_anim1() {
 			digi_flag1 = true;
 		} else if (frame == 273) {
 			kernel_abort_animation(aa[0]);
-			aainfo[0]._val1 = 0;
+			aainfo[0]._active = 0;
 			stop_speech_on_run_animation = false;
 			aa[1] = kernel_run_animation(kernel_name('F', 2), 0);
-			aainfo[1]._val1 = -1;
+			aainfo[1]._active = -1;
 			kernel_synch(KERNEL_ANIM, aa[1], KERNEL_ANIM, aa[0]);
 		}
 	}
@@ -119,9 +119,9 @@ static void room_103_anim1() {
 }
 
 static void room_103_anim2() {
-	if (kernel_anim[aa[1]].frame != aainfo[1]._val2) {
+	if (kernel_anim[aa[1]].frame != aainfo[1]._frame) {
 		int16 frame = kernel_anim[aa[1]].frame;
-		aainfo[1]._val2 = frame;
+		aainfo[1]._frame = frame;
 
 		if (frame == 165) {
 			digi_flag1 = false;
@@ -132,16 +132,16 @@ static void room_103_anim2() {
 				scratch._90 = 186;
 			} else if (frame == 195) {
 				if (aainfo[1]._val3 == 11) {
-					aainfo[1]._val2 = 186;
+					aainfo[1]._frame = 186;
 					kernel_reset_animation(aa[1], 186);
 				}
 			} else if (frame == 196) {
 				dont_frag_the_palette();
 				kernel_abort_animation(aa[1]);
-				aainfo[1]._val1 = 0;
+				aainfo[1]._active = 0;
 				stop_speech_on_run_animation = false;
 				aa[2] = kernel_run_animation(kernel_name('F', 3), 0);
-				aainfo[2]._val1 = -1;
+				aainfo[2]._active = -1;
 				kernel_reset_animation(aa[2], 3);
 				kernel_synch(KERNEL_ANIM, aa[2], KERNEL_ANIM, aa[1]);
 			}
@@ -166,7 +166,7 @@ static void room_103_anim2() {
 		} else if (scratch._90 == 186) {
 			scratch._90 = -1;
 			aainfo[1]._val3 = 13;
-			aainfo[1]._val2 = 195;
+			aainfo[1]._frame = 195;
 			kernel_reset_animation(aa[1], 195);
 		}
 	}
@@ -182,9 +182,9 @@ static void room_103_anim2() {
 }
 
 static void room_103_anim3() {
-	if (kernel_anim[aa[2]].frame != aainfo[2]._val2) {
+	if (kernel_anim[aa[2]].frame != aainfo[2]._frame) {
 		int16 frame = kernel_anim[aa[2]].frame;
-		aainfo[2]._val2 = frame;
+		aainfo[2]._frame = frame;
 
 		switch (frame) {
 		case 29:
@@ -194,7 +194,7 @@ static void room_103_anim3() {
 			break;
 		case 38:
 			if (aainfo[2]._val3 == 12) {
-				aainfo[2]._val2 = 29;
+				aainfo[2]._frame = 29;
 				kernel_reset_animation(aa[2], 29);
 			}
 			break;
@@ -205,7 +205,7 @@ static void room_103_anim3() {
 			break;
 		case 50:
 			if (aainfo[2]._val3 == 10) {
-				aainfo[2]._val2 = 44;
+				aainfo[2]._frame = 44;
 				kernel_reset_animation(aa[2], 44);
 			}
 			break;
@@ -216,7 +216,7 @@ static void room_103_anim3() {
 			break;
 		case 60:
 			if (aainfo[2]._val3 == 12) {
-				aainfo[2]._val2 = 54;
+				aainfo[2]._frame = 54;
 				kernel_reset_animation(aa[2], 54);
 			}
 			break;
@@ -227,7 +227,7 @@ static void room_103_anim3() {
 			break;
 		case 81:
 			if (aainfo[2]._val3 == 14) {
-				aainfo[2]._val2 = 72;
+				aainfo[2]._frame = 72;
 				kernel_reset_animation(aa[2], 72);
 			}
 			break;
@@ -238,7 +238,7 @@ static void room_103_anim3() {
 			break;
 		case 91:
 			if (aainfo[2]._val3 == 14) {
-				aainfo[2]._val2 = 85;
+				aainfo[2]._frame = 85;
 				kernel_reset_animation(aa[2], 85);
 			}
 			break;
@@ -249,7 +249,7 @@ static void room_103_anim3() {
 			break;
 		case 113:
 			if (aainfo[2]._val3 == 14) {
-				aainfo[2]._val2 = 109;
+				aainfo[2]._frame = 109;
 				kernel_reset_animation(aa[2], 109);
 			}
 			break;
@@ -264,10 +264,10 @@ static void room_103_anim3() {
 		case 166:
 			dont_frag_the_palette();
 			kernel_abort_animation(aa[2]);
-			aainfo[2]._val1 = 0;
+			aainfo[2]._active = 0;
 			stop_speech_on_run_animation = false;
 			aa[3] = kernel_run_animation(kernel_name('F', 4), 0);
-			aainfo[3]._val1 = -1;
+			aainfo[3]._active = -1;
 			kernel_synch(KERNEL_ANIM, aa[3], KERNEL_ANIM, aa[2]);
 			break;
 		}
@@ -278,25 +278,25 @@ static void room_103_anim3() {
 		case 29:
 			scratch._90 = -1;
 			aainfo[2]._val3 = 13;
-			aainfo[2]._val2 = 40;
+			aainfo[2]._frame = 40;
 			kernel_reset_animation(aa[2], 40);
 			break;
 		case 44:
 			scratch._90 = -1;
 			aainfo[2]._val3 = 13;
-			aainfo[2]._val2 = 50;
+			aainfo[2]._frame = 50;
 			kernel_reset_animation(aa[2], 50);
 			break;
 		case 54:
 			scratch._90 = -1;
 			aainfo[2]._val3 = 13;
-			aainfo[2]._val2 = 60;
+			aainfo[2]._frame = 60;
 			kernel_reset_animation(aa[2], 60);
 			break;
 		case 72:
 			scratch._90 = -1;
 			aainfo[2]._val3 = 13;
-			aainfo[2]._val2 = 81;
+			aainfo[2]._frame = 81;
 			kernel_reset_animation(aa[2], 81);
 			break;
 		case 85:
@@ -304,13 +304,13 @@ static void room_103_anim3() {
 			global[player_score] = 0;
 			midi_stop();
 			aainfo[2]._val3 = 13;
-			aainfo[2]._val2 = 92;
+			aainfo[2]._frame = 92;
 			kernel_reset_animation(aa[2], 92);
 			break;
 		case 109:
 			scratch._90 = -1;
 			aainfo[2]._val3 = 13;
-			aainfo[2]._val2 = 113;
+			aainfo[2]._frame = 113;
 			kernel_reset_animation(aa[2], 113);
 			break;
 		}
@@ -322,14 +322,14 @@ static void room_103_anim3() {
 }
 
 static void room_103_anim4() {
-	if (kernel_anim[aa[3]].frame != aainfo[3]._val2) {
+	if (kernel_anim[aa[3]].frame != aainfo[3]._frame) {
 		int16 frame = kernel_anim[aa[3]].frame;
-		aainfo[3]._val2 = frame;
+		aainfo[3]._frame = frame;
 
 		if (frame == 50) {
 			stop_speech_on_run_animation = true;
 			kernel_abort_animation(aa[3]);
-			aainfo[3]._val1 = 0;
+			aainfo[3]._active = 0;
 			new_room = 104;
 		} else if (frame < 50) {
 			switch (frame) {
@@ -341,7 +341,7 @@ static void room_103_anim4() {
 				break;
 			case 21:
 				if (aainfo[3]._val3 == 15) {
-					aainfo[3]._val2 = 16;
+					aainfo[3]._frame = 16;
 					kernel_reset_animation(aa[3], 16);
 				}
 				break;
@@ -352,7 +352,7 @@ static void room_103_anim4() {
 				break;
 			case 40:
 				if (aainfo[3]._val3 == 15) {
-					aainfo[3]._val2 = 37;
+					aainfo[3]._frame = 37;
 					kernel_reset_animation(aa[3], 37);
 				}
 				break;
@@ -364,12 +364,12 @@ static void room_103_anim4() {
 		if (scratch._90 == 16) {
 			scratch._90 = -1;
 			aainfo[3]._val3 = 13;
-			aainfo[3]._val2 = 21;
+			aainfo[3]._frame = 21;
 			kernel_reset_animation(aa[3], 21);
 		} else if (scratch._90 == 37) {
 			scratch._90 = -1;
 			aainfo[3]._val3 = 13;
-			aainfo[3]._val2 = 40;
+			aainfo[3]._frame = 40;
 			kernel_reset_animation(aa[3], 40);
 		} else {
 			scratch._90 = -1;
@@ -390,10 +390,10 @@ static void room_103_daemon() {
 		new_room = 904;
 	}
 
-	if (aainfo[0]._val1 != 0) room_103_anim1();
-	if (aainfo[1]._val1 != 0) room_103_anim2();
-	if (aainfo[2]._val1 != 0) room_103_anim3();
-	if (aainfo[3]._val1 != 0) room_103_anim4();
+	if (aainfo[0]._active != 0) room_103_anim1();
+	if (aainfo[1]._active != 0) room_103_anim2();
+	if (aainfo[2]._active != 0) room_103_anim3();
+	if (aainfo[3]._active != 0) room_103_anim4();
 }
 
 static void room_103_pre_parser() {
diff --git a/engines/mads/madsv2/forest/rooms/room104.cpp b/engines/mads/madsv2/forest/rooms/room104.cpp
index d435bb44d8f..5c8a7d216ab 100644
--- a/engines/mads/madsv2/forest/rooms/room104.cpp
+++ b/engines/mads/madsv2/forest/rooms/room104.cpp
@@ -90,8 +90,8 @@ static void room_104_init() {
 	global[player_score] = 0;
 
 	for (int count = 0; count < 10; count++) {
-		aainfo[count]._val1 = 0;
-		aainfo[count]._val2 = 1;
+		aainfo[count]._active = 0;
+		aainfo[count]._frame = 1;
 		aainfo[count]._val3 = 0;
 		aainfo[count]._val4 = 0;
 	}
@@ -159,12 +159,12 @@ static void room_104_init1() {
 	mouse_hide();
 
 	for (int count = 0; count < 10; count++) {
-		aainfo[count]._val1 = 0;
-		aainfo[count]._val2 = -1;
+		aainfo[count]._active = 0;
+		aainfo[count]._frame = -1;
 	}
 
 	aa[6] = kernel_run_animation(kernel_name('s', 1), 0);
-	aainfo[6]._val1 = -1;
+	aainfo[6]._active = -1;
 }
 
 static void room_104_init2() {
@@ -177,12 +177,12 @@ static void room_104_init2() {
 	mouse_hide();
 
 	for (int count = 0; count < 10; count++) {
-		aainfo[count]._val1 = 0;
-		aainfo[count]._val2 = -1;
+		aainfo[count]._active = 0;
+		aainfo[count]._frame = -1;
 	}
 
 	aa[7] = kernel_run_animation(kernel_name('T', 1), 0);
-	aainfo[7]._val1 = -1;
+	aainfo[7]._active = -1;
 }
 
 static void room_104_init3() {
@@ -197,9 +197,9 @@ static void room_104_init3() {
 	kernel_position_anim(scratch._9a, 248, 137, 94, 3);
 
 	aa[4] = kernel_run_animation(kernel_name('C', 1), 0);
-	aainfo[4]._val1 = -1;
+	aainfo[4]._active = -1;
 	aa[5] = kernel_run_animation(kernel_name('M', 1), 0);
-	aainfo[5]._val1 = -1;
+	aainfo[5]._active = -1;
 
 	if (previous_room != -2) {
 		player.x = 214;
@@ -219,7 +219,7 @@ static void room_104_init3() {
 	} else if (previous_room == 101) {
 		scratch._a0 = 0;
 		aa[0] = kernel_run_animation(kernel_name('y', 1), 100);
-		aainfo[0]._val1 = -1;
+		aainfo[0]._active = -1;
 		scratch._92 = 31;
 		return;
 	} else if (previous_room == 199) {
@@ -246,9 +246,9 @@ static void room_104_init3() {
 }
 
 static void room_104_anim1() {
-	if (kernel_anim[aa[6]].frame != aainfo[6]._val2) {
+	if (kernel_anim[aa[6]].frame != aainfo[6]._frame) {
 		int16 frame = kernel_anim[aa[6]].frame;
-		aainfo[6]._val2 = frame;
+		aainfo[6]._frame = frame;
 		scratch._a0 = 0;
 
 		switch (frame) {
@@ -264,10 +264,10 @@ static void room_104_anim1() {
 			break;
 		case 32:
 			if (aainfo[6]._val3 == 7) {
-				aainfo[6]._val2 = 13;
+				aainfo[6]._frame = 13;
 				kernel_reset_animation(aa[6], 13);
 			} else if (aainfo[6]._val3 == 9) {
-				aainfo[6]._val2 = 31;
+				aainfo[6]._frame = 31;
 				kernel_reset_animation(aa[6], 31);
 			}
 			break;
@@ -278,7 +278,7 @@ static void room_104_anim1() {
 			break;
 		case 42:
 			if (aainfo[6]._val3 == 8) {
-				aainfo[6]._val2 = 33;
+				aainfo[6]._frame = 33;
 				kernel_reset_animation(aa[6], 33);
 			}
 			break;
@@ -289,7 +289,7 @@ static void room_104_anim1() {
 			break;
 		case 45:
 			if (aainfo[6]._val3 == 9) {
-				aainfo[6]._val2 = 44;
+				aainfo[6]._frame = 44;
 				kernel_reset_animation(aa[6], 44);
 			}
 			break;
@@ -300,13 +300,13 @@ static void room_104_anim1() {
 			break;
 		case 69:
 			if (aainfo[6]._val3 == 7) {
-				aainfo[6]._val2 = 46;
+				aainfo[6]._frame = 46;
 				kernel_reset_animation(aa[6], 46);
 			}
 			break;
 		case 71:
 			if (aainfo[6]._val3 == 9) {
-				aainfo[6]._val2 = 70;
+				aainfo[6]._frame = 70;
 				kernel_reset_animation(aa[6], 70);
 			}
 			break;
@@ -339,7 +339,7 @@ static void room_104_anim1() {
 			break;
 		case 2:
 			aainfo[6]._val3 = 10;
-			aainfo[6]._val2 = 31;
+			aainfo[6]._frame = 31;
 			kernel_reset_animation(aa[6], 31);
 			scratch._a2 = -1;
 			break;
@@ -350,7 +350,7 @@ static void room_104_anim1() {
 			break;
 		case 33:
 			aainfo[6]._val3 = 10;
-			aainfo[6]._val2 = 42;
+			aainfo[6]._frame = 42;
 			kernel_reset_animation(aa[6], 42);
 			scratch._a2 = -1;
 			break;
@@ -361,9 +361,9 @@ static void room_104_anim1() {
 			break;
 		case 47:
 			aainfo[6]._val3 = 7;
-			aainfo[6]._val2 = 13;
+			aainfo[6]._frame = 13;
 			digi_play_build(104, 'c', 3, 1);
-			kernel_reset_animation(aa[6], aainfo[6]._val2);
+			kernel_reset_animation(aa[6], aainfo[6]._frame);
 			scratch._a2 = 48;
 			break;
 		case 48:
@@ -373,7 +373,7 @@ static void room_104_anim1() {
 			break;
 		case 49:
 			aainfo[6]._val3 = 10;
-			aainfo[6]._val2 = 69;
+			aainfo[6]._frame = 69;
 			kernel_reset_animation(aa[6], 69);
 			scratch._a2 = -1;
 			break;
@@ -387,9 +387,9 @@ static void room_104_anim1() {
 }
 
 static void room_104_anim2() {
-	if (kernel_anim[aa[7]].frame != aainfo[7]._val2) {
+	if (kernel_anim[aa[7]].frame != aainfo[7]._frame) {
 		int16 frame = kernel_anim[aa[7]].frame;
-		aainfo[7]._val2 = frame;
+		aainfo[7]._frame = frame;
 		scratch._a0 = 0;
 
 		switch (frame) {
@@ -410,16 +410,16 @@ static void room_104_anim2() {
 			break;
 		case 31: case 32: case 33: case 34: case 35:
 			if (aainfo[7]._val3 == 8) {
-				aainfo[7]._val2 = imath_random(30, 34);
-				kernel_reset_animation(aa[7], aainfo[7]._val2);
+				aainfo[7]._frame = imath_random(30, 34);
+				kernel_reset_animation(aa[7], aainfo[7]._frame);
 			} else {
-				aainfo[7]._val2 = 38;
+				aainfo[7]._frame = 38;
 				kernel_reset_animation(aa[7], 38);
 			}
 			break;
 		case 39:
 			if (aainfo[7]._val3 == 9) {
-				aainfo[7]._val2 = 38;
+				aainfo[7]._frame = 38;
 				kernel_reset_animation(aa[7], 38);
 			}
 			break;
@@ -430,16 +430,16 @@ static void room_104_anim2() {
 			break;
 		case 41: case 42: case 43: case 44: case 45:
 			if (aainfo[7]._val3 == 11) {
-				aainfo[7]._val2 = imath_random(40, 44);
-				kernel_reset_animation(aa[7], aainfo[7]._val2);
+				aainfo[7]._frame = imath_random(40, 44);
+				kernel_reset_animation(aa[7], aainfo[7]._frame);
 			} else {
-				aainfo[7]._val2 = 45;
+				aainfo[7]._frame = 45;
 				kernel_reset_animation(aa[7], 45);
 			}
 			break;
 		case 49:
 			if (aainfo[7]._val3 == 9) {
-				aainfo[7]._val2 = 48;
+				aainfo[7]._frame = 48;
 				kernel_reset_animation(aa[7], 48);
 			}
 			break;
@@ -450,26 +450,26 @@ static void room_104_anim2() {
 			break;
 		case 51: case 52: case 53: case 54: case 55:
 			if (aainfo[7]._val3 == 7) {
-				aainfo[7]._val2 = imath_random(50, 54);
-				kernel_reset_animation(aa[7], aainfo[7]._val2);
+				aainfo[7]._frame = imath_random(50, 54);
+				kernel_reset_animation(aa[7], aainfo[7]._frame);
 			} else {
-				aainfo[7]._val2 = 55;
+				aainfo[7]._frame = 55;
 				kernel_reset_animation(aa[7], 55);
 			}
 			break;
 		case 59:
 			if (aainfo[7]._val3 == 9) {
-				aainfo[7]._val2 = 58;
+				aainfo[7]._frame = 58;
 				kernel_reset_animation(aa[7], 58);
 			}
 			break;
 		case 64:
 			dont_frag_the_palette();
 			kernel_abort_animation(aa[7]);
-			aainfo[7]._val1 = 0;
+			aainfo[7]._active = 0;
 			stop_speech_on_run_animation = false;
 			aa[8] = kernel_run_animation(kernel_name('T', 2), 0);
-			aainfo[8]._val1 = -1;
+			aainfo[8]._active = -1;
 			kernel_synch(KERNEL_ANIM, aa[7], KERNEL_ANIM, aa[8]);
 			break;
 		}
@@ -488,7 +488,7 @@ static void room_104_anim2() {
 			break;
 		case 31:
 			aainfo[7]._val3 = 10;
-			aainfo[7]._val2 = 38;
+			aainfo[7]._frame = 38;
 			kernel_reset_animation(aa[7], 38);
 			scratch._a2 = -1;
 			break;
@@ -499,7 +499,7 @@ static void room_104_anim2() {
 			break;
 		case 41:
 			aainfo[7]._val3 = 10;
-			aainfo[7]._val2 = 45;
+			aainfo[7]._frame = 45;
 			kernel_reset_animation(aa[7], 45);
 			scratch._a2 = -1;
 			break;
@@ -510,7 +510,7 @@ static void room_104_anim2() {
 			break;
 		case 51:
 			aainfo[7]._val3 = 10;
-			aainfo[7]._val2 = 55;
+			aainfo[7]._frame = 55;
 			kernel_reset_animation(aa[7], 55);
 			scratch._a2 = -1;
 			break;
@@ -524,9 +524,9 @@ static void room_104_anim2() {
 }
 
 static void room_104_anim3() {
-	if (kernel_anim[aa[8]].frame != aainfo[8]._val2) {
+	if (kernel_anim[aa[8]].frame != aainfo[8]._frame) {
 		int16 frame = kernel_anim[aa[8]].frame;
-		aainfo[8]._val2 = frame;
+		aainfo[8]._frame = frame;
 		scratch._a0 = 0;
 
 		switch (frame) {
@@ -542,13 +542,13 @@ static void room_104_anim3() {
 			break;
 		case 45:
 			if (aainfo[8]._val3 == 7) {
-				aainfo[8]._val2 = 41;
+				aainfo[8]._frame = 41;
 				kernel_reset_animation(aa[8], 41);
 			}
 			break;
 		case 47:
 			if (aainfo[8]._val3 == 9) {
-				aainfo[8]._val2 = 46;
+				aainfo[8]._frame = 46;
 				kernel_reset_animation(aa[8], 46);
 			}
 			break;
@@ -563,13 +563,13 @@ static void room_104_anim3() {
 			break;
 		case 69:
 			if (aainfo[8]._val3 == 12) {
-				aainfo[8]._val2 = 60;
+				aainfo[8]._frame = 60;
 				kernel_reset_animation(aa[8], 60);
 			}
 			break;
 		case 74:
 			if (aainfo[8]._val3 == 9) {
-				aainfo[8]._val2 = 73;
+				aainfo[8]._frame = 73;
 				kernel_reset_animation(aa[8], 73);
 			}
 			break;
@@ -580,13 +580,13 @@ static void room_104_anim3() {
 			break;
 		case 79:
 			if (aainfo[8]._val3 == 11) {
-				aainfo[8]._val2 = 75;
+				aainfo[8]._frame = 75;
 				kernel_reset_animation(aa[8], 75);
 			}
 			break;
 		case 83:
 			if (aainfo[8]._val3 == 9) {
-				aainfo[8]._val2 = 82;
+				aainfo[8]._frame = 82;
 				kernel_reset_animation(aa[8], 82);
 			}
 			break;
@@ -598,7 +598,7 @@ static void room_104_anim3() {
 			break;
 		case 89:
 			if (aainfo[8]._val3 == 8) {
-				aainfo[8]._val2 = 84;
+				aainfo[8]._frame = 84;
 				kernel_reset_animation(aa[8], 84);
 			}
 			break;
@@ -617,7 +617,7 @@ static void room_104_anim3() {
 			break;
 		case 42:
 			aainfo[8]._val3 = 10;
-			aainfo[8]._val2 = 45;
+			aainfo[8]._frame = 45;
 			kernel_reset_animation(aa[8], 45);
 			scratch._a2 = -1;
 			break;
@@ -628,7 +628,7 @@ static void room_104_anim3() {
 			break;
 		case 60:
 			aainfo[8]._val3 = 10;
-			aainfo[8]._val2 = 70;
+			aainfo[8]._frame = 70;
 			kernel_reset_animation(aa[8], 70);
 			scratch._a2 = -1;
 			break;
@@ -639,7 +639,7 @@ static void room_104_anim3() {
 			break;
 		case 76:
 			aainfo[8]._val3 = 10;
-			aainfo[8]._val2 = 79;
+			aainfo[8]._frame = 79;
 			kernel_reset_animation(aa[8], 79);
 			scratch._a2 = -1;
 			break;
@@ -650,7 +650,7 @@ static void room_104_anim3() {
 			break;
 		case 85:
 			aainfo[8]._val3 = 10;
-			aainfo[8]._val2 = 89;
+			aainfo[8]._frame = 89;
 			kernel_reset_animation(aa[8], 89);
 			scratch._a2 = -1;
 			break;
@@ -666,9 +666,9 @@ static void room_104_anim3() {
 static void room_104_anim4() {
 	int16 var_2 = -1;
 
-	if (kernel_anim[aa[4]].frame != aainfo[4]._val2) {
+	if (kernel_anim[aa[4]].frame != aainfo[4]._frame) {
 		int16 frame = kernel_anim[aa[4]].frame;
-		aainfo[4]._val2 = frame;
+		aainfo[4]._frame = frame;
 		aainfo[4]._val4++;
 
 		if (frame == 7) {
@@ -698,7 +698,7 @@ static void room_104_anim4() {
 	}
 
 	if (var_2 >= 0) {
-		aainfo[4]._val2 = var_2;
+		aainfo[4]._frame = var_2;
 		kernel_reset_animation(aa[4], var_2);
 	}
 }
@@ -706,9 +706,9 @@ static void room_104_anim4() {
 static void room_104_anim5() {
 	int16 var_2 = -1;
 
-	if (kernel_anim[aa[5]].frame != aainfo[5]._val2) {
+	if (kernel_anim[aa[5]].frame != aainfo[5]._frame) {
 		int16 frame = kernel_anim[aa[5]].frame;
-		aainfo[5]._val2 = frame;
+		aainfo[5]._frame = frame;
 		aainfo[5]._val4++;
 
 		if (frame == 12 || frame == 5) {
@@ -735,60 +735,60 @@ static void room_104_anim5() {
 	}
 
 	if (var_2 >= 0) {
-		aainfo[5]._val2 = var_2;
+		aainfo[5]._frame = var_2;
 		kernel_reset_animation(aa[5], var_2);
 	}
 }
 
 static void room_104_anim6() {
-	if (kernel_anim[aa[0]].frame != aainfo[0]._val2)
-		aainfo[0]._val2 = kernel_anim[aa[0]].frame;
+	if (kernel_anim[aa[0]].frame != aainfo[0]._frame)
+		aainfo[0]._frame = kernel_anim[aa[0]].frame;
 
 	if (global[player_hyperwalked] == -1) {
-		aainfo[0]._val2 = scratch._92 - 1;
-		kernel_reset_animation(aa[0], aainfo[0]._val2);
+		aainfo[0]._frame = scratch._92 - 1;
+		kernel_reset_animation(aa[0], aainfo[0]._frame);
 	}
 }
 
 static void room_104_anim7() {
-	if (kernel_anim[aa[2]].frame != aainfo[2]._val2)
-		aainfo[2]._val2 = kernel_anim[aa[2]].frame;
-	int16 frame = aainfo[2]._val2;
+	if (kernel_anim[aa[2]].frame != aainfo[2]._frame)
+		aainfo[2]._frame = kernel_anim[aa[2]].frame;
+	int16 frame = aainfo[2]._frame;
 
 	if (global[player_hyperwalked] == -1) {
 		if (frame <= 23) {
-			aainfo[2]._val2 = 23;
+			aainfo[2]._frame = 23;
 			kernel_reset_animation(aa[2], 23);
 			kernel_synch(KERNEL_ANIM, aa[2], KERNEL_NOW, 0);
 		} else {
-			aainfo[2]._val2 = 44;
+			aainfo[2]._frame = 44;
 			kernel_reset_animation(aa[2], 44);
 			kernel_synch(KERNEL_ANIM, aa[2], KERNEL_NOW, 0);
 		}
 	} else if (frame == 45) {
-		aainfo[2]._val2 = 44;
+		aainfo[2]._frame = 44;
 		kernel_reset_animation(aa[2], 44);
 		kernel_synch(KERNEL_ANIM, aa[2], KERNEL_NOW, 0);
 	}
 }
 
 static void room_104_anim8() {
-	if (kernel_anim[aa[1]].frame != aainfo[1]._val2)
-		aainfo[1]._val2 = kernel_anim[aa[1]].frame;
-	int16 frame = aainfo[1]._val2;
+	if (kernel_anim[aa[1]].frame != aainfo[1]._frame)
+		aainfo[1]._frame = kernel_anim[aa[1]].frame;
+	int16 frame = aainfo[1]._frame;
 
 	if (global[player_hyperwalked] == -1) {
 		if (frame <= 23) {
-			aainfo[1]._val2 = 23;
+			aainfo[1]._frame = 23;
 			kernel_reset_animation(aa[1], 23);
 			kernel_synch(KERNEL_ANIM, aa[1], KERNEL_NOW, 0);
 		} else {
-			aainfo[1]._val2 = 44;
+			aainfo[1]._frame = 44;
 			kernel_reset_animation(aa[1], 44);
 			kernel_synch(KERNEL_ANIM, aa[1], KERNEL_NOW, 0);
 		}
 	} else if (frame == 45) {
-		aainfo[1]._val2 = 44;
+		aainfo[1]._frame = 44;
 		kernel_reset_animation(aa[1], 44);
 		kernel_synch(KERNEL_ANIM, aa[1], KERNEL_NOW, 0);
 	}
@@ -821,7 +821,7 @@ static void room_104_daemon() {
 	case 24:
 		if (scratch._8c == 3) {
 			aa[1] = kernel_run_animation(kernel_name('e', 1), 102);
-			aainfo[1]._val1 = -1;
+			aainfo[1]._active = -1;
 			scratch._96 = 1;
 			kernel_reset_animation(scratch._9a, 0);
 			kernel_synch(KERNEL_ANIM, aa[1], KERNEL_ANIM, scratch._9a);
@@ -834,7 +834,7 @@ static void room_104_daemon() {
 	case 25:
 		if (scratch._8e == 2) {
 			aa[2] = kernel_run_animation(kernel_name('R', 1), 101);
-			aainfo[2]._val1 = -1;
+			aainfo[2]._active = -1;
 			scratch._94 = 2;
 			kernel_reset_animation(scratch._9c, 0);
 			kernel_synch(KERNEL_ANIM, aa[2], KERNEL_ANIM, scratch._9c);
@@ -868,7 +868,7 @@ static void room_104_daemon() {
 
 	case 101:
 		kernel_abort_animation(aa[scratch._94]);
-		aainfo[scratch._94]._val1 = 0;
+		aainfo[scratch._94]._active = 0;
 		kernel_reset_animation(scratch._9c, 1);
 		kernel_synch(KERNEL_ANIM, scratch._9c, KERNEL_NOW, 0);
 		global[g143] = 0;
@@ -877,7 +877,7 @@ static void room_104_daemon() {
 
 	case 102:
 		kernel_abort_animation(aa[scratch._96]);
-		aainfo[scratch._96]._val1 = 0;
+		aainfo[scratch._96]._active = 0;
 		kernel_reset_animation(scratch._9a, 1);
 		kernel_synch(KERNEL_ANIM, scratch._9a, KERNEL_NOW, 0);
 		global[g133] = 0;
@@ -890,14 +890,14 @@ static void room_104_daemon() {
 		global_anim2(4, scratch._9c, global[g141], &global[g142]);
 	}
 
-	if (aainfo[0]._val1 != 0) room_104_anim6();
-	if (aainfo[2]._val1 != 0) room_104_anim7();
-	if (aainfo[1]._val1 != 0) room_104_anim8();
-	if (aainfo[4]._val1 != 0) room_104_anim4();
-	if (aainfo[5]._val1 != 0) room_104_anim5();
-	if (aainfo[6]._val1 != 0) room_104_anim1();
-	if (aainfo[7]._val1 != 0) room_104_anim2();
-	if (aainfo[8]._val1 != 0) room_104_anim3();
+	if (aainfo[0]._active != 0) room_104_anim6();
+	if (aainfo[2]._active != 0) room_104_anim7();
+	if (aainfo[1]._active != 0) room_104_anim8();
+	if (aainfo[4]._active != 0) room_104_anim4();
+	if (aainfo[5]._active != 0) room_104_anim5();
+	if (aainfo[6]._active != 0) room_104_anim1();
+	if (aainfo[7]._active != 0) room_104_anim2();
+	if (aainfo[8]._active != 0) room_104_anim3();
 }
 
 static void room_104_pre_parser() {
diff --git a/engines/mads/madsv2/forest/rooms/room106.cpp b/engines/mads/madsv2/forest/rooms/room106.cpp
index 27b9ff43c0c..19698662150 100644
--- a/engines/mads/madsv2/forest/rooms/room106.cpp
+++ b/engines/mads/madsv2/forest/rooms/room106.cpp
@@ -114,8 +114,8 @@ static void room_106_init() {
 		}
 
 		for (int16 i = 0; i < 10; i++) {
-			aainfo[i]._val1 = 0;
-			aainfo[i]._val2 = 1;
+			aainfo[i]._active = 0;
+			aainfo[i]._frame = 1;
 			aainfo[i]._val3 = 0;
 			aainfo[i]._val4 = 0;
 		}
@@ -146,12 +146,12 @@ static void room_106_init1() {
 	player.commands_allowed = 0;
 
 	for (int16 i = 0; i < 10; i++) {
-		aainfo[i]._val1 = 0;
-		aainfo[i]._val2 = -1;
+		aainfo[i]._active = 0;
+		aainfo[i]._frame = -1;
 	}
 
 	aa[9] = kernel_run_animation(kernel_name('I', 1), 0);
-	aainfo[9]._val1 = -1;
+	aainfo[9]._active = -1;
 }
 
 static void room_106_init2() {
@@ -182,7 +182,7 @@ static void room_106_init2() {
 		return;
 	case 101:
 		aa[0] = kernel_run_animation(kernel_name('y', 1), 100);
-		aainfo[0]._val1 = -1;
+		aainfo[0]._active = -1;
 		scratch._92 = 95;
 		return;
 	case 199:
@@ -198,12 +198,12 @@ static void room_106_init2() {
 		return;
 	case 201:
 		aa[0] = kernel_run_animation(kernel_name('y', 2), 100);
-		aainfo[0]._val1 = -1;
+		aainfo[0]._active = -1;
 		scratch._92 = 44;
 		return;
 	case 210:
 		aa[1] = kernel_run_animation("*RM106Y31", 0);
-		aainfo[1]._val1 = -1;
+		aainfo[1]._active = -1;
 		scratch._92 = 54;
 		return;
 	default:
@@ -221,8 +221,8 @@ static void room_106_init2() {
 
 static void room_106_anim1() {
 	int16 frame = kernel_anim[aa[9]].frame;
-	if (frame != aainfo[9]._val2) {
-		aainfo[9]._val2 = frame;
+	if (frame != aainfo[9]._frame) {
+		aainfo[9]._frame = frame;
 		if (frame == 121) {
 			new_room = 103;
 		} else if (frame < 121) {
@@ -265,9 +265,9 @@ static void room_106_anim1() {
 
 static void room_106_anim2() {
 	int16 frame = kernel_anim[aa[6]].frame;
-	if (frame == aainfo[6]._val2)
+	if (frame == aainfo[6]._frame)
 		return;
-	aainfo[6]._val2 = frame;
+	aainfo[6]._frame = frame;
 	if (scratch._a2 == 1) {
 		if (frame == 81) {
 			digi_play_build_ii('e', 5, 1);
@@ -283,27 +283,27 @@ static void room_106_anim2() {
 
 static void room_106_anim3() {
 	int16 frame = kernel_anim[aa[0]].frame;
-	if (frame != aainfo[0]._val2)
-		aainfo[0]._val2 = frame;
+	if (frame != aainfo[0]._frame)
+		aainfo[0]._frame = frame;
 	if (global[player_hyperwalked] == -1) {
-		aainfo[0]._val2 = scratch._92 - 1;
-		kernel_reset_animation(aa[0], aainfo[0]._val2);
+		aainfo[0]._frame = scratch._92 - 1;
+		kernel_reset_animation(aa[0], aainfo[0]._frame);
 	}
 }
 
 static void room_106_anim4() {
 	int16 frame = kernel_anim[aa[7]].frame;
-	if (frame != aainfo[7]._val2) {
-		aainfo[7]._val2 = frame;
+	if (frame != aainfo[7]._frame) {
+		aainfo[7]._frame = frame;
 		if (frame == 53) {
 			if (aainfo[7]._val3 == 10) {
-				aainfo[7]._val2 = 48;
+				aainfo[7]._frame = 48;
 				kernel_reset_animation(aa[7], 48);
 			}
 		} else if (frame < 53) {
 			if (frame == 7) {
 				if (aainfo[7]._val3 == 13) {
-					aainfo[7]._val2 = 5;
+					aainfo[7]._frame = 5;
 					kernel_reset_animation(aa[7], 5);
 				}
 			} else if (frame < 7) {
@@ -313,14 +313,14 @@ static void room_106_anim4() {
 					scratch._ae = 101;
 				} else if (frame == 4) {
 					if (aainfo[7]._val3 == 10) {
-						aainfo[7]._val2 = 2;
+						aainfo[7]._frame = 2;
 						kernel_reset_animation(aa[7], 2);
 					}
 				}
 			} else { // 8..52
 				if (frame == 12) {
 					if (aainfo[7]._val3 == 11) {
-						aainfo[7]._val2 = 8;
+						aainfo[7]._frame = 8;
 						kernel_reset_animation(aa[7], 8);
 					}
 				} else if (frame == 19) {
@@ -328,7 +328,7 @@ static void room_106_anim4() {
 					global[g154] = 2;
 					digi_play_build(106, 'b', 2, 1);
 					scratch._ae = 105;
-					aainfo[7]._val2 = 2;
+					aainfo[7]._frame = 2;
 					kernel_reset_animation(aa[7], 2);
 				}
 			}
@@ -343,14 +343,14 @@ static void room_106_anim4() {
 		global[g154] = 0;
 		kernel_timing_trigger(30, 28);
 		aainfo[7]._val3 = 13;
-		aainfo[7]._val2 = 5;
+		aainfo[7]._frame = 5;
 		kernel_reset_animation(aa[7], 5);
 		scratch._ae = 102;
 		return;
 	case 102:
 		global[g156] = 2;
 		aainfo[7]._val3 = 11;
-		aainfo[7]._val2 = 8;
+		aainfo[7]._frame = 8;
 		kernel_reset_animation(aa[7], 8);
 		digi_play_build(106, 'e', 1, 1);
 		scratch._ae = 103;
@@ -358,7 +358,7 @@ static void room_106_anim4() {
 	case 103:
 		global[g156] = 0;
 		aainfo[7]._val3 = 13;
-		aainfo[7]._val2 = 13;
+		aainfo[7]._frame = 13;
 		kernel_reset_animation(aa[7], 13);
 		return;
 	case 105:
@@ -366,7 +366,7 @@ static void room_106_anim4() {
 		global_digi_play(4);
 		global[g154] = 0;
 		aainfo[7]._val3 = 13;
-		aainfo[7]._val2 = 20;
+		aainfo[7]._frame = 20;
 		kernel_reset_animation(aa[7], 20);
 		return;
 	}
@@ -374,8 +374,8 @@ static void room_106_anim4() {
 
 static void room_106_anim5() {
 	int16 frame = kernel_anim[aa[8]].frame;
-	if (frame != aainfo[8]._val2) {
-		aainfo[8]._val2 = frame;
+	if (frame != aainfo[8]._frame) {
+		aainfo[8]._frame = frame;
 		if (scratch._ac == 2) {
 			if (frame == 44) {
 				aainfo[8]._val3 = 11;
@@ -383,7 +383,7 @@ static void room_106_anim5() {
 				scratch._ae = 501;
 			} else if (frame == 49) {
 				if (aainfo[8]._val3 == 11) {
-					aainfo[8]._val2 = 47;
+					aainfo[8]._frame = 47;
 					kernel_reset_animation(aa[8], 47);
 				}
 			}
@@ -394,70 +394,70 @@ static void room_106_anim5() {
 	if (scratch._ae == 501) {
 		global[g156] = 0;
 		aainfo[8]._val3 = 13;
-		aainfo[8]._val2 = 51;
+		aainfo[8]._frame = 51;
 		kernel_reset_animation(aa[8], 51);
 	}
 }
 
 static void room_106_anim6() {
 	int16 frame = kernel_anim[aa[1]].frame;
-	if (frame != aainfo[1]._val2)
-		aainfo[1]._val2 = frame;
+	if (frame != aainfo[1]._frame)
+		aainfo[1]._frame = frame;
 
-	if (global[player_hyperwalked] != -1 && aainfo[1]._val2 != scratch._92)
+	if (global[player_hyperwalked] != -1 && aainfo[1]._frame != scratch._92)
 		return;
 
 	kernel_abort_animation(aa[1]);
-	aainfo[1]._val1 = 0;
+	aainfo[1]._active = 0;
 	aa[2] = kernel_run_animation("*RM106Y32", 100);
-	aainfo[2]._val1 = -1;
+	aainfo[2]._active = -1;
 	scratch._92 = 134;
-	aainfo[2]._val2 = (global[player_hyperwalked] == -1) ? 133 : 54;
-	kernel_reset_animation(aa[2], aainfo[2]._val2);
+	aainfo[2]._frame = (global[player_hyperwalked] == -1) ? 133 : 54;
+	kernel_reset_animation(aa[2], aainfo[2]._frame);
 }
 
 static void room_106_anim7() {
 	int16 frame = kernel_anim[aa[2]].frame;
-	if (frame != aainfo[2]._val2)
-		aainfo[2]._val2 = frame;
+	if (frame != aainfo[2]._frame)
+		aainfo[2]._frame = frame;
 	if (global[player_hyperwalked] == -1) {
-		aainfo[2]._val2 = scratch._92 - 1;
-		kernel_reset_animation(aa[2], aainfo[2]._val2);
+		aainfo[2]._frame = scratch._92 - 1;
+		kernel_reset_animation(aa[2], aainfo[2]._frame);
 	}
 }
 
 static void room_106_anim8() {
 	int16 frame = kernel_anim[aa[3]].frame;
-	if (frame != aainfo[3]._val2)
-		aainfo[3]._val2 = frame;
+	if (frame != aainfo[3]._frame)
+		aainfo[3]._frame = frame;
 	if (global[player_hyperwalked] == -1) {
-		aainfo[3]._val2 = scratch._92 - 1;
-		kernel_reset_animation(aa[3], aainfo[3]._val2);
+		aainfo[3]._frame = scratch._92 - 1;
+		kernel_reset_animation(aa[3], aainfo[3]._frame);
 	}
 }
 
 static void room_106_anim9() {
 	int16 frame = kernel_anim[aa[5]].frame;
-	if (frame == aainfo[5]._val2)
+	if (frame == aainfo[5]._frame)
 		return;
-	aainfo[5]._val2 = frame;
+	aainfo[5]._frame = frame;
 	if (frame == 13)
 		digi_play_build(101, '_', 1, 2);
 }
 
 static void room_106_anim10() {
 	int16 frame = kernel_anim[aa[4]].frame;
-	if (frame != aainfo[4]._val2)
-		aainfo[4]._val2 = frame;
+	if (frame != aainfo[4]._frame)
+		aainfo[4]._frame = frame;
 
 	if (global[player_hyperwalked] == -1 && frame <= 23) {
-		aainfo[4]._val2 = 23;
+		aainfo[4]._frame = 23;
 		kernel_reset_animation(aa[4], 23);
 		kernel_synch(KERNEL_ANIM, aa[4], KERNEL_NOW, 0);
 		return;
 	}
 	if (global[player_hyperwalked] == -1 || frame == 45) {
-		aainfo[4]._val2 = 44;
+		aainfo[4]._frame = 44;
 		kernel_reset_animation(aa[4], 44);
 		kernel_synch(KERNEL_ANIM, aa[4], KERNEL_NOW, 0);
 	}
@@ -503,7 +503,7 @@ static void room_106_daemon() {
 			global[g143] = 1;
 			kernel_synch(KERNEL_ANIM, scratch._9c, KERNEL_NOW, 0);
 			aa[5] = kernel_run_animation(kernel_name('R', 1), 107);
-			aainfo[5]._val1 = -1;
+			aainfo[5]._active = -1;
 			kernel_synch(KERNEL_ANIM, aa[5], KERNEL_NOW, 0);
 		}
 		break;
@@ -514,7 +514,7 @@ static void room_106_daemon() {
 			aa[6] = kernel_run_animation(kernel_name('M', 1), 102);
 		else
 			break;
-		aainfo[6]._val1 = -1;
+		aainfo[6]._active = -1;
 		kernel_reset_animation(scratch._9a, 0);
 		kernel_synch(KERNEL_ANIM, aa[6], KERNEL_ANIM, scratch._9a);
 		global[g133] = 1;
@@ -522,7 +522,7 @@ static void room_106_daemon() {
 	case 25:
 		if (scratch._8e == 2) {
 			aa[5] = kernel_run_animation(kernel_name('R', 1), 101);
-			aainfo[5]._val1 = -1;
+			aainfo[5]._active = -1;
 			scratch._94 = 5;
 			kernel_reset_animation(scratch._9c, 0);
 			kernel_synch(KERNEL_ANIM, aa[5], KERNEL_ANIM, scratch._9c);
@@ -536,7 +536,7 @@ static void room_106_daemon() {
 			global[g133] = 1;
 			global[g143] = 1;
 			aa[3] = kernel_run_animation(kernel_name('Z', 3), 103);
-			aainfo[3]._val1 = -1;
+			aainfo[3]._active = -1;
 			scratch._98 = 3;
 			scratch._92 = 101;
 			kernel_synch(KERNEL_ANIM, scratch._9a, KERNEL_ANIM, aa[3]);
@@ -556,17 +556,17 @@ static void room_106_daemon() {
 			global[g133] = 1;
 			global[g143] = 1;
 			aa[8] = kernel_run_animation(kernel_name('x', 1), 108);
-			aainfo[8]._val1 = -1;
+			aainfo[8]._active = -1;
 			scratch._ac = 1;
 			kernel_synch(KERNEL_ANIM, scratch._9a, KERNEL_ANIM, aa[8]);
 		}
 		break;
 	case 100:
 		kernel_abort_animation(aa[0]);
-		aainfo[0]._val1 = 0;
+		aainfo[0]._active = 0;
 		if (flags[3] == 1) {
 			aa[7] = kernel_run_animation(kernel_name('t', 1), 104);
-			aainfo[7]._val1 = -1;
+			aainfo[7]._active = -1;
 		} else {
 			global[g131] = -1;
 			global[g141] = -1;
@@ -583,7 +583,7 @@ static void room_106_daemon() {
 		break;
 	case 101:
 		kernel_abort_animation(aa[scratch._94]);
-		aainfo[scratch._94]._val1 = 0;
+		aainfo[scratch._94]._active = 0;
 		kernel_reset_animation(scratch._9c, 1);
 		kernel_synch(KERNEL_ANIM, scratch._9c, KERNEL_NOW, 0);
 		global[g143] = 0;
@@ -591,7 +591,7 @@ static void room_106_daemon() {
 		break;
 	case 102:
 		kernel_abort_animation(aa[6]);
-		aainfo[6]._val1 = 0;
+		aainfo[6]._active = 0;
 		kernel_reset_animation(scratch._9a, 1);
 		kernel_synch(KERNEL_ANIM, scratch._9a, KERNEL_NOW, 0);
 		global[g133] = 0;
@@ -605,7 +605,7 @@ static void room_106_daemon() {
 		break;
 	case 104:
 		kernel_abort_animation(aa[7]);
-		aainfo[7]._val1 = 0;
+		aainfo[7]._active = 0;
 		global[g131] = -1;
 		global[g141] = -1;
 		kernel_reset_animation(scratch._9a, 1);
@@ -625,7 +625,7 @@ static void room_106_daemon() {
 		break;
 	case 107:
 		kernel_abort_animation(aa[5]);
-		aainfo[5]._val1 = 0;
+		aainfo[5]._active = 0;
 		kernel_reset_animation(scratch._9c, 1);
 		kernel_synch(KERNEL_ANIM, scratch._9c, KERNEL_NOW, 0);
 		global[g143] = 0;
@@ -651,7 +651,7 @@ static void room_106_daemon() {
 			scratch._ac = 3;
 		} else if (scratch._ac == 3) {
 			kernel_abort_animation(aa[8]);
-			aainfo[8]._val1 = 0;
+			aainfo[8]._active = 0;
 			global[g131] = -1;
 			global[g141] = -1;
 			kernel_reset_animation(scratch._9a, 1);
@@ -676,16 +676,16 @@ static void room_106_daemon() {
 		global_anim1(2, scratch._9a, global[g131], &global[g132]);
 		global_anim2(1, scratch._9c, global[g141], &global[g142]);
 	}
-	if (aainfo[0]._val1 != 0)  room_106_anim3();
-	if (aainfo[1]._val1 != 0)  room_106_anim6();
-	if (aainfo[2]._val1 != 0)  room_106_anim7();
-	if (aainfo[3]._val1 != 0)  room_106_anim8();
-	if (aainfo[5]._val1 != 0)  room_106_anim9();
-	if (aainfo[4]._val1 != 0)  room_106_anim10();
-	if (aainfo[6]._val1 != 0)  room_106_anim2();
-	if (aainfo[9]._val1 != 0)  room_106_anim1();
-	if (aainfo[7]._val1 != 0)  room_106_anim4();
-	if (aainfo[8]._val1 != 0)  room_106_anim5();
+	if (aainfo[0]._active != 0)  room_106_anim3();
+	if (aainfo[1]._active != 0)  room_106_anim6();
+	if (aainfo[2]._active != 0)  room_106_anim7();
+	if (aainfo[3]._active != 0)  room_106_anim8();
+	if (aainfo[5]._active != 0)  room_106_anim9();
+	if (aainfo[4]._active != 0)  room_106_anim10();
+	if (aainfo[6]._active != 0)  room_106_anim2();
+	if (aainfo[9]._active != 0)  room_106_anim1();
+	if (aainfo[7]._active != 0)  room_106_anim4();
+	if (aainfo[8]._active != 0)  room_106_anim5();
 
 	if (scratch._aa != 0) {
 		kernel_random_frame(scratch._a6, &global[g151], global[g154]);
diff --git a/engines/mads/madsv2/forest/rooms/room107.cpp b/engines/mads/madsv2/forest/rooms/room107.cpp
index a8c4aa9a173..16f210f3e4a 100644
--- a/engines/mads/madsv2/forest/rooms/room107.cpp
+++ b/engines/mads/madsv2/forest/rooms/room107.cpp
@@ -82,19 +82,19 @@ static void room_107_init() {
 	mouse_hide();
 
 	for (int16 i = 0; i < 10; i++) {
-		aainfo[i]._val1 = 0;
-		aainfo[i]._val2 = -1;
+		aainfo[i]._active = 0;
+		aainfo[i]._frame = -1;
 	}
 
 	kernel_timing_trigger(5, 100);
 }
 
 static void room_107_anim1() {
-	int16 prev_frame = aainfo[0]._val2;
+	int16 prev_frame = aainfo[0]._frame;
 	int16 frame = kernel_anim[aa[0]].frame;
 
 	if (frame != prev_frame) {
-		aainfo[0]._val2 = frame;
+		aainfo[0]._frame = frame;
 
 		if (frame == 30) {
 			new_room = 104;
@@ -109,7 +109,7 @@ static void room_107_anim1() {
 				scratch._8e = 21;
 			} else if (frame == 26) {
 				if (aainfo[0]._val3 == 1) {
-					aainfo[0]._val2 = 21;
+					aainfo[0]._frame = 21;
 					kernel_reset_animation(aa[0], 21);
 				}
 			}
@@ -121,7 +121,7 @@ static void room_107_anim1() {
 			aainfo[0]._val3 = 2;
 			scratch._8e = -1;
 			global_digi_play(4);
-			aainfo[0]._val2 = 26;
+			aainfo[0]._frame = 26;
 			kernel_reset_animation(aa[0], 26);
 		}
 	}
@@ -136,11 +136,11 @@ static void room_107_daemon() {
 
 	if (kernel.trigger == 100) {
 		aa[0] = kernel_run_animation(kernel_name('T', 1), 0);
-		aainfo[0]._val1 = -1;
+		aainfo[0]._active = -1;
 		scratch._8c = 30;
 	}
 
-	if (aainfo[0]._val1 != 0)
+	if (aainfo[0]._active != 0)
 		room_107_anim1();
 }
 
diff --git a/engines/mads/madsv2/forest/rooms/room199.cpp b/engines/mads/madsv2/forest/rooms/room199.cpp
index beffb90cfe8..35abb9125ac 100644
--- a/engines/mads/madsv2/forest/rooms/room199.cpp
+++ b/engines/mads/madsv2/forest/rooms/room199.cpp
@@ -92,10 +92,10 @@ static void room_199_init1() {
 		seq[3] = kernel_load_series("*rm199n14", 0); aa[9] = kernel_seq_stamp(seq[3], false, 1); kernel_seq_depth(aa[9], 5);
 	}
 	if (flags[32] >= 1) {
-		seq[4] = kernel_load_series("*rm199n15", 0); aainfo[0]._val1 = kernel_seq_stamp(seq[4], false, 1); kernel_seq_depth(aainfo[0]._val1, 5);
+		seq[4] = kernel_load_series("*rm199n15", 0); aainfo[0]._active = kernel_seq_stamp(seq[4], false, 1); kernel_seq_depth(aainfo[0]._active, 5);
 	}
 	if (flags[33] >= 1) {
-		seq[5] = kernel_load_series("*rm199n16", 0); aainfo[0]._val2 = kernel_seq_stamp(seq[5], false, 1); kernel_seq_depth(aainfo[0]._val2, 5);
+		seq[5] = kernel_load_series("*rm199n16", 0); aainfo[0]._frame = kernel_seq_stamp(seq[5], false, 1); kernel_seq_depth(aainfo[0]._frame, 5);
 	}
 }
 
@@ -106,50 +106,50 @@ static void room_199_init() {
 
 	aainfo[2]._val3 = -1;
 	aainfo[2]._val4 = 0;
-	aainfo[3]._val1 = 0;
-	aainfo[3]._val2 = -1;
+	aainfo[3]._active = 0;
+	aainfo[3]._frame = -1;
 
 	if (previous_room == 304 || previous_room == 402) {
 		if (global[g070] != 0) {
-			aainfo[2]._val2 = 1;
+			aainfo[2]._frame = 1;
 			ss[1] = kernel_load_series("*rm199z1", 0);
 			kernel_flip_hotspot(99, false);
 			kernel_flip_hotspot(174, false);
-			int16 slot = aainfo[2]._val2;
+			int16 slot = aainfo[2]._frame;
 			seq[6 + slot] = kernel_seq_stamp(ss[slot], false, 1);
 			kernel_seq_depth(seq[6 + slot], 1);
-			aainfo[2]._val1 = slot;
+			aainfo[2]._active = slot;
 			player.commands_allowed = 0;
 			kernel_timing_trigger(300, 111);
 			return;
 		}
 		room_199_init1();
-		aainfo[2]._val2 = 0;
+		aainfo[2]._frame = 0;
 		ss[0] = kernel_load_series("*rm199n1", 0);
 	} else if (previous_room == 503) {
-		aainfo[2]._val2 = 3;
+		aainfo[2]._frame = 3;
 		ss[3] = kernel_load_series("*rm199v1", 0);
 		kernel_flip_hotspot(99, false);
 		kernel_flip_hotspot(174, false);
-		int16 slot = aainfo[2]._val2;
+		int16 slot = aainfo[2]._frame;
 		seq[6 + slot] = kernel_seq_stamp(ss[slot], false, 1);
 		kernel_seq_depth(seq[6 + slot], 5);
-		aainfo[2]._val1 = slot;
+		aainfo[2]._active = slot;
 		player.commands_allowed = 0;
 		kernel_timing_trigger(2, 112);
 		return;
 	} else {
 		room_199_init1();
-		aainfo[2]._val2 = 0;
+		aainfo[2]._frame = 0;
 		ss[0] = kernel_load_series("*rm199n1", 0);
 	}
 
 	kernel_flip_hotspot(99, false);
 	kernel_flip_hotspot(174, false);
-	int16 slot = aainfo[2]._val2;
+	int16 slot = aainfo[2]._frame;
 	seq[6 + slot] = kernel_seq_stamp(ss[slot], false, 1);
 	kernel_seq_depth(seq[6 + slot], 1);
-	aainfo[2]._val1 = slot;
+	aainfo[2]._active = slot;
 }
 
 static int room_199_anim1() {
@@ -224,7 +224,7 @@ static void room_199_anim2() {
 	case 14:
 		aainfo[2]._val4 = 0;
 		aainfo[2]._val3 = -1;
-		aainfo[3]._val1 = 0;
+		aainfo[3]._active = 0;
 		kernel_timing_trigger(2, 113);
 		return;
 	default:
@@ -350,10 +350,10 @@ static void room_199_anim4() {
 	case 11:
 		if (player_has(7)) {
 			seq[4] = kernel_load_series("*ob007i", 0);
-			aainfo[0]._val1 = kernel_seq_stamp(seq[4], false, 1);
-			kernel_seq_depth(aainfo[0]._val1, 1);
-			kernel_seq_loc(aainfo[0]._val1, 270, 144);
-			kernel_seq_scale(aainfo[0]._val1, 85);
+			aainfo[0]._active = kernel_seq_stamp(seq[4], false, 1);
+			kernel_seq_depth(aainfo[0]._active, 1);
+			kernel_seq_loc(aainfo[0]._active, 270, 144);
+			kernel_seq_scale(aainfo[0]._active, 85);
 		}
 		kernel_timing_trigger(15, 115); return;
 	case 12:
@@ -370,8 +370,8 @@ static void room_199_daemon() {
 	int16 trig = kernel.trigger;
 
 	if (trig == 7) {
-		if (aainfo[3]._val2 == 11) {
-			aainfo[3]._val2 = -1;
+		if (aainfo[3]._frame == 11) {
+			aainfo[3]._frame = -1;
 			kernel_timing_trigger(30, 113);
 		}
 	} else if (trig == 111) {
@@ -390,10 +390,10 @@ static void room_199_daemon() {
 		global[g100] = result;
 		if (result) {
 			digi_play_build(521, 'e', 11, 1);
-			aainfo[3]._val2 = 11;
+			aainfo[3]._frame = 11;
 		} else {
 			aainfo[2]._val3 = 1;
-			aainfo[3]._val1 = -1;
+			aainfo[3]._active = -1;
 			digi_play_build(521, 'e', 12, 1);
 			room_199_anim2();
 		}
@@ -403,7 +403,7 @@ static void room_199_daemon() {
 
 	if (aainfo[2]._val4 != 0)
 		room_199_anim4();
-	if (aainfo[3]._val1 != 0)
+	if (aainfo[3]._active != 0)
 		room_199_anim2();
 }
 
@@ -413,10 +413,10 @@ static void room_199_pre_parser() {
 
 static void room_199_parser2() {
 	if (flags[33] >= 1) {
-		kernel_seq_delete(aainfo[0]._val2); matte_deallocate_series(seq[5], -1);
+		kernel_seq_delete(aainfo[0]._frame); matte_deallocate_series(seq[5], -1);
 	}
 	if (flags[32] >= 1) {
-		kernel_seq_delete(aainfo[0]._val1); matte_deallocate_series(seq[4], -1);
+		kernel_seq_delete(aainfo[0]._active); matte_deallocate_series(seq[4], -1);
 	}
 	if (flags[30] >= 1) {
 		kernel_seq_delete(aa[9]);            matte_deallocate_series(seq[3], -1);
@@ -460,19 +460,19 @@ static void room_199_parser2() {
 }
 
 static void room_199_parser1() {
-	if (aainfo[2]._val1 == 2)
+	if (aainfo[2]._active == 2)
 		kernel_flip_hotspot(99, 0);
-	if (aainfo[2]._val1 == 3)
+	if (aainfo[2]._active == 3)
 		kernel_flip_hotspot(174, 0);
 
-	int16 old_slot = aainfo[2]._val1;
+	int16 old_slot = aainfo[2]._active;
 	kernel_seq_delete(seq[6 + old_slot]);
 	matte_deallocate_series(ss[old_slot], -1);
 
-	if (aainfo[2]._val1 == 0)
+	if (aainfo[2]._active == 0)
 		room_199_parser2();
 
-	int16 new_slot = aainfo[2]._val2;
+	int16 new_slot = aainfo[2]._frame;
 	switch (new_slot) {
 	case 0:
 		room_199_init1();
@@ -493,7 +493,7 @@ static void room_199_parser1() {
 
 	seq[6 + new_slot] = kernel_seq_stamp(ss[new_slot], false, 1);
 	kernel_seq_depth(seq[6 + new_slot], 1);
-	aainfo[2]._val1 = aainfo[2]._val2;
+	aainfo[2]._active = aainfo[2]._frame;
 	digi_play_build_ii('_', 1, 2);
 }
 
@@ -528,21 +528,21 @@ static void room_199_parser() {
 	}
 
 	if (player_parse(156, 0)) {
-		if (aainfo[2]._val1 != 3) {
-			aainfo[2]._val1 = aainfo[2]._val2;
-			aainfo[2]._val2++;
+		if (aainfo[2]._active != 3) {
+			aainfo[2]._active = aainfo[2]._frame;
+			aainfo[2]._frame++;
 		}
-		if (aainfo[2]._val2 != aainfo[2]._val1)
+		if (aainfo[2]._frame != aainfo[2]._active)
 			room_199_parser1();
 		goto handled;
 	}
 
 	if (player_parse(90, 0)) {
-		if (aainfo[2]._val1 != 0) {
-			aainfo[2]._val1 = aainfo[2]._val2;
-			aainfo[2]._val2--;
+		if (aainfo[2]._active != 0) {
+			aainfo[2]._active = aainfo[2]._frame;
+			aainfo[2]._frame--;
 		}
-		if (aainfo[2]._val2 != aainfo[2]._val1)
+		if (aainfo[2]._frame != aainfo[2]._active)
 			room_199_parser1();
 		goto handled;
 	}
diff --git a/engines/mads/madsv2/forest/rooms/room201.cpp b/engines/mads/madsv2/forest/rooms/room201.cpp
index c34633d1139..d4344262ba8 100644
--- a/engines/mads/madsv2/forest/rooms/room201.cpp
+++ b/engines/mads/madsv2/forest/rooms/room201.cpp
@@ -119,12 +119,12 @@ static void room_201_init1() {
 		return;
 	case 106:
 		aa[0] = kernel_run_animation(kernel_name('y', 1), 100);
-		aainfo[0]._val1 = -1;
+		aainfo[0]._active = -1;
 		scratch._92 = 65;
 		return;
 	case 203:
 		aa[0] = kernel_run_animation(kernel_name('y', 2), 100);
-		aainfo[0]._val1 = -1;
+		aainfo[0]._active = -1;
 		scratch._92 = 75;
 		return;
 	case 199:
@@ -152,19 +152,19 @@ static void room_201_init1() {
 }
 
 static void room_201_anim1() {
-	if (kernel_anim[aa[0]].frame != aainfo[0]._val2)
-		aainfo[0]._val2 = kernel_anim[aa[0]].frame;
+	if (kernel_anim[aa[0]].frame != aainfo[0]._frame)
+		aainfo[0]._frame = kernel_anim[aa[0]].frame;
 
 	if (global[player_hyperwalked] != -1)
 		return;
 
-	aainfo[0]._val2 = scratch._92 - 1;
+	aainfo[0]._frame = scratch._92 - 1;
 	kernel_reset_animation(aa[0], scratch._92 - 1);
 }
 
 static void room_201_anim2() {
-	if (kernel_anim[aa[0]].frame != aainfo[0]._val2)
-		aainfo[0]._val2 = kernel_anim[aa[0]].frame;
+	if (kernel_anim[aa[0]].frame != aainfo[0]._frame)
+		aainfo[0]._frame = kernel_anim[aa[0]].frame;
 
 	if (scratch._a4 != 0)
 		kernel_random_frame(scratch._a2, &global[g153], global[g156]);
@@ -186,7 +186,7 @@ static void room_201_anim2() {
 		scratch._a4 = -1;
 		player.commands_allowed = -1;
 		player.walker_visible = -1;
-		aainfo[1]._val1 = 0;
+		aainfo[1]._active = 0;
 		kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
 		global[g131] = -1;
 		kernel_reset_animation(scratch._9a, 1);
@@ -201,32 +201,32 @@ static void room_201_anim2() {
 }
 
 static void room_201_anim3() {
-	if (kernel_anim[aa[3]].frame != aainfo[3]._val2)
-		aainfo[3]._val2 = kernel_anim[aa[3]].frame;
+	if (kernel_anim[aa[3]].frame != aainfo[3]._frame)
+		aainfo[3]._frame = kernel_anim[aa[3]].frame;
 
-	if (global[player_hyperwalked] == -1 && aainfo[3]._val2 <= 23) {
-		aainfo[3]._val2 = 23;
+	if (global[player_hyperwalked] == -1 && aainfo[3]._frame <= 23) {
+		aainfo[3]._frame = 23;
 		kernel_reset_animation(aa[3], 23);
 		kernel_synch(KERNEL_ANIM, aa[3], KERNEL_NOW, 0);
 		return;
 	}
 
-	if (global[player_hyperwalked] == -1 || aainfo[3]._val2 == 45) {
-		aainfo[3]._val2 = 44;
+	if (global[player_hyperwalked] == -1 || aainfo[3]._frame == 45) {
+		aainfo[3]._frame = 44;
 		kernel_reset_animation(aa[3], 44);
 		kernel_synch(KERNEL_ANIM, aa[3], KERNEL_NOW, 0);
 	}
 }
 
 static void room_201_anim4() {
-	if (kernel_anim[aa[4]].frame != aainfo[4]._val2) {
-		aainfo[4]._val2 = kernel_anim[aa[4]].frame;
-		int16 frame = aainfo[4]._val2;
+	if (kernel_anim[aa[4]].frame != aainfo[4]._frame) {
+		aainfo[4]._frame = kernel_anim[aa[4]].frame;
+		int16 frame = aainfo[4]._frame;
 
 		if (scratch._a8 == 1) {
 			if (frame == 10) {
 				if (aainfo[4]._val3 == 10) {
-					aainfo[4]._val2 = 9;
+					aainfo[4]._frame = 9;
 					kernel_reset_animation(aa[4], 9);
 				}
 			} else if (frame <= 10) {
@@ -236,7 +236,7 @@ static void room_201_anim4() {
 					scratch._a4 = 28;
 				} else if (frame == 8) {
 					if (aainfo[4]._val3 == 9) {
-						aainfo[4]._val2 = 4;
+						aainfo[4]._frame = 4;
 						kernel_reset_animation(aa[4], 4);
 					}
 				}
@@ -244,7 +244,7 @@ static void room_201_anim4() {
 		} else if (scratch._a8 == 2) {
 			if (frame == 13) {
 				if (aainfo[4]._val3 == 10) {
-					aainfo[4]._val2 = 11;
+					aainfo[4]._frame = 11;
 					kernel_reset_animation(aa[4], 11);
 				}
 			} else if (frame <= 13) {
@@ -254,7 +254,7 @@ static void room_201_anim4() {
 					scratch._a4 = 450;
 				} else if (frame == 10) {
 					if (aainfo[4]._val3 == 9) {
-						aainfo[4]._val2 = 5;
+						aainfo[4]._frame = 5;
 						kernel_reset_animation(aa[4], 5);
 					}
 				}
@@ -273,14 +273,14 @@ static void room_201_anim4() {
 		return;
 	case 29:
 		aainfo[4]._val3 = 9;
-		aainfo[4]._val2 = 4;
+		aainfo[4]._frame = 4;
 		kernel_reset_animation(aa[4], 4);
 		digi_play_build_ii('e', 10, 1);
 		scratch._a4 = 30;
 		return;
 	case 30:
 		aainfo[4]._val3 = 11;
-		aainfo[4]._val2 = 9;
+		aainfo[4]._frame = 9;
 		kernel_reset_animation(aa[4], 9);
 		scratch._a4 = -1;
 		return;
@@ -291,14 +291,14 @@ static void room_201_anim4() {
 		return;
 	case 451:
 		aainfo[4]._val3 = 9;
-		aainfo[4]._val2 = 5;
+		aainfo[4]._frame = 5;
 		kernel_reset_animation(aa[4], 5);
 		digi_play_build_ii('e', 10, 1);
 		scratch._a4 = 452;
 		return;
 	case 452:
 		aainfo[4]._val3 = 11;
-		aainfo[4]._val2 = 10;
+		aainfo[4]._frame = 10;
 		kernel_reset_animation(aa[4], 10);
 		scratch._a4 = -1;
 		return;
@@ -351,8 +351,8 @@ static void room_201_init() {
 	scratch._ac = 0;
 	scratch._b0 = 0;
 	for (int i = 0; i < 10; i++) {
-		aainfo[i]._val1 = 0;
-		aainfo[i]._val2 = 1;
+		aainfo[i]._active = 0;
+		aainfo[i]._frame = 1;
 		aainfo[i]._val3 = 0;
 		aainfo[i]._val4 = 0;
 	}
@@ -408,7 +408,7 @@ static void room_201_daemon() {
 	} else if (trigger > 26) {
 		if (trigger == 100) {
 			kernel_abort_animation(aa[0]);
-			aainfo[0]._val1 = 0;
+			aainfo[0]._active = 0;
 			if (flags[7] == 1) {
 				kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
 				global[g141] = -1;
@@ -421,7 +421,7 @@ static void room_201_daemon() {
 				global[g156] = 2;
 				global[g131] = -1;
 				player.walker_visible = -1;
-				aainfo[1]._val1 = -1;
+				aainfo[1]._active = -1;
 				global[g133] = 1;
 				scratch._a2 = kernel_run_animation_talk('e', 1, 0);
 				kernel_position_anim(scratch._a2, 186, 115, 76, 7);
@@ -444,14 +444,14 @@ static void room_201_daemon() {
 			}
 		} else if (trigger == 101) {
 			kernel_abort_animation(aa[scratch._94]);
-			aainfo[scratch._94]._val1 = 0;
+			aainfo[scratch._94]._active = 0;
 			kernel_reset_animation(scratch._9c, 1);
 			kernel_synch(KERNEL_ANIM, scratch._9c, KERNEL_NOW, 0);
 			global[g143] = 0;
 			player.commands_allowed = -1;
 		} else if (trigger == 102) {
 			kernel_abort_animation(aa[scratch._96]);
-			aainfo[scratch._96]._val1 = 0;
+			aainfo[scratch._96]._active = 0;
 			kernel_reset_animation(scratch._9a, 1);
 			kernel_synch(KERNEL_ANIM, scratch._9a, KERNEL_NOW, 0);
 			global[g133] = 0;
@@ -525,14 +525,14 @@ static void room_201_daemon() {
 		if (scratch._8c == 5) {
 			if (scratch._a8 == 1) {
 				aa[4] = kernel_run_animation(kernel_name('F', 1), 102);
-				aainfo[4]._val1 = -1;
+				aainfo[4]._active = -1;
 				scratch._96 = 4;
 				kernel_reset_animation(scratch._9a, 0);
 				kernel_synch(KERNEL_ANIM, aa[4], KERNEL_ANIM, scratch._9a);
 				global[g133] = 1;
 			} else if (scratch._a8 == 2) {
 				aa[4] = kernel_run_animation(kernel_name('F', 2), 102);
-				aainfo[4]._val1 = -1;
+				aainfo[4]._active = -1;
 				scratch._96 = 4;
 				kernel_reset_animation(scratch._9a, 0);
 				kernel_synch(KERNEL_ANIM, aa[4], KERNEL_ANIM, scratch._9a);
@@ -542,7 +542,7 @@ static void room_201_daemon() {
 	} else if (trigger == 25) {
 		if (scratch._8e == 2) {
 			aa[3] = kernel_run_animation(kernel_name('R', 1), 101);
-			aainfo[3]._val1 = -1;
+			aainfo[3]._active = -1;
 			scratch._94 = 3;
 			kernel_reset_animation(scratch._9c, 0);
 			kernel_synch(KERNEL_ANIM, aa[3], KERNEL_ANIM, scratch._9c);
@@ -560,10 +560,10 @@ static void room_201_daemon() {
 	global_anim1(1, scratch._9a, global[g131], &global[g132]);
 	global_anim2(1, scratch._9c, global[g141], &global[g142]);
 
-	if (aainfo[0]._val1 != 0) room_201_anim1();
-	if (aainfo[3]._val1 != 0) room_201_anim3();
-	if (aainfo[4]._val1 != 0) room_201_anim4();
-	if (aainfo[1]._val1 != 0) room_201_anim2();
+	if (aainfo[0]._active != 0) room_201_anim1();
+	if (aainfo[3]._active != 0) room_201_anim3();
+	if (aainfo[4]._active != 0) room_201_anim4();
+	if (aainfo[1]._active != 0) room_201_anim2();
 	if (scratch._ac != 0) room_201_anim5();
 	if (scratch._b0 != 0) room_201_anim6();
 }
@@ -579,8 +579,7 @@ static void room_201_parser() {
 	if (global[walker_converse_state] != 0) {
 		player.commands_allowed = 0;
 		digi_play_build_ii('c', 1, 1);
-		player.command_ready = false;
-		return;
+		goto handled;
 	}
 
 	if (player_parse(93, 0)) {
@@ -593,8 +592,7 @@ static void room_201_parser() {
 		digi_play_build_ii('b', 3, 1);
 		scratch._a4 = 40;
 		scratch._a6 = -1;
-		player.command_ready = false;
-		return;
+		goto handled;
 	}
 
 	if (player_parse(145, 0)) {
@@ -607,8 +605,7 @@ static void room_201_parser() {
 		digi_play_build_ii('b', 3, 1);
 		scratch._a4 = 41;
 		scratch._a6 = -1;
-		player.command_ready = false;
-		return;
+		goto handled;
 	}
 
 	if (player_parse(114, 77, 0)) {
@@ -616,8 +613,7 @@ static void room_201_parser() {
 		global[g135] = -1;
 		scratch._8c = 5;
 		scratch._a8 = 1;
-		player.command_ready = false;
-		return;
+		goto handled;
 	}
 
 	if (player_parse(114, 103, 0)) {
@@ -630,33 +626,35 @@ static void room_201_parser() {
 	}
 
 	if (global[player_selected_object] >= 0) {
-		player.command_ready = false;
-		return;
+		goto handled;
 	}
 
 	if (player_parse(114, 116, 0)) {
 		player.commands_allowed = 0;
 		global[g145] = -1;
 		scratch._8e = 2;
-		player.command_ready = false;
-		return;
+		goto handled;
 	}
 
 	if (player_parse(99, 0)) {
 		player.commands_allowed = 0;
 		global[g135] = -1;
 		scratch._8c = 3;
-		player.command_ready = false;
-		return;
+		goto handled;
 	}
 
 	if (player_parse(78, 119, 0)) {
 		player.commands_allowed = 0;
 		global[g135] = -1;
 		scratch._8c = 3;
-		player.command_ready = false;
-		return;
+		goto handled;
 	}
+
+	goto done;
+handled:
+	player.command_ready = false;
+done:
+	;
 }
 
 void room_201_synchronize(Common::Serializer &s) {
diff --git a/engines/mads/madsv2/forest/rooms/room203.cpp b/engines/mads/madsv2/forest/rooms/room203.cpp
index e9a0d88dedf..ae9209a5a7e 100644
--- a/engines/mads/madsv2/forest/rooms/room203.cpp
+++ b/engines/mads/madsv2/forest/rooms/room203.cpp
@@ -21,8 +21,10 @@
 
 #include "mads/madsv2/core/digi.h"
 #include "mads/madsv2/core/game.h"
+#include "mads/madsv2/core/imath.h"
 #include "mads/madsv2/core/inter.h"
 #include "mads/madsv2/core/kernel.h"
+#include "mads/madsv2/core/midi.h"
 #include "mads/madsv2/core/object.h"
 #include "mads/madsv2/core/player.h"
 #include "mads/madsv2/forest/extra.h"
@@ -56,7 +58,7 @@ struct Scratch {
 	int16 _a4;                       /* 0xA4 */
 	int16 _a6;                       /* 0xA6 */
 	int16 _a8;                       /* 0xA8 */
-	int16 _paa;                      /* 0xAA */
+	int16 _aa;                      /* 0xAA */
 	int16 _ac;                       /* 0xAC */
 	int16 _ae;                       /* 0xAE */
 	int16 _b0;                       /* 0xB0 */
@@ -102,25 +104,25 @@ static void room_203_init1() {
 
 	if (previous_room == 201) {
 		aa[0] = kernel_run_animation(kernel_name('y', 1), 101);
-		aainfo[0]._val1 = -1;
+		aainfo[0]._active = -1;
 		scratch._9a = 11;
 		scratch._92 = 60;
 		if (flags[8] == 1) {
 			aa[4] = kernel_run_animation(kernel_name('w', 1), 105);
-			aainfo[4]._val1 = -1;
-			aainfo[4]._val2 = 0;
+			aainfo[4]._active = -1;
+			aainfo[4]._frame = 0;
 			scratch._9c = 60;
 		}
 		return;
 	} else if (previous_room == 204) {
 		aa[0] = kernel_run_animation(kernel_name('y', 2), 101);
-		aainfo[0]._val1 = -1;
+		aainfo[0]._active = -1;
 		scratch._9a = 11;
 		scratch._92 = 99;
 		return;
 	} else if (previous_room == 210) {
 		aa[0] = kernel_run_animation(kernel_name('y', 3), 101);
-		aainfo[0]._val1 = -1;
+		aainfo[0]._active = -1;
 		scratch._9a = 11;
 		scratch._92 = 74;
 		global_digi_play(11);
@@ -144,8 +146,8 @@ static void room_203_init1() {
 		global_digi_play(15);
 		scratch._a6 = 2;
 		aa[4] = kernel_run_animation(kernel_name('w', 4), 105);
-		aainfo[4]._val1 = -1;
-		aainfo[4]._val2 = 0;
+		aainfo[4]._active = -1;
+		aainfo[4]._frame = 0;
 		scratch._9c = 63;
 		player.walker_visible = -1;
 		kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
@@ -157,8 +159,8 @@ static void room_203_init1() {
 		if (global[g049] != 0) {
 			aa[3] = kernel_run_animation(kernel_name('a', 1), 104);
 			scratch._98 = 30;
-			aainfo[3]._val1 = -1;
-			aainfo[3]._val2 = 25;
+			aainfo[3]._active = -1;
+			aainfo[3]._frame = 25;
 			kernel_reset_animation(aa[3], 25);
 			global[g049] = -1;
 			player.walker_visible = 0;
@@ -218,8 +220,8 @@ static void room_203_init() {
 		}
 
 		for (int i = 0; i < 10; i++) {
-			aainfo[i]._val1 = 0;
-			aainfo[i]._val2 = 1;
+			aainfo[i]._active = 0;
+			aainfo[i]._frame = 1;
 			aainfo[i]._val3 = 0;
 			aainfo[i]._val4 = 0;
 		}
@@ -231,81 +233,844 @@ static void room_203_init() {
 	room_203_init1();
 }
 
+static void room_203_anim5();
+
+static void room_203_anim1() {
+	int result = -1;
+	int frame;
+
+	if (global[player_hyperwalked])
+		local->_b0 = true;
+
+	if (kernel_anim[aa[0]].frame == aainfo[0]._frame)
+		return;
+	aainfo[0]._frame = kernel_anim[aa[0]].frame;
+
+	switch (local->_9a) {
+	case 11:
+		if (local->_9c == 60) {
+			frame = aainfo[0]._frame;
+			if (frame == 50) {
+				aainfo[4]._frame = 22;
+				kernel_reset_animation(aa[4], 22);
+			} else if (frame < 50) {
+				if (frame == 22) {
+					if (local->_a6 != 1) {
+						global_digi_play(10);
+						local->_a6 = 1;
+					}
+					aainfo[4]._frame = 3;
+					kernel_reset_animation(aa[4], 3);
+				} else if (frame == 33) {
+					aainfo[4]._frame = 8;
+					kernel_reset_animation(aa[4], 8);
+				}
+			}
+		}
+
+		if (aainfo[0]._frame == 1) {
+			if (global[g046] == 0 && flags[8] > 1 && local->_a6 != 1) {
+				global_digi_play(10);
+				local->_a6 = 1;
+			}
+		}
+
+		if (local->_b0) {
+			result = local->_92 - 1;
+			if (local->_9c == 60) {
+				aainfo[4]._frame = 22;
+				kernel_abort_animation(aa[4]);
+				aainfo[4]._active = 0;
+			}
+			local->_b0 = 0;
+		}
+		break;
+
+	case 12:
+		switch (aainfo[0]._frame) {
+		case 4:
+			digi_play_build(203, 'E', 1, 1);
+			local->_a4 = 11;
+			break;
+		case 8:
+			result = 4;
+			break;
+		case 11:
+			result = 9;
+			break;
+		case 12:
+			digi_play_build(203, 'R', 1, 1);
+			local->_a4 = 21;
+			break;
+		case 16:
+			result = 12;
+			break;
+		case 19:
+			result = 17;
+			break;
+		case 20:
+			digi_play_build(203, 'B', 1, 1);
+			local->_a4 = 31;
+			break;
+		case 24:
+			result = 20;
+			break;
+		case 27:
+			result = 25;
+			break;
+		case 28:
+			digi_play_build(203, 'E', 2, 1);
+			local->_a4 = 12;
+			break;
+		case 32:
+			result = 28;
+			break;
+		case 35:
+			result = 33;
+			break;
+		case 36:
+			digi_play_build(203, 'R', 2, 1);
+			local->_a4 = 22;
+			aainfo[4]._frame = 13;
+			kernel_reset_animation(aa[4], 13);
+			break;
+		case 40:
+			result = 36;
+			break;
+		case 42:
+			if (aainfo[4]._frame > 20)
+				result = 44;
+			break;
+		case 43:
+			result = 41;
+			break;
+		case 50:
+			if (aainfo[4]._frame < 27) {
+				result = 49;
+			} else {
+				dont_frag_the_palette();
+				kernel_abort_animation(aa[4]);
+				aainfo[4]._active = 0;
+				dont_frag_the_palette();
+				kernel_abort_animation(aa[0]);
+				aainfo[0]._active = 0;
+				aa[0] = kernel_run_animation(kernel_name('x', 3), 101);
+				aainfo[0]._active = -1;
+				aainfo[0]._frame = 0;
+				local->_9a = 13;
+				aa[4] = kernel_run_animation(kernel_name('w', 4), 105);
+				aainfo[4]._active = -1;
+				aainfo[4]._frame = 0;
+				local->_9c = 63;
+			}
+			break;
+		default:
+			break;
+		}
+		break;
+
+	case 13: {
+		frame = aainfo[0]._frame;
+		if (frame == 29) {
+			dont_frag_the_palette();
+			kernel_abort_animation(aa[4]);
+			aainfo[4]._active = 0;
+			dont_frag_the_palette();
+			kernel_abort_animation(aa[0]);
+			aainfo[0]._active = 0;
+			room_203_anim5();
+		} else if (frame < 29) {
+			if (frame == 22) {
+				result = 20;
+			} else if (frame < 22) {
+				if (frame == 1) {
+					digi_play_build(203, 'B', 2, 1);
+					local->_a4 = 32;
+				} else if (frame == 5) {
+					result = 1;
+				} else if (frame == 20) {
+					local->_a4 = 23;
+					if (flags[8] == 1) {
+						digi_play_build(203, 'R', 3, 1);
+					} else {
+						digi_play("NULL", 1);
+						result = 23;
+					}
+				}
+			} else {
+				if (frame == 24) {
+					result = 23;
+				} else if (frame == 25) {
+					local->_a4 = 13;
+					if (flags[8] == 1) {
+						digi_play_build(203, 'E', 3, 1);
+					} else {
+						digi_play("NULL", 1);
+						result = 23;
+					}
+				} else if (frame == 27) {
+					result = 25;
+				}
+			}
+		}
+		break;
+
+	case 15:
+		if (aainfo[0]._frame == 1) {
+			digi_play_build(203, '_', 500, 2);
+		}
+		break;
+
+	case 16:
+		switch (aainfo[0]._frame) {
+		case 6:
+			digi_play_build(203, 'E', 6, 1);
+			local->_a4 = 16;
+			break;
+		case 11:
+			result = 6;
+			break;
+		case 51:
+			digi_play_build(203, 'E', 7, 1);
+			local->_a4 = 17;
+			break;
+		case 56:
+			result = 51;
+			break;
+		default:
+			break;
+		}
+		break;
+	}
+
+	default:
+		break;
+	}
+
+	if (result >= 0 && aainfo[0]._active) {
+		aainfo[0]._frame = result;
+		kernel_reset_animation(aa[0], result);
+	}
+}
+
+static void room_203_anim2() {
+	int result = -1;
+
+	if (kernel_anim[aa[1]].frame != aainfo[1]._frame) {
+		aainfo[1]._frame = kernel_anim[aa[1]].frame;
+
+		switch (local->_96) {
+		case 52:
+		case 57:
+			if (aainfo[1]._frame == 4) {
+				if (local->_96 == 52) {
+					digi_play_build(203, 'e', 4, 1);
+					local->_a4 = 14;
+				} else {
+					digi_play_build(203, 'e', 5, 1);
+					local->_a4 = 15;
+				}
+			} else if (aainfo[1]._frame == 10) {
+				result = 4;
+			}
+			break;
+
+		case 53:
+			if (aainfo[1]._frame == 5)
+				digi_play_build(203, '_', 2, 2);
+			break;
+
+		case 54:
+		case 55:
+			switch (aainfo[1]._frame) {
+			case 1:
+				aainfo[1]._val4++;
+				if (aainfo[1]._val4 > 8)
+					aainfo[1]._val4 = 0;
+				else
+					result = 0;
+				break;
+			case 2:
+			case 9:
+			case 20:
+			case 29:
+				digi_play_build(203, '_', 2, 2);
+				break;
+			case 6:
+			case 24:
+				digi_play_build(203, '_', 3, 2);
+				break;
+			case 8:
+			case 26:
+				digi_play_build(203, '_', 800, 1);
+				local->_a4 = 54;
+				break;
+			case 16:
+				if (local->_ac)
+					result = 35;
+				break;
+			case 18:
+				aainfo[1]._val4++;
+				if (aainfo[1]._val4 > 4)
+					aainfo[1]._val4 = 0;
+				else
+					result = 17;
+				break;
+			case 33:
+				aainfo[1]._val4++;
+				if (aainfo[1]._val4 > 4)
+					aainfo[1]._val4 = 0;
+				else
+					result = 32;
+				break;
+			case 34:
+				local->_a8++;
+				if (local->_a8 <= 2) {
+					result = 1;
+				} else {
+					dont_frag_the_palette();
+					kernel_abort_animation(aa[1]);
+					aainfo[1]._active = 0;
+					dont_frag_the_palette();
+					kernel_abort_animation(aa[4]);
+					aainfo[4]._active = 0;
+					aa[5] = kernel_run_animation(kernel_name('h', 1), 0);
+					aainfo[5]._active = -1;
+					aainfo[5]._frame = 0;
+					aa[4] = kernel_run_animation(kernel_name('w', 5), 105);
+					aainfo[4]._active = -1;
+					aainfo[4]._frame = 0;
+					local->_9c = 64;
+				}
+				break;
+			case 39:
+				dont_frag_the_palette();
+				kernel_abort_animation(aa[1]);
+				aainfo[1]._active = 0;
+				aainfo[5]._frame = 1;
+				kernel_reset_animation(aa[5], 1);
+				kernel_synch(KERNEL_ANIM, aa[5], KERNEL_NOW, 0);
+				aainfo[4]._frame = 1;
+				kernel_reset_animation(aa[4], 1);
+				kernel_synch(KERNEL_ANIM, aa[4], KERNEL_NOW, 0);
+				aa[2] = kernel_run_animation(kernel_name('R', 3), 102);
+				aainfo[2]._active = -1;
+				aainfo[2]._frame = 0;
+				local->_94 = 41;
+				local->_a8 = 0;
+				kernel_reset_animation(local->_a0, 0);
+				kernel_synch(KERNEL_ANIM, aa[2], KERNEL_ANIM, local->_a0);
+				global[g143] = 1;
+				break;
+			default:
+				break;
+			}
+			break;
+
+		case 56:
+			if (aainfo[1]._frame == 10 && local->_ac) {
+				aainfo[3]._frame = 1;
+				kernel_reset_animation(aa[3], 1);
+			}
+			break;
+
+		default:
+			break;
+		}
+
+		if (result >= 0 && aainfo[1]._active) {
+			aainfo[1]._frame = result;
+			kernel_reset_animation(aa[1], result);
+		}
+	}
+
+	if (global[player_hyperwalked] && local->_96 == 50) {
+		aainfo[1]._frame = 59;
+		kernel_reset_animation(aa[1], 59);
+		kernel_synch(KERNEL_ANIM, aa[1], KERNEL_NOW, 0);
+	}
+}
+
+static void room_203_anim3() {
+	int result = -1;
+	int16 frame;
+
+	if (kernel_anim[aa[2]].frame == aainfo[2]._frame)
+		return;
+	aainfo[2]._frame = kernel_anim[aa[2]].frame;
+
+	switch (local->_94) {
+	case 41:
+		frame = aainfo[2]._frame;
+		if (frame == 8) {
+			aainfo[4]._frame = 11;
+			kernel_reset_animation(aa[4], 11);
+			digi_play_build(203, '_', 1, 2);
+			local->_a8++;
+		} else if (frame == 12) {
+			if (local->_ac) {
+				dont_frag_the_palette();
+				kernel_abort_animation(aa[2]);
+				aainfo[2]._active = 0;
+				kernel_reset_animation(local->_a0, 1);
+				kernel_synch(KERNEL_ANIM, local->_a0, KERNEL_NOW, 0);
+				global[g143] = 0;
+				aainfo[3]._frame = 18;
+				kernel_reset_animation(aa[3], 18);
+			} else if (local->_a8 < 5) {
+				if (imath_random(1, 100) > 80)
+					result = 1;
+				else
+					result = 11;
+			}
+		}
+		break;
+
+	case 42:
+		frame = aainfo[2]._frame;
+		if (frame == 4) {
+			digi_play_build(203, 'R', 6, 1);
+			local->_a4 = 26;
+		} else if (frame == 9) {
+			result = 4;
+		}
+		break;
+
+	case 43:
+		frame = aainfo[2]._frame;
+		if (frame == 20) {
+			result = 13;
+		} else if (frame < 20) {
+			if (frame == 1) {
+				digi_play_build(203, 'R', 4, 1);
+				local->_a4 = 24;
+			} else if (frame == 4) {
+				result = 1;
+			} else if (frame == 10) {
+				result = 6;
+			} else if (frame == 13) {
+				digi_play_build(203, 'R', 5, 1);
+				local->_a4 = 25;
+			}
+		}
+		break;
+
+	default:
+		break;
+	}
+
+	if (result >= 0 && aainfo[2]._active) {
+		aainfo[2]._frame = result;
+		kernel_reset_animation(aa[2], result);
+	}
+}
+
+static void room_203_flip_hotspots1() {
+	kernel_flip_hotspot(65, true);
+	kernel_flip_hotspot(66, true);
+	kernel_flip_hotspot(168, false);
+	kernel_flip_hotspot(170, false);
+	kernel_flip_hotspot(169, false);
+	kernel_flip_hotspot(119, false);
+	kernel_flip_hotspot(27, false);
+	kernel_flip_hotspot(21, false);
+	kernel_flip_hotspot(24, false);
+}
+
+static void room_203_flip_hotspots2() {
+	kernel_flip_hotspot(65, 0);
+	kernel_flip_hotspot(66, 0);
+	kernel_flip_hotspot(119, global[g047] < 1 ? 1 : 0);
+	kernel_flip_hotspot(27, global[g047]);
+	kernel_flip_hotspot(21, -1);
+	kernel_flip_hotspot(24, -1);
+}
+
+static void room_203_anim4() {
+	int result = -1;
+	int frame;
+
+	if (kernel_anim[aa[3]].frame == aainfo[3]._frame)
+		return;
+	aainfo[3]._frame = kernel_anim[aa[3]].frame;
+
+	switch (local->_98) {
+	case 30:
+		frame = aainfo[3]._frame;
+		if (frame == 42) {
+			dont_frag_the_palette();
+			kernel_abort_animation(aa[3]);
+			aainfo[3]._active = 0;
+			room_203_flip_hotspots2();
+			kernel_flip_hotspot(168, -1);
+			kernel_flip_hotspot(170, -1);
+			kernel_flip_hotspot(169, -1);
+			player.walker_visible = -1;
+			kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
+			player.commands_allowed = -1;
+			if (local->_ae) {
+				global[g145] = -1;
+				local->_8e = 41;
+				player.commands_allowed = 0;
+				local->_a8 = 0;
+				local->_ae = 0;
+			}
+		} else if (frame < 42) {
+			if (frame == 29) {
+				digi_play_build(203, 'b', 4, 1);
+				local->_a4 = 34;
+				local->_ac = 0;
+			} else if (frame < 29) {
+				if (frame == 1) {
+					digi_play_build(203, 'b', 3, 1);
+					local->_a4 = 33;
+				} else if (frame == 3) {
+					result = 1;
+				} else if (frame == 26) {
+					room_203_flip_hotspots1();
+					player.commands_allowed = -1;
+				} else if (frame == 28) {
+					result = 27;
+				}
+			} else {
+				if (frame == 33) {
+					result = 29;
+				} else if (frame == 34) {
+					digi_play_build(203, 'b', 5, 1);
+					local->_a4 = 35;
+					local->_ac = -1;
+				} else if (frame == 38) {
+					result = 34;
+				} else if (frame == 40) {
+					dont_frag_the_palette();
+					kernel_abort_animation(aa[3]);
+					aainfo[3]._active = 0;
+					if (local->_ac) {
+						aa[5] = kernel_run_animation(kernel_name('h', 1), 0);
+						aainfo[5]._active = -1;
+						aainfo[5]._frame = 4;
+						kernel_reset_animation(aa[5], 4);
+					}
+					aa[3] = kernel_run_animation(kernel_name('a', 2), 104);
+					aainfo[3]._active = -1;
+					aainfo[3]._frame = 0;
+					local->_98 = 31;
+					player.commands_allowed = 0;
+					room_203_flip_hotspots2();
+				}
+			}
+		}
+		break;
+
+	case 31:
+		frame = aainfo[3]._frame;
+		if (frame == 19) {
+			dont_frag_the_palette();
+			kernel_abort_animation(aa[3]);
+			aainfo[3]._active = 0;
+			aa[3] = kernel_run_animation(kernel_name('a', 3), 104);
+			local->_98 = 32;
+			aainfo[3]._active = -1;
+			aainfo[3]._frame = 0;
+			local->_a8 = 0;
+		} else if (frame < 19) {
+			if (frame == 8) {
+				digi_play_build(101, '_', 1, 2);
+			} else if (frame == 16) {
+				if (local->_ac) {
+					global[g150] = -1;
+					local->_90 = 55;
+				}
+			} else if (frame == 17) {
+				if (local->_ac)
+					result = 16;
+			}
+		}
+		break;
+
+	case 32:
+		frame = aainfo[3]._frame;
+		if (frame == 40) {
+			dont_frag_the_palette();
+			kernel_abort_animation(aa[3]);
+			aainfo[3]._active = 0;
+			if (local->_ac) {
+				dont_frag_the_palette();
+				kernel_abort_animation(aa[5]);
+				aainfo[5]._active = 0;
+			}
+			dont_frag_the_palette();
+			kernel_abort_animation(aa[4]);
+			aainfo[4]._active = 0;
+			aa[3] = kernel_run_animation(kernel_name('a', 4), 104);
+			local->_98 = 33;
+			aainfo[3]._active = -1;
+			aainfo[3]._frame = 0;
+			if (local->_ac) {
+				aa[5] = kernel_run_animation(kernel_name('h', 1), 0);
+				aainfo[5]._active = -1;
+				aainfo[5]._frame = 1;
+				kernel_reset_animation(aa[5], 1);
+			}
+			aa[4] = kernel_run_animation(kernel_name('w', 5), 105);
+			local->_9c = 64;
+			aainfo[4]._active = -1;
+			aainfo[4]._frame = 0;
+		} else if (frame < 40) {
+			if (frame == 13 || frame == 21 || frame == 29) {
+				aainfo[4]._frame = 1;
+				kernel_reset_animation(aa[4], 1);
+				kernel_synch(KERNEL_ANIM, aa[4], KERNEL_ANIM, aa[3]);
+			} else if (frame < 13) {
+				if (frame == 5) {
+					digi_play_build(203, '_', 4, 2);
+				} else if (frame == 11) {
+					aainfo[4]._frame = 17;
+					kernel_reset_animation(aa[4], 17);
+					kernel_synch(KERNEL_ANIM, aa[4], KERNEL_ANIM, aa[3]);
+					if (imath_random(0, 100) > 65)
+						local->_aa = 0;
+					else
+						local->_aa = -1;
+				} else if (frame == 12) {
+					local->_a8++;
+					digi_play_build(203, '_', 5, 2);
+				}
+			} else {
+				if (frame == 18) {
+					if (local->_a8 > 3 || local->_ac)
+						result = 34;
+				} else if (frame == 34) {
+					result = 9;
+				}
+			}
+		}
+		break;
+
+	case 33:
+		frame = aainfo[3]._frame;
+		if (frame == 63) {
+			result = 60;
+		} else if (frame < 63) {
+			if (frame == 34) {
+				digi_play_build(101, '_', 1, 2);
+				display_interface();
+			} else if (frame < 34) {
+				if (frame == 1) {
+					result = 0;
+				} else if (frame == 4) {
+					digi_play_build(203, '_', 4, 2);
+				}
+			} else {
+				if (frame == 57) {
+					if (local->_ac) {
+						digi_play_build(203, 'b', 9, 1);
+						local->_a4 = 39;
+					} else {
+						digi_play_build(203, 'b', 6, 1);
+						local->_a4 = 36;
+					}
+				} else if (frame == 59) {
+					result = 57;
+				}
+			}
+		}
+		break;
+
+	default:
+		break;
+	}
+
+	if (result >= 0 && aainfo[3]._active) {
+		aainfo[3]._frame = result;
+		kernel_reset_animation(aa[3], result);
+	}
+}
+
+static void room_203_anim5() {
+	int result = -1;
+
+	if (kernel_anim[aa[4]].frame == aainfo[4]._frame)
+		return;
+	aainfo[4]._frame = kernel_anim[aa[4]].frame;
+
+	switch (local->_9c) {
+	case 60: {
+		int16 frame = aainfo[4]._frame;
+		if (frame == 22) {
+			result = 18;
+		} else if (frame < 22) {
+			if (frame == 2)
+				result = 1;
+			else if (frame == 7)
+				result = 5;
+		}
+		break;
+	}
+	case 61:
+		if (aainfo[4]._frame == 13)
+			result = 11;
+		break;
+	case 62: {
+		int16 frame = aainfo[4]._frame;
+		if (frame == 29) {
+			result = 27;
+		} else if (frame < 29) {
+			if (frame == 1) {
+				global_digi_play(12);
+				local->_a6 = 4;
+			} else if (frame == 19) {
+				digi_play_build(103, '_', 3, 2);
+				if (local->_a6 != 2) {
+					global_digi_play(15);
+					local->_a6 = 2;
+				}
+			} else if (frame == 22) {
+				digi_play_build(203, '_', 500, 2);
+			}
+		}
+		break;
+	}
+	case 63: {
+		int16 frame = aainfo[4]._frame;
+		if (frame == 19) {
+			result = 17;
+		} else if (frame < 19) {
+			if (frame == 2) {
+				if (local->_aa) {
+					if (local->_a8 > 1)
+						digi_play_build(203, '_', 500, 2);
+					local->_aa = 0;
+				}
+			} else if (frame == 9) {
+				result = 7;
+			} else if (frame == 13) {
+				result = 1;
+			}
+		}
+		break;
+	}
+	case 64: {
+		int16 frame = aainfo[4]._frame;
+		if (frame == 25) {
+			midi_stop();
+			local->_a6 = 0;
+		} else if (frame < 25) {
+			if (frame == 1) {
+				global_digi_play(5);
+				local->_a6 = 3;
+			} else if (frame == 18) {
+				if (!local->_ac && local->_98 == 33) {
+					aainfo[3]._frame = 1;
+					kernel_reset_animation(aa[3], 1);
+				}
+			}
+		}
+		break;
+	}
+	default:
+		break;
+	}
+
+	if (result >= 0 && aainfo[4]._active) {
+		aainfo[4]._frame = result;
+		kernel_reset_animation(aa[4], result);
+	}
+}
+
+static void room_203_anim6() {
+	int result = -1;
+
+	if (kernel_anim[aa[5]].frame == aainfo[5]._frame)
+		return;
+	aainfo[5]._frame = kernel_anim[aa[5]].frame;
+
+	if (aainfo[5]._frame == 2)
+		result = 1;
+	else if (aainfo[5]._frame == 5)
+		result = 4;
+
+	if (result >= 0) {
+		aainfo[5]._frame = result;
+		kernel_reset_animation(aa[5], result);
+	}
+}
+
 static void room_203_daemon() {
 	switch (kernel.trigger) {
 	case 7:
 		switch (local->_a4) {
 		case 11:
 			kernel_timing_trigger(40, 106);
-			aainfo[0]._val2 = 9;
+			aainfo[0]._frame = 9;
 			kernel_reset_animation(aa[0], 9);
 			break;
 
 		case 12:
 			kernel_timing_trigger(40, 106);
-			aainfo[0]._val2 = 33;
+			aainfo[0]._frame = 33;
 			kernel_reset_animation(aa[0], 33);
 			break;
 
 		case 13:
-			aainfo[0]._val2 = 28;
+			aainfo[0]._frame = 28;
 			kernel_reset_animation(aa[0], 28);
 			break;
 
 		case 14:
 		case 15:
-			aainfo[1]._val2 = 11;
+			aainfo[1]._frame = 11;
 			kernel_reset_animation(aa[1], 11);
 			break;
 
 		case 16:
-			aainfo[0]._val2 = 12;
+			aainfo[0]._frame = 12;
 			kernel_reset_animation(aa[0], 12);
 			break;
 
 		case 17:
-			aainfo[0]._val2 = 57;
+			aainfo[0]._frame = 57;
 			kernel_reset_animation(aa[0], 57);
 			break;
 
 		case 21:
 			kernel_timing_trigger(40, 106);
-			aainfo[0]._val2 = 17;
+			aainfo[0]._frame = 17;
 			kernel_reset_animation(aa[0], 17);
 			break;
 
 		case 22:
 			dont_frag_the_palette();
 			kernel_abort_animation(aa[4]);
-			aainfo[4]._val1 = 0;
+			aainfo[4]._active = 0;
 
 			aa[4] = kernel_run_animation(kernel_name('w', 3), 105);
-			aainfo[4]._val1 = -1;
-			aainfo[4]._val2 = 0;
+			aainfo[4]._active = -1;
+			aainfo[4]._frame = 0;
 			local->_9c = 62;
-			aainfo[0]._val2 = 41;
+			aainfo[0]._frame = 41;
 			kernel_reset_animation(aa[0], 41);
 			break;
 
 		case 23:
 			kernel_timing_trigger(40, 106);
-			aainfo[0]._val2 = 23;
+			aainfo[0]._frame = 23;
 			kernel_reset_animation(aa[0], 23);
 			break;
 
 		case 24:
 			kernel_timing_trigger(40, 106);
-			aainfo[2]._val2 = 6;
+			aainfo[2]._frame = 6;
 			kernel_reset_animation(aa[2], 6);
 			break;
 
 		case 25:
-			aainfo[2]._val2 = 20;
+			aainfo[2]._frame = 20;
 			kernel_reset_animation(aa[2], 20);
 			break;
 
 		case 26:
-			aainfo[2]._val2 = 10;
+			aainfo[2]._frame = 10;
 			kernel_reset_animation(aa[2], 10);
 			break;
 
@@ -321,41 +1086,41 @@ static void room_203_daemon() {
 
 		case 31:
 			kernel_timing_trigger(40, 106);
-			aainfo[0]._val2 = 25;
+			aainfo[0]._frame = 25;
 			kernel_reset_animation(aa[0], 25);
 			break;
 
 		case 32:
-			aainfo[0]._val2 = 5;
+			aainfo[0]._frame = 5;
 			kernel_reset_animation(aa[0], 5);
 			break;
 
 		case 33:
-			aainfo[3]._val2 = 3;
+			aainfo[3]._frame = 3;
 			kernel_reset_animation(aa[3], 3);
 			break;
 
 		case 34:
 		case 35:
-			aainfo[3]._val2 = 39;
+			aainfo[3]._frame = 39;
 			kernel_reset_animation(aa[3], 39);
 			break;
 
 		case 36:
 			kernel_timing_trigger(40, 106);
-			aainfo[3]._val2 = 60;
+			aainfo[3]._frame = 60;
 			kernel_reset_animation(aa[3], 60);
 			break;
 
 		case 38:
 		case 39:
-			aainfo[3]._val2 = 63;
+			aainfo[3]._frame = 63;
 			kernel_reset_animation(aa[3], 63);
 			break;
 
 		case 51:
 			kernel_timing_trigger(40, 106);
-			aainfo[0]._val2 = 24;
+			aainfo[0]._frame = 24;
 			kernel_reset_animation(aa[0], 24);
 			break;
 
@@ -375,8 +1140,8 @@ static void room_203_daemon() {
 		switch (local->_8c) {
 		case 50:
 			aa[1] = kernel_run_animation(kernel_name('Z', 1), 103);
-			aainfo[1]._val1 = -1;
-			aainfo[1]._val2 = 0;
+			aainfo[1]._active = -1;
+			aainfo[1]._frame = 0;
 			local->_96 = 50;
 			player.commands_allowed = false;
 			player.walker_visible = false;
@@ -390,8 +1155,8 @@ static void room_203_daemon() {
 
 		case 51:
 			aa[1] = kernel_run_animation(kernel_name('e', 4), 103);
-			aainfo[1]._val1 = -1;
-			aainfo[1]._val2 = 0;
+			aainfo[1]._active = -1;
+			aainfo[1]._frame = 0;
 			local->_96 = 52;
 			player.commands_allowed = false;
 			player.walker_visible = false;
@@ -412,8 +1177,8 @@ static void room_203_daemon() {
 		switch (local->_8e) {
 		case 41:
 			aa[2] = kernel_run_animation(kernel_name('R', 3), 102);
-			aainfo[2]._val1 = -1;
-			aainfo[2]._val2 = 0;
+			aainfo[2]._active = -1;
+			aainfo[2]._frame = 0;
 			local->_94 = 41;
 			local->_a8 = 0;
 			kernel_reset_animation(local->_a0, 0);
@@ -423,8 +1188,8 @@ static void room_203_daemon() {
 
 		case 42:
 			aa[2] = kernel_run_animation(kernel_name('R', 1), 102);
-			aainfo[2]._val1 = -1;
-			aainfo[2]._val2 = 0;
+			aainfo[2]._active = -1;
+			aainfo[2]._frame = 0;
 			local->_94 = 42;
 			kernel_reset_animation(local->_a0, 0);
 			kernel_synch(KERNEL_ANIM, aa[2], KERNEL_ANIM, local->_a0);
@@ -433,8 +1198,8 @@ static void room_203_daemon() {
 
 		case 43:
 			aa[2] = kernel_run_animation(kernel_name('R', 2), 102);
-			aainfo[2]._val1 = -1;
-			aainfo[2]._val2 = 0;
+			aainfo[2]._active = -1;
+			aainfo[2]._frame = 0;
 			local->_94 = 43;
 			kernel_reset_animation(local->_a0, 0);
 			kernel_synch(KERNEL_ANIM, aa[2], KERNEL_ANIM, local->_a0);
@@ -466,8 +1231,8 @@ static void room_203_daemon() {
 		switch (local->_90) {
 		case 14:
 			aa[0] = kernel_run_animation(kernel_name('x', 4), 101);
-			aainfo[0]._val1 = -1;
-			aainfo[0]._val2 = 0;
+			aainfo[0]._active = -1;
+			aainfo[0]._frame = 0;
 			kernel_synch(KERNEL_ANIM, aa[0], KERNEL_NOW, 0);
 			local->_9a = 14;
 			break;
@@ -475,10 +1240,10 @@ static void room_203_daemon() {
 		case 15:
 			dont_frag_the_palette();
 			kernel_abort_animation(aa[4]);
-			aainfo[4]._val1 = 0;
+			aainfo[4]._active = 0;
 			aa[0] = kernel_run_animation(kernel_name('z', 2), 101);
-			aainfo[0]._val1 = -1;
-			aainfo[0]._val2 = 0;
+			aainfo[0]._active = -1;
+			aainfo[0]._frame = 0;
 			kernel_synch(KERNEL_ANIM, aa[0], KERNEL_NOW, 0);
 			local->_9a = 15;
 			break;
@@ -487,8 +1252,8 @@ static void room_203_daemon() {
 			player.walker_visible = true;
 			kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
 			aa[0] = kernel_run_animation(kernel_name('m', 1), 101);
-			aainfo[0]._val1 = -1;
-			aainfo[0]._val2 = 0;
+			aainfo[0]._active = -1;
+			aainfo[0]._frame = 0;
 			kernel_synch(KERNEL_ANIM, aa[0], KERNEL_NOW, 0);
 			local->_9a = 16;
 			kernel_seq_delete(seq[0]);
@@ -500,8 +1265,8 @@ static void room_203_daemon() {
 			kernel_synch(KERNEL_ANIM, aa[local->_a0], KERNEL_NOW, 0);
 			aa[1] = kernel_run_animation(kernel_name('e', 1), 103);
 			kernel_synch(KERNEL_ANIM, aa[1], KERNEL_NOW, 0);
-			aainfo[1]._val1 = -1;
-			aainfo[1]._val2 = 0;
+			aainfo[1]._active = -1;
+			aainfo[1]._frame = 0;
 			local->_a8 = 0;
 			break;
 
@@ -513,7 +1278,7 @@ static void room_203_daemon() {
 	case 101:
 		dont_frag_the_palette();
 		kernel_abort_animation(aa[0]);
-		aainfo[0]._val1 = 0;
+		aainfo[0]._active = 0;
 
 		switch (local->_9a) {
 		case 11:
@@ -536,21 +1301,21 @@ static void room_203_daemon() {
 				}
 
 				aa[0] = kernel_run_animation(kernel_name('x', 2), 101);
-				aainfo[0]._val1 = -1;
-				aainfo[0]._val2 = 0;
+				aainfo[0]._active = -1;
+				aainfo[0]._frame = 0;
 				local->_9a = 12;
 
 				if (flags[8] == 1) {
 					aa[4] = kernel_run_animation(kernel_name('w', 2), 105);
-					aainfo[4]._val1 = -1;
-					aainfo[4]._val2 = 0;
+					aainfo[4]._active = -1;
+					aainfo[4]._frame = 0;
 					local->_9c = 61;
 				} else {
 					aa[4] = kernel_run_animation(kernel_name('w', 3), 105);
-					aainfo[4]._val1 = -1;
-					aainfo[4]._val2 = 0;
+					aainfo[4]._active = -1;
+					aainfo[4]._frame = 0;
 					local->_9c = 62;
-					aainfo[0]._val2 = 41;
+					aainfo[0]._frame = 41;
 					kernel_reset_animation(aa[0], 41);
 				}
 			}
@@ -624,17 +1389,17 @@ static void room_203_daemon() {
 	case 102:
 		dont_frag_the_palette();
 		kernel_abort_animation(aa[2]);
-		aainfo[2]._val1 = 0;
-		aainfo[2]._val2 = 0;
+		aainfo[2]._active = 0;
+		aainfo[2]._frame = 0;
 
 		switch (local->_8e) {
 		case 41:
 			dont_frag_the_palette();
 			kernel_abort_animation(aa[4]);
-			aainfo[4]._val1 = 0;
+			aainfo[4]._active = 0;
 			aa[4] = kernel_run_animation(kernel_name('w', 5), 105);
-			aainfo[4]._val1 = -1;
-			aainfo[4]._val2 = 0;
+			aainfo[4]._active = -1;
+			aainfo[4]._frame = 0;
 			local->_9c = 64;
 			kernel_synch(KERNEL_ANIM, aa[4], KERNEL_NOW, 0);
 			kernel_reset_animation(local->_a0, 1);
@@ -668,8 +1433,8 @@ static void room_203_daemon() {
 	case 103:
 		dont_frag_the_palette();
 		kernel_abort_animation(aa[1]);
-		aainfo[1]._val1 = 0;
-		aainfo[1]._val2 = 0;
+		aainfo[1]._active = 0;
+		aainfo[1]._frame = 0;
 
 		switch (local->_96) {
 		case 50:
@@ -680,18 +1445,18 @@ static void room_203_daemon() {
 		case 52:
 			aa[1] = kernel_run_animation(kernel_name('e', 1), 103);
 			kernel_synch(KERNEL_ANIM, aa[1], KERNEL_NOW, 0);
-			aainfo[1]._val1 = -1;
-			aainfo[1]._val2 = 0;
+			aainfo[1]._active = -1;
+			aainfo[1]._frame = 0;
 			local->_96 = 53;
 			local->_a8 = 0;
 			break;
 
 		case 53:
 			aa[1] = kernel_run_animation(kernel_name('e', 2), 103);
-			aainfo[1]._val1 = -1;
-			aainfo[1]._val2 = 0;
+			aainfo[1]._active = -1;
+			aainfo[1]._frame = 0;
 			aainfo[1]._val4 = 0;
-			aainfo[4]._val2 = 17;
+			aainfo[4]._frame = 17;
 			kernel_reset_animation(aa[4], 0);
 			kernel_synch(KERNEL_ANIM, aa[4], KERNEL_NOW, 0);
 			local->_96 = local->_ac ? 55 : 54;
@@ -706,8 +1471,8 @@ static void room_203_daemon() {
 			} else {
 				aa[1] = kernel_run_animation(kernel_name('e', 4), 103);
 				kernel_synch(KERNEL_ANIM, aa[1], KERNEL_NOW, 0);
-				aainfo[1]._val1 = -1;
-				aainfo[1]._val2 = 0;
+				aainfo[1]._active = -1;
+				aainfo[1]._frame = 0;
 				local->_96 = 57;
 			}
 			break;
@@ -730,7 +1495,7 @@ static void room_203_daemon() {
 	case 104:
 		dont_frag_the_palette();
 		kernel_abort_animation(aa[3]);
-		aainfo[3]._val1 = 0;
+		aainfo[3]._active = 0;
 
 		if (local->_98 == 33) {
 			player.walker_visible = true;
@@ -746,7 +1511,7 @@ static void room_203_daemon() {
 	case 105:
 		dont_frag_the_palette();
 		kernel_abort_animation(aa[4]);
-		aainfo[4]._val1 = 0;
+		aainfo[4]._active = 0;
 
 		if (local->_9c == 64) {
 			if (local->_94 == 41 && local->_ac == 0) {
@@ -757,10 +1522,10 @@ static void room_203_daemon() {
 			if (local->_96 == 54) {
 				dont_frag_the_palette();
 				kernel_abort_animation(aa[5]);
-				aainfo[5]._val1 = 0;
+				aainfo[5]._active = 0;
 				aa[1] = kernel_run_animation(kernel_name('e', 3), 103);
-				aainfo[1]._val1 = -1;
-				aainfo[1]._val2 = 0;
+				aainfo[1]._active = -1;
+				aainfo[1]._frame = 0;
 				local->_96 = 56;
 			}
 
@@ -770,10 +1535,10 @@ static void room_203_daemon() {
 			if (local->_ac) {
 				dont_frag_the_palette();
 				kernel_abort_animation(aa[5]);
-				aainfo[5]._val1 = 0;
+				aainfo[5]._active = 0;
 				aa[1] = kernel_run_animation(kernel_name('e', 3), 103);
-				aainfo[1]._val1 = -1;
-				aainfo[1]._val2 = 0;
+				aainfo[1]._active = -1;
+				aainfo[1]._frame = 0;
 				local->_96 = 56;
 			}
 			break;
@@ -785,39 +1550,39 @@ static void room_203_daemon() {
 		case 23:
 			digi_play_build(203, 'W', 1, 1);
 			local->_a4 = 51;
-			aainfo[4]._val2 = 1;
+			aainfo[4]._frame = 1;
 			kernel_reset_animation(aa[4], 1);
 			break;
 
 		case 24:
-			aainfo[2]._val2 = 10;
+			aainfo[2]._frame = 10;
 			kernel_reset_animation(aa[2], 10);
 			break;
 
 		case 31:
-			aainfo[0]._val2 = 27;
+			aainfo[0]._frame = 27;
 			kernel_reset_animation(aa[0], 27);
 			break;
 
 		case 36:
-			aainfo[3]._val2 = 57;
+			aainfo[3]._frame = 57;
 			kernel_reset_animation(aa[3], 57);
 			digi_play_build(203, 'b', 8, 1);
 			local->_a4 = 38;
 			break;
 
 		case 34:
-			aainfo[0]._val2 = 11;
+			aainfo[0]._frame = 11;
 			kernel_reset_animation(aa[0], 11);
 			break;
 
 		case 35:
-			aainfo[0]._val2 = 35;
+			aainfo[0]._frame = 35;
 			kernel_reset_animation(aa[0], 35);
 			break;
 
 		case 44:
-			aainfo[0]._val2 = 19;
+			aainfo[0]._frame = 19;
 			kernel_reset_animation(aa[0], 19);
 			break;
 
@@ -833,6 +1598,30 @@ static void room_203_daemon() {
 	default:
 		break;
 	}
+
+	if (global[g046]) {
+		global_anim1(3, local->_9e, global[g131], &global[g132]);
+		global_anim2(3, local->_a0, global[g141], &global[g142]);
+	} else {
+		global_anim1(9, local->_9e, global[g131], &global[g132]);
+		global_anim2(9, local->_a0, global[g141], &global[g142]);
+	}
+
+	global[g030] = local->_a2;
+	if (local->_b2)
+		kernel_random_frame(local->_a2, &global[g151], global[g154]);
+	if (aainfo[0]._active)
+		room_203_anim1();
+	if (aainfo[2]._active)
+		room_203_anim3();
+	if (aainfo[1]._active)
+		room_203_anim2();
+	if (aainfo[3]._active)
+		room_203_anim4();
+	if (aainfo[4]._active)
+		room_203_anim5();
+	if (aainfo[5]._active)
+		room_203_anim6();
 }
 
 static void room_203_pre_parser() {
@@ -856,8 +1645,7 @@ static void room_203_parser() {
 			global[g150] = -1;
 			scratch._90 = 15;
 		}
-		player.command_ready = false;
-		return;
+		goto handled;
 	}
 
 	if (player_parse(13, 21, 0)) {
@@ -866,8 +1654,7 @@ static void room_203_parser() {
 			global[g150] = -1;
 			scratch._90 = 15;
 		}
-		player.command_ready = false;
-		return;
+		goto handled;
 	}
 
 	if (player_parse(13, 24, 0) && global[g046] == 0) {
@@ -884,7 +1671,7 @@ static void room_203_parser() {
 			scratch._a8 = 0;
 			if (global[g049] != 0) {
 				inter_spin_object(8);
-				aainfo[3]._val2 = 28;
+				aainfo[3]._frame = 28;
 				kernel_reset_animation(aa[3], 28);
 				global[g049] = 0;
 			} else {
@@ -896,8 +1683,7 @@ static void room_203_parser() {
 			digi_play_build_ii('c', 1, 1);
 			scratch._a4 = 52;
 		}
-		player.command_ready = false;
-		return;
+		goto handled;
 	}
 
 	if (global[player_selected_object] >= 0) {
@@ -909,7 +1695,7 @@ static void room_203_parser() {
 				kernel.trigger_setup_mode = KERNEL_TRIGGER_DAEMON;
 				kernel_timing_trigger(1, 107);
 				if (global[g049] != 0) {
-					aainfo[3]._val2 = 41;
+					aainfo[3]._frame = 41;
 					kernel_reset_animation(aa[3], 41);
 					global[g049] = 0;
 					scratch._ae = -1;
@@ -928,22 +1714,21 @@ static void room_203_parser() {
 				kernel_timing_trigger(1, 107);
 				if (global[g049] != 0) {
 					scratch._ac = 0;
-					aainfo[3]._val2 = 28;
+					aainfo[3]._frame = 28;
 					kernel_reset_animation(aa[3], 28);
 					global[g049] = 0;
 				} else {
 					kernel.trigger_setup_mode = KERNEL_TRIGGER_DAEMON;
 					aa[3] = kernel_run_animation(kernel_name('a', 2), 104);
-					aainfo[3]._val1 = -1;
-					aainfo[3]._val2 = 0;
+					aainfo[3]._active = -1;
+					aainfo[3]._frame = 0;
 					scratch._98 = 31;
 					player.walker_visible = 0;
 					kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
 				}
 			}
 		}
-		player.command_ready = false;
-		return;
+		goto handled;
 	}
 
 	if (player_parse(78, 119, 0)) {
@@ -961,8 +1746,7 @@ static void room_203_parser() {
 			digi_play_build_ii('b', 1, 1);
 			scratch._a4 = 30;
 		}
-		player.command_ready = false;
-		return;
+		goto handled;
 	}
 
 	if (player_parse(78, 168, 0)) {
@@ -970,17 +1754,17 @@ static void room_203_parser() {
 			kernel.trigger_setup_mode = KERNEL_TRIGGER_DAEMON;
 			aa[3] = kernel_run_animation(kernel_name('a', 1), 104);
 			scratch._98 = 30;
-			aainfo[3]._val1 = -1;
+			aainfo[3]._active = -1;
 			global[g049] = -1;
-			aainfo[3]._val2 = 0;
+			aainfo[3]._frame = 0;
 			player.walker_visible = 0;
 			player.commands_allowed = 0;
 			kernel_synch(KERNEL_ANIM, aa[3], KERNEL_NOW, 0);
 		} else if (player_has(8)) {
 			kernel.trigger_setup_mode = KERNEL_TRIGGER_DAEMON;
 			aa[3] = kernel_run_animation(kernel_name('a', 2), 104);
-			aainfo[3]._val1 = -1;
-			aainfo[3]._val2 = 0;
+			aainfo[3]._active = -1;
+			aainfo[3]._frame = 0;
 			scratch._98 = 31;
 			player.walker_visible = 0;
 			kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
@@ -990,42 +1774,37 @@ static void room_203_parser() {
 			global[g150] = -1;
 			scratch._90 = 15;
 		}
-		player.command_ready = false;
-		return;
+		goto handled;
 	}
 
 	if (player_parse(78, 169, 0)) {
 		player.commands_allowed = 0;
 		global[g135] = -1;
 		scratch._8c = 51;
-		player.command_ready = false;
-		return;
+		goto handled;
 	}
 
 	if (player_parse(78, 170, 0)) {
 		player.commands_allowed = 0;
 		global[g145] = -1;
 		scratch._8e = 43;
-		player.command_ready = false;
-		return;
+		goto handled;
 	}
 
 	if (player_parse(78, 65, 0)) {
 		player.commands_allowed = 0;
-		aainfo[3]._val2 = 28;
+		aainfo[3]._frame = 28;
 		kernel_reset_animation(aa[3], 28);
 		global[g049] = 0;
-		player.command_ready = false;
-		return;
+		goto handled;
 	}
 
 	if (player_parse(78, 66, 0)) {
 		player.commands_allowed = 0;
-		aainfo[3]._val2 = 33;
+		aainfo[3]._frame = 33;
 		kernel_reset_animation(aa[3], 33);
 		global[g049] = 0;
-		player.command_ready = false;
-		return;
+		goto handled;
 	}
 
 	if (global[g049] == 0)
@@ -1033,11 +1812,17 @@ static void room_203_parser() {
 
 	if (player_parse(13, 106, 0)) {
 		player.commands_allowed = 0;
-		aainfo[3]._val2 = 41;
+		aainfo[3]._frame = 41;
 		kernel_reset_animation(aa[3], 41);
 		global[g049] = 0;
-		player.command_ready = false;
+		goto handled;
 	}
+
+	goto done;
+handled:
+	player.command_ready = false;
+done:
+	;
 }
 
 void room_203_synchronize(Common::Serializer &s) {
@@ -1060,7 +1845,7 @@ void room_203_synchronize(Common::Serializer &s) {
 	s.syncAsSint16LE(scratch._a4);
 	s.syncAsSint16LE(scratch._a6);
 	s.syncAsSint16LE(scratch._a8);
-	s.syncAsSint16LE(scratch._paa);
+	s.syncAsSint16LE(scratch._aa);
 	s.syncAsSint16LE(scratch._ac);
 	s.syncAsSint16LE(scratch._ae);
 	s.syncAsSint16LE(scratch._b0);
diff --git a/engines/mads/madsv2/forest/rooms/room204.cpp b/engines/mads/madsv2/forest/rooms/room204.cpp
index 8ab1e7fbcd2..e7787fb2b75 100644
--- a/engines/mads/madsv2/forest/rooms/room204.cpp
+++ b/engines/mads/madsv2/forest/rooms/room204.cpp
@@ -19,18 +19,15 @@
  *
  */
 
-#include "mads/madsv2/core/conv.h"
+#include "mads/madsv2/core/digi.h"
 #include "mads/madsv2/core/game.h"
-#include "mads/madsv2/core/imath.h"
-#include "mads/madsv2/core/inter.h"
+#include "mads/madsv2/core/global.h"
 #include "mads/madsv2/core/kernel.h"
-#include "mads/madsv2/core/sound.h"
-#include "mads/madsv2/core/text.h"
-#include "mads/madsv2/forest/mads/inventory.h"
-#include "mads/madsv2/forest/mads/sounds.h"
-#include "mads/madsv2/forest/mads/words.h"
+#include "mads/madsv2/core/midi.h"
+#include "mads/madsv2/core/object.h"
+#include "mads/madsv2/core/player.h"
 #include "mads/madsv2/forest/global.h"
-#include "mads/madsv2/forest/rooms/section1.h"
+#include "mads/madsv2/forest/rooms/section2.h"
 #include "mads/madsv2/forest/rooms/room204.h"
 
 namespace MADS {
@@ -39,37 +36,209 @@ namespace Forest {
 namespace Rooms {
 
 struct Scratch {
-	int16 sprite[10];       /* Sprite series handles */
-	int16 sequence[10];     /* Sequence handles      */
-	int16 animation[10];     /* Animation handles     */
-	AnimationInfo animation_info[10];
+	int16 sprite[10];                /* Sprite series handles  0x00 */
+	int16 sequence[10];              /* Sequence handles       0x14 */
+	int16 animation[10];             /* Animation handles      0x28 */
+	AnimationInfo animation_info[10];/* Animation info         0x3C */
+	int16 _8c;
+	int16 _8e;
+	int16 _90;
+	int16 _92;                       /*                        0x92 */
+	int16 _pad94[3];                 /*                        0x94 */
+	int16 _9a;                       /*                        0x9A */
+	int16 _9c;                       /*                        0x9C */
+	int16 _9e;
+	int16 _a0;
+	int16 _a2;
+	int16 _a4;
+	int16 _a6;                       /*                        0xA6 */
+	int16 _a8;                       /*                        0xA8 */
 };
 
 static Scratch scratch;
 
-#define local (&scratch)
-#define ss    local->sprite
-#define seq   local->sequence
-#define aa    local->animation
+#define local  (&scratch)
+#define ss     local->sprite
+#define seq    local->sequence
+#define aa     local->animation
 #define aainfo scratch.animation_info
 
+static void room_204_init1();
 
 static void room_204_init() {
+	global[player_score] = -1;
+	midi_stop();
+	scratch._a8 = 0;
+	scratch._a6 = -1;
+
+	if (object_is_here(9)) {
+		ss[0] = kernel_load_series(kernel_name('p', 1), 0);
+		seq[0] = kernel_seq_stamp(ss[0], false, KERNEL_FIRST);
+		kernel_seq_depth(seq[0], 7);
+		kernel_seq_loc(seq[0], 293, 87);
+		kernel_seq_scale(seq[0], 54);
+	} else {
+		kernel_flip_hotspot(158, -1);
+	}
+
+	if (previous_room != KERNEL_RESTORING_GAME) {
+		if (previous_room != 199) {
+			player.walker_visible = 0;
+			player.commands_allowed = 0;
+		}
+
+		for (int count = 0; count < 10; count++) {
+			aainfo[count]._active = 0;
+			aainfo[count]._frame = 1;
+			aainfo[count]._val3 = 0;
+			aainfo[count]._val4 = 0;
+		}
+
+		if (previous_room != 199 && flags[18] != 3)
+			flags[18]++;
+	}
+
+	room_204_init1();
+	if (flags[18] == -3)
+		flags[18] = 1;
+}
+
+static void room_204_init1() {
+	global[g131] = 0;
+	global[g141] = 0;
+	scratch._9c = kernel_run_animation_disp('r', 2, 0);
+	kernel_position_anim(scratch._9c, 171, 116, 73, 8);
+	scratch._9a = kernel_run_animation_disp('e', 3, 0);
+	kernel_position_anim(scratch._9a, 148, 120, 76, 7);
+
+	if (previous_room != KERNEL_RESTORING_GAME) {
+		player.x = 194;
+		player.y = 121;
+		player.facing = 1;
+	}
+
+	if (previous_room == KERNEL_RESTORING_GAME) {
+		global[g131] = -1;
+		global[g141] = -1;
+		kernel_reset_animation(scratch._9a, 2);
+		kernel_reset_animation(scratch._9c, 2);
+		global[g133] = 0;
+		global[g143] = 0;
+		return;
+	} else if (previous_room == 203) {
+		aa[0] = kernel_run_animation(kernel_name('y', 1), 100);
+		aainfo[0]._active = -1;
+		scratch._92 = 74;
+		return;
+	} else if (previous_room == 205) {
+		aa[0] = kernel_run_animation(kernel_name('y', 2), 100);
+		aainfo[0]._active = -1;
+		scratch._92 = 78;
+		return;
+	} else if (previous_room == 220) {
+		aa[0] = kernel_run_animation(kernel_name('y', 3), 100);
+		aainfo[0]._active = -1;
+		scratch._92 = 66;
+		return;
+	} else if (previous_room == 199) {
+		global[g131] = -1;
+		global[g141] = -1;
+		kernel_reset_animation(scratch._9a, 2);
+		kernel_reset_animation(scratch._9c, 2);
+		global[g133] = 0;
+		global[g143] = 0;
+		restore_player();
+	} else {
+		global[g131] = -1;
+		global[g141] = -1;
+		kernel_reset_animation(scratch._9a, 2);
+		kernel_reset_animation(scratch._9c, 2);
+		global[g133] = 0;
+		global[g143] = 0;
+	}
+
+	player.commands_allowed = -1;
+	player.walker_visible = -1;
 }
 
 static void room_204_daemon() {
 }
 
 static void room_204_pre_parser() {
+	if (player_parse(13, 23, 0))
+		player.walk_off_edge_to_room = 203;
+
+	if (player_parse(13, 25, 0))
+		player.walk_off_edge_to_room = flags[13] ? 205 : 220;
 }
 
 static void room_204_parser() {
+	if (global[walker_converse_state]) {
+		player.commands_allowed = false;
+		digi_play_build_ii('c', 1, 1);
+		goto handled;
+	}
+
+	if (global[player_selected_object] >= 0) {
+		goto handled;
+	}
+
+	if (player_parse(114, 103, 0)) {
+		player.commands_allowed = false;
+		global[g135] = -1;
+		local->_8c = 5;
+		local->_a6 = 1;
+		goto handled;
+	}
+
+	if (player_parse(114, 82, 0)) {
+		player.commands_allowed = false;
+		global[g135] = -1;
+		local->_8c = 5;
+		local->_a6 = 2;
+		goto handled;
+	}
+
+	if (player_parse(126, 158, 0)) {
+		global[g154] = 2;
+		player.commands_allowed = false;
+		kernel_run_animation_talk('b', 9, 0);
+		kernel_position_anim(local->_9e, player.x, player.y, player.scale, player.depth);
+		goto handled;
+	}
+
+	if (player_parse(78, 119, 0)) {
+		player.commands_allowed = false;
+		global[g135] = -1;
+		local->_8c = 3;
+		goto handled;
+	}
+
+	goto done;
+handled:
+	player.command_ready = false;
+done:
+	;
 }
 
 void room_204_synchronize(Common::Serializer &s) {
 	for (int16 &v : scratch.sprite)    s.syncAsSint16LE(v);
 	for (int16 &v : scratch.sequence)  s.syncAsSint16LE(v);
 	for (int16 &v : scratch.animation) s.syncAsSint16LE(v);
+	for (AnimationInfo &ai : scratch.animation_info) ai.synchronize(s);
+	s.syncAsSint16LE(scratch._8c);
+	s.syncAsSint16LE(scratch._8e);
+	s.syncAsSint16LE(scratch._90);
+	s.syncAsSint16LE(scratch._92);
+	for (int16 &v : scratch._pad94)  s.syncAsSint16LE(v);
+	s.syncAsSint16LE(scratch._9a);
+	s.syncAsSint16LE(scratch._9c);
+	s.syncAsSint16LE(scratch._9e);
+	s.syncAsSint16LE(scratch._a0);
+	s.syncAsSint16LE(scratch._a2);
+	s.syncAsSint16LE(scratch._a4);
+	s.syncAsSint16LE(scratch._a6);
+	s.syncAsSint16LE(scratch._a8);
 }
 
 void room_204_preload() {
@@ -78,8 +247,8 @@ void room_204_preload() {
 	room_parser_code_pointer = room_204_parser;
 	room_daemon_code_pointer = room_204_daemon;
 
-	section_1_walker();
-	section_1_interface();
+	section_2_walker();
+	section_2_interface();
 }
 
 } // namespace Rooms


Commit: 1db23e2d9741ae3c20326d2ced9d0fb4ce88b675
    https://github.com/scummvm/scummvm/commit/1db23e2d9741ae3c20326d2ced9d0fb4ce88b675
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-06-07T14:15:13+10:00

Commit Message:
MADS: FOREST: Implemented room 204, other rooms preload functions

Changed paths:
    engines/mads/madsv2/forest/global.cpp
    engines/mads/madsv2/forest/global.h
    engines/mads/madsv2/forest/rooms/room101.cpp
    engines/mads/madsv2/forest/rooms/room103.cpp
    engines/mads/madsv2/forest/rooms/room104.cpp
    engines/mads/madsv2/forest/rooms/room106.cpp
    engines/mads/madsv2/forest/rooms/room107.cpp
    engines/mads/madsv2/forest/rooms/room199.cpp
    engines/mads/madsv2/forest/rooms/room201.cpp
    engines/mads/madsv2/forest/rooms/room203.cpp
    engines/mads/madsv2/forest/rooms/room204.cpp
    engines/mads/madsv2/forest/rooms/room205.cpp
    engines/mads/madsv2/forest/rooms/room210.cpp
    engines/mads/madsv2/forest/rooms/room211.cpp
    engines/mads/madsv2/forest/rooms/room220.cpp
    engines/mads/madsv2/forest/rooms/room221.cpp
    engines/mads/madsv2/forest/rooms/room301.cpp
    engines/mads/madsv2/forest/rooms/room302.cpp
    engines/mads/madsv2/forest/rooms/room303.cpp
    engines/mads/madsv2/forest/rooms/room304.cpp
    engines/mads/madsv2/forest/rooms/room305.cpp
    engines/mads/madsv2/forest/rooms/room306.cpp
    engines/mads/madsv2/forest/rooms/room307.cpp
    engines/mads/madsv2/forest/rooms/room308.cpp
    engines/mads/madsv2/forest/rooms/room321.cpp
    engines/mads/madsv2/forest/rooms/room322.cpp
    engines/mads/madsv2/forest/rooms/room401.cpp
    engines/mads/madsv2/forest/rooms/room402.cpp
    engines/mads/madsv2/forest/rooms/room403.cpp
    engines/mads/madsv2/forest/rooms/room404.cpp
    engines/mads/madsv2/forest/rooms/room405.cpp
    engines/mads/madsv2/forest/rooms/room420.cpp
    engines/mads/madsv2/forest/rooms/room501.cpp
    engines/mads/madsv2/forest/rooms/room503.cpp
    engines/mads/madsv2/forest/rooms/room509.cpp
    engines/mads/madsv2/forest/rooms/room510.cpp
    engines/mads/madsv2/forest/rooms/room520.cpp
    engines/mads/madsv2/forest/rooms/section1.cpp
    engines/mads/madsv2/forest/rooms/section1.h
    engines/mads/madsv2/forest/rooms/section2.cpp
    engines/mads/madsv2/forest/rooms/section2.h
    engines/mads/madsv2/forest/rooms/section3.cpp
    engines/mads/madsv2/forest/rooms/section3.h
    engines/mads/madsv2/forest/rooms/section4.cpp
    engines/mads/madsv2/forest/rooms/section4.h
    engines/mads/madsv2/forest/rooms/section5.cpp
    engines/mads/madsv2/forest/rooms/section5.h


diff --git a/engines/mads/madsv2/forest/global.cpp b/engines/mads/madsv2/forest/global.cpp
index 22f546ca4f5..f086e6dc87c 100644
--- a/engines/mads/madsv2/forest/global.cpp
+++ b/engines/mads/madsv2/forest/global.cpp
@@ -26,6 +26,7 @@
 #include "mads/madsv2/core/imath.h"
 #include "mads/madsv2/core/kernel.h"
 #include "mads/madsv2/core/midi.h"
+#include "mads/madsv2/core/pal.h"
 #include "mads/madsv2/forest/global.h"
 #include "mads/madsv2/forest/extra.h"
 
@@ -169,6 +170,30 @@ void sync_room(Common::Serializer &s) {
 	}
 }
 
+void global_section_walker() {
+	char buf[80];
+
+	Common::strcpy_s(buf, player.series_name);
+	global[g017] = -1;
+
+	if (global[g016]) {
+		*player.series_name = '\0';
+	} else if (!player.force_series) {
+		Common::strcpy_s(player.series_name, "B");
+	}
+
+	if (strcmp(player.series_name, buf))
+		player.walker_must_reload = true;
+
+	player.scaling_velocity = true;
+}
+
+void global_section_interface() {
+	Common::strcpy_s(kernel.interface, kernel_interface_name(0));
+	pal_change_color(254, 56, 47, 32);
+}
+
+
 static void global_anim1_1(int arg_0, int arg_2, int16 *arg_4) {
 	if (kernel_anim[arg_0].frame == *arg_4)
 		return;
diff --git a/engines/mads/madsv2/forest/global.h b/engines/mads/madsv2/forest/global.h
index 75b09e04960..c34c61328c3 100644
--- a/engines/mads/madsv2/forest/global.h
+++ b/engines/mads/madsv2/forest/global.h
@@ -259,6 +259,8 @@ extern bool room_203_flag;
 
 extern void global_section_constructor();
 extern void sync_room(Common::Serializer &s);
+extern void global_section_walker();
+extern void global_section_interface();
 
 extern void global_anim1(int arg_0, int arg_2, int arg_4, int16 *arg_6);
 extern void global_anim2(int arg_0, int arg_2, int arg_4, int16 *arg_6);
diff --git a/engines/mads/madsv2/forest/rooms/room101.cpp b/engines/mads/madsv2/forest/rooms/room101.cpp
index 101cc01d88d..08ff07b5afc 100644
--- a/engines/mads/madsv2/forest/rooms/room101.cpp
+++ b/engines/mads/madsv2/forest/rooms/room101.cpp
@@ -1120,8 +1120,8 @@ void room_101_preload() {
 	if (flags[0] == -4)
 		global[g016] = -1;
 
-	section_1_walker();
-	section_1_interface();
+	global_section_walker();
+	global_section_interface();
 	player.walker_must_reload = true;
 }
 
diff --git a/engines/mads/madsv2/forest/rooms/room103.cpp b/engines/mads/madsv2/forest/rooms/room103.cpp
index d12e5461d0f..740ce94eb2a 100644
--- a/engines/mads/madsv2/forest/rooms/room103.cpp
+++ b/engines/mads/madsv2/forest/rooms/room103.cpp
@@ -426,8 +426,8 @@ void room_103_preload() {
 	if (flags[1] == -4)
 		global[g016] = -1;
 	if (previous_room != 199) {
-		section_1_walker();
-		section_1_interface();
+		global_section_walker();
+		global_section_interface();
 	}
 
 	player.walker_must_reload = true;
diff --git a/engines/mads/madsv2/forest/rooms/room104.cpp b/engines/mads/madsv2/forest/rooms/room104.cpp
index 5c8a7d216ab..4fec105fb1f 100644
--- a/engines/mads/madsv2/forest/rooms/room104.cpp
+++ b/engines/mads/madsv2/forest/rooms/room104.cpp
@@ -974,8 +974,8 @@ void room_104_preload() {
 	room_daemon_code_pointer     = room_104_daemon;
 
 	global[g016] = (global[g101] || room_id == 103) ? -1 : 0;
-	section_1_walker();
-	section_1_interface();
+	global_section_walker();
+	global_section_interface();
 	player.walker_must_reload = true;
 }
 
diff --git a/engines/mads/madsv2/forest/rooms/room106.cpp b/engines/mads/madsv2/forest/rooms/room106.cpp
index 19698662150..ae2f2a7019e 100644
--- a/engines/mads/madsv2/forest/rooms/room106.cpp
+++ b/engines/mads/madsv2/forest/rooms/room106.cpp
@@ -821,8 +821,8 @@ void room_106_preload() {
 	if (flags[3] == -4)
 		global[g016] = -1;
 
-	section_1_walker();
-	section_1_interface();
+	global_section_walker();
+	global_section_interface();
 	player.walker_must_reload = true;
 }
 
diff --git a/engines/mads/madsv2/forest/rooms/room107.cpp b/engines/mads/madsv2/forest/rooms/room107.cpp
index 16f210f3e4a..ebf9365e9e6 100644
--- a/engines/mads/madsv2/forest/rooms/room107.cpp
+++ b/engines/mads/madsv2/forest/rooms/room107.cpp
@@ -164,8 +164,8 @@ void room_107_preload() {
 	room_parser_code_pointer = room_107_parser;
 	room_daemon_code_pointer = room_107_daemon;
 
-	section_1_walker();
-	section_1_interface();
+	global_section_walker();
+	global_section_interface();
 	player.walker_must_reload = true;
 }
 
diff --git a/engines/mads/madsv2/forest/rooms/room199.cpp b/engines/mads/madsv2/forest/rooms/room199.cpp
index 35abb9125ac..e84be3e44d7 100644
--- a/engines/mads/madsv2/forest/rooms/room199.cpp
+++ b/engines/mads/madsv2/forest/rooms/room199.cpp
@@ -569,8 +569,8 @@ void room_199_preload() {
 
 	global[g016] = -1;
 	player.walker_visible = false;
-	section_1_walker();
-	section_1_interface();
+	global_section_walker();
+	global_section_interface();
 }
 
 } // namespace Rooms
diff --git a/engines/mads/madsv2/forest/rooms/room201.cpp b/engines/mads/madsv2/forest/rooms/room201.cpp
index d4344262ba8..fd9efce246f 100644
--- a/engines/mads/madsv2/forest/rooms/room201.cpp
+++ b/engines/mads/madsv2/forest/rooms/room201.cpp
@@ -688,8 +688,9 @@ void room_201_preload() {
 	room_pre_parser_code_pointer = room_201_pre_parser;
 	room_parser_code_pointer     = room_201_parser;
 	room_daemon_code_pointer     = room_201_daemon;
-	section_2_walker();
-	section_2_interface();
+
+	global_section_walker();
+	global_section_interface();
 	player.walker_must_reload = true;
 }
 
diff --git a/engines/mads/madsv2/forest/rooms/room203.cpp b/engines/mads/madsv2/forest/rooms/room203.cpp
index ae9209a5a7e..a64b2fa3af5 100644
--- a/engines/mads/madsv2/forest/rooms/room203.cpp
+++ b/engines/mads/madsv2/forest/rooms/room203.cpp
@@ -1858,8 +1858,9 @@ void room_203_preload() {
 	room_pre_parser_code_pointer = room_203_pre_parser;
 	room_parser_code_pointer     = room_203_parser;
 	room_daemon_code_pointer     = room_203_daemon;
-	section_2_walker();
-	section_2_interface();
+
+	global_section_walker();
+	global_section_interface();
 	player.walker_must_reload = true;
 }
 
diff --git a/engines/mads/madsv2/forest/rooms/room204.cpp b/engines/mads/madsv2/forest/rooms/room204.cpp
index e7787fb2b75..c1a28a8c961 100644
--- a/engines/mads/madsv2/forest/rooms/room204.cpp
+++ b/engines/mads/madsv2/forest/rooms/room204.cpp
@@ -22,11 +22,13 @@
 #include "mads/madsv2/core/digi.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/global.h"
+#include "mads/madsv2/core/inter.h"
 #include "mads/madsv2/core/kernel.h"
 #include "mads/madsv2/core/midi.h"
 #include "mads/madsv2/core/object.h"
 #include "mads/madsv2/core/player.h"
 #include "mads/madsv2/forest/global.h"
+#include "mads/madsv2/forest/journal.h"
 #include "mads/madsv2/forest/rooms/section2.h"
 #include "mads/madsv2/forest/rooms/room204.h"
 
@@ -44,7 +46,9 @@ struct Scratch {
 	int16 _8e;
 	int16 _90;
 	int16 _92;                       /*                        0x92 */
-	int16 _pad94[3];                 /*                        0x94 */
+	int16 _94;                       /*                        0x94 */
+	int16 _96;                       /*                        0x96 */
+	int16 _98;                       /*                        0x98 */
 	int16 _9a;                       /*                        0x9A */
 	int16 _9c;                       /*                        0x9C */
 	int16 _9e;
@@ -64,6 +68,12 @@ static Scratch scratch;
 #define aainfo scratch.animation_info
 
 static void room_204_init1();
+static void room_204_anim1();
+static void room_204_anim2();
+static void room_204_anim3();
+static void room_204_anim4();
+static void room_204_anim5();
+static void room_204_anim6();
 
 static void room_204_init() {
 	global[player_score] = -1;
@@ -162,6 +172,377 @@ static void room_204_init1() {
 }
 
 static void room_204_daemon() {
+	int trigger = kernel.trigger;
+
+	if (trigger == 103) {
+		goto trigger_103;
+	} else if (trigger > 103) {
+		goto bottom;
+	} else if (trigger == 25) {
+		goto trigger_25;
+	} else if (trigger > 25) {
+		// triggers 26-102
+		if (trigger == 26) {
+			goto trigger_26;
+		} else if (trigger == 100) {
+			goto trigger_100;
+		} else if (trigger == 101) {
+			// abort _94 slot anim, re-enable
+			kernel_abort_animation(aa[scratch._94]);
+			aainfo[scratch._94]._active = 0;
+			kernel_reset_animation(scratch._9c, 1);
+			kernel_synch(3, scratch._9c, 4, 0);
+			global[g143] = 0;
+			player.commands_allowed = -1;
+			goto bottom;
+		} else if (trigger == 102) {
+			// abort _96 slot anim, re-enable
+			kernel_abort_animation(aa[scratch._96]);
+			aainfo[scratch._96]._active = 0;
+			kernel_reset_animation(scratch._9a, 1);
+			kernel_synch(3, scratch._9a, 4, 0);
+			global[g133] = 0;
+			player.commands_allowed = -1;
+			goto bottom;
+		} else {
+			goto bottom;
+		}
+	} else if (trigger == 0) {
+		goto bottom;
+	} else if (trigger == 7) {
+		goto trigger_7;
+	} else if (trigger == 24) {
+		goto trigger_24;
+	} else {
+		goto bottom;
+	}
+
+trigger_7:
+	if (global[walker_converse_state] != 0) {
+		global[walker_converse_state] = 0;
+		close_journal(3);
+		player.commands_allowed = -1;
+		goto bottom;
+	}
+	if (scratch._a4 != 400)
+		goto bottom;
+	kernel_abort_animation(scratch._9e);
+	scratch._a4 = -1;
+	scratch._a8 = 0;
+	kernel_synch(2, 0, 4, 0);
+	scratch._a6 = 3;
+	player.walker_visible = -1;
+	global[g150] = -1;
+	scratch._90 = 6;
+	goto bottom;
+
+trigger_24:
+	if (scratch._8c == 3) {
+		aa[2] = kernel_run_animation(kernel_name('E', 1), 102);
+		aainfo[2]._active = -1;
+		scratch._96 = 2;
+		kernel_reset_animation(scratch._9a, 0);
+		kernel_synch(3, aa[2], 3, scratch._9a);
+		global[g133] = 1;
+	} else if (scratch._8c == 5) {
+		if (scratch._a6 == 1) {
+			aa[4] = kernel_run_animation(kernel_name('F', 1), 102);
+		} else if (scratch._a6 == 2) {
+			aa[4] = kernel_run_animation(kernel_name('F', 2), 102);
+		} else {
+			goto bottom;
+		}
+		aainfo[4]._active = -1;
+		scratch._96 = 4;
+		kernel_reset_animation(scratch._9a, 0);
+		kernel_synch(3, aa[4], 3, scratch._9a);
+		global[g133] = 1;
+	}
+	goto bottom;
+
+trigger_25:
+	if (scratch._8e == 2) {
+		aa[3] = kernel_run_animation(kernel_name('R', 1), 101);
+		aainfo[3]._active = -1;
+		scratch._94 = 3;
+		kernel_reset_animation(scratch._9c, 0);
+		kernel_synch(3, aa[3], 3, scratch._9c);
+		global[g143] = 1;
+	}
+	goto bottom;
+
+trigger_26:
+	if (scratch._90 == 1) {
+		kernel_reset_animation(scratch._9a, 0);
+		kernel_reset_animation(scratch._9c, 0);
+		global[g133] = 1;
+		global[g143] = 1;
+	} else if (scratch._90 == 6) {
+		if (scratch._a6 != 3)
+			goto bottom;
+		aa[5] = kernel_run_animation(kernel_name('P', 1), 103);
+		aainfo[5]._active = -1;
+		scratch._98 = 5;
+		kernel_reset_animation(scratch._9a, 0);
+		kernel_reset_animation(scratch._9c, 0);
+		kernel_synch(3, aa[5], 4, 0);
+		global[g133] = 1;
+		global[g143] = 1;
+	}
+	goto bottom;
+
+trigger_100:
+	kernel_abort_animation(aa[0]);
+	aainfo[0]._active = 0;
+	if (flags[18] == 1) {
+		kernel_synch(2, 0, 4, 0);
+		global[g017] = 0;
+		global[g154] = 0;
+		kernel_synch(3, scratch._9a, 4, 0);
+		global[g131] = -1;
+		global[g133] = 1;
+		global[g156] = 0;
+		kernel_synch(3, scratch._9c, 4, 0);
+		global[g143] = 1;
+		scratch._a4 = 1;
+		global[g141] = -1;
+		player.walker_visible = -1;
+		aainfo[1]._active = -1;
+		global[g155] = 2;
+		scratch._a0 = kernel_run_animation_talk('r', 2, 0);
+		kernel_position_anim(scratch._a0, 171, 116, 73, 8);
+		scratch._a2 = kernel_run_animation_talk('e', 3, 0);
+		kernel_position_anim(scratch._a2, 148, 120, 76, 7);
+		kernel_synch(3, scratch._a2, 3, scratch._9a);
+		kernel_synch(3, scratch._a0, 3, scratch._9c);
+		kernel_synch(3, scratch._9e, 2, 0);
+		digi_play_build(204, 114, 1, 1);
+	} else {
+		global[g131] = -1;
+		global[g141] = -1;
+		kernel_reset_animation(scratch._9a, 1);
+		kernel_reset_animation(scratch._9c, 1);
+		global[g133] = 0;
+		global[g143] = 0;
+		kernel_synch(3, scratch._9a, 4, 0);
+		kernel_synch(3, scratch._9c, 4, 0);
+		player.walker_visible = -1;
+		kernel_synch(2, 0, 4, 0);
+		player.commands_allowed = -1;
+	}
+	goto bottom;
+
+trigger_103:
+	if (scratch._a6 != 3)
+		goto bottom;
+	kernel_abort_animation(aa[5]);
+	aainfo[5]._active = 0;
+	kernel_reset_animation(scratch._9c, 1);
+	kernel_synch(3, scratch._9c, 4, 0);
+	global[g143] = 0;
+	kernel_reset_animation(scratch._9a, 1);
+	kernel_synch(3, scratch._9a, 4, 0);
+	global[g133] = 0;
+	kernel_flip_hotspot(158, -1);
+	inter_move_object(9, 2);
+	global[player_score] = -1;
+	player.commands_allowed = -1;
+
+bottom:
+	global_anim1(3, scratch._9a, global[g131], &global[g132]);
+	global_anim2(2, scratch._9c, global[g141], &global[g142]);
+
+	if (scratch._a8 != 0)
+		kernel_random_frame(scratch._9e, &global[g151], global[g154]);
+
+	if (aainfo[0]._active != 0) room_204_anim1();
+	if (aainfo[1]._active != 0) room_204_anim2();
+	if (aainfo[3]._active != 0) room_204_anim3();
+	if (aainfo[2]._active != 0) room_204_anim4();
+	if (aainfo[4]._active != 0) room_204_anim5();
+	if (aainfo[5]._active != 0) room_204_anim6();
+}
+
+static void room_204_anim1() {
+	if (kernel_anim[aa[0]].frame != aainfo[0]._frame)
+		aainfo[0]._frame = kernel_anim[aa[0]].frame;
+
+	if (global[player_hyperwalked] == -1) {
+		aainfo[0]._frame = scratch._92 - 1;
+		kernel_reset_animation(aa[0], aainfo[0]._frame);
+	}
+}
+
+static void room_204_anim2() {
+	if (kernel_anim[aa[1]].frame != aainfo[1]._frame)
+		aainfo[1]._frame = kernel_anim[aa[1]].frame;
+
+	if (scratch._a4 == 0)
+		return;
+
+	kernel_random_frame(scratch._a2, &global[g153], global[g156]);
+	kernel_random_frame(scratch._a0, &global[g152], global[g155]);
+
+	if (kernel.trigger != 7 && kernel.trigger != 28)
+		return;
+
+	if (scratch._a4 == 1) {
+		global[g155] = 0;
+		kernel_timing_trigger(10, 28);
+		scratch._a4 = 2;
+	} else if (scratch._a4 == 2) {
+		global[g156] = 2;
+		scratch._a4 = 3;
+		digi_play_build(204, 'e', 1, 1);
+	} else if (scratch._a4 == 3) {
+		scratch._a4 = 4;
+		kernel_abort_animation(scratch._a2);
+		kernel_abort_animation(scratch._a0);
+		player.commands_allowed = -1;
+		player.walker_visible = -1;
+		global[g017] = -1;
+		aainfo[1]._active = 0;
+		kernel_synch(2, 0, 4, 0);
+		global[g131] = -1;
+		global[g141] = -1;
+		kernel_reset_animation(scratch._9a, 1);
+		kernel_reset_animation(scratch._9c, 1);
+		kernel_synch(3, scratch._9a, 4, 0);
+		kernel_synch(3, scratch._9c, 4, 0);
+		global[g133] = 0;
+		global[g143] = 0;
+	}
+}
+
+static void room_204_anim3() {
+	if (kernel_anim[aa[3]].frame != aainfo[3]._frame)
+		aainfo[3]._frame = kernel_anim[aa[3]].frame;
+}
+
+static void room_204_anim4() {
+	if (kernel_anim[aa[2]].frame != aainfo[2]._frame)
+		aainfo[2]._frame = kernel_anim[aa[2]].frame;
+}
+
+static void room_204_anim5() {
+	if (kernel_anim[aa[4]].frame != aainfo[4]._frame) {
+		aainfo[4]._frame = kernel_anim[aa[4]].frame;
+		int16 ax = aainfo[4]._frame;
+
+		if (scratch._a6 == 1) {
+			if (ax == 51) {
+				if (aainfo[4]._val3 == 13) {
+					aainfo[4]._frame = 49;
+					kernel_reset_animation(aa[4], 49);
+				}
+			} else if (ax < 51) {
+				if (ax == 43) {
+					aainfo[4]._val3 = 11;
+					digi_play_build_ii('e', 4, 1);
+					scratch._a4 = 43;
+				} else if (ax == 48) {
+					if (aainfo[4]._val3 == 11) {
+						aainfo[4]._frame = 43;
+						kernel_reset_animation(aa[4], 43);
+					}
+				}
+			}
+		} else if (scratch._a6 == 2) {
+			if (ax == 13) {
+				if (aainfo[4]._val3 == 13) {
+					aainfo[4]._frame = 11;
+					kernel_reset_animation(aa[4], 11);
+				}
+			} else if (ax < 13) {
+				if (ax == 5) {
+					aainfo[4]._val3 = 11;
+					digi_play_build_ii('e', 9, 1);
+					scratch._a4 = 500;
+				} else if (ax == 10) {
+					if (aainfo[4]._val3 == 11) {
+						aainfo[4]._frame = 5;
+						kernel_reset_animation(aa[4], 5);
+					}
+				}
+			}
+		}
+	}
+
+	if (kernel.trigger != 7 && kernel.trigger != 28)
+		return;
+
+	if (scratch._a4 == 43) {
+		kernel_timing_trigger(30, 28);
+		aainfo[4]._val3 = 13;
+		scratch._a4 = 44;
+	} else if (scratch._a4 == 44) {
+		aainfo[4]._val3 = 11;
+		aainfo[4]._frame = 43;
+		kernel_reset_animation(aa[4], 43);
+		digi_play_build_ii('e', 10, 1);
+		scratch._a4 = 45;
+	} else if (scratch._a4 == 45) {
+		aainfo[4]._val3 = 14;
+		aainfo[4]._frame = 50;
+		kernel_reset_animation(aa[4], 50);
+		scratch._a4 = -1;
+	} else if (scratch._a4 == 500) {
+		kernel_timing_trigger(30, 28);
+		aainfo[4]._val3 = 13;
+		scratch._a4 = 501;
+	} else if (scratch._a4 == 501) {
+		aainfo[4]._val3 = 11;
+		aainfo[4]._frame = 5;
+		kernel_reset_animation(aa[4], 5);
+		digi_play_build_ii('e', 10, 1);
+		scratch._a4 = 502;
+	} else if (scratch._a4 == 502) {
+		aainfo[4]._val3 = 14;
+		aainfo[4]._frame = 11;
+		kernel_reset_animation(aa[4], 11);
+		scratch._a4 = -1;
+	}
+}
+static void room_204_anim6() {
+	if (kernel_anim[aa[5]].frame != aainfo[5]._frame) {
+		aainfo[5]._frame = kernel_anim[aa[5]].frame;
+		int16 ax = aainfo[5]._frame;
+
+		if (scratch._a6 == 3) {
+			if (ax == 64) {
+				if (aainfo[5]._val3 == 13) {
+					digi_play_build(101, '_', 1, 2);
+					scratch._a4 = 64;
+				}
+			} else if (ax < 64) {
+				if (ax == 5) {
+					aainfo[5]._val3 = 11;
+					digi_play_build(204, 'e', 2, 1);
+					scratch._a4 = 5;
+				} else if (ax == 10) {
+					if (aainfo[5]._val3 == 11) {
+						aainfo[5]._frame = 5;
+						kernel_reset_animation(aa[5], 5);
+					}
+				}
+			}
+		}
+	}
+
+	if (kernel.trigger == 7 || kernel.trigger == 28) {
+		if (scratch._a4 == 5) {
+			aainfo[5]._val3 = 13;
+			aainfo[5]._frame = 11;
+			kernel_reset_animation(aa[5], 11);
+			scratch._a4 = -1;
+			kernel_seq_delete(seq[0]);
+		}
+	}
+
+	if (kernel.trigger == 8 || kernel.trigger == 28) {
+		if (scratch._a4 == 64)
+			scratch._a4 = -1;
+	}
 }
 
 static void room_204_pre_parser() {
@@ -230,7 +611,9 @@ void room_204_synchronize(Common::Serializer &s) {
 	s.syncAsSint16LE(scratch._8e);
 	s.syncAsSint16LE(scratch._90);
 	s.syncAsSint16LE(scratch._92);
-	for (int16 &v : scratch._pad94)  s.syncAsSint16LE(v);
+	s.syncAsSint16LE(scratch._94);
+	s.syncAsSint16LE(scratch._96);
+	s.syncAsSint16LE(scratch._98);
 	s.syncAsSint16LE(scratch._9a);
 	s.syncAsSint16LE(scratch._9c);
 	s.syncAsSint16LE(scratch._9e);
@@ -247,8 +630,9 @@ void room_204_preload() {
 	room_parser_code_pointer = room_204_parser;
 	room_daemon_code_pointer = room_204_daemon;
 
-	section_2_walker();
-	section_2_interface();
+	global_section_walker();
+	global_section_interface();
+	player.walker_must_reload = true;
 }
 
 } // namespace Rooms
diff --git a/engines/mads/madsv2/forest/rooms/room205.cpp b/engines/mads/madsv2/forest/rooms/room205.cpp
index 0da27750430..1b1bd136598 100644
--- a/engines/mads/madsv2/forest/rooms/room205.cpp
+++ b/engines/mads/madsv2/forest/rooms/room205.cpp
@@ -78,8 +78,9 @@ void room_205_preload() {
 	room_parser_code_pointer = room_205_parser;
 	room_daemon_code_pointer = room_205_daemon;
 
-	section_1_walker();
-	section_1_interface();
+	global_section_walker();
+	global_section_interface();
+	player.walker_must_reload = true;
 }
 
 } // namespace Rooms
diff --git a/engines/mads/madsv2/forest/rooms/room210.cpp b/engines/mads/madsv2/forest/rooms/room210.cpp
index 235c69f63df..95bc2a25e6a 100644
--- a/engines/mads/madsv2/forest/rooms/room210.cpp
+++ b/engines/mads/madsv2/forest/rooms/room210.cpp
@@ -78,8 +78,9 @@ void room_210_preload() {
 	room_parser_code_pointer = room_210_parser;
 	room_daemon_code_pointer = room_210_daemon;
 
-	section_1_walker();
-	section_1_interface();
+	global_section_walker();
+	global_section_interface();
+	player.walker_must_reload = true;
 }
 
 } // namespace Rooms
diff --git a/engines/mads/madsv2/forest/rooms/room211.cpp b/engines/mads/madsv2/forest/rooms/room211.cpp
index 69b91185a8d..cf22e3282b7 100644
--- a/engines/mads/madsv2/forest/rooms/room211.cpp
+++ b/engines/mads/madsv2/forest/rooms/room211.cpp
@@ -78,8 +78,9 @@ void room_211_preload() {
 	room_parser_code_pointer = room_211_parser;
 	room_daemon_code_pointer = room_211_daemon;
 
-	section_1_walker();
-	section_1_interface();
+	global_section_walker();
+	global_section_interface();
+	player.walker_must_reload = true;
 }
 
 } // namespace Rooms
diff --git a/engines/mads/madsv2/forest/rooms/room220.cpp b/engines/mads/madsv2/forest/rooms/room220.cpp
index 643c4b96747..09982c1aa9a 100644
--- a/engines/mads/madsv2/forest/rooms/room220.cpp
+++ b/engines/mads/madsv2/forest/rooms/room220.cpp
@@ -78,8 +78,11 @@ void room_220_preload() {
 	room_parser_code_pointer = room_220_parser;
 	room_daemon_code_pointer = room_220_daemon;
 
-	section_1_walker();
-	section_1_interface();
+	flags[13] = 7;
+	global[g016] = -1;
+	global_section_walker();
+	global_section_interface();
+	player.walker_must_reload = true;
 }
 
 } // namespace Rooms
diff --git a/engines/mads/madsv2/forest/rooms/room221.cpp b/engines/mads/madsv2/forest/rooms/room221.cpp
index 74145af6669..062d5ff6244 100644
--- a/engines/mads/madsv2/forest/rooms/room221.cpp
+++ b/engines/mads/madsv2/forest/rooms/room221.cpp
@@ -78,8 +78,9 @@ void room_221_preload() {
 	room_parser_code_pointer = room_221_parser;
 	room_daemon_code_pointer = room_221_daemon;
 
-	section_1_walker();
-	section_1_interface();
+	global_section_walker();
+	global_section_interface();
+	player.walker_must_reload = true;
 }
 
 } // namespace Rooms
diff --git a/engines/mads/madsv2/forest/rooms/room301.cpp b/engines/mads/madsv2/forest/rooms/room301.cpp
index 85f0c82eade..db009299b16 100644
--- a/engines/mads/madsv2/forest/rooms/room301.cpp
+++ b/engines/mads/madsv2/forest/rooms/room301.cpp
@@ -78,8 +78,9 @@ void room_301_preload() {
 	room_parser_code_pointer = room_301_parser;
 	room_daemon_code_pointer = room_301_daemon;
 
-	section_1_walker();
-	section_1_interface();
+	global_section_walker();
+	global_section_interface();
+	player.walker_must_reload = true;
 }
 
 } // namespace Rooms
diff --git a/engines/mads/madsv2/forest/rooms/room302.cpp b/engines/mads/madsv2/forest/rooms/room302.cpp
index 82a6e09a042..155a4e5dfe4 100644
--- a/engines/mads/madsv2/forest/rooms/room302.cpp
+++ b/engines/mads/madsv2/forest/rooms/room302.cpp
@@ -78,8 +78,9 @@ void room_302_preload() {
 	room_parser_code_pointer = room_302_parser;
 	room_daemon_code_pointer = room_302_daemon;
 
-	section_1_walker();
-	section_1_interface();
+	global_section_walker();
+	global_section_interface();
+	player.walker_must_reload = true;
 }
 
 } // namespace Rooms
diff --git a/engines/mads/madsv2/forest/rooms/room303.cpp b/engines/mads/madsv2/forest/rooms/room303.cpp
index abb32340a3c..ded667515aa 100644
--- a/engines/mads/madsv2/forest/rooms/room303.cpp
+++ b/engines/mads/madsv2/forest/rooms/room303.cpp
@@ -78,8 +78,9 @@ void room_303_preload() {
 	room_parser_code_pointer = room_303_parser;
 	room_daemon_code_pointer = room_303_daemon;
 
-	section_1_walker();
-	section_1_interface();
+	global_section_walker();
+	global_section_interface();
+	player.walker_must_reload = true;
 }
 
 } // namespace Rooms
diff --git a/engines/mads/madsv2/forest/rooms/room304.cpp b/engines/mads/madsv2/forest/rooms/room304.cpp
index 0cbdeae59d8..e41c65ca889 100644
--- a/engines/mads/madsv2/forest/rooms/room304.cpp
+++ b/engines/mads/madsv2/forest/rooms/room304.cpp
@@ -78,8 +78,9 @@ void room_304_preload() {
 	room_parser_code_pointer = room_304_parser;
 	room_daemon_code_pointer = room_304_daemon;
 
-	section_1_walker();
-	section_1_interface();
+	global_section_walker();
+	global_section_interface();
+	player.walker_must_reload = true;
 }
 
 } // namespace Rooms
diff --git a/engines/mads/madsv2/forest/rooms/room305.cpp b/engines/mads/madsv2/forest/rooms/room305.cpp
index a3369107f8d..89c14ccbe20 100644
--- a/engines/mads/madsv2/forest/rooms/room305.cpp
+++ b/engines/mads/madsv2/forest/rooms/room305.cpp
@@ -78,8 +78,9 @@ void room_305_preload() {
 	room_parser_code_pointer = room_305_parser;
 	room_daemon_code_pointer = room_305_daemon;
 
-	section_1_walker();
-	section_1_interface();
+	global_section_walker();
+	global_section_interface();
+	player.walker_must_reload = true;
 }
 
 } // namespace Rooms
diff --git a/engines/mads/madsv2/forest/rooms/room306.cpp b/engines/mads/madsv2/forest/rooms/room306.cpp
index 44c11f19ac9..da270f67e99 100644
--- a/engines/mads/madsv2/forest/rooms/room306.cpp
+++ b/engines/mads/madsv2/forest/rooms/room306.cpp
@@ -78,8 +78,9 @@ void room_306_preload() {
 	room_parser_code_pointer = room_306_parser;
 	room_daemon_code_pointer = room_306_daemon;
 
-	section_1_walker();
-	section_1_interface();
+	global_section_walker();
+	global_section_interface();
+	player.walker_must_reload = true;
 }
 
 } // namespace Rooms
diff --git a/engines/mads/madsv2/forest/rooms/room307.cpp b/engines/mads/madsv2/forest/rooms/room307.cpp
index 04f7f2dd846..bbe1745f3b4 100644
--- a/engines/mads/madsv2/forest/rooms/room307.cpp
+++ b/engines/mads/madsv2/forest/rooms/room307.cpp
@@ -78,8 +78,9 @@ void room_307_preload() {
 	room_parser_code_pointer = room_307_parser;
 	room_daemon_code_pointer = room_307_daemon;
 
-	section_1_walker();
-	section_1_interface();
+	global_section_walker();
+	global_section_interface();
+	player.walker_must_reload = true;
 }
 
 } // namespace Rooms
diff --git a/engines/mads/madsv2/forest/rooms/room308.cpp b/engines/mads/madsv2/forest/rooms/room308.cpp
index 6be0433fca1..247e246d499 100644
--- a/engines/mads/madsv2/forest/rooms/room308.cpp
+++ b/engines/mads/madsv2/forest/rooms/room308.cpp
@@ -78,8 +78,9 @@ void room_308_preload() {
 	room_parser_code_pointer = room_308_parser;
 	room_daemon_code_pointer = room_308_daemon;
 
-	section_1_walker();
-	section_1_interface();
+	global_section_walker();
+	global_section_interface();
+	player.walker_must_reload = true;
 }
 
 } // namespace Rooms
diff --git a/engines/mads/madsv2/forest/rooms/room321.cpp b/engines/mads/madsv2/forest/rooms/room321.cpp
index ff3ef4df085..e4a6aeee089 100644
--- a/engines/mads/madsv2/forest/rooms/room321.cpp
+++ b/engines/mads/madsv2/forest/rooms/room321.cpp
@@ -78,8 +78,9 @@ void room_321_preload() {
 	room_parser_code_pointer = room_321_parser;
 	room_daemon_code_pointer = room_321_daemon;
 
-	section_1_walker();
-	section_1_interface();
+	global_section_walker();
+	global_section_interface();
+	player.walker_must_reload = true;
 }
 
 } // namespace Rooms
diff --git a/engines/mads/madsv2/forest/rooms/room322.cpp b/engines/mads/madsv2/forest/rooms/room322.cpp
index 8f1777d3c83..a5ca31e41d6 100644
--- a/engines/mads/madsv2/forest/rooms/room322.cpp
+++ b/engines/mads/madsv2/forest/rooms/room322.cpp
@@ -78,8 +78,9 @@ void room_322_preload() {
 	room_parser_code_pointer = room_322_parser;
 	room_daemon_code_pointer = room_322_daemon;
 
-	section_1_walker();
-	section_1_interface();
+	global_section_walker();
+	global_section_interface();
+	player.walker_must_reload = true;
 }
 
 } // namespace Rooms
diff --git a/engines/mads/madsv2/forest/rooms/room401.cpp b/engines/mads/madsv2/forest/rooms/room401.cpp
index 4ea88c25dec..63aa362b881 100644
--- a/engines/mads/madsv2/forest/rooms/room401.cpp
+++ b/engines/mads/madsv2/forest/rooms/room401.cpp
@@ -78,8 +78,9 @@ void room_401_preload() {
 	room_parser_code_pointer = room_401_parser;
 	room_daemon_code_pointer = room_401_daemon;
 
-	section_1_walker();
-	section_1_interface();
+	global_section_walker();
+	global_section_interface();
+	player.walker_must_reload = true;
 }
 
 } // namespace Rooms
diff --git a/engines/mads/madsv2/forest/rooms/room402.cpp b/engines/mads/madsv2/forest/rooms/room402.cpp
index 99e263ca4e3..4956ac04328 100644
--- a/engines/mads/madsv2/forest/rooms/room402.cpp
+++ b/engines/mads/madsv2/forest/rooms/room402.cpp
@@ -78,8 +78,9 @@ void room_402_preload() {
 	room_parser_code_pointer = room_402_parser;
 	room_daemon_code_pointer = room_402_daemon;
 
-	section_1_walker();
-	section_1_interface();
+	global_section_walker();
+	global_section_interface();
+	player.walker_must_reload = true;
 }
 
 } // namespace Rooms
diff --git a/engines/mads/madsv2/forest/rooms/room403.cpp b/engines/mads/madsv2/forest/rooms/room403.cpp
index c9e44095b24..e0ddcbd5450 100644
--- a/engines/mads/madsv2/forest/rooms/room403.cpp
+++ b/engines/mads/madsv2/forest/rooms/room403.cpp
@@ -78,8 +78,9 @@ void room_403_preload() {
 	room_parser_code_pointer = room_403_parser;
 	room_daemon_code_pointer = room_403_daemon;
 
-	section_1_walker();
-	section_1_interface();
+	global_section_walker();
+	global_section_interface();
+	player.walker_must_reload = true;
 }
 
 } // namespace Rooms
diff --git a/engines/mads/madsv2/forest/rooms/room404.cpp b/engines/mads/madsv2/forest/rooms/room404.cpp
index 24d2b042ea2..39c6517e3cb 100644
--- a/engines/mads/madsv2/forest/rooms/room404.cpp
+++ b/engines/mads/madsv2/forest/rooms/room404.cpp
@@ -78,8 +78,9 @@ void room_404_preload() {
 	room_parser_code_pointer = room_404_parser;
 	room_daemon_code_pointer = room_404_daemon;
 
-	section_1_walker();
-	section_1_interface();
+	global_section_walker();
+	global_section_interface();
+	player.walker_must_reload = true;
 }
 
 } // namespace Rooms
diff --git a/engines/mads/madsv2/forest/rooms/room405.cpp b/engines/mads/madsv2/forest/rooms/room405.cpp
index 282231b5cf3..8a4fb1797b3 100644
--- a/engines/mads/madsv2/forest/rooms/room405.cpp
+++ b/engines/mads/madsv2/forest/rooms/room405.cpp
@@ -78,8 +78,9 @@ void room_405_preload() {
 	room_parser_code_pointer = room_405_parser;
 	room_daemon_code_pointer = room_405_daemon;
 
-	section_1_walker();
-	section_1_interface();
+	global_section_walker();
+	global_section_interface();
+	player.walker_must_reload = true;
 }
 
 } // namespace Rooms
diff --git a/engines/mads/madsv2/forest/rooms/room420.cpp b/engines/mads/madsv2/forest/rooms/room420.cpp
index 9cda90a1583..6fa3c35a826 100644
--- a/engines/mads/madsv2/forest/rooms/room420.cpp
+++ b/engines/mads/madsv2/forest/rooms/room420.cpp
@@ -78,8 +78,12 @@ void room_420_preload() {
 	room_parser_code_pointer = room_420_parser;
 	room_daemon_code_pointer = room_420_daemon;
 
-	section_1_walker();
-	section_1_interface();
+	if (flags[31] == -4)
+		global[g016] = -1;
+
+	global_section_walker();
+	global_section_interface();
+	player.walker_must_reload = true;
 }
 
 } // namespace Rooms
diff --git a/engines/mads/madsv2/forest/rooms/room501.cpp b/engines/mads/madsv2/forest/rooms/room501.cpp
index 0856cadf4e6..a0f7801ec0f 100644
--- a/engines/mads/madsv2/forest/rooms/room501.cpp
+++ b/engines/mads/madsv2/forest/rooms/room501.cpp
@@ -78,8 +78,9 @@ void room_501_preload() {
 	room_parser_code_pointer = room_501_parser;
 	room_daemon_code_pointer = room_501_daemon;
 
-	section_1_walker();
-	section_1_interface();
+	global_section_walker();
+	global_section_interface();
+	player.walker_must_reload = true;
 }
 
 } // namespace Rooms
diff --git a/engines/mads/madsv2/forest/rooms/room503.cpp b/engines/mads/madsv2/forest/rooms/room503.cpp
index 5ecd14289d0..89187a9c0cf 100644
--- a/engines/mads/madsv2/forest/rooms/room503.cpp
+++ b/engines/mads/madsv2/forest/rooms/room503.cpp
@@ -78,8 +78,9 @@ void room_503_preload() {
 	room_parser_code_pointer = room_503_parser;
 	room_daemon_code_pointer = room_503_daemon;
 
-	section_1_walker();
-	section_1_interface();
+	global_section_walker();
+	global_section_interface();
+	player.walker_must_reload = true;
 }
 
 } // namespace Rooms
diff --git a/engines/mads/madsv2/forest/rooms/room509.cpp b/engines/mads/madsv2/forest/rooms/room509.cpp
index b91d93105db..4bef95018d3 100644
--- a/engines/mads/madsv2/forest/rooms/room509.cpp
+++ b/engines/mads/madsv2/forest/rooms/room509.cpp
@@ -78,8 +78,9 @@ void room_509_preload() {
 	room_parser_code_pointer = room_509_parser;
 	room_daemon_code_pointer = room_509_daemon;
 
-	section_1_walker();
-	section_1_interface();
+	global[g016] = -1;
+	global_section_walker();
+	global_section_interface();
 }
 
 } // namespace Rooms
diff --git a/engines/mads/madsv2/forest/rooms/room510.cpp b/engines/mads/madsv2/forest/rooms/room510.cpp
index ec39479b552..546e036c21f 100644
--- a/engines/mads/madsv2/forest/rooms/room510.cpp
+++ b/engines/mads/madsv2/forest/rooms/room510.cpp
@@ -78,8 +78,9 @@ void room_510_preload() {
 	room_parser_code_pointer = room_510_parser;
 	room_daemon_code_pointer = room_510_daemon;
 
-	section_1_walker();
-	section_1_interface();
+	global_section_walker();
+	global_section_interface();
+	player.walker_must_reload = true;
 }
 
 } // namespace Rooms
diff --git a/engines/mads/madsv2/forest/rooms/room520.cpp b/engines/mads/madsv2/forest/rooms/room520.cpp
index 3413c1f4a33..dd529a92c73 100644
--- a/engines/mads/madsv2/forest/rooms/room520.cpp
+++ b/engines/mads/madsv2/forest/rooms/room520.cpp
@@ -78,8 +78,9 @@ void room_520_preload() {
 	room_parser_code_pointer = room_520_parser;
 	room_daemon_code_pointer = room_520_daemon;
 
-	section_1_walker();
-	section_1_interface();
+	global_section_walker();
+	global_section_interface();
+	player.walker_must_reload = true;
 }
 
 } // namespace Rooms
diff --git a/engines/mads/madsv2/forest/rooms/section1.cpp b/engines/mads/madsv2/forest/rooms/section1.cpp
index 5f3c90d3fc8..77a32937c40 100644
--- a/engines/mads/madsv2/forest/rooms/section1.cpp
+++ b/engines/mads/madsv2/forest/rooms/section1.cpp
@@ -46,12 +46,6 @@ extern void room_199_preload();
 void section_1_init() {
 }
 
-void section_1_walker() {	
-}
-
-void section_1_interface() {
-}
-
 void section_1_music() {
 }
 
diff --git a/engines/mads/madsv2/forest/rooms/section1.h b/engines/mads/madsv2/forest/rooms/section1.h
index a7155c7ab18..52264344270 100644
--- a/engines/mads/madsv2/forest/rooms/section1.h
+++ b/engines/mads/madsv2/forest/rooms/section1.h
@@ -30,8 +30,6 @@ namespace MADSV2 {
 namespace Forest {
 namespace Rooms {
 
-extern void section_1_walker();
-extern void section_1_interface();
 extern void section_1_music();
 
 } // namespace Rooms
diff --git a/engines/mads/madsv2/forest/rooms/section2.cpp b/engines/mads/madsv2/forest/rooms/section2.cpp
index 6612dd7ceb6..3e443ae6d49 100644
--- a/engines/mads/madsv2/forest/rooms/section2.cpp
+++ b/engines/mads/madsv2/forest/rooms/section2.cpp
@@ -49,12 +49,6 @@ extern void room_221_preload();
 void section_2_init() {
 }
 
-void section_2_walker() {
-}
-
-void section_2_interface() {
-}
-
 void section_2_music() {
 }
 
diff --git a/engines/mads/madsv2/forest/rooms/section2.h b/engines/mads/madsv2/forest/rooms/section2.h
index 74f6ad42cab..b40b8bc0b96 100644
--- a/engines/mads/madsv2/forest/rooms/section2.h
+++ b/engines/mads/madsv2/forest/rooms/section2.h
@@ -29,8 +29,6 @@ namespace MADSV2 {
 namespace Forest {
 namespace Rooms {
 
-extern void section_2_walker();
-extern void section_2_interface();
 extern void section_2_music();
 
 } // namespace Rooms
diff --git a/engines/mads/madsv2/forest/rooms/section3.cpp b/engines/mads/madsv2/forest/rooms/section3.cpp
index 001137f0332..0a27ddaa3c8 100644
--- a/engines/mads/madsv2/forest/rooms/section3.cpp
+++ b/engines/mads/madsv2/forest/rooms/section3.cpp
@@ -50,12 +50,6 @@ extern void room_322_preload();
 void section_3_init() {
 }
 
-void section_3_walker() {
-}
-
-void section_3_interface() {
-}
-
 void section_3_music() {
 }
 
diff --git a/engines/mads/madsv2/forest/rooms/section3.h b/engines/mads/madsv2/forest/rooms/section3.h
index c336dae2b96..492bd3dd024 100644
--- a/engines/mads/madsv2/forest/rooms/section3.h
+++ b/engines/mads/madsv2/forest/rooms/section3.h
@@ -29,8 +29,6 @@ namespace MADSV2 {
 namespace Forest {
 namespace Rooms {
 
-extern void section_3_walker();
-extern void section_3_interface();
 extern void section_3_music();
 
 } // namespace Rooms
diff --git a/engines/mads/madsv2/forest/rooms/section4.cpp b/engines/mads/madsv2/forest/rooms/section4.cpp
index 7c43a725519..0bb6771dfdf 100644
--- a/engines/mads/madsv2/forest/rooms/section4.cpp
+++ b/engines/mads/madsv2/forest/rooms/section4.cpp
@@ -46,12 +46,6 @@ extern void room_420_preload();
 void section_4_init() {
 }
 
-void section_4_walker() {
-}
-
-void section_4_interface() {
-}
-
 void section_4_music() {
 }
 
diff --git a/engines/mads/madsv2/forest/rooms/section4.h b/engines/mads/madsv2/forest/rooms/section4.h
index 0cb39c29ddf..4072cc1eebe 100644
--- a/engines/mads/madsv2/forest/rooms/section4.h
+++ b/engines/mads/madsv2/forest/rooms/section4.h
@@ -29,8 +29,6 @@ namespace MADSV2 {
 namespace Forest {
 namespace Rooms {
 
-extern void section_4_walker();
-extern void section_4_interface();
 extern void section_4_music();
 
 } // namespace Rooms
diff --git a/engines/mads/madsv2/forest/rooms/section5.cpp b/engines/mads/madsv2/forest/rooms/section5.cpp
index c9ef2163620..1e37f9c1d8e 100644
--- a/engines/mads/madsv2/forest/rooms/section5.cpp
+++ b/engines/mads/madsv2/forest/rooms/section5.cpp
@@ -46,12 +46,6 @@ void section_5_init() {
 	player.scaling_velocity = true;
 }
 
-void section_5_walker() {
-}
-
-void section_5_interface() {
-}
-
 void section_5_music() {
 }
 
diff --git a/engines/mads/madsv2/forest/rooms/section5.h b/engines/mads/madsv2/forest/rooms/section5.h
index 01aacfde8c5..640903ebbbb 100644
--- a/engines/mads/madsv2/forest/rooms/section5.h
+++ b/engines/mads/madsv2/forest/rooms/section5.h
@@ -29,8 +29,6 @@ namespace MADSV2 {
 namespace Forest {
 namespace Rooms {
 
-extern void section_5_walker();
-extern void section_5_interface();
 extern void section_5_music();
 
 } // namespace Rooms



From noreply at scummvm.org  Sun Jun  7 05:55:36 2026
From: noreply at scummvm.org (bluegr)
Date: Sun,  7 Jun 2026 05:55:36 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 9f31d256415fb6a667dc4bad037da4c59384ef5d
Message-ID: <20260607055536.79B897005BC@ron.scummvm.net>

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:
c1653febe9 NANCY: More work on the gridmappuzzle in Nancy10
c6df72b129 NANCY: Fix incoming call sequence in Nancy10+
9f31d25641 NANCY: Use the per-frame blit array when checking for overlay hotspots


Commit: c1653febe9650952aa4738bae345151b3b77b2c5
    https://github.com/scummvm/scummvm/commit/c1653febe9650952aa4738bae345151b3b77b2c5
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-06-07T08:55:22+03:00

Commit Message:
NANCY: More work on the gridmappuzzle in Nancy10

- Glyph sprites are now drawn correctly
- Glyphs now appear correctly when discovering them
- Glyphs now render correctly when placing them on the map
- Glyphs can only be dropped at specific locations on the map grid
- Letters are rendered correctly when dropping glyphs on the map

Changed paths:
    engines/nancy/action/puzzle/gridmappuzzle.cpp
    engines/nancy/action/puzzle/gridmappuzzle.h


diff --git a/engines/nancy/action/puzzle/gridmappuzzle.cpp b/engines/nancy/action/puzzle/gridmappuzzle.cpp
index 0fee0e4d43a..21d1ccbc0f4 100644
--- a/engines/nancy/action/puzzle/gridmappuzzle.cpp
+++ b/engines/nancy/action/puzzle/gridmappuzzle.cpp
@@ -78,9 +78,9 @@ void GridMapPuzzle::readData(Common::SeekableReadStream &stream) {
 	stream.skip(1);
 
 	for (int i = 0; i < kMaxItems; ++i)
-		_leftHalfIdx[i] = stream.readSint16LE();
+		_letterByMapRow[i] = stream.readSint16LE();
 	for (int i = 0; i < kMaxItems; ++i)
-		_rightHalfIdx[i] = stream.readSint16LE();
+		_letterByMapCol[i] = stream.readSint16LE();
 	for (int i = 0; i < kMaxItems; ++i)
 		_resultSlot[i] = stream.readSint16LE();
 
@@ -131,6 +131,7 @@ void GridMapPuzzle::readData(Common::SeekableReadStream &stream) {
 
 void GridMapPuzzle::initState() {
 	GridMapPuzzleData *gmd = (GridMapPuzzleData *)NancySceneState.getPuzzleData(GridMapPuzzleData::getTag());
+
 	if (_retainState && gmd && gmd->itemState.size() >= (uint)_numItems * 6) {
 		const Common::Array<int16> &state = gmd->itemState;
 		for (int i = 0; i < (int)_numItems; ++i) {
@@ -141,17 +142,18 @@ void GridMapPuzzle::initState() {
 			_items[i].itemsRow = state[i * 6 + 4];
 			_items[i].itemsCol = state[i * 6 + 5];
 		}
-		return;
+	} else {
+		for (int i = 0; i < kMaxItems; ++i)
+			_items[i] = ItemSlot();
 	}
 
-	for (int i = 0; i < kMaxItems; ++i)
-		_items[i] = ItemSlot();
-
-	// Items with their autoplace flag set start placed in the items grid
-	// (acting as the source/inventory). Other items stay hidden until released
-	// by gameplay events outside this puzzle.
+	// Always re-check autoplace flags: items released after the last save
+	// (e.g. discovered by examining a wall between visits) should appear in
+	// the items grid even when restoring a previous state.
 	const int itemsCols = MAX<int>(1, (int)_itemsCols);
 	for (int i = 0; i < (int)_numItems; ++i) {
+		if (_items[i].inMap || _items[i].inItems)
+			continue;
 		if (_autoPlaceFlag[i] != -1 && NancySceneState.getEventFlag(_autoPlaceFlag[i], g_nancy->_true)) {
 			_items[i].inItems = true;
 			_items[i].itemsRow = (int16)(i / itemsCols);
@@ -189,6 +191,9 @@ void GridMapPuzzle::init() {
 	g_nancy->_resource->loadImage(_boardImageName, _boardImage);
 	_boardImage.setTransparentColor(_drawSurface.getTransparentColor());
 
+	g_nancy->_resource->loadImage(_cursorImageName, _cursorImage);
+	_cursorImage.setTransparentColor(_drawSurface.getTransparentColor());
+
 	initState();
 
 	_heldItem = -1;
@@ -256,14 +261,16 @@ void GridMapPuzzle::execute() {
 }
 
 Common::Rect GridMapPuzzle::mapCellRect(int row, int col) const {
-	int x = (int)_mapOriginX + col * ((int)_mapSpacingX + _mapCellW);
-	int y = (int)_mapOriginY + row * ((int)_mapSpacingY + _mapCellH);
+	// Stride uses the raw src-rect dimensions (right - left, before readRect's
+	// inclusive???exclusive +1), to match the original's cell layout.
+	int x = (int)_mapOriginX + col * ((int)_mapSpacingX + _mapCellW - 1);
+	int y = (int)_mapOriginY + row * ((int)_mapSpacingY + _mapCellH - 1);
 	return Common::Rect(x, y, x + _mapCellW, y + _mapCellH);
 }
 
 Common::Rect GridMapPuzzle::itemsCellRect(int row, int col) const {
-	int x = (int)_itemsOriginX + col * ((int)_itemsSpacingX + _itemsCellW);
-	int y = (int)_itemsOriginY + row * ((int)_itemsSpacingY + _itemsCellH);
+	int x = (int)_itemsOriginX + col * ((int)_itemsSpacingX + _itemsCellW - 1);
+	int y = (int)_itemsOriginY + row * ((int)_itemsSpacingY + _itemsCellH - 1);
 	return Common::Rect(x, y, x + _itemsCellW, y + _itemsCellH);
 }
 
@@ -309,20 +316,24 @@ int GridMapPuzzle::findItemInItems(int row, int col) const {
 	return -1;
 }
 
-Common::Rect GridMapPuzzle::resultsCellRect(int row, int col) const {
-	int x = (int)_resultsOriginX + col * ((int)_resultsSpacingX + _resultsCellW);
-	int y = (int)_resultsOriginY + row * ((int)_resultsSpacingY + _resultsCellH);
-	return Common::Rect(x, y, x + _resultsCellW, y + _resultsCellH);
-}
-
-bool GridMapPuzzle::isCorrectMapPlacement(int item, int row, int col) const {
+bool GridMapPuzzle::isValidMapSlot(int row, int col) const {
+	// A map cell is a real placement slot iff at least one solution places
+	// some item there. Empty map cells outside any solution reject drops.
 	for (int s = 0; s < (int)_numSolutions; ++s) {
-		if (_solutionRows[s][item] == (int16)row && _solutionCols[s][item] == (int16)col)
-			return true;
+		for (int i = 0; i < (int)_numItems; ++i) {
+			if (_solutionRows[s][i] == (int16)row && _solutionCols[s][i] == (int16)col)
+				return true;
+		}
 	}
 	return false;
 }
 
+Common::Rect GridMapPuzzle::resultsCellRect(int row, int col) const {
+	int x = (int)_resultsOriginX + col * ((int)_resultsSpacingX + _resultsCellW - 1);
+	int y = (int)_resultsOriginY + row * ((int)_resultsSpacingY + _resultsCellH - 1);
+	return Common::Rect(x, y, x + _resultsCellW, y + _resultsCellH);
+}
+
 void GridMapPuzzle::handleInput(NancyInput &input) {
 	if (_state != kRun || _subState != kPlaying)
 		return;
@@ -332,6 +343,7 @@ void GridMapPuzzle::handleInput(NancyInput &input) {
 
 	if (_heldItem != -1 && _heldDrawPos != mouseVP) {
 		_heldDrawPos = mouseVP;
+		_skipHeldDraw = false;
 		redraw();
 	}
 
@@ -359,6 +371,16 @@ void GridMapPuzzle::handleInput(NancyInput &input) {
 	if (!(input.input & NancyInput::kLeftMouseButtonUp))
 		return;
 
+	// Reset the held-draw suppression flag on every click; the swap branch
+	// below sets it back to true when appropriate.
+	_skipHeldDraw = false;
+
+	// Sync the held draw position to the click point. Mouse-move tracking
+	// only runs while something is held, so without this a pickup right
+	// after a plain drop would render the new held glyph at the previous
+	// drop's coordinates for one frame.
+	_heldDrawPos = mouseVP;
+
 	int existingItem = hitMap ? findItemInMap(row, col) : findItemInItems(iRow, iCol);
 
 	if (_heldItem == -1) {
@@ -374,23 +396,27 @@ void GridMapPuzzle::handleInput(NancyInput &input) {
 			g_nancy->_sound->playSound(_pickupSound);
 		}
 	} else {
-		// Drops onto the map grid only succeed when the cell is the correct
-		// placement for the held item per any solution; otherwise the cell
-		// rejects the drop and the item stays held.
-		if (hitMap && !isCorrectMapPlacement(_heldItem, row, col))
+		// Drop. Map cells only accept the held item if they are real
+		// placement slots (i.e. used by some solution); items cells always
+		// accept. An occupied cell sends its current occupant back to the
+		// cursor so the player can swap.
+		if (hitMap && !isValidMapSlot(row, col))
 			return;
 
 		if (existingItem != -1) {
-			// Items-grid cells allow swap: the existing item becomes the new
-			// held one. Map cells are gated by the correctness check above, so
-			// they can't be occupied by anything except the held item itself.
-			if (hitMap)
-				return;
-			_items[existingItem].inItems = false;
-			_items[_heldItem].inItems = true;
-			_items[_heldItem].itemsRow = (int16)iRow;
-			_items[_heldItem].itemsCol = (int16)iCol;
+			if (hitMap) {
+				_items[existingItem].inMap = false;
+				_items[_heldItem].inMap = true;
+				_items[_heldItem].mapRow = (int16)row;
+				_items[_heldItem].mapCol = (int16)col;
+			} else {
+				_items[existingItem].inItems = false;
+				_items[_heldItem].inItems = true;
+				_items[_heldItem].itemsRow = (int16)iRow;
+				_items[_heldItem].itemsCol = (int16)iCol;
+			}
 			_heldItem = existingItem;
+			_skipHeldDraw = true;
 			if (_pickupSound.name != "NO SOUND") {
 				g_nancy->_sound->loadSound(_pickupSound);
 				g_nancy->_sound->playSound(_pickupSound);
@@ -446,6 +472,8 @@ void GridMapPuzzle::redraw() {
 
 	for (int i = 0; i < (int)_numItems; ++i) {
 		const ItemSlot &slot = _items[i];
+		// Small map-glyph atlas is on the board image; large items-grid
+		// sprites and the drag cursor are on the cursor image.
 		if (slot.inMap && slot.mapRow >= 0 && slot.mapCol >= 0) {
 			const Common::Rect &src = _mapItemSrcRects[i];
 			if (!src.isEmpty()) {
@@ -457,14 +485,16 @@ void GridMapPuzzle::redraw() {
 			const Common::Rect &src = _itemsItemSrcRects[i];
 			if (!src.isEmpty()) {
 				Common::Rect dst = itemsCellRect(slot.itemsRow, slot.itemsCol);
-				_drawSurface.blitFrom(_boardImage, src, Common::Point(dst.left, dst.top));
+				_drawSurface.blitFrom(_cursorImage, src, Common::Point(dst.left, dst.top));
 			}
 		}
 	}
 
 	// Each item placed in the map contributes two letter halves to the results
-	// bar (its assigned slot indexes a pair of adjacent cells). When all items
-	// are correctly placed the letters spell out the solution sentence.
+	// bar at the item's fixed slot. The letters themselves are looked up from
+	// the placement coordinates: column picks the left half, row the right.
+	// When the right items end up at the right cells the strip spells out
+	// the solution sentence.
 	const int resultsCols = MAX<int>(1, (int)_resultsCols);
 	for (int i = 0; i < (int)_numItems; ++i) {
 		if (!_items[i].inMap)
@@ -475,8 +505,10 @@ void GridMapPuzzle::redraw() {
 		const int base = slot * 2;
 		const int row  = base / resultsCols;
 		const int col  = base % resultsCols;
-		const int li = _leftHalfIdx[i];
-		const int ri = _rightHalfIdx[i];
+		const int mapRow = (int)_items[i].mapRow;
+		const int mapCol = (int)_items[i].mapCol;
+		const int li = (mapCol >= 0 && mapCol < kMaxItems) ? _letterByMapCol[mapCol] : -1;
+		const int ri = (mapRow >= 0 && mapRow < kMaxItems) ? _letterByMapRow[mapRow] : -1;
 		if (li >= 0 && li < kMaxResultRects && !_resultSrcRects[li].isEmpty()) {
 			Common::Rect dst = resultsCellRect(row, col);
 			_drawSurface.blitFrom(_boardImage, _resultSrcRects[li], Common::Point(dst.left, dst.top));
@@ -487,14 +519,14 @@ void GridMapPuzzle::redraw() {
 		}
 	}
 
-	if (_heldItem >= 0 && _heldItem < (int)_numItems) {
+	if (_heldItem >= 0 && _heldItem < (int)_numItems && !_skipHeldDraw) {
 		const Common::Rect &src = _itemsItemSrcRects[_heldItem].isEmpty()
 		                          ? _mapItemSrcRects[_heldItem]
 		                          : _itemsItemSrcRects[_heldItem];
 		if (!src.isEmpty()) {
 			int x = _heldDrawPos.x - src.width()  / 2;
 			int y = _heldDrawPos.y - src.height() / 2;
-			_drawSurface.blitFrom(_boardImage, src, Common::Point(x, y));
+			_drawSurface.blitFrom(_cursorImage, src, Common::Point(x, y));
 		}
 	}
 
diff --git a/engines/nancy/action/puzzle/gridmappuzzle.h b/engines/nancy/action/puzzle/gridmappuzzle.h
index 4630e56513b..0a0b6c53ba1 100644
--- a/engines/nancy/action/puzzle/gridmappuzzle.h
+++ b/engines/nancy/action/puzzle/gridmappuzzle.h
@@ -93,15 +93,18 @@ protected:
 	Common::Rect _itemsItemSrcRects[kMaxItems];
 	Common::Rect _resultSrcRects[kMaxResultRects];
 
-	int16 _leftHalfIdx[kMaxItems];
-	int16 _rightHalfIdx[kMaxItems];
-	int16 _resultSlot[kMaxItems];
+	// Result-letter lookups are indexed by the placed item's MAP coordinates,
+	// not by its item index. _letterByMapCol[mapCol] is rendered as the left
+	// half of the item's letter pair; _letterByMapRow[mapRow] as the right.
+	int16 _letterByMapRow[kMaxItems] = {};
+	int16 _letterByMapCol[kMaxItems] = {};
+	int16 _resultSlot[kMaxItems] = {};
 
-	int16 _autoPlaceFlag[kMaxItems];
+	int16 _autoPlaceFlag[kMaxItems] = {};
 
 	uint16 _numSolutions = 0;
-	int16 _solutionRows[kMaxSolutions][kMaxItems];
-	int16 _solutionCols[kMaxSolutions][kMaxItems];
+	int16 _solutionRows[kMaxSolutions][kMaxItems] = {};
+	int16 _solutionCols[kMaxSolutions][kMaxItems] = {};
 
 	SoundDescription _pickupSound;
 	SoundDescription _placeSound;
@@ -129,9 +132,14 @@ protected:
 	ItemSlot _items[kMaxItems];
 	int      _heldItem = -1;
 	Common::Point _heldDrawPos;
+	// Set on a swap (drop onto an occupied cell) so the freshly-placed
+	// glyph isn't covered by the picked-up one drawn at the cursor.
+	// Cleared on the next mouse move.
+	bool     _skipHeldDraw = false;
 	bool     _isSolved = false;
 
 	Graphics::ManagedSurface _boardImage;
+	Graphics::ManagedSurface _cursorImage; // item sprite atlas (right-side panel)
 
 	void initState();
 	void persistState();
@@ -143,7 +151,7 @@ protected:
 	bool hitTestItems(const Common::Point &p, int &outRow, int &outCol) const;
 	int findItemInMap(int row, int col) const;
 	int findItemInItems(int row, int col) const;
-	bool isCorrectMapPlacement(int item, int row, int col) const;
+	bool isValidMapSlot(int row, int col) const;
 	void checkSolved();
 };
 


Commit: c6df72b129d8daab1c09d348ce98597e36c37792
    https://github.com/scummvm/scummvm/commit/c6df72b129d8daab1c09d348ce98597e36c37792
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-06-07T08:55:24+03:00

Commit Message:
NANCY: Fix incoming call sequence in Nancy10+

Changed paths:
    engines/nancy/action/miscrecords.cpp
    engines/nancy/ui/cellphonepopup.cpp
    engines/nancy/ui/cellphonepopup.h


diff --git a/engines/nancy/action/miscrecords.cpp b/engines/nancy/action/miscrecords.cpp
index f3054b3afbe..7ac1e104dcd 100644
--- a/engines/nancy/action/miscrecords.cpp
+++ b/engines/nancy/action/miscrecords.cpp
@@ -221,7 +221,6 @@ void ControlUIItems::execute() {
 			break;
 		case kUITypeCellphone: {
 			UI::CellPhonePopup &phone = NancySceneState.getCellPhonePopup();
-			phone.open();
 
 			if (_startScene != (int16)kNoScene) {
 				SceneChangeDescription scene;
@@ -230,11 +229,10 @@ void ControlUIItems::execute() {
 				scene.verticalOffset = 0;
 				// The destination scene's sound carries the conversation audio.
 				scene.continueSceneSound = kLoadSceneSound;
-
-				// Save the pre-call scene on the popup so AR 128 returns
-				// there without touching the global push slot.
-				phone.setReturnScene(NancySceneState.getSceneInfo());
-				NancySceneState.changeScene(scene);
+				// Phone rings, picks up, and changeScenes into `scene`.
+				phone.startIncomingCall(scene);
+			} else {
+				phone.open();
 			}
 			break;
 		}
diff --git a/engines/nancy/ui/cellphonepopup.cpp b/engines/nancy/ui/cellphonepopup.cpp
index d9aab409861..dcec5b05f61 100644
--- a/engines/nancy/ui/cellphonepopup.cpp
+++ b/engines/nancy/ui/cellphonepopup.cpp
@@ -265,6 +265,21 @@ void CellPhonePopup::open() {
 	}
 }
 
+void CellPhonePopup::startIncomingCall(const SceneChangeDescription &scene) {
+	// open() resets state, so save the pending scene afterwards. Joining
+	// kPlaceCall hands off to the existing ring / pickup / connect chain;
+	// kLookupContact skips the contact lookup when _hasPendingCallScene
+	// is set, and kConnected uses the stored scene for changeScene.
+	if (!_isVisible) {
+		open();
+	}
+	_pendingCallScene = scene;
+	_hasPendingCallScene = true;
+	_resolvedContact = -1;
+	resetDialPad();
+	enterScreenState(kPlaceCall);
+}
+
 void CellPhonePopup::close() {
 	if (!_isVisible) {
 		return;
@@ -274,6 +289,9 @@ void CellPhonePopup::close() {
 		g_nancy->_sound->stopSound(_callSound);
 	}
 
+	// Closing the phone while ringing declines the call.
+	_hasPendingCallScene = false;
+
 	setVisible(false);
 
 	if (!_uiclData->header.sounds[1].name.empty()) {
@@ -314,14 +332,19 @@ void CellPhonePopup::updateGraphics() {
 		break;
 
 	case kLookupContact: {
-		// Directory-mode calls pre-resolve the contact, so only fall back
-		// to dial-buffer lookup when the contact isn't already known.
-		if (_resolvedContact == -1) {
-			_resolvedContact = findContactByDialBuffer();
+		// Incoming calls already know the destination scene, so the
+		// contact lookup is skipped. Directory-mode outgoing calls
+		// pre-resolve the contact, leaving only dial-buffer lookup.
+		if (!_hasPendingCallScene) {
+			if (_resolvedContact == -1) {
+				_resolvedContact = findContactByDialBuffer();
+			}
+			if (_resolvedContact == -1) {
+				enterScreenState(kInvalidNumber);
+				break;
+			}
 		}
-		if (_resolvedContact == -1) {
-			enterScreenState(kInvalidNumber);
-		} else if (playSoundIfPresent(_uiclData->pickupSound)) {
+		if (playSoundIfPresent(_uiclData->pickupSound)) {
 			enterScreenState(kWaitPickup);
 		} else {
 			enterScreenState(kConnected);
@@ -340,7 +363,15 @@ void CellPhonePopup::updateGraphics() {
 		// Trigger the scene change once, then sit in kConnected so the
 		// connecting sprite stays on screen for the duration of the
 		// conversation. AR 128 closes the popup when the call ends.
-		if (_resolvedContact >= 0 &&
+		// Incoming calls carry their destination in _pendingCallScene;
+		// outgoing calls resolve it from the active contact.
+		if (_hasPendingCallScene) {
+			SceneChangeDescription scene = _pendingCallScene;
+			_hasPendingCallScene = false;
+			setReturnScene(NancySceneState.getSceneInfo());
+			NancySceneState.changeScene(scene);
+			resetDialPad();
+		} else if (_resolvedContact >= 0 &&
 				_resolvedContact < (int)_contacts.size()) {
 			triggerContactCallSceneChange((uint)_resolvedContact);
 			_resolvedContact = -1;
diff --git a/engines/nancy/ui/cellphonepopup.h b/engines/nancy/ui/cellphonepopup.h
index 0536c135a7f..9a2759b502b 100644
--- a/engines/nancy/ui/cellphonepopup.h
+++ b/engines/nancy/ui/cellphonepopup.h
@@ -71,6 +71,12 @@ public:
 	void setReturnScene(const SceneChangeDescription &scene);
 	bool consumeReturnScene(SceneChangeDescription &out);
 
+	// Start an incoming-call sequence: opens the popup, stores the
+	// destination scene, and joins the kPlaceCall state chain so it
+	// rings, picks up, shows the connecting sprite, and changeScenes
+	// into `scene` (AR 128 returns via the setReturnScene slot).
+	void startIncomingCall(const SceneChangeDescription &scene);
+
 private:
 	enum ScreenState : int {
 		kWelcome          = 0,
@@ -219,6 +225,11 @@ private:
 	bool _noSignal = false;
 	bool _batteryLow = false;
 
+	// Incoming-call destination (set by startIncomingCall, consumed by the
+	// kConnected handler once the player has answered).
+	SceneChangeDescription _pendingCallScene;
+	bool _hasPendingCallScene = false;
+
 	SceneChangeDescription _returnScene;
 	bool _hasReturnScene = false;
 };


Commit: 9f31d256415fb6a667dc4bad037da4c59384ef5d
    https://github.com/scummvm/scummvm/commit/9f31d256415fb6a667dc4bad037da4c59384ef5d
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-06-07T08:55:25+03:00

Commit Message:
NANCY: Use the per-frame blit array when checking for overlay hotspots

This check is now done like the ones before it, to ensure uniformity

Changed paths:
    engines/nancy/action/overlay.cpp


diff --git a/engines/nancy/action/overlay.cpp b/engines/nancy/action/overlay.cpp
index f4da96d9787..dcf30656ceb 100644
--- a/engines/nancy/action/overlay.cpp
+++ b/engines/nancy/action/overlay.cpp
@@ -347,7 +347,7 @@ void Overlay::execute() {
 						} else {
 							// nancy3 added a per-frame flag for hotspots. This allows the overlay to be clickable
 							// even without a scene change (useful for setting flags).
-							if (_blitDescriptions[i].hasHotspot == kPlayOverlayWithHotspot) {
+							if (_blitDescriptions[blitsForThisFrame[i]].hasHotspot == kPlayOverlayWithHotspot) {
 								_hotspot = _screenPosition;
 								_hasHotspot = true;
 							}



From noreply at scummvm.org  Sun Jun  7 06:13:49 2026
From: noreply at scummvm.org (dreammaster)
Date: Sun,  7 Jun 2026 06:13:49 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 fad0168d342db0803073701d6e58db6324678344
Message-ID: <20260607061349.048677005BC@ron.scummvm.net>

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

Summary:
fad0168d34 MADS: FOREST: Implemented room 205


Commit: fad0168d342db0803073701d6e58db6324678344
    https://github.com/scummvm/scummvm/commit/fad0168d342db0803073701d6e58db6324678344
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-06-07T16:13:41+10:00

Commit Message:
MADS: FOREST: Implemented room 205

Changed paths:
    engines/mads/madsv2/forest/rooms/room203.cpp
    engines/mads/madsv2/forest/rooms/room204.cpp
    engines/mads/madsv2/forest/rooms/room205.cpp


diff --git a/engines/mads/madsv2/forest/rooms/room203.cpp b/engines/mads/madsv2/forest/rooms/room203.cpp
index a64b2fa3af5..5b5d2f67ddd 100644
--- a/engines/mads/madsv2/forest/rooms/room203.cpp
+++ b/engines/mads/madsv2/forest/rooms/room203.cpp
@@ -1083,6 +1083,7 @@ static void room_203_daemon() {
 			kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
 			global[g150] = -1;
 			local->_90 = global[g046] ? 16 : 15;
+			break;
 
 		case 31:
 			kernel_timing_trigger(40, 106);
diff --git a/engines/mads/madsv2/forest/rooms/room204.cpp b/engines/mads/madsv2/forest/rooms/room204.cpp
index c1a28a8c961..4567ab9eddb 100644
--- a/engines/mads/madsv2/forest/rooms/room204.cpp
+++ b/engines/mads/madsv2/forest/rooms/room204.cpp
@@ -503,6 +503,7 @@ static void room_204_anim5() {
 		scratch._a4 = -1;
 	}
 }
+
 static void room_204_anim6() {
 	if (kernel_anim[aa[5]].frame != aainfo[5]._frame) {
 		aainfo[5]._frame = kernel_anim[aa[5]].frame;
diff --git a/engines/mads/madsv2/forest/rooms/room205.cpp b/engines/mads/madsv2/forest/rooms/room205.cpp
index 1b1bd136598..3bc219ec5e0 100644
--- a/engines/mads/madsv2/forest/rooms/room205.cpp
+++ b/engines/mads/madsv2/forest/rooms/room205.cpp
@@ -19,18 +19,19 @@
  *
  */
 
-#include "mads/madsv2/core/conv.h"
+#include "mads/madsv2/core/digi.h"
 #include "mads/madsv2/core/game.h"
+#include "mads/madsv2/core/global.h"
 #include "mads/madsv2/core/imath.h"
 #include "mads/madsv2/core/inter.h"
 #include "mads/madsv2/core/kernel.h"
-#include "mads/madsv2/core/sound.h"
-#include "mads/madsv2/core/text.h"
-#include "mads/madsv2/forest/mads/inventory.h"
-#include "mads/madsv2/forest/mads/sounds.h"
-#include "mads/madsv2/forest/mads/words.h"
+#include "mads/madsv2/core/matte.h"
+#include "mads/madsv2/core/midi.h"
+#include "mads/madsv2/core/object.h"
+#include "mads/madsv2/core/player.h"
 #include "mads/madsv2/forest/global.h"
-#include "mads/madsv2/forest/rooms/section1.h"
+#include "mads/madsv2/forest/journal.h"
+#include "mads/madsv2/forest/rooms/section2.h"
 #include "mads/madsv2/forest/rooms/room205.h"
 
 namespace MADS {
@@ -39,10 +40,23 @@ namespace Forest {
 namespace Rooms {
 
 struct Scratch {
-	int16 sprite[10];       /* Sprite series handles */
-	int16 sequence[10];     /* Sequence handles      */
-	int16 animation[10];     /* Animation handles     */
-	AnimationInfo animation_info[10];
+	int16 sprite[10];                /* Sprite series handles  0x00 */
+	int16 sequence[10];              /* Sequence handles       0x14 */
+	int16 animation[10];             /* Animation handles      0x28 */
+	AnimationInfo animation_info[10];/* Animation info         0x3C */
+	int16 _8c;                       /*                        0x8C */
+	int16 _8e;                       /*                        0x8E */
+	int16 _90;                       /*                        0x90 */
+	int16 _92;                       /*                        0x92 */
+	int16 _94;
+	int16 _96;
+	int16 _98;
+	int16 _9a;                       /*                        0x9A */
+	int16 _9c;                       /*                        0x9C */
+	int16 _9e;                       /*                        0x9E */
+	int16 _a0;                       /*                        0xA0 */
+	int16 _a2;                       /*                        0xA2 */
+	int16 _a4;                       /*                        0xA4 */
 };
 
 static Scratch scratch;
@@ -53,23 +67,421 @@ static Scratch scratch;
 #define aa    local->animation
 #define aainfo scratch.animation_info
 
+static void room_205_init1() {
+	global[g131] = 0;
+	global[g141] = 0;
+	scratch._9c = kernel_run_animation_disp('r', 9, 0);
+	kernel_position_anim(scratch._9c, 76, 130, 62, 5);
+	scratch._9a = kernel_run_animation_disp('e', 9, 0);
+	kernel_position_anim(scratch._9a, 120, 138, 74, 4);
+
+	if (previous_room == KERNEL_RESTORING_GAME) {
+		global[g131] = -1;
+		global[g141] = -1;
+		kernel_reset_animation(scratch._9a, 2);
+		kernel_reset_animation(scratch._9c, 2);
+		global[g133] = 0;
+		global[g143] = 0;
+		return;
+	}
+
+	player.x = 101;
+	player.y = 124;
+	player.facing = 3;
+
+	if (previous_room == 101) {
+		global[player_score] = 0;
+		global[g009] = 0;
+		digi_stop(3);
+		midi_stop();
+		viewing_at_y = 22;
+		flags[10]--;
+		player.commands_allowed = 0;
+		player.walker_visible = 0;
+		aa[6] = kernel_run_animation(kernel_name('t', 1), 104);
+		aainfo[6]._active = -1;
+		return;
+	} else if (previous_room == 199) {
+		global[g131] = -1;
+		global[g141] = -1;
+		kernel_reset_animation(scratch._9a, 2);
+		kernel_reset_animation(scratch._9c, 2);
+		global[g133] = 0;
+		global[g143] = 0;
+		restore_player();
+	} else if (previous_room == 204) {
+		aa[0] = kernel_run_animation(kernel_name('y', 1), 100);
+		aainfo[0]._active = -1;
+		scratch._92 = 57;
+		return;
+	} else if (previous_room == 301) {
+		aa[0] = kernel_run_animation(kernel_name('y', 2), 100);
+		aainfo[0]._active = -1;
+		scratch._92 = 91;
+		return;
+	} else {
+		global[g131] = -1;
+		global[g141] = -1;
+		kernel_reset_animation(scratch._9a, 2);
+		kernel_reset_animation(scratch._9c, 2);
+		global[g133] = 0;
+		global[g143] = 0;
+	}
+
+	player.commands_allowed = -1;
+	player.walker_visible = -1;
+}
 
 static void room_205_init() {
+	scratch._a4 = 0;
+
+	if (previous_room != KERNEL_RESTORING_GAME) {
+		if (previous_room != 199) {
+			player.walker_visible = 0;
+			player.commands_allowed = 0;
+		}
+
+		for (int count = 0; count < 10; count++) {
+			aainfo[count]._active = 0;
+			aainfo[count]._frame = -1;
+			aainfo[count]._val3 = 0;
+			aainfo[count]._val4 = 0;
+		}
+
+		if (previous_room != 199 && flags[10] != 3)
+			flags[10]++;
+	}
+
+	if (flags[10] == -3)
+		flags[10] = 1;
+	room_205_init1();
+}
+
+static void room_205_anim1() {
+	if (kernel_anim[aa[0]].frame != aainfo[0]._frame)
+		aainfo[0]._frame = kernel_anim[aa[0]].frame;
+
+	if (global[player_hyperwalked] == -1) {
+		aainfo[0]._frame = scratch._92 - 1;
+		kernel_reset_animation(aa[0], aainfo[0]._frame);
+	}
+}
+
+static void room_205_anim2() {
+	if (kernel_anim[aa[1]].frame != aainfo[1]._frame)
+		aainfo[1]._frame = kernel_anim[aa[1]].frame;
+
+	if (scratch._a4 == 0)
+		return;
+
+	kernel_random_frame(scratch._9e, &global[g151], global[g154]);
+	kernel_random_frame(scratch._a2, &global[g153], global[g156]);
+
+	if (kernel.trigger != 7 && kernel.trigger != 28)
+		return;
+
+	if (scratch._a4 == 1) {
+		global[g154] = 0;
+		kernel_timing_trigger(20, 28);
+		scratch._a4 = 2;
+	} else if (scratch._a4 == 2) {
+		global[g156] = 2;
+		scratch._a4 = 3;
+		digi_play_build(205, 'e', 1, 1);
+	} else if (scratch._a4 == 3) {
+		scratch._a4 = 4;
+		kernel_abort_animation(scratch._9e);
+		kernel_abort_animation(scratch._a2);
+		player.commands_allowed = -1;
+		player.walker_visible = -1;
+		global[g017] = -1;
+		aainfo[1]._active = 0;
+		kernel_synch(2, 0, 4, 0);
+		global[g131] = -1;
+		global[g141] = -1;
+		kernel_reset_animation(scratch._9a, 1);
+		kernel_reset_animation(scratch._9c, 1);
+		kernel_synch(3, scratch._9a, 4, 0);
+		kernel_synch(3, scratch._9c, 4, 0);
+		global[g133] = 0;
+		global[g143] = 0;
+	}
+}
+
+static void room_205_anim3() {
+	if (kernel_anim[aa[3]].frame != aainfo[3]._frame)
+		aainfo[3]._frame = kernel_anim[aa[3]].frame;
+	int16 frame = aainfo[3]._frame;
+
+	if (global[player_hyperwalked] == -1 && frame <= 23) {
+		aainfo[3]._frame = 23;
+		kernel_reset_animation(aa[3], 23);
+		kernel_synch(3, aa[3], 4, 0);
+		return;
+	}
+
+	if (global[player_hyperwalked] == -1 || frame == 45) {
+		aainfo[3]._frame = 44;
+		kernel_reset_animation(aa[3], 44);
+		kernel_synch(3, aa[3], 4, 0);
+	}
+}
+
+static void room_205_anim4() {
+	if (kernel_anim[aa[2]].frame != aainfo[2]._frame)
+		aainfo[2]._frame = kernel_anim[aa[2]].frame;
+	int16 frame = aainfo[2]._frame;
+
+	if (global[player_hyperwalked] == -1 && frame <= 23) {
+		aainfo[2]._frame = 23;
+		kernel_reset_animation(aa[2], 23);
+		kernel_synch(3, aa[2], 4, 0);
+		return;
+	}
+
+	if (global[player_hyperwalked] == -1 || frame == 45) {
+		aainfo[2]._frame = 44;
+		kernel_reset_animation(aa[2], 44);
+		kernel_synch(3, aa[2], 4, 0);
+	}
+}
+
+static void room_205_anim5() {
+	if (kernel_anim[aa[6]].frame == aainfo[6]._frame)
+		return;
+	aainfo[6]._frame = kernel_anim[aa[6]].frame;
+
+	if (aainfo[6]._frame == 135) {
+		global[g009] = 0;
+		global_digi_play(9);
+		kernel_timing_trigger(180, 105);
+	} else if (aainfo[6]._frame == 138) {
+		aainfo[6]._frame = 137;
+		kernel_reset_animation(aa[6], 137);
+	}
 }
 
 static void room_205_daemon() {
+	switch (kernel.trigger) {
+	case 7:
+		switch (local->_a4) {
+		case 120:
+			if (global[walker_converse_state]) {
+				global[walker_converse_state] = 0;
+				close_journal(3);
+				player.commands_allowed = true;
+			}
+			break;
+
+		case 121:
+			player.commands_allowed = true;
+			if (player.x == 208 && player.y == 102) {
+				kernel_timing_trigger(imath_random(5, 120), 28);
+				local->_a4 = 122;
+			} else {
+				local->_a4 = 0;
+			}
+			break;
+
+		default:
+			break;
+		}
+		break;
+
+	case 24:
+		if (local->_8c == 3) {
+			aa[2] = kernel_run_animation(kernel_name('E', 1), 102);
+			aainfo[2]._active = -1;
+			local->_96 = 2;
+			kernel_reset_animation(local->_9a, 0);
+			kernel_synch(KERNEL_ANIM, aa[2], KERNEL_ANIM, local->_9a);
+			global[g133] = 1;
+		}
+		break;
+
+	case 25:
+		if (local->_8e == 2) {
+			aa[3] = kernel_run_animation(kernel_name('R', 1), 101);
+			aainfo[3]._active = -1;
+			local->_94 = 3;
+			kernel_reset_animation(local->_9c, 0);
+			kernel_synch(KERNEL_ANIM, aa[3], KERNEL_ANIM, local->_9c);
+			global[g143] = 1;
+		}
+		break;
+
+	case 26:
+		kernel_reset_animation(local->_9a, 0);
+		kernel_reset_animation(local->_9c, 0);
+		global[g133] = 1;
+		global[g143] = 1;
+		break;
+
+	case 28:
+		if (local->_a4 == 122) {
+			if (player.x == 208 && player.y == 102) {
+				digi_play_build(221, '_', 1, 1);
+				local->_a4 = 121;
+			} else {
+				local->_a4 = 0;
+			}
+		}
+		break;
+
+	case 100:
+		kernel_abort_animation(aa[0]);
+		aainfo[0]._active = 0;
+
+		if (flags[10] == 1) {
+			kernel_synch(2, 0, 4, 0);
+			scratch._a4 = 1;
+			global[g154] = 2;
+			aainfo[1]._active = -1;
+			global[g141] = -1;
+			global[g017] = 0;
+			player.walker_visible = 0;
+			global[g143] = 0;
+			kernel_reset_animation(scratch._9c, 1);
+			kernel_synch(3, scratch._9c, 4, 0);
+			kernel_synch(3, scratch._9a, 4, 0);
+			global[g131] = -1;
+			global[g133] = 1;
+			global[g156] = 0;
+			scratch._a2 = kernel_run_animation_talk('e', 9, 0);
+			kernel_position_anim(scratch._a2, 120, 138, 74, 4);
+			scratch._9e = kernel_run_animation_talk('b', 2, 0);
+			kernel_position_anim(scratch._9e, player.x, player.y, player.scale, player.depth);
+			kernel_synch(KERNEL_ANIM, scratch._a2, KERNEL_ANIM, scratch._9a);
+			kernel_synch(KERNEL_ANIM, scratch._9e, KERNEL_PLAYER, 0);
+			digi_play_build(205, 'b', 1, 1);
+		} else {
+			global[g131] = -1;
+			global[g141] = -1;
+			kernel_reset_animation(scratch._9a, 1);
+			kernel_synch(KERNEL_ANIM, scratch._9a, KERNEL_PLAYER, 0);
+			kernel_reset_animation(scratch._9c, 1);
+			kernel_synch(KERNEL_ANIM, scratch._9c, KERNEL_PLAYER, 0);
+			player.walker_visible = -1;
+			global[g133] = 0;
+			global[g143] = 0;
+			kernel_synch(2, 0, 4, 0);
+			player.commands_allowed = true;
+		}
+		break;
+
+	case 101:
+		kernel_abort_animation(aa[local->_94]);
+		aainfo[local->_94]._active = false;
+		kernel_reset_animation(local->_9c, 1);
+		kernel_synch(KERNEL_ANIM, local->_9c, KERNEL_PLAYER, 0);
+		global[g143] = 0;
+		player.commands_allowed = true;
+		break;
+
+	case 102:
+		kernel_abort_animation(aa[local->_96]);
+		aainfo[local->_96]._active = false;
+		kernel_reset_animation(local->_9a, 1);
+		kernel_synch(KERNEL_ANIM, local->_9a, KERNEL_PLAYER, 0);
+		global[g133] = 0;
+		player.commands_allowed = true;
+		break;
+
+	case 104:
+		aainfo[6]._active = 0;
+		kernel_abort_animation(aa[6]);
+		new_room = 101;
+		break;
+
+	case 105:
+		aainfo[6]._frame = 139;
+		kernel_reset_animation(aa[6], 139);
+		break;
+
+	default:
+		break;
+	}
+
+	global_anim1(9, local->_9a, global[g131], &global[g132]);
+	global_anim2(9, local->_9c, global[g141], &global[g142]);
+
+	if (aainfo[0]._active)
+		room_205_anim1();
+	if (aainfo[1]._active)
+		room_205_anim2();
+	if (aainfo[3]._active)
+		room_205_anim3();
+	if (aainfo[2]._active)
+		room_205_anim4();
+	if (aainfo[6]._active)
+		room_205_anim5();
 }
 
 static void room_205_pre_parser() {
+	if (player_parse(13, 24, 0))
+		player.walk_off_edge_to_room = 204;
+
+	if (player_parse(13, 34, 0))
+		player.walk_off_edge_to_room = 301;
 }
 
 static void room_205_parser() {
+	if (global[walker_converse_state] != 0) {
+		player.commands_allowed = 0;
+		digi_play_build_ii('c', 1, 1);
+		scratch._a4 = 120;
+		player.command_ready = false;
+		return;
+	}
+
+	if (player_parse(78, 107, 0)) {
+		player.commands_allowed = 0;
+		scratch._a4 = 121;
+		digi_play_build(221, '_', 1, 1);
+		player.command_ready = false;
+		return;
+	}
+
+	if (global[player_selected_object] >= 0) {
+		player.command_ready = false;
+		return;
+	}
+
+	if (player_parse(114, 116, 0)) {
+		player.commands_allowed = 0;
+		global[g145] = -1;
+		scratch._8e = 2;
+		player.command_ready = false;
+		return;
+	}
+
+	if (player_parse(126, 99, 0) || player_parse(78, 119, 0)) {
+		player.commands_allowed = 0;
+		global[g135] = -1;
+		scratch._8c = 3;
+		player.command_ready = false;
+		return;
+	}
 }
 
 void room_205_synchronize(Common::Serializer &s) {
 	for (int16 &v : scratch.sprite)    s.syncAsSint16LE(v);
 	for (int16 &v : scratch.sequence)  s.syncAsSint16LE(v);
 	for (int16 &v : scratch.animation) s.syncAsSint16LE(v);
+	for (AnimationInfo &ai : scratch.animation_info) ai.synchronize(s);
+	s.syncAsSint16LE(scratch._8c);
+	s.syncAsSint16LE(scratch._8e);
+	s.syncAsSint16LE(scratch._90);
+	s.syncAsSint16LE(scratch._92);
+	s.syncAsSint16LE(scratch._94);
+	s.syncAsSint16LE(scratch._96);
+	s.syncAsSint16LE(scratch._98);
+	s.syncAsSint16LE(scratch._9a);
+	s.syncAsSint16LE(scratch._9c);
+	s.syncAsSint16LE(scratch._9e);
+	s.syncAsSint16LE(scratch._a0);
+	s.syncAsSint16LE(scratch._a2);
+	s.syncAsSint16LE(scratch._a4);
 }
 
 void room_205_preload() {



From noreply at scummvm.org  Sun Jun  7 06:38:08 2026
From: noreply at scummvm.org (lotharsm)
Date: Sun,  7 Jun 2026 06:38:08 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 474c043d22f4b06802c260a15fb50d2aad33697e
Message-ID: <20260607063808.131907005BC@ron.scummvm.net>

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

Summary:
474c043d22 GIT: Ignore .orig and .rej files caused by failed manual patching


Commit: 474c043d22f4b06802c260a15fb50d2aad33697e
    https://github.com/scummvm/scummvm/commit/474c043d22f4b06802c260a15fb50d2aad33697e
Author: Lothar Serra Mari (mail at serra.me)
Date: 2026-06-07T08:37:21+02:00

Commit Message:
GIT: Ignore .orig and .rej files caused by failed manual patching

Changed paths:
    .gitignore


diff --git a/.gitignore b/.gitignore
index 001b99b495d..fa078a6c191 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,6 +9,8 @@
 lib*.a
 .deps
 *_pch.cpp
+*.*.orig
+*.*.rej
 
 /config.log
 /scummvm



From noreply at scummvm.org  Sun Jun  7 10:00:02 2026
From: noreply at scummvm.org (lotharsm)
Date: Sun,  7 Jun 2026 10:00:02 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 0e45da59c013beb6b1d41c6eb9481e3bcec118dc
Message-ID: <20260607100002.6B5257005BC@ron.scummvm.net>

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

Summary:
0e45da59c0 GIT: Fix globs for ignoring .orig and .rej files caused by failed manual patching


Commit: 0e45da59c013beb6b1d41c6eb9481e3bcec118dc
    https://github.com/scummvm/scummvm/commit/0e45da59c013beb6b1d41c6eb9481e3bcec118dc
Author: Lothar Serra Mari (mail at serra.me)
Date: 2026-06-07T11:59:40+02:00

Commit Message:
GIT: Fix globs for ignoring .orig and .rej files caused by failed manual patching

Changed paths:
    .gitignore


diff --git a/.gitignore b/.gitignore
index fa078a6c191..faa35f7a791 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,8 +9,8 @@
 lib*.a
 .deps
 *_pch.cpp
-*.*.orig
-*.*.rej
+*.orig
+*.rej
 
 /config.log
 /scummvm



From noreply at scummvm.org  Sun Jun  7 10:05:09 2026
From: noreply at scummvm.org (sev-)
Date: Sun,  7 Jun 2026 10:05:09 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 ef0550f4413e042be8074ac49972b0d84044dbc7
Message-ID: <20260607100509.3AE8C7005BC@ron.scummvm.net>

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:
5ced268bd9 DIRECTOR: LINGO: Silently ignore setTheCast on empty cast slots within bounds
be0caaa7cd DIRECTOR: LINGO: Move cast bounds check to Movie::getMaxCastID
ef0550f441 DIRECTOR: LINGO: Fire mouseDown and mouseUp handlers on press for immediate sprites


Commit: 5ced268bd9681b5de93dd6b47d8b6cb1c6f29eac
    https://github.com/scummvm/scummvm/commit/5ced268bd9681b5de93dd6b47d8b6cb1c6f29eac
Author: ramyak-sharma (ramyaksharma1 at gmail.com)
Date: 2026-06-07T12:05:03+02:00

Commit Message:
DIRECTOR: LINGO: Silently ignore setTheCast on empty cast slots within bounds

In the original Director, setting a property on a non-existent cast member whose ID
is within the range [1, maxCastID] fails silently. Only IDs above the highest allocated
slot produce an error. Match that behavior by checking both the movie cast and shared
cast bounds before raising a lingoError.

Changed paths:
    engines/director/lingo/lingo-the.cpp


diff --git a/engines/director/lingo/lingo-the.cpp b/engines/director/lingo/lingo-the.cpp
index 211dfe71ada..4dbda933a7a 100644
--- a/engines/director/lingo/lingo-the.cpp
+++ b/engines/director/lingo/lingo-the.cpp
@@ -2192,9 +2192,26 @@ void Lingo::setTheCast(Datum &id1, int field, Datum &d) {
 	}
 
 	CastMemberID id = id1.asMemberID();
+	Cast *sharedCast = movie->getSharedCast();
+	Cast *cast = movie->getCast(id);
+	int maxID = 0;
+
+	if (sharedCast) {
+		maxID = MAX(maxID, sharedCast->getCastMaxID());
+	}
+	if (cast) {
+		maxID = MAX(maxID, cast->getCastMaxID());
+	}
 
 	CastMember *member = movie->getCastMember(id);
+
 	if (!member) {
+		if (id.member >= 1 && id.member <= maxID) {
+			debugC(1, kDebugLingoExec,
+				  "Lingo::setTheCast(): %s not found, ignoring (empty slot within cast bounds)",
+				  id.asString().c_str());
+			return;
+		}
 		g_lingo->lingoError("Lingo::setTheCast(): %s not found", id.asString().c_str());
 		return;
 	}
@@ -2205,7 +2222,6 @@ void Lingo::setTheCast(Datum &id1, int field, Datum &d) {
 			return;
 		}
 		CastMember *replacement = (CastMember *)d.u.obj;
-		Cast *cast = movie->getCast(id);
 		cast->duplicateCastMember(replacement, nullptr, id.member);
 		return;
 	}


Commit: be0caaa7cd28ec49b31a409f0addcff4b3da6bfc
    https://github.com/scummvm/scummvm/commit/be0caaa7cd28ec49b31a409f0addcff4b3da6bfc
Author: ramyak-sharma (ramyaksharma1 at gmail.com)
Date: 2026-06-07T12:05:03+02:00

Commit Message:
DIRECTOR: LINGO: Move cast bounds check to Movie::getMaxCastID

Encapsulate shared cast presence and querying in Movie::getMaxCastID()
instead of handling it directly in Lingo::setTheCast.

Changed paths:
    engines/director/lingo/lingo-the.cpp
    engines/director/movie.cpp
    engines/director/movie.h


diff --git a/engines/director/lingo/lingo-the.cpp b/engines/director/lingo/lingo-the.cpp
index 4dbda933a7a..938700cde23 100644
--- a/engines/director/lingo/lingo-the.cpp
+++ b/engines/director/lingo/lingo-the.cpp
@@ -2192,21 +2192,10 @@ void Lingo::setTheCast(Datum &id1, int field, Datum &d) {
 	}
 
 	CastMemberID id = id1.asMemberID();
-	Cast *sharedCast = movie->getSharedCast();
-	Cast *cast = movie->getCast(id);
-	int maxID = 0;
-
-	if (sharedCast) {
-		maxID = MAX(maxID, sharedCast->getCastMaxID());
-	}
-	if (cast) {
-		maxID = MAX(maxID, cast->getCastMaxID());
-	}
-
 	CastMember *member = movie->getCastMember(id);
 
 	if (!member) {
-		if (id.member >= 1 && id.member <= maxID) {
+		if (id.member >= 1 && id.member <= movie->getMaxCastID()) {
 			debugC(1, kDebugLingoExec,
 				  "Lingo::setTheCast(): %s not found, ignoring (empty slot within cast bounds)",
 				  id.asString().c_str());
@@ -2222,6 +2211,7 @@ void Lingo::setTheCast(Datum &id1, int field, Datum &d) {
 			return;
 		}
 		CastMember *replacement = (CastMember *)d.u.obj;
+		Cast *cast = movie->getCast(id);
 		cast->duplicateCastMember(replacement, nullptr, id.member);
 		return;
 	}
diff --git a/engines/director/movie.cpp b/engines/director/movie.cpp
index 218e12f5906..d01233acc7d 100644
--- a/engines/director/movie.cpp
+++ b/engines/director/movie.cpp
@@ -708,6 +708,17 @@ const Stxt *Movie::getStxt(CastMemberID memberID) {
 	return result;
 }
 
+int Movie::getMaxCastID() {
+	int max = 0;
+	for (auto &it : _casts) {
+		max = MAX(max, it._value->getCastMaxID());
+	}
+	if (_sharedCast) {
+		max = MAX(max, _sharedCast->getCastMaxID());
+	}
+	return max;
+}
+
 LingoArchive *Movie::getMainLingoArch() {
 	return _casts.getVal(DEFAULT_CAST_LIB)->_lingoArchive;
 }
diff --git a/engines/director/movie.h b/engines/director/movie.h
index 773c60262f6..b60b5e2dee5 100644
--- a/engines/director/movie.h
+++ b/engines/director/movie.h
@@ -127,6 +127,7 @@ public:
 	CastMemberInfo *getCastMemberInfo(CastMemberID memberID);
 	bool isValidCastMember(CastMemberID memberID, CastType type);
 	const Stxt *getStxt(CastMemberID memberID);
+	int getMaxCastID();
 
 
 	LingoArchive *getMainLingoArch();


Commit: ef0550f4413e042be8074ac49972b0d84044dbc7
    https://github.com/scummvm/scummvm/commit/ef0550f4413e042be8074ac49972b0d84044dbc7
Author: ramyak-sharma (ramyaksharma1 at gmail.com)
Date: 2026-06-07T12:05:03+02:00

Commit Message:
DIRECTOR: LINGO: Fire mouseDown and mouseUp handlers on press for immediate sprites

In Director 3+, when a sprite has the immediate flag set, both mouseDown and mouseUp handlers
should fire on mouse press rather than waiting for release. Previously ScummVM fired them on
release, causing the stillDown loop inside drag handlers to exit immediately. Fix by queuing
both mouseDown and mouseUp events (sprite and cast handler levels) when a mouseDown event is
received for an immediate sprite in D3+

Changed paths:
    engines/director/lingo/lingo-events.cpp


diff --git a/engines/director/lingo/lingo-events.cpp b/engines/director/lingo/lingo-events.cpp
index 544ed07ac2e..cd47531690c 100644
--- a/engines/director/lingo/lingo-events.cpp
+++ b/engines/director/lingo/lingo-events.cpp
@@ -500,7 +500,27 @@ void Movie::queueEvent(Common::Queue<LingoEvent> &queue, LEvent event, int targe
 		// In D2-3, specific objects handle each event, with no passing
 		switch(event) {
 		case kEventMouseUp:
+			if (_vm->getVersion() >= 300) {
+				uint16 spriteId = _score->getMouseSpriteIDFromPos(pos);
+				Sprite *sprite = _score->getSpriteById(spriteId);
+				if (sprite && sprite->_immediate)
+					break;
+			}
+			// fall through
 		case kEventMouseDown:
+			if (_vm->getVersion() >= 300) {
+				uint16 spriteId = _score->getMouseSpriteIDFromPos(pos);
+				Sprite *sprite = _score->getSpriteById(spriteId);
+				if (sprite && sprite->_immediate) {
+					queue.push(LingoEvent(kEventMouseDown, eventId, kSpriteHandler, false, pos));
+					queue.push(LingoEvent(kEventMouseDown, eventId, kCastHandler, false, pos));
+					_nextEventId++;
+					int mouseUpEventId = _nextEventId;
+					queue.push(LingoEvent(kEventMouseUp, mouseUpEventId, kSpriteHandler, false, pos));
+					queue.push(LingoEvent(kEventMouseUp, mouseUpEventId, kCastHandler, false, pos));
+					break;
+				}
+			}
 			queue.push(LingoEvent(event, eventId, kSpriteHandler, false, pos));
 			queue.push(LingoEvent(event, eventId, kCastHandler, false, pos));
 			break;
@@ -534,11 +554,28 @@ void Movie::queueEvent(Common::Queue<LingoEvent> &queue, LEvent event, int targe
 		 * Once one of these objects handles the event, any event handlers queued
 		 * for the same event will be ignored unless the pass command was called.
 		 */
+
+		uint16 spriteId = _score->getMouseSpriteIDFromPos(pos);
+		Sprite *sprite = _score->getSpriteById(spriteId);
+
 		switch (event) {
 		case kEventKeyUp:
 		case kEventKeyDown:
 		case kEventMouseUp:
+			if (sprite && sprite->_immediate) {
+				break;
+			}
+			// fall through
 		case kEventMouseDown:
+			if (sprite && sprite->_immediate) {
+				queue.push(LingoEvent(kEventMouseDown, eventId, kSpriteHandler, false, pos, spriteId));
+				queue.push(LingoEvent(kEventMouseDown, eventId, kCastHandler, false, pos, spriteId));
+				_nextEventId++;
+				int mouseUpEventId = _nextEventId;
+				queue.push(LingoEvent(kEventMouseUp, mouseUpEventId, kSpriteHandler, false, pos, spriteId));
+				queue.push(LingoEvent(kEventMouseUp, mouseUpEventId, kCastHandler, false, pos, spriteId));
+				break;
+			}
 		case kEventRightMouseUp:
 		case kEventRightMouseDown:
 		case kEventBeginSprite:



From noreply at scummvm.org  Sun Jun  7 10:08:09 2026
From: noreply at scummvm.org (AndywinXp)
Date: Sun,  7 Jun 2026 10:08:09 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 a8f12a9fc41061b4cea88fed01a3d782c5080d7d
Message-ID: <20260607100809.8F7CC7005BC@ron.scummvm.net>

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

Summary:
a8f12a9fc4 FREESCAPE: Fix freescape-less builds on platforms using create_project


Commit: a8f12a9fc41061b4cea88fed01a3d782c5080d7d
    https://github.com/scummvm/scummvm/commit/a8f12a9fc41061b4cea88fed01a3d782c5080d7d
Author: AndywinXp (andywinxp at gmail.com)
Date: 2026-06-07T12:08:01+02:00

Commit Message:
FREESCAPE: Fix freescape-less builds on platforms using create_project

Changed paths:
    engines/freescape/module.mk


diff --git a/engines/freescape/module.mk b/engines/freescape/module.mk
index 57fcd93b830..cb5881321fd 100644
--- a/engines/freescape/module.mk
+++ b/engines/freescape/module.mk
@@ -106,5 +106,11 @@ endif
 include $(srcdir)/rules.mk
 
 # Detection objects
-DETECT_OBJS += $(MODULE)/detection.o \
-	$(MODULE)/zx_tape.o
+DETECT_OBJS += $(MODULE)/detection.o
+
+# Skip building the following objects if a static
+# module is enabled, because it already has the contents.
+ifneq ($(ENABLE_FREESCAPE), STATIC_PLUGIN)
+# External dependencies for detection.
+DETECT_OBJS += $(MODULE)/zx_tape.o
+endif



From noreply at scummvm.org  Sun Jun  7 12:24:23 2026
From: noreply at scummvm.org (athrxx)
Date: Sun,  7 Jun 2026 12:24:23 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 6ea218c2bcc105062ab6d65df3485df67c4c0424
Message-ID: <20260607122423.E01D87005BC@ron.scummvm.net>

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:
b81bf949c8 SCUMM: (EGA dithering mode) - fix screen shaking
6ea218c2bc SCUMM: (EGA dithering mode) - fix bug in setCurrentPalette


Commit: b81bf949c8a1353fdba14b17dd48b1979f4f86ab
    https://github.com/scummvm/scummvm/commit/b81bf949c8a1353fdba14b17dd48b1979f4f86ab
Author: athrxx (athrxx at scummvm.org)
Date: 2026-06-07T14:23:40+02:00

Commit Message:
SCUMM: (EGA dithering mode) - fix screen shaking

Changed paths:
    engines/scumm/gfx.cpp


diff --git a/engines/scumm/gfx.cpp b/engines/scumm/gfx.cpp
index b51caa6f975..aa4eace00cc 100644
--- a/engines/scumm/gfx.cpp
+++ b/engines/scumm/gfx.cpp
@@ -5004,9 +5004,11 @@ void ScummEngine::updateScreenShakeEffect() {
 	if (!_shakeNextTick)
 		_shakeNextTick = now;
 
+	int multiplier = (_enableEGADithering ? 2 : 1) * _textSurfaceMultiplier;
+
 	while (now >= _shakeNextTick) {
 		_shakeFrame = (_shakeFrame + 1) % NUM_SHAKE_POSITIONS;
-		_system->setShakePos(0, -shake_positions[_shakeFrame] * _textSurfaceMultiplier);
+		_system->setShakePos(0, -shake_positions[_shakeFrame] * multiplier);
 		// In DOTT (and probably all other imuse games) this runs on the imuse timer which is a PIT 0 Timer at 291.304 Hz.
 		// Apparently it is the same timer setting for all sound drivers although it is set up not in the main executable
 		// but inside each respective ims driver during the driver load/init process. The screen shakes update every 8 ticks.


Commit: 6ea218c2bcc105062ab6d65df3485df67c4c0424
    https://github.com/scummvm/scummvm/commit/6ea218c2bcc105062ab6d65df3485df67c4c0424
Author: athrxx (athrxx at scummvm.org)
Date: 2026-06-07T14:23:45+02:00

Commit Message:
SCUMM: (EGA dithering mode) - fix bug in setCurrentPalette

(needs to trigger a full screen update in EGA mode)

Changed paths:
    engines/scumm/palette.cpp


diff --git a/engines/scumm/palette.cpp b/engines/scumm/palette.cpp
index 50ca8fdb0c5..4c5da8ce304 100644
--- a/engines/scumm/palette.cpp
+++ b/engines/scumm/palette.cpp
@@ -1501,6 +1501,8 @@ void ScummEngine::setCurrentPalette(int palindex) {
 				_egaColorMap[1][i] = *pals++ >> 4;
 			}
 		}
+		_virtscr[kMainVirtScreen].setDirtyRange(0, _virtscr[kMainVirtScreen].h);
+		_virtscr[kVerbVirtScreen].setDirtyRange(0, _virtscr[kVerbVirtScreen].h);
 	} else {
 		setPaletteFromPtr(pals);
 	}



From noreply at scummvm.org  Sun Jun  7 13:19:23 2026
From: noreply at scummvm.org (Helco)
Date: Sun,  7 Jun 2026 13:19:23 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 b1b61e85e0d634a6103a09a50c9e951dfed263a0
Message-ID: <20260607131923.F042D7005BC@ron.scummvm.net>

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:
4d110c28b8 ALCACHOFA: Fix assertion when cancelling video playback
45348a0626 ALCACHOFA: Fix instant fades not staying faded
b1b61e85e0 ALCACHOFA: V2:


Commit: 4d110c28b817b4c806745e855abdc189a9597aa7
    https://github.com/scummvm/scummvm/commit/4d110c28b817b4c806745e855abdc189a9597aa7
Author: Helco (hermann.noll at hotmail.com)
Date: 2026-06-07T15:16:59+02:00

Commit Message:
ALCACHOFA: Fix assertion when cancelling video playback

Changed paths:
    engines/alcachofa/alcachofa.cpp
    engines/alcachofa/alcachofa.h
    engines/alcachofa/player.cpp


diff --git a/engines/alcachofa/alcachofa.cpp b/engines/alcachofa/alcachofa.cpp
index 0fb648f95c7..081435ab463 100644
--- a/engines/alcachofa/alcachofa.cpp
+++ b/engines/alcachofa/alcachofa.cpp
@@ -226,6 +226,9 @@ void AlcachofaEngine::playVideo(int32 videoId) {
 		g_system->delayMillis(decoder->getTimeToNextFrame());
 	}
 	decoder->stop();
+
+	_input.nextFrame(); // otherwise the menu might open after just cancelling a video
+	_renderer->begin(); // we were within a frame, this resets the state
 }
 
 void AlcachofaEngine::fadeExit() {
@@ -317,7 +320,7 @@ void AlcachofaEngine::pauseEngineIntern(bool pause) {
 bool AlcachofaEngine::canLoadGameStateCurrently(U32String *msg) {
 	if (_menu == nullptr)
 		return false; // the autosave wants to trigger even during error() while starting the game
-	if (!_eventLoopSemaphore.isReleased())
+	if (!isInSpecialGameLoop())
 		return false;
 	return
 		(menu().isOpen() && menu().interactionSemaphore().isReleased()) ||
diff --git a/engines/alcachofa/alcachofa.h b/engines/alcachofa/alcachofa.h
index 96008e8fc69..53d5c6f723b 100644
--- a/engines/alcachofa/alcachofa.h
+++ b/engines/alcachofa/alcachofa.h
@@ -162,6 +162,7 @@ public:
 			(f == kSupportsReturnToLauncher);
 	};
 
+	bool isInSpecialGameLoop() const { return !_eventLoopSemaphore.isReleased(); }
 	bool canLoadGameStateCurrently(Common::U32String *msg = nullptr) override;
 	bool canSaveGameStateCurrently(Common::U32String *msg = nullptr) override {
 		return canLoadGameStateCurrently(msg);
diff --git a/engines/alcachofa/player.cpp b/engines/alcachofa/player.cpp
index a1dd96fe410..441e8933b28 100644
--- a/engines/alcachofa/player.cpp
+++ b/engines/alcachofa/player.cpp
@@ -354,9 +354,11 @@ void Player::setActiveCharacter(MainCharacterKind kind) {
 }
 
 bool Player::isAllowedToOpenMenu() {
-	return !g_engine->menu().isOpen() &&
-		   g_engine->game().isAllowedToOpenMenu() &&
-		   !_isInTemporaryRoom; // we cannot reliably store this state across multiple room changes
+	return
+		!g_engine->menu().isOpen() &&
+		!g_engine->isInSpecialGameLoop() &&
+		!_isInTemporaryRoom && // we cannot reliably store this state across multiple room changes
+		g_engine->game().isAllowedToOpenMenu();
 }
 
 Room *Player::lastGameRoom() const {


Commit: 45348a0626eb2769df8856b0de8cc0d4657f65ec
    https://github.com/scummvm/scummvm/commit/45348a0626eb2769df8856b0de8cc0d4657f65ec
Author: Helco (hermann.noll at hotmail.com)
Date: 2026-06-07T15:16:59+02:00

Commit Message:
ALCACHOFA: Fix instant fades not staying faded

Changed paths:
    engines/alcachofa/graphics.cpp


diff --git a/engines/alcachofa/graphics.cpp b/engines/alcachofa/graphics.cpp
index 913de0b1d40..b1e1cd48ad4 100644
--- a/engines/alcachofa/graphics.cpp
+++ b/engines/alcachofa/graphics.cpp
@@ -1196,10 +1196,12 @@ Task *fade(Process &process, FadeType fadeType,
 	int32 duration, EasingType easingType,
 	int8 order,
 	PermanentFadeAction permanentFadeAction) {
-	if (duration <= 0)
-		return new DelayTask(process, 0);
 	if (!process.isActiveForPlayer())
 		return new DelayTask(process, (uint32)duration);
+	if (duration <= 0) {
+		g_engine->globalUI().isPermanentFaded() = permanentFadeAction == PermanentFadeAction::SetFaded;
+		return new DelayTask(process, 0);
+	}
 	return new FadeTask(process, fadeType, from, to, duration, easingType, order, permanentFadeAction);
 }
 


Commit: b1b61e85e0d634a6103a09a50c9e951dfed263a0
    https://github.com/scummvm/scummvm/commit/b1b61e85e0d634a6103a09a50c9e951dfed263a0
Author: Helco (hermann.noll at hotmail.com)
Date: 2026-06-07T15:17:00+02:00

Commit Message:
ALCACHOFA: V2:

Changed paths:
    engines/alcachofa/game-v2.cpp
    engines/alcachofa/game.cpp
    engines/alcachofa/game.h
    engines/alcachofa/sounds.cpp


diff --git a/engines/alcachofa/game-v2.cpp b/engines/alcachofa/game-v2.cpp
index 0eb55d68fcf..0ad459dd50c 100644
--- a/engines/alcachofa/game-v2.cpp
+++ b/engines/alcachofa/game-v2.cpp
@@ -232,6 +232,12 @@ public:
 			return Game::unknownCamLerpTarget(action, name);
 		return nullptr;
 	}
+
+	bool shouldMusicLoop() override {
+		// The credits cutscene is slightly longer than the
+		// so we just disable looping and keep the last couple seconds quiet
+		return !g_engine->player().currentRoom()->name().equalsIgnoreCase("CREDITOS");
+	}
 };
 
 static constexpr const char *kMapFilesSecta[] = {
@@ -280,7 +286,7 @@ public:
 	String getMusicPath(int32 trackId) override {
 		const Room *room = g_engine->player().lastGameRoom();
 		const char *dirName = room != nullptr && room->mapIndex() == 1 ? "Music_Cleopatra" : "Music";
-		return String::format("%s/Track%02d", dirName, trackId);
+		return String::format("%s/Track%02d", dirName, trackId); // 1-based indexing
 	}
 
 private:
@@ -298,7 +304,7 @@ public:
 	}
 
 	String getMusicPath(int32 trackId) override {
-		return String::format("track%d", trackId);
+		return String::format("track%d", trackId - 1); // 0-based indexing
 	}
 
 	bool isKnownBadVideo(int32 videoId) override {
@@ -321,7 +327,7 @@ public:
 	}
 
 	String getMusicPath(int32 trackId) override {
-		return String::format("track%d", trackId);
+		return String::format("track%d", trackId - 1); // 0-based indexing
 	}
 
 	bool isKnownBadVideo(int32 videoId) override {
diff --git a/engines/alcachofa/game.cpp b/engines/alcachofa/game.cpp
index db0a7d79965..042b8b868b2 100644
--- a/engines/alcachofa/game.cpp
+++ b/engines/alcachofa/game.cpp
@@ -84,6 +84,10 @@ void Game::tooManyDrawRequests(int order) {
 	_message("Too many draw requests in order %d", order);
 }
 
+bool Game::shouldMusicLoop() {
+	return true;
+}
+
 bool Game::shouldCharacterTrigger(const Character *character, const char *action) {
 	return true;
 }
diff --git a/engines/alcachofa/game.h b/engines/alcachofa/game.h
index 41489eb72a3..fee703beb25 100644
--- a/engines/alcachofa/game.h
+++ b/engines/alcachofa/game.h
@@ -72,6 +72,7 @@ public:
 	virtual Common::String getSoundPath(const char *filename) = 0; ///< Without file-extension
 	virtual Common::String getMusicPath(int32 trackId) = 0; ///< Without file-extension
 	virtual int32 getCharacterJingle(MainCharacterKind kind) = 0;
+	virtual bool shouldMusicLoop();
 	virtual bool shouldFilterTexturesByDefault() = 0;
 	virtual bool shouldClipCamera() = 0;
 	virtual bool isAllowedToInteract() = 0;
diff --git a/engines/alcachofa/sounds.cpp b/engines/alcachofa/sounds.cpp
index d6789ab1289..7275759bc3d 100644
--- a/engines/alcachofa/sounds.cpp
+++ b/engines/alcachofa/sounds.cpp
@@ -368,7 +368,7 @@ void Sounds::startMusic(int musicId) {
 	auto path = g_engine->game().getMusicPath(musicId);
 	_musicSoundID = playSoundInternal(path, Mixer::kMaxChannelVolume, Mixer::kMusicSoundType);
 	_isMusicPlaying = true;
-	_nextMusicID = musicId;
+	_nextMusicID = g_engine->game().shouldMusicLoop() ? musicId : -1;
 }
 
 void Sounds::queueMusic(int musicId) {



From noreply at scummvm.org  Sun Jun  7 13:22:54 2026
From: noreply at scummvm.org (ScummVM-Translations)
Date: Sun,  7 Jun 2026 13:22:54 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm-web master ->
 5e179583682a2a4b24b0662d10ac043a0872566f
Message-ID: <20260607132254.D72277005BC@ron.scummvm.net>

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

Summary:
5e17958368 L10N: Update translation (French) (Strings)


Commit: 5e179583682a2a4b24b0662d10ac043a0872566f
    https://github.com/scummvm/scummvm-web/commit/5e179583682a2a4b24b0662d10ac043a0872566f
Author: Purple T (ZEONK at hotmail.com)
Date: 2026-06-07T13:22:49Z

Commit Message:
L10N: Update translation (French) (Strings)

Currently translated at 100.0% (173 of 173 strings)

Changed paths:
    data/fr/strings.json


diff --git a/data/fr/strings.json b/data/fr/strings.json
index c710cafd..8acf6a8c 100644
--- a/data/fr/strings.json
+++ b/data/fr/strings.json
@@ -177,5 +177,6 @@
     "compatibilityDetailsDetails": "D??tails",
     "downloadsBadgeTitle": "T??l??charger ScummVM {version}",
     "menuMainFreewareGames": "Jeux gratuits & Suppl??ments",
-    "compatibilityDetailsChartColScummVmId": "ID ScummVM"
+    "compatibilityDetailsChartColScummVmId": "ID ScummVM",
+    "menuMainDumper": "Dumper Companion"
 }



From noreply at scummvm.org  Sun Jun  7 13:22:55 2026
From: noreply at scummvm.org (ScummVM-Translations)
Date: Sun,  7 Jun 2026 13:22:55 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 f56c40ce259f8d28582a09b13517ca05fbc68692
Message-ID: <20260607132255.4F85B7005BC@ron.scummvm.net>

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

Summary:
f56c40ce25 I18N: Update translation (French)


Commit: f56c40ce259f8d28582a09b13517ca05fbc68692
    https://github.com/scummvm/scummvm/commit/f56c40ce259f8d28582a09b13517ca05fbc68692
Author: Purple T (ZEONK at hotmail.com)
Date: 2026-06-07T13:22:49Z

Commit Message:
I18N: Update translation (French)

Currently translated at 100.0% (3204 of 3204 strings)

Changed paths:
    po/fr_FR.po


diff --git a/po/fr_FR.po b/po/fr_FR.po
index 09f4daeae01..e40a60aab58 100644
--- a/po/fr_FR.po
+++ b/po/fr_FR.po
@@ -8,7 +8,7 @@ msgstr ""
 "Project-Id-Version: ScummVM 1.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
 "POT-Creation-Date: 2026-06-06 11:26+0000\n"
-"PO-Revision-Date: 2026-06-02 11:22+0000\n"
+"PO-Revision-Date: 2026-06-07 13:22+0000\n"
 "Last-Translator: Purple T <ZEONK at hotmail.com>\n"
 "Language-Team: French <https://translations.scummvm.org/projects/scummvm/"
 "scummvm/fr/>\n"
@@ -13782,10 +13782,8 @@ msgid "Fire / select"
 msgstr "Tirer / S??lectionner"
 
 #: engines/scumm/metaengine.cpp:1186
-#, fuzzy
-#| msgid "Skip / menu back"
 msgid "Walk / menu back"
-msgstr "Passer / menu retour"
+msgstr "Marcher / menu retour"
 
 #: engines/scumm/metaengine.cpp:1191
 msgid "Skip / menu back"



From noreply at scummvm.org  Sun Jun  7 14:04:32 2026
From: noreply at scummvm.org (lotharsm)
Date: Sun,  7 Jun 2026 14:04:32 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm-web master ->
 6c15c1d17d179ca74c6b30f8bec04a7a8384b567
Message-ID: <20260607140432.BCB707005BC@ron.scummvm.net>

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

Summary:
6c15c1d17d DATA: Update data files


Commit: 6c15c1d17d179ca74c6b30f8bec04a7a8384b567
    https://github.com/scummvm/scummvm-web/commit/6c15c1d17d179ca74c6b30f8bec04a7a8384b567
Author: lotharsm (mail at serra.me)
Date: 2026-06-07T14:04:27Z

Commit Message:
DATA: Update data files

Changed paths:
    data/en/game_demos.yaml
    data/en/games.yaml


diff --git a/data/en/game_demos.yaml b/data/en/game_demos.yaml
index c8297bf7..8c833f57 100644
--- a/data/en/game_demos.yaml
+++ b/data/en/game_demos.yaml
@@ -4337,6 +4337,15 @@
     language2: ''
     language3: ''
     url: /frs/demos/scumm/he/freddi3-win-demo-nl.zip
+-
+    id: 'scumm:freddi3'
+    platform: win
+    '': ''
+    category: German
+    language: de
+    language2: ''
+    language3: ''
+    url: /frs/demos/scumm/he/freddi3-win-demo-de.zip
 -
     id: 'scumm:freddi3'
     platform: win
@@ -4390,7 +4399,7 @@
     language: fr
     language2: ''
     language3: ''
-    url: /frs/demos/scumm/he/freddi4-win-demo.fr.zip
+    url: /frs/demos/scumm/he/freddi4-win-demo-fr.zip
 -
     id: 'scumm:freddi4'
     platform: win
@@ -4634,6 +4643,15 @@
     language2: ''
     language3: ''
     url: /frs/demos/scumm/he/pajama-win-demo-fr.zip
+-
+    id: 'scumm:pajama'
+    platform: win
+    '': ''
+    category: Dutch
+    language: nl
+    language2: ''
+    language3: ''
+    url: /frs/demos/scumm/he/pajama-win-demo-nl.zip
 -
     id: 'scumm:pajama'
     platform: win
@@ -4670,6 +4688,15 @@
     language2: ''
     language3: ''
     url: /frs/demos/scumm/he/pajama2-win-demo-nl.zip
+-
+    id: 'scumm:pajama2'
+    platform: win
+    '': ''
+    category: German
+    language: de
+    language2: ''
+    language3: ''
+    url: /frs/demos/scumm/he/pajama2-win-demo-de.zip
 -
     id: 'scumm:pajama2'
     platform: win
@@ -5075,6 +5102,15 @@
     language2: ''
     language3: ''
     url: /frs/demos/scumm/he/spyfox-win-demo-nl.zip
+-
+    id: 'scumm:spyfox'
+    platform: win
+    '': ''
+    category: German
+    language: de
+    language2: ''
+    language3: ''
+    url: /frs/demos/scumm/he/spyfox-win-demo-de.zip
 -
     id: 'scumm:spyfox'
     platform: win
diff --git a/data/en/games.yaml b/data/en/games.yaml
index 2da77d08..4bb07ace 100644
--- a/data/en/games.yaml
+++ b/data/en/games.yaml
@@ -38,7 +38,7 @@
     gog_id: noctropolis
     zoom_id: ''
     additional_stores: ''
-    datafiles: ''
+    datafiles: Noctropolis
     wikipedia_page: Noctropolis
     series_id: ''
 -



From noreply at scummvm.org  Sun Jun  7 14:10:19 2026
From: noreply at scummvm.org (bluegr)
Date: Sun,  7 Jun 2026 14:10:19 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 58e81c2b68c1ca3a52ff111d22812887c7893e25
Message-ID: <20260607141019.693977005BC@ron.scummvm.net>

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

Summary:
58e81c2b68 AGS: Detection, initial support for Gobliiins 5 from Gobliiins Collection (Switch)


Commit: 58e81c2b68c1ca3a52ff111d22812887c7893e25
    https://github.com/scummvm/scummvm/commit/58e81c2b68c1ca3a52ff111d22812887c7893e25
Author: eientei95 (einstein95 at users.noreply.github.com)
Date: 2026-06-07T17:10:14+03:00

Commit Message:
AGS: Detection, initial support for Gobliiins 5 from Gobliiins Collection (Switch)

Changed paths:
  A engines/ags/plugins/ags_console_sys_plugin/ags_console_sys_plugin.cpp
  A engines/ags/plugins/ags_console_sys_plugin/ags_console_sys_plugin.h
    engines/ags/detection_tables.h
    engines/ags/module.mk
    engines/ags/plugins/plugin_base.cpp


diff --git a/engines/ags/detection_tables.h b/engines/ags/detection_tables.h
index 65e498068a1..a4c9695a04b 100644
--- a/engines/ags/detection_tables.h
+++ b/engines/ags/detection_tables.h
@@ -325,6 +325,7 @@ const PlainGameDescriptor GAME_NAMES[] = {
 	{ "ghostdream", "Ghostdream" },
 	{ "ghostman", "Ghostman: The Council Calamity" },
 	{ "gigant", "Gigant" },
+	{ "gobliiins5", "Gobliiins5 - L'Invasion des Morglotons" },
 	{ "gobliiins5-1", "Gobliiins5 - L'Invasion des Morglotons Part1/Demo" },
 	{ "gobliiins5-2", "Gobliiins5 - L'Invasion des Morglotons Part2" },
 	{ "gobliiins5-3", "Gobliiins5 - L'Invasion des Morglotons Part3" },
@@ -5012,6 +5013,9 @@ const AGSGameDescription GAME_DESCRIPTIONS[] = {
 	GAME_ENTRY_STEAM("gobliiins5-1", "Gobliiins5-Part1.ags", "41513db1bd8870e43426b9e94bb26ad1", 171800863),
 	// Steam release 2023-11-16
 	GAME_ENTRY_STEAM("gobliiins5-1", "Gobliiins5-Part1.ags", "41513db1bd8870e43426b9e94bb26ad1", 171800709),
+	// Gobliiins Collection release 2026-05-27
+	GAME_ENTRY_PLATFORM("gobliiins5", "Gobliiins5.ags", "58c44c065cbdbddf83e9535ef17ed51f", 281472880, "Switch"),
+
 
 	// Itch.io v? release 2026-02-10
 	GAME_ENTRY("gobliins6", "Gobliins6.exe", "t:aac0f0616ede4ec1d4300103b008f34b", 1287053862),
diff --git a/engines/ags/module.mk b/engines/ags/module.mk
index de0a00f9444..afc68429efb 100644
--- a/engines/ags/module.mk
+++ b/engines/ags/module.mk
@@ -318,6 +318,7 @@ MODULE_OBJS = \
 	plugins/ags_bm_font_renderer/ags_bm_font_renderer.o \
 	plugins/ags_clipboard/ags_clipboard.o \
 	plugins/ags_collision_detector/ags_collision_detector.o \
+	plugins/ags_console_sys_plugin/ags_console_sys_plugin.o \
 	plugins/ags_consoles/ags_consoles.o \
 	plugins/ags_controller/ags_controller.o \
 	plugins/ags_controller/ags_controller_arcnor.o \
diff --git a/engines/ags/plugins/ags_console_sys_plugin/ags_console_sys_plugin.cpp b/engines/ags/plugins/ags_console_sys_plugin/ags_console_sys_plugin.cpp
new file mode 100644
index 00000000000..d4da1f0edb6
--- /dev/null
+++ b/engines/ags/plugins/ags_console_sys_plugin/ags_console_sys_plugin.cpp
@@ -0,0 +1,146 @@
+/* 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
+ * 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 "ags/plugins/ags_console_sys_plugin/ags_console_sys_plugin.h"
+// #include "ags/shared/core/platform.h"
+#include "common/config-manager.h"
+#include "common/language.h"
+
+namespace AGS3 {
+namespace Plugins {
+namespace AGSConsoleSysPlugin {
+
+const char *AGSConsoleSysPlugin::AGS_GetPluginName() {
+	return "AGS Console System Plugin";
+}
+
+void AGSConsoleSysPlugin::AGS_EngineStartup(IAGSEngine *engine) {
+	PluginBase::AGS_EngineStartup(engine);
+
+	SCRIPT_METHOD(AGSConsoleSysPlugin::GetSystemInputDeviceType^0, AGSConsoleSysPlugin::GetSystemInputDeviceType);
+	SCRIPT_METHOD(AGSConsoleSysPlugin::GetSystemLanguage^0, AGSConsoleSysPlugin::GetSystemLanguage);
+	SCRIPT_METHOD(AGSConsoleSysPlugin::GetSystemPlatform^0, AGSConsoleSysPlugin::GetSystemPlatform);
+	SCRIPT_METHOD(AGSConsoleSysPlugin::GetTextureFilteringType^0, AGSConsoleSysPlugin::GetTextureFilteringType);
+	SCRIPT_METHOD(AGSConsoleSysPlugin::IsSaveHandlerBusy^0, AGSConsoleSysPlugin::IsSaveHandlerBusy);
+	SCRIPT_METHOD(AGSConsoleSysPlugin::ResetControllerColor^0, AGSConsoleSysPlugin::ResetControllerColor);
+	SCRIPT_METHOD(AGSConsoleSysPlugin::SetBorder^1, AGSConsoleSysPlugin::SetBorder);
+	SCRIPT_METHOD(AGSConsoleSysPlugin::SetControllerColor^3, AGSConsoleSysPlugin::SetControllerColor);
+	SCRIPT_METHOD(AGSConsoleSysPlugin::SetPixelPerfect^1, AGSConsoleSysPlugin::SetPixelPerfect);
+	SCRIPT_METHOD(AGSConsoleSysPlugin::SetTextureFilteringType^1, AGSConsoleSysPlugin::SetTextureFilteringType);
+
+	SCRIPT_METHOD(Game_RAG::EnableCheats^0, AGSConsoleSysPlugin::EnableCheats);
+}
+
+void AGSConsoleSysPlugin::GetSystemInputDeviceType(ScriptMethodParams &params) {
+	warning("AGSConsoleSysPlugin::GetSystemInputDeviceType STUB: returning false");
+	params._result = 0;
+}
+
+void AGSConsoleSysPlugin::GetSystemLanguage(ScriptMethodParams &params) {
+	// PARAMS0();
+	// Returns the current system language as an integer
+	// 0   = German
+	// 1/2 = English (unknown which is UK/US)
+	// 3   = Spanish
+	// 4   = ?
+	// 5/6 = French (unknown which is France/Canada)
+	// 7   = Italian
+	if (!ConfMan.get("language").empty()) {
+		// Map the language defined in the command-line "language" option to its description
+		Common::Language lang = Common::parseLanguage(ConfMan.get("language"));
+
+		switch (lang) {
+		case Common::DE_DEU:
+			params._result = 0;
+			break;
+		case Common::EN_ANY:
+			params._result = 1;
+			break;
+		case Common::ES_ESP:
+			params._result = 3;
+			break;
+		case Common::FR_FRA:
+			params._result = 5;
+			break;
+		case Common::IT_ITA:
+			params._result = 7;
+			break;
+		default:
+			params._result = 1; // default to English
+			break;
+		}
+	} else
+		params._result = 1; // default to English
+}
+
+void AGSConsoleSysPlugin::GetSystemPlatform(ScriptMethodParams &params) {
+	warning("AGSConsoleSysPlugin::GetSystemPlatform STUB: returning 0");
+	params._result = 0;
+}
+
+void AGSConsoleSysPlugin::GetTextureFilteringType(ScriptMethodParams &params) {
+	warning("AGSConsoleSysPlugin::GetTextureFilteringType STUB: returning false");
+	params._result = false;
+}
+
+void AGSConsoleSysPlugin::IsSaveHandlerBusy(ScriptMethodParams &params) {
+	warning("AGSConsoleSysPlugin::IsSaveHandlerBusy STUB: returning false");
+	params._result = false;
+}
+
+void AGSConsoleSysPlugin::ResetControllerColor(ScriptMethodParams &params) {
+	warning("AGSConsoleSysPlugin::ResetControllerColor STUB: returning false");
+	params._result = false;
+}
+
+void AGSConsoleSysPlugin::SetBorder(ScriptMethodParams &params) {
+	// PARAMS1(char *, filename);
+	// Loads a BMP and uses it as a border. Result is ignored.
+	// If "none.bmp", border is removed.
+	params._result = 0;
+}
+
+void AGSConsoleSysPlugin::SetControllerColor(ScriptMethodParams &params) {
+	// PARAMS3(?);
+	warning("AGSConsoleSysPlugin::SetControllerColor STUB: returning false");
+	params._result = false;
+}
+
+void AGSConsoleSysPlugin::SetPixelPerfect(ScriptMethodParams &params) {
+	// PARAMS1(?);
+	warning("AGSConsoleSysPlugin::SetPixelPerfect STUB: returning false");
+	params._result = false;
+}
+
+void AGSConsoleSysPlugin::SetTextureFilteringType(ScriptMethodParams &params) {
+	// PARAMS1(?);
+	warning("AGSConsoleSysPlugin::SetTextureFilteringType STUB: returning false");
+	params._result = false;
+}
+
+void AGSConsoleSysPlugin::EnableCheats(ScriptMethodParams &params) {
+	params._result = false;
+}
+
+
+} // namespace AGSConsoleSysPlugin
+} // namespace Plugins
+} // namespace AGS3
diff --git a/engines/ags/plugins/ags_console_sys_plugin/ags_console_sys_plugin.h b/engines/ags/plugins/ags_console_sys_plugin/ags_console_sys_plugin.h
new file mode 100644
index 00000000000..2a332ef4d8e
--- /dev/null
+++ b/engines/ags/plugins/ags_console_sys_plugin/ags_console_sys_plugin.h
@@ -0,0 +1,60 @@
+/* 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
+ * 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 AGS_PLUGINS_AGS_CONSOLE_SYS_PLUGIN_H
+#define AGS_PLUGINS_AGS_CONSOLE_SYS_PLUGIN_H
+
+#include "ags/plugins/ags_plugin.h"
+
+namespace AGS3 {
+namespace Plugins {
+namespace AGSConsoleSysPlugin {
+
+class AGSConsoleSysPlugin : public PluginBase {
+	SCRIPT_HASH(AGSConsoleSysPlugin)
+
+private:
+	void GetSystemInputDeviceType(ScriptMethodParams &params);
+	void GetSystemLanguage(ScriptMethodParams &params);
+	void GetSystemPlatform(ScriptMethodParams &params);
+	void GetTextureFilteringType(ScriptMethodParams &params);
+	void IsSaveHandlerBusy(ScriptMethodParams &params);
+	void ResetControllerColor(ScriptMethodParams &params);
+	void SetBorder(ScriptMethodParams &params);
+	void SetControllerColor(ScriptMethodParams &params);
+	void SetPixelPerfect(ScriptMethodParams &params);
+	void SetTextureFilteringType(ScriptMethodParams &params);
+	void EnableCheats(ScriptMethodParams &params);
+
+public:
+	AGSConsoleSysPlugin() : PluginBase() {}
+	virtual ~AGSConsoleSysPlugin() {}
+
+	const char *AGS_GetPluginName() override;
+	void AGS_EngineStartup(IAGSEngine *engine) override;
+
+};
+
+} // namespace AGSConsoleSysPlugin
+} // namespace Plugins
+} // namespace AGS3
+
+#endif
diff --git a/engines/ags/plugins/plugin_base.cpp b/engines/ags/plugins/plugin_base.cpp
index 9905531dbeb..00515e6f690 100644
--- a/engines/ags/plugins/plugin_base.cpp
+++ b/engines/ags/plugins/plugin_base.cpp
@@ -27,6 +27,7 @@
 #include "ags/plugins/ags_bm_font_renderer/ags_bm_font_renderer.h"
 #include "ags/plugins/ags_clipboard/ags_clipboard.h"
 #include "ags/plugins/ags_collision_detector/ags_collision_detector.h"
+#include "ags/plugins/ags_console_sys_plugin/ags_console_sys_plugin.h"
 #include "ags/plugins/ags_consoles/ags_consoles.h"
 #include "ags/plugins/ags_controller/ags_controller.h"
 #include "ags/plugins/ags_controller/ags_controller_arcnor.h"
@@ -95,6 +96,9 @@ Plugins::PluginBase *pluginOpen(const char *filename) {
 	if (fname.equalsIgnoreCase("AGSConsoles"))
 		return new AGSConsoles::AGSConsoles();
 
+	if (fname.equalsIgnoreCase("AGSConsoleSysPlugin") || fname.equalsIgnoreCase("ags_console_system_plugin"))
+		return new AGSConsoleSysPlugin::AGSConsoleSysPlugin();
+
 	if (fname.equalsIgnoreCase("AGSController") || fname.equalsIgnoreCase("agscontrollerpulp"))
 		return new AGSController::AGSController();
 
@@ -161,7 +165,7 @@ Plugins::PluginBase *pluginOpen(const char *filename) {
 		return new AGSGalaxySteam::AGSWadjetEyeSteam();
 
 	if (fname.equalsIgnoreCase("agsteam") || fname.equalsIgnoreCase("agsteam-unified") ||
-	        fname.equalsIgnoreCase("agsteam-disjoint"))
+	        fname.equalsIgnoreCase("agsteam-disjoint") || fname.equalsIgnoreCase("ags_achievements"))
 		return new AGSGalaxySteam::AGSSteam();
 
 	if (fname.equalsIgnoreCase("AGSTouch"))



From noreply at scummvm.org  Sun Jun  7 19:32:42 2026
From: noreply at scummvm.org (sev-)
Date: Sun,  7 Jun 2026 19:32:42 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 5f01c272c1508d470f7a8d468cfe12fa0beb2ad4
Message-ID: <20260607193242.C5E567005BC@ron.scummvm.net>

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:
a97b925d2e DIRECTOR: DT: improve search with new modes and ui
5f01c272c1 DIRECTOR: DT: add Windows panel showing loaded windows and all movies


Commit: a97b925d2e888c0ad1c09eeb41bb6bd9e62e5fd6
    https://github.com/scummvm/scummvm/commit/a97b925d2e888c0ad1c09eeb41bb6bd9e62e5fd6
Author: ramyak-sharma (ramyaksharma1 at gmail.com)
Date: 2026-06-07T21:32:37+02:00

Commit Message:
DIRECTOR: DT: improve search with new modes and ui

Changed paths:
    engines/director/debugger/dt-internal.h
    engines/director/debugger/dt-search.cpp
    engines/director/lingo/lingo-object.h


diff --git a/engines/director/debugger/dt-internal.h b/engines/director/debugger/dt-internal.h
index 998f4795286..f1b51776c1d 100644
--- a/engines/director/debugger/dt-internal.h
+++ b/engines/director/debugger/dt-internal.h
@@ -104,8 +104,9 @@ typedef struct ImGuiWindows {
 
 enum SearchMode {
 	kSearchAll = 0,
-	kSearchHandlerNames,
-	kSearchScriptBody,
+	kSearchHandlers,
+	kSearchVariables,
+	kSearchBody,
 };
 
 typedef struct ScriptData {
@@ -329,6 +330,7 @@ typedef struct ImGuiState {
 } ImGuiState;
 
 // debugtools.cpp
+const LingoDec::Handler *getHandler(const Cast *cast, CastMemberID id, const Common::String &handlerId);
 ImGuiScript toImGuiScript(ScriptType scriptType, CastMemberID id, const Common::String &handlerId);
 ScriptContext *getScriptContext(CastMemberID id);
 ScriptContext *getScriptContext(uint32 nameIndex, CastMemberID castId, Common::String handler);
diff --git a/engines/director/debugger/dt-search.cpp b/engines/director/debugger/dt-search.cpp
index 51edf530b28..454f5400449 100644
--- a/engines/director/debugger/dt-search.cpp
+++ b/engines/director/debugger/dt-search.cpp
@@ -22,8 +22,9 @@
 #include "director/director.h"
 #include "director/debugger/dt-internal.h"
 
+#include "director/archive.h"
 #include "director/cast.h"
-#include "director/castmember/text.h"
+#include "director/castmember/castmember.h"
 #include "director/movie.h"
 
 namespace Director {
@@ -31,57 +32,82 @@ namespace DT {
 
 static const char *searchModeNames[] = {
 	"All",
-	"Handler Names",
-	"Script Body",
+	"Handlers",
+	"Variables",
+	"Body",
 };
 
+static bool containsQuery(const Common::String &s, const Common::String &query) {
+	Common::String lower = s;
+	lower.toLowercase();
+	return lower.contains(query.c_str());
+}
+
 static void searchCast(Cast *cast, int castLibID, const Common::String &query, Director::Lingo *lingo, Movie *movie, SearchMode mode) {
-	if (!cast) return;
-	if (!cast->_lingoArchive) return;
+	if (!cast || !cast->_lingoArchive)
+		return;
 
 	for (int i = 0; i <= kMaxScriptType; i++) {
 		if (cast->_lingoArchive->scriptContexts[i].empty())
 			continue;
 
 		for (auto &scriptContext : cast->_lingoArchive->scriptContexts[i]) {
+			// check property names once per script context, shared across all its handlers
+			bool propertyMatch = false;
+			if (mode == kSearchAll || mode == kSearchVariables) {
+				for (const Common::String &name : scriptContext._value->getPropertyNames()) {
+					if (containsQuery(name, query)) {
+						propertyMatch = true;
+						break;
+					}
+				}
+			}
+
+			// also search the cast member name itself
+			bool castNameMatch = false;
+			if (mode == kSearchAll || mode == kSearchHandlers) {
+				CastMember *cm = cast->getCastMember(scriptContext._key, false);
+				if (cm && containsQuery(cm->getName(), query))
+					castNameMatch = true;
+			}
+
 			for (auto &functionHandler : scriptContext._value->_functionHandlers) {
 				bool found = false;
 
-				// Search handler name
-				if (mode == kSearchAll || mode == kSearchHandlerNames) {
-					Common::String handlerLower = functionHandler._key;
-					handlerLower.toLowercase();
-					if (handlerLower.contains(query.c_str()))
+				// handler name and cast member name
+				if (mode == kSearchAll || mode == kSearchHandlers) {
+					if (castNameMatch || containsQuery(functionHandler._key, query))
 						found = true;
+				}
 
-					// Search property and global names
-					if (!found) {
+				// variable names -> property (from ScriptContext), argument and global (from lingodec handler)
+				if (!found && (mode == kSearchAll || mode == kSearchVariables)) {
+					if (propertyMatch) {
+						found = true;
+					} else {
 						CastMemberID memberID(scriptContext._key, castLibID);
-						ImGuiScript script = toImGuiScript(scriptContext._value->_scriptType, memberID, functionHandler._key);
-						for (auto &name : script.propertyNames) {
-							Common::String n = name;
-							n.toLowercase();
-							if (n.contains(query.c_str())) { found = true; break; }
-						}
-						if (!found) {
-							for (auto &name : script.globalNames) {
-								Common::String n = name;
-								n.toLowercase();
-								if (n.contains(query.c_str())) { found = true; break; }
+						const LingoDec::Handler *ldHandler = getHandler(cast, memberID, functionHandler._key);
+						if (ldHandler) {
+							for (const Common::String &name : ldHandler->argumentNames) {
+								if (containsQuery(name, query)) { found = true; break; }
+							}
+							if (!found) {
+								for (const Common::String &name : ldHandler->globalNames) {
+									if (containsQuery(name, query)) { found = true; break; }
+								}
 							}
 						}
 					}
 				}
 
-				// Search script body via decodeInstruction
-				if (!found && lingo && (mode == kSearchAll || mode == kSearchScriptBody)) {
+				// acript body via decoded bytecode
+				if (!found && lingo && (mode == kSearchAll || mode == kSearchBody)) {
 					Symbol &sym = functionHandler._value;
 					if (sym.type == HANDLER && sym.u.defn) {
 						uint pc = 0;
 						while (pc < sym.u.defn->size()) {
 							Common::String line = lingo->decodeInstruction(sym.u.defn, pc, &pc);
-							line.toLowercase();
-							if (line.contains(query.c_str())) {
+							if (containsQuery(line, query)) {
 								found = true;
 								break;
 							}
@@ -130,7 +156,7 @@ void showSearchBar() {
 	auto &search = _state->_search;
 
 	ImGui::SetNextWindowPos(ImVec2(20, 20), ImGuiCond_FirstUseEver);
-	ImGui::SetNextWindowSize(ImVec2(480, 240), ImGuiCond_FirstUseEver);
+	ImGui::SetNextWindowSize(ImVec2(560, 320), ImGuiCond_FirstUseEver);
 
 	if (!ImGui::Begin("Search", &_state->_w.search)) {
 		_state->_dbg._highlightQuery = "";
@@ -144,6 +170,9 @@ void showSearchBar() {
 
 	ImGui::SameLine();
 	ImGui::SetNextItemWidth(-80);
+	// focus the input field when the window first opens
+	if (ImGui::IsWindowAppearing())
+		ImGui::SetKeyboardFocusHere();
 	if (ImGui::InputText("##search", search.input, sizeof(search.input),
 			ImGuiInputTextFlags_EnterReturnsTrue))
 		search.dirty = true;
@@ -156,35 +185,62 @@ void showSearchBar() {
 		search.dirty = false;
 		Common::String query(search.input);
 		query.toLowercase();
-		if (!query.empty())
+		if (query.empty()) {
+			// clear results immediately when the input is erased
+			search.results.clear();
+			_state->_dbg._highlightQuery = "";
+		} else {
 			runSearch(query, (SearchMode)search.mode);
+		}
 	}
 
-	// display results
 	ImGui::Separator();
 	ImGui::Text("Results: %d", (int)search.results.size());
 	ImGui::Separator();
 
-	if (ImGui::BeginTable("##results", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_Resizable | ImGuiTableFlags_RowBg)) {
-		ImGui::TableSetupColumn("Handler", 0, 240.f);
-		ImGui::TableSetupColumn("Cast", ImGuiTableColumnFlags_WidthStretch, 240.f);
+	ImVec2 tableSize = ImGui::GetContentRegionAvail();
+	if (ImGui::BeginTable("##results", 3,
+			ImGuiTableFlags_Borders | ImGuiTableFlags_SizingFixedFit |
+			ImGuiTableFlags_Resizable | ImGuiTableFlags_RowBg |
+			ImGuiTableFlags_ScrollY, tableSize)) {
+		ImGui::TableSetupScrollFreeze(0, 1);
+		ImGui::TableSetupColumn("Handler", 0, 200.f);
+		ImGui::TableSetupColumn("Type", 0, 75.f);
+		ImGui::TableSetupColumn("Cast Member", ImGuiTableColumnFlags_WidthStretch, 200.f);
 		ImGui::TableHeadersRow();
 
 		for (int i = 0; i < (int)search.results.size(); i++) {
-			auto &script = search.results[i];
+			ImGuiScript &script = search.results[i];
 			ImGui::PushID(i);
 			ImGui::TableNextRow();
 			ImGui::TableNextColumn();
-			if (ImGui::Selectable(script.handlerName.c_str())) {
+
+			if (ImGui::Selectable(script.handlerName.c_str(), false,
+					ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowOverlap)) {
 				Common::String q(search.input);
 				q.toLowercase();
 				_state->_dbg._highlightQuery = q;
-
 				addToOpenHandlers(script);
 				_state->_dbg._goToDefinition = true;
 			}
+
+			ImGui::TableNextColumn();
+			ImGui::TextUnformatted(scriptType2str(script.type));
+
 			ImGui::TableNextColumn();
-			ImGui::Text("%s", script.id.asString().c_str());
+			// show human-readable cast member name, falling back to the ID string
+			Common::String castLabel = script.id.asString();
+			Cast *resCast = nullptr;
+			if (script.id.castLib == SHARED_CAST_LIB)
+				resCast = movie->getSharedCast();
+			else
+				resCast = movie->getCasts()->getValOrDefault(script.id.castLib, nullptr);
+			if (resCast) {
+				CastMember *cm = resCast->getCastMember(script.id.member, false);
+				if (cm) castLabel = getDisplayName(cm);
+			}
+			ImGui::TextUnformatted(castLabel.c_str());
+
 			ImGui::PopID();
 		}
 		ImGui::EndTable();
diff --git a/engines/director/lingo/lingo-object.h b/engines/director/lingo/lingo-object.h
index 335a9846969..ebd29576ad4 100644
--- a/engines/director/lingo/lingo-object.h
+++ b/engines/director/lingo/lingo-object.h
@@ -239,6 +239,8 @@ public:
 	bool isFactory() const { return _objType == kFactoryObj; };
 	void setFactory(bool flag) { _objType = flag ? kFactoryObj : kScriptObj; }
 
+	const Common::Array<Common::String> &getPropertyNames() const { return _propertyNames; }
+
 	void setOnlyInLctxContexts() { _onlyInLctxContexts = true; }
 	bool getOnlyInLctxContexts() { return _onlyInLctxContexts; }
 


Commit: 5f01c272c1508d470f7a8d468cfe12fa0beb2ad4
    https://github.com/scummvm/scummvm/commit/5f01c272c1508d470f7a8d468cfe12fa0beb2ad4
Author: ramyak-sharma (ramyaksharma1 at gmail.com)
Date: 2026-06-07T21:32:37+02:00

Commit Message:
DIRECTOR: DT: add Windows panel showing loaded windows and all movies

Changed paths:
    engines/director/cast.cpp
    engines/director/debugger/debugtools.cpp
    engines/director/debugger/dt-internal.h
    engines/director/debugger/dt-lists.cpp
    engines/director/debugger/dt-save-state.cpp


diff --git a/engines/director/cast.cpp b/engines/director/cast.cpp
index 1915d5d1558..f9001f7009a 100644
--- a/engines/director/cast.cpp
+++ b/engines/director/cast.cpp
@@ -314,7 +314,8 @@ void Cast::setArchive(Common::SharedPtr<Archive> archive) {
 }
 
 void Cast::loadArchive() {
-	loadConfig();
+	if (!loadConfig())
+		return;
 	loadCast();
 }
 
@@ -527,8 +528,11 @@ bool Cast::loadConfig() {
 		//Calculation and verification of checksum
 		uint32 check = computeChecksum();
 
-		if (check != _checksum)
-			warning("BUILDBOT: The checksum for this VWCF resource is incorrect. Got %04x, but expected %04x", check, _checksum);
+		if (check != _checksum) {
+			warning("BUILDBOT: The checksum for this VWCF resource is incorrect. Got %08x, but expected %08x", check, _checksum);
+			delete stream;
+			return false;
+		}
 
 		if (_version >= kFileVer400 && _version < kFileVer500) {
 			_field30 = stream->readSint16();
diff --git a/engines/director/debugger/debugtools.cpp b/engines/director/debugger/debugtools.cpp
index 6512c3e3eaf..eac525112e0 100644
--- a/engines/director/debugger/debugtools.cpp
+++ b/engines/director/debugger/debugtools.cpp
@@ -916,6 +916,7 @@ void onImGuiRender() {
 			ImGui::MenuItem("Watched Vars", NULL, &_state->_w.watchedVars);
 			ImGui::MenuItem("Logger", NULL, &_state->_w.logger);
 			ImGui::MenuItem("Archive", NULL, &_state->_w.archive);
+			ImGui::MenuItem("Windows", NULL, &_state->_w.windows);
 			ImGui::MenuItem("Execution Context", NULL, &_state->_w.executionContext);
 
 			ImGui::SeparatorText("Misc");
@@ -949,6 +950,7 @@ void onImGuiRender() {
 	showBreakpointList();
 	showSettings();
 	showArchive();
+	showWindows();
 	showWatchedVars();
 	_state->_logger->draw("Logger", &_state->_w.logger);
 }
diff --git a/engines/director/debugger/dt-internal.h b/engines/director/debugger/dt-internal.h
index f1b51776c1d..1372e834365 100644
--- a/engines/director/debugger/dt-internal.h
+++ b/engines/director/debugger/dt-internal.h
@@ -99,6 +99,7 @@ typedef struct ImGuiWindows {
 	bool executionContext = false;
 	bool search = false;
 	bool imageViewer = false;
+	bool windows = false;
 } ImGuiWindows;
 
 
@@ -373,6 +374,7 @@ void showVars();
 void showWatchedVars();
 void showBreakpointList();
 void showArchive();
+void showWindows();
 
 // dt-score.cpp
 void showScore();
diff --git a/engines/director/debugger/dt-lists.cpp b/engines/director/debugger/dt-lists.cpp
index a88aecd4615..7208a81c51d 100644
--- a/engines/director/debugger/dt-lists.cpp
+++ b/engines/director/debugger/dt-lists.cpp
@@ -20,12 +20,18 @@
  */
 
 #include "director/director.h"
+#include "common/archive.h"
 #include "director/archive.h"
+#include "director/cast.h"
 #include "director/debugger.h"
 #include "director/debugger/dt-internal.h"
-
+#include "director/movie.h"
+#include "director/score.h"
+#include "director/window.h"
 #include "director/lingo/lingo-object.h"
 
+#include "graphics/macgui/macwindow.h"
+
 namespace Director {
 namespace DT {
 
@@ -355,6 +361,152 @@ void showArchive() {
 	ImGui::End();
 }
 
+void showWindows() {
+	if (!_state->_w.windows)
+		return;
+
+	ImGui::SetNextWindowPos(ImVec2(20, 20), ImGuiCond_FirstUseEver);
+	ImGui::SetNextWindowSize(ImVec2(700, 280), ImGuiCond_FirstUseEver);
+
+	if (!ImGui::Begin("Windows", &_state->_w.windows)) {
+		ImGui::End();
+		return;
+	}
+
+	// Stage is not in getWindowList() ??? it must be prepended separately
+	Common::Array<Window *> allWindows;
+	allWindows.push_back(g_director->getStage());
+	for (auto w : *g_director->getWindowList())
+		allWindows.push_back(w);
+
+	float availH = ImGui::GetContentRegionAvail().y;
+	ImVec2 tableSize(0, availH * 0.5f);
+	if (ImGui::BeginTable("##windowtable", 6,
+			ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg |
+			ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_Resizable |
+			ImGuiTableFlags_ScrollY, tableSize)) {
+		ImGui::TableSetupScrollFreeze(0, 1);
+		ImGui::TableSetupColumn("Title",       0, 100.f);
+		ImGui::TableSetupColumn("Movie",       0, 130.f);
+		ImGui::TableSetupColumn("Filename",    ImGuiTableColumnFlags_WidthStretch, 160.f);
+		ImGui::TableSetupColumn("State",       0, 75.f);
+		ImGui::TableSetupColumn("Frame",       0, 55.f);
+		ImGui::TableSetupColumn("Shared Cast", ImGuiTableColumnFlags_WidthStretch, 140.f);
+		ImGui::TableHeadersRow();
+
+		for (uint i = 0; i < allWindows.size(); i++) {
+			Window *window = allWindows[i];
+			ImGui::PushID((int)i);
+			ImGui::TableNextRow();
+
+			ImGui::TableNextColumn();
+			bool isStage = (window == g_director->getStage());
+			const char *title = isStage ? "(stage)" :
+				(window->getMacWindow() ? window->getMacWindow()->getTitle().c_str() : "");
+			ImGui::TextUnformatted(title);
+
+			Movie *movie = window->getCurrentMovie();
+
+			ImGui::TableNextColumn();
+			if (movie)
+				ImGui::TextUnformatted(movie->getMacName().c_str());
+			else
+				ImGui::TextDisabled("(none)");
+
+			ImGui::TableNextColumn();
+			Common::String filename = window->getFileName();
+			if (!filename.empty())
+				ImGui::TextUnformatted(filename.c_str());
+			else if (!window->getCurrentPath().empty())
+				ImGui::TextUnformatted(window->getCurrentPath().c_str());
+			else
+				ImGui::TextDisabled("(none)");
+
+			ImGui::TableNextColumn();
+			if (movie) {
+				Score *score = movie->getScore();
+				if (score) {
+					const char *stateStr = "unknown";
+					switch (score->_playState) {
+					case kPlayNotStarted: stateStr = "not started"; break;
+					case kPlayStarted:    stateStr = "playing";     break;
+					case kPlayStopped:    stateStr = "stopped";     break;
+					case kPlayPaused:     stateStr = "paused";      break;
+					default:              stateStr = "other";       break;
+					}
+					ImGui::TextUnformatted(stateStr);
+				}
+			} else {
+				ImGui::TextDisabled("-");
+			}
+
+			ImGui::TableNextColumn();
+			if (movie) {
+				Score *score = movie->getScore();
+				if (score)
+					ImGui::Text("%d / %d", score->getCurrentFrameNum(), score->getFramesNum());
+				else
+					ImGui::TextDisabled("-");
+			} else {
+				ImGui::TextDisabled("-");
+			}
+
+			ImGui::TableNextColumn();
+			Cast *sharedCast = movie ? movie->getSharedCast() : nullptr;
+			if (sharedCast && sharedCast->getArchive())
+				ImGui::TextUnformatted(sharedCast->getArchive()->getPathName().toString().c_str());
+			else
+				ImGui::TextDisabled("(none)");
+
+			ImGui::PopID();
+		}
+
+		ImGui::EndTable();
+	}
+
+	ImGui::SeparatorText("All Movies");
+
+	Common::ArchiveMemberList dirFiles;
+	SearchMan.listMatchingMembers(dirFiles, "*.dir");
+	SearchMan.listMatchingMembers(dirFiles, "*.DIR");
+
+	// deduplicate by name
+	Common::HashMap<Common::String, bool, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> seen;
+	Common::ArchiveMemberList uniqueFiles;
+	for (auto &f : dirFiles) {
+		if (!seen.contains(f->getName())) {
+			seen[f->getName()] = true;
+			uniqueFiles.push_back(f);
+		}
+	}
+
+	ImGui::Text("%d movies found", (int)uniqueFiles.size());
+
+	if (ImGui::BeginTable("##allfiles", 1,
+			ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg |
+			ImGuiTableFlags_SizingStretchProp | ImGuiTableFlags_ScrollY,
+			ImVec2(0, ImGui::GetContentRegionAvail().y))) {
+		ImGui::TableSetupScrollFreeze(0, 1);
+		ImGui::TableSetupColumn("Filename", ImGuiTableColumnFlags_WidthStretch);
+		ImGui::TableHeadersRow();
+
+		for (auto &f : uniqueFiles) {
+			ImGui::TableNextRow();
+			ImGui::TableNextColumn();
+			if (ImGui::Selectable(f->getName().c_str(), false, ImGuiSelectableFlags_SpanAllColumns)) {
+				Datum frame, movie;
+				movie.type = STRING;
+				movie.u.s = new Common::String(f->getName());
+				g_lingo->func_goto(frame, movie);
+			}
+		}
+
+		ImGui::EndTable();
+	}
+
+	ImGui::End();
+}
+
 
 } // namespace DT
 } // namespace Director
diff --git a/engines/director/debugger/dt-save-state.cpp b/engines/director/debugger/dt-save-state.cpp
index e8088fc94e0..689977fd4f9 100644
--- a/engines/director/debugger/dt-save-state.cpp
+++ b/engines/director/debugger/dt-save-state.cpp
@@ -49,6 +49,7 @@ Common::Array<WindowFlag> getWindowFlags() {
 		{ "Settings",			&_state->_w.settings		 },
 		{ "Vars",				&_state->_w.vars			 },
 		{ "Watched Vars",		&_state->_w.watchedVars		 },
+		{ "Windows",			&_state->_w.windows			 },
 	};
 }
 



From noreply at scummvm.org  Sun Jun  7 21:08:07 2026
From: noreply at scummvm.org (neuromancer)
Date: Sun,  7 Jun 2026 21:08:07 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 61d84074f729396526b43868bd5ae90796e70922
Message-ID: <20260607210807.1AADB7005BC@ron.scummvm.net>

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:
8dc22ae389 SCUMM: RA2: added no damage game flag to help debugging
61d84074f7 SCUMM: RA1: make sure sound buffer is large enough


Commit: 8dc22ae389235c18eaeca97db34455f67476d32f
    https://github.com/scummvm/scummvm/commit/8dc22ae389235c18eaeca97db34455f67476d32f
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-07T23:07:40+02:00

Commit Message:
SCUMM: RA2: added no damage game flag to help debugging

Changed paths:
    engines/scumm/detection.h
    engines/scumm/detection_tables.h
    engines/scumm/insane/rebel2/iact.cpp
    engines/scumm/insane/rebel2/rebel.cpp
    engines/scumm/insane/rebel2/rebel.h
    engines/scumm/insane/rebel2/render.cpp
    engines/scumm/metaengine.cpp
    engines/scumm/smush/rebel/smush_player_ra2.cpp
    engines/scumm/smush/rebel/smush_player_ra2.h


diff --git a/engines/scumm/detection.h b/engines/scumm/detection.h
index cd6dbfa9d43..29f49690434 100644
--- a/engines/scumm/detection.h
+++ b/engines/scumm/detection.h
@@ -41,6 +41,7 @@ namespace Scumm {
 #define GAMEOPTION_REBEL2_HIRES                              GUIO_GAMEOPTIONS10
 #define GAMEOPTION_REBEL2_UNLOCK_ALL                         GUIO_GAMEOPTIONS11
 #define GAMEOPTION_REBEL1_UNLOCK_ALL                         GUIO_GAMEOPTIONS12
+#define GAMEOPTION_REBEL2_NO_DAMAGE                          GUIO_GAMEOPTIONS13
 
 /**
  * Descriptor of a specific SCUMM game. Used internally to store
diff --git a/engines/scumm/detection_tables.h b/engines/scumm/detection_tables.h
index 9c2260772c4..8e14a3c0152 100644
--- a/engines/scumm/detection_tables.h
+++ b/engines/scumm/detection_tables.h
@@ -230,8 +230,8 @@ static const GameSettings gameVariantsTable[] = {
 
 	{"rebel1", "", 0, GID_REBEL1, 7, 0, MDT_NONE, 0, Common::kPlatformDOS, GUIO2(GUIO_NOMIDI, GAMEOPTION_REBEL1_UNLOCK_ALL)},
 
-	{"rebel2", "", 0, GID_REBEL2, 7, 0, MDT_NONE, 0, Common::kPlatformDOS, GUIO3(GUIO_NOMIDI, GAMEOPTION_REBEL2_HIRES, GAMEOPTION_REBEL2_UNLOCK_ALL)},
-	{"rebel2", "Demo", 0, GID_REBEL2, 7, 0, MDT_NONE, GF_DEMO, Common::kPlatformDOS, GUIO3(GUIO_NOMIDI, GAMEOPTION_REBEL2_HIRES, GAMEOPTION_REBEL2_UNLOCK_ALL)},
+	{"rebel2", "", 0, GID_REBEL2, 7, 0, MDT_NONE, 0, Common::kPlatformDOS, GUIO4(GUIO_NOMIDI, GAMEOPTION_REBEL2_HIRES, GAMEOPTION_REBEL2_UNLOCK_ALL, GAMEOPTION_REBEL2_NO_DAMAGE)},
+	{"rebel2", "Demo", 0, GID_REBEL2, 7, 0, MDT_NONE, GF_DEMO, Common::kPlatformDOS, GUIO4(GUIO_NOMIDI, GAMEOPTION_REBEL2_HIRES, GAMEOPTION_REBEL2_UNLOCK_ALL, GAMEOPTION_REBEL2_NO_DAMAGE)},
 
 	{"dig",  "", 0, GID_DIG, 7, 0, MDT_NONE, 0, UNK, GUIO5(GUIO_NOMIDI, GAMEOPTION_ENHANCEMENTS, GAMEOPTION_ORIGINALGUI, GAMEOPTION_LOWLATENCYAUDIO, GAMEOPTION_TTS)},
 	{"dig",  "Demo", 0, GID_DIG, 7, 0, MDT_NONE, GF_DEMO, UNK, GUIO4(GUIO_NOMIDI, GAMEOPTION_ORIGINALGUI, GAMEOPTION_LOWLATENCYAUDIO, GAMEOPTION_TTS)},
diff --git a/engines/scumm/insane/rebel2/iact.cpp b/engines/scumm/insane/rebel2/iact.cpp
index 8572dd26a67..4d484fdb487 100644
--- a/engines/scumm/insane/rebel2/iact.cpp
+++ b/engines/scumm/insane/rebel2/iact.cpp
@@ -484,15 +484,13 @@ void InsaneRebel2::iactRebel2Opcode3(Common::SeekableReadStream &b, int16 par2,
 				debug("Rebel2 Opcode3: probability=%d roll=%d (need roll < prob)", probability, roll);
 
 				if (roll < probability) {
-					if (!_rebelInvulnerable) {
-						int damageAmount = (params.shotDamage >= 0) ? params.shotDamage : 0;
-						_playerDamage += damageAmount;
-						if (_playerDamage > 255)
-							_playerDamage = 255;
+					int damageAmount = (params.shotDamage >= 0) ? params.shotDamage : 0;
+					if (applyPlayerDamage(damageAmount)) {
 						debug("Rebel2: H25 PROBABILISTIC damage from %d. Damage=%d total=%d",
 							srcIdBody1, damageAmount, _playerDamage);
 					}
-					initDamageFlash();
+					if (!_noDamage)
+						initDamageFlash();
 				}
 			} else {
 				debug("Rebel2 Opcode3: H25 par3=5 BLOCKED (damageLevel=%d isBitSet=%d)",
@@ -509,16 +507,14 @@ void InsaneRebel2::iactRebel2Opcode3(Common::SeekableReadStream &b, int16 par2,
 
 		// Direct damage: par4==100, separate from par3 branches (lines 99-111)
 		if (par4 == 100 && !isBitSet(srcIdBody0)) {
-			if (!_rebelInvulnerable) {
-				LevelDifficultyParams dparams = getDifficultyParams();
-				int directHitDamage = (dparams.missDamage >= 0) ? dparams.missDamage : 0;
-				_playerDamage += directHitDamage;
-				if (_playerDamage > 255)
-					_playerDamage = 255;
+			LevelDifficultyParams dparams = getDifficultyParams();
+			int directHitDamage = (dparams.missDamage >= 0) ? dparams.missDamage : 0;
+			if (applyPlayerDamage(directHitDamage)) {
 				debug("Rebel2: H25 DIRECT HIT par4=100 damage=%d total=%d",
 					directHitDamage, _playerDamage);
 			}
-			initDamageFlash();
+			if (!_noDamage)
+				initDamageFlash();
 		}
 	} else if (par3 == 1 || par3 == 2) {
 		// Non-Handler-25 direct hit path ??? FUN_4092D9 lines 209-227
@@ -546,14 +542,12 @@ void InsaneRebel2::iactRebel2Opcode3(Common::SeekableReadStream &b, int16 par2,
 				}
 
 				if (shouldDamage) {
-					if (!_rebelInvulnerable) {
-						_playerDamage += directHitDamage;
-						if (_playerDamage > 255)
-							_playerDamage = 255;
+					if (applyPlayerDamage(directHitDamage)) {
 						debug("Rebel2: DIRECT HIT damage from enemy %d. par3=%d par4=%d damage=%d total=%d",
 							srcId, par3, par4, directHitDamage, _playerDamage);
 					}
-					initDamageFlash();
+					if (!_noDamage)
+						initDamageFlash();
 				}
 			}
 		}
@@ -572,18 +566,17 @@ void InsaneRebel2::iactRebel2Opcode3(Common::SeekableReadStream &b, int16 par2,
 			debug("Rebel2 Opcode3: probability=%d roll=%d (need roll < prob)", probability, roll);
 
 			if (roll < probability) {
-				if (!_rebelInvulnerable) {
-					int damageAmount = (params.shotDamage >= 0) ? params.shotDamage : 0;
-					_playerDamage += damageAmount;
-					if (_playerDamage > 255)
-						_playerDamage = 255;
+				int damageAmount = (params.shotDamage >= 0) ? params.shotDamage : 0;
+				if (applyPlayerDamage(damageAmount)) {
 					debug("Rebel2: PROBABILISTIC damage from enemy %d. Damage=%d total=%d",
 						srcId, damageAmount, _playerDamage);
 				}
-				if (_rebelHandler == 8) {
-					triggerDamageEffect();
-				} else {
-					initDamageFlash();
+				if (!_noDamage) {
+					if (_rebelHandler == 8) {
+						triggerDamageEffect();
+					} else {
+						initDamageFlash();
+					}
 				}
 			}
 		}
@@ -985,12 +978,9 @@ void InsaneRebel2::handleOpcode6Handler7(Common::SeekableReadStream &b, int16 pa
 					_velocityHistory[i] = -127;
 				_hitCooldown = 10;
 				_spaceShotDirection = 1;
-				initDamageFlash();
-				if (!_rebelInvulnerable) {
-					_playerDamage += corridorWallDmg;
-					if (_playerDamage > 255)
-						_playerDamage = 255;
-				}
+				applyPlayerDamage(corridorWallDmg);
+				if (!_noDamage)
+					initDamageFlash();
 				_rebelHitCounter++;
 				playSfx(1, 127, 100);  // CRASH.SAD, right wall, pan right
 			}
@@ -1004,12 +994,9 @@ void InsaneRebel2::handleOpcode6Handler7(Common::SeekableReadStream &b, int16 pa
 					_velocityHistory[i] = 127;
 				_hitCooldown = 10;
 				_spaceShotDirection = 0;
-				initDamageFlash();
-				if (!_rebelInvulnerable) {
-					_playerDamage += corridorWallDmg;
-					if (_playerDamage > 255)
-						_playerDamage = 255;
-				}
+				applyPlayerDamage(corridorWallDmg);
+				if (!_noDamage)
+					initDamageFlash();
 				_rebelHitCounter++;
 				playSfx(1, 127, -100);  // CRASH.SAD, left wall, pan left
 			}
diff --git a/engines/scumm/insane/rebel2/rebel.cpp b/engines/scumm/insane/rebel2/rebel.cpp
index 7b8cb21dee8..e356621903c 100644
--- a/engines/scumm/insane/rebel2/rebel.cpp
+++ b/engines/scumm/insane/rebel2/rebel.cpp
@@ -187,6 +187,7 @@ InsaneRebel2::InsaneRebel2(ScummEngine_v7 *scumm) {
 	_playerShield = 255; // Full shields by default (255)
 	_playerLives = 3;
 	_playerScore = 0;
+	_noDamage = false;
 	_viewX = 0;
 	_viewY = 0;
 
@@ -491,6 +492,7 @@ InsaneRebel2::InsaneRebel2(ScummEngine_v7 *scumm) {
 	// Based on original debug mode (DAT_0047ab34 == 'd') from FUN_00415CF8
 	// Set to true to bypass normal unlock progression
 	_debugUnlockAll = ConfMan.getBool("rebel2_unlock_all");
+	_noDamage = ConfMan.getBool("rebel2_no_damage");
 
 	for (i = 0; i < 16; i++) {
 		// If debug unlock is enabled, unlock all chapters
@@ -1110,10 +1112,14 @@ bool InsaneRebel2::notifyEvent(const Common::Event &event) {
 			    _gameState == kStateGameplay &&
 			    _rebelHandler != 0 &&
 			    event.kbd.hasFlags(Common::KBD_SHIFT)) {
-				_playerDamage = 255;
-				_playerShield = 0;
-				debug("Rebel2: Shift+D pressed - forcing player death");
-				_vm->_smushVideoShouldFinish = true;
+				if (!_noDamage) {
+					_playerDamage = 255;
+					_playerShield = 0;
+					debug("Rebel2: Shift+D pressed - forcing player death");
+					_vm->_smushVideoShouldFinish = true;
+				} else {
+					debug("Rebel2: Shift+D pressed - no damage mode prevents forced death");
+				}
 				return true;  // Consume the event
 			}
 			break;
@@ -1311,6 +1317,17 @@ InsaneRebel2::LevelDifficultyParams InsaneRebel2::getDifficultyParams() const {
 	return kDifficultyTable[diff][lvIdx];
 }
 
+bool InsaneRebel2::applyPlayerDamage(int damage) {
+	if (_noDamage || _rebelInvulnerable || damage <= 0)
+		return false;
+
+	_playerDamage += damage;
+	if (_playerDamage > 255)
+		_playerDamage = 255;
+
+	return true;
+}
+
 // addScore -- Score system with bonus life awards (FUN_0041bf8d).
 void InsaneRebel2::addScore(int points) {
 	// Calculate bonus life threshold based on difficulty (DAT_0047a7fa)
diff --git a/engines/scumm/insane/rebel2/rebel.h b/engines/scumm/insane/rebel2/rebel.h
index 765ab1f4b22..9df2a13101b 100644
--- a/engines/scumm/insane/rebel2/rebel.h
+++ b/engines/scumm/insane/rebel2/rebel.h
@@ -149,6 +149,7 @@ public:
 	Common::String _passwordInput; // Current password input string (max 8 chars)
 	bool _chapterUnlocked[16];    // Which chapters are unlocked
 	bool _debugUnlockAll;         // Debug flag to unlock all chapters for testing
+	bool _noDamage;               // Game option: disable player damage
 
 	// Unlock all chapters for testing (debug mode)
 	// Call this to enable access to all chapters without passwords
@@ -870,6 +871,7 @@ public:
 	void applyHandler7ObstacleHit(const CollisionZone &zone, int zoneIndex);
 	void awardHandler7DodgeScore();
 	void checkHandler7ObstacleZones(uint16 &warningMask);
+	bool applyPlayerDamage(int damage);
 	bool applyHandler7WallDamage(int wallDamage);
 	void resetHandler7HorizontalVelocity(int16 velocity);
 	void checkHandler7TopBoundary(const CollisionZone &zone, int16 vMargin, int wallDamage, uint16 &warningMask);
diff --git a/engines/scumm/insane/rebel2/render.cpp b/engines/scumm/insane/rebel2/render.cpp
index 1c986483361..7a84b815bff 100644
--- a/engines/scumm/insane/rebel2/render.cpp
+++ b/engines/scumm/insane/rebel2/render.cpp
@@ -1781,15 +1781,13 @@ void InsaneRebel2::checkCollisionZones(byte *renderBitmap, int pitch, int width,
 				// Original: DAT_0047a7ec += DAT_0047e0f6[chapter * 0x242 + level * 0x22]
 				int collisionDamage = (dparams.dodgeDamage >= 0) ? dparams.dodgeDamage : 0;
 
-				if (!_rebelInvulnerable) {
-					_playerDamage += collisionDamage;
-					if (_playerDamage > 255)
-						_playerDamage = 255;
+				if (applyPlayerDamage(collisionDamage)) {
 					debug("Rebel2: COLLISION damage! zone=%d aim=(%d,%d) damage=%d total=%d",
 						i, aimX, aimY, collisionDamage, _playerDamage);
 				}
 				// Visual effect ??? FUN_00420515 (palette flash)
-				initDamageFlash();
+				if (!_noDamage)
+					initDamageFlash();
 				// TODO: FUN_0041189e sound based on collision direction
 			} else {
 				// Safely passed ??? award score bonus
@@ -1884,13 +1882,10 @@ void InsaneRebel2::applyHandler7ObstacleHit(const InsaneRebel2::CollisionZone &z
 
 	LevelDifficultyParams params = getDifficultyParams();
 	int collisionDamage = (params.dodgeDamage >= 0) ? params.dodgeDamage : 0;
-	if (!_rebelInvulnerable) {
-		_playerDamage += collisionDamage;
-		if (_playerDamage > 255)
-			_playerDamage = 255;
-	}
+	applyPlayerDamage(collisionDamage);
 	_rebelHitCounter++;
-	initDamageFlash();
+	if (!_noDamage)
+		initDamageFlash();
 	// Pan based on ship X position relative to screen center.
 	playSfx(1, 127, CLIP((_flyShipScreenX - 212) * 127 / 160, -127, 127));
 	debug("Rebel2: Handler7 Mode0/2 OBSTACLE HIT zone=%d ship=(%d,%d) damage=%d",
@@ -1946,12 +1941,10 @@ void InsaneRebel2::checkHandler7ObstacleZones(uint16 &warningMask) {
 
 bool InsaneRebel2::applyHandler7WallDamage(int wallDamage) {
 	if (_hitCooldown < 5 && !_rebelInvulnerable) {
-		_playerDamage += wallDamage;
-		if (_playerDamage > 255)
-			_playerDamage = 255;
+		const bool damageApplied = applyPlayerDamage(wallDamage);
 		_rebelHitCounter++;
 		_hitCooldown = 10;
-		return true;
+		return damageApplied;
 	}
 
 	return false;
@@ -1972,14 +1965,16 @@ void InsaneRebel2::checkHandler7TopBoundary(const InsaneRebel2::CollisionZone &z
 		int16 edgeY = (int16)((_flyShipScreenX - x1) * (y2 - y1) / (x2 - x1) + y1 + vMargin);
 		if (_flyShipScreenY < edgeY) {
 			// Ship above top wall - push down.
-			if (applyHandler7WallDamage(wallDamage)) {
+			const bool damageApplied = applyHandler7WallDamage(wallDamage);
+			if (damageApplied) {
 				debug("Rebel2: Handler7 Mode1/3 TOP WALL ship=(%d,%d) edgeY=%d damage=%d",
 					_flyShipScreenX, _flyShipScreenY, edgeY, wallDamage);
 			}
 			_spaceShotDirection = 2;  // Direction: pushed down
 			_flyShipScreenY = edgeY;  // Push-back
 			playSfx(1, 127, 0);  // CRASH.SAD, top wall -> center pan (always)
-			initDamageFlash();
+			if (!_noDamage)
+				initDamageFlash();
 		} else if (_flyShipScreenY < edgeY + 0x28) {
 			warningMask |= 4;
 		}
@@ -1996,14 +1991,16 @@ void InsaneRebel2::checkHandler7BottomBoundary(const InsaneRebel2::CollisionZone
 		_corridorBottomY = vMargin + edgeY;  // DAT_00443b10 update
 		if (edgeY < _flyShipScreenY) {
 			// Ship below bottom wall - push up.
-			if (applyHandler7WallDamage(wallDamage)) {
+			const bool damageApplied = applyHandler7WallDamage(wallDamage);
+			if (damageApplied) {
 				debug("Rebel2: Handler7 Mode1/3 BOTTOM WALL ship=(%d,%d) edgeY=%d damage=%d",
 					_flyShipScreenX, _flyShipScreenY, edgeY, wallDamage);
 			}
 			_spaceShotDirection = 3;  // Direction: pushed up
 			_flyShipScreenY = edgeY;  // Push-back
 			playSfx(1, 127, 0);  // CRASH.SAD, bottom wall -> center pan (always)
-			initDamageFlash();
+			if (!_noDamage)
+				initDamageFlash();
 		} else if (edgeY - 0x28 < _flyShipScreenY) {
 			warningMask |= 8;
 		}
@@ -2024,13 +2021,15 @@ void InsaneRebel2::checkHandler7LeftBoundary(const InsaneRebel2::CollisionZone &
 			// FUN_40E35E resets horizontal history to force immediate rightward correction.
 			resetHandler7HorizontalVelocity(127);
 
-			if (applyHandler7WallDamage(wallDamage)) {
+			const bool damageApplied = applyHandler7WallDamage(wallDamage);
+			if (damageApplied) {
 				debug("Rebel2: Handler7 Mode1/3 LEFT WALL ship=(%d,%d) edgeX=%d damage=%d",
 					_flyShipScreenX, _flyShipScreenY, edgeX, wallDamage);
 			}
 			_spaceShotDirection = 0;  // Direction: pushed right
 			playSfx(1, 127, -100);  // CRASH.SAD, left wall -> pan left (always)
-			initDamageFlash();
+			if (!_noDamage)
+				initDamageFlash();
 		}
 	}
 }
@@ -2049,13 +2048,15 @@ void InsaneRebel2::checkHandler7RightBoundary(const InsaneRebel2::CollisionZone
 			// FUN_40E35E resets horizontal history to force immediate leftward correction.
 			resetHandler7HorizontalVelocity(-127);
 
-			if (applyHandler7WallDamage(wallDamage)) {
+			const bool damageApplied = applyHandler7WallDamage(wallDamage);
+			if (damageApplied) {
 				debug("Rebel2: Handler7 Mode1/3 RIGHT WALL ship=(%d,%d) edgeX=%d damage=%d",
 					_flyShipScreenX, _flyShipScreenY, edgeX, wallDamage);
 			}
 			_spaceShotDirection = 1;  // Direction: pushed left
 			playSfx(1, 127, 100);  // CRASH.SAD, right wall -> pan right (always)
-			initDamageFlash();
+			if (!_noDamage)
+				initDamageFlash();
 		}
 	}
 }
diff --git a/engines/scumm/metaengine.cpp b/engines/scumm/metaengine.cpp
index 1bd84c7d668..8f899433929 100644
--- a/engines/scumm/metaengine.cpp
+++ b/engines/scumm/metaengine.cpp
@@ -896,6 +896,15 @@ static const ExtraGuiOption enableRebel2UnlockAll = {
 	0
 };
 
+static const ExtraGuiOption enableRebel2NoDamage = {
+	_s("No damage"),
+	_s("Disable player damage"),
+	"rebel2_no_damage",
+	false,
+	0,
+	0
+};
+
 const ExtraGuiOption enableRebel1UnlockAll = {
 	_s("Unlock all levels"),
 	_s("All levels will be available without requiring passwords"),
@@ -952,6 +961,9 @@ const ExtraGuiOptions ScummMetaEngine::getExtraGuiOptions(const Common::String &
 	if (target.empty() || guiOptions.contains(GAMEOPTION_REBEL2_UNLOCK_ALL)) {
 		options.push_back(enableRebel2UnlockAll);
 	}
+	if (target.empty() || guiOptions.contains(GAMEOPTION_REBEL2_NO_DAMAGE)) {
+		options.push_back(enableRebel2NoDamage);
+	}
 	if (target.empty() || guiOptions.contains(GAMEOPTION_REBEL1_UNLOCK_ALL)) {
 		options.push_back(enableRebel1UnlockAll);
 	}
diff --git a/engines/scumm/smush/rebel/smush_player_ra2.cpp b/engines/scumm/smush/rebel/smush_player_ra2.cpp
index b24ea74678c..222b4914574 100644
--- a/engines/scumm/smush/rebel/smush_player_ra2.cpp
+++ b/engines/scumm/smush/rebel/smush_player_ra2.cpp
@@ -45,6 +45,7 @@ namespace Scumm {
 constexpr int kRebel2LoadBufferSize = 400000;
 constexpr int kRebel2GameplaySurfaceWidth = 0x1a8;
 constexpr int kRebel2GameplaySurfaceHeight = 0x104;
+constexpr int kRebel2MusicTrackSize = 800000;
 
 bool isRebel2FullFrameDeltaCodec(int codec) {
 	return codec == SMUSH_CODEC_DELTA_BLOCKS || codec == SMUSH_CODEC_DELTA_GLYPHS;
@@ -96,6 +97,7 @@ void smushDecodeRA2Uncompressed(byte *dst, const byte *src, int left, int top,
 SmushPlayerRebel2::SmushPlayerRebel2(ScummEngine_v7 *scumm, IMuseDigital *imuseDigital, Insane *insane)
 	: SmushPlayer(scumm, imuseDigital, insane) {
 	initGamePlayerFields();
+	ra2InitAudioTrackSizes();
 }
 
 SmushPlayerRebel2::~SmushPlayerRebel2() {
@@ -157,6 +159,23 @@ void SmushPlayerRebel2::destroyGamePlayerFields() {
 	_loadBuffer = nullptr;
 }
 
+void SmushPlayerRebel2::ra2InitAudioTrackSizes() {
+	const int musicTrack = SMUSH_MAX_TRACKS - 1;
+	if (_smushNumTracks <= musicTrack || _smushTracks[musicTrack].blockSize >= kRebel2MusicTrackSize)
+		return;
+
+	uint8 *blockPtr = (uint8 *)realloc(_smushTracks[musicTrack].blockPtr, kRebel2MusicTrackSize);
+	if (!blockPtr) {
+		warning("SmushPlayerRebel2::ra2InitAudioTrackSizes: failed to allocate %d-byte music track",
+			kRebel2MusicTrackSize);
+		return;
+	}
+
+	_smushTracks[musicTrack].blockPtr = blockPtr;
+	_smushTracks[musicTrack].blockSize = kRebel2MusicTrackSize;
+	memset(_smushTracks[musicTrack].blockPtr, 127, _smushTracks[musicTrack].blockSize);
+}
+
 /**
  * RA2-specific initialization in SmushPlayer::init().
  * Re-pushes the SMUSH palette for videos that inherit it from the previous video,
diff --git a/engines/scumm/smush/rebel/smush_player_ra2.h b/engines/scumm/smush/rebel/smush_player_ra2.h
index ac4700919e8..81cb8ea62dd 100644
--- a/engines/scumm/smush/rebel/smush_player_ra2.h
+++ b/engines/scumm/smush/rebel/smush_player_ra2.h
@@ -75,6 +75,7 @@ private:
 	void ra2StoreFobjData(int codec, const byte *data, int32 dataSize,
 						  int left, int top, int width, int height);
 	void ra2HandleGost(int32 subSize, Common::SeekableReadStream &b);
+	void ra2InitAudioTrackSizes();
 
 	// LOAD chunk streaming buffer (embedded resource data)
 	byte *_loadBuffer;


Commit: 61d84074f729396526b43868bd5ae90796e70922
    https://github.com/scummvm/scummvm/commit/61d84074f729396526b43868bd5ae90796e70922
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-07T23:07:40+02:00

Commit Message:
SCUMM: RA1: make sure sound buffer is large enough

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


diff --git a/engines/scumm/smush/rebel/smush_player_ra1.cpp b/engines/scumm/smush/rebel/smush_player_ra1.cpp
index 694e947c881..55667d15056 100644
--- a/engines/scumm/smush/rebel/smush_player_ra1.cpp
+++ b/engines/scumm/smush/rebel/smush_player_ra1.cpp
@@ -47,7 +47,8 @@ enum {
 	kRA1PresentationScreenWidth = 320,
 	kRA1PresentationScreenHeight = 200,
 	kRA1PresentationWidth = kRA1PresentationScreenWidth - kRA1PresentationBorder * 2,
-	kRA1PresentationHeight = kRA1PresentationScreenHeight - kRA1PresentationBorder * 2
+	kRA1PresentationHeight = kRA1PresentationScreenHeight - kRA1PresentationBorder * 2,
+	kRebel1LongAudioTrackSize = 500000
 };
 
 static void ra1ApplyCenteredFetchPlacement(InsaneRebel1 *rebel1, int width, int height, int &left, int &top) {
@@ -127,6 +128,7 @@ static void ra1RememberDisplayedFrame(byte *&buffer, int32 &bufferSize, int &sto
 SmushPlayerRebel1::SmushPlayerRebel1(ScummEngine_v7 *scumm, IMuseDigital *imuseDigital, Insane *insane)
 	: SmushPlayer(scumm, imuseDigital, insane) {
 	initGamePlayerFields();
+	ra1InitAudioTrackSizes();
 }
 
 SmushPlayerRebel1::~SmushPlayerRebel1() {
@@ -173,6 +175,23 @@ void SmushPlayerRebel1::destroyGamePlayerFields() {
 	_ra1FadeFrameSize = 0;
 }
 
+void SmushPlayerRebel1::ra1InitAudioTrackSizes() {
+	const int longAudioTrack = SMUSH_MAX_TRACKS - 1;
+	if (_smushNumTracks <= longAudioTrack || _smushTracks[longAudioTrack].blockSize >= kRebel1LongAudioTrackSize)
+		return;
+
+	uint8 *blockPtr = (uint8 *)realloc(_smushTracks[longAudioTrack].blockPtr, kRebel1LongAudioTrackSize);
+	if (!blockPtr) {
+		warning("SmushPlayerRebel1::ra1InitAudioTrackSizes: failed to allocate %d-byte long audio track",
+			kRebel1LongAudioTrackSize);
+		return;
+	}
+
+	_smushTracks[longAudioTrack].blockPtr = blockPtr;
+	_smushTracks[longAudioTrack].blockSize = kRebel1LongAudioTrackSize;
+	memset(_smushTracks[longAudioTrack].blockPtr, 127, _smushTracks[longAudioTrack].blockSize);
+}
+
 void SmushPlayerRebel1::resetGameVideoState() {
 	_ra1HasCleanFrame = false;
 	free(_ra1ObjOverlayData);
diff --git a/engines/scumm/smush/rebel/smush_player_ra1.h b/engines/scumm/smush/rebel/smush_player_ra1.h
index 95ec9485179..65570b04539 100644
--- a/engines/scumm/smush/rebel/smush_player_ra1.h
+++ b/engines/scumm/smush/rebel/smush_player_ra1.h
@@ -71,6 +71,7 @@ private:
 	bool ra1HandleUnknownFrameChunk(uint32 subType, int32 subSize);
 	bool ra1DispatchFrameChunk(uint32 subType, int32 subSize, int32 &frameSize,
 		Common::SeekableReadStream &b, bool fastForwarding);
+	void ra1InitAudioTrackSizes();
 
 	// RA1 clean frame buffer for delta source restoration
 	byte *_ra1CleanFrame;



From noreply at scummvm.org  Sun Jun  7 21:08:11 2026
From: noreply at scummvm.org (ScummVM-Translations)
Date: Sun,  7 Jun 2026 21:08:11 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 cba34035daaf8091cbe8698e38389383e916e820
Message-ID: <20260607210811.B3BD97005CF@ron.scummvm.net>

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

Summary:
cba34035da I18N: Update translations templates


Commit: cba34035daaf8091cbe8698e38389383e916e820
    https://github.com/scummvm/scummvm/commit/cba34035daaf8091cbe8698e38389383e916e820
Author: Weblate (noreply at weblate.org)
Date: 2026-06-07T21:08:07Z

Commit Message:
I18N: Update translations templates

Changed paths:
    po/scummvm.pot


diff --git a/po/scummvm.pot b/po/scummvm.pot
index a316b4c30c2..1e240a95821 100644
--- a/po/scummvm.pot
+++ b/po/scummvm.pot
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2026.2.1git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-06 11:26+0000\n"
+"POT-Creation-Date: 2026-06-07 21:08+0000\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL at ADDRESS>\n"
 "Language-Team: LANGUAGE <LL at li.org>\n"
@@ -1014,7 +1014,7 @@ msgstr ""
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1088
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1100
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1035,7 +1035,7 @@ msgstr ""
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1055
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1067
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1056,7 +1056,7 @@ msgstr ""
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1068
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1080
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1076,7 +1076,7 @@ msgstr ""
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1075
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1087
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3589,7 +3589,7 @@ msgid "Game not implemented"
 msgstr ""
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
-#: engines/scumm/metaengine.cpp:1038
+#: engines/scumm/metaengine.cpp:1050
 msgid "Default game keymappings"
 msgstr ""
 
@@ -5435,7 +5435,7 @@ msgstr ""
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1196 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1208 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr ""
 
@@ -6796,7 +6796,7 @@ msgstr ""
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1114 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1126 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -7443,7 +7443,7 @@ msgstr ""
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1101
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1113
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -9330,7 +9330,7 @@ msgid "Player ammo will never decrease."
 msgstr ""
 
 #: engines/hypno/metaengine.cpp:70 engines/scumm/metaengine.cpp:891
-#: engines/scumm/metaengine.cpp:900
+#: engines/scumm/metaengine.cpp:909
 msgid "Unlock all levels"
 msgstr ""
 
@@ -12390,96 +12390,104 @@ msgstr ""
 msgid "Run the game in 640x400 high resolution mode instead of 320x200"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:901
+#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:910
 msgid "All levels will be available without requiring passwords"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1049
+#: engines/scumm/metaengine.cpp:900
+msgid "No damage"
+msgstr ""
+
+#: engines/scumm/metaengine.cpp:901
+msgid "Disable player damage"
+msgstr ""
+
+#: engines/scumm/metaengine.cpp:1061
 msgid "Down left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1062
+#: engines/scumm/metaengine.cpp:1074
 msgid "Down right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1082
+#: engines/scumm/metaengine.cpp:1094
 msgid "Up left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1095
+#: engines/scumm/metaengine.cpp:1107
 msgid "Up right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1107
+#: engines/scumm/metaengine.cpp:1119
 msgid "Switch weapon"
 msgstr ""
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1121
+#: engines/scumm/metaengine.cpp:1133
 msgid "Win the bike fight cheat"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1130
+#: engines/scumm/metaengine.cpp:1142
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1209
+#: engines/scumm/metaengine.cpp:1144 engines/scumm/metaengine.cpp:1221
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1214
+#: engines/scumm/metaengine.cpp:1149 engines/scumm/metaengine.cpp:1226
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1219
+#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1231
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1224
+#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1229
+#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1241
 msgid "Stick up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
+#: engines/scumm/metaengine.cpp:1171 engines/scumm/metaengine.cpp:1248
 msgid "Stick down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1243
+#: engines/scumm/metaengine.cpp:1178 engines/scumm/metaengine.cpp:1255
 msgid "Stick left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1250
+#: engines/scumm/metaengine.cpp:1185 engines/scumm/metaengine.cpp:1262
 msgid "Stick right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1257
+#: engines/scumm/metaengine.cpp:1192 engines/scumm/metaengine.cpp:1269
 msgid "Fire / select"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1186
+#: engines/scumm/metaengine.cpp:1198
 msgid "Walk / menu back"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1191
+#: engines/scumm/metaengine.cpp:1203
 msgid "Skip / menu back"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1207
+#: engines/scumm/metaengine.cpp:1219
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1262
+#: engines/scumm/metaengine.cpp:1274
 msgid "Cover / back"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1268
+#: engines/scumm/metaengine.cpp:1280
 msgid "Skip / back"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1275
+#: engines/scumm/metaengine.cpp:1287
 msgid "Skip / menu"
 msgstr ""
 



From noreply at scummvm.org  Sun Jun  7 21:08:43 2026
From: noreply at scummvm.org (ScummVM-Translations)
Date: Sun,  7 Jun 2026 21:08:43 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 a8d0c18358d07b92088f1cfae718fcf9638404c1
Message-ID: <20260607210843.D814D7005BC@ron.scummvm.net>

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

Summary:
a8d0c18358 I18N: Update translation files


Commit: a8d0c18358d07b92088f1cfae718fcf9638404c1
    https://github.com/scummvm/scummvm/commit/a8d0c18358d07b92088f1cfae718fcf9638404c1
Author: Weblate (noreply at weblate.org)
Date: 2026-06-07T21:08:24Z

Commit Message:
I18N: Update translation files

Updated by "Update PO files to match POT (msgmerge)" hook in Weblate.

Translation: ScummVM/scummvm
Translate-URL: https://translations.scummvm.org/projects/scummvm/scummvm/

Changed paths:
    po/ar.po
    po/be-tarask.po
    po/be_BY.po
    po/ca_ES.po
    po/cs_CZ.po
    po/da.po
    po/de_DE.po
    po/el.po
    po/es_ES.po
    po/eu.po
    po/fi_FI.po
    po/fr_FR.po
    po/gl_ES.po
    po/he.po
    po/hi.po
    po/hu_HU.po
    po/it_IT.po
    po/ja.po
    po/ka.po
    po/ko.po
    po/nb_NO.po
    po/nl_NL.po
    po/nn_NO.po
    po/pl_PL.po
    po/pt_BR.po
    po/pt_PT.po
    po/ro.po
    po/ru_RU.po
    po/sv_SE.po
    po/tr.po
    po/uk_UA.po
    po/zh.po
    po/zh_Hans.po
    po/zh_Hant.po


diff --git a/po/ar.po b/po/ar.po
index 49e5b161910..7a4863c1600 100644
--- a/po/ar.po
+++ b/po/ar.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-06 11:26+0000\n"
+"POT-Creation-Date: 2026-06-07 21:08+0000\n"
 "PO-Revision-Date: 2026-02-24 22:26+0000\n"
 "Last-Translator: Mohamed Shaaban <mohmedbashamedo at hotmail.com>\n"
 "Language-Team: Arabic <https://translations.scummvm.org/projects/scummvm/"
@@ -1060,7 +1060,7 @@ msgstr "??????????????"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1088
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1100
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1081,7 +1081,7 @@ msgstr "????????"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1055
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1067
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1102,7 +1102,7 @@ msgstr "????????"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1068
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1080
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1122,7 +1122,7 @@ msgstr "????????"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1075
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1087
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3912,7 +3912,7 @@ msgid "Game not implemented"
 msgstr "?????????? ???????????? ?????? ??????????"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
-#: engines/scumm/metaengine.cpp:1038
+#: engines/scumm/metaengine.cpp:1050
 #, fuzzy
 #| msgid "Default game keymap"
 msgid "Default game keymappings"
@@ -5935,7 +5935,7 @@ msgstr "R2"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1196 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1208 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "??????????????"
 
@@ -7459,7 +7459,7 @@ msgstr "?????????? ????????????"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1114 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1126 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -8225,7 +8225,7 @@ msgstr "??????????????"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1101
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1113
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -10538,7 +10538,7 @@ msgid "Player ammo will never decrease."
 msgstr "???? ?????????? ?????????? ???????????? ??????????."
 
 #: engines/hypno/metaengine.cpp:70 engines/scumm/metaengine.cpp:891
-#: engines/scumm/metaengine.cpp:900
+#: engines/scumm/metaengine.cpp:909
 msgid "Unlock all levels"
 msgstr "???????? ???????? ??????????????????"
 
@@ -14169,124 +14169,136 @@ msgstr "?????????? ?????? ??????????"
 msgid "Run the game in 640x400 high resolution mode instead of 320x200"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:901
+#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:910
 #, fuzzy
 #| msgid "All levels will be available to play."
 msgid "All levels will be available without requiring passwords"
 msgstr "?????????? ???????? ?????????????????? ?????????? ????????."
 
-#: engines/scumm/metaengine.cpp:1049
+#: engines/scumm/metaengine.cpp:900
+#, fuzzy
+#| msgid "No date saved"
+msgid "No damage"
+msgstr "???? ?????? ?????? ??????????????"
+
+#: engines/scumm/metaengine.cpp:901
+#, fuzzy
+#| msgid "Disable save menu"
+msgid "Disable player damage"
+msgstr "?????????? ?????????? ??????????"
+
+#: engines/scumm/metaengine.cpp:1061
 #, fuzzy
 #| msgid "Down Left"
 msgid "Down left"
 msgstr "???????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1062
+#: engines/scumm/metaengine.cpp:1074
 #, fuzzy
 #| msgid "Down Right"
 msgid "Down right"
 msgstr "???????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1082
+#: engines/scumm/metaengine.cpp:1094
 #, fuzzy
 #| msgid "Upper left item"
 msgid "Up left"
 msgstr "???????????? ???????????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1095
+#: engines/scumm/metaengine.cpp:1107
 #, fuzzy
 #| msgid "Up Right"
 msgid "Up right"
 msgstr "???????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1107
+#: engines/scumm/metaengine.cpp:1119
 msgid "Switch weapon"
 msgstr "?????????? ????????????"
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1121
+#: engines/scumm/metaengine.cpp:1133
 msgid "Win the bike fight cheat"
 msgstr "?????? ???????? ???? ?????????? ??????????????"
 
-#: engines/scumm/metaengine.cpp:1130
+#: engines/scumm/metaengine.cpp:1142
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1209
+#: engines/scumm/metaengine.cpp:1144 engines/scumm/metaengine.cpp:1221
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1214
+#: engines/scumm/metaengine.cpp:1149 engines/scumm/metaengine.cpp:1226
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1219
+#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1231
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1224
+#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1229
+#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1241
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "??????????"
 
-#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
+#: engines/scumm/metaengine.cpp:1171 engines/scumm/metaengine.cpp:1248
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "?????? ???????????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1243
+#: engines/scumm/metaengine.cpp:1178 engines/scumm/metaengine.cpp:1255
 #, fuzzy
 #| msgid "Slide Left"
 msgid "Stick left"
 msgstr "?????????????? ?????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1250
+#: engines/scumm/metaengine.cpp:1185 engines/scumm/metaengine.cpp:1262
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick right"
 msgstr "???????????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1257
+#: engines/scumm/metaengine.cpp:1192 engines/scumm/metaengine.cpp:1269
 #, fuzzy
 #| msgid "Move left"
 msgid "Fire / select"
 msgstr "???????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1186
+#: engines/scumm/metaengine.cpp:1198
 #, fuzzy
 #| msgid "Open main menu"
 msgid "Walk / menu back"
 msgstr "???????? ?????????????? ????????????????"
 
-#: engines/scumm/metaengine.cpp:1191
+#: engines/scumm/metaengine.cpp:1203
 #, fuzzy
 #| msgid "Open main menu"
 msgid "Skip / menu back"
 msgstr "???????? ?????????????? ????????????????"
 
-#: engines/scumm/metaengine.cpp:1207
+#: engines/scumm/metaengine.cpp:1219
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1262
+#: engines/scumm/metaengine.cpp:1274
 #, fuzzy
 #| msgid "Move Back"
 msgid "Cover / back"
 msgstr "???????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1268
+#: engines/scumm/metaengine.cpp:1280
 #, fuzzy
 #| msgid "Skip dialogue"
 msgid "Skip / back"
 msgstr "???????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1275
+#: engines/scumm/metaengine.cpp:1287
 #, fuzzy
 #| msgid "Open main menu"
 msgid "Skip / menu"
diff --git a/po/be-tarask.po b/po/be-tarask.po
index 12945442165..e7e9a491e83 100644
--- a/po/be-tarask.po
+++ b/po/be-tarask.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-06 11:26+0000\n"
+"POT-Creation-Date: 2026-06-07 21:08+0000\n"
 "PO-Revision-Date: 2023-02-26 15:01+0000\n"
 "Last-Translator: Kastu?? K. <oakum2334 at gmail.com>\n"
 "Language-Team: Belarusian (Tara??kievica) <https://translations.scummvm.org/"
@@ -1071,7 +1071,7 @@ msgstr "????????????????????"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1088
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1100
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1092,7 +1092,7 @@ msgstr "??????????"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1055
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1067
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1113,7 +1113,7 @@ msgstr "????????"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1068
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1080
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1133,7 +1133,7 @@ msgstr "????????????"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1075
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1087
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3868,7 +3868,7 @@ msgid "Game not implemented"
 msgstr "ID ???????????? ???? ??????????????????????????????"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
-#: engines/scumm/metaengine.cpp:1038
+#: engines/scumm/metaengine.cpp:1050
 #, fuzzy
 #| msgid "Default game keymap"
 msgid "Default game keymappings"
@@ -5868,7 +5868,7 @@ msgstr "R2"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1196 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1208 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "????????"
 
@@ -7401,7 +7401,7 @@ msgstr "???????????????????? ?????????? ??????"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1114 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1126 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -8172,7 +8172,7 @@ msgstr "????????????????????"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1101
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1113
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -10493,7 +10493,7 @@ msgid "Player ammo will never decrease."
 msgstr "?????????? ???????????? ???????????? ???? ????????????????????."
 
 #: engines/hypno/metaengine.cpp:70 engines/scumm/metaengine.cpp:891
-#: engines/scumm/metaengine.cpp:900
+#: engines/scumm/metaengine.cpp:909
 msgid "Unlock all levels"
 msgstr ""
 
@@ -14100,120 +14100,132 @@ msgstr "?????????????? ???????????????????????? ??????????????????"
 msgid "Run the game in 640x400 high resolution mode instead of 320x200"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:901
+#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:910
 msgid "All levels will be available without requiring passwords"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1049
+#: engines/scumm/metaengine.cpp:900
+#, fuzzy
+#| msgid "No date saved"
+msgid "No damage"
+msgstr "???????? ???? ??????????????????"
+
+#: engines/scumm/metaengine.cpp:901
+#, fuzzy
+#| msgid "Disable save menu"
+msgid "Disable player damage"
+msgstr "???????????????? ???????? ????????????????????"
+
+#: engines/scumm/metaengine.cpp:1061
 #, fuzzy
 msgid "Down left"
 msgstr "?????????????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1062
+#: engines/scumm/metaengine.cpp:1074
 #, fuzzy
 msgid "Down right"
 msgstr "?????????????? ??????????????"
 
-#: engines/scumm/metaengine.cpp:1082
+#: engines/scumm/metaengine.cpp:1094
 #, fuzzy
 #| msgid "Upper left item"
 msgid "Up left"
 msgstr "???????????? ?????????? ??????"
 
-#: engines/scumm/metaengine.cpp:1095
+#: engines/scumm/metaengine.cpp:1107
 #, fuzzy
 msgid "Up right"
 msgstr "??????????????"
 
-#: engines/scumm/metaengine.cpp:1107
+#: engines/scumm/metaengine.cpp:1119
 #, fuzzy
 msgid "Switch weapon"
 msgstr "????????????????????"
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1121
+#: engines/scumm/metaengine.cpp:1133
 msgid "Win the bike fight cheat"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1130
+#: engines/scumm/metaengine.cpp:1142
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1209
+#: engines/scumm/metaengine.cpp:1144 engines/scumm/metaengine.cpp:1221
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1214
+#: engines/scumm/metaengine.cpp:1149 engines/scumm/metaengine.cpp:1226
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1219
+#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1231
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1224
+#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1229
+#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1241
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "??????????????"
 
-#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
+#: engines/scumm/metaengine.cpp:1171 engines/scumm/metaengine.cpp:1248
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "?????????????????????????? ????????"
 
-#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1243
+#: engines/scumm/metaengine.cpp:1178 engines/scumm/metaengine.cpp:1255
 #, fuzzy
 #| msgid "Slide Left"
 msgid "Stick left"
 msgstr "?????????????????????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1250
+#: engines/scumm/metaengine.cpp:1185 engines/scumm/metaengine.cpp:1262
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick right"
 msgstr "?????????????????????? ??????????????"
 
-#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1257
+#: engines/scumm/metaengine.cpp:1192 engines/scumm/metaengine.cpp:1269
 #, fuzzy
 #| msgid "Move left"
 msgid "Fire / select"
 msgstr "?????????????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1186
+#: engines/scumm/metaengine.cpp:1198
 #, fuzzy
 #| msgid "Open main menu"
 msgid "Walk / menu back"
 msgstr "?????????????? ???????????????? ????????"
 
-#: engines/scumm/metaengine.cpp:1191
+#: engines/scumm/metaengine.cpp:1203
 #, fuzzy
 #| msgid "Open main menu"
 msgid "Skip / menu back"
 msgstr "?????????????? ???????????????? ????????"
 
-#: engines/scumm/metaengine.cpp:1207
+#: engines/scumm/metaengine.cpp:1219
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1262
+#: engines/scumm/metaengine.cpp:1274
 #, fuzzy
 #| msgid "Move Back"
 msgid "Cover / back"
 msgstr "?????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1268
+#: engines/scumm/metaengine.cpp:1280
 #, fuzzy
 #| msgid "Skip dialogue"
 msgid "Skip / back"
 msgstr "?????????????????????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1275
+#: engines/scumm/metaengine.cpp:1287
 #, fuzzy
 #| msgid "Open main menu"
 msgid "Skip / menu"
diff --git a/po/be_BY.po b/po/be_BY.po
index c1de8f156ac..3c7079c3750 100644
--- a/po/be_BY.po
+++ b/po/be_BY.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-06 11:26+0000\n"
+"POT-Creation-Date: 2026-06-07 21:08+0000\n"
 "PO-Revision-Date: 2021-03-21 08:24+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Belarusian <https://translations.scummvm.org/projects/scummvm/"
@@ -1074,7 +1074,7 @@ msgstr "??????????????????????????"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1088
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1100
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1095,7 +1095,7 @@ msgstr "??????????"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1055
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1067
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1116,7 +1116,7 @@ msgstr "????????"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1068
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1080
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1136,7 +1136,7 @@ msgstr "????????????"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1075
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1087
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3873,7 +3873,7 @@ msgid "Game not implemented"
 msgstr "Game id ???? ??????????????????????????"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
-#: engines/scumm/metaengine.cpp:1038
+#: engines/scumm/metaengine.cpp:1050
 #, fuzzy
 #| msgid "Default game keymap"
 msgid "Default game keymappings"
@@ -5877,7 +5877,7 @@ msgstr "R2"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1196 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1208 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "????????"
 
@@ -7382,7 +7382,7 @@ msgstr "?????????????????????? ??????"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1114 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1126 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -8137,7 +8137,7 @@ msgstr "??????????????????????????"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1101
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1113
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -10392,7 +10392,7 @@ msgid "Player ammo will never decrease."
 msgstr ""
 
 #: engines/hypno/metaengine.cpp:70 engines/scumm/metaengine.cpp:891
-#: engines/scumm/metaengine.cpp:900
+#: engines/scumm/metaengine.cpp:909
 msgid "Unlock all levels"
 msgstr ""
 
@@ -13910,119 +13910,130 @@ msgstr "???????????????? ?????????????????????????? ?????????????? ????????????
 msgid "Run the game in 640x400 high resolution mode instead of 320x200"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:901
+#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:910
 msgid "All levels will be available without requiring passwords"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1049
+#: engines/scumm/metaengine.cpp:900
+#, fuzzy
+#| msgid "No date saved"
+msgid "No damage"
+msgstr "???????? ???? ????????????????"
+
+#: engines/scumm/metaengine.cpp:901
+#, fuzzy
+msgid "Disable player damage"
+msgstr "???? ???????????????????? ???????????????? ????????????"
+
+#: engines/scumm/metaengine.cpp:1061
 #, fuzzy
 msgid "Down left"
 msgstr "?????????????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1062
+#: engines/scumm/metaengine.cpp:1074
 #, fuzzy
 msgid "Down right"
 msgstr "?????????????? ??????????????"
 
-#: engines/scumm/metaengine.cpp:1082
+#: engines/scumm/metaengine.cpp:1094
 #, fuzzy
 #| msgid "Upper left item"
 msgid "Up left"
 msgstr "???????????? ???????? ??????????????"
 
-#: engines/scumm/metaengine.cpp:1095
+#: engines/scumm/metaengine.cpp:1107
 #, fuzzy
 msgid "Up right"
 msgstr "??????????????"
 
-#: engines/scumm/metaengine.cpp:1107
+#: engines/scumm/metaengine.cpp:1119
 #, fuzzy
 msgid "Switch weapon"
 msgstr "?????????? ??????????:"
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1121
+#: engines/scumm/metaengine.cpp:1133
 msgid "Win the bike fight cheat"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1130
+#: engines/scumm/metaengine.cpp:1142
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1209
+#: engines/scumm/metaengine.cpp:1144 engines/scumm/metaengine.cpp:1221
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1214
+#: engines/scumm/metaengine.cpp:1149 engines/scumm/metaengine.cpp:1226
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1219
+#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1231
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1224
+#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1229
+#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1241
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "??????????????"
 
-#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
+#: engines/scumm/metaengine.cpp:1171 engines/scumm/metaengine.cpp:1248
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "???????????????????? ????????"
 
-#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1243
+#: engines/scumm/metaengine.cpp:1178 engines/scumm/metaengine.cpp:1255
 #, fuzzy
 #| msgid "Slide Left"
 msgid "Stick left"
 msgstr "???????????????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1250
+#: engines/scumm/metaengine.cpp:1185 engines/scumm/metaengine.cpp:1262
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick right"
 msgstr "???????????????? ??????????????"
 
-#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1257
+#: engines/scumm/metaengine.cpp:1192 engines/scumm/metaengine.cpp:1269
 #, fuzzy
 #| msgid "Move left"
 msgid "Fire / select"
 msgstr "?????????????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1186
+#: engines/scumm/metaengine.cpp:1198
 #, fuzzy
 #| msgid "Open main menu"
 msgid "Walk / menu back"
 msgstr "?????????????? ???????????????? ????????"
 
-#: engines/scumm/metaengine.cpp:1191
+#: engines/scumm/metaengine.cpp:1203
 #, fuzzy
 #| msgid "Open main menu"
 msgid "Skip / menu back"
 msgstr "?????????????? ???????????????? ????????"
 
-#: engines/scumm/metaengine.cpp:1207
+#: engines/scumm/metaengine.cpp:1219
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1262
+#: engines/scumm/metaengine.cpp:1274
 #, fuzzy
 #| msgid "Move Back"
 msgid "Cover / back"
 msgstr "???????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1268
+#: engines/scumm/metaengine.cpp:1280
 #, fuzzy
 msgid "Skip / back"
 msgstr "???????????????????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1275
+#: engines/scumm/metaengine.cpp:1287
 #, fuzzy
 #| msgid "Open main menu"
 msgid "Skip / menu"
diff --git a/po/ca_ES.po b/po/ca_ES.po
index 455f5b1aee4..77ddf5eeb1a 100644
--- a/po/ca_ES.po
+++ b/po/ca_ES.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.6.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-06 11:26+0000\n"
+"POT-Creation-Date: 2026-06-07 21:08+0000\n"
 "PO-Revision-Date: 2026-05-26 04:22+0000\n"
 "Last-Translator: Xaviu <xaviu at xaviu.com>\n"
 "Language-Team: Catalan <https://translations.scummvm.org/projects/scummvm/"
@@ -1057,7 +1057,7 @@ msgstr "Interaccionar"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1088
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1100
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1078,7 +1078,7 @@ msgstr "Amunt"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1055
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1067
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1099,7 +1099,7 @@ msgstr "Avall"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1068
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1080
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1119,7 +1119,7 @@ msgstr "Esquerra"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1075
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1087
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3796,7 +3796,7 @@ msgid "Game not implemented"
 msgstr "Joc no implementat"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
-#: engines/scumm/metaengine.cpp:1038
+#: engines/scumm/metaengine.cpp:1050
 msgid "Default game keymappings"
 msgstr "Assignaci?? de tecles per defecte del joc"
 
@@ -5679,7 +5679,7 @@ msgstr "R2"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1196 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1208 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "Men??"
 
@@ -7144,7 +7144,7 @@ msgstr "Activa / desactiva el combat"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1114 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1126 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -7812,7 +7812,7 @@ msgstr "Parla / Interactua"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1101
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1113
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -9781,7 +9781,7 @@ msgid "Player ammo will never decrease."
 msgstr "La munici?? del jugador mai disminuir??."
 
 #: engines/hypno/metaengine.cpp:70 engines/scumm/metaengine.cpp:891
-#: engines/scumm/metaengine.cpp:900
+#: engines/scumm/metaengine.cpp:909
 msgid "Unlock all levels"
 msgstr "Desbloqueja tots els nivells"
 
@@ -12981,116 +12981,128 @@ msgstr "Alta resoluci?? activada"
 msgid "Run the game in 640x400 high resolution mode instead of 320x200"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:901
+#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:910
 #, fuzzy
 #| msgid "All levels will be available to play."
 msgid "All levels will be available without requiring passwords"
 msgstr "Tots els nivells estaran disponibles per jugar."
 
-#: engines/scumm/metaengine.cpp:1049
+#: engines/scumm/metaengine.cpp:900
+#, fuzzy
+#| msgid "No date saved"
+msgid "No damage"
+msgstr "No hi ha data desada"
+
+#: engines/scumm/metaengine.cpp:901
+#, fuzzy
+#| msgid "Disable save menu"
+msgid "Disable player damage"
+msgstr "Desactiva el men?? de desar"
+
+#: engines/scumm/metaengine.cpp:1061
 msgid "Down left"
 msgstr "A baix a l'esquerra"
 
-#: engines/scumm/metaengine.cpp:1062
+#: engines/scumm/metaengine.cpp:1074
 msgid "Down right"
 msgstr "A baix a la dreta"
 
-#: engines/scumm/metaengine.cpp:1082
+#: engines/scumm/metaengine.cpp:1094
 msgid "Up left"
 msgstr "Amunt a l'esquerra"
 
-#: engines/scumm/metaengine.cpp:1095
+#: engines/scumm/metaengine.cpp:1107
 msgid "Up right"
 msgstr "Amunt a la dreta"
 
-#: engines/scumm/metaengine.cpp:1107
+#: engines/scumm/metaengine.cpp:1119
 msgid "Switch weapon"
 msgstr "Canvia d'arma"
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1121
+#: engines/scumm/metaengine.cpp:1133
 msgid "Win the bike fight cheat"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1130
+#: engines/scumm/metaengine.cpp:1142
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1209
+#: engines/scumm/metaengine.cpp:1144 engines/scumm/metaengine.cpp:1221
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1214
+#: engines/scumm/metaengine.cpp:1149 engines/scumm/metaengine.cpp:1226
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1219
+#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1231
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1224
+#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1229
+#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1241
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "Recull"
 
-#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
+#: engines/scumm/metaengine.cpp:1171 engines/scumm/metaengine.cpp:1248
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "Despla??ament avall"
 
-#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1243
+#: engines/scumm/metaengine.cpp:1178 engines/scumm/metaengine.cpp:1255
 #, fuzzy
 #| msgid "Slide left"
 msgid "Stick left"
 msgstr "Llisca cap a l'esquerra"
 
-#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1250
+#: engines/scumm/metaengine.cpp:1185 engines/scumm/metaengine.cpp:1262
 #, fuzzy
 #| msgid "Slide right"
 msgid "Stick right"
 msgstr "Llisca cap a la dreta"
 
-#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1257
+#: engines/scumm/metaengine.cpp:1192 engines/scumm/metaengine.cpp:1269
 #, fuzzy
 #| msgid "Move / Select"
 msgid "Fire / select"
 msgstr "Mou / Selecciona"
 
-#: engines/scumm/metaengine.cpp:1186
+#: engines/scumm/metaengine.cpp:1198
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Walk / menu back"
 msgstr "Omet el men?? principal"
 
-#: engines/scumm/metaengine.cpp:1191
+#: engines/scumm/metaengine.cpp:1203
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu back"
 msgstr "Omet el men?? principal"
 
-#: engines/scumm/metaengine.cpp:1207
+#: engines/scumm/metaengine.cpp:1219
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1262
+#: engines/scumm/metaengine.cpp:1274
 #, fuzzy
 #| msgid "Move back"
 msgid "Cover / back"
 msgstr "Mou enrere"
 
-#: engines/scumm/metaengine.cpp:1268
+#: engines/scumm/metaengine.cpp:1280
 #, fuzzy
 #| msgid "Skip walk"
 msgid "Skip / back"
 msgstr "Omet el despla??ament"
 
-#: engines/scumm/metaengine.cpp:1275
+#: engines/scumm/metaengine.cpp:1287
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu"
diff --git a/po/cs_CZ.po b/po/cs_CZ.po
index cfbbdb780e5..65e96289db6 100644
--- a/po/cs_CZ.po
+++ b/po/cs_CZ.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.7.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-06 11:26+0000\n"
+"POT-Creation-Date: 2026-06-07 21:08+0000\n"
 "PO-Revision-Date: 2026-02-09 20:22+0000\n"
 "Last-Translator: AsciiWolf <mail at asciiwolf.com>\n"
 "Language-Team: Czech <https://translations.scummvm.org/projects/scummvm/"
@@ -1074,7 +1074,7 @@ msgstr "Interakce"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1088
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1100
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1095,7 +1095,7 @@ msgstr "Nahoru"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1055
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1067
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1116,7 +1116,7 @@ msgstr "Dol??"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1068
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1080
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1136,7 +1136,7 @@ msgstr "Doleva"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1075
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1087
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3861,7 +3861,7 @@ msgid "Game not implemented"
 msgstr "Id hry nen?? podporov??no"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
-#: engines/scumm/metaengine.cpp:1038
+#: engines/scumm/metaengine.cpp:1050
 #, fuzzy
 #| msgid "Default game keymap"
 msgid "Default game keymappings"
@@ -5845,7 +5845,7 @@ msgstr ""
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1196 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1208 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "Menu"
 
@@ -7336,7 +7336,7 @@ msgstr "Povolit zachycov??n?? my??i"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1114 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1126 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -8081,7 +8081,7 @@ msgstr "Interakce"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1101
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1113
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -10281,7 +10281,7 @@ msgid "Player ammo will never decrease."
 msgstr ""
 
 #: engines/hypno/metaengine.cpp:70 engines/scumm/metaengine.cpp:891
-#: engines/scumm/metaengine.cpp:900
+#: engines/scumm/metaengine.cpp:909
 msgid "Unlock all levels"
 msgstr ""
 
@@ -13720,115 +13720,126 @@ msgstr "Povolit grafiku ve vysok??m rozli??en??"
 msgid "Run the game in 640x400 high resolution mode instead of 320x200"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:901
+#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:910
 msgid "All levels will be available without requiring passwords"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1049
+#: engines/scumm/metaengine.cpp:900
+#, fuzzy
+#| msgid "No date saved"
+msgid "No damage"
+msgstr "Neulo??ena ????dn?? data"
+
+#: engines/scumm/metaengine.cpp:901
+#, fuzzy
+msgid "Disable player damage"
+msgstr "Nelze ulo??it hru"
+
+#: engines/scumm/metaengine.cpp:1061
 #, fuzzy
 msgid "Down left"
 msgstr "Oto??it se doleva"
 
-#: engines/scumm/metaengine.cpp:1062
+#: engines/scumm/metaengine.cpp:1074
 #, fuzzy
 msgid "Down right"
 msgstr "Oto??it se doprava"
 
-#: engines/scumm/metaengine.cpp:1082
+#: engines/scumm/metaengine.cpp:1094
 #, fuzzy
 #| msgid "Upper left item"
 msgid "Up left"
 msgstr "Polo??ka vlevo naho??e"
 
-#: engines/scumm/metaengine.cpp:1095
+#: engines/scumm/metaengine.cpp:1107
 #, fuzzy
 msgid "Up right"
 msgstr "Doprava"
 
-#: engines/scumm/metaengine.cpp:1107
+#: engines/scumm/metaengine.cpp:1119
 #, fuzzy
 msgid "Switch weapon"
 msgstr "M??n??n?? postav:"
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1121
+#: engines/scumm/metaengine.cpp:1133
 msgid "Win the bike fight cheat"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1130
+#: engines/scumm/metaengine.cpp:1142
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1209
+#: engines/scumm/metaengine.cpp:1144 engines/scumm/metaengine.cpp:1221
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1214
+#: engines/scumm/metaengine.cpp:1149 engines/scumm/metaengine.cpp:1226
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1219
+#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1231
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1224
+#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1229
+#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1241
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "Sebrat"
 
-#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
+#: engines/scumm/metaengine.cpp:1171 engines/scumm/metaengine.cpp:1248
 #, fuzzy
 msgid "Stick down"
 msgstr "Posunout seznam dolu"
 
-#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1243
+#: engines/scumm/metaengine.cpp:1178 engines/scumm/metaengine.cpp:1255
 #, fuzzy
 #| msgid "Slide Left"
 msgid "Stick left"
 msgstr "P??esunout se Doleva"
 
-#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1250
+#: engines/scumm/metaengine.cpp:1185 engines/scumm/metaengine.cpp:1262
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick right"
 msgstr "P??esunout se Doprava"
 
-#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1257
+#: engines/scumm/metaengine.cpp:1192 engines/scumm/metaengine.cpp:1269
 #, fuzzy
 msgid "Fire / select"
 msgstr "P??esunout se doleva"
 
-#: engines/scumm/metaengine.cpp:1186
+#: engines/scumm/metaengine.cpp:1198
 #, fuzzy
 msgid "Walk / menu back"
 msgstr "~N~??vrat do Spou??t????e"
 
-#: engines/scumm/metaengine.cpp:1191
+#: engines/scumm/metaengine.cpp:1203
 #, fuzzy
 msgid "Skip / menu back"
 msgstr "~N~??vrat do Spou??t????e"
 
-#: engines/scumm/metaengine.cpp:1207
+#: engines/scumm/metaengine.cpp:1219
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1262
+#: engines/scumm/metaengine.cpp:1274
 #, fuzzy
 #| msgid "Move Back"
 msgid "Cover / back"
 msgstr "Vzad"
 
-#: engines/scumm/metaengine.cpp:1268
+#: engines/scumm/metaengine.cpp:1280
 #, fuzzy
 msgid "Skip / back"
 msgstr "P??esko??it ????dek"
 
-#: engines/scumm/metaengine.cpp:1275
+#: engines/scumm/metaengine.cpp:1287
 #, fuzzy
 msgid "Skip / menu"
 msgstr "~N~??vrat do Spou??t????e"
diff --git a/po/da.po b/po/da.po
index 3929298f883..623a5c3b8ac 100644
--- a/po/da.po
+++ b/po/da.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-06 11:26+0000\n"
+"POT-Creation-Date: 2026-06-07 21:08+0000\n"
 "PO-Revision-Date: 2023-09-07 17:04+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Danish <https://translations.scummvm.org/projects/scummvm/"
@@ -1077,7 +1077,7 @@ msgstr "interagere"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1088
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1100
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1098,7 +1098,7 @@ msgstr "Op"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1055
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1067
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1119,7 +1119,7 @@ msgstr "Ned"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1068
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1080
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1139,7 +1139,7 @@ msgstr "Venstre"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1075
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1087
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3872,7 +3872,7 @@ msgid "Game not implemented"
 msgstr "Spil-id ikke underst??ttet"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
-#: engines/scumm/metaengine.cpp:1038
+#: engines/scumm/metaengine.cpp:1050
 #, fuzzy
 msgid "Default game keymappings"
 msgstr "~I~ndstillinger ..."
@@ -5864,7 +5864,7 @@ msgstr ""
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1196 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1208 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "Menu"
 
@@ -7363,7 +7363,7 @@ msgstr "Museindfangning til/fra"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1114 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1126 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -8108,7 +8108,7 @@ msgstr "interagere"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1101
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1113
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -10302,7 +10302,7 @@ msgid "Player ammo will never decrease."
 msgstr ""
 
 #: engines/hypno/metaengine.cpp:70 engines/scumm/metaengine.cpp:891
-#: engines/scumm/metaengine.cpp:900
+#: engines/scumm/metaengine.cpp:909
 msgid "Unlock all levels"
 msgstr ""
 
@@ -13811,115 +13811,126 @@ msgstr "Aktiv??r grafik i h??j opl??sning"
 msgid "Run the game in 640x400 high resolution mode instead of 320x200"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:901
+#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:910
 msgid "All levels will be available without requiring passwords"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1049
+#: engines/scumm/metaengine.cpp:900
+#, fuzzy
+#| msgid "No date saved"
+msgid "No damage"
+msgstr "Ingen dato gemt"
+
+#: engines/scumm/metaengine.cpp:901
+#, fuzzy
+msgid "Disable player damage"
+msgstr "Kunne ikke gemme spil"
+
+#: engines/scumm/metaengine.cpp:1061
 #, fuzzy
 msgid "Down left"
 msgstr "Drej til venstre"
 
-#: engines/scumm/metaengine.cpp:1062
+#: engines/scumm/metaengine.cpp:1074
 #, fuzzy
 msgid "Down right"
 msgstr "Drej til h??jre"
 
-#: engines/scumm/metaengine.cpp:1082
+#: engines/scumm/metaengine.cpp:1094
 #, fuzzy
 #| msgid "Upper left item"
 msgid "Up left"
 msgstr "??verste venstre punkt"
 
-#: engines/scumm/metaengine.cpp:1095
+#: engines/scumm/metaengine.cpp:1107
 #, fuzzy
 msgid "Up right"
 msgstr "H??jre"
 
-#: engines/scumm/metaengine.cpp:1107
+#: engines/scumm/metaengine.cpp:1119
 #, fuzzy
 msgid "Switch weapon"
 msgstr "Skifter karaktere:"
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1121
+#: engines/scumm/metaengine.cpp:1133
 msgid "Win the bike fight cheat"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1130
+#: engines/scumm/metaengine.cpp:1142
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1209
+#: engines/scumm/metaengine.cpp:1144 engines/scumm/metaengine.cpp:1221
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1214
+#: engines/scumm/metaengine.cpp:1149 engines/scumm/metaengine.cpp:1226
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1219
+#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1231
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1224
+#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1229
+#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1241
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "Tag op"
 
-#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
+#: engines/scumm/metaengine.cpp:1171 engines/scumm/metaengine.cpp:1248
 #, fuzzy
 msgid "Stick down"
 msgstr "Rul liste ned"
 
-#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1243
+#: engines/scumm/metaengine.cpp:1178 engines/scumm/metaengine.cpp:1255
 #, fuzzy
 #| msgid "Slide Left"
 msgid "Stick left"
 msgstr "Flyt til venstre"
 
-#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1250
+#: engines/scumm/metaengine.cpp:1185 engines/scumm/metaengine.cpp:1262
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick right"
 msgstr "Flyt til h??jre"
 
-#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1257
+#: engines/scumm/metaengine.cpp:1192 engines/scumm/metaengine.cpp:1269
 #, fuzzy
 msgid "Fire / select"
 msgstr "Flyt til venstre"
 
-#: engines/scumm/metaengine.cpp:1186
+#: engines/scumm/metaengine.cpp:1198
 #, fuzzy
 msgid "Walk / menu back"
 msgstr "~R~etur til oversigt"
 
-#: engines/scumm/metaengine.cpp:1191
+#: engines/scumm/metaengine.cpp:1203
 #, fuzzy
 msgid "Skip / menu back"
 msgstr "~R~etur til oversigt"
 
-#: engines/scumm/metaengine.cpp:1207
+#: engines/scumm/metaengine.cpp:1219
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1262
+#: engines/scumm/metaengine.cpp:1274
 #, fuzzy
 #| msgid "Move Back"
 msgid "Cover / back"
 msgstr "Flyt bagud"
 
-#: engines/scumm/metaengine.cpp:1268
+#: engines/scumm/metaengine.cpp:1280
 #, fuzzy
 msgid "Skip / back"
 msgstr "Spring linje over"
 
-#: engines/scumm/metaengine.cpp:1275
+#: engines/scumm/metaengine.cpp:1287
 #, fuzzy
 msgid "Skip / menu"
 msgstr "~R~etur til oversigt"
diff --git a/po/de_DE.po b/po/de_DE.po
index 3472d71570d..32c37bdc113 100644
--- a/po/de_DE.po
+++ b/po/de_DE.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.10.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-06 11:26+0000\n"
+"POT-Creation-Date: 2026-06-07 21:08+0000\n"
 "PO-Revision-Date: 2026-05-05 16:31+0000\n"
 "Last-Translator: Lothar Serra Mari <lothar.serramari at scummvm.org>\n"
 "Language-Team: German <https://translations.scummvm.org/projects/scummvm/"
@@ -1064,7 +1064,7 @@ msgstr "Interagieren"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1088
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1100
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1085,7 +1085,7 @@ msgstr "Hoch"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1055
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1067
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1106,7 +1106,7 @@ msgstr "Runter"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1068
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1080
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1126,7 +1126,7 @@ msgstr "Links"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1075
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1087
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3929,7 +3929,7 @@ msgid "Game not implemented"
 msgstr "Spiel nicht implementiert"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
-#: engines/scumm/metaengine.cpp:1038
+#: engines/scumm/metaengine.cpp:1050
 msgid "Default game keymappings"
 msgstr "Standard-Tastenbelegung f??r Spiele"
 
@@ -6144,7 +6144,7 @@ msgstr "R2"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1196 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1208 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "Men??"
 
@@ -7627,7 +7627,7 @@ msgstr "Kampf umschalten"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1114 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1126 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -8294,7 +8294,7 @@ msgstr "Sprechen / Interagieren"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1101
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1113
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -10296,7 +10296,7 @@ msgid "Player ammo will never decrease."
 msgstr "Die Munition des Spielers wird sich nie verringern."
 
 #: engines/hypno/metaengine.cpp:70 engines/scumm/metaengine.cpp:891
-#: engines/scumm/metaengine.cpp:900
+#: engines/scumm/metaengine.cpp:909
 msgid "Unlock all levels"
 msgstr "Alle Level freischalten"
 
@@ -13712,116 +13712,128 @@ msgstr "Hohe Aufl??sung eingeschaltet"
 msgid "Run the game in 640x400 high resolution mode instead of 320x200"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:901
+#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:910
 #, fuzzy
 #| msgid "All levels will be available to play."
 msgid "All levels will be available without requiring passwords"
 msgstr "Alle Level k??nnen direkt gespielt werden."
 
-#: engines/scumm/metaengine.cpp:1049
+#: engines/scumm/metaengine.cpp:900
+#, fuzzy
+#| msgid "No date saved"
+msgid "No damage"
+msgstr "Kein Datum gespeichert"
+
+#: engines/scumm/metaengine.cpp:901
+#, fuzzy
+#| msgid "Disable save menu"
+msgid "Disable player damage"
+msgstr "Speichermen?? deaktivieren"
+
+#: engines/scumm/metaengine.cpp:1061
 msgid "Down left"
 msgstr "Unten links"
 
-#: engines/scumm/metaengine.cpp:1062
+#: engines/scumm/metaengine.cpp:1074
 msgid "Down right"
 msgstr "Unten rechts"
 
-#: engines/scumm/metaengine.cpp:1082
+#: engines/scumm/metaengine.cpp:1094
 msgid "Up left"
 msgstr "Oben links"
 
-#: engines/scumm/metaengine.cpp:1095
+#: engines/scumm/metaengine.cpp:1107
 msgid "Up right"
 msgstr "Oben rechts"
 
-#: engines/scumm/metaengine.cpp:1107
+#: engines/scumm/metaengine.cpp:1119
 msgid "Switch weapon"
 msgstr "Waffe wechseln"
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1121
+#: engines/scumm/metaengine.cpp:1133
 msgid "Win the bike fight cheat"
 msgstr "Cheat: Bike-Kampf gewinnen"
 
-#: engines/scumm/metaengine.cpp:1130
+#: engines/scumm/metaengine.cpp:1142
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1209
+#: engines/scumm/metaengine.cpp:1144 engines/scumm/metaengine.cpp:1221
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1214
+#: engines/scumm/metaengine.cpp:1149 engines/scumm/metaengine.cpp:1226
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1219
+#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1231
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1224
+#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1229
+#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1241
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "Nimm"
 
-#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
+#: engines/scumm/metaengine.cpp:1171 engines/scumm/metaengine.cpp:1248
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "Nach unten scrollen"
 
-#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1243
+#: engines/scumm/metaengine.cpp:1178 engines/scumm/metaengine.cpp:1255
 #, fuzzy
 #| msgid "Slide left"
 msgid "Stick left"
 msgstr "Slide nach links"
 
-#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1250
+#: engines/scumm/metaengine.cpp:1185 engines/scumm/metaengine.cpp:1262
 #, fuzzy
 #| msgid "Slide right"
 msgid "Stick right"
 msgstr "Slide nach rechts"
 
-#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1257
+#: engines/scumm/metaengine.cpp:1192 engines/scumm/metaengine.cpp:1269
 #, fuzzy
 #| msgid "Move / Select"
 msgid "Fire / select"
 msgstr "Bewegen / Ausw??hlen"
 
-#: engines/scumm/metaengine.cpp:1186
+#: engines/scumm/metaengine.cpp:1198
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Walk / menu back"
 msgstr "Hauptmen?? ??berspringen"
 
-#: engines/scumm/metaengine.cpp:1191
+#: engines/scumm/metaengine.cpp:1203
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu back"
 msgstr "Hauptmen?? ??berspringen"
 
-#: engines/scumm/metaengine.cpp:1207
+#: engines/scumm/metaengine.cpp:1219
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1262
+#: engines/scumm/metaengine.cpp:1274
 #, fuzzy
 #| msgid "Move back"
 msgid "Cover / back"
 msgstr "Zur??ckgehen"
 
-#: engines/scumm/metaengine.cpp:1268
+#: engines/scumm/metaengine.cpp:1280
 #, fuzzy
 #| msgid "Skip walk"
 msgid "Skip / back"
 msgstr "Spaziergang ??berspringen"
 
-#: engines/scumm/metaengine.cpp:1275
+#: engines/scumm/metaengine.cpp:1287
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu"
diff --git a/po/el.po b/po/el.po
index 094cc35897c..e5eb847103d 100644
--- a/po/el.po
+++ b/po/el.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.10.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-06 11:26+0000\n"
+"POT-Creation-Date: 2026-06-07 21:08+0000\n"
 "PO-Revision-Date: 2026-04-23 00:22+0000\n"
 "Last-Translator: Antoniou Athanasios <a.antoniou79 at gmail.com>\n"
 "Language-Team: Greek <https://translations.scummvm.org/projects/scummvm/"
@@ -1067,7 +1067,7 @@ msgstr "??????????????????????????"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1088
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1100
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1088,7 +1088,7 @@ msgstr "????????"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1055
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1067
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1109,7 +1109,7 @@ msgstr "????????"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1068
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1080
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1129,7 +1129,7 @@ msgstr "????????????????"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1075
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1087
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -4000,7 +4000,7 @@ msgid "Game not implemented"
 msgstr "???? ???????????????? ?????? ???????? ????????????????????"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
-#: engines/scumm/metaengine.cpp:1038
+#: engines/scumm/metaengine.cpp:1050
 msgid "Default game keymappings"
 msgstr "?????????????? ???????????????????????????? ???????????????? ????????????????????"
 
@@ -6268,7 +6268,7 @@ msgstr "R2"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1196 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1208 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "??????????"
 
@@ -7755,7 +7755,7 @@ msgstr "???????????????? ?????????????????????? ?????? ????????"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1114 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1126 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -8429,7 +8429,7 @@ msgstr "???????? / ??????????????????????????"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1101
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1113
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -10425,7 +10425,7 @@ msgid "Player ammo will never decrease."
 msgstr "???? ???????????????????? ?????? ???????????? ?????? ???? ???????????????? ????????."
 
 #: engines/hypno/metaengine.cpp:70 engines/scumm/metaengine.cpp:891
-#: engines/scumm/metaengine.cpp:900
+#: engines/scumm/metaengine.cpp:909
 msgid "Unlock all levels"
 msgstr "???????????????????? ???????? ?????? ????????????????"
 
@@ -13867,116 +13867,128 @@ msgstr "???????????????????????? ???????????? ????????????????"
 msgid "Run the game in 640x400 high resolution mode instead of 320x200"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:901
+#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:910
 #, fuzzy
 #| msgid "All levels will be available to play."
 msgid "All levels will be available without requiring passwords"
 msgstr "?????? ???? ?????????????? ???? ???????????? ?????????????????? ?????? ????????????????."
 
-#: engines/scumm/metaengine.cpp:1049
+#: engines/scumm/metaengine.cpp:900
+#, fuzzy
+#| msgid "No date saved"
+msgid "No damage"
+msgstr "?????????? ????????????. ????/??????"
+
+#: engines/scumm/metaengine.cpp:901
+#, fuzzy
+#| msgid "Disable save menu"
+msgid "Disable player damage"
+msgstr "???????????????????????????? ?????????? ??????????????????????"
+
+#: engines/scumm/metaengine.cpp:1061
 msgid "Down left"
 msgstr "???????? ????????????????"
 
-#: engines/scumm/metaengine.cpp:1062
+#: engines/scumm/metaengine.cpp:1074
 msgid "Down right"
 msgstr "???????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1082
+#: engines/scumm/metaengine.cpp:1094
 msgid "Up left"
 msgstr "???????? ????????????????"
 
-#: engines/scumm/metaengine.cpp:1095
+#: engines/scumm/metaengine.cpp:1107
 msgid "Up right"
 msgstr "???????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1107
+#: engines/scumm/metaengine.cpp:1119
 msgid "Switch weapon"
 msgstr "???????????? ??????????"
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1121
+#: engines/scumm/metaengine.cpp:1133
 msgid "Win the bike fight cheat"
 msgstr "?????????? ?????? ???????? ?????? ???????? ???? ?????? ??????????????"
 
-#: engines/scumm/metaengine.cpp:1130
+#: engines/scumm/metaengine.cpp:1142
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1209
+#: engines/scumm/metaengine.cpp:1144 engines/scumm/metaengine.cpp:1221
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1214
+#: engines/scumm/metaengine.cpp:1149 engines/scumm/metaengine.cpp:1226
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1219
+#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1231
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1224
+#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1229
+#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1241
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "????????????"
 
-#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
+#: engines/scumm/metaengine.cpp:1171 engines/scumm/metaengine.cpp:1248
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "???????????? ????????"
 
-#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1243
+#: engines/scumm/metaengine.cpp:1178 engines/scumm/metaengine.cpp:1255
 #, fuzzy
 #| msgid "Slide left"
 msgid "Stick left"
 msgstr "???????????????? ????????????????"
 
-#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1250
+#: engines/scumm/metaengine.cpp:1185 engines/scumm/metaengine.cpp:1262
 #, fuzzy
 #| msgid "Slide right"
 msgid "Stick right"
 msgstr "???????????????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1257
+#: engines/scumm/metaengine.cpp:1192 engines/scumm/metaengine.cpp:1269
 #, fuzzy
 #| msgid "Move / Select"
 msgid "Fire / select"
 msgstr "???????????? / ??????????????"
 
-#: engines/scumm/metaengine.cpp:1186
+#: engines/scumm/metaengine.cpp:1198
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Walk / menu back"
 msgstr "?????????????????? ?????????????????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1191
+#: engines/scumm/metaengine.cpp:1203
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu back"
 msgstr "?????????????????? ?????????????????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1207
+#: engines/scumm/metaengine.cpp:1219
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1262
+#: engines/scumm/metaengine.cpp:1274
 #, fuzzy
 #| msgid "Move Back"
 msgid "Cover / back"
 msgstr "?????????????????? ????????"
 
-#: engines/scumm/metaengine.cpp:1268
+#: engines/scumm/metaengine.cpp:1280
 #, fuzzy
 #| msgid "Skip walk"
 msgid "Skip / back"
 msgstr "?????????????????? ??????????????????"
 
-#: engines/scumm/metaengine.cpp:1275
+#: engines/scumm/metaengine.cpp:1287
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu"
diff --git a/po/es_ES.po b/po/es_ES.po
index 2f377329644..a06406f61c0 100644
--- a/po/es_ES.po
+++ b/po/es_ES.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.4.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-06 11:26+0000\n"
+"POT-Creation-Date: 2026-06-07 21:08+0000\n"
 "PO-Revision-Date: 2026-06-03 08:30+0000\n"
 "Last-Translator: IlDucci <pajaroloco_2 at hotmail.com>\n"
 "Language-Team: Spanish <https://translations.scummvm.org/projects/scummvm/"
@@ -1052,7 +1052,7 @@ msgstr "Interactuar"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1088
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1100
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1073,7 +1073,7 @@ msgstr "Arriba"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1055
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1067
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1094,7 +1094,7 @@ msgstr "Abajo"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1068
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1080
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1114,7 +1114,7 @@ msgstr "Izquierda"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1075
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1087
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3932,7 +3932,7 @@ msgid "Game not implemented"
 msgstr "Juego no implementado"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
-#: engines/scumm/metaengine.cpp:1038
+#: engines/scumm/metaengine.cpp:1050
 msgid "Default game keymappings"
 msgstr "Asignaciones predet. del juego"
 
@@ -6166,7 +6166,7 @@ msgstr "R2"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1196 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1208 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "Men??"
 
@@ -7642,7 +7642,7 @@ msgstr "Entrar/Salir del modo de combate"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1114 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1126 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -8311,7 +8311,7 @@ msgstr "Hablar/Interactuar"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1101
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1113
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -10302,7 +10302,7 @@ msgid "Player ammo will never decrease."
 msgstr "La munici??n del jugador no se reducir?? nunca."
 
 #: engines/hypno/metaengine.cpp:70 engines/scumm/metaengine.cpp:891
-#: engines/scumm/metaengine.cpp:900
+#: engines/scumm/metaengine.cpp:909
 msgid "Unlock all levels"
 msgstr "Desbloquear todos los niveles"
 
@@ -13687,98 +13687,110 @@ msgstr ""
 "Muestra el juego en el modo de alta resoluci??n a 640??400 p??xeles, en vez de "
 "a 320??200"
 
-#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:901
+#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:910
 msgid "All levels will be available without requiring passwords"
 msgstr "Desbloquea todos los niveles sin necesitar contrase??as"
 
-#: engines/scumm/metaengine.cpp:1049
+#: engines/scumm/metaengine.cpp:900
+#, fuzzy
+#| msgid "No date saved"
+msgid "No damage"
+msgstr "No hay fecha guardada"
+
+#: engines/scumm/metaengine.cpp:901
+#, fuzzy
+#| msgid "Disable save menu"
+msgid "Disable player damage"
+msgstr "Desactivar men?? de guardado"
+
+#: engines/scumm/metaengine.cpp:1061
 msgid "Down left"
 msgstr "Bajar a la izquierda"
 
-#: engines/scumm/metaengine.cpp:1062
+#: engines/scumm/metaengine.cpp:1074
 msgid "Down right"
 msgstr "Bajar a la derecha"
 
-#: engines/scumm/metaengine.cpp:1082
+#: engines/scumm/metaengine.cpp:1094
 msgid "Up left"
 msgstr "Subir a la izquierda"
 
-#: engines/scumm/metaengine.cpp:1095
+#: engines/scumm/metaengine.cpp:1107
 msgid "Up right"
 msgstr "Subir a la derecha"
 
-#: engines/scumm/metaengine.cpp:1107
+#: engines/scumm/metaengine.cpp:1119
 msgid "Switch weapon"
 msgstr "Cambiar de arma"
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1121
+#: engines/scumm/metaengine.cpp:1133
 msgid "Win the bike fight cheat"
 msgstr "Truco para ganar pelea de motos"
 
-#: engines/scumm/metaengine.cpp:1130
+#: engines/scumm/metaengine.cpp:1142
 msgid "Rebel Assault controls"
 msgstr "Controles de Rebel Assault"
 
-#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1209
+#: engines/scumm/metaengine.cpp:1144 engines/scumm/metaengine.cpp:1221
 msgid "Aim up / menu up"
 msgstr "Apuntar arriba/Subir men??"
 
-#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1214
+#: engines/scumm/metaengine.cpp:1149 engines/scumm/metaengine.cpp:1226
 msgid "Aim down / menu down"
 msgstr "Apuntar abajo/Bajar men??"
 
-#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1219
+#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1231
 msgid "Aim left / menu left"
 msgstr "Apuntar a izda./Men?? a izda."
 
-#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1224
+#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
 msgid "Aim right / menu right"
 msgstr "Apuntar a dcha./Men?? a dcha."
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1229
+#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1241
 msgid "Stick up"
 msgstr "Stick hacia arriba"
 
-#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
+#: engines/scumm/metaengine.cpp:1171 engines/scumm/metaengine.cpp:1248
 msgid "Stick down"
 msgstr "Stick hacia abajo"
 
-#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1243
+#: engines/scumm/metaengine.cpp:1178 engines/scumm/metaengine.cpp:1255
 msgid "Stick left"
 msgstr "Stick hacia la izquierda"
 
-#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1250
+#: engines/scumm/metaengine.cpp:1185 engines/scumm/metaengine.cpp:1262
 msgid "Stick right"
 msgstr "Stick hacia la derecha"
 
-#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1257
+#: engines/scumm/metaengine.cpp:1192 engines/scumm/metaengine.cpp:1269
 msgid "Fire / select"
 msgstr "Disparar/Seleccionar"
 
-#: engines/scumm/metaengine.cpp:1186
+#: engines/scumm/metaengine.cpp:1198
 #, fuzzy
 #| msgid "Skip / menu back"
 msgid "Walk / menu back"
 msgstr "Omitir/Volver (men??s)"
 
-#: engines/scumm/metaengine.cpp:1191
+#: engines/scumm/metaengine.cpp:1203
 msgid "Skip / menu back"
 msgstr "Omitir/Volver (men??s)"
 
-#: engines/scumm/metaengine.cpp:1207
+#: engines/scumm/metaengine.cpp:1219
 msgid "Rebel Assault II controls"
 msgstr "Controles de Rebel Assault II"
 
-#: engines/scumm/metaengine.cpp:1262
+#: engines/scumm/metaengine.cpp:1274
 msgid "Cover / back"
 msgstr "Cubrirse/Volver"
 
-#: engines/scumm/metaengine.cpp:1268
+#: engines/scumm/metaengine.cpp:1280
 msgid "Skip / back"
 msgstr "Omitir/Volver"
 
-#: engines/scumm/metaengine.cpp:1275
+#: engines/scumm/metaengine.cpp:1287
 msgid "Skip / menu"
 msgstr "Omitir/Men??"
 
diff --git a/po/eu.po b/po/eu.po
index 50b277da5ee..1d65c241bb4 100644
--- a/po/eu.po
+++ b/po/eu.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.5.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-06 11:26+0000\n"
+"POT-Creation-Date: 2026-06-07 21:08+0000\n"
 "PO-Revision-Date: 2024-02-05 18:42+0000\n"
 "Last-Translator: Josu Igoa <josuigoa at ni.eus>\n"
 "Language-Team: Basque <https://translations.scummvm.org/projects/scummvm/"
@@ -1044,7 +1044,7 @@ msgstr "Elkarreragin"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1088
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1100
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1065,7 +1065,7 @@ msgstr "Gora"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1055
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1067
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1086,7 +1086,7 @@ msgstr "Behera"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1068
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1080
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1106,7 +1106,7 @@ msgstr "Ezker"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1075
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1087
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3898,7 +3898,7 @@ msgid "Game not implemented"
 msgstr "Jokoaren ID ez-onartua"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
-#: engines/scumm/metaengine.cpp:1038
+#: engines/scumm/metaengine.cpp:1050
 #, fuzzy
 msgid "Default game keymappings"
 msgstr "~A~ukerak..."
@@ -5919,7 +5919,7 @@ msgstr ""
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1196 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1208 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "Menua"
 
@@ -7409,7 +7409,7 @@ msgstr "Mututu/Ez-mututu"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1114 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1126 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -8156,7 +8156,7 @@ msgstr "Elkarreragin"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1101
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1113
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -10374,7 +10374,7 @@ msgid "Player ammo will never decrease."
 msgstr ""
 
 #: engines/hypno/metaengine.cpp:70 engines/scumm/metaengine.cpp:891
-#: engines/scumm/metaengine.cpp:900
+#: engines/scumm/metaengine.cpp:909
 msgid "Unlock all levels"
 msgstr ""
 
@@ -13778,115 +13778,126 @@ msgstr "Gaitu erresoluzio altuko grafikoak"
 msgid "Run the game in 640x400 high resolution mode instead of 320x200"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:901
+#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:910
 msgid "All levels will be available without requiring passwords"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1049
+#: engines/scumm/metaengine.cpp:900
+#, fuzzy
+#| msgid "No date saved"
+msgid "No damage"
+msgstr "Ez dago datarik gordeta"
+
+#: engines/scumm/metaengine.cpp:901
+#, fuzzy
+msgid "Disable player damage"
+msgstr "Ezin izan da jokoa gorde"
+
+#: engines/scumm/metaengine.cpp:1061
 #, fuzzy
 msgid "Down left"
 msgstr "Ezkerrera biratu"
 
-#: engines/scumm/metaengine.cpp:1062
+#: engines/scumm/metaengine.cpp:1074
 #, fuzzy
 msgid "Down right"
 msgstr "Eskuinera biratu"
 
-#: engines/scumm/metaengine.cpp:1082
+#: engines/scumm/metaengine.cpp:1094
 #, fuzzy
 #| msgid "Upper left item"
 msgid "Up left"
 msgstr "Goiko ezkerreko objektua"
 
-#: engines/scumm/metaengine.cpp:1095
+#: engines/scumm/metaengine.cpp:1107
 #, fuzzy
 msgid "Up right"
 msgstr "Eskuin"
 
-#: engines/scumm/metaengine.cpp:1107
+#: engines/scumm/metaengine.cpp:1119
 #, fuzzy
 msgid "Switch weapon"
 msgstr "Pertsonaia aldatu:"
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1121
+#: engines/scumm/metaengine.cpp:1133
 msgid "Win the bike fight cheat"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1130
+#: engines/scumm/metaengine.cpp:1142
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1209
+#: engines/scumm/metaengine.cpp:1144 engines/scumm/metaengine.cpp:1221
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1214
+#: engines/scumm/metaengine.cpp:1149 engines/scumm/metaengine.cpp:1226
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1219
+#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1231
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1224
+#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1229
+#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1241
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "Jaso"
 
-#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
+#: engines/scumm/metaengine.cpp:1171 engines/scumm/metaengine.cpp:1248
 #, fuzzy
 msgid "Stick down"
 msgstr "Behera"
 
-#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1243
+#: engines/scumm/metaengine.cpp:1178 engines/scumm/metaengine.cpp:1255
 #, fuzzy
 #| msgid "Slide Left"
 msgid "Stick left"
 msgstr "Ezkerrera irristatu"
 
-#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1250
+#: engines/scumm/metaengine.cpp:1185 engines/scumm/metaengine.cpp:1262
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick right"
 msgstr "Eskuinera irristatu"
 
-#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1257
+#: engines/scumm/metaengine.cpp:1192 engines/scumm/metaengine.cpp:1269
 #, fuzzy
 msgid "Fire / select"
 msgstr "Ezkerrera irristatu"
 
-#: engines/scumm/metaengine.cpp:1186
+#: engines/scumm/metaengine.cpp:1198
 #, fuzzy
 msgid "Walk / menu back"
 msgstr "It~z~uli abiarazlera"
 
-#: engines/scumm/metaengine.cpp:1191
+#: engines/scumm/metaengine.cpp:1203
 #, fuzzy
 msgid "Skip / menu back"
 msgstr "It~z~uli abiarazlera"
 
-#: engines/scumm/metaengine.cpp:1207
+#: engines/scumm/metaengine.cpp:1219
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1262
+#: engines/scumm/metaengine.cpp:1274
 #, fuzzy
 #| msgid "Move Back"
 msgid "Cover / back"
 msgstr "Atzera mugitu"
 
-#: engines/scumm/metaengine.cpp:1268
+#: engines/scumm/metaengine.cpp:1280
 #, fuzzy
 msgid "Skip / back"
 msgstr "Lerroa saltatu"
 
-#: engines/scumm/metaengine.cpp:1275
+#: engines/scumm/metaengine.cpp:1287
 #, fuzzy
 msgid "Skip / menu"
 msgstr "It~z~uli abiarazlera"
diff --git a/po/fi_FI.po b/po/fi_FI.po
index daa57d5207d..b0620b6b570 100644
--- a/po/fi_FI.po
+++ b/po/fi_FI.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.6.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-06 11:26+0000\n"
+"POT-Creation-Date: 2026-06-07 21:08+0000\n"
 "PO-Revision-Date: 2026-02-07 15:22+0000\n"
 "Last-Translator: Timo Mikkolainen <tmikkola at gmail.com>\n"
 "Language-Team: Finnish <https://translations.scummvm.org/projects/scummvm/"
@@ -1052,7 +1052,7 @@ msgstr "K??yt??"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1088
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1100
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1073,7 +1073,7 @@ msgstr "Yl??s"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1055
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1067
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1094,7 +1094,7 @@ msgstr "Alas"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1068
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1080
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1114,7 +1114,7 @@ msgstr "Vasen"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1075
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1087
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3950,7 +3950,7 @@ msgid "Game not implemented"
 msgstr "Pelill?? ei ole toteutusta"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
-#: engines/scumm/metaengine.cpp:1038
+#: engines/scumm/metaengine.cpp:1050
 #, fuzzy
 #| msgid "Default game keymap"
 msgid "Default game keymappings"
@@ -6263,7 +6263,7 @@ msgstr "R2"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1196 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1208 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "Valikko"
 
@@ -7725,7 +7725,7 @@ msgstr "Taistelumoodi p????lle / pois"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1114 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1126 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -8423,7 +8423,7 @@ msgstr "Puhu / Vuorovaikuta"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1101
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1113
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -10498,7 +10498,7 @@ msgid "Player ammo will never decrease."
 msgstr "Pelaajan ammukset eiv??t v??hene."
 
 #: engines/hypno/metaengine.cpp:70 engines/scumm/metaengine.cpp:891
-#: engines/scumm/metaengine.cpp:900
+#: engines/scumm/metaengine.cpp:909
 msgid "Unlock all levels"
 msgstr "Avaa kaikki tasot"
 
@@ -13912,116 +13912,128 @@ msgstr "K??yt?? korkeaa resoluutiota"
 msgid "Run the game in 640x400 high resolution mode instead of 320x200"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:901
+#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:910
 #, fuzzy
 #| msgid "All levels will be available to play."
 msgid "All levels will be available without requiring passwords"
 msgstr "Kaikki tasot ovat pelattavissa."
 
-#: engines/scumm/metaengine.cpp:1049
+#: engines/scumm/metaengine.cpp:900
+#, fuzzy
+#| msgid "No date saved"
+msgid "No damage"
+msgstr "P??iv??yst?? ei ole tallennettu"
+
+#: engines/scumm/metaengine.cpp:901
+#, fuzzy
+#| msgid "Disable save menu"
+msgid "Disable player damage"
+msgstr "Tallennusvalikko pois p????lt??"
+
+#: engines/scumm/metaengine.cpp:1061
 msgid "Down left"
 msgstr "Alavasemmalle"
 
-#: engines/scumm/metaengine.cpp:1062
+#: engines/scumm/metaengine.cpp:1074
 msgid "Down right"
 msgstr "Alaoikealle"
 
-#: engines/scumm/metaengine.cpp:1082
+#: engines/scumm/metaengine.cpp:1094
 msgid "Up left"
 msgstr "Yl??vasemmalle"
 
-#: engines/scumm/metaengine.cpp:1095
+#: engines/scumm/metaengine.cpp:1107
 msgid "Up right"
 msgstr "Yl??oikealle"
 
-#: engines/scumm/metaengine.cpp:1107
+#: engines/scumm/metaengine.cpp:1119
 msgid "Switch weapon"
 msgstr "Vaihda asetta"
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1121
+#: engines/scumm/metaengine.cpp:1133
 msgid "Win the bike fight cheat"
 msgstr "Voita moottoripy??r??taistelu-huijaus"
 
-#: engines/scumm/metaengine.cpp:1130
+#: engines/scumm/metaengine.cpp:1142
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1209
+#: engines/scumm/metaengine.cpp:1144 engines/scumm/metaengine.cpp:1221
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1214
+#: engines/scumm/metaengine.cpp:1149 engines/scumm/metaengine.cpp:1226
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1219
+#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1231
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1224
+#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1229
+#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1241
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "Ota"
 
-#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
+#: engines/scumm/metaengine.cpp:1171 engines/scumm/metaengine.cpp:1248
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "Vierit?? alas"
 
-#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1243
+#: engines/scumm/metaengine.cpp:1178 engines/scumm/metaengine.cpp:1255
 #, fuzzy
 #| msgid "Slide left"
 msgid "Stick left"
 msgstr "Liu'u vasemmalle"
 
-#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1250
+#: engines/scumm/metaengine.cpp:1185 engines/scumm/metaengine.cpp:1262
 #, fuzzy
 #| msgid "Slide right"
 msgid "Stick right"
 msgstr "Liu'u oikealle"
 
-#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1257
+#: engines/scumm/metaengine.cpp:1192 engines/scumm/metaengine.cpp:1269
 #, fuzzy
 #| msgid "Move / Select"
 msgid "Fire / select"
 msgstr "Liiku / Valitse"
 
-#: engines/scumm/metaengine.cpp:1186
+#: engines/scumm/metaengine.cpp:1198
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Walk / menu back"
 msgstr "Ohita p????valikko"
 
-#: engines/scumm/metaengine.cpp:1191
+#: engines/scumm/metaengine.cpp:1203
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu back"
 msgstr "Ohita p????valikko"
 
-#: engines/scumm/metaengine.cpp:1207
+#: engines/scumm/metaengine.cpp:1219
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1262
+#: engines/scumm/metaengine.cpp:1274
 #, fuzzy
 #| msgid "Move back"
 msgid "Cover / back"
 msgstr "Liiku taaksep??in"
 
-#: engines/scumm/metaengine.cpp:1268
+#: engines/scumm/metaengine.cpp:1280
 #, fuzzy
 #| msgid "Skip walk"
 msgid "Skip / back"
 msgstr "Ohita k??vely"
 
-#: engines/scumm/metaengine.cpp:1275
+#: engines/scumm/metaengine.cpp:1287
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu"
diff --git a/po/fr_FR.po b/po/fr_FR.po
index e40a60aab58..eb4d1cf0ae9 100644
--- a/po/fr_FR.po
+++ b/po/fr_FR.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-06 11:26+0000\n"
+"POT-Creation-Date: 2026-06-07 21:08+0000\n"
 "PO-Revision-Date: 2026-06-07 13:22+0000\n"
 "Last-Translator: Purple T <ZEONK at hotmail.com>\n"
 "Language-Team: French <https://translations.scummvm.org/projects/scummvm/"
@@ -1063,7 +1063,7 @@ msgstr "Interagir"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1088
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1100
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1084,7 +1084,7 @@ msgstr "Haut"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1055
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1067
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1105,7 +1105,7 @@ msgstr "Bas"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1068
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1080
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1125,7 +1125,7 @@ msgstr "Gauche"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1075
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1087
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3955,7 +3955,7 @@ msgid "Game not implemented"
 msgstr "Jeu non impl??ment??"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
-#: engines/scumm/metaengine.cpp:1038
+#: engines/scumm/metaengine.cpp:1050
 msgid "Default game keymappings"
 msgstr "Conf. de touches par d??faut"
 
@@ -6181,7 +6181,7 @@ msgstr "R2"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1196 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1208 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "Menu"
 
@@ -7647,7 +7647,7 @@ msgstr "Act. / D??sact. combat"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1114 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1126 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -8311,7 +8311,7 @@ msgstr "Parler / Interagir"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1101
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1113
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -10303,7 +10303,7 @@ msgid "Player ammo will never decrease."
 msgstr "La quantit?? de munitions ne baissera jamais."
 
 #: engines/hypno/metaengine.cpp:70 engines/scumm/metaengine.cpp:891
-#: engines/scumm/metaengine.cpp:900
+#: engines/scumm/metaengine.cpp:909
 msgid "Unlock all levels"
 msgstr "D??bloquer tous les niveaux"
 
@@ -13712,96 +13712,108 @@ msgstr "Mode Haute r??solution"
 msgid "Run the game in 640x400 high resolution mode instead of 320x200"
 msgstr "Lance le jeu en 640x400 haute r??solution au lieu de 320x200"
 
-#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:901
+#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:910
 msgid "All levels will be available without requiring passwords"
 msgstr "Tous les niveaux seront disponibles sans demander de mots de passe"
 
-#: engines/scumm/metaengine.cpp:1049
+#: engines/scumm/metaengine.cpp:900
+#, fuzzy
+#| msgid "No date saved"
+msgid "No damage"
+msgstr "Date inconnue"
+
+#: engines/scumm/metaengine.cpp:901
+#, fuzzy
+#| msgid "Disable save menu"
+msgid "Disable player damage"
+msgstr "D??sactiver le menu sauvegarde"
+
+#: engines/scumm/metaengine.cpp:1061
 msgid "Down left"
 msgstr "Bas gauche"
 
-#: engines/scumm/metaengine.cpp:1062
+#: engines/scumm/metaengine.cpp:1074
 msgid "Down right"
 msgstr "Bas droite"
 
-#: engines/scumm/metaengine.cpp:1082
+#: engines/scumm/metaengine.cpp:1094
 msgid "Up left"
 msgstr "Haut gauche"
 
-#: engines/scumm/metaengine.cpp:1095
+#: engines/scumm/metaengine.cpp:1107
 msgid "Up right"
 msgstr "Haut droite"
 
-#: engines/scumm/metaengine.cpp:1107
+#: engines/scumm/metaengine.cpp:1119
 msgid "Switch weapon"
 msgstr "Changer d'arme"
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1121
+#: engines/scumm/metaengine.cpp:1133
 msgid "Win the bike fight cheat"
 msgstr "Gagner les bagarres en b??cane (cheat)"
 
-#: engines/scumm/metaengine.cpp:1130
+#: engines/scumm/metaengine.cpp:1142
 msgid "Rebel Assault controls"
 msgstr "Contr??les de Rebel Assault"
 
-#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1209
+#: engines/scumm/metaengine.cpp:1144 engines/scumm/metaengine.cpp:1221
 msgid "Aim up / menu up"
 msgstr "Vis??e haut / Menu haut"
 
-#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1214
+#: engines/scumm/metaengine.cpp:1149 engines/scumm/metaengine.cpp:1226
 msgid "Aim down / menu down"
 msgstr "Vis??e bas / Menu bas"
 
-#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1219
+#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1231
 msgid "Aim left / menu left"
 msgstr "Vis??e gauche / menu gauche"
 
-#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1224
+#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
 msgid "Aim right / menu right"
 msgstr "Vis??e droite / Menu droite"
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1229
+#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1241
 msgid "Stick up"
 msgstr "Manche haut"
 
-#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
+#: engines/scumm/metaengine.cpp:1171 engines/scumm/metaengine.cpp:1248
 msgid "Stick down"
 msgstr "Manche bas"
 
-#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1243
+#: engines/scumm/metaengine.cpp:1178 engines/scumm/metaengine.cpp:1255
 msgid "Stick left"
 msgstr "Manche gauche"
 
-#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1250
+#: engines/scumm/metaengine.cpp:1185 engines/scumm/metaengine.cpp:1262
 msgid "Stick right"
 msgstr "Manche droite"
 
-#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1257
+#: engines/scumm/metaengine.cpp:1192 engines/scumm/metaengine.cpp:1269
 msgid "Fire / select"
 msgstr "Tirer / S??lectionner"
 
-#: engines/scumm/metaengine.cpp:1186
+#: engines/scumm/metaengine.cpp:1198
 msgid "Walk / menu back"
 msgstr "Marcher / menu retour"
 
-#: engines/scumm/metaengine.cpp:1191
+#: engines/scumm/metaengine.cpp:1203
 msgid "Skip / menu back"
 msgstr "Passer / menu retour"
 
-#: engines/scumm/metaengine.cpp:1207
+#: engines/scumm/metaengine.cpp:1219
 msgid "Rebel Assault II controls"
 msgstr "Contr??les de Rebel Assault II"
 
-#: engines/scumm/metaengine.cpp:1262
+#: engines/scumm/metaengine.cpp:1274
 msgid "Cover / back"
 msgstr "Couverture / retour"
 
-#: engines/scumm/metaengine.cpp:1268
+#: engines/scumm/metaengine.cpp:1280
 msgid "Skip / back"
 msgstr "Passer / retour"
 
-#: engines/scumm/metaengine.cpp:1275
+#: engines/scumm/metaengine.cpp:1287
 msgid "Skip / menu"
 msgstr "Passer / menu"
 
diff --git a/po/gl_ES.po b/po/gl_ES.po
index 2212038d996..730c1dd582b 100644
--- a/po/gl_ES.po
+++ b/po/gl_ES.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-06 11:26+0000\n"
+"POT-Creation-Date: 2026-06-07 21:08+0000\n"
 "PO-Revision-Date: 2020-09-26 21:13+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Galician <https://translations.scummvm.org/projects/scummvm/"
@@ -1070,7 +1070,7 @@ msgstr "Interpolaci??n:"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1088
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1100
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1091,7 +1091,7 @@ msgstr "Arriba"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1055
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1067
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1112,7 +1112,7 @@ msgstr "Abaixo"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1068
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1080
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1132,7 +1132,7 @@ msgstr "Esquerda"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1075
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1087
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3859,7 +3859,7 @@ msgid "Game not implemented"
 msgstr "ID de xogo non compatible"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
-#: engines/scumm/metaengine.cpp:1038
+#: engines/scumm/metaengine.cpp:1050
 #, fuzzy
 msgid "Default game keymappings"
 msgstr "~O~pci??ns..."
@@ -5853,7 +5853,7 @@ msgstr ""
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1196 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1208 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "Men??"
 
@@ -7342,7 +7342,7 @@ msgstr "Activar/desactivar captura de rato"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1114 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1126 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -8088,7 +8088,7 @@ msgstr "Interpolaci??n:"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1101
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1113
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -10281,7 +10281,7 @@ msgid "Player ammo will never decrease."
 msgstr ""
 
 #: engines/hypno/metaengine.cpp:70 engines/scumm/metaengine.cpp:891
-#: engines/scumm/metaengine.cpp:900
+#: engines/scumm/metaengine.cpp:909
 msgid "Unlock all levels"
 msgstr ""
 
@@ -13734,115 +13734,126 @@ msgstr "Activar gr??ficos de alta resoluci??n"
 msgid "Run the game in 640x400 high resolution mode instead of 320x200"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:901
+#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:910
 msgid "All levels will be available without requiring passwords"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1049
+#: engines/scumm/metaengine.cpp:900
+#, fuzzy
+#| msgid "No date saved"
+msgid "No damage"
+msgstr "Non hai data gardada"
+
+#: engines/scumm/metaengine.cpp:901
+#, fuzzy
+msgid "Disable player damage"
+msgstr "Erro ao gardar a partida"
+
+#: engines/scumm/metaengine.cpp:1061
 #, fuzzy
 msgid "Down left"
 msgstr "Xirar ?? esquerda"
 
-#: engines/scumm/metaengine.cpp:1062
+#: engines/scumm/metaengine.cpp:1074
 #, fuzzy
 msgid "Down right"
 msgstr "Xirar ?? dereita"
 
-#: engines/scumm/metaengine.cpp:1082
+#: engines/scumm/metaengine.cpp:1094
 #, fuzzy
 #| msgid "Upper left item"
 msgid "Up left"
 msgstr "Obxecto esquerdo arriba"
 
-#: engines/scumm/metaengine.cpp:1095
+#: engines/scumm/metaengine.cpp:1107
 #, fuzzy
 msgid "Up right"
 msgstr "Dereita"
 
-#: engines/scumm/metaengine.cpp:1107
+#: engines/scumm/metaengine.cpp:1119
 #, fuzzy
 msgid "Switch weapon"
 msgstr "Cambiar personaxes:"
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1121
+#: engines/scumm/metaengine.cpp:1133
 msgid "Win the bike fight cheat"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1130
+#: engines/scumm/metaengine.cpp:1142
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1209
+#: engines/scumm/metaengine.cpp:1144 engines/scumm/metaengine.cpp:1221
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1214
+#: engines/scumm/metaengine.cpp:1149 engines/scumm/metaengine.cpp:1226
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1219
+#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1231
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1224
+#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1229
+#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1241
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "Coller"
 
-#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
+#: engines/scumm/metaengine.cpp:1171 engines/scumm/metaengine.cpp:1248
 #, fuzzy
 msgid "Stick down"
 msgstr "Baixar lista"
 
-#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1243
+#: engines/scumm/metaengine.cpp:1178 engines/scumm/metaengine.cpp:1255
 #, fuzzy
 #| msgid "Slide Left"
 msgid "Stick left"
 msgstr "Esvarar ?? esquerda"
 
-#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1250
+#: engines/scumm/metaengine.cpp:1185 engines/scumm/metaengine.cpp:1262
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick right"
 msgstr "Esvarar ?? dereita"
 
-#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1257
+#: engines/scumm/metaengine.cpp:1192 engines/scumm/metaengine.cpp:1269
 #, fuzzy
 msgid "Fire / select"
 msgstr "Mover ?? esquerda"
 
-#: engines/scumm/metaengine.cpp:1186
+#: engines/scumm/metaengine.cpp:1198
 #, fuzzy
 msgid "Walk / menu back"
 msgstr "~V~olver ao Iniciador"
 
-#: engines/scumm/metaengine.cpp:1191
+#: engines/scumm/metaengine.cpp:1203
 #, fuzzy
 msgid "Skip / menu back"
 msgstr "~V~olver ao Iniciador"
 
-#: engines/scumm/metaengine.cpp:1207
+#: engines/scumm/metaengine.cpp:1219
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1262
+#: engines/scumm/metaengine.cpp:1274
 #, fuzzy
 #| msgid "Move Back"
 msgid "Cover / back"
 msgstr "Mover cara atr??s"
 
-#: engines/scumm/metaengine.cpp:1268
+#: engines/scumm/metaengine.cpp:1280
 #, fuzzy
 msgid "Skip / back"
 msgstr "Omitir li??a"
 
-#: engines/scumm/metaengine.cpp:1275
+#: engines/scumm/metaengine.cpp:1287
 #, fuzzy
 msgid "Skip / menu"
 msgstr "~V~olver ao Iniciador"
diff --git a/po/he.po b/po/he.po
index 1ed13aa192a..0aa623f5628 100644
--- a/po/he.po
+++ b/po/he.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.1.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-06 11:26+0000\n"
+"POT-Creation-Date: 2026-06-07 21:08+0000\n"
 "PO-Revision-Date: 2025-04-09 20:18+0000\n"
 "Last-Translator: Niv Baehr <bloop93 at gmail.com>\n"
 "Language-Team: Hebrew <https://translations.scummvm.org/projects/scummvm/"
@@ -1033,7 +1033,7 @@ msgstr "??????????"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1088
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1100
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1054,7 +1054,7 @@ msgstr "??????????"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1055
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1067
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1075,7 +1075,7 @@ msgstr "????????"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1068
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1080
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1095,7 +1095,7 @@ msgstr "??????????"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1075
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1087
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3807,7 +3807,7 @@ msgid "Game not implemented"
 msgstr "???????? ?????????? ???????? ????????"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
-#: engines/scumm/metaengine.cpp:1038
+#: engines/scumm/metaengine.cpp:1050
 #, fuzzy
 #| msgid "Default game keymap"
 msgid "Default game keymappings"
@@ -5811,7 +5811,7 @@ msgstr "R2"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1196 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1208 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "??????????"
 
@@ -7325,7 +7325,7 @@ msgstr "?????????? ??????????"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1114 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1126 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -8087,7 +8087,7 @@ msgstr "????????????????????"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1101
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1113
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -10392,7 +10392,7 @@ msgid "Player ammo will never decrease."
 msgstr "???????????? ?????????? ???? ???????? ??????????."
 
 #: engines/hypno/metaengine.cpp:70 engines/scumm/metaengine.cpp:891
-#: engines/scumm/metaengine.cpp:900
+#: engines/scumm/metaengine.cpp:909
 msgid "Unlock all levels"
 msgstr "?????????? ???? ????????????"
 
@@ -13954,121 +13954,133 @@ msgstr "?????????? ?????????? ??????????"
 msgid "Run the game in 640x400 high resolution mode instead of 320x200"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:901
+#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:910
 #, fuzzy
 #| msgid "All levels will be available to play."
 msgid "All levels will be available without requiring passwords"
 msgstr "???? ???????????? ???????? ???????????? ??????????."
 
-#: engines/scumm/metaengine.cpp:1049
+#: engines/scumm/metaengine.cpp:900
+#, fuzzy
+#| msgid "No date saved"
+msgid "No damage"
+msgstr "???? ???????? ??????????"
+
+#: engines/scumm/metaengine.cpp:901
+#, fuzzy
+#| msgid "Disable save menu"
+msgid "Disable player damage"
+msgstr "?????????? ?????????? ??????????"
+
+#: engines/scumm/metaengine.cpp:1061
 #, fuzzy
 msgid "Down left"
 msgstr "???????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1062
+#: engines/scumm/metaengine.cpp:1074
 #, fuzzy
 msgid "Down right"
 msgstr "?????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1082
+#: engines/scumm/metaengine.cpp:1094
 #, fuzzy
 #| msgid "Upper left item"
 msgid "Up left"
 msgstr "???????? ?????????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1095
+#: engines/scumm/metaengine.cpp:1107
 #, fuzzy
 msgid "Up right"
 msgstr "??????????"
 
-#: engines/scumm/metaengine.cpp:1107
+#: engines/scumm/metaengine.cpp:1119
 msgid "Switch weapon"
 msgstr "?????????? ??????"
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1121
+#: engines/scumm/metaengine.cpp:1133
 msgid "Win the bike fight cheat"
 msgstr "?????????? ?????????????? ???????? ????????????????"
 
-#: engines/scumm/metaengine.cpp:1130
+#: engines/scumm/metaengine.cpp:1142
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1209
+#: engines/scumm/metaengine.cpp:1144 engines/scumm/metaengine.cpp:1221
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1214
+#: engines/scumm/metaengine.cpp:1149 engines/scumm/metaengine.cpp:1226
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1219
+#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1231
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1224
+#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1229
+#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1241
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "??????????"
 
-#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
+#: engines/scumm/metaengine.cpp:1171 engines/scumm/metaengine.cpp:1248
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "?????????? ??????"
 
-#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1243
+#: engines/scumm/metaengine.cpp:1178 engines/scumm/metaengine.cpp:1255
 #, fuzzy
 #| msgid "Slide Left"
 msgid "Stick left"
 msgstr "???????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1250
+#: engines/scumm/metaengine.cpp:1185 engines/scumm/metaengine.cpp:1262
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick right"
 msgstr "???????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1257
+#: engines/scumm/metaengine.cpp:1192 engines/scumm/metaengine.cpp:1269
 #, fuzzy
 #| msgid "Move left"
 msgid "Fire / select"
 msgstr "?????????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1186
+#: engines/scumm/metaengine.cpp:1198
 #, fuzzy
 #| msgid "Open main menu"
 msgid "Walk / menu back"
 msgstr "?????????? ?????????? ????????"
 
-#: engines/scumm/metaengine.cpp:1191
+#: engines/scumm/metaengine.cpp:1203
 #, fuzzy
 #| msgid "Open main menu"
 msgid "Skip / menu back"
 msgstr "?????????? ?????????? ????????"
 
-#: engines/scumm/metaengine.cpp:1207
+#: engines/scumm/metaengine.cpp:1219
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1262
+#: engines/scumm/metaengine.cpp:1274
 #, fuzzy
 #| msgid "Move Back"
 msgid "Cover / back"
 msgstr "??????????"
 
-#: engines/scumm/metaengine.cpp:1268
+#: engines/scumm/metaengine.cpp:1280
 #, fuzzy
 #| msgid "Skip dialogue"
 msgid "Skip / back"
 msgstr "?????????? ???? ????????"
 
-#: engines/scumm/metaengine.cpp:1275
+#: engines/scumm/metaengine.cpp:1287
 #, fuzzy
 #| msgid "Open main menu"
 msgid "Skip / menu"
diff --git a/po/hi.po b/po/hi.po
index 11d46e9d9d4..49c564e60de 100644
--- a/po/hi.po
+++ b/po/hi.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-06 11:26+0000\n"
+"POT-Creation-Date: 2026-06-07 21:08+0000\n"
 "PO-Revision-Date: 2026-05-12 22:22+0000\n"
 "Last-Translator: KARTIK SAXENA <kartiksaxena2005 at gmail.com>\n"
 "Language-Team: Hindi <https://translations.scummvm.org/projects/scummvm/"
@@ -1042,7 +1042,7 @@ msgstr ""
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1088
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1100
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1063,7 +1063,7 @@ msgstr "?????????"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1055
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1067
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1084,7 +1084,7 @@ msgstr "????????????"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1068
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1080
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1104,7 +1104,7 @@ msgstr "????????????"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1075
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1087
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3734,7 +3734,7 @@ msgid "Game not implemented"
 msgstr ""
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
-#: engines/scumm/metaengine.cpp:1038
+#: engines/scumm/metaengine.cpp:1050
 #, fuzzy
 #| msgid "Game Options"
 msgid "Default game keymappings"
@@ -5638,7 +5638,7 @@ msgstr ""
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1196 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1208 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr ""
 
@@ -7059,7 +7059,7 @@ msgstr "???????????????????????? ?????????"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1114 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1126 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -7780,7 +7780,7 @@ msgstr "???????????? ??????????????? ????????????????????????"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1101
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1113
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -9906,7 +9906,7 @@ msgid "Player ammo will never decrease."
 msgstr ""
 
 #: engines/hypno/metaengine.cpp:70 engines/scumm/metaengine.cpp:891
-#: engines/scumm/metaengine.cpp:900
+#: engines/scumm/metaengine.cpp:909
 msgid "Unlock all levels"
 msgstr ""
 
@@ -13211,119 +13211,129 @@ msgstr ""
 msgid "Run the game in 640x400 high resolution mode instead of 320x200"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:901
+#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:910
 msgid "All levels will be available without requiring passwords"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1049
+#: engines/scumm/metaengine.cpp:900
+msgid "No damage"
+msgstr ""
+
+#: engines/scumm/metaengine.cpp:901
+#, fuzzy
+#| msgid "Disable falling"
+msgid "Disable player damage"
+msgstr "??????????????? ?????????????????? ????????????"
+
+#: engines/scumm/metaengine.cpp:1061
 #, fuzzy
 #| msgid "Down Left"
 msgid "Down left"
 msgstr "???????????? ??????????????????"
 
-#: engines/scumm/metaengine.cpp:1062
+#: engines/scumm/metaengine.cpp:1074
 #, fuzzy
 #| msgid "Down Right"
 msgid "Down right"
 msgstr "??????????????? ??????????????????"
 
-#: engines/scumm/metaengine.cpp:1082
+#: engines/scumm/metaengine.cpp:1094
 #, fuzzy
 #| msgid "Move Left"
 msgid "Up left"
 msgstr "???????????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1095
+#: engines/scumm/metaengine.cpp:1107
 #, fuzzy
 #| msgid "Up Right"
 msgid "Up right"
 msgstr "????????????"
 
-#: engines/scumm/metaengine.cpp:1107
+#: engines/scumm/metaengine.cpp:1119
 msgid "Switch weapon"
 msgstr ""
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1121
+#: engines/scumm/metaengine.cpp:1133
 msgid "Win the bike fight cheat"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1130
+#: engines/scumm/metaengine.cpp:1142
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1209
+#: engines/scumm/metaengine.cpp:1144 engines/scumm/metaengine.cpp:1221
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1214
+#: engines/scumm/metaengine.cpp:1149 engines/scumm/metaengine.cpp:1226
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1219
+#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1231
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1224
+#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1229
+#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1241
 msgid "Stick up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
+#: engines/scumm/metaengine.cpp:1171 engines/scumm/metaengine.cpp:1248
 #, fuzzy
 msgid "Stick down"
 msgstr "???????????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1243
+#: engines/scumm/metaengine.cpp:1178 engines/scumm/metaengine.cpp:1255
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick left"
 msgstr "?????????????????? ?????????????????? ?????????"
 
-#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1250
+#: engines/scumm/metaengine.cpp:1185 engines/scumm/metaengine.cpp:1262
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick right"
 msgstr "?????????????????? ?????????????????? ?????????"
 
-#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1257
+#: engines/scumm/metaengine.cpp:1192 engines/scumm/metaengine.cpp:1269
 #, fuzzy
 #| msgid "Move Left"
 msgid "Fire / select"
 msgstr "???????????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1186
+#: engines/scumm/metaengine.cpp:1198
 #, fuzzy
 #| msgid "Skip autosave"
 msgid "Walk / menu back"
 msgstr "?????????????????? ??????????????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1191
+#: engines/scumm/metaengine.cpp:1203
 #, fuzzy
 #| msgid "Skip autosave"
 msgid "Skip / menu back"
 msgstr "?????????????????? ??????????????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1207
+#: engines/scumm/metaengine.cpp:1219
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1262
+#: engines/scumm/metaengine.cpp:1274
 #, fuzzy
 #| msgid "Move Forward"
 msgid "Cover / back"
 msgstr "????????? ?????????"
 
-#: engines/scumm/metaengine.cpp:1268
+#: engines/scumm/metaengine.cpp:1280
 #, fuzzy
 #| msgid "Show hidden files"
 msgid "Skip / back"
 msgstr "???????????? ????????? ?????????????????? ?????????????????????"
 
-#: engines/scumm/metaengine.cpp:1275
+#: engines/scumm/metaengine.cpp:1287
 #, fuzzy
 #| msgid "Skip autosave"
 msgid "Skip / menu"
diff --git a/po/hu_HU.po b/po/hu_HU.po
index c559a14e895..d57c74f0d21 100644
--- a/po/hu_HU.po
+++ b/po/hu_HU.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-06 11:26+0000\n"
+"POT-Creation-Date: 2026-06-07 21:08+0000\n"
 "PO-Revision-Date: 2025-03-07 00:18+0000\n"
 "Last-Translator: Hov??nszki Tam??s <gahex68379 at lofiey.com>\n"
 "Language-Team: Hungarian <https://translations.scummvm.org/projects/scummvm/"
@@ -1053,7 +1053,7 @@ msgstr "Interakci??"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1088
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1100
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1074,7 +1074,7 @@ msgstr "Fel"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1055
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1067
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1095,7 +1095,7 @@ msgstr "Le"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1068
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1080
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1115,7 +1115,7 @@ msgstr "Bal"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1075
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1087
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3961,7 +3961,7 @@ msgid "Game not implemented"
 msgstr "A j??t??k m??g nem ker??lt implement??l??sra"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
-#: engines/scumm/metaengine.cpp:1038
+#: engines/scumm/metaengine.cpp:1050
 #, fuzzy
 #| msgid "Default game keymap"
 msgid "Default game keymappings"
@@ -6351,7 +6351,7 @@ msgstr "R2"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1196 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1208 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "Men??"
 
@@ -7844,7 +7844,7 @@ msgstr "Harcmodor V??lt??s"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1114 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1126 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -8606,7 +8606,7 @@ msgstr "Besz??l??s/Interakci??"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1101
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1113
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -10848,7 +10848,7 @@ msgid "Player ammo will never decrease."
 msgstr "A j??t??kos l??szere soha nem fog cs??kkenni."
 
 #: engines/hypno/metaengine.cpp:70 engines/scumm/metaengine.cpp:891
-#: engines/scumm/metaengine.cpp:900
+#: engines/scumm/metaengine.cpp:909
 msgid "Unlock all levels"
 msgstr "Minden p??lya el??rhet??v??"
 
@@ -14411,124 +14411,136 @@ msgstr "Magas felbont??s be"
 msgid "Run the game in 640x400 high resolution mode instead of 320x200"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:901
+#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:910
 #, fuzzy
 #| msgid "All levels will be available to play."
 msgid "All levels will be available without requiring passwords"
 msgstr "Minden p??lya el??rhet??v?? v??lik a j??t??kos sz??m??ra."
 
-#: engines/scumm/metaengine.cpp:1049
+#: engines/scumm/metaengine.cpp:900
+#, fuzzy
+#| msgid "No date saved"
+msgid "No damage"
+msgstr "D??tum nem ker??lt ment??sre"
+
+#: engines/scumm/metaengine.cpp:901
+#, fuzzy
+#| msgid "Disable save menu"
+msgid "Disable player damage"
+msgstr "Ment??s men?? letilt??s"
+
+#: engines/scumm/metaengine.cpp:1061
 #, fuzzy
 #| msgid "Down Left"
 msgid "Down left"
 msgstr "Balra Le"
 
-#: engines/scumm/metaengine.cpp:1062
+#: engines/scumm/metaengine.cpp:1074
 #, fuzzy
 #| msgid "Down Right"
 msgid "Down right"
 msgstr "Jobbra Le"
 
-#: engines/scumm/metaengine.cpp:1082
+#: engines/scumm/metaengine.cpp:1094
 #, fuzzy
 #| msgid "Upper left item"
 msgid "Up left"
 msgstr "Bal fels?? t??rgy"
 
-#: engines/scumm/metaengine.cpp:1095
+#: engines/scumm/metaengine.cpp:1107
 #, fuzzy
 #| msgid "Up Right"
 msgid "Up right"
 msgstr "Jobbra Fel"
 
-#: engines/scumm/metaengine.cpp:1107
+#: engines/scumm/metaengine.cpp:1119
 msgid "Switch weapon"
 msgstr "Fegyver v??lt??s"
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1121
+#: engines/scumm/metaengine.cpp:1133
 msgid "Win the bike fight cheat"
 msgstr "Csal??s a motoros harc megnyer??s??hez"
 
-#: engines/scumm/metaengine.cpp:1130
+#: engines/scumm/metaengine.cpp:1142
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1209
+#: engines/scumm/metaengine.cpp:1144 engines/scumm/metaengine.cpp:1221
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1214
+#: engines/scumm/metaengine.cpp:1149 engines/scumm/metaengine.cpp:1226
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1219
+#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1231
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1224
+#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1229
+#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1241
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "Felvesz"
 
-#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
+#: engines/scumm/metaengine.cpp:1171 engines/scumm/metaengine.cpp:1248
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "G??rget??s LE"
 
-#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1243
+#: engines/scumm/metaengine.cpp:1178 engines/scumm/metaengine.cpp:1255
 #, fuzzy
 #| msgid "Slide Left"
 msgid "Stick left"
 msgstr "Cs??sz??s balra"
 
-#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1250
+#: engines/scumm/metaengine.cpp:1185 engines/scumm/metaengine.cpp:1262
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick right"
 msgstr "Cs??sz??s jobbra"
 
-#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1257
+#: engines/scumm/metaengine.cpp:1192 engines/scumm/metaengine.cpp:1269
 #, fuzzy
 #| msgid "Move left"
 msgid "Fire / select"
 msgstr "Mozg??s balra"
 
-#: engines/scumm/metaengine.cpp:1186
+#: engines/scumm/metaengine.cpp:1198
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Walk / menu back"
 msgstr "F?? men?? ??tugr??sa"
 
-#: engines/scumm/metaengine.cpp:1191
+#: engines/scumm/metaengine.cpp:1203
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu back"
 msgstr "F?? men?? ??tugr??sa"
 
-#: engines/scumm/metaengine.cpp:1207
+#: engines/scumm/metaengine.cpp:1219
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1262
+#: engines/scumm/metaengine.cpp:1274
 #, fuzzy
 #| msgid "Move Back"
 msgid "Cover / back"
 msgstr "Mozg??s h??tra"
 
-#: engines/scumm/metaengine.cpp:1268
+#: engines/scumm/metaengine.cpp:1280
 #, fuzzy
 #| msgid "Skip dialogue"
 msgid "Skip / back"
 msgstr "Dial??gus ??tugr??sa"
 
-#: engines/scumm/metaengine.cpp:1275
+#: engines/scumm/metaengine.cpp:1287
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu"
diff --git a/po/it_IT.po b/po/it_IT.po
index ceb532b26c9..00a8e6f0867 100644
--- a/po/it_IT.po
+++ b/po/it_IT.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-06 11:26+0000\n"
+"POT-Creation-Date: 2026-06-07 21:08+0000\n"
 "PO-Revision-Date: 2026-06-04 19:00+0000\n"
 "Last-Translator: Walter Agazzi <walter.agazzi at protonmail.com>\n"
 "Language-Team: Italian <https://translations.scummvm.org/projects/scummvm/"
@@ -1056,7 +1056,7 @@ msgstr "Interazione"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1088
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1100
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1077,7 +1077,7 @@ msgstr "Su"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1055
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1067
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1098,7 +1098,7 @@ msgstr "Gi??"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1068
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1080
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1118,7 +1118,7 @@ msgstr "Sinistra"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1075
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1087
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3923,7 +3923,7 @@ msgid "Game not implemented"
 msgstr "Gioco non implementato"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
-#: engines/scumm/metaengine.cpp:1038
+#: engines/scumm/metaengine.cpp:1050
 msgid "Default game keymappings"
 msgstr "Mappa tasti predefinita"
 
@@ -6148,7 +6148,7 @@ msgstr "R2"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1196 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1208 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "Menu"
 
@@ -7625,7 +7625,7 @@ msgstr "Attiva / Disattiva modalit?? combattimento"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1114 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1126 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -8287,7 +8287,7 @@ msgstr "Parlare / Interazione"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1101
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1113
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -10268,7 +10268,7 @@ msgid "Player ammo will never decrease."
 msgstr "Il giocatore non consumer?? mai munizioni."
 
 #: engines/hypno/metaengine.cpp:70 engines/scumm/metaengine.cpp:891
-#: engines/scumm/metaengine.cpp:900
+#: engines/scumm/metaengine.cpp:909
 msgid "Unlock all levels"
 msgstr "Sblocca tutti i livelli"
 
@@ -13657,99 +13657,111 @@ msgstr "Modalit?? ad alta risoluzione"
 msgid "Run the game in 640x400 high resolution mode instead of 320x200"
 msgstr "Esegue il gioco in alta risoluzione (640x400), anzich?? 320x200"
 
-#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:901
+#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:910
 msgid "All levels will be available without requiring passwords"
 msgstr ""
 "Permette l'accesso a qualsiasi livello del gioco senza richiedere password"
 
-#: engines/scumm/metaengine.cpp:1049
+#: engines/scumm/metaengine.cpp:900
+#, fuzzy
+#| msgid "No date saved"
+msgid "No damage"
+msgstr "Nessuna data salvata"
+
+#: engines/scumm/metaengine.cpp:901
+#, fuzzy
+#| msgid "Disable save menu"
+msgid "Disable player damage"
+msgstr "Disabilita men?? di salvataggio"
+
+#: engines/scumm/metaengine.cpp:1061
 msgid "Down left"
 msgstr "Gi?? sinistra"
 
-#: engines/scumm/metaengine.cpp:1062
+#: engines/scumm/metaengine.cpp:1074
 msgid "Down right"
 msgstr "Gi?? destra"
 
-#: engines/scumm/metaengine.cpp:1082
+#: engines/scumm/metaengine.cpp:1094
 msgid "Up left"
 msgstr "Su sinistra"
 
-#: engines/scumm/metaengine.cpp:1095
+#: engines/scumm/metaengine.cpp:1107
 msgid "Up right"
 msgstr "Su destra"
 
-#: engines/scumm/metaengine.cpp:1107
+#: engines/scumm/metaengine.cpp:1119
 msgid "Switch weapon"
 msgstr "Cambia arma"
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1121
+#: engines/scumm/metaengine.cpp:1133
 msgid "Win the bike fight cheat"
 msgstr "Cheat: Vinci combattimento in moto"
 
-#: engines/scumm/metaengine.cpp:1130
+#: engines/scumm/metaengine.cpp:1142
 msgid "Rebel Assault controls"
 msgstr "Controlli di Rebel Assault"
 
-#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1209
+#: engines/scumm/metaengine.cpp:1144 engines/scumm/metaengine.cpp:1221
 msgid "Aim up / menu up"
 msgstr "Mira verso l'alto / men?? su"
 
-#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1214
+#: engines/scumm/metaengine.cpp:1149 engines/scumm/metaengine.cpp:1226
 msgid "Aim down / menu down"
 msgstr "Mira verso il basso / men?? gi??"
 
-#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1219
+#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1231
 msgid "Aim left / menu left"
 msgstr "Mira a sinistra / men?? sinistra"
 
-#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1224
+#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
 msgid "Aim right / menu right"
 msgstr "Mira a destra / men?? destra"
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1229
+#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1241
 msgid "Stick up"
 msgstr "Joystick su"
 
-#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
+#: engines/scumm/metaengine.cpp:1171 engines/scumm/metaengine.cpp:1248
 msgid "Stick down"
 msgstr "Joystick gi??"
 
-#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1243
+#: engines/scumm/metaengine.cpp:1178 engines/scumm/metaengine.cpp:1255
 msgid "Stick left"
 msgstr "Joystick sinistra"
 
-#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1250
+#: engines/scumm/metaengine.cpp:1185 engines/scumm/metaengine.cpp:1262
 msgid "Stick right"
 msgstr "Joystick destra"
 
-#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1257
+#: engines/scumm/metaengine.cpp:1192 engines/scumm/metaengine.cpp:1269
 msgid "Fire / select"
 msgstr "Spara / seleziona"
 
-#: engines/scumm/metaengine.cpp:1186
+#: engines/scumm/metaengine.cpp:1198
 #, fuzzy
 #| msgid "Skip / menu back"
 msgid "Walk / menu back"
 msgstr "Salta / indietro (men??)"
 
-#: engines/scumm/metaengine.cpp:1191
+#: engines/scumm/metaengine.cpp:1203
 msgid "Skip / menu back"
 msgstr "Salta / indietro (men??)"
 
-#: engines/scumm/metaengine.cpp:1207
+#: engines/scumm/metaengine.cpp:1219
 msgid "Rebel Assault II controls"
 msgstr "Controlli di Rebel Assault II"
 
-#: engines/scumm/metaengine.cpp:1262
+#: engines/scumm/metaengine.cpp:1274
 msgid "Cover / back"
 msgstr "Copertura / indietro"
 
-#: engines/scumm/metaengine.cpp:1268
+#: engines/scumm/metaengine.cpp:1280
 msgid "Skip / back"
 msgstr "Salta / indietro"
 
-#: engines/scumm/metaengine.cpp:1275
+#: engines/scumm/metaengine.cpp:1287
 msgid "Skip / menu"
 msgstr "Salta / men??"
 
diff --git a/po/ja.po b/po/ja.po
index 7b0afd958b3..7cc6b6bc3ff 100644
--- a/po/ja.po
+++ b/po/ja.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-06 11:26+0000\n"
+"POT-Creation-Date: 2026-06-07 21:08+0000\n"
 "PO-Revision-Date: 2026-06-04 07:22+0000\n"
 "Last-Translator: VAN-Gluon <van.gluonp at gmail.com>\n"
 "Language-Team: Japanese <https://translations.scummvm.org/projects/scummvm/"
@@ -1042,7 +1042,7 @@ msgstr "??????"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1088
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1100
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1063,7 +1063,7 @@ msgstr "???"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1055
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1067
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1084,7 +1084,7 @@ msgstr "???"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1068
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1080
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1104,7 +1104,7 @@ msgstr "???"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1075
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1087
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3781,7 +3781,7 @@ msgid "Game not implemented"
 msgstr "?????????????????????"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
-#: engines/scumm/metaengine.cpp:1038
+#: engines/scumm/metaengine.cpp:1050
 msgid "Default game keymappings"
 msgstr "?????????????????????(???????????????)"
 
@@ -6002,7 +6002,7 @@ msgstr "R2"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1196 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1208 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "????????????"
 
@@ -7448,7 +7448,7 @@ msgstr "??????????????????"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1114 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1126 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -8209,7 +8209,7 @@ msgstr "??????/??????"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1101
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1113
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -10463,7 +10463,7 @@ msgid "Player ammo will never decrease."
 msgstr "?????????????????????????????????????????????"
 
 #: engines/hypno/metaengine.cpp:70 engines/scumm/metaengine.cpp:891
-#: engines/scumm/metaengine.cpp:900
+#: engines/scumm/metaengine.cpp:909
 msgid "Unlock all levels"
 msgstr "??????????????????"
 
@@ -14029,124 +14029,136 @@ msgstr "????????????ON"
 msgid "Run the game in 640x400 high resolution mode instead of 320x200"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:901
+#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:910
 #, fuzzy
 #| msgid "All levels will be available to play."
 msgid "All levels will be available without requiring passwords"
 msgstr "?????????????????????????????????????????????"
 
-#: engines/scumm/metaengine.cpp:1049
+#: engines/scumm/metaengine.cpp:900
+#, fuzzy
+#| msgid "No date saved"
+msgid "No damage"
+msgstr "???????????????"
+
+#: engines/scumm/metaengine.cpp:901
+#, fuzzy
+#| msgid "Disable save menu"
+msgid "Disable player damage"
+msgstr "???????????????????????????????????????"
+
+#: engines/scumm/metaengine.cpp:1061
 #, fuzzy
 #| msgid "Down Left"
 msgid "Down left"
 msgstr "??????"
 
-#: engines/scumm/metaengine.cpp:1062
+#: engines/scumm/metaengine.cpp:1074
 #, fuzzy
 #| msgid "Down Right"
 msgid "Down right"
 msgstr "??????"
 
-#: engines/scumm/metaengine.cpp:1082
+#: engines/scumm/metaengine.cpp:1094
 #, fuzzy
 #| msgid "Upper left item"
 msgid "Up left"
 msgstr "?????????????????????"
 
-#: engines/scumm/metaengine.cpp:1095
+#: engines/scumm/metaengine.cpp:1107
 #, fuzzy
 #| msgid "Up Right"
 msgid "Up right"
 msgstr "??????"
 
-#: engines/scumm/metaengine.cpp:1107
+#: engines/scumm/metaengine.cpp:1119
 msgid "Switch weapon"
 msgstr "?????????????????????"
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1121
+#: engines/scumm/metaengine.cpp:1133
 msgid "Win the bike fight cheat"
 msgstr "????????????????????????"
 
-#: engines/scumm/metaengine.cpp:1130
+#: engines/scumm/metaengine.cpp:1142
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1209
+#: engines/scumm/metaengine.cpp:1144 engines/scumm/metaengine.cpp:1221
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1214
+#: engines/scumm/metaengine.cpp:1149 engines/scumm/metaengine.cpp:1226
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1219
+#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1231
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1224
+#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1229
+#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1241
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "??????"
 
-#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
+#: engines/scumm/metaengine.cpp:1171 engines/scumm/metaengine.cpp:1248
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "?????????????????????"
 
-#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1243
+#: engines/scumm/metaengine.cpp:1178 engines/scumm/metaengine.cpp:1255
 #, fuzzy
 #| msgid "Slide Left"
 msgid "Stick left"
 msgstr "??????????????????"
 
-#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1250
+#: engines/scumm/metaengine.cpp:1185 engines/scumm/metaengine.cpp:1262
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick right"
 msgstr "??????????????????"
 
-#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1257
+#: engines/scumm/metaengine.cpp:1192 engines/scumm/metaengine.cpp:1269
 #, fuzzy
 #| msgid "Move left"
 msgid "Fire / select"
 msgstr "????????????"
 
-#: engines/scumm/metaengine.cpp:1186
+#: engines/scumm/metaengine.cpp:1198
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Walk / menu back"
 msgstr "????????????????????????????????????"
 
-#: engines/scumm/metaengine.cpp:1191
+#: engines/scumm/metaengine.cpp:1203
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu back"
 msgstr "????????????????????????????????????"
 
-#: engines/scumm/metaengine.cpp:1207
+#: engines/scumm/metaengine.cpp:1219
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1262
+#: engines/scumm/metaengine.cpp:1274
 #, fuzzy
 #| msgid "Move Back"
 msgid "Cover / back"
 msgstr "??????"
 
-#: engines/scumm/metaengine.cpp:1268
+#: engines/scumm/metaengine.cpp:1280
 #, fuzzy
 #| msgid "Skip dialogue"
 msgid "Skip / back"
 msgstr "??????????????????????????????"
 
-#: engines/scumm/metaengine.cpp:1275
+#: engines/scumm/metaengine.cpp:1287
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu"
diff --git a/po/ka.po b/po/ka.po
index e9982a65682..4d3b63b2689 100644
--- a/po/ka.po
+++ b/po/ka.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-06 11:26+0000\n"
+"POT-Creation-Date: 2026-06-07 21:08+0000\n"
 "PO-Revision-Date: 2026-06-05 10:22+0000\n"
 "Last-Translator: NorwayFun <temuri.doghonadze at gmail.com>\n"
 "Language-Team: Georgian <https://translations.scummvm.org/projects/scummvm/"
@@ -1021,7 +1021,7 @@ msgstr "??????????????????????????????"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1088
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1100
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1042,7 +1042,7 @@ msgstr "?????????????????????"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1055
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1067
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1063,7 +1063,7 @@ msgstr "??????????????????"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1068
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1080
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1083,7 +1083,7 @@ msgstr "????????????????????????"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1075
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1087
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3606,7 +3606,7 @@ msgid "Game not implemented"
 msgstr "?????????????????? ????????????????????????????????????????????? ????????????"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
-#: engines/scumm/metaengine.cpp:1038
+#: engines/scumm/metaengine.cpp:1050
 msgid "Default game keymappings"
 msgstr "???????????????????????????????????? ??????????????????????????? ??????????????????????????? ??????????????????"
 
@@ -5463,7 +5463,7 @@ msgstr "R2"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1196 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1208 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "???????????????"
 
@@ -6830,7 +6830,7 @@ msgstr "????????????????????? ????????????????????????"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1114 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1126 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -7477,7 +7477,7 @@ msgstr "????????????????????? / ??????????????????????????????"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1101
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1113
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -9364,7 +9364,7 @@ msgid "Player ammo will never decrease."
 msgstr ""
 
 #: engines/hypno/metaengine.cpp:70 engines/scumm/metaengine.cpp:891
-#: engines/scumm/metaengine.cpp:900
+#: engines/scumm/metaengine.cpp:909
 msgid "Unlock all levels"
 msgstr "??????????????? ??????????????? ???????????????????????????"
 
@@ -12424,102 +12424,114 @@ msgstr "?????????????????? ????????????????????????????????? ??????????????????"
 msgid "Run the game in 640x400 high resolution mode instead of 320x200"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:901
+#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:910
 msgid "All levels will be available without requiring passwords"
 msgstr "??????????????????????????? ??????????????????????????????????????? ?????????????????? ??????????????? ???????????? ????????????????????? ??????????????????????????? ??????????????????"
 
-#: engines/scumm/metaengine.cpp:1049
+#: engines/scumm/metaengine.cpp:900
+#, fuzzy
+#| msgid "No date saved"
+msgid "No damage"
+msgstr "?????????????????? ?????? ????????????????????????"
+
+#: engines/scumm/metaengine.cpp:901
+#, fuzzy
+#| msgid "Disable save menu"
+msgid "Disable player damage"
+msgstr "???????????????????????? ?????????????????? ????????????????????????"
+
+#: engines/scumm/metaengine.cpp:1061
 msgid "Down left"
 msgstr "??????????????? ????????????????????????"
 
-#: engines/scumm/metaengine.cpp:1062
+#: engines/scumm/metaengine.cpp:1074
 msgid "Down right"
 msgstr "??????????????? ????????????????????????"
 
-#: engines/scumm/metaengine.cpp:1082
+#: engines/scumm/metaengine.cpp:1094
 msgid "Up left"
 msgstr "???????????? ????????????????????????"
 
-#: engines/scumm/metaengine.cpp:1095
+#: engines/scumm/metaengine.cpp:1107
 msgid "Up right"
 msgstr "??????????????? ????????????????????????"
 
-#: engines/scumm/metaengine.cpp:1107
+#: engines/scumm/metaengine.cpp:1119
 msgid "Switch weapon"
 msgstr "????????????????????? ????????????????????????"
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1121
+#: engines/scumm/metaengine.cpp:1133
 msgid "Win the bike fight cheat"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1130
+#: engines/scumm/metaengine.cpp:1142
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1209
+#: engines/scumm/metaengine.cpp:1144 engines/scumm/metaengine.cpp:1221
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1214
+#: engines/scumm/metaengine.cpp:1149 engines/scumm/metaengine.cpp:1226
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1219
+#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1231
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1224
+#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1229
+#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1241
 msgid "Stick up"
 msgstr "???????????????"
 
-#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
+#: engines/scumm/metaengine.cpp:1171 engines/scumm/metaengine.cpp:1248
 msgid "Stick down"
 msgstr "????????????????????????"
 
-#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1243
+#: engines/scumm/metaengine.cpp:1178 engines/scumm/metaengine.cpp:1255
 msgid "Stick left"
 msgstr "??????????????????????????? ????????????????????????"
 
-#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1250
+#: engines/scumm/metaengine.cpp:1185 engines/scumm/metaengine.cpp:1262
 msgid "Stick right"
 msgstr "??????????????????????????? ????????????????????????"
 
-#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1257
+#: engines/scumm/metaengine.cpp:1192 engines/scumm/metaengine.cpp:1269
 msgid "Fire / select"
 msgstr "?????????????????? / ????????????????????????"
 
-#: engines/scumm/metaengine.cpp:1186
+#: engines/scumm/metaengine.cpp:1198
 #, fuzzy
 #| msgid "Skip / menu back"
 msgid "Walk / menu back"
 msgstr "?????????????????????????????? / ??????????????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1191
+#: engines/scumm/metaengine.cpp:1203
 msgid "Skip / menu back"
 msgstr "?????????????????????????????? / ??????????????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1207
+#: engines/scumm/metaengine.cpp:1219
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1262
+#: engines/scumm/metaengine.cpp:1274
 #, fuzzy
 #| msgid "Move back"
 msgid "Cover / back"
 msgstr "???????????? ????????????????????????"
 
-#: engines/scumm/metaengine.cpp:1268
+#: engines/scumm/metaengine.cpp:1280
 #, fuzzy
 #| msgid "Skip walk"
 msgid "Skip / back"
 msgstr "???????????????????????? ??????????????????????????????"
 
-#: engines/scumm/metaengine.cpp:1275
+#: engines/scumm/metaengine.cpp:1287
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu"
diff --git a/po/ko.po b/po/ko.po
index fbff096317e..52c82f8c25b 100644
--- a/po/ko.po
+++ b/po/ko.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-06 11:26+0000\n"
+"POT-Creation-Date: 2026-06-07 21:08+0000\n"
 "PO-Revision-Date: 2026-05-19 20:22+0000\n"
 "Last-Translator: Hoseok Seo <ddinghoya at gmail.com>\n"
 "Language-Team: Korean <https://translations.scummvm.org/projects/scummvm/"
@@ -1044,7 +1044,7 @@ msgstr "??????"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1088
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1100
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1065,7 +1065,7 @@ msgstr "??????"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1055
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1067
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1086,7 +1086,7 @@ msgstr "?????????"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1068
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1080
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1106,7 +1106,7 @@ msgstr "??????"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1075
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1087
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3835,7 +3835,7 @@ msgid "Game not implemented"
 msgstr "????????? ???????????? ?????????"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
-#: engines/scumm/metaengine.cpp:1038
+#: engines/scumm/metaengine.cpp:1050
 msgid "Default game keymappings"
 msgstr "?????? ?????? ?????????"
 
@@ -6013,7 +6013,7 @@ msgstr "R2 ??????"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1196 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1208 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "??????"
 
@@ -7444,7 +7444,7 @@ msgstr "?????? ??????"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1114 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1126 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -8106,7 +8106,7 @@ msgstr "?????? / ??????"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1101
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1113
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -10056,7 +10056,7 @@ msgid "Player ammo will never decrease."
 msgstr "??????????????? ????????? ???????????? ????????????."
 
 #: engines/hypno/metaengine.cpp:70 engines/scumm/metaengine.cpp:891
-#: engines/scumm/metaengine.cpp:900
+#: engines/scumm/metaengine.cpp:909
 msgid "Unlock all levels"
 msgstr "?????? ?????? ?????? ??????"
 
@@ -13340,116 +13340,128 @@ msgstr "???????????? ???"
 msgid "Run the game in 640x400 high resolution mode instead of 320x200"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:901
+#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:910
 #, fuzzy
 #| msgid "All levels will be available to play."
 msgid "All levels will be available without requiring passwords"
 msgstr "?????? ???????????? ???????????? ??? ????????????."
 
-#: engines/scumm/metaengine.cpp:1049
+#: engines/scumm/metaengine.cpp:900
+#, fuzzy
+#| msgid "No date saved"
+msgid "No damage"
+msgstr "????????? ???????????? ?????????"
+
+#: engines/scumm/metaengine.cpp:901
+#, fuzzy
+#| msgid "Disable save menu"
+msgid "Disable player damage"
+msgstr "?????? ?????? ????????????"
+
+#: engines/scumm/metaengine.cpp:1061
 msgid "Down left"
 msgstr "?????? ??????"
 
-#: engines/scumm/metaengine.cpp:1062
+#: engines/scumm/metaengine.cpp:1074
 msgid "Down right"
 msgstr "????????? ??????"
 
-#: engines/scumm/metaengine.cpp:1082
+#: engines/scumm/metaengine.cpp:1094
 msgid "Up left"
 msgstr "?????? ??????"
 
-#: engines/scumm/metaengine.cpp:1095
+#: engines/scumm/metaengine.cpp:1107
 msgid "Up right"
 msgstr "????????? ??????"
 
-#: engines/scumm/metaengine.cpp:1107
+#: engines/scumm/metaengine.cpp:1119
 msgid "Switch weapon"
 msgstr "?????? ??????"
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1121
+#: engines/scumm/metaengine.cpp:1133
 msgid "Win the bike fight cheat"
 msgstr "????????? ???????????? ???????????? ??????"
 
-#: engines/scumm/metaengine.cpp:1130
+#: engines/scumm/metaengine.cpp:1142
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1209
+#: engines/scumm/metaengine.cpp:1144 engines/scumm/metaengine.cpp:1221
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1214
+#: engines/scumm/metaengine.cpp:1149 engines/scumm/metaengine.cpp:1226
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1219
+#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1231
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1224
+#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1229
+#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1241
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "??????"
 
-#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
+#: engines/scumm/metaengine.cpp:1171 engines/scumm/metaengine.cpp:1248
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "????????? ?????????"
 
-#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1243
+#: engines/scumm/metaengine.cpp:1178 engines/scumm/metaengine.cpp:1255
 #, fuzzy
 #| msgid "Slide left"
 msgid "Stick left"
 msgstr "???????????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1250
+#: engines/scumm/metaengine.cpp:1185 engines/scumm/metaengine.cpp:1262
 #, fuzzy
 #| msgid "Slide right"
 msgid "Stick right"
 msgstr "????????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1257
+#: engines/scumm/metaengine.cpp:1192 engines/scumm/metaengine.cpp:1269
 #, fuzzy
 #| msgid "Move / Select"
 msgid "Fire / select"
 msgstr "?????? / ??????"
 
-#: engines/scumm/metaengine.cpp:1186
+#: engines/scumm/metaengine.cpp:1198
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Walk / menu back"
 msgstr "?????? ?????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1191
+#: engines/scumm/metaengine.cpp:1203
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu back"
 msgstr "?????? ?????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1207
+#: engines/scumm/metaengine.cpp:1219
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1262
+#: engines/scumm/metaengine.cpp:1274
 #, fuzzy
 #| msgid "Move back"
 msgid "Cover / back"
 msgstr "?????? ??????"
 
-#: engines/scumm/metaengine.cpp:1268
+#: engines/scumm/metaengine.cpp:1280
 #, fuzzy
 #| msgid "Skip walk"
 msgid "Skip / back"
 msgstr "?????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1275
+#: engines/scumm/metaengine.cpp:1287
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu"
diff --git a/po/nb_NO.po b/po/nb_NO.po
index 2816787779f..ef991945c7b 100644
--- a/po/nb_NO.po
+++ b/po/nb_NO.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-06 11:26+0000\n"
+"POT-Creation-Date: 2026-06-07 21:08+0000\n"
 "PO-Revision-Date: 2026-02-07 15:22+0000\n"
 "Last-Translator: Einar Johan Tr??an S??m??en <einarjohants at gmail.com>\n"
 "Language-Team: Norwegian Bokm??l <https://translations.scummvm.org/projects/"
@@ -1052,7 +1052,7 @@ msgstr "Samhandle"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1088
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1100
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1073,7 +1073,7 @@ msgstr "Opp"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1055
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1067
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1094,7 +1094,7 @@ msgstr "Ned"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1068
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1080
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1114,7 +1114,7 @@ msgstr "Venstre"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1075
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1087
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3963,7 +3963,7 @@ msgid "Game not implemented"
 msgstr "Spillet er ikke implementert"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
-#: engines/scumm/metaengine.cpp:1038
+#: engines/scumm/metaengine.cpp:1050
 #, fuzzy
 #| msgid "Default game keymap"
 msgid "Default game keymappings"
@@ -6412,7 +6412,7 @@ msgstr "R2"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1196 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1208 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "Meny"
 
@@ -7910,7 +7910,7 @@ msgstr "Skru kampmodus av/p??"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1114 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1126 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -8678,7 +8678,7 @@ msgstr "Konverser/Interager"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1101
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1113
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -10930,7 +10930,7 @@ msgid "Player ammo will never decrease."
 msgstr "Spillerens ammunisjon minsker aldri."
 
 #: engines/hypno/metaengine.cpp:70 engines/scumm/metaengine.cpp:891
-#: engines/scumm/metaengine.cpp:900
+#: engines/scumm/metaengine.cpp:909
 msgid "Unlock all levels"
 msgstr "L??ser opp alle niv??er"
 
@@ -14502,124 +14502,136 @@ msgstr "H??y oppl??sning p??"
 msgid "Run the game in 640x400 high resolution mode instead of 320x200"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:901
+#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:910
 #, fuzzy
 #| msgid "All levels will be available to play."
 msgid "All levels will be available without requiring passwords"
 msgstr "Alle niv??er ??pnes for spilling."
 
-#: engines/scumm/metaengine.cpp:1049
+#: engines/scumm/metaengine.cpp:900
+#, fuzzy
+#| msgid "No date saved"
+msgid "No damage"
+msgstr "Ingen dato lagret"
+
+#: engines/scumm/metaengine.cpp:901
+#, fuzzy
+#| msgid "Disable save menu"
+msgid "Disable player damage"
+msgstr "Skru av lagringsmeny"
+
+#: engines/scumm/metaengine.cpp:1061
 #, fuzzy
 #| msgid "Down Left"
 msgid "Down left"
 msgstr "Ned til venstre"
 
-#: engines/scumm/metaengine.cpp:1062
+#: engines/scumm/metaengine.cpp:1074
 #, fuzzy
 #| msgid "Down Right"
 msgid "Down right"
 msgstr "Ned til h??yre"
 
-#: engines/scumm/metaengine.cpp:1082
+#: engines/scumm/metaengine.cpp:1094
 #, fuzzy
 #| msgid "Upper left item"
 msgid "Up left"
 msgstr "??vre venstre gjenstand"
 
-#: engines/scumm/metaengine.cpp:1095
+#: engines/scumm/metaengine.cpp:1107
 #, fuzzy
 #| msgid "Up Right"
 msgid "Up right"
 msgstr "Opp til h??yre"
 
-#: engines/scumm/metaengine.cpp:1107
+#: engines/scumm/metaengine.cpp:1119
 msgid "Switch weapon"
 msgstr "Bytte v??pen"
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1121
+#: engines/scumm/metaengine.cpp:1133
 msgid "Win the bike fight cheat"
 msgstr "Juksefunksjon for ?? vinne motorsykkelkamper"
 
-#: engines/scumm/metaengine.cpp:1130
+#: engines/scumm/metaengine.cpp:1142
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1209
+#: engines/scumm/metaengine.cpp:1144 engines/scumm/metaengine.cpp:1221
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1214
+#: engines/scumm/metaengine.cpp:1149 engines/scumm/metaengine.cpp:1226
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1219
+#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1231
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1224
+#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1229
+#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1241
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "Plukk opp"
 
-#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
+#: engines/scumm/metaengine.cpp:1171 engines/scumm/metaengine.cpp:1248
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "Bla ned"
 
-#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1243
+#: engines/scumm/metaengine.cpp:1178 engines/scumm/metaengine.cpp:1255
 #, fuzzy
 #| msgid "Slide Left"
 msgid "Stick left"
 msgstr "Skli mot Venstre"
 
-#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1250
+#: engines/scumm/metaengine.cpp:1185 engines/scumm/metaengine.cpp:1262
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick right"
 msgstr "Skli mot H??yre"
 
-#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1257
+#: engines/scumm/metaengine.cpp:1192 engines/scumm/metaengine.cpp:1269
 #, fuzzy
 #| msgid "Move left"
 msgid "Fire / select"
 msgstr "G?? til venstre"
 
-#: engines/scumm/metaengine.cpp:1186
+#: engines/scumm/metaengine.cpp:1198
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Walk / menu back"
 msgstr "Hopp over hovedmeny"
 
-#: engines/scumm/metaengine.cpp:1191
+#: engines/scumm/metaengine.cpp:1203
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu back"
 msgstr "Hopp over hovedmeny"
 
-#: engines/scumm/metaengine.cpp:1207
+#: engines/scumm/metaengine.cpp:1219
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1262
+#: engines/scumm/metaengine.cpp:1274
 #, fuzzy
 #| msgid "Move Back"
 msgid "Cover / back"
 msgstr "Beveg Bakover"
 
-#: engines/scumm/metaengine.cpp:1268
+#: engines/scumm/metaengine.cpp:1280
 #, fuzzy
 #| msgid "Skip dialogue"
 msgid "Skip / back"
 msgstr "Hopp over dialog"
 
-#: engines/scumm/metaengine.cpp:1275
+#: engines/scumm/metaengine.cpp:1287
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu"
diff --git a/po/nl_NL.po b/po/nl_NL.po
index 1ea3795dc78..0ef2ea79e66 100644
--- a/po/nl_NL.po
+++ b/po/nl_NL.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.9.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-06 11:26+0000\n"
+"POT-Creation-Date: 2026-06-07 21:08+0000\n"
 "PO-Revision-Date: 2026-02-11 14:23+0000\n"
 "Last-Translator: SecularSteve <fairfull.playing at gmail.com>\n"
 "Language-Team: Dutch <https://translations.scummvm.org/projects/scummvm/"
@@ -1053,7 +1053,7 @@ msgstr "Interactie"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1088
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1100
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1074,7 +1074,7 @@ msgstr "Omhoog"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1055
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1067
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1095,7 +1095,7 @@ msgstr "Omlaag"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1068
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1080
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1115,7 +1115,7 @@ msgstr "Links"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1075
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1087
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3896,7 +3896,7 @@ msgid "Game not implemented"
 msgstr "Spel niet ge??mplementeerd"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
-#: engines/scumm/metaengine.cpp:1038
+#: engines/scumm/metaengine.cpp:1050
 msgid "Default game keymappings"
 msgstr "Standaard speltoetsenkoppelingen"
 
@@ -6087,7 +6087,7 @@ msgstr "R2"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1196 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1208 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "Menu"
 
@@ -7552,7 +7552,7 @@ msgstr "Schakel gevecht in/uit"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1114 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1126 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -8251,7 +8251,7 @@ msgstr "Praten / Interactie"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1101
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1113
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -10317,7 +10317,7 @@ msgid "Player ammo will never decrease."
 msgstr "Munitie van speler zal niet verminderen."
 
 #: engines/hypno/metaengine.cpp:70 engines/scumm/metaengine.cpp:891
-#: engines/scumm/metaengine.cpp:900
+#: engines/scumm/metaengine.cpp:909
 msgid "Unlock all levels"
 msgstr "Geef alle levels vrij"
 
@@ -13703,116 +13703,128 @@ msgstr "Hoge resolutie aan"
 msgid "Run the game in 640x400 high resolution mode instead of 320x200"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:901
+#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:910
 #, fuzzy
 #| msgid "All levels will be available to play."
 msgid "All levels will be available without requiring passwords"
 msgstr "Alle levels kunnen dan gespeeld worden."
 
-#: engines/scumm/metaengine.cpp:1049
+#: engines/scumm/metaengine.cpp:900
+#, fuzzy
+#| msgid "No date saved"
+msgid "No damage"
+msgstr "Geen datum opgeslagen"
+
+#: engines/scumm/metaengine.cpp:901
+#, fuzzy
+#| msgid "Disable save menu"
+msgid "Disable player damage"
+msgstr "Bewaar menu uitschakelen"
+
+#: engines/scumm/metaengine.cpp:1061
 msgid "Down left"
 msgstr "Linksonder"
 
-#: engines/scumm/metaengine.cpp:1062
+#: engines/scumm/metaengine.cpp:1074
 msgid "Down right"
 msgstr "Rechtsonder"
 
-#: engines/scumm/metaengine.cpp:1082
+#: engines/scumm/metaengine.cpp:1094
 msgid "Up left"
 msgstr "Linksboven"
 
-#: engines/scumm/metaengine.cpp:1095
+#: engines/scumm/metaengine.cpp:1107
 msgid "Up right"
 msgstr "Rechtop"
 
-#: engines/scumm/metaengine.cpp:1107
+#: engines/scumm/metaengine.cpp:1119
 msgid "Switch weapon"
 msgstr "Wissel wapen"
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1121
+#: engines/scumm/metaengine.cpp:1133
 msgid "Win the bike fight cheat"
 msgstr "Win de fietsstrijd-cheat"
 
-#: engines/scumm/metaengine.cpp:1130
+#: engines/scumm/metaengine.cpp:1142
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1209
+#: engines/scumm/metaengine.cpp:1144 engines/scumm/metaengine.cpp:1221
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1214
+#: engines/scumm/metaengine.cpp:1149 engines/scumm/metaengine.cpp:1226
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1219
+#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1231
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1224
+#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1229
+#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1241
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "Pak op"
 
-#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
+#: engines/scumm/metaengine.cpp:1171 engines/scumm/metaengine.cpp:1248
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "Scroll omlaag"
 
-#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1243
+#: engines/scumm/metaengine.cpp:1178 engines/scumm/metaengine.cpp:1255
 #, fuzzy
 #| msgid "Slide left"
 msgid "Stick left"
 msgstr "Schuif naar link"
 
-#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1250
+#: engines/scumm/metaengine.cpp:1185 engines/scumm/metaengine.cpp:1262
 #, fuzzy
 #| msgid "Slide right"
 msgid "Stick right"
 msgstr "Schuif naar rechts"
 
-#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1257
+#: engines/scumm/metaengine.cpp:1192 engines/scumm/metaengine.cpp:1269
 #, fuzzy
 #| msgid "Move / Select"
 msgid "Fire / select"
 msgstr "Verplaatsen / Selecteren"
 
-#: engines/scumm/metaengine.cpp:1186
+#: engines/scumm/metaengine.cpp:1198
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Walk / menu back"
 msgstr "Hoofdmenu overslaan"
 
-#: engines/scumm/metaengine.cpp:1191
+#: engines/scumm/metaengine.cpp:1203
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu back"
 msgstr "Hoofdmenu overslaan"
 
-#: engines/scumm/metaengine.cpp:1207
+#: engines/scumm/metaengine.cpp:1219
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1262
+#: engines/scumm/metaengine.cpp:1274
 #, fuzzy
 #| msgid "Move back"
 msgid "Cover / back"
 msgstr "Teruggaan"
 
-#: engines/scumm/metaengine.cpp:1268
+#: engines/scumm/metaengine.cpp:1280
 #, fuzzy
 #| msgid "Skip walk"
 msgid "Skip / back"
 msgstr "Sla wandeling over"
 
-#: engines/scumm/metaengine.cpp:1275
+#: engines/scumm/metaengine.cpp:1287
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu"
diff --git a/po/nn_NO.po b/po/nn_NO.po
index 657c4c577bb..ad1e0f5c544 100644
--- a/po/nn_NO.po
+++ b/po/nn_NO.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-06 11:26+0000\n"
+"POT-Creation-Date: 2026-06-07 21:08+0000\n"
 "PO-Revision-Date: 2023-12-14 14:28+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Norwegian Nynorsk <https://translations.scummvm.org/projects/"
@@ -1057,7 +1057,7 @@ msgstr "Interager"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1088
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1100
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1078,7 +1078,7 @@ msgstr "Opp"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1055
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1067
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1099,7 +1099,7 @@ msgstr "Ned"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1068
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1080
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1119,7 +1119,7 @@ msgstr "Venstre"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1075
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1087
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3822,7 +3822,7 @@ msgid "Game not implemented"
 msgstr "Spel ID ikkje st??tta"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
-#: engines/scumm/metaengine.cpp:1038
+#: engines/scumm/metaengine.cpp:1050
 #, fuzzy
 #| msgid "Game Options"
 msgid "Default game keymappings"
@@ -5814,7 +5814,7 @@ msgstr ""
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1196 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1208 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "Meny"
 
@@ -7288,7 +7288,7 @@ msgstr "Veksle musl??sing"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1114 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1126 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -8036,7 +8036,7 @@ msgstr "Interpolering:"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1101
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1113
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -10250,7 +10250,7 @@ msgid "Player ammo will never decrease."
 msgstr ""
 
 #: engines/hypno/metaengine.cpp:70 engines/scumm/metaengine.cpp:891
-#: engines/scumm/metaengine.cpp:900
+#: engines/scumm/metaengine.cpp:909
 msgid "Unlock all levels"
 msgstr ""
 
@@ -13631,115 +13631,126 @@ msgstr "Nytt h??goppl??yseleg grafikk"
 msgid "Run the game in 640x400 high resolution mode instead of 320x200"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:901
+#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:910
 msgid "All levels will be available without requiring passwords"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1049
+#: engines/scumm/metaengine.cpp:900
+#, fuzzy
+#| msgid "No date saved"
+msgid "No damage"
+msgstr "Ingen dato lagra"
+
+#: engines/scumm/metaengine.cpp:901
+#, fuzzy
+msgid "Disable player damage"
+msgstr "Klarte ikkje lagre spel"
+
+#: engines/scumm/metaengine.cpp:1061
 #, fuzzy
 msgid "Down left"
 msgstr "Snu til Venstre"
 
-#: engines/scumm/metaengine.cpp:1062
+#: engines/scumm/metaengine.cpp:1074
 #, fuzzy
 msgid "Down right"
 msgstr "Snu til H??yre"
 
-#: engines/scumm/metaengine.cpp:1082
+#: engines/scumm/metaengine.cpp:1094
 #, fuzzy
 #| msgid "Upper left item"
 msgid "Up left"
 msgstr "??vre venstre gjenstand"
 
-#: engines/scumm/metaengine.cpp:1095
+#: engines/scumm/metaengine.cpp:1107
 #, fuzzy
 msgid "Up right"
 msgstr "H??gre"
 
-#: engines/scumm/metaengine.cpp:1107
+#: engines/scumm/metaengine.cpp:1119
 #, fuzzy
 msgid "Switch weapon"
 msgstr "Veksle karakterar:"
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1121
+#: engines/scumm/metaengine.cpp:1133
 msgid "Win the bike fight cheat"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1130
+#: engines/scumm/metaengine.cpp:1142
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1209
+#: engines/scumm/metaengine.cpp:1144 engines/scumm/metaengine.cpp:1221
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1214
+#: engines/scumm/metaengine.cpp:1149 engines/scumm/metaengine.cpp:1226
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1219
+#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1231
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1224
+#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1229
+#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1241
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "Plukk opp"
 
-#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
+#: engines/scumm/metaengine.cpp:1171 engines/scumm/metaengine.cpp:1248
 #, fuzzy
 msgid "Stick down"
 msgstr "Bla liste ned"
 
-#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1243
+#: engines/scumm/metaengine.cpp:1178 engines/scumm/metaengine.cpp:1255
 #, fuzzy
 #| msgid "Slide Left"
 msgid "Stick left"
 msgstr "Skli til Venstre"
 
-#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1250
+#: engines/scumm/metaengine.cpp:1185 engines/scumm/metaengine.cpp:1262
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick right"
 msgstr "Skli til H??yre"
 
-#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1257
+#: engines/scumm/metaengine.cpp:1192 engines/scumm/metaengine.cpp:1269
 #, fuzzy
 msgid "Fire / select"
 msgstr "Skli til Venstre"
 
-#: engines/scumm/metaengine.cpp:1186
+#: engines/scumm/metaengine.cpp:1198
 #, fuzzy
 msgid "Walk / menu back"
 msgstr "Tilbake til Oppsta~r~tar"
 
-#: engines/scumm/metaengine.cpp:1191
+#: engines/scumm/metaengine.cpp:1203
 #, fuzzy
 msgid "Skip / menu back"
 msgstr "Tilbake til Oppsta~r~tar"
 
-#: engines/scumm/metaengine.cpp:1207
+#: engines/scumm/metaengine.cpp:1219
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1262
+#: engines/scumm/metaengine.cpp:1274
 #, fuzzy
 #| msgid "Move Back"
 msgid "Cover / back"
 msgstr "Beveg Bakover"
 
-#: engines/scumm/metaengine.cpp:1268
+#: engines/scumm/metaengine.cpp:1280
 #, fuzzy
 msgid "Skip / back"
 msgstr "Hopp over linje"
 
-#: engines/scumm/metaengine.cpp:1275
+#: engines/scumm/metaengine.cpp:1287
 #, fuzzy
 msgid "Skip / menu"
 msgstr "Tilbake til Oppsta~r~tar"
diff --git a/po/pl_PL.po b/po/pl_PL.po
index c790a0f499e..cb5b125f96a 100644
--- a/po/pl_PL.po
+++ b/po/pl_PL.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-06 11:26+0000\n"
+"POT-Creation-Date: 2026-06-07 21:08+0000\n"
 "PO-Revision-Date: 2026-04-17 00:22+0000\n"
 "Last-Translator: SkiffPL <skiff at windowslive.com>\n"
 "Language-Team: Polish <https://translations.scummvm.org/projects/scummvm/"
@@ -1054,7 +1054,7 @@ msgstr "Interakcja"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1088
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1100
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1075,7 +1075,7 @@ msgstr "W g??r??"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1055
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1067
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1096,7 +1096,7 @@ msgstr "W d????"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1068
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1080
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1116,7 +1116,7 @@ msgstr "W lewo"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1075
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1087
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3887,7 +3887,7 @@ msgid "Game not implemented"
 msgstr "Gra nie jest obs??ugiwana"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
-#: engines/scumm/metaengine.cpp:1038
+#: engines/scumm/metaengine.cpp:1050
 msgid "Default game keymappings"
 msgstr "Domy??lne mapowanie klawiatury"
 
@@ -6099,7 +6099,7 @@ msgstr "R2"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1196 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1208 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "Menu"
 
@@ -7566,7 +7566,7 @@ msgstr "Prze????cz tryb walki"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1114 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1126 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -8234,7 +8234,7 @@ msgstr "Rozmowa/interakcja"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1101
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1113
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -10427,7 +10427,7 @@ msgid "Player ammo will never decrease."
 msgstr "Amunicja gracza nigdy si?? nie zmniejszy."
 
 #: engines/hypno/metaengine.cpp:70 engines/scumm/metaengine.cpp:891
-#: engines/scumm/metaengine.cpp:900
+#: engines/scumm/metaengine.cpp:909
 msgid "Unlock all levels"
 msgstr "Odblokuj wszystkie poziomy"
 
@@ -13882,116 +13882,128 @@ msgstr "Wysoka rozdzielczo???? w????czona"
 msgid "Run the game in 640x400 high resolution mode instead of 320x200"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:901
+#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:910
 #, fuzzy
 #| msgid "All levels will be available to play."
 msgid "All levels will be available without requiring passwords"
 msgstr "Wszystkie poziomy gry b??d?? dost??pne."
 
-#: engines/scumm/metaengine.cpp:1049
+#: engines/scumm/metaengine.cpp:900
+#, fuzzy
+#| msgid "No date saved"
+msgid "No damage"
+msgstr "Brak daty"
+
+#: engines/scumm/metaengine.cpp:901
+#, fuzzy
+#| msgid "Disable save menu"
+msgid "Disable player damage"
+msgstr "Wy????cz menu zapisu"
+
+#: engines/scumm/metaengine.cpp:1061
 msgid "Down left"
 msgstr "W d???? w lewo"
 
-#: engines/scumm/metaengine.cpp:1062
+#: engines/scumm/metaengine.cpp:1074
 msgid "Down right"
 msgstr "W d???? w prawo"
 
-#: engines/scumm/metaengine.cpp:1082
+#: engines/scumm/metaengine.cpp:1094
 msgid "Up left"
 msgstr "W g??r?? na lewo"
 
-#: engines/scumm/metaengine.cpp:1095
+#: engines/scumm/metaengine.cpp:1107
 msgid "Up right"
 msgstr "W g??r?? w prawo"
 
-#: engines/scumm/metaengine.cpp:1107
+#: engines/scumm/metaengine.cpp:1119
 msgid "Switch weapon"
 msgstr "Zmie?? bro??"
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1121
+#: engines/scumm/metaengine.cpp:1133
 msgid "Win the bike fight cheat"
 msgstr "Cheat 'wygraj walk?? na motocyklu'"
 
-#: engines/scumm/metaengine.cpp:1130
+#: engines/scumm/metaengine.cpp:1142
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1209
+#: engines/scumm/metaengine.cpp:1144 engines/scumm/metaengine.cpp:1221
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1214
+#: engines/scumm/metaengine.cpp:1149 engines/scumm/metaengine.cpp:1226
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1219
+#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1231
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1224
+#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1229
+#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1241
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "Podnie??"
 
-#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
+#: engines/scumm/metaengine.cpp:1171 engines/scumm/metaengine.cpp:1248
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "Przewi?? w d????"
 
-#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1243
+#: engines/scumm/metaengine.cpp:1178 engines/scumm/metaengine.cpp:1255
 #, fuzzy
 #| msgid "Slide left"
 msgid "Stick left"
 msgstr "??lizg w lewo"
 
-#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1250
+#: engines/scumm/metaengine.cpp:1185 engines/scumm/metaengine.cpp:1262
 #, fuzzy
 #| msgid "Slide right"
 msgid "Stick right"
 msgstr "??lizg w prawo"
 
-#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1257
+#: engines/scumm/metaengine.cpp:1192 engines/scumm/metaengine.cpp:1269
 #, fuzzy
 #| msgid "Move / Select"
 msgid "Fire / select"
 msgstr "Ruch / Zaznacz"
 
-#: engines/scumm/metaengine.cpp:1186
+#: engines/scumm/metaengine.cpp:1198
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Walk / menu back"
 msgstr "Pomijaj menu g????wne"
 
-#: engines/scumm/metaengine.cpp:1191
+#: engines/scumm/metaengine.cpp:1203
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu back"
 msgstr "Pomijaj menu g????wne"
 
-#: engines/scumm/metaengine.cpp:1207
+#: engines/scumm/metaengine.cpp:1219
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1262
+#: engines/scumm/metaengine.cpp:1274
 #, fuzzy
 #| msgid "Move back"
 msgid "Cover / back"
 msgstr "Ruch do ty??u"
 
-#: engines/scumm/metaengine.cpp:1268
+#: engines/scumm/metaengine.cpp:1280
 #, fuzzy
 #| msgid "Skip dialog"
 msgid "Skip / back"
 msgstr "Pomi?? dialog"
 
-#: engines/scumm/metaengine.cpp:1275
+#: engines/scumm/metaengine.cpp:1287
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu"
diff --git a/po/pt_BR.po b/po/pt_BR.po
index 91ecef4ce23..13e3d5e665b 100644
--- a/po/pt_BR.po
+++ b/po/pt_BR.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-06 11:26+0000\n"
+"POT-Creation-Date: 2026-06-07 21:08+0000\n"
 "PO-Revision-Date: 2024-11-16 02:13+0000\n"
 "Last-Translator: Marcel Souza Lemes <marcosoutsider at gmail.com>\n"
 "Language-Team: Portuguese (Brazil) <https://translations.scummvm.org/"
@@ -1059,7 +1059,7 @@ msgstr "Interagir"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1088
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1100
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1080,7 +1080,7 @@ msgstr "Cima"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1055
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1067
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1101,7 +1101,7 @@ msgstr "Baixo"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1068
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1080
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1121,7 +1121,7 @@ msgstr "Esquerda"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1075
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1087
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3981,7 +3981,7 @@ msgid "Game not implemented"
 msgstr "Jogo n??o implementado"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
-#: engines/scumm/metaengine.cpp:1038
+#: engines/scumm/metaengine.cpp:1050
 #, fuzzy
 #| msgid "Default game keymap"
 msgid "Default game keymappings"
@@ -6376,7 +6376,7 @@ msgstr "R2"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1196 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1208 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "Menu"
 
@@ -7881,7 +7881,7 @@ msgstr "Alternar Modo de Combate"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1114 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1126 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -8645,7 +8645,7 @@ msgstr "Falar/Interagir"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1101
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1113
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -10889,7 +10889,7 @@ msgid "Player ammo will never decrease."
 msgstr "A muni????o do jogador nunca diminuir??."
 
 #: engines/hypno/metaengine.cpp:70 engines/scumm/metaengine.cpp:891
-#: engines/scumm/metaengine.cpp:900
+#: engines/scumm/metaengine.cpp:909
 msgid "Unlock all levels"
 msgstr "Desbloquear todos os n??veis"
 
@@ -14474,124 +14474,136 @@ msgstr "Alta resolu????o ativada"
 msgid "Run the game in 640x400 high resolution mode instead of 320x200"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:901
+#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:910
 #, fuzzy
 #| msgid "All levels will be available to play."
 msgid "All levels will be available without requiring passwords"
 msgstr "Todos os n??veis estar??o dispon??veis para jogar."
 
-#: engines/scumm/metaengine.cpp:1049
+#: engines/scumm/metaengine.cpp:900
+#, fuzzy
+#| msgid "No date saved"
+msgid "No damage"
+msgstr "Sem data salva"
+
+#: engines/scumm/metaengine.cpp:901
+#, fuzzy
+#| msgid "Disable save menu"
+msgid "Disable player damage"
+msgstr "Desabilitar menu de salvar"
+
+#: engines/scumm/metaengine.cpp:1061
 #, fuzzy
 #| msgid "Down Left"
 msgid "Down left"
 msgstr "Para Baixo ?? Esquerda"
 
-#: engines/scumm/metaengine.cpp:1062
+#: engines/scumm/metaengine.cpp:1074
 #, fuzzy
 #| msgid "Down Right"
 msgid "Down right"
 msgstr "Para Baixo ?? Direita"
 
-#: engines/scumm/metaengine.cpp:1082
+#: engines/scumm/metaengine.cpp:1094
 #, fuzzy
 #| msgid "Upper left item"
 msgid "Up left"
 msgstr "Item da esquerda superior"
 
-#: engines/scumm/metaengine.cpp:1095
+#: engines/scumm/metaengine.cpp:1107
 #, fuzzy
 #| msgid "Up Right"
 msgid "Up right"
 msgstr "Para Cima ?? Direita"
 
-#: engines/scumm/metaengine.cpp:1107
+#: engines/scumm/metaengine.cpp:1119
 msgid "Switch weapon"
 msgstr "Trocar arma"
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1121
+#: engines/scumm/metaengine.cpp:1133
 msgid "Win the bike fight cheat"
 msgstr "Trapa??a para vencer a briga de motos"
 
-#: engines/scumm/metaengine.cpp:1130
+#: engines/scumm/metaengine.cpp:1142
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1209
+#: engines/scumm/metaengine.cpp:1144 engines/scumm/metaengine.cpp:1221
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1214
+#: engines/scumm/metaengine.cpp:1149 engines/scumm/metaengine.cpp:1226
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1219
+#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1231
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1224
+#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1229
+#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1241
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "Pegar"
 
-#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
+#: engines/scumm/metaengine.cpp:1171 engines/scumm/metaengine.cpp:1248
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "Rolar para baixo"
 
-#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1243
+#: engines/scumm/metaengine.cpp:1178 engines/scumm/metaengine.cpp:1255
 #, fuzzy
 #| msgid "Slide Left"
 msgid "Stick left"
 msgstr "Deslizar para Esquerda"
 
-#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1250
+#: engines/scumm/metaengine.cpp:1185 engines/scumm/metaengine.cpp:1262
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick right"
 msgstr "Deslizar para Direita"
 
-#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1257
+#: engines/scumm/metaengine.cpp:1192 engines/scumm/metaengine.cpp:1269
 #, fuzzy
 #| msgid "Move left"
 msgid "Fire / select"
 msgstr "Mover para esquerda"
 
-#: engines/scumm/metaengine.cpp:1186
+#: engines/scumm/metaengine.cpp:1198
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Walk / menu back"
 msgstr "Omitir menu principal"
 
-#: engines/scumm/metaengine.cpp:1191
+#: engines/scumm/metaengine.cpp:1203
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu back"
 msgstr "Omitir menu principal"
 
-#: engines/scumm/metaengine.cpp:1207
+#: engines/scumm/metaengine.cpp:1219
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1262
+#: engines/scumm/metaengine.cpp:1274
 #, fuzzy
 #| msgid "Move Back"
 msgid "Cover / back"
 msgstr "Mover para Tr??s"
 
-#: engines/scumm/metaengine.cpp:1268
+#: engines/scumm/metaengine.cpp:1280
 #, fuzzy
 #| msgid "Skip dialogue"
 msgid "Skip / back"
 msgstr "Pular di??logo"
 
-#: engines/scumm/metaengine.cpp:1275
+#: engines/scumm/metaengine.cpp:1287
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu"
diff --git a/po/pt_PT.po b/po/pt_PT.po
index c038eba1e80..4f34eb04ae2 100644
--- a/po/pt_PT.po
+++ b/po/pt_PT.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.10.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-06 11:26+0000\n"
+"POT-Creation-Date: 2026-06-07 21:08+0000\n"
 "PO-Revision-Date: 2022-06-05 14:09+0000\n"
 "Last-Translator: Daniel <supsuper at gmail.com>\n"
 "Language-Team: Portuguese (Portugal) <https://translations.scummvm.org/"
@@ -1072,7 +1072,7 @@ msgstr "Interagir"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1088
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1100
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1093,7 +1093,7 @@ msgstr "Cima"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1055
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1067
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1114,7 +1114,7 @@ msgstr "Baixo"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1068
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1080
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1134,7 +1134,7 @@ msgstr "Esquerda"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1075
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1087
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3855,7 +3855,7 @@ msgid "Game not implemented"
 msgstr "ID de jogo n??o suportado"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
-#: engines/scumm/metaengine.cpp:1038
+#: engines/scumm/metaengine.cpp:1050
 #, fuzzy
 #| msgid "Default game keymap"
 msgid "Default game keymappings"
@@ -5838,7 +5838,7 @@ msgstr "R2"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1196 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1208 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "Menu"
 
@@ -7320,7 +7320,7 @@ msgstr "Ativar/desativar combate"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1114 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1126 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -8066,7 +8066,7 @@ msgstr "Interagir"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1101
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1113
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -10299,7 +10299,7 @@ msgid "Player ammo will never decrease."
 msgstr ""
 
 #: engines/hypno/metaengine.cpp:70 engines/scumm/metaengine.cpp:891
-#: engines/scumm/metaengine.cpp:900
+#: engines/scumm/metaengine.cpp:909
 msgid "Unlock all levels"
 msgstr ""
 
@@ -13753,118 +13753,129 @@ msgstr "Alta resolu????o ativada"
 msgid "Run the game in 640x400 high resolution mode instead of 320x200"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:901
+#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:910
 msgid "All levels will be available without requiring passwords"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1049
+#: engines/scumm/metaengine.cpp:900
+#, fuzzy
+#| msgid "No date saved"
+msgid "No damage"
+msgstr "Nenhuma data guardada"
+
+#: engines/scumm/metaengine.cpp:901
+#, fuzzy
+msgid "Disable player damage"
+msgstr "Falha a guardar o jogo"
+
+#: engines/scumm/metaengine.cpp:1061
 #, fuzzy
 msgid "Down left"
 msgstr "Girar ?? esquerda"
 
-#: engines/scumm/metaengine.cpp:1062
+#: engines/scumm/metaengine.cpp:1074
 #, fuzzy
 msgid "Down right"
 msgstr "Girar ?? direita"
 
-#: engines/scumm/metaengine.cpp:1082
+#: engines/scumm/metaengine.cpp:1094
 #, fuzzy
 #| msgid "Move left"
 msgid "Up left"
 msgstr "Mover para a esquerda"
 
-#: engines/scumm/metaengine.cpp:1095
+#: engines/scumm/metaengine.cpp:1107
 #, fuzzy
 msgid "Up right"
 msgstr "Direita"
 
-#: engines/scumm/metaengine.cpp:1107
+#: engines/scumm/metaengine.cpp:1119
 #, fuzzy
 msgid "Switch weapon"
 msgstr "Trocar ecr??s"
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1121
+#: engines/scumm/metaengine.cpp:1133
 msgid "Win the bike fight cheat"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1130
+#: engines/scumm/metaengine.cpp:1142
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1209
+#: engines/scumm/metaengine.cpp:1144 engines/scumm/metaengine.cpp:1221
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1214
+#: engines/scumm/metaengine.cpp:1149 engines/scumm/metaengine.cpp:1226
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1219
+#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1231
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1224
+#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1229
+#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1241
 #, fuzzy
 #| msgid "Left Stick X"
 msgid "Stick up"
 msgstr "Anal??gico Esquerdo X"
 
-#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
+#: engines/scumm/metaengine.cpp:1171 engines/scumm/metaengine.cpp:1248
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "Deslizar para baixo"
 
-#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1243
+#: engines/scumm/metaengine.cpp:1178 engines/scumm/metaengine.cpp:1255
 #, fuzzy
 #| msgid "Slide Left"
 msgid "Stick left"
 msgstr "Deslizar ?? esquerda"
 
-#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1250
+#: engines/scumm/metaengine.cpp:1185 engines/scumm/metaengine.cpp:1262
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick right"
 msgstr "Deslizar ?? direita"
 
-#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1257
+#: engines/scumm/metaengine.cpp:1192 engines/scumm/metaengine.cpp:1269
 #, fuzzy
 #| msgid "Move left"
 msgid "Fire / select"
 msgstr "Mover para a esquerda"
 
-#: engines/scumm/metaengine.cpp:1186
+#: engines/scumm/metaengine.cpp:1198
 #, fuzzy
 msgid "Walk / menu back"
 msgstr "~M~enu principal"
 
-#: engines/scumm/metaengine.cpp:1191
+#: engines/scumm/metaengine.cpp:1203
 #, fuzzy
 msgid "Skip / menu back"
 msgstr "~M~enu principal"
 
-#: engines/scumm/metaengine.cpp:1207
+#: engines/scumm/metaengine.cpp:1219
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1262
+#: engines/scumm/metaengine.cpp:1274
 #, fuzzy
 #| msgid "Move Back"
 msgid "Cover / back"
 msgstr "Retroceder"
 
-#: engines/scumm/metaengine.cpp:1268
+#: engines/scumm/metaengine.cpp:1280
 #, fuzzy
 #| msgid "Skip dialogue"
 msgid "Skip / back"
 msgstr "Ignorar frase"
 
-#: engines/scumm/metaengine.cpp:1275
+#: engines/scumm/metaengine.cpp:1287
 #, fuzzy
 msgid "Skip / menu"
 msgstr "~M~enu principal"
diff --git a/po/ro.po b/po/ro.po
index 8609f3c0cba..b074276637b 100644
--- a/po/ro.po
+++ b/po/ro.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.10.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-06 11:26+0000\n"
+"POT-Creation-Date: 2026-06-07 21:08+0000\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: Automatically generated\n"
 "Language-Team: none\n"
@@ -1013,7 +1013,7 @@ msgstr ""
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1088
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1100
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1034,7 +1034,7 @@ msgstr ""
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1055
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1067
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1055,7 +1055,7 @@ msgstr ""
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1068
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1080
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1075,7 +1075,7 @@ msgstr ""
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1075
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1087
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3588,7 +3588,7 @@ msgid "Game not implemented"
 msgstr ""
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
-#: engines/scumm/metaengine.cpp:1038
+#: engines/scumm/metaengine.cpp:1050
 msgid "Default game keymappings"
 msgstr ""
 
@@ -5434,7 +5434,7 @@ msgstr ""
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1196 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1208 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr ""
 
@@ -6795,7 +6795,7 @@ msgstr ""
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1114 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1126 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -7442,7 +7442,7 @@ msgstr ""
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1101
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1113
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -9329,7 +9329,7 @@ msgid "Player ammo will never decrease."
 msgstr ""
 
 #: engines/hypno/metaengine.cpp:70 engines/scumm/metaengine.cpp:891
-#: engines/scumm/metaengine.cpp:900
+#: engines/scumm/metaengine.cpp:909
 msgid "Unlock all levels"
 msgstr ""
 
@@ -12389,96 +12389,104 @@ msgstr ""
 msgid "Run the game in 640x400 high resolution mode instead of 320x200"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:901
+#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:910
 msgid "All levels will be available without requiring passwords"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1049
+#: engines/scumm/metaengine.cpp:900
+msgid "No damage"
+msgstr ""
+
+#: engines/scumm/metaengine.cpp:901
+msgid "Disable player damage"
+msgstr ""
+
+#: engines/scumm/metaengine.cpp:1061
 msgid "Down left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1062
+#: engines/scumm/metaengine.cpp:1074
 msgid "Down right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1082
+#: engines/scumm/metaengine.cpp:1094
 msgid "Up left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1095
+#: engines/scumm/metaengine.cpp:1107
 msgid "Up right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1107
+#: engines/scumm/metaengine.cpp:1119
 msgid "Switch weapon"
 msgstr ""
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1121
+#: engines/scumm/metaengine.cpp:1133
 msgid "Win the bike fight cheat"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1130
+#: engines/scumm/metaengine.cpp:1142
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1209
+#: engines/scumm/metaengine.cpp:1144 engines/scumm/metaengine.cpp:1221
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1214
+#: engines/scumm/metaengine.cpp:1149 engines/scumm/metaengine.cpp:1226
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1219
+#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1231
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1224
+#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1229
+#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1241
 msgid "Stick up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
+#: engines/scumm/metaengine.cpp:1171 engines/scumm/metaengine.cpp:1248
 msgid "Stick down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1243
+#: engines/scumm/metaengine.cpp:1178 engines/scumm/metaengine.cpp:1255
 msgid "Stick left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1250
+#: engines/scumm/metaengine.cpp:1185 engines/scumm/metaengine.cpp:1262
 msgid "Stick right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1257
+#: engines/scumm/metaengine.cpp:1192 engines/scumm/metaengine.cpp:1269
 msgid "Fire / select"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1186
+#: engines/scumm/metaengine.cpp:1198
 msgid "Walk / menu back"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1191
+#: engines/scumm/metaengine.cpp:1203
 msgid "Skip / menu back"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1207
+#: engines/scumm/metaengine.cpp:1219
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1262
+#: engines/scumm/metaengine.cpp:1274
 msgid "Cover / back"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1268
+#: engines/scumm/metaengine.cpp:1280
 msgid "Skip / back"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1275
+#: engines/scumm/metaengine.cpp:1287
 msgid "Skip / menu"
 msgstr ""
 
diff --git a/po/ru_RU.po b/po/ru_RU.po
index 6118daf1595..399e20a1fe1 100644
--- a/po/ru_RU.po
+++ b/po/ru_RU.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.8.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-06 11:26+0000\n"
+"POT-Creation-Date: 2026-06-07 21:08+0000\n"
 "PO-Revision-Date: 2026-05-06 08:54+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Russian <https://translations.scummvm.org/projects/scummvm/"
@@ -1051,7 +1051,7 @@ msgstr "????????????????????????????"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1088
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1100
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1072,7 +1072,7 @@ msgstr "??????????"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1055
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1067
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1093,7 +1093,7 @@ msgstr "????????"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1068
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1080
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1113,7 +1113,7 @@ msgstr "??????????"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1075
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1087
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3896,7 +3896,7 @@ msgid "Game not implemented"
 msgstr "???????? ???? ??????????????????????"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
-#: engines/scumm/metaengine.cpp:1038
+#: engines/scumm/metaengine.cpp:1050
 msgid "Default game keymappings"
 msgstr "?????????????????? ???????????? ???????? ???? ??????????????????"
 
@@ -6109,7 +6109,7 @@ msgstr "R2"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1196 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1208 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "????????"
 
@@ -7566,7 +7566,7 @@ msgstr "?????????????????????? ??????"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1114 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1126 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -8229,7 +8229,7 @@ msgstr "???????????????? / ??????????????????????????????????"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1101
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1113
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -10197,7 +10197,7 @@ msgid "Player ammo will never decrease."
 msgstr "???????????????????? ???????????? ?????????????? ???? ??????????????????????."
 
 #: engines/hypno/metaengine.cpp:70 engines/scumm/metaengine.cpp:891
-#: engines/scumm/metaengine.cpp:900
+#: engines/scumm/metaengine.cpp:909
 msgid "Unlock all levels"
 msgstr "???????????????????????????? ?????? ????????????"
 
@@ -13551,116 +13551,128 @@ msgstr "?????????????? ???????????????????? ????????????????"
 msgid "Run the game in 640x400 high resolution mode instead of 320x200"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:901
+#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:910
 #, fuzzy
 #| msgid "All levels will be available to play."
 msgid "All levels will be available without requiring passwords"
 msgstr "?????? ???????????? ?????????? ???????????????? ?????? ????????."
 
-#: engines/scumm/metaengine.cpp:1049
+#: engines/scumm/metaengine.cpp:900
+#, fuzzy
+#| msgid "No date saved"
+msgid "No damage"
+msgstr "???????? ???? ????????????????"
+
+#: engines/scumm/metaengine.cpp:901
+#, fuzzy
+#| msgid "Disable save menu"
+msgid "Disable player damage"
+msgstr "?????????????????? ???????? ????????????????????"
+
+#: engines/scumm/metaengine.cpp:1061
 msgid "Down left"
 msgstr "????????-??????????"
 
-#: engines/scumm/metaengine.cpp:1062
+#: engines/scumm/metaengine.cpp:1074
 msgid "Down right"
 msgstr "????????-????????????"
 
-#: engines/scumm/metaengine.cpp:1082
+#: engines/scumm/metaengine.cpp:1094
 msgid "Up left"
 msgstr "??????????-??????????"
 
-#: engines/scumm/metaengine.cpp:1095
+#: engines/scumm/metaengine.cpp:1107
 msgid "Up right"
 msgstr "??????????-????????????"
 
-#: engines/scumm/metaengine.cpp:1107
+#: engines/scumm/metaengine.cpp:1119
 msgid "Switch weapon"
 msgstr "?????????????????????? ????????????"
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1121
+#: engines/scumm/metaengine.cpp:1133
 msgid "Win the bike fight cheat"
 msgstr "?????? ?????? ???????????????? ???????? ???? ????????????"
 
-#: engines/scumm/metaengine.cpp:1130
+#: engines/scumm/metaengine.cpp:1142
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1209
+#: engines/scumm/metaengine.cpp:1144 engines/scumm/metaengine.cpp:1221
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1214
+#: engines/scumm/metaengine.cpp:1149 engines/scumm/metaengine.cpp:1226
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1219
+#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1231
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1224
+#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1229
+#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1241
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "??????????????"
 
-#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
+#: engines/scumm/metaengine.cpp:1171 engines/scumm/metaengine.cpp:1248
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "???????????????????? ????????"
 
-#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1243
+#: engines/scumm/metaengine.cpp:1178 engines/scumm/metaengine.cpp:1255
 #, fuzzy
 #| msgid "Slide left"
 msgid "Stick left"
 msgstr "?????????????????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1250
+#: engines/scumm/metaengine.cpp:1185 engines/scumm/metaengine.cpp:1262
 #, fuzzy
 #| msgid "Slide right"
 msgid "Stick right"
 msgstr "?????????????????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1257
+#: engines/scumm/metaengine.cpp:1192 engines/scumm/metaengine.cpp:1269
 #, fuzzy
 #| msgid "Move / Select"
 msgid "Fire / select"
 msgstr "???????? / ??????????????"
 
-#: engines/scumm/metaengine.cpp:1186
+#: engines/scumm/metaengine.cpp:1198
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Walk / menu back"
 msgstr "???????????????????? ?????????????? ????????"
 
-#: engines/scumm/metaengine.cpp:1191
+#: engines/scumm/metaengine.cpp:1203
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu back"
 msgstr "???????????????????? ?????????????? ????????"
 
-#: engines/scumm/metaengine.cpp:1207
+#: engines/scumm/metaengine.cpp:1219
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1262
+#: engines/scumm/metaengine.cpp:1274
 #, fuzzy
 #| msgid "Move back"
 msgid "Cover / back"
 msgstr "???????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1268
+#: engines/scumm/metaengine.cpp:1280
 #, fuzzy
 #| msgid "Skip walk"
 msgid "Skip / back"
 msgstr "???????????????????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1275
+#: engines/scumm/metaengine.cpp:1287
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu"
diff --git a/po/sv_SE.po b/po/sv_SE.po
index f00f664f6d2..efad31c10fa 100644
--- a/po/sv_SE.po
+++ b/po/sv_SE.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.5.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-06 11:26+0000\n"
+"POT-Creation-Date: 2026-06-07 21:08+0000\n"
 "PO-Revision-Date: 2026-04-19 13:22+0000\n"
 "Last-Translator: Daniel Nylander <po at danielnylander.se>\n"
 "Language-Team: Swedish <https://translations.scummvm.org/projects/scummvm/"
@@ -1053,7 +1053,7 @@ msgstr "Interagera"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1088
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1100
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1074,7 +1074,7 @@ msgstr "Upp"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1055
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1067
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1095,7 +1095,7 @@ msgstr "Ner"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1068
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1080
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1115,7 +1115,7 @@ msgstr "V??nster"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1075
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1087
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3886,7 +3886,7 @@ msgid "Game not implemented"
 msgstr "Spelet ??r inte implementerat"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
-#: engines/scumm/metaengine.cpp:1038
+#: engines/scumm/metaengine.cpp:1050
 msgid "Default game keymappings"
 msgstr "Standardinst??llningar f??r tangentbordskommandon i spelet"
 
@@ -6086,7 +6086,7 @@ msgstr "R2"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1196 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1208 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "Meny"
 
@@ -7541,7 +7541,7 @@ msgstr "V??xla strid"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1114 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1126 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -8209,7 +8209,7 @@ msgstr "Prata/Interagera"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1101
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1113
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -10238,7 +10238,7 @@ msgid "Player ammo will never decrease."
 msgstr "Spelarens ammunition kommer aldrig att minska."
 
 #: engines/hypno/metaengine.cpp:70 engines/scumm/metaengine.cpp:891
-#: engines/scumm/metaengine.cpp:900
+#: engines/scumm/metaengine.cpp:909
 msgid "Unlock all levels"
 msgstr "L??s upp alla niv??er"
 
@@ -13600,116 +13600,128 @@ msgstr "H??guppl??st grafik p??"
 msgid "Run the game in 640x400 high resolution mode instead of 320x200"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:901
+#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:910
 #, fuzzy
 #| msgid "All levels will be available to play."
 msgid "All levels will be available without requiring passwords"
 msgstr "Alla niv??er kommer att finnas tillg??ngliga att spela."
 
-#: engines/scumm/metaengine.cpp:1049
+#: engines/scumm/metaengine.cpp:900
+#, fuzzy
+#| msgid "No date saved"
+msgid "No damage"
+msgstr "Inget datum sparat"
+
+#: engines/scumm/metaengine.cpp:901
+#, fuzzy
+#| msgid "Disable save menu"
+msgid "Disable player damage"
+msgstr "Inaktivera sparningsmeny"
+
+#: engines/scumm/metaengine.cpp:1061
 msgid "Down left"
 msgstr "Ned??t v??nster"
 
-#: engines/scumm/metaengine.cpp:1062
+#: engines/scumm/metaengine.cpp:1074
 msgid "Down right"
 msgstr "Ned??t h??ger"
 
-#: engines/scumm/metaengine.cpp:1082
+#: engines/scumm/metaengine.cpp:1094
 msgid "Up left"
 msgstr "Upp??t v??nster"
 
-#: engines/scumm/metaengine.cpp:1095
+#: engines/scumm/metaengine.cpp:1107
 msgid "Up right"
 msgstr "Upp??t h??ger"
 
-#: engines/scumm/metaengine.cpp:1107
+#: engines/scumm/metaengine.cpp:1119
 msgid "Switch weapon"
 msgstr "Byt vapen"
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1121
+#: engines/scumm/metaengine.cpp:1133
 msgid "Win the bike fight cheat"
 msgstr "Vinn cykelkampsfusket"
 
-#: engines/scumm/metaengine.cpp:1130
+#: engines/scumm/metaengine.cpp:1142
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1209
+#: engines/scumm/metaengine.cpp:1144 engines/scumm/metaengine.cpp:1221
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1214
+#: engines/scumm/metaengine.cpp:1149 engines/scumm/metaengine.cpp:1226
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1219
+#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1231
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1224
+#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1229
+#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1241
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "Ta"
 
-#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
+#: engines/scumm/metaengine.cpp:1171 engines/scumm/metaengine.cpp:1248
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "Bl??ddra ned??t"
 
-#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1243
+#: engines/scumm/metaengine.cpp:1178 engines/scumm/metaengine.cpp:1255
 #, fuzzy
 #| msgid "Slide left"
 msgid "Stick left"
 msgstr "Glid v??nster"
 
-#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1250
+#: engines/scumm/metaengine.cpp:1185 engines/scumm/metaengine.cpp:1262
 #, fuzzy
 #| msgid "Slide right"
 msgid "Stick right"
 msgstr "Glid h??ger"
 
-#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1257
+#: engines/scumm/metaengine.cpp:1192 engines/scumm/metaengine.cpp:1269
 #, fuzzy
 #| msgid "Move / Select"
 msgid "Fire / select"
 msgstr "Flytta / V??lj"
 
-#: engines/scumm/metaengine.cpp:1186
+#: engines/scumm/metaengine.cpp:1198
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Walk / menu back"
 msgstr "Hoppa ??ver huvudmeny"
 
-#: engines/scumm/metaengine.cpp:1191
+#: engines/scumm/metaengine.cpp:1203
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu back"
 msgstr "Hoppa ??ver huvudmeny"
 
-#: engines/scumm/metaengine.cpp:1207
+#: engines/scumm/metaengine.cpp:1219
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1262
+#: engines/scumm/metaengine.cpp:1274
 #, fuzzy
 #| msgid "Move back"
 msgid "Cover / back"
 msgstr "Stega bak??t"
 
-#: engines/scumm/metaengine.cpp:1268
+#: engines/scumm/metaengine.cpp:1280
 #, fuzzy
 #| msgid "Skip walk"
 msgid "Skip / back"
 msgstr "Hoppa ??ver g??ng"
 
-#: engines/scumm/metaengine.cpp:1275
+#: engines/scumm/metaengine.cpp:1287
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu"
diff --git a/po/tr.po b/po/tr.po
index 9ffbb03cd1c..2730a1f1239 100644
--- a/po/tr.po
+++ b/po/tr.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-06 11:26+0000\n"
+"POT-Creation-Date: 2026-06-07 21:08+0000\n"
 "PO-Revision-Date: 2024-10-10 12:33+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Turkish <https://translations.scummvm.org/projects/scummvm/"
@@ -1057,7 +1057,7 @@ msgstr "Etkile??im"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1088
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1100
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1078,7 +1078,7 @@ msgstr "Yukar??"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1055
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1067
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1099,7 +1099,7 @@ msgstr "A??a????"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1068
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1080
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1119,7 +1119,7 @@ msgstr "Sol"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1075
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1087
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3832,7 +3832,7 @@ msgid "Game not implemented"
 msgstr "Oyun ID si desteklenmiyor"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
-#: engines/scumm/metaengine.cpp:1038
+#: engines/scumm/metaengine.cpp:1050
 #, fuzzy
 msgid "Default game keymappings"
 msgstr "A~y~arlar..."
@@ -5762,7 +5762,7 @@ msgstr ""
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1196 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1208 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr ""
 
@@ -7194,7 +7194,7 @@ msgstr "Altyaz??lar"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1114 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1126 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -7904,7 +7904,7 @@ msgstr "Etkile??im"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1101
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1113
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -9981,7 +9981,7 @@ msgid "Player ammo will never decrease."
 msgstr ""
 
 #: engines/hypno/metaengine.cpp:70 engines/scumm/metaengine.cpp:891
-#: engines/scumm/metaengine.cpp:900
+#: engines/scumm/metaengine.cpp:909
 msgid "Unlock all levels"
 msgstr ""
 
@@ -13258,111 +13258,123 @@ msgstr ""
 msgid "Run the game in 640x400 high resolution mode instead of 320x200"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:901
+#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:910
 msgid "All levels will be available without requiring passwords"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1049
+#: engines/scumm/metaengine.cpp:900
+#, fuzzy
+#| msgid "No date saved"
+msgid "No damage"
+msgstr "Tarih kaydedilmemi??"
+
+#: engines/scumm/metaengine.cpp:901
+#, fuzzy
+#| msgid "Playback"
+msgid "Disable player damage"
+msgstr "Oynatma"
+
+#: engines/scumm/metaengine.cpp:1061
 #, fuzzy
 msgid "Down left"
 msgstr "Sol"
 
-#: engines/scumm/metaengine.cpp:1062
+#: engines/scumm/metaengine.cpp:1074
 #, fuzzy
 msgid "Down right"
 msgstr "Sa??"
 
-#: engines/scumm/metaengine.cpp:1082
+#: engines/scumm/metaengine.cpp:1094
 #, fuzzy
 msgid "Up left"
 msgstr "Sol"
 
-#: engines/scumm/metaengine.cpp:1095
+#: engines/scumm/metaengine.cpp:1107
 #, fuzzy
 msgid "Up right"
 msgstr "Sa??"
 
-#: engines/scumm/metaengine.cpp:1107
+#: engines/scumm/metaengine.cpp:1119
 #, fuzzy
 msgid "Switch weapon"
 msgstr "Oyunu se??"
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1121
+#: engines/scumm/metaengine.cpp:1133
 msgid "Win the bike fight cheat"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1130
+#: engines/scumm/metaengine.cpp:1142
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1209
+#: engines/scumm/metaengine.cpp:1144 engines/scumm/metaengine.cpp:1221
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1214
+#: engines/scumm/metaengine.cpp:1149 engines/scumm/metaengine.cpp:1226
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1219
+#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1231
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1224
+#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1229
+#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1241
 msgid "Stick up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
+#: engines/scumm/metaengine.cpp:1171 engines/scumm/metaengine.cpp:1248
 #, fuzzy
 msgid "Stick down"
 msgstr "A??a????"
 
-#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1243
+#: engines/scumm/metaengine.cpp:1178 engines/scumm/metaengine.cpp:1255
 #, fuzzy
 msgid "Stick left"
 msgstr "Sa??"
 
-#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1250
+#: engines/scumm/metaengine.cpp:1185 engines/scumm/metaengine.cpp:1262
 #, fuzzy
 msgid "Stick right"
 msgstr "Sa??"
 
-#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1257
+#: engines/scumm/metaengine.cpp:1192 engines/scumm/metaengine.cpp:1269
 #, fuzzy
 msgid "Fire / select"
 msgstr "Sol"
 
-#: engines/scumm/metaengine.cpp:1186
+#: engines/scumm/metaengine.cpp:1198
 #, fuzzy
 msgid "Walk / menu back"
 msgstr "??ptal"
 
-#: engines/scumm/metaengine.cpp:1191
+#: engines/scumm/metaengine.cpp:1203
 #, fuzzy
 msgid "Skip / menu back"
 msgstr "??ptal"
 
-#: engines/scumm/metaengine.cpp:1207
+#: engines/scumm/metaengine.cpp:1219
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1262
+#: engines/scumm/metaengine.cpp:1274
 #, fuzzy
 #| msgid "Backend"
 msgid "Cover / back"
 msgstr "Altyap??"
 
-#: engines/scumm/metaengine.cpp:1268
+#: engines/scumm/metaengine.cpp:1280
 #, fuzzy
 #| msgid "Show hidden files"
 msgid "Skip / back"
 msgstr "Gizli dosyalar?? g??ster"
 
-#: engines/scumm/metaengine.cpp:1275
+#: engines/scumm/metaengine.cpp:1287
 #, fuzzy
 msgid "Skip / menu"
 msgstr "??ptal"
diff --git a/po/uk_UA.po b/po/uk_UA.po
index 0863ac82e01..cec453858f0 100644
--- a/po/uk_UA.po
+++ b/po/uk_UA.po
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.9.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-06 11:26+0000\n"
+"POT-Creation-Date: 2026-06-07 21:08+0000\n"
 "PO-Revision-Date: 2026-05-06 08:54+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Ukrainian <https://translations.scummvm.org/projects/scummvm/"
@@ -1055,7 +1055,7 @@ msgstr "??????????????????"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1088
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1100
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1076,7 +1076,7 @@ msgstr "????????????"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1055
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1067
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1097,7 +1097,7 @@ msgstr "????????????"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1068
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1080
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1117,7 +1117,7 @@ msgstr "????????????"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1075
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1087
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3890,7 +3890,7 @@ msgid "Game not implemented"
 msgstr "?????? ???? ??????????????????????"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
-#: engines/scumm/metaengine.cpp:1038
+#: engines/scumm/metaengine.cpp:1050
 msgid "Default game keymappings"
 msgstr "???????? ???????????? ?????? ???? ??????????????????????"
 
@@ -6095,7 +6095,7 @@ msgstr "R2"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1196 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1208 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "????????"
 
@@ -7546,7 +7546,7 @@ msgstr "???????????????????? ?????????? ??????"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1114 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1126 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -8210,7 +8210,7 @@ msgstr "???????????????? / ??????????????????????"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1101
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1113
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -10176,7 +10176,7 @@ msgid "Player ammo will never decrease."
 msgstr "???????????????????? ???????????? ???????????? ???? ??????????????????????."
 
 #: engines/hypno/metaengine.cpp:70 engines/scumm/metaengine.cpp:891
-#: engines/scumm/metaengine.cpp:900
+#: engines/scumm/metaengine.cpp:909
 msgid "Unlock all levels"
 msgstr "???????????????????????? ?????? ??????????"
 
@@ -13510,116 +13510,128 @@ msgstr "???????????? ???????????????????????? ??????????????????"
 msgid "Run the game in 640x400 high resolution mode instead of 320x200"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:901
+#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:910
 #, fuzzy
 #| msgid "All levels will be available to play."
 msgid "All levels will be available without requiring passwords"
 msgstr "?????? ?????????? ???????????? ???????????????? ?????? ??????."
 
-#: engines/scumm/metaengine.cpp:1049
+#: engines/scumm/metaengine.cpp:900
+#, fuzzy
+#| msgid "No date saved"
+msgid "No damage"
+msgstr "???????? ???? ????????????????"
+
+#: engines/scumm/metaengine.cpp:901
+#, fuzzy
+#| msgid "Disable save menu"
+msgid "Disable player damage"
+msgstr "???????????????????? ???????? ????????????????????"
+
+#: engines/scumm/metaengine.cpp:1061
 msgid "Down left"
 msgstr "????????????-????????????"
 
-#: engines/scumm/metaengine.cpp:1062
+#: engines/scumm/metaengine.cpp:1074
 msgid "Down right"
 msgstr "????????????-??????????????"
 
-#: engines/scumm/metaengine.cpp:1082
+#: engines/scumm/metaengine.cpp:1094
 msgid "Up left"
 msgstr "????????????-????????????"
 
-#: engines/scumm/metaengine.cpp:1095
+#: engines/scumm/metaengine.cpp:1107
 msgid "Up right"
 msgstr "????????????-??????????????"
 
-#: engines/scumm/metaengine.cpp:1107
+#: engines/scumm/metaengine.cpp:1119
 msgid "Switch weapon"
 msgstr "???????????????????? ??????????"
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1121
+#: engines/scumm/metaengine.cpp:1133
 msgid "Win the bike fight cheat"
 msgstr "?????? ???????????????????? ?????? ???? ??????????????"
 
-#: engines/scumm/metaengine.cpp:1130
+#: engines/scumm/metaengine.cpp:1142
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1209
+#: engines/scumm/metaengine.cpp:1144 engines/scumm/metaengine.cpp:1221
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1214
+#: engines/scumm/metaengine.cpp:1149 engines/scumm/metaengine.cpp:1226
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1219
+#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1231
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1224
+#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1229
+#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1241
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "??????????????????"
 
-#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
+#: engines/scumm/metaengine.cpp:1171 engines/scumm/metaengine.cpp:1248
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "?????????????????????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1243
+#: engines/scumm/metaengine.cpp:1178 engines/scumm/metaengine.cpp:1255
 #, fuzzy
 #| msgid "Slide left"
 msgid "Stick left"
 msgstr "???????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1250
+#: engines/scumm/metaengine.cpp:1185 engines/scumm/metaengine.cpp:1262
 #, fuzzy
 #| msgid "Slide right"
 msgid "Stick right"
 msgstr "???????? ??????????????"
 
-#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1257
+#: engines/scumm/metaengine.cpp:1192 engines/scumm/metaengine.cpp:1269
 #, fuzzy
 #| msgid "Move / Select"
 msgid "Fire / select"
 msgstr "???????????????? / ??????????????"
 
-#: engines/scumm/metaengine.cpp:1186
+#: engines/scumm/metaengine.cpp:1198
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Walk / menu back"
 msgstr "???????????????????? ?????????????? ????????"
 
-#: engines/scumm/metaengine.cpp:1191
+#: engines/scumm/metaengine.cpp:1203
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu back"
 msgstr "???????????????????? ?????????????? ????????"
 
-#: engines/scumm/metaengine.cpp:1207
+#: engines/scumm/metaengine.cpp:1219
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1262
+#: engines/scumm/metaengine.cpp:1274
 #, fuzzy
 #| msgid "Move back"
 msgid "Cover / back"
 msgstr "???????????????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1268
+#: engines/scumm/metaengine.cpp:1280
 #, fuzzy
 #| msgid "Skip walk"
 msgid "Skip / back"
 msgstr "???????????????????? ??????????????"
 
-#: engines/scumm/metaengine.cpp:1275
+#: engines/scumm/metaengine.cpp:1287
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu"
diff --git a/po/zh.po b/po/zh.po
index 05e0bbecd8b..398f22256cc 100644
--- a/po/zh.po
+++ b/po/zh.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-06 11:26+0000\n"
+"POT-Creation-Date: 2026-06-07 21:08+0000\n"
 "PO-Revision-Date: 2024-05-14 12:42+0000\n"
 "Last-Translator: ?????? <clonewith at qq.com>\n"
 "Language-Team: Chinese <https://translations.scummvm.org/projects/scummvm/"
@@ -1033,7 +1033,7 @@ msgstr "??????"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1088
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1100
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1054,7 +1054,7 @@ msgstr "??????"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1055
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1067
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1075,7 +1075,7 @@ msgstr "??????"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1068
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1080
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1095,7 +1095,7 @@ msgstr "??????"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1075
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1087
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3645,7 +3645,7 @@ msgid "Game not implemented"
 msgstr ""
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
-#: engines/scumm/metaengine.cpp:1038
+#: engines/scumm/metaengine.cpp:1050
 #, fuzzy
 #| msgid "Game Options"
 msgid "Default game keymappings"
@@ -5543,7 +5543,7 @@ msgstr ""
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1196 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1208 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr ""
 
@@ -6954,7 +6954,7 @@ msgstr "??????????????????"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1114 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1126 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -7675,7 +7675,7 @@ msgstr "??????"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1101
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1113
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -9795,7 +9795,7 @@ msgid "Player ammo will never decrease."
 msgstr ""
 
 #: engines/hypno/metaengine.cpp:70 engines/scumm/metaengine.cpp:891
-#: engines/scumm/metaengine.cpp:900
+#: engines/scumm/metaengine.cpp:909
 msgid "Unlock all levels"
 msgstr ""
 
@@ -13083,112 +13083,122 @@ msgstr ""
 msgid "Run the game in 640x400 high resolution mode instead of 320x200"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:901
+#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:910
 msgid "All levels will be available without requiring passwords"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1049
+#: engines/scumm/metaengine.cpp:900
+msgid "No damage"
+msgstr ""
+
+#: engines/scumm/metaengine.cpp:901
+#, fuzzy
+#| msgid "Special sound effects volume"
+msgid "Disable player damage"
+msgstr "??????????????????"
+
+#: engines/scumm/metaengine.cpp:1061
 #, fuzzy
 msgid "Down left"
 msgstr "??????"
 
-#: engines/scumm/metaengine.cpp:1062
+#: engines/scumm/metaengine.cpp:1074
 #, fuzzy
 msgid "Down right"
 msgstr "??????"
 
-#: engines/scumm/metaengine.cpp:1082
+#: engines/scumm/metaengine.cpp:1094
 #, fuzzy
 msgid "Up left"
 msgstr "??????"
 
-#: engines/scumm/metaengine.cpp:1095
+#: engines/scumm/metaengine.cpp:1107
 #, fuzzy
 msgid "Up right"
 msgstr "??????"
 
-#: engines/scumm/metaengine.cpp:1107
+#: engines/scumm/metaengine.cpp:1119
 msgid "Switch weapon"
 msgstr ""
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1121
+#: engines/scumm/metaengine.cpp:1133
 msgid "Win the bike fight cheat"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1130
+#: engines/scumm/metaengine.cpp:1142
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1209
+#: engines/scumm/metaengine.cpp:1144 engines/scumm/metaengine.cpp:1221
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1214
+#: engines/scumm/metaengine.cpp:1149 engines/scumm/metaengine.cpp:1226
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1219
+#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1231
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1224
+#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1229
+#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1241
 msgid "Stick up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
+#: engines/scumm/metaengine.cpp:1171 engines/scumm/metaengine.cpp:1248
 #, fuzzy
 msgid "Stick down"
 msgstr "??????"
 
-#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1243
+#: engines/scumm/metaengine.cpp:1178 engines/scumm/metaengine.cpp:1255
 #, fuzzy
 msgid "Stick left"
 msgstr "??????"
 
-#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1250
+#: engines/scumm/metaengine.cpp:1185 engines/scumm/metaengine.cpp:1262
 #, fuzzy
 msgid "Stick right"
 msgstr "??????"
 
-#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1257
+#: engines/scumm/metaengine.cpp:1192 engines/scumm/metaengine.cpp:1269
 #, fuzzy
 msgid "Fire / select"
 msgstr "??????"
 
-#: engines/scumm/metaengine.cpp:1186
+#: engines/scumm/metaengine.cpp:1198
 #, fuzzy
 #| msgid "Skip autosave"
 msgid "Walk / menu back"
 msgstr "??????????????????"
 
-#: engines/scumm/metaengine.cpp:1191
+#: engines/scumm/metaengine.cpp:1203
 #, fuzzy
 #| msgid "Skip autosave"
 msgid "Skip / menu back"
 msgstr "??????????????????"
 
-#: engines/scumm/metaengine.cpp:1207
+#: engines/scumm/metaengine.cpp:1219
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1262
+#: engines/scumm/metaengine.cpp:1274
 #, fuzzy
 #| msgid "Backend"
 msgid "Cover / back"
 msgstr "??????"
 
-#: engines/scumm/metaengine.cpp:1268
+#: engines/scumm/metaengine.cpp:1280
 #, fuzzy
 #| msgid "Show hidden files"
 msgid "Skip / back"
 msgstr "?????????????????????"
 
-#: engines/scumm/metaengine.cpp:1275
+#: engines/scumm/metaengine.cpp:1287
 #, fuzzy
 #| msgid "Skip autosave"
 msgid "Skip / menu"
diff --git a/po/zh_Hans.po b/po/zh_Hans.po
index 9baf5253dff..00dd032d587 100644
--- a/po/zh_Hans.po
+++ b/po/zh_Hans.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.9.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-06 11:26+0000\n"
+"POT-Creation-Date: 2026-06-07 21:08+0000\n"
 "PO-Revision-Date: 2024-12-01 22:18+0000\n"
 "Last-Translator: ?????? <clonewith at qq.com>\n"
 "Language-Team: Chinese (Simplified Han script) <https://"
@@ -1034,7 +1034,7 @@ msgstr "??????"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1088
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1100
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1055,7 +1055,7 @@ msgstr "??????"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1055
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1067
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1076,7 +1076,7 @@ msgstr "??????"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1068
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1080
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1096,7 +1096,7 @@ msgstr "??????"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1075
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1087
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3846,7 +3846,7 @@ msgid "Game not implemented"
 msgstr "???????????????"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
-#: engines/scumm/metaengine.cpp:1038
+#: engines/scumm/metaengine.cpp:1050
 #, fuzzy
 #| msgid "Default game keymap"
 msgid "Default game keymappings"
@@ -6179,7 +6179,7 @@ msgstr "R2"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1196 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1208 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "??????"
 
@@ -7629,7 +7629,7 @@ msgstr "????????????"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1114 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1126 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -8384,7 +8384,7 @@ msgstr "??????/??????"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1101
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1113
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -10607,7 +10607,7 @@ msgid "Player ammo will never decrease."
 msgstr "???????????????????????????"
 
 #: engines/hypno/metaengine.cpp:70 engines/scumm/metaengine.cpp:891
-#: engines/scumm/metaengine.cpp:900
+#: engines/scumm/metaengine.cpp:909
 msgid "Unlock all levels"
 msgstr "??????????????????"
 
@@ -14052,124 +14052,136 @@ msgstr "??????????????????"
 msgid "Run the game in 640x400 high resolution mode instead of 320x200"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:901
+#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:910
 #, fuzzy
 #| msgid "All levels will be available to play."
 msgid "All levels will be available without requiring passwords"
 msgstr "??????????????????????????????"
 
-#: engines/scumm/metaengine.cpp:1049
+#: engines/scumm/metaengine.cpp:900
+#, fuzzy
+#| msgid "No date saved"
+msgid "No damage"
+msgstr "???????????????"
+
+#: engines/scumm/metaengine.cpp:901
+#, fuzzy
+#| msgid "Disable save menu"
+msgid "Disable player damage"
+msgstr "??????????????????"
+
+#: engines/scumm/metaengine.cpp:1061
 #, fuzzy
 #| msgid "Down Left"
 msgid "Down left"
 msgstr "??????"
 
-#: engines/scumm/metaengine.cpp:1062
+#: engines/scumm/metaengine.cpp:1074
 #, fuzzy
 #| msgid "Down Right"
 msgid "Down right"
 msgstr "??????"
 
-#: engines/scumm/metaengine.cpp:1082
+#: engines/scumm/metaengine.cpp:1094
 #, fuzzy
 #| msgid "Upper left item"
 msgid "Up left"
 msgstr "???????????????"
 
-#: engines/scumm/metaengine.cpp:1095
+#: engines/scumm/metaengine.cpp:1107
 #, fuzzy
 #| msgid "Up Right"
 msgid "Up right"
 msgstr "??????"
 
-#: engines/scumm/metaengine.cpp:1107
+#: engines/scumm/metaengine.cpp:1119
 msgid "Switch weapon"
 msgstr "????????????"
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1121
+#: engines/scumm/metaengine.cpp:1133
 msgid "Win the bike fight cheat"
 msgstr "???????????????????????????"
 
-#: engines/scumm/metaengine.cpp:1130
+#: engines/scumm/metaengine.cpp:1142
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1209
+#: engines/scumm/metaengine.cpp:1144 engines/scumm/metaengine.cpp:1221
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1214
+#: engines/scumm/metaengine.cpp:1149 engines/scumm/metaengine.cpp:1226
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1219
+#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1231
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1224
+#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1229
+#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1241
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "??????"
 
-#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
+#: engines/scumm/metaengine.cpp:1171 engines/scumm/metaengine.cpp:1248
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "????????????"
 
-#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1243
+#: engines/scumm/metaengine.cpp:1178 engines/scumm/metaengine.cpp:1255
 #, fuzzy
 #| msgid "Slide Left"
 msgid "Stick left"
 msgstr "????????????"
 
-#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1250
+#: engines/scumm/metaengine.cpp:1185 engines/scumm/metaengine.cpp:1262
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick right"
 msgstr "????????????"
 
-#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1257
+#: engines/scumm/metaengine.cpp:1192 engines/scumm/metaengine.cpp:1269
 #, fuzzy
 #| msgid "Move left"
 msgid "Fire / select"
 msgstr "????????????"
 
-#: engines/scumm/metaengine.cpp:1186
+#: engines/scumm/metaengine.cpp:1198
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Walk / menu back"
 msgstr "???????????????"
 
-#: engines/scumm/metaengine.cpp:1191
+#: engines/scumm/metaengine.cpp:1203
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu back"
 msgstr "???????????????"
 
-#: engines/scumm/metaengine.cpp:1207
+#: engines/scumm/metaengine.cpp:1219
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1262
+#: engines/scumm/metaengine.cpp:1274
 #, fuzzy
 #| msgid "Move Back"
 msgid "Cover / back"
 msgstr "????????????"
 
-#: engines/scumm/metaengine.cpp:1268
+#: engines/scumm/metaengine.cpp:1280
 #, fuzzy
 #| msgid "Skip dialogue"
 msgid "Skip / back"
 msgstr "????????????"
 
-#: engines/scumm/metaengine.cpp:1275
+#: engines/scumm/metaengine.cpp:1287
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu"
diff --git a/po/zh_Hant.po b/po/zh_Hant.po
index 8af6799126d..43820ce8fa0 100644
--- a/po/zh_Hant.po
+++ b/po/zh_Hant.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.9.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-06 11:26+0000\n"
+"POT-Creation-Date: 2026-06-07 21:08+0000\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: Automatically generated\n"
 "Language-Team: none\n"
@@ -1014,7 +1014,7 @@ msgstr ""
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1088
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1100
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1035,7 +1035,7 @@ msgstr ""
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1055
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1067
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1056,7 +1056,7 @@ msgstr ""
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1068
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1080
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1076,7 +1076,7 @@ msgstr ""
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1075
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1087
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3589,7 +3589,7 @@ msgid "Game not implemented"
 msgstr ""
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
-#: engines/scumm/metaengine.cpp:1038
+#: engines/scumm/metaengine.cpp:1050
 msgid "Default game keymappings"
 msgstr ""
 
@@ -5435,7 +5435,7 @@ msgstr ""
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1196 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1208 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr ""
 
@@ -6796,7 +6796,7 @@ msgstr ""
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1114 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1126 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -7443,7 +7443,7 @@ msgstr ""
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1101
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1113
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -9330,7 +9330,7 @@ msgid "Player ammo will never decrease."
 msgstr ""
 
 #: engines/hypno/metaengine.cpp:70 engines/scumm/metaengine.cpp:891
-#: engines/scumm/metaengine.cpp:900
+#: engines/scumm/metaengine.cpp:909
 msgid "Unlock all levels"
 msgstr ""
 
@@ -12390,96 +12390,104 @@ msgstr ""
 msgid "Run the game in 640x400 high resolution mode instead of 320x200"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:901
+#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:910
 msgid "All levels will be available without requiring passwords"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1049
+#: engines/scumm/metaengine.cpp:900
+msgid "No damage"
+msgstr ""
+
+#: engines/scumm/metaengine.cpp:901
+msgid "Disable player damage"
+msgstr ""
+
+#: engines/scumm/metaengine.cpp:1061
 msgid "Down left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1062
+#: engines/scumm/metaengine.cpp:1074
 msgid "Down right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1082
+#: engines/scumm/metaengine.cpp:1094
 msgid "Up left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1095
+#: engines/scumm/metaengine.cpp:1107
 msgid "Up right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1107
+#: engines/scumm/metaengine.cpp:1119
 msgid "Switch weapon"
 msgstr ""
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1121
+#: engines/scumm/metaengine.cpp:1133
 msgid "Win the bike fight cheat"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1130
+#: engines/scumm/metaengine.cpp:1142
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1132 engines/scumm/metaengine.cpp:1209
+#: engines/scumm/metaengine.cpp:1144 engines/scumm/metaengine.cpp:1221
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1137 engines/scumm/metaengine.cpp:1214
+#: engines/scumm/metaengine.cpp:1149 engines/scumm/metaengine.cpp:1226
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1142 engines/scumm/metaengine.cpp:1219
+#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1231
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1147 engines/scumm/metaengine.cpp:1224
+#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1152 engines/scumm/metaengine.cpp:1229
+#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1241
 msgid "Stick up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
+#: engines/scumm/metaengine.cpp:1171 engines/scumm/metaengine.cpp:1248
 msgid "Stick down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1166 engines/scumm/metaengine.cpp:1243
+#: engines/scumm/metaengine.cpp:1178 engines/scumm/metaengine.cpp:1255
 msgid "Stick left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1250
+#: engines/scumm/metaengine.cpp:1185 engines/scumm/metaengine.cpp:1262
 msgid "Stick right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1180 engines/scumm/metaengine.cpp:1257
+#: engines/scumm/metaengine.cpp:1192 engines/scumm/metaengine.cpp:1269
 msgid "Fire / select"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1186
+#: engines/scumm/metaengine.cpp:1198
 msgid "Walk / menu back"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1191
+#: engines/scumm/metaengine.cpp:1203
 msgid "Skip / menu back"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1207
+#: engines/scumm/metaengine.cpp:1219
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1262
+#: engines/scumm/metaengine.cpp:1274
 msgid "Cover / back"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1268
+#: engines/scumm/metaengine.cpp:1280
 msgid "Skip / back"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1275
+#: engines/scumm/metaengine.cpp:1287
 msgid "Skip / menu"
 msgstr ""
 



From noreply at scummvm.org  Sun Jun  7 22:09:28 2026
From: noreply at scummvm.org (sev-)
Date: Sun,  7 Jun 2026 22:09:28 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 9429b3892d86023735567a6f2aca1901ecf564d1
Message-ID: <20260607220928.CBBD97005BC@ron.scummvm.net>

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

Summary:
9429b3892d BOLT: Split out game code into their own folders


Commit: 9429b3892d86023735567a6f2aca1901ecf564d1
    https://github.com/scummvm/scummvm/commit/9429b3892d86023735567a6f2aca1901ecf564d1
Author: N.E.C. (beholdnec at gmail.com)
Date: 2026-06-08T00:09:23+02:00

Commit Message:
BOLT: Split out game code into their own folders

Changed paths:
  A engines/bolt/carnival/barker.cpp
  A engines/bolt/carnival/booth.cpp
  A engines/bolt/carnival/booths/fred.cpp
  A engines/bolt/carnival/booths/george.cpp
  A engines/bolt/carnival/booths/huck.cpp
  A engines/bolt/carnival/booths/scooby.cpp
  A engines/bolt/carnival/booths/topcat.cpp
  A engines/bolt/carnival/booths/yogi.cpp
  A engines/bolt/carnival/carnival.cpp
  A engines/bolt/carnival/carnival.h
  A engines/bolt/carnival/resource.cpp
  A engines/bolt/crete/crete.cpp
  A engines/bolt/crete/crete.h
  A engines/bolt/merlin/merlin.cpp
  A engines/bolt/merlin/merlin.h
  R engines/bolt/barker.cpp
  R engines/bolt/booth.cpp
  R engines/bolt/booths/fred.cpp
  R engines/bolt/booths/george.cpp
  R engines/bolt/booths/huck.cpp
  R engines/bolt/booths/scooby.cpp
  R engines/bolt/booths/topcat.cpp
  R engines/bolt/booths/yogi.cpp
    engines/bolt/bolt.cpp
    engines/bolt/bolt.h
    engines/bolt/metaengine.cpp
    engines/bolt/module.mk
    engines/bolt/resource.cpp


diff --git a/engines/bolt/bolt.cpp b/engines/bolt/bolt.cpp
index 70b53368b7f..49c2e5e630d 100644
--- a/engines/bolt/bolt.cpp
+++ b/engines/bolt/bolt.cpp
@@ -39,8 +39,6 @@ BoltEngine::BoltEngine(OSystem *syst, const ADGameDescription *gameDesc) : Engin
 	_gameDescription(gameDesc), _randomSource("Bolt") {
 	g_engine = this;
 	_xp = new XpLib(this);
-
-	initCallbacks();
 }
 
 BoltEngine::~BoltEngine() {
@@ -81,120 +79,6 @@ Common::Error BoltEngine::run() {
 	return Common::kNoError;
 }
 
-void BoltEngine::boltMain() {
-	byte *testAlloc;
-	BarkerTable *barkerTable;
-	byte *boothSprite;
-
-	testAlloc = (byte *)_xp->allocMem(0x100000);
-	if (!testAlloc)
-		return;
-
-	_xp->freeMem(testAlloc);
-
-	_xp->randomize();
-
-	if (allocResourceIndex()) {
-		_boothsBoltLib = nullptr;
-
-		if (openBOLTLib(&_boothsBoltLib, &_boothsBoltCallbacks, assetPath("booths.blt"))) {
-			int16 chosenSpecId = _extendedViewport ? 0 : 1;
-
-			if (_xp->setDisplaySpec(&_displayMode, &_displaySpecs[chosenSpecId])) {
-				assert(_displayMode >= 0);
-				_displayWidth = _displaySpecs[_displayMode].width;
-				_displayHeight = _displaySpecs[_displayMode].height;
-
-				// Center within 384x240 virtual coordinate space...
-				_displayX = (EXTENDED_SCREEN_WIDTH - _displayWidth) / 2;
-				_displayY = (EXTENDED_SCREEN_HEIGHT - _displayHeight) / 2;
-
-				_xp->setCoordSpec(_displayX, _displayY, _displayWidth, _displayHeight);
-				
-				if (_displayMode == 0) {
-					_rtfHandle = openRTF(assetPath("cc_og.av"));
-				} else {
-					_rtfHandle = openRTF(assetPath("cc_cr.av"));
-				}
-
-				if (_rtfHandle) {
-					int16 boothGroupBase = _isDemo ? 0x0E00 : 0x1700;
-
-					if (!_isDemo) {
-						playAV(_rtfHandle, 0, _displayWidth, _displayHeight, _displayX, _displayY);
-					}
-
-					boothSprite = getBOLTMember(_boothsBoltLib, (_displayMode != 0) ? (boothGroupBase + 1) : (boothGroupBase + 2));
-
-					_xp->setTransparency(false);
-
-					displayColors(getBOLTMember(_boothsBoltLib, boothGroupBase), stFront, 0);
-					displayPic(boothSprite, _displayX, _displayY, stFront);
-
-					_xp->updateDisplay();
-
-					displayColors(getBOLTMember(_boothsBoltLib, boothGroupBase), stBack, 0);
-					displayPic(boothSprite, _displayX, _displayY, stBack);
-
-					playAV(_rtfHandle, _isDemo ? 0 : 2, _displayWidth, _displayHeight, _displayX, _displayY);
-
-					freeBOLTGroup(_boothsBoltLib, boothGroupBase, 1);
-
-					if (getBOLTGroup(_boothsBoltLib, 0, 1)) {
-						setCursorPict(memberAddr(_boothsBoltLib, 0));
-
-						if (initVRam(1500)) {
-							barkerTable = createBarker(2, _isDemo ? 19 : 17);
-							if (barkerTable) {
-								// Register booth handlers...
-								registerSideShow(barkerTable, &BoltEngine::hucksBooth, 3);
-								registerSideShow(barkerTable, &BoltEngine::fredsBooth, 4);
-								registerSideShow(barkerTable, &BoltEngine::scoobysBooth, 5);
-								registerSideShow(barkerTable, &BoltEngine::yogisBooth, 6);
-								registerSideShow(barkerTable, &BoltEngine::georgesBooth, 7);
-								registerSideShow(barkerTable, &BoltEngine::topCatsBooth, 8);
-								registerSideShow(barkerTable, &BoltEngine::mainEntrance, 9);
-								registerSideShow(barkerTable, &BoltEngine::huckGame, 10);
-								registerSideShow(barkerTable, &BoltEngine::fredGame, 11);
-								registerSideShow(barkerTable, &BoltEngine::scoobyGame, 12);
-								registerSideShow(barkerTable, &BoltEngine::yogiGame, 13);
-								registerSideShow(barkerTable, &BoltEngine::georgeGame, 14);
-								registerSideShow(barkerTable, &BoltEngine::topCatGame, 15);
-								registerSideShow(barkerTable, &BoltEngine::winALetter, 16);
-
-								if (_isDemo) {
-									registerSideShow(barkerTable, &BoltEngine::displayDemoPict, 17);
-									registerSideShow(barkerTable, &BoltEngine::endDemo, 18);
-								}
-
-								_lettersWon = 0;
-								_xp->setScreenSaverTimer(1800);
-
-								// The barker function runs the main loop, starting at mainEntrance()...
-								if (!checkError())
-									barker(barkerTable, 9);
-
-								freeBarker(barkerTable);
-							}
-						}
-					}
-				}
-			}
-		}
-	}
-
-	freeBOLTGroup(_boothsBoltLib, 0, 1);
-
-	if (_boothsBoltLib != 0)
-		closeBOLTLib(&_boothsBoltLib);
-
-	if (_rtfHandle != nullptr)
-		closeRTF(_rtfHandle);
-
-	freeVRam();
-	freeResourceIndex();
-}
-
 void BoltEngine::setCursorPict(byte *sprite) {
 	byte cursorBitmap[32]; // 16x16
 	byte *srcPtr = getResolvedPtr(sprite, 0x12);
@@ -214,25 +98,4 @@ void BoltEngine::setCursorPict(byte *sprite) {
 	_xp->setCursorImage(cursorBitmap, 7, 7);
 }
 
-int16 BoltEngine::displayDemoPict(int16 prevBooth) {
-	uint32 eventData;
-
-	while (_xp->getEvent(etMouseDown, &eventData) == etMouseDown);
-
-	while (_xp->getEvent(etTimer, &eventData) != etTimer);
-
-	_xp->stopCycle();
-	_xp->setTransparency(false);
-
-	int16 memberId = (_displayMode != 0) ? 0x1002 : 0x1001;
-	displayPic(getBOLTMember(_boothsBoltLib, memberId), _displayX, _displayY, stFront);
-	displayColors(getBOLTMember(_boothsBoltLib, 0x1000), stFront, 0);
-
-	_xp->updateDisplay();
-
-	while (_xp->getEvent(etMouseDown, &eventData) != etMouseDown);
-
-	return prevBooth;
-}
-
 } // End of namespace Bolt
diff --git a/engines/bolt/bolt.h b/engines/bolt/bolt.h
index 2a2a1b9a5f7..146f916cb40 100644
--- a/engines/bolt/bolt.h
+++ b/engines/bolt/bolt.h
@@ -53,15 +53,6 @@ typedef struct DisplaySpecs {
 	int16 height;
 } DisplaySpecs;
 
-typedef int16 (BoltEngine::*SideShowHandler)(int16 prevBooth);
-
-struct BarkerTable {
-	SideShowHandler *handlers;
-	int16 count;
-	int16 minIndex;
-	int16 maxIndex;
-};
-
 typedef struct BOLTHeader {
 	byte header[11];
 	byte groupCount;
@@ -312,240 +303,6 @@ typedef struct SoundInfo {
 	}
 } SoundInfo;
 
-
-typedef struct FredEntityState {
-	uint16 flags;
-	int16 frameCountdown;
-	int16 animMode;
-	int16 frameIndex;
-	byte *animTable;
-	int16 direction;
-	int32 xPos;
-	int32 yPos;
-	int32 prevXPos;
-	int32 prevYPos;
-	int16 speed;
-	byte *pathTable;
-	int16 pathIndex;
-
-	FredEntityState() {
-		flags = 0;
-		frameCountdown = 0;
-		animMode = 0;
-		frameIndex = 0;
-		animTable = nullptr;
-		direction = 0;
-		xPos = 0;
-		yPos = 0;
-		prevXPos = 0;
-		prevYPos = 0;
-		speed = 0;
-		pathTable = nullptr;
-		pathIndex = 0;
-	}
-} FredEntityState;
-
-// HUCK GAME
-
-typedef struct HuckState {
-	int16 levelNumber;
-	int16 slotIndex[10];
-	int16 levelComplete;
-	int16 drawTable1[24];
-	int16 drawTable2[24];
-	int16 giftCount;
-	int16 hasCycle;
-	int16 selectionPending;
-	int16 selected1Slot;
-	int16 selected2Slot;
-	int16 selected1SpriteId;
-	int16 selected2SpriteId;
-
-	HuckState() {
-		levelNumber = 0;
-		memset(slotIndex, 0, sizeof(slotIndex));
-		levelComplete = 0;
-		memset(drawTable1, 0, sizeof(drawTable1));
-		memset(drawTable2, 0, sizeof(drawTable2));
-		giftCount = 0;
-		hasCycle = 0;
-		selectionPending = 0;
-		selected1Slot = 0;
-		selected2Slot = 0;
-		selected1SpriteId = 0;
-		selected2SpriteId = 0;
-	}
-} HuckState;
-
-// GEORGE GAME
-
-typedef struct GeorgeEntityState {
-	uint16 flags;
-	int16 frameCountdown;
-	int16 animMode;
-	int16 variant;
-	int16 frameIndex;
-	byte *animTable;
-	int32 joyInput;
-	int32 x;
-	int32 y;
-	int32 prevX;
-	int32 prevY;
-	int32 velX;
-	int32 velY;
-	int32 accelX;
-	int32 accelY;
-	byte *pathTable;
-	int16 pathIndex;
-
-	GeorgeEntityState() {
-		flags = 0;
-		frameCountdown = 0;
-		animMode = 0;
-		variant = 0;
-		frameIndex = 0;
-		animTable = nullptr;
-		joyInput = 0;
-		x = 0;
-		y = 0;
-		prevX = 0;
-		prevY = 0;
-		velX = 0;
-		velY = 0;
-		accelX = 0;
-		accelY = 0;
-		pathTable = nullptr;
-		pathIndex = 0;
-	}
-} GeorgeEntityState;
-
-// SCOOBY GAME
-
-typedef struct ScoobyState {
-	int16 levelNumber;
-	int16 slotIndex[10];
-	int16 levelComplete;
-	int16 wallStates[25][4];
-	int16 scoobyCell;
-	int16 scoobySavedCell;
-	int16 leftNeighbor;
-	int16 rightNeighbor;
-	int16 upNeighbor;
-	int16 downNeighbor;
-	int16 activeLevel;
-	int16 scoobyX;
-	int16 scoobyY;
-	int16 velocityX;
-	int16 velocityY;
-	int16 targetVelocityX;
-	int16 targetVelocityY;
-	int16 transitionTarget;
-	int16 currentAnim;
-	int16 direction;
-	int16 spriteFrameCount;
-	byte *frameData[6];
-
-	ScoobyState() {
-		levelNumber = 0;
-
-		for (int i = 0; i < 10; i++)
-			slotIndex[i] = 0;
-
-		levelComplete = 0;
-
-		for (int i = 0; i < 25; i++)
-			for (int j = 0; j < 4; j++)
-				wallStates[i][j] = 0;
-
-		scoobyCell = 0;
-		scoobySavedCell = 0;
-		leftNeighbor = 0;
-		rightNeighbor = 0;
-		upNeighbor = 0;
-		downNeighbor = 0;
-		activeLevel = 0;
-		scoobyX = 0;
-		scoobyY = 0;
-		velocityX = 0;
-		velocityY = 0;
-		targetVelocityX = 0;
-		targetVelocityY = 0;
-		transitionTarget = 0;
-		currentAnim = 0;
-		direction = 0;
-		spriteFrameCount = 0;
-
-		for (int i = 0; i < 6; i++)
-			frameData[i] = nullptr;
-	}
-} ScoobyState;
-
-typedef struct ScoobyRect {
-	int16 left;
-	int16 right;
-	int16 top;
-	int16 bottom;
-
-	ScoobyRect() {
-		left = 0;
-		right = 0;
-		top = 0;
-		bottom = 0;
-	}
-} ScoobyRect;
-
-// TOPCAT GAME
-
-typedef struct TopCatAnim {
-	int16 animType;
-	int16 animIndex;
-	int16 transitionToNextQuestionFlag;
-	int16 *seqPtr;
-
-	TopCatAnim() {
-		animType = 0;
-		animIndex = 0;
-		transitionToNextQuestionFlag = 0;
-		seqPtr = nullptr;
-	}
-} TopCatAnim;
-
-// YOGI GAME
-
-typedef struct YogiState {
-	int16 levelNumber;
-	int16 currentSlot;
-	int16 levelIndex[10];
-	int16 slotIndex[10];
-	int16 levelComplete;
-	int16 basketSound[24];
-	int16 basketState[24];
-	int16 basketCount;
-	int16 matchCount;
-	int16 selectionPending;
-	int16 selected1Slot;
-	int16 selected2Slot;
-	int16 sound1;
-	int16 sound2;
-
-	YogiState() {
-		levelNumber = 0;
-		currentSlot = 0;
-		memset(levelIndex, 0, sizeof(levelIndex));
-		memset(slotIndex, 0, sizeof(slotIndex));
-		levelComplete = 0;
-		memset(basketSound, 0, sizeof(basketSound));
-		memset(basketState, 0, sizeof(basketState));
-		basketCount = 0;
-		matchCount = 0;
-		selectionPending = 0;
-		selected1Slot = 0;
-		selected2Slot = 0;
-		sound1 = 0;
-		sound2 = 0;
-	}
-} YogiState;
-
 class BoltEngine : public Engine {
 friend class XpLib;
 
@@ -553,11 +310,13 @@ private:
 	const ADGameDescription *_gameDescription;
 	Common::RandomSource _randomSource;
 protected:
+	// BoltEngine can only be constructed by one of the Carnival, Merlin or Crete engines.
+	BoltEngine(OSystem *syst, const ADGameDescription *gameDesc);
+
 	// Engine APIs
 	Common::Error run() override;
 
 public:
-	BoltEngine(OSystem *syst, const ADGameDescription *gameDesc);
 	~BoltEngine() override;
 
 	uint32 getFeatures() const;
@@ -590,106 +349,8 @@ protected:
 	bool _isDemo = false;
 
 	// xpMain
-	void boltMain();
+	virtual void boltMain() = 0;
 	void setCursorPict(byte *sprite);
-	int16 displayDemoPict(int16 prevBooth);
-
-	// Booth
-	void startCycle(byte *cycleResource);
-	void displayBooth(int16 page);
-	void playAVOverBooth(int16 animIndex);
-	int16 hucksBooth(int16 prevBooth);
-	int16 fredsBooth(int16 prevBooth);
-	int16 scoobysBooth(int16 prevBooth);
-	int16 yogisBooth(int16 prevBooth);
-	int16 georgesBooth(int16 prevBooth);
-	int16 topCatsBooth(int16 prevBooth);
-	int16 mainEntrance(int16 prevBooth);
-	bool loadBooth(int16 boothId);
-	void unloadBooth();
-	int16 openBooth(int16 boothId);
-	void closeBooth();
-	void playTour();
-	void finishPlayingHelp(int16 activeHotspot);
-	int16 hotSpotActive(int16 hotspot);
-	void hoverHotSpot();
-	int16 boothEventLoop();
-	void resetInactivityState();
-	bool handleButtonPress(int16 hotspot);
-	void blastColors(byte **paletteTable, int16 index, int16 mode);
-	void setColors(int16 index);
-	void restoreColors(int16 index);
-	void loadColors();
-	void shiftColorMap(byte *colorMap, int16 delta);
-	void playBoothAV();
-	void mainEntranceHelpBlink();
-	void boothHelpBlink();
-	void tourPaletteCycleStep();
-	void fadeToBlack(int16 steps);
-	void flushInput();
-	int16 winALetter(int16 prevBooth);
-	int16 endDemo(int16 prevBooth);
-
-	int16 _lettersWon = 0;
-	bool _allLettersWonFlag = false;
-	int _displayMode = 0;
-	int32 _displayX = 0;
-	int32 _displayY = 0;
-	int32 _displayWidth = 0;
-	int32 _displayHeight = 0;
-
-	int16 _boothLoadedMask = 0;
-	int16 _currentBoothScene = 0;
-	int16 _boothNumHotspots = 0;
-	int16 _boothNumAnimations = 0;
-	byte *_boothSceneDesc = nullptr;
-	byte *_boothHotPalDescs[4];
-	Common::Rect _boothHotspotDescs[8]; 
-	byte *_boothAnimDescs[7];
-	byte *_boothAnimPalDescs[4];
-	byte _savedPalettes[7][30];
-	byte _savedHotPalettes[4][9];
-	byte *_boothPalCycleData = nullptr;
-	XPPicDesc _boothLetterSprite;
-	bool _needInitCursorPos = true;
-	byte *_boothVisitSignOn = nullptr;
-	byte *_boothVisitSignOff = nullptr;
-	byte _leftDoorNavTable[3]  = { 3, 2, 4 };
-	byte _rightDoorNavTable[3] = { 1, 0, 5 };
-
-	int16 _cursorX = 0;
-	int16 _cursorY = 0;
-	int16 _hoveredHotspot = 0;
-	uint32 _helpTimer = 0;
-	int16 _keyReleased = 0;
-	int16 _keyLeft = 0;
-	int16 _keyRight = 0;
-	int16 _keyUp = 0;
-	int16 _keyDown = 0;
-	int16 _helpFlag = 0;
-	int16 _keyEnter = 0;
-
-	int16 _tourStep = 0;
-	int16 _helpPlaying = 0;
-	int16 _helpIsIdle = 0;
-	int16 _idleHelpAudioAvailable = 1;
-
-	int16 _huckWins = 0;
-	int16 _fredWins = 0;
-	int16 _scoobyWins = 0;
-	int16 _yogiWins = 0;
-	int16 _georgeWins = 0;
-	int16 _topCatWins = 0;
-
-	// Barker
-	BarkerTable *createBarker(int16 minIndex, int16 maxIndex);
-	void freeBarker(BarkerTable *table);
-	bool registerSideShow(BarkerTable *table, SideShowHandler handler, int16 boothId);
-	int16 barker(BarkerTable *table, int16 startBooth);
-	bool checkError();
-
-	int32 _curErrorCode = 0;
-	int16 _currentBoothId = 0;
 
 	// Utils
 	void displayColors(byte *palette, int16 page, int16 flags);
@@ -759,7 +420,7 @@ protected:
 	static void swapSpriteHeaderCb();
 	static void freeSpriteCleanUpCb();
 
-	void initCallbacks();
+	virtual void initCallbacks() = 0;
 
 	int16 _resourceIndexCount = 1000;
 	uint32 **_resourceIndex = nullptr;
@@ -788,6 +449,7 @@ protected:
 	int32 _vramUsedBytes = 0;
 	byte *_allocatedMemPool = nullptr;
 	uint32 _allocatedMemPoolSize = 0;
+	int32 _curErrorCode = 0;
 
 	// RTF
 	RTFResource *openRTF(const char *fileName);
@@ -913,477 +575,6 @@ protected:
 	int16 _spriteScreenAY = 0;
 	int16 _spriteScreenBX = 0;
 	int16 _spriteScreenBY = 0;
-
-	// --- MINIGAMES ---
-
-	// --- FRED ---
-	int16 fredGame(int16 prevBooth);
-	bool initFred();
-	void cleanUpFred();
-	bool initFredLevel(int16 levelGroup, int16 palGroup);
-	void termFredLevel(int16 levelGroup, int16 palGroup);
-	void swapFredAnimEntry();
-	void swapFredAnimDesc();
-	void swapFredLevelDesc();
-	int16 playFred();
-	int16 helpFred();
-	void hiliteFredHelpObject(byte *entry, int16 highlight);
-	void helpAnimStep();
-	bool spawnBalloon();
-	int16 calcBalloonSpawnDelay();
-	int16 selectBalloonRow();
-	void setFredAnimMode(FredEntityState *state, int16 mode);
-	void renderFredScene();
-	void getFredSoundInfo(BOLTLib *lib, int16 memberId, SoundInfo *soundInfo);
-	void playFredSound(SoundInfo *oneShot, SoundInfo *loop);
-	void updateFredSound();
-
-	static void resolveAllRefsCb();
-	static void swapFredAnimEntryCb();
-	static void swapFredAnimDescCb();
-	static void swapFredLevelDescCb();
-
-	BOLTLib *_fredBoltLib = nullptr;
-	BOLTCallbacks _fredBoltCallbacks;
-
-	static BOLTCallback _fredTypeLoadCallbacks[28];
-	static BOLTCallback _fredTypeFreeCallbacks[28];
-
-	byte *_fredLevelPtr = nullptr;
-	byte *_fredBackground = nullptr;
-	byte *_fredBalloonString = nullptr;
-	byte *_fredPalette = nullptr;
-	byte *_fredFacingLeftRect = nullptr;
-	byte *_fredFacingRightRect = nullptr;
-	byte *_fredTurningRect = nullptr;
-	byte *_fredBalloonRect = nullptr;
-	byte *_fredHelpEntries = nullptr;
-	byte *_fredPlayButton = nullptr;
-	byte *_fredSprites[10] = { nullptr };
-	FredEntityState **_fredEntitiesTable = nullptr;
-
-	byte *_fredRowBounds = nullptr;
-	byte *_fredShuffleTable = nullptr;
-	byte *_fredCycleRaw = nullptr;
-	byte *_fredBalloonSprite = nullptr;
-	byte *_fredPathMatrix = nullptr;
-	byte *_fredCurrentHelpObject = nullptr;
-	byte *_fredHoveredEntry = nullptr;
-
-	SoundInfo _fredSounds[4];
-
-	SoundInfo *_fredCurrentSound = nullptr;
-	SoundInfo *_fredLoopSound = nullptr;
-	SoundInfo *_fredPendingOneShot = nullptr;
-	SoundInfo *_fredPendingLoop = nullptr;
-
-	FredEntityState _fredSprite;
-
-	int16 _fredSaveData[3] = { 0, 0, 0 };
-	int16 _fredLevelIndex = 0;
-
-	int16 _fredBalloonSpawnDelay = 0;
-	int16 _fredBalloonSearchIdx = 0;
-	uint32 _fredTimer = 0;
-	int16 _fredHelpStep = -1;
-	int16 _fredShowHelp = 1;
-
-	XPCycleState _fredCycleSpecs[4];
-
-	// --- GEORGE ---
-	int16 georgeGame(int16 prevBooth);
-	bool initGeorge();
-	void cleanUpGeorge();
-	bool initGeorgeLevel(int16 level, int16 variant);
-	void termGeorgeLevel(int16 level, int16 variant);
-	void swapGeorgeFrameArray();
-	void swapGeorgeHelpEntry();
-	void swapGeorgeThresholds();
-	int16 playGeorge();
-	int16 helpGeorge();
-	void hiliteGeorgeHelpObject(byte *entry, int16 highlight);
-	void advanceHelpAnimation();
-	bool spawnSatellite();
-	int16 getRandomSatelliteWait();
-	int16 getRandomAsteroidRow();
-	bool confirmAsteroidHitTest();
-	bool spawnAsteroid();
-	int16 getRandomAsteroidWait();
-	int16 getAsteroidRow();
-	void setGeorgeAnimMode(GeorgeEntityState *sat, int16 mode);
-	void setSatelliteAnimMode(GeorgeEntityState *sat, int16 mode, int16 variant);
-	void setAsteroidAnimMode(GeorgeEntityState *sat, int16 mode, int16 variant);
-	void drawFlyingObjects();
-	void getGeorgeSoundInfo(BOLTLib *boltLib, int16 member, SoundInfo *outInfo, byte priority);
-	void playGeorgeSound(SoundInfo *newSound, SoundInfo *nextSound);
-	void updateGeorgeSound();
-
-	static void swapGeorgeFrameArrayCb();
-	static void swapGeorgeHelpEntryCb();
-	static void swapGeorgeThresholdsCb();
-
-	BOLTLib *_georgeBoltLib = nullptr;
-	BOLTCallbacks _georgeBoltCallbacks;
-
-	static BOLTCallback _georgeTypeLoadCallbacks[28];
-	static BOLTCallback _georgeTypeFreeCallbacks[28];
-
-	byte *_georgePrevActiveHelpObject = nullptr;
-	byte *_georgeActiveHelpObject = nullptr;
-	int16 _georgeHelpStep = -1;
-	int16 _georgeHelpActive = 1;
-
-	GeorgeEntityState **_georgeEntityList = nullptr;
-	int16 _georgeCarIdx = 0;
-	int16 _georgeNumSatellites = 0;
-	int16 _georgeFirstAsteroidIdx = 0;
-	int16 _georgeTotalSatellites = 0;
-	byte *_georgeCarPics[3] = { nullptr };
-	byte *_georgeHelpObjects = nullptr;
-	byte *_georgeHelpSequence = nullptr;
-	uint32 _georgeHelpTimer = 0;
-	byte *_georgeSatelliteShuffleTable = nullptr;
-	byte *_georgeAsteroidShuffleTable = nullptr;
-	int16 *_georgeSatelliteThresholds = nullptr;
-	int16 *_georgeAsteroidThresholds = nullptr;
-	byte *_georgeSatellitePaths = nullptr;
-	byte *_georgeAsteroidPaths = nullptr;
-	byte *_georgeCollisionRect = nullptr;
-	byte *_georgeSatelliteCollisionRects = nullptr;
-	byte *_georgeAsteroidCollisionRects = nullptr;
-	byte *_georgeSatelliteGfx = nullptr;
-	byte *_georgeAsteroidGfx = nullptr;
-	int16 _georgeSatelliteWait = 0;
-	int16 _georgeAsteroidWait = 0;
-	int16 _georgeSatelliteSearchIdx = 0;
-	int16 _georgeAsteroidSearchIdx = 0;
-	int16 _georgeHitSearchIdx = 0;
-	byte *_georgeBgPic = nullptr;
-	byte *_georgePalette = nullptr;
-	byte *_georgePalCycleRawData = nullptr;
-	XPCycleState _georgePalCycleSpecs[4];
-	int16 _georgeCollectedSatellitesNum = 0;
-	SoundInfo _georgeSoundCarStartUp;
-	SoundInfo _georgeSoundCarGoesAway;
-	SoundInfo _georgeSoundCarLoopHi;
-	SoundInfo _georgeSoundCarLoopLo;
-	SoundInfo _georgeSoundCarTumble;
-	SoundInfo **_georgeSatelliteSoundList = nullptr;
-	byte _georgeSoundChannelCounter = 0;
-	SoundInfo *_georgeSoundToPlay = nullptr;
-	SoundInfo *_georgeSoundCurrent = nullptr;
-	SoundInfo *_georgeSoundQueued = nullptr;
-	SoundInfo *_georgeSoundNext = nullptr;
-	int16 _georgeSaveData[3] = { 0 };
-	int16 *_georgeThresholds = nullptr;
-
-	// --- HUCK ---
-	void playSoundMapHuck(int16 memberId);
-	void waitSoundMapHuck();
-	void setHuckColors(int16 which);
-	void restoreHuckColors(int16 which);
-	void startHuckShuffleTimer();
-	void drawGift(int16 slot);
-	void drawHuckGifts();
-	void checkHuckLevelComplete();
-	bool initHuckDisplay();
-	bool loadHuckResources();
-	void unloadHuckResources();
-	bool initHuckLevel();
-	bool resumeHuckLevel();
-	bool initHuck();
-	void huckToggleBlinking(int16 *state, int16 which);
-	void huckUpdateHotSpots(int16 x, int16 y);
-	int16 findGift(int16 x, int16 y);
-	bool handleGiftSelect(int16 x, int16 y);
-	void huckHandleActionButton(int16 x, int16 y);
-	void giftSwap();
-	void resolveHuckSelection();
-	void handleEvent(int16 eventType, uint32 eventData);
-	void playHuck();
-	void cleanUpHuck();
-	int16 huckGame(int16 prevBooth);
-	void swapHuckWordArray();
-	void swapHuckWords();
-
-	static void swapHuckWordArrayCb();
-	static void swapHuckWordsCb();
-
-	BOLTLib *_huckBoltLib = nullptr;
-	BOLTCallbacks _huckBoltCallbacks;
-
-	static BOLTCallback _huckTypeLoadCallbacks[27];
-	static BOLTCallback _huckTypeFreeCallbacks[27];
-
-	int16 _huckSoundPlaying = 0;
-	int16 _huckHotSpotCount = 0;
-	int16 _huckActionState = 0;
-	uint32 _huckScreensaverTimer = 0;
-	uint32 _huckBlinkTimer = 0;
-	int16 _huckScreensaverFlag = 0;
-	int16 _huckBlinkFlag = 0;
-	uint32 _huckShuffleTimer = 0;
-	int16 _huckGlobal[30] = { 0 };
-	HuckState _huckState;
-	byte *_huckGiftPic = nullptr;
-	byte *_huckBgPic = nullptr;
-	int16 _huckGiftGroupId = 0;
-	int16 _huckVariantGroupId = 0;
-	byte *_huckBgDisplayPic = nullptr;
-	int16 _huckScrollOffset = 0;
-	int16 _huckPalRange[8] = { 0 };
-	int16 _huckReturnBooth = 0;
-	int16 _huckExitFlag = 0;
-	int16 _huckCursorX = 0;
-	int16 _huckCursorY = 0;
-	byte _huckPalSave0[15] = { 0 };
-	byte _huckPalHighlight0[15] = { 0 };
-	byte _huckPalSave1[15] = { 0 };
-	byte _huckPalHighlight1[15] = { 0 };
-	XPPicDesc _huckScratchPic;
-
-	// --- SCOOBY ---
-	bool loadScoobyBaseAssets();
-	void cleanUpScoobyBaseAssets();
-	void displayPicClipHack(byte *pic, int16 offsetX, int16 offsetY, int16 *clipRect, int16 displayMode);
-	void drawMovingWalls(int16 cellIdx, int16 direction, int16 picFrame, int16 bgFrame);
-	void drawAllMovingWalls();
-	void animateTransition(int16 memberIdx);
-	void clearPictMSb(byte *pic);
-	void initScoobyLevelGraphics();
-	bool initScoobyLevelAssets();
-	void cleanUpScoobyLevelGraphics();
-	void setScoobySpriteDirection(int16 startMember);
-	void playSoundMapScooby(int16 memberIdx);
-	void playWallSound();
-	void animateWalls();
-	void decideDirection();
-	void updateScoobySound();
-	void setScoobySound(int16 mode);
-	void updateScoobyLocation();
-	void updateScoobyWalls();
-	void updateScoobyDirection(int16 inputDir);
-	void updateScoobyTransition();
-	bool initScoobyLevel();
-	bool resumeScoobyLevel();
-	bool initScooby();
-	void cleanUpScooby();
-	int16 helpScooby();
-	void hiliteScoobyHelpObject(byte *entry, int16 highlight);
-	int16 xpDirToBOLTDir(uint32 xpDir);
-	int16 playScooby();
-	int16 scoobyGame(int16 prevBooth);
-	void swapScoobyHelpEntry();
-	void swapScoobyWordArray();
-
-	static void swapScoobyHelpEntryCb();
-	static void swapScoobyWordArrayCb();
-
-	BOLTLib *_scoobyBoltLib = nullptr;
-	BOLTCallbacks _scoobyBoltCallbacks;
-
-	static BOLTCallback _scoobyTypeLoadCallbacks[28];
-	static BOLTCallback _scoobyTypeFreeCallbacks[28];
-
-	int16 _scoobySoundMode = 0;
-	int16 _scoobySoundPlaying = 0;
-	int16 _scoobyShowHelp = 1;
-
-	int16 _scoobyLastInputDir = 0;
-	int16 _scoobyTransitionFrom = 0;
-	int16 _scoobyTransitionTarget = 0;
-	int16 _scoobyTransitionVelX = 0;
-	int16 _scoobyTransitionVelY = 0;
-	XPPicDesc _scoobyTempPic;
-	byte _scoobyGlobalSaveData[0x3C] = { 0 };
-	ScoobyState _scoobyGameState;
-	SSprite _scoobySprite;
-	byte *_scoobyBaseData = nullptr;
-	byte *_scoobyBgPic = nullptr;
-	byte *_scoobyWallPicsA[4] = { nullptr };
-	byte *_scoobyWallPicsB[5] = { nullptr };
-	ScoobyRect _scoobyCellBounds[25];
-	Common::Point _scoobyLevelStartXY[25];
-	byte *_scoobyLevelData = nullptr;
-	int16 _scoobySelectedGraphicsGroup = 0;
-	int16 _scoobyDifficulty = 0;
-	int16 _scoobyLevelCount = 0;
-	int16 _scoobyMoveRequested = 0;
-	int16 _scoobyTransitioning = 0;
-	int16 _scoobyDesiredDir = 0;
-	int16 _scoobyInputHoldCount = 0;
-	int16 _scoobyWallAnimating = 0;
-	int16 _scoobyWallAnimStep = 0;
-	int16 _scoobyWallsToOpen = 0;
-	int16 _scoobyWallsToClose = 0;
-
-	// --- TOPCAT ---
-	int16 topCatGame(int16 prevBooth);
-	bool initTopCat();
-	void cleanUpTopCat();
-	int16 playTopCat();
-	int16 handleActionButton(int16 *result);
-	void queueAnim(int16 animType, int16 param);
-	bool maintainAnim(int16 soundEvent);
-	void maintainIdleSound(int16 decrement);
-	bool startNextAnim(int16 *playAnswerAnim);
-	void setAnimType(int16 newType);
-	void highlightObject(byte *entry, int16 highlight);
-	bool setupNextQuestion();
-	void adjustColors(byte *pic, int8 shift);
-	void shuffleTopCatQuestions();
-	void shuffleTopCatPermutations();
-	void getTopCatSoundInfo(BOLTLib *lib, int16 memberId, SoundInfo *soundInfo);
-	void setScoreLights(int16 litMask);
-	void swapTopCatHelpEntry();
-
-	static void swapTopCatHelpEntryCb();
-
-	RTFResource *_topCatRtfHandle = nullptr;
-	RTFResource *_topCatAvHandle = nullptr;
-	BOLTLib *_topCatBoltLib = nullptr;
-	BOLTCallbacks _topCatBoltCallbacks;
-
-	static BOLTCallback _topCatTypeLoadCallbacks[26];
-	static BOLTCallback _topCatTypeFreeCallbacks[26];
-
-	byte *_topCatBackgroundPalette = nullptr;
-	byte *_topCatBackground = nullptr;
-	byte *_topCatBackgroundAnimationPalette = nullptr;
-	byte *_topCatGraphicsAssets = nullptr;
-
-	int16 _topCatBackgroundAnimFrame = 0;
-	int16 _topCatMaxBackgroundAnimFrames = 0;
-	int16 _topCatCurrentAnimType = 0;
-	int16 _topCatAnimStateMachineStep = 0;
-	int16 _topCatAnimQueueSize = 0;
-	int16 _topCatQueuedSoundFrames = 0;
-	byte *_topCatButtonsPalette = nullptr;
-	byte *_topCatBlinkEntries = nullptr;
-	byte *_topCatLightsPalette = nullptr;
-	int16 *_topCatBlinkSeqPtr = nullptr;
-	byte *_topCatSelectedChoiceOverlayGfx = nullptr;
-	byte *_topCatCycleData = nullptr;
-	XPCycleState _topCatCycleSpecs[4];
-	byte *_topCatShuffledQuestions = nullptr;
-	byte *_topCatShuffledAnswers = nullptr;
-	byte *_topCatAnswersPermutations = nullptr;
-	byte *_topCatAnswers = nullptr;
-	byte *_topCatAnswersScreenPositions = nullptr;
-	int16 _topCatSavedScore = 0;
-	int16 _topCatSaveHistory = 0;
-	int16 _topCatScore = 0;
-	int16 _topCatShuffledQuestionsArrayIdx = 0;
-	uint32 _topCatBlinkTimer = 0;
-	SoundInfo _topCatSoundInfo;
-	byte _topCatSavedShuffledQuestions[60] = { 0 };
-	byte _topCatSavedShuffledAnswers[60] = { 0 };
-	byte _topCatSavedAnswersPermutations[60 * 3] = { 0 };
-	byte _topCatSaveBuffer[302] = { 0 };
-	byte *_topCatHoveredEntry = nullptr;
-	byte *_topCatHelpButton = nullptr;
-	byte *_topCatBackButton = nullptr;
-	byte *_topCatBlinkEntry = nullptr;
-	int16 _topCatCycleStep = 0;
-	int16 _topCatDrawnQuestionId = 0;
-	int16 _topCatCurrentAnswerIdx = 0;
-	XPCycleState _topCatChoiceCycleState[4];
-	TopCatAnim _topCatAnimQueue[3];
-	int16 _topCatCorrectAnimIdx = 0;
-	int16 _topCatWrongAnimIdx = 0;
-	int16 _topCatShouldPlayAnswerAnim = 0;
-	int16 _topCatMaintainSoundFlag = 0;
-	int16 _topCatPermTableA[3] = { 0, 1, 2 };
-	int16 _topCatPermTableB[3] = { 0, 1, 2 };
-	int16 _topCatBlinkSeq0[5] = { 0x01, 0x00, 0x01, 0x00, 0x01 };
-	int16 _topCatBlinkSeq1[5] = { 0x03, 0x01, 0x03, 0x01, 0x03 };
-	int16 _topCatBlinkSeq2[5] = { 0x07, 0x03, 0x07, 0x03, 0x07 };
-	int16 _topCatBlinkSeq3[5] = { 0x0F, 0x07, 0x0F, 0x07, 0x0F };
-	int16 _topCatBlinkSeq4[5] = { 0x1F, 0x0F, 0x1F, 0x0F, 0x1F };
-	int16 _topCatBlinkSeq5[5] = { 0x3F, 0x1F, 0x3F, 0x1F, 0x3F };
-	int16 _topCatBlinkSeq6[25] = {
-		0x09, 0x12, 0x24, 0x09, 0x12, 0x24,
-		0x09, 0x12, 0x24, 0x09, 0x12, 0x24,
-		0x09, 0x12, 0x24, 0x09, 0x12, 0x24,
-		0x09, 0x12, 0x24, 0x09, 0x12, 0x24,
-		0x3F
-	};
-
-	int16 *_topCatBlinkSeqTable[7] = {
-		_topCatBlinkSeq0, _topCatBlinkSeq1, _topCatBlinkSeq2,
-		_topCatBlinkSeq3, _topCatBlinkSeq4, _topCatBlinkSeq5,
-		_topCatBlinkSeq6
-	};
-
-	// --- YOGI ---
-	void playSoundMapYogi(int16 memberId);
-	void waitSoundMapYogi();
-	void stopSoundYogi();
-	void setYogiColors(int16 which);
-	void restoreYogiColors(int16 which);
-	void drawBasket(int16 slot, byte *basketSprite);
-	void drawAllBaskets();
-	void handleYogiMatch();
-	bool loadYogiBgPic();
-	void unloadYogiBgPic();
-	void drawYogiLevel();
-	bool loadYogiLevel();
-	void unloadYogiResources();
-	bool initYogiLevel();
-	bool resumeYogiLevel();
-	bool initYogi();
-	void yogiToggleBlinking(int16 which, int16 *state);
-	void yogiUpdateHotSpots(int16 x, int16 y);
-	int16 findBasket(int16 x, int16 y);
-	void resolveYogiSelection();
-	bool handleBasketSelect(int16 x, int16 y);
-	void yogiHandleActionButton(int16 x, int16 y);
-	void handleYogiEvent(int16 eventType, uint32 eventData);
-	void playYogi();
-	void cleanUpYogi();
-	int16 yogiGame(int16 prevBooth);
-
-	void swapYogiAllWords();
-	void swapYogiFirstWord();
-
-	static void swapYogiAllWordsCb();
-	static void swapYogiFirstWordCb();
-
-	BOLTLib *_yogiBoltLib = nullptr;
-	BOLTCallbacks _yogiBoltCallbacks;
-
-	static BOLTCallback _yogiTypeLoadCallbacks[27];
-	static BOLTCallback _yogiTypeFreeCallbacks[27];
-
-	int16 _yogiSoundPlaying = 0;
-	int16 _yogiSoundActive = 0;
-	int16 _yogiHotSpotCount = 0;
-	int16 _yogiAnimActive = 0;
-	uint32 _yogiBlinkTimer1 = 0;
-	uint32 _yogiBlinkTimer2 = 0;
-	int16 _yogiBlinkState1 = 0;
-	int16 _yogiBlinkState2 = 0;
-	int16 _yogiGlobal[0xA0] = { 0 };
-	YogiState _yogiState;
-	byte *_yogiBasketPic = nullptr;
-	int16 _yogiLevelGroupId = 0;
-	byte *_yogiBgPic = nullptr;
-	byte *_yogiNormalSprite = nullptr;
-	byte *_yogiHlSprite = nullptr;
-	byte *_yogiAnimSprite = nullptr;
-	int16 _yogiSpriteStride = 0;
-	int16 _yogiSpriteHeight = 0;
-	int16 _yogiPalRange[8] = { 0 };
-	int16 _yogiReturnBooth = 0;
-	int16 _yogiExitFlag = 0;
-	int16 _yogiLevelId = 0;
-	int16 _yogiCursorX = 0;
-	int16 _yogiCursorY = 0;
-	byte _yogiPalSave0[15] = { 0 };
-	byte _yogiPalHighlight0[15] = { 0 };
-	byte _yogiPalSave1[15] = { 0 };
-	byte _yogiPalHighlight1[15] = { 0 };
-	XPPicDesc _yogiScratchBuf;
 };
 
 extern BoltEngine *g_engine;
diff --git a/engines/bolt/barker.cpp b/engines/bolt/carnival/barker.cpp
similarity index 83%
rename from engines/bolt/barker.cpp
rename to engines/bolt/carnival/barker.cpp
index f9d355c70a6..ec02102346e 100644
--- a/engines/bolt/barker.cpp
+++ b/engines/bolt/carnival/barker.cpp
@@ -19,11 +19,13 @@
  *
  */
 
-#include "bolt/bolt.h"
+#include "bolt/carnival/carnival.h"
 
 namespace Bolt {
 
-BarkerTable *BoltEngine::createBarker(int16 minIndex, int16 maxIndex) {
+namespace Carnival {
+
+BarkerTable *CarnivalEngine::createBarker(int16 minIndex, int16 maxIndex) {
 	bool success = false;
 
 	BarkerTable *table = new BarkerTable();
@@ -52,7 +54,7 @@ BarkerTable *BoltEngine::createBarker(int16 minIndex, int16 maxIndex) {
 	}
 }
 
-void BoltEngine::freeBarker(BarkerTable *table) {
+void CarnivalEngine::freeBarker(BarkerTable *table) {
 	if (!table)
 		return;
 
@@ -62,7 +64,7 @@ void BoltEngine::freeBarker(BarkerTable *table) {
 	delete table;
 }
 
-bool BoltEngine::registerSideShow(BarkerTable *table, SideShowHandler handler, int16 boothId) {
+bool CarnivalEngine::registerSideShow(BarkerTable *table, SideShowHandler handler, int16 boothId) {
 	if (table->minIndex > boothId || table->maxIndex < boothId) {
 		_curErrorCode = 5;
 		return false;
@@ -74,7 +76,7 @@ bool BoltEngine::registerSideShow(BarkerTable *table, SideShowHandler handler, i
 	return true;
 }
 
-int16 BoltEngine::barker(BarkerTable *table, int16 startBooth) {
+int16 CarnivalEngine::barker(BarkerTable *table, int16 startBooth) {
 	int16 prevBooth = 0;
 	int16 currentBooth = startBooth;
 
@@ -93,8 +95,10 @@ int16 BoltEngine::barker(BarkerTable *table, int16 startBooth) {
 	return _currentBoothId;
 }
 
-bool BoltEngine::checkError() {
+bool CarnivalEngine::checkError() {
 	return _curErrorCode != 0;
 }
 
+} // End of namespace Carnival
+
 } // End of namespace Bolt
diff --git a/engines/bolt/booth.cpp b/engines/bolt/carnival/booth.cpp
similarity index 93%
rename from engines/bolt/booth.cpp
rename to engines/bolt/carnival/booth.cpp
index 95d072bf756..94463c0b732 100644
--- a/engines/bolt/booth.cpp
+++ b/engines/bolt/carnival/booth.cpp
@@ -19,11 +19,13 @@
  *
  */
 
-#include "bolt/bolt.h"
+#include "bolt/carnival/carnival.h"
 
 namespace Bolt {
 
-void BoltEngine::startCycle(byte *cycleResource) {
+namespace Carnival {
+
+void CarnivalEngine::startCycle(byte *cycleResource) {
 	if (!cycleResource)
 		return;
 
@@ -32,7 +34,7 @@ void BoltEngine::startCycle(byte *cycleResource) {
 	_xp->startCycle(specs);
 }
 
-void BoltEngine::displayBooth(int16 page) {
+void CarnivalEngine::displayBooth(int16 page) {
 	_xp->setTransparency(page);
 	_xp->displayPic(&_boothLetterSprite, _displayX, _displayY, page);
 	displayColors(_boothPalCycleData, page, 0);
@@ -43,7 +45,7 @@ void BoltEngine::displayBooth(int16 page) {
 	_xp->updateDisplay();
 }
 
-void BoltEngine::playAVOverBooth(int16 animIndex) {
+void CarnivalEngine::playAVOverBooth(int16 animIndex) {
 	_xp->hideCursor();
 	displayBooth(stBack);
 	playAV(_rtfHandle, animIndex, _displayWidth, _displayHeight, _displayX, _displayY);
@@ -51,7 +53,7 @@ void BoltEngine::playAVOverBooth(int16 animIndex) {
 	_xp->showCursor();
 }
 
-int16 BoltEngine::hucksBooth(int16 prevBooth) {
+int16 CarnivalEngine::hucksBooth(int16 prevBooth) {
 	int16 result = openBooth(3);
 	closeBooth();
 
@@ -71,7 +73,7 @@ int16 BoltEngine::hucksBooth(int16 prevBooth) {
 	}
 }
 
-int16 BoltEngine::fredsBooth(int16 prevBooth) {
+int16 CarnivalEngine::fredsBooth(int16 prevBooth) {
 	int16 result = openBooth(4);
 	closeBooth();
 
@@ -91,7 +93,7 @@ int16 BoltEngine::fredsBooth(int16 prevBooth) {
 	}
 }
 
-int16 BoltEngine::scoobysBooth(int16 prevBooth) {
+int16 CarnivalEngine::scoobysBooth(int16 prevBooth) {
 	int16 result = openBooth(5);
 	closeBooth();
 
@@ -114,7 +116,7 @@ int16 BoltEngine::scoobysBooth(int16 prevBooth) {
 	}
 }
 
-int16 BoltEngine::yogisBooth(int16 prevBooth) {
+int16 CarnivalEngine::yogisBooth(int16 prevBooth) {
 	int16 result = openBooth(6);
 	closeBooth();
 
@@ -137,7 +139,7 @@ int16 BoltEngine::yogisBooth(int16 prevBooth) {
 	}
 }
 
-int16 BoltEngine::georgesBooth(int16 prevBooth) {
+int16 CarnivalEngine::georgesBooth(int16 prevBooth) {
 	int16 result = openBooth(7);
 	closeBooth();
 
@@ -160,7 +162,7 @@ int16 BoltEngine::georgesBooth(int16 prevBooth) {
 	}
 }
 
-int16 BoltEngine::topCatsBooth(int16 prevBooth) {
+int16 CarnivalEngine::topCatsBooth(int16 prevBooth) {
 	int16 result = openBooth(8);
 	closeBooth();
 
@@ -180,7 +182,7 @@ int16 BoltEngine::topCatsBooth(int16 prevBooth) {
 	}
 }
 
-int16 BoltEngine::mainEntrance(int16 prevBooth) {
+int16 CarnivalEngine::mainEntrance(int16 prevBooth) {
 	int16 result = openBooth(9);
 	closeBooth();
 
@@ -206,7 +208,7 @@ int16 BoltEngine::mainEntrance(int16 prevBooth) {
 	}
 }
 
-bool BoltEngine::loadBooth(int16 boothId) {
+bool CarnivalEngine::loadBooth(int16 boothId) {
 	switch (boothId) {
 	case 3:
 		if (_boothLoadedMask & 0x01)
@@ -270,7 +272,7 @@ bool BoltEngine::loadBooth(int16 boothId) {
 	return true;
 }
 
-void BoltEngine::unloadBooth() {
+void CarnivalEngine::unloadBooth() {
 	if (_boothLoadedMask & 0x01)
 		freeBOLTGroup(_boothsBoltLib, 0x100, 1);
 
@@ -297,7 +299,7 @@ void BoltEngine::unloadBooth() {
 	_boothLoadedMask = 0;
 }
 
-int16 BoltEngine::openBooth(int16 boothId) {
+int16 CarnivalEngine::openBooth(int16 boothId) {
 	int16 baseResId;
 	int16 resId;
 
@@ -392,7 +394,7 @@ int16 BoltEngine::openBooth(int16 boothId) {
 	return boothEventLoop();
 }
 
-void BoltEngine::closeBooth() {
+void CarnivalEngine::closeBooth() {
 	uint16 buttonState;
 
 	_xp->readCursor(&buttonState, &_cursorY, &_cursorX);
@@ -401,7 +403,7 @@ void BoltEngine::closeBooth() {
 	unloadBooth();
 }
 
-void BoltEngine::playTour() {
+void CarnivalEngine::playTour() {
 	int16 playing = 1;
 
 	_xp->hideCursor();
@@ -442,7 +444,7 @@ void BoltEngine::playTour() {
 	_xp->showCursor();
 }
 
-void BoltEngine::finishPlayingHelp(int16 activeHotspot) {
+void CarnivalEngine::finishPlayingHelp(int16 activeHotspot) {
 	_helpPlaying = 0;
 
 	if (_helpIsIdle != 0) {
@@ -483,7 +485,7 @@ void BoltEngine::finishPlayingHelp(int16 activeHotspot) {
 	}
 }
 
-int16 BoltEngine::hotSpotActive(int16 hotspot) {
+int16 CarnivalEngine::hotSpotActive(int16 hotspot) {
 	switch (hotspot) {
 	case 0:
 	case 1:
@@ -512,7 +514,7 @@ int16 BoltEngine::hotSpotActive(int16 hotspot) {
 	}
 }
 
-void BoltEngine::hoverHotSpot() {
+void CarnivalEngine::hoverHotSpot() {
 	int16 i = 0;
 
 	while (i < _boothNumHotspots) {
@@ -538,7 +540,7 @@ void BoltEngine::hoverHotSpot() {
 	}
 }
 
-int16 BoltEngine::boothEventLoop() {
+int16 CarnivalEngine::boothEventLoop() {
 	uint32 eventData;
 	int16 exitCode = 0;
 
@@ -699,7 +701,7 @@ int16 BoltEngine::boothEventLoop() {
 	return _hoveredHotspot;
 }
 
-void BoltEngine::resetInactivityState() {
+void CarnivalEngine::resetInactivityState() {
 	if (_helpTimer != 0) {
 		_xp->killTimer(_helpTimer);
 		_helpTimer = 0;
@@ -717,7 +719,7 @@ void BoltEngine::resetInactivityState() {
 	_helpFlag = 0;
 }
 
-bool BoltEngine::handleButtonPress(int16 hotspot) {
+bool CarnivalEngine::handleButtonPress(int16 hotspot) {
 	byte savedLeftDoor[3];
 	byte savedRightDoor[3];
 
@@ -879,7 +881,7 @@ bool BoltEngine::handleButtonPress(int16 hotspot) {
 	}
 }
 
-void BoltEngine::blastColors(byte **paletteTable, int16 index, int16 mode) {
+void CarnivalEngine::blastColors(byte **paletteTable, int16 index, int16 mode) {
 	byte localPalette[384];
 	int16 si = 0;
 
@@ -900,7 +902,7 @@ void BoltEngine::blastColors(byte **paletteTable, int16 index, int16 mode) {
 	}
 }
 
-void BoltEngine::setColors(int16 index) {
+void CarnivalEngine::setColors(int16 index) {
 	if (_currentBoothScene == 9) {
 		switch (index) {
 		case 0:
@@ -948,7 +950,7 @@ void BoltEngine::setColors(int16 index) {
 	}
 }
 
-void BoltEngine::restoreColors(int16 index) {
+void CarnivalEngine::restoreColors(int16 index) {
 	if (_currentBoothScene == 9) {
 		switch (index) {
 		case 0:
@@ -1008,7 +1010,7 @@ void BoltEngine::restoreColors(int16 index) {
 	}
 }
 
-void BoltEngine::loadColors() {
+void CarnivalEngine::loadColors() {
 	for (int16 i = 0; i < _boothNumAnimations; i++) {
 		byte *animDesc = _boothAnimDescs[i];
 		_xp->getPalette((int16)READ_UINT16(animDesc), (int16)READ_UINT16(animDesc + 2), _savedPalettes[i]);
@@ -1026,12 +1028,12 @@ void BoltEngine::loadColors() {
 	}
 }
 
-void BoltEngine::shiftColorMap(byte *colorMap, int16 delta) {
+void CarnivalEngine::shiftColorMap(byte *colorMap, int16 delta) {
 	WRITE_BE_INT16(colorMap + 2, READ_BE_INT16(colorMap + 2) + delta);
 	WRITE_BE_INT16(colorMap + 4, READ_BE_INT16(colorMap + 4) + delta);
 }
 
-void BoltEngine::playBoothAV() {
+void CarnivalEngine::playBoothAV() {
 	switch (_currentBoothScene) {
 	case 3:
 		playAVOverBooth(6 - (_isDemo ? 3 : 0));
@@ -1054,7 +1056,7 @@ void BoltEngine::playBoothAV() {
 	}
 }
 
-void BoltEngine::mainEntranceHelpBlink() {
+void CarnivalEngine::mainEntranceHelpBlink() {
 	_tourStep++;
 
 	switch (_tourStep) {
@@ -1126,7 +1128,7 @@ void BoltEngine::mainEntranceHelpBlink() {
 	}
 }
 
-void BoltEngine::boothHelpBlink() {
+void CarnivalEngine::boothHelpBlink() {
 	_tourStep++;
 
 	switch (_tourStep) {
@@ -1169,7 +1171,7 @@ void BoltEngine::boothHelpBlink() {
 	}
 }
 
-void BoltEngine::tourPaletteCycleStep() {
+void CarnivalEngine::tourPaletteCycleStep() {
 	static const int16 cycleResIds[] = {
 		0x700, 0x719, 0x71E, 0x720, 0x725, 0x72A, 0x72F, 0x731,
 		-1,    0x736, 0x73B, 0x73F, 0x743, -1
@@ -1193,7 +1195,7 @@ void BoltEngine::tourPaletteCycleStep() {
 	startCycle(memberAddr(_boothsBoltLib, resId));
 }
 
-void BoltEngine::fadeToBlack(int16 steps) {
+void CarnivalEngine::fadeToBlack(int16 steps) {
 	byte palette[768];
 	XPPicDesc desc;
 
@@ -1229,7 +1231,7 @@ void BoltEngine::fadeToBlack(int16 steps) {
 	_xp->setFrameRate(0);
 }
 
-void BoltEngine::flushInput() {
+void CarnivalEngine::flushInput() {
 	uint32 eventBuf;
 
 	// Drain all mouse-down events
@@ -1239,7 +1241,7 @@ void BoltEngine::flushInput() {
 	while (_xp->getEvent(etMouseUp, &eventBuf) == etMouseUp);
 }
 
-int16 BoltEngine::winALetter(int16 prevBooth) {
+int16 CarnivalEngine::winALetter(int16 prevBooth) {
 	_lettersWon++;
 
 	if (_isDemo && _lettersWon == 6)
@@ -1303,7 +1305,7 @@ int16 BoltEngine::winALetter(int16 prevBooth) {
 	}
 }
 
-int16 BoltEngine::endDemo(int16 prevBooth) {
+int16 CarnivalEngine::endDemo(int16 prevBooth) {
 	_xp->hideCursor();
 	_xp->setTransparency(false);
 
@@ -1319,4 +1321,6 @@ int16 BoltEngine::endDemo(int16 prevBooth) {
 	return 0;
 }
 
+} // End of namespace Carnival
+
 } // End of namespace Bolt
diff --git a/engines/bolt/booths/fred.cpp b/engines/bolt/carnival/booths/fred.cpp
similarity index 96%
rename from engines/bolt/booths/fred.cpp
rename to engines/bolt/carnival/booths/fred.cpp
index 0bba92b44ce..ef10f64e82b 100644
--- a/engines/bolt/booths/fred.cpp
+++ b/engines/bolt/carnival/booths/fred.cpp
@@ -19,11 +19,13 @@
  *
  */
 
-#include "bolt/bolt.h"
+#include "bolt/carnival/carnival.h"
 
 namespace Bolt {
 
-int16 BoltEngine::fredGame(int16 prevBooth) {
+namespace Carnival {
+
+int16 CarnivalEngine::fredGame(int16 prevBooth) {
 	int16 savedTimer = _xp->setInactivityTimer(30);
 
 	if (!initFred()) {
@@ -38,7 +40,7 @@ int16 BoltEngine::fredGame(int16 prevBooth) {
 	return result;
 }
 
-bool BoltEngine::initFred() {
+bool CarnivalEngine::initFred() {
 	const char *path = assetPath("fred.blt");
 
 	if (!openBOLTLib(&_fredBoltLib, &_fredBoltCallbacks, path))
@@ -127,7 +129,7 @@ bool BoltEngine::initFred() {
 	return true;
 }
 
-void BoltEngine::cleanUpFred() {
+void CarnivalEngine::cleanUpFred() {
 	int16 savedLevel = _fredSaveData[1]; // Level data group index
 	int16 savedPalette = _fredSaveData[2]; // Palette group index
 
@@ -159,7 +161,7 @@ void BoltEngine::cleanUpFred() {
 	_xp->updateDisplay();
 }
 
-bool BoltEngine::initFredLevel(int16 levelGroup, int16 palGroup) {
+bool CarnivalEngine::initFredLevel(int16 levelGroup, int16 palGroup) {
 	// Load level data group...
 	if (!getBOLTGroup(_fredBoltLib, levelGroup * 0x200 + 0x100, 1))
 		return false;
@@ -283,7 +285,7 @@ bool BoltEngine::initFredLevel(int16 levelGroup, int16 palGroup) {
 	return true;
 }
 
-void BoltEngine::termFredLevel(int16 levelGroup, int16 palGroup) {
+void CarnivalEngine::termFredLevel(int16 levelGroup, int16 palGroup) {
 	// Free balloon state structs...
 	if (_fredEntitiesTable) {
 		uint16 numBalloons = READ_UINT16(_fredLevelPtr + 0x0A);
@@ -310,7 +312,7 @@ void BoltEngine::termFredLevel(int16 levelGroup, int16 palGroup) {
 	freeBOLTGroup(_fredBoltLib, levelGroup * 0x200 + 0x100, 1);
 }
 
-void BoltEngine::swapFredAnimEntry() {
+void CarnivalEngine::swapFredAnimEntry() {
 	byte *data = _boltCurrentMemberEntry->dataPtr;
 	uint32 decompSize = _boltCurrentMemberEntry->decompSize;
 	uint32 offset = 0;
@@ -322,7 +324,7 @@ void BoltEngine::swapFredAnimEntry() {
 	}
 }
 
-void BoltEngine::swapFredAnimDesc() {
+void CarnivalEngine::swapFredAnimDesc() {
 	byte *data = _boltCurrentMemberEntry->dataPtr;
 	uint32 decompSize = _boltCurrentMemberEntry->decompSize;
 	uint32 offset = 0;
@@ -347,7 +349,7 @@ void BoltEngine::swapFredAnimDesc() {
 	}
 }
 
-void BoltEngine::swapFredLevelDesc() {
+void CarnivalEngine::swapFredLevelDesc() {
 	byte *data = _boltCurrentMemberEntry->dataPtr;
 
 	WRITE_UINT16(data + 0x00, READ_BE_INT16(data + 0x00));
@@ -359,7 +361,7 @@ void BoltEngine::swapFredLevelDesc() {
 	WRITE_UINT16(data + 0x0C, READ_BE_INT16(data + 0x0C));
 }
 
-int16 BoltEngine::playFred() {
+int16 CarnivalEngine::playFred() {
 	int16 result = 0;
 	int16 allCaught = 0;
 	int16 exitLoop = 0;
@@ -713,7 +715,7 @@ int16 BoltEngine::playFred() {
 	return result;
 }
 
-int16 BoltEngine::helpFred() {
+int16 CarnivalEngine::helpFred() {
 	byte *firstEntry = getResolvedPtr(_fredHelpEntries, 0);
 	byte *picDesc = getResolvedPtr(firstEntry, 4);
 
@@ -949,7 +951,7 @@ int16 BoltEngine::helpFred() {
 	return exitResult;
 }
 
-void BoltEngine::hiliteFredHelpObject(byte *entry, int16 highlight) {
+void CarnivalEngine::hiliteFredHelpObject(byte *entry, int16 highlight) {
 	if (!entry)
 		return;
 
@@ -966,7 +968,7 @@ void BoltEngine::hiliteFredHelpObject(byte *entry, int16 highlight) {
 	}
 }
 
-void BoltEngine::helpAnimStep() {
+void CarnivalEngine::helpAnimStep() {
 	if (_fredHelpStep < 0 || _fredHelpStep >= 4)
 		return;
 
@@ -1000,7 +1002,7 @@ void BoltEngine::helpAnimStep() {
 	_fredHelpStep++;
 }
 
-bool BoltEngine::spawnBalloon() {
+bool CarnivalEngine::spawnBalloon() {
 	// Count active balloons...
 	int16 activeCount = 0;
 	int16 i = 0;
@@ -1087,13 +1089,13 @@ bool BoltEngine::spawnBalloon() {
 	return true;
 }
 
-int16 BoltEngine::calcBalloonSpawnDelay() {
+int16 CarnivalEngine::calcBalloonSpawnDelay() {
 	int16 range = READ_UINT16(_fredLevelPtr + 2);
 	int16 base = READ_UINT16(_fredLevelPtr + 0);
 	return _xp->getRandom(range) + base;
 }
 
-int16 BoltEngine::selectBalloonRow() {
+int16 CarnivalEngine::selectBalloonRow() {
 	// Get Fred's center X position...
 	int32 fredX = _fredSprite.xPos >> 8;
 
@@ -1124,7 +1126,7 @@ int16 BoltEngine::selectBalloonRow() {
 	return randomRow;
 }
 
-void BoltEngine::setFredAnimMode(FredEntityState *state, int16 mode) {
+void CarnivalEngine::setFredAnimMode(FredEntityState *state, int16 mode) {
 	if (mode >= 0x0B && mode < 0x0B + (_fredLevelPtr ? READ_UINT16(_fredLevelPtr + 0x0C) : 0)) {
 		state->animMode = 0x0B;
 
@@ -1175,7 +1177,7 @@ void BoltEngine::setFredAnimMode(FredEntityState *state, int16 mode) {
 	}
 }
 
-void BoltEngine::renderFredScene() {
+void CarnivalEngine::renderFredScene() {
 	_xp->fillDisplay(0, stFront);
 
 	int16 idx = 0;
@@ -1204,12 +1206,12 @@ void BoltEngine::renderFredScene() {
 	}
 }
 
-void BoltEngine::getFredSoundInfo(BOLTLib *lib, int16 memberId, SoundInfo *soundInfo) {
+void CarnivalEngine::getFredSoundInfo(BOLTLib *lib, int16 memberId, SoundInfo *soundInfo) {
 	soundInfo->data = memberAddr(lib, memberId);
 	soundInfo->size = memberSize(lib, memberId);
 }
 
-void BoltEngine::playFredSound(SoundInfo *oneShot, SoundInfo *loop) {
+void CarnivalEngine::playFredSound(SoundInfo *oneShot, SoundInfo *loop) {
 	_fredPendingOneShot = oneShot;
 	_fredPendingLoop = loop; 
 
@@ -1231,7 +1233,7 @@ void BoltEngine::playFredSound(SoundInfo *oneShot, SoundInfo *loop) {
 	}
 }
 
-void BoltEngine::updateFredSound() {
+void CarnivalEngine::updateFredSound() {
 	bool startLoop = false;
 
 	if (_fredLoopSound != nullptr) {
@@ -1263,4 +1265,6 @@ void BoltEngine::updateFredSound() {
 	}
 }
 
+} // End of namespace Carnival
+
 } // End of namespace Bolt
diff --git a/engines/bolt/booths/george.cpp b/engines/bolt/carnival/booths/george.cpp
similarity index 95%
rename from engines/bolt/booths/george.cpp
rename to engines/bolt/carnival/booths/george.cpp
index 05c4ce2899e..4be74a19ff0 100644
--- a/engines/bolt/booths/george.cpp
+++ b/engines/bolt/carnival/booths/george.cpp
@@ -19,11 +19,13 @@
  *
  */
 
-#include "bolt/bolt.h"
+#include "bolt/carnival/carnival.h"
 
 namespace Bolt {
 
-int16 BoltEngine::georgeGame(int16 prevBooth) {
+namespace Carnival {
+
+int16 CarnivalEngine::georgeGame(int16 prevBooth) {
 	int16 result;
 	int16 prevInactivity = _xp->setInactivityTimer(30);
 
@@ -38,7 +40,7 @@ int16 BoltEngine::georgeGame(int16 prevBooth) {
 	return result;
 }
 
-bool BoltEngine::initGeorge() {
+bool CarnivalEngine::initGeorge() {
 	if (!openBOLTLib(&_georgeBoltLib, &_georgeBoltCallbacks, assetPath("george.blt")))
 		return false;
 
@@ -115,7 +117,7 @@ bool BoltEngine::initGeorge() {
 	return true;
 }
 
-void BoltEngine::cleanUpGeorge() {
+void CarnivalEngine::cleanUpGeorge() {
 	// Save current progress...
 	int16 savedLevel = _georgeSaveData[1];
 	int16 savedVariant = _georgeSaveData[2];
@@ -152,7 +154,7 @@ void BoltEngine::cleanUpGeorge() {
 	_xp->updateDisplay();
 }
 
-bool BoltEngine::initGeorgeLevel(int16 level, int16 variant) {
+bool CarnivalEngine::initGeorgeLevel(int16 level, int16 variant) {
 	int16 levelGroup = (level * 2) << 8 | 0x100;
 	int16 variantGroup = (variant * 2) << 8 | 0x200;
 
@@ -360,7 +362,7 @@ bool BoltEngine::initGeorgeLevel(int16 level, int16 variant) {
 	return true;
 }
 
-void BoltEngine::termGeorgeLevel(int16 level, int16 variant) {
+void CarnivalEngine::termGeorgeLevel(int16 level, int16 variant) {
 	if (_georgeEntityList) {
 		for (int16 i = 0; i < _georgeTotalSatellites; i++) {
 			GeorgeEntityState *sat = _georgeEntityList[i];
@@ -402,7 +404,7 @@ void BoltEngine::termGeorgeLevel(int16 level, int16 variant) {
 	freeBOLTGroup(_georgeBoltLib, (level * 2) << 8 | 0x100, 1);
 }
 
-void BoltEngine::swapGeorgeFrameArray() {
+void CarnivalEngine::swapGeorgeFrameArray() {
 	byte *data = _boltCurrentMemberEntry->dataPtr;
 	uint32 size = _boltCurrentMemberEntry->decompSize;
 	uint32 off = 0;
@@ -414,7 +416,7 @@ void BoltEngine::swapGeorgeFrameArray() {
 	}
 }
 
-void BoltEngine::swapGeorgeHelpEntry() {
+void CarnivalEngine::swapGeorgeHelpEntry() {
 	byte *data = _boltCurrentMemberEntry->dataPtr;
 	uint32 size = _boltCurrentMemberEntry->decompSize;
 	uint32 off = 0;
@@ -435,13 +437,13 @@ void BoltEngine::swapGeorgeHelpEntry() {
 	}
 }
 
-void BoltEngine::swapGeorgeThresholds() {
+void CarnivalEngine::swapGeorgeThresholds() {
 	byte *data = _boltCurrentMemberEntry->dataPtr;
 	for (int16 i = 0; i < 12; i++)
 		WRITE_UINT16(data + i * 2, READ_BE_UINT16(data + i * 2));
 }
 
-int16 BoltEngine::playGeorge() {
+int16 CarnivalEngine::playGeorge() {
 	int16 returnCode = 0;
 	int16 flyActive = 1;
 	int16 winSeq = 0;
@@ -779,7 +781,7 @@ int16 BoltEngine::playGeorge() {
 	return returnCode;
 }
 
-int16 BoltEngine::helpGeorge() {
+int16 CarnivalEngine::helpGeorge() {
 	byte *firstObj = getResolvedPtr(_georgeHelpObjects, 0);
 	byte *firstInfo = getResolvedPtr(firstObj, 0x04);
 	int16 curX = READ_UINT16(firstInfo + 0x06) + READ_UINT16(firstInfo + 0x0A) - 10;
@@ -1005,7 +1007,7 @@ int16 BoltEngine::helpGeorge() {
 	return exitCode;
 }
 
-void BoltEngine::hiliteGeorgeHelpObject(byte *entry, int16 highlight) {
+void CarnivalEngine::hiliteGeorgeHelpObject(byte *entry, int16 highlight) {
 	if (!entry)
 		return;
 
@@ -1018,7 +1020,7 @@ void BoltEngine::hiliteGeorgeHelpObject(byte *entry, int16 highlight) {
 	}
 }
 
-void BoltEngine::advanceHelpAnimation() {
+void CarnivalEngine::advanceHelpAnimation() {
 	if (_georgeHelpStep < 0 || _georgeHelpStep >= 4)
 		return;
 
@@ -1048,7 +1050,7 @@ void BoltEngine::advanceHelpAnimation() {
 
 	_georgeHelpStep++;
 }
-bool BoltEngine::spawnSatellite() {
+bool CarnivalEngine::spawnSatellite() {
 	// Count active satellites (type 3)...
 	int16 activeCount = 0;
 	for (int16 i = 0;; i++) {
@@ -1115,13 +1117,13 @@ bool BoltEngine::spawnSatellite() {
 	return true;
 }
 
-int16 BoltEngine::getRandomSatelliteWait() {
+int16 CarnivalEngine::getRandomSatelliteWait() {
 	int16 range = _georgeThresholds[2];
 	int16 topY = _georgeThresholds[1];
 	return _xp->getRandom(range) + topY;
 }
 
-int16 BoltEngine::getRandomAsteroidRow() {
+int16 CarnivalEngine::getRandomAsteroidRow() {
 	GeorgeEntityState *carSat = _georgeEntityList[_georgeCarIdx];
 	int16 carY = carSat->y >> 8;
 
@@ -1147,7 +1149,7 @@ int16 BoltEngine::getRandomAsteroidRow() {
 	return randRow;
 }
 
-bool BoltEngine::confirmAsteroidHitTest() {
+bool CarnivalEngine::confirmAsteroidHitTest() {
 	GeorgeEntityState *candidate = nullptr;
 	int16 startIdx = _georgeHitSearchIdx;
 	int16 idx = startIdx;
@@ -1175,7 +1177,7 @@ bool BoltEngine::confirmAsteroidHitTest() {
 	return true;
 }
 
-bool BoltEngine::spawnAsteroid() {
+bool CarnivalEngine::spawnAsteroid() {
 	// Count active asteroids (type 5)...
 	int16 activeCount = 0;
 	for (int16 i = 0;; i++) {
@@ -1241,13 +1243,13 @@ bool BoltEngine::spawnAsteroid() {
 	return true;
 }
 
-int16 BoltEngine::getRandomAsteroidWait() {
+int16 CarnivalEngine::getRandomAsteroidWait() {
     int16 base  = _georgeThresholds[7];
     int16 range = _georgeThresholds[8];
     return _xp->getRandom(range) + base;
 }
 
-int16 BoltEngine::getAsteroidRow() {
+int16 CarnivalEngine::getAsteroidRow() {
 	GeorgeEntityState *carSat = _georgeEntityList[_georgeCarIdx];
 	int16 carY = carSat->y >> 8;
 
@@ -1266,7 +1268,7 @@ int16 BoltEngine::getAsteroidRow() {
 	return row;
 }
 
-void BoltEngine::setGeorgeAnimMode(GeorgeEntityState *entity, int16 mode) {
+void CarnivalEngine::setGeorgeAnimMode(GeorgeEntityState *entity, int16 mode) {
 	entity->animMode = mode;
 	entity->variant = 0;
 	entity->animTable = _georgeCarPics[mode];
@@ -1274,7 +1276,7 @@ void BoltEngine::setGeorgeAnimMode(GeorgeEntityState *entity, int16 mode) {
 	entity->frameCountdown = READ_UINT16(entity->animTable + 0x04);
 }
 
-void BoltEngine::setSatelliteAnimMode(GeorgeEntityState *entity, int16 mode, int16 variant) {
+void CarnivalEngine::setSatelliteAnimMode(GeorgeEntityState *entity, int16 mode, int16 variant) {
 	if (mode == 4) {
 		// Deactivate...
 		entity->animMode = mode;
@@ -1289,7 +1291,7 @@ void BoltEngine::setSatelliteAnimMode(GeorgeEntityState *entity, int16 mode, int
 	entity->frameCountdown = READ_UINT16(entity->animTable + 0x04);
 }
 
-void BoltEngine::setAsteroidAnimMode(GeorgeEntityState *entity, int16 mode, int16 variant) {
+void CarnivalEngine::setAsteroidAnimMode(GeorgeEntityState *entity, int16 mode, int16 variant) {
 	entity->animMode = mode;
 	entity->variant = variant;
 	entity->animTable = getResolvedPtr(_georgeAsteroidGfx, variant * 4);
@@ -1297,7 +1299,7 @@ void BoltEngine::setAsteroidAnimMode(GeorgeEntityState *entity, int16 mode, int1
 	entity->frameCountdown = READ_UINT16(entity->animTable + 0x04);
 }
 
-void BoltEngine::drawFlyingObjects() {
+void CarnivalEngine::drawFlyingObjects() {
 	_xp->fillDisplay(0, stFront);
 
 	for (int16 i = 0;; i++) {
@@ -1317,14 +1319,14 @@ void BoltEngine::drawFlyingObjects() {
 	}
 }
 
-void BoltEngine::getGeorgeSoundInfo(BOLTLib *boltLib, int16 member, SoundInfo *outInfo, byte priority) {
+void CarnivalEngine::getGeorgeSoundInfo(BOLTLib *boltLib, int16 member, SoundInfo *outInfo, byte priority) {
 	outInfo->data = memberAddr(boltLib, member);
 	outInfo->size = memberSize(boltLib, member);
 	outInfo->priority = priority;
 	outInfo->channel = _georgeSoundChannelCounter++;
 }
 
-void BoltEngine::playGeorgeSound(SoundInfo *newSound, SoundInfo *nextSound) {
+void CarnivalEngine::playGeorgeSound(SoundInfo *newSound, SoundInfo *nextSound) {
 	// If a queued sound exists and has higher priority than new sound, don't replace it...
 	if (newSound && _georgeSoundQueued) {
 		if (_georgeSoundQueued->priority > newSound->priority)
@@ -1356,7 +1358,7 @@ void BoltEngine::playGeorgeSound(SoundInfo *newSound, SoundInfo *nextSound) {
 		updateGeorgeSound();
 }
 
-void BoltEngine::updateGeorgeSound() {
+void CarnivalEngine::updateGeorgeSound() {
 	int16 playBoth = 0;
 
 	if (_georgeSoundCurrent != nullptr) {
@@ -1390,4 +1392,6 @@ void BoltEngine::updateGeorgeSound() {
 	_xp->playSound(_georgeSoundCurrent->data, _georgeSoundCurrent->size, 22050);
 }
 
+} // End of namespace Carnival
+
 } // End of namespace Bolt
diff --git a/engines/bolt/booths/huck.cpp b/engines/bolt/carnival/booths/huck.cpp
similarity index 94%
rename from engines/bolt/booths/huck.cpp
rename to engines/bolt/carnival/booths/huck.cpp
index 4c94e0f5bb1..635ee141524 100644
--- a/engines/bolt/booths/huck.cpp
+++ b/engines/bolt/carnival/booths/huck.cpp
@@ -21,11 +21,13 @@
 
 #include "common/memstream.h"
 
-#include "bolt/bolt.h"
+#include "bolt/carnival/carnival.h"
 
 namespace Bolt {
 
-void BoltEngine::playSoundMapHuck(int16 memberId) {
+namespace Carnival {
+
+void CarnivalEngine::playSoundMapHuck(int16 memberId) {
 	byte *soundData = getBOLTMember(_huckBoltLib, memberId);
 	uint32 soundSize = memberSize(_huckBoltLib, memberId);
 	if (soundData) {
@@ -34,7 +36,7 @@ void BoltEngine::playSoundMapHuck(int16 memberId) {
 	}
 }
 
-void BoltEngine::waitSoundMapHuck() {
+void CarnivalEngine::waitSoundMapHuck() {
 	if (!_huckSoundPlaying)
 		return;
 
@@ -43,7 +45,7 @@ void BoltEngine::waitSoundMapHuck() {
 	_huckSoundPlaying--;
 }
 
-void BoltEngine::setHuckColors(int16 which) {
+void CarnivalEngine::setHuckColors(int16 which) {
 	if (which == 0) {
 		int16 count = _huckPalRange[1] - _huckPalRange[0] + 1;
 		_xp->setPalette(count, _huckPalRange[0], _huckPalHighlight0);
@@ -53,7 +55,7 @@ void BoltEngine::setHuckColors(int16 which) {
 	}
 }
 
-void BoltEngine::restoreHuckColors(int16 which) {
+void CarnivalEngine::restoreHuckColors(int16 which) {
 	if (which == 0) {
 		int16 count = _huckPalRange[1] - _huckPalRange[0] + 1;
 		_xp->setPalette(count, _huckPalRange[0], _huckPalSave0);
@@ -63,7 +65,7 @@ void BoltEngine::restoreHuckColors(int16 which) {
 	}
 }
 
-void BoltEngine::startHuckShuffleTimer() {
+void CarnivalEngine::startHuckShuffleTimer() {
 	if (_huckShuffleTimer) {
 		_xp->killTimer(_huckShuffleTimer);
 		_huckShuffleTimer = 0;
@@ -76,7 +78,7 @@ void BoltEngine::startHuckShuffleTimer() {
 	}
 }
 
-void BoltEngine::drawGift(int16 slot) {
+void CarnivalEngine::drawGift(int16 slot) {
 	byte *giftSprite = memberAddr(_huckBoltLib, _huckState.drawTable1[slot]);
 	int16 sprStride = READ_UINT16(giftSprite + 0x0A);
 	int16 sprHeight = READ_UINT16(giftSprite + 0x0C);
@@ -134,7 +136,7 @@ void BoltEngine::drawGift(int16 slot) {
 	_xp->displayPic(&_huckScratchPic, slotY, slotX, stFront);
 }
 
-void BoltEngine::drawHuckGifts() {
+void CarnivalEngine::drawHuckGifts() {
 	_xp->fillDisplay(0, stFront);
 
 	int16 giftCount = READ_UINT16(_huckGiftPic);
@@ -166,7 +168,7 @@ void BoltEngine::drawHuckGifts() {
 	}
 }
 
-void BoltEngine::checkHuckLevelComplete() {
+void CarnivalEngine::checkHuckLevelComplete() {
 	if (_huckState.giftCount == 2) {
 		// Last pair matched, level complete!
 		_huckExitFlag = 1;
@@ -194,7 +196,7 @@ void BoltEngine::checkHuckLevelComplete() {
 	startHuckShuffleTimer();
 }
 
-bool BoltEngine::initHuckDisplay() {
+bool CarnivalEngine::initHuckDisplay() {
 	byte *palPtr = memberAddr(_huckBoltLib, READ_UINT16(_huckBgPic));
 
 	_huckHotSpotCount = 0;
@@ -266,7 +268,7 @@ bool BoltEngine::initHuckDisplay() {
 	return true;
 }
 
-bool BoltEngine::loadHuckResources() {
+bool CarnivalEngine::loadHuckResources() {
 	int16 stateIdx = _huckState.levelNumber - 1;
 	int16 giftGroupId = (stateIdx << 10) + 0x100;
 	_huckGiftGroupId = giftGroupId;
@@ -300,14 +302,14 @@ bool BoltEngine::loadHuckResources() {
 	return true;
 }
 
-void BoltEngine::unloadHuckResources() {
+void CarnivalEngine::unloadHuckResources() {
 	_xp->stopCycle();
 	_xp->hideCursor();
 	freeBOLTGroup(_huckBoltLib, _huckVariantGroupId, 1);
 	freeBOLTGroup(_huckBoltLib, _huckGiftGroupId, 1);
 }
 
-bool BoltEngine::initHuckLevel() {
+bool CarnivalEngine::initHuckLevel() {
 	// Advance slot variant (wraps 0..2)
 	int16 stateIdx = _huckState.levelNumber - 1;
 	_huckState.slotIndex[stateIdx]++;
@@ -355,7 +357,7 @@ bool BoltEngine::initHuckLevel() {
 	return true;
 }
 
-bool BoltEngine::resumeHuckLevel() {
+bool CarnivalEngine::resumeHuckLevel() {
 	if (!loadHuckResources())
 		return false;
 
@@ -370,7 +372,7 @@ bool BoltEngine::resumeHuckLevel() {
 	return true;
 }
 
-bool BoltEngine::initHuck() {
+bool CarnivalEngine::initHuck() {
 	_xp->randomize();
 	_huckSoundPlaying = 0;
 
@@ -436,7 +438,7 @@ bool BoltEngine::initHuck() {
 		return resumeHuckLevel();
 }
 
-void BoltEngine::huckToggleBlinking(int16 *state, int16 which) {
+void CarnivalEngine::huckToggleBlinking(int16 *state, int16 which) {
 	*state = (*state == 0) ? 1 : 0;
 	if (*state)
 		setHuckColors(which);
@@ -444,7 +446,7 @@ void BoltEngine::huckToggleBlinking(int16 *state, int16 which) {
 		restoreHuckColors(which);
 }
 
-void BoltEngine::huckUpdateHotSpots(int16 x, int16 y) {
+void CarnivalEngine::huckUpdateHotSpots(int16 x, int16 y) {
 	Common::Rect helpRect(
 		READ_UINT16(_huckGiftPic + 0x84), READ_UINT16(_huckGiftPic + 0x88),
 		READ_UINT16(_huckGiftPic + 0x86), READ_UINT16(_huckGiftPic + 0x8A));
@@ -472,7 +474,7 @@ void BoltEngine::huckUpdateHotSpots(int16 x, int16 y) {
 	}
 }
 
-int16 BoltEngine::findGift(int16 x, int16 y) {
+int16 CarnivalEngine::findGift(int16 x, int16 y) {
 	byte *sprite0 = memberAddr(_huckBoltLib, _huckState.drawTable1[0]);
 	int16 sprH = READ_UINT16(sprite0 + 0x0A);
 	int16 sprW = READ_UINT16(sprite0 + 0x0C);
@@ -505,7 +507,7 @@ int16 BoltEngine::findGift(int16 x, int16 y) {
 	return -1;
 }
 
-bool BoltEngine::handleGiftSelect(int16 x, int16 y) {
+bool CarnivalEngine::handleGiftSelect(int16 x, int16 y) {
 	int16 slot = findGift(x, y);
 	if (slot == -1)
 		return false;
@@ -552,7 +554,7 @@ bool BoltEngine::handleGiftSelect(int16 x, int16 y) {
 	return true;
 }
 
-void BoltEngine::huckHandleActionButton(int16 x, int16 y) {
+void CarnivalEngine::huckHandleActionButton(int16 x, int16 y) {
 	bool var_6 = false;
 	bool var_4 = false;
 
@@ -612,7 +614,7 @@ void BoltEngine::huckHandleActionButton(int16 x, int16 y) {
 	}
 }
 
-void BoltEngine::giftSwap() {
+void CarnivalEngine::giftSwap() {
 	if (_huckState.giftCount <= 2)
 		return;
 
@@ -690,7 +692,7 @@ void BoltEngine::giftSwap() {
 	drawGift(slotB);
 }
 
-void BoltEngine::resolveHuckSelection() {
+void CarnivalEngine::resolveHuckSelection() {
 	if (!_huckState.selectionPending)
 		return;
 
@@ -717,7 +719,7 @@ void BoltEngine::resolveHuckSelection() {
 	}
 }
 
-void BoltEngine::handleEvent(int16 eventType, uint32 eventData) {
+void CarnivalEngine::handleEvent(int16 eventType, uint32 eventData) {
 	switch (eventType) {
 	case etMouseDown:
 		huckHandleActionButton(_huckCursorX, _huckCursorY);
@@ -825,7 +827,7 @@ void BoltEngine::handleEvent(int16 eventType, uint32 eventData) {
 	}
 }
 
-void BoltEngine::playHuck() {
+void CarnivalEngine::playHuck() {
 	_huckReturnBooth = 0x10;
 
 	while (!shouldQuit()) {
@@ -849,7 +851,7 @@ void BoltEngine::playHuck() {
 		_huckReturnBooth = 0;
 }
 
-void BoltEngine::cleanUpHuck() {
+void CarnivalEngine::cleanUpHuck() {
 	unloadHuckResources();
 
 	if (_huckScratchPic.pixelData) {
@@ -894,7 +896,7 @@ void BoltEngine::cleanUpHuck() {
 	_xp->updateDisplay();
 }
 
-int16 BoltEngine::huckGame(int16 prevBooth) {
+int16 CarnivalEngine::huckGame(int16 prevBooth) {
 	if (!openBOLTLib(&_huckBoltLib, &_huckBoltCallbacks, assetPath("huck.blt")))
 		return _huckReturnBooth;
 
@@ -911,7 +913,7 @@ int16 BoltEngine::huckGame(int16 prevBooth) {
 	return _huckReturnBooth;
 }
 
-void BoltEngine::swapHuckWordArray() {
+void CarnivalEngine::swapHuckWordArray() {
 	byte *ptr = _boltCurrentMemberEntry->dataPtr;
 	if (!ptr)
 		return;
@@ -921,7 +923,7 @@ void BoltEngine::swapHuckWordArray() {
 		WRITE_UINT16(ptr, READ_BE_UINT16(ptr));
 }
 
-void BoltEngine::swapHuckWords() {
+void CarnivalEngine::swapHuckWords() {
 	byte *ptr = _boltCurrentMemberEntry->dataPtr;
 	if (!ptr)
 		return;
@@ -930,4 +932,6 @@ void BoltEngine::swapHuckWords() {
 	WRITE_UINT16(ptr + 2, READ_BE_UINT16(ptr + 2));
 }
 
+} // End of namespace Carnival
+
 } // End of namespace Bolt
diff --git a/engines/bolt/booths/scooby.cpp b/engines/bolt/carnival/booths/scooby.cpp
similarity index 96%
rename from engines/bolt/booths/scooby.cpp
rename to engines/bolt/carnival/booths/scooby.cpp
index 262754d2a89..8dd1d0c5071 100644
--- a/engines/bolt/booths/scooby.cpp
+++ b/engines/bolt/carnival/booths/scooby.cpp
@@ -21,11 +21,13 @@
 
 #include "common/memstream.h"
 
-#include "bolt/bolt.h"
+#include "bolt/carnival/carnival.h"
 
 namespace Bolt {
 
-bool BoltEngine::loadScoobyBaseAssets() {
+namespace Carnival {
+
+bool CarnivalEngine::loadScoobyBaseAssets() {
 	if (!getBOLTGroup(_scoobyBoltLib, 0, 1))
 		return false;
 
@@ -33,11 +35,11 @@ bool BoltEngine::loadScoobyBaseAssets() {
 	return true;
 }
 
-void BoltEngine::cleanUpScoobyBaseAssets() {
+void CarnivalEngine::cleanUpScoobyBaseAssets() {
 	freeBOLTGroup(_scoobyBoltLib, 0, 1);
 }
 
-void BoltEngine::displayPicClipHack(byte *pic, int16 offsetX, int16 offsetY, int16 *clipRect, int16 displayMode) {
+void CarnivalEngine::displayPicClipHack(byte *pic, int16 offsetX, int16 offsetY, int16 *clipRect, int16 displayMode) {
 	int16 clipW = clipRect[2];
 	int16 clipH = clipRect[3];
 
@@ -64,7 +66,7 @@ void BoltEngine::displayPicClipHack(byte *pic, int16 offsetX, int16 offsetY, int
 					displayMode);
 }
 
-void BoltEngine::drawMovingWalls(int16 cellIdx, int16 direction, int16 picFrame, int16 bgFrame) {
+void CarnivalEngine::drawMovingWalls(int16 cellIdx, int16 direction, int16 picFrame, int16 bgFrame) {
 	// Clamp frames to valid range
 	if (picFrame > 5)
 		picFrame = 5;
@@ -156,7 +158,7 @@ void BoltEngine::drawMovingWalls(int16 cellIdx, int16 direction, int16 picFrame,
 	}
 }
 
-void BoltEngine::drawAllMovingWalls() {
+void CarnivalEngine::drawAllMovingWalls() {
 	ScoobyState *state = &_scoobyGameState;
 
 	// Phase 1: Draw open passages between adjacent cells (both sides state 2)
@@ -235,7 +237,7 @@ void BoltEngine::drawAllMovingWalls() {
 	}
 }
 
-void BoltEngine::animateTransition(int16 memberIdx) {
+void CarnivalEngine::animateTransition(int16 memberIdx) {
 	_scoobyGameState.spriteFrameCount = 1;
 
 	_scoobyGameState.frameData[0] = memberAddr(_scoobyBoltLib, memberIdx - 1);
@@ -246,7 +248,7 @@ void BoltEngine::animateTransition(int16 memberIdx) {
 	_scoobyGameState.velocityY = 0;
 }
 
-void BoltEngine::clearPictMSb(byte *pic) {
+void CarnivalEngine::clearPictMSb(byte *pic) {
 	// Check if bit 8 of flags word at pic+0 is set (byte at pic+1, bit 0)
 	if (READ_UINT16(pic) & 0x100)
 		return;
@@ -259,7 +261,7 @@ void BoltEngine::clearPictMSb(byte *pic) {
 	}
 }
 
-void BoltEngine::initScoobyLevelGraphics() {
+void CarnivalEngine::initScoobyLevelGraphics() {
 	// Get palette from level data
 	byte *palette = memberAddr(_scoobyBoltLib, READ_UINT16(_scoobyLevelData));
 
@@ -326,7 +328,7 @@ void BoltEngine::initScoobyLevelGraphics() {
 	_xp->updateDisplay();
 }
 
-bool BoltEngine::initScoobyLevelAssets() {
+bool CarnivalEngine::initScoobyLevelAssets() {
 	uint32 maxPicSize = 0;
 
 	// Compute graphics group index from level number
@@ -430,12 +432,12 @@ bool BoltEngine::initScoobyLevelAssets() {
 	return true;
 }
 
-void BoltEngine::cleanUpScoobyLevelGraphics() {
+void CarnivalEngine::cleanUpScoobyLevelGraphics() {
 	_xp->stopCycle();
 	freeBOLTGroup(_scoobyBoltLib, _scoobySelectedGraphicsGroup, 1);
 }
 
-void BoltEngine::setScoobySpriteDirection(int16 startMember) {
+void CarnivalEngine::setScoobySpriteDirection(int16 startMember) {
 	_scoobyGameState.spriteFrameCount = 6;
 
 	int16 memberIdx = startMember;
@@ -446,7 +448,7 @@ void BoltEngine::setScoobySpriteDirection(int16 startMember) {
 	setSSpriteFrames(&_scoobySprite, _scoobyGameState.spriteFrameCount, _scoobyGameState.frameData, 1);
 }
 
-void BoltEngine::playSoundMapScooby(int16 memberIdx) {
+void CarnivalEngine::playSoundMapScooby(int16 memberIdx) {
 	byte *soundData = getBOLTMember(_scoobyBoltLib, memberIdx);
 	uint32 soundSize = memberSize(_scoobyBoltLib, memberIdx);
 
@@ -455,7 +457,7 @@ void BoltEngine::playSoundMapScooby(int16 memberIdx) {
 	}
 }
 
-void BoltEngine::playWallSound() {
+void CarnivalEngine::playWallSound() {
 	if (_scoobySoundPlaying == 0)
 		return;
 
@@ -500,7 +502,7 @@ void BoltEngine::playWallSound() {
 	playSoundMapScooby(soundMember);
 }
 
-void BoltEngine::animateWalls() {
+void CarnivalEngine::animateWalls() {
 	// Phase 1: Animate current cell's walls
 	int16 curCell = _scoobyGameState.scoobyCell;
 
@@ -645,7 +647,7 @@ void BoltEngine::animateWalls() {
 	}
 }
 
-void BoltEngine::decideDirection() {
+void CarnivalEngine::decideDirection() {
 	int16 cell = _scoobyGameState.scoobyCell;
 	int16 targetDir = _scoobyGameState.transitionTarget;
 
@@ -752,7 +754,7 @@ void BoltEngine::decideDirection() {
 	}
 }
 
-void BoltEngine::updateScoobySound() {
+void CarnivalEngine::updateScoobySound() {
 	switch (_scoobySoundMode) {
 	case 0:
 		if (_scoobySoundPlaying == 0) {
@@ -771,14 +773,14 @@ void BoltEngine::updateScoobySound() {
 	}
 }
 
-void BoltEngine::setScoobySound(int16 mode) {
+void CarnivalEngine::setScoobySound(int16 mode) {
 	if (mode != _scoobySoundMode) {
 		_scoobySoundMode = mode;
 		updateScoobySound();
 	}
 }
 
-void BoltEngine::updateScoobyLocation() {
+void CarnivalEngine::updateScoobyLocation() {
 	Common::Point loc;
 	getSSpriteLoc(&_scoobySprite, &loc);
 	_scoobyGameState.scoobyX = loc.x;
@@ -878,7 +880,7 @@ void BoltEngine::updateScoobyLocation() {
 		_scoobyGameState.slotIndex[slotIdx] = 0;
 }
 
-void BoltEngine::updateScoobyWalls() {
+void CarnivalEngine::updateScoobyWalls() {
 	if (_scoobyWallAnimating != 0) {
 		_scoobyWallAnimStep++;
 		if (_scoobyWallAnimStep > 5) {
@@ -1021,7 +1023,7 @@ void BoltEngine::updateScoobyWalls() {
 	}
 }
 
-void BoltEngine::updateScoobyDirection(int16 inputDir) {
+void CarnivalEngine::updateScoobyDirection(int16 inputDir) {
 	int16 cell = _scoobyGameState.scoobyCell;
 
 	// If at cell center, wall passable, and no transition active then accept immediately
@@ -1113,7 +1115,7 @@ void BoltEngine::updateScoobyDirection(int16 inputDir) {
 	}
 }
 
-void BoltEngine::updateScoobyTransition() {
+void CarnivalEngine::updateScoobyTransition() {
 	if (_scoobyTransitioning != 0) {
 		// Active transition in progress
 		switch (_scoobyTransitionTarget) {
@@ -1337,7 +1339,7 @@ epilogue:
 	setScoobySound(soundMode);
 }
 
-bool BoltEngine::initScoobyLevel() {
+bool CarnivalEngine::initScoobyLevel() {
 	if (!initScoobyLevelAssets())
 		return false;
 
@@ -1389,7 +1391,7 @@ skipLevelSet:
 	return true;
 }
 
-bool BoltEngine::resumeScoobyLevel() {
+bool CarnivalEngine::resumeScoobyLevel() {
 	if (!initScoobyLevelAssets())
 		return false;
 
@@ -1412,7 +1414,7 @@ bool BoltEngine::resumeScoobyLevel() {
 	return true;
 }
 
-bool BoltEngine::initScooby() {
+bool CarnivalEngine::initScooby() {
 	_xp->randomize();
 
 	_scoobyMoveRequested = 0;
@@ -1508,7 +1510,7 @@ bool BoltEngine::initScooby() {
 	return true;
 }
 
-void BoltEngine::cleanUpScooby() {
+void CarnivalEngine::cleanUpScooby() {
 	vSave(&_scoobyGlobalSaveData, 0x3C, "ScoobyGlobal");
 
 	// Serialize the game state into a flat 0x11A-byte BE buffer
@@ -1567,7 +1569,7 @@ void BoltEngine::cleanUpScooby() {
 	_xp->updateDisplay();
 }
 
-int16 BoltEngine::helpScooby() {
+int16 CarnivalEngine::helpScooby() {
 	int16 selection = 2;
 	int16 isPlaying = 0;
 	int16 soundParam = 0;
@@ -1829,7 +1831,7 @@ int16 BoltEngine::helpScooby() {
 	return selection;
 }
 
-void BoltEngine::hiliteScoobyHelpObject(byte *entry, int16 highlight) {
+void CarnivalEngine::hiliteScoobyHelpObject(byte *entry, int16 highlight) {
 	if (!entry)
 		return;
 
@@ -1844,7 +1846,7 @@ void BoltEngine::hiliteScoobyHelpObject(byte *entry, int16 highlight) {
 	}
 }
 
-int16 BoltEngine::xpDirToBOLTDir(uint32 xpDir) {
+int16 CarnivalEngine::xpDirToBOLTDir(uint32 xpDir) {
 	// xpDir packs (X << 16 | Y & 0xFFFF) where X,Y are -1/0/1
 	// Returns BOLT direction:
 	//   0=up,   1=up-right,  2=right, 3=down-right,
@@ -1872,7 +1874,7 @@ int16 BoltEngine::xpDirToBOLTDir(uint32 xpDir) {
 	return 0;
 }
 
-int16 BoltEngine::playScooby() {
+int16 CarnivalEngine::playScooby() {
 	int16 inputDir = -1;
 
 	// Check if help screen should show on startup
@@ -1930,7 +1932,7 @@ int16 BoltEngine::playScooby() {
 	return 0;
 }
 
-int16 BoltEngine::scoobyGame(int16 prevBooth) {
+int16 CarnivalEngine::scoobyGame(int16 prevBooth) {
 	int16 result = 5;
 
 	if (!openBOLTLib(&_scoobyBoltLib, &_scoobyBoltCallbacks, assetPath("scooby.blt")))
@@ -1949,7 +1951,7 @@ int16 BoltEngine::scoobyGame(int16 prevBooth) {
 	return result;
 }
 
-void BoltEngine::swapScoobyHelpEntry() {
+void CarnivalEngine::swapScoobyHelpEntry() {
 	byte *data = _boltCurrentMemberEntry->dataPtr;
 	if (data == nullptr)
 		return;
@@ -1962,7 +1964,7 @@ void BoltEngine::swapScoobyHelpEntry() {
 	unpackColors(13, data + 0x0E);
 }
 
-void BoltEngine::swapScoobyWordArray() {
+void CarnivalEngine::swapScoobyWordArray() {
 	byte *data = _boltCurrentMemberEntry->dataPtr;
 	if (data == nullptr)
 		return;
@@ -1975,4 +1977,6 @@ void BoltEngine::swapScoobyWordArray() {
 	}
 }
 
+} // End of namespace Carnival
+
 } // End of namespace Bolt
diff --git a/engines/bolt/booths/topcat.cpp b/engines/bolt/carnival/booths/topcat.cpp
similarity index 96%
rename from engines/bolt/booths/topcat.cpp
rename to engines/bolt/carnival/booths/topcat.cpp
index 5690af46d6d..398194ce931 100644
--- a/engines/bolt/booths/topcat.cpp
+++ b/engines/bolt/carnival/booths/topcat.cpp
@@ -19,11 +19,13 @@
  *
  */
 
-#include "bolt/bolt.h"
+#include "bolt/carnival/carnival.h"
 
 namespace Bolt {
 
-int16 Bolt::BoltEngine::topCatGame(int16 prevBooth) {
+namespace Carnival {
+
+int16 CarnivalEngine::topCatGame(int16 prevBooth) {
 	int16 savedTimer = _xp->setInactivityTimer(30);
 
 	if (!initTopCat()) {
@@ -38,7 +40,7 @@ int16 Bolt::BoltEngine::topCatGame(int16 prevBooth) {
 	return result;
 }
 
-bool BoltEngine::initTopCat() {
+bool CarnivalEngine::initTopCat() {
 	_topCatRtfHandle = openRTF(assetPath(_displayMode != 0 ? "topcatcr.av" : "topcatog.av"));
 	if (!_topCatRtfHandle)
 		return false;
@@ -210,7 +212,7 @@ bool BoltEngine::initTopCat() {
 	return true;
 }
 
-void BoltEngine::cleanUpTopCat() {
+void CarnivalEngine::cleanUpTopCat() {
 	_xp->stopSound();
 	_xp->stopCycle();
 
@@ -281,7 +283,7 @@ void BoltEngine::cleanUpTopCat() {
 #endif
 }
 
-int16 BoltEngine::playTopCat() {
+int16 CarnivalEngine::playTopCat() {
 	int16 result = 0;
 	int16 exitFlag = 0;
 
@@ -521,7 +523,7 @@ int16 BoltEngine::playTopCat() {
 	}
 }
 
-int16 BoltEngine::handleActionButton(int16 *result) {
+int16 CarnivalEngine::handleActionButton(int16 *result) {
 	int16 prevState = _topCatCurrentAnimType;
 
 	switch (_topCatCurrentAnimType) {
@@ -644,7 +646,7 @@ int16 BoltEngine::handleActionButton(int16 *result) {
 	return 0;
 }
 
-void BoltEngine::queueAnim(int16 animType, int16 param) {
+void CarnivalEngine::queueAnim(int16 animType, int16 param) {
 	int16 slot = _topCatAnimQueueSize;
 	_topCatAnimQueueSize++;
 
@@ -690,7 +692,7 @@ void BoltEngine::queueAnim(int16 animType, int16 param) {
 	}
 }
 
-bool BoltEngine::maintainAnim(int16 soundEvent) {
+bool CarnivalEngine::maintainAnim(int16 soundEvent) {
 	int16 animDone = 0;
 
 	switch (_topCatAnimStateMachineStep) {
@@ -790,7 +792,7 @@ bool BoltEngine::maintainAnim(int16 soundEvent) {
 	return true;
 }
 
-void BoltEngine::maintainIdleSound(int16 decrement) {
+void CarnivalEngine::maintainIdleSound(int16 decrement) {
 	byte *soundData = _topCatSoundInfo.data;
 	int16 chunkSize = (int16)(_topCatSoundInfo.size / _topCatMaxBackgroundAnimFrames);
 
@@ -810,7 +812,7 @@ void BoltEngine::maintainIdleSound(int16 decrement) {
 	}
 }
 
-bool BoltEngine::startNextAnim(int16 *playAnswerAnim) {
+bool CarnivalEngine::startNextAnim(int16 *playAnswerAnim) {
 	TopCatAnim *entry = &_topCatAnimQueue[0];
 	bool startResult = false;
 
@@ -876,7 +878,7 @@ bool BoltEngine::startNextAnim(int16 *playAnswerAnim) {
 	return true;
 }
 
-void BoltEngine::setAnimType(int16 newType) {
+void CarnivalEngine::setAnimType(int16 newType) {
 	if (_topCatCurrentAnimType == newType)
 		return;
 
@@ -952,7 +954,7 @@ void BoltEngine::setAnimType(int16 newType) {
 	_topCatAnimStateMachineStep = 0;
 }
 
-void BoltEngine::highlightObject(byte *entry, int16 highlight) {
+void CarnivalEngine::highlightObject(byte *entry, int16 highlight) {
 	if (!entry)
 		return;
 
@@ -967,7 +969,7 @@ void BoltEngine::highlightObject(byte *entry, int16 highlight) {
 	_system->updateScreen();
 }
 
-bool BoltEngine::setupNextQuestion() {
+bool CarnivalEngine::setupNextQuestion() {
 	_xp->stopCycle();
 
 	int16 attempts = 0;
@@ -1035,7 +1037,7 @@ bool BoltEngine::setupNextQuestion() {
 	return true;
 }
 
-void BoltEngine::adjustColors(byte *pic, int8 shift) {
+void CarnivalEngine::adjustColors(byte *pic, int8 shift) {
 	int16 count = READ_UINT16(pic + 0x0C);
 	byte *data = getResolvedPtr(pic, 0x12);
 
@@ -1056,7 +1058,7 @@ void BoltEngine::adjustColors(byte *pic, int8 shift) {
 	}
 }
 
-void BoltEngine::shuffleTopCatQuestions() {
+void CarnivalEngine::shuffleTopCatQuestions() {
 	int16 answerOff = 0;
 
 	for (int16 i = 0; i < 60; i++) {
@@ -1078,7 +1080,7 @@ void BoltEngine::shuffleTopCatQuestions() {
 	}
 }
 
-void BoltEngine::shuffleTopCatPermutations() {
+void CarnivalEngine::shuffleTopCatPermutations() {
 	// Shuffle first permutation table...
 	for (int16 i = 0; i < 3; i++) {
 		int16 randIdx = _xp->getRandom(3);
@@ -1098,12 +1100,12 @@ void BoltEngine::shuffleTopCatPermutations() {
 	}
 }
 
-void BoltEngine::getTopCatSoundInfo(BOLTLib *lib, int16 memberId, SoundInfo *soundInfo) {
+void CarnivalEngine::getTopCatSoundInfo(BOLTLib *lib, int16 memberId, SoundInfo *soundInfo) {
 	soundInfo->data = memberAddr(lib, memberId);
 	soundInfo->size = memberSize(lib, memberId);
 }
 
-void BoltEngine::setScoreLights(int16 litMask) {
+void CarnivalEngine::setScoreLights(int16 litMask) {
 	int16 lightIdx = 0;
 	int16 tableOff = 0;
 
@@ -1130,7 +1132,7 @@ void BoltEngine::setScoreLights(int16 litMask) {
 	_system->updateScreen();
 }
 
-void BoltEngine::swapTopCatHelpEntry() {
+void CarnivalEngine::swapTopCatHelpEntry() {
 	byte *data = _boltCurrentMemberEntry->dataPtr;
 	uint32 decompSize = _boltCurrentMemberEntry->decompSize;
 	int16 offset = 0;
@@ -1149,4 +1151,6 @@ void BoltEngine::swapTopCatHelpEntry() {
 	}
 }
 
+} // End of namespace Carnival
+
 } // End of namespace Bolt
diff --git a/engines/bolt/booths/yogi.cpp b/engines/bolt/carnival/booths/yogi.cpp
similarity index 93%
rename from engines/bolt/booths/yogi.cpp
rename to engines/bolt/carnival/booths/yogi.cpp
index d77c33aa42f..ebbcbae0006 100644
--- a/engines/bolt/booths/yogi.cpp
+++ b/engines/bolt/carnival/booths/yogi.cpp
@@ -21,11 +21,13 @@
 
 #include "common/memstream.h"
 
-#include "bolt/bolt.h"
+#include "bolt/carnival/carnival.h"
 
 namespace Bolt {
 
-void BoltEngine::playSoundMapYogi(int16 memberId) {
+namespace Carnival {
+
+void CarnivalEngine::playSoundMapYogi(int16 memberId) {
 	byte *soundData = getBOLTMember(_yogiBoltLib, memberId);
 	int32 soundSize = memberSize(_yogiBoltLib, memberId);
 	if (soundData) {
@@ -35,7 +37,7 @@ void BoltEngine::playSoundMapYogi(int16 memberId) {
 	}
 }
 
-void BoltEngine::waitSoundMapYogi() {
+void CarnivalEngine::waitSoundMapYogi() {
 	if (!_yogiSoundPlaying)
 		return;
 
@@ -46,28 +48,28 @@ void BoltEngine::waitSoundMapYogi() {
 	_yogiSoundPlaying--;
 }
 
-void BoltEngine::stopSoundYogi() {
+void CarnivalEngine::stopSoundYogi() {
 	if (_yogiSoundActive) {
 		_xp->stopSound();
 		_yogiSoundActive = 0;
 	}
 }
 
-void BoltEngine::setYogiColors(int16 which) {
+void CarnivalEngine::setYogiColors(int16 which) {
 	if (which == 0)
 		_xp->setPalette(_yogiPalRange[1] - _yogiPalRange[0] + 1, _yogiPalRange[0], _yogiPalHighlight0);
 	else if (which == 1)
 		_xp->setPalette(_yogiPalRange[5] - _yogiPalRange[4] + 1, _yogiPalRange[4], _yogiPalHighlight1);
 }
 
-void BoltEngine::restoreYogiColors(int16 which) {
+void CarnivalEngine::restoreYogiColors(int16 which) {
 	if (which == 0)
 		_xp->setPalette(_yogiPalRange[1] - _yogiPalRange[0] + 1, _yogiPalRange[0], _yogiPalSave0);
 	else if (which == 1)
 		_xp->setPalette(_yogiPalRange[5] - _yogiPalRange[4] + 1, _yogiPalRange[4], _yogiPalSave1);
 }
 
-void BoltEngine::drawBasket(int16 slot, byte *basketSprite) {
+void CarnivalEngine::drawBasket(int16 slot, byte *basketSprite) {
 	int16 sprStride = READ_UINT16(basketSprite + 0x0A);
 	int16 sprHeight = READ_UINT16(basketSprite + 0x0C);
 
@@ -115,7 +117,7 @@ void BoltEngine::drawBasket(int16 slot, byte *basketSprite) {
 	_xp->displayPic(&_yogiScratchBuf, slotX, slotY, stFront);
 }
 
-void BoltEngine::drawAllBaskets() {
+void CarnivalEngine::drawAllBaskets() {
 	_xp->fillDisplay(0, stFront);
 
 	int16 basketCount = READ_UINT16(_yogiBasketPic);
@@ -133,7 +135,7 @@ void BoltEngine::drawAllBaskets() {
 	}
 }
 
-void BoltEngine::handleYogiMatch() {
+void CarnivalEngine::handleYogiMatch() {
 	playSoundMapYogi((int16)READ_UINT16(_yogiBasketPic + 0x9E));
 
 	if (_yogiState.basketCount == 2) {
@@ -155,7 +157,7 @@ void BoltEngine::handleYogiMatch() {
 	waitSoundMapYogi();
 }
 
-bool BoltEngine::loadYogiBgPic() {
+bool CarnivalEngine::loadYogiBgPic() {
 	int16 groupId;
 
 	if (_displayMode == 0) {
@@ -173,11 +175,11 @@ bool BoltEngine::loadYogiBgPic() {
 	return true;
 }
 
-void BoltEngine::unloadYogiBgPic() {
+void CarnivalEngine::unloadYogiBgPic() {
 	freeBOLTGroup(_yogiBoltLib, 0x100, 1);
 }
 
-void BoltEngine::drawYogiLevel() {
+void CarnivalEngine::drawYogiLevel() {
 	byte *palSprite = memberAddr(_yogiBoltLib, READ_UINT16(_yogiBasketPic + 0x72));
 
 	_yogiSoundActive = 0;
@@ -224,7 +226,7 @@ void BoltEngine::drawYogiLevel() {
 	_xp->getPalette(_yogiPalRange[6], _yogiPalRange[7] - _yogiPalRange[6] + 1, _yogiPalHighlight1);
 }
 
-bool BoltEngine::loadYogiLevel() {
+bool CarnivalEngine::loadYogiLevel() {
 	int32 maxSize = 0;
 
 	_yogiLevelGroupId = (_yogiState.levelNumber - 1) * 0x100 + 0x300;
@@ -260,14 +262,14 @@ bool BoltEngine::loadYogiLevel() {
 	return true;
 }
 
-void BoltEngine::unloadYogiResources() {
+void CarnivalEngine::unloadYogiResources() {
 	_xp->hideCursor();
 	int16 basketGroupId = (_yogiLevelId << 8) + 0xD00;
 	freeBOLTGroup(_yogiBoltLib, basketGroupId, 1);
 	freeBOLTGroup(_yogiBoltLib, _yogiLevelGroupId, 1);
 }
 
-bool BoltEngine::initYogiLevel() {
+bool CarnivalEngine::initYogiLevel() {
 	_yogiState.currentSlot++;
 	if (_yogiState.currentSlot >= 10)
 		_yogiState.currentSlot = 0;
@@ -319,7 +321,7 @@ bool BoltEngine::initYogiLevel() {
 	return true;
 }
 
-bool BoltEngine::resumeYogiLevel() {
+bool CarnivalEngine::resumeYogiLevel() {
 	_yogiLevelId = _yogiState.slotIndex[_yogiState.currentSlot];
 
 	if (!loadYogiLevel())
@@ -329,7 +331,7 @@ bool BoltEngine::resumeYogiLevel() {
 	return true;
 }
 
-bool BoltEngine::initYogi() {
+bool CarnivalEngine::initYogi() {
 	_yogiSoundPlaying = 0;
 
 	if (!loadYogiBgPic())
@@ -408,7 +410,7 @@ bool BoltEngine::initYogi() {
 		return resumeYogiLevel();
 }
 
-void BoltEngine::yogiToggleBlinking(int16 which, int16 *state) {
+void CarnivalEngine::yogiToggleBlinking(int16 which, int16 *state) {
 	*state = (*state == 0) ? 1 : 0;
 	if (*state)
 		setYogiColors(which);
@@ -416,7 +418,7 @@ void BoltEngine::yogiToggleBlinking(int16 which, int16 *state) {
 		restoreYogiColors(which);
 }
 
-void BoltEngine::yogiUpdateHotSpots(int16 x, int16 y) {
+void CarnivalEngine::yogiUpdateHotSpots(int16 x, int16 y) {
 	Common::Rect helpRect(
 		READ_UINT16(_yogiBasketPic + 0x74), READ_UINT16(_yogiBasketPic + 0x78),
 		READ_UINT16(_yogiBasketPic + 0x76), READ_UINT16(_yogiBasketPic + 0x7A));
@@ -443,7 +445,7 @@ void BoltEngine::yogiUpdateHotSpots(int16 x, int16 y) {
 	}
 }
 
-int16 BoltEngine::findBasket(int16 x, int16 y) {
+int16 CarnivalEngine::findBasket(int16 x, int16 y) {
 	int16 basketCount = READ_UINT16(_yogiBasketPic);
 	for (int16 i = basketCount - 1; i >= 0; i--) {
 		if (_yogiState.basketState[i] != 0)
@@ -470,7 +472,7 @@ int16 BoltEngine::findBasket(int16 x, int16 y) {
 	return -1;
 }
 
-void BoltEngine::resolveYogiSelection() {
+void CarnivalEngine::resolveYogiSelection() {
 	if (_yogiState.sound1 == _yogiState.sound2) {
 		_yogiState.basketState[_yogiState.selected1Slot] = 1;
 		_yogiState.basketState[_yogiState.selected2Slot] = 1;
@@ -496,7 +498,7 @@ void BoltEngine::resolveYogiSelection() {
 	}
 }
 
-bool BoltEngine::handleBasketSelect(int16 x, int16 y) {
+bool CarnivalEngine::handleBasketSelect(int16 x, int16 y) {
 	int16 slot = findBasket(x, y);
 	if (slot == -1)
 		return false;
@@ -558,7 +560,7 @@ bool BoltEngine::handleBasketSelect(int16 x, int16 y) {
 	return true;
 }
 
-void BoltEngine::yogiHandleActionButton(int16 x, int16 y) {
+void CarnivalEngine::yogiHandleActionButton(int16 x, int16 y) {
 	bool didAction = false;
 	bool stoppedAnim = false;
 
@@ -617,7 +619,7 @@ void BoltEngine::yogiHandleActionButton(int16 x, int16 y) {
 	}
 }
 
-void BoltEngine::handleYogiEvent(int16 eventType, uint32 eventData) {
+void CarnivalEngine::handleYogiEvent(int16 eventType, uint32 eventData) {
 	switch (eventType) {
 	case etMouseDown:
 		yogiHandleActionButton(_yogiCursorX, _yogiCursorY);
@@ -688,7 +690,7 @@ void BoltEngine::handleYogiEvent(int16 eventType, uint32 eventData) {
 	}
 }
 
-void BoltEngine::playYogi() {
+void CarnivalEngine::playYogi() {
 	_yogiReturnBooth = 16;
 
 	while (!shouldQuit()) {
@@ -712,7 +714,7 @@ void BoltEngine::playYogi() {
 		_yogiReturnBooth = 0;
 }
 
-void BoltEngine::cleanUpYogi() {
+void CarnivalEngine::cleanUpYogi() {
 	if (_yogiScratchBuf.pixelData) {
 		_xp->freeMem(_yogiScratchBuf.pixelData);
 		_yogiScratchBuf.pixelData = nullptr;
@@ -764,7 +766,7 @@ void BoltEngine::cleanUpYogi() {
 	_xp->updateDisplay();
 }
 
-int16 BoltEngine::yogiGame(int16 prevBooth) {
+int16 CarnivalEngine::yogiGame(int16 prevBooth) {
 	if (!openBOLTLib(&_yogiBoltLib, &_yogiBoltCallbacks, assetPath("yogi.blt")))
 		return _yogiReturnBooth;
 
@@ -780,7 +782,7 @@ int16 BoltEngine::yogiGame(int16 prevBooth) {
 	return _yogiReturnBooth;
 }
 
-void BoltEngine::swapYogiAllWords() {
+void CarnivalEngine::swapYogiAllWords() {
 	byte *ptr = _boltCurrentMemberEntry->dataPtr;
 	if (!ptr)
 		return;
@@ -792,7 +794,7 @@ void BoltEngine::swapYogiAllWords() {
 	}
 }
 
-void BoltEngine::swapYogiFirstWord() {
+void CarnivalEngine::swapYogiFirstWord() {
 	byte *ptr = (byte *)_boltCurrentMemberEntry->dataPtr;
 	if (!ptr)
 		return;
@@ -800,4 +802,6 @@ void BoltEngine::swapYogiFirstWord() {
 	WRITE_UINT16(ptr, READ_BE_UINT16(ptr));
 }
 
+} // End of namespace Carnival
+
 } // End of namespace Bolt
diff --git a/engines/bolt/carnival/carnival.cpp b/engines/bolt/carnival/carnival.cpp
new file mode 100644
index 00000000000..e88a43d4947
--- /dev/null
+++ b/engines/bolt/carnival/carnival.cpp
@@ -0,0 +1,171 @@
+/* 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 "bolt/carnival/carnival.h"
+
+namespace Bolt {
+
+namespace Carnival {
+	
+CarnivalEngine::CarnivalEngine(OSystem *syst, const ADGameDescription *gameDesc)
+	: BoltEngine(syst, gameDesc) {
+	initCallbacks();
+}
+
+void CarnivalEngine::boltMain() {
+	byte *testAlloc;
+	BarkerTable *barkerTable;
+	byte *boothSprite;
+
+	testAlloc = (byte *)_xp->allocMem(0x100000);
+	if (!testAlloc)
+		return;
+
+	_xp->freeMem(testAlloc);
+
+	_xp->randomize();
+
+	if (allocResourceIndex()) {
+		_boothsBoltLib = nullptr;
+
+		if (openBOLTLib(&_boothsBoltLib, &_boothsBoltCallbacks, assetPath("booths.blt"))) {
+			int16 chosenSpecId = _extendedViewport ? 0 : 1;
+
+			if (_xp->setDisplaySpec(&_displayMode, &_displaySpecs[chosenSpecId])) {
+				assert(_displayMode >= 0);
+				_displayWidth = _displaySpecs[_displayMode].width;
+				_displayHeight = _displaySpecs[_displayMode].height;
+
+				// Center within 384x240 virtual coordinate space...
+				_displayX = (EXTENDED_SCREEN_WIDTH - _displayWidth) / 2;
+				_displayY = (EXTENDED_SCREEN_HEIGHT - _displayHeight) / 2;
+
+				_xp->setCoordSpec(_displayX, _displayY, _displayWidth, _displayHeight);
+				
+				if (_displayMode == 0) {
+					_rtfHandle = openRTF(assetPath("cc_og.av"));
+				} else {
+					_rtfHandle = openRTF(assetPath("cc_cr.av"));
+				}
+
+				if (_rtfHandle) {
+					int16 boothGroupBase = _isDemo ? 0x0E00 : 0x1700;
+
+					if (!_isDemo) {
+						playAV(_rtfHandle, 0, _displayWidth, _displayHeight, _displayX, _displayY);
+					}
+
+					boothSprite = getBOLTMember(_boothsBoltLib, (_displayMode != 0) ? (boothGroupBase + 1) : (boothGroupBase + 2));
+
+					_xp->setTransparency(false);
+
+					displayColors(getBOLTMember(_boothsBoltLib, boothGroupBase), stFront, 0);
+					displayPic(boothSprite, _displayX, _displayY, stFront);
+
+					_xp->updateDisplay();
+
+					displayColors(getBOLTMember(_boothsBoltLib, boothGroupBase), stBack, 0);
+					displayPic(boothSprite, _displayX, _displayY, stBack);
+
+					playAV(_rtfHandle, _isDemo ? 0 : 2, _displayWidth, _displayHeight, _displayX, _displayY);
+
+					freeBOLTGroup(_boothsBoltLib, boothGroupBase, 1);
+
+					if (getBOLTGroup(_boothsBoltLib, 0, 1)) {
+						setCursorPict(memberAddr(_boothsBoltLib, 0));
+
+						if (initVRam(1500)) {
+							barkerTable = createBarker(2, _isDemo ? 19 : 17);
+							if (barkerTable) {
+								// Register booth handlers...
+								registerSideShow(barkerTable, &CarnivalEngine::hucksBooth, 3);
+								registerSideShow(barkerTable, &CarnivalEngine::fredsBooth, 4);
+								registerSideShow(barkerTable, &CarnivalEngine::scoobysBooth, 5);
+								registerSideShow(barkerTable, &CarnivalEngine::yogisBooth, 6);
+								registerSideShow(barkerTable, &CarnivalEngine::georgesBooth, 7);
+								registerSideShow(barkerTable, &CarnivalEngine::topCatsBooth, 8);
+								registerSideShow(barkerTable, &CarnivalEngine::mainEntrance, 9);
+								registerSideShow(barkerTable, &CarnivalEngine::huckGame, 10);
+								registerSideShow(barkerTable, &CarnivalEngine::fredGame, 11);
+								registerSideShow(barkerTable, &CarnivalEngine::scoobyGame, 12);
+								registerSideShow(barkerTable, &CarnivalEngine::yogiGame, 13);
+								registerSideShow(barkerTable, &CarnivalEngine::georgeGame, 14);
+								registerSideShow(barkerTable, &CarnivalEngine::topCatGame, 15);
+								registerSideShow(barkerTable, &CarnivalEngine::winALetter, 16);
+
+								if (_isDemo) {
+									registerSideShow(barkerTable, &CarnivalEngine::displayDemoPict, 17);
+									registerSideShow(barkerTable, &CarnivalEngine::endDemo, 18);
+								}
+
+								_lettersWon = 0;
+								_xp->setScreenSaverTimer(1800);
+
+								// The barker function runs the main loop, starting at mainEntrance()...
+								if (!checkError())
+									barker(barkerTable, 9);
+
+								freeBarker(barkerTable);
+							}
+						}
+					}
+				}
+			}
+		}
+	}
+
+	freeBOLTGroup(_boothsBoltLib, 0, 1);
+
+	if (_boothsBoltLib != 0)
+		closeBOLTLib(&_boothsBoltLib);
+
+	if (_rtfHandle != nullptr)
+		closeRTF(_rtfHandle);
+
+	freeVRam();
+	freeResourceIndex();
+}
+
+
+int16 CarnivalEngine::displayDemoPict(int16 prevBooth) {
+	uint32 eventData;
+
+	while (_xp->getEvent(etMouseDown, &eventData) == etMouseDown);
+
+	while (_xp->getEvent(etTimer, &eventData) != etTimer);
+
+	_xp->stopCycle();
+	_xp->setTransparency(false);
+
+	int16 memberId = (_displayMode != 0) ? 0x1002 : 0x1001;
+	displayPic(getBOLTMember(_boothsBoltLib, memberId), _displayX, _displayY, stFront);
+	displayColors(getBOLTMember(_boothsBoltLib, 0x1000), stFront, 0);
+
+	_xp->updateDisplay();
+
+	while (_xp->getEvent(etMouseDown, &eventData) != etMouseDown);
+
+	return prevBooth;
+}
+
+} // End of namespace Carnival
+
+} // End of namespace Bolt
diff --git a/engines/bolt/carnival/carnival.h b/engines/bolt/carnival/carnival.h
new file mode 100644
index 00000000000..8bf4c0124ef
--- /dev/null
+++ b/engines/bolt/carnival/carnival.h
@@ -0,0 +1,857 @@
+/* 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 CARNIVAL_CARNIVAL_H
+#define CARNIVAL_CARNIVAL_H
+
+#include "bolt/bolt.h"
+
+namespace Bolt {
+
+namespace Carnival {
+
+class CarnivalEngine;
+typedef int16 (CarnivalEngine::*SideShowHandler)(int16 prevBooth);
+
+struct BarkerTable {
+	SideShowHandler *handlers;
+	int16 count;
+	int16 minIndex;
+	int16 maxIndex;
+};
+
+typedef struct FredEntityState {
+	uint16 flags;
+	int16 frameCountdown;
+	int16 animMode;
+	int16 frameIndex;
+	byte *animTable;
+	int16 direction;
+	int32 xPos;
+	int32 yPos;
+	int32 prevXPos;
+	int32 prevYPos;
+	int16 speed;
+	byte *pathTable;
+	int16 pathIndex;
+
+	FredEntityState() {
+		flags = 0;
+		frameCountdown = 0;
+		animMode = 0;
+		frameIndex = 0;
+		animTable = nullptr;
+		direction = 0;
+		xPos = 0;
+		yPos = 0;
+		prevXPos = 0;
+		prevYPos = 0;
+		speed = 0;
+		pathTable = nullptr;
+		pathIndex = 0;
+	}
+} FredEntityState;
+
+// HUCK GAME
+
+typedef struct HuckState {
+	int16 levelNumber;
+	int16 slotIndex[10];
+	int16 levelComplete;
+	int16 drawTable1[24];
+	int16 drawTable2[24];
+	int16 giftCount;
+	int16 hasCycle;
+	int16 selectionPending;
+	int16 selected1Slot;
+	int16 selected2Slot;
+	int16 selected1SpriteId;
+	int16 selected2SpriteId;
+
+	HuckState() {
+		levelNumber = 0;
+		memset(slotIndex, 0, sizeof(slotIndex));
+		levelComplete = 0;
+		memset(drawTable1, 0, sizeof(drawTable1));
+		memset(drawTable2, 0, sizeof(drawTable2));
+		giftCount = 0;
+		hasCycle = 0;
+		selectionPending = 0;
+		selected1Slot = 0;
+		selected2Slot = 0;
+		selected1SpriteId = 0;
+		selected2SpriteId = 0;
+	}
+} HuckState;
+
+// GEORGE GAME
+
+typedef struct GeorgeEntityState {
+	uint16 flags;
+	int16 frameCountdown;
+	int16 animMode;
+	int16 variant;
+	int16 frameIndex;
+	byte *animTable;
+	int32 joyInput;
+	int32 x;
+	int32 y;
+	int32 prevX;
+	int32 prevY;
+	int32 velX;
+	int32 velY;
+	int32 accelX;
+	int32 accelY;
+	byte *pathTable;
+	int16 pathIndex;
+
+	GeorgeEntityState() {
+		flags = 0;
+		frameCountdown = 0;
+		animMode = 0;
+		variant = 0;
+		frameIndex = 0;
+		animTable = nullptr;
+		joyInput = 0;
+		x = 0;
+		y = 0;
+		prevX = 0;
+		prevY = 0;
+		velX = 0;
+		velY = 0;
+		accelX = 0;
+		accelY = 0;
+		pathTable = nullptr;
+		pathIndex = 0;
+	}
+} GeorgeEntityState;
+
+// SCOOBY GAME
+
+typedef struct ScoobyState {
+	int16 levelNumber;
+	int16 slotIndex[10];
+	int16 levelComplete;
+	int16 wallStates[25][4];
+	int16 scoobyCell;
+	int16 scoobySavedCell;
+	int16 leftNeighbor;
+	int16 rightNeighbor;
+	int16 upNeighbor;
+	int16 downNeighbor;
+	int16 activeLevel;
+	int16 scoobyX;
+	int16 scoobyY;
+	int16 velocityX;
+	int16 velocityY;
+	int16 targetVelocityX;
+	int16 targetVelocityY;
+	int16 transitionTarget;
+	int16 currentAnim;
+	int16 direction;
+	int16 spriteFrameCount;
+	byte *frameData[6];
+
+	ScoobyState() {
+		levelNumber = 0;
+
+		for (int i = 0; i < 10; i++)
+			slotIndex[i] = 0;
+
+		levelComplete = 0;
+
+		for (int i = 0; i < 25; i++)
+			for (int j = 0; j < 4; j++)
+				wallStates[i][j] = 0;
+
+		scoobyCell = 0;
+		scoobySavedCell = 0;
+		leftNeighbor = 0;
+		rightNeighbor = 0;
+		upNeighbor = 0;
+		downNeighbor = 0;
+		activeLevel = 0;
+		scoobyX = 0;
+		scoobyY = 0;
+		velocityX = 0;
+		velocityY = 0;
+		targetVelocityX = 0;
+		targetVelocityY = 0;
+		transitionTarget = 0;
+		currentAnim = 0;
+		direction = 0;
+		spriteFrameCount = 0;
+
+		for (int i = 0; i < 6; i++)
+			frameData[i] = nullptr;
+	}
+} ScoobyState;
+
+typedef struct ScoobyRect {
+	int16 left;
+	int16 right;
+	int16 top;
+	int16 bottom;
+
+	ScoobyRect() {
+		left = 0;
+		right = 0;
+		top = 0;
+		bottom = 0;
+	}
+} ScoobyRect;
+
+// TOPCAT GAME
+
+typedef struct TopCatAnim {
+	int16 animType;
+	int16 animIndex;
+	int16 transitionToNextQuestionFlag;
+	int16 *seqPtr;
+
+	TopCatAnim() {
+		animType = 0;
+		animIndex = 0;
+		transitionToNextQuestionFlag = 0;
+		seqPtr = nullptr;
+	}
+} TopCatAnim;
+
+// YOGI GAME
+
+typedef struct YogiState {
+	int16 levelNumber;
+	int16 currentSlot;
+	int16 levelIndex[10];
+	int16 slotIndex[10];
+	int16 levelComplete;
+	int16 basketSound[24];
+	int16 basketState[24];
+	int16 basketCount;
+	int16 matchCount;
+	int16 selectionPending;
+	int16 selected1Slot;
+	int16 selected2Slot;
+	int16 sound1;
+	int16 sound2;
+
+	YogiState() {
+		levelNumber = 0;
+		currentSlot = 0;
+		memset(levelIndex, 0, sizeof(levelIndex));
+		memset(slotIndex, 0, sizeof(slotIndex));
+		levelComplete = 0;
+		memset(basketSound, 0, sizeof(basketSound));
+		memset(basketState, 0, sizeof(basketState));
+		basketCount = 0;
+		matchCount = 0;
+		selectionPending = 0;
+		selected1Slot = 0;
+		selected2Slot = 0;
+		sound1 = 0;
+		sound2 = 0;
+	}
+} YogiState;
+
+class CarnivalEngine : public BoltEngine {
+public:
+	CarnivalEngine(OSystem *syst, const ADGameDescription *gameDesc);
+
+protected:
+	// xpMain
+	void boltMain() override;
+	int16 displayDemoPict(int16 prevBooth);
+
+	void initCallbacks() override;
+	
+	// Booth
+	void startCycle(byte *cycleResource);
+	void displayBooth(int16 page);
+	void playAVOverBooth(int16 animIndex);
+	int16 hucksBooth(int16 prevBooth);
+	int16 fredsBooth(int16 prevBooth);
+	int16 scoobysBooth(int16 prevBooth);
+	int16 yogisBooth(int16 prevBooth);
+	int16 georgesBooth(int16 prevBooth);
+	int16 topCatsBooth(int16 prevBooth);
+	int16 mainEntrance(int16 prevBooth);
+	bool loadBooth(int16 boothId);
+	void unloadBooth();
+	int16 openBooth(int16 boothId);
+	void closeBooth();
+	void playTour();
+	void finishPlayingHelp(int16 activeHotspot);
+	int16 hotSpotActive(int16 hotspot);
+	void hoverHotSpot();
+	int16 boothEventLoop();
+	void resetInactivityState();
+	bool handleButtonPress(int16 hotspot);
+	void blastColors(byte **paletteTable, int16 index, int16 mode);
+	void setColors(int16 index);
+	void restoreColors(int16 index);
+	void loadColors();
+	void shiftColorMap(byte *colorMap, int16 delta);
+	void playBoothAV();
+	void mainEntranceHelpBlink();
+	void boothHelpBlink();
+	void tourPaletteCycleStep();
+	void fadeToBlack(int16 steps);
+	void flushInput();
+	int16 winALetter(int16 prevBooth);
+	int16 endDemo(int16 prevBooth);
+
+	int16 _lettersWon = 0;
+	bool _allLettersWonFlag = false;
+	int _displayMode = 0;
+	int32 _displayX = 0;
+	int32 _displayY = 0;
+	int32 _displayWidth = 0;
+	int32 _displayHeight = 0;
+
+	int16 _boothLoadedMask = 0;
+	int16 _currentBoothScene = 0;
+	int16 _boothNumHotspots = 0;
+	int16 _boothNumAnimations = 0;
+	byte *_boothSceneDesc = nullptr;
+	byte *_boothHotPalDescs[4];
+	Common::Rect _boothHotspotDescs[8]; 
+	byte *_boothAnimDescs[7];
+	byte *_boothAnimPalDescs[4];
+	byte _savedPalettes[7][30];
+	byte _savedHotPalettes[4][9];
+	byte *_boothPalCycleData = nullptr;
+	XPPicDesc _boothLetterSprite;
+	bool _needInitCursorPos = true;
+	byte *_boothVisitSignOn = nullptr;
+	byte *_boothVisitSignOff = nullptr;
+	byte _leftDoorNavTable[3]  = { 3, 2, 4 };
+	byte _rightDoorNavTable[3] = { 1, 0, 5 };
+
+	int16 _cursorX = 0;
+	int16 _cursorY = 0;
+	int16 _hoveredHotspot = 0;
+	uint32 _helpTimer = 0;
+	int16 _keyReleased = 0;
+	int16 _keyLeft = 0;
+	int16 _keyRight = 0;
+	int16 _keyUp = 0;
+	int16 _keyDown = 0;
+	int16 _helpFlag = 0;
+	int16 _keyEnter = 0;
+
+	int16 _tourStep = 0;
+	int16 _helpPlaying = 0;
+	int16 _helpIsIdle = 0;
+	int16 _idleHelpAudioAvailable = 1;
+
+	int16 _huckWins = 0;
+	int16 _fredWins = 0;
+	int16 _scoobyWins = 0;
+	int16 _yogiWins = 0;
+	int16 _georgeWins = 0;
+	int16 _topCatWins = 0;
+
+	// Barker
+	BarkerTable *createBarker(int16 minIndex, int16 maxIndex);
+	void freeBarker(BarkerTable *table);
+	bool registerSideShow(BarkerTable *table, SideShowHandler handler, int16 boothId);
+	int16 barker(BarkerTable *table, int16 startBooth);
+	bool checkError();
+
+	int16 _currentBoothId = 0;
+
+	// --- MINIGAMES ---
+
+	// --- FRED ---
+	int16 fredGame(int16 prevBooth);
+	bool initFred();
+	void cleanUpFred();
+	bool initFredLevel(int16 levelGroup, int16 palGroup);
+	void termFredLevel(int16 levelGroup, int16 palGroup);
+	void swapFredAnimEntry();
+	void swapFredAnimDesc();
+	void swapFredLevelDesc();
+	int16 playFred();
+	int16 helpFred();
+	void hiliteFredHelpObject(byte *entry, int16 highlight);
+	void helpAnimStep();
+	bool spawnBalloon();
+	int16 calcBalloonSpawnDelay();
+	int16 selectBalloonRow();
+	void setFredAnimMode(FredEntityState *state, int16 mode);
+	void renderFredScene();
+	void getFredSoundInfo(BOLTLib *lib, int16 memberId, SoundInfo *soundInfo);
+	void playFredSound(SoundInfo *oneShot, SoundInfo *loop);
+	void updateFredSound();
+
+	static void resolveAllRefsCb();
+	static void swapFredAnimEntryCb();
+	static void swapFredAnimDescCb();
+	static void swapFredLevelDescCb();
+
+	BOLTLib *_fredBoltLib = nullptr;
+	BOLTCallbacks _fredBoltCallbacks;
+
+	static BOLTCallback _fredTypeLoadCallbacks[28];
+	static BOLTCallback _fredTypeFreeCallbacks[28];
+
+	byte *_fredLevelPtr = nullptr;
+	byte *_fredBackground = nullptr;
+	byte *_fredBalloonString = nullptr;
+	byte *_fredPalette = nullptr;
+	byte *_fredFacingLeftRect = nullptr;
+	byte *_fredFacingRightRect = nullptr;
+	byte *_fredTurningRect = nullptr;
+	byte *_fredBalloonRect = nullptr;
+	byte *_fredHelpEntries = nullptr;
+	byte *_fredPlayButton = nullptr;
+	byte *_fredSprites[10] = { nullptr };
+	FredEntityState **_fredEntitiesTable = nullptr;
+
+	byte *_fredRowBounds = nullptr;
+	byte *_fredShuffleTable = nullptr;
+	byte *_fredCycleRaw = nullptr;
+	byte *_fredBalloonSprite = nullptr;
+	byte *_fredPathMatrix = nullptr;
+	byte *_fredCurrentHelpObject = nullptr;
+	byte *_fredHoveredEntry = nullptr;
+
+	SoundInfo _fredSounds[4];
+
+	SoundInfo *_fredCurrentSound = nullptr;
+	SoundInfo *_fredLoopSound = nullptr;
+	SoundInfo *_fredPendingOneShot = nullptr;
+	SoundInfo *_fredPendingLoop = nullptr;
+
+	FredEntityState _fredSprite;
+
+	int16 _fredSaveData[3] = { 0, 0, 0 };
+	int16 _fredLevelIndex = 0;
+
+	int16 _fredBalloonSpawnDelay = 0;
+	int16 _fredBalloonSearchIdx = 0;
+	uint32 _fredTimer = 0;
+	int16 _fredHelpStep = -1;
+	int16 _fredShowHelp = 1;
+
+	XPCycleState _fredCycleSpecs[4];
+
+	// --- GEORGE ---
+	int16 georgeGame(int16 prevBooth);
+	bool initGeorge();
+	void cleanUpGeorge();
+	bool initGeorgeLevel(int16 level, int16 variant);
+	void termGeorgeLevel(int16 level, int16 variant);
+	void swapGeorgeFrameArray();
+	void swapGeorgeHelpEntry();
+	void swapGeorgeThresholds();
+	int16 playGeorge();
+	int16 helpGeorge();
+	void hiliteGeorgeHelpObject(byte *entry, int16 highlight);
+	void advanceHelpAnimation();
+	bool spawnSatellite();
+	int16 getRandomSatelliteWait();
+	int16 getRandomAsteroidRow();
+	bool confirmAsteroidHitTest();
+	bool spawnAsteroid();
+	int16 getRandomAsteroidWait();
+	int16 getAsteroidRow();
+	void setGeorgeAnimMode(GeorgeEntityState *sat, int16 mode);
+	void setSatelliteAnimMode(GeorgeEntityState *sat, int16 mode, int16 variant);
+	void setAsteroidAnimMode(GeorgeEntityState *sat, int16 mode, int16 variant);
+	void drawFlyingObjects();
+	void getGeorgeSoundInfo(BOLTLib *boltLib, int16 member, SoundInfo *outInfo, byte priority);
+	void playGeorgeSound(SoundInfo *newSound, SoundInfo *nextSound);
+	void updateGeorgeSound();
+
+	static void swapGeorgeFrameArrayCb();
+	static void swapGeorgeHelpEntryCb();
+	static void swapGeorgeThresholdsCb();
+
+	BOLTLib *_georgeBoltLib = nullptr;
+	BOLTCallbacks _georgeBoltCallbacks;
+
+	static BOLTCallback _georgeTypeLoadCallbacks[28];
+	static BOLTCallback _georgeTypeFreeCallbacks[28];
+
+	byte *_georgePrevActiveHelpObject = nullptr;
+	byte *_georgeActiveHelpObject = nullptr;
+	int16 _georgeHelpStep = -1;
+	int16 _georgeHelpActive = 1;
+
+	GeorgeEntityState **_georgeEntityList = nullptr;
+	int16 _georgeCarIdx = 0;
+	int16 _georgeNumSatellites = 0;
+	int16 _georgeFirstAsteroidIdx = 0;
+	int16 _georgeTotalSatellites = 0;
+	byte *_georgeCarPics[3] = { nullptr };
+	byte *_georgeHelpObjects = nullptr;
+	byte *_georgeHelpSequence = nullptr;
+	uint32 _georgeHelpTimer = 0;
+	byte *_georgeSatelliteShuffleTable = nullptr;
+	byte *_georgeAsteroidShuffleTable = nullptr;
+	int16 *_georgeSatelliteThresholds = nullptr;
+	int16 *_georgeAsteroidThresholds = nullptr;
+	byte *_georgeSatellitePaths = nullptr;
+	byte *_georgeAsteroidPaths = nullptr;
+	byte *_georgeCollisionRect = nullptr;
+	byte *_georgeSatelliteCollisionRects = nullptr;
+	byte *_georgeAsteroidCollisionRects = nullptr;
+	byte *_georgeSatelliteGfx = nullptr;
+	byte *_georgeAsteroidGfx = nullptr;
+	int16 _georgeSatelliteWait = 0;
+	int16 _georgeAsteroidWait = 0;
+	int16 _georgeSatelliteSearchIdx = 0;
+	int16 _georgeAsteroidSearchIdx = 0;
+	int16 _georgeHitSearchIdx = 0;
+	byte *_georgeBgPic = nullptr;
+	byte *_georgePalette = nullptr;
+	byte *_georgePalCycleRawData = nullptr;
+	XPCycleState _georgePalCycleSpecs[4];
+	int16 _georgeCollectedSatellitesNum = 0;
+	SoundInfo _georgeSoundCarStartUp;
+	SoundInfo _georgeSoundCarGoesAway;
+	SoundInfo _georgeSoundCarLoopHi;
+	SoundInfo _georgeSoundCarLoopLo;
+	SoundInfo _georgeSoundCarTumble;
+	SoundInfo **_georgeSatelliteSoundList = nullptr;
+	byte _georgeSoundChannelCounter = 0;
+	SoundInfo *_georgeSoundToPlay = nullptr;
+	SoundInfo *_georgeSoundCurrent = nullptr;
+	SoundInfo *_georgeSoundQueued = nullptr;
+	SoundInfo *_georgeSoundNext = nullptr;
+	int16 _georgeSaveData[3] = { 0 };
+	int16 *_georgeThresholds = nullptr;
+
+	// --- HUCK ---
+	void playSoundMapHuck(int16 memberId);
+	void waitSoundMapHuck();
+	void setHuckColors(int16 which);
+	void restoreHuckColors(int16 which);
+	void startHuckShuffleTimer();
+	void drawGift(int16 slot);
+	void drawHuckGifts();
+	void checkHuckLevelComplete();
+	bool initHuckDisplay();
+	bool loadHuckResources();
+	void unloadHuckResources();
+	bool initHuckLevel();
+	bool resumeHuckLevel();
+	bool initHuck();
+	void huckToggleBlinking(int16 *state, int16 which);
+	void huckUpdateHotSpots(int16 x, int16 y);
+	int16 findGift(int16 x, int16 y);
+	bool handleGiftSelect(int16 x, int16 y);
+	void huckHandleActionButton(int16 x, int16 y);
+	void giftSwap();
+	void resolveHuckSelection();
+	void handleEvent(int16 eventType, uint32 eventData);
+	void playHuck();
+	void cleanUpHuck();
+	int16 huckGame(int16 prevBooth);
+	void swapHuckWordArray();
+	void swapHuckWords();
+
+	static void swapHuckWordArrayCb();
+	static void swapHuckWordsCb();
+
+	BOLTLib *_huckBoltLib = nullptr;
+	BOLTCallbacks _huckBoltCallbacks;
+
+	static BOLTCallback _huckTypeLoadCallbacks[27];
+	static BOLTCallback _huckTypeFreeCallbacks[27];
+
+	int16 _huckSoundPlaying = 0;
+	int16 _huckHotSpotCount = 0;
+	int16 _huckActionState = 0;
+	uint32 _huckScreensaverTimer = 0;
+	uint32 _huckBlinkTimer = 0;
+	int16 _huckScreensaverFlag = 0;
+	int16 _huckBlinkFlag = 0;
+	uint32 _huckShuffleTimer = 0;
+	int16 _huckGlobal[30] = { 0 };
+	HuckState _huckState;
+	byte *_huckGiftPic = nullptr;
+	byte *_huckBgPic = nullptr;
+	int16 _huckGiftGroupId = 0;
+	int16 _huckVariantGroupId = 0;
+	byte *_huckBgDisplayPic = nullptr;
+	int16 _huckScrollOffset = 0;
+	int16 _huckPalRange[8] = { 0 };
+	int16 _huckReturnBooth = 0;
+	int16 _huckExitFlag = 0;
+	int16 _huckCursorX = 0;
+	int16 _huckCursorY = 0;
+	byte _huckPalSave0[15] = { 0 };
+	byte _huckPalHighlight0[15] = { 0 };
+	byte _huckPalSave1[15] = { 0 };
+	byte _huckPalHighlight1[15] = { 0 };
+	XPPicDesc _huckScratchPic;
+
+	// --- SCOOBY ---
+	bool loadScoobyBaseAssets();
+	void cleanUpScoobyBaseAssets();
+	void displayPicClipHack(byte *pic, int16 offsetX, int16 offsetY, int16 *clipRect, int16 displayMode);
+	void drawMovingWalls(int16 cellIdx, int16 direction, int16 picFrame, int16 bgFrame);
+	void drawAllMovingWalls();
+	void animateTransition(int16 memberIdx);
+	void clearPictMSb(byte *pic);
+	void initScoobyLevelGraphics();
+	bool initScoobyLevelAssets();
+	void cleanUpScoobyLevelGraphics();
+	void setScoobySpriteDirection(int16 startMember);
+	void playSoundMapScooby(int16 memberIdx);
+	void playWallSound();
+	void animateWalls();
+	void decideDirection();
+	void updateScoobySound();
+	void setScoobySound(int16 mode);
+	void updateScoobyLocation();
+	void updateScoobyWalls();
+	void updateScoobyDirection(int16 inputDir);
+	void updateScoobyTransition();
+	bool initScoobyLevel();
+	bool resumeScoobyLevel();
+	bool initScooby();
+	void cleanUpScooby();
+	int16 helpScooby();
+	void hiliteScoobyHelpObject(byte *entry, int16 highlight);
+	int16 xpDirToBOLTDir(uint32 xpDir);
+	int16 playScooby();
+	int16 scoobyGame(int16 prevBooth);
+	void swapScoobyHelpEntry();
+	void swapScoobyWordArray();
+
+	static void swapScoobyHelpEntryCb();
+	static void swapScoobyWordArrayCb();
+
+	BOLTLib *_scoobyBoltLib = nullptr;
+	BOLTCallbacks _scoobyBoltCallbacks;
+
+	static BOLTCallback _scoobyTypeLoadCallbacks[28];
+	static BOLTCallback _scoobyTypeFreeCallbacks[28];
+
+	int16 _scoobySoundMode = 0;
+	int16 _scoobySoundPlaying = 0;
+	int16 _scoobyShowHelp = 1;
+
+	int16 _scoobyLastInputDir = 0;
+	int16 _scoobyTransitionFrom = 0;
+	int16 _scoobyTransitionTarget = 0;
+	int16 _scoobyTransitionVelX = 0;
+	int16 _scoobyTransitionVelY = 0;
+	XPPicDesc _scoobyTempPic;
+	byte _scoobyGlobalSaveData[0x3C] = { 0 };
+	ScoobyState _scoobyGameState;
+	SSprite _scoobySprite;
+	byte *_scoobyBaseData = nullptr;
+	byte *_scoobyBgPic = nullptr;
+	byte *_scoobyWallPicsA[4] = { nullptr };
+	byte *_scoobyWallPicsB[5] = { nullptr };
+	ScoobyRect _scoobyCellBounds[25];
+	Common::Point _scoobyLevelStartXY[25];
+	byte *_scoobyLevelData = nullptr;
+	int16 _scoobySelectedGraphicsGroup = 0;
+	int16 _scoobyDifficulty = 0;
+	int16 _scoobyLevelCount = 0;
+	int16 _scoobyMoveRequested = 0;
+	int16 _scoobyTransitioning = 0;
+	int16 _scoobyDesiredDir = 0;
+	int16 _scoobyInputHoldCount = 0;
+	int16 _scoobyWallAnimating = 0;
+	int16 _scoobyWallAnimStep = 0;
+	int16 _scoobyWallsToOpen = 0;
+	int16 _scoobyWallsToClose = 0;
+
+	// --- TOPCAT ---
+	int16 topCatGame(int16 prevBooth);
+	bool initTopCat();
+	void cleanUpTopCat();
+	int16 playTopCat();
+	int16 handleActionButton(int16 *result);
+	void queueAnim(int16 animType, int16 param);
+	bool maintainAnim(int16 soundEvent);
+	void maintainIdleSound(int16 decrement);
+	bool startNextAnim(int16 *playAnswerAnim);
+	void setAnimType(int16 newType);
+	void highlightObject(byte *entry, int16 highlight);
+	bool setupNextQuestion();
+	void adjustColors(byte *pic, int8 shift);
+	void shuffleTopCatQuestions();
+	void shuffleTopCatPermutations();
+	void getTopCatSoundInfo(BOLTLib *lib, int16 memberId, SoundInfo *soundInfo);
+	void setScoreLights(int16 litMask);
+	void swapTopCatHelpEntry();
+
+	static void swapTopCatHelpEntryCb();
+
+	RTFResource *_topCatRtfHandle = nullptr;
+	RTFResource *_topCatAvHandle = nullptr;
+	BOLTLib *_topCatBoltLib = nullptr;
+	BOLTCallbacks _topCatBoltCallbacks;
+
+	static BOLTCallback _topCatTypeLoadCallbacks[26];
+	static BOLTCallback _topCatTypeFreeCallbacks[26];
+
+	byte *_topCatBackgroundPalette = nullptr;
+	byte *_topCatBackground = nullptr;
+	byte *_topCatBackgroundAnimationPalette = nullptr;
+	byte *_topCatGraphicsAssets = nullptr;
+
+	int16 _topCatBackgroundAnimFrame = 0;
+	int16 _topCatMaxBackgroundAnimFrames = 0;
+	int16 _topCatCurrentAnimType = 0;
+	int16 _topCatAnimStateMachineStep = 0;
+	int16 _topCatAnimQueueSize = 0;
+	int16 _topCatQueuedSoundFrames = 0;
+	byte *_topCatButtonsPalette = nullptr;
+	byte *_topCatBlinkEntries = nullptr;
+	byte *_topCatLightsPalette = nullptr;
+	int16 *_topCatBlinkSeqPtr = nullptr;
+	byte *_topCatSelectedChoiceOverlayGfx = nullptr;
+	byte *_topCatCycleData = nullptr;
+	XPCycleState _topCatCycleSpecs[4];
+	byte *_topCatShuffledQuestions = nullptr;
+	byte *_topCatShuffledAnswers = nullptr;
+	byte *_topCatAnswersPermutations = nullptr;
+	byte *_topCatAnswers = nullptr;
+	byte *_topCatAnswersScreenPositions = nullptr;
+	int16 _topCatSavedScore = 0;
+	int16 _topCatSaveHistory = 0;
+	int16 _topCatScore = 0;
+	int16 _topCatShuffledQuestionsArrayIdx = 0;
+	uint32 _topCatBlinkTimer = 0;
+	SoundInfo _topCatSoundInfo;
+	byte _topCatSavedShuffledQuestions[60] = { 0 };
+	byte _topCatSavedShuffledAnswers[60] = { 0 };
+	byte _topCatSavedAnswersPermutations[60 * 3] = { 0 };
+	byte _topCatSaveBuffer[302] = { 0 };
+	byte *_topCatHoveredEntry = nullptr;
+	byte *_topCatHelpButton = nullptr;
+	byte *_topCatBackButton = nullptr;
+	byte *_topCatBlinkEntry = nullptr;
+	int16 _topCatCycleStep = 0;
+	int16 _topCatDrawnQuestionId = 0;
+	int16 _topCatCurrentAnswerIdx = 0;
+	XPCycleState _topCatChoiceCycleState[4];
+	TopCatAnim _topCatAnimQueue[3];
+	int16 _topCatCorrectAnimIdx = 0;
+	int16 _topCatWrongAnimIdx = 0;
+	int16 _topCatShouldPlayAnswerAnim = 0;
+	int16 _topCatMaintainSoundFlag = 0;
+	int16 _topCatPermTableA[3] = { 0, 1, 2 };
+	int16 _topCatPermTableB[3] = { 0, 1, 2 };
+	int16 _topCatBlinkSeq0[5] = { 0x01, 0x00, 0x01, 0x00, 0x01 };
+	int16 _topCatBlinkSeq1[5] = { 0x03, 0x01, 0x03, 0x01, 0x03 };
+	int16 _topCatBlinkSeq2[5] = { 0x07, 0x03, 0x07, 0x03, 0x07 };
+	int16 _topCatBlinkSeq3[5] = { 0x0F, 0x07, 0x0F, 0x07, 0x0F };
+	int16 _topCatBlinkSeq4[5] = { 0x1F, 0x0F, 0x1F, 0x0F, 0x1F };
+	int16 _topCatBlinkSeq5[5] = { 0x3F, 0x1F, 0x3F, 0x1F, 0x3F };
+	int16 _topCatBlinkSeq6[25] = {
+		0x09, 0x12, 0x24, 0x09, 0x12, 0x24,
+		0x09, 0x12, 0x24, 0x09, 0x12, 0x24,
+		0x09, 0x12, 0x24, 0x09, 0x12, 0x24,
+		0x09, 0x12, 0x24, 0x09, 0x12, 0x24,
+		0x3F
+	};
+
+	int16 *_topCatBlinkSeqTable[7] = {
+		_topCatBlinkSeq0, _topCatBlinkSeq1, _topCatBlinkSeq2,
+		_topCatBlinkSeq3, _topCatBlinkSeq4, _topCatBlinkSeq5,
+		_topCatBlinkSeq6
+	};
+
+	// --- YOGI ---
+	void playSoundMapYogi(int16 memberId);
+	void waitSoundMapYogi();
+	void stopSoundYogi();
+	void setYogiColors(int16 which);
+	void restoreYogiColors(int16 which);
+	void drawBasket(int16 slot, byte *basketSprite);
+	void drawAllBaskets();
+	void handleYogiMatch();
+	bool loadYogiBgPic();
+	void unloadYogiBgPic();
+	void drawYogiLevel();
+	bool loadYogiLevel();
+	void unloadYogiResources();
+	bool initYogiLevel();
+	bool resumeYogiLevel();
+	bool initYogi();
+	void yogiToggleBlinking(int16 which, int16 *state);
+	void yogiUpdateHotSpots(int16 x, int16 y);
+	int16 findBasket(int16 x, int16 y);
+	void resolveYogiSelection();
+	bool handleBasketSelect(int16 x, int16 y);
+	void yogiHandleActionButton(int16 x, int16 y);
+	void handleYogiEvent(int16 eventType, uint32 eventData);
+	void playYogi();
+	void cleanUpYogi();
+	int16 yogiGame(int16 prevBooth);
+
+	void swapYogiAllWords();
+	void swapYogiFirstWord();
+
+	static void swapYogiAllWordsCb();
+	static void swapYogiFirstWordCb();
+
+	BOLTLib *_yogiBoltLib = nullptr;
+	BOLTCallbacks _yogiBoltCallbacks;
+
+	static BOLTCallback _yogiTypeLoadCallbacks[27];
+	static BOLTCallback _yogiTypeFreeCallbacks[27];
+
+	int16 _yogiSoundPlaying = 0;
+	int16 _yogiSoundActive = 0;
+	int16 _yogiHotSpotCount = 0;
+	int16 _yogiAnimActive = 0;
+	uint32 _yogiBlinkTimer1 = 0;
+	uint32 _yogiBlinkTimer2 = 0;
+	int16 _yogiBlinkState1 = 0;
+	int16 _yogiBlinkState2 = 0;
+	int16 _yogiGlobal[0xA0] = { 0 };
+	YogiState _yogiState;
+	byte *_yogiBasketPic = nullptr;
+	int16 _yogiLevelGroupId = 0;
+	byte *_yogiBgPic = nullptr;
+	byte *_yogiNormalSprite = nullptr;
+	byte *_yogiHlSprite = nullptr;
+	byte *_yogiAnimSprite = nullptr;
+	int16 _yogiSpriteStride = 0;
+	int16 _yogiSpriteHeight = 0;
+	int16 _yogiPalRange[8] = { 0 };
+	int16 _yogiReturnBooth = 0;
+	int16 _yogiExitFlag = 0;
+	int16 _yogiLevelId = 0;
+	int16 _yogiCursorX = 0;
+	int16 _yogiCursorY = 0;
+	byte _yogiPalSave0[15] = { 0 };
+	byte _yogiPalHighlight0[15] = { 0 };
+	byte _yogiPalSave1[15] = { 0 };
+	byte _yogiPalHighlight1[15] = { 0 };
+	XPPicDesc _yogiScratchBuf;
+};
+
+} // End of namespace Carnival
+
+} // End of namespace Bolt
+
+#endif // CARNIVAL_CARNIVAL_H
diff --git a/engines/bolt/carnival/resource.cpp b/engines/bolt/carnival/resource.cpp
new file mode 100644
index 00000000000..bb848f9f857
--- /dev/null
+++ b/engines/bolt/carnival/resource.cpp
@@ -0,0 +1,274 @@
+/* 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 "bolt/carnival/carnival.h"
+
+namespace Bolt {
+
+namespace Carnival {
+	
+void CarnivalEngine::resolveAllRefsCb() { ((CarnivalEngine *)g_engine)->resolveAllRefs(); }
+void CarnivalEngine::swapFredAnimEntryCb() { ((CarnivalEngine *)g_engine)->swapFredAnimEntry(); }
+void CarnivalEngine::swapFredAnimDescCb() { ((CarnivalEngine *)g_engine)->swapFredAnimDesc(); }
+void CarnivalEngine::swapFredLevelDescCb() { ((CarnivalEngine *)g_engine)->swapFredLevelDesc(); }
+
+void CarnivalEngine::swapGeorgeFrameArrayCb() { ((CarnivalEngine *)g_engine)->swapGeorgeFrameArray(); }
+void CarnivalEngine::swapGeorgeHelpEntryCb() { ((CarnivalEngine *)g_engine)->swapGeorgeHelpEntry(); }
+void CarnivalEngine::swapGeorgeThresholdsCb() { ((CarnivalEngine *)g_engine)->swapGeorgeThresholds(); }
+
+void CarnivalEngine::swapHuckWordArrayCb() { ((CarnivalEngine *)g_engine)->swapHuckWordArray(); }
+void CarnivalEngine::swapHuckWordsCb() { ((CarnivalEngine *)g_engine)->swapHuckWords(); }
+
+void CarnivalEngine::swapScoobyHelpEntryCb() { ((CarnivalEngine *)g_engine)->swapScoobyHelpEntry(); }
+void CarnivalEngine::swapScoobyWordArrayCb() { ((CarnivalEngine *)g_engine)->swapScoobyWordArray(); }
+
+void CarnivalEngine::swapTopCatHelpEntryCb() { ((CarnivalEngine *)g_engine)->swapTopCatHelpEntry(); }
+
+void CarnivalEngine::swapYogiAllWordsCb() { ((CarnivalEngine *)g_engine)->swapYogiAllWords(); }
+void CarnivalEngine::swapYogiFirstWordCb() { ((CarnivalEngine *)g_engine)->swapYogiFirstWord(); }
+
+BOLTCallback CarnivalEngine::_fredTypeLoadCallbacks[28];
+BOLTCallback CarnivalEngine::_fredTypeFreeCallbacks[28];
+
+BOLTCallback CarnivalEngine::_georgeTypeLoadCallbacks[28];
+BOLTCallback CarnivalEngine::_georgeTypeFreeCallbacks[28];
+
+BOLTCallback CarnivalEngine::_huckTypeLoadCallbacks[27];
+BOLTCallback CarnivalEngine::_huckTypeFreeCallbacks[27];
+
+BOLTCallback CarnivalEngine::_scoobyTypeLoadCallbacks[28];
+BOLTCallback CarnivalEngine::_scoobyTypeFreeCallbacks[28];
+
+BOLTCallback CarnivalEngine::_topCatTypeLoadCallbacks[26];
+BOLTCallback CarnivalEngine::_topCatTypeFreeCallbacks[26];
+
+BOLTCallback CarnivalEngine::_yogiTypeLoadCallbacks[27];
+BOLTCallback CarnivalEngine::_yogiTypeFreeCallbacks[27];
+
+void CarnivalEngine::initCallbacks() {
+	// --- BOOTHS ---
+	for (int i = 0; i < ARRAYSIZE(_defaultTypeLoadCallbacks); i++) {
+		_defaultTypeLoadCallbacks[i] = noOpCb;
+	}
+
+	_defaultTypeLoadCallbacks[2] = swapAllWordsCb;
+	_defaultTypeLoadCallbacks[8] = swapSpriteHeaderCb;
+	_defaultTypeLoadCallbacks[10] = swapPicHeaderCb;
+	_defaultTypeLoadCallbacks[11] = swapAndResolvePicDescCb;
+	_defaultTypeLoadCallbacks[12] = swapFirstTwoWordsCb;
+	_defaultTypeLoadCallbacks[14] = swapFirstFourWordsCb;
+
+	for (int i = 0; i < ARRAYSIZE(_defaultTypeFreeCallbacks); i++) {
+		_defaultTypeFreeCallbacks[i] = noOpCb;
+	}
+
+	_defaultTypeFreeCallbacks[8] = freeSpriteCleanUpCb;
+
+	for (int i = 0; i < ARRAYSIZE(_defaultMemberLoadCallbacks); i++) {
+		_defaultMemberLoadCallbacks[i] = noOpCb;
+	}
+
+	for (int i = 0; i < ARRAYSIZE(_defaultMemberFreeCallbacks); i++) {
+		_defaultMemberFreeCallbacks[i] = noOpCb;
+	}
+
+	for (int i = 0; i < ARRAYSIZE(_defaultGroupLoadCallbacks); i++) {
+		_defaultGroupLoadCallbacks[i] = noOpCb;
+	}
+
+	for (int i = 0; i < ARRAYSIZE(_defaultGroupFreeCallbacks); i++) {
+		_defaultGroupFreeCallbacks[i] = noOpCb;
+	}
+
+	_boothsBoltCallbacks.typeLoadCallbacks = _defaultTypeLoadCallbacks;
+	_boothsBoltCallbacks.typeFreeCallbacks = _defaultTypeFreeCallbacks;
+	_boothsBoltCallbacks.memberLoadCallbacks = _defaultMemberLoadCallbacks;
+	_boothsBoltCallbacks.memberFreeCallbacks = _defaultMemberFreeCallbacks;
+	_boothsBoltCallbacks.groupLoadCallbacks = _defaultGroupLoadCallbacks;
+	_boothsBoltCallbacks.groupFreeCallbacks = _defaultGroupFreeCallbacks;
+
+	// --- FRED ---
+	for (int i = 0; i < ARRAYSIZE(_fredTypeLoadCallbacks); i++) {
+		_fredTypeLoadCallbacks[i] = noOpCb;
+	}
+
+	_fredTypeLoadCallbacks[2] = swapAllWordsCb;
+	_fredTypeLoadCallbacks[6] = resolveAllRefsCb;
+	_fredTypeLoadCallbacks[8] = swapSpriteHeaderCb;
+	_fredTypeLoadCallbacks[10] = swapPicHeaderCb;
+	_fredTypeLoadCallbacks[11] = swapAndResolvePicDescCb;
+	_fredTypeLoadCallbacks[12] = swapFirstTwoWordsCb;
+	_fredTypeLoadCallbacks[14] = swapFirstFourWordsCb;
+	_fredTypeLoadCallbacks[25] = swapFredAnimEntryCb;
+	_fredTypeLoadCallbacks[26] = swapFredAnimDescCb;
+	_fredTypeLoadCallbacks[27] = swapFredLevelDescCb;
+
+	for (int i = 0; i < ARRAYSIZE(_fredTypeFreeCallbacks); i++) {
+		_fredTypeFreeCallbacks[i] = noOpCb;
+	}
+
+	_fredTypeFreeCallbacks[8] = freeSpriteCleanUpCb;
+
+	_fredBoltCallbacks.typeLoadCallbacks = _fredTypeLoadCallbacks;
+	_fredBoltCallbacks.typeFreeCallbacks = _fredTypeFreeCallbacks;
+	_fredBoltCallbacks.memberLoadCallbacks = _defaultMemberLoadCallbacks;
+	_fredBoltCallbacks.memberFreeCallbacks = _defaultMemberFreeCallbacks;
+	_fredBoltCallbacks.groupLoadCallbacks = _defaultGroupLoadCallbacks;
+	_fredBoltCallbacks.groupFreeCallbacks = _defaultGroupFreeCallbacks;
+
+	// --- GEORGE ---
+	for (int i = 0; i < ARRAYSIZE(_georgeTypeLoadCallbacks); i++) {
+		_georgeTypeLoadCallbacks[i] = noOpCb;
+	}
+
+	_georgeTypeLoadCallbacks[2] = swapAllWordsCb;
+	_georgeTypeLoadCallbacks[6] = resolveAllRefsCb;
+	_georgeTypeLoadCallbacks[8] = swapSpriteHeaderCb;
+	_georgeTypeLoadCallbacks[10] = swapPicHeaderCb;
+	_georgeTypeLoadCallbacks[11] = swapAndResolvePicDescCb;
+	_georgeTypeLoadCallbacks[12] = swapFirstTwoWordsCb;
+	_georgeTypeLoadCallbacks[14] = swapFirstFourWordsCb;
+	_georgeTypeLoadCallbacks[25] = swapGeorgeFrameArrayCb;
+	_georgeTypeLoadCallbacks[26] = swapGeorgeHelpEntryCb;
+	_georgeTypeLoadCallbacks[27] = swapGeorgeThresholdsCb;
+
+	for (int i = 0; i < ARRAYSIZE(_georgeTypeFreeCallbacks); i++) {
+		_georgeTypeFreeCallbacks[i] = noOpCb;
+	}
+
+	_georgeTypeFreeCallbacks[8] = freeSpriteCleanUpCb;
+
+	_georgeBoltCallbacks.typeLoadCallbacks = _georgeTypeLoadCallbacks;
+	_georgeBoltCallbacks.typeFreeCallbacks = _georgeTypeFreeCallbacks;
+	_georgeBoltCallbacks.memberLoadCallbacks = _defaultMemberLoadCallbacks;
+	_georgeBoltCallbacks.memberFreeCallbacks = _defaultMemberFreeCallbacks;
+	_georgeBoltCallbacks.groupLoadCallbacks = _defaultGroupLoadCallbacks;
+	_georgeBoltCallbacks.groupFreeCallbacks = _defaultGroupFreeCallbacks;
+
+	// --- HUCK ---
+	for (int i = 0; i < ARRAYSIZE(_huckTypeLoadCallbacks); i++) {
+		_huckTypeLoadCallbacks[i] = noOpCb;
+	}
+
+	_huckTypeLoadCallbacks[2] = swapAllWordsCb;
+	_huckTypeLoadCallbacks[6] = resolveAllRefsCb;
+	_huckTypeLoadCallbacks[8] = swapSpriteHeaderCb;
+	_huckTypeLoadCallbacks[10] = swapPicHeaderCb;
+	_huckTypeLoadCallbacks[11] = swapAndResolvePicDescCb;
+	_huckTypeLoadCallbacks[12] = swapFirstTwoWordsCb;
+	_huckTypeLoadCallbacks[14] = swapFirstFourWordsCb;
+	_huckTypeLoadCallbacks[25] = swapHuckWordArrayCb;
+	_huckTypeLoadCallbacks[26] = swapHuckWordsCb;
+
+	for (int i = 0; i < ARRAYSIZE(_huckTypeFreeCallbacks); i++) {
+		_huckTypeFreeCallbacks[i] = noOpCb;
+	}
+
+	_huckTypeFreeCallbacks[8] = freeSpriteCleanUpCb;
+
+	_huckBoltCallbacks.typeLoadCallbacks = _huckTypeLoadCallbacks;
+	_huckBoltCallbacks.typeFreeCallbacks = _huckTypeFreeCallbacks;
+	_huckBoltCallbacks.memberLoadCallbacks = _defaultMemberLoadCallbacks;
+	_huckBoltCallbacks.memberFreeCallbacks = _defaultMemberFreeCallbacks;
+	_huckBoltCallbacks.groupLoadCallbacks = _defaultGroupLoadCallbacks;
+	_huckBoltCallbacks.groupFreeCallbacks = _defaultGroupFreeCallbacks;
+
+	// --- SCOOBY ---
+	for (int i = 0; i < ARRAYSIZE(_scoobyTypeLoadCallbacks); i++) {
+		_scoobyTypeLoadCallbacks[i] = noOpCb;
+	}
+
+	_scoobyTypeLoadCallbacks[8] = swapSpriteHeaderCb;
+	_scoobyTypeLoadCallbacks[10] = swapPicHeaderCb;
+	_scoobyTypeLoadCallbacks[11] = swapAndResolvePicDescCb;
+	_scoobyTypeLoadCallbacks[12] = swapFirstTwoWordsCb;
+	_scoobyTypeLoadCallbacks[26] = swapScoobyHelpEntryCb;
+	_scoobyTypeLoadCallbacks[27] = swapScoobyWordArrayCb;
+
+	for (int i = 0; i < ARRAYSIZE(_scoobyTypeFreeCallbacks); i++) {
+		_scoobyTypeFreeCallbacks[i] = noOpCb;
+	}
+
+	_scoobyTypeFreeCallbacks[8] = freeSpriteCleanUpCb;
+
+	_scoobyBoltCallbacks.typeLoadCallbacks = _scoobyTypeLoadCallbacks;
+	_scoobyBoltCallbacks.typeFreeCallbacks = _scoobyTypeFreeCallbacks;
+	_scoobyBoltCallbacks.memberLoadCallbacks = _defaultMemberLoadCallbacks;
+	_scoobyBoltCallbacks.memberFreeCallbacks = _defaultMemberFreeCallbacks;
+	_scoobyBoltCallbacks.groupLoadCallbacks = _defaultGroupLoadCallbacks;
+	_scoobyBoltCallbacks.groupFreeCallbacks = _defaultGroupFreeCallbacks;
+
+	// --- TOPCAT ---
+	for (int i = 0; i < ARRAYSIZE(_topCatTypeLoadCallbacks); i++) {
+		_topCatTypeLoadCallbacks[i] = noOpCb;
+	}
+
+	_topCatTypeLoadCallbacks[2] = swapAllWordsCb;
+	_topCatTypeLoadCallbacks[4] = swapAllLongsCb;
+	_topCatTypeLoadCallbacks[6] = resolveAllRefsCb;
+	_topCatTypeLoadCallbacks[8] = swapSpriteHeaderCb;
+	_topCatTypeLoadCallbacks[10] = swapPicHeaderCb;
+	_topCatTypeLoadCallbacks[11] = swapAndResolvePicDescCb;
+	_topCatTypeLoadCallbacks[12] = swapFirstTwoWordsCb;
+	_topCatTypeLoadCallbacks[25] = swapTopCatHelpEntryCb;
+
+	for (int i = 0; i < ARRAYSIZE(_topCatTypeFreeCallbacks); i++) {
+		_topCatTypeFreeCallbacks[i] = noOpCb;
+	}
+
+	_topCatTypeFreeCallbacks[8] = freeSpriteCleanUpCb;
+
+	_topCatBoltCallbacks.typeLoadCallbacks = _topCatTypeLoadCallbacks;
+	_topCatBoltCallbacks.typeFreeCallbacks = _topCatTypeFreeCallbacks;
+	_topCatBoltCallbacks.memberLoadCallbacks = _defaultMemberLoadCallbacks;
+	_topCatBoltCallbacks.memberFreeCallbacks = _defaultMemberFreeCallbacks;
+	_topCatBoltCallbacks.groupLoadCallbacks = _defaultGroupLoadCallbacks;
+	_topCatBoltCallbacks.groupFreeCallbacks = _defaultGroupFreeCallbacks;
+
+	// --- YOGI ---
+	for (int i = 0; i < ARRAYSIZE(_yogiTypeLoadCallbacks); i++) {
+		_yogiTypeLoadCallbacks[i] = noOpCb;
+	}
+
+	_yogiTypeLoadCallbacks[2] = swapAllWordsCb;
+	_yogiTypeLoadCallbacks[8] = swapSpriteHeaderCb;
+	_yogiTypeLoadCallbacks[10] = swapPicHeaderCb;
+	_yogiTypeLoadCallbacks[14] = swapFirstFourWordsCb;
+	_yogiTypeLoadCallbacks[25] = swapYogiFirstWordCb;
+	_yogiTypeLoadCallbacks[26] = swapYogiAllWordsCb;
+
+	for (int i = 0; i < ARRAYSIZE(_yogiTypeFreeCallbacks); i++) {
+		_yogiTypeFreeCallbacks[i] = noOpCb;
+	}
+
+	_yogiTypeFreeCallbacks[8] = freeSpriteCleanUpCb;
+
+	_yogiBoltCallbacks.typeLoadCallbacks = _yogiTypeLoadCallbacks;
+	_yogiBoltCallbacks.typeFreeCallbacks = _yogiTypeFreeCallbacks;
+	_yogiBoltCallbacks.memberLoadCallbacks = _defaultMemberLoadCallbacks;
+	_yogiBoltCallbacks.memberFreeCallbacks = _defaultMemberFreeCallbacks;
+	_yogiBoltCallbacks.groupLoadCallbacks = _defaultGroupLoadCallbacks;
+	_yogiBoltCallbacks.groupFreeCallbacks = _defaultGroupFreeCallbacks;
+}
+
+} // End of namespace Carnival
+
+} // End of namespace Bolt
diff --git a/engines/bolt/crete/crete.cpp b/engines/bolt/crete/crete.cpp
new file mode 100644
index 00000000000..c005575d7b8
--- /dev/null
+++ b/engines/bolt/crete/crete.cpp
@@ -0,0 +1,44 @@
+/* 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 "bolt/crete/crete.h"
+
+namespace Bolt {
+
+namespace Crete {
+	
+CreteEngine::CreteEngine(OSystem *syst, const ADGameDescription *gameDesc)
+	: BoltEngine(syst, gameDesc) {
+	initCallbacks();
+}
+
+void CreteEngine::boltMain() {
+	// TODO
+	errorUnsupportedGame("Not implemented yet");
+}
+
+void CreteEngine::initCallbacks() {
+	// TODO
+}
+
+} // End of namespace Crete
+
+} // End of namespace Bolt
diff --git a/engines/bolt/crete/crete.h b/engines/bolt/crete/crete.h
new file mode 100644
index 00000000000..32879459f31
--- /dev/null
+++ b/engines/bolt/crete/crete.h
@@ -0,0 +1,46 @@
+/* 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 CRETE_CRETE_H
+#define CRETE_CRETE_H
+
+#include "bolt/bolt.h"
+
+namespace Bolt {
+
+namespace Crete {
+
+class CreteEngine : public BoltEngine {
+public:
+	CreteEngine(OSystem *syst, const ADGameDescription *gameDesc);
+	
+protected:
+	// xpMain
+	void boltMain() override;
+
+	void initCallbacks() override;
+};
+
+} // End of namespace Crete
+
+} // End of namespace Bolt
+
+#endif // CRETE_CRETE_H
diff --git a/engines/bolt/merlin/merlin.cpp b/engines/bolt/merlin/merlin.cpp
new file mode 100644
index 00000000000..9b49d1b00db
--- /dev/null
+++ b/engines/bolt/merlin/merlin.cpp
@@ -0,0 +1,44 @@
+/* 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 "bolt/merlin/merlin.h"
+
+namespace Bolt {
+
+namespace Merlin {
+	
+MerlinEngine::MerlinEngine(OSystem *syst, const ADGameDescription *gameDesc)
+	: BoltEngine(syst, gameDesc) {
+	initCallbacks();
+}
+
+void MerlinEngine::boltMain() {
+	// TODO
+	errorUnsupportedGame("Not implemented yet");
+}
+
+void MerlinEngine::initCallbacks() {
+	// TODO
+}
+
+} // End of namespace Merlin
+
+} // End of namespace Bolt
diff --git a/engines/bolt/merlin/merlin.h b/engines/bolt/merlin/merlin.h
new file mode 100644
index 00000000000..c335d8996e5
--- /dev/null
+++ b/engines/bolt/merlin/merlin.h
@@ -0,0 +1,46 @@
+/* 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 MERLIN_MERLIN_H
+#define MERLIN_MERLIN_H
+
+#include "bolt/bolt.h"
+
+namespace Bolt {
+
+namespace Merlin {
+
+class MerlinEngine : public BoltEngine {
+public:
+	MerlinEngine(OSystem *syst, const ADGameDescription *gameDesc);
+	
+protected:
+	// xpMain
+	void boltMain() override;
+
+	void initCallbacks() override;
+};
+
+} // End of namespace Merlin
+
+} // End of namespace Bolt
+
+#endif // MERLIN_MERLIN_H
diff --git a/engines/bolt/metaengine.cpp b/engines/bolt/metaengine.cpp
index ef801c093fc..8c4938cdff6 100644
--- a/engines/bolt/metaengine.cpp
+++ b/engines/bolt/metaengine.cpp
@@ -24,6 +24,9 @@
 #include "bolt/bolt.h"
 #include "bolt/metaengine.h"
 #include "bolt/detection.h"
+#include "bolt/carnival/carnival.h"
+#include "bolt/crete/crete.h"
+#include "bolt/merlin/merlin.h"
 
 namespace Bolt {
 
@@ -53,7 +56,16 @@ const ADExtraGuiOptionsMap *BoltMetaEngine::getAdvancedExtraGuiOptions() const {
 }
 
 Common::Error BoltMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const {
-	*engine = new Bolt::BoltEngine(syst, desc);
+	if (strcmp(desc->gameId, "carnival") == 0) {
+		*engine = new Bolt::Carnival::CarnivalEngine(syst, desc);
+	} else if (strcmp(desc->gameId, "crete") == 0) {
+		*engine = new Bolt::Crete::CreteEngine(syst, desc);
+	} else if (strcmp(desc->gameId, "merlin") == 0) {
+		*engine = new Bolt::Merlin::MerlinEngine(syst, desc);
+	} else {
+		return Common::kUnsupportedGameidError;
+	}
+
 	return Common::kNoError;
 }
 
diff --git a/engines/bolt/module.mk b/engines/bolt/module.mk
index fe2ff69f77b..8c904d6c540 100644
--- a/engines/bolt/module.mk
+++ b/engines/bolt/module.mk
@@ -3,15 +3,19 @@ MODULE := engines/bolt
 MODULE_OBJS = \
 	anim.o \
 	av.o \
-	barker.o \
+	carnival/barker.o \
 	bolt.o \
-	booth.o \
-	booths/fred.o \
-	booths/george.o \
-	booths/huck.o \
-	booths/scooby.o \
-	booths/topcat.o \
-	booths/yogi.o \
+	carnival/booth.o \
+	carnival/booths/fred.o \
+	carnival/booths/george.o \
+	carnival/booths/huck.o \
+	carnival/booths/scooby.o \
+	carnival/booths/topcat.o \
+	carnival/booths/yogi.o \
+	carnival/carnival.o \
+	carnival/resource.o \
+	crete/crete.o \
+	merlin/merlin.o \
 	metaengine.o \
 	resource.o \
 	rtf.o \
diff --git a/engines/bolt/resource.cpp b/engines/bolt/resource.cpp
index 7fe4d40bc70..58b772810df 100644
--- a/engines/bolt/resource.cpp
+++ b/engines/bolt/resource.cpp
@@ -640,24 +640,6 @@ BOLTCallback BoltEngine::_defaultMemberFreeCallbacks[25];
 BOLTCallback BoltEngine::_defaultGroupLoadCallbacks[25];
 BOLTCallback BoltEngine::_defaultGroupFreeCallbacks[25];
 
-BOLTCallback BoltEngine::_fredTypeLoadCallbacks[28];
-BOLTCallback BoltEngine::_fredTypeFreeCallbacks[28];
-
-BOLTCallback BoltEngine::_georgeTypeLoadCallbacks[28];
-BOLTCallback BoltEngine::_georgeTypeFreeCallbacks[28];
-
-BOLTCallback BoltEngine::_huckTypeLoadCallbacks[27];
-BOLTCallback BoltEngine::_huckTypeFreeCallbacks[27];
-
-BOLTCallback BoltEngine::_scoobyTypeLoadCallbacks[28];
-BOLTCallback BoltEngine::_scoobyTypeFreeCallbacks[28];
-
-BOLTCallback BoltEngine::_topCatTypeLoadCallbacks[26];
-BOLTCallback BoltEngine::_topCatTypeFreeCallbacks[26];
-
-BOLTCallback BoltEngine::_yogiTypeLoadCallbacks[27];
-BOLTCallback BoltEngine::_yogiTypeFreeCallbacks[27];
-
 void BoltEngine::noOpCb() {}
 void BoltEngine::swapAllWordsCb() { ((BoltEngine *)g_engine)->swapAllWords(); }
 void BoltEngine::swapAllLongsCb() { ((BoltEngine *)g_engine)->swapAllLongs(); }
@@ -669,230 +651,4 @@ void BoltEngine::swapFirstFourWordsCb() { ((BoltEngine *)g_engine)->swapFirstFou
 void BoltEngine::swapSpriteHeaderCb() { ((BoltEngine *)g_engine)->swapSpriteHeader(); }
 void BoltEngine::freeSpriteCleanUpCb() { ((BoltEngine *)g_engine)->freeSpriteCleanUp(); }
 
-void BoltEngine::resolveAllRefsCb() { ((BoltEngine *)g_engine)->resolveAllRefs(); }
-void BoltEngine::swapFredAnimEntryCb() { ((BoltEngine *)g_engine)->swapFredAnimEntry(); }
-void BoltEngine::swapFredAnimDescCb() { ((BoltEngine *)g_engine)->swapFredAnimDesc(); }
-void BoltEngine::swapFredLevelDescCb() { ((BoltEngine *)g_engine)->swapFredLevelDesc(); }
-
-void BoltEngine::swapGeorgeFrameArrayCb() { ((BoltEngine *)g_engine)->swapGeorgeFrameArray(); }
-void BoltEngine::swapGeorgeHelpEntryCb() { ((BoltEngine *)g_engine)->swapGeorgeHelpEntry(); }
-void BoltEngine::swapGeorgeThresholdsCb() { ((BoltEngine *)g_engine)->swapGeorgeThresholds(); }
-
-void BoltEngine::swapHuckWordArrayCb() { ((BoltEngine *)g_engine)->swapHuckWordArray(); }
-void BoltEngine::swapHuckWordsCb() { ((BoltEngine *)g_engine)->swapHuckWords(); }
-
-void BoltEngine::swapScoobyHelpEntryCb() { ((BoltEngine *)g_engine)->swapScoobyHelpEntry(); }
-void BoltEngine::swapScoobyWordArrayCb() { ((BoltEngine *)g_engine)->swapScoobyWordArray(); }
-
-void BoltEngine::swapTopCatHelpEntryCb() { ((BoltEngine *)g_engine)->swapTopCatHelpEntry(); }
-
-void BoltEngine::swapYogiAllWordsCb() { ((BoltEngine *)g_engine)->swapYogiAllWords(); }
-void BoltEngine::swapYogiFirstWordCb() { ((BoltEngine *)g_engine)->swapYogiFirstWord(); }
-
-void BoltEngine::initCallbacks() {
-	// --- BOOTHS ---
-	for (int i = 0; i < ARRAYSIZE(_defaultTypeLoadCallbacks); i++) {
-		_defaultTypeLoadCallbacks[i] = noOpCb;
-	}
-
-	_defaultTypeLoadCallbacks[2] = swapAllWordsCb;
-	_defaultTypeLoadCallbacks[8] = swapSpriteHeaderCb;
-	_defaultTypeLoadCallbacks[10] = swapPicHeaderCb;
-	_defaultTypeLoadCallbacks[11] = swapAndResolvePicDescCb;
-	_defaultTypeLoadCallbacks[12] = swapFirstTwoWordsCb;
-	_defaultTypeLoadCallbacks[14] = swapFirstFourWordsCb;
-
-	for (int i = 0; i < ARRAYSIZE(_defaultTypeFreeCallbacks); i++) {
-		_defaultTypeFreeCallbacks[i] = noOpCb;
-	}
-
-	_defaultTypeFreeCallbacks[8] = freeSpriteCleanUpCb;
-
-	for (int i = 0; i < ARRAYSIZE(_defaultMemberLoadCallbacks); i++) {
-		_defaultMemberLoadCallbacks[i] = noOpCb;
-	}
-
-	for (int i = 0; i < ARRAYSIZE(_defaultMemberFreeCallbacks); i++) {
-		_defaultMemberFreeCallbacks[i] = noOpCb;
-	}
-
-	for (int i = 0; i < ARRAYSIZE(_defaultGroupLoadCallbacks); i++) {
-		_defaultGroupLoadCallbacks[i] = noOpCb;
-	}
-
-	for (int i = 0; i < ARRAYSIZE(_defaultGroupFreeCallbacks); i++) {
-		_defaultGroupFreeCallbacks[i] = noOpCb;
-	}
-
-	_boothsBoltCallbacks.typeLoadCallbacks = _defaultTypeLoadCallbacks;
-	_boothsBoltCallbacks.typeFreeCallbacks = _defaultTypeFreeCallbacks;
-	_boothsBoltCallbacks.memberLoadCallbacks = _defaultMemberLoadCallbacks;
-	_boothsBoltCallbacks.memberFreeCallbacks = _defaultMemberFreeCallbacks;
-	_boothsBoltCallbacks.groupLoadCallbacks = _defaultGroupLoadCallbacks;
-	_boothsBoltCallbacks.groupFreeCallbacks = _defaultGroupFreeCallbacks;
-
-	// --- FRED ---
-	for (int i = 0; i < ARRAYSIZE(_fredTypeLoadCallbacks); i++) {
-		_fredTypeLoadCallbacks[i] = noOpCb;
-	}
-
-	_fredTypeLoadCallbacks[2] = swapAllWordsCb;
-	_fredTypeLoadCallbacks[6] = resolveAllRefsCb;
-	_fredTypeLoadCallbacks[8] = swapSpriteHeaderCb;
-	_fredTypeLoadCallbacks[10] = swapPicHeaderCb;
-	_fredTypeLoadCallbacks[11] = swapAndResolvePicDescCb;
-	_fredTypeLoadCallbacks[12] = swapFirstTwoWordsCb;
-	_fredTypeLoadCallbacks[14] = swapFirstFourWordsCb;
-	_fredTypeLoadCallbacks[25] = swapFredAnimEntryCb;
-	_fredTypeLoadCallbacks[26] = swapFredAnimDescCb;
-	_fredTypeLoadCallbacks[27] = swapFredLevelDescCb;
-
-	for (int i = 0; i < ARRAYSIZE(_fredTypeFreeCallbacks); i++) {
-		_fredTypeFreeCallbacks[i] = noOpCb;
-	}
-
-	_fredTypeFreeCallbacks[8] = freeSpriteCleanUpCb;
-
-	_fredBoltCallbacks.typeLoadCallbacks = _fredTypeLoadCallbacks;
-	_fredBoltCallbacks.typeFreeCallbacks = _fredTypeFreeCallbacks;
-	_fredBoltCallbacks.memberLoadCallbacks = _defaultMemberLoadCallbacks;
-	_fredBoltCallbacks.memberFreeCallbacks = _defaultMemberFreeCallbacks;
-	_fredBoltCallbacks.groupLoadCallbacks = _defaultGroupLoadCallbacks;
-	_fredBoltCallbacks.groupFreeCallbacks = _defaultGroupFreeCallbacks;
-
-	// --- GEORGE ---
-	for (int i = 0; i < ARRAYSIZE(_georgeTypeLoadCallbacks); i++) {
-		_georgeTypeLoadCallbacks[i] = noOpCb;
-	}
-
-	_georgeTypeLoadCallbacks[2] = swapAllWordsCb;
-	_georgeTypeLoadCallbacks[6] = resolveAllRefsCb;
-	_georgeTypeLoadCallbacks[8] = swapSpriteHeaderCb;
-	_georgeTypeLoadCallbacks[10] = swapPicHeaderCb;
-	_georgeTypeLoadCallbacks[11] = swapAndResolvePicDescCb;
-	_georgeTypeLoadCallbacks[12] = swapFirstTwoWordsCb;
-	_georgeTypeLoadCallbacks[14] = swapFirstFourWordsCb;
-	_georgeTypeLoadCallbacks[25] = swapGeorgeFrameArrayCb;
-	_georgeTypeLoadCallbacks[26] = swapGeorgeHelpEntryCb;
-	_georgeTypeLoadCallbacks[27] = swapGeorgeThresholdsCb;
-
-	for (int i = 0; i < ARRAYSIZE(_georgeTypeFreeCallbacks); i++) {
-		_georgeTypeFreeCallbacks[i] = noOpCb;
-	}
-
-	_georgeTypeFreeCallbacks[8] = freeSpriteCleanUpCb;
-
-	_georgeBoltCallbacks.typeLoadCallbacks = _georgeTypeLoadCallbacks;
-	_georgeBoltCallbacks.typeFreeCallbacks = _georgeTypeFreeCallbacks;
-	_georgeBoltCallbacks.memberLoadCallbacks = _defaultMemberLoadCallbacks;
-	_georgeBoltCallbacks.memberFreeCallbacks = _defaultMemberFreeCallbacks;
-	_georgeBoltCallbacks.groupLoadCallbacks = _defaultGroupLoadCallbacks;
-	_georgeBoltCallbacks.groupFreeCallbacks = _defaultGroupFreeCallbacks;
-
-	// --- HUCK ---
-	for (int i = 0; i < ARRAYSIZE(_huckTypeLoadCallbacks); i++) {
-		_huckTypeLoadCallbacks[i] = noOpCb;
-	}
-
-	_huckTypeLoadCallbacks[2] = swapAllWordsCb;
-	_huckTypeLoadCallbacks[6] = resolveAllRefsCb;
-	_huckTypeLoadCallbacks[8] = swapSpriteHeaderCb;
-	_huckTypeLoadCallbacks[10] = swapPicHeaderCb;
-	_huckTypeLoadCallbacks[11] = swapAndResolvePicDescCb;
-	_huckTypeLoadCallbacks[12] = swapFirstTwoWordsCb;
-	_huckTypeLoadCallbacks[14] = swapFirstFourWordsCb;
-	_huckTypeLoadCallbacks[25] = swapHuckWordArrayCb;
-	_huckTypeLoadCallbacks[26] = swapHuckWordsCb;
-
-	for (int i = 0; i < ARRAYSIZE(_huckTypeFreeCallbacks); i++) {
-		_huckTypeFreeCallbacks[i] = noOpCb;
-	}
-
-	_huckTypeFreeCallbacks[8] = freeSpriteCleanUpCb;
-
-	_huckBoltCallbacks.typeLoadCallbacks = _huckTypeLoadCallbacks;
-	_huckBoltCallbacks.typeFreeCallbacks = _huckTypeFreeCallbacks;
-	_huckBoltCallbacks.memberLoadCallbacks = _defaultMemberLoadCallbacks;
-	_huckBoltCallbacks.memberFreeCallbacks = _defaultMemberFreeCallbacks;
-	_huckBoltCallbacks.groupLoadCallbacks = _defaultGroupLoadCallbacks;
-	_huckBoltCallbacks.groupFreeCallbacks = _defaultGroupFreeCallbacks;
-
-	// --- SCOOBY ---
-	for (int i = 0; i < ARRAYSIZE(_scoobyTypeLoadCallbacks); i++) {
-		_scoobyTypeLoadCallbacks[i] = noOpCb;
-	}
-
-	_scoobyTypeLoadCallbacks[8] = swapSpriteHeaderCb;
-	_scoobyTypeLoadCallbacks[10] = swapPicHeaderCb;
-	_scoobyTypeLoadCallbacks[11] = swapAndResolvePicDescCb;
-	_scoobyTypeLoadCallbacks[12] = swapFirstTwoWordsCb;
-	_scoobyTypeLoadCallbacks[26] = swapScoobyHelpEntryCb;
-	_scoobyTypeLoadCallbacks[27] = swapScoobyWordArrayCb;
-
-	for (int i = 0; i < ARRAYSIZE(_scoobyTypeFreeCallbacks); i++) {
-		_scoobyTypeFreeCallbacks[i] = noOpCb;
-	}
-
-	_scoobyTypeFreeCallbacks[8] = freeSpriteCleanUpCb;
-
-	_scoobyBoltCallbacks.typeLoadCallbacks = _scoobyTypeLoadCallbacks;
-	_scoobyBoltCallbacks.typeFreeCallbacks = _scoobyTypeFreeCallbacks;
-	_scoobyBoltCallbacks.memberLoadCallbacks = _defaultMemberLoadCallbacks;
-	_scoobyBoltCallbacks.memberFreeCallbacks = _defaultMemberFreeCallbacks;
-	_scoobyBoltCallbacks.groupLoadCallbacks = _defaultGroupLoadCallbacks;
-	_scoobyBoltCallbacks.groupFreeCallbacks = _defaultGroupFreeCallbacks;
-
-	// --- TOPCAT ---
-	for (int i = 0; i < ARRAYSIZE(_topCatTypeLoadCallbacks); i++) {
-		_topCatTypeLoadCallbacks[i] = noOpCb;
-	}
-
-	_topCatTypeLoadCallbacks[2] = swapAllWordsCb;
-	_topCatTypeLoadCallbacks[4] = swapAllLongsCb;
-	_topCatTypeLoadCallbacks[6] = resolveAllRefsCb;
-	_topCatTypeLoadCallbacks[8] = swapSpriteHeaderCb;
-	_topCatTypeLoadCallbacks[10] = swapPicHeaderCb;
-	_topCatTypeLoadCallbacks[11] = swapAndResolvePicDescCb;
-	_topCatTypeLoadCallbacks[12] = swapFirstTwoWordsCb;
-	_topCatTypeLoadCallbacks[25] = swapTopCatHelpEntryCb;
-
-	for (int i = 0; i < ARRAYSIZE(_topCatTypeFreeCallbacks); i++) {
-		_topCatTypeFreeCallbacks[i] = noOpCb;
-	}
-
-	_topCatTypeFreeCallbacks[8] = freeSpriteCleanUpCb;
-
-	_topCatBoltCallbacks.typeLoadCallbacks = _topCatTypeLoadCallbacks;
-	_topCatBoltCallbacks.typeFreeCallbacks = _topCatTypeFreeCallbacks;
-	_topCatBoltCallbacks.memberLoadCallbacks = _defaultMemberLoadCallbacks;
-	_topCatBoltCallbacks.memberFreeCallbacks = _defaultMemberFreeCallbacks;
-	_topCatBoltCallbacks.groupLoadCallbacks = _defaultGroupLoadCallbacks;
-	_topCatBoltCallbacks.groupFreeCallbacks = _defaultGroupFreeCallbacks;
-
-	// --- YOGI ---
-	for (int i = 0; i < ARRAYSIZE(_yogiTypeLoadCallbacks); i++) {
-		_yogiTypeLoadCallbacks[i] = noOpCb;
-	}
-
-	_yogiTypeLoadCallbacks[2] = swapAllWordsCb;
-	_yogiTypeLoadCallbacks[8] = swapSpriteHeaderCb;
-	_yogiTypeLoadCallbacks[10] = swapPicHeaderCb;
-	_yogiTypeLoadCallbacks[14] = swapFirstFourWordsCb;
-	_yogiTypeLoadCallbacks[25] = swapYogiFirstWordCb;
-	_yogiTypeLoadCallbacks[26] = swapYogiAllWordsCb;
-
-	for (int i = 0; i < ARRAYSIZE(_yogiTypeFreeCallbacks); i++) {
-		_yogiTypeFreeCallbacks[i] = noOpCb;
-	}
-
-	_yogiTypeFreeCallbacks[8] = freeSpriteCleanUpCb;
-
-	_yogiBoltCallbacks.typeLoadCallbacks = _yogiTypeLoadCallbacks;
-	_yogiBoltCallbacks.typeFreeCallbacks = _yogiTypeFreeCallbacks;
-	_yogiBoltCallbacks.memberLoadCallbacks = _defaultMemberLoadCallbacks;
-	_yogiBoltCallbacks.memberFreeCallbacks = _defaultMemberFreeCallbacks;
-	_yogiBoltCallbacks.groupLoadCallbacks = _defaultGroupLoadCallbacks;
-	_yogiBoltCallbacks.groupFreeCallbacks = _defaultGroupFreeCallbacks;
-}
-
 } // End of namespace Bolt



From noreply at scummvm.org  Sun Jun  7 22:09:33 2026
From: noreply at scummvm.org (ScummVM-Translations)
Date: Sun,  7 Jun 2026 22:09:33 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 9fa4ef438deb1e66ae6c15f9d5c208bf8caf58ae
Message-ID: <20260607220933.A38DA7005CF@ron.scummvm.net>

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

Summary:
9fa4ef438d I18N: Update translations templates


Commit: 9fa4ef438deb1e66ae6c15f9d5c208bf8caf58ae
    https://github.com/scummvm/scummvm/commit/9fa4ef438deb1e66ae6c15f9d5c208bf8caf58ae
Author: Weblate (noreply at weblate.org)
Date: 2026-06-07T22:09:29Z

Commit Message:
I18N: Update translations templates

Changed paths:
    po/scummvm.pot


diff --git a/po/scummvm.pot b/po/scummvm.pot
index 1e240a95821..3af0a30942f 100644
--- a/po/scummvm.pot
+++ b/po/scummvm.pot
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2026.2.1git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-07 21:08+0000\n"
+"POT-Creation-Date: 2026-06-07 22:09+0000\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL at ADDRESS>\n"
 "Language-Team: LANGUAGE <LL at li.org>\n"
@@ -6984,11 +6984,11 @@ msgstr ""
 msgid "Quit game"
 msgstr ""
 
-#: engines/bolt/metaengine.cpp:34
+#: engines/bolt/metaengine.cpp:37
 msgid "Extended game resolution"
 msgstr ""
 
-#: engines/bolt/metaengine.cpp:35
+#: engines/bolt/metaengine.cpp:38
 msgid ""
 "This option allows the game to play at a slightly extended resolution. This "
 "is what the Mac and CDi versions are supposed to play like."



From noreply at scummvm.org  Sun Jun  7 22:10:05 2026
From: noreply at scummvm.org (ScummVM-Translations)
Date: Sun,  7 Jun 2026 22:10:05 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 d6e1bc6235ab90362a23cf1be343adc72ef44583
Message-ID: <20260607221005.1187D7005BC@ron.scummvm.net>

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

Summary:
d6e1bc6235 I18N: Update translation files


Commit: d6e1bc6235ab90362a23cf1be343adc72ef44583
    https://github.com/scummvm/scummvm/commit/d6e1bc6235ab90362a23cf1be343adc72ef44583
Author: Weblate (noreply at weblate.org)
Date: 2026-06-07T22:09:46Z

Commit Message:
I18N: Update translation files

Updated by "Update PO files to match POT (msgmerge)" hook in Weblate.

Translation: ScummVM/scummvm
Translate-URL: https://translations.scummvm.org/projects/scummvm/scummvm/

Changed paths:
    po/ar.po
    po/be-tarask.po
    po/be_BY.po
    po/ca_ES.po
    po/cs_CZ.po
    po/da.po
    po/de_DE.po
    po/el.po
    po/es_ES.po
    po/eu.po
    po/fi_FI.po
    po/fr_FR.po
    po/gl_ES.po
    po/he.po
    po/hi.po
    po/hu_HU.po
    po/it_IT.po
    po/ja.po
    po/ka.po
    po/ko.po
    po/nb_NO.po
    po/nl_NL.po
    po/nn_NO.po
    po/pl_PL.po
    po/pt_BR.po
    po/pt_PT.po
    po/ro.po
    po/ru_RU.po
    po/sv_SE.po
    po/tr.po
    po/uk_UA.po
    po/zh.po
    po/zh_Hans.po
    po/zh_Hant.po


diff --git a/po/ar.po b/po/ar.po
index 7a4863c1600..78a2fa78d82 100644
--- a/po/ar.po
+++ b/po/ar.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-07 21:08+0000\n"
+"POT-Creation-Date: 2026-06-07 22:09+0000\n"
 "PO-Revision-Date: 2026-02-24 22:26+0000\n"
 "Last-Translator: Mohamed Shaaban <mohmedbashamedo at hotmail.com>\n"
 "Language-Team: Arabic <https://translations.scummvm.org/projects/scummvm/"
@@ -7665,13 +7665,13 @@ msgstr "?????????? ???????????? ????????????"
 msgid "Quit game"
 msgstr "???? ???????????? ????????????"
 
-#: engines/bolt/metaengine.cpp:34
+#: engines/bolt/metaengine.cpp:37
 #, fuzzy
 #| msgid "Enable high resolution"
 msgid "Extended game resolution"
 msgstr "?????????? ?????????? ??????????????"
 
-#: engines/bolt/metaengine.cpp:35
+#: engines/bolt/metaengine.cpp:38
 msgid ""
 "This option allows the game to play at a slightly extended resolution. This "
 "is what the Mac and CDi versions are supposed to play like."
diff --git a/po/be-tarask.po b/po/be-tarask.po
index e7e9a491e83..2d3be656900 100644
--- a/po/be-tarask.po
+++ b/po/be-tarask.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-07 21:08+0000\n"
+"POT-Creation-Date: 2026-06-07 22:09+0000\n"
 "PO-Revision-Date: 2023-02-26 15:01+0000\n"
 "Last-Translator: Kastu?? K. <oakum2334 at gmail.com>\n"
 "Language-Team: Belarusian (Tara??kievica) <https://translations.scummvm.org/"
@@ -7606,13 +7606,13 @@ msgstr "???????????????? ??????????????"
 msgid "Quit game"
 msgstr "?????????????? ?? ????????????"
 
-#: engines/bolt/metaengine.cpp:34
+#: engines/bolt/metaengine.cpp:37
 #, fuzzy
 #| msgid "Enable high resolution"
 msgid "Extended game resolution"
 msgstr "???????????????? ?????????????? ????????????????????????"
 
-#: engines/bolt/metaengine.cpp:35
+#: engines/bolt/metaengine.cpp:38
 msgid ""
 "This option allows the game to play at a slightly extended resolution. This "
 "is what the Mac and CDi versions are supposed to play like."
diff --git a/po/be_BY.po b/po/be_BY.po
index 3c7079c3750..74a5366bd78 100644
--- a/po/be_BY.po
+++ b/po/be_BY.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-07 21:08+0000\n"
+"POT-Creation-Date: 2026-06-07 22:09+0000\n"
 "PO-Revision-Date: 2021-03-21 08:24+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Belarusian <https://translations.scummvm.org/projects/scummvm/"
@@ -7577,12 +7577,12 @@ msgstr ""
 msgid "Quit game"
 msgstr "???????????? ?? ????????????"
 
-#: engines/bolt/metaengine.cpp:34
+#: engines/bolt/metaengine.cpp:37
 #, fuzzy
 msgid "Extended game resolution"
 msgstr "???????????????? ?????????????????????????? ?????????????? ???????????????? ????????????????????"
 
-#: engines/bolt/metaengine.cpp:35
+#: engines/bolt/metaengine.cpp:38
 msgid ""
 "This option allows the game to play at a slightly extended resolution. This "
 "is what the Mac and CDi versions are supposed to play like."
diff --git a/po/ca_ES.po b/po/ca_ES.po
index 77ddf5eeb1a..9f9796475a3 100644
--- a/po/ca_ES.po
+++ b/po/ca_ES.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.6.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-07 21:08+0000\n"
+"POT-Creation-Date: 2026-06-07 22:09+0000\n"
 "PO-Revision-Date: 2026-05-26 04:22+0000\n"
 "Last-Translator: Xaviu <xaviu at xaviu.com>\n"
 "Language-Team: Catalan <https://translations.scummvm.org/projects/scummvm/"
@@ -7332,11 +7332,11 @@ msgstr "Base de dades de pistes"
 msgid "Quit game"
 msgstr "Surt del joc"
 
-#: engines/bolt/metaengine.cpp:34
+#: engines/bolt/metaengine.cpp:37
 msgid "Extended game resolution"
 msgstr "Resoluci?? del joc ampliada"
 
-#: engines/bolt/metaengine.cpp:35
+#: engines/bolt/metaengine.cpp:38
 msgid ""
 "This option allows the game to play at a slightly extended resolution. This "
 "is what the Mac and CDi versions are supposed to play like."
diff --git a/po/cs_CZ.po b/po/cs_CZ.po
index 65e96289db6..1d2a357c9e0 100644
--- a/po/cs_CZ.po
+++ b/po/cs_CZ.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.7.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-07 21:08+0000\n"
+"POT-Creation-Date: 2026-06-07 22:09+0000\n"
 "PO-Revision-Date: 2026-02-09 20:22+0000\n"
 "Last-Translator: AsciiWolf <mail at asciiwolf.com>\n"
 "Language-Team: Czech <https://translations.scummvm.org/projects/scummvm/"
@@ -7532,12 +7532,12 @@ msgstr ""
 msgid "Quit game"
 msgstr " (Hra)"
 
-#: engines/bolt/metaengine.cpp:34
+#: engines/bolt/metaengine.cpp:37
 #, fuzzy
 msgid "Extended game resolution"
 msgstr "Povolit grafiku ve vysok??m rozli??en??"
 
-#: engines/bolt/metaengine.cpp:35
+#: engines/bolt/metaengine.cpp:38
 msgid ""
 "This option allows the game to play at a slightly extended resolution. This "
 "is what the Mac and CDi versions are supposed to play like."
diff --git a/po/da.po b/po/da.po
index 623a5c3b8ac..c6a653ba9eb 100644
--- a/po/da.po
+++ b/po/da.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-07 21:08+0000\n"
+"POT-Creation-Date: 2026-06-07 22:09+0000\n"
 "PO-Revision-Date: 2023-09-07 17:04+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Danish <https://translations.scummvm.org/projects/scummvm/"
@@ -7560,12 +7560,12 @@ msgstr ""
 msgid "Quit game"
 msgstr " (Spil)"
 
-#: engines/bolt/metaengine.cpp:34
+#: engines/bolt/metaengine.cpp:37
 #, fuzzy
 msgid "Extended game resolution"
 msgstr "Aktiv??r grafik i h??j opl??sning"
 
-#: engines/bolt/metaengine.cpp:35
+#: engines/bolt/metaengine.cpp:38
 msgid ""
 "This option allows the game to play at a slightly extended resolution. This "
 "is what the Mac and CDi versions are supposed to play like."
diff --git a/po/de_DE.po b/po/de_DE.po
index 32c37bdc113..2057a4a6727 100644
--- a/po/de_DE.po
+++ b/po/de_DE.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.10.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-07 21:08+0000\n"
+"POT-Creation-Date: 2026-06-07 22:09+0000\n"
 "PO-Revision-Date: 2026-05-05 16:31+0000\n"
 "Last-Translator: Lothar Serra Mari <lothar.serramari at scummvm.org>\n"
 "Language-Team: German <https://translations.scummvm.org/projects/scummvm/"
@@ -7815,11 +7815,11 @@ msgstr "Hinweis-Datenbank"
 msgid "Quit game"
 msgstr "Spiel beenden"
 
-#: engines/bolt/metaengine.cpp:34
+#: engines/bolt/metaengine.cpp:37
 msgid "Extended game resolution"
 msgstr "Erweiterte Spiel-Aufl??sung"
 
-#: engines/bolt/metaengine.cpp:35
+#: engines/bolt/metaengine.cpp:38
 msgid ""
 "This option allows the game to play at a slightly extended resolution. This "
 "is what the Mac and CDi versions are supposed to play like."
diff --git a/po/el.po b/po/el.po
index e5eb847103d..1002be7626f 100644
--- a/po/el.po
+++ b/po/el.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.10.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-07 21:08+0000\n"
+"POT-Creation-Date: 2026-06-07 22:09+0000\n"
 "PO-Revision-Date: 2026-04-23 00:22+0000\n"
 "Last-Translator: Antoniou Athanasios <a.antoniou79 at gmail.com>\n"
 "Language-Team: Greek <https://translations.scummvm.org/projects/scummvm/"
@@ -7943,11 +7943,11 @@ msgstr "???????? ?????????????????? ?????????????????? ??????????????"
 msgid "Quit game"
 msgstr "???????????? ?????? ???? ????????????????"
 
-#: engines/bolt/metaengine.cpp:34
+#: engines/bolt/metaengine.cpp:37
 msgid "Extended game resolution"
 msgstr "???????????????????? ?????????????? ????????????????????"
 
-#: engines/bolt/metaengine.cpp:35
+#: engines/bolt/metaengine.cpp:38
 msgid ""
 "This option allows the game to play at a slightly extended resolution. This "
 "is what the Mac and CDi versions are supposed to play like."
diff --git a/po/es_ES.po b/po/es_ES.po
index a06406f61c0..066f553be8b 100644
--- a/po/es_ES.po
+++ b/po/es_ES.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.4.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-07 21:08+0000\n"
+"POT-Creation-Date: 2026-06-07 22:09+0000\n"
 "PO-Revision-Date: 2026-06-03 08:30+0000\n"
 "Last-Translator: IlDucci <pajaroloco_2 at hotmail.com>\n"
 "Language-Team: Spanish <https://translations.scummvm.org/projects/scummvm/"
@@ -7830,11 +7830,11 @@ msgstr "Base de datos de pistas"
 msgid "Quit game"
 msgstr "Salir del juego"
 
-#: engines/bolt/metaengine.cpp:34
+#: engines/bolt/metaengine.cpp:37
 msgid "Extended game resolution"
 msgstr "Resoluci??n expandida"
 
-#: engines/bolt/metaengine.cpp:35
+#: engines/bolt/metaengine.cpp:38
 msgid ""
 "This option allows the game to play at a slightly extended resolution. This "
 "is what the Mac and CDi versions are supposed to play like."
diff --git a/po/eu.po b/po/eu.po
index 1d65c241bb4..64a2ccffe6b 100644
--- a/po/eu.po
+++ b/po/eu.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.5.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-07 21:08+0000\n"
+"POT-Creation-Date: 2026-06-07 22:09+0000\n"
 "PO-Revision-Date: 2024-02-05 18:42+0000\n"
 "Last-Translator: Josu Igoa <josuigoa at ni.eus>\n"
 "Language-Team: Basque <https://translations.scummvm.org/projects/scummvm/"
@@ -7607,12 +7607,12 @@ msgstr ""
 msgid "Quit game"
 msgstr " (Jokoa)"
 
-#: engines/bolt/metaengine.cpp:34
+#: engines/bolt/metaengine.cpp:37
 #, fuzzy
 msgid "Extended game resolution"
 msgstr "Gaitu erresoluzio altuko grafikoak"
 
-#: engines/bolt/metaengine.cpp:35
+#: engines/bolt/metaengine.cpp:38
 msgid ""
 "This option allows the game to play at a slightly extended resolution. This "
 "is what the Mac and CDi versions are supposed to play like."
diff --git a/po/fi_FI.po b/po/fi_FI.po
index b0620b6b570..98307ddbf3f 100644
--- a/po/fi_FI.po
+++ b/po/fi_FI.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.6.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-07 21:08+0000\n"
+"POT-Creation-Date: 2026-06-07 22:09+0000\n"
 "PO-Revision-Date: 2026-02-07 15:22+0000\n"
 "Last-Translator: Timo Mikkolainen <tmikkola at gmail.com>\n"
 "Language-Team: Finnish <https://translations.scummvm.org/projects/scummvm/"
@@ -7913,13 +7913,13 @@ msgstr "Vihjeiden tietokanta"
 msgid "Quit game"
 msgstr "Lopeta peli"
 
-#: engines/bolt/metaengine.cpp:34
+#: engines/bolt/metaengine.cpp:37
 #, fuzzy
 #| msgid "Enable high resolution"
 msgid "Extended game resolution"
 msgstr "K??yt?? korkeaa resoluutiota"
 
-#: engines/bolt/metaengine.cpp:35
+#: engines/bolt/metaengine.cpp:38
 msgid ""
 "This option allows the game to play at a slightly extended resolution. This "
 "is what the Mac and CDi versions are supposed to play like."
diff --git a/po/fr_FR.po b/po/fr_FR.po
index eb4d1cf0ae9..9d9302420a7 100644
--- a/po/fr_FR.po
+++ b/po/fr_FR.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-07 21:08+0000\n"
+"POT-Creation-Date: 2026-06-07 22:09+0000\n"
 "PO-Revision-Date: 2026-06-07 13:22+0000\n"
 "Last-Translator: Purple T <ZEONK at hotmail.com>\n"
 "Language-Team: French <https://translations.scummvm.org/projects/scummvm/"
@@ -7835,11 +7835,11 @@ msgstr "Panneau des indices"
 msgid "Quit game"
 msgstr "Quitter le jeu"
 
-#: engines/bolt/metaengine.cpp:34
+#: engines/bolt/metaengine.cpp:37
 msgid "Extended game resolution"
 msgstr "Jeu en r??solution ??tendue"
 
-#: engines/bolt/metaengine.cpp:35
+#: engines/bolt/metaengine.cpp:38
 msgid ""
 "This option allows the game to play at a slightly extended resolution. This "
 "is what the Mac and CDi versions are supposed to play like."
diff --git a/po/gl_ES.po b/po/gl_ES.po
index 730c1dd582b..cfff58ef99d 100644
--- a/po/gl_ES.po
+++ b/po/gl_ES.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-07 21:08+0000\n"
+"POT-Creation-Date: 2026-06-07 22:09+0000\n"
 "PO-Revision-Date: 2020-09-26 21:13+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Galician <https://translations.scummvm.org/projects/scummvm/"
@@ -7540,12 +7540,12 @@ msgstr ""
 msgid "Quit game"
 msgstr " (Xogo)"
 
-#: engines/bolt/metaengine.cpp:34
+#: engines/bolt/metaengine.cpp:37
 #, fuzzy
 msgid "Extended game resolution"
 msgstr "Activar gr??ficos de alta resoluci??n"
 
-#: engines/bolt/metaengine.cpp:35
+#: engines/bolt/metaengine.cpp:38
 msgid ""
 "This option allows the game to play at a slightly extended resolution. This "
 "is what the Mac and CDi versions are supposed to play like."
diff --git a/po/he.po b/po/he.po
index 0aa623f5628..b8248295ac9 100644
--- a/po/he.po
+++ b/po/he.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.1.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-07 21:08+0000\n"
+"POT-Creation-Date: 2026-06-07 22:09+0000\n"
 "PO-Revision-Date: 2025-04-09 20:18+0000\n"
 "Last-Translator: Niv Baehr <bloop93 at gmail.com>\n"
 "Language-Team: Hebrew <https://translations.scummvm.org/projects/scummvm/"
@@ -7531,13 +7531,13 @@ msgstr "?????? ???????????? ???? ??????????"
 msgid "Quit game"
 msgstr "?????????? ????????????"
 
-#: engines/bolt/metaengine.cpp:34
+#: engines/bolt/metaengine.cpp:37
 #, fuzzy
 #| msgid "Enable high resolution"
 msgid "Extended game resolution"
 msgstr "?????????? ?????????? ??????????"
 
-#: engines/bolt/metaengine.cpp:35
+#: engines/bolt/metaengine.cpp:38
 msgid ""
 "This option allows the game to play at a slightly extended resolution. This "
 "is what the Mac and CDi versions are supposed to play like."
diff --git a/po/hi.po b/po/hi.po
index 49c564e60de..5c4fc384911 100644
--- a/po/hi.po
+++ b/po/hi.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-07 21:08+0000\n"
+"POT-Creation-Date: 2026-06-07 22:09+0000\n"
 "PO-Revision-Date: 2026-05-12 22:22+0000\n"
 "Last-Translator: KARTIK SAXENA <kartiksaxena2005 at gmail.com>\n"
 "Language-Team: Hindi <https://translations.scummvm.org/projects/scummvm/"
@@ -7256,13 +7256,13 @@ msgstr ""
 msgid "Quit game"
 msgstr "????????? quit ????????????"
 
-#: engines/bolt/metaengine.cpp:34
+#: engines/bolt/metaengine.cpp:37
 #, fuzzy
 #| msgid "Change game options"
 msgid "Extended game resolution"
 msgstr "????????? ?????? ?????????????????? ???????????????"
 
-#: engines/bolt/metaengine.cpp:35
+#: engines/bolt/metaengine.cpp:38
 msgid ""
 "This option allows the game to play at a slightly extended resolution. This "
 "is what the Mac and CDi versions are supposed to play like."
diff --git a/po/hu_HU.po b/po/hu_HU.po
index d57c74f0d21..3f98b8f5f50 100644
--- a/po/hu_HU.po
+++ b/po/hu_HU.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-07 21:08+0000\n"
+"POT-Creation-Date: 2026-06-07 22:09+0000\n"
 "PO-Revision-Date: 2025-03-07 00:18+0000\n"
 "Last-Translator: Hov??nszki Tam??s <gahex68379 at lofiey.com>\n"
 "Language-Team: Hungarian <https://translations.scummvm.org/projects/scummvm/"
@@ -8048,13 +8048,13 @@ msgstr "Nyom Adatb??zis"
 msgid "Quit game"
 msgstr "Kil??p??s j??t??kb??l"
 
-#: engines/bolt/metaengine.cpp:34
+#: engines/bolt/metaengine.cpp:37
 #, fuzzy
 #| msgid "Enable high resolution"
 msgid "Extended game resolution"
 msgstr "Nagy felbont??s enged??lyez??se"
 
-#: engines/bolt/metaengine.cpp:35
+#: engines/bolt/metaengine.cpp:38
 msgid ""
 "This option allows the game to play at a slightly extended resolution. This "
 "is what the Mac and CDi versions are supposed to play like."
diff --git a/po/it_IT.po b/po/it_IT.po
index 00a8e6f0867..7a39be51202 100644
--- a/po/it_IT.po
+++ b/po/it_IT.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-07 21:08+0000\n"
+"POT-Creation-Date: 2026-06-07 22:09+0000\n"
 "PO-Revision-Date: 2026-06-04 19:00+0000\n"
 "Last-Translator: Walter Agazzi <walter.agazzi at protonmail.com>\n"
 "Language-Team: Italian <https://translations.scummvm.org/projects/scummvm/"
@@ -7813,11 +7813,11 @@ msgstr "Database indizi"
 msgid "Quit game"
 msgstr "Esci dal gioco"
 
-#: engines/bolt/metaengine.cpp:34
+#: engines/bolt/metaengine.cpp:37
 msgid "Extended game resolution"
 msgstr "Risoluzione di gioco estesa"
 
-#: engines/bolt/metaengine.cpp:35
+#: engines/bolt/metaengine.cpp:38
 msgid ""
 "This option allows the game to play at a slightly extended resolution. This "
 "is what the Mac and CDi versions are supposed to play like."
diff --git a/po/ja.po b/po/ja.po
index 7cc6b6bc3ff..4532ea2d1f4 100644
--- a/po/ja.po
+++ b/po/ja.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-07 21:08+0000\n"
+"POT-Creation-Date: 2026-06-07 22:09+0000\n"
 "PO-Revision-Date: 2026-06-04 07:22+0000\n"
 "Last-Translator: VAN-Gluon <van.gluonp at gmail.com>\n"
 "Language-Team: Japanese <https://translations.scummvm.org/projects/scummvm/"
@@ -7652,13 +7652,13 @@ msgstr "??????????????????????????????"
 msgid "Quit game"
 msgstr "???????????????"
 
-#: engines/bolt/metaengine.cpp:34
+#: engines/bolt/metaengine.cpp:37
 #, fuzzy
 #| msgid "Enable high resolution"
 msgid "Extended game resolution"
 msgstr "????????????????????????"
 
-#: engines/bolt/metaengine.cpp:35
+#: engines/bolt/metaengine.cpp:38
 msgid ""
 "This option allows the game to play at a slightly extended resolution. This "
 "is what the Mac and CDi versions are supposed to play like."
diff --git a/po/ka.po b/po/ka.po
index 4d3b63b2689..72a27ce86f6 100644
--- a/po/ka.po
+++ b/po/ka.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-07 21:08+0000\n"
+"POT-Creation-Date: 2026-06-07 22:09+0000\n"
 "PO-Revision-Date: 2026-06-05 10:22+0000\n"
 "Last-Translator: NorwayFun <temuri.doghonadze at gmail.com>\n"
 "Language-Team: Georgian <https://translations.scummvm.org/projects/scummvm/"
@@ -7018,11 +7018,11 @@ msgstr "?????????????????????????????? ????????????"
 msgid "Quit game"
 msgstr "??????????????????????????? ??????????????????"
 
-#: engines/bolt/metaengine.cpp:34
+#: engines/bolt/metaengine.cpp:37
 msgid "Extended game resolution"
 msgstr "???????????????????????????????????? ????????????????????? ?????????????????????????????????"
 
-#: engines/bolt/metaengine.cpp:35
+#: engines/bolt/metaengine.cpp:38
 msgid ""
 "This option allows the game to play at a slightly extended resolution. This "
 "is what the Mac and CDi versions are supposed to play like."
diff --git a/po/ko.po b/po/ko.po
index 52c82f8c25b..a823f53cd90 100644
--- a/po/ko.po
+++ b/po/ko.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-07 21:08+0000\n"
+"POT-Creation-Date: 2026-06-07 22:09+0000\n"
 "PO-Revision-Date: 2026-05-19 20:22+0000\n"
 "Last-Translator: Hoseok Seo <ddinghoya at gmail.com>\n"
 "Language-Team: Korean <https://translations.scummvm.org/projects/scummvm/"
@@ -7632,11 +7632,11 @@ msgstr "?????? ??????????????????"
 msgid "Quit game"
 msgstr "?????? ??????"
 
-#: engines/bolt/metaengine.cpp:34
+#: engines/bolt/metaengine.cpp:37
 msgid "Extended game resolution"
 msgstr "????????? ?????? ?????????"
 
-#: engines/bolt/metaengine.cpp:35
+#: engines/bolt/metaengine.cpp:38
 msgid ""
 "This option allows the game to play at a slightly extended resolution. This "
 "is what the Mac and CDi versions are supposed to play like."
diff --git a/po/nb_NO.po b/po/nb_NO.po
index ef991945c7b..3aa47a2a6e0 100644
--- a/po/nb_NO.po
+++ b/po/nb_NO.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-07 21:08+0000\n"
+"POT-Creation-Date: 2026-06-07 22:09+0000\n"
 "PO-Revision-Date: 2026-02-07 15:22+0000\n"
 "Last-Translator: Einar Johan Tr??an S??m??en <einarjohants at gmail.com>\n"
 "Language-Team: Norwegian Bokm??l <https://translations.scummvm.org/projects/"
@@ -8114,13 +8114,13 @@ msgstr "Ledetr??dsdatabase"
 msgid "Quit game"
 msgstr "Avslutt spill"
 
-#: engines/bolt/metaengine.cpp:34
+#: engines/bolt/metaengine.cpp:37
 #, fuzzy
 #| msgid "Enable high resolution"
 msgid "Extended game resolution"
 msgstr "Aktiver h??y oppl??sning"
 
-#: engines/bolt/metaengine.cpp:35
+#: engines/bolt/metaengine.cpp:38
 msgid ""
 "This option allows the game to play at a slightly extended resolution. This "
 "is what the Mac and CDi versions are supposed to play like."
diff --git a/po/nl_NL.po b/po/nl_NL.po
index 0ef2ea79e66..36c193559a4 100644
--- a/po/nl_NL.po
+++ b/po/nl_NL.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.9.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-07 21:08+0000\n"
+"POT-Creation-Date: 2026-06-07 22:09+0000\n"
 "PO-Revision-Date: 2026-02-11 14:23+0000\n"
 "Last-Translator: SecularSteve <fairfull.playing at gmail.com>\n"
 "Language-Team: Dutch <https://translations.scummvm.org/projects/scummvm/"
@@ -7740,13 +7740,13 @@ msgstr "Aanwijzingen database"
 msgid "Quit game"
 msgstr "Spel verlaten"
 
-#: engines/bolt/metaengine.cpp:34
+#: engines/bolt/metaengine.cpp:37
 #, fuzzy
 #| msgid "Enable high resolution"
 msgid "Extended game resolution"
 msgstr "Gebruik hoge resolutie"
 
-#: engines/bolt/metaengine.cpp:35
+#: engines/bolt/metaengine.cpp:38
 msgid ""
 "This option allows the game to play at a slightly extended resolution. This "
 "is what the Mac and CDi versions are supposed to play like."
diff --git a/po/nn_NO.po b/po/nn_NO.po
index ad1e0f5c544..0acdf4d0969 100644
--- a/po/nn_NO.po
+++ b/po/nn_NO.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-07 21:08+0000\n"
+"POT-Creation-Date: 2026-06-07 22:09+0000\n"
 "PO-Revision-Date: 2023-12-14 14:28+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Norwegian Nynorsk <https://translations.scummvm.org/projects/"
@@ -7487,12 +7487,12 @@ msgstr ""
 msgid "Quit game"
 msgstr " (Spel)"
 
-#: engines/bolt/metaengine.cpp:34
+#: engines/bolt/metaengine.cpp:37
 #, fuzzy
 msgid "Extended game resolution"
 msgstr "Nytt h??goppl??yseleg grafikk"
 
-#: engines/bolt/metaengine.cpp:35
+#: engines/bolt/metaengine.cpp:38
 msgid ""
 "This option allows the game to play at a slightly extended resolution. This "
 "is what the Mac and CDi versions are supposed to play like."
diff --git a/po/pl_PL.po b/po/pl_PL.po
index cb5b125f96a..49144fdfa45 100644
--- a/po/pl_PL.po
+++ b/po/pl_PL.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-07 21:08+0000\n"
+"POT-Creation-Date: 2026-06-07 22:09+0000\n"
 "PO-Revision-Date: 2026-04-17 00:22+0000\n"
 "Last-Translator: SkiffPL <skiff at windowslive.com>\n"
 "Language-Team: Polish <https://translations.scummvm.org/projects/scummvm/"
@@ -7754,11 +7754,11 @@ msgstr "Baza danych wskaz??wek"
 msgid "Quit game"
 msgstr "Wyjd?? z gry"
 
-#: engines/bolt/metaengine.cpp:34
+#: engines/bolt/metaengine.cpp:37
 msgid "Extended game resolution"
 msgstr "Zwi??kszona rozdzielczo???? gry"
 
-#: engines/bolt/metaengine.cpp:35
+#: engines/bolt/metaengine.cpp:38
 msgid ""
 "This option allows the game to play at a slightly extended resolution. This "
 "is what the Mac and CDi versions are supposed to play like."
diff --git a/po/pt_BR.po b/po/pt_BR.po
index 13e3d5e665b..f66c0828273 100644
--- a/po/pt_BR.po
+++ b/po/pt_BR.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-07 21:08+0000\n"
+"POT-Creation-Date: 2026-06-07 22:09+0000\n"
 "PO-Revision-Date: 2024-11-16 02:13+0000\n"
 "Last-Translator: Marcel Souza Lemes <marcosoutsider at gmail.com>\n"
 "Language-Team: Portuguese (Brazil) <https://translations.scummvm.org/"
@@ -8085,13 +8085,13 @@ msgstr "Base de Dados de Pistas"
 msgid "Quit game"
 msgstr "Sair do jogo"
 
-#: engines/bolt/metaengine.cpp:34
+#: engines/bolt/metaengine.cpp:37
 #, fuzzy
 #| msgid "Enable high resolution"
 msgid "Extended game resolution"
 msgstr "Habilitar alta resolu????o"
 
-#: engines/bolt/metaengine.cpp:35
+#: engines/bolt/metaengine.cpp:38
 msgid ""
 "This option allows the game to play at a slightly extended resolution. This "
 "is what the Mac and CDi versions are supposed to play like."
diff --git a/po/pt_PT.po b/po/pt_PT.po
index 4f34eb04ae2..f7a8ceacf8c 100644
--- a/po/pt_PT.po
+++ b/po/pt_PT.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.10.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-07 21:08+0000\n"
+"POT-Creation-Date: 2026-06-07 22:09+0000\n"
 "PO-Revision-Date: 2022-06-05 14:09+0000\n"
 "Last-Translator: Daniel <supsuper at gmail.com>\n"
 "Language-Team: Portuguese (Portugal) <https://translations.scummvm.org/"
@@ -7517,13 +7517,13 @@ msgstr ""
 msgid "Quit game"
 msgstr "Sair do Jogo"
 
-#: engines/bolt/metaengine.cpp:34
+#: engines/bolt/metaengine.cpp:37
 #, fuzzy
 #| msgid "Enable high resolution"
 msgid "Extended game resolution"
 msgstr "Ativar alta resolu????o"
 
-#: engines/bolt/metaengine.cpp:35
+#: engines/bolt/metaengine.cpp:38
 msgid ""
 "This option allows the game to play at a slightly extended resolution. This "
 "is what the Mac and CDi versions are supposed to play like."
diff --git a/po/ro.po b/po/ro.po
index b074276637b..5d1d4e3cd07 100644
--- a/po/ro.po
+++ b/po/ro.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.10.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-07 21:08+0000\n"
+"POT-Creation-Date: 2026-06-07 22:09+0000\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: Automatically generated\n"
 "Language-Team: none\n"
@@ -6983,11 +6983,11 @@ msgstr ""
 msgid "Quit game"
 msgstr ""
 
-#: engines/bolt/metaengine.cpp:34
+#: engines/bolt/metaengine.cpp:37
 msgid "Extended game resolution"
 msgstr ""
 
-#: engines/bolt/metaengine.cpp:35
+#: engines/bolt/metaengine.cpp:38
 msgid ""
 "This option allows the game to play at a slightly extended resolution. This "
 "is what the Mac and CDi versions are supposed to play like."
diff --git a/po/ru_RU.po b/po/ru_RU.po
index 399e20a1fe1..21bf46ad328 100644
--- a/po/ru_RU.po
+++ b/po/ru_RU.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.8.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-07 21:08+0000\n"
+"POT-Creation-Date: 2026-06-07 22:09+0000\n"
 "PO-Revision-Date: 2026-05-06 08:54+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Russian <https://translations.scummvm.org/projects/scummvm/"
@@ -7754,11 +7754,11 @@ msgstr "???????? ????????"
 msgid "Quit game"
 msgstr "?????????? ???? ????????"
 
-#: engines/bolt/metaengine.cpp:34
+#: engines/bolt/metaengine.cpp:37
 msgid "Extended game resolution"
 msgstr "?????????????????????? ???????????????????? ????????"
 
-#: engines/bolt/metaengine.cpp:35
+#: engines/bolt/metaengine.cpp:38
 msgid ""
 "This option allows the game to play at a slightly extended resolution. This "
 "is what the Mac and CDi versions are supposed to play like."
diff --git a/po/sv_SE.po b/po/sv_SE.po
index efad31c10fa..ef5beab3f7e 100644
--- a/po/sv_SE.po
+++ b/po/sv_SE.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.5.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-07 21:08+0000\n"
+"POT-Creation-Date: 2026-06-07 22:09+0000\n"
 "PO-Revision-Date: 2026-04-19 13:22+0000\n"
 "Last-Translator: Daniel Nylander <po at danielnylander.se>\n"
 "Language-Team: Swedish <https://translations.scummvm.org/projects/scummvm/"
@@ -7729,13 +7729,13 @@ msgstr "Databas f??r ledtr??dar"
 msgid "Quit game"
 msgstr "Avsluta spel"
 
-#: engines/bolt/metaengine.cpp:34
+#: engines/bolt/metaengine.cpp:37
 #, fuzzy
 #| msgid "Enable high resolution"
 msgid "Extended game resolution"
 msgstr "Aktivera h??g uppl??sning"
 
-#: engines/bolt/metaengine.cpp:35
+#: engines/bolt/metaengine.cpp:38
 msgid ""
 "This option allows the game to play at a slightly extended resolution. This "
 "is what the Mac and CDi versions are supposed to play like."
diff --git a/po/tr.po b/po/tr.po
index 2730a1f1239..c743e42b1d9 100644
--- a/po/tr.po
+++ b/po/tr.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-07 21:08+0000\n"
+"POT-Creation-Date: 2026-06-07 22:09+0000\n"
 "PO-Revision-Date: 2024-10-10 12:33+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Turkish <https://translations.scummvm.org/projects/scummvm/"
@@ -7390,13 +7390,13 @@ msgstr ""
 msgid "Quit game"
 msgstr "Oyun"
 
-#: engines/bolt/metaengine.cpp:34
+#: engines/bolt/metaengine.cpp:37
 #, fuzzy
 #| msgid "Create a new saved game"
 msgid "Extended game resolution"
 msgstr "Yeni oyun kayd?? yaratma"
 
-#: engines/bolt/metaengine.cpp:35
+#: engines/bolt/metaengine.cpp:38
 msgid ""
 "This option allows the game to play at a slightly extended resolution. This "
 "is what the Mac and CDi versions are supposed to play like."
diff --git a/po/uk_UA.po b/po/uk_UA.po
index cec453858f0..f2ef829268a 100644
--- a/po/uk_UA.po
+++ b/po/uk_UA.po
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.9.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-07 21:08+0000\n"
+"POT-Creation-Date: 2026-06-07 22:09+0000\n"
 "PO-Revision-Date: 2026-05-06 08:54+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Ukrainian <https://translations.scummvm.org/projects/scummvm/"
@@ -7734,11 +7734,11 @@ msgstr "???????? ?????????? ??????????????"
 msgid "Quit game"
 msgstr "?????????? ?? ??????"
 
-#: engines/bolt/metaengine.cpp:34
+#: engines/bolt/metaengine.cpp:37
 msgid "Extended game resolution"
 msgstr "?????????????????? ???????????????????????? ??????"
 
-#: engines/bolt/metaengine.cpp:35
+#: engines/bolt/metaengine.cpp:38
 msgid ""
 "This option allows the game to play at a slightly extended resolution. This "
 "is what the Mac and CDi versions are supposed to play like."
diff --git a/po/zh.po b/po/zh.po
index 398f22256cc..1db9ae320c4 100644
--- a/po/zh.po
+++ b/po/zh.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-07 21:08+0000\n"
+"POT-Creation-Date: 2026-06-07 22:09+0000\n"
 "PO-Revision-Date: 2024-05-14 12:42+0000\n"
 "Last-Translator: ?????? <clonewith at qq.com>\n"
 "Language-Team: Chinese <https://translations.scummvm.org/projects/scummvm/"
@@ -7150,13 +7150,13 @@ msgstr ""
 msgid "Quit game"
 msgstr "????????????"
 
-#: engines/bolt/metaengine.cpp:34
+#: engines/bolt/metaengine.cpp:37
 #, fuzzy
 #| msgid "Change game options"
 msgid "Extended game resolution"
 msgstr "??????????????????"
 
-#: engines/bolt/metaengine.cpp:35
+#: engines/bolt/metaengine.cpp:38
 msgid ""
 "This option allows the game to play at a slightly extended resolution. This "
 "is what the Mac and CDi versions are supposed to play like."
diff --git a/po/zh_Hans.po b/po/zh_Hans.po
index 00dd032d587..20216c501a3 100644
--- a/po/zh_Hans.po
+++ b/po/zh_Hans.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.9.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-07 21:08+0000\n"
+"POT-Creation-Date: 2026-06-07 22:09+0000\n"
 "PO-Revision-Date: 2024-12-01 22:18+0000\n"
 "Last-Translator: ?????? <clonewith at qq.com>\n"
 "Language-Team: Chinese (Simplified Han script) <https://"
@@ -7833,13 +7833,13 @@ msgstr "???????????????"
 msgid "Quit game"
 msgstr "????????????"
 
-#: engines/bolt/metaengine.cpp:34
+#: engines/bolt/metaengine.cpp:37
 #, fuzzy
 #| msgid "Enable high resolution"
 msgid "Extended game resolution"
 msgstr "??????????????????"
 
-#: engines/bolt/metaengine.cpp:35
+#: engines/bolt/metaengine.cpp:38
 msgid ""
 "This option allows the game to play at a slightly extended resolution. This "
 "is what the Mac and CDi versions are supposed to play like."
diff --git a/po/zh_Hant.po b/po/zh_Hant.po
index 43820ce8fa0..010a102e306 100644
--- a/po/zh_Hant.po
+++ b/po/zh_Hant.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.9.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-07 21:08+0000\n"
+"POT-Creation-Date: 2026-06-07 22:09+0000\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: Automatically generated\n"
 "Language-Team: none\n"
@@ -6984,11 +6984,11 @@ msgstr ""
 msgid "Quit game"
 msgstr ""
 
-#: engines/bolt/metaengine.cpp:34
+#: engines/bolt/metaengine.cpp:37
 msgid "Extended game resolution"
 msgstr ""
 
-#: engines/bolt/metaengine.cpp:35
+#: engines/bolt/metaengine.cpp:38
 msgid ""
 "This option allows the game to play at a slightly extended resolution. This "
 "is what the Mac and CDi versions are supposed to play like."



From noreply at scummvm.org  Mon Jun  8 00:47:52 2026
From: noreply at scummvm.org (dreammaster)
Date: Mon,  8 Jun 2026 00:47:52 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 0b5dd4d5820fc4ab298d0f60ac3b3e8fb32f6681
Message-ID: <20260608004752.C473B7005CC@ron.scummvm.net>

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:
02a6b031d4 MADS: FOREST: Section rooms done cleanup
ac089399f2 MADS: FOREST: Implemented room 210
0b5dd4d582 MADS: FOREST: Implemented rooms 211 and 220


Commit: 02a6b031d46b90b58221f5acc3f57f2fe40691a6
    https://github.com/scummvm/scummvm/commit/02a6b031d46b90b58221f5acc3f57f2fe40691a6
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-06-08T09:10:48+10:00

Commit Message:
MADS: FOREST: Section rooms done cleanup

Changed paths:
    engines/mads/madsv2/forest/rooms/room201.cpp
    engines/mads/madsv2/forest/rooms/room203.cpp
    engines/mads/madsv2/forest/rooms/room204.cpp
    engines/mads/madsv2/forest/rooms/room205.cpp


diff --git a/engines/mads/madsv2/forest/rooms/room201.cpp b/engines/mads/madsv2/forest/rooms/room201.cpp
index fd9efce246f..31ab0cd044c 100644
--- a/engines/mads/madsv2/forest/rooms/room201.cpp
+++ b/engines/mads/madsv2/forest/rooms/room201.cpp
@@ -36,29 +36,29 @@ namespace Forest {
 namespace Rooms {
 
 struct Scratch {
-	int16 sprite[10];                /* 0x00 ??? sprite series handles  */
-	int16 sequence[10];              /* 0x14 ??? sequence handles       */
-	int16 animation[10];             /* 0x28 ??? animation handles      */
-	AnimationInfo animation_info[10];/* 0x3C */
-	int16 _8c;                       /* 0x8C */
-	int16 _8e;                       /* 0x8E */
-	int16 _pad90;                    /* 0x90 */
-	int16 _92;                       /* 0x92 */
-	int16 _94;                       /* 0x94 ??? animation slot index for 'R' anim */
-	int16 _96;                       /* 0x96 ??? animation slot index for 'F' anim */
-	int16 _98;                       /* 0x98 */
-	int16 _9a;                       /* 0x9A ??? animation handle */
-	int16 _9c;                       /* 0x9C ??? animation handle */
-	int16 _pad9e;                    /* 0x9E */
-	int16 _a0;                       /* 0xA0 ??? talk animation handle */
-	int16 _a2;                       /* 0xA2 ??? animation handle for random-frame walk */
-	int16 _a4;                       /* 0xA4 */
-	int16 _a6;                       /* 0xA6 */
-	int16 _a8;                       /* 0xA8 */
-	int16 _aa;                       /* 0xAA ??? cached frame for anim5 */
-	int16 _ac;                       /* 0xAC */
-	int16 _ae;                       /* 0xAE ??? cached frame for anim6 */
-	int16 _b0;                       /* 0xB0 */
+	int16 sprite[10];
+	int16 sequence[10];
+	int16 animation[10];
+	AnimationInfo animation_info[10];
+	int16 _8c;
+	int16 _8e;
+	int16 _90;
+	int16 _92;
+	int16 _94;
+	int16 _96;
+	int16 _98;
+	int16 _9a;
+	int16 _9c;
+	int16 _9e;
+	int16 _a0;
+	int16 _a2;
+	int16 _a4;
+	int16 _a6;
+	int16 _a8;
+	int16 _aa;
+	int16 _ac;
+	int16 _ae;
+	int16 _b0;
 };
 
 static Scratch scratch;
@@ -151,6 +151,32 @@ static void room_201_init1() {
 	}
 }
 
+static void room_201_init() {
+	scratch._a6 = 0;
+	scratch._a4 = -1;
+
+	if (previous_room != KERNEL_RESTORING_GAME && previous_room != 199) {
+		player.walker_visible = 0;
+		player.commands_allowed = 0;
+	}
+
+	scratch._ac = 0;
+	scratch._b0 = 0;
+	for (int i = 0; i < 10; i++) {
+		aainfo[i]._active = 0;
+		aainfo[i]._frame = 1;
+		aainfo[i]._val3 = 0;
+		aainfo[i]._val4 = 0;
+	}
+
+	if (previous_room != 199 && flags[7] != 3)
+		flags[7]++;
+
+	if (flags[7] == -3)
+		flags[7] = 1;
+	room_201_init1();
+}
+
 static void room_201_anim1() {
 	if (kernel_anim[aa[0]].frame != aainfo[0]._frame)
 		aainfo[0]._frame = kernel_anim[aa[0]].frame;
@@ -339,32 +365,6 @@ static void room_201_anim6() {
 	}
 }
 
-static void room_201_init() {
-	scratch._a6 = 0;
-	scratch._a4 = -1;
-
-	if (previous_room != KERNEL_RESTORING_GAME && previous_room != 199) {
-		player.walker_visible = 0;
-		player.commands_allowed = 0;
-	}
-
-	scratch._ac = 0;
-	scratch._b0 = 0;
-	for (int i = 0; i < 10; i++) {
-		aainfo[i]._active = 0;
-		aainfo[i]._frame = 1;
-		aainfo[i]._val3 = 0;
-		aainfo[i]._val4 = 0;
-	}
-
-	if (previous_room != 199 && flags[7] != 3)
-		flags[7]++;
-
-	if (flags[7] == -3)
-		flags[7] = 1;
-	room_201_init1();
-}
-
 static void room_201_daemon() {
 	int16 trigger = kernel.trigger;
 
@@ -664,14 +664,14 @@ void room_201_synchronize(Common::Serializer &s) {
 	for (AnimationInfo &ai : scratch.animation_info) ai.synchronize(s);
 	s.syncAsSint16LE(scratch._8c);
 	s.syncAsSint16LE(scratch._8e);
-	s.syncAsSint16LE(scratch._pad90);
+	s.syncAsSint16LE(scratch._90);
 	s.syncAsSint16LE(scratch._92);
 	s.syncAsSint16LE(scratch._94);
 	s.syncAsSint16LE(scratch._96);
 	s.syncAsSint16LE(scratch._98);
 	s.syncAsSint16LE(scratch._9a);
 	s.syncAsSint16LE(scratch._9c);
-	s.syncAsSint16LE(scratch._pad9e);
+	s.syncAsSint16LE(scratch._9e);
 	s.syncAsSint16LE(scratch._a0);
 	s.syncAsSint16LE(scratch._a2);
 	s.syncAsSint16LE(scratch._a4);
diff --git a/engines/mads/madsv2/forest/rooms/room203.cpp b/engines/mads/madsv2/forest/rooms/room203.cpp
index 5b5d2f67ddd..72638c88bd0 100644
--- a/engines/mads/madsv2/forest/rooms/room203.cpp
+++ b/engines/mads/madsv2/forest/rooms/room203.cpp
@@ -39,31 +39,31 @@ namespace Forest {
 namespace Rooms {
 
 struct Scratch {
-	int16 sprite[10];                /* 0x00 ??? sprite series handles  */
-	int16 sequence[10];              /* 0x14 ??? sequence handles       */
-	int16 animation[10];             /* 0x28 ??? animation handles      */
-	AnimationInfo animation_info[10];/* 0x3C */
-	int16 _8c;                       /* 0x8C */
-	int16 _8e;                       /* 0x8E */
-	int16 _90;                       /* 0x90 */
-	int16 _92;                       /* 0x92 */
-	int16 _94;                       /* 0x94 */
-	int16 _96;                       /* 0x96 */
-	int16 _98;                       /* 0x98 */
-	int16 _9a;                       /* 0x9A */
-	int16 _9c;                       /* 0x9C */
-	int16 _9e;                       /* 0x9E ??? animation handle */
-	int16 _a0;                       /* 0xA0 ??? animation handle */
-	int16 _a2;                       /* 0xA2 ??? talk animation handle */
-	int16 _a4;                       /* 0xA4 */
-	int16 _a6;                       /* 0xA6 */
-	int16 _a8;                       /* 0xA8 */
-	int16 _aa;                      /* 0xAA */
-	int16 _ac;                       /* 0xAC */
-	int16 _ae;                       /* 0xAE */
-	int16 _b0;                       /* 0xB0 */
-	int16 _b2;                       /* 0xB2 */
-	int16 _b4;                       /* 0xB4 */
+	int16 sprite[10];
+	int16 sequence[10];
+	int16 animation[10];
+	AnimationInfo animation_info[10];
+	int16 _8c;
+	int16 _8e;
+	int16 _90;
+	int16 _92;
+	int16 _94;
+	int16 _96;
+	int16 _98;
+	int16 _9a;
+	int16 _9c;
+	int16 _9e;
+	int16 _a0;
+	int16 _a2;
+	int16 _a4;
+	int16 _a6;
+	int16 _a8;
+	int16 _aa;
+	int16 _ac;
+	int16 _ae;
+	int16 _b0;
+	int16 _b2;
+	int16 _b4;
 };
 
 static Scratch scratch;
diff --git a/engines/mads/madsv2/forest/rooms/room204.cpp b/engines/mads/madsv2/forest/rooms/room204.cpp
index 4567ab9eddb..11c9f3dc451 100644
--- a/engines/mads/madsv2/forest/rooms/room204.cpp
+++ b/engines/mads/madsv2/forest/rooms/room204.cpp
@@ -38,25 +38,25 @@ namespace Forest {
 namespace Rooms {
 
 struct Scratch {
-	int16 sprite[10];                /* Sprite series handles  0x00 */
-	int16 sequence[10];              /* Sequence handles       0x14 */
-	int16 animation[10];             /* Animation handles      0x28 */
-	AnimationInfo animation_info[10];/* Animation info         0x3C */
+	int16 sprite[10];
+	int16 sequence[10];
+	int16 animation[10];
+	AnimationInfo animation_info[10];
 	int16 _8c;
 	int16 _8e;
 	int16 _90;
-	int16 _92;                       /*                        0x92 */
-	int16 _94;                       /*                        0x94 */
-	int16 _96;                       /*                        0x96 */
-	int16 _98;                       /*                        0x98 */
-	int16 _9a;                       /*                        0x9A */
-	int16 _9c;                       /*                        0x9C */
+	int16 _92;
+	int16 _94;
+	int16 _96;
+	int16 _98;
+	int16 _9a;
+	int16 _9c;
 	int16 _9e;
 	int16 _a0;
 	int16 _a2;
 	int16 _a4;
-	int16 _a6;                       /*                        0xA6 */
-	int16 _a8;                       /*                        0xA8 */
+	int16 _a6;
+	int16 _a8;
 };
 
 static Scratch scratch;
@@ -67,51 +67,6 @@ static Scratch scratch;
 #define aa     local->animation
 #define aainfo scratch.animation_info
 
-static void room_204_init1();
-static void room_204_anim1();
-static void room_204_anim2();
-static void room_204_anim3();
-static void room_204_anim4();
-static void room_204_anim5();
-static void room_204_anim6();
-
-static void room_204_init() {
-	global[player_score] = -1;
-	midi_stop();
-	scratch._a8 = 0;
-	scratch._a6 = -1;
-
-	if (object_is_here(9)) {
-		ss[0] = kernel_load_series(kernel_name('p', 1), 0);
-		seq[0] = kernel_seq_stamp(ss[0], false, KERNEL_FIRST);
-		kernel_seq_depth(seq[0], 7);
-		kernel_seq_loc(seq[0], 293, 87);
-		kernel_seq_scale(seq[0], 54);
-	} else {
-		kernel_flip_hotspot(158, -1);
-	}
-
-	if (previous_room != KERNEL_RESTORING_GAME) {
-		if (previous_room != 199) {
-			player.walker_visible = 0;
-			player.commands_allowed = 0;
-		}
-
-		for (int count = 0; count < 10; count++) {
-			aainfo[count]._active = 0;
-			aainfo[count]._frame = 1;
-			aainfo[count]._val3 = 0;
-			aainfo[count]._val4 = 0;
-		}
-
-		if (previous_room != 199 && flags[18] != 3)
-			flags[18]++;
-	}
-
-	room_204_init1();
-	if (flags[18] == -3)
-		flags[18] = 1;
-}
 
 static void room_204_init1() {
 	global[g131] = 0;
@@ -171,196 +126,42 @@ static void room_204_init1() {
 	player.walker_visible = -1;
 }
 
-static void room_204_daemon() {
-	int trigger = kernel.trigger;
+static void room_204_init() {
+	global[player_score] = -1;
+	midi_stop();
+	scratch._a8 = 0;
+	scratch._a6 = -1;
 
-	if (trigger == 103) {
-		goto trigger_103;
-	} else if (trigger > 103) {
-		goto bottom;
-	} else if (trigger == 25) {
-		goto trigger_25;
-	} else if (trigger > 25) {
-		// triggers 26-102
-		if (trigger == 26) {
-			goto trigger_26;
-		} else if (trigger == 100) {
-			goto trigger_100;
-		} else if (trigger == 101) {
-			// abort _94 slot anim, re-enable
-			kernel_abort_animation(aa[scratch._94]);
-			aainfo[scratch._94]._active = 0;
-			kernel_reset_animation(scratch._9c, 1);
-			kernel_synch(3, scratch._9c, 4, 0);
-			global[g143] = 0;
-			player.commands_allowed = -1;
-			goto bottom;
-		} else if (trigger == 102) {
-			// abort _96 slot anim, re-enable
-			kernel_abort_animation(aa[scratch._96]);
-			aainfo[scratch._96]._active = 0;
-			kernel_reset_animation(scratch._9a, 1);
-			kernel_synch(3, scratch._9a, 4, 0);
-			global[g133] = 0;
-			player.commands_allowed = -1;
-			goto bottom;
-		} else {
-			goto bottom;
-		}
-	} else if (trigger == 0) {
-		goto bottom;
-	} else if (trigger == 7) {
-		goto trigger_7;
-	} else if (trigger == 24) {
-		goto trigger_24;
+	if (object_is_here(9)) {
+		ss[0] = kernel_load_series(kernel_name('p', 1), 0);
+		seq[0] = kernel_seq_stamp(ss[0], false, KERNEL_FIRST);
+		kernel_seq_depth(seq[0], 7);
+		kernel_seq_loc(seq[0], 293, 87);
+		kernel_seq_scale(seq[0], 54);
 	} else {
-		goto bottom;
+		kernel_flip_hotspot(158, -1);
 	}
 
-trigger_7:
-	if (global[walker_converse_state] != 0) {
-		global[walker_converse_state] = 0;
-		close_journal(3);
-		player.commands_allowed = -1;
-		goto bottom;
-	}
-	if (scratch._a4 != 400)
-		goto bottom;
-	kernel_abort_animation(scratch._9e);
-	scratch._a4 = -1;
-	scratch._a8 = 0;
-	kernel_synch(2, 0, 4, 0);
-	scratch._a6 = 3;
-	player.walker_visible = -1;
-	global[g150] = -1;
-	scratch._90 = 6;
-	goto bottom;
-
-trigger_24:
-	if (scratch._8c == 3) {
-		aa[2] = kernel_run_animation(kernel_name('E', 1), 102);
-		aainfo[2]._active = -1;
-		scratch._96 = 2;
-		kernel_reset_animation(scratch._9a, 0);
-		kernel_synch(3, aa[2], 3, scratch._9a);
-		global[g133] = 1;
-	} else if (scratch._8c == 5) {
-		if (scratch._a6 == 1) {
-			aa[4] = kernel_run_animation(kernel_name('F', 1), 102);
-		} else if (scratch._a6 == 2) {
-			aa[4] = kernel_run_animation(kernel_name('F', 2), 102);
-		} else {
-			goto bottom;
+	if (previous_room != KERNEL_RESTORING_GAME) {
+		if (previous_room != 199) {
+			player.walker_visible = 0;
+			player.commands_allowed = 0;
 		}
-		aainfo[4]._active = -1;
-		scratch._96 = 4;
-		kernel_reset_animation(scratch._9a, 0);
-		kernel_synch(3, aa[4], 3, scratch._9a);
-		global[g133] = 1;
-	}
-	goto bottom;
 
-trigger_25:
-	if (scratch._8e == 2) {
-		aa[3] = kernel_run_animation(kernel_name('R', 1), 101);
-		aainfo[3]._active = -1;
-		scratch._94 = 3;
-		kernel_reset_animation(scratch._9c, 0);
-		kernel_synch(3, aa[3], 3, scratch._9c);
-		global[g143] = 1;
-	}
-	goto bottom;
-
-trigger_26:
-	if (scratch._90 == 1) {
-		kernel_reset_animation(scratch._9a, 0);
-		kernel_reset_animation(scratch._9c, 0);
-		global[g133] = 1;
-		global[g143] = 1;
-	} else if (scratch._90 == 6) {
-		if (scratch._a6 != 3)
-			goto bottom;
-		aa[5] = kernel_run_animation(kernel_name('P', 1), 103);
-		aainfo[5]._active = -1;
-		scratch._98 = 5;
-		kernel_reset_animation(scratch._9a, 0);
-		kernel_reset_animation(scratch._9c, 0);
-		kernel_synch(3, aa[5], 4, 0);
-		global[g133] = 1;
-		global[g143] = 1;
-	}
-	goto bottom;
+		for (int count = 0; count < 10; count++) {
+			aainfo[count]._active = 0;
+			aainfo[count]._frame = 1;
+			aainfo[count]._val3 = 0;
+			aainfo[count]._val4 = 0;
+		}
 
-trigger_100:
-	kernel_abort_animation(aa[0]);
-	aainfo[0]._active = 0;
-	if (flags[18] == 1) {
-		kernel_synch(2, 0, 4, 0);
-		global[g017] = 0;
-		global[g154] = 0;
-		kernel_synch(3, scratch._9a, 4, 0);
-		global[g131] = -1;
-		global[g133] = 1;
-		global[g156] = 0;
-		kernel_synch(3, scratch._9c, 4, 0);
-		global[g143] = 1;
-		scratch._a4 = 1;
-		global[g141] = -1;
-		player.walker_visible = -1;
-		aainfo[1]._active = -1;
-		global[g155] = 2;
-		scratch._a0 = kernel_run_animation_talk('r', 2, 0);
-		kernel_position_anim(scratch._a0, 171, 116, 73, 8);
-		scratch._a2 = kernel_run_animation_talk('e', 3, 0);
-		kernel_position_anim(scratch._a2, 148, 120, 76, 7);
-		kernel_synch(3, scratch._a2, 3, scratch._9a);
-		kernel_synch(3, scratch._a0, 3, scratch._9c);
-		kernel_synch(3, scratch._9e, 2, 0);
-		digi_play_build(204, 114, 1, 1);
-	} else {
-		global[g131] = -1;
-		global[g141] = -1;
-		kernel_reset_animation(scratch._9a, 1);
-		kernel_reset_animation(scratch._9c, 1);
-		global[g133] = 0;
-		global[g143] = 0;
-		kernel_synch(3, scratch._9a, 4, 0);
-		kernel_synch(3, scratch._9c, 4, 0);
-		player.walker_visible = -1;
-		kernel_synch(2, 0, 4, 0);
-		player.commands_allowed = -1;
+		if (previous_room != 199 && flags[18] != 3)
+			flags[18]++;
 	}
-	goto bottom;
-
-trigger_103:
-	if (scratch._a6 != 3)
-		goto bottom;
-	kernel_abort_animation(aa[5]);
-	aainfo[5]._active = 0;
-	kernel_reset_animation(scratch._9c, 1);
-	kernel_synch(3, scratch._9c, 4, 0);
-	global[g143] = 0;
-	kernel_reset_animation(scratch._9a, 1);
-	kernel_synch(3, scratch._9a, 4, 0);
-	global[g133] = 0;
-	kernel_flip_hotspot(158, -1);
-	inter_move_object(9, 2);
-	global[player_score] = -1;
-	player.commands_allowed = -1;
-
-bottom:
-	global_anim1(3, scratch._9a, global[g131], &global[g132]);
-	global_anim2(2, scratch._9c, global[g141], &global[g142]);
-
-	if (scratch._a8 != 0)
-		kernel_random_frame(scratch._9e, &global[g151], global[g154]);
 
-	if (aainfo[0]._active != 0) room_204_anim1();
-	if (aainfo[1]._active != 0) room_204_anim2();
-	if (aainfo[3]._active != 0) room_204_anim3();
-	if (aainfo[2]._active != 0) room_204_anim4();
-	if (aainfo[4]._active != 0) room_204_anim5();
-	if (aainfo[5]._active != 0) room_204_anim6();
+	room_204_init1();
+	if (flags[18] == -3)
+		flags[18] = 1;
 }
 
 static void room_204_anim1() {
@@ -546,6 +347,198 @@ static void room_204_anim6() {
 	}
 }
 
+static void room_204_daemon() {
+	int trigger = kernel.trigger;
+
+	if (trigger == 103) {
+		goto trigger_103;
+	} else if (trigger > 103) {
+		goto bottom;
+	} else if (trigger == 25) {
+		goto trigger_25;
+	} else if (trigger > 25) {
+		// triggers 26-102
+		if (trigger == 26) {
+			goto trigger_26;
+		} else if (trigger == 100) {
+			goto trigger_100;
+		} else if (trigger == 101) {
+			// abort _94 slot anim, re-enable
+			kernel_abort_animation(aa[scratch._94]);
+			aainfo[scratch._94]._active = 0;
+			kernel_reset_animation(scratch._9c, 1);
+			kernel_synch(3, scratch._9c, 4, 0);
+			global[g143] = 0;
+			player.commands_allowed = -1;
+			goto bottom;
+		} else if (trigger == 102) {
+			// abort _96 slot anim, re-enable
+			kernel_abort_animation(aa[scratch._96]);
+			aainfo[scratch._96]._active = 0;
+			kernel_reset_animation(scratch._9a, 1);
+			kernel_synch(3, scratch._9a, 4, 0);
+			global[g133] = 0;
+			player.commands_allowed = -1;
+			goto bottom;
+		} else {
+			goto bottom;
+		}
+	} else if (trigger == 0) {
+		goto bottom;
+	} else if (trigger == 7) {
+		goto trigger_7;
+	} else if (trigger == 24) {
+		goto trigger_24;
+	} else {
+		goto bottom;
+	}
+
+trigger_7:
+	if (global[walker_converse_state] != 0) {
+		global[walker_converse_state] = 0;
+		close_journal(3);
+		player.commands_allowed = -1;
+		goto bottom;
+	}
+	if (scratch._a4 != 400)
+		goto bottom;
+	kernel_abort_animation(scratch._9e);
+	scratch._a4 = -1;
+	scratch._a8 = 0;
+	kernel_synch(2, 0, 4, 0);
+	scratch._a6 = 3;
+	player.walker_visible = -1;
+	global[g150] = -1;
+	scratch._90 = 6;
+	goto bottom;
+
+trigger_24:
+	if (scratch._8c == 3) {
+		aa[2] = kernel_run_animation(kernel_name('E', 1), 102);
+		aainfo[2]._active = -1;
+		scratch._96 = 2;
+		kernel_reset_animation(scratch._9a, 0);
+		kernel_synch(3, aa[2], 3, scratch._9a);
+		global[g133] = 1;
+	} else if (scratch._8c == 5) {
+		if (scratch._a6 == 1) {
+			aa[4] = kernel_run_animation(kernel_name('F', 1), 102);
+		} else if (scratch._a6 == 2) {
+			aa[4] = kernel_run_animation(kernel_name('F', 2), 102);
+		} else {
+			goto bottom;
+		}
+		aainfo[4]._active = -1;
+		scratch._96 = 4;
+		kernel_reset_animation(scratch._9a, 0);
+		kernel_synch(3, aa[4], 3, scratch._9a);
+		global[g133] = 1;
+	}
+	goto bottom;
+
+trigger_25:
+	if (scratch._8e == 2) {
+		aa[3] = kernel_run_animation(kernel_name('R', 1), 101);
+		aainfo[3]._active = -1;
+		scratch._94 = 3;
+		kernel_reset_animation(scratch._9c, 0);
+		kernel_synch(3, aa[3], 3, scratch._9c);
+		global[g143] = 1;
+	}
+	goto bottom;
+
+trigger_26:
+	if (scratch._90 == 1) {
+		kernel_reset_animation(scratch._9a, 0);
+		kernel_reset_animation(scratch._9c, 0);
+		global[g133] = 1;
+		global[g143] = 1;
+	} else if (scratch._90 == 6) {
+		if (scratch._a6 != 3)
+			goto bottom;
+		aa[5] = kernel_run_animation(kernel_name('P', 1), 103);
+		aainfo[5]._active = -1;
+		scratch._98 = 5;
+		kernel_reset_animation(scratch._9a, 0);
+		kernel_reset_animation(scratch._9c, 0);
+		kernel_synch(3, aa[5], 4, 0);
+		global[g133] = 1;
+		global[g143] = 1;
+	}
+	goto bottom;
+
+trigger_100:
+	kernel_abort_animation(aa[0]);
+	aainfo[0]._active = 0;
+	if (flags[18] == 1) {
+		kernel_synch(2, 0, 4, 0);
+		global[g017] = 0;
+		global[g154] = 0;
+		kernel_synch(3, scratch._9a, 4, 0);
+		global[g131] = -1;
+		global[g133] = 1;
+		global[g156] = 0;
+		kernel_synch(3, scratch._9c, 4, 0);
+		global[g143] = 1;
+		scratch._a4 = 1;
+		global[g141] = -1;
+		player.walker_visible = -1;
+		aainfo[1]._active = -1;
+		global[g155] = 2;
+		scratch._a0 = kernel_run_animation_talk('r', 2, 0);
+		kernel_position_anim(scratch._a0, 171, 116, 73, 8);
+		scratch._a2 = kernel_run_animation_talk('e', 3, 0);
+		kernel_position_anim(scratch._a2, 148, 120, 76, 7);
+		kernel_synch(3, scratch._a2, 3, scratch._9a);
+		kernel_synch(3, scratch._a0, 3, scratch._9c);
+		kernel_synch(3, scratch._9e, 2, 0);
+		digi_play_build(204, 114, 1, 1);
+	} else {
+		global[g131] = -1;
+		global[g141] = -1;
+		kernel_reset_animation(scratch._9a, 1);
+		kernel_reset_animation(scratch._9c, 1);
+		global[g133] = 0;
+		global[g143] = 0;
+		kernel_synch(3, scratch._9a, 4, 0);
+		kernel_synch(3, scratch._9c, 4, 0);
+		player.walker_visible = -1;
+		kernel_synch(2, 0, 4, 0);
+		player.commands_allowed = -1;
+	}
+	goto bottom;
+
+trigger_103:
+	if (scratch._a6 != 3)
+		goto bottom;
+	kernel_abort_animation(aa[5]);
+	aainfo[5]._active = 0;
+	kernel_reset_animation(scratch._9c, 1);
+	kernel_synch(3, scratch._9c, 4, 0);
+	global[g143] = 0;
+	kernel_reset_animation(scratch._9a, 1);
+	kernel_synch(3, scratch._9a, 4, 0);
+	global[g133] = 0;
+	kernel_flip_hotspot(158, -1);
+	inter_move_object(9, 2);
+	global[player_score] = -1;
+	player.commands_allowed = -1;
+
+bottom:
+	global_anim1(3, scratch._9a, global[g131], &global[g132]);
+	global_anim2(2, scratch._9c, global[g141], &global[g142]);
+
+	if (scratch._a8 != 0)
+		kernel_random_frame(scratch._9e, &global[g151], global[g154]);
+
+	if (aainfo[0]._active != 0) room_204_anim1();
+	if (aainfo[1]._active != 0) room_204_anim2();
+	if (aainfo[3]._active != 0) room_204_anim3();
+	if (aainfo[2]._active != 0) room_204_anim4();
+	if (aainfo[4]._active != 0) room_204_anim5();
+	if (aainfo[5]._active != 0) room_204_anim6();
+}
+
 static void room_204_pre_parser() {
 	if (player_parse(13, 23, 0))
 		player.walk_off_edge_to_room = 203;
diff --git a/engines/mads/madsv2/forest/rooms/room205.cpp b/engines/mads/madsv2/forest/rooms/room205.cpp
index 3bc219ec5e0..a6f427c53f2 100644
--- a/engines/mads/madsv2/forest/rooms/room205.cpp
+++ b/engines/mads/madsv2/forest/rooms/room205.cpp
@@ -40,23 +40,23 @@ namespace Forest {
 namespace Rooms {
 
 struct Scratch {
-	int16 sprite[10];                /* Sprite series handles  0x00 */
-	int16 sequence[10];              /* Sequence handles       0x14 */
-	int16 animation[10];             /* Animation handles      0x28 */
-	AnimationInfo animation_info[10];/* Animation info         0x3C */
-	int16 _8c;                       /*                        0x8C */
-	int16 _8e;                       /*                        0x8E */
-	int16 _90;                       /*                        0x90 */
-	int16 _92;                       /*                        0x92 */
+	int16 sprite[10];
+	int16 sequence[10];
+	int16 animation[10];
+	AnimationInfo animation_info[10];
+	int16 _8c;
+	int16 _8e;
+	int16 _90;
+	int16 _92;
 	int16 _94;
 	int16 _96;
 	int16 _98;
-	int16 _9a;                       /*                        0x9A */
-	int16 _9c;                       /*                        0x9C */
-	int16 _9e;                       /*                        0x9E */
-	int16 _a0;                       /*                        0xA0 */
-	int16 _a2;                       /*                        0xA2 */
-	int16 _a4;                       /*                        0xA4 */
+	int16 _9a;
+	int16 _9c;
+	int16 _9e;
+	int16 _a0;
+	int16 _a2;
+	int16 _a4;
 };
 
 static Scratch scratch;


Commit: ac089399f23c242a113f52729c2b029657c5d2fc
    https://github.com/scummvm/scummvm/commit/ac089399f23c242a113f52729c2b029657c5d2fc
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-06-08T10:28:56+10:00

Commit Message:
MADS: FOREST: Implemented room 210

Changed paths:
    engines/mads/madsv2/core/digi.cpp
    engines/mads/madsv2/core/digi.h
    engines/mads/madsv2/forest/rooms/room101.cpp
    engines/mads/madsv2/forest/rooms/room103.cpp
    engines/mads/madsv2/forest/rooms/room106.cpp
    engines/mads/madsv2/forest/rooms/room210.cpp


diff --git a/engines/mads/madsv2/core/digi.cpp b/engines/mads/madsv2/core/digi.cpp
index a5ede99202d..5d08cae44b4 100644
--- a/engines/mads/madsv2/core/digi.cpp
+++ b/engines/mads/madsv2/core/digi.cpp
@@ -25,11 +25,14 @@
 namespace MADS {
 namespace MADSV2 {
 
-bool digi_flag1;
 int digi_val1;
+int digi_val2;
+int digi_timing_index;
 
 void digi_install() {
-	digi_flag1 = false;
+	digi_val1 = 0;
+	digi_val2 = 0;
+	digi_timing_index = 0;
 }
 
 void digi_uninstall() {
diff --git a/engines/mads/madsv2/core/digi.h b/engines/mads/madsv2/core/digi.h
index 8e11848d5ef..db4eee253ae 100644
--- a/engines/mads/madsv2/core/digi.h
+++ b/engines/mads/madsv2/core/digi.h
@@ -27,8 +27,9 @@
 namespace MADS {
 namespace MADSV2 {
 
-extern bool digi_flag1;
 extern int digi_val1;
+extern int digi_val2;
+extern int digi_timing_index;
 
 extern void digi_install();
 extern void digi_play(const char *name, int slot);
diff --git a/engines/mads/madsv2/forest/rooms/room101.cpp b/engines/mads/madsv2/forest/rooms/room101.cpp
index 08ff07b5afc..b8d0b5abb75 100644
--- a/engines/mads/madsv2/forest/rooms/room101.cpp
+++ b/engines/mads/madsv2/forest/rooms/room101.cpp
@@ -984,7 +984,7 @@ static void room_101_daemon() {
 	case 113:
 		digi_initial_volume(90);
 		digi_play_build(101, '_', 5, 3);
-		digi_flag1 = true;
+		digi_val2 = -1;
 		break;
 
 	}
diff --git a/engines/mads/madsv2/forest/rooms/room103.cpp b/engines/mads/madsv2/forest/rooms/room103.cpp
index 740ce94eb2a..999ad6a4f85 100644
--- a/engines/mads/madsv2/forest/rooms/room103.cpp
+++ b/engines/mads/madsv2/forest/rooms/room103.cpp
@@ -97,7 +97,7 @@ static void room_103_anim1() {
 			digi_initial_volume(20);
 			digi_play_build(101, '_', 5, 3);
 			scratch._92 = 1;
-			digi_flag1 = true;
+			digi_val2 = -1;
 		} else if (frame == 273) {
 			kernel_abort_animation(aa[0]);
 			aainfo[0]._active = 0;
@@ -113,7 +113,7 @@ static void room_103_anim1() {
 			digi_initial_volume(20);
 			scratch._92 = 1;
 			digi_play_build(101, '_', 5, 3);
-			digi_flag1 = true;
+			digi_val2 = -1;
 		}
 	}
 }
@@ -124,7 +124,7 @@ static void room_103_anim2() {
 		aainfo[1]._frame = frame;
 
 		if (frame == 165) {
-			digi_flag1 = false;
+			digi_val2 = 0;
 		} else if (frame > 165) {
 			if (frame == 186) {
 				aainfo[1]._val3 = 11;
@@ -150,12 +150,12 @@ static void room_103_anim2() {
 				digi_initial_volume(50);
 				digi_play_build(101, '_', 5, 3);
 				scratch._92 = 1;
-				digi_flag1 = true;
+				digi_val2 = -1;
 			} else if (frame == 160) {
 				digi_initial_volume(60);
 				digi_play_build(103, '_', 1, 1);
 				scratch._90 = 160;
-				digi_flag1 = true;
+				digi_val2 = -1;
 			}
 		}
 	}
@@ -176,7 +176,7 @@ static void room_103_anim2() {
 			digi_initial_volume(90);
 			scratch._92++;
 			digi_play_build(101, '_', 5, 3);
-			digi_flag1 = true;
+			digi_val2 = -1;
 		}
 	}
 }
diff --git a/engines/mads/madsv2/forest/rooms/room106.cpp b/engines/mads/madsv2/forest/rooms/room106.cpp
index ae2f2a7019e..9f6a92ade9e 100644
--- a/engines/mads/madsv2/forest/rooms/room106.cpp
+++ b/engines/mads/madsv2/forest/rooms/room106.cpp
@@ -668,7 +668,7 @@ static void room_106_daemon() {
 	case 109:
 		digi_initial_volume(50);
 		digi_play_build(101, '_', 5, 3);
-		digi_flag1 = true;
+		digi_val2 = -1;
 		break;
 	}
 
diff --git a/engines/mads/madsv2/forest/rooms/room210.cpp b/engines/mads/madsv2/forest/rooms/room210.cpp
index 95bc2a25e6a..f1ec570a629 100644
--- a/engines/mads/madsv2/forest/rooms/room210.cpp
+++ b/engines/mads/madsv2/forest/rooms/room210.cpp
@@ -19,17 +19,18 @@
  *
  */
 
-#include "mads/madsv2/core/conv.h"
+#include "mads/madsv2/core/digi.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/imath.h"
-#include "mads/madsv2/core/inter.h"
 #include "mads/madsv2/core/kernel.h"
-#include "mads/madsv2/core/sound.h"
-#include "mads/madsv2/core/text.h"
+#include "mads/madsv2/core/midi.h"
+#include "mads/madsv2/core/player.h"
+#include "mads/madsv2/core/speech.h"
 #include "mads/madsv2/forest/mads/inventory.h"
 #include "mads/madsv2/forest/mads/sounds.h"
 #include "mads/madsv2/forest/mads/words.h"
 #include "mads/madsv2/forest/global.h"
+#include "mads/madsv2/forest/journal.h"
 #include "mads/madsv2/forest/rooms/section1.h"
 #include "mads/madsv2/forest/rooms/room210.h"
 
@@ -39,10 +40,26 @@ namespace Forest {
 namespace Rooms {
 
 struct Scratch {
-	int16 sprite[10];       /* Sprite series handles */
-	int16 sequence[10];     /* Sequence handles      */
-	int16 animation[10];     /* Animation handles     */
-	AnimationInfo animation_info[10];
+	int16 sprite[10];              // 0x00???0x13
+	int16 sequence[10];            // 0x14???0x27
+	int16 animation[10];           // 0x28???0x3B
+	AnimationInfo animation_info[10]; // 0x3C???0x8B
+	int16 _8c;
+	int16 _8e;
+	int16 _90;
+	int16 _92;
+	int16 _94;
+	int16 _96;
+	int16 _98;
+	int16 _9a;
+	int16 _9c;
+	int16 _9e;
+	int16 _a0;
+	int16 _a2;
+	int16 _a4;
+	int16 _a6;
+	int16 _a8;                     // 0xA8
+	int16 _aa;
 };
 
 static Scratch scratch;
@@ -54,27 +71,611 @@ static Scratch scratch;
 #define aainfo scratch.animation_info
 
 
+static void room_210_init1() {
+	global[g131] = 0;
+	global[g141] = 0;
+
+	scratch._a4 = kernel_run_animation_disp('r', 9, 0);
+	kernel_position_anim(scratch._a4, 147, 155, 80, 1);
+
+	scratch._a2 = kernel_run_animation_disp('e', 7, 0);
+	kernel_position_anim(scratch._a2, 95, 155, 80, 1);
+
+	if (previous_room != KERNEL_LAST) {
+		player.x = 120;
+		player.y = 144;
+		player.facing = 2;
+	}
+
+	if (previous_room != 199) {
+		player.walker_visible = 0;
+		player.commands_allowed = 0;
+	}
+
+	if (previous_room == 321)
+		previous_room = 303;
+	if (previous_room == 322)
+		previous_room = 307;
+
+	if (previous_room == global[g041]) {
+		aa[0] = kernel_run_animation(kernel_name('y', 1), 100);
+		aainfo[0]._active = -1;
+		aainfo[0]._frame = 0;
+		scratch._92 = 79;
+		return;
+	}
+
+	if (previous_room == global[g042]) {
+		aa[0] = kernel_run_animation(kernel_name('y', 2), 100);
+		aainfo[0]._active = -1;
+		aainfo[0]._frame = 0;
+		scratch._92 = 72;
+		return;
+	}
+
+	if (previous_room == global[g043]) {
+		aa[0] = kernel_run_animation(kernel_name('y', 3), 100);
+		aainfo[0]._active = -1;
+		aainfo[0]._frame = 0;
+		scratch._92 = 66;
+		return;
+	}
+
+	if (previous_room == global[g044]) {
+		aa[0] = kernel_run_animation(kernel_name('y', 4), 100);
+		aainfo[0]._active = -1;
+		aainfo[0]._frame = 0;
+		scratch._92 = 77;
+		return;
+	}
+
+	if (previous_room == global[g045]) {
+		aa[0] = kernel_run_animation(kernel_name('y', 5), 100);
+		scratch._92 = 88;
+		aainfo[0]._active = -1;
+		aainfo[0]._frame = 0;
+		return;
+	}
+
+	global[g131] = -1;
+	global[g141] = -1;
+	kernel_reset_animation(scratch._a2, 2);
+	kernel_reset_animation(scratch._a4, 2);
+	global[g133] = 0;
+	global[g143] = 0;
+
+	if (previous_room == 199)
+		restore_player();
+
+	player.commands_allowed = -1;
+	player.walker_visible = -1;
+}
+
 static void room_210_init() {
+	midi_stop();
+	scratch._a8 = -1;
+	global[player_score] = 0;
+	// TODO: identify word_7130E ??? used as an index to select which 16-bit half of
+	// global.walker_timing to zero: global[walker_timing + word_7130E] = 0
+	global[walker_timing] = 0;
+
+	for (int i = 0; i < 10; i++) {
+		aainfo[i]._active = 0;
+		aainfo[i]._frame = 0;
+		aainfo[i]._val3 = 0;
+		aainfo[i]._val4 = 0;
+	}
+
+	aa[2] = kernel_run_animation(kernel_name('t', 1), 102);
+	aa[3] = kernel_run_animation(kernel_name('t', 2), 102);
+	aa[4] = kernel_run_animation(kernel_name('t', 3), 102);
+	aa[5] = kernel_run_animation(kernel_name('t', 4), 102);
+	aa[6] = kernel_run_animation(kernel_name('t', 5), 102);
+
+	kernel_timing_trigger(imath_random(10, 300), 103);
+	kernel_timing_trigger(imath_random(10, 300), 104);
+	kernel_timing_trigger(imath_random(10, 300), 105);
+	kernel_timing_trigger(imath_random(10, 300), 106);
+	kernel_timing_trigger(imath_random(10, 300), 107);
+	kernel_timing_trigger(1, 109);
+
+	if (previous_room != 199 && flags[11] != 3)
+		flags[11]++;
+
+	room_210_init1();
 }
 
-static void room_210_daemon() {
+static void room_210_anim1() {
+	if (kernel_anim[aa[0]].frame != aainfo[0]._frame)
+		aainfo[0]._frame = kernel_anim[aa[0]].frame;
+
+	if (global[player_hyperwalked] != -1)
+		return;
+	aainfo[0]._frame = scratch._92 - 1;
+	kernel_reset_animation(aa[0], aainfo[0]._frame);
 }
 
-static void room_210_pre_parser() {
+static void room_210_anim2() {
+	if (kernel_anim[aa[1]].frame != aainfo[1]._frame)
+		aainfo[1]._frame = kernel_anim[aa[1]].frame;
+
+	if (global[player_hyperwalked] != -1)
+		return;
+	aainfo[1]._frame = scratch._94 - 1;
+	kernel_reset_animation(aa[1], aainfo[1]._frame);
+}
+
+static void room_210_anim3() {
+	if (kernel_anim[aa[7]].frame == aainfo[7]._frame)
+		return;
+	aainfo[7]._frame = kernel_anim[aa[7]].frame;
+
+	int16 f = aainfo[7]._frame;
+	if (f == 1) {
+		kernel_timing_trigger(10, 114);
+	} else if (f == 6) {
+		aainfo[7]._frame = 2;
+		kernel_reset_animation(aa[7], 2);
+	}
+}
+
+static void room_210_anim4() {
+	int16 result = -1;
+
+	if (kernel_anim[aa[8]].frame == aainfo[8]._frame)
+		return;
+	aainfo[8]._frame = kernel_anim[aa[8]].frame;
+	int16 f = aainfo[8]._frame;
+
+	if (f == 11) {
+		result = 9;
+	} else if (f < 11) {
+		if (f == 1) {
+			digi_play_build(210, 'R', 1, 1);
+			scratch._a6 = 2;
+		} else if (f == 3) {
+			result = 1;
+		} else if (f == 9) {
+			digi_play_build(210, 'E', 1, 1);
+			scratch._a6 = 3;
+		}
+	}
+
+	if (result >= 0) {
+		aainfo[8]._frame = result;
+		kernel_reset_animation(aa[8], result);
+	}
+}
+
+static void room_210_anim5() {
+	if (kernel_anim[aa[2]].frame != aainfo[2]._frame) {
+		aainfo[2]._frame = kernel_anim[aa[2]].frame;
+		int16 f = aainfo[2]._frame;
+		if (f == 1) {
+			aainfo[2]._frame = 0;
+			kernel_reset_animation(aa[2], 0);
+		} else if (f == 52 || f == 78) {
+			aainfo[2]._frame = 0;
+			kernel_reset_animation(aa[2], 0);
+			kernel_timing_trigger(imath_random(100, 300), 103);
+		}
+	}
+
+	if (kernel_anim[aa[3]].frame != aainfo[3]._frame) {
+		aainfo[3]._frame = kernel_anim[aa[3]].frame;
+		int16 f = aainfo[3]._frame;
+		if (f == 1) {
+			aainfo[3]._frame = 0;
+			kernel_reset_animation(aa[3], 0);
+		} else if (f == 19 || f == 63) {
+			aainfo[3]._frame = 0;
+			kernel_reset_animation(aa[3], 0);
+			kernel_timing_trigger(imath_random(100, 300), 104);
+		}
+	}
+
+	if (kernel_anim[aa[4]].frame != aainfo[4]._frame) {
+		aainfo[4]._frame = kernel_anim[aa[4]].frame;
+		int16 f = aainfo[4]._frame;
+		if (f == 1) {
+			aainfo[4]._frame = 0;
+			kernel_reset_animation(aa[4], 0);
+		} else if (f == 20 || f == 82) {
+			aainfo[4]._frame = 0;
+			kernel_reset_animation(aa[4], 0);
+			kernel_timing_trigger(imath_random(100, 300), 105);
+		}
+	}
+
+	if (kernel_anim[aa[5]].frame != aainfo[5]._frame) {
+		aainfo[5]._frame = kernel_anim[aa[5]].frame;
+		int16 f = aainfo[5]._frame;
+		if (f == 1) {
+			aainfo[5]._frame = 0;
+			kernel_reset_animation(aa[5], 0);
+		} else if (f == 34 || f == 57) {
+			aainfo[5]._frame = 0;
+			kernel_reset_animation(aa[5], 0);
+			kernel_timing_trigger(imath_random(100, 300), 106);
+		}
+	}
+
+	if (kernel_anim[aa[6]].frame == aainfo[6]._frame)
+		return;
+	aainfo[6]._frame = kernel_anim[aa[6]].frame;
+	int16 f = aainfo[6]._frame;
+	if (f == 1) {
+		aainfo[6]._frame = 0;
+		kernel_reset_animation(aa[6], 0);
+	} else if (f == 27 || f == 53) {
+		aainfo[6]._frame = 0;
+		kernel_reset_animation(aa[6], 0);
+		kernel_timing_trigger(imath_random(100, 300), 107);
+	}
+}
+
+static void room_210_anim6(int roomNum) {
+	if (roomNum == 106) {
+		if (global[g021] != 0) { new_room = 106; return; }
+	} else if (roomNum == 203) {
+		if (global[g040] != 0) { new_room = 203; return; }
+	} else if (roomNum == 302) {
+		if (global[g063] != 0) { new_room = 302; return; }
+	} else if (roomNum == 305) {
+		if (global[g060] != 0) { new_room = 305; return; }
+	} else if (roomNum == 307) {
+		if (global[g061] != 0) {
+			if (global[g064] != 0)
+				new_room = 322;
+			else
+				new_room = 307;
+			return;
+		}
+	} else {
+		return;
+	}
+	kernel_timing_trigger(imath_random(40, 80), 108);
+}
+
+static void room_210_anim7() {
+	switch (scratch._aa) {
+	case 1:
+		aa[0] = kernel_run_animation(kernel_name('a', 1), 110);
+		aainfo[0]._active = -1;
+		scratch._92 = 65;
+		break;
+	case 2:
+		aa[0] = kernel_run_animation(kernel_name('a', 2), 110);
+		aainfo[0]._active = -1;
+		scratch._92 = 60;
+		break;
+	case 3:
+		aa[0] = kernel_run_animation(kernel_name('a', 3), 110);
+		aainfo[0]._active = -1;
+		scratch._92 = 64;
+		break;
+	case 4:
+		aa[0] = kernel_run_animation(kernel_name('a', 4), 110);
+		aainfo[0]._active = -1;
+		scratch._92 = 70;
+		break;
+	case 5:
+		aa[0] = kernel_run_animation(kernel_name('a', 5), 110);
+		aainfo[0]._active = -1;
+		scratch._92 = 89;
+		break;
+	default:
+		break;
+	}
+	kernel_timing_trigger(1, 109);
+}
+
+static void room_210_daemon() {
+	switch (kernel.trigger) {
+	case 7:
+		switch (scratch._a6) {
+		case 1:
+			aainfo[7]._frame = 6;
+			kernel_reset_animation(aa[7], 6);
+			break;
+		case 2:
+			aainfo[8]._frame = 4;
+			kernel_reset_animation(aa[8], 4);
+			break;
+		case 3:
+			aainfo[8]._frame = 12;
+			kernel_reset_animation(aa[8], 12);
+			if (digi_val1 != 0) kernel_timing_trigger(1, 109);
+			break;
+		case 4:
+			global[walker_converse_state] = 0;
+			close_journal(3);
+			player.commands_allowed = -1;
+			if (digi_val1 != 0) kernel_timing_trigger(1, 109);
+			break;
+		}
+		scratch._a6 = 0;
+		break;
+
+	case 26:
+		kernel_abort_animation(scratch._a2);
+		kernel_abort_animation(scratch._a4);
+		scratch._a2 = -1;
+		switch (scratch._90) {
+		case 1:
+			aa[1] = kernel_run_animation(kernel_name('z', 1), 101);
+			aainfo[1]._active = -1;
+			scratch._94 = 36;
+			break;
+		case 2:
+			aa[1] = kernel_run_animation(kernel_name('z', 2), 101);
+			aainfo[1]._active = -1;
+			scratch._94 = 35;
+			break;
+		case 3:
+			aa[1] = kernel_run_animation(kernel_name('z', 3), 101);
+			aainfo[1]._active = -1;
+			scratch._94 = 59;
+			break;
+		case 4:
+			aa[1] = kernel_run_animation(kernel_name('z', 4), 101);
+			aainfo[1]._active = -1;
+			scratch._94 = 71;
+			break;
+		case 5:
+			aa[1] = kernel_run_animation(kernel_name('z', 5), 101);
+			aainfo[1]._active = -1;
+			scratch._94 = 84;
+			break;
+		}
+		aainfo[1]._active = -1;
+		aainfo[1]._frame = 2;
+		kernel_reset_animation(aa[1], 2);
+		kernel_synch(KERNEL_ANIM, aa[1], KERNEL_NOW, 0);
+		kernel_timing_trigger(1, 109);
+		break;
+
+	case 100:
+		kernel_abort_animation(aa[0]);
+		aainfo[0]._active = 0;
+		if (flags[11] == 1) {
+			aa[8] = kernel_run_animation(kernel_name('x', 1), 112);
+			kernel_synch(KERNEL_ANIM, aa[8], KERNEL_NOW, 0);
+			aainfo[8]._active = -1;
+			scratch._a8 = 0;
+			kernel_timing_trigger(1, 109);
+		} else {
+			global[g131] = -1; global[g141] = -1;
+			kernel_reset_animation(scratch._a2, 1);
+			kernel_synch(KERNEL_ANIM, scratch._a2, KERNEL_NOW, 0);
+			kernel_reset_animation(scratch._a4, 1);
+			kernel_synch(KERNEL_ANIM, scratch._a4, KERNEL_NOW, 0);
+			player.walker_visible = -1;
+			global[g133] = 0; global[g143] = 0;
+			kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
+			player.commands_allowed = -1;
+			scratch._a8 = -1;
+		}
+		break;
+
+	case 101:
+		kernel_abort_animation(aa[1]);
+		aainfo[1]._active = 0;
+		switch (scratch._90) {
+		case 1:
+			room_210_anim6(global[g041]);
+			scratch._aa = 1;
+			break;
+		case 2:
+			room_210_anim6(global[g042]);
+			scratch._aa = 2;
+			break;
+		case 3:
+			room_210_anim6(global[g043]);
+			scratch._aa = 3;
+			break;
+		case 4:
+			room_210_anim6(global[g044]);
+			scratch._aa = 4;
+			break;
+		case 5:
+			room_210_anim6(global[g045]);
+			scratch._aa = 5;
+			break;
+		default:
+			speech_ems_play("d5", kernel.trigger, false);
+			break;
+		}
+		break;
+
+	case 102:
+		break;
+
+	case 103:
+		aainfo[2]._frame = imath_random(0, 1) ? 60 : 2;
+		kernel_reset_animation(aa[2], aainfo[2]._frame);
+		break;
+
+	case 104:
+		aainfo[3]._frame = imath_random(0, 1) ? 40 : 2;
+		kernel_reset_animation(aa[3], aainfo[3]._frame);
+		break;
+
+	case 105:
+		aainfo[4]._frame = imath_random(0, 1) ? 30 : 2;
+		kernel_reset_animation(aa[4], aainfo[4]._frame);
+		break;
+
+	case 106:
+		aainfo[5]._frame = imath_random(0, 1) ? 40 : 2;
+		kernel_reset_animation(aa[5], aainfo[5]._frame);
+		break;
+
+	case 107:
+		aainfo[6]._frame = imath_random(0, 1) ? 30 : 2;
+		kernel_reset_animation(aa[6], aainfo[6]._frame);
+		break;
+
+	case 108:
+		room_210_anim7();
+		break;
+
+	case 109:
+		digi_initial_volume(60);
+		digi_play_build(210, '_', 1, 3);
+		digi_val2 = 100;
+		break;
+
+	case 110:
+		kernel_abort_animation(aa[0]);
+		aainfo[0]._active = 0;
+		aa[7] = kernel_run_animation(kernel_name('a', 0), 111);
+		kernel_synch(KERNEL_ANIM, aa[7], KERNEL_NOW, 0);
+		aainfo[7]._active = -1;
+		scratch._a8 = 0;
+		kernel_timing_trigger(1, 109);
+		break;
+
+	case 111:
+		global[g131] = -1;
+		global[g141] = -1;
+		kernel_reset_animation(scratch._a2, 1);
+		kernel_synch(KERNEL_ANIM, scratch._a2, KERNEL_NOW, 0);
+		kernel_reset_animation(scratch._a4, 1);
+		kernel_synch(KERNEL_ANIM, scratch._a4, KERNEL_NOW, 0);
+		player.walker_visible = -1;
+		global[g133] = 0; global[g143] = 0;
+		kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
+		player.commands_allowed = -1;
+		scratch._a8 = -1;
+		kernel_timing_trigger(1, 109);
+		break;
+
+	case 112:
+		kernel_abort_animation(aa[8]);
+		aainfo[8]._active = 0;
+		global[g131] = -1;
+		global[g141] = -1;
+		kernel_reset_animation(scratch._a2, 1);
+		kernel_synch(KERNEL_ANIM, scratch._a2, KERNEL_NOW, 0);
+		kernel_reset_animation(scratch._a4, 1);
+		kernel_synch(KERNEL_ANIM, scratch._a4, KERNEL_NOW, 0);
+		player.walker_visible = -1;
+		global[g133] = 0;
+		global[g143] = 0;
+		kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
+		player.commands_allowed = -1;
+		scratch._a8 = -1;
+		break;
+
+	case 114:
+		digi_play_build(210, 'B', 1, 1);
+		scratch._a6 = 1;
+		break;
+
+	default:
+		if (kernel.trigger >= 100)
+			speech_ems_play("d4", kernel.trigger, false);
+		break;
+	}
+
+	if (scratch._a2 >= 0) {
+		global_anim1(7, scratch._a2, global[g131], &global[g132]);
+		global_anim2(9, scratch._a4, global[g141], &global[g142]);
+	}
+	if (aainfo[0]._active != 0) room_210_anim1();
+	if (aainfo[1]._active != 0) room_210_anim2();
+	if (aainfo[7]._active != 0) room_210_anim3();
+	if (aainfo[8]._active != 0) room_210_anim4();
+	room_210_anim5();
 }
 
 static void room_210_parser() {
+	if (global[walker_converse_state] != 0) {
+		player.commands_allowed = 0;
+		digi_play_build_ii('c', 1, 1);
+		scratch._a6 = 4;
+		player.command_ready = false;
+		return;
+	}
+
+	if (global[player_selected_object] >= 0) {
+		player.command_ready = false;
+		return;
+	}
+
+	if (player_parse(78, 85, 0)) {
+		player.commands_allowed = 0;
+		player.walker_visible = 0;
+		global[g150] = -1;
+		scratch._90 = 1;
+		player.command_ready = false;
+		return;
+	}
+
+	if (player_parse(78, 86, 0)) {
+		player.commands_allowed = 0;
+		player.walker_visible = 0;
+		global[g150] = -1;
+		scratch._90 = 2;
+		player.command_ready = false;
+		return;
+	}
+
+	if (player_parse(78, 87, 0)) {
+		player.commands_allowed = 0;
+		player.walker_visible = 0;
+		global[g150] = -1;
+		scratch._90 = 3;
+		player.command_ready = false;
+		return;
+	}
+
+	if (player_parse(78, 88, 0)) {
+		player.commands_allowed = 0;
+		player.walker_visible = 0;
+		global[g150] = -1;
+		scratch._90 = 4;
+		player.command_ready = false;
+		return;
+	}
+
+	if (player_parse(78, 89, 0)) {
+		player.commands_allowed = 0;
+		player.walker_visible = 0;
+		global[g150] = -1;
+		scratch._90 = 5;
+		player.command_ready = false;
+		return;
+	}
 }
 
 void room_210_synchronize(Common::Serializer &s) {
 	for (int16 &v : scratch.sprite)    s.syncAsSint16LE(v);
 	for (int16 &v : scratch.sequence)  s.syncAsSint16LE(v);
 	for (int16 &v : scratch.animation) s.syncAsSint16LE(v);
+	for (AnimationInfo &ai : scratch.animation_info) ai.synchronize(s);
+	s.syncAsSint16LE(scratch._8c);
+	s.syncAsSint16LE(scratch._8e);
+	s.syncAsSint16LE(scratch._90);
+	s.syncAsSint16LE(scratch._92);
+	s.syncAsSint16LE(scratch._94);
+	s.syncAsSint16LE(scratch._96);
+	s.syncAsSint16LE(scratch._98);
+	s.syncAsSint16LE(scratch._9a);
+	s.syncAsSint16LE(scratch._9c);
+	s.syncAsSint16LE(scratch._9e);
+	s.syncAsSint16LE(scratch._a0);
+	s.syncAsSint16LE(scratch._a2);
+	s.syncAsSint16LE(scratch._a4);
+	s.syncAsSint16LE(scratch._a6);
+	s.syncAsSint16LE(scratch._a8);
+	s.syncAsSint16LE(scratch._aa);
 }
 
 void room_210_preload() {
 	room_init_code_pointer = room_210_init;
-	room_pre_parser_code_pointer = room_210_pre_parser;
+	room_pre_parser_code_pointer = NULL;
 	room_parser_code_pointer = room_210_parser;
 	room_daemon_code_pointer = room_210_daemon;
 


Commit: 0b5dd4d5820fc4ab298d0f60ac3b3e8fb32f6681
    https://github.com/scummvm/scummvm/commit/0b5dd4d5820fc4ab298d0f60ac3b3e8fb32f6681
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-06-08T10:47:36+10:00

Commit Message:
MADS: FOREST: Implemented rooms 211 and 220

Changed paths:
    engines/mads/madsv2/forest/rooms/room211.cpp
    engines/mads/madsv2/forest/rooms/room220.cpp


diff --git a/engines/mads/madsv2/forest/rooms/room211.cpp b/engines/mads/madsv2/forest/rooms/room211.cpp
index cf22e3282b7..a572b5b546d 100644
--- a/engines/mads/madsv2/forest/rooms/room211.cpp
+++ b/engines/mads/madsv2/forest/rooms/room211.cpp
@@ -19,16 +19,12 @@
  *
  */
 
-#include "mads/madsv2/core/conv.h"
+#include "mads/madsv2/core/digi.h"
 #include "mads/madsv2/core/game.h"
-#include "mads/madsv2/core/imath.h"
-#include "mads/madsv2/core/inter.h"
 #include "mads/madsv2/core/kernel.h"
-#include "mads/madsv2/core/sound.h"
-#include "mads/madsv2/core/text.h"
-#include "mads/madsv2/forest/mads/inventory.h"
-#include "mads/madsv2/forest/mads/sounds.h"
-#include "mads/madsv2/forest/mads/words.h"
+#include "mads/madsv2/core/matte.h"
+#include "mads/madsv2/core/midi.h"
+#include "mads/madsv2/core/player.h"
 #include "mads/madsv2/forest/global.h"
 #include "mads/madsv2/forest/rooms/section1.h"
 #include "mads/madsv2/forest/rooms/room211.h"
@@ -39,10 +35,11 @@ namespace Forest {
 namespace Rooms {
 
 struct Scratch {
-	int16 sprite[10];       /* Sprite series handles */
-	int16 sequence[10];     /* Sequence handles      */
-	int16 animation[10];     /* Animation handles     */
+	int16 sprite[10];
+	int16 sequence[10];
+	int16 animation[10];
 	AnimationInfo animation_info[10];
+	int16 _8c;
 };
 
 static Scratch scratch;
@@ -55,21 +52,75 @@ static Scratch scratch;
 
 
 static void room_211_init() {
+	midi_stop();
+	global[player_score] = -1;
+	viewing_at_y = 22;
+	global[g009] = 0;
+	player.walker_visible = 0;
+	player.commands_allowed = 0;
+
+	for (int i = 0; i < 10; i++) {
+		aainfo[i]._active = 0;
+		aainfo[i]._frame = -1;
+	}
+
+	kernel_timing_trigger(5, 100);
+}
+
+static void room_211_anim1() {
+	if (kernel_anim[aa[0]].frame == aainfo[0]._frame)
+		return;
+	aainfo[0]._frame = kernel_anim[aa[0]].frame;
+	int16 f = aainfo[0]._frame;
+
+	if (f == 75) {
+		new_room = 221;
+		return;
+	}
+	if (f > 75)
+		return;
+
+	if (f == 3) {
+		digi_play_build(211, '_', 500, 1);
+	} else if (f == 13 || f == 19 || f == 23 || f == 28) {
+		digi_play_build(220, '_', 1, 2);
+	} else if (f == 34) {
+		digi_play_build(211, '_', 700, 1);
+	}
 }
 
 static void room_211_daemon() {
+	switch (kernel.trigger) {
+	case 100:
+		aa[0] = kernel_run_animation(kernel_name('y', 1), 0);
+		aainfo[0]._active = -1;
+		scratch._8c = 75;
+		break;
+	case 101:
+		digi_play_build(221, 'e', 6, 1);
+		break;
+	default:
+		break;
+	}
+
+	if (aainfo[0]._active != 0)
+		room_211_anim1();
 }
 
 static void room_211_pre_parser() {
+	// No implementation
 }
 
 static void room_211_parser() {
+	// No implementation
 }
 
 void room_211_synchronize(Common::Serializer &s) {
 	for (int16 &v : scratch.sprite)    s.syncAsSint16LE(v);
 	for (int16 &v : scratch.sequence)  s.syncAsSint16LE(v);
 	for (int16 &v : scratch.animation) s.syncAsSint16LE(v);
+	for (AnimationInfo &ai : scratch.animation_info) ai.synchronize(s);
+	s.syncAsSint16LE(scratch._8c);
 }
 
 void room_211_preload() {
diff --git a/engines/mads/madsv2/forest/rooms/room220.cpp b/engines/mads/madsv2/forest/rooms/room220.cpp
index 09982c1aa9a..4973007164b 100644
--- a/engines/mads/madsv2/forest/rooms/room220.cpp
+++ b/engines/mads/madsv2/forest/rooms/room220.cpp
@@ -1,4 +1,4 @@
-???/* ScummVM - Graphic Adventure Engine
+/* 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
@@ -19,16 +19,12 @@
  *
  */
 
-#include "mads/madsv2/core/conv.h"
+#include "mads/madsv2/core/digi.h"
 #include "mads/madsv2/core/game.h"
-#include "mads/madsv2/core/imath.h"
-#include "mads/madsv2/core/inter.h"
 #include "mads/madsv2/core/kernel.h"
-#include "mads/madsv2/core/sound.h"
-#include "mads/madsv2/core/text.h"
-#include "mads/madsv2/forest/mads/inventory.h"
-#include "mads/madsv2/forest/mads/sounds.h"
-#include "mads/madsv2/forest/mads/words.h"
+#include "mads/madsv2/core/matte.h"
+#include "mads/madsv2/core/midi.h"
+#include "mads/madsv2/core/player.h"
 #include "mads/madsv2/forest/global.h"
 #include "mads/madsv2/forest/rooms/section1.h"
 #include "mads/madsv2/forest/rooms/room220.h"
@@ -39,10 +35,20 @@ namespace Forest {
 namespace Rooms {
 
 struct Scratch {
-	int16 sprite[10];       /* Sprite series handles */
-	int16 sequence[10];     /* Sequence handles      */
-	int16 animation[10];     /* Animation handles     */
+	int16 sprite[10];
+	int16 sequence[10];
+	int16 animation[10];
 	AnimationInfo animation_info[10];
+	int16 _8c;
+	int16 _8e;
+	int16 _90;
+	int16 _92;
+	int16 _94;
+	int16 _96;
+	int16 _98;
+	int16 _9a;
+	int16 _9c;
+	int16 _9e;
 };
 
 static Scratch scratch;
@@ -55,21 +61,192 @@ static Scratch scratch;
 
 
 static void room_220_init() {
+	global[player_score] = -1;
+	global[g009] = -1;
+	global_digi_play(10);
+	viewing_at_y = 22;
+	player.walker_visible = 0;
+	player.commands_allowed = 0;
+
+	for (int i = 0; i < 10; i++) {
+		aainfo[i]._active = 0;
+		aainfo[i]._frame = -1;
+	}
+
+	aa[0] = kernel_run_animation("*RM220Y11", 0);
+	aainfo[0]._active = -1;
+	scratch._92 = 121;
+}
+
+static void room_220_anim1() {
+	int16 result = -1;
+
+	if (kernel_anim[aa[0]].frame == aainfo[0]._frame)
+		return;
+	aainfo[0]._frame = kernel_anim[aa[0]].frame;
+	int16 f = aainfo[0]._frame;
+
+	if (f == 121) {
+		kernel_abort_animation(aa[0]);
+		aainfo[0]._active = 0;
+		global[g009] = -1;
+		global_digi_play(15);
+		aa[1] = kernel_run_animation("*RM220Y12", 0);
+		aainfo[1]._active = -1;
+		scratch._92 = 55;
+		kernel_synch(KERNEL_ANIM, aa[0], KERNEL_ANIM, aa[1]);
+	} else if (f < 121) {
+		if (f == 52) {
+			result = 51;
+		} else if (f < 52) {
+			if (f == 43) {
+				result = 41;
+			} else if (f < 43) {
+				if (f == 41) {
+					digi_play_build(220, 'E', 1, 1);
+					scratch._9e = 3;
+				}
+			} else {
+				if (f == 45) {
+					result = 44;
+				} else if (f == 47) {
+					digi_play_build(220, 'R', 1, 1);
+					scratch._9e = 1;
+				} else if (f == 50) {
+					result = 47;
+				}
+			}
+		} else {
+			if (f == 53) {
+				digi_play_build(220, 'B', 1, 1);
+				scratch._9e = 4;
+			} else if (f == 57) {
+				result = 53;
+			} else if (f == 58) {
+				result = 57;
+			} else if (f == 85 || f == 92 || f == 98 || f == 103) {
+				digi_play_build(220, '_', 1, 2);
+			} else if (f == 115) {
+				midi_stop();
+			}
+		}
+	}
+
+	if (result >= 0) {
+		aainfo[0]._frame = result;
+		kernel_reset_animation(aa[0], result);
+	}
+}
+
+static void room_220_anim2() {
+	int16 result = -1;
+
+	if (kernel_anim[aa[1]].frame == aainfo[1]._frame)
+		return;
+	aainfo[1]._frame = kernel_anim[aa[1]].frame;
+	int16 f = aainfo[1]._frame;
+
+	if (f == 55) {
+		new_room = 221;
+	} else if (f < 55) {
+		if (f == 28) {
+			digi_play_build(220, '_', 1, 2);
+		} else if (f > 28) {
+			if (f == 30) {
+				digi_play_build(220, 'R', 1, 2);
+				scratch._9e = 2;
+			} else if (f == 46 || f == 54) {
+				digi_play_build(220, '_', 1, 2);
+			}
+		} else {
+			if (f == 4) {
+				digi_play_build(220, '_', 2, 2);
+			} else if (f == 9) {
+				digi_play_build(103, '_', 3, 2);
+			} else if (f == 15) {
+				digi_play_build(220, '_', 3, 2);
+			}
+		}
+	}
+
+	if (result >= 0) {
+		aainfo[1]._frame = result;
+		kernel_reset_animation(aa[1], result);
+	}
 }
 
 static void room_220_daemon() {
+	switch (kernel.trigger) {
+	case 7:
+		kernel_timing_trigger(40, 102);
+		switch (scratch._9e) {
+		case 1:
+			aainfo[0]._frame = 51;
+			kernel_reset_animation(aa[0], 51);
+			break;
+		case 3:
+			aainfo[0]._frame = 44;
+			kernel_reset_animation(aa[0], 44);
+			break;
+		case 4:
+			aainfo[0]._frame = 57;
+			kernel_reset_animation(aa[0], 57);
+			break;
+		default:
+			break;
+		}
+		break;
+	case 102:
+		switch (scratch._9e) {
+		case 1:
+			aainfo[0]._frame = 52;
+			kernel_reset_animation(aa[0], 52);
+			break;
+		case 3:
+			aainfo[0]._frame = 46;
+			kernel_reset_animation(aa[0], 46);
+			break;
+		case 4:
+			aainfo[0]._frame = 58;
+			kernel_reset_animation(aa[0], 58);
+			break;
+		default:
+			break;
+		}
+		break;
+	default:
+		break;
+	}
+
+	if (aainfo[0]._active != 0)
+		room_220_anim1();
+	if (aainfo[1]._active != 0)
+		room_220_anim2();
 }
 
 static void room_220_pre_parser() {
+	// No implementation
 }
 
 static void room_220_parser() {
+	// No implementation
 }
 
 void room_220_synchronize(Common::Serializer &s) {
 	for (int16 &v : scratch.sprite)    s.syncAsSint16LE(v);
 	for (int16 &v : scratch.sequence)  s.syncAsSint16LE(v);
 	for (int16 &v : scratch.animation) s.syncAsSint16LE(v);
+	for (AnimationInfo &ai : scratch.animation_info) ai.synchronize(s);
+	s.syncAsSint16LE(scratch._8c);
+	s.syncAsSint16LE(scratch._8e);
+	s.syncAsSint16LE(scratch._90);
+	s.syncAsSint16LE(scratch._92);
+	s.syncAsSint16LE(scratch._94);
+	s.syncAsSint16LE(scratch._96);
+	s.syncAsSint16LE(scratch._98);
+	s.syncAsSint16LE(scratch._9a);
+	s.syncAsSint16LE(scratch._9c);
+	s.syncAsSint16LE(scratch._9e);
 }
 
 void room_220_preload() {



From noreply at scummvm.org  Mon Jun  8 04:14:28 2026
From: noreply at scummvm.org (dreammaster)
Date: Mon,  8 Jun 2026 04:14:28 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 bfbf011101b5ad509fcc25e7d75da5cb9da5121d
Message-ID: <20260608041428.8C13A7005CC@ron.scummvm.net>

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

Summary:
bfbf011101 MADS: FOREST: Implemented room 301


Commit: bfbf011101b5ad509fcc25e7d75da5cb9da5121d
    https://github.com/scummvm/scummvm/commit/bfbf011101b5ad509fcc25e7d75da5cb9da5121d
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-06-08T14:14:17+10:00

Commit Message:
MADS: FOREST: Implemented room 301

Changed paths:
    engines/mads/madsv2/forest/global.cpp
    engines/mads/madsv2/forest/global.h
    engines/mads/madsv2/forest/rooms/room301.cpp


diff --git a/engines/mads/madsv2/forest/global.cpp b/engines/mads/madsv2/forest/global.cpp
index f086e6dc87c..96b4c21eca6 100644
--- a/engines/mads/madsv2/forest/global.cpp
+++ b/engines/mads/madsv2/forest/global.cpp
@@ -909,6 +909,107 @@ void global_daemon_code() {
 	}
 }
 
+void global_anim3(int handle, int16 *frame) {
+	if (kernel_anim[handle].frame == *frame)
+		return;
+	int16 var_2 = -1;
+	*frame = (int16)kernel_anim[handle].frame;
+	int16 f = *frame;
+
+	switch (f) {
+	case 18:
+		digi_play_build(301, '_', 1, 2);
+		break;
+	case 39:
+		global[walker_converse_now] = 3;
+		break;
+	case 40:
+		if (global[walker_converse_now] == 3) {
+			kernel_timing_trigger(6, 27);
+			global[walker_converse_now] = 0;
+		}
+		var_2 = 39;
+		break;
+	case 43:
+		var_2 = 3;
+		break;
+	case 44:
+		if (global[g007] == 3) {
+			if (global[walker_converse_now] == 2) {
+				kernel_timing_trigger(6, 27);
+				global[walker_converse_now] = 3;
+			}
+			var_2 = f - 1;
+		} else if (global[g007] != 6) {
+			var_2 = 45;
+		}
+		break;
+	case 45:
+		if (global[g007] == 6) {
+			if (global[walker_converse_now] == 2) {
+				kernel_timing_trigger(6, 27);
+				global[walker_converse_now] = 3;
+			}
+			var_2 = f - 1;
+		}
+		break;
+	case 46:
+		if (global[g007] == 9) {
+			if (global[walker_converse_now] == 2) {
+				kernel_timing_trigger(6, 27);
+				global[walker_converse_now] = 3;
+			}
+			var_2 = f - 1;
+		}
+		break;
+	case 47:
+		if (global[g007] == 1) {
+			if (global[walker_converse_now] == 2) {
+				kernel_timing_trigger(6, 27);
+				global[walker_converse_now] = 3;
+			}
+			var_2 = f - 1;
+		} else if (global[g007] != 4) {
+			var_2 = 48;
+		}
+		break;
+	case 48:
+		if (global[g007] == 4) {
+			if (global[walker_converse_now] == 2) {
+				kernel_timing_trigger(6, 27);
+				global[walker_converse_now] = 3;
+			}
+			var_2 = f - 1;
+		}
+		break;
+	case 49:
+		if (global[g007] == 7) {
+			if (global[walker_converse_now] == 2) {
+				kernel_timing_trigger(6, 27);
+				global[walker_converse_now] = 3;
+			}
+			var_2 = f - 1;
+		}
+		break;
+	case 50:
+		if (global[g007] == 8) {
+			if (global[walker_converse_now] == 2) {
+				kernel_timing_trigger(6, 27);
+				global[walker_converse_now] = 3;
+			}
+			var_2 = f - 1;
+		}
+		break;
+	default:
+		break;
+	}
+
+	if (var_2 >= 0) {
+		kernel_reset_animation(handle, var_2);
+		*frame = var_2;
+	}
+}
+
 } // namespace Forest
 } // namespace MADSV2
 } // namespace MADS
diff --git a/engines/mads/madsv2/forest/global.h b/engines/mads/madsv2/forest/global.h
index c34c61328c3..7de3fd09301 100644
--- a/engines/mads/madsv2/forest/global.h
+++ b/engines/mads/madsv2/forest/global.h
@@ -264,6 +264,7 @@ extern void global_section_interface();
 
 extern void global_anim1(int arg_0, int arg_2, int arg_4, int16 *arg_6);
 extern void global_anim2(int arg_0, int arg_2, int arg_4, int16 *arg_6);
+extern void global_anim3(int handle, int16 *frame);
 extern void global_digi_play(int num);
 extern void global_daemon_code();
 
diff --git a/engines/mads/madsv2/forest/rooms/room301.cpp b/engines/mads/madsv2/forest/rooms/room301.cpp
index db009299b16..45d959c7884 100644
--- a/engines/mads/madsv2/forest/rooms/room301.cpp
+++ b/engines/mads/madsv2/forest/rooms/room301.cpp
@@ -19,18 +19,15 @@
  *
  */
 
-#include "mads/madsv2/core/conv.h"
+#include "mads/madsv2/forest/journal.h"
+#include "mads/madsv2/core/digi.h"
 #include "mads/madsv2/core/game.h"
-#include "mads/madsv2/core/imath.h"
 #include "mads/madsv2/core/inter.h"
 #include "mads/madsv2/core/kernel.h"
-#include "mads/madsv2/core/sound.h"
-#include "mads/madsv2/core/text.h"
-#include "mads/madsv2/forest/mads/inventory.h"
-#include "mads/madsv2/forest/mads/sounds.h"
-#include "mads/madsv2/forest/mads/words.h"
+#include "mads/madsv2/core/midi.h"
+#include "mads/madsv2/core/object.h"
+#include "mads/madsv2/core/player.h"
 #include "mads/madsv2/forest/global.h"
-#include "mads/madsv2/forest/rooms/section1.h"
 #include "mads/madsv2/forest/rooms/room301.h"
 
 namespace MADS {
@@ -39,10 +36,26 @@ namespace Forest {
 namespace Rooms {
 
 struct Scratch {
-	int16 sprite[10];       /* Sprite series handles */
-	int16 sequence[10];     /* Sequence handles      */
-	int16 animation[10];     /* Animation handles     */
+	int16 sprite[10];
+	int16 sequence[10];
+	int16 animation[10];
 	AnimationInfo animation_info[10];
+	int16 _8c;
+	int16 _8e;
+	int16 _90;
+	int16 _92;
+	int16 _94;
+	int16 _96;
+	int16 _98;
+	int16 _9a;
+	int16 _9c;
+	int16 _9e;
+	int16 _a0;
+	int16 _a2;
+	int16 _a4;
+	int16 _a6;
+	int16 _a8;
+	int16 _aa;
 };
 
 static Scratch scratch;
@@ -54,22 +67,795 @@ static Scratch scratch;
 #define aainfo scratch.animation_info
 
 
+static void room_301_init1() {
+	global[g131] = 0;
+	global[g141] = 0;
+	scratch._9c = kernel_run_animation_disp('r', 2, 0);
+	kernel_position_anim(scratch._9c, 130, 134, 72, 6);
+	scratch._9a = kernel_run_animation_disp('e', 1, 0);
+	kernel_position_anim(scratch._9a, 100, 127, 63, 6);
+
+	if (previous_room != KERNEL_LAST) {
+		player.x = 164;
+		player.y = 132;
+		player.facing = 3;
+	}
+
+	switch (previous_room) {
+	case 221:
+	case 101:
+	case 205:
+	case 302:
+	case 305:
+	case 307:
+	case 322:
+		aa[0] = kernel_run_animation(kernel_name('t', 1), 100);
+		aainfo[0]._active = -1;
+		scratch._92 = 53;
+		player.x = 61;
+		player.y = 155;
+		player.facing = 2;
+		return;
+
+	case 199:
+		global[g131] = -1;
+		global[g141] = -1;
+		kernel_reset_animation(scratch._9a, 2);
+		kernel_reset_animation(scratch._9c, 2);
+		global[g133] = 0;
+		global[g143] = 0;
+		restore_player();
+		player.commands_allowed = -1;
+		player.walker_visible = -1;
+		return;
+
+	case KERNEL_LAST:
+		global[g131] = -1;
+		global[g141] = -1;
+		kernel_reset_animation(scratch._9a, 2);
+		kernel_reset_animation(scratch._9c, 2);
+		global[g133] = 0;
+		global[g143] = 0;
+		return;
+
+	default:
+		global[g131] = -1;
+		global[g141] = -1;
+		kernel_reset_animation(scratch._9a, 2);
+		kernel_reset_animation(scratch._9c, 2);
+		global[g133] = 0;
+		global[g143] = 0;
+		player.commands_allowed = -1;
+		player.walker_visible = -1;
+		return;
+	}
+}
+
+static void room_301_init2() {
+	global[g131] = 0;
+	global[g141] = 0;
+	scratch._9c = kernel_run_animation_disp('r', 2, 0);
+	kernel_position_anim(scratch._9c, 130, 134, 72, 6);
+	scratch._9a = kernel_run_animation_disp('e', 1, 0);
+	kernel_position_anim(scratch._9a, 100, 127, 63, 6);
+
+	if (previous_room != KERNEL_LAST) {
+		player.x = 164;
+		player.y = 132;
+		player.facing = 3;
+	}
+
+	switch (previous_room) {
+	case 302:
+		aa[0] = kernel_run_animation(kernel_name('y', 2), 100);
+		aainfo[0]._active = -1;
+		scratch._92 = 88;
+		return;
+
+	case 205:
+		aa[0] = kernel_run_animation(kernel_name('y', 1), 100);
+		aainfo[0]._active = -1;
+		scratch._92 = 53;
+		return;
+
+	case 305:
+		aa[0] = kernel_run_animation(kernel_name('y', 3), 100);
+		aainfo[0]._active = -1;
+		scratch._92 = 85;
+		return;
+
+	case 307:
+	case 322:
+		aa[0] = kernel_run_animation(kernel_name('y', 4), 100);
+		aainfo[0]._active = -1;
+		scratch._92 = 149;
+		return;
+
+	case 199:
+		global[g131] = -1;
+		global[g141] = -1;
+		kernel_reset_animation(scratch._9a, 2);
+		kernel_reset_animation(scratch._9c, 2);
+		global[g133] = 0;
+		global[g143] = 0;
+		restore_player();
+		player.commands_allowed = -1;
+		player.walker_visible = -1;
+		return;
+
+	case KERNEL_LAST:
+		global[g131] = -1;
+		global[g141] = -1;
+		kernel_reset_animation(scratch._9a, 2);
+		kernel_reset_animation(scratch._9c, 2);
+		global[g133] = 0;
+		global[g143] = 0;
+		return;
+
+	default:
+		global[g131] = -1;
+		global[g141] = -1;
+		kernel_reset_animation(scratch._9a, 2);
+		kernel_reset_animation(scratch._9c, 2);
+		global[g133] = 0;
+		global[g143] = 0;
+		player.commands_allowed = -1;
+		player.walker_visible = -1;
+		return;
+	}
+}
+
 static void room_301_init() {
+	scratch._aa = 0;
+	scratch._a8 = -1;
+	scratch._a4 = 0;
+	global[g009] = 0;
+	midi_stop();
+
+	if (object_is_here(15)) {
+		ss[0] = kernel_load_series(kernel_name('p', 2), 0);
+		seq[0] = kernel_seq_stamp(ss[0], false, -1);
+		kernel_seq_depth(seq[0], 10);
+		kernel_seq_loc(seq[0], 195, 124);
+		kernel_seq_scale(seq[0], 59);
+	} else {
+		kernel_flip_hotspot(101, 0);
+	}
+
+	if (object_is_here(10)) {
+		ss[1] = kernel_load_series(kernel_name('p', 1), 0);
+		seq[1] = kernel_seq_stamp(ss[1], false, -1);
+		kernel_seq_depth(seq[1], 1);
+		kernel_seq_loc(seq[1], 302, 143);
+		kernel_seq_scale(seq[1], 85);
+	} else {
+		kernel_flip_hotspot(163, 0);
+	}
+
+	if (previous_room != KERNEL_LAST) {
+		if (previous_room != 199) {
+			player.walker_visible = 0;
+			player.commands_allowed = 0;
+		}
+
+		for (int count = 0; count < 10; count++) {
+			aainfo[count]._active = 0;
+			aainfo[count]._frame = 0;
+			aainfo[count]._val3 = 0;
+			aainfo[count]._val4 = 0;
+		}
+
+		if (previous_room != 199 && flags[30] != 3)
+			flags[30]++;
+	}
+
+	if (flags[30] == 1)
+		room_301_init1();
+	else
+		room_301_init2();
+}
+
+static void room_301_anim1() {
+	if (kernel_anim[aa[0]].frame == aainfo[0]._frame)
+		return;
+	aainfo[0]._frame = kernel_anim[aa[0]].frame;
+	int16 f = aainfo[0]._frame;
+
+	if (flags[30] == 1) {
+		if (f == 2) {
+			digi_play_build(301, '_', 2, 1);
+		} else if (f == 11) {
+			aainfo[0]._frame = 8;
+			kernel_reset_animation(aa[0], 8);
+		}
+	}
+
+	if (flags[30] == 1 && kernel.trigger == 7) {
+		aainfo[0]._frame = 12;
+		kernel_reset_animation(aa[0], 12);
+	}
+
+	if (global[player_hyperwalked] == -1 && flags[30] != 1) {
+		int16 reset_frame = scratch._92 - 1;
+		aainfo[0]._frame = reset_frame;
+		kernel_reset_animation(aa[0], reset_frame);
+	}
+}
+
+static void room_301_anim2() {
+	if (scratch._a4 == 0)
+		return;
+
+	kernel_random_frame(scratch._a2, &global[g153], global[g156]);
+	kernel_random_frame(scratch._a0, &global[g152], global[g155]);
+	kernel_random_frame(scratch._9e, &global[g151], global[g154]);
+
+	if (kernel.trigger != 7 && kernel.trigger != 28)
+		return;
+
+	switch (scratch._a4) {
+	case 2:
+		global[g155] = 0;
+		kernel_timing_trigger(30, 28);
+		scratch._a4 = 3;
+		break;
+	case 3:
+		scratch._a4 = 4;
+		global[g156] = 2;
+		digi_play_build(301, 'e', 2, 1);
+		break;
+	case 4:
+		global[g156] = 0;
+		kernel_timing_trigger(30, 28);
+		scratch._a4 = 5;
+		break;
+	case 5:
+		scratch._a4 = 6;
+		global[g154] = 2;
+		digi_play_build(301, 'b', 2, 1);
+		break;
+	case 6:
+		global[g154] = 0;
+		kernel_timing_trigger(30, 28);
+		scratch._a4 = 7;
+		break;
+	case 7:
+		global[g155] = 2;
+		scratch._a4 = 8;
+		digi_play_build(301, 'r', 3, 1);
+		break;
+	case 8:
+		global[g155] = 0;
+		kernel_timing_trigger(30, 28);
+		scratch._a4 = 9;
+		break;
+	case 9:
+		kernel_reset_animation(scratch._a2, 0);
+		global[g156] = 1;
+		scratch._a6 = kernel_run_animation_write(0);
+		kernel_reset_animation(scratch._a6, 41);
+		kernel_position_anim(scratch._a6, 24, 152, 97, 3);
+		global[walker_converse_now] = 1;
+		global[g007] = 3;
+		kernel_synch(KERNEL_ANIM, scratch._a2, KERNEL_NOW, 0);
+		kernel_synch(KERNEL_ANIM, scratch._a6, KERNEL_NOW, 0);
+		scratch._a4 = 10;
+		break;
+	default:
+		break;
+	}
+}
+
+static void room_301_anim3() {
+	if (kernel_anim[aa[2]].frame == aainfo[2]._frame)
+		return;
+	aainfo[2]._frame = kernel_anim[aa[2]].frame;
+}
+
+static void room_301_anim4() {
+	if (kernel_anim[aa[4]].frame != aainfo[4]._frame) {
+		int16 f = kernel_anim[aa[4]].frame;
+		aainfo[4]._frame = f;
+
+		if (scratch._a8 == 1) {
+			if (f == 52) {
+				if (aainfo[4]._val3 == 13) {
+					aainfo[4]._frame = 50;
+					kernel_reset_animation(aa[4], 50);
+				}
+			} else if (f < 52) {
+				if (f == 43) {
+					aainfo[4]._val3 = 11;
+					digi_play_build_ii('e', 7, 1);
+					scratch._a4 = 43;
+				} else if (f == 48) {
+					if (aainfo[4]._val3 == 11) {
+						aainfo[4]._frame = 43;
+						kernel_reset_animation(aa[4], 43);
+					}
+				}
+			}
+		} else if (scratch._a8 == 2) {
+			if (f == 13) {
+				if (aainfo[4]._val3 == 13) {
+					aainfo[4]._frame = 11;
+					kernel_reset_animation(aa[4], 11);
+				}
+			} else if (f < 13) {
+				if (f == 5) {
+					aainfo[4]._val3 = 11;
+					digi_play_build_ii('e', 6, 1);
+					scratch._a4 = 500;
+				} else if (f == 10) {
+					if (aainfo[4]._val3 == 11) {
+						aainfo[4]._frame = 5;
+						kernel_reset_animation(aa[4], 5);
+					}
+				}
+			}
+		}
+	}
+
+	if (kernel.trigger != 7 && kernel.trigger != 28)
+		return;
+
+	switch (scratch._a4) {
+	case 43:
+		kernel_timing_trigger(30, 28);
+		aainfo[4]._val3 = 13;
+		scratch._a4 = 44;
+		break;
+	case 44:
+		aainfo[4]._val3 = 11;
+		aainfo[4]._frame = 43;
+		kernel_reset_animation(aa[4], 43);
+		digi_play_build_ii('e', 10, 1);
+		scratch._a4 = 45;
+		break;
+	case 45:
+		aainfo[4]._val3 = 14;
+		aainfo[4]._frame = 50;
+		kernel_reset_animation(aa[4], 50);
+		scratch._a4 = -1;
+		break;
+	case 500:
+		kernel_timing_trigger(30, 28);
+		aainfo[4]._val3 = 13;
+		scratch._a4 = 501;
+		break;
+	case 501:
+		aainfo[4]._val3 = 11;
+		aainfo[4]._frame = 5;
+		kernel_reset_animation(aa[4], 5);
+		digi_play_build_ii('e', 10, 1);
+		scratch._a4 = 502;
+		break;
+	case 502:
+		aainfo[4]._val3 = 14;
+		aainfo[4]._frame = 11;
+		kernel_reset_animation(aa[4], 11);
+		scratch._a4 = -1;
+		break;
+	default:
+		break;
+	}
+}
+
+static void room_301_anim5() {
+	if (kernel_anim[aa[5]].frame != aainfo[5]._frame) {
+		int16 f = kernel_anim[aa[5]].frame;
+		aainfo[5]._frame = f;
+
+		if (scratch._a8 == 1) {
+			if (f == 40) {
+				if (aainfo[5]._val3 == 13) {
+					digi_play_build(101, '_', 1, 2);
+					scratch._a4 = 40;
+				}
+			} else if (f < 40) {
+				if (f == 5) {
+					aainfo[5]._val3 = 11;
+					digi_play_build(301, 'e', 3, 1);
+					scratch._a4 = 5;
+				} else if (f == 10) {
+					if (aainfo[5]._val3 == 11) {
+						aainfo[5]._frame = 5;
+						kernel_reset_animation(aa[5], 5);
+					}
+				}
+			}
+		} else if (scratch._a8 == 2) {
+			if (f == 41) {
+				aainfo[5]._val3 = 11;
+				digi_play_build(301, 'e', 4, 1);
+				scratch._a4 = 41;
+			} else if (f == 46) {
+				if (aainfo[5]._val3 == 11) {
+					aainfo[5]._frame = 41;
+					kernel_reset_animation(aa[5], 41);
+				}
+			}
+		} else if (scratch._a8 == 3) {
+			if (f == 9 && aainfo[5]._val3 == 13) {
+				digi_play_build(101, '_', 1, 2);
+				scratch._a4 = 9;
+			}
+		}
+	}
+
+	if (kernel.trigger == 7 || kernel.trigger == 28) {
+		if (scratch._a4 == 5) {
+			aainfo[5]._val3 = 13;
+			aainfo[5]._frame = 11;
+			kernel_reset_animation(aa[5], 11);
+			scratch._a4 = -1;
+			kernel_seq_delete(seq[0]);
+		} else if (scratch._a4 == 41) {
+			aainfo[5]._val3 = 13;
+			aainfo[5]._frame = 47;
+			kernel_reset_animation(aa[5], 47);
+			scratch._a4 = -1;
+			kernel_seq_delete(seq[1]);
+		}
+	}
+
+	if (kernel.trigger == 8 || kernel.trigger == 28) {
+		if (scratch._a4 == 9 || scratch._a4 == 40)
+			scratch._a4 = -1;
+	}
 }
 
 static void room_301_daemon() {
+	switch (kernel.trigger) {
+	case 25:
+		if (scratch._8e == 2) {
+			aa[3] = kernel_run_animation(kernel_name('R', 1), 101);
+			aainfo[3]._active = -1;
+			scratch._94 = 3;
+			kernel_reset_animation(scratch._9c, 0);
+			kernel_synch(KERNEL_ANIM, aa[3], KERNEL_ANIM, scratch._9c);
+			global[g143] = 1;
+		}
+		break;
+
+	case 24:
+		if (scratch._8c == 3) {
+			aa[2] = kernel_run_animation(kernel_name('E', 1), 102);
+			aainfo[2]._active = -1;
+			scratch._96 = 2;
+			kernel_reset_animation(scratch._9a, 0);
+			kernel_synch(KERNEL_ANIM, aa[2], KERNEL_ANIM, scratch._9a);
+			global[g133] = 1;
+		} else if (scratch._8c == 5) {
+			if (scratch._a8 == 1) {
+				aa[4] = kernel_run_animation(kernel_name('F', 2), 102);
+				aainfo[4]._active = -1;
+				scratch._96 = 4;
+				kernel_reset_animation(scratch._9a, 0);
+				kernel_synch(KERNEL_ANIM, aa[4], KERNEL_ANIM, scratch._9a);
+				global[g133] = 1;
+			} else if (scratch._a8 == 2) {
+				aa[4] = kernel_run_animation(kernel_name('F', 1), 102);
+				aainfo[4]._active = -1;
+				scratch._96 = 4;
+				kernel_reset_animation(scratch._9a, 0);
+				kernel_synch(KERNEL_ANIM, aa[4], KERNEL_ANIM, scratch._9a);
+				global[g133] = 1;
+			}
+		}
+		break;
+
+	case 7:
+		if (global[walker_converse_state] != 0) {
+			global[walker_converse_state] = 0;
+			close_journal(3);
+			player.commands_allowed = -1;
+		} else if (scratch._a4 == 300) {
+			kernel_abort_animation(scratch._9e);
+			scratch._a4 = -1;
+			scratch._aa = 0;
+			kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
+			scratch._a8 = 1;
+			player.walker_visible = -1;
+			global[g150] = -1;
+			scratch._90 = 6;
+		} else if (scratch._a4 == 400) {
+			kernel_abort_animation(scratch._9e);
+			scratch._a4 = -1;
+			scratch._aa = 0;
+			kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
+			scratch._a8 = 2;
+			player.walker_visible = -1;
+			global[g150] = -1;
+			scratch._90 = 6;
+		}
+		break;
+
+	case 26:
+		if (scratch._90 == 1) {
+			kernel_reset_animation(scratch._9a, 0);
+			kernel_reset_animation(scratch._9c, 0);
+		} else if (scratch._90 == 6) {
+			if (scratch._a8 == 1 || scratch._a8 == 2) {
+				aa[5] = kernel_run_animation(kernel_name('P', scratch._a8), 103);
+				aainfo[5]._active = -1;
+				scratch._98 = 5;
+				kernel_reset_animation(scratch._9a, 0);
+				kernel_reset_animation(scratch._9c, 0);
+				kernel_synch(KERNEL_ANIM, aa[5], KERNEL_ANIM, scratch._9a);
+				kernel_synch(KERNEL_ANIM, aa[5], KERNEL_ANIM, scratch._9c);
+			} else if (scratch._a8 == 3) {
+				kernel_abort_animation(aa[5]);
+				aa[5] = kernel_run_animation(kernel_name('P', 3), 103);
+				break;
+			} else {
+				break;
+			}
+		} else {
+			break;
+		}
+		global[g133] = 1;
+		global[g143] = 1;
+		break;
+
+	case 27:
+		global[walker_converse_now] = 0;
+		kernel_abort_animation(scratch._a6);
+		kernel_abort_animation(scratch._a2);
+		kernel_abort_animation(scratch._9e);
+		kernel_abort_animation(scratch._a0);
+		aa[1] = kernel_run_animation(kernel_name('t', 2), 104);
+		aainfo[1]._active = -1;
+		scratch._a4 = 0;
+		break;
+
+	case 103:
+		if (scratch._a8 == 1) {
+			kernel_abort_animation(aa[5]);
+			aainfo[5]._active = 0;
+			kernel_reset_animation(scratch._9c, 1);
+			kernel_synch(KERNEL_ANIM, scratch._9c, KERNEL_NOW, 0);
+			global[g143] = 0;
+			kernel_reset_animation(scratch._9a, 1);
+			kernel_synch(KERNEL_ANIM, scratch._9a, KERNEL_NOW, 0);
+			global[g133] = 0;
+			kernel_flip_hotspot(101, 0);
+			inter_move_object(15, 2);
+			global[player_score] = -1;
+			player.commands_allowed = -1;
+		} else if (scratch._a8 == 2) {
+			kernel_abort_animation(aa[5]);
+			aa[5] = kernel_run_animation(kernel_name('P', 3), 103);
+			scratch._a8 = 3;
+			kernel_synch(KERNEL_ANIM, aa[5], KERNEL_NOW, 0);
+		} else if (scratch._a8 == 3) {
+			kernel_abort_animation(aa[5]);
+			aainfo[5]._active = 0;
+			kernel_reset_animation(scratch._9c, 1);
+			kernel_synch(KERNEL_ANIM, scratch._9c, KERNEL_NOW, 0);
+			global[g143] = 0;
+			kernel_reset_animation(scratch._9a, 1);
+			kernel_synch(KERNEL_ANIM, scratch._9a, KERNEL_NOW, 0);
+			global[g133] = 0;
+			kernel_flip_hotspot(163, 0);
+			inter_move_object(10, 2);
+			global[player_score] = -1;
+			player.commands_allowed = -1;
+		}
+		break;
+
+	case 102:
+		kernel_abort_animation(aa[4]);
+		aainfo[4]._active = 0;
+		kernel_reset_animation(scratch._9a, 1);
+		kernel_synch(KERNEL_ANIM, scratch._9a, KERNEL_NOW, 0);
+		global[g133] = 0;
+		global[player_score] = -1;
+		player.commands_allowed = -1;
+		break;
+
+	case 101:
+		kernel_abort_animation(aa[scratch._94]);
+		aainfo[scratch._94]._active = 0;
+		kernel_reset_animation(scratch._9c, 1);
+		kernel_synch(KERNEL_ANIM, scratch._9c, KERNEL_NOW, 0);
+		global[g143] = 0;
+		player.commands_allowed = -1;
+		break;
+
+	case 100:
+		kernel_abort_animation(aa[0]);
+		aainfo[0]._active = 0;
+		if (flags[30] == 1) {
+			kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
+			aainfo[1]._active = -1;
+			global[g017] = 0;
+			player.walker_visible = 0;
+			global[g154] = 0;
+			kernel_synch(KERNEL_ANIM, scratch._9a, KERNEL_NOW, 0);
+			global[g131] = -1;
+			global[g133] = 1;
+			global[g156] = 0;
+			kernel_synch(KERNEL_ANIM, scratch._9c, KERNEL_NOW, 0);
+			global[g141] = -1;
+			global[g155] = 2;
+			global[g143] = 1;
+			scratch._a0 = kernel_run_animation_talk('r', 1, 0);
+			kernel_position_anim(scratch._a0, 110, 152, 97, 3);
+			scratch._9e = kernel_run_animation_talk('b', 3, 0);
+			kernel_position_anim(scratch._9e, player.x, player.y, player.scale, player.depth);
+			scratch._a2 = kernel_run_animation_talk('e', 2, 0);
+			kernel_position_anim(scratch._a2, 24, 152, 97, 3);
+			kernel_synch(KERNEL_ANIM, scratch._a0, KERNEL_ANIM, scratch._9c);
+			kernel_synch(KERNEL_ANIM, scratch._9e, KERNEL_PLAYER, 0);
+			kernel_synch(KERNEL_ANIM, scratch._a2, KERNEL_ANIM, scratch._9a);
+			scratch._a4 = 2;
+			digi_play_build(301, 'r', 2, 1);
+		} else {
+			global[g131] = -1;
+			global[g141] = -1;
+			kernel_reset_animation(scratch._9a, 1);
+			kernel_synch(KERNEL_ANIM, scratch._9a, KERNEL_NOW, 0);
+			kernel_reset_animation(scratch._9c, 1);
+			kernel_synch(KERNEL_ANIM, scratch._9c, KERNEL_NOW, 0);
+			player.walker_visible = -1;
+			global[g133] = 0;
+			global[g143] = 0;
+			kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
+			player.commands_allowed = -1;
+		}
+		break;
+
+	case 104:
+		kernel_abort_animation(aa[1]);
+		player.x = 164;
+		player.y = 132;
+		player.facing = 3;
+		player.commands_allowed = -1;
+		player.walker_visible = -1;
+		global[g017] = -1;
+		aainfo[1]._active = 0;
+		kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
+		global[g131] = -1;
+		global[g141] = -1;
+		kernel_reset_animation(scratch._9a, 1);
+		kernel_reset_animation(scratch._9c, 1);
+		kernel_synch(KERNEL_ANIM, scratch._9a, KERNEL_NOW, 0);
+		kernel_synch(KERNEL_ANIM, scratch._9c, KERNEL_NOW, 0);
+		player.walker_visible = -1;
+		global[g133] = 0;
+		global[g143] = 0;
+		kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
+		player.commands_allowed = -1;
+		break;
+
+	default:
+		break;
+	}
+
+	global_anim1(1, scratch._9a, global[g131], &global[g132]);
+	global_anim2(2, scratch._9c, global[g141], &global[g142]);
+	if (scratch._aa != 0)
+		kernel_random_frame(scratch._9e, &global[g151], global[g154]);
+	if (aainfo[0]._active != 0)
+		room_301_anim1();
+	if (aainfo[2]._active != 0)
+		room_301_anim3();
+	if (aainfo[4]._active != 0)
+		room_301_anim4();
+	if (aainfo[1]._active != 0)
+		room_301_anim2();
+	if (aainfo[5]._active != 0)
+		room_301_anim5();
+	if (global[walker_converse_now] != 0)
+		global_anim3(scratch._a6, &global[g008]);
 }
 
 static void room_301_pre_parser() {
+	if (player_parse(13, 25, 0))
+		player.walk_off_edge_to_room = 205;
+
+	if (player_parse(13, 38, 0))
+		player.walk_off_edge_to_room = 305;
+
+	if (player_parse(13, 40, 0)) {
+		if (global[g064] != 0)
+			player.walk_off_edge_to_room = 322;
+		else
+			player.walk_off_edge_to_room = 307;
+	}
+
+	if (player_parse(13, 44, 0))
+		player.walk_off_edge_to_room = 322;
 }
 
 static void room_301_parser() {
+	if (global[walker_converse_state] != 0) {
+		player.commands_allowed = 0;
+		digi_play_build_ii('c', 1, 1);
+		player.command_ready = 0;
+		return;
+	}
+
+	if (player_parse(114, 83, 0)) {
+		player.commands_allowed = 0;
+		global[g135] = -1;
+		scratch._8c = 5;
+		scratch._a8 = 1;
+		player.command_ready = 0;
+		return;
+	}
+
+	if (player_parse(114, 146, 0)) {
+		player.commands_allowed = 0;
+		global[g135] = -1;
+		scratch._8c = 5;
+		scratch._a8 = 2;
+		player.command_ready = 0;
+		return;
+	}
+
+	if (player_parse(13, 35, 0)) {
+		new_room = 302;
+		player.command_ready = 0;
+		return;
+	}
+
+	if (global[player_selected_object] >= 0) {
+		player.command_ready = 0;
+		return;
+	}
+
+	if (player_parse(126, 163, 0)) {
+		global[g154] = 2;
+		player.commands_allowed = 0;
+		player.walker_visible = 0;
+		scratch._9e = kernel_run_animation_talk('b', 2, 0);
+		kernel_position_anim(scratch._9e, player.x, player.y, player.scale, player.depth);
+		kernel_synch(KERNEL_ANIM, scratch._9e, KERNEL_PLAYER, 0);
+		digi_play_build_ii('b', 2, 1);
+		scratch._a4 = 400;
+		scratch._aa = -1;
+		player.command_ready = 0;
+		return;
+	}
+
+	if (player_parse(126, 101, 0)) {
+		global[g154] = 2;
+		player.commands_allowed = 0;
+		player.walker_visible = 0;
+		scratch._9e = kernel_run_animation_talk('b', 4, 0);
+		kernel_position_anim(scratch._9e, player.x, player.y, player.scale, player.depth);
+		kernel_synch(KERNEL_ANIM, scratch._9e, KERNEL_PLAYER, 0);
+		digi_play_build_ii('b', 2, 1);
+		scratch._a4 = 300;
+		scratch._aa = -1;
+		player.command_ready = 0;
+		return;
+	}
 }
 
 void room_301_synchronize(Common::Serializer &s) {
 	for (int16 &v : scratch.sprite)    s.syncAsSint16LE(v);
 	for (int16 &v : scratch.sequence)  s.syncAsSint16LE(v);
 	for (int16 &v : scratch.animation) s.syncAsSint16LE(v);
+	for (AnimationInfo &ai : scratch.animation_info) ai.synchronize(s);
+	s.syncAsSint16LE(scratch._8c);
+	s.syncAsSint16LE(scratch._8e);
+	s.syncAsSint16LE(scratch._90);
+	s.syncAsSint16LE(scratch._92);
+	s.syncAsSint16LE(scratch._94);
+	s.syncAsSint16LE(scratch._96);
+	s.syncAsSint16LE(scratch._98);
+	s.syncAsSint16LE(scratch._9a);
+	s.syncAsSint16LE(scratch._9c);
+	s.syncAsSint16LE(scratch._9e);
+	s.syncAsSint16LE(scratch._a0);
+	s.syncAsSint16LE(scratch._a2);
+	s.syncAsSint16LE(scratch._a4);
+	s.syncAsSint16LE(scratch._a6);
+	s.syncAsSint16LE(scratch._a8);
+	s.syncAsSint16LE(scratch._aa);
 }
 
 void room_301_preload() {



From noreply at scummvm.org  Mon Jun  8 08:48:12 2026
From: noreply at scummvm.org (ScummVM-Translations)
Date: Mon,  8 Jun 2026 08:48:12 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 69ed8864e5f518b279cc6642f68746ad18864b0d
Message-ID: <20260608084812.902967005CC@ron.scummvm.net>

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

Summary:
69ed8864e5 I18N: Update translation (Spanish)


Commit: 69ed8864e5f518b279cc6642f68746ad18864b0d
    https://github.com/scummvm/scummvm/commit/69ed8864e5f518b279cc6642f68746ad18864b0d
Author: IlDucci (pajaroloco_2 at hotmail.com)
Date: 2026-06-08T08:48:05Z

Commit Message:
I18N: Update translation (Spanish)

Currently translated at 100.0% (3206 of 3206 strings)

Changed paths:
    po/es_ES.po


diff --git a/po/es_ES.po b/po/es_ES.po
index 066f553be8b..7f69ffb80e2 100644
--- a/po/es_ES.po
+++ b/po/es_ES.po
@@ -8,7 +8,7 @@ msgstr ""
 "Project-Id-Version: ScummVM 1.4.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
 "POT-Creation-Date: 2026-06-07 22:09+0000\n"
-"PO-Revision-Date: 2026-06-03 08:30+0000\n"
+"PO-Revision-Date: 2026-06-08 08:48+0000\n"
 "Last-Translator: IlDucci <pajaroloco_2 at hotmail.com>\n"
 "Language-Team: Spanish <https://translations.scummvm.org/projects/scummvm/"
 "scummvm/es/>\n"
@@ -13692,16 +13692,12 @@ msgid "All levels will be available without requiring passwords"
 msgstr "Desbloquea todos los niveles sin necesitar contrase??as"
 
 #: engines/scumm/metaengine.cpp:900
-#, fuzzy
-#| msgid "No date saved"
 msgid "No damage"
-msgstr "No hay fecha guardada"
+msgstr "Sin da??os"
 
 #: engines/scumm/metaengine.cpp:901
-#, fuzzy
-#| msgid "Disable save menu"
 msgid "Disable player damage"
-msgstr "Desactivar men?? de guardado"
+msgstr "Desactiva los da??os recibidos por el jugador"
 
 #: engines/scumm/metaengine.cpp:1061
 msgid "Down left"
@@ -13769,10 +13765,8 @@ msgid "Fire / select"
 msgstr "Disparar/Seleccionar"
 
 #: engines/scumm/metaengine.cpp:1198
-#, fuzzy
-#| msgid "Skip / menu back"
 msgid "Walk / menu back"
-msgstr "Omitir/Volver (men??s)"
+msgstr "Andar/Volver (men??s)"
 
 #: engines/scumm/metaengine.cpp:1203
 msgid "Skip / menu back"



From noreply at scummvm.org  Mon Jun  8 08:48:30 2026
From: noreply at scummvm.org (ScummVM-Translations)
Date: Mon,  8 Jun 2026 08:48:30 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm-web master ->
 f51e85c63cca72f058531a86e943a014ead62e1f
Message-ID: <20260608084830.EDA997005CC@ron.scummvm.net>

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

Summary:
f51e85c63c L10N: Update translation (Spanish) (Strings)


Commit: f51e85c63cca72f058531a86e943a014ead62e1f
    https://github.com/scummvm/scummvm-web/commit/f51e85c63cca72f058531a86e943a014ead62e1f
Author: IlDucci (pajaroloco_2 at hotmail.com)
Date: 2026-06-08T08:48:25Z

Commit Message:
L10N: Update translation (Spanish) (Strings)

Currently translated at 100.0% (173 of 173 strings)

Changed paths:
    data/es/strings.json


diff --git a/data/es/strings.json b/data/es/strings.json
index 86bbc34b..77660fe1 100644
--- a/data/es/strings.json
+++ b/data/es/strings.json
@@ -177,5 +177,6 @@
     "compatibilityDetailsDetails": "M??s informaci??n",
     "downloadsBadgeTitle": "Descargar ScummVM {version}",
     "menuMainFreewareGames": "Juegos y complementos ??freeware??",
-    "compatibilityDetailsChartColScummVmId": "ID de ScummVM"
+    "compatibilityDetailsChartColScummVmId": "ID de ScummVM",
+    "menuMainDumper": "Asistente de volcado"
 }



From noreply at scummvm.org  Mon Jun  8 11:04:13 2026
From: noreply at scummvm.org (dreammaster)
Date: Mon,  8 Jun 2026 11:04:13 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 f89f33ada9abbed530497962f6e6bce2635ca7b0
Message-ID: <20260608110413.984617005CC@ron.scummvm.net>

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

Summary:
a2937c6433 MADS: DRAGONSPHERE: Implement difficulty selection dialog
9053adf4fd MADS: DRAGONSPHERE: Implemented original game menu
0c14fffaa5 MADS: DRAGONSPHERE: Original save/load dialogs
a7119861d5 MADS: DRAGONSPHERE: Game options dialog
79728624fb MADS: DRAGONSPHERE: Game score dialog
f89f33ada9 MADS: DRAGONSPHERE: CDROM options dialog


Commit: a2937c6433d95bd76d08af9d0f45f77b63c32563
    https://github.com/scummvm/scummvm/commit/a2937c6433d95bd76d08af9d0f45f77b63c32563
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-06-08T21:03:25+10:00

Commit Message:
MADS: DRAGONSPHERE: Implement difficulty selection dialog

Changed paths:
    engines/mads/madsv2/core/game.cpp
    engines/mads/madsv2/dragonsphere/mads/quotes.h
    engines/mads/madsv2/dragonsphere/menus.cpp


diff --git a/engines/mads/madsv2/core/game.cpp b/engines/mads/madsv2/core/game.cpp
index d0d4fd24b75..d3cb1fc3718 100644
--- a/engines/mads/madsv2/core/game.cpp
+++ b/engines/mads/madsv2/core/game.cpp
@@ -1245,7 +1245,8 @@ void game_control() {
 		// Get difficulty level if new game
 		if (!kernel.teleported_in && (game.difficulty == -1)) {
 			// Difficulty menu
-			if (g_engine->getGameID() == GType_Phantom)
+			int gameId = g_engine->getGameID();
+			if (gameId == GType_Phantom || gameId == GType_Dragonsphere)
 				kernel.activate_menu = GAME_DIFFICULTY_MENU;
 			game_exec_function(game_menu_routine);
 			if (!game.going)
diff --git a/engines/mads/madsv2/dragonsphere/mads/quotes.h b/engines/mads/madsv2/dragonsphere/mads/quotes.h
index 0e4f55ce069..8e6671dbb09 100644
--- a/engines/mads/madsv2/dragonsphere/mads/quotes.h
+++ b/engines/mads/madsv2/dragonsphere/mads/quotes.h
@@ -29,6 +29,9 @@ namespace MADSV2 {
 namespace Dragonsphere {
 
 enum {
+	quote_difficulty_title    =  49,
+	quote_difficulty_item1    =  50,
+	quote_difficulty_item2    =  51,
 	quote_poem_one_how        =  74,
 	quote_poem_one_thou       =  75,
 	quote_poem_one_soft       =  76,
diff --git a/engines/mads/madsv2/dragonsphere/menus.cpp b/engines/mads/madsv2/dragonsphere/menus.cpp
index 9fe53d0397f..ac9b1ebe0a1 100644
--- a/engines/mads/madsv2/dragonsphere/menus.cpp
+++ b/engines/mads/madsv2/dragonsphere/menus.cpp
@@ -32,6 +32,7 @@
 #include "mads/madsv2/core/text.h"
 #include "mads/madsv2/dragonsphere/menus.h"
 #include "mads/madsv2/dragonsphere/global.h"
+#include "mads/madsv2/dragonsphere/mads/quotes.h"
 
 namespace MADS {
 namespace MADSV2 {
@@ -40,7 +41,7 @@ namespace Dragonsphere {
 #define SAVE_MENU_PIXEL_WIDTH           200
 #define MAX_SAVES_ON_SCREEN             10
 
-#define SPACE_BETWEEN                   -1
+#define SPACE_BETWEEN                   1
 
 #define MAIN_MENU_ITEM_WIDTH            140
 #define MAIN_MENU_FORCE_WIDTH           160
@@ -91,6 +92,15 @@ static int global_restore(int id) {
 }
 
 void global_menu_system_init() {
+	menu_quotes = quote_load(
+		1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
+		11, 12, 13, 15, 17, 18, 14, 16, 19, 20,
+		21, 22, 23, 24, 25, 26, 27, 28, 29, 30,
+		31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
+		41, 42, 43, 44, 45, 46, 47, 48, 49, 50,
+		51, 52, 53, 54, 55, 56, 57, 58, 59, 60,
+		61, 0
+	);
 }
 
 void global_menu_system_shutdown() {
@@ -396,7 +406,6 @@ static void global_menu_options() {
 }
 
 static void global_menu_difficulty() {
-#if 0
 	PopupItem *easy_item;
 	PopupItem *result;
 
@@ -415,7 +424,6 @@ static void global_menu_difficulty() {
 		POPUP_CENTER, POPUP_FILL, MAIN_MENU_ITEM_WIDTH, 0,
 		0, 0, 0);
 
-
 	popup_width_force(DIFFICULTY_MENU_FORCE_WIDTH);
 
 	game_menu_popup->cancel_item = NULL;
@@ -445,7 +453,6 @@ static void global_menu_difficulty() {
 	}
 
 	popup_dialog_destroy();
-#endif
 }
 
 static void global_menu_main() {


Commit: 9053adf4fd09c8694f273aa971aa8aadf9bbdc48
    https://github.com/scummvm/scummvm/commit/9053adf4fd09c8694f273aa971aa8aadf9bbdc48
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-06-08T21:03:25+10:00

Commit Message:
MADS: DRAGONSPHERE: Implemented original game menu

Changed paths:
    engines/mads/mads.h
    engines/mads/madsv2/dragonsphere/mads/quotes.h
    engines/mads/madsv2/dragonsphere/menus.cpp
    engines/mads/metaengine.cpp


diff --git a/engines/mads/mads.h b/engines/mads/mads.h
index 17f5733c734..b591b1ea824 100644
--- a/engines/mads/mads.h
+++ b/engines/mads/mads.h
@@ -83,6 +83,7 @@ public:
 	uint32 getGameID() const;
 	uint32 getGameFeatures() const;
 	bool isDemo() const;
+	bool isCDROM() const;
 
 	int getRandomNumber(int maxNumber);
 	int getRandomNumber(int minNumber, int maxNumber);
diff --git a/engines/mads/madsv2/dragonsphere/mads/quotes.h b/engines/mads/madsv2/dragonsphere/mads/quotes.h
index 8e6671dbb09..a1e0c326866 100644
--- a/engines/mads/madsv2/dragonsphere/mads/quotes.h
+++ b/engines/mads/madsv2/dragonsphere/mads/quotes.h
@@ -29,6 +29,13 @@ namespace MADSV2 {
 namespace Dragonsphere {
 
 enum {
+	quote_main_item1          =  12,
+	quote_main_item2          =  13,
+	quote_main_item6          =  14,
+	quote_main_item3          =  15,
+	quote_main_item7          =  16,
+	quote_main_item4          =  17,
+	quote_main_item5          =  18,
 	quote_difficulty_title    =  49,
 	quote_difficulty_item1    =  50,
 	quote_difficulty_item2    =  51,
diff --git a/engines/mads/madsv2/dragonsphere/menus.cpp b/engines/mads/madsv2/dragonsphere/menus.cpp
index ac9b1ebe0a1..1a64bf4ab38 100644
--- a/engines/mads/madsv2/dragonsphere/menus.cpp
+++ b/engines/mads/madsv2/dragonsphere/menus.cpp
@@ -456,41 +456,48 @@ static void global_menu_difficulty() {
 }
 
 static void global_menu_main() {
-#if 0
 	PopupItem *save_item;
 	PopupItem *restore_item;
 	PopupItem *score_item;
 	PopupItem *options_item;
+	PopupItem *cdrom_item;
 	PopupItem *resume_item;
 	PopupItem *quit_item;
 	PopupItem *result;
 
+	box_param.menu_text_y_offset = 3;
+	box_param.menu_text_x_bonus = 0;
+
 	popup_dialog_create(game_menu_popup, GAME_DIALOG_HEAP, 20);
 
 	popup_sprite(box_param.logo, 1, POPUP_CENTER, POPUP_FILL);
 	popup_blank(6);
 
 	save_item = popup_menu(menu_quote(quote_main_item1),
-		POPUP_CENTER, POPUP_FILL, MAIN_MENU_ITEM_WIDTH, 0,
-		0, 0, 0);
+		POPUP_CENTER, POPUP_FILL, MAIN_MENU_ITEM_WIDTH, 0, 0, 0, 0);
 
 	popup_blank(SPACE_BETWEEN);
 
 	restore_item = popup_menu(menu_quote(quote_main_item2),
-		POPUP_CENTER, POPUP_FILL, MAIN_MENU_ITEM_WIDTH, 0,
-		0, 0, 0);
+		POPUP_CENTER, POPUP_FILL, MAIN_MENU_ITEM_WIDTH, 0, 0, 0, 0);
 
 	popup_blank(SPACE_BETWEEN);
 
 	score_item = popup_menu(menu_quote(quote_main_item6),
-		POPUP_CENTER, POPUP_FILL, MAIN_MENU_ITEM_WIDTH, 0,
-		0, 0, 0);
+		POPUP_CENTER, POPUP_FILL, MAIN_MENU_ITEM_WIDTH, 0, 0, 0, 0);
 
 	popup_blank(SPACE_BETWEEN);
 
 	options_item = popup_menu(menu_quote(quote_main_item3),
-		POPUP_CENTER, POPUP_FILL, MAIN_MENU_ITEM_WIDTH, 0,
-		0, 0, 0);
+		POPUP_CENTER, POPUP_FILL, MAIN_MENU_ITEM_WIDTH, 0, 0, 0, 0);
+
+	cdrom_item = nullptr;
+	if (g_engine->isCDROM()) {
+		popup_blank(SPACE_BETWEEN);
+
+		cdrom_item = popup_menu(menu_quote(quote_main_item7),
+			POPUP_CENTER, POPUP_FILL, MAIN_MENU_ITEM_WIDTH, 0, 0, 0, 0);
+	}
 
 	popup_blank(SPACE_BETWEEN);
 
@@ -517,6 +524,8 @@ static void global_menu_main() {
 		kernel.activate_menu = GAME_OPTIONS_MENU;
 	} else if (result == score_item) {
 		kernel.activate_menu = GAME_SCORE_MENU;
+	} else if (result == cdrom_item) {
+		kernel.activate_menu = GAME_CD_MENU;
 	} else {
 		kernel.activate_menu = GAME_NO_MENU;
 	}
@@ -551,7 +560,6 @@ static void global_menu_main() {
 	default:
 		break;
 	}
-#endif
 }
 
 void global_game_menu() {
diff --git a/engines/mads/metaengine.cpp b/engines/mads/metaengine.cpp
index 56dea28a691..6f562eb1d5e 100644
--- a/engines/mads/metaengine.cpp
+++ b/engines/mads/metaengine.cpp
@@ -167,6 +167,10 @@ bool MADSEngine::isDemo() const {
 	return (bool)(_gameDescription->desc.flags & ADGF_DEMO);
 }
 
+bool MADSEngine::isCDROM() const {
+	return (bool)(_gameDescription->desc.flags & ADGF_CD);
+}
+
 Common::Language MADSEngine::getLanguage() const {
 	return _gameDescription->desc.language;
 }


Commit: 0c14fffaa51d025e57c6ca83007e67b2ec8acaad
    https://github.com/scummvm/scummvm/commit/0c14fffaa51d025e57c6ca83007e67b2ec8acaad
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-06-08T21:03:25+10:00

Commit Message:
MADS: DRAGONSPHERE: Original save/load dialogs

Changed paths:
    engines/mads/detection_tables.h
    engines/mads/madsv2/dragonsphere/mads/quotes.h
    engines/mads/madsv2/dragonsphere/menus.cpp


diff --git a/engines/mads/detection_tables.h b/engines/mads/detection_tables.h
index 357e2713cc3..3db67c365c2 100644
--- a/engines/mads/detection_tables.h
+++ b/engines/mads/detection_tables.h
@@ -156,7 +156,7 @@ static const MADSGameDescription gameDescriptions[] = {
 			Common::EN_ANY,
 			Common::kPlatformDOS,
 			ADGF_UNSTABLE,
-			GUIO4(GUIO_NOMIDI, GAMEOPTION_EASY_MOUSE, GAMEOPTION_COPY_PROTECTION, GAMEOPTION_ORIGINAL_SAVELOAD)
+			GUIO3(GUIO_NOMIDI, GAMEOPTION_EASY_MOUSE, GAMEOPTION_ORIGINAL_SAVELOAD)
 		},
 		GType_Phantom,
 		0
@@ -171,7 +171,7 @@ static const MADSGameDescription gameDescriptions[] = {
 			Common::EN_ANY,
 			Common::kPlatformDOS,
 			ADGF_UNSTABLE | ADGF_CD,
-			GUIO4(GUIO_NOMIDI, GAMEOPTION_EASY_MOUSE, GAMEOPTION_COPY_PROTECTION, GAMEOPTION_ORIGINAL_SAVELOAD)
+			GUIO3(GUIO_NOMIDI, GAMEOPTION_EASY_MOUSE, GAMEOPTION_ORIGINAL_SAVELOAD)
 		},
 		GType_Phantom,
 		0
@@ -186,7 +186,7 @@ static const MADSGameDescription gameDescriptions[] = {
 			Common::EN_ANY,
 			Common::kPlatformDOS,
 			ADGF_UNSTABLE | ADGF_DEMO,
-			GUIO5(GUIO_NOMIDI, GAMEOPTION_EASY_MOUSE, GAMEOPTION_NAUGHTY_MODE, GAMEOPTION_COPY_PROTECTION, GAMEOPTION_ORIGINAL_SAVELOAD)
+			GUIO3(GUIO_NOMIDI, GAMEOPTION_EASY_MOUSE, GAMEOPTION_ORIGINAL_SAVELOAD)
 		},
 		GType_Phantom,
 		0
@@ -201,7 +201,7 @@ static const MADSGameDescription gameDescriptions[] = {
 			Common::EN_ANY,
 			Common::kPlatformDOS,
 			ADGF_UNSTABLE,
-			GUIO1(GAMEOPTION_EASY_MOUSE)
+			GUIO3(GUIO_NOMIDI, GAMEOPTION_EASY_MOUSE, GAMEOPTION_ORIGINAL_SAVELOAD)
 		},
 		GType_Dragonsphere,
 		0
@@ -217,7 +217,7 @@ static const MADSGameDescription gameDescriptions[] = {
 			Common::EN_ANY,
 			Common::kPlatformDOS,
 			GF_INSTALLER | ADGF_UNSTABLE,
-			GUIO1(GAMEOPTION_EASY_MOUSE)
+			GUIO3(GUIO_NOMIDI, GAMEOPTION_EASY_MOUSE, GAMEOPTION_ORIGINAL_SAVELOAD)
 		},
 		GType_Dragonsphere,
 		0
@@ -232,7 +232,7 @@ static const MADSGameDescription gameDescriptions[] = {
 			Common::EN_ANY,
 			Common::kPlatformDOS,
 			ADGF_UNSTABLE | ADGF_DEMO,
-			GUIO1(GAMEOPTION_EASY_MOUSE)
+			GUIO3(GUIO_NOMIDI, GAMEOPTION_EASY_MOUSE, GAMEOPTION_ORIGINAL_SAVELOAD)
 		},
 		GType_Dragonsphere,
 		0
diff --git a/engines/mads/madsv2/dragonsphere/mads/quotes.h b/engines/mads/madsv2/dragonsphere/mads/quotes.h
index a1e0c326866..fafc8365a4d 100644
--- a/engines/mads/madsv2/dragonsphere/mads/quotes.h
+++ b/engines/mads/madsv2/dragonsphere/mads/quotes.h
@@ -29,6 +29,12 @@ namespace MADSV2 {
 namespace Dragonsphere {
 
 enum {
+	quote_menu_cancel         =   2,
+	quote_menu_save           =   3,
+	quote_menu_restore        =   4,
+	quote_menu_clear          =   5,
+	quote_menu_empty          =   9,
+	quote_menu_unnamed        =  10,
 	quote_main_item1          =  12,
 	quote_main_item2          =  13,
 	quote_main_item6          =  14,
@@ -36,6 +42,8 @@ enum {
 	quote_main_item7          =  16,
 	quote_main_item4          =  17,
 	quote_main_item5          =  18,
+	quote_save_title          =  47,
+	quote_restore_title       =  48,
 	quote_difficulty_title    =  49,
 	quote_difficulty_item1    =  50,
 	quote_difficulty_item2    =  51,
diff --git a/engines/mads/madsv2/dragonsphere/menus.cpp b/engines/mads/madsv2/dragonsphere/menus.cpp
index 1a64bf4ab38..e27bd6b2d42 100644
--- a/engines/mads/madsv2/dragonsphere/menus.cpp
+++ b/engines/mads/madsv2/dragonsphere/menus.cpp
@@ -127,7 +127,6 @@ static void global_menu_score() {
 }
 
 static void global_menu_save_restore(int save) {
-#if 0
 	int status = -1;
 	int selection;
 	char *save_game_name;
@@ -235,7 +234,6 @@ static void global_menu_save_restore(int save) {
 	if (status >= 0) {
 		global_alert(status);
 	}
-#endif
 }
 
 static void global_menu_options() {


Commit: a7119861d5435b365f32e9aeb8b41b3d8c73517d
    https://github.com/scummvm/scummvm/commit/a7119861d5435b365f32e9aeb8b41b3d8c73517d
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-06-08T21:03:26+10:00

Commit Message:
MADS: DRAGONSPHERE: Game options dialog

Changed paths:
    engines/mads/madsv2/dragonsphere/mads/quotes.h
    engines/mads/madsv2/dragonsphere/menus.cpp


diff --git a/engines/mads/madsv2/dragonsphere/mads/quotes.h b/engines/mads/madsv2/dragonsphere/mads/quotes.h
index fafc8365a4d..f0ed295795a 100644
--- a/engines/mads/madsv2/dragonsphere/mads/quotes.h
+++ b/engines/mads/madsv2/dragonsphere/mads/quotes.h
@@ -29,6 +29,7 @@ namespace MADSV2 {
 namespace Dragonsphere {
 
 enum {
+	quote_menu_done           =   1,
 	quote_menu_cancel         =   2,
 	quote_menu_save           =   3,
 	quote_menu_restore        =   4,
@@ -42,6 +43,26 @@ enum {
 	quote_main_item7          =  16,
 	quote_main_item4          =  17,
 	quote_main_item5          =  18,
+	quote_options_item1       =  20,
+	quote_options_item2       =  21,
+	quote_options_item3       =  22,
+	quote_options_item4       =  23,
+	quote_options_item5       =  24,
+	quote_options_item6       =  25,
+	quote_options_item1a      =  26,
+	quote_options_item1b      =  27,
+	quote_options_item2a      =  28,
+	quote_options_item2b      =  29,
+	quote_options_item3a      =  30,
+	quote_options_item3b      =  31,
+	quote_options_item4a      =  32,
+	quote_options_item4b      =  33,
+	quote_options_item4c      =  34,
+	quote_options_item5a      =  35,
+	quote_options_item5b      =  36,
+	quote_options_item5c      =  37,
+	quote_options_item6a      =  38,
+	quote_options_item6b      =  39,
 	quote_save_title          =  47,
 	quote_restore_title       =  48,
 	quote_difficulty_title    =  49,
diff --git a/engines/mads/madsv2/dragonsphere/menus.cpp b/engines/mads/madsv2/dragonsphere/menus.cpp
index e27bd6b2d42..c23462941fd 100644
--- a/engines/mads/madsv2/dragonsphere/menus.cpp
+++ b/engines/mads/madsv2/dragonsphere/menus.cpp
@@ -237,7 +237,6 @@ static void global_menu_save_restore(int save) {
 }
 
 static void global_menu_options() {
-#if 0
 	int initial_1, initial_2, initial_3;
 	int initial_4, initial_5, initial_6;
 	int former_music;
@@ -400,7 +399,6 @@ static void global_menu_options() {
 	}
 
 	popup_dialog_destroy();
-#endif
 }
 
 static void global_menu_difficulty() {


Commit: 79728624fb47220798c6758587914e54504603ff
    https://github.com/scummvm/scummvm/commit/79728624fb47220798c6758587914e54504603ff
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-06-08T21:03:26+10:00

Commit Message:
MADS: DRAGONSPHERE: Game score dialog

Changed paths:
    engines/mads/madsv2/dragonsphere/menus.cpp


diff --git a/engines/mads/madsv2/dragonsphere/menus.cpp b/engines/mads/madsv2/dragonsphere/menus.cpp
index c23462941fd..5ca0808e21c 100644
--- a/engines/mads/madsv2/dragonsphere/menus.cpp
+++ b/engines/mads/madsv2/dragonsphere/menus.cpp
@@ -124,6 +124,37 @@ static void global_alert(int status) {
 }
 
 static void global_menu_score() {
+	int score;
+
+	score = global[player_score];
+	if (score > 250) score = 250;
+
+	text_index[0] = score;
+	text_index[1] = 250;
+
+	if (score <= 25) {
+		text_index[2] = 1;          /* Stage sweeper */
+	} else if (score <= 50) {
+		text_index[2] = 2;          /* Dresser */
+	} else if (score <= 75) {
+		text_index[2] = 3;          /* Usher */
+	} else if (score <= 100) {
+		text_index[2] = 4;          /* Stagehand */
+	} else if (score <= 150) {
+		text_index[2] = 5;          /* Chorus Member */
+	} else if (score <= 200) {
+		text_index[2] = 6;          /* Supporting Player */
+	} else if (score <= 249) {
+		text_index[2] = 7;          /* Star Player */
+	} else if (score <= 250) {
+		text_index[2] = 8;          /* Director */
+	} else {
+		text_index[2] = 9;          /* Bug Finder! */
+	}
+
+	text_show(99);
+
+	kernel.activate_menu = GAME_NO_MENU;
 }
 
 static void global_menu_save_restore(int save) {


Commit: f89f33ada9abbed530497962f6e6bce2635ca7b0
    https://github.com/scummvm/scummvm/commit/f89f33ada9abbed530497962f6e6bce2635ca7b0
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-06-08T21:03:35+10:00

Commit Message:
MADS: DRAGONSPHERE: CDROM options dialog

Changed paths:
    engines/mads/detection_tables.h
    engines/mads/madsv2/dragonsphere/mads/quotes.h
    engines/mads/madsv2/dragonsphere/menus.cpp


diff --git a/engines/mads/detection_tables.h b/engines/mads/detection_tables.h
index 3db67c365c2..fb4e4d042eb 100644
--- a/engines/mads/detection_tables.h
+++ b/engines/mads/detection_tables.h
@@ -192,6 +192,21 @@ static const MADSGameDescription gameDescriptions[] = {
 		0
 	},
 
+	{
+		// Dragonsphere DOS English CD
+		{
+			"dragonsphere",
+			"CD",
+			AD_ENTRY2s("section1.hag", "2770e441d296be5e806194693eebd95a", 2061199, "speech.idx", "80d36ba3ca88e9d078cc19a7678aeb87", 1250),
+			Common::EN_ANY,
+			Common::kPlatformDOS,
+			ADGF_UNSTABLE | ADGF_CD,
+			GUIO3(GUIO_NOMIDI, GAMEOPTION_EASY_MOUSE, GAMEOPTION_ORIGINAL_SAVELOAD)
+		},
+		GType_Dragonsphere,
+		0
+	},
+
 	{
 		// Dragonsphere DOS English
 		{
diff --git a/engines/mads/madsv2/dragonsphere/mads/quotes.h b/engines/mads/madsv2/dragonsphere/mads/quotes.h
index f0ed295795a..5bb247d59fe 100644
--- a/engines/mads/madsv2/dragonsphere/mads/quotes.h
+++ b/engines/mads/madsv2/dragonsphere/mads/quotes.h
@@ -63,6 +63,12 @@ enum {
 	quote_options_item5c      =  37,
 	quote_options_item6a      =  38,
 	quote_options_item6b      =  39,
+	quote_cdrom_item1         =  41,
+	quote_cdrom_item2         =  42,
+	quote_cdrom_item1a        =  43,
+	quote_cdrom_item1b        =  44,
+	quote_cdrom_item2a        =  45,
+	quote_cdrom_item2b        =  46,
 	quote_save_title          =  47,
 	quote_restore_title       =  48,
 	quote_difficulty_title    =  49,
diff --git a/engines/mads/madsv2/dragonsphere/menus.cpp b/engines/mads/madsv2/dragonsphere/menus.cpp
index 5ca0808e21c..39ebcac1388 100644
--- a/engines/mads/madsv2/dragonsphere/menus.cpp
+++ b/engines/mads/madsv2/dragonsphere/menus.cpp
@@ -52,6 +52,9 @@ namespace Dragonsphere {
 #define OPTIONS_MENU_FORCE_WIDTH        170
 #define OPTIONS_MENU_OFF_CENTER         10
 
+#define CDROM_MENU_ITEM_WIDTH           160
+#define CDROM_MENU_FORCE_WIDTH          170
+#define CDROM_MENU_OFF_CENTER           10
 
 #define SAVE_SUCCESSFUL                 1
 #define RESTORE_SUCCESSFUL              2
@@ -482,6 +485,106 @@ static void global_menu_difficulty() {
 	popup_dialog_destroy();
 }
 
+static void global_menu_cdrom() {
+	int initial_1, initial_2;
+	int former_speech;
+	int former_text;
+	PopupItem *speech_item;
+	PopupItem *text_item;
+	PopupItem *done_button, *cancel_button;
+	PopupItem *result;
+
+	global_unload_config_parameters();
+
+	box_param.menu_text_y_offset = 3;
+	box_param.menu_text_x_bonus = 0;
+
+	popup_dialog_create(game_menu_popup, GAME_DIALOG_HEAP, 20);
+
+	popup_sprite(box_param.logo, 1, POPUP_CENTER, POPUP_FILL);
+	popup_blank(2);
+
+	former_speech = config_file.speech_flag;
+	former_text = config_file.show_speech_boxes;
+
+	initial_1 = config_file.speech_flag ? 0 : 1;
+	initial_2 = config_file.show_speech_boxes ? 0 : 1;
+
+	speech_item = popup_menu(menu_quote(quote_cdrom_item1),
+		POPUP_CENTER, POPUP_FILL, CDROM_MENU_ITEM_WIDTH,
+		CDROM_MENU_OFF_CENTER, 2, 40, initial_1);
+	popup_menu_option(speech_item, menu_quote(quote_cdrom_item1a));
+	popup_menu_option(speech_item, menu_quote(quote_cdrom_item1b));
+
+	popup_blank(SPACE_BETWEEN);
+
+	text_item = popup_menu(menu_quote(quote_cdrom_item2),
+		POPUP_CENTER, POPUP_FILL, CDROM_MENU_ITEM_WIDTH,
+		CDROM_MENU_OFF_CENTER, 2, 40, initial_2);
+	popup_menu_option(text_item, menu_quote(quote_cdrom_item2a));
+	popup_menu_option(text_item, menu_quote(quote_cdrom_item2b));
+
+	done_button = popup_button(menu_quote(quote_menu_done), POPUP_LEFT);
+	cancel_button = popup_cancel_button(menu_quote(quote_menu_cancel));
+
+	popup_width_force(CDROM_MENU_FORCE_WIDTH);
+
+	result = popup_execute();
+
+	kernel.activate_menu = GAME_MAIN_MENU;
+
+	if (result == cancel_button) {
+		switch (game_menu_popup->key) {
+		case alt_x_key:
+		case ctrl_x_key:
+		case alt_q_key:
+		case ctrl_q_key:
+			game.going = false;
+			kernel.activate_menu = GAME_NO_MENU;
+			break;
+
+		case f1_key:
+			kernel.activate_menu = GAME_MAIN_MENU;
+			break;
+
+		case f2_key:
+			kernel.activate_menu = GAME_SAVE_MENU;
+			break;
+
+		case f3_key:
+			kernel.activate_menu = GAME_RESTORE_MENU;
+			break;
+
+		case f4_key:
+			kernel.activate_menu = GAME_SCORE_MENU;
+			break;
+
+		case f5_key:
+			kernel.activate_menu = GAME_MAIN_MENU;
+			break;
+
+		default:
+			kernel.activate_menu = GAME_MAIN_MENU;
+			break;
+		}
+	}
+
+	if (result == done_button) {
+		config_file.speech_flag = !speech_item->list->picked_element;
+		config_file.show_speech_boxes = !text_item->list->picked_element;
+
+		global_write_config_file();
+		global_load_config_parameters();
+
+		kernel.activate_menu = GAME_NO_MENU;
+	}
+
+	if (former_speech != config_file.speech_flag)
+		speech_all_off();
+
+	popup_dialog_destroy();
+}
+
 static void global_menu_main() {
 	PopupItem *save_item;
 	PopupItem *restore_item;
@@ -634,6 +737,9 @@ void global_game_menu() {
 		case GAME_SCORE_MENU:
 			global_menu_score();
 			break;
+		case GAME_CD_MENU:
+			global_menu_cdrom();
+			break;
 		default:
 			kernel.activate_menu = GAME_NO_MENU;
 			break;



From noreply at scummvm.org  Mon Jun  8 11:04:16 2026
From: noreply at scummvm.org (ScummVM-Translations)
Date: Mon,  8 Jun 2026 11:04:16 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 e220a31ba99e8afebbbe12c000a6989cae8c7a91
Message-ID: <20260608110416.84DA87005CF@ron.scummvm.net>

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

Summary:
e220a31ba9 I18N: Update translations templates


Commit: e220a31ba99e8afebbbe12c000a6989cae8c7a91
    https://github.com/scummvm/scummvm/commit/e220a31ba99e8afebbbe12c000a6989cae8c7a91
Author: Weblate (noreply at weblate.org)
Date: 2026-06-08T11:04:12Z

Commit Message:
I18N: Update translations templates

Changed paths:
    po/scummvm.pot


diff --git a/po/scummvm.pot b/po/scummvm.pot
index 3af0a30942f..f49545f372e 100644
--- a/po/scummvm.pot
+++ b/po/scummvm.pot
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2026.2.1git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-07 22:09+0000\n"
+"POT-Creation-Date: 2026-06-08 11:04+0000\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL at ADDRESS>\n"
 "Language-Team: LANGUAGE <LL at li.org>\n"
@@ -3603,7 +3603,7 @@ msgstr ""
 #: engines/hugo/metaengine.cpp:134 engines/illusions/metaengine.cpp:94
 #: engines/kyra/engine/eobcommon.cpp:369 engines/kyra/engine/lol.cpp:505
 #: engines/lure/metaengine.cpp:177 engines/made/metaengine.cpp:137
-#: engines/mads/metaengine.cpp:318 engines/mm/metaengine.cpp:186
+#: engines/mads/metaengine.cpp:322 engines/mm/metaengine.cpp:186
 #: engines/mtropolis/metaengine.cpp:161 engines/nancy/input.cpp:181
 #: engines/parallaction/metaengine.cpp:138 engines/saga/metaengine.cpp:278
 #: engines/sludge/keymapper_tables.h:561 engines/sludge/keymapper_tables.h:580
@@ -3636,7 +3636,7 @@ msgstr ""
 #: engines/hopkins/metaengine.cpp:217 engines/hugo/metaengine.cpp:141
 #: engines/illusions/metaengine.cpp:101 engines/kyra/engine/eobcommon.cpp:370
 #: engines/kyra/engine/lol.cpp:506 engines/lure/metaengine.cpp:183
-#: engines/made/metaengine.cpp:144 engines/mads/metaengine.cpp:324
+#: engines/made/metaengine.cpp:144 engines/mads/metaengine.cpp:328
 #: engines/mm/metaengine.cpp:192 engines/mtropolis/metaengine.cpp:167
 #: engines/nancy/input.cpp:188 engines/parallaction/metaengine.cpp:144
 #: engines/saga/metaengine.cpp:289 engines/sludge/keymapper_tables.h:567
@@ -5635,7 +5635,7 @@ msgstr ""
 #: engines/hugo/metaengine.cpp:129 engines/hypno/metaengine.cpp:128
 #: engines/illusions/metaengine.cpp:89 engines/lab/metaengine.cpp:149
 #: engines/lure/metaengine.cpp:168 engines/made/metaengine.cpp:132
-#: engines/mads/metaengine.cpp:312 engines/neverhood/metaengine.cpp:185
+#: engines/mads/metaengine.cpp:316 engines/neverhood/metaengine.cpp:185
 #: engines/ngi/metaengine.cpp:115 engines/petka/metaengine.cpp:113
 #: engines/prince/metaengine.cpp:227 engines/private/metaengine.cpp:127
 #: engines/queen/metaengine.cpp:146 engines/sherlock/metaengine.cpp:313
@@ -6163,7 +6163,7 @@ msgstr ""
 #: engines/hypno/metaengine.cpp:267 engines/hypno/metaengine.cpp:311
 #: engines/illusions/metaengine.cpp:90 engines/lab/metaengine.cpp:152
 #: engines/lure/metaengine.cpp:169 engines/made/metaengine.cpp:133
-#: engines/mads/metaengine.cpp:313 engines/neverhood/metaengine.cpp:186
+#: engines/mads/metaengine.cpp:317 engines/neverhood/metaengine.cpp:186
 #: engines/queen/metaengine.cpp:147 engines/saga/metaengine.cpp:268
 #: engines/sludge/keymapper_tables.h:33 engines/stark/metaengine.cpp:185
 #: engines/sword1/metaengine.cpp:267 engines/tinsel/metaengine.cpp:241
@@ -6612,7 +6612,7 @@ msgstr ""
 #: engines/bbvs/metaengine.cpp:111 engines/cruise/metaengine.cpp:223
 #: engines/freescape/movement.cpp:107 engines/got/metaengine.cpp:78
 #: engines/hugo/metaengine.cpp:197 engines/lure/metaengine.cpp:209
-#: engines/mads/metaengine.cpp:330 engines/mm/mm1/metaengine.cpp:42
+#: engines/mads/metaengine.cpp:334 engines/mm/mm1/metaengine.cpp:42
 #: engines/mm/mm1/metaengine.cpp:48 engines/saga/metaengine.cpp:328
 #: engines/tinsel/metaengine.cpp:258 engines/tsage/metaengine.cpp:190
 #: engines/tucker/metaengine.cpp:221 engines/twine/metaengine.cpp:388
@@ -6899,7 +6899,7 @@ msgstr ""
 #: engines/freescape/games/eclipse/eclipse.cpp:330
 #: engines/hopkins/metaengine.cpp:236 engines/hugo/metaengine.cpp:165
 #: engines/lure/metaengine.cpp:191 engines/made/metaengine.cpp:192
-#: engines/mads/metaengine.cpp:343 engines/mohawk/myst.cpp:583
+#: engines/mads/metaengine.cpp:347 engines/mohawk/myst.cpp:583
 #: engines/mohawk/riven.cpp:864 engines/pegasus/pegasus.cpp:2825
 #: engines/petka/metaengine.cpp:198 engines/prince/metaengine.cpp:243
 #: engines/saga/metaengine.cpp:442 engines/sherlock/metaengine.cpp:462
@@ -6975,7 +6975,7 @@ msgstr ""
 #: engines/freescape/games/dark/dark.cpp:236
 #: engines/freescape/games/driller/driller.cpp:129
 #: engines/freescape/games/eclipse/eclipse.cpp:340
-#: engines/mads/metaengine.cpp:399 engines/phoenixvr/metaengine.cpp:80
+#: engines/mads/metaengine.cpp:403 engines/phoenixvr/metaengine.cpp:80
 #: engines/saga/metaengine.cpp:437 engines/sherlock/metaengine.cpp:359
 #: engines/sherlock/metaengine.cpp:618 engines/sherlock/metaengine.cpp:852
 #: engines/stark/metaengine.cpp:280 engines/sword1/metaengine.cpp:295
@@ -9239,7 +9239,7 @@ msgid "Repeat last line"
 msgstr ""
 
 #: engines/hugo/metaengine.cpp:172 engines/lure/metaengine.cpp:197
-#: engines/mads/metaengine.cpp:349 engines/tsage/metaengine.cpp:247
+#: engines/mads/metaengine.cpp:353 engines/tsage/metaengine.cpp:247
 msgid "Restore game"
 msgstr ""
 
@@ -9428,7 +9428,7 @@ msgstr ""
 msgid "Direction keymappings"
 msgstr ""
 
-#: engines/hypno/metaengine.cpp:316 engines/mads/metaengine.cpp:387
+#: engines/hypno/metaengine.cpp:316 engines/mads/metaengine.cpp:391
 #: engines/tsage/metaengine.cpp:259
 msgid "Show credits"
 msgstr ""
@@ -10014,39 +10014,39 @@ msgstr ""
 msgid "Naughty game mode"
 msgstr ""
 
-#: engines/mads/metaengine.cpp:314
+#: engines/mads/metaengine.cpp:318
 msgid "Start menu keymappings"
 msgstr ""
 
-#: engines/mads/metaengine.cpp:337
+#: engines/mads/metaengine.cpp:341
 msgid "Open game menu"
 msgstr ""
 
-#: engines/mads/metaengine.cpp:355
+#: engines/mads/metaengine.cpp:359
 msgid "Scroll bar up"
 msgstr ""
 
-#: engines/mads/metaengine.cpp:361
+#: engines/mads/metaengine.cpp:365
 msgid "Scroll bar down"
 msgstr ""
 
-#: engines/mads/metaengine.cpp:369
+#: engines/mads/metaengine.cpp:373
 msgid "Start game"
 msgstr ""
 
-#: engines/mads/metaengine.cpp:375
+#: engines/mads/metaengine.cpp:379
 msgid "Resume game"
 msgstr ""
 
-#: engines/mads/metaengine.cpp:381
+#: engines/mads/metaengine.cpp:385
 msgid "Show intro"
 msgstr ""
 
-#: engines/mads/metaengine.cpp:393
+#: engines/mads/metaengine.cpp:397
 msgid "Show quotes"
 msgstr ""
 
-#: engines/mads/metaengine.cpp:406
+#: engines/mads/metaengine.cpp:410
 msgid "Restart animation"
 msgstr ""
 



From noreply at scummvm.org  Mon Jun  8 11:04:48 2026
From: noreply at scummvm.org (ScummVM-Translations)
Date: Mon,  8 Jun 2026 11:04:48 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 ad31b5aec2f8d4abb018c214562bc52adb892d29
Message-ID: <20260608110448.15D707005CC@ron.scummvm.net>

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

Summary:
ad31b5aec2 I18N: Update translation files


Commit: ad31b5aec2f8d4abb018c214562bc52adb892d29
    https://github.com/scummvm/scummvm/commit/ad31b5aec2f8d4abb018c214562bc52adb892d29
Author: Weblate (noreply at weblate.org)
Date: 2026-06-08T11:04:28Z

Commit Message:
I18N: Update translation files

Updated by "Update PO files to match POT (msgmerge)" hook in Weblate.

Translation: ScummVM/scummvm
Translate-URL: https://translations.scummvm.org/projects/scummvm/scummvm/

Changed paths:
    po/ar.po
    po/be-tarask.po
    po/be_BY.po
    po/ca_ES.po
    po/cs_CZ.po
    po/da.po
    po/de_DE.po
    po/el.po
    po/es_ES.po
    po/eu.po
    po/fi_FI.po
    po/fr_FR.po
    po/gl_ES.po
    po/he.po
    po/hi.po
    po/hu_HU.po
    po/it_IT.po
    po/ja.po
    po/ka.po
    po/ko.po
    po/nb_NO.po
    po/nl_NL.po
    po/nn_NO.po
    po/pl_PL.po
    po/pt_BR.po
    po/pt_PT.po
    po/ro.po
    po/ru_RU.po
    po/sv_SE.po
    po/tr.po
    po/uk_UA.po
    po/zh.po
    po/zh_Hans.po
    po/zh_Hant.po


diff --git a/po/ar.po b/po/ar.po
index 78a2fa78d82..70e14492376 100644
--- a/po/ar.po
+++ b/po/ar.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-07 22:09+0000\n"
+"POT-Creation-Date: 2026-06-08 11:04+0000\n"
 "PO-Revision-Date: 2026-02-24 22:26+0000\n"
 "Last-Translator: Mohamed Shaaban <mohmedbashamedo at hotmail.com>\n"
 "Language-Team: Arabic <https://translations.scummvm.org/projects/scummvm/"
@@ -3928,7 +3928,7 @@ msgstr "?????????? ???????????? ???????????? ????????????????????"
 #: engines/hugo/metaengine.cpp:134 engines/illusions/metaengine.cpp:94
 #: engines/kyra/engine/eobcommon.cpp:369 engines/kyra/engine/lol.cpp:505
 #: engines/lure/metaengine.cpp:177 engines/made/metaengine.cpp:137
-#: engines/mads/metaengine.cpp:318 engines/mm/metaengine.cpp:186
+#: engines/mads/metaengine.cpp:322 engines/mm/metaengine.cpp:186
 #: engines/mtropolis/metaengine.cpp:161 engines/nancy/input.cpp:181
 #: engines/parallaction/metaengine.cpp:138 engines/saga/metaengine.cpp:278
 #: engines/sludge/keymapper_tables.h:561 engines/sludge/keymapper_tables.h:580
@@ -3965,7 +3965,7 @@ msgstr "?????????? ????????????"
 #: engines/hopkins/metaengine.cpp:217 engines/hugo/metaengine.cpp:141
 #: engines/illusions/metaengine.cpp:101 engines/kyra/engine/eobcommon.cpp:370
 #: engines/kyra/engine/lol.cpp:506 engines/lure/metaengine.cpp:183
-#: engines/made/metaengine.cpp:144 engines/mads/metaengine.cpp:324
+#: engines/made/metaengine.cpp:144 engines/mads/metaengine.cpp:328
 #: engines/mm/metaengine.cpp:192 engines/mtropolis/metaengine.cpp:167
 #: engines/nancy/input.cpp:188 engines/parallaction/metaengine.cpp:144
 #: engines/saga/metaengine.cpp:289 engines/sludge/keymapper_tables.h:567
@@ -6135,7 +6135,7 @@ msgstr "???????? ???? ???????? ??????????????..."
 #: engines/hugo/metaengine.cpp:129 engines/hypno/metaengine.cpp:128
 #: engines/illusions/metaengine.cpp:89 engines/lab/metaengine.cpp:149
 #: engines/lure/metaengine.cpp:168 engines/made/metaengine.cpp:132
-#: engines/mads/metaengine.cpp:312 engines/neverhood/metaengine.cpp:185
+#: engines/mads/metaengine.cpp:316 engines/neverhood/metaengine.cpp:185
 #: engines/ngi/metaengine.cpp:115 engines/petka/metaengine.cpp:113
 #: engines/prince/metaengine.cpp:227 engines/private/metaengine.cpp:127
 #: engines/queen/metaengine.cpp:146 engines/sherlock/metaengine.cpp:313
@@ -6707,7 +6707,7 @@ msgstr ""
 #: engines/hypno/metaengine.cpp:267 engines/hypno/metaengine.cpp:311
 #: engines/illusions/metaengine.cpp:90 engines/lab/metaengine.cpp:152
 #: engines/lure/metaengine.cpp:169 engines/made/metaengine.cpp:133
-#: engines/mads/metaengine.cpp:313 engines/neverhood/metaengine.cpp:186
+#: engines/mads/metaengine.cpp:317 engines/neverhood/metaengine.cpp:186
 #: engines/queen/metaengine.cpp:147 engines/saga/metaengine.cpp:268
 #: engines/sludge/keymapper_tables.h:33 engines/stark/metaengine.cpp:185
 #: engines/sword1/metaengine.cpp:267 engines/tinsel/metaengine.cpp:241
@@ -7250,7 +7250,7 @@ msgstr ""
 #: engines/bbvs/metaengine.cpp:111 engines/cruise/metaengine.cpp:223
 #: engines/freescape/movement.cpp:107 engines/got/metaengine.cpp:78
 #: engines/hugo/metaengine.cpp:197 engines/lure/metaengine.cpp:209
-#: engines/mads/metaengine.cpp:330 engines/mm/mm1/metaengine.cpp:42
+#: engines/mads/metaengine.cpp:334 engines/mm/mm1/metaengine.cpp:42
 #: engines/mm/mm1/metaengine.cpp:48 engines/saga/metaengine.cpp:328
 #: engines/tinsel/metaengine.cpp:258 engines/tsage/metaengine.cpp:190
 #: engines/tucker/metaengine.cpp:221 engines/twine/metaengine.cpp:388
@@ -7572,7 +7572,7 @@ msgstr "?????????? ?????????? ????????????????"
 #: engines/freescape/games/eclipse/eclipse.cpp:330
 #: engines/hopkins/metaengine.cpp:236 engines/hugo/metaengine.cpp:165
 #: engines/lure/metaengine.cpp:191 engines/made/metaengine.cpp:192
-#: engines/mads/metaengine.cpp:343 engines/mohawk/myst.cpp:583
+#: engines/mads/metaengine.cpp:347 engines/mohawk/myst.cpp:583
 #: engines/mohawk/riven.cpp:864 engines/pegasus/pegasus.cpp:2825
 #: engines/petka/metaengine.cpp:198 engines/prince/metaengine.cpp:243
 #: engines/saga/metaengine.cpp:442 engines/sherlock/metaengine.cpp:462
@@ -7654,7 +7654,7 @@ msgstr "?????????? ???????????? ????????????"
 #: engines/freescape/games/dark/dark.cpp:236
 #: engines/freescape/games/driller/driller.cpp:129
 #: engines/freescape/games/eclipse/eclipse.cpp:340
-#: engines/mads/metaengine.cpp:399 engines/phoenixvr/metaengine.cpp:80
+#: engines/mads/metaengine.cpp:403 engines/phoenixvr/metaengine.cpp:80
 #: engines/saga/metaengine.cpp:437 engines/sherlock/metaengine.cpp:359
 #: engines/sherlock/metaengine.cpp:618 engines/sherlock/metaengine.cpp:852
 #: engines/stark/metaengine.cpp:280 engines/sword1/metaengine.cpp:295
@@ -10429,7 +10429,7 @@ msgid "Repeat last line"
 msgstr "?????????? ?????????? ?????? ??????"
 
 #: engines/hugo/metaengine.cpp:172 engines/lure/metaengine.cpp:197
-#: engines/mads/metaengine.cpp:349 engines/tsage/metaengine.cpp:247
+#: engines/mads/metaengine.cpp:353 engines/tsage/metaengine.cpp:247
 #, fuzzy
 #| msgid "Restore game:"
 msgid "Restore game"
@@ -10656,7 +10656,7 @@ msgstr "???????????? ????????????"
 msgid "Direction keymappings"
 msgstr "???????????? ????????????"
 
-#: engines/hypno/metaengine.cpp:316 engines/mads/metaengine.cpp:387
+#: engines/hypno/metaengine.cpp:316 engines/mads/metaengine.cpp:391
 #: engines/tsage/metaengine.cpp:259
 msgid "Show credits"
 msgstr "?????????? ????????????????????"
@@ -11396,55 +11396,55 @@ msgstr "?????????? ???????????? ????????????????"
 msgid "Naughty game mode"
 msgstr "?????? ???????????? ??????????????"
 
-#: engines/mads/metaengine.cpp:314
+#: engines/mads/metaengine.cpp:318
 #, fuzzy
 #| msgid "Game Options"
 msgid "Start menu keymappings"
 msgstr "???????????? ????????????"
 
-#: engines/mads/metaengine.cpp:337
+#: engines/mads/metaengine.cpp:341
 #, fuzzy
 #| msgid "Open main menu"
 msgid "Open game menu"
 msgstr "???????? ?????????????? ????????????????"
 
-#: engines/mads/metaengine.cpp:355
+#: engines/mads/metaengine.cpp:359
 #, fuzzy
 #| msgid "Scroll up"
 msgid "Scroll bar up"
 msgstr "?????????? ?????? ????????"
 
-#: engines/mads/metaengine.cpp:361
+#: engines/mads/metaengine.cpp:365
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Scroll bar down"
 msgstr "?????? ???????????? ??????????"
 
-#: engines/mads/metaengine.cpp:369
+#: engines/mads/metaengine.cpp:373
 #, fuzzy
 #| msgid "Restore game:"
 msgid "Start game"
 msgstr "?????????????? ????????:"
 
-#: engines/mads/metaengine.cpp:375
+#: engines/mads/metaengine.cpp:379
 #, fuzzy
 #| msgid "Restore game:"
 msgid "Resume game"
 msgstr "?????????????? ????????:"
 
-#: engines/mads/metaengine.cpp:381
+#: engines/mads/metaengine.cpp:385
 #, fuzzy
 #| msgid "Show hints"
 msgid "Show intro"
 msgstr "?????????????? ????????????"
 
-#: engines/mads/metaengine.cpp:393
+#: engines/mads/metaengine.cpp:397
 #, fuzzy
 #| msgid "Speech Options"
 msgid "Show quotes"
 msgstr "???????????? ????????????"
 
-#: engines/mads/metaengine.cpp:406
+#: engines/mads/metaengine.cpp:410
 #, fuzzy
 #| msgid "Transitions:"
 msgid "Restart animation"
diff --git a/po/be-tarask.po b/po/be-tarask.po
index 2d3be656900..98978f867f2 100644
--- a/po/be-tarask.po
+++ b/po/be-tarask.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-07 22:09+0000\n"
+"POT-Creation-Date: 2026-06-08 11:04+0000\n"
 "PO-Revision-Date: 2023-02-26 15:01+0000\n"
 "Last-Translator: Kastu?? K. <oakum2334 at gmail.com>\n"
 "Language-Team: Belarusian (Tara??kievica) <https://translations.scummvm.org/"
@@ -3884,7 +3884,7 @@ msgstr "?????????????????????? ?????????????? ????????????"
 #: engines/hugo/metaengine.cpp:134 engines/illusions/metaengine.cpp:94
 #: engines/kyra/engine/eobcommon.cpp:369 engines/kyra/engine/lol.cpp:505
 #: engines/lure/metaengine.cpp:177 engines/made/metaengine.cpp:137
-#: engines/mads/metaengine.cpp:318 engines/mm/metaengine.cpp:186
+#: engines/mads/metaengine.cpp:322 engines/mm/metaengine.cpp:186
 #: engines/mtropolis/metaengine.cpp:161 engines/nancy/input.cpp:181
 #: engines/parallaction/metaengine.cpp:138 engines/saga/metaengine.cpp:278
 #: engines/sludge/keymapper_tables.h:561 engines/sludge/keymapper_tables.h:580
@@ -3921,7 +3921,7 @@ msgstr "?????????????? ????????????"
 #: engines/hopkins/metaengine.cpp:217 engines/hugo/metaengine.cpp:141
 #: engines/illusions/metaengine.cpp:101 engines/kyra/engine/eobcommon.cpp:370
 #: engines/kyra/engine/lol.cpp:506 engines/lure/metaengine.cpp:183
-#: engines/made/metaengine.cpp:144 engines/mads/metaengine.cpp:324
+#: engines/made/metaengine.cpp:144 engines/mads/metaengine.cpp:328
 #: engines/mm/metaengine.cpp:192 engines/mtropolis/metaengine.cpp:167
 #: engines/nancy/input.cpp:188 engines/parallaction/metaengine.cpp:144
 #: engines/saga/metaengine.cpp:289 engines/sludge/keymapper_tables.h:567
@@ -6068,7 +6068,7 @@ msgstr "?????????????????? ????????????..."
 #: engines/hugo/metaengine.cpp:129 engines/hypno/metaengine.cpp:128
 #: engines/illusions/metaengine.cpp:89 engines/lab/metaengine.cpp:149
 #: engines/lure/metaengine.cpp:168 engines/made/metaengine.cpp:132
-#: engines/mads/metaengine.cpp:312 engines/neverhood/metaengine.cpp:185
+#: engines/mads/metaengine.cpp:316 engines/neverhood/metaengine.cpp:185
 #: engines/ngi/metaengine.cpp:115 engines/petka/metaengine.cpp:113
 #: engines/prince/metaengine.cpp:227 engines/private/metaengine.cpp:127
 #: engines/queen/metaengine.cpp:146 engines/sherlock/metaengine.cpp:313
@@ -6643,7 +6643,7 @@ msgstr ""
 #: engines/hypno/metaengine.cpp:267 engines/hypno/metaengine.cpp:311
 #: engines/illusions/metaengine.cpp:90 engines/lab/metaengine.cpp:152
 #: engines/lure/metaengine.cpp:169 engines/made/metaengine.cpp:133
-#: engines/mads/metaengine.cpp:313 engines/neverhood/metaengine.cpp:186
+#: engines/mads/metaengine.cpp:317 engines/neverhood/metaengine.cpp:186
 #: engines/queen/metaengine.cpp:147 engines/saga/metaengine.cpp:268
 #: engines/sludge/keymapper_tables.h:33 engines/stark/metaengine.cpp:185
 #: engines/sword1/metaengine.cpp:267 engines/tinsel/metaengine.cpp:241
@@ -7191,7 +7191,7 @@ msgstr ""
 #: engines/bbvs/metaengine.cpp:111 engines/cruise/metaengine.cpp:223
 #: engines/freescape/movement.cpp:107 engines/got/metaengine.cpp:78
 #: engines/hugo/metaengine.cpp:197 engines/lure/metaengine.cpp:209
-#: engines/mads/metaengine.cpp:330 engines/mm/mm1/metaengine.cpp:42
+#: engines/mads/metaengine.cpp:334 engines/mm/mm1/metaengine.cpp:42
 #: engines/mm/mm1/metaengine.cpp:48 engines/saga/metaengine.cpp:328
 #: engines/tinsel/metaengine.cpp:258 engines/tsage/metaengine.cpp:190
 #: engines/tucker/metaengine.cpp:221 engines/twine/metaengine.cpp:388
@@ -7513,7 +7513,7 @@ msgstr "???????????????? ??????"
 #: engines/freescape/games/eclipse/eclipse.cpp:330
 #: engines/hopkins/metaengine.cpp:236 engines/hugo/metaengine.cpp:165
 #: engines/lure/metaengine.cpp:191 engines/made/metaengine.cpp:192
-#: engines/mads/metaengine.cpp:343 engines/mohawk/myst.cpp:583
+#: engines/mads/metaengine.cpp:347 engines/mohawk/myst.cpp:583
 #: engines/mohawk/riven.cpp:864 engines/pegasus/pegasus.cpp:2825
 #: engines/petka/metaengine.cpp:198 engines/prince/metaengine.cpp:243
 #: engines/saga/metaengine.cpp:442 engines/sherlock/metaengine.cpp:462
@@ -7595,7 +7595,7 @@ msgstr "???????????????? ??????????????"
 #: engines/freescape/games/dark/dark.cpp:236
 #: engines/freescape/games/driller/driller.cpp:129
 #: engines/freescape/games/eclipse/eclipse.cpp:340
-#: engines/mads/metaengine.cpp:399 engines/phoenixvr/metaengine.cpp:80
+#: engines/mads/metaengine.cpp:403 engines/phoenixvr/metaengine.cpp:80
 #: engines/saga/metaengine.cpp:437 engines/sherlock/metaengine.cpp:359
 #: engines/sherlock/metaengine.cpp:618 engines/sherlock/metaengine.cpp:852
 #: engines/stark/metaengine.cpp:280 engines/sword1/metaengine.cpp:295
@@ -10384,7 +10384,7 @@ msgid "Repeat last line"
 msgstr "???????????????????? ?????????????? ????????????"
 
 #: engines/hugo/metaengine.cpp:172 engines/lure/metaengine.cpp:197
-#: engines/mads/metaengine.cpp:349 engines/tsage/metaengine.cpp:247
+#: engines/mads/metaengine.cpp:353 engines/tsage/metaengine.cpp:247
 #, fuzzy
 #| msgid "Restore game:"
 msgid "Restore game"
@@ -10611,7 +10611,7 @@ msgstr "~??~?????????? ????????????"
 msgid "Direction keymappings"
 msgstr "~??~?????????? ????????????"
 
-#: engines/hypno/metaengine.cpp:316 engines/mads/metaengine.cpp:387
+#: engines/hypno/metaengine.cpp:316 engines/mads/metaengine.cpp:391
 #: engines/tsage/metaengine.cpp:259
 msgid "Show credits"
 msgstr "???????????????? ??????????"
@@ -11355,55 +11355,55 @@ msgstr "?????????????????? ?????????????????? ????????????"
 msgid "Naughty game mode"
 msgstr "?????????????? ?????????? ????????????"
 
-#: engines/mads/metaengine.cpp:314
+#: engines/mads/metaengine.cpp:318
 #, fuzzy
 #| msgid "Game Options"
 msgid "Start menu keymappings"
 msgstr "~??~?????????? ????????????"
 
-#: engines/mads/metaengine.cpp:337
+#: engines/mads/metaengine.cpp:341
 #, fuzzy
 #| msgid "Open main menu"
 msgid "Open game menu"
 msgstr "?????????????? ???????????????? ????????"
 
-#: engines/mads/metaengine.cpp:355
+#: engines/mads/metaengine.cpp:359
 #, fuzzy
 #| msgid "Scroll up"
 msgid "Scroll bar up"
 msgstr "?????????????????????????? ??????????"
 
-#: engines/mads/metaengine.cpp:361
+#: engines/mads/metaengine.cpp:365
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Scroll bar down"
 msgstr "?????????????????????????? ????????"
 
-#: engines/mads/metaengine.cpp:369
+#: engines/mads/metaengine.cpp:373
 #, fuzzy
 #| msgid "Restore game:"
 msgid "Start game"
 msgstr "???????????????? ????????????:"
 
-#: engines/mads/metaengine.cpp:375
+#: engines/mads/metaengine.cpp:379
 #, fuzzy
 #| msgid "Restore game:"
 msgid "Resume game"
 msgstr "???????????????? ????????????:"
 
-#: engines/mads/metaengine.cpp:381
+#: engines/mads/metaengine.cpp:385
 #, fuzzy
 #| msgid "Show hints"
 msgid "Show intro"
 msgstr "???????????????? ????????????????"
 
-#: engines/mads/metaengine.cpp:393
+#: engines/mads/metaengine.cpp:397
 #, fuzzy
 #| msgid "Speech Options"
 msgid "Show quotes"
 msgstr "???????????? ????????????"
 
-#: engines/mads/metaengine.cpp:406
+#: engines/mads/metaengine.cpp:410
 #, fuzzy
 #| msgid "Transitions:"
 msgid "Restart animation"
diff --git a/po/be_BY.po b/po/be_BY.po
index 74a5366bd78..da058c3ee1d 100644
--- a/po/be_BY.po
+++ b/po/be_BY.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-07 22:09+0000\n"
+"POT-Creation-Date: 2026-06-08 11:04+0000\n"
 "PO-Revision-Date: 2021-03-21 08:24+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Belarusian <https://translations.scummvm.org/projects/scummvm/"
@@ -3889,7 +3889,7 @@ msgstr "?????????????????? ???????????? ???? ??????????????????"
 #: engines/hugo/metaengine.cpp:134 engines/illusions/metaengine.cpp:94
 #: engines/kyra/engine/eobcommon.cpp:369 engines/kyra/engine/lol.cpp:505
 #: engines/lure/metaengine.cpp:177 engines/made/metaengine.cpp:137
-#: engines/mads/metaengine.cpp:318 engines/mm/metaengine.cpp:186
+#: engines/mads/metaengine.cpp:322 engines/mm/metaengine.cpp:186
 #: engines/mtropolis/metaengine.cpp:161 engines/nancy/input.cpp:181
 #: engines/parallaction/metaengine.cpp:138 engines/saga/metaengine.cpp:278
 #: engines/sludge/keymapper_tables.h:561 engines/sludge/keymapper_tables.h:580
@@ -3926,7 +3926,7 @@ msgstr "???????????????? ????????????????"
 #: engines/hopkins/metaengine.cpp:217 engines/hugo/metaengine.cpp:141
 #: engines/illusions/metaengine.cpp:101 engines/kyra/engine/eobcommon.cpp:370
 #: engines/kyra/engine/lol.cpp:506 engines/lure/metaengine.cpp:183
-#: engines/made/metaengine.cpp:144 engines/mads/metaengine.cpp:324
+#: engines/made/metaengine.cpp:144 engines/mads/metaengine.cpp:328
 #: engines/mm/metaengine.cpp:192 engines/mtropolis/metaengine.cpp:167
 #: engines/nancy/input.cpp:188 engines/parallaction/metaengine.cpp:144
 #: engines/saga/metaengine.cpp:289 engines/sludge/keymapper_tables.h:567
@@ -6077,7 +6077,7 @@ msgstr "???????????????? ????????????????????..."
 #: engines/hugo/metaengine.cpp:129 engines/hypno/metaengine.cpp:128
 #: engines/illusions/metaengine.cpp:89 engines/lab/metaengine.cpp:149
 #: engines/lure/metaengine.cpp:168 engines/made/metaengine.cpp:132
-#: engines/mads/metaengine.cpp:312 engines/neverhood/metaengine.cpp:185
+#: engines/mads/metaengine.cpp:316 engines/neverhood/metaengine.cpp:185
 #: engines/ngi/metaengine.cpp:115 engines/petka/metaengine.cpp:113
 #: engines/prince/metaengine.cpp:227 engines/private/metaengine.cpp:127
 #: engines/queen/metaengine.cpp:146 engines/sherlock/metaengine.cpp:313
@@ -6649,7 +6649,7 @@ msgstr ""
 #: engines/hypno/metaengine.cpp:267 engines/hypno/metaengine.cpp:311
 #: engines/illusions/metaengine.cpp:90 engines/lab/metaengine.cpp:152
 #: engines/lure/metaengine.cpp:169 engines/made/metaengine.cpp:133
-#: engines/mads/metaengine.cpp:313 engines/neverhood/metaengine.cpp:186
+#: engines/mads/metaengine.cpp:317 engines/neverhood/metaengine.cpp:186
 #: engines/queen/metaengine.cpp:147 engines/saga/metaengine.cpp:268
 #: engines/sludge/keymapper_tables.h:33 engines/stark/metaengine.cpp:185
 #: engines/sword1/metaengine.cpp:267 engines/tinsel/metaengine.cpp:241
@@ -7174,7 +7174,7 @@ msgstr ""
 #: engines/bbvs/metaengine.cpp:111 engines/cruise/metaengine.cpp:223
 #: engines/freescape/movement.cpp:107 engines/got/metaengine.cpp:78
 #: engines/hugo/metaengine.cpp:197 engines/lure/metaengine.cpp:209
-#: engines/mads/metaengine.cpp:330 engines/mm/mm1/metaengine.cpp:42
+#: engines/mads/metaengine.cpp:334 engines/mm/mm1/metaengine.cpp:42
 #: engines/mm/mm1/metaengine.cpp:48 engines/saga/metaengine.cpp:328
 #: engines/tinsel/metaengine.cpp:258 engines/tsage/metaengine.cpp:190
 #: engines/tucker/metaengine.cpp:221 engines/twine/metaengine.cpp:388
@@ -7490,7 +7490,7 @@ msgstr "?????????????????????? ??????"
 #: engines/freescape/games/eclipse/eclipse.cpp:330
 #: engines/hopkins/metaengine.cpp:236 engines/hugo/metaengine.cpp:165
 #: engines/lure/metaengine.cpp:191 engines/made/metaengine.cpp:192
-#: engines/mads/metaengine.cpp:343 engines/mohawk/myst.cpp:583
+#: engines/mads/metaengine.cpp:347 engines/mohawk/myst.cpp:583
 #: engines/mohawk/riven.cpp:864 engines/pegasus/pegasus.cpp:2825
 #: engines/petka/metaengine.cpp:198 engines/prince/metaengine.cpp:243
 #: engines/saga/metaengine.cpp:442 engines/sherlock/metaengine.cpp:462
@@ -7566,7 +7566,7 @@ msgstr ""
 #: engines/freescape/games/dark/dark.cpp:236
 #: engines/freescape/games/driller/driller.cpp:129
 #: engines/freescape/games/eclipse/eclipse.cpp:340
-#: engines/mads/metaengine.cpp:399 engines/phoenixvr/metaengine.cpp:80
+#: engines/mads/metaengine.cpp:403 engines/phoenixvr/metaengine.cpp:80
 #: engines/saga/metaengine.cpp:437 engines/sherlock/metaengine.cpp:359
 #: engines/sherlock/metaengine.cpp:618 engines/sherlock/metaengine.cpp:852
 #: engines/stark/metaengine.cpp:280 engines/sword1/metaengine.cpp:295
@@ -10280,7 +10280,7 @@ msgid "Repeat last line"
 msgstr "???????? ???? ????????????????"
 
 #: engines/hugo/metaengine.cpp:172 engines/lure/metaengine.cpp:197
-#: engines/mads/metaengine.cpp:349 engines/tsage/metaengine.cpp:247
+#: engines/mads/metaengine.cpp:353 engines/tsage/metaengine.cpp:247
 #, fuzzy
 #| msgid "Restore game:"
 msgid "Restore game"
@@ -10504,7 +10504,7 @@ msgstr "~??~??????????..."
 msgid "Direction keymappings"
 msgstr "~??~??????????..."
 
-#: engines/hypno/metaengine.cpp:316 engines/mads/metaengine.cpp:387
+#: engines/hypno/metaengine.cpp:316 engines/mads/metaengine.cpp:391
 #: engines/tsage/metaengine.cpp:259
 #, fuzzy
 msgid "Show credits"
@@ -11232,53 +11232,53 @@ msgstr "?????????????????? ?????????????????? ????????????"
 msgid "Naughty game mode"
 msgstr "?????????????? ?????????? ????????????"
 
-#: engines/mads/metaengine.cpp:314
+#: engines/mads/metaengine.cpp:318
 #, fuzzy
 msgid "Start menu keymappings"
 msgstr "~??~??????????..."
 
-#: engines/mads/metaengine.cpp:337
+#: engines/mads/metaengine.cpp:341
 #, fuzzy
 #| msgid "Open main menu"
 msgid "Open game menu"
 msgstr "?????????????? ???????????????? ????????"
 
-#: engines/mads/metaengine.cpp:355
+#: engines/mads/metaengine.cpp:359
 #, fuzzy
 #| msgid "Scroll up"
 msgid "Scroll bar up"
 msgstr "???????????????????? ??????????"
 
-#: engines/mads/metaengine.cpp:361
+#: engines/mads/metaengine.cpp:365
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Scroll bar down"
 msgstr "???????????????????? ????????"
 
-#: engines/mads/metaengine.cpp:369
+#: engines/mads/metaengine.cpp:373
 #, fuzzy
 #| msgid "Restore game:"
 msgid "Start game"
 msgstr "???????????????? ????????????:"
 
-#: engines/mads/metaengine.cpp:375
+#: engines/mads/metaengine.cpp:379
 #, fuzzy
 #| msgid "Restore game:"
 msgid "Resume game"
 msgstr "???????????????? ????????????:"
 
-#: engines/mads/metaengine.cpp:381
+#: engines/mads/metaengine.cpp:385
 #, fuzzy
 #| msgid "Show hints"
 msgid "Show intro"
 msgstr "???????????????? ????????????????"
 
-#: engines/mads/metaengine.cpp:393
+#: engines/mads/metaengine.cpp:397
 #, fuzzy
 msgid "Show quotes"
 msgstr "???????????? ????????????"
 
-#: engines/mads/metaengine.cpp:406
+#: engines/mads/metaengine.cpp:410
 #, fuzzy
 #| msgid "Transitions:"
 msgid "Restart animation"
diff --git a/po/ca_ES.po b/po/ca_ES.po
index 9f9796475a3..ae0476cfb19 100644
--- a/po/ca_ES.po
+++ b/po/ca_ES.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.6.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-07 22:09+0000\n"
+"POT-Creation-Date: 2026-06-08 11:04+0000\n"
 "PO-Revision-Date: 2026-05-26 04:22+0000\n"
 "Last-Translator: Xaviu <xaviu at xaviu.com>\n"
 "Language-Team: Catalan <https://translations.scummvm.org/projects/scummvm/"
@@ -3810,7 +3810,7 @@ msgstr "Assignaci?? de tecles per defecte del joc"
 #: engines/hugo/metaengine.cpp:134 engines/illusions/metaengine.cpp:94
 #: engines/kyra/engine/eobcommon.cpp:369 engines/kyra/engine/lol.cpp:505
 #: engines/lure/metaengine.cpp:177 engines/made/metaengine.cpp:137
-#: engines/mads/metaengine.cpp:318 engines/mm/metaengine.cpp:186
+#: engines/mads/metaengine.cpp:322 engines/mm/metaengine.cpp:186
 #: engines/mtropolis/metaengine.cpp:161 engines/nancy/input.cpp:181
 #: engines/parallaction/metaengine.cpp:138 engines/saga/metaengine.cpp:278
 #: engines/sludge/keymapper_tables.h:561 engines/sludge/keymapper_tables.h:580
@@ -3843,7 +3843,7 @@ msgstr "Clic central"
 #: engines/hopkins/metaengine.cpp:217 engines/hugo/metaengine.cpp:141
 #: engines/illusions/metaengine.cpp:101 engines/kyra/engine/eobcommon.cpp:370
 #: engines/kyra/engine/lol.cpp:506 engines/lure/metaengine.cpp:183
-#: engines/made/metaengine.cpp:144 engines/mads/metaengine.cpp:324
+#: engines/made/metaengine.cpp:144 engines/mads/metaengine.cpp:328
 #: engines/mm/metaengine.cpp:192 engines/mtropolis/metaengine.cpp:167
 #: engines/nancy/input.cpp:188 engines/parallaction/metaengine.cpp:144
 #: engines/saga/metaengine.cpp:289 engines/sludge/keymapper_tables.h:567
@@ -5879,7 +5879,7 @@ msgstr "Comprova si hi ha actualitzacions..."
 #: engines/hugo/metaengine.cpp:129 engines/hypno/metaengine.cpp:128
 #: engines/illusions/metaengine.cpp:89 engines/lab/metaengine.cpp:149
 #: engines/lure/metaengine.cpp:168 engines/made/metaengine.cpp:132
-#: engines/mads/metaengine.cpp:312 engines/neverhood/metaengine.cpp:185
+#: engines/mads/metaengine.cpp:316 engines/neverhood/metaengine.cpp:185
 #: engines/ngi/metaengine.cpp:115 engines/petka/metaengine.cpp:113
 #: engines/prince/metaengine.cpp:227 engines/private/metaengine.cpp:127
 #: engines/queen/metaengine.cpp:146 engines/sherlock/metaengine.cpp:313
@@ -6442,7 +6442,7 @@ msgstr "AGOS principal"
 #: engines/hypno/metaengine.cpp:267 engines/hypno/metaengine.cpp:311
 #: engines/illusions/metaengine.cpp:90 engines/lab/metaengine.cpp:152
 #: engines/lure/metaengine.cpp:169 engines/made/metaengine.cpp:133
-#: engines/mads/metaengine.cpp:313 engines/neverhood/metaengine.cpp:186
+#: engines/mads/metaengine.cpp:317 engines/neverhood/metaengine.cpp:186
 #: engines/queen/metaengine.cpp:147 engines/saga/metaengine.cpp:268
 #: engines/sludge/keymapper_tables.h:33 engines/stark/metaengine.cpp:185
 #: engines/sword1/metaengine.cpp:267 engines/tinsel/metaengine.cpp:241
@@ -6942,7 +6942,7 @@ msgstr "Truc de l'ull del soldat 714"
 #: engines/bbvs/metaengine.cpp:111 engines/cruise/metaengine.cpp:223
 #: engines/freescape/movement.cpp:107 engines/got/metaengine.cpp:78
 #: engines/hugo/metaengine.cpp:197 engines/lure/metaengine.cpp:209
-#: engines/mads/metaengine.cpp:330 engines/mm/mm1/metaengine.cpp:42
+#: engines/mads/metaengine.cpp:334 engines/mm/mm1/metaengine.cpp:42
 #: engines/mm/mm1/metaengine.cpp:48 engines/saga/metaengine.cpp:328
 #: engines/tinsel/metaengine.cpp:258 engines/tsage/metaengine.cpp:190
 #: engines/tucker/metaengine.cpp:221 engines/twine/metaengine.cpp:388
@@ -7247,7 +7247,7 @@ msgstr "Activa / desactiva la privadesa de la pista"
 #: engines/freescape/games/eclipse/eclipse.cpp:330
 #: engines/hopkins/metaengine.cpp:236 engines/hugo/metaengine.cpp:165
 #: engines/lure/metaengine.cpp:191 engines/made/metaengine.cpp:192
-#: engines/mads/metaengine.cpp:343 engines/mohawk/myst.cpp:583
+#: engines/mads/metaengine.cpp:347 engines/mohawk/myst.cpp:583
 #: engines/mohawk/riven.cpp:864 engines/pegasus/pegasus.cpp:2825
 #: engines/petka/metaengine.cpp:198 engines/prince/metaengine.cpp:243
 #: engines/saga/metaengine.cpp:442 engines/sherlock/metaengine.cpp:462
@@ -7323,7 +7323,7 @@ msgstr "Base de dades de pistes"
 #: engines/freescape/games/dark/dark.cpp:236
 #: engines/freescape/games/driller/driller.cpp:129
 #: engines/freescape/games/eclipse/eclipse.cpp:340
-#: engines/mads/metaengine.cpp:399 engines/phoenixvr/metaengine.cpp:80
+#: engines/mads/metaengine.cpp:403 engines/phoenixvr/metaengine.cpp:80
 #: engines/saga/metaengine.cpp:437 engines/sherlock/metaengine.cpp:359
 #: engines/sherlock/metaengine.cpp:618 engines/sherlock/metaengine.cpp:852
 #: engines/stark/metaengine.cpp:280 engines/sword1/metaengine.cpp:295
@@ -9689,7 +9689,7 @@ msgid "Repeat last line"
 msgstr "Repeteix l'??ltima l??nia"
 
 #: engines/hugo/metaengine.cpp:172 engines/lure/metaengine.cpp:197
-#: engines/mads/metaengine.cpp:349 engines/tsage/metaengine.cpp:247
+#: engines/mads/metaengine.cpp:353 engines/tsage/metaengine.cpp:247
 msgid "Restore game"
 msgstr "Recupera la partida"
 
@@ -9881,7 +9881,7 @@ msgstr "Assignacions de pausa"
 msgid "Direction keymappings"
 msgstr "Assignaci?? de tecles de direcci??"
 
-#: engines/hypno/metaengine.cpp:316 engines/mads/metaengine.cpp:387
+#: engines/hypno/metaengine.cpp:316 engines/mads/metaengine.cpp:391
 #: engines/tsage/metaengine.cpp:259
 msgid "Show credits"
 msgstr "Mostra els cr??dits"
@@ -10528,39 +10528,39 @@ msgstr "Interf??cie de joc animada"
 msgid "Naughty game mode"
 msgstr "Mode de joc pujat de to"
 
-#: engines/mads/metaengine.cpp:314
+#: engines/mads/metaengine.cpp:318
 msgid "Start menu keymappings"
 msgstr "Assignaci?? de tecles del men?? d'inici"
 
-#: engines/mads/metaengine.cpp:337
+#: engines/mads/metaengine.cpp:341
 msgid "Open game menu"
 msgstr "Obre el men?? del joc"
 
-#: engines/mads/metaengine.cpp:355
+#: engines/mads/metaengine.cpp:359
 msgid "Scroll bar up"
 msgstr "Despla??a la barra cap amunt"
 
-#: engines/mads/metaengine.cpp:361
+#: engines/mads/metaengine.cpp:365
 msgid "Scroll bar down"
 msgstr "Despla??a la barra cap avall"
 
-#: engines/mads/metaengine.cpp:369
+#: engines/mads/metaengine.cpp:373
 msgid "Start game"
 msgstr "Inicia la partida"
 
-#: engines/mads/metaengine.cpp:375
+#: engines/mads/metaengine.cpp:379
 msgid "Resume game"
 msgstr "Repr??n la partida"
 
-#: engines/mads/metaengine.cpp:381
+#: engines/mads/metaengine.cpp:385
 msgid "Show intro"
 msgstr "Mostra la introducci??"
 
-#: engines/mads/metaengine.cpp:393
+#: engines/mads/metaengine.cpp:397
 msgid "Show quotes"
 msgstr "Mostra les cites"
 
-#: engines/mads/metaengine.cpp:406
+#: engines/mads/metaengine.cpp:410
 msgid "Restart animation"
 msgstr "Repr??n animaci??"
 
diff --git a/po/cs_CZ.po b/po/cs_CZ.po
index 1d2a357c9e0..07e9b697706 100644
--- a/po/cs_CZ.po
+++ b/po/cs_CZ.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.7.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-07 22:09+0000\n"
+"POT-Creation-Date: 2026-06-08 11:04+0000\n"
 "PO-Revision-Date: 2026-02-09 20:22+0000\n"
 "Last-Translator: AsciiWolf <mail at asciiwolf.com>\n"
 "Language-Team: Czech <https://translations.scummvm.org/projects/scummvm/"
@@ -3877,7 +3877,7 @@ msgstr "V??choz?? rozlo??en?? kl??ves"
 #: engines/hugo/metaengine.cpp:134 engines/illusions/metaengine.cpp:94
 #: engines/kyra/engine/eobcommon.cpp:369 engines/kyra/engine/lol.cpp:505
 #: engines/lure/metaengine.cpp:177 engines/made/metaengine.cpp:137
-#: engines/mads/metaengine.cpp:318 engines/mm/metaengine.cpp:186
+#: engines/mads/metaengine.cpp:322 engines/mm/metaengine.cpp:186
 #: engines/mtropolis/metaengine.cpp:161 engines/nancy/input.cpp:181
 #: engines/parallaction/metaengine.cpp:138 engines/saga/metaengine.cpp:278
 #: engines/sludge/keymapper_tables.h:561 engines/sludge/keymapper_tables.h:580
@@ -3914,7 +3914,7 @@ msgstr "Kliknut?? prost??edn??m tla????tkem"
 #: engines/hopkins/metaengine.cpp:217 engines/hugo/metaengine.cpp:141
 #: engines/illusions/metaengine.cpp:101 engines/kyra/engine/eobcommon.cpp:370
 #: engines/kyra/engine/lol.cpp:506 engines/lure/metaengine.cpp:183
-#: engines/made/metaengine.cpp:144 engines/mads/metaengine.cpp:324
+#: engines/made/metaengine.cpp:144 engines/mads/metaengine.cpp:328
 #: engines/mm/metaengine.cpp:192 engines/mtropolis/metaengine.cpp:167
 #: engines/nancy/input.cpp:188 engines/parallaction/metaengine.cpp:144
 #: engines/saga/metaengine.cpp:289 engines/sludge/keymapper_tables.h:567
@@ -6045,7 +6045,7 @@ msgstr "Zkontrolovat Aktualizace..."
 #: engines/hugo/metaengine.cpp:129 engines/hypno/metaengine.cpp:128
 #: engines/illusions/metaengine.cpp:89 engines/lab/metaengine.cpp:149
 #: engines/lure/metaengine.cpp:168 engines/made/metaengine.cpp:132
-#: engines/mads/metaengine.cpp:312 engines/neverhood/metaengine.cpp:185
+#: engines/mads/metaengine.cpp:316 engines/neverhood/metaengine.cpp:185
 #: engines/ngi/metaengine.cpp:115 engines/petka/metaengine.cpp:113
 #: engines/prince/metaengine.cpp:227 engines/private/metaengine.cpp:127
 #: engines/queen/metaengine.cpp:146 engines/sherlock/metaengine.cpp:313
@@ -6614,7 +6614,7 @@ msgstr ""
 #: engines/hypno/metaengine.cpp:267 engines/hypno/metaengine.cpp:311
 #: engines/illusions/metaengine.cpp:90 engines/lab/metaengine.cpp:152
 #: engines/lure/metaengine.cpp:169 engines/made/metaengine.cpp:133
-#: engines/mads/metaengine.cpp:313 engines/neverhood/metaengine.cpp:186
+#: engines/mads/metaengine.cpp:317 engines/neverhood/metaengine.cpp:186
 #: engines/queen/metaengine.cpp:147 engines/saga/metaengine.cpp:268
 #: engines/sludge/keymapper_tables.h:33 engines/stark/metaengine.cpp:185
 #: engines/sword1/metaengine.cpp:267 engines/tinsel/metaengine.cpp:241
@@ -7136,7 +7136,7 @@ msgstr ""
 #: engines/bbvs/metaengine.cpp:111 engines/cruise/metaengine.cpp:223
 #: engines/freescape/movement.cpp:107 engines/got/metaengine.cpp:78
 #: engines/hugo/metaengine.cpp:197 engines/lure/metaengine.cpp:209
-#: engines/mads/metaengine.cpp:330 engines/mm/mm1/metaengine.cpp:42
+#: engines/mads/metaengine.cpp:334 engines/mm/mm1/metaengine.cpp:42
 #: engines/mm/mm1/metaengine.cpp:48 engines/saga/metaengine.cpp:328
 #: engines/tinsel/metaengine.cpp:258 engines/tsage/metaengine.cpp:190
 #: engines/tucker/metaengine.cpp:221 engines/twine/metaengine.cpp:388
@@ -7445,7 +7445,7 @@ msgstr "Povolit zachycov??n?? my??i"
 #: engines/freescape/games/eclipse/eclipse.cpp:330
 #: engines/hopkins/metaengine.cpp:236 engines/hugo/metaengine.cpp:165
 #: engines/lure/metaengine.cpp:191 engines/made/metaengine.cpp:192
-#: engines/mads/metaengine.cpp:343 engines/mohawk/myst.cpp:583
+#: engines/mads/metaengine.cpp:347 engines/mohawk/myst.cpp:583
 #: engines/mohawk/riven.cpp:864 engines/pegasus/pegasus.cpp:2825
 #: engines/petka/metaengine.cpp:198 engines/prince/metaengine.cpp:243
 #: engines/saga/metaengine.cpp:442 engines/sherlock/metaengine.cpp:462
@@ -7522,7 +7522,7 @@ msgstr ""
 #: engines/freescape/games/dark/dark.cpp:236
 #: engines/freescape/games/driller/driller.cpp:129
 #: engines/freescape/games/eclipse/eclipse.cpp:340
-#: engines/mads/metaengine.cpp:399 engines/phoenixvr/metaengine.cpp:80
+#: engines/mads/metaengine.cpp:403 engines/phoenixvr/metaengine.cpp:80
 #: engines/saga/metaengine.cpp:437 engines/sherlock/metaengine.cpp:359
 #: engines/sherlock/metaengine.cpp:618 engines/sherlock/metaengine.cpp:852
 #: engines/stark/metaengine.cpp:280 engines/sword1/metaengine.cpp:295
@@ -10175,7 +10175,7 @@ msgid "Repeat last line"
 msgstr "Neulo??ena ????dn?? data"
 
 #: engines/hugo/metaengine.cpp:172 engines/lure/metaengine.cpp:197
-#: engines/mads/metaengine.cpp:349 engines/tsage/metaengine.cpp:247
+#: engines/mads/metaengine.cpp:353 engines/tsage/metaengine.cpp:247
 #, fuzzy
 #| msgid "Restore game:"
 msgid "Restore game"
@@ -10393,7 +10393,7 @@ msgstr "~V~olby..."
 msgid "Direction keymappings"
 msgstr "~V~olby..."
 
-#: engines/hypno/metaengine.cpp:316 engines/mads/metaengine.cpp:387
+#: engines/hypno/metaengine.cpp:316 engines/mads/metaengine.cpp:391
 #: engines/tsage/metaengine.cpp:259
 #, fuzzy
 msgid "Show credits"
@@ -11085,48 +11085,48 @@ msgstr "Animovan?? hern?? rozhran??"
 msgid "Naughty game mode"
 msgstr "Hanbat?? hern?? re??im"
 
-#: engines/mads/metaengine.cpp:314
+#: engines/mads/metaengine.cpp:318
 #, fuzzy
 msgid "Start menu keymappings"
 msgstr "~V~olby..."
 
-#: engines/mads/metaengine.cpp:337
+#: engines/mads/metaengine.cpp:341
 msgid "Open game menu"
 msgstr "Otev????t menu hry"
 
-#: engines/mads/metaengine.cpp:355
+#: engines/mads/metaengine.cpp:359
 #, fuzzy
 msgid "Scroll bar up"
 msgstr "Posunout seznam nahoru"
 
-#: engines/mads/metaengine.cpp:361
+#: engines/mads/metaengine.cpp:365
 #, fuzzy
 msgid "Scroll bar down"
 msgstr "Posunout seznam dolu"
 
-#: engines/mads/metaengine.cpp:369
+#: engines/mads/metaengine.cpp:373
 #, fuzzy
 #| msgid "Restore game:"
 msgid "Start game"
 msgstr "Obnovit hru:"
 
-#: engines/mads/metaengine.cpp:375
+#: engines/mads/metaengine.cpp:379
 #, fuzzy
 #| msgid "Restore game:"
 msgid "Resume game"
 msgstr "Obnovit hru:"
 
-#: engines/mads/metaengine.cpp:381
+#: engines/mads/metaengine.cpp:385
 #, fuzzy
 msgid "Show intro"
 msgstr "Zobrazit skryt?? soubory"
 
-#: engines/mads/metaengine.cpp:393
+#: engines/mads/metaengine.cpp:397
 #, fuzzy
 msgid "Show quotes"
 msgstr "Pouze ??e??"
 
-#: engines/mads/metaengine.cpp:406
+#: engines/mads/metaengine.cpp:410
 #, fuzzy
 #| msgid "Transitions:"
 msgid "Restart animation"
diff --git a/po/da.po b/po/da.po
index c6a653ba9eb..f9b285b0891 100644
--- a/po/da.po
+++ b/po/da.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-07 22:09+0000\n"
+"POT-Creation-Date: 2026-06-08 11:04+0000\n"
 "PO-Revision-Date: 2023-09-07 17:04+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Danish <https://translations.scummvm.org/projects/scummvm/"
@@ -3887,7 +3887,7 @@ msgstr "~I~ndstillinger ..."
 #: engines/hugo/metaengine.cpp:134 engines/illusions/metaengine.cpp:94
 #: engines/kyra/engine/eobcommon.cpp:369 engines/kyra/engine/lol.cpp:505
 #: engines/lure/metaengine.cpp:177 engines/made/metaengine.cpp:137
-#: engines/mads/metaengine.cpp:318 engines/mm/metaengine.cpp:186
+#: engines/mads/metaengine.cpp:322 engines/mm/metaengine.cpp:186
 #: engines/mtropolis/metaengine.cpp:161 engines/nancy/input.cpp:181
 #: engines/parallaction/metaengine.cpp:138 engines/saga/metaengine.cpp:278
 #: engines/sludge/keymapper_tables.h:561 engines/sludge/keymapper_tables.h:580
@@ -3924,7 +3924,7 @@ msgstr "Midterklik"
 #: engines/hopkins/metaengine.cpp:217 engines/hugo/metaengine.cpp:141
 #: engines/illusions/metaengine.cpp:101 engines/kyra/engine/eobcommon.cpp:370
 #: engines/kyra/engine/lol.cpp:506 engines/lure/metaengine.cpp:183
-#: engines/made/metaengine.cpp:144 engines/mads/metaengine.cpp:324
+#: engines/made/metaengine.cpp:144 engines/mads/metaengine.cpp:328
 #: engines/mm/metaengine.cpp:192 engines/mtropolis/metaengine.cpp:167
 #: engines/nancy/input.cpp:188 engines/parallaction/metaengine.cpp:144
 #: engines/saga/metaengine.cpp:289 engines/sludge/keymapper_tables.h:567
@@ -6064,7 +6064,7 @@ msgstr "S??g efter opdateringer ..."
 #: engines/hugo/metaengine.cpp:129 engines/hypno/metaengine.cpp:128
 #: engines/illusions/metaengine.cpp:89 engines/lab/metaengine.cpp:149
 #: engines/lure/metaengine.cpp:168 engines/made/metaengine.cpp:132
-#: engines/mads/metaengine.cpp:312 engines/neverhood/metaengine.cpp:185
+#: engines/mads/metaengine.cpp:316 engines/neverhood/metaengine.cpp:185
 #: engines/ngi/metaengine.cpp:115 engines/petka/metaengine.cpp:113
 #: engines/prince/metaengine.cpp:227 engines/private/metaengine.cpp:127
 #: engines/queen/metaengine.cpp:146 engines/sherlock/metaengine.cpp:313
@@ -6628,7 +6628,7 @@ msgstr ""
 #: engines/hypno/metaengine.cpp:267 engines/hypno/metaengine.cpp:311
 #: engines/illusions/metaengine.cpp:90 engines/lab/metaengine.cpp:152
 #: engines/lure/metaengine.cpp:169 engines/made/metaengine.cpp:133
-#: engines/mads/metaengine.cpp:313 engines/neverhood/metaengine.cpp:186
+#: engines/mads/metaengine.cpp:317 engines/neverhood/metaengine.cpp:186
 #: engines/queen/metaengine.cpp:147 engines/saga/metaengine.cpp:268
 #: engines/sludge/keymapper_tables.h:33 engines/stark/metaengine.cpp:185
 #: engines/sword1/metaengine.cpp:267 engines/tinsel/metaengine.cpp:241
@@ -7156,7 +7156,7 @@ msgstr ""
 #: engines/bbvs/metaengine.cpp:111 engines/cruise/metaengine.cpp:223
 #: engines/freescape/movement.cpp:107 engines/got/metaengine.cpp:78
 #: engines/hugo/metaengine.cpp:197 engines/lure/metaengine.cpp:209
-#: engines/mads/metaengine.cpp:330 engines/mm/mm1/metaengine.cpp:42
+#: engines/mads/metaengine.cpp:334 engines/mm/mm1/metaengine.cpp:42
 #: engines/mm/mm1/metaengine.cpp:48 engines/saga/metaengine.cpp:328
 #: engines/tinsel/metaengine.cpp:258 engines/tsage/metaengine.cpp:190
 #: engines/tucker/metaengine.cpp:221 engines/twine/metaengine.cpp:388
@@ -7473,7 +7473,7 @@ msgstr "Museindfangning til/fra"
 #: engines/freescape/games/eclipse/eclipse.cpp:330
 #: engines/hopkins/metaengine.cpp:236 engines/hugo/metaengine.cpp:165
 #: engines/lure/metaengine.cpp:191 engines/made/metaengine.cpp:192
-#: engines/mads/metaengine.cpp:343 engines/mohawk/myst.cpp:583
+#: engines/mads/metaengine.cpp:347 engines/mohawk/myst.cpp:583
 #: engines/mohawk/riven.cpp:864 engines/pegasus/pegasus.cpp:2825
 #: engines/petka/metaengine.cpp:198 engines/prince/metaengine.cpp:243
 #: engines/saga/metaengine.cpp:442 engines/sherlock/metaengine.cpp:462
@@ -7550,7 +7550,7 @@ msgstr ""
 #: engines/freescape/games/dark/dark.cpp:236
 #: engines/freescape/games/driller/driller.cpp:129
 #: engines/freescape/games/eclipse/eclipse.cpp:340
-#: engines/mads/metaengine.cpp:399 engines/phoenixvr/metaengine.cpp:80
+#: engines/mads/metaengine.cpp:403 engines/phoenixvr/metaengine.cpp:80
 #: engines/saga/metaengine.cpp:437 engines/sherlock/metaengine.cpp:359
 #: engines/sherlock/metaengine.cpp:618 engines/sherlock/metaengine.cpp:852
 #: engines/stark/metaengine.cpp:280 engines/sword1/metaengine.cpp:295
@@ -10196,7 +10196,7 @@ msgid "Repeat last line"
 msgstr "Ingen dato gemt"
 
 #: engines/hugo/metaengine.cpp:172 engines/lure/metaengine.cpp:197
-#: engines/mads/metaengine.cpp:349 engines/tsage/metaengine.cpp:247
+#: engines/mads/metaengine.cpp:353 engines/tsage/metaengine.cpp:247
 #, fuzzy
 #| msgid "Restore game:"
 msgid "Restore game"
@@ -10414,7 +10414,7 @@ msgstr "~I~ndstillinger ..."
 msgid "Direction keymappings"
 msgstr "~I~ndstillinger ..."
 
-#: engines/hypno/metaengine.cpp:316 engines/mads/metaengine.cpp:387
+#: engines/hypno/metaengine.cpp:316 engines/mads/metaengine.cpp:391
 #: engines/tsage/metaengine.cpp:259
 #, fuzzy
 msgid "Show credits"
@@ -11140,49 +11140,49 @@ msgstr "Animeret spilbrugerflade"
 msgid "Naughty game mode"
 msgstr "Uhyggelig spiltilstand"
 
-#: engines/mads/metaengine.cpp:314
+#: engines/mads/metaengine.cpp:318
 #, fuzzy
 msgid "Start menu keymappings"
 msgstr "~I~ndstillinger ..."
 
-#: engines/mads/metaengine.cpp:337
+#: engines/mads/metaengine.cpp:341
 #, fuzzy
 msgid "Open game menu"
 msgstr "~R~etur til oversigt"
 
-#: engines/mads/metaengine.cpp:355
+#: engines/mads/metaengine.cpp:359
 #, fuzzy
 msgid "Scroll bar up"
 msgstr "Rul liste op"
 
-#: engines/mads/metaengine.cpp:361
+#: engines/mads/metaengine.cpp:365
 #, fuzzy
 msgid "Scroll bar down"
 msgstr "Rul liste ned"
 
-#: engines/mads/metaengine.cpp:369
+#: engines/mads/metaengine.cpp:373
 #, fuzzy
 #| msgid "Restore game:"
 msgid "Start game"
 msgstr "Gendan spil:"
 
-#: engines/mads/metaengine.cpp:375
+#: engines/mads/metaengine.cpp:379
 #, fuzzy
 #| msgid "Restore game:"
 msgid "Resume game"
 msgstr "Gendan spil:"
 
-#: engines/mads/metaengine.cpp:381
+#: engines/mads/metaengine.cpp:385
 #, fuzzy
 msgid "Show intro"
 msgstr "Vis skjulte filer"
 
-#: engines/mads/metaengine.cpp:393
+#: engines/mads/metaengine.cpp:397
 #, fuzzy
 msgid "Show quotes"
 msgstr "Kun tale"
 
-#: engines/mads/metaengine.cpp:406
+#: engines/mads/metaengine.cpp:410
 #, fuzzy
 #| msgid "Transitions:"
 msgid "Restart animation"
diff --git a/po/de_DE.po b/po/de_DE.po
index 2057a4a6727..c46ebd4d0e6 100644
--- a/po/de_DE.po
+++ b/po/de_DE.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.10.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-07 22:09+0000\n"
+"POT-Creation-Date: 2026-06-08 11:04+0000\n"
 "PO-Revision-Date: 2026-05-05 16:31+0000\n"
 "Last-Translator: Lothar Serra Mari <lothar.serramari at scummvm.org>\n"
 "Language-Team: German <https://translations.scummvm.org/projects/scummvm/"
@@ -3943,7 +3943,7 @@ msgstr "Standard-Tastenbelegung f??r Spiele"
 #: engines/hugo/metaengine.cpp:134 engines/illusions/metaengine.cpp:94
 #: engines/kyra/engine/eobcommon.cpp:369 engines/kyra/engine/lol.cpp:505
 #: engines/lure/metaengine.cpp:177 engines/made/metaengine.cpp:137
-#: engines/mads/metaengine.cpp:318 engines/mm/metaengine.cpp:186
+#: engines/mads/metaengine.cpp:322 engines/mm/metaengine.cpp:186
 #: engines/mtropolis/metaengine.cpp:161 engines/nancy/input.cpp:181
 #: engines/parallaction/metaengine.cpp:138 engines/saga/metaengine.cpp:278
 #: engines/sludge/keymapper_tables.h:561 engines/sludge/keymapper_tables.h:580
@@ -3976,7 +3976,7 @@ msgstr "Mittelklick"
 #: engines/hopkins/metaengine.cpp:217 engines/hugo/metaengine.cpp:141
 #: engines/illusions/metaengine.cpp:101 engines/kyra/engine/eobcommon.cpp:370
 #: engines/kyra/engine/lol.cpp:506 engines/lure/metaengine.cpp:183
-#: engines/made/metaengine.cpp:144 engines/mads/metaengine.cpp:324
+#: engines/made/metaengine.cpp:144 engines/mads/metaengine.cpp:328
 #: engines/mm/metaengine.cpp:192 engines/mtropolis/metaengine.cpp:167
 #: engines/nancy/input.cpp:188 engines/parallaction/metaengine.cpp:144
 #: engines/saga/metaengine.cpp:289 engines/sludge/keymapper_tables.h:567
@@ -6344,7 +6344,7 @@ msgstr "Suche nach Aktualisierungen..."
 #: engines/hugo/metaengine.cpp:129 engines/hypno/metaengine.cpp:128
 #: engines/illusions/metaengine.cpp:89 engines/lab/metaengine.cpp:149
 #: engines/lure/metaengine.cpp:168 engines/made/metaengine.cpp:132
-#: engines/mads/metaengine.cpp:312 engines/neverhood/metaengine.cpp:185
+#: engines/mads/metaengine.cpp:316 engines/neverhood/metaengine.cpp:185
 #: engines/ngi/metaengine.cpp:115 engines/petka/metaengine.cpp:113
 #: engines/prince/metaengine.cpp:227 engines/private/metaengine.cpp:127
 #: engines/queen/metaengine.cpp:146 engines/sherlock/metaengine.cpp:313
@@ -6910,7 +6910,7 @@ msgstr "AGOS"
 #: engines/hypno/metaengine.cpp:267 engines/hypno/metaengine.cpp:311
 #: engines/illusions/metaengine.cpp:90 engines/lab/metaengine.cpp:152
 #: engines/lure/metaengine.cpp:169 engines/made/metaengine.cpp:133
-#: engines/mads/metaengine.cpp:313 engines/neverhood/metaengine.cpp:186
+#: engines/mads/metaengine.cpp:317 engines/neverhood/metaengine.cpp:186
 #: engines/queen/metaengine.cpp:147 engines/saga/metaengine.cpp:268
 #: engines/sludge/keymapper_tables.h:33 engines/stark/metaengine.cpp:185
 #: engines/sword1/metaengine.cpp:267 engines/tinsel/metaengine.cpp:241
@@ -7419,7 +7419,7 @@ msgstr "Soldier 714 Augen-Cheat"
 #: engines/bbvs/metaengine.cpp:111 engines/cruise/metaengine.cpp:223
 #: engines/freescape/movement.cpp:107 engines/got/metaengine.cpp:78
 #: engines/hugo/metaengine.cpp:197 engines/lure/metaengine.cpp:209
-#: engines/mads/metaengine.cpp:330 engines/mm/mm1/metaengine.cpp:42
+#: engines/mads/metaengine.cpp:334 engines/mm/mm1/metaengine.cpp:42
 #: engines/mm/mm1/metaengine.cpp:48 engines/saga/metaengine.cpp:328
 #: engines/tinsel/metaengine.cpp:258 engines/tsage/metaengine.cpp:190
 #: engines/tucker/metaengine.cpp:221 engines/twine/metaengine.cpp:388
@@ -7730,7 +7730,7 @@ msgstr "Hinweis-Datenschutz umschalten"
 #: engines/freescape/games/eclipse/eclipse.cpp:330
 #: engines/hopkins/metaengine.cpp:236 engines/hugo/metaengine.cpp:165
 #: engines/lure/metaengine.cpp:191 engines/made/metaengine.cpp:192
-#: engines/mads/metaengine.cpp:343 engines/mohawk/myst.cpp:583
+#: engines/mads/metaengine.cpp:347 engines/mohawk/myst.cpp:583
 #: engines/mohawk/riven.cpp:864 engines/pegasus/pegasus.cpp:2825
 #: engines/petka/metaengine.cpp:198 engines/prince/metaengine.cpp:243
 #: engines/saga/metaengine.cpp:442 engines/sherlock/metaengine.cpp:462
@@ -7806,7 +7806,7 @@ msgstr "Hinweis-Datenbank"
 #: engines/freescape/games/dark/dark.cpp:236
 #: engines/freescape/games/driller/driller.cpp:129
 #: engines/freescape/games/eclipse/eclipse.cpp:340
-#: engines/mads/metaengine.cpp:399 engines/phoenixvr/metaengine.cpp:80
+#: engines/mads/metaengine.cpp:403 engines/phoenixvr/metaengine.cpp:80
 #: engines/saga/metaengine.cpp:437 engines/sherlock/metaengine.cpp:359
 #: engines/sherlock/metaengine.cpp:618 engines/sherlock/metaengine.cpp:852
 #: engines/stark/metaengine.cpp:280 engines/sword1/metaengine.cpp:295
@@ -10203,7 +10203,7 @@ msgid "Repeat last line"
 msgstr "Letzte Zeile wiederholen"
 
 #: engines/hugo/metaengine.cpp:172 engines/lure/metaengine.cpp:197
-#: engines/mads/metaengine.cpp:349 engines/tsage/metaengine.cpp:247
+#: engines/mads/metaengine.cpp:353 engines/tsage/metaengine.cpp:247
 msgid "Restore game"
 msgstr "Spiel wiederherstellen"
 
@@ -10396,7 +10396,7 @@ msgstr "Tastaturbelegungen pausieren"
 msgid "Direction keymappings"
 msgstr "Richtungs-Tastenbelegungen"
 
-#: engines/hypno/metaengine.cpp:316 engines/mads/metaengine.cpp:387
+#: engines/hypno/metaengine.cpp:316 engines/mads/metaengine.cpp:391
 #: engines/tsage/metaengine.cpp:259
 msgid "Show credits"
 msgstr "Abspann anzeigen"
@@ -11051,39 +11051,39 @@ msgstr "Animierte Spieloberfl??che"
 msgid "Naughty game mode"
 msgstr "Unanst??ndiger Spielmodus"
 
-#: engines/mads/metaengine.cpp:314
+#: engines/mads/metaengine.cpp:318
 msgid "Start menu keymappings"
 msgstr "Tastenk??rzel f??r das Start-Men??"
 
-#: engines/mads/metaengine.cpp:337
+#: engines/mads/metaengine.cpp:341
 msgid "Open game menu"
 msgstr "Spielmen?? ??ffnen"
 
-#: engines/mads/metaengine.cpp:355
+#: engines/mads/metaengine.cpp:359
 msgid "Scroll bar up"
 msgstr "Scroll-Leiste nach oben"
 
-#: engines/mads/metaengine.cpp:361
+#: engines/mads/metaengine.cpp:365
 msgid "Scroll bar down"
 msgstr "Scroll-Leiste nach unten"
 
-#: engines/mads/metaengine.cpp:369
+#: engines/mads/metaengine.cpp:373
 msgid "Start game"
 msgstr "Spiel starten"
 
-#: engines/mads/metaengine.cpp:375
+#: engines/mads/metaengine.cpp:379
 msgid "Resume game"
 msgstr "Spiel fortsetzen"
 
-#: engines/mads/metaengine.cpp:381
+#: engines/mads/metaengine.cpp:385
 msgid "Show intro"
 msgstr "Intro anzeigen"
 
-#: engines/mads/metaengine.cpp:393
+#: engines/mads/metaengine.cpp:397
 msgid "Show quotes"
 msgstr "Zitate anzeigen"
 
-#: engines/mads/metaengine.cpp:406
+#: engines/mads/metaengine.cpp:410
 msgid "Restart animation"
 msgstr "Animation neu starten"
 
diff --git a/po/el.po b/po/el.po
index 1002be7626f..336380fcf40 100644
--- a/po/el.po
+++ b/po/el.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.10.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-07 22:09+0000\n"
+"POT-Creation-Date: 2026-06-08 11:04+0000\n"
 "PO-Revision-Date: 2026-04-23 00:22+0000\n"
 "Last-Translator: Antoniou Athanasios <a.antoniou79 at gmail.com>\n"
 "Language-Team: Greek <https://translations.scummvm.org/projects/scummvm/"
@@ -4014,7 +4014,7 @@ msgstr "?????????????? ???????????????????????????? ???????????????? ??????????
 #: engines/hugo/metaengine.cpp:134 engines/illusions/metaengine.cpp:94
 #: engines/kyra/engine/eobcommon.cpp:369 engines/kyra/engine/lol.cpp:505
 #: engines/lure/metaengine.cpp:177 engines/made/metaengine.cpp:137
-#: engines/mads/metaengine.cpp:318 engines/mm/metaengine.cpp:186
+#: engines/mads/metaengine.cpp:322 engines/mm/metaengine.cpp:186
 #: engines/mtropolis/metaengine.cpp:161 engines/nancy/input.cpp:181
 #: engines/parallaction/metaengine.cpp:138 engines/saga/metaengine.cpp:278
 #: engines/sludge/keymapper_tables.h:561 engines/sludge/keymapper_tables.h:580
@@ -4047,7 +4047,7 @@ msgstr "???????????? ????????"
 #: engines/hopkins/metaengine.cpp:217 engines/hugo/metaengine.cpp:141
 #: engines/illusions/metaengine.cpp:101 engines/kyra/engine/eobcommon.cpp:370
 #: engines/kyra/engine/lol.cpp:506 engines/lure/metaengine.cpp:183
-#: engines/made/metaengine.cpp:144 engines/mads/metaengine.cpp:324
+#: engines/made/metaengine.cpp:144 engines/mads/metaengine.cpp:328
 #: engines/mm/metaengine.cpp:192 engines/mtropolis/metaengine.cpp:167
 #: engines/nancy/input.cpp:188 engines/parallaction/metaengine.cpp:144
 #: engines/saga/metaengine.cpp:289 engines/sludge/keymapper_tables.h:567
@@ -6468,7 +6468,7 @@ msgstr "?????????????? ??????????????????????..."
 #: engines/hugo/metaengine.cpp:129 engines/hypno/metaengine.cpp:128
 #: engines/illusions/metaengine.cpp:89 engines/lab/metaengine.cpp:149
 #: engines/lure/metaengine.cpp:168 engines/made/metaengine.cpp:132
-#: engines/mads/metaengine.cpp:312 engines/neverhood/metaengine.cpp:185
+#: engines/mads/metaengine.cpp:316 engines/neverhood/metaengine.cpp:185
 #: engines/ngi/metaengine.cpp:115 engines/petka/metaengine.cpp:113
 #: engines/prince/metaengine.cpp:227 engines/private/metaengine.cpp:127
 #: engines/queen/metaengine.cpp:146 engines/sherlock/metaengine.cpp:313
@@ -7038,7 +7038,7 @@ msgstr "AGOS - ??????????"
 #: engines/hypno/metaengine.cpp:267 engines/hypno/metaengine.cpp:311
 #: engines/illusions/metaengine.cpp:90 engines/lab/metaengine.cpp:152
 #: engines/lure/metaengine.cpp:169 engines/made/metaengine.cpp:133
-#: engines/mads/metaengine.cpp:313 engines/neverhood/metaengine.cpp:186
+#: engines/mads/metaengine.cpp:317 engines/neverhood/metaengine.cpp:186
 #: engines/queen/metaengine.cpp:147 engines/saga/metaengine.cpp:268
 #: engines/sludge/keymapper_tables.h:33 engines/stark/metaengine.cpp:185
 #: engines/sword1/metaengine.cpp:267 engines/tinsel/metaengine.cpp:241
@@ -7544,7 +7544,7 @@ msgstr "?????????? '???????? ?????? ?????????????????? 714'"
 #: engines/bbvs/metaengine.cpp:111 engines/cruise/metaengine.cpp:223
 #: engines/freescape/movement.cpp:107 engines/got/metaengine.cpp:78
 #: engines/hugo/metaengine.cpp:197 engines/lure/metaengine.cpp:209
-#: engines/mads/metaengine.cpp:330 engines/mm/mm1/metaengine.cpp:42
+#: engines/mads/metaengine.cpp:334 engines/mm/mm1/metaengine.cpp:42
 #: engines/mm/mm1/metaengine.cpp:48 engines/saga/metaengine.cpp:328
 #: engines/tinsel/metaengine.cpp:258 engines/tsage/metaengine.cpp:190
 #: engines/tucker/metaengine.cpp:221 engines/twine/metaengine.cpp:388
@@ -7858,7 +7858,7 @@ msgstr "???????????????? ?????????????????????????? ??????????????????"
 #: engines/freescape/games/eclipse/eclipse.cpp:330
 #: engines/hopkins/metaengine.cpp:236 engines/hugo/metaengine.cpp:165
 #: engines/lure/metaengine.cpp:191 engines/made/metaengine.cpp:192
-#: engines/mads/metaengine.cpp:343 engines/mohawk/myst.cpp:583
+#: engines/mads/metaengine.cpp:347 engines/mohawk/myst.cpp:583
 #: engines/mohawk/riven.cpp:864 engines/pegasus/pegasus.cpp:2825
 #: engines/petka/metaengine.cpp:198 engines/prince/metaengine.cpp:243
 #: engines/saga/metaengine.cpp:442 engines/sherlock/metaengine.cpp:462
@@ -7934,7 +7934,7 @@ msgstr "???????? ?????????????????? ?????????????????? ??????????????"
 #: engines/freescape/games/dark/dark.cpp:236
 #: engines/freescape/games/driller/driller.cpp:129
 #: engines/freescape/games/eclipse/eclipse.cpp:340
-#: engines/mads/metaengine.cpp:399 engines/phoenixvr/metaengine.cpp:80
+#: engines/mads/metaengine.cpp:403 engines/phoenixvr/metaengine.cpp:80
 #: engines/saga/metaengine.cpp:437 engines/sherlock/metaengine.cpp:359
 #: engines/sherlock/metaengine.cpp:618 engines/sherlock/metaengine.cpp:852
 #: engines/stark/metaengine.cpp:280 engines/sword1/metaengine.cpp:295
@@ -10332,7 +10332,7 @@ msgid "Repeat last line"
 msgstr "?????????????????? ?????????????????? ??????????????"
 
 #: engines/hugo/metaengine.cpp:172 engines/lure/metaengine.cpp:197
-#: engines/mads/metaengine.cpp:349 engines/tsage/metaengine.cpp:247
+#: engines/mads/metaengine.cpp:353 engines/tsage/metaengine.cpp:247
 msgid "Restore game"
 msgstr "?????????????????? ????????????????????"
 
@@ -10525,7 +10525,7 @@ msgstr "?????????????? ????????????????"
 msgid "Direction keymappings"
 msgstr "?????????????? ??????????????????????"
 
-#: engines/hypno/metaengine.cpp:316 engines/mads/metaengine.cpp:387
+#: engines/hypno/metaengine.cpp:316 engines/mads/metaengine.cpp:391
 #: engines/tsage/metaengine.cpp:259
 msgid "Show credits"
 msgstr "?????????????? ??????????????????????"
@@ -11180,39 +11180,39 @@ msgstr "?????????????????? ?????????????? ????????????????????"
 msgid "Naughty game mode"
 msgstr "???????????????????? ???????????????????? ?????? ????????????????"
 
-#: engines/mads/metaengine.cpp:314
+#: engines/mads/metaengine.cpp:318
 msgid "Start menu keymappings"
 msgstr "?????????????? ?????? ?????????? ??????????????"
 
-#: engines/mads/metaengine.cpp:337
+#: engines/mads/metaengine.cpp:341
 msgid "Open game menu"
 msgstr "?????????????? ?????????? ????????????????????"
 
-#: engines/mads/metaengine.cpp:355
+#: engines/mads/metaengine.cpp:359
 msgid "Scroll bar up"
 msgstr "???????????? ???????????? ????????"
 
-#: engines/mads/metaengine.cpp:361
+#: engines/mads/metaengine.cpp:365
 msgid "Scroll bar down"
 msgstr "???????????? ????????"
 
-#: engines/mads/metaengine.cpp:369
+#: engines/mads/metaengine.cpp:373
 msgid "Start game"
 msgstr "???????????? ????????????????????"
 
-#: engines/mads/metaengine.cpp:375
+#: engines/mads/metaengine.cpp:379
 msgid "Resume game"
 msgstr "???????????????? ????????????????????"
 
-#: engines/mads/metaengine.cpp:381
+#: engines/mads/metaengine.cpp:385
 msgid "Show intro"
 msgstr "?????????????? ??????????????????"
 
-#: engines/mads/metaengine.cpp:393
+#: engines/mads/metaengine.cpp:397
 msgid "Show quotes"
 msgstr "?????????????? ????????????????"
 
-#: engines/mads/metaengine.cpp:406
+#: engines/mads/metaengine.cpp:410
 msgid "Restart animation"
 msgstr "???????????????????? ??????????????"
 
diff --git a/po/es_ES.po b/po/es_ES.po
index 7f69ffb80e2..25e3d40af4a 100644
--- a/po/es_ES.po
+++ b/po/es_ES.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.4.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-07 22:09+0000\n"
+"POT-Creation-Date: 2026-06-08 11:04+0000\n"
 "PO-Revision-Date: 2026-06-08 08:48+0000\n"
 "Last-Translator: IlDucci <pajaroloco_2 at hotmail.com>\n"
 "Language-Team: Spanish <https://translations.scummvm.org/projects/scummvm/"
@@ -3946,7 +3946,7 @@ msgstr "Asignaciones predet. del juego"
 #: engines/hugo/metaengine.cpp:134 engines/illusions/metaengine.cpp:94
 #: engines/kyra/engine/eobcommon.cpp:369 engines/kyra/engine/lol.cpp:505
 #: engines/lure/metaengine.cpp:177 engines/made/metaengine.cpp:137
-#: engines/mads/metaengine.cpp:318 engines/mm/metaengine.cpp:186
+#: engines/mads/metaengine.cpp:322 engines/mm/metaengine.cpp:186
 #: engines/mtropolis/metaengine.cpp:161 engines/nancy/input.cpp:181
 #: engines/parallaction/metaengine.cpp:138 engines/saga/metaengine.cpp:278
 #: engines/sludge/keymapper_tables.h:561 engines/sludge/keymapper_tables.h:580
@@ -3979,7 +3979,7 @@ msgstr "Clic central"
 #: engines/hopkins/metaengine.cpp:217 engines/hugo/metaengine.cpp:141
 #: engines/illusions/metaengine.cpp:101 engines/kyra/engine/eobcommon.cpp:370
 #: engines/kyra/engine/lol.cpp:506 engines/lure/metaengine.cpp:183
-#: engines/made/metaengine.cpp:144 engines/mads/metaengine.cpp:324
+#: engines/made/metaengine.cpp:144 engines/mads/metaengine.cpp:328
 #: engines/mm/metaengine.cpp:192 engines/mtropolis/metaengine.cpp:167
 #: engines/nancy/input.cpp:188 engines/parallaction/metaengine.cpp:144
 #: engines/saga/metaengine.cpp:289 engines/sludge/keymapper_tables.h:567
@@ -6366,7 +6366,7 @@ msgstr "Buscar actualizaciones..."
 #: engines/hugo/metaengine.cpp:129 engines/hypno/metaengine.cpp:128
 #: engines/illusions/metaengine.cpp:89 engines/lab/metaengine.cpp:149
 #: engines/lure/metaengine.cpp:168 engines/made/metaengine.cpp:132
-#: engines/mads/metaengine.cpp:312 engines/neverhood/metaengine.cpp:185
+#: engines/mads/metaengine.cpp:316 engines/neverhood/metaengine.cpp:185
 #: engines/ngi/metaengine.cpp:115 engines/petka/metaengine.cpp:113
 #: engines/prince/metaengine.cpp:227 engines/private/metaengine.cpp:127
 #: engines/queen/metaengine.cpp:146 engines/sherlock/metaengine.cpp:313
@@ -6932,7 +6932,7 @@ msgstr "AGOS - Principal"
 #: engines/hypno/metaengine.cpp:267 engines/hypno/metaengine.cpp:311
 #: engines/illusions/metaengine.cpp:90 engines/lab/metaengine.cpp:152
 #: engines/lure/metaengine.cpp:169 engines/made/metaengine.cpp:133
-#: engines/mads/metaengine.cpp:313 engines/neverhood/metaengine.cpp:186
+#: engines/mads/metaengine.cpp:317 engines/neverhood/metaengine.cpp:186
 #: engines/queen/metaengine.cpp:147 engines/saga/metaengine.cpp:268
 #: engines/sludge/keymapper_tables.h:33 engines/stark/metaengine.cpp:185
 #: engines/sword1/metaengine.cpp:267 engines/tinsel/metaengine.cpp:241
@@ -7441,7 +7441,7 @@ msgstr "Truco del ojo del soldado 714"
 #: engines/bbvs/metaengine.cpp:111 engines/cruise/metaengine.cpp:223
 #: engines/freescape/movement.cpp:107 engines/got/metaengine.cpp:78
 #: engines/hugo/metaengine.cpp:197 engines/lure/metaengine.cpp:209
-#: engines/mads/metaengine.cpp:330 engines/mm/mm1/metaengine.cpp:42
+#: engines/mads/metaengine.cpp:334 engines/mm/mm1/metaengine.cpp:42
 #: engines/mm/mm1/metaengine.cpp:48 engines/saga/metaengine.cpp:328
 #: engines/tinsel/metaengine.cpp:258 engines/tsage/metaengine.cpp:190
 #: engines/tucker/metaengine.cpp:221 engines/twine/metaengine.cpp:388
@@ -7745,7 +7745,7 @@ msgstr "Ocultar pista"
 #: engines/freescape/games/eclipse/eclipse.cpp:330
 #: engines/hopkins/metaengine.cpp:236 engines/hugo/metaengine.cpp:165
 #: engines/lure/metaengine.cpp:191 engines/made/metaengine.cpp:192
-#: engines/mads/metaengine.cpp:343 engines/mohawk/myst.cpp:583
+#: engines/mads/metaengine.cpp:347 engines/mohawk/myst.cpp:583
 #: engines/mohawk/riven.cpp:864 engines/pegasus/pegasus.cpp:2825
 #: engines/petka/metaengine.cpp:198 engines/prince/metaengine.cpp:243
 #: engines/saga/metaengine.cpp:442 engines/sherlock/metaengine.cpp:462
@@ -7821,7 +7821,7 @@ msgstr "Base de datos de pistas"
 #: engines/freescape/games/dark/dark.cpp:236
 #: engines/freescape/games/driller/driller.cpp:129
 #: engines/freescape/games/eclipse/eclipse.cpp:340
-#: engines/mads/metaengine.cpp:399 engines/phoenixvr/metaengine.cpp:80
+#: engines/mads/metaengine.cpp:403 engines/phoenixvr/metaengine.cpp:80
 #: engines/saga/metaengine.cpp:437 engines/sherlock/metaengine.cpp:359
 #: engines/sherlock/metaengine.cpp:618 engines/sherlock/metaengine.cpp:852
 #: engines/stark/metaengine.cpp:280 engines/sword1/metaengine.cpp:295
@@ -10209,7 +10209,7 @@ msgid "Repeat last line"
 msgstr "Repetir la ??ltima frase"
 
 #: engines/hugo/metaengine.cpp:172 engines/lure/metaengine.cpp:197
-#: engines/mads/metaengine.cpp:349 engines/tsage/metaengine.cpp:247
+#: engines/mads/metaengine.cpp:353 engines/tsage/metaengine.cpp:247
 msgid "Restore game"
 msgstr "Cargar partida"
 
@@ -10401,7 +10401,7 @@ msgstr "Asignaciones de pausa"
 msgid "Direction keymappings"
 msgstr "Asignaciones de direcciones"
 
-#: engines/hypno/metaengine.cpp:316 engines/mads/metaengine.cpp:387
+#: engines/hypno/metaengine.cpp:316 engines/mads/metaengine.cpp:391
 #: engines/tsage/metaengine.cpp:259
 msgid "Show credits"
 msgstr "Mostrar cr??ditos"
@@ -11054,39 +11054,39 @@ msgstr "Interfaz de juego animada"
 msgid "Naughty game mode"
 msgstr "Modo de juego picante"
 
-#: engines/mads/metaengine.cpp:314
+#: engines/mads/metaengine.cpp:318
 msgid "Start menu keymappings"
 msgstr "Asignaciones del men?? de inicio"
 
-#: engines/mads/metaengine.cpp:337
+#: engines/mads/metaengine.cpp:341
 msgid "Open game menu"
 msgstr "Abrir men?? del juego"
 
-#: engines/mads/metaengine.cpp:355
+#: engines/mads/metaengine.cpp:359
 msgid "Scroll bar up"
 msgstr "Subir barra de desplazamiento"
 
-#: engines/mads/metaengine.cpp:361
+#: engines/mads/metaengine.cpp:365
 msgid "Scroll bar down"
 msgstr "Bajar barra de desplazamiento"
 
-#: engines/mads/metaengine.cpp:369
+#: engines/mads/metaengine.cpp:373
 msgid "Start game"
 msgstr "Iniciar partida"
 
-#: engines/mads/metaengine.cpp:375
+#: engines/mads/metaengine.cpp:379
 msgid "Resume game"
 msgstr "Cargar partida"
 
-#: engines/mads/metaengine.cpp:381
+#: engines/mads/metaengine.cpp:385
 msgid "Show intro"
 msgstr "Mostrar introducci??n"
 
-#: engines/mads/metaengine.cpp:393
+#: engines/mads/metaengine.cpp:397
 msgid "Show quotes"
 msgstr "Mostrar citas"
 
-#: engines/mads/metaengine.cpp:406
+#: engines/mads/metaengine.cpp:410
 msgid "Restart animation"
 msgstr "Reiniciar animaci??n"
 
diff --git a/po/eu.po b/po/eu.po
index 64a2ccffe6b..3c745d0b557 100644
--- a/po/eu.po
+++ b/po/eu.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.5.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-07 22:09+0000\n"
+"POT-Creation-Date: 2026-06-08 11:04+0000\n"
 "PO-Revision-Date: 2024-02-05 18:42+0000\n"
 "Last-Translator: Josu Igoa <josuigoa at ni.eus>\n"
 "Language-Team: Basque <https://translations.scummvm.org/projects/scummvm/"
@@ -3913,7 +3913,7 @@ msgstr "~A~ukerak..."
 #: engines/hugo/metaengine.cpp:134 engines/illusions/metaengine.cpp:94
 #: engines/kyra/engine/eobcommon.cpp:369 engines/kyra/engine/lol.cpp:505
 #: engines/lure/metaengine.cpp:177 engines/made/metaengine.cpp:137
-#: engines/mads/metaengine.cpp:318 engines/mm/metaengine.cpp:186
+#: engines/mads/metaengine.cpp:322 engines/mm/metaengine.cpp:186
 #: engines/mtropolis/metaengine.cpp:161 engines/nancy/input.cpp:181
 #: engines/parallaction/metaengine.cpp:138 engines/saga/metaengine.cpp:278
 #: engines/sludge/keymapper_tables.h:561 engines/sludge/keymapper_tables.h:580
@@ -3950,7 +3950,7 @@ msgstr "Erdiko klika"
 #: engines/hopkins/metaengine.cpp:217 engines/hugo/metaengine.cpp:141
 #: engines/illusions/metaengine.cpp:101 engines/kyra/engine/eobcommon.cpp:370
 #: engines/kyra/engine/lol.cpp:506 engines/lure/metaengine.cpp:183
-#: engines/made/metaengine.cpp:144 engines/mads/metaengine.cpp:324
+#: engines/made/metaengine.cpp:144 engines/mads/metaengine.cpp:328
 #: engines/mm/metaengine.cpp:192 engines/mtropolis/metaengine.cpp:167
 #: engines/nancy/input.cpp:188 engines/parallaction/metaengine.cpp:144
 #: engines/saga/metaengine.cpp:289 engines/sludge/keymapper_tables.h:567
@@ -6119,7 +6119,7 @@ msgstr "Eguneraketak bilatzen..."
 #: engines/hugo/metaengine.cpp:129 engines/hypno/metaengine.cpp:128
 #: engines/illusions/metaengine.cpp:89 engines/lab/metaengine.cpp:149
 #: engines/lure/metaengine.cpp:168 engines/made/metaengine.cpp:132
-#: engines/mads/metaengine.cpp:312 engines/neverhood/metaengine.cpp:185
+#: engines/mads/metaengine.cpp:316 engines/neverhood/metaengine.cpp:185
 #: engines/ngi/metaengine.cpp:115 engines/petka/metaengine.cpp:113
 #: engines/prince/metaengine.cpp:227 engines/private/metaengine.cpp:127
 #: engines/queen/metaengine.cpp:146 engines/sherlock/metaengine.cpp:313
@@ -6686,7 +6686,7 @@ msgstr ""
 #: engines/hypno/metaengine.cpp:267 engines/hypno/metaengine.cpp:311
 #: engines/illusions/metaengine.cpp:90 engines/lab/metaengine.cpp:152
 #: engines/lure/metaengine.cpp:169 engines/made/metaengine.cpp:133
-#: engines/mads/metaengine.cpp:313 engines/neverhood/metaengine.cpp:186
+#: engines/mads/metaengine.cpp:317 engines/neverhood/metaengine.cpp:186
 #: engines/queen/metaengine.cpp:147 engines/saga/metaengine.cpp:268
 #: engines/sludge/keymapper_tables.h:33 engines/stark/metaengine.cpp:185
 #: engines/sword1/metaengine.cpp:267 engines/tinsel/metaengine.cpp:241
@@ -7208,7 +7208,7 @@ msgstr ""
 #: engines/bbvs/metaengine.cpp:111 engines/cruise/metaengine.cpp:223
 #: engines/freescape/movement.cpp:107 engines/got/metaengine.cpp:78
 #: engines/hugo/metaengine.cpp:197 engines/lure/metaengine.cpp:209
-#: engines/mads/metaengine.cpp:330 engines/mm/mm1/metaengine.cpp:42
+#: engines/mads/metaengine.cpp:334 engines/mm/mm1/metaengine.cpp:42
 #: engines/mm/mm1/metaengine.cpp:48 engines/saga/metaengine.cpp:328
 #: engines/tinsel/metaengine.cpp:258 engines/tsage/metaengine.cpp:190
 #: engines/tucker/metaengine.cpp:221 engines/twine/metaengine.cpp:388
@@ -7519,7 +7519,7 @@ msgstr "Mututu/Ez-mututu"
 #: engines/freescape/games/eclipse/eclipse.cpp:330
 #: engines/hopkins/metaengine.cpp:236 engines/hugo/metaengine.cpp:165
 #: engines/lure/metaengine.cpp:191 engines/made/metaengine.cpp:192
-#: engines/mads/metaengine.cpp:343 engines/mohawk/myst.cpp:583
+#: engines/mads/metaengine.cpp:347 engines/mohawk/myst.cpp:583
 #: engines/mohawk/riven.cpp:864 engines/pegasus/pegasus.cpp:2825
 #: engines/petka/metaengine.cpp:198 engines/prince/metaengine.cpp:243
 #: engines/saga/metaengine.cpp:442 engines/sherlock/metaengine.cpp:462
@@ -7597,7 +7597,7 @@ msgstr ""
 #: engines/freescape/games/dark/dark.cpp:236
 #: engines/freescape/games/driller/driller.cpp:129
 #: engines/freescape/games/eclipse/eclipse.cpp:340
-#: engines/mads/metaengine.cpp:399 engines/phoenixvr/metaengine.cpp:80
+#: engines/mads/metaengine.cpp:403 engines/phoenixvr/metaengine.cpp:80
 #: engines/saga/metaengine.cpp:437 engines/sherlock/metaengine.cpp:359
 #: engines/sherlock/metaengine.cpp:618 engines/sherlock/metaengine.cpp:852
 #: engines/stark/metaengine.cpp:280 engines/sword1/metaengine.cpp:295
@@ -10268,7 +10268,7 @@ msgid "Repeat last line"
 msgstr "Ez dago datarik gordeta"
 
 #: engines/hugo/metaengine.cpp:172 engines/lure/metaengine.cpp:197
-#: engines/mads/metaengine.cpp:349 engines/tsage/metaengine.cpp:247
+#: engines/mads/metaengine.cpp:353 engines/tsage/metaengine.cpp:247
 #, fuzzy
 #| msgid "Restore game:"
 msgid "Restore game"
@@ -10485,7 +10485,7 @@ msgstr "~A~ukerak..."
 msgid "Direction keymappings"
 msgstr "~A~ukerak..."
 
-#: engines/hypno/metaengine.cpp:316 engines/mads/metaengine.cpp:387
+#: engines/hypno/metaengine.cpp:316 engines/mads/metaengine.cpp:391
 #: engines/tsage/metaengine.cpp:259
 #, fuzzy
 msgid "Show credits"
@@ -11165,49 +11165,49 @@ msgstr ""
 msgid "Naughty game mode"
 msgstr ""
 
-#: engines/mads/metaengine.cpp:314
+#: engines/mads/metaengine.cpp:318
 #, fuzzy
 msgid "Start menu keymappings"
 msgstr "~A~ukerak..."
 
-#: engines/mads/metaengine.cpp:337
+#: engines/mads/metaengine.cpp:341
 #, fuzzy
 msgid "Open game menu"
 msgstr "It~z~uli abiarazlera"
 
-#: engines/mads/metaengine.cpp:355
+#: engines/mads/metaengine.cpp:359
 #, fuzzy
 msgid "Scroll bar up"
 msgstr "Gora"
 
-#: engines/mads/metaengine.cpp:361
+#: engines/mads/metaengine.cpp:365
 #, fuzzy
 msgid "Scroll bar down"
 msgstr "Behera"
 
-#: engines/mads/metaengine.cpp:369
+#: engines/mads/metaengine.cpp:373
 #, fuzzy
 #| msgid "Restore game:"
 msgid "Start game"
 msgstr "Jokoa kargatu:"
 
-#: engines/mads/metaengine.cpp:375
+#: engines/mads/metaengine.cpp:379
 #, fuzzy
 #| msgid "Restore game:"
 msgid "Resume game"
 msgstr "Jokoa kargatu:"
 
-#: engines/mads/metaengine.cpp:381
+#: engines/mads/metaengine.cpp:385
 #, fuzzy
 msgid "Show intro"
 msgstr "Erakutsi fitxategi ezkutuak"
 
-#: engines/mads/metaengine.cpp:393
+#: engines/mads/metaengine.cpp:397
 #, fuzzy
 msgid "Show quotes"
 msgstr "Ahotsak bakarrik"
 
-#: engines/mads/metaengine.cpp:406
+#: engines/mads/metaengine.cpp:410
 #, fuzzy
 msgid "Restart animation"
 msgstr "~T~rantsizioak gaituta"
diff --git a/po/fi_FI.po b/po/fi_FI.po
index 98307ddbf3f..1231163dea8 100644
--- a/po/fi_FI.po
+++ b/po/fi_FI.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.6.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-07 22:09+0000\n"
+"POT-Creation-Date: 2026-06-08 11:04+0000\n"
 "PO-Revision-Date: 2026-02-07 15:22+0000\n"
 "Last-Translator: Timo Mikkolainen <tmikkola at gmail.com>\n"
 "Language-Team: Finnish <https://translations.scummvm.org/projects/scummvm/"
@@ -3966,7 +3966,7 @@ msgstr "Oletusn??pp??inasetukset"
 #: engines/hugo/metaengine.cpp:134 engines/illusions/metaengine.cpp:94
 #: engines/kyra/engine/eobcommon.cpp:369 engines/kyra/engine/lol.cpp:505
 #: engines/lure/metaengine.cpp:177 engines/made/metaengine.cpp:137
-#: engines/mads/metaengine.cpp:318 engines/mm/metaengine.cpp:186
+#: engines/mads/metaengine.cpp:322 engines/mm/metaengine.cpp:186
 #: engines/mtropolis/metaengine.cpp:161 engines/nancy/input.cpp:181
 #: engines/parallaction/metaengine.cpp:138 engines/saga/metaengine.cpp:278
 #: engines/sludge/keymapper_tables.h:561 engines/sludge/keymapper_tables.h:580
@@ -3999,7 +3999,7 @@ msgstr "Keskinapsautus"
 #: engines/hopkins/metaengine.cpp:217 engines/hugo/metaengine.cpp:141
 #: engines/illusions/metaengine.cpp:101 engines/kyra/engine/eobcommon.cpp:370
 #: engines/kyra/engine/lol.cpp:506 engines/lure/metaengine.cpp:183
-#: engines/made/metaengine.cpp:144 engines/mads/metaengine.cpp:324
+#: engines/made/metaengine.cpp:144 engines/mads/metaengine.cpp:328
 #: engines/mm/metaengine.cpp:192 engines/mtropolis/metaengine.cpp:167
 #: engines/nancy/input.cpp:188 engines/parallaction/metaengine.cpp:144
 #: engines/saga/metaengine.cpp:289 engines/sludge/keymapper_tables.h:567
@@ -6463,7 +6463,7 @@ msgstr "Tarkista p??ivitykset..."
 #: engines/hugo/metaengine.cpp:129 engines/hypno/metaengine.cpp:128
 #: engines/illusions/metaengine.cpp:89 engines/lab/metaengine.cpp:149
 #: engines/lure/metaengine.cpp:168 engines/made/metaengine.cpp:132
-#: engines/mads/metaengine.cpp:312 engines/neverhood/metaengine.cpp:185
+#: engines/mads/metaengine.cpp:316 engines/neverhood/metaengine.cpp:185
 #: engines/ngi/metaengine.cpp:115 engines/petka/metaengine.cpp:113
 #: engines/prince/metaengine.cpp:227 engines/private/metaengine.cpp:127
 #: engines/queen/metaengine.cpp:146 engines/sherlock/metaengine.cpp:313
@@ -7021,7 +7021,7 @@ msgstr "AGOS p????taso"
 #: engines/hypno/metaengine.cpp:267 engines/hypno/metaengine.cpp:311
 #: engines/illusions/metaengine.cpp:90 engines/lab/metaengine.cpp:152
 #: engines/lure/metaengine.cpp:169 engines/made/metaengine.cpp:133
-#: engines/mads/metaengine.cpp:313 engines/neverhood/metaengine.cpp:186
+#: engines/mads/metaengine.cpp:317 engines/neverhood/metaengine.cpp:186
 #: engines/queen/metaengine.cpp:147 engines/saga/metaengine.cpp:268
 #: engines/sludge/keymapper_tables.h:33 engines/stark/metaengine.cpp:185
 #: engines/sword1/metaengine.cpp:267 engines/tinsel/metaengine.cpp:241
@@ -7523,7 +7523,7 @@ msgstr "Sotilas 714:n silm??huijaus"
 #: engines/bbvs/metaengine.cpp:111 engines/cruise/metaengine.cpp:223
 #: engines/freescape/movement.cpp:107 engines/got/metaengine.cpp:78
 #: engines/hugo/metaengine.cpp:197 engines/lure/metaengine.cpp:209
-#: engines/mads/metaengine.cpp:330 engines/mm/mm1/metaengine.cpp:42
+#: engines/mads/metaengine.cpp:334 engines/mm/mm1/metaengine.cpp:42
 #: engines/mm/mm1/metaengine.cpp:48 engines/saga/metaengine.cpp:328
 #: engines/tinsel/metaengine.cpp:258 engines/tsage/metaengine.cpp:190
 #: engines/tucker/metaengine.cpp:221 engines/twine/metaengine.cpp:388
@@ -7828,7 +7828,7 @@ msgstr "Vihjeen julkisuus p????lle / pois"
 #: engines/freescape/games/eclipse/eclipse.cpp:330
 #: engines/hopkins/metaengine.cpp:236 engines/hugo/metaengine.cpp:165
 #: engines/lure/metaengine.cpp:191 engines/made/metaengine.cpp:192
-#: engines/mads/metaengine.cpp:343 engines/mohawk/myst.cpp:583
+#: engines/mads/metaengine.cpp:347 engines/mohawk/myst.cpp:583
 #: engines/mohawk/riven.cpp:864 engines/pegasus/pegasus.cpp:2825
 #: engines/petka/metaengine.cpp:198 engines/prince/metaengine.cpp:243
 #: engines/saga/metaengine.cpp:442 engines/sherlock/metaengine.cpp:462
@@ -7904,7 +7904,7 @@ msgstr "Vihjeiden tietokanta"
 #: engines/freescape/games/dark/dark.cpp:236
 #: engines/freescape/games/driller/driller.cpp:129
 #: engines/freescape/games/eclipse/eclipse.cpp:340
-#: engines/mads/metaengine.cpp:399 engines/phoenixvr/metaengine.cpp:80
+#: engines/mads/metaengine.cpp:403 engines/phoenixvr/metaengine.cpp:80
 #: engines/saga/metaengine.cpp:437 engines/sherlock/metaengine.cpp:359
 #: engines/sherlock/metaengine.cpp:618 engines/sherlock/metaengine.cpp:852
 #: engines/stark/metaengine.cpp:280 engines/sword1/metaengine.cpp:295
@@ -10407,7 +10407,7 @@ msgid "Repeat last line"
 msgstr "Toista viimeisin rivi"
 
 #: engines/hugo/metaengine.cpp:172 engines/lure/metaengine.cpp:197
-#: engines/mads/metaengine.cpp:349 engines/tsage/metaengine.cpp:247
+#: engines/mads/metaengine.cpp:353 engines/tsage/metaengine.cpp:247
 msgid "Restore game"
 msgstr "Lataa pelitallenne"
 
@@ -10612,7 +10612,7 @@ msgstr "Valikon n??pp??inkartoitukset"
 msgid "Direction keymappings"
 msgstr "Intron n??pp??inkartoitukset"
 
-#: engines/hypno/metaengine.cpp:316 engines/mads/metaengine.cpp:387
+#: engines/hypno/metaengine.cpp:316 engines/mads/metaengine.cpp:391
 #: engines/tsage/metaengine.cpp:259
 msgid "Show credits"
 msgstr "N??yt?? lopputekstit"
@@ -11255,39 +11255,39 @@ msgstr "Animoitu pelik??ytt??liittym??"
 msgid "Naughty game mode"
 msgstr "Tuhma pelitila"
 
-#: engines/mads/metaengine.cpp:314
+#: engines/mads/metaengine.cpp:318
 msgid "Start menu keymappings"
 msgstr "K??ynnistysvalikon n??pp??inkartoitukset"
 
-#: engines/mads/metaengine.cpp:337
+#: engines/mads/metaengine.cpp:341
 msgid "Open game menu"
 msgstr "Avaa pelin valikko"
 
-#: engines/mads/metaengine.cpp:355
+#: engines/mads/metaengine.cpp:359
 msgid "Scroll bar up"
 msgstr "Vierit?? yl??s"
 
-#: engines/mads/metaengine.cpp:361
+#: engines/mads/metaengine.cpp:365
 msgid "Scroll bar down"
 msgstr "Vierit?? alas"
 
-#: engines/mads/metaengine.cpp:369
+#: engines/mads/metaengine.cpp:373
 msgid "Start game"
 msgstr "K??ynnist?? peli"
 
-#: engines/mads/metaengine.cpp:375
+#: engines/mads/metaengine.cpp:379
 msgid "Resume game"
 msgstr "Jatka peli??"
 
-#: engines/mads/metaengine.cpp:381
+#: engines/mads/metaengine.cpp:385
 msgid "Show intro"
 msgstr "N??yt?? intro"
 
-#: engines/mads/metaengine.cpp:393
+#: engines/mads/metaengine.cpp:397
 msgid "Show quotes"
 msgstr "N??yt?? lainaukset"
 
-#: engines/mads/metaengine.cpp:406
+#: engines/mads/metaengine.cpp:410
 msgid "Restart animation"
 msgstr "Uudelleenk??ynnist?? animaatio"
 
diff --git a/po/fr_FR.po b/po/fr_FR.po
index 9d9302420a7..c2c2c8c8279 100644
--- a/po/fr_FR.po
+++ b/po/fr_FR.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-07 22:09+0000\n"
+"POT-Creation-Date: 2026-06-08 11:04+0000\n"
 "PO-Revision-Date: 2026-06-07 13:22+0000\n"
 "Last-Translator: Purple T <ZEONK at hotmail.com>\n"
 "Language-Team: French <https://translations.scummvm.org/projects/scummvm/"
@@ -3969,7 +3969,7 @@ msgstr "Conf. de touches par d??faut"
 #: engines/hugo/metaengine.cpp:134 engines/illusions/metaengine.cpp:94
 #: engines/kyra/engine/eobcommon.cpp:369 engines/kyra/engine/lol.cpp:505
 #: engines/lure/metaengine.cpp:177 engines/made/metaengine.cpp:137
-#: engines/mads/metaengine.cpp:318 engines/mm/metaengine.cpp:186
+#: engines/mads/metaengine.cpp:322 engines/mm/metaengine.cpp:186
 #: engines/mtropolis/metaengine.cpp:161 engines/nancy/input.cpp:181
 #: engines/parallaction/metaengine.cpp:138 engines/saga/metaengine.cpp:278
 #: engines/sludge/keymapper_tables.h:561 engines/sludge/keymapper_tables.h:580
@@ -4002,7 +4002,7 @@ msgstr "Clic Milieu"
 #: engines/hopkins/metaengine.cpp:217 engines/hugo/metaengine.cpp:141
 #: engines/illusions/metaengine.cpp:101 engines/kyra/engine/eobcommon.cpp:370
 #: engines/kyra/engine/lol.cpp:506 engines/lure/metaengine.cpp:183
-#: engines/made/metaengine.cpp:144 engines/mads/metaengine.cpp:324
+#: engines/made/metaengine.cpp:144 engines/mads/metaengine.cpp:328
 #: engines/mm/metaengine.cpp:192 engines/mtropolis/metaengine.cpp:167
 #: engines/nancy/input.cpp:188 engines/parallaction/metaengine.cpp:144
 #: engines/saga/metaengine.cpp:289 engines/sludge/keymapper_tables.h:567
@@ -6381,7 +6381,7 @@ msgstr "Recherche des mises ?? jour..."
 #: engines/hugo/metaengine.cpp:129 engines/hypno/metaengine.cpp:128
 #: engines/illusions/metaengine.cpp:89 engines/lab/metaengine.cpp:149
 #: engines/lure/metaengine.cpp:168 engines/made/metaengine.cpp:132
-#: engines/mads/metaengine.cpp:312 engines/neverhood/metaengine.cpp:185
+#: engines/mads/metaengine.cpp:316 engines/neverhood/metaengine.cpp:185
 #: engines/ngi/metaengine.cpp:115 engines/petka/metaengine.cpp:113
 #: engines/prince/metaengine.cpp:227 engines/private/metaengine.cpp:127
 #: engines/queen/metaengine.cpp:146 engines/sherlock/metaengine.cpp:313
@@ -6944,7 +6944,7 @@ msgstr "AGOS principal"
 #: engines/hypno/metaengine.cpp:267 engines/hypno/metaengine.cpp:311
 #: engines/illusions/metaengine.cpp:90 engines/lab/metaengine.cpp:152
 #: engines/lure/metaengine.cpp:169 engines/made/metaengine.cpp:133
-#: engines/mads/metaengine.cpp:313 engines/neverhood/metaengine.cpp:186
+#: engines/mads/metaengine.cpp:317 engines/neverhood/metaengine.cpp:186
 #: engines/queen/metaengine.cpp:147 engines/saga/metaengine.cpp:268
 #: engines/sludge/keymapper_tables.h:33 engines/stark/metaengine.cpp:185
 #: engines/sword1/metaengine.cpp:267 engines/tinsel/metaengine.cpp:241
@@ -7443,7 +7443,7 @@ msgstr "Triche 'Soldier 714 Eye'"
 #: engines/bbvs/metaengine.cpp:111 engines/cruise/metaengine.cpp:223
 #: engines/freescape/movement.cpp:107 engines/got/metaengine.cpp:78
 #: engines/hugo/metaengine.cpp:197 engines/lure/metaengine.cpp:209
-#: engines/mads/metaengine.cpp:330 engines/mm/mm1/metaengine.cpp:42
+#: engines/mads/metaengine.cpp:334 engines/mm/mm1/metaengine.cpp:42
 #: engines/mm/mm1/metaengine.cpp:48 engines/saga/metaengine.cpp:328
 #: engines/tinsel/metaengine.cpp:258 engines/tsage/metaengine.cpp:190
 #: engines/tucker/metaengine.cpp:221 engines/twine/metaengine.cpp:388
@@ -7750,7 +7750,7 @@ msgstr "Act. / D??sact. indice priv??"
 #: engines/freescape/games/eclipse/eclipse.cpp:330
 #: engines/hopkins/metaengine.cpp:236 engines/hugo/metaengine.cpp:165
 #: engines/lure/metaengine.cpp:191 engines/made/metaengine.cpp:192
-#: engines/mads/metaengine.cpp:343 engines/mohawk/myst.cpp:583
+#: engines/mads/metaengine.cpp:347 engines/mohawk/myst.cpp:583
 #: engines/mohawk/riven.cpp:864 engines/pegasus/pegasus.cpp:2825
 #: engines/petka/metaengine.cpp:198 engines/prince/metaengine.cpp:243
 #: engines/saga/metaengine.cpp:442 engines/sherlock/metaengine.cpp:462
@@ -7826,7 +7826,7 @@ msgstr "Panneau des indices"
 #: engines/freescape/games/dark/dark.cpp:236
 #: engines/freescape/games/driller/driller.cpp:129
 #: engines/freescape/games/eclipse/eclipse.cpp:340
-#: engines/mads/metaengine.cpp:399 engines/phoenixvr/metaengine.cpp:80
+#: engines/mads/metaengine.cpp:403 engines/phoenixvr/metaengine.cpp:80
 #: engines/saga/metaengine.cpp:437 engines/sherlock/metaengine.cpp:359
 #: engines/sherlock/metaengine.cpp:618 engines/sherlock/metaengine.cpp:852
 #: engines/stark/metaengine.cpp:280 engines/sword1/metaengine.cpp:295
@@ -10211,7 +10211,7 @@ msgid "Repeat last line"
 msgstr "R??p??ter la derni??re ligne"
 
 #: engines/hugo/metaengine.cpp:172 engines/lure/metaengine.cpp:197
-#: engines/mads/metaengine.cpp:349 engines/tsage/metaengine.cpp:247
+#: engines/mads/metaengine.cpp:353 engines/tsage/metaengine.cpp:247
 msgid "Restore game"
 msgstr "Restaurer"
 
@@ -10403,7 +10403,7 @@ msgstr "Conf. des touches Pause"
 msgid "Direction keymappings"
 msgstr "Conf. des touches Directions"
 
-#: engines/hypno/metaengine.cpp:316 engines/mads/metaengine.cpp:387
+#: engines/hypno/metaengine.cpp:316 engines/mads/metaengine.cpp:391
 #: engines/tsage/metaengine.cpp:259
 msgid "Show credits"
 msgstr "Afficher les cr??dits"
@@ -11064,39 +11064,39 @@ msgstr "Interface du jeu anim??e"
 msgid "Naughty game mode"
 msgstr "Mode coquin"
 
-#: engines/mads/metaengine.cpp:314
+#: engines/mads/metaengine.cpp:318
 msgid "Start menu keymappings"
 msgstr "Conf. des touches Menu D??marrage"
 
-#: engines/mads/metaengine.cpp:337
+#: engines/mads/metaengine.cpp:341
 msgid "Open game menu"
 msgstr "Ouvrir le menu du jeu"
 
-#: engines/mads/metaengine.cpp:355
+#: engines/mads/metaengine.cpp:359
 msgid "Scroll bar up"
 msgstr "Barre de d??filement haut"
 
-#: engines/mads/metaengine.cpp:361
+#: engines/mads/metaengine.cpp:365
 msgid "Scroll bar down"
 msgstr "Barre de d??filement bas"
 
-#: engines/mads/metaengine.cpp:369
+#: engines/mads/metaengine.cpp:373
 msgid "Start game"
 msgstr "D??marrer une partie"
 
-#: engines/mads/metaengine.cpp:375
+#: engines/mads/metaengine.cpp:379
 msgid "Resume game"
 msgstr "Reprendre la partie"
 
-#: engines/mads/metaengine.cpp:381
+#: engines/mads/metaengine.cpp:385
 msgid "Show intro"
 msgstr "Afficher l'introduction"
 
-#: engines/mads/metaengine.cpp:393
+#: engines/mads/metaengine.cpp:397
 msgid "Show quotes"
 msgstr "Afficher les citations"
 
-#: engines/mads/metaengine.cpp:406
+#: engines/mads/metaengine.cpp:410
 msgid "Restart animation"
 msgstr "Red??marrer l'animation"
 
diff --git a/po/gl_ES.po b/po/gl_ES.po
index cfff58ef99d..747f8704a77 100644
--- a/po/gl_ES.po
+++ b/po/gl_ES.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-07 22:09+0000\n"
+"POT-Creation-Date: 2026-06-08 11:04+0000\n"
 "PO-Revision-Date: 2020-09-26 21:13+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Galician <https://translations.scummvm.org/projects/scummvm/"
@@ -3874,7 +3874,7 @@ msgstr "~O~pci??ns..."
 #: engines/hugo/metaengine.cpp:134 engines/illusions/metaengine.cpp:94
 #: engines/kyra/engine/eobcommon.cpp:369 engines/kyra/engine/lol.cpp:505
 #: engines/lure/metaengine.cpp:177 engines/made/metaengine.cpp:137
-#: engines/mads/metaengine.cpp:318 engines/mm/metaengine.cpp:186
+#: engines/mads/metaengine.cpp:322 engines/mm/metaengine.cpp:186
 #: engines/mtropolis/metaengine.cpp:161 engines/nancy/input.cpp:181
 #: engines/parallaction/metaengine.cpp:138 engines/saga/metaengine.cpp:278
 #: engines/sludge/keymapper_tables.h:561 engines/sludge/keymapper_tables.h:580
@@ -3911,7 +3911,7 @@ msgstr "Bot??n central"
 #: engines/hopkins/metaengine.cpp:217 engines/hugo/metaengine.cpp:141
 #: engines/illusions/metaengine.cpp:101 engines/kyra/engine/eobcommon.cpp:370
 #: engines/kyra/engine/lol.cpp:506 engines/lure/metaengine.cpp:183
-#: engines/made/metaengine.cpp:144 engines/mads/metaengine.cpp:324
+#: engines/made/metaengine.cpp:144 engines/mads/metaengine.cpp:328
 #: engines/mm/metaengine.cpp:192 engines/mtropolis/metaengine.cpp:167
 #: engines/nancy/input.cpp:188 engines/parallaction/metaengine.cpp:144
 #: engines/saga/metaengine.cpp:289 engines/sludge/keymapper_tables.h:567
@@ -6053,7 +6053,7 @@ msgstr "Buscar actualizaci??ns..."
 #: engines/hugo/metaengine.cpp:129 engines/hypno/metaengine.cpp:128
 #: engines/illusions/metaengine.cpp:89 engines/lab/metaengine.cpp:149
 #: engines/lure/metaengine.cpp:168 engines/made/metaengine.cpp:132
-#: engines/mads/metaengine.cpp:312 engines/neverhood/metaengine.cpp:185
+#: engines/mads/metaengine.cpp:316 engines/neverhood/metaengine.cpp:185
 #: engines/ngi/metaengine.cpp:115 engines/petka/metaengine.cpp:113
 #: engines/prince/metaengine.cpp:227 engines/private/metaengine.cpp:127
 #: engines/queen/metaengine.cpp:146 engines/sherlock/metaengine.cpp:313
@@ -6621,7 +6621,7 @@ msgstr ""
 #: engines/hypno/metaengine.cpp:267 engines/hypno/metaengine.cpp:311
 #: engines/illusions/metaengine.cpp:90 engines/lab/metaengine.cpp:152
 #: engines/lure/metaengine.cpp:169 engines/made/metaengine.cpp:133
-#: engines/mads/metaengine.cpp:313 engines/neverhood/metaengine.cpp:186
+#: engines/mads/metaengine.cpp:317 engines/neverhood/metaengine.cpp:186
 #: engines/queen/metaengine.cpp:147 engines/saga/metaengine.cpp:268
 #: engines/sludge/keymapper_tables.h:33 engines/stark/metaengine.cpp:185
 #: engines/sword1/metaengine.cpp:267 engines/tinsel/metaengine.cpp:241
@@ -7142,7 +7142,7 @@ msgstr ""
 #: engines/bbvs/metaengine.cpp:111 engines/cruise/metaengine.cpp:223
 #: engines/freescape/movement.cpp:107 engines/got/metaengine.cpp:78
 #: engines/hugo/metaengine.cpp:197 engines/lure/metaengine.cpp:209
-#: engines/mads/metaengine.cpp:330 engines/mm/mm1/metaengine.cpp:42
+#: engines/mads/metaengine.cpp:334 engines/mm/mm1/metaengine.cpp:42
 #: engines/mm/mm1/metaengine.cpp:48 engines/saga/metaengine.cpp:328
 #: engines/tinsel/metaengine.cpp:258 engines/tsage/metaengine.cpp:190
 #: engines/tucker/metaengine.cpp:221 engines/twine/metaengine.cpp:388
@@ -7452,7 +7452,7 @@ msgstr "Activar/desactivar captura de rato"
 #: engines/freescape/games/eclipse/eclipse.cpp:330
 #: engines/hopkins/metaengine.cpp:236 engines/hugo/metaengine.cpp:165
 #: engines/lure/metaengine.cpp:191 engines/made/metaengine.cpp:192
-#: engines/mads/metaengine.cpp:343 engines/mohawk/myst.cpp:583
+#: engines/mads/metaengine.cpp:347 engines/mohawk/myst.cpp:583
 #: engines/mohawk/riven.cpp:864 engines/pegasus/pegasus.cpp:2825
 #: engines/petka/metaengine.cpp:198 engines/prince/metaengine.cpp:243
 #: engines/saga/metaengine.cpp:442 engines/sherlock/metaengine.cpp:462
@@ -7530,7 +7530,7 @@ msgstr ""
 #: engines/freescape/games/dark/dark.cpp:236
 #: engines/freescape/games/driller/driller.cpp:129
 #: engines/freescape/games/eclipse/eclipse.cpp:340
-#: engines/mads/metaengine.cpp:399 engines/phoenixvr/metaengine.cpp:80
+#: engines/mads/metaengine.cpp:403 engines/phoenixvr/metaengine.cpp:80
 #: engines/saga/metaengine.cpp:437 engines/sherlock/metaengine.cpp:359
 #: engines/sherlock/metaengine.cpp:618 engines/sherlock/metaengine.cpp:852
 #: engines/stark/metaengine.cpp:280 engines/sword1/metaengine.cpp:295
@@ -10175,7 +10175,7 @@ msgid "Repeat last line"
 msgstr "Non hai data gardada"
 
 #: engines/hugo/metaengine.cpp:172 engines/lure/metaengine.cpp:197
-#: engines/mads/metaengine.cpp:349 engines/tsage/metaengine.cpp:247
+#: engines/mads/metaengine.cpp:353 engines/tsage/metaengine.cpp:247
 #, fuzzy
 #| msgid "Restore game:"
 msgid "Restore game"
@@ -10393,7 +10393,7 @@ msgstr "~O~pci??ns..."
 msgid "Direction keymappings"
 msgstr "~O~pci??ns..."
 
-#: engines/hypno/metaengine.cpp:316 engines/mads/metaengine.cpp:387
+#: engines/hypno/metaengine.cpp:316 engines/mads/metaengine.cpp:391
 #: engines/tsage/metaengine.cpp:259
 #, fuzzy
 msgid "Show credits"
@@ -11088,49 +11088,49 @@ msgstr "Interface de xogo animada"
 msgid "Naughty game mode"
 msgstr "Modo de xogo rebelde"
 
-#: engines/mads/metaengine.cpp:314
+#: engines/mads/metaengine.cpp:318
 #, fuzzy
 msgid "Start menu keymappings"
 msgstr "~O~pci??ns..."
 
-#: engines/mads/metaengine.cpp:337
+#: engines/mads/metaengine.cpp:341
 #, fuzzy
 msgid "Open game menu"
 msgstr "~V~olver ao Iniciador"
 
-#: engines/mads/metaengine.cpp:355
+#: engines/mads/metaengine.cpp:359
 #, fuzzy
 msgid "Scroll bar up"
 msgstr "Subir lista"
 
-#: engines/mads/metaengine.cpp:361
+#: engines/mads/metaengine.cpp:365
 #, fuzzy
 msgid "Scroll bar down"
 msgstr "Baixar lista"
 
-#: engines/mads/metaengine.cpp:369
+#: engines/mads/metaengine.cpp:373
 #, fuzzy
 #| msgid "Restore game:"
 msgid "Start game"
 msgstr "Restaurar xogo:"
 
-#: engines/mads/metaengine.cpp:375
+#: engines/mads/metaengine.cpp:379
 #, fuzzy
 #| msgid "Restore game:"
 msgid "Resume game"
 msgstr "Restaurar xogo:"
 
-#: engines/mads/metaengine.cpp:381
+#: engines/mads/metaengine.cpp:385
 #, fuzzy
 msgid "Show intro"
 msgstr "Mostrar ficheiros ocultos"
 
-#: engines/mads/metaengine.cpp:393
+#: engines/mads/metaengine.cpp:397
 #, fuzzy
 msgid "Show quotes"
 msgstr "S?? voz"
 
-#: engines/mads/metaengine.cpp:406
+#: engines/mads/metaengine.cpp:410
 #, fuzzy
 #| msgid "Transitions:"
 msgid "Restart animation"
diff --git a/po/he.po b/po/he.po
index b8248295ac9..0cac186ac42 100644
--- a/po/he.po
+++ b/po/he.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.1.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-07 22:09+0000\n"
+"POT-Creation-Date: 2026-06-08 11:04+0000\n"
 "PO-Revision-Date: 2025-04-09 20:18+0000\n"
 "Last-Translator: Niv Baehr <bloop93 at gmail.com>\n"
 "Language-Team: Hebrew <https://translations.scummvm.org/projects/scummvm/"
@@ -3823,7 +3823,7 @@ msgstr "?????????? ??????????"
 #: engines/hugo/metaengine.cpp:134 engines/illusions/metaengine.cpp:94
 #: engines/kyra/engine/eobcommon.cpp:369 engines/kyra/engine/lol.cpp:505
 #: engines/lure/metaengine.cpp:177 engines/made/metaengine.cpp:137
-#: engines/mads/metaengine.cpp:318 engines/mm/metaengine.cpp:186
+#: engines/mads/metaengine.cpp:322 engines/mm/metaengine.cpp:186
 #: engines/mtropolis/metaengine.cpp:161 engines/nancy/input.cpp:181
 #: engines/parallaction/metaengine.cpp:138 engines/saga/metaengine.cpp:278
 #: engines/sludge/keymapper_tables.h:561 engines/sludge/keymapper_tables.h:580
@@ -3860,7 +3860,7 @@ msgstr "?????????? ????????????"
 #: engines/hopkins/metaengine.cpp:217 engines/hugo/metaengine.cpp:141
 #: engines/illusions/metaengine.cpp:101 engines/kyra/engine/eobcommon.cpp:370
 #: engines/kyra/engine/lol.cpp:506 engines/lure/metaengine.cpp:183
-#: engines/made/metaengine.cpp:144 engines/mads/metaengine.cpp:324
+#: engines/made/metaengine.cpp:144 engines/mads/metaengine.cpp:328
 #: engines/mm/metaengine.cpp:192 engines/mtropolis/metaengine.cpp:167
 #: engines/nancy/input.cpp:188 engines/parallaction/metaengine.cpp:144
 #: engines/saga/metaengine.cpp:289 engines/sludge/keymapper_tables.h:567
@@ -6011,7 +6011,7 @@ msgstr "?????????? ??????????????..."
 #: engines/hugo/metaengine.cpp:129 engines/hypno/metaengine.cpp:128
 #: engines/illusions/metaengine.cpp:89 engines/lab/metaengine.cpp:149
 #: engines/lure/metaengine.cpp:168 engines/made/metaengine.cpp:132
-#: engines/mads/metaengine.cpp:312 engines/neverhood/metaengine.cpp:185
+#: engines/mads/metaengine.cpp:316 engines/neverhood/metaengine.cpp:185
 #: engines/ngi/metaengine.cpp:115 engines/petka/metaengine.cpp:113
 #: engines/prince/metaengine.cpp:227 engines/private/metaengine.cpp:127
 #: engines/queen/metaengine.cpp:146 engines/sherlock/metaengine.cpp:313
@@ -6577,7 +6577,7 @@ msgstr ""
 #: engines/hypno/metaengine.cpp:267 engines/hypno/metaengine.cpp:311
 #: engines/illusions/metaengine.cpp:90 engines/lab/metaengine.cpp:152
 #: engines/lure/metaengine.cpp:169 engines/made/metaengine.cpp:133
-#: engines/mads/metaengine.cpp:313 engines/neverhood/metaengine.cpp:186
+#: engines/mads/metaengine.cpp:317 engines/neverhood/metaengine.cpp:186
 #: engines/queen/metaengine.cpp:147 engines/saga/metaengine.cpp:268
 #: engines/sludge/keymapper_tables.h:33 engines/stark/metaengine.cpp:185
 #: engines/sword1/metaengine.cpp:267 engines/tinsel/metaengine.cpp:241
@@ -7118,7 +7118,7 @@ msgstr ""
 #: engines/bbvs/metaengine.cpp:111 engines/cruise/metaengine.cpp:223
 #: engines/freescape/movement.cpp:107 engines/got/metaengine.cpp:78
 #: engines/hugo/metaengine.cpp:197 engines/lure/metaengine.cpp:209
-#: engines/mads/metaengine.cpp:330 engines/mm/mm1/metaengine.cpp:42
+#: engines/mads/metaengine.cpp:334 engines/mm/mm1/metaengine.cpp:42
 #: engines/mm/mm1/metaengine.cpp:48 engines/saga/metaengine.cpp:328
 #: engines/tinsel/metaengine.cpp:258 engines/tsage/metaengine.cpp:190
 #: engines/tucker/metaengine.cpp:221 engines/twine/metaengine.cpp:388
@@ -7438,7 +7438,7 @@ msgstr "?????????? ???????????? ??????????"
 #: engines/freescape/games/eclipse/eclipse.cpp:330
 #: engines/hopkins/metaengine.cpp:236 engines/hugo/metaengine.cpp:165
 #: engines/lure/metaengine.cpp:191 engines/made/metaengine.cpp:192
-#: engines/mads/metaengine.cpp:343 engines/mohawk/myst.cpp:583
+#: engines/mads/metaengine.cpp:347 engines/mohawk/myst.cpp:583
 #: engines/mohawk/riven.cpp:864 engines/pegasus/pegasus.cpp:2825
 #: engines/petka/metaengine.cpp:198 engines/prince/metaengine.cpp:243
 #: engines/saga/metaengine.cpp:442 engines/sherlock/metaengine.cpp:462
@@ -7520,7 +7520,7 @@ msgstr "?????? ???????????? ???? ??????????"
 #: engines/freescape/games/dark/dark.cpp:236
 #: engines/freescape/games/driller/driller.cpp:129
 #: engines/freescape/games/eclipse/eclipse.cpp:340
-#: engines/mads/metaengine.cpp:399 engines/phoenixvr/metaengine.cpp:80
+#: engines/mads/metaengine.cpp:403 engines/phoenixvr/metaengine.cpp:80
 #: engines/saga/metaengine.cpp:437 engines/sherlock/metaengine.cpp:359
 #: engines/sherlock/metaengine.cpp:618 engines/sherlock/metaengine.cpp:852
 #: engines/stark/metaengine.cpp:280 engines/sword1/metaengine.cpp:295
@@ -10283,7 +10283,7 @@ msgid "Repeat last line"
 msgstr "?????????? ???????????? ?????????????? ????????"
 
 #: engines/hugo/metaengine.cpp:172 engines/lure/metaengine.cpp:197
-#: engines/mads/metaengine.cpp:349 engines/tsage/metaengine.cpp:247
+#: engines/mads/metaengine.cpp:353 engines/tsage/metaengine.cpp:247
 #, fuzzy
 #| msgid "Restore game:"
 msgid "Restore game"
@@ -10510,7 +10510,7 @@ msgstr "??~??~???????????? ????????"
 msgid "Direction keymappings"
 msgstr "??~??~???????????? ????????"
 
-#: engines/hypno/metaengine.cpp:316 engines/mads/metaengine.cpp:387
+#: engines/hypno/metaengine.cpp:316 engines/mads/metaengine.cpp:391
 #: engines/tsage/metaengine.cpp:259
 msgid "Show credits"
 msgstr "???????? ?????????????? ????????"
@@ -11248,55 +11248,55 @@ msgstr "???????? ???????? ??????????????"
 msgid "Naughty game mode"
 msgstr "?????? ???????? ????????"
 
-#: engines/mads/metaengine.cpp:314
+#: engines/mads/metaengine.cpp:318
 #, fuzzy
 #| msgid "Game Options"
 msgid "Start menu keymappings"
 msgstr "??~??~???????????? ????????"
 
-#: engines/mads/metaengine.cpp:337
+#: engines/mads/metaengine.cpp:341
 #, fuzzy
 #| msgid "Open main menu"
 msgid "Open game menu"
 msgstr "?????????? ?????????? ????????"
 
-#: engines/mads/metaengine.cpp:355
+#: engines/mads/metaengine.cpp:359
 #, fuzzy
 #| msgid "Scroll up"
 msgid "Scroll bar up"
 msgstr "?????????? ????????"
 
-#: engines/mads/metaengine.cpp:361
+#: engines/mads/metaengine.cpp:365
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Scroll bar down"
 msgstr "?????????? ??????"
 
-#: engines/mads/metaengine.cpp:369
+#: engines/mads/metaengine.cpp:373
 #, fuzzy
 #| msgid "Restore game:"
 msgid "Start game"
 msgstr "?????????? ????????:"
 
-#: engines/mads/metaengine.cpp:375
+#: engines/mads/metaengine.cpp:379
 #, fuzzy
 #| msgid "Restore game:"
 msgid "Resume game"
 msgstr "?????????? ????????:"
 
-#: engines/mads/metaengine.cpp:381
+#: engines/mads/metaengine.cpp:385
 #, fuzzy
 #| msgid "Show hints"
 msgid "Show intro"
 msgstr "???????? ??????????"
 
-#: engines/mads/metaengine.cpp:393
+#: engines/mads/metaengine.cpp:397
 #, fuzzy
 #| msgid "Speech Options"
 msgid "Show quotes"
 msgstr "???????????????? ??????????"
 
-#: engines/mads/metaengine.cpp:406
+#: engines/mads/metaengine.cpp:410
 #, fuzzy
 #| msgid "Faster animations"
 msgid "Restart animation"
diff --git a/po/hi.po b/po/hi.po
index 5c4fc384911..8640d8b2935 100644
--- a/po/hi.po
+++ b/po/hi.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-07 22:09+0000\n"
+"POT-Creation-Date: 2026-06-08 11:04+0000\n"
 "PO-Revision-Date: 2026-05-12 22:22+0000\n"
 "Last-Translator: KARTIK SAXENA <kartiksaxena2005 at gmail.com>\n"
 "Language-Team: Hindi <https://translations.scummvm.org/projects/scummvm/"
@@ -3750,7 +3750,7 @@ msgstr "????????? ??????????????????(O)..."
 #: engines/hugo/metaengine.cpp:134 engines/illusions/metaengine.cpp:94
 #: engines/kyra/engine/eobcommon.cpp:369 engines/kyra/engine/lol.cpp:505
 #: engines/lure/metaengine.cpp:177 engines/made/metaengine.cpp:137
-#: engines/mads/metaengine.cpp:318 engines/mm/metaengine.cpp:186
+#: engines/mads/metaengine.cpp:322 engines/mm/metaengine.cpp:186
 #: engines/mtropolis/metaengine.cpp:161 engines/nancy/input.cpp:181
 #: engines/parallaction/metaengine.cpp:138 engines/saga/metaengine.cpp:278
 #: engines/sludge/keymapper_tables.h:561 engines/sludge/keymapper_tables.h:580
@@ -3787,7 +3787,7 @@ msgstr "?????????????????? ???????????????"
 #: engines/hopkins/metaengine.cpp:217 engines/hugo/metaengine.cpp:141
 #: engines/illusions/metaengine.cpp:101 engines/kyra/engine/eobcommon.cpp:370
 #: engines/kyra/engine/lol.cpp:506 engines/lure/metaengine.cpp:183
-#: engines/made/metaengine.cpp:144 engines/mads/metaengine.cpp:324
+#: engines/made/metaengine.cpp:144 engines/mads/metaengine.cpp:328
 #: engines/mm/metaengine.cpp:192 engines/mtropolis/metaengine.cpp:167
 #: engines/nancy/input.cpp:188 engines/parallaction/metaengine.cpp:144
 #: engines/saga/metaengine.cpp:289 engines/sludge/keymapper_tables.h:567
@@ -5838,7 +5838,7 @@ msgstr ""
 #: engines/hugo/metaengine.cpp:129 engines/hypno/metaengine.cpp:128
 #: engines/illusions/metaengine.cpp:89 engines/lab/metaengine.cpp:149
 #: engines/lure/metaengine.cpp:168 engines/made/metaengine.cpp:132
-#: engines/mads/metaengine.cpp:312 engines/neverhood/metaengine.cpp:185
+#: engines/mads/metaengine.cpp:316 engines/neverhood/metaengine.cpp:185
 #: engines/ngi/metaengine.cpp:115 engines/petka/metaengine.cpp:113
 #: engines/prince/metaengine.cpp:227 engines/private/metaengine.cpp:127
 #: engines/queen/metaengine.cpp:146 engines/sherlock/metaengine.cpp:313
@@ -6385,7 +6385,7 @@ msgstr ""
 #: engines/hypno/metaengine.cpp:267 engines/hypno/metaengine.cpp:311
 #: engines/illusions/metaengine.cpp:90 engines/lab/metaengine.cpp:152
 #: engines/lure/metaengine.cpp:169 engines/made/metaengine.cpp:133
-#: engines/mads/metaengine.cpp:313 engines/neverhood/metaengine.cpp:186
+#: engines/mads/metaengine.cpp:317 engines/neverhood/metaengine.cpp:186
 #: engines/queen/metaengine.cpp:147 engines/saga/metaengine.cpp:268
 #: engines/sludge/keymapper_tables.h:33 engines/stark/metaengine.cpp:185
 #: engines/sword1/metaengine.cpp:267 engines/tinsel/metaengine.cpp:241
@@ -6865,7 +6865,7 @@ msgstr ""
 #: engines/bbvs/metaengine.cpp:111 engines/cruise/metaengine.cpp:223
 #: engines/freescape/movement.cpp:107 engines/got/metaengine.cpp:78
 #: engines/hugo/metaengine.cpp:197 engines/lure/metaengine.cpp:209
-#: engines/mads/metaengine.cpp:330 engines/mm/mm1/metaengine.cpp:42
+#: engines/mads/metaengine.cpp:334 engines/mm/mm1/metaengine.cpp:42
 #: engines/mm/mm1/metaengine.cpp:48 engines/saga/metaengine.cpp:328
 #: engines/tinsel/metaengine.cpp:258 engines/tsage/metaengine.cpp:190
 #: engines/tucker/metaengine.cpp:221 engines/twine/metaengine.cpp:388
@@ -7169,7 +7169,7 @@ msgstr ""
 #: engines/freescape/games/eclipse/eclipse.cpp:330
 #: engines/hopkins/metaengine.cpp:236 engines/hugo/metaengine.cpp:165
 #: engines/lure/metaengine.cpp:191 engines/made/metaengine.cpp:192
-#: engines/mads/metaengine.cpp:343 engines/mohawk/myst.cpp:583
+#: engines/mads/metaengine.cpp:347 engines/mohawk/myst.cpp:583
 #: engines/mohawk/riven.cpp:864 engines/pegasus/pegasus.cpp:2825
 #: engines/petka/metaengine.cpp:198 engines/prince/metaengine.cpp:243
 #: engines/saga/metaengine.cpp:442 engines/sherlock/metaengine.cpp:462
@@ -7245,7 +7245,7 @@ msgstr ""
 #: engines/freescape/games/dark/dark.cpp:236
 #: engines/freescape/games/driller/driller.cpp:129
 #: engines/freescape/games/eclipse/eclipse.cpp:340
-#: engines/mads/metaengine.cpp:399 engines/phoenixvr/metaengine.cpp:80
+#: engines/mads/metaengine.cpp:403 engines/phoenixvr/metaengine.cpp:80
 #: engines/saga/metaengine.cpp:437 engines/sherlock/metaengine.cpp:359
 #: engines/sherlock/metaengine.cpp:618 engines/sherlock/metaengine.cpp:852
 #: engines/stark/metaengine.cpp:280 engines/sword1/metaengine.cpp:295
@@ -9800,7 +9800,7 @@ msgid "Repeat last line"
 msgstr ""
 
 #: engines/hugo/metaengine.cpp:172 engines/lure/metaengine.cpp:197
-#: engines/mads/metaengine.cpp:349 engines/tsage/metaengine.cpp:247
+#: engines/mads/metaengine.cpp:353 engines/tsage/metaengine.cpp:247
 #, fuzzy
 #| msgid "Enable restored content"
 msgid "Restore game"
@@ -10019,7 +10019,7 @@ msgstr "????????? ??????????????????(O)..."
 msgid "Direction keymappings"
 msgstr "????????? ??????????????????(O)..."
 
-#: engines/hypno/metaengine.cpp:316 engines/mads/metaengine.cpp:387
+#: engines/hypno/metaengine.cpp:316 engines/mads/metaengine.cpp:391
 #: engines/tsage/metaengine.cpp:259
 #, fuzzy
 msgid "Show credits"
@@ -10679,53 +10679,53 @@ msgstr ""
 msgid "Naughty game mode"
 msgstr ""
 
-#: engines/mads/metaengine.cpp:314
+#: engines/mads/metaengine.cpp:318
 #, fuzzy
 #| msgid "Game Options"
 msgid "Start menu keymappings"
 msgstr "????????? ??????????????????(O)..."
 
-#: engines/mads/metaengine.cpp:337
+#: engines/mads/metaengine.cpp:341
 #, fuzzy
 #| msgid "Game menu"
 msgid "Open game menu"
 msgstr "????????? ????????????"
 
-#: engines/mads/metaengine.cpp:355
+#: engines/mads/metaengine.cpp:359
 #, fuzzy
 #| msgid "Scroll Up"
 msgid "Scroll bar up"
 msgstr "????????? ????????????????????? ????????????"
 
-#: engines/mads/metaengine.cpp:361
+#: engines/mads/metaengine.cpp:365
 #, fuzzy
 msgid "Scroll bar down"
 msgstr "???????????? ????????????"
 
-#: engines/mads/metaengine.cpp:369
+#: engines/mads/metaengine.cpp:373
 #, fuzzy
 #| msgid "Saved games"
 msgid "Start game"
 msgstr "????????? ???????????????"
 
-#: engines/mads/metaengine.cpp:375
+#: engines/mads/metaengine.cpp:379
 #, fuzzy
 #| msgid "Enable restored content"
 msgid "Resume game"
 msgstr "Restored content ??????????????? ????????????"
 
-#: engines/mads/metaengine.cpp:381
+#: engines/mads/metaengine.cpp:385
 #, fuzzy
 msgid "Show intro"
 msgstr "???????????? ????????? files ??????????????????"
 
-#: engines/mads/metaengine.cpp:393
+#: engines/mads/metaengine.cpp:397
 #, fuzzy
 #| msgid "Speech Options"
 msgid "Show quotes"
 msgstr "???????????? ?????? ??????????????????(O)..."
 
-#: engines/mads/metaengine.cpp:406
+#: engines/mads/metaengine.cpp:410
 #, fuzzy
 #| msgid "Saved games"
 msgid "Restart animation"
diff --git a/po/hu_HU.po b/po/hu_HU.po
index 3f98b8f5f50..a2ad259f20e 100644
--- a/po/hu_HU.po
+++ b/po/hu_HU.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-07 22:09+0000\n"
+"POT-Creation-Date: 2026-06-08 11:04+0000\n"
 "PO-Revision-Date: 2025-03-07 00:18+0000\n"
 "Last-Translator: Hov??nszki Tam??s <gahex68379 at lofiey.com>\n"
 "Language-Team: Hungarian <https://translations.scummvm.org/projects/scummvm/"
@@ -3977,7 +3977,7 @@ msgstr "Alap??rtelmezett billenty??zet kioszt??s"
 #: engines/hugo/metaengine.cpp:134 engines/illusions/metaengine.cpp:94
 #: engines/kyra/engine/eobcommon.cpp:369 engines/kyra/engine/lol.cpp:505
 #: engines/lure/metaengine.cpp:177 engines/made/metaengine.cpp:137
-#: engines/mads/metaengine.cpp:318 engines/mm/metaengine.cpp:186
+#: engines/mads/metaengine.cpp:322 engines/mm/metaengine.cpp:186
 #: engines/mtropolis/metaengine.cpp:161 engines/nancy/input.cpp:181
 #: engines/parallaction/metaengine.cpp:138 engines/saga/metaengine.cpp:278
 #: engines/sludge/keymapper_tables.h:561 engines/sludge/keymapper_tables.h:580
@@ -4014,7 +4014,7 @@ msgstr "K??z??ps?? katt"
 #: engines/hopkins/metaengine.cpp:217 engines/hugo/metaengine.cpp:141
 #: engines/illusions/metaengine.cpp:101 engines/kyra/engine/eobcommon.cpp:370
 #: engines/kyra/engine/lol.cpp:506 engines/lure/metaengine.cpp:183
-#: engines/made/metaengine.cpp:144 engines/mads/metaengine.cpp:324
+#: engines/made/metaengine.cpp:144 engines/mads/metaengine.cpp:328
 #: engines/mm/metaengine.cpp:192 engines/mtropolis/metaengine.cpp:167
 #: engines/nancy/input.cpp:188 engines/parallaction/metaengine.cpp:144
 #: engines/saga/metaengine.cpp:289 engines/sludge/keymapper_tables.h:567
@@ -6551,7 +6551,7 @@ msgstr "Friss??t??sek keres??se..."
 #: engines/hugo/metaengine.cpp:129 engines/hypno/metaengine.cpp:128
 #: engines/illusions/metaengine.cpp:89 engines/lab/metaengine.cpp:149
 #: engines/lure/metaengine.cpp:168 engines/made/metaengine.cpp:132
-#: engines/mads/metaengine.cpp:312 engines/neverhood/metaengine.cpp:185
+#: engines/mads/metaengine.cpp:316 engines/neverhood/metaengine.cpp:185
 #: engines/ngi/metaengine.cpp:115 engines/petka/metaengine.cpp:113
 #: engines/prince/metaengine.cpp:227 engines/private/metaengine.cpp:127
 #: engines/queen/metaengine.cpp:146 engines/sherlock/metaengine.cpp:313
@@ -7114,7 +7114,7 @@ msgstr "F?? AGOS"
 #: engines/hypno/metaengine.cpp:267 engines/hypno/metaengine.cpp:311
 #: engines/illusions/metaengine.cpp:90 engines/lab/metaengine.cpp:152
 #: engines/lure/metaengine.cpp:169 engines/made/metaengine.cpp:133
-#: engines/mads/metaengine.cpp:313 engines/neverhood/metaengine.cpp:186
+#: engines/mads/metaengine.cpp:317 engines/neverhood/metaengine.cpp:186
 #: engines/queen/metaengine.cpp:147 engines/saga/metaengine.cpp:268
 #: engines/sludge/keymapper_tables.h:33 engines/stark/metaengine.cpp:185
 #: engines/sword1/metaengine.cpp:267 engines/tinsel/metaengine.cpp:241
@@ -7637,7 +7637,7 @@ msgstr "Soldier 714 Szem Csal??s"
 #: engines/bbvs/metaengine.cpp:111 engines/cruise/metaengine.cpp:223
 #: engines/freescape/movement.cpp:107 engines/got/metaengine.cpp:78
 #: engines/hugo/metaengine.cpp:197 engines/lure/metaengine.cpp:209
-#: engines/mads/metaengine.cpp:330 engines/mm/mm1/metaengine.cpp:42
+#: engines/mads/metaengine.cpp:334 engines/mm/mm1/metaengine.cpp:42
 #: engines/mm/mm1/metaengine.cpp:48 engines/saga/metaengine.cpp:328
 #: engines/tinsel/metaengine.cpp:258 engines/tsage/metaengine.cpp:190
 #: engines/tucker/metaengine.cpp:221 engines/twine/metaengine.cpp:388
@@ -7957,7 +7957,7 @@ msgstr "Nyom publik??l??sa"
 #: engines/freescape/games/eclipse/eclipse.cpp:330
 #: engines/hopkins/metaengine.cpp:236 engines/hugo/metaengine.cpp:165
 #: engines/lure/metaengine.cpp:191 engines/made/metaengine.cpp:192
-#: engines/mads/metaengine.cpp:343 engines/mohawk/myst.cpp:583
+#: engines/mads/metaengine.cpp:347 engines/mohawk/myst.cpp:583
 #: engines/mohawk/riven.cpp:864 engines/pegasus/pegasus.cpp:2825
 #: engines/petka/metaengine.cpp:198 engines/prince/metaengine.cpp:243
 #: engines/saga/metaengine.cpp:442 engines/sherlock/metaengine.cpp:462
@@ -8039,7 +8039,7 @@ msgstr "Nyom Adatb??zis"
 #: engines/freescape/games/dark/dark.cpp:236
 #: engines/freescape/games/driller/driller.cpp:129
 #: engines/freescape/games/eclipse/eclipse.cpp:340
-#: engines/mads/metaengine.cpp:399 engines/phoenixvr/metaengine.cpp:80
+#: engines/mads/metaengine.cpp:403 engines/phoenixvr/metaengine.cpp:80
 #: engines/saga/metaengine.cpp:437 engines/sherlock/metaengine.cpp:359
 #: engines/sherlock/metaengine.cpp:618 engines/sherlock/metaengine.cpp:852
 #: engines/stark/metaengine.cpp:280 engines/sword1/metaengine.cpp:295
@@ -10755,7 +10755,7 @@ msgid "Repeat last line"
 msgstr "Utols?? sor megism??tl??se"
 
 #: engines/hugo/metaengine.cpp:172 engines/lure/metaengine.cpp:197
-#: engines/mads/metaengine.cpp:349 engines/tsage/metaengine.cpp:247
+#: engines/mads/metaengine.cpp:353 engines/tsage/metaengine.cpp:247
 msgid "Restore game"
 msgstr "J??t??k vissza??ll??t??sa"
 
@@ -10968,7 +10968,7 @@ msgstr "J??t??k gombkioszt??sok"
 msgid "Direction keymappings"
 msgstr "Index gombkioszt??s"
 
-#: engines/hypno/metaengine.cpp:316 engines/mads/metaengine.cpp:387
+#: engines/hypno/metaengine.cpp:316 engines/mads/metaengine.cpp:391
 #: engines/tsage/metaengine.cpp:259
 msgid "Show credits"
 msgstr "St??blista megjelen??t??se"
@@ -11672,39 +11672,39 @@ msgstr "Anim??lt j??t??k fel??let"
 msgid "Naughty game mode"
 msgstr "Pajkos j??t??km??d"
 
-#: engines/mads/metaengine.cpp:314
+#: engines/mads/metaengine.cpp:318
 msgid "Start menu keymappings"
 msgstr "Start men?? gombkioszt??sok"
 
-#: engines/mads/metaengine.cpp:337
+#: engines/mads/metaengine.cpp:341
 msgid "Open game menu"
 msgstr "J??t??k men?? megnyit??sa"
 
-#: engines/mads/metaengine.cpp:355
+#: engines/mads/metaengine.cpp:359
 msgid "Scroll bar up"
 msgstr "Szkroll s??v fel"
 
-#: engines/mads/metaengine.cpp:361
+#: engines/mads/metaengine.cpp:365
 msgid "Scroll bar down"
 msgstr "Szkroll s??v le"
 
-#: engines/mads/metaengine.cpp:369
+#: engines/mads/metaengine.cpp:373
 msgid "Start game"
 msgstr "J??t??k ind??t??sa"
 
-#: engines/mads/metaengine.cpp:375
+#: engines/mads/metaengine.cpp:379
 msgid "Resume game"
 msgstr "J??t??k folytat??sa"
 
-#: engines/mads/metaengine.cpp:381
+#: engines/mads/metaengine.cpp:385
 msgid "Show intro"
 msgstr "Bevezet?? megjelen??t??se"
 
-#: engines/mads/metaengine.cpp:393
+#: engines/mads/metaengine.cpp:397
 msgid "Show quotes"
 msgstr "Id??zetek megjelen??t??se"
 
-#: engines/mads/metaengine.cpp:406
+#: engines/mads/metaengine.cpp:410
 msgid "Restart animation"
 msgstr "Anim??ci?? ??jraind??t??sa"
 
diff --git a/po/it_IT.po b/po/it_IT.po
index 7a39be51202..e430ed6b6a5 100644
--- a/po/it_IT.po
+++ b/po/it_IT.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-07 22:09+0000\n"
+"POT-Creation-Date: 2026-06-08 11:04+0000\n"
 "PO-Revision-Date: 2026-06-04 19:00+0000\n"
 "Last-Translator: Walter Agazzi <walter.agazzi at protonmail.com>\n"
 "Language-Team: Italian <https://translations.scummvm.org/projects/scummvm/"
@@ -3937,7 +3937,7 @@ msgstr "Mappa tasti predefinita"
 #: engines/hugo/metaengine.cpp:134 engines/illusions/metaengine.cpp:94
 #: engines/kyra/engine/eobcommon.cpp:369 engines/kyra/engine/lol.cpp:505
 #: engines/lure/metaengine.cpp:177 engines/made/metaengine.cpp:137
-#: engines/mads/metaengine.cpp:318 engines/mm/metaengine.cpp:186
+#: engines/mads/metaengine.cpp:322 engines/mm/metaengine.cpp:186
 #: engines/mtropolis/metaengine.cpp:161 engines/nancy/input.cpp:181
 #: engines/parallaction/metaengine.cpp:138 engines/saga/metaengine.cpp:278
 #: engines/sludge/keymapper_tables.h:561 engines/sludge/keymapper_tables.h:580
@@ -3970,7 +3970,7 @@ msgstr "Clic centrale"
 #: engines/hopkins/metaengine.cpp:217 engines/hugo/metaengine.cpp:141
 #: engines/illusions/metaengine.cpp:101 engines/kyra/engine/eobcommon.cpp:370
 #: engines/kyra/engine/lol.cpp:506 engines/lure/metaengine.cpp:183
-#: engines/made/metaengine.cpp:144 engines/mads/metaengine.cpp:324
+#: engines/made/metaengine.cpp:144 engines/mads/metaengine.cpp:328
 #: engines/mm/metaengine.cpp:192 engines/mtropolis/metaengine.cpp:167
 #: engines/nancy/input.cpp:188 engines/parallaction/metaengine.cpp:144
 #: engines/saga/metaengine.cpp:289 engines/sludge/keymapper_tables.h:567
@@ -6348,7 +6348,7 @@ msgstr "Cerca aggiornamenti..."
 #: engines/hugo/metaengine.cpp:129 engines/hypno/metaengine.cpp:128
 #: engines/illusions/metaengine.cpp:89 engines/lab/metaengine.cpp:149
 #: engines/lure/metaengine.cpp:168 engines/made/metaengine.cpp:132
-#: engines/mads/metaengine.cpp:312 engines/neverhood/metaengine.cpp:185
+#: engines/mads/metaengine.cpp:316 engines/neverhood/metaengine.cpp:185
 #: engines/ngi/metaengine.cpp:115 engines/petka/metaengine.cpp:113
 #: engines/prince/metaengine.cpp:227 engines/private/metaengine.cpp:127
 #: engines/queen/metaengine.cpp:146 engines/sherlock/metaengine.cpp:313
@@ -6916,7 +6916,7 @@ msgstr "AGOS - Principali"
 #: engines/hypno/metaengine.cpp:267 engines/hypno/metaengine.cpp:311
 #: engines/illusions/metaengine.cpp:90 engines/lab/metaengine.cpp:152
 #: engines/lure/metaengine.cpp:169 engines/made/metaengine.cpp:133
-#: engines/mads/metaengine.cpp:313 engines/neverhood/metaengine.cpp:186
+#: engines/mads/metaengine.cpp:317 engines/neverhood/metaengine.cpp:186
 #: engines/queen/metaengine.cpp:147 engines/saga/metaengine.cpp:268
 #: engines/sludge/keymapper_tables.h:33 engines/stark/metaengine.cpp:185
 #: engines/sword1/metaengine.cpp:267 engines/tinsel/metaengine.cpp:241
@@ -7422,7 +7422,7 @@ msgstr "Cheat 'Occhio del soldato 714'"
 #: engines/bbvs/metaengine.cpp:111 engines/cruise/metaengine.cpp:223
 #: engines/freescape/movement.cpp:107 engines/got/metaengine.cpp:78
 #: engines/hugo/metaengine.cpp:197 engines/lure/metaengine.cpp:209
-#: engines/mads/metaengine.cpp:330 engines/mm/mm1/metaengine.cpp:42
+#: engines/mads/metaengine.cpp:334 engines/mm/mm1/metaengine.cpp:42
 #: engines/mm/mm1/metaengine.cpp:48 engines/saga/metaengine.cpp:328
 #: engines/tinsel/metaengine.cpp:258 engines/tsage/metaengine.cpp:190
 #: engines/tucker/metaengine.cpp:221 engines/twine/metaengine.cpp:388
@@ -7728,7 +7728,7 @@ msgstr "Imposta indizio privato/pubblico"
 #: engines/freescape/games/eclipse/eclipse.cpp:330
 #: engines/hopkins/metaengine.cpp:236 engines/hugo/metaengine.cpp:165
 #: engines/lure/metaengine.cpp:191 engines/made/metaengine.cpp:192
-#: engines/mads/metaengine.cpp:343 engines/mohawk/myst.cpp:583
+#: engines/mads/metaengine.cpp:347 engines/mohawk/myst.cpp:583
 #: engines/mohawk/riven.cpp:864 engines/pegasus/pegasus.cpp:2825
 #: engines/petka/metaengine.cpp:198 engines/prince/metaengine.cpp:243
 #: engines/saga/metaengine.cpp:442 engines/sherlock/metaengine.cpp:462
@@ -7804,7 +7804,7 @@ msgstr "Database indizi"
 #: engines/freescape/games/dark/dark.cpp:236
 #: engines/freescape/games/driller/driller.cpp:129
 #: engines/freescape/games/eclipse/eclipse.cpp:340
-#: engines/mads/metaengine.cpp:399 engines/phoenixvr/metaengine.cpp:80
+#: engines/mads/metaengine.cpp:403 engines/phoenixvr/metaengine.cpp:80
 #: engines/saga/metaengine.cpp:437 engines/sherlock/metaengine.cpp:359
 #: engines/sherlock/metaengine.cpp:618 engines/sherlock/metaengine.cpp:852
 #: engines/stark/metaengine.cpp:280 engines/sword1/metaengine.cpp:295
@@ -10175,7 +10175,7 @@ msgid "Repeat last line"
 msgstr "Ripeti l'ultima linea di testo"
 
 #: engines/hugo/metaengine.cpp:172 engines/lure/metaengine.cpp:197
-#: engines/mads/metaengine.cpp:349 engines/tsage/metaengine.cpp:247
+#: engines/mads/metaengine.cpp:353 engines/tsage/metaengine.cpp:247
 msgid "Restore game"
 msgstr "Ripristina gioco"
 
@@ -10366,7 +10366,7 @@ msgstr "Mappatura tasti pausa"
 msgid "Direction keymappings"
 msgstr "Mappatura tasti di direzione"
 
-#: engines/hypno/metaengine.cpp:316 engines/mads/metaengine.cpp:387
+#: engines/hypno/metaengine.cpp:316 engines/mads/metaengine.cpp:391
 #: engines/tsage/metaengine.cpp:259
 msgid "Show credits"
 msgstr "Mostra riconoscimenti"
@@ -11021,39 +11021,39 @@ msgstr "Animazioni interfaccia di gioco"
 msgid "Naughty game mode"
 msgstr "Modalit?? os??"
 
-#: engines/mads/metaengine.cpp:314
+#: engines/mads/metaengine.cpp:318
 msgid "Start menu keymappings"
 msgstr "Mappature men?? principale"
 
-#: engines/mads/metaengine.cpp:337
+#: engines/mads/metaengine.cpp:341
 msgid "Open game menu"
 msgstr "Apri men?? del gioco"
 
-#: engines/mads/metaengine.cpp:355
+#: engines/mads/metaengine.cpp:359
 msgid "Scroll bar up"
 msgstr "Barra scorrimento su"
 
-#: engines/mads/metaengine.cpp:361
+#: engines/mads/metaengine.cpp:365
 msgid "Scroll bar down"
 msgstr "Barra scorrimento gi??"
 
-#: engines/mads/metaengine.cpp:369
+#: engines/mads/metaengine.cpp:373
 msgid "Start game"
 msgstr "Inizia partita"
 
-#: engines/mads/metaengine.cpp:375
+#: engines/mads/metaengine.cpp:379
 msgid "Resume game"
 msgstr "Ripristina partita"
 
-#: engines/mads/metaengine.cpp:381
+#: engines/mads/metaengine.cpp:385
 msgid "Show intro"
 msgstr "Mostra introduzione"
 
-#: engines/mads/metaengine.cpp:393
+#: engines/mads/metaengine.cpp:397
 msgid "Show quotes"
 msgstr "Mostra citazioni"
 
-#: engines/mads/metaengine.cpp:406
+#: engines/mads/metaengine.cpp:410
 msgid "Restart animation"
 msgstr "Ripeti animazione"
 
diff --git a/po/ja.po b/po/ja.po
index 4532ea2d1f4..dbcd14cb513 100644
--- a/po/ja.po
+++ b/po/ja.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-07 22:09+0000\n"
+"POT-Creation-Date: 2026-06-08 11:04+0000\n"
 "PO-Revision-Date: 2026-06-04 07:22+0000\n"
 "Last-Translator: VAN-Gluon <van.gluonp at gmail.com>\n"
 "Language-Team: Japanese <https://translations.scummvm.org/projects/scummvm/"
@@ -3795,7 +3795,7 @@ msgstr "?????????????????????(???????????????)"
 #: engines/hugo/metaengine.cpp:134 engines/illusions/metaengine.cpp:94
 #: engines/kyra/engine/eobcommon.cpp:369 engines/kyra/engine/lol.cpp:505
 #: engines/lure/metaengine.cpp:177 engines/made/metaengine.cpp:137
-#: engines/mads/metaengine.cpp:318 engines/mm/metaengine.cpp:186
+#: engines/mads/metaengine.cpp:322 engines/mm/metaengine.cpp:186
 #: engines/mtropolis/metaengine.cpp:161 engines/nancy/input.cpp:181
 #: engines/parallaction/metaengine.cpp:138 engines/saga/metaengine.cpp:278
 #: engines/sludge/keymapper_tables.h:561 engines/sludge/keymapper_tables.h:580
@@ -3828,7 +3828,7 @@ msgstr "???????????????"
 #: engines/hopkins/metaengine.cpp:217 engines/hugo/metaengine.cpp:141
 #: engines/illusions/metaengine.cpp:101 engines/kyra/engine/eobcommon.cpp:370
 #: engines/kyra/engine/lol.cpp:506 engines/lure/metaengine.cpp:183
-#: engines/made/metaengine.cpp:144 engines/mads/metaengine.cpp:324
+#: engines/made/metaengine.cpp:144 engines/mads/metaengine.cpp:328
 #: engines/mm/metaengine.cpp:192 engines/mtropolis/metaengine.cpp:167
 #: engines/nancy/input.cpp:188 engines/parallaction/metaengine.cpp:144
 #: engines/saga/metaengine.cpp:289 engines/sludge/keymapper_tables.h:567
@@ -6202,7 +6202,7 @@ msgstr "??????????????????????????????..."
 #: engines/hugo/metaengine.cpp:129 engines/hypno/metaengine.cpp:128
 #: engines/illusions/metaengine.cpp:89 engines/lab/metaengine.cpp:149
 #: engines/lure/metaengine.cpp:168 engines/made/metaengine.cpp:132
-#: engines/mads/metaengine.cpp:312 engines/neverhood/metaengine.cpp:185
+#: engines/mads/metaengine.cpp:316 engines/neverhood/metaengine.cpp:185
 #: engines/ngi/metaengine.cpp:115 engines/petka/metaengine.cpp:113
 #: engines/prince/metaengine.cpp:227 engines/private/metaengine.cpp:127
 #: engines/queen/metaengine.cpp:146 engines/sherlock/metaengine.cpp:313
@@ -6757,7 +6757,7 @@ msgstr "AGOS?????????"
 #: engines/hypno/metaengine.cpp:267 engines/hypno/metaengine.cpp:311
 #: engines/illusions/metaengine.cpp:90 engines/lab/metaengine.cpp:152
 #: engines/lure/metaengine.cpp:169 engines/made/metaengine.cpp:133
-#: engines/mads/metaengine.cpp:313 engines/neverhood/metaengine.cpp:186
+#: engines/mads/metaengine.cpp:317 engines/neverhood/metaengine.cpp:186
 #: engines/queen/metaengine.cpp:147 engines/saga/metaengine.cpp:268
 #: engines/sludge/keymapper_tables.h:33 engines/stark/metaengine.cpp:185
 #: engines/sword1/metaengine.cpp:267 engines/tinsel/metaengine.cpp:241
@@ -7254,7 +7254,7 @@ msgstr "??????714???????????????"
 #: engines/bbvs/metaengine.cpp:111 engines/cruise/metaengine.cpp:223
 #: engines/freescape/movement.cpp:107 engines/got/metaengine.cpp:78
 #: engines/hugo/metaengine.cpp:197 engines/lure/metaengine.cpp:209
-#: engines/mads/metaengine.cpp:330 engines/mm/mm1/metaengine.cpp:42
+#: engines/mads/metaengine.cpp:334 engines/mm/mm1/metaengine.cpp:42
 #: engines/mm/mm1/metaengine.cpp:48 engines/saga/metaengine.cpp:328
 #: engines/tinsel/metaengine.cpp:258 engines/tsage/metaengine.cpp:190
 #: engines/tucker/metaengine.cpp:221 engines/twine/metaengine.cpp:388
@@ -7561,7 +7561,7 @@ msgstr "???????????????????????????"
 #: engines/freescape/games/eclipse/eclipse.cpp:330
 #: engines/hopkins/metaengine.cpp:236 engines/hugo/metaengine.cpp:165
 #: engines/lure/metaengine.cpp:191 engines/made/metaengine.cpp:192
-#: engines/mads/metaengine.cpp:343 engines/mohawk/myst.cpp:583
+#: engines/mads/metaengine.cpp:347 engines/mohawk/myst.cpp:583
 #: engines/mohawk/riven.cpp:864 engines/pegasus/pegasus.cpp:2825
 #: engines/petka/metaengine.cpp:198 engines/prince/metaengine.cpp:243
 #: engines/saga/metaengine.cpp:442 engines/sherlock/metaengine.cpp:462
@@ -7643,7 +7643,7 @@ msgstr "??????????????????????????????"
 #: engines/freescape/games/dark/dark.cpp:236
 #: engines/freescape/games/driller/driller.cpp:129
 #: engines/freescape/games/eclipse/eclipse.cpp:340
-#: engines/mads/metaengine.cpp:399 engines/phoenixvr/metaengine.cpp:80
+#: engines/mads/metaengine.cpp:403 engines/phoenixvr/metaengine.cpp:80
 #: engines/saga/metaengine.cpp:437 engines/sherlock/metaengine.cpp:359
 #: engines/sherlock/metaengine.cpp:618 engines/sherlock/metaengine.cpp:852
 #: engines/stark/metaengine.cpp:280 engines/sword1/metaengine.cpp:295
@@ -10354,7 +10354,7 @@ msgid "Repeat last line"
 msgstr "????????????????????????????????????????????????"
 
 #: engines/hugo/metaengine.cpp:172 engines/lure/metaengine.cpp:197
-#: engines/mads/metaengine.cpp:349 engines/tsage/metaengine.cpp:247
+#: engines/mads/metaengine.cpp:353 engines/tsage/metaengine.cpp:247
 #, fuzzy
 #| msgid "Restore game:"
 msgid "Restore game"
@@ -10583,7 +10583,7 @@ msgstr "???????????????????????????"
 msgid "Direction keymappings"
 msgstr "???????????????????????????"
 
-#: engines/hypno/metaengine.cpp:316 engines/mads/metaengine.cpp:387
+#: engines/hypno/metaengine.cpp:316 engines/mads/metaengine.cpp:391
 #: engines/tsage/metaengine.cpp:259
 msgid "Show credits"
 msgstr "????????????????????????"
@@ -11310,55 +11310,55 @@ msgstr "?????????????????????????????????????????????????????????"
 msgid "Naughty game mode"
 msgstr "??????????????????????????????"
 
-#: engines/mads/metaengine.cpp:314
+#: engines/mads/metaengine.cpp:318
 #, fuzzy
 #| msgid "Game keymappings"
 msgid "Start menu keymappings"
 msgstr "???????????????????????????"
 
-#: engines/mads/metaengine.cpp:337
+#: engines/mads/metaengine.cpp:341
 #, fuzzy
 #| msgid "Open main menu"
 msgid "Open game menu"
 msgstr "??????????????????????????????"
 
-#: engines/mads/metaengine.cpp:355
+#: engines/mads/metaengine.cpp:359
 #, fuzzy
 #| msgid "Scroll up"
 msgid "Scroll bar up"
 msgstr "?????????????????????"
 
-#: engines/mads/metaengine.cpp:361
+#: engines/mads/metaengine.cpp:365
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Scroll bar down"
 msgstr "?????????????????????"
 
-#: engines/mads/metaengine.cpp:369
+#: engines/mads/metaengine.cpp:373
 #, fuzzy
 #| msgid "Restore game:"
 msgid "Start game"
 msgstr "????????????????????????:"
 
-#: engines/mads/metaengine.cpp:375
+#: engines/mads/metaengine.cpp:379
 #, fuzzy
 #| msgid "Restore game:"
 msgid "Resume game"
 msgstr "????????????????????????:"
 
-#: engines/mads/metaengine.cpp:381
+#: engines/mads/metaengine.cpp:385
 #, fuzzy
 #| msgid "Show hints"
 msgid "Show intro"
 msgstr "??????????????????"
 
-#: engines/mads/metaengine.cpp:393
+#: engines/mads/metaengine.cpp:397
 #, fuzzy
 #| msgid "Speech Options"
 msgid "Show quotes"
 msgstr "???????????????????????????"
 
-#: engines/mads/metaengine.cpp:406
+#: engines/mads/metaengine.cpp:410
 #, fuzzy
 #| msgid "Faster animations"
 msgid "Restart animation"
diff --git a/po/ka.po b/po/ka.po
index 72a27ce86f6..c629cc27a72 100644
--- a/po/ka.po
+++ b/po/ka.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-07 22:09+0000\n"
+"POT-Creation-Date: 2026-06-08 11:04+0000\n"
 "PO-Revision-Date: 2026-06-05 10:22+0000\n"
 "Last-Translator: NorwayFun <temuri.doghonadze at gmail.com>\n"
 "Language-Team: Georgian <https://translations.scummvm.org/projects/scummvm/"
@@ -3620,7 +3620,7 @@ msgstr "???????????????????????????????????? ??????????????????????????? ??????
 #: engines/hugo/metaengine.cpp:134 engines/illusions/metaengine.cpp:94
 #: engines/kyra/engine/eobcommon.cpp:369 engines/kyra/engine/lol.cpp:505
 #: engines/lure/metaengine.cpp:177 engines/made/metaengine.cpp:137
-#: engines/mads/metaengine.cpp:318 engines/mm/metaengine.cpp:186
+#: engines/mads/metaengine.cpp:322 engines/mm/metaengine.cpp:186
 #: engines/mtropolis/metaengine.cpp:161 engines/nancy/input.cpp:181
 #: engines/parallaction/metaengine.cpp:138 engines/saga/metaengine.cpp:278
 #: engines/sludge/keymapper_tables.h:561 engines/sludge/keymapper_tables.h:580
@@ -3653,7 +3653,7 @@ msgstr "????????? ???????????????"
 #: engines/hopkins/metaengine.cpp:217 engines/hugo/metaengine.cpp:141
 #: engines/illusions/metaengine.cpp:101 engines/kyra/engine/eobcommon.cpp:370
 #: engines/kyra/engine/lol.cpp:506 engines/lure/metaengine.cpp:183
-#: engines/made/metaengine.cpp:144 engines/mads/metaengine.cpp:324
+#: engines/made/metaengine.cpp:144 engines/mads/metaengine.cpp:328
 #: engines/mm/metaengine.cpp:192 engines/mtropolis/metaengine.cpp:167
 #: engines/nancy/input.cpp:188 engines/parallaction/metaengine.cpp:144
 #: engines/saga/metaengine.cpp:289 engines/sludge/keymapper_tables.h:567
@@ -5663,7 +5663,7 @@ msgstr "???????????????????????????????????? ???????????????????????????..."
 #: engines/hugo/metaengine.cpp:129 engines/hypno/metaengine.cpp:128
 #: engines/illusions/metaengine.cpp:89 engines/lab/metaengine.cpp:149
 #: engines/lure/metaengine.cpp:168 engines/made/metaengine.cpp:132
-#: engines/mads/metaengine.cpp:312 engines/neverhood/metaengine.cpp:185
+#: engines/mads/metaengine.cpp:316 engines/neverhood/metaengine.cpp:185
 #: engines/ngi/metaengine.cpp:115 engines/petka/metaengine.cpp:113
 #: engines/prince/metaengine.cpp:227 engines/private/metaengine.cpp:127
 #: engines/queen/metaengine.cpp:146 engines/sherlock/metaengine.cpp:313
@@ -6191,7 +6191,7 @@ msgstr "????????????????????? AGOS"
 #: engines/hypno/metaengine.cpp:267 engines/hypno/metaengine.cpp:311
 #: engines/illusions/metaengine.cpp:90 engines/lab/metaengine.cpp:152
 #: engines/lure/metaengine.cpp:169 engines/made/metaengine.cpp:133
-#: engines/mads/metaengine.cpp:313 engines/neverhood/metaengine.cpp:186
+#: engines/mads/metaengine.cpp:317 engines/neverhood/metaengine.cpp:186
 #: engines/queen/metaengine.cpp:147 engines/saga/metaengine.cpp:268
 #: engines/sludge/keymapper_tables.h:33 engines/stark/metaengine.cpp:185
 #: engines/sword1/metaengine.cpp:267 engines/tinsel/metaengine.cpp:241
@@ -6646,7 +6646,7 @@ msgstr ""
 #: engines/bbvs/metaengine.cpp:111 engines/cruise/metaengine.cpp:223
 #: engines/freescape/movement.cpp:107 engines/got/metaengine.cpp:78
 #: engines/hugo/metaengine.cpp:197 engines/lure/metaengine.cpp:209
-#: engines/mads/metaengine.cpp:330 engines/mm/mm1/metaengine.cpp:42
+#: engines/mads/metaengine.cpp:334 engines/mm/mm1/metaengine.cpp:42
 #: engines/mm/mm1/metaengine.cpp:48 engines/saga/metaengine.cpp:328
 #: engines/tinsel/metaengine.cpp:258 engines/tsage/metaengine.cpp:190
 #: engines/tucker/metaengine.cpp:221 engines/twine/metaengine.cpp:388
@@ -6933,7 +6933,7 @@ msgstr "???????????????????????? ?????????????????????????????????????????????
 #: engines/freescape/games/eclipse/eclipse.cpp:330
 #: engines/hopkins/metaengine.cpp:236 engines/hugo/metaengine.cpp:165
 #: engines/lure/metaengine.cpp:191 engines/made/metaengine.cpp:192
-#: engines/mads/metaengine.cpp:343 engines/mohawk/myst.cpp:583
+#: engines/mads/metaengine.cpp:347 engines/mohawk/myst.cpp:583
 #: engines/mohawk/riven.cpp:864 engines/pegasus/pegasus.cpp:2825
 #: engines/petka/metaengine.cpp:198 engines/prince/metaengine.cpp:243
 #: engines/saga/metaengine.cpp:442 engines/sherlock/metaengine.cpp:462
@@ -7009,7 +7009,7 @@ msgstr "?????????????????????????????? ????????????"
 #: engines/freescape/games/dark/dark.cpp:236
 #: engines/freescape/games/driller/driller.cpp:129
 #: engines/freescape/games/eclipse/eclipse.cpp:340
-#: engines/mads/metaengine.cpp:399 engines/phoenixvr/metaengine.cpp:80
+#: engines/mads/metaengine.cpp:403 engines/phoenixvr/metaengine.cpp:80
 #: engines/saga/metaengine.cpp:437 engines/sherlock/metaengine.cpp:359
 #: engines/sherlock/metaengine.cpp:618 engines/sherlock/metaengine.cpp:852
 #: engines/stark/metaengine.cpp:280 engines/sword1/metaengine.cpp:295
@@ -9273,7 +9273,7 @@ msgid "Repeat last line"
 msgstr "???????????? ??????????????? ???????????????????????????"
 
 #: engines/hugo/metaengine.cpp:172 engines/lure/metaengine.cpp:197
-#: engines/mads/metaengine.cpp:349 engines/tsage/metaengine.cpp:247
+#: engines/mads/metaengine.cpp:353 engines/tsage/metaengine.cpp:247
 msgid "Restore game"
 msgstr "????????????????????? ?????????????????????"
 
@@ -9462,7 +9462,7 @@ msgstr "????????????????????????????????? ????????????????????????????????????
 msgid "Direction keymappings"
 msgstr "???????????????????????????????????? ????????????????????????????????? ????????????????????????????????????"
 
-#: engines/hypno/metaengine.cpp:316 engines/mads/metaengine.cpp:387
+#: engines/hypno/metaengine.cpp:316 engines/mads/metaengine.cpp:391
 #: engines/tsage/metaengine.cpp:259
 msgid "Show credits"
 msgstr "?????????????????????????????? ?????????????????????"
@@ -10048,39 +10048,39 @@ msgstr "????????????????????????????????? ????????????????????? ???????????????
 msgid "Naughty game mode"
 msgstr "??????????????????????????? ????????????????????? ??????????????????"
 
-#: engines/mads/metaengine.cpp:314
+#: engines/mads/metaengine.cpp:318
 msgid "Start menu keymappings"
 msgstr "Start ?????????????????? ????????????????????????????????? ????????????????????????????????????"
 
-#: engines/mads/metaengine.cpp:337
+#: engines/mads/metaengine.cpp:341
 msgid "Open game menu"
 msgstr "????????????????????? ?????????????????? ??????????????????"
 
-#: engines/mads/metaengine.cpp:355
+#: engines/mads/metaengine.cpp:359
 msgid "Scroll bar up"
 msgstr "?????????????????? ??????????????? ???????????????"
 
-#: engines/mads/metaengine.cpp:361
+#: engines/mads/metaengine.cpp:365
 msgid "Scroll bar down"
 msgstr "?????????????????? ??????????????? ????????????????????????"
 
-#: engines/mads/metaengine.cpp:369
+#: engines/mads/metaengine.cpp:373
 msgid "Start game"
 msgstr "????????????????????? ?????????????????????"
 
-#: engines/mads/metaengine.cpp:375
+#: engines/mads/metaengine.cpp:379
 msgid "Resume game"
 msgstr "????????????????????? ??????????????????????????????"
 
-#: engines/mads/metaengine.cpp:381
+#: engines/mads/metaengine.cpp:385
 msgid "Show intro"
 msgstr "???????????????????????? ?????????????????????"
 
-#: engines/mads/metaengine.cpp:393
+#: engines/mads/metaengine.cpp:397
 msgid "Show quotes"
 msgstr "??????????????????????????? ?????????????????????"
 
-#: engines/mads/metaengine.cpp:406
+#: engines/mads/metaengine.cpp:410
 msgid "Restart animation"
 msgstr "??????????????????????????? ???????????????????????? ?????????????????????"
 
diff --git a/po/ko.po b/po/ko.po
index a823f53cd90..38b24fdc461 100644
--- a/po/ko.po
+++ b/po/ko.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-07 22:09+0000\n"
+"POT-Creation-Date: 2026-06-08 11:04+0000\n"
 "PO-Revision-Date: 2026-05-19 20:22+0000\n"
 "Last-Translator: Hoseok Seo <ddinghoya at gmail.com>\n"
 "Language-Team: Korean <https://translations.scummvm.org/projects/scummvm/"
@@ -3849,7 +3849,7 @@ msgstr "?????? ?????? ?????????"
 #: engines/hugo/metaengine.cpp:134 engines/illusions/metaengine.cpp:94
 #: engines/kyra/engine/eobcommon.cpp:369 engines/kyra/engine/lol.cpp:505
 #: engines/lure/metaengine.cpp:177 engines/made/metaengine.cpp:137
-#: engines/mads/metaengine.cpp:318 engines/mm/metaengine.cpp:186
+#: engines/mads/metaengine.cpp:322 engines/mm/metaengine.cpp:186
 #: engines/mtropolis/metaengine.cpp:161 engines/nancy/input.cpp:181
 #: engines/parallaction/metaengine.cpp:138 engines/saga/metaengine.cpp:278
 #: engines/sludge/keymapper_tables.h:561 engines/sludge/keymapper_tables.h:580
@@ -3882,7 +3882,7 @@ msgstr "?????? ??????"
 #: engines/hopkins/metaengine.cpp:217 engines/hugo/metaengine.cpp:141
 #: engines/illusions/metaengine.cpp:101 engines/kyra/engine/eobcommon.cpp:370
 #: engines/kyra/engine/lol.cpp:506 engines/lure/metaengine.cpp:183
-#: engines/made/metaengine.cpp:144 engines/mads/metaengine.cpp:324
+#: engines/made/metaengine.cpp:144 engines/mads/metaengine.cpp:328
 #: engines/mm/metaengine.cpp:192 engines/mtropolis/metaengine.cpp:167
 #: engines/nancy/input.cpp:188 engines/parallaction/metaengine.cpp:144
 #: engines/saga/metaengine.cpp:289 engines/sludge/keymapper_tables.h:567
@@ -6213,7 +6213,7 @@ msgstr "???????????? ?????? ???..."
 #: engines/hugo/metaengine.cpp:129 engines/hypno/metaengine.cpp:128
 #: engines/illusions/metaengine.cpp:89 engines/lab/metaengine.cpp:149
 #: engines/lure/metaengine.cpp:168 engines/made/metaengine.cpp:132
-#: engines/mads/metaengine.cpp:312 engines/neverhood/metaengine.cpp:185
+#: engines/mads/metaengine.cpp:316 engines/neverhood/metaengine.cpp:185
 #: engines/ngi/metaengine.cpp:115 engines/petka/metaengine.cpp:113
 #: engines/prince/metaengine.cpp:227 engines/private/metaengine.cpp:127
 #: engines/queen/metaengine.cpp:146 engines/sherlock/metaengine.cpp:313
@@ -6763,7 +6763,7 @@ msgstr "AGOS ??????"
 #: engines/hypno/metaengine.cpp:267 engines/hypno/metaengine.cpp:311
 #: engines/illusions/metaengine.cpp:90 engines/lab/metaengine.cpp:152
 #: engines/lure/metaengine.cpp:169 engines/made/metaengine.cpp:133
-#: engines/mads/metaengine.cpp:313 engines/neverhood/metaengine.cpp:186
+#: engines/mads/metaengine.cpp:317 engines/neverhood/metaengine.cpp:186
 #: engines/queen/metaengine.cpp:147 engines/saga/metaengine.cpp:268
 #: engines/sludge/keymapper_tables.h:33 engines/stark/metaengine.cpp:185
 #: engines/sword1/metaengine.cpp:267 engines/tinsel/metaengine.cpp:241
@@ -7250,7 +7250,7 @@ msgstr "Soldier 714 Eye ??????"
 #: engines/bbvs/metaengine.cpp:111 engines/cruise/metaengine.cpp:223
 #: engines/freescape/movement.cpp:107 engines/got/metaengine.cpp:78
 #: engines/hugo/metaengine.cpp:197 engines/lure/metaengine.cpp:209
-#: engines/mads/metaengine.cpp:330 engines/mm/mm1/metaengine.cpp:42
+#: engines/mads/metaengine.cpp:334 engines/mm/mm1/metaengine.cpp:42
 #: engines/mm/mm1/metaengine.cpp:48 engines/saga/metaengine.cpp:328
 #: engines/tinsel/metaengine.cpp:258 engines/tsage/metaengine.cpp:190
 #: engines/tucker/metaengine.cpp:221 engines/twine/metaengine.cpp:388
@@ -7547,7 +7547,7 @@ msgstr "?????? ?????? ?????? ?????? ??????"
 #: engines/freescape/games/eclipse/eclipse.cpp:330
 #: engines/hopkins/metaengine.cpp:236 engines/hugo/metaengine.cpp:165
 #: engines/lure/metaengine.cpp:191 engines/made/metaengine.cpp:192
-#: engines/mads/metaengine.cpp:343 engines/mohawk/myst.cpp:583
+#: engines/mads/metaengine.cpp:347 engines/mohawk/myst.cpp:583
 #: engines/mohawk/riven.cpp:864 engines/pegasus/pegasus.cpp:2825
 #: engines/petka/metaengine.cpp:198 engines/prince/metaengine.cpp:243
 #: engines/saga/metaengine.cpp:442 engines/sherlock/metaengine.cpp:462
@@ -7623,7 +7623,7 @@ msgstr "?????? ??????????????????"
 #: engines/freescape/games/dark/dark.cpp:236
 #: engines/freescape/games/driller/driller.cpp:129
 #: engines/freescape/games/eclipse/eclipse.cpp:340
-#: engines/mads/metaengine.cpp:399 engines/phoenixvr/metaengine.cpp:80
+#: engines/mads/metaengine.cpp:403 engines/phoenixvr/metaengine.cpp:80
 #: engines/saga/metaengine.cpp:437 engines/sherlock/metaengine.cpp:359
 #: engines/sherlock/metaengine.cpp:618 engines/sherlock/metaengine.cpp:852
 #: engines/stark/metaengine.cpp:280 engines/sword1/metaengine.cpp:295
@@ -9965,7 +9965,7 @@ msgid "Repeat last line"
 msgstr "????????? ??? ??????"
 
 #: engines/hugo/metaengine.cpp:172 engines/lure/metaengine.cpp:197
-#: engines/mads/metaengine.cpp:349 engines/tsage/metaengine.cpp:247
+#: engines/mads/metaengine.cpp:353 engines/tsage/metaengine.cpp:247
 msgid "Restore game"
 msgstr "?????? ??????"
 
@@ -10154,7 +10154,7 @@ msgstr "?????? ?????? ?????????"
 msgid "Direction keymappings"
 msgstr "?????? ?????????"
 
-#: engines/hypno/metaengine.cpp:316 engines/mads/metaengine.cpp:387
+#: engines/hypno/metaengine.cpp:316 engines/mads/metaengine.cpp:391
 #: engines/tsage/metaengine.cpp:259
 msgid "Show credits"
 msgstr "????????? ??????"
@@ -10792,39 +10792,39 @@ msgstr "??????????????? ?????? ???????????????"
 msgid "Naughty game mode"
 msgstr "????????? ?????? ??????"
 
-#: engines/mads/metaengine.cpp:314
+#: engines/mads/metaengine.cpp:318
 msgid "Start menu keymappings"
 msgstr "?????? ?????? ?????????"
 
-#: engines/mads/metaengine.cpp:337
+#: engines/mads/metaengine.cpp:341
 msgid "Open game menu"
 msgstr "?????? ?????? ??????"
 
-#: engines/mads/metaengine.cpp:355
+#: engines/mads/metaengine.cpp:359
 msgid "Scroll bar up"
 msgstr "??????????????? ??????"
 
-#: engines/mads/metaengine.cpp:361
+#: engines/mads/metaengine.cpp:365
 msgid "Scroll bar down"
 msgstr "??????????????? ?????????"
 
-#: engines/mads/metaengine.cpp:369
+#: engines/mads/metaengine.cpp:373
 msgid "Start game"
 msgstr "?????? ??????"
 
-#: engines/mads/metaengine.cpp:375
+#: engines/mads/metaengine.cpp:379
 msgid "Resume game"
 msgstr "?????? ??????"
 
-#: engines/mads/metaengine.cpp:381
+#: engines/mads/metaengine.cpp:385
 msgid "Show intro"
 msgstr "????????? ??????"
 
-#: engines/mads/metaengine.cpp:393
+#: engines/mads/metaengine.cpp:397
 msgid "Show quotes"
 msgstr "????????? ??????"
 
-#: engines/mads/metaengine.cpp:406
+#: engines/mads/metaengine.cpp:410
 msgid "Restart animation"
 msgstr "??????????????? ?????????"
 
diff --git a/po/nb_NO.po b/po/nb_NO.po
index 3aa47a2a6e0..8b45896f7ec 100644
--- a/po/nb_NO.po
+++ b/po/nb_NO.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-07 22:09+0000\n"
+"POT-Creation-Date: 2026-06-08 11:04+0000\n"
 "PO-Revision-Date: 2026-02-07 15:22+0000\n"
 "Last-Translator: Einar Johan Tr??an S??m??en <einarjohants at gmail.com>\n"
 "Language-Team: Norwegian Bokm??l <https://translations.scummvm.org/projects/"
@@ -3979,7 +3979,7 @@ msgstr "Spillets standard knappoppsett"
 #: engines/hugo/metaengine.cpp:134 engines/illusions/metaengine.cpp:94
 #: engines/kyra/engine/eobcommon.cpp:369 engines/kyra/engine/lol.cpp:505
 #: engines/lure/metaengine.cpp:177 engines/made/metaengine.cpp:137
-#: engines/mads/metaengine.cpp:318 engines/mm/metaengine.cpp:186
+#: engines/mads/metaengine.cpp:322 engines/mm/metaengine.cpp:186
 #: engines/mtropolis/metaengine.cpp:161 engines/nancy/input.cpp:181
 #: engines/parallaction/metaengine.cpp:138 engines/saga/metaengine.cpp:278
 #: engines/sludge/keymapper_tables.h:561 engines/sludge/keymapper_tables.h:580
@@ -4016,7 +4016,7 @@ msgstr "Midtklikk"
 #: engines/hopkins/metaengine.cpp:217 engines/hugo/metaengine.cpp:141
 #: engines/illusions/metaengine.cpp:101 engines/kyra/engine/eobcommon.cpp:370
 #: engines/kyra/engine/lol.cpp:506 engines/lure/metaengine.cpp:183
-#: engines/made/metaengine.cpp:144 engines/mads/metaengine.cpp:324
+#: engines/made/metaengine.cpp:144 engines/mads/metaengine.cpp:328
 #: engines/mm/metaengine.cpp:192 engines/mtropolis/metaengine.cpp:167
 #: engines/nancy/input.cpp:188 engines/parallaction/metaengine.cpp:144
 #: engines/saga/metaengine.cpp:289 engines/sludge/keymapper_tables.h:567
@@ -6612,7 +6612,7 @@ msgstr "Sjekk for oppdateringer..."
 #: engines/hugo/metaengine.cpp:129 engines/hypno/metaengine.cpp:128
 #: engines/illusions/metaengine.cpp:89 engines/lab/metaengine.cpp:149
 #: engines/lure/metaengine.cpp:168 engines/made/metaengine.cpp:132
-#: engines/mads/metaengine.cpp:312 engines/neverhood/metaengine.cpp:185
+#: engines/mads/metaengine.cpp:316 engines/neverhood/metaengine.cpp:185
 #: engines/ngi/metaengine.cpp:115 engines/petka/metaengine.cpp:113
 #: engines/prince/metaengine.cpp:227 engines/private/metaengine.cpp:127
 #: engines/queen/metaengine.cpp:146 engines/sherlock/metaengine.cpp:313
@@ -7174,7 +7174,7 @@ msgstr "AGOS prim??r"
 #: engines/hypno/metaengine.cpp:267 engines/hypno/metaengine.cpp:311
 #: engines/illusions/metaengine.cpp:90 engines/lab/metaengine.cpp:152
 #: engines/lure/metaengine.cpp:169 engines/made/metaengine.cpp:133
-#: engines/mads/metaengine.cpp:313 engines/neverhood/metaengine.cpp:186
+#: engines/mads/metaengine.cpp:317 engines/neverhood/metaengine.cpp:186
 #: engines/queen/metaengine.cpp:147 engines/saga/metaengine.cpp:268
 #: engines/sludge/keymapper_tables.h:33 engines/stark/metaengine.cpp:185
 #: engines/sword1/metaengine.cpp:267 engines/tinsel/metaengine.cpp:241
@@ -7698,7 +7698,7 @@ msgstr "Soldat 714-??yejuks"
 #: engines/bbvs/metaengine.cpp:111 engines/cruise/metaengine.cpp:223
 #: engines/freescape/movement.cpp:107 engines/got/metaengine.cpp:78
 #: engines/hugo/metaengine.cpp:197 engines/lure/metaengine.cpp:209
-#: engines/mads/metaengine.cpp:330 engines/mm/mm1/metaengine.cpp:42
+#: engines/mads/metaengine.cpp:334 engines/mm/mm1/metaengine.cpp:42
 #: engines/mm/mm1/metaengine.cpp:48 engines/saga/metaengine.cpp:328
 #: engines/tinsel/metaengine.cpp:258 engines/tsage/metaengine.cpp:190
 #: engines/tucker/metaengine.cpp:221 engines/twine/metaengine.cpp:388
@@ -8023,7 +8023,7 @@ msgstr "Ledetr??der privat/offentlig"
 #: engines/freescape/games/eclipse/eclipse.cpp:330
 #: engines/hopkins/metaengine.cpp:236 engines/hugo/metaengine.cpp:165
 #: engines/lure/metaengine.cpp:191 engines/made/metaengine.cpp:192
-#: engines/mads/metaengine.cpp:343 engines/mohawk/myst.cpp:583
+#: engines/mads/metaengine.cpp:347 engines/mohawk/myst.cpp:583
 #: engines/mohawk/riven.cpp:864 engines/pegasus/pegasus.cpp:2825
 #: engines/petka/metaengine.cpp:198 engines/prince/metaengine.cpp:243
 #: engines/saga/metaengine.cpp:442 engines/sherlock/metaengine.cpp:462
@@ -8105,7 +8105,7 @@ msgstr "Ledetr??dsdatabase"
 #: engines/freescape/games/dark/dark.cpp:236
 #: engines/freescape/games/driller/driller.cpp:129
 #: engines/freescape/games/eclipse/eclipse.cpp:340
-#: engines/mads/metaengine.cpp:399 engines/phoenixvr/metaengine.cpp:80
+#: engines/mads/metaengine.cpp:403 engines/phoenixvr/metaengine.cpp:80
 #: engines/saga/metaengine.cpp:437 engines/sherlock/metaengine.cpp:359
 #: engines/sherlock/metaengine.cpp:618 engines/sherlock/metaengine.cpp:852
 #: engines/stark/metaengine.cpp:280 engines/sword1/metaengine.cpp:295
@@ -10839,7 +10839,7 @@ msgid "Repeat last line"
 msgstr "Gjenta forrige linje"
 
 #: engines/hugo/metaengine.cpp:172 engines/lure/metaengine.cpp:197
-#: engines/mads/metaengine.cpp:349 engines/tsage/metaengine.cpp:247
+#: engines/mads/metaengine.cpp:353 engines/tsage/metaengine.cpp:247
 msgid "Restore game"
 msgstr "Gjenopprett spill"
 
@@ -11051,7 +11051,7 @@ msgstr "Spilltasteoppsett"
 msgid "Direction keymappings"
 msgstr "Indeks-tasteoppsett"
 
-#: engines/hypno/metaengine.cpp:316 engines/mads/metaengine.cpp:387
+#: engines/hypno/metaengine.cpp:316 engines/mads/metaengine.cpp:391
 #: engines/tsage/metaengine.cpp:259
 msgid "Show credits"
 msgstr "Vis medvirkende"
@@ -11750,39 +11750,39 @@ msgstr "Animert spillgrensesnitt"
 msgid "Naughty game mode"
 msgstr "Frekk spillmodus"
 
-#: engines/mads/metaengine.cpp:314
+#: engines/mads/metaengine.cpp:318
 msgid "Start menu keymappings"
 msgstr "Startmeny-tasteoppsett"
 
-#: engines/mads/metaengine.cpp:337
+#: engines/mads/metaengine.cpp:341
 msgid "Open game menu"
 msgstr "??pne spillmeny"
 
-#: engines/mads/metaengine.cpp:355
+#: engines/mads/metaengine.cpp:359
 msgid "Scroll bar up"
 msgstr "Bla opp"
 
-#: engines/mads/metaengine.cpp:361
+#: engines/mads/metaengine.cpp:365
 msgid "Scroll bar down"
 msgstr "Bla ned"
 
-#: engines/mads/metaengine.cpp:369
+#: engines/mads/metaengine.cpp:373
 msgid "Start game"
 msgstr "Start spill"
 
-#: engines/mads/metaengine.cpp:375
+#: engines/mads/metaengine.cpp:379
 msgid "Resume game"
 msgstr "Fortsett spill"
 
-#: engines/mads/metaengine.cpp:381
+#: engines/mads/metaengine.cpp:385
 msgid "Show intro"
 msgstr "Vis intro"
 
-#: engines/mads/metaengine.cpp:393
+#: engines/mads/metaengine.cpp:397
 msgid "Show quotes"
 msgstr "Vis sitater"
 
-#: engines/mads/metaengine.cpp:406
+#: engines/mads/metaengine.cpp:410
 msgid "Restart animation"
 msgstr "Start animasjon p?? nytt"
 
diff --git a/po/nl_NL.po b/po/nl_NL.po
index 36c193559a4..938ae747187 100644
--- a/po/nl_NL.po
+++ b/po/nl_NL.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.9.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-07 22:09+0000\n"
+"POT-Creation-Date: 2026-06-08 11:04+0000\n"
 "PO-Revision-Date: 2026-02-11 14:23+0000\n"
 "Last-Translator: SecularSteve <fairfull.playing at gmail.com>\n"
 "Language-Team: Dutch <https://translations.scummvm.org/projects/scummvm/"
@@ -3910,7 +3910,7 @@ msgstr "Standaard speltoetsenkoppelingen"
 #: engines/hugo/metaengine.cpp:134 engines/illusions/metaengine.cpp:94
 #: engines/kyra/engine/eobcommon.cpp:369 engines/kyra/engine/lol.cpp:505
 #: engines/lure/metaengine.cpp:177 engines/made/metaengine.cpp:137
-#: engines/mads/metaengine.cpp:318 engines/mm/metaengine.cpp:186
+#: engines/mads/metaengine.cpp:322 engines/mm/metaengine.cpp:186
 #: engines/mtropolis/metaengine.cpp:161 engines/nancy/input.cpp:181
 #: engines/parallaction/metaengine.cpp:138 engines/saga/metaengine.cpp:278
 #: engines/sludge/keymapper_tables.h:561 engines/sludge/keymapper_tables.h:580
@@ -3943,7 +3943,7 @@ msgstr "Middenklik"
 #: engines/hopkins/metaengine.cpp:217 engines/hugo/metaengine.cpp:141
 #: engines/illusions/metaengine.cpp:101 engines/kyra/engine/eobcommon.cpp:370
 #: engines/kyra/engine/lol.cpp:506 engines/lure/metaengine.cpp:183
-#: engines/made/metaengine.cpp:144 engines/mads/metaengine.cpp:324
+#: engines/made/metaengine.cpp:144 engines/mads/metaengine.cpp:328
 #: engines/mm/metaengine.cpp:192 engines/mtropolis/metaengine.cpp:167
 #: engines/nancy/input.cpp:188 engines/parallaction/metaengine.cpp:144
 #: engines/saga/metaengine.cpp:289 engines/sludge/keymapper_tables.h:567
@@ -6287,7 +6287,7 @@ msgstr "Controleren op updates..."
 #: engines/hugo/metaengine.cpp:129 engines/hypno/metaengine.cpp:128
 #: engines/illusions/metaengine.cpp:89 engines/lab/metaengine.cpp:149
 #: engines/lure/metaengine.cpp:168 engines/made/metaengine.cpp:132
-#: engines/mads/metaengine.cpp:312 engines/neverhood/metaengine.cpp:185
+#: engines/mads/metaengine.cpp:316 engines/neverhood/metaengine.cpp:185
 #: engines/ngi/metaengine.cpp:115 engines/petka/metaengine.cpp:113
 #: engines/prince/metaengine.cpp:227 engines/private/metaengine.cpp:127
 #: engines/queen/metaengine.cpp:146 engines/sherlock/metaengine.cpp:313
@@ -6851,7 +6851,7 @@ msgstr "AGOS basis"
 #: engines/hypno/metaengine.cpp:267 engines/hypno/metaengine.cpp:311
 #: engines/illusions/metaengine.cpp:90 engines/lab/metaengine.cpp:152
 #: engines/lure/metaengine.cpp:169 engines/made/metaengine.cpp:133
-#: engines/mads/metaengine.cpp:313 engines/neverhood/metaengine.cpp:186
+#: engines/mads/metaengine.cpp:317 engines/neverhood/metaengine.cpp:186
 #: engines/queen/metaengine.cpp:147 engines/saga/metaengine.cpp:268
 #: engines/sludge/keymapper_tables.h:33 engines/stark/metaengine.cpp:185
 #: engines/sword1/metaengine.cpp:267 engines/tinsel/metaengine.cpp:241
@@ -7354,7 +7354,7 @@ msgstr "Soldier 714 Oog Valsspelen"
 #: engines/bbvs/metaengine.cpp:111 engines/cruise/metaengine.cpp:223
 #: engines/freescape/movement.cpp:107 engines/got/metaengine.cpp:78
 #: engines/hugo/metaengine.cpp:197 engines/lure/metaengine.cpp:209
-#: engines/mads/metaengine.cpp:330 engines/mm/mm1/metaengine.cpp:42
+#: engines/mads/metaengine.cpp:334 engines/mm/mm1/metaengine.cpp:42
 #: engines/mm/mm1/metaengine.cpp:48 engines/saga/metaengine.cpp:328
 #: engines/tinsel/metaengine.cpp:258 engines/tsage/metaengine.cpp:190
 #: engines/tucker/metaengine.cpp:221 engines/twine/metaengine.cpp:388
@@ -7655,7 +7655,7 @@ msgstr "Privacy van aanwijzingen in-/uitschakelen"
 #: engines/freescape/games/eclipse/eclipse.cpp:330
 #: engines/hopkins/metaengine.cpp:236 engines/hugo/metaengine.cpp:165
 #: engines/lure/metaengine.cpp:191 engines/made/metaengine.cpp:192
-#: engines/mads/metaengine.cpp:343 engines/mohawk/myst.cpp:583
+#: engines/mads/metaengine.cpp:347 engines/mohawk/myst.cpp:583
 #: engines/mohawk/riven.cpp:864 engines/pegasus/pegasus.cpp:2825
 #: engines/petka/metaengine.cpp:198 engines/prince/metaengine.cpp:243
 #: engines/saga/metaengine.cpp:442 engines/sherlock/metaengine.cpp:462
@@ -7731,7 +7731,7 @@ msgstr "Aanwijzingen database"
 #: engines/freescape/games/dark/dark.cpp:236
 #: engines/freescape/games/driller/driller.cpp:129
 #: engines/freescape/games/eclipse/eclipse.cpp:340
-#: engines/mads/metaengine.cpp:399 engines/phoenixvr/metaengine.cpp:80
+#: engines/mads/metaengine.cpp:403 engines/phoenixvr/metaengine.cpp:80
 #: engines/saga/metaengine.cpp:437 engines/sherlock/metaengine.cpp:359
 #: engines/sherlock/metaengine.cpp:618 engines/sherlock/metaengine.cpp:852
 #: engines/stark/metaengine.cpp:280 engines/sword1/metaengine.cpp:295
@@ -10225,7 +10225,7 @@ msgid "Repeat last line"
 msgstr "Herhaal laatste regel"
 
 #: engines/hugo/metaengine.cpp:172 engines/lure/metaengine.cpp:197
-#: engines/mads/metaengine.cpp:349 engines/tsage/metaengine.cpp:247
+#: engines/mads/metaengine.cpp:353 engines/tsage/metaengine.cpp:247
 msgid "Restore game"
 msgstr "Laad opgeslagen spel"
 
@@ -10416,7 +10416,7 @@ msgstr "Toetsencombinaties pauzeren"
 msgid "Direction keymappings"
 msgstr "Richtingstoetsindelingen"
 
-#: engines/hypno/metaengine.cpp:316 engines/mads/metaengine.cpp:387
+#: engines/hypno/metaengine.cpp:316 engines/mads/metaengine.cpp:391
 #: engines/tsage/metaengine.cpp:259
 msgid "Show credits"
 msgstr "Tool credits"
@@ -11065,39 +11065,39 @@ msgstr "Geanimeerde spel interface"
 msgid "Naughty game mode"
 msgstr "Ondeugende spelmodus"
 
-#: engines/mads/metaengine.cpp:314
+#: engines/mads/metaengine.cpp:318
 msgid "Start menu keymappings"
 msgstr "Toetsenbordindeling startmenu"
 
-#: engines/mads/metaengine.cpp:337
+#: engines/mads/metaengine.cpp:341
 msgid "Open game menu"
 msgstr "Open spelmenu"
 
-#: engines/mads/metaengine.cpp:355
+#: engines/mads/metaengine.cpp:359
 msgid "Scroll bar up"
 msgstr "Schuifbalk omhoog"
 
-#: engines/mads/metaengine.cpp:361
+#: engines/mads/metaengine.cpp:365
 msgid "Scroll bar down"
 msgstr "Scrollbalk omlaag"
 
-#: engines/mads/metaengine.cpp:369
+#: engines/mads/metaengine.cpp:373
 msgid "Start game"
 msgstr "Start spel"
 
-#: engines/mads/metaengine.cpp:375
+#: engines/mads/metaengine.cpp:379
 msgid "Resume game"
 msgstr "Hervat spel"
 
-#: engines/mads/metaengine.cpp:381
+#: engines/mads/metaengine.cpp:385
 msgid "Show intro"
 msgstr "Intro tonen"
 
-#: engines/mads/metaengine.cpp:393
+#: engines/mads/metaengine.cpp:397
 msgid "Show quotes"
 msgstr "Citaten weergeven"
 
-#: engines/mads/metaengine.cpp:406
+#: engines/mads/metaengine.cpp:410
 msgid "Restart animation"
 msgstr "Animatie opnieuw starten"
 
diff --git a/po/nn_NO.po b/po/nn_NO.po
index 0acdf4d0969..f3ce860d457 100644
--- a/po/nn_NO.po
+++ b/po/nn_NO.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-07 22:09+0000\n"
+"POT-Creation-Date: 2026-06-08 11:04+0000\n"
 "PO-Revision-Date: 2023-12-14 14:28+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Norwegian Nynorsk <https://translations.scummvm.org/projects/"
@@ -3838,7 +3838,7 @@ msgstr "Spelval"
 #: engines/hugo/metaengine.cpp:134 engines/illusions/metaengine.cpp:94
 #: engines/kyra/engine/eobcommon.cpp:369 engines/kyra/engine/lol.cpp:505
 #: engines/lure/metaengine.cpp:177 engines/made/metaengine.cpp:137
-#: engines/mads/metaengine.cpp:318 engines/mm/metaengine.cpp:186
+#: engines/mads/metaengine.cpp:322 engines/mm/metaengine.cpp:186
 #: engines/mtropolis/metaengine.cpp:161 engines/nancy/input.cpp:181
 #: engines/parallaction/metaengine.cpp:138 engines/saga/metaengine.cpp:278
 #: engines/sludge/keymapper_tables.h:561 engines/sludge/keymapper_tables.h:580
@@ -3875,7 +3875,7 @@ msgstr "Midtklikk"
 #: engines/hopkins/metaengine.cpp:217 engines/hugo/metaengine.cpp:141
 #: engines/illusions/metaengine.cpp:101 engines/kyra/engine/eobcommon.cpp:370
 #: engines/kyra/engine/lol.cpp:506 engines/lure/metaengine.cpp:183
-#: engines/made/metaengine.cpp:144 engines/mads/metaengine.cpp:324
+#: engines/made/metaengine.cpp:144 engines/mads/metaengine.cpp:328
 #: engines/mm/metaengine.cpp:192 engines/mtropolis/metaengine.cpp:167
 #: engines/nancy/input.cpp:188 engines/parallaction/metaengine.cpp:144
 #: engines/saga/metaengine.cpp:289 engines/sludge/keymapper_tables.h:567
@@ -6015,7 +6015,7 @@ msgstr "SJ?? etter oppdateringar..."
 #: engines/hugo/metaengine.cpp:129 engines/hypno/metaengine.cpp:128
 #: engines/illusions/metaengine.cpp:89 engines/lab/metaengine.cpp:149
 #: engines/lure/metaengine.cpp:168 engines/made/metaengine.cpp:132
-#: engines/mads/metaengine.cpp:312 engines/neverhood/metaengine.cpp:185
+#: engines/mads/metaengine.cpp:316 engines/neverhood/metaengine.cpp:185
 #: engines/ngi/metaengine.cpp:115 engines/petka/metaengine.cpp:113
 #: engines/prince/metaengine.cpp:227 engines/private/metaengine.cpp:127
 #: engines/queen/metaengine.cpp:146 engines/sherlock/metaengine.cpp:313
@@ -6580,7 +6580,7 @@ msgstr ""
 #: engines/hypno/metaengine.cpp:267 engines/hypno/metaengine.cpp:311
 #: engines/illusions/metaengine.cpp:90 engines/lab/metaengine.cpp:152
 #: engines/lure/metaengine.cpp:169 engines/made/metaengine.cpp:133
-#: engines/mads/metaengine.cpp:313 engines/neverhood/metaengine.cpp:186
+#: engines/mads/metaengine.cpp:317 engines/neverhood/metaengine.cpp:186
 #: engines/queen/metaengine.cpp:147 engines/saga/metaengine.cpp:268
 #: engines/sludge/keymapper_tables.h:33 engines/stark/metaengine.cpp:185
 #: engines/sword1/metaengine.cpp:267 engines/tinsel/metaengine.cpp:241
@@ -7088,7 +7088,7 @@ msgstr ""
 #: engines/bbvs/metaengine.cpp:111 engines/cruise/metaengine.cpp:223
 #: engines/freescape/movement.cpp:107 engines/got/metaengine.cpp:78
 #: engines/hugo/metaengine.cpp:197 engines/lure/metaengine.cpp:209
-#: engines/mads/metaengine.cpp:330 engines/mm/mm1/metaengine.cpp:42
+#: engines/mads/metaengine.cpp:334 engines/mm/mm1/metaengine.cpp:42
 #: engines/mm/mm1/metaengine.cpp:48 engines/saga/metaengine.cpp:328
 #: engines/tinsel/metaengine.cpp:258 engines/tsage/metaengine.cpp:190
 #: engines/tucker/metaengine.cpp:221 engines/twine/metaengine.cpp:388
@@ -7399,7 +7399,7 @@ msgstr "Veksle musl??sing"
 #: engines/freescape/games/eclipse/eclipse.cpp:330
 #: engines/hopkins/metaengine.cpp:236 engines/hugo/metaengine.cpp:165
 #: engines/lure/metaengine.cpp:191 engines/made/metaengine.cpp:192
-#: engines/mads/metaengine.cpp:343 engines/mohawk/myst.cpp:583
+#: engines/mads/metaengine.cpp:347 engines/mohawk/myst.cpp:583
 #: engines/mohawk/riven.cpp:864 engines/pegasus/pegasus.cpp:2825
 #: engines/petka/metaengine.cpp:198 engines/prince/metaengine.cpp:243
 #: engines/saga/metaengine.cpp:442 engines/sherlock/metaengine.cpp:462
@@ -7477,7 +7477,7 @@ msgstr ""
 #: engines/freescape/games/dark/dark.cpp:236
 #: engines/freescape/games/driller/driller.cpp:129
 #: engines/freescape/games/eclipse/eclipse.cpp:340
-#: engines/mads/metaengine.cpp:399 engines/phoenixvr/metaengine.cpp:80
+#: engines/mads/metaengine.cpp:403 engines/phoenixvr/metaengine.cpp:80
 #: engines/saga/metaengine.cpp:437 engines/sherlock/metaengine.cpp:359
 #: engines/sherlock/metaengine.cpp:618 engines/sherlock/metaengine.cpp:852
 #: engines/stark/metaengine.cpp:280 engines/sword1/metaengine.cpp:295
@@ -10144,7 +10144,7 @@ msgid "Repeat last line"
 msgstr "Ingen dato lagra"
 
 #: engines/hugo/metaengine.cpp:172 engines/lure/metaengine.cpp:197
-#: engines/mads/metaengine.cpp:349 engines/tsage/metaengine.cpp:247
+#: engines/mads/metaengine.cpp:353 engines/tsage/metaengine.cpp:247
 #, fuzzy
 #| msgid "Restore game:"
 msgid "Restore game"
@@ -10367,7 +10367,7 @@ msgstr "Spelval"
 msgid "Direction keymappings"
 msgstr "Spelval"
 
-#: engines/hypno/metaengine.cpp:316 engines/mads/metaengine.cpp:387
+#: engines/hypno/metaengine.cpp:316 engines/mads/metaengine.cpp:391
 #: engines/tsage/metaengine.cpp:259
 #, fuzzy
 msgid "Show credits"
@@ -11029,50 +11029,50 @@ msgstr ""
 msgid "Naughty game mode"
 msgstr ""
 
-#: engines/mads/metaengine.cpp:314
+#: engines/mads/metaengine.cpp:318
 #, fuzzy
 #| msgid "Game Options"
 msgid "Start menu keymappings"
 msgstr "Spelval"
 
-#: engines/mads/metaengine.cpp:337
+#: engines/mads/metaengine.cpp:341
 #, fuzzy
 msgid "Open game menu"
 msgstr "Tilbake til Oppsta~r~tar"
 
-#: engines/mads/metaengine.cpp:355
+#: engines/mads/metaengine.cpp:359
 #, fuzzy
 msgid "Scroll bar up"
 msgstr "Bla liste opp"
 
-#: engines/mads/metaengine.cpp:361
+#: engines/mads/metaengine.cpp:365
 #, fuzzy
 msgid "Scroll bar down"
 msgstr "Bla liste ned"
 
-#: engines/mads/metaengine.cpp:369
+#: engines/mads/metaengine.cpp:373
 #, fuzzy
 #| msgid "Restore game:"
 msgid "Start game"
 msgstr "Gjenopprett spel:"
 
-#: engines/mads/metaengine.cpp:375
+#: engines/mads/metaengine.cpp:379
 #, fuzzy
 #| msgid "Restore game:"
 msgid "Resume game"
 msgstr "Gjenopprett spel:"
 
-#: engines/mads/metaengine.cpp:381
+#: engines/mads/metaengine.cpp:385
 #, fuzzy
 msgid "Show intro"
 msgstr "Vis skjulte filer"
 
-#: engines/mads/metaengine.cpp:393
+#: engines/mads/metaengine.cpp:397
 #, fuzzy
 msgid "Show quotes"
 msgstr "Berre Tale"
 
-#: engines/mads/metaengine.cpp:406
+#: engines/mads/metaengine.cpp:410
 #, fuzzy
 msgid "Restart animation"
 msgstr "~O~vergangar aktivert"
diff --git a/po/pl_PL.po b/po/pl_PL.po
index 49144fdfa45..101a35c7c52 100644
--- a/po/pl_PL.po
+++ b/po/pl_PL.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-07 22:09+0000\n"
+"POT-Creation-Date: 2026-06-08 11:04+0000\n"
 "PO-Revision-Date: 2026-04-17 00:22+0000\n"
 "Last-Translator: SkiffPL <skiff at windowslive.com>\n"
 "Language-Team: Polish <https://translations.scummvm.org/projects/scummvm/"
@@ -3901,7 +3901,7 @@ msgstr "Domy??lne mapowanie klawiatury"
 #: engines/hugo/metaengine.cpp:134 engines/illusions/metaengine.cpp:94
 #: engines/kyra/engine/eobcommon.cpp:369 engines/kyra/engine/lol.cpp:505
 #: engines/lure/metaengine.cpp:177 engines/made/metaengine.cpp:137
-#: engines/mads/metaengine.cpp:318 engines/mm/metaengine.cpp:186
+#: engines/mads/metaengine.cpp:322 engines/mm/metaengine.cpp:186
 #: engines/mtropolis/metaengine.cpp:161 engines/nancy/input.cpp:181
 #: engines/parallaction/metaengine.cpp:138 engines/saga/metaengine.cpp:278
 #: engines/sludge/keymapper_tables.h:561 engines/sludge/keymapper_tables.h:580
@@ -3934,7 +3934,7 @@ msgstr "??rodkowy przycisk"
 #: engines/hopkins/metaengine.cpp:217 engines/hugo/metaengine.cpp:141
 #: engines/illusions/metaengine.cpp:101 engines/kyra/engine/eobcommon.cpp:370
 #: engines/kyra/engine/lol.cpp:506 engines/lure/metaengine.cpp:183
-#: engines/made/metaengine.cpp:144 engines/mads/metaengine.cpp:324
+#: engines/made/metaengine.cpp:144 engines/mads/metaengine.cpp:328
 #: engines/mm/metaengine.cpp:192 engines/mtropolis/metaengine.cpp:167
 #: engines/nancy/input.cpp:188 engines/parallaction/metaengine.cpp:144
 #: engines/saga/metaengine.cpp:289 engines/sludge/keymapper_tables.h:567
@@ -6299,7 +6299,7 @@ msgstr "Szukaj aktualizacji..."
 #: engines/hugo/metaengine.cpp:129 engines/hypno/metaengine.cpp:128
 #: engines/illusions/metaengine.cpp:89 engines/lab/metaengine.cpp:149
 #: engines/lure/metaengine.cpp:168 engines/made/metaengine.cpp:132
-#: engines/mads/metaengine.cpp:312 engines/neverhood/metaengine.cpp:185
+#: engines/mads/metaengine.cpp:316 engines/neverhood/metaengine.cpp:185
 #: engines/ngi/metaengine.cpp:115 engines/petka/metaengine.cpp:113
 #: engines/prince/metaengine.cpp:227 engines/private/metaengine.cpp:127
 #: engines/queen/metaengine.cpp:146 engines/sherlock/metaengine.cpp:313
@@ -6864,7 +6864,7 @@ msgstr "G????wny AGOS"
 #: engines/hypno/metaengine.cpp:267 engines/hypno/metaengine.cpp:311
 #: engines/illusions/metaengine.cpp:90 engines/lab/metaengine.cpp:152
 #: engines/lure/metaengine.cpp:169 engines/made/metaengine.cpp:133
-#: engines/mads/metaengine.cpp:313 engines/neverhood/metaengine.cpp:186
+#: engines/mads/metaengine.cpp:317 engines/neverhood/metaengine.cpp:186
 #: engines/queen/metaengine.cpp:147 engines/saga/metaengine.cpp:268
 #: engines/sludge/keymapper_tables.h:33 engines/stark/metaengine.cpp:185
 #: engines/sword1/metaengine.cpp:267 engines/tinsel/metaengine.cpp:241
@@ -7363,7 +7363,7 @@ msgstr "Cheat oczu ??o??nierza 714"
 #: engines/bbvs/metaengine.cpp:111 engines/cruise/metaengine.cpp:223
 #: engines/freescape/movement.cpp:107 engines/got/metaengine.cpp:78
 #: engines/hugo/metaengine.cpp:197 engines/lure/metaengine.cpp:209
-#: engines/mads/metaengine.cpp:330 engines/mm/mm1/metaengine.cpp:42
+#: engines/mads/metaengine.cpp:334 engines/mm/mm1/metaengine.cpp:42
 #: engines/mm/mm1/metaengine.cpp:48 engines/saga/metaengine.cpp:328
 #: engines/tinsel/metaengine.cpp:258 engines/tsage/metaengine.cpp:190
 #: engines/tucker/metaengine.cpp:221 engines/twine/metaengine.cpp:388
@@ -7669,7 +7669,7 @@ msgstr "Prze????cz prywatno???? wskaz??wek"
 #: engines/freescape/games/eclipse/eclipse.cpp:330
 #: engines/hopkins/metaengine.cpp:236 engines/hugo/metaengine.cpp:165
 #: engines/lure/metaengine.cpp:191 engines/made/metaengine.cpp:192
-#: engines/mads/metaengine.cpp:343 engines/mohawk/myst.cpp:583
+#: engines/mads/metaengine.cpp:347 engines/mohawk/myst.cpp:583
 #: engines/mohawk/riven.cpp:864 engines/pegasus/pegasus.cpp:2825
 #: engines/petka/metaengine.cpp:198 engines/prince/metaengine.cpp:243
 #: engines/saga/metaengine.cpp:442 engines/sherlock/metaengine.cpp:462
@@ -7745,7 +7745,7 @@ msgstr "Baza danych wskaz??wek"
 #: engines/freescape/games/dark/dark.cpp:236
 #: engines/freescape/games/driller/driller.cpp:129
 #: engines/freescape/games/eclipse/eclipse.cpp:340
-#: engines/mads/metaengine.cpp:399 engines/phoenixvr/metaengine.cpp:80
+#: engines/mads/metaengine.cpp:403 engines/phoenixvr/metaengine.cpp:80
 #: engines/saga/metaengine.cpp:437 engines/sherlock/metaengine.cpp:359
 #: engines/sherlock/metaengine.cpp:618 engines/sherlock/metaengine.cpp:852
 #: engines/stark/metaengine.cpp:280 engines/sword1/metaengine.cpp:295
@@ -10335,7 +10335,7 @@ msgid "Repeat last line"
 msgstr "Powt??rz ostatni?? lini??"
 
 #: engines/hugo/metaengine.cpp:172 engines/lure/metaengine.cpp:197
-#: engines/mads/metaengine.cpp:349 engines/tsage/metaengine.cpp:247
+#: engines/mads/metaengine.cpp:353 engines/tsage/metaengine.cpp:247
 msgid "Restore game"
 msgstr "Wzn??w gr??"
 
@@ -10544,7 +10544,7 @@ msgstr "Mapowanie klawiszy menu pauzy"
 msgid "Direction keymappings"
 msgstr "Mapowanie klawiszy intra"
 
-#: engines/hypno/metaengine.cpp:316 engines/mads/metaengine.cpp:387
+#: engines/hypno/metaengine.cpp:316 engines/mads/metaengine.cpp:391
 #: engines/tsage/metaengine.cpp:259
 msgid "Show credits"
 msgstr "Wy??wietl autor??w"
@@ -11217,39 +11217,39 @@ msgstr "Animowany interfejs gry"
 msgid "Naughty game mode"
 msgstr "Niegrzeczny tryb gry"
 
-#: engines/mads/metaengine.cpp:314
+#: engines/mads/metaengine.cpp:318
 msgid "Start menu keymappings"
 msgstr "Mapowanie klawiszy menu start"
 
-#: engines/mads/metaengine.cpp:337
+#: engines/mads/metaengine.cpp:341
 msgid "Open game menu"
 msgstr "Otw??rz menu gry"
 
-#: engines/mads/metaengine.cpp:355
+#: engines/mads/metaengine.cpp:359
 msgid "Scroll bar up"
 msgstr "Pasek przewijania w g??r??"
 
-#: engines/mads/metaengine.cpp:361
+#: engines/mads/metaengine.cpp:365
 msgid "Scroll bar down"
 msgstr "Pasek przewijania w d????"
 
-#: engines/mads/metaengine.cpp:369
+#: engines/mads/metaengine.cpp:373
 msgid "Start game"
 msgstr "Uruchom gr??"
 
-#: engines/mads/metaengine.cpp:375
+#: engines/mads/metaengine.cpp:379
 msgid "Resume game"
 msgstr "Wzn??w gr??"
 
-#: engines/mads/metaengine.cpp:381
+#: engines/mads/metaengine.cpp:385
 msgid "Show intro"
 msgstr "Pokazuj intro"
 
-#: engines/mads/metaengine.cpp:393
+#: engines/mads/metaengine.cpp:397
 msgid "Show quotes"
 msgstr "Pokazuj cytaty"
 
-#: engines/mads/metaengine.cpp:406
+#: engines/mads/metaengine.cpp:410
 msgid "Restart animation"
 msgstr "Uruchom animacj?? ponownie"
 
diff --git a/po/pt_BR.po b/po/pt_BR.po
index f66c0828273..edb5ca8d364 100644
--- a/po/pt_BR.po
+++ b/po/pt_BR.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-07 22:09+0000\n"
+"POT-Creation-Date: 2026-06-08 11:04+0000\n"
 "PO-Revision-Date: 2024-11-16 02:13+0000\n"
 "Last-Translator: Marcel Souza Lemes <marcosoutsider at gmail.com>\n"
 "Language-Team: Portuguese (Brazil) <https://translations.scummvm.org/"
@@ -3997,7 +3997,7 @@ msgstr "Mapeamento de tecla padr??o do jogo"
 #: engines/hugo/metaengine.cpp:134 engines/illusions/metaengine.cpp:94
 #: engines/kyra/engine/eobcommon.cpp:369 engines/kyra/engine/lol.cpp:505
 #: engines/lure/metaengine.cpp:177 engines/made/metaengine.cpp:137
-#: engines/mads/metaengine.cpp:318 engines/mm/metaengine.cpp:186
+#: engines/mads/metaengine.cpp:322 engines/mm/metaengine.cpp:186
 #: engines/mtropolis/metaengine.cpp:161 engines/nancy/input.cpp:181
 #: engines/parallaction/metaengine.cpp:138 engines/saga/metaengine.cpp:278
 #: engines/sludge/keymapper_tables.h:561 engines/sludge/keymapper_tables.h:580
@@ -4030,7 +4030,7 @@ msgstr "Clique com bot??o do meio"
 #: engines/hopkins/metaengine.cpp:217 engines/hugo/metaengine.cpp:141
 #: engines/illusions/metaengine.cpp:101 engines/kyra/engine/eobcommon.cpp:370
 #: engines/kyra/engine/lol.cpp:506 engines/lure/metaengine.cpp:183
-#: engines/made/metaengine.cpp:144 engines/mads/metaengine.cpp:324
+#: engines/made/metaengine.cpp:144 engines/mads/metaengine.cpp:328
 #: engines/mm/metaengine.cpp:192 engines/mtropolis/metaengine.cpp:167
 #: engines/nancy/input.cpp:188 engines/parallaction/metaengine.cpp:144
 #: engines/saga/metaengine.cpp:289 engines/sludge/keymapper_tables.h:567
@@ -6576,7 +6576,7 @@ msgstr "Procurar por Atualiza????es..."
 #: engines/hugo/metaengine.cpp:129 engines/hypno/metaengine.cpp:128
 #: engines/illusions/metaengine.cpp:89 engines/lab/metaengine.cpp:149
 #: engines/lure/metaengine.cpp:168 engines/made/metaengine.cpp:132
-#: engines/mads/metaengine.cpp:312 engines/neverhood/metaengine.cpp:185
+#: engines/mads/metaengine.cpp:316 engines/neverhood/metaengine.cpp:185
 #: engines/ngi/metaengine.cpp:115 engines/petka/metaengine.cpp:113
 #: engines/prince/metaengine.cpp:227 engines/private/metaengine.cpp:127
 #: engines/queen/metaengine.cpp:146 engines/sherlock/metaengine.cpp:313
@@ -7142,7 +7142,7 @@ msgstr "AGOS principal"
 #: engines/hypno/metaengine.cpp:267 engines/hypno/metaengine.cpp:311
 #: engines/illusions/metaengine.cpp:90 engines/lab/metaengine.cpp:152
 #: engines/lure/metaengine.cpp:169 engines/made/metaengine.cpp:133
-#: engines/mads/metaengine.cpp:313 engines/neverhood/metaengine.cpp:186
+#: engines/mads/metaengine.cpp:317 engines/neverhood/metaengine.cpp:186
 #: engines/queen/metaengine.cpp:147 engines/saga/metaengine.cpp:268
 #: engines/sludge/keymapper_tables.h:33 engines/stark/metaengine.cpp:185
 #: engines/sword1/metaengine.cpp:267 engines/tinsel/metaengine.cpp:241
@@ -7666,7 +7666,7 @@ msgstr "Trapa??a de Olho do Soldado 714"
 #: engines/bbvs/metaengine.cpp:111 engines/cruise/metaengine.cpp:223
 #: engines/freescape/movement.cpp:107 engines/got/metaengine.cpp:78
 #: engines/hugo/metaengine.cpp:197 engines/lure/metaengine.cpp:209
-#: engines/mads/metaengine.cpp:330 engines/mm/mm1/metaengine.cpp:42
+#: engines/mads/metaengine.cpp:334 engines/mm/mm1/metaengine.cpp:42
 #: engines/mm/mm1/metaengine.cpp:48 engines/saga/metaengine.cpp:328
 #: engines/tinsel/metaengine.cpp:258 engines/tsage/metaengine.cpp:190
 #: engines/tucker/metaengine.cpp:221 engines/twine/metaengine.cpp:388
@@ -7994,7 +7994,7 @@ msgstr "Alternar Privacidade de Pistas"
 #: engines/freescape/games/eclipse/eclipse.cpp:330
 #: engines/hopkins/metaengine.cpp:236 engines/hugo/metaengine.cpp:165
 #: engines/lure/metaengine.cpp:191 engines/made/metaengine.cpp:192
-#: engines/mads/metaengine.cpp:343 engines/mohawk/myst.cpp:583
+#: engines/mads/metaengine.cpp:347 engines/mohawk/myst.cpp:583
 #: engines/mohawk/riven.cpp:864 engines/pegasus/pegasus.cpp:2825
 #: engines/petka/metaengine.cpp:198 engines/prince/metaengine.cpp:243
 #: engines/saga/metaengine.cpp:442 engines/sherlock/metaengine.cpp:462
@@ -8076,7 +8076,7 @@ msgstr "Base de Dados de Pistas"
 #: engines/freescape/games/dark/dark.cpp:236
 #: engines/freescape/games/driller/driller.cpp:129
 #: engines/freescape/games/eclipse/eclipse.cpp:340
-#: engines/mads/metaengine.cpp:399 engines/phoenixvr/metaengine.cpp:80
+#: engines/mads/metaengine.cpp:403 engines/phoenixvr/metaengine.cpp:80
 #: engines/saga/metaengine.cpp:437 engines/sherlock/metaengine.cpp:359
 #: engines/sherlock/metaengine.cpp:618 engines/sherlock/metaengine.cpp:852
 #: engines/stark/metaengine.cpp:280 engines/sword1/metaengine.cpp:295
@@ -10798,7 +10798,7 @@ msgid "Repeat last line"
 msgstr "Repetir ??ltima linha"
 
 #: engines/hugo/metaengine.cpp:172 engines/lure/metaengine.cpp:197
-#: engines/mads/metaengine.cpp:349 engines/tsage/metaengine.cpp:247
+#: engines/mads/metaengine.cpp:353 engines/tsage/metaengine.cpp:247
 msgid "Restore game"
 msgstr "Restaurar jogo"
 
@@ -11009,7 +11009,7 @@ msgstr "Mapeamento de teclas do jogo"
 msgid "Direction keymappings"
 msgstr "Mapeamentos de teclas de ??ndice"
 
-#: engines/hypno/metaengine.cpp:316 engines/mads/metaengine.cpp:387
+#: engines/hypno/metaengine.cpp:316 engines/mads/metaengine.cpp:391
 #: engines/tsage/metaengine.cpp:259
 msgid "Show credits"
 msgstr "Exibir cr??ditos"
@@ -11715,39 +11715,39 @@ msgstr "Interface de jogo animada"
 msgid "Naughty game mode"
 msgstr "Modo de jogo impr??prio"
 
-#: engines/mads/metaengine.cpp:314
+#: engines/mads/metaengine.cpp:318
 msgid "Start menu keymappings"
 msgstr "Mapeamento de teclas do menu Iniciar"
 
-#: engines/mads/metaengine.cpp:337
+#: engines/mads/metaengine.cpp:341
 msgid "Open game menu"
 msgstr "Abrir menu do jogo"
 
-#: engines/mads/metaengine.cpp:355
+#: engines/mads/metaengine.cpp:359
 msgid "Scroll bar up"
 msgstr "Rolar barra para cima"
 
-#: engines/mads/metaengine.cpp:361
+#: engines/mads/metaengine.cpp:365
 msgid "Scroll bar down"
 msgstr "Rolar barra para baixo"
 
-#: engines/mads/metaengine.cpp:369
+#: engines/mads/metaengine.cpp:373
 msgid "Start game"
 msgstr "Iniciar jogo"
 
-#: engines/mads/metaengine.cpp:375
+#: engines/mads/metaengine.cpp:379
 msgid "Resume game"
 msgstr "Retomar jogo"
 
-#: engines/mads/metaengine.cpp:381
+#: engines/mads/metaengine.cpp:385
 msgid "Show intro"
 msgstr "Exibir introdu????o"
 
-#: engines/mads/metaengine.cpp:393
+#: engines/mads/metaengine.cpp:397
 msgid "Show quotes"
 msgstr "Exibir cita????es"
 
-#: engines/mads/metaengine.cpp:406
+#: engines/mads/metaengine.cpp:410
 msgid "Restart animation"
 msgstr "Reiniciar anima????o"
 
diff --git a/po/pt_PT.po b/po/pt_PT.po
index f7a8ceacf8c..9c3f9297527 100644
--- a/po/pt_PT.po
+++ b/po/pt_PT.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.10.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-07 22:09+0000\n"
+"POT-Creation-Date: 2026-06-08 11:04+0000\n"
 "PO-Revision-Date: 2022-06-05 14:09+0000\n"
 "Last-Translator: Daniel <supsuper at gmail.com>\n"
 "Language-Team: Portuguese (Portugal) <https://translations.scummvm.org/"
@@ -3871,7 +3871,7 @@ msgstr "Mapeamento padr??o"
 #: engines/hugo/metaengine.cpp:134 engines/illusions/metaengine.cpp:94
 #: engines/kyra/engine/eobcommon.cpp:369 engines/kyra/engine/lol.cpp:505
 #: engines/lure/metaengine.cpp:177 engines/made/metaengine.cpp:137
-#: engines/mads/metaengine.cpp:318 engines/mm/metaengine.cpp:186
+#: engines/mads/metaengine.cpp:322 engines/mm/metaengine.cpp:186
 #: engines/mtropolis/metaengine.cpp:161 engines/nancy/input.cpp:181
 #: engines/parallaction/metaengine.cpp:138 engines/saga/metaengine.cpp:278
 #: engines/sludge/keymapper_tables.h:561 engines/sludge/keymapper_tables.h:580
@@ -3908,7 +3908,7 @@ msgstr "Clique do meio"
 #: engines/hopkins/metaengine.cpp:217 engines/hugo/metaengine.cpp:141
 #: engines/illusions/metaengine.cpp:101 engines/kyra/engine/eobcommon.cpp:370
 #: engines/kyra/engine/lol.cpp:506 engines/lure/metaengine.cpp:183
-#: engines/made/metaengine.cpp:144 engines/mads/metaengine.cpp:324
+#: engines/made/metaengine.cpp:144 engines/mads/metaengine.cpp:328
 #: engines/mm/metaengine.cpp:192 engines/mtropolis/metaengine.cpp:167
 #: engines/nancy/input.cpp:188 engines/parallaction/metaengine.cpp:144
 #: engines/saga/metaengine.cpp:289 engines/sludge/keymapper_tables.h:567
@@ -6038,7 +6038,7 @@ msgstr "Procurar por Atualiza????es..."
 #: engines/hugo/metaengine.cpp:129 engines/hypno/metaengine.cpp:128
 #: engines/illusions/metaengine.cpp:89 engines/lab/metaengine.cpp:149
 #: engines/lure/metaengine.cpp:168 engines/made/metaengine.cpp:132
-#: engines/mads/metaengine.cpp:312 engines/neverhood/metaengine.cpp:185
+#: engines/mads/metaengine.cpp:316 engines/neverhood/metaengine.cpp:185
 #: engines/ngi/metaengine.cpp:115 engines/petka/metaengine.cpp:113
 #: engines/prince/metaengine.cpp:227 engines/private/metaengine.cpp:127
 #: engines/queen/metaengine.cpp:146 engines/sherlock/metaengine.cpp:313
@@ -6601,7 +6601,7 @@ msgstr ""
 #: engines/hypno/metaengine.cpp:267 engines/hypno/metaengine.cpp:311
 #: engines/illusions/metaengine.cpp:90 engines/lab/metaengine.cpp:152
 #: engines/lure/metaengine.cpp:169 engines/made/metaengine.cpp:133
-#: engines/mads/metaengine.cpp:313 engines/neverhood/metaengine.cpp:186
+#: engines/mads/metaengine.cpp:317 engines/neverhood/metaengine.cpp:186
 #: engines/queen/metaengine.cpp:147 engines/saga/metaengine.cpp:268
 #: engines/sludge/keymapper_tables.h:33 engines/stark/metaengine.cpp:185
 #: engines/sword1/metaengine.cpp:267 engines/tinsel/metaengine.cpp:241
@@ -7116,7 +7116,7 @@ msgstr ""
 #: engines/bbvs/metaengine.cpp:111 engines/cruise/metaengine.cpp:223
 #: engines/freescape/movement.cpp:107 engines/got/metaengine.cpp:78
 #: engines/hugo/metaengine.cpp:197 engines/lure/metaengine.cpp:209
-#: engines/mads/metaengine.cpp:330 engines/mm/mm1/metaengine.cpp:42
+#: engines/mads/metaengine.cpp:334 engines/mm/mm1/metaengine.cpp:42
 #: engines/mm/mm1/metaengine.cpp:48 engines/saga/metaengine.cpp:328
 #: engines/tinsel/metaengine.cpp:258 engines/tsage/metaengine.cpp:190
 #: engines/tucker/metaengine.cpp:221 engines/twine/metaengine.cpp:388
@@ -7430,7 +7430,7 @@ msgstr "Ativar/desativar som"
 #: engines/freescape/games/eclipse/eclipse.cpp:330
 #: engines/hopkins/metaengine.cpp:236 engines/hugo/metaengine.cpp:165
 #: engines/lure/metaengine.cpp:191 engines/made/metaengine.cpp:192
-#: engines/mads/metaengine.cpp:343 engines/mohawk/myst.cpp:583
+#: engines/mads/metaengine.cpp:347 engines/mohawk/myst.cpp:583
 #: engines/mohawk/riven.cpp:864 engines/pegasus/pegasus.cpp:2825
 #: engines/petka/metaengine.cpp:198 engines/prince/metaengine.cpp:243
 #: engines/saga/metaengine.cpp:442 engines/sherlock/metaengine.cpp:462
@@ -7506,7 +7506,7 @@ msgstr ""
 #: engines/freescape/games/dark/dark.cpp:236
 #: engines/freescape/games/driller/driller.cpp:129
 #: engines/freescape/games/eclipse/eclipse.cpp:340
-#: engines/mads/metaengine.cpp:399 engines/phoenixvr/metaengine.cpp:80
+#: engines/mads/metaengine.cpp:403 engines/phoenixvr/metaengine.cpp:80
 #: engines/saga/metaengine.cpp:437 engines/sherlock/metaengine.cpp:359
 #: engines/sherlock/metaengine.cpp:618 engines/sherlock/metaengine.cpp:852
 #: engines/stark/metaengine.cpp:280 engines/sword1/metaengine.cpp:295
@@ -10187,7 +10187,7 @@ msgid "Repeat last line"
 msgstr "Nenhuma data guardada"
 
 #: engines/hugo/metaengine.cpp:172 engines/lure/metaengine.cpp:197
-#: engines/mads/metaengine.cpp:349 engines/tsage/metaengine.cpp:247
+#: engines/mads/metaengine.cpp:353 engines/tsage/metaengine.cpp:247
 #, fuzzy
 #| msgid "Restore game:"
 msgid "Restore game"
@@ -10416,7 +10416,7 @@ msgstr "Op????es do Jogo"
 msgid "Direction keymappings"
 msgstr "Op????es do Jogo"
 
-#: engines/hypno/metaengine.cpp:316 engines/mads/metaengine.cpp:387
+#: engines/hypno/metaengine.cpp:316 engines/mads/metaengine.cpp:391
 #: engines/tsage/metaengine.cpp:259
 #, fuzzy
 msgid "Show credits"
@@ -11103,54 +11103,54 @@ msgstr "Interface do jogo animada"
 msgid "Naughty game mode"
 msgstr "Modo de jogo atrevido"
 
-#: engines/mads/metaengine.cpp:314
+#: engines/mads/metaengine.cpp:318
 #, fuzzy
 #| msgid "Game Options"
 msgid "Start menu keymappings"
 msgstr "Op????es do Jogo"
 
-#: engines/mads/metaengine.cpp:337
+#: engines/mads/metaengine.cpp:341
 #, fuzzy
 msgid "Open game menu"
 msgstr "~M~enu principal"
 
-#: engines/mads/metaengine.cpp:355
+#: engines/mads/metaengine.cpp:359
 #, fuzzy
 #| msgid "Scroll up"
 msgid "Scroll bar up"
 msgstr "Deslizar para cima"
 
-#: engines/mads/metaengine.cpp:361
+#: engines/mads/metaengine.cpp:365
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Scroll bar down"
 msgstr "Deslizar para baixo"
 
-#: engines/mads/metaengine.cpp:369
+#: engines/mads/metaengine.cpp:373
 #, fuzzy
 #| msgid "Restore game:"
 msgid "Start game"
 msgstr "Restaurar jogo:"
 
-#: engines/mads/metaengine.cpp:375
+#: engines/mads/metaengine.cpp:379
 #, fuzzy
 #| msgid "Restore game:"
 msgid "Resume game"
 msgstr "Restaurar jogo:"
 
-#: engines/mads/metaengine.cpp:381
+#: engines/mads/metaengine.cpp:385
 #, fuzzy
 #| msgid "Show hints"
 msgid "Show intro"
 msgstr "Mostrar dicas"
 
-#: engines/mads/metaengine.cpp:393
+#: engines/mads/metaengine.cpp:397
 #, fuzzy
 #| msgid "Speech Options"
 msgid "Show quotes"
 msgstr "Op????es de Di??logo"
 
-#: engines/mads/metaengine.cpp:406
+#: engines/mads/metaengine.cpp:410
 #, fuzzy
 #| msgid "Transitions:"
 msgid "Restart animation"
diff --git a/po/ro.po b/po/ro.po
index 5d1d4e3cd07..0f19672a0de 100644
--- a/po/ro.po
+++ b/po/ro.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.10.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-07 22:09+0000\n"
+"POT-Creation-Date: 2026-06-08 11:04+0000\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: Automatically generated\n"
 "Language-Team: none\n"
@@ -3602,7 +3602,7 @@ msgstr ""
 #: engines/hugo/metaengine.cpp:134 engines/illusions/metaengine.cpp:94
 #: engines/kyra/engine/eobcommon.cpp:369 engines/kyra/engine/lol.cpp:505
 #: engines/lure/metaengine.cpp:177 engines/made/metaengine.cpp:137
-#: engines/mads/metaengine.cpp:318 engines/mm/metaengine.cpp:186
+#: engines/mads/metaengine.cpp:322 engines/mm/metaengine.cpp:186
 #: engines/mtropolis/metaengine.cpp:161 engines/nancy/input.cpp:181
 #: engines/parallaction/metaengine.cpp:138 engines/saga/metaengine.cpp:278
 #: engines/sludge/keymapper_tables.h:561 engines/sludge/keymapper_tables.h:580
@@ -3635,7 +3635,7 @@ msgstr ""
 #: engines/hopkins/metaengine.cpp:217 engines/hugo/metaengine.cpp:141
 #: engines/illusions/metaengine.cpp:101 engines/kyra/engine/eobcommon.cpp:370
 #: engines/kyra/engine/lol.cpp:506 engines/lure/metaengine.cpp:183
-#: engines/made/metaengine.cpp:144 engines/mads/metaengine.cpp:324
+#: engines/made/metaengine.cpp:144 engines/mads/metaengine.cpp:328
 #: engines/mm/metaengine.cpp:192 engines/mtropolis/metaengine.cpp:167
 #: engines/nancy/input.cpp:188 engines/parallaction/metaengine.cpp:144
 #: engines/saga/metaengine.cpp:289 engines/sludge/keymapper_tables.h:567
@@ -5634,7 +5634,7 @@ msgstr ""
 #: engines/hugo/metaengine.cpp:129 engines/hypno/metaengine.cpp:128
 #: engines/illusions/metaengine.cpp:89 engines/lab/metaengine.cpp:149
 #: engines/lure/metaengine.cpp:168 engines/made/metaengine.cpp:132
-#: engines/mads/metaengine.cpp:312 engines/neverhood/metaengine.cpp:185
+#: engines/mads/metaengine.cpp:316 engines/neverhood/metaengine.cpp:185
 #: engines/ngi/metaengine.cpp:115 engines/petka/metaengine.cpp:113
 #: engines/prince/metaengine.cpp:227 engines/private/metaengine.cpp:127
 #: engines/queen/metaengine.cpp:146 engines/sherlock/metaengine.cpp:313
@@ -6162,7 +6162,7 @@ msgstr ""
 #: engines/hypno/metaengine.cpp:267 engines/hypno/metaengine.cpp:311
 #: engines/illusions/metaengine.cpp:90 engines/lab/metaengine.cpp:152
 #: engines/lure/metaengine.cpp:169 engines/made/metaengine.cpp:133
-#: engines/mads/metaengine.cpp:313 engines/neverhood/metaengine.cpp:186
+#: engines/mads/metaengine.cpp:317 engines/neverhood/metaengine.cpp:186
 #: engines/queen/metaengine.cpp:147 engines/saga/metaengine.cpp:268
 #: engines/sludge/keymapper_tables.h:33 engines/stark/metaengine.cpp:185
 #: engines/sword1/metaengine.cpp:267 engines/tinsel/metaengine.cpp:241
@@ -6611,7 +6611,7 @@ msgstr ""
 #: engines/bbvs/metaengine.cpp:111 engines/cruise/metaengine.cpp:223
 #: engines/freescape/movement.cpp:107 engines/got/metaengine.cpp:78
 #: engines/hugo/metaengine.cpp:197 engines/lure/metaengine.cpp:209
-#: engines/mads/metaengine.cpp:330 engines/mm/mm1/metaengine.cpp:42
+#: engines/mads/metaengine.cpp:334 engines/mm/mm1/metaengine.cpp:42
 #: engines/mm/mm1/metaengine.cpp:48 engines/saga/metaengine.cpp:328
 #: engines/tinsel/metaengine.cpp:258 engines/tsage/metaengine.cpp:190
 #: engines/tucker/metaengine.cpp:221 engines/twine/metaengine.cpp:388
@@ -6898,7 +6898,7 @@ msgstr ""
 #: engines/freescape/games/eclipse/eclipse.cpp:330
 #: engines/hopkins/metaengine.cpp:236 engines/hugo/metaengine.cpp:165
 #: engines/lure/metaengine.cpp:191 engines/made/metaengine.cpp:192
-#: engines/mads/metaengine.cpp:343 engines/mohawk/myst.cpp:583
+#: engines/mads/metaengine.cpp:347 engines/mohawk/myst.cpp:583
 #: engines/mohawk/riven.cpp:864 engines/pegasus/pegasus.cpp:2825
 #: engines/petka/metaengine.cpp:198 engines/prince/metaengine.cpp:243
 #: engines/saga/metaengine.cpp:442 engines/sherlock/metaengine.cpp:462
@@ -6974,7 +6974,7 @@ msgstr ""
 #: engines/freescape/games/dark/dark.cpp:236
 #: engines/freescape/games/driller/driller.cpp:129
 #: engines/freescape/games/eclipse/eclipse.cpp:340
-#: engines/mads/metaengine.cpp:399 engines/phoenixvr/metaengine.cpp:80
+#: engines/mads/metaengine.cpp:403 engines/phoenixvr/metaengine.cpp:80
 #: engines/saga/metaengine.cpp:437 engines/sherlock/metaengine.cpp:359
 #: engines/sherlock/metaengine.cpp:618 engines/sherlock/metaengine.cpp:852
 #: engines/stark/metaengine.cpp:280 engines/sword1/metaengine.cpp:295
@@ -9238,7 +9238,7 @@ msgid "Repeat last line"
 msgstr ""
 
 #: engines/hugo/metaengine.cpp:172 engines/lure/metaengine.cpp:197
-#: engines/mads/metaengine.cpp:349 engines/tsage/metaengine.cpp:247
+#: engines/mads/metaengine.cpp:353 engines/tsage/metaengine.cpp:247
 msgid "Restore game"
 msgstr ""
 
@@ -9427,7 +9427,7 @@ msgstr ""
 msgid "Direction keymappings"
 msgstr ""
 
-#: engines/hypno/metaengine.cpp:316 engines/mads/metaengine.cpp:387
+#: engines/hypno/metaengine.cpp:316 engines/mads/metaengine.cpp:391
 #: engines/tsage/metaengine.cpp:259
 msgid "Show credits"
 msgstr ""
@@ -10013,39 +10013,39 @@ msgstr ""
 msgid "Naughty game mode"
 msgstr ""
 
-#: engines/mads/metaengine.cpp:314
+#: engines/mads/metaengine.cpp:318
 msgid "Start menu keymappings"
 msgstr ""
 
-#: engines/mads/metaengine.cpp:337
+#: engines/mads/metaengine.cpp:341
 msgid "Open game menu"
 msgstr ""
 
-#: engines/mads/metaengine.cpp:355
+#: engines/mads/metaengine.cpp:359
 msgid "Scroll bar up"
 msgstr ""
 
-#: engines/mads/metaengine.cpp:361
+#: engines/mads/metaengine.cpp:365
 msgid "Scroll bar down"
 msgstr ""
 
-#: engines/mads/metaengine.cpp:369
+#: engines/mads/metaengine.cpp:373
 msgid "Start game"
 msgstr ""
 
-#: engines/mads/metaengine.cpp:375
+#: engines/mads/metaengine.cpp:379
 msgid "Resume game"
 msgstr ""
 
-#: engines/mads/metaengine.cpp:381
+#: engines/mads/metaengine.cpp:385
 msgid "Show intro"
 msgstr ""
 
-#: engines/mads/metaengine.cpp:393
+#: engines/mads/metaengine.cpp:397
 msgid "Show quotes"
 msgstr ""
 
-#: engines/mads/metaengine.cpp:406
+#: engines/mads/metaengine.cpp:410
 msgid "Restart animation"
 msgstr ""
 
diff --git a/po/ru_RU.po b/po/ru_RU.po
index 21bf46ad328..280efc225b8 100644
--- a/po/ru_RU.po
+++ b/po/ru_RU.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.8.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-07 22:09+0000\n"
+"POT-Creation-Date: 2026-06-08 11:04+0000\n"
 "PO-Revision-Date: 2026-05-06 08:54+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Russian <https://translations.scummvm.org/projects/scummvm/"
@@ -3910,7 +3910,7 @@ msgstr "?????????????????? ???????????? ???????? ???? ??????????????????"
 #: engines/hugo/metaengine.cpp:134 engines/illusions/metaengine.cpp:94
 #: engines/kyra/engine/eobcommon.cpp:369 engines/kyra/engine/lol.cpp:505
 #: engines/lure/metaengine.cpp:177 engines/made/metaengine.cpp:137
-#: engines/mads/metaengine.cpp:318 engines/mm/metaengine.cpp:186
+#: engines/mads/metaengine.cpp:322 engines/mm/metaengine.cpp:186
 #: engines/mtropolis/metaengine.cpp:161 engines/nancy/input.cpp:181
 #: engines/parallaction/metaengine.cpp:138 engines/saga/metaengine.cpp:278
 #: engines/sludge/keymapper_tables.h:561 engines/sludge/keymapper_tables.h:580
@@ -3943,7 +3943,7 @@ msgstr "?????????????? ????????????"
 #: engines/hopkins/metaengine.cpp:217 engines/hugo/metaengine.cpp:141
 #: engines/illusions/metaengine.cpp:101 engines/kyra/engine/eobcommon.cpp:370
 #: engines/kyra/engine/lol.cpp:506 engines/lure/metaengine.cpp:183
-#: engines/made/metaengine.cpp:144 engines/mads/metaengine.cpp:324
+#: engines/made/metaengine.cpp:144 engines/mads/metaengine.cpp:328
 #: engines/mm/metaengine.cpp:192 engines/mtropolis/metaengine.cpp:167
 #: engines/nancy/input.cpp:188 engines/parallaction/metaengine.cpp:144
 #: engines/saga/metaengine.cpp:289 engines/sludge/keymapper_tables.h:567
@@ -6309,7 +6309,7 @@ msgstr "?????????????????? ????????????????????..."
 #: engines/hugo/metaengine.cpp:129 engines/hypno/metaengine.cpp:128
 #: engines/illusions/metaengine.cpp:89 engines/lab/metaengine.cpp:149
 #: engines/lure/metaengine.cpp:168 engines/made/metaengine.cpp:132
-#: engines/mads/metaengine.cpp:312 engines/neverhood/metaengine.cpp:185
+#: engines/mads/metaengine.cpp:316 engines/neverhood/metaengine.cpp:185
 #: engines/ngi/metaengine.cpp:115 engines/petka/metaengine.cpp:113
 #: engines/prince/metaengine.cpp:227 engines/private/metaengine.cpp:127
 #: engines/queen/metaengine.cpp:146 engines/sherlock/metaengine.cpp:313
@@ -6866,7 +6866,7 @@ msgstr "?????????????? AGOS"
 #: engines/hypno/metaengine.cpp:267 engines/hypno/metaengine.cpp:311
 #: engines/illusions/metaengine.cpp:90 engines/lab/metaengine.cpp:152
 #: engines/lure/metaengine.cpp:169 engines/made/metaengine.cpp:133
-#: engines/mads/metaengine.cpp:313 engines/neverhood/metaengine.cpp:186
+#: engines/mads/metaengine.cpp:317 engines/neverhood/metaengine.cpp:186
 #: engines/queen/metaengine.cpp:147 engines/saga/metaengine.cpp:268
 #: engines/sludge/keymapper_tables.h:33 engines/stark/metaengine.cpp:185
 #: engines/sword1/metaengine.cpp:267 engines/tinsel/metaengine.cpp:241
@@ -7366,7 +7366,7 @@ msgstr "?????? ???????? ?????????????? 714"
 #: engines/bbvs/metaengine.cpp:111 engines/cruise/metaengine.cpp:223
 #: engines/freescape/movement.cpp:107 engines/got/metaengine.cpp:78
 #: engines/hugo/metaengine.cpp:197 engines/lure/metaengine.cpp:209
-#: engines/mads/metaengine.cpp:330 engines/mm/mm1/metaengine.cpp:42
+#: engines/mads/metaengine.cpp:334 engines/mm/mm1/metaengine.cpp:42
 #: engines/mm/mm1/metaengine.cpp:48 engines/saga/metaengine.cpp:328
 #: engines/tinsel/metaengine.cpp:258 engines/tsage/metaengine.cpp:190
 #: engines/tucker/metaengine.cpp:221 engines/twine/metaengine.cpp:388
@@ -7669,7 +7669,7 @@ msgstr "?????????????????????? ?????????????????????? ?????? ????????"
 #: engines/freescape/games/eclipse/eclipse.cpp:330
 #: engines/hopkins/metaengine.cpp:236 engines/hugo/metaengine.cpp:165
 #: engines/lure/metaengine.cpp:191 engines/made/metaengine.cpp:192
-#: engines/mads/metaengine.cpp:343 engines/mohawk/myst.cpp:583
+#: engines/mads/metaengine.cpp:347 engines/mohawk/myst.cpp:583
 #: engines/mohawk/riven.cpp:864 engines/pegasus/pegasus.cpp:2825
 #: engines/petka/metaengine.cpp:198 engines/prince/metaengine.cpp:243
 #: engines/saga/metaengine.cpp:442 engines/sherlock/metaengine.cpp:462
@@ -7745,7 +7745,7 @@ msgstr "???????? ????????"
 #: engines/freescape/games/dark/dark.cpp:236
 #: engines/freescape/games/driller/driller.cpp:129
 #: engines/freescape/games/eclipse/eclipse.cpp:340
-#: engines/mads/metaengine.cpp:399 engines/phoenixvr/metaengine.cpp:80
+#: engines/mads/metaengine.cpp:403 engines/phoenixvr/metaengine.cpp:80
 #: engines/saga/metaengine.cpp:437 engines/sherlock/metaengine.cpp:359
 #: engines/sherlock/metaengine.cpp:618 engines/sherlock/metaengine.cpp:852
 #: engines/stark/metaengine.cpp:280 engines/sword1/metaengine.cpp:295
@@ -10105,7 +10105,7 @@ msgid "Repeat last line"
 msgstr "?????????????????? ?????????????????? ??????????"
 
 #: engines/hugo/metaengine.cpp:172 engines/lure/metaengine.cpp:197
-#: engines/mads/metaengine.cpp:349 engines/tsage/metaengine.cpp:247
+#: engines/mads/metaengine.cpp:353 engines/tsage/metaengine.cpp:247
 msgid "Restore game"
 msgstr "???????????????????????? ????????"
 
@@ -10295,7 +10295,7 @@ msgstr "?????????????????? ???????????? ??????????"
 msgid "Direction keymappings"
 msgstr "?????????????????? ???????????? ??????????????????????"
 
-#: engines/hypno/metaengine.cpp:316 engines/mads/metaengine.cpp:387
+#: engines/hypno/metaengine.cpp:316 engines/mads/metaengine.cpp:391
 #: engines/tsage/metaengine.cpp:259
 msgid "Show credits"
 msgstr "???????????????? ??????????????"
@@ -10943,39 +10943,39 @@ msgstr "?????????????????????????? ?????????????????? ????????"
 msgid "Naughty game mode"
 msgstr "?????????????????????? ?????????? ????????"
 
-#: engines/mads/metaengine.cpp:314
+#: engines/mads/metaengine.cpp:318
 msgid "Start menu keymappings"
 msgstr "?????????????????? ???????????? ???????????????????? ????????"
 
-#: engines/mads/metaengine.cpp:337
+#: engines/mads/metaengine.cpp:341
 msgid "Open game menu"
 msgstr "?????????????? ?????????????? ????????"
 
-#: engines/mads/metaengine.cpp:355
+#: engines/mads/metaengine.cpp:359
 msgid "Scroll bar up"
 msgstr "?????????????????? ??????????"
 
-#: engines/mads/metaengine.cpp:361
+#: engines/mads/metaengine.cpp:365
 msgid "Scroll bar down"
 msgstr "?????????????????? ????????"
 
-#: engines/mads/metaengine.cpp:369
+#: engines/mads/metaengine.cpp:373
 msgid "Start game"
 msgstr "???????????? ????????"
 
-#: engines/mads/metaengine.cpp:375
+#: engines/mads/metaengine.cpp:379
 msgid "Resume game"
 msgstr "???????????????????? ????????"
 
-#: engines/mads/metaengine.cpp:381
+#: engines/mads/metaengine.cpp:385
 msgid "Show intro"
 msgstr "???????????????? ????????????????????"
 
-#: engines/mads/metaengine.cpp:393
+#: engines/mads/metaengine.cpp:397
 msgid "Show quotes"
 msgstr "???????????????? ????????????"
 
-#: engines/mads/metaengine.cpp:406
+#: engines/mads/metaengine.cpp:410
 msgid "Restart animation"
 msgstr "?????????????????????????? ????????????????"
 
diff --git a/po/sv_SE.po b/po/sv_SE.po
index ef5beab3f7e..4548282e8c0 100644
--- a/po/sv_SE.po
+++ b/po/sv_SE.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.5.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-07 22:09+0000\n"
+"POT-Creation-Date: 2026-06-08 11:04+0000\n"
 "PO-Revision-Date: 2026-04-19 13:22+0000\n"
 "Last-Translator: Daniel Nylander <po at danielnylander.se>\n"
 "Language-Team: Swedish <https://translations.scummvm.org/projects/scummvm/"
@@ -3900,7 +3900,7 @@ msgstr "Standardinst??llningar f??r tangentbordskommandon i spelet"
 #: engines/hugo/metaengine.cpp:134 engines/illusions/metaengine.cpp:94
 #: engines/kyra/engine/eobcommon.cpp:369 engines/kyra/engine/lol.cpp:505
 #: engines/lure/metaengine.cpp:177 engines/made/metaengine.cpp:137
-#: engines/mads/metaengine.cpp:318 engines/mm/metaengine.cpp:186
+#: engines/mads/metaengine.cpp:322 engines/mm/metaengine.cpp:186
 #: engines/mtropolis/metaengine.cpp:161 engines/nancy/input.cpp:181
 #: engines/parallaction/metaengine.cpp:138 engines/saga/metaengine.cpp:278
 #: engines/sludge/keymapper_tables.h:561 engines/sludge/keymapper_tables.h:580
@@ -3933,7 +3933,7 @@ msgstr "Mittenklick"
 #: engines/hopkins/metaengine.cpp:217 engines/hugo/metaengine.cpp:141
 #: engines/illusions/metaengine.cpp:101 engines/kyra/engine/eobcommon.cpp:370
 #: engines/kyra/engine/lol.cpp:506 engines/lure/metaengine.cpp:183
-#: engines/made/metaengine.cpp:144 engines/mads/metaengine.cpp:324
+#: engines/made/metaengine.cpp:144 engines/mads/metaengine.cpp:328
 #: engines/mm/metaengine.cpp:192 engines/mtropolis/metaengine.cpp:167
 #: engines/nancy/input.cpp:188 engines/parallaction/metaengine.cpp:144
 #: engines/saga/metaengine.cpp:289 engines/sludge/keymapper_tables.h:567
@@ -6286,7 +6286,7 @@ msgstr "Leta efter uppdateringar..."
 #: engines/hugo/metaengine.cpp:129 engines/hypno/metaengine.cpp:128
 #: engines/illusions/metaengine.cpp:89 engines/lab/metaengine.cpp:149
 #: engines/lure/metaengine.cpp:168 engines/made/metaengine.cpp:132
-#: engines/mads/metaengine.cpp:312 engines/neverhood/metaengine.cpp:185
+#: engines/mads/metaengine.cpp:316 engines/neverhood/metaengine.cpp:185
 #: engines/ngi/metaengine.cpp:115 engines/petka/metaengine.cpp:113
 #: engines/prince/metaengine.cpp:227 engines/private/metaengine.cpp:127
 #: engines/queen/metaengine.cpp:146 engines/sherlock/metaengine.cpp:313
@@ -6843,7 +6843,7 @@ msgstr "Huvudsakliga f??r AGOS"
 #: engines/hypno/metaengine.cpp:267 engines/hypno/metaengine.cpp:311
 #: engines/illusions/metaengine.cpp:90 engines/lab/metaengine.cpp:152
 #: engines/lure/metaengine.cpp:169 engines/made/metaengine.cpp:133
-#: engines/mads/metaengine.cpp:313 engines/neverhood/metaengine.cpp:186
+#: engines/mads/metaengine.cpp:317 engines/neverhood/metaengine.cpp:186
 #: engines/queen/metaengine.cpp:147 engines/saga/metaengine.cpp:268
 #: engines/sludge/keymapper_tables.h:33 engines/stark/metaengine.cpp:185
 #: engines/sword1/metaengine.cpp:267 engines/tinsel/metaengine.cpp:241
@@ -7339,7 +7339,7 @@ msgstr "Soldier 714 ??gonfusk"
 #: engines/bbvs/metaengine.cpp:111 engines/cruise/metaengine.cpp:223
 #: engines/freescape/movement.cpp:107 engines/got/metaengine.cpp:78
 #: engines/hugo/metaengine.cpp:197 engines/lure/metaengine.cpp:209
-#: engines/mads/metaengine.cpp:330 engines/mm/mm1/metaengine.cpp:42
+#: engines/mads/metaengine.cpp:334 engines/mm/mm1/metaengine.cpp:42
 #: engines/mm/mm1/metaengine.cpp:48 engines/saga/metaengine.cpp:328
 #: engines/tinsel/metaengine.cpp:258 engines/tsage/metaengine.cpp:190
 #: engines/tucker/metaengine.cpp:221 engines/twine/metaengine.cpp:388
@@ -7644,7 +7644,7 @@ msgstr "V??xla sekretess f??r ledtr??dar"
 #: engines/freescape/games/eclipse/eclipse.cpp:330
 #: engines/hopkins/metaengine.cpp:236 engines/hugo/metaengine.cpp:165
 #: engines/lure/metaengine.cpp:191 engines/made/metaengine.cpp:192
-#: engines/mads/metaengine.cpp:343 engines/mohawk/myst.cpp:583
+#: engines/mads/metaengine.cpp:347 engines/mohawk/myst.cpp:583
 #: engines/mohawk/riven.cpp:864 engines/pegasus/pegasus.cpp:2825
 #: engines/petka/metaengine.cpp:198 engines/prince/metaengine.cpp:243
 #: engines/saga/metaengine.cpp:442 engines/sherlock/metaengine.cpp:462
@@ -7720,7 +7720,7 @@ msgstr "Databas f??r ledtr??dar"
 #: engines/freescape/games/dark/dark.cpp:236
 #: engines/freescape/games/driller/driller.cpp:129
 #: engines/freescape/games/eclipse/eclipse.cpp:340
-#: engines/mads/metaengine.cpp:399 engines/phoenixvr/metaengine.cpp:80
+#: engines/mads/metaengine.cpp:403 engines/phoenixvr/metaengine.cpp:80
 #: engines/saga/metaengine.cpp:437 engines/sherlock/metaengine.cpp:359
 #: engines/sherlock/metaengine.cpp:618 engines/sherlock/metaengine.cpp:852
 #: engines/stark/metaengine.cpp:280 engines/sword1/metaengine.cpp:295
@@ -10146,7 +10146,7 @@ msgid "Repeat last line"
 msgstr "Upprepa senaste rad"
 
 #: engines/hugo/metaengine.cpp:172 engines/lure/metaengine.cpp:197
-#: engines/mads/metaengine.cpp:349 engines/tsage/metaengine.cpp:247
+#: engines/mads/metaengine.cpp:353 engines/tsage/metaengine.cpp:247
 msgid "Restore game"
 msgstr "??terst??ll spel"
 
@@ -10338,7 +10338,7 @@ msgstr "Kortkommandon f??r paus"
 msgid "Direction keymappings"
 msgstr "Kortkommandon f??r riktning"
 
-#: engines/hypno/metaengine.cpp:316 engines/mads/metaengine.cpp:387
+#: engines/hypno/metaengine.cpp:316 engines/mads/metaengine.cpp:391
 #: engines/tsage/metaengine.cpp:259
 msgid "Show credits"
 msgstr "Visa medverkande"
@@ -10985,39 +10985,39 @@ msgstr "Animerat spelgr??nssnitt"
 msgid "Naughty game mode"
 msgstr "\"Busigt\" spell??ge"
 
-#: engines/mads/metaengine.cpp:314
+#: engines/mads/metaengine.cpp:318
 msgid "Start menu keymappings"
 msgstr "Kortkommandon f??r startmeny"
 
-#: engines/mads/metaengine.cpp:337
+#: engines/mads/metaengine.cpp:341
 msgid "Open game menu"
 msgstr "??ppna spelmenyn"
 
-#: engines/mads/metaengine.cpp:355
+#: engines/mads/metaengine.cpp:359
 msgid "Scroll bar up"
 msgstr "Rullningslist upp"
 
-#: engines/mads/metaengine.cpp:361
+#: engines/mads/metaengine.cpp:365
 msgid "Scroll bar down"
 msgstr "Rullningslist ner"
 
-#: engines/mads/metaengine.cpp:369
+#: engines/mads/metaengine.cpp:373
 msgid "Start game"
 msgstr "Starta spelet"
 
-#: engines/mads/metaengine.cpp:375
+#: engines/mads/metaengine.cpp:379
 msgid "Resume game"
 msgstr "??teruppta spelet"
 
-#: engines/mads/metaengine.cpp:381
+#: engines/mads/metaengine.cpp:385
 msgid "Show intro"
 msgstr "Visa intro"
 
-#: engines/mads/metaengine.cpp:393
+#: engines/mads/metaengine.cpp:397
 msgid "Show quotes"
 msgstr "Visa citat"
 
-#: engines/mads/metaengine.cpp:406
+#: engines/mads/metaengine.cpp:410
 msgid "Restart animation"
 msgstr "Starta om animering"
 
diff --git a/po/tr.po b/po/tr.po
index c743e42b1d9..b710adbe0b6 100644
--- a/po/tr.po
+++ b/po/tr.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-07 22:09+0000\n"
+"POT-Creation-Date: 2026-06-08 11:04+0000\n"
 "PO-Revision-Date: 2024-10-10 12:33+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Turkish <https://translations.scummvm.org/projects/scummvm/"
@@ -3847,7 +3847,7 @@ msgstr "A~y~arlar..."
 #: engines/hugo/metaengine.cpp:134 engines/illusions/metaengine.cpp:94
 #: engines/kyra/engine/eobcommon.cpp:369 engines/kyra/engine/lol.cpp:505
 #: engines/lure/metaengine.cpp:177 engines/made/metaengine.cpp:137
-#: engines/mads/metaengine.cpp:318 engines/mm/metaengine.cpp:186
+#: engines/mads/metaengine.cpp:322 engines/mm/metaengine.cpp:186
 #: engines/mtropolis/metaengine.cpp:161 engines/nancy/input.cpp:181
 #: engines/parallaction/metaengine.cpp:138 engines/saga/metaengine.cpp:278
 #: engines/sludge/keymapper_tables.h:561 engines/sludge/keymapper_tables.h:580
@@ -3882,7 +3882,7 @@ msgstr "Sa??"
 #: engines/hopkins/metaengine.cpp:217 engines/hugo/metaengine.cpp:141
 #: engines/illusions/metaengine.cpp:101 engines/kyra/engine/eobcommon.cpp:370
 #: engines/kyra/engine/lol.cpp:506 engines/lure/metaengine.cpp:183
-#: engines/made/metaengine.cpp:144 engines/mads/metaengine.cpp:324
+#: engines/made/metaengine.cpp:144 engines/mads/metaengine.cpp:328
 #: engines/mm/metaengine.cpp:192 engines/mtropolis/metaengine.cpp:167
 #: engines/nancy/input.cpp:188 engines/parallaction/metaengine.cpp:144
 #: engines/saga/metaengine.cpp:289 engines/sludge/keymapper_tables.h:567
@@ -5964,7 +5964,7 @@ msgstr ""
 #: engines/hugo/metaengine.cpp:129 engines/hypno/metaengine.cpp:128
 #: engines/illusions/metaengine.cpp:89 engines/lab/metaengine.cpp:149
 #: engines/lure/metaengine.cpp:168 engines/made/metaengine.cpp:132
-#: engines/mads/metaengine.cpp:312 engines/neverhood/metaengine.cpp:185
+#: engines/mads/metaengine.cpp:316 engines/neverhood/metaengine.cpp:185
 #: engines/ngi/metaengine.cpp:115 engines/petka/metaengine.cpp:113
 #: engines/prince/metaengine.cpp:227 engines/private/metaengine.cpp:127
 #: engines/queen/metaengine.cpp:146 engines/sherlock/metaengine.cpp:313
@@ -6512,7 +6512,7 @@ msgstr ""
 #: engines/hypno/metaengine.cpp:267 engines/hypno/metaengine.cpp:311
 #: engines/illusions/metaengine.cpp:90 engines/lab/metaengine.cpp:152
 #: engines/lure/metaengine.cpp:169 engines/made/metaengine.cpp:133
-#: engines/mads/metaengine.cpp:313 engines/neverhood/metaengine.cpp:186
+#: engines/mads/metaengine.cpp:317 engines/neverhood/metaengine.cpp:186
 #: engines/queen/metaengine.cpp:147 engines/saga/metaengine.cpp:268
 #: engines/sludge/keymapper_tables.h:33 engines/stark/metaengine.cpp:185
 #: engines/sword1/metaengine.cpp:267 engines/tinsel/metaengine.cpp:241
@@ -7001,7 +7001,7 @@ msgstr ""
 #: engines/bbvs/metaengine.cpp:111 engines/cruise/metaengine.cpp:223
 #: engines/freescape/movement.cpp:107 engines/got/metaengine.cpp:78
 #: engines/hugo/metaengine.cpp:197 engines/lure/metaengine.cpp:209
-#: engines/mads/metaengine.cpp:330 engines/mm/mm1/metaengine.cpp:42
+#: engines/mads/metaengine.cpp:334 engines/mm/mm1/metaengine.cpp:42
 #: engines/mm/mm1/metaengine.cpp:48 engines/saga/metaengine.cpp:328
 #: engines/tinsel/metaengine.cpp:258 engines/tsage/metaengine.cpp:190
 #: engines/tucker/metaengine.cpp:221 engines/twine/metaengine.cpp:388
@@ -7302,7 +7302,7 @@ msgstr ""
 #: engines/freescape/games/eclipse/eclipse.cpp:330
 #: engines/hopkins/metaengine.cpp:236 engines/hugo/metaengine.cpp:165
 #: engines/lure/metaengine.cpp:191 engines/made/metaengine.cpp:192
-#: engines/mads/metaengine.cpp:343 engines/mohawk/myst.cpp:583
+#: engines/mads/metaengine.cpp:347 engines/mohawk/myst.cpp:583
 #: engines/mohawk/riven.cpp:864 engines/pegasus/pegasus.cpp:2825
 #: engines/petka/metaengine.cpp:198 engines/prince/metaengine.cpp:243
 #: engines/saga/metaengine.cpp:442 engines/sherlock/metaengine.cpp:462
@@ -7380,7 +7380,7 @@ msgstr ""
 #: engines/freescape/games/dark/dark.cpp:236
 #: engines/freescape/games/driller/driller.cpp:129
 #: engines/freescape/games/eclipse/eclipse.cpp:340
-#: engines/mads/metaengine.cpp:399 engines/phoenixvr/metaengine.cpp:80
+#: engines/mads/metaengine.cpp:403 engines/phoenixvr/metaengine.cpp:80
 #: engines/saga/metaengine.cpp:437 engines/sherlock/metaengine.cpp:359
 #: engines/sherlock/metaengine.cpp:618 engines/sherlock/metaengine.cpp:852
 #: engines/stark/metaengine.cpp:280 engines/sword1/metaengine.cpp:295
@@ -9877,7 +9877,7 @@ msgid "Repeat last line"
 msgstr "Tarih kaydedilmemi??"
 
 #: engines/hugo/metaengine.cpp:172 engines/lure/metaengine.cpp:197
-#: engines/mads/metaengine.cpp:349 engines/tsage/metaengine.cpp:247
+#: engines/mads/metaengine.cpp:353 engines/tsage/metaengine.cpp:247
 #, fuzzy
 msgid "Restore game"
 msgstr "Oyun y??kle:"
@@ -10090,7 +10090,7 @@ msgstr "A~y~arlar..."
 msgid "Direction keymappings"
 msgstr "A~y~arlar..."
 
-#: engines/hypno/metaengine.cpp:316 engines/mads/metaengine.cpp:387
+#: engines/hypno/metaengine.cpp:316 engines/mads/metaengine.cpp:391
 #: engines/tsage/metaengine.cpp:259
 #, fuzzy
 msgid "Show credits"
@@ -10726,46 +10726,46 @@ msgstr ""
 msgid "Naughty game mode"
 msgstr ""
 
-#: engines/mads/metaengine.cpp:314
+#: engines/mads/metaengine.cpp:318
 #, fuzzy
 msgid "Start menu keymappings"
 msgstr "A~y~arlar..."
 
-#: engines/mads/metaengine.cpp:337
+#: engines/mads/metaengine.cpp:341
 #, fuzzy
 msgid "Open game menu"
 msgstr "Oyun"
 
-#: engines/mads/metaengine.cpp:355
+#: engines/mads/metaengine.cpp:359
 msgid "Scroll bar up"
 msgstr ""
 
-#: engines/mads/metaengine.cpp:361
+#: engines/mads/metaengine.cpp:365
 #, fuzzy
 msgid "Scroll bar down"
 msgstr "A??a????"
 
-#: engines/mads/metaengine.cpp:369
+#: engines/mads/metaengine.cpp:373
 #, fuzzy
 msgid "Start game"
 msgstr "Oyun y??kle:"
 
-#: engines/mads/metaengine.cpp:375
+#: engines/mads/metaengine.cpp:379
 #, fuzzy
 msgid "Resume game"
 msgstr "Oyun y??kle:"
 
-#: engines/mads/metaengine.cpp:381
+#: engines/mads/metaengine.cpp:385
 #, fuzzy
 msgid "Show intro"
 msgstr "Gizli dosyalar?? g??ster"
 
-#: engines/mads/metaengine.cpp:393
+#: engines/mads/metaengine.cpp:397
 #, fuzzy
 msgid "Show quotes"
 msgstr "A~y~arlar..."
 
-#: engines/mads/metaengine.cpp:406
+#: engines/mads/metaengine.cpp:410
 #, fuzzy
 msgid "Restart animation"
 msgstr "Oyun y??kle:"
diff --git a/po/uk_UA.po b/po/uk_UA.po
index f2ef829268a..f0412b5fb8f 100644
--- a/po/uk_UA.po
+++ b/po/uk_UA.po
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.9.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-07 22:09+0000\n"
+"POT-Creation-Date: 2026-06-08 11:04+0000\n"
 "PO-Revision-Date: 2026-05-06 08:54+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Ukrainian <https://translations.scummvm.org/projects/scummvm/"
@@ -3904,7 +3904,7 @@ msgstr "???????? ???????????? ?????? ???? ??????????????????????"
 #: engines/hugo/metaengine.cpp:134 engines/illusions/metaengine.cpp:94
 #: engines/kyra/engine/eobcommon.cpp:369 engines/kyra/engine/lol.cpp:505
 #: engines/lure/metaengine.cpp:177 engines/made/metaengine.cpp:137
-#: engines/mads/metaengine.cpp:318 engines/mm/metaengine.cpp:186
+#: engines/mads/metaengine.cpp:322 engines/mm/metaengine.cpp:186
 #: engines/mtropolis/metaengine.cpp:161 engines/nancy/input.cpp:181
 #: engines/parallaction/metaengine.cpp:138 engines/saga/metaengine.cpp:278
 #: engines/sludge/keymapper_tables.h:561 engines/sludge/keymapper_tables.h:580
@@ -3937,7 +3937,7 @@ msgstr "???????????????? ????????"
 #: engines/hopkins/metaengine.cpp:217 engines/hugo/metaengine.cpp:141
 #: engines/illusions/metaengine.cpp:101 engines/kyra/engine/eobcommon.cpp:370
 #: engines/kyra/engine/lol.cpp:506 engines/lure/metaengine.cpp:183
-#: engines/made/metaengine.cpp:144 engines/mads/metaengine.cpp:324
+#: engines/made/metaengine.cpp:144 engines/mads/metaengine.cpp:328
 #: engines/mm/metaengine.cpp:192 engines/mtropolis/metaengine.cpp:167
 #: engines/nancy/input.cpp:188 engines/parallaction/metaengine.cpp:144
 #: engines/saga/metaengine.cpp:289 engines/sludge/keymapper_tables.h:567
@@ -6295,7 +6295,7 @@ msgstr "???????????????????? ??????????????????..."
 #: engines/hugo/metaengine.cpp:129 engines/hypno/metaengine.cpp:128
 #: engines/illusions/metaengine.cpp:89 engines/lab/metaengine.cpp:149
 #: engines/lure/metaengine.cpp:168 engines/made/metaengine.cpp:132
-#: engines/mads/metaengine.cpp:312 engines/neverhood/metaengine.cpp:185
+#: engines/mads/metaengine.cpp:316 engines/neverhood/metaengine.cpp:185
 #: engines/ngi/metaengine.cpp:115 engines/petka/metaengine.cpp:113
 #: engines/prince/metaengine.cpp:227 engines/private/metaengine.cpp:127
 #: engines/queen/metaengine.cpp:146 engines/sherlock/metaengine.cpp:313
@@ -6853,7 +6853,7 @@ msgstr "?????????????? AGOS"
 #: engines/hypno/metaengine.cpp:267 engines/hypno/metaengine.cpp:311
 #: engines/illusions/metaengine.cpp:90 engines/lab/metaengine.cpp:152
 #: engines/lure/metaengine.cpp:169 engines/made/metaengine.cpp:133
-#: engines/mads/metaengine.cpp:313 engines/neverhood/metaengine.cpp:186
+#: engines/mads/metaengine.cpp:317 engines/neverhood/metaengine.cpp:186
 #: engines/queen/metaengine.cpp:147 engines/saga/metaengine.cpp:268
 #: engines/sludge/keymapper_tables.h:33 engines/stark/metaengine.cpp:185
 #: engines/sword1/metaengine.cpp:267 engines/tinsel/metaengine.cpp:241
@@ -7347,7 +7347,7 @@ msgstr "?????? ?????? ?????? Soldier 714"
 #: engines/bbvs/metaengine.cpp:111 engines/cruise/metaengine.cpp:223
 #: engines/freescape/movement.cpp:107 engines/got/metaengine.cpp:78
 #: engines/hugo/metaengine.cpp:197 engines/lure/metaengine.cpp:209
-#: engines/mads/metaengine.cpp:330 engines/mm/mm1/metaengine.cpp:42
+#: engines/mads/metaengine.cpp:334 engines/mm/mm1/metaengine.cpp:42
 #: engines/mm/mm1/metaengine.cpp:48 engines/saga/metaengine.cpp:328
 #: engines/tinsel/metaengine.cpp:258 engines/tsage/metaengine.cpp:190
 #: engines/tucker/metaengine.cpp:221 engines/twine/metaengine.cpp:388
@@ -7649,7 +7649,7 @@ msgstr "???????????????????? ?????????????????????? ????????????????"
 #: engines/freescape/games/eclipse/eclipse.cpp:330
 #: engines/hopkins/metaengine.cpp:236 engines/hugo/metaengine.cpp:165
 #: engines/lure/metaengine.cpp:191 engines/made/metaengine.cpp:192
-#: engines/mads/metaengine.cpp:343 engines/mohawk/myst.cpp:583
+#: engines/mads/metaengine.cpp:347 engines/mohawk/myst.cpp:583
 #: engines/mohawk/riven.cpp:864 engines/pegasus/pegasus.cpp:2825
 #: engines/petka/metaengine.cpp:198 engines/prince/metaengine.cpp:243
 #: engines/saga/metaengine.cpp:442 engines/sherlock/metaengine.cpp:462
@@ -7725,7 +7725,7 @@ msgstr "???????? ?????????? ??????????????"
 #: engines/freescape/games/dark/dark.cpp:236
 #: engines/freescape/games/driller/driller.cpp:129
 #: engines/freescape/games/eclipse/eclipse.cpp:340
-#: engines/mads/metaengine.cpp:399 engines/phoenixvr/metaengine.cpp:80
+#: engines/mads/metaengine.cpp:403 engines/phoenixvr/metaengine.cpp:80
 #: engines/saga/metaengine.cpp:437 engines/sherlock/metaengine.cpp:359
 #: engines/sherlock/metaengine.cpp:618 engines/sherlock/metaengine.cpp:852
 #: engines/stark/metaengine.cpp:280 engines/sword1/metaengine.cpp:295
@@ -10085,7 +10085,7 @@ msgid "Repeat last line"
 msgstr "?????????????????? ???????????????? ??????????"
 
 #: engines/hugo/metaengine.cpp:172 engines/lure/metaengine.cpp:197
-#: engines/mads/metaengine.cpp:349 engines/tsage/metaengine.cpp:247
+#: engines/mads/metaengine.cpp:353 engines/tsage/metaengine.cpp:247
 msgid "Restore game"
 msgstr "?????????????????? ??????"
 
@@ -10274,7 +10274,7 @@ msgstr "???????? ???????????? ??????????"
 msgid "Direction keymappings"
 msgstr "???????? ???????????? ????????????????"
 
-#: engines/hypno/metaengine.cpp:316 engines/mads/metaengine.cpp:387
+#: engines/hypno/metaengine.cpp:316 engines/mads/metaengine.cpp:391
 #: engines/tsage/metaengine.cpp:259
 msgid "Show credits"
 msgstr "???????????????? ??????????????"
@@ -10921,39 +10921,39 @@ msgstr "???????????????????? ?????????????????? ??????"
 msgid "Naughty game mode"
 msgstr "?????????????????? ?????????? ??????"
 
-#: engines/mads/metaengine.cpp:314
+#: engines/mads/metaengine.cpp:318
 msgid "Start menu keymappings"
 msgstr "???????? ???????????? ???????????????????? ????????"
 
-#: engines/mads/metaengine.cpp:337
+#: engines/mads/metaengine.cpp:341
 msgid "Open game menu"
 msgstr "???????????????? ???????? ??????"
 
-#: engines/mads/metaengine.cpp:355
+#: engines/mads/metaengine.cpp:359
 msgid "Scroll bar up"
 msgstr "?????????????????????? ????????????"
 
-#: engines/mads/metaengine.cpp:361
+#: engines/mads/metaengine.cpp:365
 msgid "Scroll bar down"
 msgstr "?????????????????????? ????????????"
 
-#: engines/mads/metaengine.cpp:369
+#: engines/mads/metaengine.cpp:373
 msgid "Start game"
 msgstr "???????????? ??????"
 
-#: engines/mads/metaengine.cpp:375
+#: engines/mads/metaengine.cpp:379
 msgid "Resume game"
 msgstr "???????????????? ??????"
 
-#: engines/mads/metaengine.cpp:381
+#: engines/mads/metaengine.cpp:385
 msgid "Show intro"
 msgstr "???????????????????? ????????????????"
 
-#: engines/mads/metaengine.cpp:393
+#: engines/mads/metaengine.cpp:397
 msgid "Show quotes"
 msgstr "???????????????????? ????????????"
 
-#: engines/mads/metaengine.cpp:406
+#: engines/mads/metaengine.cpp:410
 msgid "Restart animation"
 msgstr "?????????????????? ???????????????? ????????????"
 
diff --git a/po/zh.po b/po/zh.po
index 1db9ae320c4..bf8c2513525 100644
--- a/po/zh.po
+++ b/po/zh.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-07 22:09+0000\n"
+"POT-Creation-Date: 2026-06-08 11:04+0000\n"
 "PO-Revision-Date: 2024-05-14 12:42+0000\n"
 "Last-Translator: ?????? <clonewith at qq.com>\n"
 "Language-Team: Chinese <https://translations.scummvm.org/projects/scummvm/"
@@ -3661,7 +3661,7 @@ msgstr "????????????"
 #: engines/hugo/metaengine.cpp:134 engines/illusions/metaengine.cpp:94
 #: engines/kyra/engine/eobcommon.cpp:369 engines/kyra/engine/lol.cpp:505
 #: engines/lure/metaengine.cpp:177 engines/made/metaengine.cpp:137
-#: engines/mads/metaengine.cpp:318 engines/mm/metaengine.cpp:186
+#: engines/mads/metaengine.cpp:322 engines/mm/metaengine.cpp:186
 #: engines/mtropolis/metaengine.cpp:161 engines/nancy/input.cpp:181
 #: engines/parallaction/metaengine.cpp:138 engines/saga/metaengine.cpp:278
 #: engines/sludge/keymapper_tables.h:561 engines/sludge/keymapper_tables.h:580
@@ -3698,7 +3698,7 @@ msgstr "??????"
 #: engines/hopkins/metaengine.cpp:217 engines/hugo/metaengine.cpp:141
 #: engines/illusions/metaengine.cpp:101 engines/kyra/engine/eobcommon.cpp:370
 #: engines/kyra/engine/lol.cpp:506 engines/lure/metaengine.cpp:183
-#: engines/made/metaengine.cpp:144 engines/mads/metaengine.cpp:324
+#: engines/made/metaengine.cpp:144 engines/mads/metaengine.cpp:328
 #: engines/mm/metaengine.cpp:192 engines/mtropolis/metaengine.cpp:167
 #: engines/nancy/input.cpp:188 engines/parallaction/metaengine.cpp:144
 #: engines/saga/metaengine.cpp:289 engines/sludge/keymapper_tables.h:567
@@ -5743,7 +5743,7 @@ msgstr ""
 #: engines/hugo/metaengine.cpp:129 engines/hypno/metaengine.cpp:128
 #: engines/illusions/metaengine.cpp:89 engines/lab/metaengine.cpp:149
 #: engines/lure/metaengine.cpp:168 engines/made/metaengine.cpp:132
-#: engines/mads/metaengine.cpp:312 engines/neverhood/metaengine.cpp:185
+#: engines/mads/metaengine.cpp:316 engines/neverhood/metaengine.cpp:185
 #: engines/ngi/metaengine.cpp:115 engines/petka/metaengine.cpp:113
 #: engines/prince/metaengine.cpp:227 engines/private/metaengine.cpp:127
 #: engines/queen/metaengine.cpp:146 engines/sherlock/metaengine.cpp:313
@@ -6283,7 +6283,7 @@ msgstr ""
 #: engines/hypno/metaengine.cpp:267 engines/hypno/metaengine.cpp:311
 #: engines/illusions/metaengine.cpp:90 engines/lab/metaengine.cpp:152
 #: engines/lure/metaengine.cpp:169 engines/made/metaengine.cpp:133
-#: engines/mads/metaengine.cpp:313 engines/neverhood/metaengine.cpp:186
+#: engines/mads/metaengine.cpp:317 engines/neverhood/metaengine.cpp:186
 #: engines/queen/metaengine.cpp:147 engines/saga/metaengine.cpp:268
 #: engines/sludge/keymapper_tables.h:33 engines/stark/metaengine.cpp:185
 #: engines/sword1/metaengine.cpp:267 engines/tinsel/metaengine.cpp:241
@@ -6764,7 +6764,7 @@ msgstr ""
 #: engines/bbvs/metaengine.cpp:111 engines/cruise/metaengine.cpp:223
 #: engines/freescape/movement.cpp:107 engines/got/metaengine.cpp:78
 #: engines/hugo/metaengine.cpp:197 engines/lure/metaengine.cpp:209
-#: engines/mads/metaengine.cpp:330 engines/mm/mm1/metaengine.cpp:42
+#: engines/mads/metaengine.cpp:334 engines/mm/mm1/metaengine.cpp:42
 #: engines/mm/mm1/metaengine.cpp:48 engines/saga/metaengine.cpp:328
 #: engines/tinsel/metaengine.cpp:258 engines/tsage/metaengine.cpp:190
 #: engines/tucker/metaengine.cpp:221 engines/twine/metaengine.cpp:388
@@ -7063,7 +7063,7 @@ msgstr ""
 #: engines/freescape/games/eclipse/eclipse.cpp:330
 #: engines/hopkins/metaengine.cpp:236 engines/hugo/metaengine.cpp:165
 #: engines/lure/metaengine.cpp:191 engines/made/metaengine.cpp:192
-#: engines/mads/metaengine.cpp:343 engines/mohawk/myst.cpp:583
+#: engines/mads/metaengine.cpp:347 engines/mohawk/myst.cpp:583
 #: engines/mohawk/riven.cpp:864 engines/pegasus/pegasus.cpp:2825
 #: engines/petka/metaengine.cpp:198 engines/prince/metaengine.cpp:243
 #: engines/saga/metaengine.cpp:442 engines/sherlock/metaengine.cpp:462
@@ -7139,7 +7139,7 @@ msgstr ""
 #: engines/freescape/games/dark/dark.cpp:236
 #: engines/freescape/games/driller/driller.cpp:129
 #: engines/freescape/games/eclipse/eclipse.cpp:340
-#: engines/mads/metaengine.cpp:399 engines/phoenixvr/metaengine.cpp:80
+#: engines/mads/metaengine.cpp:403 engines/phoenixvr/metaengine.cpp:80
 #: engines/saga/metaengine.cpp:437 engines/sherlock/metaengine.cpp:359
 #: engines/sherlock/metaengine.cpp:618 engines/sherlock/metaengine.cpp:852
 #: engines/stark/metaengine.cpp:280 engines/sword1/metaengine.cpp:295
@@ -9700,7 +9700,7 @@ msgid "Repeat last line"
 msgstr ""
 
 #: engines/hugo/metaengine.cpp:172 engines/lure/metaengine.cpp:197
-#: engines/mads/metaengine.cpp:349 engines/tsage/metaengine.cpp:247
+#: engines/mads/metaengine.cpp:353 engines/tsage/metaengine.cpp:247
 #, fuzzy
 #| msgid "In 2D games"
 msgid "Restore game"
@@ -9911,7 +9911,7 @@ msgstr "????????????"
 msgid "Direction keymappings"
 msgstr "????????????"
 
-#: engines/hypno/metaengine.cpp:316 engines/mads/metaengine.cpp:387
+#: engines/hypno/metaengine.cpp:316 engines/mads/metaengine.cpp:391
 #: engines/tsage/metaengine.cpp:259
 msgid "Show credits"
 msgstr "?????????????????????"
@@ -10558,51 +10558,51 @@ msgstr ""
 msgid "Naughty game mode"
 msgstr ""
 
-#: engines/mads/metaengine.cpp:314
+#: engines/mads/metaengine.cpp:318
 #, fuzzy
 #| msgid "Game Options"
 msgid "Start menu keymappings"
 msgstr "????????????"
 
-#: engines/mads/metaengine.cpp:337
+#: engines/mads/metaengine.cpp:341
 msgid "Open game menu"
 msgstr ""
 
-#: engines/mads/metaengine.cpp:355
+#: engines/mads/metaengine.cpp:359
 msgid "Scroll bar up"
 msgstr ""
 
-#: engines/mads/metaengine.cpp:361
+#: engines/mads/metaengine.cpp:365
 #, fuzzy
 #| msgid "Scroll Down"
 msgid "Scroll bar down"
 msgstr "????????????"
 
-#: engines/mads/metaengine.cpp:369
+#: engines/mads/metaengine.cpp:373
 #, fuzzy
 #| msgid "In 2D games"
 msgid "Start game"
 msgstr "2D?????????"
 
-#: engines/mads/metaengine.cpp:375
+#: engines/mads/metaengine.cpp:379
 #, fuzzy
 #| msgid "In 2D games"
 msgid "Resume game"
 msgstr "2D?????????"
 
-#: engines/mads/metaengine.cpp:381
+#: engines/mads/metaengine.cpp:385
 #, fuzzy
 #| msgid "Show hints"
 msgid "Show intro"
 msgstr "????????????"
 
-#: engines/mads/metaengine.cpp:393
+#: engines/mads/metaengine.cpp:397
 #, fuzzy
 #| msgid "Speech Options"
 msgid "Show quotes"
 msgstr "????????????"
 
-#: engines/mads/metaengine.cpp:406
+#: engines/mads/metaengine.cpp:410
 #, fuzzy
 #| msgid "In 2D games"
 msgid "Restart animation"
diff --git a/po/zh_Hans.po b/po/zh_Hans.po
index 20216c501a3..e5f4143720e 100644
--- a/po/zh_Hans.po
+++ b/po/zh_Hans.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.9.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-07 22:09+0000\n"
+"POT-Creation-Date: 2026-06-08 11:04+0000\n"
 "PO-Revision-Date: 2024-12-01 22:18+0000\n"
 "Last-Translator: ?????? <clonewith at qq.com>\n"
 "Language-Team: Chinese (Simplified Han script) <https://"
@@ -3862,7 +3862,7 @@ msgstr "????????????????????????"
 #: engines/hugo/metaengine.cpp:134 engines/illusions/metaengine.cpp:94
 #: engines/kyra/engine/eobcommon.cpp:369 engines/kyra/engine/lol.cpp:505
 #: engines/lure/metaengine.cpp:177 engines/made/metaengine.cpp:137
-#: engines/mads/metaengine.cpp:318 engines/mm/metaengine.cpp:186
+#: engines/mads/metaengine.cpp:322 engines/mm/metaengine.cpp:186
 #: engines/mtropolis/metaengine.cpp:161 engines/nancy/input.cpp:181
 #: engines/parallaction/metaengine.cpp:138 engines/saga/metaengine.cpp:278
 #: engines/sludge/keymapper_tables.h:561 engines/sludge/keymapper_tables.h:580
@@ -3899,7 +3899,7 @@ msgstr "????????????"
 #: engines/hopkins/metaengine.cpp:217 engines/hugo/metaengine.cpp:141
 #: engines/illusions/metaengine.cpp:101 engines/kyra/engine/eobcommon.cpp:370
 #: engines/kyra/engine/lol.cpp:506 engines/lure/metaengine.cpp:183
-#: engines/made/metaengine.cpp:144 engines/mads/metaengine.cpp:324
+#: engines/made/metaengine.cpp:144 engines/mads/metaengine.cpp:328
 #: engines/mm/metaengine.cpp:192 engines/mtropolis/metaengine.cpp:167
 #: engines/nancy/input.cpp:188 engines/parallaction/metaengine.cpp:144
 #: engines/saga/metaengine.cpp:289 engines/sludge/keymapper_tables.h:567
@@ -6379,7 +6379,7 @@ msgstr "??????????????????..."
 #: engines/hugo/metaengine.cpp:129 engines/hypno/metaengine.cpp:128
 #: engines/illusions/metaengine.cpp:89 engines/lab/metaengine.cpp:149
 #: engines/lure/metaengine.cpp:168 engines/made/metaengine.cpp:132
-#: engines/mads/metaengine.cpp:312 engines/neverhood/metaengine.cpp:185
+#: engines/mads/metaengine.cpp:316 engines/neverhood/metaengine.cpp:185
 #: engines/ngi/metaengine.cpp:115 engines/petka/metaengine.cpp:113
 #: engines/prince/metaengine.cpp:227 engines/private/metaengine.cpp:127
 #: engines/queen/metaengine.cpp:146 engines/sherlock/metaengine.cpp:313
@@ -6923,7 +6923,7 @@ msgstr "AGOS ?????????"
 #: engines/hypno/metaengine.cpp:267 engines/hypno/metaengine.cpp:311
 #: engines/illusions/metaengine.cpp:90 engines/lab/metaengine.cpp:152
 #: engines/lure/metaengine.cpp:169 engines/made/metaengine.cpp:133
-#: engines/mads/metaengine.cpp:313 engines/neverhood/metaengine.cpp:186
+#: engines/mads/metaengine.cpp:317 engines/neverhood/metaengine.cpp:186
 #: engines/queen/metaengine.cpp:147 engines/saga/metaengine.cpp:268
 #: engines/sludge/keymapper_tables.h:33 engines/stark/metaengine.cpp:185
 #: engines/sword1/metaengine.cpp:267 engines/tinsel/metaengine.cpp:241
@@ -7431,7 +7431,7 @@ msgstr "Soldier 714 Eye ??????"
 #: engines/bbvs/metaengine.cpp:111 engines/cruise/metaengine.cpp:223
 #: engines/freescape/movement.cpp:107 engines/got/metaengine.cpp:78
 #: engines/hugo/metaengine.cpp:197 engines/lure/metaengine.cpp:209
-#: engines/mads/metaengine.cpp:330 engines/mm/mm1/metaengine.cpp:42
+#: engines/mads/metaengine.cpp:334 engines/mm/mm1/metaengine.cpp:42
 #: engines/mm/mm1/metaengine.cpp:48 engines/saga/metaengine.cpp:328
 #: engines/tinsel/metaengine.cpp:258 engines/tsage/metaengine.cpp:190
 #: engines/tucker/metaengine.cpp:221 engines/twine/metaengine.cpp:388
@@ -7742,7 +7742,7 @@ msgstr "??????????????????"
 #: engines/freescape/games/eclipse/eclipse.cpp:330
 #: engines/hopkins/metaengine.cpp:236 engines/hugo/metaengine.cpp:165
 #: engines/lure/metaengine.cpp:191 engines/made/metaengine.cpp:192
-#: engines/mads/metaengine.cpp:343 engines/mohawk/myst.cpp:583
+#: engines/mads/metaengine.cpp:347 engines/mohawk/myst.cpp:583
 #: engines/mohawk/riven.cpp:864 engines/pegasus/pegasus.cpp:2825
 #: engines/petka/metaengine.cpp:198 engines/prince/metaengine.cpp:243
 #: engines/saga/metaengine.cpp:442 engines/sherlock/metaengine.cpp:462
@@ -7824,7 +7824,7 @@ msgstr "???????????????"
 #: engines/freescape/games/dark/dark.cpp:236
 #: engines/freescape/games/driller/driller.cpp:129
 #: engines/freescape/games/eclipse/eclipse.cpp:340
-#: engines/mads/metaengine.cpp:399 engines/phoenixvr/metaengine.cpp:80
+#: engines/mads/metaengine.cpp:403 engines/phoenixvr/metaengine.cpp:80
 #: engines/saga/metaengine.cpp:437 engines/sherlock/metaengine.cpp:359
 #: engines/sherlock/metaengine.cpp:618 engines/sherlock/metaengine.cpp:852
 #: engines/stark/metaengine.cpp:280 engines/sword1/metaengine.cpp:295
@@ -10516,7 +10516,7 @@ msgid "Repeat last line"
 msgstr "??????????????????"
 
 #: engines/hugo/metaengine.cpp:172 engines/lure/metaengine.cpp:197
-#: engines/mads/metaengine.cpp:349 engines/tsage/metaengine.cpp:247
+#: engines/mads/metaengine.cpp:353 engines/tsage/metaengine.cpp:247
 msgid "Restore game"
 msgstr "????????????"
 
@@ -10723,7 +10723,7 @@ msgstr "??????????????????"
 msgid "Direction keymappings"
 msgstr "??????????????????"
 
-#: engines/hypno/metaengine.cpp:316 engines/mads/metaengine.cpp:387
+#: engines/hypno/metaengine.cpp:316 engines/mads/metaengine.cpp:391
 #: engines/tsage/metaengine.cpp:259
 msgid "Show credits"
 msgstr "????????????????????????"
@@ -11412,39 +11412,39 @@ msgstr "??????????????????"
 msgid "Naughty game mode"
 msgstr "??????????????????"
 
-#: engines/mads/metaengine.cpp:314
+#: engines/mads/metaengine.cpp:318
 msgid "Start menu keymappings"
 msgstr "????????????????????????"
 
-#: engines/mads/metaengine.cpp:337
+#: engines/mads/metaengine.cpp:341
 msgid "Open game menu"
 msgstr "??????????????????"
 
-#: engines/mads/metaengine.cpp:355
+#: engines/mads/metaengine.cpp:359
 msgid "Scroll bar up"
 msgstr "????????????"
 
-#: engines/mads/metaengine.cpp:361
+#: engines/mads/metaengine.cpp:365
 msgid "Scroll bar down"
 msgstr "????????????"
 
-#: engines/mads/metaengine.cpp:369
+#: engines/mads/metaengine.cpp:373
 msgid "Start game"
 msgstr "????????????"
 
-#: engines/mads/metaengine.cpp:375
+#: engines/mads/metaengine.cpp:379
 msgid "Resume game"
 msgstr "????????????"
 
-#: engines/mads/metaengine.cpp:381
+#: engines/mads/metaengine.cpp:385
 msgid "Show intro"
 msgstr "??????????????????"
 
-#: engines/mads/metaengine.cpp:393
+#: engines/mads/metaengine.cpp:397
 msgid "Show quotes"
 msgstr "????????????"
 
-#: engines/mads/metaengine.cpp:406
+#: engines/mads/metaengine.cpp:410
 msgid "Restart animation"
 msgstr "??????????????????"
 
diff --git a/po/zh_Hant.po b/po/zh_Hant.po
index 010a102e306..41bd49341aa 100644
--- a/po/zh_Hant.po
+++ b/po/zh_Hant.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.9.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-07 22:09+0000\n"
+"POT-Creation-Date: 2026-06-08 11:04+0000\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: Automatically generated\n"
 "Language-Team: none\n"
@@ -3603,7 +3603,7 @@ msgstr ""
 #: engines/hugo/metaengine.cpp:134 engines/illusions/metaengine.cpp:94
 #: engines/kyra/engine/eobcommon.cpp:369 engines/kyra/engine/lol.cpp:505
 #: engines/lure/metaengine.cpp:177 engines/made/metaengine.cpp:137
-#: engines/mads/metaengine.cpp:318 engines/mm/metaengine.cpp:186
+#: engines/mads/metaengine.cpp:322 engines/mm/metaengine.cpp:186
 #: engines/mtropolis/metaengine.cpp:161 engines/nancy/input.cpp:181
 #: engines/parallaction/metaengine.cpp:138 engines/saga/metaengine.cpp:278
 #: engines/sludge/keymapper_tables.h:561 engines/sludge/keymapper_tables.h:580
@@ -3636,7 +3636,7 @@ msgstr ""
 #: engines/hopkins/metaengine.cpp:217 engines/hugo/metaengine.cpp:141
 #: engines/illusions/metaengine.cpp:101 engines/kyra/engine/eobcommon.cpp:370
 #: engines/kyra/engine/lol.cpp:506 engines/lure/metaengine.cpp:183
-#: engines/made/metaengine.cpp:144 engines/mads/metaengine.cpp:324
+#: engines/made/metaengine.cpp:144 engines/mads/metaengine.cpp:328
 #: engines/mm/metaengine.cpp:192 engines/mtropolis/metaengine.cpp:167
 #: engines/nancy/input.cpp:188 engines/parallaction/metaengine.cpp:144
 #: engines/saga/metaengine.cpp:289 engines/sludge/keymapper_tables.h:567
@@ -5635,7 +5635,7 @@ msgstr ""
 #: engines/hugo/metaengine.cpp:129 engines/hypno/metaengine.cpp:128
 #: engines/illusions/metaengine.cpp:89 engines/lab/metaengine.cpp:149
 #: engines/lure/metaengine.cpp:168 engines/made/metaengine.cpp:132
-#: engines/mads/metaengine.cpp:312 engines/neverhood/metaengine.cpp:185
+#: engines/mads/metaengine.cpp:316 engines/neverhood/metaengine.cpp:185
 #: engines/ngi/metaengine.cpp:115 engines/petka/metaengine.cpp:113
 #: engines/prince/metaengine.cpp:227 engines/private/metaengine.cpp:127
 #: engines/queen/metaengine.cpp:146 engines/sherlock/metaengine.cpp:313
@@ -6163,7 +6163,7 @@ msgstr ""
 #: engines/hypno/metaengine.cpp:267 engines/hypno/metaengine.cpp:311
 #: engines/illusions/metaengine.cpp:90 engines/lab/metaengine.cpp:152
 #: engines/lure/metaengine.cpp:169 engines/made/metaengine.cpp:133
-#: engines/mads/metaengine.cpp:313 engines/neverhood/metaengine.cpp:186
+#: engines/mads/metaengine.cpp:317 engines/neverhood/metaengine.cpp:186
 #: engines/queen/metaengine.cpp:147 engines/saga/metaengine.cpp:268
 #: engines/sludge/keymapper_tables.h:33 engines/stark/metaengine.cpp:185
 #: engines/sword1/metaengine.cpp:267 engines/tinsel/metaengine.cpp:241
@@ -6612,7 +6612,7 @@ msgstr ""
 #: engines/bbvs/metaengine.cpp:111 engines/cruise/metaengine.cpp:223
 #: engines/freescape/movement.cpp:107 engines/got/metaengine.cpp:78
 #: engines/hugo/metaengine.cpp:197 engines/lure/metaengine.cpp:209
-#: engines/mads/metaengine.cpp:330 engines/mm/mm1/metaengine.cpp:42
+#: engines/mads/metaengine.cpp:334 engines/mm/mm1/metaengine.cpp:42
 #: engines/mm/mm1/metaengine.cpp:48 engines/saga/metaengine.cpp:328
 #: engines/tinsel/metaengine.cpp:258 engines/tsage/metaengine.cpp:190
 #: engines/tucker/metaengine.cpp:221 engines/twine/metaengine.cpp:388
@@ -6899,7 +6899,7 @@ msgstr ""
 #: engines/freescape/games/eclipse/eclipse.cpp:330
 #: engines/hopkins/metaengine.cpp:236 engines/hugo/metaengine.cpp:165
 #: engines/lure/metaengine.cpp:191 engines/made/metaengine.cpp:192
-#: engines/mads/metaengine.cpp:343 engines/mohawk/myst.cpp:583
+#: engines/mads/metaengine.cpp:347 engines/mohawk/myst.cpp:583
 #: engines/mohawk/riven.cpp:864 engines/pegasus/pegasus.cpp:2825
 #: engines/petka/metaengine.cpp:198 engines/prince/metaengine.cpp:243
 #: engines/saga/metaengine.cpp:442 engines/sherlock/metaengine.cpp:462
@@ -6975,7 +6975,7 @@ msgstr ""
 #: engines/freescape/games/dark/dark.cpp:236
 #: engines/freescape/games/driller/driller.cpp:129
 #: engines/freescape/games/eclipse/eclipse.cpp:340
-#: engines/mads/metaengine.cpp:399 engines/phoenixvr/metaengine.cpp:80
+#: engines/mads/metaengine.cpp:403 engines/phoenixvr/metaengine.cpp:80
 #: engines/saga/metaengine.cpp:437 engines/sherlock/metaengine.cpp:359
 #: engines/sherlock/metaengine.cpp:618 engines/sherlock/metaengine.cpp:852
 #: engines/stark/metaengine.cpp:280 engines/sword1/metaengine.cpp:295
@@ -9239,7 +9239,7 @@ msgid "Repeat last line"
 msgstr ""
 
 #: engines/hugo/metaengine.cpp:172 engines/lure/metaengine.cpp:197
-#: engines/mads/metaengine.cpp:349 engines/tsage/metaengine.cpp:247
+#: engines/mads/metaengine.cpp:353 engines/tsage/metaengine.cpp:247
 msgid "Restore game"
 msgstr ""
 
@@ -9428,7 +9428,7 @@ msgstr ""
 msgid "Direction keymappings"
 msgstr ""
 
-#: engines/hypno/metaengine.cpp:316 engines/mads/metaengine.cpp:387
+#: engines/hypno/metaengine.cpp:316 engines/mads/metaengine.cpp:391
 #: engines/tsage/metaengine.cpp:259
 msgid "Show credits"
 msgstr ""
@@ -10014,39 +10014,39 @@ msgstr ""
 msgid "Naughty game mode"
 msgstr ""
 
-#: engines/mads/metaengine.cpp:314
+#: engines/mads/metaengine.cpp:318
 msgid "Start menu keymappings"
 msgstr ""
 
-#: engines/mads/metaengine.cpp:337
+#: engines/mads/metaengine.cpp:341
 msgid "Open game menu"
 msgstr ""
 
-#: engines/mads/metaengine.cpp:355
+#: engines/mads/metaengine.cpp:359
 msgid "Scroll bar up"
 msgstr ""
 
-#: engines/mads/metaengine.cpp:361
+#: engines/mads/metaengine.cpp:365
 msgid "Scroll bar down"
 msgstr ""
 
-#: engines/mads/metaengine.cpp:369
+#: engines/mads/metaengine.cpp:373
 msgid "Start game"
 msgstr ""
 
-#: engines/mads/metaengine.cpp:375
+#: engines/mads/metaengine.cpp:379
 msgid "Resume game"
 msgstr ""
 
-#: engines/mads/metaengine.cpp:381
+#: engines/mads/metaengine.cpp:385
 msgid "Show intro"
 msgstr ""
 
-#: engines/mads/metaengine.cpp:393
+#: engines/mads/metaengine.cpp:397
 msgid "Show quotes"
 msgstr ""
 
-#: engines/mads/metaengine.cpp:406
+#: engines/mads/metaengine.cpp:410
 msgid "Restart animation"
 msgstr ""
 



From noreply at scummvm.org  Mon Jun  8 11:34:44 2026
From: noreply at scummvm.org (dreammaster)
Date: Mon,  8 Jun 2026 11:34:44 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 082a623246a2ebcd28c8a3c2803d0e435012ab53
Message-ID: <20260608113444.556317005CC@ron.scummvm.net>

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

Summary:
082a623246 MADS: NEBULAR: Fix bug #16849 Game semi-stuck while using binoculars


Commit: 082a623246a2ebcd28c8a3c2803d0e435012ab53
    https://github.com/scummvm/scummvm/commit/082a623246a2ebcd28c8a3c2803d0e435012ab53
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-06-08T21:34:36+10:00

Commit Message:
MADS: NEBULAR: Fix bug #16849 Game semi-stuck while using binoculars

Changed paths:
    NEWS.md
    engines/mads/nebular/nebular_scenes2.cpp


diff --git a/NEWS.md b/NEWS.md
index 25658b24a49..7c4c8e69855 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -32,6 +32,9 @@ For a more comprehensive changelog of the latest experimental code, see:
      which means that some rare issues concerning characters behavior
      should now be gone.
 
+ MADS:
+   - In Rex Nebular fix game being semi-stuck while using binoculars.
+
  MM:
    - Reworked keymapper for MM1.
    - Fixed MM1 Enhanced allowing trading more than the character owned.
diff --git a/engines/mads/nebular/nebular_scenes2.cpp b/engines/mads/nebular/nebular_scenes2.cpp
index ba3a37441f3..c0e6bf5bd0f 100644
--- a/engines/mads/nebular/nebular_scenes2.cpp
+++ b/engines/mads/nebular/nebular_scenes2.cpp
@@ -880,8 +880,9 @@ void Scene202::actions() {
 		if (!_ladderTopFl) {
 			switch (_game._trigger) {
 			case 0:
+				_toTeleportFl = true;
 				_game._player._stepEnabled = false;
-				_game._player._visible= false;
+				_game._player._visible = false;
 				_globals._sequenceIndexes[10] = _scene->_sequences.addSpriteCycle(_globals._spriteIndexes[9], false, 6, 1, 0, 0);
 				_scene->_sequences.setAnimRange(_globals._sequenceIndexes[10], 1, 6);
 				_scene->_sequences.setPosition(_globals._sequenceIndexes[10], Common::Point(172, 123));



From noreply at scummvm.org  Mon Jun  8 16:01:15 2026
From: noreply at scummvm.org (GandalfTheWhite80)
Date: Mon,  8 Jun 2026 16:01:15 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm-icons master ->
 abf06e612bfcc432d58e220fa3ca403481e03381
Message-ID: <20260608160115.361477005CC@ron.scummvm.net>

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

Summary:
abf06e612b ICON: Discworld Noir (#348)


Commit: abf06e612bfcc432d58e220fa3ca403481e03381
    https://github.com/scummvm/scummvm-icons/commit/abf06e612bfcc432d58e220fa3ca403481e03381
Author: Garret Noling (garret at werockjustbecause.com)
Date: 2026-06-08T18:01:11+02:00

Commit Message:
ICON: Discworld Noir (#348)

* Add icon for Discworld Noir

* ICON: Updated Discworld Noir icon with better compression

Changed paths:
  A icons/tinsel-noir.png
    TOC.md


diff --git a/TOC.md b/TOC.md
index 2e08d85..d6eb906 100644
--- a/TOC.md
+++ b/TOC.md
@@ -778,6 +778,7 @@
 | ??? | tetraedge | syberia2           | Syberia II                                                                 | GandalfTheWhite80 |
 | ??? | tinsel | dw                  | Discworld                                                                    | rehail, GandalfTheWhite80 |
 | ??? | tinsel | dw2                 | Discworld 2: Missing Presumed ...!?                                          | JenniBee, GandalfTheWhite80 |
+| ??? | tinsel | noir                | Discworld Noir                                                               | dibz |
 | ??? | titanic | titanic             | Starship Titanic                                                             | thedoc, GandalfTheWhite80 |
 | ??? | toltecs | toltecs             | 3 Skulls of the Toltecs                                                      |Canuma, GandalfTheWhite80|
 | ??? | tony | tony                | Tony Tough and the Night of Roasted Moths                                    | rehail |
diff --git a/icons/tinsel-noir.png b/icons/tinsel-noir.png
new file mode 100644
index 0000000..61f27c5
Binary files /dev/null and b/icons/tinsel-noir.png differ



From noreply at scummvm.org  Mon Jun  8 16:22:57 2026
From: noreply at scummvm.org (ScummVM-Translations)
Date: Mon,  8 Jun 2026 16:22:57 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 a2148202097692917a010d2788be40456980e2b8
Message-ID: <20260608162257.D024A7005CC@ron.scummvm.net>

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

Summary:
a214820209 I18N: Update translation (French)


Commit: a2148202097692917a010d2788be40456980e2b8
    https://github.com/scummvm/scummvm/commit/a2148202097692917a010d2788be40456980e2b8
Author: Purple T (ZEONK at hotmail.com)
Date: 2026-06-08T16:22:49Z

Commit Message:
I18N: Update translation (French)

Currently translated at 100.0% (3206 of 3206 strings)

Changed paths:
    po/fr_FR.po


diff --git a/po/fr_FR.po b/po/fr_FR.po
index c2c2c8c8279..2753f9b320c 100644
--- a/po/fr_FR.po
+++ b/po/fr_FR.po
@@ -8,7 +8,7 @@ msgstr ""
 "Project-Id-Version: ScummVM 1.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
 "POT-Creation-Date: 2026-06-08 11:04+0000\n"
-"PO-Revision-Date: 2026-06-07 13:22+0000\n"
+"PO-Revision-Date: 2026-06-08 16:22+0000\n"
 "Last-Translator: Purple T <ZEONK at hotmail.com>\n"
 "Language-Team: French <https://translations.scummvm.org/projects/scummvm/"
 "scummvm/fr/>\n"
@@ -13717,16 +13717,12 @@ msgid "All levels will be available without requiring passwords"
 msgstr "Tous les niveaux seront disponibles sans demander de mots de passe"
 
 #: engines/scumm/metaengine.cpp:900
-#, fuzzy
-#| msgid "No date saved"
 msgid "No damage"
-msgstr "Date inconnue"
+msgstr "Aucun d??g??t"
 
 #: engines/scumm/metaengine.cpp:901
-#, fuzzy
-#| msgid "Disable save menu"
 msgid "Disable player damage"
-msgstr "D??sactiver le menu sauvegarde"
+msgstr "D??sactive les d??g??ts re??us par le joueur"
 
 #: engines/scumm/metaengine.cpp:1061
 msgid "Down left"



From noreply at scummvm.org  Mon Jun  8 20:46:36 2026
From: noreply at scummvm.org (eriktorbjorn)
Date: Mon,  8 Jun 2026 20:46:36 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 75531b2dd1062b3e47bb4a1764cd97a6c0d152c7
Message-ID: <20260608204636.F237B7005CC@ron.scummvm.net>

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

Summary:
75531b2dd1 MADS: DRAGONSPHERE: Update global_menu_score() comments


Commit: 75531b2dd1062b3e47bb4a1764cd97a6c0d152c7
    https://github.com/scummvm/scummvm/commit/75531b2dd1062b3e47bb4a1764cd97a6c0d152c7
Author: Torbj??rn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2026-06-08T22:45:41+02:00

Commit Message:
MADS: DRAGONSPHERE: Update global_menu_score() comments

They still referred to the rankings in Return of the Phantom.

Changed paths:
    engines/mads/madsv2/dragonsphere/menus.cpp


diff --git a/engines/mads/madsv2/dragonsphere/menus.cpp b/engines/mads/madsv2/dragonsphere/menus.cpp
index 39ebcac1388..5ab4e05b32b 100644
--- a/engines/mads/madsv2/dragonsphere/menus.cpp
+++ b/engines/mads/madsv2/dragonsphere/menus.cpp
@@ -136,23 +136,23 @@ static void global_menu_score() {
 	text_index[1] = 250;
 
 	if (score <= 25) {
-		text_index[2] = 1;          /* Stage sweeper */
+		text_index[2] = 1;          /* Dogsbody */
 	} else if (score <= 50) {
-		text_index[2] = 2;          /* Dresser */
+		text_index[2] = 2;          /* Varlet */
 	} else if (score <= 75) {
-		text_index[2] = 3;          /* Usher */
+		text_index[2] = 3;          /* Yeoman */
 	} else if (score <= 100) {
-		text_index[2] = 4;          /* Stagehand */
+		text_index[2] = 4;          /* Squire */
 	} else if (score <= 150) {
-		text_index[2] = 5;          /* Chorus Member */
+		text_index[2] = 5;          /* Knight */
 	} else if (score <= 200) {
-		text_index[2] = 6;          /* Supporting Player */
+		text_index[2] = 6;          /* Petty Noble */
 	} else if (score <= 249) {
-		text_index[2] = 7;          /* Star Player */
+		text_index[2] = 7;          /* Noble */
 	} else if (score <= 250) {
-		text_index[2] = 8;          /* Director */
+		text_index[2] = 8;          /* King */
 	} else {
-		text_index[2] = 9;          /* Bug Finder! */
+		text_index[2] = 9;          /* King+ */
 	}
 
 	text_show(99);



From noreply at scummvm.org  Mon Jun  8 21:44:05 2026
From: noreply at scummvm.org (sev-)
Date: Mon,  8 Jun 2026 21:44:05 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 b88ca9fda3f4b8a54a462f898c973d147e1f450d
Message-ID: <20260608214405.7FE477005CC@ron.scummvm.net>

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

Summary:
b88ca9fda3 DIRECTOR: Remove too aggressive check of VWCF checksum comparison


Commit: b88ca9fda3f4b8a54a462f898c973d147e1f450d
    https://github.com/scummvm/scummvm/commit/b88ca9fda3f4b8a54a462f898c973d147e1f450d
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2026-06-08T23:43:09+02:00

Commit Message:
DIRECTOR: Remove too aggressive check of VWCF checksum comparison

Changed paths:
    engines/director/cast.cpp


diff --git a/engines/director/cast.cpp b/engines/director/cast.cpp
index f9001f7009a..d2f4adb65a7 100644
--- a/engines/director/cast.cpp
+++ b/engines/director/cast.cpp
@@ -528,11 +528,8 @@ bool Cast::loadConfig() {
 		//Calculation and verification of checksum
 		uint32 check = computeChecksum();
 
-		if (check != _checksum) {
+		if (check != _checksum)
 			warning("BUILDBOT: The checksum for this VWCF resource is incorrect. Got %08x, but expected %08x", check, _checksum);
-			delete stream;
-			return false;
-		}
 
 		if (_version >= kFileVer400 && _version < kFileVer500) {
 			_field30 = stream->readSint16();



From noreply at scummvm.org  Tue Jun  9 00:12:21 2026
From: noreply at scummvm.org (sluicebox)
Date: Tue,  9 Jun 2026 00:12:21 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 9c9b568d71f322991c819b4d44887193fed880d9
Message-ID: <20260609001221.A5C427005BC@ron.scummvm.net>

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

Summary:
9c9b568d71 SCI32: Fix RAMA floating digits in math puzzle


Commit: 9c9b568d71f322991c819b4d44887193fed880d9
    https://github.com/scummvm/scummvm/commit/9c9b568d71f322991c819b4d44887193fed880d9
Author: sluicebox (22204938+sluicebox at users.noreply.github.com)
Date: 2026-06-08T17:09:31-07:00

Commit Message:
SCI32: Fix RAMA floating digits in math puzzle

Fixes bugs #10261, #16837

Changed paths:
    engines/sci/engine/script_patches.cpp


diff --git a/engines/sci/engine/script_patches.cpp b/engines/sci/engine/script_patches.cpp
index c5c7a992d1f..5f5bf142938 100644
--- a/engines/sci/engine/script_patches.cpp
+++ b/engines/sci/engine/script_patches.cpp
@@ -25477,6 +25477,47 @@ static const uint16 ramaDemoBrokenButtonsPatch[] = {
 	PATCH_END
 };
 
+// The Bangkok addition/subtraction puzzle has an incompatibility with ScummVM's
+//  SCI32 drawing algorithm, causing digits to float in the upper left corner.
+//  The script uses many View objects to display its question digits, but does
+//  not explicitly hide them when they are not in use. Instead it merely sets
+//  their coordinates to (0,0). This appears to be an oversight, as the answer
+//  digits are correctly hidden by setting their cels to a transparent image.
+//  As the question digits have no fixed priority, the interpreter sets their
+//  priority to their y value, leaving them with priority zero. This is the same
+//  as the background view. In SSCI this happened to work because its last-ditch
+//  sorting algorithm used each screen item's internal memory address.
+//
+// We fix this by setting unused digit coordinates to (-2,-2) instead of (0,0),
+//  causing their priority to be set to -2 and hidden below the background view.
+//  A priority of -1 would probably also work, but there are places in scripts
+//  where -1 is a sentinel value, so it is safer to use a different value.
+//
+// Applies to: All versions
+// Responsible methods: quesDigit1:init, quesDigit2:init, ...
+// Fixes bugs: #10261, #16837
+static const uint16 ramaDigitPriorityPcSignature[] = {
+	SIG_MAGICDWORD,
+	0x35, 0x00,                    // ldi 00
+	0x65, 0x1e,                    // aTop x
+	                               // line
+	                               // aTop y
+	SIG_END
+};
+
+static const uint16 ramaDigitPriorityMacSignature[] = {
+	SIG_MAGICDWORD,
+	0x35, 0x00,                    // ldi 00
+	0x65, 0x36,                    // aTop x
+	                               // aTop y
+	SIG_END
+};
+
+static const uint16 ramaDigitPriorityPatch[] = {
+	0x35, 0xfe,                   // ldi fe [ -2 ]
+	PATCH_END
+};
+
 static const SciScriptPatcherEntry ramaSignatures[] = {
 	{  true,     0, "disable video benchmarking",                     1, sci2BenchmarkSignature,          sci2BenchmarkReversePatch },
 	{  true,    12, "disable video benchmarking",                     1, sci2BenchmarkSignature,          sci2BenchmarkReversePatch },
@@ -25485,6 +25526,8 @@ static const SciScriptPatcherEntry ramaSignatures[] = {
 	{  true,    85, "fix SaveManager to use normal readWord calls",   1, ramaSerializeRegTSignature1,     ramaSerializeRegTPatch1 },
 	{  true,    90, "demo: remove broken button handlers",            1, ramaDemoBrokenButtonsSignature,  ramaDemoBrokenButtonsPatch },
 	{  true,   201, "fix crash restoring save games using NukeTimer", 1, ramaNukeTimerSignature,          ramaNukeTimerPatch },
+	{  true,  4008, "fix digit priority",                            17, ramaDigitPriorityPcSignature,    ramaDigitPriorityPatch },
+	{  true,  4008, "fix digit priority",                            17, ramaDigitPriorityMacSignature,   ramaDigitPriorityPatch },
 	{  true, 64928, "Narrator lockup fix",                            1, sciNarratorLockupSignature,      sciNarratorLockupPatch },
 	{  true, 64990, "disable change directory button",                1, ramaChangeDirSignature,          ramaChangeDirPatch },
 	SCI_SIGNATUREENTRY_TERMINATOR



From noreply at scummvm.org  Tue Jun  9 00:44:50 2026
From: noreply at scummvm.org (tag2015)
Date: Tue,  9 Jun 2026 00:44:50 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 9f9becdb5d97629fae4668730512e725505824cd
Message-ID: <20260609004450.16DA67005BC@ron.scummvm.net>

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:
ade29cbd90 AGS: Update blackwell entries
9f9becdb5d AGS: Update Wadjet games 1/2


Commit: ade29cbd903e01a7b93dafb97921ffc3c03f31d2
    https://github.com/scummvm/scummvm/commit/ade29cbd903e01a7b93dafb97921ffc3c03f31d2
Author: Walter Agazzi (walter.agazzi at protonmail.com)
Date: 2026-06-09T02:43:37+02:00

Commit Message:
AGS: Update blackwell entries

Changed paths:
    engines/ags/detection_tables.h


diff --git a/engines/ags/detection_tables.h b/engines/ags/detection_tables.h
index a4c9695a04b..a13cf3dea04 100644
--- a/engines/ags/detection_tables.h
+++ b/engines/ags/detection_tables.h
@@ -4652,12 +4652,14 @@ const AGSGameDescription GAME_DESCRIPTIONS[] = {
 	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("blackwell2", "unbound.exe", "0c77a306c0604c46e8c3bdb1002e453b", 41552865, 0), // v4.3
 	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("blackwell2", "unbound.exe", "0c77a306c0604c46e8c3bdb1002e453b", 41554223, 0), // v4.4a
 	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("blackwell2", "unbound.exe", "0c77a306c0604c46e8c3bdb1002e453b", 41555601, 0), // v4.5
+	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("blackwell2", "unbound.exe", "0c77a306c0604c46e8c3bdb1002e453b", 41555090, 0), // v4.6
 	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("blackwell2", "unbound.ags", "67d4c87f7005084587f3c072be7279e5", 38329275, 0), // Linux v3.0
 	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("blackwell2", "unbound.ags", "610d1a1a025809083761c3236a527889", 38330460, 0), // Linux v3.0c
 	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("blackwell2", "unbound.ags", "68586a9bdf0ba899540f09d9990f26fc", 38333133, 0), // Linux v3.0d
 	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("blackwell2", "unbound.ags", "66a36785d7ab52f3d5de0e1813d8b2c4", 38337969, 0), // Linux v3.1a
 	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("blackwell2", "unbound.ags", "2ad5a8c3ea3a7af6469a9eccd0c6ef65", 38364621, 0), // Linux v4.3
 	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("blackwell2", "unbound.ags", "70b3974f23b2a2ecdcfb152fdb296557", 38365979, 0), // Linux v4.4a
+	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("blackwell2", "unbound.ags", "00a42cb48c3fde19a0f77be582b93e99", 38367357, 0), // Linux v4.5
 	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("blackwell2", "ac2game.dat", "82f8bf2635ea1b5bfb2e8693fa883f89", 57638596, 0), // Mac
 	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("blackwell2", "ac2game.dat", "c787c663b92cb3596a8e7219f0f9bb25", 57639048, 0), // Mac v2.3
 	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("blackwell2", "ac2game.dat", "53b931909fc86ede449eb371e5396c36", 57639192, 0), // Mac v2.4
@@ -4667,6 +4669,7 @@ const AGSGameDescription GAME_DESCRIPTIONS[] = {
 	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("blackwell2", "ac2game.dat", "66a36785d7ab52f3d5de0e1813d8b2c4", 38337969, 0), // Mac v3.1a-v3.3
 	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("blackwell2", "ac2game.dat", "2ad5a8c3ea3a7af6469a9eccd0c6ef65", 38364621, 0), // Mac v4.3
 	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("blackwell2", "ac2game.dat", "70b3974f23b2a2ecdcfb152fdb296557", 38365979, 0), // Mac v4.4a
+	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("blackwell2", "ac2game.dat", "00a42cb48c3fde19a0f77be582b93e99", 38367357, 0), // Mac v4.5
 	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("blackwell2", "unbound.exe", "5c3a940514d91431e8e1c372018851ca", 14496128, GAMEFLAG_PLUGINS_AGSTEAM_WADJETEYE), // Win
 	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("blackwell2", "unbound.exe", "5c3a940514d91431e8e1c372018851ca", 14469500, GAMEFLAG_PLUGINS_AGSTEAM_WADJETEYE),
 	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("blackwell2", "unbound.exe", "00edc7b69ae377f6093ac567fd901849", 15683333, GAMEFLAG_PLUGINS_AGSTEAM_WADJETEYE),
@@ -4713,11 +4716,13 @@ const AGSGameDescription GAME_DESCRIPTIONS[] = {
 	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("blackwell3", "convergence.exe", "7335d87ffcafea6c9d833a4accb2a986", 104795348, 0),  // v3.1a
 	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("blackwell3", "convergence.exe", "7335d87ffcafea6c9d833a4accb2a986", 104847631, 0),  // v3.4a
 	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("blackwell3", "convergence.exe", "7335d87ffcafea6c9d833a4accb2a986", 104865661, 0),  // v3.6a
+	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("blackwell3", "convergence.exe", "7335d87ffcafea6c9d833a4accb2a986", 104868664, 0),  // v3.7
 	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("blackwell3", "convergence.ags", "c36dd508e650e798ccd4673df6f61a8f", 101541153, 0),  // Linux v3.0
 	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("blackwell3", "convergence.ags", "cba47b92a794f69d9dfde32e0b09e733", 101541172, 0),  // Linux v3.0b
 	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("blackwell3", "convergence.ags", "0e5941423ec7d2d34be9dd0fce38b5da", 101544640, 0),  // Linux v3.1a
 	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("blackwell3", "convergence.ags", "f32e7fa4bdf1442db8b221e637364197", 101596923, 0),  // Linux v3.4a
 	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("blackwell3", "convergence.ags", "a8332cdc4df77590483744442ada613f", 101614953, 0),  // Linux v3.6a
+	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("blackwell3", "convergence.ags", "cc7dd264b93d270676b05ea640c373bd", 101617956, 0),  // Linux v3.7
 	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("blackwell3", "ac2game.dat", "0b99002018dcf6d3db95925006da01bb", 170769466, 0), // Mac
 	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("blackwell3", "ac2game.dat", "0b6f9ba727ce934a515dcff9af9c3b9e", 170769945, 0), // Mac v2.3
 	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("blackwell3", "ac2game.dat", "613c49405b24b367a961a7e14a3849d0", 170769944, 0), // Mac v2.3b
@@ -4726,6 +4731,7 @@ const AGSGameDescription GAME_DESCRIPTIONS[] = {
 	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("blackwell3", "ac2game.dat", "0e5941423ec7d2d34be9dd0fce38b5da", 101544640, 0), // Mac v3.1a-v3.3
 	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("blackwell3", "ac2game.dat", "f32e7fa4bdf1442db8b221e637364197", 101596923, 0), // Mac v3.4a
 	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("blackwell3", "ac2game.dat", "a8332cdc4df77590483744442ada613f", 101614953, 0), // Mac v3.6a
+	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("blackwell3", "ac2game.dat", "cc7dd264b93d270676b05ea640c373bd", 101617956, 0), // Mac v3.7
 	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("blackwell3", "convergence.exe", "2260c1a21aba7ac00baf0100d4ca54f1", 172578803, GAMEFLAG_PLUGINS_AGSTEAM_WADJETEYE),  // Win
 	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("blackwell3", "convergence.exe", "2260c1a21aba7ac00baf0100d4ca54f1", 171969680, GAMEFLAG_PLUGINS_AGSTEAM_WADJETEYE),
 	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("blackwell3", "convergence.exe", "f261c2505f98503a1ec9f1dff7f13bda", 173873229, GAMEFLAG_PLUGINS_AGSTEAM_WADJETEYE),
@@ -4737,21 +4743,25 @@ const AGSGameDescription GAME_DESCRIPTIONS[] = {
 	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("blackwell3", "convergence.exe", "7335d87ffcafea6c9d833a4accb2a986", 104795126, GAMEFLAG_PLUGINS_AGSTEAM_WADJETEYE),
 	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("blackwell3", "convergence.exe", "7335d87ffcafea6c9d833a4accb2a986", 104847409, GAMEFLAG_PLUGINS_AGSTEAM_WADJETEYE),
 	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("blackwell3", "convergence.exe", "7335d87ffcafea6c9d833a4accb2a986", 104865439, GAMEFLAG_PLUGINS_AGSTEAM_WADJETEYE),
+	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("blackwell3", "convergence.exe", "7335d87ffcafea6c9d833a4accb2a986", 104868442, GAMEFLAG_PLUGINS_AGSTEAM_WADJETEYE),
 	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("blackwell3", "convergence.ags", "6aef643a63ffb0ab0bf2df290c19a971", 101540923, GAMEFLAG_PLUGINS_AGSTEAM_WADJETEYE),  // Linux
 	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("blackwell3", "convergence.ags", "b857e7009df631cb1eee4ce7ba93878d", 101540950, GAMEFLAG_PLUGINS_AGSTEAM_WADJETEYE),
 	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("blackwell3", "convergence.ags", "a3f8b25ef46d0f76113307e2c56b682c", 101544418, GAMEFLAG_PLUGINS_AGSTEAM_WADJETEYE),
 	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("blackwell3", "convergence.ags", "cc40256406092666dbd4c35f1b3fe8c2", 101596701, GAMEFLAG_PLUGINS_AGSTEAM_WADJETEYE),
 	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("blackwell3", "convergence.ags", "1e074aa859e6be6044dab6b1119066cc", 101614731, GAMEFLAG_PLUGINS_AGSTEAM_WADJETEYE),
+	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("blackwell3", "convergence.ags", "ae44865c3811063f2612dc57a0b4b75a", 101617734, GAMEFLAG_PLUGINS_AGSTEAM_WADJETEYE),
 	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("blackwell3", "ac2game.dat", "2844e3823d74652d282b8173bbde2b8b", 170703726, GAMEFLAG_PLUGINS_AGSTEAM_WADJETEYE), // Linux(?) & Mac
 	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("blackwell3", "ac2game.dat", "0b99002018dcf6d3db95925006da01bb", 170769466, GAMEFLAG_PLUGINS_AGSTEAM_WADJETEYE), // Mac
 	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("blackwell3", "ac2game.dat", "b857e7009df631cb1eee4ce7ba93878d", 101540950, GAMEFLAG_PLUGINS_AGSTEAM_WADJETEYE),
 	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("blackwell3", "ac2game.dat", "a3f8b25ef46d0f76113307e2c56b682c", 101544418, GAMEFLAG_PLUGINS_AGSTEAM_WADJETEYE),
 	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("blackwell3", "ac2game.dat", "cc40256406092666dbd4c35f1b3fe8c2", 101596701, GAMEFLAG_PLUGINS_AGSTEAM_WADJETEYE),
 	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("blackwell3", "ac2game.dat", "1e074aa859e6be6044dab6b1119066cc", 101614731, GAMEFLAG_PLUGINS_AGSTEAM_WADJETEYE),
+	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("blackwell3", "ac2game.dat", "ae44865c3811063f2612dc57a0b4b75a", 101617734, GAMEFLAG_PLUGINS_AGSTEAM_WADJETEYE),
 	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("blackwell3", "Convergence.ags", "a20d1e625f1832f2282e7f595c1de70a", 101579536, GAMEFLAG_PLUGINS_AGSTEAM_WADJETEYE),  // Steamdeck
 	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("blackwell3", "Convergence.ags", "6307e8f7247669c6ff9aebd78e33d81e", 101583023, GAMEFLAG_PLUGINS_AGSTEAM_WADJETEYE),
 	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("blackwell3", "Convergence.ags", "9e9c7e39ecef730c28dc67f240681283", 101635346, GAMEFLAG_PLUGINS_AGSTEAM_WADJETEYE),
 	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("blackwell3", "Convergence.ags", "0f088c61a502ee0cd3f36126588c766a", 101653258, GAMEFLAG_PLUGINS_AGSTEAM_WADJETEYE),
+	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("blackwell3", "Convergence.ags", "a4c62861a27b688cc2fe04466c0e172f", 101656261, GAMEFLAG_PLUGINS_AGSTEAM_WADJETEYE),
 	GAME_ENTRY_EN("blackwell3", "convergence.exe", "2260c1a21aba7ac00baf0100d4ca54f1", 172578032), // Humble Bundle (Windows, v1.1)
 	GAME_ENTRY_EN("blackwell3", "ac2game.dat", "b177ae79e72dfacad8c3af0a64d80395", 171379482), // Big Fish Games (Windows)
 	GAME_ENTRY_EN("blackwell3", "ac2game.dat", "c894f6005f479ba8f12e87223920aa6a", 170700456), // Humble Bundle (Linux)
@@ -4816,14 +4826,17 @@ const AGSGameDescription GAME_DESCRIPTIONS[] = {
 	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("blackwell5", "epiphany.exe", "ee9f05e549e1890a66fa29f174514d83", 247075915, 0),  //v3.0
 	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("blackwell5", "epiphany.exe", "ee9f05e549e1890a66fa29f174514d83", 247079377, 0),  //v3.1a
 	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("blackwell5", "epiphany.exe", "ee9f05e549e1890a66fa29f174514d83", 247079717, 0),  //v3.3a
+	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("blackwell5", "epiphany.exe", "f25b204923c4a08a64be69ce60369014", 247061035, 0),  //v4.2a
 	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("blackwell5", "epiphany.ags", "de7d49da1a1c5e0cd5b01a3c3770921d", 243851319, 0),  //Linux v3.0
 	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("blackwell5", "epiphany.ags", "de7d49da1a1c5e0cd5b01a3c3770921d", 243854781, 0),  //Linux v3.1a
 	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("blackwell5", "epiphany.ags", "de7d49da1a1c5e0cd5b01a3c3770921d", 243855121, 0),  //Linux v3.3a
+	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("blackwell5", "epiphany.ags", "de7d49da1a1c5e0cd5b01a3c3770921d", 243870743, 0),  //Linux v4.2a
 	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("blackwell5", "agsgame.dat", "c1cddd6fcdbcd030beda9f10d4e4270a", 281849897, 0), // Linux
 	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("blackwell5", "ac2game.dat", "8acaa20eab5589cdc2fd81ef3d55eff3", 279928291, 0), // Mac v2.3
 	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("blackwell5", "ac2game.dat", "de7d49da1a1c5e0cd5b01a3c3770921d", 243851319, 0), // Mac v3.0
 	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("blackwell5", "ac2game.dat", "de7d49da1a1c5e0cd5b01a3c3770921d", 243854781, 0), // Mac v3.1a-v3.3
 	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("blackwell5", "ac2game.dat", "de7d49da1a1c5e0cd5b01a3c3770921d", 243855121, 0), // Mac v3.3a
+	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("blackwell5", "ac2game.dat", "de7d49da1a1c5e0cd5b01a3c3770921d", 243870743, 0), // Mac v4.2a
 	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("blackwell5", "epiphany.exe", "c1cddd6fcdbcd030beda9f10d4e4270a", 281856724, GAMEFLAG_PLUGINS_AGSTEAM_WADJETEYE),
 	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("blackwell5", "epiphany.exe", "764f20abb335b94ab8ec6a4ef6db01ea", 283020359, GAMEFLAG_PLUGINS_AGSTEAM_WADJETEYE),
 	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("blackwell5", "epiphany.exe", "c3f7a995bbea7ce4ba7a2a97995c677e", 283092455, GAMEFLAG_PLUGINS_AGSTEAM_WADJETEYE),
@@ -4831,12 +4844,16 @@ const AGSGameDescription GAME_DESCRIPTIONS[] = {
 	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("blackwell5", "epiphany.exe", "ee9f05e549e1890a66fa29f174514d83", 247075644, GAMEFLAG_PLUGINS_AGSTEAM_WADJETEYE),
 	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("blackwell5", "epiphany.exe", "ee9f05e549e1890a66fa29f174514d83", 247079106, GAMEFLAG_PLUGINS_AGSTEAM_WADJETEYE),
 	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("blackwell5", "epiphany.exe", "ee9f05e549e1890a66fa29f174514d83", 247079446, GAMEFLAG_PLUGINS_AGSTEAM_WADJETEYE),
+	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("blackwell5", "epiphany.exe", "f25b204923c4a08a64be69ce60369014", 247080703, GAMEFLAG_PLUGINS_AGSTEAM_WADJETEYE),
 	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("blackwell5", "ac2game.dat", "de7d49da1a1c5e0cd5b01a3c3770921d", 243851040, GAMEFLAG_PLUGINS_AGSTEAM_WADJETEYE),  // Mac
 	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("blackwell5", "ac2game.dat", "de7d49da1a1c5e0cd5b01a3c3770921d", 243854510, GAMEFLAG_PLUGINS_AGSTEAM_WADJETEYE),  // Mac
+	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("blackwell5", "ac2game.dat", "de7d49da1a1c5e0cd5b01a3c3770921d", 243870472, GAMEFLAG_PLUGINS_AGSTEAM_WADJETEYE),  // Mac
 	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("blackwell5", "epiphany.ags", "de7d49da1a1c5e0cd5b01a3c3770921d", 243851040, GAMEFLAG_PLUGINS_AGSTEAM_WADJETEYE),  // Linux
 	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("blackwell5", "epiphany.ags", "de7d49da1a1c5e0cd5b01a3c3770921d", 243854510, GAMEFLAG_PLUGINS_AGSTEAM_WADJETEYE),  // Linux
 	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("blackwell5", "epiphany.ags", "de7d49da1a1c5e0cd5b01a3c3770921d", 243854850, GAMEFLAG_PLUGINS_AGSTEAM_WADJETEYE),  // Linux
+	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("blackwell5", "epiphany.ags", "de7d49da1a1c5e0cd5b01a3c3770921d", 243870472, GAMEFLAG_PLUGINS_AGSTEAM_WADJETEYE),  // Linux
 	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("blackwell5", "epiphany.ags", "de7d49da1a1c5e0cd5b01a3c3770921d", 243903178, GAMEFLAG_PLUGINS_AGSTEAM_WADJETEYE),  // Steamdeck
+	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("blackwell5", "epiphany.ags", "de7d49da1a1c5e0cd5b01a3c3770921d", 243921357, GAMEFLAG_PLUGINS_AGSTEAM_WADJETEYE),  // Steamdeck
 	GAME_ENTRY_EN_GOG("brownieadv1", "Brownie1.exe", "3c8d2feaba74ad4978316824c903c046", 140161903),
 	GAME_ENTRY_EN_STEAM("captaindisaster", "cd-dhamsb-1-0-1.exe", "e2d290f8f21c6a83a9e8c5f3a0425b5e", 150665897),
 	DETECTION_ENTRY_GUIO("castleagony", "Agony.exe", "387ff720e746ae46e93f463fd58d77a4", 21017019, Common::EN_ANY, "Steam", GUIO2(GUIO_NOLANG, GAMEOPTION_NO_SAVELOAD), ADGF_NO_FLAGS, 0),


Commit: 9f9becdb5d97629fae4668730512e725505824cd
    https://github.com/scummvm/scummvm/commit/9f9becdb5d97629fae4668730512e725505824cd
Author: Walter Agazzi (walter.agazzi at protonmail.com)
Date: 2026-06-09T02:43:37+02:00

Commit Message:
AGS: Update Wadjet games 1/2

Gemini Rue / Resonance

Changed paths:
    engines/ags/detection_tables.h


diff --git a/engines/ags/detection_tables.h b/engines/ags/detection_tables.h
index a13cf3dea04..da58997bb15 100644
--- a/engines/ags/detection_tables.h
+++ b/engines/ags/detection_tables.h
@@ -4959,6 +4959,7 @@ const AGSGameDescription GAME_DESCRIPTIONS[] = {
 	GAME_ENTRY_PLUGIN_GOG_NOAUTOSAVE("geminirue", "gemini rue.exe", "3b7e8fdf626c2f8e6538669cdab91c0e", 61474210, 0),  // Win v3.6
 	GAME_ENTRY_PLUGIN_GOG_NOAUTOSAVE("geminirue", "gemini rue.exe", "3b7e8fdf626c2f8e6538669cdab91c0e", 61477681, 0),  // Win v3.7
 	GAME_ENTRY_PLUGIN_GOG_NOAUTOSAVE("geminirue", "gemini rue.exe", "3b7e8fdf626c2f8e6538669cdab91c0e", 61479508, 0),  // Win v3.7.1
+	GAME_ENTRY_PLUGIN_GOG_NOAUTOSAVE("geminirue", "gemini rue.exe", "ebe0d32cd60025c9a3c6ed7e5b6c04d3", 61464543, 0),  // Win v3.7.4
 	GAME_ENTRY_PLUGIN_GOG_NOAUTOSAVE("geminirue", "ac2game.dat", "056239de34dfa52cfe1b645eee4eacb7", 70356639, 0), // Mac v2.1
 	GAME_ENTRY_PLUGIN_GOG_NOAUTOSAVE("geminirue", "ac2game.dat", "056239de34dfa52cfe1b645eee4eacb7", 70375199, 0), // Mac v2.2
 	GAME_ENTRY_PLUGIN_GOG_NOAUTOSAVE("geminirue", "ac2game.dat", "056239de34dfa52cfe1b645eee4eacb7", 70412031, 0), // Mac v2.3
@@ -4968,6 +4969,7 @@ const AGSGameDescription GAME_DESCRIPTIONS[] = {
 	GAME_ENTRY_PLUGIN_GOG_NOAUTOSAVE("geminirue", "ac2game.dat", "ab6c0c2d89adb2d529131190a657be6a", 58284358, 0), // Mac v3.5f
 	GAME_ENTRY_PLUGIN_GOG_NOAUTOSAVE("geminirue", "ac2game.dat", "ab6c0c2d89adb2d529131190a657be6a", 58285454, 0), // Mac v3.6
 	GAME_ENTRY_PLUGIN_GOG_NOAUTOSAVE("geminirue", "ac2game.dat", "ab6c0c2d89adb2d529131190a657be6a", 58290752, 0), // Mac v3.7.1
+	GAME_ENTRY_PLUGIN_GOG_NOAUTOSAVE("geminirue", "ac2game.dat", "ab6c0c2d89adb2d529131190a657be6a", 58310091, 0), // Mac v3.7.4
 	GAME_ENTRY_PLUGIN_GOG_NOAUTOSAVE("geminirue", "agsgame.dat", "f3c0c7d3892bdd8963e8ce017f73de08", 62069353, 0), // Linux
 	GAME_ENTRY_PLUGIN_GOG_NOAUTOSAVE("geminirue", "Gemini Rue.ags", "ab6c0c2d89adb2d529131190a657be6a", 58212622, 0),  // Linux v3.0
 	GAME_ENTRY_PLUGIN_GOG_NOAUTOSAVE("geminirue", "Gemini Rue.ags", "ab6c0c2d89adb2d529131190a657be6a", 58249454, 0),  // Linux v3.0.1
@@ -4975,6 +4977,7 @@ const AGSGameDescription GAME_DESCRIPTIONS[] = {
 	GAME_ENTRY_PLUGIN_GOG_NOAUTOSAVE("geminirue", "Gemini Rue.ags", "ab6c0c2d89adb2d529131190a657be6a", 58284358, 0),  // Linux v3.5d
 	GAME_ENTRY_PLUGIN_GOG_NOAUTOSAVE("geminirue", "Gemini Rue.ags", "ab6c0c2d89adb2d529131190a657be6a", 58285454, 0),  // Linux v3.6
 	GAME_ENTRY_PLUGIN_GOG_NOAUTOSAVE("geminirue", "Gemini Rue.ags", "ab6c0c2d89adb2d529131190a657be6a", 58290752, 0),  // Linux v3.7.1
+	GAME_ENTRY_PLUGIN_GOG_NOAUTOSAVE("geminirue", "Gemini Rue.ags", "ab6c0c2d89adb2d529131190a657be6a", 58310091, 0),  // Linux v3.7.4
 	GAME_ENTRY("geminirue", "gemini rue.exe", "0bdfb2139abaae5ae2dc85f1aacfd3f6", 61926695),
 	DETECTION_ENTRY("geminirue", "gemini rue.exe", "0bdfb2139abaae5ae2dc85f1aacfd3f6", 64650419, Common::DE_DEU, nullptr, ADGF_NO_FLAGS, GAMEFLAG_FORCE_AA), // German retail
 	GAME_ENTRY_LANG("geminirue", "gemini rue.exe", "0bdfb2139abaae5ae2dc85f1aacfd3f6", 64654187, Common::RU_RUS),  // Russian (Logrus)
@@ -4986,12 +4989,14 @@ const AGSGameDescription GAME_DESCRIPTIONS[] = {
 	GAME_ENTRY_PLUGIN_STEAM_NOAUTOSAVE("geminirue", "gemini rue.exe", "3b7e8fdf626c2f8e6538669cdab91c0e", 61473936, GAMEFLAG_PLUGINS_AGSTEAM_WADJETEYE),  // Win v3.6
 	GAME_ENTRY_PLUGIN_STEAM_NOAUTOSAVE("geminirue", "gemini rue.exe", "3b7e8fdf626c2f8e6538669cdab91c0e", 61477407, GAMEFLAG_PLUGINS_AGSTEAM_WADJETEYE),  // Win v3.7
 	GAME_ENTRY_PLUGIN_STEAM_NOAUTOSAVE("geminirue", "gemini rue.exe", "3b7e8fdf626c2f8e6538669cdab91c0e", 61479234, GAMEFLAG_PLUGINS_AGSTEAM_WADJETEYE),  // Win v3.7.1
+	GAME_ENTRY_PLUGIN_STEAM_NOAUTOSAVE("geminirue", "gemini rue.exe", "ebe0d32cd60025c9a3c6ed7e5b6c04d3", 61464269, GAMEFLAG_PLUGINS_AGSTEAM_WADJETEYE),  // Win v3.7.4
 	GAME_ENTRY_PLUGIN_STEAM_NOAUTOSAVE("geminirue", "ac2game.dat", "ab6c0c2d89adb2d529131190a657be6a", 58249180, GAMEFLAG_PLUGINS_AGSTEAM_WADJETEYE),  // Mac
 	GAME_ENTRY_PLUGIN_STEAM_NOAUTOSAVE("geminirue", "ac2game.dat", "ab6c0c2d89adb2d529131190a657be6a", 58283296, GAMEFLAG_PLUGINS_AGSTEAM_WADJETEYE),  // Mac v3.2
 	GAME_ENTRY_PLUGIN_STEAM_NOAUTOSAVE("geminirue", "ac2game.dat", "ab6c0c2d89adb2d529131190a657be6a", 58284084, GAMEFLAG_PLUGINS_AGSTEAM_WADJETEYE),  // Mac v3.5
 	GAME_ENTRY_PLUGIN_STEAM_NOAUTOSAVE("geminirue", "ac2game.dat", "ab6c0c2d89adb2d529131190a657be6a", 58285180, GAMEFLAG_PLUGINS_AGSTEAM_WADJETEYE),  // Mac v3.6
 	GAME_ENTRY_PLUGIN_STEAM_NOAUTOSAVE("geminirue", "ac2game.dat", "ab6c0c2d89adb2d529131190a657be6a", 58288651, GAMEFLAG_PLUGINS_AGSTEAM_WADJETEYE),  // Mac v3.7
 	GAME_ENTRY_PLUGIN_STEAM_NOAUTOSAVE("geminirue", "ac2game.dat", "ab6c0c2d89adb2d529131190a657be6a", 58290478, GAMEFLAG_PLUGINS_AGSTEAM_WADJETEYE),  // Mac v3.7.1
+	GAME_ENTRY_PLUGIN_STEAM_NOAUTOSAVE("geminirue", "ac2game.dat", "ab6c0c2d89adb2d529131190a657be6a", 58309817, GAMEFLAG_PLUGINS_AGSTEAM_WADJETEYE),  // Mac v3.7.4
 	GAME_ENTRY_PLUGIN_STEAM_NOAUTOSAVE("geminirue", "agsgame.dat", "f3c0c7d3892bdd8963e8ce017f73de08", 62059297, GAMEFLAG_PLUGINS_AGSTEAM_WADJETEYE),  // Linux
 	GAME_ENTRY_PLUGIN_STEAM_NOAUTOSAVE("geminirue", "Gemini Rue.ags", "ab6c0c2d89adb2d529131190a657be6a", 58249180, GAMEFLAG_PLUGINS_AGSTEAM_WADJETEYE),  //Linux v3.0
 	GAME_ENTRY_PLUGIN_STEAM_NOAUTOSAVE("geminirue", "Gemini Rue.ags", "ab6c0c2d89adb2d529131190a657be6a", 58283296, GAMEFLAG_PLUGINS_AGSTEAM_WADJETEYE),  //Linux v3.2
@@ -4999,7 +5004,9 @@ const AGSGameDescription GAME_DESCRIPTIONS[] = {
 	GAME_ENTRY_PLUGIN_STEAM_NOAUTOSAVE("geminirue", "Gemini Rue.ags", "ab6c0c2d89adb2d529131190a657be6a", 58285180, GAMEFLAG_PLUGINS_AGSTEAM_WADJETEYE),  //Linux v3.6
 	GAME_ENTRY_PLUGIN_STEAM_NOAUTOSAVE("geminirue", "Gemini Rue.ags", "ab6c0c2d89adb2d529131190a657be6a", 58288651, GAMEFLAG_PLUGINS_AGSTEAM_WADJETEYE),  //Linux v3.7
 	GAME_ENTRY_PLUGIN_STEAM_NOAUTOSAVE("geminirue", "Gemini Rue.ags", "ab6c0c2d89adb2d529131190a657be6a", 58290478, GAMEFLAG_PLUGINS_AGSTEAM_WADJETEYE),  //Linux v3.7.1
+	GAME_ENTRY_PLUGIN_STEAM_NOAUTOSAVE("geminirue", "Gemini Rue.ags", "ab6c0c2d89adb2d529131190a657be6a", 58309817, GAMEFLAG_PLUGINS_AGSTEAM_WADJETEYE),  //Linux v3.7.4
 	GAME_ENTRY_PLUGIN_STEAM_NOAUTOSAVE("geminirue", "Gemini Rue.ags", "ab6c0c2d89adb2d529131190a657be6a", 58296560, GAMEFLAG_PLUGINS_AGSTEAM_WADJETEYE),  //Steamdeck
+	GAME_ENTRY_PLUGIN_STEAM_NOAUTOSAVE("geminirue", "Gemini Rue.ags", "ab6c0c2d89adb2d529131190a657be6a", 58317436, GAMEFLAG_PLUGINS_AGSTEAM_WADJETEYE),  //Steamdeck
 	GAME_ENTRY("geminirue", "gemini_rue_pc.exe", "e8f1d07a6b363e9cc80dac5367f1b4ba", 72860463), // Humble Bundle
 	GAME_ENTRY("geminirue", "ac2game.dat", "f3c0c7d3892bdd8963e8ce017f73de08", 62852566), // Android
 	GAME_ENTRY("geminirue", "ac2game.dat", "e8f1d07a6b363e9cc80dac5367f1b4ba", 72836785), // MacOS, Humble Bundle
@@ -5288,30 +5295,37 @@ const AGSGameDescription GAME_DESCRIPTIONS[] = {
 	GAME_ENTRY_EN_GOG("resonance", "resonance.exe", "b6ce764bf83d025c7c0aa50ceb006e09", 732933098),  // v3.0
 	GAME_ENTRY_EN_GOG("resonance", "resonance.exe", "b6ce764bf83d025c7c0aa50ceb006e09", 732944570),  // v3.1c
 	GAME_ENTRY_EN_GOG("resonance", "resonance.exe", "b6ce764bf83d025c7c0aa50ceb006e09", 732944702),  // v3.5
+	GAME_ENTRY_EN_GOG("resonance", "resonance.exe", "afe3fbae02099a90dcaa608b1f921566", 732926985),  // v3.6
 	GAME_ENTRY_EN_GOG("resonance", "ac2game.dat", "1dd9f78fbd1947fdf66a820928baf8a4", 847433642), // Mac v2.3
 	GAME_ENTRY_EN_GOG("resonance", "ac2game.dat", "920f99ac1e32f07ccde5c960aac52d8c", 729663446), // Mac v3.0
 	GAME_ENTRY_EN_GOG("resonance", "ac2game.dat", "920f99ac1e32f07ccde5c960aac52d8c", 729674918), // Mac v3.2d
 	GAME_ENTRY_EN_GOG("resonance", "ac2game.dat", "920f99ac1e32f07ccde5c960aac52d8c", 729675050), // Mac v3.5
+	GAME_ENTRY_EN_GOG("resonance", "ac2game.dat", "920f99ac1e32f07ccde5c960aac52d8c", 729691637), // Mac v3.6
 	GAME_ENTRY_EN_GOG("resonance", "agsgame.dat", "2e635c22bcbf0ed3d46f1bcde71812d4", 849404957), // Linux
 	GAME_ENTRY_EN_GOG("resonance", "resonance.ags", "920f99ac1e32f07ccde5c960aac52d8c", 729663446), // Linux 3.0
 	GAME_ENTRY_EN_GOG("resonance", "resonance.ags", "920f99ac1e32f07ccde5c960aac52d8c", 729674918), // Linux 3.0d
 	GAME_ENTRY_EN_GOG("resonance", "resonance.ags", "920f99ac1e32f07ccde5c960aac52d8c", 729675050), // Linux 3.5
+	GAME_ENTRY_EN_GOG("resonance", "resonance.ags", "920f99ac1e32f07ccde5c960aac52d8c", 729691637), // Linux 3.6
 	GAME_ENTRY_PLUGIN_STEAM_EN("resonance", "resonance.exe", "2e635c22bcbf0ed3d46f1bcde71812d4", 849410915, GAMEFLAG_PLUGINS_AGSTEAM_WADJETEYE),  // Win original
 	GAME_ENTRY_PLUGIN_STEAM_EN("resonance", "resonance.exe", "6cc23ce7cdf62de776c6b8ddb6b8a7ff", 850642889, GAMEFLAG_PLUGINS_AGSTEAM_WADJETEYE),  // Win-Linux
 	GAME_ENTRY_PLUGIN_STEAM_EN("resonance", "resonance.exe", "b6ce764bf83d025c7c0aa50ceb006e09", 732932834, GAMEFLAG_PLUGINS_AGSTEAM_WADJETEYE),  // Win 3.0
 	GAME_ENTRY_PLUGIN_STEAM_EN("resonance", "resonance.exe", "b6ce764bf83d025c7c0aa50ceb006e09", 732932842, GAMEFLAG_PLUGINS_AGSTEAM_WADJETEYE),  // Win 3.0 updated
 	GAME_ENTRY_PLUGIN_STEAM_EN("resonance", "resonance.exe", "b6ce764bf83d025c7c0aa50ceb006e09", 732944314, GAMEFLAG_PLUGINS_AGSTEAM_WADJETEYE),  // Win 3.1
 	GAME_ENTRY_PLUGIN_STEAM_EN("resonance", "resonance.exe", "b6ce764bf83d025c7c0aa50ceb006e09", 732944446, GAMEFLAG_PLUGINS_AGSTEAM_WADJETEYE),  // Win 3.5
+	GAME_ENTRY_PLUGIN_STEAM_EN("resonance", "resonance.exe", "afe3fbae02099a90dcaa608b1f921566", 732926729, GAMEFLAG_PLUGINS_AGSTEAM_WADJETEYE),  // Win 3.6
 	GAME_ENTRY_PLUGIN_STEAM_EN("resonance", "resonance.ags", "920f99ac1e32f07ccde5c960aac52d8c", 729663182, GAMEFLAG_PLUGINS_AGSTEAM_WADJETEYE),  // Linux 3.0
 	GAME_ENTRY_PLUGIN_STEAM_EN("resonance", "resonance.ags", "920f99ac1e32f07ccde5c960aac52d8c", 729674662, GAMEFLAG_PLUGINS_AGSTEAM_WADJETEYE),  // Linux 3.0d
 	GAME_ENTRY_PLUGIN_STEAM_EN("resonance", "resonance.ags", "920f99ac1e32f07ccde5c960aac52d8c", 729674794, GAMEFLAG_PLUGINS_AGSTEAM_WADJETEYE),  // Linux 3.5
+	GAME_ENTRY_PLUGIN_STEAM_EN("resonance", "resonance.ags", "920f99ac1e32f07ccde5c960aac52d8c", 729691381, GAMEFLAG_PLUGINS_AGSTEAM_WADJETEYE),  // Linux 3.6
 	GAME_ENTRY_PLUGIN_STEAM_EN("resonance", "resonance.ags", "920f99ac1e32f07ccde5c960aac52d8c", 729681114, GAMEFLAG_PLUGINS_AGSTEAM_WADJETEYE),  // Steamdeck
 	GAME_ENTRY_PLUGIN_STEAM_EN("resonance", "resonance.ags", "920f99ac1e32f07ccde5c960aac52d8c", 729681246, GAMEFLAG_PLUGINS_AGSTEAM_WADJETEYE),  // Steamdeck
+	GAME_ENTRY_PLUGIN_STEAM_EN("resonance", "resonance.ags", "920f99ac1e32f07ccde5c960aac52d8c", 729699044, GAMEFLAG_PLUGINS_AGSTEAM_WADJETEYE),  // Steamdeck
 	GAME_ENTRY_PLUGIN_STEAM_EN("resonance", "ac2game.dat", "1dd9f78fbd1947fdf66a820928baf8a4", 847433141, GAMEFLAG_PLUGINS_AGSTEAM_WADJETEYE),  // Mac
 	GAME_ENTRY_PLUGIN_STEAM_EN("resonance", "ac2game.dat", "920f99ac1e32f07ccde5c960aac52d8c", 729663182, GAMEFLAG_PLUGINS_AGSTEAM_WADJETEYE),  // Mac 3.0
 	GAME_ENTRY_PLUGIN_STEAM_EN("resonance", "ac2game.dat", "920f99ac1e32f07ccde5c960aac52d8c", 729663190, GAMEFLAG_PLUGINS_AGSTEAM_WADJETEYE),  // Mac 3.0 updated
 	GAME_ENTRY_PLUGIN_STEAM_EN("resonance", "ac2game.dat", "920f99ac1e32f07ccde5c960aac52d8c", 729674662, GAMEFLAG_PLUGINS_AGSTEAM_WADJETEYE),  // Mac 3.2
 	GAME_ENTRY_PLUGIN_STEAM_EN("resonance", "ac2game.dat", "920f99ac1e32f07ccde5c960aac52d8c", 729674794, GAMEFLAG_PLUGINS_AGSTEAM_WADJETEYE),  // Mac 3.5
+	GAME_ENTRY_PLUGIN_STEAM_EN("resonance", "ac2game.dat", "920f99ac1e32f07ccde5c960aac52d8c", 729691381, GAMEFLAG_PLUGINS_AGSTEAM_WADJETEYE),  // Mac 3.6
 	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("richardandalice", "richardandalice.exe", "1023d348f67ba958f78ed62d029565c7", 109164768, 0),
 	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("richardandalice", "richardandalice.exe", "f47d11397e2059a25b11057ea6cf3409", 108264256, 0),
 	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("richardandalice", "richardandalice.exe", "9a7e38a1810e6a1839601fb5c6bd42e6", 108602238, 0), // Desura - GOG v1.0



From noreply at scummvm.org  Tue Jun  9 08:43:12 2026
From: noreply at scummvm.org (dreammaster)
Date: Tue,  9 Jun 2026 08:43:12 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 12adf7f0700c0ec4e01cdfde0cd40a5fc3e3cbcd
Message-ID: <20260609084312.D422E7005BC@ron.scummvm.net>

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:
98bc7ae0bf MADS: FOREST: Implemented room 302
12adf7f070 MADS: FOREST: Implemented room 303


Commit: 98bc7ae0bf2323f98d7391516e6e38f4a6b96a07
    https://github.com/scummvm/scummvm/commit/98bc7ae0bf2323f98d7391516e6e38f4a6b96a07
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-06-09T18:42:48+10:00

Commit Message:
MADS: FOREST: Implemented room 302

Changed paths:
    engines/mads/madsv2/forest/rooms/room302.cpp


diff --git a/engines/mads/madsv2/forest/rooms/room302.cpp b/engines/mads/madsv2/forest/rooms/room302.cpp
index 155a4e5dfe4..2e86c40f7cc 100644
--- a/engines/mads/madsv2/forest/rooms/room302.cpp
+++ b/engines/mads/madsv2/forest/rooms/room302.cpp
@@ -20,16 +20,21 @@
  */
 
 #include "mads/madsv2/core/conv.h"
+#include "mads/madsv2/core/digi.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/imath.h"
 #include "mads/madsv2/core/inter.h"
 #include "mads/madsv2/core/kernel.h"
+#include "mads/madsv2/core/midi.h"
+#include "mads/madsv2/core/object.h"
+#include "mads/madsv2/core/player.h"
 #include "mads/madsv2/core/sound.h"
 #include "mads/madsv2/core/text.h"
 #include "mads/madsv2/forest/mads/inventory.h"
 #include "mads/madsv2/forest/mads/sounds.h"
 #include "mads/madsv2/forest/mads/words.h"
 #include "mads/madsv2/forest/global.h"
+#include "mads/madsv2/forest/journal.h"
 #include "mads/madsv2/forest/rooms/section1.h"
 #include "mads/madsv2/forest/rooms/room302.h"
 
@@ -39,10 +44,27 @@ namespace Forest {
 namespace Rooms {
 
 struct Scratch {
-	int16 sprite[10];       /* Sprite series handles */
-	int16 sequence[10];     /* Sequence handles      */
-	int16 animation[10];     /* Animation handles     */
+	int16 sprite[10];
+	int16 sequence[10];
+	int16 animation[10];
 	AnimationInfo animation_info[10];
+	int16 _8c;
+	int16 _8e;
+	int16 _90;
+	int16 _92;
+	int16 _94;
+	int16 _96;
+	int16 _98;
+	int16 _9a;
+	int16 _9c;
+	int16 _9e;
+	int16 _a0;
+	int16 _a2;
+	int16 _a4;
+	int16 _a6;
+	int16 _a8;
+	int16 _aa;
+	int16 _ac;
 };
 
 static Scratch scratch;
@@ -54,22 +76,883 @@ static Scratch scratch;
 #define aainfo scratch.animation_info
 
 
+static void room_302_init1() {
+	global[g131] = 0;
+	global[g141] = 0;
+
+	scratch._9a = kernel_run_animation_disp('e', 1, 0);
+	kernel_position_anim(scratch._9a, 104, 109, 68, 9);
+
+	scratch._9c = kernel_run_animation_disp('r', 3, 0);
+	kernel_position_anim(scratch._9c, 137, 117, 73, 8);
+
+	if (previous_room != KERNEL_LAST) {
+		player.x = 178;
+		player.y = 124;
+		player.facing = 1;
+	}
+
+	if (previous_room == 210) {
+		aa[0] = kernel_run_animation(kernel_name('y', 1), 100);
+		scratch.animation_info[0]._active = -1;
+		scratch._92 = 66;
+		return;
+	}
+	if (previous_room == 301) {
+		aa[0] = kernel_run_animation(kernel_name('y', 2), 100);
+		scratch.animation_info[0]._active = -1;
+		scratch._92 = 67;
+		return;
+	}
+	if (previous_room == 303 || previous_room == 321) {
+		aa[0] = kernel_run_animation(kernel_name('y', 3), 100);
+		scratch.animation_info[0]._active = -1;
+		scratch._92 = 90;
+		return;
+	}
+	if (previous_room == 304) {
+		aa[0] = kernel_run_animation(kernel_name('y', 4), 100);
+		scratch.animation_info[0]._active = -1;
+		scratch._92 = 53;
+		return;
+	}
+	if (previous_room == 199) {
+		global[g131] = -1;
+		global[g141] = -1;
+		kernel_reset_animation(scratch._9a, 2);
+		kernel_reset_animation(scratch._9c, 2);
+		global[g133] = 0;
+		global[g143] = 0;
+		restore_player();
+		player.commands_allowed = -1;
+		player.walker_visible = -1;
+		return;
+	}
+	if (previous_room == KERNEL_LAST) {
+		global[g131] = -1;
+		global[g141] = -1;
+		kernel_reset_animation(scratch._9a, 2);
+		kernel_reset_animation(scratch._9c, 2);
+		global[g133] = 0;
+		global[g143] = 0;
+		return;
+	}
+
+	// default ??? other previous rooms
+	global[g131] = -1;
+	global[g141] = -1;
+	kernel_reset_animation(scratch._9a, 2);
+	kernel_reset_animation(scratch._9c, 2);
+	global[g133] = 0;
+	global[g143] = 0;
+	player.commands_allowed = -1;
+	player.walker_visible = -1;
+	kernel_flip_hotspot(119, global[g048] == 0 ? 1 : 0);
+	kernel_flip_hotspot(27, global[g048]);
+	kernel_flip_hotspot(75, global[g067] == 0 ? 1 : 0);
+	kernel_flip_hotspot(37, global[g067]);
+}
+
 static void room_302_init() {
+	scratch._aa = 0;
+	scratch._a8 = -1;
+	scratch._a4 = 0;
+	global[walker_converse_now] = 0;
+	scratch._ac = 0;
+	global[g009] = 0;
+
+	if (previous_room != 304)
+		midi_stop();
+
+	global[perform_displacements] = 240;
+	global[player_score] = -1;
+
+	if (object_is_here(1)) {
+		ss[0] = kernel_load_series(kernel_name('P', 3), 0);
+		seq[0] = kernel_seq_stamp(ss[0], false, KERNEL_FIRST);
+		kernel_seq_depth(seq[0], 9);
+		kernel_seq_loc(seq[0], 244, 105);
+		kernel_seq_scale(seq[0], 65);
+	} else {
+		kernel_flip_hotspot(137, 0);
+	}
+
+	ss[1] = kernel_load_series(kernel_name('p', 1), 0);
+	ss[2] = kernel_load_series(kernel_name('p', 2), 0);
+	ss[3] = kernel_load_series("*rm302p21", 0);
+
+	if (global[g048]) {
+		kernel_flip_hotspot(173, 0);
+		kernel_flip_hotspot(27, -1);
+		seq[2] = kernel_seq_stamp(ss[2], false, KERNEL_FIRST);
+		kernel_seq_depth(seq[2], 15);
+		kernel_seq_loc(seq[2], 33, 86);
+		kernel_seq_scale(seq[2], 52);
+		seq[3] = kernel_seq_stamp(ss[3], false, KERNEL_FIRST);
+		kernel_seq_depth(seq[3], 15);
+		kernel_seq_loc(seq[3], 15, 86);
+		kernel_seq_scale(seq[3], 52);
+	} else {
+		kernel_flip_hotspot(173, -1);
+		kernel_flip_hotspot(27, 0);
+		seq[1] = kernel_seq_stamp(ss[1], false, KERNEL_FIRST);
+		kernel_seq_depth(seq[1], 15);
+		kernel_seq_loc(seq[1], 18, 90);
+		kernel_seq_scale(seq[1], 55);
+	}
+
+	if (global[g067]) {
+		kernel_flip_hotspot(75, 0);
+		kernel_flip_hotspot(37, -1);
+	} else {
+		kernel_flip_hotspot(75, -1);
+		kernel_flip_hotspot(37, 0);
+	}
+
+	if (previous_room != KERNEL_LAST) {
+		if (previous_room != 199) {
+			player.walker_visible = 0;
+			player.commands_allowed = 0;
+		}
+
+		for (int i = 0; i < 10; i++) {
+			scratch.animation_info[i]._active = 0;
+			scratch.animation_info[i]._frame = 0;
+			scratch.animation_info[i]._val3 = 0;
+			scratch.animation_info[i]._val4 = 0;
+		}
+
+		if (previous_room != 199) {
+			if (flags[16] != 3)
+				flags[16]++;
+		}
+	}
+
+	room_302_init1();
+}
+
+static void room_302_anim1() {
+	if (kernel_anim[aa[0]].frame != scratch.animation_info[0]._frame)
+		scratch.animation_info[0]._frame = kernel_anim[aa[0]].frame;
+	if (global[player_hyperwalked] == -1) {
+		scratch.animation_info[0]._frame = scratch._92 - 1;
+		kernel_reset_animation(aa[0], scratch.animation_info[0]._frame);
+	}
+}
+
+static void room_302_anim2() {
+	if (!scratch._a4)
+		return;
+
+	if (!scratch._ac)
+		kernel_random_frame(scratch._a2, &global[g153], global[g156]);
+	kernel_random_frame(scratch._9e, &global[g151], global[g154]);
+
+	if (kernel.trigger != 7 && kernel.trigger != 28)
+		return;
+
+	int16 a4 = scratch._a4;
+	if (a4 == 2) {
+		global[g154] = 0;
+		kernel_timing_trigger(30, 28);
+		scratch._a4 = 3;
+	} else if (a4 == 3) {
+		global[g156] = 2;
+		scratch._a4 = 4;
+		digi_play_build(302, 'E', 1, 1);
+	} else if (a4 == 4) {
+		global[g156] = 0;
+		kernel_timing_trigger(30, 28);
+		scratch._a4 = 5;
+	} else if (a4 == 5) {
+		scratch._a4 = 6;
+		kernel_abort_animation(scratch._a2);
+		kernel_abort_animation(scratch._9e);
+		player.commands_allowed = -1;
+		player.walker_visible = -1;
+		global[g017] = -1;
+		scratch.animation_info[1]._active = 0;
+		kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
+		global[g131] = -1;
+		kernel_reset_animation(scratch._9a, 1);
+		kernel_synch(KERNEL_ANIM, scratch._9a, KERNEL_NOW, 0);
+		global[g133] = 0;
+	}
+}
+
+static void room_302_anim3() {
+	int16 frame = (int16)kernel_anim[aa[2]].frame;
+	if (frame != scratch.animation_info[2]._frame)
+		scratch.animation_info[2]._frame = frame;
+
+	if (global[player_hyperwalked] == -1 && frame <= 20) {
+		scratch.animation_info[2]._frame = 20;
+		kernel_reset_animation(aa[2], 20);
+		kernel_synch(KERNEL_ANIM, aa[2], KERNEL_NOW, 0);
+		return;
+	}
+	if (global[player_hyperwalked] == -1 || frame == 40) {
+		scratch.animation_info[2]._frame = 39;
+		kernel_reset_animation(aa[2], 39);
+		kernel_synch(KERNEL_ANIM, aa[2], KERNEL_NOW, 0);
+	}
+}
+
+static void room_302_anim4() {
+	if (kernel_anim[aa[3]].frame != scratch.animation_info[3]._frame)
+		scratch.animation_info[3]._frame = kernel_anim[aa[3]].frame;
+}
+
+static void room_302_anim5() {
+	int16 frame = (int16)kernel_anim[aa[4]].frame;
+
+	if (frame != scratch.animation_info[4]._frame) {
+		scratch.animation_info[4]._frame = frame;
+
+		if (scratch._a8 == 1) {
+			if (frame == 13) {
+				if (scratch.animation_info[4]._val3 == 23) {
+					scratch.animation_info[4]._frame = 11;
+					kernel_reset_animation(aa[4], 11);
+				}
+			} else if (frame <= 13) {
+				if (frame == 5) {
+					scratch.animation_info[4]._val3 = 21;
+					digi_play_build_ii('e', 8, 1);
+					scratch._a4 = 5;
+				} else if (frame == 10) {
+					if (scratch.animation_info[4]._val3 == 21) {
+						scratch.animation_info[4]._frame = 5;
+						kernel_reset_animation(aa[4], 5);
+					}
+				}
+			}
+		} else if (scratch._a8 == 2) {
+			if (frame == 61) {
+				if (scratch.animation_info[4]._val3 == 23) {
+					scratch.animation_info[4]._frame = 58;
+					kernel_reset_animation(aa[4], 58);
+				}
+			} else if (frame <= 61) {
+				if (frame == 53) {
+					scratch.animation_info[4]._val3 = 21;
+					digi_play_build_ii('e', 9, 1);
+					scratch._a4 = 500;
+				} else if (frame == 57) {
+					if (scratch.animation_info[4]._val3 == 21) {
+						scratch.animation_info[4]._frame = 53;
+						kernel_reset_animation(aa[4], 53);
+					}
+				}
+			}
+		} else if (scratch._a8 == 3) {
+			if (frame == 5) {
+				scratch.animation_info[4]._val3 = 21;
+				digi_play_build(302, 'e', 2, 1);
+				scratch._a4 = 50;
+			} else if (frame == 12) {
+				if (scratch.animation_info[4]._val3 == 21) {
+					scratch.animation_info[4]._frame = 5;
+					kernel_reset_animation(aa[4], 5);
+				}
+			}
+		}
+	}
+
+	if (kernel.trigger != 7 && kernel.trigger != 28)
+		return;
+
+	int16 a4 = scratch._a4;
+	if (a4 == 50) {
+		scratch.animation_info[4]._val3 = 19;
+		scratch.animation_info[4]._frame = 11;
+		kernel_reset_animation(aa[4], 11);
+		scratch._a4 = -1;
+	} else if (a4 < 50) {
+		if (a4 == 5) {
+			kernel_timing_trigger(30, 28);
+			scratch.animation_info[4]._val3 = 23;
+			scratch._a4 = 6;
+		} else if (a4 == 6) {
+			scratch.animation_info[4]._val3 = 21;
+			scratch.animation_info[4]._frame = 5;
+			kernel_reset_animation(aa[4], 5);
+			digi_play_build_ii('e', 10, 1);
+			scratch._a4 = 7;
+		} else if (a4 == 7) {
+			scratch.animation_info[4]._val3 = 19;
+			scratch.animation_info[4]._frame = 11;
+			kernel_reset_animation(aa[4], 11);
+			scratch._a4 = -1;
+		}
+	} else {
+		if (a4 == 500) {
+			kernel_timing_trigger(30, 28);
+			scratch.animation_info[4]._val3 = 23;
+			scratch._a4 = 501;
+		} else if (a4 == 501) {
+			scratch.animation_info[4]._val3 = 21;
+			scratch.animation_info[4]._frame = 53;
+			kernel_reset_animation(aa[4], 53);
+			digi_play_build_ii('e', 10, 1);
+			scratch._a4 = 502;
+		} else if (a4 == 502) {
+			scratch.animation_info[4]._val3 = 19;
+			scratch.animation_info[4]._frame = 58;
+			kernel_reset_animation(aa[4], 58);
+			scratch._a4 = -1;
+		}
+	}
+}
+
+static void room_302_anim6() {
+	int16 frame = (int16)kernel_anim[aa[5]].frame;
+
+	if (frame != scratch.animation_info[5]._frame) {
+		scratch.animation_info[5]._frame = frame;
+
+		if (scratch._a8 == 1) {
+			if (frame == 58) {
+				if (scratch.animation_info[5]._val3 == 23) {
+					digi_play_build(101, '_', 1, 2);
+					scratch._a4 = 58;
+				}
+			} else if (frame < 58) {
+				if (frame == 5) {
+					scratch.animation_info[5]._val3 = 21;
+					digi_play_build(302, 'e', 3, 1);
+					scratch._a4 = 5;
+				} else if (frame == 10) {
+					if (scratch.animation_info[5]._val3 == 21) {
+						scratch.animation_info[5]._frame = 5;
+						kernel_reset_animation(aa[5], 5);
+					}
+				}
+			}
+		}
+	}
+
+	if (kernel.trigger == 7 || kernel.trigger == 28) {
+		if (scratch._a4 == 5) {
+			scratch.animation_info[5]._val3 = 23;
+			scratch.animation_info[5]._frame = 11;
+			kernel_reset_animation(aa[5], 11);
+			scratch._a4 = -1;
+			kernel_seq_delete(seq[0]);
+		}
+	}
+
+	if (kernel.trigger == 8 || kernel.trigger == 28) {
+		int16 a4 = scratch._a4;
+		if (a4 == 9 || a4 == 58)
+			scratch._a4 = -1;
+	}
+}
+
+static void room_302_anim7() {
+	int16 result = -1;
+	int16 frame = (int16)kernel_anim[aa[6]].frame;
+
+	if (frame != scratch.animation_info[6]._frame) {
+		scratch.animation_info[6]._frame = frame;
+
+		if (scratch._98 == 104 && scratch._a8 == 1) {
+			if (frame == 72) {
+				if (scratch.animation_info[6]._val3 == 23) {
+					scratch.animation_info[6]._frame = 70;
+					kernel_reset_animation(aa[6], 70);
+				}
+			} else if (frame < 72) {
+				if (frame == 14) {
+					if (scratch.animation_info[6]._val3 == 23) {
+						scratch.animation_info[6]._frame = 12;
+						kernel_reset_animation(aa[6], 12);
+					}
+				} else if (frame < 14) {
+					if (frame == 6) {
+						scratch.animation_info[6]._val3 = 22;
+						digi_play_build(302, 'r', 1, 1);
+						scratch._a4 = 6;
+						scratch._a8 = 1;
+					} else if (frame == 11) {
+						if (scratch.animation_info[6]._val3 == 22) {
+							scratch.animation_info[6]._frame = 6;
+							kernel_reset_animation(aa[6], 6);
+						}
+					}
+				} else {
+					if (frame == 66) {
+						scratch.animation_info[6]._val3 = 22;
+						digi_play_build(302, 'r', 2, 1);
+						scratch._a4 = 66;
+					} else if (frame == 70) {
+						if (scratch.animation_info[6]._val3 == 22) {
+							scratch.animation_info[6]._frame = 66;
+							kernel_reset_animation(aa[6], 66);
+						}
+					}
+				}
+			}
+		}
+	}
+
+	if (result >= 0) {
+		scratch.animation_info[6]._frame = result;
+		kernel_reset_animation(aa[6], result);
+	}
+
+	if (kernel.trigger != 7 && kernel.trigger != 28)
+		return;
+
+	int16 a4 = scratch._a4;
+	if (a4 == 67) {
+		scratch.animation_info[6]._val3 = 19;
+		scratch.animation_info[6]._frame = 71;
+		kernel_reset_animation(aa[6], 71);
+		scratch._a4 = -1;
+	} else if (a4 > 67) {
+		return;
+	} else if (a4 == 6) {
+		kernel_timing_trigger(45, 28);
+		scratch.animation_info[6]._val3 = 23;
+		scratch._a4 = 7;
+	} else if (a4 == 7) {
+		scratch.animation_info[6]._val3 = 19;
+		scratch.animation_info[6]._frame = 12;
+		kernel_reset_animation(aa[6], 12);
+		scratch._a4 = -1;
+	} else if (a4 == 66) {
+		kernel_timing_trigger(45, 28);
+		scratch.animation_info[6]._val3 = 23;
+		scratch._a4 = 67;
+	}
+}
+
+static void room_302_anim8() {
+	if (kernel_anim[aa[7]].frame != scratch.animation_info[7]._frame)
+		scratch.animation_info[7]._frame = kernel_anim[aa[7]].frame;
 }
 
 static void room_302_daemon() {
+	switch (kernel.trigger) {
+	case 7:
+		if (global[walker_converse_state] != 0) {
+			global[walker_converse_state] = 0;
+			close_journal(3);
+			player.commands_allowed = -1;
+		} else {
+			if (scratch._a4 == 200) {
+				kernel_abort_animation(scratch._9e);
+				scratch._a4 = -1;
+				player.walker_visible = -1;
+				scratch._aa = 0;
+				kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
+				global[g150] = -1;
+				scratch._a8 = 1;
+				scratch._90 = 104;
+			} else if (scratch._a4 == 400) {
+				kernel_abort_animation(scratch._9e);
+				scratch._a4 = -1;
+				player.walker_visible = -1;
+				scratch._aa = 0;
+				kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
+				scratch._a8 = 1;
+				global[g150] = -1;
+				scratch._90 = 6;
+			}
+		}
+		break;
+
+	case 24:
+		if (scratch._8c == 3) {
+			aa[2] = kernel_run_animation(kernel_name('E', 1), 102);
+			scratch.animation_info[2]._active = -1;
+			scratch._96 = 2;
+			kernel_reset_animation(scratch._9a, 0);
+			kernel_synch(KERNEL_ANIM, aa[2], KERNEL_ANIM, scratch._9a);
+			global[g133] = 1;
+		} else if (scratch._8c == 5) {
+			if (scratch._a8 == 1) {
+				aa[4] = kernel_run_animation(kernel_name('F', 1), 102);
+				scratch.animation_info[4]._active = -1;
+				scratch._96 = 4;
+				kernel_reset_animation(scratch._9a, 0);
+				kernel_synch(KERNEL_ANIM, aa[4], KERNEL_ANIM, scratch._9a);
+				global[g133] = 1;
+			} else if (scratch._a8 == 2) {
+				aa[4] = kernel_run_animation(kernel_name('F', 2), 102);
+				scratch.animation_info[4]._active = -1;
+				scratch._96 = 4;
+				kernel_reset_animation(scratch._9a, 0);
+				kernel_synch(KERNEL_ANIM, aa[4], KERNEL_ANIM, scratch._9a);
+				global[g133] = 1;
+			}
+		} else if (scratch._8c == 7) {
+			global[player_score] = 0;
+			aa[7] = kernel_run_animation(kernel_name('Z', 2), 106);
+			scratch.animation_info[7]._active = -1;
+			scratch.animation_info[7]._frame = 0;
+			scratch._96 = 7;
+			player.walker_visible = -1;
+			player.commands_allowed = 0;
+			kernel_reset_animation(scratch._9a, 0);
+			kernel_synch(KERNEL_ANIM, aa[7], KERNEL_ANIM, scratch._9a);
+			kernel_synch(KERNEL_PLAYER, 0, KERNEL_ANIM, aa[7]);
+			global[g133] = 1;
+		} else if (scratch._8c == 8) {
+			aa[4] = kernel_run_animation(kernel_name('E', 1), 102);
+			scratch.animation_info[4]._active = -1;
+			scratch._96 = 4;
+			scratch._a8 = 3;
+			kernel_reset_animation(scratch._9a, 0);
+			kernel_synch(KERNEL_ANIM, aa[4], KERNEL_ANIM, scratch._9a);
+			global[g133] = 1;
+		}
+		break;
+
+	case 25:
+		if (scratch._8e == 2) {
+			aa[3] = kernel_run_animation(kernel_name('R', 1), 101);
+			scratch.animation_info[3]._active = -1;
+			scratch._94 = 3;
+			kernel_reset_animation(scratch._9c, 0);
+			kernel_synch(KERNEL_ANIM, aa[3], KERNEL_ANIM, scratch._9c);
+			global[g143] = 1;
+		}
+		break;
+
+	case 26: {
+		int16 a90 = scratch._90;
+		if (a90 == 104) {
+			if (scratch._a8 == 1) {
+				kernel_reset_animation(scratch._9a, 0);
+				kernel_reset_animation(scratch._9c, 0);
+				kernel_synch(KERNEL_ANIM, aa[scratch._9a], KERNEL_NOW, 0);
+				kernel_synch(KERNEL_ANIM, aa[scratch._9c], KERNEL_NOW, 0);
+				global[g133] = 1;
+				global[g143] = 1;
+				global[g131] = 0;
+				global[g141] = 0;
+				kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
+				player.walker_visible = -1;
+				player.commands_allowed = 0;
+				global[player_score] = 0;
+				aa[6] = kernel_run_animation(kernel_name('R', 1), 105);
+				scratch.animation_info[6]._active = -1;
+				scratch.animation_info[6]._frame = 0;
+				kernel_synch(KERNEL_ANIM, aa[6], KERNEL_NOW, 0);
+				scratch._98 = 104;
+				kernel_seq_delete(seq[1]);
+			} else if (scratch._a8 == 2) {
+				kernel_abort_animation(aa[6]);
+				aa[6] = kernel_run_animation(kernel_name('R', 2), 103);
+			}
+		} else if (a90 < 104) {
+			if (a90 == 1) {
+				kernel_reset_animation(scratch._9a, 0);
+				kernel_reset_animation(scratch._9c, 0);
+				global[g133] = 1;
+				global[g143] = 1;
+			} else if (a90 == 6) {
+				if (scratch._a8 == 1) {
+					aa[5] = kernel_run_animation(kernel_name('P', 1), 103);
+					scratch.animation_info[5]._active = -1;
+					scratch._98 = 5;
+					kernel_reset_animation(scratch._9a, 0);
+					kernel_reset_animation(scratch._9c, 0);
+					kernel_synch(KERNEL_ANIM, aa[5], KERNEL_NOW, 0);
+					global[g133] = 1;
+					global[g143] = 1;
+				}
+			}
+		}
+		break;
+	}
+
+	case 27:
+		scratch._ac = 0;
+		global[walker_converse_now] = 0;
+		kernel_abort_animation(scratch._a6);
+		global[g156] = 0;
+		scratch._a2 = kernel_run_animation_talk('e', 2, 0);
+		kernel_position_anim(scratch._a2, 104, 109, 68, 9);
+		kernel_synch(KERNEL_ANIM, scratch._a2, KERNEL_NOW, 0);
+		global[g154] = 2;
+		scratch._a4 = 2;
+		digi_play_build(302, 'b', 1, 1);
+		break;
+
+	case 100:
+		kernel_abort_animation(aa[0]);
+		scratch.animation_info[0]._active = 0;
+		if (flags[16] == 1) {
+			kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
+			scratch._a4 = 1;
+			global[g017] = 0;
+			player.walker_visible = 0;
+			global[g154] = 0;
+			scratch.animation_info[1]._active = -1;
+			global[g131] = -1;
+			global[g141] = -1;
+			kernel_reset_animation(scratch._9a, 1);
+			kernel_synch(KERNEL_ANIM, scratch._9a, KERNEL_NOW, 0);
+			kernel_reset_animation(scratch._9c, 1);
+			kernel_synch(KERNEL_ANIM, scratch._9c, KERNEL_NOW, 0);
+			global[g133] = 1;
+			global[g143] = 0;
+			scratch._9e = kernel_run_animation_talk('b', 7, 0);
+			kernel_position_anim(scratch._9e, player.x, player.y, player.scale, player.depth);
+			kernel_synch(KERNEL_ANIM, scratch._9e, KERNEL_PLAYER, 0);
+			scratch._ac = -1;
+			scratch._a6 = kernel_run_animation_write(0);
+			kernel_reset_animation(scratch._a6, 41);
+			kernel_position_anim(scratch._a6, 104, 109, 68, 9);
+			global[walker_converse_now] = 1;
+			global[g007] = 1;
+			kernel_synch(KERNEL_ANIM, scratch._a6, KERNEL_ANIM, scratch._9a);
+		} else {
+			global[g131] = -1;
+			global[g141] = -1;
+			kernel_reset_animation(scratch._9a, 1);
+			kernel_synch(KERNEL_ANIM, scratch._9a, KERNEL_NOW, 0);
+			kernel_reset_animation(scratch._9c, 1);
+			kernel_synch(KERNEL_ANIM, scratch._9c, KERNEL_NOW, 0);
+			player.walker_visible = -1;
+			global[g133] = 0;
+			global[g143] = 0;
+			kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
+			player.commands_allowed = -1;
+		}
+		break;
+
+	case 101:
+		kernel_abort_animation(aa[scratch._94]);
+		scratch.animation_info[scratch._94]._active = 0;
+		kernel_reset_animation(scratch._9c, 1);
+		kernel_synch(KERNEL_ANIM, scratch._9c, KERNEL_NOW, 0);
+		global[g143] = 0;
+		player.commands_allowed = -1;
+		break;
+
+	case 102:
+		kernel_abort_animation(aa[scratch._96]);
+		scratch.animation_info[scratch._96]._active = 0;
+		kernel_reset_animation(scratch._9a, 1);
+		kernel_synch(KERNEL_ANIM, scratch._9a, KERNEL_NOW, 0);
+		global[g133] = 0;
+		global[player_score] = -1;
+		player.commands_allowed = -1;
+		break;
+
+	case 103:
+		if (scratch._a8 == 1) {
+			kernel_abort_animation(aa[5]);
+			scratch.animation_info[5]._active = 0;
+			kernel_reset_animation(scratch._9c, 1);
+			kernel_synch(KERNEL_ANIM, scratch._9c, KERNEL_NOW, 0);
+			global[g143] = 0;
+			kernel_reset_animation(scratch._9a, 1);
+			kernel_synch(KERNEL_ANIM, scratch._9a, KERNEL_NOW, 0);
+			global[g133] = 0;
+			kernel_flip_hotspot(137, 0);
+			inter_move_object(1, 2);
+			global[player_score] = -1;
+			player.commands_allowed = -1;
+		}
+		break;
+
+	case 105:
+		if (scratch._98 == 104) {
+			if (scratch._a8 == 1) {
+				kernel_abort_animation(aa[6]);
+				aa[6] = kernel_run_animation(kernel_name('R', 2), 105);
+				scratch._a8 = 2;
+				kernel_synch(KERNEL_ANIM, aa[6], KERNEL_NOW, 0);
+			} else if (scratch._a8 == 2) {
+				kernel_abort_animation(aa[6]);
+				scratch.animation_info[6]._active = 0;
+				global[g131] = -1;
+				global[g141] = -1;
+				kernel_reset_animation(scratch._9a, 1);
+				kernel_reset_animation(scratch._9c, 1);
+				kernel_synch(KERNEL_ANIM, scratch._9a, KERNEL_NOW, 0);
+				kernel_synch(KERNEL_ANIM, scratch._9c, KERNEL_NOW, 0);
+				global[g048] = -1;
+				global[g133] = 0;
+				global[g143] = 0;
+				kernel_flip_hotspot(173, 0);
+				kernel_flip_hotspot(27, -1);
+				seq[2] = kernel_seq_stamp(ss[2], 0, -1);
+				kernel_seq_depth(seq[2], 15);
+				kernel_seq_loc(seq[2], 33, 86);
+				kernel_seq_scale(seq[2], 52);
+				seq[3] = kernel_seq_stamp(ss[3], 0, -1);
+				kernel_seq_depth(seq[3], 15);
+				kernel_seq_loc(seq[3], 15, 86);
+				kernel_seq_scale(seq[3], 52);
+				player.commands_allowed = -1;
+			}
+		}
+		break;
+
+	case 106:
+		kernel_abort_animation(aa[7]);
+		scratch.animation_info[7]._active = 0;
+		scratch.animation_info[7]._frame = 0;
+		kernel_reset_animation(scratch._9c, 1);
+		kernel_synch(KERNEL_ANIM, scratch._9c, KERNEL_NOW, 0);
+		global[g143] = 0;
+		player.commands_allowed = -1;
+		if (scratch._96 - 7 == 0) {
+			global[g063] = -1;
+			new_room = 210;
+		}
+		scratch._96 = 0;
+		break;
+	default:
+		break;
+	}
+
+	global_anim1(1, scratch._9a, global[g131], &global[g132]);
+	global_anim2(3, scratch._9c, global[g141], &global[g142]);
+
+	if (scratch.animation_info[0]._active) room_302_anim1();
+	if (scratch.animation_info[3]._active) room_302_anim4();
+	if (scratch.animation_info[2]._active) room_302_anim3();
+	if (scratch.animation_info[4]._active) room_302_anim5();
+	if (scratch.animation_info[5]._active) room_302_anim6();
+	if (scratch.animation_info[6]._active) room_302_anim7();
+	if (scratch.animation_info[7]._active) room_302_anim8();
+	if (scratch.animation_info[1]._active) room_302_anim2();
+
+	if (global[walker_converse_now])
+		global_anim3(scratch._a6, &global[g008]);
+
+	if (scratch._aa)
+		kernel_random_frame(scratch._9e, &global[g151], global[g154]);
 }
 
 static void room_302_pre_parser() {
+	if (player_parse(13, 34, 0))
+		player.walk_off_edge_to_room = 301;
+
+	if (player_parse(13, 36, 0)) {
+		if (global[g064])
+			player.walk_off_edge_to_room = 321;
+		else
+			player.walk_off_edge_to_room = 303;
+	}
 }
 
 static void room_302_parser() {
+	if (global[walker_converse_state]) {
+		player.commands_allowed = 0;
+		digi_play_build_ii('c', 1, 1);
+		player.command_ready = 0;
+		return;
+	}
+
+	if (player_parse(13, 37, 0)) {
+		new_room = 304;
+		player.command_ready = 0;
+		return;
+	}
+
+	if (player_parse(13, 27, 0)) {
+		global[g135] = -1;
+		scratch._8c = 7;
+		player.command_ready = 0;
+		return;
+	}
+
+	if (global[player_selected_object] >= 0) {
+		player.command_ready = 0;
+		return;
+	}
+
+	if (player_parse(114, 150, 0)) {
+		global[player_score] = 0;
+		digi_stop(3);
+		player.commands_allowed = 0;
+		global[g135] = -1;
+		scratch._8c = 5;
+		scratch._a8 = 1;
+		player.command_ready = 0;
+		return;
+	}
+
+	if (player_parse(114, 82, 0)) {
+		global[player_score] = 0;
+		digi_stop(3);
+		player.commands_allowed = 0;
+		global[g135] = -1;
+		scratch._8c = 5;
+		scratch._a8 = 2;
+		player.command_ready = 0;
+		return;
+	}
+
+	if (player_parse(126, 137, 0)) {
+		global[g154] = 2;
+		global[player_score] = 0;
+		player.commands_allowed = 0;
+		player.walker_visible = 0;
+		scratch._9e = kernel_run_animation_talk('b', 6, 0);
+		kernel_position_anim(scratch._9e, player.x, player.y, player.scale, player.depth);
+		kernel_synch(KERNEL_ANIM, scratch._9e, KERNEL_PLAYER, 0);
+		digi_play_build_ii('b', 2, 1);
+		scratch._a4 = 400;
+		scratch._aa = -1;
+		player.command_ready = 0;
+		return;
+	}
+
+	if (player_parse(78, 173, 0)) {
+		global[g154] = 2;
+		global[player_score] = 0;
+		player.commands_allowed = 0;
+		player.walker_visible = 0;
+		scratch._9e = kernel_run_animation_talk('b', 4, 0);
+		kernel_position_anim(scratch._9e, player.x, player.y, player.scale, player.depth);
+		kernel_synch(KERNEL_ANIM, scratch._9e, KERNEL_PLAYER, 0);
+		digi_play_build_ii('b', 1, 1);
+		scratch._a4 = 200;
+		scratch._aa = -1;
+		player.command_ready = 0;
+		return;
+	}
+
+	if (player_parse(78, 75, 0)) {
+		global[player_score] = 0;
+		player.commands_allowed = 0;
+		global[g135] = -1;
+		scratch._8c = 8;
+		player.command_ready = 0;
+	}
 }
 
 void room_302_synchronize(Common::Serializer &s) {
 	for (int16 &v : scratch.sprite)    s.syncAsSint16LE(v);
 	for (int16 &v : scratch.sequence)  s.syncAsSint16LE(v);
 	for (int16 &v : scratch.animation) s.syncAsSint16LE(v);
+	for (auto &ai : scratch.animation_info) ai.synchronize(s);
+	s.syncAsSint16LE(scratch._8c);
+	s.syncAsSint16LE(scratch._8e);
+	s.syncAsSint16LE(scratch._90);
+	s.syncAsSint16LE(scratch._92);
+	s.syncAsSint16LE(scratch._94);
+	s.syncAsSint16LE(scratch._96);
+	s.syncAsSint16LE(scratch._98);
+	s.syncAsSint16LE(scratch._9a);
+	s.syncAsSint16LE(scratch._9c);
+	s.syncAsSint16LE(scratch._9e);
+	s.syncAsSint16LE(scratch._a0);
+	s.syncAsSint16LE(scratch._a2);
+	s.syncAsSint16LE(scratch._a4);
+	s.syncAsSint16LE(scratch._a6);
+	s.syncAsSint16LE(scratch._a8);
+	s.syncAsSint16LE(scratch._aa);
+	s.syncAsSint16LE(scratch._ac);
 }
 
 void room_302_preload() {


Commit: 12adf7f0700c0ec4e01cdfde0cd40a5fc3e3cbcd
    https://github.com/scummvm/scummvm/commit/12adf7f0700c0ec4e01cdfde0cd40a5fc3e3cbcd
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-06-09T18:42:48+10:00

Commit Message:
MADS: FOREST: Implemented room 303

Changed paths:
    engines/mads/madsv2/forest/rooms/room303.cpp


diff --git a/engines/mads/madsv2/forest/rooms/room303.cpp b/engines/mads/madsv2/forest/rooms/room303.cpp
index ded667515aa..ba06a7eeb61 100644
--- a/engines/mads/madsv2/forest/rooms/room303.cpp
+++ b/engines/mads/madsv2/forest/rooms/room303.cpp
@@ -20,16 +20,20 @@
  */
 
 #include "mads/madsv2/core/conv.h"
+#include "mads/madsv2/core/digi.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/imath.h"
 #include "mads/madsv2/core/inter.h"
 #include "mads/madsv2/core/kernel.h"
+#include "mads/madsv2/core/player.h"
 #include "mads/madsv2/core/sound.h"
+#include "mads/madsv2/core/sprite.h"
 #include "mads/madsv2/core/text.h"
 #include "mads/madsv2/forest/mads/inventory.h"
 #include "mads/madsv2/forest/mads/sounds.h"
 #include "mads/madsv2/forest/mads/words.h"
 #include "mads/madsv2/forest/global.h"
+#include "mads/madsv2/forest/journal.h"
 #include "mads/madsv2/forest/rooms/section1.h"
 #include "mads/madsv2/forest/rooms/room303.h"
 
@@ -39,10 +43,15 @@ namespace Forest {
 namespace Rooms {
 
 struct Scratch {
-	int16 sprite[10];       /* Sprite series handles */
-	int16 sequence[10];     /* Sequence handles      */
-	int16 animation[10];     /* Animation handles     */
+	int16 sprite[10];
+	int16 sequence[10];
+	int16 animation[10];
 	AnimationInfo animation_info[10];
+	int16 _8c, _8e, _90, _92, _94, _96, _98, _9a, _9c, _9e, _a0, _a2;
+	int16 _a4;  // scratch+0xA4
+	int16 _a6;  // scratch+0xA6
+	int16 _a8;  // scratch+0xA8
+	int16 _aa;  // scratch+0xAA
 };
 
 static Scratch scratch;
@@ -53,23 +62,884 @@ static Scratch scratch;
 #define aa    local->animation
 #define aainfo scratch.animation_info
 
+static void room_303_init1();
+static void room_303_init2();
+
+static void room_303_anim1() {
+	if (kernel_anim[aa[0]].frame != scratch.animation_info[0]._frame)
+		scratch.animation_info[0]._frame = kernel_anim[aa[0]].frame;
+
+	if (global[player_hyperwalked] == -1) {
+		scratch.animation_info[0]._frame = scratch._92 - 1;
+		kernel_reset_animation(aa[0], scratch.animation_info[0]._frame);
+	}
+}
+
+static void room_303_anim2() {
+	int16 ax = scratch.animation_info[1]._frame;
+	if (kernel_anim[aa[1]].frame != ax) {
+		ax = kernel_anim[aa[1]].frame;
+		scratch.animation_info[1]._frame = ax;
+	}
+
+	if (global[player_hyperwalked] == -1) {
+		if (ax <= 23) {
+			scratch.animation_info[1]._frame = 23;
+			kernel_reset_animation(aa[1], 23);
+			kernel_synch(KERNEL_ANIM, aa[1], KERNEL_NOW, 0);
+			return;
+		}
+		scratch.animation_info[1]._frame = 44;
+		kernel_reset_animation(aa[1], 44);
+		kernel_synch(KERNEL_ANIM, aa[1], KERNEL_NOW, 0);
+	} else {
+		if (ax == 45) {
+			scratch.animation_info[1]._frame = 44;
+			kernel_reset_animation(aa[1], 44);
+			kernel_synch(KERNEL_ANIM, aa[1], KERNEL_NOW, 0);
+		}
+	}
+}
+
+static void room_303_anim3() {
+	int16 ax = scratch.animation_info[2]._frame;
+	if (kernel_anim[aa[2]].frame != ax) {
+		ax = kernel_anim[aa[2]].frame;
+		scratch.animation_info[2]._frame = ax;
+	}
+
+	if (global[player_hyperwalked] == -1) {
+		if (ax <= 23) {
+			scratch.animation_info[2]._frame = 23;
+			kernel_reset_animation(aa[2], 23);
+			kernel_synch(KERNEL_ANIM, aa[2], KERNEL_NOW, 0);
+			return;
+		}
+		scratch.animation_info[2]._frame = 44;
+		kernel_reset_animation(aa[2], 44);
+		kernel_synch(KERNEL_ANIM, aa[2], KERNEL_NOW, 0);
+	} else {
+		if (ax == 45) {
+			scratch.animation_info[2]._frame = 44;
+			kernel_reset_animation(aa[2], 44);
+			kernel_synch(KERNEL_ANIM, aa[2], KERNEL_NOW, 0);
+		}
+	}
+}
+
+static void room_303_anim4_trigger() {
+	if (kernel.trigger != 7 && kernel.trigger != 28)
+		return;
+
+	int16 a8 = scratch._a8;
+	if (a8 == 7) {
+		kernel_timing_trigger(30, 28);
+		scratch.animation_info[3]._val3 = 13;
+		scratch._a8 = 8;
+	} else if (a8 == 8) {
+		scratch.animation_info[3]._val3 = 11;
+		scratch.animation_info[3]._frame = 7;
+		kernel_reset_animation(aa[3], 7);
+		digi_play_build_ii('e', 10, 1);
+		scratch._a8 = 9;
+	} else if (a8 == 9) {
+		scratch.animation_info[3]._val3 = 14;
+		scratch.animation_info[3]._frame = 13;
+		kernel_reset_animation(aa[3], 13);
+		scratch._a8 = -1;
+	} else if (a8 == 43) {
+		kernel_timing_trigger(30, 28);
+		scratch.animation_info[3]._val3 = 13;
+		scratch._a8 = 44;
+	} else if (a8 == 44) {
+		scratch.animation_info[3]._val3 = 11;
+		scratch.animation_info[3]._frame = 43;
+		kernel_reset_animation(aa[3], 43);
+		digi_play_build_ii('e', 10, 1);
+		scratch._a8 = 45;
+	} else if (a8 == 45) {
+		scratch.animation_info[3]._val3 = 14;
+		scratch.animation_info[3]._frame = 49;
+		kernel_reset_animation(aa[3], 49);
+		scratch._a8 = -1;
+	}
+}
+
+static void room_303_anim4() {
+	int16 ax = scratch.animation_info[3]._frame;
+	if (kernel_anim[aa[3]].frame == ax) {
+		room_303_anim4_trigger();
+		return;
+	}
+
+	ax = kernel_anim[aa[3]].frame;
+	scratch.animation_info[3]._frame = ax;
+
+	if (scratch._a6 == 1) {
+		if (ax == 15) {
+			if (scratch.animation_info[3]._val3 == 13) {
+				scratch.animation_info[3]._frame = 13;
+				kernel_reset_animation(aa[3], 13);
+			}
+		} else if (ax < 15) {
+			if (ax == 7) {
+				scratch.animation_info[3]._val3 = 11;
+				digi_play_build_ii('e', 2, 1);
+				scratch._a8 = 7;
+			} else if (ax == 12) {
+				if (scratch.animation_info[3]._val3 == 11) {
+					scratch.animation_info[3]._frame = 7;
+					kernel_reset_animation(aa[3], 7);
+				}
+			}
+		}
+	} else if (scratch._a6 == 2) {
+		if (ax == 51) {
+			if (scratch.animation_info[3]._val3 == 13) {
+				scratch.animation_info[3]._frame = 49;
+				kernel_reset_animation(aa[3], 49);
+			}
+		} else if (ax < 51) {
+			if (ax == 43) {
+				scratch.animation_info[3]._val3 = 11;
+				digi_play_build_ii('e', 4, 1);
+				scratch._a8 = 43;
+			} else if (ax == 48) {
+				if (scratch.animation_info[3]._val3 == 11) {
+					scratch.animation_info[3]._frame = 43;
+					kernel_reset_animation(aa[3], 43);
+				}
+			}
+		}
+	}
+
+	room_303_anim4_trigger();
+}
+
+static void room_303_anim6_trigger() {
+	if (kernel.trigger == 7 || kernel.trigger == 28) {
+		int16 a8 = scratch._a8;
+		if (a8 == 9) {
+			scratch.animation_info[5]._val3 = 13;
+			scratch.animation_info[5]._frame = 16;
+			kernel_reset_animation(aa[5], 16);
+			scratch._a8 = -1;
+		} else if (a8 == 40) {
+			scratch.animation_info[5]._val3 = 13;
+			scratch.animation_info[5]._frame = 10;
+			kernel_reset_animation(aa[5], 10);
+			scratch._a8 = -1;
+		}
+	}
+
+	if (kernel.trigger == 8 || kernel.trigger == 28) {
+		int16 a8 = scratch._a8;
+		if (a8 == 35 || a8 == 48 || a8 == 530)
+			scratch._a8 = -1;
+	}
+}
+
+static void room_303_anim6() {
+	int16 ax = scratch.animation_info[5]._frame;
+	if (kernel_anim[aa[5]].frame == ax) {
+		room_303_anim6_trigger();
+		return;
+	}
+
+	ax = kernel_anim[aa[5]].frame;
+	scratch.animation_info[5]._frame = ax;
+
+	if (scratch._a6 == 1) {
+		if (ax == 48) {
+			if (scratch.animation_info[5]._val3 == 13) {
+				digi_play_build(101, '_', 1, 2);
+				scratch._a8 = 48;
+			}
+		} else if (ax < 48) {
+			if (ax == 9) {
+				scratch.animation_info[5]._val3 = 11;
+				digi_play_build(303, 'e', 2, 1);
+				scratch._a8 = 9;
+			} else if (ax == 15) {
+				if (scratch.animation_info[5]._val3 == 11) {
+					scratch.animation_info[5]._frame = 9;
+					kernel_reset_animation(aa[5], 9);
+				}
+			} else if (ax == 35) {
+				if (scratch.animation_info[5]._val3 == 13) {
+					digi_play_build(303, '_', 1, 2);
+					scratch._a8 = 35;
+				}
+			}
+		}
+	} else if (scratch._a6 == 2) {
+		if (ax == 53) {
+			digi_play_build(101, '_', 1, 2);
+			scratch._a8 = 530;
+		} else if (ax < 53) {
+			if (ax == 2) {
+				kernel_seq_delete(seq[0]);
+			} else if (ax == 4) {
+				scratch.animation_info[5]._val3 = 11;
+				digi_play_build(303, 'e', 700, 1);
+				scratch._a8 = 40;
+			} else if (ax == 9) {
+				if (scratch.animation_info[5]._val3 == 11) {
+					scratch.animation_info[5]._frame = 4;
+					kernel_reset_animation(aa[5], 4);
+				}
+			}
+		}
+	}
+
+	room_303_anim6_trigger();
+}
+
+static void room_303_anim7() {
+	int16 result = -1;
+
+	if (kernel_anim[aa[6]].frame != scratch.animation_info[6]._frame) {
+		int16 frame = kernel_anim[aa[6]].frame;
+		scratch.animation_info[6]._frame = frame;
+
+		bool check_val3 = (frame == 1) ||
+		                  (frame >= 5 && frame <= 10) ||
+		                  (frame >= 12 && frame <= 16);
+
+		if (check_val3) {
+			int16 v = scratch.animation_info[6]._val3;
+			if (v == 0)
+				result = 0;
+			else if (v == 1)
+				result = imath_random(11, 15);
+			else if (v == 2)
+				result = imath_random(4, 9);
+		}
+
+		if (result >= 0) {
+			kernel_reset_animation(aa[6], result);
+			scratch.animation_info[6]._frame = result;
+		}
+	}
+
+	if (kernel.trigger != 7 && kernel.trigger != 28)
+		return;
+
+	int16 idx = scratch._a8 - 100;
+	if (idx < 0 || idx > 9)
+		return;
+
+	switch (idx) {
+	case 0:
+		scratch.animation_info[7]._val3 = 0;
+		kernel_timing_trigger(30, 28);
+		scratch._a8++;
+		break;
+	case 1:
+		scratch.animation_info[6]._val3 = 1;
+		digi_play_build(303, 'r', 1, 1);
+		scratch._a8++;
+		break;
+	case 2:
+		scratch.animation_info[6]._val3 = 0;
+		kernel_timing_trigger(30, 28);
+		scratch._a8++;
+		break;
+	case 3:
+		scratch.animation_info[7]._val3 = 3;
+		digi_play_build(303, 'f', 2, 1);
+		scratch._a8++;
+		break;
+	case 4:
+		scratch.animation_info[7]._val3 = 0;
+		kernel_timing_trigger(30, 28);
+		scratch._a8++;
+		break;
+	case 5:
+		scratch.animation_info[6]._val3 = 2;
+		digi_play_build(303, 'e', 1, 1);
+		scratch._a8++;
+		break;
+	case 6:
+		scratch.animation_info[6]._val3 = 0;
+		kernel_timing_trigger(30, 28);
+		scratch._a8++;
+		break;
+	case 7:
+		scratch.animation_info[7]._val3 = 3;
+		digi_play_build(303, 'f', 3, 1);
+		scratch._a8++;
+		break;
+	case 8:
+		scratch.animation_info[7]._val3 = 0;
+		kernel_timing_trigger(30, 28);
+		scratch._a8++;
+		break;
+	case 9:
+		dont_frag_the_palette();
+		kernel_abort_animation(aa[7]);
+		scratch.animation_info[7]._active = 0;
+		aa[7] = kernel_run_animation(kernel_name('t', 6), 105);
+		break;
+	}
+}
+
+static void room_303_anim8() {
+	int16 var_2 = -1;
+
+	if (kernel_anim[aa[7]].frame == scratch.animation_info[7]._frame)
+		return;
+
+	int16 frame = kernel_anim[aa[7]].frame;
+	scratch.animation_info[7]._frame = frame;
+
+	bool check_val3 = (frame == 1) || (frame >= 4 && frame <= 10);
+
+	if (check_val3) {
+		int16 v = scratch.animation_info[7]._val3;
+		if (v == 0)
+			var_2 = 0;
+		else if (v == 3)
+			var_2 = imath_random(3, 9);
+	}
+
+	if (var_2 >= 0) {
+		kernel_reset_animation(aa[7], var_2);
+		scratch.animation_info[7]._frame = var_2;
+	}
+}
 
 static void room_303_init() {
+	global[player_score] = 0;
+	scratch._a4 = 0;
+	scratch._a6 = -1;
+	scratch._aa = -1;
+
+	ss[1] = kernel_load_series(kernel_name('p', 1), 0);
+	seq[1] = kernel_seq_stamp(ss[1], false, KERNEL_FIRST);
+	kernel_seq_depth(seq[1], 15);
+
+	if (!player_has(6)) {
+		scratch._a6 = -1;
+	} else {
+		kernel_flip_hotspot(132, 0);
+	}
+
+	if (!global[g068]) {
+		ss[0] = kernel_load_series(kernel_name('p', 2), 0);
+		seq[0] = kernel_seq_stamp(ss[0], false, KERNEL_FIRST);
+		kernel_seq_depth(seq[0], 7);
+		kernel_seq_loc(seq[0], 280, 130);
+		kernel_seq_scale(seq[0], 91);
+	} else {
+		kernel_flip_hotspot(125, 0);
+	}
+
+	if (previous_room != KERNEL_LAST) {
+		if (previous_room != 199) {
+			player.walker_visible = 0;
+			player.commands_allowed = 0;
+		}
+
+		for (int i = 0; i < 10; i++) {
+			scratch.animation_info[i]._active = 0;
+			scratch.animation_info[i]._frame = 1;
+			scratch.animation_info[i]._val3 = 0;
+			scratch.animation_info[i]._val4 = 0;
+		}
+
+		if (previous_room != 199 && flags[34] != 3)
+			flags[34]++;
+	}
+
+	int16 f = flags[34];
+	if (f == 1) {
+		room_303_init1();
+	} else if (f == 2 || f == 3) {
+		room_303_init2();
+	}
+}
+
+static void room_303_init1() {
+	global[g133] = 1;
+	global[g143] = 1;
+	global[g131] = 0;
+	global[g141] = 0;
+
+	scratch._9c = kernel_run_animation_disp('r', 6, 0);
+	kernel_position_anim(scratch._9c, 83, 131, 92, 6);
+
+	scratch._9a = kernel_run_animation_disp('e', 6, 0);
+	kernel_position_anim(scratch._9a, 109, 139, 94, 5);
+
+	if (previous_room != KERNEL_LAST) {
+		player.x = 138;
+		player.y = 130;
+		player.facing = 6;
+	}
+
+	if (previous_room == KERNEL_LAST) {
+		global[g131] = -1;
+		global[g141] = -1;
+		kernel_reset_animation(scratch._9a, 2);
+		kernel_reset_animation(scratch._9c, 2);
+		global[g133] = 0;
+		global[g143] = 0;
+		return;
+	}
+
+	if (previous_room == 302) {
+		aa[0] = kernel_run_animation(kernel_name('y', 1), 100);
+		scratch.animation_info[0]._active = -1;
+		scratch._92 = 51;
+		return;
+	}
+
+	if (previous_room == 199) {
+		global[g131] = -1;
+		global[g141] = -1;
+		kernel_reset_animation(scratch._9a, 2);
+		kernel_reset_animation(scratch._9c, 2);
+		global[g133] = 0;
+		global[g143] = 0;
+		restore_player();
+		player.commands_allowed = -1;
+		player.walker_visible = -1;
+		return;
+	}
+
+	global[g131] = -1;
+	global[g141] = -1;
+	kernel_reset_animation(scratch._9a, 2);
+	kernel_reset_animation(scratch._9c, 2);
+	global[g133] = 0;
+	global[g143] = 0;
+	player.commands_allowed = -1;
+	player.walker_visible = -1;
+}
+
+static void room_303_init2() {
+	global[g131] = 0;
+	global[g141] = 0;
+
+	scratch._9c = kernel_run_animation_disp('r', 6, 0);
+	kernel_position_anim(scratch._9c, 83, 131, 92, 6);
+
+	scratch._9a = kernel_run_animation_disp('e', 6, 0);
+	kernel_position_anim(scratch._9a, 109, 139, 94, 5);
+
+	if (previous_room != KERNEL_LAST) {
+		player.x = 138;
+		player.y = 130;
+		player.facing = 6;
+	}
+
+	if (previous_room == KERNEL_LAST) {
+		global[g131] = -1;
+		global[g141] = -1;
+		kernel_reset_animation(scratch._9a, 2);
+		kernel_reset_animation(scratch._9c, 2);
+		global[g133] = 0;
+		global[g143] = 0;
+		return;
+	}
+
+	if (previous_room == 302) {
+		aa[0] = kernel_run_animation(kernel_name('y', 1), 100);
+		scratch.animation_info[0]._active = -1;
+		scratch._92 = 51;
+		return;
+	}
+
+	if (previous_room == 199) {
+		global[g131] = -1;
+		global[g141] = -1;
+		kernel_reset_animation(scratch._9a, 2);
+		kernel_reset_animation(scratch._9c, 2);
+		global[g133] = 0;
+		global[g143] = 0;
+		restore_player();
+		player.commands_allowed = -1;
+		player.walker_visible = -1;
+		return;
+	}
+
+	global[g131] = -1;
+	global[g141] = -1;
+	kernel_reset_animation(scratch._9a, 2);
+	kernel_reset_animation(scratch._9c, 2);
+	global[g133] = 0;
+	global[g143] = 0;
+	player.commands_allowed = -1;
+	player.walker_visible = -1;
 }
 
 static void room_303_daemon() {
+	switch (kernel.trigger) {
+	case 100:
+		dont_frag_the_palette();
+		kernel_abort_animation(aa[0]);
+		scratch.animation_info[0]._active = 0;
+		if (global[g065]) {
+			global[g131] = -1;
+			global[g141] = -1;
+			kernel_reset_animation(scratch._9a, 1);
+			kernel_synch(KERNEL_ANIM, scratch._9a, KERNEL_NOW, 0);
+			kernel_reset_animation(scratch._9c, 1);
+			kernel_synch(KERNEL_ANIM, scratch._9c, KERNEL_NOW, 0);
+			player.walker_visible = -1;
+			global[g133] = 0;
+			global[g143] = 0;
+			kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
+			player.commands_allowed = -1;
+		} else {
+			global[g065] = -1;
+			aa[6] = kernel_run_animation(kernel_name('t', 5), 0);
+			scratch.animation_info[6]._active = -1;
+			scratch.animation_info[6]._val3 = 0;
+			aa[7] = kernel_run_animation(kernel_name('t', 1), 104);
+		}
+		break;
+
+	case 101:
+		dont_frag_the_palette();
+		kernel_abort_animation(aa[scratch._94]);
+		scratch.animation_info[scratch._94]._active = 0;
+		kernel_reset_animation(scratch._9c, 1);
+		kernel_synch(KERNEL_ANIM, scratch._9c, KERNEL_NOW, 0);
+		global[g143] = 0;
+		player.commands_allowed = -1;
+		break;
+
+	case 102:
+		dont_frag_the_palette();
+		kernel_abort_animation(aa[scratch._96]);
+		scratch.animation_info[scratch._96]._active = 0;
+		kernel_reset_animation(scratch._9a, 1);
+		kernel_synch(KERNEL_ANIM, scratch._9a, KERNEL_NOW, 0);
+		global[g133] = 0;
+		player.commands_allowed = -1;
+		break;
+
+	case 103:
+		if (scratch._a6 == 1) {
+			dont_frag_the_palette();
+			kernel_abort_animation(aa[5]);
+			scratch.animation_info[5]._active = 0;
+			kernel_reset_animation(scratch._9c, 1);
+			kernel_synch(KERNEL_ANIM, scratch._9c, KERNEL_NOW, 0);
+			global[g143] = 0;
+			kernel_reset_animation(scratch._9a, 1);
+			kernel_synch(KERNEL_ANIM, scratch._9a, KERNEL_NOW, 0);
+			global[g133] = 0;
+			kernel_flip_hotspot(132, 0);
+			inter_move_object(6, 2);
+			player.commands_allowed = -1;
+		} else if (scratch._a6 == 2) {
+			dont_frag_the_palette();
+			kernel_abort_animation(aa[5]);
+			scratch.animation_info[5]._active = 0;
+			kernel_reset_animation(scratch._9c, 1);
+			kernel_synch(KERNEL_ANIM, scratch._9c, KERNEL_NOW, 0);
+			global[g143] = 0;
+			kernel_reset_animation(scratch._9a, 1);
+			kernel_synch(KERNEL_ANIM, scratch._9a, KERNEL_NOW, 0);
+			global[g133] = 0;
+			kernel_flip_hotspot(125, 0);
+			if (!player_has(5))
+				inter_move_object(5, 2);
+			global[g068] = -1;
+			player.commands_allowed = -1;
+		}
+		break;
+
+	case 104:
+		dont_frag_the_palette();
+		kernel_abort_animation(aa[7]);
+		aa[7] = kernel_run_animation(kernel_name('t', 3), 106);
+		break;
+
+	case 105:
+		dont_frag_the_palette();
+		kernel_abort_animation(aa[7]);
+		dont_frag_the_palette();
+		kernel_abort_animation(aa[6]);
+		scratch.animation_info[6]._active = 0;
+		global[g141] = -1;
+		kernel_reset_animation(scratch._9c, 1);
+		kernel_synch(KERNEL_ANIM, scratch._9c, KERNEL_NOW, 0);
+		global[g143] = 0;
+		scratch._aa = kernel_run_animation_write(0);
+		kernel_reset_animation(scratch._aa, 1);
+		kernel_position_anim(scratch._aa, 109, 139, 94, 5);
+		global[walker_converse_now] = 1;
+		global[g007] = 6;
+		kernel_synch(KERNEL_ANIM, scratch._aa, KERNEL_NOW, 0);
+		player.walker_visible = -1;
+		kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
+		break;
+
+	case 106:
+		dont_frag_the_palette();
+		kernel_abort_animation(aa[7]);
+		aa[7] = kernel_run_animation(kernel_name('t', 2), 0);
+		scratch.animation_info[7]._active = -1;
+		scratch.animation_info[7]._val3 = 0;
+		kernel_timing_trigger(10, 107);
+		break;
+
+	case 7:
+		if (global[walker_converse_state] != 0) {
+			global[walker_converse_state] = 0;
+			close_journal(3);
+			player.commands_allowed = -1;
+		} else if (scratch._a8 == 123) {
+			dont_frag_the_palette();
+			kernel_abort_animation(scratch._9e);
+			scratch._a8 = -1;
+			scratch._a4 = 0;
+			kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
+			player.walker_visible = -1;
+			player.commands_allowed = -1;
+		} else if (scratch._a8 == 300) {
+			dont_frag_the_palette();
+			kernel_abort_animation(scratch._9e);
+			scratch._a8 = -1;
+			scratch._a4 = 0;
+			kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
+			scratch._a6 = 2;
+			player.walker_visible = -1;
+			global[g150] = -1;
+			scratch._90 = 6;
+		} else if (scratch._a8 == 400) {
+			dont_frag_the_palette();
+			kernel_abort_animation(scratch._9e);
+			scratch._a8 = -1;
+			scratch._a4 = 0;
+			kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
+			scratch._a6 = 1;
+			player.walker_visible = -1;
+			global[g150] = -1;
+			scratch._90 = 6;
+		}
+		break;
+
+	case 24:
+		if (scratch._8c == 3) {
+			aa[1] = kernel_run_animation(kernel_name('E', 1), 102);
+			scratch.animation_info[1]._active = -1;
+			scratch._96 = 1;
+			kernel_reset_animation(scratch._9a, 0);
+			kernel_synch(KERNEL_ANIM, aa[1], KERNEL_ANIM, scratch._9a);
+			global[g133] = 1;
+		} else if (scratch._8c == 5) {
+			if (scratch._a6 == 1) {
+				aa[3] = kernel_run_animation(kernel_name('F', 1), 102);
+			} else if (scratch._a6 == 2) {
+				aa[3] = kernel_run_animation(kernel_name('F', 2), 102);
+			} else {
+				break;
+			}
+			scratch.animation_info[3]._active = -1;
+			scratch._96 = 3;
+			kernel_reset_animation(scratch._9a, 0);
+			kernel_synch(KERNEL_ANIM, aa[3], KERNEL_ANIM, scratch._9a);
+			global[g133] = 1;
+		}
+		break;
+
+	case 25:
+		if (scratch._8e == 2) {
+			aa[2] = kernel_run_animation(kernel_name('R', 1), 101);
+			scratch.animation_info[2]._active = -1;
+			scratch._94 = 2;
+			kernel_reset_animation(scratch._9c, 0);
+			kernel_synch(KERNEL_ANIM, aa[2], KERNEL_ANIM, scratch._9c);
+			global[g143] = 1;
+		}
+		break;
+
+	case 26:
+		if (scratch._90 == 1) {
+			kernel_reset_animation(scratch._9a, 0);
+			kernel_reset_animation(scratch._9c, 0);
+			global[g133] = 1;
+			global[g143] = 1;
+		} else if (scratch._90 == 6) {
+			if (scratch._a6 == 1) {
+				aa[5] = kernel_run_animation(kernel_name('P', 1), 103);
+				scratch.animation_info[5]._active = -1;
+				scratch._98 = 5;
+				kernel_reset_animation(scratch._9a, 0);
+				kernel_reset_animation(scratch._9c, 0);
+				kernel_synch(KERNEL_ANIM, aa[5], KERNEL_NOW, 0);
+				global[g133] = 1;
+				global[g143] = 1;
+			} else if (scratch._a6 == 2) {
+				aa[5] = kernel_run_animation(kernel_name('P', 2), 103);
+				scratch.animation_info[5]._active = -1;
+				scratch._98 = 5;
+				kernel_reset_animation(scratch._9a, 0);
+				kernel_reset_animation(scratch._9c, 0);
+				kernel_synch(KERNEL_ANIM, scratch._9a, KERNEL_ANIM, aa[5]);
+				kernel_synch(KERNEL_ANIM, scratch._9c, KERNEL_ANIM, aa[5]);
+				global[g133] = 1;
+				global[g143] = 1;
+			}
+		}
+		break;
+
+	case 27:
+		kernel_abort_animation(scratch._aa);
+		scratch._aa = -1;
+		global[g131] = -1;
+		kernel_reset_animation(scratch._9a, 2);
+		kernel_synch(KERNEL_ANIM, scratch._9a, KERNEL_NOW, 0);
+		global[g133] = 0;
+		player.commands_allowed = -1;
+		break;
+
+	case 107:
+		digi_play_build(303, 'f', 1, 1);
+		scratch._a8 = 100;
+		scratch.animation_info[7]._val3 = 3;
+		break;
+
+	default:
+		break;
+	}
+
+	global_anim1(6, scratch._9a, global[g131], &global[g132]);
+	global_anim2(6, scratch._9c, global[g141], &global[g142]);
+
+	if (scratch._a4 != 0)
+		kernel_random_frame(scratch._9e, &global[g151], global[g154]);
+
+	if (scratch._aa >= 0)
+		global_anim3(scratch._aa, &global[g008]);
+
+	if (scratch.animation_info[0]._active) room_303_anim1();
+	if (scratch.animation_info[2]._active) room_303_anim3();
+	if (scratch.animation_info[1]._active) room_303_anim2();
+	if (scratch.animation_info[3]._active) room_303_anim4();
+	if (scratch.animation_info[5]._active) room_303_anim6();
+	if (scratch.animation_info[6]._active) room_303_anim7();
+	if (scratch.animation_info[7]._active) room_303_anim8();
 }
 
 static void room_303_pre_parser() {
+	if (player_parse(13, 35, 0))
+		player.walk_off_edge_to_room = 302;
 }
 
 static void room_303_parser() {
+	if (global[walker_converse_state]) {
+		player.commands_allowed = 0;
+		digi_play_build_ii('c', 1, 1);
+		player.command_ready = 0;
+		return;
+	}
+
+	if (player_parse(177, 0)) {
+		global[g154] = 2;
+		player.walker_visible = 0;
+		player.commands_allowed = 0;
+		scratch._9e = kernel_run_animation_talk('b', 7, 0);
+		kernel_position_anim(scratch._9e, player.x, player.y, player.scale, player.depth);
+		kernel_synch(KERNEL_ANIM, scratch._9e, KERNEL_PLAYER, 0);
+		digi_play_build(303, 'b', 1, 1);
+		scratch._a8 = 123;
+		scratch._a4 = -1;
+		player.command_ready = 0;
+		return;
+	}
+
+	if (global[player_selected_object] >= 0) {
+		player.command_ready = 0;
+		return;
+	}
+
+	if (player_parse(114, 77, 0)) {
+		global[player_score] = 0;
+		digi_stop(3);
+		player.commands_allowed = 0;
+		global[g135] = -1;
+		scratch._8c = 5;
+		scratch._a6 = 1;
+		player.command_ready = 0;
+		return;
+	}
+
+	if (player_parse(114, 103, 0)) {
+		global[player_score] = 0;
+		digi_stop(3);
+		player.commands_allowed = 0;
+		global[g135] = -1;
+		scratch._8c = 5;
+		scratch._a6 = 2;
+		player.command_ready = 0;
+		return;
+	}
+
+	if (player_parse(126, 132, 0)) {
+		global[g154] = 2;
+		global[player_score] = 0;
+		player.commands_allowed = 0;
+		player.walker_visible = 0;
+		scratch._9e = kernel_run_animation_talk('b', 1, 0);
+		kernel_position_anim(scratch._9e, player.x, player.y, player.scale, player.depth);
+		kernel_synch(KERNEL_ANIM, scratch._9e, KERNEL_PLAYER, 0);
+		digi_play_build_ii('b', 3, 1);
+		scratch._a8 = 400;
+		scratch._a4 = -1;
+		player.command_ready = 0;
+		return;
+	}
+
+	if (player_parse(126, 125, 0)) {
+		global[g154] = 2;
+		global[player_score] = 0;
+		player.commands_allowed = 0;
+		player.walker_visible = 0;
+		scratch._9e = kernel_run_animation_talk('b', 3, 0);
+		kernel_position_anim(scratch._9e, player.x, player.y, player.scale, player.depth);
+		kernel_synch(KERNEL_ANIM, scratch._9e, KERNEL_PLAYER, 0);
+		digi_play_build_ii('b', 3, 1);
+		scratch._a8 = 300;
+		scratch._a4 = -1;
+		player.command_ready = 0;
+		return;
+	}
+
+	if (player_parse(78, 119, 0)) {
+		player.commands_allowed = 0;
+		global[g135] = -1;
+		scratch._8c = 3;
+		player.command_ready = 0;
+	}
 }
 
 void room_303_synchronize(Common::Serializer &s) {
 	for (int16 &v : scratch.sprite)    s.syncAsSint16LE(v);
 	for (int16 &v : scratch.sequence)  s.syncAsSint16LE(v);
 	for (int16 &v : scratch.animation) s.syncAsSint16LE(v);
+	for (auto &ai : scratch.animation_info) ai.synchronize(s);
+	s.syncAsSint16LE(scratch._8c);
+	s.syncAsSint16LE(scratch._8e);
+	s.syncAsSint16LE(scratch._90);
+	s.syncAsSint16LE(scratch._92);
+	s.syncAsSint16LE(scratch._94);
+	s.syncAsSint16LE(scratch._96);
+	s.syncAsSint16LE(scratch._98);
+	s.syncAsSint16LE(scratch._9a);
+	s.syncAsSint16LE(scratch._9c);
+	s.syncAsSint16LE(scratch._9e);
+	s.syncAsSint16LE(scratch._a0);
+	s.syncAsSint16LE(scratch._a2);
+	s.syncAsSint16LE(scratch._a4);
+	s.syncAsSint16LE(scratch._a6);
+	s.syncAsSint16LE(scratch._a8);
+	s.syncAsSint16LE(scratch._aa);
 }
 
 void room_303_preload() {



From noreply at scummvm.org  Tue Jun  9 09:23:55 2026
From: noreply at scummvm.org (dreammaster)
Date: Tue,  9 Jun 2026 09:23:55 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 223bf4652f342909ed6b366a4102fc0802666bdc
Message-ID: <20260609092355.E3D507005BC@ron.scummvm.net>

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

Summary:
223bf4652f MADS: FOREST: Implemented room 304


Commit: 223bf4652f342909ed6b366a4102fc0802666bdc
    https://github.com/scummvm/scummvm/commit/223bf4652f342909ed6b366a4102fc0802666bdc
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-06-09T19:23:42+10:00

Commit Message:
MADS: FOREST: Implemented room 304

Changed paths:
    engines/mads/madsv2/forest/rooms/room304.cpp


diff --git a/engines/mads/madsv2/forest/rooms/room304.cpp b/engines/mads/madsv2/forest/rooms/room304.cpp
index e41c65ca889..e024a8e17ba 100644
--- a/engines/mads/madsv2/forest/rooms/room304.cpp
+++ b/engines/mads/madsv2/forest/rooms/room304.cpp
@@ -20,16 +20,20 @@
  */
 
 #include "mads/madsv2/core/conv.h"
+#include "mads/madsv2/core/digi.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/imath.h"
 #include "mads/madsv2/core/inter.h"
 #include "mads/madsv2/core/kernel.h"
+#include "mads/madsv2/core/midi.h"
+#include "mads/madsv2/core/player.h"
 #include "mads/madsv2/core/sound.h"
 #include "mads/madsv2/core/text.h"
 #include "mads/madsv2/forest/mads/inventory.h"
 #include "mads/madsv2/forest/mads/sounds.h"
 #include "mads/madsv2/forest/mads/words.h"
 #include "mads/madsv2/forest/global.h"
+#include "mads/madsv2/forest/journal.h"
 #include "mads/madsv2/forest/rooms/section1.h"
 #include "mads/madsv2/forest/rooms/room304.h"
 
@@ -39,10 +43,11 @@ namespace Forest {
 namespace Rooms {
 
 struct Scratch {
-	int16 sprite[10];       /* Sprite series handles */
-	int16 sequence[10];     /* Sequence handles      */
-	int16 animation[10];     /* Animation handles     */
+	int16 sprite[10];
+	int16 sequence[10];
+	int16 animation[10];
 	AnimationInfo animation_info[10];
+	int16 _8c, _8e, _90, _92, _94, _96, _98, _9a, _9c, _9e, _a0;
 };
 
 static Scratch scratch;
@@ -54,22 +59,543 @@ static Scratch scratch;
 #define aainfo scratch.animation_info
 
 
+static void room_304_init1() {
+	global[g131] = 0;
+	global[g141] = 0;
+
+	scratch._9c = kernel_run_animation_disp('r', 3, 0);
+	kernel_position_anim(scratch._9c, 60, 136, 93, 3);
+
+	scratch._9a = kernel_run_animation_disp('e', 9, 0);
+	kernel_position_anim(scratch._9a, 87, 150, 98, 3);
+
+	if (global[g064]) {
+		kernel_flip_hotspot(178, 0);
+		kernel_flip_hotspot(179, 0);
+	} else {
+		kernel_timing_trigger(1, 107);
+		aa[4] = kernel_run_animation(kernel_name('n', 2), 105);
+		scratch.animation_info[4]._active = -1;
+		aa[5] = kernel_run_animation(kernel_name('n', 1), 106);
+		scratch.animation_info[5]._active = -1;
+	}
+
+	if (previous_room != KERNEL_LAST) {
+		player.x = 154;
+		player.y = 154;
+		player.facing = 9;
+	}
+
+	if (previous_room == 302) {
+		aa[0] = kernel_run_animation(kernel_name('y', 1), 100);
+		scratch.animation_info[0]._active = -1;
+		scratch._92 = 55;
+		return;
+	}
+
+	if (previous_room == 199)
+		restore_player();
+
+	global[g131] = -1;
+	global[g141] = -1;
+	kernel_reset_animation(scratch._9a, 2);
+	kernel_reset_animation(scratch._9c, 2);
+	global[g133] = 0;
+	global[g143] = 0;
+	player.commands_allowed = -1;
+	player.walker_visible = -1;
+}
+
 static void room_304_init() {
+	midi_stop();
+	digi_stop(2);
+	global[g070] = 0;
+
+	if (previous_room != KERNEL_LAST) {
+		if (previous_room != 199) {
+			player.walker_visible = 0;
+			player.commands_allowed = 0;
+		}
+
+		for (int i = 0; i < 10; i++) {
+			scratch.animation_info[i]._active = 0;
+			scratch.animation_info[i]._frame = 1;
+			scratch.animation_info[i]._val3 = 0;
+			scratch.animation_info[i]._val4 = 0;
+		}
+
+		if (previous_room != 199 && flags[18] != 3)
+			flags[18]++;
+	}
+
+	room_304_init1();
+}
+
+static void room_304_finish() {
+	player.commands_allowed = -1;
+	if (digi_val1)
+		kernel_timing_trigger(1, 107);
+}
+
+static void room_304_restore() {
+	global[g131] = -1;
+	global[g141] = -1;
+	kernel_reset_animation(scratch._9a, 1);
+	kernel_synch(KERNEL_ANIM, scratch._9a, KERNEL_NOW, 0);
+	kernel_reset_animation(scratch._9c, 1);
+	kernel_synch(KERNEL_ANIM, scratch._9c, KERNEL_NOW, 0);
+	player.walker_visible = -1;
+	global[g133] = 0;
+	global[g143] = 0;
+	kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
+	room_304_finish();
+}
+
+static void room_304_anim1() {
+	if (kernel_anim[aa[0]].frame != scratch.animation_info[0]._frame)
+		scratch.animation_info[0]._frame = kernel_anim[aa[0]].frame;
+
+	if (global[player_hyperwalked] == -1) {
+		scratch.animation_info[0]._frame = scratch._92 - 1;
+		kernel_reset_animation(aa[0], scratch.animation_info[0]._frame);
+	}
+}
+
+static void room_304_anim2() {
+	int16 var_2 = -1;
+
+	if (kernel_anim[aa[1]].frame == scratch.animation_info[1]._frame)
+		return;
+
+	scratch.animation_info[1]._frame = kernel_anim[aa[1]].frame;
+
+	if (scratch._96 == 2) {
+		int16 frame = scratch.animation_info[1]._frame;
+		if (frame == 5) {
+			digi_play_build(304, 'e', 3, 1);
+			scratch._9e = 6;
+		} else if (frame == 10) {
+			var_2 = 5;
+		}
+	}
+
+	if (var_2 >= 0) {
+		scratch.animation_info[1]._frame = var_2;
+		kernel_reset_animation(aa[1], var_2);
+	}
+}
+
+static void room_304_anim3() {
+	if (kernel_anim[aa[2]].frame != scratch.animation_info[2]._frame)
+		scratch.animation_info[2]._frame = kernel_anim[aa[2]].frame;
+}
+
+static void room_304_anim4() {
+	int16 var_2 = -1;
+
+	if (kernel_anim[aa[3]].frame == scratch.animation_info[3]._frame)
+		return;
+
+	int16 frame = kernel_anim[aa[3]].frame;
+	scratch.animation_info[3]._frame = frame;
+
+	switch (scratch._98) {
+	case 1:
+		if (frame == 65) {
+			scratch.animation_info[3]._frame = 60;
+			kernel_reset_animation(aa[3], 60);
+		} else if (frame < 65) {
+			if (frame == 34) {
+				scratch.animation_info[3]._frame = 33;
+				kernel_reset_animation(aa[3], 33);
+			} else if (frame > 34) {
+				if (frame == 51) {
+					digi_play_build(304, 'b', 2, 1);
+					scratch._9e = 4;
+				} else if (frame > 51) {
+					if (frame == 56) {
+						scratch.animation_info[3]._frame = 51;
+						kernel_reset_animation(aa[3], 51);
+					} else if (frame == 59) {
+						scratch.animation_info[3]._frame = 57;
+						kernel_reset_animation(aa[3], 57);
+					} else if (frame == 60) {
+						digi_play_build(304, 'e', 2, 1);
+						scratch._9e = 5;
+					}
+				} else {
+					if (frame == 39) {
+						digi_play_build(304, 'e', 1, 1);
+						scratch._9e = 3;
+					} else if (frame == 44) {
+						scratch.animation_info[3]._frame = 39;
+						kernel_reset_animation(aa[3], 39);
+					}
+				}
+			} else {
+				if (frame == 8) {
+					digi_play_build(304, 'r', 1, 1);
+					scratch._9e = 1;
+				} else if (frame == 13) {
+					scratch.animation_info[3]._frame = 8;
+					kernel_reset_animation(aa[3], 8);
+				} else if (frame == 23) {
+					digi_play_build(304, 'b', 1, 1);
+					scratch._9e = 2;
+					kernel_timing_trigger(240, 108);
+				} else if (frame == 25) {
+					scratch.animation_info[3]._frame = 23;
+					kernel_reset_animation(aa[3], 23);
+				} else if (frame == 28) {
+					scratch.animation_info[3]._frame = 25;
+					kernel_reset_animation(aa[3], 25);
+				}
+			}
+		}
+		break;
+	case 3:
+		if (frame == 57) {
+			digi_play_build(304, 'e', 4, 1);
+			scratch._9e = 7;
+		} else if (frame == 62) {
+			var_2 = 57;
+		}
+		break;
+	case 5:
+		if (frame == 1) {
+			global[g064] = -1;
+			digi_play_build(304, '_', 2, 2);
+			scratch._a0 = 2;
+		}
+		break;
+	case 6:
+		if (frame == 1) {
+			digi_play_build(304, '_', 3, 2);
+			scratch._a0 = 3;
+			global[g009] = 0;
+			global_digi_play(14);
+		} else if (frame == 11) {
+			digi_stop(2);
+		}
+		break;
+	case 7:
+		if (frame == 4) {
+			digi_play_build(304, 'b', 3, 1);
+			scratch._9e = 8;
+		} else if (frame == 10) {
+			var_2 = 4;
+		} else if (frame > 10) {
+			if (frame == 12) {
+				digi_play_build(304, '_', 716, 2);
+				scratch._a0 = 4;
+			} else if (frame >= 70 && frame <= 72) {
+				digi_stop(2);
+				scratch._a0 = 0;
+			}
+		}
+		break;
+	case 8:
+		if (frame == 5) {
+			digi_play_build(304, 'e', 2, 1);
+			scratch._9e = 10;
+		} else if (frame == 10) {
+			var_2 = 5;
+		}
+		break;
+	default:
+		break;
+	}
+
+	if (var_2 >= 0) {
+		scratch.animation_info[3]._frame = var_2;
+		kernel_reset_animation(aa[3], var_2);
+	}
+}
+
+static void room_304_anim5() {
+	if (kernel_anim[aa[4]].frame == scratch.animation_info[4]._frame)
+		return;
+
+	int16 frame = kernel_anim[aa[4]].frame;
+	scratch.animation_info[4]._frame = frame;
+
+	if (frame == 4) {
+		scratch.animation_info[4]._frame = 0;
+		kernel_reset_animation(aa[4], 0);
+	}
+}
+
+static void room_304_anim6() {
+	if (kernel_anim[aa[5]].frame == scratch.animation_info[5]._frame)
+		return;
+
+	int16 frame = kernel_anim[aa[5]].frame;
+	scratch.animation_info[5]._frame = frame;
+
+	if (frame == 2) {
+		scratch.animation_info[5]._frame = 0;
+		kernel_reset_animation(aa[5], 0);
+	} else if (frame == 4) {
+		scratch.animation_info[5]._frame = 3;
+		kernel_reset_animation(aa[5], 3);
+	}
 }
 
 static void room_304_daemon() {
+	switch (kernel.trigger) {
+	case 8:
+		if (scratch._a0 == 4) {
+			digi_play_build(304, '_', 716, 2);
+			scratch._a0 = 4;
+		} else {
+			scratch._a0 = 0;
+		}
+		break;
+
+	case 24:
+		if (scratch._8c == 2) {
+			aa[1] = kernel_run_animation(kernel_name('e', 1), 102);
+			scratch.animation_info[1]._active = -1;
+			scratch._96 = 2;
+			kernel_reset_animation(scratch._9a, 0);
+			kernel_synch(KERNEL_ANIM, aa[1], KERNEL_ANIM, scratch._9a);
+			global[g133] = 1;
+			kernel_timing_trigger(1, 107);
+		}
+		break;
+
+	case 25:
+		break;
+
+	case 26:
+		if (scratch._90 == 3) {
+			kernel_reset_animation(scratch._9a, 0);
+			kernel_synch(KERNEL_ANIM, scratch._9a, KERNEL_NOW, 0);
+			global[g133] = 1;
+			dont_frag_the_palette();
+			kernel_abort_animation(aa[5]);
+			scratch.animation_info[5]._active = 0;
+			kernel_flip_hotspot(178, 0);
+			kernel_flip_hotspot(179, 0);
+			aa[3] = kernel_run_animation(kernel_name('e', 2), 103);
+			scratch.animation_info[3]._active = -1;
+			scratch._98 = 3;
+			kernel_timing_trigger(1, 107);
+		}
+		break;
+
+	case 100:
+		kernel_abort_animation(aa[0]);
+		scratch.animation_info[0]._active = 0;
+		if (flags[18] == 1) {
+			aa[3] = kernel_run_animation(kernel_name('t', 1), 103);
+			scratch.animation_info[3]._active = -1;
+			scratch.animation_info[3]._frame = 0;
+			scratch._98 = 1;
+			kernel_timing_trigger(1, 107);
+		} else if (global[g064]) {
+			room_304_restore();
+		} else {
+			global[g141] = -1;
+			kernel_reset_animation(scratch._9c, 1);
+			kernel_synch(KERNEL_ANIM, scratch._9c, KERNEL_NOW, 0);
+			player.walker_visible = -1;
+			global[g143] = 0;
+			kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
+			aa[3] = kernel_run_animation(kernel_name('e', 1), 103);
+			scratch.animation_info[3]._active = -1;
+			scratch.animation_info[3]._frame = 0;
+			scratch._98 = 8;
+			kernel_timing_trigger(1, 107);
+		}
+		break;
+
+	case 101:
+		kernel_abort_animation(aa[scratch._94]);
+		scratch.animation_info[scratch._94]._active = 0;
+		kernel_reset_animation(scratch._9c, 1);
+		kernel_synch(KERNEL_ANIM, scratch._9c, KERNEL_NOW, 0);
+		global[g143] = 0;
+		room_304_finish();
+		break;
+
+	case 102:
+		dont_frag_the_palette();
+		kernel_abort_animation(aa[1]);
+		scratch.animation_info[1]._active = 0;
+		if (scratch._96 == 2) {
+			global[g070] = -1;
+			display_journal();
+		}
+		kernel_reset_animation(scratch._9a, 1);
+		kernel_synch(KERNEL_ANIM, scratch._9a, KERNEL_NOW, 0);
+		global[g133] = 0;
+		room_304_finish();
+		break;
+
+	case 103:
+		dont_frag_the_palette();
+		kernel_abort_animation(aa[3]);
+		scratch.animation_info[3]._active = 0;
+		switch (scratch._98) {
+		case 1:
+			room_304_restore();
+			break;
+		case 3:
+			aa[3] = kernel_run_animation(kernel_name('e', 3), 103);
+			scratch.animation_info[3]._active = -1;
+			scratch.animation_info[3]._frame = 0;
+			scratch._98 = 4;
+			kernel_timing_trigger(1, 107);
+			break;
+		case 4:
+			dont_frag_the_palette();
+			kernel_abort_animation(aa[4]);
+			scratch.animation_info[4]._active = 0;
+			aa[3] = kernel_run_animation(kernel_name('e', 4), 103);
+			scratch.animation_info[3]._active = -1;
+			scratch.animation_info[3]._frame = 0;
+			scratch._98 = 5;
+			kernel_timing_trigger(1, 107);
+			break;
+		case 5:
+			aa[3] = kernel_run_animation(kernel_name('e', 5), 103);
+			scratch.animation_info[3]._active = -1;
+			scratch.animation_info[3]._frame = 0;
+			scratch._98 = 6;
+			kernel_timing_trigger(1, 107);
+			break;
+		case 6:
+			kernel_reset_animation(scratch._9c, 0);
+			kernel_synch(KERNEL_ANIM, aa[2], KERNEL_ANIM, scratch._9c);
+			global[g143] = 1;
+			player.walker_visible = 0;
+			kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
+			aa[3] = kernel_run_animation(kernel_name('b', 1), 103);
+			scratch.animation_info[3]._active = -1;
+			scratch.animation_info[3]._frame = 0;
+			scratch._98 = 7;
+			kernel_timing_trigger(1, 107);
+			break;
+		case 7:
+			global[g131] = -1;
+			global[g141] = -1;
+			kernel_reset_animation(scratch._9a, 1);
+			kernel_synch(KERNEL_ANIM, scratch._9a, KERNEL_NOW, 0);
+			kernel_reset_animation(scratch._9c, 1);
+			kernel_synch(KERNEL_ANIM, scratch._9c, KERNEL_NOW, 0);
+			player.walker_visible = -1;
+			global[g133] = 0;
+			global[g143] = 0;
+			kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
+			player.commands_allowed = -1;
+			global[walker_converse_state] = 0;
+			close_journal(3);
+			break;
+		case 8:
+			global[g131] = -1;
+			kernel_reset_animation(scratch._9a, 1);
+			kernel_synch(KERNEL_ANIM, scratch._9a, KERNEL_NOW, 0);
+			global[g133] = 0;
+			room_304_finish();
+			break;
+		default:
+			break;
+		}
+		break;
+
+	case 104:
+		switch (scratch._9e) {
+		case 2:
+			scratch.animation_info[3]._frame = 37;
+			kernel_reset_animation(aa[3], 37);
+			break;
+		case 4:
+			scratch.animation_info[3]._frame = 59;
+			kernel_reset_animation(aa[3], 59);
+			break;
+		default:
+			break;
+		}
+		break;
+
+	case 108:
+		scratch.animation_info[3]._frame = 25;
+		kernel_reset_animation(aa[3], 25);
+		break;
+
+	default:
+		break;
+	}
+
+	global_anim1(9, scratch._9a, global[g131], &global[g132]);
+	global_anim2(3, scratch._9c, global[g141], &global[g142]);
+
+	if (scratch.animation_info[0]._active) room_304_anim1();
+	if (scratch.animation_info[3]._active) room_304_anim4();
+	if (scratch.animation_info[2]._active) room_304_anim3();
+	if (scratch.animation_info[1]._active) room_304_anim2();
+	if (scratch.animation_info[4]._active) room_304_anim5();
+	if (scratch.animation_info[5]._active) room_304_anim6();
 }
 
 static void room_304_pre_parser() {
+	if (player_parse(13, 35, 0))
+		player.walk_off_edge_to_room = 302;
 }
 
 static void room_304_parser() {
+	if (global[walker_converse_state]) {
+		if (global[g064]) {
+			player.commands_allowed = 0;
+			digi_play_build_ii('c', 1, 1);
+			scratch._9e = 9;
+		} else {
+			player.commands_allowed = 0;
+			global[g150] = -1;
+			scratch._90 = 3;
+		}
+		player.command_ready = 0;
+		return;
+	}
+
+	if (global[player_selected_object] >= 0) {
+		player.command_ready = 0;
+		return;
+	}
+
+	if (player_parse(78, 178, 0)) {
+		player.commands_allowed = 0;
+		global[g150] = -1;
+		scratch._90 = 3;
+		player.command_ready = 0;
+		return;
+	}
+
+	if (player_parse(78, 179, 0)) {
+		player.commands_allowed = 0;
+		global[g135] = -1;
+		scratch._8c = 2;
+		player.command_ready = 0;
+	}
 }
 
 void room_304_synchronize(Common::Serializer &s) {
 	for (int16 &v : scratch.sprite)    s.syncAsSint16LE(v);
 	for (int16 &v : scratch.sequence)  s.syncAsSint16LE(v);
 	for (int16 &v : scratch.animation) s.syncAsSint16LE(v);
+	for (auto &ai : scratch.animation_info) ai.synchronize(s);
+	s.syncAsSint16LE(scratch._8c);
+	s.syncAsSint16LE(scratch._8e);
+	s.syncAsSint16LE(scratch._90);
+	s.syncAsSint16LE(scratch._92);
+	s.syncAsSint16LE(scratch._94);
+	s.syncAsSint16LE(scratch._96);
+	s.syncAsSint16LE(scratch._98);
+	s.syncAsSint16LE(scratch._9a);
+	s.syncAsSint16LE(scratch._9c);
+	s.syncAsSint16LE(scratch._9e);
+	s.syncAsSint16LE(scratch._a0);
 }
 
 void room_304_preload() {



From noreply at scummvm.org  Tue Jun  9 10:24:22 2026
From: noreply at scummvm.org (ScummVM-Translations)
Date: Tue,  9 Jun 2026 10:24:22 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 eaa1122db6a7908db58850150febaea0e2a1b10c
Message-ID: <20260609102422.995827005BC@ron.scummvm.net>

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

Summary:
eaa1122db6 I18N: Update translation (Italian)


Commit: eaa1122db6a7908db58850150febaea0e2a1b10c
    https://github.com/scummvm/scummvm/commit/eaa1122db6a7908db58850150febaea0e2a1b10c
Author: Walter Agazzi (walter.agazzi at protonmail.com)
Date: 2026-06-09T10:24:13Z

Commit Message:
I18N: Update translation (Italian)

Currently translated at 100.0% (3206 of 3206 strings)

Changed paths:
    po/it_IT.po


diff --git a/po/it_IT.po b/po/it_IT.po
index e430ed6b6a5..31685c62986 100644
--- a/po/it_IT.po
+++ b/po/it_IT.po
@@ -8,7 +8,7 @@ msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
 "POT-Creation-Date: 2026-06-08 11:04+0000\n"
-"PO-Revision-Date: 2026-06-04 19:00+0000\n"
+"PO-Revision-Date: 2026-06-09 10:24+0000\n"
 "Last-Translator: Walter Agazzi <walter.agazzi at protonmail.com>\n"
 "Language-Team: Italian <https://translations.scummvm.org/projects/scummvm/"
 "scummvm/it/>\n"
@@ -13663,16 +13663,12 @@ msgstr ""
 "Permette l'accesso a qualsiasi livello del gioco senza richiedere password"
 
 #: engines/scumm/metaengine.cpp:900
-#, fuzzy
-#| msgid "No date saved"
 msgid "No damage"
-msgstr "Nessuna data salvata"
+msgstr "Nessun danno"
 
 #: engines/scumm/metaengine.cpp:901
-#, fuzzy
-#| msgid "Disable save menu"
 msgid "Disable player damage"
-msgstr "Disabilita men?? di salvataggio"
+msgstr "Disabilita i danni al giocatore"
 
 #: engines/scumm/metaengine.cpp:1061
 msgid "Down left"
@@ -13740,10 +13736,8 @@ msgid "Fire / select"
 msgstr "Spara / seleziona"
 
 #: engines/scumm/metaengine.cpp:1198
-#, fuzzy
-#| msgid "Skip / menu back"
 msgid "Walk / menu back"
-msgstr "Salta / indietro (men??)"
+msgstr "Cammina / Men?? indietro"
 
 #: engines/scumm/metaengine.cpp:1203
 msgid "Skip / menu back"



From noreply at scummvm.org  Tue Jun  9 11:29:44 2026
From: noreply at scummvm.org (sev-)
Date: Tue,  9 Jun 2026 11:29:44 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 0c19a66330371c9570977917623fecb52d7deb1c
Message-ID: <20260609112944.BC6837005BC@ron.scummvm.net>

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

Summary:
0c19a66330 DIRECTOR: Allow overriding random seed via GUI option


Commit: 0c19a66330371c9570977917623fecb52d7deb1c
    https://github.com/scummvm/scummvm/commit/0c19a66330371c9570977917623fecb52d7deb1c
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2026-06-09T13:29:22+02:00

Commit Message:
DIRECTOR: Allow overriding random seed via GUI option

Please denote that the original always had seed inited with 1,
e.g. not a conventional timestamp or anything. The Director Player,
thus, was generating the same random number sequence on restart

Changed paths:
    engines/director/util.cpp


diff --git a/engines/director/util.cpp b/engines/director/util.cpp
index 43903f030d0..0d42c361f1f 100644
--- a/engines/director/util.cpp
+++ b/engines/director/util.cpp
@@ -1206,6 +1206,12 @@ Common::Path dumpFactoryName(const char *prefix, const char *name, const char *e
 void RandomState::setSeed(int seed) {
 	init(32);
 
+	// If we a GUI override, use that instead of the provided seed
+	if (ConfMan.hasKey("random_seed")) {
+		debugC(1, kDebugLingoExec, "Random seed is requested to be set to %d, but overridden by config to %d", seed, ConfMan.getInt("random_seed"));
+		seed = ConfMan.getInt("random_seed");
+	}
+
 	_seed = seed ? seed : 1;
 }
 
@@ -1238,7 +1244,9 @@ void RandomState::init(int len) {
 		_len = (1 << len) - 1;
 	}
 
-	_seed = 1;
+	// The original is _always_ initalized with a seed of 1. It is hardcoded and is by design
+	// The developers were offered to use `set the randomSeed` Lingo
+	setSeed(1);
 	_mask = masks[len - 2];
 }
 



From noreply at scummvm.org  Tue Jun  9 12:03:14 2026
From: noreply at scummvm.org (tag2015)
Date: Tue,  9 Jun 2026 12:03:14 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 13994b4606b79195d49bad9e854cb056c9042321
Message-ID: <20260609120314.2DBA17005BC@ron.scummvm.net>

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

Summary:
13994b4606 AGS: Update Wadjet games 2/2


Commit: 13994b4606b79195d49bad9e854cb056c9042321
    https://github.com/scummvm/scummvm/commit/13994b4606b79195d49bad9e854cb056c9042321
Author: Walter Agazzi (walter.agazzi at protonmail.com)
Date: 2026-06-09T13:53:16+02:00

Commit Message:
AGS: Update Wadjet games 2/2

Shardlight / Strangeland / Technobabylon
Fix #16844, #16845, #16846

Changed paths:
    engines/ags/detection_tables.h


diff --git a/engines/ags/detection_tables.h b/engines/ags/detection_tables.h
index da58997bb15..e96ec24354d 100644
--- a/engines/ags/detection_tables.h
+++ b/engines/ags/detection_tables.h
@@ -5352,24 +5352,29 @@ const AGSGameDescription GAME_DESCRIPTIONS[] = {
 	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("shardlight", "shardlight.exe", "400682c45fb6c0754ef545b96e7efc1d", 391410784, 0),
 	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("shardlight", "shardlight.exe", "400682c45fb6c0754ef545b96e7efc1d", 391410671, 0),
 	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("shardlight", "shardlight.exe", "400682c45fb6c0754ef545b96e7efc1d", 391411135, 0),
+	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("shardlight", "shardlight.exe", "b9fec0a50efd3a7017c3f69de79ae889", 391393730, 0),
 	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("shardlight", "Shardlight.ags", "df94d3c1cec961c89894c676b000e7bc", 388236426, 0),  // Linux
 	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("shardlight", "Shardlight.ags", "df94d3c1cec961c89894c676b000e7bc", 388236696, 0),  // Linux
 	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("shardlight", "Shardlight.ags", "df94d3c1cec961c89894c676b000e7bc", 388237388, 0),  // Linux
 	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("shardlight", "Shardlight.ags", "df94d3c1cec961c89894c676b000e7bc", 388237275, 0),  // Linux
 	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("shardlight", "Shardlight.ags", "df94d3c1cec961c89894c676b000e7bc", 388237739, 0),  // Linux
+	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("shardlight", "Shardlight.ags", "df94d3c1cec961c89894c676b000e7bc", 388254638, 0),  // Linux
 	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("shardlight", "Shardlight.ags", "df94d3c1cec961c89894c676b000e7bc", 388266389, 0), // Steamdeck
+	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("shardlight", "Shardlight.ags", "df94d3c1cec961c89894c676b000e7bc", 388283662, 0), // Steamdeck
 	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("shardlight", "ac2game.dat", "df94d3c1cec961c89894c676b000e7bc", 640348156, 0),  // Mac
 	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("shardlight", "ac2game.dat", "df94d3c1cec961c89894c676b000e7bc", 388236426, 0),  // Mac
 	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("shardlight", "ac2game.dat", "df94d3c1cec961c89894c676b000e7bc", 388236696, 0),  // Mac
 	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("shardlight", "ac2game.dat", "df94d3c1cec961c89894c676b000e7bc", 388237388, 0),  // Mac
 	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("shardlight", "ac2game.dat", "df94d3c1cec961c89894c676b000e7bc", 388237275, 0),  // Mac
 	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("shardlight", "ac2game.dat", "df94d3c1cec961c89894c676b000e7bc", 388237739, 0),  // Mac
+	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("shardlight", "ac2game.dat", "df94d3c1cec961c89894c676b000e7bc", 388254638, 0),  // Mac
 	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("shardlight", "shardlight.exe", "cc447656f408ac32e8443274c3f70c67", 643461930, 0), // Win v2.4
 	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("shardlight", "shardlight.exe", "400682c45fb6c0754ef545b96e7efc1d", 391410104, 0), // Win v3.0
 	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("shardlight", "shardlight.exe", "400682c45fb6c0754ef545b96e7efc1d", 391410366, 0), // Win v3.1
 	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("shardlight", "shardlight.exe", "400682c45fb6c0754ef545b96e7efc1d", 391411058, 0), // Win v3.2
 	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("shardlight", "shardlight.exe", "400682c45fb6c0754ef545b96e7efc1d", 391410945, 0), // Win v3.3
 	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("shardlight", "shardlight.exe", "400682c45fb6c0754ef545b96e7efc1d", 391411409, 0), // Win v3.4
+	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("shardlight", "shardlight.exe", "b9fec0a50efd3a7017c3f69de79ae889", 391394004, 0), // Win v3.7
 	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("shardlight", "ac2game.dat", "df94d3c1cec961c89894c676b000e7bc", 640346706, 0), // Mac v2.3a
 	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("shardlight", "ac2game.dat", "df94d3c1cec961c89894c676b000e7bc", 640348438, 0), // Mac v2.4
 	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("shardlight", "ac2game.dat", "df94d3c1cec961c89894c676b000e7bc", 388236708, 0), // Mac v3.0
@@ -5377,11 +5382,13 @@ const AGSGameDescription GAME_DESCRIPTIONS[] = {
 	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("shardlight", "ac2game.dat", "df94d3c1cec961c89894c676b000e7bc", 388237662, 0), // Mac v3.2-v3.4
 	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("shardlight", "ac2game.dat", "df94d3c1cec961c89894c676b000e7bc", 388237549, 0), // Mac v3.5
 	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("shardlight", "ac2game.dat", "df94d3c1cec961c89894c676b000e7bc", 388238013, 0), // Mac v3.6
+	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("shardlight", "ac2game.dat", "df94d3c1cec961c89894c676b000e7bc", 388254912, 0), // Mac v3.7
 	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("shardlight", "Shardlight.ags", "df94d3c1cec961c89894c676b000e7bc", 388236708, 0), // Linux v3.0
 	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("shardlight", "Shardlight.ags", "df94d3c1cec961c89894c676b000e7bc", 388236970, 0), // Linux v3.1
 	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("shardlight", "Shardlight.ags", "df94d3c1cec961c89894c676b000e7bc", 388237662, 0), // Linux v3.2
 	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("shardlight", "Shardlight.ags", "df94d3c1cec961c89894c676b000e7bc", 388237549, 0), // Linux v3.5
 	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("shardlight", "Shardlight.ags", "df94d3c1cec961c89894c676b000e7bc", 388238013, 0), // Linux v3.6
+	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("shardlight", "Shardlight.ags", "df94d3c1cec961c89894c676b000e7bc", 388254912, 0), // Linux v3.7
 	GAME_ENTRY_EN("shardlight", "shardlight.exe", "ee801fba52d252249677a9170bd2db96", 642151325),
 	GAME_ENTRY_EN("shardlight", "shardlight.exe", "ee801fba52d252249677a9170bd2db96", 642142591),
 	GAME_ENTRY_EN("shivah", "shivah.exe", "6cddccb3744ec5c6af7c398fb7b3b11c", 19542815), // Official website
@@ -5434,23 +5441,30 @@ const AGSGameDescription GAME_DESCRIPTIONS[] = {
 	DETECTION_ENTRY_GUIO("strangeland", "Strangeland.exe", "c5978d50a9b7ee1c8a50a731847d8504", 3187004936, Common::UNK_LANG, "GOG.com", GUIO3(GUIO_NOLANG, GUIO_NOLAUNCHLOAD, GAMEOPTION_NO_SAVELOAD), ADGF_NO_FLAGS, 0), // Win v3.0
 	DETECTION_ENTRY_GUIO("strangeland", "Strangeland.exe", "a20e9de4567f25b692c724d9783436ac", 3012885588, Common::UNK_LANG, "GOG.com", GUIO3(GUIO_NOLANG, GUIO_NOLAUNCHLOAD, GAMEOPTION_NO_SAVELOAD), ADGF_NO_FLAGS, 0), // Win v3.2c
 	DETECTION_ENTRY_GUIO("strangeland", "Strangeland.exe", "a20e9de4567f25b692c724d9783436ac", 3012887472, Common::UNK_LANG, "GOG.com", GUIO3(GUIO_NOLANG, GUIO_NOLAUNCHLOAD, GAMEOPTION_NO_SAVELOAD), ADGF_NO_FLAGS, 0), // Win v3.3
+	DETECTION_ENTRY_GUIO("strangeland", "Strangeland.exe", "a20e9de4567f25b692c724d9783436ac", 3013047904, Common::UNK_LANG, "GOG.com", GUIO3(GUIO_NOLANG, GUIO_NOLAUNCHLOAD, GAMEOPTION_NO_SAVELOAD), ADGF_NO_FLAGS, 0), // Win v4.1
 	DETECTION_ENTRY_GUIO("strangeland", "ac2game.dat", "511e08ad31c4020a9ca01b6f7ce1365f", 3009634736, Common::UNK_LANG, "GOG.com", GUIO3(GUIO_NOLANG, GUIO_NOLAUNCHLOAD, GAMEOPTION_NO_SAVELOAD), ADGF_NO_FLAGS, 0), // Mac
 	DETECTION_ENTRY_GUIO("strangeland", "ac2game.dat", "7e5abc5202098bd00ddef999854eb9ab", 3183368017, Common::UNK_LANG, "GOG.com", GUIO3(GUIO_NOLANG, GUIO_NOLAUNCHLOAD, GAMEOPTION_NO_SAVELOAD), ADGF_NO_FLAGS, 0), // Mac
 	DETECTION_ENTRY_GUIO("strangeland", "ac2game.dat", "e4b9f0aa26c1115143ad483c99bc9383", 3183880236, Common::UNK_LANG, "GOG.com", GUIO3(GUIO_NOLANG, GUIO_NOLAUNCHLOAD, GAMEOPTION_NO_SAVELOAD), ADGF_NO_FLAGS, 0), // Mac v2.5a
 	DETECTION_ENTRY_GUIO("strangeland", "ac2game.dat", "86e8b93b8231d6c571669f1621561a21", 3183886917, Common::UNK_LANG, "GOG.com", GUIO3(GUIO_NOLANG, GUIO_NOLAUNCHLOAD, GAMEOPTION_NO_SAVELOAD), ADGF_NO_FLAGS, 0), // Mac v2.7
 	DETECTION_ENTRY_GUIO("strangeland", "ac2game.dat", "c0d0009485795a4ff0cf9dbe5ad82a2f", 3183902196, Common::UNK_LANG, "GOG.com", GUIO3(GUIO_NOLANG, GUIO_NOLAUNCHLOAD, GAMEOPTION_NO_SAVELOAD), ADGF_NO_FLAGS, 0), // Mac v3?
+	DETECTION_ENTRY_GUIO("strangeland", "ac2game.dat", "0d676355c58cde3a7ce7a61abe3180e2", 3009812556, Common::UNK_LANG, "GOG.com", GUIO3(GUIO_NOLANG, GUIO_NOLAUNCHLOAD, GAMEOPTION_NO_SAVELOAD), ADGF_NO_FLAGS, 0), // Mac v4.1
+	DETECTION_ENTRY_GUIO("strangeland", "Strangeland.ags", "0d676355c58cde3a7ce7a61abe3180e2", 3009812556, Common::UNK_LANG, "GOG.com", GUIO3(GUIO_NOLANG, GUIO_NOLAUNCHLOAD, GAMEOPTION_NO_SAVELOAD), ADGF_NO_FLAGS, 0), // Linux v4.1
 	DETECTION_ENTRY_GUIO("strangeland", "Strangeland.exe", "c5978d50a9b7ee1c8a50a731847d8504", 3186428869, Common::UNK_LANG, "Steam", GUIO3(GUIO_NOLANG, GUIO_NOLAUNCHLOAD, GAMEOPTION_NO_SAVELOAD), ADGF_NO_FLAGS, 0), // Win 2.3
 	DETECTION_ENTRY_GUIO("strangeland", "Strangeland.exe", "c5978d50a9b7ee1c8a50a731847d8504", 3187004654, Common::UNK_LANG, "Steam", GUIO3(GUIO_NOLANG, GUIO_NOLAUNCHLOAD, GAMEOPTION_NO_SAVELOAD), ADGF_NO_FLAGS, 0), // Win-Linux v3.0
 	DETECTION_ENTRY_GUIO("strangeland", "Strangeland.exe", "a20e9de4567f25b692c724d9783436ac", 3012885224, Common::UNK_LANG, "Steam", GUIO3(GUIO_NOLANG, GUIO_NOLAUNCHLOAD, GAMEOPTION_NO_SAVELOAD), ADGF_NO_FLAGS, 0), // Win v3.2?
 	DETECTION_ENTRY_GUIO("strangeland", "Strangeland.exe", "a20e9de4567f25b692c724d9783436ac", 3012885314, Common::UNK_LANG, "Steam", GUIO3(GUIO_NOLANG, GUIO_NOLAUNCHLOAD, GAMEOPTION_NO_SAVELOAD), ADGF_NO_FLAGS, 0), // Win v3.2c
 	DETECTION_ENTRY_GUIO("strangeland", "Strangeland.exe", "a20e9de4567f25b692c724d9783436ac", 3012887258, Common::UNK_LANG, "Steam", GUIO3(GUIO_NOLANG, GUIO_NOLAUNCHLOAD, GAMEOPTION_NO_SAVELOAD), ADGF_NO_FLAGS, 0), // Win v3.3
+	DETECTION_ENTRY_GUIO("strangeland", "Strangeland.exe", "a20e9de4567f25b692c724d9783436ac", 3013047630, Common::UNK_LANG, "Steam", GUIO3(GUIO_NOLANG, GUIO_NOLAUNCHLOAD, GAMEOPTION_NO_SAVELOAD), ADGF_NO_FLAGS, 0), // Win v4.1
 	DETECTION_ENTRY_GUIO("strangeland", "Strangeland.ags", "186db7411f5dd4b8996be8a47d3a15ea", 3009649966, Common::UNK_LANG, "Steam", GUIO3(GUIO_NOLANG, GUIO_NOLAUNCHLOAD, GAMEOPTION_NO_SAVELOAD), ADGF_NO_FLAGS, 0), // Linux v3.2c
 	DETECTION_ENTRY_GUIO("strangeland", "Strangeland.ags", "d305575f31c9dbf0ddfe156a4e313261", 3009651910, Common::UNK_LANG, "Steam", GUIO3(GUIO_NOLANG, GUIO_NOLAUNCHLOAD, GAMEOPTION_NO_SAVELOAD), ADGF_NO_FLAGS, 0), // Linux v3.3
+	DETECTION_ENTRY_GUIO("strangeland", "Strangeland.ags", "a275c941ce0afff0ef69fb7376c04e17", 3009812282, Common::UNK_LANG, "Steam", GUIO3(GUIO_NOLANG, GUIO_NOLAUNCHLOAD, GAMEOPTION_NO_SAVELOAD), ADGF_NO_FLAGS, 0), // Linux v4.1
 	DETECTION_ENTRY_GUIO("strangeland", "Strangeland.ags", "be199765b7e2d7e01a96d215f26e4aa8", 3010815535, Common::UNK_LANG, "Steam", GUIO3(GUIO_NOLANG, GUIO_NOLAUNCHLOAD, GAMEOPTION_NO_SAVELOAD), ADGF_NO_FLAGS, 0), // Steamdeck
+	DETECTION_ENTRY_GUIO("strangeland", "Strangeland.ags", "82d4462a5d040f0a1beb383ac3fc2271", 3010975907, Common::UNK_LANG, "Steam", GUIO3(GUIO_NOLANG, GUIO_NOLAUNCHLOAD, GAMEOPTION_NO_SAVELOAD), ADGF_NO_FLAGS, 0), // Steamdeck
 	DETECTION_ENTRY_GUIO("strangeland", "ac2game.dat", "e037ab3414dec971b90715c7dbb56f4c", 3183901914, Common::UNK_LANG, "Steam", GUIO3(GUIO_NOLANG, GUIO_NOLAUNCHLOAD, GAMEOPTION_NO_SAVELOAD), ADGF_NO_FLAGS, 0), // Mac
 	DETECTION_ENTRY_GUIO("strangeland", "ac2game.dat", "f82b54c827a6c77c9f3922f8d3c52565", 3183901922, Common::UNK_LANG, "Steam", GUIO3(GUIO_NOLANG, GUIO_NOLAUNCHLOAD, GAMEOPTION_NO_SAVELOAD), ADGF_NO_FLAGS, 0), // Mac
 	DETECTION_ENTRY_GUIO("strangeland", "ac2game.dat", "186db7411f5dd4b8996be8a47d3a15ea", 3009649966, Common::UNK_LANG, "Steam", GUIO3(GUIO_NOLANG, GUIO_NOLAUNCHLOAD, GAMEOPTION_NO_SAVELOAD), ADGF_NO_FLAGS, 0), // Mac v3.6
 	DETECTION_ENTRY_GUIO("strangeland", "ac2game.dat", "d305575f31c9dbf0ddfe156a4e313261", 3009651910, Common::UNK_LANG, "Steam", GUIO3(GUIO_NOLANG, GUIO_NOLAUNCHLOAD, GAMEOPTION_NO_SAVELOAD), ADGF_NO_FLAGS, 0), // Mac v3.6a
+	DETECTION_ENTRY_GUIO("strangeland", "ac2game.dat", "a275c941ce0afff0ef69fb7376c04e17", 3009812282, Common::UNK_LANG, "Steam", GUIO3(GUIO_NOLANG, GUIO_NOLAUNCHLOAD, GAMEOPTION_NO_SAVELOAD), ADGF_NO_FLAGS, 0), // Mac v4.1
 	GAME_ENTRY("sulifallenharmony", "Suli Fallen Harmony.exe", "8a58836830dce896e4366f57791ab6cc", 35900870),  // v1.4 itch.io En-Fr
 	GAME_ENTRY_STEAM("sumatra", "sumatra fate of yandi.exe", "57c868b1a81c0335ab60970292cd79d8", 170088886),  // En-Fr-De-Es
 	GAME_ENTRY_STEAM("sumatra", "sumatra fate of yandi.exe", "b4407ac542f316456f549fc8a60dccae", 170634789),
@@ -5480,17 +5494,21 @@ const AGSGameDescription GAME_DESCRIPTIONS[] = {
 	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("technobabylon", "technobabylon.exe", "6475b8eb253b8d992a052869de4632cb", 420115055, 0),  // Win v3.0.5
 	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("technobabylon", "technobabylon.exe", "6475b8eb253b8d992a052869de4632cb", 420128182, 0),  // Win v3.0.6d
 	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("technobabylon", "technobabylon.exe", "6475b8eb253b8d992a052869de4632cb", 420132618, 0),  // Win v3.6
+	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("technobabylon", "technobabylon.exe", "5b51997553c61ec6beebd7d43f6e8243", 420117826, 0),  // Win v3.9
 	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("technobabylon", "technobabylon.ags", "9d48667020cf3e3612a753934b16cc04", 416922690, 0),  // Linux v3.0
 	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("technobabylon", "technobabylon.ags", "9d48667020cf3e3612a753934b16cc04", 416929445, 0),  // Linux v3.0.1
 	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("technobabylon", "technobabylon.ags", "9d48667020cf3e3612a753934b16cc04", 416930395, 0),  // Linux v3.0.5
 	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("technobabylon", "technobabylon.ags", "9d48667020cf3e3612a753934b16cc04", 416943522, 0),  // Linux v3.0.6d
 	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("technobabylon", "technobabylon.ags", "9d48667020cf3e3612a753934b16cc04", 416947958, 0),  // Linux v3.6
+	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("technobabylon", "technobabylon.ags", "9d48667020cf3e3612a753934b16cc04", 416967470, 0),  // Linux v3.9
 	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("technobabylon", "technobabylon.ags", "9d48667020cf3e3612a753934b16cc04", 416970420, 0),  // Steamdeck
+	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("technobabylon", "technobabylon.ags", "9d48667020cf3e3612a753934b16cc04", 416990450, 0),  // Steamdeck
 	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("technobabylon", "ac2game.dat", "570e69be27d3fa94b50f2779100e3fed", 445700420, 0),  // Mac
 	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("technobabylon", "ac2game.dat", "9d48667020cf3e3612a753934b16cc04", 416929445, 0),  // Mac
 	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("technobabylon", "ac2game.dat", "9d48667020cf3e3612a753934b16cc04", 416930395, 0),  // Mac v3.0.5
 	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("technobabylon", "ac2game.dat", "9d48667020cf3e3612a753934b16cc04", 416943522, 0),  // Mac v3.0.6d
 	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("technobabylon", "ac2game.dat", "9d48667020cf3e3612a753934b16cc04", 416947958, 0),  // Mac v3.6
+	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("technobabylon", "ac2game.dat", "9d48667020cf3e3612a753934b16cc04", 416967425, 0),  // Mac v3.9
 	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("technobabylon", "technobabylon.exe", "83cd1ad574bcfffe1b11504a32402b1e", 448796372, 0), // Win v2.2
 	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("technobabylon", "technobabylon.exe", "83cd1ad574bcfffe1b11504a32402b1e", 448825442, 0), // Win v2.5
 	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("technobabylon", "technobabylon.exe", "83cd1ad574bcfffe1b11504a32402b1e", 448836736, 0), // Win v2.5a
@@ -5499,6 +5517,7 @@ const AGSGameDescription GAME_DESCRIPTIONS[] = {
 	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("technobabylon", "technobabylon.exe", "6475b8eb253b8d992a052869de4632cb", 420115313, 0), // Win v3.0.5
 	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("technobabylon", "technobabylon.exe", "6475b8eb253b8d992a052869de4632cb", 420128440, 0), // Win v3.0.6d
 	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("technobabylon", "technobabylon.exe", "6475b8eb253b8d992a052869de4632cb", 420132942, 0), // Win v3.6
+	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("technobabylon", "technobabylon.exe", "5b51997553c61ec6beebd7d43f6e8243", 420117324, 0), // Win v3.9
 	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("technobabylon", "ac2game.dat", "570e69be27d3fa94b50f2779100e3fed", 445671616, 0), // Mac v2.2a
 	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("technobabylon", "ac2game.dat", "570e69be27d3fa94b50f2779100e3fed", 445711980, 0), // Mac v2.5a
 	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("technobabylon", "ac2game.dat", "9d48667020cf3e3612a753934b16cc04", 416922956, 0), // Mac v3.0
@@ -5506,11 +5525,13 @@ const AGSGameDescription GAME_DESCRIPTIONS[] = {
 	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("technobabylon", "ac2game.dat", "9d48667020cf3e3612a753934b16cc04", 416930653, 0), // Mac v3.1-v3.2
 	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("technobabylon", "ac2game.dat", "9d48667020cf3e3612a753934b16cc04", 416943780, 0), // Mac v3.3c
 	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("technobabylon", "ac2game.dat", "9d48667020cf3e3612a753934b16cc04", 416948282, 0), // Mac v3.6
+	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("technobabylon", "ac2game.dat", "9d48667020cf3e3612a753934b16cc04", 416966968, 0), // Mac v3.9
 	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("technobabylon", "technobabylon.ags", "9d48667020cf3e3612a753934b16cc04", 416929711, 0), // Linux v3.0.1
 	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("technobabylon", "technobabylon.ags", "9d48667020cf3e3612a753934b16cc04", 416930653, 0), // Linux v3.0.5
 	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("technobabylon", "technobabylon.ags", "9d48667020cf3e3612a753934b16cc04", 416942832, 0), // Linux v3.0.6c
 	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("technobabylon", "technobabylon.ags", "9d48667020cf3e3612a753934b16cc04", 416943780, 0), // Linux v3.0.6d
 	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("technobabylon", "technobabylon.ags", "9d48667020cf3e3612a753934b16cc04", 416948282, 0), // Linux v3.6
+	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("technobabylon", "technobabylon.ags", "9d48667020cf3e3612a753934b16cc04", 416966968, 0), // Linux v3.9
 	GAME_ENTRY_EN("technobabylon", "technobabylon.exe", "e523535e730f313e1ed9b19b1a9dd228", 447894701),  // Humble Bundle
 	GAME_ENTRY_EN_STEAM("theadventuresoffatman", "fatman.exe", "6cddccb3744ec5c6af7c398fb7b3b11c", 44449205),
 	GAME_ENTRY_STEAM("theadventuresoftheblackhawk", "The Adventures of The Black Hawk.ags", "275e81b2ba532d12399b6329fa484ed4", 658932547), // En-Es



From noreply at scummvm.org  Tue Jun  9 12:18:39 2026
From: noreply at scummvm.org (sev-)
Date: Tue,  9 Jun 2026 12:18:39 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 ca6bf735759ef3739fbdae5ace43dffce4c193ba
Message-ID: <20260609121839.6F3227005BC@ron.scummvm.net>

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

Summary:
ca6bf73575 DIRECTOR: Prevent stack overflow introduced in the last commit


Commit: ca6bf735759ef3739fbdae5ace43dffce4c193ba
    https://github.com/scummvm/scummvm/commit/ca6bf735759ef3739fbdae5ace43dffce4c193ba
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2026-06-09T14:18:03+02:00

Commit Message:
DIRECTOR: Prevent stack overflow introduced in the last commit

Changed paths:
    engines/director/util.cpp
    engines/director/util.h


diff --git a/engines/director/util.cpp b/engines/director/util.cpp
index 0d42c361f1f..c65c3ee66c7 100644
--- a/engines/director/util.cpp
+++ b/engines/director/util.cpp
@@ -1203,8 +1203,9 @@ Common::Path dumpFactoryName(const char *prefix, const char *name, const char *e
 	return Common::Path(Common::String::format("./dumps/%s-factory-%s.%s", prefix, name, ext), '/');
 }
 
-void RandomState::setSeed(int seed) {
-	init(32);
+void RandomState::setSeed(int seed, bool runInit) {
+	if (runInit)
+		init(32);
 
 	// If we a GUI override, use that instead of the provided seed
 	if (ConfMan.hasKey("random_seed")) {
@@ -1246,7 +1247,7 @@ void RandomState::init(int len) {
 
 	// The original is _always_ initalized with a seed of 1. It is hardcoded and is by design
 	// The developers were offered to use `set the randomSeed` Lingo
-	setSeed(1);
+	setSeed(1, false);
 	_mask = masks[len - 2];
 }
 
diff --git a/engines/director/util.h b/engines/director/util.h
index 1bbf9e44f55..8d5e746c6e9 100644
--- a/engines/director/util.h
+++ b/engines/director/util.h
@@ -81,7 +81,7 @@ public:
 		_seed = _mask = _len = 0;
 	}
 
-	void setSeed(int seed);
+	void setSeed(int seed, bool runInit = true);
 	uint32 getSeed() { return _seed; }
 	int32 getRandom(int32 range);
 



From noreply at scummvm.org  Tue Jun  9 13:22:12 2026
From: noreply at scummvm.org (neuromancer)
Date: Tue,  9 Jun 2026 13:22:12 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 50674c1a69af97ec6a98cb5296640baf66ca9153
Message-ID: <20260609132212.CDA3C7005BC@ron.scummvm.net>

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

Summary:
7dc5e27590 SCUMM: RA2: reset music after each mission
34e0d8d555 SCUMM: RA2: initial code to support high resolution mode, disabled by default
b05208d428 SCUMM: RA2: perspective correction in high resolution mode for L2
f00a3eb8de SCUMM: RA2: gameplay corrected for in high resolution mode for L3
9644e84561 SCUMM: RA2: implement damage recovery
74401bdb4d SCUMM: RA2: some features for yoda mode
50674c1a69 SCUMM: RA1: no damage mode


Commit: 7dc5e27590620f4864e3ecb7a257d00aa9ea4b14
    https://github.com/scummvm/scummvm/commit/7dc5e27590620f4864e3ecb7a257d00aa9ea4b14
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-09T15:21:42+02:00

Commit Message:
SCUMM: RA2: reset music after each mission

Changed paths:
    engines/scumm/insane/rebel2/audio.cpp
    engines/scumm/insane/rebel2/levels.cpp
    engines/scumm/insane/rebel2/rebel.h
    engines/scumm/insane/rebel2/runlevels.cpp


diff --git a/engines/scumm/insane/rebel2/audio.cpp b/engines/scumm/insane/rebel2/audio.cpp
index cddd81a69c0..989d93074ef 100644
--- a/engines/scumm/insane/rebel2/audio.cpp
+++ b/engines/scumm/insane/rebel2/audio.cpp
@@ -48,6 +48,14 @@ void InsaneRebel2::terminateAudio() {
 	_audio.terminate();
 }
 
+void InsaneRebel2::resetVideoAudio() {
+	_audio.reset();
+
+	SmushPlayer *splayer = ((ScummEngine_v7 *)_vm)->_splayer;
+	if (splayer)
+		splayer->resetAudioTracks();
+}
+
 // queueAudioData -- Queue raw PCM data for playback on a track.
 // Creates the queuing stream on first use. RA2 audio is 8-bit unsigned mono.
 void InsaneRebel2::queueAudioData(int trackIdx, uint8 *data, int32 size, int volume, int pan) {
diff --git a/engines/scumm/insane/rebel2/levels.cpp b/engines/scumm/insane/rebel2/levels.cpp
index a5d34e8b9fa..a90f85136cb 100644
--- a/engines/scumm/insane/rebel2/levels.cpp
+++ b/engines/scumm/insane/rebel2/levels.cpp
@@ -186,6 +186,7 @@ void InsaneRebel2::playMissionBriefing() {
 // All wrapper functions (FUN_00417168/4171c5/417ab2/417327) add | 8 before calling FUN_0041f4d0.
 void InsaneRebel2::playCinematic(const char *filename) {
 	restoreDamageFlashPalette();
+	resetVideoAudio();
 	_gameplaySectionActive = false;
 	_rebelHandler = 0;
 	_rebelStatusBarSprite = 0;  // No status bar during cinematics
@@ -202,6 +203,7 @@ void InsaneRebel2::playVideoWithText(const char *filename, int textID, int textX
                                      int fadeInFrame, int fadeOutFrame) {
 
 	restoreDamageFlashPalette();
+	resetVideoAudio();
 	_gameplaySectionActive = false;
 	_rebelHandler = 0;
 	_rebelStatusBarSprite = 0;
@@ -274,6 +276,7 @@ void InsaneRebel2::playLevelBegin(int levelId) {
 void InsaneRebel2::playLevelEnd(int levelId) {
 
 	restoreDamageFlashPalette();
+	resetVideoAudio();
 	_gameplaySectionActive = false;
 	_rebelHandler = 0;
 	_rebelStatusBarSprite = 0;  // No status bar during end cinematic
@@ -294,6 +297,7 @@ void InsaneRebel2::playLevelEnd(int levelId) {
 void InsaneRebel2::playLevelRetry(int levelId) {
 
 	restoreDamageFlashPalette();
+	resetVideoAudio();
 	_gameplaySectionActive = false;
 	_rebelHandler = 0;
 	_rebelStatusBarSprite = 0;  // Reset for retry - will be set by IACT opcode 6 if needed
@@ -314,6 +318,7 @@ void InsaneRebel2::playLevelRetry(int levelId) {
 void InsaneRebel2::playLevelGameOver(int levelId) {
 
 	restoreDamageFlashPalette();
+	resetVideoAudio();
 	_gameplaySectionActive = false;
 	_rebelHandler = 0;
 	_rebelStatusBarSprite = 0;  // No status bar during game over cinematic
@@ -381,6 +386,7 @@ void InsaneRebel2::playEndingSequence() {
 void InsaneRebel2::playCreditsSequence() {
 
 	debug("Rebel2: Playing menu credits");
+	resetVideoAudio();
 
 	SmushPlayer *splayer = ((ScummEngine_v7 *)_vm)->_splayer;
 	splayer->setCurVideoFlags(0x20);
@@ -721,6 +727,7 @@ Common::String InsaneRebel2::selectDeathVideoVariant(int levelId, int phase, int
 void InsaneRebel2::playLevelDeathVariant(int levelId, int phase, int frame) {
 
 	restoreDamageFlashPalette();
+	resetVideoAudio();
 	_gameplaySectionActive = false;
 	_rebelHandler = 0;
 	_rebelStatusBarSprite = 0;  // No status bar during death cinematic
@@ -749,6 +756,7 @@ void InsaneRebel2::playLevelDeathVariant(int levelId, int phase, int frame) {
 void InsaneRebel2::playLevelRetryVariant(int levelId, int phase) {
 
 	restoreDamageFlashPalette();
+	resetVideoAudio();
 	_gameplaySectionActive = false;
 	_rebelHandler = 0;
 	_rebelStatusBarSprite = 0;  // Reset for retry - will be set by IACT opcode 6 if needed
diff --git a/engines/scumm/insane/rebel2/rebel.h b/engines/scumm/insane/rebel2/rebel.h
index 9df2a13101b..1ab92ca4a60 100644
--- a/engines/scumm/insane/rebel2/rebel.h
+++ b/engines/scumm/insane/rebel2/rebel.h
@@ -1312,6 +1312,9 @@ public:
 	// Terminate audio system
 	void terminateAudio();
 
+	// Reset streamed SAN audio at independent video boundaries.
+	void resetVideoAudio();
+
 	// Process audio dispatches - called from SmushPlayer when iMUSE is null
 	// This replaces the iMUSE audio path for RA2
 	void processAudioFrame(int16 feedSize);
diff --git a/engines/scumm/insane/rebel2/runlevels.cpp b/engines/scumm/insane/rebel2/runlevels.cpp
index ede78f398b2..2048677e5ea 100644
--- a/engines/scumm/insane/rebel2/runlevels.cpp
+++ b/engines/scumm/insane/rebel2/runlevels.cpp
@@ -199,6 +199,8 @@ bool InsaneRebel2::playLevelSegment(const char *filename, uint16 flags, bool rec
 		_gameplaySectionActive = true;
 		enableIOSGamepadController();
 	} else {
+		if (_gameplaySectionActive)
+			resetVideoAudio();
 		_gameplaySectionActive = false;
 		restoreIOSGamepadController();
 	}


Commit: 34e0d8d5556a5927b28a56c469f82905ec32056e
    https://github.com/scummvm/scummvm/commit/34e0d8d5556a5927b28a56c469f82905ec32056e
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-09T15:21:42+02:00

Commit Message:
SCUMM: RA2: initial code to support high resolution mode, disabled by default

Changed paths:
    engines/scumm/insane/rebel2/iact.cpp
    engines/scumm/insane/rebel2/levels.cpp
    engines/scumm/insane/rebel2/menu.cpp
    engines/scumm/insane/rebel2/rebel.cpp
    engines/scumm/insane/rebel2/rebel.h
    engines/scumm/insane/rebel2/render.cpp
    engines/scumm/metaengine.cpp
    engines/scumm/scumm.cpp
    engines/scumm/smush/rebel/smush_player_ra2.cpp
    engines/scumm/smush/rebel/smush_player_ra2.h
    engines/scumm/smush/smush_player.cpp
    engines/scumm/smush/smush_player.h


diff --git a/engines/scumm/insane/rebel2/iact.cpp b/engines/scumm/insane/rebel2/iact.cpp
index 4d484fdb487..6487dd5528b 100644
--- a/engines/scumm/insane/rebel2/iact.cpp
+++ b/engines/scumm/insane/rebel2/iact.cpp
@@ -827,8 +827,9 @@ void InsaneRebel2::handleOpcode6Handler7(Common::SeekableReadStream &b, int16 pa
 	// Step 1: Raw mouse input as offset from screen center.
 	// DAT_0047a7e0 = mouseX - 160, DAT_0047a7e2 = mouseY - 100.
 	// Handler 7 applies DAT_0047a7fe to its local vertical input after clamping.
-	const int16 mouseX = _vm->_mouse.x;
-	const int16 mouseY = _vm->_mouse.y;
+	const Common::Point aimPos = getGameplayAimPoint();
+	const int16 mouseX = aimPos.x;
+	const int16 mouseY = aimPos.y;
 	int16 inputX = (int16)(mouseX - 160);  // DAT_0047a7e0
 	int16 inputY = (int16)(mouseY - 100);  // DAT_0047a7e2
 
@@ -1555,8 +1556,8 @@ void InsaneRebel2::iactRebel2Opcode6(byte *renderBitmap, Common::SeekableReadStr
 // Handler-specific routing:
 //   Handler 7  (FLY):  FLY NUT sprites via par4 (1, 2, 3, 11)
 //   Handler 8  (POV):  POV NUT sprites via par3 (1, 3, 6, 7) or background via par4=5
-//   Handler 0x26 (turret): Turret HUD NUT via par3 (1-4)
-//   Handler 0x19: Mixed turret mode, similar to 0x26
+//   Handler 0x26 (turret): Turret HUD NUT via par3/par4 (1-4)
+//   Handler 0x19: Speeder bike GRD/HUD resources via par4
 //
 // ScummVM refactor helper for opcode 8 Handler 7 FLY loading, not a separate retail function.
 bool InsaneRebel2::loadOpcode8Handler7FlySprites(Common::SeekableReadStream &b, int64 startPos, int64 remaining, int16 par4) {
@@ -1738,11 +1739,14 @@ void InsaneRebel2::loadOpcode8EmbeddedAnim(byte *renderBitmap, Common::SeekableR
 bool InsaneRebel2::handleOpcode8EmbeddedAnim(byte *renderBitmap, byte *animData, int32 animDataSize, int16 par3, int16 par4) {
 	bool handled = false;
 
-	// Handler 0x26/0x19: Turret HUD Overlays.
-	// FUN_00407fcb case 8: par3 1-4 for HUD NUT loading.
-	if (!handled && (_rebelHandler == 0x26 || _rebelHandler == 0x19)) {
-		if (par3 >= 1 && par3 <= 4) {
-			handled = loadTurretHudOverlay(animData, animDataSize, par3);
+	// Handler 0x26: Turret HUD Overlays.
+	// FUN_00407fcb case 8: handler 0x26 uses par4 1-4 for HUD NUT loading.
+	// Some chunks use par3 for the same low/high selector.
+	if (!handled && _rebelHandler == 0x26) {
+		int hudSelector = (par4 >= 1 && par4 <= 4) ? par4 : par3;
+
+		if (hudSelector >= 1 && hudSelector <= 4) {
+			handled = loadTurretHudOverlay(animData, animDataSize, hudSelector);
 		}
 	}
 
@@ -1780,9 +1784,13 @@ bool InsaneRebel2::handleOpcode8EmbeddedAnim(byte *renderBitmap, byte *animData,
 	// Fallback: Embedded SAN HUD overlays.
 	// For other cases, load as embedded SAN frame to HUD overlay slots.
 	if (!handled) {
-		// Skip high-res data (par3 == 2, 4).
-		if (par3 == 2 || par3 == 4) {
-			debug("Rebel2 Opcode 8: Skipping high-res HUD par3=%d", par3);
+		const bool highRes = isHiRes();
+		const bool highResHud = (par3 == 2 || par3 == 4);
+		const bool lowResHud = (par3 == 1 || par3 == 3);
+
+		if ((!highRes && highResHud) || (highRes && lowResHud)) {
+			debug("Rebel2 Opcode 8: Skipping %s HUD par3=%d while running in %s mode",
+				highResHud ? "high-res" : "low-res", par3, highRes ? "high-res" : "low-res");
 			handled = true;
 		} else {
 			// Determine userId: Handler 0x19 uses par3, others use par4.
@@ -2126,45 +2134,48 @@ bool InsaneRebel2::loadHandler7FlySprites(Common::SeekableReadStream &b, int64 r
 	return assigned;
 }
 
-// loadTurretHudOverlay -- Handler 0x26/0x19 turret HUD loading (FUN_00407fcb case 8).
-bool InsaneRebel2::loadTurretHudOverlay(byte *animData, int32 size, int16 par3) {
-	// Handler 0x26/0x19 turret HUD overlay loading - FUN_00407fcb case 8
+// loadTurretHudOverlay -- Handler 0x26 turret HUD loading (FUN_00407fcb case 8).
+bool InsaneRebel2::loadTurretHudOverlay(byte *animData, int32 size, int16 selector) {
+	// Handler 0x26 turret HUD overlay loading - FUN_00407fcb case 8
 	// Resolution-dependent loading:
-	//   par3 == 1: Low-res primary HUD (DAT_0047fe78 / _hudOverlayNut)
-	//   par3 == 2: High-res primary HUD (skip in 320x200 mode)
-	//   par3 == 3: Low-res secondary HUD (DAT_0047fe80 / _hudOverlay2Nut)
-	//   par3 == 4: High-res secondary HUD (skip in 320x200 mode)
+	//   selector == 1: Low-res primary HUD (DAT_0047fe78 / _hudOverlayNut)
+	//   selector == 2: High-res primary HUD (DAT_0047fe78 / _hudOverlayNut)
+	//   selector == 3: Low-res secondary HUD (DAT_0047fe80 / _hudOverlay2Nut)
+	//   selector == 4: High-res secondary HUD (DAT_0047fe80 / _hudOverlay2Nut)
 
 	if (!animData || size <= 0) {
 		return false;
 	}
 
-	// ScummVM runs at 320x200 (low-res), skip high-res data
-	if (par3 == 2 || par3 == 4) {
-		debug("Rebel2 loadTurretHudOverlay: Skipping high-res HUD par3=%d (running in low-res mode)", par3);
-		return true;  // Successfully "handled" by skipping
+	const bool highRes = isHiRes();
+	const int primarySlot = highRes ? 2 : 1;
+	const int secondarySlot = highRes ? 4 : 3;
+
+	if (selector >= 1 && selector <= 4 && selector != primarySlot && selector != secondarySlot) {
+		debug("Rebel2 loadTurretHudOverlay: Skipping %s HUD selector=%d (running in %s mode)",
+			(selector == 2 || selector == 4) ? "high-res" : "low-res", selector,
+			highRes ? "high-res" : "low-res");
+		return true;
 	}
 
-	if (par3 != 1 && par3 != 3) {
+	if (selector != primarySlot && selector != secondarySlot) {
 		return false;  // Not a turret HUD slot
 	}
 
 	NutRenderer *newNut = makeRebel2SpriteFromData(_vm, animData, size);
 	if (!newNut || newNut->getNumChars() <= 0) {
-		debug("Rebel2 loadTurretHudOverlay: NUT load failed for par3=%d", par3);
+		debug("Rebel2 loadTurretHudOverlay: NUT load failed for selector=%d", selector);
 		delete newNut;
 		return false;
 	}
 
-	debug("Rebel2 loadTurretHudOverlay: Loaded turret HUD NUT par3=%d with %d sprites",
-		par3, newNut->getNumChars());
+	debug("Rebel2 loadTurretHudOverlay: Loaded turret HUD NUT selector=%d with %d sprites",
+		selector, newNut->getNumChars());
 
-	if (par3 == 1) {
-		// Low-res primary HUD overlay
+	if (selector == primarySlot) {
 		delete _hudOverlayNut;
 		_hudOverlayNut = newNut;
-	} else {  // par3 == 3
-		// Low-res secondary HUD overlay
+	} else {
 		delete _hudOverlay2Nut;
 		_hudOverlay2Nut = newNut;
 	}
diff --git a/engines/scumm/insane/rebel2/levels.cpp b/engines/scumm/insane/rebel2/levels.cpp
index a90f85136cb..b133a232415 100644
--- a/engines/scumm/insane/rebel2/levels.cpp
+++ b/engines/scumm/insane/rebel2/levels.cpp
@@ -416,10 +416,14 @@ void InsaneRebel2::warpGameplayMouseNow(int x, int y) {
 	if (eventMan)
 		eventMan->purgeMouseEvents();
 
-	_vm->_mouse.x = x;
-	_vm->_mouse.y = y;
-	_vm->_system->warpMouse(_vm->_macScreen ? x * 2 : x,
-		_vm->_macScreen ? y * 2 + 2 * _vm->_macScreenDrawOffset : y);
+	const int scale = isHiRes() ? 2 : 1;
+	const int physicalX = x * scale;
+	const int physicalY = y * scale;
+
+	_vm->_mouse.x = physicalX;
+	_vm->_mouse.y = physicalY;
+	_vm->_system->warpMouse(_vm->_macScreen ? physicalX * 2 : physicalX,
+		_vm->_macScreen ? physicalY * 2 + 2 * _vm->_macScreenDrawOffset : physicalY);
 
 	if (eventMan)
 		eventMan->purgeMouseEvents();
diff --git a/engines/scumm/insane/rebel2/menu.cpp b/engines/scumm/insane/rebel2/menu.cpp
index f82fe30354e..400928d1832 100644
--- a/engines/scumm/insane/rebel2/menu.cpp
+++ b/engines/scumm/insane/rebel2/menu.cpp
@@ -122,12 +122,11 @@ int InsaneRebel2::processMenuInput() {
 
 	int result = -1;
 
-	// Menu item Y positions (low-res 320x200 mode):
-	// From FUN_0041f5ae: baseY = numItems * -5 + 0x68
-	// With 7 selectable items: 7 * -5 + 104 = 69
-	// Items at Y = 69, 79, 89, 99, 109, 119, 129 with spacing of 10
-	const int baseY = _menuItemCount * -5 + 0x68;
-	const int itemSpacing = 10;
+	const bool highRes = isHiRes();
+	const int baseY = highRes ? (_menuItemCount * -5 + 0x5a) * 2 + 0x1c : _menuItemCount * -5 + 0x68;
+	const int itemSpacing = highRes ? 20 : 10;
+	const int itemHitTop = highRes ? 2 : 1;
+	const int itemHitHeight = highRes ? 18 : 10;
 
 	// Process events from the queue (populated by notifyEvent)
 	while (!_menuEventQueue.empty()) {
@@ -184,7 +183,7 @@ int InsaneRebel2::processMenuInput() {
 			_vm->_mouse.y = event.mouse.y;
 			for (int i = 0; i < _menuItemCount; i++) {
 				int itemY = baseY + i * itemSpacing;
-				if (event.mouse.y >= itemY - 1 && event.mouse.y < itemY + 9) {
+				if (event.mouse.y >= itemY - itemHitTop && event.mouse.y < itemY - itemHitTop + itemHitHeight) {
 					_menuSelection = i;
 					result = _menuSelection;
 					debug("Menu: Item %d selected (mouse)", _menuSelection);
@@ -198,7 +197,8 @@ int InsaneRebel2::processMenuInput() {
 				int mouseY = event.mouse.y;
 				for (int i = 0; i < _menuItemCount; i++) {
 					int itemY = baseY + i * itemSpacing;
-					if (mouseY >= itemY - 4 && mouseY < itemY + 6) {
+					if (mouseY >= itemY - itemHitTop - 3 * (highRes ? 2 : 1) &&
+							mouseY < itemY - itemHitTop + itemHitHeight - 3 * (highRes ? 2 : 1)) {
 						if (i != _menuSelection) {
 							_menuSelection = i;
 							debug(5, "Menu: Hover selection changed to %d (mouseY=%d)", _menuSelection, mouseY);
@@ -258,10 +258,13 @@ void InsaneRebel2::drawMenuItems(byte *renderBitmap, int pitch, int width, int h
 	//   Item Y:      param_3 * -5 + i * 10 + 0x68
 	//   Box Y:       param_3 * -5 + i * 10 + 0x67  (1px above text)
 
-	const int centerX = width / 2;
-	const int titleY = numItems * -5 + (leftAligned ? 0x56 : 0x51);
-	const int itemBaseY = numItems * -5 + 0x68;
-	const int itemSpacing = 10;
+	const bool highRes = isHiRes();
+	const int centerX = highRes ? 0x140 : width / 2;
+	const int titleY = highRes ?
+		(numItems * -5 + 0x5a) * 2 + (leftAligned ? -8 : -0x12) :
+		numItems * -5 + (leftAligned ? 0x56 : 0x51);
+	const int itemBaseY = highRes ? (numItems * -5 + 0x5a) * 2 + 0x1c : numItems * -5 + 0x68;
+	const int itemSpacing = highRes ? 20 : 10;
 
 	NutRenderer *fonts[3] = { _smush_talkfontNut, _smush_smalfontNut, _smush_titlefontNut };
 	NutRenderer *defaultFont = fonts[0] ? fonts[0] : _smush_smalfontNut;
@@ -286,7 +289,7 @@ void InsaneRebel2::drawMenuItems(byte *renderBitmap, int pitch, int width, int h
 	// -------------------------------------------------------------------
 	{
 		int titleWidth = getStringWidth(items[0]);
-		int titleX = leftAligned ? 40 : (centerX - titleWidth / 2);
+		int titleX = leftAligned ? (highRes ? 0x50 : 0x28) : (centerX - titleWidth / 2);
 		drawString(items[0], titleX, titleY);
 	}
 
@@ -300,25 +303,25 @@ void InsaneRebel2::drawMenuItems(byte *renderBitmap, int pitch, int width, int h
 		const char *text = items[i + 1];
 
 		int textWidth = getStringWidth(text);
-		int textX = leftAligned ? 23 : (centerX - textWidth / 2);
+		int textX = leftAligned ? (highRes ? 0x2e : 0x17) : (centerX - textWidth / 2);
 		drawString(text, textX, itemY);
 
 		// Selection highlight box - FUN_004292d0
 		if (i == selection) {
 			// Width: textWidth + ((DAT_0047a808 < 2) - 1 & 6) + 6 = textWidth + 6
-			int bracketWidth = textWidth + 6;
+			int bracketWidth = textWidth + (highRes ? 12 : 6);
 			// Height: ((DAT_0047a808 < 2) - 1 & 10) + 10 = 10
-			int bracketHeight = 10;
+			int bracketHeight = highRes ? 20 : 10;
 
 			// Flash color: (-((DAT_0047a7e4 & 1) == 0) & 8U) - 0x10
 			// bit0==0: 8-16=248(0xF8), bit0==1: 0-16=240(0xF0)
 			byte highlightColor = ((_vm->_system->getMillis() / 133) & 1) ? 248 : 240;
 
 			// Box position: Y = itemY - 1 (0x67 vs 0x68)
-			int leftX = leftAligned ? 20 : (centerX - bracketWidth / 2);
+			int leftX = leftAligned ? (highRes ? 0x28 : 0x14) : (centerX - bracketWidth / 2);
 			int rightX = leftX + bracketWidth;
-			int topY = itemY - 1;
-			int bottomY = itemY + bracketHeight - 1;
+			int topY = highRes ? itemY - 2 : itemY - 1;
+			int bottomY = topY + bracketHeight - 1;
 
 			int screenW = _vm->_screenWidth;
 			int screenH = _vm->_screenHeight;
@@ -929,10 +932,14 @@ int InsaneRebel2::processChapterSelectInput() {
 			_vm->_mouse.x = event.mouse.x;
 			_vm->_mouse.y = event.mouse.y;
 			{
-				int baseY = _chapterItemCount * -5 + 0x68;
+				const bool highRes = isHiRes();
+				const int baseY = highRes ? (_chapterItemCount * -5 + 0x5a) * 2 + 0x1c : _chapterItemCount * -5 + 0x68;
+				const int itemSpacing = highRes ? 20 : 10;
+				const int itemHitTop = highRes ? 2 : 1;
+				const int itemHitHeight = highRes ? 18 : 10;
 				for (int i = 0; i < _chapterItemCount; i++) {
-					int itemY = baseY + i * 10;
-					if (event.mouse.y >= itemY - 1 && event.mouse.y < itemY + 9) {
+					int itemY = baseY + i * itemSpacing;
+					if (event.mouse.y >= itemY - itemHitTop && event.mouse.y < itemY - itemHitTop + itemHitHeight) {
 						_chapterSelection = i;
 						_previewOffsetY = _chapterSelection * -50 + 75;
 						updateMenuVirtualKeyboard();
@@ -948,12 +955,17 @@ int InsaneRebel2::processChapterSelectInput() {
 			{
 				// Mouse hover changes highlight (original FUN_0041f5ae mouse mode).
 				// Item Y = numItems * -5 + i * 10 + 0x68
-				int baseY = _chapterItemCount * -5 + 0x68;
+				const bool highRes = isHiRes();
+				const int baseY = highRes ? (_chapterItemCount * -5 + 0x5a) * 2 + 0x1c : _chapterItemCount * -5 + 0x68;
+				const int itemSpacing = highRes ? 20 : 10;
+				const int itemHitTop = highRes ? 2 : 1;
+				const int itemHitHeight = highRes ? 18 : 10;
 				int mouseY = event.mouse.y;
 
 				for (int i = 0; i < _chapterItemCount; i++) {
-					int itemY = baseY + i * 10;
-					if (mouseY >= itemY - 4 && mouseY < itemY + 6) {
+					int itemY = baseY + i * itemSpacing;
+					if (mouseY >= itemY - itemHitTop - 3 * (highRes ? 2 : 1) &&
+							mouseY < itemY - itemHitTop + itemHitHeight - 3 * (highRes ? 2 : 1)) {
 						if (i != _chapterSelection) {
 							_chapterSelection = i;
 							_previewOffsetY = _chapterSelection * -50 + 75;
@@ -1442,8 +1454,11 @@ int InsaneRebel2::processLevelSelectInput() {
 	if (itemCount <= 0)
 		return -1;
 
-	const int itemBaseY = itemCount * -5 + 0x68;
-	const int itemSpacing = 10;
+	const bool highRes = isHiRes();
+	const int itemBaseY = highRes ? (itemCount * -5 + 0x5a) * 2 + 0x1c : itemCount * -5 + 0x68;
+	const int itemSpacing = highRes ? 20 : 10;
+	const int itemHitTop = highRes ? 2 : 1;
+	const int itemHitHeight = highRes ? 18 : 10;
 
 	while (!_menuEventQueue.empty()) {
 		Common::Event event = _menuEventQueue.pop();
@@ -1494,7 +1509,7 @@ int InsaneRebel2::processLevelSelectInput() {
 			_vm->_mouse.y = event.mouse.y;
 			for (int i = 0; i < itemCount; i++) {
 				int itemY = itemBaseY + i * itemSpacing;
-				if (event.mouse.y >= itemY - 1 && event.mouse.y < itemY + 9) {
+				if (event.mouse.y >= itemY - itemHitTop && event.mouse.y < itemY - itemHitTop + itemHitHeight) {
 					selection = i;
 					result = selection;
 					break;
@@ -1507,7 +1522,8 @@ int InsaneRebel2::processLevelSelectInput() {
 			_vm->_mouse.y = event.mouse.y;
 			for (int i = 0; i < itemCount; i++) {
 				int itemY = itemBaseY + i * itemSpacing;
-				if (event.mouse.y >= itemY - 4 && event.mouse.y < itemY + 6) {
+				if (event.mouse.y >= itemY - itemHitTop - 3 * (highRes ? 2 : 1) &&
+						event.mouse.y < itemY - itemHitTop + itemHitHeight - 3 * (highRes ? 2 : 1)) {
 					selection = i;
 					break;
 				}
@@ -1759,52 +1775,54 @@ void InsaneRebel2::drawTopPilotsOverlay(byte *renderBitmap, int pitch, int width
 	if (!splayer)
 		return;
 
+	const int scale = isHiRes() ? 2 : 1;
+
 	// Title centered at X=152, Y=10 (TITLFONT)
-	drawMenuStringCentered(renderBitmap, "^f02Top Pilots", 152, 10);
+	drawMenuStringCentered(renderBitmap, "^f02Top Pilots", 152 * scale, 10 * scale);
 
 	// Column headers at Y=30 (SMALFONT), positioned to match data columns
-	int headerY = 30;
+	int headerY = 30 * scale;
 	int headerColor = 5;
-	drawMenuStringCentered(renderBitmap, "^f01Rank", 43, headerY, headerColor);
-	drawMenuString(renderBitmap, "^f01Name", 88, headerY, headerColor);
-	drawMenuStringCentered(renderBitmap, "^f01Difficulty", 195, headerY, headerColor);
-	drawMenuStringCentered(renderBitmap, "^f01Chapter", 245, headerY, headerColor);
-	drawMenuStringRight(renderBitmap, "^f01Score", 295, headerY, headerColor);
+	drawMenuStringCentered(renderBitmap, "^f01Rank", 43 * scale, headerY, headerColor);
+	drawMenuString(renderBitmap, "^f01Name", 88 * scale, headerY, headerColor);
+	drawMenuStringCentered(renderBitmap, "^f01Difficulty", 195 * scale, headerY, headerColor);
+	drawMenuStringCentered(renderBitmap, "^f01Chapter", 245 * scale, headerY, headerColor);
+	drawMenuStringRight(renderBitmap, "^f01Score", 295 * scale, headerY, headerColor);
 
 	// Animated reveal: show up to _topPilotsFrameCount entries
 	int showCount = MIN(_topPilotsFrameCount, _numRankings);
 
 	for (int row = 0; row < showCount; row++) {
 		const RankingEntry &r = _rankings[row];
-		int rowY = row * 10 + 42;
+		int rowY = (row * 10 + 42) * scale;
 		int color = 244;  // 0xF4
 
 		// Column 1: Rank medals at X=43, centered (font 0 = TALKFONT)
 		Common::String rankStr = getRankString(r.rating);
 		if (!rankStr.empty()) {
 			Common::String rankFmt = Common::String::format("^f00%s", rankStr.c_str());
-			drawMenuStringCentered(renderBitmap, rankFmt.c_str(), 43, rowY, color);
+			drawMenuStringCentered(renderBitmap, rankFmt.c_str(), 43 * scale, rowY, color);
 		}
 
 		// Column 2: Pilot name at X=88, left-aligned (font 1 = SMALFONT)
 		Common::String nameFmt = Common::String::format("^f01%s", r.name);
-		drawMenuString(renderBitmap, nameFmt.c_str(), 88, rowY, color);
+		drawMenuString(renderBitmap, nameFmt.c_str(), 88 * scale, rowY, color);
 
 		// Column 3: Difficulty at X=195, centered - TRS (difficulty + 155)
 		int trsIdx = CLIP((int)r.difficulty, 0, 5) + 155;
 		const char *diffStr = splayer->getString(trsIdx);
 		if (diffStr && diffStr[0]) {
 			Common::String diffFmt = Common::String::format("^f01%s", diffStr);
-			drawMenuStringCentered(renderBitmap, diffFmt.c_str(), 195, rowY, color);
+			drawMenuStringCentered(renderBitmap, diffFmt.c_str(), 195 * scale, rowY, color);
 		}
 
 		// Column 4: Highest chapter at X=245, centered
 		Common::String chFmt = Common::String::format("^f01%d", (int)r.chapter);
-		drawMenuStringCentered(renderBitmap, chFmt.c_str(), 245, rowY, color);
+		drawMenuStringCentered(renderBitmap, chFmt.c_str(), 245 * scale, rowY, color);
 
 		// Column 5: Total score at X=295, right-aligned
 		Common::String scoreFmt = Common::String::format("^f01%ld", (long)r.score);
-		drawMenuStringRight(renderBitmap, scoreFmt.c_str(), 295, rowY, color);
+		drawMenuStringRight(renderBitmap, scoreFmt.c_str(), 295 * scale, rowY, color);
 	}
 
 	_topPilotsFrameCount++;
@@ -1938,10 +1956,14 @@ int InsaneRebel2::processOptionsInput() {
 		if (event.type == Common::EVENT_LBUTTONDOWN) {
 			// Mouse click on items ??? match drawMenuItems Y positions
 			int mouseY = event.mouse.y;
-			int baseY = _optionsItemCount * -5 + 0x68;
+			const bool highRes = isHiRes();
+			const int baseY = highRes ? (_optionsItemCount * -5 + 0x5a) * 2 + 0x1c : _optionsItemCount * -5 + 0x68;
+			const int itemSpacing = highRes ? 20 : 10;
+			const int itemHitTop = highRes ? 2 : 1;
+			const int itemHitHeight = highRes ? 18 : 10;
 			for (int i = 0; i < _optionsItemCount; i++) {
-				int itemY = baseY + i * 10;
-				if (mouseY >= itemY - 1 && mouseY < itemY + 9) {
+				int itemY = baseY + i * itemSpacing;
+				if (mouseY >= itemY - itemHitTop && mouseY < itemY - itemHitTop + itemHitHeight) {
 					_optionsSelection = i;
 					// Simulate enter for this item
 					Common::Event enterEvent;
diff --git a/engines/scumm/insane/rebel2/rebel.cpp b/engines/scumm/insane/rebel2/rebel.cpp
index e356621903c..93c786740df 100644
--- a/engines/scumm/insane/rebel2/rebel.cpp
+++ b/engines/scumm/insane/rebel2/rebel.cpp
@@ -129,15 +129,14 @@ InsaneRebel2::InsaneRebel2(ScummEngine_v7 *scumm) {
 	_smush_bencutNut = nullptr;
 	_smush_bensgoggNut = nullptr;
 
-	// Rebel Assault 2 specific initialization can go here
+	const bool highRes = isHiRes();
 
-	// Rebel Assault 2: Load cockpit sprites NUT which contains crosshairs, explosions, status bar
-	// CPITIMAG.NUT = low-res (320x200), CPITIMHI.NUT = high-res (640x480)
-	// The current renderer runs at 320x200, so use the low-res assets.
-	_smush_iconsNut = new NutRenderer(_vm, "SYSTM/CPITIMAG.NUT");
+	// Rebel Assault 2: Load cockpit sprites NUT which contains crosshairs,
+	// explosions, reticles, and warning cues.
+	_smush_iconsNut = new NutRenderer(_vm, highRes ? "SYSTM/CPITIMHI.NUT" : "SYSTM/CPITIMAG.NUT");
 	_smush_icons2Nut = nullptr;  // Not used for Rebel2
 
-	// Initialize laser texture buffer (DAT_0047fee4) from sprite 5 of CPITIMAG.NUT
+	// Initialize laser texture buffer (DAT_0047fee4) from sprite 5 of CPITIMAG/CPITIMHI.NUT
 	// This is done by FUN_0040BAB0/FUN_0040BB87 in the original with sprite index 5
 	// Sprite 5 is 136x13 pixels - a wide, thin texture perfect for laser beams
 	_laserTexture.pixels = nullptr;
@@ -156,7 +155,7 @@ InsaneRebel2::InsaneRebel2(ScummEngine_v7 *scumm) {
 	//   >= 0: Edge highlights enabled, >= 1: high-detail (secondary NUTs, widescreen)
 	// Always use high detail in ScummVM.
 	_rebelDetailMode = 1;
-	_smush_cockpitNut = new NutRenderer(_vm, "SYSTM/DISPFONT.NUT");
+	_smush_cockpitNut = new NutRenderer(_vm, highRes ? "SYSTM/DIHIFONT.NUT" : "SYSTM/DISPFONT.NUT");
 
 	// Load DIHIFONT.NUT for in-video messages/subtitles (Opcode 9)
 	_rebelMsgFont = makeRebel2Font(_vm, "SYSTM/DIHIFONT.NUT");
@@ -167,10 +166,10 @@ InsaneRebel2::InsaneRebel2(ScummEngine_v7 *scumm) {
 	//   Font 1 (^f01): SMALFONT.NUT - Small font for format code switching
 	//   Font 2 (^f02): TITLFONT.NUT - Title font
 	//   Font 3 (^f03): POVFONT.NUT - POV font
-	_smush_talkfontNut = makeRebel2Font(_vm, "SYSTM/TALKFONT.NUT");
-	_smush_smalfontNut = makeRebel2Font(_vm, "SYSTM/SMALFONT.NUT");
-	_smush_titlefontNut = makeRebel2Font(_vm, "SYSTM/TITLFONT.NUT");
-	_smush_povfontNut = makeRebel2Font(_vm, "SYSTM/POVFONT.NUT");
+	_smush_talkfontNut = makeRebel2Font(_vm, highRes ? "SYSTM/TKHIFONT.NUT" : "SYSTM/TALKFONT.NUT");
+	_smush_smalfontNut = makeRebel2Font(_vm, highRes ? "SYSTM/SMHIFONT.NUT" : "SYSTM/SMALFONT.NUT");
+	_smush_titlefontNut = makeRebel2Font(_vm, highRes ? "SYSTM/TIHIFONT.NUT" : "SYSTM/TITLFONT.NUT");
+	_smush_povfontNut = makeRebel2Font(_vm, highRes ? "SYSTM/POHIFONT.NUT" : "SYSTM/POVFONT.NUT");
 
 	_pauseOverlayActive = false;
 	memset(_savedPausePalette, 0, sizeof(_savedPausePalette));
@@ -190,6 +189,8 @@ InsaneRebel2::InsaneRebel2(ScummEngine_v7 *scumm) {
 	_noDamage = false;
 	_viewX = 0;
 	_viewY = 0;
+	_hiResPresentationViewX = 0;
+	_hiResPresentationViewY = 0;
 
 	// Damage visual effect counters (FUN_420515/420562/420754/42073B)
 	_damageFlashCounter = 0;
@@ -618,6 +619,10 @@ InsaneRebel2::~InsaneRebel2() {
 	}
 }
 
+bool InsaneRebel2::isHiRes() const {
+	return _vm->_screenWidth >= 640 && _vm->_screenHeight >= 400;
+}
+
 void InsaneRebel2::openGameplayMainMenu(SmushPlayer *splayer) {
 	if (!splayer)
 		return;
@@ -676,25 +681,25 @@ bool InsaneRebel2::notifyEvent(const Common::Event &event) {
 		if (_gameplayMouseSettleUntil != 0) {
 			const uint32 now = _vm->_system->getMillis();
 			if (now < _gameplayMouseSettleUntil) {
+				const int mouseScale = isHiRes() ? 2 : 1;
 				const int jumpX = event.mouse.x - _vm->_mouse.x;
 				const int jumpY = event.mouse.y - _vm->_mouse.y;
 				const bool largeAbsoluteJump =
-					ABS(jumpX) >= kRA2Handler7MouseSettleJumpThreshold ||
-					ABS(jumpY) >= kRA2Handler7MouseSettleJumpThreshold;
+					ABS(jumpX) >= kRA2Handler7MouseSettleJumpThreshold * mouseScale ||
+					ABS(jumpY) >= kRA2Handler7MouseSettleJumpThreshold * mouseScale;
 				const bool smallRelativeMove =
-					ABS((int)event.relMouse.x) < kRA2Handler7MouseSettleRelativeThreshold &&
-					ABS((int)event.relMouse.y) < kRA2Handler7MouseSettleRelativeThreshold;
+					ABS((int)event.relMouse.x) < kRA2Handler7MouseSettleRelativeThreshold * mouseScale &&
+					ABS((int)event.relMouse.y) < kRA2Handler7MouseSettleRelativeThreshold * mouseScale;
 				const bool nearWindowEdge =
-					event.mouse.x <= kRA2Handler7MouseSettleEdgeMargin ||
-					event.mouse.x >= kRA2GameplayMouseMaxX - kRA2Handler7MouseSettleEdgeMargin ||
-					event.mouse.y <= kRA2Handler7MouseSettleEdgeMargin ||
-					event.mouse.y >= kRA2GameplayMouseMaxY - kRA2Handler7MouseSettleEdgeMargin;
+					event.mouse.x <= kRA2Handler7MouseSettleEdgeMargin * mouseScale ||
+					event.mouse.x >= kRA2GameplayMouseMaxX * mouseScale - kRA2Handler7MouseSettleEdgeMargin * mouseScale ||
+					event.mouse.y <= kRA2Handler7MouseSettleEdgeMargin * mouseScale ||
+					event.mouse.y >= kRA2GameplayMouseMaxY * mouseScale - kRA2Handler7MouseSettleEdgeMargin * mouseScale;
 
 				if (largeAbsoluteJump && smallRelativeMove && nearWindowEdge) {
-					const int recenterX = _vm->_mouse.x;
-					const int recenterY = _vm->_mouse.y;
+					const Common::Point recenter = getGameplayAimPoint();
 					_gameplayMouseSettleUntil = now + kRA2Handler7MouseSettleExtendMs;
-					warpGameplayMouseNow(recenterX, recenterY);
+					warpGameplayMouseNow(recenter.x, recenter.y);
 
 					debugC(DEBUG_INSANE, "Rebel2 H7 mouse settle: suppress pos=(%d,%d) rel=(%d,%d) current=(%d,%d) until=%u",
 						event.mouse.x, event.mouse.y, event.relMouse.x, event.relMouse.y,
@@ -1371,11 +1376,12 @@ void InsaneRebel2::renderScoreHUD(byte *renderBitmap, int pitch, int width, int
 	char scoreStr[16];
 	Common::sprintf_s(scoreStr, "%07d", _playerScore);
 
-	// Score position from FUN_0041c012 assembly (low-res mode):
-	//   X = ((DAT_0047a808 < 2) - 1 & 0x101) + 0x101 = 0x101 = 257
-	//   Y = ((DAT_0047a808 < 2) - 1 & 4) + 4 = 4 (within status bar)
-	int scoreX = 257 + _viewX;
-	int scoreY = statusBarY + 4 + _viewY;
+	// Score position from FUN_0041c012 assembly:
+	//   X = 0x101 low-res, 0x202 high-res
+	//   Y = 4 low-res, 8 high-res (within status bar)
+	const int statusScale = isHiRes() ? 2 : 1;
+	int scoreX = 257 * statusScale + _viewX;
+	int scoreY = statusBarY + 4 * statusScale + _viewY;
 
 	// Render each digit as a NUT sprite (direct pixel blit with color 0 transparency).
 	// This matches the original's FUN_00434cb0 ??? FUN_004341a0 text rendering which
@@ -1772,12 +1778,19 @@ Common::Point InsaneRebel2::getGameplayAimPoint() {
 	// Pure getter (queried many times per frame): the aim/reticle follows the virtual
 	// mouse position. Directional controls pan that position incrementally once per frame
 	// via updateGameplayAimFromGamepad(), rather than snapping the reticle to a screen edge.
+	int x = _vm->_mouse.x;
 	int y = _vm->_mouse.y;
+	if (isHiRes()) {
+		x /= 2;
+		y /= 2;
+	}
+	x = CLIP<int>(x, 0, 319);
+	y = CLIP<int>(y, 0, 199);
 	if (_optControlsFlipped) {
 		// Original DAT_0047a7fe reverses only the up/down gameplay axis.
 		y = CLIP<int>(200 - y, 0, 199);
 	}
-	return Common::Point(_vm->_mouse.x, y);
+	return Common::Point(x, y);
 }
 
 // Apply the user's configured analog deadzone so a resting stick reports no
@@ -1837,6 +1850,7 @@ void InsaneRebel2::updateGameplayAimFromGamepad() {
 		}
 
 		if (axisX || axisY || _gamepadAimActive) {
+			const Common::Point aimPos = getGameplayAimPoint();
 			const int centerX = 160;
 			const int centerY = 100;
 			const int absAxisX = ABS(axisX);
@@ -1850,8 +1864,8 @@ void InsaneRebel2::updateGameplayAimFromGamepad() {
 				centerY - curvedY * centerY / 127 :
 				centerY + curvedY * (199 - centerY) / 127;
 			const int maxStep = (axisX || axisY) ? 14 : 10;
-			const int distX = targetX - _vm->_mouse.x;
-			const int distY = targetY - _vm->_mouse.y;
+			const int distX = targetX - aimPos.x;
+			const int distY = targetY - aimPos.y;
 
 			if (distX || distY) {
 				deltaX = CLIP<int>(distX, -maxStep, maxStep);
@@ -1882,8 +1896,10 @@ void InsaneRebel2::updateGameplayAimFromGamepad() {
 	_gamepadAimActive = true;
 
 	// Integrate velocity into the reticle, clamped to the 320x200 play area.
-	_vm->_mouse.x = (int16)CLIP<int>(_vm->_mouse.x + deltaX, 0, 319);
-	_vm->_mouse.y = (int16)CLIP<int>(_vm->_mouse.y + deltaY, 0, 199);
+	Common::Point aimPos = getGameplayAimPoint();
+	const int scale = isHiRes() ? 2 : 1;
+	_vm->_mouse.x = (int16)(CLIP<int>(aimPos.x + deltaX, 0, 319) * scale);
+	_vm->_mouse.y = (int16)(CLIP<int>(aimPos.y + deltaY, 0, 199) * scale);
 }
 
 bool InsaneRebel2::isBitSet(int n) {
diff --git a/engines/scumm/insane/rebel2/rebel.h b/engines/scumm/insane/rebel2/rebel.h
index 1ab92ca4a60..f7a5490dbf8 100644
--- a/engines/scumm/insane/rebel2/rebel.h
+++ b/engines/scumm/insane/rebel2/rebel.h
@@ -538,6 +538,7 @@ public:
 
 	// Get current handler ID (8, 25, 38 etc.) for SMUSH player to query
 	int getHandler() const { return _rebelHandler; }
+	bool isHiRes() const;
 
 	void iactRebel2Scene1(byte *renderBitmap, int32 codecparam, int32 setupsan12,
 				  int32 setupsan13, Common::SeekableReadStream &b, int32 size, int32 flags,
@@ -591,7 +592,7 @@ public:
 	void updateGameplayDamageEffects(byte *renderBitmap, int pitch, int width, int height);
 	void checkGameplayPostRenderCollisions(byte *renderBitmap, int pitch, int width, int height, int32 curFrame);
 
-	// Draw NUT-based HUD overlays for Handler 0x26/0x19 turret modes
+	// Draw NUT-based HUD overlays for Handler 0x26 turret modes
 	void renderTurretHudOverlays(byte *renderBitmap, int pitch, int width, int height, int32 curFrame);
 
 	// Draw embedded SAN HUD overlays from IACT chunks
@@ -642,7 +643,7 @@ public:
 	bool loadHandler7ShotTable(Common::SeekableReadStream &b, int64 startPos, int64 remaining, int16 par4);
 
 	// Load turret HUD overlay NUT from ANIM data
-	bool loadTurretHudOverlay(byte *animData, int32 size, int16 par3);
+	bool loadTurretHudOverlay(byte *animData, int32 size, int16 selector);
 
 	// Load Handler 8 ship POV NUT sprites from ANIM data (par4 = sprite type: 1,3,6,7)
 	bool loadHandler8ShipSprites(byte *animData, int32 size, int16 par4);
@@ -888,6 +889,8 @@ public:
 
 	int _viewX;
 	int _viewY;
+	int _hiResPresentationViewX;
+	int _hiResPresentationViewY;
 
 	// ---------------------------------------------------------------------------
 	// Damage Visual Effect System
diff --git a/engines/scumm/insane/rebel2/render.cpp b/engines/scumm/insane/rebel2/render.cpp
index 7a84b815bff..50534d256e2 100644
--- a/engines/scumm/insane/rebel2/render.cpp
+++ b/engines/scumm/insane/rebel2/render.cpp
@@ -24,12 +24,14 @@
 #include "common/util.h"
 
 #include "graphics/cursorman.h"
+#include "graphics/managed_surface.h"
 
 #include "scumm/scumm_v7.h"
 
 #include "scumm/smush/smush_player.h"
 #include "scumm/smush/rebel/codec_ra2.h"
 #include "scumm/smush/rebel/font_rebel2.h"
+#include "scumm/smush/rebel/smush_player_ra2.h"
 
 #include "scumm/insane/rebel2/rebel.h"
 
@@ -184,12 +186,12 @@ void InsaneRebel2::renderEmbeddedFrame(byte *renderBitmap, const EmbeddedSanFram
 	// Render the decoded embedded frame to the video buffer
 	// Skip immediate draw for handlers that render HUD during post-processing:
 	// - Handler 7/8: Ship direction sprites selected based on direction
-	// - Handler 0x26/0x19: Cockpit HUD positioned based on mouse/crosshair
+	// - Handler 0x26: Cockpit HUD positioned based on mouse/crosshair
 	//
 	// Exception: Handler 25 (0x19) background overlays (par4/userId=4, 6, 7) should draw immediately.
 	// These complete the visual scene and are NOT positioned by mouse/crosshair.
 	bool skipImmediateDraw = (_rebelHandler == 7 || _rebelHandler == 8 ||
-	                          _rebelHandler == 0x26 || _rebelHandler == 0x19);
+	                          _rebelHandler == 0x26);
 
 	// Handler 25 overlays:
 	// - userId 4 (corridor overlay): draw immediately at the current view offset.
@@ -340,9 +342,9 @@ void InsaneRebel2::loadEmbeddedSan(int userId, byte *animData, int32 size, byte
 					debug("Rebel2: Embedded HUD frame: userId=%d, %dx%d at (%d,%d), codec=%d",
 						userId, width, height, left, top, codec);
 
-					// Skip high-resolution frames - ScummVM runs at 320x200
-					// If frame dimensions exceed low-res screen size, it's high-res data
-					if (width > 400 || height > 250) {
+					// High-resolution HUD frames are used when the RA2 high-res option
+					// selects a 640x400 virtual screen. Keep skipping them in low-res mode.
+					if (!isHiRes() && (width > 400 || height > 250)) {
 						debug("Rebel2: SKIPPING high-res embedded frame: userId=%d, %dx%d (exceeds 400x250)",
 							userId, width, height);
 						stream.seek(nextSubPos);
@@ -1469,6 +1471,14 @@ void InsaneRebel2::drawLaserBeam(byte *dst, int pitch, int width, int height,
 	int16 texH = _laserTexture.height;
 	byte *texPixels = _laserTexture.pixels;
 
+	const bool renderHiRes = isHiRes() && width >= 640 && height >= 400;
+	if (renderHiRes) {
+		gunX = (int16)((gunX - _hiResPresentationViewX) * 2);
+		gunY = (int16)((gunY - _hiResPresentationViewY) * 2);
+		targetX = (int16)((targetX - _hiResPresentationViewX) * 2);
+		targetY = (int16)((targetY - _hiResPresentationViewY) * 2);
+	}
+
 	// FUN_0040BBF6 line 23: sVar7 = (thickness * animFrame * 16) / maxFrames
 	if (maxFrames == 0)
 		maxFrames = 1;
@@ -1493,10 +1503,10 @@ void InsaneRebel2::drawLaserBeam(byte *dst, int pitch, int width, int height,
 
 	// Original callers pass a clip rect for the gameplay viewport (excluding status bar).
 	// This preserves texture phase at the viewport edge and avoids visibly "chopped" beams.
-	int clipLeft = CLIP<int>(_viewX, 0, width - 1);
-	int clipTop = CLIP<int>(_viewY, 0, height - 1);
-	int clipRight = CLIP<int>(_viewX + 319, 0, width - 1);
-	int clipBottom = CLIP<int>(_viewY + 179, 0, height - 1);
+	int clipLeft = renderHiRes ? 0 : CLIP<int>(_viewX, 0, width - 1);
+	int clipTop = renderHiRes ? 0 : CLIP<int>(_viewY, 0, height - 1);
+	int clipRight = renderHiRes ? MIN(width - 1, 639) : CLIP<int>(_viewX + 319, 0, width - 1);
+	int clipBottom = renderHiRes ? MIN(height - 1, 359) : CLIP<int>(_viewY + 179, 0, height - 1);
 	if (clipLeft > clipRight || clipTop > clipBottom)
 		return;
 	int edgeClipLeft = CLIP<int>(clipLeft + 1, 1, width - 2);
@@ -1695,8 +1705,9 @@ void InsaneRebel2::checkCollisionZones(byte *renderBitmap, int pitch, int width,
 	// Calculate aim position in centered coordinates.
 	// Handler 0x26 applies the mouse-mode vertical inversion before DAT_0047a7fe
 	// (FUN_407FCB lines 108-123), so this must not use getGameplayAimPoint().
-	const int rawX = _vm->_mouse.x - 160;
-	const int rawY = _vm->_mouse.y - 100;
+	const Common::Point aimPos = getGameplayAimPoint();
+	const int rawX = aimPos.x - 160;
+	const int rawY = aimPos.y - 100;
 	int16 aimX = (int16)(rawX * 52 / 160);
 	int16 aimY = (int16)((_optControlsFlipped ? -rawY : rawY) * 45 / 100);
 
@@ -2189,6 +2200,75 @@ void renderNutSpriteClipped(byte *dst, int pitch, int dstH,
 	}
 }
 
+static void renderNutSpriteScaledClipped(byte *dst, int pitch, int width, int height,
+		int clipLeft, int clipTop, int clipRight, int clipBottom,
+		int x, int y, NutRenderer *nut, int spriteIdx, bool mirror, int scale, bool transparent231) {
+	if (!nut || spriteIdx < 0 || spriteIdx >= nut->getNumChars() || !dst)
+		return;
+
+	if (scale < 1)
+		scale = 1;
+
+	const int dstW = MIN(width, pitch);
+	if (dstW <= 0 || height <= 0)
+		return;
+
+	if (clipLeft < 0)
+		clipLeft = 0;
+	if (clipTop < 0)
+		clipTop = 0;
+	if (clipRight > dstW)
+		clipRight = dstW;
+	if (clipBottom > height)
+		clipBottom = height;
+	if (clipLeft >= clipRight || clipTop >= clipBottom)
+		return;
+
+	int srcW = nut->getCharWidth(spriteIdx);
+	int srcH = nut->getCharHeight(spriteIdx);
+	const byte *src = nut->getCharData(spriteIdx);
+	if (!src || srcW <= 0 || srcH <= 0)
+		return;
+
+	const int scaledW = srcW * scale;
+	const int scaledH = srcH * scale;
+	int drawLeft = MAX(x, clipLeft);
+	int drawTop = MAX(y, clipTop);
+	int drawRight = MIN(x + scaledW, clipRight);
+	int drawBottom = MIN(y + scaledH, clipBottom);
+	if (drawLeft >= drawRight || drawTop >= drawBottom)
+		return;
+
+	if (!transparent231 && x >= clipLeft && y >= clipTop &&
+			x + scaledW <= clipRight && y + scaledH <= clipBottom) {
+		const Graphics::PixelFormat format = Graphics::PixelFormat::createFormatCLUT8();
+		Graphics::Surface srcSurface;
+		srcSurface.init(srcW, srcH, srcW, const_cast<byte *>(src), format);
+
+		Graphics::ManagedSurface dstSurface;
+		dstSurface.surfacePtr()->init(dstW, height, pitch, dst, format);
+		dstSurface.transBlitFrom(srcSurface, Common::Rect(0, 0, srcW, srcH),
+			Common::Rect(x, y, x + scaledW, y + scaledH), 0, mirror);
+		return;
+	}
+
+	for (int dy = drawTop; dy < drawBottom; dy++) {
+		int srcY = (dy - y) / scale;
+		const byte *srcRow = src + srcY * srcW;
+		byte *dstRow = dst + dy * pitch;
+
+		for (int dx = drawLeft; dx < drawRight; dx++) {
+			int srcX = (dx - x) / scale;
+			if (mirror)
+				srcX = srcW - 1 - srcX;
+
+			byte px = srcRow[srcX];
+			if (px != 0 && (!transparent231 || px != 231))
+				dstRow[dx] = px;
+		}
+	}
+}
+
 // renderNutSpriteMirrored -- NUT sprite with optional horizontal flip (FUN_004236e0).
 void InsaneRebel2::renderNutSpriteMirrored(byte *dst, int pitch, int width, int height, int x, int y, NutRenderer *nut, int spriteIdx, bool mirror) {
 	if (!nut || spriteIdx < 0 || spriteIdx >= nut->getNumChars())
@@ -2249,6 +2329,13 @@ void InsaneRebel2::renderNutSpriteMirrored(byte *dst, int pitch, int width, int
 
 // updatePostRenderScroll -- Set SmushPlayer scroll offsets for the current frame.
 void InsaneRebel2::updatePostRenderScroll(int width, int height) {
+	if (_rebelHandler == 0) {
+		_viewX = 0;
+		_viewY = 0;
+		_player->setScrollOffset(0, 0);
+		return;
+	}
+
 	if (_rebelHandler == 8) {
 		// Handler 8 follows FUN_00401234/FUN_00401CCF: the camera is applied
 		// through FUN_00424510 before FOBJ decoding, not by scrolling the final
@@ -2259,10 +2346,24 @@ void InsaneRebel2::updatePostRenderScroll(int width, int height) {
 		return;
 	}
 
-	// Rebel Assault 2 uses a buffer larger (424x260) than screen (320x200).
-	// Map mouse X (0-320) to Scroll X (0-104), and Y (0-200) to Scroll Y (0-60).
-	int maxScrollX = width - _vm->_screenWidth;
-	int maxScrollY = height - _vm->_screenHeight;
+	if (_rebelHandler == 25 && !isHiRes()) {
+		// Handler 25's low-res L2 corridor layers are authored for the 320x200
+		// viewport. The backing buffer may be larger to decode unclipped FOBJ
+		// data, but panning the final copy exposes unfilled columns/rows and
+		// breaks the corridor perspective.
+		_viewX = 0;
+		_viewY = 0;
+		_player->setScrollOffset(0, 0);
+		return;
+	}
+
+	// Rebel Assault 2 uses a native 320x200 viewport into buffers that may be
+	// larger (424x260). High-res mode still scrolls in those logical units; the
+	// selected viewport is promoted to 640x400 after native FOBJ decoding.
+	const int viewportWidth = isHiRes() ? 320 : _vm->_screenWidth;
+	const int viewportHeight = isHiRes() ? 200 : _vm->_screenHeight;
+	int maxScrollX = width - viewportWidth;
+	int maxScrollY = height - viewportHeight;
 
 	if (maxScrollX < 0)
 		maxScrollX = 0;
@@ -2271,8 +2372,8 @@ void InsaneRebel2::updatePostRenderScroll(int width, int height) {
 
 	// Simple linear mapping: Center of screen corresponds to center of buffer.
 	Common::Point aimPos = getGameplayAimPoint();
-	_viewX = (aimPos.x * maxScrollX) / _vm->_screenWidth;
-	_viewY = (aimPos.y * maxScrollY) / _vm->_screenHeight;
+	_viewX = (aimPos.x * maxScrollX) / viewportWidth;
+	_viewY = (aimPos.y * maxScrollY) / viewportHeight;
 
 	_player->setScrollOffset(_viewX, _viewY);
 }
@@ -2526,6 +2627,24 @@ void InsaneRebel2::renderGameplayPostFrame(byte *renderBitmap, int pitch, int wi
 	// Original: FUN_00403240 only runs handlers when DAT_0047a814 == 0.
 	processMouse();
 
+	_hiResPresentationViewX = 0;
+	_hiResPresentationViewY = 0;
+	if (isHiRes()) {
+		SmushPlayerRebel2 *ra2Player = static_cast<SmushPlayerRebel2 *>(_player);
+		int nativeViewX = _viewX;
+		int nativeViewY = _viewY;
+		if (ra2Player && ra2Player->ra2PromoteCurrentFrameToHiRes(_viewX, _viewY)) {
+			renderBitmap = _player->_dst;
+			width = _player->_width;
+			height = _player->_height;
+			pitch = width;
+			_hiResPresentationViewX = nativeViewX;
+			_hiResPresentationViewY = nativeViewY;
+			_viewX = 0;
+			_viewY = 0;
+		}
+	}
+
 	// NOTE: Level 2 handler 8's background is restored in procPreRendering before
 	// SMUSH decodes the frame's FOBJ sprites. Handler 25 draws its corridor overlay
 	// from IACT opcode 6 instead. Redrawing either here would overwrite enemies.
@@ -2585,7 +2704,7 @@ void InsaneRebel2::renderGameplayPostFrame(byte *renderBitmap, int pitch, int wi
 	renderHandler25ShipPre(renderBitmap, pitch, width, height);
 	renderHandler25Ship(renderBitmap, pitch, width, height);
 
-	// STEP 1A: Draw NUT-based HUD overlays for Handler 0x26/0x19 (FUN_004089ab lines 195-226).
+	// STEP 1A: Draw NUT-based HUD overlays for Handler 0x26 (FUN_004089ab lines 195-226).
 	// These are cockpit frame, crosshair, and reticle - drawn ON TOP of laser beams.
 	renderTurretHudOverlays(renderBitmap, pitch, width, height, curFrame);
 
@@ -2641,15 +2760,26 @@ void InsaneRebel2::procPostRendering(byte *renderBitmap, int32 codecparam, int32
 	updatePostRenderScroll(width, height);
 	updatePostRenderDeath();
 
-	// Use video content coordinates, NOT buffer coordinates
-	const int videoWidth = 320;    // Native video width
-	const int videoHeight = 200;   // Native video height
-	const int statusBarY = 180;    // 0xb4 - status bar starts at Y=180 in video coords
+	// Use video content coordinates, NOT oversized low-res gameplay-buffer coordinates.
+	const int hudScale = isHiRes() ? 2 : getRebel2IndicatorScale(width, height);
+	const int videoWidth = 320 * hudScale;
+	const int videoHeight = 200 * hudScale;
+	const int statusBarY = 180 * hudScale;    // 0xb4 low-res, 0x168 high-res
 
 	// Hide HUD/status bar during intro videos (marked by SmushPlayer video flag 0x20)
 	// The 0x20 flag indicates a non-interactive cutscene/intro sequence OR menu
 	bool introPlaying = ((_player->_curVideoFlags & 0x20) != 0);
 
+	if (isHiRes() && _rebelHandler == 0) {
+		SmushPlayerRebel2 *ra2Player = static_cast<SmushPlayerRebel2 *>(_player);
+		if (ra2Player && ra2Player->ra2PromoteCurrentFrameToHiRes(0, 0)) {
+			renderBitmap = _player->_dst;
+			width = _player->_width;
+			height = _player->_height;
+			pitch = width;
+		}
+	}
+
 	if (handlePostRenderMenuModes(renderBitmap, pitch, width, height, introPlaying))
 		return;
 
@@ -2873,7 +3003,8 @@ void InsaneRebel2::renderTextOverlay(byte *renderBitmap, int pitch, int width, i
 			lines.push_back(cur);
 	}
 
-	int drawY = _textOverlayY;
+	const int textScale = isHiRes() ? 2 : 1;
+	int drawY = _textOverlayY * textScale;
 	int visCount = 0;
 
 	for (uint lineIdx = 0; lineIdx < lines.size() && visCount < displayLen; lineIdx++) {
@@ -2902,7 +3033,7 @@ void InsaneRebel2::renderTextOverlay(byte *renderBitmap, int pitch, int width, i
 		}
 
 		// Draw line centered at textX
-		int drawX = _textOverlayX - lineWidth / 2;
+		int drawX = _textOverlayX * textScale - lineWidth / 2;
 		int lineCharsDrawn = 0;
 		{
 			const char *s = lineStr;
@@ -2954,9 +3085,9 @@ void InsaneRebel2::renderStatusBarBackground(byte *renderBitmap, int pitch, int
 	}
 }
 
-// renderTurretHudOverlays -- NUT-based HUD for Handler 0x26/0x19 (FUN_004089ab).
+// renderTurretHudOverlays -- NUT-based HUD for Handler 0x26 (FUN_004089ab).
 void InsaneRebel2::renderTurretHudOverlays(byte *renderBitmap, int pitch, int width, int height, int32 curFrame) {
-	// Draw NUT-based HUD overlays for Handler 0x26/0x19 (turret modes)
+	// Draw NUT-based HUD overlays for Handler 0x26 (turret modes)
 	// From FUN_004089ab disassembly (lines 195-226):
 	// - DAT_0047fe78 (_hudOverlayNut): Primary HUD overlay with 6 animation frames
 	// - Position formula (low-res):
@@ -2964,7 +3095,7 @@ void InsaneRebel2::renderTurretHudOverlays(byte *renderBitmap, int pitch, int wi
 	//   Y = 182 - (mouseOffsetY >> 4) - height - spriteOffsetY
 	// - Animation: spriteIndex = (frameCounter / 2) % 6
 
-	if ((_rebelHandler != 0x26 && _rebelHandler != 0x19) || !_hudOverlayNut || _hudOverlayNut->getNumChars() <= 0)
+	if (_rebelHandler != 0x26 || !_hudOverlayNut || _hudOverlayNut->getNumChars() <= 0)
 		return;
 
 	// Calculate mouse offset (clamped to -127..127)
@@ -2988,26 +3119,34 @@ void InsaneRebel2::renderTurretHudOverlays(byte *renderBitmap, int pitch, int wi
 		animFrame = (curFrame / 2) % animFrameCount;
 	}
 
-	// Get sprite dimensions
-	int spriteW = _hudOverlayNut->getCharWidth(animFrame);
-	int spriteH = _hudOverlayNut->getCharHeight(animFrame);
+	const int hudScale = isHiRes() ? 2 : getRebel2IndicatorScale(width, height);
 
-	// Position calculation from assembly (low-res mode)
-	int spriteOffsetX = 0;
-	int spriteOffsetY = 0;
-	int hudX = 160 + (mouseOffsetX >> 4) - (spriteW / 2) - spriteOffsetX;
-	int hudY = 182 - (mouseOffsetY >> 4) - spriteH - spriteOffsetY;
+	// FUN_004089ab computes the moving overlay anchor from sprite 0's dimensions
+	// and offsets, then FUN_004236e0 applies each rendered frame's own offsets.
+	const int baseSpriteW = _hudOverlayNut->getCharWidth(0);
+	const int baseSpriteH = _hudOverlayNut->getCharHeight(0);
+	const int baseSpriteXOff = _hudOverlayNut->getCharXOffset(0);
+	const int baseSpriteYOff = _hudOverlayNut->getCharYOffset(0);
+	const int horizontalTerm = (mouseOffsetX * hudScale) >> 4;
+	const int verticalInput = isHiRes() ? mouseOffsetY * 2 - 0x100 : mouseOffsetY - 0x80;
+	const int verticalTerm = verticalInput >> 4;
+	int hudX = 160 * hudScale + horizontalTerm - baseSpriteW / 2 - baseSpriteXOff;
+	int hudY = 182 * hudScale - verticalTerm - baseSpriteH - baseSpriteYOff;
 
 	// Apply view offset for scrolling background
 	hudX += _viewX;
 	hudY += _viewY;
 
 	// Draw base cockpit (sprite 0 always drawn first)
-	renderNutSprite(renderBitmap, pitch, width, height, hudX, hudY, _hudOverlayNut, 0);
+	renderNutSprite(renderBitmap, pitch, width, height,
+		hudX + baseSpriteXOff, hudY + baseSpriteYOff, _hudOverlayNut, 0);
 
 	// Draw animation overlay frame if not frame 0
 	if (animFrame != 0 && animFrame < numSprites) {
-		renderNutSprite(renderBitmap, pitch, width, height, hudX, hudY, _hudOverlayNut, animFrame);
+		renderNutSprite(renderBitmap, pitch, width, height,
+			hudX + _hudOverlayNut->getCharXOffset(animFrame),
+			hudY + _hudOverlayNut->getCharYOffset(animFrame),
+			_hudOverlayNut, animFrame);
 	}
 
 	debug(5, "Rebel2 HUD: Drawing NUT overlay frame %d/%d at (%d,%d) mouseOffset=(%d,%d)",
@@ -3017,8 +3156,8 @@ void InsaneRebel2::renderTurretHudOverlays(byte *renderBitmap, int pitch, int wi
 	if (_hudOverlay2Nut && _hudOverlay2Nut->getNumChars() > 0) {
 		int spr2W = _hudOverlay2Nut->getCharWidth(0);
 		int spr2H = _hudOverlay2Nut->getCharHeight(0);
-		int hud2X = 160 + (mouseOffsetX >> 4) - (spr2W / 2) + _viewX;
-		int hud2Y = 182 - (mouseOffsetY >> 4) - spr2H + _viewY;
+		int hud2X = 160 * hudScale + ((mouseOffsetX * hudScale) >> 4) - (spr2W / 2) + _viewX;
+		int hud2Y = 182 * hudScale - ((mouseOffsetY * hudScale) >> 4) - spr2H + _viewY;
 		renderNutSprite(renderBitmap, pitch, width, height, hud2X, hud2Y, _hudOverlay2Nut, 0);
 	}
 }
@@ -3085,16 +3224,18 @@ void InsaneRebel2::renderEmbeddedHudOverlays(byte *renderBitmap, int pitch, int
 		int destX = frame.renderX;
 		int destY = frame.renderY;
 
-		// Handler 0x26/0x19 turret positioning
-		if ((_rebelHandler == 0x26 || _rebelHandler == 0x19) && (hudSlot == 1 || hudSlot == 2)) {
-			destX = 160 - frame.width / 2 - frame.renderX;
-			destY = 200 - frame.height - frame.renderY;
+		const int hudScale = isHiRes() ? 2 : getRebel2IndicatorScale(width, height);
+
+		// Handler 0x26 turret positioning
+		if (_rebelHandler == 0x26 && hudSlot >= 1 && hudSlot <= 4) {
+			destX = 160 * hudScale - frame.width / 2 - frame.renderX;
+			destY = 200 * hudScale - frame.height - frame.renderY;
 		}
 
 		// Handler 7 large cockpit frame positioning
 		if (_rebelHandler == 7 && (hudSlot == 1 || hudSlot == 2) && frame.width > 100) {
-			destX = 160 - frame.width / 2 - frame.renderX;
-			destY = 170 - frame.height - frame.renderY;
+			destX = 160 * hudScale - frame.width / 2 - frame.renderX;
+			destY = 170 * hudScale - frame.height - frame.renderY;
 		} else if (_rebelHandler == 7 && destX > 100 && destY > 50) {
 			int16 offsetX = (_shipPosX - 160) / 8;
 			int16 offsetY = (_shipPosY - 100) / 8;
@@ -3127,6 +3268,7 @@ void InsaneRebel2::renderStatusBarSprites(byte *renderBitmap, int pitch, int wid
 		return;
 
 	int numSprites = _smush_cockpitNut->getNumChars();
+	const int statusScale = isHiRes() ? 2 : getRebel2IndicatorScale(width, height);
 
 	// --- Sprite 1: Status bar background (always drawn first as base layer) ---
 	if (numSprites > 1) {
@@ -3152,13 +3294,16 @@ void InsaneRebel2::renderStatusBarSprites(byte *renderBitmap, int pitch, int wid
 	//   If shield > 0xAA (170): alert blink with sprite 7
 	if (numSprites > 6) {
 		// Bar width from assembly: param_1 >> 2 (low-res)
-		int damageBarWidth = _playerDamage >> 2;
+		int damageBarWidth = (_playerDamage * statusScale) >> 2;
 
 		const byte *src = _smush_cockpitNut->getCharData(6);
 		int sw = _smush_cockpitNut->getCharWidth(6);
 		int sh = _smush_cockpitNut->getCharHeight(6);
 
-		const int dmgClipX = 63, dmgClipY = 9, dmgClipW = 64, dmgClipH = 6;
+		const int dmgClipX = 63 * statusScale;
+		const int dmgClipY = 9 * statusScale;
+		const int dmgClipW = 64 * statusScale;
+		const int dmgClipH = 6 * statusScale;
 
 		if (src && sw > 0 && sh > 0 && damageBarWidth > 0) {
 			int drawW = MIN(damageBarWidth, MIN(dmgClipW, sw - dmgClipX));
@@ -3223,12 +3368,16 @@ void InsaneRebel2::renderStatusBarSprites(byte *renderBitmap, int pitch, int wid
 		int livesBarWidth = (_playerLives * 5 - 5) * 2;
 		if (livesBarWidth > 50)
 			livesBarWidth = 50;
+		livesBarWidth *= statusScale;
 
 		const byte *src = _smush_cockpitNut->getCharData(6);
 		int sw = _smush_cockpitNut->getCharWidth(6);
 		int sh = _smush_cockpitNut->getCharHeight(6);
 
-		const int livClipX = 168, livClipY = 7, livClipW = 50, livClipH = 9;
+		const int livClipX = 168 * statusScale;
+		const int livClipY = 7 * statusScale;
+		const int livClipW = 50 * statusScale;
+		const int livClipH = 9 * statusScale;
 
 		if (src && sw > 0 && sh > 0 && livesBarWidth > 0) {
 			int drawW = MIN(livesBarWidth, MIN(livClipW, sw - livClipX));
@@ -3412,6 +3561,8 @@ void InsaneRebel2::renderVehicleShotImpacts(byte *renderBitmap, int pitch, int w
 	if (_rebelHandler != 8)
 		return;
 
+	const bool renderHiRes = isHiRes() && width >= 640 && height >= 400;
+
 	for (int i = 0; i < 7; i++) {
 		VehicleShotImpact &impact = _vehicleShotImpacts[i];
 		if (impact.counter <= 0)
@@ -3419,6 +3570,10 @@ void InsaneRebel2::renderVehicleShotImpacts(byte *renderBitmap, int pitch, int w
 
 		int drawX = impact.x - _shipPosX;
 		int drawY = impact.y - _shipPosY;
+		if (renderHiRes) {
+			drawX = (drawX - _hiResPresentationViewX) * 2;
+			drawY = (drawY - _hiResPresentationViewY) * 2;
+		}
 
 		// Original draws DAT_0047e020 repeatedly based on remaining life, then
 		// DAT_0047e018 once, both using the sampled background-mask sprite index.
@@ -3456,9 +3611,15 @@ void InsaneRebel2::renderHandler25ShipPre(byte *renderBitmap, int pitch, int wid
 		return;
 
 	// CRITICAL: Clip height to 180 (0xb4) + viewport Y to avoid drawing over status bar.
-	// For oversized buffers (e.g., Level 12's 424x260), the status bar is at
-	// Y = 180 + _viewY in buffer coordinates.
-	int renderHeight = MIN(height, 180 + _viewY);
+	// In high-res presentation the low-res GRD sprite is scaled into the promoted
+	// 640x400 frame, so the gameplay clip becomes 360 pixels tall.
+	const bool renderHiRes = isHiRes() && width >= 640 && height >= 400;
+	const int renderScale = renderHiRes ? 2 : 1;
+	const int nativeViewX = renderHiRes ? _hiResPresentationViewX : 0;
+	const int nativeViewY = renderHiRes ? _hiResPresentationViewY : 0;
+	const int nativeBufferViewX = renderHiRes ? nativeViewX : _viewX;
+	const int nativeBufferViewY = renderHiRes ? nativeViewY : _viewY;
+	int renderHeight = renderHiRes ? MIN(height, 180 * renderScale) : MIN(height, 180 + _viewY);
 
 	// Draw _grd001Sprite based on _grdSpriteMode (DAT_00457900)
 	// Each mode has specific conditions from FUN_0041db5e:
@@ -3493,32 +3654,36 @@ void InsaneRebel2::renderHandler25ShipPre(byte *renderBitmap, int pitch, int wid
 		int16 spriteYOffset = _grd001Sprite->getCharYOffset(0);
 
 		// Add viewport offset so sprite follows the visible area.
-		// For 320x200 buffers (Level 2), _viewX/_viewY are 0 ??? no change.
-		// For oversized buffers (Level 12's 424x260), the viewport scrolls
-		// and sprites must be drawn at the correct position within it.
-		int drawX = _rebelViewOffset2X + spriteXOffset + _viewX;
-		int drawY = _rebelViewOffset2Y + spriteYOffset + _viewY;
+		// Handler 25 stays viewport-locked in low-res mode, so _viewX/_viewY
+		// remain 0 even when the backing buffer is larger than 320x200.
+		// Other oversized-buffer modes scroll and need this compensation.
+		int nativeDrawX = _rebelViewOffset2X + spriteXOffset + nativeBufferViewX;
+		int nativeDrawY = _rebelViewOffset2Y + spriteYOffset + nativeBufferViewY;
+		int drawX = renderHiRes ? (nativeDrawX - nativeViewX) * renderScale : nativeDrawX;
+		int drawY = renderHiRes ? (nativeDrawY - nativeViewY) * renderScale : nativeDrawY;
 
 		// Apply half-width clipping from FUN_41DB5E:
 		// - mode1 uncovered: left half
 		// - mode4 uncovered: right half
 		int clipLeft = 0;
-		int clipRight = width;
+		int clipRight = renderHiRes ? 320 : width;
 		if (useHalfWidth) {
-			const int halfWidth = width / 2;
+			const int halfWidth = clipRight / 2;
 			clipLeft = useRightHalf ? halfWidth : 0;
 			clipRight = clipLeft + halfWidth;
 		}
+		int scaledClipLeft = clipLeft * renderScale;
+		int scaledClipRight = clipRight * renderScale;
 
 		// FUN_41DB5E mode-4 uncovered mutates DAT_00482230/34 (clip region),
 		// not DAT_00457910 (draw X). Keep drawX unchanged and clip only.
-		renderNutSpriteClipped(renderBitmap, pitch, renderHeight,
-			clipLeft, 0, clipRight, renderHeight,
-			drawX, drawY, _grd001Sprite, 0);
+		renderNutSpriteScaledClipped(renderBitmap, pitch, width, renderHeight,
+			scaledClipLeft, 0, scaledClipRight, renderHeight,
+			drawX, drawY, _grd001Sprite, 0, false, renderScale, false);
 
-		debug("Rebel2 Handler25 PRE: GRD001 at (%d,%d) nutOff(%d,%d) viewOff(%d,%d) size(%d,%d) mode=%d dmg=%d halfW=%d rightHalf=%d clip=[%d,%d)",
+		debug("Rebel2 Handler25 PRE: GRD001 at (%d,%d) nutOff(%d,%d) viewOff(%d,%d) size(%d,%d) mode=%d dmg=%d halfW=%d rightHalf=%d clip=[%d,%d) scale=%d",
 			drawX, drawY, spriteXOffset, spriteYOffset, _rebelViewOffset2X, _rebelViewOffset2Y,
-			spriteW, spriteH, _grdSpriteMode, _rebelDamageLevel, useHalfWidth ? 1 : 0, useRightHalf ? 1 : 0, clipLeft, clipRight);
+			spriteW, spriteH, _grdSpriteMode, _rebelDamageLevel, useHalfWidth ? 1 : 0, useRightHalf ? 1 : 0, scaledClipLeft, scaledClipRight, renderScale);
 	}
 }
 
@@ -3534,7 +3699,13 @@ void InsaneRebel2::renderHandler25Ship(byte *renderBitmap, int pitch, int width,
 		return;
 
 	// CRITICAL: Clip height to 180 (0xb4) + viewport Y to avoid drawing over status bar.
-	int renderHeight = MIN(height, 180 + _viewY);
+	const bool renderHiRes = isHiRes() && width >= 640 && height >= 400;
+	const int renderScale = renderHiRes ? 2 : 1;
+	const int nativeViewX = renderHiRes ? _hiResPresentationViewX : 0;
+	const int nativeViewY = renderHiRes ? _hiResPresentationViewY : 0;
+	const int nativeBufferViewX = renderHiRes ? nativeViewX : _viewX;
+	const int nativeBufferViewY = renderHiRes ? nativeViewY : _viewY;
+	int renderHeight = renderHiRes ? MIN(height, 180 * renderScale) : MIN(height, 180 + _viewY);
 
 	// _grd002Sprite (GRD002) is always drawn if it exists (from FUN_41DB5E line 230)
 	// The sprite index is calculated based on damage level and aiming position
@@ -3642,26 +3813,30 @@ void InsaneRebel2::renderHandler25Ship(byte *renderBitmap, int pitch, int width,
 		int16 spriteXOffset = _grd002Sprite->getCharXOffset(spriteIdx);
 		int16 spriteYOffset = _grd002Sprite->getCharYOffset(spriteIdx);
 
-		int drawX, drawY;
+		int nativeDrawX, nativeDrawY;
 
 		if (shouldMirror) {
 			// Mirrored position: X = DAT_00457910 + (320 - sprite_width - sprite_x_offset)
 			// From assembly lines 240-243
-			drawX = _rebelViewOffset2X + (320 - spriteW - spriteXOffset) + _viewX;
+			nativeDrawX = _rebelViewOffset2X + (320 - spriteW - spriteXOffset) + nativeBufferViewX;
 		} else {
 			// Normal position: X = DAT_00457910 + sprite_internal_x_offset
 			// From assembly line 238
-			drawX = _rebelViewOffset2X + spriteXOffset + _viewX;
+			nativeDrawX = _rebelViewOffset2X + spriteXOffset + nativeBufferViewX;
 		}
 
 		// Y = sprite_internal_y_offset + DAT_00457912
 		// From assembly line 246
-		drawY = spriteYOffset + _rebelViewOffset2Y + _viewY;
+		nativeDrawY = spriteYOffset + _rebelViewOffset2Y + nativeBufferViewY;
+		int drawX = renderHiRes ? (nativeDrawX - nativeViewX) * renderScale : nativeDrawX;
+		int drawY = renderHiRes ? (nativeDrawY - nativeViewY) * renderScale : nativeDrawY;
 
-		renderNutSpriteMirrored(renderBitmap, pitch, width, renderHeight, drawX, drawY, _grd002Sprite, spriteIdx, shouldMirror);
+		renderNutSpriteScaledClipped(renderBitmap, pitch, width, renderHeight,
+			0, 0, width, renderHeight,
+			drawX, drawY, _grd002Sprite, spriteIdx, shouldMirror, renderScale, true);
 
-		debug("Rebel2 Handler25: GRD002 at (%d,%d) nutOffset(%d,%d) viewOffset(%d,%d) size(%d,%d) spriteIdx=%d damage=%d dir=%d mirror=%d",
-			drawX, drawY, spriteXOffset, spriteYOffset, _rebelViewOffset2X, _rebelViewOffset2Y, spriteW, spriteH, spriteIdx, _rebelDamageLevel, _rebelFlightDir, shouldMirror ? 1 : 0);
+		debug("Rebel2 Handler25: GRD002 at (%d,%d) nutOffset(%d,%d) viewOffset(%d,%d) size(%d,%d) spriteIdx=%d damage=%d dir=%d mirror=%d scale=%d",
+			drawX, drawY, spriteXOffset, spriteYOffset, _rebelViewOffset2X, _rebelViewOffset2Y, spriteW, spriteH, spriteIdx, _rebelDamageLevel, _rebelFlightDir, shouldMirror ? 1 : 0, renderScale);
 	}
 }
 
@@ -3738,8 +3913,13 @@ void InsaneRebel2::renderEnemyOverlays(byte *renderBitmap, int pitch, int width,
 	// so indicators stay aligned with decoded enemy sprites.
 	int fobjOffX = _player ? _player->_fobjOffsetX : 0;
 	int fobjOffY = _player ? _player->_fobjOffsetY : 0;
+	const bool renderHiRes = isHiRes() && width >= 640 && height >= 400;
+	const int indicatorScale = renderHiRes ? 2 : getRebel2IndicatorScale(width, height);
+	const int nativeViewX = renderHiRes ? _hiResPresentationViewX : _viewX;
+	const int nativeViewY = renderHiRes ? _hiResPresentationViewY : _viewY;
+	const int nativeVideoWidth = renderHiRes ? videoWidth / indicatorScale : 320;
 
-	Common::Rect viewRect(_viewX, _viewY, _viewX + videoWidth, _viewY + 200);
+	Common::Rect viewRect(nativeViewX, nativeViewY, nativeViewX + nativeVideoWidth, nativeViewY + 200);
 	const int sizeClamp = dparams.specialDamage; // DAT_0047e0fa in FUN_40A2E0
 
 	for (Common::List<enemy>::iterator it = _enemies.begin(); it != _enemies.end(); ++it) {
@@ -3784,8 +3964,14 @@ void InsaneRebel2::renderEnemyOverlays(byte *renderBitmap, int pitch, int width,
 		int centerY = r.top + halfH;
 		int iw = _smush_iconsNut->getCharWidth(spriteIndex);
 		int ih = _smush_iconsNut->getCharHeight(spriteIndex);
+		int drawX = centerX * indicatorScale - iw / 2;
+		int drawY = centerY * indicatorScale - ih / 2;
+		if (renderHiRes) {
+			drawX = (centerX - nativeViewX) * indicatorScale - iw / 2;
+			drawY = (centerY - nativeViewY) * indicatorScale - ih / 2;
+		}
 		renderNutSprite(renderBitmap, pitch, width, height,
-			centerX - iw / 2, centerY - ih / 2, _smush_iconsNut, spriteIndex);
+			drawX, drawY, _smush_iconsNut, spriteIndex);
 	}
 }
 
@@ -3858,8 +4044,15 @@ void InsaneRebel2::renderExplosionFrame(byte *renderBitmap, int pitch, int width
 	if (_smush_iconsNut->getNumChars() > spriteIndex) {
 		int ew = _smush_iconsNut->getCharWidth(spriteIndex);
 		int eh = _smush_iconsNut->getCharHeight(spriteIndex);
+		const bool renderHiRes = isHiRes() && width >= 640 && height >= 400;
+		int drawX = screenX;
+		int drawY = screenY;
+		if (renderHiRes) {
+			drawX = (screenX - _hiResPresentationViewX) * 2;
+			drawY = (screenY - _hiResPresentationViewY) * 2;
+		}
 		renderNutSprite(renderBitmap, pitch, width, height,
-			screenX - ew / 2, screenY - eh / 2, _smush_iconsNut, spriteIndex);
+			drawX - ew / 2, drawY - eh / 2, _smush_iconsNut, spriteIndex);
 	}
 
 	if (advance == kExplosionAdvanceAfterDraw)
@@ -3938,11 +4131,14 @@ void InsaneRebel2::renderSpaceExplosions(byte *renderBitmap, int pitch, int widt
 
 		int numChars = _smush_iconsNut->getNumChars();
 		int spriteIndex = 0x15 - _hitCooldown;  // 21 - remaining cooldown
+		const bool renderHiRes = isHiRes() && width >= 640 && height >= 400;
+		const int nativeViewX = renderHiRes ? _hiResPresentationViewX : _viewX;
+		const int nativeViewY = renderHiRes ? _hiResPresentationViewY : _viewY;
 
 		if (spriteIndex >= 0 && spriteIndex < numChars) {
 			// Compute ship screen position (simplified FUN_0041c720 transform)
-			int shipDrawX = (_flyShipScreenX - 0xd4) + _perspectiveX + 160 + _viewX;
-			int shipDrawY = (_flyShipScreenY - 0x82) + _perspectiveY + 100 + _viewY;
+			int shipDrawX = (_flyShipScreenX - 0xd4) + _perspectiveX + 160 + nativeViewX;
+			int shipDrawY = (_flyShipScreenY - 0x82) + _perspectiveY + 100 + nativeViewY;
 
 			// Per-direction offset from ship center.
 			// Original uses lookup tables (DAT_004438da etc.) indexed by
@@ -3968,6 +4164,10 @@ void InsaneRebel2::renderSpaceExplosions(byte *renderBitmap, int pitch, int widt
 
 			int drawX = shipDrawX + offsetX;
 			int drawY = shipDrawY + offsetY;
+			if (renderHiRes) {
+				drawX = (drawX - nativeViewX) * 2;
+				drawY = (drawY - nativeViewY) * 2;
+			}
 
 			int ew = _smush_iconsNut->getCharWidth(spriteIndex);
 			int eh = _smush_iconsNut->getCharHeight(spriteIndex);
@@ -4025,6 +4225,9 @@ void InsaneRebel2::renderTurretLaserShots(byte *renderBitmap, int pitch, int wid
 	// Uses pre-initialized _laserTexture from sprite 5 of CPITIMAG.NUT
 
 	int16 maxDuration = getShotMaxDuration();
+	const bool renderHiRes = isHiRes() && width >= 640 && height >= 400;
+	const int nativeViewX = renderHiRes ? _hiResPresentationViewX : _viewX;
+	const int nativeViewY = renderHiRes ? _hiResPresentationViewY : _viewY;
 
 	for (int i = 0; i < 2; i++) {
 		if (_turretShots[i].counter <= 0)
@@ -4032,7 +4235,7 @@ void InsaneRebel2::renderTurretLaserShots(byte *renderBitmap, int pitch, int wid
 
 		// Calculate sound panning from target X position (FUN_004262f0 call)
 		// sVar1 = ((2 - counter) * (targetX - 160)) / 2, clamped to [-127, 127]
-		int16 pan = ((2 - _turretShots[i].counter) * (_turretShots[i].targetX - _viewX - 160)) / 2;
+		int16 pan = ((2 - _turretShots[i].counter) * (_turretShots[i].targetX - nativeViewX - 160)) / 2;
 		pan = CLIP<int16>(pan, -127, 127);
 		// TODO: Apply panning to sound channel i+1
 
@@ -4049,15 +4252,15 @@ void InsaneRebel2::renderTurretLaserShots(byte *renderBitmap, int pitch, int wid
 			// Gun 2: (0xa0, 0x17c) = (160, 380) - center bottom (off-screen, clipped)
 			// Gun 3: (0x0a, 0xaa) = (10, 170) - left
 			drawLaserBeam(renderBitmap, pitch, width, height,
-				310 + _viewX, 170 + _viewY, targetX, targetY,
+				310 + nativeViewX, 170 + nativeViewY, targetX, targetY,
 				progress, maxDuration, 12, 8, 12);
 
 			drawLaserBeam(renderBitmap, pitch, width, height,
-				160 + _viewX, 380 + _viewY, targetX, targetY,
+				160 + nativeViewX, 380 + nativeViewY, targetX, targetY,
 				progress, maxDuration, 8, 5, 12);
 
 			drawLaserBeam(renderBitmap, pitch, width, height,
-				10 + _viewX, 170 + _viewY, targetX, targetY,
+				10 + nativeViewX, 170 + nativeViewY, targetX, targetY,
 				progress, maxDuration, 12, 8, 12);
 			break;
 
@@ -4068,11 +4271,11 @@ void InsaneRebel2::renderTurretLaserShots(byte *renderBitmap, int pitch, int wid
 			// Right: (0xd2, 0xe6) = (210, 230)
 			// Assembly: widthScale=0x19(25), heightScale=8, thickness=0xC(12)
 			drawLaserBeam(renderBitmap, pitch, width, height,
-				110 + _viewX, 230 + _viewY, targetX, targetY,
+				110 + nativeViewX, 230 + nativeViewY, targetX, targetY,
 				progress, maxDuration, 25, 8, 12);
 
 			drawLaserBeam(renderBitmap, pitch, width, height,
-				210 + _viewX, 230 + _viewY, targetX, targetY,
+				210 + nativeViewX, 230 + nativeViewY, targetX, targetY,
 				progress, maxDuration, 25, 8, 12);
 			break;
 
@@ -4082,11 +4285,11 @@ void InsaneRebel2::renderTurretLaserShots(byte *renderBitmap, int pitch, int wid
 			// Gun 2: (0, 0)
 			// Assembly: widthScale=0x19(25), heightScale=8, thickness=0xC(12)
 			drawLaserBeam(renderBitmap, pitch, width, height,
-				-100 + _viewX, 0 + _viewY, targetX, targetY,
+				-100 + nativeViewX, 0 + nativeViewY, targetX, targetY,
 				progress, maxDuration, 25, 8, 12);
 
 			drawLaserBeam(renderBitmap, pitch, width, height,
-				0 + _viewX, 0 + _viewY, targetX, targetY,
+				0 + nativeViewX, 0 + nativeViewY, targetX, targetY,
 				progress, maxDuration, 25, 8, 12);
 			break;
 
@@ -4097,19 +4300,19 @@ void InsaneRebel2::renderTurretLaserShots(byte *renderBitmap, int pitch, int wid
 			// Assembly: widthScale=0x19(25), heightScale=8, thickness=0xC(12)
 			if ((_turretShots[i].seqNum & 1) == 0) {
 				drawLaserBeam(renderBitmap, pitch, width, height,
-					10 + _viewX, 50 + _viewY, targetX, targetY,
+					10 + nativeViewX, 50 + nativeViewY, targetX, targetY,
 					progress, maxDuration, 25, 8, 12);
 
 				drawLaserBeam(renderBitmap, pitch, width, height,
-					310 + _viewX, 130 + _viewY, targetX, targetY,
+					310 + nativeViewX, 130 + nativeViewY, targetX, targetY,
 					progress, maxDuration, 25, 8, 12);
 			} else {
 				drawLaserBeam(renderBitmap, pitch, width, height,
-					310 + _viewX, 50 + _viewY, targetX, targetY,
+					310 + nativeViewX, 50 + nativeViewY, targetX, targetY,
 					progress, maxDuration, 25, 8, 12);
 
 				drawLaserBeam(renderBitmap, pitch, width, height,
-					10 + _viewX, 130 + _viewY, targetX, targetY,
+					10 + nativeViewX, 130 + nativeViewY, targetX, targetY,
 					progress, maxDuration, 25, 8, 12);
 			}
 			break;
@@ -4448,8 +4651,9 @@ void InsaneRebel2::renderCrosshair(byte *renderBitmap, int pitch, int width, int
 		int ch = _smush_iconsNut->getCharHeight(reticleIndex);
 
 		// Calculate crosshair position
-		int crosshairX = aimPos.x;
-		int crosshairY = aimPos.y;
+		const int reticleScale = isHiRes() ? 2 : getRebel2IndicatorScale(width, height);
+		int crosshairX = aimPos.x * reticleScale;
+		int crosshairY = aimPos.y * reticleScale;
 		if (_rebelHandler != 7) {
 			crosshairX += _viewX;
 			crosshairY += _viewY;
@@ -4458,8 +4662,8 @@ void InsaneRebel2::renderCrosshair(byte *renderBitmap, int pitch, int width, int
 		// Handler 25 (0x19): Add view offset to crosshair position
 		// From FUN_41DB5E lines 198-199: X = DAT_00457914 + DAT_0045790c, Y = DAT_00457916 + DAT_0045790e
 		if (_rebelHandler == 25) {
-			crosshairX += _rebelViewOffsetX;
-			crosshairY += _rebelViewOffsetY;
+			crosshairX += _rebelViewOffsetX * reticleScale;
+			crosshairY += _rebelViewOffsetY * reticleScale;
 		}
 
 		// FUN_004236e0 with flags=2 applies the NUT frame offsets, then centers.
diff --git a/engines/scumm/metaengine.cpp b/engines/scumm/metaengine.cpp
index 8f899433929..0f75ecbec27 100644
--- a/engines/scumm/metaengine.cpp
+++ b/engines/scumm/metaengine.cpp
@@ -882,7 +882,7 @@ static const ExtraGuiOption enableRebel2HiRes = {
 	_s("High resolution mode"),
 	_s("Run the game in 640x400 high resolution mode instead of 320x200"),
 	"rebel2_hires",
-	true,
+	false,
 	0,
 	0
 };
diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp
index 7d19565b732..ba18b97a5bf 100644
--- a/engines/scumm/scumm.cpp
+++ b/engines/scumm/scumm.cpp
@@ -401,6 +401,9 @@ ScummEngine::ScummEngine(OSystem *syst, const DetectorResult &dr)
 		// #15666, #11290, and <https://forums.scummvm.org/viewtopic.php?p=97395#p97395>).
 		if (_game.id == GID_LOOM || !ConfMan.getBool("trim_fmtowns_to_200_pixels"))
 			_screenHeight = 240;
+	} else if (_game.id == GID_REBEL2 && ConfMan.getBool("rebel2_hires")) {
+		_screenWidth = 640;
+		_screenHeight = 400;
 	} else if (_game.version == 8 || _game.heversion >= 71) {
 		// COMI uses 640x480. Likewise starting from version 7.1, HE games use
 		// 640x480, too.
@@ -1826,7 +1829,7 @@ void ScummEngine_v7::setupScumm(const Common::Path &macResourceFile) {
 
 	if (_game.id == GID_REBEL2) {
 		_res->allocResTypeData(rtBuffer, 0, 10, kDynamicResTypeMode);
-		initScreens(0, 200);
+		initScreens(0, _screenHeight);
 
 		_numVariables = 256;
 		_scummVars = (int32 *)calloc(_numVariables, sizeof(int32));
diff --git a/engines/scumm/smush/rebel/smush_player_ra2.cpp b/engines/scumm/smush/rebel/smush_player_ra2.cpp
index 222b4914574..a4484f551c6 100644
--- a/engines/scumm/smush/rebel/smush_player_ra2.cpp
+++ b/engines/scumm/smush/rebel/smush_player_ra2.cpp
@@ -29,6 +29,8 @@
 #include "common/rect.h"
 #include "common/system.h"
 
+#include "graphics/blit.h"
+
 #include "scumm/scumm.h"
 #include "scumm/scumm_v7.h"
 #include "scumm/smush/smush_font.h"
@@ -58,6 +60,30 @@ bool isRebel2GameplayActive(Insane *insane) {
 	return static_cast<InsaneRebel2 *>(insane)->getHandler() != 0;
 }
 
+static void scaleNativeViewportToHiRes(byte *dst, int dstPitch, int dstWidth, int dstHeight,
+		const byte *src, int srcPitch, int srcWidth, int srcHeight, int scrollX, int scrollY) {
+	if (!dst || !src || dstPitch <= 0 || dstWidth < 640 || dstHeight < 400 ||
+			srcPitch <= 0 || srcWidth <= 0 || srcHeight <= 0)
+		return;
+
+	memset(dst, 0, (size_t)dstPitch * dstHeight);
+
+	scrollX = CLIP<int>(scrollX, 0, MAX<int>(0, srcWidth - 1));
+	scrollY = CLIP<int>(scrollY, 0, MAX<int>(0, srcHeight - 1));
+
+	const int outWidth = MIN<int>(320, dstWidth / 2);
+	const int outHeight = MIN<int>(200, dstHeight / 2);
+	const int srcViewWidth = MIN<int>(outWidth, srcWidth - scrollX);
+	const int srcViewHeight = MIN<int>(outHeight, srcHeight - scrollY);
+	if (srcViewWidth <= 0 || srcViewHeight <= 0)
+		return;
+
+	const byte *srcView = src + scrollY * srcPitch + scrollX;
+	Graphics::scaleBlit(dst, srcView, dstPitch, srcPitch,
+		srcViewWidth * 2, srcViewHeight * 2, srcViewWidth, srcViewHeight,
+		Graphics::PixelFormat::createFormatCLUT8());
+}
+
 void smushDecodeRA2Uncompressed(byte *dst, const byte *src, int left, int top,
 		int width, int height, int dstPitch, int srcPitch, int dataSize,
 		int srcSkipX, int srcSkipY) {
@@ -141,6 +167,10 @@ void SmushPlayerRebel2::initGamePlayerFields() {
 	_ra2FrameObjectOriginalWidth = 0;
 	_ra2FrameObjectSurfaceWidth = 0;
 	_ra2FrameObjectSurfaceHeight = 0;
+	_ra2LowResVideoBuffer = nullptr;
+	_ra2LowResVideoBufferSize = 0;
+	_ra2NativeFrameNeedsClear = false;
+	_ra2UsingGameplaySurface = false;
 	_ra2PendingAnimHeaderPalette = false;
 	memset(_ra2Codec45Palette, 0, sizeof(_ra2Codec45Palette));
 	memset(_ra2Codec45Lookup, 0, sizeof(_ra2Codec45Lookup));
@@ -157,6 +187,9 @@ void SmushPlayerRebel2::destroyGamePlayerFields() {
 	_lastFobjData = nullptr;
 	free(_loadBuffer);
 	_loadBuffer = nullptr;
+	free(_ra2LowResVideoBuffer);
+	_ra2LowResVideoBuffer = nullptr;
+	_ra2LowResVideoBufferSize = 0;
 }
 
 void SmushPlayerRebel2::ra2InitAudioTrackSizes() {
@@ -183,6 +216,7 @@ void SmushPlayerRebel2::ra2InitAudioTrackSizes() {
  */
 void SmushPlayerRebel2::initGameVideoState() {
 	_ra2PendingAnimHeaderPalette = false;
+	_ra2UsingGameplaySurface = false;
 
 	// Re-push the SMUSH palette to the system. Videos like O_LEVEL.SAN
 	// have no NPAL chunk and inherit the palette from the previous video.
@@ -215,11 +249,81 @@ void SmushPlayerRebel2::releaseGameVideoState() {
 	_lastFobjData = nullptr;
 	_lastFobjDataSize = 0;
 	_hasFrameFobjForGost = false;
+	_ra2NativeFrameNeedsClear = false;
 	// FUN_00423880 allocates the STOR overlay buffer once for the SMUSH subsystem,
 	// and FUN_004246d0 replays the last stored FOBJ even after a new SAN starts.
 	// Level 12 wave segments depend on this when 12P01_B.SAN starts with FTCH.
 }
 
+bool SmushPlayerRebel2::ra2IsHighResMode() const {
+	return _vm->_screenWidth >= 640 && _vm->_screenHeight >= 400;
+}
+
+bool SmushPlayerRebel2::ra2EnsureLowResVideoBuffer() {
+	const int bufSize = 320 * 200;
+	if (_ra2LowResVideoBuffer != nullptr && _ra2LowResVideoBufferSize >= bufSize)
+		return true;
+
+	byte *newBuffer = (byte *)realloc(_ra2LowResVideoBuffer, bufSize);
+	if (!newBuffer) {
+		warning("SmushPlayerRebel2::ra2EnsureLowResVideoBuffer: Failed to allocate %d-byte decode buffer", bufSize);
+		free(_ra2LowResVideoBuffer);
+		_ra2LowResVideoBuffer = nullptr;
+		_ra2LowResVideoBufferSize = 0;
+		return false;
+	}
+
+	_ra2LowResVideoBuffer = newBuffer;
+	_ra2LowResVideoBufferSize = bufSize;
+	memset(_ra2LowResVideoBuffer, 0, _ra2LowResVideoBufferSize);
+	return true;
+}
+
+void SmushPlayerRebel2::ra2ClearCurrentTarget() {
+	if (!_dst)
+		return;
+
+	int clearSize = 0;
+	if (_dst == _specialBuffer && _width > 0 && _height > 0) {
+		const int64 size64 = (int64)_width * _height;
+		if (size64 <= INT_MAX && size64 <= _specialBufferSize)
+			clearSize = (int)size64;
+	} else if (_dst == _ra2LowResVideoBuffer && _width > 0 && _height > 0) {
+		const int64 size64 = (int64)_width * _height;
+		if (size64 <= INT_MAX && size64 <= _ra2LowResVideoBufferSize)
+			clearSize = (int)size64;
+	} else {
+		clearSize = _vm->_screenWidth * _vm->_screenHeight;
+	}
+
+	if (clearSize > 0)
+		memset(_dst, 0, clearSize);
+}
+
+bool SmushPlayerRebel2::ra2PromoteCurrentFrameToHiRes(int scrollX, int scrollY) {
+	if (!ra2IsHighResMode() || !_dst || _width <= 0 || _height <= 0)
+		return false;
+
+	VirtScreen *vs = &_vm->_virtscr[kMainVirtScreen];
+	byte *screen = vs->getPixels(0, 0);
+	if (_dst == screen && _width == _vm->_screenWidth && _height == _vm->_screenHeight)
+		return false;
+
+	const byte *src = _dst;
+	const int srcPitch = _width;
+	const int srcWidth = _width;
+	const int srcHeight = _height;
+
+	scaleNativeViewportToHiRes(screen, _vm->_screenWidth, _vm->_screenWidth, _vm->_screenHeight,
+		src, srcPitch, srcWidth, srcHeight, scrollX, scrollY);
+
+	_dst = screen;
+	_width = _vm->_screenWidth;
+	_height = _vm->_screenHeight;
+	setScrollOffset(0, 0);
+	return true;
+}
+
 /**
  * RA2-specific FTCH handling.
  * For Handler 25, skips FTCH to preserve overlays.
@@ -355,24 +459,31 @@ bool SmushPlayerRebel2::handleGameTextRendering(const char *str, int fontId, int
 }
 
 SmushFont *SmushPlayerRebel2::getGameFont(int font) {
-	// Font table for low-res mode (320x200). Original exe uses pointer
-	// arithmetic to select hi/lo font pairs (e.g. TKHIFONT+0x14=TALKFONT).
+	// Original exe uses pointer arithmetic to select hi/lo font pairs.
 	// Font 0: TALKFONT (TKHIFONT hi-res)
 	// Font 1: SMALFONT (SMHIFONT hi-res)
 	// Font 2: TITLFONT (TIHIFONT hi-res)
 	// Font 3: POVFONT  (POHIFONT hi-res)
-	const char *ra2_fonts[] = {
+	const char *ra2FontsLo[] = {
 		"SYSTM/TALKFONT.NUT",
 		"SYSTM/SMALFONT.NUT",
 		"SYSTM/TITLFONT.NUT",
 		"SYSTM/POVFONT.NUT"
 	};
-	int numFonts = ARRAYSIZE(ra2_fonts);
+	const char *ra2FontsHi[] = {
+		"SYSTM/TKHIFONT.NUT",
+		"SYSTM/SMHIFONT.NUT",
+		"SYSTM/TIHIFONT.NUT",
+		"SYSTM/POHIFONT.NUT"
+	};
+	const bool highRes = _vm->_screenWidth >= 640 && _vm->_screenHeight >= 400;
+	const char **ra2Fonts = highRes ? ra2FontsHi : ra2FontsLo;
+	int numFonts = ARRAYSIZE(ra2FontsLo);
 	if (font >= 0 && font < numFonts) {
-		_sf[font] = new SmushFont(_vm, ra2_fonts[font], true);
+		_sf[font] = new SmushFont(_vm, ra2Fonts[font], true);
 	} else {
 		debugC(DEBUG_SMUSH, "SmushPlayerRebel2::getGameFont: RA2 unknown font %d, using TALKFONT", font);
-		_sf[font] = new SmushFont(_vm, ra2_fonts[0], true);
+		_sf[font] = new SmushFont(_vm, ra2Fonts[0], true);
 	}
 	return _sf[font];
 }
@@ -716,6 +827,13 @@ void SmushPlayerRebel2::ra2HandleTextResource(const char *str, int fontId, int c
 										int width, int height, TextStyleFlags flg) {
 	ensureMultiFont();
 	_multiFont->setDefaultFont(fontId);
+	const int scale = (_vm->_screenWidth >= 640 && _vm->_screenHeight >= 400) ? 2 : 1;
+	pos_x *= scale;
+	pos_y *= scale;
+	left *= scale;
+	top *= scale;
+	width *= scale;
+	height *= scale;
 
 	debugC(DEBUG_SMUSH, "SmushPlayerRebel2::ra2HandleTextResource: RA2 TRES frame=%d fontId=%d color=%d flags=0x%x pos=(%d,%d) clip=(%d,%d,%d,%d) str=\"%.40s\"",
 		  _frame, fontId, color, (int)flg, pos_x, pos_y, left, top, width, height, str);
@@ -748,8 +866,25 @@ void SmushPlayerRebel2::ra2PrepareFrameObjectSurface(int left, int top, int widt
 
 bool SmushPlayerRebel2::ra2SelectFrameBuffer(int codec, int width, int height) {
 	if (codec == SMUSH_CODEC_RA2_BOMP) {
-		if (_specialBuffer != nullptr) {
+		const bool highRes = ra2IsHighResMode();
+		const bool gameplayActive = isRebel2GameplayActive(_insane);
+		if (highRes && (!gameplayActive || !_ra2UsingGameplaySurface || _specialBuffer == nullptr)) {
+			if (!ra2EnsureLowResVideoBuffer())
+				return false;
+			_dst = _ra2LowResVideoBuffer;
+			_width = 320;
+			_height = 200;
+			if (_ra2NativeFrameNeedsClear) {
+				ra2ClearCurrentTarget();
+				_ra2NativeFrameNeedsClear = false;
+			}
+			debugC(DEBUG_SMUSH, "SmushPlayerRebel2::ra2SelectFrameBuffer: Using low-res decode buffer for high-res codec 45 mask");
+		} else if (_specialBuffer != nullptr) {
 			_dst = _specialBuffer;
+			if (_ra2NativeFrameNeedsClear) {
+				ra2ClearCurrentTarget();
+				_ra2NativeFrameNeedsClear = false;
+			}
 			debugC(DEBUG_SMUSH, "SmushPlayerRebel2::ra2SelectFrameBuffer: Using _specialBuffer for codec 45 mask");
 		} else {
 			VirtScreen *vs = &_vm->_virtscr[kMainVirtScreen];
@@ -760,26 +895,75 @@ bool SmushPlayerRebel2::ra2SelectFrameBuffer(int codec, int width, int height) {
 	}
 
 	// Rebel2 allocates the low-res gameplay target as 424x260 (FUN_00424730).
-	// Use that target once an oversized gameplay FOBJ appears, then keep small
-	// overlay FOBJ chunks compositing into it. Pure 320x200 gameplay videos keep
-	// drawing their small FOBJ chunks directly onto the main screen.
+	// High-res presentation still decodes video into native 320x200/424x260
+	// surfaces, then promotes the selected viewport to the 640x400 screen.
 	const int screenSize = _vm->_screenWidth * _vm->_screenHeight;
+	const int nativeScreenSize = 320 * 200;
 	const int64 fobjSize64 = (int64)width * height;
 	int surfaceWidth = width;
 	int surfaceHeight = height;
 
-	const bool useGameplaySurface = isRebel2GameplayActive(_insane) &&
-		!isRebel2FullFrameDeltaCodec(codec) &&
-		(fobjSize64 > screenSize || _specialBuffer != nullptr);
+	const bool highRes = ra2IsHighResMode();
+	const bool gameplayActive = isRebel2GameplayActive(_insane);
+	const bool fullFrameDelta = isRebel2FullFrameDeltaCodec(codec);
+
+	if (!gameplayActive && fobjSize64 <= nativeScreenSize) {
+		if (highRes) {
+			if (!ra2EnsureLowResVideoBuffer())
+				return false;
+			_dst = _ra2LowResVideoBuffer;
+			_width = 320;
+			_height = 200;
+		} else {
+			VirtScreen *vs = &_vm->_virtscr[kMainVirtScreen];
+			_dst = vs->getPixels(0, 0);
+			_width = _vm->_screenWidth;
+			_height = _vm->_screenHeight;
+		}
+
+		if (_ra2NativeFrameNeedsClear) {
+			ra2ClearCurrentTarget();
+			_ra2NativeFrameNeedsClear = false;
+		}
+		debugC(DEBUG_SMUSH, "SmushPlayerRebel2::ra2SelectFrameBuffer: Using native screen target for menu/cinematic FOBJ %dx%d",
+			width, height);
+		return true;
+	}
+
+	const bool oversizedNative = fobjSize64 > nativeScreenSize ||
+		width > 320 || height > 200 ||
+		_ra2FrameObjectSurfaceWidth > 320 || _ra2FrameObjectSurfaceHeight > 200;
+	const bool useGameplaySurface = gameplayActive && !fullFrameDelta &&
+		(oversizedNative || _ra2UsingGameplaySurface);
 
 	if (useGameplaySurface) {
 		surfaceWidth = kRebel2GameplaySurfaceWidth;
 		surfaceHeight = kRebel2GameplaySurfaceHeight;
-	} else if (fobjSize64 > screenSize && !isRebel2FullFrameDeltaCodec(codec)) {
+		_ra2UsingGameplaySurface = true;
+	} else if (!highRes && fobjSize64 > screenSize && !fullFrameDelta) {
 		surfaceWidth = MAX(surfaceWidth, _ra2FrameObjectSurfaceWidth);
 		surfaceHeight = MAX(surfaceHeight, _ra2FrameObjectSurfaceHeight);
 	}
 
+	if (highRes && !useGameplaySurface && !oversizedNative) {
+		if (width <= 0 || height <= 0) {
+			debugC(DEBUG_SMUSH, "SmushPlayerRebel2::ra2SelectFrameBuffer: Skipping invalid FOBJ dimensions %dx%d", width, height);
+			return false;
+		}
+		if (!ra2EnsureLowResVideoBuffer())
+			return false;
+		_dst = _ra2LowResVideoBuffer;
+		_width = 320;
+		_height = 200;
+		if (_ra2NativeFrameNeedsClear) {
+			ra2ClearCurrentTarget();
+			_ra2NativeFrameNeedsClear = false;
+		}
+		debugC(DEBUG_SMUSH, "SmushPlayerRebel2::ra2SelectFrameBuffer: Using low-res decode buffer for high-res FOBJ %dx%d",
+			width, height);
+		return true;
+	}
+
 	const int64 bufSize64 = (int64)surfaceWidth * surfaceHeight;
 	if (width <= 0 || height <= 0 || surfaceWidth <= 0 || surfaceHeight <= 0 || bufSize64 > INT_MAX) {
 		debugC(DEBUG_SMUSH, "SmushPlayerRebel2::ra2SelectFrameBuffer: Skipping invalid FOBJ dimensions %dx%d", width, height);
@@ -787,8 +971,9 @@ bool SmushPlayerRebel2::ra2SelectFrameBuffer(int codec, int width, int height) {
 	}
 
 	const int bufSize = (int)bufSize64;
-	if (bufSize > screenSize) {
-		// Frame is larger than screen - need special buffer
+	const bool needsSpecialBuffer = useGameplaySurface || oversizedNative || bufSize > screenSize;
+	if (needsSpecialBuffer) {
+		// Frame is larger than the native target - need special buffer.
 		if (_specialBuffer == nullptr || bufSize > _specialBufferSize) {
 			byte *newSpecialBuffer = (byte *)malloc(bufSize);
 			if (newSpecialBuffer == nullptr) {
@@ -808,9 +993,13 @@ bool SmushPlayerRebel2::ra2SelectFrameBuffer(int codec, int width, int height) {
 		_height = surfaceHeight;
 	}
 
-	if (bufSize > screenSize &&
-	    _specialBuffer != nullptr && _specialBufferSize >= bufSize) {
+	if (needsSpecialBuffer &&
+			_specialBuffer != nullptr && _specialBufferSize >= bufSize) {
 		_dst = _specialBuffer;
+		if (_ra2NativeFrameNeedsClear) {
+			ra2ClearCurrentTarget();
+			_ra2NativeFrameNeedsClear = false;
+		}
 		debugC(DEBUG_SMUSH, "SmushPlayerRebel2::ra2SelectFrameBuffer: Using _specialBuffer %dx%d for oversized FOBJ %dx%d",
 			_width, _height, width, height);
 	} else {
@@ -962,6 +1151,10 @@ bool SmushPlayerRebel2::handleGameFrameBufferSelect(int codec, int width, int he
 
 bool SmushPlayerRebel2::handleGameDimensionOverride(int codec, int width, int height) {
 	if ((height != _vm->_screenHeight) || (width != _vm->_screenWidth)) {
+		if (_dst == _ra2LowResVideoBuffer && _ra2LowResVideoBuffer != nullptr) {
+			return true;
+		}
+
 		if (_insane != nullptr) {
 			InsaneRebel2 *rebel2 = static_cast<InsaneRebel2 *>(_insane);
 			if (rebel2->getHandler() != 0) {
@@ -983,6 +1176,13 @@ bool SmushPlayerRebel2::handleGameDimensionOverride(int codec, int width, int he
 	return false;
 }
 
+int SmushPlayerRebel2::handleGameFrameObjectPitch(int pitch) {
+	if (_dst == _ra2LowResVideoBuffer && _ra2LowResVideoBuffer != nullptr && _width > 0)
+		return _width;
+
+	return pitch;
+}
+
 bool SmushPlayerRebel2::handleGameAdjustCoords(int codec, int &left, int &top, int &width, int &height, int pitch, int *srcSkipY) {
 	int sourceSkipY = 0;
 	const int adjustedLeft = left + _fobjOffsetX;
@@ -997,7 +1197,27 @@ bool SmushPlayerRebel2::handleGameAdjustCoords(int codec, int &left, int &top, i
 		return true;
 	}
 
-	adjustFrameCoords(left, top, width, height, pitch, &sourceSkipY);
+	if (_dst == _ra2LowResVideoBuffer && _ra2LowResVideoBuffer != nullptr) {
+		left += _fobjOffsetX;
+		top += _fobjOffsetY;
+
+		if (top < 0) {
+			sourceSkipY = -top;
+			height += top;
+			top = 0;
+		}
+		if (left < 0) {
+			width += left;
+			left = 0;
+		}
+		if (top + height > _height)
+			height = _height - top;
+		if (left + width > pitch)
+			width = pitch - left;
+	} else {
+		adjustFrameCoords(left, top, width, height, pitch, &sourceSkipY);
+	}
+
 	if (codec == SMUSH_CODEC_LINE_UPDATE || codec == SMUSH_CODEC_LINE_UPDATE2 ||
 			codec == SMUSH_CODEC_SKIP_RLE || codec == SMUSH_CODEC_UNCOMPRESSED) {
 		_ra2FrameSourceSkipY = sourceSkipY;
@@ -1042,29 +1262,43 @@ void SmushPlayerRebel2::handleGameFrameObjectPost(int codec, const byte *data, i
 	}
 }
 
+void SmushPlayerRebel2::handleGameFrameObjectDecoded(int codec, int left, int top, int width, int height) {
+	if (!ra2IsHighResMode() || isRebel2GameplayActive(_insane))
+		return;
+
+	ra2PromoteCurrentFrameToHiRes(0, 0);
+}
+
 void SmushPlayerRebel2::handleGameFrameStart() {
 	_hasFrameFobjForGost = false;
+	_ra2NativeFrameNeedsClear = ((_curVideoFlags & 0x20) == 0);
 
 	if (_ra2PendingAnimHeaderPalette) {
 		setDirtyColors(0, 255);
 		_ra2PendingAnimHeaderPalette = false;
 	}
 
+	if (ra2IsHighResMode() && isRebel2GameplayActive(_insane)) {
+		if (_ra2UsingGameplaySurface && _specialBuffer != nullptr &&
+				_specialBufferSize >= kRebel2GameplaySurfaceWidth * kRebel2GameplaySurfaceHeight) {
+			_dst = _specialBuffer;
+			_width = kRebel2GameplaySurfaceWidth;
+			_height = kRebel2GameplaySurfaceHeight;
+		} else if (ra2EnsureLowResVideoBuffer()) {
+			_dst = _ra2LowResVideoBuffer;
+			_width = 320;
+			_height = 200;
+		}
+	}
+
 	// FUN_00424d70 clears the target buffer before decoding a frame
 	// unless playback flags contain 0x20. Codec 21 frames in levels like
 	// LEV05/05PLAY.SAN only contain non-zero literals, so stale skipped pixels
 	// must not survive from the previous frame.
 	if ((_curVideoFlags & 0x20) == 0 && _dst != nullptr) {
-		int clearSize = 0;
-		if (_dst == _specialBuffer && _width > 0 && _height > 0) {
-			const int64 size64 = (int64)_width * _height;
-			if (size64 <= INT_MAX && size64 <= _specialBufferSize)
-				clearSize = (int)size64;
-		} else {
-			clearSize = _vm->_screenWidth * _vm->_screenHeight;
-		}
-		if (clearSize > 0)
-			memset(_dst, 0, clearSize);
+		ra2ClearCurrentTarget();
+		if (!ra2IsHighResMode() || isRebel2GameplayActive(_insane) || _dst != _vm->_virtscr[kMainVirtScreen].getPixels(0, 0))
+			_ra2NativeFrameNeedsClear = false;
 	}
 }
 
diff --git a/engines/scumm/smush/rebel/smush_player_ra2.h b/engines/scumm/smush/rebel/smush_player_ra2.h
index 81cb8ea62dd..86035fc6bc7 100644
--- a/engines/scumm/smush/rebel/smush_player_ra2.h
+++ b/engines/scumm/smush/rebel/smush_player_ra2.h
@@ -30,6 +30,7 @@ class SmushPlayerRebel2 : public SmushPlayer {
 public:
 	SmushPlayerRebel2(ScummEngine_v7 *scumm, IMuseDigital *imuseDigital, Insane *insane);
 	~SmushPlayerRebel2() override;
+	bool ra2PromoteCurrentFrameToHiRes(int scrollX, int scrollY);
 
 protected:
 	void initGamePlayerFields() override;
@@ -50,11 +51,13 @@ protected:
 	bool shouldRouteAllIACTs() const override { return true; }
 	bool handleGameFrameBufferSelect(int codec, int width, int height) override;
 	bool handleGameDimensionOverride(int codec, int width, int height) override;
+	int handleGameFrameObjectPitch(int pitch) override;
 	bool handleGameAdjustCoords(int codec, int &left, int &top, int &width, int &height, int pitch, int *srcSkipY) override;
 	bool handleGameCodecDecode(int codec, const uint8 *src, int left, int top, int width, int height, int pitch, int dataSize, uint8 param = 0, uint16 parm2 = 0) override;
 	bool handleGameStoreFrame() override;
 	void handleGameFrameObjectPre(int codec, int left, int top, int width, int height, int dataSize) override;
 	void handleGameFrameObjectPost(int codec, const byte *data, int32 dataSize, int left, int top, int width, int height) override;
+	void handleGameFrameObjectDecoded(int codec, int left, int top, int width, int height) override;
 	void handleGameFrameStart() override;
 	bool handleGameSkipChunk(uint32 subType, int32 subSize, Common::SeekableReadStream &b) override;
 	void handleGameGost(int32 subSize, Common::SeekableReadStream &b) override;
@@ -69,6 +72,9 @@ private:
 							   int width, int height, TextStyleFlags flg);
 	void ra2PrepareFrameObjectSurface(int left, int top, int width, int height);
 	bool ra2SelectFrameBuffer(int codec, int width, int height);
+	bool ra2EnsureLowResVideoBuffer();
+	void ra2ClearCurrentTarget();
+	bool ra2IsHighResMode() const;
 	bool ra2DecodeCodec(int codec, const uint8 *src, int left, int top,
 						int width, int height, int pitch, int dataSize);
 	void ra2HandleDeltaPalette(int32 subSize, Common::SeekableReadStream &b);
@@ -89,6 +95,10 @@ private:
 	int _ra2FrameObjectOriginalWidth;
 	int _ra2FrameObjectSurfaceWidth;
 	int _ra2FrameObjectSurfaceHeight;
+	byte *_ra2LowResVideoBuffer;
+	int _ra2LowResVideoBufferSize;
+	bool _ra2NativeFrameNeedsClear;
+	bool _ra2UsingGameplaySurface;
 	bool _ra2PendingAnimHeaderPalette;
 	byte _ra2Codec45Palette[0x300];
 	byte _ra2Codec45Lookup[0x8000];
diff --git a/engines/scumm/smush/smush_player.cpp b/engines/scumm/smush/smush_player.cpp
index a6db75dfc8e..067fe39a575 100644
--- a/engines/scumm/smush/smush_player.cpp
+++ b/engines/scumm/smush/smush_player.cpp
@@ -894,6 +894,7 @@ void SmushPlayer::decodeFrameObject(int codec, const uint8 *src, int left, int t
 	int pitch = _vm->_screenWidth;
 	if (_dst == _specialBuffer)
 		pitch = _width;
+	pitch = handleGameFrameObjectPitch(pitch);
 
 	// Save original FOBJ position and dimensions before clipping. Codec 37/47
 	// (delta block/glyph) decode the full frame starting at (0,0). Codec 2
@@ -949,6 +950,8 @@ void SmushPlayer::decodeFrameObject(int codec, const uint8 *src, int left, int t
 		}
 	}
 
+	handleGameFrameObjectDecoded(codec, left, top, width, height);
+
 	if (_storeFrame && !handleGameStoreFrame()) {
 		if (_frameBuffer == nullptr) {
 			_frameBuffer = (byte *)malloc(_width * _height);
diff --git a/engines/scumm/smush/smush_player.h b/engines/scumm/smush/smush_player.h
index a5dc1ff6f79..fe55b5e7ec1 100644
--- a/engines/scumm/smush/smush_player.h
+++ b/engines/scumm/smush/smush_player.h
@@ -318,11 +318,13 @@ protected:
 	virtual bool shouldRouteAllIACTs() const { return false; }
 	virtual bool handleGameFrameBufferSelect(int codec, int width, int height) { return false; }
 	virtual bool handleGameDimensionOverride(int codec, int width, int height) { return false; }
+	virtual int handleGameFrameObjectPitch(int pitch) { return pitch; }
 	virtual bool handleGameAdjustCoords(int codec, int &left, int &top, int &width, int &height, int pitch, int *srcSkipY) { return false; }
 	virtual bool handleGameCodecDecode(int codec, const uint8 *src, int left, int top, int width, int height, int pitch, int dataSize, uint8 param = 0, uint16 parm2 = 0) { return false; }
 	virtual bool handleGameStoreFrame() { return false; }
 	virtual void handleGameFrameObjectPre(int codec, int left, int top, int width, int height, int dataSize) {}
 	virtual void handleGameFrameObjectPost(int codec, const byte *data, int32 dataSize, int left, int top, int width, int height) {}
+	virtual void handleGameFrameObjectDecoded(int codec, int left, int top, int width, int height) {}
 	virtual void handleGameFrameStart() {}
 	virtual bool handleGameSkipChunk(uint32 subType, int32 subSize, Common::SeekableReadStream &b) { return false; }
 	virtual void handleGameGost(int32 subSize, Common::SeekableReadStream &b) {}


Commit: b05208d42899f28bbc523ad0727bb854d6064666
    https://github.com/scummvm/scummvm/commit/b05208d42899f28bbc523ad0727bb854d6064666
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-09T15:21:42+02:00

Commit Message:
SCUMM: RA2: perspective correction in high resolution mode for L2

Changed paths:
    engines/scumm/insane/rebel2/iact.cpp
    engines/scumm/insane/rebel2/rebel.cpp
    engines/scumm/insane/rebel2/rebel.h
    engines/scumm/insane/rebel2/render.cpp
    engines/scumm/insane/rebel2/runlevels.cpp


diff --git a/engines/scumm/insane/rebel2/iact.cpp b/engines/scumm/insane/rebel2/iact.cpp
index 6487dd5528b..607838a25fd 100644
--- a/engines/scumm/insane/rebel2/iact.cpp
+++ b/engines/scumm/insane/rebel2/iact.cpp
@@ -1769,8 +1769,9 @@ bool InsaneRebel2::handleOpcode8EmbeddedAnim(byte *renderBitmap, byte *animData,
 	//   par4=5: 320x200 background -> DAT_0048226c
 	//   par4=6: Overlay -> DAT_00482250, draws immediately
 	//   par4=7: Overlay -> DAT_00482248, draws immediately
+	//   par4=10: GRD005 - Mode 3 overlay -> DAT_00482258 / _grd005Sprite
 	if (!handled && _rebelHandler == 25) {
-		if (par4 == 1 || par4 == 2) {
+		if (par4 == 1 || par4 == 2 || par4 == 10) {
 			handled = loadHandler25GrdSprites(animData, animDataSize, par4);
 		} else if (par4 == 5) {
 			handled = loadLevel2Background(animData, animDataSize, renderBitmap);
@@ -2242,13 +2243,14 @@ bool InsaneRebel2::loadHandler25GrdSprites(byte *animData, int32 size, int16 par
 	// par4 values (from IACT data offset +6):
 	//   1: GRD001 - Primary ship sprite (DAT_00482240 / _grd001Sprite)
 	//   2: GRD002 - Secondary ship sprite (DAT_00482238 / _grd002Sprite)
+	//   10: GRD005 - Mode 3 overlay sprite (DAT_00482258 / _grd005Sprite)
 
 	if (!animData || size <= 0) {
 		return false;
 	}
 
 	// Only handle valid GRD sprite slots
-	if (par4 != 1 && par4 != 2) {
+	if (par4 != 1 && par4 != 2 && par4 != 10) {
 		return false;
 	}
 
@@ -2273,6 +2275,11 @@ bool InsaneRebel2::loadHandler25GrdSprites(byte *animData, int32 size, int16 par
 		_grd002Sprite = newNut;
 		debug("Rebel2: _grd002Sprite set with %d sprites", newNut->getNumChars());
 		break;
+	case 10: // GRD005 - Mode 3 overlay sprite
+		delete _grd005Sprite;
+		_grd005Sprite = newNut;
+		debug("Rebel2: _grd005Sprite set with %d sprites", newNut->getNumChars());
+		break;
 	default:
 		delete newNut;
 		return false;
diff --git a/engines/scumm/insane/rebel2/rebel.cpp b/engines/scumm/insane/rebel2/rebel.cpp
index 93c786740df..07a9f1842ed 100644
--- a/engines/scumm/insane/rebel2/rebel.cpp
+++ b/engines/scumm/insane/rebel2/rebel.cpp
@@ -440,6 +440,7 @@ InsaneRebel2::InsaneRebel2(ScummEngine_v7 *scumm) {
 	// Initialize Handler 25 GRD ship system
 	_grd001Sprite = nullptr;     // DAT_00482240 - GRD001 primary ship
 	_grd002Sprite = nullptr;     // DAT_00482238 - GRD002 secondary ship
+	_grd005Sprite = nullptr;     // DAT_00482258 - GRD005 mode 3 overlay
 	_grdSpriteMode = 0;          // DAT_00457900 - sprite mode (1,2,3,4)
 	memset(_grdShotOriginX, 0, sizeof(_grdShotOriginX));
 	memset(_grdShotOriginY, 0, sizeof(_grdShotOriginY));
@@ -604,6 +605,7 @@ InsaneRebel2::~InsaneRebel2() {
 	// Clean up Handler 25 GRD ship sprites
 	delete _grd001Sprite;
 	delete _grd002Sprite;
+	delete _grd005Sprite;
 
 	// Clean up Handler 0x26 turret HUD overlays
 	delete _hudOverlayNut;
diff --git a/engines/scumm/insane/rebel2/rebel.h b/engines/scumm/insane/rebel2/rebel.h
index f7a5490dbf8..2a7e42c8854 100644
--- a/engines/scumm/insane/rebel2/rebel.h
+++ b/engines/scumm/insane/rebel2/rebel.h
@@ -1191,6 +1191,7 @@ public:
 	// Based on FUN_0041cadb case 6 and FUN_0041db5e disassembly:
 	// - DAT_00482240: Primary ship sprite (GRD001, par4=1)
 	// - DAT_00482238: Secondary ship sprite (GRD002, par4=2)
+	// - DAT_00482258: Mode 3 overlay sprite (GRD005, par4=10)
 	// - DAT_00457900: Sprite mode (1,2,3,4) controls which sprite to draw
 	// - DAT_00457910: Ship X screen position
 	// - DAT_00457912: Ship Y screen position
@@ -1199,6 +1200,7 @@ public:
 
 	NutRenderer *_grd001Sprite;      // DAT_00482240 - GRD001 primary ship NUT
 	NutRenderer *_grd002Sprite;      // DAT_00482238 - GRD002 secondary ship NUT
+	NutRenderer *_grd005Sprite;      // DAT_00482258 - GRD005 mode 3 overlay NUT
 
 	// Handler 25 shot-origin lookup tables from opcode 8/par4=8 text payload.
 	// Indices 5..19 are filled by the retail "%hd %hd ..." parser (FUN_0041CADB case 6).
diff --git a/engines/scumm/insane/rebel2/render.cpp b/engines/scumm/insane/rebel2/render.cpp
index 50534d256e2..3d6eb84afde 100644
--- a/engines/scumm/insane/rebel2/render.cpp
+++ b/engines/scumm/insane/rebel2/render.cpp
@@ -2346,11 +2346,12 @@ void InsaneRebel2::updatePostRenderScroll(int width, int height) {
 		return;
 	}
 
-	if (_rebelHandler == 25 && !isHiRes()) {
-		// Handler 25's low-res L2 corridor layers are authored for the 320x200
-		// viewport. The backing buffer may be larger to decode unclipped FOBJ
-		// data, but panning the final copy exposes unfilled columns/rows and
-		// breaks the corridor perspective.
+	if (_rebelHandler == 25) {
+		// Handler 25's L2 corridor layers are authored for the 320x200 viewport.
+		// The backing buffer may be larger to decode unclipped FOBJ data, but
+		// panning the final copy exposes unfilled columns/rows and breaks the
+		// corridor perspective. High-res mode must scale that same locked native
+		// viewport instead of selecting a different region of the 424x260 buffer.
 		_viewX = 0;
 		_viewY = 0;
 		_player->setScrollOffset(0, 0);
@@ -3684,6 +3685,27 @@ void InsaneRebel2::renderHandler25ShipPre(byte *renderBitmap, int pitch, int wid
 		debug("Rebel2 Handler25 PRE: GRD001 at (%d,%d) nutOff(%d,%d) viewOff(%d,%d) size(%d,%d) mode=%d dmg=%d halfW=%d rightHalf=%d clip=[%d,%d) scale=%d",
 			drawX, drawY, spriteXOffset, spriteYOffset, _rebelViewOffset2X, _rebelViewOffset2Y,
 			spriteW, spriteH, _grdSpriteMode, _rebelDamageLevel, useHalfWidth ? 1 : 0, useRightHalf ? 1 : 0, scaledClipLeft, scaledClipRight, renderScale);
+
+		if (_grdSpriteMode == 3 && _grd005Sprite && _grd005Sprite->getNumChars() > 1) {
+			const int overlayIdx = 1;
+			int overlayW = _grd005Sprite->getCharWidth(overlayIdx);
+			int overlayH = _grd005Sprite->getCharHeight(overlayIdx);
+			int16 overlayXOffset = _grd005Sprite->getCharXOffset(overlayIdx);
+			int16 overlayYOffset = _grd005Sprite->getCharYOffset(overlayIdx);
+
+			int nativeOverlayX = _rebelViewOffset2X + overlayXOffset + nativeBufferViewX;
+			int nativeOverlayY = _rebelViewOffset2Y + overlayYOffset + nativeBufferViewY;
+			int overlayDrawX = renderHiRes ? (nativeOverlayX - nativeViewX) * renderScale : nativeOverlayX;
+			int overlayDrawY = renderHiRes ? (nativeOverlayY - nativeViewY) * renderScale : nativeOverlayY;
+
+			renderNutSpriteScaledClipped(renderBitmap, pitch, width, renderHeight,
+				0, 0, width, renderHeight,
+				overlayDrawX, overlayDrawY, _grd005Sprite, overlayIdx, false, renderScale, false);
+
+			debug("Rebel2 Handler25 PRE: GRD005 at (%d,%d) nutOff(%d,%d) viewOff(%d,%d) size(%d,%d) mode=%d scale=%d",
+				overlayDrawX, overlayDrawY, overlayXOffset, overlayYOffset,
+				_rebelViewOffset2X, _rebelViewOffset2Y, overlayW, overlayH, _grdSpriteMode, renderScale);
+		}
 	}
 }
 
diff --git a/engines/scumm/insane/rebel2/runlevels.cpp b/engines/scumm/insane/rebel2/runlevels.cpp
index 2048677e5ea..e75dadb9d74 100644
--- a/engines/scumm/insane/rebel2/runlevels.cpp
+++ b/engines/scumm/insane/rebel2/runlevels.cpp
@@ -278,6 +278,8 @@ void InsaneRebel2::resetLevelPhaseState(bool clearEnemies) {
 	_grd001Sprite = nullptr;
 	delete _grd002Sprite;
 	_grd002Sprite = nullptr;
+	delete _grd005Sprite;
+	_grd005Sprite = nullptr;
 	_grdShotOriginTableLoaded = false;
 
 	clearEmbeddedHudFrames();


Commit: f00a3eb8de71ff21e662b9fa82462abc7c5eb3d5
    https://github.com/scummvm/scummvm/commit/f00a3eb8de71ff21e662b9fa82462abc7c5eb3d5
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-09T15:21:42+02:00

Commit Message:
SCUMM: RA2: gameplay corrected for in high resolution mode for L3

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


diff --git a/engines/scumm/insane/rebel2/rebel.h b/engines/scumm/insane/rebel2/rebel.h
index 2a7e42c8854..dfb972235c8 100644
--- a/engines/scumm/insane/rebel2/rebel.h
+++ b/engines/scumm/insane/rebel2/rebel.h
@@ -604,6 +604,9 @@ public:
 
 	// Draw Handler 7 ship sprite (third-person ship - FLY sprites)
 	void renderHandler7Ship(byte *renderBitmap, int pitch, int width, int height);
+	void renderHandler7FlySprite(byte *renderBitmap, int pitch, int width, int height,
+		bool renderHiRes, int renderScale, int nativeViewX, int nativeViewY,
+		int nativeX, int nativeY, NutRenderer *nut, int spriteIndex);
 
 	// Draw Handler 8 ship sprite (third-person on foot - POV sprites)
 	void renderHandler8Ship(byte *renderBitmap, int pitch, int width, int height);
diff --git a/engines/scumm/insane/rebel2/render.cpp b/engines/scumm/insane/rebel2/render.cpp
index 3d6eb84afde..b4789c5cb03 100644
--- a/engines/scumm/insane/rebel2/render.cpp
+++ b/engines/scumm/insane/rebel2/render.cpp
@@ -3403,6 +3403,21 @@ void InsaneRebel2::renderStatusBarSprites(byte *renderBitmap, int pitch, int wid
 	}
 }
 
+// renderHandler7FlySprite -- Draw a native Handler 7 FLY sprite into the current presentation target.
+void InsaneRebel2::renderHandler7FlySprite(byte *renderBitmap, int pitch, int width, int height,
+		bool renderHiRes, int renderScale, int nativeViewX, int nativeViewY,
+		int nativeX, int nativeY, NutRenderer *nut, int spriteIndex) {
+	int dstX = renderHiRes ? (nativeX - nativeViewX) * renderScale : nativeX;
+	int dstY = renderHiRes ? (nativeY - nativeViewY) * renderScale : nativeY;
+
+	if (renderHiRes) {
+		renderNutSpriteScaledClipped(renderBitmap, pitch, width, height,
+			0, 0, width, height, dstX, dstY, nut, spriteIndex, false, renderScale, true);
+	} else {
+		renderNutSprite(renderBitmap, pitch, width, height, dstX, dstY, nut, spriteIndex);
+	}
+}
+
 // renderHandler7Ship -- Handler 7 third-person ship rendering (FUN_0040d836).
 void InsaneRebel2::renderHandler7Ship(byte *renderBitmap, int pitch, int width, int height) {
 	// Handler 7 Ship Rendering (Third-Person Ship - FLY sprites)
@@ -3421,7 +3436,20 @@ void InsaneRebel2::renderHandler7Ship(byte *renderBitmap, int pitch, int width,
 	if (spriteIndex >= numSprites)
 		spriteIndex = numSprites - 1;
 
+	const bool renderHiRes = isHiRes() && width >= 640 && height >= 400;
+	const int renderScale = renderHiRes ? 2 : 1;
+	const int nativeViewX = renderHiRes ? _hiResPresentationViewX : 0;
+	const int nativeViewY = renderHiRes ? _hiResPresentationViewY : 0;
+
 	Common::Point shipDraw = getHandler7ShipDrawPoint();
+	if (renderHiRes) {
+		// Low-res draws into the native source buffer with _viewX/_viewY baked in,
+		// then SmushPlayer copies the scrolled viewport. High-res promotion has
+		// already consumed those offsets, so reconstruct the same native source
+		// position before applying the 2x presentation transform.
+		shipDraw.x += nativeViewX;
+		shipDraw.y += nativeViewY;
+	}
 	int shipCenterX = shipDraw.x + 0xd4;
 	int shipCenterY = shipDraw.y + 0x82;
 
@@ -3436,7 +3464,9 @@ void InsaneRebel2::renderHandler7Ship(byte *renderBitmap, int pitch, int width,
 				int cueIndex = _flyEffectAnimCounter % 10;
 				if (cueIndex >= 0 && cueIndex < laserChars) {
 					int cueX = ((shipCenterX - 0x28) - leftDist) - leftDist / 2;
-					renderNutSprite(renderBitmap, pitch, width, height, cueX, shipCenterY, _flyLaserSprite, cueIndex);
+					renderHandler7FlySprite(renderBitmap, pitch, width, height,
+						renderHiRes, renderScale, nativeViewX, nativeViewY,
+						cueX, shipCenterY, _flyLaserSprite, cueIndex);
 				}
 			}
 
@@ -3445,44 +3475,57 @@ void InsaneRebel2::renderHandler7Ship(byte *renderBitmap, int pitch, int width,
 				int cueIndex = (_flyEffectAnimCounter % 10) + 10;
 				if (cueIndex >= 0 && cueIndex < laserChars) {
 					int cueX = rightDist / 2 + rightDist + shipCenterX + 0x28;
-					renderNutSprite(renderBitmap, pitch, width, height, cueX, shipCenterY, _flyLaserSprite, cueIndex);
+					renderHandler7FlySprite(renderBitmap, pitch, width, height,
+						renderHiRes, renderScale, nativeViewX, nativeViewY,
+						cueX, shipCenterY, _flyLaserSprite, cueIndex);
 				}
 			}
 		} else {
 			int16 bottomDist = _corridorBottomY - _flyShipScreenY;
 			int bottomX = shipCenterX;
-			int bottomY = (_corridorBottomY - 0x82) + _perspectiveY + 100 + _viewY;
+			int bottomY = (_corridorBottomY - 0x82) + _perspectiveY + 100 +
+				(renderHiRes ? nativeViewY : _viewY);
 
 			if (bottomDist < 0x19) {
 				_flyEffectAnimCounter++;
 				int cueIndex = _flyEffectAnimCounter % 10;
 				if (cueIndex >= 0 && cueIndex < laserChars)
-					renderNutSprite(renderBitmap, pitch, width, height, bottomX, bottomY, _flyLaserSprite, cueIndex);
+					renderHandler7FlySprite(renderBitmap, pitch, width, height,
+						renderHiRes, renderScale, nativeViewX, nativeViewY,
+						bottomX, bottomY, _flyLaserSprite, cueIndex);
 			}
 			if (bottomDist < 0x32) {
 				_flyEffectAnimCounter++;
 				int cueIndex = _flyEffectAnimCounter % 10;
 				if (cueIndex >= 0 && cueIndex < laserChars)
-					renderNutSprite(renderBitmap, pitch, width, height, bottomX, bottomY, _flyLaserSprite, cueIndex);
+					renderHandler7FlySprite(renderBitmap, pitch, width, height,
+						renderHiRes, renderScale, nativeViewX, nativeViewY,
+						bottomX, bottomY, _flyLaserSprite, cueIndex);
 			}
 
 			int cueIndex = _flyEffectAnimCounter % 10;
 			if (cueIndex >= 0 && cueIndex < laserChars)
-				renderNutSprite(renderBitmap, pitch, width, height, bottomX, bottomY, _flyLaserSprite, cueIndex);
+				renderHandler7FlySprite(renderBitmap, pitch, width, height,
+					renderHiRes, renderScale, nativeViewX, nativeViewY,
+					bottomX, bottomY, _flyLaserSprite, cueIndex);
 		}
 	}
 
-	int drawX = shipDraw.x;
-	int drawY = shipDraw.y;
+	int drawX = renderHiRes ? (shipDraw.x - nativeViewX) * renderScale : shipDraw.x;
+	int drawY = renderHiRes ? (shipDraw.y - nativeViewY) * renderScale : shipDraw.y;
 
-	renderNutSprite(renderBitmap, pitch, width, height, drawX, drawY, _flyShipSprite, spriteIndex);
+	renderHandler7FlySprite(renderBitmap, pitch, width, height,
+		renderHiRes, renderScale, nativeViewX, nativeViewY,
+		shipDraw.x, shipDraw.y, _flyShipSprite, spriteIndex);
 
 	// FUN_40D836 lines 176-180: optional FLY002 overlay pass at ship position.
 	if (_flyLaserSprite && _flyOverlayRepeatCount > 0) {
 		int overlayIndex = spriteIndex + 0x14;
 		if (overlayIndex >= 0 && overlayIndex < _flyLaserSprite->getNumChars()) {
 			for (int i = 0; i < _flyOverlayRepeatCount; i++) {
-				renderNutSprite(renderBitmap, pitch, width, height, drawX, drawY, _flyLaserSprite, overlayIndex);
+				renderHandler7FlySprite(renderBitmap, pitch, width, height,
+					renderHiRes, renderScale, nativeViewX, nativeViewY,
+					shipDraw.x, shipDraw.y, _flyLaserSprite, overlayIndex);
 			}
 		}
 	}
@@ -3490,7 +3533,9 @@ void InsaneRebel2::renderHandler7Ship(byte *renderBitmap, int pitch, int width,
 	// FUN_40D836 lines 181-183: optional FLY003 overlay in high detail mode.
 	if (_flyTargetSprite && _rebelDetailMode >= 0 &&
 		spriteIndex >= 0 && spriteIndex < _flyTargetSprite->getNumChars()) {
-		renderNutSprite(renderBitmap, pitch, width, height, drawX, drawY, _flyTargetSprite, spriteIndex);
+		renderHandler7FlySprite(renderBitmap, pitch, width, height,
+			renderHiRes, renderScale, nativeViewX, nativeViewY,
+			shipDraw.x, shipDraw.y, _flyTargetSprite, spriteIndex);
 	}
 
 	debug("Rebel2 Handler7Ship: draw=(%d,%d) sprite=%d/%d shipPos=(%d,%d) persp=(%d,%d) smoothVel=%d vertIn=%d fxCtr=%d fxRep=%d",
@@ -4623,6 +4668,9 @@ void InsaneRebel2::renderCrosshair(byte *renderBitmap, int pitch, int width, int
 	if (_rebelHandler == 8) {
 		worldMousePos.x += _shipPosX;
 		worldMousePos.y += _shipPosY;
+	} else if (_rebelHandler == 7 && isHiRes() && width >= 640 && height >= 400) {
+		worldMousePos.x += _hiResPresentationViewX;
+		worldMousePos.y += _hiResPresentationViewY;
 	} else if (_rebelHandler != 7) {
 		worldMousePos.x += _viewX;
 		worldMousePos.y += _viewY;


Commit: 9644e845610eca7909dc9ec711d90e69c6a1bb16
    https://github.com/scummvm/scummvm/commit/9644e845610eca7909dc9ec711d90e69c6a1bb16
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-09T15:21:42+02:00

Commit Message:
SCUMM: RA2: implement damage recovery

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


diff --git a/engines/scumm/insane/rebel2/rebel.h b/engines/scumm/insane/rebel2/rebel.h
index dfb972235c8..f4d8536df49 100644
--- a/engines/scumm/insane/rebel2/rebel.h
+++ b/engines/scumm/insane/rebel2/rebel.h
@@ -590,6 +590,7 @@ public:
 	void renderGameplayPostFrame(byte *renderBitmap, int pitch, int width, int height,
 								 int videoWidth, int videoHeight, int statusBarY, int32 curFrame);
 	void updateGameplayDamageEffects(byte *renderBitmap, int pitch, int width, int height);
+	void updateGameplayDamageRecovery(int32 curFrame);
 	void checkGameplayPostRenderCollisions(byte *renderBitmap, int pitch, int width, int height, int32 curFrame);
 
 	// Draw NUT-based HUD overlays for Handler 0x26 turret modes
diff --git a/engines/scumm/insane/rebel2/render.cpp b/engines/scumm/insane/rebel2/render.cpp
index b4789c5cb03..54938cee693 100644
--- a/engines/scumm/insane/rebel2/render.cpp
+++ b/engines/scumm/insane/rebel2/render.cpp
@@ -2602,6 +2602,21 @@ void InsaneRebel2::updateGameplayDamageEffects(byte *renderBitmap, int pitch, in
 	}
 }
 
+// updateGameplayDamageRecovery -- Apply RA2's damage auto-reduction.
+void InsaneRebel2::updateGameplayDamageRecovery(int32 curFrame) {
+	// Handler 0x26 (FUN_4089AB), Handler 8 (FUN_401CCF), and Handler 7
+	// (FUN_40D836) decrement DAT_0047a7ec once every 16 frames after
+	// gameplay collision processing. Handler 25's FUN_41DB5E only awards the
+	// timed score tick in the same slot and does not reduce damage.
+	if ((_rebelHandler != 0x26 && _rebelHandler != 8 && _rebelHandler != 7) ||
+			(curFrame & 0xf) != 0 || _playerDamage <= 0) {
+		return;
+	}
+
+	_playerDamage--;
+	_playerShield = 255 - _playerDamage;
+}
+
 // checkGameplayPostRenderCollisions -- Run handler-specific collision checks.
 void InsaneRebel2::checkGameplayPostRenderCollisions(byte *renderBitmap, int pitch, int width, int height, int32 curFrame) {
 	// Per-frame collision checking against registered zones.
@@ -2717,6 +2732,7 @@ void InsaneRebel2::renderGameplayPostFrame(byte *renderBitmap, int pitch, int wi
 
 	updateGameplayDamageEffects(renderBitmap, pitch, width, height);
 	checkGameplayPostRenderCollisions(renderBitmap, pitch, width, height, curFrame);
+	updateGameplayDamageRecovery(curFrame);
 
 	// Crosshair/reticle (FUN_004089ab, FUN_0040d836).
 	renderCrosshair(renderBitmap, pitch, width, height);


Commit: 74401bdb4db847e2f1c7649cd0ab3d3b03153e04
    https://github.com/scummvm/scummvm/commit/74401bdb4db847e2f1c7649cd0ab3d3b03153e04
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-09T15:21:42+02:00

Commit Message:
SCUMM: RA2: some features for yoda mode

Changed paths:
    engines/scumm/detection.h
    engines/scumm/detection_tables.h
    engines/scumm/insane/rebel2/iact.cpp
    engines/scumm/insane/rebel2/levels.cpp
    engines/scumm/insane/rebel2/menu.cpp
    engines/scumm/insane/rebel2/rebel.cpp
    engines/scumm/insane/rebel2/rebel.h
    engines/scumm/insane/rebel2/render.cpp
    engines/scumm/insane/rebel2/runlevels.cpp
    engines/scumm/metaengine.cpp


diff --git a/engines/scumm/detection.h b/engines/scumm/detection.h
index 29f49690434..c64121e80a5 100644
--- a/engines/scumm/detection.h
+++ b/engines/scumm/detection.h
@@ -42,6 +42,7 @@ namespace Scumm {
 #define GAMEOPTION_REBEL2_UNLOCK_ALL                         GUIO_GAMEOPTIONS11
 #define GAMEOPTION_REBEL1_UNLOCK_ALL                         GUIO_GAMEOPTIONS12
 #define GAMEOPTION_REBEL2_NO_DAMAGE                          GUIO_GAMEOPTIONS13
+#define GAMEOPTION_REBEL2_YODA_MODE                          GUIO_GAMEOPTIONS14
 
 /**
  * Descriptor of a specific SCUMM game. Used internally to store
diff --git a/engines/scumm/detection_tables.h b/engines/scumm/detection_tables.h
index 8e14a3c0152..fb90c801359 100644
--- a/engines/scumm/detection_tables.h
+++ b/engines/scumm/detection_tables.h
@@ -230,8 +230,8 @@ static const GameSettings gameVariantsTable[] = {
 
 	{"rebel1", "", 0, GID_REBEL1, 7, 0, MDT_NONE, 0, Common::kPlatformDOS, GUIO2(GUIO_NOMIDI, GAMEOPTION_REBEL1_UNLOCK_ALL)},
 
-	{"rebel2", "", 0, GID_REBEL2, 7, 0, MDT_NONE, 0, Common::kPlatformDOS, GUIO4(GUIO_NOMIDI, GAMEOPTION_REBEL2_HIRES, GAMEOPTION_REBEL2_UNLOCK_ALL, GAMEOPTION_REBEL2_NO_DAMAGE)},
-	{"rebel2", "Demo", 0, GID_REBEL2, 7, 0, MDT_NONE, GF_DEMO, Common::kPlatformDOS, GUIO4(GUIO_NOMIDI, GAMEOPTION_REBEL2_HIRES, GAMEOPTION_REBEL2_UNLOCK_ALL, GAMEOPTION_REBEL2_NO_DAMAGE)},
+	{"rebel2", "", 0, GID_REBEL2, 7, 0, MDT_NONE, 0, Common::kPlatformDOS, GUIO5(GUIO_NOMIDI, GAMEOPTION_REBEL2_HIRES, GAMEOPTION_REBEL2_UNLOCK_ALL, GAMEOPTION_REBEL2_NO_DAMAGE, GAMEOPTION_REBEL2_YODA_MODE)},
+	{"rebel2", "Demo", 0, GID_REBEL2, 7, 0, MDT_NONE, GF_DEMO, Common::kPlatformDOS, GUIO5(GUIO_NOMIDI, GAMEOPTION_REBEL2_HIRES, GAMEOPTION_REBEL2_UNLOCK_ALL, GAMEOPTION_REBEL2_NO_DAMAGE, GAMEOPTION_REBEL2_YODA_MODE)},
 
 	{"dig",  "", 0, GID_DIG, 7, 0, MDT_NONE, 0, UNK, GUIO5(GUIO_NOMIDI, GAMEOPTION_ENHANCEMENTS, GAMEOPTION_ORIGINALGUI, GAMEOPTION_LOWLATENCYAUDIO, GAMEOPTION_TTS)},
 	{"dig",  "Demo", 0, GID_DIG, 7, 0, MDT_NONE, GF_DEMO, UNK, GUIO4(GUIO_NOMIDI, GAMEOPTION_ORIGINALGUI, GAMEOPTION_LOWLATENCYAUDIO, GAMEOPTION_TTS)},
diff --git a/engines/scumm/insane/rebel2/iact.cpp b/engines/scumm/insane/rebel2/iact.cpp
index 607838a25fd..a96388d0991 100644
--- a/engines/scumm/insane/rebel2/iact.cpp
+++ b/engines/scumm/insane/rebel2/iact.cpp
@@ -1142,7 +1142,7 @@ void InsaneRebel2::handleOpcode6Handler25(byte *renderBitmap, Common::SeekableRe
 	// Autopilot logic (lines 123-146).
 	// From original FUN_0041cadb - NO damageLevel check, toggle happens immediately.
 	// The damage level counter provides the smooth visual transition.
-	if (!_rebelInvulnerable) {
+	if (!_rebelAutoPlay) {
 		if (_rebelAutopilot == 0) {
 			// Uncovered: RIGHT button enters cover.
 			if ((_rebelControlMode & 2) != 0) {
@@ -1160,7 +1160,7 @@ void InsaneRebel2::handleOpcode6Handler25(byte *renderBitmap, Common::SeekableRe
 		// Clear control mode after processing (sticky flags consumed).
 		_rebelControlMode = 0;
 	} else {
-		// Invulnerable mode: random autopilot changes.
+		// Auto play: random autopilot changes.
 		if (_rebelAutopilot == 0) {
 			if (_vm->_rnd.getRandomNumber(100) == 0) {
 				_rebelAutopilot = 1;
@@ -1345,7 +1345,7 @@ void InsaneRebel2::handleOpcode6GenericInit(int16 par4) {
 void InsaneRebel2::updateOpcode6GenericFlightState() {
 	// Step 3: Autopilot/control mode logic (lines 123-146)
 	// This determines whether the ship flies on autopilot or manual control.
-	if (!_rebelInvulnerable) {
+	if (!_rebelAutoPlay) {
 		// Normal mode: check control mode flags.
 		if (_rebelAutopilot == 0) {
 			if ((_rebelControlMode & 2) != 0) {
@@ -1357,7 +1357,7 @@ void InsaneRebel2::updateOpcode6GenericFlightState() {
 			}
 		}
 	} else {
-		// Invulnerable mode: random autopilot changes.
+		// Auto play: random autopilot changes.
 		if (_rebelAutopilot == 0) {
 			if (_vm->_rnd.getRandomNumber(100) == 0) {
 				_rebelAutopilot = 1;
diff --git a/engines/scumm/insane/rebel2/levels.cpp b/engines/scumm/insane/rebel2/levels.cpp
index b133a232415..ebbdff25fd0 100644
--- a/engines/scumm/insane/rebel2/levels.cpp
+++ b/engines/scumm/insane/rebel2/levels.cpp
@@ -81,10 +81,17 @@ void InsaneRebel2::runGame() {
 	while (!_vm->shouldQuit()) {
 		int menuResult = runMainMenu();
 
-		if (menuResult == 0 || _vm->shouldQuit())
+		if (menuResult == kMenuQuit || _vm->shouldQuit())
 			break;
 
-		if (menuResult == kMenuNewGame || menuResult == kMenuContinue) {
+		if (menuResult == kMenuResumeDemo) {
+			playIntroSequence();
+			if (!_vm->shouldQuit())
+				showTopPilots();
+			continue;
+		}
+
+		if (menuResult == kMenuNewGame) {
 			int pilotResult = runLevelSelect();
 
 			if (pilotResult == kLevelSelectQuit || _vm->shouldQuit())
diff --git a/engines/scumm/insane/rebel2/menu.cpp b/engines/scumm/insane/rebel2/menu.cpp
index 400928d1832..b80ece9239a 100644
--- a/engines/scumm/insane/rebel2/menu.cpp
+++ b/engines/scumm/insane/rebel2/menu.cpp
@@ -48,6 +48,7 @@ namespace Scumm {
 void InsaneRebel2::resetMenu() {
 	_menuSelection = 0;
 	_menuInactivityTimer = 0;
+	_menuInactivityTimedOut = false;
 	_menuRepeatDelay = 0;
 	_menuSelectionConfirmed = false;
 	setVirtualKeyboardVisible(false);
@@ -591,7 +592,7 @@ void InsaneRebel2::showPauseOverlay() {
 }
 
 // runMainMenu -- Main menu loop (FUN_004147B2).
-// Returns kMenuNewGame, kMenuContinue, kMenuCredits, or 0 (quit).
+// Returns kMenuNewGame, kMenuResumeDemo, or kMenuQuit.
 int InsaneRebel2::runMainMenu() {
 
 	debug("Rebel2: Entering main menu");
@@ -630,7 +631,14 @@ int InsaneRebel2::runMainMenu() {
 		// Check for quit
 		if (_vm->shouldQuit()) {
 			_menuInputActive = false;
-			return 0;
+			return kMenuQuit;
+		}
+
+		if (_menuInactivityTimedOut) {
+			debug("Rebel2: Main menu inactivity - resuming intro/demo loop");
+			_menuInactivityTimedOut = false;
+			_menuInputActive = false;
+			return kMenuResumeDemo;
 		}
 
 		// Only process selection if user explicitly confirmed (ENTER/ESC),
@@ -651,7 +659,7 @@ int InsaneRebel2::runMainMenu() {
 		//   case 0 (TRS 11): Start Game -> pilot selection, returns 2
 		//   case 1 (TRS 12): Options -> FUN_00416787 options screen
 		//   case 2 (TRS 13): Calibrate Joystick -> FUN_00425820
-		//   case 3 (TRS 14): Continue Intro -> replay O_OPEN videos
+		//   case 3 (TRS 14): Continue Intro -> return to intro/demo loop
 		//   case 4 (TRS 15): Show Top Pilots -> FUN_00420116(-1)
 		//   case 5 (TRS 16): Show Credits -> play O_CREDIT.SAN, returns 1
 		//   case 6 (TRS 17): Return to Launcher -> quit, returns 0
@@ -673,22 +681,10 @@ int InsaneRebel2::runMainMenu() {
 			// joystick calibration flow is required here.
 			break;
 
-		case 3:  // Continue Intro -> replay intro videos
-			debug("Rebel2: Continue Intro selected - replaying intro");
-			// Temporarily switch to intro state to disable menu overlay
-			// This emulates FUN_004142BD case 0 behavior
-			_gameState = kStateIntro;
+		case 3:  // Continue Intro -> return to intro/demo loop
+			debug("Rebel2: Continue Intro selected - resuming intro/demo loop");
 			_menuInputActive = false;
-			// Play intro sequence again (O_OPEN_A/B)
-			splayer->setCurVideoFlags(0x20);
-			splayer->play("OPEN/O_OPEN_A.SAN", 15);
-			if (!_vm->shouldQuit()) {
-				splayer->play("OPEN/O_OPEN_B.SAN", 15);
-			}
-			// Restore menu state
-			_gameState = kStateMainMenu;
-			_menuInputActive = true;
-			break;
+			return kMenuResumeDemo;
 
 		case 4:  // Show Top Pilots -> high score display (FUN_00420116(-1))
 			debug("Rebel2: Show Top Pilots selected");
@@ -709,7 +705,7 @@ int InsaneRebel2::runMainMenu() {
 		case 6:  // Return to Launcher -> quit game
 			debug("Rebel2: Return to Launcher selected");
 			_menuInputActive = false;
-			return 0;  // Return 0 to exit
+			return kMenuQuit;
 
 		default:
 			debug("Rebel2: Unknown menu selection %d", _menuSelection);
@@ -718,7 +714,7 @@ int InsaneRebel2::runMainMenu() {
 	}
 
 	_menuInputActive = false;
-	return 0;
+	return kMenuQuit;
 }
 
 // ---------------------------------------------------------------------------
diff --git a/engines/scumm/insane/rebel2/rebel.cpp b/engines/scumm/insane/rebel2/rebel.cpp
index 07a9f1842ed..9959ca44bdd 100644
--- a/engines/scumm/insane/rebel2/rebel.cpp
+++ b/engines/scumm/insane/rebel2/rebel.cpp
@@ -208,11 +208,13 @@ InsaneRebel2::InsaneRebel2(ScummEngine_v7 *scumm) {
 	_textOverlayFadeIn = 0;
 	_textOverlayFadeOut = 0;
 
-	// Retail globals mapped: hit counter, cooldown, invulnerability flag
+	// Retail globals mapped: hit counter, cooldown, movie/auto-play flags
 	_rebelOp6Initialized = false;
 	_rebelHitCounter = 0;
 	_rebelKillCounter = 0;
-	_rebelInvulnerable = false;
+	_rebelYodaMode = false;
+	_rebelMovieMode = false;
+	_rebelAutoPlay = false;
 	_rebelWaveState = 0;
 	_rebelPhaseState = 0;
 
@@ -476,6 +478,7 @@ InsaneRebel2::InsaneRebel2(ScummEngine_v7 *scumm) {
 	// FUN_0041f5ae uses the selectable item count for Y position calculation.
 	_menuItemCount = 7;
 	_menuInactivityTimer = 0;
+	_menuInactivityTimedOut = false;
 	_lastMenuVariant = -1;        // No previous menu video
 	_menuRepeatDelay = 0;
 	_menuSelectionConfirmed = false;
@@ -495,6 +498,7 @@ InsaneRebel2::InsaneRebel2(ScummEngine_v7 *scumm) {
 	// Set to true to bypass normal unlock progression
 	_debugUnlockAll = ConfMan.getBool("rebel2_unlock_all");
 	_noDamage = ConfMan.getBool("rebel2_no_damage");
+	_rebelYodaMode = ConfMan.getBool("rebel2_yoda_mode");
 
 	for (i = 0; i < 16; i++) {
 		// If debug unlock is enabled, unlock all chapters
@@ -533,7 +537,7 @@ InsaneRebel2::InsaneRebel2(ScummEngine_v7 *scumm) {
 
 	// Initialize options menu state (FUN_004167A6 defaults)
 	_optionsSelection = 0;
-	_optionsItemCount = 9;
+	_optionsItemCount = 8;
 	_optMusicEnabled = !_vm->_mixer->isSoundTypeMuted(Audio::Mixer::kMusicSoundType);
 	_optSfxEnabled = !_vm->_mixer->isSoundTypeMuted(Audio::Mixer::kSFXSoundType);
 	_optVoicesEnabled = !_vm->_mixer->isSoundTypeMuted(Audio::Mixer::kSpeechSoundType);
@@ -678,6 +682,29 @@ bool InsaneRebel2::notifyEvent(const Common::Event &event) {
 	if (_vm->isPaused())
 		return false;
 
+	if (_rebelYodaMode && event.type == Common::EVENT_KEYDOWN && !event.kbdRepeat && event.kbd.hasFlags(Common::KBD_ALT)) {
+		switch (event.kbd.keycode) {
+		case Common::KEYCODE_m:
+			// Retail DAT_0047ab60: Yoda-mode Movie Mode skips playable
+			// sections and keeps the story/cutscene sequence moving.
+			_rebelMovieMode = !_rebelMovieMode;
+			debug("Rebel2: Movie mode %s", _rebelMovieMode ? "enabled" : "disabled");
+			if (_rebelMovieMode && splayer && _gameState == kStateGameplay && _rebelHandler != 0)
+				_vm->_smushVideoShouldFinish = true;
+			return true;
+
+		case Common::KEYCODE_p:
+			// Retail DAT_0047ab64: Yoda-mode Auto Play makes gameplay
+			// computer controlled.
+			_rebelAutoPlay = !_rebelAutoPlay;
+			debug("Rebel2: Auto play %s", _rebelAutoPlay ? "enabled" : "disabled");
+			return true;
+
+		default:
+			break;
+		}
+	}
+
 	if (_gameState == kStateGameplay && _rebelHandler == 7 &&
 			event.type == Common::EVENT_MOUSEMOVE) {
 		if (_gameplayMouseSettleUntil != 0) {
@@ -1325,7 +1352,7 @@ InsaneRebel2::LevelDifficultyParams InsaneRebel2::getDifficultyParams() const {
 }
 
 bool InsaneRebel2::applyPlayerDamage(int damage) {
-	if (_noDamage || _rebelInvulnerable || damage <= 0)
+	if (_noDamage || _rebelAutoPlay || damage <= 0)
 		return false;
 
 	_playerDamage += damage;
@@ -1615,8 +1642,16 @@ int32 InsaneRebel2::processMouse() {
 	// Shot trigger behavior:
 	// - Handler 25 keeps edge-triggered clicks due cover-toggle/sticky input semantics.
 	// - Other gameplay handlers fire while button is held; slot counters still rate-limit.
-	bool triggerShot = (_rebelHandler == 25) ? (leftPressed && !leftWasPressed) : leftPressed;
 	bool canShoot = isShootingAllowed();
+	bool autoFire = _rebelAutoPlay && canShoot && _gameState == kStateGameplay && _rebelHandler != 0;
+	if (autoFire && _player) {
+		const int autoFirePeriod = (_rebelHandler == 8) ? 6 : 7;
+		autoFire = (_player->_frame % autoFirePeriod) == 0;
+	}
+	if (autoFire)
+		_rebelControlMode |= 1;
+
+	bool triggerShot = ((_rebelHandler == 25) ? (leftPressed && !leftWasPressed) : leftPressed) || autoFire;
 	if (_rebelHandler == 8) {
 		// FUN_00401CCF uses the same per-frame fire bit both to spawn shots and
 		// to choose the POV gun sprite. Keep the sprite driven by the event-manager
@@ -1625,7 +1660,9 @@ int32 InsaneRebel2::processMouse() {
 	}
 	if (triggerShot && canShoot) {
 		Common::Point mousePos;
-		if (_rebelHandler == 7) {
+		if (autoFire) {
+			mousePos = getRebelAutoPlayAimPoint();
+		} else if (_rebelHandler == 7) {
 			mousePos = getHandler7ShotTargetPoint();
 		} else if (_rebelHandler == 8) {
 			mousePos = getHandler8ShotTargetPoint();
@@ -1776,10 +1813,47 @@ int32 InsaneRebel2::processMouse() {
 	return buttons;
 }
 
+Common::Point InsaneRebel2::getRebelAutoPlayAimPoint() {
+	Common::Point target(160, 100);
+	int bestDistance = 0x7fffffff;
+
+	for (Common::List<enemy>::iterator it = _enemies.begin(); it != _enemies.end(); ++it) {
+		if (!it->active || it->destroyed)
+			continue;
+
+		int x = (it->rect.left + it->rect.right) / 2;
+		int y = (it->rect.top + it->rect.bottom) / 2;
+		if (_rebelHandler == 8) {
+			x -= _shipPosX;
+			y -= _shipPosY;
+		} else if (_rebelHandler != 7) {
+			x -= _viewX;
+			y -= _viewY;
+		}
+
+		if (x < -32 || x > 351 || y < -32 || y > 231)
+			continue;
+
+		const int dx = x - 160;
+		const int dy = y - 100;
+		const int distance = dx * dx + dy * dy;
+		if (distance < bestDistance) {
+			bestDistance = distance;
+			target.x = CLIP<int>(x, 0, 319);
+			target.y = CLIP<int>(y, 0, 199);
+		}
+	}
+
+	return target;
+}
+
 Common::Point InsaneRebel2::getGameplayAimPoint() {
 	// Pure getter (queried many times per frame): the aim/reticle follows the virtual
 	// mouse position. Directional controls pan that position incrementally once per frame
 	// via updateGameplayAimFromGamepad(), rather than snapping the reticle to a screen edge.
+	if (_rebelAutoPlay && _gameState == kStateGameplay && !_menuInputActive)
+		return getRebelAutoPlayAimPoint();
+
 	int x = _vm->_mouse.x;
 	int y = _vm->_mouse.y;
 	if (isHiRes()) {
@@ -1803,7 +1877,7 @@ int16 applyRebel2AnalogDeadzone(int16 axisValue) {
 }
 
 void InsaneRebel2::updateGameplayAimFromGamepad() {
-	if (_menuInputActive || _gameState != kStateGameplay)
+	if (_menuInputActive || _gameState != kStateGameplay || _rebelAutoPlay)
 		return;
 
 	const int dpadX = (_vm->getActionState(kScummActionInsaneRight) ? 1 : 0) -
diff --git a/engines/scumm/insane/rebel2/rebel.h b/engines/scumm/insane/rebel2/rebel.h
index f4d8536df49..1b263aed8ee 100644
--- a/engines/scumm/insane/rebel2/rebel.h
+++ b/engines/scumm/insane/rebel2/rebel.h
@@ -79,19 +79,17 @@ public:
 
 	// Menu selection results (return values from FUN_004147B2)
 	enum MenuResult {
-		kMenuNewGame = 2,     // case 0: New Game
-		kMenuContinue = 4,    // case 1: Continue
-		kMenuOptions = 0,     // case 2: Options (stays in menu)
-		kMenuExit = 0,        // case 3: Exit to title
-		kMenuUnknown = 0,     // case 4: Unknown function
+		kMenuQuit = -1,       // case 6: Return to Launcher
+		kMenuResumeDemo = 0,  // case 3 / inactivity: Continue Intro
 		kMenuCredits = 1,     // case 5: Show credits
-		kMenuQuit = 0         // case 6: Quit game
+		kMenuNewGame = 2      // case 0: Start Game
 	};
 
 	GameState _gameState;           // Current game state
 	int _menuSelection;             // Current menu item (0-6), mirrors DAT_00459988
 	int _menuItemCount;             // Number of menu items (7 for main menu)
 	int _menuInactivityTimer;       // Timeout counter (300 frames = ~10 sec)
+	bool _menuInactivityTimedOut;   // Main menu should return to the intro/demo loop
 	int _lastMenuVariant;           // Last random menu video shown (DAT_00482400)
 	int _menuRepeatDelay;           // Delay for key repeat (DAT_00459ce0)
 	bool _menuSelectionConfirmed;   // True only when user explicitly confirmed a selection
@@ -219,8 +217,8 @@ public:
 	// ---------------------------------------------------------------------------
 	// TRS strings: 89 (title), 90-101 (toggle labels), 103 (volume), 107/109 (back)
 	// Original settings array at DAT_00482e20[0..4]:
-	//   [0]=auto control, [1]=music, [2]=voices, [3]=sound
-	// Additional flags: DAT_0047a7fe (text/indicators), DAT_0047a80a (rapid fire/arrows)
+	//   [0]=text, [1]=music, [2]=voices, [3]=sound, [4]=hidden abort flag
+	// Additional flags: DAT_0047a7fe (controls normal/flipped), DAT_0047a80a (rapid fire)
 	// Volume: DAT_0047a804 (0-127), SFX vol: DAT_0047a802 (127-768)
 
 	void showOptionsMenu();
@@ -511,6 +509,7 @@ public:
 
 	int32 processMouse() override;
 	Common::Point getGameplayAimPoint();
+	Common::Point getRebelAutoPlayAimPoint();
 	// Per-frame: pan the gameplay reticle incrementally from the held directional controls
 	// (on-screen/physical gamepad dpad, keyboard arrows) instead of snapping it to a screen
 	// edge. Call once per frame; getGameplayAimPoint() stays a pure getter.
@@ -929,7 +928,9 @@ public:
 	bool _rebelOp6Initialized; // Guard: opcode 6 init block (clearBit/links/wave) runs once per video
 	int _rebelHitCounter;    // DAT_0047ab80 - hit counter / state tracker
 	int _rebelKillCounter;   // DAT_0047ab88 - enemies destroyed this phase
-	bool _rebelInvulnerable; // DAT_0047ab64 - toggles invulnerability / state
+	bool _rebelYodaMode;     // DAT_0047ab5a > 2 - unlocks Yoda-mode shortcuts
+	bool _rebelMovieMode;    // DAT_0047ab60 - Alt+M skips playable sections
+	bool _rebelAutoPlay;     // DAT_0047ab64 - Alt+P computer-controlled play
 
 	// Enemy wave/phase state tracking (FUN_004028c5 / FUN_00417b61)
 	// DAT_0047ab98: Per-wave enemy kill state. Bits set when enemy types are destroyed.
diff --git a/engines/scumm/insane/rebel2/render.cpp b/engines/scumm/insane/rebel2/render.cpp
index 54938cee693..408e4d87411 100644
--- a/engines/scumm/insane/rebel2/render.cpp
+++ b/engines/scumm/insane/rebel2/render.cpp
@@ -1951,7 +1951,7 @@ void InsaneRebel2::checkHandler7ObstacleZones(uint16 &warningMask) {
 }
 
 bool InsaneRebel2::applyHandler7WallDamage(int wallDamage) {
-	if (_hitCooldown < 5 && !_rebelInvulnerable) {
+	if (_hitCooldown < 5 && !_rebelAutoPlay) {
 		const bool damageApplied = applyPlayerDamage(wallDamage);
 		_rebelHitCounter++;
 		_hitCooldown = 10;
@@ -2515,13 +2515,11 @@ bool InsaneRebel2::handlePostRenderMenuModes(byte *renderBitmap, int pitch, int
 		// At 12fps video rate, 300 frames = ~25 seconds of inactivity.
 		// The original checks: if (local_8 > 299) return 0.
 		if (_menuInactivityTimer > 300) {
-			debug("Rebel2: Menu inactivity timeout - ending video to loop");
-			// Signal video to end so menu loop plays new video.
-			// This emulates the attract mode behavior where a new random
-			// menu video is selected after inactivity.
+			debug("Rebel2: Menu inactivity timeout - resuming intro/demo loop");
 			_menuInactivityTimer = 0;
-			// Don't set _smushVideoShouldFinish here - let video end naturally.
-			// This will cause runMainMenu to loop and play a new random video.
+			_menuInactivityTimedOut = true;
+			_menuSelectionConfirmed = false;
+			_vm->_smushVideoShouldFinish = true;
 		}
 
 		// Draw menu selection overlay.
diff --git a/engines/scumm/insane/rebel2/runlevels.cpp b/engines/scumm/insane/rebel2/runlevels.cpp
index e75dadb9d74..d09d5bb4b4c 100644
--- a/engines/scumm/insane/rebel2/runlevels.cpp
+++ b/engines/scumm/insane/rebel2/runlevels.cpp
@@ -100,6 +100,16 @@ InsaneRebel2::WaveEndResult InsaneRebel2::processWaveEnd(int16 mask, int16 *budg
 
 	WaveEndResult result;
 
+	if (_rebelMovieMode) {
+		_skipSectionRequested = false;
+		_rebelPhaseState = mask;
+		_rebelWaveState = mask;
+		debug("Rebel2 processWaveEnd: movie mode completed gameplay wave (mask=0x%x)", (uint16)mask);
+		result.completed = true;
+		result.skipped = true;
+		return result;
+	}
+
 	// Debug shortcut path: force-end current section when requested via Shift+S.
 	if (_skipSectionRequested) {
 		_skipSectionRequested = false;
@@ -192,6 +202,16 @@ bool InsaneRebel2::playLevelSegment(const char *filename, uint16 flags, bool rec
 	SmushPlayer *splayer = ((ScummEngine_v7 *)_vm)->_splayer;
 
 	const bool isRecordedGameplay = recordFrame && (flags & 0x08) != 0;
+	if (isRecordedGameplay && _rebelMovieMode) {
+		debug("Rebel2: Movie mode skipping gameplay segment: %s", filename);
+		_gameplaySectionActive = false;
+		restoreIOSGamepadController();
+		if (recordFrame)
+			_deathFrame = 0;
+		restoreDamageFlashPalette();
+		return true;
+	}
+
 	if (isRecordedGameplay) {
 		// Center only at the section boundary; looped wave videos are continuations.
 		if (!_gameplaySectionActive && (flags & 0x40) == 0)
diff --git a/engines/scumm/metaengine.cpp b/engines/scumm/metaengine.cpp
index 0f75ecbec27..ba6e3e32042 100644
--- a/engines/scumm/metaengine.cpp
+++ b/engines/scumm/metaengine.cpp
@@ -905,6 +905,15 @@ static const ExtraGuiOption enableRebel2NoDamage = {
 	0
 };
 
+static const ExtraGuiOption enableRebel2YodaMode = {
+	_s("Yoda mode"),
+	_s("Enable original Yoda mode shortcuts, including movie mode and auto play"),
+	"rebel2_yoda_mode",
+	false,
+	0,
+	0
+};
+
 const ExtraGuiOption enableRebel1UnlockAll = {
 	_s("Unlock all levels"),
 	_s("All levels will be available without requiring passwords"),
@@ -964,6 +973,9 @@ const ExtraGuiOptions ScummMetaEngine::getExtraGuiOptions(const Common::String &
 	if (target.empty() || guiOptions.contains(GAMEOPTION_REBEL2_NO_DAMAGE)) {
 		options.push_back(enableRebel2NoDamage);
 	}
+	if (target.empty() || guiOptions.contains(GAMEOPTION_REBEL2_YODA_MODE)) {
+		options.push_back(enableRebel2YodaMode);
+	}
 	if (target.empty() || guiOptions.contains(GAMEOPTION_REBEL1_UNLOCK_ALL)) {
 		options.push_back(enableRebel1UnlockAll);
 	}


Commit: 50674c1a69af97ec6a98cb5296640baf66ca9153
    https://github.com/scummvm/scummvm/commit/50674c1a69af97ec6a98cb5296640baf66ca9153
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-09T15:21:42+02:00

Commit Message:
SCUMM: RA1: no damage mode

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


diff --git a/engines/scumm/detection.h b/engines/scumm/detection.h
index c64121e80a5..b04b215f58b 100644
--- a/engines/scumm/detection.h
+++ b/engines/scumm/detection.h
@@ -43,6 +43,7 @@ namespace Scumm {
 #define GAMEOPTION_REBEL1_UNLOCK_ALL                         GUIO_GAMEOPTIONS12
 #define GAMEOPTION_REBEL2_NO_DAMAGE                          GUIO_GAMEOPTIONS13
 #define GAMEOPTION_REBEL2_YODA_MODE                          GUIO_GAMEOPTIONS14
+#define GAMEOPTION_REBEL1_NO_DAMAGE                          GUIO_GAMEOPTIONS15
 
 /**
  * Descriptor of a specific SCUMM game. Used internally to store
diff --git a/engines/scumm/detection_tables.h b/engines/scumm/detection_tables.h
index fb90c801359..f6baccb46fa 100644
--- a/engines/scumm/detection_tables.h
+++ b/engines/scumm/detection_tables.h
@@ -228,7 +228,7 @@ static const GameSettings gameVariantsTable[] = {
 	{"ft",   "Remastered", 0, GID_FT,  7, 0, MDT_NONE, GF_DOUBLEFINE_PAK, UNK, GUIO5(GUIO_NOMIDI, GAMEOPTION_ENHANCEMENTS, GAMEOPTION_ORIGINALGUI, GAMEOPTION_LOWLATENCYAUDIO, GAMEOPTION_TTS)},
 	{"ft",   "Demo", 0, GID_FT,  7, 0, MDT_NONE, GF_DEMO, UNK, GUIO4(GUIO_NOMIDI, GAMEOPTION_ORIGINALGUI, GAMEOPTION_LOWLATENCYAUDIO, GAMEOPTION_TTS)},
 
-	{"rebel1", "", 0, GID_REBEL1, 7, 0, MDT_NONE, 0, Common::kPlatformDOS, GUIO2(GUIO_NOMIDI, GAMEOPTION_REBEL1_UNLOCK_ALL)},
+	{"rebel1", "", 0, GID_REBEL1, 7, 0, MDT_NONE, 0, Common::kPlatformDOS, GUIO3(GUIO_NOMIDI, GAMEOPTION_REBEL1_UNLOCK_ALL, GAMEOPTION_REBEL1_NO_DAMAGE)},
 
 	{"rebel2", "", 0, GID_REBEL2, 7, 0, MDT_NONE, 0, Common::kPlatformDOS, GUIO5(GUIO_NOMIDI, GAMEOPTION_REBEL2_HIRES, GAMEOPTION_REBEL2_UNLOCK_ALL, GAMEOPTION_REBEL2_NO_DAMAGE, GAMEOPTION_REBEL2_YODA_MODE)},
 	{"rebel2", "Demo", 0, GID_REBEL2, 7, 0, MDT_NONE, GF_DEMO, Common::kPlatformDOS, GUIO5(GUIO_NOMIDI, GAMEOPTION_REBEL2_HIRES, GAMEOPTION_REBEL2_UNLOCK_ALL, GAMEOPTION_REBEL2_NO_DAMAGE, GAMEOPTION_REBEL2_YODA_MODE)},
diff --git a/engines/scumm/insane/rebel1/iact.cpp b/engines/scumm/insane/rebel1/iact.cpp
index 9c96d37650a..59c2e7b5861 100644
--- a/engines/scumm/insane/rebel1/iact.cpp
+++ b/engines/scumm/insane/rebel1/iact.cpp
@@ -1223,7 +1223,7 @@ void InsaneRebel1::updateShipPhysics() {
 
 	// Damage guard/mask from FUN_1DEB5: (_damageFlags & 0x96) != 0
 	// damageFlags & 0x96 = bits 1,2,4,7 = wall collisions (0x16) + projectile hit (0x80)
-	if ((_damageFlags & 0x96) != 0 && _damageCooldown == 0 &&
+	if (!_noDamage && (_damageFlags & 0x96) != 0 && _damageCooldown == 0 &&
 		_health >= 0 && _deathTimer <= 0) {
 		// Projectile hit (bit 7 = 0x80)
 		if (_damageFlags & 0x80)
@@ -1392,7 +1392,7 @@ void InsaneRebel1::updateTurretPhysics() {
 	_turretFrameShipCenterValid = true;
 
 	// Damage gate from FUN_1E6A7.
-	if (_damageFlags != 0 && _damageCooldown == 0 && _health >= 0 && _deathTimer <= 0) {
+	if (!_noDamage && _damageFlags != 0 && _damageCooldown == 0 && _health >= 0 && _deathTimer <= 0) {
 		if (_damageFlags == 0x80)
 			_health -= _tuning.shot;
 		else
@@ -1603,7 +1603,7 @@ void InsaneRebel1::updateGameOp0BPhysics() {
 	// Damage application (FUN_1CDA7 lines 20-41)
 	// Original 0x0B mapping: 0x80 -> +0x13, 0x40 -> +0x0F, 0x20 -> +0x11.
 	// No cooldown ??? all three damage types can stack each frame
-	if (_damageFlags != 0 && _health >= 0 && _deathTimer < 1) {
+	if (!_noDamage && _damageFlags != 0 && _health >= 0 && _deathTimer < 1) {
 		const int16 oldHealth = _health;
 		const byte appliedDamageFlags = _damageFlags;
 		_screenFlash = 5;
@@ -2035,7 +2035,7 @@ void InsaneRebel1::updateOnFootSequence() {
 	// --- Damage handling (from HandleGameOp19_OnFootSequence) ---
 	// On-foot damage uses the same heavy-damage tuning byte as ship shot/collision
 	// damage in the original, not the miss penalty.
-	if (_damageFlags != 0 && _damageCooldown == 0 && _health >= 0 && _deathTimer < 1) {
+	if (!_noDamage && _damageFlags != 0 && _damageCooldown == 0 && _health >= 0 && _deathTimer < 1) {
 		const int16 oldHealth = _health;
 		_health -= _tuning.shot;
 		if (_health < 0) {
diff --git a/engines/scumm/insane/rebel1/rebel.cpp b/engines/scumm/insane/rebel1/rebel.cpp
index 4e981793ec0..e4bfb41ef65 100644
--- a/engines/scumm/insane/rebel1/rebel.cpp
+++ b/engines/scumm/insane/rebel1/rebel.cpp
@@ -307,6 +307,7 @@ InsaneRebel1::InsaneRebel1(ScummEngine_v7 *scumm) : Insane(), _vm(scumm) {
 	_hudDirtyFlag = 0;
 	_maxChapterUnlocked = 0;
 	_unlockAllLevels = ConfMan.getBool("rebel1_unlock_all");
+	_noDamage = ConfMan.getBool("rebel1_no_damage");
 	_interactiveVideoActive = false;
 	_preserveInteractiveRuntimeState = false;
 	_interactiveVideoCheatSkipped = false;
diff --git a/engines/scumm/insane/rebel1/rebel.h b/engines/scumm/insane/rebel1/rebel.h
index a110ab1e295..71138104d32 100644
--- a/engines/scumm/insane/rebel1/rebel.h
+++ b/engines/scumm/insane/rebel1/rebel.h
@@ -502,6 +502,7 @@ private:
 	byte _hudDirtyFlag;          // 0x7601: 0xFF after HUD redraw (set by renderHUD)
 	int16 _maxChapterUnlocked;   // 0x7730: highest unlocked passcode slot (0=none)
 	bool _unlockAllLevels;       // ScummVM option: expose level select without passcodes
+	bool _noDamage;              // ScummVM option: suppress player damage
 
 	static const int16 kMaxHealth = 98;
 	static const int16 kDeathTimerInit = 30;
diff --git a/engines/scumm/metaengine.cpp b/engines/scumm/metaengine.cpp
index ba6e3e32042..06c6bd74354 100644
--- a/engines/scumm/metaengine.cpp
+++ b/engines/scumm/metaengine.cpp
@@ -923,6 +923,15 @@ const ExtraGuiOption enableRebel1UnlockAll = {
 	0
 };
 
+const ExtraGuiOption enableRebel1NoDamage = {
+	_s("No damage"),
+	_s("Disable player damage"),
+	"rebel1_no_damage",
+	false,
+	0,
+	0
+};
+
 const ExtraGuiOptions ScummMetaEngine::getExtraGuiOptions(const Common::String &target) const {
 	ExtraGuiOptions options;
 	// Query the GUI options
@@ -979,6 +988,9 @@ const ExtraGuiOptions ScummMetaEngine::getExtraGuiOptions(const Common::String &
 	if (target.empty() || guiOptions.contains(GAMEOPTION_REBEL1_UNLOCK_ALL)) {
 		options.push_back(enableRebel1UnlockAll);
 	}
+	if (target.empty() || guiOptions.contains(GAMEOPTION_REBEL1_NO_DAMAGE)) {
+		options.push_back(enableRebel1NoDamage);
+	}
 	if (target.empty() || gameid == "comi") {
 		options.push_back(comiObjectLabelsOption);
 



From noreply at scummvm.org  Tue Jun  9 13:22:15 2026
From: noreply at scummvm.org (ScummVM-Translations)
Date: Tue,  9 Jun 2026 13:22:15 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 c9f2a37a5b96dc95863b86410fe466cdf6dfdff6
Message-ID: <20260609132215.B64447005CF@ron.scummvm.net>

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

Summary:
c9f2a37a5b I18N: Update translations templates


Commit: c9f2a37a5b96dc95863b86410fe466cdf6dfdff6
    https://github.com/scummvm/scummvm/commit/c9f2a37a5b96dc95863b86410fe466cdf6dfdff6
Author: Weblate (noreply at weblate.org)
Date: 2026-06-09T13:22:10Z

Commit Message:
I18N: Update translations templates

Changed paths:
    po/scummvm.pot


diff --git a/po/scummvm.pot b/po/scummvm.pot
index f49545f372e..30005aee849 100644
--- a/po/scummvm.pot
+++ b/po/scummvm.pot
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2026.2.1git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-08 11:04+0000\n"
+"POT-Creation-Date: 2026-06-09 13:22+0000\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL at ADDRESS>\n"
 "Language-Team: LANGUAGE <LL at li.org>\n"
@@ -329,7 +329,7 @@ msgstr ""
 #: engines/hypno/metaengine.cpp:192 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:197
 #: engines/mm/mm1/metaengine.cpp:54 engines/parallaction/metaengine.cpp:169
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4467
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
 #: engines/sherlock/metaengine.cpp:638 engines/sherlock/metaengine.cpp:1244
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:328
@@ -348,7 +348,7 @@ msgstr ""
 #: engines/hypno/metaengine.cpp:198 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:204
 #: engines/mm/mm1/metaengine.cpp:52 engines/parallaction/metaengine.cpp:175
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4467
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
 #: engines/sherlock/metaengine.cpp:643 engines/sherlock/metaengine.cpp:1250
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:329
@@ -376,7 +376,7 @@ msgstr ""
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:123
 #: engines/drascula/saveload.cpp:51 engines/grim/inputdialog.cpp:83
 #: engines/parallaction/saveload.cpp:270 engines/sci/engine/kgraphics.cpp:76
-#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4479
+#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4482
 #: engines/sword1/control.cpp:2816
 msgid "OK"
 msgstr ""
@@ -1014,7 +1014,7 @@ msgstr ""
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1100
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1124
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1035,7 +1035,7 @@ msgstr ""
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1067
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1091
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1056,7 +1056,7 @@ msgstr ""
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1080
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1104
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1076,7 +1076,7 @@ msgstr ""
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1087
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1111
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3162,7 +3162,7 @@ msgstr ""
 #: engines/mohawk/riven_stacks/aspit.cpp:454
 #: engines/neverhood/metaengine.cpp:200 engines/saga/metaengine.cpp:449
 #: engines/scumm/help.cpp:84 engines/scumm/help.cpp:86
-#: engines/scumm/scumm.cpp:4479 engines/sludge/keymapper_tables.h:95
+#: engines/scumm/scumm.cpp:4482 engines/sludge/keymapper_tables.h:95
 #: engines/sludge/keymapper_tables.h:144 engines/sludge/keymapper_tables.h:223
 #: engines/sludge/keymapper_tables.h:343 engines/sludge/keymapper_tables.h:355
 #: engines/sludge/keymapper_tables.h:416 engines/sludge/keymapper_tables.h:429
@@ -3589,7 +3589,7 @@ msgid "Game not implemented"
 msgstr ""
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
-#: engines/scumm/metaengine.cpp:1050
+#: engines/scumm/metaengine.cpp:1074
 msgid "Default game keymappings"
 msgstr ""
 
@@ -5435,7 +5435,7 @@ msgstr ""
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1208 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1232 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr ""
 
@@ -6334,7 +6334,7 @@ msgstr ""
 msgid "Quick load or save game isn't supported in this location"
 msgstr ""
 
-#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3418
+#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3421
 #, c-format
 msgid ""
 "Failed to load saved game from file:\n"
@@ -6342,7 +6342,7 @@ msgid ""
 "%s"
 msgstr ""
 
-#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3405
+#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3408
 #, c-format
 msgid ""
 "Failed to save game to file:\n"
@@ -6350,7 +6350,7 @@ msgid ""
 "%s"
 msgstr ""
 
-#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3438
+#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3441
 #, c-format
 msgid ""
 "Successfully saved game in file:\n"
@@ -6796,7 +6796,7 @@ msgstr ""
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1126 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -7443,7 +7443,7 @@ msgstr ""
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1113
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1137
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -9330,7 +9330,7 @@ msgid "Player ammo will never decrease."
 msgstr ""
 
 #: engines/hypno/metaengine.cpp:70 engines/scumm/metaengine.cpp:891
-#: engines/scumm/metaengine.cpp:909
+#: engines/scumm/metaengine.cpp:918
 msgid "Unlock all levels"
 msgstr ""
 
@@ -11477,7 +11477,7 @@ msgid "Your Name:"
 msgstr ""
 
 #. I18N: Button, start hosting online multiplayer game
-#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4485
+#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4488
 msgid "Host"
 msgstr ""
 
@@ -11487,7 +11487,7 @@ msgid "Querying games..."
 msgstr ""
 
 #. I18N: Join online multiplayer game
-#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4485
+#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4488
 msgid "Join"
 msgstr ""
 
@@ -12390,104 +12390,112 @@ msgstr ""
 msgid "Run the game in 640x400 high resolution mode instead of 320x200"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:910
+#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:919
 msgid "All levels will be available without requiring passwords"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:900
+#: engines/scumm/metaengine.cpp:900 engines/scumm/metaengine.cpp:927
 msgid "No damage"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:901
+#: engines/scumm/metaengine.cpp:901 engines/scumm/metaengine.cpp:928
 msgid "Disable player damage"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1061
+#: engines/scumm/metaengine.cpp:909
+msgid "Yoda mode"
+msgstr ""
+
+#: engines/scumm/metaengine.cpp:910
+msgid "Enable original Yoda mode shortcuts, including movie mode and auto play"
+msgstr ""
+
+#: engines/scumm/metaengine.cpp:1085
 msgid "Down left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1074
+#: engines/scumm/metaengine.cpp:1098
 msgid "Down right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1094
+#: engines/scumm/metaengine.cpp:1118
 msgid "Up left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1107
+#: engines/scumm/metaengine.cpp:1131
 msgid "Up right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1119
+#: engines/scumm/metaengine.cpp:1143
 msgid "Switch weapon"
 msgstr ""
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1133
+#: engines/scumm/metaengine.cpp:1157
 msgid "Win the bike fight cheat"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1142
+#: engines/scumm/metaengine.cpp:1166
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1144 engines/scumm/metaengine.cpp:1221
+#: engines/scumm/metaengine.cpp:1168 engines/scumm/metaengine.cpp:1245
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1149 engines/scumm/metaengine.cpp:1226
+#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1250
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1231
+#: engines/scumm/metaengine.cpp:1178 engines/scumm/metaengine.cpp:1255
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
+#: engines/scumm/metaengine.cpp:1183 engines/scumm/metaengine.cpp:1260
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1241
+#: engines/scumm/metaengine.cpp:1188 engines/scumm/metaengine.cpp:1265
 msgid "Stick up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1171 engines/scumm/metaengine.cpp:1248
+#: engines/scumm/metaengine.cpp:1195 engines/scumm/metaengine.cpp:1272
 msgid "Stick down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1178 engines/scumm/metaengine.cpp:1255
+#: engines/scumm/metaengine.cpp:1202 engines/scumm/metaengine.cpp:1279
 msgid "Stick left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1185 engines/scumm/metaengine.cpp:1262
+#: engines/scumm/metaengine.cpp:1209 engines/scumm/metaengine.cpp:1286
 msgid "Stick right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1192 engines/scumm/metaengine.cpp:1269
+#: engines/scumm/metaengine.cpp:1216 engines/scumm/metaengine.cpp:1293
 msgid "Fire / select"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1198
+#: engines/scumm/metaengine.cpp:1222
 msgid "Walk / menu back"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1203
+#: engines/scumm/metaengine.cpp:1227
 msgid "Skip / menu back"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1219
+#: engines/scumm/metaengine.cpp:1243
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1274
+#: engines/scumm/metaengine.cpp:1298
 msgid "Cover / back"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1280
+#: engines/scumm/metaengine.cpp:1304
 msgid "Skip / back"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1287
+#: engines/scumm/metaengine.cpp:1311
 msgid "Skip / menu"
 msgstr ""
 
@@ -12513,43 +12521,43 @@ msgid ""
 "320x200, under 'engine' tab."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1362
+#: engines/scumm/scumm.cpp:1365
 #, c-format
 msgid "This game requires the '%s' Macintosh executable for its fonts."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1371
+#: engines/scumm/scumm.cpp:1374
 #, c-format
 msgid ""
 "This game requires the '%s' Macintosh executable for its music and fonts."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1375
+#: engines/scumm/scumm.cpp:1378
 #, c-format
 msgid ""
 "Could not find the '%s' Macintosh executable to read resources from. %s will "
 "be disabled."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1376
+#: engines/scumm/scumm.cpp:1379
 msgid "The Mac GUI"
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1376
+#: engines/scumm/scumm.cpp:1379
 msgid "The music and the Mac GUI"
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1405
+#: engines/scumm/scumm.cpp:1408
 #, c-format
 msgid "Could not open Macintosh resource file %s"
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1408
+#: engines/scumm/scumm.cpp:1411
 #, c-format
 msgid "Could not find resource fork in Macintosh resource file %s"
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1415
+#: engines/scumm/scumm.cpp:1418
 #, c-format
 msgid ""
 "'%s' appears to be the wrong Dig executable. It may be the launcher one "
@@ -12558,7 +12566,7 @@ msgid ""
 "its sub-folders should be what you need."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1581
+#: engines/scumm/scumm.cpp:1584
 msgid ""
 "It appears your ScummVM version was not built with TrueType Fonts support.\n"
 "\n"
@@ -12566,34 +12574,34 @@ msgid ""
 "some of the graphics on screen will be missing."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1915
+#: engines/scumm/scumm.cpp:1918
 msgid ""
 "Audio files compressed with ScummVM Tools were detected; *.BUN/*.SOU\n"
 "compression is not supported anymore for this game, audio will be disabled.\n"
 "Please copy the game from the original media without compression."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:2405
+#: engines/scumm/scumm.cpp:2408
 #, c-format
 msgid ""
 "Native MIDI support requires the Roland Upgrade from LucasArts,\n"
 "but %s is missing. Using AdLib instead."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:2420
+#: engines/scumm/scumm.cpp:2423
 msgid ""
 "This particular version of Monkey Island 1 is known to miss some\n"
 "required resources for MT-32. Using AdLib instead."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:4361
+#: engines/scumm/scumm.cpp:4364
 msgid ""
 "Usually, Maniac Mansion would start now. But for that to work, the game "
 "files for Maniac Mansion have to be in the 'Maniac' directory inside the "
 "Tentacle game directory, and the game has to be added to ScummVM."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:4485
+#: engines/scumm/scumm.cpp:4488
 msgid "Would you like to host or join a network play session?"
 msgstr ""
 



From noreply at scummvm.org  Tue Jun  9 13:22:49 2026
From: noreply at scummvm.org (ScummVM-Translations)
Date: Tue,  9 Jun 2026 13:22:49 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 de3f6dfb8276bb25dea67923ecc61dfa0a25d4f8
Message-ID: <20260609132249.065A97005BC@ron.scummvm.net>

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

Summary:
de3f6dfb82 I18N: Update translation files


Commit: de3f6dfb8276bb25dea67923ecc61dfa0a25d4f8
    https://github.com/scummvm/scummvm/commit/de3f6dfb8276bb25dea67923ecc61dfa0a25d4f8
Author: Weblate (noreply at weblate.org)
Date: 2026-06-09T13:22:30Z

Commit Message:
I18N: Update translation files

Updated by "Update PO files to match POT (msgmerge)" hook in Weblate.

Translation: ScummVM/scummvm
Translate-URL: https://translations.scummvm.org/projects/scummvm/scummvm/

Changed paths:
    po/ar.po
    po/be-tarask.po
    po/be_BY.po
    po/ca_ES.po
    po/cs_CZ.po
    po/da.po
    po/de_DE.po
    po/el.po
    po/es_ES.po
    po/eu.po
    po/fi_FI.po
    po/fr_FR.po
    po/gl_ES.po
    po/he.po
    po/hi.po
    po/hu_HU.po
    po/it_IT.po
    po/ja.po
    po/ka.po
    po/ko.po
    po/nb_NO.po
    po/nl_NL.po
    po/nn_NO.po
    po/pl_PL.po
    po/pt_BR.po
    po/pt_PT.po
    po/ro.po
    po/ru_RU.po
    po/sv_SE.po
    po/tr.po
    po/uk_UA.po
    po/zh.po
    po/zh_Hans.po
    po/zh_Hant.po


diff --git a/po/ar.po b/po/ar.po
index 70e14492376..4d33b6f7fca 100644
--- a/po/ar.po
+++ b/po/ar.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-08 11:04+0000\n"
+"POT-Creation-Date: 2026-06-09 13:22+0000\n"
 "PO-Revision-Date: 2026-02-24 22:26+0000\n"
 "Last-Translator: Mohamed Shaaban <mohmedbashamedo at hotmail.com>\n"
 "Language-Team: Arabic <https://translations.scummvm.org/projects/scummvm/"
@@ -354,7 +354,7 @@ msgstr "?????????? ?????? ???????? ????????. ???? ???????? ????????????????"
 #: engines/hypno/metaengine.cpp:192 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:197
 #: engines/mm/mm1/metaengine.cpp:54 engines/parallaction/metaengine.cpp:169
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4467
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
 #: engines/sherlock/metaengine.cpp:638 engines/sherlock/metaengine.cpp:1244
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:328
@@ -373,7 +373,7 @@ msgstr "??????"
 #: engines/hypno/metaengine.cpp:198 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:204
 #: engines/mm/mm1/metaengine.cpp:52 engines/parallaction/metaengine.cpp:175
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4467
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
 #: engines/sherlock/metaengine.cpp:643 engines/sherlock/metaengine.cpp:1250
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:329
@@ -403,7 +403,7 @@ msgstr ""
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:123
 #: engines/drascula/saveload.cpp:51 engines/grim/inputdialog.cpp:83
 #: engines/parallaction/saveload.cpp:270 engines/sci/engine/kgraphics.cpp:76
-#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4479
+#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4482
 #: engines/sword1/control.cpp:2816
 msgid "OK"
 msgstr "??????????"
@@ -1060,7 +1060,7 @@ msgstr "??????????????"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1100
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1124
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1081,7 +1081,7 @@ msgstr "????????"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1067
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1091
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1102,7 +1102,7 @@ msgstr "????????"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1080
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1104
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1122,7 +1122,7 @@ msgstr "????????"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1087
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1111
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3393,7 +3393,7 @@ msgstr ""
 #: engines/mohawk/riven_stacks/aspit.cpp:454
 #: engines/neverhood/metaengine.cpp:200 engines/saga/metaengine.cpp:449
 #: engines/scumm/help.cpp:84 engines/scumm/help.cpp:86
-#: engines/scumm/scumm.cpp:4479 engines/sludge/keymapper_tables.h:95
+#: engines/scumm/scumm.cpp:4482 engines/sludge/keymapper_tables.h:95
 #: engines/sludge/keymapper_tables.h:144 engines/sludge/keymapper_tables.h:223
 #: engines/sludge/keymapper_tables.h:343 engines/sludge/keymapper_tables.h:355
 #: engines/sludge/keymapper_tables.h:416 engines/sludge/keymapper_tables.h:429
@@ -3912,7 +3912,7 @@ msgid "Game not implemented"
 msgstr "?????????? ???????????? ?????? ??????????"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
-#: engines/scumm/metaengine.cpp:1050
+#: engines/scumm/metaengine.cpp:1074
 #, fuzzy
 #| msgid "Default game keymap"
 msgid "Default game keymappings"
@@ -5935,7 +5935,7 @@ msgstr "R2"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1208 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1232 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "??????????????"
 
@@ -6936,7 +6936,7 @@ msgstr ""
 msgid "Quick load or save game isn't supported in this location"
 msgstr "???? ?????? ?????? ?????????????? ???????????? ???? ?????? ???????????? ???? ?????? ????????????"
 
-#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3418
+#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3421
 #, c-format
 msgid ""
 "Failed to load saved game from file:\n"
@@ -6947,7 +6947,7 @@ msgstr ""
 "\n"
 "%s"
 
-#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3405
+#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3408
 #, c-format
 msgid ""
 "Failed to save game to file:\n"
@@ -6958,7 +6958,7 @@ msgstr ""
 "\n"
 "%s"
 
-#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3438
+#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3441
 #, c-format
 msgid ""
 "Successfully saved game in file:\n"
@@ -7459,7 +7459,7 @@ msgstr "?????????? ????????????"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1126 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -8225,7 +8225,7 @@ msgstr "??????????????"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1113
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1137
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -10538,7 +10538,7 @@ msgid "Player ammo will never decrease."
 msgstr "???? ?????????? ?????????? ???????????? ??????????."
 
 #: engines/hypno/metaengine.cpp:70 engines/scumm/metaengine.cpp:891
-#: engines/scumm/metaengine.cpp:909
+#: engines/scumm/metaengine.cpp:918
 msgid "Unlock all levels"
 msgstr "???????? ???????? ??????????????????"
 
@@ -13184,7 +13184,7 @@ msgid "Your Name:"
 msgstr "??????????:"
 
 #. I18N: Button, start hosting online multiplayer game
-#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4485
+#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4488
 msgid "Host"
 msgstr "????????????"
 
@@ -13194,7 +13194,7 @@ msgid "Querying games..."
 msgstr "???????? ?????????????????? ???? ?????????????? ..."
 
 #. I18N: Join online multiplayer game
-#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4485
+#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4488
 msgid "Join"
 msgstr "????????"
 
@@ -14169,136 +14169,146 @@ msgstr "?????????? ?????? ??????????"
 msgid "Run the game in 640x400 high resolution mode instead of 320x200"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:910
+#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:919
 #, fuzzy
 #| msgid "All levels will be available to play."
 msgid "All levels will be available without requiring passwords"
 msgstr "?????????? ???????? ?????????????????? ?????????? ????????."
 
-#: engines/scumm/metaengine.cpp:900
+#: engines/scumm/metaengine.cpp:900 engines/scumm/metaengine.cpp:927
 #, fuzzy
 #| msgid "No date saved"
 msgid "No damage"
 msgstr "???? ?????? ?????? ??????????????"
 
-#: engines/scumm/metaengine.cpp:901
+#: engines/scumm/metaengine.cpp:901 engines/scumm/metaengine.cpp:928
 #, fuzzy
 #| msgid "Disable save menu"
 msgid "Disable player damage"
 msgstr "?????????? ?????????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1061
+#: engines/scumm/metaengine.cpp:909
+#, fuzzy
+#| msgid "Fast move modifier"
+msgid "Yoda mode"
+msgstr "???????? ???????????? ????????????"
+
+#: engines/scumm/metaengine.cpp:910
+msgid "Enable original Yoda mode shortcuts, including movie mode and auto play"
+msgstr ""
+
+#: engines/scumm/metaengine.cpp:1085
 #, fuzzy
 #| msgid "Down Left"
 msgid "Down left"
 msgstr "???????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1074
+#: engines/scumm/metaengine.cpp:1098
 #, fuzzy
 #| msgid "Down Right"
 msgid "Down right"
 msgstr "???????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1094
+#: engines/scumm/metaengine.cpp:1118
 #, fuzzy
 #| msgid "Upper left item"
 msgid "Up left"
 msgstr "???????????? ???????????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1107
+#: engines/scumm/metaengine.cpp:1131
 #, fuzzy
 #| msgid "Up Right"
 msgid "Up right"
 msgstr "???????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1119
+#: engines/scumm/metaengine.cpp:1143
 msgid "Switch weapon"
 msgstr "?????????? ????????????"
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1133
+#: engines/scumm/metaengine.cpp:1157
 msgid "Win the bike fight cheat"
 msgstr "?????? ???????? ???? ?????????? ??????????????"
 
-#: engines/scumm/metaengine.cpp:1142
+#: engines/scumm/metaengine.cpp:1166
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1144 engines/scumm/metaengine.cpp:1221
+#: engines/scumm/metaengine.cpp:1168 engines/scumm/metaengine.cpp:1245
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1149 engines/scumm/metaengine.cpp:1226
+#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1250
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1231
+#: engines/scumm/metaengine.cpp:1178 engines/scumm/metaengine.cpp:1255
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
+#: engines/scumm/metaengine.cpp:1183 engines/scumm/metaengine.cpp:1260
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1241
+#: engines/scumm/metaengine.cpp:1188 engines/scumm/metaengine.cpp:1265
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "??????????"
 
-#: engines/scumm/metaengine.cpp:1171 engines/scumm/metaengine.cpp:1248
+#: engines/scumm/metaengine.cpp:1195 engines/scumm/metaengine.cpp:1272
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "?????? ???????????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1178 engines/scumm/metaengine.cpp:1255
+#: engines/scumm/metaengine.cpp:1202 engines/scumm/metaengine.cpp:1279
 #, fuzzy
 #| msgid "Slide Left"
 msgid "Stick left"
 msgstr "?????????????? ?????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1185 engines/scumm/metaengine.cpp:1262
+#: engines/scumm/metaengine.cpp:1209 engines/scumm/metaengine.cpp:1286
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick right"
 msgstr "???????????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1192 engines/scumm/metaengine.cpp:1269
+#: engines/scumm/metaengine.cpp:1216 engines/scumm/metaengine.cpp:1293
 #, fuzzy
 #| msgid "Move left"
 msgid "Fire / select"
 msgstr "???????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1198
+#: engines/scumm/metaengine.cpp:1222
 #, fuzzy
 #| msgid "Open main menu"
 msgid "Walk / menu back"
 msgstr "???????? ?????????????? ????????????????"
 
-#: engines/scumm/metaengine.cpp:1203
+#: engines/scumm/metaengine.cpp:1227
 #, fuzzy
 #| msgid "Open main menu"
 msgid "Skip / menu back"
 msgstr "???????? ?????????????? ????????????????"
 
-#: engines/scumm/metaengine.cpp:1219
+#: engines/scumm/metaengine.cpp:1243
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1274
+#: engines/scumm/metaengine.cpp:1298
 #, fuzzy
 #| msgid "Move Back"
 msgid "Cover / back"
 msgstr "???????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1280
+#: engines/scumm/metaengine.cpp:1304
 #, fuzzy
 #| msgid "Skip dialogue"
 msgid "Skip / back"
 msgstr "???????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1287
+#: engines/scumm/metaengine.cpp:1311
 #, fuzzy
 #| msgid "Open main menu"
 msgid "Skip / menu"
@@ -14331,18 +14341,18 @@ msgstr ""
 "???????? ?????????? ?????????? ???????? ?????????? ?????? ???????????????? ???? ???????? ?????? ?????????? ?????? 200 ??300 ?? "
 "?????? ?????????? ?????????? \"????????????\"."
 
-#: engines/scumm/scumm.cpp:1362
+#: engines/scumm/scumm.cpp:1365
 #, c-format
 msgid "This game requires the '%s' Macintosh executable for its fonts."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1371
+#: engines/scumm/scumm.cpp:1374
 #, c-format
 msgid ""
 "This game requires the '%s' Macintosh executable for its music and fonts."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1375
+#: engines/scumm/scumm.cpp:1378
 #, fuzzy, c-format
 #| msgid ""
 #| "Could not find the 'Monkey Island' Macintosh executable to read the\n"
@@ -14354,25 +14364,25 @@ msgstr ""
 "???????? ???????????? ?????? ?????? Macintosh ???????????? ?????????????? \"Monkey Island\" ???????????? ??????\n"
 "?????????? ????. ???????? ?????????? ????????????????."
 
-#: engines/scumm/scumm.cpp:1376
+#: engines/scumm/scumm.cpp:1379
 msgid "The Mac GUI"
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1376
+#: engines/scumm/scumm.cpp:1379
 msgid "The music and the Mac GUI"
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1405
+#: engines/scumm/scumm.cpp:1408
 #, c-format
 msgid "Could not open Macintosh resource file %s"
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1408
+#: engines/scumm/scumm.cpp:1411
 #, c-format
 msgid "Could not find resource fork in Macintosh resource file %s"
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1415
+#: engines/scumm/scumm.cpp:1418
 #, c-format
 msgid ""
 "'%s' appears to be the wrong Dig executable. It may be the launcher one "
@@ -14381,7 +14391,7 @@ msgid ""
 "its sub-folders should be what you need."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1581
+#: engines/scumm/scumm.cpp:1584
 msgid ""
 "It appears your ScummVM version was not built with TrueType Fonts support.\n"
 "\n"
@@ -14389,7 +14399,7 @@ msgid ""
 "some of the graphics on screen will be missing."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1915
+#: engines/scumm/scumm.cpp:1918
 msgid ""
 "Audio files compressed with ScummVM Tools were detected; *.BUN/*.SOU\n"
 "compression is not supported anymore for this game, audio will be disabled.\n"
@@ -14399,7 +14409,7 @@ msgstr ""
 "???? ?????? ?????????? ?????????????? ???????? ???????????? ?? ?????????? ?????????? ??????????.\n"
 "???????? ?????? ???????????? ???? ?????????????? ?????????????? ?????? ??????."
 
-#: engines/scumm/scumm.cpp:2405
+#: engines/scumm/scumm.cpp:2408
 #, c-format
 msgid ""
 "Native MIDI support requires the Roland Upgrade from LucasArts,\n"
@@ -14408,7 +14418,7 @@ msgstr ""
 "?????????? ?????? MIDI ???????????? ?????????? Roland ???? LucasArts ??\n"
 "??????%s ??????????. ?????????????? AdLib ?????????? ???? ??????."
 
-#: engines/scumm/scumm.cpp:2420
+#: engines/scumm/scumm.cpp:2423
 msgid ""
 "This particular version of Monkey Island 1 is known to miss some\n"
 "required resources for MT-32. Using AdLib instead."
@@ -14416,7 +14426,7 @@ msgstr ""
 "???? ?????????????? ???? ?????? ???????????? ???????????? ???? Monkey Island 1 ?????????? ?????????? ????????\n"
 "?????????????? ???????????????? ?? MT-32. ?????????????? AdLib ?????????? ???? ??????."
 
-#: engines/scumm/scumm.cpp:4361
+#: engines/scumm/scumm.cpp:4364
 msgid ""
 "Usually, Maniac Mansion would start now. But for that to work, the game "
 "files for Maniac Mansion have to be in the 'Maniac' directory inside the "
@@ -14426,7 +14436,7 @@ msgstr ""
 "???????????? ???????????? ?? Maniac Mansion ???? ???????? \"Maniac\" ???????? ???????? ???????? Tentacle ?? "
 "???????? ?????????? ???????????? ?????? ScummVM."
 
-#: engines/scumm/scumm.cpp:4485
+#: engines/scumm/scumm.cpp:4488
 msgid "Would you like to host or join a network play session?"
 msgstr "???? ???????? ???? ?????????????? ???? ???????????????? ?????? ???????? ?????? ?????? ??????????????"
 
diff --git a/po/be-tarask.po b/po/be-tarask.po
index 98978f867f2..7c138d1c392 100644
--- a/po/be-tarask.po
+++ b/po/be-tarask.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-08 11:04+0000\n"
+"POT-Creation-Date: 2026-06-09 13:22+0000\n"
 "PO-Revision-Date: 2023-02-26 15:01+0000\n"
 "Last-Translator: Kastu?? K. <oakum2334 at gmail.com>\n"
 "Language-Team: Belarusian (Tara??kievica) <https://translations.scummvm.org/"
@@ -356,7 +356,7 @@ msgstr "?????????? ???????????????? ???????????? ??????????. ???? ???????????? 
 #: engines/hypno/metaengine.cpp:192 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:197
 #: engines/mm/mm1/metaengine.cpp:54 engines/parallaction/metaengine.cpp:169
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4467
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
 #: engines/sherlock/metaengine.cpp:638 engines/sherlock/metaengine.cpp:1244
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:328
@@ -375,7 +375,7 @@ msgstr "??????"
 #: engines/hypno/metaengine.cpp:198 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:204
 #: engines/mm/mm1/metaengine.cpp:52 engines/parallaction/metaengine.cpp:175
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4467
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
 #: engines/sherlock/metaengine.cpp:643 engines/sherlock/metaengine.cpp:1250
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:329
@@ -406,7 +406,7 @@ msgstr ""
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:123
 #: engines/drascula/saveload.cpp:51 engines/grim/inputdialog.cpp:83
 #: engines/parallaction/saveload.cpp:270 engines/sci/engine/kgraphics.cpp:76
-#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4479
+#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4482
 #: engines/sword1/control.cpp:2816
 msgid "OK"
 msgstr "????"
@@ -1071,7 +1071,7 @@ msgstr "????????????????????"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1100
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1124
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1092,7 +1092,7 @@ msgstr "??????????"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1067
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1091
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1113,7 +1113,7 @@ msgstr "????????"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1080
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1104
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1133,7 +1133,7 @@ msgstr "????????????"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1087
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1111
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3360,7 +3360,7 @@ msgstr ""
 #: engines/mohawk/riven_stacks/aspit.cpp:454
 #: engines/neverhood/metaengine.cpp:200 engines/saga/metaengine.cpp:449
 #: engines/scumm/help.cpp:84 engines/scumm/help.cpp:86
-#: engines/scumm/scumm.cpp:4479 engines/sludge/keymapper_tables.h:95
+#: engines/scumm/scumm.cpp:4482 engines/sludge/keymapper_tables.h:95
 #: engines/sludge/keymapper_tables.h:144 engines/sludge/keymapper_tables.h:223
 #: engines/sludge/keymapper_tables.h:343 engines/sludge/keymapper_tables.h:355
 #: engines/sludge/keymapper_tables.h:416 engines/sludge/keymapper_tables.h:429
@@ -3868,7 +3868,7 @@ msgid "Game not implemented"
 msgstr "ID ???????????? ???? ??????????????????????????????"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
-#: engines/scumm/metaengine.cpp:1050
+#: engines/scumm/metaengine.cpp:1074
 #, fuzzy
 #| msgid "Default game keymap"
 msgid "Default game keymappings"
@@ -5868,7 +5868,7 @@ msgstr "R2"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1208 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1232 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "????????"
 
@@ -6872,7 +6872,7 @@ msgstr ""
 msgid "Quick load or save game isn't supported in this location"
 msgstr "???????????? ???????????????????????? ???? ???????????????????? ???? ?????????????????????????????? ?? ?????????? ??????????????"
 
-#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3418
+#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3421
 #, c-format
 msgid ""
 "Failed to load saved game from file:\n"
@@ -6883,7 +6883,7 @@ msgstr ""
 "\n"
 "%s"
 
-#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3405
+#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3408
 #, c-format
 msgid ""
 "Failed to save game to file:\n"
@@ -6894,7 +6894,7 @@ msgstr ""
 "\n"
 "%s"
 
-#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3438
+#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3441
 #, c-format
 msgid ""
 "Successfully saved game in file:\n"
@@ -7401,7 +7401,7 @@ msgstr "???????????????????? ?????????? ??????"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1126 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -8172,7 +8172,7 @@ msgstr "????????????????????"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1113
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1137
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -10493,7 +10493,7 @@ msgid "Player ammo will never decrease."
 msgstr "?????????? ???????????? ???????????? ???? ????????????????????."
 
 #: engines/hypno/metaengine.cpp:70 engines/scumm/metaengine.cpp:891
-#: engines/scumm/metaengine.cpp:909
+#: engines/scumm/metaengine.cpp:918
 msgid "Unlock all levels"
 msgstr ""
 
@@ -13142,7 +13142,7 @@ msgid "Your Name:"
 msgstr "??????????:"
 
 #. I18N: Button, start hosting online multiplayer game
-#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4485
+#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4488
 msgid "Host"
 msgstr ""
 
@@ -13154,7 +13154,7 @@ msgid "Querying games..."
 msgstr "???????????????????? ???????????????"
 
 #. I18N: Join online multiplayer game
-#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4485
+#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4488
 msgid "Join"
 msgstr ""
 
@@ -14100,132 +14100,142 @@ msgstr "?????????????? ???????????????????????? ??????????????????"
 msgid "Run the game in 640x400 high resolution mode instead of 320x200"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:910
+#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:919
 msgid "All levels will be available without requiring passwords"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:900
+#: engines/scumm/metaengine.cpp:900 engines/scumm/metaengine.cpp:927
 #, fuzzy
 #| msgid "No date saved"
 msgid "No damage"
 msgstr "???????? ???? ??????????????????"
 
-#: engines/scumm/metaengine.cpp:901
+#: engines/scumm/metaengine.cpp:901 engines/scumm/metaengine.cpp:928
 #, fuzzy
 #| msgid "Disable save menu"
 msgid "Disable player damage"
 msgstr "???????????????? ???????? ????????????????????"
 
-#: engines/scumm/metaengine.cpp:1061
+#: engines/scumm/metaengine.cpp:909
+#, fuzzy
+#| msgid "Fast move modifier"
+msgid "Yoda mode"
+msgstr "?????????????????? ?????????????? ????????"
+
+#: engines/scumm/metaengine.cpp:910
+msgid "Enable original Yoda mode shortcuts, including movie mode and auto play"
+msgstr ""
+
+#: engines/scumm/metaengine.cpp:1085
 #, fuzzy
 msgid "Down left"
 msgstr "?????????????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1074
+#: engines/scumm/metaengine.cpp:1098
 #, fuzzy
 msgid "Down right"
 msgstr "?????????????? ??????????????"
 
-#: engines/scumm/metaengine.cpp:1094
+#: engines/scumm/metaengine.cpp:1118
 #, fuzzy
 #| msgid "Upper left item"
 msgid "Up left"
 msgstr "???????????? ?????????? ??????"
 
-#: engines/scumm/metaengine.cpp:1107
+#: engines/scumm/metaengine.cpp:1131
 #, fuzzy
 msgid "Up right"
 msgstr "??????????????"
 
-#: engines/scumm/metaengine.cpp:1119
+#: engines/scumm/metaengine.cpp:1143
 #, fuzzy
 msgid "Switch weapon"
 msgstr "????????????????????"
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1133
+#: engines/scumm/metaengine.cpp:1157
 msgid "Win the bike fight cheat"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1142
+#: engines/scumm/metaengine.cpp:1166
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1144 engines/scumm/metaengine.cpp:1221
+#: engines/scumm/metaengine.cpp:1168 engines/scumm/metaengine.cpp:1245
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1149 engines/scumm/metaengine.cpp:1226
+#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1250
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1231
+#: engines/scumm/metaengine.cpp:1178 engines/scumm/metaengine.cpp:1255
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
+#: engines/scumm/metaengine.cpp:1183 engines/scumm/metaengine.cpp:1260
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1241
+#: engines/scumm/metaengine.cpp:1188 engines/scumm/metaengine.cpp:1265
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "??????????????"
 
-#: engines/scumm/metaengine.cpp:1171 engines/scumm/metaengine.cpp:1248
+#: engines/scumm/metaengine.cpp:1195 engines/scumm/metaengine.cpp:1272
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "?????????????????????????? ????????"
 
-#: engines/scumm/metaengine.cpp:1178 engines/scumm/metaengine.cpp:1255
+#: engines/scumm/metaengine.cpp:1202 engines/scumm/metaengine.cpp:1279
 #, fuzzy
 #| msgid "Slide Left"
 msgid "Stick left"
 msgstr "?????????????????????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1185 engines/scumm/metaengine.cpp:1262
+#: engines/scumm/metaengine.cpp:1209 engines/scumm/metaengine.cpp:1286
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick right"
 msgstr "?????????????????????? ??????????????"
 
-#: engines/scumm/metaengine.cpp:1192 engines/scumm/metaengine.cpp:1269
+#: engines/scumm/metaengine.cpp:1216 engines/scumm/metaengine.cpp:1293
 #, fuzzy
 #| msgid "Move left"
 msgid "Fire / select"
 msgstr "?????????????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1198
+#: engines/scumm/metaengine.cpp:1222
 #, fuzzy
 #| msgid "Open main menu"
 msgid "Walk / menu back"
 msgstr "?????????????? ???????????????? ????????"
 
-#: engines/scumm/metaengine.cpp:1203
+#: engines/scumm/metaengine.cpp:1227
 #, fuzzy
 #| msgid "Open main menu"
 msgid "Skip / menu back"
 msgstr "?????????????? ???????????????? ????????"
 
-#: engines/scumm/metaengine.cpp:1219
+#: engines/scumm/metaengine.cpp:1243
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1274
+#: engines/scumm/metaengine.cpp:1298
 #, fuzzy
 #| msgid "Move Back"
 msgid "Cover / back"
 msgstr "?????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1280
+#: engines/scumm/metaengine.cpp:1304
 #, fuzzy
 #| msgid "Skip dialogue"
 msgid "Skip / back"
 msgstr "?????????????????????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1287
+#: engines/scumm/metaengine.cpp:1311
 #, fuzzy
 #| msgid "Open main menu"
 msgid "Skip / menu"
@@ -14257,18 +14267,18 @@ msgstr ""
 "???????????????? ???????????????????? ???????????? ???????? ???????? ?????????????????? ???????????????????? ???????????????????????? ???? "
 "320x200 ???? ???????????? ????????????????????."
 
-#: engines/scumm/scumm.cpp:1362
+#: engines/scumm/scumm.cpp:1365
 #, c-format
 msgid "This game requires the '%s' Macintosh executable for its fonts."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1371
+#: engines/scumm/scumm.cpp:1374
 #, c-format
 msgid ""
 "This game requires the '%s' Macintosh executable for its music and fonts."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1375
+#: engines/scumm/scumm.cpp:1378
 #, fuzzy, c-format
 #| msgid ""
 #| "Could not find the 'Monkey Island' Macintosh executable to read the\n"
@@ -14280,25 +14290,25 @@ msgstr ""
 "???? ???????????? ???????????????? ???????????????????? ???????? 'Monkey Island' Macintosh, ??????\n"
 "?????????????????? ??????????????????????. ???????????? ?????????? ????????????????."
 
-#: engines/scumm/scumm.cpp:1376
+#: engines/scumm/scumm.cpp:1379
 msgid "The Mac GUI"
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1376
+#: engines/scumm/scumm.cpp:1379
 msgid "The music and the Mac GUI"
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1405
+#: engines/scumm/scumm.cpp:1408
 #, c-format
 msgid "Could not open Macintosh resource file %s"
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1408
+#: engines/scumm/scumm.cpp:1411
 #, c-format
 msgid "Could not find resource fork in Macintosh resource file %s"
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1415
+#: engines/scumm/scumm.cpp:1418
 #, c-format
 msgid ""
 "'%s' appears to be the wrong Dig executable. It may be the launcher one "
@@ -14307,7 +14317,7 @@ msgid ""
 "its sub-folders should be what you need."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1581
+#: engines/scumm/scumm.cpp:1584
 msgid ""
 "It appears your ScummVM version was not built with TrueType Fonts support.\n"
 "\n"
@@ -14315,7 +14325,7 @@ msgid ""
 "some of the graphics on screen will be missing."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1915
+#: engines/scumm/scumm.cpp:1918
 msgid ""
 "Audio files compressed with ScummVM Tools were detected; *.BUN/*.SOU\n"
 "compression is not supported anymore for this game, audio will be disabled.\n"
@@ -14325,7 +14335,7 @@ msgstr ""
 "?????????? ???? ?????????????????????????????? ?????? ?????????? ????????????, ?????? ?????????? ????????????????.\n"
 "???????? ??????????, ?????????????????? ???????????? ?? ???????????????????????? ?????????????????? ?????? ??????????????."
 
-#: engines/scumm/scumm.cpp:2405
+#: engines/scumm/scumm.cpp:2408
 #, c-format
 msgid ""
 "Native MIDI support requires the Roland Upgrade from LucasArts,\n"
@@ -14334,13 +14344,13 @@ msgstr ""
 "?????? ?????????????? ???????????????????????? MIDI ?????? Roland Upgrade ???? LucasArts,\n"
 "?????? ???????????? %s. ?????????????????????????????? AdLib ????????????."
 
-#: engines/scumm/scumm.cpp:2420
+#: engines/scumm/scumm.cpp:2423
 msgid ""
 "This particular version of Monkey Island 1 is known to miss some\n"
 "required resources for MT-32. Using AdLib instead."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:4361
+#: engines/scumm/scumm.cpp:4364
 msgid ""
 "Usually, Maniac Mansion would start now. But for that to work, the game "
 "files for Maniac Mansion have to be in the 'Maniac' directory inside the "
@@ -14350,7 +14360,7 @@ msgstr ""
 "Maniac Mansion ???????????? ???????? ?? ?????????? 'Maniac' ???????????? ?????????? ???????????? Day of the "
 "Tentacle, ?? ???????????? ???????????? ???????? ?????????????? ?? ScummVM."
 
-#: engines/scumm/scumm.cpp:4485
+#: engines/scumm/scumm.cpp:4488
 #, fuzzy
 msgid "Would you like to host or join a network play session?"
 msgstr "???? ???????????? ???? ???????????????? ?????? ???????????????????? ?????????????"
diff --git a/po/be_BY.po b/po/be_BY.po
index da058c3ee1d..667bb8c73a7 100644
--- a/po/be_BY.po
+++ b/po/be_BY.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-08 11:04+0000\n"
+"POT-Creation-Date: 2026-06-09 13:22+0000\n"
 "PO-Revision-Date: 2021-03-21 08:24+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Belarusian <https://translations.scummvm.org/projects/scummvm/"
@@ -356,7 +356,7 @@ msgstr "?????????? ???????????? ?????????? ????????????????. ??????????????????
 #: engines/hypno/metaengine.cpp:192 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:197
 #: engines/mm/mm1/metaengine.cpp:54 engines/parallaction/metaengine.cpp:169
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4467
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
 #: engines/sherlock/metaengine.cpp:638 engines/sherlock/metaengine.cpp:1244
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:328
@@ -375,7 +375,7 @@ msgstr "??????"
 #: engines/hypno/metaengine.cpp:198 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:204
 #: engines/mm/mm1/metaengine.cpp:52 engines/parallaction/metaengine.cpp:175
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4467
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
 #: engines/sherlock/metaengine.cpp:643 engines/sherlock/metaengine.cpp:1250
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:329
@@ -405,7 +405,7 @@ msgstr ""
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:123
 #: engines/drascula/saveload.cpp:51 engines/grim/inputdialog.cpp:83
 #: engines/parallaction/saveload.cpp:270 engines/sci/engine/kgraphics.cpp:76
-#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4479
+#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4482
 #: engines/sword1/control.cpp:2816
 msgid "OK"
 msgstr "OK"
@@ -1074,7 +1074,7 @@ msgstr "??????????????????????????"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1100
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1124
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1095,7 +1095,7 @@ msgstr "??????????"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1067
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1091
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1116,7 +1116,7 @@ msgstr "????????"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1080
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1104
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1136,7 +1136,7 @@ msgstr "????????????"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1087
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1111
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3378,7 +3378,7 @@ msgstr ""
 #: engines/mohawk/riven_stacks/aspit.cpp:454
 #: engines/neverhood/metaengine.cpp:200 engines/saga/metaengine.cpp:449
 #: engines/scumm/help.cpp:84 engines/scumm/help.cpp:86
-#: engines/scumm/scumm.cpp:4479 engines/sludge/keymapper_tables.h:95
+#: engines/scumm/scumm.cpp:4482 engines/sludge/keymapper_tables.h:95
 #: engines/sludge/keymapper_tables.h:144 engines/sludge/keymapper_tables.h:223
 #: engines/sludge/keymapper_tables.h:343 engines/sludge/keymapper_tables.h:355
 #: engines/sludge/keymapper_tables.h:416 engines/sludge/keymapper_tables.h:429
@@ -3873,7 +3873,7 @@ msgid "Game not implemented"
 msgstr "Game id ???? ??????????????????????????"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
-#: engines/scumm/metaengine.cpp:1050
+#: engines/scumm/metaengine.cpp:1074
 #, fuzzy
 #| msgid "Default game keymap"
 msgid "Default game keymappings"
@@ -5877,7 +5877,7 @@ msgstr "R2"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1208 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1232 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "????????"
 
@@ -6855,7 +6855,7 @@ msgstr ""
 msgid "Quick load or save game isn't supported in this location"
 msgstr ""
 
-#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3418
+#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3421
 #, c-format
 msgid ""
 "Failed to load saved game from file:\n"
@@ -6866,7 +6866,7 @@ msgstr ""
 "\n"
 "%s"
 
-#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3405
+#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3408
 #, c-format
 msgid ""
 "Failed to save game to file:\n"
@@ -6877,7 +6877,7 @@ msgstr ""
 "\n"
 "%s"
 
-#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3438
+#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3441
 #, c-format
 msgid ""
 "Successfully saved game in file:\n"
@@ -7382,7 +7382,7 @@ msgstr "?????????????????????? ??????"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1126 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -8137,7 +8137,7 @@ msgstr "??????????????????????????"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1113
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1137
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -10392,7 +10392,7 @@ msgid "Player ammo will never decrease."
 msgstr ""
 
 #: engines/hypno/metaengine.cpp:70 engines/scumm/metaengine.cpp:891
-#: engines/scumm/metaengine.cpp:909
+#: engines/scumm/metaengine.cpp:918
 msgid "Unlock all levels"
 msgstr ""
 
@@ -12968,7 +12968,7 @@ msgid "Your Name:"
 msgstr "??????????:"
 
 #. I18N: Button, start hosting online multiplayer game
-#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4485
+#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4488
 msgid "Host"
 msgstr ""
 
@@ -12980,7 +12980,7 @@ msgid "Querying games..."
 msgstr "???????????????? ????????????..."
 
 #. I18N: Join online multiplayer game
-#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4485
+#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4488
 msgid "Join"
 msgstr ""
 
@@ -13910,130 +13910,139 @@ msgstr "???????????????? ?????????????????????????? ?????????????? ????????????
 msgid "Run the game in 640x400 high resolution mode instead of 320x200"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:910
+#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:919
 msgid "All levels will be available without requiring passwords"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:900
+#: engines/scumm/metaengine.cpp:900 engines/scumm/metaengine.cpp:927
 #, fuzzy
 #| msgid "No date saved"
 msgid "No damage"
 msgstr "???????? ???? ????????????????"
 
-#: engines/scumm/metaengine.cpp:901
+#: engines/scumm/metaengine.cpp:901 engines/scumm/metaengine.cpp:928
 #, fuzzy
 msgid "Disable player damage"
 msgstr "???? ???????????????????? ???????????????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1061
+#: engines/scumm/metaengine.cpp:909
+#, fuzzy
+msgid "Yoda mode"
+msgstr "?????????? ??????????"
+
+#: engines/scumm/metaengine.cpp:910
+msgid "Enable original Yoda mode shortcuts, including movie mode and auto play"
+msgstr ""
+
+#: engines/scumm/metaengine.cpp:1085
 #, fuzzy
 msgid "Down left"
 msgstr "?????????????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1074
+#: engines/scumm/metaengine.cpp:1098
 #, fuzzy
 msgid "Down right"
 msgstr "?????????????? ??????????????"
 
-#: engines/scumm/metaengine.cpp:1094
+#: engines/scumm/metaengine.cpp:1118
 #, fuzzy
 #| msgid "Upper left item"
 msgid "Up left"
 msgstr "???????????? ???????? ??????????????"
 
-#: engines/scumm/metaengine.cpp:1107
+#: engines/scumm/metaengine.cpp:1131
 #, fuzzy
 msgid "Up right"
 msgstr "??????????????"
 
-#: engines/scumm/metaengine.cpp:1119
+#: engines/scumm/metaengine.cpp:1143
 #, fuzzy
 msgid "Switch weapon"
 msgstr "?????????? ??????????:"
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1133
+#: engines/scumm/metaengine.cpp:1157
 msgid "Win the bike fight cheat"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1142
+#: engines/scumm/metaengine.cpp:1166
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1144 engines/scumm/metaengine.cpp:1221
+#: engines/scumm/metaengine.cpp:1168 engines/scumm/metaengine.cpp:1245
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1149 engines/scumm/metaengine.cpp:1226
+#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1250
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1231
+#: engines/scumm/metaengine.cpp:1178 engines/scumm/metaengine.cpp:1255
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
+#: engines/scumm/metaengine.cpp:1183 engines/scumm/metaengine.cpp:1260
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1241
+#: engines/scumm/metaengine.cpp:1188 engines/scumm/metaengine.cpp:1265
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "??????????????"
 
-#: engines/scumm/metaengine.cpp:1171 engines/scumm/metaengine.cpp:1248
+#: engines/scumm/metaengine.cpp:1195 engines/scumm/metaengine.cpp:1272
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "???????????????????? ????????"
 
-#: engines/scumm/metaengine.cpp:1178 engines/scumm/metaengine.cpp:1255
+#: engines/scumm/metaengine.cpp:1202 engines/scumm/metaengine.cpp:1279
 #, fuzzy
 #| msgid "Slide Left"
 msgid "Stick left"
 msgstr "???????????????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1185 engines/scumm/metaengine.cpp:1262
+#: engines/scumm/metaengine.cpp:1209 engines/scumm/metaengine.cpp:1286
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick right"
 msgstr "???????????????? ??????????????"
 
-#: engines/scumm/metaengine.cpp:1192 engines/scumm/metaengine.cpp:1269
+#: engines/scumm/metaengine.cpp:1216 engines/scumm/metaengine.cpp:1293
 #, fuzzy
 #| msgid "Move left"
 msgid "Fire / select"
 msgstr "?????????????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1198
+#: engines/scumm/metaengine.cpp:1222
 #, fuzzy
 #| msgid "Open main menu"
 msgid "Walk / menu back"
 msgstr "?????????????? ???????????????? ????????"
 
-#: engines/scumm/metaengine.cpp:1203
+#: engines/scumm/metaengine.cpp:1227
 #, fuzzy
 #| msgid "Open main menu"
 msgid "Skip / menu back"
 msgstr "?????????????? ???????????????? ????????"
 
-#: engines/scumm/metaengine.cpp:1219
+#: engines/scumm/metaengine.cpp:1243
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1274
+#: engines/scumm/metaengine.cpp:1298
 #, fuzzy
 #| msgid "Move Back"
 msgid "Cover / back"
 msgstr "???????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1280
+#: engines/scumm/metaengine.cpp:1304
 #, fuzzy
 msgid "Skip / back"
 msgstr "???????????????????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1287
+#: engines/scumm/metaengine.cpp:1311
 #, fuzzy
 #| msgid "Open main menu"
 msgid "Skip / menu"
@@ -14061,18 +14070,18 @@ msgid ""
 "320x200, under 'engine' tab."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1362
+#: engines/scumm/scumm.cpp:1365
 #, c-format
 msgid "This game requires the '%s' Macintosh executable for its fonts."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1371
+#: engines/scumm/scumm.cpp:1374
 #, c-format
 msgid ""
 "This game requires the '%s' Macintosh executable for its music and fonts."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1375
+#: engines/scumm/scumm.cpp:1378
 #, fuzzy, c-format
 #| msgid ""
 #| "Could not find the 'Monkey Island' Macintosh executable to read the\n"
@@ -14085,25 +14094,25 @@ msgstr ""
 "??????????????????\n"
 "???????????????? ?????? ??????????????????????. ???????????? ?????????? ??????????????????."
 
-#: engines/scumm/scumm.cpp:1376
+#: engines/scumm/scumm.cpp:1379
 msgid "The Mac GUI"
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1376
+#: engines/scumm/scumm.cpp:1379
 msgid "The music and the Mac GUI"
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1405
+#: engines/scumm/scumm.cpp:1408
 #, c-format
 msgid "Could not open Macintosh resource file %s"
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1408
+#: engines/scumm/scumm.cpp:1411
 #, c-format
 msgid "Could not find resource fork in Macintosh resource file %s"
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1415
+#: engines/scumm/scumm.cpp:1418
 #, c-format
 msgid ""
 "'%s' appears to be the wrong Dig executable. It may be the launcher one "
@@ -14112,7 +14121,7 @@ msgid ""
 "its sub-folders should be what you need."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1581
+#: engines/scumm/scumm.cpp:1584
 msgid ""
 "It appears your ScummVM version was not built with TrueType Fonts support.\n"
 "\n"
@@ -14120,14 +14129,14 @@ msgid ""
 "some of the graphics on screen will be missing."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1915
+#: engines/scumm/scumm.cpp:1918
 msgid ""
 "Audio files compressed with ScummVM Tools were detected; *.BUN/*.SOU\n"
 "compression is not supported anymore for this game, audio will be disabled.\n"
 "Please copy the game from the original media without compression."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:2405
+#: engines/scumm/scumm.cpp:2408
 #, c-format
 msgid ""
 "Native MIDI support requires the Roland Upgrade from LucasArts,\n"
@@ -14136,13 +14145,13 @@ msgstr ""
 "?????????? \"??????????????\" MIDI ???????????????? ???????????????????? Roland Upgrade ????\n"
 "LucasArts, ?????? ???????????? %s. ???????????????????????? ???? AdLib."
 
-#: engines/scumm/scumm.cpp:2420
+#: engines/scumm/scumm.cpp:2423
 msgid ""
 "This particular version of Monkey Island 1 is known to miss some\n"
 "required resources for MT-32. Using AdLib instead."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:4361
+#: engines/scumm/scumm.cpp:4364
 msgid ""
 "Usually, Maniac Mansion would start now. But for that to work, the game "
 "files for Maniac Mansion have to be in the 'Maniac' directory inside the "
@@ -14153,7 +14162,7 @@ msgstr ""
 "???????????? ???????????????????? ???????????? Tentacle ?? ???????? ???????????? ?????????????? ???????? ?????????????????? ?? "
 "ScummVM."
 
-#: engines/scumm/scumm.cpp:4485
+#: engines/scumm/scumm.cpp:4488
 #, fuzzy
 msgid "Would you like to host or join a network play session?"
 msgstr "???? ???? ???????????? ???????????????? ?????? ?????????????????? ?????????????"
diff --git a/po/ca_ES.po b/po/ca_ES.po
index ae0476cfb19..098ad4b46e9 100644
--- a/po/ca_ES.po
+++ b/po/ca_ES.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.6.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-08 11:04+0000\n"
+"POT-Creation-Date: 2026-06-09 13:22+0000\n"
 "PO-Revision-Date: 2026-05-26 04:22+0000\n"
 "Last-Translator: Xaviu <xaviu at xaviu.com>\n"
 "Language-Team: Catalan <https://translations.scummvm.org/projects/scummvm/"
@@ -341,7 +341,7 @@ msgstr "Un altre Emmagatzematge est?? actiu ara mateix. Voleu interrompre'l?"
 #: engines/hypno/metaengine.cpp:192 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:197
 #: engines/mm/mm1/metaengine.cpp:54 engines/parallaction/metaengine.cpp:169
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4467
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
 #: engines/sherlock/metaengine.cpp:638 engines/sherlock/metaengine.cpp:1244
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:328
@@ -360,7 +360,7 @@ msgstr "S??"
 #: engines/hypno/metaengine.cpp:198 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:204
 #: engines/mm/mm1/metaengine.cpp:52 engines/parallaction/metaengine.cpp:175
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4467
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
 #: engines/sherlock/metaengine.cpp:643 engines/sherlock/metaengine.cpp:1250
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:329
@@ -388,7 +388,7 @@ msgstr "El contingut del codi JSON est?? mal format."
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:123
 #: engines/drascula/saveload.cpp:51 engines/grim/inputdialog.cpp:83
 #: engines/parallaction/saveload.cpp:270 engines/sci/engine/kgraphics.cpp:76
-#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4479
+#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4482
 #: engines/sword1/control.cpp:2816
 msgid "OK"
 msgstr "D'acord"
@@ -1057,7 +1057,7 @@ msgstr "Interaccionar"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1100
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1124
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1078,7 +1078,7 @@ msgstr "Amunt"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1067
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1091
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1099,7 +1099,7 @@ msgstr "Avall"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1080
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1104
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1119,7 +1119,7 @@ msgstr "Esquerra"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1087
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1111
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3294,7 +3294,7 @@ msgstr "Motor de jocs d'aventura gr??fica"
 #: engines/mohawk/riven_stacks/aspit.cpp:454
 #: engines/neverhood/metaengine.cpp:200 engines/saga/metaengine.cpp:449
 #: engines/scumm/help.cpp:84 engines/scumm/help.cpp:86
-#: engines/scumm/scumm.cpp:4479 engines/sludge/keymapper_tables.h:95
+#: engines/scumm/scumm.cpp:4482 engines/sludge/keymapper_tables.h:95
 #: engines/sludge/keymapper_tables.h:144 engines/sludge/keymapper_tables.h:223
 #: engines/sludge/keymapper_tables.h:343 engines/sludge/keymapper_tables.h:355
 #: engines/sludge/keymapper_tables.h:416 engines/sludge/keymapper_tables.h:429
@@ -3796,7 +3796,7 @@ msgid "Game not implemented"
 msgstr "Joc no implementat"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
-#: engines/scumm/metaengine.cpp:1050
+#: engines/scumm/metaengine.cpp:1074
 msgid "Default game keymappings"
 msgstr "Assignaci?? de tecles per defecte del joc"
 
@@ -5679,7 +5679,7 @@ msgstr "R2"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1208 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1232 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "Men??"
 
@@ -6629,7 +6629,7 @@ msgstr ""
 "La c??rrega r??pida o desament de la partida no est?? s'admet en aquesta "
 "ubicaci??"
 
-#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3418
+#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3421
 #, c-format
 msgid ""
 "Failed to load saved game from file:\n"
@@ -6640,7 +6640,7 @@ msgstr ""
 "\n"
 "%s"
 
-#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3405
+#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3408
 #, c-format
 msgid ""
 "Failed to save game to file:\n"
@@ -6651,7 +6651,7 @@ msgstr ""
 "\n"
 "%s"
 
-#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3438
+#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3441
 #, c-format
 msgid ""
 "Successfully saved game in file:\n"
@@ -7144,7 +7144,7 @@ msgstr "Activa / desactiva el combat"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1126 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -7812,7 +7812,7 @@ msgstr "Parla / Interactua"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1113
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1137
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -9781,7 +9781,7 @@ msgid "Player ammo will never decrease."
 msgstr "La munici?? del jugador mai disminuir??."
 
 #: engines/hypno/metaengine.cpp:70 engines/scumm/metaengine.cpp:891
-#: engines/scumm/metaengine.cpp:909
+#: engines/scumm/metaengine.cpp:918
 msgid "Unlock all levels"
 msgstr "Desbloqueja tots els nivells"
 
@@ -12065,7 +12065,7 @@ msgid "Your Name:"
 msgstr "El vostre nom:"
 
 #. I18N: Button, start hosting online multiplayer game
-#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4485
+#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4488
 msgid "Host"
 msgstr ""
 
@@ -12075,7 +12075,7 @@ msgid "Querying games..."
 msgstr "S'estan cercant partides..."
 
 #. I18N: Join online multiplayer game
-#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4485
+#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4488
 msgid "Join"
 msgstr ""
 
@@ -12981,128 +12981,138 @@ msgstr "Alta resoluci?? activada"
 msgid "Run the game in 640x400 high resolution mode instead of 320x200"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:910
+#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:919
 #, fuzzy
 #| msgid "All levels will be available to play."
 msgid "All levels will be available without requiring passwords"
 msgstr "Tots els nivells estaran disponibles per jugar."
 
-#: engines/scumm/metaengine.cpp:900
+#: engines/scumm/metaengine.cpp:900 engines/scumm/metaengine.cpp:927
 #, fuzzy
 #| msgid "No date saved"
 msgid "No damage"
 msgstr "No hi ha data desada"
 
-#: engines/scumm/metaengine.cpp:901
+#: engines/scumm/metaengine.cpp:901 engines/scumm/metaengine.cpp:928
 #, fuzzy
 #| msgid "Disable save menu"
 msgid "Disable player damage"
 msgstr "Desactiva el men?? de desar"
 
-#: engines/scumm/metaengine.cpp:1061
+#: engines/scumm/metaengine.cpp:909
+#, fuzzy
+#| msgid "Fast mode"
+msgid "Yoda mode"
+msgstr "Mode r??pid"
+
+#: engines/scumm/metaengine.cpp:910
+msgid "Enable original Yoda mode shortcuts, including movie mode and auto play"
+msgstr ""
+
+#: engines/scumm/metaengine.cpp:1085
 msgid "Down left"
 msgstr "A baix a l'esquerra"
 
-#: engines/scumm/metaengine.cpp:1074
+#: engines/scumm/metaengine.cpp:1098
 msgid "Down right"
 msgstr "A baix a la dreta"
 
-#: engines/scumm/metaengine.cpp:1094
+#: engines/scumm/metaengine.cpp:1118
 msgid "Up left"
 msgstr "Amunt a l'esquerra"
 
-#: engines/scumm/metaengine.cpp:1107
+#: engines/scumm/metaengine.cpp:1131
 msgid "Up right"
 msgstr "Amunt a la dreta"
 
-#: engines/scumm/metaengine.cpp:1119
+#: engines/scumm/metaengine.cpp:1143
 msgid "Switch weapon"
 msgstr "Canvia d'arma"
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1133
+#: engines/scumm/metaengine.cpp:1157
 msgid "Win the bike fight cheat"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1142
+#: engines/scumm/metaengine.cpp:1166
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1144 engines/scumm/metaengine.cpp:1221
+#: engines/scumm/metaengine.cpp:1168 engines/scumm/metaengine.cpp:1245
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1149 engines/scumm/metaengine.cpp:1226
+#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1250
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1231
+#: engines/scumm/metaengine.cpp:1178 engines/scumm/metaengine.cpp:1255
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
+#: engines/scumm/metaengine.cpp:1183 engines/scumm/metaengine.cpp:1260
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1241
+#: engines/scumm/metaengine.cpp:1188 engines/scumm/metaengine.cpp:1265
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "Recull"
 
-#: engines/scumm/metaengine.cpp:1171 engines/scumm/metaengine.cpp:1248
+#: engines/scumm/metaengine.cpp:1195 engines/scumm/metaengine.cpp:1272
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "Despla??ament avall"
 
-#: engines/scumm/metaengine.cpp:1178 engines/scumm/metaengine.cpp:1255
+#: engines/scumm/metaengine.cpp:1202 engines/scumm/metaengine.cpp:1279
 #, fuzzy
 #| msgid "Slide left"
 msgid "Stick left"
 msgstr "Llisca cap a l'esquerra"
 
-#: engines/scumm/metaengine.cpp:1185 engines/scumm/metaengine.cpp:1262
+#: engines/scumm/metaengine.cpp:1209 engines/scumm/metaengine.cpp:1286
 #, fuzzy
 #| msgid "Slide right"
 msgid "Stick right"
 msgstr "Llisca cap a la dreta"
 
-#: engines/scumm/metaengine.cpp:1192 engines/scumm/metaengine.cpp:1269
+#: engines/scumm/metaengine.cpp:1216 engines/scumm/metaengine.cpp:1293
 #, fuzzy
 #| msgid "Move / Select"
 msgid "Fire / select"
 msgstr "Mou / Selecciona"
 
-#: engines/scumm/metaengine.cpp:1198
+#: engines/scumm/metaengine.cpp:1222
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Walk / menu back"
 msgstr "Omet el men?? principal"
 
-#: engines/scumm/metaengine.cpp:1203
+#: engines/scumm/metaengine.cpp:1227
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu back"
 msgstr "Omet el men?? principal"
 
-#: engines/scumm/metaengine.cpp:1219
+#: engines/scumm/metaengine.cpp:1243
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1274
+#: engines/scumm/metaengine.cpp:1298
 #, fuzzy
 #| msgid "Move back"
 msgid "Cover / back"
 msgstr "Mou enrere"
 
-#: engines/scumm/metaengine.cpp:1280
+#: engines/scumm/metaengine.cpp:1304
 #, fuzzy
 #| msgid "Skip walk"
 msgid "Skip / back"
 msgstr "Omet el despla??ament"
 
-#: engines/scumm/metaengine.cpp:1287
+#: engines/scumm/metaengine.cpp:1311
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu"
@@ -13130,43 +13140,43 @@ msgid ""
 "320x200, under 'engine' tab."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1362
+#: engines/scumm/scumm.cpp:1365
 #, c-format
 msgid "This game requires the '%s' Macintosh executable for its fonts."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1371
+#: engines/scumm/scumm.cpp:1374
 #, c-format
 msgid ""
 "This game requires the '%s' Macintosh executable for its music and fonts."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1375
+#: engines/scumm/scumm.cpp:1378
 #, c-format
 msgid ""
 "Could not find the '%s' Macintosh executable to read resources from. %s will "
 "be disabled."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1376
+#: engines/scumm/scumm.cpp:1379
 msgid "The Mac GUI"
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1376
+#: engines/scumm/scumm.cpp:1379
 msgid "The music and the Mac GUI"
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1405
+#: engines/scumm/scumm.cpp:1408
 #, c-format
 msgid "Could not open Macintosh resource file %s"
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1408
+#: engines/scumm/scumm.cpp:1411
 #, c-format
 msgid "Could not find resource fork in Macintosh resource file %s"
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1415
+#: engines/scumm/scumm.cpp:1418
 #, c-format
 msgid ""
 "'%s' appears to be the wrong Dig executable. It may be the launcher one "
@@ -13175,7 +13185,7 @@ msgid ""
 "its sub-folders should be what you need."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1581
+#: engines/scumm/scumm.cpp:1584
 msgid ""
 "It appears your ScummVM version was not built with TrueType Fonts support.\n"
 "\n"
@@ -13183,14 +13193,14 @@ msgid ""
 "some of the graphics on screen will be missing."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1915
+#: engines/scumm/scumm.cpp:1918
 msgid ""
 "Audio files compressed with ScummVM Tools were detected; *.BUN/*.SOU\n"
 "compression is not supported anymore for this game, audio will be disabled.\n"
 "Please copy the game from the original media without compression."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:2405
+#: engines/scumm/scumm.cpp:2408
 #, c-format
 msgid ""
 "Native MIDI support requires the Roland Upgrade from LucasArts,\n"
@@ -13199,13 +13209,13 @@ msgstr ""
 "El suport de MIDI natiu requereix l'actualitzaci?? Roland de LucasArts,\n"
 "per?? no s'ha trobat %s. S'utilitzar?? AdLib."
 
-#: engines/scumm/scumm.cpp:2420
+#: engines/scumm/scumm.cpp:2423
 msgid ""
 "This particular version of Monkey Island 1 is known to miss some\n"
 "required resources for MT-32. Using AdLib instead."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:4361
+#: engines/scumm/scumm.cpp:4364
 msgid ""
 "Usually, Maniac Mansion would start now. But for that to work, the game "
 "files for Maniac Mansion have to be in the 'Maniac' directory inside the "
@@ -13216,7 +13226,7 @@ msgstr ""
 "'Maniac' dins del directori del joc Tentacle, i el joc s'ha d'afegir a "
 "ScummVM."
 
-#: engines/scumm/scumm.cpp:4485
+#: engines/scumm/scumm.cpp:4488
 msgid "Would you like to host or join a network play session?"
 msgstr "Voleu crear o unir-vos a una sessi?? de joc en xarxa?"
 
diff --git a/po/cs_CZ.po b/po/cs_CZ.po
index 07e9b697706..5e1f37a8f40 100644
--- a/po/cs_CZ.po
+++ b/po/cs_CZ.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.7.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-08 11:04+0000\n"
+"POT-Creation-Date: 2026-06-09 13:22+0000\n"
 "PO-Revision-Date: 2026-02-09 20:22+0000\n"
 "Last-Translator: AsciiWolf <mail at asciiwolf.com>\n"
 "Language-Team: Czech <https://translations.scummvm.org/projects/scummvm/"
@@ -357,7 +357,7 @@ msgstr "Jin?? ??lo??i??t?? pr??v?? pracuje. Chcete ho p??eru??it?"
 #: engines/hypno/metaengine.cpp:192 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:197
 #: engines/mm/mm1/metaengine.cpp:54 engines/parallaction/metaengine.cpp:169
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4467
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
 #: engines/sherlock/metaengine.cpp:638 engines/sherlock/metaengine.cpp:1244
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:328
@@ -376,7 +376,7 @@ msgstr "Ano"
 #: engines/hypno/metaengine.cpp:198 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:204
 #: engines/mm/mm1/metaengine.cpp:52 engines/parallaction/metaengine.cpp:175
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4467
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
 #: engines/sherlock/metaengine.cpp:643 engines/sherlock/metaengine.cpp:1250
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:329
@@ -408,7 +408,7 @@ msgstr ""
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:123
 #: engines/drascula/saveload.cpp:51 engines/grim/inputdialog.cpp:83
 #: engines/parallaction/saveload.cpp:270 engines/sci/engine/kgraphics.cpp:76
-#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4479
+#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4482
 #: engines/sword1/control.cpp:2816
 msgid "OK"
 msgstr "OK"
@@ -1074,7 +1074,7 @@ msgstr "Interakce"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1100
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1124
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1095,7 +1095,7 @@ msgstr "Nahoru"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1067
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1091
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1116,7 +1116,7 @@ msgstr "Dol??"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1080
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1104
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1136,7 +1136,7 @@ msgstr "Doleva"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1087
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1111
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3359,7 +3359,7 @@ msgstr "Grafick?? engine pro hry ????nru adventur"
 #: engines/mohawk/riven_stacks/aspit.cpp:454
 #: engines/neverhood/metaengine.cpp:200 engines/saga/metaengine.cpp:449
 #: engines/scumm/help.cpp:84 engines/scumm/help.cpp:86
-#: engines/scumm/scumm.cpp:4479 engines/sludge/keymapper_tables.h:95
+#: engines/scumm/scumm.cpp:4482 engines/sludge/keymapper_tables.h:95
 #: engines/sludge/keymapper_tables.h:144 engines/sludge/keymapper_tables.h:223
 #: engines/sludge/keymapper_tables.h:343 engines/sludge/keymapper_tables.h:355
 #: engines/sludge/keymapper_tables.h:416 engines/sludge/keymapper_tables.h:429
@@ -3861,7 +3861,7 @@ msgid "Game not implemented"
 msgstr "Id hry nen?? podporov??no"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
-#: engines/scumm/metaengine.cpp:1050
+#: engines/scumm/metaengine.cpp:1074
 #, fuzzy
 #| msgid "Default game keymap"
 msgid "Default game keymappings"
@@ -5845,7 +5845,7 @@ msgstr ""
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1208 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1232 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "Menu"
 
@@ -6819,7 +6819,7 @@ msgstr ""
 msgid "Quick load or save game isn't supported in this location"
 msgstr ""
 
-#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3418
+#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3421
 #, c-format
 msgid ""
 "Failed to load saved game from file:\n"
@@ -6830,7 +6830,7 @@ msgstr ""
 "\n"
 "%s"
 
-#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3405
+#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3408
 #, c-format
 msgid ""
 "Failed to save game to file:\n"
@@ -6841,7 +6841,7 @@ msgstr ""
 "\n"
 "%s"
 
-#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3438
+#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3441
 #, c-format
 msgid ""
 "Successfully saved game in file:\n"
@@ -7336,7 +7336,7 @@ msgstr "Povolit zachycov??n?? my??i"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1126 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -8081,7 +8081,7 @@ msgstr "Interakce"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1113
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1137
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -10281,7 +10281,7 @@ msgid "Player ammo will never decrease."
 msgstr ""
 
 #: engines/hypno/metaengine.cpp:70 engines/scumm/metaengine.cpp:891
-#: engines/scumm/metaengine.cpp:909
+#: engines/scumm/metaengine.cpp:918
 msgid "Unlock all levels"
 msgstr ""
 
@@ -12778,7 +12778,7 @@ msgid "Your Name:"
 msgstr "Jm??no:"
 
 #. I18N: Button, start hosting online multiplayer game
-#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4485
+#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4488
 msgid "Host"
 msgstr ""
 
@@ -12790,7 +12790,7 @@ msgid "Querying games..."
 msgstr "Ukl??d??n?? hry..."
 
 #. I18N: Join online multiplayer game
-#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4485
+#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4488
 msgid "Join"
 msgstr ""
 
@@ -13720,126 +13720,135 @@ msgstr "Povolit grafiku ve vysok??m rozli??en??"
 msgid "Run the game in 640x400 high resolution mode instead of 320x200"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:910
+#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:919
 msgid "All levels will be available without requiring passwords"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:900
+#: engines/scumm/metaengine.cpp:900 engines/scumm/metaengine.cpp:927
 #, fuzzy
 #| msgid "No date saved"
 msgid "No damage"
 msgstr "Neulo??ena ????dn?? data"
 
-#: engines/scumm/metaengine.cpp:901
+#: engines/scumm/metaengine.cpp:901 engines/scumm/metaengine.cpp:928
 #, fuzzy
 msgid "Disable player damage"
 msgstr "Nelze ulo??it hru"
 
-#: engines/scumm/metaengine.cpp:1061
+#: engines/scumm/metaengine.cpp:909
+#, fuzzy
+msgid "Yoda mode"
+msgstr "Rychl?? re??im"
+
+#: engines/scumm/metaengine.cpp:910
+msgid "Enable original Yoda mode shortcuts, including movie mode and auto play"
+msgstr ""
+
+#: engines/scumm/metaengine.cpp:1085
 #, fuzzy
 msgid "Down left"
 msgstr "Oto??it se doleva"
 
-#: engines/scumm/metaengine.cpp:1074
+#: engines/scumm/metaengine.cpp:1098
 #, fuzzy
 msgid "Down right"
 msgstr "Oto??it se doprava"
 
-#: engines/scumm/metaengine.cpp:1094
+#: engines/scumm/metaengine.cpp:1118
 #, fuzzy
 #| msgid "Upper left item"
 msgid "Up left"
 msgstr "Polo??ka vlevo naho??e"
 
-#: engines/scumm/metaengine.cpp:1107
+#: engines/scumm/metaengine.cpp:1131
 #, fuzzy
 msgid "Up right"
 msgstr "Doprava"
 
-#: engines/scumm/metaengine.cpp:1119
+#: engines/scumm/metaengine.cpp:1143
 #, fuzzy
 msgid "Switch weapon"
 msgstr "M??n??n?? postav:"
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1133
+#: engines/scumm/metaengine.cpp:1157
 msgid "Win the bike fight cheat"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1142
+#: engines/scumm/metaengine.cpp:1166
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1144 engines/scumm/metaengine.cpp:1221
+#: engines/scumm/metaengine.cpp:1168 engines/scumm/metaengine.cpp:1245
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1149 engines/scumm/metaengine.cpp:1226
+#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1250
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1231
+#: engines/scumm/metaengine.cpp:1178 engines/scumm/metaengine.cpp:1255
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
+#: engines/scumm/metaengine.cpp:1183 engines/scumm/metaengine.cpp:1260
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1241
+#: engines/scumm/metaengine.cpp:1188 engines/scumm/metaengine.cpp:1265
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "Sebrat"
 
-#: engines/scumm/metaengine.cpp:1171 engines/scumm/metaengine.cpp:1248
+#: engines/scumm/metaengine.cpp:1195 engines/scumm/metaengine.cpp:1272
 #, fuzzy
 msgid "Stick down"
 msgstr "Posunout seznam dolu"
 
-#: engines/scumm/metaengine.cpp:1178 engines/scumm/metaengine.cpp:1255
+#: engines/scumm/metaengine.cpp:1202 engines/scumm/metaengine.cpp:1279
 #, fuzzy
 #| msgid "Slide Left"
 msgid "Stick left"
 msgstr "P??esunout se Doleva"
 
-#: engines/scumm/metaengine.cpp:1185 engines/scumm/metaengine.cpp:1262
+#: engines/scumm/metaengine.cpp:1209 engines/scumm/metaengine.cpp:1286
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick right"
 msgstr "P??esunout se Doprava"
 
-#: engines/scumm/metaengine.cpp:1192 engines/scumm/metaengine.cpp:1269
+#: engines/scumm/metaengine.cpp:1216 engines/scumm/metaengine.cpp:1293
 #, fuzzy
 msgid "Fire / select"
 msgstr "P??esunout se doleva"
 
-#: engines/scumm/metaengine.cpp:1198
+#: engines/scumm/metaengine.cpp:1222
 #, fuzzy
 msgid "Walk / menu back"
 msgstr "~N~??vrat do Spou??t????e"
 
-#: engines/scumm/metaengine.cpp:1203
+#: engines/scumm/metaengine.cpp:1227
 #, fuzzy
 msgid "Skip / menu back"
 msgstr "~N~??vrat do Spou??t????e"
 
-#: engines/scumm/metaengine.cpp:1219
+#: engines/scumm/metaengine.cpp:1243
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1274
+#: engines/scumm/metaengine.cpp:1298
 #, fuzzy
 #| msgid "Move Back"
 msgid "Cover / back"
 msgstr "Vzad"
 
-#: engines/scumm/metaengine.cpp:1280
+#: engines/scumm/metaengine.cpp:1304
 #, fuzzy
 msgid "Skip / back"
 msgstr "P??esko??it ????dek"
 
-#: engines/scumm/metaengine.cpp:1287
+#: engines/scumm/metaengine.cpp:1311
 #, fuzzy
 msgid "Skip / menu"
 msgstr "~N~??vrat do Spou??t????e"
@@ -13866,18 +13875,18 @@ msgid ""
 "320x200, under 'engine' tab."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1362
+#: engines/scumm/scumm.cpp:1365
 #, c-format
 msgid "This game requires the '%s' Macintosh executable for its fonts."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1371
+#: engines/scumm/scumm.cpp:1374
 #, c-format
 msgid ""
 "This game requires the '%s' Macintosh executable for its music and fonts."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1375
+#: engines/scumm/scumm.cpp:1378
 #, fuzzy, c-format
 #| msgid ""
 #| "Could not find the 'Monkey Island' Macintosh executable to read the\n"
@@ -13889,25 +13898,25 @@ msgstr ""
 "Nelze naj??t spustiteln?? soubor 'Monkey Island' pro Macintosh z\n"
 "jeho?? maj?? b??t na??teny hudebn?? n??stroje. Hudba bude zak??z??na."
 
-#: engines/scumm/scumm.cpp:1376
+#: engines/scumm/scumm.cpp:1379
 msgid "The Mac GUI"
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1376
+#: engines/scumm/scumm.cpp:1379
 msgid "The music and the Mac GUI"
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1405
+#: engines/scumm/scumm.cpp:1408
 #, c-format
 msgid "Could not open Macintosh resource file %s"
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1408
+#: engines/scumm/scumm.cpp:1411
 #, c-format
 msgid "Could not find resource fork in Macintosh resource file %s"
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1415
+#: engines/scumm/scumm.cpp:1418
 #, c-format
 msgid ""
 "'%s' appears to be the wrong Dig executable. It may be the launcher one "
@@ -13916,7 +13925,7 @@ msgid ""
 "its sub-folders should be what you need."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1581
+#: engines/scumm/scumm.cpp:1584
 msgid ""
 "It appears your ScummVM version was not built with TrueType Fonts support.\n"
 "\n"
@@ -13924,14 +13933,14 @@ msgid ""
 "some of the graphics on screen will be missing."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1915
+#: engines/scumm/scumm.cpp:1918
 msgid ""
 "Audio files compressed with ScummVM Tools were detected; *.BUN/*.SOU\n"
 "compression is not supported anymore for this game, audio will be disabled.\n"
 "Please copy the game from the original media without compression."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:2405
+#: engines/scumm/scumm.cpp:2408
 #, c-format
 msgid ""
 "Native MIDI support requires the Roland Upgrade from LucasArts,\n"
@@ -13940,13 +13949,13 @@ msgstr ""
 "P??irozen?? podpora MIDI vy??aduje Aktualizaci Roland od LucasArts,\n"
 "ale %s chyb??. M??sto toho je pou??it AdLib."
 
-#: engines/scumm/scumm.cpp:2420
+#: engines/scumm/scumm.cpp:2423
 msgid ""
 "This particular version of Monkey Island 1 is known to miss some\n"
 "required resources for MT-32. Using AdLib instead."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:4361
+#: engines/scumm/scumm.cpp:4364
 msgid ""
 "Usually, Maniac Mansion would start now. But for that to work, the game "
 "files for Maniac Mansion have to be in the 'Maniac' directory inside the "
@@ -13956,7 +13965,7 @@ msgstr ""
 "mus?? b??t soubory se hrou um??st??ny do slo??ky 'Maniac' uvnit?? slo??ky se hrou "
 "Tentacle a hra mus?? b??t p??id??na do ScummVM."
 
-#: engines/scumm/scumm.cpp:4485
+#: engines/scumm/scumm.cpp:4488
 #, fuzzy
 msgid "Would you like to host or join a network play session?"
 msgstr "Chcete hru nahr??t nebo ulo??it?"
diff --git a/po/da.po b/po/da.po
index f9b285b0891..6698d8f54c7 100644
--- a/po/da.po
+++ b/po/da.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-08 11:04+0000\n"
+"POT-Creation-Date: 2026-06-09 13:22+0000\n"
 "PO-Revision-Date: 2023-09-07 17:04+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Danish <https://translations.scummvm.org/projects/scummvm/"
@@ -358,7 +358,7 @@ msgstr "Et andet lager er aktivt nu. Vil du afbryde det?"
 #: engines/hypno/metaengine.cpp:192 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:197
 #: engines/mm/mm1/metaengine.cpp:54 engines/parallaction/metaengine.cpp:169
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4467
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
 #: engines/sherlock/metaengine.cpp:638 engines/sherlock/metaengine.cpp:1244
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:328
@@ -377,7 +377,7 @@ msgstr "Ja"
 #: engines/hypno/metaengine.cpp:198 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:204
 #: engines/mm/mm1/metaengine.cpp:52 engines/parallaction/metaengine.cpp:175
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4467
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
 #: engines/sherlock/metaengine.cpp:643 engines/sherlock/metaengine.cpp:1250
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:329
@@ -407,7 +407,7 @@ msgstr ""
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:123
 #: engines/drascula/saveload.cpp:51 engines/grim/inputdialog.cpp:83
 #: engines/parallaction/saveload.cpp:270 engines/sci/engine/kgraphics.cpp:76
-#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4479
+#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4482
 #: engines/sword1/control.cpp:2816
 msgid "OK"
 msgstr "OK"
@@ -1077,7 +1077,7 @@ msgstr "interagere"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1100
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1124
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1098,7 +1098,7 @@ msgstr "Op"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1067
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1091
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1119,7 +1119,7 @@ msgstr "Ned"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1080
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1104
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1139,7 +1139,7 @@ msgstr "Venstre"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1087
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1111
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3376,7 +3376,7 @@ msgstr ""
 #: engines/mohawk/riven_stacks/aspit.cpp:454
 #: engines/neverhood/metaengine.cpp:200 engines/saga/metaengine.cpp:449
 #: engines/scumm/help.cpp:84 engines/scumm/help.cpp:86
-#: engines/scumm/scumm.cpp:4479 engines/sludge/keymapper_tables.h:95
+#: engines/scumm/scumm.cpp:4482 engines/sludge/keymapper_tables.h:95
 #: engines/sludge/keymapper_tables.h:144 engines/sludge/keymapper_tables.h:223
 #: engines/sludge/keymapper_tables.h:343 engines/sludge/keymapper_tables.h:355
 #: engines/sludge/keymapper_tables.h:416 engines/sludge/keymapper_tables.h:429
@@ -3872,7 +3872,7 @@ msgid "Game not implemented"
 msgstr "Spil-id ikke underst??ttet"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
-#: engines/scumm/metaengine.cpp:1050
+#: engines/scumm/metaengine.cpp:1074
 #, fuzzy
 msgid "Default game keymappings"
 msgstr "~I~ndstillinger ..."
@@ -5864,7 +5864,7 @@ msgstr ""
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1208 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1232 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "Menu"
 
@@ -6833,7 +6833,7 @@ msgstr ""
 msgid "Quick load or save game isn't supported in this location"
 msgstr ""
 
-#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3418
+#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3421
 #, c-format
 msgid ""
 "Failed to load saved game from file:\n"
@@ -6844,7 +6844,7 @@ msgstr ""
 "\n"
 "%s"
 
-#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3405
+#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3408
 #, c-format
 msgid ""
 "Failed to save game to file:\n"
@@ -6855,7 +6855,7 @@ msgstr ""
 "\n"
 "%s"
 
-#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3438
+#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3441
 #, c-format
 msgid ""
 "Successfully saved game in file:\n"
@@ -7363,7 +7363,7 @@ msgstr "Museindfangning til/fra"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1126 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -8108,7 +8108,7 @@ msgstr "interagere"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1113
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1137
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -10302,7 +10302,7 @@ msgid "Player ammo will never decrease."
 msgstr ""
 
 #: engines/hypno/metaengine.cpp:70 engines/scumm/metaengine.cpp:891
-#: engines/scumm/metaengine.cpp:909
+#: engines/scumm/metaengine.cpp:918
 msgid "Unlock all levels"
 msgstr ""
 
@@ -12869,7 +12869,7 @@ msgid "Your Name:"
 msgstr "Navn:"
 
 #. I18N: Button, start hosting online multiplayer game
-#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4485
+#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4488
 msgid "Host"
 msgstr ""
 
@@ -12881,7 +12881,7 @@ msgid "Querying games..."
 msgstr "Gemmer spil ..."
 
 #. I18N: Join online multiplayer game
-#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4485
+#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4488
 msgid "Join"
 msgstr ""
 
@@ -13811,126 +13811,135 @@ msgstr "Aktiv??r grafik i h??j opl??sning"
 msgid "Run the game in 640x400 high resolution mode instead of 320x200"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:910
+#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:919
 msgid "All levels will be available without requiring passwords"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:900
+#: engines/scumm/metaengine.cpp:900 engines/scumm/metaengine.cpp:927
 #, fuzzy
 #| msgid "No date saved"
 msgid "No damage"
 msgstr "Ingen dato gemt"
 
-#: engines/scumm/metaengine.cpp:901
+#: engines/scumm/metaengine.cpp:901 engines/scumm/metaengine.cpp:928
 #, fuzzy
 msgid "Disable player damage"
 msgstr "Kunne ikke gemme spil"
 
-#: engines/scumm/metaengine.cpp:1061
+#: engines/scumm/metaengine.cpp:909
+#, fuzzy
+msgid "Yoda mode"
+msgstr "Hurtigtilstand"
+
+#: engines/scumm/metaengine.cpp:910
+msgid "Enable original Yoda mode shortcuts, including movie mode and auto play"
+msgstr ""
+
+#: engines/scumm/metaengine.cpp:1085
 #, fuzzy
 msgid "Down left"
 msgstr "Drej til venstre"
 
-#: engines/scumm/metaengine.cpp:1074
+#: engines/scumm/metaengine.cpp:1098
 #, fuzzy
 msgid "Down right"
 msgstr "Drej til h??jre"
 
-#: engines/scumm/metaengine.cpp:1094
+#: engines/scumm/metaengine.cpp:1118
 #, fuzzy
 #| msgid "Upper left item"
 msgid "Up left"
 msgstr "??verste venstre punkt"
 
-#: engines/scumm/metaengine.cpp:1107
+#: engines/scumm/metaengine.cpp:1131
 #, fuzzy
 msgid "Up right"
 msgstr "H??jre"
 
-#: engines/scumm/metaengine.cpp:1119
+#: engines/scumm/metaengine.cpp:1143
 #, fuzzy
 msgid "Switch weapon"
 msgstr "Skifter karaktere:"
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1133
+#: engines/scumm/metaengine.cpp:1157
 msgid "Win the bike fight cheat"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1142
+#: engines/scumm/metaengine.cpp:1166
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1144 engines/scumm/metaengine.cpp:1221
+#: engines/scumm/metaengine.cpp:1168 engines/scumm/metaengine.cpp:1245
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1149 engines/scumm/metaengine.cpp:1226
+#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1250
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1231
+#: engines/scumm/metaengine.cpp:1178 engines/scumm/metaengine.cpp:1255
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
+#: engines/scumm/metaengine.cpp:1183 engines/scumm/metaengine.cpp:1260
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1241
+#: engines/scumm/metaengine.cpp:1188 engines/scumm/metaengine.cpp:1265
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "Tag op"
 
-#: engines/scumm/metaengine.cpp:1171 engines/scumm/metaengine.cpp:1248
+#: engines/scumm/metaengine.cpp:1195 engines/scumm/metaengine.cpp:1272
 #, fuzzy
 msgid "Stick down"
 msgstr "Rul liste ned"
 
-#: engines/scumm/metaengine.cpp:1178 engines/scumm/metaengine.cpp:1255
+#: engines/scumm/metaengine.cpp:1202 engines/scumm/metaengine.cpp:1279
 #, fuzzy
 #| msgid "Slide Left"
 msgid "Stick left"
 msgstr "Flyt til venstre"
 
-#: engines/scumm/metaengine.cpp:1185 engines/scumm/metaengine.cpp:1262
+#: engines/scumm/metaengine.cpp:1209 engines/scumm/metaengine.cpp:1286
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick right"
 msgstr "Flyt til h??jre"
 
-#: engines/scumm/metaengine.cpp:1192 engines/scumm/metaengine.cpp:1269
+#: engines/scumm/metaengine.cpp:1216 engines/scumm/metaengine.cpp:1293
 #, fuzzy
 msgid "Fire / select"
 msgstr "Flyt til venstre"
 
-#: engines/scumm/metaengine.cpp:1198
+#: engines/scumm/metaengine.cpp:1222
 #, fuzzy
 msgid "Walk / menu back"
 msgstr "~R~etur til oversigt"
 
-#: engines/scumm/metaengine.cpp:1203
+#: engines/scumm/metaengine.cpp:1227
 #, fuzzy
 msgid "Skip / menu back"
 msgstr "~R~etur til oversigt"
 
-#: engines/scumm/metaengine.cpp:1219
+#: engines/scumm/metaengine.cpp:1243
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1274
+#: engines/scumm/metaengine.cpp:1298
 #, fuzzy
 #| msgid "Move Back"
 msgid "Cover / back"
 msgstr "Flyt bagud"
 
-#: engines/scumm/metaengine.cpp:1280
+#: engines/scumm/metaengine.cpp:1304
 #, fuzzy
 msgid "Skip / back"
 msgstr "Spring linje over"
 
-#: engines/scumm/metaengine.cpp:1287
+#: engines/scumm/metaengine.cpp:1311
 #, fuzzy
 msgid "Skip / menu"
 msgstr "~R~etur til oversigt"
@@ -13957,18 +13966,18 @@ msgid ""
 "320x200, under 'engine' tab."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1362
+#: engines/scumm/scumm.cpp:1365
 #, c-format
 msgid "This game requires the '%s' Macintosh executable for its fonts."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1371
+#: engines/scumm/scumm.cpp:1374
 #, c-format
 msgid ""
 "This game requires the '%s' Macintosh executable for its music and fonts."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1375
+#: engines/scumm/scumm.cpp:1378
 #, fuzzy, c-format
 #| msgid ""
 #| "Could not find the 'Monkey Island' Macintosh executable to read the\n"
@@ -13980,25 +13989,25 @@ msgstr ""
 "Kunne ikke finde den 'Monkey Island' Macintosh eksekverbare fil, til\n"
 "at l??se instrumenterne fra. Musik bliver deaktiveret."
 
-#: engines/scumm/scumm.cpp:1376
+#: engines/scumm/scumm.cpp:1379
 msgid "The Mac GUI"
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1376
+#: engines/scumm/scumm.cpp:1379
 msgid "The music and the Mac GUI"
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1405
+#: engines/scumm/scumm.cpp:1408
 #, c-format
 msgid "Could not open Macintosh resource file %s"
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1408
+#: engines/scumm/scumm.cpp:1411
 #, c-format
 msgid "Could not find resource fork in Macintosh resource file %s"
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1415
+#: engines/scumm/scumm.cpp:1418
 #, c-format
 msgid ""
 "'%s' appears to be the wrong Dig executable. It may be the launcher one "
@@ -14007,7 +14016,7 @@ msgid ""
 "its sub-folders should be what you need."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1581
+#: engines/scumm/scumm.cpp:1584
 msgid ""
 "It appears your ScummVM version was not built with TrueType Fonts support.\n"
 "\n"
@@ -14015,14 +14024,14 @@ msgid ""
 "some of the graphics on screen will be missing."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1915
+#: engines/scumm/scumm.cpp:1918
 msgid ""
 "Audio files compressed with ScummVM Tools were detected; *.BUN/*.SOU\n"
 "compression is not supported anymore for this game, audio will be disabled.\n"
 "Please copy the game from the original media without compression."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:2405
+#: engines/scumm/scumm.cpp:2408
 #, c-format
 msgid ""
 "Native MIDI support requires the Roland Upgrade from LucasArts,\n"
@@ -14031,13 +14040,13 @@ msgstr ""
 "Indbygget underst??ttelse af MIDI kr??ver Roland opgradering fra LucasArts,\n"
 "men %s mangler. Bruger AdLib i stedet."
 
-#: engines/scumm/scumm.cpp:2420
+#: engines/scumm/scumm.cpp:2423
 msgid ""
 "This particular version of Monkey Island 1 is known to miss some\n"
 "required resources for MT-32. Using AdLib instead."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:4361
+#: engines/scumm/scumm.cpp:4364
 msgid ""
 "Usually, Maniac Mansion would start now. But for that to work, the game "
 "files for Maniac Mansion have to be in the 'Maniac' directory inside the "
@@ -14047,7 +14056,7 @@ msgstr ""
 "spil filerne til Maniac Mansion skal v??re i \"Maniac\"-mappen inde i "
 "Tentacle-spilmappen, og spillet skal tilf??jes til ScummVM."
 
-#: engines/scumm/scumm.cpp:4485
+#: engines/scumm/scumm.cpp:4488
 #, fuzzy
 msgid "Would you like to host or join a network play session?"
 msgstr "Vil du indl??se eller gemme spillet?"
diff --git a/po/de_DE.po b/po/de_DE.po
index c46ebd4d0e6..73fb0ba9618 100644
--- a/po/de_DE.po
+++ b/po/de_DE.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.10.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-08 11:04+0000\n"
+"POT-Creation-Date: 2026-06-09 13:22+0000\n"
 "PO-Revision-Date: 2026-05-05 16:31+0000\n"
 "Last-Translator: Lothar Serra Mari <lothar.serramari at scummvm.org>\n"
 "Language-Team: German <https://translations.scummvm.org/projects/scummvm/"
@@ -343,7 +343,7 @@ msgstr ""
 #: engines/hypno/metaengine.cpp:192 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:197
 #: engines/mm/mm1/metaengine.cpp:54 engines/parallaction/metaengine.cpp:169
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4467
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
 #: engines/sherlock/metaengine.cpp:638 engines/sherlock/metaengine.cpp:1244
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:328
@@ -362,7 +362,7 @@ msgstr "Ja"
 #: engines/hypno/metaengine.cpp:198 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:204
 #: engines/mm/mm1/metaengine.cpp:52 engines/parallaction/metaengine.cpp:175
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4467
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
 #: engines/sherlock/metaengine.cpp:643 engines/sherlock/metaengine.cpp:1250
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:329
@@ -390,7 +390,7 @@ msgstr "JSON-Code ist fehlerhaft."
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:123
 #: engines/drascula/saveload.cpp:51 engines/grim/inputdialog.cpp:83
 #: engines/parallaction/saveload.cpp:270 engines/sci/engine/kgraphics.cpp:76
-#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4479
+#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4482
 #: engines/sword1/control.cpp:2816
 msgid "OK"
 msgstr "OK"
@@ -1064,7 +1064,7 @@ msgstr "Interagieren"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1100
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1124
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1085,7 +1085,7 @@ msgstr "Hoch"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1067
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1091
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1106,7 +1106,7 @@ msgstr "Runter"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1080
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1104
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1126,7 +1126,7 @@ msgstr "Links"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1087
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1111
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3426,7 +3426,7 @@ msgstr "Spiele-Engine f??r Grafik-Adventures"
 #: engines/mohawk/riven_stacks/aspit.cpp:454
 #: engines/neverhood/metaengine.cpp:200 engines/saga/metaengine.cpp:449
 #: engines/scumm/help.cpp:84 engines/scumm/help.cpp:86
-#: engines/scumm/scumm.cpp:4479 engines/sludge/keymapper_tables.h:95
+#: engines/scumm/scumm.cpp:4482 engines/sludge/keymapper_tables.h:95
 #: engines/sludge/keymapper_tables.h:144 engines/sludge/keymapper_tables.h:223
 #: engines/sludge/keymapper_tables.h:343 engines/sludge/keymapper_tables.h:355
 #: engines/sludge/keymapper_tables.h:416 engines/sludge/keymapper_tables.h:429
@@ -3929,7 +3929,7 @@ msgid "Game not implemented"
 msgstr "Spiel nicht implementiert"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
-#: engines/scumm/metaengine.cpp:1050
+#: engines/scumm/metaengine.cpp:1074
 msgid "Default game keymappings"
 msgstr "Standard-Tastenbelegung f??r Spiele"
 
@@ -6144,7 +6144,7 @@ msgstr "R2"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1208 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1232 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "Men??"
 
@@ -7101,7 +7101,7 @@ msgstr ""
 "Schnelles Laden oder Speichern eines Spiels wird an diesem Ort nicht "
 "unterst??tzt"
 
-#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3418
+#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3421
 #, c-format
 msgid ""
 "Failed to load saved game from file:\n"
@@ -7112,7 +7112,7 @@ msgstr ""
 "\n"
 "%s"
 
-#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3405
+#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3408
 #, c-format
 msgid ""
 "Failed to save game to file:\n"
@@ -7123,7 +7123,7 @@ msgstr ""
 "\n"
 "%s"
 
-#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3438
+#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3441
 #, c-format
 msgid ""
 "Successfully saved game in file:\n"
@@ -7627,7 +7627,7 @@ msgstr "Kampf umschalten"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1126 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -8294,7 +8294,7 @@ msgstr "Sprechen / Interagieren"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1113
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1137
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -10296,7 +10296,7 @@ msgid "Player ammo will never decrease."
 msgstr "Die Munition des Spielers wird sich nie verringern."
 
 #: engines/hypno/metaengine.cpp:70 engines/scumm/metaengine.cpp:891
-#: engines/scumm/metaengine.cpp:909
+#: engines/scumm/metaengine.cpp:918
 msgid "Unlock all levels"
 msgstr "Alle Level freischalten"
 
@@ -12720,7 +12720,7 @@ msgid "Your Name:"
 msgstr "Dein Name:"
 
 #. I18N: Button, start hosting online multiplayer game
-#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4485
+#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4488
 msgid "Host"
 msgstr "Hosten"
 
@@ -12730,7 +12730,7 @@ msgid "Querying games..."
 msgstr "Spiele werden abgefragt..."
 
 #. I18N: Join online multiplayer game
-#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4485
+#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4488
 msgid "Join"
 msgstr "Beitreten"
 
@@ -13712,128 +13712,138 @@ msgstr "Hohe Aufl??sung eingeschaltet"
 msgid "Run the game in 640x400 high resolution mode instead of 320x200"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:910
+#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:919
 #, fuzzy
 #| msgid "All levels will be available to play."
 msgid "All levels will be available without requiring passwords"
 msgstr "Alle Level k??nnen direkt gespielt werden."
 
-#: engines/scumm/metaengine.cpp:900
+#: engines/scumm/metaengine.cpp:900 engines/scumm/metaengine.cpp:927
 #, fuzzy
 #| msgid "No date saved"
 msgid "No damage"
 msgstr "Kein Datum gespeichert"
 
-#: engines/scumm/metaengine.cpp:901
+#: engines/scumm/metaengine.cpp:901 engines/scumm/metaengine.cpp:928
 #, fuzzy
 #| msgid "Disable save menu"
 msgid "Disable player damage"
 msgstr "Speichermen?? deaktivieren"
 
-#: engines/scumm/metaengine.cpp:1061
+#: engines/scumm/metaengine.cpp:909
+#, fuzzy
+#| msgid "Fast mode"
+msgid "Yoda mode"
+msgstr "Das Spiel wird schneller abgespielt"
+
+#: engines/scumm/metaengine.cpp:910
+msgid "Enable original Yoda mode shortcuts, including movie mode and auto play"
+msgstr ""
+
+#: engines/scumm/metaengine.cpp:1085
 msgid "Down left"
 msgstr "Unten links"
 
-#: engines/scumm/metaengine.cpp:1074
+#: engines/scumm/metaengine.cpp:1098
 msgid "Down right"
 msgstr "Unten rechts"
 
-#: engines/scumm/metaengine.cpp:1094
+#: engines/scumm/metaengine.cpp:1118
 msgid "Up left"
 msgstr "Oben links"
 
-#: engines/scumm/metaengine.cpp:1107
+#: engines/scumm/metaengine.cpp:1131
 msgid "Up right"
 msgstr "Oben rechts"
 
-#: engines/scumm/metaengine.cpp:1119
+#: engines/scumm/metaengine.cpp:1143
 msgid "Switch weapon"
 msgstr "Waffe wechseln"
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1133
+#: engines/scumm/metaengine.cpp:1157
 msgid "Win the bike fight cheat"
 msgstr "Cheat: Bike-Kampf gewinnen"
 
-#: engines/scumm/metaengine.cpp:1142
+#: engines/scumm/metaengine.cpp:1166
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1144 engines/scumm/metaengine.cpp:1221
+#: engines/scumm/metaengine.cpp:1168 engines/scumm/metaengine.cpp:1245
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1149 engines/scumm/metaengine.cpp:1226
+#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1250
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1231
+#: engines/scumm/metaengine.cpp:1178 engines/scumm/metaengine.cpp:1255
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
+#: engines/scumm/metaengine.cpp:1183 engines/scumm/metaengine.cpp:1260
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1241
+#: engines/scumm/metaengine.cpp:1188 engines/scumm/metaengine.cpp:1265
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "Nimm"
 
-#: engines/scumm/metaengine.cpp:1171 engines/scumm/metaengine.cpp:1248
+#: engines/scumm/metaengine.cpp:1195 engines/scumm/metaengine.cpp:1272
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "Nach unten scrollen"
 
-#: engines/scumm/metaengine.cpp:1178 engines/scumm/metaengine.cpp:1255
+#: engines/scumm/metaengine.cpp:1202 engines/scumm/metaengine.cpp:1279
 #, fuzzy
 #| msgid "Slide left"
 msgid "Stick left"
 msgstr "Slide nach links"
 
-#: engines/scumm/metaengine.cpp:1185 engines/scumm/metaengine.cpp:1262
+#: engines/scumm/metaengine.cpp:1209 engines/scumm/metaengine.cpp:1286
 #, fuzzy
 #| msgid "Slide right"
 msgid "Stick right"
 msgstr "Slide nach rechts"
 
-#: engines/scumm/metaengine.cpp:1192 engines/scumm/metaengine.cpp:1269
+#: engines/scumm/metaengine.cpp:1216 engines/scumm/metaengine.cpp:1293
 #, fuzzy
 #| msgid "Move / Select"
 msgid "Fire / select"
 msgstr "Bewegen / Ausw??hlen"
 
-#: engines/scumm/metaengine.cpp:1198
+#: engines/scumm/metaengine.cpp:1222
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Walk / menu back"
 msgstr "Hauptmen?? ??berspringen"
 
-#: engines/scumm/metaengine.cpp:1203
+#: engines/scumm/metaengine.cpp:1227
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu back"
 msgstr "Hauptmen?? ??berspringen"
 
-#: engines/scumm/metaengine.cpp:1219
+#: engines/scumm/metaengine.cpp:1243
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1274
+#: engines/scumm/metaengine.cpp:1298
 #, fuzzy
 #| msgid "Move back"
 msgid "Cover / back"
 msgstr "Zur??ckgehen"
 
-#: engines/scumm/metaengine.cpp:1280
+#: engines/scumm/metaengine.cpp:1304
 #, fuzzy
 #| msgid "Skip walk"
 msgid "Skip / back"
 msgstr "Spaziergang ??berspringen"
 
-#: engines/scumm/metaengine.cpp:1287
+#: engines/scumm/metaengine.cpp:1311
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu"
@@ -13876,14 +13886,14 @@ msgstr ""
 "Ein korrektes Seitenverh??ltnis kann erreicht werden, in dem im Engine-Tab "
 "die Aufl??sung auf 320x200 begrenzt wird."
 
-#: engines/scumm/scumm.cpp:1362
+#: engines/scumm/scumm.cpp:1365
 #, c-format
 msgid "This game requires the '%s' Macintosh executable for its fonts."
 msgstr ""
 "Dieses Spiel ben??tigt die '%s'-Datei der Macintosh-Version zur "
 "Schriftdarstellung."
 
-#: engines/scumm/scumm.cpp:1371
+#: engines/scumm/scumm.cpp:1374
 #, c-format
 msgid ""
 "This game requires the '%s' Macintosh executable for its music and fonts."
@@ -13891,32 +13901,32 @@ msgstr ""
 "Dieses Spiel ben??tigt die '%s'-Datei der Macintosh-Version zur "
 "Schriftdarstellung und f??r die Musikwiedergabe."
 
-#: engines/scumm/scumm.cpp:1375
+#: engines/scumm/scumm.cpp:1378
 #, c-format
 msgid ""
 "Could not find the '%s' Macintosh executable to read resources from. %s will "
 "be disabled."
 msgstr "Macintosh-Programmdatei von '%s' nicht gefunden. %s wird deaktiviert."
 
-#: engines/scumm/scumm.cpp:1376
+#: engines/scumm/scumm.cpp:1379
 msgid "The Mac GUI"
 msgstr "Die Mac-Oberfl??che"
 
-#: engines/scumm/scumm.cpp:1376
+#: engines/scumm/scumm.cpp:1379
 msgid "The music and the Mac GUI"
 msgstr "Die Musik und die Mac-Oberfl??che"
 
-#: engines/scumm/scumm.cpp:1405
+#: engines/scumm/scumm.cpp:1408
 #, c-format
 msgid "Could not open Macintosh resource file %s"
 msgstr "Die Macintosh-Resource-Datei %s konnte nicht ge??ffnet werden"
 
-#: engines/scumm/scumm.cpp:1408
+#: engines/scumm/scumm.cpp:1411
 #, c-format
 msgid "Could not find resource fork in Macintosh resource file %s"
 msgstr "In der Macintosh-Resource-Datei %s wurde kein Resource Fork gefunden"
 
-#: engines/scumm/scumm.cpp:1415
+#: engines/scumm/scumm.cpp:1418
 #, c-format
 msgid ""
 "'%s' appears to be the wrong Dig executable. It may be the launcher one "
@@ -13930,7 +13940,7 @@ msgstr ""
 "Verzeichnis 'The Dig f' auf deiner CD. Die Unterverzeichnisse dieses Ordners "
 "sollten alles enthalten, was du suchst."
 
-#: engines/scumm/scumm.cpp:1581
+#: engines/scumm/scumm.cpp:1584
 msgid ""
 "It appears your ScummVM version was not built with TrueType Fonts support.\n"
 "\n"
@@ -13943,7 +13953,7 @@ msgstr ""
 "Da die One-Stop Fun Shop-Reihe in gro??em Umfang TTF-Fonts verwendet,\n"
 "werden einige Grafiken auf dem Bildschirm fehlen."
 
-#: engines/scumm/scumm.cpp:1915
+#: engines/scumm/scumm.cpp:1918
 msgid ""
 "Audio files compressed with ScummVM Tools were detected; *.BUN/*.SOU\n"
 "compression is not supported anymore for this game, audio will be disabled.\n"
@@ -13957,7 +13967,7 @@ msgstr ""
 "Kompression von\n"
 "den originalen Medien."
 
-#: engines/scumm/scumm.cpp:2405
+#: engines/scumm/scumm.cpp:2408
 #, c-format
 msgid ""
 "Native MIDI support requires the Roland Upgrade from LucasArts,\n"
@@ -13967,7 +13977,7 @@ msgstr ""
 "Roland-Upgrade von LucasArts, aber %s\n"
 "fehlt. Stattdessen wird AdLib verwendet."
 
-#: engines/scumm/scumm.cpp:2420
+#: engines/scumm/scumm.cpp:2423
 msgid ""
 "This particular version of Monkey Island 1 is known to miss some\n"
 "required resources for MT-32. Using AdLib instead."
@@ -13975,7 +13985,7 @@ msgstr ""
 "Diese Version von Monkey Island 1 ist daf??r bekannt, dass einige\n"
 "f??r MT-32 ben??tigte Ressourcen fehlen. Stattdessen wird AdLib verwendet."
 
-#: engines/scumm/scumm.cpp:4361
+#: engines/scumm/scumm.cpp:4364
 msgid ""
 "Usually, Maniac Mansion would start now. But for that to work, the game "
 "files for Maniac Mansion have to be in the 'Maniac' directory inside the "
@@ -13986,7 +13996,7 @@ msgstr ""
 "im Startmen?? von ScummVM und w??hlen das Verzeichnis \"Maniac\" im "
 "Verzeichnis dieses Spiels aus."
 
-#: engines/scumm/scumm.cpp:4485
+#: engines/scumm/scumm.cpp:4488
 msgid "Would you like to host or join a network play session?"
 msgstr ""
 "M??chtest Du ein Spiel ??ber das Netzwerk hosten oder einem Spiel beitreten?"
diff --git a/po/el.po b/po/el.po
index 336380fcf40..d24ceba91ec 100644
--- a/po/el.po
+++ b/po/el.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.10.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-08 11:04+0000\n"
+"POT-Creation-Date: 2026-06-09 13:22+0000\n"
 "PO-Revision-Date: 2026-04-23 00:22+0000\n"
 "Last-Translator: Antoniou Athanasios <a.antoniou79 at gmail.com>\n"
 "Language-Team: Greek <https://translations.scummvm.org/projects/scummvm/"
@@ -342,7 +342,7 @@ msgstr ""
 #: engines/hypno/metaengine.cpp:192 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:197
 #: engines/mm/mm1/metaengine.cpp:54 engines/parallaction/metaengine.cpp:169
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4467
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
 #: engines/sherlock/metaengine.cpp:638 engines/sherlock/metaengine.cpp:1244
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:328
@@ -361,7 +361,7 @@ msgstr "??????"
 #: engines/hypno/metaengine.cpp:198 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:204
 #: engines/mm/mm1/metaengine.cpp:52 engines/parallaction/metaengine.cpp:175
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4467
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
 #: engines/sherlock/metaengine.cpp:643 engines/sherlock/metaengine.cpp:1250
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:329
@@ -389,7 +389,7 @@ msgstr "???? ?????????????????????? ?????? ???????????? JSON ?????? ?????????? 
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:123
 #: engines/drascula/saveload.cpp:51 engines/grim/inputdialog.cpp:83
 #: engines/parallaction/saveload.cpp:270 engines/sci/engine/kgraphics.cpp:76
-#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4479
+#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4482
 #: engines/sword1/control.cpp:2816
 msgid "OK"
 msgstr "OK"
@@ -1067,7 +1067,7 @@ msgstr "??????????????????????????"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1100
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1124
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1088,7 +1088,7 @@ msgstr "????????"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1067
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1091
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1109,7 +1109,7 @@ msgstr "????????"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1080
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1104
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1129,7 +1129,7 @@ msgstr "????????????????"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1087
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1111
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3481,7 +3481,7 @@ msgstr "???????????? ???????????????????? ??????????????????????"
 #: engines/mohawk/riven_stacks/aspit.cpp:454
 #: engines/neverhood/metaengine.cpp:200 engines/saga/metaengine.cpp:449
 #: engines/scumm/help.cpp:84 engines/scumm/help.cpp:86
-#: engines/scumm/scumm.cpp:4479 engines/sludge/keymapper_tables.h:95
+#: engines/scumm/scumm.cpp:4482 engines/sludge/keymapper_tables.h:95
 #: engines/sludge/keymapper_tables.h:144 engines/sludge/keymapper_tables.h:223
 #: engines/sludge/keymapper_tables.h:343 engines/sludge/keymapper_tables.h:355
 #: engines/sludge/keymapper_tables.h:416 engines/sludge/keymapper_tables.h:429
@@ -4000,7 +4000,7 @@ msgid "Game not implemented"
 msgstr "???? ???????????????? ?????? ???????? ????????????????????"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
-#: engines/scumm/metaengine.cpp:1050
+#: engines/scumm/metaengine.cpp:1074
 msgid "Default game keymappings"
 msgstr "?????????????? ???????????????????????????? ???????????????? ????????????????????"
 
@@ -6268,7 +6268,7 @@ msgstr "R2"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1208 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1232 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "??????????"
 
@@ -7225,7 +7225,7 @@ msgstr ""
 "?? ?????????????? ?????????????? ?? ???????????????????? ???????????????????? ?????? ?????????????????????????? ???? ???????? ?????? "
 "??????????????"
 
-#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3418
+#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3421
 #, c-format
 msgid ""
 "Failed to load saved game from file:\n"
@@ -7236,7 +7236,7 @@ msgstr ""
 "\n"
 "%s"
 
-#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3405
+#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3408
 #, c-format
 msgid ""
 "Failed to save game to file:\n"
@@ -7247,7 +7247,7 @@ msgstr ""
 "\n"
 "%s"
 
-#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3438
+#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3441
 #, c-format
 msgid ""
 "Successfully saved game in file:\n"
@@ -7755,7 +7755,7 @@ msgstr "???????????????? ?????????????????????? ?????? ????????"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1126 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -8429,7 +8429,7 @@ msgstr "???????? / ??????????????????????????"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1113
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1137
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -10425,7 +10425,7 @@ msgid "Player ammo will never decrease."
 msgstr "???? ???????????????????? ?????? ???????????? ?????? ???? ???????????????? ????????."
 
 #: engines/hypno/metaengine.cpp:70 engines/scumm/metaengine.cpp:891
-#: engines/scumm/metaengine.cpp:909
+#: engines/scumm/metaengine.cpp:918
 msgid "Unlock all levels"
 msgstr "???????????????????? ???????? ?????? ????????????????"
 
@@ -12863,7 +12863,7 @@ msgid "Your Name:"
 msgstr "???? ?????????? ??????:"
 
 #. I18N: Button, start hosting online multiplayer game
-#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4485
+#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4488
 msgid "Host"
 msgstr "??????????????????"
 
@@ -12873,7 +12873,7 @@ msgid "Querying games..."
 msgstr "?????????????????? ????????????????????..."
 
 #. I18N: Join online multiplayer game
-#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4485
+#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4488
 msgid "Join"
 msgstr "??????????????????"
 
@@ -13867,128 +13867,138 @@ msgstr "???????????????????????? ???????????? ????????????????"
 msgid "Run the game in 640x400 high resolution mode instead of 320x200"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:910
+#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:919
 #, fuzzy
 #| msgid "All levels will be available to play."
 msgid "All levels will be available without requiring passwords"
 msgstr "?????? ???? ?????????????? ???? ???????????? ?????????????????? ?????? ????????????????."
 
-#: engines/scumm/metaengine.cpp:900
+#: engines/scumm/metaengine.cpp:900 engines/scumm/metaengine.cpp:927
 #, fuzzy
 #| msgid "No date saved"
 msgid "No damage"
 msgstr "?????????? ????????????. ????/??????"
 
-#: engines/scumm/metaengine.cpp:901
+#: engines/scumm/metaengine.cpp:901 engines/scumm/metaengine.cpp:928
 #, fuzzy
 #| msgid "Disable save menu"
 msgid "Disable player damage"
 msgstr "???????????????????????????? ?????????? ??????????????????????"
 
-#: engines/scumm/metaengine.cpp:1061
+#: engines/scumm/metaengine.cpp:909
+#, fuzzy
+#| msgid "Fast mode"
+msgid "Yoda mode"
+msgstr "???????????????? ????????????????????"
+
+#: engines/scumm/metaengine.cpp:910
+msgid "Enable original Yoda mode shortcuts, including movie mode and auto play"
+msgstr ""
+
+#: engines/scumm/metaengine.cpp:1085
 msgid "Down left"
 msgstr "???????? ????????????????"
 
-#: engines/scumm/metaengine.cpp:1074
+#: engines/scumm/metaengine.cpp:1098
 msgid "Down right"
 msgstr "???????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1094
+#: engines/scumm/metaengine.cpp:1118
 msgid "Up left"
 msgstr "???????? ????????????????"
 
-#: engines/scumm/metaengine.cpp:1107
+#: engines/scumm/metaengine.cpp:1131
 msgid "Up right"
 msgstr "???????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1119
+#: engines/scumm/metaengine.cpp:1143
 msgid "Switch weapon"
 msgstr "???????????? ??????????"
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1133
+#: engines/scumm/metaengine.cpp:1157
 msgid "Win the bike fight cheat"
 msgstr "?????????? ?????? ???????? ?????? ???????? ???? ?????? ??????????????"
 
-#: engines/scumm/metaengine.cpp:1142
+#: engines/scumm/metaengine.cpp:1166
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1144 engines/scumm/metaengine.cpp:1221
+#: engines/scumm/metaengine.cpp:1168 engines/scumm/metaengine.cpp:1245
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1149 engines/scumm/metaengine.cpp:1226
+#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1250
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1231
+#: engines/scumm/metaengine.cpp:1178 engines/scumm/metaengine.cpp:1255
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
+#: engines/scumm/metaengine.cpp:1183 engines/scumm/metaengine.cpp:1260
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1241
+#: engines/scumm/metaengine.cpp:1188 engines/scumm/metaengine.cpp:1265
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "????????????"
 
-#: engines/scumm/metaengine.cpp:1171 engines/scumm/metaengine.cpp:1248
+#: engines/scumm/metaengine.cpp:1195 engines/scumm/metaengine.cpp:1272
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "???????????? ????????"
 
-#: engines/scumm/metaengine.cpp:1178 engines/scumm/metaengine.cpp:1255
+#: engines/scumm/metaengine.cpp:1202 engines/scumm/metaengine.cpp:1279
 #, fuzzy
 #| msgid "Slide left"
 msgid "Stick left"
 msgstr "???????????????? ????????????????"
 
-#: engines/scumm/metaengine.cpp:1185 engines/scumm/metaengine.cpp:1262
+#: engines/scumm/metaengine.cpp:1209 engines/scumm/metaengine.cpp:1286
 #, fuzzy
 #| msgid "Slide right"
 msgid "Stick right"
 msgstr "???????????????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1192 engines/scumm/metaengine.cpp:1269
+#: engines/scumm/metaengine.cpp:1216 engines/scumm/metaengine.cpp:1293
 #, fuzzy
 #| msgid "Move / Select"
 msgid "Fire / select"
 msgstr "???????????? / ??????????????"
 
-#: engines/scumm/metaengine.cpp:1198
+#: engines/scumm/metaengine.cpp:1222
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Walk / menu back"
 msgstr "?????????????????? ?????????????????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1203
+#: engines/scumm/metaengine.cpp:1227
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu back"
 msgstr "?????????????????? ?????????????????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1219
+#: engines/scumm/metaengine.cpp:1243
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1274
+#: engines/scumm/metaengine.cpp:1298
 #, fuzzy
 #| msgid "Move Back"
 msgid "Cover / back"
 msgstr "?????????????????? ????????"
 
-#: engines/scumm/metaengine.cpp:1280
+#: engines/scumm/metaengine.cpp:1304
 #, fuzzy
 #| msgid "Skip walk"
 msgid "Skip / back"
 msgstr "?????????????????? ??????????????????"
 
-#: engines/scumm/metaengine.cpp:1287
+#: engines/scumm/metaengine.cpp:1311
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu"
@@ -14031,14 +14041,14 @@ msgstr ""
 "?? ???????????????? ?????????????????? ???????????????????? ???????????? ???? ???????????????????? ???? ?????????????????? ?????? "
 "???????????????? ?????? ???????????????? ???? 320x200 ?????? ?????? ?????????????? '????????????'."
 
-#: engines/scumm/scumm.cpp:1362
+#: engines/scumm/scumm.cpp:1365
 #, c-format
 msgid "This game requires the '%s' Macintosh executable for its fonts."
 msgstr ""
 "???????? ???? ???????????????? ?????????????? ???? ???????????????????? '%s' ?????? Macintosh ?????? ?????? "
 "???????????????????????????? ??????."
 
-#: engines/scumm/scumm.cpp:1371
+#: engines/scumm/scumm.cpp:1374
 #, c-format
 msgid ""
 "This game requires the '%s' Macintosh executable for its music and fonts."
@@ -14046,7 +14056,7 @@ msgstr ""
 "???????? ???? ???????????????? ?????????????? ???? ???????????????????? '%s' ?????? Macintosh ?????? ???? ?????????????? ?????? "
 "?????? ???????????????????????????? ??????."
 
-#: engines/scumm/scumm.cpp:1375
+#: engines/scumm/scumm.cpp:1378
 #, c-format
 msgid ""
 "Could not find the '%s' Macintosh executable to read resources from. %s will "
@@ -14055,25 +14065,25 @@ msgstr ""
 "???????????????? ?????????????? ?????? ?????????????????????? '%s' ?????? Macintosh ?????? ?????? ???????????????? ??????????. "
 "???????????????????????????? ??????: %s ."
 
-#: engines/scumm/scumm.cpp:1376
+#: engines/scumm/scumm.cpp:1379
 msgid "The Mac GUI"
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1376
+#: engines/scumm/scumm.cpp:1379
 msgid "The music and the Mac GUI"
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1405
+#: engines/scumm/scumm.cpp:1408
 #, c-format
 msgid "Could not open Macintosh resource file %s"
 msgstr "???????????????? ???????????????????? ?????? ?????????????? ?????????? ?????? Macintosh %s"
 
-#: engines/scumm/scumm.cpp:1408
+#: engines/scumm/scumm.cpp:1411
 #, c-format
 msgid "Could not find resource fork in Macintosh resource file %s"
 msgstr "???????????????? ?????????????? ?????????????????????? ?????????? ?????? ???????????? ?????????? ?????? Macintosh %s"
 
-#: engines/scumm/scumm.cpp:1415
+#: engines/scumm/scumm.cpp:1418
 #, c-format
 msgid ""
 "'%s' appears to be the wrong Dig executable. It may be the launcher one "
@@ -14087,7 +14097,7 @@ msgstr ""
 "??????????????????. ???????????????????? ???????? ???????????????? 'The Dig f' ?????? CD ??????. ???????????????????????? ?????? "
 "???????? ??????-???????????????????? ?????? ???????????? ???? ???????????????????????????? ??????."
 
-#: engines/scumm/scumm.cpp:1581
+#: engines/scumm/scumm.cpp:1584
 msgid ""
 "It appears your ScummVM version was not built with TrueType Fonts support.\n"
 "\n"
@@ -14101,7 +14111,7 @@ msgstr ""
 "???????????????????????????? TTF,\n"
 "???????????? ?????? ???? ?????????????? ?????? ?????? ???????????? ???? ??????????????."
 
-#: engines/scumm/scumm.cpp:1915
+#: engines/scumm/scumm.cpp:1918
 msgid ""
 "Audio files compressed with ScummVM Tools were detected; *.BUN/*.SOU\n"
 "compression is not supported anymore for this game, audio will be disabled.\n"
@@ -14114,7 +14124,7 @@ msgstr ""
 "?????????????????????? ???? ?????????????????????? ???? ?????????????????? (original), ????????????????????, ???????????? ???????? "
 "?????? ????????????????????."
 
-#: engines/scumm/scumm.cpp:2405
+#: engines/scumm/scumm.cpp:2408
 #, c-format
 msgid ""
 "Native MIDI support requires the Roland Upgrade from LucasArts,\n"
@@ -14123,7 +14133,7 @@ msgstr ""
 "?? ???????????????????? ???????????????? MIDI ?????????????? ?????? ???????????????????? Roland ?????? ???? LucasArts,\n"
 "???????? ???? %s ????????????. ??????' ?????????? ???? ?????????? ?????????? AdLib."
 
-#: engines/scumm/scumm.cpp:2420
+#: engines/scumm/scumm.cpp:2423
 msgid ""
 "This particular version of Monkey Island 1 is known to miss some\n"
 "required resources for MT-32. Using AdLib instead."
@@ -14132,7 +14142,7 @@ msgstr ""
 "????????????????????\n"
 "???????????????? ?????? ?????? ???????????????????? ?????? MT-32. ??????' ?????????? ???? ?????????? ?????????? AdLib."
 
-#: engines/scumm/scumm.cpp:4361
+#: engines/scumm/scumm.cpp:4364
 msgid ""
 "Usually, Maniac Mansion would start now. But for that to work, the game "
 "files for Maniac Mansion have to be in the 'Maniac' directory inside the "
@@ -14143,7 +14153,7 @@ msgstr ""
 "???????????? 'Maniac' ???????? ?????? ???????????? ???????????????????? ?????? Tentacle, ?????? ???? ???????????????? "
 "???????????? ???? ?????????????????? ?????? ScummVM."
 
-#: engines/scumm/scumm.cpp:4485
+#: engines/scumm/scumm.cpp:4488
 msgid "Would you like to host or join a network play session?"
 msgstr "???????????? ???? ???????????????????????? ?? ???? ?????????????????????? ???? online ???????????????? ????????????????????;"
 
diff --git a/po/es_ES.po b/po/es_ES.po
index 25e3d40af4a..784b440a7d7 100644
--- a/po/es_ES.po
+++ b/po/es_ES.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.4.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-08 11:04+0000\n"
+"POT-Creation-Date: 2026-06-09 13:22+0000\n"
 "PO-Revision-Date: 2026-06-08 08:48+0000\n"
 "Last-Translator: IlDucci <pajaroloco_2 at hotmail.com>\n"
 "Language-Team: Spanish <https://translations.scummvm.org/projects/scummvm/"
@@ -339,7 +339,7 @@ msgstr "Ya hay otro almacenamiento activo. ??Quieres interrumpirlo?"
 #: engines/hypno/metaengine.cpp:192 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:197
 #: engines/mm/mm1/metaengine.cpp:54 engines/parallaction/metaengine.cpp:169
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4467
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
 #: engines/sherlock/metaengine.cpp:638 engines/sherlock/metaengine.cpp:1244
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:328
@@ -358,7 +358,7 @@ msgstr "S??"
 #: engines/hypno/metaengine.cpp:198 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:204
 #: engines/mm/mm1/metaengine.cpp:52 engines/parallaction/metaengine.cpp:175
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4467
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
 #: engines/sherlock/metaengine.cpp:643 engines/sherlock/metaengine.cpp:1250
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:329
@@ -386,7 +386,7 @@ msgstr "Los contenidos del c??digo JSON no son v??lidos."
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:123
 #: engines/drascula/saveload.cpp:51 engines/grim/inputdialog.cpp:83
 #: engines/parallaction/saveload.cpp:270 engines/sci/engine/kgraphics.cpp:76
-#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4479
+#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4482
 #: engines/sword1/control.cpp:2816
 msgid "OK"
 msgstr "Aceptar"
@@ -1052,7 +1052,7 @@ msgstr "Interactuar"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1100
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1124
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1073,7 +1073,7 @@ msgstr "Arriba"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1067
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1091
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1094,7 +1094,7 @@ msgstr "Abajo"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1080
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1104
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1114,7 +1114,7 @@ msgstr "Izquierda"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1087
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1111
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3429,7 +3429,7 @@ msgstr "Motor de aventuras gr??ficas"
 #: engines/mohawk/riven_stacks/aspit.cpp:454
 #: engines/neverhood/metaengine.cpp:200 engines/saga/metaengine.cpp:449
 #: engines/scumm/help.cpp:84 engines/scumm/help.cpp:86
-#: engines/scumm/scumm.cpp:4479 engines/sludge/keymapper_tables.h:95
+#: engines/scumm/scumm.cpp:4482 engines/sludge/keymapper_tables.h:95
 #: engines/sludge/keymapper_tables.h:144 engines/sludge/keymapper_tables.h:223
 #: engines/sludge/keymapper_tables.h:343 engines/sludge/keymapper_tables.h:355
 #: engines/sludge/keymapper_tables.h:416 engines/sludge/keymapper_tables.h:429
@@ -3932,7 +3932,7 @@ msgid "Game not implemented"
 msgstr "Juego no implementado"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
-#: engines/scumm/metaengine.cpp:1050
+#: engines/scumm/metaengine.cpp:1074
 msgid "Default game keymappings"
 msgstr "Asignaciones predet. del juego"
 
@@ -6166,7 +6166,7 @@ msgstr "R2"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1208 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1232 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "Men??"
 
@@ -7120,7 +7120,7 @@ msgstr ""
 msgid "Quick load or save game isn't supported in this location"
 msgstr "No se pueden usar el guardado o carga r??pidos en este lugar"
 
-#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3418
+#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3421
 #, c-format
 msgid ""
 "Failed to load saved game from file:\n"
@@ -7131,7 +7131,7 @@ msgstr ""
 "\n"
 "%s"
 
-#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3405
+#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3408
 #, c-format
 msgid ""
 "Failed to save game to file:\n"
@@ -7142,7 +7142,7 @@ msgstr ""
 "\n"
 "%s"
 
-#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3438
+#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3441
 #, c-format
 msgid ""
 "Successfully saved game in file:\n"
@@ -7642,7 +7642,7 @@ msgstr "Entrar/Salir del modo de combate"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1126 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -8311,7 +8311,7 @@ msgstr "Hablar/Interactuar"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1113
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1137
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -10302,7 +10302,7 @@ msgid "Player ammo will never decrease."
 msgstr "La munici??n del jugador no se reducir?? nunca."
 
 #: engines/hypno/metaengine.cpp:70 engines/scumm/metaengine.cpp:891
-#: engines/scumm/metaengine.cpp:909
+#: engines/scumm/metaengine.cpp:918
 msgid "Unlock all levels"
 msgstr "Desbloquear todos los niveles"
 
@@ -12695,7 +12695,7 @@ msgid "Your Name:"
 msgstr "Tu nombre:"
 
 #. I18N: Button, start hosting online multiplayer game
-#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4485
+#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4488
 msgid "Host"
 msgstr "Crear"
 
@@ -12705,7 +12705,7 @@ msgid "Querying games..."
 msgstr "Buscando partidas..."
 
 #. I18N: Join online multiplayer game
-#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4485
+#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4488
 msgid "Join"
 msgstr "Unirse"
 
@@ -13687,104 +13687,114 @@ msgstr ""
 "Muestra el juego en el modo de alta resoluci??n a 640??400 p??xeles, en vez de "
 "a 320??200"
 
-#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:910
+#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:919
 msgid "All levels will be available without requiring passwords"
 msgstr "Desbloquea todos los niveles sin necesitar contrase??as"
 
-#: engines/scumm/metaengine.cpp:900
+#: engines/scumm/metaengine.cpp:900 engines/scumm/metaengine.cpp:927
 msgid "No damage"
 msgstr "Sin da??os"
 
-#: engines/scumm/metaengine.cpp:901
+#: engines/scumm/metaengine.cpp:901 engines/scumm/metaengine.cpp:928
 msgid "Disable player damage"
 msgstr "Desactiva los da??os recibidos por el jugador"
 
-#: engines/scumm/metaengine.cpp:1061
+#: engines/scumm/metaengine.cpp:909
+#, fuzzy
+#| msgid "Fast mode"
+msgid "Yoda mode"
+msgstr "Modo r??pido"
+
+#: engines/scumm/metaengine.cpp:910
+msgid "Enable original Yoda mode shortcuts, including movie mode and auto play"
+msgstr ""
+
+#: engines/scumm/metaengine.cpp:1085
 msgid "Down left"
 msgstr "Bajar a la izquierda"
 
-#: engines/scumm/metaengine.cpp:1074
+#: engines/scumm/metaengine.cpp:1098
 msgid "Down right"
 msgstr "Bajar a la derecha"
 
-#: engines/scumm/metaengine.cpp:1094
+#: engines/scumm/metaengine.cpp:1118
 msgid "Up left"
 msgstr "Subir a la izquierda"
 
-#: engines/scumm/metaengine.cpp:1107
+#: engines/scumm/metaengine.cpp:1131
 msgid "Up right"
 msgstr "Subir a la derecha"
 
-#: engines/scumm/metaengine.cpp:1119
+#: engines/scumm/metaengine.cpp:1143
 msgid "Switch weapon"
 msgstr "Cambiar de arma"
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1133
+#: engines/scumm/metaengine.cpp:1157
 msgid "Win the bike fight cheat"
 msgstr "Truco para ganar pelea de motos"
 
-#: engines/scumm/metaengine.cpp:1142
+#: engines/scumm/metaengine.cpp:1166
 msgid "Rebel Assault controls"
 msgstr "Controles de Rebel Assault"
 
-#: engines/scumm/metaengine.cpp:1144 engines/scumm/metaengine.cpp:1221
+#: engines/scumm/metaengine.cpp:1168 engines/scumm/metaengine.cpp:1245
 msgid "Aim up / menu up"
 msgstr "Apuntar arriba/Subir men??"
 
-#: engines/scumm/metaengine.cpp:1149 engines/scumm/metaengine.cpp:1226
+#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1250
 msgid "Aim down / menu down"
 msgstr "Apuntar abajo/Bajar men??"
 
-#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1231
+#: engines/scumm/metaengine.cpp:1178 engines/scumm/metaengine.cpp:1255
 msgid "Aim left / menu left"
 msgstr "Apuntar a izda./Men?? a izda."
 
-#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
+#: engines/scumm/metaengine.cpp:1183 engines/scumm/metaengine.cpp:1260
 msgid "Aim right / menu right"
 msgstr "Apuntar a dcha./Men?? a dcha."
 
-#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1241
+#: engines/scumm/metaengine.cpp:1188 engines/scumm/metaengine.cpp:1265
 msgid "Stick up"
 msgstr "Stick hacia arriba"
 
-#: engines/scumm/metaengine.cpp:1171 engines/scumm/metaengine.cpp:1248
+#: engines/scumm/metaengine.cpp:1195 engines/scumm/metaengine.cpp:1272
 msgid "Stick down"
 msgstr "Stick hacia abajo"
 
-#: engines/scumm/metaengine.cpp:1178 engines/scumm/metaengine.cpp:1255
+#: engines/scumm/metaengine.cpp:1202 engines/scumm/metaengine.cpp:1279
 msgid "Stick left"
 msgstr "Stick hacia la izquierda"
 
-#: engines/scumm/metaengine.cpp:1185 engines/scumm/metaengine.cpp:1262
+#: engines/scumm/metaengine.cpp:1209 engines/scumm/metaengine.cpp:1286
 msgid "Stick right"
 msgstr "Stick hacia la derecha"
 
-#: engines/scumm/metaengine.cpp:1192 engines/scumm/metaengine.cpp:1269
+#: engines/scumm/metaengine.cpp:1216 engines/scumm/metaengine.cpp:1293
 msgid "Fire / select"
 msgstr "Disparar/Seleccionar"
 
-#: engines/scumm/metaengine.cpp:1198
+#: engines/scumm/metaengine.cpp:1222
 msgid "Walk / menu back"
 msgstr "Andar/Volver (men??s)"
 
-#: engines/scumm/metaengine.cpp:1203
+#: engines/scumm/metaengine.cpp:1227
 msgid "Skip / menu back"
 msgstr "Omitir/Volver (men??s)"
 
-#: engines/scumm/metaengine.cpp:1219
+#: engines/scumm/metaengine.cpp:1243
 msgid "Rebel Assault II controls"
 msgstr "Controles de Rebel Assault II"
 
-#: engines/scumm/metaengine.cpp:1274
+#: engines/scumm/metaengine.cpp:1298
 msgid "Cover / back"
 msgstr "Cubrirse/Volver"
 
-#: engines/scumm/metaengine.cpp:1280
+#: engines/scumm/metaengine.cpp:1304
 msgid "Skip / back"
 msgstr "Omitir/Volver"
 
-#: engines/scumm/metaengine.cpp:1287
+#: engines/scumm/metaengine.cpp:1311
 msgid "Skip / menu"
 msgstr "Omitir/Men??"
 
@@ -13825,14 +13835,14 @@ msgstr ""
 "Para corregirla, se debe recortar la resoluci??n a 320x200. Esta opci??n est?? "
 "disponible en la pesta??a Motor."
 
-#: engines/scumm/scumm.cpp:1362
+#: engines/scumm/scumm.cpp:1365
 #, c-format
 msgid "This game requires the '%s' Macintosh executable for its fonts."
 msgstr ""
 "Este juego necesita el ejecutable para Macintosh ??%s?? para poder utilizar "
 "sus tipograf??as."
 
-#: engines/scumm/scumm.cpp:1371
+#: engines/scumm/scumm.cpp:1374
 #, c-format
 msgid ""
 "This game requires the '%s' Macintosh executable for its music and fonts."
@@ -13840,7 +13850,7 @@ msgstr ""
 "Este juego necesita el ejecutable para Macintosh ??%s?? para poder utilizar su "
 "m??sica y tipograf??as."
 
-#: engines/scumm/scumm.cpp:1375
+#: engines/scumm/scumm.cpp:1378
 #, c-format
 msgid ""
 "Could not find the '%s' Macintosh executable to read resources from. %s will "
@@ -13850,27 +13860,27 @@ msgstr ""
 "para obtener recursos. Se desactivar?? lo siguiente:\n"
 "%s"
 
-#: engines/scumm/scumm.cpp:1376
+#: engines/scumm/scumm.cpp:1379
 msgid "The Mac GUI"
 msgstr "Interfaz estilo Mac"
 
-#: engines/scumm/scumm.cpp:1376
+#: engines/scumm/scumm.cpp:1379
 msgid "The music and the Mac GUI"
 msgstr "Interfaz y m??sicas estilo Mac"
 
-#: engines/scumm/scumm.cpp:1405
+#: engines/scumm/scumm.cpp:1408
 #, c-format
 msgid "Could not open Macintosh resource file %s"
 msgstr "No se ha podido abrir el archivo de recursos de Macintosh %s"
 
-#: engines/scumm/scumm.cpp:1408
+#: engines/scumm/scumm.cpp:1411
 #, c-format
 msgid "Could not find resource fork in Macintosh resource file %s"
 msgstr ""
 "No se ha podido encontrar la bifurcaci??n de recursos en el archivo de "
 "recursos de Macintosh %s"
 
-#: engines/scumm/scumm.cpp:1415
+#: engines/scumm/scumm.cpp:1418
 #, c-format
 msgid ""
 "'%s' appears to be the wrong Dig executable. It may be the launcher one "
@@ -13883,7 +13893,7 @@ msgstr ""
 "men??s y ventanas necesarias. Busca una carpeta llamada ??The Dig f?? en tu CD. "
 "Cualquiera de los ejecutables que haya en las subcarpetas valdr??."
 
-#: engines/scumm/scumm.cpp:1581
+#: engines/scumm/scumm.cpp:1584
 msgid ""
 "It appears your ScummVM version was not built with TrueType Fonts support.\n"
 "\n"
@@ -13897,7 +13907,7 @@ msgstr ""
 "medida,\n"
 "faltar??n algunos gr??ficos."
 
-#: engines/scumm/scumm.cpp:1915
+#: engines/scumm/scumm.cpp:1918
 msgid ""
 "Audio files compressed with ScummVM Tools were detected; *.BUN/*.SOU\n"
 "compression is not supported anymore for this game, audio will be disabled.\n"
@@ -13908,7 +13918,7 @@ msgstr ""
 "para este juego, as?? que el audio ser?? desactivado.\n"
 "Vuelve a copiar el juego a partir de los archivos originales sin comprimir."
 
-#: engines/scumm/scumm.cpp:2405
+#: engines/scumm/scumm.cpp:2408
 #, c-format
 msgid ""
 "Native MIDI support requires the Roland Upgrade from LucasArts,\n"
@@ -13917,7 +13927,7 @@ msgstr ""
 "El soporte MIDI nativo requiere la actualizaci??n Roland de LucasArts,\n"
 "pero %s no est?? disponible. Se usar?? AdLib."
 
-#: engines/scumm/scumm.cpp:2420
+#: engines/scumm/scumm.cpp:2423
 msgid ""
 "This particular version of Monkey Island 1 is known to miss some\n"
 "required resources for MT-32. Using AdLib instead."
@@ -13926,7 +13936,7 @@ msgstr ""
 "por no tener ciertos elementos necesarios para MT-32.\n"
 "Utilizando AdLib en su lugar."
 
-#: engines/scumm/scumm.cpp:4361
+#: engines/scumm/scumm.cpp:4364
 msgid ""
 "Usually, Maniac Mansion would start now. But for that to work, the game "
 "files for Maniac Mansion have to be in the 'Maniac' directory inside the "
@@ -13936,7 +13946,7 @@ msgstr ""
 "posible debes mover los archivos de Maniac Mansion al directorio ??Maniac??, "
 "dentro del directorio de DOTT, y a??adir el juego a ScummVM."
 
-#: engines/scumm/scumm.cpp:4485
+#: engines/scumm/scumm.cpp:4488
 msgid "Would you like to host or join a network play session?"
 msgstr "??Quieres crear o unirte a una sesi??n de juego en red?"
 
diff --git a/po/eu.po b/po/eu.po
index 3c745d0b557..fff83223272 100644
--- a/po/eu.po
+++ b/po/eu.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.5.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-08 11:04+0000\n"
+"POT-Creation-Date: 2026-06-09 13:22+0000\n"
 "PO-Revision-Date: 2024-02-05 18:42+0000\n"
 "Last-Translator: Josu Igoa <josuigoa at ni.eus>\n"
 "Language-Team: Basque <https://translations.scummvm.org/projects/scummvm/"
@@ -340,7 +340,7 @@ msgstr "Beste biltegiratzen bat dago orain martxan. Gelditu nahi al duzu?"
 #: engines/hypno/metaengine.cpp:192 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:197
 #: engines/mm/mm1/metaengine.cpp:54 engines/parallaction/metaengine.cpp:169
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4467
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
 #: engines/sherlock/metaengine.cpp:638 engines/sherlock/metaengine.cpp:1244
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:328
@@ -359,7 +359,7 @@ msgstr "Bai"
 #: engines/hypno/metaengine.cpp:198 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:204
 #: engines/mm/mm1/metaengine.cpp:52 engines/parallaction/metaengine.cpp:175
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4467
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
 #: engines/sherlock/metaengine.cpp:643 engines/sherlock/metaengine.cpp:1250
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:329
@@ -387,7 +387,7 @@ msgstr "JSON kodearen edukia ez dago ongi osatuta."
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:123
 #: engines/drascula/saveload.cpp:51 engines/grim/inputdialog.cpp:83
 #: engines/parallaction/saveload.cpp:270 engines/sci/engine/kgraphics.cpp:76
-#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4479
+#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4482
 #: engines/sword1/control.cpp:2816
 msgid "OK"
 msgstr "Ados"
@@ -1044,7 +1044,7 @@ msgstr "Elkarreragin"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1100
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1124
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1065,7 +1065,7 @@ msgstr "Gora"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1067
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1091
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1086,7 +1086,7 @@ msgstr "Behera"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1080
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1104
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1106,7 +1106,7 @@ msgstr "Ezker"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1087
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1111
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3410,7 +3410,7 @@ msgstr "Abendura grafikoen motorea"
 #: engines/mohawk/riven_stacks/aspit.cpp:454
 #: engines/neverhood/metaengine.cpp:200 engines/saga/metaengine.cpp:449
 #: engines/scumm/help.cpp:84 engines/scumm/help.cpp:86
-#: engines/scumm/scumm.cpp:4479 engines/sludge/keymapper_tables.h:95
+#: engines/scumm/scumm.cpp:4482 engines/sludge/keymapper_tables.h:95
 #: engines/sludge/keymapper_tables.h:144 engines/sludge/keymapper_tables.h:223
 #: engines/sludge/keymapper_tables.h:343 engines/sludge/keymapper_tables.h:355
 #: engines/sludge/keymapper_tables.h:416 engines/sludge/keymapper_tables.h:429
@@ -3898,7 +3898,7 @@ msgid "Game not implemented"
 msgstr "Jokoaren ID ez-onartua"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
-#: engines/scumm/metaengine.cpp:1050
+#: engines/scumm/metaengine.cpp:1074
 #, fuzzy
 msgid "Default game keymappings"
 msgstr "~A~ukerak..."
@@ -5919,7 +5919,7 @@ msgstr ""
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1208 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1232 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "Menua"
 
@@ -6891,7 +6891,7 @@ msgstr ""
 msgid "Quick load or save game isn't supported in this location"
 msgstr ""
 
-#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3418
+#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3421
 #, fuzzy, c-format
 msgid ""
 "Failed to load saved game from file:\n"
@@ -6902,7 +6902,7 @@ msgstr ""
 "\n"
 "%s"
 
-#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3405
+#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3408
 #, fuzzy, c-format
 msgid ""
 "Failed to save game to file:\n"
@@ -6913,7 +6913,7 @@ msgstr ""
 "\n"
 "%s"
 
-#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3438
+#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3441
 #, fuzzy, c-format
 msgid ""
 "Successfully saved game in file:\n"
@@ -7409,7 +7409,7 @@ msgstr "Mututu/Ez-mututu"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1126 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -8156,7 +8156,7 @@ msgstr "Elkarreragin"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1113
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1137
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -10374,7 +10374,7 @@ msgid "Player ammo will never decrease."
 msgstr ""
 
 #: engines/hypno/metaengine.cpp:70 engines/scumm/metaengine.cpp:891
-#: engines/scumm/metaengine.cpp:909
+#: engines/scumm/metaengine.cpp:918
 msgid "Unlock all levels"
 msgstr ""
 
@@ -12836,7 +12836,7 @@ msgid "Your Name:"
 msgstr "Izena:"
 
 #. I18N: Button, start hosting online multiplayer game
-#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4485
+#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4488
 msgid "Host"
 msgstr ""
 
@@ -12848,7 +12848,7 @@ msgid "Querying games..."
 msgstr "Jokoa gordetzen..."
 
 #. I18N: Join online multiplayer game
-#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4485
+#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4488
 msgid "Join"
 msgstr ""
 
@@ -13778,126 +13778,135 @@ msgstr "Gaitu erresoluzio altuko grafikoak"
 msgid "Run the game in 640x400 high resolution mode instead of 320x200"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:910
+#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:919
 msgid "All levels will be available without requiring passwords"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:900
+#: engines/scumm/metaengine.cpp:900 engines/scumm/metaengine.cpp:927
 #, fuzzy
 #| msgid "No date saved"
 msgid "No damage"
 msgstr "Ez dago datarik gordeta"
 
-#: engines/scumm/metaengine.cpp:901
+#: engines/scumm/metaengine.cpp:901 engines/scumm/metaengine.cpp:928
 #, fuzzy
 msgid "Disable player damage"
 msgstr "Ezin izan da jokoa gorde"
 
-#: engines/scumm/metaengine.cpp:1061
+#: engines/scumm/metaengine.cpp:909
+#, fuzzy
+msgid "Yoda mode"
+msgstr "Modu bizkorra"
+
+#: engines/scumm/metaengine.cpp:910
+msgid "Enable original Yoda mode shortcuts, including movie mode and auto play"
+msgstr ""
+
+#: engines/scumm/metaengine.cpp:1085
 #, fuzzy
 msgid "Down left"
 msgstr "Ezkerrera biratu"
 
-#: engines/scumm/metaengine.cpp:1074
+#: engines/scumm/metaengine.cpp:1098
 #, fuzzy
 msgid "Down right"
 msgstr "Eskuinera biratu"
 
-#: engines/scumm/metaengine.cpp:1094
+#: engines/scumm/metaengine.cpp:1118
 #, fuzzy
 #| msgid "Upper left item"
 msgid "Up left"
 msgstr "Goiko ezkerreko objektua"
 
-#: engines/scumm/metaengine.cpp:1107
+#: engines/scumm/metaengine.cpp:1131
 #, fuzzy
 msgid "Up right"
 msgstr "Eskuin"
 
-#: engines/scumm/metaengine.cpp:1119
+#: engines/scumm/metaengine.cpp:1143
 #, fuzzy
 msgid "Switch weapon"
 msgstr "Pertsonaia aldatu:"
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1133
+#: engines/scumm/metaengine.cpp:1157
 msgid "Win the bike fight cheat"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1142
+#: engines/scumm/metaengine.cpp:1166
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1144 engines/scumm/metaengine.cpp:1221
+#: engines/scumm/metaengine.cpp:1168 engines/scumm/metaengine.cpp:1245
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1149 engines/scumm/metaengine.cpp:1226
+#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1250
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1231
+#: engines/scumm/metaengine.cpp:1178 engines/scumm/metaengine.cpp:1255
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
+#: engines/scumm/metaengine.cpp:1183 engines/scumm/metaengine.cpp:1260
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1241
+#: engines/scumm/metaengine.cpp:1188 engines/scumm/metaengine.cpp:1265
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "Jaso"
 
-#: engines/scumm/metaengine.cpp:1171 engines/scumm/metaengine.cpp:1248
+#: engines/scumm/metaengine.cpp:1195 engines/scumm/metaengine.cpp:1272
 #, fuzzy
 msgid "Stick down"
 msgstr "Behera"
 
-#: engines/scumm/metaengine.cpp:1178 engines/scumm/metaengine.cpp:1255
+#: engines/scumm/metaengine.cpp:1202 engines/scumm/metaengine.cpp:1279
 #, fuzzy
 #| msgid "Slide Left"
 msgid "Stick left"
 msgstr "Ezkerrera irristatu"
 
-#: engines/scumm/metaengine.cpp:1185 engines/scumm/metaengine.cpp:1262
+#: engines/scumm/metaengine.cpp:1209 engines/scumm/metaengine.cpp:1286
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick right"
 msgstr "Eskuinera irristatu"
 
-#: engines/scumm/metaengine.cpp:1192 engines/scumm/metaengine.cpp:1269
+#: engines/scumm/metaengine.cpp:1216 engines/scumm/metaengine.cpp:1293
 #, fuzzy
 msgid "Fire / select"
 msgstr "Ezkerrera irristatu"
 
-#: engines/scumm/metaengine.cpp:1198
+#: engines/scumm/metaengine.cpp:1222
 #, fuzzy
 msgid "Walk / menu back"
 msgstr "It~z~uli abiarazlera"
 
-#: engines/scumm/metaengine.cpp:1203
+#: engines/scumm/metaengine.cpp:1227
 #, fuzzy
 msgid "Skip / menu back"
 msgstr "It~z~uli abiarazlera"
 
-#: engines/scumm/metaengine.cpp:1219
+#: engines/scumm/metaengine.cpp:1243
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1274
+#: engines/scumm/metaengine.cpp:1298
 #, fuzzy
 #| msgid "Move Back"
 msgid "Cover / back"
 msgstr "Atzera mugitu"
 
-#: engines/scumm/metaengine.cpp:1280
+#: engines/scumm/metaengine.cpp:1304
 #, fuzzy
 msgid "Skip / back"
 msgstr "Lerroa saltatu"
 
-#: engines/scumm/metaengine.cpp:1287
+#: engines/scumm/metaengine.cpp:1311
 #, fuzzy
 msgid "Skip / menu"
 msgstr "It~z~uli abiarazlera"
@@ -13924,18 +13933,18 @@ msgid ""
 "320x200, under 'engine' tab."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1362
+#: engines/scumm/scumm.cpp:1365
 #, c-format
 msgid "This game requires the '%s' Macintosh executable for its fonts."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1371
+#: engines/scumm/scumm.cpp:1374
 #, c-format
 msgid ""
 "This game requires the '%s' Macintosh executable for its music and fonts."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1375
+#: engines/scumm/scumm.cpp:1378
 #, fuzzy, c-format
 #| msgid ""
 #| "Could not find the 'Monkey Island' Macintosh executable to read the\n"
@@ -13947,25 +13956,25 @@ msgstr ""
 "Ezin izan da 'Monkey Island' Macintosh exekutagarria aurkitu instrumentuak\n"
 "bertatik irakurtzeko. Musika desgaituta egongo da."
 
-#: engines/scumm/scumm.cpp:1376
+#: engines/scumm/scumm.cpp:1379
 msgid "The Mac GUI"
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1376
+#: engines/scumm/scumm.cpp:1379
 msgid "The music and the Mac GUI"
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1405
+#: engines/scumm/scumm.cpp:1408
 #, c-format
 msgid "Could not open Macintosh resource file %s"
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1408
+#: engines/scumm/scumm.cpp:1411
 #, c-format
 msgid "Could not find resource fork in Macintosh resource file %s"
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1415
+#: engines/scumm/scumm.cpp:1418
 #, c-format
 msgid ""
 "'%s' appears to be the wrong Dig executable. It may be the launcher one "
@@ -13974,7 +13983,7 @@ msgid ""
 "its sub-folders should be what you need."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1581
+#: engines/scumm/scumm.cpp:1584
 msgid ""
 "It appears your ScummVM version was not built with TrueType Fonts support.\n"
 "\n"
@@ -13982,14 +13991,14 @@ msgid ""
 "some of the graphics on screen will be missing."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1915
+#: engines/scumm/scumm.cpp:1918
 msgid ""
 "Audio files compressed with ScummVM Tools were detected; *.BUN/*.SOU\n"
 "compression is not supported anymore for this game, audio will be disabled.\n"
 "Please copy the game from the original media without compression."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:2405
+#: engines/scumm/scumm.cpp:2408
 #, c-format
 msgid ""
 "Native MIDI support requires the Roland Upgrade from LucasArts,\n"
@@ -13998,13 +14007,13 @@ msgstr ""
 "MIDI euskarri natiboak LucasArts-en Roland eguneraketa behar du,\n"
 "baina %s ez dago eskuragarri. AdLib erabiliko da."
 
-#: engines/scumm/scumm.cpp:2420
+#: engines/scumm/scumm.cpp:2423
 msgid ""
 "This particular version of Monkey Island 1 is known to miss some\n"
 "required resources for MT-32. Using AdLib instead."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:4361
+#: engines/scumm/scumm.cpp:4364
 msgid ""
 "Usually, Maniac Mansion would start now. But for that to work, the game "
 "files for Maniac Mansion have to be in the 'Maniac' directory inside the "
@@ -14014,7 +14023,7 @@ msgstr ""
 "fitxategiak 'Maniac' direktorio barruan egon behar dira, Tentacle jokoko "
 "direktorioan, eta jokoa ScummVM-ra gehitu behar da."
 
-#: engines/scumm/scumm.cpp:4485
+#: engines/scumm/scumm.cpp:4488
 #, fuzzy
 msgid "Would you like to host or join a network play session?"
 msgstr "Jokoa kargatu edo gorde nahi duzu?"
diff --git a/po/fi_FI.po b/po/fi_FI.po
index 1231163dea8..37dff557f4f 100644
--- a/po/fi_FI.po
+++ b/po/fi_FI.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.6.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-08 11:04+0000\n"
+"POT-Creation-Date: 2026-06-09 13:22+0000\n"
 "PO-Revision-Date: 2026-02-07 15:22+0000\n"
 "Last-Translator: Timo Mikkolainen <tmikkola at gmail.com>\n"
 "Language-Team: Finnish <https://translations.scummvm.org/projects/scummvm/"
@@ -340,7 +340,7 @@ msgstr "Toinen tallennustila on aktiivinen. Haluatko keskeytt???? sen?"
 #: engines/hypno/metaengine.cpp:192 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:197
 #: engines/mm/mm1/metaengine.cpp:54 engines/parallaction/metaengine.cpp:169
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4467
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
 #: engines/sherlock/metaengine.cpp:638 engines/sherlock/metaengine.cpp:1244
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:328
@@ -359,7 +359,7 @@ msgstr "Kyll??"
 #: engines/hypno/metaengine.cpp:198 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:204
 #: engines/mm/mm1/metaengine.cpp:52 engines/parallaction/metaengine.cpp:175
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4467
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
 #: engines/sherlock/metaengine.cpp:643 engines/sherlock/metaengine.cpp:1250
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:329
@@ -387,7 +387,7 @@ msgstr "JSON-koodin sis??lt?? on ep??muodostunutta."
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:123
 #: engines/drascula/saveload.cpp:51 engines/grim/inputdialog.cpp:83
 #: engines/parallaction/saveload.cpp:270 engines/sci/engine/kgraphics.cpp:76
-#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4479
+#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4482
 #: engines/sword1/control.cpp:2816
 msgid "OK"
 msgstr "OK"
@@ -1052,7 +1052,7 @@ msgstr "K??yt??"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1100
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1124
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1073,7 +1073,7 @@ msgstr "Yl??s"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1067
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1091
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1094,7 +1094,7 @@ msgstr "Alas"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1080
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1104
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1114,7 +1114,7 @@ msgstr "Vasen"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1087
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1111
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3453,7 +3453,7 @@ msgstr "Pelimoottori graafisille seikkailupeleille"
 #: engines/mohawk/riven_stacks/aspit.cpp:454
 #: engines/neverhood/metaengine.cpp:200 engines/saga/metaengine.cpp:449
 #: engines/scumm/help.cpp:84 engines/scumm/help.cpp:86
-#: engines/scumm/scumm.cpp:4479 engines/sludge/keymapper_tables.h:95
+#: engines/scumm/scumm.cpp:4482 engines/sludge/keymapper_tables.h:95
 #: engines/sludge/keymapper_tables.h:144 engines/sludge/keymapper_tables.h:223
 #: engines/sludge/keymapper_tables.h:343 engines/sludge/keymapper_tables.h:355
 #: engines/sludge/keymapper_tables.h:416 engines/sludge/keymapper_tables.h:429
@@ -3950,7 +3950,7 @@ msgid "Game not implemented"
 msgstr "Pelill?? ei ole toteutusta"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
-#: engines/scumm/metaengine.cpp:1050
+#: engines/scumm/metaengine.cpp:1074
 #, fuzzy
 #| msgid "Default game keymap"
 msgid "Default game keymappings"
@@ -6263,7 +6263,7 @@ msgstr "R2"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1208 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1232 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "Valikko"
 
@@ -7206,7 +7206,7 @@ msgstr ""
 msgid "Quick load or save game isn't supported in this location"
 msgstr "Pelin pikalataus tai tallennus ei ole tuettu t??ss?? sijainnissa"
 
-#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3418
+#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3421
 #, c-format
 msgid ""
 "Failed to load saved game from file:\n"
@@ -7217,7 +7217,7 @@ msgstr ""
 "\n"
 "%s"
 
-#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3405
+#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3408
 #, c-format
 msgid ""
 "Failed to save game to file:\n"
@@ -7228,7 +7228,7 @@ msgstr ""
 "\n"
 "%s"
 
-#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3438
+#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3441
 #, c-format
 msgid ""
 "Successfully saved game in file:\n"
@@ -7725,7 +7725,7 @@ msgstr "Taistelumoodi p????lle / pois"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1126 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -8423,7 +8423,7 @@ msgstr "Puhu / Vuorovaikuta"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1113
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1137
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -10498,7 +10498,7 @@ msgid "Player ammo will never decrease."
 msgstr "Pelaajan ammukset eiv??t v??hene."
 
 #: engines/hypno/metaengine.cpp:70 engines/scumm/metaengine.cpp:891
-#: engines/scumm/metaengine.cpp:909
+#: engines/scumm/metaengine.cpp:918
 msgid "Unlock all levels"
 msgstr "Avaa kaikki tasot"
 
@@ -12936,7 +12936,7 @@ msgid "Your Name:"
 msgstr "Sinun nimesi:"
 
 #. I18N: Button, start hosting online multiplayer game
-#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4485
+#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4488
 msgid "Host"
 msgstr "Is??nn??i"
 
@@ -12946,7 +12946,7 @@ msgid "Querying games..."
 msgstr "Etsit????n pelej??..."
 
 #. I18N: Join online multiplayer game
-#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4485
+#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4488
 msgid "Join"
 msgstr "Liity"
 
@@ -13912,128 +13912,138 @@ msgstr "K??yt?? korkeaa resoluutiota"
 msgid "Run the game in 640x400 high resolution mode instead of 320x200"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:910
+#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:919
 #, fuzzy
 #| msgid "All levels will be available to play."
 msgid "All levels will be available without requiring passwords"
 msgstr "Kaikki tasot ovat pelattavissa."
 
-#: engines/scumm/metaengine.cpp:900
+#: engines/scumm/metaengine.cpp:900 engines/scumm/metaengine.cpp:927
 #, fuzzy
 #| msgid "No date saved"
 msgid "No damage"
 msgstr "P??iv??yst?? ei ole tallennettu"
 
-#: engines/scumm/metaengine.cpp:901
+#: engines/scumm/metaengine.cpp:901 engines/scumm/metaengine.cpp:928
 #, fuzzy
 #| msgid "Disable save menu"
 msgid "Disable player damage"
 msgstr "Tallennusvalikko pois p????lt??"
 
-#: engines/scumm/metaengine.cpp:1061
+#: engines/scumm/metaengine.cpp:909
+#, fuzzy
+#| msgid "Fast mode"
+msgid "Yoda mode"
+msgstr "Nopea tila"
+
+#: engines/scumm/metaengine.cpp:910
+msgid "Enable original Yoda mode shortcuts, including movie mode and auto play"
+msgstr ""
+
+#: engines/scumm/metaengine.cpp:1085
 msgid "Down left"
 msgstr "Alavasemmalle"
 
-#: engines/scumm/metaengine.cpp:1074
+#: engines/scumm/metaengine.cpp:1098
 msgid "Down right"
 msgstr "Alaoikealle"
 
-#: engines/scumm/metaengine.cpp:1094
+#: engines/scumm/metaengine.cpp:1118
 msgid "Up left"
 msgstr "Yl??vasemmalle"
 
-#: engines/scumm/metaengine.cpp:1107
+#: engines/scumm/metaengine.cpp:1131
 msgid "Up right"
 msgstr "Yl??oikealle"
 
-#: engines/scumm/metaengine.cpp:1119
+#: engines/scumm/metaengine.cpp:1143
 msgid "Switch weapon"
 msgstr "Vaihda asetta"
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1133
+#: engines/scumm/metaengine.cpp:1157
 msgid "Win the bike fight cheat"
 msgstr "Voita moottoripy??r??taistelu-huijaus"
 
-#: engines/scumm/metaengine.cpp:1142
+#: engines/scumm/metaengine.cpp:1166
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1144 engines/scumm/metaengine.cpp:1221
+#: engines/scumm/metaengine.cpp:1168 engines/scumm/metaengine.cpp:1245
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1149 engines/scumm/metaengine.cpp:1226
+#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1250
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1231
+#: engines/scumm/metaengine.cpp:1178 engines/scumm/metaengine.cpp:1255
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
+#: engines/scumm/metaengine.cpp:1183 engines/scumm/metaengine.cpp:1260
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1241
+#: engines/scumm/metaengine.cpp:1188 engines/scumm/metaengine.cpp:1265
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "Ota"
 
-#: engines/scumm/metaengine.cpp:1171 engines/scumm/metaengine.cpp:1248
+#: engines/scumm/metaengine.cpp:1195 engines/scumm/metaengine.cpp:1272
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "Vierit?? alas"
 
-#: engines/scumm/metaengine.cpp:1178 engines/scumm/metaengine.cpp:1255
+#: engines/scumm/metaengine.cpp:1202 engines/scumm/metaengine.cpp:1279
 #, fuzzy
 #| msgid "Slide left"
 msgid "Stick left"
 msgstr "Liu'u vasemmalle"
 
-#: engines/scumm/metaengine.cpp:1185 engines/scumm/metaengine.cpp:1262
+#: engines/scumm/metaengine.cpp:1209 engines/scumm/metaengine.cpp:1286
 #, fuzzy
 #| msgid "Slide right"
 msgid "Stick right"
 msgstr "Liu'u oikealle"
 
-#: engines/scumm/metaengine.cpp:1192 engines/scumm/metaengine.cpp:1269
+#: engines/scumm/metaengine.cpp:1216 engines/scumm/metaengine.cpp:1293
 #, fuzzy
 #| msgid "Move / Select"
 msgid "Fire / select"
 msgstr "Liiku / Valitse"
 
-#: engines/scumm/metaengine.cpp:1198
+#: engines/scumm/metaengine.cpp:1222
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Walk / menu back"
 msgstr "Ohita p????valikko"
 
-#: engines/scumm/metaengine.cpp:1203
+#: engines/scumm/metaengine.cpp:1227
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu back"
 msgstr "Ohita p????valikko"
 
-#: engines/scumm/metaengine.cpp:1219
+#: engines/scumm/metaengine.cpp:1243
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1274
+#: engines/scumm/metaengine.cpp:1298
 #, fuzzy
 #| msgid "Move back"
 msgid "Cover / back"
 msgstr "Liiku taaksep??in"
 
-#: engines/scumm/metaengine.cpp:1280
+#: engines/scumm/metaengine.cpp:1304
 #, fuzzy
 #| msgid "Skip walk"
 msgid "Skip / back"
 msgstr "Ohita k??vely"
 
-#: engines/scumm/metaengine.cpp:1287
+#: engines/scumm/metaengine.cpp:1311
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu"
@@ -14074,12 +14084,12 @@ msgstr ""
 "Kuvasuhteen korjaus saadaan mahdolliseksi trimmaamalla resoluution "
 "320x200:aan 'Pelimoottori'-v??lilehdell??."
 
-#: engines/scumm/scumm.cpp:1362
+#: engines/scumm/scumm.cpp:1365
 #, c-format
 msgid "This game requires the '%s' Macintosh executable for its fonts."
 msgstr "T??m?? peli tarvitsee '%s' Macintosh-ohjelmatiedoston fonteilleen."
 
-#: engines/scumm/scumm.cpp:1371
+#: engines/scumm/scumm.cpp:1374
 #, c-format
 msgid ""
 "This game requires the '%s' Macintosh executable for its music and fonts."
@@ -14087,7 +14097,7 @@ msgstr ""
 "T??m?? peli tarvitsee '%s' Macintosh-ohjelmatiedoston musiikilleen ja "
 "fonteilleen."
 
-#: engines/scumm/scumm.cpp:1375
+#: engines/scumm/scumm.cpp:1378
 #, c-format
 msgid ""
 "Could not find the '%s' Macintosh executable to read resources from. %s will "
@@ -14096,25 +14106,25 @@ msgstr ""
 "Macintosh-ohjelmatiedostoa \"%s\" ei l??ydetty resurssien lukemiseksi. %s "
 "tulee olemaan pois k??yt??st??."
 
-#: engines/scumm/scumm.cpp:1376
+#: engines/scumm/scumm.cpp:1379
 msgid "The Mac GUI"
 msgstr "Mac-k??ytt??liittym??"
 
-#: engines/scumm/scumm.cpp:1376
+#: engines/scumm/scumm.cpp:1379
 msgid "The music and the Mac GUI"
 msgstr "Musiikki ja Mac-k??ytt??liittym??"
 
-#: engines/scumm/scumm.cpp:1405
+#: engines/scumm/scumm.cpp:1408
 #, c-format
 msgid "Could not open Macintosh resource file %s"
 msgstr "Macintosh-resurssitiedoston %s avaaminen ep??onnistui"
 
-#: engines/scumm/scumm.cpp:1408
+#: engines/scumm/scumm.cpp:1411
 #, c-format
 msgid "Could not find resource fork in Macintosh resource file %s"
 msgstr "Macintosh-resurssitiedostosta %s ei l??ytynyt resource fork:ia"
 
-#: engines/scumm/scumm.cpp:1415
+#: engines/scumm/scumm.cpp:1418
 #, c-format
 msgid ""
 "'%s' appears to be the wrong Dig executable. It may be the launcher one "
@@ -14127,7 +14137,7 @@ msgstr ""
 "tarvituista menu- ja dialogim????rityksist??. Etsi CD:lt??si 'The Dig f'-"
 "hakemisto. Mink?? tahansa sen alahakemistoista pit??isi toimia."
 
-#: engines/scumm/scumm.cpp:1581
+#: engines/scumm/scumm.cpp:1584
 msgid ""
 "It appears your ScummVM version was not built with TrueType Fonts support.\n"
 "\n"
@@ -14135,7 +14145,7 @@ msgid ""
 "some of the graphics on screen will be missing."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1915
+#: engines/scumm/scumm.cpp:1918
 msgid ""
 "Audio files compressed with ScummVM Tools were detected; *.BUN/*.SOU\n"
 "compression is not supported anymore for this game, audio will be disabled.\n"
@@ -14145,7 +14155,7 @@ msgstr ""
 "pakkaukset eiv??t en???? ole tuettuja t??ss?? peliss??, joten ????net eiv??t kuulu.\n"
 "Ole hyv?? ja asenna peli uudelleen sopivien ????nitiedostojen kera."
 
-#: engines/scumm/scumm.cpp:2405
+#: engines/scumm/scumm.cpp:2408
 #, c-format
 msgid ""
 "Native MIDI support requires the Roland Upgrade from LucasArts,\n"
@@ -14154,7 +14164,7 @@ msgstr ""
 "Suora MIDI tuki vaatii Roland p??ivityksen LucasArtsilta, mutta\n"
 "%s puuttuu. K??ytet????n AdLibia sen sijaan."
 
-#: engines/scumm/scumm.cpp:2420
+#: engines/scumm/scumm.cpp:2423
 msgid ""
 "This particular version of Monkey Island 1 is known to miss some\n"
 "required resources for MT-32. Using AdLib instead."
@@ -14163,7 +14173,7 @@ msgstr ""
 "vaadittuja k??yt??n mahdollistavia osasia MT-32:lle. Otetaan sen sijasta "
 "k??ytt????n AdLib."
 
-#: engines/scumm/scumm.cpp:4361
+#: engines/scumm/scumm.cpp:4364
 msgid ""
 "Usually, Maniac Mansion would start now. But for that to work, the game "
 "files for Maniac Mansion have to be in the 'Maniac' directory inside the "
@@ -14173,7 +14183,7 @@ msgstr ""
 "Maniac Mansionin pelitiedostojen pit???? l??yty?? \"Maniac\"-kansiosta Tentacle-"
 "pelikansion sis??lt??, ja sen pit???? my??s olla lis??tty pelin?? ScummVM:????n."
 
-#: engines/scumm/scumm.cpp:4485
+#: engines/scumm/scumm.cpp:4488
 msgid "Would you like to host or join a network play session?"
 msgstr "Haluatko is??nn??id?? vai liitty?? verkkopeliin?"
 
diff --git a/po/fr_FR.po b/po/fr_FR.po
index 2753f9b320c..888fa81f1db 100644
--- a/po/fr_FR.po
+++ b/po/fr_FR.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-08 11:04+0000\n"
+"POT-Creation-Date: 2026-06-09 13:22+0000\n"
 "PO-Revision-Date: 2026-06-08 16:22+0000\n"
 "Last-Translator: Purple T <ZEONK at hotmail.com>\n"
 "Language-Team: French <https://translations.scummvm.org/projects/scummvm/"
@@ -341,7 +341,7 @@ msgstr ""
 #: engines/hypno/metaengine.cpp:192 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:197
 #: engines/mm/mm1/metaengine.cpp:54 engines/parallaction/metaengine.cpp:169
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4467
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
 #: engines/sherlock/metaengine.cpp:638 engines/sherlock/metaengine.cpp:1244
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:328
@@ -360,7 +360,7 @@ msgstr "Oui"
 #: engines/hypno/metaengine.cpp:198 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:204
 #: engines/mm/mm1/metaengine.cpp:52 engines/parallaction/metaengine.cpp:175
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4467
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
 #: engines/sherlock/metaengine.cpp:643 engines/sherlock/metaengine.cpp:1250
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:329
@@ -388,7 +388,7 @@ msgstr "Les contenus du code JSON sont malform??s."
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:123
 #: engines/drascula/saveload.cpp:51 engines/grim/inputdialog.cpp:83
 #: engines/parallaction/saveload.cpp:270 engines/sci/engine/kgraphics.cpp:76
-#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4479
+#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4482
 #: engines/sword1/control.cpp:2816
 msgid "OK"
 msgstr "OK"
@@ -1063,7 +1063,7 @@ msgstr "Interagir"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1100
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1124
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1084,7 +1084,7 @@ msgstr "Haut"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1067
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1091
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1105,7 +1105,7 @@ msgstr "Bas"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1080
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1104
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1125,7 +1125,7 @@ msgstr "Gauche"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1087
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1111
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3444,7 +3444,7 @@ msgstr "Moteur de jeux d'aventure graphiques"
 #: engines/mohawk/riven_stacks/aspit.cpp:454
 #: engines/neverhood/metaengine.cpp:200 engines/saga/metaengine.cpp:449
 #: engines/scumm/help.cpp:84 engines/scumm/help.cpp:86
-#: engines/scumm/scumm.cpp:4479 engines/sludge/keymapper_tables.h:95
+#: engines/scumm/scumm.cpp:4482 engines/sludge/keymapper_tables.h:95
 #: engines/sludge/keymapper_tables.h:144 engines/sludge/keymapper_tables.h:223
 #: engines/sludge/keymapper_tables.h:343 engines/sludge/keymapper_tables.h:355
 #: engines/sludge/keymapper_tables.h:416 engines/sludge/keymapper_tables.h:429
@@ -3955,7 +3955,7 @@ msgid "Game not implemented"
 msgstr "Jeu non impl??ment??"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
-#: engines/scumm/metaengine.cpp:1050
+#: engines/scumm/metaengine.cpp:1074
 msgid "Default game keymappings"
 msgstr "Conf. de touches par d??faut"
 
@@ -6181,7 +6181,7 @@ msgstr "R2"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1208 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1232 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "Menu"
 
@@ -7129,7 +7129,7 @@ msgstr ""
 msgid "Quick load or save game isn't supported in this location"
 msgstr "Le chargement ou sauvegarde rapide n'est pas possible ?? cet endroit"
 
-#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3418
+#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3421
 #, c-format
 msgid ""
 "Failed to load saved game from file:\n"
@@ -7140,7 +7140,7 @@ msgstr ""
 "\n"
 "%s"
 
-#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3405
+#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3408
 #, c-format
 msgid ""
 "Failed to save game to file:\n"
@@ -7151,7 +7151,7 @@ msgstr ""
 "\n"
 "%s"
 
-#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3438
+#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3441
 #, c-format
 msgid ""
 "Successfully saved game in file:\n"
@@ -7647,7 +7647,7 @@ msgstr "Act. / D??sact. combat"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1126 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -8311,7 +8311,7 @@ msgstr "Parler / Interagir"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1113
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1137
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -10303,7 +10303,7 @@ msgid "Player ammo will never decrease."
 msgstr "La quantit?? de munitions ne baissera jamais."
 
 #: engines/hypno/metaengine.cpp:70 engines/scumm/metaengine.cpp:891
-#: engines/scumm/metaengine.cpp:909
+#: engines/scumm/metaengine.cpp:918
 msgid "Unlock all levels"
 msgstr "D??bloquer tous les niveaux"
 
@@ -12719,7 +12719,7 @@ msgid "Your Name:"
 msgstr "Votre nom :"
 
 #. I18N: Button, start hosting online multiplayer game
-#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4485
+#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4488
 msgid "Host"
 msgstr "H??berger"
 
@@ -12729,7 +12729,7 @@ msgid "Querying games..."
 msgstr "Recherche de parties..."
 
 #. I18N: Join online multiplayer game
-#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4485
+#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4488
 msgid "Join"
 msgstr "Rejoindre"
 
@@ -13712,104 +13712,114 @@ msgstr "Mode Haute r??solution"
 msgid "Run the game in 640x400 high resolution mode instead of 320x200"
 msgstr "Lance le jeu en 640x400 haute r??solution au lieu de 320x200"
 
-#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:910
+#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:919
 msgid "All levels will be available without requiring passwords"
 msgstr "Tous les niveaux seront disponibles sans demander de mots de passe"
 
-#: engines/scumm/metaengine.cpp:900
+#: engines/scumm/metaengine.cpp:900 engines/scumm/metaengine.cpp:927
 msgid "No damage"
 msgstr "Aucun d??g??t"
 
-#: engines/scumm/metaengine.cpp:901
+#: engines/scumm/metaengine.cpp:901 engines/scumm/metaengine.cpp:928
 msgid "Disable player damage"
 msgstr "D??sactive les d??g??ts re??us par le joueur"
 
-#: engines/scumm/metaengine.cpp:1061
+#: engines/scumm/metaengine.cpp:909
+#, fuzzy
+#| msgid "Fast mode"
+msgid "Yoda mode"
+msgstr "Mode rapide"
+
+#: engines/scumm/metaengine.cpp:910
+msgid "Enable original Yoda mode shortcuts, including movie mode and auto play"
+msgstr ""
+
+#: engines/scumm/metaengine.cpp:1085
 msgid "Down left"
 msgstr "Bas gauche"
 
-#: engines/scumm/metaengine.cpp:1074
+#: engines/scumm/metaengine.cpp:1098
 msgid "Down right"
 msgstr "Bas droite"
 
-#: engines/scumm/metaengine.cpp:1094
+#: engines/scumm/metaengine.cpp:1118
 msgid "Up left"
 msgstr "Haut gauche"
 
-#: engines/scumm/metaengine.cpp:1107
+#: engines/scumm/metaengine.cpp:1131
 msgid "Up right"
 msgstr "Haut droite"
 
-#: engines/scumm/metaengine.cpp:1119
+#: engines/scumm/metaengine.cpp:1143
 msgid "Switch weapon"
 msgstr "Changer d'arme"
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1133
+#: engines/scumm/metaengine.cpp:1157
 msgid "Win the bike fight cheat"
 msgstr "Gagner les bagarres en b??cane (cheat)"
 
-#: engines/scumm/metaengine.cpp:1142
+#: engines/scumm/metaengine.cpp:1166
 msgid "Rebel Assault controls"
 msgstr "Contr??les de Rebel Assault"
 
-#: engines/scumm/metaengine.cpp:1144 engines/scumm/metaengine.cpp:1221
+#: engines/scumm/metaengine.cpp:1168 engines/scumm/metaengine.cpp:1245
 msgid "Aim up / menu up"
 msgstr "Vis??e haut / Menu haut"
 
-#: engines/scumm/metaengine.cpp:1149 engines/scumm/metaengine.cpp:1226
+#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1250
 msgid "Aim down / menu down"
 msgstr "Vis??e bas / Menu bas"
 
-#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1231
+#: engines/scumm/metaengine.cpp:1178 engines/scumm/metaengine.cpp:1255
 msgid "Aim left / menu left"
 msgstr "Vis??e gauche / menu gauche"
 
-#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
+#: engines/scumm/metaengine.cpp:1183 engines/scumm/metaengine.cpp:1260
 msgid "Aim right / menu right"
 msgstr "Vis??e droite / Menu droite"
 
-#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1241
+#: engines/scumm/metaengine.cpp:1188 engines/scumm/metaengine.cpp:1265
 msgid "Stick up"
 msgstr "Manche haut"
 
-#: engines/scumm/metaengine.cpp:1171 engines/scumm/metaengine.cpp:1248
+#: engines/scumm/metaengine.cpp:1195 engines/scumm/metaengine.cpp:1272
 msgid "Stick down"
 msgstr "Manche bas"
 
-#: engines/scumm/metaengine.cpp:1178 engines/scumm/metaengine.cpp:1255
+#: engines/scumm/metaengine.cpp:1202 engines/scumm/metaengine.cpp:1279
 msgid "Stick left"
 msgstr "Manche gauche"
 
-#: engines/scumm/metaengine.cpp:1185 engines/scumm/metaengine.cpp:1262
+#: engines/scumm/metaengine.cpp:1209 engines/scumm/metaengine.cpp:1286
 msgid "Stick right"
 msgstr "Manche droite"
 
-#: engines/scumm/metaengine.cpp:1192 engines/scumm/metaengine.cpp:1269
+#: engines/scumm/metaengine.cpp:1216 engines/scumm/metaengine.cpp:1293
 msgid "Fire / select"
 msgstr "Tirer / S??lectionner"
 
-#: engines/scumm/metaengine.cpp:1198
+#: engines/scumm/metaengine.cpp:1222
 msgid "Walk / menu back"
 msgstr "Marcher / menu retour"
 
-#: engines/scumm/metaengine.cpp:1203
+#: engines/scumm/metaengine.cpp:1227
 msgid "Skip / menu back"
 msgstr "Passer / menu retour"
 
-#: engines/scumm/metaengine.cpp:1219
+#: engines/scumm/metaengine.cpp:1243
 msgid "Rebel Assault II controls"
 msgstr "Contr??les de Rebel Assault II"
 
-#: engines/scumm/metaengine.cpp:1274
+#: engines/scumm/metaengine.cpp:1298
 msgid "Cover / back"
 msgstr "Couverture / retour"
 
-#: engines/scumm/metaengine.cpp:1280
+#: engines/scumm/metaengine.cpp:1304
 msgid "Skip / back"
 msgstr "Passer / retour"
 
-#: engines/scumm/metaengine.cpp:1287
+#: engines/scumm/metaengine.cpp:1311
 msgid "Skip / menu"
 msgstr "Passer / menu"
 
@@ -13850,13 +13860,13 @@ msgstr ""
 "La correction du rapport d'aspect peut ??tre obtenue en rognant la r??solution "
 "?? 320x200, sous l'onglet \"moteur\"."
 
-#: engines/scumm/scumm.cpp:1362
+#: engines/scumm/scumm.cpp:1365
 #, c-format
 msgid "This game requires the '%s' Macintosh executable for its fonts."
 msgstr ""
 "Ce jeu n??cessite l'ex??cutable Macintosh '%s' pour ses polices de caract??res."
 
-#: engines/scumm/scumm.cpp:1371
+#: engines/scumm/scumm.cpp:1374
 #, c-format
 msgid ""
 "This game requires the '%s' Macintosh executable for its music and fonts."
@@ -13864,7 +13874,7 @@ msgstr ""
 "Ce jeu n??cessite l'ex??cutable Macintosh '%s' pour sa musique et ses polices "
 "de caract??res."
 
-#: engines/scumm/scumm.cpp:1375
+#: engines/scumm/scumm.cpp:1378
 #, c-format
 msgid ""
 "Could not find the '%s' Macintosh executable to read resources from. %s will "
@@ -13873,27 +13883,27 @@ msgstr ""
 "Impossible de trouver l'ex??cutable Macintosh '%s' ?? partir duquel lire les "
 "ressources. '%s' d??sactiv??(e)(s)."
 
-#: engines/scumm/scumm.cpp:1376
+#: engines/scumm/scumm.cpp:1379
 msgid "The Mac GUI"
 msgstr "Interface graphique Mac"
 
-#: engines/scumm/scumm.cpp:1376
+#: engines/scumm/scumm.cpp:1379
 msgid "The music and the Mac GUI"
 msgstr "Musique et interface graphique Mac"
 
-#: engines/scumm/scumm.cpp:1405
+#: engines/scumm/scumm.cpp:1408
 #, c-format
 msgid "Could not open Macintosh resource file %s"
 msgstr "Impossible d'ouvrir le fichier de ressources Macintosh %s"
 
-#: engines/scumm/scumm.cpp:1408
+#: engines/scumm/scumm.cpp:1411
 #, c-format
 msgid "Could not find resource fork in Macintosh resource file %s"
 msgstr ""
 "Impossible de trouver le fork de ressources dans le fichier de ressources "
 "Macintosh %s"
 
-#: engines/scumm/scumm.cpp:1415
+#: engines/scumm/scumm.cpp:1418
 #, c-format
 msgid ""
 "'%s' appears to be the wrong Dig executable. It may be the launcher one "
@@ -13907,7 +13917,7 @@ msgstr ""
 "f' sur votre CD. N'importe lequel de ses sous-dossiers devrait correspondre "
 "?? ce dont vous avez besoin."
 
-#: engines/scumm/scumm.cpp:1581
+#: engines/scumm/scumm.cpp:1584
 msgid ""
 "It appears your ScummVM version was not built with TrueType Fonts support.\n"
 "\n"
@@ -13920,7 +13930,7 @@ msgstr ""
 "TTF,\n"
 "certains ??l??ments graphiques ?? l'??cran seront manquants."
 
-#: engines/scumm/scumm.cpp:1915
+#: engines/scumm/scumm.cpp:1918
 msgid ""
 "Audio files compressed with ScummVM Tools were detected; *.BUN/*.SOU\n"
 "compression is not supported anymore for this game, audio will be disabled.\n"
@@ -13932,7 +13942,7 @@ msgstr ""
 "d??sactiv??.\n"
 "Veuillez copier le jeu ?? partir du support original sans compression."
 
-#: engines/scumm/scumm.cpp:2405
+#: engines/scumm/scumm.cpp:2408
 #, c-format
 msgid ""
 "Native MIDI support requires the Roland Upgrade from LucasArts,\n"
@@ -13941,7 +13951,7 @@ msgstr ""
 "Le support MIDI natif n??cessite la mise ?? jour Roland de LucasArts,\n"
 "mais %s est manquant. AdLib sera utilis??."
 
-#: engines/scumm/scumm.cpp:2420
+#: engines/scumm/scumm.cpp:2423
 msgid ""
 "This particular version of Monkey Island 1 is known to miss some\n"
 "required resources for MT-32. Using AdLib instead."
@@ -13949,7 +13959,7 @@ msgstr ""
 "Cette version de Monkey Island 1 n'a pas ??t?? produite avec toutes\n"
 "les ressources n??cessaires pour le MT-32. AdLib sera utilis??."
 
-#: engines/scumm/scumm.cpp:4361
+#: engines/scumm/scumm.cpp:4364
 msgid ""
 "Usually, Maniac Mansion would start now. But for that to work, the game "
 "files for Maniac Mansion have to be in the 'Maniac' directory inside the "
@@ -13960,7 +13970,7 @@ msgstr ""
 "r??pertoire 'Maniac' ?? l'int??rieur du r??pertoire du jeu Day of the Tentacle, "
 "et le jeu doit ??tre ajout?? ?? ScummVM."
 
-#: engines/scumm/scumm.cpp:4485
+#: engines/scumm/scumm.cpp:4488
 msgid "Would you like to host or join a network play session?"
 msgstr "Voulez-vous h??berger ou rejoindre une partie de jeu en r??seau ?"
 
diff --git a/po/gl_ES.po b/po/gl_ES.po
index 747f8704a77..91dbdb71af5 100644
--- a/po/gl_ES.po
+++ b/po/gl_ES.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-08 11:04+0000\n"
+"POT-Creation-Date: 2026-06-09 13:22+0000\n"
 "PO-Revision-Date: 2020-09-26 21:13+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Galician <https://translations.scummvm.org/projects/scummvm/"
@@ -351,7 +351,7 @@ msgstr "Hai outro almacenamento activo. Seguro que queres interrompelo?"
 #: engines/hypno/metaengine.cpp:192 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:197
 #: engines/mm/mm1/metaengine.cpp:54 engines/parallaction/metaengine.cpp:169
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4467
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
 #: engines/sherlock/metaengine.cpp:638 engines/sherlock/metaengine.cpp:1244
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:328
@@ -370,7 +370,7 @@ msgstr "Si"
 #: engines/hypno/metaengine.cpp:198 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:204
 #: engines/mm/mm1/metaengine.cpp:52 engines/parallaction/metaengine.cpp:175
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4467
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
 #: engines/sherlock/metaengine.cpp:643 engines/sherlock/metaengine.cpp:1250
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:329
@@ -400,7 +400,7 @@ msgstr ""
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:123
 #: engines/drascula/saveload.cpp:51 engines/grim/inputdialog.cpp:83
 #: engines/parallaction/saveload.cpp:270 engines/sci/engine/kgraphics.cpp:76
-#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4479
+#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4482
 #: engines/sword1/control.cpp:2816
 msgid "OK"
 msgstr "Aceptar"
@@ -1070,7 +1070,7 @@ msgstr "Interpolaci??n:"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1100
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1124
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1091,7 +1091,7 @@ msgstr "Arriba"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1067
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1091
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1112,7 +1112,7 @@ msgstr "Abaixo"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1080
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1104
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1132,7 +1132,7 @@ msgstr "Esquerda"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1087
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1111
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3366,7 +3366,7 @@ msgstr ""
 #: engines/mohawk/riven_stacks/aspit.cpp:454
 #: engines/neverhood/metaengine.cpp:200 engines/saga/metaengine.cpp:449
 #: engines/scumm/help.cpp:84 engines/scumm/help.cpp:86
-#: engines/scumm/scumm.cpp:4479 engines/sludge/keymapper_tables.h:95
+#: engines/scumm/scumm.cpp:4482 engines/sludge/keymapper_tables.h:95
 #: engines/sludge/keymapper_tables.h:144 engines/sludge/keymapper_tables.h:223
 #: engines/sludge/keymapper_tables.h:343 engines/sludge/keymapper_tables.h:355
 #: engines/sludge/keymapper_tables.h:416 engines/sludge/keymapper_tables.h:429
@@ -3859,7 +3859,7 @@ msgid "Game not implemented"
 msgstr "ID de xogo non compatible"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
-#: engines/scumm/metaengine.cpp:1050
+#: engines/scumm/metaengine.cpp:1074
 #, fuzzy
 msgid "Default game keymappings"
 msgstr "~O~pci??ns..."
@@ -5853,7 +5853,7 @@ msgstr ""
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1208 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1232 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "Men??"
 
@@ -6826,7 +6826,7 @@ msgstr ""
 msgid "Quick load or save game isn't supported in this location"
 msgstr ""
 
-#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3418
+#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3421
 #, c-format
 msgid ""
 "Failed to load saved game from file:\n"
@@ -6837,7 +6837,7 @@ msgstr ""
 "\n"
 "%s"
 
-#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3405
+#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3408
 #, c-format
 msgid ""
 "Failed to save game to file:\n"
@@ -6848,7 +6848,7 @@ msgstr ""
 "\n"
 "%s"
 
-#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3438
+#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3441
 #, c-format
 msgid ""
 "Successfully saved game in file:\n"
@@ -7342,7 +7342,7 @@ msgstr "Activar/desactivar captura de rato"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1126 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -8088,7 +8088,7 @@ msgstr "Interpolaci??n:"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1113
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1137
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -10281,7 +10281,7 @@ msgid "Player ammo will never decrease."
 msgstr ""
 
 #: engines/hypno/metaengine.cpp:70 engines/scumm/metaengine.cpp:891
-#: engines/scumm/metaengine.cpp:909
+#: engines/scumm/metaengine.cpp:918
 msgid "Unlock all levels"
 msgstr ""
 
@@ -12792,7 +12792,7 @@ msgid "Your Name:"
 msgstr "Nome:"
 
 #. I18N: Button, start hosting online multiplayer game
-#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4485
+#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4488
 msgid "Host"
 msgstr ""
 
@@ -12804,7 +12804,7 @@ msgid "Querying games..."
 msgstr "Gardando..."
 
 #. I18N: Join online multiplayer game
-#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4485
+#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4488
 msgid "Join"
 msgstr ""
 
@@ -13734,126 +13734,135 @@ msgstr "Activar gr??ficos de alta resoluci??n"
 msgid "Run the game in 640x400 high resolution mode instead of 320x200"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:910
+#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:919
 msgid "All levels will be available without requiring passwords"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:900
+#: engines/scumm/metaengine.cpp:900 engines/scumm/metaengine.cpp:927
 #, fuzzy
 #| msgid "No date saved"
 msgid "No damage"
 msgstr "Non hai data gardada"
 
-#: engines/scumm/metaengine.cpp:901
+#: engines/scumm/metaengine.cpp:901 engines/scumm/metaengine.cpp:928
 #, fuzzy
 msgid "Disable player damage"
 msgstr "Erro ao gardar a partida"
 
-#: engines/scumm/metaengine.cpp:1061
+#: engines/scumm/metaengine.cpp:909
+#, fuzzy
+msgid "Yoda mode"
+msgstr "Modo R??pido"
+
+#: engines/scumm/metaengine.cpp:910
+msgid "Enable original Yoda mode shortcuts, including movie mode and auto play"
+msgstr ""
+
+#: engines/scumm/metaengine.cpp:1085
 #, fuzzy
 msgid "Down left"
 msgstr "Xirar ?? esquerda"
 
-#: engines/scumm/metaengine.cpp:1074
+#: engines/scumm/metaengine.cpp:1098
 #, fuzzy
 msgid "Down right"
 msgstr "Xirar ?? dereita"
 
-#: engines/scumm/metaengine.cpp:1094
+#: engines/scumm/metaengine.cpp:1118
 #, fuzzy
 #| msgid "Upper left item"
 msgid "Up left"
 msgstr "Obxecto esquerdo arriba"
 
-#: engines/scumm/metaengine.cpp:1107
+#: engines/scumm/metaengine.cpp:1131
 #, fuzzy
 msgid "Up right"
 msgstr "Dereita"
 
-#: engines/scumm/metaengine.cpp:1119
+#: engines/scumm/metaengine.cpp:1143
 #, fuzzy
 msgid "Switch weapon"
 msgstr "Cambiar personaxes:"
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1133
+#: engines/scumm/metaengine.cpp:1157
 msgid "Win the bike fight cheat"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1142
+#: engines/scumm/metaengine.cpp:1166
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1144 engines/scumm/metaengine.cpp:1221
+#: engines/scumm/metaengine.cpp:1168 engines/scumm/metaengine.cpp:1245
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1149 engines/scumm/metaengine.cpp:1226
+#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1250
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1231
+#: engines/scumm/metaengine.cpp:1178 engines/scumm/metaengine.cpp:1255
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
+#: engines/scumm/metaengine.cpp:1183 engines/scumm/metaengine.cpp:1260
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1241
+#: engines/scumm/metaengine.cpp:1188 engines/scumm/metaengine.cpp:1265
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "Coller"
 
-#: engines/scumm/metaengine.cpp:1171 engines/scumm/metaengine.cpp:1248
+#: engines/scumm/metaengine.cpp:1195 engines/scumm/metaengine.cpp:1272
 #, fuzzy
 msgid "Stick down"
 msgstr "Baixar lista"
 
-#: engines/scumm/metaengine.cpp:1178 engines/scumm/metaengine.cpp:1255
+#: engines/scumm/metaengine.cpp:1202 engines/scumm/metaengine.cpp:1279
 #, fuzzy
 #| msgid "Slide Left"
 msgid "Stick left"
 msgstr "Esvarar ?? esquerda"
 
-#: engines/scumm/metaengine.cpp:1185 engines/scumm/metaengine.cpp:1262
+#: engines/scumm/metaengine.cpp:1209 engines/scumm/metaengine.cpp:1286
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick right"
 msgstr "Esvarar ?? dereita"
 
-#: engines/scumm/metaengine.cpp:1192 engines/scumm/metaengine.cpp:1269
+#: engines/scumm/metaengine.cpp:1216 engines/scumm/metaengine.cpp:1293
 #, fuzzy
 msgid "Fire / select"
 msgstr "Mover ?? esquerda"
 
-#: engines/scumm/metaengine.cpp:1198
+#: engines/scumm/metaengine.cpp:1222
 #, fuzzy
 msgid "Walk / menu back"
 msgstr "~V~olver ao Iniciador"
 
-#: engines/scumm/metaengine.cpp:1203
+#: engines/scumm/metaengine.cpp:1227
 #, fuzzy
 msgid "Skip / menu back"
 msgstr "~V~olver ao Iniciador"
 
-#: engines/scumm/metaengine.cpp:1219
+#: engines/scumm/metaengine.cpp:1243
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1274
+#: engines/scumm/metaengine.cpp:1298
 #, fuzzy
 #| msgid "Move Back"
 msgid "Cover / back"
 msgstr "Mover cara atr??s"
 
-#: engines/scumm/metaengine.cpp:1280
+#: engines/scumm/metaengine.cpp:1304
 #, fuzzy
 msgid "Skip / back"
 msgstr "Omitir li??a"
 
-#: engines/scumm/metaengine.cpp:1287
+#: engines/scumm/metaengine.cpp:1311
 #, fuzzy
 msgid "Skip / menu"
 msgstr "~V~olver ao Iniciador"
@@ -13880,18 +13889,18 @@ msgid ""
 "320x200, under 'engine' tab."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1362
+#: engines/scumm/scumm.cpp:1365
 #, c-format
 msgid "This game requires the '%s' Macintosh executable for its fonts."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1371
+#: engines/scumm/scumm.cpp:1374
 #, c-format
 msgid ""
 "This game requires the '%s' Macintosh executable for its music and fonts."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1375
+#: engines/scumm/scumm.cpp:1378
 #, fuzzy, c-format
 #| msgid ""
 #| "Could not find the 'Monkey Island' Macintosh executable to read the\n"
@@ -13903,25 +13912,25 @@ msgstr ""
 "Erro ao buscar o executable de Monkey Island para Macintosh\n"
 "do que empregar os instrumentos. A m??sica desactivarase."
 
-#: engines/scumm/scumm.cpp:1376
+#: engines/scumm/scumm.cpp:1379
 msgid "The Mac GUI"
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1376
+#: engines/scumm/scumm.cpp:1379
 msgid "The music and the Mac GUI"
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1405
+#: engines/scumm/scumm.cpp:1408
 #, c-format
 msgid "Could not open Macintosh resource file %s"
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1408
+#: engines/scumm/scumm.cpp:1411
 #, c-format
 msgid "Could not find resource fork in Macintosh resource file %s"
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1415
+#: engines/scumm/scumm.cpp:1418
 #, c-format
 msgid ""
 "'%s' appears to be the wrong Dig executable. It may be the launcher one "
@@ -13930,7 +13939,7 @@ msgid ""
 "its sub-folders should be what you need."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1581
+#: engines/scumm/scumm.cpp:1584
 msgid ""
 "It appears your ScummVM version was not built with TrueType Fonts support.\n"
 "\n"
@@ -13938,14 +13947,14 @@ msgid ""
 "some of the graphics on screen will be missing."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1915
+#: engines/scumm/scumm.cpp:1918
 msgid ""
 "Audio files compressed with ScummVM Tools were detected; *.BUN/*.SOU\n"
 "compression is not supported anymore for this game, audio will be disabled.\n"
 "Please copy the game from the original media without compression."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:2405
+#: engines/scumm/scumm.cpp:2408
 #, c-format
 msgid ""
 "Native MIDI support requires the Roland Upgrade from LucasArts,\n"
@@ -13954,13 +13963,13 @@ msgstr ""
 "A compatibilidade nativa con MIDI precisa a actualizaci??n de Roland\n"
 "de LucasArts, mais falla %s. Empregarase AdLib."
 
-#: engines/scumm/scumm.cpp:2420
+#: engines/scumm/scumm.cpp:2423
 msgid ""
 "This particular version of Monkey Island 1 is known to miss some\n"
 "required resources for MT-32. Using AdLib instead."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:4361
+#: engines/scumm/scumm.cpp:4364
 msgid ""
 "Usually, Maniac Mansion would start now. But for that to work, the game "
 "files for Maniac Mansion have to be in the 'Maniac' directory inside the "
@@ -13970,7 +13979,7 @@ msgstr ""
 "que estar no directorio \"Maniac\" dentro do directorio \"Tentacle\". "
 "Ademais, c??mpre engadir o xogo a ScummVM."
 
-#: engines/scumm/scumm.cpp:4485
+#: engines/scumm/scumm.cpp:4488
 #, fuzzy
 msgid "Would you like to host or join a network play session?"
 msgstr "Queres cargar ou gardar a partida?"
diff --git a/po/he.po b/po/he.po
index 0cac186ac42..e875efe1ab8 100644
--- a/po/he.po
+++ b/po/he.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.1.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-08 11:04+0000\n"
+"POT-Creation-Date: 2026-06-09 13:22+0000\n"
 "PO-Revision-Date: 2025-04-09 20:18+0000\n"
 "Last-Translator: Niv Baehr <bloop93 at gmail.com>\n"
 "Language-Team: Hebrew <https://translations.scummvm.org/projects/scummvm/"
@@ -341,7 +341,7 @@ msgstr "?????????? ?????? ???????? ??????. ?????? ???????????? ?????"
 #: engines/hypno/metaengine.cpp:192 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:197
 #: engines/mm/mm1/metaengine.cpp:54 engines/parallaction/metaengine.cpp:169
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4467
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
 #: engines/sherlock/metaengine.cpp:638 engines/sherlock/metaengine.cpp:1244
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:328
@@ -360,7 +360,7 @@ msgstr "????"
 #: engines/hypno/metaengine.cpp:198 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:204
 #: engines/mm/mm1/metaengine.cpp:52 engines/parallaction/metaengine.cpp:175
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4467
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
 #: engines/sherlock/metaengine.cpp:643 engines/sherlock/metaengine.cpp:1250
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:329
@@ -388,7 +388,7 @@ msgstr "???????? ?????? JSON ??????????."
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:123
 #: engines/drascula/saveload.cpp:51 engines/grim/inputdialog.cpp:83
 #: engines/parallaction/saveload.cpp:270 engines/sci/engine/kgraphics.cpp:76
-#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4479
+#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4482
 #: engines/sword1/control.cpp:2816
 msgid "OK"
 msgstr "??????????"
@@ -1033,7 +1033,7 @@ msgstr "??????????"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1100
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1124
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1054,7 +1054,7 @@ msgstr "??????????"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1067
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1091
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1075,7 +1075,7 @@ msgstr "????????"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1080
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1104
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1095,7 +1095,7 @@ msgstr "??????????"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1087
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1111
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3305,7 +3305,7 @@ msgstr "???????? ???????? ???????????? ????????"
 #: engines/mohawk/riven_stacks/aspit.cpp:454
 #: engines/neverhood/metaengine.cpp:200 engines/saga/metaengine.cpp:449
 #: engines/scumm/help.cpp:84 engines/scumm/help.cpp:86
-#: engines/scumm/scumm.cpp:4479 engines/sludge/keymapper_tables.h:95
+#: engines/scumm/scumm.cpp:4482 engines/sludge/keymapper_tables.h:95
 #: engines/sludge/keymapper_tables.h:144 engines/sludge/keymapper_tables.h:223
 #: engines/sludge/keymapper_tables.h:343 engines/sludge/keymapper_tables.h:355
 #: engines/sludge/keymapper_tables.h:416 engines/sludge/keymapper_tables.h:429
@@ -3807,7 +3807,7 @@ msgid "Game not implemented"
 msgstr "???????? ?????????? ???????? ????????"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
-#: engines/scumm/metaengine.cpp:1050
+#: engines/scumm/metaengine.cpp:1074
 #, fuzzy
 #| msgid "Default game keymap"
 msgid "Default game keymappings"
@@ -5811,7 +5811,7 @@ msgstr "R2"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1208 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1232 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "??????????"
 
@@ -6804,7 +6804,7 @@ msgstr ""
 msgid "Quick load or save game isn't supported in this location"
 msgstr "?????????? ?????????? ???? ?????????? ?????????? ???? ???????????? ???????????? ????"
 
-#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3418
+#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3421
 #, c-format
 msgid ""
 "Failed to load saved game from file:\n"
@@ -6815,7 +6815,7 @@ msgstr ""
 "\n"
 "%s"
 
-#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3405
+#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3408
 #, c-format
 msgid ""
 "Failed to save game to file:\n"
@@ -6826,7 +6826,7 @@ msgstr ""
 "\n"
 "%s"
 
-#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3438
+#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3441
 #, c-format
 msgid ""
 "Successfully saved game in file:\n"
@@ -7325,7 +7325,7 @@ msgstr "?????????? ??????????"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1126 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -8087,7 +8087,7 @@ msgstr "????????????????????"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1113
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1137
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -10392,7 +10392,7 @@ msgid "Player ammo will never decrease."
 msgstr "???????????? ?????????? ???? ???????? ??????????."
 
 #: engines/hypno/metaengine.cpp:70 engines/scumm/metaengine.cpp:891
-#: engines/scumm/metaengine.cpp:909
+#: engines/scumm/metaengine.cpp:918
 msgid "Unlock all levels"
 msgstr "?????????? ???? ????????????"
 
@@ -12985,7 +12985,7 @@ msgid "Your Name:"
 msgstr "????????:"
 
 #. I18N: Button, start hosting online multiplayer game
-#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4485
+#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4488
 msgid "Host"
 msgstr "??????????"
 
@@ -12997,7 +12997,7 @@ msgid "Querying games..."
 msgstr "???????? ???????? ..."
 
 #. I18N: Join online multiplayer game
-#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4485
+#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4488
 msgid "Join"
 msgstr "??????????????"
 
@@ -13954,133 +13954,143 @@ msgstr "?????????? ?????????? ??????????"
 msgid "Run the game in 640x400 high resolution mode instead of 320x200"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:910
+#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:919
 #, fuzzy
 #| msgid "All levels will be available to play."
 msgid "All levels will be available without requiring passwords"
 msgstr "???? ???????????? ???????? ???????????? ??????????."
 
-#: engines/scumm/metaengine.cpp:900
+#: engines/scumm/metaengine.cpp:900 engines/scumm/metaengine.cpp:927
 #, fuzzy
 #| msgid "No date saved"
 msgid "No damage"
 msgstr "???? ???????? ??????????"
 
-#: engines/scumm/metaengine.cpp:901
+#: engines/scumm/metaengine.cpp:901 engines/scumm/metaengine.cpp:928
 #, fuzzy
 #| msgid "Disable save menu"
 msgid "Disable player damage"
 msgstr "?????????? ?????????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1061
+#: engines/scumm/metaengine.cpp:909
+#, fuzzy
+#| msgid "Fast move modifier"
+msgid "Yoda mode"
+msgstr "?????? ?????????? ??????????"
+
+#: engines/scumm/metaengine.cpp:910
+msgid "Enable original Yoda mode shortcuts, including movie mode and auto play"
+msgstr ""
+
+#: engines/scumm/metaengine.cpp:1085
 #, fuzzy
 msgid "Down left"
 msgstr "???????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1074
+#: engines/scumm/metaengine.cpp:1098
 #, fuzzy
 msgid "Down right"
 msgstr "?????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1094
+#: engines/scumm/metaengine.cpp:1118
 #, fuzzy
 #| msgid "Upper left item"
 msgid "Up left"
 msgstr "???????? ?????????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1107
+#: engines/scumm/metaengine.cpp:1131
 #, fuzzy
 msgid "Up right"
 msgstr "??????????"
 
-#: engines/scumm/metaengine.cpp:1119
+#: engines/scumm/metaengine.cpp:1143
 msgid "Switch weapon"
 msgstr "?????????? ??????"
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1133
+#: engines/scumm/metaengine.cpp:1157
 msgid "Win the bike fight cheat"
 msgstr "?????????? ?????????????? ???????? ????????????????"
 
-#: engines/scumm/metaengine.cpp:1142
+#: engines/scumm/metaengine.cpp:1166
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1144 engines/scumm/metaengine.cpp:1221
+#: engines/scumm/metaengine.cpp:1168 engines/scumm/metaengine.cpp:1245
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1149 engines/scumm/metaengine.cpp:1226
+#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1250
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1231
+#: engines/scumm/metaengine.cpp:1178 engines/scumm/metaengine.cpp:1255
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
+#: engines/scumm/metaengine.cpp:1183 engines/scumm/metaengine.cpp:1260
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1241
+#: engines/scumm/metaengine.cpp:1188 engines/scumm/metaengine.cpp:1265
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "??????????"
 
-#: engines/scumm/metaengine.cpp:1171 engines/scumm/metaengine.cpp:1248
+#: engines/scumm/metaengine.cpp:1195 engines/scumm/metaengine.cpp:1272
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "?????????? ??????"
 
-#: engines/scumm/metaengine.cpp:1178 engines/scumm/metaengine.cpp:1255
+#: engines/scumm/metaengine.cpp:1202 engines/scumm/metaengine.cpp:1279
 #, fuzzy
 #| msgid "Slide Left"
 msgid "Stick left"
 msgstr "???????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1185 engines/scumm/metaengine.cpp:1262
+#: engines/scumm/metaengine.cpp:1209 engines/scumm/metaengine.cpp:1286
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick right"
 msgstr "???????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1192 engines/scumm/metaengine.cpp:1269
+#: engines/scumm/metaengine.cpp:1216 engines/scumm/metaengine.cpp:1293
 #, fuzzy
 #| msgid "Move left"
 msgid "Fire / select"
 msgstr "?????????? ??????????"
 
-#: engines/scumm/metaengine.cpp:1198
+#: engines/scumm/metaengine.cpp:1222
 #, fuzzy
 #| msgid "Open main menu"
 msgid "Walk / menu back"
 msgstr "?????????? ?????????? ????????"
 
-#: engines/scumm/metaengine.cpp:1203
+#: engines/scumm/metaengine.cpp:1227
 #, fuzzy
 #| msgid "Open main menu"
 msgid "Skip / menu back"
 msgstr "?????????? ?????????? ????????"
 
-#: engines/scumm/metaengine.cpp:1219
+#: engines/scumm/metaengine.cpp:1243
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1274
+#: engines/scumm/metaengine.cpp:1298
 #, fuzzy
 #| msgid "Move Back"
 msgid "Cover / back"
 msgstr "??????????"
 
-#: engines/scumm/metaengine.cpp:1280
+#: engines/scumm/metaengine.cpp:1304
 #, fuzzy
 #| msgid "Skip dialogue"
 msgid "Skip / back"
 msgstr "?????????? ???? ????????"
 
-#: engines/scumm/metaengine.cpp:1287
+#: engines/scumm/metaengine.cpp:1311
 #, fuzzy
 #| msgid "Open main menu"
 msgid "Skip / menu"
@@ -14111,18 +14121,18 @@ msgstr ""
 "?????????? ???????????? ?????????? ?????? ??????????.\n"
 "???????? ?????????? ?????????? ?????? ?????????? ???? ?????? ?????????? ?????????????????? ??-320x200, ?????????????? '????????'."
 
-#: engines/scumm/scumm.cpp:1362
+#: engines/scumm/scumm.cpp:1365
 #, c-format
 msgid "This game requires the '%s' Macintosh executable for its fonts."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1371
+#: engines/scumm/scumm.cpp:1374
 #, c-format
 msgid ""
 "This game requires the '%s' Macintosh executable for its music and fonts."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1375
+#: engines/scumm/scumm.cpp:1378
 #, fuzzy, c-format
 #| msgid ""
 #| "Could not find the 'Monkey Island' Macintosh executable to read the\n"
@@ -14134,25 +14144,25 @@ msgstr ""
 "???? ???????? ?????????? ???? ???????????? ?????????????? '???? ????????????' ?????? ?????????? ????\n"
 "?????????????? ??. ?????????????? ??????????."
 
-#: engines/scumm/scumm.cpp:1376
+#: engines/scumm/scumm.cpp:1379
 msgid "The Mac GUI"
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1376
+#: engines/scumm/scumm.cpp:1379
 msgid "The music and the Mac GUI"
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1405
+#: engines/scumm/scumm.cpp:1408
 #, c-format
 msgid "Could not open Macintosh resource file %s"
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1408
+#: engines/scumm/scumm.cpp:1411
 #, c-format
 msgid "Could not find resource fork in Macintosh resource file %s"
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1415
+#: engines/scumm/scumm.cpp:1418
 #, c-format
 msgid ""
 "'%s' appears to be the wrong Dig executable. It may be the launcher one "
@@ -14161,7 +14171,7 @@ msgid ""
 "its sub-folders should be what you need."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1581
+#: engines/scumm/scumm.cpp:1584
 msgid ""
 "It appears your ScummVM version was not built with TrueType Fonts support.\n"
 "\n"
@@ -14169,7 +14179,7 @@ msgid ""
 "some of the graphics on screen will be missing."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1915
+#: engines/scumm/scumm.cpp:1918
 msgid ""
 "Audio files compressed with ScummVM Tools were detected; *.BUN/*.SOU\n"
 "compression is not supported anymore for this game, audio will be disabled.\n"
@@ -14179,7 +14189,7 @@ msgstr ""
 "???????????? ???????? ???????? ???? ?????? ???????? ??????????, ???????? ????????????.\n"
 "???? ???????????? ???? ?????????? ???????????? ?????????????? ?????? ????????????."
 
-#: engines/scumm/scumm.cpp:2405
+#: engines/scumm/scumm.cpp:2408
 #, c-format
 msgid ""
 "Native MIDI support requires the Roland Upgrade from LucasArts,\n"
@@ -14188,7 +14198,7 @@ msgstr ""
 "?????????? MIDI ???????????? ?????????? ???? ?????????? ???????????? ??- LucasArts,\n"
 "?????? %s ??????. ?????????? ?????? ?????????????? ??- AdLib."
 
-#: engines/scumm/scumm.cpp:2420
+#: engines/scumm/scumm.cpp:2423
 msgid ""
 "This particular version of Monkey Island 1 is known to miss some\n"
 "required resources for MT-32. Using AdLib instead."
@@ -14196,7 +14206,7 @@ msgstr ""
 "???????? ???? ???? ???? ???????????? 1 ?????????? ??????????\n"
 "???????????? ?????????? ?????????????? ???????? MT-32. ?????????????? ??-AdLib ??????????."
 
-#: engines/scumm/scumm.cpp:4361
+#: engines/scumm/scumm.cpp:4364
 msgid ""
 "Usually, Maniac Mansion would start now. But for that to work, the game "
 "files for Maniac Mansion have to be in the 'Maniac' directory inside the "
@@ -14206,7 +14216,7 @@ msgstr ""
 "Mansion ???????????? ?????????? ?????????????? 'Maniac' ???????? ???????????? ?????????? Tentacle, ?????? ???????????? "
 "???? ?????????? ??- ScummVM."
 
-#: engines/scumm/scumm.cpp:4485
+#: engines/scumm/scumm.cpp:4488
 #, fuzzy
 msgid "Would you like to host or join a network play session?"
 msgstr "?????? ?????????????? ?????????? ???? ?????????? ?????????"
diff --git a/po/hi.po b/po/hi.po
index 8640d8b2935..ea94e7a273b 100644
--- a/po/hi.po
+++ b/po/hi.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-08 11:04+0000\n"
+"POT-Creation-Date: 2026-06-09 13:22+0000\n"
 "PO-Revision-Date: 2026-05-12 22:22+0000\n"
 "Last-Translator: KARTIK SAXENA <kartiksaxena2005 at gmail.com>\n"
 "Language-Team: Hindi <https://translations.scummvm.org/projects/scummvm/"
@@ -339,7 +339,7 @@ msgstr "?????? ???????????? ????????????????????? ????????? ????????? ?????? ???
 #: engines/hypno/metaengine.cpp:192 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:197
 #: engines/mm/mm1/metaengine.cpp:54 engines/parallaction/metaengine.cpp:169
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4467
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
 #: engines/sherlock/metaengine.cpp:638 engines/sherlock/metaengine.cpp:1244
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:328
@@ -358,7 +358,7 @@ msgstr "?????????"
 #: engines/hypno/metaengine.cpp:198 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:204
 #: engines/mm/mm1/metaengine.cpp:52 engines/parallaction/metaengine.cpp:175
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4467
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
 #: engines/sherlock/metaengine.cpp:643 engines/sherlock/metaengine.cpp:1250
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:329
@@ -386,7 +386,7 @@ msgstr "JSON ????????? ?????? ????????????????????? ???????????? ?????????"
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:123
 #: engines/drascula/saveload.cpp:51 engines/grim/inputdialog.cpp:83
 #: engines/parallaction/saveload.cpp:270 engines/sci/engine/kgraphics.cpp:76
-#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4479
+#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4482
 #: engines/sword1/control.cpp:2816
 msgid "OK"
 msgstr "????????? ??????"
@@ -1042,7 +1042,7 @@ msgstr ""
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1100
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1124
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1063,7 +1063,7 @@ msgstr "?????????"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1067
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1091
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1084,7 +1084,7 @@ msgstr "????????????"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1080
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1104
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1104,7 +1104,7 @@ msgstr "????????????"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1087
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1111
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3279,7 +3279,7 @@ msgstr ""
 #: engines/mohawk/riven_stacks/aspit.cpp:454
 #: engines/neverhood/metaengine.cpp:200 engines/saga/metaengine.cpp:449
 #: engines/scumm/help.cpp:84 engines/scumm/help.cpp:86
-#: engines/scumm/scumm.cpp:4479 engines/sludge/keymapper_tables.h:95
+#: engines/scumm/scumm.cpp:4482 engines/sludge/keymapper_tables.h:95
 #: engines/sludge/keymapper_tables.h:144 engines/sludge/keymapper_tables.h:223
 #: engines/sludge/keymapper_tables.h:343 engines/sludge/keymapper_tables.h:355
 #: engines/sludge/keymapper_tables.h:416 engines/sludge/keymapper_tables.h:429
@@ -3734,7 +3734,7 @@ msgid "Game not implemented"
 msgstr ""
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
-#: engines/scumm/metaengine.cpp:1050
+#: engines/scumm/metaengine.cpp:1074
 #, fuzzy
 #| msgid "Game Options"
 msgid "Default game keymappings"
@@ -5638,7 +5638,7 @@ msgstr ""
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1208 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1232 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr ""
 
@@ -6572,7 +6572,7 @@ msgstr ""
 msgid "Quick load or save game isn't supported in this location"
 msgstr ""
 
-#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3418
+#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3421
 #, c-format
 msgid ""
 "Failed to load saved game from file:\n"
@@ -6580,7 +6580,7 @@ msgid ""
 "%s"
 msgstr ""
 
-#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3405
+#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3408
 #, c-format
 msgid ""
 "Failed to save game to file:\n"
@@ -6588,7 +6588,7 @@ msgid ""
 "%s"
 msgstr ""
 
-#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3438
+#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3441
 #, c-format
 msgid ""
 "Successfully saved game in file:\n"
@@ -7059,7 +7059,7 @@ msgstr "???????????????????????? ?????????"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1126 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -7780,7 +7780,7 @@ msgstr "???????????? ??????????????? ????????????????????????"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1113
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1137
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -9906,7 +9906,7 @@ msgid "Player ammo will never decrease."
 msgstr ""
 
 #: engines/hypno/metaengine.cpp:70 engines/scumm/metaengine.cpp:891
-#: engines/scumm/metaengine.cpp:909
+#: engines/scumm/metaengine.cpp:918
 msgid "Unlock all levels"
 msgstr ""
 
@@ -12286,7 +12286,7 @@ msgid "Your Name:"
 msgstr "???????????? ?????????:"
 
 #. I18N: Button, start hosting online multiplayer game
-#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4485
+#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4488
 msgid "Host"
 msgstr ""
 
@@ -12296,7 +12296,7 @@ msgid "Querying games..."
 msgstr ""
 
 #. I18N: Join online multiplayer game
-#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4485
+#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4488
 msgid "Join"
 msgstr ""
 
@@ -13211,129 +13211,139 @@ msgstr ""
 msgid "Run the game in 640x400 high resolution mode instead of 320x200"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:910
+#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:919
 msgid "All levels will be available without requiring passwords"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:900
+#: engines/scumm/metaengine.cpp:900 engines/scumm/metaengine.cpp:927
 msgid "No damage"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:901
+#: engines/scumm/metaengine.cpp:901 engines/scumm/metaengine.cpp:928
 #, fuzzy
 #| msgid "Disable falling"
 msgid "Disable player damage"
 msgstr "??????????????? ?????????????????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1061
+#: engines/scumm/metaengine.cpp:909
+#, fuzzy
+#| msgid "Graphics mode:"
+msgid "Yoda mode"
+msgstr "?????????????????????????????? ?????????:"
+
+#: engines/scumm/metaengine.cpp:910
+msgid "Enable original Yoda mode shortcuts, including movie mode and auto play"
+msgstr ""
+
+#: engines/scumm/metaengine.cpp:1085
 #, fuzzy
 #| msgid "Down Left"
 msgid "Down left"
 msgstr "???????????? ??????????????????"
 
-#: engines/scumm/metaengine.cpp:1074
+#: engines/scumm/metaengine.cpp:1098
 #, fuzzy
 #| msgid "Down Right"
 msgid "Down right"
 msgstr "??????????????? ??????????????????"
 
-#: engines/scumm/metaengine.cpp:1094
+#: engines/scumm/metaengine.cpp:1118
 #, fuzzy
 #| msgid "Move Left"
 msgid "Up left"
 msgstr "???????????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1107
+#: engines/scumm/metaengine.cpp:1131
 #, fuzzy
 #| msgid "Up Right"
 msgid "Up right"
 msgstr "????????????"
 
-#: engines/scumm/metaengine.cpp:1119
+#: engines/scumm/metaengine.cpp:1143
 msgid "Switch weapon"
 msgstr ""
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1133
+#: engines/scumm/metaengine.cpp:1157
 msgid "Win the bike fight cheat"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1142
+#: engines/scumm/metaengine.cpp:1166
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1144 engines/scumm/metaengine.cpp:1221
+#: engines/scumm/metaengine.cpp:1168 engines/scumm/metaengine.cpp:1245
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1149 engines/scumm/metaengine.cpp:1226
+#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1250
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1231
+#: engines/scumm/metaengine.cpp:1178 engines/scumm/metaengine.cpp:1255
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
+#: engines/scumm/metaengine.cpp:1183 engines/scumm/metaengine.cpp:1260
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1241
+#: engines/scumm/metaengine.cpp:1188 engines/scumm/metaengine.cpp:1265
 msgid "Stick up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1171 engines/scumm/metaengine.cpp:1248
+#: engines/scumm/metaengine.cpp:1195 engines/scumm/metaengine.cpp:1272
 #, fuzzy
 msgid "Stick down"
 msgstr "???????????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1178 engines/scumm/metaengine.cpp:1255
+#: engines/scumm/metaengine.cpp:1202 engines/scumm/metaengine.cpp:1279
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick left"
 msgstr "?????????????????? ?????????????????? ?????????"
 
-#: engines/scumm/metaengine.cpp:1185 engines/scumm/metaengine.cpp:1262
+#: engines/scumm/metaengine.cpp:1209 engines/scumm/metaengine.cpp:1286
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick right"
 msgstr "?????????????????? ?????????????????? ?????????"
 
-#: engines/scumm/metaengine.cpp:1192 engines/scumm/metaengine.cpp:1269
+#: engines/scumm/metaengine.cpp:1216 engines/scumm/metaengine.cpp:1293
 #, fuzzy
 #| msgid "Move Left"
 msgid "Fire / select"
 msgstr "???????????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1198
+#: engines/scumm/metaengine.cpp:1222
 #, fuzzy
 #| msgid "Skip autosave"
 msgid "Walk / menu back"
 msgstr "?????????????????? ??????????????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1203
+#: engines/scumm/metaengine.cpp:1227
 #, fuzzy
 #| msgid "Skip autosave"
 msgid "Skip / menu back"
 msgstr "?????????????????? ??????????????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1219
+#: engines/scumm/metaengine.cpp:1243
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1274
+#: engines/scumm/metaengine.cpp:1298
 #, fuzzy
 #| msgid "Move Forward"
 msgid "Cover / back"
 msgstr "????????? ?????????"
 
-#: engines/scumm/metaengine.cpp:1280
+#: engines/scumm/metaengine.cpp:1304
 #, fuzzy
 #| msgid "Show hidden files"
 msgid "Skip / back"
 msgstr "???????????? ????????? ?????????????????? ?????????????????????"
 
-#: engines/scumm/metaengine.cpp:1287
+#: engines/scumm/metaengine.cpp:1311
 #, fuzzy
 #| msgid "Skip autosave"
 msgid "Skip / menu"
@@ -13361,43 +13371,43 @@ msgid ""
 "320x200, under 'engine' tab."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1362
+#: engines/scumm/scumm.cpp:1365
 #, c-format
 msgid "This game requires the '%s' Macintosh executable for its fonts."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1371
+#: engines/scumm/scumm.cpp:1374
 #, c-format
 msgid ""
 "This game requires the '%s' Macintosh executable for its music and fonts."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1375
+#: engines/scumm/scumm.cpp:1378
 #, c-format
 msgid ""
 "Could not find the '%s' Macintosh executable to read resources from. %s will "
 "be disabled."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1376
+#: engines/scumm/scumm.cpp:1379
 msgid "The Mac GUI"
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1376
+#: engines/scumm/scumm.cpp:1379
 msgid "The music and the Mac GUI"
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1405
+#: engines/scumm/scumm.cpp:1408
 #, c-format
 msgid "Could not open Macintosh resource file %s"
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1408
+#: engines/scumm/scumm.cpp:1411
 #, c-format
 msgid "Could not find resource fork in Macintosh resource file %s"
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1415
+#: engines/scumm/scumm.cpp:1418
 #, c-format
 msgid ""
 "'%s' appears to be the wrong Dig executable. It may be the launcher one "
@@ -13406,7 +13416,7 @@ msgid ""
 "its sub-folders should be what you need."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1581
+#: engines/scumm/scumm.cpp:1584
 msgid ""
 "It appears your ScummVM version was not built with TrueType Fonts support.\n"
 "\n"
@@ -13414,34 +13424,34 @@ msgid ""
 "some of the graphics on screen will be missing."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1915
+#: engines/scumm/scumm.cpp:1918
 msgid ""
 "Audio files compressed with ScummVM Tools were detected; *.BUN/*.SOU\n"
 "compression is not supported anymore for this game, audio will be disabled.\n"
 "Please copy the game from the original media without compression."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:2405
+#: engines/scumm/scumm.cpp:2408
 #, c-format
 msgid ""
 "Native MIDI support requires the Roland Upgrade from LucasArts,\n"
 "but %s is missing. Using AdLib instead."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:2420
+#: engines/scumm/scumm.cpp:2423
 msgid ""
 "This particular version of Monkey Island 1 is known to miss some\n"
 "required resources for MT-32. Using AdLib instead."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:4361
+#: engines/scumm/scumm.cpp:4364
 msgid ""
 "Usually, Maniac Mansion would start now. But for that to work, the game "
 "files for Maniac Mansion have to be in the 'Maniac' directory inside the "
 "Tentacle game directory, and the game has to be added to ScummVM."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:4485
+#: engines/scumm/scumm.cpp:4488
 msgid "Would you like to host or join a network play session?"
 msgstr ""
 
diff --git a/po/hu_HU.po b/po/hu_HU.po
index a2ad259f20e..fd2bb2a8e6e 100644
--- a/po/hu_HU.po
+++ b/po/hu_HU.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-08 11:04+0000\n"
+"POT-Creation-Date: 2026-06-09 13:22+0000\n"
 "PO-Revision-Date: 2025-03-07 00:18+0000\n"
 "Last-Translator: Hov??nszki Tam??s <gahex68379 at lofiey.com>\n"
 "Language-Team: Hungarian <https://translations.scummvm.org/projects/scummvm/"
@@ -343,7 +343,7 @@ msgstr ""
 #: engines/hypno/metaengine.cpp:192 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:197
 #: engines/mm/mm1/metaengine.cpp:54 engines/parallaction/metaengine.cpp:169
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4467
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
 #: engines/sherlock/metaengine.cpp:638 engines/sherlock/metaengine.cpp:1244
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:328
@@ -362,7 +362,7 @@ msgstr "Igen"
 #: engines/hypno/metaengine.cpp:198 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:204
 #: engines/mm/mm1/metaengine.cpp:52 engines/parallaction/metaengine.cpp:175
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4467
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
 #: engines/sherlock/metaengine.cpp:643 engines/sherlock/metaengine.cpp:1250
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:329
@@ -390,7 +390,7 @@ msgstr "A JSON k??d tartalma hib??s."
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:123
 #: engines/drascula/saveload.cpp:51 engines/grim/inputdialog.cpp:83
 #: engines/parallaction/saveload.cpp:270 engines/sci/engine/kgraphics.cpp:76
-#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4479
+#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4482
 #: engines/sword1/control.cpp:2816
 msgid "OK"
 msgstr "OK"
@@ -1053,7 +1053,7 @@ msgstr "Interakci??"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1100
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1124
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1074,7 +1074,7 @@ msgstr "Fel"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1067
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1091
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1095,7 +1095,7 @@ msgstr "Le"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1080
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1104
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1115,7 +1115,7 @@ msgstr "Bal"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1087
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1111
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3457,7 +3457,7 @@ msgstr "Grafikus kalandj??t??k motor"
 #: engines/mohawk/riven_stacks/aspit.cpp:454
 #: engines/neverhood/metaengine.cpp:200 engines/saga/metaengine.cpp:449
 #: engines/scumm/help.cpp:84 engines/scumm/help.cpp:86
-#: engines/scumm/scumm.cpp:4479 engines/sludge/keymapper_tables.h:95
+#: engines/scumm/scumm.cpp:4482 engines/sludge/keymapper_tables.h:95
 #: engines/sludge/keymapper_tables.h:144 engines/sludge/keymapper_tables.h:223
 #: engines/sludge/keymapper_tables.h:343 engines/sludge/keymapper_tables.h:355
 #: engines/sludge/keymapper_tables.h:416 engines/sludge/keymapper_tables.h:429
@@ -3961,7 +3961,7 @@ msgid "Game not implemented"
 msgstr "A j??t??k m??g nem ker??lt implement??l??sra"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
-#: engines/scumm/metaengine.cpp:1050
+#: engines/scumm/metaengine.cpp:1074
 #, fuzzy
 #| msgid "Default game keymap"
 msgid "Default game keymappings"
@@ -6351,7 +6351,7 @@ msgstr "R2"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1208 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1232 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "Men??"
 
@@ -7309,7 +7309,7 @@ msgstr ""
 msgid "Quick load or save game isn't supported in this location"
 msgstr "A j??t??k gyors t??lt??se vagy ment??se nem t??mogatott ezen a helyen"
 
-#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3418
+#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3421
 #, c-format
 msgid ""
 "Failed to load saved game from file:\n"
@@ -7320,7 +7320,7 @@ msgstr ""
 "\n"
 "%s"
 
-#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3405
+#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3408
 #, c-format
 msgid ""
 "Failed to save game to file:\n"
@@ -7331,7 +7331,7 @@ msgstr ""
 "\n"
 "%s"
 
-#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3438
+#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3441
 #, c-format
 msgid ""
 "Successfully saved game in file:\n"
@@ -7844,7 +7844,7 @@ msgstr "Harcmodor V??lt??s"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1126 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -8606,7 +8606,7 @@ msgstr "Besz??l??s/Interakci??"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1113
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1137
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -10848,7 +10848,7 @@ msgid "Player ammo will never decrease."
 msgstr "A j??t??kos l??szere soha nem fog cs??kkenni."
 
 #: engines/hypno/metaengine.cpp:70 engines/scumm/metaengine.cpp:891
-#: engines/scumm/metaengine.cpp:909
+#: engines/scumm/metaengine.cpp:918
 msgid "Unlock all levels"
 msgstr "Minden p??lya el??rhet??v??"
 
@@ -13438,7 +13438,7 @@ msgid "Your Name:"
 msgstr "Az ??n Neve:"
 
 #. I18N: Button, start hosting online multiplayer game
-#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4485
+#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4488
 msgid "Host"
 msgstr "Gazda"
 
@@ -13448,7 +13448,7 @@ msgid "Querying games..."
 msgstr "J??t??kok lek??rdez??se..."
 
 #. I18N: Join online multiplayer game
-#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4485
+#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4488
 msgid "Join"
 msgstr "Csatlakoz??s"
 
@@ -14411,136 +14411,146 @@ msgstr "Magas felbont??s be"
 msgid "Run the game in 640x400 high resolution mode instead of 320x200"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:910
+#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:919
 #, fuzzy
 #| msgid "All levels will be available to play."
 msgid "All levels will be available without requiring passwords"
 msgstr "Minden p??lya el??rhet??v?? v??lik a j??t??kos sz??m??ra."
 
-#: engines/scumm/metaengine.cpp:900
+#: engines/scumm/metaengine.cpp:900 engines/scumm/metaengine.cpp:927
 #, fuzzy
 #| msgid "No date saved"
 msgid "No damage"
 msgstr "D??tum nem ker??lt ment??sre"
 
-#: engines/scumm/metaengine.cpp:901
+#: engines/scumm/metaengine.cpp:901 engines/scumm/metaengine.cpp:928
 #, fuzzy
 #| msgid "Disable save menu"
 msgid "Disable player damage"
 msgstr "Ment??s men?? letilt??s"
 
-#: engines/scumm/metaengine.cpp:1061
+#: engines/scumm/metaengine.cpp:909
+#, fuzzy
+#| msgid "Fast mode"
+msgid "Yoda mode"
+msgstr "Sebes m??d"
+
+#: engines/scumm/metaengine.cpp:910
+msgid "Enable original Yoda mode shortcuts, including movie mode and auto play"
+msgstr ""
+
+#: engines/scumm/metaengine.cpp:1085
 #, fuzzy
 #| msgid "Down Left"
 msgid "Down left"
 msgstr "Balra Le"
 
-#: engines/scumm/metaengine.cpp:1074
+#: engines/scumm/metaengine.cpp:1098
 #, fuzzy
 #| msgid "Down Right"
 msgid "Down right"
 msgstr "Jobbra Le"
 
-#: engines/scumm/metaengine.cpp:1094
+#: engines/scumm/metaengine.cpp:1118
 #, fuzzy
 #| msgid "Upper left item"
 msgid "Up left"
 msgstr "Bal fels?? t??rgy"
 
-#: engines/scumm/metaengine.cpp:1107
+#: engines/scumm/metaengine.cpp:1131
 #, fuzzy
 #| msgid "Up Right"
 msgid "Up right"
 msgstr "Jobbra Fel"
 
-#: engines/scumm/metaengine.cpp:1119
+#: engines/scumm/metaengine.cpp:1143
 msgid "Switch weapon"
 msgstr "Fegyver v??lt??s"
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1133
+#: engines/scumm/metaengine.cpp:1157
 msgid "Win the bike fight cheat"
 msgstr "Csal??s a motoros harc megnyer??s??hez"
 
-#: engines/scumm/metaengine.cpp:1142
+#: engines/scumm/metaengine.cpp:1166
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1144 engines/scumm/metaengine.cpp:1221
+#: engines/scumm/metaengine.cpp:1168 engines/scumm/metaengine.cpp:1245
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1149 engines/scumm/metaengine.cpp:1226
+#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1250
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1231
+#: engines/scumm/metaengine.cpp:1178 engines/scumm/metaengine.cpp:1255
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
+#: engines/scumm/metaengine.cpp:1183 engines/scumm/metaengine.cpp:1260
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1241
+#: engines/scumm/metaengine.cpp:1188 engines/scumm/metaengine.cpp:1265
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "Felvesz"
 
-#: engines/scumm/metaengine.cpp:1171 engines/scumm/metaengine.cpp:1248
+#: engines/scumm/metaengine.cpp:1195 engines/scumm/metaengine.cpp:1272
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "G??rget??s LE"
 
-#: engines/scumm/metaengine.cpp:1178 engines/scumm/metaengine.cpp:1255
+#: engines/scumm/metaengine.cpp:1202 engines/scumm/metaengine.cpp:1279
 #, fuzzy
 #| msgid "Slide Left"
 msgid "Stick left"
 msgstr "Cs??sz??s balra"
 
-#: engines/scumm/metaengine.cpp:1185 engines/scumm/metaengine.cpp:1262
+#: engines/scumm/metaengine.cpp:1209 engines/scumm/metaengine.cpp:1286
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick right"
 msgstr "Cs??sz??s jobbra"
 
-#: engines/scumm/metaengine.cpp:1192 engines/scumm/metaengine.cpp:1269
+#: engines/scumm/metaengine.cpp:1216 engines/scumm/metaengine.cpp:1293
 #, fuzzy
 #| msgid "Move left"
 msgid "Fire / select"
 msgstr "Mozg??s balra"
 
-#: engines/scumm/metaengine.cpp:1198
+#: engines/scumm/metaengine.cpp:1222
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Walk / menu back"
 msgstr "F?? men?? ??tugr??sa"
 
-#: engines/scumm/metaengine.cpp:1203
+#: engines/scumm/metaengine.cpp:1227
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu back"
 msgstr "F?? men?? ??tugr??sa"
 
-#: engines/scumm/metaengine.cpp:1219
+#: engines/scumm/metaengine.cpp:1243
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1274
+#: engines/scumm/metaengine.cpp:1298
 #, fuzzy
 #| msgid "Move Back"
 msgid "Cover / back"
 msgstr "Mozg??s h??tra"
 
-#: engines/scumm/metaengine.cpp:1280
+#: engines/scumm/metaengine.cpp:1304
 #, fuzzy
 #| msgid "Skip dialogue"
 msgid "Skip / back"
 msgstr "Dial??gus ??tugr??sa"
 
-#: engines/scumm/metaengine.cpp:1287
+#: engines/scumm/metaengine.cpp:1311
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu"
@@ -14582,13 +14592,13 @@ msgstr ""
 "A k??par??ny korrekci??ja ??gy ??rhet?? el, hogy a 'motor' f??l alatt a felbont??st "
 "320x200-ra v??gja le."
 
-#: engines/scumm/scumm.cpp:1362
+#: engines/scumm/scumm.cpp:1365
 #, c-format
 msgid "This game requires the '%s' Macintosh executable for its fonts."
 msgstr ""
 "A j??t??khoz sz??ks??g van a '%s' Macintosh ind??t??f??jlra a bet??t??pus miatt."
 
-#: engines/scumm/scumm.cpp:1371
+#: engines/scumm/scumm.cpp:1374
 #, c-format
 msgid ""
 "This game requires the '%s' Macintosh executable for its music and fonts."
@@ -14596,7 +14606,7 @@ msgstr ""
 "A j??t??khoz sz??ks??g van a '%s' Macintosh ind??t??f??jlra a zene ??s bet??t??pus "
 "miatt."
 
-#: engines/scumm/scumm.cpp:1375
+#: engines/scumm/scumm.cpp:1378
 #, c-format
 msgid ""
 "Could not find the '%s' Macintosh executable to read resources from. %s will "
@@ -14605,25 +14615,25 @@ msgstr ""
 "Az adatok nem nyerhet??ek ki, mert nem tal??lhat?? a '%s' Macintosh ind??t??f??jl. "
 "Emiatt %s kikapcsol??sra ker??l."
 
-#: engines/scumm/scumm.cpp:1376
+#: engines/scumm/scumm.cpp:1379
 msgid "The Mac GUI"
 msgstr "A Mac fel??let"
 
-#: engines/scumm/scumm.cpp:1376
+#: engines/scumm/scumm.cpp:1379
 msgid "The music and the Mac GUI"
 msgstr "A zene ??s a Mac fel??let"
 
-#: engines/scumm/scumm.cpp:1405
+#: engines/scumm/scumm.cpp:1408
 #, c-format
 msgid "Could not open Macintosh resource file %s"
 msgstr "Nem nyithat?? meg a %s adat f??jl"
 
-#: engines/scumm/scumm.cpp:1408
+#: engines/scumm/scumm.cpp:1411
 #, c-format
 msgid "Could not find resource fork in Macintosh resource file %s"
 msgstr "Fork adat nem tal??lhat?? a %s Macintosh adatf??jlban"
 
-#: engines/scumm/scumm.cpp:1415
+#: engines/scumm/scumm.cpp:1418
 #, c-format
 msgid ""
 "'%s' appears to be the wrong Dig executable. It may be the launcher one "
@@ -14636,7 +14646,7 @@ msgstr ""
 "men?? ??s dial??gus defin??ci??kat. Keresse meg a 'The Dig f' mapp??t a CD-j??n. "
 "Ennek alk??nyvt??raiban kell legyen az amit keres."
 
-#: engines/scumm/scumm.cpp:1581
+#: engines/scumm/scumm.cpp:1584
 msgid ""
 "It appears your ScummVM version was not built with TrueType Fonts support.\n"
 "\n"
@@ -14644,7 +14654,7 @@ msgid ""
 "some of the graphics on screen will be missing."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1915
+#: engines/scumm/scumm.cpp:1918
 msgid ""
 "Audio files compressed with ScummVM Tools were detected; *.BUN/*.SOU\n"
 "compression is not supported anymore for this game, audio will be disabled.\n"
@@ -14654,7 +14664,7 @@ msgstr ""
 "a t??m??r??t??s m??r nem t??mogatott ebben a j??t??kban, a hang letilt??sra ker??l.\n"
 "K??rj??k, m??solja a t??m??r??tetlen j??t??kot az eredeti adathordoz??r??l."
 
-#: engines/scumm/scumm.cpp:2405
+#: engines/scumm/scumm.cpp:2408
 #, c-format
 msgid ""
 "Native MIDI support requires the Roland Upgrade from LucasArts,\n"
@@ -14663,7 +14673,7 @@ msgstr ""
 "A nat??v MIDI t??mogat??shoz sz??ks??ges a Roland Upgrade a LucasArts-t??l,\n"
 "de a %s hi??nyzik. Helyette AdLib ker??l haszn??latra."
 
-#: engines/scumm/scumm.cpp:2420
+#: engines/scumm/scumm.cpp:2423
 msgid ""
 "This particular version of Monkey Island 1 is known to miss some\n"
 "required resources for MT-32. Using AdLib instead."
@@ -14671,7 +14681,7 @@ msgstr ""
 "A Monkey Island 1 ezen verzi??ja arr??l ismeretes, hogy hi??nyzik n??h??ny\n"
 "sz??ks??ges er??forr??s az MT-32-h??z. Helyette AdLib ker??l haszn??latra."
 
-#: engines/scumm/scumm.cpp:4361
+#: engines/scumm/scumm.cpp:4364
 msgid ""
 "Usually, Maniac Mansion would start now. But for that to work, the game "
 "files for Maniac Mansion have to be in the 'Maniac' directory inside the "
@@ -14681,7 +14691,7 @@ msgstr ""
 "Maniac Mansion j??t??k f??jlokban benne kell lennie a 'Maniac' mapp??ban a "
 "Tentacle mapp??n bel??l, ??s a j??t??kot hozz?? kell adni a ScummVM ind??t??hoz."
 
-#: engines/scumm/scumm.cpp:4485
+#: engines/scumm/scumm.cpp:4488
 msgid "Would you like to host or join a network play session?"
 msgstr "Szeretne h??l??zati j??t??kmenetet hosztolni vagy csatlakozni?"
 
diff --git a/po/it_IT.po b/po/it_IT.po
index 31685c62986..938e0dce233 100644
--- a/po/it_IT.po
+++ b/po/it_IT.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-08 11:04+0000\n"
+"POT-Creation-Date: 2026-06-09 13:22+0000\n"
 "PO-Revision-Date: 2026-06-09 10:24+0000\n"
 "Last-Translator: Walter Agazzi <walter.agazzi at protonmail.com>\n"
 "Language-Team: Italian <https://translations.scummvm.org/projects/scummvm/"
@@ -340,7 +340,7 @@ msgstr "Un altro servizio di archiviazione ?? attivo. Vuoi interromperlo?"
 #: engines/hypno/metaengine.cpp:192 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:197
 #: engines/mm/mm1/metaengine.cpp:54 engines/parallaction/metaengine.cpp:169
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4467
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
 #: engines/sherlock/metaengine.cpp:638 engines/sherlock/metaengine.cpp:1244
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:328
@@ -359,7 +359,7 @@ msgstr "S??"
 #: engines/hypno/metaengine.cpp:198 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:204
 #: engines/mm/mm1/metaengine.cpp:52 engines/parallaction/metaengine.cpp:175
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4467
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
 #: engines/sherlock/metaengine.cpp:643 engines/sherlock/metaengine.cpp:1250
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:329
@@ -387,7 +387,7 @@ msgstr "Il codice JSON non ?? valido."
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:123
 #: engines/drascula/saveload.cpp:51 engines/grim/inputdialog.cpp:83
 #: engines/parallaction/saveload.cpp:270 engines/sci/engine/kgraphics.cpp:76
-#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4479
+#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4482
 #: engines/sword1/control.cpp:2816
 msgid "OK"
 msgstr "OK"
@@ -1056,7 +1056,7 @@ msgstr "Interazione"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1100
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1124
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1077,7 +1077,7 @@ msgstr "Su"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1067
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1091
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1098,7 +1098,7 @@ msgstr "Gi??"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1080
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1104
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1118,7 +1118,7 @@ msgstr "Sinistra"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1087
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1111
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3418,7 +3418,7 @@ msgstr "Motore per avventure grafiche"
 #: engines/mohawk/riven_stacks/aspit.cpp:454
 #: engines/neverhood/metaengine.cpp:200 engines/saga/metaengine.cpp:449
 #: engines/scumm/help.cpp:84 engines/scumm/help.cpp:86
-#: engines/scumm/scumm.cpp:4479 engines/sludge/keymapper_tables.h:95
+#: engines/scumm/scumm.cpp:4482 engines/sludge/keymapper_tables.h:95
 #: engines/sludge/keymapper_tables.h:144 engines/sludge/keymapper_tables.h:223
 #: engines/sludge/keymapper_tables.h:343 engines/sludge/keymapper_tables.h:355
 #: engines/sludge/keymapper_tables.h:416 engines/sludge/keymapper_tables.h:429
@@ -3923,7 +3923,7 @@ msgid "Game not implemented"
 msgstr "Gioco non implementato"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
-#: engines/scumm/metaengine.cpp:1050
+#: engines/scumm/metaengine.cpp:1074
 msgid "Default game keymappings"
 msgstr "Mappa tasti predefinita"
 
@@ -6148,7 +6148,7 @@ msgstr "R2"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1208 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1232 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "Menu"
 
@@ -7103,7 +7103,7 @@ msgid "Quick load or save game isn't supported in this location"
 msgstr ""
 "Il salvataggio/caricamento rapido non ?? disponibile in questa locazione"
 
-#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3418
+#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3421
 #, c-format
 msgid ""
 "Failed to load saved game from file:\n"
@@ -7114,7 +7114,7 @@ msgstr ""
 "\n"
 "%s"
 
-#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3405
+#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3408
 #, c-format
 msgid ""
 "Failed to save game to file:\n"
@@ -7125,7 +7125,7 @@ msgstr ""
 "\n"
 "%s"
 
-#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3438
+#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3441
 #, c-format
 msgid ""
 "Successfully saved game in file:\n"
@@ -7625,7 +7625,7 @@ msgstr "Attiva / Disattiva modalit?? combattimento"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1126 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -8287,7 +8287,7 @@ msgstr "Parlare / Interazione"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1113
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1137
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -10268,7 +10268,7 @@ msgid "Player ammo will never decrease."
 msgstr "Il giocatore non consumer?? mai munizioni."
 
 #: engines/hypno/metaengine.cpp:70 engines/scumm/metaengine.cpp:891
-#: engines/scumm/metaengine.cpp:909
+#: engines/scumm/metaengine.cpp:918
 msgid "Unlock all levels"
 msgstr "Sblocca tutti i livelli"
 
@@ -12671,7 +12671,7 @@ msgid "Your Name:"
 msgstr "Tuo Nome:"
 
 #. I18N: Button, start hosting online multiplayer game
-#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4485
+#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4488
 msgid "Host"
 msgstr "Ospita"
 
@@ -12681,7 +12681,7 @@ msgid "Querying games..."
 msgstr "Ricerca partite..."
 
 #. I18N: Join online multiplayer game
-#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4485
+#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4488
 msgid "Join"
 msgstr "Unisciti"
 
@@ -13657,105 +13657,115 @@ msgstr "Modalit?? ad alta risoluzione"
 msgid "Run the game in 640x400 high resolution mode instead of 320x200"
 msgstr "Esegue il gioco in alta risoluzione (640x400), anzich?? 320x200"
 
-#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:910
+#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:919
 msgid "All levels will be available without requiring passwords"
 msgstr ""
 "Permette l'accesso a qualsiasi livello del gioco senza richiedere password"
 
-#: engines/scumm/metaengine.cpp:900
+#: engines/scumm/metaengine.cpp:900 engines/scumm/metaengine.cpp:927
 msgid "No damage"
 msgstr "Nessun danno"
 
-#: engines/scumm/metaengine.cpp:901
+#: engines/scumm/metaengine.cpp:901 engines/scumm/metaengine.cpp:928
 msgid "Disable player damage"
 msgstr "Disabilita i danni al giocatore"
 
-#: engines/scumm/metaengine.cpp:1061
+#: engines/scumm/metaengine.cpp:909
+#, fuzzy
+#| msgid "Fast mode"
+msgid "Yoda mode"
+msgstr "Modalit?? velocizzata"
+
+#: engines/scumm/metaengine.cpp:910
+msgid "Enable original Yoda mode shortcuts, including movie mode and auto play"
+msgstr ""
+
+#: engines/scumm/metaengine.cpp:1085
 msgid "Down left"
 msgstr "Gi?? sinistra"
 
-#: engines/scumm/metaengine.cpp:1074
+#: engines/scumm/metaengine.cpp:1098
 msgid "Down right"
 msgstr "Gi?? destra"
 
-#: engines/scumm/metaengine.cpp:1094
+#: engines/scumm/metaengine.cpp:1118
 msgid "Up left"
 msgstr "Su sinistra"
 
-#: engines/scumm/metaengine.cpp:1107
+#: engines/scumm/metaengine.cpp:1131
 msgid "Up right"
 msgstr "Su destra"
 
-#: engines/scumm/metaengine.cpp:1119
+#: engines/scumm/metaengine.cpp:1143
 msgid "Switch weapon"
 msgstr "Cambia arma"
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1133
+#: engines/scumm/metaengine.cpp:1157
 msgid "Win the bike fight cheat"
 msgstr "Cheat: Vinci combattimento in moto"
 
-#: engines/scumm/metaengine.cpp:1142
+#: engines/scumm/metaengine.cpp:1166
 msgid "Rebel Assault controls"
 msgstr "Controlli di Rebel Assault"
 
-#: engines/scumm/metaengine.cpp:1144 engines/scumm/metaengine.cpp:1221
+#: engines/scumm/metaengine.cpp:1168 engines/scumm/metaengine.cpp:1245
 msgid "Aim up / menu up"
 msgstr "Mira verso l'alto / men?? su"
 
-#: engines/scumm/metaengine.cpp:1149 engines/scumm/metaengine.cpp:1226
+#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1250
 msgid "Aim down / menu down"
 msgstr "Mira verso il basso / men?? gi??"
 
-#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1231
+#: engines/scumm/metaengine.cpp:1178 engines/scumm/metaengine.cpp:1255
 msgid "Aim left / menu left"
 msgstr "Mira a sinistra / men?? sinistra"
 
-#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
+#: engines/scumm/metaengine.cpp:1183 engines/scumm/metaengine.cpp:1260
 msgid "Aim right / menu right"
 msgstr "Mira a destra / men?? destra"
 
-#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1241
+#: engines/scumm/metaengine.cpp:1188 engines/scumm/metaengine.cpp:1265
 msgid "Stick up"
 msgstr "Joystick su"
 
-#: engines/scumm/metaengine.cpp:1171 engines/scumm/metaengine.cpp:1248
+#: engines/scumm/metaengine.cpp:1195 engines/scumm/metaengine.cpp:1272
 msgid "Stick down"
 msgstr "Joystick gi??"
 
-#: engines/scumm/metaengine.cpp:1178 engines/scumm/metaengine.cpp:1255
+#: engines/scumm/metaengine.cpp:1202 engines/scumm/metaengine.cpp:1279
 msgid "Stick left"
 msgstr "Joystick sinistra"
 
-#: engines/scumm/metaengine.cpp:1185 engines/scumm/metaengine.cpp:1262
+#: engines/scumm/metaengine.cpp:1209 engines/scumm/metaengine.cpp:1286
 msgid "Stick right"
 msgstr "Joystick destra"
 
-#: engines/scumm/metaengine.cpp:1192 engines/scumm/metaengine.cpp:1269
+#: engines/scumm/metaengine.cpp:1216 engines/scumm/metaengine.cpp:1293
 msgid "Fire / select"
 msgstr "Spara / seleziona"
 
-#: engines/scumm/metaengine.cpp:1198
+#: engines/scumm/metaengine.cpp:1222
 msgid "Walk / menu back"
 msgstr "Cammina / Men?? indietro"
 
-#: engines/scumm/metaengine.cpp:1203
+#: engines/scumm/metaengine.cpp:1227
 msgid "Skip / menu back"
 msgstr "Salta / indietro (men??)"
 
-#: engines/scumm/metaengine.cpp:1219
+#: engines/scumm/metaengine.cpp:1243
 msgid "Rebel Assault II controls"
 msgstr "Controlli di Rebel Assault II"
 
-#: engines/scumm/metaengine.cpp:1274
+#: engines/scumm/metaengine.cpp:1298
 msgid "Cover / back"
 msgstr "Copertura / indietro"
 
-#: engines/scumm/metaengine.cpp:1280
+#: engines/scumm/metaengine.cpp:1304
 msgid "Skip / back"
 msgstr "Salta / indietro"
 
-#: engines/scumm/metaengine.cpp:1287
+#: engines/scumm/metaengine.cpp:1311
 msgid "Skip / menu"
 msgstr "Salta / men??"
 
@@ -13796,13 +13806,13 @@ msgstr ""
 "Perch?? funzioni, devi abilitare anche l'opzione \"Riduci la risoluzione "
 "verticale\" presente nella scheda \"motore\"."
 
-#: engines/scumm/scumm.cpp:1362
+#: engines/scumm/scumm.cpp:1365
 #, c-format
 msgid "This game requires the '%s' Macintosh executable for its fonts."
 msgstr ""
 "Questo gioco richiede l'eseguibile Macintosh '%s', per caricare i caratteri."
 
-#: engines/scumm/scumm.cpp:1371
+#: engines/scumm/scumm.cpp:1374
 #, c-format
 msgid ""
 "This game requires the '%s' Macintosh executable for its music and fonts."
@@ -13810,7 +13820,7 @@ msgstr ""
 "Questo gioco richiede l'eseguibile Macintosh '%s', per caricare la musica e "
 "caratteri."
 
-#: engines/scumm/scumm.cpp:1375
+#: engines/scumm/scumm.cpp:1378
 #, c-format
 msgid ""
 "Could not find the '%s' Macintosh executable to read resources from. %s will "
@@ -13819,26 +13829,26 @@ msgstr ""
 "L'eseguibile Macintosh '%s', necessario per caricare le risorse, non ?? stato "
 "trovato. Verranno disabilitati: %s."
 
-#: engines/scumm/scumm.cpp:1376
+#: engines/scumm/scumm.cpp:1379
 msgid "The Mac GUI"
 msgstr "Interfaccia grafica Mac"
 
-#: engines/scumm/scumm.cpp:1376
+#: engines/scumm/scumm.cpp:1379
 msgid "The music and the Mac GUI"
 msgstr "Musica e interfaccia grafica Macintosh"
 
-#: engines/scumm/scumm.cpp:1405
+#: engines/scumm/scumm.cpp:1408
 #, c-format
 msgid "Could not open Macintosh resource file %s"
 msgstr "Non ?? stato possibile aprire il file risorsa Macintosh %s"
 
-#: engines/scumm/scumm.cpp:1408
+#: engines/scumm/scumm.cpp:1411
 #, c-format
 msgid "Could not find resource fork in Macintosh resource file %s"
 msgstr ""
 "Non ?? stato possibile trovare la resource fork nel file risorsa Macintosh %s"
 
-#: engines/scumm/scumm.cpp:1415
+#: engines/scumm/scumm.cpp:1418
 #, c-format
 msgid ""
 "'%s' appears to be the wrong Dig executable. It may be the launcher one "
@@ -13851,7 +13861,7 @@ msgstr ""
 "definizioni per i menu e le finestre. Prova a cercare una cartella 'The Dig "
 "f' nel CD, e quindi seleziona una sua sottodirectory qualsiasi."
 
-#: engines/scumm/scumm.cpp:1581
+#: engines/scumm/scumm.cpp:1584
 msgid ""
 "It appears your ScummVM version was not built with TrueType Fonts support.\n"
 "\n"
@@ -13864,7 +13874,7 @@ msgstr ""
 "Dato the la serie One-Stop Fun Shop fa uso estensivo dei caratteri TTF,\n"
 "alcune parti della grafica saranno assenti."
 
-#: engines/scumm/scumm.cpp:1915
+#: engines/scumm/scumm.cpp:1918
 msgid ""
 "Audio files compressed with ScummVM Tools were detected; *.BUN/*.SOU\n"
 "compression is not supported anymore for this game, audio will be disabled.\n"
@@ -13876,7 +13886,7 @@ msgstr ""
 "disabilitato.\n"
 "Per favore, copia tali files dai supporti originali senza comprimerli."
 
-#: engines/scumm/scumm.cpp:2405
+#: engines/scumm/scumm.cpp:2408
 #, c-format
 msgid ""
 "Native MIDI support requires the Roland Upgrade from LucasArts,\n"
@@ -13885,7 +13895,7 @@ msgstr ""
 "Il supporto nativo MIDI richiede il Roland Upgrade della LucasArts,\n"
 "ma %s non ?? presente. Verr?? usato AdLib."
 
-#: engines/scumm/scumm.cpp:2420
+#: engines/scumm/scumm.cpp:2423
 msgid ""
 "This particular version of Monkey Island 1 is known to miss some\n"
 "required resources for MT-32. Using AdLib instead."
@@ -13893,7 +13903,7 @@ msgstr ""
 "Questa specifica versione di Monkey Island 1 ?? priva di alcune\n"
 "risorse necessarie per il sonoro MT-32. Verr?? utilizzato AdLib."
 
-#: engines/scumm/scumm.cpp:4361
+#: engines/scumm/scumm.cpp:4364
 msgid ""
 "Usually, Maniac Mansion would start now. But for that to work, the game "
 "files for Maniac Mansion have to be in the 'Maniac' directory inside the "
@@ -13904,7 +13914,7 @@ msgstr ""
 "sottocartella \"MANIAC\" all'interno della cartella principale di Day Of The "
 "Tentacle. Il gioco deve inoltre essere aggiunto a ScummVM."
 
-#: engines/scumm/scumm.cpp:4485
+#: engines/scumm/scumm.cpp:4488
 msgid "Would you like to host or join a network play session?"
 msgstr "Desideri ospitare o collegarti ad una sessione di gioco in rete?"
 
diff --git a/po/ja.po b/po/ja.po
index dbcd14cb513..25d6f05e03c 100644
--- a/po/ja.po
+++ b/po/ja.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-08 11:04+0000\n"
+"POT-Creation-Date: 2026-06-09 13:22+0000\n"
 "PO-Revision-Date: 2026-06-04 07:22+0000\n"
 "Last-Translator: VAN-Gluon <van.gluonp at gmail.com>\n"
 "Language-Team: Japanese <https://translations.scummvm.org/projects/scummvm/"
@@ -339,7 +339,7 @@ msgstr "????????????????????????????????????????????????????????????????????????
 #: engines/hypno/metaengine.cpp:192 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:197
 #: engines/mm/mm1/metaengine.cpp:54 engines/parallaction/metaengine.cpp:169
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4467
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
 #: engines/sherlock/metaengine.cpp:638 engines/sherlock/metaengine.cpp:1244
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:328
@@ -358,7 +358,7 @@ msgstr "??????"
 #: engines/hypno/metaengine.cpp:198 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:204
 #: engines/mm/mm1/metaengine.cpp:52 engines/parallaction/metaengine.cpp:175
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4467
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
 #: engines/sherlock/metaengine.cpp:643 engines/sherlock/metaengine.cpp:1250
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:329
@@ -386,7 +386,7 @@ msgstr "JSON????????????????????????????????????"
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:123
 #: engines/drascula/saveload.cpp:51 engines/grim/inputdialog.cpp:83
 #: engines/parallaction/saveload.cpp:270 engines/sci/engine/kgraphics.cpp:76
-#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4479
+#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4482
 #: engines/sword1/control.cpp:2816
 msgid "OK"
 msgstr "OK"
@@ -1042,7 +1042,7 @@ msgstr "??????"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1100
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1124
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1063,7 +1063,7 @@ msgstr "???"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1067
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1091
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1084,7 +1084,7 @@ msgstr "???"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1080
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1104
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1104,7 +1104,7 @@ msgstr "???"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1087
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1111
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3306,7 +3306,7 @@ msgstr "???????????????????????????????????????????????????"
 #: engines/mohawk/riven_stacks/aspit.cpp:454
 #: engines/neverhood/metaengine.cpp:200 engines/saga/metaengine.cpp:449
 #: engines/scumm/help.cpp:84 engines/scumm/help.cpp:86
-#: engines/scumm/scumm.cpp:4479 engines/sludge/keymapper_tables.h:95
+#: engines/scumm/scumm.cpp:4482 engines/sludge/keymapper_tables.h:95
 #: engines/sludge/keymapper_tables.h:144 engines/sludge/keymapper_tables.h:223
 #: engines/sludge/keymapper_tables.h:343 engines/sludge/keymapper_tables.h:355
 #: engines/sludge/keymapper_tables.h:416 engines/sludge/keymapper_tables.h:429
@@ -3781,7 +3781,7 @@ msgid "Game not implemented"
 msgstr "?????????????????????"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
-#: engines/scumm/metaengine.cpp:1050
+#: engines/scumm/metaengine.cpp:1074
 msgid "Default game keymappings"
 msgstr "?????????????????????(???????????????)"
 
@@ -6002,7 +6002,7 @@ msgstr "R2"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1208 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1232 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "????????????"
 
@@ -6943,7 +6943,7 @@ msgid "Quick load or save game isn't supported in this location"
 msgstr ""
 "????????????????????????????????????????????????????????????????????????????????????????????????????????????"
 
-#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3418
+#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3421
 #, c-format
 msgid ""
 "Failed to load saved game from file:\n"
@@ -6954,7 +6954,7 @@ msgstr ""
 "\n"
 "%s"
 
-#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3405
+#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3408
 #, c-format
 msgid ""
 "Failed to save game to file:\n"
@@ -6965,7 +6965,7 @@ msgstr ""
 "\n"
 "%s"
 
-#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3438
+#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3441
 #, c-format
 msgid ""
 "Successfully saved game in file:\n"
@@ -7448,7 +7448,7 @@ msgstr "??????????????????"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1126 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -8209,7 +8209,7 @@ msgstr "??????/??????"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1113
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1137
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -10463,7 +10463,7 @@ msgid "Player ammo will never decrease."
 msgstr "?????????????????????????????????????????????"
 
 #: engines/hypno/metaengine.cpp:70 engines/scumm/metaengine.cpp:891
-#: engines/scumm/metaengine.cpp:909
+#: engines/scumm/metaengine.cpp:918
 msgid "Unlock all levels"
 msgstr "??????????????????"
 
@@ -13058,7 +13058,7 @@ msgid "Your Name:"
 msgstr "??????????????????"
 
 #. I18N: Button, start hosting online multiplayer game
-#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4485
+#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4488
 msgid "Host"
 msgstr "?????????"
 
@@ -13068,7 +13068,7 @@ msgid "Querying games..."
 msgstr "?????????????????????..."
 
 #. I18N: Join online multiplayer game
-#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4485
+#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4488
 msgid "Join"
 msgstr "??????"
 
@@ -14029,136 +14029,146 @@ msgstr "????????????ON"
 msgid "Run the game in 640x400 high resolution mode instead of 320x200"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:910
+#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:919
 #, fuzzy
 #| msgid "All levels will be available to play."
 msgid "All levels will be available without requiring passwords"
 msgstr "?????????????????????????????????????????????"
 
-#: engines/scumm/metaengine.cpp:900
+#: engines/scumm/metaengine.cpp:900 engines/scumm/metaengine.cpp:927
 #, fuzzy
 #| msgid "No date saved"
 msgid "No damage"
 msgstr "???????????????"
 
-#: engines/scumm/metaengine.cpp:901
+#: engines/scumm/metaengine.cpp:901 engines/scumm/metaengine.cpp:928
 #, fuzzy
 #| msgid "Disable save menu"
 msgid "Disable player damage"
 msgstr "???????????????????????????????????????"
 
-#: engines/scumm/metaengine.cpp:1061
+#: engines/scumm/metaengine.cpp:909
+#, fuzzy
+#| msgid "Fast mode"
+msgid "Yoda mode"
+msgstr "???????????????"
+
+#: engines/scumm/metaengine.cpp:910
+msgid "Enable original Yoda mode shortcuts, including movie mode and auto play"
+msgstr ""
+
+#: engines/scumm/metaengine.cpp:1085
 #, fuzzy
 #| msgid "Down Left"
 msgid "Down left"
 msgstr "??????"
 
-#: engines/scumm/metaengine.cpp:1074
+#: engines/scumm/metaengine.cpp:1098
 #, fuzzy
 #| msgid "Down Right"
 msgid "Down right"
 msgstr "??????"
 
-#: engines/scumm/metaengine.cpp:1094
+#: engines/scumm/metaengine.cpp:1118
 #, fuzzy
 #| msgid "Upper left item"
 msgid "Up left"
 msgstr "?????????????????????"
 
-#: engines/scumm/metaengine.cpp:1107
+#: engines/scumm/metaengine.cpp:1131
 #, fuzzy
 #| msgid "Up Right"
 msgid "Up right"
 msgstr "??????"
 
-#: engines/scumm/metaengine.cpp:1119
+#: engines/scumm/metaengine.cpp:1143
 msgid "Switch weapon"
 msgstr "?????????????????????"
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1133
+#: engines/scumm/metaengine.cpp:1157
 msgid "Win the bike fight cheat"
 msgstr "????????????????????????"
 
-#: engines/scumm/metaengine.cpp:1142
+#: engines/scumm/metaengine.cpp:1166
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1144 engines/scumm/metaengine.cpp:1221
+#: engines/scumm/metaengine.cpp:1168 engines/scumm/metaengine.cpp:1245
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1149 engines/scumm/metaengine.cpp:1226
+#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1250
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1231
+#: engines/scumm/metaengine.cpp:1178 engines/scumm/metaengine.cpp:1255
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
+#: engines/scumm/metaengine.cpp:1183 engines/scumm/metaengine.cpp:1260
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1241
+#: engines/scumm/metaengine.cpp:1188 engines/scumm/metaengine.cpp:1265
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "??????"
 
-#: engines/scumm/metaengine.cpp:1171 engines/scumm/metaengine.cpp:1248
+#: engines/scumm/metaengine.cpp:1195 engines/scumm/metaengine.cpp:1272
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "?????????????????????"
 
-#: engines/scumm/metaengine.cpp:1178 engines/scumm/metaengine.cpp:1255
+#: engines/scumm/metaengine.cpp:1202 engines/scumm/metaengine.cpp:1279
 #, fuzzy
 #| msgid "Slide Left"
 msgid "Stick left"
 msgstr "??????????????????"
 
-#: engines/scumm/metaengine.cpp:1185 engines/scumm/metaengine.cpp:1262
+#: engines/scumm/metaengine.cpp:1209 engines/scumm/metaengine.cpp:1286
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick right"
 msgstr "??????????????????"
 
-#: engines/scumm/metaengine.cpp:1192 engines/scumm/metaengine.cpp:1269
+#: engines/scumm/metaengine.cpp:1216 engines/scumm/metaengine.cpp:1293
 #, fuzzy
 #| msgid "Move left"
 msgid "Fire / select"
 msgstr "????????????"
 
-#: engines/scumm/metaengine.cpp:1198
+#: engines/scumm/metaengine.cpp:1222
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Walk / menu back"
 msgstr "????????????????????????????????????"
 
-#: engines/scumm/metaengine.cpp:1203
+#: engines/scumm/metaengine.cpp:1227
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu back"
 msgstr "????????????????????????????????????"
 
-#: engines/scumm/metaengine.cpp:1219
+#: engines/scumm/metaengine.cpp:1243
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1274
+#: engines/scumm/metaengine.cpp:1298
 #, fuzzy
 #| msgid "Move Back"
 msgid "Cover / back"
 msgstr "??????"
 
-#: engines/scumm/metaengine.cpp:1280
+#: engines/scumm/metaengine.cpp:1304
 #, fuzzy
 #| msgid "Skip dialogue"
 msgid "Skip / back"
 msgstr "??????????????????????????????"
 
-#: engines/scumm/metaengine.cpp:1287
+#: engines/scumm/metaengine.cpp:1311
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu"
@@ -14190,13 +14200,13 @@ msgstr ""
 "?????????????????????????????????[????????????]?????????????????????320x200????????????????????????????????????"
 "??????????????????"
 
-#: engines/scumm/scumm.cpp:1362
+#: engines/scumm/scumm.cpp:1365
 #, fuzzy, c-format
 #| msgid "This game requires the 'Indy' Macintosh executable for its fonts."
 msgid "This game requires the '%s' Macintosh executable for its fonts."
 msgstr "?????????????????????????????????Macintosh????????????????????????Indy?????????????????????"
 
-#: engines/scumm/scumm.cpp:1371
+#: engines/scumm/scumm.cpp:1374
 #, fuzzy, c-format
 #| msgid ""
 #| "This game requires the 'Loom' Macintosh executable for its music and "
@@ -14205,7 +14215,7 @@ msgid ""
 "This game requires the '%s' Macintosh executable for its music and fonts."
 msgstr "??????????????????BGM??????????????????Macintosh????????????????????????Loom?????????????????????"
 
-#: engines/scumm/scumm.cpp:1375
+#: engines/scumm/scumm.cpp:1378
 #, fuzzy, c-format
 #| msgid ""
 #| "Could not find the 'Monkey Island' Macintosh executable to read the\n"
@@ -14218,25 +14228,25 @@ msgstr ""
 "??????\n"
 "?????????????????????????????????"
 
-#: engines/scumm/scumm.cpp:1376
+#: engines/scumm/scumm.cpp:1379
 msgid "The Mac GUI"
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1376
+#: engines/scumm/scumm.cpp:1379
 msgid "The music and the Mac GUI"
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1405
+#: engines/scumm/scumm.cpp:1408
 #, c-format
 msgid "Could not open Macintosh resource file %s"
 msgstr "Macintosh??????????????? %s ??????????????????"
 
-#: engines/scumm/scumm.cpp:1408
+#: engines/scumm/scumm.cpp:1411
 #, c-format
 msgid "Could not find resource fork in Macintosh resource file %s"
 msgstr "Macintosh??????????????? %s ???????????????????????????????????????"
 
-#: engines/scumm/scumm.cpp:1415
+#: engines/scumm/scumm.cpp:1418
 #, c-format
 msgid ""
 "'%s' appears to be the wrong Dig executable. It may be the launcher one "
@@ -14245,7 +14255,7 @@ msgid ""
 "its sub-folders should be what you need."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1581
+#: engines/scumm/scumm.cpp:1584
 msgid ""
 "It appears your ScummVM version was not built with TrueType Fonts support.\n"
 "\n"
@@ -14253,7 +14263,7 @@ msgid ""
 "some of the graphics on screen will be missing."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1915
+#: engines/scumm/scumm.cpp:1918
 msgid ""
 "Audio files compressed with ScummVM Tools were detected; *.BUN/*.SOU\n"
 "compression is not supported anymore for this game, audio will be disabled.\n"
@@ -14263,7 +14273,7 @@ msgstr ""
 "??????????????????????????????????????????????????????????????????????????????????????????????????????\n"
 "????????????????????????????????????????????????????????????????????????????????????"
 
-#: engines/scumm/scumm.cpp:2405
+#: engines/scumm/scumm.cpp:2408
 #, c-format
 msgid ""
 "Native MIDI support requires the Roland Upgrade from LucasArts,\n"
@@ -14272,7 +14282,7 @@ msgstr ""
 "???????????????MIDI????????????????????????LucasArts?????????the Roland Upgrade??????????????????\n"
 "?????????%s?????????????????????????????????AdLib?????????????????????"
 
-#: engines/scumm/scumm.cpp:2420
+#: engines/scumm/scumm.cpp:2423
 msgid ""
 "This particular version of Monkey Island 1 is known to miss some\n"
 "required resources for MT-32. Using AdLib instead."
@@ -14280,7 +14290,7 @@ msgstr ""
 "??????Monkey Island 1?????????????????????????????????MT-32????????????\n"
 "???????????????????????????????????????AdLib?????????????????????"
 
-#: engines/scumm/scumm.cpp:4361
+#: engines/scumm/scumm.cpp:4364
 msgid ""
 "Usually, Maniac Mansion would start now. But for that to work, the game "
 "files for Maniac Mansion have to be in the 'Maniac' directory inside the "
@@ -14290,7 +14300,7 @@ msgstr ""
 "???????????????????????????Maniac????????????????????????????????????????????????????????????????????????"
 "ScummVM???????????????????????????????????????????????????"
 
-#: engines/scumm/scumm.cpp:4485
+#: engines/scumm/scumm.cpp:4488
 msgid "Would you like to host or join a network play session?"
 msgstr "???????????????????????????????????????????????????????"
 
diff --git a/po/ka.po b/po/ka.po
index c629cc27a72..f11777ff5d6 100644
--- a/po/ka.po
+++ b/po/ka.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-08 11:04+0000\n"
+"POT-Creation-Date: 2026-06-09 13:22+0000\n"
 "PO-Revision-Date: 2026-06-05 10:22+0000\n"
 "Last-Translator: NorwayFun <temuri.doghonadze at gmail.com>\n"
 "Language-Team: Georgian <https://translations.scummvm.org/projects/scummvm/"
@@ -332,7 +332,7 @@ msgstr "???????????? ???????????? ?????????????????? ?????????????????????. ???
 #: engines/hypno/metaengine.cpp:192 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:197
 #: engines/mm/mm1/metaengine.cpp:54 engines/parallaction/metaengine.cpp:169
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4467
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
 #: engines/sherlock/metaengine.cpp:638 engines/sherlock/metaengine.cpp:1244
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:328
@@ -351,7 +351,7 @@ msgstr "????????????"
 #: engines/hypno/metaengine.cpp:198 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:204
 #: engines/mm/mm1/metaengine.cpp:52 engines/parallaction/metaengine.cpp:175
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4467
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
 #: engines/sherlock/metaengine.cpp:643 engines/sherlock/metaengine.cpp:1250
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:329
@@ -379,7 +379,7 @@ msgstr "JSON ??????????????? ?????????????????????????????? ??????????????????
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:123
 #: engines/drascula/saveload.cpp:51 engines/grim/inputdialog.cpp:83
 #: engines/parallaction/saveload.cpp:270 engines/sci/engine/kgraphics.cpp:76
-#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4479
+#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4482
 #: engines/sword1/control.cpp:2816
 msgid "OK"
 msgstr "????????????"
@@ -1021,7 +1021,7 @@ msgstr "??????????????????????????????"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1100
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1124
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1042,7 +1042,7 @@ msgstr "?????????????????????"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1067
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1091
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1063,7 +1063,7 @@ msgstr "??????????????????"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1080
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1104
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1083,7 +1083,7 @@ msgstr "????????????????????????"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1087
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1111
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3175,7 +3175,7 @@ msgstr "????????????????????????????????????????????? ????????????????????? ???
 #: engines/mohawk/riven_stacks/aspit.cpp:454
 #: engines/neverhood/metaengine.cpp:200 engines/saga/metaengine.cpp:449
 #: engines/scumm/help.cpp:84 engines/scumm/help.cpp:86
-#: engines/scumm/scumm.cpp:4479 engines/sludge/keymapper_tables.h:95
+#: engines/scumm/scumm.cpp:4482 engines/sludge/keymapper_tables.h:95
 #: engines/sludge/keymapper_tables.h:144 engines/sludge/keymapper_tables.h:223
 #: engines/sludge/keymapper_tables.h:343 engines/sludge/keymapper_tables.h:355
 #: engines/sludge/keymapper_tables.h:416 engines/sludge/keymapper_tables.h:429
@@ -3606,7 +3606,7 @@ msgid "Game not implemented"
 msgstr "?????????????????? ????????????????????????????????????????????? ????????????"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
-#: engines/scumm/metaengine.cpp:1050
+#: engines/scumm/metaengine.cpp:1074
 msgid "Default game keymappings"
 msgstr "???????????????????????????????????? ??????????????????????????? ??????????????????????????? ??????????????????"
 
@@ -5463,7 +5463,7 @@ msgstr "R2"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1208 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1232 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "???????????????"
 
@@ -6362,7 +6362,7 @@ msgstr ""
 msgid "Quick load or save game isn't supported in this location"
 msgstr ""
 
-#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3418
+#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3421
 #, c-format
 msgid ""
 "Failed to load saved game from file:\n"
@@ -6370,7 +6370,7 @@ msgid ""
 "%s"
 msgstr ""
 
-#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3405
+#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3408
 #, c-format
 msgid ""
 "Failed to save game to file:\n"
@@ -6381,7 +6381,7 @@ msgstr ""
 "\n"
 "%s"
 
-#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3438
+#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3441
 #, c-format
 msgid ""
 "Successfully saved game in file:\n"
@@ -6830,7 +6830,7 @@ msgstr "????????????????????? ????????????????????????"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1126 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -7477,7 +7477,7 @@ msgstr "????????????????????? / ??????????????????????????????"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1113
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1137
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -9364,7 +9364,7 @@ msgid "Player ammo will never decrease."
 msgstr ""
 
 #: engines/hypno/metaengine.cpp:70 engines/scumm/metaengine.cpp:891
-#: engines/scumm/metaengine.cpp:909
+#: engines/scumm/metaengine.cpp:918
 msgid "Unlock all levels"
 msgstr "??????????????? ??????????????? ???????????????????????????"
 
@@ -11511,7 +11511,7 @@ msgid "Your Name:"
 msgstr "?????????????????? ??????????????????:"
 
 #. I18N: Button, start hosting online multiplayer game
-#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4485
+#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4488
 msgid "Host"
 msgstr "1"
 
@@ -11521,7 +11521,7 @@ msgid "Querying games..."
 msgstr "??????????????????????????? ???????????????????????????..."
 
 #. I18N: Join online multiplayer game
-#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4485
+#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4488
 msgid "Join"
 msgstr "????????????????????????"
 
@@ -12424,114 +12424,124 @@ msgstr "?????????????????? ????????????????????????????????? ??????????????????"
 msgid "Run the game in 640x400 high resolution mode instead of 320x200"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:910
+#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:919
 msgid "All levels will be available without requiring passwords"
 msgstr "??????????????????????????? ??????????????????????????????????????? ?????????????????? ??????????????? ???????????? ????????????????????? ??????????????????????????? ??????????????????"
 
-#: engines/scumm/metaengine.cpp:900
+#: engines/scumm/metaengine.cpp:900 engines/scumm/metaengine.cpp:927
 #, fuzzy
 #| msgid "No date saved"
 msgid "No damage"
 msgstr "?????????????????? ?????? ????????????????????????"
 
-#: engines/scumm/metaengine.cpp:901
+#: engines/scumm/metaengine.cpp:901 engines/scumm/metaengine.cpp:928
 #, fuzzy
 #| msgid "Disable save menu"
 msgid "Disable player damage"
 msgstr "???????????????????????? ?????????????????? ????????????????????????"
 
-#: engines/scumm/metaengine.cpp:1061
+#: engines/scumm/metaengine.cpp:909
+#, fuzzy
+#| msgid "Fast mode"
+msgid "Yoda mode"
+msgstr "?????????????????? ??????????????????"
+
+#: engines/scumm/metaengine.cpp:910
+msgid "Enable original Yoda mode shortcuts, including movie mode and auto play"
+msgstr ""
+
+#: engines/scumm/metaengine.cpp:1085
 msgid "Down left"
 msgstr "??????????????? ????????????????????????"
 
-#: engines/scumm/metaengine.cpp:1074
+#: engines/scumm/metaengine.cpp:1098
 msgid "Down right"
 msgstr "??????????????? ????????????????????????"
 
-#: engines/scumm/metaengine.cpp:1094
+#: engines/scumm/metaengine.cpp:1118
 msgid "Up left"
 msgstr "???????????? ????????????????????????"
 
-#: engines/scumm/metaengine.cpp:1107
+#: engines/scumm/metaengine.cpp:1131
 msgid "Up right"
 msgstr "??????????????? ????????????????????????"
 
-#: engines/scumm/metaengine.cpp:1119
+#: engines/scumm/metaengine.cpp:1143
 msgid "Switch weapon"
 msgstr "????????????????????? ????????????????????????"
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1133
+#: engines/scumm/metaengine.cpp:1157
 msgid "Win the bike fight cheat"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1142
+#: engines/scumm/metaengine.cpp:1166
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1144 engines/scumm/metaengine.cpp:1221
+#: engines/scumm/metaengine.cpp:1168 engines/scumm/metaengine.cpp:1245
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1149 engines/scumm/metaengine.cpp:1226
+#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1250
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1231
+#: engines/scumm/metaengine.cpp:1178 engines/scumm/metaengine.cpp:1255
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
+#: engines/scumm/metaengine.cpp:1183 engines/scumm/metaengine.cpp:1260
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1241
+#: engines/scumm/metaengine.cpp:1188 engines/scumm/metaengine.cpp:1265
 msgid "Stick up"
 msgstr "???????????????"
 
-#: engines/scumm/metaengine.cpp:1171 engines/scumm/metaengine.cpp:1248
+#: engines/scumm/metaengine.cpp:1195 engines/scumm/metaengine.cpp:1272
 msgid "Stick down"
 msgstr "????????????????????????"
 
-#: engines/scumm/metaengine.cpp:1178 engines/scumm/metaengine.cpp:1255
+#: engines/scumm/metaengine.cpp:1202 engines/scumm/metaengine.cpp:1279
 msgid "Stick left"
 msgstr "??????????????????????????? ????????????????????????"
 
-#: engines/scumm/metaengine.cpp:1185 engines/scumm/metaengine.cpp:1262
+#: engines/scumm/metaengine.cpp:1209 engines/scumm/metaengine.cpp:1286
 msgid "Stick right"
 msgstr "??????????????????????????? ????????????????????????"
 
-#: engines/scumm/metaengine.cpp:1192 engines/scumm/metaengine.cpp:1269
+#: engines/scumm/metaengine.cpp:1216 engines/scumm/metaengine.cpp:1293
 msgid "Fire / select"
 msgstr "?????????????????? / ????????????????????????"
 
-#: engines/scumm/metaengine.cpp:1198
+#: engines/scumm/metaengine.cpp:1222
 #, fuzzy
 #| msgid "Skip / menu back"
 msgid "Walk / menu back"
 msgstr "?????????????????????????????? / ??????????????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1203
+#: engines/scumm/metaengine.cpp:1227
 msgid "Skip / menu back"
 msgstr "?????????????????????????????? / ??????????????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1219
+#: engines/scumm/metaengine.cpp:1243
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1274
+#: engines/scumm/metaengine.cpp:1298
 #, fuzzy
 #| msgid "Move back"
 msgid "Cover / back"
 msgstr "???????????? ????????????????????????"
 
-#: engines/scumm/metaengine.cpp:1280
+#: engines/scumm/metaengine.cpp:1304
 #, fuzzy
 #| msgid "Skip walk"
 msgid "Skip / back"
 msgstr "???????????????????????? ??????????????????????????????"
 
-#: engines/scumm/metaengine.cpp:1287
+#: engines/scumm/metaengine.cpp:1311
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu"
@@ -12559,43 +12569,43 @@ msgid ""
 "320x200, under 'engine' tab."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1362
+#: engines/scumm/scumm.cpp:1365
 #, c-format
 msgid "This game requires the '%s' Macintosh executable for its fonts."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1371
+#: engines/scumm/scumm.cpp:1374
 #, c-format
 msgid ""
 "This game requires the '%s' Macintosh executable for its music and fonts."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1375
+#: engines/scumm/scumm.cpp:1378
 #, c-format
 msgid ""
 "Could not find the '%s' Macintosh executable to read resources from. %s will "
 "be disabled."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1376
+#: engines/scumm/scumm.cpp:1379
 msgid "The Mac GUI"
 msgstr "Mac-?????? ??????????????????????????????"
 
-#: engines/scumm/scumm.cpp:1376
+#: engines/scumm/scumm.cpp:1379
 msgid "The music and the Mac GUI"
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1405
+#: engines/scumm/scumm.cpp:1408
 #, c-format
 msgid "Could not open Macintosh resource file %s"
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1408
+#: engines/scumm/scumm.cpp:1411
 #, c-format
 msgid "Could not find resource fork in Macintosh resource file %s"
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1415
+#: engines/scumm/scumm.cpp:1418
 #, c-format
 msgid ""
 "'%s' appears to be the wrong Dig executable. It may be the launcher one "
@@ -12604,7 +12614,7 @@ msgid ""
 "its sub-folders should be what you need."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1581
+#: engines/scumm/scumm.cpp:1584
 msgid ""
 "It appears your ScummVM version was not built with TrueType Fonts support.\n"
 "\n"
@@ -12612,34 +12622,34 @@ msgid ""
 "some of the graphics on screen will be missing."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1915
+#: engines/scumm/scumm.cpp:1918
 msgid ""
 "Audio files compressed with ScummVM Tools were detected; *.BUN/*.SOU\n"
 "compression is not supported anymore for this game, audio will be disabled.\n"
 "Please copy the game from the original media without compression."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:2405
+#: engines/scumm/scumm.cpp:2408
 #, c-format
 msgid ""
 "Native MIDI support requires the Roland Upgrade from LucasArts,\n"
 "but %s is missing. Using AdLib instead."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:2420
+#: engines/scumm/scumm.cpp:2423
 msgid ""
 "This particular version of Monkey Island 1 is known to miss some\n"
 "required resources for MT-32. Using AdLib instead."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:4361
+#: engines/scumm/scumm.cpp:4364
 msgid ""
 "Usually, Maniac Mansion would start now. But for that to work, the game "
 "files for Maniac Mansion have to be in the 'Maniac' directory inside the "
 "Tentacle game directory, and the game has to be added to ScummVM."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:4485
+#: engines/scumm/scumm.cpp:4488
 msgid "Would you like to host or join a network play session?"
 msgstr ""
 
diff --git a/po/ko.po b/po/ko.po
index 38b24fdc461..d24fc890174 100644
--- a/po/ko.po
+++ b/po/ko.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-08 11:04+0000\n"
+"POT-Creation-Date: 2026-06-09 13:22+0000\n"
 "PO-Revision-Date: 2026-05-19 20:22+0000\n"
 "Last-Translator: Hoseok Seo <ddinghoya at gmail.com>\n"
 "Language-Team: Korean <https://translations.scummvm.org/projects/scummvm/"
@@ -339,7 +339,7 @@ msgstr "?????? ???????????? ?????? ?????? ????????????. ????????????????"
 #: engines/hypno/metaengine.cpp:192 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:197
 #: engines/mm/mm1/metaengine.cpp:54 engines/parallaction/metaengine.cpp:169
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4467
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
 #: engines/sherlock/metaengine.cpp:638 engines/sherlock/metaengine.cpp:1244
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:328
@@ -358,7 +358,7 @@ msgstr "???"
 #: engines/hypno/metaengine.cpp:198 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:204
 #: engines/mm/mm1/metaengine.cpp:52 engines/parallaction/metaengine.cpp:175
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4467
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
 #: engines/sherlock/metaengine.cpp:643 engines/sherlock/metaengine.cpp:1250
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:329
@@ -386,7 +386,7 @@ msgstr "JSON ?????? ????????? ?????????????????????."
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:123
 #: engines/drascula/saveload.cpp:51 engines/grim/inputdialog.cpp:83
 #: engines/parallaction/saveload.cpp:270 engines/sci/engine/kgraphics.cpp:76
-#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4479
+#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4482
 #: engines/sword1/control.cpp:2816
 msgid "OK"
 msgstr "??????"
@@ -1044,7 +1044,7 @@ msgstr "??????"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1100
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1124
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1065,7 +1065,7 @@ msgstr "??????"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1067
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1091
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1086,7 +1086,7 @@ msgstr "?????????"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1080
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1104
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1106,7 +1106,7 @@ msgstr "??????"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1087
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1111
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3352,7 +3352,7 @@ msgstr "????????? ?????? ?????? ??????"
 #: engines/mohawk/riven_stacks/aspit.cpp:454
 #: engines/neverhood/metaengine.cpp:200 engines/saga/metaengine.cpp:449
 #: engines/scumm/help.cpp:84 engines/scumm/help.cpp:86
-#: engines/scumm/scumm.cpp:4479 engines/sludge/keymapper_tables.h:95
+#: engines/scumm/scumm.cpp:4482 engines/sludge/keymapper_tables.h:95
 #: engines/sludge/keymapper_tables.h:144 engines/sludge/keymapper_tables.h:223
 #: engines/sludge/keymapper_tables.h:343 engines/sludge/keymapper_tables.h:355
 #: engines/sludge/keymapper_tables.h:416 engines/sludge/keymapper_tables.h:429
@@ -3835,7 +3835,7 @@ msgid "Game not implemented"
 msgstr "????????? ???????????? ?????????"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
-#: engines/scumm/metaengine.cpp:1050
+#: engines/scumm/metaengine.cpp:1074
 msgid "Default game keymappings"
 msgstr "?????? ?????? ?????????"
 
@@ -6013,7 +6013,7 @@ msgstr "R2 ??????"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1208 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1232 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "??????"
 
@@ -6948,7 +6948,7 @@ msgstr ""
 msgid "Quick load or save game isn't supported in this location"
 msgstr "??? ??????????????? ?????? ?????? ???????????? ?????? ????????? ???????????? ??????"
 
-#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3418
+#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3421
 #, c-format
 msgid ""
 "Failed to load saved game from file:\n"
@@ -6959,7 +6959,7 @@ msgstr ""
 "\n"
 "%s"
 
-#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3405
+#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3408
 #, c-format
 msgid ""
 "Failed to save game to file:\n"
@@ -6970,7 +6970,7 @@ msgstr ""
 "\n"
 "%s"
 
-#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3438
+#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3441
 #, c-format
 msgid ""
 "Successfully saved game in file:\n"
@@ -7444,7 +7444,7 @@ msgstr "?????? ??????"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1126 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -8106,7 +8106,7 @@ msgstr "?????? / ??????"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1113
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1137
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -10056,7 +10056,7 @@ msgid "Player ammo will never decrease."
 msgstr "??????????????? ????????? ???????????? ????????????."
 
 #: engines/hypno/metaengine.cpp:70 engines/scumm/metaengine.cpp:891
-#: engines/scumm/metaengine.cpp:909
+#: engines/scumm/metaengine.cpp:918
 msgid "Unlock all levels"
 msgstr "?????? ?????? ?????? ??????"
 
@@ -12378,7 +12378,7 @@ msgid "Your Name:"
 msgstr "????????? ??????:"
 
 #. I18N: Button, start hosting online multiplayer game
-#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4485
+#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4488
 msgid "Host"
 msgstr "?????????"
 
@@ -12388,7 +12388,7 @@ msgid "Querying games..."
 msgstr "?????? ?????? ???..."
 
 #. I18N: Join online multiplayer game
-#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4485
+#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4488
 msgid "Join"
 msgstr "??????"
 
@@ -13340,128 +13340,138 @@ msgstr "???????????? ???"
 msgid "Run the game in 640x400 high resolution mode instead of 320x200"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:910
+#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:919
 #, fuzzy
 #| msgid "All levels will be available to play."
 msgid "All levels will be available without requiring passwords"
 msgstr "?????? ???????????? ???????????? ??? ????????????."
 
-#: engines/scumm/metaengine.cpp:900
+#: engines/scumm/metaengine.cpp:900 engines/scumm/metaengine.cpp:927
 #, fuzzy
 #| msgid "No date saved"
 msgid "No damage"
 msgstr "????????? ???????????? ?????????"
 
-#: engines/scumm/metaengine.cpp:901
+#: engines/scumm/metaengine.cpp:901 engines/scumm/metaengine.cpp:928
 #, fuzzy
 #| msgid "Disable save menu"
 msgid "Disable player damage"
 msgstr "?????? ?????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1061
+#: engines/scumm/metaengine.cpp:909
+#, fuzzy
+#| msgid "Fast mode"
+msgid "Yoda mode"
+msgstr "?????? ??????"
+
+#: engines/scumm/metaengine.cpp:910
+msgid "Enable original Yoda mode shortcuts, including movie mode and auto play"
+msgstr ""
+
+#: engines/scumm/metaengine.cpp:1085
 msgid "Down left"
 msgstr "?????? ??????"
 
-#: engines/scumm/metaengine.cpp:1074
+#: engines/scumm/metaengine.cpp:1098
 msgid "Down right"
 msgstr "????????? ??????"
 
-#: engines/scumm/metaengine.cpp:1094
+#: engines/scumm/metaengine.cpp:1118
 msgid "Up left"
 msgstr "?????? ??????"
 
-#: engines/scumm/metaengine.cpp:1107
+#: engines/scumm/metaengine.cpp:1131
 msgid "Up right"
 msgstr "????????? ??????"
 
-#: engines/scumm/metaengine.cpp:1119
+#: engines/scumm/metaengine.cpp:1143
 msgid "Switch weapon"
 msgstr "?????? ??????"
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1133
+#: engines/scumm/metaengine.cpp:1157
 msgid "Win the bike fight cheat"
 msgstr "????????? ???????????? ???????????? ??????"
 
-#: engines/scumm/metaengine.cpp:1142
+#: engines/scumm/metaengine.cpp:1166
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1144 engines/scumm/metaengine.cpp:1221
+#: engines/scumm/metaengine.cpp:1168 engines/scumm/metaengine.cpp:1245
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1149 engines/scumm/metaengine.cpp:1226
+#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1250
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1231
+#: engines/scumm/metaengine.cpp:1178 engines/scumm/metaengine.cpp:1255
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
+#: engines/scumm/metaengine.cpp:1183 engines/scumm/metaengine.cpp:1260
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1241
+#: engines/scumm/metaengine.cpp:1188 engines/scumm/metaengine.cpp:1265
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "??????"
 
-#: engines/scumm/metaengine.cpp:1171 engines/scumm/metaengine.cpp:1248
+#: engines/scumm/metaengine.cpp:1195 engines/scumm/metaengine.cpp:1272
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "????????? ?????????"
 
-#: engines/scumm/metaengine.cpp:1178 engines/scumm/metaengine.cpp:1255
+#: engines/scumm/metaengine.cpp:1202 engines/scumm/metaengine.cpp:1279
 #, fuzzy
 #| msgid "Slide left"
 msgid "Stick left"
 msgstr "???????????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1185 engines/scumm/metaengine.cpp:1262
+#: engines/scumm/metaengine.cpp:1209 engines/scumm/metaengine.cpp:1286
 #, fuzzy
 #| msgid "Slide right"
 msgid "Stick right"
 msgstr "????????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1192 engines/scumm/metaengine.cpp:1269
+#: engines/scumm/metaengine.cpp:1216 engines/scumm/metaengine.cpp:1293
 #, fuzzy
 #| msgid "Move / Select"
 msgid "Fire / select"
 msgstr "?????? / ??????"
 
-#: engines/scumm/metaengine.cpp:1198
+#: engines/scumm/metaengine.cpp:1222
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Walk / menu back"
 msgstr "?????? ?????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1203
+#: engines/scumm/metaengine.cpp:1227
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu back"
 msgstr "?????? ?????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1219
+#: engines/scumm/metaengine.cpp:1243
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1274
+#: engines/scumm/metaengine.cpp:1298
 #, fuzzy
 #| msgid "Move back"
 msgid "Cover / back"
 msgstr "?????? ??????"
 
-#: engines/scumm/metaengine.cpp:1280
+#: engines/scumm/metaengine.cpp:1304
 #, fuzzy
 #| msgid "Skip walk"
 msgid "Skip / back"
 msgstr "?????? ????????????"
 
-#: engines/scumm/metaengine.cpp:1287
+#: engines/scumm/metaengine.cpp:1311
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu"
@@ -13500,19 +13510,19 @@ msgstr ""
 "320x240?????? ????????? ????????? ???????????? ????????????.\n"
 "????????? ????????? '??????' ????????? ???????????? 320x200?????? ???????????? ????????? ??? ????????????."
 
-#: engines/scumm/scumm.cpp:1362
+#: engines/scumm/scumm.cpp:1365
 #, c-format
 msgid "This game requires the '%s' Macintosh executable for its fonts."
 msgstr "??? ????????? ????????? ??????????????? '%s' ???????????? ?????? ????????? ???????????????."
 
-#: engines/scumm/scumm.cpp:1371
+#: engines/scumm/scumm.cpp:1374
 #, c-format
 msgid ""
 "This game requires the '%s' Macintosh executable for its music and fonts."
 msgstr ""
 "??? ????????? ????????? ????????? ??????????????? '%s' ???????????? ?????? ????????? ???????????????."
 
-#: engines/scumm/scumm.cpp:1375
+#: engines/scumm/scumm.cpp:1378
 #, c-format
 msgid ""
 "Could not find the '%s' Macintosh executable to read resources from. %s will "
@@ -13521,25 +13531,25 @@ msgstr ""
 "???????????? ?????? ?????? ???????????? ?????? ?????? '%s'??? ?????? ??? ????????????. %s??? ????????????"
 "?????????."
 
-#: engines/scumm/scumm.cpp:1376
+#: engines/scumm/scumm.cpp:1379
 msgid "The Mac GUI"
 msgstr "??? GUI"
 
-#: engines/scumm/scumm.cpp:1376
+#: engines/scumm/scumm.cpp:1379
 msgid "The music and the Mac GUI"
 msgstr "????????? ??? GUI"
 
-#: engines/scumm/scumm.cpp:1405
+#: engines/scumm/scumm.cpp:1408
 #, c-format
 msgid "Could not open Macintosh resource file %s"
 msgstr "???????????? ????????? ?????? %s???(???) ??? ??? ??????"
 
-#: engines/scumm/scumm.cpp:1408
+#: engines/scumm/scumm.cpp:1411
 #, c-format
 msgid "Could not find resource fork in Macintosh resource file %s"
 msgstr "???????????? ????????? ?????? %s?????? ????????? ????????? ?????? ??? ??????"
 
-#: engines/scumm/scumm.cpp:1415
+#: engines/scumm/scumm.cpp:1418
 #, c-format
 msgid ""
 "'%s' appears to be the wrong Dig executable. It may be the launcher one "
@@ -13551,7 +13561,7 @@ msgstr ""
 "????????? ????????? ????????? ?????? ?????? ????????? ????????? ????????????. CD?????? 'The Dig f' ???"
 "?????? ????????????. ?????? ?????? ??? ????????? ????????? ?????? ?????? ????????????."
 
-#: engines/scumm/scumm.cpp:1581
+#: engines/scumm/scumm.cpp:1584
 msgid ""
 "It appears your ScummVM version was not built with TrueType Fonts support.\n"
 "\n"
@@ -13564,7 +13574,7 @@ msgstr ""
 "One-Stop Fun Shop ???????????? TTF ????????? ?????? ???????????? ?????????,\n"
 "????????? ?????? ???????????? ???????????? ?????? ??? ????????????."
 
-#: engines/scumm/scumm.cpp:1915
+#: engines/scumm/scumm.cpp:1918
 msgid ""
 "Audio files compressed with ScummVM Tools were detected; *.BUN/*.SOU\n"
 "compression is not supported anymore for this game, audio will be disabled.\n"
@@ -13574,7 +13584,7 @@ msgstr ""
 "????????? ??? ???????????? ??? ?????? ???????????? ???????????? ???????????? ?????????????????????.\n"
 "????????? ???????????? ?????? ?????? ??????????????? ???????????????."
 
-#: engines/scumm/scumm.cpp:2405
+#: engines/scumm/scumm.cpp:2408
 #, c-format
 msgid ""
 "Native MIDI support requires the Roland Upgrade from LucasArts,\n"
@@ -13583,7 +13593,7 @@ msgstr ""
 "?????? ?????? ???????????? ?????????????????? ????????? ?????????????????? ???????????????,\n"
 "%s??? ????????????. ?????? ???????????? ???????????????."
 
-#: engines/scumm/scumm.cpp:2420
+#: engines/scumm/scumm.cpp:2423
 msgid ""
 "This particular version of Monkey Island 1 is known to miss some\n"
 "required resources for MT-32. Using AdLib instead."
@@ -13591,7 +13601,7 @@ msgstr ""
 "??? ?????? ????????? ????????? ??? 1??? MT-32??? ????????? ???????????? ??????\n"
 "?????? ????????? ????????? ????????????. ?????? ???????????? ???????????????."
 
-#: engines/scumm/scumm.cpp:4361
+#: engines/scumm/scumm.cpp:4364
 msgid ""
 "Usually, Maniac Mansion would start now. But for that to work, the game "
 "files for Maniac Mansion have to be in the 'Maniac' directory inside the "
@@ -13601,7 +13611,7 @@ msgstr ""
 "?????? ????????? Tentacle ?????? ???????????? ?????? 'Maniac' ??????????????? ????????? ??????, ??????"
 "??? ScummVM??? ???????????? ?????????."
 
-#: engines/scumm/scumm.cpp:4485
+#: engines/scumm/scumm.cpp:4488
 msgid "Would you like to host or join a network play session?"
 msgstr "???????????? ????????? ????????? ?????????????????? ???????????? ?????????????"
 
diff --git a/po/nb_NO.po b/po/nb_NO.po
index 8b45896f7ec..9ef1b065548 100644
--- a/po/nb_NO.po
+++ b/po/nb_NO.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-08 11:04+0000\n"
+"POT-Creation-Date: 2026-06-09 13:22+0000\n"
 "PO-Revision-Date: 2026-02-07 15:22+0000\n"
 "Last-Translator: Einar Johan Tr??an S??m??en <einarjohants at gmail.com>\n"
 "Language-Team: Norwegian Bokm??l <https://translations.scummvm.org/projects/"
@@ -341,7 +341,7 @@ msgstr "Et annet fillager er aktivt. ??nsker du ?? avbryte det?"
 #: engines/hypno/metaengine.cpp:192 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:197
 #: engines/mm/mm1/metaengine.cpp:54 engines/parallaction/metaengine.cpp:169
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4467
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
 #: engines/sherlock/metaengine.cpp:638 engines/sherlock/metaengine.cpp:1244
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:328
@@ -360,7 +360,7 @@ msgstr "Ja"
 #: engines/hypno/metaengine.cpp:198 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:204
 #: engines/mm/mm1/metaengine.cpp:52 engines/parallaction/metaengine.cpp:175
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4467
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
 #: engines/sherlock/metaengine.cpp:643 engines/sherlock/metaengine.cpp:1250
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:329
@@ -388,7 +388,7 @@ msgstr "Innholdet i JSON-koden er feilutformet."
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:123
 #: engines/drascula/saveload.cpp:51 engines/grim/inputdialog.cpp:83
 #: engines/parallaction/saveload.cpp:270 engines/sci/engine/kgraphics.cpp:76
-#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4479
+#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4482
 #: engines/sword1/control.cpp:2816
 msgid "OK"
 msgstr "OK"
@@ -1052,7 +1052,7 @@ msgstr "Samhandle"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1100
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1124
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1073,7 +1073,7 @@ msgstr "Opp"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1067
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1091
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1094,7 +1094,7 @@ msgstr "Ned"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1080
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1104
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1114,7 +1114,7 @@ msgstr "Venstre"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1087
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1111
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3461,7 +3461,7 @@ msgstr "Spillmotor for grafikkbaserte eventyr"
 #: engines/mohawk/riven_stacks/aspit.cpp:454
 #: engines/neverhood/metaengine.cpp:200 engines/saga/metaengine.cpp:449
 #: engines/scumm/help.cpp:84 engines/scumm/help.cpp:86
-#: engines/scumm/scumm.cpp:4479 engines/sludge/keymapper_tables.h:95
+#: engines/scumm/scumm.cpp:4482 engines/sludge/keymapper_tables.h:95
 #: engines/sludge/keymapper_tables.h:144 engines/sludge/keymapper_tables.h:223
 #: engines/sludge/keymapper_tables.h:343 engines/sludge/keymapper_tables.h:355
 #: engines/sludge/keymapper_tables.h:416 engines/sludge/keymapper_tables.h:429
@@ -3963,7 +3963,7 @@ msgid "Game not implemented"
 msgstr "Spillet er ikke implementert"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
-#: engines/scumm/metaengine.cpp:1050
+#: engines/scumm/metaengine.cpp:1074
 #, fuzzy
 #| msgid "Default game keymap"
 msgid "Default game keymappings"
@@ -6412,7 +6412,7 @@ msgstr "R2"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1208 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1232 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "Meny"
 
@@ -7370,7 +7370,7 @@ msgstr ""
 msgid "Quick load or save game isn't supported in this location"
 msgstr "Hurtiglasting eller -lagring av spill st??ttes ikke her"
 
-#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3418
+#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3421
 #, c-format
 msgid ""
 "Failed to load saved game from file:\n"
@@ -7381,7 +7381,7 @@ msgstr ""
 "\n"
 "%s"
 
-#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3405
+#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3408
 #, c-format
 msgid ""
 "Failed to save game to file:\n"
@@ -7392,7 +7392,7 @@ msgstr ""
 "\n"
 "%s"
 
-#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3438
+#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3441
 #, c-format
 msgid ""
 "Successfully saved game in file:\n"
@@ -7910,7 +7910,7 @@ msgstr "Skru kampmodus av/p??"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1126 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -8678,7 +8678,7 @@ msgstr "Konverser/Interager"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1113
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1137
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -10930,7 +10930,7 @@ msgid "Player ammo will never decrease."
 msgstr "Spillerens ammunisjon minsker aldri."
 
 #: engines/hypno/metaengine.cpp:70 engines/scumm/metaengine.cpp:891
-#: engines/scumm/metaengine.cpp:909
+#: engines/scumm/metaengine.cpp:918
 msgid "Unlock all levels"
 msgstr "L??ser opp alle niv??er"
 
@@ -13523,7 +13523,7 @@ msgid "Your Name:"
 msgstr "Ditt navn:"
 
 #. I18N: Button, start hosting online multiplayer game
-#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4485
+#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4488
 msgid "Host"
 msgstr "Vert"
 
@@ -13533,7 +13533,7 @@ msgid "Querying games..."
 msgstr "Sjekker spill ..."
 
 #. I18N: Join online multiplayer game
-#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4485
+#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4488
 msgid "Join"
 msgstr "Bli med"
 
@@ -14502,136 +14502,146 @@ msgstr "H??y oppl??sning p??"
 msgid "Run the game in 640x400 high resolution mode instead of 320x200"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:910
+#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:919
 #, fuzzy
 #| msgid "All levels will be available to play."
 msgid "All levels will be available without requiring passwords"
 msgstr "Alle niv??er ??pnes for spilling."
 
-#: engines/scumm/metaengine.cpp:900
+#: engines/scumm/metaengine.cpp:900 engines/scumm/metaengine.cpp:927
 #, fuzzy
 #| msgid "No date saved"
 msgid "No damage"
 msgstr "Ingen dato lagret"
 
-#: engines/scumm/metaengine.cpp:901
+#: engines/scumm/metaengine.cpp:901 engines/scumm/metaengine.cpp:928
 #, fuzzy
 #| msgid "Disable save menu"
 msgid "Disable player damage"
 msgstr "Skru av lagringsmeny"
 
-#: engines/scumm/metaengine.cpp:1061
+#: engines/scumm/metaengine.cpp:909
+#, fuzzy
+#| msgid "Fast mode"
+msgid "Yoda mode"
+msgstr "Hurtigmodus"
+
+#: engines/scumm/metaengine.cpp:910
+msgid "Enable original Yoda mode shortcuts, including movie mode and auto play"
+msgstr ""
+
+#: engines/scumm/metaengine.cpp:1085
 #, fuzzy
 #| msgid "Down Left"
 msgid "Down left"
 msgstr "Ned til venstre"
 
-#: engines/scumm/metaengine.cpp:1074
+#: engines/scumm/metaengine.cpp:1098
 #, fuzzy
 #| msgid "Down Right"
 msgid "Down right"
 msgstr "Ned til h??yre"
 
-#: engines/scumm/metaengine.cpp:1094
+#: engines/scumm/metaengine.cpp:1118
 #, fuzzy
 #| msgid "Upper left item"
 msgid "Up left"
 msgstr "??vre venstre gjenstand"
 
-#: engines/scumm/metaengine.cpp:1107
+#: engines/scumm/metaengine.cpp:1131
 #, fuzzy
 #| msgid "Up Right"
 msgid "Up right"
 msgstr "Opp til h??yre"
 
-#: engines/scumm/metaengine.cpp:1119
+#: engines/scumm/metaengine.cpp:1143
 msgid "Switch weapon"
 msgstr "Bytte v??pen"
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1133
+#: engines/scumm/metaengine.cpp:1157
 msgid "Win the bike fight cheat"
 msgstr "Juksefunksjon for ?? vinne motorsykkelkamper"
 
-#: engines/scumm/metaengine.cpp:1142
+#: engines/scumm/metaengine.cpp:1166
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1144 engines/scumm/metaengine.cpp:1221
+#: engines/scumm/metaengine.cpp:1168 engines/scumm/metaengine.cpp:1245
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1149 engines/scumm/metaengine.cpp:1226
+#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1250
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1231
+#: engines/scumm/metaengine.cpp:1178 engines/scumm/metaengine.cpp:1255
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
+#: engines/scumm/metaengine.cpp:1183 engines/scumm/metaengine.cpp:1260
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1241
+#: engines/scumm/metaengine.cpp:1188 engines/scumm/metaengine.cpp:1265
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "Plukk opp"
 
-#: engines/scumm/metaengine.cpp:1171 engines/scumm/metaengine.cpp:1248
+#: engines/scumm/metaengine.cpp:1195 engines/scumm/metaengine.cpp:1272
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "Bla ned"
 
-#: engines/scumm/metaengine.cpp:1178 engines/scumm/metaengine.cpp:1255
+#: engines/scumm/metaengine.cpp:1202 engines/scumm/metaengine.cpp:1279
 #, fuzzy
 #| msgid "Slide Left"
 msgid "Stick left"
 msgstr "Skli mot Venstre"
 
-#: engines/scumm/metaengine.cpp:1185 engines/scumm/metaengine.cpp:1262
+#: engines/scumm/metaengine.cpp:1209 engines/scumm/metaengine.cpp:1286
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Stick right"
 msgstr "Skli mot H??yre"
 
-#: engines/scumm/metaengine.cpp:1192 engines/scumm/metaengine.cpp:1269
+#: engines/scumm/metaengine.cpp:1216 engines/scumm/metaengine.cpp:1293
 #, fuzzy
 #| msgid "Move left"
 msgid "Fire / select"
 msgstr "G?? til venstre"
 
-#: engines/scumm/metaengine.cpp:1198
+#: engines/scumm/metaengine.cpp:1222
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Walk / menu back"
 msgstr "Hopp over hovedmeny"
 
-#: engines/scumm/metaengine.cpp:1203
+#: engines/scumm/metaengine.cpp:1227
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu back"
 msgstr "Hopp over hovedmeny"
 
-#: engines/scumm/metaengine.cpp:1219
+#: engines/scumm/metaengine.cpp:1243
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1274
+#: engines/scumm/metaengine.cpp:1298
 #, fuzzy
 #| msgid "Move Back"
 msgid "Cover / back"
 msgstr "Beveg Bakover"
 
-#: engines/scumm/metaengine.cpp:1280
+#: engines/scumm/metaengine.cpp:1304
 #, fuzzy
 #| msgid "Skip dialogue"
 msgid "Skip / back"
 msgstr "Hopp over dialog"
 
-#: engines/scumm/metaengine.cpp:1287
+#: engines/scumm/metaengine.cpp:1311
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu"
@@ -14663,14 +14673,14 @@ msgstr ""
 "Du kan korrigere bildeformatet ved ?? kutte skjermbildet til en oppl??sning p?? "
 "320x200 via 'Spillmotor'-fanen."
 
-#: engines/scumm/scumm.cpp:1362
+#: engines/scumm/scumm.cpp:1365
 #, fuzzy, c-format
 #| msgid "This game requires the 'Indy' Macintosh executable for its fonts."
 msgid "This game requires the '%s' Macintosh executable for its fonts."
 msgstr ""
 "Dette spillet fordrer exe-filen til 'Indy' Macintosh for ?? vise rett fonter."
 
-#: engines/scumm/scumm.cpp:1371
+#: engines/scumm/scumm.cpp:1374
 #, fuzzy, c-format
 #| msgid ""
 #| "This game requires the 'Loom' Macintosh executable for its music and "
@@ -14680,7 +14690,7 @@ msgid ""
 msgstr ""
 "Dette spillet krever exe-filen til 'Loom' Macintosh for ?? vise rett fonter."
 
-#: engines/scumm/scumm.cpp:1375
+#: engines/scumm/scumm.cpp:1378
 #, fuzzy, c-format
 #| msgid ""
 #| "Could not find the 'Monkey Island' Macintosh executable to read the\n"
@@ -14692,25 +14702,25 @@ msgstr ""
 "Kunne ikke finne Macintosh-bin??rfila ??Monkey Island?? for ?? lese\n"
 "instrumenter fra den. Musikk vil bli deaktivert."
 
-#: engines/scumm/scumm.cpp:1376
+#: engines/scumm/scumm.cpp:1379
 msgid "The Mac GUI"
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1376
+#: engines/scumm/scumm.cpp:1379
 msgid "The music and the Mac GUI"
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1405
+#: engines/scumm/scumm.cpp:1408
 #, c-format
 msgid "Could not open Macintosh resource file %s"
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1408
+#: engines/scumm/scumm.cpp:1411
 #, c-format
 msgid "Could not find resource fork in Macintosh resource file %s"
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1415
+#: engines/scumm/scumm.cpp:1418
 #, c-format
 msgid ""
 "'%s' appears to be the wrong Dig executable. It may be the launcher one "
@@ -14719,7 +14729,7 @@ msgid ""
 "its sub-folders should be what you need."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1581
+#: engines/scumm/scumm.cpp:1584
 msgid ""
 "It appears your ScummVM version was not built with TrueType Fonts support.\n"
 "\n"
@@ -14727,7 +14737,7 @@ msgid ""
 "some of the graphics on screen will be missing."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:1915
+#: engines/scumm/scumm.cpp:1918
 msgid ""
 "Audio files compressed with ScummVM Tools were detected; *.BUN/*.SOU\n"
 "compression is not supported anymore for this game, audio will be disabled.\n"
@@ -14739,7 +14749,7 @@ msgstr ""
 "Kopier spillet fra sitt opprinnelige lagringsmedium uten bruk av "
 "komprimering."
 
-#: engines/scumm/scumm.cpp:2405
+#: engines/scumm/scumm.cpp:2408
 #, c-format
 msgid ""
 "Native MIDI support requires the Roland Upgrade from LucasArts,\n"
@@ -14748,7 +14758,7 @@ msgstr ""
 "Ekte MIDI-st??tte krever ??Roland Upgrade?? fra LucasArts,\n"
 "men %s mangler. Bruker AdLib istedet."
 
-#: engines/scumm/scumm.cpp:2420
+#: engines/scumm/scumm.cpp:2423
 msgid ""
 "This particular version of Monkey Island 1 is known to miss some\n"
 "required resources for MT-32. Using AdLib instead."
@@ -14756,7 +14766,7 @@ msgstr ""
 "Denne spesifikke utgaven av Monkey Island 1 mangler noen av\n"
 "de p??krevde ressursene for ?? st??tte MT-32. Bruker AdLib i stedet."
 
-#: engines/scumm/scumm.cpp:4361
+#: engines/scumm/scumm.cpp:4364
 msgid ""
 "Usually, Maniac Mansion would start now. But for that to work, the game "
 "files for Maniac Mansion have to be in the 'Maniac' directory inside the "
@@ -14766,7 +14776,7 @@ msgstr ""
 "m?? Maniac Mansion-filene ligge i ??Maniac?? mappa inni Tentacle-spillmappa, og "
 "spillet m?? v??re lagt til i ScummVM."
 
-#: engines/scumm/scumm.cpp:4485
+#: engines/scumm/scumm.cpp:4488
 msgid "Would you like to host or join a network play session?"
 msgstr "??nsker du ?? v??re vert for eller bli med i en nettverksbasert spill??kt?"
 
diff --git a/po/nl_NL.po b/po/nl_NL.po
index 938ae747187..7096b3f750b 100644
--- a/po/nl_NL.po
+++ b/po/nl_NL.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.9.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-08 11:04+0000\n"
+"POT-Creation-Date: 2026-06-09 13:22+0000\n"
 "PO-Revision-Date: 2026-02-11 14:23+0000\n"
 "Last-Translator: SecularSteve <fairfull.playing at gmail.com>\n"
 "Language-Team: Dutch <https://translations.scummvm.org/projects/scummvm/"
@@ -342,7 +342,7 @@ msgstr "Een andere opslag is actief. Wilt u die onderbreken?"
 #: engines/hypno/metaengine.cpp:192 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:197
 #: engines/mm/mm1/metaengine.cpp:54 engines/parallaction/metaengine.cpp:169
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4467
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
 #: engines/sherlock/metaengine.cpp:638 engines/sherlock/metaengine.cpp:1244
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:328
@@ -361,7 +361,7 @@ msgstr "Ja"
 #: engines/hypno/metaengine.cpp:198 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:204
 #: engines/mm/mm1/metaengine.cpp:52 engines/parallaction/metaengine.cpp:175
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4467
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
 #: engines/sherlock/metaengine.cpp:643 engines/sherlock/metaengine.cpp:1250
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:329
@@ -389,7 +389,7 @@ msgstr "JSON code heeft niet het juiste formaat."
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:123
 #: engines/drascula/saveload.cpp:51 engines/grim/inputdialog.cpp:83
 #: engines/parallaction/saveload.cpp:270 engines/sci/engine/kgraphics.cpp:76
-#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4479
+#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4482
 #: engines/sword1/control.cpp:2816
 msgid "OK"
 msgstr "OK"
@@ -1053,7 +1053,7 @@ msgstr "Interactie"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1100
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1124
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1074,7 +1074,7 @@ msgstr "Omhoog"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1067
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1091
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1095,7 +1095,7 @@ msgstr "Omlaag"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1080
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1104
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1115,7 +1115,7 @@ msgstr "Links"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1087
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1111
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3393,7 +3393,7 @@ msgstr "Grafische avonturen spel engine"
 #: engines/mohawk/riven_stacks/aspit.cpp:454
 #: engines/neverhood/metaengine.cpp:200 engines/saga/metaengine.cpp:449
 #: engines/scumm/help.cpp:84 engines/scumm/help.cpp:86
-#: engines/scumm/scumm.cpp:4479 engines/sludge/keymapper_tables.h:95
+#: engines/scumm/scumm.cpp:4482 engines/sludge/keymapper_tables.h:95
 #: engines/sludge/keymapper_tables.h:144 engines/sludge/keymapper_tables.h:223
 #: engines/sludge/keymapper_tables.h:343 engines/sludge/keymapper_tables.h:355
 #: engines/sludge/keymapper_tables.h:416 engines/sludge/keymapper_tables.h:429
@@ -3896,7 +3896,7 @@ msgid "Game not implemented"
 msgstr "Spel niet ge??mplementeerd"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
-#: engines/scumm/metaengine.cpp:1050
+#: engines/scumm/metaengine.cpp:1074
 msgid "Default game keymappings"
 msgstr "Standaard speltoetsenkoppelingen"
 
@@ -6087,7 +6087,7 @@ msgstr "R2"
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1208 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1232 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "Menu"
 
@@ -7037,7 +7037,7 @@ msgstr ""
 msgid "Quick load or save game isn't supported in this location"
 msgstr "Snel laden of bewaren wordt niet ondersteund op deze locatie"
 
-#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3418
+#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3421
 #, c-format
 msgid ""
 "Failed to load saved game from file:\n"
@@ -7048,7 +7048,7 @@ msgstr ""
 "\n"
 "%s"
 
-#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3405
+#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3408
 #, c-format
 msgid ""
 "Failed to save game to file:\n"
@@ -7059,7 +7059,7 @@ msgstr ""
 "\n"
 "%s"
 
-#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3438
+#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3441
 #, c-format
 msgid ""
 "Successfully saved game in file:\n"
@@ -7552,7 +7552,7 @@ msgstr "Schakel gevecht in/uit"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1126 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -8251,7 +8251,7 @@ msgstr "Praten / Interactie"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1113
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1137
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -10317,7 +10317,7 @@ msgid "Player ammo will never decrease."
 msgstr "Munitie van speler zal niet verminderen."
 
 #: engines/hypno/metaengine.cpp:70 engines/scumm/metaengine.cpp:891
-#: engines/scumm/metaengine.cpp:909
+#: engines/scumm/metaengine.cpp:918
 msgid "Unlock all levels"
 msgstr "Geef alle levels vrij"
 
@@ -12717,7 +12717,7 @@ msgid "Your Name:"
 msgstr "Uw naam:"
 
 #. I18N: Button, start hosting online multiplayer game
-#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4485
+#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4488
 msgid "Host"
 msgstr "Host"
 
@@ -12727,7 +12727,7 @@ msgid "Querying games..."
 msgstr "Opvragen spellen..."
 
 #. I18N: Join online multiplayer game
-#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4485
+#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4488
 msgid "Join"
 msgstr "Meedoen"
 
@@ -13703,128 +13703,138 @@ msgstr "Hoge resolutie aan"
 msgid "Run the game in 640x400 high resolution mode instead of 320x200"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:910
+#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:919
 #, fuzzy
 #| msgid "All levels will be available to play."
 msgid "All levels will be available without requiring passwords"
 msgstr "Alle levels kunnen dan gespeeld worden."
 
-#: engines/scumm/metaengine.cpp:900
+#: engines/scumm/metaengine.cpp:900 engines/scumm/metaengine.cpp:927
 #, fuzzy
 #| msgid "No date saved"
 msgid "No damage"
 msgstr "Geen datum opgeslagen"
 
-#: engines/scumm/metaengine.cpp:901
+#: engines/scumm/metaengine.cpp:901 engines/scumm/metaengine.cpp:928
 #, fuzzy
 #| msgid "Disable save menu"
 msgid "Disable player damage"
 msgstr "Bewaar menu uitschakelen"
 
-#: engines/scumm/metaengine.cpp:1061
+#: engines/scumm/metaengine.cpp:909
+#, fuzzy
+#| msgid "Fast mode"
+msgid "Yoda mode"
+msgstr "Snelle modus"
+
+#: engines/scumm/metaengine.cpp:910
+msgid "Enable original Yoda mode shortcuts, including movie mode and auto play"
+msgstr ""
+
+#: engines/scumm/metaengine.cpp:1085
 msgid "Down left"
 msgstr "Linksonder"
 
-#: engines/scumm/metaengine.cpp:1074
+#: engines/scumm/metaengine.cpp:1098
 msgid "Down right"
 msgstr "Rechtsonder"
 
-#: engines/scumm/metaengine.cpp:1094
+#: engines/scumm/metaengine.cpp:1118
 msgid "Up left"
 msgstr "Linksboven"
 
-#: engines/scumm/metaengine.cpp:1107
+#: engines/scumm/metaengine.cpp:1131
 msgid "Up right"
 msgstr "Rechtop"
 
-#: engines/scumm/metaengine.cpp:1119
+#: engines/scumm/metaengine.cpp:1143
 msgid "Switch weapon"
 msgstr "Wissel wapen"
 
 #. I18N: Lets one skip the bike/car fight sequences in Full Throttle
-#: engines/scumm/metaengine.cpp:1133
+#: engines/scumm/metaengine.cpp:1157
 msgid "Win the bike fight cheat"
 msgstr "Win de fietsstrijd-cheat"
 
-#: engines/scumm/metaengine.cpp:1142
+#: engines/scumm/metaengine.cpp:1166
 msgid "Rebel Assault controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1144 engines/scumm/metaengine.cpp:1221
+#: engines/scumm/metaengine.cpp:1168 engines/scumm/metaengine.cpp:1245
 msgid "Aim up / menu up"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1149 engines/scumm/metaengine.cpp:1226
+#: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1250
 msgid "Aim down / menu down"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1154 engines/scumm/metaengine.cpp:1231
+#: engines/scumm/metaengine.cpp:1178 engines/scumm/metaengine.cpp:1255
 msgid "Aim left / menu left"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1159 engines/scumm/metaengine.cpp:1236
+#: engines/scumm/metaengine.cpp:1183 engines/scumm/metaengine.cpp:1260
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1164 engines/scumm/metaengine.cpp:1241
+#: engines/scumm/metaengine.cpp:1188 engines/scumm/metaengine.cpp:1265
 #, fuzzy
 #| msgid "Pick up"
 msgid "Stick up"
 msgstr "Pak op"
 
-#: engines/scumm/metaengine.cpp:1171 engines/scumm/metaengine.cpp:1248
+#: engines/scumm/metaengine.cpp:1195 engines/scumm/metaengine.cpp:1272
 #, fuzzy
 #| msgid "Scroll down"
 msgid "Stick down"
 msgstr "Scroll omlaag"
 
-#: engines/scumm/metaengine.cpp:1178 engines/scumm/metaengine.cpp:1255
+#: engines/scumm/metaengine.cpp:1202 engines/scumm/metaengine.cpp:1279
 #, fuzzy
 #| msgid "Slide left"
 msgid "Stick left"
 msgstr "Schuif naar link"
 
-#: engines/scumm/metaengine.cpp:1185 engines/scumm/metaengine.cpp:1262
+#: engines/scumm/metaengine.cpp:1209 engines/scumm/metaengine.cpp:1286
 #, fuzzy
 #| msgid "Slide right"
 msgid "Stick right"
 msgstr "Schuif naar rechts"
 
-#: engines/scumm/metaengine.cpp:1192 engines/scumm/metaengine.cpp:1269
+#: engines/scumm/metaengine.cpp:1216 engines/scumm/metaengine.cpp:1293
 #, fuzzy
 #| msgid "Move / Select"
 msgid "Fire / select"
 msgstr "Verplaatsen / Selecteren"
 
-#: engines/scumm/metaengine.cpp:1198
+#: engines/scumm/metaengine.cpp:1222
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Walk / menu back"
 msgstr "Hoofdmenu overslaan"
 
-#: engines/scumm/metaengine.cpp:1203
+#: engines/scumm/metaengine.cpp:1227
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu back"
 msgstr "Hoofdmenu overslaan"
 
-#: engines/scumm/metaengine.cpp:1219
+#: engines/scumm/metaengine.cpp:1243
 msgid "Rebel Assault II controls"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1274
+#: engines/scumm/metaengine.cpp:1298
 #, fuzzy
 #| msgid "Move back"
 msgid "Cover / back"
 msgstr "Teruggaan"
 
-#: engines/scumm/metaengine.cpp:1280
+#: engines/scumm/metaengine.cpp:1304
 #, fuzzy
 #| msgid "Skip walk"
 msgid "Skip / back"
 msgstr "Sla wandeling over"
 
-#: engines/scumm/metaengine.cpp:1287
+#: engines/scumm/metaengine.cpp:1311
 #, fuzzy
 #| msgid "Skip main menu"
 msgid "Skip / menu"
@@ -13866,14 +13876,14 @@ msgstr ""
 "Aspect ratio correctie kan gerealiseerd worden door de resolutie bij te "
 "knippen naar 320x200, onder de 'engine' bladwijzer."
 
-#: engines/scumm/scumm.cpp:1362
+#: engines/scumm/scumm.cpp:1365
 #, c-format
 msgid "This game requires the '%s' Macintosh executable for its fonts."
 msgstr ""
 "Voor dit spel is het uitvoerbare bestand '%s' voor Macintosh nodig voor de "
 "lettertypen."
 
-#: engines/scumm/scumm.cpp:1371
+#: engines/scumm/scumm.cpp:1374
 #, c-format
 msgid ""
 "This game requires the '%s' Macintosh executable for its music and fonts."
@@ -13881,7 +13891,7 @@ msgstr ""
 "Voor dit spel is het uitvoerbare bestand '%s' voor Macintosh nodig voor de "
 "muziek en lettertypen."
 
-#: engines/scumm/scumm.cpp:1375
+#: engines/scumm/scumm.cpp:1378
 #, c-format
 msgid ""
 "Could not find the '%s' Macintosh executable to read resources from. %s will "
@@ -13890,25 +13900,25 @@ msgstr ""
 "Het Macintosh-uitvoerbare bestand '%s' om bronnen uit te lezen, kon niet "
 "worden gevonden. %s wordt uitgeschakeld."
 
-#: engines/scumm/scumm.cpp:1376
+#: engines/scumm/scumm.cpp:1379
 msgid "The Mac GUI"
 msgstr "De Mac GUI"
 
-#: engines/scumm/scumm.cpp:1376
+#: engines/scumm/scumm.cpp:1379
 msgid "The music and the Mac GUI"
 msgstr "De muziek en de Mac GUI"
 
-#: engines/scumm/scumm.cpp:1405
+#: engines/scumm/scumm.cpp:1408
 #, c-format
 msgid "Could not open Macintosh resource file %s"
 msgstr "Kon Macintosch bronbestand %s niet openen"
 
-#: engines/scumm/scumm.cpp:1408
+#: engines/scumm/scumm.cpp:1411
 #, c-format
 msgid "Could not find resource fork in Macintosh resource file %s"
 msgstr "Kan resource fork niet vinden in Macintosh-resourcebestand %s"
 
-#: engines/scumm/scumm.cpp:1415
+#: engines/scumm/scumm.cpp:1418
 #, c-format
 msgid ""
 "'%s' appears to be the wrong Dig executable. It may be the launcher one "
@@ -13922,7 +13932,7 @@ msgstr ""
 "de naam 'The Dig f'. Elk van de submappen daarin zou moeten zijn wat u nodig "
 "hebt."
 
-#: engines/scumm/scumm.cpp:1581
+#: engines/scumm/scumm.cpp:1584
 msgid ""
 "It appears your ScummVM version was not built with TrueType Fonts support.\n"
 "\n"
@@ -13935,7 +13945,7 @@ msgstr ""
 "Aangezien de One-Stop Fun Shop-serie veel gebruikmaakt van TTF-lettertypen,\n"
 "zullen sommige afbeeldingen op het scherm ontbreken."
 
-#: engines/scumm/scumm.cpp:1915
+#: engines/scumm/scumm.cpp:1918
 msgid ""
 "Audio files compressed with ScummVM Tools were detected; *.BUN/*.SOU\n"
 "compression is not supported anymore for this game, audio will be disabled.\n"
@@ -13947,7 +13957,7 @@ msgstr ""
 "uitgeschakeld.\n"
 "Kopieer a.u.b. het spel van de originele media zonder compressie."
 
-#: engines/scumm/scumm.cpp:2405
+#: engines/scumm/scumm.cpp:2408
 #, c-format
 msgid ""
 "Native MIDI support requires the Roland Upgrade from LucasArts,\n"
@@ -13956,7 +13966,7 @@ msgstr ""
 "Voor MIDI support is de Roland Upgrade van Lucasarts vereist,\n"
 "maar %s ontbreekt. Er wordt nu AdLib gebruikt."
 
-#: engines/scumm/scumm.cpp:2420
+#: engines/scumm/scumm.cpp:2423
 msgid ""
 "This particular version of Monkey Island 1 is known to miss some\n"
 "required resources for MT-32. Using AdLib instead."
@@ -13965,7 +13975,7 @@ msgstr ""
 "vereiste bronnen voor MT-32 ontbreken. In plaats daarvan wordt AdLib "
 "gebruikt."
 
-#: engines/scumm/scumm.cpp:4361
+#: engines/scumm/scumm.cpp:4364
 msgid ""
 "Usually, Maniac Mansion would start now. But for that to work, the game "
 "files for Maniac Mansion have to be in the 'Maniac' directory inside the "
@@ -13975,7 +13985,7 @@ msgstr ""
 "moeten de Maniac Mansion bestanden in de 'Maniac' map in de Tentacle spelmap "
 "staan, en het spel moet worden toegevoegd aan ScummVM."
 
-#: engines/scumm/scumm.cpp:4485
+#: engines/scumm/scumm.cpp:4488
 msgid "Would you like to host or join a network play session?"
 msgstr "Wilt u een netwerk spel hosten of aan meedoen?"
 
diff --git a/po/nn_NO.po b/po/nn_NO.po
index f3ce860d457..f1621f0453b 100644
--- a/po/nn_NO.po
+++ b/po/nn_NO.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-08 11:04+0000\n"
+"POT-Creation-Date: 2026-06-09 13:22+0000\n"
 "PO-Revision-Date: 2023-12-14 14:28+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Norwegian Nynorsk <https://translations.scummvm.org/projects/"
@@ -344,7 +344,7 @@ msgstr "Ein anna lagringsteneste arbeider nett no. Vill du abryta han?"
 #: engines/hypno/metaengine.cpp:192 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:197
 #: engines/mm/mm1/metaengine.cpp:54 engines/parallaction/metaengine.cpp:169
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4467
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
 #: engines/sherlock/metaengine.cpp:638 engines/sherlock/metaengine.cpp:1244
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:328
@@ -363,7 +363,7 @@ msgstr "Ja"
 #: engines/hypno/metaengine.cpp:198 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:204
 #: engines/mm/mm1/metaengine.cpp:52 engines/parallaction/metaengine.cpp:175
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4467
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
 #: engines/sherlock/metaengine.cpp:643 engines/sherlock/metaengine.cpp:1250
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:329
@@ -391,7 +391,7 @@ msgstr "JSON-koden er feil utforma."
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:123
 #: engines/drascula/saveload.cpp:51 engines/grim/inputdialog.cpp:83
 #: engines/parallaction/saveload.cpp:270 engines/sci/engine/kgraphics.cpp:76
-#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4479
+#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4482
 #: engines/sword1/control.cpp:2816
 msgid "OK"
 msgstr "OK"
@@ -1057,7 +1057,7 @@ msgstr "Interager"
 #: engines/dragons/metaengine.cpp:158 engines/freescape/movement.cpp:35
 #: engines/got/metaengine.cpp:70 engines/griffon/metaengine.cpp:121
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
-#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1100
+#: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1124
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
 #: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:45
@@ -1078,7 +1078,7 @@ msgstr "Opp"
 #: engines/dragons/metaengine.cpp:164 engines/freescape/movement.cpp:44
 #: engines/got/metaengine.cpp:71 engines/griffon/metaengine.cpp:127
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
-#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1067
+#: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1091
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
 #: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
 #: engines/ultima/ultima0/metaengine.cpp:46
@@ -1099,7 +1099,7 @@ msgstr "Ned"
 #: engines/dragons/metaengine.cpp:170 engines/glk/dialogs.cpp:371
 #: engines/got/metaengine.cpp:72 engines/griffon/metaengine.cpp:133
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
-#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1080
+#: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1104
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
 #: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
@@ -1119,7 +1119,7 @@ msgstr "Venstre"
 #: engines/dragons/metaengine.cpp:176 engines/glk/dialogs.cpp:373
 #: engines/got/metaengine.cpp:73 engines/griffon/metaengine.cpp:139
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
-#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1087
+#: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1111
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
 #: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
@@ -3336,7 +3336,7 @@ msgstr ""
 #: engines/mohawk/riven_stacks/aspit.cpp:454
 #: engines/neverhood/metaengine.cpp:200 engines/saga/metaengine.cpp:449
 #: engines/scumm/help.cpp:84 engines/scumm/help.cpp:86
-#: engines/scumm/scumm.cpp:4479 engines/sludge/keymapper_tables.h:95
+#: engines/scumm/scumm.cpp:4482 engines/sludge/keymapper_tables.h:95
 #: engines/sludge/keymapper_tables.h:144 engines/sludge/keymapper_tables.h:223
 #: engines/sludge/keymapper_tables.h:343 engines/sludge/keymapper_tables.h:355
 #: engines/sludge/keymapper_tables.h:416 engines/sludge/keymapper_tables.h:429
@@ -3822,7 +3822,7 @@ msgid "Game not implemented"
 msgstr "Spel ID ikkje st??tta"
 
 #: engines/metaengine.cpp:75 engines/saga/metaengine.cpp:267
-#: engines/scumm/metaengine.cpp:1050
+#: engines/scumm/metaengine.cpp:1074
 #, fuzzy
 #| msgid "Game Options"
 msgid "Default game keymappings"
@@ -5814,7 +5814,7 @@ msgstr ""
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
 #: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
-#: engines/scumm/metaengine.cpp:1208 engines/toltecs/metaengine.cpp:252
+#: engines/scumm/metaengine.cpp:1232 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "Meny"
 
@@ -6781,7 +6781,7 @@ msgstr ""
 msgid "Quick load or save game isn't supported in this location"
 msgstr ""
 
-#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3418
+#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3421
 #, fuzzy, c-format
 msgid ""
 "Failed to load saved game from file:\n"
@@ -6789,7 +6789,7 @@ msgid ""
 "%s"
 msgstr "Kunne ikkje laste lagra spel fr?? fil."
 
-#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3405
+#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3408
 #, fuzzy, c-format
 msgid ""
 "Failed to save game to file:\n"
@@ -6797,7 +6797,7 @@ msgid ""
 "%s"
 msgstr "Klarte ikkje lagre speltilstand til fil."
 
-#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3438
+#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3441
 #, fuzzy, c-format
 msgid ""
 "Successfully saved game in file:\n"
@@ -7288,7 +7288,7 @@ msgstr "Veksle musl??sing"
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
 #: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
-#: engines/scumm/metaengine.cpp:1126 engines/supernova/metaengine.cpp:309
+#: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
@@ -8036,7 +8036,7 @@ msgstr "Interpolering:"
 #. I18N: Combat command in Might & Magic 1
 #: engines/crab/input/input.cpp:125 engines/efh/metaengine.cpp:487
 #: engines/efh/metaengine.cpp:512 engines/griffon/metaengine.cpp:145
-#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1113
+#: engines/mm/mm1/metaengine.cpp:70 engines/scumm/metaengine.cpp:1137
 #: engines/ultima/ultima0/metaengine.cpp:82
 #: engines/ultima/ultima4/metaengine.cpp:48
 #: engines/ultima/nuvie/metaengine.cpp:74
@@ -10250,7 +10250,7 @@ msgid "Player ammo will never decrease."
 msgstr ""
 
 #: engines/hypno/metaengine.cpp:70 engines/scumm/metaengine.cpp:891
-#: engines/scumm/metaengine.cpp:909
+#: engines/scumm/metaengine.cpp:918
 msgid "Unlock all levels"
 msgstr ""
 
@@ -12688,7 +12688,7 @@ msgid "Your Name:"
 msgstr "Namn:"
 
 #. I18N: Button, start hosting online multiplayer game
-#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4485
+#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4488
 msgid "Host"
 msgstr ""
 
@@ -12700,7 +12700,7 @@ msgid "Querying games..."
 msgstr "Lagrar spel..."
 
 #. I18N: Join online multiplayer game
-#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4485
+#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4488
 msgid "Join"
 msgstr ""
 
@@ -13631,126 +13631,135 @@ msgstr "Nytt h??goppl??yseleg grafikk"
 msgid "Run the game in 640x400 high resolution mode instead of 320x200"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:910
+#: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:919
 msgid "All levels will be available without requiring passwords"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:900
+#: engines/scumm/metaengine.cpp:900 engines/scumm/metaengine.cpp:927
 #, fuzzy
 #| msgid "No date saved"
 msgid "No damage"
 msgstr "Ingen dato lagra"
 
-#: engines/scumm/metaengine.cpp:901
+#: engines/scumm/metaengine.cpp:901 engines/scumm/metaengine.cpp:928
 #, fuzzy
 msgid "Disable player damage"
 msgstr "Klarte ikkje lagre spel"
 
-#: engines/scumm/metaengine.cpp:1061
+#: engines/scumm/metaengine.cpp:909
+#, fuzzy
+msgid "Yoda mode"
+msgstr "Rask modus"
+
+#: engines/scumm/metaengine.cpp:910
+msgid "Enable original Yoda mode shortcuts, including movie mode and auto play"
+msgstr ""
+
+#: engines/scumm/metaengine.cpp:1085
 #, fuzzy
 msgid "Down left"
 msgstr "Snu til Venstre"
 
-#: engines/scumm/metaengine.cpp:1074
+#: engines/scumm/metaengine.cpp:1098
 #, fuzzy
 msgid "Down right"
 msgstr "Snu til H??yre"
 
-#: engines/scumm/metaengine.cpp:1094
+#: engines/scumm/metaengine.cpp:1118
 #, fuzzy
 #| msgid "Upper left item"
 msgid "Up left"
 msgstr "??vre venstre gjenstand"
 
-#: engines/scumm/metaengine.cpp:1107
+#: engines/scumm/metaengine.cpp:1131
 #, fuzzy
 msgid "Up right"
 msgstr "H??gre"
 
-#: engines/scumm/metaengine.cpp:1119
+#: engines/scumm/metaengine.cpp:1143
 #, fuzzy
 msgid "Switch weapon"
 msgstr "Veksle karakterar:"



From noreply at scummvm.org  Tue Jun  9 17:30:02 2026
From: noreply at scummvm.org (sev-)
Date: Tue,  9 Jun 2026 17:30:02 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 13d857ee33912212235cfabb8b77fbd891ca861d
Message-ID: <20260609173002.32DF87005BC@ron.scummvm.net>

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:
7f50af3e33 DIRECTOR: Don't resize MIAW window to movie rect on load
5f9373d1d9 DIRECTOR: Fix `the stageLeft/Top/RightBottom`
13d857ee33 DIRECTOR: Don't send window events for versions below 5.0


Commit: 7f50af3e33e6c82ea7c4a704b247af0a0f291568
    https://github.com/scummvm/scummvm/commit/7f50af3e33e6c82ea7c4a704b247af0a0f291568
Author: Zachary Berry (zberry92 at gmail.com)
Date: 2026-06-09T19:29:56+02:00

Commit Message:
DIRECTOR: Don't resize MIAW window to movie rect on load

Movie::loadArchive() was calling resizeInner(_movieRect) for all windows,
overriding the size Lingo set via "set the rect of window X". When a MIAW
like a map preview is sized to 274x218 by script but the loaded movie has a
640x480 declared rect, the surface was resized to 640x480 and most of the
content appeared off-screen. Only resize non-stage windows when they are
still at the default 1x1 size (no Lingo rect has been set).

Assisted-by: Claude:claude-sonnet-4-6

Changed paths:
    engines/director/movie.cpp


diff --git a/engines/director/movie.cpp b/engines/director/movie.cpp
index d01233acc7d..5ec6b45a37a 100644
--- a/engines/director/movie.cpp
+++ b/engines/director/movie.cpp
@@ -235,11 +235,15 @@ bool Movie::loadArchive() {
 	g_director->_lastPalette = CastMemberID();
 
 	bool recenter = false;
-	// If the stage dimensions are different, delete it and start again.
-	// Otherwise, do not clear it so there can be a nice transition.
-	if (_window->getSurface()->w != _movieRect.width() || _window->getSurface()->h != _movieRect.height()) {
-		_window->resizeInner(_movieRect.width(), _movieRect.height());
-		recenter = true;
+	// For the stage, always resize to the movie rect.
+	// For MIAWs, only resize if the window hasn't been explicitly sized by Lingo
+	// (i.e. still at the 1x1 default from createWindow).
+	bool windowSizeIsDefault = (_window->getSurface()->w <= 1 && _window->getSurface()->h <= 1);
+	if (_window == _vm->getStage() || windowSizeIsDefault) {
+		if (_window->getSurface()->w != _movieRect.width() || _window->getSurface()->h != _movieRect.height()) {
+			_window->resizeInner(_movieRect.width(), _movieRect.height());
+			recenter = true;
+		}
 	}
 
 	// TODO: Add more options for desktop dimensions


Commit: 5f9373d1d9ad560a4bc733336a330eacaf6bbb45
    https://github.com/scummvm/scummvm/commit/5f9373d1d9ad560a4bc733336a330eacaf6bbb45
Author: Zachary Berry (zberry92 at gmail.com)
Date: 2026-06-09T19:29:56+02:00

Commit Message:
DIRECTOR: Fix `the stageLeft/Top/RightBottom`

Returned values relative to the current active window instead of the main
stage window.

Fixes Elroy Hits the Pavement  map preview centering. When opening the map,
preview MIAW in Elroy (Director 4), the window was being positioned incorrectly.

Changed paths:
    engines/director/lingo/lingo-the.cpp


diff --git a/engines/director/lingo/lingo-the.cpp b/engines/director/lingo/lingo-the.cpp
index 938700cde23..ad14a527e28 100644
--- a/engines/director/lingo/lingo-the.cpp
+++ b/engines/director/lingo/lingo-the.cpp
@@ -1108,32 +1108,20 @@ Datum Lingo::getTheEntity(int entity, Datum &id, int field) {
 		d = _vm->getStage();
 		break;
 	case kTheStageBottom:
-		{
-			Window *window = _vm->getCurrentWindow();
-			d = window->_window->getInnerDimensions().bottom;
-		}
+		d = _vm->getStage()->_window->getInnerDimensions().bottom;
 		break;
 	case kTheStageColor:
 		// TODO: Provide proper reverse transform for non-indexed color
 		d = (int)g_director->transformColor(g_director->getCurrentWindow()->getStageColor());
 		break;
 	case kTheStageLeft:
-		{
-			Window *window = _vm->getCurrentWindow();
-			d = window->_window->getInnerDimensions().left;
-		}
+		d = _vm->getStage()->_window->getInnerDimensions().left;
 		break;
 	case kTheStageRight:
-		{
-			Window *window = _vm->getCurrentWindow();
-			d = window->_window->getInnerDimensions().right;
-		}
+		d = _vm->getStage()->_window->getInnerDimensions().right;
 		break;
 	case kTheStageTop:
-		{
-			Window *window = _vm->getCurrentWindow();
-			d = window->_window->getInnerDimensions().top;
-		}
+		d = _vm->getStage()->_window->getInnerDimensions().top;
 		break;
 	case kTheStillDown:
 		if (Director::DT::isMouseInputIgnored()) {


Commit: 13d857ee33912212235cfabb8b77fbd891ca861d
    https://github.com/scummvm/scummvm/commit/13d857ee33912212235cfabb8b77fbd891ca861d
Author: Zachary Berry (zberry92 at gmail.com)
Date: 2026-06-09T19:29:56+02:00

Commit Message:
DIRECTOR: Don't send window events for versions below 5.0

Skip window events (like 'openWindow') for Director versions below 5.0.
In Director 4, MIAW window events did not exist. A game script uses
'on openWindow' as a manual procedural helper. By sending the openWindow
event unconditionally, ScummVM recursively triggered 'openWindow' in the
context of the preview window itself, which caused it to call 'forget' on itself
and be removed from the execution list.

Changed paths:
    engines/director/events.cpp


diff --git a/engines/director/events.cpp b/engines/director/events.cpp
index 59db793449a..cfa09d42fc3 100644
--- a/engines/director/events.cpp
+++ b/engines/director/events.cpp
@@ -436,6 +436,11 @@ bool Window::processWMEvent(Graphics::WindowClick click, Common::Event &event) {
 }
 
 void Window::sendWindowEvent(LEvent event) {
+	// Built-in window events introduced in D5.
+	// Could fire if lower-version has a similarly named event.
+	if (_vm->getVersion() < 500)
+		return;
+
 	if (_currentMovie && _window->isVisible() && !_isStage) {
 		// We cannot call processEvent here directly because it might
 		// be called from within another event processing (like 'on startMovie'	)



From noreply at scummvm.org  Tue Jun  9 17:31:30 2026
From: noreply at scummvm.org (sev-)
Date: Tue,  9 Jun 2026 17:31:30 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 c9add4a24dcab419142e43db4b1c668178c9bbd1
Message-ID: <20260609173130.BF5037005BC@ron.scummvm.net>

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:
276f3f2b05 DIRECTOR: LINGO: Fix FileIO appending save file
c9add4a24d DIRECTOR: Move _playbackPaused from engine global to Window


Commit: 276f3f2b05c055eb8d520da1c703536617df902b
    https://github.com/scummvm/scummvm/commit/276f3f2b05c055eb8d520da1c703536617df902b
Author: Zachary Berry (zberry92 at gmail.com)
Date: 2026-06-09T19:31:26+02:00

Commit Message:
DIRECTOR: LINGO: Fix FileIO appending save file

Changed paths:
    engines/director/lingo/xlibs/f/fileio.cpp


diff --git a/engines/director/lingo/xlibs/f/fileio.cpp b/engines/director/lingo/xlibs/f/fileio.cpp
index 71f0645908c..aa612552184 100644
--- a/engines/director/lingo/xlibs/f/fileio.cpp
+++ b/engines/director/lingo/xlibs/f/fileio.cpp
@@ -291,20 +291,19 @@ FileIOError FileObject::open(const Common::String &origpath, const Common::Strin
 	} else if (option.equalsIgnoreCase("append")) {
 		Common::SeekableReadStream *inFile = saves->openForLoading(filename);
 		if (!inFile) {
+			// Create file if it doesn't exist.
 			Common::Path location = findPath(origpath);
-			Common::SeekableReadStream *file = Common::MacResManager::openFileOrDataFork(location);
-			if (!file) {
-				return saveFileError();
-			}
-			inFile = file;
+			inFile = Common::MacResManager::openFileOrDataFork(location);
 		}
 		_outStream = new Common::MemoryWriteStreamDynamic(DisposeAfterUse::YES);
-		byte b = inFile->readByte();
-		while (!inFile->eos() && !inFile->err()) {
-			_outStream->writeByte(b);
-			b = inFile->readByte();
+		if (inFile) {
+			byte b = inFile->readByte();
+			while (!inFile->eos() && !inFile->err()) {
+				_outStream->writeByte(b);
+				b = inFile->readByte();
+			}
+			delete inFile;
 		}
-		delete inFile;
 	} else {
 		error("Unsupported FileIO option: '%s'", option.c_str());
 	}
@@ -393,13 +392,15 @@ void FileIO::m_openFile(int nargs) {
 	int mode = d1.asInt();
 	Common::String option;
 	switch (mode) {
+	case 0:
+		option = "append";
+		break;
 	case 1:
 		option = "read";
 		break;
 	case 2:
 		option = "write";
 		break;
-	case 0:
 	default:
 		warning("FIXME: Mode %d not supported, falling back to read", mode);
 		option = "read";
@@ -565,7 +566,17 @@ void FileIO::m_writeChar(int nargs) {
 		return;
 	}
 
-	me->_outStream->writeByte(d.asInt());
+	// The XObject API passes an integer ASCII code (mWriteChar, charNum),
+	// while the Xtra API passes the character itself as a string (writeChar, theChar).
+	byte ch;
+	if (d.type == STRING) {
+		Common::String s = d.asString();
+		ch = s.empty() ? 0 : (byte)s[0];
+	} else {
+		ch = (byte)d.asInt();
+	}
+
+	me->_outStream->writeByte(ch);
 	g_lingo->push(Datum(kErrorNone));
 }
 


Commit: c9add4a24dcab419142e43db4b1c668178c9bbd1
    https://github.com/scummvm/scummvm/commit/c9add4a24dcab419142e43db4b1c668178c9bbd1
Author: Zachary Berry (zberry92 at gmail.com)
Date: 2026-06-09T19:31:26+02:00

Commit Message:
DIRECTOR: Move _playbackPaused from engine global to Window

Having _playbackPaused in DirectorEngine would pause all windows
on pause(). Moving it to Window allows games to pause one window
and start another one.

This was impacting pulling up the map in Escape from Dimension Q.
The map Window would be created but never start.

Assisted-by: Claude:claude-sonnet-4-6

Changed paths:
    engines/director/director.cpp
    engines/director/director.h
    engines/director/lingo/lingo-builtins.cpp
    engines/director/lingo/lingo-funcs.cpp
    engines/director/lingo/lingo-the.cpp
    engines/director/score.cpp
    engines/director/window.cpp
    engines/director/window.h


diff --git a/engines/director/director.cpp b/engines/director/director.cpp
index bd7afa3ea54..53d167f1331 100644
--- a/engines/director/director.cpp
+++ b/engines/director/director.cpp
@@ -157,7 +157,6 @@ DirectorEngine::DirectorEngine(OSystem *syst, const DirectorGameDescription *gam
 		_machineType = 9;	// Macintosh IIci
 	}
 
-	_playbackPaused = false;
 	_centerStage = true;
 
 	_surface = nullptr;
@@ -225,6 +224,7 @@ void DirectorEngine::forgetWindow(Window *window) {
 			return;
 	}
 	window->setVisible(false, true);
+	window->getSoundManager()->stopSound();
 	_windowsToForget.push_back(window);
 }
 
diff --git a/engines/director/director.h b/engines/director/director.h
index b994db9b77d..9b506c8bd43 100644
--- a/engines/director/director.h
+++ b/engines/director/director.h
@@ -267,7 +267,6 @@ public:
 	int _colorDepth;
 	Common::HashMap<int, int> _KeyCodes;
 	int _machineType;
-	bool _playbackPaused;
 	bool _centerStage;
 	char _dirSeparator;
 	bool _fixStageSize;
diff --git a/engines/director/lingo/lingo-builtins.cpp b/engines/director/lingo/lingo-builtins.cpp
index 510e5762ddf..8126a6843e7 100644
--- a/engines/director/lingo/lingo-builtins.cpp
+++ b/engines/director/lingo/lingo-builtins.cpp
@@ -1954,7 +1954,7 @@ void LB::b_cancelIdleLoad(int nargs) {
 }
 
 void LB::b_continue(int nargs) {
-	g_director->_playbackPaused = false;
+	g_director->getCurrentWindow()->_playbackPaused = false;
 }
 
 void LB::b_dontPassEvent(int nargs) {
@@ -2087,7 +2087,7 @@ void LB::b_pass(int nargs) {
 }
 
 void LB::b_pause(int nargs) {
-	g_director->_playbackPaused = true;
+	g_director->getCurrentWindow()->_playbackPaused = true;
 }
 
 void LB::b_play(int nargs) {
diff --git a/engines/director/lingo/lingo-funcs.cpp b/engines/director/lingo/lingo-funcs.cpp
index d905703e130..563125e7346 100644
--- a/engines/director/lingo/lingo-funcs.cpp
+++ b/engines/director/lingo/lingo-funcs.cpp
@@ -40,7 +40,7 @@
 namespace Director {
 
 void Lingo::func_goto(Datum &frame, Datum &movie, bool calledfromgo) {
-	_vm->_playbackPaused = false;
+	_vm->getCurrentWindow()->_playbackPaused = false;
 
 	if (!_vm->getCurrentMovie())
 		return;
@@ -120,6 +120,7 @@ void Lingo::func_gotoloop() {
 	if (!_vm->getCurrentMovie())
 		return;
 	Window *stage = _vm->getCurrentWindow();
+	stage->_playbackPaused = false;
 	Score *score = stage->getCurrentMovie()->getScore();
 	debugC(3, kDebugLingoExec, "Lingo::func_gotoloop(): looping frame %d", score->getCurrentFrameNum());
 
@@ -133,6 +134,7 @@ void Lingo::func_gotonext() {
 		return;
 
 	Window *stage = _vm->getCurrentWindow();
+	stage->_playbackPaused = false;
 	Score *score = stage->getCurrentMovie()->getScore();
 	score->gotoNext();
 	debugC(3, kDebugLingoExec, "Lingo::func_gotonext(): going to next frame %d", score->getNextFrame());
@@ -145,6 +147,7 @@ void Lingo::func_gotoprevious() {
 		return;
 
 	Window *stage = _vm->getCurrentWindow();
+	stage->_playbackPaused = false;
 	Score *score = stage->getCurrentMovie()->getScore();
 	score->gotoPrevious();
 	debugC(3, kDebugLingoExec, "Lingo::func_gotoprevious(): going to previous frame %d", score->getNextFrame());
diff --git a/engines/director/lingo/lingo-the.cpp b/engines/director/lingo/lingo-the.cpp
index ad14a527e28..3afd0676ebc 100644
--- a/engines/director/lingo/lingo-the.cpp
+++ b/engines/director/lingo/lingo-the.cpp
@@ -961,7 +961,7 @@ Datum Lingo::getTheEntity(int entity, Datum &id, int field) {
 		d = g_lingo->_state->callstack[g_lingo->_state->callstack.size() - 1]->paramCount;
 		break;
 	case kThePauseState:
-		d = (int) g_director->_playbackPaused;
+		d = (int) g_director->getCurrentWindow()->_playbackPaused;
 		break;
 	case kThePerFrameHook:
 		d = _perFrameHook;
diff --git a/engines/director/score.cpp b/engines/director/score.cpp
index 1465d21b7e6..29dc5cd5d14 100644
--- a/engines/director/score.cpp
+++ b/engines/director/score.cpp
@@ -440,7 +440,7 @@ bool Score::isWaitingForNextFrame() {
 void Score::updateCurrentFrame() {
 	uint32 nextFrameNumberToLoad = _curFrameNumber;
 
-	if (!_vm->_playbackPaused) {
+	if (!_window->_playbackPaused) {
 		if (_nextFrame) {
 			// With the advent of demand loading frames and due to partial updates, we rebuild our channel data
 			// when jumping.
@@ -506,7 +506,7 @@ void Score::updateCurrentFrame() {
 		// Finally, update the channels and buffer any dirty rectangles.
 		// This will ignore any channel data that is overridden with the puppet flag.
 		updateSprites(kRenderModeNormal, true);
-	} else if (!_vm->_playbackPaused) {
+	} else if (!_window->_playbackPaused) {
 		// Loading the same frame; e.g. "go to frame".
 		// This is mostly a no-op, however any sprite changes for
 		// non-puppet sprites will be reverted.
@@ -626,7 +626,7 @@ void Score::update() {
 	}
 
 	// Process timeout events independently of the frame delay
-	if (_haveInteractivity && !_vm->_playbackPaused) {
+	if (_haveInteractivity && !_window->_playbackPaused) {
 		if (_vm->getMacTicks() - _movie->_lastTimeOut >= _movie->_timeOutLength) {
 			_movie->processEvent(kEventTimeout);
 			_movie->_lastTimeOut = _vm->getMacTicks();
@@ -651,7 +651,7 @@ void Score::update() {
 	}
 
 	// For previous frame
-	if (!_window->_newMovieStarted && !_vm->_playbackPaused) {
+	if (!_window->_newMovieStarted && !_window->_playbackPaused) {
 		// When Lingo::func_goto* is called, _nextFrame is set
 		// and _skipFrameAdvance is set to true.
 		// exitFrame is not called in this case.
@@ -685,7 +685,7 @@ void Score::update() {
 	}
 
 	// Kill behaviors if they are going to expire next frame
-	if (!_vm->_playbackPaused)
+	if (!_window->_playbackPaused)
 		killScriptInstances(_nextFrame ? _nextFrame : _curFrameNumber + 1);
 
 	CastMemberID oldSound1 = _currentFrame->_mainChannels.sound1;
@@ -716,7 +716,7 @@ void Score::update() {
 		// Director 4 and below will allow infinite recursion via the perFrameHook.
 		if (_version < kFileVer500) {
 			// new frame, first call the perFrameHook (if one exists)
-			if (!_window->_newMovieStarted && !_vm->_playbackPaused) {
+			if (!_window->_newMovieStarted && !_window->_playbackPaused) {
 				// Call the perFrameHook as soon as a frame switch is done.
 				// If there is a transition, the perFrameHook is called
 				// after each transition subframe instead of here.
@@ -742,7 +742,7 @@ void Score::update() {
 		// Director 5 and above actually check for recursion for the perFrameHook.
 		if (_version >= kFileVer500) {
 			// new frame, first call the perFrameHook (if one exists)
-			if (!_window->_newMovieStarted && !_vm->_playbackPaused) {
+			if (!_window->_newMovieStarted && !_window->_playbackPaused) {
 				// Call the perFrameHook as soon as a frame switch is done.
 				// If there is a transition, the perFrameHook is called
 				// after each transition subframe instead of here.
@@ -796,7 +796,7 @@ void Score::update() {
 	// then call the stepMovie hook (if one exists)
 	// D4 and above only call it if _allowOutdatedLingo is enabled.
 	count = _window->frozenLingoStateCount();
-	if (!_vm->_playbackPaused && (_version < kFileVer400 || _movie->_allowOutdatedLingo)) {
+	if (!_window->_playbackPaused && (_version < kFileVer400 || _movie->_allowOutdatedLingo)) {
 		_movie->processEvent(kEventStepMovie);
 	}
 	// If this stepMovie call is frozen, drop the next enterFrame event
@@ -811,7 +811,7 @@ void Score::update() {
 
 	// then call the enterFrame hook (if one exists)
 	count = _window->frozenLingoStateCount();
-	if (!_vm->_playbackPaused) {
+	if (!_window->_playbackPaused) {
 		_exitFrameCalled = false;
 		if (_version >= kFileVer400) {
 			_movie->processEvent(kEventEnterFrame);
@@ -832,7 +832,7 @@ void Score::update() {
 	if (!_nextFrame && _window->_nextMovie.movie.empty() && !processFrozenScripts())
 		return;
 
-	if (!_vm->_playbackPaused) {
+	if (!_window->_playbackPaused) {
 		if (_movie->_timeOutPlay)
 			_movie->_lastTimeOut = _vm->getMacTicks();
 	}
@@ -853,7 +853,7 @@ void Score::renderFrame(uint16 frameId, RenderMode mode, bool sound1Changed, boo
 		incrementFilmLoops();
 		_window->render();
 		_skipTransition = false;
-	} else if (_vm->_playbackPaused) {
+	} else if (_window->_playbackPaused) {
 		updateSprites(mode);
 		incrementFilmLoops();
 		_window->render();
diff --git a/engines/director/window.cpp b/engines/director/window.cpp
index 295d262fb48..63d92adc3e2 100644
--- a/engines/director/window.cpp
+++ b/engines/director/window.cpp
@@ -71,6 +71,7 @@ Window::Window(int id, bool scrollable, bool resizable, bool editable, Graphics:
 	_isModal = false;
 	_skipFrameAdvance = false;
 	_resetScreen = false;
+	_playbackPaused = false;
 
 	// Owned by the window manager
 	_window = new Graphics::MacWindow(id, scrollable, resizable, editable, wm);
@@ -473,6 +474,10 @@ void Window::setVisible(bool visible, bool silent) {
 	if (!_currentMovie && !silent)
 		ensureMovieIsLoaded();
 
+	// If a modal window is not visible, release the locks it holds.
+	if (!visible && _isModal)
+		setModal(false);
+
 	_window->setVisible(visible);
 
 	if (visible)
diff --git a/engines/director/window.h b/engines/director/window.h
index 0139e0cb962..57a38c8c529 100644
--- a/engines/director/window.h
+++ b/engines/director/window.h
@@ -229,6 +229,7 @@ public:
 	bool _newMovieFirstDraw;
 	bool _skipFrameAdvance;
 	bool _resetScreen;
+	bool _playbackPaused;
 
 private:
 	uint32 _stageColor;



From noreply at scummvm.org  Tue Jun  9 19:14:34 2026
From: noreply at scummvm.org (neuromancer)
Date: Tue,  9 Jun 2026 19:14:34 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 9ef0dfae2debdd325736ea2556dff658ef9293a3
Message-ID: <20260609191434.269F47005BC@ron.scummvm.net>

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

Summary:
21a0fa47b9 SCUMM: RA2: better gameplay support for high resolution mode
082fbb3a19 SCUMM: RA2: avoid palette corruption in high resolution mode
eb0ce18ea5 SCUMM: RA: make sure all the debug statements using channels
9ef0dfae2d SCUMM: RA: removed redundant tags from debug messages


Commit: 21a0fa47b99a3d1c3ae37895d78e3c6f9fd66125
    https://github.com/scummvm/scummvm/commit/21a0fa47b99a3d1c3ae37895d78e3c6f9fd66125
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-09T21:14:12+02:00

Commit Message:
SCUMM: RA2: better gameplay support for high resolution mode

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


diff --git a/engines/scumm/insane/rebel2/rebel.cpp b/engines/scumm/insane/rebel2/rebel.cpp
index 9959ca44bdd..25c7d9f7a23 100644
--- a/engines/scumm/insane/rebel2/rebel.cpp
+++ b/engines/scumm/insane/rebel2/rebel.cpp
@@ -191,6 +191,7 @@ InsaneRebel2::InsaneRebel2(ScummEngine_v7 *scumm) {
 	_viewY = 0;
 	_hiResPresentationViewX = 0;
 	_hiResPresentationViewY = 0;
+	_gameplayPresentationClipBottom = 179;
 
 	// Damage visual effect counters (FUN_420515/420562/420754/42073B)
 	_damageFlashCounter = 0;
diff --git a/engines/scumm/insane/rebel2/rebel.h b/engines/scumm/insane/rebel2/rebel.h
index 1b263aed8ee..1e26ac3fb71 100644
--- a/engines/scumm/insane/rebel2/rebel.h
+++ b/engines/scumm/insane/rebel2/rebel.h
@@ -894,6 +894,7 @@ public:
 	int _viewY;
 	int _hiResPresentationViewX;
 	int _hiResPresentationViewY;
+	int _gameplayPresentationClipBottom;
 
 	// ---------------------------------------------------------------------------
 	// Damage Visual Effect System
@@ -1053,7 +1054,8 @@ public:
 
 	// Handler-specific explosion rendering
 	void renderExplosionFrame(byte *renderBitmap, int pitch, int width, int height,
-	                          Explosion &explosion, int screenX, int screenY, ExplosionFrameAdvance advance);
+	                          Explosion &explosion, int screenX, int screenY, ExplosionFrameAdvance advance,
+	                          bool resolutionDependentScale);
 	void renderTurretExplosions(byte *renderBitmap, int pitch, int width, int height);     // FUN_409FBC (Handler 0x26)
 	void renderVehicleExplosions(byte *renderBitmap, int pitch, int width, int height);    // FUN_402696 (Handler 8)
 	void renderSpaceExplosions(byte *renderBitmap, int pitch, int width, int height);      // FUN_40F1C5 (Handler 7)
diff --git a/engines/scumm/insane/rebel2/render.cpp b/engines/scumm/insane/rebel2/render.cpp
index 408e4d87411..31088a4d446 100644
--- a/engines/scumm/insane/rebel2/render.cpp
+++ b/engines/scumm/insane/rebel2/render.cpp
@@ -1506,7 +1506,7 @@ void InsaneRebel2::drawLaserBeam(byte *dst, int pitch, int width, int height,
 	int clipLeft = renderHiRes ? 0 : CLIP<int>(_viewX, 0, width - 1);
 	int clipTop = renderHiRes ? 0 : CLIP<int>(_viewY, 0, height - 1);
 	int clipRight = renderHiRes ? MIN(width - 1, 639) : CLIP<int>(_viewX + 319, 0, width - 1);
-	int clipBottom = renderHiRes ? MIN(height - 1, 359) : CLIP<int>(_viewY + 179, 0, height - 1);
+	int clipBottom = renderHiRes ? MIN(height - 1, _gameplayPresentationClipBottom) : CLIP<int>(_viewY + 179, 0, height - 1);
 	if (clipLeft > clipRight || clipTop > clipBottom)
 		return;
 	int edgeClipLeft = CLIP<int>(clipLeft + 1, 1, width - 2);
@@ -2641,6 +2641,10 @@ void InsaneRebel2::renderGameplayPostFrame(byte *renderBitmap, int pitch, int wi
 	// Original: FUN_00403240 only runs handlers when DAT_0047a814 == 0.
 	processMouse();
 
+	_gameplayPresentationClipBottom = statusBarY - 1;
+	if (isHiRes() && _rebelHandler == 7 && curFrame != 0)
+		_gameplayPresentationClipBottom = MIN(_gameplayPresentationClipBottom, 170 * 2 - 1);
+
 	_hiResPresentationViewX = 0;
 	_hiResPresentationViewY = 0;
 	if (isHiRes()) {
@@ -2916,10 +2920,13 @@ void InsaneRebel2::updateDamageEffect(byte *renderBitmap, int pitch, int width,
 
 		// Temporary buffer for scanline rotation (case 1 in original)
 		byte tempLine[640];
+		const bool renderHiRes = isHiRes() && width >= 640 && height >= 400;
+		const int maxY = MIN(height, renderHiRes ? 360 : 180);
 
 		for (int n = numLines; n > 0; n--) {
-			// Pick a random scanline within the gameplay area (0..179, not status bar)
-			int maxY = MIN(height, 180);
+			// Pick a random scanline within the gameplay area, not the status bar.
+			if (maxY <= 0)
+				continue;
 			int scanline = _vm->_rnd.getRandomNumber(maxY - 1);
 
 			byte *linePtr = renderBitmap + pitch * scanline;
@@ -3425,8 +3432,9 @@ void InsaneRebel2::renderHandler7FlySprite(byte *renderBitmap, int pitch, int wi
 	int dstY = renderHiRes ? (nativeY - nativeViewY) * renderScale : nativeY;
 
 	if (renderHiRes) {
+		const int clipBottom = MIN(height, _gameplayPresentationClipBottom + 1);
 		renderNutSpriteScaledClipped(renderBitmap, pitch, width, height,
-			0, 0, width, height, dstX, dstY, nut, spriteIndex, false, renderScale, true);
+			0, 0, width, clipBottom, dstX, dstY, nut, spriteIndex, false, renderScale, true);
 	} else {
 		renderNutSprite(renderBitmap, pitch, width, height, dstX, dstY, nut, spriteIndex);
 	}
@@ -4093,12 +4101,13 @@ void InsaneRebel2::renderExplosions(byte *renderBitmap, int pitch, int width, in
 	}
 }
 
-// renderExplosionFrame -- Shared low-res explosion sprite path.
+// renderExplosionFrame -- Shared explosion sprite path.
 // The original handlers reach this through separate retail functions. In the
-// 320x200 path used here, they share the same scale buckets and centered NUT
-// draw; callers keep their coordinate transforms and frame timing explicit.
+// 320x200 path used here, they share centered NUT drawing; callers keep their
+// coordinate transforms, frame timing, and scale bucket rules explicit.
 void InsaneRebel2::renderExplosionFrame(byte *renderBitmap, int pitch, int width, int height,
-		InsaneRebel2::Explosion &explosion, int screenX, int screenY, ExplosionFrameAdvance advance) {
+		InsaneRebel2::Explosion &explosion, int screenX, int screenY, ExplosionFrameAdvance advance,
+		bool resolutionDependentScale) {
 	if (!explosion.active)
 		return;
 
@@ -4110,14 +4119,16 @@ void InsaneRebel2::renderExplosionFrame(byte *renderBitmap, int pitch, int width
 	if (advance == kExplosionAdvanceBeforeDraw)
 		explosion.counter--;
 
-	// Fixed low-res thresholds (0x0b=11, 0x15=21).
+	const bool renderHiRes = isHiRes() && width >= 640 && height >= 400;
+	const int mediumThreshold = (resolutionDependentScale && renderHiRes) ? 20 : 10;
+	const int largeThreshold = (resolutionDependentScale && renderHiRes) ? 40 : 20;
 	int baseIndex;
-	if (explosion.scale < 11) {
-		baseIndex = 9;
-	} else if (explosion.scale < 21) {
+	if (explosion.scale > largeThreshold) {
+		baseIndex = 29;
+	} else if (explosion.scale > mediumThreshold) {
 		baseIndex = 19;
 	} else {
-		baseIndex = 29;
+		baseIndex = 9;
 	}
 
 	int spriteIndex = baseIndex + (12 - explosion.counter);
@@ -4125,15 +4136,20 @@ void InsaneRebel2::renderExplosionFrame(byte *renderBitmap, int pitch, int width
 	if (_smush_iconsNut->getNumChars() > spriteIndex) {
 		int ew = _smush_iconsNut->getCharWidth(spriteIndex);
 		int eh = _smush_iconsNut->getCharHeight(spriteIndex);
-		const bool renderHiRes = isHiRes() && width >= 640 && height >= 400;
 		int drawX = screenX;
 		int drawY = screenY;
 		if (renderHiRes) {
 			drawX = (screenX - _hiResPresentationViewX) * 2;
 			drawY = (screenY - _hiResPresentationViewY) * 2;
 		}
-		renderNutSprite(renderBitmap, pitch, width, height,
-			drawX - ew / 2, drawY - eh / 2, _smush_iconsNut, spriteIndex);
+		if (renderHiRes) {
+			renderNutSpriteClipped(renderBitmap, pitch, height,
+				0, 0, MIN(width, pitch), _gameplayPresentationClipBottom + 1,
+				drawX - ew / 2, drawY - eh / 2, _smush_iconsNut, spriteIndex);
+		} else {
+			renderNutSprite(renderBitmap, pitch, width, height,
+				drawX - ew / 2, drawY - eh / 2, _smush_iconsNut, spriteIndex);
+		}
 	}
 
 	if (advance == kExplosionAdvanceAfterDraw)
@@ -4156,7 +4172,7 @@ void InsaneRebel2::renderTurretExplosions(byte *renderBitmap, int pitch, int wid
 		int screenX = _explosions[i].x;
 		int screenY = _explosions[i].y;
 		renderExplosionFrame(renderBitmap, pitch, width, height, _explosions[i],
-			screenX, screenY, kExplosionAdvanceAfterDraw);
+			screenX, screenY, kExplosionAdvanceAfterDraw, false);
 	}
 }
 
@@ -4175,13 +4191,13 @@ void InsaneRebel2::renderVehicleExplosions(byte *renderBitmap, int pitch, int wi
 		int screenX = _explosions[i].x - _shipPosX;
 		int screenY = _explosions[i].y - _shipPosY;
 		renderExplosionFrame(renderBitmap, pitch, width, height, _explosions[i],
-			screenX, screenY, kExplosionAdvanceAfterDraw);
+			screenX, screenY, kExplosionAdvanceAfterDraw, false);
 	}
 }
 
 // renderSpaceExplosions -- Handler 7 space explosion rendering (FUN_40F1C5).
 // Position: FUN_0041c720 3D->2D projection.
-// Original scale thresholds are resolution-dependent; current low-res path uses <11/<21.
+// Original scale thresholds are resolution-dependent (<11/<21, doubled in high-res).
 // Secondary NUT: DAT_0047ff00 (FLY004, if DAT_0047a7fc >= 0).
 void InsaneRebel2::renderSpaceExplosions(byte *renderBitmap, int pitch, int width, int height) {
 	if (!_smush_iconsNut)
@@ -4197,7 +4213,7 @@ void InsaneRebel2::renderSpaceExplosions(byte *renderBitmap, int pitch, int widt
 		int screenX = _explosions[i].x;
 		int screenY = _explosions[i].y;
 		renderExplosionFrame(renderBitmap, pitch, width, height, _explosions[i],
-			screenX, screenY, kExplosionAdvanceAfterDraw);
+			screenX, screenY, kExplosionAdvanceAfterDraw, true);
 	}
 
 	// --- Part 2: Corridor/zone hit explosion (FUN_40F1C5 lines 61-85) ---
@@ -4252,8 +4268,14 @@ void InsaneRebel2::renderSpaceExplosions(byte *renderBitmap, int pitch, int widt
 
 			int ew = _smush_iconsNut->getCharWidth(spriteIndex);
 			int eh = _smush_iconsNut->getCharHeight(spriteIndex);
-			renderNutSprite(renderBitmap, pitch, width, height,
-				drawX - ew / 2, drawY - eh / 2, _smush_iconsNut, spriteIndex);
+			if (renderHiRes) {
+				renderNutSpriteClipped(renderBitmap, pitch, height,
+					0, 0, MIN(width, pitch), _gameplayPresentationClipBottom + 1,
+					drawX - ew / 2, drawY - eh / 2, _smush_iconsNut, spriteIndex);
+			} else {
+				renderNutSprite(renderBitmap, pitch, width, height,
+					drawX - ew / 2, drawY - eh / 2, _smush_iconsNut, spriteIndex);
+			}
 
 			debug("Rebel2 H7 corridor explosion: dir=%d frame=%d pos=(%d,%d) cooldown=%d",
 				_spaceShotDirection, spriteIndex, drawX, drawY, _hitCooldown);
@@ -4263,7 +4285,7 @@ void InsaneRebel2::renderSpaceExplosions(byte *renderBitmap, int pitch, int widt
 
 // renderHandler25Explosions -- Handler 25 FPS explosion rendering (FUN_41F29A).
 // Position: world coords + view offset (DAT_0045790c/0e = _rebelViewOffsetX/Y).
-// Original scale thresholds follow Handler 7; current low-res path uses <11/<21. No sound panning.
+// Original scale thresholds follow Handler 7 (<11/<21, doubled in high-res). No sound panning.
 void InsaneRebel2::renderHandler25Explosions(byte *renderBitmap, int pitch, int width, int height) {
 	if (!_smush_iconsNut)
 		return;
@@ -4276,7 +4298,7 @@ void InsaneRebel2::renderHandler25Explosions(byte *renderBitmap, int pitch, int
 		int screenX = _explosions[i].x + _rebelViewOffsetX;
 		int screenY = _explosions[i].y + _rebelViewOffsetY;
 		renderExplosionFrame(renderBitmap, pitch, width, height, _explosions[i],
-			screenX, screenY, kExplosionAdvanceBeforeDraw);
+			screenX, screenY, kExplosionAdvanceBeforeDraw, true);
 	}
 }
 
@@ -4604,25 +4626,26 @@ void InsaneRebel2::renderHandler8PovOverlay(byte *renderBitmap, int pitch, int w
 	fontSet.fonts[3] = _smush_povfontNut;
 
 	ScummEngine_v7 *vm = (ScummEngine_v7 *)_vm;
+	const int povScale = isHiRes() ? 2 : getRebel2IndicatorScale(width, height);
 
 	// Original updates DAT_0047e048 with random(5)+0x23 when random(20)==0.
 	if (getHandler8PovOverlayRandom(vm, 20) == 0)
 		_handler8HudGlyph = (char)(getHandler8PovOverlayRandom(vm, 5) + 0x23);
 
 	drawHandler8PovOverlayText(fontSet, renderBitmap, pitch, width, height,
-		getHandler8PovOverlayString(200), 10, 5, 1, kStyleAlignLeft);
+		getHandler8PovOverlayString(200), 10 * povScale, 5 * povScale, 1, kStyleAlignLeft);
 
 	char buffer[128];
 	Common::sprintf_s(buffer, "^f03&#$%c", _handler8HudGlyph);
 	drawHandler8PovOverlayText(fontSet, renderBitmap, pitch, width, height,
-		buffer, 10, 150, 248, kStyleAlignLeft);
+		buffer, 10 * povScale, 150 * povScale, 248, kStyleAlignLeft);
 
 	const char *text = getHandler8PovOverlayString(203);
 	if (text) {
 		Common::sprintf_s(buffer, text,
 			(unsigned long)(uint32)(int32)getHandler8PovOverlayRandom(vm, 20000));
 		drawHandler8PovOverlayText(fontSet, renderBitmap, pitch, width, height,
-			buffer, 10, 170, 1, kStyleAlignLeft);
+			buffer, 10 * povScale, 170 * povScale, 1, kStyleAlignLeft);
 	}
 
 	text = getHandler8PovOverlayString(202);
@@ -4630,7 +4653,7 @@ void InsaneRebel2::renderHandler8PovOverlay(byte *renderBitmap, int pitch, int w
 		Common::sprintf_s(buffer, text,
 			(unsigned long)(uint32)(int32)getHandler8PovOverlayRandom(vm, -0xd50));
 		drawHandler8PovOverlayText(fontSet, renderBitmap, pitch, width, height,
-			buffer, 220, 160, 1, kStyleAlignLeft);
+			buffer, 220 * povScale, 160 * povScale, 1, kStyleAlignLeft);
 	}
 
 	text = getHandler8PovOverlayString(204);
@@ -4638,7 +4661,7 @@ void InsaneRebel2::renderHandler8PovOverlay(byte *renderBitmap, int pitch, int w
 		Common::sprintf_s(buffer, text, (unsigned long)(uint32)(int32)(int16)_shipPosX,
 			(unsigned long)(uint32)(int32)(int16)_shipPosY);
 		drawHandler8PovOverlayText(fontSet, renderBitmap, pitch, width, height,
-			buffer, 220, 170, 1, kStyleAlignLeft);
+			buffer, 220 * povScale, 170 * povScale, 1, kStyleAlignLeft);
 	}
 
 	if (_handler8HudMessageTimer == 0) {
@@ -4649,7 +4672,7 @@ void InsaneRebel2::renderHandler8PovOverlay(byte *renderBitmap, int pitch, int w
 		_handler8HudMessageTimer--;
 		drawHandler8PovOverlayText(fontSet, renderBitmap, pitch, width, height,
 			getHandler8PovOverlayString(_handler8HudMessageIndex + 0xcd),
-			200, 5, 1, kStyleAlignCenter);
+			200 * povScale, 5 * povScale, 1, kStyleAlignCenter);
 	}
 }
 


Commit: 082fbb3a19a2c3dfe6e11cf15fbaea6e4ead7010
    https://github.com/scummvm/scummvm/commit/082fbb3a19a2c3dfe6e11cf15fbaea6e4ead7010
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-09T21:14:12+02:00

Commit Message:
SCUMM: RA2: avoid palette corruption in high resolution mode

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


diff --git a/engines/scumm/insane/rebel2/iact.cpp b/engines/scumm/insane/rebel2/iact.cpp
index a96388d0991..6a9cb134570 100644
--- a/engines/scumm/insane/rebel2/iact.cpp
+++ b/engines/scumm/insane/rebel2/iact.cpp
@@ -122,7 +122,10 @@ void InsaneRebel2::procPreRendering(byte *renderBitmap) {
 	// offset on frame 0 doesn't affect the STOR/FTCH mechanism.
 	if (_gameState == kStateChapterSelect && _player) {
 		if (renderBitmap) {
-			memset(renderBitmap, 0, _vm->_screenWidth * _vm->_screenHeight);
+			const int clearWidth = (_player->_width > 0) ? _player->_width : _vm->_screenWidth;
+			const int clearHeight = (_player->_height > 0) ? _player->_height : _vm->_screenHeight;
+			if (clearWidth > 0 && clearHeight > 0)
+				memset(renderBitmap, 0, (size_t)clearWidth * clearHeight);
 		}
 		_player->_fobjOffsetX = _previewOffsetX;
 		_player->_fobjOffsetY = _previewOffsetY;
diff --git a/engines/scumm/insane/rebel2/menu.cpp b/engines/scumm/insane/rebel2/menu.cpp
index b80ece9239a..2a17e4d0e4f 100644
--- a/engines/scumm/insane/rebel2/menu.cpp
+++ b/engines/scumm/insane/rebel2/menu.cpp
@@ -987,9 +987,12 @@ void InsaneRebel2::drawPreviewBox(byte *renderBitmap, int pitch, int width, int
 	// Low-res (320x200) coordinates from FUN_00415CF8:
 	// Outer box: X=0xe4 (228), Y=0x49 (73), W=0x54 (84), H=0x36 (54), color=0xF8
 	// Inner box: X=0xe5 (229), Y=0x4a (74), W=0x52 (82), H=0x34 (52), color=4
+	// High-res uses the original DAT_0047a808 >= 2 formulas, doubling the
+	// low-res anchors and dimensions.
+	const int scale = isHiRes() ? 2 : 1;
 
 	// Outer border (bright)
-	int outerX = 228, outerY = 73, outerW = 84, outerH = 54;
+	int outerX = 228 * scale, outerY = 73 * scale, outerW = 84 * scale, outerH = 54 * scale;
 	byte outerColor = 0xF8;
 
 	// Draw outer box edges
@@ -1021,7 +1024,7 @@ void InsaneRebel2::drawPreviewBox(byte *renderBitmap, int pitch, int width, int
 	}
 
 	// Inner border (dark)
-	int innerX = 229, innerY = 74, innerW = 82, innerH = 52;
+	int innerX = 229 * scale, innerY = 74 * scale, innerW = 82 * scale, innerH = 52 * scale;
 	byte innerColor = 4;
 
 	// Top edge
@@ -1115,6 +1118,8 @@ void InsaneRebel2::drawChapterInfoLine(byte *renderBitmap, int pitch, int width,
 	if (!splayer)
 		return;
 
+	const int scale = isHiRes() ? 2 : 1;
+
 	if (_chapterUnlocked[_chapterSelection]) {
 		// Unlocked: show score info using TRS 80 at X=25 (0x19), Y=190 (0xbe)
 		// TRS 80 = "^f01^c248Pilots: %hd  Score: %ld  Rank: ^f00%s"
@@ -1137,7 +1142,7 @@ void InsaneRebel2::drawChapterInfoLine(byte *renderBitmap, int pitch, int width,
 		Common::String displayStr = Common::String::format(fmtStr,
 			(short)pilotLives, (long)pilotScore, rankStr.c_str());
 
-		drawMenuString(renderBitmap, displayStr.c_str(), 25, 190);
+		drawMenuString(renderBitmap, displayStr.c_str(), 25 * scale, 190 * scale);
 	} else {
 		const char *lockStr = splayer->getString(81);
 		if (!lockStr || !lockStr[0])
@@ -1148,7 +1153,7 @@ void InsaneRebel2::drawChapterInfoLine(byte *renderBitmap, int pitch, int width,
 		Common::String displayStr = Common::String::format("%s ^c005%s%c",
 			lockStr, _passwordInput.c_str(), cursor);
 
-		drawMenuString(renderBitmap, displayStr.c_str(), 30, 190);
+		drawMenuString(renderBitmap, displayStr.c_str(), 30 * scale, 190 * scale);
 	}
 }
 
diff --git a/engines/scumm/smush/rebel/smush_player_ra2.cpp b/engines/scumm/smush/rebel/smush_player_ra2.cpp
index a4484f551c6..1f9e36a6834 100644
--- a/engines/scumm/smush/rebel/smush_player_ra2.cpp
+++ b/engines/scumm/smush/rebel/smush_player_ra2.cpp
@@ -1262,13 +1262,6 @@ void SmushPlayerRebel2::handleGameFrameObjectPost(int codec, const byte *data, i
 	}
 }
 
-void SmushPlayerRebel2::handleGameFrameObjectDecoded(int codec, int left, int top, int width, int height) {
-	if (!ra2IsHighResMode() || isRebel2GameplayActive(_insane))
-		return;
-
-	ra2PromoteCurrentFrameToHiRes(0, 0);
-}
-
 void SmushPlayerRebel2::handleGameFrameStart() {
 	_hasFrameFobjForGost = false;
 	_ra2NativeFrameNeedsClear = ((_curVideoFlags & 0x20) == 0);
@@ -1278,12 +1271,18 @@ void SmushPlayerRebel2::handleGameFrameStart() {
 		_ra2PendingAnimHeaderPalette = false;
 	}
 
-	if (ra2IsHighResMode() && isRebel2GameplayActive(_insane)) {
-		if (_ra2UsingGameplaySurface && _specialBuffer != nullptr &&
-				_specialBufferSize >= kRebel2GameplaySurfaceWidth * kRebel2GameplaySurfaceHeight) {
-			_dst = _specialBuffer;
-			_width = kRebel2GameplaySurfaceWidth;
-			_height = kRebel2GameplaySurfaceHeight;
+	if (ra2IsHighResMode()) {
+		if (isRebel2GameplayActive(_insane)) {
+			if (_ra2UsingGameplaySurface && _specialBuffer != nullptr &&
+					_specialBufferSize >= kRebel2GameplaySurfaceWidth * kRebel2GameplaySurfaceHeight) {
+				_dst = _specialBuffer;
+				_width = kRebel2GameplaySurfaceWidth;
+				_height = kRebel2GameplaySurfaceHeight;
+			} else if (ra2EnsureLowResVideoBuffer()) {
+				_dst = _ra2LowResVideoBuffer;
+				_width = 320;
+				_height = 200;
+			}
 		} else if (ra2EnsureLowResVideoBuffer()) {
 			_dst = _ra2LowResVideoBuffer;
 			_width = 320;
diff --git a/engines/scumm/smush/rebel/smush_player_ra2.h b/engines/scumm/smush/rebel/smush_player_ra2.h
index 86035fc6bc7..45a2869db2a 100644
--- a/engines/scumm/smush/rebel/smush_player_ra2.h
+++ b/engines/scumm/smush/rebel/smush_player_ra2.h
@@ -57,7 +57,6 @@ protected:
 	bool handleGameStoreFrame() override;
 	void handleGameFrameObjectPre(int codec, int left, int top, int width, int height, int dataSize) override;
 	void handleGameFrameObjectPost(int codec, const byte *data, int32 dataSize, int left, int top, int width, int height) override;
-	void handleGameFrameObjectDecoded(int codec, int left, int top, int width, int height) override;
 	void handleGameFrameStart() override;
 	bool handleGameSkipChunk(uint32 subType, int32 subSize, Common::SeekableReadStream &b) override;
 	void handleGameGost(int32 subSize, Common::SeekableReadStream &b) override;


Commit: eb0ce18ea57d48985649455cd80969aee4a08c7c
    https://github.com/scummvm/scummvm/commit/eb0ce18ea57d48985649455cd80969aee4a08c7c
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-09T21:14:12+02:00

Commit Message:
SCUMM: RA: make sure all the debug statements using channels

Changed paths:
    engines/scumm/insane/rebel/rebel_audio.cpp
    engines/scumm/insane/rebel1/iact.cpp
    engines/scumm/insane/rebel2/audio.cpp
    engines/scumm/insane/rebel2/iact.cpp
    engines/scumm/insane/rebel2/levels.cpp
    engines/scumm/insane/rebel2/menu.cpp
    engines/scumm/insane/rebel2/rebel.cpp
    engines/scumm/insane/rebel2/render.cpp
    engines/scumm/insane/rebel2/runlevels.cpp
    engines/scumm/smush/rebel/font_rebel2.cpp
    engines/scumm/smush/rebel/smush_player_ra1.cpp


diff --git a/engines/scumm/insane/rebel/rebel_audio.cpp b/engines/scumm/insane/rebel/rebel_audio.cpp
index b7856bcc978..9e04c4352b9 100644
--- a/engines/scumm/insane/rebel/rebel_audio.cpp
+++ b/engines/scumm/insane/rebel/rebel_audio.cpp
@@ -73,7 +73,7 @@ void RebelAudio::queueData(int trackIdx, const uint8 *data, int32 size, int volu
 	const int sourceRate = sampleRate > 0 ? sampleRate : _sampleRate;
 
 	if (!_streams[trackIdx]) {
-		debug(1, "RebelAudio: Creating audio stream for track %d at %d Hz", trackIdx, _sampleRate);
+		debugC(DEBUG_INSANE, "RebelAudio: Creating audio stream for track %d at %d Hz", trackIdx, _sampleRate);
 		_streams[trackIdx] = Audio::makeQueuingAudioStream(_sampleRate, false);
 		_trackActive[trackIdx] = true;
 		_vm->_mixer->playStream(Audio::Mixer::kSFXSoundType, &_handles[trackIdx],
diff --git a/engines/scumm/insane/rebel1/iact.cpp b/engines/scumm/insane/rebel1/iact.cpp
index 59c2e7b5861..dada01703ef 100644
--- a/engines/scumm/insane/rebel1/iact.cpp
+++ b/engines/scumm/insane/rebel1/iact.cpp
@@ -636,7 +636,7 @@ bool InsaneRebel1::handleFrameObjectTarget(int16 objectId, int16 left, int16 top
 		// replacement/death frame. This is used heavily by Level 9 troopers.
 		_frameObjectState[byteIndex] &= ~bit;
 		_frameObjectHitRevealPending = false;
-		debug(5, "RA1 FOBJ reveal: object=%d frameObjectByte=%d bit=0x%02x",
+		debugC(DEBUG_INSANE, "RA1 FOBJ reveal: object=%d frameObjectByte=%d bit=0x%02x",
 			objectId, byteIndex, bit);
 	}
 
@@ -1293,7 +1293,7 @@ void InsaneRebel1::updateShipPhysics() {
 	if (_currentLevel != 6)
 		checkDynamicLevelBranch();
 
-	debug(7, "RA1 ship: pos=(%d,%d) roll=%d lift=%d accX=%d accY=%d dir=%d health=%d corridor=[%d,%d]-[%d,%d]",
+	debugC(DEBUG_INSANE, "RA1 ship: pos=(%d,%d) roll=%d lift=%d accX=%d accY=%d dir=%d health=%d corridor=[%d,%d]-[%d,%d]",
 		_shipPosX, _shipPosY, _rollAccum, _liftSmooth,
 		_posAccumX, _posAccumY, _shipDirIndex, _health,
 		_corridorLeftX, _corridorTopY, _corridorRightX, _corridorBottomY);
@@ -1858,7 +1858,7 @@ void InsaneRebel1::updateGameOp0BPhysics() {
 
 	checkDynamicLevelBranch();
 
-	debug(7, "RA1 GAME 0x0B: pos=(%d,%d) avg=(%d,%d) view=(%d,%d) health=%d flash=%d",
+	debugC(DEBUG_INSANE, "RA1 GAME 0x0B: pos=(%d,%d) avg=(%d,%d) view=(%d,%d) health=%d flash=%d",
 		_shipPosX, _shipPosY, _avgInputX, _avgInputY,
 		_perspectiveX, _perspectiveY, _health, _screenFlash);
 }
@@ -2103,7 +2103,7 @@ void InsaneRebel1::handleGameOpcode5EReset(uint32 param1) {
 	// RA1 dispatcher inline reset/init path (FUN_1BE1B case 0x5E).
 	// This is not a pure control-mode assignment.
 	if (_frameDispatchFlags & 0x40) {
-		debug(7, "RA1 GAME 0x5E: reset suppressed by dispatch flags=0x%02x",
+		debugC(DEBUG_INSANE, "RA1 GAME 0x5E: reset suppressed by dispatch flags=0x%02x",
 			_frameDispatchFlags);
 		return;
 	}
@@ -2177,7 +2177,7 @@ void InsaneRebel1::handleGameOpcode5EReset(uint32 param1) {
 	if (_currentLevel == 7)
 		memset(_frameObjectState + 150, 0xFF, 150);
 
-	debug(5, "RA1 GAME 0x5E: reset state field1=%d mode=%d", (int32)param1, (int)_flyControlMode);
+	debugC(DEBUG_INSANE, "RA1 GAME 0x5E: reset state field1=%d mode=%d", (int32)param1, (int)_flyControlMode);
 }
 
 void InsaneRebel1::handleGameOpcode5DLinkLatch(uint32 param1) {
@@ -2192,7 +2192,7 @@ void InsaneRebel1::handleGameOpcode5DLinkLatch(uint32 param1) {
 		}
 	}
 
-	debug(5, "RA1 GAME 0x5D (link/event latch) param=%u", _gameLatch5D);
+	debugC(DEBUG_INSANE, "RA1 GAME 0x5D (link/event latch) param=%u", _gameLatch5D);
 }
 
 void InsaneRebel1::handleGameOpcode5FRandomHitLatch(uint32 param1) {
@@ -2205,7 +2205,7 @@ void InsaneRebel1::handleGameOpcode5FRandomHitLatch(uint32 param1) {
 		}
 	}
 
-	debug(5, "RA1 GAME 0x5F (random-hit latch) param=%u", _gameLatch5F);
+	debugC(DEBUG_INSANE, "RA1 GAME 0x5F (random-hit latch) param=%u", _gameLatch5F);
 }
 
 void InsaneRebel1::handleGameOpcode07ShipFlight(int32 subSize, Common::SeekableReadStream &b, uint32 param1) {
@@ -2219,7 +2219,7 @@ void InsaneRebel1::handleGameOpcode07ShipFlight(int32 subSize, Common::SeekableR
 		b.readUint32BE(); // f2 (max frames, unused in physics)
 		_driftParam = (int16)(int32)b.readUint32BE();
 		b.readUint32BE(); // f4 (unused in original assembly)
-		debug(7, "RA1 GAME 0x07: counter=%d driftParam=%d", _gameCounter, _driftParam);
+		debugC(DEBUG_INSANE, "RA1 GAME 0x07: counter=%d driftParam=%d", _gameCounter, _driftParam);
 	}
 }
 
@@ -2291,7 +2291,7 @@ void InsaneRebel1::handleGameOpcode0DCorridor(int32 subSize, Common::SeekableRea
 			_damageFlags, suppressDirectionalDamage ? 1 : 0);
 	}
 
-	debug(5, "RA1 GAME 0x0D: raw=[%d,%d]+(%d,%d) cam=(%d,%d) transformed=[%d,%d]-[%d,%d]",
+	debugC(DEBUG_INSANE, "RA1 GAME 0x0D: raw=[%d,%d]+(%d,%d) cam=(%d,%d) transformed=[%d,%d]-[%d,%d]",
 		corridorLeft, corridorTop, corridorWidth, corridorHeight,
 		_perspectiveX, _perspectiveY,
 		_corridorLeftX, _corridorTopY, _corridorRightX, _corridorBottomY);
@@ -2332,7 +2332,7 @@ void InsaneRebel1::handleGameOpcode0EZone(int32 subSize, Common::SeekableReadStr
 			rawZoneLeft, rawZoneTop, zoneWidth, zoneHeight,
 			_perspectiveX, _perspectiveY, _damageFlags);
 	}
-	debug(7, "RA1 GAME 0x0E: ship=(%d,%d) zone=[%d,%d]-[%d,%d] cam=(%d,%d) flags=0x%02x",
+	debugC(DEBUG_INSANE, "RA1 GAME 0x0E: ship=(%d,%d) zone=[%d,%d]-[%d,%d] cam=(%d,%d) flags=0x%02x",
 		collisionShipX, collisionShipY, zoneLeft, zoneTop, zoneRight, zoneBottom,
 		_perspectiveX, _perspectiveY, _damageFlags);
 }
@@ -2356,7 +2356,7 @@ void InsaneRebel1::handleGameOpcode0BFirstPerson(int32 subSize, Common::Seekable
 			debugC(DEBUG_INSANE, "RA1: finishing 0x0B interactive video at counter=%d/%u", _gameCounter, maxFrames);
 		}
 	}
-	debug(7, "RA1 GAME 0x0B: counter=%d", _gameCounter);
+	debugC(DEBUG_INSANE, "RA1 GAME 0x0B: counter=%d", _gameCounter);
 	if (!_gameOp0BPhysicsUpdatedThisFrame) {
 		updateGameOp0BPhysics();
 		_gameOp0BPhysicsUpdatedThisFrame = true;
@@ -2396,7 +2396,7 @@ void InsaneRebel1::handleGameOpcode5ATarget(int32 subSize, Common::SeekableReadS
 			checkTargetHit(targetIdx, left, top, right, bottom);
 		}
 	}
-	debug(5, "RA1 GAME 0x5A: target=%d rect=[%d,%d]-[%d,%d] prox=%d",
+	debugC(DEBUG_INSANE, "RA1 GAME 0x5A: target=%d rect=[%d,%d]-[%d,%d] prox=%d",
 		targetIdx, left, top, right, bottom, _targetProximity);
 }
 
@@ -2412,7 +2412,7 @@ void InsaneRebel1::handleGameCounterOpcode(uint32 opcode, int32 subSize, Common:
 			debugC(DEBUG_INSANE, "RA1 GAME 0x09: counter=%d params=(%d,%d,%d) opcodeMask=0x%08x",
 				_gameCounter, param2, param3, param4, _frameGameOpcodeMask);
 		} else {
-			debug(5, "RA1 GAME 0x%02x: counter=%d params=(%d,%d,%d)",
+			debugC(DEBUG_INSANE, "RA1 GAME 0x%02x: counter=%d params=(%d,%d,%d)",
 				opcode, _gameCounter, param2, param3, param4);
 		}
 	}
@@ -2433,14 +2433,14 @@ void InsaneRebel1::handleGameChunk(int32 subSize, Common::SeekableReadStream &b,
 	// g_combatModeFlags skips gameplay dispatch entirely; bit 5 of g_hudDisableFlags
 	// suppresses the handlers while still requesting HUD refresh for a few opcodes.
 	if (_gameplayFlags75ff & 1) {
-		debug(7, "RA1 GAME 0x%02x: skipped by combat mode flags=0x%02x",
+		debugC(DEBUG_INSANE, "RA1 GAME 0x%02x: skipped by combat mode flags=0x%02x",
 			opcode, _gameplayFlags75ff);
 		return;
 	}
 	if (_gameplayFlags75fe & 0x20) {
 		if (ra1DispatcherHudOnlyWhenDisabled(opcode))
 			_hudRenderFlag = 0xFF;
-		debug(7, "RA1 GAME 0x%02x: skipped by HUD disable flags=0x%02x",
+		debugC(DEBUG_INSANE, "RA1 GAME 0x%02x: skipped by HUD disable flags=0x%02x",
 			opcode, _gameplayFlags75fe);
 		return;
 	}
@@ -2488,7 +2488,7 @@ void InsaneRebel1::handleGameChunk(int32 subSize, Common::SeekableReadStream &b,
 		break;
 
 	default:
-		debug(7, "RA1 GAME unknown 0x%02x size=%d", opcode, subSize);
+		debugC(DEBUG_INSANE, "RA1 GAME unknown 0x%02x size=%d", opcode, subSize);
 		break;
 	}
 }
@@ -2566,7 +2566,7 @@ void InsaneRebel1::processShot() {
 			effectiveOpcode, _gameCounter, slot, cursorX, cursorY, originX, originY,
 			_shipDirIndex, _flyControlMode);
 	} else {
-		debug(5, "RA1 shot: slot=%d pos=(%d,%d) origin=(%d,%d)", slot,
+		debugC(DEBUG_INSANE, "RA1 shot: slot=%d pos=(%d,%d) origin=(%d,%d)", slot,
 			cursorX, cursorY, originX, originY);
 	}
 }
@@ -2678,7 +2678,7 @@ void InsaneRebel1::checkTargetHit(int16 targetIdx, int16 left, int16 top, int16
 						if ((_gameplayFlags75fe & 0x10) == 0)
 							playSfx(kSfxExplode, 127, sfxPan);
 
-						debug(3, "RA1 HIT: target=%d gost=%d pos=(%d,%d) score=%d kills=%d bangSprites=%d",
+						debugC(DEBUG_INSANE, "RA1 HIT: target=%d gost=%d pos=(%d,%d) score=%d kills=%d bangSprites=%d",
 							targetIdx, gi, _gostSlots[gi].posX, _gostSlots[gi].posY,
 							_score, _killCount, _bangBank.numSprites);
 						return;
diff --git a/engines/scumm/insane/rebel2/audio.cpp b/engines/scumm/insane/rebel2/audio.cpp
index 989d93074ef..3001e4c4908 100644
--- a/engines/scumm/insane/rebel2/audio.cpp
+++ b/engines/scumm/insane/rebel2/audio.cpp
@@ -96,7 +96,7 @@ void InsaneRebel2::loadSfx() {
 		ScummFile *file = _vm->instantiateScummFile();
 		_vm->openFile(*file, kRA2SfxFiles[i]);
 		if (!file->isOpen()) {
-			debug("InsaneRebel2::loadSfx: Could not open %s", kRA2SfxFiles[i]);
+			debugC(DEBUG_INSANE, "InsaneRebel2::loadSfx: Could not open %s", kRA2SfxFiles[i]);
 			delete file;
 			continue;
 		}
@@ -105,7 +105,7 @@ void InsaneRebel2::loadSfx() {
 		// We scan for the SDAT tag to find the PCM data.
 		uint32 fileSize = file->size();
 		if (fileSize < 38) {  // Minimum: 8 (SAUD) + 22 (STRK) + 8 (SDAT header)
-			debug("InsaneRebel2::loadSfx: %s too small (%d bytes)", kRA2SfxFiles[i], fileSize);
+			debugC(DEBUG_INSANE, "InsaneRebel2::loadSfx: %s too small (%d bytes)", kRA2SfxFiles[i], fileSize);
 			file->close();
 			delete file;
 			continue;
@@ -114,7 +114,7 @@ void InsaneRebel2::loadSfx() {
 		// Verify SAUD tag
 		uint32 tag = file->readUint32BE();
 		if (tag != MKTAG('S', 'A', 'U', 'D')) {
-			debug("InsaneRebel2::loadSfx: %s not a SAUD file (tag=0x%08x)", kRA2SfxFiles[i], tag);
+			debugC(DEBUG_INSANE, "InsaneRebel2::loadSfx: %s not a SAUD file (tag=0x%08x)", kRA2SfxFiles[i], tag);
 			file->close();
 			delete file;
 			continue;
@@ -134,7 +134,7 @@ void InsaneRebel2::loadSfx() {
 				if (_sfxData[i]) {
 					file->read(_sfxData[i], pcmSize);
 					_sfxSize[i] = pcmSize;
-					debug("InsaneRebel2::loadSfx: Loaded %s (%d bytes PCM)", kRA2SfxFiles[i], pcmSize);
+					debugC(DEBUG_INSANE, "InsaneRebel2::loadSfx: Loaded %s (%d bytes PCM)", kRA2SfxFiles[i], pcmSize);
 				}
 				foundSdat = true;
 				break;
@@ -145,7 +145,7 @@ void InsaneRebel2::loadSfx() {
 		}
 
 		if (!foundSdat) {
-			debug("InsaneRebel2::loadSfx: No SDAT chunk in %s", kRA2SfxFiles[i]);
+			debugC(DEBUG_INSANE, "InsaneRebel2::loadSfx: No SDAT chunk in %s", kRA2SfxFiles[i]);
 		}
 
 		file->close();
@@ -196,7 +196,7 @@ void InsaneRebel2::playSfx(int slot, int volume, int pan) {
 	_vm->_mixer->playStream(Audio::Mixer::kSFXSoundType, &_sfxHandles[slot],
 		stream, -1, scaledVolume, pan);
 
-	debug(5, "InsaneRebel2::playSfx: slot=%d vol=%d pan=%d size=%d", slot, volume, pan, _sfxSize[slot]);
+	debugC(DEBUG_INSANE, "InsaneRebel2::playSfx: slot=%d vol=%d pan=%d size=%d", slot, volume, pan, _sfxSize[slot]);
 }
 
 // loadAuxSfx -- Load sound data into auxiliary buffer (FUN_004118df).
@@ -205,7 +205,7 @@ void InsaneRebel2::loadAuxSfx(int buffer, const byte *data, uint32 size) {
 		return;
 	}
 	if ((int)size > kRA2AuxBufSize) {
-		debug("InsaneRebel2::loadAuxSfx: buffer %d size %d exceeds max %d, truncating",
+		debugC(DEBUG_INSANE, "InsaneRebel2::loadAuxSfx: buffer %d size %d exceeds max %d, truncating",
 			buffer, size, kRA2AuxBufSize);
 		size = kRA2AuxBufSize;
 	}
@@ -213,7 +213,7 @@ void InsaneRebel2::loadAuxSfx(int buffer, const byte *data, uint32 size) {
 	memcpy(_auxSfxData[buffer], data, size);
 	_auxSfxSize[buffer] = size;
 
-	debug(5, "InsaneRebel2::loadAuxSfx: buffer=%d size=%d", buffer, size);
+	debugC(DEBUG_INSANE, "InsaneRebel2::loadAuxSfx: buffer=%d size=%d", buffer, size);
 }
 
 // playAuxSfx -- Play from auxiliary buffer (FUN_00411931).
@@ -260,7 +260,7 @@ void InsaneRebel2::playAuxSfx(int buffer, int volume, int pan) {
 	_vm->_mixer->playStream(Audio::Mixer::kSFXSoundType, &_auxSfxHandles[buffer],
 		stream, -1, scaledVolume, pan);
 
-	debug(5, "InsaneRebel2::playAuxSfx: buffer=%d vol=%d pan=%d pcmSize=%d", buffer, volume, pan, pcmSize);
+	debugC(DEBUG_INSANE, "InsaneRebel2::playAuxSfx: buffer=%d vol=%d pan=%d pcmSize=%d", buffer, volume, pan, pcmSize);
 }
 
 } // End of namespace Scumm
diff --git a/engines/scumm/insane/rebel2/iact.cpp b/engines/scumm/insane/rebel2/iact.cpp
index 6a9cb134570..d1e34b8355a 100644
--- a/engines/scumm/insane/rebel2/iact.cpp
+++ b/engines/scumm/insane/rebel2/iact.cpp
@@ -49,7 +49,7 @@ static bool readLevel2BackgroundChunkHeader(Common::SeekableReadStream &stream,
 
 	const int64 dataStart = stream.pos();
 	if ((int64)chunkSize > containerEnd - dataStart) {
-		debug("Rebel2 loadLevel2Background: Truncated %s chunk 0x%08X at %lld: size=%u, remaining=%lld",
+		debugC(DEBUG_INSANE, "Rebel2 loadLevel2Background: Truncated %s chunk 0x%08X at %lld: size=%u, remaining=%lld",
 			context, tag, headerPos, chunkSize, containerEnd - dataStart);
 		return false;
 	}
@@ -136,7 +136,7 @@ void InsaneRebel2::procPreRendering(byte *renderBitmap) {
 void InsaneRebel2::procIACT(byte *renderBitmap, int32 codecparam, int32 setupsan12,
 					  int32 setupsan13, Common::SeekableReadStream &b, int32 size, int32 flags,
 					  int16 par1, int16 par2, int16 par3, int16 par4) {
-	debug("Rebel2 IACT: opcode=%d par2=%d par3=%d par4=%d gameState=%d sceneId=%d",
+	debugC(DEBUG_INSANE, "Rebel2 IACT: opcode=%d par2=%d par3=%d par4=%d gameState=%d sceneId=%d",
 		par1, par2, par3, par4, _gameState, _currSceneId);
 
 	if (_keyboardDisable)
@@ -146,12 +146,12 @@ void InsaneRebel2::procIACT(byte *renderBitmap, int32 codecparam, int32 setupsan
 	// Menu IACTs have par1=8 (code), par2=46 (flags), par4>=1000 (userId)
 	// The embedded ANIM contains the full menu frame
 	if (_gameState == kStateMainMenu && par1 == 8 && par4 >= 1000) {
-		debug("Rebel2 IACT: Menu mode - processing embedded ANIM (userId=%d)", par4);
+		debugC(DEBUG_INSANE, "Rebel2 IACT: Menu mode - processing embedded ANIM (userId=%d)", par4);
 
 		// Scan for embedded ANIM tag in the IACT data
 		int64 startPos = b.pos();
 		int64 totalSize = b.size();
-		debug("Rebel2 IACT: stream pos=%d, size=%d, remaining=%d",
+		debugC(DEBUG_INSANE, "Rebel2 IACT: stream pos=%d, size=%d, remaining=%d",
 			(int)startPos, (int)totalSize, (int)(totalSize - startPos));
 
 		if (totalSize > startPos) {
@@ -160,7 +160,7 @@ void InsaneRebel2::procIACT(byte *renderBitmap, int32 codecparam, int32 setupsan
 			byte *scanBuf = (byte *)malloc(scanSize);
 			if (scanBuf) {
 				int bytesRead = b.read(scanBuf, scanSize);
-				debug("Rebel2 IACT: Read %d bytes, first 16: %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X",
+				debugC(DEBUG_INSANE, "Rebel2 IACT: Read %d bytes, first 16: %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X",
 					bytesRead, scanBuf[0], scanBuf[1], scanBuf[2], scanBuf[3],
 					scanBuf[4], scanBuf[5], scanBuf[6], scanBuf[7],
 					scanBuf[8], scanBuf[9], scanBuf[10], scanBuf[11],
@@ -172,7 +172,7 @@ void InsaneRebel2::procIACT(byte *renderBitmap, int32 codecparam, int32 setupsan
 						int64 animStreamPos = startPos + i;
 						uint32 animReportedSize = READ_BE_UINT32(scanBuf + i + 4);
 						int32 toCopy = (int)MIN<int64>((int64)animReportedSize + 8, totalSize - animStreamPos);
-						debug("Rebel2 IACT: Found embedded ANIM at offset %d, size %d", (int)i, (int)animReportedSize);
+						debugC(DEBUG_INSANE, "Rebel2 IACT: Found embedded ANIM at offset %d, size %d", (int)i, (int)animReportedSize);
 						if (toCopy > 0) {
 							byte *animData = (byte *)malloc(toCopy);
 							if (animData) {
@@ -189,7 +189,7 @@ void InsaneRebel2::procIACT(byte *renderBitmap, int32 codecparam, int32 setupsan
 					}
 				}
 
-				debug("Rebel2 IACT: No ANIM tag found in menu IACT data");
+				debugC(DEBUG_INSANE, "Rebel2 IACT: No ANIM tag found in menu IACT data");
 				b.seek(startPos);
 				free(scanBuf);
 			}
@@ -243,7 +243,7 @@ void InsaneRebel2::iactRebel2Scene1(byte *renderBitmap, int32 codecparam, int32
 		// Sub-opcode 0x0D (13) = Primary collision zones (obstacles)
 		// Sub-opcode 0x0E (14) = Secondary collision zones (boundaries)
 		// par2 is the sub-opcode that determines which zone table to use
-		debug("Rebel2 IACT Opcode 5: par2=%d par3=%d par4=%d", par2, par3, par4);
+		debugC(DEBUG_INSANE, "Rebel2 IACT Opcode 5: par2=%d par3=%d par4=%d", par2, par3, par4);
 
 		if (par2 == 0x0D || par2 == 0x0E) {
 			// Register the collision zone from the remaining IACT data
@@ -275,7 +275,7 @@ void InsaneRebel2::iactRebel2Scene1(byte *renderBitmap, int32 codecparam, int32
 			// DAT_00443b12 = horizontal wind, DAT_00443b14 = vertical wind
 			_windParamX = body0;
 			_windParamY = body1;
-			debug("Rebel2 Opcode 7 par4=0: wind=(%d,%d)", body0, body1);
+			debugC(DEBUG_INSANE, "Rebel2 Opcode 7 par4=0: wind=(%d,%d)", body0, body1);
 			break;
 		case 1:
 			// Set LEFT X boundary and TOP Y boundary
@@ -287,7 +287,7 @@ void InsaneRebel2::iactRebel2Scene1(byte *renderBitmap, int32 codecparam, int32
 			} else if (_flyControlMode == 0) {
 				_corridorLeftX += 20;
 			}
-			debug("Rebel2 Opcode 7 par4=1: corridor left=%d top=%d (adjusted left=%d)",
+			debugC(DEBUG_INSANE, "Rebel2 Opcode 7 par4=1: corridor left=%d top=%d (adjusted left=%d)",
 				body0, body1, _corridorLeftX);
 			break;
 		case 2:
@@ -300,16 +300,16 @@ void InsaneRebel2::iactRebel2Scene1(byte *renderBitmap, int32 codecparam, int32
 			} else if (_flyControlMode == 0) {
 				_corridorRightX -= 20;
 			}
-			debug("Rebel2 Opcode 7 par4=2: corridor right=%d bottom=%d (adjusted right=%d)",
+			debugC(DEBUG_INSANE, "Rebel2 Opcode 7 par4=2: corridor right=%d bottom=%d (adjusted right=%d)",
 				body0, body1, _corridorRightX);
 			break;
 		case 5:
 			// DAT_00443b52: repeats FLY002 ship overlay in FUN_40D836.
 			_flyOverlayRepeatCount = body0;
-			debug("Rebel2 Opcode 7 par4=5: flyOverlayRepeat=%d", _flyOverlayRepeatCount);
+			debugC(DEBUG_INSANE, "Rebel2 Opcode 7 par4=5: flyOverlayRepeat=%d", _flyOverlayRepeatCount);
 			break;
 		default:
-			debug("Rebel2 Opcode 7 par4=%d: body=(%d,%d) ??? unknown sub-opcode", par4, body0, body1);
+			debugC(DEBUG_INSANE, "Rebel2 Opcode 7 par4=%d: body=(%d,%d) ??? unknown sub-opcode", par4, body0, body1);
 			break;
 		}
 
@@ -324,9 +324,9 @@ void InsaneRebel2::iactRebel2Scene1(byte *renderBitmap, int32 codecparam, int32
 		iactRebel2Opcode9(renderBitmap, b, par2, par3, par4);
 	} else if (par1 == 0 || par1 == 1) {
 		// Low Opcodes seen in logs
-		debug("Rebel2 IACT: Low Opcode %d (par2=%d par3=%d par4=%d)", par1, par2, par3, par4);
+		debugC(DEBUG_INSANE, "Rebel2 IACT: Low Opcode %d (par2=%d par3=%d par4=%d)", par1, par2, par3, par4);
 	} else {
-		debug("Rebel2 IACT: Unknown Opcode %d (par2=%d par3=%d par4=%d)", par1, par2, par3, par4);
+		debugC(DEBUG_INSANE, "Rebel2 IACT: Unknown Opcode %d (par2=%d par3=%d par4=%d)", par1, par2, par3, par4);
 	}
 }
 
@@ -359,13 +359,13 @@ void InsaneRebel2::iactRebel2Opcode2(Common::SeekableReadStream &b, int16 par2,
 			// while the parent is alive, and revealed when the parent is destroyed.
 			if (!isBitSet(parentId)) {
 				setBit(childId);
-				debug("Rebel2: Linked ID=%d to Parent=%d (Slot 0) - child DISABLED (parent alive)", childId, parentId);
+				debugC(DEBUG_INSANE, "Rebel2: Linked ID=%d to Parent=%d (Slot 0) - child DISABLED (parent alive)", childId, parentId);
 			} else {
 				clearBit(childId);
-				debug("Rebel2: Linked ID=%d to Parent=%d (Slot 0) - child ENABLED (parent dead)", childId, parentId);
+				debugC(DEBUG_INSANE, "Rebel2: Linked ID=%d to Parent=%d (Slot 0) - child ENABLED (parent dead)", childId, parentId);
 			}
 		} else {
-			debug("Rebel2: Skipping link with invalid IDs childId=%d parentId=%d", childId, parentId);
+			debugC(DEBUG_INSANE, "Rebel2: Skipping link with invalid IDs childId=%d parentId=%d", childId, parentId);
 		}
 		return;
 	} else if (par3 == 1) { // Probabilistic / counter cases: par3 == 1
@@ -385,13 +385,13 @@ void InsaneRebel2::iactRebel2Opcode2(Common::SeekableReadStream &b, int16 par2,
 			if (value == 100) {
 				// par4==100: Force enable the target (original: FUN_00423a00)
 				clearBit(targetId);
-				debug("Rebel2 Opcode2 (H%d): Force ENABLE target=%d (par4=100)", _rebelHandler, targetId);
+				debugC(DEBUG_INSANE, "Rebel2 Opcode2 (H%d): Force ENABLE target=%d (par4=100)", _rebelHandler, targetId);
 			} else {
 				// Check wave state: if enemy type has been killed, disable target
 				int bitMask = 1 << (value & 0x1f);
 				if ((_rebelWaveState & bitMask) != 0) {
 					setBit(targetId);
-					debug("Rebel2 Opcode2 (H%d): Disable target=%d (type %d killed, wave=0x%x)", _rebelHandler, targetId, value, _rebelWaveState);
+					debugC(DEBUG_INSANE, "Rebel2 Opcode2 (H%d): Disable target=%d (type %d killed, wave=0x%x)", _rebelHandler, targetId, value, _rebelWaveState);
 				}
 			}
 			return;
@@ -400,19 +400,19 @@ void InsaneRebel2::iactRebel2Opcode2(Common::SeekableReadStream &b, int16 par2,
 		if (value > 1 && value < 10) { // 1 < value < 10: random disable
 			if (_vm->_rnd.getRandomNumber(value) == 0) {
 				setBit(targetId);
-				debug("Rebel2 IACT Opcode2: Random DISABLE target=%d (value=%d)", targetId, value);
+				debugC(DEBUG_INSANE, "Rebel2 IACT Opcode2: Random DISABLE target=%d (value=%d)", targetId, value);
 			}
 		} else if (value > 10 && value < 20) { // 10 < value < 20: enable/disable with special value==11 = force enable
 			if (value == 11) {
 				clearBit(targetId);
-				debug("Rebel2 IACT Opcode2: FORCE ENABLE target=%d (value=11)", targetId);
+				debugC(DEBUG_INSANE, "Rebel2 IACT Opcode2: FORCE ENABLE target=%d (value=11)", targetId);
 			} else {
 				if (_vm->_rnd.getRandomNumber(value - 10) == 0) {
 					clearBit(targetId);
-					debug("Rebel2 IACT Opcode2: Random ENABLE target=%d (value=%d)", targetId, value);
+					debugC(DEBUG_INSANE, "Rebel2 IACT Opcode2: Random ENABLE target=%d (value=%d)", targetId, value);
 				} else {
 					setBit(targetId);
-					debug("Rebel2 IACT Opcode2: Random DISABLE target=%d (value=%d)", targetId, value);
+					debugC(DEBUG_INSANE, "Rebel2 IACT Opcode2: Random DISABLE target=%d (value=%d)", targetId, value);
 				}
 			}
 		} else if (value > 99 && value < 110) { // 99 < value < 110: increment value counter if target active
@@ -421,7 +421,7 @@ void InsaneRebel2::iactRebel2Opcode2(Common::SeekableReadStream &b, int16 par2,
 				if (idx >= 0 && idx < 10) {
 					_rebelValueCounters[idx]++;
 					_rebelLastCounter = _rebelValueCounters[idx];
-					debug("Rebel2 IACT Opcode2: Increment VAL counter[%d] -> %d (target=%d)", value, _rebelValueCounters[idx], targetId);
+					debugC(DEBUG_INSANE, "Rebel2 IACT Opcode2: Increment VAL counter[%d] -> %d (target=%d)", value, _rebelValueCounters[idx], targetId);
 				}
 			}
 
@@ -431,14 +431,14 @@ void InsaneRebel2::iactRebel2Opcode2(Common::SeekableReadStream &b, int16 par2,
 					if (!isBitSet(targetId)) {
 						_rebelMaskCounters[slot]++;
 						_rebelLastCounter = _rebelMaskCounters[slot];
-						debug("Rebel2 IACT Opcode2: Increment MASK counter[%d] -> %d (target=%d)", slot, _rebelMaskCounters[slot], targetId);
+						debugC(DEBUG_INSANE, "Rebel2 IACT Opcode2: Increment MASK counter[%d] -> %d (target=%d)", slot, _rebelMaskCounters[slot], targetId);
 					}
 				}
 			}
 		}
 
 		// Unknown sub-type: log and return
-		debug("Rebel2 IACT Opcode2: Unhandled par3=%d par4=%d", par3, par4);
+		debugC(DEBUG_INSANE, "Rebel2 IACT Opcode2: Unhandled par3=%d par4=%d", par3, par4);
 	}
 }
 
@@ -477,35 +477,35 @@ void InsaneRebel2::iactRebel2Opcode3(Common::SeekableReadStream &b, int16 par2,
 
 		if (par3 == 5) {
 			// Probabilistic damage with cover check (lines 81-92)
-			debug("Rebel2 Opcode3: H25 par3=5 srcId=%d isBitSet=%d damageLevel=%d",
+			debugC(DEBUG_INSANE, "Rebel2 Opcode3: H25 par3=5 srcId=%d isBitSet=%d damageLevel=%d",
 				srcIdBody1, isBitSet(srcIdBody1), _rebelDamageLevel);
 
 			if (_rebelDamageLevel < 2 && !isBitSet(srcIdBody1)) {
 				LevelDifficultyParams params = getDifficultyParams();
 				int probability = (params.shotAccuracy >= 0) ? params.shotAccuracy : 0;
 				int roll = _vm->_rnd.getRandomNumber(99);
-				debug("Rebel2 Opcode3: probability=%d roll=%d (need roll < prob)", probability, roll);
+				debugC(DEBUG_INSANE, "Rebel2 Opcode3: probability=%d roll=%d (need roll < prob)", probability, roll);
 
 				if (roll < probability) {
 					int damageAmount = (params.shotDamage >= 0) ? params.shotDamage : 0;
 					if (applyPlayerDamage(damageAmount)) {
-						debug("Rebel2: H25 PROBABILISTIC damage from %d. Damage=%d total=%d",
+						debugC(DEBUG_INSANE, "Rebel2: H25 PROBABILISTIC damage from %d. Damage=%d total=%d",
 							srcIdBody1, damageAmount, _playerDamage);
 					}
 					if (!_noDamage)
 						initDamageFlash();
 				}
 			} else {
-				debug("Rebel2 Opcode3: H25 par3=5 BLOCKED (damageLevel=%d isBitSet=%d)",
+				debugC(DEBUG_INSANE, "Rebel2 Opcode3: H25 par3=5 BLOCKED (damageLevel=%d isBitSet=%d)",
 					_rebelDamageLevel, isBitSet(srcIdBody1));
 			}
 		} else if (par3 == 1 && !isBitSet(srcIdBody0) && par4 != 4) {
 			// Hit counter only ??? NO damage (lines 94-98)
 			_rebelHitCounter++;
-			debug("Rebel2: H25 hit counter++ -> %d (par3=1 par4=%d, no damage)",
+			debugC(DEBUG_INSANE, "Rebel2: H25 hit counter++ -> %d (par3=1 par4=%d, no damage)",
 				_rebelHitCounter, par4);
 		} else {
-			debug("Rebel2 Opcode3: H25 par3=%d par4=%d (no action)", par3, par4);
+			debugC(DEBUG_INSANE, "Rebel2 Opcode3: H25 par3=%d par4=%d (no action)", par3, par4);
 		}
 
 		// Direct damage: par4==100, separate from par3 branches (lines 99-111)
@@ -513,7 +513,7 @@ void InsaneRebel2::iactRebel2Opcode3(Common::SeekableReadStream &b, int16 par2,
 			LevelDifficultyParams dparams = getDifficultyParams();
 			int directHitDamage = (dparams.missDamage >= 0) ? dparams.missDamage : 0;
 			if (applyPlayerDamage(directHitDamage)) {
-				debug("Rebel2: H25 DIRECT HIT par4=100 damage=%d total=%d",
+				debugC(DEBUG_INSANE, "Rebel2: H25 DIRECT HIT par4=100 damage=%d total=%d",
 					directHitDamage, _playerDamage);
 			}
 			if (!_noDamage)
@@ -523,12 +523,12 @@ void InsaneRebel2::iactRebel2Opcode3(Common::SeekableReadStream &b, int16 par2,
 		// Non-Handler-25 direct hit path ??? FUN_4092D9 lines 209-227
 		int16 srcId = b.readSint16LE(); // body[0] (offset +8): source enemy ID
 
-		debug("Rebel2 Opcode3: par3=%d par4=%d srcId=%d isBitSet=%d",
+		debugC(DEBUG_INSANE, "Rebel2 Opcode3: par3=%d par4=%d srcId=%d isBitSet=%d",
 			par3, par4, srcId, isBitSet(srcId));
 
 		if (!isBitSet(srcId)) {
 			_rebelHitCounter++;
-			debug("Rebel2: Incremented hit counter -> %d", _rebelHitCounter);
+			debugC(DEBUG_INSANE, "Rebel2: Incremented hit counter -> %d", _rebelHitCounter);
 
 			LevelDifficultyParams dparams = getDifficultyParams();
 			int directHitDamage = (dparams.missDamage >= 0) ? dparams.missDamage : 0;
@@ -546,7 +546,7 @@ void InsaneRebel2::iactRebel2Opcode3(Common::SeekableReadStream &b, int16 par2,
 
 				if (shouldDamage) {
 					if (applyPlayerDamage(directHitDamage)) {
-						debug("Rebel2: DIRECT HIT damage from enemy %d. par3=%d par4=%d damage=%d total=%d",
+						debugC(DEBUG_INSANE, "Rebel2: DIRECT HIT damage from enemy %d. par3=%d par4=%d damage=%d total=%d",
 							srcId, par3, par4, directHitDamage, _playerDamage);
 					}
 					if (!_noDamage)
@@ -559,19 +559,19 @@ void InsaneRebel2::iactRebel2Opcode3(Common::SeekableReadStream &b, int16 par2,
 		b.skip(2); // Skip body[0]
 		int16 srcId = b.readSint16LE(); // body[1] (offset +10)
 
-		debug("Rebel2 Opcode3: par3=5 srcId=%d isBitSet=%d", srcId, isBitSet(srcId));
+		debugC(DEBUG_INSANE, "Rebel2 Opcode3: par3=5 srcId=%d isBitSet=%d", srcId, isBitSet(srcId));
 
 		if (!isBitSet(srcId)) {
 			LevelDifficultyParams params = getDifficultyParams();
 			int probability = (params.shotAccuracy >= 0) ? params.shotAccuracy : 0;
 
 			int roll = _vm->_rnd.getRandomNumber(99);
-			debug("Rebel2 Opcode3: probability=%d roll=%d (need roll < prob)", probability, roll);
+			debugC(DEBUG_INSANE, "Rebel2 Opcode3: probability=%d roll=%d (need roll < prob)", probability, roll);
 
 			if (roll < probability) {
 				int damageAmount = (params.shotDamage >= 0) ? params.shotDamage : 0;
 				if (applyPlayerDamage(damageAmount)) {
-					debug("Rebel2: PROBABILISTIC damage from enemy %d. Damage=%d total=%d",
+					debugC(DEBUG_INSANE, "Rebel2: PROBABILISTIC damage from enemy %d. Damage=%d total=%d",
 						srcId, damageAmount, _playerDamage);
 				}
 				if (!_noDamage) {
@@ -584,7 +584,7 @@ void InsaneRebel2::iactRebel2Opcode3(Common::SeekableReadStream &b, int16 par2,
 			}
 		}
 	} else {
-		debug("Rebel2 Opcode3: UNHANDLED par3=%d par4=%d", par3, par4);
+		debugC(DEBUG_INSANE, "Rebel2 Opcode3: UNHANDLED par3=%d par4=%d", par3, par4);
 	}
 }
 
@@ -597,7 +597,7 @@ void InsaneRebel2::updateOpcode6Handler(int16 par2) {
 			_level2BackgroundLoaded = false;
 		}
 		_rebelHandler = par2;
-		debug("Rebel2 Opcode 6: Setting handler=%d", par2);
+		debugC(DEBUG_INSANE, "Rebel2 Opcode 6: Setting handler=%d", par2);
 	}
 }
 
@@ -634,7 +634,7 @@ void InsaneRebel2::handleOpcode6Handler8(Common::SeekableReadStream &b, int16 pa
 		}
 		_rebelWaveState = _rebelPhaseState;
 		_rebelOp6Initialized = true;
-		debug("Rebel2 Opcode 6 (Handler 8): Wave init, wave=0x%x", _rebelWaveState);
+		debugC(DEBUG_INSANE, "Rebel2 Opcode 6 (Handler 8): Wave init, wave=0x%x", _rebelWaveState);
 	}
 
 	// Skip position calculation for special modes 4 and 5
@@ -775,7 +775,7 @@ void InsaneRebel2::handleOpcode6Handler8(Common::SeekableReadStream &b, int16 pa
 			_vm->getActionState(kScummActionInsaneAttack);
 	}
 
-	debug("Rebel2 Opcode 6 (Handler 8): mode=%d bodyFlag=%d range=%d shipPos=(%d,%d) target=(%d,%d) firing=%d dir=(%d,%d,%d)",
+	debugC(DEBUG_INSANE, "Rebel2 Opcode 6 (Handler 8): mode=%d bodyFlag=%d range=%d shipPos=(%d,%d) target=(%d,%d) firing=%d dir=(%d,%d,%d)",
 		_shipLevelMode, bodyStatusFlag, _movementRangeLimit, _shipPosX, _shipPosY, _shipTargetX, _shipTargetY, _shipFiring,
 		_shipDirectionH, _shipDirectionV, _shipDirectionIndex);
 }
@@ -792,7 +792,7 @@ void InsaneRebel2::handleOpcode6Handler7(Common::SeekableReadStream &b, int16 pa
 	//   Mode 2: Combat mode - shooting ENABLED, SECONDARY zones
 	//   Mode 3: Tunnel flight - PRIMARY zones, per-edge push-back (hMargin=0x0f)
 	_flyControlMode = par4;
-	debug("Rebel2 Opcode 6 (Handler 7): Control mode set to %d (shooting %s)",
+	debugC(DEBUG_INSANE, "Rebel2 Opcode 6 (Handler 7): Control mode set to %d (shooting %s)",
 		par4, (par4 == 2) ? "ENABLED" : "DISABLED");
 
 	// Status bar: param_5[4] == 1 in original (first body word, 5th IACT word)
@@ -809,7 +809,7 @@ void InsaneRebel2::handleOpcode6Handler7(Common::SeekableReadStream &b, int16 pa
 		if (_smush_iconsNut && _smush_iconsNut->getNumChars() > 5) {
 			initLaserTexture(_smush_iconsNut, 5);
 		}
-		debug("Rebel2 Opcode 6 (Handler 7): Status bar enabled (body flag=%d)", bodyStatusFlag);
+		debugC(DEBUG_INSANE, "Rebel2 Opcode 6 (Handler 7): Status bar enabled (body flag=%d)", bodyStatusFlag);
 	}
 
 	// Ship position update - FUN_40C3CC case 4, lines 49-327.
@@ -1125,7 +1125,7 @@ void InsaneRebel2::handleOpcode6Handler25(byte *renderBitmap, Common::SeekableRe
 			}
 			_rebelWaveState = _rebelPhaseState;
 			_rebelOp6Initialized = true;
-			debug("Rebel2 Opcode 6 (Handler 25): Wave init, wave=0x%x autopilot=%d damageLevel=%d",
+			debugC(DEBUG_INSANE, "Rebel2 Opcode 6 (Handler 25): Wave init, wave=0x%x autopilot=%d damageLevel=%d",
 				_rebelWaveState, _rebelAutopilot, _rebelDamageLevel);
 		}
 	}
@@ -1139,7 +1139,7 @@ void InsaneRebel2::handleOpcode6Handler25(byte *renderBitmap, Common::SeekableRe
 	// Mode 4: Alternative uncovered position - sprite on right
 	_grdSpriteMode = par4;  // local_14[3] maps to par4 (offset 6-7)
 
-	debug("Rebel2 Handler25 Opcode6: par2=%d par3=%d par4=%d(mode) par5=%d(reset) autopilot=%d damageLevel=%d controlMode=%d",
+	debugC(DEBUG_INSANE, "Rebel2 Handler25 Opcode6: par2=%d par3=%d par4=%d(mode) par5=%d(reset) autopilot=%d damageLevel=%d controlMode=%d",
 		par2, par3, par4, par5, _rebelAutopilot, _rebelDamageLevel, _rebelControlMode);
 
 	// Autopilot logic (lines 123-146).
@@ -1150,13 +1150,13 @@ void InsaneRebel2::handleOpcode6Handler25(byte *renderBitmap, Common::SeekableRe
 			// Uncovered: RIGHT button enters cover.
 			if ((_rebelControlMode & 2) != 0) {
 				_rebelAutopilot = 1;
-				debug("Rebel2 Handler25: Entering cover (right click), controlMode=%d", _rebelControlMode);
+				debugC(DEBUG_INSANE, "Rebel2 Handler25: Entering cover (right click), controlMode=%d", _rebelControlMode);
 			}
 		} else {
 			// Covered: ANY button exits cover.
 			if (_rebelControlMode != 0) {
 				_rebelAutopilot = 0;
-				debug("Rebel2 Handler25: Exiting cover (button click), controlMode=%d", _rebelControlMode);
+				debugC(DEBUG_INSANE, "Rebel2 Handler25: Exiting cover (button click), controlMode=%d", _rebelControlMode);
 			}
 		}
 
@@ -1191,7 +1191,7 @@ void InsaneRebel2::handleOpcode6Handler25(byte *renderBitmap, Common::SeekableRe
 		}
 	}
 	if (_rebelDamageLevel != prevDamageLevel) {
-		debug("Rebel2 Handler25: damageLevel transition %d -> %d (autopilot=%d)",
+		debugC(DEBUG_INSANE, "Rebel2 Handler25: damageLevel transition %d -> %d (autopilot=%d)",
 			prevDamageLevel, _rebelDamageLevel, _rebelAutopilot);
 	}
 
@@ -1263,10 +1263,10 @@ void InsaneRebel2::handleOpcode6Handler25(byte *renderBitmap, Common::SeekableRe
 		_rebelViewOffset2X = _rebelDamageLevel * -22;
 		_rebelViewOffsetY = 0;
 		_rebelViewOffset2Y = 0;
-		debug("Rebel2 Opcode 6 (Handler 25): Unknown mode %d, using Mode 1 fallback", _grdSpriteMode);
+		debugC(DEBUG_INSANE, "Rebel2 Opcode 6 (Handler 25): Unknown mode %d, using Mode 1 fallback", _grdSpriteMode);
 	}
 
-	debug("Rebel2 Opcode 6 (Handler 25): mode=%d damage=%d dir=%d autopilot=%d viewOff=(%d,%d) spritePos=(%d,%d)",
+	debugC(DEBUG_INSANE, "Rebel2 Opcode 6 (Handler 25): mode=%d damage=%d dir=%d autopilot=%d viewOff=(%d,%d) spritePos=(%d,%d)",
 		_grdSpriteMode, _rebelDamageLevel, _rebelFlightDir, _rebelAutopilot,
 		_rebelViewOffsetX, _rebelViewOffsetY, _rebelViewOffset2X, _rebelViewOffset2Y);
 
@@ -1314,7 +1314,7 @@ void InsaneRebel2::handleOpcode6Turret(Common::SeekableReadStream &b, int16 par4
 			_rebelWaveState = _rebelPhaseState;
 			_rebelHitCounter = 0;
 			_rebelOp6Initialized = true;
-			debug("Rebel2 Opcode 6 (Handler 0x26): Wave init, levelType=%d waveState=0x%x",
+			debugC(DEBUG_INSANE, "Rebel2 Opcode 6 (Handler 0x26): Wave init, levelType=%d waveState=0x%x",
 				_rebelLevelType, _rebelWaveState);
 		}
 	}
@@ -1339,7 +1339,7 @@ void InsaneRebel2::handleOpcode6GenericInit(int16 par4) {
 			_rebelWaveState = _rebelPhaseState;
 			_rebelHitCounter = 0;
 			_rebelOp6Initialized = true;
-			debug("Rebel2 Opcode 6: Wave init - cleared bits/links, waveState=0x%x", _rebelWaveState);
+			debugC(DEBUG_INSANE, "Rebel2 Opcode 6: Wave init - cleared bits/links, waveState=0x%x", _rebelWaveState);
 		}
 	}
 }
@@ -1458,7 +1458,7 @@ void InsaneRebel2::updateOpcode6GenericFlightState() {
 		break;
 	}
 
-	debug("Rebel2 Opcode 6: levelType=%d autopilot=%d damageLevel=%d viewOffset=(%d,%d)",
+	debugC(DEBUG_INSANE, "Rebel2 Opcode 6: levelType=%d autopilot=%d damageLevel=%d viewOffset=(%d,%d)",
 		_rebelLevelType, _rebelAutopilot, _rebelDamageLevel, _rebelViewOffsetX, _rebelViewOffsetY);
 }
 
@@ -1522,7 +1522,7 @@ void InsaneRebel2::iactRebel2Opcode6(byte *renderBitmap, Common::SeekableReadStr
 	// - Handler-specific status/reset word
 	// - Different view offset calculations
 
-	debug("Rebel2 IACT Opcode 6: par2=%d par3=%d par4=%d", par2, par3, par4);
+	debugC(DEBUG_INSANE, "Rebel2 IACT Opcode 6: par2=%d par3=%d par4=%d", par2, par3, par4);
 
 	updateOpcode6Handler(par2);
 
@@ -1610,7 +1610,7 @@ bool InsaneRebel2::loadOpcode8EdgeTable(Common::SeekableReadStream &b, int64 sta
 			b.read(edgeData, 8 + 32896);
 			initEdgeTable(edgeData);
 			free(edgeData);
-			debug("Rebel2 Opcode 8: Loaded per-level edge blend table (par4=1000)");
+			debugC(DEBUG_INSANE, "Rebel2 Opcode 8: Loaded per-level edge blend table (par4=1000)");
 		}
 		b.seek(startPos);
 		return true;
@@ -1651,11 +1651,11 @@ bool InsaneRebel2::loadOpcode8AuxSfx(Common::SeekableReadStream &b, int64 startP
 				b.read(soundData, dataSize);
 				loadAuxSfx(auxBuffer, soundData, dataSize);
 				free(soundData);
-				debug("Rebel2 Opcode 8: Loaded %u bytes into aux sound buffer %d (par4=%d)",
+				debugC(DEBUG_INSANE, "Rebel2 Opcode 8: Loaded %u bytes into aux sound buffer %d (par4=%d)",
 					dataSize, auxBuffer, par4);
 			}
 		} else {
-			debug("Rebel2 Opcode 8: Aux sound par4=%d dataSize=%u exceeds remaining=%lld",
+			debugC(DEBUG_INSANE, "Rebel2 Opcode 8: Aux sound par4=%d dataSize=%u exceeds remaining=%lld",
 				par4, dataSize, (long long)remaining);
 		}
 	}
@@ -1682,7 +1682,7 @@ bool InsaneRebel2::loadOpcode8ShotOriginTable(Common::SeekableReadStream &b, int
 // ScummVM refactor helper for opcode 8 embedded ANIM scanning, not a separate retail function.
 void InsaneRebel2::loadOpcode8EmbeddedAnim(byte *renderBitmap, Common::SeekableReadStream &b, int64 startPos, int64 remaining, int16 par3, int16 par4) {
 	// Remaining handlers require finding ANIM tag in the stream.
-	debug("Rebel2 Opcode 8: Scanning for ANIM tag (startPos=%lld remaining=%lld)",
+	debugC(DEBUG_INSANE, "Rebel2 Opcode 8: Scanning for ANIM tag (startPos=%lld remaining=%lld)",
 		(long long)startPos, (long long)remaining);
 
 	if (remaining <= 0) {
@@ -1696,19 +1696,19 @@ void InsaneRebel2::loadOpcode8EmbeddedAnim(byte *renderBitmap, Common::SeekableR
 	}
 
 	int bytesRead = b.read(scanBuf, scanSize);
-	debug("Rebel2 Opcode 8: Read %d bytes for ANIM scan", bytesRead);
+	debugC(DEBUG_INSANE, "Rebel2 Opcode 8: Read %d bytes for ANIM scan", bytesRead);
 
 	int animOffset = -1;
 	for (int i = 0; i + 8 <= bytesRead; ++i) {
 		if (READ_BE_UINT32(scanBuf + i) == MKTAG('A','N','I','M')) {
 			animOffset = i;
-			debug("Rebel2 Opcode 8: Found ANIM at offset %d", i);
+			debugC(DEBUG_INSANE, "Rebel2 Opcode 8: Found ANIM at offset %d", i);
 			break;
 		}
 	}
 
 	if (animOffset < 0) {
-		debug("Rebel2 Opcode 8: No ANIM tag found");
+		debugC(DEBUG_INSANE, "Rebel2 Opcode 8: No ANIM tag found");
 		free(scanBuf);
 		b.seek(startPos);
 		return;
@@ -1779,7 +1779,7 @@ bool InsaneRebel2::handleOpcode8EmbeddedAnim(byte *renderBitmap, byte *animData,
 		} else if (par4 == 5) {
 			handled = loadLevel2Background(animData, animDataSize, renderBitmap);
 		} else if (par4 == 4 || par4 == 6 || par4 == 7) {
-			debug("Rebel2 Opcode 8: Handler 25 overlay par4=%d - drawing to screen", par4);
+			debugC(DEBUG_INSANE, "Rebel2 Opcode 8: Handler 25 overlay par4=%d - drawing to screen", par4);
 			loadEmbeddedSan(par4, animData, animDataSize, renderBitmap);
 			handled = true;
 		}
@@ -1793,7 +1793,7 @@ bool InsaneRebel2::handleOpcode8EmbeddedAnim(byte *renderBitmap, byte *animData,
 		const bool lowResHud = (par3 == 1 || par3 == 3);
 
 		if ((!highRes && highResHud) || (highRes && lowResHud)) {
-			debug("Rebel2 Opcode 8: Skipping %s HUD par3=%d while running in %s mode",
+			debugC(DEBUG_INSANE, "Rebel2 Opcode 8: Skipping %s HUD par3=%d while running in %s mode",
 				highResHud ? "high-res" : "low-res", par3, highRes ? "high-res" : "low-res");
 			handled = true;
 		} else {
@@ -1807,7 +1807,7 @@ bool InsaneRebel2::handleOpcode8EmbeddedAnim(byte *renderBitmap, byte *animData,
 
 			// Skip audio tracks (userId >= 1000).
 			if (userId > 0 && userId < 1000) {
-				debug("Rebel2 Opcode 8: Loading embedded SAN HUD userId=%d (handler=%d par3=%d par4=%d)",
+				debugC(DEBUG_INSANE, "Rebel2 Opcode 8: Loading embedded SAN HUD userId=%d (handler=%d par3=%d par4=%d)",
 					userId, _rebelHandler, par3, par4);
 				loadEmbeddedSan(userId, animData, animDataSize, renderBitmap);
 				handled = true;
@@ -1816,14 +1816,14 @@ bool InsaneRebel2::handleOpcode8EmbeddedAnim(byte *renderBitmap, byte *animData,
 	}
 
 	if (!handled) {
-		debug("Rebel2 Opcode 8: Unhandled case - handler=%d par3=%d par4=%d", _rebelHandler, par3, par4);
+		debugC(DEBUG_INSANE, "Rebel2 Opcode 8: Unhandled case - handler=%d par3=%d par4=%d", _rebelHandler, par3, par4);
 	}
 
 	return handled;
 }
 
 void InsaneRebel2::iactRebel2Opcode8(byte *renderBitmap, Common::SeekableReadStream &b, int32 chunkSize, int16 par2, int16 par3, int16 par4) {
-	debug("Rebel2 IACT Opcode 8: handler=%d par2=%d par3=%d par4=%d (gameState=%d)",
+	debugC(DEBUG_INSANE, "Rebel2 IACT Opcode 8: handler=%d par2=%d par3=%d par4=%d (gameState=%d)",
 		_rebelHandler, par2, par3, par4, _gameState);
 
 	int64 startPos = b.pos();
@@ -1914,7 +1914,7 @@ bool InsaneRebel2::loadHandler25ShotOriginTable(Common::SeekableReadStream &b, i
 	b.seek(savedPos);
 
 	if (count < 20) {
-		debug("Rebel2 Opcode 8: Handler25 par4=8 parse failed (count=%d, expected up to 30)", count);
+		debugC(DEBUG_INSANE, "Rebel2 Opcode 8: Handler25 par4=8 parse failed (count=%d, expected up to 30)", count);
 		return false;
 	}
 
@@ -1930,7 +1930,7 @@ bool InsaneRebel2::loadHandler25ShotOriginTable(Common::SeekableReadStream &b, i
 	}
 	_grdShotOriginTableLoaded = true;
 
-	debug("Rebel2 Opcode 8: Loaded Handler25 shot-origin table (pairs=%d) idx5=(%d,%d) idx14=(%d,%d)",
+	debugC(DEBUG_INSANE, "Rebel2 Opcode 8: Loaded Handler25 shot-origin table (pairs=%d) idx5=(%d,%d) idx14=(%d,%d)",
 		count / 2, _grdShotOriginX[5], _grdShotOriginY[5], _grdShotOriginX[14], _grdShotOriginY[14]);
 	return true;
 }
@@ -1998,7 +1998,7 @@ bool InsaneRebel2::loadHandler7ShotTable(Common::SeekableReadStream &b, int64 st
 	b.seek(savedPos);
 
 	if (count < 70) {
-		debug("Rebel2 Opcode 8: Handler7 par4=%d shot table parse failed (count=%d)", par4, count);
+		debugC(DEBUG_INSANE, "Rebel2 Opcode 8: Handler7 par4=%d shot table parse failed (count=%d)", par4, count);
 		return false;
 	}
 
@@ -2014,7 +2014,7 @@ bool InsaneRebel2::loadHandler7ShotTable(Common::SeekableReadStream &b, int64 st
 	else
 		_flyRightGunTableLoaded = true;
 
-	debug("Rebel2 Opcode 8: Loaded Handler7 %s gun table idx0=(%d,%d) idx17=(%d,%d) idx34=(%d,%d)",
+	debugC(DEBUG_INSANE, "Rebel2 Opcode 8: Loaded Handler7 %s gun table idx0=(%d,%d) idx17=(%d,%d) idx34=(%d,%d)",
 		(par4 == 12) ? "left" : "right",
 		tableX[0], tableY[0], tableX[17], tableY[17], tableX[34], tableY[34]);
 	return true;
@@ -2050,13 +2050,13 @@ bool InsaneRebel2::loadHandler7FlySprites(Common::SeekableReadStream &b, int64 r
 		return false;
 	}
 
-	debug("Rebel2 loadHandler7FlySprites: header bytes: %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X",
+	debugC(DEBUG_INSANE, "Rebel2 loadHandler7FlySprites: header bytes: %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X",
 		header[0], header[1], header[2], header[3], header[4],
 		header[5], header[6], header[7], header[8], header[9]);
 
 	// Size is at offset 14 from IACT start = bytes 6-9 of our header buffer
 	uint32 nutSize = READ_LE_UINT32(header + 6);
-	debug("Rebel2 loadHandler7FlySprites: par4=%d nutSize=%u remaining=%lld",
+	debugC(DEBUG_INSANE, "Rebel2 loadHandler7FlySprites: par4=%d nutSize=%u remaining=%lld",
 		par4, nutSize, (long long)remaining);
 
 	if (nutSize == 0 || nutSize > (uint32)(remaining - 10)) {
@@ -2069,7 +2069,7 @@ bool InsaneRebel2::loadHandler7FlySprites(Common::SeekableReadStream &b, int64 r
 	}
 
 	int bytesRead = b.read(nutData, nutSize);
-	debug("Rebel2 loadHandler7FlySprites: Read %d/%u bytes, first 16: %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X",
+	debugC(DEBUG_INSANE, "Rebel2 loadHandler7FlySprites: Read %d/%u bytes, first 16: %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X",
 		bytesRead, nutSize,
 		bytesRead > 0 ? nutData[0] : 0, bytesRead > 1 ? nutData[1] : 0,
 		bytesRead > 2 ? nutData[2] : 0, bytesRead > 3 ? nutData[3] : 0,
@@ -2099,13 +2099,13 @@ bool InsaneRebel2::loadHandler7FlySprites(Common::SeekableReadStream &b, int64 r
 	// Load as a Rebel2 embedded sprite ANIM.
 	NutRenderer *newNut = makeRebel2SpriteFromData(_vm, nutData, bytesRead);
 	if (!newNut || newNut->getNumChars() <= 0) {
-		debug("Rebel2 loadHandler7FlySprites: NUT load failed for par4=%d", par4);
+		debugC(DEBUG_INSANE, "Rebel2 loadHandler7FlySprites: NUT load failed for par4=%d", par4);
 		delete newNut;
 		free(nutData);
 		return false;
 	}
 
-	debug("Rebel2 loadHandler7FlySprites: Loaded FLY NUT par4=%d with %d sprites",
+	debugC(DEBUG_INSANE, "Rebel2 loadHandler7FlySprites: Loaded FLY NUT par4=%d with %d sprites",
 		par4, newNut->getNumChars());
 
 	// Assign to appropriate slot based on par4 (matches FUN_0040c3cc case 6 switch)
@@ -2114,7 +2114,7 @@ bool InsaneRebel2::loadHandler7FlySprites(Common::SeekableReadStream &b, int64 r
 	case 1:  // FLY001 - Ship direction sprites (35 frames)
 		delete _flyShipSprite;
 		_flyShipSprite = newNut;
-		debug("Rebel2: _flyShipSprite set with %d sprites", newNut->getNumChars());
+		debugC(DEBUG_INSANE, "Rebel2: _flyShipSprite set with %d sprites", newNut->getNumChars());
 		break;
 	case 2:  // FLY003 - Targeting overlay
 		delete _flyTargetSprite;
@@ -2156,7 +2156,7 @@ bool InsaneRebel2::loadTurretHudOverlay(byte *animData, int32 size, int16 select
 	const int secondarySlot = highRes ? 4 : 3;
 
 	if (selector >= 1 && selector <= 4 && selector != primarySlot && selector != secondarySlot) {
-		debug("Rebel2 loadTurretHudOverlay: Skipping %s HUD selector=%d (running in %s mode)",
+		debugC(DEBUG_INSANE, "Rebel2 loadTurretHudOverlay: Skipping %s HUD selector=%d (running in %s mode)",
 			(selector == 2 || selector == 4) ? "high-res" : "low-res", selector,
 			highRes ? "high-res" : "low-res");
 		return true;
@@ -2168,12 +2168,12 @@ bool InsaneRebel2::loadTurretHudOverlay(byte *animData, int32 size, int16 select
 
 	NutRenderer *newNut = makeRebel2SpriteFromData(_vm, animData, size);
 	if (!newNut || newNut->getNumChars() <= 0) {
-		debug("Rebel2 loadTurretHudOverlay: NUT load failed for selector=%d", selector);
+		debugC(DEBUG_INSANE, "Rebel2 loadTurretHudOverlay: NUT load failed for selector=%d", selector);
 		delete newNut;
 		return false;
 	}
 
-	debug("Rebel2 loadTurretHudOverlay: Loaded turret HUD NUT selector=%d with %d sprites",
+	debugC(DEBUG_INSANE, "Rebel2 loadTurretHudOverlay: Loaded turret HUD NUT selector=%d with %d sprites",
 		selector, newNut->getNumChars());
 
 	if (selector == primarySlot) {
@@ -2207,12 +2207,12 @@ bool InsaneRebel2::loadHandler8ShipSprites(byte *animData, int32 size, int16 par
 
 	NutRenderer *newNut = makeRebel2SpriteFromData(_vm, animData, size);
 	if (!newNut || newNut->getNumChars() <= 0) {
-		debug("Rebel2 loadHandler8ShipSprites: NUT load failed for par4=%d", par4);
+		debugC(DEBUG_INSANE, "Rebel2 loadHandler8ShipSprites: NUT load failed for par4=%d", par4);
 		delete newNut;
 		return false;
 	}
 
-	debug("Rebel2 loadHandler8ShipSprites: Loaded ship NUT par4=%d with %d sprites",
+	debugC(DEBUG_INSANE, "Rebel2 loadHandler8ShipSprites: Loaded ship NUT par4=%d with %d sprites",
 		par4, newNut->getNumChars());
 
 	switch (par4) {
@@ -2259,29 +2259,29 @@ bool InsaneRebel2::loadHandler25GrdSprites(byte *animData, int32 size, int16 par
 
 	NutRenderer *newNut = makeRebel2SpriteFromData(_vm, animData, size);
 	if (!newNut || newNut->getNumChars() <= 0) {
-		debug("Rebel2 loadHandler25GrdSprites: NUT load failed for par4=%d", par4);
+		debugC(DEBUG_INSANE, "Rebel2 loadHandler25GrdSprites: NUT load failed for par4=%d", par4);
 		delete newNut;
 		return false;
 	}
 
-	debug("Rebel2 loadHandler25GrdSprites: Loaded GRD NUT par4=%d with %d sprites",
+	debugC(DEBUG_INSANE, "Rebel2 loadHandler25GrdSprites: Loaded GRD NUT par4=%d with %d sprites",
 		par4, newNut->getNumChars());
 
 	switch (par4) {
 	case 1:  // GRD001 - Primary ship sprite
 		delete _grd001Sprite;
 		_grd001Sprite = newNut;
-		debug("Rebel2: _grd001Sprite set with %d sprites", newNut->getNumChars());
+		debugC(DEBUG_INSANE, "Rebel2: _grd001Sprite set with %d sprites", newNut->getNumChars());
 		break;
 	case 2:  // GRD002 - Secondary ship sprite
 		delete _grd002Sprite;
 		_grd002Sprite = newNut;
-		debug("Rebel2: _grd002Sprite set with %d sprites", newNut->getNumChars());
+		debugC(DEBUG_INSANE, "Rebel2: _grd002Sprite set with %d sprites", newNut->getNumChars());
 		break;
 	case 10: // GRD005 - Mode 3 overlay sprite
 		delete _grd005Sprite;
 		_grd005Sprite = newNut;
-		debug("Rebel2: _grd005Sprite set with %d sprites", newNut->getNumChars());
+		debugC(DEBUG_INSANE, "Rebel2: _grd005Sprite set with %d sprites", newNut->getNumChars());
 		break;
 	default:
 		delete newNut;
@@ -2301,7 +2301,7 @@ bool InsaneRebel2::loadLevel2Background(byte *animData, int32 size, byte *render
 		return false;
 	}
 
-	debug("Rebel2 loadLevel2Background: Loading Level 2 background (animSize=%d)", size);
+	debugC(DEBUG_INSANE, "Rebel2 loadLevel2Background: Loading Level 2 background (animSize=%d)", size);
 
 	// Allocate background buffer if needed (320x200 = 64000 bytes)
 	if (_level2Background == nullptr) {
@@ -2317,7 +2317,7 @@ bool InsaneRebel2::loadLevel2Background(byte *animData, int32 size, byte *render
 
 	uint32 animTag = stream.readUint32BE();
 	if (animTag != MKTAG('A','N','I','M')) {
-		debug("Rebel2 loadLevel2Background: Missing ANIM tag, got 0x%08X", animTag);
+		debugC(DEBUG_INSANE, "Rebel2 loadLevel2Background: Missing ANIM tag, got 0x%08X", animTag);
 		return false;
 	}
 
@@ -2326,10 +2326,10 @@ bool InsaneRebel2::loadLevel2Background(byte *animData, int32 size, byte *render
 	if ((int64)animSize <= streamEnd - 8) {
 		animEnd = 8 + (int64)animSize;
 	} else {
-		debug("Rebel2 loadLevel2Background: ANIM truncated: reported size=%u, actual=%lld",
+		debugC(DEBUG_INSANE, "Rebel2 loadLevel2Background: ANIM truncated: reported size=%u, actual=%lld",
 			animSize, streamEnd - 8);
 	}
-	debug("Rebel2 loadLevel2Background: Found ANIM tag, size=%u", animSize);
+	debugC(DEBUG_INSANE, "Rebel2 loadLevel2Background: Found ANIM tag, size=%u", animSize);
 
 	bool foundBackground = false;
 	while (!stream.eos() && stream.pos() + 8 <= animEnd && !foundBackground) {
@@ -2345,7 +2345,7 @@ bool InsaneRebel2::loadLevel2Background(byte *animData, int32 size, byte *render
 			continue;
 		}
 
-		debug("Rebel2 loadLevel2Background: Found FRME at %lld, size=%u", stream.pos() - 8, chunkSize);
+		debugC(DEBUG_INSANE, "Rebel2 loadLevel2Background: Found FRME at %lld, size=%u", stream.pos() - 8, chunkSize);
 
 		while (stream.pos() + 8 <= chunkDataEnd && !stream.eos() && !foundBackground) {
 			uint32 subTag;
@@ -2361,7 +2361,7 @@ bool InsaneRebel2::loadLevel2Background(byte *animData, int32 size, byte *render
 			}
 
 			if (subSize < 14) {
-				debug("Rebel2 loadLevel2Background: FOBJ too small: size=%u", subSize);
+				debugC(DEBUG_INSANE, "Rebel2 loadLevel2Background: FOBJ too small: size=%u", subSize);
 				stream.seek(nextSubPos);
 				continue;
 			}
@@ -2375,7 +2375,7 @@ bool InsaneRebel2::loadLevel2Background(byte *animData, int32 size, byte *render
 			stream.readUint16LE();  // unknown
 			stream.readUint16LE();  // unknown
 
-			debug("Rebel2 loadLevel2Background: Found FOBJ: codec=%d pos=(%d,%d) size=%dx%d",
+			debugC(DEBUG_INSANE, "Rebel2 loadLevel2Background: Found FOBJ: codec=%d pos=(%d,%d) size=%dx%d",
 				codec, fobjX, fobjY, fobjW, fobjH);
 
 			// Decode codec 3 (RLE) into the original 320x200 background buffer.
@@ -2389,7 +2389,7 @@ bool InsaneRebel2::loadLevel2Background(byte *animData, int32 size, byte *render
 				smushDecodeRLEOpaque(_level2Background, rleData, fobjX, fobjY, drawW, drawH, 320,
 					(int)(subDataEnd - stream.pos()));
 
-				debug("Rebel2 loadLevel2Background: Decoded Level 2 background (%dx%d at %d,%d, clipped to %dx%d)",
+				debugC(DEBUG_INSANE, "Rebel2 loadLevel2Background: Decoded Level 2 background (%dx%d at %d,%d, clipped to %dx%d)",
 					fobjW, fobjH, fobjX, fobjY, drawW, drawH);
 				_level2BackgroundLoaded = true;
 				foundBackground = true;
@@ -2402,9 +2402,9 @@ bool InsaneRebel2::loadLevel2Background(byte *animData, int32 size, byte *render
 						for (int by = 0; by < 200; by++) {
 							memcpy(renderBitmap + by * 320, _level2Background + by * 320, 320);
 						}
-						debug("Rebel2 loadLevel2Background: Copied to renderBitmap (pitch=%d)", bufferPitch);
+						debugC(DEBUG_INSANE, "Rebel2 loadLevel2Background: Copied to renderBitmap (pitch=%d)", bufferPitch);
 					} else {
-						debug("Rebel2 loadLevel2Background: Skipping renderBitmap copy (pitch=%d != 320)", bufferPitch);
+						debugC(DEBUG_INSANE, "Rebel2 loadLevel2Background: Skipping renderBitmap copy (pitch=%d != 320)", bufferPitch);
 					}
 				}
 			}
@@ -2416,7 +2416,7 @@ bool InsaneRebel2::loadLevel2Background(byte *animData, int32 size, byte *render
 	}
 
 	if (!foundBackground) {
-		debug("Rebel2 loadLevel2Background: Failed to find/decode background FOBJ");
+		debugC(DEBUG_INSANE, "Rebel2 loadLevel2Background: Failed to find/decode background FOBJ");
 	}
 
 	return foundBackground;
@@ -2471,9 +2471,9 @@ void InsaneRebel2::iactRebel2Opcode9(byte *renderBitmap, Common::SeekableReadStr
 		// Try to get string from SMUSH player's string resource
 		if (_player && _player->getString(stringIndex)) {
 			textStr = _player->getString(stringIndex);
-			debug("Rebel2 Opcode 9: TRES string index=%d -> \"%s\"", stringIndex, textStr);
+			debugC(DEBUG_INSANE, "Rebel2 Opcode 9: TRES string index=%d -> \"%s\"", stringIndex, textStr);
 		} else {
-			debug("Rebel2 Opcode 9: TRES string index=%d not found", stringIndex);
+			debugC(DEBUG_INSANE, "Rebel2 Opcode 9: TRES string index=%d not found", stringIndex);
 			return;
 		}
 
@@ -2513,11 +2513,11 @@ void InsaneRebel2::iactRebel2Opcode9(byte *renderBitmap, Common::SeekableReadStr
 		textBuffer[textLen] = '\0';
 		textStr = textBuffer;
 
-		debug("Rebel2 Opcode 9: Inline text at (%d,%d) flags=0x%x -> \"%s\"", posX, posY, textFlags, textStr);
+		debugC(DEBUG_INSANE, "Rebel2 Opcode 9: Inline text at (%d,%d) flags=0x%x -> \"%s\"", posX, posY, textFlags, textStr);
 	}
 
 	if (!textStr || textStr[0] == '\0') {
-		debug("Rebel2 Opcode 9: Empty text string, skipping");
+		debugC(DEBUG_INSANE, "Rebel2 Opcode 9: Empty text string, skipping");
 		return;
 	}
 
@@ -2542,7 +2542,7 @@ void InsaneRebel2::iactRebel2Opcode9(byte *renderBitmap, Common::SeekableReadStr
 
 	// Use the message font loaded during initialization (DIHIFONT.NUT)
 	if (!_rebelMsgFont) {
-		debug("Rebel2 Opcode 9: No message font loaded (_rebelMsgFont is null)");
+		debugC(DEBUG_INSANE, "Rebel2 Opcode 9: No message font loaded (_rebelMsgFont is null)");
 		return;
 	}
 
@@ -2655,7 +2655,7 @@ void InsaneRebel2::iactRebel2Opcode9(byte *renderBitmap, Common::SeekableReadStr
 		}
 	}
 
-	debug("Rebel2 Opcode 9: Rendered subtitle at (%d,%d) flags=0x%x clip=(%d,%d,%d,%d)",
+	debugC(DEBUG_INSANE, "Rebel2 Opcode 9: Rendered subtitle at (%d,%d) flags=0x%x clip=(%d,%d,%d,%d)",
 		posX, posY, textFlags, clipX, clipY, clipW, clipH);
 }
 
@@ -2675,7 +2675,7 @@ void InsaneRebel2::enemyUpdate(byte *renderBitmap, Common::SeekableReadStream &b
 
 	// If disabled, stop processing this object
 	if (disabled) {
-		// debug("Rebel2: Skipping Opcode 4 for disabled enemy ID=%d", enemyId);
+		// debugC(DEBUG_INSANE, "Rebel2: Skipping Opcode 4 for disabled enemy ID=%d", enemyId);
 		return;
 	}
 
@@ -2691,7 +2691,7 @@ void InsaneRebel2::enemyUpdate(byte *renderBitmap, Common::SeekableReadStream &b
 	// In the original (FUN_004028C5/FUN_0041E7C2): sVar5/sVar2 = *(short *)(*local + 6)
 	// This maps to par4 (userId field). Used for DAT_0047ab98 wave state bitmask:
 	//   DAT_0047ab98 |= 1 << (type & 0x1f)
-	debug(5, "Rebel2 Opcode4: handler=%d enemyId=%d par2=%d par3=%d par4/type=%d pos=(%d,%d) size=(%d,%d)",
+	debugC(DEBUG_INSANE, "Rebel2 Opcode4: handler=%d enemyId=%d par2=%d par3=%d par4/type=%d pos=(%d,%d) size=(%d,%d)",
 		_rebelHandler, enemyId, par2, par3, par4, x, y, w, h);
 
 	bool found = false;
diff --git a/engines/scumm/insane/rebel2/levels.cpp b/engines/scumm/insane/rebel2/levels.cpp
index ebbdff25fd0..ea7e586e033 100644
--- a/engines/scumm/insane/rebel2/levels.cpp
+++ b/engines/scumm/insane/rebel2/levels.cpp
@@ -108,7 +108,7 @@ void InsaneRebel2::runGame() {
 			if (chapterResult == kChapterSelectPlay) {
 				// _selectedChapter is 0-based, runLevel expects 1-based
 				int selectedLevel = _selectedChapter + 1;
-				debug("InsaneRebel2: Starting chapter %d (level %d)", _selectedChapter + 1, selectedLevel);
+				debugC(DEBUG_INSANE, "InsaneRebel2: Starting chapter %d (level %d)", _selectedChapter + 1, selectedLevel);
 
 				// Ending selected directly from chapter select (FUN_0041bbe8, case 0xf)
 				if (selectedLevel == 16) {
@@ -154,7 +154,7 @@ void InsaneRebel2::runGame() {
 // We skip the easter eggs and play both O_OPEN_A + O_OPEN_B unconditionally.
 //
 void InsaneRebel2::playIntroSequence() {
-	debug("Rebel2: Playing intro sequence");
+	debugC(DEBUG_INSANE, "Rebel2: Playing intro sequence");
 
 	_gameState = kStateIntro;
 	_menuInputActive = false;
@@ -163,7 +163,7 @@ void InsaneRebel2::playIntroSequence() {
 
 	// Play main intro (OPEN/O_OPEN_A.SAN)
 	// Original: FUN_0041f4d0("OPEN/O_OPEN_A.SAN", 0x28, 0xffff, 0xffff, 0)
-	debug("Rebel2: Playing main intro (O_OPEN_A.SAN)");
+	debugC(DEBUG_INSANE, "Rebel2: Playing main intro (O_OPEN_A.SAN)");
 	splayer->setCurVideoFlags(0x28);
 	splayer->play("OPEN/O_OPEN_A.SAN", 15);
 
@@ -173,7 +173,7 @@ void InsaneRebel2::playIntroSequence() {
 	// Play additional intro (OPEN/O_OPEN_B.SAN)
 	// Original: conditional on DAT_0047ab45 || DAT_0047ab47
 	// We play unconditionally (matches "Continue Intro" menu behavior)
-	debug("Rebel2: Playing additional intro (O_OPEN_B.SAN)");
+	debugC(DEBUG_INSANE, "Rebel2: Playing additional intro (O_OPEN_B.SAN)");
 	splayer->setCurVideoFlags(0x28);
 	splayer->play("OPEN/O_OPEN_B.SAN", 15);
 }
@@ -181,7 +181,7 @@ void InsaneRebel2::playIntroSequence() {
 // playMissionBriefing -- Mission briefing screen (FUN_00415CF8).
 void InsaneRebel2::playMissionBriefing() {
 
-	debug("Rebel2: Playing mission briefing");
+	debugC(DEBUG_INSANE, "Rebel2: Playing mission briefing");
 
 	SmushPlayer *splayer = ((ScummEngine_v7 *)_vm)->_splayer;
 	splayer->setCurVideoFlags(0x08);  // Briefing mode flag
@@ -268,7 +268,7 @@ void InsaneRebel2::playLevelBegin(int levelId) {
 	Common::String prefix = getLevelPrefix(levelId);
 	Common::String filename = Common::String::format("%s/%sBEG.SAN", dir.c_str(), prefix.c_str());
 
-	debug("Rebel2: Playing level %d beginning: %s", levelId, filename.c_str());
+	debugC(DEBUG_INSANE, "Rebel2: Playing level %d beginning: %s", levelId, filename.c_str());
 
 	if (levelId >= 1 && levelId <= 15 && levelTextParams[levelId].textID >= 0) {
 		const TextOverlayParams &p = levelTextParams[levelId];
@@ -292,7 +292,7 @@ void InsaneRebel2::playLevelEnd(int levelId) {
 	Common::String prefix = getLevelPrefix(levelId);
 	Common::String filename = Common::String::format("%s/%sEND.SAN", dir.c_str(), prefix.c_str());
 
-	debug("Rebel2: Playing level %d end: %s", levelId, filename.c_str());
+	debugC(DEBUG_INSANE, "Rebel2: Playing level %d end: %s", levelId, filename.c_str());
 
 	SmushPlayer *splayer = ((ScummEngine_v7 *)_vm)->_splayer;
 	// Original: FUN_00417327 adds | 8, so flags = 0x20 | 0x08 = 0x28
@@ -313,7 +313,7 @@ void InsaneRebel2::playLevelRetry(int levelId) {
 	Common::String prefix = getLevelPrefix(levelId);
 	Common::String filename = Common::String::format("%s/%sRETRY.SAN", dir.c_str(), prefix.c_str());
 
-	debug("Rebel2: Playing level %d retry: %s", levelId, filename.c_str());
+	debugC(DEBUG_INSANE, "Rebel2: Playing level %d retry: %s", levelId, filename.c_str());
 
 	SmushPlayer *splayer = ((ScummEngine_v7 *)_vm)->_splayer;
 	// Original: FUN_00417168 adds | 8, so flags = 0x20 | 0x08 = 0x28
@@ -334,7 +334,7 @@ void InsaneRebel2::playLevelGameOver(int levelId) {
 	Common::String prefix = getLevelPrefix(levelId);
 	Common::String filename = Common::String::format("%s/%sOVER.SAN", dir.c_str(), prefix.c_str());
 
-	debug("Rebel2: Playing level %d game over: %s", levelId, filename.c_str());
+	debugC(DEBUG_INSANE, "Rebel2: Playing level %d game over: %s", levelId, filename.c_str());
 
 	SmushPlayer *splayer = ((ScummEngine_v7 *)_vm)->_splayer;
 	// Original: FUN_00417ab2 adds | 8, so flags = 0x20 | 0x08 = 0x28
@@ -355,7 +355,7 @@ void InsaneRebel2::playLevelGameOver(int levelId) {
 //
 void InsaneRebel2::playEndingSequence() {
 
-	debug("Rebel2: Playing ending sequence (difficulty=%d)", _difficulty);
+	debugC(DEBUG_INSANE, "Rebel2: Playing ending sequence (difficulty=%d)", _difficulty);
 
 	// Switch to gameplay state to stop menu overlay rendering
 	_gameState = kStateGameplay;
@@ -392,7 +392,7 @@ void InsaneRebel2::playEndingSequence() {
 // This is the credits accessible from the main menu, NOT the ending credits.
 void InsaneRebel2::playCreditsSequence() {
 
-	debug("Rebel2: Playing menu credits");
+	debugC(DEBUG_INSANE, "Rebel2: Playing menu credits");
 	resetVideoAudio();
 
 	SmushPlayer *splayer = ((ScummEngine_v7 *)_vm)->_splayer;
@@ -439,7 +439,7 @@ void InsaneRebel2::warpGameplayMouseNow(int x, int y) {
 // runLevel -- Main level dispatcher, calls per-level handlers.
 int InsaneRebel2::runLevel(int levelId) {
 
-	debug("Rebel2: Starting level %d", levelId);
+	debugC(DEBUG_INSANE, "Rebel2: Starting level %d", levelId);
 
 	// Validate level ID
 	if (levelId < 1 || levelId > 15) {
@@ -755,7 +755,7 @@ void InsaneRebel2::playLevelDeathVariant(int levelId, int phase, int frame) {
 		filename = Common::String::format("%s/%sDIE_%s.SAN", dir.c_str(), prefix.c_str(), variant.c_str());
 	}
 
-	debug("Rebel2: Playing death video: %s (phase=%d, frame=%d)", filename.c_str(), phase, frame);
+	debugC(DEBUG_INSANE, "Rebel2: Playing death video: %s (phase=%d, frame=%d)", filename.c_str(), phase, frame);
 
 	SmushPlayer *splayer = ((ScummEngine_v7 *)_vm)->_splayer;
 	// Original: FUN_00417168 adds | 8, so flags = 0x20 | 0x08 = 0x28
@@ -784,7 +784,7 @@ void InsaneRebel2::playLevelRetryVariant(int levelId, int phase) {
 		filename = Common::String::format("%s/%sRETRY.SAN", dir.c_str(), prefix.c_str());
 	}
 
-	debug("Rebel2: Playing retry video: %s (phase=%d)", filename.c_str(), phase);
+	debugC(DEBUG_INSANE, "Rebel2: Playing retry video: %s (phase=%d)", filename.c_str(), phase);
 
 	SmushPlayer *splayer = ((ScummEngine_v7 *)_vm)->_splayer;
 	// Original: FUN_00417168 adds | 8, so flags = 0x20 | 0x08 = 0x28
diff --git a/engines/scumm/insane/rebel2/menu.cpp b/engines/scumm/insane/rebel2/menu.cpp
index 2a17e4d0e4f..e40c088534d 100644
--- a/engines/scumm/insane/rebel2/menu.cpp
+++ b/engines/scumm/insane/rebel2/menu.cpp
@@ -84,7 +84,7 @@ void InsaneRebel2::updateMenuVirtualKeyboard() {
 
 // unlockAllChapters -- Debug mode unlock (FUN_00415CF8 lines 60-71, DAT_0047ab34=='d').
 void InsaneRebel2::unlockAllChapters() {
-	debug("Rebel2: Unlocking all chapters for testing");
+	debugC(DEBUG_INSANE, "Rebel2: Unlocking all chapters for testing");
 	for (int i = 0; i < 16; i++) {
 		_chapterUnlocked[i] = true;
 		_levelUnlocked[i] = true;
@@ -106,7 +106,7 @@ Common::String InsaneRebel2::getRandomMenuVideo() {
 
 	// Map 0-14 to A-O (case 0/default = A, 1 = B, etc.)
 	char letter = 'A' + variant;
-	debug("Rebel2: Selected menu variant %c", letter);
+	debugC(DEBUG_INSANE, "Rebel2: Selected menu variant %c", letter);
 	return Common::String::format("OPEN/O_MENU_%c.SAN", letter);
 }
 
@@ -145,7 +145,7 @@ int InsaneRebel2::processMenuInput() {
 				}
 				// Reset repeat delay counter (DAT_00459ce0)
 				_menuRepeatDelay = 3;
-				debug("Menu: Selection changed to %d (UP)", _menuSelection);
+				debugC(DEBUG_INSANE, "Menu: Selection changed to %d (UP)", _menuSelection);
 				break;
 
 			case Common::KEYCODE_DOWN:
@@ -155,7 +155,7 @@ int InsaneRebel2::processMenuInput() {
 					_menuSelection = 0;
 				}
 				_menuRepeatDelay = 3;
-				debug("Menu: Selection changed to %d (DOWN)", _menuSelection);
+				debugC(DEBUG_INSANE, "Menu: Selection changed to %d (DOWN)", _menuSelection);
 				break;
 
 			case Common::KEYCODE_RETURN:
@@ -163,14 +163,14 @@ int InsaneRebel2::processMenuInput() {
 				// Confirm selection - emulates key code 0x0d
 				if (_menuSelection >= 0 && _menuSelection < _menuItemCount) {
 					result = _menuSelection;
-					debug("Menu: Item %d selected (ENTER)", _menuSelection);
+					debugC(DEBUG_INSANE, "Menu: Item %d selected (ENTER)", _menuSelection);
 				}
 				break;
 
 			case Common::KEYCODE_ESCAPE:
 				// Synthetic custom back action - quit/back (last item)
 				result = _menuItemCount - 1;  // Select quit option
-				debug("Menu: Back action - selecting quit (item %d)", result);
+				debugC(DEBUG_INSANE, "Menu: Back action - selecting quit (item %d)", result);
 				break;
 
 			default:
@@ -187,7 +187,7 @@ int InsaneRebel2::processMenuInput() {
 				if (event.mouse.y >= itemY - itemHitTop && event.mouse.y < itemY - itemHitTop + itemHitHeight) {
 					_menuSelection = i;
 					result = _menuSelection;
-					debug("Menu: Item %d selected (mouse)", _menuSelection);
+					debugC(DEBUG_INSANE, "Menu: Item %d selected (mouse)", _menuSelection);
 					break;
 				}
 			}
@@ -202,7 +202,7 @@ int InsaneRebel2::processMenuInput() {
 							mouseY < itemY - itemHitTop + itemHitHeight - 3 * (highRes ? 2 : 1)) {
 						if (i != _menuSelection) {
 							_menuSelection = i;
-							debug(5, "Menu: Hover selection changed to %d (mouseY=%d)", _menuSelection, mouseY);
+							debugC(DEBUG_INSANE, "Menu: Hover selection changed to %d (mouseY=%d)", _menuSelection, mouseY);
 						}
 						break;
 					}
@@ -453,7 +453,7 @@ void InsaneRebel2::drawMenuOverlay(byte *renderBitmap, int pitch, int width, int
 
 	SmushPlayer *splayer = ((ScummEngine_v7 *)_vm)->_splayer;
 	if (!splayer) {
-		debug(1, "drawMenuOverlay: SmushPlayer not available for TRS strings!");
+		debugC(DEBUG_INSANE, "drawMenuOverlay: SmushPlayer not available for TRS strings!");
 		return;
 	}
 
@@ -462,7 +462,7 @@ void InsaneRebel2::drawMenuOverlay(byte *renderBitmap, int pitch, int width, int
 	for (int i = 0; i < 8; i++) {
 		menuItems[i] = splayer->getString(10 + i);
 		if (!menuItems[i] || !menuItems[i][0]) {
-			debug(1, "drawMenuOverlay: TRS string %d not found!", 10 + i);
+			debugC(DEBUG_INSANE, "drawMenuOverlay: TRS string %d not found!", 10 + i);
 			menuItems[i] = "";
 		}
 	}
@@ -595,7 +595,7 @@ void InsaneRebel2::showPauseOverlay() {
 // Returns kMenuNewGame, kMenuResumeDemo, or kMenuQuit.
 int InsaneRebel2::runMainMenu() {
 
-	debug("Rebel2: Entering main menu");
+	debugC(DEBUG_INSANE, "Rebel2: Entering main menu");
 
 	resetMenu();
 	_gameState = kStateMainMenu;
@@ -617,7 +617,7 @@ int InsaneRebel2::runMainMenu() {
 
 		// Select and play a random menu video
 		Common::String menuVideo = getRandomMenuVideo();
-		debug("Rebel2: Playing menu video: %s", menuVideo.c_str());
+		debugC(DEBUG_INSANE, "Rebel2: Playing menu video: %s", menuVideo.c_str());
 
 		// Set video flags for menu (0x20 = intro/menu flag)
 		// This tells procPostRendering we're in menu mode
@@ -635,7 +635,7 @@ int InsaneRebel2::runMainMenu() {
 		}
 
 		if (_menuInactivityTimedOut) {
-			debug("Rebel2: Main menu inactivity - resuming intro/demo loop");
+			debugC(DEBUG_INSANE, "Rebel2: Main menu inactivity - resuming intro/demo loop");
 			_menuInactivityTimedOut = false;
 			_menuInputActive = false;
 			return kMenuResumeDemo;
@@ -652,7 +652,7 @@ int InsaneRebel2::runMainMenu() {
 		_menuSelectionConfirmed = false;
 
 		// A selection was made - process it
-		debug("Rebel2: Menu video ended with selection=%d", _menuSelection);
+		debugC(DEBUG_INSANE, "Rebel2: Menu video ended with selection=%d", _menuSelection);
 
 		// Process the menu result based on current selection
 		// Menu items matching GAME.TRS indices 11-17 (FUN_004147B2):
@@ -665,34 +665,34 @@ int InsaneRebel2::runMainMenu() {
 		//   case 6 (TRS 17): Return to Launcher -> quit, returns 0
 		switch (_menuSelection) {
 		case 0:  // Start Game -> go to pilot selection
-			debug("Rebel2: Start Game selected - going to pilot selection");
+			debugC(DEBUG_INSANE, "Rebel2: Start Game selected - going to pilot selection");
 			_gameState = kStatePilotSelect;
 			_menuInputActive = false;
 			return kMenuNewGame;  // Return 2 (kMenuNewGame)
 
 		case 1:  // Options -> show options menu (FUN_00416787)
-			debug("Rebel2: Options selected");
+			debugC(DEBUG_INSANE, "Rebel2: Options selected");
 			showOptionsMenu();
 			break;
 
 		case 2:  // Calibrate Joystick
-			debug("Rebel2: Calibrate Joystick selected - no-op for modern joystick support");
+			debugC(DEBUG_INSANE, "Rebel2: Calibrate Joystick selected - no-op for modern joystick support");
 			// Modern controller support uses live keymapper actions; no explicit
 			// joystick calibration flow is required here.
 			break;
 
 		case 3:  // Continue Intro -> return to intro/demo loop
-			debug("Rebel2: Continue Intro selected - resuming intro/demo loop");
+			debugC(DEBUG_INSANE, "Rebel2: Continue Intro selected - resuming intro/demo loop");
 			_menuInputActive = false;
 			return kMenuResumeDemo;
 
 		case 4:  // Show Top Pilots -> high score display (FUN_00420116(-1))
-			debug("Rebel2: Show Top Pilots selected");
+			debugC(DEBUG_INSANE, "Rebel2: Show Top Pilots selected");
 			showTopPilots();
 			break;
 
 		case 5:  // Show Credits -> play credits video
-			debug("Rebel2: Show Credits selected - playing O_CREDIT.SAN");
+			debugC(DEBUG_INSANE, "Rebel2: Show Credits selected - playing O_CREDIT.SAN");
 			_gameState = kStateCredits;
 			_menuInputActive = false;
 			splayer->setCurVideoFlags(0x20);
@@ -703,12 +703,12 @@ int InsaneRebel2::runMainMenu() {
 			break;
 
 		case 6:  // Return to Launcher -> quit game
-			debug("Rebel2: Return to Launcher selected");
+			debugC(DEBUG_INSANE, "Rebel2: Return to Launcher selected");
 			_menuInputActive = false;
 			return kMenuQuit;
 
 		default:
-			debug("Rebel2: Unknown menu selection %d", _menuSelection);
+			debugC(DEBUG_INSANE, "Rebel2: Unknown menu selection %d", _menuSelection);
 			break;
 		}
 	}
@@ -725,7 +725,7 @@ int InsaneRebel2::runMainMenu() {
 // Returns kChapterSelectPlay, kChapterSelectBack, or kChapterSelectQuit.
 int InsaneRebel2::runChapterSelect() {
 
-	debug("Rebel2: Entering chapter selection (FUN_00415CF8)");
+	debugC(DEBUG_INSANE, "Rebel2: Entering chapter selection (FUN_00415CF8)");
 
 	// Enable menu input capture
 	_menuInputActive = true;
@@ -769,7 +769,7 @@ int InsaneRebel2::runChapterSelect() {
 	while (!_vm->shouldQuit()) {
 		_vm->_smushVideoShouldFinish = false;
 
-		debug("Rebel2: Playing chapter select background: OPEN/O_LEVEL.SAN");
+		debugC(DEBUG_INSANE, "Rebel2: Playing chapter select background: OPEN/O_LEVEL.SAN");
 
 		// Flags: 0x20 (overlay/menu rendering) | 0x08 (preserve buffer, suppress
 		// AHDR speed override). Matches original FUN_0041f4d0 parameter 8.
@@ -795,12 +795,12 @@ int InsaneRebel2::runChapterSelect() {
 		_vm->_smushVideoShouldFinish = false;
 		_menuSelectionConfirmed = false;
 
-		debug("Rebel2: Chapter selection made: %d", _chapterSelection);
+		debugC(DEBUG_INSANE, "Rebel2: Chapter selection made: %d", _chapterSelection);
 
 		// Process chapter selection (lines 134-236 of FUN_00415CF8)
 		if (_chapterSelection == 16) {
 			// BACK selected (index 16 = 17th item)
-			debug("Rebel2: BACK to main menu selected");
+			debugC(DEBUG_INSANE, "Rebel2: BACK to main menu selected");
 			setVirtualKeyboardVisible(false);
 			_menuInputActive = false;
 			return kChapterSelectBack;
@@ -810,7 +810,7 @@ int InsaneRebel2::runChapterSelect() {
 			if (_chapterUnlocked[_chapterSelection]) {
 				// Unlocked chapter ??? play it
 				_selectedChapter = _chapterSelection;
-				debug("Rebel2: Chapter %d selected (unlocked)", _selectedChapter + 1);
+				debugC(DEBUG_INSANE, "Rebel2: Chapter %d selected (unlocked)", _selectedChapter + 1);
 				setVirtualKeyboardVisible(false);
 				_menuInputActive = false;
 				return kChapterSelectPlay;
@@ -834,13 +834,13 @@ int InsaneRebel2::runChapterSelect() {
 					setBit(16 - _chapterSelection);
 					_passwordInput.clear();
 					updateMenuVirtualKeyboard();
-					debug("Rebel2: Chapter %d unlocked via password", _chapterSelection + 1);
+					debugC(DEBUG_INSANE, "Rebel2: Chapter %d unlocked via password", _chapterSelection + 1);
 					continue;  // Re-render with updated unlock state
 				}
 			}
 			// Wrong password or no password entered
 			_passwordInput.clear();
-			debug("Rebel2: Password rejected for chapter %d", _chapterSelection + 1);
+			debugC(DEBUG_INSANE, "Rebel2: Password rejected for chapter %d", _chapterSelection + 1);
 		}
 	}
 
@@ -873,7 +873,7 @@ int InsaneRebel2::processChapterSelectInput() {
 				// Update preview offset (FUN_00425170: Y = selected * -50 + 75)
 				_previewOffsetY = _chapterSelection * -50 + 75;
 				updateMenuVirtualKeyboard();
-				debug("ChapterSelect: Selection changed to %d (UP) offsetY=%d", _chapterSelection, _previewOffsetY);
+				debugC(DEBUG_INSANE, "ChapterSelect: Selection changed to %d (UP) offsetY=%d", _chapterSelection, _previewOffsetY);
 				break;
 
 			case Common::KEYCODE_DOWN:
@@ -886,14 +886,14 @@ int InsaneRebel2::processChapterSelectInput() {
 				// Update preview offset (FUN_00425170: Y = selected * -50 + 75)
 				_previewOffsetY = _chapterSelection * -50 + 75;
 				updateMenuVirtualKeyboard();
-				debug("ChapterSelect: Selection changed to %d (DOWN) offsetY=%d", _chapterSelection, _previewOffsetY);
+				debugC(DEBUG_INSANE, "ChapterSelect: Selection changed to %d (DOWN) offsetY=%d", _chapterSelection, _previewOffsetY);
 				break;
 
 			case Common::KEYCODE_RETURN:
 			case Common::KEYCODE_KP_ENTER:
 				if (_chapterSelection >= 0 && _chapterSelection < _chapterItemCount) {
 					result = _chapterSelection;
-					debug("ChapterSelect: Item %d selected (ENTER)", _chapterSelection);
+					debugC(DEBUG_INSANE, "ChapterSelect: Item %d selected (ENTER)", _chapterSelection);
 				}
 				break;
 
@@ -901,14 +901,14 @@ int InsaneRebel2::processChapterSelectInput() {
 				// Synthetic custom back action (same as selecting BACK)
 				setVirtualKeyboardVisible(false);
 				result = 16;  // BACK index
-				debug("ChapterSelect: Back action - back to menu");
+				debugC(DEBUG_INSANE, "ChapterSelect: Back action - back to menu");
 				break;
 
 			case Common::KEYCODE_BACKSPACE:
 				// Backspace for password input (line 107-112 of FUN_00415CF8)
 				if (!_passwordInput.empty()) {
 					_passwordInput.deleteLastChar();
-					debug("ChapterSelect: Password backspace, now: %s", _passwordInput.c_str());
+					debugC(DEBUG_INSANE, "ChapterSelect: Password backspace, now: %s", _passwordInput.c_str());
 				}
 				break;
 
@@ -917,7 +917,7 @@ int InsaneRebel2::processChapterSelectInput() {
 				if (event.kbd.ascii >= 0x20 && event.kbd.ascii <= 0x7E) {
 					if (_passwordInput.size() < 8) {
 						_passwordInput += (char)event.kbd.ascii;
-						debug("ChapterSelect: Password input: %s", _passwordInput.c_str());
+						debugC(DEBUG_INSANE, "ChapterSelect: Password input: %s", _passwordInput.c_str());
 					}
 				}
 				break;
@@ -940,7 +940,7 @@ int InsaneRebel2::processChapterSelectInput() {
 						_previewOffsetY = _chapterSelection * -50 + 75;
 						updateMenuVirtualKeyboard();
 						result = _chapterSelection;
-						debug("ChapterSelect: Item %d selected (mouse)", _chapterSelection);
+						debugC(DEBUG_INSANE, "ChapterSelect: Item %d selected (mouse)", _chapterSelection);
 						break;
 					}
 				}
@@ -966,7 +966,7 @@ int InsaneRebel2::processChapterSelectInput() {
 							_chapterSelection = i;
 							_previewOffsetY = _chapterSelection * -50 + 75;
 							updateMenuVirtualKeyboard();
-							debug(5, "ChapterSelect: Hover changed to %d", _chapterSelection);
+							debugC(DEBUG_INSANE, "ChapterSelect: Hover changed to %d", _chapterSelection);
 						}
 						break;
 					}
@@ -1222,7 +1222,7 @@ void InsaneRebel2::drawChapterSelectOverlay(byte *renderBitmap, int pitch, int w
 // Returns kLevelSelectPlay, kLevelSelectBack, or kLevelSelectQuit.
 int InsaneRebel2::runLevelSelect() {
 
-	debug("Rebel2: Entering pilot selection (FUN_00414A41), %d pilots loaded", _numPilots);
+	debugC(DEBUG_INSANE, "Rebel2: Entering pilot selection (FUN_00414A41), %d pilots loaded", _numPilots);
 
 	_menuInputActive = true;
 	while (!_menuEventQueue.empty())
@@ -1303,7 +1303,7 @@ int InsaneRebel2::runLevelSelect() {
 					copyPilot(pilotIndex);
 					savePilots();
 					_levelSelection = pilotIndex;
-					debug("Rebel2: Copied pilot %d, now %d pilots", pilotIndex, _numPilots);
+					debugC(DEBUG_INSANE, "Rebel2: Copied pilot %d, now %d pilots", pilotIndex, _numPilots);
 				} else {
 					deletePilot(pilotIndex);
 					savePilots();
@@ -1312,7 +1312,7 @@ int InsaneRebel2::runLevelSelect() {
 					} else {
 						_levelSelection = 0;
 					}
-					debug("Rebel2: Deleted pilot %d, %d remaining", pilotIndex, _numPilots);
+					debugC(DEBUG_INSANE, "Rebel2: Deleted pilot %d, %d remaining", pilotIndex, _numPilots);
 				}
 			}
 			_pilotMenuMode = kPilotModeSelect;
@@ -1322,7 +1322,7 @@ int InsaneRebel2::runLevelSelect() {
 		}
 
 		// --- Normal pilot menu selection ---
-		debug("Rebel2: Pilot selection: %d (numPilots=%d)", _levelSelection, _numPilots);
+		debugC(DEBUG_INSANE, "Rebel2: Pilot selection: %d (numPilots=%d)", _levelSelection, _numPilots);
 
 		if (_levelSelection < _numPilots) {
 			// Existing pilot selected ??? activate and go to chapter select
@@ -1334,7 +1334,7 @@ int InsaneRebel2::runLevelSelect() {
 				_chapterUnlocked[i] = _debugUnlockAll || (_pilots[_activePilot].damage[i] < 0xFF);
 			}
 
-			debug("Rebel2: Pilot '%s' selected (slot %d, difficulty %d)",
+			debugC(DEBUG_INSANE, "Rebel2: Pilot '%s' selected (slot %d, difficulty %d)",
 			      _pilots[_activePilot].name, _activePilot, _difficulty);
 			setVirtualKeyboardVisible(false);
 			_menuInputActive = false;
@@ -1349,7 +1349,7 @@ int InsaneRebel2::runLevelSelect() {
 				_pilotMenuMode = kPilotModeNameInput;
 				_levelItemCount = _numPilots + 4;
 				updateMenuVirtualKeyboard();
-				debug("Rebel2: NEW PILOT - entering name for slot %d", newIdx);
+				debugC(DEBUG_INSANE, "Rebel2: NEW PILOT - entering name for slot %d", newIdx);
 			}
 			continue;
 
@@ -1359,7 +1359,7 @@ int InsaneRebel2::runLevelSelect() {
 				_pilotMenuMode = kPilotModeCopySelect;
 				_levelSelection = 0;
 				_levelItemCount = _numPilots;
-				debug("Rebel2: COPY PILOT - selecting source");
+				debugC(DEBUG_INSANE, "Rebel2: COPY PILOT - selecting source");
 			}
 			continue;
 
@@ -1369,7 +1369,7 @@ int InsaneRebel2::runLevelSelect() {
 				_pilotMenuMode = kPilotModeDeleteSelect;
 				_levelSelection = 0;
 				_levelItemCount = _numPilots;
-				debug("Rebel2: DELETE PILOT - selecting target");
+				debugC(DEBUG_INSANE, "Rebel2: DELETE PILOT - selecting target");
 			}
 			continue;
 
@@ -1406,7 +1406,7 @@ int InsaneRebel2::processLevelSelectInput() {
 						setVirtualKeyboardVisible(false);
 						_menuSelectionConfirmed = true;
 						_vm->_smushVideoShouldFinish = true;
-						debug("PilotName: confirmed '%s'", _pilotNameInput.c_str());
+						debugC(DEBUG_INSANE, "PilotName: confirmed '%s'", _pilotNameInput.c_str());
 					}
 				} else if (event.kbd.keycode == Common::KEYCODE_ESCAPE) {
 					// Synthetic custom back action - cancel name entry
@@ -1416,7 +1416,7 @@ int InsaneRebel2::processLevelSelectInput() {
 					_pilotMenuMode = kPilotModeSelect;
 					_levelItemCount = _numPilots + 4;
 					updateMenuVirtualKeyboard();
-					debug("PilotName: cancelled");
+					debugC(DEBUG_INSANE, "PilotName: cancelled");
 				} else if (event.kbd.keycode == Common::KEYCODE_BACKSPACE) {
 					// Backspace ??? remove last character
 					if (_pilotNameInput.size() > 0) {
@@ -1561,7 +1561,7 @@ void InsaneRebel2::drawLevelSelectOverlay(byte *renderBitmap, int pitch, int wid
 
 	SmushPlayer *splayer = ((ScummEngine_v7 *)_vm)->_splayer;
 	if (!splayer) {
-		debug(1, "drawLevelSelectOverlay: SmushPlayer not available for TRS strings!");
+		debugC(DEBUG_INSANE, "drawLevelSelectOverlay: SmushPlayer not available for TRS strings!");
 		return;
 	}
 
@@ -1731,7 +1731,7 @@ void InsaneRebel2::insertRanking(const char *name, int32 score, int32 rating,
 }
 
 void InsaneRebel2::showTopPilots() {
-	debug("Rebel2: Showing Top Pilots screen (FUN_00420116)");
+	debugC(DEBUG_INSANE, "Rebel2: Showing Top Pilots screen (FUN_00420116)");
 
 	_menuInputActive = true;
 	while (!_menuEventQueue.empty())
@@ -1754,7 +1754,7 @@ void InsaneRebel2::showTopPilots() {
 	_gameState = kStateMainMenu;
 	_menuInputActive = true;
 
-	debug("Rebel2: Top Pilots screen finished");
+	debugC(DEBUG_INSANE, "Rebel2: Top Pilots screen finished");
 }
 
 void InsaneRebel2::drawTopPilotsOverlay(byte *renderBitmap, int pitch, int width, int height) {
@@ -1838,7 +1838,7 @@ void InsaneRebel2::drawTopPilotsOverlay(byte *renderBitmap, int pitch, int width
 
 // showOptionsMenu -- Options menu loop (FUN_00416787).
 void InsaneRebel2::showOptionsMenu() {
-	debug("Rebel2: Showing Options menu (FUN_00416787)");
+	debugC(DEBUG_INSANE, "Rebel2: Showing Options menu (FUN_00416787)");
 
 	_menuInputActive = true;
 	while (!_menuEventQueue.empty())
@@ -1864,7 +1864,7 @@ void InsaneRebel2::showOptionsMenu() {
 	_gameState = kStateMainMenu;
 	_menuInputActive = true;
 
-	debug("Rebel2: Options menu finished");
+	debugC(DEBUG_INSANE, "Rebel2: Options menu finished");
 }
 
 int InsaneRebel2::processOptionsInput() {
diff --git a/engines/scumm/insane/rebel2/rebel.cpp b/engines/scumm/insane/rebel2/rebel.cpp
index 25c7d9f7a23..3b98f147377 100644
--- a/engines/scumm/insane/rebel2/rebel.cpp
+++ b/engines/scumm/insane/rebel2/rebel.cpp
@@ -689,7 +689,7 @@ bool InsaneRebel2::notifyEvent(const Common::Event &event) {
 			// Retail DAT_0047ab60: Yoda-mode Movie Mode skips playable
 			// sections and keeps the story/cutscene sequence moving.
 			_rebelMovieMode = !_rebelMovieMode;
-			debug("Rebel2: Movie mode %s", _rebelMovieMode ? "enabled" : "disabled");
+			debugC(DEBUG_INSANE, "Rebel2: Movie mode %s", _rebelMovieMode ? "enabled" : "disabled");
 			if (_rebelMovieMode && splayer && _gameState == kStateGameplay && _rebelHandler != 0)
 				_vm->_smushVideoShouldFinish = true;
 			return true;
@@ -698,7 +698,7 @@ bool InsaneRebel2::notifyEvent(const Common::Event &event) {
 			// Retail DAT_0047ab64: Yoda-mode Auto Play makes gameplay
 			// computer controlled.
 			_rebelAutoPlay = !_rebelAutoPlay;
-			debug("Rebel2: Auto play %s", _rebelAutoPlay ? "enabled" : "disabled");
+			debugC(DEBUG_INSANE, "Rebel2: Auto play %s", _rebelAutoPlay ? "enabled" : "disabled");
 			return true;
 
 		default:
@@ -901,11 +901,11 @@ bool InsaneRebel2::notifyEvent(const Common::Event &event) {
 			}
 
 			if (_gameState == kStateGameplay && _rebelHandler != 0) {
-				debug("Rebel2: Skip/back action ignored during gameplay");
+				debugC(DEBUG_INSANE, "Rebel2: Skip/back action ignored during gameplay");
 				return true;
 			}
 
-			debug("Rebel2: Skip/back action - skipping video");
+			debugC(DEBUG_INSANE, "Rebel2: Skip/back action - skipping video");
 			_vm->_smushVideoShouldFinish = true;
 			return true;
 		}
@@ -915,7 +915,7 @@ bool InsaneRebel2::notifyEvent(const Common::Event &event) {
 				return true;
 
 			if (_menuInputActive && menuState) {
-				debug("Rebel2: Back/menu action in menu - opening ScummVM menu");
+				debugC(DEBUG_INSANE, "Rebel2: Back/menu action in menu - opening ScummVM menu");
 				openMenuMainMenu(splayer);
 				return true;
 			}
@@ -924,23 +924,23 @@ bool InsaneRebel2::notifyEvent(const Common::Event &event) {
 				if (_lastGameplayMenuCloseTime != 0) {
 					const uint32 elapsedSinceMenuClose = _vm->_system->getMillis() - _lastGameplayMenuCloseTime;
 					if (elapsedSinceMenuClose < 500) {
-						debug("Rebel2: Ignoring repeated gameplay menu action (%u ms)", elapsedSinceMenuClose);
+						debugC(DEBUG_INSANE, "Rebel2: Ignoring repeated gameplay menu action (%u ms)", elapsedSinceMenuClose);
 						return true;
 					}
 				}
 
-				debug("Rebel2: Back/menu action during gameplay - opening ScummVM menu");
+				debugC(DEBUG_INSANE, "Rebel2: Back/menu action during gameplay - opening ScummVM menu");
 				openGameplayMainMenu(splayer);
 				return true;
 			}
 
-			debug("Rebel2: Back/menu action - skipping video");
+			debugC(DEBUG_INSANE, "Rebel2: Back/menu action - skipping video");
 			_vm->_smushVideoShouldFinish = true;
 			return true;
 		}
 
 		if (pressed && splayer && splayer->_paused && _gameState == kStateGameplay) {
-			debug("Rebel2: Joystick action while paused - unpausing");
+			debugC(DEBUG_INSANE, "Rebel2: Joystick action while paused - unpausing");
 			if (_pauseOverlayActive) {
 				_vm->_system->getPaletteManager()->setPalette(_savedPausePalette, 0, 256);
 				_pauseOverlayActive = false;
@@ -1032,11 +1032,11 @@ bool InsaneRebel2::notifyEvent(const Common::Event &event) {
 			(event.type == Common::EVENT_MAINMENU ||
 			 (event.type == Common::EVENT_KEYDOWN && event.kbd.keycode == Common::KEYCODE_ESCAPE))) {
 		if (event.type == Common::EVENT_KEYDOWN && event.kbdRepeat) {
-			debug("Rebel2: Ignoring repeated ESC keydown in menu");
+			debugC(DEBUG_INSANE, "Rebel2: Ignoring repeated ESC keydown in menu");
 			return true;
 		}
 
-		debug("Rebel2: Opening ScummVM menu from menu state");
+		debugC(DEBUG_INSANE, "Rebel2: Opening ScummVM menu from menu state");
 		openMenuMainMenu(splayer);
 		return true;
 	}
@@ -1051,14 +1051,14 @@ bool InsaneRebel2::notifyEvent(const Common::Event &event) {
 	if (gameplayMenuTrigger && _gameState == kStateGameplay && _lastGameplayMenuCloseTime != 0) {
 		const uint32 elapsedSinceMenuClose = _vm->_system->getMillis() - _lastGameplayMenuCloseTime;
 		if (elapsedSinceMenuClose < 500) {
-			debug("Rebel2: Ignoring repeated gameplay menu trigger (%u ms)", elapsedSinceMenuClose);
+			debugC(DEBUG_INSANE, "Rebel2: Ignoring repeated gameplay menu trigger (%u ms)", elapsedSinceMenuClose);
 			return true;
 		}
 	}
 
 	if (event.type == Common::EVENT_MAINMENU && splayer &&
 			_gameState == kStateGameplay && _rebelHandler != 0) {
-		debug("Rebel2: Main menu action during gameplay - opening ScummVM menu");
+		debugC(DEBUG_INSANE, "Rebel2: Main menu action during gameplay - opening ScummVM menu");
 		openGameplayMainMenu(splayer);
 		return true;
 	}
@@ -1068,7 +1068,7 @@ bool InsaneRebel2::notifyEvent(const Common::Event &event) {
 		// leaving the key repeat source to synthesize another ESC immediately after
 		// returning to gameplay. Treat repeats as part of the original press.
 		if (event.kbd.keycode == Common::KEYCODE_ESCAPE && event.kbdRepeat) {
-			debug("Rebel2: Ignoring repeated ESC keydown");
+			debugC(DEBUG_INSANE, "Rebel2: Ignoring repeated ESC keydown");
 			return true;
 		}
 		if (_menuInputActive && event.kbdRepeat &&
@@ -1076,14 +1076,14 @@ bool InsaneRebel2::notifyEvent(const Common::Event &event) {
 				 event.kbd.keycode == Common::KEYCODE_DOWN ||
 				 event.kbd.keycode == Common::KEYCODE_LEFT ||
 				 event.kbd.keycode == Common::KEYCODE_RIGHT)) {
-			debug("Rebel2: Ignoring repeated menu direction keydown");
+			debugC(DEBUG_INSANE, "Rebel2: Ignoring repeated menu direction keydown");
 			return true;
 		}
 
 		// When paused during gameplay, ANY key unpauses (FUN_405A21 line 360-365).
 		// ESC additionally opens the ScummVM menu (original: quit key exits level).
 		if (splayer && splayer->_paused && _gameState == kStateGameplay) {
-			debug("Rebel2: Key pressed while paused - unpausing");
+			debugC(DEBUG_INSANE, "Rebel2: Key pressed while paused - unpausing");
 			// Restore the original palette saved by showPauseOverlay
 			if (_pauseOverlayActive) {
 				_vm->_system->getPaletteManager()->setPalette(_savedPausePalette, 0, 256);
@@ -1091,7 +1091,7 @@ bool InsaneRebel2::notifyEvent(const Common::Event &event) {
 			}
 			splayer->unpause();
 			if (event.kbd.keycode == Common::KEYCODE_ESCAPE && _rebelHandler != 0) {
-				debug("Rebel2: ESC during pause - opening ScummVM menu");
+				debugC(DEBUG_INSANE, "Rebel2: ESC during pause - opening ScummVM menu");
 				openGameplayMainMenu(splayer);
 			}
 			return true;
@@ -1106,11 +1106,11 @@ bool InsaneRebel2::notifyEvent(const Common::Event &event) {
 			if (splayer) {
 				if (_gameState == kStateGameplay && _rebelHandler != 0) {
 					// During active gameplay (handler != 0): pause and open ScummVM menu.
-					debug("Rebel2: ESC pressed during gameplay - opening ScummVM menu");
+					debugC(DEBUG_INSANE, "Rebel2: ESC pressed during gameplay - opening ScummVM menu");
 					openGameplayMainMenu(splayer);
 				} else {
 					// During cutscenes/intros/mission briefings: skip video
-					debug("Rebel2: ESC pressed - skipping video");
+					debugC(DEBUG_INSANE, "Rebel2: ESC pressed - skipping video");
 					_vm->_smushVideoShouldFinish = true;
 				}
 				return true;  // Consume the event
@@ -1121,7 +1121,7 @@ bool InsaneRebel2::notifyEvent(const Common::Event &event) {
 			// SPACE pauses during gameplay (FUN_405A21).
 			// Unpausing is handled above (any key while paused).
 			if (splayer && _gameState == kStateGameplay && !splayer->_paused) {
-				debug("Rebel2: SPACE pressed - pausing");
+				debugC(DEBUG_INSANE, "Rebel2: SPACE pressed - pausing");
 				splayer->pause();
 				showPauseOverlay();
 				return true;
@@ -1135,7 +1135,7 @@ bool InsaneRebel2::notifyEvent(const Common::Event &event) {
 			    _rebelHandler != 0 &&
 			    event.kbd.hasFlags(Common::KBD_SHIFT)) {
 				_skipSectionRequested = true;
-				debug("Rebel2: Shift+S pressed - requesting gameplay section skip");
+				debugC(DEBUG_INSANE, "Rebel2: Shift+S pressed - requesting gameplay section skip");
 				_vm->_smushVideoShouldFinish = true;
 				return true;  // Consume the event
 			}
@@ -1150,10 +1150,10 @@ bool InsaneRebel2::notifyEvent(const Common::Event &event) {
 				if (!_noDamage) {
 					_playerDamage = 255;
 					_playerShield = 0;
-					debug("Rebel2: Shift+D pressed - forcing player death");
+					debugC(DEBUG_INSANE, "Rebel2: Shift+D pressed - forcing player death");
 					_vm->_smushVideoShouldFinish = true;
 				} else {
-					debug("Rebel2: Shift+D pressed - no damage mode prevents forced death");
+					debugC(DEBUG_INSANE, "Rebel2: Shift+D pressed - no damage mode prevents forced death");
 				}
 				return true;  // Consume the event
 			}
@@ -1385,13 +1385,13 @@ void InsaneRebel2::addScore(int points) {
 			// Award bonus life
 			_playerLives++;
 			// TODO: Play bonus life sound (FUN_0041189e(5, 0, 0x7f, 0, 0))
-			debug("Rebel2: BONUS LIFE! Score crossed %d threshold. Lives=%d", threshold, _playerLives);
+			debugC(DEBUG_INSANE, "Rebel2: BONUS LIFE! Score crossed %d threshold. Lives=%d", threshold, _playerLives);
 		}
 	}
 
 	// Add points to score
 	_playerScore += points;
-	debug("Rebel2: Score +%d = %d", points, _playerScore);
+	debugC(DEBUG_INSANE, "Rebel2: Score +%d = %d", points, _playerScore);
 }
 
 // Render score text to HUD (part of FUN_0041c012)
@@ -1478,7 +1478,7 @@ bool InsaneRebel2::loadPilots() {
 		_numPilots = i + 1;
 	}
 
-	debug("Rebel2: Loaded %d pilot(s)", _numPilots);
+	debugC(DEBUG_INSANE, "Rebel2: Loaded %d pilot(s)", _numPilots);
 	return _numPilots > 0;
 }
 
@@ -1520,7 +1520,7 @@ bool InsaneRebel2::savePilots() {
 		_vm->_saveFileMan->removeSavefile(filename);
 	}
 
-	debug("Rebel2: Saved %d pilot(s)", _numPilots);
+	debugC(DEBUG_INSANE, "Rebel2: Saved %d pilot(s)", _numPilots);
 	return ok;
 }
 
@@ -1671,7 +1671,7 @@ int32 InsaneRebel2::processMouse() {
 			mousePos = getGameplayAimPoint();
 		}
 		Common::Point gameplayAim = getGameplayAimPoint();
-		debug("Rebel2 Click: Mouse=(%d,%d) Target=(%d,%d) Enemies=%d",
+		debugC(DEBUG_INSANE, "Rebel2 Click: Mouse=(%d,%d) Target=(%d,%d) Enemies=%d",
 			gameplayAim.x, gameplayAim.y, mousePos.x, mousePos.y, _enemies.size());
 
 		// Spawn visual shot immediately
@@ -1690,7 +1690,7 @@ int32 InsaneRebel2::processMouse() {
 		// Check for hit on any active enemy
 		Common::List<enemy>::iterator it;
 		for (it = _enemies.begin(); it != _enemies.end(); ++it) {
-			debug("  Enemy ID=%d active=%d destroyed=%d rect=(%d,%d)-(%d,%d) contains=%d",
+			debugC(DEBUG_INSANE, "  Enemy ID=%d active=%d destroyed=%d rect=(%d,%d)-(%d,%d) contains=%d",
 				it->id, it->active, it->destroyed,
 				it->rect.left, it->rect.top, it->rect.right, it->rect.bottom,
 				it->rect.contains(worldMousePos));
@@ -1699,7 +1699,7 @@ int32 InsaneRebel2::processMouse() {
 				// Enemy hit!
 				it->active = false;
 				it->destroyed = true;  // Mark as destroyed so IACT won't re-activate
-				debug("Rebel2: HIT enemy ID=%d type=%d at (%d,%d) - Rect: (%d,%d)-(%d,%d)",
+				debugC(DEBUG_INSANE, "Rebel2: HIT enemy ID=%d type=%d at (%d,%d) - Rect: (%d,%d)-(%d,%d)",
 					it->id, it->type, mousePos.x, mousePos.y,
 					it->rect.left, it->rect.top, it->rect.right, it->rect.bottom);
 
@@ -1749,7 +1749,7 @@ int32 InsaneRebel2::processMouse() {
 				// This tracks which enemy GROUPS have been killed in this wave
 				if (it->type > 0 && it->type < 32) {
 					_rebelWaveState |= (1 << it->type);
-					debug("Rebel2: Wave state updated: 0x%x (set bit %d)", _rebelWaveState, it->type);
+					debugC(DEBUG_INSANE, "Rebel2: Wave state updated: 0x%x (set bit %d)", _rebelWaveState, it->type);
 				}
 
 				// Increment kill counter (DAT_0047ab88)
@@ -1766,17 +1766,17 @@ int32 InsaneRebel2::processMouse() {
 					// Slot 2: Enable (Explosion?)
 					if (_rebelLinks[id][2] != 0) {
 						clearBit(_rebelLinks[id][2]);
-						debug("Rebel2: Enabled dependency Slot 2 (ID=%d) for Parent %d", _rebelLinks[id][2], id);
+						debugC(DEBUG_INSANE, "Rebel2: Enabled dependency Slot 2 (ID=%d) for Parent %d", _rebelLinks[id][2], id);
 					}
 					// Slot 1: Enable (Explosion?)
 					if (_rebelLinks[id][1] != 0) {
 						clearBit(_rebelLinks[id][1]);
-						debug("Rebel2: Enabled dependency Slot 1 (ID=%d) for Parent %d", _rebelLinks[id][1], id);
+						debugC(DEBUG_INSANE, "Rebel2: Enabled dependency Slot 1 (ID=%d) for Parent %d", _rebelLinks[id][1], id);
 					}
 					// Slot 0: Disable (Shots?)
 					if (_rebelLinks[id][0] != 0) {
 						setBit(_rebelLinks[id][0]);
-						debug("Rebel2: Disabled dependency Slot 0 (ID=%d) for Parent %d", _rebelLinks[id][0], id);
+						debugC(DEBUG_INSANE, "Rebel2: Disabled dependency Slot 0 (ID=%d) for Parent %d", _rebelLinks[id][0], id);
 					}
 				}
 
@@ -2061,7 +2061,7 @@ void InsaneRebel2::procSKIP(int32 subSize, Common::SeekableReadStream &b) {
 		if (bit1) {
 			_player->_skipNext = true;
 		}
-		debug("Rebel2 SKIP: single ID=%d bit=%d skip=%d frame=%d", par1, bit1 ? 1 : 0, _player->_skipNext ? 1 : 0, _player->_frame);
+		debugC(DEBUG_INSANE, "Rebel2 SKIP: single ID=%d bit=%d skip=%d frame=%d", par1, bit1 ? 1 : 0, _player->_skipNext ? 1 : 0, _player->_frame);
 	} else {
 		// Dual ID mode: skip if bits are different (XOR logic)
 		bool bit1 = isBitSet(par1);
@@ -2069,7 +2069,7 @@ void InsaneRebel2::procSKIP(int32 subSize, Common::SeekableReadStream &b) {
 		if (bit1 != bit2) {
 			_player->_skipNext = true;
 		}
-		debug("Rebel2 SKIP: dual ID1=%d(bit=%d) ID2=%d(bit=%d) skip=%d frame=%d", par1, bit1 ? 1 : 0, par2, bit2 ? 1 : 0, _player->_skipNext ? 1 : 0, _player->_frame);
+		debugC(DEBUG_INSANE, "Rebel2 SKIP: dual ID1=%d(bit=%d) ID2=%d(bit=%d) skip=%d frame=%d", par1, bit1 ? 1 : 0, par2, bit2 ? 1 : 0, _player->_skipNext ? 1 : 0, _player->_frame);
 	}
 }
 
diff --git a/engines/scumm/insane/rebel2/render.cpp b/engines/scumm/insane/rebel2/render.cpp
index 31088a4d446..a33ef1aa849 100644
--- a/engines/scumm/insane/rebel2/render.cpp
+++ b/engines/scumm/insane/rebel2/render.cpp
@@ -168,7 +168,7 @@ static bool readEmbeddedSanChunkHeader(Common::SeekableReadStream &stream, int64
 
 	const int64 dataStart = stream.pos();
 	if ((int64)chunkSize > containerEnd - dataStart) {
-		debug("Rebel2: Truncated embedded SAN %s chunk 0x%08X at %lld: size=%u, remaining=%lld",
+		debugC(DEBUG_INSANE, "Rebel2: Truncated embedded SAN %s chunk 0x%08X at %lld: size=%u, remaining=%lld",
 			context, tag, headerPos, chunkSize, containerEnd - dataStart);
 		return false;
 	}
@@ -204,12 +204,12 @@ void InsaneRebel2::renderEmbeddedFrame(byte *renderBitmap, const EmbeddedSanFram
 	}
 	if (_rebelHandler == 0x19 && (userId == 6 || userId == 7)) {
 		skipImmediateDraw = false;
-		debug("Rebel2: Handler 25 static overlay userId=%d - forcing immediate draw", userId);
+		debugC(DEBUG_INSANE, "Rebel2: Handler 25 static overlay userId=%d - forcing immediate draw", userId);
 	}
 
 	if (!frame.valid || !renderBitmap || skipImmediateDraw) {
 		if (skipImmediateDraw && frame.valid) {
-			debug("Rebel2: Skipped immediate draw for Handler %d HUD %d (will render during post-processing)",
+			debugC(DEBUG_INSANE, "Rebel2: Skipped immediate draw for Handler %d HUD %d (will render during post-processing)",
 				_rebelHandler, userId);
 		}
 		return;
@@ -220,7 +220,7 @@ void InsaneRebel2::renderEmbeddedFrame(byte *renderBitmap, const EmbeddedSanFram
 
 	blitEmbeddedFrameRegion(renderBitmap, pitch, pitch, bufHeight, frame,
 		frame.renderX, frame.renderY, 0, 0, frame.width, frame.height);
-	debug("Rebel2: Rendered embedded HUD %d at (%d,%d)", userId, frame.renderX, frame.renderY);
+	debugC(DEBUG_INSANE, "Rebel2: Rendered embedded HUD %d at (%d,%d)", userId, frame.renderX, frame.renderY);
 }
 
 void InsaneRebel2::drawHandler25CorridorOverlay(byte *renderBitmap) {
@@ -269,7 +269,7 @@ void InsaneRebel2::drawHandler25CorridorOverlay(byte *renderBitmap) {
 			   drawWidth);
 	}
 
-	debug("Rebel2 Handler25: Corridor overlay drawn at (%d,%d) size(%d,%d)",
+	debugC(DEBUG_INSANE, "Rebel2 Handler25: Corridor overlay drawn at (%d,%d) size(%d,%d)",
 		_rebelViewOffsetX, _rebelViewOffsetY, corridorOverlay.width, corridorOverlay.height);
 }
 
@@ -282,7 +282,7 @@ void InsaneRebel2::drawHandler25CorridorOverlay(byte *renderBitmap) {
 void InsaneRebel2::loadEmbeddedSan(int userId, byte *animData, int32 size, byte *renderBitmap) {
 	// Validate userId - Level 3 uses slots 0-11, allow up to 15 for safety
 	if (userId < 0 || userId > 15 || !animData || size < 8) {
-		debug("Rebel2: Invalid embedded SAN: userId=%d, size=%d", userId, size);
+		debugC(DEBUG_INSANE, "Rebel2: Invalid embedded SAN: userId=%d, size=%d", userId, size);
 		return;
 	}
 
@@ -292,7 +292,7 @@ void InsaneRebel2::loadEmbeddedSan(int userId, byte *animData, int32 size, byte
 	// Read ANIM header
 	uint32 animTag = stream.readUint32BE();
 	if (animTag != MKTAG('A','N','I','M')) {
-		debug("Rebel2: Embedded SAN missing ANIM tag, got 0x%08X", animTag);
+		debugC(DEBUG_INSANE, "Rebel2: Embedded SAN missing ANIM tag, got 0x%08X", animTag);
 		return;
 	}
 	uint32 animSize = stream.readUint32BE();
@@ -300,9 +300,9 @@ void InsaneRebel2::loadEmbeddedSan(int userId, byte *animData, int32 size, byte
 	if ((int64)animSize <= streamEnd - 8) {
 		animEnd = 8 + (int64)animSize;
 	} else {
-		debug("Rebel2: Embedded ANIM truncated: reported size=%u, actual=%lld", animSize, streamEnd - 8);
+		debugC(DEBUG_INSANE, "Rebel2: Embedded ANIM truncated: reported size=%u, actual=%lld", animSize, streamEnd - 8);
 	}
-	debug("Rebel2: Parsing embedded ANIM: userId=%d, reported size=%u, actual=%lld", userId, animSize, streamEnd - 8);
+	debugC(DEBUG_INSANE, "Rebel2: Parsing embedded ANIM: userId=%d, reported size=%u, actual=%lld", userId, animSize, streamEnd - 8);
 
 	// Iterate through chunks to find FRME -> FOBJ
 	while (!stream.eos() && stream.pos() + 8 <= animEnd) {
@@ -325,7 +325,7 @@ void InsaneRebel2::loadEmbeddedSan(int userId, byte *animData, int32 size, byte
 
 				if (subTag == MKTAG('F','O','B','J')) {
 					if (subSize < 14) {
-						debug("Rebel2: Embedded FOBJ too small: userId=%d, size=%u", userId, subSize);
+						debugC(DEBUG_INSANE, "Rebel2: Embedded FOBJ too small: userId=%d, size=%u", userId, subSize);
 						stream.seek(nextSubPos);
 						continue;
 					}
@@ -339,13 +339,13 @@ void InsaneRebel2::loadEmbeddedSan(int userId, byte *animData, int32 size, byte
 					stream.readUint16LE();  // unknown
 					stream.readUint16LE();  // unknown
 
-					debug("Rebel2: Embedded HUD frame: userId=%d, %dx%d at (%d,%d), codec=%d",
+					debugC(DEBUG_INSANE, "Rebel2: Embedded HUD frame: userId=%d, %dx%d at (%d,%d), codec=%d",
 						userId, width, height, left, top, codec);
 
 					// High-resolution HUD frames are used when the RA2 high-res option
 					// selects a 640x400 virtual screen. Keep skipping them in low-res mode.
 					if (!isHiRes() && (width > 400 || height > 250)) {
-						debug("Rebel2: SKIPPING high-res embedded frame: userId=%d, %dx%d (exceeds 400x250)",
+						debugC(DEBUG_INSANE, "Rebel2: SKIPPING high-res embedded frame: userId=%d, %dx%d (exceeds 400x250)",
 							userId, width, height);
 						stream.seek(nextSubPos);
 						continue;
@@ -383,7 +383,7 @@ void InsaneRebel2::loadEmbeddedSan(int userId, byte *animData, int32 size, byte
 							}
 							uint32 bytesRead = stream.read(fobjData, dataSize);
 							if (bytesRead != (uint32)dataSize) {
-								debug("Rebel2: Short embedded FOBJ read: got %u of %d bytes", bytesRead, dataSize);
+								debugC(DEBUG_INSANE, "Rebel2: Short embedded FOBJ read: got %u of %d bytes", bytesRead, dataSize);
 								free(fobjData);
 								return;
 							}
@@ -393,17 +393,17 @@ void InsaneRebel2::loadEmbeddedSan(int userId, byte *animData, int32 size, byte
 								// Codec 1/3: RLE - use existing decoder (FUN_0042C590)
 								smushDecodeRLE(frame.pixels, fobjData, 0, 0, width, height, width);
 								frame.valid = true;
-								debug("Rebel2: Decoded embedded HUD (codec %d/RLE): %dx%d", codec, width, height);
+								debugC(DEBUG_INSANE, "Rebel2: Decoded embedded HUD (codec %d/RLE): %dx%d", codec, width, height);
 							} else if (codec == 20) {
 								// Codec 20: Uncompressed (FUN_0042C400)
 								smushDecodeUncompressed(frame.pixels, fobjData, 0, 0, width, height, width);
 								frame.valid = true;
-								debug("Rebel2: Decoded embedded HUD (codec 20/raw): %dx%d", width, height);
+								debugC(DEBUG_INSANE, "Rebel2: Decoded embedded HUD (codec 20/raw): %dx%d", width, height);
 							} else if (codec == 21 || codec == 44) {
 								// Codec 21/44: Line update (FUN_0042BD60)
 								smushDecodeLineUpdate(frame.pixels, fobjData, 0, 0, width, height, width, dataSize);
 								frame.valid = true;
-								debug("Rebel2: Decoded embedded HUD (codec %d/line update): %dx%d", codec, width, height);
+								debugC(DEBUG_INSANE, "Rebel2: Decoded embedded HUD (codec %d/line update): %dx%d", codec, width, height);
 							} else if (codec == 45) {
 								// Codec 45: blur/wipe mask (FUN_0042B460 -> FUN_0042B530 -> FUN_0042DDF0)
 								smushDecodeRA2Blur(frame.pixels, fobjData, 0, 0, width, height, width, dataSize,
@@ -413,16 +413,16 @@ void InsaneRebel2::loadEmbeddedSan(int userId, byte *animData, int32 size, byte
 								// Codec 23: Skip/copy with embedded RLE (FUN_0042BBF0)
 								smushDecodeSkipRLE(frame.pixels, fobjData, 0, 0, width, height, width, dataSize);
 								frame.valid = true;
-								debug("Rebel2: Decoded embedded HUD (codec 23/skip-RLE): %dx%d", width, height);
+								debugC(DEBUG_INSANE, "Rebel2: Decoded embedded HUD (codec 23/skip-RLE): %dx%d", width, height);
 							} else {
-								debug("Rebel2: Unsupported embedded HUD codec %d", codec);
+								debugC(DEBUG_INSANE, "Rebel2: Unsupported embedded HUD codec %d", codec);
 								frame.valid = false;
 							}
 
 							// Count non-zero pixels to verify frame has content
 							if (frame.valid) {
 								int nonZeroPixels = countEmbeddedFramePixels(frame);
-								debug("Rebel2: Frame userId=%d has %d non-zero pixels (%d%%)",
+								debugC(DEBUG_INSANE, "Rebel2: Frame userId=%d has %d non-zero pixels (%d%%)",
 									userId, nonZeroPixels, (nonZeroPixels * 100) / (width * height));
 							}
 
@@ -447,7 +447,7 @@ void InsaneRebel2::loadEmbeddedSan(int userId, byte *animData, int32 size, byte
 		}
 	}
 
-	debug("Rebel2: No FOBJ found in embedded SAN userId=%d", userId);
+	debugC(DEBUG_INSANE, "Rebel2: No FOBJ found in embedded SAN userId=%d", userId);
 }
 
 // Spawn explosion into the shared 5-slot system.
@@ -611,7 +611,7 @@ void InsaneRebel2::spawnHandler25Shot(int x, int y) {
 				_turretShots[i].gunY = _rebelViewOffset2Y + 140 + _viewY;
 			}
 
-			debug("Rebel2 Handler25: Spawned shot %d target (%d,%d) gun (%d,%d)",
+			debugC(DEBUG_INSANE, "Rebel2 Handler25: Spawned shot %d target (%d,%d) gun (%d,%d)",
 				i, _turretShots[i].targetX, _turretShots[i].targetY,
 				_turretShots[i].gunX, _turretShots[i].gunY);
 			break;
@@ -1147,7 +1147,7 @@ void InsaneRebel2::initLaserTexture(NutRenderer *nut, int spriteIdx) {
 		}
 	}
 
-	debug("Rebel2: Initialized laser texture %dx%d from sprite %d (xoff=%d yoff=%d src=%dx%d)",
+	debugC(DEBUG_INSANE, "Rebel2: Initialized laser texture %dx%d from sprite %d (xoff=%d yoff=%d src=%dx%d)",
 	      texWidth, texHeight, spriteIdx, srcXOff, srcYOff, srcWidth, srcHeight);
 }
 
@@ -1514,7 +1514,7 @@ void InsaneRebel2::drawLaserBeam(byte *dst, int pitch, int width, int height,
 	int edgeClipRight = CLIP<int>(clipRight - 1, 1, width - 2);
 	int edgeClipBottom = CLIP<int>(clipBottom - 1, 1, height - 2);
 
-	debug(5, "Rebel2: drawLaserBeam gun(%d,%d) tgt(%d,%d) start(%d,%d) end(%d,%d) anim=%d/%d ws=%d hs=%d th=%d",
+	debugC(DEBUG_INSANE, "Rebel2: drawLaserBeam gun(%d,%d) tgt(%d,%d) start(%d,%d) end(%d,%d) anim=%d/%d ws=%d hs=%d th=%d",
 		gunX, gunY, targetX, targetY, startX, startY, endX, endY,
 		animFrame, maxFrames, widthScale, heightScale, thickness);
 
@@ -1660,16 +1660,16 @@ void InsaneRebel2::registerCollisionZone(Common::SeekableReadStream &b, int16 su
 	// Register zone into appropriate table based on sub-opcode
 	if (subOpcode == 0x0D && _primaryZoneCount < kMaxCollisionZones) {
 		_primaryZones[_primaryZoneCount++] = zone;
-		debug("Rebel2: Registered PRIMARY zone %d: filter=%d fields=[%d,%d] quad=(%d,%d)-(%d,%d)-(%d,%d)-(%d,%d)",
+		debugC(DEBUG_INSANE, "Rebel2: Registered PRIMARY zone %d: filter=%d fields=[%d,%d] quad=(%d,%d)-(%d,%d)-(%d,%d)-(%d,%d)",
 			_primaryZoneCount - 1, par4, field1, field2,
 			x1, y1, x2, y2, x3, y3, x4, y4);
 	} else if (subOpcode == 0x0E && _secondaryZoneCount < kMaxCollisionZones) {
 		_secondaryZones[_secondaryZoneCount++] = zone;
-		debug("Rebel2: Registered SECONDARY zone %d: filter=%d fields=[%d,%d] quad=(%d,%d)-(%d,%d)-(%d,%d)-(%d,%d)",
+		debugC(DEBUG_INSANE, "Rebel2: Registered SECONDARY zone %d: filter=%d fields=[%d,%d] quad=(%d,%d)-(%d,%d)-(%d,%d)-(%d,%d)",
 			_secondaryZoneCount - 1, par4, field1, field2,
 			x1, y1, x2, y2, x3, y3, x4, y4);
 	} else {
-		debug("Rebel2: WARNING - Could not register zone (subOpcode=%d, primary=%d, secondary=%d)",
+		debugC(DEBUG_INSANE, "Rebel2: WARNING - Could not register zone (subOpcode=%d, primary=%d, secondary=%d)",
 			subOpcode, _primaryZoneCount, _secondaryZoneCount);
 	}
 }
@@ -1793,7 +1793,7 @@ void InsaneRebel2::checkCollisionZones(byte *renderBitmap, int pitch, int width,
 				int collisionDamage = (dparams.dodgeDamage >= 0) ? dparams.dodgeDamage : 0;
 
 				if (applyPlayerDamage(collisionDamage)) {
-					debug("Rebel2: COLLISION damage! zone=%d aim=(%d,%d) damage=%d total=%d",
+					debugC(DEBUG_INSANE, "Rebel2: COLLISION damage! zone=%d aim=(%d,%d) damage=%d total=%d",
 						i, aimX, aimY, collisionDamage, _playerDamage);
 				}
 				// Visual effect ??? FUN_00420515 (palette flash)
@@ -1899,7 +1899,7 @@ void InsaneRebel2::applyHandler7ObstacleHit(const InsaneRebel2::CollisionZone &z
 		initDamageFlash();
 	// Pan based on ship X position relative to screen center.
 	playSfx(1, 127, CLIP((_flyShipScreenX - 212) * 127 / 160, -127, 127));
-	debug("Rebel2: Handler7 Mode0/2 OBSTACLE HIT zone=%d ship=(%d,%d) damage=%d",
+	debugC(DEBUG_INSANE, "Rebel2: Handler7 Mode0/2 OBSTACLE HIT zone=%d ship=(%d,%d) damage=%d",
 		zoneIndex, _flyShipScreenX, _flyShipScreenY, collisionDamage);
 }
 
@@ -1978,7 +1978,7 @@ void InsaneRebel2::checkHandler7TopBoundary(const InsaneRebel2::CollisionZone &z
 			// Ship above top wall - push down.
 			const bool damageApplied = applyHandler7WallDamage(wallDamage);
 			if (damageApplied) {
-				debug("Rebel2: Handler7 Mode1/3 TOP WALL ship=(%d,%d) edgeY=%d damage=%d",
+				debugC(DEBUG_INSANE, "Rebel2: Handler7 Mode1/3 TOP WALL ship=(%d,%d) edgeY=%d damage=%d",
 					_flyShipScreenX, _flyShipScreenY, edgeY, wallDamage);
 			}
 			_spaceShotDirection = 2;  // Direction: pushed down
@@ -2004,7 +2004,7 @@ void InsaneRebel2::checkHandler7BottomBoundary(const InsaneRebel2::CollisionZone
 			// Ship below bottom wall - push up.
 			const bool damageApplied = applyHandler7WallDamage(wallDamage);
 			if (damageApplied) {
-				debug("Rebel2: Handler7 Mode1/3 BOTTOM WALL ship=(%d,%d) edgeY=%d damage=%d",
+				debugC(DEBUG_INSANE, "Rebel2: Handler7 Mode1/3 BOTTOM WALL ship=(%d,%d) edgeY=%d damage=%d",
 					_flyShipScreenX, _flyShipScreenY, edgeY, wallDamage);
 			}
 			_spaceShotDirection = 3;  // Direction: pushed up
@@ -2034,7 +2034,7 @@ void InsaneRebel2::checkHandler7LeftBoundary(const InsaneRebel2::CollisionZone &
 
 			const bool damageApplied = applyHandler7WallDamage(wallDamage);
 			if (damageApplied) {
-				debug("Rebel2: Handler7 Mode1/3 LEFT WALL ship=(%d,%d) edgeX=%d damage=%d",
+				debugC(DEBUG_INSANE, "Rebel2: Handler7 Mode1/3 LEFT WALL ship=(%d,%d) edgeX=%d damage=%d",
 					_flyShipScreenX, _flyShipScreenY, edgeX, wallDamage);
 			}
 			_spaceShotDirection = 0;  // Direction: pushed right
@@ -2061,7 +2061,7 @@ void InsaneRebel2::checkHandler7RightBoundary(const InsaneRebel2::CollisionZone
 
 			const bool damageApplied = applyHandler7WallDamage(wallDamage);
 			if (damageApplied) {
-				debug("Rebel2: Handler7 Mode1/3 RIGHT WALL ship=(%d,%d) edgeX=%d damage=%d",
+				debugC(DEBUG_INSANE, "Rebel2: Handler7 Mode1/3 RIGHT WALL ship=(%d,%d) edgeX=%d damage=%d",
 					_flyShipScreenX, _flyShipScreenY, edgeX, wallDamage);
 			}
 			_spaceShotDirection = 1;  // Direction: pushed left
@@ -2454,7 +2454,7 @@ bool InsaneRebel2::handlePostRenderMenuModes(byte *renderBitmap, int pitch, int
 
 		// If a selection was confirmed, signal video to stop.
 		if (selection >= 0) {
-			debug("Rebel2: Pilot selection confirmed: %d", selection);
+			debugC(DEBUG_INSANE, "Rebel2: Pilot selection confirmed: %d", selection);
 			_menuSelectionConfirmed = true;
 			_vm->_smushVideoShouldFinish = true;
 		}
@@ -2479,7 +2479,7 @@ bool InsaneRebel2::handlePostRenderMenuModes(byte *renderBitmap, int pitch, int
 
 		// If a selection was confirmed, signal video to stop.
 		if (selection >= 0) {
-			debug("Rebel2: Chapter selection confirmed: %d", selection);
+			debugC(DEBUG_INSANE, "Rebel2: Chapter selection confirmed: %d", selection);
 			_menuSelectionConfirmed = true;
 			_vm->_smushVideoShouldFinish = true;
 		}
@@ -2515,7 +2515,7 @@ bool InsaneRebel2::handlePostRenderMenuModes(byte *renderBitmap, int pitch, int
 		// At 12fps video rate, 300 frames = ~25 seconds of inactivity.
 		// The original checks: if (local_8 > 299) return 0.
 		if (_menuInactivityTimer > 300) {
-			debug("Rebel2: Menu inactivity timeout - resuming intro/demo loop");
+			debugC(DEBUG_INSANE, "Rebel2: Menu inactivity timeout - resuming intro/demo loop");
 			_menuInactivityTimer = 0;
 			_menuInactivityTimedOut = true;
 			_menuSelectionConfirmed = false;
@@ -2528,7 +2528,7 @@ bool InsaneRebel2::handlePostRenderMenuModes(byte *renderBitmap, int pitch, int
 
 		// If a selection was confirmed, signal video to stop.
 		if (selection >= 0) {
-			debug("Rebel2: Menu selection confirmed: %d", selection);
+			debugC(DEBUG_INSANE, "Rebel2: Menu selection confirmed: %d", selection);
 			_menuSelectionConfirmed = true;
 			_vm->_smushVideoShouldFinish = true;
 		}
@@ -2559,7 +2559,7 @@ bool InsaneRebel2::handlePostRenderIntro(byte *renderBitmap, int pitch, int widt
 		// Track state transition for debugging.
 		if (!_introCursorPushed) {
 			_introCursorPushed = true;
-			debug("Rebel2: Intro/cinematic mode (handler=0, flags=0x%x, state=%d) - HUD disabled, mouse hidden",
+			debugC(DEBUG_INSANE, "Rebel2: Intro/cinematic mode (handler=0, flags=0x%x, state=%d) - HUD disabled, mouse hidden",
 				  _player->_curVideoFlags, _gameState);
 		}
 
@@ -2574,7 +2574,7 @@ bool InsaneRebel2::handlePostRenderIntro(byte *renderBitmap, int pitch, int widt
 	// Gameplay mode - handler was set by IACT opcode 6.
 	if (_introCursorPushed) {
 		_introCursorPushed = false;
-		debug("Rebel2: Gameplay mode (handler=%d, flags=0x%x, state=%d) - HUD enabled",
+		debugC(DEBUG_INSANE, "Rebel2: Gameplay mode (handler=%d, flags=0x%x, state=%d) - HUD enabled",
 			  _rebelHandler, _player->_curVideoFlags, _gameState);
 	}
 
@@ -2694,7 +2694,7 @@ void InsaneRebel2::renderGameplayPostFrame(byte *renderBitmap, int pitch, int wi
 	renderStatusBarBackground(renderBitmap, pitch, width, height, videoWidth, videoHeight, statusBarY);
 
 	// Ship rendering (FUN_00401ccf for Handler 8, FUN_0040d836 for Handler 7).
-	debug("Rebel2 Ship Check: handler=%d shipSprite=%p flyShipSprite=%p shipLevelMode=%d numSprites=%d/%d",
+	debugC(DEBUG_INSANE, "Rebel2 Ship Check: handler=%d shipSprite=%p flyShipSprite=%p shipLevelMode=%d numSprites=%d/%d",
 		_rebelHandler, (void*)_shipSprite, (void*)_flyShipSprite, _shipLevelMode,
 		_shipSprite ? _shipSprite->getNumChars() : 0,
 		_flyShipSprite ? _flyShipSprite->getNumChars() : 0);
@@ -2974,7 +2974,7 @@ void InsaneRebel2::renderTextOverlay(byte *renderBitmap, int pitch, int width, i
 
 	SmushPlayer *splayer = ((ScummEngine_v7 *)_vm)->_splayer;
 	const char *text = splayer->getString(_textOverlayID);
-	debug(5, "Rebel2: Text overlay frame %d/%d-%d textID=0x%x text='%s'",
+	debugC(DEBUG_INSANE, "Rebel2: Text overlay frame %d/%d-%d textID=0x%x text='%s'",
 	      curFrame, _textOverlayFadeIn, _textOverlayFadeOut, _textOverlayID,
 	      text ? text : "(null)");
 	if (!text)
@@ -3171,7 +3171,7 @@ void InsaneRebel2::renderTurretHudOverlays(byte *renderBitmap, int pitch, int wi
 			_hudOverlayNut, animFrame);
 	}
 
-	debug(5, "Rebel2 HUD: Drawing NUT overlay frame %d/%d at (%d,%d) mouseOffset=(%d,%d)",
+	debugC(DEBUG_INSANE, "Rebel2 HUD: Drawing NUT overlay frame %d/%d at (%d,%d) mouseOffset=(%d,%d)",
 		  animFrame, numSprites, hudX, hudY, mouseOffsetX, mouseOffsetY);
 
 	// Draw secondary HUD overlay if present (DAT_0047fe80)
@@ -3204,7 +3204,7 @@ void InsaneRebel2::renderEmbeddedHudOverlays(byte *renderBitmap, int pitch, int
 
 		// Skip small frames at (0,0) - likely animation patches
 		if (frame.renderX == 0 && frame.renderY == 0 && frame.width < 50 && frame.height < 60) {
-			debug(3, "Rebel2: Skipping small embedded frame at (0,0): slot=%d size=%dx%d",
+			debugC(DEBUG_INSANE, "Rebel2: Skipping small embedded frame at (0,0): slot=%d size=%dx%d",
 				hudSlot, frame.width, frame.height);
 			continue;
 		}
@@ -3268,7 +3268,7 @@ void InsaneRebel2::renderEmbeddedHudOverlays(byte *renderBitmap, int pitch, int
 		destX += _viewX;
 		destY += _viewY;
 
-		debug(3, "Rebel2: Rendering embedded HUD slot=%d size=%dx%d at (%d,%d)",
+		debugC(DEBUG_INSANE, "Rebel2: Rendering embedded HUD slot=%d size=%dx%d at (%d,%d)",
 			hudSlot, frame.width, frame.height, destX, destY);
 
 		blitEmbeddedFrameRegion(renderBitmap, pitch, pitch, height, frame,
@@ -3560,7 +3560,7 @@ void InsaneRebel2::renderHandler7Ship(byte *renderBitmap, int pitch, int width,
 			shipDraw.x, shipDraw.y, _flyTargetSprite, spriteIndex);
 	}
 
-	debug("Rebel2 Handler7Ship: draw=(%d,%d) sprite=%d/%d shipPos=(%d,%d) persp=(%d,%d) smoothVel=%d vertIn=%d fxCtr=%d fxRep=%d",
+	debugC(DEBUG_INSANE, "Rebel2 Handler7Ship: draw=(%d,%d) sprite=%d/%d shipPos=(%d,%d) persp=(%d,%d) smoothVel=%d vertIn=%d fxCtr=%d fxRep=%d",
 		drawX, drawY, spriteIndex, numSprites, _flyShipScreenX, _flyShipScreenY,
 		_perspectiveX, _perspectiveY, _smoothedVelocity, _verticalInput, _flyEffectAnimCounter, _flyOverlayRepeatCount);
 }
@@ -3618,7 +3618,7 @@ void InsaneRebel2::renderHandler8Ship(byte *renderBitmap, int pitch, int width,
 
 	int sprW = _shipSprite->getCharWidth(spriteIndex);
 	int sprH = _shipSprite->getCharHeight(spriteIndex);
-	debug("Rebel2 Handler8: Ship at (%d,%d) raw(%d,%d) offset(%d,%d) nutOff(%d,%d) size(%d,%d) bottom=%d view(%d,%d) sprite=%d/%d",
+	debugC(DEBUG_INSANE, "Rebel2 Handler8: Ship at (%d,%d) raw(%d,%d) offset(%d,%d) nutOff(%d,%d) size(%d,%d) bottom=%d view(%d,%d) sprite=%d/%d",
 		drawX, drawY, _shipPosX, _shipPosY, displayOffsetX, displayOffsetY,
 		spriteXOffset, spriteYOffset, sprW, sprH, drawY + sprH,
 		_viewX, _viewY, spriteIndex, numSprites);
@@ -3749,7 +3749,7 @@ void InsaneRebel2::renderHandler25ShipPre(byte *renderBitmap, int pitch, int wid
 			scaledClipLeft, 0, scaledClipRight, renderHeight,
 			drawX, drawY, _grd001Sprite, 0, false, renderScale, false);
 
-		debug("Rebel2 Handler25 PRE: GRD001 at (%d,%d) nutOff(%d,%d) viewOff(%d,%d) size(%d,%d) mode=%d dmg=%d halfW=%d rightHalf=%d clip=[%d,%d) scale=%d",
+		debugC(DEBUG_INSANE, "Rebel2 Handler25 PRE: GRD001 at (%d,%d) nutOff(%d,%d) viewOff(%d,%d) size(%d,%d) mode=%d dmg=%d halfW=%d rightHalf=%d clip=[%d,%d) scale=%d",
 			drawX, drawY, spriteXOffset, spriteYOffset, _rebelViewOffset2X, _rebelViewOffset2Y,
 			spriteW, spriteH, _grdSpriteMode, _rebelDamageLevel, useHalfWidth ? 1 : 0, useRightHalf ? 1 : 0, scaledClipLeft, scaledClipRight, renderScale);
 
@@ -3769,7 +3769,7 @@ void InsaneRebel2::renderHandler25ShipPre(byte *renderBitmap, int pitch, int wid
 				0, 0, width, renderHeight,
 				overlayDrawX, overlayDrawY, _grd005Sprite, overlayIdx, false, renderScale, false);
 
-			debug("Rebel2 Handler25 PRE: GRD005 at (%d,%d) nutOff(%d,%d) viewOff(%d,%d) size(%d,%d) mode=%d scale=%d",
+			debugC(DEBUG_INSANE, "Rebel2 Handler25 PRE: GRD005 at (%d,%d) nutOff(%d,%d) viewOff(%d,%d) size(%d,%d) mode=%d scale=%d",
 				overlayDrawX, overlayDrawY, overlayXOffset, overlayYOffset,
 				_rebelViewOffset2X, _rebelViewOffset2Y, overlayW, overlayH, _grdSpriteMode, renderScale);
 		}
@@ -3924,7 +3924,7 @@ void InsaneRebel2::renderHandler25Ship(byte *renderBitmap, int pitch, int width,
 			0, 0, width, renderHeight,
 			drawX, drawY, _grd002Sprite, spriteIdx, shouldMirror, renderScale, true);
 
-		debug("Rebel2 Handler25: GRD002 at (%d,%d) nutOffset(%d,%d) viewOffset(%d,%d) size(%d,%d) spriteIdx=%d damage=%d dir=%d mirror=%d scale=%d",
+		debugC(DEBUG_INSANE, "Rebel2 Handler25: GRD002 at (%d,%d) nutOffset(%d,%d) viewOffset(%d,%d) size(%d,%d) spriteIdx=%d damage=%d dir=%d mirror=%d scale=%d",
 			drawX, drawY, spriteXOffset, spriteYOffset, _rebelViewOffset2X, _rebelViewOffset2Y, spriteW, spriteH, spriteIdx, _rebelDamageLevel, _rebelFlightDir, shouldMirror ? 1 : 0, renderScale);
 	}
 }
@@ -3979,7 +3979,7 @@ void InsaneRebel2::renderFallbackShip(byte *renderBitmap, int pitch, int width,
 	blitEmbeddedFrameRegion(renderBitmap, pitch, width, height, shipFrame,
 		drawX, drawY, srcX, srcY, spriteW, spriteH);
 
-	debug("Rebel2: Ship (fallback) at (%d,%d) strip=(%d,%d) of (%dx%d) dir=(%d,%d)",
+	debugC(DEBUG_INSANE, "Rebel2: Ship (fallback) at (%d,%d) strip=(%d,%d) of (%dx%d) dir=(%d,%d)",
 		drawX, drawY, srcX, srcY, numHorizontal, numVertical, _shipDirectionH, _shipDirectionV);
 }
 
@@ -4277,7 +4277,7 @@ void InsaneRebel2::renderSpaceExplosions(byte *renderBitmap, int pitch, int widt
 					drawX - ew / 2, drawY - eh / 2, _smush_iconsNut, spriteIndex);
 			}
 
-			debug("Rebel2 H7 corridor explosion: dir=%d frame=%d pos=(%d,%d) cooldown=%d",
+			debugC(DEBUG_INSANE, "Rebel2 H7 corridor explosion: dir=%d frame=%d pos=(%d,%d) cooldown=%d",
 				_spaceShotDirection, spriteIndex, drawX, drawY, _hitCooldown);
 		}
 	}
@@ -4573,7 +4573,7 @@ void InsaneRebel2::renderHandler25LaserShots(byte *renderBitmap, int pitch, int
 
 		_turretShots[i].counter--;
 
-		debug("Rebel2 Handler25: Laser shot %d from (%d,%d) to (%d,%d) progress=%d/%d",
+		debugC(DEBUG_INSANE, "Rebel2 Handler25: Laser shot %d from (%d,%d) to (%d,%d) progress=%d/%d",
 			i, gunX, gunY, targetX, targetY, progress, maxDuration);
 	}
 }
diff --git a/engines/scumm/insane/rebel2/runlevels.cpp b/engines/scumm/insane/rebel2/runlevels.cpp
index d09d5bb4b4c..c81b5626fc7 100644
--- a/engines/scumm/insane/rebel2/runlevels.cpp
+++ b/engines/scumm/insane/rebel2/runlevels.cpp
@@ -57,14 +57,14 @@ int InsaneRebel2::runLevel1() {
 
 		if (_playerShield > 0) {
 			// Level completed!
-			debug("Rebel2: Level 1 completed!");
+			debugC(DEBUG_INSANE, "Rebel2: Level 1 completed!");
 			playLevelEnd(1);
 			_levelUnlocked[1] = true;  // Unlock level 2
 			return kLevelNextLevel;
 		}
 
 		// Player died - play death video with random A/B variant
-		debug("Rebel2: Level 1 death at frame %d, lives=%d", _deathFrame, _playerLives - 1);
+		debugC(DEBUG_INSANE, "Rebel2: Level 1 death at frame %d, lives=%d", _deathFrame, _playerLives - 1);
 		playLevelDeathVariant(1, 1, _deathFrame);
 
 		if (_vm->shouldQuit())
@@ -104,7 +104,7 @@ InsaneRebel2::WaveEndResult InsaneRebel2::processWaveEnd(int16 mask, int16 *budg
 		_skipSectionRequested = false;
 		_rebelPhaseState = mask;
 		_rebelWaveState = mask;
-		debug("Rebel2 processWaveEnd: movie mode completed gameplay wave (mask=0x%x)", (uint16)mask);
+		debugC(DEBUG_INSANE, "Rebel2 processWaveEnd: movie mode completed gameplay wave (mask=0x%x)", (uint16)mask);
 		result.completed = true;
 		result.skipped = true;
 		return result;
@@ -115,7 +115,7 @@ InsaneRebel2::WaveEndResult InsaneRebel2::processWaveEnd(int16 mask, int16 *budg
 		_skipSectionRequested = false;
 		_rebelPhaseState = mask;
 		_rebelWaveState = mask;
-		debug("Rebel2 processWaveEnd: Shift+S skip consumed (mask=0x%x)", (uint16)mask);
+		debugC(DEBUG_INSANE, "Rebel2 processWaveEnd: Shift+S skip consumed (mask=0x%x)", (uint16)mask);
 		result.completed = true;
 		result.skipped = true;
 		return result;
@@ -133,7 +133,7 @@ InsaneRebel2::WaveEndResult InsaneRebel2::processWaveEnd(int16 mask, int16 *budg
 	// Step 2: Copy wave state to phase state (line 33)
 	// DAT_0047ab9c = DAT_0047ab98
 	_rebelPhaseState = _rebelWaveState;
-	debug("Rebel2 processWaveEnd: waveState=0x%x -> phaseState=0x%x mask=0x%x budget=%d threshold=%d flags=%d",
+	debugC(DEBUG_INSANE, "Rebel2 processWaveEnd: waveState=0x%x -> phaseState=0x%x mask=0x%x budget=%d threshold=%d flags=%d",
 		_rebelWaveState, _rebelPhaseState, mask, budget ? *budget : -1, threshold, flags);
 
 	// Step 3: Kill redistribution - add random unkilled types (lines 34-47)
@@ -187,7 +187,7 @@ InsaneRebel2::WaveEndResult InsaneRebel2::processWaveEnd(int16 mask, int16 *budg
 		creditCount++;
 	}
 
-	debug("Rebel2 processWaveEnd: result=0x%x phaseState=0x%x (after redistribution) budget=%d",
+	debugC(DEBUG_INSANE, "Rebel2 processWaveEnd: result=0x%x phaseState=0x%x (after redistribution) budget=%d",
 		result.creditedBits, _rebelPhaseState, budget ? *budget : -1);
 
 	// Step 5: Stop conditions (lines 74-78)
@@ -203,7 +203,7 @@ bool InsaneRebel2::playLevelSegment(const char *filename, uint16 flags, bool rec
 
 	const bool isRecordedGameplay = recordFrame && (flags & 0x08) != 0;
 	if (isRecordedGameplay && _rebelMovieMode) {
-		debug("Rebel2: Movie mode skipping gameplay segment: %s", filename);
+		debugC(DEBUG_INSANE, "Rebel2: Movie mode skipping gameplay segment: %s", filename);
 		_gameplaySectionActive = false;
 		restoreIOSGamepadController();
 		if (recordFrame)
@@ -245,7 +245,7 @@ int InsaneRebel2::calculateAccuracy(int kills, int misses) const {
 
 bool InsaneRebel2::handleLevelDeath(int levelId, int phase,
 		const char *deathVideo, const char *retryVideo, int &levelResult) {
-	debug("Rebel2: Level %d Phase %d death", levelId, phase);
+	debugC(DEBUG_INSANE, "Rebel2: Level %d Phase %d death", levelId, phase);
 	playCinematic(deathVideo);
 	if (_vm->shouldQuit()) {
 		levelResult = kLevelQuit;
@@ -426,7 +426,7 @@ int InsaneRebel2::runLevel2() {
 		int16 budget = kLevel2BudgetBase[0] + _vm->_rnd.getRandomNumber(2);
 
 		// Play A.SAN (background loader) ??? flags 0x28 (preserve buffer, gameplay mode)
-		debug("Rebel2: Level 2 Phase 1 - playing 02P01_A.SAN (background) budget=%d", budget);
+		debugC(DEBUG_INSANE, "Rebel2: Level 2 Phase 1 - playing 02P01_A.SAN (background) budget=%d", budget);
 		if (!playLevelSegment("LEV02/P1/02P01_A.SAN", 0x28))
 			return kLevelQuit;
 
@@ -446,14 +446,14 @@ int InsaneRebel2::runLevel2() {
 				"LEV02/P1/02P01_C.SAN",
 				"LEV02/P1/02P01_D.SAN"
 			};
-			debug("Rebel2: Phase 1 wave - playing %s (state=0x%x budget=%d)", variants[variant], _rebelPhaseState, budget);
+			debugC(DEBUG_INSANE, "Rebel2: Phase 1 wave - playing %s (state=0x%x budget=%d)", variants[variant], _rebelPhaseState, budget);
 			// Wave videos use flags 0x428 (original: FUN_0041f4d0 param_2=0x428)
 			if (!playLevelSegment(variants[variant], 0x428))
 				return kLevelQuit;
 
 			// processWaveEnd with threshold=0x14 (20) ??? enables early exit when enemies killed
 			processWaveEnd(0x36, &budget, 0x14, 0);
-			debug("Rebel2: Phase 1 wave done - state=0x%x (need 0x06) budget=%d", _rebelPhaseState, budget);
+			debugC(DEBUG_INSANE, "Rebel2: Phase 1 wave done - state=0x%x (need 0x06) budget=%d", _rebelPhaseState, budget);
 		}
 
 		// Check for bonus (bit 4 = 0x10)
@@ -492,7 +492,7 @@ int InsaneRebel2::runLevel2() {
 		_rebelHandler = 8;
 
 		// Play A.SAN (background loader)
-		debug("Rebel2: Level 2 Phase 2 - playing 02P02_A.SAN (background) budget=%d", budget);
+		debugC(DEBUG_INSANE, "Rebel2: Level 2 Phase 2 - playing 02P02_A.SAN (background) budget=%d", budget);
 		if (!playLevelSegment("LEV02/P2/02P02_A.SAN", 0x28))
 			return kLevelQuit;
 
@@ -529,7 +529,7 @@ int InsaneRebel2::runLevel2() {
 				filename = "LEV02/P2/02P02_D.SAN"; break;
 			}
 
-			debug("Rebel2: Phase 2 wave - playing %s (state=0x%x sel=0x%x budget=%d)", filename, _rebelPhaseState, waveSelect, budget);
+			debugC(DEBUG_INSANE, "Rebel2: Phase 2 wave - playing %s (state=0x%x sel=0x%x budget=%d)", filename, _rebelPhaseState, waveSelect, budget);
 			if (!playLevelSegment(filename, 0x428))
 				return kLevelQuit;
 		}
@@ -569,7 +569,7 @@ int InsaneRebel2::runLevel2() {
 		_rebelHandler = 8;
 
 		// Play A.SAN (background loader)
-		debug("Rebel2: Level 2 Phase 3 - playing 02P03_A.SAN (background) budget=%d", budget);
+		debugC(DEBUG_INSANE, "Rebel2: Level 2 Phase 3 - playing 02P03_A.SAN (background) budget=%d", budget);
 		if (!playLevelSegment("LEV02/P3/02P03_A.SAN", 0x28))
 			return kLevelQuit;
 
@@ -614,13 +614,13 @@ int InsaneRebel2::runLevel2() {
 					filename = "LEV02/P3/02P03_I.SAN"; break;
 				}
 
-				debug("Rebel2: Phase 3 wave - playing %s (state=0x%x sel=0x%x budget=%d)", filename, _rebelPhaseState, waveSelect, budget);
+				debugC(DEBUG_INSANE, "Rebel2: Phase 3 wave - playing %s (state=0x%x sel=0x%x budget=%d)", filename, _rebelPhaseState, waveSelect, budget);
 				if (!playLevelSegment(filename, 0x428))
 					return kLevelQuit;
 
 				// processWaveEnd at BOTTOM with threshold=0x14
 				waveEnd = processWaveEnd(0x3e, &budget, 0x14, 0);
-				debug("Rebel2: Phase 3 wave done - state=0x%x (need 0x0e) budget=%d", _rebelPhaseState, budget);
+				debugC(DEBUG_INSANE, "Rebel2: Phase 3 wave done - state=0x%x (need 0x0e) budget=%d", _rebelPhaseState, budget);
 			}
 		}
 
@@ -643,7 +643,7 @@ int InsaneRebel2::runLevel2() {
 		{
 			totalMisses += _rebelHitCounter;
 			int accuracy = calculateAccuracy(totalKills, totalMisses);
-			debug("Rebel2: Level 2 completed! kills=%d misses=%d accuracy=%d%% bonus=%d",
+			debugC(DEBUG_INSANE, "Rebel2: Level 2 completed! kills=%d misses=%d accuracy=%d%% bonus=%d",
 				totalKills, totalMisses, accuracy, bonusCount);
 		}
 
@@ -682,7 +682,7 @@ int InsaneRebel2::runLevel3() {
 		resetHandler7FlightState();
 
 		// Play phase 1 gameplay (03PLAY1.SAN)
-		debug("Rebel2: Level 3 Phase 1");
+		debugC(DEBUG_INSANE, "Rebel2: Level 3 Phase 1");
 		if (!playLevelSegment("LEV03/03PLAY1.SAN", 0x28))
 			return kLevelQuit;
 
@@ -693,7 +693,7 @@ int InsaneRebel2::runLevel3() {
 		}
 
 		// Died in phase 1 - frame-based death video
-		debug("Rebel2: Level 3 Phase 1 death at frame %d", _deathFrame);
+		debugC(DEBUG_INSANE, "Rebel2: Level 3 Phase 1 death at frame %d", _deathFrame);
 		playLevelDeathVariant(3, 1, _deathFrame);
 		if (_vm->shouldQuit())
 			return kLevelQuit;
@@ -736,20 +736,20 @@ int InsaneRebel2::runLevel3() {
 		resetHandler7FlightState();
 
 		// Play phase 2 gameplay (03PLAY2.SAN)
-		debug("Rebel2: Level 3 Phase 2");
+		debugC(DEBUG_INSANE, "Rebel2: Level 3 Phase 2");
 		if (!playLevelSegment("LEV03/03PLAY2.SAN", 0x28))
 			return kLevelQuit;
 
 		if (_playerShield > 0) {
 			// Level completed!
-			debug("Rebel2: Level 3 completed!");
+			debugC(DEBUG_INSANE, "Rebel2: Level 3 completed!");
 			playLevelEnd(3);
 			_levelUnlocked[3] = true;  // Unlock level 4
 			return kLevelNextLevel;
 		}
 
 		// Died in phase 2 - frame-based death video
-		debug("Rebel2: Level 3 Phase 2 death at frame %d", _deathFrame);
+		debugC(DEBUG_INSANE, "Rebel2: Level 3 Phase 2 death at frame %d", _deathFrame);
 		playLevelDeathVariant(3, 2, _deathFrame);
 		if (_vm->shouldQuit())
 			return kLevelQuit;
@@ -796,20 +796,20 @@ int InsaneRebel2::runLevel4() {
 		clearBit(0);
 
 		// Play gameplay (04PLAY.SAN)
-		debug("Rebel2: Level 4 gameplay");
+		debugC(DEBUG_INSANE, "Rebel2: Level 4 gameplay");
 		if (!playLevelSegment("LEV04/04PLAY.SAN", 0x28))
 			return kLevelQuit;
 
 		if (_playerShield > 0) {
 			// Level completed!
-			debug("Rebel2: Level 4 completed!");
+			debugC(DEBUG_INSANE, "Rebel2: Level 4 completed!");
 			playLevelEnd(4);
 			_levelUnlocked[4] = true;  // Unlock level 5
 			return kLevelNextLevel;
 		}
 
 		// Died - play death video (04DIE.SAN, no variants)
-		debug("Rebel2: Level 4 death");
+		debugC(DEBUG_INSANE, "Rebel2: Level 4 death");
 		playLevelDeathVariant(4, 1, _deathFrame);
 		if (_vm->shouldQuit())
 			return kLevelQuit;
@@ -851,20 +851,20 @@ int InsaneRebel2::runLevel5() {
 
 		// Play gameplay (05PLAY.SAN)
 		// Original: FUN_0041f4d0("05PLAY.SAN", 8, -1, -1, 0)
-		debug("Rebel2: Level 5 gameplay");
+		debugC(DEBUG_INSANE, "Rebel2: Level 5 gameplay");
 		if (!playLevelSegment("LEV05/05PLAY.SAN", 0x08))
 			return kLevelQuit;
 
 		if (_playerShield > 0) {
 			// Level completed!
-			debug("Rebel2: Level 5 completed!");
+			debugC(DEBUG_INSANE, "Rebel2: Level 5 completed!");
 			playLevelEnd(5);
 			_levelUnlocked[5] = true;  // Unlock level 6
 			return kLevelNextLevel;
 		}
 
 		// Died - play death video with random A/B variant
-		debug("Rebel2: Level 5 death at frame %d", _deathFrame);
+		debugC(DEBUG_INSANE, "Rebel2: Level 5 death at frame %d", _deathFrame);
 		playLevelDeathVariant(5, 1, _deathFrame);
 		if (_vm->shouldQuit())
 			return kLevelQuit;
@@ -918,7 +918,7 @@ int InsaneRebel2::runLevel6() {
 		_rebelLevelType = 5;  // DAT_0047a7f8 = 5
 		_currentPhase = 1;
 
-		debug("Rebel2: Level 6 Phase 1");
+		debugC(DEBUG_INSANE, "Rebel2: Level 6 Phase 1");
 		if (!playLevelSegment("LEV06/06PLAY1.SAN", 0x28))
 			return kLevelQuit;
 		// TODO: Mid-level switch at frame 0x2a8 to 06PLAY1B.SAN (flags 0x468)
@@ -926,7 +926,7 @@ int InsaneRebel2::runLevel6() {
 
 		if (_playerShield <= 0) {
 			// Died in phase 1
-			debug("Rebel2: Level 6 Phase 1 death at frame %d", _deathFrame);
+			debugC(DEBUG_INSANE, "Rebel2: Level 6 Phase 1 death at frame %d", _deathFrame);
 			playLevelDeathVariant(6, 1, _deathFrame);
 			if (_vm->shouldQuit())
 				return kLevelQuit;
@@ -959,20 +959,20 @@ int InsaneRebel2::runLevel6() {
 			_playerScore = phase1Score;
 			clearBit(0);  // FUN_00407d10
 
-			debug("Rebel2: Level 6 Phase 2");
+			debugC(DEBUG_INSANE, "Rebel2: Level 6 Phase 2");
 			if (!playLevelSegment("LEV06/06PLAY2.SAN", 0x28))
 				return kLevelQuit;
 
 			if (_playerShield > 0) {
 				// Level completed!
-				debug("Rebel2: Level 6 completed!");
+				debugC(DEBUG_INSANE, "Rebel2: Level 6 completed!");
 				playLevelEnd(6);
 				_levelUnlocked[6] = true;
 				return kLevelNextLevel;
 			}
 
 			// Died in phase 2
-			debug("Rebel2: Level 6 Phase 2 death at frame %d", _deathFrame);
+			debugC(DEBUG_INSANE, "Rebel2: Level 6 Phase 2 death at frame %d", _deathFrame);
 			playLevelDeathVariant(6, 2, _deathFrame);
 			if (_vm->shouldQuit())
 				return kLevelQuit;
@@ -1036,7 +1036,7 @@ int InsaneRebel2::runLevel7() {
 			return kLevelQuit;
 
 		if (_playerShield > 0) {
-			debug("Rebel2: Level 7 completed!");
+			debugC(DEBUG_INSANE, "Rebel2: Level 7 completed!");
 			playLevelEnd(7);
 			_levelUnlocked[7] = true;
 			return kLevelNextLevel;
@@ -1044,7 +1044,7 @@ int InsaneRebel2::runLevel7() {
 
 		// Death video: DIE_B if fork reached, DIE_A if not
 		// Original: s_LEV07_07DIE_B + ((DAT_0047ab8c != 0) - 1 & 0x14)
-		debug("Rebel2: Level 7 death at frame %d, fork=%d", _deathFrame, reachedFork);
+		debugC(DEBUG_INSANE, "Rebel2: Level 7 death at frame %d, fork=%d", _deathFrame, reachedFork);
 		if (reachedFork) {
 			playCinematic("LEV07/07DIE_B.SAN");
 		} else {
@@ -1098,7 +1098,7 @@ int InsaneRebel2::runLevel8() {
 
 		if (_playerShield > 0) {
 			int accuracy = calculateAccuracy(_rebelKillCounter, _rebelHitCounter);
-			debug("Rebel2: Level 8 completed! accuracy=%d%%", accuracy);
+			debugC(DEBUG_INSANE, "Rebel2: Level 8 completed! accuracy=%d%%", accuracy);
 			playLevelEnd(8);
 			_levelUnlocked[8] = true;
 			return kLevelNextLevel;
@@ -1106,7 +1106,7 @@ int InsaneRebel2::runLevel8() {
 
 		// Death: random A or B
 		// Original: random(2) ??? A or B via string pointer arithmetic
-		debug("Rebel2: Level 8 death at frame %d", _deathFrame);
+		debugC(DEBUG_INSANE, "Rebel2: Level 8 death at frame %d", _deathFrame);
 		playLevelDeathVariant(8, 1, _deathFrame);
 		if (_vm->shouldQuit())
 			return kLevelQuit;
@@ -1162,7 +1162,7 @@ int InsaneRebel2::runLevel9() {
 
 		if (_playerShield > 0) {
 			int accuracy = calculateAccuracy(_rebelKillCounter, _rebelHitCounter);
-			debug("Rebel2: Level 9 completed! accuracy=%d%%", accuracy);
+			debugC(DEBUG_INSANE, "Rebel2: Level 9 completed! accuracy=%d%%", accuracy);
 			playLevelEnd(9);
 			_levelUnlocked[9] = true;
 			return kLevelNextLevel;
@@ -1170,7 +1170,7 @@ int InsaneRebel2::runLevel9() {
 
 		// Death: based on DAT_0047ab94 (death cause tracking)
 		// Original: 0???DIE_A, 1???DIE_C, else???DIE_B
-		debug("Rebel2: Level 9 death at frame %d", _deathFrame);
+		debugC(DEBUG_INSANE, "Rebel2: Level 9 death at frame %d", _deathFrame);
 		playLevelDeathVariant(9, 1, _deathFrame);
 		if (_vm->shouldQuit())
 			return kLevelQuit;
@@ -1225,7 +1225,7 @@ int InsaneRebel2::runLevel10() {
 
 		if (_playerShield > 0) {
 			int accuracy = calculateAccuracy(_rebelKillCounter, _rebelHitCounter);
-			debug("Rebel2: Level 10 completed! accuracy=%d%%", accuracy);
+			debugC(DEBUG_INSANE, "Rebel2: Level 10 completed! accuracy=%d%%", accuracy);
 			playLevelEnd(10);
 			_levelUnlocked[10] = true;
 			return kLevelNextLevel;
@@ -1233,7 +1233,7 @@ int InsaneRebel2::runLevel10() {
 
 		// Death + Retry: original plays both in sequence
 		// Original: lives--, if 0 break to game over, else DIE+RETRY
-		debug("Rebel2: Level 10 death at frame %d", _deathFrame);
+		debugC(DEBUG_INSANE, "Rebel2: Level 10 death at frame %d", _deathFrame);
 		_playerLives--;
 		if (_playerLives <= 0) {
 			playLevelGameOver(10);
@@ -1297,7 +1297,7 @@ int InsaneRebel2::runLevel11() {
 		int16 budget = kLevel11BudgetBase[0] + _vm->_rnd.getRandomNumber(2);
 
 		// Play A.SAN (background loader)
-		debug("Rebel2: Level 11 Phase 1 - playing 11P01_A.SAN budget=%d", budget);
+		debugC(DEBUG_INSANE, "Rebel2: Level 11 Phase 1 - playing 11P01_A.SAN budget=%d", budget);
 		if (!playLevelSegment("LEV11/P1/11P01_A.SAN", 0x28))
 			return kLevelQuit;
 
@@ -1327,7 +1327,7 @@ int InsaneRebel2::runLevel11() {
 				default: filename = "LEV11/P1/11P01_D.SAN"; break;  // sel == 0
 				}
 
-				debug("Rebel2: Level 11 Phase 1 wave - %s (state=0x%x sel=%d)", filename, _rebelPhaseState, sel);
+				debugC(DEBUG_INSANE, "Rebel2: Level 11 Phase 1 wave - %s (state=0x%x sel=%d)", filename, _rebelPhaseState, sel);
 				if (!playLevelSegment(filename, 0x428))
 					return kLevelQuit;
 
@@ -1361,7 +1361,7 @@ int InsaneRebel2::runLevel11() {
 		_rebelHandler = 8;
 
 		// Play A.SAN (background loader)
-		debug("Rebel2: Level 11 Phase 2 - playing 11P02_A.SAN budget=%d", budget);
+		debugC(DEBUG_INSANE, "Rebel2: Level 11 Phase 2 - playing 11P02_A.SAN budget=%d", budget);
 		if (!playLevelSegment("LEV11/P2/11P02_A.SAN", 0x28))
 			return kLevelQuit;
 
@@ -1383,7 +1383,7 @@ int InsaneRebel2::runLevel11() {
 				default: filename = "LEV11/P2/11P02_D.SAN"; break;
 				}
 
-				debug("Rebel2: Level 11 Phase 2 wave - %s (state=0x%x)", filename, _rebelPhaseState);
+				debugC(DEBUG_INSANE, "Rebel2: Level 11 Phase 2 wave - %s (state=0x%x)", filename, _rebelPhaseState);
 				if (!playLevelSegment(filename, 0x428))
 					return kLevelQuit;
 
@@ -1418,7 +1418,7 @@ int InsaneRebel2::runLevel11() {
 		budget = kLevel11BudgetBase[2] + _vm->_rnd.getRandomNumber(2);
 		_rebelHandler = 8;
 
-		debug("Rebel2: Level 11 Phase 3 first half - playing 11P03_A.SAN budget=%d", budget);
+		debugC(DEBUG_INSANE, "Rebel2: Level 11 Phase 3 first half - playing 11P03_A.SAN budget=%d", budget);
 		if (!playLevelSegment("LEV11/P3/11P03_A.SAN", 0x28))
 			return kLevelQuit;
 
@@ -1456,7 +1456,7 @@ int InsaneRebel2::runLevel11() {
 				default: filename = "LEV11/P3/11P03_E.SAN"; break;  // duplicate E
 				}
 
-				debug("Rebel2: Level 11 Phase 3a wave - %s (state=0x%x variantIdx=%d)", filename, _rebelPhaseState, variantIdx);
+				debugC(DEBUG_INSANE, "Rebel2: Level 11 Phase 3a wave - %s (state=0x%x variantIdx=%d)", filename, _rebelPhaseState, variantIdx);
 				if (!playLevelSegment(filename, 0x428))
 					return kLevelQuit;
 
@@ -1508,7 +1508,7 @@ int InsaneRebel2::runLevel11() {
 		budget = kLevel11BudgetBase[3] + _vm->_rnd.getRandomNumber(2);
 
 		// Play G.SAN (background loader for second half)
-		debug("Rebel2: Level 11 Phase 3 second half - playing 11P03_G.SAN budget=%d", budget);
+		debugC(DEBUG_INSANE, "Rebel2: Level 11 Phase 3 second half - playing 11P03_G.SAN budget=%d", budget);
 		if (!playLevelSegment("LEV11/P3/11P03_G.SAN", 0x28))
 			return kLevelQuit;
 
@@ -1541,7 +1541,7 @@ int InsaneRebel2::runLevel11() {
 				default: filename = "LEV11/P3/11P03_L.SAN"; break;
 				}
 
-				debug("Rebel2: Level 11 Phase 3b wave - %s (state=0x%x variantIdx=%d)", filename, _rebelPhaseState, variantIdx);
+				debugC(DEBUG_INSANE, "Rebel2: Level 11 Phase 3b wave - %s (state=0x%x variantIdx=%d)", filename, _rebelPhaseState, variantIdx);
 				if (!playLevelSegment(filename, 0x428))
 					return kLevelQuit;
 
@@ -1565,7 +1565,7 @@ int InsaneRebel2::runLevel11() {
 		{
 			totalMisses += _rebelHitCounter;
 			int accuracy = calculateAccuracy(totalKills, totalMisses);
-			debug("Rebel2: Level 11 completed! kills=%d misses=%d accuracy=%d%%",
+			debugC(DEBUG_INSANE, "Rebel2: Level 11 completed! kills=%d misses=%d accuracy=%d%%",
 				totalKills, totalMisses, accuracy);
 		}
 
@@ -1617,7 +1617,7 @@ int InsaneRebel2::runLevel12() {
 		int16 budget = kLevel12BudgetBase[0] + _vm->_rnd.getRandomNumber(2);
 
 		// Initialization video (12P05.SAN)
-		debug("Rebel2: Level 12 Phase 1 - init 12P05.SAN budget=%d", budget);
+		debugC(DEBUG_INSANE, "Rebel2: Level 12 Phase 1 - init 12P05.SAN budget=%d", budget);
 		if (!playLevelSegment("LEV12/12P05.SAN", 0x28, false))
 			return kLevelQuit;
 		processWaveEnd(1, &budget, 0, 0);
@@ -1644,7 +1644,7 @@ int InsaneRebel2::runLevel12() {
 				default: filename = "LEV12/P1/12P01_B.SAN"; break;
 				}
 
-				debug("Rebel2: Level 12 Phase 1 wave - %s (state=0x%x sel=%d)", filename, _rebelPhaseState, sel);
+				debugC(DEBUG_INSANE, "Rebel2: Level 12 Phase 1 wave - %s (state=0x%x sel=%d)", filename, _rebelPhaseState, sel);
 				if (!playLevelSegment(filename, 0x428))
 					return kLevelQuit;
 
@@ -1668,7 +1668,7 @@ int InsaneRebel2::runLevel12() {
 		budget = kLevel12BudgetBase[1] + _vm->_rnd.getRandomNumber(3);
 
 		// Initialization video (12P06.SAN)
-		debug("Rebel2: Level 12 Phase 2 - init 12P06.SAN budget=%d", budget);
+		debugC(DEBUG_INSANE, "Rebel2: Level 12 Phase 2 - init 12P06.SAN budget=%d", budget);
 		if (!playLevelSegment("LEV12/12P06.SAN", 0x428, false))
 			return kLevelQuit;
 		processWaveEnd(1, &budget, 0, 0);
@@ -1704,7 +1704,7 @@ int InsaneRebel2::runLevel12() {
 				default: filename = "LEV12/P2/12P02_F.SAN"; break;
 				}
 
-				debug("Rebel2: Level 12 Phase 2 wave - %s (state=0x%x variantIdx=%d)", filename, _rebelPhaseState, variantIdx);
+				debugC(DEBUG_INSANE, "Rebel2: Level 12 Phase 2 wave - %s (state=0x%x variantIdx=%d)", filename, _rebelPhaseState, variantIdx);
 				if (!playLevelSegment(filename, 0x428))
 					return kLevelQuit;
 
@@ -1730,7 +1730,7 @@ int InsaneRebel2::runLevel12() {
 		budget = kLevel12BudgetBase[2] + _vm->_rnd.getRandomNumber(3);
 
 		// Initialization video (12P07.SAN)
-		debug("Rebel2: Level 12 Phase 3 - init 12P07.SAN budget=%d", budget);
+		debugC(DEBUG_INSANE, "Rebel2: Level 12 Phase 3 - init 12P07.SAN budget=%d", budget);
 		if (!playLevelSegment("LEV12/12P07.SAN", 0x428, false))
 			return kLevelQuit;
 		processWaveEnd(1, &budget, 0, 0);
@@ -1764,7 +1764,7 @@ int InsaneRebel2::runLevel12() {
 				default: filename = "LEV12/P3/12P03_F.SAN"; break;  // duplicate F
 				}
 
-				debug("Rebel2: Level 12 Phase 3 wave - %s (state=0x%x variantIdx=%d)", filename, _rebelPhaseState, variantIdx);
+				debugC(DEBUG_INSANE, "Rebel2: Level 12 Phase 3 wave - %s (state=0x%x variantIdx=%d)", filename, _rebelPhaseState, variantIdx);
 				if (!playLevelSegment(filename, 0x428))
 					return kLevelQuit;
 
@@ -1788,7 +1788,7 @@ int InsaneRebel2::runLevel12() {
 		budget = kLevel12BudgetBase[3] + _vm->_rnd.getRandomNumber(3);
 
 		// Initialization video (12P08.SAN)
-		debug("Rebel2: Level 12 Phase 4 - init 12P08.SAN budget=%d", budget);
+		debugC(DEBUG_INSANE, "Rebel2: Level 12 Phase 4 - init 12P08.SAN budget=%d", budget);
 		if (!playLevelSegment("LEV12/12P08.SAN", 0x428, false))
 			return kLevelQuit;
 		processWaveEnd(1, &budget, 0, 0);
@@ -1821,7 +1821,7 @@ int InsaneRebel2::runLevel12() {
 				default: filename = "LEV12/P4/12P04_F.SAN"; break;
 				}
 
-				debug("Rebel2: Level 12 Phase 4 wave - %s (state=0x%x variantIdx=%d)", filename, _rebelPhaseState, variantIdx);
+				debugC(DEBUG_INSANE, "Rebel2: Level 12 Phase 4 wave - %s (state=0x%x variantIdx=%d)", filename, _rebelPhaseState, variantIdx);
 				if (!playLevelSegment(filename, 0x428))
 					return kLevelQuit;
 
@@ -1846,7 +1846,7 @@ int InsaneRebel2::runLevel12() {
 		// ----- LEVEL COMPLETED -----
 		{
 			int accuracy = calculateAccuracy(_rebelKillCounter, _rebelHitCounter);
-			debug("Rebel2: Level 12 completed! kills=%d misses=%d accuracy=%d%%",
+			debugC(DEBUG_INSANE, "Rebel2: Level 12 completed! kills=%d misses=%d accuracy=%d%%",
 				_rebelKillCounter, _rebelHitCounter, accuracy);
 		}
 
@@ -1908,14 +1908,14 @@ int InsaneRebel2::runLevel13() {
 
 		if (_playerShield > 0) {
 			int accuracy = calculateAccuracy(_rebelKillCounter, _rebelHitCounter);
-			debug("Rebel2: Level 13 completed! accuracy=%d%%", accuracy);
+			debugC(DEBUG_INSANE, "Rebel2: Level 13 completed! accuracy=%d%%", accuracy);
 			playLevelEnd(13);
 			_levelUnlocked[13] = true;
 			return kLevelNextLevel;
 		}
 
 		// Death: frame-based variant selection (FUN_0041B3E1 lines 47-61)
-		debug("Rebel2: Level 13 death at frame %d", _deathFrame);
+		debugC(DEBUG_INSANE, "Rebel2: Level 13 death at frame %d", _deathFrame);
 		playLevelDeathVariant(13, 1, _deathFrame);
 		if (_vm->shouldQuit())
 			return kLevelQuit;
@@ -1964,14 +1964,14 @@ int InsaneRebel2::runLevel14() {
 
 		if (_playerShield > 0) {
 			int accuracy = calculateAccuracy(_rebelKillCounter, _rebelHitCounter);
-			debug("Rebel2: Level 14 completed! accuracy=%d%%", accuracy);
+			debugC(DEBUG_INSANE, "Rebel2: Level 14 completed! accuracy=%d%%", accuracy);
 			playLevelEnd(14);
 			_levelUnlocked[14] = true;
 			return kLevelNextLevel;
 		}
 
 		// Death: single video (14DIE.SAN)
-		debug("Rebel2: Level 14 death at frame %d", _deathFrame);
+		debugC(DEBUG_INSANE, "Rebel2: Level 14 death at frame %d", _deathFrame);
 		playCinematic("LEV14/14DIE.SAN");
 		if (_vm->shouldQuit())
 			return kLevelQuit;
@@ -2036,7 +2036,7 @@ int InsaneRebel2::runLevel15() {
 
 		if (_playerShield > 0) {
 			int accuracy = calculateAccuracy(_rebelKillCounter, _rebelHitCounter);
-			debug("Rebel2: Level 15 completed! accuracy=%d%%", accuracy);
+			debugC(DEBUG_INSANE, "Rebel2: Level 15 completed! accuracy=%d%%", accuracy);
 			playLevelEnd(15);
 			_levelUnlocked[15] = true;
 			// Level 15 completion leads to credits (FUN_0041BBE8)
@@ -2044,7 +2044,7 @@ int InsaneRebel2::runLevel15() {
 		}
 
 		// Death: frame-based variant selection (FUN_0041B8D7 lines 46-65)
-		debug("Rebel2: Level 15 death at frame %d", _deathFrame);
+		debugC(DEBUG_INSANE, "Rebel2: Level 15 death at frame %d", _deathFrame);
 		playLevelDeathVariant(15, 1, _deathFrame);
 		if (_vm->shouldQuit())
 			return kLevelQuit;
diff --git a/engines/scumm/smush/rebel/font_rebel2.cpp b/engines/scumm/smush/rebel/font_rebel2.cpp
index a9f69d0cbdb..df5179f7332 100644
--- a/engines/scumm/smush/rebel/font_rebel2.cpp
+++ b/engines/scumm/smush/rebel/font_rebel2.cpp
@@ -154,7 +154,7 @@ void Rebel2NutRenderer::loadRebel2Font(const char *filename) {
 	if (_numChars <= 0 || decodedLength == 0)
 		error("Rebel2NutRenderer::loadRebel2Font(%s) no decodable characters", filename);
 
-	debug(1, "Rebel2NutRenderer::loadRebel2Font('%s') - decodedLength = %d", filename, decodedLength);
+	debugC(DEBUG_SMUSH, "Rebel2NutRenderer::loadRebel2Font('%s') - decodedLength = %d", filename, decodedLength);
 
 	_decodedData = new byte[decodedLength];
 	byte *decodedPtr = _decodedData;
@@ -370,7 +370,7 @@ void Rebel2NutRenderer::loadRebel2SpriteFromData(const byte *data, int32 dataSiz
 		decodeRebel2Frame(_chars[i].src, frame, codec45Palette, codec45Lookup);
 	}
 
-	debug(1, "Rebel2NutRenderer::loadRebel2SpriteFromData() - numChars=%d decodedLength=%u", _numChars, (uint32)decodedLength);
+	debugC(DEBUG_SMUSH, "Rebel2NutRenderer::loadRebel2SpriteFromData() - numChars=%d decodedLength=%u", _numChars, (uint32)decodedLength);
 }
 
 NutRenderer *makeRebel2Font(ScummEngine *vm, const char *filename) {
diff --git a/engines/scumm/smush/rebel/smush_player_ra1.cpp b/engines/scumm/smush/rebel/smush_player_ra1.cpp
index 55667d15056..e882c9788cb 100644
--- a/engines/scumm/smush/rebel/smush_player_ra1.cpp
+++ b/engines/scumm/smush/rebel/smush_player_ra1.cpp
@@ -856,7 +856,7 @@ bool SmushPlayerRebel1::ra1HandleUnknownFrameChunk(uint32 subType, int32 subSize
 	byte tb2 = (subType >> 8) & 0xFF, tb3 = subType & 0xFF;
 	if (tb0 > 0x40 && tb0 < 0x5B && tb1 > 0x40 && tb1 < 0x5B &&
 	    tb2 > 0x40 && tb2 < 0x5B && tb3 > 0x40 && tb3 < 0x5B) {
-		debug(5, "RA1: unknown uppercase tag %s at frame %d, stopping frame parse", tag2str(subType), _frame);
+		debugC(DEBUG_SMUSH, "RA1: unknown uppercase tag %s at frame %d, stopping frame parse", tag2str(subType), _frame);
 		return true;
 	}
 


Commit: 9ef0dfae2debdd325736ea2556dff658ef9293a3
    https://github.com/scummvm/scummvm/commit/9ef0dfae2debdd325736ea2556dff658ef9293a3
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-09T21:14:12+02:00

Commit Message:
SCUMM: RA: removed redundant tags from debug messages

Changed paths:
    engines/scumm/insane/rebel1/iact.cpp
    engines/scumm/insane/rebel1/menu.cpp
    engines/scumm/insane/rebel1/rebel.cpp
    engines/scumm/insane/rebel1/render.cpp
    engines/scumm/insane/rebel1/runlevels.cpp
    engines/scumm/insane/rebel1/saveload.cpp
    engines/scumm/insane/rebel2/iact.cpp
    engines/scumm/insane/rebel2/levels.cpp
    engines/scumm/insane/rebel2/menu.cpp
    engines/scumm/insane/rebel2/rebel.cpp
    engines/scumm/insane/rebel2/render.cpp
    engines/scumm/insane/rebel2/runlevels.cpp
    engines/scumm/smush/rebel/smush_player_ra1.cpp


diff --git a/engines/scumm/insane/rebel1/iact.cpp b/engines/scumm/insane/rebel1/iact.cpp
index dada01703ef..16fb90cce40 100644
--- a/engines/scumm/insane/rebel1/iact.cpp
+++ b/engines/scumm/insane/rebel1/iact.cpp
@@ -636,7 +636,7 @@ bool InsaneRebel1::handleFrameObjectTarget(int16 objectId, int16 left, int16 top
 		// replacement/death frame. This is used heavily by Level 9 troopers.
 		_frameObjectState[byteIndex] &= ~bit;
 		_frameObjectHitRevealPending = false;
-		debugC(DEBUG_INSANE, "RA1 FOBJ reveal: object=%d frameObjectByte=%d bit=0x%02x",
+		debugC(DEBUG_INSANE, "FOBJ reveal: object=%d frameObjectByte=%d bit=0x%02x",
 			objectId, byteIndex, bit);
 	}
 
@@ -717,7 +717,7 @@ void InsaneRebel1::checkDynamicLevelBranch(int32 curFrame) {
 			_vm->_smushVideoShouldFinish = true;
 			const int32 resumeFrame = (_currentLevel == 6 && _pendingRouteStartFrame < 0) ?
 				0 : _pendingRouteStartFrame;
-			debugC(DEBUG_INSANE, "RA1 L%d cutover: route=%d -> %d at %s=%u (resumeFrame=%d)",
+			debugC(DEBUG_INSANE, "L%d cutover: route=%d -> %d at %s=%u (resumeFrame=%d)",
 				_currentLevel + 1, _levelRouteIndex, _pendingRouteIndex,
 				_currentLevel == 6 ? "localFrame" : "frame",
 				(unsigned)routeFrame, (int)resumeFrame);
@@ -759,7 +759,7 @@ void InsaneRebel1::checkDynamicLevelBranch(int32 curFrame) {
 				: (branchX < kLevel7BranchThreshold[nextRoute]);
 			if (!takeBranch) {
 				if (routeFrame == decisionFrame)
-					debugC(DEBUG_INSANE, "RA1 L7 branch miss: route=%d candidate=%d localFrame=%u gameFrame=%d shipX=%d dir=%d threshold=%d",
+					debugC(DEBUG_INSANE, "L7 branch miss: route=%d candidate=%d localFrame=%u gameFrame=%d shipX=%d dir=%d threshold=%d",
 						route, nextRoute, (unsigned)routeFrame, (int)_gameCounter, branchX,
 						kLevel7BranchDir[nextRoute], kLevel7BranchThreshold[nextRoute]);
 				continue;
@@ -770,7 +770,7 @@ void InsaneRebel1::checkDynamicLevelBranch(int32 curFrame) {
 			_pendingRouteStartFrame = (int32)routeFrame;
 			_pendingRouteVideoStartFrame = 1 + (_pendingRouteCutoverFrame - _pendingRouteStartFrame);
 			_level7WarningFrames = 0;
-			debugC(DEBUG_INSANE, "RA1 L7 branch: route=%d -> %d at localFrame=%u gameFrame=%d decisionFrame=%u shipX=%d resumeSourceFrame=%d cutoverFrame=%d destFrame=%d",
+			debugC(DEBUG_INSANE, "L7 branch: route=%d -> %d at localFrame=%u gameFrame=%d decisionFrame=%u shipX=%d resumeSourceFrame=%d cutoverFrame=%d destFrame=%d",
 				route, nextRoute, (unsigned)routeFrame, (int)_gameCounter, (unsigned)decisionFrame, branchX,
 				(int)_pendingRouteStartFrame, (int)_pendingRouteCutoverFrame,
 				(int)_pendingRouteVideoStartFrame);
@@ -868,7 +868,7 @@ void InsaneRebel1::updateFlightVariantCursor() {
 	_flightAimY = CLIP<int32>(shipBaseY + ((liftTerm * kRA1Op09AimYScale[bucket]) >> 8),
 		kRA1MinY, kRA1MaxY);
 
-	debugC(DEBUG_INSANE, "RA1 op09 cursor: frame=%d shipBase=(%d,%d) shipPos=(%d,%d) aim=(%d,%d) roll=%d lift=%d bucket=%d dir=%d persp=(%d,%d)",
+	debugC(DEBUG_INSANE, "op09 cursor: frame=%d shipBase=(%d,%d) shipPos=(%d,%d) aim=(%d,%d) roll=%d lift=%d bucket=%d dir=%d persp=(%d,%d)",
 		_gameCounter, shipBaseX, shipBaseY, _shipPosX, _shipPosY, _flightAimX, _flightAimY,
 		_rollAccum, _liftSmooth, bucket, _shipDirIndex, _perspectiveX, _perspectiveY);
 }
@@ -1199,7 +1199,7 @@ void InsaneRebel1::updateShipPhysics() {
 	if (_shipBank.numSprites > 0)
 		_shipDirIndex = CLIP<int16>((int16)(vComponent + hComponent), 0, _shipBank.numSprites - 1);
 
-	debugC(DEBUG_INSANE, "RA1 ship input: frame=%d source=%s controls=%s turbulence=%d usedJoystick=%d raw=(%d,%d) clipped=(%d,%d) storedAxis=(%d,%d) actionState(L,R,U,D)=(%d,%d,%d,%d) roll=%d lift=%d pos=(%d,%d) view=(%d,%d) dir=%d level=%d mode=%d opcode=0x%X",
+	debugC(DEBUG_INSANE, "ship input: frame=%d source=%s controls=%s turbulence=%d usedJoystick=%d raw=(%d,%d) clipped=(%d,%d) storedAxis=(%d,%d) actionState(L,R,U,D)=(%d,%d,%d,%d) roll=%d lift=%d pos=(%d,%d) view=(%d,%d) dir=%d level=%d mode=%d opcode=0x%X",
 		_gameCounter, inputSourceName,
 		"enhanced", 0, usedJoystick,
 		rawInputX, rawInputY, inputX, inputY,
@@ -1283,9 +1283,9 @@ void InsaneRebel1::updateShipPhysics() {
 			_rightPathSelected = true;
 			preserveInteractiveVideoAudioState();
 			_vm->_smushVideoShouldFinish = true;
-			debugC(DEBUG_INSANE, "RA1: Right path selected (counter=%d, shipX=%d)", _gameCounter, _shipPosX);
+			debugC(DEBUG_INSANE, "Right path selected (counter=%d, shipX=%d)", _gameCounter, _shipPosX);
 		} else {
-			debugC(DEBUG_INSANE, "RA1: Left path retained (counter=%d, shipX=%d)", _gameCounter, _shipPosX);
+			debugC(DEBUG_INSANE, "Left path retained (counter=%d, shipX=%d)", _gameCounter, _shipPosX);
 		}
 		_pathBranchEnabled = false;
 	}
@@ -1293,7 +1293,7 @@ void InsaneRebel1::updateShipPhysics() {
 	if (_currentLevel != 6)
 		checkDynamicLevelBranch();
 
-	debugC(DEBUG_INSANE, "RA1 ship: pos=(%d,%d) roll=%d lift=%d accX=%d accY=%d dir=%d health=%d corridor=[%d,%d]-[%d,%d]",
+	debugC(DEBUG_INSANE, "ship: pos=(%d,%d) roll=%d lift=%d accX=%d accY=%d dir=%d health=%d corridor=[%d,%d]-[%d,%d]",
 		_shipPosX, _shipPosY, _rollAccum, _liftSmooth,
 		_posAccumX, _posAccumY, _shipDirIndex, _health,
 		_corridorLeftX, _corridorTopY, _corridorRightX, _corridorBottomY);
@@ -1441,7 +1441,7 @@ void InsaneRebel1::updateTurretPhysics() {
 			inputY /= 2;
 		}
 
-		debugC(DEBUG_INSANE, "RA1 turret input: source=%s controls=%s mouse=(%d,%d) actions(L,R,U,D)=(%d,%d,%d,%d) raw=(%d,%d) final=(%d,%d) level=%d mode=%d opcode=0x%X",
+		debugC(DEBUG_INSANE, "turret input: source=%s controls=%s mouse=(%d,%d) actions(L,R,U,D)=(%d,%d,%d,%d) raw=(%d,%d) final=(%d,%d) level=%d mode=%d opcode=0x%X",
 			usedJoystick ? "joystick-actions" : "mouse-path",
 			"enhanced",
 			_vm->_mouse.x, _vm->_mouse.y,
@@ -1623,13 +1623,13 @@ void InsaneRebel1::updateGameOp0BPhysics() {
 		// FUN_1CDA7 dispatches g_sfxDamageHit, initialized from SYS/BOOM.SAD.
 		playSfx(kSfxBoom, 127, 0);
 		if (_currentLevel == 1) {
-			debugC(DEBUG_INSANE, "RA1 L2 player hit: frame=%u view=(%d,%d) latch=%u asteroid=%d flags=0x%02x health=%d->%d",
+			debugC(DEBUG_INSANE, "L2 player hit: frame=%u view=(%d,%d) latch=%u asteroid=%d flags=0x%02x health=%d->%d",
 				(unsigned)(uint16)_gameCounter, _perspectiveX, _perspectiveY,
 				(unsigned)_gameLatch5D, level2AsteroidHit ? 1 : 0,
 				appliedDamageFlags, oldHealth, _health);
 		}
 		if (level8WalkerPlayerHit) {
-			debugC(DEBUG_INSANE, "RA1 L8 player hit by walker: route=%d frame=%u view=(%d,%d) flags=0x%02x health=%d->%d",
+			debugC(DEBUG_INSANE, "L8 player hit by walker: route=%d frame=%u view=(%d,%d) flags=0x%02x health=%d->%d",
 				CLIP<int>(_levelRouteIndex, 0, 2), (unsigned)(uint16)_gameCounter,
 				_perspectiveX, _perspectiveY, appliedDamageFlags, oldHealth, _health);
 		}
@@ -1701,7 +1701,7 @@ void InsaneRebel1::updateGameOp0BPhysics() {
 	}
 	_inputAxisDeltaX = inputX;
 
-	debugC(DEBUG_INSANE, "RA1 GAME 0x0B input: frame=%d source=%s controls=%s window=%d precisionPad=%d view=(%d,%d) health=%d prevFlags=0x%02x axis=(%d,%d) mouse=(%d,%d) actions(L,R,U,D)=(%d,%d,%d,%d) raw=(%d,%d) final=(%d,%d) level=%d opcode=0x%X",
+	debugC(DEBUG_INSANE, "GAME 0x0B input: frame=%d source=%s controls=%s window=%d precisionPad=%d view=(%d,%d) health=%d prevFlags=0x%02x axis=(%d,%d) mouse=(%d,%d) actions(L,R,U,D)=(%d,%d,%d,%d) raw=(%d,%d) final=(%d,%d) level=%d opcode=0x%X",
 		_gameCounter,
 		inputSourceName,
 		"enhanced",
@@ -1774,7 +1774,7 @@ void InsaneRebel1::updateGameOp0BPhysics() {
 			const bool target215Destroyed = isFrameObjectPrimarySet(215);
 			if (!target211Destroyed || !target213Destroyed || !target215Destroyed) {
 				_levelGameplayPhase = 1;
-				debugC(DEBUG_INSANE, "RA1 L12 retry armed: frame=0x%04x targets=(%d,%d,%d)",
+				debugC(DEBUG_INSANE, "L12 retry armed: frame=0x%04x targets=(%d,%d,%d)",
 					_frameCounter,
 					target211Destroyed ? 1 : 0,
 					target213Destroyed ? 1 : 0,
@@ -1858,7 +1858,7 @@ void InsaneRebel1::updateGameOp0BPhysics() {
 
 	checkDynamicLevelBranch();
 
-	debugC(DEBUG_INSANE, "RA1 GAME 0x0B: pos=(%d,%d) avg=(%d,%d) view=(%d,%d) health=%d flash=%d",
+	debugC(DEBUG_INSANE, "GAME 0x0B: pos=(%d,%d) avg=(%d,%d) view=(%d,%d) health=%d flash=%d",
 		_shipPosX, _shipPosY, _avgInputX, _avgInputY,
 		_perspectiveX, _perspectiveY, _health, _screenFlash);
 }
@@ -2046,7 +2046,7 @@ void InsaneRebel1::updateOnFootSequence() {
 		_damageCooldown = 3;
 		playSfx(kSfxBoom, 127, 0);
 		_screenFlash = 5;
-		debugC(DEBUG_INSANE, "RA1 on-foot player hit: frame=%u latch=%u flags=0x%02x health=%d->%d",
+		debugC(DEBUG_INSANE, "on-foot player hit: frame=%u latch=%u flags=0x%02x health=%d->%d",
 			(unsigned)(uint16)_gameCounter, (unsigned)_gameLatch5D, _damageFlags, oldHealth, _health);
 	}
 }
@@ -2103,7 +2103,7 @@ void InsaneRebel1::handleGameOpcode5EReset(uint32 param1) {
 	// RA1 dispatcher inline reset/init path (FUN_1BE1B case 0x5E).
 	// This is not a pure control-mode assignment.
 	if (_frameDispatchFlags & 0x40) {
-		debugC(DEBUG_INSANE, "RA1 GAME 0x5E: reset suppressed by dispatch flags=0x%02x",
+		debugC(DEBUG_INSANE, "GAME 0x5E: reset suppressed by dispatch flags=0x%02x",
 			_frameDispatchFlags);
 		return;
 	}
@@ -2177,7 +2177,7 @@ void InsaneRebel1::handleGameOpcode5EReset(uint32 param1) {
 	if (_currentLevel == 7)
 		memset(_frameObjectState + 150, 0xFF, 150);
 
-	debugC(DEBUG_INSANE, "RA1 GAME 0x5E: reset state field1=%d mode=%d", (int32)param1, (int)_flyControlMode);
+	debugC(DEBUG_INSANE, "GAME 0x5E: reset state field1=%d mode=%d", (int32)param1, (int)_flyControlMode);
 }
 
 void InsaneRebel1::handleGameOpcode5DLinkLatch(uint32 param1) {
@@ -2192,7 +2192,7 @@ void InsaneRebel1::handleGameOpcode5DLinkLatch(uint32 param1) {
 		}
 	}
 
-	debugC(DEBUG_INSANE, "RA1 GAME 0x5D (link/event latch) param=%u", _gameLatch5D);
+	debugC(DEBUG_INSANE, "GAME 0x5D (link/event latch) param=%u", _gameLatch5D);
 }
 
 void InsaneRebel1::handleGameOpcode5FRandomHitLatch(uint32 param1) {
@@ -2205,7 +2205,7 @@ void InsaneRebel1::handleGameOpcode5FRandomHitLatch(uint32 param1) {
 		}
 	}
 
-	debugC(DEBUG_INSANE, "RA1 GAME 0x5F (random-hit latch) param=%u", _gameLatch5F);
+	debugC(DEBUG_INSANE, "GAME 0x5F (random-hit latch) param=%u", _gameLatch5F);
 }
 
 void InsaneRebel1::handleGameOpcode07ShipFlight(int32 subSize, Common::SeekableReadStream &b, uint32 param1) {
@@ -2219,7 +2219,7 @@ void InsaneRebel1::handleGameOpcode07ShipFlight(int32 subSize, Common::SeekableR
 		b.readUint32BE(); // f2 (max frames, unused in physics)
 		_driftParam = (int16)(int32)b.readUint32BE();
 		b.readUint32BE(); // f4 (unused in original assembly)
-		debugC(DEBUG_INSANE, "RA1 GAME 0x07: counter=%d driftParam=%d", _gameCounter, _driftParam);
+		debugC(DEBUG_INSANE, "GAME 0x07: counter=%d driftParam=%d", _gameCounter, _driftParam);
 	}
 }
 
@@ -2285,13 +2285,13 @@ void InsaneRebel1::handleGameOpcode0DCorridor(int32 subSize, Common::SeekableRea
 		}
 	}
 	if ((_damageFlags & 0x0F) != oldDirectionalFlags) {
-		debugC(DEBUG_INSANE, "RA1 0x0D hit: ship=(%d,%d) corridor=[%d,%d]-[%d,%d] flags=0x%02x zoneSuppressed=%d",
+		debugC(DEBUG_INSANE, "0x0D hit: ship=(%d,%d) corridor=[%d,%d]-[%d,%d] flags=0x%02x zoneSuppressed=%d",
 			collisionShipX, collisionShipY,
 			_corridorLeftX, _corridorTopY, _corridorRightX, _corridorBottomY,
 			_damageFlags, suppressDirectionalDamage ? 1 : 0);
 	}
 
-	debugC(DEBUG_INSANE, "RA1 GAME 0x0D: raw=[%d,%d]+(%d,%d) cam=(%d,%d) transformed=[%d,%d]-[%d,%d]",
+	debugC(DEBUG_INSANE, "GAME 0x0D: raw=[%d,%d]+(%d,%d) cam=(%d,%d) transformed=[%d,%d]-[%d,%d]",
 		corridorLeft, corridorTop, corridorWidth, corridorHeight,
 		_perspectiveX, _perspectiveY,
 		_corridorLeftX, _corridorTopY, _corridorRightX, _corridorBottomY);
@@ -2327,12 +2327,12 @@ void InsaneRebel1::handleGameOpcode0EZone(int32 subSize, Common::SeekableReadStr
 		collisionShipX > zoneLeft && collisionShipX < zoneRight &&
 		collisionShipY > zoneTop && collisionShipY < zoneBottom) {
 		_damageFlags |= 0x10;
-		debugC(DEBUG_INSANE, "RA1 0x0E hit: ship=(%d,%d) zone=[%d,%d]-[%d,%d] raw=[%d,%d]+(%d,%d) cam=(%d,%d) flags=0x%02x",
+		debugC(DEBUG_INSANE, "0x0E hit: ship=(%d,%d) zone=[%d,%d]-[%d,%d] raw=[%d,%d]+(%d,%d) cam=(%d,%d) flags=0x%02x",
 			collisionShipX, collisionShipY, zoneLeft, zoneTop, zoneRight, zoneBottom,
 			rawZoneLeft, rawZoneTop, zoneWidth, zoneHeight,
 			_perspectiveX, _perspectiveY, _damageFlags);
 	}
-	debugC(DEBUG_INSANE, "RA1 GAME 0x0E: ship=(%d,%d) zone=[%d,%d]-[%d,%d] cam=(%d,%d) flags=0x%02x",
+	debugC(DEBUG_INSANE, "GAME 0x0E: ship=(%d,%d) zone=[%d,%d]-[%d,%d] cam=(%d,%d) flags=0x%02x",
 		collisionShipX, collisionShipY, zoneLeft, zoneTop, zoneRight, zoneBottom,
 		_perspectiveX, _perspectiveY, _damageFlags);
 }
@@ -2353,10 +2353,10 @@ void InsaneRebel1::handleGameOpcode0BFirstPerson(int32 subSize, Common::Seekable
 		if (_interactiveVideoActive && maxFrames > 0 &&
 			_gameCounter >= (int32)maxFrames - 1) {
 			_vm->_smushVideoShouldFinish = true;
-			debugC(DEBUG_INSANE, "RA1: finishing 0x0B interactive video at counter=%d/%u", _gameCounter, maxFrames);
+			debugC(DEBUG_INSANE, "finishing 0x0B interactive video at counter=%d/%u", _gameCounter, maxFrames);
 		}
 	}
-	debugC(DEBUG_INSANE, "RA1 GAME 0x0B: counter=%d", _gameCounter);
+	debugC(DEBUG_INSANE, "GAME 0x0B: counter=%d", _gameCounter);
 	if (!_gameOp0BPhysicsUpdatedThisFrame) {
 		updateGameOp0BPhysics();
 		_gameOp0BPhysicsUpdatedThisFrame = true;
@@ -2396,7 +2396,7 @@ void InsaneRebel1::handleGameOpcode5ATarget(int32 subSize, Common::SeekableReadS
 			checkTargetHit(targetIdx, left, top, right, bottom);
 		}
 	}
-	debugC(DEBUG_INSANE, "RA1 GAME 0x5A: target=%d rect=[%d,%d]-[%d,%d] prox=%d",
+	debugC(DEBUG_INSANE, "GAME 0x5A: target=%d rect=[%d,%d]-[%d,%d] prox=%d",
 		targetIdx, left, top, right, bottom, _targetProximity);
 }
 
@@ -2409,10 +2409,10 @@ void InsaneRebel1::handleGameCounterOpcode(uint32 opcode, int32 subSize, Common:
 		uint32 param3 = b.readUint32BE();
 		uint32 param4 = b.readUint32BE();
 		if (opcode == 0x09) {
-			debugC(DEBUG_INSANE, "RA1 GAME 0x09: counter=%d params=(%d,%d,%d) opcodeMask=0x%08x",
+			debugC(DEBUG_INSANE, "GAME 0x09: counter=%d params=(%d,%d,%d) opcodeMask=0x%08x",
 				_gameCounter, param2, param3, param4, _frameGameOpcodeMask);
 		} else {
-			debugC(DEBUG_INSANE, "RA1 GAME 0x%02x: counter=%d params=(%d,%d,%d)",
+			debugC(DEBUG_INSANE, "GAME 0x%02x: counter=%d params=(%d,%d,%d)",
 				opcode, _gameCounter, param2, param3, param4);
 		}
 	}
@@ -2433,14 +2433,14 @@ void InsaneRebel1::handleGameChunk(int32 subSize, Common::SeekableReadStream &b,
 	// g_combatModeFlags skips gameplay dispatch entirely; bit 5 of g_hudDisableFlags
 	// suppresses the handlers while still requesting HUD refresh for a few opcodes.
 	if (_gameplayFlags75ff & 1) {
-		debugC(DEBUG_INSANE, "RA1 GAME 0x%02x: skipped by combat mode flags=0x%02x",
+		debugC(DEBUG_INSANE, "GAME 0x%02x: skipped by combat mode flags=0x%02x",
 			opcode, _gameplayFlags75ff);
 		return;
 	}
 	if (_gameplayFlags75fe & 0x20) {
 		if (ra1DispatcherHudOnlyWhenDisabled(opcode))
 			_hudRenderFlag = 0xFF;
-		debugC(DEBUG_INSANE, "RA1 GAME 0x%02x: skipped by HUD disable flags=0x%02x",
+		debugC(DEBUG_INSANE, "GAME 0x%02x: skipped by HUD disable flags=0x%02x",
 			opcode, _gameplayFlags75fe);
 		return;
 	}
@@ -2488,7 +2488,7 @@ void InsaneRebel1::handleGameChunk(int32 subSize, Common::SeekableReadStream &b,
 		break;
 
 	default:
-		debugC(DEBUG_INSANE, "RA1 GAME unknown 0x%02x size=%d", opcode, subSize);
+		debugC(DEBUG_INSANE, "GAME unknown 0x%02x size=%d", opcode, subSize);
 		break;
 	}
 }
@@ -2562,11 +2562,11 @@ void InsaneRebel1::processShot() {
 	playSfx(torpedoMode ? kSfxAlert : kSfxLaserShot, 127, 0);
 
 	if (effectiveOpcode == 0x09 || _currentLevel == 4) {
-		debugC(DEBUG_INSANE, "RA1 shot: opcode=0x%02x frame=%d slot=%d cursor=(%d,%d) origin=(%d,%d) dir=%d mode=%d",
+		debugC(DEBUG_INSANE, "shot: opcode=0x%02x frame=%d slot=%d cursor=(%d,%d) origin=(%d,%d) dir=%d mode=%d",
 			effectiveOpcode, _gameCounter, slot, cursorX, cursorY, originX, originY,
 			_shipDirIndex, _flyControlMode);
 	} else {
-		debugC(DEBUG_INSANE, "RA1 shot: slot=%d pos=(%d,%d) origin=(%d,%d)", slot,
+		debugC(DEBUG_INSANE, "shot: slot=%d pos=(%d,%d) origin=(%d,%d)", slot,
 			cursorX, cursorY, originX, originY);
 	}
 }
@@ -2610,7 +2610,7 @@ void InsaneRebel1::checkTargetHit(int16 targetIdx, int16 left, int16 top, int16
 		if (slot < kMaxTargetBoxes)
 			_targetBoxVariant[slot] = CLIP<int16>((int16)(_targetBoxVariant[slot] + 3), 0, 5);
 
-		debugC(DEBUG_INSANE, "RA1 target near: opcode=0x%02x target=%d raw=[%d,%d]-[%d,%d] cursorScreen=(%d,%d) cursorTest=(%d,%d) snap=%d prox=%d view=(%d,%d)",
+		debugC(DEBUG_INSANE, "target near: opcode=0x%02x target=%d raw=[%d,%d]-[%d,%d] cursorScreen=(%d,%d) cursorTest=(%d,%d) snap=%d prox=%d view=(%d,%d)",
 			effectiveOpcode, targetIdx, left, top, right, bottom,
 			screenCursorX, screenCursorY, curX, curY, snap, _targetProximity,
 			_perspectiveX, _perspectiveY);
@@ -2667,7 +2667,7 @@ void InsaneRebel1::checkTargetHit(int16 targetIdx, int16 left, int16 top, int16
 									damagedState += Common::String::format("%d,", objectId);
 							}
 
-							debugC(DEBUG_INSANE, "RA1 L8 armor: hitObject=%d damaged=[%s] hidden=[%s]",
+							debugC(DEBUG_INSANE, "L8 armor: hitObject=%d damaged=[%s] hidden=[%s]",
 								targetIdx + 1, damagedState.c_str(), hiddenState.c_str());
 						}
 
@@ -2678,7 +2678,7 @@ void InsaneRebel1::checkTargetHit(int16 targetIdx, int16 left, int16 top, int16
 						if ((_gameplayFlags75fe & 0x10) == 0)
 							playSfx(kSfxExplode, 127, sfxPan);
 
-						debugC(DEBUG_INSANE, "RA1 HIT: target=%d gost=%d pos=(%d,%d) score=%d kills=%d bangSprites=%d",
+						debugC(DEBUG_INSANE, "HIT: target=%d gost=%d pos=(%d,%d) score=%d kills=%d bangSprites=%d",
 							targetIdx, gi, _gostSlots[gi].posX, _gostSlots[gi].posY,
 							_score, _killCount, _bangBank.numSprites);
 						return;
diff --git a/engines/scumm/insane/rebel1/menu.cpp b/engines/scumm/insane/rebel1/menu.cpp
index f0613e5c408..6a02456d482 100644
--- a/engines/scumm/insane/rebel1/menu.cpp
+++ b/engines/scumm/insane/rebel1/menu.cpp
@@ -714,7 +714,7 @@ bool InsaneRebel1::notifyEvent(const Common::Event &event) {
 				_gameplayMouseSettleUntil = now + kRA1GameplayMouseSettleExtendMs;
 				warpGameplayMouseNow(recenterX, recenterY);
 
-				debugC(DEBUG_INSANE, "RA1 mouse settle: suppress pos=(%d,%d) rel=(%d,%d) current=(%d,%d) until=%u opcode=0x%X",
+				debugC(DEBUG_INSANE, "mouse settle: suppress pos=(%d,%d) rel=(%d,%d) current=(%d,%d) until=%u opcode=0x%X",
 					event.mouse.x, event.mouse.y, event.relMouse.x, event.relMouse.y,
 					_vm->_mouse.x, _vm->_mouse.y, _gameplayMouseSettleUntil,
 					getEffectiveGameOpcode());
@@ -745,14 +745,14 @@ bool InsaneRebel1::notifyEvent(const Common::Event &event) {
 
 	if (event.type == Common::EVENT_JOYAXIS_MOTION) {
 		_lastJoystickAxisEventTime = _vm->_system->getMillis();
-		debugC(DEBUG_INSANE, "RA1 input raw-joy-axis: axis=%d pos=%d menu=%d gameplay=%d storedAxis=(%d,%d)",
+		debugC(DEBUG_INSANE, "input raw-joy-axis: axis=%d pos=%d menu=%d gameplay=%d storedAxis=(%d,%d)",
 			event.joystick.axis, event.joystick.position,
 			_menuActive, _interactiveVideoActive && !_menuActive,
 			_joystickAxisX, _joystickAxisY);
 	}
 
 	if (event.type == Common::EVENT_JOYBUTTON_DOWN || event.type == Common::EVENT_JOYBUTTON_UP) {
-		debugC(DEBUG_INSANE, "RA1 input raw-joy-button: button=%d pressed=%d menu=%d gameplay=%d storedAxis=(%d,%d)",
+		debugC(DEBUG_INSANE, "input raw-joy-button: button=%d pressed=%d menu=%d gameplay=%d storedAxis=(%d,%d)",
 			event.joystick.button, event.type == Common::EVENT_JOYBUTTON_DOWN,
 			_menuActive, _interactiveVideoActive && !_menuActive,
 			_joystickAxisX, _joystickAxisY);
@@ -768,12 +768,12 @@ bool InsaneRebel1::notifyEvent(const Common::Event &event) {
 		switch (event.customType) {
 		case kScummBackendActionRebel1AxisUp:
 			if (event.joystick.position == 0 && _joystickAxisY > 0) {
-				debugC(DEBUG_INSANE, "RA1 input mapped-axis ignored-reset: %s pos=0 current=(%d,%d)",
+				debugC(DEBUG_INSANE, "input mapped-axis ignored-reset: %s pos=0 current=(%d,%d)",
 					getRebel1BackendAxisName(event.customType), _joystickAxisX, _joystickAxisY);
 				return true;
 			}
 			_joystickAxisY = -axisPosition;
-			debugC(DEBUG_INSANE, "RA1 input mapped-axis: %s pos=%d rawPos=%d old=(%d,%d) new=(%d,%d) menu=%d gameplay=%d",
+			debugC(DEBUG_INSANE, "input mapped-axis: %s pos=%d rawPos=%d old=(%d,%d) new=(%d,%d) menu=%d gameplay=%d",
 				getRebel1BackendAxisName(event.customType), axisPosition, event.joystick.position,
 				oldAxisX, oldAxisY, _joystickAxisX, _joystickAxisY,
 				_menuActive, _interactiveVideoActive && !_menuActive);
@@ -782,12 +782,12 @@ bool InsaneRebel1::notifyEvent(const Common::Event &event) {
 			return true;
 		case kScummBackendActionRebel1AxisDown:
 			if (event.joystick.position == 0 && _joystickAxisY < 0) {
-				debugC(DEBUG_INSANE, "RA1 input mapped-axis ignored-reset: %s pos=0 current=(%d,%d)",
+				debugC(DEBUG_INSANE, "input mapped-axis ignored-reset: %s pos=0 current=(%d,%d)",
 					getRebel1BackendAxisName(event.customType), _joystickAxisX, _joystickAxisY);
 				return true;
 			}
 			_joystickAxisY = axisPosition;
-			debugC(DEBUG_INSANE, "RA1 input mapped-axis: %s pos=%d rawPos=%d old=(%d,%d) new=(%d,%d) menu=%d gameplay=%d",
+			debugC(DEBUG_INSANE, "input mapped-axis: %s pos=%d rawPos=%d old=(%d,%d) new=(%d,%d) menu=%d gameplay=%d",
 				getRebel1BackendAxisName(event.customType), axisPosition, event.joystick.position,
 				oldAxisX, oldAxisY, _joystickAxisX, _joystickAxisY,
 				_menuActive, _interactiveVideoActive && !_menuActive);
@@ -796,12 +796,12 @@ bool InsaneRebel1::notifyEvent(const Common::Event &event) {
 			return true;
 		case kScummBackendActionRebel1AxisLeft:
 			if (event.joystick.position == 0 && _joystickAxisX > 0) {
-				debugC(DEBUG_INSANE, "RA1 input mapped-axis ignored-reset: %s pos=0 current=(%d,%d)",
+				debugC(DEBUG_INSANE, "input mapped-axis ignored-reset: %s pos=0 current=(%d,%d)",
 					getRebel1BackendAxisName(event.customType), _joystickAxisX, _joystickAxisY);
 				return true;
 			}
 			_joystickAxisX = -axisPosition;
-			debugC(DEBUG_INSANE, "RA1 input mapped-axis: %s pos=%d rawPos=%d old=(%d,%d) new=(%d,%d) menu=%d gameplay=%d",
+			debugC(DEBUG_INSANE, "input mapped-axis: %s pos=%d rawPos=%d old=(%d,%d) new=(%d,%d) menu=%d gameplay=%d",
 				getRebel1BackendAxisName(event.customType), axisPosition, event.joystick.position,
 				oldAxisX, oldAxisY, _joystickAxisX, _joystickAxisY,
 				_menuActive, _interactiveVideoActive && !_menuActive);
@@ -810,12 +810,12 @@ bool InsaneRebel1::notifyEvent(const Common::Event &event) {
 			return true;
 		case kScummBackendActionRebel1AxisRight:
 			if (event.joystick.position == 0 && _joystickAxisX < 0) {
-				debugC(DEBUG_INSANE, "RA1 input mapped-axis ignored-reset: %s pos=0 current=(%d,%d)",
+				debugC(DEBUG_INSANE, "input mapped-axis ignored-reset: %s pos=0 current=(%d,%d)",
 					getRebel1BackendAxisName(event.customType), _joystickAxisX, _joystickAxisY);
 				return true;
 			}
 			_joystickAxisX = axisPosition;
-			debugC(DEBUG_INSANE, "RA1 input mapped-axis: %s pos=%d rawPos=%d old=(%d,%d) new=(%d,%d) menu=%d gameplay=%d",
+			debugC(DEBUG_INSANE, "input mapped-axis: %s pos=%d rawPos=%d old=(%d,%d) new=(%d,%d) menu=%d gameplay=%d",
 				getRebel1BackendAxisName(event.customType), axisPosition, event.joystick.position,
 				oldAxisX, oldAxisY, _joystickAxisX, _joystickAxisY,
 				_menuActive, _interactiveVideoActive && !_menuActive);
@@ -831,7 +831,7 @@ bool InsaneRebel1::notifyEvent(const Common::Event &event) {
 		event.type == Common::EVENT_CUSTOM_ENGINE_ACTION_END) {
 		const bool pressed = (event.type == Common::EVENT_CUSTOM_ENGINE_ACTION_START);
 
-		debugC(DEBUG_INSANE, "RA1 input mapped-action: action=%s custom=%u pressed=%d menu=%d gameplay=%d storedAxis=(%d,%d) actionState(L,R,U,D)=(%d,%d,%d,%d)",
+		debugC(DEBUG_INSANE, "input mapped-action: action=%s custom=%u pressed=%d menu=%d gameplay=%d storedAxis=(%d,%d) actionState(L,R,U,D)=(%d,%d,%d,%d)",
 			getRebel1ActionName(event.customType), event.customType, pressed,
 			_menuActive, _interactiveVideoActive && !_menuActive,
 			_joystickAxisX, _joystickAxisY,
@@ -947,10 +947,10 @@ bool InsaneRebel1::notifyEvent(const Common::Event &event) {
 		const uint32 elapsedSinceAxis = _lastJoystickAxisEventTime ? now - _lastJoystickAxisEventTime : 0xffffffffu;
 
 		if (_interactiveVideoActive && !_menuActive) {
-			debugC(DEBUG_INSANE, "RA1 input mainmenu-event: gameplay=1 elapsedSinceAxis=%u storedAxis=(%d,%d)",
+			debugC(DEBUG_INSANE, "input mainmenu-event: gameplay=1 elapsedSinceAxis=%u storedAxis=(%d,%d)",
 				elapsedSinceAxis, _joystickAxisX, _joystickAxisY);
 			if (elapsedSinceAxis <= kRA1JoystickAxisEscGuardMs) {
-				debugC(DEBUG_INSANE, "RA1 input ignored mainmenu event after recent joystick axis movement (%u ms)", elapsedSinceAxis);
+				debugC(DEBUG_INSANE, "input ignored mainmenu event after recent joystick axis movement (%u ms)", elapsedSinceAxis);
 				return true;
 			}
 
@@ -966,11 +966,11 @@ bool InsaneRebel1::notifyEvent(const Common::Event &event) {
 			event.kbd.keycode == Common::KEYCODE_ESCAPE) {
 			const uint32 now = _vm->_system->getMillis();
 			const uint32 elapsedSinceAxis = _lastJoystickAxisEventTime ? now - _lastJoystickAxisEventTime : 0xffffffffu;
-			debugC(DEBUG_INSANE, "RA1 input keydown-escape: gameplay=1 ascii=%d flags=0x%x repeat=%d elapsedSinceAxis=%u storedAxis=(%d,%d)",
+			debugC(DEBUG_INSANE, "input keydown-escape: gameplay=1 ascii=%d flags=0x%x repeat=%d elapsedSinceAxis=%u storedAxis=(%d,%d)",
 				event.kbd.ascii, event.kbd.flags, event.kbdRepeat,
 				elapsedSinceAxis, _joystickAxisX, _joystickAxisY);
 			if (elapsedSinceAxis <= kRA1JoystickAxisEscGuardMs) {
-				debugC(DEBUG_INSANE, "RA1 input ignored ESC after recent joystick axis movement (%u ms)", elapsedSinceAxis);
+				debugC(DEBUG_INSANE, "input ignored ESC after recent joystick axis movement (%u ms)", elapsedSinceAxis);
 				return true;
 			}
 
@@ -1282,7 +1282,7 @@ int InsaneRebel1::runPasscodeEntryDialog() {
 			_maxChapterUnlocked = MAX<int16>(_maxChapterUnlocked, i);
 			if (targetLevel <= kRA1NumLevels)
 				_startLevel = targetLevel;
-			debugC(DEBUG_INSANE, "RA1 passcode accepted: slot=%d password=%s difficulty=%d target=%d",
+			debugC(DEBUG_INSANE, "passcode accepted: slot=%d password=%s difficulty=%d target=%d",
 				i, password, _difficulty, targetLevel);
 			return targetLevel;
 		}
@@ -1298,13 +1298,13 @@ int InsaneRebel1::runPasscodeEntryDialog() {
 			_difficulty = getRebel1ThreeDOPasscodeDifficulty(i);
 			if (targetLevel <= kRA1NumLevels)
 				_startLevel = targetLevel;
-			debugC(DEBUG_INSANE, "RA1 3DO passcode accepted: slot=%d password=%s difficulty=%d target=%d",
+			debugC(DEBUG_INSANE, "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);
+	debugC(DEBUG_INSANE, "passcode rejected: '%s'", _textEntryBuffer);
 	return 0;
 }
 
@@ -1331,7 +1331,7 @@ bool InsaneRebel1::runHighScoreNameEntry() {
 	Common::strlcpy(_highScores[slot].name, storedName.c_str(), sizeof(_highScores[slot].name));
 	_highScores[slot].difficulty = _difficulty;
 	_highScoreEntryIndex = -1;
-	debugC(DEBUG_INSANE, "RA1 high score inserted: slot=%d name=%s score=%ld difficulty=%d",
+	debugC(DEBUG_INSANE, "high score inserted: slot=%d name=%s score=%ld difficulty=%d",
 		slot, _highScores[slot].name, (long)_highScores[slot].score, _highScores[slot].difficulty);
 	return true;
 }
diff --git a/engines/scumm/insane/rebel1/rebel.cpp b/engines/scumm/insane/rebel1/rebel.cpp
index e4bfb41ef65..b1e67bafc9c 100644
--- a/engines/scumm/insane/rebel1/rebel.cpp
+++ b/engines/scumm/insane/rebel1/rebel.cpp
@@ -193,7 +193,7 @@ void InsaneRebel1::loadTuningForLevel(int level) {
 	_protectedTargetA = 0;
 	_protectedTargetB = 0;
 
-	debugC(DEBUG_INSANE, "RA1: Loaded tuning level=%d diff=%d: roll=%d lift=%d slide=%d drift=%d snap=%d "
+	debugC(DEBUG_INSANE, "Loaded tuning level=%d diff=%d: roll=%d lift=%d slide=%d drift=%d snap=%d "
 		"miss=%d wham=%d shot=%d kill=%d time=%d levelPts=%d bonus=%d flags=0x%x",
 		level, d, _tuning.roll, _tuning.lift, _tuning.slide, _tuning.drift, _tuning.snap,
 		_tuning.miss, _tuning.wham, _tuning.shot, _tuning.kill,
diff --git a/engines/scumm/insane/rebel1/render.cpp b/engines/scumm/insane/rebel1/render.cpp
index aafe827bde7..a485368963a 100644
--- a/engines/scumm/insane/rebel1/render.cpp
+++ b/engines/scumm/insane/rebel1/render.cpp
@@ -1355,7 +1355,7 @@ void InsaneRebel1::renderLaserShots(byte *dst, int pitch, int width, int height)
 				const int start1Y = shipBaseY + emit.y1;
 				const int start2X = shipBaseX + emit.x2;
 				const int start2Y = shipBaseY + emit.y2;
-				debugC(DEBUG_INSANE, "RA1 op09 shotRender: frame=%d timer=%d shipBase=(%d,%d) target=(%d,%d) emit1=(%d,%d) emit2=(%d,%d) dir=%d variant=%d mode=%d",
+				debugC(DEBUG_INSANE, "op09 shotRender: frame=%d timer=%d shipBase=(%d,%d) target=(%d,%d) emit1=(%d,%d) emit2=(%d,%d) dir=%d variant=%d mode=%d",
 					_gameCounter, timer, shipBaseX, shipBaseY, targetX, targetY,
 					start1X, start1Y, start2X, start2Y, _shipDirIndex,
 					_shotSlots[i].variant, _flyControlMode);
@@ -2006,7 +2006,7 @@ void InsaneRebel1::updateLevel8WalkerState() {
 			_pendingRouteIndex = newRoute;
 			_pendingRouteCutoverFrame = _gameCounter + 7;
 			_pendingRouteStartFrame = _pendingRouteCutoverFrame;
-			debugC(DEBUG_INSANE, "RA1 L8 branch: route=%d -> %d at frame=%u shipX=%d resumeTimelineFrame=%d cutoverFrame=%d",
+			debugC(DEBUG_INSANE, "L8 branch: route=%d -> %d at frame=%u shipX=%d resumeTimelineFrame=%d cutoverFrame=%d",
 				route, newRoute, (unsigned)_gameCounter, _shipPosX,
 				(int)_pendingRouteStartFrame, (int)_pendingRouteCutoverFrame);
 		}
diff --git a/engines/scumm/insane/rebel1/runlevels.cpp b/engines/scumm/insane/rebel1/runlevels.cpp
index 02b741f8a74..b4bc7849507 100644
--- a/engines/scumm/insane/rebel1/runlevels.cpp
+++ b/engines/scumm/insane/rebel1/runlevels.cpp
@@ -576,7 +576,7 @@ bool InsaneRebel1::runLevel4() {
 			if (shieldGeneratorsDestroyed)
 				break;
 
-			debugC(DEBUG_INSANE, "RA1 L4 replaying shield-generator phase: hits=(%d,%d) protected=(%d,%d)",
+			debugC(DEBUG_INSANE, "L4 replaying shield-generator phase: hits=(%d,%d) protected=(%d,%d)",
 				(int)_shieldGenHitsA, (int)_shieldGenHitsB,
 				(int)_protectedTargetA, (int)_protectedTargetB);
 			replayingShieldGeneratorPhase = true;
@@ -892,7 +892,7 @@ bool InsaneRebel1::runLevel8() {
 			// shield register is nonzero. ScummVM's blocking SMUSH play returns
 			// when one route pass ends, so explicitly replay that route and
 			// preserve the accumulated walker damage.
-			debugC(DEBUG_INSANE, "RA1 L8 replaying route=%d walkerHealth=%d killCount=%d",
+			debugC(DEBUG_INSANE, "L8 replaying route=%d walkerHealth=%d killCount=%d",
 				route, (int)_walkerHealth, (int)_killCount);
 			routeStartFrame = 0;
 			_walkerTimer = 0;
@@ -958,7 +958,7 @@ bool InsaneRebel1::runLevel9() {
 			if (_killCount > 0)
 				return (_shipPosX < kRA1CenterX) ? 0 : 1;
 
-			debugC(DEBUG_INSANE, "RA1 L9 selector '%s' ended without target hit; replaying", filename);
+			debugC(DEBUG_INSANE, "L9 selector '%s' ended without target hit; replaying", filename);
 		}
 		return -1;
 	};
@@ -1342,7 +1342,7 @@ bool InsaneRebel1::runLevel14() {
 			if (level14Phase1Complete)
 				break;
 
-			debugC(DEBUG_INSANE, "RA1 L14 replaying phase 1: successFrames=%d targetsDestroyed=%d",
+			debugC(DEBUG_INSANE, "L14 replaying phase 1: successFrames=%d targetsDestroyed=%d",
 				(int)_level14SuccessFrames, areLevel14Phase1TargetsDestroyed() ? 1 : 0);
 			replayingLevel14Phase1 = true;
 		}
@@ -1401,7 +1401,7 @@ bool InsaneRebel1::runLevel14() {
 				if (level14Phase2Complete)
 					break;
 
-				debugC(DEBUG_INSANE, "RA1 L14 replaying phase 2: video=%s successFrames=%d targetsDestroyed=%d",
+				debugC(DEBUG_INSANE, "L14 replaying phase 2: video=%s successFrames=%d targetsDestroyed=%d",
 					level14Phase2Video, (int)_level14SuccessFrames,
 					areLevel14Phase2TargetsDestroyed() ? 1 : 0);
 				replayingLevel14Phase2 = true;
@@ -1735,30 +1735,30 @@ void InsaneRebel1::resolveSeek(const char *filename, int32 startFrame, int32 &vi
 			_pendingRouteVideoStartFrame : 1;
 		videoOffset = findAnimFrameChunkOffset(_vm, filename, videoStartFrame);
 		if (videoOffset < 0) {
-			debugC(DEBUG_INSANE, "RA1 L7 route switch: route=%d destinationFrame=%d offset lookup failed",
+			debugC(DEBUG_INSANE, "L7 route switch: route=%d destinationFrame=%d offset lookup failed",
 				_levelRouteIndex, (int)videoStartFrame);
 			videoStartFrame = 0;
 			videoOffset = 0;
 		} else {
-			debugC(DEBUG_INSANE, "RA1 L7 route switch: route=%d decisionLocalFrame=%d opens destination at localFrame=%d offset=0x%x",
+			debugC(DEBUG_INSANE, "L7 route switch: route=%d decisionLocalFrame=%d opens destination at localFrame=%d offset=0x%x",
 				_levelRouteIndex, (int)_pendingRouteStartFrame,
 				(int)videoStartFrame, (unsigned)videoOffset);
 		}
 	} else if (_currentLevel == 7 && resumingRoute) {
 		videoOffset = findAnimFrameChunkOffsetByGameCounter(_vm, filename, startFrame, videoStartFrame);
 		if (videoOffset < 0) {
-			debugC(DEBUG_INSANE, "RA1 L8 resume: route=%d timelineFrame=%d GAME counter lookup failed",
+			debugC(DEBUG_INSANE, "L8 resume: route=%d timelineFrame=%d GAME counter lookup failed",
 				_levelRouteIndex, (int)startFrame);
 			videoStartFrame = startFrame;
 			videoOffset = findAnimFrameChunkOffset(_vm, filename, videoStartFrame);
 		}
 		if (videoOffset < 0) {
-			debugC(DEBUG_INSANE, "RA1 L8 resume: route=%d timelineFrame=%d localFrame=%d offset lookup failed",
+			debugC(DEBUG_INSANE, "L8 resume: route=%d timelineFrame=%d localFrame=%d offset lookup failed",
 				_levelRouteIndex, (int)startFrame, (int)videoStartFrame);
 			videoStartFrame = 0;
 			videoOffset = 0;
 		} else {
-			debugC(DEBUG_INSANE, "RA1 L8 resume: route=%d timelineFrame=%d -> localFrame=%d offset=0x%x",
+			debugC(DEBUG_INSANE, "L8 resume: route=%d timelineFrame=%d -> localFrame=%d offset=0x%x",
 				_levelRouteIndex, (int)startFrame, (int)videoStartFrame, (unsigned)videoOffset);
 		}
 	} else if (_currentLevel == 13 && resumingRoute) {
@@ -1766,7 +1766,7 @@ void InsaneRebel1::resolveSeek(const char *filename, int32 startFrame, int32 &vi
 		// oldMaxFrame-0x0F, 1, -1). That frame number belongs to L14PLAY2's
 		// timeline; L14PLY2B is already the continuation clip and starts at its
 		// matching lead-in frame. Preserve the current state, but do not seek.
-		debugC(DEBUG_INSANE, "RA1 L14 splice: L14PLAY2 timelineFrame=%d -> L14PLY2B frame 0",
+		debugC(DEBUG_INSANE, "L14 splice: L14PLAY2 timelineFrame=%d -> L14PLY2B frame 0",
 			(int)startFrame);
 	}
 }
@@ -1796,7 +1796,7 @@ void InsaneRebel1::captureInteractiveVideoInput() {
 	if (!isTouchscreenActive())
 		g_system->lockMouse(true);
 
-	debugC(DEBUG_INSANE, "RA1 centerGameplayAim: mouse=(%d,%d) joystick=(%d,%d) gamepadAim=%d settleUntil=%u preserve=%d",
+	debugC(DEBUG_INSANE, "centerGameplayAim: mouse=(%d,%d) joystick=(%d,%d) gamepadAim=%d settleUntil=%u preserve=%d",
 		_vm->_mouse.x, _vm->_mouse.y, _joystickAxisX, _joystickAxisY,
 		_gamepadAimActive ? 1 : 0, _gameplayMouseSettleUntil,
 		preserveInputState ? 1 : 0);
diff --git a/engines/scumm/insane/rebel1/saveload.cpp b/engines/scumm/insane/rebel1/saveload.cpp
index 09335106b4d..61d7ab8bef5 100644
--- a/engines/scumm/insane/rebel1/saveload.cpp
+++ b/engines/scumm/insane/rebel1/saveload.cpp
@@ -115,7 +115,7 @@ Common::Error InsaneRebel1::writeSaveState(int slot, const Common::String &desc,
 		return Common::kWritingFailed;
 	}
 
-	debugC(DEBUG_INSANE, "RA1: saved slot=%d level=%d lives=%d score=%d desc='%s'",
+	debugC(DEBUG_INSANE, "saved slot=%d level=%d lives=%d score=%d desc='%s'",
 		slot, state.resumeLevel, state.lives, state.score, desc.c_str());
 	return Common::kNoError;
 }
@@ -219,7 +219,7 @@ Common::Error InsaneRebel1::loadGameState(int slot, bool startupLoad) {
 		_vm->_smushVideoShouldFinish = true;
 	}
 
-	debugC(DEBUG_INSANE, "RA1: loaded slot=%d level=%d lives=%d score=%d", slot, _resumeLevel, _lives, _score);
+	debugC(DEBUG_INSANE, "loaded slot=%d level=%d lives=%d score=%d", slot, _resumeLevel, _lives, _score);
 	return Common::kNoError;
 }
 
@@ -238,7 +238,7 @@ void InsaneRebel1::autosaveProgress() {
 	if (readSaveState(slot, oldState, &oldDesc)) {
 		if (oldState.resumeLevel > state.resumeLevel ||
 				(oldState.resumeLevel == state.resumeLevel && oldState.lives >= state.lives)) {
-			debugC(DEBUG_INSANE, "RA1: skipping autosave slot=%d level=%d lives=%d; existing level=%d lives=%d",
+			debugC(DEBUG_INSANE, "skipping autosave slot=%d level=%d lives=%d; existing level=%d lives=%d",
 				slot, state.resumeLevel, state.lives, oldState.resumeLevel, oldState.lives);
 			return;
 		}
diff --git a/engines/scumm/insane/rebel2/iact.cpp b/engines/scumm/insane/rebel2/iact.cpp
index d1e34b8355a..b640ad26023 100644
--- a/engines/scumm/insane/rebel2/iact.cpp
+++ b/engines/scumm/insane/rebel2/iact.cpp
@@ -49,7 +49,7 @@ static bool readLevel2BackgroundChunkHeader(Common::SeekableReadStream &stream,
 
 	const int64 dataStart = stream.pos();
 	if ((int64)chunkSize > containerEnd - dataStart) {
-		debugC(DEBUG_INSANE, "Rebel2 loadLevel2Background: Truncated %s chunk 0x%08X at %lld: size=%u, remaining=%lld",
+		debugC(DEBUG_INSANE, "loadLevel2Background: Truncated %s chunk 0x%08X at %lld: size=%u, remaining=%lld",
 			context, tag, headerPos, chunkSize, containerEnd - dataStart);
 		return false;
 	}
@@ -136,7 +136,7 @@ void InsaneRebel2::procPreRendering(byte *renderBitmap) {
 void InsaneRebel2::procIACT(byte *renderBitmap, int32 codecparam, int32 setupsan12,
 					  int32 setupsan13, Common::SeekableReadStream &b, int32 size, int32 flags,
 					  int16 par1, int16 par2, int16 par3, int16 par4) {
-	debugC(DEBUG_INSANE, "Rebel2 IACT: opcode=%d par2=%d par3=%d par4=%d gameState=%d sceneId=%d",
+	debugC(DEBUG_INSANE, "IACT: opcode=%d par2=%d par3=%d par4=%d gameState=%d sceneId=%d",
 		par1, par2, par3, par4, _gameState, _currSceneId);
 
 	if (_keyboardDisable)
@@ -146,12 +146,12 @@ void InsaneRebel2::procIACT(byte *renderBitmap, int32 codecparam, int32 setupsan
 	// Menu IACTs have par1=8 (code), par2=46 (flags), par4>=1000 (userId)
 	// The embedded ANIM contains the full menu frame
 	if (_gameState == kStateMainMenu && par1 == 8 && par4 >= 1000) {
-		debugC(DEBUG_INSANE, "Rebel2 IACT: Menu mode - processing embedded ANIM (userId=%d)", par4);
+		debugC(DEBUG_INSANE, "IACT: Menu mode - processing embedded ANIM (userId=%d)", par4);
 
 		// Scan for embedded ANIM tag in the IACT data
 		int64 startPos = b.pos();
 		int64 totalSize = b.size();
-		debugC(DEBUG_INSANE, "Rebel2 IACT: stream pos=%d, size=%d, remaining=%d",
+		debugC(DEBUG_INSANE, "IACT: stream pos=%d, size=%d, remaining=%d",
 			(int)startPos, (int)totalSize, (int)(totalSize - startPos));
 
 		if (totalSize > startPos) {
@@ -160,7 +160,7 @@ void InsaneRebel2::procIACT(byte *renderBitmap, int32 codecparam, int32 setupsan
 			byte *scanBuf = (byte *)malloc(scanSize);
 			if (scanBuf) {
 				int bytesRead = b.read(scanBuf, scanSize);
-				debugC(DEBUG_INSANE, "Rebel2 IACT: Read %d bytes, first 16: %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X",
+				debugC(DEBUG_INSANE, "IACT: Read %d bytes, first 16: %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X",
 					bytesRead, scanBuf[0], scanBuf[1], scanBuf[2], scanBuf[3],
 					scanBuf[4], scanBuf[5], scanBuf[6], scanBuf[7],
 					scanBuf[8], scanBuf[9], scanBuf[10], scanBuf[11],
@@ -172,7 +172,7 @@ void InsaneRebel2::procIACT(byte *renderBitmap, int32 codecparam, int32 setupsan
 						int64 animStreamPos = startPos + i;
 						uint32 animReportedSize = READ_BE_UINT32(scanBuf + i + 4);
 						int32 toCopy = (int)MIN<int64>((int64)animReportedSize + 8, totalSize - animStreamPos);
-						debugC(DEBUG_INSANE, "Rebel2 IACT: Found embedded ANIM at offset %d, size %d", (int)i, (int)animReportedSize);
+						debugC(DEBUG_INSANE, "IACT: Found embedded ANIM at offset %d, size %d", (int)i, (int)animReportedSize);
 						if (toCopy > 0) {
 							byte *animData = (byte *)malloc(toCopy);
 							if (animData) {
@@ -189,7 +189,7 @@ void InsaneRebel2::procIACT(byte *renderBitmap, int32 codecparam, int32 setupsan
 					}
 				}
 
-				debugC(DEBUG_INSANE, "Rebel2 IACT: No ANIM tag found in menu IACT data");
+				debugC(DEBUG_INSANE, "IACT: No ANIM tag found in menu IACT data");
 				b.seek(startPos);
 				free(scanBuf);
 			}
@@ -243,7 +243,7 @@ void InsaneRebel2::iactRebel2Scene1(byte *renderBitmap, int32 codecparam, int32
 		// Sub-opcode 0x0D (13) = Primary collision zones (obstacles)
 		// Sub-opcode 0x0E (14) = Secondary collision zones (boundaries)
 		// par2 is the sub-opcode that determines which zone table to use
-		debugC(DEBUG_INSANE, "Rebel2 IACT Opcode 5: par2=%d par3=%d par4=%d", par2, par3, par4);
+		debugC(DEBUG_INSANE, "IACT Opcode 5: par2=%d par3=%d par4=%d", par2, par3, par4);
 
 		if (par2 == 0x0D || par2 == 0x0E) {
 			// Register the collision zone from the remaining IACT data
@@ -275,7 +275,7 @@ void InsaneRebel2::iactRebel2Scene1(byte *renderBitmap, int32 codecparam, int32
 			// DAT_00443b12 = horizontal wind, DAT_00443b14 = vertical wind
 			_windParamX = body0;
 			_windParamY = body1;
-			debugC(DEBUG_INSANE, "Rebel2 Opcode 7 par4=0: wind=(%d,%d)", body0, body1);
+			debugC(DEBUG_INSANE, "Opcode 7 par4=0: wind=(%d,%d)", body0, body1);
 			break;
 		case 1:
 			// Set LEFT X boundary and TOP Y boundary
@@ -287,7 +287,7 @@ void InsaneRebel2::iactRebel2Scene1(byte *renderBitmap, int32 codecparam, int32
 			} else if (_flyControlMode == 0) {
 				_corridorLeftX += 20;
 			}
-			debugC(DEBUG_INSANE, "Rebel2 Opcode 7 par4=1: corridor left=%d top=%d (adjusted left=%d)",
+			debugC(DEBUG_INSANE, "Opcode 7 par4=1: corridor left=%d top=%d (adjusted left=%d)",
 				body0, body1, _corridorLeftX);
 			break;
 		case 2:
@@ -300,16 +300,16 @@ void InsaneRebel2::iactRebel2Scene1(byte *renderBitmap, int32 codecparam, int32
 			} else if (_flyControlMode == 0) {
 				_corridorRightX -= 20;
 			}
-			debugC(DEBUG_INSANE, "Rebel2 Opcode 7 par4=2: corridor right=%d bottom=%d (adjusted right=%d)",
+			debugC(DEBUG_INSANE, "Opcode 7 par4=2: corridor right=%d bottom=%d (adjusted right=%d)",
 				body0, body1, _corridorRightX);
 			break;
 		case 5:
 			// DAT_00443b52: repeats FLY002 ship overlay in FUN_40D836.
 			_flyOverlayRepeatCount = body0;
-			debugC(DEBUG_INSANE, "Rebel2 Opcode 7 par4=5: flyOverlayRepeat=%d", _flyOverlayRepeatCount);
+			debugC(DEBUG_INSANE, "Opcode 7 par4=5: flyOverlayRepeat=%d", _flyOverlayRepeatCount);
 			break;
 		default:
-			debugC(DEBUG_INSANE, "Rebel2 Opcode 7 par4=%d: body=(%d,%d) ??? unknown sub-opcode", par4, body0, body1);
+			debugC(DEBUG_INSANE, "Opcode 7 par4=%d: body=(%d,%d) ??? unknown sub-opcode", par4, body0, body1);
 			break;
 		}
 
@@ -324,9 +324,9 @@ void InsaneRebel2::iactRebel2Scene1(byte *renderBitmap, int32 codecparam, int32
 		iactRebel2Opcode9(renderBitmap, b, par2, par3, par4);
 	} else if (par1 == 0 || par1 == 1) {
 		// Low Opcodes seen in logs
-		debugC(DEBUG_INSANE, "Rebel2 IACT: Low Opcode %d (par2=%d par3=%d par4=%d)", par1, par2, par3, par4);
+		debugC(DEBUG_INSANE, "IACT: Low Opcode %d (par2=%d par3=%d par4=%d)", par1, par2, par3, par4);
 	} else {
-		debugC(DEBUG_INSANE, "Rebel2 IACT: Unknown Opcode %d (par2=%d par3=%d par4=%d)", par1, par2, par3, par4);
+		debugC(DEBUG_INSANE, "IACT: Unknown Opcode %d (par2=%d par3=%d par4=%d)", par1, par2, par3, par4);
 	}
 }
 
@@ -359,13 +359,13 @@ void InsaneRebel2::iactRebel2Opcode2(Common::SeekableReadStream &b, int16 par2,
 			// while the parent is alive, and revealed when the parent is destroyed.
 			if (!isBitSet(parentId)) {
 				setBit(childId);
-				debugC(DEBUG_INSANE, "Rebel2: Linked ID=%d to Parent=%d (Slot 0) - child DISABLED (parent alive)", childId, parentId);
+				debugC(DEBUG_INSANE, "Linked ID=%d to Parent=%d (Slot 0) - child DISABLED (parent alive)", childId, parentId);
 			} else {
 				clearBit(childId);
-				debugC(DEBUG_INSANE, "Rebel2: Linked ID=%d to Parent=%d (Slot 0) - child ENABLED (parent dead)", childId, parentId);
+				debugC(DEBUG_INSANE, "Linked ID=%d to Parent=%d (Slot 0) - child ENABLED (parent dead)", childId, parentId);
 			}
 		} else {
-			debugC(DEBUG_INSANE, "Rebel2: Skipping link with invalid IDs childId=%d parentId=%d", childId, parentId);
+			debugC(DEBUG_INSANE, "Skipping link with invalid IDs childId=%d parentId=%d", childId, parentId);
 		}
 		return;
 	} else if (par3 == 1) { // Probabilistic / counter cases: par3 == 1
@@ -385,13 +385,13 @@ void InsaneRebel2::iactRebel2Opcode2(Common::SeekableReadStream &b, int16 par2,
 			if (value == 100) {
 				// par4==100: Force enable the target (original: FUN_00423a00)
 				clearBit(targetId);
-				debugC(DEBUG_INSANE, "Rebel2 Opcode2 (H%d): Force ENABLE target=%d (par4=100)", _rebelHandler, targetId);
+				debugC(DEBUG_INSANE, "Opcode2 (H%d): Force ENABLE target=%d (par4=100)", _rebelHandler, targetId);
 			} else {
 				// Check wave state: if enemy type has been killed, disable target
 				int bitMask = 1 << (value & 0x1f);
 				if ((_rebelWaveState & bitMask) != 0) {
 					setBit(targetId);
-					debugC(DEBUG_INSANE, "Rebel2 Opcode2 (H%d): Disable target=%d (type %d killed, wave=0x%x)", _rebelHandler, targetId, value, _rebelWaveState);
+					debugC(DEBUG_INSANE, "Opcode2 (H%d): Disable target=%d (type %d killed, wave=0x%x)", _rebelHandler, targetId, value, _rebelWaveState);
 				}
 			}
 			return;
@@ -400,19 +400,19 @@ void InsaneRebel2::iactRebel2Opcode2(Common::SeekableReadStream &b, int16 par2,
 		if (value > 1 && value < 10) { // 1 < value < 10: random disable
 			if (_vm->_rnd.getRandomNumber(value) == 0) {
 				setBit(targetId);
-				debugC(DEBUG_INSANE, "Rebel2 IACT Opcode2: Random DISABLE target=%d (value=%d)", targetId, value);
+				debugC(DEBUG_INSANE, "IACT Opcode2: Random DISABLE target=%d (value=%d)", targetId, value);
 			}
 		} else if (value > 10 && value < 20) { // 10 < value < 20: enable/disable with special value==11 = force enable
 			if (value == 11) {
 				clearBit(targetId);
-				debugC(DEBUG_INSANE, "Rebel2 IACT Opcode2: FORCE ENABLE target=%d (value=11)", targetId);
+				debugC(DEBUG_INSANE, "IACT Opcode2: FORCE ENABLE target=%d (value=11)", targetId);
 			} else {
 				if (_vm->_rnd.getRandomNumber(value - 10) == 0) {
 					clearBit(targetId);
-					debugC(DEBUG_INSANE, "Rebel2 IACT Opcode2: Random ENABLE target=%d (value=%d)", targetId, value);
+					debugC(DEBUG_INSANE, "IACT Opcode2: Random ENABLE target=%d (value=%d)", targetId, value);
 				} else {
 					setBit(targetId);
-					debugC(DEBUG_INSANE, "Rebel2 IACT Opcode2: Random DISABLE target=%d (value=%d)", targetId, value);
+					debugC(DEBUG_INSANE, "IACT Opcode2: Random DISABLE target=%d (value=%d)", targetId, value);
 				}
 			}
 		} else if (value > 99 && value < 110) { // 99 < value < 110: increment value counter if target active
@@ -421,7 +421,7 @@ void InsaneRebel2::iactRebel2Opcode2(Common::SeekableReadStream &b, int16 par2,
 				if (idx >= 0 && idx < 10) {
 					_rebelValueCounters[idx]++;
 					_rebelLastCounter = _rebelValueCounters[idx];
-					debugC(DEBUG_INSANE, "Rebel2 IACT Opcode2: Increment VAL counter[%d] -> %d (target=%d)", value, _rebelValueCounters[idx], targetId);
+					debugC(DEBUG_INSANE, "IACT Opcode2: Increment VAL counter[%d] -> %d (target=%d)", value, _rebelValueCounters[idx], targetId);
 				}
 			}
 
@@ -431,14 +431,14 @@ void InsaneRebel2::iactRebel2Opcode2(Common::SeekableReadStream &b, int16 par2,
 					if (!isBitSet(targetId)) {
 						_rebelMaskCounters[slot]++;
 						_rebelLastCounter = _rebelMaskCounters[slot];
-						debugC(DEBUG_INSANE, "Rebel2 IACT Opcode2: Increment MASK counter[%d] -> %d (target=%d)", slot, _rebelMaskCounters[slot], targetId);
+						debugC(DEBUG_INSANE, "IACT Opcode2: Increment MASK counter[%d] -> %d (target=%d)", slot, _rebelMaskCounters[slot], targetId);
 					}
 				}
 			}
 		}
 
 		// Unknown sub-type: log and return
-		debugC(DEBUG_INSANE, "Rebel2 IACT Opcode2: Unhandled par3=%d par4=%d", par3, par4);
+		debugC(DEBUG_INSANE, "IACT Opcode2: Unhandled par3=%d par4=%d", par3, par4);
 	}
 }
 
@@ -477,35 +477,35 @@ void InsaneRebel2::iactRebel2Opcode3(Common::SeekableReadStream &b, int16 par2,
 
 		if (par3 == 5) {
 			// Probabilistic damage with cover check (lines 81-92)
-			debugC(DEBUG_INSANE, "Rebel2 Opcode3: H25 par3=5 srcId=%d isBitSet=%d damageLevel=%d",
+			debugC(DEBUG_INSANE, "Opcode3: H25 par3=5 srcId=%d isBitSet=%d damageLevel=%d",
 				srcIdBody1, isBitSet(srcIdBody1), _rebelDamageLevel);
 
 			if (_rebelDamageLevel < 2 && !isBitSet(srcIdBody1)) {
 				LevelDifficultyParams params = getDifficultyParams();
 				int probability = (params.shotAccuracy >= 0) ? params.shotAccuracy : 0;
 				int roll = _vm->_rnd.getRandomNumber(99);
-				debugC(DEBUG_INSANE, "Rebel2 Opcode3: probability=%d roll=%d (need roll < prob)", probability, roll);
+				debugC(DEBUG_INSANE, "Opcode3: probability=%d roll=%d (need roll < prob)", probability, roll);
 
 				if (roll < probability) {
 					int damageAmount = (params.shotDamage >= 0) ? params.shotDamage : 0;
 					if (applyPlayerDamage(damageAmount)) {
-						debugC(DEBUG_INSANE, "Rebel2: H25 PROBABILISTIC damage from %d. Damage=%d total=%d",
+						debugC(DEBUG_INSANE, "H25 PROBABILISTIC damage from %d. Damage=%d total=%d",
 							srcIdBody1, damageAmount, _playerDamage);
 					}
 					if (!_noDamage)
 						initDamageFlash();
 				}
 			} else {
-				debugC(DEBUG_INSANE, "Rebel2 Opcode3: H25 par3=5 BLOCKED (damageLevel=%d isBitSet=%d)",
+				debugC(DEBUG_INSANE, "Opcode3: H25 par3=5 BLOCKED (damageLevel=%d isBitSet=%d)",
 					_rebelDamageLevel, isBitSet(srcIdBody1));
 			}
 		} else if (par3 == 1 && !isBitSet(srcIdBody0) && par4 != 4) {
 			// Hit counter only ??? NO damage (lines 94-98)
 			_rebelHitCounter++;
-			debugC(DEBUG_INSANE, "Rebel2: H25 hit counter++ -> %d (par3=1 par4=%d, no damage)",
+			debugC(DEBUG_INSANE, "H25 hit counter++ -> %d (par3=1 par4=%d, no damage)",
 				_rebelHitCounter, par4);
 		} else {
-			debugC(DEBUG_INSANE, "Rebel2 Opcode3: H25 par3=%d par4=%d (no action)", par3, par4);
+			debugC(DEBUG_INSANE, "Opcode3: H25 par3=%d par4=%d (no action)", par3, par4);
 		}
 
 		// Direct damage: par4==100, separate from par3 branches (lines 99-111)
@@ -513,7 +513,7 @@ void InsaneRebel2::iactRebel2Opcode3(Common::SeekableReadStream &b, int16 par2,
 			LevelDifficultyParams dparams = getDifficultyParams();
 			int directHitDamage = (dparams.missDamage >= 0) ? dparams.missDamage : 0;
 			if (applyPlayerDamage(directHitDamage)) {
-				debugC(DEBUG_INSANE, "Rebel2: H25 DIRECT HIT par4=100 damage=%d total=%d",
+				debugC(DEBUG_INSANE, "H25 DIRECT HIT par4=100 damage=%d total=%d",
 					directHitDamage, _playerDamage);
 			}
 			if (!_noDamage)
@@ -523,12 +523,12 @@ void InsaneRebel2::iactRebel2Opcode3(Common::SeekableReadStream &b, int16 par2,
 		// Non-Handler-25 direct hit path ??? FUN_4092D9 lines 209-227
 		int16 srcId = b.readSint16LE(); // body[0] (offset +8): source enemy ID
 
-		debugC(DEBUG_INSANE, "Rebel2 Opcode3: par3=%d par4=%d srcId=%d isBitSet=%d",
+		debugC(DEBUG_INSANE, "Opcode3: par3=%d par4=%d srcId=%d isBitSet=%d",
 			par3, par4, srcId, isBitSet(srcId));
 
 		if (!isBitSet(srcId)) {
 			_rebelHitCounter++;
-			debugC(DEBUG_INSANE, "Rebel2: Incremented hit counter -> %d", _rebelHitCounter);
+			debugC(DEBUG_INSANE, "Incremented hit counter -> %d", _rebelHitCounter);
 
 			LevelDifficultyParams dparams = getDifficultyParams();
 			int directHitDamage = (dparams.missDamage >= 0) ? dparams.missDamage : 0;
@@ -546,7 +546,7 @@ void InsaneRebel2::iactRebel2Opcode3(Common::SeekableReadStream &b, int16 par2,
 
 				if (shouldDamage) {
 					if (applyPlayerDamage(directHitDamage)) {
-						debugC(DEBUG_INSANE, "Rebel2: DIRECT HIT damage from enemy %d. par3=%d par4=%d damage=%d total=%d",
+						debugC(DEBUG_INSANE, "DIRECT HIT damage from enemy %d. par3=%d par4=%d damage=%d total=%d",
 							srcId, par3, par4, directHitDamage, _playerDamage);
 					}
 					if (!_noDamage)
@@ -559,19 +559,19 @@ void InsaneRebel2::iactRebel2Opcode3(Common::SeekableReadStream &b, int16 par2,
 		b.skip(2); // Skip body[0]
 		int16 srcId = b.readSint16LE(); // body[1] (offset +10)
 
-		debugC(DEBUG_INSANE, "Rebel2 Opcode3: par3=5 srcId=%d isBitSet=%d", srcId, isBitSet(srcId));
+		debugC(DEBUG_INSANE, "Opcode3: par3=5 srcId=%d isBitSet=%d", srcId, isBitSet(srcId));
 
 		if (!isBitSet(srcId)) {
 			LevelDifficultyParams params = getDifficultyParams();
 			int probability = (params.shotAccuracy >= 0) ? params.shotAccuracy : 0;
 
 			int roll = _vm->_rnd.getRandomNumber(99);
-			debugC(DEBUG_INSANE, "Rebel2 Opcode3: probability=%d roll=%d (need roll < prob)", probability, roll);
+			debugC(DEBUG_INSANE, "Opcode3: probability=%d roll=%d (need roll < prob)", probability, roll);
 
 			if (roll < probability) {
 				int damageAmount = (params.shotDamage >= 0) ? params.shotDamage : 0;
 				if (applyPlayerDamage(damageAmount)) {
-					debugC(DEBUG_INSANE, "Rebel2: PROBABILISTIC damage from enemy %d. Damage=%d total=%d",
+					debugC(DEBUG_INSANE, "PROBABILISTIC damage from enemy %d. Damage=%d total=%d",
 						srcId, damageAmount, _playerDamage);
 				}
 				if (!_noDamage) {
@@ -584,7 +584,7 @@ void InsaneRebel2::iactRebel2Opcode3(Common::SeekableReadStream &b, int16 par2,
 			}
 		}
 	} else {
-		debugC(DEBUG_INSANE, "Rebel2 Opcode3: UNHANDLED par3=%d par4=%d", par3, par4);
+		debugC(DEBUG_INSANE, "Opcode3: UNHANDLED par3=%d par4=%d", par3, par4);
 	}
 }
 
@@ -597,7 +597,7 @@ void InsaneRebel2::updateOpcode6Handler(int16 par2) {
 			_level2BackgroundLoaded = false;
 		}
 		_rebelHandler = par2;
-		debugC(DEBUG_INSANE, "Rebel2 Opcode 6: Setting handler=%d", par2);
+		debugC(DEBUG_INSANE, "Opcode 6: Setting handler=%d", par2);
 	}
 }
 
@@ -634,7 +634,7 @@ void InsaneRebel2::handleOpcode6Handler8(Common::SeekableReadStream &b, int16 pa
 		}
 		_rebelWaveState = _rebelPhaseState;
 		_rebelOp6Initialized = true;
-		debugC(DEBUG_INSANE, "Rebel2 Opcode 6 (Handler 8): Wave init, wave=0x%x", _rebelWaveState);
+		debugC(DEBUG_INSANE, "Opcode 6 (Handler 8): Wave init, wave=0x%x", _rebelWaveState);
 	}
 
 	// Skip position calculation for special modes 4 and 5
@@ -775,7 +775,7 @@ void InsaneRebel2::handleOpcode6Handler8(Common::SeekableReadStream &b, int16 pa
 			_vm->getActionState(kScummActionInsaneAttack);
 	}
 
-	debugC(DEBUG_INSANE, "Rebel2 Opcode 6 (Handler 8): mode=%d bodyFlag=%d range=%d shipPos=(%d,%d) target=(%d,%d) firing=%d dir=(%d,%d,%d)",
+	debugC(DEBUG_INSANE, "Opcode 6 (Handler 8): mode=%d bodyFlag=%d range=%d shipPos=(%d,%d) target=(%d,%d) firing=%d dir=(%d,%d,%d)",
 		_shipLevelMode, bodyStatusFlag, _movementRangeLimit, _shipPosX, _shipPosY, _shipTargetX, _shipTargetY, _shipFiring,
 		_shipDirectionH, _shipDirectionV, _shipDirectionIndex);
 }
@@ -792,7 +792,7 @@ void InsaneRebel2::handleOpcode6Handler7(Common::SeekableReadStream &b, int16 pa
 	//   Mode 2: Combat mode - shooting ENABLED, SECONDARY zones
 	//   Mode 3: Tunnel flight - PRIMARY zones, per-edge push-back (hMargin=0x0f)
 	_flyControlMode = par4;
-	debugC(DEBUG_INSANE, "Rebel2 Opcode 6 (Handler 7): Control mode set to %d (shooting %s)",
+	debugC(DEBUG_INSANE, "Opcode 6 (Handler 7): Control mode set to %d (shooting %s)",
 		par4, (par4 == 2) ? "ENABLED" : "DISABLED");
 
 	// Status bar: param_5[4] == 1 in original (first body word, 5th IACT word)
@@ -809,7 +809,7 @@ void InsaneRebel2::handleOpcode6Handler7(Common::SeekableReadStream &b, int16 pa
 		if (_smush_iconsNut && _smush_iconsNut->getNumChars() > 5) {
 			initLaserTexture(_smush_iconsNut, 5);
 		}
-		debugC(DEBUG_INSANE, "Rebel2 Opcode 6 (Handler 7): Status bar enabled (body flag=%d)", bodyStatusFlag);
+		debugC(DEBUG_INSANE, "Opcode 6 (Handler 7): Status bar enabled (body flag=%d)", bodyStatusFlag);
 	}
 
 	// Ship position update - FUN_40C3CC case 4, lines 49-327.
@@ -1084,7 +1084,7 @@ void InsaneRebel2::handleOpcode6Handler7(Common::SeekableReadStream &b, int16 pa
 		((_vm->VAR(_vm->VAR_LEFTBTN_HOLD) != 0) ||
 		 _vm->getActionState(kScummActionInsaneAttack));
 
-	debugC(DEBUG_INSANE, "Rebel2 H7: mouse=(%d,%d) raw=(%d,%d) scaled=(%d,%d) targetX=%d pos=(%d,%d) "
+	debugC(DEBUG_INSANE, "H7: mouse=(%d,%d) raw=(%d,%d) scaled=(%d,%d) targetX=%d pos=(%d,%d) "
 		"vel=%d vIn=%d dx=%d dir=%d mode=%d mouseTarget=%d",
 		mouseX, mouseY, inputX, inputY, scaledInputX, scaledInputY,
 		mouseFlightTargetX, _flyShipScreenX, _flyShipScreenY, _smoothedVelocity,
@@ -1125,7 +1125,7 @@ void InsaneRebel2::handleOpcode6Handler25(byte *renderBitmap, Common::SeekableRe
 			}
 			_rebelWaveState = _rebelPhaseState;
 			_rebelOp6Initialized = true;
-			debugC(DEBUG_INSANE, "Rebel2 Opcode 6 (Handler 25): Wave init, wave=0x%x autopilot=%d damageLevel=%d",
+			debugC(DEBUG_INSANE, "Opcode 6 (Handler 25): Wave init, wave=0x%x autopilot=%d damageLevel=%d",
 				_rebelWaveState, _rebelAutopilot, _rebelDamageLevel);
 		}
 	}
@@ -1139,7 +1139,7 @@ void InsaneRebel2::handleOpcode6Handler25(byte *renderBitmap, Common::SeekableRe
 	// Mode 4: Alternative uncovered position - sprite on right
 	_grdSpriteMode = par4;  // local_14[3] maps to par4 (offset 6-7)
 
-	debugC(DEBUG_INSANE, "Rebel2 Handler25 Opcode6: par2=%d par3=%d par4=%d(mode) par5=%d(reset) autopilot=%d damageLevel=%d controlMode=%d",
+	debugC(DEBUG_INSANE, "Handler25 Opcode6: par2=%d par3=%d par4=%d(mode) par5=%d(reset) autopilot=%d damageLevel=%d controlMode=%d",
 		par2, par3, par4, par5, _rebelAutopilot, _rebelDamageLevel, _rebelControlMode);
 
 	// Autopilot logic (lines 123-146).
@@ -1150,13 +1150,13 @@ void InsaneRebel2::handleOpcode6Handler25(byte *renderBitmap, Common::SeekableRe
 			// Uncovered: RIGHT button enters cover.
 			if ((_rebelControlMode & 2) != 0) {
 				_rebelAutopilot = 1;
-				debugC(DEBUG_INSANE, "Rebel2 Handler25: Entering cover (right click), controlMode=%d", _rebelControlMode);
+				debugC(DEBUG_INSANE, "Handler25: Entering cover (right click), controlMode=%d", _rebelControlMode);
 			}
 		} else {
 			// Covered: ANY button exits cover.
 			if (_rebelControlMode != 0) {
 				_rebelAutopilot = 0;
-				debugC(DEBUG_INSANE, "Rebel2 Handler25: Exiting cover (button click), controlMode=%d", _rebelControlMode);
+				debugC(DEBUG_INSANE, "Handler25: Exiting cover (button click), controlMode=%d", _rebelControlMode);
 			}
 		}
 
@@ -1191,7 +1191,7 @@ void InsaneRebel2::handleOpcode6Handler25(byte *renderBitmap, Common::SeekableRe
 		}
 	}
 	if (_rebelDamageLevel != prevDamageLevel) {
-		debugC(DEBUG_INSANE, "Rebel2 Handler25: damageLevel transition %d -> %d (autopilot=%d)",
+		debugC(DEBUG_INSANE, "Handler25: damageLevel transition %d -> %d (autopilot=%d)",
 			prevDamageLevel, _rebelDamageLevel, _rebelAutopilot);
 	}
 
@@ -1263,10 +1263,10 @@ void InsaneRebel2::handleOpcode6Handler25(byte *renderBitmap, Common::SeekableRe
 		_rebelViewOffset2X = _rebelDamageLevel * -22;
 		_rebelViewOffsetY = 0;
 		_rebelViewOffset2Y = 0;
-		debugC(DEBUG_INSANE, "Rebel2 Opcode 6 (Handler 25): Unknown mode %d, using Mode 1 fallback", _grdSpriteMode);
+		debugC(DEBUG_INSANE, "Opcode 6 (Handler 25): Unknown mode %d, using Mode 1 fallback", _grdSpriteMode);
 	}
 
-	debugC(DEBUG_INSANE, "Rebel2 Opcode 6 (Handler 25): mode=%d damage=%d dir=%d autopilot=%d viewOff=(%d,%d) spritePos=(%d,%d)",
+	debugC(DEBUG_INSANE, "Opcode 6 (Handler 25): mode=%d damage=%d dir=%d autopilot=%d viewOff=(%d,%d) spritePos=(%d,%d)",
 		_grdSpriteMode, _rebelDamageLevel, _rebelFlightDir, _rebelAutopilot,
 		_rebelViewOffsetX, _rebelViewOffsetY, _rebelViewOffset2X, _rebelViewOffset2Y);
 
@@ -1314,7 +1314,7 @@ void InsaneRebel2::handleOpcode6Turret(Common::SeekableReadStream &b, int16 par4
 			_rebelWaveState = _rebelPhaseState;
 			_rebelHitCounter = 0;
 			_rebelOp6Initialized = true;
-			debugC(DEBUG_INSANE, "Rebel2 Opcode 6 (Handler 0x26): Wave init, levelType=%d waveState=0x%x",
+			debugC(DEBUG_INSANE, "Opcode 6 (Handler 0x26): Wave init, levelType=%d waveState=0x%x",
 				_rebelLevelType, _rebelWaveState);
 		}
 	}
@@ -1339,7 +1339,7 @@ void InsaneRebel2::handleOpcode6GenericInit(int16 par4) {
 			_rebelWaveState = _rebelPhaseState;
 			_rebelHitCounter = 0;
 			_rebelOp6Initialized = true;
-			debugC(DEBUG_INSANE, "Rebel2 Opcode 6: Wave init - cleared bits/links, waveState=0x%x", _rebelWaveState);
+			debugC(DEBUG_INSANE, "Opcode 6: Wave init - cleared bits/links, waveState=0x%x", _rebelWaveState);
 		}
 	}
 }
@@ -1458,7 +1458,7 @@ void InsaneRebel2::updateOpcode6GenericFlightState() {
 		break;
 	}
 
-	debugC(DEBUG_INSANE, "Rebel2 Opcode 6: levelType=%d autopilot=%d damageLevel=%d viewOffset=(%d,%d)",
+	debugC(DEBUG_INSANE, "Opcode 6: levelType=%d autopilot=%d damageLevel=%d viewOffset=(%d,%d)",
 		_rebelLevelType, _rebelAutopilot, _rebelDamageLevel, _rebelViewOffsetX, _rebelViewOffsetY);
 }
 
@@ -1522,7 +1522,7 @@ void InsaneRebel2::iactRebel2Opcode6(byte *renderBitmap, Common::SeekableReadStr
 	// - Handler-specific status/reset word
 	// - Different view offset calculations
 
-	debugC(DEBUG_INSANE, "Rebel2 IACT Opcode 6: par2=%d par3=%d par4=%d", par2, par3, par4);
+	debugC(DEBUG_INSANE, "IACT Opcode 6: par2=%d par3=%d par4=%d", par2, par3, par4);
 
 	updateOpcode6Handler(par2);
 
@@ -1610,7 +1610,7 @@ bool InsaneRebel2::loadOpcode8EdgeTable(Common::SeekableReadStream &b, int64 sta
 			b.read(edgeData, 8 + 32896);
 			initEdgeTable(edgeData);
 			free(edgeData);
-			debugC(DEBUG_INSANE, "Rebel2 Opcode 8: Loaded per-level edge blend table (par4=1000)");
+			debugC(DEBUG_INSANE, "Opcode 8: Loaded per-level edge blend table (par4=1000)");
 		}
 		b.seek(startPos);
 		return true;
@@ -1651,11 +1651,11 @@ bool InsaneRebel2::loadOpcode8AuxSfx(Common::SeekableReadStream &b, int64 startP
 				b.read(soundData, dataSize);
 				loadAuxSfx(auxBuffer, soundData, dataSize);
 				free(soundData);
-				debugC(DEBUG_INSANE, "Rebel2 Opcode 8: Loaded %u bytes into aux sound buffer %d (par4=%d)",
+				debugC(DEBUG_INSANE, "Opcode 8: Loaded %u bytes into aux sound buffer %d (par4=%d)",
 					dataSize, auxBuffer, par4);
 			}
 		} else {
-			debugC(DEBUG_INSANE, "Rebel2 Opcode 8: Aux sound par4=%d dataSize=%u exceeds remaining=%lld",
+			debugC(DEBUG_INSANE, "Opcode 8: Aux sound par4=%d dataSize=%u exceeds remaining=%lld",
 				par4, dataSize, (long long)remaining);
 		}
 	}
@@ -1682,7 +1682,7 @@ bool InsaneRebel2::loadOpcode8ShotOriginTable(Common::SeekableReadStream &b, int
 // ScummVM refactor helper for opcode 8 embedded ANIM scanning, not a separate retail function.
 void InsaneRebel2::loadOpcode8EmbeddedAnim(byte *renderBitmap, Common::SeekableReadStream &b, int64 startPos, int64 remaining, int16 par3, int16 par4) {
 	// Remaining handlers require finding ANIM tag in the stream.
-	debugC(DEBUG_INSANE, "Rebel2 Opcode 8: Scanning for ANIM tag (startPos=%lld remaining=%lld)",
+	debugC(DEBUG_INSANE, "Opcode 8: Scanning for ANIM tag (startPos=%lld remaining=%lld)",
 		(long long)startPos, (long long)remaining);
 
 	if (remaining <= 0) {
@@ -1696,19 +1696,19 @@ void InsaneRebel2::loadOpcode8EmbeddedAnim(byte *renderBitmap, Common::SeekableR
 	}
 
 	int bytesRead = b.read(scanBuf, scanSize);
-	debugC(DEBUG_INSANE, "Rebel2 Opcode 8: Read %d bytes for ANIM scan", bytesRead);
+	debugC(DEBUG_INSANE, "Opcode 8: Read %d bytes for ANIM scan", bytesRead);
 
 	int animOffset = -1;
 	for (int i = 0; i + 8 <= bytesRead; ++i) {
 		if (READ_BE_UINT32(scanBuf + i) == MKTAG('A','N','I','M')) {
 			animOffset = i;
-			debugC(DEBUG_INSANE, "Rebel2 Opcode 8: Found ANIM at offset %d", i);
+			debugC(DEBUG_INSANE, "Opcode 8: Found ANIM at offset %d", i);
 			break;
 		}
 	}
 
 	if (animOffset < 0) {
-		debugC(DEBUG_INSANE, "Rebel2 Opcode 8: No ANIM tag found");
+		debugC(DEBUG_INSANE, "Opcode 8: No ANIM tag found");
 		free(scanBuf);
 		b.seek(startPos);
 		return;
@@ -1779,7 +1779,7 @@ bool InsaneRebel2::handleOpcode8EmbeddedAnim(byte *renderBitmap, byte *animData,
 		} else if (par4 == 5) {
 			handled = loadLevel2Background(animData, animDataSize, renderBitmap);
 		} else if (par4 == 4 || par4 == 6 || par4 == 7) {
-			debugC(DEBUG_INSANE, "Rebel2 Opcode 8: Handler 25 overlay par4=%d - drawing to screen", par4);
+			debugC(DEBUG_INSANE, "Opcode 8: Handler 25 overlay par4=%d - drawing to screen", par4);
 			loadEmbeddedSan(par4, animData, animDataSize, renderBitmap);
 			handled = true;
 		}
@@ -1793,7 +1793,7 @@ bool InsaneRebel2::handleOpcode8EmbeddedAnim(byte *renderBitmap, byte *animData,
 		const bool lowResHud = (par3 == 1 || par3 == 3);
 
 		if ((!highRes && highResHud) || (highRes && lowResHud)) {
-			debugC(DEBUG_INSANE, "Rebel2 Opcode 8: Skipping %s HUD par3=%d while running in %s mode",
+			debugC(DEBUG_INSANE, "Opcode 8: Skipping %s HUD par3=%d while running in %s mode",
 				highResHud ? "high-res" : "low-res", par3, highRes ? "high-res" : "low-res");
 			handled = true;
 		} else {
@@ -1807,7 +1807,7 @@ bool InsaneRebel2::handleOpcode8EmbeddedAnim(byte *renderBitmap, byte *animData,
 
 			// Skip audio tracks (userId >= 1000).
 			if (userId > 0 && userId < 1000) {
-				debugC(DEBUG_INSANE, "Rebel2 Opcode 8: Loading embedded SAN HUD userId=%d (handler=%d par3=%d par4=%d)",
+				debugC(DEBUG_INSANE, "Opcode 8: Loading embedded SAN HUD userId=%d (handler=%d par3=%d par4=%d)",
 					userId, _rebelHandler, par3, par4);
 				loadEmbeddedSan(userId, animData, animDataSize, renderBitmap);
 				handled = true;
@@ -1816,14 +1816,14 @@ bool InsaneRebel2::handleOpcode8EmbeddedAnim(byte *renderBitmap, byte *animData,
 	}
 
 	if (!handled) {
-		debugC(DEBUG_INSANE, "Rebel2 Opcode 8: Unhandled case - handler=%d par3=%d par4=%d", _rebelHandler, par3, par4);
+		debugC(DEBUG_INSANE, "Opcode 8: Unhandled case - handler=%d par3=%d par4=%d", _rebelHandler, par3, par4);
 	}
 
 	return handled;
 }
 
 void InsaneRebel2::iactRebel2Opcode8(byte *renderBitmap, Common::SeekableReadStream &b, int32 chunkSize, int16 par2, int16 par3, int16 par4) {
-	debugC(DEBUG_INSANE, "Rebel2 IACT Opcode 8: handler=%d par2=%d par3=%d par4=%d (gameState=%d)",
+	debugC(DEBUG_INSANE, "IACT Opcode 8: handler=%d par2=%d par3=%d par4=%d (gameState=%d)",
 		_rebelHandler, par2, par3, par4, _gameState);
 
 	int64 startPos = b.pos();
@@ -1914,7 +1914,7 @@ bool InsaneRebel2::loadHandler25ShotOriginTable(Common::SeekableReadStream &b, i
 	b.seek(savedPos);
 
 	if (count < 20) {
-		debugC(DEBUG_INSANE, "Rebel2 Opcode 8: Handler25 par4=8 parse failed (count=%d, expected up to 30)", count);
+		debugC(DEBUG_INSANE, "Opcode 8: Handler25 par4=8 parse failed (count=%d, expected up to 30)", count);
 		return false;
 	}
 
@@ -1930,7 +1930,7 @@ bool InsaneRebel2::loadHandler25ShotOriginTable(Common::SeekableReadStream &b, i
 	}
 	_grdShotOriginTableLoaded = true;
 
-	debugC(DEBUG_INSANE, "Rebel2 Opcode 8: Loaded Handler25 shot-origin table (pairs=%d) idx5=(%d,%d) idx14=(%d,%d)",
+	debugC(DEBUG_INSANE, "Opcode 8: Loaded Handler25 shot-origin table (pairs=%d) idx5=(%d,%d) idx14=(%d,%d)",
 		count / 2, _grdShotOriginX[5], _grdShotOriginY[5], _grdShotOriginX[14], _grdShotOriginY[14]);
 	return true;
 }
@@ -1998,7 +1998,7 @@ bool InsaneRebel2::loadHandler7ShotTable(Common::SeekableReadStream &b, int64 st
 	b.seek(savedPos);
 
 	if (count < 70) {
-		debugC(DEBUG_INSANE, "Rebel2 Opcode 8: Handler7 par4=%d shot table parse failed (count=%d)", par4, count);
+		debugC(DEBUG_INSANE, "Opcode 8: Handler7 par4=%d shot table parse failed (count=%d)", par4, count);
 		return false;
 	}
 
@@ -2014,7 +2014,7 @@ bool InsaneRebel2::loadHandler7ShotTable(Common::SeekableReadStream &b, int64 st
 	else
 		_flyRightGunTableLoaded = true;
 
-	debugC(DEBUG_INSANE, "Rebel2 Opcode 8: Loaded Handler7 %s gun table idx0=(%d,%d) idx17=(%d,%d) idx34=(%d,%d)",
+	debugC(DEBUG_INSANE, "Opcode 8: Loaded Handler7 %s gun table idx0=(%d,%d) idx17=(%d,%d) idx34=(%d,%d)",
 		(par4 == 12) ? "left" : "right",
 		tableX[0], tableY[0], tableX[17], tableY[17], tableX[34], tableY[34]);
 	return true;
@@ -2050,13 +2050,13 @@ bool InsaneRebel2::loadHandler7FlySprites(Common::SeekableReadStream &b, int64 r
 		return false;
 	}
 
-	debugC(DEBUG_INSANE, "Rebel2 loadHandler7FlySprites: header bytes: %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X",
+	debugC(DEBUG_INSANE, "loadHandler7FlySprites: header bytes: %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X",
 		header[0], header[1], header[2], header[3], header[4],
 		header[5], header[6], header[7], header[8], header[9]);
 
 	// Size is at offset 14 from IACT start = bytes 6-9 of our header buffer
 	uint32 nutSize = READ_LE_UINT32(header + 6);
-	debugC(DEBUG_INSANE, "Rebel2 loadHandler7FlySprites: par4=%d nutSize=%u remaining=%lld",
+	debugC(DEBUG_INSANE, "loadHandler7FlySprites: par4=%d nutSize=%u remaining=%lld",
 		par4, nutSize, (long long)remaining);
 
 	if (nutSize == 0 || nutSize > (uint32)(remaining - 10)) {
@@ -2069,7 +2069,7 @@ bool InsaneRebel2::loadHandler7FlySprites(Common::SeekableReadStream &b, int64 r
 	}
 
 	int bytesRead = b.read(nutData, nutSize);
-	debugC(DEBUG_INSANE, "Rebel2 loadHandler7FlySprites: Read %d/%u bytes, first 16: %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X",
+	debugC(DEBUG_INSANE, "loadHandler7FlySprites: Read %d/%u bytes, first 16: %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X",
 		bytesRead, nutSize,
 		bytesRead > 0 ? nutData[0] : 0, bytesRead > 1 ? nutData[1] : 0,
 		bytesRead > 2 ? nutData[2] : 0, bytesRead > 3 ? nutData[3] : 0,
@@ -2099,13 +2099,13 @@ bool InsaneRebel2::loadHandler7FlySprites(Common::SeekableReadStream &b, int64 r
 	// Load as a Rebel2 embedded sprite ANIM.
 	NutRenderer *newNut = makeRebel2SpriteFromData(_vm, nutData, bytesRead);
 	if (!newNut || newNut->getNumChars() <= 0) {
-		debugC(DEBUG_INSANE, "Rebel2 loadHandler7FlySprites: NUT load failed for par4=%d", par4);
+		debugC(DEBUG_INSANE, "loadHandler7FlySprites: NUT load failed for par4=%d", par4);
 		delete newNut;
 		free(nutData);
 		return false;
 	}
 
-	debugC(DEBUG_INSANE, "Rebel2 loadHandler7FlySprites: Loaded FLY NUT par4=%d with %d sprites",
+	debugC(DEBUG_INSANE, "loadHandler7FlySprites: Loaded FLY NUT par4=%d with %d sprites",
 		par4, newNut->getNumChars());
 
 	// Assign to appropriate slot based on par4 (matches FUN_0040c3cc case 6 switch)
@@ -2114,7 +2114,7 @@ bool InsaneRebel2::loadHandler7FlySprites(Common::SeekableReadStream &b, int64 r
 	case 1:  // FLY001 - Ship direction sprites (35 frames)
 		delete _flyShipSprite;
 		_flyShipSprite = newNut;
-		debugC(DEBUG_INSANE, "Rebel2: _flyShipSprite set with %d sprites", newNut->getNumChars());
+		debugC(DEBUG_INSANE, "_flyShipSprite set with %d sprites", newNut->getNumChars());
 		break;
 	case 2:  // FLY003 - Targeting overlay
 		delete _flyTargetSprite;
@@ -2156,7 +2156,7 @@ bool InsaneRebel2::loadTurretHudOverlay(byte *animData, int32 size, int16 select
 	const int secondarySlot = highRes ? 4 : 3;
 
 	if (selector >= 1 && selector <= 4 && selector != primarySlot && selector != secondarySlot) {
-		debugC(DEBUG_INSANE, "Rebel2 loadTurretHudOverlay: Skipping %s HUD selector=%d (running in %s mode)",
+		debugC(DEBUG_INSANE, "loadTurretHudOverlay: Skipping %s HUD selector=%d (running in %s mode)",
 			(selector == 2 || selector == 4) ? "high-res" : "low-res", selector,
 			highRes ? "high-res" : "low-res");
 		return true;
@@ -2168,12 +2168,12 @@ bool InsaneRebel2::loadTurretHudOverlay(byte *animData, int32 size, int16 select
 
 	NutRenderer *newNut = makeRebel2SpriteFromData(_vm, animData, size);
 	if (!newNut || newNut->getNumChars() <= 0) {
-		debugC(DEBUG_INSANE, "Rebel2 loadTurretHudOverlay: NUT load failed for selector=%d", selector);
+		debugC(DEBUG_INSANE, "loadTurretHudOverlay: NUT load failed for selector=%d", selector);
 		delete newNut;
 		return false;
 	}
 
-	debugC(DEBUG_INSANE, "Rebel2 loadTurretHudOverlay: Loaded turret HUD NUT selector=%d with %d sprites",
+	debugC(DEBUG_INSANE, "loadTurretHudOverlay: Loaded turret HUD NUT selector=%d with %d sprites",
 		selector, newNut->getNumChars());
 
 	if (selector == primarySlot) {
@@ -2207,12 +2207,12 @@ bool InsaneRebel2::loadHandler8ShipSprites(byte *animData, int32 size, int16 par
 
 	NutRenderer *newNut = makeRebel2SpriteFromData(_vm, animData, size);
 	if (!newNut || newNut->getNumChars() <= 0) {
-		debugC(DEBUG_INSANE, "Rebel2 loadHandler8ShipSprites: NUT load failed for par4=%d", par4);
+		debugC(DEBUG_INSANE, "loadHandler8ShipSprites: NUT load failed for par4=%d", par4);
 		delete newNut;
 		return false;
 	}
 
-	debugC(DEBUG_INSANE, "Rebel2 loadHandler8ShipSprites: Loaded ship NUT par4=%d with %d sprites",
+	debugC(DEBUG_INSANE, "loadHandler8ShipSprites: Loaded ship NUT par4=%d with %d sprites",
 		par4, newNut->getNumChars());
 
 	switch (par4) {
@@ -2259,29 +2259,29 @@ bool InsaneRebel2::loadHandler25GrdSprites(byte *animData, int32 size, int16 par
 
 	NutRenderer *newNut = makeRebel2SpriteFromData(_vm, animData, size);
 	if (!newNut || newNut->getNumChars() <= 0) {
-		debugC(DEBUG_INSANE, "Rebel2 loadHandler25GrdSprites: NUT load failed for par4=%d", par4);
+		debugC(DEBUG_INSANE, "loadHandler25GrdSprites: NUT load failed for par4=%d", par4);
 		delete newNut;
 		return false;
 	}
 
-	debugC(DEBUG_INSANE, "Rebel2 loadHandler25GrdSprites: Loaded GRD NUT par4=%d with %d sprites",
+	debugC(DEBUG_INSANE, "loadHandler25GrdSprites: Loaded GRD NUT par4=%d with %d sprites",
 		par4, newNut->getNumChars());
 
 	switch (par4) {
 	case 1:  // GRD001 - Primary ship sprite
 		delete _grd001Sprite;
 		_grd001Sprite = newNut;
-		debugC(DEBUG_INSANE, "Rebel2: _grd001Sprite set with %d sprites", newNut->getNumChars());
+		debugC(DEBUG_INSANE, "_grd001Sprite set with %d sprites", newNut->getNumChars());
 		break;
 	case 2:  // GRD002 - Secondary ship sprite
 		delete _grd002Sprite;
 		_grd002Sprite = newNut;
-		debugC(DEBUG_INSANE, "Rebel2: _grd002Sprite set with %d sprites", newNut->getNumChars());
+		debugC(DEBUG_INSANE, "_grd002Sprite set with %d sprites", newNut->getNumChars());
 		break;
 	case 10: // GRD005 - Mode 3 overlay sprite
 		delete _grd005Sprite;
 		_grd005Sprite = newNut;
-		debugC(DEBUG_INSANE, "Rebel2: _grd005Sprite set with %d sprites", newNut->getNumChars());
+		debugC(DEBUG_INSANE, "_grd005Sprite set with %d sprites", newNut->getNumChars());
 		break;
 	default:
 		delete newNut;
@@ -2301,7 +2301,7 @@ bool InsaneRebel2::loadLevel2Background(byte *animData, int32 size, byte *render
 		return false;
 	}
 
-	debugC(DEBUG_INSANE, "Rebel2 loadLevel2Background: Loading Level 2 background (animSize=%d)", size);
+	debugC(DEBUG_INSANE, "loadLevel2Background: Loading Level 2 background (animSize=%d)", size);
 
 	// Allocate background buffer if needed (320x200 = 64000 bytes)
 	if (_level2Background == nullptr) {
@@ -2317,7 +2317,7 @@ bool InsaneRebel2::loadLevel2Background(byte *animData, int32 size, byte *render
 
 	uint32 animTag = stream.readUint32BE();
 	if (animTag != MKTAG('A','N','I','M')) {
-		debugC(DEBUG_INSANE, "Rebel2 loadLevel2Background: Missing ANIM tag, got 0x%08X", animTag);
+		debugC(DEBUG_INSANE, "loadLevel2Background: Missing ANIM tag, got 0x%08X", animTag);
 		return false;
 	}
 
@@ -2326,10 +2326,10 @@ bool InsaneRebel2::loadLevel2Background(byte *animData, int32 size, byte *render
 	if ((int64)animSize <= streamEnd - 8) {
 		animEnd = 8 + (int64)animSize;
 	} else {
-		debugC(DEBUG_INSANE, "Rebel2 loadLevel2Background: ANIM truncated: reported size=%u, actual=%lld",
+		debugC(DEBUG_INSANE, "loadLevel2Background: ANIM truncated: reported size=%u, actual=%lld",
 			animSize, streamEnd - 8);
 	}
-	debugC(DEBUG_INSANE, "Rebel2 loadLevel2Background: Found ANIM tag, size=%u", animSize);
+	debugC(DEBUG_INSANE, "loadLevel2Background: Found ANIM tag, size=%u", animSize);
 
 	bool foundBackground = false;
 	while (!stream.eos() && stream.pos() + 8 <= animEnd && !foundBackground) {
@@ -2345,7 +2345,7 @@ bool InsaneRebel2::loadLevel2Background(byte *animData, int32 size, byte *render
 			continue;
 		}
 
-		debugC(DEBUG_INSANE, "Rebel2 loadLevel2Background: Found FRME at %lld, size=%u", stream.pos() - 8, chunkSize);
+		debugC(DEBUG_INSANE, "loadLevel2Background: Found FRME at %lld, size=%u", stream.pos() - 8, chunkSize);
 
 		while (stream.pos() + 8 <= chunkDataEnd && !stream.eos() && !foundBackground) {
 			uint32 subTag;
@@ -2361,7 +2361,7 @@ bool InsaneRebel2::loadLevel2Background(byte *animData, int32 size, byte *render
 			}
 
 			if (subSize < 14) {
-				debugC(DEBUG_INSANE, "Rebel2 loadLevel2Background: FOBJ too small: size=%u", subSize);
+				debugC(DEBUG_INSANE, "loadLevel2Background: FOBJ too small: size=%u", subSize);
 				stream.seek(nextSubPos);
 				continue;
 			}
@@ -2375,7 +2375,7 @@ bool InsaneRebel2::loadLevel2Background(byte *animData, int32 size, byte *render
 			stream.readUint16LE();  // unknown
 			stream.readUint16LE();  // unknown
 
-			debugC(DEBUG_INSANE, "Rebel2 loadLevel2Background: Found FOBJ: codec=%d pos=(%d,%d) size=%dx%d",
+			debugC(DEBUG_INSANE, "loadLevel2Background: Found FOBJ: codec=%d pos=(%d,%d) size=%dx%d",
 				codec, fobjX, fobjY, fobjW, fobjH);
 
 			// Decode codec 3 (RLE) into the original 320x200 background buffer.
@@ -2389,7 +2389,7 @@ bool InsaneRebel2::loadLevel2Background(byte *animData, int32 size, byte *render
 				smushDecodeRLEOpaque(_level2Background, rleData, fobjX, fobjY, drawW, drawH, 320,
 					(int)(subDataEnd - stream.pos()));
 
-				debugC(DEBUG_INSANE, "Rebel2 loadLevel2Background: Decoded Level 2 background (%dx%d at %d,%d, clipped to %dx%d)",
+				debugC(DEBUG_INSANE, "loadLevel2Background: Decoded Level 2 background (%dx%d at %d,%d, clipped to %dx%d)",
 					fobjW, fobjH, fobjX, fobjY, drawW, drawH);
 				_level2BackgroundLoaded = true;
 				foundBackground = true;
@@ -2402,9 +2402,9 @@ bool InsaneRebel2::loadLevel2Background(byte *animData, int32 size, byte *render
 						for (int by = 0; by < 200; by++) {
 							memcpy(renderBitmap + by * 320, _level2Background + by * 320, 320);
 						}
-						debugC(DEBUG_INSANE, "Rebel2 loadLevel2Background: Copied to renderBitmap (pitch=%d)", bufferPitch);
+						debugC(DEBUG_INSANE, "loadLevel2Background: Copied to renderBitmap (pitch=%d)", bufferPitch);
 					} else {
-						debugC(DEBUG_INSANE, "Rebel2 loadLevel2Background: Skipping renderBitmap copy (pitch=%d != 320)", bufferPitch);
+						debugC(DEBUG_INSANE, "loadLevel2Background: Skipping renderBitmap copy (pitch=%d != 320)", bufferPitch);
 					}
 				}
 			}
@@ -2416,7 +2416,7 @@ bool InsaneRebel2::loadLevel2Background(byte *animData, int32 size, byte *render
 	}
 
 	if (!foundBackground) {
-		debugC(DEBUG_INSANE, "Rebel2 loadLevel2Background: Failed to find/decode background FOBJ");
+		debugC(DEBUG_INSANE, "loadLevel2Background: Failed to find/decode background FOBJ");
 	}
 
 	return foundBackground;
@@ -2471,9 +2471,9 @@ void InsaneRebel2::iactRebel2Opcode9(byte *renderBitmap, Common::SeekableReadStr
 		// Try to get string from SMUSH player's string resource
 		if (_player && _player->getString(stringIndex)) {
 			textStr = _player->getString(stringIndex);
-			debugC(DEBUG_INSANE, "Rebel2 Opcode 9: TRES string index=%d -> \"%s\"", stringIndex, textStr);
+			debugC(DEBUG_INSANE, "Opcode 9: TRES string index=%d -> \"%s\"", stringIndex, textStr);
 		} else {
-			debugC(DEBUG_INSANE, "Rebel2 Opcode 9: TRES string index=%d not found", stringIndex);
+			debugC(DEBUG_INSANE, "Opcode 9: TRES string index=%d not found", stringIndex);
 			return;
 		}
 
@@ -2513,11 +2513,11 @@ void InsaneRebel2::iactRebel2Opcode9(byte *renderBitmap, Common::SeekableReadStr
 		textBuffer[textLen] = '\0';
 		textStr = textBuffer;
 
-		debugC(DEBUG_INSANE, "Rebel2 Opcode 9: Inline text at (%d,%d) flags=0x%x -> \"%s\"", posX, posY, textFlags, textStr);
+		debugC(DEBUG_INSANE, "Opcode 9: Inline text at (%d,%d) flags=0x%x -> \"%s\"", posX, posY, textFlags, textStr);
 	}
 
 	if (!textStr || textStr[0] == '\0') {
-		debugC(DEBUG_INSANE, "Rebel2 Opcode 9: Empty text string, skipping");
+		debugC(DEBUG_INSANE, "Opcode 9: Empty text string, skipping");
 		return;
 	}
 
@@ -2542,7 +2542,7 @@ void InsaneRebel2::iactRebel2Opcode9(byte *renderBitmap, Common::SeekableReadStr
 
 	// Use the message font loaded during initialization (DIHIFONT.NUT)
 	if (!_rebelMsgFont) {
-		debugC(DEBUG_INSANE, "Rebel2 Opcode 9: No message font loaded (_rebelMsgFont is null)");
+		debugC(DEBUG_INSANE, "Opcode 9: No message font loaded (_rebelMsgFont is null)");
 		return;
 	}
 
@@ -2655,7 +2655,7 @@ void InsaneRebel2::iactRebel2Opcode9(byte *renderBitmap, Common::SeekableReadStr
 		}
 	}
 
-	debugC(DEBUG_INSANE, "Rebel2 Opcode 9: Rendered subtitle at (%d,%d) flags=0x%x clip=(%d,%d,%d,%d)",
+	debugC(DEBUG_INSANE, "Opcode 9: Rendered subtitle at (%d,%d) flags=0x%x clip=(%d,%d,%d,%d)",
 		posX, posY, textFlags, clipX, clipY, clipW, clipH);
 }
 
@@ -2675,7 +2675,7 @@ void InsaneRebel2::enemyUpdate(byte *renderBitmap, Common::SeekableReadStream &b
 
 	// If disabled, stop processing this object
 	if (disabled) {
-		// debugC(DEBUG_INSANE, "Rebel2: Skipping Opcode 4 for disabled enemy ID=%d", enemyId);
+		// debugC(DEBUG_INSANE, "Skipping Opcode 4 for disabled enemy ID=%d", enemyId);
 		return;
 	}
 
@@ -2691,7 +2691,7 @@ void InsaneRebel2::enemyUpdate(byte *renderBitmap, Common::SeekableReadStream &b
 	// In the original (FUN_004028C5/FUN_0041E7C2): sVar5/sVar2 = *(short *)(*local + 6)
 	// This maps to par4 (userId field). Used for DAT_0047ab98 wave state bitmask:
 	//   DAT_0047ab98 |= 1 << (type & 0x1f)
-	debugC(DEBUG_INSANE, "Rebel2 Opcode4: handler=%d enemyId=%d par2=%d par3=%d par4/type=%d pos=(%d,%d) size=(%d,%d)",
+	debugC(DEBUG_INSANE, "Opcode4: handler=%d enemyId=%d par2=%d par3=%d par4/type=%d pos=(%d,%d) size=(%d,%d)",
 		_rebelHandler, enemyId, par2, par3, par4, x, y, w, h);
 
 	bool found = false;
diff --git a/engines/scumm/insane/rebel2/levels.cpp b/engines/scumm/insane/rebel2/levels.cpp
index ea7e586e033..57e786378cf 100644
--- a/engines/scumm/insane/rebel2/levels.cpp
+++ b/engines/scumm/insane/rebel2/levels.cpp
@@ -154,7 +154,7 @@ void InsaneRebel2::runGame() {
 // We skip the easter eggs and play both O_OPEN_A + O_OPEN_B unconditionally.
 //
 void InsaneRebel2::playIntroSequence() {
-	debugC(DEBUG_INSANE, "Rebel2: Playing intro sequence");
+	debugC(DEBUG_INSANE, "Playing intro sequence");
 
 	_gameState = kStateIntro;
 	_menuInputActive = false;
@@ -163,7 +163,7 @@ void InsaneRebel2::playIntroSequence() {
 
 	// Play main intro (OPEN/O_OPEN_A.SAN)
 	// Original: FUN_0041f4d0("OPEN/O_OPEN_A.SAN", 0x28, 0xffff, 0xffff, 0)
-	debugC(DEBUG_INSANE, "Rebel2: Playing main intro (O_OPEN_A.SAN)");
+	debugC(DEBUG_INSANE, "Playing main intro (O_OPEN_A.SAN)");
 	splayer->setCurVideoFlags(0x28);
 	splayer->play("OPEN/O_OPEN_A.SAN", 15);
 
@@ -173,7 +173,7 @@ void InsaneRebel2::playIntroSequence() {
 	// Play additional intro (OPEN/O_OPEN_B.SAN)
 	// Original: conditional on DAT_0047ab45 || DAT_0047ab47
 	// We play unconditionally (matches "Continue Intro" menu behavior)
-	debugC(DEBUG_INSANE, "Rebel2: Playing additional intro (O_OPEN_B.SAN)");
+	debugC(DEBUG_INSANE, "Playing additional intro (O_OPEN_B.SAN)");
 	splayer->setCurVideoFlags(0x28);
 	splayer->play("OPEN/O_OPEN_B.SAN", 15);
 }
@@ -181,7 +181,7 @@ void InsaneRebel2::playIntroSequence() {
 // playMissionBriefing -- Mission briefing screen (FUN_00415CF8).
 void InsaneRebel2::playMissionBriefing() {
 
-	debugC(DEBUG_INSANE, "Rebel2: Playing mission briefing");
+	debugC(DEBUG_INSANE, "Playing mission briefing");
 
 	SmushPlayer *splayer = ((ScummEngine_v7 *)_vm)->_splayer;
 	splayer->setCurVideoFlags(0x08);  // Briefing mode flag
@@ -268,7 +268,7 @@ void InsaneRebel2::playLevelBegin(int levelId) {
 	Common::String prefix = getLevelPrefix(levelId);
 	Common::String filename = Common::String::format("%s/%sBEG.SAN", dir.c_str(), prefix.c_str());
 
-	debugC(DEBUG_INSANE, "Rebel2: Playing level %d beginning: %s", levelId, filename.c_str());
+	debugC(DEBUG_INSANE, "Playing level %d beginning: %s", levelId, filename.c_str());
 
 	if (levelId >= 1 && levelId <= 15 && levelTextParams[levelId].textID >= 0) {
 		const TextOverlayParams &p = levelTextParams[levelId];
@@ -292,7 +292,7 @@ void InsaneRebel2::playLevelEnd(int levelId) {
 	Common::String prefix = getLevelPrefix(levelId);
 	Common::String filename = Common::String::format("%s/%sEND.SAN", dir.c_str(), prefix.c_str());
 
-	debugC(DEBUG_INSANE, "Rebel2: Playing level %d end: %s", levelId, filename.c_str());
+	debugC(DEBUG_INSANE, "Playing level %d end: %s", levelId, filename.c_str());
 
 	SmushPlayer *splayer = ((ScummEngine_v7 *)_vm)->_splayer;
 	// Original: FUN_00417327 adds | 8, so flags = 0x20 | 0x08 = 0x28
@@ -313,7 +313,7 @@ void InsaneRebel2::playLevelRetry(int levelId) {
 	Common::String prefix = getLevelPrefix(levelId);
 	Common::String filename = Common::String::format("%s/%sRETRY.SAN", dir.c_str(), prefix.c_str());
 
-	debugC(DEBUG_INSANE, "Rebel2: Playing level %d retry: %s", levelId, filename.c_str());
+	debugC(DEBUG_INSANE, "Playing level %d retry: %s", levelId, filename.c_str());
 
 	SmushPlayer *splayer = ((ScummEngine_v7 *)_vm)->_splayer;
 	// Original: FUN_00417168 adds | 8, so flags = 0x20 | 0x08 = 0x28
@@ -334,7 +334,7 @@ void InsaneRebel2::playLevelGameOver(int levelId) {
 	Common::String prefix = getLevelPrefix(levelId);
 	Common::String filename = Common::String::format("%s/%sOVER.SAN", dir.c_str(), prefix.c_str());
 
-	debugC(DEBUG_INSANE, "Rebel2: Playing level %d game over: %s", levelId, filename.c_str());
+	debugC(DEBUG_INSANE, "Playing level %d game over: %s", levelId, filename.c_str());
 
 	SmushPlayer *splayer = ((ScummEngine_v7 *)_vm)->_splayer;
 	// Original: FUN_00417ab2 adds | 8, so flags = 0x20 | 0x08 = 0x28
@@ -355,7 +355,7 @@ void InsaneRebel2::playLevelGameOver(int levelId) {
 //
 void InsaneRebel2::playEndingSequence() {
 
-	debugC(DEBUG_INSANE, "Rebel2: Playing ending sequence (difficulty=%d)", _difficulty);
+	debugC(DEBUG_INSANE, "Playing ending sequence (difficulty=%d)", _difficulty);
 
 	// Switch to gameplay state to stop menu overlay rendering
 	_gameState = kStateGameplay;
@@ -392,7 +392,7 @@ void InsaneRebel2::playEndingSequence() {
 // This is the credits accessible from the main menu, NOT the ending credits.
 void InsaneRebel2::playCreditsSequence() {
 
-	debugC(DEBUG_INSANE, "Rebel2: Playing menu credits");
+	debugC(DEBUG_INSANE, "Playing menu credits");
 	resetVideoAudio();
 
 	SmushPlayer *splayer = ((ScummEngine_v7 *)_vm)->_splayer;
@@ -412,7 +412,7 @@ void InsaneRebel2::centerGameplayAim() {
 	purgeRebel2GameplayInputEvents(eventMan);
 	_gameplayMouseSettleUntil = _vm->_system->getMillis() + kRebel2GameplayMouseSettleMs;
 
-	debugC(DEBUG_INSANE, "Rebel2 centerGameplayAim: mouse=(%d,%d) joystick=(%d,%d) gamepadAim=%d settleUntil=%u",
+	debugC(DEBUG_INSANE, "centerGameplayAim: mouse=(%d,%d) joystick=(%d,%d) gamepadAim=%d settleUntil=%u",
 		_vm->_mouse.x, _vm->_mouse.y,
 		_joystickAxisX, _joystickAxisY, _gamepadAimActive ? 1 : 0,
 		_gameplayMouseSettleUntil);
@@ -439,7 +439,7 @@ void InsaneRebel2::warpGameplayMouseNow(int x, int y) {
 // runLevel -- Main level dispatcher, calls per-level handlers.
 int InsaneRebel2::runLevel(int levelId) {
 
-	debugC(DEBUG_INSANE, "Rebel2: Starting level %d", levelId);
+	debugC(DEBUG_INSANE, "Starting level %d", levelId);
 
 	// Validate level ID
 	if (levelId < 1 || levelId > 15) {
@@ -755,7 +755,7 @@ void InsaneRebel2::playLevelDeathVariant(int levelId, int phase, int frame) {
 		filename = Common::String::format("%s/%sDIE_%s.SAN", dir.c_str(), prefix.c_str(), variant.c_str());
 	}
 
-	debugC(DEBUG_INSANE, "Rebel2: Playing death video: %s (phase=%d, frame=%d)", filename.c_str(), phase, frame);
+	debugC(DEBUG_INSANE, "Playing death video: %s (phase=%d, frame=%d)", filename.c_str(), phase, frame);
 
 	SmushPlayer *splayer = ((ScummEngine_v7 *)_vm)->_splayer;
 	// Original: FUN_00417168 adds | 8, so flags = 0x20 | 0x08 = 0x28
@@ -784,7 +784,7 @@ void InsaneRebel2::playLevelRetryVariant(int levelId, int phase) {
 		filename = Common::String::format("%s/%sRETRY.SAN", dir.c_str(), prefix.c_str());
 	}
 
-	debugC(DEBUG_INSANE, "Rebel2: Playing retry video: %s (phase=%d)", filename.c_str(), phase);
+	debugC(DEBUG_INSANE, "Playing retry video: %s (phase=%d)", filename.c_str(), phase);
 
 	SmushPlayer *splayer = ((ScummEngine_v7 *)_vm)->_splayer;
 	// Original: FUN_00417168 adds | 8, so flags = 0x20 | 0x08 = 0x28
diff --git a/engines/scumm/insane/rebel2/menu.cpp b/engines/scumm/insane/rebel2/menu.cpp
index e40c088534d..ad27d5c391d 100644
--- a/engines/scumm/insane/rebel2/menu.cpp
+++ b/engines/scumm/insane/rebel2/menu.cpp
@@ -84,7 +84,7 @@ void InsaneRebel2::updateMenuVirtualKeyboard() {
 
 // unlockAllChapters -- Debug mode unlock (FUN_00415CF8 lines 60-71, DAT_0047ab34=='d').
 void InsaneRebel2::unlockAllChapters() {
-	debugC(DEBUG_INSANE, "Rebel2: Unlocking all chapters for testing");
+	debugC(DEBUG_INSANE, "Unlocking all chapters for testing");
 	for (int i = 0; i < 16; i++) {
 		_chapterUnlocked[i] = true;
 		_levelUnlocked[i] = true;
@@ -106,7 +106,7 @@ Common::String InsaneRebel2::getRandomMenuVideo() {
 
 	// Map 0-14 to A-O (case 0/default = A, 1 = B, etc.)
 	char letter = 'A' + variant;
-	debugC(DEBUG_INSANE, "Rebel2: Selected menu variant %c", letter);
+	debugC(DEBUG_INSANE, "Selected menu variant %c", letter);
 	return Common::String::format("OPEN/O_MENU_%c.SAN", letter);
 }
 
@@ -595,7 +595,7 @@ void InsaneRebel2::showPauseOverlay() {
 // Returns kMenuNewGame, kMenuResumeDemo, or kMenuQuit.
 int InsaneRebel2::runMainMenu() {
 
-	debugC(DEBUG_INSANE, "Rebel2: Entering main menu");
+	debugC(DEBUG_INSANE, "Entering main menu");
 
 	resetMenu();
 	_gameState = kStateMainMenu;
@@ -617,7 +617,7 @@ int InsaneRebel2::runMainMenu() {
 
 		// Select and play a random menu video
 		Common::String menuVideo = getRandomMenuVideo();
-		debugC(DEBUG_INSANE, "Rebel2: Playing menu video: %s", menuVideo.c_str());
+		debugC(DEBUG_INSANE, "Playing menu video: %s", menuVideo.c_str());
 
 		// Set video flags for menu (0x20 = intro/menu flag)
 		// This tells procPostRendering we're in menu mode
@@ -635,7 +635,7 @@ int InsaneRebel2::runMainMenu() {
 		}
 
 		if (_menuInactivityTimedOut) {
-			debugC(DEBUG_INSANE, "Rebel2: Main menu inactivity - resuming intro/demo loop");
+			debugC(DEBUG_INSANE, "Main menu inactivity - resuming intro/demo loop");
 			_menuInactivityTimedOut = false;
 			_menuInputActive = false;
 			return kMenuResumeDemo;
@@ -652,7 +652,7 @@ int InsaneRebel2::runMainMenu() {
 		_menuSelectionConfirmed = false;
 
 		// A selection was made - process it
-		debugC(DEBUG_INSANE, "Rebel2: Menu video ended with selection=%d", _menuSelection);
+		debugC(DEBUG_INSANE, "Menu video ended with selection=%d", _menuSelection);
 
 		// Process the menu result based on current selection
 		// Menu items matching GAME.TRS indices 11-17 (FUN_004147B2):
@@ -665,34 +665,34 @@ int InsaneRebel2::runMainMenu() {
 		//   case 6 (TRS 17): Return to Launcher -> quit, returns 0
 		switch (_menuSelection) {
 		case 0:  // Start Game -> go to pilot selection
-			debugC(DEBUG_INSANE, "Rebel2: Start Game selected - going to pilot selection");
+			debugC(DEBUG_INSANE, "Start Game selected - going to pilot selection");
 			_gameState = kStatePilotSelect;
 			_menuInputActive = false;
 			return kMenuNewGame;  // Return 2 (kMenuNewGame)
 
 		case 1:  // Options -> show options menu (FUN_00416787)
-			debugC(DEBUG_INSANE, "Rebel2: Options selected");
+			debugC(DEBUG_INSANE, "Options selected");
 			showOptionsMenu();
 			break;
 
 		case 2:  // Calibrate Joystick
-			debugC(DEBUG_INSANE, "Rebel2: Calibrate Joystick selected - no-op for modern joystick support");
+			debugC(DEBUG_INSANE, "Calibrate Joystick selected - no-op for modern joystick support");
 			// Modern controller support uses live keymapper actions; no explicit
 			// joystick calibration flow is required here.
 			break;
 
 		case 3:  // Continue Intro -> return to intro/demo loop
-			debugC(DEBUG_INSANE, "Rebel2: Continue Intro selected - resuming intro/demo loop");
+			debugC(DEBUG_INSANE, "Continue Intro selected - resuming intro/demo loop");
 			_menuInputActive = false;
 			return kMenuResumeDemo;
 
 		case 4:  // Show Top Pilots -> high score display (FUN_00420116(-1))
-			debugC(DEBUG_INSANE, "Rebel2: Show Top Pilots selected");
+			debugC(DEBUG_INSANE, "Show Top Pilots selected");
 			showTopPilots();
 			break;
 
 		case 5:  // Show Credits -> play credits video
-			debugC(DEBUG_INSANE, "Rebel2: Show Credits selected - playing O_CREDIT.SAN");
+			debugC(DEBUG_INSANE, "Show Credits selected - playing O_CREDIT.SAN");
 			_gameState = kStateCredits;
 			_menuInputActive = false;
 			splayer->setCurVideoFlags(0x20);
@@ -703,12 +703,12 @@ int InsaneRebel2::runMainMenu() {
 			break;
 
 		case 6:  // Return to Launcher -> quit game
-			debugC(DEBUG_INSANE, "Rebel2: Return to Launcher selected");
+			debugC(DEBUG_INSANE, "Return to Launcher selected");
 			_menuInputActive = false;
 			return kMenuQuit;
 
 		default:
-			debugC(DEBUG_INSANE, "Rebel2: Unknown menu selection %d", _menuSelection);
+			debugC(DEBUG_INSANE, "Unknown menu selection %d", _menuSelection);
 			break;
 		}
 	}
@@ -725,7 +725,7 @@ int InsaneRebel2::runMainMenu() {
 // Returns kChapterSelectPlay, kChapterSelectBack, or kChapterSelectQuit.
 int InsaneRebel2::runChapterSelect() {
 
-	debugC(DEBUG_INSANE, "Rebel2: Entering chapter selection (FUN_00415CF8)");
+	debugC(DEBUG_INSANE, "Entering chapter selection (FUN_00415CF8)");
 
 	// Enable menu input capture
 	_menuInputActive = true;
@@ -769,7 +769,7 @@ int InsaneRebel2::runChapterSelect() {
 	while (!_vm->shouldQuit()) {
 		_vm->_smushVideoShouldFinish = false;
 
-		debugC(DEBUG_INSANE, "Rebel2: Playing chapter select background: OPEN/O_LEVEL.SAN");
+		debugC(DEBUG_INSANE, "Playing chapter select background: OPEN/O_LEVEL.SAN");
 
 		// Flags: 0x20 (overlay/menu rendering) | 0x08 (preserve buffer, suppress
 		// AHDR speed override). Matches original FUN_0041f4d0 parameter 8.
@@ -795,12 +795,12 @@ int InsaneRebel2::runChapterSelect() {
 		_vm->_smushVideoShouldFinish = false;
 		_menuSelectionConfirmed = false;
 
-		debugC(DEBUG_INSANE, "Rebel2: Chapter selection made: %d", _chapterSelection);
+		debugC(DEBUG_INSANE, "Chapter selection made: %d", _chapterSelection);
 
 		// Process chapter selection (lines 134-236 of FUN_00415CF8)
 		if (_chapterSelection == 16) {
 			// BACK selected (index 16 = 17th item)
-			debugC(DEBUG_INSANE, "Rebel2: BACK to main menu selected");
+			debugC(DEBUG_INSANE, "BACK to main menu selected");
 			setVirtualKeyboardVisible(false);
 			_menuInputActive = false;
 			return kChapterSelectBack;
@@ -810,7 +810,7 @@ int InsaneRebel2::runChapterSelect() {
 			if (_chapterUnlocked[_chapterSelection]) {
 				// Unlocked chapter ??? play it
 				_selectedChapter = _chapterSelection;
-				debugC(DEBUG_INSANE, "Rebel2: Chapter %d selected (unlocked)", _selectedChapter + 1);
+				debugC(DEBUG_INSANE, "Chapter %d selected (unlocked)", _selectedChapter + 1);
 				setVirtualKeyboardVisible(false);
 				_menuInputActive = false;
 				return kChapterSelectPlay;
@@ -834,13 +834,13 @@ int InsaneRebel2::runChapterSelect() {
 					setBit(16 - _chapterSelection);
 					_passwordInput.clear();
 					updateMenuVirtualKeyboard();
-					debugC(DEBUG_INSANE, "Rebel2: Chapter %d unlocked via password", _chapterSelection + 1);
+					debugC(DEBUG_INSANE, "Chapter %d unlocked via password", _chapterSelection + 1);
 					continue;  // Re-render with updated unlock state
 				}
 			}
 			// Wrong password or no password entered
 			_passwordInput.clear();
-			debugC(DEBUG_INSANE, "Rebel2: Password rejected for chapter %d", _chapterSelection + 1);
+			debugC(DEBUG_INSANE, "Password rejected for chapter %d", _chapterSelection + 1);
 		}
 	}
 
@@ -1222,7 +1222,7 @@ void InsaneRebel2::drawChapterSelectOverlay(byte *renderBitmap, int pitch, int w
 // Returns kLevelSelectPlay, kLevelSelectBack, or kLevelSelectQuit.
 int InsaneRebel2::runLevelSelect() {
 
-	debugC(DEBUG_INSANE, "Rebel2: Entering pilot selection (FUN_00414A41), %d pilots loaded", _numPilots);
+	debugC(DEBUG_INSANE, "Entering pilot selection (FUN_00414A41), %d pilots loaded", _numPilots);
 
 	_menuInputActive = true;
 	while (!_menuEventQueue.empty())
@@ -1303,7 +1303,7 @@ int InsaneRebel2::runLevelSelect() {
 					copyPilot(pilotIndex);
 					savePilots();
 					_levelSelection = pilotIndex;
-					debugC(DEBUG_INSANE, "Rebel2: Copied pilot %d, now %d pilots", pilotIndex, _numPilots);
+					debugC(DEBUG_INSANE, "Copied pilot %d, now %d pilots", pilotIndex, _numPilots);
 				} else {
 					deletePilot(pilotIndex);
 					savePilots();
@@ -1312,7 +1312,7 @@ int InsaneRebel2::runLevelSelect() {
 					} else {
 						_levelSelection = 0;
 					}
-					debugC(DEBUG_INSANE, "Rebel2: Deleted pilot %d, %d remaining", pilotIndex, _numPilots);
+					debugC(DEBUG_INSANE, "Deleted pilot %d, %d remaining", pilotIndex, _numPilots);
 				}
 			}
 			_pilotMenuMode = kPilotModeSelect;
@@ -1322,7 +1322,7 @@ int InsaneRebel2::runLevelSelect() {
 		}
 
 		// --- Normal pilot menu selection ---
-		debugC(DEBUG_INSANE, "Rebel2: Pilot selection: %d (numPilots=%d)", _levelSelection, _numPilots);
+		debugC(DEBUG_INSANE, "Pilot selection: %d (numPilots=%d)", _levelSelection, _numPilots);
 
 		if (_levelSelection < _numPilots) {
 			// Existing pilot selected ??? activate and go to chapter select
@@ -1334,7 +1334,7 @@ int InsaneRebel2::runLevelSelect() {
 				_chapterUnlocked[i] = _debugUnlockAll || (_pilots[_activePilot].damage[i] < 0xFF);
 			}
 
-			debugC(DEBUG_INSANE, "Rebel2: Pilot '%s' selected (slot %d, difficulty %d)",
+			debugC(DEBUG_INSANE, "Pilot '%s' selected (slot %d, difficulty %d)",
 			      _pilots[_activePilot].name, _activePilot, _difficulty);
 			setVirtualKeyboardVisible(false);
 			_menuInputActive = false;
@@ -1349,7 +1349,7 @@ int InsaneRebel2::runLevelSelect() {
 				_pilotMenuMode = kPilotModeNameInput;
 				_levelItemCount = _numPilots + 4;
 				updateMenuVirtualKeyboard();
-				debugC(DEBUG_INSANE, "Rebel2: NEW PILOT - entering name for slot %d", newIdx);
+				debugC(DEBUG_INSANE, "NEW PILOT - entering name for slot %d", newIdx);
 			}
 			continue;
 
@@ -1359,7 +1359,7 @@ int InsaneRebel2::runLevelSelect() {
 				_pilotMenuMode = kPilotModeCopySelect;
 				_levelSelection = 0;
 				_levelItemCount = _numPilots;
-				debugC(DEBUG_INSANE, "Rebel2: COPY PILOT - selecting source");
+				debugC(DEBUG_INSANE, "COPY PILOT - selecting source");
 			}
 			continue;
 
@@ -1369,7 +1369,7 @@ int InsaneRebel2::runLevelSelect() {
 				_pilotMenuMode = kPilotModeDeleteSelect;
 				_levelSelection = 0;
 				_levelItemCount = _numPilots;
-				debugC(DEBUG_INSANE, "Rebel2: DELETE PILOT - selecting target");
+				debugC(DEBUG_INSANE, "DELETE PILOT - selecting target");
 			}
 			continue;
 
@@ -1731,7 +1731,7 @@ void InsaneRebel2::insertRanking(const char *name, int32 score, int32 rating,
 }
 
 void InsaneRebel2::showTopPilots() {
-	debugC(DEBUG_INSANE, "Rebel2: Showing Top Pilots screen (FUN_00420116)");
+	debugC(DEBUG_INSANE, "Showing Top Pilots screen (FUN_00420116)");
 
 	_menuInputActive = true;
 	while (!_menuEventQueue.empty())
@@ -1754,7 +1754,7 @@ void InsaneRebel2::showTopPilots() {
 	_gameState = kStateMainMenu;
 	_menuInputActive = true;
 
-	debugC(DEBUG_INSANE, "Rebel2: Top Pilots screen finished");
+	debugC(DEBUG_INSANE, "Top Pilots screen finished");
 }
 
 void InsaneRebel2::drawTopPilotsOverlay(byte *renderBitmap, int pitch, int width, int height) {
@@ -1838,7 +1838,7 @@ void InsaneRebel2::drawTopPilotsOverlay(byte *renderBitmap, int pitch, int width
 
 // showOptionsMenu -- Options menu loop (FUN_00416787).
 void InsaneRebel2::showOptionsMenu() {
-	debugC(DEBUG_INSANE, "Rebel2: Showing Options menu (FUN_00416787)");
+	debugC(DEBUG_INSANE, "Showing Options menu (FUN_00416787)");
 
 	_menuInputActive = true;
 	while (!_menuEventQueue.empty())
@@ -1864,7 +1864,7 @@ void InsaneRebel2::showOptionsMenu() {
 	_gameState = kStateMainMenu;
 	_menuInputActive = true;
 
-	debugC(DEBUG_INSANE, "Rebel2: Options menu finished");
+	debugC(DEBUG_INSANE, "Options menu finished");
 }
 
 int InsaneRebel2::processOptionsInput() {
diff --git a/engines/scumm/insane/rebel2/rebel.cpp b/engines/scumm/insane/rebel2/rebel.cpp
index 3b98f147377..df84162a961 100644
--- a/engines/scumm/insane/rebel2/rebel.cpp
+++ b/engines/scumm/insane/rebel2/rebel.cpp
@@ -689,7 +689,7 @@ bool InsaneRebel2::notifyEvent(const Common::Event &event) {
 			// Retail DAT_0047ab60: Yoda-mode Movie Mode skips playable
 			// sections and keeps the story/cutscene sequence moving.
 			_rebelMovieMode = !_rebelMovieMode;
-			debugC(DEBUG_INSANE, "Rebel2: Movie mode %s", _rebelMovieMode ? "enabled" : "disabled");
+			debugC(DEBUG_INSANE, "Movie mode %s", _rebelMovieMode ? "enabled" : "disabled");
 			if (_rebelMovieMode && splayer && _gameState == kStateGameplay && _rebelHandler != 0)
 				_vm->_smushVideoShouldFinish = true;
 			return true;
@@ -698,7 +698,7 @@ bool InsaneRebel2::notifyEvent(const Common::Event &event) {
 			// Retail DAT_0047ab64: Yoda-mode Auto Play makes gameplay
 			// computer controlled.
 			_rebelAutoPlay = !_rebelAutoPlay;
-			debugC(DEBUG_INSANE, "Rebel2: Auto play %s", _rebelAutoPlay ? "enabled" : "disabled");
+			debugC(DEBUG_INSANE, "Auto play %s", _rebelAutoPlay ? "enabled" : "disabled");
 			return true;
 
 		default:
@@ -731,7 +731,7 @@ bool InsaneRebel2::notifyEvent(const Common::Event &event) {
 					_gameplayMouseSettleUntil = now + kRA2Handler7MouseSettleExtendMs;
 					warpGameplayMouseNow(recenter.x, recenter.y);
 
-					debugC(DEBUG_INSANE, "Rebel2 H7 mouse settle: suppress pos=(%d,%d) rel=(%d,%d) current=(%d,%d) until=%u",
+					debugC(DEBUG_INSANE, "H7 mouse settle: suppress pos=(%d,%d) rel=(%d,%d) current=(%d,%d) until=%u",
 						event.mouse.x, event.mouse.y, event.relMouse.x, event.relMouse.y,
 						_vm->_mouse.x, _vm->_mouse.y, _gameplayMouseSettleUntil);
 					return true;
@@ -741,7 +741,7 @@ bool InsaneRebel2::notifyEvent(const Common::Event &event) {
 			_gameplayMouseSettleUntil = 0;
 		}
 
-		debugC(DEBUG_INSANE, "Rebel2 H7 mouse event: pos=(%d,%d) rel=(%d,%d) gamepadAim=%d menuInput=%d",
+		debugC(DEBUG_INSANE, "H7 mouse event: pos=(%d,%d) rel=(%d,%d) gamepadAim=%d menuInput=%d",
 			event.mouse.x, event.mouse.y, event.relMouse.x, event.relMouse.y,
 			_gamepadAimActive ? 1 : 0, _menuInputActive ? 1 : 0);
 	}
@@ -901,11 +901,11 @@ bool InsaneRebel2::notifyEvent(const Common::Event &event) {
 			}
 
 			if (_gameState == kStateGameplay && _rebelHandler != 0) {
-				debugC(DEBUG_INSANE, "Rebel2: Skip/back action ignored during gameplay");
+				debugC(DEBUG_INSANE, "Skip/back action ignored during gameplay");
 				return true;
 			}
 
-			debugC(DEBUG_INSANE, "Rebel2: Skip/back action - skipping video");
+			debugC(DEBUG_INSANE, "Skip/back action - skipping video");
 			_vm->_smushVideoShouldFinish = true;
 			return true;
 		}
@@ -915,7 +915,7 @@ bool InsaneRebel2::notifyEvent(const Common::Event &event) {
 				return true;
 
 			if (_menuInputActive && menuState) {
-				debugC(DEBUG_INSANE, "Rebel2: Back/menu action in menu - opening ScummVM menu");
+				debugC(DEBUG_INSANE, "Back/menu action in menu - opening ScummVM menu");
 				openMenuMainMenu(splayer);
 				return true;
 			}
@@ -924,23 +924,23 @@ bool InsaneRebel2::notifyEvent(const Common::Event &event) {
 				if (_lastGameplayMenuCloseTime != 0) {
 					const uint32 elapsedSinceMenuClose = _vm->_system->getMillis() - _lastGameplayMenuCloseTime;
 					if (elapsedSinceMenuClose < 500) {
-						debugC(DEBUG_INSANE, "Rebel2: Ignoring repeated gameplay menu action (%u ms)", elapsedSinceMenuClose);
+						debugC(DEBUG_INSANE, "Ignoring repeated gameplay menu action (%u ms)", elapsedSinceMenuClose);
 						return true;
 					}
 				}
 
-				debugC(DEBUG_INSANE, "Rebel2: Back/menu action during gameplay - opening ScummVM menu");
+				debugC(DEBUG_INSANE, "Back/menu action during gameplay - opening ScummVM menu");
 				openGameplayMainMenu(splayer);
 				return true;
 			}
 
-			debugC(DEBUG_INSANE, "Rebel2: Back/menu action - skipping video");
+			debugC(DEBUG_INSANE, "Back/menu action - skipping video");
 			_vm->_smushVideoShouldFinish = true;
 			return true;
 		}
 
 		if (pressed && splayer && splayer->_paused && _gameState == kStateGameplay) {
-			debugC(DEBUG_INSANE, "Rebel2: Joystick action while paused - unpausing");
+			debugC(DEBUG_INSANE, "Joystick action while paused - unpausing");
 			if (_pauseOverlayActive) {
 				_vm->_system->getPaletteManager()->setPalette(_savedPausePalette, 0, 256);
 				_pauseOverlayActive = false;
@@ -1032,11 +1032,11 @@ bool InsaneRebel2::notifyEvent(const Common::Event &event) {
 			(event.type == Common::EVENT_MAINMENU ||
 			 (event.type == Common::EVENT_KEYDOWN && event.kbd.keycode == Common::KEYCODE_ESCAPE))) {
 		if (event.type == Common::EVENT_KEYDOWN && event.kbdRepeat) {
-			debugC(DEBUG_INSANE, "Rebel2: Ignoring repeated ESC keydown in menu");
+			debugC(DEBUG_INSANE, "Ignoring repeated ESC keydown in menu");
 			return true;
 		}
 
-		debugC(DEBUG_INSANE, "Rebel2: Opening ScummVM menu from menu state");
+		debugC(DEBUG_INSANE, "Opening ScummVM menu from menu state");
 		openMenuMainMenu(splayer);
 		return true;
 	}
@@ -1051,14 +1051,14 @@ bool InsaneRebel2::notifyEvent(const Common::Event &event) {
 	if (gameplayMenuTrigger && _gameState == kStateGameplay && _lastGameplayMenuCloseTime != 0) {
 		const uint32 elapsedSinceMenuClose = _vm->_system->getMillis() - _lastGameplayMenuCloseTime;
 		if (elapsedSinceMenuClose < 500) {
-			debugC(DEBUG_INSANE, "Rebel2: Ignoring repeated gameplay menu trigger (%u ms)", elapsedSinceMenuClose);
+			debugC(DEBUG_INSANE, "Ignoring repeated gameplay menu trigger (%u ms)", elapsedSinceMenuClose);
 			return true;
 		}
 	}
 
 	if (event.type == Common::EVENT_MAINMENU && splayer &&
 			_gameState == kStateGameplay && _rebelHandler != 0) {
-		debugC(DEBUG_INSANE, "Rebel2: Main menu action during gameplay - opening ScummVM menu");
+		debugC(DEBUG_INSANE, "Main menu action during gameplay - opening ScummVM menu");
 		openGameplayMainMenu(splayer);
 		return true;
 	}
@@ -1068,7 +1068,7 @@ bool InsaneRebel2::notifyEvent(const Common::Event &event) {
 		// leaving the key repeat source to synthesize another ESC immediately after
 		// returning to gameplay. Treat repeats as part of the original press.
 		if (event.kbd.keycode == Common::KEYCODE_ESCAPE && event.kbdRepeat) {
-			debugC(DEBUG_INSANE, "Rebel2: Ignoring repeated ESC keydown");
+			debugC(DEBUG_INSANE, "Ignoring repeated ESC keydown");
 			return true;
 		}
 		if (_menuInputActive && event.kbdRepeat &&
@@ -1076,14 +1076,14 @@ bool InsaneRebel2::notifyEvent(const Common::Event &event) {
 				 event.kbd.keycode == Common::KEYCODE_DOWN ||
 				 event.kbd.keycode == Common::KEYCODE_LEFT ||
 				 event.kbd.keycode == Common::KEYCODE_RIGHT)) {
-			debugC(DEBUG_INSANE, "Rebel2: Ignoring repeated menu direction keydown");
+			debugC(DEBUG_INSANE, "Ignoring repeated menu direction keydown");
 			return true;
 		}
 
 		// When paused during gameplay, ANY key unpauses (FUN_405A21 line 360-365).
 		// ESC additionally opens the ScummVM menu (original: quit key exits level).
 		if (splayer && splayer->_paused && _gameState == kStateGameplay) {
-			debugC(DEBUG_INSANE, "Rebel2: Key pressed while paused - unpausing");
+			debugC(DEBUG_INSANE, "Key pressed while paused - unpausing");
 			// Restore the original palette saved by showPauseOverlay
 			if (_pauseOverlayActive) {
 				_vm->_system->getPaletteManager()->setPalette(_savedPausePalette, 0, 256);
@@ -1091,7 +1091,7 @@ bool InsaneRebel2::notifyEvent(const Common::Event &event) {
 			}
 			splayer->unpause();
 			if (event.kbd.keycode == Common::KEYCODE_ESCAPE && _rebelHandler != 0) {
-				debugC(DEBUG_INSANE, "Rebel2: ESC during pause - opening ScummVM menu");
+				debugC(DEBUG_INSANE, "ESC during pause - opening ScummVM menu");
 				openGameplayMainMenu(splayer);
 			}
 			return true;
@@ -1106,11 +1106,11 @@ bool InsaneRebel2::notifyEvent(const Common::Event &event) {
 			if (splayer) {
 				if (_gameState == kStateGameplay && _rebelHandler != 0) {
 					// During active gameplay (handler != 0): pause and open ScummVM menu.
-					debugC(DEBUG_INSANE, "Rebel2: ESC pressed during gameplay - opening ScummVM menu");
+					debugC(DEBUG_INSANE, "ESC pressed during gameplay - opening ScummVM menu");
 					openGameplayMainMenu(splayer);
 				} else {
 					// During cutscenes/intros/mission briefings: skip video
-					debugC(DEBUG_INSANE, "Rebel2: ESC pressed - skipping video");
+					debugC(DEBUG_INSANE, "ESC pressed - skipping video");
 					_vm->_smushVideoShouldFinish = true;
 				}
 				return true;  // Consume the event
@@ -1121,7 +1121,7 @@ bool InsaneRebel2::notifyEvent(const Common::Event &event) {
 			// SPACE pauses during gameplay (FUN_405A21).
 			// Unpausing is handled above (any key while paused).
 			if (splayer && _gameState == kStateGameplay && !splayer->_paused) {
-				debugC(DEBUG_INSANE, "Rebel2: SPACE pressed - pausing");
+				debugC(DEBUG_INSANE, "SPACE pressed - pausing");
 				splayer->pause();
 				showPauseOverlay();
 				return true;
@@ -1135,7 +1135,7 @@ bool InsaneRebel2::notifyEvent(const Common::Event &event) {
 			    _rebelHandler != 0 &&
 			    event.kbd.hasFlags(Common::KBD_SHIFT)) {
 				_skipSectionRequested = true;
-				debugC(DEBUG_INSANE, "Rebel2: Shift+S pressed - requesting gameplay section skip");
+				debugC(DEBUG_INSANE, "Shift+S pressed - requesting gameplay section skip");
 				_vm->_smushVideoShouldFinish = true;
 				return true;  // Consume the event
 			}
@@ -1150,10 +1150,10 @@ bool InsaneRebel2::notifyEvent(const Common::Event &event) {
 				if (!_noDamage) {
 					_playerDamage = 255;
 					_playerShield = 0;
-					debugC(DEBUG_INSANE, "Rebel2: Shift+D pressed - forcing player death");
+					debugC(DEBUG_INSANE, "Shift+D pressed - forcing player death");
 					_vm->_smushVideoShouldFinish = true;
 				} else {
-					debugC(DEBUG_INSANE, "Rebel2: Shift+D pressed - no damage mode prevents forced death");
+					debugC(DEBUG_INSANE, "Shift+D pressed - no damage mode prevents forced death");
 				}
 				return true;  // Consume the event
 			}
@@ -1385,13 +1385,13 @@ void InsaneRebel2::addScore(int points) {
 			// Award bonus life
 			_playerLives++;
 			// TODO: Play bonus life sound (FUN_0041189e(5, 0, 0x7f, 0, 0))
-			debugC(DEBUG_INSANE, "Rebel2: BONUS LIFE! Score crossed %d threshold. Lives=%d", threshold, _playerLives);
+			debugC(DEBUG_INSANE, "BONUS LIFE! Score crossed %d threshold. Lives=%d", threshold, _playerLives);
 		}
 	}
 
 	// Add points to score
 	_playerScore += points;
-	debugC(DEBUG_INSANE, "Rebel2: Score +%d = %d", points, _playerScore);
+	debugC(DEBUG_INSANE, "Score +%d = %d", points, _playerScore);
 }
 
 // Render score text to HUD (part of FUN_0041c012)
@@ -1478,7 +1478,7 @@ bool InsaneRebel2::loadPilots() {
 		_numPilots = i + 1;
 	}
 
-	debugC(DEBUG_INSANE, "Rebel2: Loaded %d pilot(s)", _numPilots);
+	debugC(DEBUG_INSANE, "Loaded %d pilot(s)", _numPilots);
 	return _numPilots > 0;
 }
 
@@ -1520,7 +1520,7 @@ bool InsaneRebel2::savePilots() {
 		_vm->_saveFileMan->removeSavefile(filename);
 	}
 
-	debugC(DEBUG_INSANE, "Rebel2: Saved %d pilot(s)", _numPilots);
+	debugC(DEBUG_INSANE, "Saved %d pilot(s)", _numPilots);
 	return ok;
 }
 
@@ -1671,7 +1671,7 @@ int32 InsaneRebel2::processMouse() {
 			mousePos = getGameplayAimPoint();
 		}
 		Common::Point gameplayAim = getGameplayAimPoint();
-		debugC(DEBUG_INSANE, "Rebel2 Click: Mouse=(%d,%d) Target=(%d,%d) Enemies=%d",
+		debugC(DEBUG_INSANE, "Click: Mouse=(%d,%d) Target=(%d,%d) Enemies=%d",
 			gameplayAim.x, gameplayAim.y, mousePos.x, mousePos.y, _enemies.size());
 
 		// Spawn visual shot immediately
@@ -1699,7 +1699,7 @@ int32 InsaneRebel2::processMouse() {
 				// Enemy hit!
 				it->active = false;
 				it->destroyed = true;  // Mark as destroyed so IACT won't re-activate
-				debugC(DEBUG_INSANE, "Rebel2: HIT enemy ID=%d type=%d at (%d,%d) - Rect: (%d,%d)-(%d,%d)",
+				debugC(DEBUG_INSANE, "HIT enemy ID=%d type=%d at (%d,%d) - Rect: (%d,%d)-(%d,%d)",
 					it->id, it->type, mousePos.x, mousePos.y,
 					it->rect.left, it->rect.top, it->rect.right, it->rect.bottom);
 
@@ -1749,7 +1749,7 @@ int32 InsaneRebel2::processMouse() {
 				// This tracks which enemy GROUPS have been killed in this wave
 				if (it->type > 0 && it->type < 32) {
 					_rebelWaveState |= (1 << it->type);
-					debugC(DEBUG_INSANE, "Rebel2: Wave state updated: 0x%x (set bit %d)", _rebelWaveState, it->type);
+					debugC(DEBUG_INSANE, "Wave state updated: 0x%x (set bit %d)", _rebelWaveState, it->type);
 				}
 
 				// Increment kill counter (DAT_0047ab88)
@@ -1766,17 +1766,17 @@ int32 InsaneRebel2::processMouse() {
 					// Slot 2: Enable (Explosion?)
 					if (_rebelLinks[id][2] != 0) {
 						clearBit(_rebelLinks[id][2]);
-						debugC(DEBUG_INSANE, "Rebel2: Enabled dependency Slot 2 (ID=%d) for Parent %d", _rebelLinks[id][2], id);
+						debugC(DEBUG_INSANE, "Enabled dependency Slot 2 (ID=%d) for Parent %d", _rebelLinks[id][2], id);
 					}
 					// Slot 1: Enable (Explosion?)
 					if (_rebelLinks[id][1] != 0) {
 						clearBit(_rebelLinks[id][1]);
-						debugC(DEBUG_INSANE, "Rebel2: Enabled dependency Slot 1 (ID=%d) for Parent %d", _rebelLinks[id][1], id);
+						debugC(DEBUG_INSANE, "Enabled dependency Slot 1 (ID=%d) for Parent %d", _rebelLinks[id][1], id);
 					}
 					// Slot 0: Disable (Shots?)
 					if (_rebelLinks[id][0] != 0) {
 						setBit(_rebelLinks[id][0]);
-						debugC(DEBUG_INSANE, "Rebel2: Disabled dependency Slot 0 (ID=%d) for Parent %d", _rebelLinks[id][0], id);
+						debugC(DEBUG_INSANE, "Disabled dependency Slot 0 (ID=%d) for Parent %d", _rebelLinks[id][0], id);
 					}
 				}
 
@@ -2061,7 +2061,7 @@ void InsaneRebel2::procSKIP(int32 subSize, Common::SeekableReadStream &b) {
 		if (bit1) {
 			_player->_skipNext = true;
 		}
-		debugC(DEBUG_INSANE, "Rebel2 SKIP: single ID=%d bit=%d skip=%d frame=%d", par1, bit1 ? 1 : 0, _player->_skipNext ? 1 : 0, _player->_frame);
+		debugC(DEBUG_INSANE, "SKIP: single ID=%d bit=%d skip=%d frame=%d", par1, bit1 ? 1 : 0, _player->_skipNext ? 1 : 0, _player->_frame);
 	} else {
 		// Dual ID mode: skip if bits are different (XOR logic)
 		bool bit1 = isBitSet(par1);
@@ -2069,7 +2069,7 @@ void InsaneRebel2::procSKIP(int32 subSize, Common::SeekableReadStream &b) {
 		if (bit1 != bit2) {
 			_player->_skipNext = true;
 		}
-		debugC(DEBUG_INSANE, "Rebel2 SKIP: dual ID1=%d(bit=%d) ID2=%d(bit=%d) skip=%d frame=%d", par1, bit1 ? 1 : 0, par2, bit2 ? 1 : 0, _player->_skipNext ? 1 : 0, _player->_frame);
+		debugC(DEBUG_INSANE, "SKIP: dual ID1=%d(bit=%d) ID2=%d(bit=%d) skip=%d frame=%d", par1, bit1 ? 1 : 0, par2, bit2 ? 1 : 0, _player->_skipNext ? 1 : 0, _player->_frame);
 	}
 }
 
diff --git a/engines/scumm/insane/rebel2/render.cpp b/engines/scumm/insane/rebel2/render.cpp
index a33ef1aa849..4256f4e6638 100644
--- a/engines/scumm/insane/rebel2/render.cpp
+++ b/engines/scumm/insane/rebel2/render.cpp
@@ -168,7 +168,7 @@ static bool readEmbeddedSanChunkHeader(Common::SeekableReadStream &stream, int64
 
 	const int64 dataStart = stream.pos();
 	if ((int64)chunkSize > containerEnd - dataStart) {
-		debugC(DEBUG_INSANE, "Rebel2: Truncated embedded SAN %s chunk 0x%08X at %lld: size=%u, remaining=%lld",
+		debugC(DEBUG_INSANE, "Truncated embedded SAN %s chunk 0x%08X at %lld: size=%u, remaining=%lld",
 			context, tag, headerPos, chunkSize, containerEnd - dataStart);
 		return false;
 	}
@@ -204,12 +204,12 @@ void InsaneRebel2::renderEmbeddedFrame(byte *renderBitmap, const EmbeddedSanFram
 	}
 	if (_rebelHandler == 0x19 && (userId == 6 || userId == 7)) {
 		skipImmediateDraw = false;
-		debugC(DEBUG_INSANE, "Rebel2: Handler 25 static overlay userId=%d - forcing immediate draw", userId);
+		debugC(DEBUG_INSANE, "Handler 25 static overlay userId=%d - forcing immediate draw", userId);
 	}
 
 	if (!frame.valid || !renderBitmap || skipImmediateDraw) {
 		if (skipImmediateDraw && frame.valid) {
-			debugC(DEBUG_INSANE, "Rebel2: Skipped immediate draw for Handler %d HUD %d (will render during post-processing)",
+			debugC(DEBUG_INSANE, "Skipped immediate draw for Handler %d HUD %d (will render during post-processing)",
 				_rebelHandler, userId);
 		}
 		return;
@@ -220,7 +220,7 @@ void InsaneRebel2::renderEmbeddedFrame(byte *renderBitmap, const EmbeddedSanFram
 
 	blitEmbeddedFrameRegion(renderBitmap, pitch, pitch, bufHeight, frame,
 		frame.renderX, frame.renderY, 0, 0, frame.width, frame.height);
-	debugC(DEBUG_INSANE, "Rebel2: Rendered embedded HUD %d at (%d,%d)", userId, frame.renderX, frame.renderY);
+	debugC(DEBUG_INSANE, "Rendered embedded HUD %d at (%d,%d)", userId, frame.renderX, frame.renderY);
 }
 
 void InsaneRebel2::drawHandler25CorridorOverlay(byte *renderBitmap) {
@@ -269,7 +269,7 @@ void InsaneRebel2::drawHandler25CorridorOverlay(byte *renderBitmap) {
 			   drawWidth);
 	}
 
-	debugC(DEBUG_INSANE, "Rebel2 Handler25: Corridor overlay drawn at (%d,%d) size(%d,%d)",
+	debugC(DEBUG_INSANE, "Handler25: Corridor overlay drawn at (%d,%d) size(%d,%d)",
 		_rebelViewOffsetX, _rebelViewOffsetY, corridorOverlay.width, corridorOverlay.height);
 }
 
@@ -282,7 +282,7 @@ void InsaneRebel2::drawHandler25CorridorOverlay(byte *renderBitmap) {
 void InsaneRebel2::loadEmbeddedSan(int userId, byte *animData, int32 size, byte *renderBitmap) {
 	// Validate userId - Level 3 uses slots 0-11, allow up to 15 for safety
 	if (userId < 0 || userId > 15 || !animData || size < 8) {
-		debugC(DEBUG_INSANE, "Rebel2: Invalid embedded SAN: userId=%d, size=%d", userId, size);
+		debugC(DEBUG_INSANE, "Invalid embedded SAN: userId=%d, size=%d", userId, size);
 		return;
 	}
 
@@ -292,7 +292,7 @@ void InsaneRebel2::loadEmbeddedSan(int userId, byte *animData, int32 size, byte
 	// Read ANIM header
 	uint32 animTag = stream.readUint32BE();
 	if (animTag != MKTAG('A','N','I','M')) {
-		debugC(DEBUG_INSANE, "Rebel2: Embedded SAN missing ANIM tag, got 0x%08X", animTag);
+		debugC(DEBUG_INSANE, "Embedded SAN missing ANIM tag, got 0x%08X", animTag);
 		return;
 	}
 	uint32 animSize = stream.readUint32BE();
@@ -300,9 +300,9 @@ void InsaneRebel2::loadEmbeddedSan(int userId, byte *animData, int32 size, byte
 	if ((int64)animSize <= streamEnd - 8) {
 		animEnd = 8 + (int64)animSize;
 	} else {
-		debugC(DEBUG_INSANE, "Rebel2: Embedded ANIM truncated: reported size=%u, actual=%lld", animSize, streamEnd - 8);
+		debugC(DEBUG_INSANE, "Embedded ANIM truncated: reported size=%u, actual=%lld", animSize, streamEnd - 8);
 	}
-	debugC(DEBUG_INSANE, "Rebel2: Parsing embedded ANIM: userId=%d, reported size=%u, actual=%lld", userId, animSize, streamEnd - 8);
+	debugC(DEBUG_INSANE, "Parsing embedded ANIM: userId=%d, reported size=%u, actual=%lld", userId, animSize, streamEnd - 8);
 
 	// Iterate through chunks to find FRME -> FOBJ
 	while (!stream.eos() && stream.pos() + 8 <= animEnd) {
@@ -325,7 +325,7 @@ void InsaneRebel2::loadEmbeddedSan(int userId, byte *animData, int32 size, byte
 
 				if (subTag == MKTAG('F','O','B','J')) {
 					if (subSize < 14) {
-						debugC(DEBUG_INSANE, "Rebel2: Embedded FOBJ too small: userId=%d, size=%u", userId, subSize);
+						debugC(DEBUG_INSANE, "Embedded FOBJ too small: userId=%d, size=%u", userId, subSize);
 						stream.seek(nextSubPos);
 						continue;
 					}
@@ -339,13 +339,13 @@ void InsaneRebel2::loadEmbeddedSan(int userId, byte *animData, int32 size, byte
 					stream.readUint16LE();  // unknown
 					stream.readUint16LE();  // unknown
 
-					debugC(DEBUG_INSANE, "Rebel2: Embedded HUD frame: userId=%d, %dx%d at (%d,%d), codec=%d",
+					debugC(DEBUG_INSANE, "Embedded HUD frame: userId=%d, %dx%d at (%d,%d), codec=%d",
 						userId, width, height, left, top, codec);
 
 					// High-resolution HUD frames are used when the RA2 high-res option
 					// selects a 640x400 virtual screen. Keep skipping them in low-res mode.
 					if (!isHiRes() && (width > 400 || height > 250)) {
-						debugC(DEBUG_INSANE, "Rebel2: SKIPPING high-res embedded frame: userId=%d, %dx%d (exceeds 400x250)",
+						debugC(DEBUG_INSANE, "SKIPPING high-res embedded frame: userId=%d, %dx%d (exceeds 400x250)",
 							userId, width, height);
 						stream.seek(nextSubPos);
 						continue;
@@ -383,7 +383,7 @@ void InsaneRebel2::loadEmbeddedSan(int userId, byte *animData, int32 size, byte
 							}
 							uint32 bytesRead = stream.read(fobjData, dataSize);
 							if (bytesRead != (uint32)dataSize) {
-								debugC(DEBUG_INSANE, "Rebel2: Short embedded FOBJ read: got %u of %d bytes", bytesRead, dataSize);
+								debugC(DEBUG_INSANE, "Short embedded FOBJ read: got %u of %d bytes", bytesRead, dataSize);
 								free(fobjData);
 								return;
 							}
@@ -393,17 +393,17 @@ void InsaneRebel2::loadEmbeddedSan(int userId, byte *animData, int32 size, byte
 								// Codec 1/3: RLE - use existing decoder (FUN_0042C590)
 								smushDecodeRLE(frame.pixels, fobjData, 0, 0, width, height, width);
 								frame.valid = true;
-								debugC(DEBUG_INSANE, "Rebel2: Decoded embedded HUD (codec %d/RLE): %dx%d", codec, width, height);
+								debugC(DEBUG_INSANE, "Decoded embedded HUD (codec %d/RLE): %dx%d", codec, width, height);
 							} else if (codec == 20) {
 								// Codec 20: Uncompressed (FUN_0042C400)
 								smushDecodeUncompressed(frame.pixels, fobjData, 0, 0, width, height, width);
 								frame.valid = true;
-								debugC(DEBUG_INSANE, "Rebel2: Decoded embedded HUD (codec 20/raw): %dx%d", width, height);
+								debugC(DEBUG_INSANE, "Decoded embedded HUD (codec 20/raw): %dx%d", width, height);
 							} else if (codec == 21 || codec == 44) {
 								// Codec 21/44: Line update (FUN_0042BD60)
 								smushDecodeLineUpdate(frame.pixels, fobjData, 0, 0, width, height, width, dataSize);
 								frame.valid = true;
-								debugC(DEBUG_INSANE, "Rebel2: Decoded embedded HUD (codec %d/line update): %dx%d", codec, width, height);
+								debugC(DEBUG_INSANE, "Decoded embedded HUD (codec %d/line update): %dx%d", codec, width, height);
 							} else if (codec == 45) {
 								// Codec 45: blur/wipe mask (FUN_0042B460 -> FUN_0042B530 -> FUN_0042DDF0)
 								smushDecodeRA2Blur(frame.pixels, fobjData, 0, 0, width, height, width, dataSize,
@@ -413,16 +413,16 @@ void InsaneRebel2::loadEmbeddedSan(int userId, byte *animData, int32 size, byte
 								// Codec 23: Skip/copy with embedded RLE (FUN_0042BBF0)
 								smushDecodeSkipRLE(frame.pixels, fobjData, 0, 0, width, height, width, dataSize);
 								frame.valid = true;
-								debugC(DEBUG_INSANE, "Rebel2: Decoded embedded HUD (codec 23/skip-RLE): %dx%d", width, height);
+								debugC(DEBUG_INSANE, "Decoded embedded HUD (codec 23/skip-RLE): %dx%d", width, height);
 							} else {
-								debugC(DEBUG_INSANE, "Rebel2: Unsupported embedded HUD codec %d", codec);
+								debugC(DEBUG_INSANE, "Unsupported embedded HUD codec %d", codec);
 								frame.valid = false;
 							}
 
 							// Count non-zero pixels to verify frame has content
 							if (frame.valid) {
 								int nonZeroPixels = countEmbeddedFramePixels(frame);
-								debugC(DEBUG_INSANE, "Rebel2: Frame userId=%d has %d non-zero pixels (%d%%)",
+								debugC(DEBUG_INSANE, "Frame userId=%d has %d non-zero pixels (%d%%)",
 									userId, nonZeroPixels, (nonZeroPixels * 100) / (width * height));
 							}
 
@@ -447,7 +447,7 @@ void InsaneRebel2::loadEmbeddedSan(int userId, byte *animData, int32 size, byte
 		}
 	}
 
-	debugC(DEBUG_INSANE, "Rebel2: No FOBJ found in embedded SAN userId=%d", userId);
+	debugC(DEBUG_INSANE, "No FOBJ found in embedded SAN userId=%d", userId);
 }
 
 // Spawn explosion into the shared 5-slot system.
@@ -611,7 +611,7 @@ void InsaneRebel2::spawnHandler25Shot(int x, int y) {
 				_turretShots[i].gunY = _rebelViewOffset2Y + 140 + _viewY;
 			}
 
-			debugC(DEBUG_INSANE, "Rebel2 Handler25: Spawned shot %d target (%d,%d) gun (%d,%d)",
+			debugC(DEBUG_INSANE, "Handler25: Spawned shot %d target (%d,%d) gun (%d,%d)",
 				i, _turretShots[i].targetX, _turretShots[i].targetY,
 				_turretShots[i].gunX, _turretShots[i].gunY);
 			break;
@@ -1147,7 +1147,7 @@ void InsaneRebel2::initLaserTexture(NutRenderer *nut, int spriteIdx) {
 		}
 	}
 
-	debugC(DEBUG_INSANE, "Rebel2: Initialized laser texture %dx%d from sprite %d (xoff=%d yoff=%d src=%dx%d)",
+	debugC(DEBUG_INSANE, "Initialized laser texture %dx%d from sprite %d (xoff=%d yoff=%d src=%dx%d)",
 	      texWidth, texHeight, spriteIdx, srcXOff, srcYOff, srcWidth, srcHeight);
 }
 
@@ -1514,7 +1514,7 @@ void InsaneRebel2::drawLaserBeam(byte *dst, int pitch, int width, int height,
 	int edgeClipRight = CLIP<int>(clipRight - 1, 1, width - 2);
 	int edgeClipBottom = CLIP<int>(clipBottom - 1, 1, height - 2);
 
-	debugC(DEBUG_INSANE, "Rebel2: drawLaserBeam gun(%d,%d) tgt(%d,%d) start(%d,%d) end(%d,%d) anim=%d/%d ws=%d hs=%d th=%d",
+	debugC(DEBUG_INSANE, "drawLaserBeam gun(%d,%d) tgt(%d,%d) start(%d,%d) end(%d,%d) anim=%d/%d ws=%d hs=%d th=%d",
 		gunX, gunY, targetX, targetY, startX, startY, endX, endY,
 		animFrame, maxFrames, widthScale, heightScale, thickness);
 
@@ -1660,16 +1660,16 @@ void InsaneRebel2::registerCollisionZone(Common::SeekableReadStream &b, int16 su
 	// Register zone into appropriate table based on sub-opcode
 	if (subOpcode == 0x0D && _primaryZoneCount < kMaxCollisionZones) {
 		_primaryZones[_primaryZoneCount++] = zone;
-		debugC(DEBUG_INSANE, "Rebel2: Registered PRIMARY zone %d: filter=%d fields=[%d,%d] quad=(%d,%d)-(%d,%d)-(%d,%d)-(%d,%d)",
+		debugC(DEBUG_INSANE, "Registered PRIMARY zone %d: filter=%d fields=[%d,%d] quad=(%d,%d)-(%d,%d)-(%d,%d)-(%d,%d)",
 			_primaryZoneCount - 1, par4, field1, field2,
 			x1, y1, x2, y2, x3, y3, x4, y4);
 	} else if (subOpcode == 0x0E && _secondaryZoneCount < kMaxCollisionZones) {
 		_secondaryZones[_secondaryZoneCount++] = zone;
-		debugC(DEBUG_INSANE, "Rebel2: Registered SECONDARY zone %d: filter=%d fields=[%d,%d] quad=(%d,%d)-(%d,%d)-(%d,%d)-(%d,%d)",
+		debugC(DEBUG_INSANE, "Registered SECONDARY zone %d: filter=%d fields=[%d,%d] quad=(%d,%d)-(%d,%d)-(%d,%d)-(%d,%d)",
 			_secondaryZoneCount - 1, par4, field1, field2,
 			x1, y1, x2, y2, x3, y3, x4, y4);
 	} else {
-		debugC(DEBUG_INSANE, "Rebel2: WARNING - Could not register zone (subOpcode=%d, primary=%d, secondary=%d)",
+		debugC(DEBUG_INSANE, "WARNING - Could not register zone (subOpcode=%d, primary=%d, secondary=%d)",
 			subOpcode, _primaryZoneCount, _secondaryZoneCount);
 	}
 }
@@ -1793,7 +1793,7 @@ void InsaneRebel2::checkCollisionZones(byte *renderBitmap, int pitch, int width,
 				int collisionDamage = (dparams.dodgeDamage >= 0) ? dparams.dodgeDamage : 0;
 
 				if (applyPlayerDamage(collisionDamage)) {
-					debugC(DEBUG_INSANE, "Rebel2: COLLISION damage! zone=%d aim=(%d,%d) damage=%d total=%d",
+					debugC(DEBUG_INSANE, "COLLISION damage! zone=%d aim=(%d,%d) damage=%d total=%d",
 						i, aimX, aimY, collisionDamage, _playerDamage);
 				}
 				// Visual effect ??? FUN_00420515 (palette flash)
@@ -1899,7 +1899,7 @@ void InsaneRebel2::applyHandler7ObstacleHit(const InsaneRebel2::CollisionZone &z
 		initDamageFlash();
 	// Pan based on ship X position relative to screen center.
 	playSfx(1, 127, CLIP((_flyShipScreenX - 212) * 127 / 160, -127, 127));
-	debugC(DEBUG_INSANE, "Rebel2: Handler7 Mode0/2 OBSTACLE HIT zone=%d ship=(%d,%d) damage=%d",
+	debugC(DEBUG_INSANE, "Handler7 Mode0/2 OBSTACLE HIT zone=%d ship=(%d,%d) damage=%d",
 		zoneIndex, _flyShipScreenX, _flyShipScreenY, collisionDamage);
 }
 
@@ -1978,7 +1978,7 @@ void InsaneRebel2::checkHandler7TopBoundary(const InsaneRebel2::CollisionZone &z
 			// Ship above top wall - push down.
 			const bool damageApplied = applyHandler7WallDamage(wallDamage);
 			if (damageApplied) {
-				debugC(DEBUG_INSANE, "Rebel2: Handler7 Mode1/3 TOP WALL ship=(%d,%d) edgeY=%d damage=%d",
+				debugC(DEBUG_INSANE, "Handler7 Mode1/3 TOP WALL ship=(%d,%d) edgeY=%d damage=%d",
 					_flyShipScreenX, _flyShipScreenY, edgeY, wallDamage);
 			}
 			_spaceShotDirection = 2;  // Direction: pushed down
@@ -2004,7 +2004,7 @@ void InsaneRebel2::checkHandler7BottomBoundary(const InsaneRebel2::CollisionZone
 			// Ship below bottom wall - push up.
 			const bool damageApplied = applyHandler7WallDamage(wallDamage);
 			if (damageApplied) {
-				debugC(DEBUG_INSANE, "Rebel2: Handler7 Mode1/3 BOTTOM WALL ship=(%d,%d) edgeY=%d damage=%d",
+				debugC(DEBUG_INSANE, "Handler7 Mode1/3 BOTTOM WALL ship=(%d,%d) edgeY=%d damage=%d",
 					_flyShipScreenX, _flyShipScreenY, edgeY, wallDamage);
 			}
 			_spaceShotDirection = 3;  // Direction: pushed up
@@ -2034,7 +2034,7 @@ void InsaneRebel2::checkHandler7LeftBoundary(const InsaneRebel2::CollisionZone &
 
 			const bool damageApplied = applyHandler7WallDamage(wallDamage);
 			if (damageApplied) {
-				debugC(DEBUG_INSANE, "Rebel2: Handler7 Mode1/3 LEFT WALL ship=(%d,%d) edgeX=%d damage=%d",
+				debugC(DEBUG_INSANE, "Handler7 Mode1/3 LEFT WALL ship=(%d,%d) edgeX=%d damage=%d",
 					_flyShipScreenX, _flyShipScreenY, edgeX, wallDamage);
 			}
 			_spaceShotDirection = 0;  // Direction: pushed right
@@ -2061,7 +2061,7 @@ void InsaneRebel2::checkHandler7RightBoundary(const InsaneRebel2::CollisionZone
 
 			const bool damageApplied = applyHandler7WallDamage(wallDamage);
 			if (damageApplied) {
-				debugC(DEBUG_INSANE, "Rebel2: Handler7 Mode1/3 RIGHT WALL ship=(%d,%d) edgeX=%d damage=%d",
+				debugC(DEBUG_INSANE, "Handler7 Mode1/3 RIGHT WALL ship=(%d,%d) edgeX=%d damage=%d",
 					_flyShipScreenX, _flyShipScreenY, edgeX, wallDamage);
 			}
 			_spaceShotDirection = 1;  // Direction: pushed left
@@ -2454,7 +2454,7 @@ bool InsaneRebel2::handlePostRenderMenuModes(byte *renderBitmap, int pitch, int
 
 		// If a selection was confirmed, signal video to stop.
 		if (selection >= 0) {
-			debugC(DEBUG_INSANE, "Rebel2: Pilot selection confirmed: %d", selection);
+			debugC(DEBUG_INSANE, "Pilot selection confirmed: %d", selection);
 			_menuSelectionConfirmed = true;
 			_vm->_smushVideoShouldFinish = true;
 		}
@@ -2479,7 +2479,7 @@ bool InsaneRebel2::handlePostRenderMenuModes(byte *renderBitmap, int pitch, int
 
 		// If a selection was confirmed, signal video to stop.
 		if (selection >= 0) {
-			debugC(DEBUG_INSANE, "Rebel2: Chapter selection confirmed: %d", selection);
+			debugC(DEBUG_INSANE, "Chapter selection confirmed: %d", selection);
 			_menuSelectionConfirmed = true;
 			_vm->_smushVideoShouldFinish = true;
 		}
@@ -2515,7 +2515,7 @@ bool InsaneRebel2::handlePostRenderMenuModes(byte *renderBitmap, int pitch, int
 		// At 12fps video rate, 300 frames = ~25 seconds of inactivity.
 		// The original checks: if (local_8 > 299) return 0.
 		if (_menuInactivityTimer > 300) {
-			debugC(DEBUG_INSANE, "Rebel2: Menu inactivity timeout - resuming intro/demo loop");
+			debugC(DEBUG_INSANE, "Menu inactivity timeout - resuming intro/demo loop");
 			_menuInactivityTimer = 0;
 			_menuInactivityTimedOut = true;
 			_menuSelectionConfirmed = false;
@@ -2528,7 +2528,7 @@ bool InsaneRebel2::handlePostRenderMenuModes(byte *renderBitmap, int pitch, int
 
 		// If a selection was confirmed, signal video to stop.
 		if (selection >= 0) {
-			debugC(DEBUG_INSANE, "Rebel2: Menu selection confirmed: %d", selection);
+			debugC(DEBUG_INSANE, "Menu selection confirmed: %d", selection);
 			_menuSelectionConfirmed = true;
 			_vm->_smushVideoShouldFinish = true;
 		}
@@ -2559,7 +2559,7 @@ bool InsaneRebel2::handlePostRenderIntro(byte *renderBitmap, int pitch, int widt
 		// Track state transition for debugging.
 		if (!_introCursorPushed) {
 			_introCursorPushed = true;
-			debugC(DEBUG_INSANE, "Rebel2: Intro/cinematic mode (handler=0, flags=0x%x, state=%d) - HUD disabled, mouse hidden",
+			debugC(DEBUG_INSANE, "Intro/cinematic mode (handler=0, flags=0x%x, state=%d) - HUD disabled, mouse hidden",
 				  _player->_curVideoFlags, _gameState);
 		}
 
@@ -2574,7 +2574,7 @@ bool InsaneRebel2::handlePostRenderIntro(byte *renderBitmap, int pitch, int widt
 	// Gameplay mode - handler was set by IACT opcode 6.
 	if (_introCursorPushed) {
 		_introCursorPushed = false;
-		debugC(DEBUG_INSANE, "Rebel2: Gameplay mode (handler=%d, flags=0x%x, state=%d) - HUD enabled",
+		debugC(DEBUG_INSANE, "Gameplay mode (handler=%d, flags=0x%x, state=%d) - HUD enabled",
 			  _rebelHandler, _player->_curVideoFlags, _gameState);
 	}
 
@@ -2694,7 +2694,7 @@ void InsaneRebel2::renderGameplayPostFrame(byte *renderBitmap, int pitch, int wi
 	renderStatusBarBackground(renderBitmap, pitch, width, height, videoWidth, videoHeight, statusBarY);
 
 	// Ship rendering (FUN_00401ccf for Handler 8, FUN_0040d836 for Handler 7).
-	debugC(DEBUG_INSANE, "Rebel2 Ship Check: handler=%d shipSprite=%p flyShipSprite=%p shipLevelMode=%d numSprites=%d/%d",
+	debugC(DEBUG_INSANE, "Ship Check: handler=%d shipSprite=%p flyShipSprite=%p shipLevelMode=%d numSprites=%d/%d",
 		_rebelHandler, (void*)_shipSprite, (void*)_flyShipSprite, _shipLevelMode,
 		_shipSprite ? _shipSprite->getNumChars() : 0,
 		_flyShipSprite ? _flyShipSprite->getNumChars() : 0);
@@ -2974,7 +2974,7 @@ void InsaneRebel2::renderTextOverlay(byte *renderBitmap, int pitch, int width, i
 
 	SmushPlayer *splayer = ((ScummEngine_v7 *)_vm)->_splayer;
 	const char *text = splayer->getString(_textOverlayID);
-	debugC(DEBUG_INSANE, "Rebel2: Text overlay frame %d/%d-%d textID=0x%x text='%s'",
+	debugC(DEBUG_INSANE, "Text overlay frame %d/%d-%d textID=0x%x text='%s'",
 	      curFrame, _textOverlayFadeIn, _textOverlayFadeOut, _textOverlayID,
 	      text ? text : "(null)");
 	if (!text)
@@ -3171,7 +3171,7 @@ void InsaneRebel2::renderTurretHudOverlays(byte *renderBitmap, int pitch, int wi
 			_hudOverlayNut, animFrame);
 	}
 
-	debugC(DEBUG_INSANE, "Rebel2 HUD: Drawing NUT overlay frame %d/%d at (%d,%d) mouseOffset=(%d,%d)",
+	debugC(DEBUG_INSANE, "HUD: Drawing NUT overlay frame %d/%d at (%d,%d) mouseOffset=(%d,%d)",
 		  animFrame, numSprites, hudX, hudY, mouseOffsetX, mouseOffsetY);
 
 	// Draw secondary HUD overlay if present (DAT_0047fe80)
@@ -3204,7 +3204,7 @@ void InsaneRebel2::renderEmbeddedHudOverlays(byte *renderBitmap, int pitch, int
 
 		// Skip small frames at (0,0) - likely animation patches
 		if (frame.renderX == 0 && frame.renderY == 0 && frame.width < 50 && frame.height < 60) {
-			debugC(DEBUG_INSANE, "Rebel2: Skipping small embedded frame at (0,0): slot=%d size=%dx%d",
+			debugC(DEBUG_INSANE, "Skipping small embedded frame at (0,0): slot=%d size=%dx%d",
 				hudSlot, frame.width, frame.height);
 			continue;
 		}
@@ -3268,7 +3268,7 @@ void InsaneRebel2::renderEmbeddedHudOverlays(byte *renderBitmap, int pitch, int
 		destX += _viewX;
 		destY += _viewY;
 
-		debugC(DEBUG_INSANE, "Rebel2: Rendering embedded HUD slot=%d size=%dx%d at (%d,%d)",
+		debugC(DEBUG_INSANE, "Rendering embedded HUD slot=%d size=%dx%d at (%d,%d)",
 			hudSlot, frame.width, frame.height, destX, destY);
 
 		blitEmbeddedFrameRegion(renderBitmap, pitch, pitch, height, frame,
@@ -3560,7 +3560,7 @@ void InsaneRebel2::renderHandler7Ship(byte *renderBitmap, int pitch, int width,
 			shipDraw.x, shipDraw.y, _flyTargetSprite, spriteIndex);
 	}
 
-	debugC(DEBUG_INSANE, "Rebel2 Handler7Ship: draw=(%d,%d) sprite=%d/%d shipPos=(%d,%d) persp=(%d,%d) smoothVel=%d vertIn=%d fxCtr=%d fxRep=%d",
+	debugC(DEBUG_INSANE, "Handler7Ship: draw=(%d,%d) sprite=%d/%d shipPos=(%d,%d) persp=(%d,%d) smoothVel=%d vertIn=%d fxCtr=%d fxRep=%d",
 		drawX, drawY, spriteIndex, numSprites, _flyShipScreenX, _flyShipScreenY,
 		_perspectiveX, _perspectiveY, _smoothedVelocity, _verticalInput, _flyEffectAnimCounter, _flyOverlayRepeatCount);
 }
@@ -3618,7 +3618,7 @@ void InsaneRebel2::renderHandler8Ship(byte *renderBitmap, int pitch, int width,
 
 	int sprW = _shipSprite->getCharWidth(spriteIndex);
 	int sprH = _shipSprite->getCharHeight(spriteIndex);
-	debugC(DEBUG_INSANE, "Rebel2 Handler8: Ship at (%d,%d) raw(%d,%d) offset(%d,%d) nutOff(%d,%d) size(%d,%d) bottom=%d view(%d,%d) sprite=%d/%d",
+	debugC(DEBUG_INSANE, "Handler8: Ship at (%d,%d) raw(%d,%d) offset(%d,%d) nutOff(%d,%d) size(%d,%d) bottom=%d view(%d,%d) sprite=%d/%d",
 		drawX, drawY, _shipPosX, _shipPosY, displayOffsetX, displayOffsetY,
 		spriteXOffset, spriteYOffset, sprW, sprH, drawY + sprH,
 		_viewX, _viewY, spriteIndex, numSprites);
@@ -3749,7 +3749,7 @@ void InsaneRebel2::renderHandler25ShipPre(byte *renderBitmap, int pitch, int wid
 			scaledClipLeft, 0, scaledClipRight, renderHeight,
 			drawX, drawY, _grd001Sprite, 0, false, renderScale, false);
 
-		debugC(DEBUG_INSANE, "Rebel2 Handler25 PRE: GRD001 at (%d,%d) nutOff(%d,%d) viewOff(%d,%d) size(%d,%d) mode=%d dmg=%d halfW=%d rightHalf=%d clip=[%d,%d) scale=%d",
+		debugC(DEBUG_INSANE, "Handler25 PRE: GRD001 at (%d,%d) nutOff(%d,%d) viewOff(%d,%d) size(%d,%d) mode=%d dmg=%d halfW=%d rightHalf=%d clip=[%d,%d) scale=%d",
 			drawX, drawY, spriteXOffset, spriteYOffset, _rebelViewOffset2X, _rebelViewOffset2Y,
 			spriteW, spriteH, _grdSpriteMode, _rebelDamageLevel, useHalfWidth ? 1 : 0, useRightHalf ? 1 : 0, scaledClipLeft, scaledClipRight, renderScale);
 
@@ -3769,7 +3769,7 @@ void InsaneRebel2::renderHandler25ShipPre(byte *renderBitmap, int pitch, int wid
 				0, 0, width, renderHeight,
 				overlayDrawX, overlayDrawY, _grd005Sprite, overlayIdx, false, renderScale, false);
 
-			debugC(DEBUG_INSANE, "Rebel2 Handler25 PRE: GRD005 at (%d,%d) nutOff(%d,%d) viewOff(%d,%d) size(%d,%d) mode=%d scale=%d",
+			debugC(DEBUG_INSANE, "Handler25 PRE: GRD005 at (%d,%d) nutOff(%d,%d) viewOff(%d,%d) size(%d,%d) mode=%d scale=%d",
 				overlayDrawX, overlayDrawY, overlayXOffset, overlayYOffset,
 				_rebelViewOffset2X, _rebelViewOffset2Y, overlayW, overlayH, _grdSpriteMode, renderScale);
 		}
@@ -3924,7 +3924,7 @@ void InsaneRebel2::renderHandler25Ship(byte *renderBitmap, int pitch, int width,
 			0, 0, width, renderHeight,
 			drawX, drawY, _grd002Sprite, spriteIdx, shouldMirror, renderScale, true);
 
-		debugC(DEBUG_INSANE, "Rebel2 Handler25: GRD002 at (%d,%d) nutOffset(%d,%d) viewOffset(%d,%d) size(%d,%d) spriteIdx=%d damage=%d dir=%d mirror=%d scale=%d",
+		debugC(DEBUG_INSANE, "Handler25: GRD002 at (%d,%d) nutOffset(%d,%d) viewOffset(%d,%d) size(%d,%d) spriteIdx=%d damage=%d dir=%d mirror=%d scale=%d",
 			drawX, drawY, spriteXOffset, spriteYOffset, _rebelViewOffset2X, _rebelViewOffset2Y, spriteW, spriteH, spriteIdx, _rebelDamageLevel, _rebelFlightDir, shouldMirror ? 1 : 0, renderScale);
 	}
 }
@@ -3979,7 +3979,7 @@ void InsaneRebel2::renderFallbackShip(byte *renderBitmap, int pitch, int width,
 	blitEmbeddedFrameRegion(renderBitmap, pitch, width, height, shipFrame,
 		drawX, drawY, srcX, srcY, spriteW, spriteH);
 
-	debugC(DEBUG_INSANE, "Rebel2: Ship (fallback) at (%d,%d) strip=(%d,%d) of (%dx%d) dir=(%d,%d)",
+	debugC(DEBUG_INSANE, "Ship (fallback) at (%d,%d) strip=(%d,%d) of (%dx%d) dir=(%d,%d)",
 		drawX, drawY, srcX, srcY, numHorizontal, numVertical, _shipDirectionH, _shipDirectionV);
 }
 
@@ -4277,7 +4277,7 @@ void InsaneRebel2::renderSpaceExplosions(byte *renderBitmap, int pitch, int widt
 					drawX - ew / 2, drawY - eh / 2, _smush_iconsNut, spriteIndex);
 			}
 
-			debugC(DEBUG_INSANE, "Rebel2 H7 corridor explosion: dir=%d frame=%d pos=(%d,%d) cooldown=%d",
+			debugC(DEBUG_INSANE, "H7 corridor explosion: dir=%d frame=%d pos=(%d,%d) cooldown=%d",
 				_spaceShotDirection, spriteIndex, drawX, drawY, _hitCooldown);
 		}
 	}
@@ -4573,7 +4573,7 @@ void InsaneRebel2::renderHandler25LaserShots(byte *renderBitmap, int pitch, int
 
 		_turretShots[i].counter--;
 
-		debugC(DEBUG_INSANE, "Rebel2 Handler25: Laser shot %d from (%d,%d) to (%d,%d) progress=%d/%d",
+		debugC(DEBUG_INSANE, "Handler25: Laser shot %d from (%d,%d) to (%d,%d) progress=%d/%d",
 			i, gunX, gunY, targetX, targetY, progress, maxDuration);
 	}
 }
diff --git a/engines/scumm/insane/rebel2/runlevels.cpp b/engines/scumm/insane/rebel2/runlevels.cpp
index c81b5626fc7..b0e9a677f18 100644
--- a/engines/scumm/insane/rebel2/runlevels.cpp
+++ b/engines/scumm/insane/rebel2/runlevels.cpp
@@ -57,14 +57,14 @@ int InsaneRebel2::runLevel1() {
 
 		if (_playerShield > 0) {
 			// Level completed!
-			debugC(DEBUG_INSANE, "Rebel2: Level 1 completed!");
+			debugC(DEBUG_INSANE, "Level 1 completed!");
 			playLevelEnd(1);
 			_levelUnlocked[1] = true;  // Unlock level 2
 			return kLevelNextLevel;
 		}
 
 		// Player died - play death video with random A/B variant
-		debugC(DEBUG_INSANE, "Rebel2: Level 1 death at frame %d, lives=%d", _deathFrame, _playerLives - 1);
+		debugC(DEBUG_INSANE, "Level 1 death at frame %d, lives=%d", _deathFrame, _playerLives - 1);
 		playLevelDeathVariant(1, 1, _deathFrame);
 
 		if (_vm->shouldQuit())
@@ -104,7 +104,7 @@ InsaneRebel2::WaveEndResult InsaneRebel2::processWaveEnd(int16 mask, int16 *budg
 		_skipSectionRequested = false;
 		_rebelPhaseState = mask;
 		_rebelWaveState = mask;
-		debugC(DEBUG_INSANE, "Rebel2 processWaveEnd: movie mode completed gameplay wave (mask=0x%x)", (uint16)mask);
+		debugC(DEBUG_INSANE, "processWaveEnd: movie mode completed gameplay wave (mask=0x%x)", (uint16)mask);
 		result.completed = true;
 		result.skipped = true;
 		return result;
@@ -115,7 +115,7 @@ InsaneRebel2::WaveEndResult InsaneRebel2::processWaveEnd(int16 mask, int16 *budg
 		_skipSectionRequested = false;
 		_rebelPhaseState = mask;
 		_rebelWaveState = mask;
-		debugC(DEBUG_INSANE, "Rebel2 processWaveEnd: Shift+S skip consumed (mask=0x%x)", (uint16)mask);
+		debugC(DEBUG_INSANE, "processWaveEnd: Shift+S skip consumed (mask=0x%x)", (uint16)mask);
 		result.completed = true;
 		result.skipped = true;
 		return result;
@@ -133,7 +133,7 @@ InsaneRebel2::WaveEndResult InsaneRebel2::processWaveEnd(int16 mask, int16 *budg
 	// Step 2: Copy wave state to phase state (line 33)
 	// DAT_0047ab9c = DAT_0047ab98
 	_rebelPhaseState = _rebelWaveState;
-	debugC(DEBUG_INSANE, "Rebel2 processWaveEnd: waveState=0x%x -> phaseState=0x%x mask=0x%x budget=%d threshold=%d flags=%d",
+	debugC(DEBUG_INSANE, "processWaveEnd: waveState=0x%x -> phaseState=0x%x mask=0x%x budget=%d threshold=%d flags=%d",
 		_rebelWaveState, _rebelPhaseState, mask, budget ? *budget : -1, threshold, flags);
 
 	// Step 3: Kill redistribution - add random unkilled types (lines 34-47)
@@ -187,7 +187,7 @@ InsaneRebel2::WaveEndResult InsaneRebel2::processWaveEnd(int16 mask, int16 *budg
 		creditCount++;
 	}
 
-	debugC(DEBUG_INSANE, "Rebel2 processWaveEnd: result=0x%x phaseState=0x%x (after redistribution) budget=%d",
+	debugC(DEBUG_INSANE, "processWaveEnd: result=0x%x phaseState=0x%x (after redistribution) budget=%d",
 		result.creditedBits, _rebelPhaseState, budget ? *budget : -1);
 
 	// Step 5: Stop conditions (lines 74-78)
@@ -203,7 +203,7 @@ bool InsaneRebel2::playLevelSegment(const char *filename, uint16 flags, bool rec
 
 	const bool isRecordedGameplay = recordFrame && (flags & 0x08) != 0;
 	if (isRecordedGameplay && _rebelMovieMode) {
-		debugC(DEBUG_INSANE, "Rebel2: Movie mode skipping gameplay segment: %s", filename);
+		debugC(DEBUG_INSANE, "Movie mode skipping gameplay segment: %s", filename);
 		_gameplaySectionActive = false;
 		restoreIOSGamepadController();
 		if (recordFrame)
@@ -245,7 +245,7 @@ int InsaneRebel2::calculateAccuracy(int kills, int misses) const {
 
 bool InsaneRebel2::handleLevelDeath(int levelId, int phase,
 		const char *deathVideo, const char *retryVideo, int &levelResult) {
-	debugC(DEBUG_INSANE, "Rebel2: Level %d Phase %d death", levelId, phase);
+	debugC(DEBUG_INSANE, "Level %d Phase %d death", levelId, phase);
 	playCinematic(deathVideo);
 	if (_vm->shouldQuit()) {
 		levelResult = kLevelQuit;
@@ -426,7 +426,7 @@ int InsaneRebel2::runLevel2() {
 		int16 budget = kLevel2BudgetBase[0] + _vm->_rnd.getRandomNumber(2);
 
 		// Play A.SAN (background loader) ??? flags 0x28 (preserve buffer, gameplay mode)
-		debugC(DEBUG_INSANE, "Rebel2: Level 2 Phase 1 - playing 02P01_A.SAN (background) budget=%d", budget);
+		debugC(DEBUG_INSANE, "Level 2 Phase 1 - playing 02P01_A.SAN (background) budget=%d", budget);
 		if (!playLevelSegment("LEV02/P1/02P01_A.SAN", 0x28))
 			return kLevelQuit;
 
@@ -446,14 +446,14 @@ int InsaneRebel2::runLevel2() {
 				"LEV02/P1/02P01_C.SAN",
 				"LEV02/P1/02P01_D.SAN"
 			};
-			debugC(DEBUG_INSANE, "Rebel2: Phase 1 wave - playing %s (state=0x%x budget=%d)", variants[variant], _rebelPhaseState, budget);
+			debugC(DEBUG_INSANE, "Phase 1 wave - playing %s (state=0x%x budget=%d)", variants[variant], _rebelPhaseState, budget);
 			// Wave videos use flags 0x428 (original: FUN_0041f4d0 param_2=0x428)
 			if (!playLevelSegment(variants[variant], 0x428))
 				return kLevelQuit;
 
 			// processWaveEnd with threshold=0x14 (20) ??? enables early exit when enemies killed
 			processWaveEnd(0x36, &budget, 0x14, 0);
-			debugC(DEBUG_INSANE, "Rebel2: Phase 1 wave done - state=0x%x (need 0x06) budget=%d", _rebelPhaseState, budget);
+			debugC(DEBUG_INSANE, "Phase 1 wave done - state=0x%x (need 0x06) budget=%d", _rebelPhaseState, budget);
 		}
 
 		// Check for bonus (bit 4 = 0x10)
@@ -492,7 +492,7 @@ int InsaneRebel2::runLevel2() {
 		_rebelHandler = 8;
 
 		// Play A.SAN (background loader)
-		debugC(DEBUG_INSANE, "Rebel2: Level 2 Phase 2 - playing 02P02_A.SAN (background) budget=%d", budget);
+		debugC(DEBUG_INSANE, "Level 2 Phase 2 - playing 02P02_A.SAN (background) budget=%d", budget);
 		if (!playLevelSegment("LEV02/P2/02P02_A.SAN", 0x28))
 			return kLevelQuit;
 
@@ -529,7 +529,7 @@ int InsaneRebel2::runLevel2() {
 				filename = "LEV02/P2/02P02_D.SAN"; break;
 			}
 
-			debugC(DEBUG_INSANE, "Rebel2: Phase 2 wave - playing %s (state=0x%x sel=0x%x budget=%d)", filename, _rebelPhaseState, waveSelect, budget);
+			debugC(DEBUG_INSANE, "Phase 2 wave - playing %s (state=0x%x sel=0x%x budget=%d)", filename, _rebelPhaseState, waveSelect, budget);
 			if (!playLevelSegment(filename, 0x428))
 				return kLevelQuit;
 		}
@@ -569,7 +569,7 @@ int InsaneRebel2::runLevel2() {
 		_rebelHandler = 8;
 
 		// Play A.SAN (background loader)
-		debugC(DEBUG_INSANE, "Rebel2: Level 2 Phase 3 - playing 02P03_A.SAN (background) budget=%d", budget);
+		debugC(DEBUG_INSANE, "Level 2 Phase 3 - playing 02P03_A.SAN (background) budget=%d", budget);
 		if (!playLevelSegment("LEV02/P3/02P03_A.SAN", 0x28))
 			return kLevelQuit;
 
@@ -614,13 +614,13 @@ int InsaneRebel2::runLevel2() {
 					filename = "LEV02/P3/02P03_I.SAN"; break;
 				}
 
-				debugC(DEBUG_INSANE, "Rebel2: Phase 3 wave - playing %s (state=0x%x sel=0x%x budget=%d)", filename, _rebelPhaseState, waveSelect, budget);
+				debugC(DEBUG_INSANE, "Phase 3 wave - playing %s (state=0x%x sel=0x%x budget=%d)", filename, _rebelPhaseState, waveSelect, budget);
 				if (!playLevelSegment(filename, 0x428))
 					return kLevelQuit;
 
 				// processWaveEnd at BOTTOM with threshold=0x14
 				waveEnd = processWaveEnd(0x3e, &budget, 0x14, 0);
-				debugC(DEBUG_INSANE, "Rebel2: Phase 3 wave done - state=0x%x (need 0x0e) budget=%d", _rebelPhaseState, budget);
+				debugC(DEBUG_INSANE, "Phase 3 wave done - state=0x%x (need 0x0e) budget=%d", _rebelPhaseState, budget);
 			}
 		}
 
@@ -643,7 +643,7 @@ int InsaneRebel2::runLevel2() {
 		{
 			totalMisses += _rebelHitCounter;
 			int accuracy = calculateAccuracy(totalKills, totalMisses);
-			debugC(DEBUG_INSANE, "Rebel2: Level 2 completed! kills=%d misses=%d accuracy=%d%% bonus=%d",
+			debugC(DEBUG_INSANE, "Level 2 completed! kills=%d misses=%d accuracy=%d%% bonus=%d",
 				totalKills, totalMisses, accuracy, bonusCount);
 		}
 
@@ -682,7 +682,7 @@ int InsaneRebel2::runLevel3() {
 		resetHandler7FlightState();
 
 		// Play phase 1 gameplay (03PLAY1.SAN)
-		debugC(DEBUG_INSANE, "Rebel2: Level 3 Phase 1");
+		debugC(DEBUG_INSANE, "Level 3 Phase 1");
 		if (!playLevelSegment("LEV03/03PLAY1.SAN", 0x28))
 			return kLevelQuit;
 
@@ -693,7 +693,7 @@ int InsaneRebel2::runLevel3() {
 		}
 
 		// Died in phase 1 - frame-based death video
-		debugC(DEBUG_INSANE, "Rebel2: Level 3 Phase 1 death at frame %d", _deathFrame);
+		debugC(DEBUG_INSANE, "Level 3 Phase 1 death at frame %d", _deathFrame);
 		playLevelDeathVariant(3, 1, _deathFrame);
 		if (_vm->shouldQuit())
 			return kLevelQuit;
@@ -736,20 +736,20 @@ int InsaneRebel2::runLevel3() {
 		resetHandler7FlightState();
 
 		// Play phase 2 gameplay (03PLAY2.SAN)
-		debugC(DEBUG_INSANE, "Rebel2: Level 3 Phase 2");
+		debugC(DEBUG_INSANE, "Level 3 Phase 2");
 		if (!playLevelSegment("LEV03/03PLAY2.SAN", 0x28))
 			return kLevelQuit;
 
 		if (_playerShield > 0) {
 			// Level completed!
-			debugC(DEBUG_INSANE, "Rebel2: Level 3 completed!");
+			debugC(DEBUG_INSANE, "Level 3 completed!");
 			playLevelEnd(3);
 			_levelUnlocked[3] = true;  // Unlock level 4
 			return kLevelNextLevel;
 		}
 
 		// Died in phase 2 - frame-based death video
-		debugC(DEBUG_INSANE, "Rebel2: Level 3 Phase 2 death at frame %d", _deathFrame);
+		debugC(DEBUG_INSANE, "Level 3 Phase 2 death at frame %d", _deathFrame);
 		playLevelDeathVariant(3, 2, _deathFrame);
 		if (_vm->shouldQuit())
 			return kLevelQuit;
@@ -796,20 +796,20 @@ int InsaneRebel2::runLevel4() {
 		clearBit(0);
 
 		// Play gameplay (04PLAY.SAN)
-		debugC(DEBUG_INSANE, "Rebel2: Level 4 gameplay");
+		debugC(DEBUG_INSANE, "Level 4 gameplay");
 		if (!playLevelSegment("LEV04/04PLAY.SAN", 0x28))
 			return kLevelQuit;
 
 		if (_playerShield > 0) {
 			// Level completed!
-			debugC(DEBUG_INSANE, "Rebel2: Level 4 completed!");
+			debugC(DEBUG_INSANE, "Level 4 completed!");
 			playLevelEnd(4);
 			_levelUnlocked[4] = true;  // Unlock level 5
 			return kLevelNextLevel;
 		}
 
 		// Died - play death video (04DIE.SAN, no variants)
-		debugC(DEBUG_INSANE, "Rebel2: Level 4 death");
+		debugC(DEBUG_INSANE, "Level 4 death");
 		playLevelDeathVariant(4, 1, _deathFrame);
 		if (_vm->shouldQuit())
 			return kLevelQuit;
@@ -851,20 +851,20 @@ int InsaneRebel2::runLevel5() {
 
 		// Play gameplay (05PLAY.SAN)
 		// Original: FUN_0041f4d0("05PLAY.SAN", 8, -1, -1, 0)
-		debugC(DEBUG_INSANE, "Rebel2: Level 5 gameplay");
+		debugC(DEBUG_INSANE, "Level 5 gameplay");
 		if (!playLevelSegment("LEV05/05PLAY.SAN", 0x08))
 			return kLevelQuit;
 
 		if (_playerShield > 0) {
 			// Level completed!
-			debugC(DEBUG_INSANE, "Rebel2: Level 5 completed!");
+			debugC(DEBUG_INSANE, "Level 5 completed!");
 			playLevelEnd(5);
 			_levelUnlocked[5] = true;  // Unlock level 6
 			return kLevelNextLevel;
 		}
 
 		// Died - play death video with random A/B variant
-		debugC(DEBUG_INSANE, "Rebel2: Level 5 death at frame %d", _deathFrame);
+		debugC(DEBUG_INSANE, "Level 5 death at frame %d", _deathFrame);
 		playLevelDeathVariant(5, 1, _deathFrame);
 		if (_vm->shouldQuit())
 			return kLevelQuit;
@@ -918,7 +918,7 @@ int InsaneRebel2::runLevel6() {
 		_rebelLevelType = 5;  // DAT_0047a7f8 = 5
 		_currentPhase = 1;
 
-		debugC(DEBUG_INSANE, "Rebel2: Level 6 Phase 1");
+		debugC(DEBUG_INSANE, "Level 6 Phase 1");
 		if (!playLevelSegment("LEV06/06PLAY1.SAN", 0x28))
 			return kLevelQuit;
 		// TODO: Mid-level switch at frame 0x2a8 to 06PLAY1B.SAN (flags 0x468)
@@ -926,7 +926,7 @@ int InsaneRebel2::runLevel6() {
 
 		if (_playerShield <= 0) {
 			// Died in phase 1
-			debugC(DEBUG_INSANE, "Rebel2: Level 6 Phase 1 death at frame %d", _deathFrame);
+			debugC(DEBUG_INSANE, "Level 6 Phase 1 death at frame %d", _deathFrame);
 			playLevelDeathVariant(6, 1, _deathFrame);
 			if (_vm->shouldQuit())
 				return kLevelQuit;
@@ -959,20 +959,20 @@ int InsaneRebel2::runLevel6() {
 			_playerScore = phase1Score;
 			clearBit(0);  // FUN_00407d10
 
-			debugC(DEBUG_INSANE, "Rebel2: Level 6 Phase 2");
+			debugC(DEBUG_INSANE, "Level 6 Phase 2");
 			if (!playLevelSegment("LEV06/06PLAY2.SAN", 0x28))
 				return kLevelQuit;
 
 			if (_playerShield > 0) {
 				// Level completed!
-				debugC(DEBUG_INSANE, "Rebel2: Level 6 completed!");
+				debugC(DEBUG_INSANE, "Level 6 completed!");
 				playLevelEnd(6);
 				_levelUnlocked[6] = true;
 				return kLevelNextLevel;
 			}
 
 			// Died in phase 2
-			debugC(DEBUG_INSANE, "Rebel2: Level 6 Phase 2 death at frame %d", _deathFrame);
+			debugC(DEBUG_INSANE, "Level 6 Phase 2 death at frame %d", _deathFrame);
 			playLevelDeathVariant(6, 2, _deathFrame);
 			if (_vm->shouldQuit())
 				return kLevelQuit;
@@ -1036,7 +1036,7 @@ int InsaneRebel2::runLevel7() {
 			return kLevelQuit;
 
 		if (_playerShield > 0) {
-			debugC(DEBUG_INSANE, "Rebel2: Level 7 completed!");
+			debugC(DEBUG_INSANE, "Level 7 completed!");
 			playLevelEnd(7);
 			_levelUnlocked[7] = true;
 			return kLevelNextLevel;
@@ -1044,7 +1044,7 @@ int InsaneRebel2::runLevel7() {
 
 		// Death video: DIE_B if fork reached, DIE_A if not
 		// Original: s_LEV07_07DIE_B + ((DAT_0047ab8c != 0) - 1 & 0x14)
-		debugC(DEBUG_INSANE, "Rebel2: Level 7 death at frame %d, fork=%d", _deathFrame, reachedFork);
+		debugC(DEBUG_INSANE, "Level 7 death at frame %d, fork=%d", _deathFrame, reachedFork);
 		if (reachedFork) {
 			playCinematic("LEV07/07DIE_B.SAN");
 		} else {
@@ -1098,7 +1098,7 @@ int InsaneRebel2::runLevel8() {
 
 		if (_playerShield > 0) {
 			int accuracy = calculateAccuracy(_rebelKillCounter, _rebelHitCounter);
-			debugC(DEBUG_INSANE, "Rebel2: Level 8 completed! accuracy=%d%%", accuracy);
+			debugC(DEBUG_INSANE, "Level 8 completed! accuracy=%d%%", accuracy);
 			playLevelEnd(8);
 			_levelUnlocked[8] = true;
 			return kLevelNextLevel;
@@ -1106,7 +1106,7 @@ int InsaneRebel2::runLevel8() {
 
 		// Death: random A or B
 		// Original: random(2) ??? A or B via string pointer arithmetic
-		debugC(DEBUG_INSANE, "Rebel2: Level 8 death at frame %d", _deathFrame);
+		debugC(DEBUG_INSANE, "Level 8 death at frame %d", _deathFrame);
 		playLevelDeathVariant(8, 1, _deathFrame);
 		if (_vm->shouldQuit())
 			return kLevelQuit;
@@ -1162,7 +1162,7 @@ int InsaneRebel2::runLevel9() {
 
 		if (_playerShield > 0) {
 			int accuracy = calculateAccuracy(_rebelKillCounter, _rebelHitCounter);
-			debugC(DEBUG_INSANE, "Rebel2: Level 9 completed! accuracy=%d%%", accuracy);
+			debugC(DEBUG_INSANE, "Level 9 completed! accuracy=%d%%", accuracy);
 			playLevelEnd(9);
 			_levelUnlocked[9] = true;
 			return kLevelNextLevel;
@@ -1170,7 +1170,7 @@ int InsaneRebel2::runLevel9() {
 
 		// Death: based on DAT_0047ab94 (death cause tracking)
 		// Original: 0???DIE_A, 1???DIE_C, else???DIE_B
-		debugC(DEBUG_INSANE, "Rebel2: Level 9 death at frame %d", _deathFrame);
+		debugC(DEBUG_INSANE, "Level 9 death at frame %d", _deathFrame);
 		playLevelDeathVariant(9, 1, _deathFrame);
 		if (_vm->shouldQuit())
 			return kLevelQuit;
@@ -1225,7 +1225,7 @@ int InsaneRebel2::runLevel10() {
 
 		if (_playerShield > 0) {
 			int accuracy = calculateAccuracy(_rebelKillCounter, _rebelHitCounter);
-			debugC(DEBUG_INSANE, "Rebel2: Level 10 completed! accuracy=%d%%", accuracy);
+			debugC(DEBUG_INSANE, "Level 10 completed! accuracy=%d%%", accuracy);
 			playLevelEnd(10);
 			_levelUnlocked[10] = true;
 			return kLevelNextLevel;
@@ -1233,7 +1233,7 @@ int InsaneRebel2::runLevel10() {
 
 		// Death + Retry: original plays both in sequence
 		// Original: lives--, if 0 break to game over, else DIE+RETRY
-		debugC(DEBUG_INSANE, "Rebel2: Level 10 death at frame %d", _deathFrame);
+		debugC(DEBUG_INSANE, "Level 10 death at frame %d", _deathFrame);
 		_playerLives--;
 		if (_playerLives <= 0) {
 			playLevelGameOver(10);
@@ -1297,7 +1297,7 @@ int InsaneRebel2::runLevel11() {
 		int16 budget = kLevel11BudgetBase[0] + _vm->_rnd.getRandomNumber(2);
 
 		// Play A.SAN (background loader)
-		debugC(DEBUG_INSANE, "Rebel2: Level 11 Phase 1 - playing 11P01_A.SAN budget=%d", budget);
+		debugC(DEBUG_INSANE, "Level 11 Phase 1 - playing 11P01_A.SAN budget=%d", budget);
 		if (!playLevelSegment("LEV11/P1/11P01_A.SAN", 0x28))
 			return kLevelQuit;
 
@@ -1327,7 +1327,7 @@ int InsaneRebel2::runLevel11() {
 				default: filename = "LEV11/P1/11P01_D.SAN"; break;  // sel == 0
 				}
 
-				debugC(DEBUG_INSANE, "Rebel2: Level 11 Phase 1 wave - %s (state=0x%x sel=%d)", filename, _rebelPhaseState, sel);
+				debugC(DEBUG_INSANE, "Level 11 Phase 1 wave - %s (state=0x%x sel=%d)", filename, _rebelPhaseState, sel);
 				if (!playLevelSegment(filename, 0x428))
 					return kLevelQuit;
 
@@ -1361,7 +1361,7 @@ int InsaneRebel2::runLevel11() {
 		_rebelHandler = 8;
 
 		// Play A.SAN (background loader)
-		debugC(DEBUG_INSANE, "Rebel2: Level 11 Phase 2 - playing 11P02_A.SAN budget=%d", budget);
+		debugC(DEBUG_INSANE, "Level 11 Phase 2 - playing 11P02_A.SAN budget=%d", budget);
 		if (!playLevelSegment("LEV11/P2/11P02_A.SAN", 0x28))
 			return kLevelQuit;
 
@@ -1383,7 +1383,7 @@ int InsaneRebel2::runLevel11() {
 				default: filename = "LEV11/P2/11P02_D.SAN"; break;
 				}
 
-				debugC(DEBUG_INSANE, "Rebel2: Level 11 Phase 2 wave - %s (state=0x%x)", filename, _rebelPhaseState);
+				debugC(DEBUG_INSANE, "Level 11 Phase 2 wave - %s (state=0x%x)", filename, _rebelPhaseState);
 				if (!playLevelSegment(filename, 0x428))
 					return kLevelQuit;
 
@@ -1418,7 +1418,7 @@ int InsaneRebel2::runLevel11() {
 		budget = kLevel11BudgetBase[2] + _vm->_rnd.getRandomNumber(2);
 		_rebelHandler = 8;
 
-		debugC(DEBUG_INSANE, "Rebel2: Level 11 Phase 3 first half - playing 11P03_A.SAN budget=%d", budget);
+		debugC(DEBUG_INSANE, "Level 11 Phase 3 first half - playing 11P03_A.SAN budget=%d", budget);
 		if (!playLevelSegment("LEV11/P3/11P03_A.SAN", 0x28))
 			return kLevelQuit;
 
@@ -1456,7 +1456,7 @@ int InsaneRebel2::runLevel11() {
 				default: filename = "LEV11/P3/11P03_E.SAN"; break;  // duplicate E
 				}
 
-				debugC(DEBUG_INSANE, "Rebel2: Level 11 Phase 3a wave - %s (state=0x%x variantIdx=%d)", filename, _rebelPhaseState, variantIdx);
+				debugC(DEBUG_INSANE, "Level 11 Phase 3a wave - %s (state=0x%x variantIdx=%d)", filename, _rebelPhaseState, variantIdx);
 				if (!playLevelSegment(filename, 0x428))
 					return kLevelQuit;
 
@@ -1508,7 +1508,7 @@ int InsaneRebel2::runLevel11() {
 		budget = kLevel11BudgetBase[3] + _vm->_rnd.getRandomNumber(2);
 
 		// Play G.SAN (background loader for second half)
-		debugC(DEBUG_INSANE, "Rebel2: Level 11 Phase 3 second half - playing 11P03_G.SAN budget=%d", budget);
+		debugC(DEBUG_INSANE, "Level 11 Phase 3 second half - playing 11P03_G.SAN budget=%d", budget);
 		if (!playLevelSegment("LEV11/P3/11P03_G.SAN", 0x28))
 			return kLevelQuit;
 
@@ -1541,7 +1541,7 @@ int InsaneRebel2::runLevel11() {
 				default: filename = "LEV11/P3/11P03_L.SAN"; break;
 				}
 
-				debugC(DEBUG_INSANE, "Rebel2: Level 11 Phase 3b wave - %s (state=0x%x variantIdx=%d)", filename, _rebelPhaseState, variantIdx);
+				debugC(DEBUG_INSANE, "Level 11 Phase 3b wave - %s (state=0x%x variantIdx=%d)", filename, _rebelPhaseState, variantIdx);
 				if (!playLevelSegment(filename, 0x428))
 					return kLevelQuit;
 
@@ -1565,7 +1565,7 @@ int InsaneRebel2::runLevel11() {
 		{
 			totalMisses += _rebelHitCounter;
 			int accuracy = calculateAccuracy(totalKills, totalMisses);
-			debugC(DEBUG_INSANE, "Rebel2: Level 11 completed! kills=%d misses=%d accuracy=%d%%",
+			debugC(DEBUG_INSANE, "Level 11 completed! kills=%d misses=%d accuracy=%d%%",
 				totalKills, totalMisses, accuracy);
 		}
 
@@ -1617,7 +1617,7 @@ int InsaneRebel2::runLevel12() {
 		int16 budget = kLevel12BudgetBase[0] + _vm->_rnd.getRandomNumber(2);
 
 		// Initialization video (12P05.SAN)
-		debugC(DEBUG_INSANE, "Rebel2: Level 12 Phase 1 - init 12P05.SAN budget=%d", budget);
+		debugC(DEBUG_INSANE, "Level 12 Phase 1 - init 12P05.SAN budget=%d", budget);
 		if (!playLevelSegment("LEV12/12P05.SAN", 0x28, false))
 			return kLevelQuit;
 		processWaveEnd(1, &budget, 0, 0);
@@ -1644,7 +1644,7 @@ int InsaneRebel2::runLevel12() {
 				default: filename = "LEV12/P1/12P01_B.SAN"; break;
 				}
 
-				debugC(DEBUG_INSANE, "Rebel2: Level 12 Phase 1 wave - %s (state=0x%x sel=%d)", filename, _rebelPhaseState, sel);
+				debugC(DEBUG_INSANE, "Level 12 Phase 1 wave - %s (state=0x%x sel=%d)", filename, _rebelPhaseState, sel);
 				if (!playLevelSegment(filename, 0x428))
 					return kLevelQuit;
 
@@ -1668,7 +1668,7 @@ int InsaneRebel2::runLevel12() {
 		budget = kLevel12BudgetBase[1] + _vm->_rnd.getRandomNumber(3);
 
 		// Initialization video (12P06.SAN)
-		debugC(DEBUG_INSANE, "Rebel2: Level 12 Phase 2 - init 12P06.SAN budget=%d", budget);
+		debugC(DEBUG_INSANE, "Level 12 Phase 2 - init 12P06.SAN budget=%d", budget);
 		if (!playLevelSegment("LEV12/12P06.SAN", 0x428, false))
 			return kLevelQuit;
 		processWaveEnd(1, &budget, 0, 0);
@@ -1704,7 +1704,7 @@ int InsaneRebel2::runLevel12() {
 				default: filename = "LEV12/P2/12P02_F.SAN"; break;
 				}
 
-				debugC(DEBUG_INSANE, "Rebel2: Level 12 Phase 2 wave - %s (state=0x%x variantIdx=%d)", filename, _rebelPhaseState, variantIdx);
+				debugC(DEBUG_INSANE, "Level 12 Phase 2 wave - %s (state=0x%x variantIdx=%d)", filename, _rebelPhaseState, variantIdx);
 				if (!playLevelSegment(filename, 0x428))
 					return kLevelQuit;
 
@@ -1730,7 +1730,7 @@ int InsaneRebel2::runLevel12() {
 		budget = kLevel12BudgetBase[2] + _vm->_rnd.getRandomNumber(3);
 
 		// Initialization video (12P07.SAN)
-		debugC(DEBUG_INSANE, "Rebel2: Level 12 Phase 3 - init 12P07.SAN budget=%d", budget);
+		debugC(DEBUG_INSANE, "Level 12 Phase 3 - init 12P07.SAN budget=%d", budget);
 		if (!playLevelSegment("LEV12/12P07.SAN", 0x428, false))
 			return kLevelQuit;
 		processWaveEnd(1, &budget, 0, 0);
@@ -1764,7 +1764,7 @@ int InsaneRebel2::runLevel12() {
 				default: filename = "LEV12/P3/12P03_F.SAN"; break;  // duplicate F
 				}
 
-				debugC(DEBUG_INSANE, "Rebel2: Level 12 Phase 3 wave - %s (state=0x%x variantIdx=%d)", filename, _rebelPhaseState, variantIdx);
+				debugC(DEBUG_INSANE, "Level 12 Phase 3 wave - %s (state=0x%x variantIdx=%d)", filename, _rebelPhaseState, variantIdx);
 				if (!playLevelSegment(filename, 0x428))
 					return kLevelQuit;
 
@@ -1788,7 +1788,7 @@ int InsaneRebel2::runLevel12() {
 		budget = kLevel12BudgetBase[3] + _vm->_rnd.getRandomNumber(3);
 
 		// Initialization video (12P08.SAN)
-		debugC(DEBUG_INSANE, "Rebel2: Level 12 Phase 4 - init 12P08.SAN budget=%d", budget);
+		debugC(DEBUG_INSANE, "Level 12 Phase 4 - init 12P08.SAN budget=%d", budget);
 		if (!playLevelSegment("LEV12/12P08.SAN", 0x428, false))
 			return kLevelQuit;
 		processWaveEnd(1, &budget, 0, 0);
@@ -1821,7 +1821,7 @@ int InsaneRebel2::runLevel12() {
 				default: filename = "LEV12/P4/12P04_F.SAN"; break;
 				}
 
-				debugC(DEBUG_INSANE, "Rebel2: Level 12 Phase 4 wave - %s (state=0x%x variantIdx=%d)", filename, _rebelPhaseState, variantIdx);
+				debugC(DEBUG_INSANE, "Level 12 Phase 4 wave - %s (state=0x%x variantIdx=%d)", filename, _rebelPhaseState, variantIdx);
 				if (!playLevelSegment(filename, 0x428))
 					return kLevelQuit;
 
@@ -1846,7 +1846,7 @@ int InsaneRebel2::runLevel12() {
 		// ----- LEVEL COMPLETED -----
 		{
 			int accuracy = calculateAccuracy(_rebelKillCounter, _rebelHitCounter);
-			debugC(DEBUG_INSANE, "Rebel2: Level 12 completed! kills=%d misses=%d accuracy=%d%%",
+			debugC(DEBUG_INSANE, "Level 12 completed! kills=%d misses=%d accuracy=%d%%",
 				_rebelKillCounter, _rebelHitCounter, accuracy);
 		}
 
@@ -1908,14 +1908,14 @@ int InsaneRebel2::runLevel13() {
 
 		if (_playerShield > 0) {
 			int accuracy = calculateAccuracy(_rebelKillCounter, _rebelHitCounter);
-			debugC(DEBUG_INSANE, "Rebel2: Level 13 completed! accuracy=%d%%", accuracy);
+			debugC(DEBUG_INSANE, "Level 13 completed! accuracy=%d%%", accuracy);
 			playLevelEnd(13);
 			_levelUnlocked[13] = true;
 			return kLevelNextLevel;
 		}
 
 		// Death: frame-based variant selection (FUN_0041B3E1 lines 47-61)
-		debugC(DEBUG_INSANE, "Rebel2: Level 13 death at frame %d", _deathFrame);
+		debugC(DEBUG_INSANE, "Level 13 death at frame %d", _deathFrame);
 		playLevelDeathVariant(13, 1, _deathFrame);
 		if (_vm->shouldQuit())
 			return kLevelQuit;
@@ -1964,14 +1964,14 @@ int InsaneRebel2::runLevel14() {
 
 		if (_playerShield > 0) {
 			int accuracy = calculateAccuracy(_rebelKillCounter, _rebelHitCounter);
-			debugC(DEBUG_INSANE, "Rebel2: Level 14 completed! accuracy=%d%%", accuracy);
+			debugC(DEBUG_INSANE, "Level 14 completed! accuracy=%d%%", accuracy);
 			playLevelEnd(14);
 			_levelUnlocked[14] = true;
 			return kLevelNextLevel;
 		}
 
 		// Death: single video (14DIE.SAN)
-		debugC(DEBUG_INSANE, "Rebel2: Level 14 death at frame %d", _deathFrame);
+		debugC(DEBUG_INSANE, "Level 14 death at frame %d", _deathFrame);
 		playCinematic("LEV14/14DIE.SAN");
 		if (_vm->shouldQuit())
 			return kLevelQuit;
@@ -2036,7 +2036,7 @@ int InsaneRebel2::runLevel15() {
 
 		if (_playerShield > 0) {
 			int accuracy = calculateAccuracy(_rebelKillCounter, _rebelHitCounter);
-			debugC(DEBUG_INSANE, "Rebel2: Level 15 completed! accuracy=%d%%", accuracy);
+			debugC(DEBUG_INSANE, "Level 15 completed! accuracy=%d%%", accuracy);
 			playLevelEnd(15);
 			_levelUnlocked[15] = true;
 			// Level 15 completion leads to credits (FUN_0041BBE8)
@@ -2044,7 +2044,7 @@ int InsaneRebel2::runLevel15() {
 		}
 
 		// Death: frame-based variant selection (FUN_0041B8D7 lines 46-65)
-		debugC(DEBUG_INSANE, "Rebel2: Level 15 death at frame %d", _deathFrame);
+		debugC(DEBUG_INSANE, "Level 15 death at frame %d", _deathFrame);
 		playLevelDeathVariant(15, 1, _deathFrame);
 		if (_vm->shouldQuit())
 			return kLevelQuit;
diff --git a/engines/scumm/smush/rebel/smush_player_ra1.cpp b/engines/scumm/smush/rebel/smush_player_ra1.cpp
index e882c9788cb..8a9fc94d781 100644
--- a/engines/scumm/smush/rebel/smush_player_ra1.cpp
+++ b/engines/scumm/smush/rebel/smush_player_ra1.cpp
@@ -296,13 +296,13 @@ bool SmushPlayerRebel1::handleGameFetch(int32 subSize, Common::SeekableReadStrea
 			}
 		}
 
-		debugC(DEBUG_SMUSH, "RA1 FTCH: frame=%d id=0x%08x pos=(%d,%d) using stored FOBJ codec=%d size=%dx%d",
+		debugC(DEBUG_SMUSH, "FTCH: frame=%d id=0x%08x pos=(%d,%d) using stored FOBJ codec=%d size=%dx%d",
 			_frame, fetchId, left, top, storedCodec, _storedFobjWidth, _storedFobjHeight);
 		decodeFrameObject(storedCodec, _storedFobjData, left, top,
 			_storedFobjWidth, _storedFobjHeight, _storedFobjDataSize,
 			storedParam, _storedFobjParm2);
 	} else {
-		debugC(DEBUG_SMUSH, "RA1 FTCH: frame=%d id=0x%08x with no stored FOBJ data", _frame, fetchId);
+		debugC(DEBUG_SMUSH, "FTCH: frame=%d id=0x%08x with no stored FOBJ data", _frame, fetchId);
 	}
 
 	return true;
@@ -319,7 +319,7 @@ void SmushPlayerRebel1::ra1HandleGost(int32 subSize, Common::SeekableReadStream
 	const int32 ghostY = b.readSint32BE();
 
 	if (!_hasFrameFobjForGost || _lastFobjData == nullptr || _lastFobjDataSize <= 0) {
-		debugC(DEBUG_SMUSH, "RA1 GOST: frame=%d ignored type=0x%08x pos=(%d,%d) (no current-frame FOBJ cached)",
+		debugC(DEBUG_SMUSH, "GOST: frame=%d ignored type=0x%08x pos=(%d,%d) (no current-frame FOBJ cached)",
 			_frame, ghostType, ghostX, ghostY);
 		return;
 	}
@@ -336,12 +336,12 @@ void SmushPlayerRebel1::ra1HandleGost(int32 subSize, Common::SeekableReadStream
 		priorityFlags = 0x6000;
 		break;
 	default:
-		debugC(DEBUG_SMUSH, "RA1 GOST: frame=%d ignored unknown type=0x%08x pos=(%d,%d)",
+		debugC(DEBUG_SMUSH, "GOST: frame=%d ignored unknown type=0x%08x pos=(%d,%d)",
 			_frame, ghostType, ghostX, ghostY);
 		return;
 	}
 
-	debugC(DEBUG_SMUSH, "RA1 GOST: frame=%d type=0x%08x flags=0x%04x pos=(%d,%d) size=%dx%d codec=%d",
+	debugC(DEBUG_SMUSH, "GOST: frame=%d type=0x%08x flags=0x%04x pos=(%d,%d) size=%dx%d codec=%d",
 		_frame, ghostType, priorityFlags, ghostX, ghostY,
 		_lastFobjWidth, _lastFobjHeight, _lastFobjCodec);
 
@@ -638,7 +638,7 @@ bool SmushPlayerRebel1::handleGameStoreFrame() {
 }
 
 void SmushPlayerRebel1::handleGameFrameObjectPre(int codec, int left, int top, int width, int height, int dataSize) {
-	debugC(DEBUG_SMUSH, "RA1 FOBJ: frame=%d codec=%d pos=(%d,%d) size=%dx%d dataSize=%d storeFrame=%d",
+	debugC(DEBUG_SMUSH, "FOBJ: frame=%d codec=%d pos=(%d,%d) size=%dx%d dataSize=%d storeFrame=%d",
 		_frame, codec, left, top, width, height, dataSize, _storeFrame);
 }
 
@@ -856,7 +856,7 @@ bool SmushPlayerRebel1::ra1HandleUnknownFrameChunk(uint32 subType, int32 subSize
 	byte tb2 = (subType >> 8) & 0xFF, tb3 = subType & 0xFF;
 	if (tb0 > 0x40 && tb0 < 0x5B && tb1 > 0x40 && tb1 < 0x5B &&
 	    tb2 > 0x40 && tb2 < 0x5B && tb3 > 0x40 && tb3 < 0x5B) {
-		debugC(DEBUG_SMUSH, "RA1: unknown uppercase tag %s at frame %d, stopping frame parse", tag2str(subType), _frame);
+		debugC(DEBUG_SMUSH, "unknown uppercase tag %s at frame %d, stopping frame parse", tag2str(subType), _frame);
 		return true;
 	}
 



From noreply at scummvm.org  Tue Jun  9 21:35:37 2026
From: noreply at scummvm.org (sev-)
Date: Tue,  9 Jun 2026 21:35:37 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 042d55526379d5c499803e8f608591f4a952d6cf
Message-ID: <20260609213537.DDB677005BC@ron.scummvm.net>

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

Summary:
042d555263 DIRECTOR: Fix regressoin in VWCF checksum calculation


Commit: 042d55526379d5c499803e8f608591f4a952d6cf
    https://github.com/scummvm/scummvm/commit/042d55526379d5c499803e8f608591f4a952d6cf
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2026-06-09T23:35:26+02:00

Commit Message:
DIRECTOR: Fix regressoin in VWCF checksum calculation

It was introduced in 46ae24cdbbed5faf7ab5ba25ce30525160253530,
where we skip reading _castArrayStart and _castArrayEnd,
which are later used for the checkusm calculations.

This is patch of a problem, and we could need to see how to compute
it in case we modify cast before saving

Changed paths:
    engines/director/cast.cpp
    engines/director/cast.h


diff --git a/engines/director/cast.cpp b/engines/director/cast.cpp
index d2f4adb65a7..7f403b7cd5e 100644
--- a/engines/director/cast.cpp
+++ b/engines/director/cast.cpp
@@ -78,6 +78,7 @@ Cast::Cast(Movie *movie, uint16 castLibID, bool isShared, bool isExternal, uint1
 	_lingoArchive = new LingoArchive(this);
 
 	_castArrayStart = _castArrayEnd = 0;
+	_castArrayStartForChecksum = _castArrayEndForChecksum = 0;
 
 	_castIDoffset = 0;
 
@@ -403,15 +404,15 @@ bool Cast::loadConfig() {
 		// For D4 and below, and for external castlibs,
 		// the config chunk is the source of truth about
 		// where the members start and end.
-		_castArrayStart = stream->readUint16();
-		_castArrayEnd = stream->readUint16();
+		_castArrayStart = _castArrayStartForChecksum = stream->readUint16();
+		_castArrayEnd = _castArrayEndForChecksum = stream->readUint16();
 	} else {
 		// castArrayStart and castArrayEnd are defined
 		// in the MCsL chunk read by Movie::loadCastLibMapping,
 		// the one in the config chunk is likely to be incorrect
 		// (e.g. multiple internal casts)
-		stream->readUint16();
-		stream->readUint16();
+		_castArrayStartForChecksum = stream->readUint16();
+		_castArrayEndForChecksum = stream->readUint16();
 	}
 
 	// D3 and below use this, override for D4 and over
@@ -625,9 +626,9 @@ void Cast::saveConfig(Common::SeekableWriteStream *writeStream, uint32 offset) {
 
 	Movie::writeRect(writeStream, _checkRect);      // 4, 6, 8, 10
 
-	writeStream->writeUint16BE(_castArrayStart);    // 12
+	writeStream->writeUint16BE(_castArrayStartForChecksum);    // 12
 	// This will change
-	writeStream->writeUint16BE(_castArrayStart + _castArchive->getResourceIDList(MKTAG('C', 'A', 'S', 't')).size());      // 14
+	writeStream->writeUint16BE(_castArrayStartForChecksum + _castArchive->getResourceIDList(MKTAG('C', 'A', 'S', 't')).size());      // 14
 
 	writeStream->writeByte(_readRate);              // 16
 	writeStream->writeByte(_lightswitch);           // 17
@@ -1171,8 +1172,8 @@ uint32 Cast::computeChecksum() {
 	check *= _checkRect.left + 4;
 	check /= _checkRect.bottom + 5;
 	check *= _checkRect.right + 6;
-	check -= _castArrayStart + 7;
-	check *= _castArrayEnd + 8;
+	check -= _castArrayStartForChecksum + 7;
+	check *= _castArrayEndForChecksum + 8;
 	check -= (int8)_readRate + 9;
 	check -= _lightswitch + 10;
 
diff --git a/engines/director/cast.h b/engines/director/cast.h
index 6005bdd3562..d6be2246252 100644
--- a/engines/director/cast.h
+++ b/engines/director/cast.h
@@ -186,6 +186,9 @@ public:
 	LingoDec::ScriptContext *_lingodec = nullptr;
 	LingoDec::ChunkResolver *_chunkResolver = nullptr;
 
+	uint16 _castArrayStartForChecksum;
+	uint16 _castArrayEndForChecksum;
+
 	/* Config Data to be saved */
 	/*  0 */ uint16 _len;
 	/*  2 */ uint16 _fileVersion;



From noreply at scummvm.org  Wed Jun 10 00:55:26 2026
From: noreply at scummvm.org (bluegr)
Date: Wed, 10 Jun 2026 00:55:26 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 3233cdadc6733e8023767b3219e91c055def270d
Message-ID: <20260610005526.673067005CC@ron.scummvm.net>

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

Summary:
24ba7b9812 NANCY: Add new functionality for secondary videos in Nancy10+
5147519578 NANCY: Implement new functionality in collisionpuzzle for Nancy10+
50d6f13a1d NANCY: Add a temporary hack for character availability in Nancy10
5bbbcc20b6 NANCY: Set the event flag after meeting Dave in the cellar in Nancy10
06811cb9b6 NANCY: Implement some of the changes to multibuildpuzzle for Nancy10
3233cdadc6 NANCY: Implement event flag handling for scene hotspots in Nancy10+


Commit: 24ba7b9812a22639b3d802aee6bbc98ac1753f22
    https://github.com/scummvm/scummvm/commit/24ba7b9812a22639b3d802aee6bbc98ac1753f22
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-06-10T03:55:10+03:00

Commit Message:
NANCY: Add new functionality for secondary videos in Nancy10+

- Event flags can now be set per frame IDs, same as in secondary movies
- The current scene is pushed based on a flag when the video is
  triggered, in contract with previous games, where this happened
  unconditionally

Changed paths:
    engines/nancy/action/secondaryvideo.cpp
    engines/nancy/action/secondaryvideo.h


diff --git a/engines/nancy/action/secondaryvideo.cpp b/engines/nancy/action/secondaryvideo.cpp
index 808362db49e..407bbc787e9 100644
--- a/engines/nancy/action/secondaryvideo.cpp
+++ b/engines/nancy/action/secondaryvideo.cpp
@@ -105,6 +105,13 @@ void PlaySecondaryVideo::updateGraphics() {
 			if (_decoder.needsUpdate()) {
 				GraphicsManager::copyToManaged(*_decoder.decodeNextFrame(), _fullFrame, !_paletteFilename.empty(), _videoFormat == kSmallVideoFormat);
 				_needsRedraw = true;
+
+				for (const FlagAtFrame &f : _frameFlags) {
+					if (f.flagDesc.label != -1 &&
+							_decoder.getCurFrame() == f.frameID) {
+						NancySceneState.setEventFlag(f.flagDesc);
+					}
+				}
 			}
 
 			if (lastAnimationFrame > -1 &&
@@ -155,6 +162,9 @@ void PlaySecondaryVideo::handleInput(NancyInput &input) {
 }
 
 void PlaySecondaryVideo::readData(Common::SeekableReadStream &stream) {
+	uint16 numFrameEvents = 0;
+	uint16 numVideoDescs = 0;
+
 	Common::Serializer ser(&stream, nullptr);
 	ser.setVersion(g_nancy->getGameType());
 
@@ -176,14 +186,25 @@ void PlaySecondaryVideo::readData(Common::SeekableReadStream &stream) {
 	ser.syncAsUint16LE(_onHoverEndLastFrame);
 
 	_sceneChange.readData(stream, ser.getVersion() == kGameTypeVampire);
-	ser.skip(1, kGameTypeNancy1);
 
-	// Count of extra 6-byte entries that sit between the header and the
-	// video descs in the original layout. Always 0 in practice.
-	ser.skip(2, kGameTypeNancy10);
+	if (g_nancy->getGameType() <= kGameTypeNancy9) {
+		ser.skip(1, kGameTypeNancy1);
+	} else {
+		byte pushSceneByte = 0;
+		ser.syncAsByte(pushSceneByte);
+		_pushSceneOnTrigger = (pushSceneByte == 1);
+		ser.syncAsUint16LE(numFrameEvents);
+	}
 
-	uint16 numVideoDescs = 0;
 	ser.syncAsUint16LE(numVideoDescs);
+
+	_frameFlags.resize(numFrameEvents);
+	for (uint i = 0; i < numFrameEvents; ++i) {
+		ser.syncAsSint16LE(_frameFlags[i].frameID);
+		ser.syncAsSint16LE(_frameFlags[i].flagDesc.label);
+		ser.syncAsUint16LE(_frameFlags[i].flagDesc.flag);
+	}
+
 	_videoDescs.resize(numVideoDescs);
 	for (uint i = 0; i < numVideoDescs; ++i) {
 		_videoDescs[i].readData(stream);
@@ -242,7 +263,8 @@ void PlaySecondaryVideo::execute() {
 		break;
 	}
 	case kActionTrigger:
-		NancySceneState.pushScene();
+		if (g_nancy->getGameType() < kGameTypeNancy10 || _pushSceneOnTrigger)
+			NancySceneState.pushScene();
 		NancySceneState.changeScene(_sceneChange);
 		finishExecution();
 		break;
diff --git a/engines/nancy/action/secondaryvideo.h b/engines/nancy/action/secondaryvideo.h
index 9117dedb011..d6cde625e98 100644
--- a/engines/nancy/action/secondaryvideo.h
+++ b/engines/nancy/action/secondaryvideo.h
@@ -68,6 +68,16 @@ public:
 	uint16 _onHoverEndLastFrame = 0;
 	SceneChangeDescription _sceneChange;
 
+	// Nancy 10+: push current scene before the sceneChange target.
+	bool _pushSceneOnTrigger = false;
+
+	// Nancy 10+: set an event flag when playback reaches frameID.
+	struct FlagAtFrame {
+		int16 frameID;
+		FlagDescription flagDesc;
+	};
+	Common::Array<FlagAtFrame> _frameFlags;
+
 	Common::Array<SecondaryVideoDescription> _videoDescs;
 
 	bool canHaveHotspot() const override { return true; }


Commit: 514751957847e5b0601b2573ecdc41b639d1dde9
    https://github.com/scummvm/scummvm/commit/514751957847e5b0601b2573ecdc41b639d1dde9
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-06-10T03:55:10+03:00

Commit Message:
NANCY: Implement new functionality in collisionpuzzle for Nancy10+

Fixes the cellar staircase puzzle in Nancy10

Changed paths:
    engines/nancy/action/puzzle/collisionpuzzle.cpp
    engines/nancy/action/puzzle/collisionpuzzle.h


diff --git a/engines/nancy/action/puzzle/collisionpuzzle.cpp b/engines/nancy/action/puzzle/collisionpuzzle.cpp
index 8de4dc95f53..60e73452dc6 100644
--- a/engines/nancy/action/puzzle/collisionpuzzle.cpp
+++ b/engines/nancy/action/puzzle/collisionpuzzle.cpp
@@ -187,26 +187,35 @@ void CollisionPuzzle::readData(Common::SeekableReadStream &stream) {
 	readFilename(stream, _imageName);
 	uint16 numPieces = 0;
 
-	uint16 width = stream.readUint16LE();
-	uint16 height = stream.readUint16LE();
+	// Nancy 10+ TileMove stores rows then cols (was width then height for square grids)
+	uint16 width, height;
+	if (_puzzleType == kTileMove && g_nancy->getGameType() >= kGameTypeNancy10) {
+		height = stream.readUint16LE();
+		width = stream.readUint16LE();
+	} else {
+		width = stream.readUint16LE();
+		height = stream.readUint16LE();
+	}
 
 	if (_puzzleType == kCollision) {
 		numPieces = stream.readUint16LE();
 	} else {
 		_tileMoveExitPos.y = stream.readUint16LE();
 		_tileMoveExitPos.x = stream.readUint16LE();
-		_tileMoveExitSize = stream.readUint16LE();
+		_tileMoveExitIndex = stream.readUint16LE();
 		numPieces = 6;
 	}
 
+	const uint maxGridSize = (_puzzleType == kTileMove && g_nancy->getGameType() >= kGameTypeNancy10) ? 11 : 8;
+
 	_grid.resize(height, Common::Array<uint16>(width));
 	for (uint y = 0; y < height; ++y) {
 		for (uint x = 0; x < width; ++x) {
 			_grid[y][x] = stream.readUint16LE();
 		}
-		stream.skip((8 - width) * 2);
+		stream.skip((maxGridSize - width) * 2);
 	}
-	stream.skip((8 - height) * 8 * 2);
+	stream.skip((maxGridSize - height) * maxGridSize * 2);
 
 	if (_puzzleType == kCollision) {
 		_startLocations.resize(numPieces);
@@ -240,15 +249,21 @@ void CollisionPuzzle::readData(Common::SeekableReadStream &stream) {
 
 	if (g_nancy->getGameType() <= kGameTypeNancy7) {
 		stream.skip(3);
+	} else if (_puzzleType == kCollision && g_nancy->getGameType() >= kGameTypeNancy10) {
+		stream.skip(3);
 	} else if (_puzzleType == kTileMove) {
 		uint16 numTimerGraphics = stream.readUint16LE();
 		_timerTime = stream.readUint32LE();
-		readRectArray(stream, _timerSrcs, numTimerGraphics, 10);
-		_timerFlagIds.resize(numTimerGraphics);
-		for (uint i = 0; i < numTimerGraphics; ++i) {
-			_timerFlagIds[i] = stream.readSint16LE();
+		if (numTimerGraphics) {
+			readRectArray(stream, _timerSrcs, numTimerGraphics, 10);
+			_timerFlagIds.resize(numTimerGraphics);
+			for (uint i = 0; i < numTimerGraphics; ++i) {
+				_timerFlagIds[i] = stream.readSint16LE();
+			}
+			stream.skip((10 - numTimerGraphics) * 2);
+		} else {
+			stream.skip(10 * 16 + 10 * 2);	// 10x16-byte rects + 10x2-byte flag IDs
 		}
-		stream.skip((10 - numTimerGraphics) * 2);
 		readRect(stream, _timerDest);
 	}
 
@@ -300,13 +315,30 @@ void CollisionPuzzle::execute() {
 					return;
 				}
 			}
+		} else if (_tileMoveExitIndex == 20 && g_nancy->getGameType() >= kGameTypeNancy10) {
+			// Stair-slider sentinel: solves when the exit cell is uncovered
+			bool exitCovered = false;
+			for (uint i = 0; i < _pieces.size(); ++i) {
+				Common::Rect r(_pieces[i]._gridPos.x, _pieces[i]._gridPos.y,
+								_pieces[i]._gridPos.x + _pieces[i]._w,
+								_pieces[i]._gridPos.y + _pieces[i]._h);
+				if (r.contains(_tileMoveExitPos)) {
+					exitCovered = true;
+					break;
+				}
+			}
+			if (exitCovered) {
+				return;
+			}
 		} else {
 			// Check if either:
 			// - the solve tile is over the exit or;
 			// - the solve tile is outside the bounds of the grid (and is thus inside the exit)
 			Common::Point pos = _pieces[0]._gridPos;
 			Common::Rect posRect(pos.x, pos.y, pos.x + _pieces[0]._w, pos.y + _pieces[0]._h);
-			Common::Rect gridRect(_grid.size(), _grid[0].size());
+			int16 w = g_nancy->getGameType() <= kGameTypeNancy9 ? _grid.size() : _grid[0].size();
+			int16 h = g_nancy->getGameType() <= kGameTypeNancy9 ? _grid[0].size() : _grid.size();
+			Common::Rect gridRect(w, h);
 			if (!posRect.contains(_tileMoveExitPos) && gridRect.contains(pos)) {
 				return;
 			}
@@ -458,9 +490,9 @@ Common::Point CollisionPuzzle::movePiece(uint pieceID, WallType direction) {
 		Common::Rect compareRect(newPos.x, newPos.y, newPos.x + _pieces[pieceID]._w, newPos.y + _pieces[pieceID]._h);
 		if (compareRect.contains(_tileMoveExitPos)) {
 			if (horizontal && (_tileMoveExitPos.x == 0 || _tileMoveExitPos.x == (int)_grid[0].size() - 1)) {
-				newPos.x += inc * _tileMoveExitSize;
+				newPos.x += inc * _tileMoveExitIndex;
 			} else if (!horizontal && (_tileMoveExitPos.y == 0 || _tileMoveExitPos.y == (int)_grid.size() - 1)) {
-				newPos.y += inc * _tileMoveExitSize;
+				newPos.y += inc * _tileMoveExitIndex;
 			}
 		}
 	}
diff --git a/engines/nancy/action/puzzle/collisionpuzzle.h b/engines/nancy/action/puzzle/collisionpuzzle.h
index 034d0566d61..ab8c29638fe 100644
--- a/engines/nancy/action/puzzle/collisionpuzzle.h
+++ b/engines/nancy/action/puzzle/collisionpuzzle.h
@@ -88,7 +88,7 @@ protected:
 	Common::Rect _blockSrc;
 
 	Common::Point _tileMoveExitPos = Common::Point(-1, -1);
-	uint _tileMoveExitSize = 0;
+	uint _tileMoveExitIndex = 0;
 
 	bool _usesExitButton = false;
 	Common::Rect _exitButtonSrc;


Commit: 50d6f13a1d87666cf76af16541203bc35ad73078
    https://github.com/scummvm/scummvm/commit/50d6f13a1d87666cf76af16541203bc35ad73078
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-06-10T03:55:11+03:00

Commit Message:
NANCY: Add a temporary hack for character availability in Nancy10

Character checks are currently not being handled correctly in
secondary videos in Nancy10. Adding a temporary hack and TODO for
now, till we figure out what's missing.

Changed paths:
    engines/nancy/action/secondaryvideo.cpp


diff --git a/engines/nancy/action/secondaryvideo.cpp b/engines/nancy/action/secondaryvideo.cpp
index 407bbc787e9..ff51376a40d 100644
--- a/engines/nancy/action/secondaryvideo.cpp
+++ b/engines/nancy/action/secondaryvideo.cpp
@@ -241,6 +241,18 @@ void PlaySecondaryVideo::execute() {
 				_currentViewportFrame = -1;
 			}
 
+			// HACK: Checks for character availability in Nancy10. These are
+			// currently not handled correctly, so we hardcode them here at the moment.
+			// TODO: Find out why these are not handled correctly and implement a
+			// proper solution for them.
+			if (g_nancy->getGameType() == kGameTypeNancy10) {
+				uint16 sceneId = NancySceneState.getSceneInfo().sceneID;
+				if ((sceneId == 2307 && NancySceneState.getEventFlag(556, g_nancy->_false)) || // EV_ST_Available
+					(sceneId == 2605 && NancySceneState.getEventFlag(588, g_nancy->_false)) || // EV_TB_Available
+					(sceneId == 2915 && NancySceneState.getEventFlag(156, g_nancy->_false))) // EV_DG_Available
+					_currentViewportFrame = -1;
+			}
+
 			if (_currentViewportFrame != -1) {
 				if (!_isInFrame) {
 					_decoder.start();


Commit: 5bbbcc20b67ae98bcac6b8cbc389e3445b077230
    https://github.com/scummvm/scummvm/commit/5bbbcc20b67ae98bcac6b8cbc389e3445b077230
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-06-10T03:55:12+03:00

Commit Message:
NANCY: Set the event flag after meeting Dave in the cellar in Nancy10

The event flag for meeting Dave in the Cellar is not set correctly in
Nancy 10, so we hardcode the flag change to avoid talking with him over
and over again. This is a temporary hack with an associated TODO for
now, till we figure out what's missing.

Changed paths:
    engines/nancy/state/scene.cpp


diff --git a/engines/nancy/state/scene.cpp b/engines/nancy/state/scene.cpp
index f3f856c11ce..808785d48a7 100644
--- a/engines/nancy/state/scene.cpp
+++ b/engines/nancy/state/scene.cpp
@@ -250,6 +250,14 @@ void Scene::changeScene(const SceneChangeDescription &sceneDescription) {
 		return;
 	}
 
+	// HACK: The event flag for meeting Dave in the cellar is not set
+	// correctly in Nancy 10, so we hardcode the flag change here to
+	// avoid talking with him over and over again.
+	// TODO: Find out why this flag is not set correctly and implement a
+	// proper solution for it.
+	if (g_nancy->getGameType() == kGameTypeNancy10 && sceneDescription.sceneID == 3996)
+		NancySceneState.setEventFlag(314, g_nancy->_true);	// EV_Met_DG_Cellar
+
 	_sceneState.nextScene = sceneDescription;
 	_state = kLoad;
 }


Commit: 06811cb9b6b368fa1ff789ea19e01e1775caa04f
    https://github.com/scummvm/scummvm/commit/06811cb9b6b368fa1ff789ea19e01e1775caa04f
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-06-10T03:55:14+03:00

Commit Message:
NANCY: Implement some of the changes to multibuildpuzzle for Nancy10

Partially fixes the cake mixing puzzle in Nancy10, although there's
still work to be done

Changed paths:
    engines/nancy/action/puzzle/multibuildpuzzle.cpp
    engines/nancy/action/puzzle/multibuildpuzzle.h


diff --git a/engines/nancy/action/puzzle/multibuildpuzzle.cpp b/engines/nancy/action/puzzle/multibuildpuzzle.cpp
index 88ec7581676..91a03ab0300 100644
--- a/engines/nancy/action/puzzle/multibuildpuzzle.cpp
+++ b/engines/nancy/action/puzzle/multibuildpuzzle.cpp
@@ -86,6 +86,8 @@ void MultiBuildPuzzle::registerGraphics() {
 }
 
 void MultiBuildPuzzle::readData(Common::SeekableReadStream &stream) {
+	const bool isNancy10 = g_nancy->getGameType() >= kGameTypeNancy10;
+
 	readFilename(stream, _primaryImageName);
 
 	Common::String secName;
@@ -93,6 +95,10 @@ void MultiBuildPuzzle::readData(Common::SeekableReadStream &stream) {
 	_closeupImageName = Common::Path(secName);
 	_hasCloseupImage = (secName != "NO_FILE" && !secName.empty());
 
+	if (isNancy10) {
+		_retainState = stream.readByte() != 0;	// TODO: state isn't saved yet
+	}
+
 	_numPieces = stream.readUint16LE();
 	_requiredPieces = stream.readUint16LE();
 
@@ -108,23 +114,48 @@ void MultiBuildPuzzle::readData(Common::SeekableReadStream &stream) {
 	_allowAltZoneSnap = stream.readByte() != 0;
 	_checkOverlapOnDrop = stream.readByte() != 0;
 
+	if (isNancy10) {
+		// Completion-animation block: image name + sprite-sheet rect + 4 layout
+		// shorts (cols, framesPerStep, spacing, totalRows). TODO: not wired up.
+		Common::String animName;
+		readFilename(stream, animName);
+		_animImageName = Common::Path(animName);
+		_hasAnimImage = (animName != "NO_FILE" && !animName.empty());
+		readRect(stream, _animRect);
+		for (uint i = 0; i < 4; ++i)
+			_animLayout[i] = stream.readSint16LE();
+	}
+
 	// Pieces: data file always has 20 ?? 67-byte slots; only _numPieces are used.
 	// Reserve up-front so counter-spawn push_back doesn't reallocate (pieces are
 	// RenderObjects already registered with the graphics manager).
+	// In Nancy 10, the pieces have been bumped up to 83.
 	_pieces.reserve(80);
 	_pieces.resize(_numPieces);
 	for (uint i = 0; i < 20; ++i) {
 		if (i < _numPieces) {
 			Piece &p = _pieces[i];
-			readRect(stream, p.srcRect);
-			readRect(stream, p.homeRect);
-			readRect(stream, p.altSrcRect);
-			readRect(stream, p.cuSrcRect);
-			p.counterByte  = stream.readByte();
-			p.mustPlace    = stream.readByte();
-			p.mustNotPlace = stream.readByte();
+			if (isNancy10) {
+				stream.skip(16); // TODO: leading reserved rect, empty in cake puzzle.
+				readRect(stream, p.srcRect);
+				readRect(stream, p.altSrcRect);
+				readRect(stream, p.cuSrcRect);
+				readRect(stream, p.placedDstRect);
+				p.counterByte  = stream.readByte();
+				p.mustPlace    = stream.readByte();
+				p.mustNotPlace = stream.readByte();
+				p.homeRect = p.srcRect;
+			} else {
+				readRect(stream, p.srcRect);
+				readRect(stream, p.homeRect);
+				readRect(stream, p.altSrcRect);
+				readRect(stream, p.cuSrcRect);
+				p.counterByte  = stream.readByte();
+				p.mustPlace    = stream.readByte();
+				p.mustNotPlace = stream.readByte();
+			}
 		} else {
-			stream.skip(67);
+			stream.skip(isNancy10 ? 83 : 67);
 		}
 	}
 
@@ -132,6 +163,12 @@ void MultiBuildPuzzle::readData(Common::SeekableReadStream &stream) {
 	_pickupSound.readNormal(stream);
 	_dropSound.readNormal(stream);
 
+	if (isNancy10) {
+		_animSound.readNormal(stream);
+		stream.skip(7 * 33);	// TODO: 7 unknown strings
+		stream.skip(2); // TODO: 4th cursor id at the front of the block; role TBD.
+	}
+
 	_dragCursorID  = stream.readSint16LE();
 	_exitCursorID1 = stream.readSint16LE();
 	_exitCursorID2 = stream.readSint16LE();
@@ -369,6 +406,16 @@ void MultiBuildPuzzle::handleInput(NancyInput &input) {
 	if (!viewData)
 		return;
 	Common::Rect vpScreen = viewData->screenPosition;
+
+	// Exit hotspots are checked against full-screen coords, so they work even
+	// when the cursor strays below the viewport (cake mixing's bottom-edge
+	// exit at y=340 sits outside the viewport in Nancy 10).
+	if (!_isDragging && _selectedPiece == -1 && !vpScreen.contains(input.mousePos)) {
+		if (!checkExitHotspot(_exitHotspot, _exitCursorID1, input))
+			checkExitHotspot(_exitHotspot2, _exitCursorID2, input);
+		return;
+	}
+
 	if (!vpScreen.contains(input.mousePos))
 		return;
 
@@ -622,6 +669,20 @@ void MultiBuildPuzzle::updatePieceRender(int pieceIdx) {
 	bool isSelected = (!_isDragging && pieceIdx == _selectedPiece);
 	bool isDragging  = (_isDragging  && pieceIdx == _pickedUpPiece);
 
+	// Nancy 10: at-rest pieces are already painted into the primary scene
+	// overlay at their home positions, so the engine doesn't draw them.
+	// Hide the placed piece too when a completion-animation overlay handles
+	// the visual (cake mixing).
+	if (g_nancy->getGameType() >= kGameTypeNancy10 && !isDragging && !isSelected) {
+		if (!p.isPlaced || _hasAnimImage) {
+			p.setVisible(false);
+			p.moveTo(p.gameRect);
+			return;
+		}
+	}
+
+	p.setVisible(true);
+
 	if (p.isPlaced || isDragging) {
 		// Placed or being dragged: show rotation sprite.
 		int rot = p.curRotation;
diff --git a/engines/nancy/action/puzzle/multibuildpuzzle.h b/engines/nancy/action/puzzle/multibuildpuzzle.h
index 0a3b755982d..df8e5b7a686 100644
--- a/engines/nancy/action/puzzle/multibuildpuzzle.h
+++ b/engines/nancy/action/puzzle/multibuildpuzzle.h
@@ -64,6 +64,7 @@ protected:
 		Common::Rect homeRect;    // Slot position in viewport coords
 		Common::Rect altSrcRect;  // If non-empty: used as source for sprite creation
 		Common::Rect cuSrcRect;   // Source in closeup image
+		Common::Rect placedDstRect; // Nancy 10: placement destination on screen
 		uint8 counterByte = 0;    // Non-zero: respawns on placement; doesn't count toward solve
 		uint8 mustPlace = 0;      // Must be placed for solution
 		uint8 mustNotPlace = 0;   // Placing this fails the solution check
@@ -85,6 +86,14 @@ protected:
 	Common::Path _closeupImageName;
 	bool _hasCloseupImage = false;
 
+	// Nancy 10 additions
+	bool _retainState = false;      // TODO: state-persistence not yet wired up
+	Common::Path _animImageName;    // Completion animation sprite sheet
+	bool _hasAnimImage = false;
+	Common::Rect _animRect;
+	int16 _animLayout[4] = {};      // cols / framesPerStep / spacing / totalRows
+	SoundDescription _animSound;    // Sound played during the animation
+
 	uint16 _numPieces = 0;
 	uint16 _requiredPieces = 0;        // Minimum placed pieces (counterByte==0) for solve check
 	bool _autoSolveOnDrop = false;     // If true, solve check fires after each drop


Commit: 3233cdadc6733e8023767b3219e91c055def270d
    https://github.com/scummvm/scummvm/commit/3233cdadc6733e8023767b3219e91c055def270d
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-06-10T03:55:15+03:00

Commit Message:
NANCY: Implement event flag handling for scene hotspots in Nancy10+

Changed paths:
    engines/nancy/action/datarecords.cpp


diff --git a/engines/nancy/action/datarecords.cpp b/engines/nancy/action/datarecords.cpp
index 0f6bf6bcd68..500ab2797af 100644
--- a/engines/nancy/action/datarecords.cpp
+++ b/engines/nancy/action/datarecords.cpp
@@ -387,14 +387,18 @@ void EventFlagsMultiHS::execute() {
 
 		break;
 	case kActionTrigger:
-		if (_hoverCursor != CursorManager::kCustom1 && _hoverCursor != CursorManager::kCustom2) {
+		// Swallow clicks if the cursor is in the puzzle-drag range
+		if (g_nancy->getGameType() <= kGameTypeNancy9 && (_hoverCursor == CursorManager::kCustom1 || _hoverCursor == CursorManager::kCustom2)) {
+			_state = kRun;
+		} else if (g_nancy->getGameType() >= kGameTypeNancy10 && (int)_hoverCursor >= 35) {
+			_state = kRun;
+		} else {
 			_hasHotspot = false;
 			EventFlags::execute();
 			finishExecution();
-			break;
-		} else {
-			_state = kRun;
 		}
+
+		break;
 	}
 }
 



From noreply at scummvm.org  Wed Jun 10 09:28:53 2026
From: noreply at scummvm.org (kelmer44)
Date: Wed, 10 Jun 2026 09:28:53 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 a944b46afd66e865f2c3cdbe4459e515b9f27c40
Message-ID: <20260610092853.AE8727005CC@ron.scummvm.net>

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

Summary:
a944b46afd PELROCK: Make sure pointer is properly reserved. PVS-Studio V575


Commit: a944b46afd66e865f2c3cdbe4459e515b9f27c40
    https://github.com/scummvm/scummvm/commit/a944b46afd66e865f2c3cdbe4459e515b9f27c40
Author: kelmer (kelmer at gmail.com)
Date: 2026-06-10T11:25:33+02:00

Commit Message:
PELROCK: Make sure pointer is properly reserved. PVS-Studio V575

Changed paths:
    engines/pelrock/sound.cpp


diff --git a/engines/pelrock/sound.cpp b/engines/pelrock/sound.cpp
index 940a1336a24..3a082f0918a 100644
--- a/engines/pelrock/sound.cpp
+++ b/engines/pelrock/sound.cpp
@@ -204,6 +204,7 @@ int SoundManager::playSound(SonidoFile sound, int channel, int loopCount) {
 		uint32 pcmSize = sound.size - headerSize;
 		byte *pcmData = (byte *)malloc(pcmSize);
 		assert(pcmData);
+		assert(data);
 		memcpy(pcmData, data + headerSize, pcmSize);
 		free(data);
 



From noreply at scummvm.org  Wed Jun 10 09:35:52 2026
From: noreply at scummvm.org (bluegr)
Date: Wed, 10 Jun 2026 09:35:52 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 26aee4e81f9c6cccfde4b7b7525443b0ea156591
Message-ID: <20260610093552.9B29A7005CC@ron.scummvm.net>

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:
9192240e43 MOHAWK: Sound - Fix out of bounds seek crash.
26aee4e81f MOHAWK: LB - Increase sensitivity.


Commit: 9192240e43d437688f72df42197a78a50e2c3844
    https://github.com/scummvm/scummvm/commit/9192240e43d437688f72df42197a78a50e2c3844
Author: Alstruit (34786806+Alstruit at users.noreply.github.com)
Date: 2026-06-10T12:35:48+03:00

Commit Message:
MOHAWK: Sound - Fix out of bounds seek crash.

Some Mohawk sound files may report the chunk size greater than the actual chunk size probed.
Example: "Arthur's Computer Adventure".

Fixes Assertion `_pos <= _end' failed error.

Changed paths:
    engines/mohawk/sound.cpp


diff --git a/engines/mohawk/sound.cpp b/engines/mohawk/sound.cpp
index 8c104e0e17b..39f1f84de0d 100644
--- a/engines/mohawk/sound.cpp
+++ b/engines/mohawk/sound.cpp
@@ -47,6 +47,20 @@ namespace Mohawk {
  * @param stream The stream to look for samples.
  */
 void scanAndFixAudioPops(DataChunk &dataChunk, uint32 &dataSize, Common::SeekableReadStream *stream) {
+
+	// Some assets declare more DATA payload bytes than remain in the current stream.
+	// Clamp dataSize to the bytes physically available before probing the final samples.
+	if (stream->size() != -1) {
+		int64 availableBytes = stream->size() - stream->pos();
+		if (availableBytes >= 0 && dataSize > (uint32)availableBytes) {
+			debug(1, "MOHAWK: Clamping declared dataSize %u to actual available bytes %u", dataSize, (uint32)availableBytes);
+			dataSize = (uint32)availableBytes;
+		}
+	}
+
+	if (dataSize < 4)
+		return;
+
 	const int PCM8_U_SILENCE = 0x80;
 	const int SQUELCH = 32; // Threshold of discontinuity before removing. Lower values = increased sensitivity.
 	bool is_safe = false;
@@ -325,6 +339,14 @@ Audio::RewindableAudioStream *Sound::makeLivingBooksWaveStream_v1(Common::Seekab
 	} else
 		error("Could not find Old Mohawk Sound header");
 
+	if (size >= 4 && ConfMan.getBool("fix_audio_pops")) {
+		DataChunk chunk;
+		memset(&chunk, 0, sizeof(DataChunk));
+		chunk.sampleCount = size;
+
+		scanAndFixAudioPops(chunk, size, stream);
+	}
+
 	Common::SeekableReadStream *dataStream = stream->readStream(size);
 	delete stream;
 


Commit: 26aee4e81f9c6cccfde4b7b7525443b0ea156591
    https://github.com/scummvm/scummvm/commit/26aee4e81f9c6cccfde4b7b7525443b0ea156591
Author: Alstruit (34786806+Alstruit at users.noreply.github.com)
Date: 2026-06-10T12:35:48+03:00

Commit Message:
MOHAWK: LB - Increase sensitivity.

A value of a distance of 8 units is enough to trigger a pop.

Found in "bearfight" on page 2. First pop is small but still perceptible.

Changed paths:
    engines/mohawk/sound.cpp


diff --git a/engines/mohawk/sound.cpp b/engines/mohawk/sound.cpp
index 39f1f84de0d..f87c3612b57 100644
--- a/engines/mohawk/sound.cpp
+++ b/engines/mohawk/sound.cpp
@@ -62,7 +62,7 @@ void scanAndFixAudioPops(DataChunk &dataChunk, uint32 &dataSize, Common::Seekabl
 		return;
 
 	const int PCM8_U_SILENCE = 0x80;
-	const int SQUELCH = 32; // Threshold of discontinuity before removing. Lower values = increased sensitivity.
+	const int SQUELCH = 8; // Threshold of discontinuity before removing. Lower values = increased sensitivity.
 	bool is_safe = false;
 
 	// Peek at the last 4 samples without permanently moving the stream pointer.
@@ -76,7 +76,7 @@ void scanAndFixAudioPops(DataChunk &dataChunk, uint32 &dataSize, Common::Seekabl
 	// any minor fluctuation is inaudible and the sound is considered safe.
 	bool is_stable_and_quiet = true;
 	for (int i = 0; i < 4; i++) {
-		if (abs(s[i] - PCM8_U_SILENCE) > SQUELCH) {
+		if (abs(s[i] - PCM8_U_SILENCE) >= SQUELCH) {
 			is_stable_and_quiet = false;
 			break;
 		}



From noreply at scummvm.org  Wed Jun 10 11:01:11 2026
From: noreply at scummvm.org (tag2015)
Date: Wed, 10 Jun 2026 11:01:11 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 5c627f59bc1a18389721f9e6a349899a11c130fb
Message-ID: <20260610110111.3E78B7005CC@ron.scummvm.net>

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

Summary:
5c627f59bc DIRECTOR: Add detection entries for Piposh games


Commit: 5c627f59bc1a18389721f9e6a349899a11c130fb
    https://github.com/scummvm/scummvm/commit/5c627f59bc1a18389721f9e6a349899a11c130fb
Author: Walter Agazzi (walter.agazzi at protonmail.com)
Date: 2026-06-10T12:42:07+02:00

Commit Message:
DIRECTOR: Add detection entries for Piposh games

Fix #16651

Changed paths:
    engines/director/detection_tables.h


diff --git a/engines/director/detection_tables.h b/engines/director/detection_tables.h
index d42a9a3601d..23587c7d912 100644
--- a/engines/director/detection_tables.h
+++ b/engines/director/detection_tables.h
@@ -1083,8 +1083,12 @@ static const PlainGameDescriptor directorGames[] = {
 	{ "zwergnase",			"Zwerg Nase: Ein interaktives M??rchen" },
 
 	// Hebrew titles
-	{ "petel",				"?????? ??????" },
 	{ "harating",			"?????????? ????????????????" },  // BeTchachey HaRating - TV Ratings Intrigue
+	{ "petel",				"?????? ??????" },
+	{ "piposh1",			"??????????" },  // aka Piposh: Hollywood
+	{ "piposh2",			"?????????? 2" },
+	{ "piposh3d",			"3D ??????????" },
+	{ "piposhdream",		"?????????? ?????????? ????????????" }, // Piposh in: A Dream Come True
 
 	// Italian titles
 	{ "brillo",				"Brillo: I Segreti del Bosco Antico" },
@@ -10370,6 +10374,19 @@ static const DirectorGameDescription gameDescriptions[] = {
 	WINGAME2_l("peppi", "",			"PIPPI.EXE",		 "t:acf172e5effe2f5c15e50f3c781aef40", 1274514,
 									"AAA_MOOV/dlia.mov", "f:e6f6ef6ba73332210aee3f9d9cf408fd",  480186, Common::RU_RUS, 800),
 
+	// "Piposh" series developed by Guillotine, published by Hed Arzi Multimedia
+	WINGAME2_l("piposh1", "", "piposh.exe",			  "t:9d33c0d6a4cfb70c33f87f6e8a1f23fd", 5665996,
+							  "PIPDATA/AFTRUMB1.DXR", "f:04529141e3c6e027331bd2da8e328a7c", 886670, Common::HE_ISR, 850),
+
+	WINGAME2_l("piposh2", "", "piposh2.exe",	   "t:9d33c0d6a4cfb70c33f87f6e8a1f23fd", 5665996,
+							  "PIP2DATA/AIR1.DXR", "f:cc6c9bb1acf76a0697a30d626e89543c", 2119111, Common::HE_ISR, 850),
+
+	// Only launcher is Director. Actual game uses 3D GameStudio / A5 engine
+	WINGAME1_l("piposh3d", "Launcher", "piposh3d.exe", "t:4dfd8c52d8cff6d1b7a9435a966f4e78", 5427592, Common::HE_ISR, 850),
+
+	WINGAME2_l("piposhdream", "", "dream.exe",	   "t:786558a29a117ff446a2419df3fa6756", 5120988,
+								  "COMEIN.dxr",	   "f:cd6f2b3a7768a6c8052b2ed1868f8c48", 3298134, Common::HE_ISR, 850),
+
 	// Original name is "Syng, l??r og leg"
 	MACGAME1_l("pixeline1", "", "xn--syng, lr og leg-3lb",		"a44511b8ff0e46b4f9c85dd1cb58d458", 157815, Common::DA_DNK, 851),
 	WINGAME1_l("pixeline1", "", "xn--syng, lr og leg.exe-rxb", 	"0c81c84ca4966313ad27d47daba44537", 3797528, Common::DA_DNK, 851),



From noreply at scummvm.org  Wed Jun 10 11:58:51 2026
From: noreply at scummvm.org (ScummVM-Translations)
Date: Wed, 10 Jun 2026 11:58:51 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 c87e1450d7db92ef3cb40ee7b6bb920e64ed52bb
Message-ID: <20260610115851.832887005CC@ron.scummvm.net>

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

Summary:
c87e1450d7 I18N: Update translation (Spanish)


Commit: c87e1450d7db92ef3cb40ee7b6bb920e64ed52bb
    https://github.com/scummvm/scummvm/commit/c87e1450d7db92ef3cb40ee7b6bb920e64ed52bb
Author: IlDucci (pajaroloco_2 at hotmail.com)
Date: 2026-06-10T11:58:44Z

Commit Message:
I18N: Update translation (Spanish)

Currently translated at 100.0% (3208 of 3208 strings)

Changed paths:
    po/es_ES.po


diff --git a/po/es_ES.po b/po/es_ES.po
index 784b440a7d7..8ca3e8162c6 100644
--- a/po/es_ES.po
+++ b/po/es_ES.po
@@ -8,7 +8,7 @@ msgstr ""
 "Project-Id-Version: ScummVM 1.4.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
 "POT-Creation-Date: 2026-06-09 13:22+0000\n"
-"PO-Revision-Date: 2026-06-08 08:48+0000\n"
+"PO-Revision-Date: 2026-06-10 11:58+0000\n"
 "Last-Translator: IlDucci <pajaroloco_2 at hotmail.com>\n"
 "Language-Team: Spanish <https://translations.scummvm.org/projects/scummvm/"
 "scummvm/es/>\n"
@@ -13700,14 +13700,14 @@ msgid "Disable player damage"
 msgstr "Desactiva los da??os recibidos por el jugador"
 
 #: engines/scumm/metaengine.cpp:909
-#, fuzzy
-#| msgid "Fast mode"
 msgid "Yoda mode"
-msgstr "Modo r??pido"
+msgstr "Modo Yoda"
 
 #: engines/scumm/metaengine.cpp:910
 msgid "Enable original Yoda mode shortcuts, including movie mode and auto play"
 msgstr ""
+"Activa los accesos directos originales del modo Yoda, que incluyen el modo "
+"pel??cula y la reproducci??n autom??tica"
 
 #: engines/scumm/metaengine.cpp:1085
 msgid "Down left"



From noreply at scummvm.org  Wed Jun 10 12:30:11 2026
From: noreply at scummvm.org (lotharsm)
Date: Wed, 10 Jun 2026 12:30:11 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 5775cd3ec739cd2185cabd71d8e9682d499fd328
Message-ID: <20260610123011.0C93E7005CC@ron.scummvm.net>

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

Summary:
5775cd3ec7 NEWS: Update German NEWS file


Commit: 5775cd3ec739cd2185cabd71d8e9682d499fd328
    https://github.com/scummvm/scummvm/commit/5775cd3ec739cd2185cabd71d8e9682d499fd328
Author: Lothar Serra Mari (mail at serra.me)
Date: 2026-06-10T14:29:38+02:00

Commit Message:
NEWS: Update German NEWS file

Changed paths:
    doc/de/NEUES.md


diff --git a/doc/de/NEUES.md b/doc/de/NEUES.md
index ed9278c9525..bbce441dd6f 100644
--- a/doc/de/NEUES.md
+++ b/doc/de/NEUES.md
@@ -32,6 +32,10 @@ Umfangreichere Informationen ??ber die ??nderungen des aktuellen Codes findest D
      sodass einige seltene Probleme mit Charakteren nun nicht mehr auftreten
 	 sollten.
 
+ MADS:
+   - Fehler in Rex Nebular behoben, der dazu f??hrt, dass das Spiel einfriert, wenn
+     das Fernglas benutzt wird.
+
  MM:
    - Verbesserte Unterst??tzung f??r Tastenk??rzel in MM1.
    - Charaktere k??nnen in MM1 Enhanced nur die Gegenst??nde verkaufen, die sie besitzen.



From noreply at scummvm.org  Wed Jun 10 14:02:58 2026
From: noreply at scummvm.org (neuromancer)
Date: Wed, 10 Jun 2026 14:02:58 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 00eb162fb89b7d0ea43318735127b7840c452c4e
Message-ID: <20260610140258.8D1CF7005CC@ron.scummvm.net>

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

Summary:
fa472c1a6f SCUMM: RA2: added missing SAUD code to better handle volume per channel
e80d90092b SCUMM: RA1: apply correct volume setting when changing the volume in-game
1758ffa0f5 SCUMM: RA1: added some missing audio code related with chunks and triggers
00eb162fb8 SCUMM: RA: normalize comments to avoid redunancies or incoherent wording


Commit: fa472c1a6fd5ff755c71be53fc4c2f591ab0b984
    https://github.com/scummvm/scummvm/commit/fa472c1a6fd5ff755c71be53fc4c2f591ab0b984
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-10T16:02:41+02:00

Commit Message:
SCUMM: RA2: added missing SAUD code to better handle volume per channel

Changed paths:
    engines/scumm/insane/rebel/rebel_audio.cpp
    engines/scumm/insane/rebel/rebel_audio.h
    engines/scumm/insane/rebel2/audio.cpp
    engines/scumm/insane/rebel2/menu.cpp
    engines/scumm/insane/rebel2/rebel.h
    engines/scumm/smush/rebel/smush_player_ra2.cpp
    engines/scumm/smush/rebel/smush_player_ra2.h


diff --git a/engines/scumm/insane/rebel/rebel_audio.cpp b/engines/scumm/insane/rebel/rebel_audio.cpp
index 9e04c4352b9..e5779d9f79b 100644
--- a/engines/scumm/insane/rebel/rebel_audio.cpp
+++ b/engines/scumm/insane/rebel/rebel_audio.cpp
@@ -23,6 +23,8 @@
 #include "audio/decoders/raw.h"
 #include "audio/mixer.h"
 
+#include "common/util.h"
+
 #include "scumm/scumm_v7.h"
 #include "scumm/smush/smush_player.h"
 #include "scumm/insane/rebel/rebel_audio.h"
@@ -32,7 +34,11 @@ namespace Scumm {
 RebelAudio::RebelAudio() : _vm(nullptr), _sampleRate(11025) {
 	for (int i = 0; i < kMaxTracks; i++) {
 		_streams[i] = nullptr;
+		_fadeStreams[i] = nullptr;
 		_trackActive[i] = false;
+		_fadeTrackActive[i] = false;
+		_streamTypes[i] = Audio::Mixer::kSFXSoundType;
+		_fadeStreamTypes[i] = Audio::Mixer::kSFXSoundType;
 	}
 }
 
@@ -41,7 +47,11 @@ void RebelAudio::init(ScummEngine_v7 *vm, int sampleRate) {
 	_sampleRate = sampleRate;
 	for (int i = 0; i < kMaxTracks; i++) {
 		_streams[i] = nullptr;
+		_fadeStreams[i] = nullptr;
 		_trackActive[i] = false;
+		_fadeTrackActive[i] = false;
+		_streamTypes[i] = Audio::Mixer::kSFXSoundType;
+		_fadeStreamTypes[i] = Audio::Mixer::kSFXSoundType;
 	}
 }
 
@@ -50,15 +60,8 @@ void RebelAudio::reset() {
 		return;
 
 	for (int i = 0; i < kMaxTracks; i++) {
-		if (_trackActive[i]) {
-			_vm->_mixer->stopHandle(_handles[i]);
-			_trackActive[i] = false;
-		}
-		if (_streams[i]) {
-			_streams[i]->finish();
-			delete _streams[i];
-			_streams[i] = nullptr;
-		}
+		stopStream(i);
+		stopFadeStream(i);
 	}
 }
 
@@ -67,16 +70,165 @@ void RebelAudio::terminate() {
 }
 
 void RebelAudio::queueData(int trackIdx, const uint8 *data, int32 size, int volume, int pan, int sampleRate) {
+	queueData(trackIdx, data, size, volume, pan, sampleRate, Audio::Mixer::kSFXSoundType);
+}
+
+void RebelAudio::stopStream(int trackIdx) {
+	if (!_vm || trackIdx < 0 || trackIdx >= kMaxTracks)
+		return;
+
+	if (_trackActive[trackIdx]) {
+		_vm->_mixer->stopHandle(_handles[trackIdx]);
+		_trackActive[trackIdx] = false;
+	}
+
+	if (_streams[trackIdx]) {
+		_streams[trackIdx]->finish();
+		delete _streams[trackIdx];
+		_streams[trackIdx] = nullptr;
+	}
+}
+
+void RebelAudio::stopFadeStream(int trackIdx) {
+	if (!_vm || trackIdx < 0 || trackIdx >= kMaxTracks)
+		return;
+
+	if (_fadeTrackActive[trackIdx]) {
+		_vm->_mixer->stopHandle(_fadeHandles[trackIdx]);
+		_fadeTrackActive[trackIdx] = false;
+	}
+
+	if (_fadeStreams[trackIdx]) {
+		_fadeStreams[trackIdx]->finish();
+		delete _fadeStreams[trackIdx];
+		_fadeStreams[trackIdx] = nullptr;
+	}
+}
+
+Audio::Mixer::SoundType RebelAudio::getSoundType(int flags) const {
+	switch (flags & TRK_TYPE_MASK) {
+	case IS_SPEECH:
+		return Audio::Mixer::kSpeechSoundType;
+	case IS_BKG_MUSIC:
+		return Audio::Mixer::kMusicSoundType;
+	case IS_SFX:
+	default:
+		return Audio::Mixer::kSFXSoundType;
+	}
+}
+
+int RebelAudio::getTrackPan(SmushPlayer *player, int idx) const {
+	if (!player || idx < 0 || idx >= player->_smushNumTracks)
+		return 0;
+
+	return CLIP<int>((int8)player->_smushTracks[idx].pan, -127, 127);
+}
+
+int RebelAudio::computeMixVolume(SmushPlayer *player, int idx) const {
+	if (!player || idx < 0 || idx >= player->_smushNumTracks)
+		return 0;
+
+	const SmushPlayer::SmushAudioTrack &track = player->_smushTracks[idx];
+	int baseVolume;
+
+	switch (track.flags & TRK_TYPE_MASK) {
+	case IS_SFX:
+		baseVolume = (player->_smushTrackVols[1] * track.volume) >> 7;
+		break;
+	case IS_BKG_MUSIC:
+		baseVolume = (player->_smushTrackVols[3] * track.volume) >> 7;
+		break;
+	case IS_SPEECH:
+		baseVolume = (player->_smushTrackVols[2] * track.volume) >> 7;
+		break;
+	default:
+		baseVolume = track.volume;
+		break;
+	}
+
+	int mixVolume = (baseVolume * player->_smushTrackVols[0]) / 127;
+	if ((track.flags & TRK_TYPE_MASK) == IS_BKG_MUSIC && player->isChanActive(CHN_SPEECH))
+		mixVolume = (mixVolume * player->_gainReductionMultiplier) >> 8;
+
+	return CLIP<int>(mixVolume, 0, 127);
+}
+
+void RebelAudio::updateTrackMixerState(SmushPlayer *player, int idx) {
+	if (!_vm || !player || idx < 0 || idx >= player->_smushNumTracks || idx >= kMaxTracks)
+		return;
+
+	if (_trackActive[idx]) {
+		const int scaledVolume = (computeMixVolume(player, idx) * Audio::Mixer::kMaxChannelVolume) / 127;
+		const int scaledPan = (getTrackPan(player, idx) * 127) / 128;
+		_vm->_mixer->setChannelVolume(_handles[idx], scaledVolume);
+		_vm->_mixer->setChannelBalance(_handles[idx], scaledPan);
+	}
+}
+
+void RebelAudio::queueFadeData(int trackIdx, const uint8 *data, int32 size, int volume, int pan, int sampleRate, Audio::Mixer::SoundType soundType) {
 	if (!_vm || trackIdx < 0 || trackIdx >= kMaxTracks || size <= 0 || !data)
 		return;
 
 	const int sourceRate = sampleRate > 0 ? sampleRate : _sampleRate;
 
+	if (_fadeStreams[trackIdx] && _fadeStreamTypes[trackIdx] != soundType)
+		stopFadeStream(trackIdx);
+
+	if (!_fadeStreams[trackIdx]) {
+		debugC(DEBUG_INSANE, "RebelAudio: Creating fade audio stream for track %d at %d Hz", trackIdx, _sampleRate);
+		_fadeStreams[trackIdx] = Audio::makeQueuingAudioStream(_sampleRate, false);
+		_fadeStreamTypes[trackIdx] = soundType;
+		_fadeTrackActive[trackIdx] = true;
+		_vm->_mixer->playStream(soundType, &_fadeHandles[trackIdx],
+			_fadeStreams[trackIdx], -1, Audio::Mixer::kMaxChannelVolume, 0,
+			DisposeAfterUse::NO);
+	}
+
+	int32 queueSize = size;
+	if (sourceRate != _sampleRate) {
+		queueSize = ((int64)size * _sampleRate + sourceRate / 2) / sourceRate;
+		if (queueSize <= 0)
+			queueSize = 1;
+	}
+
+	byte *audioCopy = (byte *)malloc(queueSize);
+	if (!audioCopy)
+		return;
+
+	if (sourceRate == _sampleRate) {
+		memcpy(audioCopy, data, queueSize);
+	} else {
+		for (int32 i = 0; i < queueSize; i++) {
+			int32 srcPos = ((int64)i * sourceRate) / _sampleRate;
+			if (srcPos >= size)
+				srcPos = size - 1;
+			audioCopy[i] = data[srcPos];
+		}
+	}
+
+	_fadeStreams[trackIdx]->queueBuffer(audioCopy, queueSize, DisposeAfterUse::YES, Audio::FLAG_UNSIGNED);
+
+	const int scaledVolume = (CLIP<int>(volume, 0, 127) * Audio::Mixer::kMaxChannelVolume) / 127;
+	const int scaledPan = (CLIP<int>(pan, -127, 127) * 127) / 128;
+	_vm->_mixer->setChannelVolume(_fadeHandles[trackIdx], scaledVolume);
+	_vm->_mixer->setChannelBalance(_fadeHandles[trackIdx], scaledPan);
+}
+
+void RebelAudio::queueData(int trackIdx, const uint8 *data, int32 size, int volume, int pan, int sampleRate, Audio::Mixer::SoundType soundType) {
+	if (!_vm || trackIdx < 0 || trackIdx >= kMaxTracks || size <= 0 || !data)
+		return;
+
+	const int sourceRate = sampleRate > 0 ? sampleRate : _sampleRate;
+
+	if (_streams[trackIdx] && _streamTypes[trackIdx] != soundType)
+		stopStream(trackIdx);
+
 	if (!_streams[trackIdx]) {
 		debugC(DEBUG_INSANE, "RebelAudio: Creating audio stream for track %d at %d Hz", trackIdx, _sampleRate);
 		_streams[trackIdx] = Audio::makeQueuingAudioStream(_sampleRate, false);
+		_streamTypes[trackIdx] = soundType;
 		_trackActive[trackIdx] = true;
-		_vm->_mixer->playStream(Audio::Mixer::kSFXSoundType, &_handles[trackIdx],
+		_vm->_mixer->playStream(soundType, &_handles[trackIdx],
 			_streams[trackIdx], -1, Audio::Mixer::kMaxChannelVolume, 0,
 			DisposeAfterUse::NO);
 	}
@@ -105,12 +257,63 @@ void RebelAudio::queueData(int trackIdx, const uint8 *data, int32 size, int volu
 
 	_streams[trackIdx]->queueBuffer(audioCopy, queueSize, DisposeAfterUse::YES, Audio::FLAG_UNSIGNED);
 
-	const int scaledVolume = (volume * Audio::Mixer::kMaxChannelVolume) / 127;
-	const int scaledPan = (pan * 127) / 128;
+	const int scaledVolume = (CLIP<int>(volume, 0, 127) * Audio::Mixer::kMaxChannelVolume) / 127;
+	const int scaledPan = (CLIP<int>(pan, -127, 127) * 127) / 128;
 	_vm->_mixer->setChannelVolume(_handles[trackIdx], scaledVolume);
 	_vm->_mixer->setChannelBalance(_handles[trackIdx], scaledPan);
 }
 
+void RebelAudio::setGroupVolume(SmushPlayer *player, int groupId, int volume) {
+	if (!player)
+		return;
+
+	volume = CLIP<int>(volume, 0, 127);
+	switch (groupId) {
+	case GRP_MASTER:
+		player->_smushTrackVols[0] = volume;
+		for (int i = 0; i < player->_smushNumTracks; i++)
+			updateTrackMixerState(player, i);
+		return;
+	case GRP_SFX:
+		player->_smushTrackVols[1] = volume;
+		for (int i = 0; i < player->_smushNumTracks; i++)
+			updateTrackMixerState(player, i);
+		return;
+	case GRP_BKGMUS:
+		player->_smushTrackVols[3] = volume;
+		for (int i = 0; i < player->_smushNumTracks; i++)
+			updateTrackMixerState(player, i);
+		return;
+	case GRP_SPEECH:
+		player->_smushTrackVols[2] = volume;
+		for (int i = 0; i < player->_smushNumTracks; i++)
+			updateTrackMixerState(player, i);
+		return;
+	default:
+		break;
+	}
+
+	for (int i = 0; i < player->_smushNumTracks; i++) {
+		if (player->_smushTracks[i].groupId == groupId) {
+			player->_smushTracks[i].volume = volume;
+			updateTrackMixerState(player, i);
+		}
+	}
+}
+
+void RebelAudio::setGroupPan(SmushPlayer *player, int groupId, int pan) {
+	if (!player)
+		return;
+
+	pan = CLIP<int>(pan, -127, 127);
+	for (int i = 0; i < player->_smushNumTracks; i++) {
+		if (player->_smushTracks[i].groupId == groupId) {
+			player->_smushTracks[i].pan = (byte)(int8)pan;
+			updateTrackMixerState(player, i);
+		}
+	}
+}
+
 bool RebelAudio::processAudioCodes(SmushPlayer *player, int idx, int32 &tmpFeedSize, int &mixVolume) {
 	uint8 *code, *buf, subcode, value;
 	int chunk;
@@ -193,13 +396,11 @@ bool RebelAudio::processAudioCodes(SmushPlayer *player, int idx, int32 &tmpFeedS
 			switch (code[2]) {
 			case SAUD_VALUEID_ALL_VOLS:
 				player->_smushTrackVols[0] = code[3];
+				mixVolume = computeMixVolume(player, idx);
 				break;
 			case SAUD_VALUEID_TRK_VOL:
 				player->_smushTracks[idx].volume = code[3];
-				mixVolume = (player->_smushTrackVols[0] * player->_smushTracks[idx].volume) / 127;
-
-				if ((player->_smushTracks[idx].flags & TRK_TYPE_MASK) == IS_BKG_MUSIC && player->isChanActive(CHN_SPEECH))
-					mixVolume = (mixVolume * player->_gainReductionMultiplier) >> 8;
+				mixVolume = computeMixVolume(player, idx);
 				break;
 			case SAUD_VALUEID_TRK_PAN:
 				player->_smushTracks[idx].pan = code[3];
@@ -215,9 +416,11 @@ bool RebelAudio::processAudioCodes(SmushPlayer *player, int idx, int32 &tmpFeedS
 			switch (code[2]) {
 			case SAUD_VALUEID_ALL_VOLS:
 				player->_smushTrackVols[0] += code[3];
+				mixVolume = computeMixVolume(player, idx);
 				break;
 			case SAUD_VALUEID_TRK_VOL:
 				player->_smushTracks[idx].volume += code[3];
+				mixVolume = computeMixVolume(player, idx);
 				break;
 			case SAUD_VALUEID_TRK_PAN:
 				player->_smushTracks[idx].pan += code[3];
@@ -308,12 +511,16 @@ void RebelAudio::processFrame(SmushPlayer *player, int16 feedSize) {
 	if (player->_paused)
 		return;
 
+	const int kMaxMixChunkSize = 0x100;
+	bool speechIsPlaying = false;
+
 	if (player->_smushTracksNeedInit) {
 		player->_smushTracksNeedInit = false;
 		for (int i = 0; i < SMUSH_MAX_TRACKS; i++) {
 			player->_smushDispatch[i].fadeRemaining = 0;
 			player->_smushDispatch[i].fadeVolume = 0;
 			player->_smushDispatch[i].fadeSampleRate = 0;
+			player->_smushDispatch[i].volumeStep = 0;
 			player->_smushDispatch[i].elapsedAudio = 0;
 			player->_smushDispatch[i].audioLength = 0;
 		}
@@ -323,35 +530,104 @@ void RebelAudio::processFrame(SmushPlayer *player, int16 feedSize) {
 		SmushPlayer::SmushAudioTrack &track = player->_smushTracks[i];
 		SmushPlayer::SmushAudioDispatch &dispatch = player->_smushDispatch[i];
 
-		if (track.state == TRK_STATE_INACTIVE || !track.blockPtr)
+		if (!track.blockPtr)
 			continue;
 
-		bool isPlayableTrack =
+		const bool isPlayableTrack =
 			((track.flags & TRK_TYPE_MASK) == IS_SPEECH && player->isChanActive(CHN_SPEECH)) ||
 			((track.flags & TRK_TYPE_MASK) == IS_BKG_MUSIC && player->isChanActive(CHN_BKGMUS)) ||
 			((track.flags & TRK_TYPE_MASK) == IS_SFX && player->isChanActive(CHN_OTHER));
 
-		if (!isPlayableTrack)
-			continue;
+		const Audio::Mixer::SoundType soundType = getSoundType(track.flags);
+		int mixVolume = computeMixVolume(player, i);
+
+		if (track.state == TRK_STATE_FADING && dispatch.state == TRK_STATE_PLAYING &&
+				dispatch.dataBuf && dispatch.dataSize > 0 && track.fadeBuf) {
+			const int32 fadeStartOffset = dispatch.audioRemaining % dispatch.dataSize;
+			dispatch.fadeRemaining = SMUSH_FADE_SIZE;
+			dispatch.fadeVolume = track.volume;
+			dispatch.fadeSampleRate = dispatch.sampleRate;
+
+			memset(track.fadeBuf, 127, dispatch.fadeRemaining);
+			int32 copySize = dispatch.dataSize - fadeStartOffset;
+			if (copySize > dispatch.fadeRemaining)
+				copySize = dispatch.fadeRemaining;
+			if (copySize > 0)
+				memcpy(track.fadeBuf, &dispatch.dataBuf[fadeStartOffset], copySize);
+
+			dispatch.volumeStep = 0;
+			stopFadeStream(i);
+			stopStream(i);
+		} else if (track.state == TRK_STATE_PLAYING && dispatch.dataBuf && dispatch.dataSize > 0 && track.fadeBuf) {
+			if (dispatch.audioRemaining < track.availableSize - track.dataSize + 15000 && track.availableSize < track.sdatSize) {
+				dispatch.volumeStep = 0;
+				int32 mixInFrameCount = track.availableSize - dispatch.audioRemaining - 15000;
+
+				if (mixInFrameCount > dispatch.currentOffset)
+					mixInFrameCount = dispatch.currentOffset;
+
+				if (dispatch.audioRemaining + mixInFrameCount > track.sdatSize - dispatch.dataSize)
+					mixInFrameCount = track.sdatSize - dispatch.dataSize - dispatch.audioRemaining;
+
+				if (mixInFrameCount > 0) {
+					const int32 fadeStartOffset = dispatch.audioRemaining % dispatch.dataSize;
+					dispatch.fadeRemaining = SMUSH_FADE_SIZE;
+					dispatch.fadeSampleRate = dispatch.sampleRate;
+
+					memset(track.fadeBuf, 127, dispatch.fadeRemaining);
+					int32 copySize = dispatch.dataSize - fadeStartOffset;
+					if (copySize > dispatch.fadeRemaining)
+						copySize = dispatch.fadeRemaining;
+					if (copySize > 0)
+						memcpy(track.fadeBuf, &dispatch.dataBuf[fadeStartOffset], copySize);
+
+					dispatch.audioRemaining += mixInFrameCount;
+					dispatch.currentOffset -= mixInFrameCount;
+					stopFadeStream(i);
+				}
+			}
+		}
 
-		int baseVolume;
-		switch (track.flags & TRK_TYPE_MASK) {
-		case IS_SFX:
-			baseVolume = (player->_smushTrackVols[1] * track.volume) >> 7;
-			break;
-		case IS_BKG_MUSIC:
-			baseVolume = (player->_smushTrackVols[3] * track.volume) >> 7;
-			break;
-		case IS_SPEECH:
-			baseVolume = (player->_smushTrackVols[2] * track.volume) >> 7;
-			break;
-		default:
-			baseVolume = track.volume;
-			break;
+		if (dispatch.fadeRemaining && dispatch.fadeSampleRate > 0 && player->_smushAudioSampleRate > 0 && track.fadeBuf) {
+			int32 maxFadeChunkSize = ((int64)dispatch.fadeSampleRate * feedSize + player->_smushAudioSampleRate - 1) / player->_smushAudioSampleRate;
+			if (maxFadeChunkSize <= 0)
+				maxFadeChunkSize = 1;
+
+			int32 fadeRemaining = MIN<int32>(dispatch.fadeRemaining, maxFadeChunkSize);
+			while (fadeRemaining > 0) {
+				int32 fadeInFrameCount = MIN<int32>(fadeRemaining, kMaxMixChunkSize);
+				const int fadeOffset = SMUSH_FADE_SIZE - dispatch.fadeRemaining;
+
+				if (isPlayableTrack) {
+					int fadeBaseVolume;
+					switch (track.flags & TRK_TYPE_MASK) {
+					case IS_SFX:
+						fadeBaseVolume = (player->_smushTrackVols[1] * dispatch.fadeVolume) >> 7;
+						break;
+					case IS_BKG_MUSIC:
+						fadeBaseVolume = (player->_smushTrackVols[3] * dispatch.fadeVolume) >> 7;
+						break;
+					case IS_SPEECH:
+						fadeBaseVolume = (player->_smushTrackVols[2] * dispatch.fadeVolume) >> 7;
+						break;
+					default:
+						fadeBaseVolume = dispatch.fadeVolume;
+						break;
+					}
+					const int fadeVolume = CLIP<int>(dispatch.fadeRemaining * fadeBaseVolume * player->_smushTrackVols[0] / (SMUSH_FADE_SIZE * 127), 0, 127);
+					queueFadeData(i, &track.fadeBuf[fadeOffset], fadeInFrameCount, fadeVolume, getTrackPan(player, i), dispatch.fadeSampleRate, soundType);
+				}
+
+				fadeRemaining -= fadeInFrameCount;
+				dispatch.fadeRemaining -= fadeInFrameCount;
+			}
 		}
-		int mixVolume = baseVolume * player->_smushTrackVols[0] / 127;
 
 		if (track.state == TRK_STATE_FADING) {
+			if (dispatch.state != TRK_STATE_PLAYING)
+				dispatch.volumeStep = 16;
+
+			stopStream(i);
 			dispatch.headerPtr = track.dataBuf;
 			dispatch.dataBuf = track.subChunkPtr;
 			dispatch.dataSize = track.dataSize;
@@ -366,15 +642,21 @@ void RebelAudio::processFrame(SmushPlayer *player, int16 feedSize) {
 			while (tmpFeedSize > 0) {
 				int32 mixInFrameCount = dispatch.currentOffset;
 
-				if (mixInFrameCount > 0 && dispatch.dataBuf && dispatch.dataSize > 0) {
+				if (mixInFrameCount > 0) {
+					if (!dispatch.dataBuf || dispatch.dataSize <= 0)
+						break;
+
 					if (dispatch.audioRemaining < 0)
 						dispatch.audioRemaining = 0;
 
 					int32 offset = dispatch.audioRemaining % dispatch.dataSize;
+					int32 maxFrames = mixInFrameCount;
 
 					if (dispatch.sampleRate > 0 && player->_smushAudioSampleRate > 0) {
-						int32 maxFrames = ((int64)dispatch.sampleRate * tmpFeedSize +
+						maxFrames = ((int64)dispatch.sampleRate * tmpFeedSize +
 							player->_smushAudioSampleRate - 1) / player->_smushAudioSampleRate;
+						if (maxFrames <= 0)
+							maxFrames = 1;
 						if (mixInFrameCount > maxFrames)
 							mixInFrameCount = maxFrames;
 					}
@@ -382,31 +664,57 @@ void RebelAudio::processFrame(SmushPlayer *player, int16 feedSize) {
 					if (offset + mixInFrameCount > dispatch.dataSize)
 						mixInFrameCount = dispatch.dataSize - offset;
 
-					if (dispatch.audioRemaining + mixInFrameCount > track.availableSize) {
-						mixInFrameCount = track.availableSize - dispatch.audioRemaining;
-						if (mixInFrameCount <= 0) {
-							track.state = TRK_STATE_ENDING;
-							break;
-						}
+					if (dispatch.audioRemaining + mixInFrameCount <= track.availableSize) {
+						if (dispatch.volumeStep < 16)
+							dispatch.volumeStep++;
+
+						if (mixInFrameCount > kMaxMixChunkSize)
+							mixInFrameCount = kMaxMixChunkSize;
+
+						track.state = TRK_STATE_PLAYING;
+
+						if (!speechIsPlaying)
+							speechIsPlaying = (track.flags & TRK_TYPE_MASK) == IS_SPEECH;
+					} else {
+						if (dispatch.volumeStep)
+							dispatch.volumeStep--;
+
+						track.state = TRK_STATE_ENDING;
+
+						if (mixInFrameCount > kMaxMixChunkSize)
+							mixInFrameCount = kMaxMixChunkSize;
+
+						dispatch.audioRemaining -= mixInFrameCount;
+						dispatch.currentOffset += mixInFrameCount;
+						offset = dispatch.audioRemaining % dispatch.dataSize;
 					}
 
 					if (mixInFrameCount > 0) {
 						if (!dispatch.dataBuf || offset < 0 || offset + mixInFrameCount > dispatch.dataSize)
 							break;
 
-						track.state = TRK_STATE_PLAYING;
-						queueData(i, &dispatch.dataBuf[offset], mixInFrameCount, mixVolume, track.pan, dispatch.sampleRate);
+						int32 consumedFeed;
+						if (dispatch.sampleRate > 0 && player->_smushAudioSampleRate > 0) {
+							if (mixInFrameCount == maxFrames) {
+								consumedFeed = tmpFeedSize;
+							} else {
+								consumedFeed = ((int64)mixInFrameCount * player->_smushAudioSampleRate +
+									dispatch.sampleRate - 1) / dispatch.sampleRate;
+								if (consumedFeed <= 0)
+									consumedFeed = 1;
+							}
+						} else {
+							consumedFeed = mixInFrameCount;
+						}
+
+						if (isPlayableTrack)
+							queueData(i, &dispatch.dataBuf[offset], mixInFrameCount,
+								(mixVolume * dispatch.volumeStep) >> 4, getTrackPan(player, i),
+								dispatch.sampleRate, soundType);
 
 						dispatch.currentOffset -= mixInFrameCount;
 						dispatch.audioRemaining += mixInFrameCount;
-
-						if (dispatch.sampleRate > 0) {
-							int32 consumedFeed = ((int64)mixInFrameCount * player->_smushAudioSampleRate +
-								dispatch.sampleRate - 1) / dispatch.sampleRate;
-							tmpFeedSize -= consumedFeed;
-						} else {
-							tmpFeedSize -= mixInFrameCount;
-						}
+						tmpFeedSize -= consumedFeed;
 					}
 				}
 
@@ -422,6 +730,18 @@ void RebelAudio::processFrame(SmushPlayer *player, int16 feedSize) {
 		track.audioRemaining = dispatch.audioRemaining;
 		dispatch.state = track.state;
 	}
+
+	if (speechIsPlaying) {
+		if (player->_gainReductionMultiplier > player->_gainReductionLowerBound) {
+			player->_gainReductionMultiplier -= (feedSize * 2 * player->_gainReductionFactor) >> 13;
+			if (player->_gainReductionMultiplier < player->_gainReductionLowerBound)
+				player->_gainReductionMultiplier = player->_gainReductionLowerBound;
+		}
+	} else if (player->_gainReductionMultiplier < 256) {
+		player->_gainReductionMultiplier += (feedSize * 2 * player->_gainReductionFactor) >> 15;
+		if (player->_gainReductionMultiplier > 256)
+			player->_gainReductionMultiplier = 256;
+	}
 }
 
 } // End of namespace Scumm
diff --git a/engines/scumm/insane/rebel/rebel_audio.h b/engines/scumm/insane/rebel/rebel_audio.h
index 4b13efe5e06..20ae8d10a0d 100644
--- a/engines/scumm/insane/rebel/rebel_audio.h
+++ b/engines/scumm/insane/rebel/rebel_audio.h
@@ -45,16 +45,31 @@ public:
 
 	void queueData(int trackIdx, const uint8 *data, int32 size, int volume, int pan, int sampleRate = 0);
 	void processFrame(SmushPlayer *player, int16 feedSize);
+	void setGroupVolume(SmushPlayer *player, int groupId, int volume);
+	void setGroupPan(SmushPlayer *player, int groupId, int pan);
 
 private:
 	static const int kMaxTracks = 4;
 
 	bool processAudioCodes(SmushPlayer *player, int idx, int32 &tmpFeedSize, int &mixVolume);
+	int computeMixVolume(SmushPlayer *player, int idx) const;
+	Audio::Mixer::SoundType getSoundType(int flags) const;
+	int getTrackPan(SmushPlayer *player, int idx) const;
+	void updateTrackMixerState(SmushPlayer *player, int idx);
+	void stopStream(int trackIdx);
+	void stopFadeStream(int trackIdx);
+	void queueData(int trackIdx, const uint8 *data, int32 size, int volume, int pan, int sampleRate, Audio::Mixer::SoundType soundType);
+	void queueFadeData(int trackIdx, const uint8 *data, int32 size, int volume, int pan, int sampleRate, Audio::Mixer::SoundType soundType);
 
 	ScummEngine_v7 *_vm;
 	Audio::QueuingAudioStream *_streams[kMaxTracks];
+	Audio::QueuingAudioStream *_fadeStreams[kMaxTracks];
 	Audio::SoundHandle _handles[kMaxTracks];
+	Audio::SoundHandle _fadeHandles[kMaxTracks];
 	bool _trackActive[kMaxTracks];
+	bool _fadeTrackActive[kMaxTracks];
+	Audio::Mixer::SoundType _streamTypes[kMaxTracks];
+	Audio::Mixer::SoundType _fadeStreamTypes[kMaxTracks];
 	int _sampleRate;
 };
 
diff --git a/engines/scumm/insane/rebel2/audio.cpp b/engines/scumm/insane/rebel2/audio.cpp
index 3001e4c4908..bf3b26e9122 100644
--- a/engines/scumm/insane/rebel2/audio.cpp
+++ b/engines/scumm/insane/rebel2/audio.cpp
@@ -20,6 +20,7 @@
  */
 
 #include "common/system.h"
+#include "common/util.h"
 
 #include "scumm/file.h"
 #include "scumm/scumm_v7.h"
@@ -175,6 +176,8 @@ void InsaneRebel2::playSfx(int slot, int volume, int pan) {
 	if (slot < 0 || slot >= kRA2NumSfx || !_sfxData[slot] || _sfxSize[slot] == 0) {
 		return;
 	}
+	if (_player && !_player->isChanActive(CHN_OTHER))
+		return;
 
 	// Stop any previous instance of this SFX slot
 	_vm->_mixer->stopHandle(_sfxHandles[slot]);
@@ -190,13 +193,18 @@ void InsaneRebel2::playSfx(int slot, int volume, int pan) {
 	Audio::SeekableAudioStream *stream = Audio::makeRawStream(
 		pcmCopy, _sfxSize[slot], 11025, Audio::FLAG_UNSIGNED, DisposeAfterUse::YES);
 
-	// Scale volume from 0-127 to ScummVM's 0-255 range
-	int scaledVolume = (volume * Audio::Mixer::kMaxChannelVolume) / 127;
+	int mixVolume = CLIP(volume, 0, 127);
+	if (_player) {
+		const int baseVolume = (_player->_smushTrackVols[1] * mixVolume) >> 7;
+		mixVolume = (baseVolume * _player->_smushTrackVols[0]) / 127;
+	}
+	const int scaledVolume = (mixVolume * Audio::Mixer::kMaxChannelVolume) / 127;
+	const int clampedPan = CLIP(pan, -127, 127);
 
 	_vm->_mixer->playStream(Audio::Mixer::kSFXSoundType, &_sfxHandles[slot],
-		stream, -1, scaledVolume, pan);
+		stream, -1, scaledVolume, clampedPan);
 
-	debugC(DEBUG_INSANE, "InsaneRebel2::playSfx: slot=%d vol=%d pan=%d size=%d", slot, volume, pan, _sfxSize[slot]);
+	debugC(DEBUG_INSANE, "InsaneRebel2::playSfx: slot=%d vol=%d pan=%d size=%d", slot, mixVolume, clampedPan, _sfxSize[slot]);
 }
 
 // loadAuxSfx -- Load sound data into auxiliary buffer (FUN_004118df).
@@ -222,6 +230,8 @@ void InsaneRebel2::playAuxSfx(int buffer, int volume, int pan) {
 	if (buffer < 0 || buffer >= kRA2NumAuxSfx || !_auxSfxData[buffer] || _auxSfxSize[buffer] == 0) {
 		return;
 	}
+	if (_player && !_player->isChanActive(CHN_OTHER))
+		return;
 
 	_vm->_mixer->stopHandle(_auxSfxHandles[buffer]);
 
@@ -255,12 +265,18 @@ void InsaneRebel2::playAuxSfx(int buffer, int volume, int pan) {
 	Audio::SeekableAudioStream *stream = Audio::makeRawStream(
 		pcmCopy, pcmSize, 11025, Audio::FLAG_UNSIGNED, DisposeAfterUse::YES);
 
-	int scaledVolume = (volume * Audio::Mixer::kMaxChannelVolume) / 127;
+	int mixVolume = CLIP(volume, 0, 127);
+	if (_player) {
+		const int baseVolume = (_player->_smushTrackVols[1] * mixVolume) >> 7;
+		mixVolume = (baseVolume * _player->_smushTrackVols[0]) / 127;
+	}
+	const int scaledVolume = (mixVolume * Audio::Mixer::kMaxChannelVolume) / 127;
+	const int clampedPan = CLIP(pan, -127, 127);
 
 	_vm->_mixer->playStream(Audio::Mixer::kSFXSoundType, &_auxSfxHandles[buffer],
-		stream, -1, scaledVolume, pan);
+		stream, -1, scaledVolume, clampedPan);
 
-	debugC(DEBUG_INSANE, "InsaneRebel2::playAuxSfx: buffer=%d vol=%d pan=%d pcmSize=%d", buffer, volume, pan, pcmSize);
+	debugC(DEBUG_INSANE, "InsaneRebel2::playAuxSfx: buffer=%d vol=%d pan=%d pcmSize=%d", buffer, mixVolume, clampedPan, pcmSize);
 }
 
 } // End of namespace Scumm
diff --git a/engines/scumm/insane/rebel2/menu.cpp b/engines/scumm/insane/rebel2/menu.cpp
index ad27d5c391d..7e26e6dd250 100644
--- a/engines/scumm/insane/rebel2/menu.cpp
+++ b/engines/scumm/insane/rebel2/menu.cpp
@@ -45,6 +45,13 @@ namespace Scumm {
 // ---------------------------------------------------------------------------
 // Emulates retail menu system from FUN_004147B2 and FUN_0041FDC8.
 
+static void setRebel2MixerVolume(ScummEngine_v7 *vm, int volumeLevel) {
+	const int mixerVolume = CLIP<int>(volumeLevel * 2, 0, (int)Audio::Mixer::kMaxMixerVolume);
+	vm->_mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, mixerVolume);
+	vm->_mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, mixerVolume);
+	vm->_mixer->setVolumeForSoundType(Audio::Mixer::kSpeechSoundType, mixerVolume);
+}
+
 void InsaneRebel2::resetMenu() {
 	_menuSelection = 0;
 	_menuInactivityTimer = 0;
@@ -1891,10 +1898,7 @@ int InsaneRebel2::processOptionsInput() {
 				// Volume slider: decrease by 4 (original step size)
 				if (_optionsSelection == 6) {
 					_optVolumeLevel = MAX(0, _optVolumeLevel - 4);
-					_vm->_mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType,
-					    CLIP<int>(_optVolumeLevel * 2, 0, (int)Audio::Mixer::kMaxMixerVolume));
-					_vm->_mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType,
-					    CLIP<int>(_optVolumeLevel * 2, 0, (int)Audio::Mixer::kMaxMixerVolume));
+					setRebel2MixerVolume(_vm, _optVolumeLevel);
 				}
 				return -1;
 
@@ -1902,10 +1906,7 @@ int InsaneRebel2::processOptionsInput() {
 				// Volume slider: increase by 4
 				if (_optionsSelection == 6) {
 					_optVolumeLevel = MIN(127, _optVolumeLevel + 4);
-					_vm->_mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType,
-					    CLIP<int>(_optVolumeLevel * 2, 0, (int)Audio::Mixer::kMaxMixerVolume));
-					_vm->_mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType,
-					    CLIP<int>(_optVolumeLevel * 2, 0, (int)Audio::Mixer::kMaxMixerVolume));
+					setRebel2MixerVolume(_vm, _optVolumeLevel);
 				}
 				return -1;
 
diff --git a/engines/scumm/insane/rebel2/rebel.h b/engines/scumm/insane/rebel2/rebel.h
index 1e26ac3fb71..ca5c902b908 100644
--- a/engines/scumm/insane/rebel2/rebel.h
+++ b/engines/scumm/insane/rebel2/rebel.h
@@ -219,7 +219,7 @@ public:
 	// Original settings array at DAT_00482e20[0..4]:
 	//   [0]=text, [1]=music, [2]=voices, [3]=sound, [4]=hidden abort flag
 	// Additional flags: DAT_0047a7fe (controls normal/flipped), DAT_0047a80a (rapid fire)
-	// Volume: DAT_0047a804 (0-127), SFX vol: DAT_0047a802 (127-768)
+	// Volume: DAT_0047a804 (0-127), brightness/gamma: DAT_0047a802 (127-768)
 
 	void showOptionsMenu();
 	void drawOptionsOverlay(byte *renderBitmap, int pitch, int width, int height);
diff --git a/engines/scumm/smush/rebel/smush_player_ra2.cpp b/engines/scumm/smush/rebel/smush_player_ra2.cpp
index 1f9e36a6834..163c1923f60 100644
--- a/engines/scumm/smush/rebel/smush_player_ra2.cpp
+++ b/engines/scumm/smush/rebel/smush_player_ra2.cpp
@@ -217,6 +217,7 @@ void SmushPlayerRebel2::ra2InitAudioTrackSizes() {
 void SmushPlayerRebel2::initGameVideoState() {
 	_ra2PendingAnimHeaderPalette = false;
 	_ra2UsingGameplaySurface = false;
+	_smushAudioTable[100] = 0;
 
 	// Re-push the SMUSH palette to the system. Videos like O_LEVEL.SAN
 	// have no NPAL chunk and inherit the palette from the previous video.
@@ -1308,6 +1309,12 @@ bool SmushPlayerRebel2::handleGameSkipChunk(uint32 subType, int32 subSize, Commo
 		return true;
 	}
 
+	if (subType == MKTAG('P','S','A','D')) {
+		if (!_compressedFileMode && !isFastForwardingCurrentFrame())
+			ra2HandleFrameAudioChunk(subType, subSize, b);
+		return true;
+	}
+
 	if (subType == MKTAG('X','P','A','L')) {
 		ra2HandleDeltaPalette(subSize, b);
 		return true;
@@ -1316,6 +1323,68 @@ bool SmushPlayerRebel2::handleGameSkipChunk(uint32 subType, int32 subSize, Commo
 	return false;
 }
 
+void SmushPlayerRebel2::ra2HandleFrameAudioChunk(uint32 subType, int32 subSize, Common::SeekableReadStream &b) {
+	if (subSize <= 0)
+		return;
+
+	uint8 *audioChunk = (uint8 *)malloc(subSize + 8);
+	if (!audioChunk) {
+		b.skip(subSize);
+		return;
+	}
+
+	WRITE_BE_UINT32(audioChunk, subType);
+	WRITE_BE_UINT32(audioChunk + 4, subSize);
+	b.read(audioChunk + 8, subSize);
+	ra2FeedAudio(audioChunk, 0, 127, 0, 0);
+	free(audioChunk);
+}
+
+void SmushPlayerRebel2::ra2FeedAudio(uint8 *srcBuf, int groupId, int volume, int pan, int16 flags) {
+	int32 maxFrames;
+	uint16 trkId, index;
+
+	if (!_smushAudioInitialized)
+		return;
+
+	if (srcBuf[8] == 0 && srcBuf[9] == 0 && srcBuf[12] == 0 && srcBuf[13] == 0 && srcBuf[16] == 0 && srcBuf[17] == 0) {
+		trkId = READ_BE_INT16(&srcBuf[10]);
+		index = READ_BE_INT16(&srcBuf[14]);
+		maxFrames = READ_BE_INT16(&srcBuf[18]);
+
+		handleSAUDChunk(
+			srcBuf + 20,
+			READ_BE_UINT32(&srcBuf[4]) - 12,
+			groupId,
+			volume,
+			pan,
+			flags,
+			trkId,
+			index,
+			maxFrames);
+	} else {
+		trkId = READ_LE_INT16(&srcBuf[8]);
+		index = READ_LE_INT16(&srcBuf[10]);
+		maxFrames = READ_LE_INT16(&srcBuf[12]);
+		flags |= READ_LE_INT16(&srcBuf[14]);
+		volume = (volume * srcBuf[16]) >> 7;
+
+		const int panDelta = (int8)srcBuf[17];
+		const int effPan = (panDelta == -128) ? 128 : pan + panDelta;
+
+		handleSAUDChunk(
+			srcBuf + 18,
+			READ_BE_UINT32(&srcBuf[4]) - 10,
+			groupId,
+			volume,
+			effPan,
+			flags,
+			trkId,
+			index,
+			maxFrames);
+	}
+}
+
 void SmushPlayerRebel2::handleGameGost(int32 subSize, Common::SeekableReadStream &b) {
 	ra2HandleGost(subSize, b);
 }
diff --git a/engines/scumm/smush/rebel/smush_player_ra2.h b/engines/scumm/smush/rebel/smush_player_ra2.h
index 45a2869db2a..cd5d2351776 100644
--- a/engines/scumm/smush/rebel/smush_player_ra2.h
+++ b/engines/scumm/smush/rebel/smush_player_ra2.h
@@ -81,6 +81,8 @@ private:
 						  int left, int top, int width, int height);
 	void ra2HandleGost(int32 subSize, Common::SeekableReadStream &b);
 	void ra2InitAudioTrackSizes();
+	void ra2HandleFrameAudioChunk(uint32 subType, int32 subSize, Common::SeekableReadStream &b);
+	void ra2FeedAudio(uint8 *srcBuf, int groupId, int volume, int pan, int16 flags);
 
 	// LOAD chunk streaming buffer (embedded resource data)
 	byte *_loadBuffer;


Commit: e80d90092b47c1c6162cc8f22fd1b0410ca21afc
    https://github.com/scummvm/scummvm/commit/e80d90092b47c1c6162cc8f22fd1b0410ca21afc
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-10T16:02:41+02:00

Commit Message:
SCUMM: RA1: apply correct volume setting when changing the volume in-game

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


diff --git a/engines/scumm/insane/rebel1/menu.cpp b/engines/scumm/insane/rebel1/menu.cpp
index 6a02456d482..7886ae46923 100644
--- a/engines/scumm/insane/rebel1/menu.cpp
+++ b/engines/scumm/insane/rebel1/menu.cpp
@@ -76,8 +76,10 @@ static int getRebel1MenuAxisDirection(int16 axisValue) {
 
 static void setRebel1Volume(ScummEngine_v7 *vm, int &volume, int delta) {
 	volume = CLIP<int>(volume + delta, 0, 127);
-	vm->_mixer->setVolumeForSoundType(Audio::Mixer::kPlainSoundType,
-		(volume * Audio::Mixer::kMaxChannelVolume) / 127);
+	const int mixerVolume = (volume * Audio::Mixer::kMaxChannelVolume) / 127;
+	vm->_mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, mixerVolume);
+	vm->_mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, mixerVolume);
+	vm->_mixer->setVolumeForSoundType(Audio::Mixer::kSpeechSoundType, mixerVolume);
 	ConfMan.setInt("music_volume", (volume * 256) / 127);
 	ConfMan.setInt("sfx_volume", (volume * 256) / 127);
 	ConfMan.setInt("speech_volume", (volume * 256) / 127);
diff --git a/engines/scumm/insane/rebel1/rebel.cpp b/engines/scumm/insane/rebel1/rebel.cpp
index b1e67bafc9c..92af6c0367b 100644
--- a/engines/scumm/insane/rebel1/rebel.cpp
+++ b/engines/scumm/insane/rebel1/rebel.cpp
@@ -356,7 +356,7 @@ InsaneRebel1::InsaneRebel1(ScummEngine_v7 *scumm) : Insane(), _vm(scumm) {
 	_optTextEnabled = ConfMan.getBool("subtitles");
 	_optControlsYFlip = false;
 	_optRapidFire = true;
-	_optVolume = _vm->_mixer->getVolumeForSoundType(Audio::Mixer::kPlainSoundType) * 127 / Audio::Mixer::kMaxChannelVolume;
+	_optVolume = _vm->_mixer->getVolumeForSoundType(Audio::Mixer::kMusicSoundType) * 127 / Audio::Mixer::kMaxChannelVolume;
 
 	// Default high scores ??? from DS:0x1D0/0x298/0x2C0
 	const struct { const char *name; int32 score; byte difficulty; } kDefaultScores[kHighScoreCount] = {


Commit: 1758ffa0f57f8f2a872509b19d0b38a5496678dc
    https://github.com/scummvm/scummvm/commit/1758ffa0f57f8f2a872509b19d0b38a5496678dc
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-10T16:02:41+02:00

Commit Message:
SCUMM: RA1: added some missing audio code related with chunks and triggers

Changed paths:
    engines/scumm/insane/rebel1/audio.cpp
    engines/scumm/insane/rebel1/menu.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/audio.cpp b/engines/scumm/insane/rebel1/audio.cpp
index 64cc5e095f2..95643e67f17 100644
--- a/engines/scumm/insane/rebel1/audio.cpp
+++ b/engines/scumm/insane/rebel1/audio.cpp
@@ -67,6 +67,18 @@ void InsaneRebel1::processAudioFrame(int16 feedSize) {
 	_audio.processFrame(_player, feedSize);
 }
 
+void InsaneRebel1::applyAudioOptions() {
+	_vm->_mixer->muteSoundType(Audio::Mixer::kMusicSoundType, !_optMusicEnabled);
+	_vm->_mixer->muteSoundType(Audio::Mixer::kSFXSoundType, !_optSfxEnabled);
+	_vm->_mixer->muteSoundType(Audio::Mixer::kSpeechSoundType, !_optSfxEnabled);
+
+	if (_player) {
+		_player->setChanFlag(CHN_BKGMUS, _optMusicEnabled ? 1 : 0);
+		_player->setChanFlag(CHN_OTHER, _optSfxEnabled ? 1 : 0);
+		_player->setChanFlag(CHN_SPEECH, _optSfxEnabled ? 1 : 0);
+	}
+}
+
 void InsaneRebel1::loadSfx() {
 	for (int i = 0; i < kNumSfx; i++) {
 		if (_sfxData[i] || _sfxSize[i] != 0)
@@ -133,6 +145,13 @@ void InsaneRebel1::freeSfx() {
 	}
 }
 
+void InsaneRebel1::stopSfx(int slot) {
+	if (slot < 0 || slot >= kNumSfx)
+		return;
+
+	_vm->_mixer->stopHandle(_sfxHandles[slot]);
+}
+
 void InsaneRebel1::playSfx(int slot, int volume, int pan) {
 	if (slot < 0 || slot >= kNumSfx || !_sfxData[slot] || _sfxSize[slot] == 0)
 		return;
diff --git a/engines/scumm/insane/rebel1/menu.cpp b/engines/scumm/insane/rebel1/menu.cpp
index 7886ae46923..560cf226793 100644
--- a/engines/scumm/insane/rebel1/menu.cpp
+++ b/engines/scumm/insane/rebel1/menu.cpp
@@ -1359,12 +1359,11 @@ void InsaneRebel1::runOptionsMenu() {
 				break;
 			case 2: // Toggle music
 				_optMusicEnabled = !_optMusicEnabled;
-				_vm->_mixer->muteSoundType(Audio::Mixer::kMusicSoundType, !_optMusicEnabled);
+				applyAudioOptions();
 				break;
 			case 3: // Toggle SFX + Voice
 				_optSfxEnabled = !_optSfxEnabled;
-				_vm->_mixer->muteSoundType(Audio::Mixer::kSFXSoundType, !_optSfxEnabled);
-				_vm->_mixer->muteSoundType(Audio::Mixer::kSpeechSoundType, !_optSfxEnabled);
+				applyAudioOptions();
 				break;
 			case 4: // Toggle dialogue text
 				_optTextEnabled = !ConfMan.getBool("subtitles");
diff --git a/engines/scumm/insane/rebel1/rebel.cpp b/engines/scumm/insane/rebel1/rebel.cpp
index 92af6c0367b..9c3bd5fe5b1 100644
--- a/engines/scumm/insane/rebel1/rebel.cpp
+++ b/engines/scumm/insane/rebel1/rebel.cpp
@@ -349,7 +349,8 @@ InsaneRebel1::InsaneRebel1(ScummEngine_v7 *scumm) : Insane(), _vm(scumm) {
 	// Options ??? read initial state from ScummVM mixer
 	_optRookieOneFemale = false;
 	_optMusicEnabled = !_vm->_mixer->isSoundTypeMuted(Audio::Mixer::kMusicSoundType);
-	_optSfxEnabled = !_vm->_mixer->isSoundTypeMuted(Audio::Mixer::kSFXSoundType);
+	_optSfxEnabled = !_vm->_mixer->isSoundTypeMuted(Audio::Mixer::kSFXSoundType) &&
+		!_vm->_mixer->isSoundTypeMuted(Audio::Mixer::kSpeechSoundType);
 	// Initialize the dialogue-text (subtitles) toggle from ScummVM's global setting so the
 	// game and the in-game DIALOGUE TEXT menu label reflect it. The menu toggle writes the
 	// same "subtitles" key, and ra1HandleText() gates rendering on it.
diff --git a/engines/scumm/insane/rebel1/rebel.h b/engines/scumm/insane/rebel1/rebel.h
index 71138104d32..00976d5143c 100644
--- a/engines/scumm/insane/rebel1/rebel.h
+++ b/engines/scumm/insane/rebel1/rebel.h
@@ -291,6 +291,8 @@ private:
 	void loadSfx();
 	void freeSfx();
 	void playSfx(int slot, int volume, int pan);
+	void stopSfx(int slot);
+	void applyAudioOptions();
 	void queueAudioData(int trackIdx, uint8 *data, int32 size, int volume, int pan);
 public:
 	void drawFontBankString(byte *dst, int pitch, int width, int height, int x, int y, const char *text);
diff --git a/engines/scumm/insane/rebel1/render.cpp b/engines/scumm/insane/rebel1/render.cpp
index a485368963a..c8d62911ad7 100644
--- a/engines/scumm/insane/rebel1/render.cpp
+++ b/engines/scumm/insane/rebel1/render.cpp
@@ -907,6 +907,12 @@ void InsaneRebel1::renderTargeting(byte *dst, int pitch, int width, int height)
 	const RA1SpriteBank &markerBank = (_techFontBank.numSprites > 0) ? _techFontBank : _hudFontBank;
 	const int overlayX = ra1GameplayWindowOffsetX(this);
 	const int overlayY = ra1GameplayWindowOffsetY(this);
+
+	if (_targetProximity > 0 && _prevTargetProx == 0)
+		playSfx(kSfxLockOn, 127, 0);
+	else if (_targetProximity == 0 && _prevTargetProx != 0)
+		stopSfx(kSfxLockOn);
+
 	if (markerBank.numSprites > 0) {
 		// FUN_1CB22 can switch marker sets via DAT_75FF bit 1.
 		// Baseline RA1 targeting uses '^' and animation e..h.
@@ -1771,6 +1777,7 @@ void InsaneRebel1::renderHUD(byte *dst, int pitch, int width, int height) {
 	// Extra life bonus: every 10,000 points (FUN_1BBCB lines 11-27)
 	if (_score / 10000 > _prevScore / 10000) {
 		_lives++;
+		playSfx(kSfxBonus, 127, 0);
 	}
 	_prevScore = _score;
 
@@ -1897,6 +1904,8 @@ void InsaneRebel1::renderHUD(byte *dst, int pitch, int width, int height) {
 			// FUN_1BBCB pushes string pointers 0x671b ("<<[") or 0x671f ("<<\\") into FUN_221B7.
 			const char *warningStr = aboveCritical ? "<<[" : "<<\\";
 			drawFontBankString(dst, pitch, width, height, hudX + 0x49, hudY + 0x07, warningStr);
+			if (!aboveCritical && ((_frameCounter & 0x7) == 0))
+				playSfx(kSfxKlaxon, 127, 0);
 		}
 	}
 
diff --git a/engines/scumm/insane/rebel1/runlevels.cpp b/engines/scumm/insane/rebel1/runlevels.cpp
index b4bc7849507..14e58c258bb 100644
--- a/engines/scumm/insane/rebel1/runlevels.cpp
+++ b/engines/scumm/insane/rebel1/runlevels.cpp
@@ -160,6 +160,7 @@ void InsaneRebel1::playCinematic(const char *filename, int32 startFrame) {
 	// passive-cinematic audio before chaining the next ANM.
 	_audio.reset();
 	splayer->resetAudioTracks();
+	applyAudioOptions();
 	_interactiveVideoActive = false;
 	_vm->_smushVideoShouldFinish = false;
 	splayer->setCurVideoFlags(0x420);
@@ -1701,6 +1702,7 @@ void InsaneRebel1::setupInteractiveVideoState(int32 startFrame) {
 
 	SmushPlayer *splayer = _vm->_splayer;
 	_player = splayer;
+	applyAudioOptions();
 	restoreScreenFlashPalette();
 	if (!preserveRuntimeState)
 		clearBit(0);
diff --git a/engines/scumm/smush/rebel/smush_player_ra1.cpp b/engines/scumm/smush/rebel/smush_player_ra1.cpp
index 8a9fc94d781..43f4d65e882 100644
--- a/engines/scumm/smush/rebel/smush_player_ra1.cpp
+++ b/engines/scumm/smush/rebel/smush_player_ra1.cpp
@@ -765,20 +765,85 @@ static bool ra1ScanFrameGameChunks(Common::SeekableReadStream &b, int32 frameSiz
 	return hasGameChunk;
 }
 
-void SmushPlayerRebel1::ra1HandleFrameAudioChunk(int32 subSize, Common::SeekableReadStream &b) {
+static int16 getRA1AudioChunkTypeFlags(uint32 subType) {
+	switch (subType) {
+	case MKTAG('P','V','O','C'):
+		return IS_SPEECH;
+	case MKTAG('P','S','A','D'):
+		return IS_BKG_MUSIC;
+	case MKTAG('P','S','D','2'):
+	case MKTAG('S','A','U','D'):
+	default:
+		return IS_SFX;
+	}
+}
+
+void SmushPlayerRebel1::ra1HandleFrameAudioChunk(uint32 subType, int32 subSize, Common::SeekableReadStream &b) {
 	if (_compressedFileMode || isFastForwardingCurrentFrame())
 		return;
+	if (subSize <= 0)
+		return;
 
 	uint8 *audioChunk = (uint8 *)malloc(subSize + 8);
 	if (audioChunk == nullptr)
 		return;
 
-	b.seek(-8, SEEK_CUR);
-	b.read(audioChunk, subSize + 8);
-	feedAudio(audioChunk, 0, 127, 0, 0);
+	WRITE_BE_UINT32(audioChunk, subType);
+	WRITE_BE_UINT32(audioChunk + 4, subSize);
+	b.read(audioChunk + 8, subSize);
+	ra1FeedAudio(subType, audioChunk, 0, 127, 0, 0);
 	free(audioChunk);
 }
 
+void SmushPlayerRebel1::ra1FeedAudio(uint32 subType, uint8 *srcBuf, int groupId, int volume, int pan, int16 flags) {
+	if (!_smushAudioInitialized)
+		return;
+
+	const uint32 chunkSize = READ_BE_UINT32(&srcBuf[4]);
+	const int16 typeFlags = getRA1AudioChunkTypeFlags(subType);
+
+	if (chunkSize >= 12 &&
+			srcBuf[8] == 0 && srcBuf[9] == 0 && srcBuf[12] == 0 &&
+			srcBuf[13] == 0 && srcBuf[16] == 0 && srcBuf[17] == 0) {
+		const uint16 trkId = READ_BE_INT16(&srcBuf[10]);
+		const uint16 index = READ_BE_INT16(&srcBuf[14]);
+		const int32 maxFrames = READ_BE_INT16(&srcBuf[18]);
+		flags = (flags & ~TRK_TYPE_MASK) | typeFlags;
+
+		handleSAUDChunk(
+			srcBuf + 20,
+			chunkSize - 12,
+			groupId,
+			volume,
+			pan,
+			flags,
+			trkId,
+			index,
+			maxFrames);
+	} else if (chunkSize >= 10) {
+		const uint16 trkId = READ_LE_INT16(&srcBuf[8]);
+		const uint16 index = READ_LE_INT16(&srcBuf[10]);
+		const int32 maxFrames = READ_LE_INT16(&srcBuf[12]);
+		flags |= READ_LE_INT16(&srcBuf[14]);
+		flags = (flags & ~TRK_TYPE_MASK) | typeFlags;
+		volume = (volume * srcBuf[16]) >> 7;
+
+		const int panDelta = (int8)srcBuf[17];
+		const int effPan = (panDelta == -128) ? 128 : pan + panDelta;
+
+		handleSAUDChunk(
+			srcBuf + 18,
+			chunkSize - 10,
+			groupId,
+			volume,
+			effPan,
+			flags,
+			trkId,
+			index,
+			maxFrames);
+	}
+}
+
 void SmushPlayerRebel1::ra1HandleGameFrameChunk(int32 subSize, Common::SeekableReadStream &b, bool fastForwarding) {
 	if (!fastForwarding && _insane) {
 		InsaneRebel1 *rebel1 = (InsaneRebel1 *)_insane;
@@ -837,7 +902,7 @@ void SmushPlayerRebel1::ra1HandleObjOverlayFrameChunk(int32 objDataSize, Common:
 				if (audioBuf == nullptr)
 					break;
 				memcpy(audioBuf, objBuf + objPos, embSize + 8);
-				feedAudio(audioBuf, 0, 127, 0, 0);
+				ra1FeedAudio(embTag, audioBuf, 0, 127, 0, 0);
 				free(audioBuf);
 			}
 		}
@@ -878,11 +943,11 @@ bool SmushPlayerRebel1::ra1DispatchFrameChunk(uint32 subType, int32 subSize, int
 		break;
 	case MKTAG('P','S','A','D'):
 	case MKTAG('P','V','O','C'):
-		ra1HandleFrameAudioChunk(subSize, b);
+		ra1HandleFrameAudioChunk(subType, subSize, b);
 		break;
 	case MKTAG('P','S','D','2'):
 		if (_ra1LastFrameObjectVisible)
-			ra1HandleFrameAudioChunk(subSize, b);
+			ra1HandleFrameAudioChunk(subType, subSize, b);
 		break;
 	case MKTAG('T','R','E','S'):
 	case MKTAG('T','E','X','T'):
diff --git a/engines/scumm/smush/rebel/smush_player_ra1.h b/engines/scumm/smush/rebel/smush_player_ra1.h
index 65570b04539..e5ea0da7095 100644
--- a/engines/scumm/smush/rebel/smush_player_ra1.h
+++ b/engines/scumm/smush/rebel/smush_player_ra1.h
@@ -65,7 +65,8 @@ private:
 	void ra1HandleFade(int32 subSize, Common::SeekableReadStream &b);
 	SmushFont *ra1GetFont(int font);
 	void ra1HandleText(int32 subSize, Common::SeekableReadStream &b);
-	void ra1HandleFrameAudioChunk(int32 subSize, Common::SeekableReadStream &b);
+	void ra1HandleFrameAudioChunk(uint32 subType, int32 subSize, Common::SeekableReadStream &b);
+	void ra1FeedAudio(uint32 subType, uint8 *srcBuf, int groupId, int volume, int pan, int16 flags);
 	void ra1HandleGameFrameChunk(int32 subSize, Common::SeekableReadStream &b, bool fastForwarding);
 	void ra1HandleObjOverlayFrameChunk(int32 objDataSize, Common::SeekableReadStream &b, bool fastForwarding);
 	bool ra1HandleUnknownFrameChunk(uint32 subType, int32 subSize);


Commit: 00eb162fb89b7d0ea43318735127b7840c452c4e
    https://github.com/scummvm/scummvm/commit/00eb162fb89b7d0ea43318735127b7840c452c4e
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-10T16:02:41+02:00

Commit Message:
SCUMM: RA: normalize comments to avoid redunancies or incoherent wording

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/insane/rebel1/render.cpp
    engines/scumm/insane/rebel1/runlevels.cpp
    engines/scumm/insane/rebel2/iact.cpp
    engines/scumm/insane/rebel2/levels.cpp
    engines/scumm/insane/rebel2/menu.cpp
    engines/scumm/insane/rebel2/rebel.cpp
    engines/scumm/insane/rebel2/rebel.h
    engines/scumm/insane/rebel2/render.cpp
    engines/scumm/smush/rebel/smush_player_ra1.cpp
    engines/scumm/smush/rebel/smush_player_ra2.cpp


diff --git a/engines/scumm/insane/rebel1/iact.cpp b/engines/scumm/insane/rebel1/iact.cpp
index 16fb90cce40..0c34182fe5a 100644
--- a/engines/scumm/insane/rebel1/iact.cpp
+++ b/engines/scumm/insane/rebel1/iact.cpp
@@ -736,7 +736,7 @@ void InsaneRebel1::checkDynamicLevelBranch(int32 curFrame) {
 			return;
 		const uint32 routeFrame = (uint32)curFrame;
 		// GAME 0x09 publishes its branch-tested position in g_shipPosX.
-		// ScummVM keeps the drawn ship center and the 0x09 aim cursor split,
+		// Keep the drawn ship center and the 0x09 aim cursor split,
 		// so compare the effective gameplay cursor here.
 		const int16 branchX = getGameplayCursorX();
 		const int route = CLIP<int>(_levelRouteIndex, 0, 5);
@@ -860,7 +860,7 @@ void InsaneRebel1::updateFlightVariantCursor() {
 	// Assembly-verified 0x09 layout:
 	//   ship sprite center = (_74B6 + _74BA, _74B8 + _74BC)
 	//   cursor center      = (_74BE, _74C0)
-	// In ScummVM the flight sprite center already lives in _shipPos.
+	// The flight sprite center already lives in _shipPos.
 	const int16 shipBaseX = _shipPosX;
 	const int16 shipBaseY = _shipPosY;
 	const int32 liftTerm = (int32)_liftSmooth - 0x0F;
@@ -874,7 +874,7 @@ void InsaneRebel1::updateFlightVariantCursor() {
 }
 
 // preprocessMouseAxes ??? FUN_231BE (0x231BE) centered-axis output law, adapted to
-// ScummVM's absolute 320x200 mouse space. The old DOS virtual-mouse/recenter
+// the absolute 320x200 mouse space. The old DOS virtual-mouse/recenter
 // control path is intentionally not used. For opcode 0x0B, gamepad input uses
 // the 3DO standard-pad reticle model: axis samples move the reticle, and
 // releasing the pad holds the last reticle position.
@@ -1104,7 +1104,7 @@ void InsaneRebel1::updateShipPhysics() {
 		inputSourceName = "joystick-dpad";
 
 	// --- Step 2: Roll accumulator (_74CA) ---
-	// Normal mode: accumulate. For ScummVM's absolute mouse in flight handlers,
+	// Normal mode: accumulate. For absolute mouse input in flight handlers,
 	// steer toward a bounded roll target so holding the cursor off center does
 	// not continue accelerating the ship until it clamps.
 	if ((effectiveOpcode == 0x07 || effectiveOpcode == 0x09) &&
@@ -1341,7 +1341,7 @@ void InsaneRebel1::getCollisionShipCenter(int16 &x, int16 &y) const {
 	// Original 0x0D/0x0E collision compares script zones transformed by
 	// FUN_223FE against the gameplay-window ship center (base center +
 	// g_shipOffset). This is DOS screen space; render overlays add the viewport
-	// offset separately when drawing into ScummVM's larger source buffer.
+	// offset separately when drawing into the larger source buffer.
 	//
 	// In Level 1 part 2, HandleGameOp0A_TurretVariant reuses _shipPos for the
 	// targeting cursor, so collision must read the movement accumulator instead.
@@ -1435,7 +1435,7 @@ void InsaneRebel1::updateTurretPhysics() {
 		const int16 rawInputY = inputY;
 
 		if (usedJoystick && _flyControlMode == 2) {
-			// ScummVM-only concession for Level 1 part 2. The original 0x08 handler
+			// Extra concession for Level 1 part 2. The original 0x08 handler
 			// uses raw axes directly; do not damp Level 13's surface controls.
 			inputX /= 2;
 			inputY /= 2;
@@ -1876,7 +1876,7 @@ bool InsaneRebel1::isTorpedoModeActive() const {
 }
 
 
-// ScummVM-side splits for the original on-foot GAME handlers:
+// Helper splits for the original on-foot GAME handlers:
 // HandleGameOp19_OnFootSequence (0x19) and HandleGameOp1A_OnFootVariant (0x1A).
 // On-foot handler for Level 9 (Stormtroopers). Character walks left/right, crosshair tracks mouse.
 //
@@ -2055,7 +2055,7 @@ void InsaneRebel1::updateOnFootSequence() {
 // this implementation; the original code dispatches the opcode handler directly.
 void InsaneRebel1::updateOnFootAimVariant() {
 	// --- 0x1A: Crosshair positioning (HandleGameOp1A_OnFootVariant) ---
-	// DOS used virtual-mouse axes relative to the character offset. ScummVM's
+	// DOS used virtual-mouse axes relative to the character offset. The
 	// mouse and gamepad reticle are screen-space controls so the cursor remains
 	// able to cross the whole playfield while Luke is standing at either side.
 	int16 inputX = 0, inputY = 0;
diff --git a/engines/scumm/insane/rebel1/menu.cpp b/engines/scumm/insane/rebel1/menu.cpp
index 560cf226793..60fc7bdd138 100644
--- a/engines/scumm/insane/rebel1/menu.cpp
+++ b/engines/scumm/insane/rebel1/menu.cpp
@@ -46,7 +46,7 @@ const int kRA1MenuFrameH = 0x0f;
 const int kRA1MenuRowH = 0x0f;
 const byte kRA1MenuFrameColor = 0xdf;
 // Highlight-frame geometry shared by the render*Overlay drawing and the mouse hit-testing
-// (clicking menu items is a ScummVM-exclusive feature, so the rects must stay in sync).
+// (clicking menu items is an extra feature, so the rects must stay in sync).
 const int kRA1MainMenuFrameYBase    = 0x2c;  // frame Y = (item + 1) * kRA1MenuRowH + this
 const int kRA1OptionsFrameYBase     = 0x1d;  // frame Y = (item + 1) * kRA1MenuRowH + this
 const int kRA1LevelSelectFrameYBase = 0x2c;  // frame Y = row * kRA1MenuRowH + this
@@ -613,7 +613,7 @@ void InsaneRebel1::openGameplayMainMenu() {
 		_player->unpause();
 }
 
-// ScummVM-exclusive feature (not in the original game): let the player navigate and
+// Extra 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
 // highlight frames drawn by the render*Overlay() functions, so they share the
@@ -679,7 +679,7 @@ bool InsaneRebel1::handleMenuMouse(const Common::Event &event) {
 }
 
 bool InsaneRebel1::notifyEvent(const Common::Event &event) {
-	// Global ScummVM dialogs pause the engine while their modal event loop runs.
+	// Global dialogs pause the engine while their modal event loop runs.
 	// Do not consume those mouse/key events as RA1 gameplay/menu input, or the
 	// dialog buttons cannot receive clicks while an interactive video is active.
 	if (_vm->isPaused())
@@ -741,7 +741,7 @@ bool InsaneRebel1::notifyEvent(const Common::Event &event) {
 			event.type == Common::EVENT_LBUTTONDOWN)
 		_activeInputSource = kInputSourceMouse;
 
-	// ScummVM-exclusive feature: mouse navigation/clicking of the RA1 front-end menus.
+	// Extra feature: mouse navigation/clicking of the RA1 front-end menus.
 	if (handleMenuMouse(event))
 		return true;
 
@@ -1075,7 +1075,7 @@ void InsaneRebel1::renderOptionsOverlay(byte *dst, int pitch, int width, int hei
 }
 
 void InsaneRebel1::renderLevelSelectOverlay(byte *dst, int pitch, int width, int height) {
-	// --- ScummVM level select submenu, styled like the original frontend menus ---
+	// --- Extra level select submenu, styled like the original frontend menus ---
 	const int titleW = getFontBankStringWidth("LEVEL SELECT");
 	drawMenuTitleText(dst, pitch, width, height, getRebel1MenuCenteredX(titleW), 15, "LEVEL SELECT");
 
@@ -1167,7 +1167,7 @@ void InsaneRebel1::renderMainMenuItems(byte *dst, int pitch, int width, int heig
 void InsaneRebel1::renderMainMenuOverlay(byte *dst, int pitch, int width, int height) {
 	_menuFrameCounter++;
 
-	// ScummVM-exclusive feature: the menus are mouse-clickable, so keep the default
+	// The menus are mouse-clickable, so keep the default
 	// arrow cursor visible. SmushPlayer::play() hides the system cursor for the whole
 	// video (and re-hides it every video), so re-assert visibility each rendered frame
 	// while a menu overlay is on screen. The arrow bitmap/palette is set in
@@ -1233,7 +1233,7 @@ void InsaneRebel1::playMenuBackground() {
 	_menuActive = true;
 	_menuConfirmed = false;
 	_menuFrameCounter = 0;
-	// Show ScummVM's built-in arrow pointer for the (mouse-clickable) menus. Set it once
+	// Show the built-in arrow pointer for the mouse-clickable menus. Set it once
 	// here; renderMainMenuOverlay() re-asserts showMouse() each frame because the SMUSH
 	// player forces the cursor off while a video plays. disableCursorPalette(false) ensures
 	// the arrow's own CLUT palette is used rather than the game palette.
diff --git a/engines/scumm/insane/rebel1/rebel.cpp b/engines/scumm/insane/rebel1/rebel.cpp
index 9c3bd5fe5b1..6a2ce686fb6 100644
--- a/engines/scumm/insane/rebel1/rebel.cpp
+++ b/engines/scumm/insane/rebel1/rebel.cpp
@@ -346,12 +346,12 @@ InsaneRebel1::InsaneRebel1(ScummEngine_v7 *scumm) : Insane(), _vm(scumm) {
 	_levelSelectSel = 0;
 	_startLevel = 1;
 
-	// Options ??? read initial state from ScummVM mixer
+	// Options: read initial state from the mixer.
 	_optRookieOneFemale = false;
 	_optMusicEnabled = !_vm->_mixer->isSoundTypeMuted(Audio::Mixer::kMusicSoundType);
 	_optSfxEnabled = !_vm->_mixer->isSoundTypeMuted(Audio::Mixer::kSFXSoundType) &&
 		!_vm->_mixer->isSoundTypeMuted(Audio::Mixer::kSpeechSoundType);
-	// Initialize the dialogue-text (subtitles) toggle from ScummVM's global setting so the
+	// Initialize the dialogue-text (subtitles) toggle from the global setting so the
 	// game and the in-game DIALOGUE TEXT menu label reflect it. The menu toggle writes the
 	// same "subtitles" key, and ra1HandleText() gates rendering on it.
 	_optTextEnabled = ConfMan.getBool("subtitles");
diff --git a/engines/scumm/insane/rebel1/rebel.h b/engines/scumm/insane/rebel1/rebel.h
index 00976d5143c..8a17aae30e8 100644
--- a/engines/scumm/insane/rebel1/rebel.h
+++ b/engines/scumm/insane/rebel1/rebel.h
@@ -460,7 +460,7 @@ private:
 	int _difficulty;
 
 	// Per-difficulty tuning (from assault_data_3.bin, indexed: difficulty * 0x28B + level * 0x1F)
-	// Original game loads from C:\rebltune.txt; ScummVM uses hardcoded table.
+	// Original game loads from C:\rebltune.txt; use a hardcoded table.
 	// 21 sub-levels (1A,1B,2,3,4A,4B,5A,5B,6,7,8,9A,9B,10,11,12,13,14A,14B,15A,15B)
 	struct TuningParams {
 		int16 roll;      // +0x05: horizontal speed/sensitivity
@@ -503,8 +503,8 @@ private:
 	byte _hudRenderFlag;         // 0x7600: 0xFF when HUD should render (set by combat mode handlers)
 	byte _hudDirtyFlag;          // 0x7601: 0xFF after HUD redraw (set by renderHUD)
 	int16 _maxChapterUnlocked;   // 0x7730: highest unlocked passcode slot (0=none)
-	bool _unlockAllLevels;       // ScummVM option: expose level select without passcodes
-	bool _noDamage;              // ScummVM option: suppress player damage
+	bool _unlockAllLevels;       // Option: expose level select without passcodes
+	bool _noDamage;              // Option: suppress player damage
 
 	static const int16 kMaxHealth = 98;
 	static const int16 kDeathTimerInit = 30;
@@ -565,7 +565,7 @@ private:
 	bool handleMenuCommand(RA1MenuCommand command);
 	bool handleControllerMenuAction(ScummAction action);
 	bool handleControllerMenuAxis(int16 oldAxisX, int16 oldAxisY);
-	// ScummVM-exclusive feature: navigate/click the menus with the mouse.
+	// Extra feature: navigate/click the menus with the mouse.
 	bool handleMenuMouse(const Common::Event &event);
 	bool handleTextEntryAction(ScummAction action);
 	bool handleTextEntryKey(const Common::Event &event);
@@ -597,7 +597,7 @@ private:
 	bool _optSfxEnabled;      // DAT_22b8: sfx+voice on/off
 	bool _optTextEnabled;     // DAT_22b9: dialogue text on/off
 	bool _optControlsYFlip;   // DAT_22be: Y-axis inversion
-	bool _optRapidFire;       // ScummVM option: held fire keeps shooting
+	bool _optRapidFire;       // Option: held fire keeps shooting
 	int  _optVolume;          // DAT_22c1: master volume 0..127
 
 	// High scores / TOP PILOTS display ??? data at DS:0x1D0
diff --git a/engines/scumm/insane/rebel1/render.cpp b/engines/scumm/insane/rebel1/render.cpp
index c8d62911ad7..8e031771cbf 100644
--- a/engines/scumm/insane/rebel1/render.cpp
+++ b/engines/scumm/insane/rebel1/render.cpp
@@ -47,8 +47,8 @@ int ra1GameplayWindowOffsetX(const InsaneRebel1 *rebel1) {
 	if (!rebel1 || !rebel1->isInteractiveVideoActive())
 		return 0;
 
-	// Ship/cursor/shot coordinates are in DOS's 320x200 gameplay window. Under
-	// ScummVM's FUN_224FD crop emulation, shift them into the 384x242 source
+	// Ship/cursor/shot coordinates are in DOS's 320x200 gameplay window.
+	// FUN_224FD crop emulation shifts them into the 384x242 source
 	// buffer so the final source-window crop presents them at the same screen
 	// position DOS used for gameplay and collision.
 	switch (rebel1->getEffectiveGameOpcode()) {
@@ -453,7 +453,7 @@ void renderSpriteWithFlags(byte *dst, int pitch, int width, int height,
 	}
 }
 
-// ScummVM-side helper only: the original keeps this shot-sprite math inline in
+// Helper only: the original keeps this shot-sprite math inline in
 // several GAME handlers. It is collapsed here because the direction/lerp/render
 // sequence is identical for one-beam shot sprites.
 void renderAimedShotSprite(byte *dst, int pitch, int width, int height,
@@ -481,7 +481,7 @@ void renderAimedShotPair(byte *dst, int pitch, int width, int height,
 		start2X, start2Y, targetX, targetY, lerp);
 }
 
-// ScummVM-side helper for the 0x0B fallback edge-beam path. The original keeps
+// Helper for the 0x0B fallback edge-beam path. The original keeps
 // the bucket lookup inline with the renderer, but both left/right beams share it.
 void renderBucketedShotSprite(byte *dst, int pitch, int width, int height,
 		const RA1SpriteBank &laserBank, int startX, int startY, int targetX, int targetY,
@@ -672,7 +672,7 @@ void InsaneRebel1::procPostRendering(byte *renderBitmap, int32 codecparam, int32
 			shotOverlayHandled = true;
 			if (_health >= 0) {
 				// HandleGameOp5A_ObjectOrSceneTrigger can snap g_shipPos to the
-				// target center before the shot overlay runs. The ScummVM player
+				// target center before the shot overlay runs. The player
 				// defers GAME 0x1A until after FOBJ dispatch, so preserve that
 				// snap instead of immediately replacing it with raw input again.
 				const bool preserveTargetSnap = (_targetProximity == 2 && _tuning.snap > 0);
@@ -834,7 +834,7 @@ void InsaneRebel1::procPostRendering(byte *renderBitmap, int32 codecparam, int32
 	_fireCooldown = _playerFired ? 1 : 0;
 }
 
-// ScummVM helper that groups the common shot/lock overlay calls used by the
+// Helper that groups the common shot/lock overlay calls used by the
 // original GAME handlers. GAME 0x1A uses the same pipeline without the target
 // box draw; 0x09/0x0A/0x0B include DrawTargetIndicators first. GAME 0x0B
 // defers FUN_1CB22 targeting until post-render so FTCH can restore cockpit
@@ -959,7 +959,7 @@ void InsaneRebel1::renderTargeting(byte *dst, int pitch, int width, int height)
 
 // handleLevel14Play2BSplice ??? RunLevel14Flow (0x1ACD1) queues L14PLY2B.ANM
 // from inside the L14PLAY2 loop when the current clip reaches maxFrame - 0x0F.
-// This helper is a ScummVM-side extraction; the original keeps the PlayAnmFile
+// This helper is an implementation extraction; the original keeps the PlayAnmFile
 // call inline and passes the old L14PLAY2 timeline frame to the ANM frame gate.
 void InsaneRebel1::handleLevel14Play2BSplice(int32 curFrame, int32 maxFrame) {
 	if (_currentLevel != 13 || _levelGameplayPhase != 2 || _level14Play2BSpliced ||
@@ -1505,7 +1505,7 @@ void InsaneRebel1::beginChapterSummaryOverlay(int revealOffsetFromEnd, int stopO
 	}
 }
 
-// ScummVM keeps the passcodes in clear text. The DOS table is XOR-0xAA
+// Passcodes are kept in clear text. The DOS table is XOR-0xAA
 // encoded in 15 20-byte slots at DS:0x00A4.
 static const char *const kChapterCompletePasswords[] = {
 	"FALCON", "BIGGS", "ACKBAR", "ANOAT", "KAIBURR",
@@ -1521,7 +1521,7 @@ const char *InsaneRebel1::getChapterCompletePassword(int passwordIndex) const {
 }
 
 // drawChapterSummaryOverlay ??? RunChapterCompleteSummaryScreen (0x15E42), shared by
-// the RA1 runlevel flows that call it. This helper is a ScummVM-side extraction;
+// the RA1 runlevel flows that call it. This helper is an implementation extraction;
 // the original pumps frontend frames from the runlevel after queueing the END ANM.
 void InsaneRebel1::drawChapterSummaryOverlay(byte *dst, int pitch, int width, int height,
 		int32 curFrame, int32 maxFrame) {
diff --git a/engines/scumm/insane/rebel1/runlevels.cpp b/engines/scumm/insane/rebel1/runlevels.cpp
index 14e58c258bb..f9e5fecdd56 100644
--- a/engines/scumm/insane/rebel1/runlevels.cpp
+++ b/engines/scumm/insane/rebel1/runlevels.cpp
@@ -156,7 +156,7 @@ void InsaneRebel1::playCinematic(const char *filename, int32 startFrame) {
 	_player = splayer;
 	restoreScreenFlashPalette();
 	// DOS PlayFrontendAnmAndWait keeps pumping until frontend audio clears.
-	// ScummVM's Rebel queues outlive SmushPlayer::play(), so clear stale
+	// Rebel queues outlive SmushPlayer::play(), so clear stale
 	// passive-cinematic audio before chaining the next ANM.
 	_audio.reset();
 	splayer->resetAudioTracks();
@@ -795,7 +795,7 @@ bool InsaneRebel1::runLevel7() {
 			routeSourceFrame = _pendingRouteStartFrame;
 			routeVideoStartFrame = _pendingRouteVideoStartFrame;
 			// DOS does not seek the destination route ANM here. The ANM-local
-			// decision frame is used by the playback gate/cutoff; ScummVM
+			// decision frame is used by the playback gate/cutoff; this implementation
 			// starts at the already-advanced gate target after the cutover.
 			routeStartFrame = 0;
 		}
@@ -890,7 +890,7 @@ bool InsaneRebel1::runLevel8() {
 			}
 
 			// RunLevel8Flow keeps pumping the active route while the walker
-			// shield register is nonzero. ScummVM's blocking SMUSH play returns
+			// shield register is nonzero. Blocking SMUSH play returns
 			// when one route pass ends, so explicitly replay that route and
 			// preserve the accumulated walker damage.
 			debugC(DEBUG_INSANE, "L8 replaying route=%d walkerHealth=%d killCount=%d",
@@ -927,7 +927,7 @@ bool InsaneRebel1::runLevel8() {
 bool InsaneRebel1::runLevel9() {
 	// DOS RunLevel9Flow calls RandScaleByte(2) three times before the intro.
 	// That helper advances a byte seed with seed = seed * 9 + 0x35 and returns
-	// (2 * seed) >> 8. Do not use ScummVM's session RNG here: it can turn the
+	// (2 * seed) >> 8. Do not use the session RNG here: it can turn the
 	// original right-side route into the capture/restart branch.
 	uint8 originalRouteSeed = 0;
 	auto getOriginalRouteBit = [&originalRouteSeed]() {
@@ -1629,7 +1629,7 @@ void InsaneRebel1::runGame() {
 			break;
 		}
 		case 4: {
-			// Level Select ??? ScummVM-only start point. Continue through the
+			// Level Select: extra start point. Continue through the
 			// original successor flow so post-level cinematics still play.
 			int selectedLevel = runLevelSelectMenu();
 			if (selectedLevel >= 1 && selectedLevel <= numLevels)
@@ -1731,7 +1731,7 @@ void InsaneRebel1::resolveSeek(const char *filename, int32 startFrame, int32 &vi
 
 	if (_currentLevel == 6 && level7RouteSplice) {
 		// DOS opens the route ANM from the beginning, then the armed frame gate
-		// suppresses until the adjusted target. With ScummVM's delayed cutover,
+		// suppresses until the adjusted target. With the delayed cutover,
 		// the destination must advance by the source tail already displayed.
 		videoStartFrame = (_pendingRouteVideoStartFrame > 0) ?
 			_pendingRouteVideoStartFrame : 1;
diff --git a/engines/scumm/insane/rebel2/iact.cpp b/engines/scumm/insane/rebel2/iact.cpp
index b640ad26023..a322e039bd1 100644
--- a/engines/scumm/insane/rebel2/iact.cpp
+++ b/engines/scumm/insane/rebel2/iact.cpp
@@ -588,7 +588,7 @@ void InsaneRebel2::iactRebel2Opcode3(Common::SeekableReadStream &b, int16 par2,
 	}
 }
 
-// ScummVM refactor helper for opcode 6, not a separate retail function.
+// Helper split out of FUN_004033CF case 6; not a separate original function.
 void InsaneRebel2::updateOpcode6Handler(int16 par2) {
 	// Update handler type if par2 is a known handler value (from FUN_4033CF case 6).
 	if (par2 == 7 || par2 == 8 || par2 == 0x19 || par2 == 0x26) {
@@ -601,7 +601,7 @@ void InsaneRebel2::updateOpcode6Handler(int16 par2) {
 	}
 }
 
-// ScummVM refactor helper for opcode 6 Handler 8, not a separate retail function.
+// Helper split out of FUN_00401234 case 4; not a separate original function.
 void InsaneRebel2::handleOpcode6Handler8(Common::SeekableReadStream &b, int16 par4) {
 	// Handler 8 specific logic (third-person on foot) - FUN_00401234 case 4.
 	// DAT_0043e000 = local_14[3], which maps to the IACT header's par4/userId.
@@ -780,7 +780,7 @@ void InsaneRebel2::handleOpcode6Handler8(Common::SeekableReadStream &b, int16 pa
 		_shipDirectionH, _shipDirectionV, _shipDirectionIndex);
 }
 
-// ScummVM refactor helper for opcode 6 Handler 7, not a separate retail function.
+// Helper split out of FUN_0040C3CC case 4; not a separate original function.
 void InsaneRebel2::handleOpcode6Handler7(Common::SeekableReadStream &b, int16 par4) {
 	// Handler 7 specific logic (third-person ship) - FUN_0040d836 / FUN_0040c3cc
 	// Used for Level 3 and similar space combat levels.
@@ -851,7 +851,7 @@ void InsaneRebel2::handleOpcode6Handler7(Common::SeekableReadStream &b, int16 pa
 	int16 scaledInputX = (int16)((inputX * 127) / 160);
 	int16 scaledInputY = _optControlsFlipped ? (int16)-inputY : inputY;  // local_14
 
-	// ScummVM direct mouse/touch/gamepad aiming can hold the cursor at an edge
+	// Direct mouse/touch/gamepad aiming can hold the cursor at an edge
 	// indefinitely. Keep this sensitivity concession local to Handler 7
 	// third-person ship steering.
 	scaledInputX = (int16)((scaledInputX * kRA2Handler7DirectInputNumerator) /
@@ -924,7 +924,7 @@ void InsaneRebel2::handleOpcode6Handler7(Common::SeekableReadStream &b, int16 pa
 	if (positionDeltaX > 11)
 		positionDeltaX = 12;
 
-	// Retail integrates relative flight axes. ScummVM's real mouse is an
+	// FUN_0040C3CC integrates relative flight axes. The real mouse is an
 	// absolute position, so steer toward a bounded position target instead of
 	// letting a held off-center cursor keep pushing the ship until it bounces.
 	if (useMouseFlightTarget) {
@@ -1092,7 +1092,7 @@ void InsaneRebel2::handleOpcode6Handler7(Common::SeekableReadStream &b, int16 pa
 		useMouseFlightTarget ? 1 : 0);
 }
 
-// ScummVM refactor helper for opcode 6 Handler 25, not a separate retail function.
+// Helper split out of FUN_0041CADB case 4; not a separate original function.
 void InsaneRebel2::handleOpcode6Handler25(byte *renderBitmap, Common::SeekableReadStream &b, int16 par2, int16 par3, int16 par4) {
 	// Handler 25 (0x19) specific logic (mixed mode - speeder bike).
 	// Based on FUN_0041cadb case 4 (opcode 6) lines 113-229.
@@ -1132,7 +1132,7 @@ void InsaneRebel2::handleOpcode6Handler25(byte *renderBitmap, Common::SeekableRe
 
 	// Set sprite mode (DAT_00457900 = local_14[3]) - controls which GRD sprite to render.
 	// From FUN_0041cadb line 122: DAT_00457900 = local_14[3].
-	// In ScummVM's IACT parsing: local_14[3] = offset 6-7 = par4.
+	// In this IACT parser: local_14[3] = offset 6-7 = par4.
 	// Mode 1: Uncovered, shooting position - sprite on left
 	// Mode 2: Covered, vertical shift
 	// Mode 3: Transition between covered/uncovered - sprite position depends on direction
@@ -1282,7 +1282,7 @@ void InsaneRebel2::handleOpcode6Handler25(byte *renderBitmap, Common::SeekableRe
 	drawHandler25CorridorOverlay(renderBitmap);
 }
 
-// ScummVM refactor helper for opcode 6 Handler 0x26, not a separate retail function.
+// Helper split out of FUN_00407FCB case 4; not a separate original function.
 void InsaneRebel2::handleOpcode6Turret(Common::SeekableReadStream &b, int16 par4) {
 	// Handler 0x26: FUN_407FCB line 77-79 - set level type from par4, read par5 for init trigger.
 	// param_5[3] = par4 = levelType, param_5[4] = par5 = init flag.
@@ -1320,7 +1320,7 @@ void InsaneRebel2::handleOpcode6Turret(Common::SeekableReadStream &b, int16 par4
 	}
 }
 
-// ScummVM refactor helper for opcode 6 generic init, not a separate retail function.
+// Helper split out of generic opcode 6 initialization; not a separate original function.
 void InsaneRebel2::handleOpcode6GenericInit(int16 par4) {
 	// Other handlers: par4 == 1 triggers init (NOT level type).
 	if (_rebelHandler != 0x26 && par4 == 1) {
@@ -1344,7 +1344,7 @@ void InsaneRebel2::handleOpcode6GenericInit(int16 par4) {
 	}
 }
 
-// ScummVM refactor helper for opcode 6 generic flight state, not a separate retail function.
+// Helper split out of generic opcode 6 flight state; not a separate original function.
 void InsaneRebel2::updateOpcode6GenericFlightState() {
 	// Step 3: Autopilot/control mode logic (lines 123-146)
 	// This determines whether the ship flies on autopilot or manual control.
@@ -1462,7 +1462,7 @@ void InsaneRebel2::updateOpcode6GenericFlightState() {
 		_rebelLevelType, _rebelAutopilot, _rebelDamageLevel, _rebelViewOffsetX, _rebelViewOffsetY);
 }
 
-// ScummVM refactor helper for opcode 6 embedded ANIM scan, not a separate retail function.
+// Helper split out of opcode 6 embedded ANIM scanning; not a separate original function.
 void InsaneRebel2::scanOpcode6EmbeddedAnim(byte *renderBitmap, Common::SeekableReadStream &b, int32 chunkSize, int16 par4) {
 	// Detect and load embedded ANIM (SAN) within the remaining IACT payload.
 	// Note: chunkSize is the remaining IACT payload size after par1-par4 header.
@@ -1562,7 +1562,7 @@ void InsaneRebel2::iactRebel2Opcode6(byte *renderBitmap, Common::SeekableReadStr
 //   Handler 0x26 (turret): Turret HUD NUT via par3/par4 (1-4)
 //   Handler 0x19: Speeder bike GRD/HUD resources via par4
 //
-// ScummVM refactor helper for opcode 8 Handler 7 FLY loading, not a separate retail function.
+// Helper split out of FUN_0040C3CC case 6; not a separate original function.
 bool InsaneRebel2::loadOpcode8Handler7FlySprites(Common::SeekableReadStream &b, int64 startPos, int64 remaining, int16 par4) {
 	// Handler 7: FLY NUT Loading (Third-Person Ship)
 	// FUN_0040c3cc case 6: par4 determines FLY sprite slot.
@@ -1578,7 +1578,7 @@ bool InsaneRebel2::loadOpcode8Handler7FlySprites(Common::SeekableReadStream &b,
 	return false;
 }
 
-// ScummVM refactor helper for opcode 8 Handler 7 shot table loading, not a separate retail function.
+// Helper split out of FUN_0040C3CC case 6 shot-table loading; not a separate original function.
 bool InsaneRebel2::loadOpcode8Handler7ShotTable(Common::SeekableReadStream &b, int64 startPos, int64 remaining, int16 par4) {
 	// FUN_0040c3cc case 6:
 	//   par4=12 -> FUN_0040fcfa(text, DAT_004437c2, DAT_00443808)
@@ -1595,14 +1595,14 @@ bool InsaneRebel2::loadOpcode8Handler7ShotTable(Common::SeekableReadStream &b, i
 	return false;
 }
 
-// ScummVM refactor helper for opcode 8 edge table loading, not a separate retail function.
+// Helper split out of FUN_00405663 edge-table loading; not a separate original function.
 bool InsaneRebel2::loadOpcode8EdgeTable(Common::SeekableReadStream &b, int64 startPos, int64 remaining, int16 par4) {
 	// Edge Blend Table Loading (par4 == 1000)
 	// FUN_405663: After all handler-specific opcode 8 processing, checks if par4==1000.
 	// If so, loads a per-level 256x256 color blend table from the IACT chunk data.
 	// This table controls the edge glow color of laser beams (e.g. red vs green).
 	// Data starts at byte offset 18 in the IACT chunk (in_stack_00000014 + 9 shorts).
-	// The stream is positioned after par1..par4 (8 bytes), so retail's +18 is startPos + 10.
+	// The stream is positioned after par1..par4 (8 bytes), so FUN_00405663's +18 is startPos + 10.
 	if (par4 == 1000 && remaining >= 10 + 8 + 32896) {
 		byte *edgeData = (byte *)malloc(8 + 32896);
 		if (edgeData) {
@@ -1619,7 +1619,7 @@ bool InsaneRebel2::loadOpcode8EdgeTable(Common::SeekableReadStream &b, int64 sta
 	return false;
 }
 
-// ScummVM refactor helper for opcode 8 aux SFX loading, not a separate retail function.
+// Helper split out of opcode 8 aux SFX loading; not a separate original function.
 bool InsaneRebel2::loadOpcode8AuxSfx(Common::SeekableReadStream &b, int64 startPos, int64 remaining, int16 par4) {
 	// Auxiliary Sound Buffer Loading (par4 20-47)
 	// FUN_401234 case 6 (handler 8): par4 0x14-0x1b (20-27) -> aux buffer 0
@@ -1663,7 +1663,7 @@ bool InsaneRebel2::loadOpcode8AuxSfx(Common::SeekableReadStream &b, int64 startP
 	return true;
 }
 
-// ScummVM refactor helper for opcode 8 Handler 25 shot-origin loading, not a separate retail function.
+// Helper split out of FUN_0041CADB case 6 shot-origin loading; not a separate original function.
 bool InsaneRebel2::loadOpcode8ShotOriginTable(Common::SeekableReadStream &b, int64 startPos, int64 remaining, int16 par4) {
 	// Handler 25 (0x19): Shot-Origin Lookup Table (par4 == 8)
 	// FUN_0041CADB case 6 pushes 30 short pointers into sscanf with format at 0x482360:
@@ -1679,7 +1679,7 @@ bool InsaneRebel2::loadOpcode8ShotOriginTable(Common::SeekableReadStream &b, int
 	return false;
 }
 
-// ScummVM refactor helper for opcode 8 embedded ANIM scanning, not a separate retail function.
+// Helper split out of opcode 8 embedded ANIM scanning; not a separate original function.
 void InsaneRebel2::loadOpcode8EmbeddedAnim(byte *renderBitmap, Common::SeekableReadStream &b, int64 startPos, int64 remaining, int16 par3, int16 par4) {
 	// Remaining handlers require finding ANIM tag in the stream.
 	debugC(DEBUG_INSANE, "Opcode 8: Scanning for ANIM tag (startPos=%lld remaining=%lld)",
@@ -1738,7 +1738,7 @@ void InsaneRebel2::loadOpcode8EmbeddedAnim(byte *renderBitmap, Common::SeekableR
 	b.seek(startPos);
 }
 
-// ScummVM refactor helper for opcode 8 embedded ANIM routing, not a separate retail function.
+// Helper split out of opcode 8 embedded ANIM routing; not a separate original function.
 bool InsaneRebel2::handleOpcode8EmbeddedAnim(byte *renderBitmap, byte *animData, int32 animDataSize, int16 par3, int16 par4) {
 	bool handled = false;
 
@@ -1851,7 +1851,7 @@ void InsaneRebel2::iactRebel2Opcode8(byte *renderBitmap, Common::SeekableReadStr
 bool InsaneRebel2::loadHandler25ShotOriginTable(Common::SeekableReadStream &b, int64 startPos, int64 remaining) {
 	// IACT layout at this point:
 	// - stream is positioned after par1..par4 (8 bytes consumed by caller)
-	// - retail parser reads from offset +18 relative to IACT start -> startPos + 10
+	// - FUN_0041CADB reads from offset +18 relative to IACT start -> startPos + 10
 	// - payload size for this opcode family is at offset +14 -> startPos + 6
 	if (remaining < 12)
 		return false;
@@ -1918,7 +1918,7 @@ bool InsaneRebel2::loadHandler25ShotOriginTable(Common::SeekableReadStream &b, i
 		return false;
 	}
 
-	// Retail mapping (from FUN_41CADB disassembly):
+	// FUN_0041CADB mapping:
 	// token1->0x4578b0 (X index 5), token2->0x4578d0 (Y index 5), ...
 	// token29->0x4578cc (X index 19), token30->0x4578ec (Y index 19).
 	for (int i = 0; i < 15; ++i) {
@@ -1937,7 +1937,7 @@ bool InsaneRebel2::loadHandler25ShotOriginTable(Common::SeekableReadStream &b, i
 
 // loadHandler7ShotTable -- Parse handler 7 laser muzzle coordinate pairs from IACT payload.
 bool InsaneRebel2::loadHandler7ShotTable(Common::SeekableReadStream &b, int64 startPos, int64 remaining, int16 par4) {
-	// Retail FUN_0040fcfa parses 35 "%hd %hd" pairs from offset +18 in the IACT
+	// FUN_0040FCFA parses 35 "%hd %hd" pairs from offset +18 in the IACT
 	// chunk into two parallel 35-entry tables. These tables are BSS globals in
 	// the EXE, so their values only exist in the SAN/IACT stream.
 	if (remaining < 12)
@@ -2024,7 +2024,7 @@ bool InsaneRebel2::loadHandler7ShotTable(Common::SeekableReadStream &b, int64 st
 // Opcode 8 Helper Functions
 // ---------------------------------------------------------------------------
 // Extracted from the original monolithic iactRebel2Opcode8 to match
-// the retail FUN_* function structure.
+// the original FUN_* function structure.
 
 // loadHandler7FlySprites -- Handler 7 FLY NUT loading (FUN_0040c3cc case 6).
 bool InsaneRebel2::loadHandler7FlySprites(Common::SeekableReadStream &b, int64 remaining, int16 par4) {
@@ -2394,7 +2394,7 @@ bool InsaneRebel2::loadLevel2Background(byte *animData, int32 size, byte *render
 				_level2BackgroundLoaded = true;
 				foundBackground = true;
 
-				// Handler 25 uses this buffer as a lookup mask; the retail code does not
+				// Handler 25 uses this buffer as a lookup mask; FUN_0041CADB does not
 				// copy it to the live screen. Handler 8 still uses it as a restore source.
 				if (renderBitmap && _rebelHandler != 25) {
 					int bufferPitch = (_player && _player->_width > 0) ? _player->_width : 320;
@@ -2523,7 +2523,7 @@ void InsaneRebel2::iactRebel2Opcode9(byte *renderBitmap, Common::SeekableReadStr
 
 	// Check difficulty gate (flag bit 3 = 0x08)
 	// If set, only show text if difficulty check passes (we skip this check for simplicity)
-	// In retail: FUN_00425d30(0) is called
+	// FUN_00425D30(0) is called.
 
 	// Get render buffer dimensions
 	int width = (_player && _player->_width > 0) ? _player->_width : 320;
@@ -2638,7 +2638,7 @@ void InsaneRebel2::iactRebel2Opcode9(byte *renderBitmap, Common::SeekableReadStr
 	convertedText[dstIdx] = '\0';
 
 	// Draw the text string (with converted character indices), but only when subtitles are
-	// enabled ??? opcode 9 is a subtitle/message path, so it honors ScummVM's global
+	// enabled: opcode 9 is a subtitle/message path, so it honors the global
 	// "subtitles" setting and the in-game TEXT toggle (same ConfMan key). The chunk is
 	// still fully parsed above so stream consumption is unaffected.
 	if (ConfMan.getBool("subtitles")) {
diff --git a/engines/scumm/insane/rebel2/levels.cpp b/engines/scumm/insane/rebel2/levels.cpp
index 57e786378cf..41be2808224 100644
--- a/engines/scumm/insane/rebel2/levels.cpp
+++ b/engines/scumm/insane/rebel2/levels.cpp
@@ -49,7 +49,7 @@ static void purgeRebel2GameplayInputEvents(Common::EventManager *eventMan) {
 // Level Loading System
 // ---------------------------------------------------------------------------
 // Emulates the level handler functions from FUN_00417E53 through FUN_0041BBE8.
-// Based on disassembly analysis of the retail Rebel Assault 2 executable.
+// Based on disassembly analysis of the Rebel Assault 2 executable.
 
 Common::String InsaneRebel2::getLevelDir(int levelId) {
 	return Common::String::format("LEV%02d", levelId);
@@ -472,7 +472,7 @@ int InsaneRebel2::runLevel(int levelId) {
 	// Lock the mouse to the game window during gameplay.
 	// The original hides the cursor (ShowCursor(0)) and relies on Windows confining
 	// the mouse to the game window. Without locking, the cursor can escape the
-	// ScummVM window making the ship uncontrollable.
+	// window making the ship uncontrollable.
 	_gameplaySectionActive = false;
 	CursorMan.showMouse(false);
 	g_system->lockMouse(true);
@@ -563,7 +563,7 @@ int InsaneRebel2::getRandomVariant(int max) {
 //
 // Returns variant suffix ("A", "B", "C", etc.) based on level, phase,
 // and the frame where the player died. Emulates the per-level frame
-// threshold tables in the retail level handlers.
+// threshold tables in the original level handlers.
 //
 Common::String InsaneRebel2::selectDeathVideoVariant(int levelId, int phase, int frame) {
 
diff --git a/engines/scumm/insane/rebel2/menu.cpp b/engines/scumm/insane/rebel2/menu.cpp
index 7e26e6dd250..0ada7d95441 100644
--- a/engines/scumm/insane/rebel2/menu.cpp
+++ b/engines/scumm/insane/rebel2/menu.cpp
@@ -43,7 +43,7 @@ namespace Scumm {
 // ---------------------------------------------------------------------------
 // Menu System Implementation
 // ---------------------------------------------------------------------------
-// Emulates retail menu system from FUN_004147B2 and FUN_0041FDC8.
+// Emulates original menu system from FUN_004147B2 and FUN_0041FDC8.
 
 static void setRebel2MixerVolume(ScummEngine_v7 *vm, int volumeLevel) {
 	const int mixerVolume = CLIP<int>(volumeLevel * 2, 0, (int)Audio::Mixer::kMaxMixerVolume);
@@ -123,7 +123,7 @@ Common::String InsaneRebel2::getRandomMenuVideo() {
 // Returns -1 (no action) or a 0-based selected menu item.
 // Events captured by notifyEvent() before ScummEngine consumes them.
 // Keyboard: Up=0x148, Down=0x150, Enter=0x0d.
-// Physical ESC is handled by notifyEvent() and opens the ScummVM menu.
+// Physical ESC is handled by notifyEvent() and opens the global menu.
 // Mouse mode (DAT_0047a806 == 1): Y position maps to selection.
 //
 int InsaneRebel2::processMenuInput() {
diff --git a/engines/scumm/insane/rebel2/rebel.cpp b/engines/scumm/insane/rebel2/rebel.cpp
index df84162a961..babc253b027 100644
--- a/engines/scumm/insane/rebel2/rebel.cpp
+++ b/engines/scumm/insane/rebel2/rebel.cpp
@@ -153,7 +153,7 @@ InsaneRebel2::InsaneRebel2(ScummEngine_v7 *scumm) {
 	// Set from param_10 of FUN_403BD0 (main game init). Values:
 	//   < 0: Edge highlights disabled (low-detail mode)
 	//   >= 0: Edge highlights enabled, >= 1: high-detail (secondary NUTs, widescreen)
-	// Always use high detail in ScummVM.
+	// Always use high detail.
 	_rebelDetailMode = 1;
 	_smush_cockpitNut = new NutRenderer(_vm, highRes ? "SYSTM/DIHIFONT.NUT" : "SYSTM/DISPFONT.NUT");
 
@@ -209,7 +209,7 @@ InsaneRebel2::InsaneRebel2(ScummEngine_v7 *scumm) {
 	_textOverlayFadeIn = 0;
 	_textOverlayFadeOut = 0;
 
-	// Retail globals mapped: hit counter, cooldown, movie/auto-play flags
+	// Original globals mapped: hit counter, cooldown, movie/auto-play flags.
 	_rebelOp6Initialized = false;
 	_rebelHitCounter = 0;
 	_rebelKillCounter = 0;
@@ -232,7 +232,7 @@ InsaneRebel2::InsaneRebel2(ScummEngine_v7 *scumm) {
 	_rebelViewMode1 = 0;
 	_rebelViewMode2 = 0;
 
-	// Initialize mirrored retail counters
+	// Initialize mirrored original counters.
 	for (int i = 0; i < 10; ++i) {
 		_rebelValueCounters[i] = 0;
 		_rebelMaskCounters[i] = 0;
@@ -542,7 +542,7 @@ InsaneRebel2::InsaneRebel2(ScummEngine_v7 *scumm) {
 	_optMusicEnabled = !_vm->_mixer->isSoundTypeMuted(Audio::Mixer::kMusicSoundType);
 	_optSfxEnabled = !_vm->_mixer->isSoundTypeMuted(Audio::Mixer::kSFXSoundType);
 	_optVoicesEnabled = !_vm->_mixer->isSoundTypeMuted(Audio::Mixer::kSpeechSoundType);
-	// Initialize the dialogue-text (subtitles) toggle from ScummVM's global setting so the
+	// Initialize the dialogue-text (subtitles) toggle from the global setting so the
 	// game and the in-game TEXT menu label reflect it. The menu toggle writes the same
 	// "subtitles" key, which the text-render paths gate on.
 	_optTextEnabled = ConfMan.getBool("subtitles");
@@ -676,7 +676,7 @@ void InsaneRebel2::restoreIOSGamepadController() {
 bool InsaneRebel2::notifyEvent(const Common::Event &event) {
 	SmushPlayer *splayer = ((ScummEngine_v7 *)_vm)->_splayer;
 
-	// Global ScummVM dialogs pause the engine while their modal event loop runs.
+	// Global dialogs pause the engine while their modal event loop runs.
 	// Do not consume those events as RA2 input: a key seen here would otherwise
 	// trip RA2's "any key unpauses gameplay" path while the Smush player must
 	// remain paused for the dialog/focus interval.
@@ -686,7 +686,7 @@ bool InsaneRebel2::notifyEvent(const Common::Event &event) {
 	if (_rebelYodaMode && event.type == Common::EVENT_KEYDOWN && !event.kbdRepeat && event.kbd.hasFlags(Common::KBD_ALT)) {
 		switch (event.kbd.keycode) {
 		case Common::KEYCODE_m:
-			// Retail DAT_0047ab60: Yoda-mode Movie Mode skips playable
+			// DAT_0047ab60: Yoda-mode Movie Mode skips playable
 			// sections and keeps the story/cutscene sequence moving.
 			_rebelMovieMode = !_rebelMovieMode;
 			debugC(DEBUG_INSANE, "Movie mode %s", _rebelMovieMode ? "enabled" : "disabled");
@@ -695,7 +695,7 @@ bool InsaneRebel2::notifyEvent(const Common::Event &event) {
 			return true;
 
 		case Common::KEYCODE_p:
-			// Retail DAT_0047ab64: Yoda-mode Auto Play makes gameplay
+			// DAT_0047ab64: Yoda-mode Auto Play makes gameplay
 			// computer controlled.
 			_rebelAutoPlay = !_rebelAutoPlay;
 			debugC(DEBUG_INSANE, "Auto play %s", _rebelAutoPlay ? "enabled" : "disabled");
@@ -915,7 +915,7 @@ bool InsaneRebel2::notifyEvent(const Common::Event &event) {
 				return true;
 
 			if (_menuInputActive && menuState) {
-				debugC(DEBUG_INSANE, "Back/menu action in menu - opening ScummVM menu");
+				debugC(DEBUG_INSANE, "Back/menu action in menu - opening global menu");
 				openMenuMainMenu(splayer);
 				return true;
 			}
@@ -929,7 +929,7 @@ bool InsaneRebel2::notifyEvent(const Common::Event &event) {
 					}
 				}
 
-				debugC(DEBUG_INSANE, "Back/menu action during gameplay - opening ScummVM menu");
+				debugC(DEBUG_INSANE, "Back/menu action during gameplay - opening global menu");
 				openGameplayMainMenu(splayer);
 				return true;
 			}
@@ -1036,7 +1036,7 @@ bool InsaneRebel2::notifyEvent(const Common::Event &event) {
 			return true;
 		}
 
-		debugC(DEBUG_INSANE, "Opening ScummVM menu from menu state");
+		debugC(DEBUG_INSANE, "Opening global menu from menu state");
 		openMenuMainMenu(splayer);
 		return true;
 	}
@@ -1058,7 +1058,7 @@ bool InsaneRebel2::notifyEvent(const Common::Event &event) {
 
 	if (event.type == Common::EVENT_MAINMENU && splayer &&
 			_gameState == kStateGameplay && _rebelHandler != 0) {
-		debugC(DEBUG_INSANE, "Main menu action during gameplay - opening ScummVM menu");
+		debugC(DEBUG_INSANE, "Main menu action during gameplay - opening global menu");
 		openGameplayMainMenu(splayer);
 		return true;
 	}
@@ -1081,7 +1081,7 @@ bool InsaneRebel2::notifyEvent(const Common::Event &event) {
 		}
 
 		// When paused during gameplay, ANY key unpauses (FUN_405A21 line 360-365).
-		// ESC additionally opens the ScummVM menu (original: quit key exits level).
+		// ESC additionally opens the global menu (original: quit key exits level).
 		if (splayer && splayer->_paused && _gameState == kStateGameplay) {
 			debugC(DEBUG_INSANE, "Key pressed while paused - unpausing");
 			// Restore the original palette saved by showPauseOverlay
@@ -1091,7 +1091,7 @@ bool InsaneRebel2::notifyEvent(const Common::Event &event) {
 			}
 			splayer->unpause();
 			if (event.kbd.keycode == Common::KEYCODE_ESCAPE && _rebelHandler != 0) {
-				debugC(DEBUG_INSANE, "ESC during pause - opening ScummVM menu");
+				debugC(DEBUG_INSANE, "ESC during pause - opening global menu");
 				openGameplayMainMenu(splayer);
 			}
 			return true;
@@ -1100,13 +1100,13 @@ bool InsaneRebel2::notifyEvent(const Common::Event &event) {
 		switch (event.kbd.keycode) {
 		case Common::KEYCODE_ESCAPE:
 			// ESC handling depends on game state:
-			// - In menus: Open the ScummVM menu (handled above)
-			// - During gameplay: Pause and open ScummVM menu
+			// - In menus: Open the global menu (handled above)
+			// - During gameplay: Pause and open the global menu
 			// - During cutscenes/intros: Skip video
 			if (splayer) {
 				if (_gameState == kStateGameplay && _rebelHandler != 0) {
-					// During active gameplay (handler != 0): pause and open ScummVM menu.
-					debugC(DEBUG_INSANE, "ESC pressed during gameplay - opening ScummVM menu");
+					// During active gameplay (handler != 0): pause and open the global menu.
+					debugC(DEBUG_INSANE, "ESC pressed during gameplay - opening global menu");
 					openGameplayMainMenu(splayer);
 				} else {
 					// During cutscenes/intros/mission briefings: skip video
@@ -1327,7 +1327,7 @@ InsaneRebel2::LevelDifficultyParams InsaneRebel2::getDifficultyParams() const {
 	int diff = CLIP(_difficulty, 0, 5);
 	int lvIdx = 0;
 
-	// Retail uses DAT_0047a7f8 as the per-segment difficulty table index.
+	// DAT_0047a7f8 is the per-segment difficulty table index.
 	// This is NOT the same as handler 0x26's gun "levelType" from opcode 6.
 	//
 	// Index mapping reconstructed from level handlers:
@@ -1435,7 +1435,7 @@ void InsaneRebel2::renderScoreHUD(byte *renderBitmap, int pitch, int width, int
 // ---------------------------------------------------------------------------
 // Pilot Data System
 // ---------------------------------------------------------------------------
-// Save/load pilot profiles using ScummVM's save file system.
+// Save/load pilot profiles using the save file system.
 // Original: FUN_00411980 (load) / FUN_00411A5D (save).
 
 const uint32 kPilotSaveMagic = MKTAG('R', 'A', '2', 'P');
@@ -1703,7 +1703,7 @@ int32 InsaneRebel2::processMouse() {
 					it->id, it->type, mousePos.x, mousePos.y,
 					it->rect.left, it->rect.top, it->rect.right, it->rect.bottom);
 
-				// Explosion scale is handler-specific in retail:
+				// Explosion scale is handler-specific in the original:
 				// - H8/H7/H26 use object half-width
 				// - H25 uses half-width + snapDistance (and type 100 doubles it)
 				int explosionHalfWidth = it->rect.width() / 2;
@@ -1912,8 +1912,8 @@ void InsaneRebel2::updateGameplayAimFromGamepad() {
 			activeGamepadAim = true;
 		}
 	} else if (_rebelHandler == 0x26) {
-		// Retail RA2 maps joystick axes into a small centered handler 0x26 reticle box.
-		// ScummVM deliberately exposes the full 320x200 mouse aim range for gamepads
+		// Original RA2 maps joystick axes into a small centered handler 0x26 reticle box.
+		// Gamepad aiming deliberately exposes the full 320x200 mouse aim range
 		// too, but preserves the original joystick feel: curved response for precise
 		// center aiming, and automatic return to screen center when the stick is released.
 		int axisX = 0;
diff --git a/engines/scumm/insane/rebel2/rebel.h b/engines/scumm/insane/rebel2/rebel.h
index ca5c902b908..4d1f76af1fd 100644
--- a/engines/scumm/insane/rebel2/rebel.h
+++ b/engines/scumm/insane/rebel2/rebel.h
@@ -63,7 +63,7 @@ public:
 	// ---------------------------------------------------------------------------
 	// Menu System
 	// ---------------------------------------------------------------------------
-	// Main game states (emulates retail state machine from FUN_004142BD)
+	// Main game states (emulates original state machine from FUN_004142BD)
 	enum GameState {
 		kStateIntro = 0,        // Stage 0: Intro/Credits sequence
 		kStateMainMenu = 1,     // Stage 1: Main menu (FUN_004147B2)
@@ -339,7 +339,7 @@ public:
 	};
 
 	// Main game entry point ??? full game loop (intro, menu, pilot, chapter, levels)
-	// Emulates the retail game flow from FUN_004142BD
+	// Emulates the original game flow from FUN_004142BD
 	void runGame();
 
 	// Play the intro sequence (CREDITS/O_OPEN_C, O_OPEN_D, OPEN/O_OPEN_A, O_OPEN_B)
@@ -419,8 +419,8 @@ public:
 	WaveEndResult processWaveEnd(int16 mask, int16 *budget, int16 threshold, uint16 flags);
 
 	// Play a raw SAN segment from a scripted level handler.
-	// Retail reaches these call sites through different wrappers/direct paths; this
-	// only collapses ScummVM's shared dispatch step. Callers still choose the original
+	// The original reaches these call sites through different wrappers/direct paths; this
+	// only collapses the shared dispatch step. Callers still choose the original
 	// 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);
@@ -655,7 +655,7 @@ public:
 	bool loadHandler25GrdSprites(byte *animData, int32 size, int16 par4);
 
 	// Parse Handler 25 shot-origin table text from opcode 8 (par4 = 8).
-	// Retail stores values into DAT_004578a6 / DAT_004578c6 (indices 5..19).
+	// FUN_0041CADB stores values into DAT_004578a6 / DAT_004578c6 (indices 5..19).
 	bool loadHandler25ShotOriginTable(Common::SeekableReadStream &b, int64 startPos, int64 remaining);
 
 	// Load Level 2 background from embedded ANIM
@@ -838,7 +838,7 @@ public:
 		bool active;
 	};
 
-	// Two zone tables matching retail DAT_0043fb00 (primary) and DAT_0043f9c8 (secondary)
+	// Two zone tables matching DAT_0043fb00 (primary) and DAT_0043f9c8 (secondary)
 	static const int kMaxCollisionZones = 5;
 	CollisionZone _primaryZones[kMaxCollisionZones];    // Sub-opcode 0x0D zones
 	CollisionZone _secondaryZones[kMaxCollisionZones];  // Sub-opcode 0x0E zones
@@ -922,10 +922,10 @@ public:
 	int16 _damageHighFlashCounter;       // DAT_00482408 - high-damage red flash (0..16)
 	int16 _damageShakeCounter;           // DAT_0048240c - screen shake countdown (0..10)
 	byte _damageSavedPalette[0x300];     // DAT_00459990 - palette snapshot before flash
-	byte _damageRestorePalette[0x300];   // ScummVM boundary restore snapshot
+	byte _damageRestorePalette[0x300];   // Boundary restore snapshot
 	bool _damageRestorePaletteValid;
 
-	// Rebel per-level counters / flags mapped from retail globals
+	// Rebel per-level counters / flags mapped from original globals
 	bool _rebelOp6Initialized; // Guard: opcode 6 init block (clearBit/links/wave) runs once per video
 	int _rebelHitCounter;    // DAT_0047ab80 - hit counter / state tracker
 	int _rebelKillCounter;   // DAT_0047ab88 - enemies destroyed this phase
@@ -955,7 +955,7 @@ public:
 	int _rebelViewMode1;     // DAT_00482270
 	int _rebelViewMode2;     // DAT_00482274
 
-	// Retail counters mirrored from DAT_00443618 (values 100..109) and DAT_004436e0 (mask counters 1..9)
+	// Original counters mirrored from DAT_00443618 (values 100..109) and DAT_004436e0 (mask counters 1..9)
 	short _rebelValueCounters[10]; // Index 0 -> value 100, ... Index 9 -> 109
 	short _rebelMaskCounters[10];  // Index 1..9 used; index 0 unused
 	int _rebelLastCounter;         // Mirrors DAT_0047ab90 (last updated counter)
@@ -1210,7 +1210,7 @@ public:
 	NutRenderer *_grd005Sprite;      // DAT_00482258 - GRD005 mode 3 overlay NUT
 
 	// Handler 25 shot-origin lookup tables from opcode 8/par4=8 text payload.
-	// Indices 5..19 are filled by the retail "%hd %hd ..." parser (FUN_0041CADB case 6).
+	// Indices 5..19 are filled by the "%hd %hd ..." parser in FUN_0041CADB case 6.
 	// Uncovered Level 2 firing uses indices 5..14.
 	int16 _grdShotOriginX[30];       // DAT_004578a6 equivalent
 	int16 _grdShotOriginY[30];       // DAT_004578c6 equivalent
diff --git a/engines/scumm/insane/rebel2/render.cpp b/engines/scumm/insane/rebel2/render.cpp
index 4256f4e6638..9d5c7dccb65 100644
--- a/engines/scumm/insane/rebel2/render.cpp
+++ b/engines/scumm/insane/rebel2/render.cpp
@@ -42,7 +42,7 @@ extern void smushDecodeRLE(byte *dst, const byte *src, int left, int top, int wi
 extern void smushDecodeUncompressed(byte *dst, const byte *src, int left, int top, int width, int height, int pitch);
 
 int getRebel2IndicatorScale(int width, int height) {
-	// Retail only doubles these anchors in true high-res mode (DAT_0047a808 >= 2).
+	// Original only doubles these anchors in true high-res mode (DAT_0047a808 >= 2).
 	// RA2's 424x260 low-res gameplay buffer is still displayed through a 320x200
 	// viewport, so it uses the original 320x200 indicator coordinates.
 	return (width >= 640 || height >= 400) ? 2 : 1;
@@ -555,7 +555,7 @@ void InsaneRebel2::spawnHandler25Shot(int x, int y) {
 			_turretShots[i].targetX = x + _viewX;
 			_turretShots[i].targetY = y + _viewY;
 
-			// Compute gun position from retail lookup tables.
+			// Compute gun position from original lookup tables.
 			// Original (FUN_41DB5E) stores:
 			//   DAT_0045791c[i] = gunXTable[spriteIdx] + DAT_00457910 - DAT_0045790c
 			//   DAT_00457920[i] = gunYTable[spriteIdx] + DAT_00457912 - DAT_0045790e
@@ -598,7 +598,7 @@ void InsaneRebel2::spawnHandler25Shot(int x, int y) {
 				int16 gunXTable = _grdShotOriginX[spriteIdx];
 				int16 gunYTable = _grdShotOriginY[spriteIdx];
 
-				// Mirrored X when DAT_00457902 != 0 in retail.
+				// Mirrored X when DAT_00457902 != 0.
 				if (_rebelFlightDir != 0) {
 					gunXTable = 320 - gunXTable;
 				}
@@ -644,7 +644,7 @@ Common::Point InsaneRebel2::getHandler7ProjectedPoint() {
 }
 
 Common::Point InsaneRebel2::getHandler7ShotTargetPoint() {
-	// Retail handler 7 targets the projected ship/crosshair point computed in
+	// Handler 7 targets the projected ship/crosshair point computed in
 	// FUN_0040d836; it does not use the generic mouse position for combat shots.
 	Common::Point projected = getHandler7ProjectedPoint();
 
@@ -653,7 +653,7 @@ Common::Point InsaneRebel2::getHandler7ShotTargetPoint() {
 }
 
 Common::Point InsaneRebel2::getHandler8ShotTargetPoint() {
-	// Retail handler 8 stores and draws the shot target from the damped ship
+	// Handler 8 stores and draws the shot target from the damped ship
 	// position in FUN_00401ccf, not from the current mouse/analog aim point.
 	return Common::Point(((_shipPosX - 0xa0) >> 3) + 0xa0,
 	                     ((_shipPosY - 0x28) >> 2) + 0x69);
@@ -1845,8 +1845,8 @@ void InsaneRebel2::checkCollisionZones(byte *renderBitmap, int pitch, int width,
 // Two modes: obstacle collision (secondary zones) and wall/boundary
 // collision (primary zones with per-edge push-back).
 //
-// The helpers in this block are ScummVM refactor helpers split out of
-// checkHandler7CollisionZones; they are not separate retail functions.
+// The helpers in this block are split out of checkHandler7CollisionZones;
+// they are not separate original functions.
 //
 bool InsaneRebel2::isHandler7ShipInsideObstacleZone(const InsaneRebel2::CollisionZone &zone, int margin) {
 	int x1 = zone.x1, y1 = zone.y1;
@@ -1936,7 +1936,7 @@ void InsaneRebel2::checkHandler7ObstacleZones(uint16 &warningMask) {
 		}
 
 		// FUN_40E35E line 104: mark near-danger proximity for shadow cue rendering.
-		// Uses the low byte of zone.filterValue (retail local_1c) to pick direction bits.
+		// Uses the low byte of zone.filterValue (original local_1c) to pick direction bits.
 		if (zone.field2 - 13 < zone.field1) {
 			uint32 bit = 4u << ((byte)zone.filterValue & 0x1f);
 			warningMask = (uint16)(warningMask | (uint16)bit);
@@ -4102,7 +4102,7 @@ void InsaneRebel2::renderExplosions(byte *renderBitmap, int pitch, int width, in
 }
 
 // renderExplosionFrame -- Shared explosion sprite path.
-// The original handlers reach this through separate retail functions. In the
+// The original handlers reach this through separate functions. In the
 // 320x200 path used here, they share centered NUT drawing; callers keep their
 // coordinate transforms, frame timing, and scale bucket rules explicit.
 void InsaneRebel2::renderExplosionFrame(byte *renderBitmap, int pitch, int width, int height,
@@ -4555,7 +4555,7 @@ void InsaneRebel2::renderHandler25LaserShots(byte *renderBitmap, int pitch, int
 		pan = CLIP<int16>(pan, -127, 127);
 		// TODO: Apply panning to sound channel i+1
 
-		// Retail adds DAT_0045790c/0e at render time to both gun and target.
+		// FUN_00407FCB adds DAT_0045790c/0e at render time to both gun and target.
 		int16 targetX = _turretShots[i].targetX + _rebelViewOffsetX;
 		int16 targetY = _turretShots[i].targetY + _rebelViewOffsetY;
 
@@ -4602,7 +4602,7 @@ void InsaneRebel2::renderHandler8MonitorEffect(byte *renderBitmap, int pitch, in
 		return;
 
 	// FUN_0041C6EC/FUN_00413EFC: remap every other gameplay row through
-	// FUN_00410721()+0x400. ScummVM uses the primary edge table, matching
+	// FUN_00410721()+0x400. Use the primary edge table, matching
 	// DAT_0047a81c == 0.
 	const byte *monitorTable = _edgeTable + 0x400;
 	for (int y = 1; y < effectHeight; y += 2) {
diff --git a/engines/scumm/smush/rebel/smush_player_ra1.cpp b/engines/scumm/smush/rebel/smush_player_ra1.cpp
index 43f4d65e882..69c338d5ba6 100644
--- a/engines/scumm/smush/rebel/smush_player_ra1.cpp
+++ b/engines/scumm/smush/rebel/smush_player_ra1.cpp
@@ -286,7 +286,7 @@ bool SmushPlayerRebel1::handleGameFetch(int32 subSize, Common::SeekableReadStrea
 					top += _ra1ViewportOffsetY;
 				} else {
 					ra1ApplyCenteredFetchPlacement(rebel1, _storedFobjWidth, _storedFobjHeight, left, top);
-					// ScummVM currently emulates the RA1 camera with a source-window crop
+					// RA1 camera emulation currently uses a source-window crop
 					// for interactive scenes. FTCH placement from the original executable
 					// is computed in fixed presentation space, so convert it back into the
 					// cropped buffer space used by the current renderer.
@@ -347,7 +347,7 @@ void SmushPlayerRebel1::ra1HandleGost(int32 subSize, Common::SeekableReadStream
 
 	// DOS reuses the most recent FOBJ payload for RA1 GOST and places it at the
 	// absolute BE32 coordinates stored in the chunk. Priority bits are identified
-	// here but not yet modeled in the generic ScummVM decode path.
+	// here but not yet modeled in the generic decode path.
 	decodeFrameObject(_lastFobjCodec, _lastFobjData, ghostX, ghostY,
 		_lastFobjWidth, _lastFobjHeight, _lastFobjDataSize);
 }
@@ -416,7 +416,7 @@ void SmushPlayerRebel1::ra1HandleDeltaPalette(int32 subSize, Common::SeekableRea
 			b.skip(remaining);
 
 		// Command 2 in the DOS dispatcher first restores the palette state before
-		// loading a new delta table. ScummVM keeps the active palette in _pal, so
+		// loading a new delta table. The active palette lives in _pal, so
 		// marking it dirty is the corresponding visible-side effect.
 		if (command == 2)
 			setDirtyColors(0, 255);
diff --git a/engines/scumm/smush/rebel/smush_player_ra2.cpp b/engines/scumm/smush/rebel/smush_player_ra2.cpp
index 163c1923f60..dabef54a36e 100644
--- a/engines/scumm/smush/rebel/smush_player_ra2.cpp
+++ b/engines/scumm/smush/rebel/smush_player_ra2.cpp
@@ -229,7 +229,7 @@ void SmushPlayerRebel2::initGameVideoState() {
 	_width = _vm->_screenWidth;
 	_height = _vm->_screenHeight;
 
-	// Keep ScummVM's virtual screen consistent with FUN_00424d70: bit 0x20
+	// Keep the virtual screen consistent with FUN_00424d70: bit 0x20
 	// controls per-frame clearing. Videos that clear every frame can also start
 	// from a cleared target; videos with bit 0x20 set preserve until their first
 	// decoded frame overwrites or composes over it.
@@ -451,7 +451,7 @@ bool SmushPlayerRebel2::handleGameTextRendering(const char *str, int fontId, int
 												int pos_x, int pos_y, int left, int top,
 												int width, int height, TextStyleFlags flg) {
 	// RA2 dialogue subtitles. Only render them when subtitles are enabled ??? this honors
-	// both ScummVM's global "subtitles" setting and the in-game TEXT toggle (which writes
+	// both the global "subtitles" setting and the in-game TEXT toggle (which writes
 	// the same ConfMan key). Still return true so the base handler treats the chunk as
 	// handled. Querying ConfMan per chunk also lets the setting take effect mid-video.
 	if (ConfMan.getBool("subtitles"))
@@ -732,7 +732,7 @@ bool SmushPlayerRebel2::handleGameAnimHeader(byte *headerContent) {
 		debugC(DEBUG_SMUSH, "SmushPlayerRebel2::handleGameAnimHeader: RA2 AHDR has 0x0 dims - using screen size %dx%d", _width, _height);
 	} else if (width != _vm->_screenWidth || height != _vm->_screenHeight) {
 		// FUN_00407FCB/FUN_0040C3CC update the active frame descriptor from
-		// IACT opcode 6. In ScummVM's low-res path the descriptor remains
+		// IACT opcode 6. In the low-res path the descriptor remains
 		// 320x200 and perspective is applied through FUN_00424510-equivalent
 		// FOBJ offsets. Do not let AHDR alone change pitch while _dst still
 		// points at the virtual screen; FOBJ selection will allocate a larger
@@ -1123,7 +1123,7 @@ void SmushPlayerRebel2::ra2HandleGost(int32 subSize, Common::SeekableReadStream
 		_lastFobjWidth, _lastFobjHeight, _lastFobjCodec);
 
 	// Priority bits (0x2000/0x4000/0x6000) are currently not modeled in
-	// ScummVM's SMUSH decoders. Coordinate-correct re-decode restores expected
+	// the SMUSH decoders. Coordinate-correct re-decode restores expected
 	// RA2 chapter preview behavior.
 	ra2PrepareFrameObjectSurface(left, top, _lastFobjWidth, _lastFobjHeight);
 	decodeFrameObject(_lastFobjCodec, _lastFobjData, left, top,



From noreply at scummvm.org  Wed Jun 10 16:02:32 2026
From: noreply at scummvm.org (tag2015)
Date: Wed, 10 Jun 2026 16:02:32 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 16e283fe1adb524058beb91063f32fb6f2a65d41
Message-ID: <20260610160232.83DD17005CC@ron.scummvm.net>

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

Summary:
16e283fe1a DIRECTOR: Add detection for Cats and Hats Dutch


Commit: 16e283fe1adb524058beb91063f32fb6f2a65d41
    https://github.com/scummvm/scummvm/commit/16e283fe1adb524058beb91063f32fb6f2a65d41
Author: Walter Agazzi (walter.agazzi at protonmail.com)
Date: 2026-06-10T18:01:45+02:00

Commit Message:
DIRECTOR: Add detection for Cats and Hats Dutch

Changed paths:
    engines/director/detection_tables.h


diff --git a/engines/director/detection_tables.h b/engines/director/detection_tables.h
index 23587c7d912..10c34f9d74b 100644
--- a/engines/director/detection_tables.h
+++ b/engines/director/detection_tables.h
@@ -8411,9 +8411,14 @@ static const DirectorGameDescription gameDescriptions[] = {
 	MACGAME1("catseyeview", "Vol. 7", "Cat'sEyeView7", "55d01cca925752c170ac1e9c01d1f87e", 1030462, 601),
 	WINGAME1t("catseyeview", "Vol. 7", "Cat'sEyeView7.exe", "1c8ec55a7a65ba13890697e0e92670a5", 6017657, 600),
 
+	// German title is "Hexentanz und Firlefanz", published by Tivola
 	// Bilingual English/German
 	MACGAME1("catshats", "", "Cats and Hats", "r:cc27aab06406e53f53bcf7efd154ac1c", 1033729, 602),
 	WINGAME1("catshats", "", "HEXEN32.EXE",	  "t:a6d98f124483409c0aa060fe0cfec013", 1661593, 602),
+	// Dutch title is "Speurtocht in Heksenstad", published by Lannoo
+	// Bilingual English/Dutch
+	MACGAME1("catshats", "", "Speurtocht in Heksenstad", "r:8efda43c2eac46f285b30638b366de4a",  115391, 602),
+	WINGAME1("catshats", "", "HEXEN32.EXE",				 "t:b15281937090355d004776b1a702f04e", 1660530, 602),
 
 	// Developed by Real World Multimedia, Wiltshire, England (1997)
 	// Distributed in Germany by Funware, Hamburg



From noreply at scummvm.org  Wed Jun 10 18:47:43 2026
From: noreply at scummvm.org (athrxx)
Date: Wed, 10 Jun 2026 18:47:43 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 2989f97c5dfd5cbe501bf8475af8313bf2c8476b
Message-ID: <20260610184743.63C687005CC@ron.scummvm.net>

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

Summary:
2989f97c5d SCUMM: (EGA dithering mode) - fix color glitches in DOTT


Commit: 2989f97c5dfd5cbe501bf8475af8313bf2c8476b
    https://github.com/scummvm/scummvm/commit/2989f97c5dfd5cbe501bf8475af8313bf2c8476b
Author: athrxx (athrxx at scummvm.org)
Date: 2026-06-10T20:47:02+02:00

Commit Message:
SCUMM: (EGA dithering mode) - fix color glitches in DOTT

- always set VAR_VIDEOMODE to VGA in SCUMM6 games, since the EGA
handling in the room entry and verb scripts is just leftover from MI2 which
wasn't properly updated (since the EGA mode is not supported in original
SCUMM6 games).
- make necessary fix to palette updates in EGA mode

Changed paths:
    engines/scumm/palette.cpp
    engines/scumm/room.cpp
    engines/scumm/vars.cpp


diff --git a/engines/scumm/palette.cpp b/engines/scumm/palette.cpp
index 4c5da8ce304..a3758d7f7df 100644
--- a/engines/scumm/palette.cpp
+++ b/engines/scumm/palette.cpp
@@ -1489,9 +1489,11 @@ void ScummEngine::setCurrentPalette(int palindex) {
 			// Might be glitchy, though...
 			const byte *p = getPalettePtr(_curPalIndex, _roomResource);
 			for (int i = 0; i < 256; ++i) {
-				byte col = egaFindBestMatch(p[0], p[1], p[2]);
-				_egaColorMap[0][i] = col & 0x0F;
-				_egaColorMap[1][i] = col >> 4;
+				if (_game.version < 6 || i == 15 || p[0] < 252 || p[1] < 252 || p[2] < 252) {
+					byte col = egaFindBestMatch(p[0], p[1], p[2]);
+					_egaColorMap[0][i] = col & 0x0F;
+					_egaColorMap[1][i] = col >> 4;
+				}
 				p += 3;
 			}
 		} else {
diff --git a/engines/scumm/room.cpp b/engines/scumm/room.cpp
index e6a6109ebcc..2b29cdf4df1 100644
--- a/engines/scumm/room.cpp
+++ b/engines/scumm/room.cpp
@@ -630,6 +630,17 @@ void ScummEngine::resetRoomSubBlocks() {
 	// We need to setup the current palette before initCycl for Indy4 Amiga.
 	if (_PALS_offs || _CLUT_offs)
 		setCurrentPalette(0);
+	
+	if (_game.id == GID_TENTACLE && VAR(VAR_VIDEOMODE) == 13) {
+		// This is a hack to avoid color glitches caused by incomplete EGA mode handling in the scripts.
+		// The original DOTT interpreter (executable and scripts) has various EGA mode handling leftovers
+		// from SCUMM5 in it, but the mode isn't supported. We always set VAR_VIDEOMODE to VGA setting for
+		// SCUMM6, so this codepath here is currently never executed. But it would be if we (for whatever reason)
+		// decided to change that.
+		const byte *room = getResourceAddress(rtRoom, _roomResource);
+		if (findPalInPals(room + _PALS_offs, 1))
+			setCurrentPalette(1);
+	}
 
 	// Color cycling
 	// HE 7.0 games load resources but don't use them.
diff --git a/engines/scumm/vars.cpp b/engines/scumm/vars.cpp
index e5f31017814..219aab84a56 100644
--- a/engines/scumm/vars.cpp
+++ b/engines/scumm/vars.cpp
@@ -849,7 +849,9 @@ void ScummEngine::setVideoModeVarToCurrentConfig() {
 		VAR(VAR_VIDEOMODE) = 6;
 	else if (_renderMode == Common::kRenderHercA || _renderMode == Common::kRenderHercG)
 		VAR(VAR_VIDEOMODE) = 30;
-	else if (_renderMode == Common::kRenderEGA)
+	else if (_renderMode == Common::kRenderEGA && _game.version < 6)
+		// DOTT has leftover handling for VAR_VIDEOMODE in some of its scripts (room entry and verb scripts). But this hasn't been updated to work
+		// correctly, it's really just leftover from MI2. It is easier to prevent graphics glitches by just setting the var to VGA in that case.
 		VAR(VAR_VIDEOMODE) = 13;
 	else
 		VAR(VAR_VIDEOMODE) = 19;



From noreply at scummvm.org  Wed Jun 10 20:26:40 2026
From: noreply at scummvm.org (ScummVM-Translations)
Date: Wed, 10 Jun 2026 20:26:40 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 9eed60c8061ef4d5dcc55049488bd4f6ad3b69b2
Message-ID: <20260610202640.AA55D7005CC@ron.scummvm.net>

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

Summary:
9eed60c806 I18N: Update translation (Italian)


Commit: 9eed60c8061ef4d5dcc55049488bd4f6ad3b69b2
    https://github.com/scummvm/scummvm/commit/9eed60c8061ef4d5dcc55049488bd4f6ad3b69b2
Author: Walter Agazzi (walter.agazzi at protonmail.com)
Date: 2026-06-10T20:26:33Z

Commit Message:
I18N: Update translation (Italian)

Currently translated at 100.0% (3208 of 3208 strings)

Changed paths:
    po/it_IT.po


diff --git a/po/it_IT.po b/po/it_IT.po
index 938e0dce233..3d38961282e 100644
--- a/po/it_IT.po
+++ b/po/it_IT.po
@@ -8,7 +8,7 @@ msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
 "POT-Creation-Date: 2026-06-09 13:22+0000\n"
-"PO-Revision-Date: 2026-06-09 10:24+0000\n"
+"PO-Revision-Date: 2026-06-10 20:26+0000\n"
 "Last-Translator: Walter Agazzi <walter.agazzi at protonmail.com>\n"
 "Language-Team: Italian <https://translations.scummvm.org/projects/scummvm/"
 "scummvm/it/>\n"
@@ -13671,14 +13671,14 @@ msgid "Disable player damage"
 msgstr "Disabilita i danni al giocatore"
 
 #: engines/scumm/metaengine.cpp:909
-#, fuzzy
-#| msgid "Fast mode"
 msgid "Yoda mode"
-msgstr "Modalit?? velocizzata"
+msgstr "Modalit?? Yoda"
 
 #: engines/scumm/metaengine.cpp:910
 msgid "Enable original Yoda mode shortcuts, including movie mode and auto play"
 msgstr ""
+"Abilita le scorciatoie della modalit?? Yoda, compresa la modalit?? film e il "
+"gioco automatico"
 
 #: engines/scumm/metaengine.cpp:1085
 msgid "Down left"



From noreply at scummvm.org  Wed Jun 10 23:16:23 2026
From: noreply at scummvm.org (bluegr)
Date: Wed, 10 Jun 2026 23:16:23 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 0d818b9f416b2f1d0a42b0f82c81980c19dcb829
Message-ID: <20260610231623.B29FD7005CC@ron.scummvm.net>

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:
0b94cea8c6 NANCY: More work on multibuildpuzzle for Nancy10
0d818b9f41 NANCY: Fix cake baking video in Nancy 10


Commit: 0b94cea8c6814743fcd85b41e4238d3dffbbab24
    https://github.com/scummvm/scummvm/commit/0b94cea8c6814743fcd85b41e4238d3dffbbab24
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-06-11T02:16:10+03:00

Commit Message:
NANCY: More work on multibuildpuzzle for Nancy10

The cake puzzle is solvable now. The mixing animation is still missing

Changed paths:
    engines/nancy/action/puzzle/multibuildpuzzle.cpp
    engines/nancy/action/puzzle/multibuildpuzzle.h


diff --git a/engines/nancy/action/puzzle/multibuildpuzzle.cpp b/engines/nancy/action/puzzle/multibuildpuzzle.cpp
index 91a03ab0300..5e52f8e20d3 100644
--- a/engines/nancy/action/puzzle/multibuildpuzzle.cpp
+++ b/engines/nancy/action/puzzle/multibuildpuzzle.cpp
@@ -33,6 +33,10 @@
 namespace Nancy {
 namespace Action {
 
+// Nancy 10 TODOs:
+//  - completion-animation playback (data read, never rendered)
+//  - _retainState save/restore via a PuzzleData entry
+//  - 4th cursor id, 7 ?? 33-byte string slots, per-piece reserved rect
 void MultiBuildPuzzle::init() {
 	g_nancy->_resource->loadImage(_primaryImageName, _primaryImage);
 	_primaryImage.setTransparentColor(_drawSurface.getTransparentColor());
@@ -407,9 +411,7 @@ void MultiBuildPuzzle::handleInput(NancyInput &input) {
 		return;
 	Common::Rect vpScreen = viewData->screenPosition;
 
-	// Exit hotspots are checked against full-screen coords, so they work even
-	// when the cursor strays below the viewport (cake mixing's bottom-edge
-	// exit at y=340 sits outside the viewport in Nancy 10).
+	// Exit hotspots can sit below the viewport (cake mixing).
 	if (!_isDragging && _selectedPiece == -1 && !vpScreen.contains(input.mousePos)) {
 		if (!checkExitHotspot(_exitHotspot, _exitCursorID1, input))
 			checkExitHotspot(_exitHotspot2, _exitCursorID2, input);
@@ -458,19 +460,27 @@ void MultiBuildPuzzle::handleInput(NancyInput &input) {
 
 			if (validDrop) {
 				pp.isPlaced = true;
+				const bool isNancy10 = g_nancy->getGameType() >= kGameTypeNancy10;
+				if (isNancy10) {
+					// Clone placements bump the source piece's counter.
+					int srcIdx = (pp.typeIdx >= 0) ? pp.typeIdx : placedIdx;
+					if (_pieces[srcIdx].placeCount < 255)
+						_pieces[srcIdx].placeCount++;
+				}
 				g_nancy->_sound->playSound(_dropSound);
 
 				// Counter pieces respawn at home for unlimited supply.
 				if (pp.counterByte != 0)
 					spawnCounterPiece(placedIdx);
+
+				if (isNancy10)
+					updateSolveFlags();
 			} else {
 				pp.gameRect = pp.homeRect;
 			}
 
 			updatePieceRender(placedIdx);
 
-			// Solve check runs on drop for puzzles with _autoSolveOnDrop, and
-			// also once piece count grows past the original's auto-solve trigger.
 			if (_autoSolveOnDrop || _pieces.size() > 79)
 				checkIfSolved();
 
@@ -615,30 +625,66 @@ void MultiBuildPuzzle::checkIfSolvedOnExit() {
 	}
 }
 
-void MultiBuildPuzzle::checkIfSolved() {
-	// Count = placed pieces with counterByte == 0, plus the spawn delta
-	// (so counter-piece puzzles like sandwich count each placement).
-	uint16 count = 0;
+bool MultiBuildPuzzle::updateSolveFlags() {
+	uint16 total = 0;
 	for (uint i = 0; i < _numPieces; ++i) {
-		if (_pieces[i].isPlaced && _pieces[i].counterByte == 0)
-			++count;
+		if (_pieces[i].counterByte == 0 || _allowAltZoneSnap)
+			total += _pieces[i].placeCount;
 	}
-	count += (uint16)(_pieces.size() - _numPieces);
+	total += (uint16)(_pieces.size() - _numPieces);
 
-	if (count < _requiredPieces)
-		return;
+	if (total < _requiredPieces)
+		return false;
+
+	if (_cancelScene._flag.label != kFlagNoLabel)
+		NancySceneState.setEventFlag(_cancelScene._flag);
+
+	// Preemptively clear the solve flag; the exact-match check below re-sets it.
+	if (_solveScene._flag.label != kFlagNoLabel)
+		NancySceneState.setEventFlag(_solveScene._flag.label, _solveScene._flag.flag ? 0 : 1);
 
-	// Bail without solving on any constraint failure.
 	for (uint i = 0; i < _numPieces; ++i) {
-		if (_pieces[i].isPlaced && _pieces[i].mustNotPlace)
+		if (_pieces[i].placeCount > 0 && _pieces[i].mustNotPlace > 0)
+			return false;
+		if (_pieces[i].placeCount != _pieces[i].mustPlace)
+			return false;
+	}
+
+	if (_solveScene._flag.label != kFlagNoLabel)
+		NancySceneState.setEventFlag(_solveScene._flag);
+
+	return true;
+}
+
+void MultiBuildPuzzle::checkIfSolved() {
+	const bool isNancy10 = g_nancy->getGameType() >= kGameTypeNancy10;
+
+	if (isNancy10) {
+		// Exact-count match; solveScene == kNoScene means the dialog handles
+		// the transition via the solve flag set in updateSolveFlags.
+		bool exactMatch = updateSolveFlags();
+		if (!exactMatch || _solveScene._sceneChange.sceneID == kNoScene)
 			return;
-		if (!_pieces[i].isPlaced && _pieces[i].mustPlace)
+	} else {
+		// Nancy 9: bool placement semantics, no per-drop flag setting.
+		uint16 count = 0;
+		for (uint i = 0; i < _numPieces; ++i) {
+			if (_pieces[i].isPlaced && _pieces[i].counterByte == 0)
+				++count;
+		}
+		count += (uint16)(_pieces.size() - _numPieces);
+
+		if (count < _requiredPieces)
 			return;
+
+		for (uint i = 0; i < _numPieces; ++i) {
+			if (_pieces[i].isPlaced && _pieces[i].mustNotPlace)
+				return;
+			if (!_pieces[i].isPlaced && _pieces[i].mustPlace)
+				return;
+		}
 	}
 
-	// Play sound + caption inline (rather than parking in kPlaySolveSound for
-	// execute() to pick up) so the transition is deterministic regardless of
-	// action-record processing order.
 	_isSolved = true;
 	g_nancy->_sound->playSound(_solveSound);
 
@@ -669,10 +715,8 @@ void MultiBuildPuzzle::updatePieceRender(int pieceIdx) {
 	bool isSelected = (!_isDragging && pieceIdx == _selectedPiece);
 	bool isDragging  = (_isDragging  && pieceIdx == _pickedUpPiece);
 
-	// Nancy 10: at-rest pieces are already painted into the primary scene
-	// overlay at their home positions, so the engine doesn't draw them.
-	// Hide the placed piece too when a completion-animation overlay handles
-	// the visual (cake mixing).
+	// Nancy 10: at-rest pieces are baked into the primary overlay; placed
+	// pieces hide too when the completion animation will cover them.
 	if (g_nancy->getGameType() >= kGameTypeNancy10 && !isDragging && !isSelected) {
 		if (!p.isPlaced || _hasAnimImage) {
 			p.setVisible(false);
diff --git a/engines/nancy/action/puzzle/multibuildpuzzle.h b/engines/nancy/action/puzzle/multibuildpuzzle.h
index df8e5b7a686..c47b1382355 100644
--- a/engines/nancy/action/puzzle/multibuildpuzzle.h
+++ b/engines/nancy/action/puzzle/multibuildpuzzle.h
@@ -66,8 +66,9 @@ protected:
 		Common::Rect cuSrcRect;   // Source in closeup image
 		Common::Rect placedDstRect; // Nancy 10: placement destination on screen
 		uint8 counterByte = 0;    // Non-zero: respawns on placement; doesn't count toward solve
-		uint8 mustPlace = 0;      // Must be placed for solution
-		uint8 mustNotPlace = 0;   // Placing this fails the solution check
+		uint8 mustPlace = 0;      // Required placement count (exact match for solve). 0/1 in Nancy 9; arbitrary in Nancy 10+ (e.g. cake mixing recipe quantities)
+		uint8 mustNotPlace = 0;   // Non-zero: placing this fails the solution check
+		uint8 placeCount = 0;     // Runtime: number of times this piece (or any clone of it) has been placed
 
 		Common::Rect gameRect;    // Current viewport-space rect
 		int curRotation = 0;
@@ -149,6 +150,10 @@ protected:
 
 	void checkIfSolved();
 	void checkIfSolvedOnExit();
+	// Updates the global cancel/solve event flags from the current placement
+	// state. Returns true on exact match (all placeCounts == mustPlace, no
+	// mustNotPlace pieces placed).
+	bool updateSolveFlags();
 	void updatePieceRender(int pieceIdx);
 	static void rotateSurface90CW(const Graphics::ManagedSurface &src, Graphics::ManagedSurface &dst);
 	// Clone an existing piece at the end of _pieces (counter-piece respawn).


Commit: 0d818b9f416b2f1d0a42b0f82c81980c19dcb829
    https://github.com/scummvm/scummvm/commit/0d818b9f416b2f1d0a42b0f82c81980c19dcb829
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-06-11T02:16:12+03:00

Commit Message:
NANCY: Fix cake baking video in Nancy 10

Extend setting video format to large in secondary movies for Nancy 10

Changed paths:
    engines/nancy/action/secondarymovie.cpp


diff --git a/engines/nancy/action/secondarymovie.cpp b/engines/nancy/action/secondarymovie.cpp
index 499d7b07791..c8194710fc5 100644
--- a/engines/nancy/action/secondarymovie.cpp
+++ b/engines/nancy/action/secondarymovie.cpp
@@ -56,7 +56,7 @@ void PlaySecondaryMovie::readData(Common::SeekableReadStream &stream) {
 	ser.syncAsUint16LE(_videoType, kGameTypeNancy7);
 	ser.skip(2, kGameTypeVampire, kGameTypeNancy9); // videoPlaySource
 	ser.syncAsUint16LE(_videoFormat);
-	if (g_nancy->getGameType() >= kGameTypeNancy11)
+	if (g_nancy->getGameType() >= kGameTypeNancy10)
 		_videoFormat = kLargeVideoFormat;
 	ser.skip(4, kGameTypeVampire, kGameTypeVampire); // paletteStart, paletteSize
 	ser.skip(2, kGameTypeVampire, kGameTypeNancy9);  // hasBitmapOverlaySurface



From noreply at scummvm.org  Thu Jun 11 03:22:55 2026
From: noreply at scummvm.org (ScummVM-Translations)
Date: Thu, 11 Jun 2026 03:22:55 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm-web master ->
 20451d7a7cc50474d41a4bde0273d4ac4991a7cd
Message-ID: <20260611032255.72CE67005BC@ron.scummvm.net>

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

Summary:
20451d7a7c L10N: Update translation (Italian) (Strings)


Commit: 20451d7a7cc50474d41a4bde0273d4ac4991a7cd
    https://github.com/scummvm/scummvm-web/commit/20451d7a7cc50474d41a4bde0273d4ac4991a7cd
Author: Walter Agazzi (walter.agazzi at protonmail.com)
Date: 2026-06-11T03:22:49Z

Commit Message:
L10N: Update translation (Italian) (Strings)

Currently translated at 100.0% (173 of 173 strings)

Changed paths:
    data/it/strings.json


diff --git a/data/it/strings.json b/data/it/strings.json
index e29b450d..a925e61a 100644
--- a/data/it/strings.json
+++ b/data/it/strings.json
@@ -177,5 +177,6 @@
     "compatibilityDetailsDetails": "Dettagli",
     "downloadsBadgeTitle": "Scarica ScummVM {version}",
     "menuMainFreewareGames": "Giochi Freeware e File Supplementari",
-    "compatibilityDetailsChartColScummVmId": "ID ScummVM"
+    "compatibilityDetailsChartColScummVmId": "ID ScummVM",
+    "menuMainDumper": "Dumper Companion"
 }



From noreply at scummvm.org  Thu Jun 11 07:58:52 2026
From: noreply at scummvm.org (kelmer44)
Date: Thu, 11 Jun 2026 07:58:52 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 14748588ac930dc17d999a059e4fc7b0b7acb9f9
Message-ID: <20260611075852.56A3D7005BC@ron.scummvm.net>

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

Summary:
14748588ac PELROCK: Make sure pointers are initialized in sound.cpp PVS-Studio V575, V769


Commit: 14748588ac930dc17d999a059e4fc7b0b7acb9f9
    https://github.com/scummvm/scummvm/commit/14748588ac930dc17d999a059e4fc7b0b7acb9f9
Author: kelmer (kelmer at gmail.com)
Date: 2026-06-11T09:58:44+02:00

Commit Message:
PELROCK: Make sure pointers are initialized in sound.cpp PVS-Studio V575, V769

Changed paths:
    engines/pelrock/sound.cpp


diff --git a/engines/pelrock/sound.cpp b/engines/pelrock/sound.cpp
index 3a082f0918a..b6cdf46135a 100644
--- a/engines/pelrock/sound.cpp
+++ b/engines/pelrock/sound.cpp
@@ -184,7 +184,8 @@ int SoundManager::playSound(SonidoFile sound, int channel, int loopCount) {
 	sonidosFile.read(data, sound.size);
 	sonidosFile.close();
 
-	assert(data);
+	if (!data)
+		return -1;
 	SoundFormat format = detectFormat(data, sound.size);
 	uint32 sampleRate = getSampleRate(data, format);
 	Audio::SeekableAudioStream *stream = nullptr;
@@ -203,8 +204,11 @@ int SoundManager::playSound(SonidoFile sound, int channel, int loopCount) {
 
 		uint32 pcmSize = sound.size - headerSize;
 		byte *pcmData = (byte *)malloc(pcmSize);
-		assert(pcmData);
-		assert(data);
+		if (!pcmData || !data) {
+			free(data);
+			free(pcmData);
+			return -1;
+		}
 		memcpy(pcmData, data + headerSize, pcmSize);
 		free(data);
 



From noreply at scummvm.org  Thu Jun 11 11:01:26 2026
From: noreply at scummvm.org (mduggan)
Date: Thu, 11 Jun 2026 11:01:26 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 ae6bf128b60e8d069f89d41c18b14b2ff3a82763
Message-ID: <20260611110127.034DF7005BC@ron.scummvm.net>

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

Summary:
ae6bf128b6 ACCESS: Fix unhandled enum value warning in switch


Commit: ae6bf128b60e8d069f89d41c18b14b2ff3a82763
    https://github.com/scummvm/scummvm/commit/ae6bf128b60e8d069f89d41c18b14b2ff3a82763
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2026-06-11T21:00:43+10:00

Commit Message:
ACCESS: Fix unhandled enum value warning in switch

Changed paths:
    engines/access/bubble_box.cpp


diff --git a/engines/access/bubble_box.cpp b/engines/access/bubble_box.cpp
index 5c2246d31ca..7e851dfb4cf 100644
--- a/engines/access/bubble_box.cpp
+++ b/engines/access/bubble_box.cpp
@@ -98,9 +98,11 @@ void BubbleBox::clearBubbles() {
 void BubbleBox::placeBubble(const Common::String &msg) {
 	switch (_vm->getGameID()) {
 		case kGameMartianMemorandum: 	_vm->_screen->_maxChars = 30; break;
+		case kGameCountdown:			_vm->_screen->_maxChars = 40; break;
 		case kGameAmazon: 				_vm->_screen->_maxChars = 27; break;
 		// All Noctropolis messages are pre-wrapped.
 		case kGameNoctropolis: 			_vm->_screen->_maxChars = 200; break;
+		default: error("Unsupported game type in BubbleBox::placeBubble()");
 	}
 	placeBubble1(msg);
 }



From noreply at scummvm.org  Thu Jun 11 11:39:21 2026
From: noreply at scummvm.org (tag2015)
Date: Thu, 11 Jun 2026 11:39:21 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 ac5c659d7e706fdfab3abfcea4c16c9a35e79601
Message-ID: <20260611113921.C655C7005BC@ron.scummvm.net>

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:
9d67bb390b WINTERMUTE: Add detection for Rhiannon russian version
ac5c659d7e DIRECTOR: Add detection for gary gadget swedish


Commit: 9d67bb390bf62e502cc2d2a3a15dfa46758ce8b6
    https://github.com/scummvm/scummvm/commit/9d67bb390bf62e502cc2d2a3a15dfa46758ce8b6
Author: Walter Agazzi (walter.agazzi at protonmail.com)
Date: 2026-06-11T13:13:43+02:00

Commit Message:
WINTERMUTE: Add detection for Rhiannon russian version

Fix #16648

Changed paths:
    engines/wintermute/detection_tables.h


diff --git a/engines/wintermute/detection_tables.h b/engines/wintermute/detection_tables.h
index e57e829e3d4..d47b2c83b41 100644
--- a/engines/wintermute/detection_tables.h
+++ b/engines/wintermute/detection_tables.h
@@ -4266,6 +4266,11 @@ static const WMEGameDescription gameDescriptions[] = {
 	WME_WINENTRY("rhiannon", "",
 		WME_ENTRY1s("data.dcp", "38e104ce1ebaf73e81446ab4344e1ad7", 1025512723), Common::DE_DEU, ADGF_NO_FLAGS, WME_1_8_10),
 
+	// Rhiannon: Curse of the four Branches (Russian Logrus / 1C Company)
+	// TRAC report #16648
+	WME_WINENTRY("rhiannon", "",
+		WME_ENTRY1s("data.dcp", "c833d8e6b25ba0b44cc22ee2421eab68", 1024584340), Common::RU_RUS, ADGF_NO_FLAGS, WME_1_8_10),
+
 	// Rhiannon: Curse of the four Branches (English PC DVD)
 	WME_WINENTRY("rhiannon", "DVD",
 		WME_ENTRY1s("data.dcp", "6736bbc921bb6ce5161b3ad095a97bd4", 1053441028), Common::EN_ANY, ADGF_NO_FLAGS, WME_1_8_6),


Commit: ac5c659d7e706fdfab3abfcea4c16c9a35e79601
    https://github.com/scummvm/scummvm/commit/ac5c659d7e706fdfab3abfcea4c16c9a35e79601
Author: Walter Agazzi (walter.agazzi at protonmail.com)
Date: 2026-06-11T13:13:43+02:00

Commit Message:
DIRECTOR: Add detection for gary gadget swedish

Fix #16637

Changed paths:
    engines/director/detection_tables.h


diff --git a/engines/director/detection_tables.h b/engines/director/detection_tables.h
index 10c34f9d74b..e7fa1b80af5 100644
--- a/engines/director/detection_tables.h
+++ b/engines/director/detection_tables.h
@@ -8600,7 +8600,9 @@ static const DirectorGameDescription gameDescriptions[] = {
 	MACGAME1_l("garygadget1", "",	  "Autos bauen mit Willy Werkel", "r:d251e21d690ba5ad35509e83d45dd0d0",  1032378, Common::DE_DEU, 602),
 	MACGAME2_l("garygadget1", "",	  "Program",					  "rt:02afd6d4d73e63517688ca8373df5d57", 1032442,
 									  "Movies/LBStart.dxr",			  "d:d3147a37ac1c86964755ab0e72d07d1d",  1075838, Common::NL_NLD, 602),
+	MACGAME1_l("garygadget1", "",	  "Bygg bilar med Mulle Meck",	  "r:04af7a8f667716cd9a364ae1b0c375b1",  1028937, Common::SV_SWE, 600),
 	WINGAME1_l("garygadget1", "",	  "DATA/Willy32.exe",			  "t:c55bbee037d85462b515712bfa88e7f0",  1518423, Common::DE_DEU, 650),
+	WINGAME1_l("garygadget1", "",	  "DATA/MULLE32.EXE",			  "t:a559c8b9d3bce8dd94d168d74509506d",  1512594, Common::SV_SWE, 600),
 	WINDEMO1_l("garygadget1", "Demo", "miel.exe",          "t:f8700f728df75744a96b6d7111e5725e", 1512519, Common::NL_NLD, 602),
 	MACDEMO1_l("garygadget1", "Demo", "Miel Monteur demo", "329aab81c0d74c5d3f31ddad26bcc639", 1029165, Common::NL_NLD, 600),
 



From noreply at scummvm.org  Thu Jun 11 11:48:46 2026
From: noreply at scummvm.org (dreammaster)
Date: Thu, 11 Jun 2026 11:48:46 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 e34250dd36bda47258a1986ea4ffa7769f5d2d18
Message-ID: <20260611114846.654337005BC@ron.scummvm.net>

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:
8dc76dfc6f MADS: FOREST: Implemented room 305
e34250dd36 MADS: FOREST: Implemented room 306


Commit: 8dc76dfc6fec2c6f69b54305394ff8b41aeea269
    https://github.com/scummvm/scummvm/commit/8dc76dfc6fec2c6f69b54305394ff8b41aeea269
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-06-11T21:48:40+10:00

Commit Message:
MADS: FOREST: Implemented room 305

Changed paths:
    engines/mads/madsv2/forest/rooms/room305.cpp


diff --git a/engines/mads/madsv2/forest/rooms/room305.cpp b/engines/mads/madsv2/forest/rooms/room305.cpp
index 89c14ccbe20..32a89cc1c49 100644
--- a/engines/mads/madsv2/forest/rooms/room305.cpp
+++ b/engines/mads/madsv2/forest/rooms/room305.cpp
@@ -19,18 +19,17 @@
  *
  */
 
-#include "mads/madsv2/core/conv.h"
+#include "mads/madsv2/core/digi.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/imath.h"
 #include "mads/madsv2/core/inter.h"
 #include "mads/madsv2/core/kernel.h"
-#include "mads/madsv2/core/sound.h"
-#include "mads/madsv2/core/text.h"
+#include "mads/madsv2/core/player.h"
 #include "mads/madsv2/forest/mads/inventory.h"
 #include "mads/madsv2/forest/mads/sounds.h"
 #include "mads/madsv2/forest/mads/words.h"
 #include "mads/madsv2/forest/global.h"
-#include "mads/madsv2/forest/rooms/section1.h"
+#include "mads/madsv2/forest/journal.h"
 #include "mads/madsv2/forest/rooms/room305.h"
 
 namespace MADS {
@@ -39,10 +38,26 @@ namespace Forest {
 namespace Rooms {
 
 struct Scratch {
-	int16 sprite[10];       /* Sprite series handles */
-	int16 sequence[10];     /* Sequence handles      */
-	int16 animation[10];     /* Animation handles     */
+	int16 sprite[10];
+	int16 sequence[10];
+	int16 animation[10];
 	AnimationInfo animation_info[10];
+	int16 _8c;
+	int16 _8e;
+	int16 _90;
+	int16 _92;
+	int16 _94;
+	int16 _96;
+	int16 _98;
+	int16 _9a;
+	int16 _9c;
+	int16 _9e;
+	int16 _a0;
+	int16 _a2;
+	int16 _a4;
+	int16 _a6;
+	int16 _a8;
+	int16 _aa;
 };
 
 static Scratch scratch;
@@ -54,22 +69,1097 @@ static Scratch scratch;
 #define aainfo scratch.animation_info
 
 
+static void room_305_init1() {
+	global[g131] = 0;
+	global[g141] = 0;
+
+	scratch._9c = kernel_run_animation_disp('r', 1, 0);
+	kernel_position_anim(scratch._9c, 210, 124, 100, 13);
+
+	scratch._9a = kernel_run_animation_disp('e', 2, 0);
+	kernel_position_anim(scratch._9a, 118, 142, 100, 6);
+
+	if (previous_room != KERNEL_LAST) {
+		player.x = 183;
+		player.y = 109;
+		player.facing = 2;
+	}
+
+	if (previous_room == 301) {
+		aa[1] = kernel_run_animation(kernel_name('t', 1), 104);
+		aainfo[1]._active = -1;
+		global_digi_play(14);
+		return;
+	}
+
+	if (previous_room == 306) {
+		if (global[g066] == 3) {
+			aa[0] = kernel_run_animation(kernel_name('y', 3), 100);
+			aainfo[0]._active = -1;
+			scratch._92 = 76;
+		} else {
+			player.x = 138;
+			player.y = 122;
+			player.facing = 9;
+			aa[0] = kernel_run_animation(kernel_name('y', 4), 100);
+		}
+		return;
+	}
+
+	global[g131] = -1;
+	global[g141] = -1;
+	kernel_reset_animation(scratch._9a, 2);
+	kernel_reset_animation(scratch._9c, 2);
+	global[g133] = 0;
+	global[g143] = 0;
+	player.walker_visible = -1;
+	player.commands_allowed = -1;
+}
+
+static void room_305_init2() {
+	global[g131] = 0;
+	global[g141] = 0;
+
+	scratch._9c = kernel_run_animation_disp('r', 1, 0);
+	kernel_position_anim(scratch._9c, 210, 124, 100, 13);
+
+	scratch._9a = kernel_run_animation_disp('e', 2, 0);
+	kernel_position_anim(scratch._9a, 118, 142, 100, 6);
+
+	if (previous_room != KERNEL_LAST) {
+		player.x = 183;
+		player.y = 109;
+		player.facing = 2;
+	}
+
+	if (previous_room == KERNEL_LAST) {
+		global[g131] = -1;
+		global[g141] = -1;
+		kernel_reset_animation(scratch._9a, 2);
+		kernel_reset_animation(scratch._9c, 2);
+		global[g133] = 0;
+		global[g143] = 0;
+		return;
+	}
+
+	if (previous_room == 199) {
+		global[g131] = -1;
+		global[g141] = -1;
+		kernel_reset_animation(scratch._9a, 2);
+		kernel_reset_animation(scratch._9c, 2);
+		global[g133] = 0;
+		global[g143] = 0;
+		restore_player();
+		player.commands_allowed = -1;
+		player.walker_visible = -1;
+		return;
+	}
+
+	if (previous_room == 210) {
+		aa[0] = kernel_run_animation(kernel_name('y', 1), 100);
+		aainfo[0]._active = -1;
+		scratch._92 = 52;
+		return;
+	}
+
+	if (previous_room == 301) {
+		aa[0] = kernel_run_animation(kernel_name('y', 2), 100);
+		aainfo[0]._active = -1;
+		scratch._92 = 70;
+		return;
+	}
+
+	if (previous_room == 306) {
+		if (global[g066] == 3) {
+			aa[0] = kernel_run_animation(kernel_name('y', 3), 100);
+			aainfo[0]._active = -1;
+			scratch._92 = 76;
+			return;
+		}
+		player.x = 138;
+		player.y = 122;
+		player.facing = 9;
+		aa[0] = kernel_run_animation(kernel_name('y', 4), 100);
+		return;
+	}
+
+	global[g131] = -1;
+	global[g141] = -1;
+	kernel_reset_animation(scratch._9a, 2);
+	kernel_reset_animation(scratch._9c, 2);
+	global[g133] = 0;
+	global[g143] = 0;
+	player.commands_allowed = -1;
+	player.walker_visible = -1;
+}
+
 static void room_305_init() {
+	scratch._a8 = 0;
+	scratch._aa = -1;
+
+	if (global[g074] != 0)
+		kernel_flip_hotspot(28, 0);
+
+	if (global[g069] == 0) {
+		ss[0] = kernel_load_series(kernel_name('p', 1), 0);
+		seq[0] = kernel_seq_stamp(ss[0], false, -1);
+		kernel_seq_depth(seq[0], 4);
+		kernel_seq_loc(seq[0], 229, 146);
+		kernel_seq_scale(seq[0], 100);
+	} else {
+		kernel_flip_hotspot(145, 0);
+	}
+
+	if (previous_room != KERNEL_LAST) {
+		if (previous_room != 199) {
+			player.walker_visible = 0;
+			player.commands_allowed = 0;
+		}
+
+		for (int count = 0; count < 10; count++) {
+			aainfo[count]._active = 0;
+			aainfo[count]._frame = 1;
+			aainfo[count]._val3 = 0;
+			aainfo[count]._val4 = 0;
+		}
+
+		if (previous_room != 199 && flags[19] != 3)
+			flags[19]++;
+	}
+
+	if (global[g066] < 2)
+		kernel_timing_trigger(1, 109);
+
+	switch (flags[19] - 1) {
+	case 0:
+		room_305_init1();
+		break;
+	case 1:
+	default:
+		room_305_init2();
+		break;
+	}
+
+	global[g009] = -1;
+	if (global[g066] == 1)
+		global_digi_play(8);
+}
+
+static void room_305_anim1() {
+	if (kernel_anim[aa[0]].frame != aainfo[0]._frame)
+		aainfo[0]._frame = kernel_anim[aa[0]].frame;
+
+	if (global[player_hyperwalked] == -1) {
+		int16 f = scratch._92 - 1;
+		aainfo[0]._frame = f;
+		kernel_reset_animation(aa[0], f);
+	}
+}
+
+static void room_305_anim2() {
+	if (kernel_anim[aa[1]].frame != aainfo[1]._frame) {
+		int16 f = kernel_anim[aa[1]].frame;
+		aainfo[1]._frame = f;
+
+		if (f == 61) {
+			global[g009] = 0;
+			aainfo[1]._val3 = 10;
+			digi_play_build(305, 'b', 1, 1);
+			scratch._a4 = 1;
+		} else if (f == 66 && aainfo[1]._val3 == 10) {
+			aainfo[1]._frame = 62;
+			kernel_reset_animation(aa[1], 62);
+		}
+	}
+
+	if (kernel.trigger != 7 && kernel.trigger != 28)
+		return;
+
+	if (scratch._a4 == 1) {
+		global[g154] = 0;
+		aainfo[1]._frame = 67;
+		kernel_reset_animation(aa[1], 67);
+		scratch._a4 = 2;
+	}
+}
+
+static void room_305_anim3() {
+	int16 f = aainfo[2]._frame;
+	if (kernel_anim[aa[2]].frame != f)
+		f = aainfo[2]._frame = kernel_anim[aa[2]].frame;
+
+	if (global[player_hyperwalked] == -1 && f <= 23) {
+		aainfo[2]._frame = 23;
+		kernel_reset_animation(aa[2], 23);
+		kernel_synch(3, aa[2], 4, 0);
+		return;
+	}
+	if (global[player_hyperwalked] == -1 || f == 45) {
+		aainfo[2]._frame = 44;
+		kernel_reset_animation(aa[2], 44);
+		kernel_synch(3, aa[2], 4, 0);
+	}
+}
+
+static void room_305_anim4() {
+	int16 f = aainfo[3]._frame;
+	if (kernel_anim[aa[3]].frame != f)
+		f = aainfo[3]._frame = kernel_anim[aa[3]].frame;
+
+	if (global[player_hyperwalked] == -1 && f <= 23) {
+		aainfo[3]._frame = 23;
+		kernel_reset_animation(aa[3], 23);
+		kernel_synch(3, aa[3], 4, 0);
+		return;
+	}
+	if (global[player_hyperwalked] == -1 || f == 45) {
+		aainfo[3]._frame = 44;
+		kernel_reset_animation(aa[3], 44);
+		kernel_synch(3, aa[3], 4, 0);
+	}
+}
+
+static void room_305_anim5() {
+	if (kernel_anim[aa[4]].frame != aainfo[4]._frame) {
+		int16 f = kernel_anim[aa[4]].frame;
+		aainfo[4]._frame = f;
+
+		if (scratch._aa == 1) {
+			if (f == 37) {
+				if (aainfo[4]._val3 == 11) {
+					aainfo[4]._frame = 35;
+					kernel_reset_animation(aa[4], 35);
+				}
+			} else if (f <= 37) {
+				if (f == 29) {
+					aainfo[4]._val3 = 12;
+					digi_play_build_ii('e', 1, 1);
+					scratch._a4 = 29;
+				} else if (f == 34 && aainfo[4]._val3 == 12) {
+					aainfo[4]._frame = 29;
+					kernel_reset_animation(aa[4], 29);
+				}
+			}
+		} else if (scratch._aa == 2) {
+			if (f == 15) {
+				if (aainfo[4]._val3 == 11) {
+					aainfo[4]._frame = 12;
+					kernel_reset_animation(aa[4], 12);
+				}
+			} else if (f <= 15) {
+				if (f == 5) {
+					aainfo[4]._val3 = 12;
+					digi_play_build_ii('e', 3, 1);
+					scratch._a4 = 500;
+				} else if (f == 11 && aainfo[4]._val3 == 12) {
+					aainfo[4]._frame = 5;
+					kernel_reset_animation(aa[4], 5);
+				}
+			}
+		}
+	}
+
+	if (kernel.trigger != 7 && kernel.trigger != 28)
+		return;
+
+	switch (scratch._a4) {
+	case 29:
+		kernel_timing_trigger(30, 28);
+		aainfo[4]._val3 = 11;
+		scratch._a4 = 30;
+		break;
+	case 30:
+		aainfo[4]._val3 = 12;
+		aainfo[4]._frame = 29;
+		kernel_reset_animation(aa[4], 29);
+		digi_play_build_ii('e', 10, 1);
+		scratch._a4 = 31;
+		break;
+	case 31:
+		aainfo[4]._val3 = 13;
+		aainfo[4]._frame = 35;
+		kernel_reset_animation(aa[4], 35);
+		scratch._a4 = -1;
+		break;
+	case 500:
+		kernel_timing_trigger(30, 28);
+		aainfo[4]._val3 = 11;
+		scratch._a4 = 501;
+		break;
+	case 501:
+		aainfo[4]._val3 = 12;
+		aainfo[4]._frame = 5;
+		kernel_reset_animation(aa[4], 5);
+		digi_play_build_ii('e', 10, 1);
+		scratch._a4 = 502;
+		break;
+	case 502:
+		aainfo[4]._val3 = 13;
+		aainfo[4]._frame = 11;
+		kernel_reset_animation(aa[4], 11);
+		scratch._a4 = -1;
+		break;
+	default:
+		break;
+	}
+}
+
+static void room_305_anim6() {
+	if (kernel_anim[aa[5]].frame != aainfo[5]._frame) {
+		int16 result = -1;
+		aainfo[5]._frame = kernel_anim[aa[5]].frame;
+		int16 f = aainfo[5]._frame;
+
+		if (f >= 3 && f <= 76) {
+			switch (f) {
+			case 3:
+				digi_play_build(305, 'b', 2, 1);
+				aainfo[5]._val3 = 3;
+				scratch._a4 = 10;
+				break;
+			case 4: case 5: case 6: case 7: case 8: case 9:
+				if (aainfo[5]._val3 == 3)
+					result = imath_random(3, 8);
+				else
+					result = 9;
+				break;
+			case 15:
+				result = 19;
+				break;
+			case 24:
+				aainfo[5]._val3 = 5;
+				digi_play_build(305, 'h', 1, 1);
+				scratch._a4 = 12;
+				break;
+			case 25: case 26: case 27: case 28: case 29: case 30:
+			case 31: case 32: case 33: case 34: case 35:
+			case 57: case 58: case 59: case 60: case 61: case 62: case 63:
+			case 69: case 70: case 71: case 72: case 73: case 74: case 75: case 76: {
+				int16 v = aainfo[5]._val3;
+				if (v == 11)
+					result = imath_random(68, 75);
+				else if (v < 11) {
+					if (v == 0)
+						result = 24;
+					else if (v == 4)
+						result = imath_random(56, 62);
+					else if (v == 5)
+						result = imath_random(25, 33);
+					else if (v == 6)
+						result = 34;
+				}
+				break;
+			}
+			default:
+				break;
+			}
+		}
+
+		if (result >= 0) {
+			kernel_reset_animation(aa[5], result);
+			aainfo[5]._frame = result;
+		}
+	}
+
+	if (kernel.trigger != 7 && kernel.trigger != 105)
+		return;
+
+	switch (scratch._a4) {
+	case 10:
+		aainfo[5]._val3 = 0;
+		break;
+	case 12:
+		aainfo[5]._val3 = 6;
+		kernel_timing_trigger(90, 105);
+		scratch._a4++;
+		break;
+	case 13:
+		aainfo[5]._val3 = 5;
+		digi_play_build(305, 'h', 2, 1);
+		scratch._a4++;
+		break;
+	case 14:
+		aainfo[5]._val3 = 0;
+		kernel_timing_trigger(45, 105);
+		scratch._a4++;
+		global[g009] = -1;
+		global_digi_play(6);
+		break;
+	case 15:
+		if (global[g065] != 0) {
+			aainfo[5]._val3 = 4;
+			digi_play_build(305, 'b', 3, 1);
+		} else {
+			aainfo[5]._val3 = 11;
+			digi_play_build(305, 'e', 3, 1);
+		}
+		scratch._a4++;
+		break;
+	case 16:
+		aainfo[5]._val3 = 0;
+		kernel_timing_trigger(45, 105);
+		scratch._a4++;
+		break;
+	case 17:
+		if (global[g065] != 0) {
+			aainfo[5]._val3 = 11;
+			digi_play_build(305, 'e', 4, 1);
+		} else {
+			aainfo[5]._val3 = 4;
+			digi_play_build(305, 'b', 4, 1);
+		}
+		scratch._a4++;
+		break;
+	case 18:
+		kernel_abort_animation(aa[5]);
+		aainfo[5]._active = 0;
+		aa[6] = kernel_run_animation(kernel_name('h', 3), 0);
+		aainfo[6]._active = -1;
+		kernel_timing_trigger(1, 109);
+		break;
+	default:
+		break;
+	}
+}
+
+static void room_305_anim7() {
+	if (kernel_anim[aa[6]].frame != aainfo[6]._frame) {
+		int16 result = -1;
+		aainfo[6]._frame = kernel_anim[aa[6]].frame;
+		int16 f = aainfo[6]._frame;
+
+		if (f == 25) {
+			aainfo[6]._val3 = 7;
+			digi_play_build(305, 'h', 3, 1);
+			scratch._a4 = 30;
+		} else if ((f >= 26 && f <= 34) || (f >= 39 && f <= 46)) {
+			int16 v = aainfo[6]._val3;
+			if (v == 10)
+				result = imath_random(38, 44);
+			else if (v < 10) {
+				if (v == 0)
+					result = 25;
+				else if (v == 2)
+					result = 45;
+				else if (v == 7)
+					result = imath_random(26, 33);
+			}
+		}
+
+		if (result >= 0) {
+			kernel_reset_animation(aa[6], result);
+			aainfo[6]._frame = result;
+		}
+	}
+
+	if (kernel.trigger != 7 && kernel.trigger != 105)
+		return;
+
+	switch (scratch._a4) {
+	case 30:
+		aainfo[6]._val3 = 0;
+		kernel_timing_trigger(45, 105);
+		scratch._a4++;
+		break;
+	case 31:
+	case 33:
+		aainfo[6]._val3 = 2;
+		kernel_timing_trigger(45, 105);
+		scratch._a4++;
+		break;
+	case 32:
+		aainfo[6]._val3 = 10;
+		digi_play_build(305, 'h', 4, 1);
+		scratch._a4++;
+		break;
+	case 34:
+		kernel_abort_animation(aa[6]);
+		aainfo[6]._active = 0;
+		aa[7] = kernel_run_animation(kernel_name('h', 4), 106);
+		kernel_timing_trigger(1, 109);
+		break;
+	default:
+		break;
+	}
+}
+
+static void room_305_anim8() {
+	if (kernel_anim[aa[7]].frame == aainfo[7]._frame) {
+		if (kernel.trigger != 7 && kernel.trigger != 105)
+			return;
+	} else {
+		int16 f = kernel_anim[aa[7]].frame;
+		aainfo[7]._frame = f;
+		int16 result = -1;
+
+		switch (f) {
+		case 1: case 2: case 3: case 4: case 5: case 6: case 7: case 8: case 9:
+		case 10: case 11: case 12: case 13:
+		case 18: case 19: case 20: case 21: case 22: case 23:
+		case 28: case 29: case 30: case 31: case 32: case 33: case 34:
+		case 39:
+		case 42: case 43: case 44: case 45: case 46:
+		case 52: case 53: case 54: case 55: case 56: case 57: case 58: case 59: case 60:
+			switch (aainfo[7]._val3) {
+			case 0:  result = 0; break;
+			case 4:  result = imath_random(51, 59); break;
+			case 5:  result = imath_random(1, 4); break;
+			case 9:  result = imath_random(28, 33); break;
+			case 11: result = imath_random(41, 45); break;
+			case 12: result = imath_random(17, 22); break;
+			case 13: result = 25; break;
+			case 14: result = 36; aainfo[7]._val3 = 0; break;
+			default: break;
+			}
+			break;
+		case 27:
+			if (scratch._a4 == 42)
+				digi_play_build(305, 'h', 6, 1);
+			else
+				digi_play_build(305, 'h', 7, 1);
+			digi_initial_volume(100);
+			aainfo[7]._val3 = 9;
+			break;
+		default:
+			break;
+		}
+
+		if (result >= 0) {
+			aainfo[7]._frame = result;
+			kernel_reset_animation(aa[7], result);
+		}
+	}
+
+	if (kernel.trigger != 7 && kernel.trigger != 105)
+		return;
+
+	switch (scratch._a4) {
+	case 40:
+	case 44:
+	case 48:
+	case 50:
+		aainfo[7]._val3 = 0;
+		kernel_timing_trigger(45, 105);
+		scratch._a4++;
+		break;
+	case 41:
+		if (global[g080] != 0) {
+			aainfo[7]._val3 = 5;
+			digi_play_build(305, 'h', 5, 1);
+		} else {
+			aainfo[7]._val3 = 13;
+		}
+		scratch._a4++;
+		break;
+	case 42:
+	case 46:
+		if (global[g080] != 0) {
+			aainfo[7]._val3 = 0;
+			kernel_timing_trigger(90, 105);
+		} else {
+			aainfo[7]._val3 = 14;
+			kernel_timing_trigger(90, 105);
+		}
+		scratch._a4++;
+		break;
+	case 43:
+		if (global[g080] != 0) {
+			aainfo[7]._val3 = 12;
+			digi_play_build(305, 'r', 1, 1);
+		} else {
+			aainfo[7]._val3 = 11;
+			digi_play_build(305, 'e', 5, 1);
+		}
+		scratch._a4++;
+		break;
+	case 45:
+		aainfo[7]._val3 = 13;
+		scratch._a4++;
+		break;
+	case 47:
+		aainfo[7]._val3 = 4;
+		digi_play_build(305, 'b', 7, 1);
+		scratch._a4++;
+		break;
+	case 49:
+		aainfo[7]._val3 = 5;
+		digi_play_build(305, 'h', 8, 1);
+		scratch._a4++;
+		break;
+	case 51:
+		kernel_abort_animation(aa[7]);
+		aainfo[7]._active = 0;
+		aa[7] = kernel_run_animation(kernel_name('h', 6), 107);
+		aainfo[7]._active = -1;
+		kernel_timing_trigger(1, 109);
+		break;
+	default:
+		break;
+	}
+}
+
+static void room_305_anim9() {
+	if (kernel_anim[aa[8]].frame != aainfo[8]._frame) {
+		int16 f = kernel_anim[aa[8]].frame;
+		aainfo[8]._frame = f;
+
+		if (scratch._aa == 1) {
+			if (f == 28) {
+				if (aainfo[8]._val3 == 11) {
+					digi_play_build('e', '_', 1, 2);
+					scratch._a4 = 28;
+				}
+			} else if (f <= 28) {
+				if (f == 5) {
+					aainfo[8]._val3 = 12;
+					digi_play_build(305, 'e', 2, 1);
+					scratch._a4 = 5;
+					kernel_seq_delete(seq[0]);
+				} else if (f == 10 && aainfo[8]._val3 == 12) {
+					aainfo[8]._frame = 5;
+					kernel_reset_animation(aa[8], 5);
+				}
+			}
+		}
+	}
+
+	if (kernel.trigger == 7 || kernel.trigger == 28) {
+		if (scratch._a4 == 5) {
+			aainfo[8]._val3 = 11;
+			aainfo[8]._frame = 11;
+			kernel_reset_animation(aa[8], 11);
+			scratch._a4 = -1;
+		}
+	}
+	if (kernel.trigger != 8 && kernel.trigger != 28)
+		return;
+	if (scratch._a4 == 28)
+		scratch._a4 = -1;
+}
+
+static void room_305_anim10() {
+	if (kernel_anim[aa[9]].frame != aainfo[9]._frame) {
+		int16 var_2 = -1;
+		aainfo[9]._frame = kernel_anim[aa[9]].frame;
+		int16 f = aainfo[9]._frame;
+
+		if (f == 79) {
+			kernel_abort_animation(aa[9]);
+			aainfo[9]._active = 0;
+			player.commands_allowed = -1;
+			if (digi_val1 != 0)
+				kernel_timing_trigger(1, 109);
+			global[g131] = -1;
+			kernel_reset_animation(scratch._9a, 1);
+			kernel_synch(KERNEL_ANIM, scratch._9a, KERNEL_NOW, 0);
+			global[g133] = 0;
+		} else if (f == 42) {
+			digi_play_build(305, 'e', 1, 1);
+			scratch._a4 = 666;
+		} else if (f == 43 || f == 44) {
+			if (aainfo[9]._val3 == 15)
+				var_2 = imath_random(42, 43);
+		}
+
+		if (var_2 >= 0) {
+			kernel_reset_animation(aa[9], var_2);
+			aainfo[9]._frame = var_2;
+		}
+	}
+
+	if (kernel.trigger == 7 || kernel.trigger == 105) {
+		if (scratch._a4 == 666)
+			aainfo[9]._val3 = 0;
+	}
 }
 
 static void room_305_daemon() {
+	switch (kernel.trigger) {
+	case 7:
+		if (global[walker_converse_state] != 0) {
+			global[walker_converse_state] = 0;
+			close_journal(3);
+			player.commands_allowed = -1;
+			if (digi_val1 != 0)
+				kernel_timing_trigger(1, 109);
+		} else {
+			if (scratch._a4 == 300) {
+				kernel_abort_animation(scratch._9e);
+				scratch._a4 = -1;
+				scratch._a8 = 0;
+				kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
+				scratch._aa = 1;
+				player.walker_visible = -1;
+				global[g150] = -1;
+				scratch._90 = 6;
+			} else if (scratch._a4 == 451) {
+				kernel_abort_animation(scratch._9e);
+				kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
+				scratch._a4 = -1;
+				scratch._a8 = 0;
+				scratch._8c = 3;
+				player.walker_visible = -1;
+				global[g135] = -1;
+				player.commands_allowed = 0;
+			}
+		}
+		break;
+
+	case 24:
+		if (scratch._8c == 3) {
+			if (global[g074] == 0) {
+				global[g074] = -1;
+				aa[9] = kernel_run_animation(kernel_name('e', 1), 0);
+				aainfo[9]._active = -1;
+				aainfo[9]._val3 = 15;
+				kernel_flip_hotspot(28, 0);
+			} else {
+				aa[0] = kernel_run_animation(kernel_name('Z', 1), 102);
+				aainfo[0]._active = -1;
+				scratch._92 = 38;
+			}
+			kernel_reset_animation(scratch._9a, 0);
+			kernel_synch(KERNEL_ANIM, aa[2], KERNEL_ANIM, scratch._9a);
+			kernel_synch(KERNEL_PLAYER, 0, KERNEL_ANIM, aa[2]);
+			global[g133] = 1;
+			kernel_timing_trigger(1, 109);
+		} else if (scratch._8c == 5) {
+			if (scratch._aa == 1 || scratch._aa == 2) {
+				aa[4] = kernel_run_animation(kernel_name('F', scratch._aa), 108);
+				aainfo[4]._active = -1;
+				scratch._96 = 4;
+				kernel_reset_animation(scratch._9a, 0);
+				kernel_synch(KERNEL_ANIM, aa[4], KERNEL_ANIM, scratch._9a);
+				global[g133] = 1;
+				kernel_timing_trigger(1, 109);
+			}
+		}
+		break;
+
+	case 25:
+		if (scratch._8e == 2) {
+			aa[3] = kernel_run_animation(kernel_name('R', 1), 101);
+			aainfo[3]._active = -1;
+			scratch._94 = 3;
+			kernel_reset_animation(scratch._9c, 0);
+			kernel_synch(KERNEL_ANIM, aa[3], KERNEL_ANIM, scratch._9c);
+			global[g143] = 1;
+			kernel_timing_trigger(1, 109);
+		}
+		break;
+
+	case 26:
+		if (scratch._90 == 1) {
+			kernel_reset_animation(scratch._9a, 0);
+			kernel_reset_animation(scratch._9c, 0);
+			global[g133] = 1;
+			global[g143] = 1;
+		} else if (scratch._90 == 6 && scratch._aa == 1) {
+			aa[8] = kernel_run_animation(kernel_name('P', 1), 103);
+			aainfo[8]._active = -1;
+			scratch._98 = 8;
+			kernel_reset_animation(scratch._9a, 0);
+			kernel_reset_animation(scratch._9c, 0);
+			kernel_synch(KERNEL_ANIM, scratch._9a, KERNEL_ANIM, aa[8]);
+			kernel_synch(KERNEL_ANIM, scratch._9c, KERNEL_ANIM, aa[8]);
+			global[g133] = 1;
+			global[g143] = 1;
+			kernel_timing_trigger(1, 109);
+		}
+		break;
+
+	case 27:
+		kernel_abort_animation(aa[1]);
+		aainfo[1]._active = 0;
+		global[g131] = -1;
+		kernel_reset_animation(scratch._9a, 1);
+		kernel_synch(KERNEL_ANIM, scratch._9a, KERNEL_NOW, 0);
+		global[g133] = 0;
+		player.commands_allowed = -1;
+		if (digi_val1 != 0)
+			kernel_timing_trigger(1, 109);
+		break;
+
+	case 100:
+		kernel_abort_animation(aa[0]);
+		aainfo[0]._active = 0;
+		if (global[g066] == 1) {
+			aa[5] = kernel_run_animation(kernel_name('b', 1), 0);
+			aainfo[5]._active = -1;
+			kernel_timing_trigger(1, 109);
+		} else {
+			global[g131] = -1;
+			global[g141] = -1;
+			kernel_reset_animation(scratch._9a, 1);
+			kernel_synch(KERNEL_ANIM, scratch._9a, KERNEL_NOW, 0);
+			kernel_reset_animation(scratch._9c, 1);
+			kernel_synch(KERNEL_ANIM, scratch._9c, KERNEL_NOW, 0);
+			player.walker_visible = -1;
+			global[g133] = 0;
+			global[g143] = 0;
+			kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
+			player.commands_allowed = -1;
+			if (digi_val1 != 0)
+				kernel_timing_trigger(1, 109);
+		}
+		break;
+
+	case 101: {
+		int16 idx = scratch._94;
+		kernel_abort_animation(aa[idx]);
+		aainfo[idx]._active = 0;
+		kernel_reset_animation(scratch._9c, 1);
+		kernel_synch(KERNEL_ANIM, scratch._9c, KERNEL_NOW, 0);
+		global[g143] = 0;
+		player.commands_allowed = -1;
+		if (digi_val1 != 0)
+			kernel_timing_trigger(1, 109);
+		break;
+	}
+
+	case 102:
+		global[g060] = -1;
+		new_room = 210;
+		break;
+
+	case 103:
+		if (scratch._aa == 1) {
+			kernel_abort_animation(aa[8]);
+			aainfo[8]._active = 0;
+			kernel_reset_animation(scratch._9c, 1);
+			kernel_synch(KERNEL_ANIM, scratch._9c, KERNEL_NOW, 0);
+			global[g143] = 0;
+			kernel_reset_animation(scratch._9a, 1);
+			kernel_synch(KERNEL_ANIM, scratch._9a, KERNEL_NOW, 0);
+			global[g133] = 0;
+			kernel_flip_hotspot(145, false);
+			if (!player_has(7))
+				inter_move_object(7, 2);
+			global[g069] = -1;
+			global[player_score] = -1;
+			player.commands_allowed = -1;
+			if (digi_val1 != 0)
+				kernel_timing_trigger(1, 109);
+		}
+		break;
+
+	case 104:
+		kernel_abort_animation(aa[1]);
+		// unused variable set to 0 in original
+		global[g141] = -1;
+		kernel_reset_animation(scratch._9c, 1);
+		kernel_synch(KERNEL_ANIM, scratch._9c, KERNEL_NOW, 0);
+		player.walker_visible = -1;
+		global[g143] = 0;
+		kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
+		scratch._a6 = kernel_run_animation_write(0);
+		kernel_reset_animation(scratch._a6, 41);
+		kernel_position_anim(scratch._a6, 118, 142, 100, 6);
+		global[walker_converse_now] = 1;
+		global[g007] = 2;
+		kernel_synch(KERNEL_ANIM, scratch._a6, KERNEL_NOW, 0);
+		kernel_timing_trigger(1, 109);
+		break;
+
+	case 106:
+		kernel_abort_animation(aa[7]);
+		aa[7] = kernel_run_animation(kernel_name('h', 5), 0);
+		aainfo[7]._active = -1;
+		if (global[g080] != 0)
+			digi_play_build(305, 'b', 5, 1);
+		else
+			digi_play_build(305, 'b', 6, 1);
+		aainfo[7]._val3 = 4;
+		scratch._a4 = 40;
+		kernel_timing_trigger(1, 109);
+		break;
+
+	case 107:
+		kernel_abort_animation(aa[7]);
+		global[g066] = 2;
+		global[g131] = -1;
+		global[g141] = -1;
+		kernel_reset_animation(scratch._9a, 1);
+		kernel_synch(KERNEL_ANIM, scratch._9a, KERNEL_NOW, 0);
+		kernel_reset_animation(scratch._9c, 1);
+		kernel_synch(KERNEL_ANIM, scratch._9c, KERNEL_NOW, 0);
+		player.walker_visible = -1;
+		global[g133] = 0;
+		global[g143] = 0;
+		kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
+		player.commands_allowed = -1;
+		if (digi_val1 != 0)
+			kernel_timing_trigger(1, 109);
+		break;
+
+	case 108:
+		kernel_abort_animation(aa[4]);
+		aainfo[4]._active = 0;
+		kernel_reset_animation(scratch._9a, 1);
+		kernel_synch(KERNEL_ANIM, scratch._9a, KERNEL_NOW, 0);
+		global[g133] = 0;
+		global[player_score] = -1;
+		player.commands_allowed = -1;
+		if (digi_val1 != 0)
+			kernel_timing_trigger(1, 109);
+		break;
+
+	case 109:
+		if (global[g066] < 2) {
+			digi_initial_volume(15);
+			digi_play_build(305, '_', 1, 3);
+			digi_val2 = -1;
+		}
+		break;
+
+	default:
+		break;
+	}
+
+	global_anim1(2, scratch._9a, global[g131], &global[g132]);
+	global_anim2(1, scratch._9c, global[g141], &global[g142]);
+	if (scratch._a8 != 0)
+		kernel_random_frame(scratch._9e, &global[g151], global[g154]);
+	if (aainfo[0]._active != 0)
+		room_305_anim1();
+	if (aainfo[3]._active != 0)
+		room_305_anim4();
+	if (aainfo[2]._active != 0)
+		room_305_anim3();
+	if (aainfo[4]._active != 0)
+		room_305_anim5();
+	if (aainfo[8]._active != 0)
+		room_305_anim9();
+	if (aainfo[1]._active != 0)
+		room_305_anim2();
+	if (aainfo[5]._active != 0)
+		room_305_anim6();
+	if (aainfo[6]._active != 0)
+		room_305_anim7();
+	if (aainfo[7]._active != 0)
+		room_305_anim8();
+	if (aainfo[9]._active != 0)
+		room_305_anim10();
+	if (global[walker_converse_now] != 0)
+		global_anim3(scratch._a6, &global[g008]);
 }
 
 static void room_305_pre_parser() {
+	if (player_parse(13, 34, 0))
+		player.walk_off_edge_to_room = 301;
+
+	if (player_parse(13, 39, 0))
+		player.walk_off_edge_to_room = 306;
 }
 
 static void room_305_parser() {
+	if (global[walker_converse_state] != 0) {
+		player.commands_allowed = 0;
+		digi_play_build_ii('c', 1, 1);
+		goto handled;
+	}
+
+	if (player_parse(28, 0)) {
+		global[g154] = 2;
+		player.walker_visible = 0;
+		player.commands_allowed = 0;
+		scratch._9e = kernel_run_animation_talk('b', 9, 0);
+		kernel_position_anim(scratch._9e, player.x, player.y, player.scale, player.depth);
+		scratch._a8 = -1;
+		kernel_synch(KERNEL_ANIM, scratch._9e, KERNEL_PLAYER, 0);
+		digi_play_build_ii('b', 1, 1);
+		scratch._a4 = 451;
+		kernel.trigger_setup_mode = 1;
+		kernel_timing_trigger(1, 109);
+		goto handled;
+	}
+
+	if (player_parse(27, 0)) {
+		scratch._8c = 3;
+		global[g135] = -1;
+		player.commands_allowed = 0;
+		goto handled;
+	}
+
+	if (global[player_selected_object] >= 0) {
+		goto handled;
+	}
+
+	if (player_parse(114, 118, 0)) {
+		global[player_score] = 0;
+		digi_stop(3);
+		player.commands_allowed = 0;
+		global[g135] = -1;
+		scratch._8c = 5;
+		scratch._aa = 1;
+		goto handled;
+	}
+
+	if (player_parse(114, 142, 0)) {
+		global[player_score] = 0;
+		digi_stop(3);
+		player.commands_allowed = 0;
+		global[g135] = -1;
+		scratch._8c = 5;
+		scratch._aa = 2;
+		goto handled;
+	}
+
+	if (player_parse(126, 145, 0)) {
+		global[player_score] = 0;
+		digi_stop(3);
+		global[g154] = 2;
+		player.commands_allowed = 0;
+		player.walker_visible = 0;
+		scratch._9e = kernel_run_animation_talk('b', 1, 0);
+		kernel_position_anim(scratch._9e, player.x, player.y, player.scale, player.depth);
+		kernel_synch(KERNEL_ANIM, scratch._9e, KERNEL_PLAYER, 0);
+		digi_play_build_ii('b', 3, 1);
+		scratch._a4 = 300;
+		scratch._a8 = -1;
+		kernel.trigger_setup_mode = 1;
+		kernel_timing_trigger(1, 109);
+		goto handled;
+	}
+
+	if (player_parse(78, 119, 0)) {
+		player.commands_allowed = 0;
+		global[g135] = -1;
+		scratch._8c = 3;
+		goto handled;
+	}
+
+
+	goto done;
+
+handled:
+	player.command_ready = false;
+
+done:
+	;
 }
 
 void room_305_synchronize(Common::Serializer &s) {
 	for (int16 &v : scratch.sprite)    s.syncAsSint16LE(v);
 	for (int16 &v : scratch.sequence)  s.syncAsSint16LE(v);
 	for (int16 &v : scratch.animation) s.syncAsSint16LE(v);
+	for (AnimationInfo &ai : scratch.animation_info) ai.synchronize(s);
+	s.syncAsSint16LE(scratch._8c);
+	s.syncAsSint16LE(scratch._8e);
+	s.syncAsSint16LE(scratch._90);
+	s.syncAsSint16LE(scratch._92);
+	s.syncAsSint16LE(scratch._94);
+	s.syncAsSint16LE(scratch._96);
+	s.syncAsSint16LE(scratch._98);
+	s.syncAsSint16LE(scratch._9a);
+	s.syncAsSint16LE(scratch._9c);
+	s.syncAsSint16LE(scratch._9e);
+	s.syncAsSint16LE(scratch._a0);
+	s.syncAsSint16LE(scratch._a2);
+	s.syncAsSint16LE(scratch._a4);
+	s.syncAsSint16LE(scratch._a6);
+	s.syncAsSint16LE(scratch._a8);
+	s.syncAsSint16LE(scratch._aa);
 }
 
 void room_305_preload() {


Commit: e34250dd36bda47258a1986ea4ffa7769f5d2d18
    https://github.com/scummvm/scummvm/commit/e34250dd36bda47258a1986ea4ffa7769f5d2d18
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-06-11T21:48:40+10:00

Commit Message:
MADS: FOREST: Implemented room 306

Changed paths:
    engines/mads/madsv2/forest/rooms/room306.cpp


diff --git a/engines/mads/madsv2/forest/rooms/room306.cpp b/engines/mads/madsv2/forest/rooms/room306.cpp
index da270f67e99..8d44e900d4b 100644
--- a/engines/mads/madsv2/forest/rooms/room306.cpp
+++ b/engines/mads/madsv2/forest/rooms/room306.cpp
@@ -23,14 +23,18 @@
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/imath.h"
 #include "mads/madsv2/core/inter.h"
+#include "mads/madsv2/core/digi.h"
 #include "mads/madsv2/core/kernel.h"
+#include "mads/madsv2/core/midi.h"
+#include "mads/madsv2/core/player.h"
 #include "mads/madsv2/core/sound.h"
+#include "mads/madsv2/core/sprite.h"
 #include "mads/madsv2/core/text.h"
 #include "mads/madsv2/forest/mads/inventory.h"
 #include "mads/madsv2/forest/mads/sounds.h"
 #include "mads/madsv2/forest/mads/words.h"
 #include "mads/madsv2/forest/global.h"
-#include "mads/madsv2/forest/rooms/section1.h"
+#include "mads/madsv2/forest/journal.h"
 #include "mads/madsv2/forest/rooms/room306.h"
 
 namespace MADS {
@@ -39,10 +43,26 @@ namespace Forest {
 namespace Rooms {
 
 struct Scratch {
-	int16 sprite[10];       /* Sprite series handles */
-	int16 sequence[10];     /* Sequence handles      */
-	int16 animation[10];     /* Animation handles     */
+	int16 sprite[10];
+	int16 sequence[10];
+	int16 animation[10];
 	AnimationInfo animation_info[10];
+	int16 _pad8C[2];
+	int16 x90;
+	int16 x92;
+	int16 x94;
+	int16 _pad96[2];
+	int16 x9a;
+	int16 x9c;
+	int16 x9e;
+	int16 xa0;
+	int16 xa2;
+	int16 xa4;
+	int16 xa6;
+	int16 xa8;
+	int16 xaa;
+	int16 xac;
+	int16 xae;
 };
 
 static Scratch scratch;
@@ -54,22 +74,948 @@ static Scratch scratch;
 #define aainfo scratch.animation_info
 
 
+static void room_306_init1() {
+	if (previous_room == KERNEL_RESTORING_GAME) {
+		scratch.xaa = -1;
+		scratch.x9c = kernel_run_animation_disp('r', 6, 0);
+		kernel_position_anim(scratch.x9c, 63, 134, 91, 3);
+		global[g143] = 1;
+		scratch.x9a = kernel_run_animation_disp('e', 2, 0);
+		kernel_position_anim(scratch.x9a, 88, 122, 86, 5);
+		global[g133] = 1;
+		global[g131] = -1;
+		global[g141] = -1;
+		kernel_reset_animation(scratch.x9a, 2);
+		kernel_reset_animation(scratch.x9c, 2);
+		global[g133] = 0;
+		global[g143] = 0;
+		player.walker_visible = -1;
+		player.commands_allowed = -1;
+	} else {
+		global[g131] = 0;
+		global[g141] = 0;
+		global[g133] = 1;
+		global[g143] = 1;
+		player.x = 103;
+		player.y = 136;
+		player.facing = 8;
+	}
+
+	if (previous_room == 199) {
+		scratch.xaa = -1;
+		global[g131] = 0;
+		global[g141] = 0;
+		scratch.x9c = kernel_run_animation_disp('r', 6, 0);
+		kernel_position_anim(scratch.x9c, 63, 134, 91, 3);
+		scratch.x9a = kernel_run_animation_disp('e', 2, 0);
+		kernel_position_anim(scratch.x9a, 88, 122, 86, 5);
+		global[g131] = -1;
+		global[g141] = -1;
+		kernel_reset_animation(scratch.x9a, 2);
+		kernel_reset_animation(scratch.x9c, 2);
+		global[g133] = 0;
+		global[g143] = 0;
+		restore_player();
+		player.commands_allowed = -1;
+		player.walker_visible = -1;
+	}
+
+	if (global[g066] == 3)
+		return;
+
+	aa[4] = kernel_run_animation(kernel_name('t', 5), 0);
+	aainfo[4]._active = -1;
+	aainfo[4]._val3 = 3;
+	aa[2] = kernel_run_animation(kernel_name('s', 1), 0);
+	aainfo[2]._active = -1;
+	aainfo[2]._val3 = (global[g066] == 0) ? 4 : 3;
+	aa[5] = kernel_run_animation(kernel_name('t', 7), 0);
+	aainfo[5]._active = -1;
+	aainfo[5]._val3 = 1;
+	if (global[g066] == 0) {
+		aa[7] = kernel_run_animation(kernel_name('t', 2), 0);
+		aainfo[7]._active = -1;
+		aainfo[7]._val3 = 4;
+	}
+
+	if (previous_room != KERNEL_RESTORING_GAME && previous_room != 199) {
+		aa[6] = kernel_run_animation(kernel_name('y', 1), 104);
+	}
+}
+
+static void room_306_init2() {
+	if (previous_room == KERNEL_RESTORING_GAME) {
+		scratch.xaa = -1;
+		scratch.x9c = kernel_run_animation_disp('r', 6, 0);
+		kernel_position_anim(scratch.x9c, 63, 134, 91, 3);
+		global[g143] = 1;
+		scratch.x9a = kernel_run_animation_disp('e', 2, 0);
+		kernel_position_anim(scratch.x9a, 88, 122, 86, 5);
+		global[g133] = 1;
+		global[g131] = -1;
+		global[g141] = -1;
+		kernel_reset_animation(scratch.x9a, 2);
+		kernel_reset_animation(scratch.x9c, 2);
+		global[g133] = 0;
+		global[g143] = 0;
+		player.walker_visible = -1;
+		player.commands_allowed = -1;
+	} else {
+		global[g131] = 0;
+		global[g141] = 0;
+		global[g133] = 1;
+		global[g143] = 1;
+		player.x = 103;
+		player.y = 136;
+		player.facing = 8;
+	}
+
+	if (previous_room == 199) {
+		scratch.xaa = -1;
+		global[g131] = 0;
+		global[g141] = 0;
+		scratch.x9c = kernel_run_animation_disp('r', 6, 0);
+		kernel_position_anim(scratch.x9c, 63, 134, 91, 3);
+		scratch.x9a = kernel_run_animation_disp('e', 2, 0);
+		kernel_position_anim(scratch.x9a, 88, 122, 86, 5);
+		player.x = 116;
+		player.y = 130;
+		player.facing = 4;
+		global[g131] = -1;
+		global[g141] = -1;
+		kernel_reset_animation(scratch.x9a, 2);
+		kernel_reset_animation(scratch.x9c, 2);
+		global[g133] = 0;
+		global[g143] = 0;
+		restore_player();
+		player.commands_allowed = -1;
+		player.walker_visible = -1;
+	}
+
+	if (global[g066] == 3)
+		return;
+
+	aa[8] = kernel_run_animation(kernel_name('y', 1), 0);
+	aainfo[8]._active = -1;
+	aa[3] = kernel_run_animation("*rm306t11", 0);
+	aainfo[3]._active = -1;
+	aainfo[3]._val3 = 4;
+	aa[1] = kernel_run_animation("*rm306t10", 0);
+	kernel_reset_animation(aa[1], 60);
+	aainfo[1]._active = -1;
+	aainfo[1]._val3 = 11;
+	aa[4] = kernel_run_animation(kernel_name('t', 5), 0);
+	aainfo[4]._active = -1;
+	aainfo[4]._val3 = 3;
+	aa[2] = kernel_run_animation(kernel_name('s', 1), 0);
+	aainfo[2]._active = -1;
+	aainfo[2]._val3 = (global[g066] == 0) ? 4 : 3;
+	aa[5] = kernel_run_animation(kernel_name('t', 7), 0);
+	aainfo[5]._active = -1;
+	aainfo[5]._val3 = 1;
+}
+
+static void room_306_init3() {
+	scratch.xaa = -1;
+	global[g131] = 0;
+	global[g141] = 0;
+	scratch.x9c = kernel_run_animation_disp('r', 6, 0);
+	kernel_position_anim(scratch.x9c, 63, 134, 91, 3);
+	scratch.x9a = kernel_run_animation_disp('e', 2, 0);
+	kernel_position_anim(scratch.x9a, 88, 122, 86, 5);
+
+	if (previous_room != KERNEL_RESTORING_GAME) {
+		player.x = 116;
+		player.y = 130;
+		player.facing = 4;
+	}
+
+	if (previous_room == KERNEL_RESTORING_GAME) {
+		global[g131] = -1;
+		global[g141] = -1;
+		kernel_reset_animation(scratch.x9a, 2);
+		kernel_reset_animation(scratch.x9c, 2);
+		global[g133] = 0;
+		global[g143] = 0;
+		return;
+	}
+
+	if (previous_room == 199) {
+		global[g131] = -1;
+		global[g141] = -1;
+		kernel_reset_animation(scratch.x9a, 2);
+		kernel_reset_animation(scratch.x9c, 2);
+		global[g133] = 0;
+		global[g143] = 0;
+		restore_player();
+		player.commands_allowed = -1;
+		player.walker_visible = -1;
+		return;
+	}
+
+	if (previous_room == 305) {
+		aa[0] = kernel_run_animation(kernel_name('t', 9), 100);
+		aainfo[0]._active = -1;
+		scratch.x92 = 50;
+		return;
+	}
+
+	global[g131] = -1;
+	global[g141] = -1;
+	kernel_reset_animation(scratch.x9a, 2);
+	kernel_reset_animation(scratch.x9c, 2);
+	global[g133] = 0;
+	global[g143] = 0;
+	player.commands_allowed = -1;
+	player.walker_visible = -1;
+}
+
 static void room_306_init() {
+	scratch.xa2 = 0;
+	scratch.xac = -1;
+	scratch.xae = -1;
+	scratch.xa4 = 0;
+
+	if (global[g066] >= 2)
+		kernel_flip_hotspot(180, 0);
+
+	if (previous_room != KERNEL_RESTORING_GAME) {
+		scratch.xaa = 0;
+		scratch.x9e = -1;
+
+		if (previous_room != 199) {
+			player.walker_visible = 0;
+			player.commands_allowed = 0;
+		}
+
+		for (int16 i = 0; i < 10; i++) {
+			aainfo[i]._active = 0;
+			aainfo[i]._frame = 1;
+			aainfo[i]._val3 = 0;
+			aainfo[i]._val4 = 0;
+		}
+
+		if (previous_room != 199) {
+			if (flags[20] != 3)
+				flags[20]++;
+		}
+	}
+
+	switch (flags[20]) {
+	case 1:
+		room_306_init1();
+		break;
+	case 2:
+		room_306_init2();
+		break;
+	case 3:
+		room_306_init3();
+		break;
+	}
+
+	switch (global[g066]) {
+	case 0:
+	case 1:
+		scratch.xa0 = 30;
+		kernel_timing_trigger(1, 117);
+		global[player_score] = 0;
+		break;
+	case 2:
+		kernel_timing_trigger(10, 110);
+		global[player_score] = 0;
+		break;
+	case 3:
+		kernel_timing_trigger(1, 118);
+		global[player_score] = -1;
+		break;
+	default:
+		break;
+	}
+
+	midi_stop();
+}
+
+static void room_306_anim1() {
+	int16 cur = kernel_anim[aa[0]].frame;
+	if (cur != aainfo[0]._frame)
+		aainfo[0]._frame = cur;
+	if (global[player_hyperwalked] == -1) {
+		aainfo[0]._frame = scratch.x92 - 1;
+		kernel_reset_animation(aa[0], aainfo[0]._frame);
+	}
+}
+
+static void room_306_anim7() {
+	int16 result = -1;
+	int16 cur = kernel_anim[aa[6]].frame;
+	if (cur != aainfo[6]._frame) {
+		aainfo[6]._frame = cur;
+		if (cur == 12) {
+			if (global[g066] == 3) {
+				digi_play_build_ii('b', 4, 1);
+				scratch.x9e = 90;
+				aainfo[6]._val3 = 12;
+			} else {
+				digi_play_build(306, 'r', 1, 1);
+				scratch.x9e++;
+				aainfo[6]._val3 = 6;
+			}
+		} else if (cur == 39) {
+			dont_frag_the_palette();
+			kernel_abort_animation(aa[6]);
+			aainfo[6]._active = 0;
+			scratch.xaa = 666;
+			scratch.x9c = kernel_run_animation_disp('r', 6, 0);
+			kernel_position_anim(scratch.x9c, 63, 134, 91, 3);
+			global[g141] = -1;
+			kernel_reset_animation(scratch.x9c, 2);
+			kernel_synch(KERNEL_ANIM, scratch.x9c, KERNEL_NOW, 0);
+			global[g143] = 0;
+			if (!player.been_here_before) {
+				scratch.xac = kernel_run_animation_write(0);
+				kernel_reset_animation(scratch.xac, 4);
+				kernel_position_anim(scratch.xac, 88, 122, 86, 5);
+				global[walker_converse_now] = 1;
+				global[g007] = 2;
+				kernel_synch(KERNEL_ANIM, scratch.xac, KERNEL_NOW, 0);
+			} else {
+				scratch.xaa = -1;
+				scratch.x9a = kernel_run_animation_disp('e', 2, 0);
+				kernel_position_anim(scratch.x9a, 88, 122, 86, 5);
+				global[g131] = -1;
+				kernel_reset_animation(scratch.x9a, 2);
+				kernel_synch(KERNEL_ANIM, scratch.x9a, KERNEL_NOW, 0);
+				global[g133] = 0;
+				player.commands_allowed = -1;
+			}
+			if (global[g066] != 3) {
+				scratch.xa0 = 30;
+				kernel_timing_trigger(1, 117);
+			} else {
+				kernel_timing_trigger(1, 118);
+			}
+		} else {
+			bool dispatch = (cur == 1) ||
+			                (cur >= 13 && cur <= 19) ||
+			                (cur == 24) ||
+			                (cur >= 26 && cur <= 31);
+			if (dispatch) {
+				switch (aainfo[6]._val3) {
+				case 4:  result = 0; break;
+				case 6:  result = imath_random(13, 18); break;
+				case 7:
+					result = 6;
+					if (global[g066] == 3)
+						aainfo[6]._val3 = 12;
+					break;
+				case 8:
+					result = 22;
+					aainfo[6]._val3 = (scratch.x9e == 90) ? 13 : 4;
+					break;
+				case 12: result = imath_random(26, 30); break;
+				case 13:
+					result = 31;
+					player.walker_visible = -1;
+					kernel_synch(KERNEL_PLAYER, 0, KERNEL_ANIM, aa[6]);
+					player_walk(116, 130, 4);
+					break;
+				default: break;
+				}
+			}
+		}
+		if (result >= 0) {
+			kernel_reset_animation(aa[6], result);
+			aainfo[6]._frame = result;
+		}
+	}
+	if (kernel.trigger == 7 || kernel.trigger == 105) {
+		if (scratch.x9e == 90)
+			aainfo[6]._val3 = 8;
+	}
+}
+
+static void room_306_anim8() {
+	int16 result = -1;
+	int16 cur = kernel_anim[aa[7]].frame;
+	if (cur != aainfo[7]._frame) {
+		aainfo[7]._frame = cur;
+		if (cur >= 1 && cur <= 8) {
+			switch (aainfo[7]._val3) {
+			case 3:  result = imath_random(2, 7); break;
+			case 4:  result = 0; break;
+			case 5:  result = 23; break;
+			default: break;
+			}
+		} else if (cur == 24) {
+			kernel_abort_animation(aa[7]);
+			aainfo[7]._active = 0;
+			aa[9] = kernel_run_animation(kernel_name('t', 6), 0);
+			aainfo[9]._active = -1;
+			aainfo[2]._val3 = 3;
+			scratch.xa0 = 100;
+			digi_initial_volume(100);
+			digi_play_build(305, '_', 1, 3);
+			digi_val2 = -1;
+		}
+		if (result >= 0) {
+			kernel_reset_animation(aa[7], result);
+			aainfo[7]._frame = result;
+		}
+	}
+	if (kernel.trigger != 7 && kernel.trigger != 105)
+		return;
+	switch (scratch.x9e) {
+	case 10:
+		aainfo[7]._val3 = 4;
+		kernel_timing_trigger(45, 105);
+		scratch.x9e++;
+		return;
+	case 11:
+		digi_play_build(306, 'k', 1, 1);
+		scratch.x9e++;
+		aainfo[2]._val3 = 3;
+		digi_stop(3);
+		scratch.xa4 = -1;
+		return;
+	case 12:
+		aainfo[2]._val3 = 4;
+		aainfo[7]._val3 = 4;
+		kernel_timing_trigger(5, 105);
+		scratch.x9e++;
+		scratch.xa4 = 0;
+		digi_stop(3);
+		digi_initial_volume(30);
+		digi_play_build(305, '_', 1, 3);
+		kernel_timing_trigger(1, 106);
+		digi_val2 = -1;
+		scratch.xa0 = 30;
+		return;
+	case 13:
+		aainfo[6]._val3 = 7;
+		return;
+	case 14:
+		aainfo[6]._val3 = 8;
+		kernel_timing_trigger(45, 105);
+		scratch.x9e++;
+		return;
+	case 15:
+		aainfo[6]._val3 = 4;
+		digi_stop(3);
+		scratch.xa4 = -1;
+		digi_play_build(306, 'l', 1, 1);
+		scratch.x9e++;
+		aainfo[4]._val3 = 16;
+		return;
+	case 16:
+		aainfo[4]._val3 = 3;
+		aainfo[7]._val3 = 5;
+		kernel_timing_trigger(45, 105);
+		scratch.x9e++;
+		scratch.xa4 = 0;
+		digi_initial_volume(30);
+		digi_play_build(305, '_', 1, 3);
+		kernel_timing_trigger(1, 106);
+		digi_val2 = -1;
+		scratch.xa0 = 30;
+		return;
+	default:
+		return;
+	}
+}
+
+static void room_306_anim9() {
+	int16 result = -1;
+	int16 cur = kernel_anim[aa[8]].frame;
+	if (cur == aainfo[8]._frame)
+		return;
+	aainfo[8]._frame = cur;
+	if (cur == 49) {
+		if (scratch.xae == 0)
+			kernel_timing_trigger(10, 112);
+	} else if (cur == 50) {
+		result = 49;
+	}
+	if (result >= 0) {
+		kernel_reset_animation(aa[8], result);
+		aainfo[8]._frame = result;
+	}
+}
+
+static void room_306_anim2() {
+	int16 result = -1;
+	int16 cur = kernel_anim[aa[1]].frame;
+	if (cur == aainfo[1]._frame)
+		return;
+	aainfo[1]._frame = cur;
+	if (cur == 59) {
+		kernel_abort_animation(aa[1]);
+		aainfo[1]._active = 0;
+		aainfo[3]._val3 = 10;
+	} else if (cur == 60) {
+		if (aainfo[1]._val3 == 11)
+			result = 59;
+	}
+	if (result >= 0) {
+		kernel_reset_animation(aa[1], result);
+		aainfo[1]._frame = result;
+	}
+}
+
+static void room_306_anim3() {
+	int16 result = -1;
+	int16 cur = kernel_anim[aa[2]].frame;
+	if (cur == aainfo[2]._frame)
+		return;
+	aainfo[2]._frame = cur;
+	if (cur >= 1 && cur <= 6) {
+		switch (aainfo[2]._val3) {
+		case 3:  result = imath_random(1, 2); break;
+		case 4:  result = 0; break;
+		case 14: result = imath_random(4, 5); break;
+		case 15: result = 3; break;
+		default: break;
+		}
+	}
+	if (result >= 0) {
+		kernel_reset_animation(aa[2], result);
+		aainfo[2]._frame = result;
+	}
+}
+
+static void room_306_anim4() {
+	int16 result = -1;
+	int16 cur = kernel_anim[aa[3]].frame;
+	if (cur == aainfo[3]._frame)
+		return;
+	aainfo[3]._frame = cur;
+	if (cur == 34) {
+		dont_frag_the_palette();
+		kernel_abort_animation(aa[3]);
+		aainfo[3]._active = 0;
+		global[g066] = 3;
+		global[player_score] = -1;
+		kernel_timing_trigger(1, 118);
+		aainfo[6]._val3 = 7;
+	} else if (cur == 1 || cur == 5) {
+		switch (aainfo[3]._val3) {
+		case 4:
+			result = (cur == 1) ? 0 : 4;
+			break;
+		case 9:
+			result = 1;
+			aainfo[3]._val3 = 4;
+			break;
+		case 10:
+			result = 6;
+			break;
+		default:
+			break;
+		}
+	}
+	if (result >= 0) {
+		kernel_reset_animation(aa[3], result);
+		aainfo[3]._frame = result;
+	}
+}
+
+static void room_306_anim5() {
+	int16 result = -1;
+	int16 cur = kernel_anim[aa[4]].frame;
+	if (cur == aainfo[4]._frame)
+		return;
+	aainfo[4]._frame = cur;
+	if ((cur >= 4 && cur <= 7) || (cur >= 10 && cur <= 16)) {
+		if (aainfo[4]._val3 == 3)
+			result = imath_random(3, 6);
+		else if (aainfo[4]._val3 == 16)
+			result = imath_random(9, 15);
+	}
+	if (result >= 0) {
+		kernel_reset_animation(aa[4], result);
+		aainfo[4]._frame = result;
+	}
+}
+
+static void room_306_anim6() {
+	int16 result = -1;
+	int16 cur = kernel_anim[aa[5]].frame;
+	if (cur == aainfo[5]._frame)
+		return;
+	aainfo[5]._frame = cur;
+	if (cur >= 1 && cur <= 7) {
+		switch (aainfo[5]._val3) {
+		case 0: result = 0; break;
+		case 1: result = imath_random(1, 6); break;
+		default: break;
+		}
+	}
+	if (result >= 0) {
+		kernel_reset_animation(aa[5], result);
+		aainfo[5]._frame = result;
+	}
+}
+
+static void room_306_anim10() {
+	int16 result = -1;
+	int16 cur = kernel_anim[aa[9]].frame;
+	if (cur == aainfo[9]._frame)
+		return;
+	aainfo[9]._frame = cur;
+	if (cur == 28) {
+		kernel_abort_animation(aa[9]);
+		aainfo[9]._active = 0;
+		global[g066] = 1;
+		aainfo[6]._val3 = 13;
+	} else if (cur == 15) {
+		digi_initial_volume(scratch.xa0);
+		digi_play_build(305, '_', 1, 3);
+		kernel_timing_trigger(1, 107);
+		digi_val2 = -1;
+	} else if (cur == 20) {
+		digi_play_build(203, '_', 1, 2);
+	}
+	if (result >= 0) {
+		kernel_reset_animation(aa[9], result);
+		aainfo[9]._frame = result;
+	}
 }
 
 static void room_306_daemon() {
+	switch (kernel.trigger) {
+	case 7:
+		if (global[walker_converse_state] != 0) {
+			global[walker_converse_state] = 0;
+			close_journal(3);
+			player.commands_allowed = -1;
+			if (digi_val1 != 0) {
+				if (global[g066] == 3) {
+					kernel_timing_trigger(1, 118);
+				} else {
+					scratch.xa0 = 30;
+					kernel_timing_trigger(1, 117);
+				}
+			}
+		} else {
+			switch (scratch.x9e) {
+			case 100:
+				kernel_timing_trigger(20, 113);
+				global[player_score] = -1;
+				break;
+
+			case 2000:
+				kernel_abort_animation(scratch.xa6);
+				scratch.xa2 = 0;
+				scratch.x9e = -1;
+				kernel_reset_animation(scratch.x9a, 1);
+				kernel_synch(KERNEL_ANIM, scratch.x9a, KERNEL_NOW, 0);
+				global[g133] = 0;
+				player.commands_allowed = -1;
+				if (digi_val1 != 0) {
+					if (global[g066] == 3) {
+						kernel_timing_trigger(1, 118);
+					} else {
+						scratch.xa0 = 30;
+						kernel_timing_trigger(1, 117);
+					}
+				}
+				break;
+
+			case 2500:
+				kernel_abort_animation(scratch.xa8);
+				scratch.xa2 = 0;
+				scratch.x9e = -1;
+				player.walker_visible = -1;
+				kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
+				kernel_timing_trigger(20, 114);
+				break;
+
+			case 3000:
+				aainfo[2]._val3 = 3;
+				scratch.xa2 = 0;
+				scratch.x9e = -1;
+				player.commands_allowed = -1;
+				if (digi_val1 != 0) {
+					if (global[g066] == 3) {
+						kernel_timing_trigger(1, 118);
+					} else {
+						scratch.xa0 = 30;
+						kernel_timing_trigger(1, 117);
+					}
+				}
+				break;
+
+			default:
+				break;
+			}
+		}
+		break;
+
+	case 24:
+		kernel_reset_animation(scratch.x9a, 0);
+		global[g133] = 1;
+		scratch.xa6 = kernel_run_animation_talk('e', 3, 0);
+		kernel_position_anim(scratch.xa6, 88, 122, 86, 5);
+		global[g156] = 2;
+		kernel_synch(KERNEL_ANIM, scratch.xa6, KERNEL_ANIM, scratch.x9a);
+		digi_play_build(306, 'e', 1, 1);
+		scratch.x9e = 2000;
+		scratch.xa2 = -1;
+		if (global[g066] == 3) {
+			kernel_timing_trigger(1, 118);
+		} else {
+			kernel_timing_trigger(1, 117);
+		}
+		break;
+
+	case 26:
+		kernel_reset_animation(scratch.x9a, 0);
+		kernel_reset_animation(scratch.x9c, 0);
+		global[g133] = 1;
+		global[g143] = 1;
+		break;
+
+	case 27:
+		kernel_abort_animation(scratch.xac);
+		scratch.xac = -1;
+		scratch.x9a = kernel_run_animation_disp('e', 2, 0);
+		kernel_position_anim(scratch.x9a, 88, 122, 86, 5);
+		global[g131] = -1;
+		kernel_reset_animation(scratch.x9a, 2);
+		kernel_synch(KERNEL_ANIM, scratch.x9a, KERNEL_NOW, 0);
+		global[g133] = 0;
+		scratch.xaa = -1;
+		player.commands_allowed = -1;
+		scratch.xa0 = 30;
+		kernel_timing_trigger(1, 117);
+		break;
+
+	case 100:
+		kernel_abort_animation(aa[0]);
+		aainfo[0]._active = 0;
+		if (digi_val1 != 0) {
+			if (global[g066] == 3) {
+				kernel_timing_trigger(1, 118);
+			} else {
+				scratch.xa0 = 30;
+				kernel_timing_trigger(1, 117);
+			}
+		}
+		player.commands_allowed = -1;
+		player.walker_visible = -1;
+		kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
+		global[g131] = -1;
+		global[g141] = -1;
+		kernel_reset_animation(scratch.x9a, 1);
+		kernel_synch(KERNEL_ANIM, scratch.x9a, KERNEL_NOW, 0);
+		kernel_reset_animation(scratch.x9c, 1);
+		kernel_synch(KERNEL_ANIM, scratch.x9c, KERNEL_NOW, 0);
+		global[g133] = 0;
+		global[g143] = 0;
+		break;
+
+	case 101:
+		kernel_abort_animation(aa[scratch.x94]);
+		aainfo[scratch.x94]._active = 0;
+		kernel_reset_animation(scratch.x9c, 1);
+		kernel_synch(KERNEL_ANIM, scratch.x9c, KERNEL_NOW, 0);
+		global[g143] = 0;
+		player.commands_allowed = -1;
+		if (digi_val1 != 0) {
+			if (global[g066] == 3) {
+				kernel_timing_trigger(1, 118);
+			} else {
+				scratch.xa0 = 30;
+				kernel_timing_trigger(1, 117);
+			}
+		}
+		break;
+
+	case 104:
+		kernel_abort_animation(aa[6]);
+		kernel_abort_animation(aa[7]);
+		aa[6] = kernel_run_animation(kernel_name('t', 4), 0);
+		aainfo[6]._active = -1;
+		aainfo[6]._val3 = 4;
+		aa[7] = kernel_run_animation(kernel_name('t', 2), 0);
+		aainfo[7]._active = -1;
+		aainfo[7]._val3 = 4;
+		digi_play_build(306, 'h', 1, 1);
+		aainfo[7]._val3 = 3;
+		scratch.x9e = 10;
+		digi_stop(3);
+		scratch.xa4 = -1;
+		break;
+
+	case 106:
+		if (scratch.xa0 < 101) {
+			scratch.xa0++;
+			digi_set_volume(scratch.xa0, 3);
+			if (scratch.xa0 < 101)
+				kernel_timing_trigger(1, 106);
+		}
+		break;
+
+	case 107:
+		if (scratch.xa0 > 30) {
+			scratch.xa0--;
+			digi_set_volume(scratch.xa0, 3);
+			if (scratch.xa0 > 30)
+				kernel_timing_trigger(1, 107);
+		}
+		break;
+
+	case 110:
+		kernel_timing_trigger(1300, 112);
+		digi_play_build(306, '_', 999, 1);
+		scratch.x9e = 100;
+		break;
+
+	case 111:
+		if (global[g065] != 0)
+			global[g071] = -1;
+		kernel_abort_animation(aa[5]);
+		aainfo[5]._active = 0;
+		kernel_abort_animation(aa[2]);
+		aainfo[2]._active = 0;
+		kernel_abort_animation(aa[4]);
+		aainfo[4]._active = 0;
+		kernel_reset_animation(aa[1], 0);
+		kernel_synch(KERNEL_ANIM, aa[1], KERNEL_NOW, 0);
+		break;
+
+	case 112:
+		aainfo[3]._val3 = 9;
+		kernel_timing_trigger(20, 111);
+		break;
+
+	case 113:
+		kernel_abort_animation(aa[8]);
+		aainfo[8]._active = 0;
+		aa[6] = kernel_run_animation(kernel_name('t', 4), 0);
+		kernel_reset_animation(aa[6], 7);
+		aainfo[6]._active = -1;
+		aainfo[6]._val3 = 7;
+		kernel_timing_trigger(1, 118);
+		break;
+
+	case 114:
+		aainfo[2]._val3 = 14;
+		digi_play_build(306, 'k', 2, 1);
+		scratch.x9e = 3000;
+		break;
+
+	case 117:
+		if (global[g066] <= 1 && scratch.xa4 == 0) {
+			digi_initial_volume(scratch.xa0);
+			digi_play_build(305, '_', 1, 3);
+		}
+		break;
+
+	case 118:
+		if (global[g066] == 3) {
+			digi_initial_volume(15);
+			digi_play_build(306, '_', 2, 3);
+			digi_val2 = -1;
+		}
+		break;
+
+	default:
+		break;
+	}
+
+	if (scratch.xa2 != 0) {
+		if (scratch.x9e == 2000) {
+			kernel_random_frame(scratch.xa6, &global[g153], global[g156]);
+		} else if (scratch.x9e == 2500) {
+			kernel_random_frame(scratch.xa8, &global[g151], global[g154]);
+		}
+	}
+
+	if (scratch.xaa != 0) {
+		if (scratch.xaa != 666)
+			global_anim1(2, scratch.x9a, global[g131], &global[g132]);
+		global_anim2(6, scratch.x9c, global[g141], &global[g142]);
+	}
+
+	if (scratch.xac >= 0)
+		global_anim3(scratch.xac, &global[g008]);
+
+	if (aainfo[0]._active != 0) room_306_anim1();
+	if (aainfo[5]._active != 0) room_306_anim6();
+	if (aainfo[6]._active != 0) room_306_anim7();
+	if (aainfo[7]._active != 0) room_306_anim8();
+	if (aainfo[8]._active != 0) room_306_anim9();
+	if (aainfo[9]._active != 0) room_306_anim10();
+	if (aainfo[3]._active != 0) room_306_anim4();
+	if (aainfo[1]._active != 0) room_306_anim2();
+	if (aainfo[2]._active != 0) room_306_anim3();
+	if (aainfo[4]._active != 0) room_306_anim5();
 }
 
 static void room_306_pre_parser() {
+	if (player_parse(13, 38, 0))
+		player.walk_off_edge_to_room = 305;
 }
 
 static void room_306_parser() {
+	if (global[walker_converse_state] != 0) {
+		player.commands_allowed = 0;
+		digi_play_build_ii('c', 1, 1);
+		goto handled;
+	}
+
+	if (player_parse(180, 0)) {
+		aainfo[2]._val3 = 15;
+		player.commands_allowed = 0;
+		player.walker_visible = 0;
+		scratch.xa8 = kernel_run_animation_talk('b', 9, 0);
+		kernel_position_anim(scratch.xa8, player.x, player.y, player.scale, player.depth);
+		global[g154] = 2;
+		kernel_synch(KERNEL_ANIM, scratch.xa8, KERNEL_PLAYER, 0);
+		digi_play_build(306, 'b', 1, 1);
+		scratch.x9e = 2500;
+		scratch.xa2 = -1;
+		if (digi_val1 == 0) {
+			kernel.trigger_setup_mode = 1;
+			kernel_timing_trigger(1, 117);
+		}
+		goto handled;
+	}
+
+	if (player_parse(133, 0)) {
+		player.commands_allowed = 0;
+		global[g135] = -1;
+		goto handled;
+	}
+
+	goto done;
+
+handled:
+	player.command_ready = false;
+
+done:
+	;
 }
 
 void room_306_synchronize(Common::Serializer &s) {
 	for (int16 &v : scratch.sprite)    s.syncAsSint16LE(v);
 	for (int16 &v : scratch.sequence)  s.syncAsSint16LE(v);
 	for (int16 &v : scratch.animation) s.syncAsSint16LE(v);
+	for (auto &ai : scratch.animation_info) ai.synchronize(s);
+	s.syncAsSint16LE(scratch.x90);
+	s.syncAsSint16LE(scratch.x92);
+	s.syncAsSint16LE(scratch.x94);
+	s.syncAsSint16LE(scratch.x9a);
+	s.syncAsSint16LE(scratch.x9c);
+	s.syncAsSint16LE(scratch.x9e);
+	s.syncAsSint16LE(scratch.xa0);
+	s.syncAsSint16LE(scratch.xa2);
+	s.syncAsSint16LE(scratch.xa4);
+	s.syncAsSint16LE(scratch.xa6);
+	s.syncAsSint16LE(scratch.xa8);
+	s.syncAsSint16LE(scratch.xaa);
+	s.syncAsSint16LE(scratch.xac);
+	s.syncAsSint16LE(scratch.xae);
 }
 
 void room_306_preload() {



From noreply at scummvm.org  Thu Jun 11 13:12:17 2026
From: noreply at scummvm.org (neuromancer)
Date: Thu, 11 Jun 2026 13:12:17 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 73d44af9ba7286c489835f201ff3372972fcc21c
Message-ID: <20260611131217.D5E9D7005BC@ron.scummvm.net>

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

Summary:
54c2011ec5 FREESCAPE: trigger gamepad in ios only when gameplay starts
49286628a1 FREESCAPE: allow to skip screen or select character when touchpad is used
fc6aa9c014 SCUMM RA1: do not skip cutscene with fire button in gamepad
7e7a5d6ad9 SCUMM RA1: fixed game logic in L12
7cec74da68 SCUMM RA1: keep different volume settings on-sync
7769cde6dd SCUMM RA2: fixed crash at the end of L6
73d44af9ba SCUMM RA2: fixed invalid palette on gameplay when high resolution is used


Commit: 54c2011ec5f6330c283662d79532a1a350f31feb
    https://github.com/scummvm/scummvm/commit/54c2011ec5f6330c283662d79532a1a350f31feb
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-11T15:11:52+02:00

Commit Message:
FREESCAPE: trigger gamepad in ios only when gameplay starts

Changed paths:
    engines/freescape/freescape.cpp
    engines/freescape/freescape.h


diff --git a/engines/freescape/freescape.cpp b/engines/freescape/freescape.cpp
index 7273128bedf..100e95c826f 100644
--- a/engines/freescape/freescape.cpp
+++ b/engines/freescape/freescape.cpp
@@ -39,6 +39,36 @@ namespace Freescape {
 
 FreescapeEngine *g_freescape;
 
+#ifdef IPHONE
+static const char *const kIOSGamepadControllerKey = "gamepad_controller";
+static const char *const kIOSGamepadControllerMinimalLayoutKey = "gamepad_controller_minimal_layout";
+static const char *const kIOSGamepadControllerDirectionalInputKey = "gamepad_controller_directional_input";
+static const int kIOSGamepadControllerDirectionalInputDpad = 1;
+
+static void clearIOSGamepadControllerSetting(FreescapeEngine::IOSGamepadControllerSetting &setting) {
+	setting.present = false;
+	setting.value.clear();
+}
+
+static void saveIOSGamepadControllerSetting(FreescapeEngine::IOSGamepadControllerSetting &setting,
+		const Common::ConfigManager::Domain *domain, const char *key) {
+	setting.present = domain && domain->contains(key);
+	setting.value = setting.present ? domain->getVal(key) : Common::String();
+}
+
+static void restoreIOSGamepadControllerSettingValue(const FreescapeEngine::IOSGamepadControllerSetting &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
+
 bool isEncodedCPCDirectColor(uint8 index) {
 	return index >= 64 && index < 96;
 }
@@ -285,13 +315,11 @@ FreescapeEngine::FreescapeEngine(OSystem *syst, const ADGameDescription *gd)
 	_gameStateBits = 0;
 	_eventManager = new EventManagerWrapper(g_system->getEventManager());
 
-	// Workaround to make the game playable on iOS: remove when there
-	// is a better way to hint the best controls
 #ifdef IPHONE
-	const Common::String &gameDomain = ConfMan.getActiveDomainName();
-	ConfMan.setBool("gamepad_controller", true, gameDomain);
-	ConfMan.setBool("gamepad_controller_minimal_layout", true, gameDomain);
-	ConfMan.setInt("gamepad_controller_directional_input", 1 /* kDirectionalInputDpad */, gameDomain);
+	_iosGamepadControllerSettingsSaved = false;
+	clearIOSGamepadControllerSetting(_iosGamepadController);
+	clearIOSGamepadControllerSetting(_iosGamepadControllerMinimalLayout);
+	clearIOSGamepadControllerSetting(_iosGamepadControllerDirectionalInput);
 #endif
 	g_freescape = this;
 	g_debugger = new Debugger(g_freescape);
@@ -302,7 +330,55 @@ bool FreescapeEngine::isTouchscreenActive() const {
 	return _debugSimulateTouchscreen || g_system->hasFeature(OSystem::kFeatureTouchscreen);
 }
 
+void FreescapeEngine::setIOSGamepadControllerEnabled(bool enabled) {
+#ifdef IPHONE
+	if (!_iosGamepadControllerSettingsSaved) {
+		_iosGamepadControllerDomain = ConfMan.getActiveDomainName();
+		if (_iosGamepadControllerDomain.empty())
+			return;
+
+		const Common::ConfigManager::Domain *domain = ConfMan.getDomain(_iosGamepadControllerDomain);
+		saveIOSGamepadControllerSetting(_iosGamepadController, domain, kIOSGamepadControllerKey);
+		saveIOSGamepadControllerSetting(_iosGamepadControllerMinimalLayout, domain, kIOSGamepadControllerMinimalLayoutKey);
+		saveIOSGamepadControllerSetting(_iosGamepadControllerDirectionalInput, domain, kIOSGamepadControllerDirectionalInputKey);
+		_iosGamepadControllerSettingsSaved = true;
+	}
+
+	if (_iosGamepadControllerDomain.empty())
+		return;
+
+	ConfMan.setBool(kIOSGamepadControllerKey, enabled, _iosGamepadControllerDomain);
+	if (enabled) {
+		ConfMan.setBool(kIOSGamepadControllerMinimalLayoutKey, true, _iosGamepadControllerDomain);
+		ConfMan.setInt(kIOSGamepadControllerDirectionalInputKey, kIOSGamepadControllerDirectionalInputDpad, _iosGamepadControllerDomain);
+	}
+	g_system->applyBackendSettings();
+#else
+	(void)enabled;
+#endif
+}
+
+void FreescapeEngine::restoreIOSGamepadControllerSettings() {
+#ifdef IPHONE
+	if (!_iosGamepadControllerSettingsSaved)
+		return;
+
+	restoreIOSGamepadControllerSettingValue(_iosGamepadController, _iosGamepadControllerDomain, kIOSGamepadControllerKey);
+	restoreIOSGamepadControllerSettingValue(_iosGamepadControllerMinimalLayout, _iosGamepadControllerDomain, kIOSGamepadControllerMinimalLayoutKey);
+	restoreIOSGamepadControllerSettingValue(_iosGamepadControllerDirectionalInput, _iosGamepadControllerDomain, kIOSGamepadControllerDirectionalInputKey);
+	g_system->applyBackendSettings();
+
+	clearIOSGamepadControllerSetting(_iosGamepadController);
+	clearIOSGamepadControllerSetting(_iosGamepadControllerMinimalLayout);
+	clearIOSGamepadControllerSetting(_iosGamepadControllerDirectionalInput);
+	_iosGamepadControllerDomain.clear();
+	_iosGamepadControllerSettingsSaved = false;
+#endif
+}
+
 FreescapeEngine::~FreescapeEngine() {
+	restoreIOSGamepadControllerSettings();
+
 	removeTimers();
 	delete _rnd;
 
@@ -1082,6 +1158,7 @@ void FreescapeEngine::beforeStarting() {}
 
 Common::Error FreescapeEngine::run() {
 	_vsyncEnabled = g_system->getFeatureState(OSystem::kFeatureVSync);
+	setIOSGamepadControllerEnabled(false);
 	_frameLimiter = new Graphics::FrameLimiter(g_system, ConfMan.getInt("engine_speed"));
 	// Initialize graphics
 	//_screenW = g_system->getWidth();
@@ -1093,8 +1170,10 @@ Common::Error FreescapeEngine::run() {
 
 	// The following error code will force return to launcher
 	// but it will not force any other GUI message to be displayed
-	if (!_gfx)
+	if (!_gfx) {
+		restoreIOSGamepadControllerSettings();
 		return Common::kUserCanceled;
+	}
 
 	_gfx->init();
 
@@ -1136,6 +1215,7 @@ Common::Error FreescapeEngine::run() {
 			debugC(1, kFreescapeDebugMove, "Starting area %d", _currentArea->getAreaID());
 			beforeStarting();
 			_gameStateControl = kFreescapeGameStatePlaying;
+			setIOSGamepadControllerEnabled(true);
 		} else if (_gameStateControl == kFreescapeGameStateEnd)
 			endGame();
 
@@ -1174,6 +1254,7 @@ Common::Error FreescapeEngine::run() {
 	_eventManager->clearExitEvents();
 	CursorMan.showMouse(true);
 	g_system->lockMouse(false);
+	restoreIOSGamepadControllerSettings();
 	return Common::kNoError;
 }
 
@@ -1469,6 +1550,7 @@ Common::Error FreescapeEngine::loadGameStream(Common::SeekableReadStream *stream
 	}
 	assert(_playerHeight > 0);
 	_gameStateControl = kFreescapeGameStatePlaying;
+	setIOSGamepadControllerEnabled(true);
 	return loadGameStreamExtended(stream);
 }
 
diff --git a/engines/freescape/freescape.h b/engines/freescape/freescape.h
index 7aabd4adf40..f17fe4fad25 100644
--- a/engines/freescape/freescape.h
+++ b/engines/freescape/freescape.h
@@ -369,6 +369,19 @@ public:
 	bool _useWASDControls;
 	bool _debugSimulateTouchscreen;
 	bool isTouchscreenActive() const;
+	void setIOSGamepadControllerEnabled(bool enabled);
+	void restoreIOSGamepadControllerSettings();
+#ifdef IPHONE
+	struct IOSGamepadControllerSetting {
+		bool present;
+		Common::String value;
+	};
+	bool _iosGamepadControllerSettingsSaved;
+	Common::String _iosGamepadControllerDomain;
+	IOSGamepadControllerSetting _iosGamepadController;
+	IOSGamepadControllerSetting _iosGamepadControllerMinimalLayout;
+	IOSGamepadControllerSetting _iosGamepadControllerDirectionalInput;
+#endif
 	// Player movement state
 	bool _moveForward;
 	bool _moveBackward;


Commit: 49286628a198fce090539ab6a0244c234d094f94
    https://github.com/scummvm/scummvm/commit/49286628a198fce090539ab6a0244c234d094f94
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-11T15:11:53+02:00

Commit Message:
FREESCAPE: allow to skip screen or select character when touchpad is used

Changed paths:
    engines/freescape/games/castle/amiga.cpp
    engines/freescape/games/castle/castle.cpp
    engines/freescape/games/dark/dark.cpp
    engines/freescape/games/eclipse/eclipse.cpp


diff --git a/engines/freescape/games/castle/amiga.cpp b/engines/freescape/games/castle/amiga.cpp
index 820a9ff2579..57011e8647b 100644
--- a/engines/freescape/games/castle/amiga.cpp
+++ b/engines/freescape/games/castle/amiga.cpp
@@ -998,8 +998,8 @@ private:
 			case Common::EVENT_LBUTTONDOWN:
 			case Common::EVENT_RBUTTONDOWN:
 				if (acceptSelection) {
-					int mouseX = kWidth * event.mouse.x / g_system->getWidth();
-					key = mouseX < selectionSplitX() ? 1 : 2;
+					Common::Point mouse = _engine->getNormalizedPosition(event.mouse);
+					key = mouse.x < selectionSplitX() ? 1 : 2;
 				} else if (acceptAnyKey) {
 					key = 3;
 				}
diff --git a/engines/freescape/games/castle/castle.cpp b/engines/freescape/games/castle/castle.cpp
index c410ad12703..01cab328349 100644
--- a/engines/freescape/games/castle/castle.cpp
+++ b/engines/freescape/games/castle/castle.cpp
@@ -2211,6 +2211,7 @@ void CastleEngine::selectCharacterScreen() {
 	// lines[5] = prince, lines[6] = princess for 8-bit text menus.
 	// For DOS, use riddle text line positions.
 	Common::Rect princeSelector, princessSelector;
+	Common::Rect princeHitArea, princessHitArea;
 	if (isSpectrum() || isCPC() || isC64() || isAmiga()) {
 		int x = _viewArea.left + 3;
 		int lineHeight = 12; // Castle Master line spacing in drawStringsInSurface
@@ -2225,6 +2226,8 @@ void CastleEngine::selectCharacterScreen() {
 			princeSelector = Common::Rect(x, princeY, x + selectorWidth, princeY + lineHeight);
 			princessSelector = Common::Rect(x, princessY, x + selectorWidth, princessY + lineHeight);
 		}
+		princeHitArea = Common::Rect(_viewArea.left, princeY, _viewArea.right, princeY + lineHeight);
+		princessHitArea = Common::Rect(_viewArea.left, princessY, _viewArea.right, princessY + lineHeight);
 	} else {
 		// DOS: text comes from _riddleList[21], calculate from actual riddle line positions
 		Common::Array<RiddleText> selectMessage = _riddleList[21]._lines;
@@ -2237,6 +2240,9 @@ void CastleEngine::selectCharacterScreen() {
 			else if (i == int(selectMessage.size()) - 1)
 				princessSelector = _font.getBoundingBox(selectMessage[i]._text, x, y);
 		}
+		int splitX = _fullscreenViewArea.left + _fullscreenViewArea.width() / 2;
+		princeHitArea = Common::Rect(_fullscreenViewArea.left, _fullscreenViewArea.top, splitX, _fullscreenViewArea.bottom);
+		princessHitArea = Common::Rect(splitX, _fullscreenViewArea.top, _fullscreenViewArea.right, _fullscreenViewArea.bottom);
 	}
 
 	// On touchscreen, highlight the tap areas with red outlines (expand 1px for readability)
@@ -2263,13 +2269,12 @@ void CastleEngine::selectCharacterScreen() {
 			case Common::EVENT_LBUTTONDOWN:
 				// fallthrough
 			case Common::EVENT_RBUTTONDOWN:
-				mouse.x = _screenW * event.mouse.x / g_system->getWidth();
-				mouse.y = _screenH * event.mouse.y / g_system->getHeight();
+				mouse = getNormalizedPosition(event.mouse);
 
-				if (princeSelector.contains(mouse)) {
+				if (princeHitArea.contains(mouse) || princeSelector.contains(mouse)) {
 					selected = true;
 					_selectedPrincess = false;
-				} else if (princessSelector.contains(mouse)) {
+				} else if (princessHitArea.contains(mouse) || princessSelector.contains(mouse)) {
 					selected = true;
 					_selectedPrincess = true;
 				}
diff --git a/engines/freescape/games/dark/dark.cpp b/engines/freescape/games/dark/dark.cpp
index e38695c5419..5b617b97d2b 100644
--- a/engines/freescape/games/dark/dark.cpp
+++ b/engines/freescape/games/dark/dark.cpp
@@ -1125,6 +1125,12 @@ void DarkEngine::drawInfoMenu() {
 			case Common::EVENT_SCREEN_CHANGED:
 				_gfx->computeScreenViewport();
 				break;
+			case Common::EVENT_RBUTTONDOWN:
+			// fallthrough
+			case Common::EVENT_LBUTTONDOWN:
+				if (isTouchscreenActive())
+					cont = false;
+				break;
 
 			default:
 				break;
diff --git a/engines/freescape/games/eclipse/eclipse.cpp b/engines/freescape/games/eclipse/eclipse.cpp
index 95c756865f8..57b2465bdbd 100644
--- a/engines/freescape/games/eclipse/eclipse.cpp
+++ b/engines/freescape/games/eclipse/eclipse.cpp
@@ -623,6 +623,12 @@ void EclipseEngine::drawInfoMenu() {
 			case Common::EVENT_SCREEN_CHANGED:
 				_gfx->computeScreenViewport();
 				break;
+			case Common::EVENT_RBUTTONDOWN:
+			// fallthrough
+			case Common::EVENT_LBUTTONDOWN:
+				if (isTouchscreenActive())
+					cont = false;
+				break;
 
 			default:
 				break;


Commit: fc6aa9c0149d3de550f1d2a2eb8abb26dd171902
    https://github.com/scummvm/scummvm/commit/fc6aa9c0149d3de550f1d2a2eb8abb26dd171902
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-11T15:11:53+02:00

Commit Message:
SCUMM RA1: do not skip cutscene with fire button in gamepad

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 60fc7bdd138..da9adbc3a03 100644
--- a/engines/scumm/insane/rebel1/menu.cpp
+++ b/engines/scumm/insane/rebel1/menu.cpp
@@ -686,7 +686,7 @@ bool InsaneRebel1::notifyEvent(const Common::Event &event) {
 		return false;
 
 	if (isTouchscreenActive() && !_interactiveVideoActive && !_menuActive &&
-			(event.type == Common::EVENT_LBUTTONDOWN || event.type == Common::EVENT_LBUTTONUP)) {
+			event.type == Common::EVENT_LBUTTONDOWN) {
 		_vm->_smushVideoShouldFinish = true;
 		return true;
 	}
@@ -875,9 +875,7 @@ bool InsaneRebel1::notifyEvent(const Common::Event &event) {
 		}
 
 		if (isTouchscreenActive() && !_interactiveVideoActive && !_menuActive && pressed &&
-				(event.customType == kScummActionInsaneAttack ||
-				 event.customType == kScummActionInsaneSwitch ||
-				 event.customType == kScummActionInsaneSkip)) {
+				event.customType == kScummActionInsaneSkip) {
 			_vm->_smushVideoShouldFinish = true;
 			return true;
 		}


Commit: 7e7a5d6ad97d13c55b56195d6dd4b4d1be164783
    https://github.com/scummvm/scummvm/commit/7e7a5d6ad97d13c55b56195d6dd4b4d1be164783
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-11T15:11:53+02:00

Commit Message:
SCUMM RA1: fixed game logic in L12

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 0c34182fe5a..92c798a81f0 100644
--- a/engines/scumm/insane/rebel1/iact.cpp
+++ b/engines/scumm/insane/rebel1/iact.cpp
@@ -97,6 +97,9 @@ const int16 kOnFootCursorMinX = 0;
 const int16 kOnFootCursorMaxX = 319;
 const int16 kOnFootCursorMinY = 0;
 const int16 kOnFootCursorMaxY = 199;
+const int16 kRA1Level12TargetA = 195;
+const int16 kRA1Level12TargetB = 197;
+const int16 kRA1Level12TargetC = 199;
 
 // Level 15 final approach 0x5D damage/event codes consumed by
 // RunLevel1GameLoop. The latch stores the raw GAME parameter; no translation is
@@ -1763,22 +1766,22 @@ void InsaneRebel1::updateGameOp0BPhysics() {
 	_frameCounter++;
 
 	if (_currentLevel == 11) {
-		// RunLevel12Flow checks DAT_761A bits 0x20, 0x08, and 0x02 at frame
-		// 0x550. These map to destroyed object IDs 211, 213, and 215 in the
-		// port's one-based frame-object helper. On failure the original keeps
+		// RunLevel12Flow checks the three attackers around frame 0x550. The
+		// event-mask bits map to L12PLAY's one-based FOBJ IDs 195, 197, and 199
+		// in the port's frame-object helper. On failure the original keeps
 		// pumping L12PLAY until frame 0x564, plays L12RETRY, then restarts
 		// L12PLAY without resetting health.
 		if (_levelGameplayPhase == 0 && _frameCounter == 0x550) {
-			const bool target211Destroyed = isFrameObjectPrimarySet(211);
-			const bool target213Destroyed = isFrameObjectPrimarySet(213);
-			const bool target215Destroyed = isFrameObjectPrimarySet(215);
-			if (!target211Destroyed || !target213Destroyed || !target215Destroyed) {
+			const bool targetADestroyed = isFrameObjectPrimarySet(kRA1Level12TargetA);
+			const bool targetBDestroyed = isFrameObjectPrimarySet(kRA1Level12TargetB);
+			const bool targetCDestroyed = isFrameObjectPrimarySet(kRA1Level12TargetC);
+			if (!targetADestroyed || !targetBDestroyed || !targetCDestroyed) {
 				_levelGameplayPhase = 1;
 				debugC(DEBUG_INSANE, "L12 retry armed: frame=0x%04x targets=(%d,%d,%d)",
 					_frameCounter,
-					target211Destroyed ? 1 : 0,
-					target213Destroyed ? 1 : 0,
-					target215Destroyed ? 1 : 0);
+					targetADestroyed ? 1 : 0,
+					targetBDestroyed ? 1 : 0,
+					targetCDestroyed ? 1 : 0);
 			}
 		}
 		if (_levelGameplayPhase == 1 && _frameCounter >= 0x564)


Commit: 7cec74da68d256115f2109a7a35fc15d00a91c26
    https://github.com/scummvm/scummvm/commit/7cec74da68d256115f2109a7a35fc15d00a91c26
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-11T15:11:53+02:00

Commit Message:
SCUMM RA1: keep different volume settings on-sync

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


diff --git a/engines/scumm/insane/rebel1/audio.cpp b/engines/scumm/insane/rebel1/audio.cpp
index 95643e67f17..482a764db67 100644
--- a/engines/scumm/insane/rebel1/audio.cpp
+++ b/engines/scumm/insane/rebel1/audio.cpp
@@ -19,6 +19,7 @@
  *
  */
 
+#include "common/config-manager.h"
 #include "common/system.h"
 
 #include "scumm/file.h"
@@ -68,6 +69,16 @@ void InsaneRebel1::processAudioFrame(int16 feedSize) {
 }
 
 void InsaneRebel1::applyAudioOptions() {
+	const int musicVolume = ConfMan.getInt("music_volume");
+	const int sfxVolume = ConfMan.getInt("sfx_volume");
+	const int speechVolume = ConfMan.getInt("speech_volume");
+
+	_optVolume = CLIP<int>(musicVolume / 2, 0, 127);
+
+	_vm->_mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, musicVolume);
+	_vm->_mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, sfxVolume);
+	_vm->_mixer->setVolumeForSoundType(Audio::Mixer::kSpeechSoundType, speechVolume);
+
 	_vm->_mixer->muteSoundType(Audio::Mixer::kMusicSoundType, !_optMusicEnabled);
 	_vm->_mixer->muteSoundType(Audio::Mixer::kSFXSoundType, !_optSfxEnabled);
 	_vm->_mixer->muteSoundType(Audio::Mixer::kSpeechSoundType, !_optSfxEnabled);
diff --git a/engines/scumm/insane/rebel1/menu.cpp b/engines/scumm/insane/rebel1/menu.cpp
index da9adbc3a03..cfa683a1f60 100644
--- a/engines/scumm/insane/rebel1/menu.cpp
+++ b/engines/scumm/insane/rebel1/menu.cpp
@@ -504,12 +504,16 @@ bool InsaneRebel1::handleMenuCommand(RA1MenuCommand command) {
 			_optionsSel = (_optionsSel + 1) % kOptionsItemCount;
 			return true;
 		case kRA1MenuCommandLeft:
-			if (_optionsSel == 7)
+			if (_optionsSel == 7) {
 				setRebel1Volume(_vm, _optVolume, -5);
+				applyAudioOptions();
+			}
 			return true;
 		case kRA1MenuCommandRight:
-			if (_optionsSel == 7)
+			if (_optionsSel == 7) {
 				setRebel1Volume(_vm, _optVolume, 5);
+				applyAudioOptions();
+			}
 			return true;
 		case kRA1MenuCommandCancel:
 			_optionsSel = 0;
@@ -1037,6 +1041,7 @@ void InsaneRebel1::renderHighScoresOverlay(byte *dst, int pitch, int width, int
 void InsaneRebel1::renderOptionsOverlay(byte *dst, int pitch, int width, int height) {
 	// --- Options submenu (matching original RunGameOptionsMenu) ---
 	_optTextEnabled = ConfMan.getBool("subtitles");
+	_optVolume = CLIP<int>(ConfMan.getInt("music_volume") / 2, 0, 127);
 
 	const char *kDiffNames[3] = { "EASY", "NORMAL", "HARD" };
 
diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp
index ba18b97a5bf..f0bb1a53d36 100644
--- a/engines/scumm/scumm.cpp
+++ b/engines/scumm/scumm.cpp
@@ -2696,6 +2696,14 @@ void ScummEngine_v7::syncSoundSettings() {
 		_imuseDigital->diMUSESetMusicGroupVol(ConfMan.getInt("music_volume") / 2);
 		_imuseDigital->diMUSESetVoiceGroupVol(ConfMan.getInt("speech_volume") / 2);
 		_imuseDigital->diMUSESetSFXGroupVol(ConfMan.getInt("sfx_volume") / 2);
+	} else if (_game.id == GID_REBEL1) {
+		const int musicVolume = ConfMan.getInt("music_volume");
+		const int sfxVolume = ConfMan.getInt("sfx_volume");
+		const int speechVolume = ConfMan.getInt("speech_volume");
+
+		_mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, musicVolume);
+		_mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, sfxVolume);
+		_mixer->setVolumeForSoundType(Audio::Mixer::kSpeechSoundType, speechVolume);
 	}
 }
 #endif


Commit: 7769cde6dd2e4bf1d2606a1c440ad3f23cecd9cc
    https://github.com/scummvm/scummvm/commit/7769cde6dd2e4bf1d2606a1c440ad3f23cecd9cc
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-11T15:11:53+02:00

Commit Message:
SCUMM RA2: fixed crash at the end of L6

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


diff --git a/engines/scumm/smush/rebel/smush_multi_font.cpp b/engines/scumm/smush/rebel/smush_multi_font.cpp
index 47193e58a6e..88b5bd30b46 100644
--- a/engines/scumm/smush/rebel/smush_multi_font.cpp
+++ b/engines/scumm/smush/rebel/smush_multi_font.cpp
@@ -91,14 +91,18 @@ void SmushMultiFont::drawString(const char *str, byte *buffer, Common::Rect &cli
 }
 
 void SmushMultiFont::drawStringWrap(const char *str, byte *buffer, Common::Rect &clipRect, int x, int y, int16 col, TextStyleFlags flags) {
+	drawStringWrap(str, buffer, clipRect, x, y, _vm->_screenWidth, col, flags);
+}
+
+void SmushMultiFont::drawStringWrap(const char *str, byte *buffer, Common::Rect &clipRect, int x, int y, int pitch, int16 col, TextStyleFlags flags) {
 	// Reset to default font before drawing
 	_currentFont = _defaultFont;
 	if (_vm->_game.id == GID_REBEL2) {
 		Rebel2FontSet fontSet = getRebel2FontSet();
-		drawRebel2StringWrap(fontSet, str, strlen(str), buffer, clipRect, x, y, _vm->_screenWidth, col, flags);
+		drawRebel2StringWrap(fontSet, str, strlen(str), buffer, clipRect, x, y, pitch, col, flags);
 		return;
 	}
-	_textRenderer->drawStringWrap(str, buffer, clipRect, x, y, _vm->_screenWidth, col, flags);
+	_textRenderer->drawStringWrap(str, buffer, clipRect, x, y, pitch, col, flags);
 }
 
 int SmushMultiFont::draw2byte(byte *buffer, Common::Rect &clipRect, int x, int y, int pitch, int16 col, uint16 chr) {
diff --git a/engines/scumm/smush/rebel/smush_multi_font.h b/engines/scumm/smush/rebel/smush_multi_font.h
index 1f7fba58487..88e5a72b5d7 100644
--- a/engines/scumm/smush/rebel/smush_multi_font.h
+++ b/engines/scumm/smush/rebel/smush_multi_font.h
@@ -55,6 +55,7 @@ public:
 	void drawString(const char *str, byte *buffer, Common::Rect &clipRect, int x, int y, int16 col, TextStyleFlags flags);
 	void drawString(const char *str, byte *buffer, Common::Rect &clipRect, int x, int y, int pitch, int16 col, TextStyleFlags flags);
 	void drawStringWrap(const char *str, byte *buffer, Common::Rect &clipRect, int x, int y, int16 col, TextStyleFlags flags);
+	void drawStringWrap(const char *str, byte *buffer, Common::Rect &clipRect, int x, int y, int pitch, int16 col, TextStyleFlags flags);
 
 	// GlyphRenderer_v7 interface
 	int draw2byte(byte *buffer, Common::Rect &clipRect, int x, int y, int pitch, int16 col, uint16 chr) override;
diff --git a/engines/scumm/smush/rebel/smush_player_ra2.cpp b/engines/scumm/smush/rebel/smush_player_ra2.cpp
index dabef54a36e..8369dacbd63 100644
--- a/engines/scumm/smush/rebel/smush_player_ra2.cpp
+++ b/engines/scumm/smush/rebel/smush_player_ra2.cpp
@@ -841,10 +841,10 @@ void SmushPlayerRebel2::ra2HandleTextResource(const char *str, int fontId, int c
 
 	if (flg & kStyleWordWrap) {
 		Common::Rect clipRect(MAX<int>(0, left), MAX<int>(0, top), MIN<int>(left + width, _width), MIN<int>(top + height, _height));
-		_multiFont->drawStringWrap(str, _dst, clipRect, pos_x, pos_y, color, flg);
+		_multiFont->drawStringWrap(str, _dst, clipRect, pos_x, pos_y, _width, color, flg);
 	} else {
 		Common::Rect clipRect(0, 0, _width, _height);
-		_multiFont->drawString(str, _dst, clipRect, pos_x, pos_y, color, flg);
+		_multiFont->drawString(str, _dst, clipRect, pos_x, pos_y, _width, color, flg);
 	}
 }
 


Commit: 73d44af9ba7286c489835f201ff3372972fcc21c
    https://github.com/scummvm/scummvm/commit/73d44af9ba7286c489835f201ff3372972fcc21c
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-11T15:11:53+02:00

Commit Message:
SCUMM RA2: fixed invalid palette on gameplay when high resolution is used

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 8369dacbd63..1dec1a12192 100644
--- a/engines/scumm/smush/rebel/smush_player_ra2.cpp
+++ b/engines/scumm/smush/rebel/smush_player_ra2.cpp
@@ -869,7 +869,18 @@ bool SmushPlayerRebel2::ra2SelectFrameBuffer(int codec, int width, int height) {
 	if (codec == SMUSH_CODEC_RA2_BOMP) {
 		const bool highRes = ra2IsHighResMode();
 		const bool gameplayActive = isRebel2GameplayActive(_insane);
-		if (highRes && (!gameplayActive || !_ra2UsingGameplaySurface || _specialBuffer == nullptr)) {
+		const int64 currentSurfaceSize64 = (int64)_width * _height;
+		if (highRes && gameplayActive &&
+				_dst == _specialBuffer && _specialBuffer != nullptr &&
+				_width > 320 && _height > 200 &&
+				currentSurfaceSize64 > 0 && currentSurfaceSize64 <= _specialBufferSize) {
+			if (_ra2NativeFrameNeedsClear) {
+				ra2ClearCurrentTarget();
+				_ra2NativeFrameNeedsClear = false;
+			}
+			debugC(DEBUG_SMUSH, "SmushPlayerRebel2::ra2SelectFrameBuffer: Using current _specialBuffer %dx%d for high-res codec 45 mask",
+				_width, _height);
+		} else if (highRes && (!gameplayActive || !_ra2UsingGameplaySurface || _specialBuffer == nullptr)) {
 			if (!ra2EnsureLowResVideoBuffer())
 				return false;
 			_dst = _ra2LowResVideoBuffer;
@@ -946,6 +957,21 @@ bool SmushPlayerRebel2::ra2SelectFrameBuffer(int codec, int width, int height) {
 		surfaceHeight = MAX(surfaceHeight, _ra2FrameObjectSurfaceHeight);
 	}
 
+	const int64 currentSurfaceSize64 = (int64)_width * _height;
+	if (highRes && gameplayActive && !useGameplaySurface && !oversizedNative &&
+			width > 0 && height > 0 &&
+			_dst == _specialBuffer && _specialBuffer != nullptr &&
+			_width > 320 && _height > 200 &&
+			currentSurfaceSize64 > 0 && currentSurfaceSize64 <= _specialBufferSize) {
+		if (_ra2NativeFrameNeedsClear) {
+			ra2ClearCurrentTarget();
+			_ra2NativeFrameNeedsClear = false;
+		}
+		debugC(DEBUG_SMUSH, "SmushPlayerRebel2::ra2SelectFrameBuffer: Using current _specialBuffer %dx%d for high-res gameplay FOBJ %dx%d",
+			_width, _height, width, height);
+		return true;
+	}
+
 	if (highRes && !useGameplaySurface && !oversizedNative) {
 		if (width <= 0 || height <= 0) {
 			debugC(DEBUG_SMUSH, "SmushPlayerRebel2::ra2SelectFrameBuffer: Skipping invalid FOBJ dimensions %dx%d", width, height);



From noreply at scummvm.org  Thu Jun 11 13:12:19 2026
From: noreply at scummvm.org (ScummVM-Translations)
Date: Thu, 11 Jun 2026 13:12:19 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 78ad07bb2b3652fa34bc061303aa5ae741ddb10e
Message-ID: <20260611131219.707A37005CF@ron.scummvm.net>

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

Summary:
78ad07bb2b I18N: Update translations templates


Commit: 78ad07bb2b3652fa34bc061303aa5ae741ddb10e
    https://github.com/scummvm/scummvm/commit/78ad07bb2b3652fa34bc061303aa5ae741ddb10e
Author: Weblate (noreply at weblate.org)
Date: 2026-06-11T13:12:15Z

Commit Message:
I18N: Update translations templates

Changed paths:
    po/scummvm.pot


diff --git a/po/scummvm.pot b/po/scummvm.pot
index 30005aee849..e5a39248204 100644
--- a/po/scummvm.pot
+++ b/po/scummvm.pot
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2026.2.1git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-09 13:22+0000\n"
+"POT-Creation-Date: 2026-06-11 13:12+0000\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL at ADDRESS>\n"
 "Language-Team: LANGUAGE <LL at li.org>\n"
@@ -329,7 +329,7 @@ msgstr ""
 #: engines/hypno/metaengine.cpp:192 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:197
 #: engines/mm/mm1/metaengine.cpp:54 engines/parallaction/metaengine.cpp:169
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
 #: engines/sherlock/metaengine.cpp:638 engines/sherlock/metaengine.cpp:1244
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:328
@@ -348,7 +348,7 @@ msgstr ""
 #: engines/hypno/metaengine.cpp:198 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:204
 #: engines/mm/mm1/metaengine.cpp:52 engines/parallaction/metaengine.cpp:175
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
 #: engines/sherlock/metaengine.cpp:643 engines/sherlock/metaengine.cpp:1250
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:329
@@ -376,7 +376,7 @@ msgstr ""
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:123
 #: engines/drascula/saveload.cpp:51 engines/grim/inputdialog.cpp:83
 #: engines/parallaction/saveload.cpp:270 engines/sci/engine/kgraphics.cpp:76
-#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4482
+#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4490
 #: engines/sword1/control.cpp:2816
 msgid "OK"
 msgstr ""
@@ -3162,7 +3162,7 @@ msgstr ""
 #: engines/mohawk/riven_stacks/aspit.cpp:454
 #: engines/neverhood/metaengine.cpp:200 engines/saga/metaengine.cpp:449
 #: engines/scumm/help.cpp:84 engines/scumm/help.cpp:86
-#: engines/scumm/scumm.cpp:4482 engines/sludge/keymapper_tables.h:95
+#: engines/scumm/scumm.cpp:4490 engines/sludge/keymapper_tables.h:95
 #: engines/sludge/keymapper_tables.h:144 engines/sludge/keymapper_tables.h:223
 #: engines/sludge/keymapper_tables.h:343 engines/sludge/keymapper_tables.h:355
 #: engines/sludge/keymapper_tables.h:416 engines/sludge/keymapper_tables.h:429
@@ -6334,7 +6334,7 @@ msgstr ""
 msgid "Quick load or save game isn't supported in this location"
 msgstr ""
 
-#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3421
+#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3429
 #, c-format
 msgid ""
 "Failed to load saved game from file:\n"
@@ -6342,7 +6342,7 @@ msgid ""
 "%s"
 msgstr ""
 
-#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3408
+#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3416
 #, c-format
 msgid ""
 "Failed to save game to file:\n"
@@ -6350,7 +6350,7 @@ msgid ""
 "%s"
 msgstr ""
 
-#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3441
+#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3449
 #, c-format
 msgid ""
 "Successfully saved game in file:\n"
@@ -11477,7 +11477,7 @@ msgid "Your Name:"
 msgstr ""
 
 #. I18N: Button, start hosting online multiplayer game
-#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4488
+#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4496
 msgid "Host"
 msgstr ""
 
@@ -11487,7 +11487,7 @@ msgid "Querying games..."
 msgstr ""
 
 #. I18N: Join online multiplayer game
-#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4488
+#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4496
 msgid "Join"
 msgstr ""
 
@@ -12594,14 +12594,14 @@ msgid ""
 "required resources for MT-32. Using AdLib instead."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:4364
+#: engines/scumm/scumm.cpp:4372
 msgid ""
 "Usually, Maniac Mansion would start now. But for that to work, the game "
 "files for Maniac Mansion have to be in the 'Maniac' directory inside the "
 "Tentacle game directory, and the game has to be added to ScummVM."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:4488
+#: engines/scumm/scumm.cpp:4496
 msgid "Would you like to host or join a network play session?"
 msgstr ""
 



From noreply at scummvm.org  Thu Jun 11 13:12:51 2026
From: noreply at scummvm.org (ScummVM-Translations)
Date: Thu, 11 Jun 2026 13:12:51 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 83e899a1589556f15f3fc259305444c30405150f
Message-ID: <20260611131251.8870E7005BC@ron.scummvm.net>

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

Summary:
83e899a158 I18N: Update translation files


Commit: 83e899a1589556f15f3fc259305444c30405150f
    https://github.com/scummvm/scummvm/commit/83e899a1589556f15f3fc259305444c30405150f
Author: Weblate (noreply at weblate.org)
Date: 2026-06-11T13:12:32Z

Commit Message:
I18N: Update translation files

Updated by "Update PO files to match POT (msgmerge)" hook in Weblate.

Translation: ScummVM/scummvm
Translate-URL: https://translations.scummvm.org/projects/scummvm/scummvm/

Changed paths:
    po/ar.po
    po/be-tarask.po
    po/be_BY.po
    po/ca_ES.po
    po/cs_CZ.po
    po/da.po
    po/de_DE.po
    po/el.po
    po/es_ES.po
    po/eu.po
    po/fi_FI.po
    po/fr_FR.po
    po/gl_ES.po
    po/he.po
    po/hi.po
    po/hu_HU.po
    po/it_IT.po
    po/ja.po
    po/ka.po
    po/ko.po
    po/nb_NO.po
    po/nl_NL.po
    po/nn_NO.po
    po/pl_PL.po
    po/pt_BR.po
    po/pt_PT.po
    po/ro.po
    po/ru_RU.po
    po/sv_SE.po
    po/tr.po
    po/uk_UA.po
    po/zh.po
    po/zh_Hans.po
    po/zh_Hant.po


diff --git a/po/ar.po b/po/ar.po
index 4d33b6f7fca..c9ff1bfdb20 100644
--- a/po/ar.po
+++ b/po/ar.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-09 13:22+0000\n"
+"POT-Creation-Date: 2026-06-11 13:12+0000\n"
 "PO-Revision-Date: 2026-02-24 22:26+0000\n"
 "Last-Translator: Mohamed Shaaban <mohmedbashamedo at hotmail.com>\n"
 "Language-Team: Arabic <https://translations.scummvm.org/projects/scummvm/"
@@ -354,7 +354,7 @@ msgstr "?????????? ?????? ???????? ????????. ???? ???????? ????????????????"
 #: engines/hypno/metaengine.cpp:192 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:197
 #: engines/mm/mm1/metaengine.cpp:54 engines/parallaction/metaengine.cpp:169
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
 #: engines/sherlock/metaengine.cpp:638 engines/sherlock/metaengine.cpp:1244
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:328
@@ -373,7 +373,7 @@ msgstr "??????"
 #: engines/hypno/metaengine.cpp:198 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:204
 #: engines/mm/mm1/metaengine.cpp:52 engines/parallaction/metaengine.cpp:175
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
 #: engines/sherlock/metaengine.cpp:643 engines/sherlock/metaengine.cpp:1250
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:329
@@ -403,7 +403,7 @@ msgstr ""
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:123
 #: engines/drascula/saveload.cpp:51 engines/grim/inputdialog.cpp:83
 #: engines/parallaction/saveload.cpp:270 engines/sci/engine/kgraphics.cpp:76
-#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4482
+#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4490
 #: engines/sword1/control.cpp:2816
 msgid "OK"
 msgstr "??????????"
@@ -3393,7 +3393,7 @@ msgstr ""
 #: engines/mohawk/riven_stacks/aspit.cpp:454
 #: engines/neverhood/metaengine.cpp:200 engines/saga/metaengine.cpp:449
 #: engines/scumm/help.cpp:84 engines/scumm/help.cpp:86
-#: engines/scumm/scumm.cpp:4482 engines/sludge/keymapper_tables.h:95
+#: engines/scumm/scumm.cpp:4490 engines/sludge/keymapper_tables.h:95
 #: engines/sludge/keymapper_tables.h:144 engines/sludge/keymapper_tables.h:223
 #: engines/sludge/keymapper_tables.h:343 engines/sludge/keymapper_tables.h:355
 #: engines/sludge/keymapper_tables.h:416 engines/sludge/keymapper_tables.h:429
@@ -6936,7 +6936,7 @@ msgstr ""
 msgid "Quick load or save game isn't supported in this location"
 msgstr "???? ?????? ?????? ?????????????? ???????????? ???? ?????? ???????????? ???? ?????? ????????????"
 
-#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3421
+#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3429
 #, c-format
 msgid ""
 "Failed to load saved game from file:\n"
@@ -6947,7 +6947,7 @@ msgstr ""
 "\n"
 "%s"
 
-#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3408
+#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3416
 #, c-format
 msgid ""
 "Failed to save game to file:\n"
@@ -6958,7 +6958,7 @@ msgstr ""
 "\n"
 "%s"
 
-#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3441
+#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3449
 #, c-format
 msgid ""
 "Successfully saved game in file:\n"
@@ -13184,7 +13184,7 @@ msgid "Your Name:"
 msgstr "??????????:"
 
 #. I18N: Button, start hosting online multiplayer game
-#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4488
+#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4496
 msgid "Host"
 msgstr "????????????"
 
@@ -13194,7 +13194,7 @@ msgid "Querying games..."
 msgstr "???????? ?????????????????? ???? ?????????????? ..."
 
 #. I18N: Join online multiplayer game
-#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4488
+#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4496
 msgid "Join"
 msgstr "????????"
 
@@ -14426,7 +14426,7 @@ msgstr ""
 "???? ?????????????? ???? ?????? ???????????? ???????????? ???? Monkey Island 1 ?????????? ?????????? ????????\n"
 "?????????????? ???????????????? ?? MT-32. ?????????????? AdLib ?????????? ???? ??????."
 
-#: engines/scumm/scumm.cpp:4364
+#: engines/scumm/scumm.cpp:4372
 msgid ""
 "Usually, Maniac Mansion would start now. But for that to work, the game "
 "files for Maniac Mansion have to be in the 'Maniac' directory inside the "
@@ -14436,7 +14436,7 @@ msgstr ""
 "???????????? ???????????? ?? Maniac Mansion ???? ???????? \"Maniac\" ???????? ???????? ???????? Tentacle ?? "
 "???????? ?????????? ???????????? ?????? ScummVM."
 
-#: engines/scumm/scumm.cpp:4488
+#: engines/scumm/scumm.cpp:4496
 msgid "Would you like to host or join a network play session?"
 msgstr "???? ???????? ???? ?????????????? ???? ???????????????? ?????? ???????? ?????? ?????? ??????????????"
 
diff --git a/po/be-tarask.po b/po/be-tarask.po
index 7c138d1c392..4601ee5b6f4 100644
--- a/po/be-tarask.po
+++ b/po/be-tarask.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-09 13:22+0000\n"
+"POT-Creation-Date: 2026-06-11 13:12+0000\n"
 "PO-Revision-Date: 2023-02-26 15:01+0000\n"
 "Last-Translator: Kastu?? K. <oakum2334 at gmail.com>\n"
 "Language-Team: Belarusian (Tara??kievica) <https://translations.scummvm.org/"
@@ -356,7 +356,7 @@ msgstr "?????????? ???????????????? ???????????? ??????????. ???? ???????????? 
 #: engines/hypno/metaengine.cpp:192 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:197
 #: engines/mm/mm1/metaengine.cpp:54 engines/parallaction/metaengine.cpp:169
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
 #: engines/sherlock/metaengine.cpp:638 engines/sherlock/metaengine.cpp:1244
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:328
@@ -375,7 +375,7 @@ msgstr "??????"
 #: engines/hypno/metaengine.cpp:198 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:204
 #: engines/mm/mm1/metaengine.cpp:52 engines/parallaction/metaengine.cpp:175
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
 #: engines/sherlock/metaengine.cpp:643 engines/sherlock/metaengine.cpp:1250
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:329
@@ -406,7 +406,7 @@ msgstr ""
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:123
 #: engines/drascula/saveload.cpp:51 engines/grim/inputdialog.cpp:83
 #: engines/parallaction/saveload.cpp:270 engines/sci/engine/kgraphics.cpp:76
-#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4482
+#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4490
 #: engines/sword1/control.cpp:2816
 msgid "OK"
 msgstr "????"
@@ -3360,7 +3360,7 @@ msgstr ""
 #: engines/mohawk/riven_stacks/aspit.cpp:454
 #: engines/neverhood/metaengine.cpp:200 engines/saga/metaengine.cpp:449
 #: engines/scumm/help.cpp:84 engines/scumm/help.cpp:86
-#: engines/scumm/scumm.cpp:4482 engines/sludge/keymapper_tables.h:95
+#: engines/scumm/scumm.cpp:4490 engines/sludge/keymapper_tables.h:95
 #: engines/sludge/keymapper_tables.h:144 engines/sludge/keymapper_tables.h:223
 #: engines/sludge/keymapper_tables.h:343 engines/sludge/keymapper_tables.h:355
 #: engines/sludge/keymapper_tables.h:416 engines/sludge/keymapper_tables.h:429
@@ -6872,7 +6872,7 @@ msgstr ""
 msgid "Quick load or save game isn't supported in this location"
 msgstr "???????????? ???????????????????????? ???? ???????????????????? ???? ?????????????????????????????? ?? ?????????? ??????????????"
 
-#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3421
+#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3429
 #, c-format
 msgid ""
 "Failed to load saved game from file:\n"
@@ -6883,7 +6883,7 @@ msgstr ""
 "\n"
 "%s"
 
-#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3408
+#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3416
 #, c-format
 msgid ""
 "Failed to save game to file:\n"
@@ -6894,7 +6894,7 @@ msgstr ""
 "\n"
 "%s"
 
-#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3441
+#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3449
 #, c-format
 msgid ""
 "Successfully saved game in file:\n"
@@ -13142,7 +13142,7 @@ msgid "Your Name:"
 msgstr "??????????:"
 
 #. I18N: Button, start hosting online multiplayer game
-#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4488
+#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4496
 msgid "Host"
 msgstr ""
 
@@ -13154,7 +13154,7 @@ msgid "Querying games..."
 msgstr "???????????????????? ???????????????"
 
 #. I18N: Join online multiplayer game
-#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4488
+#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4496
 msgid "Join"
 msgstr ""
 
@@ -14350,7 +14350,7 @@ msgid ""
 "required resources for MT-32. Using AdLib instead."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:4364
+#: engines/scumm/scumm.cpp:4372
 msgid ""
 "Usually, Maniac Mansion would start now. But for that to work, the game "
 "files for Maniac Mansion have to be in the 'Maniac' directory inside the "
@@ -14360,7 +14360,7 @@ msgstr ""
 "Maniac Mansion ???????????? ???????? ?? ?????????? 'Maniac' ???????????? ?????????? ???????????? Day of the "
 "Tentacle, ?? ???????????? ???????????? ???????? ?????????????? ?? ScummVM."
 
-#: engines/scumm/scumm.cpp:4488
+#: engines/scumm/scumm.cpp:4496
 #, fuzzy
 msgid "Would you like to host or join a network play session?"
 msgstr "???? ???????????? ???? ???????????????? ?????? ???????????????????? ?????????????"
diff --git a/po/be_BY.po b/po/be_BY.po
index 667bb8c73a7..fb8dc4e9b6f 100644
--- a/po/be_BY.po
+++ b/po/be_BY.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-09 13:22+0000\n"
+"POT-Creation-Date: 2026-06-11 13:12+0000\n"
 "PO-Revision-Date: 2021-03-21 08:24+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Belarusian <https://translations.scummvm.org/projects/scummvm/"
@@ -356,7 +356,7 @@ msgstr "?????????? ???????????? ?????????? ????????????????. ??????????????????
 #: engines/hypno/metaengine.cpp:192 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:197
 #: engines/mm/mm1/metaengine.cpp:54 engines/parallaction/metaengine.cpp:169
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
 #: engines/sherlock/metaengine.cpp:638 engines/sherlock/metaengine.cpp:1244
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:328
@@ -375,7 +375,7 @@ msgstr "??????"
 #: engines/hypno/metaengine.cpp:198 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:204
 #: engines/mm/mm1/metaengine.cpp:52 engines/parallaction/metaengine.cpp:175
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
 #: engines/sherlock/metaengine.cpp:643 engines/sherlock/metaengine.cpp:1250
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:329
@@ -405,7 +405,7 @@ msgstr ""
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:123
 #: engines/drascula/saveload.cpp:51 engines/grim/inputdialog.cpp:83
 #: engines/parallaction/saveload.cpp:270 engines/sci/engine/kgraphics.cpp:76
-#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4482
+#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4490
 #: engines/sword1/control.cpp:2816
 msgid "OK"
 msgstr "OK"
@@ -3378,7 +3378,7 @@ msgstr ""
 #: engines/mohawk/riven_stacks/aspit.cpp:454
 #: engines/neverhood/metaengine.cpp:200 engines/saga/metaengine.cpp:449
 #: engines/scumm/help.cpp:84 engines/scumm/help.cpp:86
-#: engines/scumm/scumm.cpp:4482 engines/sludge/keymapper_tables.h:95
+#: engines/scumm/scumm.cpp:4490 engines/sludge/keymapper_tables.h:95
 #: engines/sludge/keymapper_tables.h:144 engines/sludge/keymapper_tables.h:223
 #: engines/sludge/keymapper_tables.h:343 engines/sludge/keymapper_tables.h:355
 #: engines/sludge/keymapper_tables.h:416 engines/sludge/keymapper_tables.h:429
@@ -6855,7 +6855,7 @@ msgstr ""
 msgid "Quick load or save game isn't supported in this location"
 msgstr ""
 
-#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3421
+#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3429
 #, c-format
 msgid ""
 "Failed to load saved game from file:\n"
@@ -6866,7 +6866,7 @@ msgstr ""
 "\n"
 "%s"
 
-#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3408
+#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3416
 #, c-format
 msgid ""
 "Failed to save game to file:\n"
@@ -6877,7 +6877,7 @@ msgstr ""
 "\n"
 "%s"
 
-#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3441
+#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3449
 #, c-format
 msgid ""
 "Successfully saved game in file:\n"
@@ -12968,7 +12968,7 @@ msgid "Your Name:"
 msgstr "??????????:"
 
 #. I18N: Button, start hosting online multiplayer game
-#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4488
+#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4496
 msgid "Host"
 msgstr ""
 
@@ -12980,7 +12980,7 @@ msgid "Querying games..."
 msgstr "???????????????? ????????????..."
 
 #. I18N: Join online multiplayer game
-#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4488
+#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4496
 msgid "Join"
 msgstr ""
 
@@ -14151,7 +14151,7 @@ msgid ""
 "required resources for MT-32. Using AdLib instead."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:4364
+#: engines/scumm/scumm.cpp:4372
 msgid ""
 "Usually, Maniac Mansion would start now. But for that to work, the game "
 "files for Maniac Mansion have to be in the 'Maniac' directory inside the "
@@ -14162,7 +14162,7 @@ msgstr ""
 "???????????? ???????????????????? ???????????? Tentacle ?? ???????? ???????????? ?????????????? ???????? ?????????????????? ?? "
 "ScummVM."
 
-#: engines/scumm/scumm.cpp:4488
+#: engines/scumm/scumm.cpp:4496
 #, fuzzy
 msgid "Would you like to host or join a network play session?"
 msgstr "???? ???? ???????????? ???????????????? ?????? ?????????????????? ?????????????"
diff --git a/po/ca_ES.po b/po/ca_ES.po
index 098ad4b46e9..8441b3851e7 100644
--- a/po/ca_ES.po
+++ b/po/ca_ES.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.6.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-09 13:22+0000\n"
+"POT-Creation-Date: 2026-06-11 13:12+0000\n"
 "PO-Revision-Date: 2026-05-26 04:22+0000\n"
 "Last-Translator: Xaviu <xaviu at xaviu.com>\n"
 "Language-Team: Catalan <https://translations.scummvm.org/projects/scummvm/"
@@ -341,7 +341,7 @@ msgstr "Un altre Emmagatzematge est?? actiu ara mateix. Voleu interrompre'l?"
 #: engines/hypno/metaengine.cpp:192 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:197
 #: engines/mm/mm1/metaengine.cpp:54 engines/parallaction/metaengine.cpp:169
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
 #: engines/sherlock/metaengine.cpp:638 engines/sherlock/metaengine.cpp:1244
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:328
@@ -360,7 +360,7 @@ msgstr "S??"
 #: engines/hypno/metaengine.cpp:198 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:204
 #: engines/mm/mm1/metaengine.cpp:52 engines/parallaction/metaengine.cpp:175
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
 #: engines/sherlock/metaengine.cpp:643 engines/sherlock/metaengine.cpp:1250
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:329
@@ -388,7 +388,7 @@ msgstr "El contingut del codi JSON est?? mal format."
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:123
 #: engines/drascula/saveload.cpp:51 engines/grim/inputdialog.cpp:83
 #: engines/parallaction/saveload.cpp:270 engines/sci/engine/kgraphics.cpp:76
-#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4482
+#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4490
 #: engines/sword1/control.cpp:2816
 msgid "OK"
 msgstr "D'acord"
@@ -3294,7 +3294,7 @@ msgstr "Motor de jocs d'aventura gr??fica"
 #: engines/mohawk/riven_stacks/aspit.cpp:454
 #: engines/neverhood/metaengine.cpp:200 engines/saga/metaengine.cpp:449
 #: engines/scumm/help.cpp:84 engines/scumm/help.cpp:86
-#: engines/scumm/scumm.cpp:4482 engines/sludge/keymapper_tables.h:95
+#: engines/scumm/scumm.cpp:4490 engines/sludge/keymapper_tables.h:95
 #: engines/sludge/keymapper_tables.h:144 engines/sludge/keymapper_tables.h:223
 #: engines/sludge/keymapper_tables.h:343 engines/sludge/keymapper_tables.h:355
 #: engines/sludge/keymapper_tables.h:416 engines/sludge/keymapper_tables.h:429
@@ -6629,7 +6629,7 @@ msgstr ""
 "La c??rrega r??pida o desament de la partida no est?? s'admet en aquesta "
 "ubicaci??"
 
-#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3421
+#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3429
 #, c-format
 msgid ""
 "Failed to load saved game from file:\n"
@@ -6640,7 +6640,7 @@ msgstr ""
 "\n"
 "%s"
 
-#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3408
+#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3416
 #, c-format
 msgid ""
 "Failed to save game to file:\n"
@@ -6651,7 +6651,7 @@ msgstr ""
 "\n"
 "%s"
 
-#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3441
+#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3449
 #, c-format
 msgid ""
 "Successfully saved game in file:\n"
@@ -12065,7 +12065,7 @@ msgid "Your Name:"
 msgstr "El vostre nom:"
 
 #. I18N: Button, start hosting online multiplayer game
-#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4488
+#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4496
 msgid "Host"
 msgstr ""
 
@@ -12075,7 +12075,7 @@ msgid "Querying games..."
 msgstr "S'estan cercant partides..."
 
 #. I18N: Join online multiplayer game
-#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4488
+#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4496
 msgid "Join"
 msgstr ""
 
@@ -13215,7 +13215,7 @@ msgid ""
 "required resources for MT-32. Using AdLib instead."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:4364
+#: engines/scumm/scumm.cpp:4372
 msgid ""
 "Usually, Maniac Mansion would start now. But for that to work, the game "
 "files for Maniac Mansion have to be in the 'Maniac' directory inside the "
@@ -13226,7 +13226,7 @@ msgstr ""
 "'Maniac' dins del directori del joc Tentacle, i el joc s'ha d'afegir a "
 "ScummVM."
 
-#: engines/scumm/scumm.cpp:4488
+#: engines/scumm/scumm.cpp:4496
 msgid "Would you like to host or join a network play session?"
 msgstr "Voleu crear o unir-vos a una sessi?? de joc en xarxa?"
 
diff --git a/po/cs_CZ.po b/po/cs_CZ.po
index 5e1f37a8f40..95d0e162ff3 100644
--- a/po/cs_CZ.po
+++ b/po/cs_CZ.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.7.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-09 13:22+0000\n"
+"POT-Creation-Date: 2026-06-11 13:12+0000\n"
 "PO-Revision-Date: 2026-02-09 20:22+0000\n"
 "Last-Translator: AsciiWolf <mail at asciiwolf.com>\n"
 "Language-Team: Czech <https://translations.scummvm.org/projects/scummvm/"
@@ -357,7 +357,7 @@ msgstr "Jin?? ??lo??i??t?? pr??v?? pracuje. Chcete ho p??eru??it?"
 #: engines/hypno/metaengine.cpp:192 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:197
 #: engines/mm/mm1/metaengine.cpp:54 engines/parallaction/metaengine.cpp:169
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
 #: engines/sherlock/metaengine.cpp:638 engines/sherlock/metaengine.cpp:1244
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:328
@@ -376,7 +376,7 @@ msgstr "Ano"
 #: engines/hypno/metaengine.cpp:198 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:204
 #: engines/mm/mm1/metaengine.cpp:52 engines/parallaction/metaengine.cpp:175
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
 #: engines/sherlock/metaengine.cpp:643 engines/sherlock/metaengine.cpp:1250
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:329
@@ -408,7 +408,7 @@ msgstr ""
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:123
 #: engines/drascula/saveload.cpp:51 engines/grim/inputdialog.cpp:83
 #: engines/parallaction/saveload.cpp:270 engines/sci/engine/kgraphics.cpp:76
-#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4482
+#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4490
 #: engines/sword1/control.cpp:2816
 msgid "OK"
 msgstr "OK"
@@ -3359,7 +3359,7 @@ msgstr "Grafick?? engine pro hry ????nru adventur"
 #: engines/mohawk/riven_stacks/aspit.cpp:454
 #: engines/neverhood/metaengine.cpp:200 engines/saga/metaengine.cpp:449
 #: engines/scumm/help.cpp:84 engines/scumm/help.cpp:86
-#: engines/scumm/scumm.cpp:4482 engines/sludge/keymapper_tables.h:95
+#: engines/scumm/scumm.cpp:4490 engines/sludge/keymapper_tables.h:95
 #: engines/sludge/keymapper_tables.h:144 engines/sludge/keymapper_tables.h:223
 #: engines/sludge/keymapper_tables.h:343 engines/sludge/keymapper_tables.h:355
 #: engines/sludge/keymapper_tables.h:416 engines/sludge/keymapper_tables.h:429
@@ -6819,7 +6819,7 @@ msgstr ""
 msgid "Quick load or save game isn't supported in this location"
 msgstr ""
 
-#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3421
+#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3429
 #, c-format
 msgid ""
 "Failed to load saved game from file:\n"
@@ -6830,7 +6830,7 @@ msgstr ""
 "\n"
 "%s"
 
-#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3408
+#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3416
 #, c-format
 msgid ""
 "Failed to save game to file:\n"
@@ -6841,7 +6841,7 @@ msgstr ""
 "\n"
 "%s"
 
-#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3441
+#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3449
 #, c-format
 msgid ""
 "Successfully saved game in file:\n"
@@ -12778,7 +12778,7 @@ msgid "Your Name:"
 msgstr "Jm??no:"
 
 #. I18N: Button, start hosting online multiplayer game
-#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4488
+#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4496
 msgid "Host"
 msgstr ""
 
@@ -12790,7 +12790,7 @@ msgid "Querying games..."
 msgstr "Ukl??d??n?? hry..."
 
 #. I18N: Join online multiplayer game
-#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4488
+#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4496
 msgid "Join"
 msgstr ""
 
@@ -13955,7 +13955,7 @@ msgid ""
 "required resources for MT-32. Using AdLib instead."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:4364
+#: engines/scumm/scumm.cpp:4372
 msgid ""
 "Usually, Maniac Mansion would start now. But for that to work, the game "
 "files for Maniac Mansion have to be in the 'Maniac' directory inside the "
@@ -13965,7 +13965,7 @@ msgstr ""
 "mus?? b??t soubory se hrou um??st??ny do slo??ky 'Maniac' uvnit?? slo??ky se hrou "
 "Tentacle a hra mus?? b??t p??id??na do ScummVM."
 
-#: engines/scumm/scumm.cpp:4488
+#: engines/scumm/scumm.cpp:4496
 #, fuzzy
 msgid "Would you like to host or join a network play session?"
 msgstr "Chcete hru nahr??t nebo ulo??it?"
diff --git a/po/da.po b/po/da.po
index 6698d8f54c7..2fc8c40a15c 100644
--- a/po/da.po
+++ b/po/da.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-09 13:22+0000\n"
+"POT-Creation-Date: 2026-06-11 13:12+0000\n"
 "PO-Revision-Date: 2023-09-07 17:04+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Danish <https://translations.scummvm.org/projects/scummvm/"
@@ -358,7 +358,7 @@ msgstr "Et andet lager er aktivt nu. Vil du afbryde det?"
 #: engines/hypno/metaengine.cpp:192 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:197
 #: engines/mm/mm1/metaengine.cpp:54 engines/parallaction/metaengine.cpp:169
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
 #: engines/sherlock/metaengine.cpp:638 engines/sherlock/metaengine.cpp:1244
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:328
@@ -377,7 +377,7 @@ msgstr "Ja"
 #: engines/hypno/metaengine.cpp:198 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:204
 #: engines/mm/mm1/metaengine.cpp:52 engines/parallaction/metaengine.cpp:175
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
 #: engines/sherlock/metaengine.cpp:643 engines/sherlock/metaengine.cpp:1250
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:329
@@ -407,7 +407,7 @@ msgstr ""
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:123
 #: engines/drascula/saveload.cpp:51 engines/grim/inputdialog.cpp:83
 #: engines/parallaction/saveload.cpp:270 engines/sci/engine/kgraphics.cpp:76
-#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4482
+#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4490
 #: engines/sword1/control.cpp:2816
 msgid "OK"
 msgstr "OK"
@@ -3376,7 +3376,7 @@ msgstr ""
 #: engines/mohawk/riven_stacks/aspit.cpp:454
 #: engines/neverhood/metaengine.cpp:200 engines/saga/metaengine.cpp:449
 #: engines/scumm/help.cpp:84 engines/scumm/help.cpp:86
-#: engines/scumm/scumm.cpp:4482 engines/sludge/keymapper_tables.h:95
+#: engines/scumm/scumm.cpp:4490 engines/sludge/keymapper_tables.h:95
 #: engines/sludge/keymapper_tables.h:144 engines/sludge/keymapper_tables.h:223
 #: engines/sludge/keymapper_tables.h:343 engines/sludge/keymapper_tables.h:355
 #: engines/sludge/keymapper_tables.h:416 engines/sludge/keymapper_tables.h:429
@@ -6833,7 +6833,7 @@ msgstr ""
 msgid "Quick load or save game isn't supported in this location"
 msgstr ""
 
-#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3421
+#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3429
 #, c-format
 msgid ""
 "Failed to load saved game from file:\n"
@@ -6844,7 +6844,7 @@ msgstr ""
 "\n"
 "%s"
 
-#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3408
+#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3416
 #, c-format
 msgid ""
 "Failed to save game to file:\n"
@@ -6855,7 +6855,7 @@ msgstr ""
 "\n"
 "%s"
 
-#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3441
+#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3449
 #, c-format
 msgid ""
 "Successfully saved game in file:\n"
@@ -12869,7 +12869,7 @@ msgid "Your Name:"
 msgstr "Navn:"
 
 #. I18N: Button, start hosting online multiplayer game
-#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4488
+#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4496
 msgid "Host"
 msgstr ""
 
@@ -12881,7 +12881,7 @@ msgid "Querying games..."
 msgstr "Gemmer spil ..."
 
 #. I18N: Join online multiplayer game
-#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4488
+#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4496
 msgid "Join"
 msgstr ""
 
@@ -14046,7 +14046,7 @@ msgid ""
 "required resources for MT-32. Using AdLib instead."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:4364
+#: engines/scumm/scumm.cpp:4372
 msgid ""
 "Usually, Maniac Mansion would start now. But for that to work, the game "
 "files for Maniac Mansion have to be in the 'Maniac' directory inside the "
@@ -14056,7 +14056,7 @@ msgstr ""
 "spil filerne til Maniac Mansion skal v??re i \"Maniac\"-mappen inde i "
 "Tentacle-spilmappen, og spillet skal tilf??jes til ScummVM."
 
-#: engines/scumm/scumm.cpp:4488
+#: engines/scumm/scumm.cpp:4496
 #, fuzzy
 msgid "Would you like to host or join a network play session?"
 msgstr "Vil du indl??se eller gemme spillet?"
diff --git a/po/de_DE.po b/po/de_DE.po
index 73fb0ba9618..990686868b1 100644
--- a/po/de_DE.po
+++ b/po/de_DE.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.10.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-09 13:22+0000\n"
+"POT-Creation-Date: 2026-06-11 13:12+0000\n"
 "PO-Revision-Date: 2026-05-05 16:31+0000\n"
 "Last-Translator: Lothar Serra Mari <lothar.serramari at scummvm.org>\n"
 "Language-Team: German <https://translations.scummvm.org/projects/scummvm/"
@@ -343,7 +343,7 @@ msgstr ""
 #: engines/hypno/metaengine.cpp:192 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:197
 #: engines/mm/mm1/metaengine.cpp:54 engines/parallaction/metaengine.cpp:169
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
 #: engines/sherlock/metaengine.cpp:638 engines/sherlock/metaengine.cpp:1244
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:328
@@ -362,7 +362,7 @@ msgstr "Ja"
 #: engines/hypno/metaengine.cpp:198 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:204
 #: engines/mm/mm1/metaengine.cpp:52 engines/parallaction/metaengine.cpp:175
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
 #: engines/sherlock/metaengine.cpp:643 engines/sherlock/metaengine.cpp:1250
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:329
@@ -390,7 +390,7 @@ msgstr "JSON-Code ist fehlerhaft."
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:123
 #: engines/drascula/saveload.cpp:51 engines/grim/inputdialog.cpp:83
 #: engines/parallaction/saveload.cpp:270 engines/sci/engine/kgraphics.cpp:76
-#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4482
+#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4490
 #: engines/sword1/control.cpp:2816
 msgid "OK"
 msgstr "OK"
@@ -3426,7 +3426,7 @@ msgstr "Spiele-Engine f??r Grafik-Adventures"
 #: engines/mohawk/riven_stacks/aspit.cpp:454
 #: engines/neverhood/metaengine.cpp:200 engines/saga/metaengine.cpp:449
 #: engines/scumm/help.cpp:84 engines/scumm/help.cpp:86
-#: engines/scumm/scumm.cpp:4482 engines/sludge/keymapper_tables.h:95
+#: engines/scumm/scumm.cpp:4490 engines/sludge/keymapper_tables.h:95
 #: engines/sludge/keymapper_tables.h:144 engines/sludge/keymapper_tables.h:223
 #: engines/sludge/keymapper_tables.h:343 engines/sludge/keymapper_tables.h:355
 #: engines/sludge/keymapper_tables.h:416 engines/sludge/keymapper_tables.h:429
@@ -7101,7 +7101,7 @@ msgstr ""
 "Schnelles Laden oder Speichern eines Spiels wird an diesem Ort nicht "
 "unterst??tzt"
 
-#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3421
+#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3429
 #, c-format
 msgid ""
 "Failed to load saved game from file:\n"
@@ -7112,7 +7112,7 @@ msgstr ""
 "\n"
 "%s"
 
-#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3408
+#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3416
 #, c-format
 msgid ""
 "Failed to save game to file:\n"
@@ -7123,7 +7123,7 @@ msgstr ""
 "\n"
 "%s"
 
-#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3441
+#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3449
 #, c-format
 msgid ""
 "Successfully saved game in file:\n"
@@ -12720,7 +12720,7 @@ msgid "Your Name:"
 msgstr "Dein Name:"
 
 #. I18N: Button, start hosting online multiplayer game
-#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4488
+#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4496
 msgid "Host"
 msgstr "Hosten"
 
@@ -12730,7 +12730,7 @@ msgid "Querying games..."
 msgstr "Spiele werden abgefragt..."
 
 #. I18N: Join online multiplayer game
-#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4488
+#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4496
 msgid "Join"
 msgstr "Beitreten"
 
@@ -13985,7 +13985,7 @@ msgstr ""
 "Diese Version von Monkey Island 1 ist daf??r bekannt, dass einige\n"
 "f??r MT-32 ben??tigte Ressourcen fehlen. Stattdessen wird AdLib verwendet."
 
-#: engines/scumm/scumm.cpp:4364
+#: engines/scumm/scumm.cpp:4372
 msgid ""
 "Usually, Maniac Mansion would start now. But for that to work, the game "
 "files for Maniac Mansion have to be in the 'Maniac' directory inside the "
@@ -13996,7 +13996,7 @@ msgstr ""
 "im Startmen?? von ScummVM und w??hlen das Verzeichnis \"Maniac\" im "
 "Verzeichnis dieses Spiels aus."
 
-#: engines/scumm/scumm.cpp:4488
+#: engines/scumm/scumm.cpp:4496
 msgid "Would you like to host or join a network play session?"
 msgstr ""
 "M??chtest Du ein Spiel ??ber das Netzwerk hosten oder einem Spiel beitreten?"
diff --git a/po/el.po b/po/el.po
index d24ceba91ec..f7b5e6e22dc 100644
--- a/po/el.po
+++ b/po/el.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.10.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-09 13:22+0000\n"
+"POT-Creation-Date: 2026-06-11 13:12+0000\n"
 "PO-Revision-Date: 2026-04-23 00:22+0000\n"
 "Last-Translator: Antoniou Athanasios <a.antoniou79 at gmail.com>\n"
 "Language-Team: Greek <https://translations.scummvm.org/projects/scummvm/"
@@ -342,7 +342,7 @@ msgstr ""
 #: engines/hypno/metaengine.cpp:192 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:197
 #: engines/mm/mm1/metaengine.cpp:54 engines/parallaction/metaengine.cpp:169
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
 #: engines/sherlock/metaengine.cpp:638 engines/sherlock/metaengine.cpp:1244
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:328
@@ -361,7 +361,7 @@ msgstr "??????"
 #: engines/hypno/metaengine.cpp:198 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:204
 #: engines/mm/mm1/metaengine.cpp:52 engines/parallaction/metaengine.cpp:175
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
 #: engines/sherlock/metaengine.cpp:643 engines/sherlock/metaengine.cpp:1250
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:329
@@ -389,7 +389,7 @@ msgstr "???? ?????????????????????? ?????? ???????????? JSON ?????? ?????????? 
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:123
 #: engines/drascula/saveload.cpp:51 engines/grim/inputdialog.cpp:83
 #: engines/parallaction/saveload.cpp:270 engines/sci/engine/kgraphics.cpp:76
-#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4482
+#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4490
 #: engines/sword1/control.cpp:2816
 msgid "OK"
 msgstr "OK"
@@ -3481,7 +3481,7 @@ msgstr "???????????? ???????????????????? ??????????????????????"
 #: engines/mohawk/riven_stacks/aspit.cpp:454
 #: engines/neverhood/metaengine.cpp:200 engines/saga/metaengine.cpp:449
 #: engines/scumm/help.cpp:84 engines/scumm/help.cpp:86
-#: engines/scumm/scumm.cpp:4482 engines/sludge/keymapper_tables.h:95
+#: engines/scumm/scumm.cpp:4490 engines/sludge/keymapper_tables.h:95
 #: engines/sludge/keymapper_tables.h:144 engines/sludge/keymapper_tables.h:223
 #: engines/sludge/keymapper_tables.h:343 engines/sludge/keymapper_tables.h:355
 #: engines/sludge/keymapper_tables.h:416 engines/sludge/keymapper_tables.h:429
@@ -7225,7 +7225,7 @@ msgstr ""
 "?? ?????????????? ?????????????? ?? ???????????????????? ???????????????????? ?????? ?????????????????????????? ???? ???????? ?????? "
 "??????????????"
 
-#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3421
+#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3429
 #, c-format
 msgid ""
 "Failed to load saved game from file:\n"
@@ -7236,7 +7236,7 @@ msgstr ""
 "\n"
 "%s"
 
-#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3408
+#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3416
 #, c-format
 msgid ""
 "Failed to save game to file:\n"
@@ -7247,7 +7247,7 @@ msgstr ""
 "\n"
 "%s"
 
-#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3441
+#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3449
 #, c-format
 msgid ""
 "Successfully saved game in file:\n"
@@ -12863,7 +12863,7 @@ msgid "Your Name:"
 msgstr "???? ?????????? ??????:"
 
 #. I18N: Button, start hosting online multiplayer game
-#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4488
+#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4496
 msgid "Host"
 msgstr "??????????????????"
 
@@ -12873,7 +12873,7 @@ msgid "Querying games..."
 msgstr "?????????????????? ????????????????????..."
 
 #. I18N: Join online multiplayer game
-#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4488
+#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4496
 msgid "Join"
 msgstr "??????????????????"
 
@@ -14142,7 +14142,7 @@ msgstr ""
 "????????????????????\n"
 "???????????????? ?????? ?????? ???????????????????? ?????? MT-32. ??????' ?????????? ???? ?????????? ?????????? AdLib."
 
-#: engines/scumm/scumm.cpp:4364
+#: engines/scumm/scumm.cpp:4372
 msgid ""
 "Usually, Maniac Mansion would start now. But for that to work, the game "
 "files for Maniac Mansion have to be in the 'Maniac' directory inside the "
@@ -14153,7 +14153,7 @@ msgstr ""
 "???????????? 'Maniac' ???????? ?????? ???????????? ???????????????????? ?????? Tentacle, ?????? ???? ???????????????? "
 "???????????? ???? ?????????????????? ?????? ScummVM."
 
-#: engines/scumm/scumm.cpp:4488
+#: engines/scumm/scumm.cpp:4496
 msgid "Would you like to host or join a network play session?"
 msgstr "???????????? ???? ???????????????????????? ?? ???? ?????????????????????? ???? online ???????????????? ????????????????????;"
 
diff --git a/po/es_ES.po b/po/es_ES.po
index 8ca3e8162c6..c7b9a405b9a 100644
--- a/po/es_ES.po
+++ b/po/es_ES.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.4.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-09 13:22+0000\n"
+"POT-Creation-Date: 2026-06-11 13:12+0000\n"
 "PO-Revision-Date: 2026-06-10 11:58+0000\n"
 "Last-Translator: IlDucci <pajaroloco_2 at hotmail.com>\n"
 "Language-Team: Spanish <https://translations.scummvm.org/projects/scummvm/"
@@ -339,7 +339,7 @@ msgstr "Ya hay otro almacenamiento activo. ??Quieres interrumpirlo?"
 #: engines/hypno/metaengine.cpp:192 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:197
 #: engines/mm/mm1/metaengine.cpp:54 engines/parallaction/metaengine.cpp:169
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
 #: engines/sherlock/metaengine.cpp:638 engines/sherlock/metaengine.cpp:1244
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:328
@@ -358,7 +358,7 @@ msgstr "S??"
 #: engines/hypno/metaengine.cpp:198 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:204
 #: engines/mm/mm1/metaengine.cpp:52 engines/parallaction/metaengine.cpp:175
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
 #: engines/sherlock/metaengine.cpp:643 engines/sherlock/metaengine.cpp:1250
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:329
@@ -386,7 +386,7 @@ msgstr "Los contenidos del c??digo JSON no son v??lidos."
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:123
 #: engines/drascula/saveload.cpp:51 engines/grim/inputdialog.cpp:83
 #: engines/parallaction/saveload.cpp:270 engines/sci/engine/kgraphics.cpp:76
-#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4482
+#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4490
 #: engines/sword1/control.cpp:2816
 msgid "OK"
 msgstr "Aceptar"
@@ -3429,7 +3429,7 @@ msgstr "Motor de aventuras gr??ficas"
 #: engines/mohawk/riven_stacks/aspit.cpp:454
 #: engines/neverhood/metaengine.cpp:200 engines/saga/metaengine.cpp:449
 #: engines/scumm/help.cpp:84 engines/scumm/help.cpp:86
-#: engines/scumm/scumm.cpp:4482 engines/sludge/keymapper_tables.h:95
+#: engines/scumm/scumm.cpp:4490 engines/sludge/keymapper_tables.h:95
 #: engines/sludge/keymapper_tables.h:144 engines/sludge/keymapper_tables.h:223
 #: engines/sludge/keymapper_tables.h:343 engines/sludge/keymapper_tables.h:355
 #: engines/sludge/keymapper_tables.h:416 engines/sludge/keymapper_tables.h:429
@@ -7120,7 +7120,7 @@ msgstr ""
 msgid "Quick load or save game isn't supported in this location"
 msgstr "No se pueden usar el guardado o carga r??pidos en este lugar"
 
-#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3421
+#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3429
 #, c-format
 msgid ""
 "Failed to load saved game from file:\n"
@@ -7131,7 +7131,7 @@ msgstr ""
 "\n"
 "%s"
 
-#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3408
+#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3416
 #, c-format
 msgid ""
 "Failed to save game to file:\n"
@@ -7142,7 +7142,7 @@ msgstr ""
 "\n"
 "%s"
 
-#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3441
+#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3449
 #, c-format
 msgid ""
 "Successfully saved game in file:\n"
@@ -12695,7 +12695,7 @@ msgid "Your Name:"
 msgstr "Tu nombre:"
 
 #. I18N: Button, start hosting online multiplayer game
-#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4488
+#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4496
 msgid "Host"
 msgstr "Crear"
 
@@ -12705,7 +12705,7 @@ msgid "Querying games..."
 msgstr "Buscando partidas..."
 
 #. I18N: Join online multiplayer game
-#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4488
+#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4496
 msgid "Join"
 msgstr "Unirse"
 
@@ -13936,7 +13936,7 @@ msgstr ""
 "por no tener ciertos elementos necesarios para MT-32.\n"
 "Utilizando AdLib en su lugar."
 
-#: engines/scumm/scumm.cpp:4364
+#: engines/scumm/scumm.cpp:4372
 msgid ""
 "Usually, Maniac Mansion would start now. But for that to work, the game "
 "files for Maniac Mansion have to be in the 'Maniac' directory inside the "
@@ -13946,7 +13946,7 @@ msgstr ""
 "posible debes mover los archivos de Maniac Mansion al directorio ??Maniac??, "
 "dentro del directorio de DOTT, y a??adir el juego a ScummVM."
 
-#: engines/scumm/scumm.cpp:4488
+#: engines/scumm/scumm.cpp:4496
 msgid "Would you like to host or join a network play session?"
 msgstr "??Quieres crear o unirte a una sesi??n de juego en red?"
 
diff --git a/po/eu.po b/po/eu.po
index fff83223272..3e1d4bb4a87 100644
--- a/po/eu.po
+++ b/po/eu.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.5.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-09 13:22+0000\n"
+"POT-Creation-Date: 2026-06-11 13:12+0000\n"
 "PO-Revision-Date: 2024-02-05 18:42+0000\n"
 "Last-Translator: Josu Igoa <josuigoa at ni.eus>\n"
 "Language-Team: Basque <https://translations.scummvm.org/projects/scummvm/"
@@ -340,7 +340,7 @@ msgstr "Beste biltegiratzen bat dago orain martxan. Gelditu nahi al duzu?"
 #: engines/hypno/metaengine.cpp:192 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:197
 #: engines/mm/mm1/metaengine.cpp:54 engines/parallaction/metaengine.cpp:169
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
 #: engines/sherlock/metaengine.cpp:638 engines/sherlock/metaengine.cpp:1244
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:328
@@ -359,7 +359,7 @@ msgstr "Bai"
 #: engines/hypno/metaengine.cpp:198 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:204
 #: engines/mm/mm1/metaengine.cpp:52 engines/parallaction/metaengine.cpp:175
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
 #: engines/sherlock/metaengine.cpp:643 engines/sherlock/metaengine.cpp:1250
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:329
@@ -387,7 +387,7 @@ msgstr "JSON kodearen edukia ez dago ongi osatuta."
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:123
 #: engines/drascula/saveload.cpp:51 engines/grim/inputdialog.cpp:83
 #: engines/parallaction/saveload.cpp:270 engines/sci/engine/kgraphics.cpp:76
-#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4482
+#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4490
 #: engines/sword1/control.cpp:2816
 msgid "OK"
 msgstr "Ados"
@@ -3410,7 +3410,7 @@ msgstr "Abendura grafikoen motorea"
 #: engines/mohawk/riven_stacks/aspit.cpp:454
 #: engines/neverhood/metaengine.cpp:200 engines/saga/metaengine.cpp:449
 #: engines/scumm/help.cpp:84 engines/scumm/help.cpp:86
-#: engines/scumm/scumm.cpp:4482 engines/sludge/keymapper_tables.h:95
+#: engines/scumm/scumm.cpp:4490 engines/sludge/keymapper_tables.h:95
 #: engines/sludge/keymapper_tables.h:144 engines/sludge/keymapper_tables.h:223
 #: engines/sludge/keymapper_tables.h:343 engines/sludge/keymapper_tables.h:355
 #: engines/sludge/keymapper_tables.h:416 engines/sludge/keymapper_tables.h:429
@@ -6891,7 +6891,7 @@ msgstr ""
 msgid "Quick load or save game isn't supported in this location"
 msgstr ""
 
-#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3421
+#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3429
 #, fuzzy, c-format
 msgid ""
 "Failed to load saved game from file:\n"
@@ -6902,7 +6902,7 @@ msgstr ""
 "\n"
 "%s"
 
-#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3408
+#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3416
 #, fuzzy, c-format
 msgid ""
 "Failed to save game to file:\n"
@@ -6913,7 +6913,7 @@ msgstr ""
 "\n"
 "%s"
 
-#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3441
+#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3449
 #, fuzzy, c-format
 msgid ""
 "Successfully saved game in file:\n"
@@ -12836,7 +12836,7 @@ msgid "Your Name:"
 msgstr "Izena:"
 
 #. I18N: Button, start hosting online multiplayer game
-#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4488
+#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4496
 msgid "Host"
 msgstr ""
 
@@ -12848,7 +12848,7 @@ msgid "Querying games..."
 msgstr "Jokoa gordetzen..."
 
 #. I18N: Join online multiplayer game
-#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4488
+#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4496
 msgid "Join"
 msgstr ""
 
@@ -14013,7 +14013,7 @@ msgid ""
 "required resources for MT-32. Using AdLib instead."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:4364
+#: engines/scumm/scumm.cpp:4372
 msgid ""
 "Usually, Maniac Mansion would start now. But for that to work, the game "
 "files for Maniac Mansion have to be in the 'Maniac' directory inside the "
@@ -14023,7 +14023,7 @@ msgstr ""
 "fitxategiak 'Maniac' direktorio barruan egon behar dira, Tentacle jokoko "
 "direktorioan, eta jokoa ScummVM-ra gehitu behar da."
 
-#: engines/scumm/scumm.cpp:4488
+#: engines/scumm/scumm.cpp:4496
 #, fuzzy
 msgid "Would you like to host or join a network play session?"
 msgstr "Jokoa kargatu edo gorde nahi duzu?"
diff --git a/po/fi_FI.po b/po/fi_FI.po
index 37dff557f4f..6f295df95e8 100644
--- a/po/fi_FI.po
+++ b/po/fi_FI.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.6.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-09 13:22+0000\n"
+"POT-Creation-Date: 2026-06-11 13:12+0000\n"
 "PO-Revision-Date: 2026-02-07 15:22+0000\n"
 "Last-Translator: Timo Mikkolainen <tmikkola at gmail.com>\n"
 "Language-Team: Finnish <https://translations.scummvm.org/projects/scummvm/"
@@ -340,7 +340,7 @@ msgstr "Toinen tallennustila on aktiivinen. Haluatko keskeytt???? sen?"
 #: engines/hypno/metaengine.cpp:192 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:197
 #: engines/mm/mm1/metaengine.cpp:54 engines/parallaction/metaengine.cpp:169
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
 #: engines/sherlock/metaengine.cpp:638 engines/sherlock/metaengine.cpp:1244
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:328
@@ -359,7 +359,7 @@ msgstr "Kyll??"
 #: engines/hypno/metaengine.cpp:198 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:204
 #: engines/mm/mm1/metaengine.cpp:52 engines/parallaction/metaengine.cpp:175
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
 #: engines/sherlock/metaengine.cpp:643 engines/sherlock/metaengine.cpp:1250
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:329
@@ -387,7 +387,7 @@ msgstr "JSON-koodin sis??lt?? on ep??muodostunutta."
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:123
 #: engines/drascula/saveload.cpp:51 engines/grim/inputdialog.cpp:83
 #: engines/parallaction/saveload.cpp:270 engines/sci/engine/kgraphics.cpp:76
-#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4482
+#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4490
 #: engines/sword1/control.cpp:2816
 msgid "OK"
 msgstr "OK"
@@ -3453,7 +3453,7 @@ msgstr "Pelimoottori graafisille seikkailupeleille"
 #: engines/mohawk/riven_stacks/aspit.cpp:454
 #: engines/neverhood/metaengine.cpp:200 engines/saga/metaengine.cpp:449
 #: engines/scumm/help.cpp:84 engines/scumm/help.cpp:86
-#: engines/scumm/scumm.cpp:4482 engines/sludge/keymapper_tables.h:95
+#: engines/scumm/scumm.cpp:4490 engines/sludge/keymapper_tables.h:95
 #: engines/sludge/keymapper_tables.h:144 engines/sludge/keymapper_tables.h:223
 #: engines/sludge/keymapper_tables.h:343 engines/sludge/keymapper_tables.h:355
 #: engines/sludge/keymapper_tables.h:416 engines/sludge/keymapper_tables.h:429
@@ -7206,7 +7206,7 @@ msgstr ""
 msgid "Quick load or save game isn't supported in this location"
 msgstr "Pelin pikalataus tai tallennus ei ole tuettu t??ss?? sijainnissa"
 
-#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3421
+#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3429
 #, c-format
 msgid ""
 "Failed to load saved game from file:\n"
@@ -7217,7 +7217,7 @@ msgstr ""
 "\n"
 "%s"
 
-#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3408
+#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3416
 #, c-format
 msgid ""
 "Failed to save game to file:\n"
@@ -7228,7 +7228,7 @@ msgstr ""
 "\n"
 "%s"
 
-#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3441
+#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3449
 #, c-format
 msgid ""
 "Successfully saved game in file:\n"
@@ -12936,7 +12936,7 @@ msgid "Your Name:"
 msgstr "Sinun nimesi:"
 
 #. I18N: Button, start hosting online multiplayer game
-#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4488
+#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4496
 msgid "Host"
 msgstr "Is??nn??i"
 
@@ -12946,7 +12946,7 @@ msgid "Querying games..."
 msgstr "Etsit????n pelej??..."
 
 #. I18N: Join online multiplayer game
-#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4488
+#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4496
 msgid "Join"
 msgstr "Liity"
 
@@ -14173,7 +14173,7 @@ msgstr ""
 "vaadittuja k??yt??n mahdollistavia osasia MT-32:lle. Otetaan sen sijasta "
 "k??ytt????n AdLib."
 
-#: engines/scumm/scumm.cpp:4364
+#: engines/scumm/scumm.cpp:4372
 msgid ""
 "Usually, Maniac Mansion would start now. But for that to work, the game "
 "files for Maniac Mansion have to be in the 'Maniac' directory inside the "
@@ -14183,7 +14183,7 @@ msgstr ""
 "Maniac Mansionin pelitiedostojen pit???? l??yty?? \"Maniac\"-kansiosta Tentacle-"
 "pelikansion sis??lt??, ja sen pit???? my??s olla lis??tty pelin?? ScummVM:????n."
 
-#: engines/scumm/scumm.cpp:4488
+#: engines/scumm/scumm.cpp:4496
 msgid "Would you like to host or join a network play session?"
 msgstr "Haluatko is??nn??id?? vai liitty?? verkkopeliin?"
 
diff --git a/po/fr_FR.po b/po/fr_FR.po
index 888fa81f1db..a14a3fd61f4 100644
--- a/po/fr_FR.po
+++ b/po/fr_FR.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-09 13:22+0000\n"
+"POT-Creation-Date: 2026-06-11 13:12+0000\n"
 "PO-Revision-Date: 2026-06-08 16:22+0000\n"
 "Last-Translator: Purple T <ZEONK at hotmail.com>\n"
 "Language-Team: French <https://translations.scummvm.org/projects/scummvm/"
@@ -341,7 +341,7 @@ msgstr ""
 #: engines/hypno/metaengine.cpp:192 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:197
 #: engines/mm/mm1/metaengine.cpp:54 engines/parallaction/metaengine.cpp:169
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
 #: engines/sherlock/metaengine.cpp:638 engines/sherlock/metaengine.cpp:1244
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:328
@@ -360,7 +360,7 @@ msgstr "Oui"
 #: engines/hypno/metaengine.cpp:198 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:204
 #: engines/mm/mm1/metaengine.cpp:52 engines/parallaction/metaengine.cpp:175
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
 #: engines/sherlock/metaengine.cpp:643 engines/sherlock/metaengine.cpp:1250
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:329
@@ -388,7 +388,7 @@ msgstr "Les contenus du code JSON sont malform??s."
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:123
 #: engines/drascula/saveload.cpp:51 engines/grim/inputdialog.cpp:83
 #: engines/parallaction/saveload.cpp:270 engines/sci/engine/kgraphics.cpp:76
-#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4482
+#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4490
 #: engines/sword1/control.cpp:2816
 msgid "OK"
 msgstr "OK"
@@ -3444,7 +3444,7 @@ msgstr "Moteur de jeux d'aventure graphiques"
 #: engines/mohawk/riven_stacks/aspit.cpp:454
 #: engines/neverhood/metaengine.cpp:200 engines/saga/metaengine.cpp:449
 #: engines/scumm/help.cpp:84 engines/scumm/help.cpp:86
-#: engines/scumm/scumm.cpp:4482 engines/sludge/keymapper_tables.h:95
+#: engines/scumm/scumm.cpp:4490 engines/sludge/keymapper_tables.h:95
 #: engines/sludge/keymapper_tables.h:144 engines/sludge/keymapper_tables.h:223
 #: engines/sludge/keymapper_tables.h:343 engines/sludge/keymapper_tables.h:355
 #: engines/sludge/keymapper_tables.h:416 engines/sludge/keymapper_tables.h:429
@@ -7129,7 +7129,7 @@ msgstr ""
 msgid "Quick load or save game isn't supported in this location"
 msgstr "Le chargement ou sauvegarde rapide n'est pas possible ?? cet endroit"
 
-#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3421
+#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3429
 #, c-format
 msgid ""
 "Failed to load saved game from file:\n"
@@ -7140,7 +7140,7 @@ msgstr ""
 "\n"
 "%s"
 
-#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3408
+#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3416
 #, c-format
 msgid ""
 "Failed to save game to file:\n"
@@ -7151,7 +7151,7 @@ msgstr ""
 "\n"
 "%s"
 
-#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3441
+#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3449
 #, c-format
 msgid ""
 "Successfully saved game in file:\n"
@@ -12719,7 +12719,7 @@ msgid "Your Name:"
 msgstr "Votre nom :"
 
 #. I18N: Button, start hosting online multiplayer game
-#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4488
+#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4496
 msgid "Host"
 msgstr "H??berger"
 
@@ -12729,7 +12729,7 @@ msgid "Querying games..."
 msgstr "Recherche de parties..."
 
 #. I18N: Join online multiplayer game
-#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4488
+#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4496
 msgid "Join"
 msgstr "Rejoindre"
 
@@ -13959,7 +13959,7 @@ msgstr ""
 "Cette version de Monkey Island 1 n'a pas ??t?? produite avec toutes\n"
 "les ressources n??cessaires pour le MT-32. AdLib sera utilis??."
 
-#: engines/scumm/scumm.cpp:4364
+#: engines/scumm/scumm.cpp:4372
 msgid ""
 "Usually, Maniac Mansion would start now. But for that to work, the game "
 "files for Maniac Mansion have to be in the 'Maniac' directory inside the "
@@ -13970,7 +13970,7 @@ msgstr ""
 "r??pertoire 'Maniac' ?? l'int??rieur du r??pertoire du jeu Day of the Tentacle, "
 "et le jeu doit ??tre ajout?? ?? ScummVM."
 
-#: engines/scumm/scumm.cpp:4488
+#: engines/scumm/scumm.cpp:4496
 msgid "Would you like to host or join a network play session?"
 msgstr "Voulez-vous h??berger ou rejoindre une partie de jeu en r??seau ?"
 
diff --git a/po/gl_ES.po b/po/gl_ES.po
index 91dbdb71af5..fa39ed3dcb7 100644
--- a/po/gl_ES.po
+++ b/po/gl_ES.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-09 13:22+0000\n"
+"POT-Creation-Date: 2026-06-11 13:12+0000\n"
 "PO-Revision-Date: 2020-09-26 21:13+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Galician <https://translations.scummvm.org/projects/scummvm/"
@@ -351,7 +351,7 @@ msgstr "Hai outro almacenamento activo. Seguro que queres interrompelo?"
 #: engines/hypno/metaengine.cpp:192 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:197
 #: engines/mm/mm1/metaengine.cpp:54 engines/parallaction/metaengine.cpp:169
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
 #: engines/sherlock/metaengine.cpp:638 engines/sherlock/metaengine.cpp:1244
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:328
@@ -370,7 +370,7 @@ msgstr "Si"
 #: engines/hypno/metaengine.cpp:198 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:204
 #: engines/mm/mm1/metaengine.cpp:52 engines/parallaction/metaengine.cpp:175
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
 #: engines/sherlock/metaengine.cpp:643 engines/sherlock/metaengine.cpp:1250
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:329
@@ -400,7 +400,7 @@ msgstr ""
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:123
 #: engines/drascula/saveload.cpp:51 engines/grim/inputdialog.cpp:83
 #: engines/parallaction/saveload.cpp:270 engines/sci/engine/kgraphics.cpp:76
-#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4482
+#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4490
 #: engines/sword1/control.cpp:2816
 msgid "OK"
 msgstr "Aceptar"
@@ -3366,7 +3366,7 @@ msgstr ""
 #: engines/mohawk/riven_stacks/aspit.cpp:454
 #: engines/neverhood/metaengine.cpp:200 engines/saga/metaengine.cpp:449
 #: engines/scumm/help.cpp:84 engines/scumm/help.cpp:86
-#: engines/scumm/scumm.cpp:4482 engines/sludge/keymapper_tables.h:95
+#: engines/scumm/scumm.cpp:4490 engines/sludge/keymapper_tables.h:95
 #: engines/sludge/keymapper_tables.h:144 engines/sludge/keymapper_tables.h:223
 #: engines/sludge/keymapper_tables.h:343 engines/sludge/keymapper_tables.h:355
 #: engines/sludge/keymapper_tables.h:416 engines/sludge/keymapper_tables.h:429
@@ -6826,7 +6826,7 @@ msgstr ""
 msgid "Quick load or save game isn't supported in this location"
 msgstr ""
 
-#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3421
+#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3429
 #, c-format
 msgid ""
 "Failed to load saved game from file:\n"
@@ -6837,7 +6837,7 @@ msgstr ""
 "\n"
 "%s"
 
-#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3408
+#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3416
 #, c-format
 msgid ""
 "Failed to save game to file:\n"
@@ -6848,7 +6848,7 @@ msgstr ""
 "\n"
 "%s"
 
-#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3441
+#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3449
 #, c-format
 msgid ""
 "Successfully saved game in file:\n"
@@ -12792,7 +12792,7 @@ msgid "Your Name:"
 msgstr "Nome:"
 
 #. I18N: Button, start hosting online multiplayer game
-#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4488
+#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4496
 msgid "Host"
 msgstr ""
 
@@ -12804,7 +12804,7 @@ msgid "Querying games..."
 msgstr "Gardando..."
 
 #. I18N: Join online multiplayer game
-#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4488
+#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4496
 msgid "Join"
 msgstr ""
 
@@ -13969,7 +13969,7 @@ msgid ""
 "required resources for MT-32. Using AdLib instead."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:4364
+#: engines/scumm/scumm.cpp:4372
 msgid ""
 "Usually, Maniac Mansion would start now. But for that to work, the game "
 "files for Maniac Mansion have to be in the 'Maniac' directory inside the "
@@ -13979,7 +13979,7 @@ msgstr ""
 "que estar no directorio \"Maniac\" dentro do directorio \"Tentacle\". "
 "Ademais, c??mpre engadir o xogo a ScummVM."
 
-#: engines/scumm/scumm.cpp:4488
+#: engines/scumm/scumm.cpp:4496
 #, fuzzy
 msgid "Would you like to host or join a network play session?"
 msgstr "Queres cargar ou gardar a partida?"
diff --git a/po/he.po b/po/he.po
index e875efe1ab8..6cbfaa0621c 100644
--- a/po/he.po
+++ b/po/he.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.1.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-09 13:22+0000\n"
+"POT-Creation-Date: 2026-06-11 13:12+0000\n"
 "PO-Revision-Date: 2025-04-09 20:18+0000\n"
 "Last-Translator: Niv Baehr <bloop93 at gmail.com>\n"
 "Language-Team: Hebrew <https://translations.scummvm.org/projects/scummvm/"
@@ -341,7 +341,7 @@ msgstr "?????????? ?????? ???????? ??????. ?????? ???????????? ?????"
 #: engines/hypno/metaengine.cpp:192 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:197
 #: engines/mm/mm1/metaengine.cpp:54 engines/parallaction/metaengine.cpp:169
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
 #: engines/sherlock/metaengine.cpp:638 engines/sherlock/metaengine.cpp:1244
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:328
@@ -360,7 +360,7 @@ msgstr "????"
 #: engines/hypno/metaengine.cpp:198 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:204
 #: engines/mm/mm1/metaengine.cpp:52 engines/parallaction/metaengine.cpp:175
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
 #: engines/sherlock/metaengine.cpp:643 engines/sherlock/metaengine.cpp:1250
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:329
@@ -388,7 +388,7 @@ msgstr "???????? ?????? JSON ??????????."
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:123
 #: engines/drascula/saveload.cpp:51 engines/grim/inputdialog.cpp:83
 #: engines/parallaction/saveload.cpp:270 engines/sci/engine/kgraphics.cpp:76
-#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4482
+#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4490
 #: engines/sword1/control.cpp:2816
 msgid "OK"
 msgstr "??????????"
@@ -3305,7 +3305,7 @@ msgstr "???????? ???????? ???????????? ????????"
 #: engines/mohawk/riven_stacks/aspit.cpp:454
 #: engines/neverhood/metaengine.cpp:200 engines/saga/metaengine.cpp:449
 #: engines/scumm/help.cpp:84 engines/scumm/help.cpp:86
-#: engines/scumm/scumm.cpp:4482 engines/sludge/keymapper_tables.h:95
+#: engines/scumm/scumm.cpp:4490 engines/sludge/keymapper_tables.h:95
 #: engines/sludge/keymapper_tables.h:144 engines/sludge/keymapper_tables.h:223
 #: engines/sludge/keymapper_tables.h:343 engines/sludge/keymapper_tables.h:355
 #: engines/sludge/keymapper_tables.h:416 engines/sludge/keymapper_tables.h:429
@@ -6804,7 +6804,7 @@ msgstr ""
 msgid "Quick load or save game isn't supported in this location"
 msgstr "?????????? ?????????? ???? ?????????? ?????????? ???? ???????????? ???????????? ????"
 
-#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3421
+#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3429
 #, c-format
 msgid ""
 "Failed to load saved game from file:\n"
@@ -6815,7 +6815,7 @@ msgstr ""
 "\n"
 "%s"
 
-#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3408
+#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3416
 #, c-format
 msgid ""
 "Failed to save game to file:\n"
@@ -6826,7 +6826,7 @@ msgstr ""
 "\n"
 "%s"
 
-#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3441
+#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3449
 #, c-format
 msgid ""
 "Successfully saved game in file:\n"
@@ -12985,7 +12985,7 @@ msgid "Your Name:"
 msgstr "????????:"
 
 #. I18N: Button, start hosting online multiplayer game
-#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4488
+#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4496
 msgid "Host"
 msgstr "??????????"
 
@@ -12997,7 +12997,7 @@ msgid "Querying games..."
 msgstr "???????? ???????? ..."
 
 #. I18N: Join online multiplayer game
-#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4488
+#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4496
 msgid "Join"
 msgstr "??????????????"
 
@@ -14206,7 +14206,7 @@ msgstr ""
 "???????? ???? ???? ???? ???????????? 1 ?????????? ??????????\n"
 "???????????? ?????????? ?????????????? ???????? MT-32. ?????????????? ??-AdLib ??????????."
 
-#: engines/scumm/scumm.cpp:4364
+#: engines/scumm/scumm.cpp:4372
 msgid ""
 "Usually, Maniac Mansion would start now. But for that to work, the game "
 "files for Maniac Mansion have to be in the 'Maniac' directory inside the "
@@ -14216,7 +14216,7 @@ msgstr ""
 "Mansion ???????????? ?????????? ?????????????? 'Maniac' ???????? ???????????? ?????????? Tentacle, ?????? ???????????? "
 "???? ?????????? ??- ScummVM."
 
-#: engines/scumm/scumm.cpp:4488
+#: engines/scumm/scumm.cpp:4496
 #, fuzzy
 msgid "Would you like to host or join a network play session?"
 msgstr "?????? ?????????????? ?????????? ???? ?????????? ?????????"
diff --git a/po/hi.po b/po/hi.po
index ea94e7a273b..d0cf650bc1e 100644
--- a/po/hi.po
+++ b/po/hi.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-09 13:22+0000\n"
+"POT-Creation-Date: 2026-06-11 13:12+0000\n"
 "PO-Revision-Date: 2026-05-12 22:22+0000\n"
 "Last-Translator: KARTIK SAXENA <kartiksaxena2005 at gmail.com>\n"
 "Language-Team: Hindi <https://translations.scummvm.org/projects/scummvm/"
@@ -339,7 +339,7 @@ msgstr "?????? ???????????? ????????????????????? ????????? ????????? ?????? ???
 #: engines/hypno/metaengine.cpp:192 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:197
 #: engines/mm/mm1/metaengine.cpp:54 engines/parallaction/metaengine.cpp:169
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
 #: engines/sherlock/metaengine.cpp:638 engines/sherlock/metaengine.cpp:1244
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:328
@@ -358,7 +358,7 @@ msgstr "?????????"
 #: engines/hypno/metaengine.cpp:198 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:204
 #: engines/mm/mm1/metaengine.cpp:52 engines/parallaction/metaengine.cpp:175
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
 #: engines/sherlock/metaengine.cpp:643 engines/sherlock/metaengine.cpp:1250
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:329
@@ -386,7 +386,7 @@ msgstr "JSON ????????? ?????? ????????????????????? ???????????? ?????????"
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:123
 #: engines/drascula/saveload.cpp:51 engines/grim/inputdialog.cpp:83
 #: engines/parallaction/saveload.cpp:270 engines/sci/engine/kgraphics.cpp:76
-#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4482
+#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4490
 #: engines/sword1/control.cpp:2816
 msgid "OK"
 msgstr "????????? ??????"
@@ -3279,7 +3279,7 @@ msgstr ""
 #: engines/mohawk/riven_stacks/aspit.cpp:454
 #: engines/neverhood/metaengine.cpp:200 engines/saga/metaengine.cpp:449
 #: engines/scumm/help.cpp:84 engines/scumm/help.cpp:86
-#: engines/scumm/scumm.cpp:4482 engines/sludge/keymapper_tables.h:95
+#: engines/scumm/scumm.cpp:4490 engines/sludge/keymapper_tables.h:95
 #: engines/sludge/keymapper_tables.h:144 engines/sludge/keymapper_tables.h:223
 #: engines/sludge/keymapper_tables.h:343 engines/sludge/keymapper_tables.h:355
 #: engines/sludge/keymapper_tables.h:416 engines/sludge/keymapper_tables.h:429
@@ -6572,7 +6572,7 @@ msgstr ""
 msgid "Quick load or save game isn't supported in this location"
 msgstr ""
 
-#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3421
+#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3429
 #, c-format
 msgid ""
 "Failed to load saved game from file:\n"
@@ -6580,7 +6580,7 @@ msgid ""
 "%s"
 msgstr ""
 
-#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3408
+#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3416
 #, c-format
 msgid ""
 "Failed to save game to file:\n"
@@ -6588,7 +6588,7 @@ msgid ""
 "%s"
 msgstr ""
 
-#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3441
+#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3449
 #, c-format
 msgid ""
 "Successfully saved game in file:\n"
@@ -12286,7 +12286,7 @@ msgid "Your Name:"
 msgstr "???????????? ?????????:"
 
 #. I18N: Button, start hosting online multiplayer game
-#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4488
+#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4496
 msgid "Host"
 msgstr ""
 
@@ -12296,7 +12296,7 @@ msgid "Querying games..."
 msgstr ""
 
 #. I18N: Join online multiplayer game
-#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4488
+#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4496
 msgid "Join"
 msgstr ""
 
@@ -13444,14 +13444,14 @@ msgid ""
 "required resources for MT-32. Using AdLib instead."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:4364
+#: engines/scumm/scumm.cpp:4372
 msgid ""
 "Usually, Maniac Mansion would start now. But for that to work, the game "
 "files for Maniac Mansion have to be in the 'Maniac' directory inside the "
 "Tentacle game directory, and the game has to be added to ScummVM."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:4488
+#: engines/scumm/scumm.cpp:4496
 msgid "Would you like to host or join a network play session?"
 msgstr ""
 
diff --git a/po/hu_HU.po b/po/hu_HU.po
index fd2bb2a8e6e..c6ff7f3e0ec 100644
--- a/po/hu_HU.po
+++ b/po/hu_HU.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-09 13:22+0000\n"
+"POT-Creation-Date: 2026-06-11 13:12+0000\n"
 "PO-Revision-Date: 2025-03-07 00:18+0000\n"
 "Last-Translator: Hov??nszki Tam??s <gahex68379 at lofiey.com>\n"
 "Language-Team: Hungarian <https://translations.scummvm.org/projects/scummvm/"
@@ -343,7 +343,7 @@ msgstr ""
 #: engines/hypno/metaengine.cpp:192 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:197
 #: engines/mm/mm1/metaengine.cpp:54 engines/parallaction/metaengine.cpp:169
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
 #: engines/sherlock/metaengine.cpp:638 engines/sherlock/metaengine.cpp:1244
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:328
@@ -362,7 +362,7 @@ msgstr "Igen"
 #: engines/hypno/metaengine.cpp:198 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:204
 #: engines/mm/mm1/metaengine.cpp:52 engines/parallaction/metaengine.cpp:175
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
 #: engines/sherlock/metaengine.cpp:643 engines/sherlock/metaengine.cpp:1250
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:329
@@ -390,7 +390,7 @@ msgstr "A JSON k??d tartalma hib??s."
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:123
 #: engines/drascula/saveload.cpp:51 engines/grim/inputdialog.cpp:83
 #: engines/parallaction/saveload.cpp:270 engines/sci/engine/kgraphics.cpp:76
-#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4482
+#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4490
 #: engines/sword1/control.cpp:2816
 msgid "OK"
 msgstr "OK"
@@ -3457,7 +3457,7 @@ msgstr "Grafikus kalandj??t??k motor"
 #: engines/mohawk/riven_stacks/aspit.cpp:454
 #: engines/neverhood/metaengine.cpp:200 engines/saga/metaengine.cpp:449
 #: engines/scumm/help.cpp:84 engines/scumm/help.cpp:86
-#: engines/scumm/scumm.cpp:4482 engines/sludge/keymapper_tables.h:95
+#: engines/scumm/scumm.cpp:4490 engines/sludge/keymapper_tables.h:95
 #: engines/sludge/keymapper_tables.h:144 engines/sludge/keymapper_tables.h:223
 #: engines/sludge/keymapper_tables.h:343 engines/sludge/keymapper_tables.h:355
 #: engines/sludge/keymapper_tables.h:416 engines/sludge/keymapper_tables.h:429
@@ -7309,7 +7309,7 @@ msgstr ""
 msgid "Quick load or save game isn't supported in this location"
 msgstr "A j??t??k gyors t??lt??se vagy ment??se nem t??mogatott ezen a helyen"
 
-#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3421
+#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3429
 #, c-format
 msgid ""
 "Failed to load saved game from file:\n"
@@ -7320,7 +7320,7 @@ msgstr ""
 "\n"
 "%s"
 
-#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3408
+#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3416
 #, c-format
 msgid ""
 "Failed to save game to file:\n"
@@ -7331,7 +7331,7 @@ msgstr ""
 "\n"
 "%s"
 
-#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3441
+#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3449
 #, c-format
 msgid ""
 "Successfully saved game in file:\n"
@@ -13438,7 +13438,7 @@ msgid "Your Name:"
 msgstr "Az ??n Neve:"
 
 #. I18N: Button, start hosting online multiplayer game
-#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4488
+#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4496
 msgid "Host"
 msgstr "Gazda"
 
@@ -13448,7 +13448,7 @@ msgid "Querying games..."
 msgstr "J??t??kok lek??rdez??se..."
 
 #. I18N: Join online multiplayer game
-#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4488
+#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4496
 msgid "Join"
 msgstr "Csatlakoz??s"
 
@@ -14681,7 +14681,7 @@ msgstr ""
 "A Monkey Island 1 ezen verzi??ja arr??l ismeretes, hogy hi??nyzik n??h??ny\n"
 "sz??ks??ges er??forr??s az MT-32-h??z. Helyette AdLib ker??l haszn??latra."
 
-#: engines/scumm/scumm.cpp:4364
+#: engines/scumm/scumm.cpp:4372
 msgid ""
 "Usually, Maniac Mansion would start now. But for that to work, the game "
 "files for Maniac Mansion have to be in the 'Maniac' directory inside the "
@@ -14691,7 +14691,7 @@ msgstr ""
 "Maniac Mansion j??t??k f??jlokban benne kell lennie a 'Maniac' mapp??ban a "
 "Tentacle mapp??n bel??l, ??s a j??t??kot hozz?? kell adni a ScummVM ind??t??hoz."
 
-#: engines/scumm/scumm.cpp:4488
+#: engines/scumm/scumm.cpp:4496
 msgid "Would you like to host or join a network play session?"
 msgstr "Szeretne h??l??zati j??t??kmenetet hosztolni vagy csatlakozni?"
 
diff --git a/po/it_IT.po b/po/it_IT.po
index 3d38961282e..d84f5497f65 100644
--- a/po/it_IT.po
+++ b/po/it_IT.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-09 13:22+0000\n"
+"POT-Creation-Date: 2026-06-11 13:12+0000\n"
 "PO-Revision-Date: 2026-06-10 20:26+0000\n"
 "Last-Translator: Walter Agazzi <walter.agazzi at protonmail.com>\n"
 "Language-Team: Italian <https://translations.scummvm.org/projects/scummvm/"
@@ -340,7 +340,7 @@ msgstr "Un altro servizio di archiviazione ?? attivo. Vuoi interromperlo?"
 #: engines/hypno/metaengine.cpp:192 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:197
 #: engines/mm/mm1/metaengine.cpp:54 engines/parallaction/metaengine.cpp:169
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
 #: engines/sherlock/metaengine.cpp:638 engines/sherlock/metaengine.cpp:1244
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:328
@@ -359,7 +359,7 @@ msgstr "S??"
 #: engines/hypno/metaengine.cpp:198 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:204
 #: engines/mm/mm1/metaengine.cpp:52 engines/parallaction/metaengine.cpp:175
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
 #: engines/sherlock/metaengine.cpp:643 engines/sherlock/metaengine.cpp:1250
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:329
@@ -387,7 +387,7 @@ msgstr "Il codice JSON non ?? valido."
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:123
 #: engines/drascula/saveload.cpp:51 engines/grim/inputdialog.cpp:83
 #: engines/parallaction/saveload.cpp:270 engines/sci/engine/kgraphics.cpp:76
-#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4482
+#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4490
 #: engines/sword1/control.cpp:2816
 msgid "OK"
 msgstr "OK"
@@ -3418,7 +3418,7 @@ msgstr "Motore per avventure grafiche"
 #: engines/mohawk/riven_stacks/aspit.cpp:454
 #: engines/neverhood/metaengine.cpp:200 engines/saga/metaengine.cpp:449
 #: engines/scumm/help.cpp:84 engines/scumm/help.cpp:86
-#: engines/scumm/scumm.cpp:4482 engines/sludge/keymapper_tables.h:95
+#: engines/scumm/scumm.cpp:4490 engines/sludge/keymapper_tables.h:95
 #: engines/sludge/keymapper_tables.h:144 engines/sludge/keymapper_tables.h:223
 #: engines/sludge/keymapper_tables.h:343 engines/sludge/keymapper_tables.h:355
 #: engines/sludge/keymapper_tables.h:416 engines/sludge/keymapper_tables.h:429
@@ -7103,7 +7103,7 @@ msgid "Quick load or save game isn't supported in this location"
 msgstr ""
 "Il salvataggio/caricamento rapido non ?? disponibile in questa locazione"
 
-#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3421
+#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3429
 #, c-format
 msgid ""
 "Failed to load saved game from file:\n"
@@ -7114,7 +7114,7 @@ msgstr ""
 "\n"
 "%s"
 
-#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3408
+#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3416
 #, c-format
 msgid ""
 "Failed to save game to file:\n"
@@ -7125,7 +7125,7 @@ msgstr ""
 "\n"
 "%s"
 
-#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3441
+#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3449
 #, c-format
 msgid ""
 "Successfully saved game in file:\n"
@@ -12671,7 +12671,7 @@ msgid "Your Name:"
 msgstr "Tuo Nome:"
 
 #. I18N: Button, start hosting online multiplayer game
-#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4488
+#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4496
 msgid "Host"
 msgstr "Ospita"
 
@@ -12681,7 +12681,7 @@ msgid "Querying games..."
 msgstr "Ricerca partite..."
 
 #. I18N: Join online multiplayer game
-#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4488
+#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4496
 msgid "Join"
 msgstr "Unisciti"
 
@@ -13903,7 +13903,7 @@ msgstr ""
 "Questa specifica versione di Monkey Island 1 ?? priva di alcune\n"
 "risorse necessarie per il sonoro MT-32. Verr?? utilizzato AdLib."
 
-#: engines/scumm/scumm.cpp:4364
+#: engines/scumm/scumm.cpp:4372
 msgid ""
 "Usually, Maniac Mansion would start now. But for that to work, the game "
 "files for Maniac Mansion have to be in the 'Maniac' directory inside the "
@@ -13914,7 +13914,7 @@ msgstr ""
 "sottocartella \"MANIAC\" all'interno della cartella principale di Day Of The "
 "Tentacle. Il gioco deve inoltre essere aggiunto a ScummVM."
 
-#: engines/scumm/scumm.cpp:4488
+#: engines/scumm/scumm.cpp:4496
 msgid "Would you like to host or join a network play session?"
 msgstr "Desideri ospitare o collegarti ad una sessione di gioco in rete?"
 
diff --git a/po/ja.po b/po/ja.po
index 25d6f05e03c..10d68e213f2 100644
--- a/po/ja.po
+++ b/po/ja.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-09 13:22+0000\n"
+"POT-Creation-Date: 2026-06-11 13:12+0000\n"
 "PO-Revision-Date: 2026-06-04 07:22+0000\n"
 "Last-Translator: VAN-Gluon <van.gluonp at gmail.com>\n"
 "Language-Team: Japanese <https://translations.scummvm.org/projects/scummvm/"
@@ -339,7 +339,7 @@ msgstr "????????????????????????????????????????????????????????????????????????
 #: engines/hypno/metaengine.cpp:192 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:197
 #: engines/mm/mm1/metaengine.cpp:54 engines/parallaction/metaengine.cpp:169
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
 #: engines/sherlock/metaengine.cpp:638 engines/sherlock/metaengine.cpp:1244
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:328
@@ -358,7 +358,7 @@ msgstr "??????"
 #: engines/hypno/metaengine.cpp:198 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:204
 #: engines/mm/mm1/metaengine.cpp:52 engines/parallaction/metaengine.cpp:175
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
 #: engines/sherlock/metaengine.cpp:643 engines/sherlock/metaengine.cpp:1250
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:329
@@ -386,7 +386,7 @@ msgstr "JSON????????????????????????????????????"
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:123
 #: engines/drascula/saveload.cpp:51 engines/grim/inputdialog.cpp:83
 #: engines/parallaction/saveload.cpp:270 engines/sci/engine/kgraphics.cpp:76
-#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4482
+#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4490
 #: engines/sword1/control.cpp:2816
 msgid "OK"
 msgstr "OK"
@@ -3306,7 +3306,7 @@ msgstr "???????????????????????????????????????????????????"
 #: engines/mohawk/riven_stacks/aspit.cpp:454
 #: engines/neverhood/metaengine.cpp:200 engines/saga/metaengine.cpp:449
 #: engines/scumm/help.cpp:84 engines/scumm/help.cpp:86
-#: engines/scumm/scumm.cpp:4482 engines/sludge/keymapper_tables.h:95
+#: engines/scumm/scumm.cpp:4490 engines/sludge/keymapper_tables.h:95
 #: engines/sludge/keymapper_tables.h:144 engines/sludge/keymapper_tables.h:223
 #: engines/sludge/keymapper_tables.h:343 engines/sludge/keymapper_tables.h:355
 #: engines/sludge/keymapper_tables.h:416 engines/sludge/keymapper_tables.h:429
@@ -6943,7 +6943,7 @@ msgid "Quick load or save game isn't supported in this location"
 msgstr ""
 "????????????????????????????????????????????????????????????????????????????????????????????????????????????"
 
-#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3421
+#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3429
 #, c-format
 msgid ""
 "Failed to load saved game from file:\n"
@@ -6954,7 +6954,7 @@ msgstr ""
 "\n"
 "%s"
 
-#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3408
+#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3416
 #, c-format
 msgid ""
 "Failed to save game to file:\n"
@@ -6965,7 +6965,7 @@ msgstr ""
 "\n"
 "%s"
 
-#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3441
+#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3449
 #, c-format
 msgid ""
 "Successfully saved game in file:\n"
@@ -13058,7 +13058,7 @@ msgid "Your Name:"
 msgstr "??????????????????"
 
 #. I18N: Button, start hosting online multiplayer game
-#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4488
+#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4496
 msgid "Host"
 msgstr "?????????"
 
@@ -13068,7 +13068,7 @@ msgid "Querying games..."
 msgstr "?????????????????????..."
 
 #. I18N: Join online multiplayer game
-#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4488
+#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4496
 msgid "Join"
 msgstr "??????"
 
@@ -14290,7 +14290,7 @@ msgstr ""
 "??????Monkey Island 1?????????????????????????????????MT-32????????????\n"
 "???????????????????????????????????????AdLib?????????????????????"
 
-#: engines/scumm/scumm.cpp:4364
+#: engines/scumm/scumm.cpp:4372
 msgid ""
 "Usually, Maniac Mansion would start now. But for that to work, the game "
 "files for Maniac Mansion have to be in the 'Maniac' directory inside the "
@@ -14300,7 +14300,7 @@ msgstr ""
 "???????????????????????????Maniac????????????????????????????????????????????????????????????????????????"
 "ScummVM???????????????????????????????????????????????????"
 
-#: engines/scumm/scumm.cpp:4488
+#: engines/scumm/scumm.cpp:4496
 msgid "Would you like to host or join a network play session?"
 msgstr "???????????????????????????????????????????????????????"
 
diff --git a/po/ka.po b/po/ka.po
index f11777ff5d6..230e77fc5bc 100644
--- a/po/ka.po
+++ b/po/ka.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-09 13:22+0000\n"
+"POT-Creation-Date: 2026-06-11 13:12+0000\n"
 "PO-Revision-Date: 2026-06-05 10:22+0000\n"
 "Last-Translator: NorwayFun <temuri.doghonadze at gmail.com>\n"
 "Language-Team: Georgian <https://translations.scummvm.org/projects/scummvm/"
@@ -332,7 +332,7 @@ msgstr "???????????? ???????????? ?????????????????? ?????????????????????. ???
 #: engines/hypno/metaengine.cpp:192 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:197
 #: engines/mm/mm1/metaengine.cpp:54 engines/parallaction/metaengine.cpp:169
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
 #: engines/sherlock/metaengine.cpp:638 engines/sherlock/metaengine.cpp:1244
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:328
@@ -351,7 +351,7 @@ msgstr "????????????"
 #: engines/hypno/metaengine.cpp:198 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:204
 #: engines/mm/mm1/metaengine.cpp:52 engines/parallaction/metaengine.cpp:175
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
 #: engines/sherlock/metaengine.cpp:643 engines/sherlock/metaengine.cpp:1250
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:329
@@ -379,7 +379,7 @@ msgstr "JSON ??????????????? ?????????????????????????????? ??????????????????
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:123
 #: engines/drascula/saveload.cpp:51 engines/grim/inputdialog.cpp:83
 #: engines/parallaction/saveload.cpp:270 engines/sci/engine/kgraphics.cpp:76
-#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4482
+#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4490
 #: engines/sword1/control.cpp:2816
 msgid "OK"
 msgstr "????????????"
@@ -3175,7 +3175,7 @@ msgstr "????????????????????????????????????????????? ????????????????????? ???
 #: engines/mohawk/riven_stacks/aspit.cpp:454
 #: engines/neverhood/metaengine.cpp:200 engines/saga/metaengine.cpp:449
 #: engines/scumm/help.cpp:84 engines/scumm/help.cpp:86
-#: engines/scumm/scumm.cpp:4482 engines/sludge/keymapper_tables.h:95
+#: engines/scumm/scumm.cpp:4490 engines/sludge/keymapper_tables.h:95
 #: engines/sludge/keymapper_tables.h:144 engines/sludge/keymapper_tables.h:223
 #: engines/sludge/keymapper_tables.h:343 engines/sludge/keymapper_tables.h:355
 #: engines/sludge/keymapper_tables.h:416 engines/sludge/keymapper_tables.h:429
@@ -6362,7 +6362,7 @@ msgstr ""
 msgid "Quick load or save game isn't supported in this location"
 msgstr ""
 
-#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3421
+#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3429
 #, c-format
 msgid ""
 "Failed to load saved game from file:\n"
@@ -6370,7 +6370,7 @@ msgid ""
 "%s"
 msgstr ""
 
-#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3408
+#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3416
 #, c-format
 msgid ""
 "Failed to save game to file:\n"
@@ -6381,7 +6381,7 @@ msgstr ""
 "\n"
 "%s"
 
-#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3441
+#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3449
 #, c-format
 msgid ""
 "Successfully saved game in file:\n"
@@ -11511,7 +11511,7 @@ msgid "Your Name:"
 msgstr "?????????????????? ??????????????????:"
 
 #. I18N: Button, start hosting online multiplayer game
-#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4488
+#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4496
 msgid "Host"
 msgstr "1"
 
@@ -11521,7 +11521,7 @@ msgid "Querying games..."
 msgstr "??????????????????????????? ???????????????????????????..."
 
 #. I18N: Join online multiplayer game
-#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4488
+#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4496
 msgid "Join"
 msgstr "????????????????????????"
 
@@ -12642,14 +12642,14 @@ msgid ""
 "required resources for MT-32. Using AdLib instead."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:4364
+#: engines/scumm/scumm.cpp:4372
 msgid ""
 "Usually, Maniac Mansion would start now. But for that to work, the game "
 "files for Maniac Mansion have to be in the 'Maniac' directory inside the "
 "Tentacle game directory, and the game has to be added to ScummVM."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:4488
+#: engines/scumm/scumm.cpp:4496
 msgid "Would you like to host or join a network play session?"
 msgstr ""
 
diff --git a/po/ko.po b/po/ko.po
index d24fc890174..12b4002b0c3 100644
--- a/po/ko.po
+++ b/po/ko.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-09 13:22+0000\n"
+"POT-Creation-Date: 2026-06-11 13:12+0000\n"
 "PO-Revision-Date: 2026-05-19 20:22+0000\n"
 "Last-Translator: Hoseok Seo <ddinghoya at gmail.com>\n"
 "Language-Team: Korean <https://translations.scummvm.org/projects/scummvm/"
@@ -339,7 +339,7 @@ msgstr "?????? ???????????? ?????? ?????? ????????????. ????????????????"
 #: engines/hypno/metaengine.cpp:192 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:197
 #: engines/mm/mm1/metaengine.cpp:54 engines/parallaction/metaengine.cpp:169
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
 #: engines/sherlock/metaengine.cpp:638 engines/sherlock/metaengine.cpp:1244
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:328
@@ -358,7 +358,7 @@ msgstr "???"
 #: engines/hypno/metaengine.cpp:198 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:204
 #: engines/mm/mm1/metaengine.cpp:52 engines/parallaction/metaengine.cpp:175
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
 #: engines/sherlock/metaengine.cpp:643 engines/sherlock/metaengine.cpp:1250
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:329
@@ -386,7 +386,7 @@ msgstr "JSON ?????? ????????? ?????????????????????."
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:123
 #: engines/drascula/saveload.cpp:51 engines/grim/inputdialog.cpp:83
 #: engines/parallaction/saveload.cpp:270 engines/sci/engine/kgraphics.cpp:76
-#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4482
+#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4490
 #: engines/sword1/control.cpp:2816
 msgid "OK"
 msgstr "??????"
@@ -3352,7 +3352,7 @@ msgstr "????????? ?????? ?????? ??????"
 #: engines/mohawk/riven_stacks/aspit.cpp:454
 #: engines/neverhood/metaengine.cpp:200 engines/saga/metaengine.cpp:449
 #: engines/scumm/help.cpp:84 engines/scumm/help.cpp:86
-#: engines/scumm/scumm.cpp:4482 engines/sludge/keymapper_tables.h:95
+#: engines/scumm/scumm.cpp:4490 engines/sludge/keymapper_tables.h:95
 #: engines/sludge/keymapper_tables.h:144 engines/sludge/keymapper_tables.h:223
 #: engines/sludge/keymapper_tables.h:343 engines/sludge/keymapper_tables.h:355
 #: engines/sludge/keymapper_tables.h:416 engines/sludge/keymapper_tables.h:429
@@ -6948,7 +6948,7 @@ msgstr ""
 msgid "Quick load or save game isn't supported in this location"
 msgstr "??? ??????????????? ?????? ?????? ???????????? ?????? ????????? ???????????? ??????"
 
-#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3421
+#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3429
 #, c-format
 msgid ""
 "Failed to load saved game from file:\n"
@@ -6959,7 +6959,7 @@ msgstr ""
 "\n"
 "%s"
 
-#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3408
+#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3416
 #, c-format
 msgid ""
 "Failed to save game to file:\n"
@@ -6970,7 +6970,7 @@ msgstr ""
 "\n"
 "%s"
 
-#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3441
+#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3449
 #, c-format
 msgid ""
 "Successfully saved game in file:\n"
@@ -12378,7 +12378,7 @@ msgid "Your Name:"
 msgstr "????????? ??????:"
 
 #. I18N: Button, start hosting online multiplayer game
-#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4488
+#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4496
 msgid "Host"
 msgstr "?????????"
 
@@ -12388,7 +12388,7 @@ msgid "Querying games..."
 msgstr "?????? ?????? ???..."
 
 #. I18N: Join online multiplayer game
-#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4488
+#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4496
 msgid "Join"
 msgstr "??????"
 
@@ -13601,7 +13601,7 @@ msgstr ""
 "??? ?????? ????????? ????????? ??? 1??? MT-32??? ????????? ???????????? ??????\n"
 "?????? ????????? ????????? ????????????. ?????? ???????????? ???????????????."
 
-#: engines/scumm/scumm.cpp:4364
+#: engines/scumm/scumm.cpp:4372
 msgid ""
 "Usually, Maniac Mansion would start now. But for that to work, the game "
 "files for Maniac Mansion have to be in the 'Maniac' directory inside the "
@@ -13611,7 +13611,7 @@ msgstr ""
 "?????? ????????? Tentacle ?????? ???????????? ?????? 'Maniac' ??????????????? ????????? ??????, ??????"
 "??? ScummVM??? ???????????? ?????????."
 
-#: engines/scumm/scumm.cpp:4488
+#: engines/scumm/scumm.cpp:4496
 msgid "Would you like to host or join a network play session?"
 msgstr "???????????? ????????? ????????? ?????????????????? ???????????? ?????????????"
 
diff --git a/po/nb_NO.po b/po/nb_NO.po
index 9ef1b065548..41d8ef8aadc 100644
--- a/po/nb_NO.po
+++ b/po/nb_NO.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-09 13:22+0000\n"
+"POT-Creation-Date: 2026-06-11 13:12+0000\n"
 "PO-Revision-Date: 2026-02-07 15:22+0000\n"
 "Last-Translator: Einar Johan Tr??an S??m??en <einarjohants at gmail.com>\n"
 "Language-Team: Norwegian Bokm??l <https://translations.scummvm.org/projects/"
@@ -341,7 +341,7 @@ msgstr "Et annet fillager er aktivt. ??nsker du ?? avbryte det?"
 #: engines/hypno/metaengine.cpp:192 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:197
 #: engines/mm/mm1/metaengine.cpp:54 engines/parallaction/metaengine.cpp:169
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
 #: engines/sherlock/metaengine.cpp:638 engines/sherlock/metaengine.cpp:1244
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:328
@@ -360,7 +360,7 @@ msgstr "Ja"
 #: engines/hypno/metaengine.cpp:198 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:204
 #: engines/mm/mm1/metaengine.cpp:52 engines/parallaction/metaengine.cpp:175
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
 #: engines/sherlock/metaengine.cpp:643 engines/sherlock/metaengine.cpp:1250
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:329
@@ -388,7 +388,7 @@ msgstr "Innholdet i JSON-koden er feilutformet."
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:123
 #: engines/drascula/saveload.cpp:51 engines/grim/inputdialog.cpp:83
 #: engines/parallaction/saveload.cpp:270 engines/sci/engine/kgraphics.cpp:76
-#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4482
+#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4490
 #: engines/sword1/control.cpp:2816
 msgid "OK"
 msgstr "OK"
@@ -3461,7 +3461,7 @@ msgstr "Spillmotor for grafikkbaserte eventyr"
 #: engines/mohawk/riven_stacks/aspit.cpp:454
 #: engines/neverhood/metaengine.cpp:200 engines/saga/metaengine.cpp:449
 #: engines/scumm/help.cpp:84 engines/scumm/help.cpp:86
-#: engines/scumm/scumm.cpp:4482 engines/sludge/keymapper_tables.h:95
+#: engines/scumm/scumm.cpp:4490 engines/sludge/keymapper_tables.h:95
 #: engines/sludge/keymapper_tables.h:144 engines/sludge/keymapper_tables.h:223
 #: engines/sludge/keymapper_tables.h:343 engines/sludge/keymapper_tables.h:355
 #: engines/sludge/keymapper_tables.h:416 engines/sludge/keymapper_tables.h:429
@@ -7370,7 +7370,7 @@ msgstr ""
 msgid "Quick load or save game isn't supported in this location"
 msgstr "Hurtiglasting eller -lagring av spill st??ttes ikke her"
 
-#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3421
+#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3429
 #, c-format
 msgid ""
 "Failed to load saved game from file:\n"
@@ -7381,7 +7381,7 @@ msgstr ""
 "\n"
 "%s"
 
-#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3408
+#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3416
 #, c-format
 msgid ""
 "Failed to save game to file:\n"
@@ -7392,7 +7392,7 @@ msgstr ""
 "\n"
 "%s"
 
-#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3441
+#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3449
 #, c-format
 msgid ""
 "Successfully saved game in file:\n"
@@ -13523,7 +13523,7 @@ msgid "Your Name:"
 msgstr "Ditt navn:"
 
 #. I18N: Button, start hosting online multiplayer game
-#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4488
+#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4496
 msgid "Host"
 msgstr "Vert"
 
@@ -13533,7 +13533,7 @@ msgid "Querying games..."
 msgstr "Sjekker spill ..."
 
 #. I18N: Join online multiplayer game
-#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4488
+#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4496
 msgid "Join"
 msgstr "Bli med"
 
@@ -14766,7 +14766,7 @@ msgstr ""
 "Denne spesifikke utgaven av Monkey Island 1 mangler noen av\n"
 "de p??krevde ressursene for ?? st??tte MT-32. Bruker AdLib i stedet."
 
-#: engines/scumm/scumm.cpp:4364
+#: engines/scumm/scumm.cpp:4372
 msgid ""
 "Usually, Maniac Mansion would start now. But for that to work, the game "
 "files for Maniac Mansion have to be in the 'Maniac' directory inside the "
@@ -14776,7 +14776,7 @@ msgstr ""
 "m?? Maniac Mansion-filene ligge i ??Maniac?? mappa inni Tentacle-spillmappa, og "
 "spillet m?? v??re lagt til i ScummVM."
 
-#: engines/scumm/scumm.cpp:4488
+#: engines/scumm/scumm.cpp:4496
 msgid "Would you like to host or join a network play session?"
 msgstr "??nsker du ?? v??re vert for eller bli med i en nettverksbasert spill??kt?"
 
diff --git a/po/nl_NL.po b/po/nl_NL.po
index 7096b3f750b..74592e5f266 100644
--- a/po/nl_NL.po
+++ b/po/nl_NL.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.9.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-09 13:22+0000\n"
+"POT-Creation-Date: 2026-06-11 13:12+0000\n"
 "PO-Revision-Date: 2026-02-11 14:23+0000\n"
 "Last-Translator: SecularSteve <fairfull.playing at gmail.com>\n"
 "Language-Team: Dutch <https://translations.scummvm.org/projects/scummvm/"
@@ -342,7 +342,7 @@ msgstr "Een andere opslag is actief. Wilt u die onderbreken?"
 #: engines/hypno/metaengine.cpp:192 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:197
 #: engines/mm/mm1/metaengine.cpp:54 engines/parallaction/metaengine.cpp:169
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
 #: engines/sherlock/metaengine.cpp:638 engines/sherlock/metaengine.cpp:1244
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:328
@@ -361,7 +361,7 @@ msgstr "Ja"
 #: engines/hypno/metaengine.cpp:198 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:204
 #: engines/mm/mm1/metaengine.cpp:52 engines/parallaction/metaengine.cpp:175
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
 #: engines/sherlock/metaengine.cpp:643 engines/sherlock/metaengine.cpp:1250
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:329
@@ -389,7 +389,7 @@ msgstr "JSON code heeft niet het juiste formaat."
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:123
 #: engines/drascula/saveload.cpp:51 engines/grim/inputdialog.cpp:83
 #: engines/parallaction/saveload.cpp:270 engines/sci/engine/kgraphics.cpp:76
-#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4482
+#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4490
 #: engines/sword1/control.cpp:2816
 msgid "OK"
 msgstr "OK"
@@ -3393,7 +3393,7 @@ msgstr "Grafische avonturen spel engine"
 #: engines/mohawk/riven_stacks/aspit.cpp:454
 #: engines/neverhood/metaengine.cpp:200 engines/saga/metaengine.cpp:449
 #: engines/scumm/help.cpp:84 engines/scumm/help.cpp:86
-#: engines/scumm/scumm.cpp:4482 engines/sludge/keymapper_tables.h:95
+#: engines/scumm/scumm.cpp:4490 engines/sludge/keymapper_tables.h:95
 #: engines/sludge/keymapper_tables.h:144 engines/sludge/keymapper_tables.h:223
 #: engines/sludge/keymapper_tables.h:343 engines/sludge/keymapper_tables.h:355
 #: engines/sludge/keymapper_tables.h:416 engines/sludge/keymapper_tables.h:429
@@ -7037,7 +7037,7 @@ msgstr ""
 msgid "Quick load or save game isn't supported in this location"
 msgstr "Snel laden of bewaren wordt niet ondersteund op deze locatie"
 
-#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3421
+#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3429
 #, c-format
 msgid ""
 "Failed to load saved game from file:\n"
@@ -7048,7 +7048,7 @@ msgstr ""
 "\n"
 "%s"
 
-#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3408
+#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3416
 #, c-format
 msgid ""
 "Failed to save game to file:\n"
@@ -7059,7 +7059,7 @@ msgstr ""
 "\n"
 "%s"
 
-#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3441
+#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3449
 #, c-format
 msgid ""
 "Successfully saved game in file:\n"
@@ -12717,7 +12717,7 @@ msgid "Your Name:"
 msgstr "Uw naam:"
 
 #. I18N: Button, start hosting online multiplayer game
-#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4488
+#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4496
 msgid "Host"
 msgstr "Host"
 
@@ -12727,7 +12727,7 @@ msgid "Querying games..."
 msgstr "Opvragen spellen..."
 
 #. I18N: Join online multiplayer game
-#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4488
+#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4496
 msgid "Join"
 msgstr "Meedoen"
 
@@ -13975,7 +13975,7 @@ msgstr ""
 "vereiste bronnen voor MT-32 ontbreken. In plaats daarvan wordt AdLib "
 "gebruikt."
 
-#: engines/scumm/scumm.cpp:4364
+#: engines/scumm/scumm.cpp:4372
 msgid ""
 "Usually, Maniac Mansion would start now. But for that to work, the game "
 "files for Maniac Mansion have to be in the 'Maniac' directory inside the "
@@ -13985,7 +13985,7 @@ msgstr ""
 "moeten de Maniac Mansion bestanden in de 'Maniac' map in de Tentacle spelmap "
 "staan, en het spel moet worden toegevoegd aan ScummVM."
 
-#: engines/scumm/scumm.cpp:4488
+#: engines/scumm/scumm.cpp:4496
 msgid "Would you like to host or join a network play session?"
 msgstr "Wilt u een netwerk spel hosten of aan meedoen?"
 
diff --git a/po/nn_NO.po b/po/nn_NO.po
index f1621f0453b..2c21ef97cd2 100644
--- a/po/nn_NO.po
+++ b/po/nn_NO.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-09 13:22+0000\n"
+"POT-Creation-Date: 2026-06-11 13:12+0000\n"
 "PO-Revision-Date: 2023-12-14 14:28+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Norwegian Nynorsk <https://translations.scummvm.org/projects/"
@@ -344,7 +344,7 @@ msgstr "Ein anna lagringsteneste arbeider nett no. Vill du abryta han?"
 #: engines/hypno/metaengine.cpp:192 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:197
 #: engines/mm/mm1/metaengine.cpp:54 engines/parallaction/metaengine.cpp:169
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
 #: engines/sherlock/metaengine.cpp:638 engines/sherlock/metaengine.cpp:1244
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:328
@@ -363,7 +363,7 @@ msgstr "Ja"
 #: engines/hypno/metaengine.cpp:198 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:204
 #: engines/mm/mm1/metaengine.cpp:52 engines/parallaction/metaengine.cpp:175
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
 #: engines/sherlock/metaengine.cpp:643 engines/sherlock/metaengine.cpp:1250
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:329
@@ -391,7 +391,7 @@ msgstr "JSON-koden er feil utforma."
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:123
 #: engines/drascula/saveload.cpp:51 engines/grim/inputdialog.cpp:83
 #: engines/parallaction/saveload.cpp:270 engines/sci/engine/kgraphics.cpp:76
-#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4482
+#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4490
 #: engines/sword1/control.cpp:2816
 msgid "OK"
 msgstr "OK"
@@ -3336,7 +3336,7 @@ msgstr ""
 #: engines/mohawk/riven_stacks/aspit.cpp:454
 #: engines/neverhood/metaengine.cpp:200 engines/saga/metaengine.cpp:449
 #: engines/scumm/help.cpp:84 engines/scumm/help.cpp:86
-#: engines/scumm/scumm.cpp:4482 engines/sludge/keymapper_tables.h:95
+#: engines/scumm/scumm.cpp:4490 engines/sludge/keymapper_tables.h:95
 #: engines/sludge/keymapper_tables.h:144 engines/sludge/keymapper_tables.h:223
 #: engines/sludge/keymapper_tables.h:343 engines/sludge/keymapper_tables.h:355
 #: engines/sludge/keymapper_tables.h:416 engines/sludge/keymapper_tables.h:429
@@ -6781,7 +6781,7 @@ msgstr ""
 msgid "Quick load or save game isn't supported in this location"
 msgstr ""
 
-#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3421
+#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3429
 #, fuzzy, c-format
 msgid ""
 "Failed to load saved game from file:\n"
@@ -6789,7 +6789,7 @@ msgid ""
 "%s"
 msgstr "Kunne ikkje laste lagra spel fr?? fil."
 
-#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3408
+#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3416
 #, fuzzy, c-format
 msgid ""
 "Failed to save game to file:\n"
@@ -6797,7 +6797,7 @@ msgid ""
 "%s"
 msgstr "Klarte ikkje lagre speltilstand til fil."
 
-#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3441
+#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3449
 #, fuzzy, c-format
 msgid ""
 "Successfully saved game in file:\n"
@@ -12688,7 +12688,7 @@ msgid "Your Name:"
 msgstr "Namn:"
 
 #. I18N: Button, start hosting online multiplayer game
-#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4488
+#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4496
 msgid "Host"
 msgstr ""
 
@@ -12700,7 +12700,7 @@ msgid "Querying games..."
 msgstr "Lagrar spel..."
 
 #. I18N: Join online multiplayer game
-#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4488
+#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4496
 msgid "Join"
 msgstr ""
 
@@ -13864,7 +13864,7 @@ msgid ""
 "required resources for MT-32. Using AdLib instead."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:4364
+#: engines/scumm/scumm.cpp:4372
 msgid ""
 "Usually, Maniac Mansion would start now. But for that to work, the game "
 "files for Maniac Mansion have to be in the 'Maniac' directory inside the "
@@ -13874,7 +13874,7 @@ msgstr ""
 "m?? du ha datafilane til Maniac Mansion i ??Maniac??-mappa inni ??Tentacle??-"
 "spelmappa, og spelet m?? vere lagt til i ScummVM."
 
-#: engines/scumm/scumm.cpp:4488
+#: engines/scumm/scumm.cpp:4496
 #, fuzzy
 msgid "Would you like to host or join a network play session?"
 msgstr "Vil du ??pne eller lagre spelet?"
diff --git a/po/pl_PL.po b/po/pl_PL.po
index ba6fe33bf1e..f28023a042b 100644
--- a/po/pl_PL.po
+++ b/po/pl_PL.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-09 13:22+0000\n"
+"POT-Creation-Date: 2026-06-11 13:12+0000\n"
 "PO-Revision-Date: 2026-04-17 00:22+0000\n"
 "Last-Translator: SkiffPL <skiff at windowslive.com>\n"
 "Language-Team: Polish <https://translations.scummvm.org/projects/scummvm/"
@@ -344,7 +344,7 @@ msgstr "Inna us??uga jest aktywna. Czy chcesz j?? przerwa???"
 #: engines/hypno/metaengine.cpp:192 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:197
 #: engines/mm/mm1/metaengine.cpp:54 engines/parallaction/metaengine.cpp:169
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
 #: engines/sherlock/metaengine.cpp:638 engines/sherlock/metaengine.cpp:1244
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:328
@@ -363,7 +363,7 @@ msgstr "Tak"
 #: engines/hypno/metaengine.cpp:198 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:204
 #: engines/mm/mm1/metaengine.cpp:52 engines/parallaction/metaengine.cpp:175
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
 #: engines/sherlock/metaengine.cpp:643 engines/sherlock/metaengine.cpp:1250
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:329
@@ -391,7 +391,7 @@ msgstr "Zawarto???? kodu JSON jest nieprawid??owa."
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:123
 #: engines/drascula/saveload.cpp:51 engines/grim/inputdialog.cpp:83
 #: engines/parallaction/saveload.cpp:270 engines/sci/engine/kgraphics.cpp:76
-#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4482
+#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4490
 #: engines/sword1/control.cpp:2816
 msgid "OK"
 msgstr "OK"
@@ -3396,7 +3396,7 @@ msgstr "Silnik graficzny gier przygodowych"
 #: engines/mohawk/riven_stacks/aspit.cpp:454
 #: engines/neverhood/metaengine.cpp:200 engines/saga/metaengine.cpp:449
 #: engines/scumm/help.cpp:84 engines/scumm/help.cpp:86
-#: engines/scumm/scumm.cpp:4482 engines/sludge/keymapper_tables.h:95
+#: engines/scumm/scumm.cpp:4490 engines/sludge/keymapper_tables.h:95
 #: engines/sludge/keymapper_tables.h:144 engines/sludge/keymapper_tables.h:223
 #: engines/sludge/keymapper_tables.h:343 engines/sludge/keymapper_tables.h:355
 #: engines/sludge/keymapper_tables.h:416 engines/sludge/keymapper_tables.h:429
@@ -7050,7 +7050,7 @@ msgid "Quick load or save game isn't supported in this location"
 msgstr ""
 "Szybkie wczytywanie i zapisywanie gry nie jest obs??ugiwane w tej lokalizacji"
 
-#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3421
+#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3429
 #, c-format
 msgid ""
 "Failed to load saved game from file:\n"
@@ -7061,7 +7061,7 @@ msgstr ""
 "\n"
 "%s"
 
-#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3408
+#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3416
 #, c-format
 msgid ""
 "Failed to save game to file:\n"
@@ -7072,7 +7072,7 @@ msgstr ""
 "\n"
 "%s"
 
-#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3441
+#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3449
 #, c-format
 msgid ""
 "Successfully saved game in file:\n"
@@ -12904,7 +12904,7 @@ msgid "Your Name:"
 msgstr "Nazwa:"
 
 #. I18N: Button, start hosting online multiplayer game
-#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4488
+#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4496
 msgid "Host"
 msgstr "Hostuj"
 
@@ -12914,7 +12914,7 @@ msgid "Querying games..."
 msgstr "Uzyskiwanie listy gier..."
 
 #. I18N: Join online multiplayer game
-#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4488
+#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4496
 msgid "Join"
 msgstr "Do????cz"
 
@@ -14145,7 +14145,7 @@ msgstr ""
 "element??w\n"
 "zasob??w dla MT-32. Zostanie u??yty AdLib."
 
-#: engines/scumm/scumm.cpp:4364
+#: engines/scumm/scumm.cpp:4372
 msgid ""
 "Usually, Maniac Mansion would start now. But for that to work, the game "
 "files for Maniac Mansion have to be in the 'Maniac' directory inside the "
@@ -14156,7 +14156,7 @@ msgstr ""
 "\"Maniac\" w katalogu gry Day of the Tentacle, a gra musi by?? dodana do "
 "ScummVM."
 
-#: engines/scumm/scumm.cpp:4488
+#: engines/scumm/scumm.cpp:4496
 msgid "Would you like to host or join a network play session?"
 msgstr "Czy chcesz zorganizowa?? sesj?? gry sieciowej lub do????czy?? do niej?"
 
diff --git a/po/pt_BR.po b/po/pt_BR.po
index 40f6e4020d6..dd7f68ef5be 100644
--- a/po/pt_BR.po
+++ b/po/pt_BR.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-09 13:22+0000\n"
+"POT-Creation-Date: 2026-06-11 13:12+0000\n"
 "PO-Revision-Date: 2024-11-16 02:13+0000\n"
 "Last-Translator: Marcel Souza Lemes <marcosoutsider at gmail.com>\n"
 "Language-Team: Portuguese (Brazil) <https://translations.scummvm.org/"
@@ -345,7 +345,7 @@ msgstr "Outro Armazenamento est?? ativo no momento. Deseja interromp??-lo?"
 #: engines/hypno/metaengine.cpp:192 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:197
 #: engines/mm/mm1/metaengine.cpp:54 engines/parallaction/metaengine.cpp:169
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
 #: engines/sherlock/metaengine.cpp:638 engines/sherlock/metaengine.cpp:1244
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:328
@@ -364,7 +364,7 @@ msgstr "Sim"
 #: engines/hypno/metaengine.cpp:198 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:204
 #: engines/mm/mm1/metaengine.cpp:52 engines/parallaction/metaengine.cpp:175
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
 #: engines/sherlock/metaengine.cpp:643 engines/sherlock/metaengine.cpp:1250
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:329
@@ -393,7 +393,7 @@ msgstr "O conte??do do c??digo JSON est?? incorreto."
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:123
 #: engines/drascula/saveload.cpp:51 engines/grim/inputdialog.cpp:83
 #: engines/parallaction/saveload.cpp:270 engines/sci/engine/kgraphics.cpp:76
-#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4482
+#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4490
 #: engines/sword1/control.cpp:2816
 msgid "OK"
 msgstr "OK"
@@ -3475,7 +3475,7 @@ msgstr "Engine de jogos de aventura"
 #: engines/mohawk/riven_stacks/aspit.cpp:454
 #: engines/neverhood/metaengine.cpp:200 engines/saga/metaengine.cpp:449
 #: engines/scumm/help.cpp:84 engines/scumm/help.cpp:86
-#: engines/scumm/scumm.cpp:4482 engines/sludge/keymapper_tables.h:95
+#: engines/scumm/scumm.cpp:4490 engines/sludge/keymapper_tables.h:95
 #: engines/sludge/keymapper_tables.h:144 engines/sludge/keymapper_tables.h:223
 #: engines/sludge/keymapper_tables.h:343 engines/sludge/keymapper_tables.h:355
 #: engines/sludge/keymapper_tables.h:416 engines/sludge/keymapper_tables.h:429
@@ -7337,7 +7337,7 @@ msgstr ""
 msgid "Quick load or save game isn't supported in this location"
 msgstr "Salvar ou carregar rapidamente n??o ?? suportado neste local"
 
-#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3421
+#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3429
 #, c-format
 msgid ""
 "Failed to load saved game from file:\n"
@@ -7348,7 +7348,7 @@ msgstr ""
 "\n"
 "%s"
 
-#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3408
+#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3416
 #, c-format
 msgid ""
 "Failed to save game to file:\n"
@@ -7359,7 +7359,7 @@ msgstr ""
 "\n"
 "%s"
 
-#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3441
+#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3449
 #, c-format
 msgid ""
 "Successfully saved game in file:\n"
@@ -13497,7 +13497,7 @@ msgid "Your Name:"
 msgstr "Seu Nome:"
 
 #. I18N: Button, start hosting online multiplayer game
-#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4488
+#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4496
 msgid "Host"
 msgstr "Hospedar"
 
@@ -13507,7 +13507,7 @@ msgid "Querying games..."
 msgstr "Consultando jogos..."
 
 #. I18N: Join online multiplayer game
-#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4488
+#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4496
 msgid "Join"
 msgstr "Juntar-se"
 
@@ -14756,7 +14756,7 @@ msgstr ""
 "recursos necess??rios para usar m??sica MT-32. Ser?? utilizado AdLib em seu "
 "lugar."
 
-#: engines/scumm/scumm.cpp:4364
+#: engines/scumm/scumm.cpp:4372
 msgid ""
 "Usually, Maniac Mansion would start now. But for that to work, the game "
 "files for Maniac Mansion have to be in the 'Maniac' directory inside the "
@@ -14767,7 +14767,7 @@ msgstr ""
 "'Maniac' dentro do diret??rio do jogo Tentacle, e o jogo precisa ser "
 "adicionado ao ScummVM."
 
-#: engines/scumm/scumm.cpp:4488
+#: engines/scumm/scumm.cpp:4496
 msgid "Would you like to host or join a network play session?"
 msgstr "Gostaria de hospedar ou participar de uma sess??o de jogo em rede?"
 
diff --git a/po/pt_PT.po b/po/pt_PT.po
index 0ae1f9a7cbd..356c46e7128 100644
--- a/po/pt_PT.po
+++ b/po/pt_PT.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.10.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-09 13:22+0000\n"
+"POT-Creation-Date: 2026-06-11 13:12+0000\n"
 "PO-Revision-Date: 2022-06-05 14:09+0000\n"
 "Last-Translator: Daniel <supsuper at gmail.com>\n"
 "Language-Team: Portuguese (Portugal) <https://translations.scummvm.org/"
@@ -357,7 +357,7 @@ msgstr "Outro armazenamento est?? ocupado. Deseja interromp??-lo?"
 #: engines/hypno/metaengine.cpp:192 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:197
 #: engines/mm/mm1/metaengine.cpp:54 engines/parallaction/metaengine.cpp:169
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
 #: engines/sherlock/metaengine.cpp:638 engines/sherlock/metaengine.cpp:1244
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:328
@@ -376,7 +376,7 @@ msgstr "Sim"
 #: engines/hypno/metaengine.cpp:198 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:204
 #: engines/mm/mm1/metaengine.cpp:52 engines/parallaction/metaengine.cpp:175
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
 #: engines/sherlock/metaengine.cpp:643 engines/sherlock/metaengine.cpp:1250
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:329
@@ -406,7 +406,7 @@ msgstr ""
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:123
 #: engines/drascula/saveload.cpp:51 engines/grim/inputdialog.cpp:83
 #: engines/parallaction/saveload.cpp:270 engines/sci/engine/kgraphics.cpp:76
-#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4482
+#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4490
 #: engines/sword1/control.cpp:2816
 msgid "OK"
 msgstr "OK"
@@ -3367,7 +3367,7 @@ msgstr ""
 #: engines/mohawk/riven_stacks/aspit.cpp:454
 #: engines/neverhood/metaengine.cpp:200 engines/saga/metaengine.cpp:449
 #: engines/scumm/help.cpp:84 engines/scumm/help.cpp:86
-#: engines/scumm/scumm.cpp:4482 engines/sludge/keymapper_tables.h:95
+#: engines/scumm/scumm.cpp:4490 engines/sludge/keymapper_tables.h:95
 #: engines/sludge/keymapper_tables.h:144 engines/sludge/keymapper_tables.h:223
 #: engines/sludge/keymapper_tables.h:343 engines/sludge/keymapper_tables.h:355
 #: engines/sludge/keymapper_tables.h:416 engines/sludge/keymapper_tables.h:429
@@ -6800,7 +6800,7 @@ msgstr ""
 msgid "Quick load or save game isn't supported in this location"
 msgstr ""
 
-#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3421
+#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3429
 #, c-format
 msgid ""
 "Failed to load saved game from file:\n"
@@ -6811,7 +6811,7 @@ msgstr ""
 "\n"
 "%s"
 
-#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3408
+#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3416
 #, c-format
 msgid ""
 "Failed to save game to file:\n"
@@ -6822,7 +6822,7 @@ msgstr ""
 "\n"
 "%s"
 
-#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3441
+#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3449
 #, c-format
 msgid ""
 "Successfully saved game in file:\n"
@@ -12787,7 +12787,7 @@ msgid "Your Name:"
 msgstr "Nome:"
 
 #. I18N: Button, start hosting online multiplayer game
-#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4488
+#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4496
 msgid "Host"
 msgstr ""
 
@@ -12799,7 +12799,7 @@ msgid "Querying games..."
 msgstr "A guardar jogo..."
 
 #. I18N: Join online multiplayer game
-#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4488
+#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4496
 msgid "Join"
 msgstr ""
 
@@ -13984,14 +13984,14 @@ msgid ""
 "required resources for MT-32. Using AdLib instead."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:4364
+#: engines/scumm/scumm.cpp:4372
 msgid ""
 "Usually, Maniac Mansion would start now. But for that to work, the game "
 "files for Maniac Mansion have to be in the 'Maniac' directory inside the "
 "Tentacle game directory, and the game has to be added to ScummVM."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:4488
+#: engines/scumm/scumm.cpp:4496
 #, fuzzy
 msgid "Would you like to host or join a network play session?"
 msgstr "Deseja guardar ou carregar um jogo?"
diff --git a/po/ro.po b/po/ro.po
index 6b54dbf0343..33a6ffc395e 100644
--- a/po/ro.po
+++ b/po/ro.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.10.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-09 13:22+0000\n"
+"POT-Creation-Date: 2026-06-11 13:12+0000\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: Automatically generated\n"
 "Language-Team: none\n"
@@ -328,7 +328,7 @@ msgstr ""
 #: engines/hypno/metaengine.cpp:192 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:197
 #: engines/mm/mm1/metaengine.cpp:54 engines/parallaction/metaengine.cpp:169
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
 #: engines/sherlock/metaengine.cpp:638 engines/sherlock/metaengine.cpp:1244
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:328
@@ -347,7 +347,7 @@ msgstr ""
 #: engines/hypno/metaengine.cpp:198 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:204
 #: engines/mm/mm1/metaengine.cpp:52 engines/parallaction/metaengine.cpp:175
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
 #: engines/sherlock/metaengine.cpp:643 engines/sherlock/metaengine.cpp:1250
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:329
@@ -375,7 +375,7 @@ msgstr ""
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:123
 #: engines/drascula/saveload.cpp:51 engines/grim/inputdialog.cpp:83
 #: engines/parallaction/saveload.cpp:270 engines/sci/engine/kgraphics.cpp:76
-#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4482
+#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4490
 #: engines/sword1/control.cpp:2816
 msgid "OK"
 msgstr ""
@@ -3161,7 +3161,7 @@ msgstr ""
 #: engines/mohawk/riven_stacks/aspit.cpp:454
 #: engines/neverhood/metaengine.cpp:200 engines/saga/metaengine.cpp:449
 #: engines/scumm/help.cpp:84 engines/scumm/help.cpp:86
-#: engines/scumm/scumm.cpp:4482 engines/sludge/keymapper_tables.h:95
+#: engines/scumm/scumm.cpp:4490 engines/sludge/keymapper_tables.h:95
 #: engines/sludge/keymapper_tables.h:144 engines/sludge/keymapper_tables.h:223
 #: engines/sludge/keymapper_tables.h:343 engines/sludge/keymapper_tables.h:355
 #: engines/sludge/keymapper_tables.h:416 engines/sludge/keymapper_tables.h:429
@@ -6333,7 +6333,7 @@ msgstr ""
 msgid "Quick load or save game isn't supported in this location"
 msgstr ""
 
-#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3421
+#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3429
 #, c-format
 msgid ""
 "Failed to load saved game from file:\n"
@@ -6341,7 +6341,7 @@ msgid ""
 "%s"
 msgstr ""
 
-#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3408
+#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3416
 #, c-format
 msgid ""
 "Failed to save game to file:\n"
@@ -6349,7 +6349,7 @@ msgid ""
 "%s"
 msgstr ""
 
-#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3441
+#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3449
 #, c-format
 msgid ""
 "Successfully saved game in file:\n"
@@ -11476,7 +11476,7 @@ msgid "Your Name:"
 msgstr ""
 
 #. I18N: Button, start hosting online multiplayer game
-#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4488
+#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4496
 msgid "Host"
 msgstr ""
 
@@ -11486,7 +11486,7 @@ msgid "Querying games..."
 msgstr ""
 
 #. I18N: Join online multiplayer game
-#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4488
+#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4496
 msgid "Join"
 msgstr ""
 
@@ -12593,14 +12593,14 @@ msgid ""
 "required resources for MT-32. Using AdLib instead."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:4364
+#: engines/scumm/scumm.cpp:4372
 msgid ""
 "Usually, Maniac Mansion would start now. But for that to work, the game "
 "files for Maniac Mansion have to be in the 'Maniac' directory inside the "
 "Tentacle game directory, and the game has to be added to ScummVM."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:4488
+#: engines/scumm/scumm.cpp:4496
 msgid "Would you like to host or join a network play session?"
 msgstr ""
 
diff --git a/po/ru_RU.po b/po/ru_RU.po
index 65d124cea3b..7b5e6b5c3bf 100644
--- a/po/ru_RU.po
+++ b/po/ru_RU.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.8.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-09 13:22+0000\n"
+"POT-Creation-Date: 2026-06-11 13:12+0000\n"
 "PO-Revision-Date: 2026-05-06 08:54+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Russian <https://translations.scummvm.org/projects/scummvm/"
@@ -339,7 +339,7 @@ msgstr "???????????? ???????????????? ???????????? ??????????????????. ???? ????
 #: engines/hypno/metaengine.cpp:192 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:197
 #: engines/mm/mm1/metaengine.cpp:54 engines/parallaction/metaengine.cpp:169
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
 #: engines/sherlock/metaengine.cpp:638 engines/sherlock/metaengine.cpp:1244
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:328
@@ -358,7 +358,7 @@ msgstr "????"
 #: engines/hypno/metaengine.cpp:198 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:204
 #: engines/mm/mm1/metaengine.cpp:52 engines/parallaction/metaengine.cpp:175
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
 #: engines/sherlock/metaengine.cpp:643 engines/sherlock/metaengine.cpp:1250
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:329
@@ -386,7 +386,7 @@ msgstr "???????????????????? ???????? JSON ?????????? ???????????????? ????????
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:123
 #: engines/drascula/saveload.cpp:51 engines/grim/inputdialog.cpp:83
 #: engines/parallaction/saveload.cpp:270 engines/sci/engine/kgraphics.cpp:76
-#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4482
+#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4490
 #: engines/sword1/control.cpp:2816
 msgid "OK"
 msgstr "OK"
@@ -3400,7 +3400,7 @@ msgstr "???????????? ?????????????????????? ??????????????????"
 #: engines/mohawk/riven_stacks/aspit.cpp:454
 #: engines/neverhood/metaengine.cpp:200 engines/saga/metaengine.cpp:449
 #: engines/scumm/help.cpp:84 engines/scumm/help.cpp:86
-#: engines/scumm/scumm.cpp:4482 engines/sludge/keymapper_tables.h:95
+#: engines/scumm/scumm.cpp:4490 engines/sludge/keymapper_tables.h:95
 #: engines/sludge/keymapper_tables.h:144 engines/sludge/keymapper_tables.h:223
 #: engines/sludge/keymapper_tables.h:343 engines/sludge/keymapper_tables.h:355
 #: engines/sludge/keymapper_tables.h:416 engines/sludge/keymapper_tables.h:429
@@ -7053,7 +7053,7 @@ msgstr ""
 msgid "Quick load or save game isn't supported in this location"
 msgstr "?????????????? ???????????????????? ?????? ???????????????? ???? ???????????????????????????? ?? ???????? ??????????????"
 
-#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3421
+#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3429
 #, c-format
 msgid ""
 "Failed to load saved game from file:\n"
@@ -7064,7 +7064,7 @@ msgstr ""
 "\n"
 "%s"
 
-#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3408
+#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3416
 #, c-format
 msgid ""
 "Failed to save game to file:\n"
@@ -7075,7 +7075,7 @@ msgstr ""
 "\n"
 "%s"
 
-#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3441
+#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3449
 #, c-format
 msgid ""
 "Successfully saved game in file:\n"
@@ -12570,7 +12570,7 @@ msgid "Your Name:"
 msgstr "???????? ??????:"
 
 #. I18N: Button, start hosting online multiplayer game
-#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4488
+#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4496
 msgid "Host"
 msgstr "????????????"
 
@@ -12580,7 +12580,7 @@ msgid "Querying games..."
 msgstr "?????????????? ???????????? ??????..."
 
 #. I18N: Join online multiplayer game
-#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4488
+#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4496
 msgid "Join"
 msgstr "????????????????????????"
 
@@ -13818,7 +13818,7 @@ msgstr ""
 "?????? ???????????? Monkey Island 1 ???????????????? ??????, ?????? ?? ?????? ??????????????????????\n"
 "??????????????, ?????????????????????? ?????? MT-32. ???????????????????????? ???? AdLib."
 
-#: engines/scumm/scumm.cpp:4364
+#: engines/scumm/scumm.cpp:4372
 msgid ""
 "Usually, Maniac Mansion would start now. But for that to work, the game "
 "files for Maniac Mansion have to be in the 'Maniac' directory inside the "
@@ -13828,7 +13828,7 @@ msgstr ""
 "???????? Maniac Mansion ???????????? ???????? ?????????????????????? ?? ???????????????????? 'Maniac' ???????????? "
 "???????????????????? ???????? Tentacle ?? ???????? ???????? ???????????? ???????? ?????????????????? ?? ScummVM."
 
-#: engines/scumm/scumm.cpp:4488
+#: engines/scumm/scumm.cpp:4496
 msgid "Would you like to host or join a network play session?"
 msgstr "???? ???????????? ???????????????????????????? ?????? ???????????? ?????????????? ?????????"
 
diff --git a/po/sv_SE.po b/po/sv_SE.po
index cbf675c4686..0f8af32ffe6 100644
--- a/po/sv_SE.po
+++ b/po/sv_SE.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.5.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-09 13:22+0000\n"
+"POT-Creation-Date: 2026-06-11 13:12+0000\n"
 "PO-Revision-Date: 2026-04-19 13:22+0000\n"
 "Last-Translator: Daniel Nylander <po at danielnylander.se>\n"
 "Language-Team: Swedish <https://translations.scummvm.org/projects/scummvm/"
@@ -340,7 +340,7 @@ msgstr "En annan lagring fungerar just nu. Vill du avbryta den?"
 #: engines/hypno/metaengine.cpp:192 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:197
 #: engines/mm/mm1/metaengine.cpp:54 engines/parallaction/metaengine.cpp:169
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
 #: engines/sherlock/metaengine.cpp:638 engines/sherlock/metaengine.cpp:1244
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:328
@@ -359,7 +359,7 @@ msgstr "Ja"
 #: engines/hypno/metaengine.cpp:198 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:204
 #: engines/mm/mm1/metaengine.cpp:52 engines/parallaction/metaengine.cpp:175
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
 #: engines/sherlock/metaengine.cpp:643 engines/sherlock/metaengine.cpp:1250
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:329
@@ -387,7 +387,7 @@ msgstr "JSON-koden inneh??ller fel."
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:123
 #: engines/drascula/saveload.cpp:51 engines/grim/inputdialog.cpp:83
 #: engines/parallaction/saveload.cpp:270 engines/sci/engine/kgraphics.cpp:76
-#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4482
+#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4490
 #: engines/sword1/control.cpp:2816
 msgid "OK"
 msgstr "OK"
@@ -3390,7 +3390,7 @@ msgstr "Grafisk ??ventyrsspelmotor"
 #: engines/mohawk/riven_stacks/aspit.cpp:454
 #: engines/neverhood/metaengine.cpp:200 engines/saga/metaengine.cpp:449
 #: engines/scumm/help.cpp:84 engines/scumm/help.cpp:86
-#: engines/scumm/scumm.cpp:4482 engines/sludge/keymapper_tables.h:95
+#: engines/scumm/scumm.cpp:4490 engines/sludge/keymapper_tables.h:95
 #: engines/sludge/keymapper_tables.h:144 engines/sludge/keymapper_tables.h:223
 #: engines/sludge/keymapper_tables.h:343 engines/sludge/keymapper_tables.h:355
 #: engines/sludge/keymapper_tables.h:416 engines/sludge/keymapper_tables.h:429
@@ -7028,7 +7028,7 @@ msgstr ""
 msgid "Quick load or save game isn't supported in this location"
 msgstr "Snabbinl??sning eller sparning av spel st??ds inte p?? denna plats"
 
-#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3421
+#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3429
 #, c-format
 msgid ""
 "Failed to load saved game from file:\n"
@@ -7039,7 +7039,7 @@ msgstr ""
 "\n"
 "%s"
 
-#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3408
+#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3416
 #, c-format
 msgid ""
 "Failed to save game to file:\n"
@@ -7050,7 +7050,7 @@ msgstr ""
 "\n"
 "%s"
 
-#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3441
+#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3449
 #, c-format
 msgid ""
 "Successfully saved game in file:\n"
@@ -12621,7 +12621,7 @@ msgid "Your Name:"
 msgstr "Ditt namn:"
 
 #. I18N: Button, start hosting online multiplayer game
-#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4488
+#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4496
 msgid "Host"
 msgstr "V??rd"
 
@@ -12631,7 +12631,7 @@ msgid "Querying games..."
 msgstr "S??ker efter spel..."
 
 #. I18N: Join online multiplayer game
-#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4488
+#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4496
 msgid "Join"
 msgstr "G?? med"
 
@@ -13868,7 +13868,7 @@ msgstr ""
 "Denna specifika version av Monkey Island 1 ??r k??nd f??r att sakna\n"
 "n??gra resurser som kr??vs f??r MT-32. Anv??nd AdLib ist??llet."
 
-#: engines/scumm/scumm.cpp:4364
+#: engines/scumm/scumm.cpp:4372
 msgid ""
 "Usually, Maniac Mansion would start now. But for that to work, the game "
 "files for Maniac Mansion have to be in the 'Maniac' directory inside the "
@@ -13878,7 +13878,7 @@ msgstr ""
 "Maniac Mansion-filerna placeras i 'Maniac'-katalogen i 'Tentacle'-katalogen. "
 "Spelet m??ste ??ven l??ggas till i ScummVM."
 
-#: engines/scumm/scumm.cpp:4488
+#: engines/scumm/scumm.cpp:4496
 msgid "Would you like to host or join a network play session?"
 msgstr "Vill du st?? v??rd f??r eller g?? med i en session f??r n??tverksspel?"
 
diff --git a/po/tr.po b/po/tr.po
index 4804862c3c1..39747e32d91 100644
--- a/po/tr.po
+++ b/po/tr.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-09 13:22+0000\n"
+"POT-Creation-Date: 2026-06-11 13:12+0000\n"
 "PO-Revision-Date: 2024-10-10 12:33+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Turkish <https://translations.scummvm.org/projects/scummvm/"
@@ -346,7 +346,7 @@ msgstr "Ba??ka depolama alan?? ??u anda ??al??????yor. Engellemek ister misiniz?
 #: engines/hypno/metaengine.cpp:192 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:197
 #: engines/mm/mm1/metaengine.cpp:54 engines/parallaction/metaengine.cpp:169
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
 #: engines/sherlock/metaengine.cpp:638 engines/sherlock/metaengine.cpp:1244
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:328
@@ -365,7 +365,7 @@ msgstr "Evet"
 #: engines/hypno/metaengine.cpp:198 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:204
 #: engines/mm/mm1/metaengine.cpp:52 engines/parallaction/metaengine.cpp:175
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
 #: engines/sherlock/metaengine.cpp:643 engines/sherlock/metaengine.cpp:1250
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:329
@@ -395,7 +395,7 @@ msgstr "JSON kod i??erikleri hatal??."
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:123
 #: engines/drascula/saveload.cpp:51 engines/grim/inputdialog.cpp:83
 #: engines/parallaction/saveload.cpp:270 engines/sci/engine/kgraphics.cpp:76
-#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4482
+#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4490
 #: engines/sword1/control.cpp:2816
 msgid "OK"
 msgstr "Tamam"
@@ -3358,7 +3358,7 @@ msgstr ""
 #: engines/mohawk/riven_stacks/aspit.cpp:454
 #: engines/neverhood/metaengine.cpp:200 engines/saga/metaengine.cpp:449
 #: engines/scumm/help.cpp:84 engines/scumm/help.cpp:86
-#: engines/scumm/scumm.cpp:4482 engines/sludge/keymapper_tables.h:95
+#: engines/scumm/scumm.cpp:4490 engines/sludge/keymapper_tables.h:95
 #: engines/sludge/keymapper_tables.h:144 engines/sludge/keymapper_tables.h:223
 #: engines/sludge/keymapper_tables.h:343 engines/sludge/keymapper_tables.h:355
 #: engines/sludge/keymapper_tables.h:416 engines/sludge/keymapper_tables.h:429
@@ -6698,7 +6698,7 @@ msgstr ""
 msgid "Quick load or save game isn't supported in this location"
 msgstr ""
 
-#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3421
+#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3429
 #, c-format
 msgid ""
 "Failed to load saved game from file:\n"
@@ -6706,7 +6706,7 @@ msgid ""
 "%s"
 msgstr ""
 
-#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3408
+#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3416
 #, c-format
 msgid ""
 "Failed to save game to file:\n"
@@ -6714,7 +6714,7 @@ msgid ""
 "%s"
 msgstr ""
 
-#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3441
+#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3449
 #, c-format
 msgid ""
 "Successfully saved game in file:\n"
@@ -12323,7 +12323,7 @@ msgid "Your Name:"
 msgstr "??sim:"
 
 #. I18N: Button, start hosting online multiplayer game
-#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4488
+#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4496
 msgid "Host"
 msgstr ""
 
@@ -12334,7 +12334,7 @@ msgid "Querying games..."
 msgstr "Oyun y??kle:"
 
 #. I18N: Join online multiplayer game
-#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4488
+#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4496
 msgid "Join"
 msgstr ""
 
@@ -13483,14 +13483,14 @@ msgid ""
 "required resources for MT-32. Using AdLib instead."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:4364
+#: engines/scumm/scumm.cpp:4372
 msgid ""
 "Usually, Maniac Mansion would start now. But for that to work, the game "
 "files for Maniac Mansion have to be in the 'Maniac' directory inside the "
 "Tentacle game directory, and the game has to be added to ScummVM."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:4488
+#: engines/scumm/scumm.cpp:4496
 msgid "Would you like to host or join a network play session?"
 msgstr ""
 
diff --git a/po/uk_UA.po b/po/uk_UA.po
index 4b831e3f0d0..47fe76cb8f9 100644
--- a/po/uk_UA.po
+++ b/po/uk_UA.po
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.9.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-09 13:22+0000\n"
+"POT-Creation-Date: 2026-06-11 13:12+0000\n"
 "PO-Revision-Date: 2026-05-06 08:54+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Ukrainian <https://translations.scummvm.org/projects/scummvm/"
@@ -341,7 +341,7 @@ msgstr "???????? ???????????? ?????????????? ?????? ?? ????????????????. ???? ??
 #: engines/hypno/metaengine.cpp:192 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:197
 #: engines/mm/mm1/metaengine.cpp:54 engines/parallaction/metaengine.cpp:169
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
 #: engines/sherlock/metaengine.cpp:638 engines/sherlock/metaengine.cpp:1244
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:328
@@ -360,7 +360,7 @@ msgstr "??????"
 #: engines/hypno/metaengine.cpp:198 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:204
 #: engines/mm/mm1/metaengine.cpp:52 engines/parallaction/metaengine.cpp:175
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
 #: engines/sherlock/metaengine.cpp:643 engines/sherlock/metaengine.cpp:1250
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:329
@@ -388,7 +388,7 @@ msgstr "???????????????? ???????????? JSON-????????."
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:123
 #: engines/drascula/saveload.cpp:51 engines/grim/inputdialog.cpp:83
 #: engines/parallaction/saveload.cpp:270 engines/sci/engine/kgraphics.cpp:76
-#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4482
+#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4490
 #: engines/sword1/control.cpp:2816
 msgid "OK"
 msgstr "OK"
@@ -3398,7 +3398,7 @@ msgstr "???????????? ?????????????????? ??????????????"
 #: engines/mohawk/riven_stacks/aspit.cpp:454
 #: engines/neverhood/metaengine.cpp:200 engines/saga/metaengine.cpp:449
 #: engines/scumm/help.cpp:84 engines/scumm/help.cpp:86
-#: engines/scumm/scumm.cpp:4482 engines/sludge/keymapper_tables.h:95
+#: engines/scumm/scumm.cpp:4490 engines/sludge/keymapper_tables.h:95
 #: engines/sludge/keymapper_tables.h:144 engines/sludge/keymapper_tables.h:223
 #: engines/sludge/keymapper_tables.h:343 engines/sludge/keymapper_tables.h:355
 #: engines/sludge/keymapper_tables.h:416 engines/sludge/keymapper_tables.h:429
@@ -7038,7 +7038,7 @@ msgstr ""
 msgid "Quick load or save game isn't supported in this location"
 msgstr "???????????? ???????????????????????? ?????? ???????????????????? ???? ?????????????????????????? ?? ?????? ??????????????"
 
-#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3421
+#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3429
 #, c-format
 msgid ""
 "Failed to load saved game from file:\n"
@@ -7049,7 +7049,7 @@ msgstr ""
 "\n"
 "%s"
 
-#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3408
+#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3416
 #, c-format
 msgid ""
 "Failed to save game to file:\n"
@@ -7060,7 +7060,7 @@ msgstr ""
 "\n"
 "%s"
 
-#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3441
+#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3449
 #, c-format
 msgid ""
 "Successfully saved game in file:\n"
@@ -12535,7 +12535,7 @@ msgid "Your Name:"
 msgstr "???????? ????'??:"
 
 #. I18N: Button, start hosting online multiplayer game
-#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4488
+#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4496
 msgid "Host"
 msgstr "??????????????????"
 
@@ -12545,7 +12545,7 @@ msgid "Querying games..."
 msgstr "?????????????? ???????????? ????????..."
 
 #. I18N: Join online multiplayer game
-#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4488
+#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4496
 msgid "Join"
 msgstr "??????????????????????"
 
@@ -13775,7 +13775,7 @@ msgstr ""
 "???? ???????????? Monkey Island 1 ???????????? ?????????? ??????????????, ??????????????????\n"
 "?????? ???????????? MT-32. ?????????????????????? ???? AdLib."
 
-#: engines/scumm/scumm.cpp:4364
+#: engines/scumm/scumm.cpp:4372
 msgid ""
 "Usually, Maniac Mansion would start now. But for that to work, the game "
 "files for Maniac Mansion have to be in the 'Maniac' directory inside the "
@@ -13785,7 +13785,7 @@ msgstr ""
 "?????? ???????????????? ???????????????????? ?????????? ?????? Maniac Manssion ?? ?????????? Maniac ?????????????????? "
 "?????????? ?? ???????? Tentacle, ?? ?????????? ???????????? ???????? ?????? ?? ScummVM."
 
-#: engines/scumm/scumm.cpp:4488
+#: engines/scumm/scumm.cpp:4496
 msgid "Would you like to host or join a network play session?"
 msgstr "???? ???? ???????????? ?????????????????? ?????? ?????????????????????? ???? ?????????????????? ???????"
 
diff --git a/po/zh.po b/po/zh.po
index bbb05e93d2e..d640d58469e 100644
--- a/po/zh.po
+++ b/po/zh.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-09 13:22+0000\n"
+"POT-Creation-Date: 2026-06-11 13:12+0000\n"
 "PO-Revision-Date: 2024-05-14 12:42+0000\n"
 "Last-Translator: ?????? <clonewith at qq.com>\n"
 "Language-Team: Chinese <https://translations.scummvm.org/projects/scummvm/"
@@ -340,7 +340,7 @@ msgstr "????????????????????????????????????????????????"
 #: engines/hypno/metaengine.cpp:192 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:197
 #: engines/mm/mm1/metaengine.cpp:54 engines/parallaction/metaengine.cpp:169
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
 #: engines/sherlock/metaengine.cpp:638 engines/sherlock/metaengine.cpp:1244
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:328
@@ -359,7 +359,7 @@ msgstr "???"
 #: engines/hypno/metaengine.cpp:198 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:204
 #: engines/mm/mm1/metaengine.cpp:52 engines/parallaction/metaengine.cpp:175
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
 #: engines/sherlock/metaengine.cpp:643 engines/sherlock/metaengine.cpp:1250
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:329
@@ -387,7 +387,7 @@ msgstr "JSON ?????????????????????"
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:123
 #: engines/drascula/saveload.cpp:51 engines/grim/inputdialog.cpp:83
 #: engines/parallaction/saveload.cpp:270 engines/sci/engine/kgraphics.cpp:76
-#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4482
+#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4490
 #: engines/sword1/control.cpp:2816
 msgid "OK"
 msgstr "??????"
@@ -3206,7 +3206,7 @@ msgstr ""
 #: engines/mohawk/riven_stacks/aspit.cpp:454
 #: engines/neverhood/metaengine.cpp:200 engines/saga/metaengine.cpp:449
 #: engines/scumm/help.cpp:84 engines/scumm/help.cpp:86
-#: engines/scumm/scumm.cpp:4482 engines/sludge/keymapper_tables.h:95
+#: engines/scumm/scumm.cpp:4490 engines/sludge/keymapper_tables.h:95
 #: engines/sludge/keymapper_tables.h:144 engines/sludge/keymapper_tables.h:223
 #: engines/sludge/keymapper_tables.h:343 engines/sludge/keymapper_tables.h:355
 #: engines/sludge/keymapper_tables.h:416 engines/sludge/keymapper_tables.h:429
@@ -6472,7 +6472,7 @@ msgstr ""
 msgid "Quick load or save game isn't supported in this location"
 msgstr ""
 
-#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3421
+#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3429
 #, c-format
 msgid ""
 "Failed to load saved game from file:\n"
@@ -6480,7 +6480,7 @@ msgid ""
 "%s"
 msgstr ""
 
-#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3408
+#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3416
 #, c-format
 msgid ""
 "Failed to save game to file:\n"
@@ -6488,7 +6488,7 @@ msgid ""
 "%s"
 msgstr ""
 
-#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3441
+#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3449
 #, c-format
 msgid ""
 "Successfully saved game in file:\n"
@@ -12162,7 +12162,7 @@ msgid "Your Name:"
 msgstr "????????????"
 
 #. I18N: Button, start hosting online multiplayer game
-#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4488
+#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4496
 msgid "Host"
 msgstr ""
 
@@ -12174,7 +12174,7 @@ msgid "Querying games..."
 msgstr "???????????????"
 
 #. I18N: Join online multiplayer game
-#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4488
+#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4496
 msgid "Join"
 msgstr ""
 
@@ -13309,14 +13309,14 @@ msgid ""
 "required resources for MT-32. Using AdLib instead."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:4364
+#: engines/scumm/scumm.cpp:4372
 msgid ""
 "Usually, Maniac Mansion would start now. But for that to work, the game "
 "files for Maniac Mansion have to be in the 'Maniac' directory inside the "
 "Tentacle game directory, and the game has to be added to ScummVM."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:4488
+#: engines/scumm/scumm.cpp:4496
 msgid "Would you like to host or join a network play session?"
 msgstr ""
 
diff --git a/po/zh_Hans.po b/po/zh_Hans.po
index daf524927ce..82e6f0977ef 100644
--- a/po/zh_Hans.po
+++ b/po/zh_Hans.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.9.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-09 13:22+0000\n"
+"POT-Creation-Date: 2026-06-11 13:12+0000\n"
 "PO-Revision-Date: 2024-12-01 22:18+0000\n"
 "Last-Translator: ?????? <clonewith at qq.com>\n"
 "Language-Team: Chinese (Simplified Han script) <https://"
@@ -340,7 +340,7 @@ msgstr "????????????????????????????????????????????????"
 #: engines/hypno/metaengine.cpp:192 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:197
 #: engines/mm/mm1/metaengine.cpp:54 engines/parallaction/metaengine.cpp:169
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
 #: engines/sherlock/metaengine.cpp:638 engines/sherlock/metaengine.cpp:1244
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:328
@@ -359,7 +359,7 @@ msgstr "???"
 #: engines/hypno/metaengine.cpp:198 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:204
 #: engines/mm/mm1/metaengine.cpp:52 engines/parallaction/metaengine.cpp:175
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
 #: engines/sherlock/metaengine.cpp:643 engines/sherlock/metaengine.cpp:1250
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:329
@@ -387,7 +387,7 @@ msgstr "JSON ?????????????????????"
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:123
 #: engines/drascula/saveload.cpp:51 engines/grim/inputdialog.cpp:83
 #: engines/parallaction/saveload.cpp:270 engines/sci/engine/kgraphics.cpp:76
-#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4482
+#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4490
 #: engines/sword1/control.cpp:2816
 msgid "OK"
 msgstr "??????"
@@ -3369,7 +3369,7 @@ msgstr "????????????????????????"
 #: engines/mohawk/riven_stacks/aspit.cpp:454
 #: engines/neverhood/metaengine.cpp:200 engines/saga/metaengine.cpp:449
 #: engines/scumm/help.cpp:84 engines/scumm/help.cpp:86
-#: engines/scumm/scumm.cpp:4482 engines/sludge/keymapper_tables.h:95
+#: engines/scumm/scumm.cpp:4490 engines/sludge/keymapper_tables.h:95
 #: engines/sludge/keymapper_tables.h:144 engines/sludge/keymapper_tables.h:223
 #: engines/sludge/keymapper_tables.h:343 engines/sludge/keymapper_tables.h:355
 #: engines/sludge/keymapper_tables.h:416 engines/sludge/keymapper_tables.h:429
@@ -7118,7 +7118,7 @@ msgstr ""
 msgid "Quick load or save game isn't supported in this location"
 msgstr "???????????????????????????????????????"
 
-#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3421
+#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3429
 #, c-format
 msgid ""
 "Failed to load saved game from file:\n"
@@ -7129,7 +7129,7 @@ msgstr ""
 "\n"
 "%s"
 
-#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3408
+#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3416
 #, c-format
 msgid ""
 "Failed to save game to file:\n"
@@ -7140,7 +7140,7 @@ msgstr ""
 "\n"
 "%s"
 
-#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3441
+#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3449
 #, c-format
 msgid ""
 "Successfully saved game in file:\n"
@@ -13106,7 +13106,7 @@ msgid "Your Name:"
 msgstr "???????????????"
 
 #. I18N: Button, start hosting online multiplayer game
-#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4488
+#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4496
 msgid "Host"
 msgstr "??????"
 
@@ -13116,7 +13116,7 @@ msgid "Querying games..."
 msgstr "???????????????..."
 
 #. I18N: Join online multiplayer game
-#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4488
+#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4496
 msgid "Join"
 msgstr "??????"
 
@@ -14319,7 +14319,7 @@ msgstr ""
 "?????????????????? Monkey Island 1 ??????\n"
 "MT-32 ??????????????????????????????????????? AdLib???"
 
-#: engines/scumm/scumm.cpp:4364
+#: engines/scumm/scumm.cpp:4372
 msgid ""
 "Usually, Maniac Mansion would start now. But for that to work, the game "
 "files for Maniac Mansion have to be in the 'Maniac' directory inside the "
@@ -14328,7 +14328,7 @@ msgstr ""
 "Maniac Mansion ???????????????????????????????????????Maniac Mansion ??????????????????????????? "
 "Tentacle ?????????????????????Maniac?????????????????????????????????????????? ScummVM ??????"
 
-#: engines/scumm/scumm.cpp:4488
+#: engines/scumm/scumm.cpp:4496
 msgid "Would you like to host or join a network play session?"
 msgstr "?????????????????????????????????????????????"
 
diff --git a/po/zh_Hant.po b/po/zh_Hant.po
index 7429da2f0d0..343c57a37c6 100644
--- a/po/zh_Hant.po
+++ b/po/zh_Hant.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.9.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-09 13:22+0000\n"
+"POT-Creation-Date: 2026-06-11 13:12+0000\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: Automatically generated\n"
 "Language-Team: none\n"
@@ -329,7 +329,7 @@ msgstr ""
 #: engines/hypno/metaengine.cpp:192 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:197
 #: engines/mm/mm1/metaengine.cpp:54 engines/parallaction/metaengine.cpp:169
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
 #: engines/sherlock/metaengine.cpp:638 engines/sherlock/metaengine.cpp:1244
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:328
@@ -348,7 +348,7 @@ msgstr ""
 #: engines/hypno/metaengine.cpp:198 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:204
 #: engines/mm/mm1/metaengine.cpp:52 engines/parallaction/metaengine.cpp:175
-#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4470
+#: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
 #: engines/sherlock/metaengine.cpp:643 engines/sherlock/metaengine.cpp:1250
 #: engines/sludge/event.cpp:144 engines/toon/toon.cpp:1550
 #: engines/zvision/zvision.cpp:329
@@ -376,7 +376,7 @@ msgstr ""
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:123
 #: engines/drascula/saveload.cpp:51 engines/grim/inputdialog.cpp:83
 #: engines/parallaction/saveload.cpp:270 engines/sci/engine/kgraphics.cpp:76
-#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4482
+#: engines/sci/graphics/controls32.cpp:934 engines/scumm/scumm.cpp:4490
 #: engines/sword1/control.cpp:2816
 msgid "OK"
 msgstr ""
@@ -3162,7 +3162,7 @@ msgstr ""
 #: engines/mohawk/riven_stacks/aspit.cpp:454
 #: engines/neverhood/metaengine.cpp:200 engines/saga/metaengine.cpp:449
 #: engines/scumm/help.cpp:84 engines/scumm/help.cpp:86
-#: engines/scumm/scumm.cpp:4482 engines/sludge/keymapper_tables.h:95
+#: engines/scumm/scumm.cpp:4490 engines/sludge/keymapper_tables.h:95
 #: engines/sludge/keymapper_tables.h:144 engines/sludge/keymapper_tables.h:223
 #: engines/sludge/keymapper_tables.h:343 engines/sludge/keymapper_tables.h:355
 #: engines/sludge/keymapper_tables.h:416 engines/sludge/keymapper_tables.h:429
@@ -6334,7 +6334,7 @@ msgstr ""
 msgid "Quick load or save game isn't supported in this location"
 msgstr ""
 
-#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3421
+#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:3429
 #, c-format
 msgid ""
 "Failed to load saved game from file:\n"
@@ -6342,7 +6342,7 @@ msgid ""
 "%s"
 msgstr ""
 
-#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3408
+#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:3416
 #, c-format
 msgid ""
 "Failed to save game to file:\n"
@@ -6350,7 +6350,7 @@ msgid ""
 "%s"
 msgstr ""
 
-#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3441
+#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:3449
 #, c-format
 msgid ""
 "Successfully saved game in file:\n"
@@ -11477,7 +11477,7 @@ msgid "Your Name:"
 msgstr ""
 
 #. I18N: Button, start hosting online multiplayer game
-#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4488
+#: engines/scumm/dialog-createsession.cpp:49 engines/scumm/scumm.cpp:4496
 msgid "Host"
 msgstr ""
 
@@ -11487,7 +11487,7 @@ msgid "Querying games..."
 msgstr ""
 
 #. I18N: Join online multiplayer game
-#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4488
+#: engines/scumm/dialog-sessionselector.cpp:64 engines/scumm/scumm.cpp:4496
 msgid "Join"
 msgstr ""
 
@@ -12594,14 +12594,14 @@ msgid ""
 "required resources for MT-32. Using AdLib instead."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:4364
+#: engines/scumm/scumm.cpp:4372
 msgid ""
 "Usually, Maniac Mansion would start now. But for that to work, the game "
 "files for Maniac Mansion have to be in the 'Maniac' directory inside the "
 "Tentacle game directory, and the game has to be added to ScummVM."
 msgstr ""
 
-#: engines/scumm/scumm.cpp:4488
+#: engines/scumm/scumm.cpp:4496
 msgid "Would you like to host or join a network play session?"
 msgstr ""
 



From noreply at scummvm.org  Thu Jun 11 17:13:38 2026
From: noreply at scummvm.org (sev-)
Date: Thu, 11 Jun 2026 17:13:38 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm-sites director-buildbot ->
 0b30cfb29d24c3c60d83e2311a1ccc183ec34b60
Message-ID: <20260611171338.6E3467005BC@ron.scummvm.net>

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

Summary:
9540c630b7 IMAGEDIFF: patient zero
a076c4e60c IMAGEDIFF: Update indentation
55c489312c IMAGEDIFF: Complete basic feature set for ImageDiff
abed449585 IMAGEDIFF: Add Index Page with Target Names Listing
68bbe9bfdd IMAGEDIFF: Add target detail page with continuous timeline bar
0f7b6f4568 IMAGEDIFF: Add compare and view pages for build visualization
4c39cc148f IMAGEDIFF: Add build detail page and movie detail page
31a0c45be7 IMAGEDIFF: Add screenshots sample
a4bc152fe2 IMAGEDIFF: Added caching by storing builds
cd38a5c773 IMAGEDIFF: Refactored styling
89f8fd6d94 IMAGEDIFF: Refactor comparison logic structure
2d20458054 IMAGEDIFF: Added pagination with builds caching
3ff6ee972d IMAGEDIFF: PunnyEncoded strings name fixed on frontend
c9fdedca8f IMAGEDIFF: Decode punnyencoded string on frontend
7beb962aa7 IMAGEDIFF: Builds now arranged in increasing order
c018c3bfbd IMAGEDIFF: Cleanup all PHP code
eb97b9fcac IMAGEDIFF: Cleanup all PHP Styles and code
21c4f09a43 IMAGEDIFF: Moved python code upto root and made installation guide
0b30cfb29d IMAGEDIFF: Improved README setup guide


Commit: 9540c630b73c98503f627875b5f4c42481bcd51b
    https://github.com/scummvm/scummvm-sites/commit/9540c630b73c98503f627875b5f4c42481bcd51b
Author: Abhinav Chennubhotla (51199011+PhoenixFlame101 at users.noreply.github.com)
Date: 2026-06-11T19:13:05+02:00

Commit Message:
IMAGEDIFF: patient zero

Changed paths:
  A imagediff/.gitignore
  A imagediff/LICENSE
  A imagediff/NOTICE
  A imagediff/README.md
  A imagediff/assets/1.png
  A imagediff/assets/2.png
  A imagediff/assets/styles/main.css
  A imagediff/index.html
  A imagediff/src/imagediff.php


diff --git a/imagediff/.gitignore b/imagediff/.gitignore
new file mode 100644
index 0000000..bcad0a6
--- /dev/null
+++ b/imagediff/.gitignore
@@ -0,0 +1,474 @@
+##### Windows
+# Windows thumbnail cache files
+Thumbs.db
+Thumbs.db:encryptable
+ehthumbs.db
+ehthumbs_vista.db
+
+# Dump file
+*.stackdump
+
+# Folder config file
+[Dd]esktop.ini
+
+# Recycle Bin used on file shares
+$RECYCLE.BIN/
+
+# Windows Installer files
+*.cab
+*.msi
+*.msix
+*.msm
+*.msp
+
+# Windows shortcuts
+*.lnk
+
+##### Linux
+*~
+
+# temporary files which can be created if a process still has a handle open of a deleted file
+.fuse_hidden*
+
+# KDE directory preferences
+.directory
+
+# Linux trash folder which might appear on any partition or disk
+.Trash-*
+
+# .nfs files are created when an open file is removed but is still being accessed
+.nfs*
+
+##### MacOS
+# General
+.DS_Store
+.AppleDouble
+.LSOverride
+
+# Icon must end with two \r
+Icon
+
+# Thumbnails
+._*
+
+# Files that might appear in the root of a volume
+.DocumentRevisions-V100
+.fseventsd
+.Spotlight-V100
+.TemporaryItems
+.Trashes
+.VolumeIcon.icns
+.com.apple.timemachine.donotpresent
+
+# Directories potentially created on remote AFP share
+.AppleDB
+.AppleDesktop
+Network Trash Folder
+Temporary Items
+.apdisk
+
+##### Backup
+*.bak
+*.gho
+*.ori
+*.orig
+*.tmp
+
+##### GPG
+secring.*
+
+##### Dropbox
+# Dropbox settings and caches
+.dropbox
+.dropbox.attr
+.dropbox.cache
+
+##### SynopsysVCS
+# Waveform formats
+*.vcd
+*.vpd
+*.evcd
+*.fsdb
+
+# Default name of the simulation executable.  A different name can be
+# specified with this switch (the associated daidir database name is
+# also taken from here):  -o <path>/<filename>
+simv
+
+# Generated for Verilog and VHDL top configs
+simv.daidir/
+simv.db.dir/
+
+# Infrastructure necessary to co-simulate SystemC models with
+# Verilog/VHDL models.  An alternate directory may be specified with this
+# switch:  -Mdir=<directory_path>
+csrc/
+
+# Log file - the following switch allows to specify the file that will be
+# used to write all messages from simulation:  -l <filename>
+*.log
+
+# Coverage results (generated with urg) and database location.  The
+# following switch can also be used:  urg -dir <coverage_directory>.vdb
+simv.vdb/
+urgReport/
+
+# DVE and UCLI related files.
+DVEfiles/
+ucli.key
+
+# When the design is elaborated for DirectC, the following file is created
+# with declarations for C/C++ functions.
+vc_hdrs.h
+
+##### SVN
+.svn/
+
+##### Mercurial
+.hg/
+.hgignore
+.hgsigs
+.hgsub
+.hgsubstate
+.hgtags
+
+##### Bazaar
+.bzr/
+.bzrignore
+
+##### CVS
+/CVS/*
+**/CVS/*
+.cvsignore
+*/.cvsignore
+
+##### TortoiseGit
+# Project-level settings
+/.tgitconfig
+
+##### PuTTY
+# Private key
+*.ppk
+
+##### Vim
+# Swap
+[._]*.s[a-v][a-z]
+!*.svg  # comment out if you don't need vector files
+[._]*.sw[a-p]
+[._]s[a-rt-v][a-z]
+[._]ss[a-gi-z]
+[._]sw[a-p]
+
+# Session
+Session.vim
+Sessionx.vim
+
+# Temporary
+.netrwhist
+*~
+# Auto-generated tag files
+tags
+# Persistent undo
+[._]*.un~
+
+##### Emacs
+# -*- mode: gitignore; -*-
+*~
+\#*\#
+/.emacs.desktop
+/.emacs.desktop.lock
+*.elc
+auto-save-list
+tramp
+.\#*
+
+# Org-mode
+.org-id-locations
+*_archive
+
+# flymake-mode
+*_flymake.*
+
+# eshell files
+/eshell/history
+/eshell/lastdir
+
+# elpa packages
+/elpa/
+
+# reftex files
+*.rel
+
+# AUCTeX auto folder
+/auto/
+
+# cask packages
+.cask/
+dist/
+
+# Flycheck
+flycheck_*.el
+
+# server auth directory
+/server/
+
+# projectiles files
+.projectile
+
+# directory configuration
+.dir-locals.el
+
+# network security
+/network-security.data
+
+##### SublimeText
+# Cache files for Sublime Text
+*.tmlanguage.cache
+*.tmPreferences.cache
+*.stTheme.cache
+
+# Workspace files are user-specific
+*.sublime-workspace
+
+# Project files should be checked into the repository, unless a significant
+# proportion of contributors will probably not be using Sublime Text
+# *.sublime-project
+
+# SFTP configuration file
+sftp-config.json
+sftp-config-alt*.json
+
+# Package control specific files
+Package Control.last-run
+Package Control.ca-list
+Package Control.ca-bundle
+Package Control.system-ca-bundle
+Package Control.cache/
+Package Control.ca-certs/
+Package Control.merged-ca-bundle
+Package Control.user-ca-bundle
+oscrypto-ca-bundle.crt
+bh_unicode_properties.cache
+
+# Sublime-github package stores a github token in this file
+# https://packagecontrol.io/packages/sublime-github
+GitHub.sublime-settings
+
+##### Notepad++
+# Notepad++ backups #
+*.bak
+
+##### TextMate
+*.tmproj
+*.tmproject
+tmtags
+
+##### VisualStudioCode
+.vscode/*
+!.vscode/settings.json
+!.vscode/tasks.json
+!.vscode/launch.json
+!.vscode/extensions.json
+*.code-workspace
+
+# Local History for Visual Studio Code
+.history/
+
+##### NetBeans
+**/nbproject/private/
+**/nbproject/Makefile-*.mk
+**/nbproject/Package-*.bash
+build/
+nbbuild/
+dist/
+nbdist/
+.nb-gradle/
+
+##### JetBrains
+# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
+# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
+
+# User-specific stuff
+.idea/**/workspace.xml
+.idea/**/tasks.xml
+.idea/**/usage.statistics.xml
+.idea/**/dictionaries
+.idea/**/shelf
+
+# Generated files
+.idea/**/contentModel.xml
+
+# Sensitive or high-churn files
+.idea/**/dataSources/
+.idea/**/dataSources.ids
+.idea/**/dataSources.local.xml
+.idea/**/sqlDataSources.xml
+.idea/**/dynamic.xml
+.idea/**/uiDesigner.xml
+.idea/**/dbnavigator.xml
+
+# Gradle
+.idea/**/gradle.xml
+.idea/**/libraries
+
+# Gradle and Maven with auto-import
+# When using Gradle or Maven with auto-import, you should exclude module files,
+# since they will be recreated, and may cause churn.  Uncomment if using
+# auto-import.
+# .idea/artifacts
+# .idea/compiler.xml
+# .idea/jarRepositories.xml
+# .idea/modules.xml
+# .idea/*.iml
+# .idea/modules
+# *.iml
+# *.ipr
+
+# CMake
+cmake-build-*/
+
+# Mongo Explorer plugin
+.idea/**/mongoSettings.xml
+
+# File-based project format
+*.iws
+
+# IntelliJ
+out/
+
+# mpeltonen/sbt-idea plugin
+.idea_modules/
+
+# JIRA plugin
+atlassian-ide-plugin.xml
+
+# Cursive Clojure plugin
+.idea/replstate.xml
+
+# Crashlytics plugin (for Android Studio and IntelliJ)
+com_crashlytics_export_strings.xml
+crashlytics.properties
+crashlytics-build.properties
+fabric.properties
+
+# Editor-based Rest Client
+.idea/httpRequests
+
+# Android studio 3.1+ serialized cache file
+.idea/caches/build_file_checksums.ser
+
+##### Eclipse
+.metadata
+bin/
+tmp/
+*.tmp
+*.bak
+*.swp
+*~.nib
+local.properties
+.settings/
+.loadpath
+.recommenders
+
+# External tool builders
+.externalToolBuilders/
+
+# Locally stored "Eclipse launch configurations"
+*.launch
+
+# PyDev specific (Python IDE for Eclipse)
+*.pydevproject
+
+# CDT-specific (C/C++ Development Tooling)
+.cproject
+
+# CDT- autotools
+.autotools
+
+# Java annotation processor (APT)
+.factorypath
+
+# PDT-specific (PHP Development Tools)
+.buildpath
+
+# sbteclipse plugin
+.target
+
+# Tern plugin
+.tern-project
+
+# TeXlipse plugin
+.texlipse
+
+# STS (Spring Tool Suite)
+.springBeans
+
+# Code Recommenders
+.recommenders/
+
+# Annotation Processing
+.apt_generated/
+.apt_generated_test/
+
+# Scala IDE specific (Scala & Java development for Eclipse)
+.cache-main
+.scala_dependencies
+.worksheet
+
+# Uncomment this line if you wish to ignore the project description file.
+# Typically, this file would be tracked if it contains build/dependency configurations:
+#.project
+
+##### Dreamweaver
+# DW Dreamweaver added files
+_notes
+_compareTemp
+configs/
+dwsync.xml
+dw_php_codehinting.config
+*.mno
+
+##### CodeKit
+# General CodeKit files to ignore
+config.codekit
+config.codekit3
+/min
+
+##### Gradle
+.gradle
+**/build/
+!src/**/build/
+
+# Ignore Gradle GUI config
+gradle-app.setting
+
+# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
+!gradle-wrapper.jar
+
+# Cache of project
+.gradletasknamecache
+
+# # Work around https://youtrack.jetbrains.com/issue/IDEA-116898
+# gradle/wrapper/gradle-wrapper.properties
+
+##### Composer
+composer.phar
+/vendor/
+
+# Commit your application's lock file https://getcomposer.org/doc/01-basic-usage.md#commit-your-composer-lock-file-to-version-control
+# You may choose to ignore a library lock file http://getcomposer.org/doc/02-libraries.md#lock-file
+composer.lock
+
+##### PHP CodeSniffer
+# gitignore for the PHP Codesniffer framework
+# website: https://github.com/squizlabs/PHP_CodeSniffer
+#
+# Recommended template: PHP.gitignore
+
+/wpcs/*
+
+##### SASS
+.sass-cache/
+*.css.map
+*.sass.map
+*.scss.map
diff --git a/imagediff/LICENSE b/imagediff/LICENSE
new file mode 100644
index 0000000..d9a10c0
--- /dev/null
+++ b/imagediff/LICENSE
@@ -0,0 +1,176 @@
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
diff --git a/imagediff/NOTICE b/imagediff/NOTICE
new file mode 100644
index 0000000..8995df6
--- /dev/null
+++ b/imagediff/NOTICE
@@ -0,0 +1,13 @@
+imagediff
+Copyright 2022 Abhinav Chennubhotla
+
+imagediff was originally developed and maintained by Abhinav Chennubhotla
+
+Portions of this software were developed by various contributors, who retain
+copyright on their work. These works are licensed to Abhinav Chennubhotla
+
+imagediff is available under the Apache 2.0 license. See LICENSE for more
+information.
+
+This software uses other open source libraries. These libraries have their own
+licenses and copyright holders.
diff --git a/imagediff/README.md b/imagediff/README.md
new file mode 100644
index 0000000..67835a2
--- /dev/null
+++ b/imagediff/README.md
@@ -0,0 +1,11 @@
+# ImageDiff
+
+### Running
+
+The server can be run using
+
+    php -S localhost:8000
+
+The server will be hosted on port 8000. Visit http://localhost:8000/index.html to access the website.
+
+##### This project is  maintained by [Abhinav Chennubhotla](https://github.com/PhoenixFlame101).
diff --git a/imagediff/assets/1.png b/imagediff/assets/1.png
new file mode 100644
index 0000000..f314d43
Binary files /dev/null and b/imagediff/assets/1.png differ
diff --git a/imagediff/assets/2.png b/imagediff/assets/2.png
new file mode 100644
index 0000000..7081d2d
Binary files /dev/null and b/imagediff/assets/2.png differ
diff --git a/imagediff/assets/styles/main.css b/imagediff/assets/styles/main.css
new file mode 100644
index 0000000..46e764c
--- /dev/null
+++ b/imagediff/assets/styles/main.css
@@ -0,0 +1,3 @@
+.compare {
+    width: 33%;
+}
diff --git a/imagediff/index.html b/imagediff/index.html
new file mode 100644
index 0000000..15f6aca
--- /dev/null
+++ b/imagediff/index.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <title>ImageDiff</title>
+
+    <meta charset="UTF-8" />
+    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+    <link
+      rel="stylesheet"
+      type="text/css"
+      href="assets/styles/main.css"
+    />
+  </head>
+
+  <body>
+    <img class="compare" src="assets/1.png" />
+    <img class="compare" src="src/imagediff.php" />
+    <img class="compare" src="assets/2.png" />
+  </body>
+</html>
diff --git a/imagediff/src/imagediff.php b/imagediff/src/imagediff.php
new file mode 100644
index 0000000..b740ca5
--- /dev/null
+++ b/imagediff/src/imagediff.php
@@ -0,0 +1,51 @@
+<?php
+header('Content-Type: image/png');
+function pixelDiff(GDImage &$res, int $pixel1, int $pixel2) {
+    $r1 = ($pixel1 >> 16) & 0xFF;
+    $g1 = ($pixel1 >> 8) & 0xFF;
+    $b1 = $pixel1 & 0xFF;
+
+    $r2 = ($pixel2 >> 16) & 0xFF;
+    $g2 = ($pixel2 >> 8) & 0xFF;
+    $b2 = $pixel2 & 0xFF;
+
+    $r = abs($r1 - $r2);
+    $g = abs($g1 - $g2);
+    $b = abs($b1 - $b2);
+
+    return imagecolorallocate($res, $r, $g, $b);
+}
+
+function imageDiff($path1, $path2) {
+    $img1 = imagecreatefrompng($path1);
+    $img2 = imagecreatefrompng($path2);
+    $width = imagesx($img1);
+    $height = imagesy($img1);
+
+    $res = @imagecreatetruecolor($width, $height)
+        or die("Cannot Initialize new GD image stream");
+    $bg = imagecolorallocate($res, 0, 0, 0);
+
+    for ($i = 0; $i < $width; $i++) {
+        for ($j = 0; $j < $height; $j++) {
+            imagesetpixel($res, $i, $j, pixelDiff(
+                $res,
+                imagecolorat($img1, $i, $j),
+                imagecolorat($img2, $i, $j)
+            ));
+        }
+    }
+    ob_start();
+
+    imagepng($res);
+    $image_data = ob_get_contents();
+    imagedestroy($res);
+
+    ob_end_clean();
+
+    return base64_encode($image_data);
+}
+
+imageDiff("../assets/1.png", "../assets/2.png");
+?>
+


Commit: a076c4e60c7962f5754d845d0e7a123b07aa3c85
    https://github.com/scummvm/scummvm-sites/commit/a076c4e60c7962f5754d845d0e7a123b07aa3c85
Author: Abhinav Chennubhotla (51199011+PhoenixFlame101 at users.noreply.github.com)
Date: 2026-06-11T19:13:11+02:00

Commit Message:
IMAGEDIFF: Update indentation

Changed paths:
    imagediff/src/imagediff.php


diff --git a/imagediff/src/imagediff.php b/imagediff/src/imagediff.php
index b740ca5..a8667b3 100644
--- a/imagediff/src/imagediff.php
+++ b/imagediff/src/imagediff.php
@@ -1,49 +1,49 @@
 <?php
 header('Content-Type: image/png');
 function pixelDiff(GDImage &$res, int $pixel1, int $pixel2) {
-    $r1 = ($pixel1 >> 16) & 0xFF;
-    $g1 = ($pixel1 >> 8) & 0xFF;
-    $b1 = $pixel1 & 0xFF;
+  $r1 = ($pixel1 >> 16) & 0xFF;
+  $g1 = ($pixel1 >> 8) & 0xFF;
+  $b1 = $pixel1 & 0xFF;
 
-    $r2 = ($pixel2 >> 16) & 0xFF;
-    $g2 = ($pixel2 >> 8) & 0xFF;
-    $b2 = $pixel2 & 0xFF;
+  $r2 = ($pixel2 >> 16) & 0xFF;
+  $g2 = ($pixel2 >> 8) & 0xFF;
+  $b2 = $pixel2 & 0xFF;
 
-    $r = abs($r1 - $r2);
-    $g = abs($g1 - $g2);
-    $b = abs($b1 - $b2);
+  $r = abs($r1 - $r2);
+  $g = abs($g1 - $g2);
+  $b = abs($b1 - $b2);
 
-    return imagecolorallocate($res, $r, $g, $b);
+  return imagecolorallocate($res, $r, $g, $b);
 }
 
 function imageDiff($path1, $path2) {
-    $img1 = imagecreatefrompng($path1);
-    $img2 = imagecreatefrompng($path2);
-    $width = imagesx($img1);
-    $height = imagesy($img1);
-
-    $res = @imagecreatetruecolor($width, $height)
-        or die("Cannot Initialize new GD image stream");
-    $bg = imagecolorallocate($res, 0, 0, 0);
-
-    for ($i = 0; $i < $width; $i++) {
-        for ($j = 0; $j < $height; $j++) {
-            imagesetpixel($res, $i, $j, pixelDiff(
-                $res,
-                imagecolorat($img1, $i, $j),
-                imagecolorat($img2, $i, $j)
-            ));
-        }
+  $img1 = imagecreatefrompng($path1);
+  $img2 = imagecreatefrompng($path2);
+  $width = imagesx($img1);
+  $height = imagesy($img1);
+
+  $res = @imagecreatetruecolor($width, $height)
+    or die("Cannot Initialize new GD image stream");
+  $bg = imagecolorallocate($res, 0, 0, 0);
+
+  for ($i = 0; $i < $width; $i++) {
+    for ($j = 0; $j < $height; $j++) {
+      imagesetpixel($res, $i, $j, pixelDiff(
+        $res,
+        imagecolorat($img1, $i, $j),
+        imagecolorat($img2, $i, $j)
+      ));
     }
-    ob_start();
+  }
+  ob_start();
 
-    imagepng($res);
-    $image_data = ob_get_contents();
-    imagedestroy($res);
+  imagepng($res);
+  //   $image_data = ob_get_contents();
+  imagedestroy($res);
 
-    ob_end_clean();
+  //   ob_end_clean();
 
-    return base64_encode($image_data);
+  //   return base64_encode($image_data);
 }
 
 imageDiff("../assets/1.png", "../assets/2.png");


Commit: 55c489312c5920d55c55056a142dd642a5f1740f
    https://github.com/scummvm/scummvm-sites/commit/55c489312c5920d55c55056a142dd642a5f1740f
Author: Abhinav Chennubhotla (51199011+PhoenixFlame101 at users.noreply.github.com)
Date: 2026-06-11T19:13:16+02:00

Commit Message:
IMAGEDIFF: Complete basic feature set for ImageDiff

 - Modify imagediff.php to return base64 image
 - Add file browser
 - Change landing page from index.html to index.php

Changed paths:
  A imagediff/index.php
  A imagediff/src/display_images.php
  A imagediff/src/file_browser.php
  R imagediff/assets/1.png
  R imagediff/assets/2.png
  R imagediff/index.html
    imagediff/README.md
    imagediff/src/imagediff.php


diff --git a/imagediff/README.md b/imagediff/README.md
index 67835a2..4fc7461 100644
--- a/imagediff/README.md
+++ b/imagediff/README.md
@@ -6,6 +6,6 @@ The server can be run using
 
     php -S localhost:8000
 
-The server will be hosted on port 8000. Visit http://localhost:8000/index.html to access the website.
+The server will be hosted on port 8000. Visit http://localhost:8000/ to access the website.
 
 ##### This project is  maintained by [Abhinav Chennubhotla](https://github.com/PhoenixFlame101).
diff --git a/imagediff/assets/1.png b/imagediff/assets/1.png
deleted file mode 100644
index f314d43..0000000
Binary files a/imagediff/assets/1.png and /dev/null differ
diff --git a/imagediff/assets/2.png b/imagediff/assets/2.png
deleted file mode 100644
index 7081d2d..0000000
Binary files a/imagediff/assets/2.png and /dev/null differ
diff --git a/imagediff/index.html b/imagediff/index.html
deleted file mode 100644
index 15f6aca..0000000
--- a/imagediff/index.html
+++ /dev/null
@@ -1,20 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-  <head>
-    <title>ImageDiff</title>
-
-    <meta charset="UTF-8" />
-    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
-    <link
-      rel="stylesheet"
-      type="text/css"
-      href="assets/styles/main.css"
-    />
-  </head>
-
-  <body>
-    <img class="compare" src="assets/1.png" />
-    <img class="compare" src="src/imagediff.php" />
-    <img class="compare" src="assets/2.png" />
-  </body>
-</html>
diff --git a/imagediff/index.php b/imagediff/index.php
new file mode 100644
index 0000000..746804f
--- /dev/null
+++ b/imagediff/index.php
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html lang="en">
+
+<head>
+  <title>ImageDiff</title>
+
+  <meta charset="UTF-8" />
+  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+  <link rel="stylesheet" type="text/css" href="assets/styles/main.css" />
+
+  <?php require __DIR__ . '/src/file_browser.php' ?>
+</head>
+
+<body>
+  <div class="file-browser">
+    <?php displayDir(isset($_GET['dir']) ? $_GET['dir'] : '.') ?>
+  </div>
+</body>
+
+</html>
diff --git a/imagediff/src/display_images.php b/imagediff/src/display_images.php
new file mode 100644
index 0000000..1364f95
--- /dev/null
+++ b/imagediff/src/display_images.php
@@ -0,0 +1,48 @@
+<link rel="stylesheet" type="text/css" href="../assets/styles/main.css" />
+
+<?php
+require __DIR__ . "/imagediff.php";
+
+$src_img_path = isset($_GET['image']) ? $_GET['image'] : '';
+if (!$src_img_path) {
+  echo "<h2>Invalid Image</h2>";
+}
+
+$path_parts = explode(DIRECTORY_SEPARATOR, $src_img_path);
+$target_dir = join("/", array_slice($path_parts, 0, count($path_parts) - 2));
+$src_filename = end($path_parts);
+
+$builds = scandir($target_dir);
+rsort($builds);
+
+$loop_count = 0;
+foreach ($builds as $build) {
+  $build_path = $target_dir . "/" . $build;
+  if (!is_dir($build_path) or in_array($build, array(".", "..")))
+    continue;
+
+  $files = scandir($build_path);
+
+  foreach ($files as $file) {
+    if ($loop_count >= 3)
+      break;
+
+    $cmp_img_path = $build_path . "/" . $file;
+    if ($file == $src_filename and $cmp_img_path != $src_img_path) {
+      $res = image_diff($src_img_path, $cmp_img_path);
+      // error_log("<img class=\"compare\" src=\"" . get_relative_path(realpath("./"), $cmp_img_path) . "\" />");
+
+      echo "<img class=\"compare\" src=\"data:image/png;base64," .
+        base64_encode(file_get_contents($src_img_path)) . "\" />";
+      echo "<img class=\"compare\" src=\"data:image/png;base64," . $res . "\" />";
+      echo "<img class=\"compare\" src=\"data:image/png;base64," .
+        base64_encode(file_get_contents($cmp_img_path)) . "\" />";
+      echo "<p />";
+
+      $loop_count++;
+    }
+  }
+}
+
+?>
+
diff --git a/imagediff/src/file_browser.php b/imagediff/src/file_browser.php
new file mode 100644
index 0000000..f7c9789
--- /dev/null
+++ b/imagediff/src/file_browser.php
@@ -0,0 +1,63 @@
+<?php function displayDir($dir = './') { ?>
+  <?php
+  function file_sort($a, $b) {
+    return ($cmp = is_dir($b) - is_dir($a)) ? $cmp : strcmp($a, $b);
+  }
+
+  $current_dir = realpath($dir);
+  $dir_content = scandir($current_dir);
+  usort($dir_content, 'file_sort');
+  ?>
+
+  <div>
+    <?php
+    $path_parts = explode(DIRECTORY_SEPARATOR, $current_dir);
+    $breadcrumbs = '';
+    foreach ($path_parts as $part) {
+      if (!$part) {
+        continue;
+      }
+      $breadcrumbs .= $part . DIRECTORY_SEPARATOR;
+      echo DIRECTORY_SEPARATOR . '<a href="?dir=' . '/' . $breadcrumbs . '">' . $part . '</a>';
+    }
+    ?>
+  </div>
+
+  <table>
+    <thead>
+      <tr>
+        <th>Name</th>
+        <th>Size</th>
+      </tr>
+    </thead>
+    <tbody>
+      <?php foreach ($dir_content as $item):
+
+        if ($item == '.'):
+          continue;
+        endif;
+        $item_path = $current_dir . DIRECTORY_SEPARATOR . $item;
+        ?>
+        <tr>
+          <td>
+            <?php if (is_dir($item_path)): ?>
+              <a href="?dir=<?php echo $item_path; ?>">
+                <?php echo $item; ?>
+              </a>
+            <?php elseif (getimagesize($item_path)): ?>
+              <a href="src/display_images.php?image=<?php echo $item_path; ?>">
+                <?php echo $item; ?>
+              </a>
+            <?php else: ?>
+              <?php echo $item; ?>
+            <?php endif; ?>
+          </td>
+          <td>
+            <?php echo is_dir($item_path) ? '' : filesize($item_path) . ' b'; ?>
+          </td>
+        </tr>
+      <?php endforeach; ?>
+    </tbody>
+  </table>
+<?php } ?>
+
diff --git a/imagediff/src/imagediff.php b/imagediff/src/imagediff.php
index a8667b3..677a66c 100644
--- a/imagediff/src/imagediff.php
+++ b/imagediff/src/imagediff.php
@@ -1,6 +1,6 @@
 <?php
-header('Content-Type: image/png');
-function pixelDiff(GDImage &$res, int $pixel1, int $pixel2) {
+
+function pixel_diff(GDImage &$res, int $pixel1, int $pixel2) {
   $r1 = ($pixel1 >> 16) & 0xFF;
   $g1 = ($pixel1 >> 8) & 0xFF;
   $b1 = $pixel1 & 0xFF;
@@ -16,7 +16,7 @@ function pixelDiff(GDImage &$res, int $pixel1, int $pixel2) {
   return imagecolorallocate($res, $r, $g, $b);
 }
 
-function imageDiff($path1, $path2) {
+function image_diff($path1, $path2) {
   $img1 = imagecreatefrompng($path1);
   $img2 = imagecreatefrompng($path2);
   $width = imagesx($img1);
@@ -28,24 +28,20 @@ function imageDiff($path1, $path2) {
 
   for ($i = 0; $i < $width; $i++) {
     for ($j = 0; $j < $height; $j++) {
-      imagesetpixel($res, $i, $j, pixelDiff(
+      imagesetpixel($res, $i, $j, pixel_diff(
         $res,
         imagecolorat($img1, $i, $j),
         imagecolorat($img2, $i, $j)
       ));
     }
   }
-  ob_start();
 
+  ob_start();
   imagepng($res);
-  //   $image_data = ob_get_contents();
-  imagedestroy($res);
 
-  //   ob_end_clean();
-
-  //   return base64_encode($image_data);
+  return base64_encode(ob_get_clean());
 }
 
-imageDiff("../assets/1.png", "../assets/2.png");
+
 ?>
 


Commit: abed4495852b40a80b975f2b16adc04c73fd6b88
    https://github.com/scummvm/scummvm-sites/commit/abed4495852b40a80b975f2b16adc04c73fd6b88
Author: Chako (chaosbeiduo at outlook.com)
Date: 2026-06-11T19:13:21+02:00

Commit Message:
IMAGEDIFF: Add Index Page with Target Names Listing

Changed paths:
  A imagediff/ImageDiffwithPython/config.py
  A imagediff/ImageDiffwithPython/main.py
  A imagediff/ImageDiffwithPython/static/css/style.css
  A imagediff/ImageDiffwithPython/templates/index.html


diff --git a/imagediff/ImageDiffwithPython/config.py b/imagediff/ImageDiffwithPython/config.py
new file mode 100644
index 0000000..d5d624a
--- /dev/null
+++ b/imagediff/ImageDiffwithPython/config.py
@@ -0,0 +1,3 @@
+import os
+
+SCREENSHOTS_DIR = os.environ.get("SCREENSHOTS_DIR", "./screenshots/")
diff --git a/imagediff/ImageDiffwithPython/main.py b/imagediff/ImageDiffwithPython/main.py
new file mode 100644
index 0000000..6a07607
--- /dev/null
+++ b/imagediff/ImageDiffwithPython/main.py
@@ -0,0 +1,545 @@
+import os
+
+from PIL import Image
+from flask import Flask, render_template, jsonify, url_for, send_from_directory
+
+from config import SCREENSHOTS_DIR
+from imagediff import image_diff, encode_image, movie_diff
+
+app = Flask(__name__)
+
+def get_frame_number(filename):
+    """Extract frame number from filename."""
+    parts = filename.split('-')
+    if len(parts) > 1:
+        try:
+            return int(parts[1].split('.')[0])
+        except ValueError:
+            return 0
+    return 0
+
+def get_sorted_builds(target_path, reverse=True):
+    """Get sorted list of builds for a target."""
+    builds = [b for b in os.listdir(target_path)
+              if os.path.isdir(os.path.join(target_path, b))]
+    return sorted(builds, reverse=reverse)
+
+def collect_movie_frames(target_path, builds):
+    """Collect all movie frames information for all builds."""
+    all_movies = set()
+    build_movie_frames = {}
+    build_files = {}
+
+    # Pre-collect file information to avoid multiple directory reads
+    for build in builds:
+        build_path = os.path.join(target_path, build)
+        build_files[build] = os.listdir(build_path)
+
+    # Process movie and frame data
+    for build in builds:
+        build_movie_frames[build] = {}
+
+        for file in build_files[build]:
+            if not os.path.isfile(os.path.join(target_path, build, file)):
+                continue
+
+            if "-" in file:
+                parts = file.split("-")
+                movie_name = parts[0]
+                frame_num = parts[1].split('.')[0]
+
+                if movie_name not in build_movie_frames[build]:
+                    build_movie_frames[build][movie_name] = []
+
+                build_movie_frames[build][movie_name].append(frame_num)
+                all_movies.add(movie_name)
+
+    return all_movies, build_movie_frames, build_files
+
+def find_first_build_for_movies(all_movies, builds_ascending, build_movie_frames):
+    """Find the first build where each movie appears."""
+    first_build_for_movie = {}
+    for movie in all_movies:
+        for build in builds_ascending:
+            if movie in build_movie_frames.get(build, {}):
+                first_build_for_movie[movie] = build
+                break
+    return first_build_for_movie
+
+def calculate_reference_builds(all_movies, builds, build_movie_frames):
+    """Calculate reference builds for each movie in each build."""
+    movie_reference_builds = {}
+    for movie in all_movies:
+        movie_reference_builds[movie] = {}
+        for i, current_build in enumerate(builds):
+            has_in_current = movie in build_movie_frames.get(current_build, {})
+
+            if not has_in_current:
+                reference_build = None
+                for j in range(i+1, len(builds)):
+                    if movie in build_movie_frames.get(builds[j], {}):
+                        reference_build = builds[j]
+                        break
+                movie_reference_builds[movie][current_build] = reference_build
+    return movie_reference_builds
+
+def get_movie_frames(build_path, movie_prefix=None):
+    """Get all frames for a movie in a build path."""
+    all_files = [f for f in os.listdir(build_path)
+                 if os.path.isfile(os.path.join(build_path, f))]
+
+    if movie_prefix:
+        # Filter files for specific movie
+        movie_files = [f for f in all_files if f.startswith(f"{movie_prefix}-")]
+    else:
+        # Get all movie files
+        movie_files = [f for f in all_files if "-" in f]
+
+    return sorted(movie_files, key=get_frame_number)
+
+def extract_movie_names(files):
+    """Extract unique movie names from a list of files."""
+    movies = set()
+    for file in files:
+        if "-" in file:
+            movie_name = file.split("-")[0]
+            movies.add(movie_name)
+    return movies
+
+def create_frame_map(frames):
+    """Create a map of frame numbers to filenames."""
+    return {get_frame_number(f): f for f in frames}
+
+ at app.route('/')
+def index():
+    targets = [d for d in os.listdir(SCREENSHOTS_DIR)
+               if os.path.isdir(os.path.join(SCREENSHOTS_DIR, d))]
+
+    return render_template('index.html', targets=targets)
+
+ at app.route('/target/<target>')
+def target_detail(target):
+    target_path = os.path.join(SCREENSHOTS_DIR, target)
+
+    if not os.path.exists(target_path) or not os.path.isdir(target_path):
+        return "Target not found", 404
+
+    # Only get the build list, other time-consuming operations moved to API
+    builds = get_sorted_builds(target_path)
+
+    # Only return the page framework with build list but without table data
+    return render_template('target.html',
+                           target=target,
+                           builds=builds)
+
+# Handle time-consuming data calculations
+ at app.route('/api/target_data/<target>')
+def target_data_api(target):
+    target_path = os.path.join(SCREENSHOTS_DIR, target)
+
+    if not os.path.exists(target_path) or not os.path.isdir(target_path):
+        return jsonify({"error": "Target not found"}), 404
+
+    builds = get_sorted_builds(target_path)
+    builds_ascending = get_sorted_builds(target_path, reverse=False)
+
+    # Collect movie frame data
+    all_movies, build_movie_frames, _ = collect_movie_frames(target_path, builds)
+
+    # Calculate first build for each movie
+    first_build_for_movie = find_first_build_for_movies(
+        all_movies, builds_ascending, build_movie_frames)
+
+    # Calculate reference builds
+    movie_reference_builds = {}
+    for movie in all_movies:
+        movie_reference_builds[movie] = {}
+        for i, current_build in enumerate(builds):
+            has_in_current = movie in build_movie_frames.get(current_build, {})
+            current_frames = build_movie_frames.get(current_build, {}).get(movie, [])
+
+            if not has_in_current:
+                # Reference build needs to have the movie
+                reference_build = None
+                for j in range(i+1, len(builds)):
+                    next_build = builds[j]
+                    if movie in build_movie_frames.get(next_build, {}):
+                        reference_build = next_build
+                        break
+                movie_reference_builds[movie][current_build] = {
+                    'build': reference_build,
+                    'frames': []  # Empty since current build doesn't have the movie
+                }
+            else:
+                # Current build has the movie, find a reference build with matching frames
+                reference_data = {
+                    'build': None,
+                    'frames': current_frames
+                }
+
+                for j in range(i+1, len(builds)):
+                    next_build = builds[j]
+                    if movie in build_movie_frames.get(next_build, {}):
+                        next_frames = build_movie_frames.get(next_build, {}).get(movie, [])
+
+                        # Check if current frames exist in the next build
+                        common_frames = set(current_frames).intersection(set(next_frames))
+                        if common_frames:
+                            reference_data = {
+                                'build': next_build,
+                                'frames': list(common_frames)
+                            }
+                            break
+
+                movie_reference_builds[movie][current_build] = reference_data
+
+    # Pre-calculate image difference results
+    image_diff_cache = {}
+    def get_image_diff(current_build, prev_build, movie, frame):
+        cache_key = (current_build, prev_build, movie, frame)
+        if cache_key not in image_diff_cache:
+            current_frame_path = os.path.join(target_path, current_build, f"{movie}-{frame}.png")
+            prev_frame_path = os.path.join(target_path, prev_build, f"{movie}-{frame}.png")
+
+            if os.path.exists(current_frame_path) and os.path.exists(prev_frame_path):
+                try:
+                    image_diff_cache[cache_key] = image_diff(current_frame_path, prev_frame_path)
+                except Exception as e:
+                    print(f"Error comparing frames {movie}-{frame}: {e}")
+                    image_diff_cache[cache_key] = {'has_diff': True}
+            else:
+                image_diff_cache[cache_key] = {'has_diff': True}
+
+        return image_diff_cache[cache_key]
+
+    # Modified movie difference function to only compare specified frames
+    def get_movie_diff_for_frames(current_build, reference_build, target, movie, frames_to_compare):
+        if not frames_to_compare:
+            return False  # No frames to compare
+
+        has_any_diff = False
+        for frame in frames_to_compare:
+            diff_result = get_image_diff(current_build, reference_build, movie, frame)
+            if diff_result.get('has_diff', False):
+                has_any_diff = True
+                break
+
+        return has_any_diff
+
+    # Pre-calculate movie difference results
+    movie_diff_cache = {}
+    def get_movie_diff_cached(current_build, prev_build, target, movie):
+        cache_key = (current_build, prev_build, target, movie)
+        if cache_key not in movie_diff_cache:
+            movie_diff_cache[cache_key] = movie_diff(current_build, prev_build, target, movie)
+        return movie_diff_cache[cache_key]
+
+    movies = sorted(list(all_movies))
+    continuous_bars = {}
+
+    # Create continuous bars for visualization with updated skip logic
+    for movie in movies:
+        continuous_bars[movie] = []
+
+        for i, current_build in enumerate(builds):
+            prev_build = builds[i+1] if i < len(builds)-1 else None
+
+            has_in_current = movie in build_movie_frames.get(current_build, {})
+            current_frames = build_movie_frames.get(current_build, {}).get(movie, [])
+
+            prev_frames = []
+            if prev_build:
+                prev_frames = build_movie_frames.get(prev_build, {}).get(movie, [])
+
+            has_in_prev = len(prev_frames) > 0 if prev_build else False
+            is_first_build = (current_build == first_build_for_movie.get(movie))
+
+            # Build entry information
+            if is_first_build:
+                continuous_bars[movie].append({
+                    'build': current_build,
+                    'type': 'first'
+                })
+            elif not has_in_current and prev_build:
+                # Modified skip build logic
+                reference_data = movie_reference_builds[movie].get(current_build, {'build': None, 'frames': []})
+                reference_build = reference_data['build']
+
+                if reference_build:
+                    continuous_bars[movie].append({
+                        'build': current_build,
+                        'reference_build': reference_build,
+                        'type': 'diff',
+                        'has_diff': False,  # No diff since current build doesn't have the movie
+                        'is_skipped': True,
+                        'compare_with': reference_build
+                    })
+                else:
+                    continuous_bars[movie].append({
+                        'build': current_build,
+                        'type': 'missing'
+                    })
+            elif has_in_current and prev_build:
+                if has_in_prev:
+                    common_frames = set(current_frames).intersection(set(prev_frames))
+
+                    if len(current_frames) < len(prev_frames):
+                        # End difference check loop early
+                        has_any_diff = False
+                        for frame in common_frames:
+                            diff_result = get_image_diff(current_build, prev_build, movie, frame)
+                            if diff_result.get('has_diff', False):
+                                has_any_diff = True
+                                break
+
+                        continuous_bars[movie].append({
+                            'build': current_build,
+                            'has_diff': has_any_diff,
+                            'compare_with': prev_build,
+                            'type': 'diff',
+                            'is_partial': True
+                        })
+                    else:
+                        has_diff = get_movie_diff_cached(current_build, prev_build, target, movie)
+
+                        continuous_bars[movie].append({
+                            'build': current_build,
+                            'has_diff': has_diff,
+                            'compare_with': prev_build,
+                            'type': 'diff'
+                        })
+                else:
+                    # Modified readded build logic
+                    reference_data = movie_reference_builds[movie].get(current_build, {'build': None, 'frames': []})
+                    reference_build = reference_data['build']
+                    comparable_frames = reference_data['frames']
+
+                    has_diff = False
+                    if reference_build and comparable_frames:
+                        has_diff = get_movie_diff_for_frames(current_build, reference_build, target, movie, comparable_frames)
+
+                    continuous_bars[movie].append({
+                        'build': current_build,
+                        'type': 'readded',
+                        'compare_with': reference_build,
+                        'common_frames': comparable_frames,
+                        'has_diff': has_diff,
+                        'no_reference': reference_build is None
+                    })
+            elif not prev_build:
+                continuous_bars[movie].append({
+                    'build': current_build,
+                    'type': 'unknown'
+                })
+
+    # Generate URL templates needed by frontend
+    urls = {
+        'movie_url': url_for('movie', movie='MOVIE_PLACEHOLDER'),
+        'compare_url': url_for('compare',
+                               build1='BUILD1_PLACEHOLDER',
+                               build2='BUILD2_PLACEHOLDER',
+                               target='TARGET_PLACEHOLDER',
+                               movie='MOVIE_PLACEHOLDER'),
+        'single_build_url': url_for('view_single_build',
+                                    build='BUILD_PLACEHOLDER',
+                                    target='TARGET_PLACEHOLDER',
+                                    movie='MOVIE_PLACEHOLDER')
+    }
+
+    # Return all data to frontend
+    data = {
+        'target': target,
+        'builds': builds,
+        'movies': movies,
+        'continuous_bars': continuous_bars,
+        'urls': urls
+    }
+
+    return jsonify(data)
+
+ at app.route('/movie/<movie>')
+def movie(movie):
+    target_builds = {}
+    all_builds = set()
+    diff_matrix = {}
+
+    # Scan through all targets
+    for target in os.listdir(SCREENSHOTS_DIR):
+        target_path = os.path.join(SCREENSHOTS_DIR, target)
+        if os.path.isdir(target_path):
+            # Get sorted builds
+            builds = get_sorted_builds(target_path)
+
+            # Find builds containing this movie
+            builds_with_movie = []
+            for build in builds:
+                build_path = os.path.join(target_path, build)
+                movie_files = get_movie_frames(build_path, movie)
+
+                if movie_files:
+                    builds_with_movie.append(build)
+                    all_builds.add(build)
+
+            # Calculate diffs between consecutive builds
+            for i in range(len(builds_with_movie) - 1):
+                current_build = builds_with_movie[i]
+                prev_build = builds_with_movie[i + 1]
+
+                has_diff = movie_diff(current_build, prev_build, target, movie)
+
+                if target not in diff_matrix:
+                    diff_matrix[target] = {}
+
+                diff_matrix[target][(current_build, prev_build)] = has_diff
+
+            if builds_with_movie:
+                target_builds[target] = builds_with_movie
+
+    all_builds = sorted(list(all_builds), reverse=True)
+
+    return render_template('movie.html',
+                           movie=movie,
+                           target_builds=target_builds,
+                           all_builds=all_builds,
+                           diff_matrix=diff_matrix)
+
+ at app.route('/build/<build>')
+def build(build):
+    target_info = {}
+
+    for target in os.listdir(SCREENSHOTS_DIR):
+        target_path = os.path.join(SCREENSHOTS_DIR, target)
+        if not os.path.isdir(target_path):
+            continue
+
+        # Get sorted builds
+        builds = get_sorted_builds(target_path)
+
+        # Check if current build exists in this target
+        if build not in builds:
+            continue
+
+        # Find the previous build
+        build_index = builds.index(build)
+        prev_build = builds[build_index + 1] if build_index < len(builds) - 1 else None
+
+        # Find all movies in this build
+        build_path = os.path.join(target_path, build)
+        movie_files = get_movie_frames(build_path)
+        movies = extract_movie_names(movie_files)
+
+        # Calculate differences for each movie
+        movie_diffs = {}
+        if prev_build:
+            for movie in movies:
+                has_diff = movie_diff(build, prev_build, target, movie)
+                movie_diffs[movie] = has_diff
+
+        # Store target information
+        target_info[target] = {
+            'prev_build': prev_build,
+            'movies': sorted(list(movies)),
+            'movie_diffs': movie_diffs
+        }
+
+    return render_template('build.html',
+                           build=build,
+                           target_info=target_info)
+
+ at app.route('/compare/<build1>/<build2>/<target>/<movie>')
+def compare(build1, build2, target, movie):
+    build1_path = os.path.join(SCREENSHOTS_DIR, target, build1)
+    build2_path = os.path.join(SCREENSHOTS_DIR, target, build2)
+
+    # Get all frames for this movie in both builds
+    build1_frames = get_movie_frames(build1_path, movie)
+    build2_frames = get_movie_frames(build2_path, movie)
+
+    # Map frame numbers to filenames for easy lookup
+    build1_frame_map = create_frame_map(build1_frames)
+    build2_frame_map = create_frame_map(build2_frames)
+
+    # Find only common frames between the two builds
+    common_frame_numbers = sorted(set(build1_frame_map.keys()).intersection(set(build2_frame_map.keys())))
+
+    # For each common frame number, create a comparison entry
+    frame_comparisons = []
+
+    # Process only common frames
+    for frame_num in common_frame_numbers:
+        build1_frame = build1_frame_map.get(frame_num)
+        build2_frame = build2_frame_map.get(frame_num)
+
+        comparison = {
+            'frame_number': frame_num,
+            'build1_frame': build1_frame,
+            'build2_frame': build2_frame,
+            'has_diff': False,
+            'diff_data': None
+        }
+
+        # Calculate diff
+        img1_path = os.path.join(build1_path, build1_frame)
+        img2_path = os.path.join(build2_path, build2_frame)
+
+        try:
+            diff_result = image_diff(img1_path, img2_path)
+            comparison['has_diff'] = diff_result.get('has_diff', False)
+            comparison['diff_data'] = diff_result
+        except Exception as e:
+            print(f"Error comparing images: {e}")
+
+        frame_comparisons.append(comparison)
+
+    # Calculate summary statistics
+    stats = {
+        'total_common_frames': len(common_frame_numbers),
+        'different_frames': sum(1 for comp in frame_comparisons if comp.get('has_diff', False))
+    }
+
+    return render_template('compare.html',
+                           build1=build1,
+                           build2=build2,
+                           target=target,
+                           movie=movie,
+                           comparisons=frame_comparisons,
+                           stats=stats)
+
+ at app.route('/view/<target>/<build>/<movie>')
+def view_single_build(target, build, movie):
+    """
+    Display all frames of a movie from a single build without comparison.
+    """
+    build_path = os.path.join(SCREENSHOTS_DIR, target, build)
+    frames = get_movie_frames(build_path, movie)
+
+    # Prepare frame data for the template
+    frame_data = []
+    for frame in frames:
+        frame_path = os.path.join(build_path, frame)
+        frame_num = get_frame_number(frame)
+
+        try:
+            img = Image.open(frame_path)
+            img_data = encode_image(img)
+
+            frame_data.append({
+                'frame_number': frame_num,
+                'filename': frame,
+                'img_data': img_data
+            })
+        except Exception as e:
+            print(f"Error processing frame {frame}: {e}")
+
+    return render_template('view.html',
+                           target=target,
+                           build=build,
+                           movie=movie,
+                           frames=frame_data)
+
+ at app.route('/screenshots/<path:filename>')
+def screenshots(filename):
+    return send_from_directory(SCREENSHOTS_DIR, filename)
+
+if __name__ == '__main__':
+    app.run(debug=True, port=5001)
\ No newline at end of file
diff --git a/imagediff/ImageDiffwithPython/static/css/style.css b/imagediff/ImageDiffwithPython/static/css/style.css
new file mode 100644
index 0000000..410eea8
--- /dev/null
+++ b/imagediff/ImageDiffwithPython/static/css/style.css
@@ -0,0 +1,131 @@
+h1, h2, h3{
+    font-weight: normal;
+}
+
+body{
+    padding: 0 24px;
+}
+
+header{
+    text-align: center;
+
+    a{
+        color: unset;
+        text-decoration: none;
+    }
+}
+
+table{
+    white-space: nowrap;
+}
+
+a {
+    text-decoration: none;
+    color: #337ab7;
+}
+
+a:hover {
+    text-decoration: underline;
+}
+
+.quote{
+    margin: 18px 0;
+    padding: 12px 24px;
+
+    border: 1px solid #dcdcdc;
+    border-radius: 6px;
+    background: #f5f5f5;
+
+    overflow-x: auto;
+}
+
+.code{
+    padding: 3px 6px;
+
+    font-weight: normal;
+    border-radius: 6px;
+    background: #f7f7f7;
+}
+
+.alert {
+    padding: .75rem 1.25rem;
+    margin-bottom: 1rem;
+
+    color: #004085;
+    background-color: #cce5ff;
+    border: 1px solid #b8daff;
+    border-radius: 6px;
+}
+
+.result-table{
+    border-collapse:collapse;
+
+    text-align: center;
+}
+
+.result-table.horizontal{
+    padding-bottom: 24px;
+
+    thead>tr{
+        height: 50px;
+    }
+
+    tbody{
+        th{
+            padding-right: 12px;
+        }
+
+        td{
+            text-align: center;
+            padding-left: 5px;
+            padding-right: 5px;
+            border: 1px solid black;
+            border-left: none;
+            font-size: 80%;
+        }
+
+        td:nth-of-type(1) {
+            border-left: 1px solid black;
+        }
+
+        td.no-data {
+            border: none;
+        }
+
+        td.no-diff {
+            border-right: none;
+        }
+
+        td.success, td.first-build {
+            background-color: #90ee90;
+        }
+    }
+
+    .result-table-build{
+        width: 30px;
+        transform: translate(-12px, 10px) rotate(315deg);
+        a{
+            border-bottom: 1px solid #ccc;
+        }
+    }
+}
+
+.comparison-table {
+    width: 100%;
+    border-collapse: collapse;
+    margin-bottom: 30px;
+
+    thead{
+        height: 50px;
+    }
+
+    th, td{
+        text-align: center;
+        width: 33.33%;
+    }
+
+    .frame-image {
+        max-width: 300px;
+        max-height: 300px;
+    }
+}
\ No newline at end of file
diff --git a/imagediff/ImageDiffwithPython/templates/index.html b/imagediff/ImageDiffwithPython/templates/index.html
new file mode 100644
index 0000000..932b9ca
--- /dev/null
+++ b/imagediff/ImageDiffwithPython/templates/index.html
@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <title>ImageDiff</title>
+    <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/style.css') }}" />
+    <style>
+        .target-list {
+            margin: 20px 0;
+        }
+
+        .target-item {
+            margin: 10px 0;
+            padding: 12px;
+
+            background-color: #f5f5f5;
+            border: 1px solid #ddd;
+            border-radius: 6px;
+
+            transition: background-color 0.2s;
+        }
+
+        .target-item:hover {
+            background-color: #e9e9e9;
+        }
+    </style>
+</head>
+<body>
+<header>
+    <h1><a href="{{ url_for('index') }}">ImageDiff</a></h1>
+</header>
+
+<section>
+    <h1>Director builds</h1>
+
+    <div class="target-list">
+        {% if targets %}
+        {% for target in targets %}
+        <div class="target-item">
+            <a href="{{ url_for('target_detail', target=target) }}">{{ target }}</a>
+        </div>
+        {% endfor %}
+        {% else %}
+        <p>No targets found. Please check your screenshots directory.</p>
+        {% endif %}
+    </div>
+</section>
+</body>
+</html>
\ No newline at end of file


Commit: 68bbe9bfdda1e153bd3038d2b589b8ac6d733451
    https://github.com/scummvm/scummvm-sites/commit/68bbe9bfdda1e153bd3038d2b589b8ac6d733451
Author: Chako (chaosbeiduo at outlook.com)
Date: 2026-06-11T19:13:21+02:00

Commit Message:
IMAGEDIFF: Add target detail page with continuous timeline bar

Featured three build categories: has-diff, no-diff??? no-data. Skipped builds are classified as either has-diff or no-diff based on movie presence.

Changed paths:
  A imagediff/ImageDiffwithPython/imagediff.py
  A imagediff/ImageDiffwithPython/templates/target.html


diff --git a/imagediff/ImageDiffwithPython/imagediff.py b/imagediff/ImageDiffwithPython/imagediff.py
new file mode 100644
index 0000000..8609ab1
--- /dev/null
+++ b/imagediff/ImageDiffwithPython/imagediff.py
@@ -0,0 +1,101 @@
+from PIL import Image, ImageChops
+import os
+import base64
+from io import BytesIO
+from config import SCREENSHOTS_DIR
+
+def encode_image(image):
+    """Encode an image to a base64 string."""
+    buffered = BytesIO()
+    image.save(buffered, format="PNG")
+    return base64.b64encode(buffered.getvalue()).decode('utf-8')
+
+def image_diff(src_img_path, cmp_img_path):
+    """
+    Compute the difference between two images and return the results.
+    """
+    try:
+        src_img = Image.open(src_img_path)
+        cmp_img = Image.open(cmp_img_path)
+        diff_img = ImageChops.difference(src_img, cmp_img).convert('RGB')
+
+        has_diff = diff_img.getbbox() is not None
+
+        return {
+            'src_img_data': encode_image(src_img),
+            'cmp_img_data': encode_image(cmp_img),
+            'diff_img_data': encode_image(diff_img) if has_diff else None,
+            'has_diff': has_diff
+        }
+    except IOError:
+        return {}
+
+def movie_diff(src_build, cmp_build, target, movie):
+    """
+    Compare all frames of a movie between two builds and determine if there are any differences.
+
+    Args:
+        src_build (str): The source build name
+        cmp_build (str): The comparison build name
+        target (str): The target name
+        movie (str): The movie name
+
+    Returns:
+        bool: True if any frame has differences, False otherwise
+    """
+    src_build_path = os.path.join(SCREENSHOTS_DIR, target, src_build)
+    cmp_build_path = os.path.join(SCREENSHOTS_DIR, target, cmp_build)
+
+    # Ensure both build paths exist
+    if not os.path.exists(src_build_path) or not os.path.exists(cmp_build_path):
+        return False
+
+    # Get all frames for this movie in both builds
+    src_frames = [f for f in os.listdir(src_build_path)
+                  if os.path.isfile(os.path.join(src_build_path, f)) and f.startswith(f"{movie}-")]
+
+    cmp_frames = [f for f in os.listdir(cmp_build_path)
+                  if os.path.isfile(os.path.join(cmp_build_path, f)) and f.startswith(f"{movie}-")]
+
+    # If frame counts differ, there's definitely a difference
+    if len(src_frames) != len(cmp_frames):
+        return True
+
+    # Helper function to extract frame number from filename
+    def get_frame_number(filename):
+        parts = filename.split('-')
+        if len(parts) > 1:
+            try:
+                return int(parts[1].split('.')[0])
+            except ValueError:
+                return 0
+        return 0
+
+    src_frame_map = {get_frame_number(f): f for f in src_frames}
+    cmp_frame_map = {get_frame_number(f): f for f in cmp_frames}
+
+    all_frame_numbers = sorted(set(list(src_frame_map.keys()) + list(cmp_frame_map.keys())))
+
+    if set(src_frame_map.keys()) != set(cmp_frame_map.keys()):
+        return True
+
+    for frame_num in all_frame_numbers:
+        src_frame = src_frame_map.get(frame_num)
+        cmp_frame = cmp_frame_map.get(frame_num)
+
+        if src_frame and cmp_frame:
+            src_img_path = os.path.join(src_build_path, src_frame)
+            cmp_img_path = os.path.join(cmp_build_path, cmp_frame)
+
+            try:
+                diff_result = image_diff(src_img_path, cmp_img_path)
+
+                if diff_result.get('has_diff', False):
+                    return True
+            except Exception as e:
+                print(f"Error comparing frames {src_frame} and {cmp_frame}: {e}")
+                return True
+        else:
+            return True
+
+    return False
\ No newline at end of file
diff --git a/imagediff/ImageDiffwithPython/templates/target.html b/imagediff/ImageDiffwithPython/templates/target.html
new file mode 100644
index 0000000..92bcfdb
--- /dev/null
+++ b/imagediff/ImageDiffwithPython/templates/target.html
@@ -0,0 +1,283 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <title>Target: {{ target }} - ImageDiff</title>
+    <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/style.css') }}" />
+    <style>
+        .cell-partial {
+            position: relative;
+        }
+
+        .cell-partial::after {
+            content: "??????";
+            position: absolute;
+            font-size: 10px;
+            bottom: 0;
+            right: 0;
+        }
+
+        /* Loading animation styles */
+        #loading-overlay {
+            position: fixed;
+            top: 0;
+            left: 0;
+            width: 100%;
+            height: 100%;
+            background-color: rgba(255, 255, 255, 0.8);
+            display: flex;
+            flex-direction: column;
+            justify-content: center;
+            align-items: center;
+            z-index: 9999;
+        }
+
+        .spinner {
+            width: 60px;
+            height: 60px;
+            border: 6px solid #f3f3f3;
+            border-top: 6px solid #3498db;
+            border-radius: 50%;
+            animation: spin 1.5s linear infinite;
+        }
+
+        @keyframes spin {
+            0% { transform: rotate(0deg); }
+            100% { transform: rotate(360deg); }
+        }
+
+        .hidden {
+            display: none !important;
+        }
+
+        .loading-text {
+            margin-top: 15px;
+            font-size: 16px;
+            color: #333;
+        }
+
+        .loading-status {
+            margin-top: 8px;
+            font-size: 14px;
+            color: #666;
+        }
+    </style>
+</head>
+<body>
+<!-- Loading overlay -->
+<div id="loading-overlay">
+    <div class="spinner"></div>
+    <div class="loading-text">Loading image diff data...</div>
+    <div class="loading-status">This may take a while. Please be patient.</div>
+</div>
+
+<header>
+    <h1><a href="{{ url_for('index') }}">ImageDiff</a></h1>
+</header>
+
+<h1>Target: <span class="code">{{ target }}</span></h1>
+
+<section>
+    <h2>Recent Results</h2>
+
+    <div class="quote">
+        <table class="result-table horizontal">
+            <thead>
+            <tr>
+                <th></th>
+                {% for build in builds %}
+                <th class="result-table-build">
+                    <a href="{{ url_for('build', build=build) }}">{{ build }}</a>
+                </th>
+                {% endfor %}
+            </tr>
+            </thead>
+            <tbody id="results-body">
+            <tr>
+                <td colspan="{{ builds|length + 1 }}" style="text-align: center;">
+                    Loading...
+                </td>
+            </tr>
+            </tbody>
+        </table>
+    </div>
+</section>
+
+<script>
+    document.addEventListener('DOMContentLoaded', function() {
+        // Load data via AJAX when page is loaded
+        loadTableData();
+    });
+
+    function loadTableData() {
+        const loadingOverlay = document.getElementById('loading-overlay');
+        const resultsBody = document.getElementById('results-body');
+        const loadingStatus = document.querySelector('.loading-status');
+
+        // Show loading animation
+        loadingOverlay.classList.remove('hidden');
+
+        // Build API URL
+        const target = '{{ target }}';
+        const apiUrl = '/api/target_data/' + encodeURIComponent(target);
+
+        // Start timestamp for calculating loading time
+        const startTime = new Date().getTime();
+
+        // Function to update loading status
+        function updateLoadingStatus(message) {
+            if (loadingStatus) {
+                loadingStatus.textContent = message;
+            }
+        }
+
+        // Update loading time every second
+        let loadingTimer = setInterval(() => {
+            const elapsedSeconds = Math.floor((new Date().getTime() - startTime) / 1000);
+            updateLoadingStatus(`${elapsedSeconds} seconds have passed. Please be patient...`);
+        }, 1000);
+
+        // Send AJAX request
+        fetch(apiUrl)
+            .then(response => {
+                if (!response.ok) {
+                    throw new Error('Network response error');
+                }
+                return response.json();
+            })
+            .then(data => {
+                // Clear timer
+                clearInterval(loadingTimer);
+
+                // Update table content
+                updateTableContent(data);
+
+                // Hide loading animation
+                loadingOverlay.classList.add('hidden');
+            })
+            .catch(error => {
+                // Clear timer
+                clearInterval(loadingTimer);
+
+                console.error('Error loading data:', error);
+
+                // Show error message
+                resultsBody.innerHTML = `
+                    <tr>
+                        <td colspan="{{ builds|length + 1 }}" style="text-align: center; color: red;">
+                            Failed to load data. Please refresh the page to try again.
+                        </td>
+                    </tr>
+                `;
+
+                // Update loading status
+                updateLoadingStatus('Loading failed. Please refresh the page to try again.');
+
+                // Hide loading animation after 3 seconds
+                setTimeout(() => {
+                    loadingOverlay.classList.add('hidden');
+                }, 3000);
+            });
+    }
+
+    function updateTableContent(data) {
+        const resultsBody = document.getElementById('results-body');
+        let tableContent = '';
+
+        // Generate table content based on returned data
+        data.movies.forEach(movie => {
+            tableContent += `
+                <tr>
+                    <th>
+                        <a href="${data.urls.movie_url.replace('MOVIE_PLACEHOLDER', movie)}">${movie}</a>
+                    </th>
+            `;
+
+            // Add cells for each build
+            data.continuous_bars[movie].forEach(cell => {
+                tableContent += generateTableCell(cell, movie, data.urls, data.target);
+            });
+
+            tableContent += '</tr>';
+        });
+
+        resultsBody.innerHTML = tableContent;
+    }
+
+    function generateTableCell(cell, movie, urls, target) {
+        // Generate HTML based on cell type
+        if (cell.type === 'diff') {
+            if (cell.is_skipped) {
+                return `<td class="success no-diff" title="Build skipped (no changes from ${cell.reference_build})"></td>`;
+            } else if (cell.is_partial) {
+                if (cell.has_diff) {
+                    return `
+                        <td class="success has-diff cell-partial">
+                            <a href="${urls.compare_url
+                        .replace('BUILD1_PLACEHOLDER', cell.build)
+                        .replace('BUILD2_PLACEHOLDER', cell.compare_with)
+                        .replace('TARGET_PLACEHOLDER', target)
+                        .replace('MOVIE_PLACEHOLDER', movie)}"
+                               title="Partial frames with changes detected">
+                                ????
+                            </a>
+                        </td>
+                    `;
+                } else {
+                    return `<td class="success no-diff cell-partial" title="Partial frames with no changes"></td>`;
+                }
+            } else if (cell.has_diff) {
+                return `
+                    <td class="success has-diff">
+                        <a href="${urls.compare_url
+                    .replace('BUILD1_PLACEHOLDER', cell.build)
+                    .replace('BUILD2_PLACEHOLDER', cell.compare_with)
+                    .replace('TARGET_PLACEHOLDER', target)
+                    .replace('MOVIE_PLACEHOLDER', movie)}">
+                            ????
+                        </a>
+                    </td>
+                `;
+            } else {
+                return `<td class="success no-diff"></td>`;
+            }
+        } else if (cell.type === 'first') {
+            return `
+                <td class="success first-build">
+                    <a href="${urls.single_build_url
+                .replace('BUILD_PLACEHOLDER', cell.build)
+                .replace('TARGET_PLACEHOLDER', target)
+                .replace('MOVIE_PLACEHOLDER', movie)}">????</a>
+                </td>
+            `;
+        } else if (cell.type === 'missing') {
+            return `<td class="no-data"></td>`;
+        } else if (cell.type === 'readded') {
+            if (cell.compare_with) {
+                return `
+                    <td class="success has-diff">
+                        <a href="${urls.compare_url
+                    .replace('BUILD1_PLACEHOLDER', cell.build)
+                    .replace('BUILD2_PLACEHOLDER', cell.compare_with)
+                    .replace('TARGET_PLACEHOLDER', target)
+                    .replace('MOVIE_PLACEHOLDER', movie)}"
+                           title="Movie was re-added in this build">
+                            ????
+                        </a>
+                    </td>
+                `;
+            } else {
+                return `
+                    <td class="success has-diff">
+                        <span title="Movie was re-added but no previous version found for comparison">????</span>
+                    </td>
+                `;
+            }
+        } else {
+            return `<td class="no-data"></td>`;
+        }
+    }
+</script>
+</body>
+</html>
\ No newline at end of file


Commit: 0f7b6f4568f7520f51e1b6e1df8bbcab0d3bb28c
    https://github.com/scummvm/scummvm-sites/commit/0f7b6f4568f7520f51e1b6e1df8bbcab0d3bb28c
Author: Chako (chaosbeiduo at outlook.com)
Date: 2026-06-11T19:13:21+02:00

Commit Message:
IMAGEDIFF: Add compare and view pages for build visualization

- Compare page: Enables comparison between current build and previous build
- View page: Dedicated page for first builds that have no previous build to compare against

Changed paths:
  A imagediff/ImageDiffwithPython/templates/compare.html
  A imagediff/ImageDiffwithPython/templates/view.html


diff --git a/imagediff/ImageDiffwithPython/templates/compare.html b/imagediff/ImageDiffwithPython/templates/compare.html
new file mode 100644
index 0000000..9b16b28
--- /dev/null
+++ b/imagediff/ImageDiffwithPython/templates/compare.html
@@ -0,0 +1,77 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <title>{{ build1 }} vs. {{ build2 }}/ {{movie}}/ {{target}}</title>
+    <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/style.css') }}" />
+    <style>
+        .diff-indicator {
+            color: red;
+            font-weight: bold;
+        }
+        .no-diff-indicator {
+            color: green;
+        }
+        .missing-frame {
+            color: #999;
+        }
+    </style>
+</head>
+<body>
+<header>
+    <h1><a href="{{ url_for('index') }}">ImageDiff</a></h1>
+</header>
+
+<h2><a href="{{ url_for('build', build=build1) }}">{{ build1 }}</a> vs. <a href="{{ url_for('build', build=build2) }}">{{ build2 }}</a> / <a href="{{ url_for('movie', movie=movie) }}">{{ movie }}</a> / <a href="{{ url_for('index') }}#{{ target }}">{{ target }}</a></h2>
+
+{% if comparisons %}
+<div class="quote">
+    <b>Different Frames: {{ stats.different_frames }}</b>
+</div>
+
+<table class="comparison-table">
+    <thead>
+    <tr>
+        <th><a href="{{ url_for('build', build=build1) }}">{{ build1 }}</a></th>
+        <th>Diff</th>
+        <th><a href="{{ url_for('build', build=build2) }}">{{ build2 }}</a></th>
+    </tr>
+    </thead>
+    <tbody>
+    {% for comp in comparisons %}
+    <tr class="{% if comp.has_diff %}has-diff{% else %}no-diff{% endif %}" data-frame="{{ comp.frame_number }}">
+        <td>
+            {% if comp.diff_data and comp.diff_data.src_img_data %}
+            <img class="frame-image" src="data:image/png;base64,{{ comp.diff_data.src_img_data }}" alt="Frame {{ comp.frame_number }} in {{ build1 }}">
+            {% else %}
+            <div class="missing-frame">Image data not available</div>
+            {% endif %}
+        </td>
+        <td>
+            {% if comp.has_diff %}
+            {% if comp.diff_data and comp.diff_data.diff_img_data %}
+            <img class="frame-image" src="data:image/png;base64,{{ comp.diff_data.diff_img_data }}" alt="Difference for frame {{ comp.frame_number }}">
+            {% else %}
+            <span class="diff-indicator">DIFFERENT</span>
+            {% endif %}
+            {% else %}
+            <span class="no-diff-indicator">No difference</span>
+            {% endif %}
+        </td>
+        <td>
+            {% if comp.diff_data and comp.diff_data.cmp_img_data %}
+            <img class="frame-image" src="data:image/png;base64,{{ comp.diff_data.cmp_img_data }}" alt="Frame {{ comp.frame_number }} in {{ build2 }}">
+            {% else %}
+            <div class="missing-frame">Image data not available</div>
+            {% endif %}
+        </td>
+    </tr>
+    {% endfor %}
+    </tbody>
+</table>
+{% else %}
+<div class="quote">
+    <p>No frames found for comparison.</p>
+</div>
+{% endif %}
+</body>
+</html>
\ No newline at end of file
diff --git a/imagediff/ImageDiffwithPython/templates/view.html b/imagediff/ImageDiffwithPython/templates/view.html
new file mode 100644
index 0000000..0eec42b
--- /dev/null
+++ b/imagediff/ImageDiffwithPython/templates/view.html
@@ -0,0 +1,52 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <title>{{ build }} / {{ movie }} / {{ target }}</title>
+  <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/style.css') }}" />
+  <style>
+    .empty-cell {
+      background-color: #f8f8f8;
+      color: #999;
+      padding: 20px;
+    }
+  </style>
+</head>
+<body>
+<header>
+  <h1><a href="{{ url_for('index') }}">ImageDiff</a></h1>
+</header>
+
+<h2><a href="{{ url_for('build', build=build) }}">{{ build }}</a> / <a href="{{ url_for('movie', movie=movie) }}">{{ movie }}</a> / <a href="{{ url_for('index') }}#{{ target }}">{{ target }}</a></h2>
+
+<p class="alert">This is the first build that contains this movie, so there's no previous build to compare with.</p>
+
+{% if frames %}
+<table class="comparison-table">
+  <thead>
+  <tr>
+    <th><a href="{{ url_for('build', build=build) }}">{{ build }}</a></th>
+    <th> </th>
+    <th> </th>
+  </tr>
+  </thead>
+  <tbody>
+  {% for frame in frames %}
+  <tr>
+    <td>
+      <img class="frame-image" src="data:image/png;base64,{{ frame.img_data }}" alt="Frame {{ frame.frame_number }} in {{ build }}">
+    </td>
+    <td class="empty-cell">
+      No comparison available
+    </td>
+    <td class="empty-cell">
+      No previous build
+    </td>
+  </tr>
+  {% endfor %}
+  </tbody>
+</table>
+{% else %}
+<p>No frames found for this movie.</p>
+{% endif %}
+</body>
+</html>
\ No newline at end of file


Commit: 4c39cc148f9074b296e36d2e5d1a3accd41d08c7
    https://github.com/scummvm/scummvm-sites/commit/4c39cc148f9074b296e36d2e5d1a3accd41d08c7
Author: Chako (chaosbeiduo at outlook.com)
Date: 2026-06-11T19:13:21+02:00

Commit Message:
IMAGEDIFF: Add build detail page and movie detail page

Changed paths:
  A imagediff/ImageDiffwithPython/templates/build.html
  A imagediff/ImageDiffwithPython/templates/movie.html


diff --git a/imagediff/ImageDiffwithPython/templates/build.html b/imagediff/ImageDiffwithPython/templates/build.html
new file mode 100644
index 0000000..37adc04
--- /dev/null
+++ b/imagediff/ImageDiffwithPython/templates/build.html
@@ -0,0 +1,113 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <title>Build: {{ build }}</title>
+  <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/style.css') }}" />
+  <style>
+    .result-table{
+      thead{
+        border-bottom: 1px solid #00000029;
+      }
+
+      th, td {
+        padding: 3px 6px;
+        text-align: left;
+      }
+
+      tbody{
+        tr.new-target {
+          border-top: 2px dashed #00000029;
+        }
+
+        tr:first-child.new-target {
+          border-top: none;
+        }
+      }
+    }
+
+    .first-build {
+      color: #888;
+      font-style: italic;
+    }
+  </style>
+</head>
+<body>
+<header>
+  <h1><a href="{{ url_for('index') }}">ImageDiff</a></h1>
+</header>
+
+<h1>Information on {{ build }}</h1>
+
+{% if target_info %}
+<section class="metadata">
+  <h2>Metadata</h2>
+  <ul>
+    {% for target, info in target_info.items() %}
+    {% if loop.first %}
+    <li>Parent:
+      {% if info.prev_build %}
+      <a href="{{ url_for('build', build=info.prev_build) }}">{{ info.prev_build }}</a>
+      {% else %}
+      None (First build)
+      {% endif %}
+    </li>
+    {% endif %}
+    {% endfor %}
+  </ul>
+</section>
+
+<section>
+  <h2>Test Results</h2>
+  <div class="quote">
+    <table class="result-table">
+      <thead>
+      <tr>
+        <th>Target</th>
+        <th>Movie</th>
+        <th>Result</th>
+      </tr>
+      </thead>
+      <tbody>
+      {% for target, info in target_info.items() %}
+      {% for movie in info.movies %}
+      <tr class="{% if loop.first %}new-target{% endif %}">
+        {% if loop.first %}
+        <td rowspan="{{ info.movies|length }}">
+          <a href="{{ url_for('index') }}#{{ target }}">{{ target }}</a>
+        </td>
+        {% endif %}
+
+        <td>
+          <a href="{{ url_for('movie', movie=movie) }}">{{ movie }}</a>
+        </td>
+
+        <td>
+          {% if info.prev_build %}
+          {% if info.movie_diffs.get(movie, False) %}
+          <b>
+            <a href="{{ url_for('compare', build1=build, build2=info.prev_build, target=target, movie=movie) }}">
+              DIFF
+            </a>
+          </b>
+          {% else %}
+          <span class="no-diff">No Diff</span>
+          {% endif %}
+          {% else %}
+          <span class="first-build">First build (no comparison available)</span>
+          {% endif %}
+        </td>
+      </tr>
+      {% endfor %}
+      {% endfor %}
+      </tbody>
+    </table>
+  </div>
+</section>
+
+{% else %}
+
+<p>This build was not found in any target.</p>
+
+{% endif %}
+</body>
+</html>
\ No newline at end of file
diff --git a/imagediff/ImageDiffwithPython/templates/movie.html b/imagediff/ImageDiffwithPython/templates/movie.html
new file mode 100644
index 0000000..53e403c
--- /dev/null
+++ b/imagediff/ImageDiffwithPython/templates/movie.html
@@ -0,0 +1,90 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <title>Movie: {{ movie }}</title>
+    <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/style.css') }}" />
+</head>
+<body>
+<header>
+    <h1><a href="{{ url_for('index') }}">ImageDiff</a></h1>
+</header>
+
+<h1>{{ movie }}</h1>
+
+<section>
+    <h2>Description</h2>
+    <div class="quote">
+        {% if target_builds %}
+        <table>
+            <thead>
+            <tr>
+                <th>Target</th>
+                <th>Builds</th>
+            </tr>
+            </thead>
+            <tbody>
+            {% for target, builds in target_builds.items() %}
+            <tr>
+                <td class="target-name">
+                    <a href="#{{ target }}">{{ target }}</a>
+                </td>
+                <td>
+                    {{ builds|length }} builds
+                </td>
+            </tr>
+            {% endfor %}
+            </tbody>
+        </table>
+        {% endif %}
+    </div>
+</section>
+
+<section>
+    <h2>Recent results</h2>
+    <div class="quote">
+        <table class="result-table horizontal">
+            <thead>
+            <tr>
+                <th> </th>
+                {% for build in all_builds %}
+                <th class="result-table-build">
+                    <a href="{{ url_for('build', build=build) }}">{{ build }}</a>
+                </th>
+                {% endfor %}
+            </tr>
+            </thead>
+            <tbody>
+            {% for target, builds in target_builds.items() %}
+            <tr>
+                <th><a href="{{ url_for('index') }}#{{ target }}">{{ target }}</a></th>
+                {% for build in all_builds %}
+                {% if build in builds %}
+                {% set build_index = builds.index(build) %}
+                {% if build_index < (builds|length - 1) %}
+                {% set next_build = builds[build_index + 1] %}
+                {% if diff_matrix.get(target, {}).get((build, next_build), False) %}
+                <td class="success has-diff">
+                    <a href="{{ url_for('compare', build1=build, build2=next_build, target=target, movie=movie) }}">????</a>
+                </td>
+                {% else %}
+                <td class="success no-diff">
+                </td>
+                {% endif %}
+                {% else %}
+                <td class="success first-build">
+                    <a href="{{ url_for('view_single_build', build=build, target=target, movie=movie) }}">????</a>
+                </td>
+                {% endif %}
+                {% else %}
+                <td class="no-data">
+                </td>
+                {% endif %}
+                {% endfor %}
+            </tr>
+            {% endfor %}
+            </tbody>
+        </table>
+    </div>
+</section>
+</body>
+</html>
\ No newline at end of file


Commit: 31a0c45be79e219d84aa1b34fc4f95f905dd649f
    https://github.com/scummvm/scummvm-sites/commit/31a0c45be79e219d84aa1b34fc4f95f905dd649f
Author: Chako (chaosbeiduo at outlook.com)
Date: 2026-06-11T19:13:21+02:00

Commit Message:
IMAGEDIFF: Add screenshots sample

Changed paths:
  A imagediff/ImageDiffwithPython/screenshots/sample-target.rar


diff --git a/imagediff/ImageDiffwithPython/screenshots/sample-target.rar b/imagediff/ImageDiffwithPython/screenshots/sample-target.rar
new file mode 100644
index 0000000..f54514f
Binary files /dev/null and b/imagediff/ImageDiffwithPython/screenshots/sample-target.rar differ


Commit: a4bc152fe203a6d074648da62233081f4eb7b339
    https://github.com/scummvm/scummvm-sites/commit/a4bc152fe203a6d074648da62233081f4eb7b339
Author: Garvit Singla (garvits093 at gmail.com)
Date: 2026-06-11T19:13:21+02:00

Commit Message:
IMAGEDIFF: Added caching by storing builds

Changed paths:
    imagediff/.gitignore
    imagediff/ImageDiffwithPython/config.py
    imagediff/ImageDiffwithPython/main.py


diff --git a/imagediff/.gitignore b/imagediff/.gitignore
index bcad0a6..23b614b 100644
--- a/imagediff/.gitignore
+++ b/imagediff/.gitignore
@@ -472,3 +472,4 @@ composer.lock
 *.css.map
 *.sass.map
 *.scss.map
+sample-target 
\ No newline at end of file
diff --git a/imagediff/ImageDiffwithPython/config.py b/imagediff/ImageDiffwithPython/config.py
index d5d624a..6f95be5 100644
--- a/imagediff/ImageDiffwithPython/config.py
+++ b/imagediff/ImageDiffwithPython/config.py
@@ -1,3 +1,5 @@
 import os
 
 SCREENSHOTS_DIR = os.environ.get("SCREENSHOTS_DIR", "./screenshots/")
+CACHE_FILE = os.environ.get("CACHE_FILE", "./cache.json")
+MOVIE_CACHE_FILE = os.environ.get("MOVIE_CACHE_FILE", "./movie_cache.json")
\ No newline at end of file
diff --git a/imagediff/ImageDiffwithPython/main.py b/imagediff/ImageDiffwithPython/main.py
index 6a07607..1025b75 100644
--- a/imagediff/ImageDiffwithPython/main.py
+++ b/imagediff/ImageDiffwithPython/main.py
@@ -1,13 +1,59 @@
 import os
-
+import json
 from PIL import Image
 from flask import Flask, render_template, jsonify, url_for, send_from_directory
 
-from config import SCREENSHOTS_DIR
+from config import MOVIE_CACHE_FILE,CACHE_FILE, SCREENSHOTS_DIR
 from imagediff import image_diff, encode_image, movie_diff
 
 app = Flask(__name__)
 
+#cache functions
+#save string keys and values for json file
+def make_cache_key(build1, build2, movie, frame):
+        return f"{build1}_{build2}_{movie}_{frame}"
+    
+    # Pre-calculate image difference results
+
+def load_cache():
+# if file does not exist ??? create it
+    if not os.path.exists(CACHE_FILE):
+        with open(CACHE_FILE, "w") as f:
+            json.dump({}, f)
+        return {}
+
+    try:
+        with open(CACHE_FILE, "r") as f:
+            return json.load(f)
+    except json.JSONDecodeError:
+        # corrupted JSON ??? reset cache
+        return {}
+
+def save_cache(cache):
+    with open(CACHE_FILE, 'w') as f:
+        json.dump(cache, f)
+
+def make_movie_diff_cache_key(build1, build2, target, movie):
+    return f"{build1}_{build2}_{target}_{movie}_movie"
+
+def load_movie_diff_cache():
+    if not os.path.exists(MOVIE_CACHE_FILE):
+        with open(MOVIE_CACHE_FILE, "w") as f:
+            json.dump({}, f)
+        return {}
+    try:
+        with open(MOVIE_CACHE_FILE, "r") as f:
+            return json.load(f)
+    except json.JSONDecodeError:
+        return {}
+def save_movie_diff_cache(cache):
+    with open(MOVIE_CACHE_FILE, 'w') as f:
+        json.dump(cache, f)
+
+
+image_diff_cache = load_cache()
+movie_diff_cache = load_movie_diff_cache()
+
 def get_frame_number(filename):
     """Extract frame number from filename."""
     parts = filename.split('-')
@@ -18,6 +64,7 @@ def get_frame_number(filename):
             return 0
     return 0
 
+
 def get_sorted_builds(target_path, reverse=True):
     """Get sorted list of builds for a target."""
     builds = [b for b in os.listdir(target_path)
@@ -159,7 +206,6 @@ def target_data_api(target):
             current_frames = build_movie_frames.get(current_build, {}).get(movie, [])
 
             if not has_in_current:
-                # Reference build needs to have the movie
                 reference_build = None
                 for j in range(i+1, len(builds)):
                     next_build = builds[j]
@@ -168,21 +214,17 @@ def target_data_api(target):
                         break
                 movie_reference_builds[movie][current_build] = {
                     'build': reference_build,
-                    'frames': []  # Empty since current build doesn't have the movie
+                    'frames': []
                 }
             else:
-                # Current build has the movie, find a reference build with matching frames
                 reference_data = {
                     'build': None,
                     'frames': current_frames
                 }
-
                 for j in range(i+1, len(builds)):
                     next_build = builds[j]
                     if movie in build_movie_frames.get(next_build, {}):
                         next_frames = build_movie_frames.get(next_build, {}).get(movie, [])
-
-                        # Check if current frames exist in the next build
                         common_frames = set(current_frames).intersection(set(next_frames))
                         if common_frames:
                             reference_data = {
@@ -190,26 +232,25 @@ def target_data_api(target):
                                 'frames': list(common_frames)
                             }
                             break
-
                 movie_reference_builds[movie][current_build] = reference_data
 
-    # Pre-calculate image difference results
-    image_diff_cache = {}
     def get_image_diff(current_build, prev_build, movie, frame):
-        cache_key = (current_build, prev_build, movie, frame)
-        if cache_key not in image_diff_cache:
-            current_frame_path = os.path.join(target_path, current_build, f"{movie}-{frame}.png")
-            prev_frame_path = os.path.join(target_path, prev_build, f"{movie}-{frame}.png")
-
-            if os.path.exists(current_frame_path) and os.path.exists(prev_frame_path):
-                try:
-                    image_diff_cache[cache_key] = image_diff(current_frame_path, prev_frame_path)
-                except Exception as e:
-                    print(f"Error comparing frames {movie}-{frame}: {e}")
-                    image_diff_cache[cache_key] = {'has_diff': True}
-            else:
+        cache_key = make_cache_key(current_build, prev_build, movie, frame)
+        if cache_key in image_diff_cache:
+            return image_diff_cache[cache_key]
+
+        current_frame_path = os.path.join(target_path, current_build, f"{movie}-{frame}.png")
+        prev_frame_path = os.path.join(target_path, prev_build, f"{movie}-{frame}.png")
+
+        if os.path.exists(current_frame_path) and os.path.exists(prev_frame_path):
+            try:
+                image_diff_cache[cache_key] = image_diff(current_frame_path, prev_frame_path)
+            except Exception as e:
+                print(f"Error comparing frames {movie}-{frame}: {e}")
                 image_diff_cache[cache_key] = {'has_diff': True}
-
+        else:
+            image_diff_cache[cache_key] = {'has_diff': True}
+        save_cache(image_diff_cache)
         return image_diff_cache[cache_key]
 
     # Modified movie difference function to only compare specified frames
@@ -227,11 +268,13 @@ def target_data_api(target):
         return has_any_diff
 
     # Pre-calculate movie difference results
-    movie_diff_cache = {}
+
+    
     def get_movie_diff_cached(current_build, prev_build, target, movie):
-        cache_key = (current_build, prev_build, target, movie)
+        cache_key = make_cache_key(current_build, prev_build, movie, "movie")
         if cache_key not in movie_diff_cache:
             movie_diff_cache[cache_key] = movie_diff(current_build, prev_build, target, movie)
+            save_movie_diff_cache(movie_diff_cache)
         return movie_diff_cache[cache_key]
 
     movies = sorted(list(all_movies))


Commit: cd38a5c77360e086848a8e908db6dcdff3f7a2fb
    https://github.com/scummvm/scummvm-sites/commit/cd38a5c77360e086848a8e908db6dcdff3f7a2fb
Author: Garvit Singla (garvits093 at gmail.com)
Date: 2026-06-11T19:13:22+02:00

Commit Message:
IMAGEDIFF: Refactored styling

Changed paths:
  A imagediff/ImageDiffwithPython/__pycache__/config.cpython-314.pyc
  A imagediff/ImageDiffwithPython/__pycache__/imagediff.cpython-314.pyc
  A imagediff/ImageDiffwithPython/cache.json
  A imagediff/ImageDiffwithPython/movie_cache.json
  R imagediff/ImageDiffwithPython/screenshots/sample-target.rar
    imagediff/ImageDiffwithPython/main.py


diff --git a/imagediff/ImageDiffwithPython/__pycache__/config.cpython-314.pyc b/imagediff/ImageDiffwithPython/__pycache__/config.cpython-314.pyc
new file mode 100644
index 0000000..2f099fd
Binary files /dev/null and b/imagediff/ImageDiffwithPython/__pycache__/config.cpython-314.pyc differ
diff --git a/imagediff/ImageDiffwithPython/__pycache__/imagediff.cpython-314.pyc b/imagediff/ImageDiffwithPython/__pycache__/imagediff.cpython-314.pyc
new file mode 100644
index 0000000..af8a502
Binary files /dev/null and b/imagediff/ImageDiffwithPython/__pycache__/imagediff.cpython-314.pyc differ
diff --git a/imagediff/ImageDiffwithPython/cache.json b/imagediff/ImageDiffwithPython/cache.json
new file mode 100644
index 0000000..2021560
--- /dev/null
+++ b/imagediff/ImageDiffwithPython/cache.json
@@ -0,0 +1 @@
+{"2492_2489_BasicUserEvents_1": {"src_img_data": " ", "cmp_img_data": " ", "diff_img_data": " ", "has_diff": true}, "2461_2460_BasicUserEvents_12": {"src_img_data": " ", "cmp_img_data": " ", "diff_img_data": " ", "has_diff": true}, "2492_2460_Buttons_1": {"src_img_data": " ", "cmp_img_data": " ", "diff_img_data": " ", "has_diff": true}, "2492_2460_Cursors_1": {"src_img_data": " ", "cmp_img_data": " ", "diff_img_data": " ", "has_diff": true}, "2492_2454_Face kit_1": {"src_img_data": " ", "cmp_img_data": " ", "diff_img_data": " ", "has_diff": true}, "2492_2460_Finder Demo_1": {"src_img_data": " ", "cmp_img_data": " ", "diff_img_data": " ", "has_diff": true}, "2492_2460_Gravity Ball_1": {"src_img_data": " ", "cmp_img_data": " ", "diff_img_data": " ", "has_diff": true}, "2492_2460_Hierarchical Bird_1": {"src_img_data": " ", "cmp_img_data": " ", "diff_img_data": " ", "has_diff": true}, "2492_2460_Maze_9": {"src_img_data": " ", "cmp_img_data": " ", "diff_img_data": " ", "has_diff": true}, "2492_2460_More Puppets_1": {"src_img_data": " ", "cmp_img_data": " ", "diff_img_data": " ", "has_diff": true}, "2492_2460_Pattern Viewer_1": {"src_img_data": " ", "cmp_img_data": " ", "diff_img_data": " ", "has_diff": true}, "2492_2460_PopUp Menu_1": {"src_img_data": " ", "cmp_img_data": " ", "diff_img_data": " ", "has_diff": true}, "2492_2489_Puppet Palettes_1": {"src_img_data": " ", "cmp_img_data": " ", "diff_img_data": " ", "has_diff": true}, "2492_2454_Running Boy_1": {"src_img_data": " ", "cmp_img_data": " ", "diff_img_data": " ", "has_diff": true}, "2492_2460_Saving Text_1": {"src_img_data": " ", "cmp_img_data": " ", "diff_img_data": " ", "has_diff": true}, "2492_2460_Simple Bird_1": {"src_img_data": " ", "cmp_img_data": " ", "diff_img_data": " ", "has_diff": true}, "2492_2489_Simple Puppets_1": {"src_img_data": " ", "cmp_img_data": " ", "diff_img_data": " ", "has_diff": true}, "2492_2460_Testing User_1": {"src_img_data": " ", "cmp_img_data": " ", "diff_img_data": " ", "has_diff": true}, "2492_2460_User Menus_1": {"src_img_data": " ", "cmp_img_data": " ", "diff_img_data": " ", "has_diff": true}, "2492_2460_Windows_1": {"src_img_data": " ", "cmp_img_data": " ", "diff_img_data": " ", "has_diff": true}, "2492_2460_XCMDGlue_1": {"src_img_data": " ", "cmp_img_data": " ", "diff_img_data": " ", "has_diff": true}, "2492_2460_xn_": {"has_diff": true}, "2461_2460_BasicUserEvents_7": {"src_img_data": " ", "cmp_img_data": " ", "diff_img_data": " ", "has_diff": true}, "2492_2460_Maze_7": {"src_img_data": " ", "cmp_img_data": " ", "diff_img_data": " ", "has_diff": true}, "2461_2460_BasicUserEvents_1": {"src_img_data": " ", "cmp_img_data": " ", "diff_img_data": " ", "has_diff": true}}
\ No newline at end of file
diff --git a/imagediff/ImageDiffwithPython/main.py b/imagediff/ImageDiffwithPython/main.py
index 1025b75..690bd17 100644
--- a/imagediff/ImageDiffwithPython/main.py
+++ b/imagediff/ImageDiffwithPython/main.py
@@ -50,7 +50,7 @@ def save_movie_diff_cache(cache):
     with open(MOVIE_CACHE_FILE, 'w') as f:
         json.dump(cache, f)
 
-
+# precalculate image difference results and store in cache
 image_diff_cache = load_cache()
 movie_diff_cache = load_movie_diff_cache()
 
@@ -206,6 +206,7 @@ def target_data_api(target):
             current_frames = build_movie_frames.get(current_build, {}).get(movie, [])
 
             if not has_in_current:
+                 # Reference build needs to have the movie
                 reference_build = None
                 for j in range(i+1, len(builds)):
                     next_build = builds[j]
@@ -213,10 +214,11 @@ def target_data_api(target):
                         reference_build = next_build
                         break
                 movie_reference_builds[movie][current_build] = {
-                    'build': reference_build,
-                    'frames': []
+                    'build': reference_build, 
+                    'frames': [] # Empty since current build doesn't have the movie
                 }
             else:
+                # Current build has the movie, find a reference build with matching frames
                 reference_data = {
                     'build': None,
                     'frames': current_frames
@@ -585,4 +587,4 @@ def screenshots(filename):
     return send_from_directory(SCREENSHOTS_DIR, filename)
 
 if __name__ == '__main__':
-    app.run(debug=True, port=5001)
\ No newline at end of file
+    app.run(debug=True, port=5001)
diff --git a/imagediff/ImageDiffwithPython/movie_cache.json b/imagediff/ImageDiffwithPython/movie_cache.json
new file mode 100644
index 0000000..ad6d6dc
--- /dev/null
+++ b/imagediff/ImageDiffwithPython/movie_cache.json
@@ -0,0 +1 @@
+{"2489_2488_BasicUserEvents_movie": true, "2488_2482_BasicUserEvents_movie": true, "2482_2481_BasicUserEvents_movie": true, "2481_2475_BasicUserEvents_movie": true, "2475_2474_BasicUserEvents_movie": true, "2474_2468_BasicUserEvents_movie": true, "2468_2467_BasicUserEvents_movie": true, "2467_2461_BasicUserEvents_movie": true, "2460_2459_BasicUserEvents_movie": true, "2459_2458_BasicUserEvents_movie": true, "2458_2454_BasicUserEvents_movie": true, "2454_2453_BasicUserEvents_movie": false, "2453_2452_BasicUserEvents_movie": false, "2460_2459_Buttons_movie": false, "2459_2458_Buttons_movie": false, "2458_2454_Buttons_movie": false, "2454_2453_Buttons_movie": false, "2453_2452_Buttons_movie": false, "2460_2459_CD_movie": false, "2459_2458_CD_movie": false, "2460_2459_Cursors_movie": false, "2459_2458_Cursors_movie": false, "2458_2454_Cursors_movie": false, "2454_2453_Cursors_movie": false, "2453_2452_Cursors_movie": false, "2460_2459_Face Kit_movie": false, "2459_2458_Face Kit_movie": false, "2454_2453_Face kit_movie": false, "2453_2452_Face kit_movie": false, "2460_2459_Finder Demo_movie": false, "2459_2458_Finder Demo_movie": false, "2458_2454_Finder Demo_movie": false, "2454_2453_Finder Demo_movie": false, "2453_2452_Finder Demo_movie": false, "2460_2459_Gravity Ball_movie": false, "2459_2458_Gravity Ball_movie": false, "2458_2454_Gravity Ball_movie": false, "2454_2453_Gravity Ball_movie": false, "2453_2452_Gravity Ball_movie": false, "2460_2459_Hierarchical Bird_movie": false, "2459_2458_Hierarchical Bird_movie": false, "2458_2454_Hierarchical Bird_movie": false, "2454_2453_Hierarchical Bird_movie": false, "2453_2452_Hierarchical Bird_movie": false, "2460_2459_Indicators_movie": false, "2459_2458_Indicators_movie": false, "2458_2454_Indicators_movie": false, "2454_2453_Indicators_movie": false, "2453_2452_Indicators_movie": false, "2460_2459_Maze_movie": false, "2459_2458_Maze_movie": false, "2458_2454_Maze_movie": false, "2454_2453_Maze_movie": false, "2453_2452_Maze_movie": false, "2460_2459_More Puppets_movie": false, "2459_2458_More Puppets_movie": false, "2458_2454_More Puppets_movie": false, "2454_2453_More Puppets_movie": false, "2453_2452_More Puppets_movie": false, "2460_2459_Pattern Viewer_movie": false, "2459_2458_Pattern Viewer_movie": false, "2458_2454_Pattern Viewer_movie": false, "2454_2453_Pattern Viewer_movie": false, "2453_2452_Pattern Viewer_movie": false, "2460_2459_PopUp Menu_movie": false, "2459_2458_PopUp Menu_movie": false, "2458_2454_PopUp Menu_movie": false, "2454_2453_PopUp Menu_movie": false, "2453_2452_PopUp Menu_movie": false, "2489_2488_Puppet Palettes_movie": true, "2488_2482_Puppet Palettes_movie": true, "2482_2481_Puppet Palettes_movie": true, "2481_2475_Puppet Palettes_movie": true, "2475_2474_Puppet Palettes_movie": true, "2474_2468_Puppet Palettes_movie": true, "2468_2467_Puppet Palettes_movie": true, "2467_2461_Puppet Palettes_movie": true, "2461_2460_Puppet Palettes_movie": true, "2460_2459_Puppet Palettes_movie": true, "2459_2458_Puppet Palettes_movie": true, "2458_2454_Puppet Palettes_movie": true, "2454_2453_Puppet Palettes_movie": false, "2453_2452_Puppet Palettes_movie": false, "2454_2453_Running Boy_movie": false, "2453_2452_Running Boy_movie": false, "2460_2459_Saving Text_movie": false, "2459_2458_Saving Text_movie": false, "2458_2454_Saving Text_movie": false, "2454_2453_Saving Text_movie": false, "2453_2452_Saving Text_movie": false, "2460_2459_Simple Bird_movie": false, "2459_2458_Simple Bird_movie": false, "2458_2454_Simple Bird_movie": false, "2454_2453_Simple Bird_movie": false, "2453_2452_Simple Bird_movie": false, "2489_2488_Simple Puppets_movie": true, "2488_2482_Simple Puppets_movie": true, "2482_2481_Simple Puppets_movie": true, "2481_2475_Simple Puppets_movie": true, "2475_2474_Simple Puppets_movie": true, "2474_2468_Simple Puppets_movie": true, "2468_2467_Simple Puppets_movie": true, "2467_2461_Simple Puppets_movie": true, "2461_2460_Simple Puppets_movie": true, "2460_2459_Simple Puppets_movie": true, "2459_2458_Simple Puppets_movie": true, "2458_2454_Simple Puppets_movie": true, "2454_2453_Simple Puppets_movie": false, "2453_2452_Simple Puppets_movie": false, "2460_2459_Testing User_movie": false, "2459_2458_Testing User_movie": false, "2458_2454_Testing User_movie": false, "2454_2453_Testing User_movie": false, "2453_2452_Testing User_movie": false, "2460_2459_User Menus_movie": false, "2459_2458_User Menus_movie": false, "2458_2454_User Menus_movie": false, "2454_2453_User Menus_movie": false, "2453_2452_User Menus_movie": false, "2460_2459_Videodisc_movie": false, "2459_2458_Videodisc_movie": false, "2460_2459_Windows_movie": false, "2459_2458_Windows_movie": false, "2458_2454_Windows_movie": false, "2454_2453_Windows_movie": false, "2453_2452_Windows_movie": false, "2460_2459_XCMDGlue_movie": false, "2459_2458_XCMDGlue_movie": false, "2458_2454_XCMDGlue_movie": false, "2454_2453_XCMDGlue_movie": false, "2453_2452_XCMDGlue_movie": false, "2460_2459_editableText_movie": false, "2459_2458_editableText_movie": false, "2460_2459_moveableSprite_movie": false, "2459_2458_moveableSprite_movie": false, "2460_2459_rollOver_movie": false, "2459_2458_rollOver_movie": false, "2460_2459_xn_movie": false, "2459_2458_xn_movie": false, "2458_2454_xn_movie": false, "2454_2453_xn_movie": false, "2453_2452_xn_movie": false, "2460_2459_zoomBox_movie": false, "2459_2458_zoomBox_movie": false}
\ No newline at end of file
diff --git a/imagediff/ImageDiffwithPython/screenshots/sample-target.rar b/imagediff/ImageDiffwithPython/screenshots/sample-target.rar
deleted file mode 100644
index f54514f..0000000
Binary files a/imagediff/ImageDiffwithPython/screenshots/sample-target.rar and /dev/null differ


Commit: 89f8fd6d94d2680a69b2e4d2412c302c436ed7e9
    https://github.com/scummvm/scummvm-sites/commit/89f8fd6d94d2680a69b2e4d2412c302c436ed7e9
Author: Garvit Singla (garvits093 at gmail.com)
Date: 2026-06-11T19:13:22+02:00

Commit Message:
IMAGEDIFF: Refactor comparison logic structure

Changed paths:
    imagediff/ImageDiffwithPython/config.py
    imagediff/ImageDiffwithPython/main.py


diff --git a/imagediff/ImageDiffwithPython/config.py b/imagediff/ImageDiffwithPython/config.py
index 6f95be5..785893b 100644
--- a/imagediff/ImageDiffwithPython/config.py
+++ b/imagediff/ImageDiffwithPython/config.py
@@ -1,5 +1,4 @@
 import os
 
 SCREENSHOTS_DIR = os.environ.get("SCREENSHOTS_DIR", "./screenshots/")
-CACHE_FILE = os.environ.get("CACHE_FILE", "./cache.json")
-MOVIE_CACHE_FILE = os.environ.get("MOVIE_CACHE_FILE", "./movie_cache.json")
\ No newline at end of file
+CACHE_DIR = os.environ.get("CACHE_DIR", "./cache/")
diff --git a/imagediff/ImageDiffwithPython/main.py b/imagediff/ImageDiffwithPython/main.py
index 690bd17..8318bc1 100644
--- a/imagediff/ImageDiffwithPython/main.py
+++ b/imagediff/ImageDiffwithPython/main.py
@@ -1,36 +1,63 @@
 import os
 import json
 from PIL import Image
-from flask import Flask, render_template, jsonify, url_for, send_from_directory
+from flask import Flask, render_template, jsonify, url_for, send_from_directory, request
 
-from config import MOVIE_CACHE_FILE,CACHE_FILE, SCREENSHOTS_DIR
+from config import CACHE_DIR,SCREENSHOTS_DIR
 from imagediff import image_diff, encode_image, movie_diff
 
 app = Flask(__name__)
 
 #cache functions
+#making cache directory if it does not exist
+if not os.path.exists(CACHE_DIR):
+    os.makedirs(CACHE_DIR)
+
+#make target cache file
+def get_target_cache_file(target):
+    if not os.path.exists(f"{target}_cache.json"):
+        with open(f"{target}_cache.json", "w") as f:
+            json.dump({}, f)
+    return f"{target}_cache.json"
+    
+#make target movie cache file
+def get_target_movie_cache_file(target):
+    if not os.path.exists(f"{target}_movie_cache.json"):
+        with open(f"{target}_movie_cache.json", "w") as f:
+            json.dump({}, f)
+    return f"{target}_movie_cache.json"
+
+def get_cache_file(target_file_name,page_number):
+    if not os.path.exists(get_cache_file(target_file_name,page_number)):
+        with open(get_cache_file(target_file_name,page_number), "w") as f:
+            json.dump({}, f)
+    return os.path.join(CACHE_DIR, f"{target_file_name}_page_{page_number}.json")
+
+
 #save string keys and values for json file
-def make_cache_key(build1, build2, movie, frame):
-        return f"{build1}_{build2}_{movie}_{frame}"
+def make_cache_key(target,build1, build2, movie, frame):
+        return f"{target}_{build1}_{build2}_{movie}_{frame}"
     
     # Pre-calculate image difference results
 
-def load_cache():
+def load_cache(target_file_name, page_number):
 # if file does not exist ??? create it
-    if not os.path.exists(CACHE_FILE):
-        with open(CACHE_FILE, "w") as f:
+    cache_file = get_cache_file(target_file_name, page_number)
+    if not os.path.exists(cache_file):
+        with open(cache_file, "w") as f:
             json.dump({}, f)
         return {}
 
     try:
-        with open(CACHE_FILE, "r") as f:
+        with open(cache_file, "r") as f:
             return json.load(f)
     except json.JSONDecodeError:
         # corrupted JSON ??? reset cache
         return {}
 
-def save_cache(cache):
-    with open(CACHE_FILE, 'w') as f:
+def save_cache(cache, target_file_name, page_number):
+    cache_file = get_cache_file(target_file_name, page_number)
+    with open(cache_file, 'w') as f:
         json.dump(cache, f)
 
 def make_movie_diff_cache_key(build1, build2, target, movie):
@@ -175,9 +202,17 @@ def target_detail(target):
     builds = get_sorted_builds(target_path)
 
     # Only return the page framework with build list but without table data
+    page = int(request.args.get('page', 1))
+    page_size = 10
+    start = (page - 1) * page_size
+    end = start + page_size
+    paginated_builds = builds[start:end]
+    total_pages = (len(builds) + page_size - 1) // page_size
     return render_template('target.html',
-                           target=target,
-                           builds=builds)
+        target=target,
+        builds=paginated_builds,
+        page=page,
+        total_pages=total_pages)
 
 # Handle time-consuming data calculations
 @app.route('/api/target_data/<target>')
@@ -237,7 +272,7 @@ def target_data_api(target):
                 movie_reference_builds[movie][current_build] = reference_data
 
     def get_image_diff(current_build, prev_build, movie, frame):
-        cache_key = make_cache_key(current_build, prev_build, movie, frame)
+        cache_key = make_cache_key(target, current_build, prev_build, movie, frame)
         if cache_key in image_diff_cache:
             return image_diff_cache[cache_key]
 
@@ -273,7 +308,7 @@ def target_data_api(target):
 
     
     def get_movie_diff_cached(current_build, prev_build, target, movie):
-        cache_key = make_cache_key(current_build, prev_build, movie, "movie")
+        cache_key = make_movie_diff_cache_key(current_build, prev_build, target, movie)
         if cache_key not in movie_diff_cache:
             movie_diff_cache[cache_key] = movie_diff(current_build, prev_build, target, movie)
             save_movie_diff_cache(movie_diff_cache)
@@ -407,38 +442,39 @@ def movie(movie):
     target_builds = {}
     all_builds = set()
     diff_matrix = {}
+    page_size = 10
+    page = int(request.args.get('page', 1))
 
     # Scan through all targets
     for target in os.listdir(SCREENSHOTS_DIR):
         target_path = os.path.join(SCREENSHOTS_DIR, target)
         if os.path.isdir(target_path):
-            # Get sorted builds
             builds = get_sorted_builds(target_path)
-
-            # Find builds containing this movie
             builds_with_movie = []
             for build in builds:
                 build_path = os.path.join(target_path, build)
                 movie_files = get_movie_frames(build_path, movie)
-
                 if movie_files:
                     builds_with_movie.append(build)
                     all_builds.add(build)
 
-            # Calculate diffs between consecutive builds
-            for i in range(len(builds_with_movie) - 1):
-                current_build = builds_with_movie[i]
-                prev_build = builds_with_movie[i + 1]
+            # Pagination for builds_with_movie
+            total_pages = (len(builds_with_movie) + page_size - 1) // page_size
+            start = (page - 1) * page_size
+            end = start + page_size
+            paginated_builds_with_movie = builds_with_movie[start:end]
 
+            # Calculate diffs between consecutive builds (only for paginated builds)
+            for i in range(len(paginated_builds_with_movie) - 1):
+                current_build = paginated_builds_with_movie[i]
+                prev_build = paginated_builds_with_movie[i + 1]
                 has_diff = movie_diff(current_build, prev_build, target, movie)
-
                 if target not in diff_matrix:
                     diff_matrix[target] = {}
-
                 diff_matrix[target][(current_build, prev_build)] = has_diff
 
             if builds_with_movie:
-                target_builds[target] = builds_with_movie
+                target_builds[target] = paginated_builds_with_movie
 
     all_builds = sorted(list(all_builds), reverse=True)
 
@@ -446,7 +482,9 @@ def movie(movie):
                            movie=movie,
                            target_builds=target_builds,
                            all_builds=all_builds,
-                           diff_matrix=diff_matrix)
+                           diff_matrix=diff_matrix,
+                           page=page,
+                           total_pages=total_pages)
 
 @app.route('/build/<build>')
 def build(build):


Commit: 2d204580544abb72ca2b43c59500fb9c29165765
    https://github.com/scummvm/scummvm-sites/commit/2d204580544abb72ca2b43c59500fb9c29165765
Author: Garvit Singla (garvits093 at gmail.com)
Date: 2026-06-11T19:13:22+02:00

Commit Message:
IMAGEDIFF: Added pagination with builds caching

Changed paths:
    imagediff/ImageDiffwithPython/main.py
    imagediff/ImageDiffwithPython/templates/target.html


diff --git a/imagediff/ImageDiffwithPython/main.py b/imagediff/ImageDiffwithPython/main.py
index 8318bc1..8f21600 100644
--- a/imagediff/ImageDiffwithPython/main.py
+++ b/imagediff/ImageDiffwithPython/main.py
@@ -8,78 +8,56 @@ from imagediff import image_diff, encode_image, movie_diff
 
 app = Flask(__name__)
 
-#cache functions
-#making cache directory if it does not exist
-if not os.path.exists(CACHE_DIR):
-    os.makedirs(CACHE_DIR)
-
-#make target cache file
-def get_target_cache_file(target):
-    if not os.path.exists(f"{target}_cache.json"):
-        with open(f"{target}_cache.json", "w") as f:
-            json.dump({}, f)
-    return f"{target}_cache.json"
-    
-#make target movie cache file
-def get_target_movie_cache_file(target):
-    if not os.path.exists(f"{target}_movie_cache.json"):
-        with open(f"{target}_movie_cache.json", "w") as f:
-            json.dump({}, f)
-    return f"{target}_movie_cache.json"
-
-def get_cache_file(target_file_name,page_number):
-    if not os.path.exists(get_cache_file(target_file_name,page_number)):
-        with open(get_cache_file(target_file_name,page_number), "w") as f:
-            json.dump({}, f)
-    return os.path.join(CACHE_DIR, f"{target_file_name}_page_{page_number}.json")
-
-
-#save string keys and values for json file
-def make_cache_key(target,build1, build2, movie, frame):
-        return f"{target}_{build1}_{build2}_{movie}_{frame}"
-    
-    # Pre-calculate image difference results
-
-def load_cache(target_file_name, page_number):
-# if file does not exist ??? create it
-    cache_file = get_cache_file(target_file_name, page_number)
-    if not os.path.exists(cache_file):
-        with open(cache_file, "w") as f:
-            json.dump({}, f)
-        return {}
-
+# cache functions
+def get_cache_page_path(target, page):
+    """Generates path: cache/<target>/page_<page>.json"""
+    target_dir = os.path.join(CACHE_DIR, target)
+    if not os.path.exists(target_dir):
+        os.makedirs(target_dir)
+    return os.path.join(target_dir, f"page_{page}.json")
+
+def load_target_cache(target, page):
+    """Loads cache from disk for a specific target and page."""
+    path = get_cache_page_path(target, page)
+    if not os.path.exists(path):
+        return None
     try:
-        with open(cache_file, "r") as f:
-            return json.load(f)
-    except json.JSONDecodeError:
-        # corrupted JSON ??? reset cache
-        return {}
-
-def save_cache(cache, target_file_name, page_number):
-    cache_file = get_cache_file(target_file_name, page_number)
-    with open(cache_file, 'w') as f:
+        with open(path, "r") as f:
+            data = json.load(f)
+            return data if data else None
+    except (json.JSONDecodeError, IOError):
+        return None
+
+def save_target_cache(target, page, cache):
+    """Saves the calculated data to the specific page file."""
+    path = get_cache_page_path(target, page)
+    with open(path, "w") as f:
         json.dump(cache, f)
 
-def make_movie_diff_cache_key(build1, build2, target, movie):
-    return f"{build1}_{build2}_{target}_{movie}_movie"
+def get_frame_cache_path(target):
+    target_dir = os.path.join(CACHE_DIR, target)
+    if not os.path.exists(target_dir):
+        os.makedirs(target_dir)
+    return os.path.join(target_dir, "frame_diffs.json")
 
-def load_movie_diff_cache():
-    if not os.path.exists(MOVIE_CACHE_FILE):
-        with open(MOVIE_CACHE_FILE, "w") as f:
-            json.dump({}, f)
+def load_frame_cache(target):
+    path = get_frame_cache_path(target)
+    if not os.path.exists(path):
         return {}
     try:
-        with open(MOVIE_CACHE_FILE, "r") as f:
+        with open(path, "r") as f:
             return json.load(f)
-    except json.JSONDecodeError:
+    except:
         return {}
-def save_movie_diff_cache(cache):
-    with open(MOVIE_CACHE_FILE, 'w') as f:
+
+def save_frame_cache(target, cache):
+    path = get_frame_cache_path(target)
+    with open(path, "w") as f:
         json.dump(cache, f)
 
-# precalculate image difference results and store in cache
-image_diff_cache = load_cache()
-movie_diff_cache = load_movie_diff_cache()
+# save string keys for frame-level diffs if needed
+def make_cache_key(target, build1, build2, movie, frame):
+    return f"{target}_{build1}_{build2}_{movie}_{frame}"
 
 def get_frame_number(filename):
     """Extract frame number from filename."""
@@ -208,15 +186,28 @@ def target_detail(target):
     end = start + page_size
     paginated_builds = builds[start:end]
     total_pages = (len(builds) + page_size - 1) // page_size
+
+    # Load cache for this target and page
+    cache_data = load_target_cache(target, page)
+
     return render_template('target.html',
         target=target,
         builds=paginated_builds,
         page=page,
-        total_pages=total_pages)
+        total_pages=total_pages,
+        cache_data=cache_data)
+
 
 # Handle time-consuming data calculations
 @app.route('/api/target_data/<target>')
 def target_data_api(target):
+    page = int(request.args.get('page', 1))
+    page_size = 10
+
+    cached_data = load_target_cache(target, page)
+    if cached_data:
+        return jsonify(cached_data)
+    
     target_path = os.path.join(SCREENSHOTS_DIR, target)
 
     if not os.path.exists(target_path) or not os.path.isdir(target_path):
@@ -225,8 +216,13 @@ def target_data_api(target):
     builds = get_sorted_builds(target_path)
     builds_ascending = get_sorted_builds(target_path, reverse=False)
 
+    start_index = (page - 1) * page_size
+    end_index = start_index + page_size
+    current_page_builds = builds[start_index:end_index]
+    builds_to_process = builds[start_index:end_index + 1] if end_index < len(builds) else builds[start_index:end_index]
+
     # Collect movie frame data
-    all_movies, build_movie_frames, _ = collect_movie_frames(target_path, builds)
+    all_movies, build_movie_frames, _ = collect_movie_frames(target_path,builds_to_process)
 
     # Calculate first build for each movie
     first_build_for_movie = find_first_build_for_movies(
@@ -273,6 +269,7 @@ def target_data_api(target):
 
     def get_image_diff(current_build, prev_build, movie, frame):
         cache_key = make_cache_key(target, current_build, prev_build, movie, frame)
+        image_diff_cache = load_frame_cache(target)
         if cache_key in image_diff_cache:
             return image_diff_cache[cache_key]
 
@@ -287,7 +284,7 @@ def target_data_api(target):
                 image_diff_cache[cache_key] = {'has_diff': True}
         else:
             image_diff_cache[cache_key] = {'has_diff': True}
-        save_cache(image_diff_cache)
+        save_frame_cache(target, image_diff_cache)
         return image_diff_cache[cache_key]
 
     # Modified movie difference function to only compare specified frames
@@ -307,12 +304,12 @@ def target_data_api(target):
     # Pre-calculate movie difference results
 
     
-    def get_movie_diff_cached(current_build, prev_build, target, movie):
-        cache_key = make_movie_diff_cache_key(current_build, prev_build, target, movie)
-        if cache_key not in movie_diff_cache:
-            movie_diff_cache[cache_key] = movie_diff(current_build, prev_build, target, movie)
-            save_movie_diff_cache(movie_diff_cache)
-        return movie_diff_cache[cache_key]
+    # def get_movie_diff_cached(current_build, prev_build, target, movie):
+    #     cache_key = (current_build, prev_build, target, movie)
+    #     if cache_key not in movie_diff:
+    #         movie_diff_cache[cache_key] = movie_diff(current_build, prev_build, target, movie)
+    #         save_movie_diff_cache(movie_diff_cache, target)
+    #     return movie_diff_cache[cache_key]
 
     movies = sorted(list(all_movies))
     continuous_bars = {}
@@ -380,7 +377,7 @@ def target_data_api(target):
                             'is_partial': True
                         })
                     else:
-                        has_diff = get_movie_diff_cached(current_build, prev_build, target, movie)
+                        has_diff = movie_diff(current_build, prev_build, target, movie)
 
                         continuous_bars[movie].append({
                             'build': current_build,
@@ -429,12 +426,12 @@ def target_data_api(target):
     # Return all data to frontend
     data = {
         'target': target,
-        'builds': builds,
+        'builds': current_page_builds,
         'movies': movies,
         'continuous_bars': continuous_bars,
         'urls': urls
     }
-
+    save_target_cache(target, page, data)
     return jsonify(data)
 
 @app.route('/movie/<movie>')
@@ -442,39 +439,38 @@ def movie(movie):
     target_builds = {}
     all_builds = set()
     diff_matrix = {}
-    page_size = 10
-    page = int(request.args.get('page', 1))
 
     # Scan through all targets
     for target in os.listdir(SCREENSHOTS_DIR):
         target_path = os.path.join(SCREENSHOTS_DIR, target)
         if os.path.isdir(target_path):
+            # Get sorted builds
             builds = get_sorted_builds(target_path)
+
+            # Find builds containing this movie
             builds_with_movie = []
             for build in builds:
                 build_path = os.path.join(target_path, build)
                 movie_files = get_movie_frames(build_path, movie)
+
                 if movie_files:
                     builds_with_movie.append(build)
                     all_builds.add(build)
 
-            # Pagination for builds_with_movie
-            total_pages = (len(builds_with_movie) + page_size - 1) // page_size
-            start = (page - 1) * page_size
-            end = start + page_size
-            paginated_builds_with_movie = builds_with_movie[start:end]
+            # Calculate diffs between consecutive builds
+            for i in range(len(builds_with_movie) - 1):
+                current_build = builds_with_movie[i]
+                prev_build = builds_with_movie[i + 1]
 
-            # Calculate diffs between consecutive builds (only for paginated builds)
-            for i in range(len(paginated_builds_with_movie) - 1):
-                current_build = paginated_builds_with_movie[i]
-                prev_build = paginated_builds_with_movie[i + 1]
                 has_diff = movie_diff(current_build, prev_build, target, movie)
+
                 if target not in diff_matrix:
                     diff_matrix[target] = {}
+
                 diff_matrix[target][(current_build, prev_build)] = has_diff
 
             if builds_with_movie:
-                target_builds[target] = paginated_builds_with_movie
+                target_builds[target] = builds_with_movie
 
     all_builds = sorted(list(all_builds), reverse=True)
 
@@ -482,9 +478,7 @@ def movie(movie):
                            movie=movie,
                            target_builds=target_builds,
                            all_builds=all_builds,
-                           diff_matrix=diff_matrix,
-                           page=page,
-                           total_pages=total_pages)
+                           diff_matrix=diff_matrix)
 
 @app.route('/build/<build>')
 def build(build):
diff --git a/imagediff/ImageDiffwithPython/templates/target.html b/imagediff/ImageDiffwithPython/templates/target.html
index 92bcfdb..3fe2f97 100644
--- a/imagediff/ImageDiffwithPython/templates/target.html
+++ b/imagediff/ImageDiffwithPython/templates/target.html
@@ -101,6 +101,31 @@
             </tr>
             </tbody>
         </table>
+        <div class="pagination-container">
+    {% if total_pages > 1 %}
+        <div class="pagination">
+
+            {% if page > 1 %}
+                <a class="page-btn"
+                   href="{{ url_for('target_detail', target=target) }}?page={{ page - 1 }}">
+                    ??? Previous
+                </a>
+            {% endif %}
+
+            <span class="page-info">
+                Page {{ page }} of {{ total_pages }}
+            </span>
+
+            {% if page < total_pages %}
+                <a class="page-btn"
+                   href="{{ url_for('target_detail', target=target) }}?page={{ page + 1 }}">
+                    Next ???
+                </a>
+            {% endif %}
+
+        </div>
+    {% endif %}
+</div>
     </div>
 </section>
 
@@ -120,7 +145,7 @@
 
         // Build API URL
         const target = '{{ target }}';
-        const apiUrl = '/api/target_data/' + encodeURIComponent(target);
+        const apiUrl = `/api/target_data/${encodeURIComponent(target)}?page={{ page }}`;
 
         // Start timestamp for calculating loading time
         const startTime = new Date().getTime();
@@ -280,4 +305,4 @@
     }
 </script>
 </body>
-</html>
\ No newline at end of file
+</html>


Commit: 3ff6ee972d8b3321a53c7e9e4a3e99085245dc3f
    https://github.com/scummvm/scummvm-sites/commit/3ff6ee972d8b3321a53c7e9e4a3e99085245dc3f
Author: Garvit Singla (garvits093 at gmail.com)
Date: 2026-06-11T19:13:22+02:00

Commit Message:
IMAGEDIFF: PunnyEncoded strings name fixed on frontend

Changed paths:
    imagediff/ImageDiffwithPython/main.py
    imagediff/ImageDiffwithPython/templates/movie.html
    imagediff/ImageDiffwithPython/templates/target.html
    imagediff/ImageDiffwithPython/templates/view.html


diff --git a/imagediff/ImageDiffwithPython/main.py b/imagediff/ImageDiffwithPython/main.py
index 8f21600..10d7039 100644
--- a/imagediff/ImageDiffwithPython/main.py
+++ b/imagediff/ImageDiffwithPython/main.py
@@ -8,6 +8,44 @@ from imagediff import image_diff, encode_image, movie_diff
 
 app = Flask(__name__)
 
+# String decoding functions
+def unescape_string(s: str) -> str:
+    """unescape strings"""
+    orig_name = ""
+    s_iter = iter(s)
+    hi = next(s_iter, None)
+    while hi is not None:
+        if hi == "\x81":
+            low = next(s_iter, None)
+            assert low is not None, "Error decoding string"
+            if low == "\x79":
+                orig_name += "\x81"
+            else:
+                orig_name += chr(ord(low) - 0x80)
+        else:
+            orig_name += hi
+        hi = next(s_iter, None)
+    return orig_name
+
+def decode_string(orig: str) -> str:
+    """
+    Decode punyencoded strings
+    """
+    print("Decoding string:", orig)
+    if not orig.startswith("xn--"):
+        return orig
+    
+    print("called for xn")
+    i = len(orig) - 1
+    while i >= 0 and orig[i] == "-":
+        i -= 1
+    
+    orig = orig[:i+1]
+
+    st = orig[4:].encode("ascii").decode("punycode")
+    return unescape_string(st)
+
+
 # cache functions
 def get_cache_page_path(target, page):
     """Generates path: cache/<target>/page_<page>.json"""
@@ -61,12 +99,8 @@ def make_cache_key(target, build1, build2, movie, frame):
 
 def get_frame_number(filename):
     """Extract frame number from filename."""
-    parts = filename.split('-')
-    if len(parts) > 1:
-        try:
-            return int(parts[1].split('.')[0])
-        except ValueError:
-            return 0
+    parts = filename.rsplit('-', 1)
+    return int(parts[1].split('.')[0])
     return 0
 
 
@@ -95,16 +129,15 @@ def collect_movie_frames(target_path, builds):
             if not os.path.isfile(os.path.join(target_path, build, file)):
                 continue
 
-            if "-" in file:
-                parts = file.split("-")
-                movie_name = parts[0]
-                frame_num = parts[1].split('.')[0]
+            
+            movie_name, frame_part = file.rsplit("-", 1)
+            frame_num = frame_part.split('.')[0]
 
-                if movie_name not in build_movie_frames[build]:
-                    build_movie_frames[build][movie_name] = []
+            if movie_name not in build_movie_frames[build]:
+                build_movie_frames[build][movie_name] = []
 
-                build_movie_frames[build][movie_name].append(frame_num)
-                all_movies.add(movie_name)
+            build_movie_frames[build][movie_name].append(frame_num)
+            all_movies.add(movie_name)
 
     return all_movies, build_movie_frames, build_files
 
@@ -154,7 +187,7 @@ def extract_movie_names(files):
     movies = set()
     for file in files:
         if "-" in file:
-            movie_name = file.split("-")[0]
+            movie_name = file.rsplit("-", 1)[0]
             movies.add(movie_name)
     return movies
 
@@ -192,6 +225,7 @@ def target_detail(target):
 
     return render_template('target.html',
         target=target,
+        display_target=decode_string(target),
         builds=paginated_builds,
         page=page,
         total_pages=total_pages,
@@ -428,6 +462,7 @@ def target_data_api(target):
         'target': target,
         'builds': current_page_builds,
         'movies': movies,
+        'display_movies': [decode_string(m) for m in movies],
         'continuous_bars': continuous_bars,
         'urls': urls
     }
@@ -476,6 +511,7 @@ def movie(movie):
 
     return render_template('movie.html',
                            movie=movie,
+                           display_movie=decode_string(movie),
                            target_builds=target_builds,
                            all_builds=all_builds,
                            diff_matrix=diff_matrix)
diff --git a/imagediff/ImageDiffwithPython/templates/movie.html b/imagediff/ImageDiffwithPython/templates/movie.html
index 53e403c..53e4e39 100644
--- a/imagediff/ImageDiffwithPython/templates/movie.html
+++ b/imagediff/ImageDiffwithPython/templates/movie.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
 <html>
 <head>
-    <title>Movie: {{ movie }}</title>
+    <title>Movie: {{ display_movie }}</title>
     <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/style.css') }}" />
 </head>
 <body>
@@ -9,7 +9,7 @@
     <h1><a href="{{ url_for('index') }}">ImageDiff</a></h1>
 </header>
 
-<h1>{{ movie }}</h1>
+<h1>{{ display_movie }}</h1>
 
 <section>
     <h2>Description</h2>
@@ -87,4 +87,4 @@
     </div>
 </section>
 </body>
-</html>
\ No newline at end of file
+</html>
diff --git a/imagediff/ImageDiffwithPython/templates/target.html b/imagediff/ImageDiffwithPython/templates/target.html
index 3fe2f97..6dda88a 100644
--- a/imagediff/ImageDiffwithPython/templates/target.html
+++ b/imagediff/ImageDiffwithPython/templates/target.html
@@ -76,7 +76,7 @@
     <h1><a href="{{ url_for('index') }}">ImageDiff</a></h1>
 </header>
 
-<h1>Target: <span class="code">{{ target }}</span></h1>
+<h1>Target: <span class="code">{{ display_target }}</span></h1>
 
 <section>
     <h2>Recent Results</h2>
@@ -211,11 +211,14 @@
         let tableContent = '';
 
         // Generate table content based on returned data
-        data.movies.forEach(movie => {
+        data.movies.forEach((movie, idx) => {
+         const displayName = data.display_movies[idx];
+            console.log(data);
+            console.log("Processing movie:", movie, "Display name:", displayName);
             tableContent += `
                 <tr>
                     <th>
-                        <a href="${data.urls.movie_url.replace('MOVIE_PLACEHOLDER', movie)}">${movie}</a>
+                        <a href="${data.urls.movie_url.replace('MOVIE_PLACEHOLDER', movie)}">${displayName}</a>
                     </th>
             `;
 
diff --git a/imagediff/ImageDiffwithPython/templates/view.html b/imagediff/ImageDiffwithPython/templates/view.html
index 0eec42b..7f7c521 100644
--- a/imagediff/ImageDiffwithPython/templates/view.html
+++ b/imagediff/ImageDiffwithPython/templates/view.html
@@ -49,4 +49,4 @@
 <p>No frames found for this movie.</p>
 {% endif %}
 </body>
-</html>
\ No newline at end of file
+</html>


Commit: c9fdedca8fe27c026305bf3cf807ad52d4a45fe4
    https://github.com/scummvm/scummvm-sites/commit/c9fdedca8fe27c026305bf3cf807ad52d4a45fe4
Author: Garvit Singla (garvits093 at gmail.com)
Date: 2026-06-11T19:13:22+02:00

Commit Message:
IMAGEDIFF: Decode punnyencoded string on frontend

Changed paths:
    imagediff/ImageDiffwithPython/main.py


diff --git a/imagediff/ImageDiffwithPython/main.py b/imagediff/ImageDiffwithPython/main.py
index 10d7039..d22b598 100644
--- a/imagediff/ImageDiffwithPython/main.py
+++ b/imagediff/ImageDiffwithPython/main.py
@@ -99,9 +99,13 @@ def make_cache_key(target, build1, build2, movie, frame):
 
 def get_frame_number(filename):
     """Extract frame number from filename."""
-    parts = filename.rsplit('-', 1)
-    return int(parts[1].split('.')[0])
-    return 0
+    parts = filename.split('-')
+    if len(parts) > 1:
+        try:
+            return int(parts[1].rsplit('.')[0])
+        except ValueError:
+            return 0
+        
 
 
 def get_sorted_builds(target_path, reverse=True):


Commit: 7beb962aa7c0c3c4274b9f8b898a328a2acf901f
    https://github.com/scummvm/scummvm-sites/commit/7beb962aa7c0c3c4274b9f8b898a328a2acf901f
Author: Garvit Singla (garvits093 at gmail.com)
Date: 2026-06-11T19:13:22+02:00

Commit Message:
IMAGEDIFF: Builds now arranged in increasing order

Changed paths:
    imagediff/ImageDiffwithPython/main.py
    imagediff/ImageDiffwithPython/templates/target.html


diff --git a/imagediff/ImageDiffwithPython/main.py b/imagediff/ImageDiffwithPython/main.py
index d22b598..5ab58f4 100644
--- a/imagediff/ImageDiffwithPython/main.py
+++ b/imagediff/ImageDiffwithPython/main.py
@@ -112,7 +112,12 @@ def get_sorted_builds(target_path, reverse=True):
     """Get sorted list of builds for a target."""
     builds = [b for b in os.listdir(target_path)
               if os.path.isdir(os.path.join(target_path, b))]
-    return sorted(builds, reverse=reverse)
+    # Sort numerically to handle build IDs correctly (e.g., "2" < "245" < "2452")
+    try:
+        return sorted(builds, key=lambda x: int(x), reverse=reverse)
+    except ValueError:
+        # Fall back to string sort if build IDs are not numeric
+        return sorted(builds, reverse=reverse)
 
 def collect_movie_frames(target_path, builds):
     """Collect all movie frames information for all builds."""
@@ -214,11 +219,11 @@ def target_detail(target):
         return "Target not found", 404
 
     # Only get the build list, other time-consuming operations moved to API
-    builds = get_sorted_builds(target_path)
+    builds = get_sorted_builds(target_path,reverse=False)
 
     # Only return the page framework with build list but without table data
     page = int(request.args.get('page', 1))
-    page_size = 10
+    page_size = 20
     start = (page - 1) * page_size
     end = start + page_size
     paginated_builds = builds[start:end]
@@ -240,7 +245,7 @@ def target_detail(target):
 @app.route('/api/target_data/<target>')
 def target_data_api(target):
     page = int(request.args.get('page', 1))
-    page_size = 10
+    page_size = 20
 
     cached_data = load_target_cache(target, page)
     if cached_data:
@@ -251,13 +256,15 @@ def target_data_api(target):
     if not os.path.exists(target_path) or not os.path.isdir(target_path):
         return jsonify({"error": "Target not found"}), 404
 
-    builds = get_sorted_builds(target_path)
+    builds = get_sorted_builds(target_path,reverse=False)
     builds_ascending = get_sorted_builds(target_path, reverse=False)
 
     start_index = (page - 1) * page_size
     end_index = start_index + page_size
     current_page_builds = builds[start_index:end_index]
-    builds_to_process = builds[start_index:end_index + 1] if end_index < len(builds) else builds[start_index:end_index]
+    # Include previous build (from previous page) for comparison continuity
+    process_start = start_index - 1 if start_index > 0 else start_index
+    builds_to_process = builds[process_start:end_index + 1] if end_index < len(builds) else builds[process_start:end_index]
 
     # Collect movie frame data
     all_movies, build_movie_frames, _ = collect_movie_frames(target_path,builds_to_process)
@@ -266,19 +273,19 @@ def target_data_api(target):
     first_build_for_movie = find_first_build_for_movies(
         all_movies, builds_ascending, build_movie_frames)
 
-    # Calculate reference builds
+    # Calculate reference builds (only for builds we're processing)
     movie_reference_builds = {}
     for movie in all_movies:
         movie_reference_builds[movie] = {}
-        for i, current_build in enumerate(builds):
+        for i, current_build in enumerate(builds_to_process):
             has_in_current = movie in build_movie_frames.get(current_build, {})
             current_frames = build_movie_frames.get(current_build, {}).get(movie, [])
 
             if not has_in_current:
                  # Reference build needs to have the movie
                 reference_build = None
-                for j in range(i+1, len(builds)):
-                    next_build = builds[j]
+                for j in range(i-1, -1, -1):
+                    next_build = builds_to_process[j]
                     if movie in build_movie_frames.get(next_build, {}):
                         reference_build = next_build
                         break
@@ -292,8 +299,8 @@ def target_data_api(target):
                     'build': None,
                     'frames': current_frames
                 }
-                for j in range(i+1, len(builds)):
-                    next_build = builds[j]
+                for j in range(i-1, -1, -1):
+                    next_build = builds_to_process[j]
                     if movie in build_movie_frames.get(next_build, {}):
                         next_frames = build_movie_frames.get(next_build, {}).get(movie, [])
                         common_frames = set(current_frames).intersection(set(next_frames))
@@ -350,18 +357,49 @@ def target_data_api(target):
     #     return movie_diff_cache[cache_key]
 
     movies = sorted(list(all_movies))
+    
+    # Define display_builds early: include previous page's last build for continuity
+    display_builds = []
+    if start_index > 0:
+        # Include last build from previous page for continuity
+        display_builds.append(builds[start_index - 1])
+    display_builds.extend(current_page_builds)
+    
+    # True if a build is the context/carry-over build from the previous page
+    context_build = builds[start_index - 1] if start_index > 0 else None
+
     continuous_bars = {}
 
     # Create continuous bars for visualization with updated skip logic
     for movie in movies:
         continuous_bars[movie] = []
 
-        for i, current_build in enumerate(builds):
-            prev_build = builds[i+1] if i < len(builds)-1 else None
+        for i, current_build in enumerate(display_builds):
+            prev_build = display_builds[i-1] if i > 0 else None
 
+            is_context_build = (current_build == context_build)
             has_in_current = movie in build_movie_frames.get(current_build, {})
             current_frames = build_movie_frames.get(current_build, {}).get(movie, [])
 
+            # For the context build: check if this movie appears later in this page
+            # (means it was continuous from previous page ??? show green, no gap)
+            if is_context_build and not has_in_current:
+                movie_appears_later = any(
+                    movie in build_movie_frames.get(b, {})
+                    for b in display_builds[i+1:]
+                )
+                if movie_appears_later:
+                    continuous_bars[movie].append({
+                        'build': current_build,
+                        'type': 'no_prev'  # green, no magnifier
+                    })
+                else:
+                    continuous_bars[movie].append({
+                        'build': current_build,
+                        'type': 'missing'
+                    })
+                continue
+
             prev_frames = []
             if prev_build:
                 prev_frames = build_movie_frames.get(prev_build, {}).get(movie, [])
@@ -442,11 +480,20 @@ def target_data_api(target):
                         'no_reference': reference_build is None
                     })
             elif not prev_build:
-                continuous_bars[movie].append({
-                    'build': current_build,
-                    'type': 'unknown'
-                })
+                # No previous build context available (first entry on this page slice)
+                # Show green if movie exists (continuity carry-over), white if not
+                if has_in_current:
+                    continuous_bars[movie].append({
+                        'build': current_build,
+                        'type': 'no_prev',   # green with no magnifier ??? nothing to compare against
+                    })
+                else:
+                    continuous_bars[movie].append({
+                        'build': current_build,
+                        'type': 'missing'
+                    })
 
+    # continuous_bars is now already built with display_builds in the correct order
     # Generate URL templates needed by frontend
     urls = {
         'movie_url': url_for('movie', movie='MOVIE_PLACEHOLDER'),
@@ -464,7 +511,7 @@ def target_data_api(target):
     # Return all data to frontend
     data = {
         'target': target,
-        'builds': current_page_builds,
+        'builds': display_builds,
         'movies': movies,
         'display_movies': [decode_string(m) for m in movies],
         'continuous_bars': continuous_bars,
@@ -484,7 +531,7 @@ def movie(movie):
         target_path = os.path.join(SCREENSHOTS_DIR, target)
         if os.path.isdir(target_path):
             # Get sorted builds
-            builds = get_sorted_builds(target_path)
+            builds = get_sorted_builds(target_path,reverse=False)
 
             # Find builds containing this movie
             builds_with_movie = []
@@ -497,9 +544,9 @@ def movie(movie):
                     all_builds.add(build)
 
             # Calculate diffs between consecutive builds
-            for i in range(len(builds_with_movie) - 1):
+            for i in range(1,len(builds_with_movie)):
                 current_build = builds_with_movie[i]
-                prev_build = builds_with_movie[i + 1]
+                prev_build = builds_with_movie[i - 1]
 
                 has_diff = movie_diff(current_build, prev_build, target, movie)
 
@@ -511,7 +558,7 @@ def movie(movie):
             if builds_with_movie:
                 target_builds[target] = builds_with_movie
 
-    all_builds = sorted(list(all_builds), reverse=True)
+    all_builds = sorted(list(all_builds), reverse=False)
 
     return render_template('movie.html',
                            movie=movie,
@@ -530,7 +577,7 @@ def build(build):
             continue
 
         # Get sorted builds
-        builds = get_sorted_builds(target_path)
+        builds = get_sorted_builds(target_path,reverse=False)
 
         # Check if current build exists in this target
         if build not in builds:
@@ -538,7 +585,7 @@ def build(build):
 
         # Find the previous build
         build_index = builds.index(build)
-        prev_build = builds[build_index + 1] if build_index < len(builds) - 1 else None
+        prev_build = builds[build_index - 1] if build_index > 0 else None
 
         # Find all movies in this build
         build_path = os.path.join(target_path, build)
diff --git a/imagediff/ImageDiffwithPython/templates/target.html b/imagediff/ImageDiffwithPython/templates/target.html
index 6dda88a..841d13e 100644
--- a/imagediff/ImageDiffwithPython/templates/target.html
+++ b/imagediff/ImageDiffwithPython/templates/target.html
@@ -281,6 +281,10 @@
             `;
         } else if (cell.type === 'missing') {
             return `<td class="no-data"></td>`;
+        } else if (cell.type === 'no_prev') {
+            // Movie exists but no previous build to compare against (page boundary context)
+            // Show plain green ??? continuous color, no magnifier
+            return `<td class="success no-diff" title="Build ${cell.build}: exists, no previous data to compare"></td>`;
         } else if (cell.type === 'readded') {
             if (cell.compare_with) {
                 return `


Commit: c018c3bfbdbb4d6d9fbf60d7848875339ba4dd35
    https://github.com/scummvm/scummvm-sites/commit/c018c3bfbdbb4d6d9fbf60d7848875339ba4dd35
Author: Garvit Singla (garvits093 at gmail.com)
Date: 2026-06-11T19:13:22+02:00

Commit Message:
IMAGEDIFF: Cleanup all PHP code

Changed paths:
  R imagediff/ImageDiffwithPython/__pycache__/config.cpython-314.pyc
  R imagediff/ImageDiffwithPython/__pycache__/imagediff.cpython-314.pyc
  R imagediff/ImageDiffwithPython/cache.json
  R imagediff/ImageDiffwithPython/movie_cache.json


diff --git a/imagediff/ImageDiffwithPython/__pycache__/config.cpython-314.pyc b/imagediff/ImageDiffwithPython/__pycache__/config.cpython-314.pyc
deleted file mode 100644
index 2f099fd..0000000
Binary files a/imagediff/ImageDiffwithPython/__pycache__/config.cpython-314.pyc and /dev/null differ
diff --git a/imagediff/ImageDiffwithPython/__pycache__/imagediff.cpython-314.pyc b/imagediff/ImageDiffwithPython/__pycache__/imagediff.cpython-314.pyc
deleted file mode 100644
index af8a502..0000000
Binary files a/imagediff/ImageDiffwithPython/__pycache__/imagediff.cpython-314.pyc and /dev/null differ
diff --git a/imagediff/ImageDiffwithPython/cache.json b/imagediff/ImageDiffwithPython/cache.json
deleted file mode 100644
index 2021560..0000000
--- a/imagediff/ImageDiffwithPython/cache.json
+++ /dev/null
@@ -1 +0,0 @@
-{"2492_2489_BasicUserEvents_1": {"src_img_data": " ", "cmp_img_data": " ", "diff_img_data": " ", "has_diff": true}, "2461_2460_BasicUserEvents_12": {"src_img_data": " ", "cmp_img_data": " ", "diff_img_data": " ", "has_diff": true}, "2492_2460_Buttons_1": {"src_img_data": " ", "cmp_img_data": " ", "diff_img_data": " ", "has_diff": true}, "2492_2460_Cursors_1": {"src_img_data": " ", "cmp_img_data": " ", "diff_img_data": " ", "has_diff": true}, "2492_2454_Face kit_1": {"src_img_data": " ", "cmp_img_data": " ", "diff_img_data": " ", "has_diff": true}, "2492_2460_Finder Demo_1": {"src_img_data": " ", "cmp_img_data": " ", "diff_img_data": " ", "has_diff": true}, "2492_2460_Gravity Ball_1": {"src_img_data": " ", "cmp_img_data": " ", "diff_img_data": " ", "has_diff": true}, "2492_2460_Hierarchical Bird_1": {"src_img_data": " ", "cmp_img_data": " ", "diff_img_data": " ", "has_diff": true}, "2492_2460_Maze_9": {"src_img_data": " ", "cmp_img_data": " ", "diff_img_data": " ", "has_diff": true}, "2492_2460_More Puppets_1": {"src_img_data": " ", "cmp_img_data": " ", "diff_img_data": " ", "has_diff": true}, "2492_2460_Pattern Viewer_1": {"src_img_data": " ", "cmp_img_data": " ", "diff_img_data": " ", "has_diff": true}, "2492_2460_PopUp Menu_1": {"src_img_data": " ", "cmp_img_data": " ", "diff_img_data": " ", "has_diff": true}, "2492_2489_Puppet Palettes_1": {"src_img_data": " ", "cmp_img_data": " ", "diff_img_data": " ", "has_diff": true}, "2492_2454_Running Boy_1": {"src_img_data": " ", "cmp_img_data": " ", "diff_img_data": " ", "has_diff": true}, "2492_2460_Saving Text_1": {"src_img_data": " ", "cmp_img_data": " ", "diff_img_data": " ", "has_diff": true}, "2492_2460_Simple Bird_1": {"src_img_data": " ", "cmp_img_data": " ", "diff_img_data": " ", "has_diff": true}, "2492_2489_Simple Puppets_1": {"src_img_data": " ", "cmp_img_data": " ", "diff_img_data": " ", "has_diff": true}, "2492_2460_Testing User_1": {"src_img_data": " ", "cmp_img_data": " ", "diff_img_data": " ", "has_diff": true}, "2492_2460_User Menus_1": {"src_img_data": " ", "cmp_img_data": " ", "diff_img_data": " ", "has_diff": true}, "2492_2460_Windows_1": {"src_img_data": " ", "cmp_img_data": " ", "diff_img_data": " ", "has_diff": true}, "2492_2460_XCMDGlue_1": {"src_img_data": " ", "cmp_img_data": " ", "diff_img_data": " ", "has_diff": true}, "2492_2460_xn_": {"has_diff": true}, "2461_2460_BasicUserEvents_7": {"src_img_data": " ", "cmp_img_data": " ", "diff_img_data": " ", "has_diff": true}, "2492_2460_Maze_7": {"src_img_data": " ", "cmp_img_data": " ", "diff_img_data": " ", "has_diff": true}, "2461_2460_BasicUserEvents_1": {"src_img_data": " ", "cmp_img_data": " ", "diff_img_data": " ", "has_diff": true}}
\ No newline at end of file
diff --git a/imagediff/ImageDiffwithPython/movie_cache.json b/imagediff/ImageDiffwithPython/movie_cache.json
deleted file mode 100644
index ad6d6dc..0000000
--- a/imagediff/ImageDiffwithPython/movie_cache.json
+++ /dev/null
@@ -1 +0,0 @@
-{"2489_2488_BasicUserEvents_movie": true, "2488_2482_BasicUserEvents_movie": true, "2482_2481_BasicUserEvents_movie": true, "2481_2475_BasicUserEvents_movie": true, "2475_2474_BasicUserEvents_movie": true, "2474_2468_BasicUserEvents_movie": true, "2468_2467_BasicUserEvents_movie": true, "2467_2461_BasicUserEvents_movie": true, "2460_2459_BasicUserEvents_movie": true, "2459_2458_BasicUserEvents_movie": true, "2458_2454_BasicUserEvents_movie": true, "2454_2453_BasicUserEvents_movie": false, "2453_2452_BasicUserEvents_movie": false, "2460_2459_Buttons_movie": false, "2459_2458_Buttons_movie": false, "2458_2454_Buttons_movie": false, "2454_2453_Buttons_movie": false, "2453_2452_Buttons_movie": false, "2460_2459_CD_movie": false, "2459_2458_CD_movie": false, "2460_2459_Cursors_movie": false, "2459_2458_Cursors_movie": false, "2458_2454_Cursors_movie": false, "2454_2453_Cursors_movie": false, "2453_2452_Cursors_movie": false, "2460_2459_Face Kit_movie": false, "2459_2458_Face Kit_movie": false, "2454_2453_Face kit_movie": false, "2453_2452_Face kit_movie": false, "2460_2459_Finder Demo_movie": false, "2459_2458_Finder Demo_movie": false, "2458_2454_Finder Demo_movie": false, "2454_2453_Finder Demo_movie": false, "2453_2452_Finder Demo_movie": false, "2460_2459_Gravity Ball_movie": false, "2459_2458_Gravity Ball_movie": false, "2458_2454_Gravity Ball_movie": false, "2454_2453_Gravity Ball_movie": false, "2453_2452_Gravity Ball_movie": false, "2460_2459_Hierarchical Bird_movie": false, "2459_2458_Hierarchical Bird_movie": false, "2458_2454_Hierarchical Bird_movie": false, "2454_2453_Hierarchical Bird_movie": false, "2453_2452_Hierarchical Bird_movie": false, "2460_2459_Indicators_movie": false, "2459_2458_Indicators_movie": false, "2458_2454_Indicators_movie": false, "2454_2453_Indicators_movie": false, "2453_2452_Indicators_movie": false, "2460_2459_Maze_movie": false, "2459_2458_Maze_movie": false, "2458_2454_Maze_movie": false, "2454_2453_Maze_movie": false, "2453_2452_Maze_movie": false, "2460_2459_More Puppets_movie": false, "2459_2458_More Puppets_movie": false, "2458_2454_More Puppets_movie": false, "2454_2453_More Puppets_movie": false, "2453_2452_More Puppets_movie": false, "2460_2459_Pattern Viewer_movie": false, "2459_2458_Pattern Viewer_movie": false, "2458_2454_Pattern Viewer_movie": false, "2454_2453_Pattern Viewer_movie": false, "2453_2452_Pattern Viewer_movie": false, "2460_2459_PopUp Menu_movie": false, "2459_2458_PopUp Menu_movie": false, "2458_2454_PopUp Menu_movie": false, "2454_2453_PopUp Menu_movie": false, "2453_2452_PopUp Menu_movie": false, "2489_2488_Puppet Palettes_movie": true, "2488_2482_Puppet Palettes_movie": true, "2482_2481_Puppet Palettes_movie": true, "2481_2475_Puppet Palettes_movie": true, "2475_2474_Puppet Palettes_movie": true, "2474_2468_Puppet Palettes_movie": true, "2468_2467_Puppet Palettes_movie": true, "2467_2461_Puppet Palettes_movie": true, "2461_2460_Puppet Palettes_movie": true, "2460_2459_Puppet Palettes_movie": true, "2459_2458_Puppet Palettes_movie": true, "2458_2454_Puppet Palettes_movie": true, "2454_2453_Puppet Palettes_movie": false, "2453_2452_Puppet Palettes_movie": false, "2454_2453_Running Boy_movie": false, "2453_2452_Running Boy_movie": false, "2460_2459_Saving Text_movie": false, "2459_2458_Saving Text_movie": false, "2458_2454_Saving Text_movie": false, "2454_2453_Saving Text_movie": false, "2453_2452_Saving Text_movie": false, "2460_2459_Simple Bird_movie": false, "2459_2458_Simple Bird_movie": false, "2458_2454_Simple Bird_movie": false, "2454_2453_Simple Bird_movie": false, "2453_2452_Simple Bird_movie": false, "2489_2488_Simple Puppets_movie": true, "2488_2482_Simple Puppets_movie": true, "2482_2481_Simple Puppets_movie": true, "2481_2475_Simple Puppets_movie": true, "2475_2474_Simple Puppets_movie": true, "2474_2468_Simple Puppets_movie": true, "2468_2467_Simple Puppets_movie": true, "2467_2461_Simple Puppets_movie": true, "2461_2460_Simple Puppets_movie": true, "2460_2459_Simple Puppets_movie": true, "2459_2458_Simple Puppets_movie": true, "2458_2454_Simple Puppets_movie": true, "2454_2453_Simple Puppets_movie": false, "2453_2452_Simple Puppets_movie": false, "2460_2459_Testing User_movie": false, "2459_2458_Testing User_movie": false, "2458_2454_Testing User_movie": false, "2454_2453_Testing User_movie": false, "2453_2452_Testing User_movie": false, "2460_2459_User Menus_movie": false, "2459_2458_User Menus_movie": false, "2458_2454_User Menus_movie": false, "2454_2453_User Menus_movie": false, "2453_2452_User Menus_movie": false, "2460_2459_Videodisc_movie": false, "2459_2458_Videodisc_movie": false, "2460_2459_Windows_movie": false, "2459_2458_Windows_movie": false, "2458_2454_Windows_movie": false, "2454_2453_Windows_movie": false, "2453_2452_Windows_movie": false, "2460_2459_XCMDGlue_movie": false, "2459_2458_XCMDGlue_movie": false, "2458_2454_XCMDGlue_movie": false, "2454_2453_XCMDGlue_movie": false, "2453_2452_XCMDGlue_movie": false, "2460_2459_editableText_movie": false, "2459_2458_editableText_movie": false, "2460_2459_moveableSprite_movie": false, "2459_2458_moveableSprite_movie": false, "2460_2459_rollOver_movie": false, "2459_2458_rollOver_movie": false, "2460_2459_xn_movie": false, "2459_2458_xn_movie": false, "2458_2454_xn_movie": false, "2454_2453_xn_movie": false, "2453_2452_xn_movie": false, "2460_2459_zoomBox_movie": false, "2459_2458_zoomBox_movie": false}
\ No newline at end of file


Commit: eb97b9fcac044823678c3a6c498778ffc55ed647
    https://github.com/scummvm/scummvm-sites/commit/eb97b9fcac044823678c3a6c498778ffc55ed647
Author: Garvit Singla (garvits093 at gmail.com)
Date: 2026-06-11T19:13:22+02:00

Commit Message:
IMAGEDIFF: Cleanup all PHP Styles and code

Changed paths:
  R imagediff/LICENSE
  R imagediff/NOTICE
  R imagediff/assets/styles/main.css
  R imagediff/index.php
  R imagediff/src/display_images.php
  R imagediff/src/file_browser.php
  R imagediff/src/imagediff.php
    imagediff/.gitignore


diff --git a/imagediff/.gitignore b/imagediff/.gitignore
index 23b614b..ccef7c2 100644
--- a/imagediff/.gitignore
+++ b/imagediff/.gitignore
@@ -472,4 +472,9 @@ composer.lock
 *.css.map
 *.sass.map
 *.scss.map
-sample-target 
\ No newline at end of file
+sample-target 
+ImageDiffwithPython/screenshots/director
+ImageDiffwithPython/screenshots/sample-target
+ImageDiffwithPython/screenshots/theapartment-mac
+cache/
+__pycache__
diff --git a/imagediff/LICENSE b/imagediff/LICENSE
deleted file mode 100644
index d9a10c0..0000000
--- a/imagediff/LICENSE
+++ /dev/null
@@ -1,176 +0,0 @@
-                                 Apache License
-                           Version 2.0, January 2004
-                        http://www.apache.org/licenses/
-
-   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
-   1. Definitions.
-
-      "License" shall mean the terms and conditions for use, reproduction,
-      and distribution as defined by Sections 1 through 9 of this document.
-
-      "Licensor" shall mean the copyright owner or entity authorized by
-      the copyright owner that is granting the License.
-
-      "Legal Entity" shall mean the union of the acting entity and all
-      other entities that control, are controlled by, or are under common
-      control with that entity. For the purposes of this definition,
-      "control" means (i) the power, direct or indirect, to cause the
-      direction or management of such entity, whether by contract or
-      otherwise, or (ii) ownership of fifty percent (50%) or more of the
-      outstanding shares, or (iii) beneficial ownership of such entity.
-
-      "You" (or "Your") shall mean an individual or Legal Entity
-      exercising permissions granted by this License.
-
-      "Source" form shall mean the preferred form for making modifications,
-      including but not limited to software source code, documentation
-      source, and configuration files.
-
-      "Object" form shall mean any form resulting from mechanical
-      transformation or translation of a Source form, including but
-      not limited to compiled object code, generated documentation,
-      and conversions to other media types.
-
-      "Work" shall mean the work of authorship, whether in Source or
-      Object form, made available under the License, as indicated by a
-      copyright notice that is included in or attached to the work
-      (an example is provided in the Appendix below).
-
-      "Derivative Works" shall mean any work, whether in Source or Object
-      form, that is based on (or derived from) the Work and for which the
-      editorial revisions, annotations, elaborations, or other modifications
-      represent, as a whole, an original work of authorship. For the purposes
-      of this License, Derivative Works shall not include works that remain
-      separable from, or merely link (or bind by name) to the interfaces of,
-      the Work and Derivative Works thereof.
-
-      "Contribution" shall mean any work of authorship, including
-      the original version of the Work and any modifications or additions
-      to that Work or Derivative Works thereof, that is intentionally
-      submitted to Licensor for inclusion in the Work by the copyright owner
-      or by an individual or Legal Entity authorized to submit on behalf of
-      the copyright owner. For the purposes of this definition, "submitted"
-      means any form of electronic, verbal, or written communication sent
-      to the Licensor or its representatives, including but not limited to
-      communication on electronic mailing lists, source code control systems,
-      and issue tracking systems that are managed by, or on behalf of, the
-      Licensor for the purpose of discussing and improving the Work, but
-      excluding communication that is conspicuously marked or otherwise
-      designated in writing by the copyright owner as "Not a Contribution."
-
-      "Contributor" shall mean Licensor and any individual or Legal Entity
-      on behalf of whom a Contribution has been received by Licensor and
-      subsequently incorporated within the Work.
-
-   2. Grant of Copyright License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      copyright license to reproduce, prepare Derivative Works of,
-      publicly display, publicly perform, sublicense, and distribute the
-      Work and such Derivative Works in Source or Object form.
-
-   3. Grant of Patent License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      (except as stated in this section) patent license to make, have made,
-      use, offer to sell, sell, import, and otherwise transfer the Work,
-      where such license applies only to those patent claims licensable
-      by such Contributor that are necessarily infringed by their
-      Contribution(s) alone or by combination of their Contribution(s)
-      with the Work to which such Contribution(s) was submitted. If You
-      institute patent litigation against any entity (including a
-      cross-claim or counterclaim in a lawsuit) alleging that the Work
-      or a Contribution incorporated within the Work constitutes direct
-      or contributory patent infringement, then any patent licenses
-      granted to You under this License for that Work shall terminate
-      as of the date such litigation is filed.
-
-   4. Redistribution. You may reproduce and distribute copies of the
-      Work or Derivative Works thereof in any medium, with or without
-      modifications, and in Source or Object form, provided that You
-      meet the following conditions:
-
-      (a) You must give any other recipients of the Work or
-          Derivative Works a copy of this License; and
-
-      (b) You must cause any modified files to carry prominent notices
-          stating that You changed the files; and
-
-      (c) You must retain, in the Source form of any Derivative Works
-          that You distribute, all copyright, patent, trademark, and
-          attribution notices from the Source form of the Work,
-          excluding those notices that do not pertain to any part of
-          the Derivative Works; and
-
-      (d) If the Work includes a "NOTICE" text file as part of its
-          distribution, then any Derivative Works that You distribute must
-          include a readable copy of the attribution notices contained
-          within such NOTICE file, excluding those notices that do not
-          pertain to any part of the Derivative Works, in at least one
-          of the following places: within a NOTICE text file distributed
-          as part of the Derivative Works; within the Source form or
-          documentation, if provided along with the Derivative Works; or,
-          within a display generated by the Derivative Works, if and
-          wherever such third-party notices normally appear. The contents
-          of the NOTICE file are for informational purposes only and
-          do not modify the License. You may add Your own attribution
-          notices within Derivative Works that You distribute, alongside
-          or as an addendum to the NOTICE text from the Work, provided
-          that such additional attribution notices cannot be construed
-          as modifying the License.
-
-      You may add Your own copyright statement to Your modifications and
-      may provide additional or different license terms and conditions
-      for use, reproduction, or distribution of Your modifications, or
-      for any such Derivative Works as a whole, provided Your use,
-      reproduction, and distribution of the Work otherwise complies with
-      the conditions stated in this License.
-
-   5. Submission of Contributions. Unless You explicitly state otherwise,
-      any Contribution intentionally submitted for inclusion in the Work
-      by You to the Licensor shall be under the terms and conditions of
-      this License, without any additional terms or conditions.
-      Notwithstanding the above, nothing herein shall supersede or modify
-      the terms of any separate license agreement you may have executed
-      with Licensor regarding such Contributions.
-
-   6. Trademarks. This License does not grant permission to use the trade
-      names, trademarks, service marks, or product names of the Licensor,
-      except as required for reasonable and customary use in describing the
-      origin of the Work and reproducing the content of the NOTICE file.
-
-   7. Disclaimer of Warranty. Unless required by applicable law or
-      agreed to in writing, Licensor provides the Work (and each
-      Contributor provides its Contributions) on an "AS IS" BASIS,
-      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
-      implied, including, without limitation, any warranties or conditions
-      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
-      PARTICULAR PURPOSE. You are solely responsible for determining the
-      appropriateness of using or redistributing the Work and assume any
-      risks associated with Your exercise of permissions under this License.
-
-   8. Limitation of Liability. In no event and under no legal theory,
-      whether in tort (including negligence), contract, or otherwise,
-      unless required by applicable law (such as deliberate and grossly
-      negligent acts) or agreed to in writing, shall any Contributor be
-      liable to You for damages, including any direct, indirect, special,
-      incidental, or consequential damages of any character arising as a
-      result of this License or out of the use or inability to use the
-      Work (including but not limited to damages for loss of goodwill,
-      work stoppage, computer failure or malfunction, or any and all
-      other commercial damages or losses), even if such Contributor
-      has been advised of the possibility of such damages.
-
-   9. Accepting Warranty or Additional Liability. While redistributing
-      the Work or Derivative Works thereof, You may choose to offer,
-      and charge a fee for, acceptance of support, warranty, indemnity,
-      or other liability obligations and/or rights consistent with this
-      License. However, in accepting such obligations, You may act only
-      on Your own behalf and on Your sole responsibility, not on behalf
-      of any other Contributor, and only if You agree to indemnify,
-      defend, and hold each Contributor harmless for any liability
-      incurred by, or claims asserted against, such Contributor by reason
-      of your accepting any such warranty or additional liability.
-
-   END OF TERMS AND CONDITIONS
diff --git a/imagediff/NOTICE b/imagediff/NOTICE
deleted file mode 100644
index 8995df6..0000000
--- a/imagediff/NOTICE
+++ /dev/null
@@ -1,13 +0,0 @@
-imagediff
-Copyright 2022 Abhinav Chennubhotla
-
-imagediff was originally developed and maintained by Abhinav Chennubhotla
-
-Portions of this software were developed by various contributors, who retain
-copyright on their work. These works are licensed to Abhinav Chennubhotla
-
-imagediff is available under the Apache 2.0 license. See LICENSE for more
-information.
-
-This software uses other open source libraries. These libraries have their own
-licenses and copyright holders.
diff --git a/imagediff/assets/styles/main.css b/imagediff/assets/styles/main.css
deleted file mode 100644
index 46e764c..0000000
--- a/imagediff/assets/styles/main.css
+++ /dev/null
@@ -1,3 +0,0 @@
-.compare {
-    width: 33%;
-}
diff --git a/imagediff/index.php b/imagediff/index.php
deleted file mode 100644
index 746804f..0000000
--- a/imagediff/index.php
+++ /dev/null
@@ -1,20 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-
-<head>
-  <title>ImageDiff</title>
-
-  <meta charset="UTF-8" />
-  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
-  <link rel="stylesheet" type="text/css" href="assets/styles/main.css" />
-
-  <?php require __DIR__ . '/src/file_browser.php' ?>
-</head>
-
-<body>
-  <div class="file-browser">
-    <?php displayDir(isset($_GET['dir']) ? $_GET['dir'] : '.') ?>
-  </div>
-</body>
-
-</html>
diff --git a/imagediff/src/display_images.php b/imagediff/src/display_images.php
deleted file mode 100644
index 1364f95..0000000
--- a/imagediff/src/display_images.php
+++ /dev/null
@@ -1,48 +0,0 @@
-<link rel="stylesheet" type="text/css" href="../assets/styles/main.css" />
-
-<?php
-require __DIR__ . "/imagediff.php";
-
-$src_img_path = isset($_GET['image']) ? $_GET['image'] : '';
-if (!$src_img_path) {
-  echo "<h2>Invalid Image</h2>";
-}
-
-$path_parts = explode(DIRECTORY_SEPARATOR, $src_img_path);
-$target_dir = join("/", array_slice($path_parts, 0, count($path_parts) - 2));
-$src_filename = end($path_parts);
-
-$builds = scandir($target_dir);
-rsort($builds);
-
-$loop_count = 0;
-foreach ($builds as $build) {
-  $build_path = $target_dir . "/" . $build;
-  if (!is_dir($build_path) or in_array($build, array(".", "..")))
-    continue;
-
-  $files = scandir($build_path);
-
-  foreach ($files as $file) {
-    if ($loop_count >= 3)
-      break;
-
-    $cmp_img_path = $build_path . "/" . $file;
-    if ($file == $src_filename and $cmp_img_path != $src_img_path) {
-      $res = image_diff($src_img_path, $cmp_img_path);
-      // error_log("<img class=\"compare\" src=\"" . get_relative_path(realpath("./"), $cmp_img_path) . "\" />");
-
-      echo "<img class=\"compare\" src=\"data:image/png;base64," .
-        base64_encode(file_get_contents($src_img_path)) . "\" />";
-      echo "<img class=\"compare\" src=\"data:image/png;base64," . $res . "\" />";
-      echo "<img class=\"compare\" src=\"data:image/png;base64," .
-        base64_encode(file_get_contents($cmp_img_path)) . "\" />";
-      echo "<p />";
-
-      $loop_count++;
-    }
-  }
-}
-
-?>
-
diff --git a/imagediff/src/file_browser.php b/imagediff/src/file_browser.php
deleted file mode 100644
index f7c9789..0000000
--- a/imagediff/src/file_browser.php
+++ /dev/null
@@ -1,63 +0,0 @@
-<?php function displayDir($dir = './') { ?>
-  <?php
-  function file_sort($a, $b) {
-    return ($cmp = is_dir($b) - is_dir($a)) ? $cmp : strcmp($a, $b);
-  }
-
-  $current_dir = realpath($dir);
-  $dir_content = scandir($current_dir);
-  usort($dir_content, 'file_sort');
-  ?>
-
-  <div>
-    <?php
-    $path_parts = explode(DIRECTORY_SEPARATOR, $current_dir);
-    $breadcrumbs = '';
-    foreach ($path_parts as $part) {
-      if (!$part) {
-        continue;
-      }
-      $breadcrumbs .= $part . DIRECTORY_SEPARATOR;
-      echo DIRECTORY_SEPARATOR . '<a href="?dir=' . '/' . $breadcrumbs . '">' . $part . '</a>';
-    }
-    ?>
-  </div>
-
-  <table>
-    <thead>
-      <tr>
-        <th>Name</th>
-        <th>Size</th>
-      </tr>
-    </thead>
-    <tbody>
-      <?php foreach ($dir_content as $item):
-
-        if ($item == '.'):
-          continue;
-        endif;
-        $item_path = $current_dir . DIRECTORY_SEPARATOR . $item;
-        ?>
-        <tr>
-          <td>
-            <?php if (is_dir($item_path)): ?>
-              <a href="?dir=<?php echo $item_path; ?>">
-                <?php echo $item; ?>
-              </a>
-            <?php elseif (getimagesize($item_path)): ?>
-              <a href="src/display_images.php?image=<?php echo $item_path; ?>">
-                <?php echo $item; ?>
-              </a>
-            <?php else: ?>
-              <?php echo $item; ?>
-            <?php endif; ?>
-          </td>
-          <td>
-            <?php echo is_dir($item_path) ? '' : filesize($item_path) . ' b'; ?>
-          </td>
-        </tr>
-      <?php endforeach; ?>
-    </tbody>
-  </table>
-<?php } ?>
-
diff --git a/imagediff/src/imagediff.php b/imagediff/src/imagediff.php
deleted file mode 100644
index 677a66c..0000000
--- a/imagediff/src/imagediff.php
+++ /dev/null
@@ -1,47 +0,0 @@
-<?php
-
-function pixel_diff(GDImage &$res, int $pixel1, int $pixel2) {
-  $r1 = ($pixel1 >> 16) & 0xFF;
-  $g1 = ($pixel1 >> 8) & 0xFF;
-  $b1 = $pixel1 & 0xFF;
-
-  $r2 = ($pixel2 >> 16) & 0xFF;
-  $g2 = ($pixel2 >> 8) & 0xFF;
-  $b2 = $pixel2 & 0xFF;
-
-  $r = abs($r1 - $r2);
-  $g = abs($g1 - $g2);
-  $b = abs($b1 - $b2);
-
-  return imagecolorallocate($res, $r, $g, $b);
-}
-
-function image_diff($path1, $path2) {
-  $img1 = imagecreatefrompng($path1);
-  $img2 = imagecreatefrompng($path2);
-  $width = imagesx($img1);
-  $height = imagesy($img1);
-
-  $res = @imagecreatetruecolor($width, $height)
-    or die("Cannot Initialize new GD image stream");
-  $bg = imagecolorallocate($res, 0, 0, 0);
-
-  for ($i = 0; $i < $width; $i++) {
-    for ($j = 0; $j < $height; $j++) {
-      imagesetpixel($res, $i, $j, pixel_diff(
-        $res,
-        imagecolorat($img1, $i, $j),
-        imagecolorat($img2, $i, $j)
-      ));
-    }
-  }
-
-  ob_start();
-  imagepng($res);
-
-  return base64_encode(ob_get_clean());
-}
-
-
-?>
-


Commit: 21c4f09a43e78aa91db4732c4658c66bbb07d38f
    https://github.com/scummvm/scummvm-sites/commit/21c4f09a43e78aa91db4732c4658c66bbb07d38f
Author: Garvit Singla (garvits093 at gmail.com)
Date: 2026-06-11T19:13:22+02:00

Commit Message:
IMAGEDIFF: Moved python code upto root and made installation guide

Changed paths:
  A imagediff/config.py
  A imagediff/imagediff.py
  A imagediff/main.py
  A imagediff/requirements.txt
  A imagediff/static/css/style.css
  A imagediff/templates/build.html
  A imagediff/templates/compare.html
  A imagediff/templates/index.html
  A imagediff/templates/movie.html
  A imagediff/templates/target.html
  A imagediff/templates/view.html
  R imagediff/ImageDiffwithPython/config.py
  R imagediff/ImageDiffwithPython/imagediff.py
  R imagediff/ImageDiffwithPython/main.py
  R imagediff/ImageDiffwithPython/static/css/style.css
  R imagediff/ImageDiffwithPython/templates/build.html
  R imagediff/ImageDiffwithPython/templates/compare.html
  R imagediff/ImageDiffwithPython/templates/index.html
  R imagediff/ImageDiffwithPython/templates/movie.html
  R imagediff/ImageDiffwithPython/templates/target.html
  R imagediff/ImageDiffwithPython/templates/view.html
    imagediff/.gitignore
    imagediff/README.md


diff --git a/imagediff/.gitignore b/imagediff/.gitignore
index ccef7c2..9f5a2d0 100644
--- a/imagediff/.gitignore
+++ b/imagediff/.gitignore
@@ -473,8 +473,9 @@ composer.lock
 *.sass.map
 *.scss.map
 sample-target 
-ImageDiffwithPython/screenshots/director
-ImageDiffwithPython/screenshots/sample-target
-ImageDiffwithPython/screenshots/theapartment-mac
+director/
+sample-target/
+theapartment-mac/
 cache/
 __pycache__
+venv
diff --git a/imagediff/README.md b/imagediff/README.md
index 4fc7461..f89a5d3 100644
--- a/imagediff/README.md
+++ b/imagediff/README.md
@@ -1,11 +1,45 @@
 # ImageDiff
 
-### Running
+Web tool to compare and highlight differences between two images (built with Flask and Pillow).
 
-The server can be run using
+## Installation
 
-    php -S localhost:8000
+1. **Clone the repository**
 
-The server will be hosted on port 8000. Visit http://localhost:8000/ to access the website.
+   ```bash
+   git clone https://github.com/sev-/ImageDiff.git
+   cd ImageDiff
+   ```
 
-##### This project is  maintained by [Abhinav Chennubhotla](https://github.com/PhoenixFlame101).
+2. **Create a virtual environment**
+
+   **Linux / macOS**
+   ```bash
+   python3 -m venv venv
+   source venv/bin/activate
+   ```
+
+   **Windows**
+   ```bash
+   python -m venv venv
+   venv\Scripts\activate
+   ```
+
+3. **Install dependencies**
+   ```bash
+   pip install -r requirements.txt
+   ```
+
+## Running the server
+
+Start the application:
+
+```bash
+python app.py
+```
+
+Open in your browser:
+
+```
+http://127.0.0.1:5001
+```
diff --git a/imagediff/ImageDiffwithPython/config.py b/imagediff/config.py
similarity index 100%
rename from imagediff/ImageDiffwithPython/config.py
rename to imagediff/config.py
diff --git a/imagediff/ImageDiffwithPython/imagediff.py b/imagediff/imagediff.py
similarity index 100%
rename from imagediff/ImageDiffwithPython/imagediff.py
rename to imagediff/imagediff.py
diff --git a/imagediff/ImageDiffwithPython/main.py b/imagediff/main.py
similarity index 100%
rename from imagediff/ImageDiffwithPython/main.py
rename to imagediff/main.py
diff --git a/imagediff/requirements.txt b/imagediff/requirements.txt
new file mode 100644
index 0000000..039345d
--- /dev/null
+++ b/imagediff/requirements.txt
@@ -0,0 +1,2 @@
+Flask==3.0.3
+Pillow==12.1.1
diff --git a/imagediff/ImageDiffwithPython/static/css/style.css b/imagediff/static/css/style.css
similarity index 100%
rename from imagediff/ImageDiffwithPython/static/css/style.css
rename to imagediff/static/css/style.css
diff --git a/imagediff/ImageDiffwithPython/templates/build.html b/imagediff/templates/build.html
similarity index 100%
rename from imagediff/ImageDiffwithPython/templates/build.html
rename to imagediff/templates/build.html
diff --git a/imagediff/ImageDiffwithPython/templates/compare.html b/imagediff/templates/compare.html
similarity index 100%
rename from imagediff/ImageDiffwithPython/templates/compare.html
rename to imagediff/templates/compare.html
diff --git a/imagediff/ImageDiffwithPython/templates/index.html b/imagediff/templates/index.html
similarity index 100%
rename from imagediff/ImageDiffwithPython/templates/index.html
rename to imagediff/templates/index.html
diff --git a/imagediff/ImageDiffwithPython/templates/movie.html b/imagediff/templates/movie.html
similarity index 100%
rename from imagediff/ImageDiffwithPython/templates/movie.html
rename to imagediff/templates/movie.html
diff --git a/imagediff/ImageDiffwithPython/templates/target.html b/imagediff/templates/target.html
similarity index 100%
rename from imagediff/ImageDiffwithPython/templates/target.html
rename to imagediff/templates/target.html
diff --git a/imagediff/ImageDiffwithPython/templates/view.html b/imagediff/templates/view.html
similarity index 100%
rename from imagediff/ImageDiffwithPython/templates/view.html
rename to imagediff/templates/view.html


Commit: 0b30cfb29d24c3c60d83e2311a1ccc183ec34b60
    https://github.com/scummvm/scummvm-sites/commit/0b30cfb29d24c3c60d83e2311a1ccc183ec34b60
Author: Garvit Singla (garvits093 at gmail.com)
Date: 2026-06-11T19:13:22+02:00

Commit Message:
IMAGEDIFF: Improved README setup guide

Changed paths:
    imagediff/README.md


diff --git a/imagediff/README.md b/imagediff/README.md
index f89a5d3..0965983 100644
--- a/imagediff/README.md
+++ b/imagediff/README.md
@@ -30,6 +30,28 @@ Web tool to compare and highlight differences between two images (built with Fla
    pip install -r requirements.txt
    ```
 
+## Setting things up
+### SCREENSHOT_DIR
+
+ > The directory which stores the screenshots of the scenes
+
+```tree
+SCREENSHOTS_DIR/
+  <target>/
+    <build_number>/
+      <movie_name>/
+        998.png
+        1000.png
+        1100.png
+```
+
+### CACHE_DIR(Must be writable)
+
+> The application builds comparison timelines and stores computed results in a cache.
+
+
+Paths can be configured in config.py 
+
 ## Running the server
 
 Start the application:



From noreply at scummvm.org  Thu Jun 11 17:17:43 2026
From: noreply at scummvm.org (Helco)
Date: Thu, 11 Jun 2026 17:17:43 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 b33d0068ddf98b83267751076b289e9eb0f6d1c3
Message-ID: <20260611171743.31C757005BC@ron.scummvm.net>

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:
856fa1436e ALCACHOFA: V2: Add initial game variants for corvino
301d7f1dec ALCACHOFA: Fix saving through ScummVM menu
b33d0068dd ALCACHOFA: CORVINO: Adapt initial script, sound and text differences


Commit: 856fa1436ebeab69d7fecc05ca7a567b62a4cf29
    https://github.com/scummvm/scummvm/commit/856fa1436ebeab69d7fecc05ca7a567b62a4cf29
Author: Helco (hermann.noll at hotmail.com)
Date: 2026-06-11T19:17:13+02:00

Commit Message:
ALCACHOFA: V2: Add initial game variants for corvino

Changed paths:
    engines/alcachofa/game-v2.cpp
    engines/alcachofa/game.cpp
    engines/alcachofa/game.h


diff --git a/engines/alcachofa/game-v2.cpp b/engines/alcachofa/game-v2.cpp
index 0ad459dd50c..cd6beccc576 100644
--- a/engines/alcachofa/game-v2.cpp
+++ b/engines/alcachofa/game-v2.cpp
@@ -210,6 +210,27 @@ public:
 	bool doesRoomHaveBackground(const Room *room) override {
 		return !room->name().equalsIgnoreCase("Global");
 	}
+
+	String reencodePath(const String &path) override {
+		if (!_hasMessedUpEncoding)
+			return Game::reencodePath(path);
+
+		// Some of the Steam releases have wrong characters due to a messed up UTF8 conversion
+		U32String u32String = path.decode(Common::CodePage::kISO8859_1);
+		for (uint i = 0; i < u32String.size(); i++) {
+			const auto ch = u32String[i];
+			if (ch == 0xC1) // ?? -> ???
+				u32String[i] = 0x2561;
+			else if (ch == 0xD3) // ?? -> ??
+				u32String[i] = 0x03B1;
+			else if (ch == 0xCD) // ?? -> ???
+				u32String[i] = 0x2553;
+		}
+		return u32String.encode();
+	}
+
+protected:
+	bool _hasMessedUpEncoding = false;
 };
 
 class GameWithVersion2_0 : public GameWithVersion2 {
@@ -246,18 +267,21 @@ static constexpr const char *kMapFilesSecta[] = {
 	"Mapas/global.emc",
 	nullptr
 };
+static const char *const *const kMapFilesCorvino = kMapFilesSecta;
 
 static constexpr const char *kMapFilesMoscu[] = {
 	"Mapas/mapa1.emc",
 	"Mapas/global.emc",
 	nullptr
 };
+static const char *const *const kMapFilesBalones = kMapFilesMoscu;
 
 static constexpr const char *kMapFilesEscarabajo[] = {
 	"Mapas/mapa2.emc",
 	"Mapas/global.emc",
 	nullptr
 };
+static const char *const *const kMapFilesMamelucos = kMapFilesEscarabajo;
 
 class GameSecta : public GameWithVersion2_0 {
 public:
@@ -333,27 +357,58 @@ public:
 	bool isKnownBadVideo(int32 videoId) override {
 		return videoId == 0; // MPEG-4 codec is unsupported
 	}
+};
 
-	String reencodePath(const String &path) override {
-		if (!_hasMessedUpEncoding)
-			return Game::reencodePath(path);
+class GameWithVersion2_1 : public GameWithVersion2 {
+public:
+	char getTextFileKey() override {
+		return static_cast<char>(0x60);
+	}
 
-		// The Steam release has wrong characters due to some messed up UTF8 conversion
-		U32String u32String = path.decode(Common::CodePage::kISO8859_1);
-		for (uint i = 0; i < u32String.size(); i++) {
-			const auto ch = u32String[i];
-			if (ch == 0xC1) // ?? -> ???
-				u32String[i] = 0x2561;
-			else if (ch == 0xD3) // ?? -> ??
-				u32String[i] = 0x03B1;
-			else if (ch == 0xCD) // ?? -> ???
-				u32String[i] = 0x2553;
-		}
-		return u32String.encode();
+	String getMusicPath(int32 trackId) override {
+		return String::format("Sonidos/T%02d", trackId);
 	}
+};
 
-private:
-	bool _hasMessedUpEncoding = false;
+class GameCorvino : public GameWithVersion2_1 {
+public:
+	void onLoadedGameFiles() override {
+		g_engine->script().variable("EsJuegoCompleto") = 0;
+	}
+
+	const char *const *getMapFiles() override {
+		return kMapFilesCorvino;
+	}
+};
+
+class GameBalones : public GameWithVersion2_1 {
+public:
+	GameBalones() {
+		_hasMessedUpEncoding = !SearchMan.hasFile(Path(reencode("Animaciones/aut\xD3grafo.ani")));
+	}
+
+	void onLoadedGameFiles() override {
+		g_engine->script().variable("EsJuegoCompleto") = 1;
+	}
+
+	const char *const *getMapFiles() override {
+		return kMapFilesBalones;
+	}
+};
+
+class GameMamelucos : public GameWithVersion2_1 {
+public:
+	GameMamelucos() {
+		_hasMessedUpEncoding = !SearchMan.hasFile(Path(reencode("Animaciones/EST\xC1TICOS FILEM\xD3N.ANI")));
+	}
+
+	void onLoadedGameFiles() override {
+		g_engine->script().variable("EsJuegoCompleto") = 2;
+	}
+
+	const char *const *getMapFiles() override {
+		return kMapFilesMamelucos;
+	}
 };
 
 Game *Game::createForSecta() {
@@ -368,4 +423,16 @@ Game *Game::createForEscarabajo() {
 	return new GameEscarabajo();
 }
 
+Game *Game::createForCorvino() {
+	return new GameCorvino();
+}
+
+Game *Game::createForBalones() {
+	return new GameBalones();
+}
+
+Game *Game::createForMamelucos() {
+	return new GameMamelucos();
+}
+
 }
diff --git a/engines/alcachofa/game.cpp b/engines/alcachofa/game.cpp
index 042b8b868b2..3e5b1562caf 100644
--- a/engines/alcachofa/game.cpp
+++ b/engines/alcachofa/game.cpp
@@ -242,6 +242,16 @@ Game *Game::create() {
 			return createForEscarabajo();
 		}
 		break;
+	case EngineVersion::V2_1:
+		switch (*desc.desc.gameId) {
+		case 'c':
+			return createForCorvino();
+		case 'b':
+			return createForBalones();
+		case 'm':
+			return createForMamelucos();
+		}
+		break;
 	case EngineVersion::V3_0:
 	case EngineVersion::V3_1:
 		return createForMovieAdventureSpecial();
diff --git a/engines/alcachofa/game.h b/engines/alcachofa/game.h
index fee703beb25..1fa369e3dac 100644
--- a/engines/alcachofa/game.h
+++ b/engines/alcachofa/game.h
@@ -133,6 +133,9 @@ public:
 	static Game *createForSecta(); // V2
 	static Game *createForMoscu(); // V2
 	static Game *createForEscarabajo(); // V2
+	static Game *createForCorvino(); // V2
+	static Game *createForBalones(); // V2
+	static Game *createForMamelucos(); // V2
 
 	const Message _message;
 };


Commit: 301d7f1decd8c2456a30d5dd3cdb9b4ca036160c
    https://github.com/scummvm/scummvm/commit/301d7f1decd8c2456a30d5dd3cdb9b4ca036160c
Author: Helco (hermann.noll at hotmail.com)
Date: 2026-06-11T19:17:13+02:00

Commit Message:
ALCACHOFA: Fix saving through ScummVM menu

Changed paths:
    engines/alcachofa/alcachofa.cpp


diff --git a/engines/alcachofa/alcachofa.cpp b/engines/alcachofa/alcachofa.cpp
index 081435ab463..8aa2ec0a859 100644
--- a/engines/alcachofa/alcachofa.cpp
+++ b/engines/alcachofa/alcachofa.cpp
@@ -320,7 +320,7 @@ void AlcachofaEngine::pauseEngineIntern(bool pause) {
 bool AlcachofaEngine::canLoadGameStateCurrently(U32String *msg) {
 	if (_menu == nullptr)
 		return false; // the autosave wants to trigger even during error() while starting the game
-	if (!isInSpecialGameLoop())
+	if (isInSpecialGameLoop())
 		return false;
 	return
 		(menu().isOpen() && menu().interactionSemaphore().isReleased()) ||


Commit: b33d0068ddf98b83267751076b289e9eb0f6d1c3
    https://github.com/scummvm/scummvm/commit/b33d0068ddf98b83267751076b289e9eb0f6d1c3
Author: Helco (hermann.noll at hotmail.com)
Date: 2026-06-11T19:17:13+02:00

Commit Message:
ALCACHOFA: CORVINO: Adapt initial script, sound and text differences

Changed paths:
    engines/alcachofa/alcachofa.h
    engines/alcachofa/game-v2.cpp
    engines/alcachofa/rooms.cpp
    engines/alcachofa/script.h
    engines/alcachofa/sounds.cpp


diff --git a/engines/alcachofa/alcachofa.h b/engines/alcachofa/alcachofa.h
index 53d5c6f723b..4dc6ca7ee34 100644
--- a/engines/alcachofa/alcachofa.h
+++ b/engines/alcachofa/alcachofa.h
@@ -118,6 +118,7 @@ public:
 	inline EngineVersion version() const { return gameDescription().engineVersion; }
 	inline bool isV1() const { return gameDescription().isVersionBetween(10, 19); }
 	inline bool isV2() const { return gameDescription().isVersionBetween(20, 29); }
+	inline bool isV20() const { return gameDescription().isVersionBetween(20, 20); }
 	inline bool isV3() const { return gameDescription().isVersionBetween(30, 39); }
 
 	inline const AlcachofaGameDescription &gameDescription() const { return *_gameDescription; }
diff --git a/engines/alcachofa/game-v2.cpp b/engines/alcachofa/game-v2.cpp
index cd6beccc576..76c7b92227f 100644
--- a/engines/alcachofa/game-v2.cpp
+++ b/engines/alcachofa/game-v2.cpp
@@ -102,7 +102,12 @@ static constexpr const ScriptKernelTask kScriptKernelTaskMap[] = {
 	ScriptKernelTask::PlayMusic,
 	ScriptKernelTask::StopMusic,
 	ScriptKernelTask::WaitForMusicToEnd,
-	ScriptKernelTask::SayTextV2
+	ScriptKernelTask::SayTextV2, // *might* be used in secta, unused in corvino
+	ScriptKernelTask::AnimateCharacter, // from here on only corvino
+	ScriptKernelTask::AnimateTalking,
+	ScriptKernelTask::ClearInventory,
+	ScriptKernelTask::WaitForMouseClick,
+	ScriptKernelTask::ResetCharacter
 };
 
 class GameWithVersion2 : public Game {
@@ -157,10 +162,6 @@ public:
 		script.variable("textoson") = g_engine->config().subtitles() ? 1 : 0;
 	}
 
-	Path getVideoPath(int32 videoId) override {
-		return Path(String::format("Bin/DATA%02d.BIN", videoId));
-	}
-
 	String getSoundPath(const char *filename) override {
 		return String("Sonidos/") + filename;
 	}
@@ -243,6 +244,10 @@ public:
 		script.fixNestedMenuPop(20898); // Filemon talking to MANOLO in FILE_PIRAMIDE
 	}
 
+	Path getVideoPath(int32 videoId) override {
+		return Path(String::format("Bin/DATA%02d.BIN", videoId));
+	}
+
 	char getTextFileKey() override {
 		return static_cast<char>(0xA3);
 	}
@@ -361,12 +366,22 @@ public:
 
 class GameWithVersion2_1 : public GameWithVersion2 {
 public:
+	Path getVideoPath(int32 videoId) override {
+		return Path(String::format("Data/DATA%02d.BIN", videoId));
+	}
+
 	char getTextFileKey() override {
 		return static_cast<char>(0x60);
 	}
 
 	String getMusicPath(int32 trackId) override {
-		return String::format("Sonidos/T%02d", trackId);
+		return String::format("Sonidos/T%d", trackId);
+	}
+
+	void missingAnimation(const Common::String &fileName) override {
+		if (fileName == "VARITA.ANI") // this one seems bad, it is the inventory icon for I_RAMAS
+			return;
+		return GameWithVersion2::missingAnimation(fileName);
 	}
 };
 
@@ -379,6 +394,11 @@ public:
 	const char *const *getMapFiles() override {
 		return kMapFilesCorvino;
 	}
+
+	bool isKnownBadVideo(int32 videoId) override {
+		// These use DV codec in the steam release
+		return videoId < 2;
+	}
 };
 
 class GameBalones : public GameWithVersion2_1 {
diff --git a/engines/alcachofa/rooms.cpp b/engines/alcachofa/rooms.cpp
index f7ee4356e46..2c85ccee336 100644
--- a/engines/alcachofa/rooms.cpp
+++ b/engines/alcachofa/rooms.cpp
@@ -911,6 +911,12 @@ static char *trimTrailing(char *start, char *end) {
 	return end;
 }
 
+static bool isEmptyLine(const char *start, const char *end) {
+	while (start < end && isSpace(*start))
+		start++;
+	return start >= end;
+}
+
 void World::loadLocalizedNames() {
 	const char *filename = g_engine->game().getObjectFileName();
 	char textFileKey = g_engine->game().getTextFileKey();
@@ -946,6 +952,11 @@ void World::loadLocalizedNames() {
 		// key#value
 		while (lineStart < fileEnd) {
 			char *lineEnd = find(lineStart, fileEnd, '\n');
+			if (isEmptyLine(lineStart, lineEnd)) {
+				// empty lines are only in Corvino/Balones/Mamelucos
+				lineStart = lineEnd + 1;
+				continue;
+			}
 			char *keyEnd = find(lineStart, lineEnd, '#');
 			if (keyEnd == lineStart || keyEnd == lineEnd || keyEnd + 1 == lineEnd)
 				error("Invalid localized name line separator");
diff --git a/engines/alcachofa/script.h b/engines/alcachofa/script.h
index 378a3391a36..c324825b21b 100644
--- a/engines/alcachofa/script.h
+++ b/engines/alcachofa/script.h
@@ -128,7 +128,9 @@ enum class ScriptKernelTask {
 	FadeOut2,
 	LerpCamXYZ,
 	LerpCamToObjectKeepingZ,
-	Disguise
+	Disguise,
+	WaitForMouseClick, // only V2.1
+	ResetCharacter     // only V2.1
 };
 
 enum class ScriptFlags {
diff --git a/engines/alcachofa/sounds.cpp b/engines/alcachofa/sounds.cpp
index 7275759bc3d..15e416c2345 100644
--- a/engines/alcachofa/sounds.cpp
+++ b/engines/alcachofa/sounds.cpp
@@ -118,11 +118,11 @@ private:
 };
 
 static AudioStream *loadSND(File *file) {
-	// in V2 SND files are raw U8 PCM in mono 22100 encrypted with XOR
-	if (g_engine->isV2())
+	// only in V2.0 SND files are raw U8 PCM in mono 22100 encrypted with XOR
+	if (g_engine->isV20())
 		return makeRawStream(new XORReadStream(file, 0x55, DisposeAfterUse::YES), 22100, FLAG_UNSIGNED);
 
-	// in V1/V3 SND files are just WAV files with removed headers
+	// everywhere else SND files are just WAV files with removed headers
 	const uint32 endOfFormat = file->readUint32LE() + 2 * sizeof(uint32);
 	if (endOfFormat < 24)
 		error("Invalid SND format size");



From noreply at scummvm.org  Thu Jun 11 19:06:11 2026
From: noreply at scummvm.org (neuromancer)
Date: Thu, 11 Jun 2026 19:06:11 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 ef60248022c5f94d6309bbadcb3e3cddb2b2b012
Message-ID: <20260611190611.4E12A7005BC@ron.scummvm.net>

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:
808f0526f2 SCUMM RA2: correct shoot rendering for L10
959c1517c8 SCUMM RA2: improve responsiveness of the gamepad in some levels
ef60248022 SCUMM RA2: improved perspective in some levels


Commit: 808f0526f216e5c285a8a45ed651c20f064c6592
    https://github.com/scummvm/scummvm/commit/808f0526f216e5c285a8a45ed651c20f064c6592
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-11T21:05:59+02:00

Commit Message:
SCUMM RA2: correct shoot rendering for L10

Changed paths:
    engines/scumm/insane/rebel2/render.cpp


diff --git a/engines/scumm/insane/rebel2/render.cpp b/engines/scumm/insane/rebel2/render.cpp
index 9d5c7dccb65..d1219967e57 100644
--- a/engines/scumm/insane/rebel2/render.cpp
+++ b/engines/scumm/insane/rebel2/render.cpp
@@ -620,15 +620,14 @@ void InsaneRebel2::spawnHandler25Shot(int x, int y) {
 }
 
 Common::Point InsaneRebel2::getHandler7ShipDrawPoint() {
-	int shipCenterX = (_flyShipScreenX - 0xd4) + _perspectiveX + 160 + _viewX;
-	int shipCenterY = (_flyShipScreenY - 0x82) + _perspectiveY + 100 + _viewY;
+	Common::Point projected = getHandler7ProjectedPoint();
 
 	if (!_flyShipSprite || _flyShipSprite->getNumChars() <= 0)
-		return Common::Point(shipCenterX - 0xd4, shipCenterY - 0x82);
+		return Common::Point(projected.x - 0xd4, projected.y - 0x82);
 
 	int spriteIndex = CLIP<int>(_shipDirectionIndex, 0, _flyShipSprite->getNumChars() - 1);
-	return Common::Point(shipCenterX - _flyShipSprite->getCharWidth(spriteIndex) / 2,
-	                     shipCenterY - _flyShipSprite->getCharHeight(spriteIndex) / 2);
+	return Common::Point(projected.x - 0xd4 + _flyShipSprite->getCharXOffset(spriteIndex),
+	                     projected.y - 0x82 + _flyShipSprite->getCharYOffset(spriteIndex));
 }
 
 Common::Point InsaneRebel2::getHandler7ProjectedPoint() {
@@ -2693,13 +2692,13 @@ void InsaneRebel2::renderGameplayPostFrame(byte *renderBitmap, int pitch, int wi
 	// STEP 0: Fill status bar background (FUN_004288c0).
 	renderStatusBarBackground(renderBitmap, pitch, width, height, videoWidth, videoHeight, statusBarY);
 
-	// Ship rendering (FUN_00401ccf for Handler 8, FUN_0040d836 for Handler 7).
+	// Ship rendering. Handler 7 is drawn later, after its lasers, matching
+	// FUN_0040d836's order so the ship covers the muzzle end of the beams.
 	debugC(DEBUG_INSANE, "Ship Check: handler=%d shipSprite=%p flyShipSprite=%p shipLevelMode=%d numSprites=%d/%d",
 		_rebelHandler, (void*)_shipSprite, (void*)_flyShipSprite, _shipLevelMode,
 		_shipSprite ? _shipSprite->getNumChars() : 0,
 		_flyShipSprite ? _flyShipSprite->getNumChars() : 0);
 
-	renderHandler7Ship(renderBitmap, pitch, width, height);
 	renderVehicleShotImpacts(renderBitmap, pitch, width, height);
 	renderHandler8Ship(renderBitmap, pitch, width, height);
 	renderFallbackShip(renderBitmap, pitch, width, height);
@@ -2713,6 +2712,8 @@ void InsaneRebel2::renderGameplayPostFrame(byte *renderBitmap, int pitch, int wi
 	// Laser shot beams - drawn BEFORE cockpit/HUD overlays so cockpit covers beam edges.
 	renderLaserShots(renderBitmap, pitch, width, height);
 
+	renderHandler7Ship(renderBitmap, pitch, width, height);
+
 	// Handler 25 GRD sprites drawn AFTER enemies/explosions/lasers per original FUN_0041DB5E:
 	//   Line 193: FUN_0041f29a (enemies)
 	//   Line 194: FUN_0041e7c2 (explosions)
@@ -3463,17 +3464,20 @@ void InsaneRebel2::renderHandler7Ship(byte *renderBitmap, int pitch, int width,
 	const int nativeViewX = renderHiRes ? _hiResPresentationViewX : 0;
 	const int nativeViewY = renderHiRes ? _hiResPresentationViewY : 0;
 
+	Common::Point projected = getHandler7ProjectedPoint();
 	Common::Point shipDraw = getHandler7ShipDrawPoint();
 	if (renderHiRes) {
 		// Low-res draws into the native source buffer with _viewX/_viewY baked in,
 		// then SmushPlayer copies the scrolled viewport. High-res promotion has
 		// already consumed those offsets, so reconstruct the same native source
 		// position before applying the 2x presentation transform.
+		projected.x += nativeViewX;
+		projected.y += nativeViewY;
 		shipDraw.x += nativeViewX;
 		shipDraw.y += nativeViewY;
 	}
-	int shipCenterX = shipDraw.x + 0xd4;
-	int shipCenterY = shipDraw.y + 0x82;
+	int shipCenterX = projected.x;
+	int shipCenterY = projected.y;
 
 	// FUN_40D836 lines 108-136: FLY002 proximity cues near corridor danger.
 	if (_flyLaserSprite && _flyLaserSprite->getNumChars() > 0) {


Commit: 959c1517c892796fc5fd600b8c9f76a97ca8cbd2
    https://github.com/scummvm/scummvm/commit/959c1517c892796fc5fd600b8c9f76a97ca8cbd2
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-11T21:05:59+02:00

Commit Message:
SCUMM RA2: improve responsiveness of the gamepad in some levels

Changed paths:
    engines/scumm/insane/rebel2/iact.cpp
    engines/scumm/insane/rebel2/rebel.cpp


diff --git a/engines/scumm/insane/rebel2/iact.cpp b/engines/scumm/insane/rebel2/iact.cpp
index a322e039bd1..63178723a3a 100644
--- a/engines/scumm/insane/rebel2/iact.cpp
+++ b/engines/scumm/insane/rebel2/iact.cpp
@@ -853,14 +853,20 @@ void InsaneRebel2::handleOpcode6Handler7(Common::SeekableReadStream &b, int16 pa
 
 	// Direct mouse/touch/gamepad aiming can hold the cursor at an edge
 	// indefinitely. Keep this sensitivity concession local to Handler 7
-	// third-person ship steering.
-	scaledInputX = (int16)((scaledInputX * kRA2Handler7DirectInputNumerator) /
-		kRA2Handler7DirectInputDenominator);
-	scaledInputY = (int16)((scaledInputY * kRA2Handler7DirectInputNumerator) /
-		kRA2Handler7DirectInputDenominator);
-	const bool useMouseFlightTarget = !_gamepadAimActive;
+	// third-person ship steering. A held analog stick is already bounded by its
+	// physical range, so keep its full logical range for more responsive L10 flight.
+	if (!_gamepadAimActive) {
+		scaledInputX = (int16)((scaledInputX * kRA2Handler7DirectInputNumerator) /
+			kRA2Handler7DirectInputDenominator);
+		scaledInputY = (int16)((scaledInputY * kRA2Handler7DirectInputNumerator) /
+			kRA2Handler7DirectInputDenominator);
+	}
+	// ScummVM's controller path drives a virtual absolute aim point, like mouse/touch.
+	// Use the same bounded target steering for it; the original relative-axis
+	// integration is too sluggish for L10's low slide/lift parameters.
+	const bool useTargetSteering = true;
 	int16 mouseFlightTargetX = _flyShipScreenX;
-	if (useMouseFlightTarget) {
+	if (useTargetSteering) {
 		mouseFlightTargetX = (int16)(0xd4 + (scaledInputX * kRA2Handler7MouseTargetRangeX) / 127);
 		mouseFlightTargetX = CLIP<int16>(mouseFlightTargetX, 0x14, 0x194);
 	}
@@ -927,9 +933,10 @@ void InsaneRebel2::handleOpcode6Handler7(Common::SeekableReadStream &b, int16 pa
 	// FUN_0040C3CC integrates relative flight axes. The real mouse is an
 	// absolute position, so steer toward a bounded position target instead of
 	// letting a held off-center cursor keep pushing the ship until it bounces.
-	if (useMouseFlightTarget) {
+	if (useTargetSteering) {
 		int targetDeltaX = mouseFlightTargetX - _flyShipScreenX;
-		positionDeltaX = (int16)CLIP<int>(targetDeltaX / 4, -12, 12);
+		const int targetSteeringDivisor = _gamepadAimActive ? 2 : 4;
+		positionDeltaX = (int16)CLIP<int>(targetDeltaX / targetSteeringDivisor, -12, 12);
 		if (positionDeltaX == 0 && targetDeltaX != 0)
 			positionDeltaX = (targetDeltaX < 0) ? -1 : 1;
 	}
@@ -1085,11 +1092,11 @@ void InsaneRebel2::handleOpcode6Handler7(Common::SeekableReadStream &b, int16 pa
 		 _vm->getActionState(kScummActionInsaneAttack));
 
 	debugC(DEBUG_INSANE, "H7: mouse=(%d,%d) raw=(%d,%d) scaled=(%d,%d) targetX=%d pos=(%d,%d) "
-		"vel=%d vIn=%d dx=%d dir=%d mode=%d mouseTarget=%d",
+		"vel=%d vIn=%d dx=%d dir=%d mode=%d targetSteer=%d padAim=%d",
 		mouseX, mouseY, inputX, inputY, scaledInputX, scaledInputY,
 		mouseFlightTargetX, _flyShipScreenX, _flyShipScreenY, _smoothedVelocity,
 		_verticalInput, positionDeltaX, _shipDirectionIndex, _flyControlMode,
-		useMouseFlightTarget ? 1 : 0);
+		useTargetSteering ? 1 : 0, _gamepadAimActive ? 1 : 0);
 }
 
 // Helper split out of FUN_0041CADB case 4; not a separate original function.
diff --git a/engines/scumm/insane/rebel2/rebel.cpp b/engines/scumm/insane/rebel2/rebel.cpp
index babc253b027..910ee97a597 100644
--- a/engines/scumm/insane/rebel2/rebel.cpp
+++ b/engines/scumm/insane/rebel2/rebel.cpp
@@ -1953,6 +1953,40 @@ void InsaneRebel2::updateGameplayAimFromGamepad() {
 				return;
 			}
 		}
+	} else if (_rebelHandler == 7) {
+		int axisX = 0;
+		int axisY = 0;
+		if (dpadX || dpadY) {
+			axisX = dpadX * 127;
+			axisY = dpadY * 127;
+		} else {
+			axisX = velX;
+			axisY = velY;
+		}
+
+		if (axisX || axisY) {
+			const Common::Point aimPos = getGameplayAimPoint();
+			const int centerX = 160;
+			const int centerY = 100;
+			const int kHandler7HorizontalRange = 120;
+			const int targetX = CLIP<int>(centerX + axisX * kHandler7HorizontalRange / 127, 0, 319);
+			const int targetY = (axisY < 0) ?
+				centerY + axisY * centerY / 127 :
+				centerY + axisY * (199 - centerY) / 127;
+			const int kHandler7HorizontalMaxStep = 24;
+			const int kHandler7VerticalMaxStep = 32;
+			const int distX = targetX - aimPos.x;
+			const int distY = targetY - aimPos.y;
+
+			if (distX || distY) {
+				deltaX = CLIP<int>(distX, -kHandler7HorizontalMaxStep, kHandler7HorizontalMaxStep);
+				deltaY = CLIP<int>(distY, -kHandler7VerticalMaxStep, kHandler7VerticalMaxStep);
+				activeGamepadAim = true;
+			} else {
+				_gamepadAimActive = true;
+				return;
+			}
+		}
 	} else if (dpadX || dpadY) {
 		const int kOriginalDigitalStep = 3;
 		deltaX = dpadX * kOriginalDigitalStep;


Commit: ef60248022c5f94d6309bbadcb3e3cddb2b2b012
    https://github.com/scummvm/scummvm/commit/ef60248022c5f94d6309bbadcb3e3cddb2b2b012
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-11T21:05:59+02:00

Commit Message:
SCUMM RA2: improved perspective in some levels

Changed paths:
    engines/scumm/insane/rebel2/iact.cpp
    engines/scumm/insane/rebel2/rebel.cpp


diff --git a/engines/scumm/insane/rebel2/iact.cpp b/engines/scumm/insane/rebel2/iact.cpp
index 63178723a3a..eb59a01ad7e 100644
--- a/engines/scumm/insane/rebel2/iact.cpp
+++ b/engines/scumm/insane/rebel2/iact.cpp
@@ -861,10 +861,11 @@ void InsaneRebel2::handleOpcode6Handler7(Common::SeekableReadStream &b, int16 pa
 		scaledInputY = (int16)((scaledInputY * kRA2Handler7DirectInputNumerator) /
 			kRA2Handler7DirectInputDenominator);
 	}
-	// ScummVM's controller path drives a virtual absolute aim point, like mouse/touch.
-	// Use the same bounded target steering for it; the original relative-axis
-	// integration is too sluggish for L10's low slide/lift parameters.
-	const bool useTargetSteering = true;
+	// Mouse/touch can hold an absolute cursor at an edge indefinitely, so they use
+	// bounded target steering. Gamepad input for Handler 7 now feeds the original
+	// center-relative flight axes and lets the assembly-derived velocity history,
+	// lift/slide tables, wind, and clamps produce the ship movement.
+	const bool useTargetSteering = !_gamepadAimActive;
 	int16 mouseFlightTargetX = _flyShipScreenX;
 	if (useTargetSteering) {
 		mouseFlightTargetX = (int16)(0xd4 + (scaledInputX * kRA2Handler7MouseTargetRangeX) / 127);
diff --git a/engines/scumm/insane/rebel2/rebel.cpp b/engines/scumm/insane/rebel2/rebel.cpp
index 910ee97a597..35c0dd5186b 100644
--- a/engines/scumm/insane/rebel2/rebel.cpp
+++ b/engines/scumm/insane/rebel2/rebel.cpp
@@ -1964,23 +1964,22 @@ void InsaneRebel2::updateGameplayAimFromGamepad() {
 			axisY = velY;
 		}
 
-		if (axisX || axisY) {
+		if (axisX || axisY || _gamepadAimActive) {
 			const Common::Point aimPos = getGameplayAimPoint();
 			const int centerX = 160;
 			const int centerY = 100;
-			const int kHandler7HorizontalRange = 120;
-			const int targetX = CLIP<int>(centerX + axisX * kHandler7HorizontalRange / 127, 0, 319);
+			const int targetX = (axisX < 0) ?
+				centerX + axisX * centerX / 127 :
+				centerX + axisX * (319 - centerX) / 127;
 			const int targetY = (axisY < 0) ?
 				centerY + axisY * centerY / 127 :
 				centerY + axisY * (199 - centerY) / 127;
-			const int kHandler7HorizontalMaxStep = 24;
-			const int kHandler7VerticalMaxStep = 32;
 			const int distX = targetX - aimPos.x;
 			const int distY = targetY - aimPos.y;
 
 			if (distX || distY) {
-				deltaX = CLIP<int>(distX, -kHandler7HorizontalMaxStep, kHandler7HorizontalMaxStep);
-				deltaY = CLIP<int>(distY, -kHandler7VerticalMaxStep, kHandler7VerticalMaxStep);
+				deltaX = distX;
+				deltaY = distY;
 				activeGamepadAim = true;
 			} else {
 				_gamepadAimActive = true;



From noreply at scummvm.org  Thu Jun 11 20:39:05 2026
From: noreply at scummvm.org (whoozle)
Date: Thu, 11 Jun 2026 20:39:05 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 357565a16808393f5dba9eba27042c430ba030e2
Message-ID: <20260611203905.679197005BC@ron.scummvm.net>

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

Summary:
357565a168 PHOENIXVR: The Cameron Files: Secret at Loch Ness USA CD detection entry


Commit: 357565a16808393f5dba9eba27042c430ba030e2
    https://github.com/scummvm/scummvm/commit/357565a16808393f5dba9eba27042c430ba030e2
Author: Vladimir Menshakov (vladimir.menshakov at gmail.com)
Date: 2026-06-11T21:37:13+01:00

Commit Message:
PHOENIXVR: The Cameron Files: Secret at Loch Ness USA CD detection entry

Changed paths:
    engines/phoenixvr/detection_tables.h


diff --git a/engines/phoenixvr/detection_tables.h b/engines/phoenixvr/detection_tables.h
index 760bd6090c9..75e68d363ef 100644
--- a/engines/phoenixvr/detection_tables.h
+++ b/engines/phoenixvr/detection_tables.h
@@ -115,6 +115,16 @@ const ADGameDescription gameDescriptions[] = {
 		GUIO1(GUIO_NONE)}
 	,
 
+	{"lochness",
+		nullptr,
+		AD_ENTRY2s("script.pak", "a7ee3aae653658f93bba7f237bcf06f3", 1904,
+				   "textes.txt", "3f2deea06efed98a355ea03d69fd15ce", 1608),
+		Common::EN_USA,
+		Common::kPlatformWindows,
+		ADGF_DROPPLATFORM | ADGF_CD,
+		GUIO1(GUIO_NONE)}
+	,
+
 	{"lochness",
 		nullptr,
 		AD_ENTRY2s("script.pak", "a7ee3aae653658f93bba7f237bcf06f3", 1904,



From noreply at scummvm.org  Thu Jun 11 22:06:49 2026
From: noreply at scummvm.org (neuromancer)
Date: Thu, 11 Jun 2026 22:06:49 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 747e839f2ebb7e4bdd0619b9a7c89fe693d1e3c6
Message-ID: <20260611220649.68F1B7005BC@ron.scummvm.net>

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:
7051bf01f8 SCUMM RA2: avoid invalid decoding in L2
747e839f2e SCUMM: RA: fix train corruption in L2


Commit: 7051bf01f893bf81051b84c4ae2c8d6e84b08291
    https://github.com/scummvm/scummvm/commit/7051bf01f893bf81051b84c4ae2c8d6e84b08291
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-12T00:06:32+02:00

Commit Message:
SCUMM RA2: avoid invalid decoding in L2

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


diff --git a/engines/scumm/insane/rebel2/rebel.h b/engines/scumm/insane/rebel2/rebel.h
index 4d1f76af1fd..24810696bf1 100644
--- a/engines/scumm/insane/rebel2/rebel.h
+++ b/engines/scumm/insane/rebel2/rebel.h
@@ -537,6 +537,7 @@ public:
 
 	// Get current handler ID (8, 25, 38 etc.) for SMUSH player to query
 	int getHandler() const { return _rebelHandler; }
+	int getHandler25GrdSpriteMode() const { return _grdSpriteMode; }
 	bool isHiRes() const;
 
 	void iactRebel2Scene1(byte *renderBitmap, int32 codecparam, int32 setupsan12,
diff --git a/engines/scumm/smush/rebel/smush_player_ra2.cpp b/engines/scumm/smush/rebel/smush_player_ra2.cpp
index 1dec1a12192..aa2af155c65 100644
--- a/engines/scumm/smush/rebel/smush_player_ra2.cpp
+++ b/engines/scumm/smush/rebel/smush_player_ra2.cpp
@@ -33,6 +33,8 @@
 
 #include "scumm/scumm.h"
 #include "scumm/scumm_v7.h"
+#include "scumm/smush/codec37.h"
+#include "scumm/smush/codec47.h"
 #include "scumm/smush/smush_font.h"
 #include "scumm/smush/rebel/smush_multi_font.h"
 #include "scumm/smush/rebel/codec_ra2.h"
@@ -60,6 +62,14 @@ bool isRebel2GameplayActive(Insane *insane) {
 	return static_cast<InsaneRebel2 *>(insane)->getHandler() != 0;
 }
 
+bool isRebel2Handler25CorridorMode(Insane *insane) {
+	if (insane == nullptr)
+		return false;
+
+	InsaneRebel2 *rebel2 = static_cast<InsaneRebel2 *>(insane);
+	return rebel2->getHandler() == 25 && rebel2->getHandler25GrdSpriteMode() == 3;
+}
+
 static void scaleNativeViewportToHiRes(byte *dst, int dstPitch, int dstWidth, int dstHeight,
 		const byte *src, int srcPitch, int srcWidth, int srcHeight, int scrollX, int scrollY) {
 	if (!dst || !src || dstPitch <= 0 || dstWidth < 640 || dstHeight < 400 ||
@@ -165,8 +175,13 @@ void SmushPlayerRebel2::initGamePlayerFields() {
 	_ra2FrameSourceSkipX = 0;
 	_ra2FrameSourceSkipY = 0;
 	_ra2FrameObjectOriginalWidth = 0;
+	_ra2FrameObjectOriginalHeight = 0;
 	_ra2FrameObjectSurfaceWidth = 0;
 	_ra2FrameObjectSurfaceHeight = 0;
+	_ra2DeltaBlocksWidth = 0;
+	_ra2DeltaBlocksHeight = 0;
+	_ra2DeltaGlyphsWidth = 0;
+	_ra2DeltaGlyphsHeight = 0;
 	_ra2LowResVideoBuffer = nullptr;
 	_ra2LowResVideoBufferSize = 0;
 	_ra2NativeFrameNeedsClear = false;
@@ -854,6 +869,7 @@ void SmushPlayerRebel2::ra2HandleTextResource(const char *str, int fontId, int c
  */
 void SmushPlayerRebel2::ra2PrepareFrameObjectSurface(int left, int top, int width, int height) {
 	_ra2FrameObjectOriginalWidth = width;
+	_ra2FrameObjectOriginalHeight = height;
 	_ra2FrameObjectSurfaceWidth = width;
 	_ra2FrameObjectSurfaceHeight = height;
 
@@ -1002,17 +1018,36 @@ bool SmushPlayerRebel2::ra2SelectFrameBuffer(int codec, int width, int height) {
 	if (needsSpecialBuffer) {
 		// Frame is larger than the native target - need special buffer.
 		if (_specialBuffer == nullptr || bufSize > _specialBufferSize) {
+			byte *oldDst = _dst;
+			const int oldWidth = _width;
+			const int oldHeight = _height;
+			int oldPitch = oldWidth;
+			if (oldDst != _specialBuffer && oldDst != _ra2LowResVideoBuffer)
+				oldPitch = _vm->_screenWidth;
+			const bool oldTargetIsNative = oldDst == _specialBuffer || oldDst == _ra2LowResVideoBuffer ||
+				(oldWidth <= 320 && oldHeight <= 200);
+
 			byte *newSpecialBuffer = (byte *)malloc(bufSize);
 			if (newSpecialBuffer == nullptr) {
 				warning("SmushPlayerRebel2::ra2SelectFrameBuffer: Failed to allocate %d bytes for FOBJ %dx%d",
 					bufSize, width, height);
 				return false;
 			}
+			memset(newSpecialBuffer, 0, bufSize);
+
+			if (oldTargetIsNative && oldDst != nullptr && oldWidth > 0 && oldHeight > 0 && oldPitch > 0) {
+				const int copyWidth = MIN<int>(surfaceWidth, MIN<int>(oldWidth, oldPitch));
+				const int copyHeight = MIN<int>(surfaceHeight, oldHeight);
+				for (int y = 0; y < copyHeight; y++) {
+					memcpy(newSpecialBuffer + y * surfaceWidth,
+						   oldDst + y * oldPitch,
+						   copyWidth);
+				}
+			}
+
 			free(_specialBuffer);
 			_specialBuffer = newSpecialBuffer;
 			_specialBufferSize = bufSize;
-			// Zero-fill the new buffer to avoid garbage in areas not written by FOBJ codec
-			memset(_specialBuffer, 0, bufSize);
 			debugC(DEBUG_SMUSH, "SmushPlayerRebel2::ra2SelectFrameBuffer: Allocated new _specialBuffer %dx%d for FOBJ %dx%d (%d bytes)",
 				surfaceWidth, surfaceHeight, width, height, bufSize);
 		}
@@ -1046,6 +1081,122 @@ bool SmushPlayerRebel2::ra2SelectFrameBuffer(int codec, int width, int height) {
 	return true;
 }
 
+/**
+ * Decode RA2 full-frame delta FOBJ streams into a tight/padded scratch frame,
+ * then place the visible rectangle at the FOBJ coordinates.
+ */
+bool SmushPlayerRebel2::ra2DecodePlacedDeltaCodec(int codec, const uint8 *src, int left, int top,
+									 int width, int height, int pitch, int dataSize) {
+	if (!src || !_dst || width <= 0 || height <= 0 || pitch <= 0 || dataSize <= 0)
+		return true;
+
+	const int visibleWidth = (_ra2FrameObjectOriginalWidth > 0) ? _ra2FrameObjectOriginalWidth : width;
+	const int visibleHeight = (_ra2FrameObjectOriginalHeight > 0) ? _ra2FrameObjectOriginalHeight : height;
+	if (visibleWidth <= 0 || visibleHeight <= 0)
+		return true;
+
+	const int blockSize = (codec == SMUSH_CODEC_DELTA_BLOCKS) ? 4 : 8;
+	const int decodeWidth = (visibleWidth + blockSize - 1) & ~(blockSize - 1);
+	const int decodeHeight = (visibleHeight + blockSize - 1) & ~(blockSize - 1);
+	const int64 decodeSize64 = (int64)decodeWidth * decodeHeight;
+	if (decodeSize64 <= 0 || decodeSize64 > INT_MAX) {
+		warning("SmushPlayerRebel2::ra2DecodePlacedDeltaCodec: invalid codec %d frame size %dx%d",
+			codec, decodeWidth, decodeHeight);
+		return true;
+	}
+
+	byte *deltaFrame = (byte *)malloc((size_t)decodeSize64);
+	if (deltaFrame == nullptr) {
+		warning("SmushPlayerRebel2::ra2DecodePlacedDeltaCodec: failed to allocate %d-byte scratch frame",
+			(int)decodeSize64);
+		return true;
+	}
+
+	if (codec == SMUSH_CODEC_DELTA_BLOCKS) {
+		if (_deltaBlocksCodec != nullptr &&
+				(_ra2DeltaBlocksWidth != decodeWidth || _ra2DeltaBlocksHeight != decodeHeight)) {
+			delete _deltaBlocksCodec;
+			_deltaBlocksCodec = nullptr;
+			_ra2DeltaBlocksWidth = 0;
+			_ra2DeltaBlocksHeight = 0;
+		}
+		if (_deltaBlocksCodec == nullptr) {
+			_deltaBlocksCodec = new SmushDeltaBlocksDecoder(decodeWidth, decodeHeight);
+			_ra2DeltaBlocksWidth = decodeWidth;
+			_ra2DeltaBlocksHeight = decodeHeight;
+		}
+		_deltaBlocksCodec->decode(deltaFrame, src);
+	} else {
+		if (_deltaGlyphsCodec != nullptr &&
+				(_ra2DeltaGlyphsWidth != decodeWidth || _ra2DeltaGlyphsHeight != decodeHeight)) {
+			delete _deltaGlyphsCodec;
+			_deltaGlyphsCodec = nullptr;
+			_ra2DeltaGlyphsWidth = 0;
+			_ra2DeltaGlyphsHeight = 0;
+		}
+		if (_deltaGlyphsCodec == nullptr) {
+			_deltaGlyphsCodec = new SmushDeltaGlyphsDecoder(decodeWidth, decodeHeight);
+			_ra2DeltaGlyphsWidth = decodeWidth;
+			_ra2DeltaGlyphsHeight = decodeHeight;
+		}
+		_deltaGlyphsCodec->decode(deltaFrame, src);
+	}
+
+	int srcX = CLIP<int>(_ra2FrameSourceSkipX, 0, visibleWidth);
+	int srcY = CLIP<int>(_ra2FrameSourceSkipY, 0, visibleHeight);
+	int copyWidth = MIN<int>(width, visibleWidth - srcX);
+	int copyHeight = MIN<int>(height, visibleHeight - srcY);
+	int dstX = left;
+	int dstY = top;
+
+	if (dstX < 0) {
+		const int skip = -dstX;
+		srcX += skip;
+		copyWidth -= skip;
+		dstX = 0;
+	}
+	if (dstY < 0) {
+		const int skip = -dstY;
+		srcY += skip;
+		copyHeight -= skip;
+		dstY = 0;
+	}
+
+	const int dstHeight = (_height > 0) ? _height : _vm->_screenHeight;
+	if (dstX + copyWidth > pitch)
+		copyWidth = pitch - dstX;
+	if (dstY + copyHeight > dstHeight)
+		copyHeight = dstHeight - dstY;
+
+	const bool transparentZero = codec == SMUSH_CODEC_DELTA_BLOCKS &&
+		visibleWidth == 350 && visibleHeight == 200 &&
+		isRebel2Handler25CorridorMode(_insane);
+
+	if (copyWidth > 0 && copyHeight > 0) {
+		for (int y = 0; y < copyHeight; y++) {
+			byte *dstLine = _dst + (dstY + y) * pitch + dstX;
+			const byte *srcLine = deltaFrame + (srcY + y) * decodeWidth + srcX;
+
+			if (transparentZero) {
+				for (int x = 0; x < copyWidth; x++) {
+					if (srcLine[x] != 0)
+						dstLine[x] = srcLine[x];
+				}
+			} else {
+				memcpy(dstLine, srcLine, copyWidth);
+			}
+		}
+	}
+
+	debugC(DEBUG_SMUSH, "SmushPlayerRebel2::ra2DecodePlacedDeltaCodec: codec=%d pos=(%d,%d) visible=%dx%d decode=%dx%d copy=%dx%d skip=(%d,%d) transparent=%d",
+		codec, left, top, visibleWidth, visibleHeight, decodeWidth, decodeHeight,
+		MAX<int>(0, copyWidth), MAX<int>(0, copyHeight), _ra2FrameSourceSkipX, _ra2FrameSourceSkipY,
+		transparentZero ? 1 : 0);
+
+	free(deltaFrame);
+	return true;
+}
+
 /**
  * Dispatch to RA2-specific codec functions.
  * Returns true if the codec was handled, false for standard codecs.
@@ -1255,6 +1406,7 @@ bool SmushPlayerRebel2::handleGameAdjustCoords(int codec, int &left, int &top, i
 		// RLE data. The original FOBJ dispatcher applies FUN_00424510 offsets
 		// to the destination coordinates passed to FUN_0042cba0; it does not
 		// advance the compressed source by clipped scanlines.
+		_ra2FrameSourceSkipY = sourceSkipY;
 		if (srcSkipY)
 			*srcSkipY = 0;
 	} else if (srcSkipY) {
@@ -1264,8 +1416,11 @@ bool SmushPlayerRebel2::handleGameAdjustCoords(int codec, int &left, int &top, i
 }
 
 bool SmushPlayerRebel2::handleGameCodecDecode(int codec, const uint8 *src, int left, int top, int width, int height, int pitch, int dataSize, uint8 param, uint16 parm2) {
+	if (isRebel2FullFrameDeltaCodec(codec))
+		return ra2DecodePlacedDeltaCodec(codec, src, left, top, width, height, pitch, dataSize);
+
 	// Handle RA2-specific codecs (21, 23, 44, 45); return false for standard
-	// codecs (RLE, uncompressed, codec 37/47) so the base class decodes them.
+	// codecs (RLE) so the base class decodes them.
 	return ra2DecodeCodec(codec, src, left, top, width, height, pitch, dataSize);
 }
 
diff --git a/engines/scumm/smush/rebel/smush_player_ra2.h b/engines/scumm/smush/rebel/smush_player_ra2.h
index cd5d2351776..c3c87e8ea75 100644
--- a/engines/scumm/smush/rebel/smush_player_ra2.h
+++ b/engines/scumm/smush/rebel/smush_player_ra2.h
@@ -74,6 +74,8 @@ private:
 	bool ra2EnsureLowResVideoBuffer();
 	void ra2ClearCurrentTarget();
 	bool ra2IsHighResMode() const;
+	bool ra2DecodePlacedDeltaCodec(int codec, const uint8 *src, int left, int top,
+								   int width, int height, int pitch, int dataSize);
 	bool ra2DecodeCodec(int codec, const uint8 *src, int left, int top,
 						int width, int height, int pitch, int dataSize);
 	void ra2HandleDeltaPalette(int32 subSize, Common::SeekableReadStream &b);
@@ -94,8 +96,13 @@ private:
 	int _ra2FrameSourceSkipX;
 	int _ra2FrameSourceSkipY;
 	int _ra2FrameObjectOriginalWidth;
+	int _ra2FrameObjectOriginalHeight;
 	int _ra2FrameObjectSurfaceWidth;
 	int _ra2FrameObjectSurfaceHeight;
+	int _ra2DeltaBlocksWidth;
+	int _ra2DeltaBlocksHeight;
+	int _ra2DeltaGlyphsWidth;
+	int _ra2DeltaGlyphsHeight;
 	byte *_ra2LowResVideoBuffer;
 	int _ra2LowResVideoBufferSize;
 	bool _ra2NativeFrameNeedsClear;


Commit: 747e839f2ebb7e4bdd0619b9a7c89fe693d1e3c6
    https://github.com/scummvm/scummvm/commit/747e839f2ebb7e4bdd0619b9a7c89fe693d1e3c6
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-12T00:06:32+02:00

Commit Message:
SCUMM: RA: fix train corruption in L2

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 aa2af155c65..e44fe21ba11 100644
--- a/engines/scumm/smush/rebel/smush_player_ra2.cpp
+++ b/engines/scumm/smush/rebel/smush_player_ra2.cpp
@@ -934,6 +934,7 @@ bool SmushPlayerRebel2::ra2SelectFrameBuffer(int codec, int width, int height) {
 	const bool highRes = ra2IsHighResMode();
 	const bool gameplayActive = isRebel2GameplayActive(_insane);
 	const bool fullFrameDelta = isRebel2FullFrameDeltaCodec(codec);
+	const int64 currentSurfaceSize64 = (int64)_width * _height;
 
 	if (!gameplayActive && fobjSize64 <= nativeScreenSize) {
 		if (highRes) {
@@ -958,6 +959,22 @@ bool SmushPlayerRebel2::ra2SelectFrameBuffer(int codec, int width, int height) {
 		return true;
 	}
 
+	if (gameplayActive && !fullFrameDelta &&
+			isRebel2Handler25CorridorMode(_insane) &&
+			_dst == _specialBuffer && _specialBuffer != nullptr &&
+			_width == 350 && _height == 200 &&
+			_ra2FrameObjectSurfaceWidth <= _width &&
+			_ra2FrameObjectSurfaceHeight <= _height &&
+			currentSurfaceSize64 > 0 && currentSurfaceSize64 <= _specialBufferSize) {
+		if (_ra2NativeFrameNeedsClear) {
+			ra2ClearCurrentTarget();
+			_ra2NativeFrameNeedsClear = false;
+		}
+		debugC(DEBUG_SMUSH, "SmushPlayerRebel2::ra2SelectFrameBuffer: Using current Handler25 corridor _specialBuffer %dx%d for FOBJ %dx%d",
+			_width, _height, width, height);
+		return true;
+	}
+
 	const bool oversizedNative = fobjSize64 > nativeScreenSize ||
 		width > 320 || height > 200 ||
 		_ra2FrameObjectSurfaceWidth > 320 || _ra2FrameObjectSurfaceHeight > 200;
@@ -973,7 +990,6 @@ bool SmushPlayerRebel2::ra2SelectFrameBuffer(int codec, int width, int height) {
 		surfaceHeight = MAX(surfaceHeight, _ra2FrameObjectSurfaceHeight);
 	}
 
-	const int64 currentSurfaceSize64 = (int64)_width * _height;
 	if (highRes && gameplayActive && !useGameplaySurface && !oversizedNative &&
 			width > 0 && height > 0 &&
 			_dst == _specialBuffer && _specialBuffer != nullptr &&



From noreply at scummvm.org  Thu Jun 11 22:10:35 2026
From: noreply at scummvm.org (whoozle)
Date: Thu, 11 Jun 2026 22:10:35 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 3d68656634218d213a8aa3912573c85cc2d55850
Message-ID: <20260611221035.D499A7005BC@ron.scummvm.net>

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

Summary:
c77e04e211 PHOENIXVR: Improve text font handling
b06f7828e1 VIDEO: Allow Phoenix 4XM v1 videos
855a48e3a6 PHOENIXVR: Add more game variants
a16464659b PHOENIXVR: Improve save room rendering
e07b3297ab PHOENIXVR: Fix Amerzone save room level restore
3d68656634 PHOENIXVR: Add more game variants


Commit: c77e04e2118a4b82ff732a9d8e1b2e560c82e49a
    https://github.com/scummvm/scummvm/commit/c77e04e2118a4b82ff732a9d8e1b2e560c82e49a
Author: Scorp (scorp at mrs.mn)
Date: 2026-06-11T23:10:30+01:00

Commit Message:
PHOENIXVR: Improve text font handling

Changed paths:
    engines/phoenixvr/phoenixvr.cpp
    engines/phoenixvr/phoenixvr.h


diff --git a/engines/phoenixvr/phoenixvr.cpp b/engines/phoenixvr/phoenixvr.cpp
index 265e9a580be..fe7163b293d 100644
--- a/engines/phoenixvr/phoenixvr.cpp
+++ b/engines/phoenixvr/phoenixvr.cpp
@@ -29,6 +29,7 @@
 #include "common/memstream.h"
 #include "common/savefile.h"
 #include "common/scummsys.h"
+#include "common/str-enc.h"
 #include "common/system.h"
 #include "engines/util.h"
 #include "graphics/font.h"
@@ -54,6 +55,15 @@ namespace PhoenixVR {
 
 PhoenixVREngine *g_engine;
 
+static Common::CodePage getTextCodePage(Common::Language language) {
+	switch (language) {
+	case Common::RU_RUS:
+		return Common::kWindows1251;
+	default:
+		return Common::kWindows1252;
+	}
+}
+
 PhoenixVREngine::PhoenixVREngine(OSystem *syst, const ADGameDescription *gameDesc) : Engine(syst),
 																					 _frameLimiter(g_system, kFPSLimit),
 																					 _gameDescription(gameDesc),
@@ -758,12 +768,16 @@ void PhoenixVREngine::loadVariables() {
 
 const Graphics::Font *PhoenixVREngine::getFont(int size, bool bold) const {
 #ifdef USE_FREETYPE2
-	if (size < 14)
-		return _font12.get();
-	else if (size < 18)
-		return _font14.get();
-	else
-		return _font18.get();
+	const int fontMaxSizes[] = {10, 12, 14, 16, 18, INT_MAX};
+
+	for (uint i = 0; i < ARRAYSIZE(fontMaxSizes); ++i) {
+		if (size < fontMaxSizes[i]) {
+			const Graphics::Font *font = bold ? _boldFonts[i].get() : nullptr;
+			return font ? font : _regularFonts[i].get();
+		}
+	}
+
+	return _regularFonts[ARRAYSIZE(fontMaxSizes) - 1].get();
 #else
 	return FontMan.getFontByUsage(Graphics::FontManager::kBigGUIFont);
 #endif
@@ -822,9 +836,9 @@ void PhoenixVREngine::rollover(int textId, RolloverType type) {
 		return;
 	}
 	auto &text = _textes.getVal(textId);
-	debug("rollover %s, %s font size: %d, bold: %d, color: %02x", dstRect.toString().c_str(), text.c_str(), size, bold, color);
+	debug("rollover %s, %s font size: %d, bold: %d, color: %02x", dstRect.toString().c_str(), text.encode(Common::kUtf8).c_str(), size, bold, color);
 
-	Common::Array<Common::String> lines;
+	Common::Array<Common::U32String> lines;
 	font->wordWrapText(text, dstRect.width(), lines, Graphics::kWordWrapDefault);
 
 	auto fontH = font->getFontHeight();
@@ -983,10 +997,13 @@ Common::Error PhoenixVREngine::run() {
 
 	_arn.reset(ARN::create());
 #ifdef USE_FREETYPE2
-	static const Common::String family("NotoSerif-Bold.ttf");
-	_font12.reset(Graphics::loadTTFFontFromArchive(family, 12));
-	_font14.reset(Graphics::loadTTFFontFromArchive(family, 14));
-	_font18.reset(Graphics::loadTTFFontFromArchive(family, 18));
+	static const Common::String regular("NotoSans-Regular.ttf");
+	static const Common::String bold("NotoSans-Bold.ttf");
+	const int fontSizes[] = {8, 10, 12, 14, 16, 18};
+	for (uint i = 0; i < ARRAYSIZE(fontSizes); ++i) {
+		_regularFonts[i].reset(Graphics::loadTTFFontFromArchive(regular, fontSizes[i]));
+		_boldFonts[i].reset(Graphics::loadTTFFontFromArchive(bold, fontSizes[i]));
+	}
 #endif
 
 	setCursorDefault(0, "Cursor1.pcx");
@@ -1010,6 +1027,7 @@ Common::Error PhoenixVREngine::run() {
 	{
 		Common::File textes;
 		if (textes.open(Common::Path("textes.txt"))) {
+			Common::CodePage textCodePage = getTextCodePage(_gameDescription->language);
 			while (!textes.eos()) {
 				auto text = textes.readLine();
 				if (text.empty() || text[0] != '*')
@@ -1022,7 +1040,7 @@ Common::Error PhoenixVREngine::run() {
 					++pos;
 				while (pos < text.size() && Common::isSpace(text[pos]))
 					++pos;
-				_textes.setVal(textId, text.substr(pos));
+				_textes.setVal(textId, Common::convertToU32String(text.c_str() + pos, textCodePage));
 			}
 			debug("loaded %u textes", _textes.size());
 		}
diff --git a/engines/phoenixvr/phoenixvr.h b/engines/phoenixvr/phoenixvr.h
index 322c6be74ec..07f411c38c9 100644
--- a/engines/phoenixvr/phoenixvr.h
+++ b/engines/phoenixvr/phoenixvr.h
@@ -272,11 +272,11 @@ private:
 	Common::Array<byte> _capturedState;
 	Common::Array<byte> _loadedState;
 
-	Common::HashMap<int, Common::String> _textes;
+	Common::HashMap<int, Common::U32String> _textes;
 
-	Common::ScopedPtr<Graphics::Font> _font12;
-	Common::ScopedPtr<Graphics::Font> _font14;
-	Common::ScopedPtr<Graphics::Font> _font18;
+	static const int kFontSizeCount = 6;
+	Common::ScopedPtr<Graphics::Font> _regularFonts[kFontSizeCount];
+	Common::ScopedPtr<Graphics::Font> _boldFonts[kFontSizeCount];
 
 	Common::ScopedPtr<Graphics::ManagedSurface> _text;
 	Common::Rect _textRect;


Commit: b06f7828e18dfc6185d7faed116531418a491df9
    https://github.com/scummvm/scummvm/commit/b06f7828e18dfc6185d7faed116531418a491df9
Author: Scorp (scorp at mrs.mn)
Date: 2026-06-11T23:10:30+01:00

Commit Message:
VIDEO: Allow Phoenix 4XM v1 videos

Changed paths:
    video/4xm_decoder.cpp


diff --git a/video/4xm_decoder.cpp b/video/4xm_decoder.cpp
index 5c3cc2d0695..36d2e9ed977 100644
--- a/video/4xm_decoder.cpp
+++ b/video/4xm_decoder.cpp
@@ -108,8 +108,6 @@ class FourXMDecoder::FourXMVideoTrack : public FixedRateVideoTrack {
 
 public:
 	FourXMVideoTrack(FourXMDecoder *dec, const Common::Rational &frameRate, uint w, uint h, uint16 version) : _dec(dec), _frameRate(frameRate), _w(w), _h(h), _version(version) {
-		if (_version <= 1)
-			error("versions 0 and 1 are not supported");
 		_blockType[0].reset(new HuffmanType(HuffmanType::fromFrequencies({16, 8, 4, 2, 1, 1})));
 		_blockType[1].reset(new HuffmanType(HuffmanType::fromFrequencies({8, 0, 4, 2, 1, 1})));
 		_blockType[2].reset(new HuffmanType(HuffmanType::fromFrequencies({8, 4, 0, 2, 1, 1})));
@@ -418,7 +416,7 @@ void FourXMDecoder::FourXMVideoTrack::decode_pfrm_block(uint16 *dst, const uint1
 		}
 		return;
 	}
-	if (code == 3 && _version >= 2)
+	if (code == 3)
 		return;
 
 	if (code == 0) {


Commit: 855a48e3a66c4cb624a678e7a2788cf149944069
    https://github.com/scummvm/scummvm/commit/855a48e3a66c4cb624a678e7a2788cf149944069
Author: Scorp (scorp at mrs.mn)
Date: 2026-06-11T23:10:30+01:00

Commit Message:
PHOENIXVR: Add more game variants

Changed paths:
    engines/phoenixvr/detection_tables.h


diff --git a/engines/phoenixvr/detection_tables.h b/engines/phoenixvr/detection_tables.h
index 75e68d363ef..efe84a36f11 100644
--- a/engines/phoenixvr/detection_tables.h
+++ b/engines/phoenixvr/detection_tables.h
@@ -75,6 +75,16 @@ const ADGameDescription gameDescriptions[] = {
 		GUIO1(GUIO_NONE)
 	},
 
+	{"necrono",
+		nullptr,
+		AD_ENTRY2s("script.pak", "da42a18dd02fc01f116228d5c219b2fd", 215,
+				   "textes.txt", "ab8efb7f5e92d2b76863c181bf2dbd10", 4959),
+		Common::RU_RUS,
+		Common::kPlatformWindows,
+		ADGF_DROPPLATFORM,
+		GUIO1(GUIO_NONE)
+	},
+
 	{"necrono",
 		nullptr,
 		AD_ENTRY2s("script.pak", "86294b9c445c3e06e24269c84036a207", 223,
@@ -125,6 +135,16 @@ const ADGameDescription gameDescriptions[] = {
 		GUIO1(GUIO_NONE)}
 	,
 
+	{"lochness",
+		nullptr,
+		AD_ENTRY2s("script.pak", "a7ee3aae653658f93bba7f237bcf06f3", 1904,
+				   "textes.txt", "d1546d04243ee63f9ff6c5fc551082e1", 1763),
+		Common::RU_RUS,
+		Common::kPlatformWindows,
+		ADGF_DROPPLATFORM,
+		GUIO1(GUIO_NONE)
+	},
+
 	{"lochness",
 		nullptr,
 		AD_ENTRY2s("script.pak", "a7ee3aae653658f93bba7f237bcf06f3", 1904,


Commit: a16464659b280ccb85b0f754efb5b6ce1c6d77d6
    https://github.com/scummvm/scummvm/commit/a16464659b280ccb85b0f754efb5b6ce1c6d77d6
Author: Scorp (scorp at mrs.mn)
Date: 2026-06-11T23:10:30+01:00

Commit Message:
PHOENIXVR: Improve save room rendering

Changed paths:
    engines/phoenixvr/commands.h
    engines/phoenixvr/game_state.cpp
    engines/phoenixvr/metaengine.cpp
    engines/phoenixvr/phoenixvr.cpp
    engines/phoenixvr/phoenixvr.h


diff --git a/engines/phoenixvr/commands.h b/engines/phoenixvr/commands.h
index 4c79268aca6..4750a3538e2 100644
--- a/engines/phoenixvr/commands.h
+++ b/engines/phoenixvr/commands.h
@@ -512,13 +512,7 @@ struct LoadSave : public Script::Command {
 		if (!status)
 			return false;
 
-		static const int faces[] = {4, 3, 5, 1};
-		int face = faces[(slot - 1) / 2];
-		bool odd = (slot - 1) & 1;
-		// taken from necronomicon - misaligned
-		int x = odd ? 275 : 97;
-		int y = 200;
-		g_engine->drawSlot(slot, face, x, y);
+		g_engine->drawSaveCard(slot);
 		return true;
 	}
 
diff --git a/engines/phoenixvr/game_state.cpp b/engines/phoenixvr/game_state.cpp
index b24e1a8abd4..89e2e45e9d5 100644
--- a/engines/phoenixvr/game_state.cpp
+++ b/engines/phoenixvr/game_state.cpp
@@ -3,19 +3,31 @@
 #include "graphics/surface.h"
 
 namespace PhoenixVR {
+
+static Common::String readSaveText(Common::SeekableReadStream &stream) {
+	const uint32 size = stream.readUint32LE();
+	Common::String result;
+
+	for (uint32 i = 0; i < size; ++i) {
+		const byte c = stream.readByte();
+		if (c == 0) {
+			result += '\n';
+		} else {
+			result += static_cast<char>(c);
+		}
+	}
+
+	return result;
+}
+
 GameState GameState::load(Common::SeekableReadStream &stream) {
 	GameState state;
 
-	auto readString = [&]() {
-		auto size = stream.readUint32LE();
-		return stream.readString(0, size);
-	};
-
-	state.script = readString();
+	state.script = readSaveText(stream);
 	debug("save.script: %s", state.script.c_str());
-	state.game = readString();
+	state.game = readSaveText(stream);
 	debug("save.game: %s", state.game.c_str());
-	state.info = readString();
+	state.info = readSaveText(stream);
 	debug("save.datetime: %s", state.info.c_str());
 	uint dibHeaderSize = stream.readUint32LE();
 	stream.seek(-4, SEEK_CUR);
diff --git a/engines/phoenixvr/metaengine.cpp b/engines/phoenixvr/metaengine.cpp
index ae4dd2e88aa..19f1d24031a 100644
--- a/engines/phoenixvr/metaengine.cpp
+++ b/engines/phoenixvr/metaengine.cpp
@@ -33,6 +33,15 @@
 
 namespace PhoenixVR {
 
+static Common::String flattenSaveText(const Common::String &text) {
+	Common::String result = text;
+	for (uint i = 0; i < result.size(); ++i) {
+		if (result[i] == '\n')
+			result.setChar(' ', i);
+	}
+	return result;
+}
+
 static const ADExtraGuiOptionsMap optionsList[] = {
 	{GAMEOPTION_ORIGINAL_SAVELOAD,
 	 {_s("Use original save/load screens"),
@@ -150,7 +159,7 @@ SaveStateDescriptor PhoenixVRMetaEngine::querySaveMetaInfos(const char *target,
 	SaveStateDescriptor desc;
 	desc.setSaveSlot(slotIdx);
 	desc.setDeletableFlag(true);
-	desc.setDescription(state.game + " " + state.info);
+	desc.setDescription(PhoenixVR::flattenSaveText(state.game) + " " + PhoenixVR::flattenSaveText(state.info));
 	Graphics::PixelFormat rgb565(2, 5, 6, 5, 0, 11, 5, 0, 0);
 	desc.setThumbnail(state.getThumbnail(rgb565, 160));
 	return desc;
diff --git a/engines/phoenixvr/phoenixvr.cpp b/engines/phoenixvr/phoenixvr.cpp
index fe7163b293d..ab34e347fb7 100644
--- a/engines/phoenixvr/phoenixvr.cpp
+++ b/engines/phoenixvr/phoenixvr.cpp
@@ -64,12 +64,237 @@ static Common::CodePage getTextCodePage(Common::Language language) {
 	}
 }
 
+static bool isAmerzoneGame(const ADGameDescription *gameDesc) {
+	return !strcmp(gameDesc->gameId, "amerzone");
+}
+
+static Common::String getAmerzoneLevelLabel(const Common::String &script) {
+	static const struct {
+		const char *prefix;
+		const char *label;
+	} levels[] = {
+		{"01VR_PHARE", "Le Phare"},
+		{"02VR_ILE", "L'Ile"},
+		{"03VR_PUEBLO", "Le Pueblo"},
+		{"04VR_FLEUVE", "Le Fleuve"},
+		{"05VR_VILLAGEMARAIS", "Le Village"},
+		{"07VRTEMPLE_VOLCAN", "Le Temple"}
+	};
+
+	for (const auto &level : levels) {
+		if (script.hasPrefixIgnoreCase(level.prefix))
+			return level.label;
+	}
+
+	return "Amerzone";
+}
+
+static const char *mfull[] = {
+	"January", "February", "March", "April", "May", "June",
+	"July", "August", "September", "October", "November", "December"
+};
+
+static const char *wday[] = {
+	"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"
+};
+
+static Common::String makeSaveText(const Common::String &firstLine, const Common::String &secondLine) {
+	Common::String result = firstLine;
+	result += '\0';
+	result += secondLine;
+	return result;
+}
+
+static Common::String makeSaveText(const Common::String &firstLine, const Common::String &secondLine, const Common::String &thirdLine) {
+	Common::String result = makeSaveText(firstLine, secondLine);
+	result += '\0';
+	result += thirdLine;
+	return result;
+}
+
+static Common::String formatSaveInfo(const TimeDate &td, bool longDate, const Common::String &place = Common::String()) {
+	if (longDate) {
+		return makeSaveText(
+			Common::String::format("%s, %s %d, %04d", wday[td.tm_wday], mfull[td.tm_mon], td.tm_mday, td.tm_year + 1900),
+			Common::String::format("%02d:%02d:%02d %s", td.tm_hour, td.tm_min, td.tm_sec, td.tm_hour < 12 ? "AM" : "PM"),
+			place);
+	}
+
+	return makeSaveText(
+		Common::String::format("%s %02d %02d %04d", wday[td.tm_wday], td.tm_mday, td.tm_mon + 1, td.tm_year + 1900),
+		Common::String::format("%02d h %02d", td.tm_hour, td.tm_min));
+}
+
+static int mapSaveSlotY(int y, bool splitV, int tileY) {
+	int splitLine = (tileY + 1) * 256;
+	if (splitV && y >= splitLine)
+		return (tileY + 3) * 256 + y - splitLine;
+	return y;
+}
+
+static void fillSaveSlotRect(Graphics::Surface &dst, const Common::Rect &rect, uint32 color, bool splitV, int tileY) {
+	if (splitV) {
+		int splitLine = (tileY + 1) * 256;
+		int topH = CLIP<int>(splitLine - rect.top, 0, rect.height());
+		if (topH > 0) {
+			Common::Rect top = rect;
+			top.bottom = rect.top + topH;
+			dst.fillRect(top, color);
+		}
+		if (topH < rect.height()) {
+			int bottomY = (tileY + 3) * 256 + MAX(rect.top - splitLine, 0);
+			Common::Rect bottom(rect.left, bottomY, rect.right, bottomY + rect.height() - topH);
+			dst.fillRect(bottom, color);
+		}
+	} else {
+		dst.fillRect(rect, color);
+	}
+}
+
+static int drawSaveTextBlock(Graphics::Surface &dst, const Graphics::Font *font, const Common::String &text,
+		int x, int y, int width, uint32 color, Graphics::TextAlign align, int lineHeight, bool splitV, int tileY,
+		bool reserveEmptyFinalLine = false) {
+	bool hasText = false;
+	for (uint i = 0; i < text.size(); ++i) {
+		if (text[i] != '\n' && text[i] != '\0') {
+			hasText = true;
+			break;
+		}
+	}
+	if (!hasText)
+		return y;
+
+	uint start = 0;
+	for (uint i = 0; i < text.size(); ++i) {
+		if (text[i] == '\n' || text[i] == '\0') {
+			if (i > start) {
+				Common::String line;
+				for (uint j = start; j < i; ++j)
+					line += text[j];
+				font->drawString(&dst, line, x, mapSaveSlotY(y, splitV, tileY), width, color, align);
+				y += lineHeight;
+			} else if (reserveEmptyFinalLine && i == text.size() - 1) {
+				y += lineHeight;
+			}
+			start = i + 1;
+		}
+	}
+	if (start < text.size()) {
+		Common::String line;
+		for (uint j = start; j < text.size(); ++j)
+			line += text[j];
+		font->drawString(&dst, line, x, mapSaveSlotY(y, splitV, tileY), width, color, align);
+		y += lineHeight;
+	}
+
+	return y;
+}
+
+static int saveCardTileId(int face, int x, int y) {
+	return (face << 2) + ((y < 256) ? (x < 256 ? 0 : 1) : (x < 256 ? 3 : 2));
+}
+
+static void copyCubeFaceToSurface(Graphics::ManagedSurface &faceSurface, const Graphics::Surface &vrSurface, int face) {
+	for (int y = 0; y < 512; ++y) {
+		for (int x = 0; x < 512; ++x) {
+			const int tileId = saveCardTileId(face, x, y);
+			faceSurface.setPixel(x, y, vrSurface.getPixel(x & 0xff, (tileId << 8) + (y & 0xff)));
+		}
+	}
+}
+
+static void copySurfaceToCubeFace(Graphics::Surface &vrSurface, const Graphics::ManagedSurface &faceSurface, int face) {
+	for (int y = 0; y < 512; ++y) {
+		for (int x = 0; x < 512; ++x) {
+			const int tileId = saveCardTileId(face, x, y);
+			vrSurface.setPixel(x & 0xff, (tileId << 8) + (y & 0xff), faceSurface.getPixel(x, y));
+		}
+	}
+}
+
+static void projectSaveCard(Graphics::ManagedSurface &faceSurface, const Graphics::ManagedSurface &card, float angle) {
+	struct Vertex {
+		float x;
+		float y;
+		float invW;
+		float uOverW;
+		float vOverW;
+	};
+
+	const float srcW = static_cast<float>(card.w);
+	const float srcH = static_cast<float>(card.h);
+	const float distance = srcW / 8.0f + srcW * 8.0f / 6.283100128173828f;
+	const float cosA = cosf(angle);
+	const float sinA = sinf(angle);
+
+	auto makeVertex = [&](float modelU, float modelV, float textureU, float textureV) {
+		const float modelX = modelU - srcW / 2.0f;
+		const float modelY = distance;
+		const float modelZ = srcH / 2.0f - modelV + 32.0f;
+		const float projectedW = (modelX * sinA - modelY * cosA) / 256.0f;
+		const float invW = 1.0f / projectedW;
+
+		Vertex vertex;
+		vertex.x = (modelX * (cosA + sinA) + modelY * (sinA - cosA)) * invW;
+		vertex.y = (modelX * sinA - modelY * cosA - modelZ) * invW;
+		vertex.invW = invW;
+		vertex.uOverW = textureU * invW;
+		vertex.vOverW = textureV * invW;
+		return vertex;
+	};
+
+	Vertex vertices[4] = {
+		makeVertex(0.0f, 0.0f, srcW, srcH),
+		makeVertex(static_cast<float>(card.w), 0.0f, 0.0f, srcH),
+		makeVertex(static_cast<float>(card.w), static_cast<float>(card.h), 0.0f, 0.0f),
+		makeVertex(0.0f, static_cast<float>(card.h), srcW, 0.0f)
+	};
+
+	auto rasterizeTriangle = [&](const Vertex &a, const Vertex &b, const Vertex &c) {
+		const float area = (b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x);
+		if (ABS(area) < 0.0001f)
+			return;
+
+		int minX = CLIP<int>(static_cast<int>(floorf(MIN(a.x, MIN(b.x, c.x)))), 0, faceSurface.w - 1);
+		int maxX = CLIP<int>(static_cast<int>(ceilf(MAX(a.x, MAX(b.x, c.x)))), 0, faceSurface.w - 1);
+		int minY = CLIP<int>(static_cast<int>(floorf(MIN(a.y, MIN(b.y, c.y)))), 0, faceSurface.h - 1);
+		int maxY = CLIP<int>(static_cast<int>(ceilf(MAX(a.y, MAX(b.y, c.y)))), 0, faceSurface.h - 1);
+
+		for (int y = minY; y <= maxY; ++y) {
+			for (int x = minX; x <= maxX; ++x) {
+				const float px = static_cast<float>(x) + 0.5f;
+				const float py = static_cast<float>(y) + 0.5f;
+				const float w0 = ((b.x - px) * (c.y - py) - (b.y - py) * (c.x - px)) / area;
+				const float w1 = ((c.x - px) * (a.y - py) - (c.y - py) * (a.x - px)) / area;
+				const float w2 = 1.0f - w0 - w1;
+				if (w0 < 0.0f || w1 < 0.0f || w2 < 0.0f)
+					continue;
+
+				const float invW = w0 * a.invW + w1 * b.invW + w2 * c.invW;
+				if (ABS(invW) < 0.0001f)
+					continue;
+				const float u = (w0 * a.uOverW + w1 * b.uOverW + w2 * c.uOverW) / invW;
+				const float v = (w0 * a.vOverW + w1 * b.vOverW + w2 * c.vOverW) / invW;
+				if (u < 0.0f || u > srcW || v < 0.0f || v > srcH)
+					continue;
+
+				const int srcX = CLIP<int>(static_cast<int>(floorf(u)), 0, card.w - 1);
+				const int srcY = CLIP<int>(static_cast<int>(floorf(v)), 0, card.h - 1);
+				faceSurface.setPixel(x, y, card.getPixel(srcX, srcY));
+			}
+		}
+	};
+
+	rasterizeTriangle(vertices[0], vertices[1], vertices[2]);
+	rasterizeTriangle(vertices[0], vertices[2], vertices[3]);
+}
+
 PhoenixVREngine::PhoenixVREngine(OSystem *syst, const ADGameDescription *gameDesc) : Engine(syst),
 																					 _frameLimiter(g_system, kFPSLimit),
 																					 _gameDescription(gameDesc),
 																					 _randomSource("PhoenixVR"),
 																					 _rgb565(2, 5, 6, 5, 0, 11, 5, 0, 0),
-																					 _thumbnail(139, 103, _rgb565),
+																					 _thumbnail(isAmerzoneGame(gameDesc) ? 232 : 139, isAmerzoneGame(gameDesc) ? 174 : 103, _rgb565),
 																					 _lockKey(13),
 																					 _fov(kPi2),
 																					 _angleX(0),
@@ -1415,14 +1640,16 @@ Common::Error PhoenixVREngine::saveGameStream(Common::WriteStream *slot, bool is
 	GameState state;
 	state.script = _contextScript;
 	state.game = _contextLabel;
-
-	static const char *wday[] = {
-		"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};
+	const bool isAmerzone = gameIdMatches("amerzone");
+	Common::String amerzoneLevelLabel;
+	if (isAmerzone) {
+		amerzoneLevelLabel = getAmerzoneLevelLabel(state.script);
+		state.game.clear();
+	}
 
 	TimeDate td = {};
 	g_system->getTimeAndDate(td);
-	// Saturday 03 01 2026[\x00]23 h 17
-	state.info = Common::String::format("%s %02d %02d %04d%c%02d h %02d", wday[td.tm_wday], td.tm_mday, td.tm_mon + 1, td.tm_year + 1900, 0, td.tm_hour, td.tm_min);
+	state.info = formatSaveInfo(td, isAmerzone, amerzoneLevelLabel);
 
 	state.thumbWidth = _thumbnail.w;
 	state.thumbHeight = _thumbnail.h;
@@ -1458,11 +1685,60 @@ Common::Error PhoenixVREngine::saveGameStream(Common::WriteStream *slot, bool is
 	return Common::kNoError;
 }
 
+void PhoenixVREngine::drawSaveCard(int idx) {
+	if (!gameIdMatches("amerzone")) {
+		static const int faces[] = {4, 3, 5, 1};
+		const int face = faces[(idx - 1) / 2];
+		const bool odd = (idx - 1) & 1;
+		drawSlot(idx, face, odd ? 275 : 97, 200);
+		return;
+	}
+
+	Common::ScopedPtr<Common::InSaveFile> slot(_saveFileMan->openForLoading(getSaveStateName(idx)));
+	if (!slot)
+		return;
+
+	auto state = GameState::load(*slot);
+	auto &dst = _vr.getSurface();
+	Graphics::Surface *thumbnail = state.getThumbnail(dst.format, 232);
+	const int cardW = thumbnail->w + 6;
+	const int cardH = thumbnail->w + 30;
+
+	Graphics::ManagedSurface card(cardW, cardH, dst.format);
+	const uint32 white = dst.format.RGBToColor(0xff, 0xff, 0xff);
+	const uint32 black = dst.format.RGBToColor(0, 0, 0);
+	card.fillRect(Common::Rect(0, 0, cardW, cardH), white);
+	card.fillRect(Common::Rect(0, 0, cardW, 1), black);
+	card.fillRect(Common::Rect(0, cardH - 1, cardW, cardH), black);
+	card.fillRect(Common::Rect(0, 0, 1, cardH), black);
+	card.fillRect(Common::Rect(cardW - 1, 0, cardW, cardH), black);
+	card.copyRectToSurface(*thumbnail, 3, 6, thumbnail->getRect());
+
+	const Graphics::Font *font = getFont(16, true);
+	if (font) {
+		int textY = thumbnail->h + 18;
+		textY = drawSaveTextBlock(*card.surfacePtr(), font, state.game, 0, textY, cardW, black, Graphics::kTextAlignCenter, 18, false, 0);
+		drawSaveTextBlock(*card.surfacePtr(), font, state.info, 0, textY, cardW, black, Graphics::kTextAlignCenter, 18, false, 0);
+	}
+
+	static const int faces[] = {4, 3, 5, 1};
+	const int face = faces[(idx - 1) / 2];
+	const float angle = ((idx - 1) & 1) ? -kPi / 8.0f : kPi / 8.0f;
+	Graphics::ManagedSurface faceSurface(512, 512, dst.format);
+	copyCubeFaceToSurface(faceSurface, dst, face);
+	projectSaveCard(faceSurface, card, angle);
+	copySurfaceToCubeFace(dst, faceSurface, face);
+
+	thumbnail->free();
+	delete thumbnail;
+}
+
 void PhoenixVREngine::drawSlot(int idx, int face, int x, int y) {
 	Common::ScopedPtr<Common::InSaveFile> slot(_saveFileMan->openForLoading(getSaveStateName(idx)));
 	if (!slot)
 		return;
 	auto state = GameState::load(*slot);
+	const bool isAmerzone = gameIdMatches("amerzone");
 
 	y += face * 4 * 256;
 	bool splitV = true;
@@ -1473,8 +1749,24 @@ void PhoenixVREngine::drawSlot(int idx, int face, int x, int y) {
 	}
 
 	auto &dst = _vr.getSurface();
-	auto *src = state.getThumbnail(dst.format);
+	auto *src = state.getThumbnail(dst.format, isAmerzone ? 232 : 0);
 	int tileY = y / 256;
+	if (isAmerzone) {
+		const int cardX = x - 3;
+		const int cardY = y - 6;
+		const int cardW = src->w + 6;
+		const int cardH = src->w + 30;
+		uint32 white = dst.format.RGBToColor(0xff, 0xff, 0xff);
+		uint32 black = dst.format.RGBToColor(0, 0, 0);
+		fillSaveSlotRect(dst, Common::Rect(cardX, cardY, cardX + cardW, cardY + cardH), white, splitV, tileY);
+		fillSaveSlotRect(dst, Common::Rect(cardX, cardY, cardX + cardW, cardY + 1), black, splitV, tileY);
+		fillSaveSlotRect(dst, Common::Rect(cardX, cardY + cardH - 1, cardX + cardW, cardY + cardH), black, splitV, tileY);
+		fillSaveSlotRect(dst, Common::Rect(cardX, cardY, cardX + 1, cardY + cardH), black, splitV, tileY);
+		fillSaveSlotRect(dst, Common::Rect(cardX + cardW - 1, cardY, cardX + cardW, cardY + cardH), black, splitV, tileY);
+		x = cardX + 3;
+		y = cardY + 6;
+		tileY = y / 256;
+	}
 	auto srcRect = src->getRect();
 	short srcSplitY = MIN(y + src->h, (tileY + 1) * 256) - y;
 	if (splitV)
@@ -1485,12 +1777,26 @@ void PhoenixVREngine::drawSlot(int idx, int face, int x, int y) {
 		srcRect.bottom = src->h;
 		dst.copyRectToSurface(*src, x, (tileY + 3) * 256, srcRect);
 	}
-	auto *font = getFont(12, false);
-	static int kMargin = 14;
+	auto *font = getFont(isAmerzone ? 10 : 12, isAmerzone);
 	if (font) {
 		auto color = dst.format.RGBToColor(0, 0, 0);
-		auto dstY = splitV ? (tileY + 3) * 256 - srcSplitY : y;
-		font->drawString(&dst, state.info, x, dstY + kMargin + src->h, src->w, color, Graphics::TextAlign::kTextAlignCenter);
+		int textX = x;
+		int textW = src->w;
+		Graphics::TextAlign textAlign = Graphics::kTextAlignLeft;
+		int textY = y + 0x72;
+		int lineHeight = 14;
+		if (isAmerzone) {
+			textX = x - 3;
+			textW = src->w + 6;
+			textY = y - 6 + src->h + 14;
+			textAlign = Graphics::kTextAlignCenter;
+
+			textY = drawSaveTextBlock(dst, font, state.game, textX, textY, textW, color, textAlign, lineHeight, splitV, tileY);
+			drawSaveTextBlock(dst, font, state.info, textX, textY, textW, color, textAlign, lineHeight, splitV, tileY);
+		} else {
+			textY = drawSaveTextBlock(dst, font, state.game, textX, textY, textW, color, textAlign, lineHeight, splitV, tileY, true);
+			drawSaveTextBlock(dst, font, state.info, textX, textY, textW, color, textAlign, lineHeight, splitV, tileY);
+		}
 	}
 
 	src->free();
diff --git a/engines/phoenixvr/phoenixvr.h b/engines/phoenixvr/phoenixvr.h
index 07f411c38c9..f5928c5328b 100644
--- a/engines/phoenixvr/phoenixvr.h
+++ b/engines/phoenixvr/phoenixvr.h
@@ -175,6 +175,7 @@ public:
 	Common::Error loadGameStream(Common::SeekableReadStream *stream) override;
 	Common::Error saveGameStream(Common::WriteStream *stream, bool isAutosave = false) override;
 	void drawSlot(int idx, int face, int x, int y);
+	void drawSaveCard(int idx);
 	void captureContext();
 
 	void setContextLabel(const Common::String &contextLabel) {


Commit: e07b3297ab63c2759d7c4077d948a7c8a356e47b
    https://github.com/scummvm/scummvm/commit/e07b3297ab63c2759d7c4077d948a7c8a356e47b
Author: Scorp (scorp at mrs.mn)
Date: 2026-06-11T23:10:30+01:00

Commit Message:
PHOENIXVR: Fix Amerzone save room level restore

Changed paths:
    engines/phoenixvr/commands.h
    engines/phoenixvr/phoenixvr.cpp
    engines/phoenixvr/phoenixvr.h


diff --git a/engines/phoenixvr/commands.h b/engines/phoenixvr/commands.h
index 4750a3538e2..ca03d986cd7 100644
--- a/engines/phoenixvr/commands.h
+++ b/engines/phoenixvr/commands.h
@@ -548,6 +548,15 @@ struct LoadSave : public Script::Command {
 		} else if (n == 2) {
 			auto &srcVar = args[0];
 			auto &dstVar = args[1];
+			if (!dstVar.empty() && Common::isDigit(dstVar[0])) {
+				uint level = atoi(dstVar.c_str());
+				uint currentLevel = g_engine->currentAmerzoneLevel();
+				if (currentLevel != 0) {
+					g_engine->setVariable(srcVar, currentLevel == level);
+					return;
+				}
+			}
+
 			auto value = g_engine->getVariable(srcVar);
 			g_engine->setVariable(srcVar, 0);
 			g_engine->setVariable(dstVar, value);
diff --git a/engines/phoenixvr/phoenixvr.cpp b/engines/phoenixvr/phoenixvr.cpp
index ab34e347fb7..58672d84f74 100644
--- a/engines/phoenixvr/phoenixvr.cpp
+++ b/engines/phoenixvr/phoenixvr.cpp
@@ -341,6 +341,20 @@ bool PhoenixVREngine::gameIdMatches(const char *gameId) const {
 	return strcmp(_gameDescription->gameId, gameId) == 0;
 }
 
+uint PhoenixVREngine::currentAmerzoneLevel() const {
+	if (!gameIdMatches("amerzone"))
+		return 0;
+
+	uint index = 0;
+	for (const Common::String &level : _levels) {
+		++index;
+		if (_contextScript.hasPrefixIgnoreCase(level))
+			return index;
+	}
+
+	return _currentLevel;
+}
+
 Common::String PhoenixVREngine::removeDrive(const Common::String &path) {
 	if (path.size() < 2 || path[1] != ':')
 		return path;
@@ -1613,7 +1627,7 @@ Common::Error PhoenixVREngine::loadGameStream(Common::SeekableReadStream *slot)
 			auto &level = _levels[i];
 			if (state.script.hasPrefixIgnoreCase(level)) {
 				debug("current level is %u", i);
-				_currentLevel = i + 1;
+				_currentLevel = i;
 				break;
 			}
 		}
diff --git a/engines/phoenixvr/phoenixvr.h b/engines/phoenixvr/phoenixvr.h
index f5928c5328b..0860fe659c2 100644
--- a/engines/phoenixvr/phoenixvr.h
+++ b/engines/phoenixvr/phoenixvr.h
@@ -186,6 +186,7 @@ public:
 
 	bool wasRestarted() const { return _restarted; }
 	bool wasLoaded() const { return _loaded; }
+	uint currentAmerzoneLevel() const;
 
 	void saveVariables();
 	void loadVariables();


Commit: 3d68656634218d213a8aa3912573c85cc2d55850
    https://github.com/scummvm/scummvm/commit/3d68656634218d213a8aa3912573c85cc2d55850
Author: Scorp (scorp at mrs.mn)
Date: 2026-06-11T23:10:30+01:00

Commit Message:
PHOENIXVR: Add more game variants

Changed paths:
    engines/phoenixvr/detection_tables.h


diff --git a/engines/phoenixvr/detection_tables.h b/engines/phoenixvr/detection_tables.h
index efe84a36f11..b733ed3b108 100644
--- a/engines/phoenixvr/detection_tables.h
+++ b/engines/phoenixvr/detection_tables.h
@@ -128,8 +128,8 @@ const ADGameDescription gameDescriptions[] = {
 	{"lochness",
 		nullptr,
 		AD_ENTRY2s("script.pak", "a7ee3aae653658f93bba7f237bcf06f3", 1904,
-				   "textes.txt", "3f2deea06efed98a355ea03d69fd15ce", 1608),
-		Common::EN_USA,
+				   "textes.txt", "d1546d04243ee63f9ff6c5fc551082e1", 1763),
+		Common::RU_RUS,
 		Common::kPlatformWindows,
 		ADGF_DROPPLATFORM | ADGF_CD,
 		GUIO1(GUIO_NONE)}



From noreply at scummvm.org  Thu Jun 11 22:10:40 2026
From: noreply at scummvm.org (ScummVM-Translations)
Date: Thu, 11 Jun 2026 22:10:40 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 a4d42c6ca6e80b17d47f6248a0b9fd55350b3d24
Message-ID: <20260611221040.A5F9A7005CF@ron.scummvm.net>

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

Summary:
a4d42c6ca6 I18N: Update translations templates


Commit: a4d42c6ca6e80b17d47f6248a0b9fd55350b3d24
    https://github.com/scummvm/scummvm/commit/a4d42c6ca6e80b17d47f6248a0b9fd55350b3d24
Author: Weblate (noreply at weblate.org)
Date: 2026-06-11T22:10:35Z

Commit Message:
I18N: Update translations templates

Changed paths:
    po/scummvm.pot


diff --git a/po/scummvm.pot b/po/scummvm.pot
index e5a39248204..6f77ee1f9cf 100644
--- a/po/scummvm.pot
+++ b/po/scummvm.pot
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2026.2.1git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-11 13:12+0000\n"
+"POT-Creation-Date: 2026-06-11 22:10+0000\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL at ADDRESS>\n"
 "Language-Team: LANGUAGE <LL at li.org>\n"
@@ -5434,7 +5434,7 @@ msgstr ""
 #: backends/platform/sdl/miyoo/miyoo.cpp:114
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
-#: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
+#: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:110
 #: engines/scumm/metaengine.cpp:1232 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr ""
@@ -5810,7 +5810,7 @@ msgstr ""
 #: engines/grim/grim.cpp:621 engines/hdb/metaengine.cpp:251
 #: engines/hpl1/metaengine.cpp:94 engines/icb/icb.cpp:101
 #: engines/icb/icb.cpp:179 engines/petka/metaengine.cpp:173
-#: engines/phoenixvr/metaengine.cpp:73 engines/scumm/help.cpp:228
+#: engines/phoenixvr/metaengine.cpp:82 engines/scumm/help.cpp:228
 #: engines/scumm/help.cpp:237 engines/scumm/help.cpp:244
 #: engines/scumm/help.cpp:252 engines/sherlock/metaengine.cpp:435
 #: engines/sludge/keymapper_tables.h:58 engines/sludge/keymapper_tables.h:119
@@ -5949,7 +5949,7 @@ msgstr ""
 #: engines/got/metaengine.cpp:35 engines/groovie/metaengine.cpp:53
 #: engines/m4/metaengine.cpp:37 engines/mads/metaengine.cpp:116
 #: engines/nancy/metaengine.cpp:104 engines/neverhood/dialogs.cpp:47
-#: engines/pelrock/metaengine.cpp:35 engines/phoenixvr/metaengine.cpp:38
+#: engines/pelrock/metaengine.cpp:35 engines/phoenixvr/metaengine.cpp:47
 #: engines/sci/detection_options.h:105 engines/sherlock/metaengine.cpp:47
 #: engines/toltecs/metaengine.cpp:44 engines/trecision/metaengine.cpp:41
 #: engines/ultima/metaengine.cpp:56 engines/waynesworld/metaengine.cpp:37
@@ -5964,7 +5964,7 @@ msgstr ""
 #: engines/got/metaengine.cpp:36 engines/groovie/metaengine.cpp:54
 #: engines/m4/metaengine.cpp:38 engines/mads/metaengine.cpp:117
 #: engines/nancy/metaengine.cpp:105 engines/neverhood/dialogs.cpp:48
-#: engines/pelrock/metaengine.cpp:36 engines/phoenixvr/metaengine.cpp:39
+#: engines/pelrock/metaengine.cpp:36 engines/phoenixvr/metaengine.cpp:48
 #: engines/sci/detection_options.h:106 engines/sherlock/metaengine.cpp:48
 #: engines/toltecs/metaengine.cpp:45 engines/trecision/metaengine.cpp:42
 #: engines/ultima/metaengine.cpp:57 engines/waynesworld/metaengine.cpp:38
@@ -6494,7 +6494,7 @@ msgid "Show version"
 msgstr ""
 
 #: engines/asylum/metaengine.cpp:120 engines/crab/input/input.cpp:271
-#: engines/phoenixvr/metaengine.cpp:96 engines/queen/metaengine.cpp:253
+#: engines/phoenixvr/metaengine.cpp:105 engines/queen/metaengine.cpp:253
 #: engines/sludge/keymapper_tables.h:308 engines/sludge/keymapper_tables.h:404
 #: engines/sludge/keymapper_tables.h:471 engines/sludge/keymapper_tables.h:502
 #: engines/ultima/nuvie/metaengine.cpp:92
@@ -6503,7 +6503,7 @@ msgid "Quick load"
 msgstr ""
 
 #: engines/asylum/metaengine.cpp:125 engines/crab/input/input.cpp:266
-#: engines/phoenixvr/metaengine.cpp:91 engines/queen/metaengine.cpp:247
+#: engines/phoenixvr/metaengine.cpp:100 engines/queen/metaengine.cpp:247
 #: engines/sludge/keymapper_tables.h:64 engines/sludge/keymapper_tables.h:303
 #: engines/sludge/keymapper_tables.h:410 engines/sludge/keymapper_tables.h:465
 #: engines/sludge/keymapper_tables.h:496 engines/ultima/nuvie/metaengine.cpp:91
@@ -6795,7 +6795,7 @@ msgstr ""
 #. I18N: This keymap allows skipping video cutscenes
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
-#: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
+#: engines/phoenixvr/metaengine.cpp:94 engines/scumm/help.cpp:79
 #: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
@@ -6975,7 +6975,7 @@ msgstr ""
 #: engines/freescape/games/dark/dark.cpp:236
 #: engines/freescape/games/driller/driller.cpp:129
 #: engines/freescape/games/eclipse/eclipse.cpp:340
-#: engines/mads/metaengine.cpp:403 engines/phoenixvr/metaengine.cpp:80
+#: engines/mads/metaengine.cpp:403 engines/phoenixvr/metaengine.cpp:89
 #: engines/saga/metaengine.cpp:437 engines/sherlock/metaengine.cpp:359
 #: engines/sherlock/metaengine.cpp:618 engines/sherlock/metaengine.cpp:852
 #: engines/stark/metaengine.cpp:280 engines/sword1/metaengine.cpp:295
@@ -7592,7 +7592,7 @@ msgstr ""
 
 #. I18N: Perform action on object where cursor points
 #: engines/darkseed/metaengine.cpp:42 engines/dragons/metaengine.cpp:135
-#: engines/phoenixvr/metaengine.cpp:66 engines/tinsel/metaengine.cpp:294
+#: engines/phoenixvr/metaengine.cpp:75 engines/tinsel/metaengine.cpp:294
 #: engines/twp/metaengine.cpp:150
 msgid "Action"
 msgstr ""
@@ -10877,7 +10877,7 @@ msgstr ""
 msgid "Close current interface"
 msgstr ""
 
-#: engines/phoenixvr/metaengine.cpp:107
+#: engines/phoenixvr/metaengine.cpp:116
 #: engines/wintermute/keymapper_tables.h:239
 #: engines/wintermute/keymapper_tables.h:262
 #: engines/wintermute/keymapper_tables.h:313



From noreply at scummvm.org  Thu Jun 11 22:11:12 2026
From: noreply at scummvm.org (ScummVM-Translations)
Date: Thu, 11 Jun 2026 22:11:12 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 2402b516ea5838d43dfabec31120a14ee305fb6e
Message-ID: <20260611221112.9006A7005BC@ron.scummvm.net>

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

Summary:
2402b516ea I18N: Update translation files


Commit: 2402b516ea5838d43dfabec31120a14ee305fb6e
    https://github.com/scummvm/scummvm/commit/2402b516ea5838d43dfabec31120a14ee305fb6e
Author: Weblate (noreply at weblate.org)
Date: 2026-06-11T22:10:53Z

Commit Message:
I18N: Update translation files

Updated by "Update PO files to match POT (msgmerge)" hook in Weblate.

Translation: ScummVM/scummvm
Translate-URL: https://translations.scummvm.org/projects/scummvm/scummvm/

Changed paths:
    po/ar.po
    po/be-tarask.po
    po/be_BY.po
    po/ca_ES.po
    po/cs_CZ.po
    po/da.po
    po/de_DE.po
    po/el.po
    po/es_ES.po
    po/eu.po
    po/fi_FI.po
    po/fr_FR.po
    po/gl_ES.po
    po/he.po
    po/hi.po
    po/hu_HU.po
    po/it_IT.po
    po/ja.po
    po/ka.po
    po/ko.po
    po/nb_NO.po
    po/nl_NL.po
    po/nn_NO.po
    po/pl_PL.po
    po/pt_BR.po
    po/pt_PT.po
    po/ro.po
    po/ru_RU.po
    po/sv_SE.po
    po/tr.po
    po/uk_UA.po
    po/zh.po
    po/zh_Hans.po
    po/zh_Hant.po


diff --git a/po/ar.po b/po/ar.po
index c9ff1bfdb20..588695cc0a2 100644
--- a/po/ar.po
+++ b/po/ar.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-11 13:12+0000\n"
+"POT-Creation-Date: 2026-06-11 22:10+0000\n"
 "PO-Revision-Date: 2026-02-24 22:26+0000\n"
 "Last-Translator: Mohamed Shaaban <mohmedbashamedo at hotmail.com>\n"
 "Language-Team: Arabic <https://translations.scummvm.org/projects/scummvm/"
@@ -5934,7 +5934,7 @@ msgstr "R2"
 #: backends/platform/sdl/miyoo/miyoo.cpp:114
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
-#: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
+#: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:110
 #: engines/scumm/metaengine.cpp:1232 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "??????????????"
@@ -6326,7 +6326,7 @@ msgstr "????????"
 #: engines/grim/grim.cpp:621 engines/hdb/metaengine.cpp:251
 #: engines/hpl1/metaengine.cpp:94 engines/icb/icb.cpp:101
 #: engines/icb/icb.cpp:179 engines/petka/metaengine.cpp:173
-#: engines/phoenixvr/metaengine.cpp:73 engines/scumm/help.cpp:228
+#: engines/phoenixvr/metaengine.cpp:82 engines/scumm/help.cpp:228
 #: engines/scumm/help.cpp:237 engines/scumm/help.cpp:244
 #: engines/scumm/help.cpp:252 engines/sherlock/metaengine.cpp:435
 #: engines/sludge/keymapper_tables.h:58 engines/sludge/keymapper_tables.h:119
@@ -6474,7 +6474,7 @@ msgstr ""
 #: engines/got/metaengine.cpp:35 engines/groovie/metaengine.cpp:53
 #: engines/m4/metaengine.cpp:37 engines/mads/metaengine.cpp:116
 #: engines/nancy/metaengine.cpp:104 engines/neverhood/dialogs.cpp:47
-#: engines/pelrock/metaengine.cpp:35 engines/phoenixvr/metaengine.cpp:38
+#: engines/pelrock/metaengine.cpp:35 engines/phoenixvr/metaengine.cpp:47
 #: engines/sci/detection_options.h:105 engines/sherlock/metaengine.cpp:47
 #: engines/toltecs/metaengine.cpp:44 engines/trecision/metaengine.cpp:41
 #: engines/ultima/metaengine.cpp:56 engines/waynesworld/metaengine.cpp:37
@@ -6489,7 +6489,7 @@ msgstr "???????????? ?????????? ?????????? / ?????????????? ??????????????"
 #: engines/got/metaengine.cpp:36 engines/groovie/metaengine.cpp:54
 #: engines/m4/metaengine.cpp:38 engines/mads/metaengine.cpp:117
 #: engines/nancy/metaengine.cpp:105 engines/neverhood/dialogs.cpp:48
-#: engines/pelrock/metaengine.cpp:36 engines/phoenixvr/metaengine.cpp:39
+#: engines/pelrock/metaengine.cpp:36 engines/phoenixvr/metaengine.cpp:48
 #: engines/sci/detection_options.h:106 engines/sherlock/metaengine.cpp:48
 #: engines/toltecs/metaengine.cpp:45 engines/trecision/metaengine.cpp:42
 #: engines/ultima/metaengine.cpp:57 engines/waynesworld/metaengine.cpp:38
@@ -7117,7 +7117,7 @@ msgid "Show version"
 msgstr "?????? ??????????????"
 
 #: engines/asylum/metaengine.cpp:120 engines/crab/input/input.cpp:271
-#: engines/phoenixvr/metaengine.cpp:96 engines/queen/metaengine.cpp:253
+#: engines/phoenixvr/metaengine.cpp:105 engines/queen/metaengine.cpp:253
 #: engines/sludge/keymapper_tables.h:308 engines/sludge/keymapper_tables.h:404
 #: engines/sludge/keymapper_tables.h:471 engines/sludge/keymapper_tables.h:502
 #: engines/ultima/nuvie/metaengine.cpp:92
@@ -7126,7 +7126,7 @@ msgid "Quick load"
 msgstr "?????????? ????????"
 
 #: engines/asylum/metaengine.cpp:125 engines/crab/input/input.cpp:266
-#: engines/phoenixvr/metaengine.cpp:91 engines/queen/metaengine.cpp:247
+#: engines/phoenixvr/metaengine.cpp:100 engines/queen/metaengine.cpp:247
 #: engines/sludge/keymapper_tables.h:64 engines/sludge/keymapper_tables.h:303
 #: engines/sludge/keymapper_tables.h:410 engines/sludge/keymapper_tables.h:465
 #: engines/sludge/keymapper_tables.h:496 engines/ultima/nuvie/metaengine.cpp:91
@@ -7458,7 +7458,7 @@ msgstr "?????????? ????????????"
 #. I18N: This keymap allows skipping video cutscenes
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
-#: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
+#: engines/phoenixvr/metaengine.cpp:94 engines/scumm/help.cpp:79
 #: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
@@ -7654,7 +7654,7 @@ msgstr "?????????? ???????????? ????????????"
 #: engines/freescape/games/dark/dark.cpp:236
 #: engines/freescape/games/driller/driller.cpp:129
 #: engines/freescape/games/eclipse/eclipse.cpp:340
-#: engines/mads/metaengine.cpp:403 engines/phoenixvr/metaengine.cpp:80
+#: engines/mads/metaengine.cpp:403 engines/phoenixvr/metaengine.cpp:89
 #: engines/saga/metaengine.cpp:437 engines/sherlock/metaengine.cpp:359
 #: engines/sherlock/metaengine.cpp:618 engines/sherlock/metaengine.cpp:852
 #: engines/stark/metaengine.cpp:280 engines/sword1/metaengine.cpp:295
@@ -8392,7 +8392,7 @@ msgstr ""
 
 #. I18N: Perform action on object where cursor points
 #: engines/darkseed/metaengine.cpp:42 engines/dragons/metaengine.cpp:135
-#: engines/phoenixvr/metaengine.cpp:66 engines/tinsel/metaengine.cpp:294
+#: engines/phoenixvr/metaengine.cpp:75 engines/tinsel/metaengine.cpp:294
 #: engines/twp/metaengine.cpp:150
 msgid "Action"
 msgstr "??????"
@@ -12438,7 +12438,7 @@ msgstr "?????????? / ?????????? ???????? ????????????"
 msgid "Close current interface"
 msgstr "?????????? ???????? ????????????"
 
-#: engines/phoenixvr/metaengine.cpp:107
+#: engines/phoenixvr/metaengine.cpp:116
 #: engines/wintermute/keymapper_tables.h:239
 #: engines/wintermute/keymapper_tables.h:262
 #: engines/wintermute/keymapper_tables.h:313
diff --git a/po/be-tarask.po b/po/be-tarask.po
index 4601ee5b6f4..f1dd85b0479 100644
--- a/po/be-tarask.po
+++ b/po/be-tarask.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-11 13:12+0000\n"
+"POT-Creation-Date: 2026-06-11 22:10+0000\n"
 "PO-Revision-Date: 2023-02-26 15:01+0000\n"
 "Last-Translator: Kastu?? K. <oakum2334 at gmail.com>\n"
 "Language-Team: Belarusian (Tara??kievica) <https://translations.scummvm.org/"
@@ -5867,7 +5867,7 @@ msgstr "R2"
 #: backends/platform/sdl/miyoo/miyoo.cpp:114
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
-#: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
+#: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:110
 #: engines/scumm/metaengine.cpp:1232 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "????????"
@@ -6259,7 +6259,7 @@ msgstr "????????????"
 #: engines/grim/grim.cpp:621 engines/hdb/metaengine.cpp:251
 #: engines/hpl1/metaengine.cpp:94 engines/icb/icb.cpp:101
 #: engines/icb/icb.cpp:179 engines/petka/metaengine.cpp:173
-#: engines/phoenixvr/metaengine.cpp:73 engines/scumm/help.cpp:228
+#: engines/phoenixvr/metaengine.cpp:82 engines/scumm/help.cpp:228
 #: engines/scumm/help.cpp:237 engines/scumm/help.cpp:244
 #: engines/scumm/help.cpp:252 engines/sherlock/metaengine.cpp:435
 #: engines/sludge/keymapper_tables.h:58 engines/sludge/keymapper_tables.h:119
@@ -6407,7 +6407,7 @@ msgstr ""
 #: engines/got/metaengine.cpp:35 engines/groovie/metaengine.cpp:53
 #: engines/m4/metaengine.cpp:37 engines/mads/metaengine.cpp:116
 #: engines/nancy/metaengine.cpp:104 engines/neverhood/dialogs.cpp:47
-#: engines/pelrock/metaengine.cpp:35 engines/phoenixvr/metaengine.cpp:38
+#: engines/pelrock/metaengine.cpp:35 engines/phoenixvr/metaengine.cpp:47
 #: engines/sci/detection_options.h:105 engines/sherlock/metaengine.cpp:47
 #: engines/toltecs/metaengine.cpp:44 engines/trecision/metaengine.cpp:41
 #: engines/ultima/metaengine.cpp:56 engines/waynesworld/metaengine.cpp:37
@@ -6422,7 +6422,7 @@ msgstr "???????????????????????????? ???????????????????????? ???????????? ????
 #: engines/got/metaengine.cpp:36 engines/groovie/metaengine.cpp:54
 #: engines/m4/metaengine.cpp:38 engines/mads/metaengine.cpp:117
 #: engines/nancy/metaengine.cpp:105 engines/neverhood/dialogs.cpp:48
-#: engines/pelrock/metaengine.cpp:36 engines/phoenixvr/metaengine.cpp:39
+#: engines/pelrock/metaengine.cpp:36 engines/phoenixvr/metaengine.cpp:48
 #: engines/sci/detection_options.h:106 engines/sherlock/metaengine.cpp:48
 #: engines/toltecs/metaengine.cpp:45 engines/trecision/metaengine.cpp:42
 #: engines/ultima/metaengine.cpp:57 engines/waynesworld/metaengine.cpp:38
@@ -7057,7 +7057,7 @@ msgid "Show version"
 msgstr "?????????????????? ????????????"
 
 #: engines/asylum/metaengine.cpp:120 engines/crab/input/input.cpp:271
-#: engines/phoenixvr/metaengine.cpp:96 engines/queen/metaengine.cpp:253
+#: engines/phoenixvr/metaengine.cpp:105 engines/queen/metaengine.cpp:253
 #: engines/sludge/keymapper_tables.h:308 engines/sludge/keymapper_tables.h:404
 #: engines/sludge/keymapper_tables.h:471 engines/sludge/keymapper_tables.h:502
 #: engines/ultima/nuvie/metaengine.cpp:92
@@ -7066,7 +7066,7 @@ msgid "Quick load"
 msgstr "???????????? ????????????????????????"
 
 #: engines/asylum/metaengine.cpp:125 engines/crab/input/input.cpp:266
-#: engines/phoenixvr/metaengine.cpp:91 engines/queen/metaengine.cpp:247
+#: engines/phoenixvr/metaengine.cpp:100 engines/queen/metaengine.cpp:247
 #: engines/sludge/keymapper_tables.h:64 engines/sludge/keymapper_tables.h:303
 #: engines/sludge/keymapper_tables.h:410 engines/sludge/keymapper_tables.h:465
 #: engines/sludge/keymapper_tables.h:496 engines/ultima/nuvie/metaengine.cpp:91
@@ -7400,7 +7400,7 @@ msgstr "???????????????????? ?????????? ??????"
 #. I18N: This keymap allows skipping video cutscenes
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
-#: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
+#: engines/phoenixvr/metaengine.cpp:94 engines/scumm/help.cpp:79
 #: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
@@ -7595,7 +7595,7 @@ msgstr "???????????????? ??????????????"
 #: engines/freescape/games/dark/dark.cpp:236
 #: engines/freescape/games/driller/driller.cpp:129
 #: engines/freescape/games/eclipse/eclipse.cpp:340
-#: engines/mads/metaengine.cpp:403 engines/phoenixvr/metaengine.cpp:80
+#: engines/mads/metaengine.cpp:403 engines/phoenixvr/metaengine.cpp:89
 #: engines/saga/metaengine.cpp:437 engines/sherlock/metaengine.cpp:359
 #: engines/sherlock/metaengine.cpp:618 engines/sherlock/metaengine.cpp:852
 #: engines/stark/metaengine.cpp:280 engines/sword1/metaengine.cpp:295
@@ -8341,7 +8341,7 @@ msgstr ""
 
 #. I18N: Perform action on object where cursor points
 #: engines/darkseed/metaengine.cpp:42 engines/dragons/metaengine.cpp:135
-#: engines/phoenixvr/metaengine.cpp:66 engines/tinsel/metaengine.cpp:294
+#: engines/phoenixvr/metaengine.cpp:75 engines/tinsel/metaengine.cpp:294
 #: engines/twp/metaengine.cpp:150
 msgid "Action"
 msgstr "????????"
@@ -12386,7 +12386,7 @@ msgstr "???????????????? / ???????????? ??????????????"
 msgid "Close current interface"
 msgstr "???????????? ?????????????????? ????????"
 
-#: engines/phoenixvr/metaengine.cpp:107
+#: engines/phoenixvr/metaengine.cpp:116
 #: engines/wintermute/keymapper_tables.h:239
 #: engines/wintermute/keymapper_tables.h:262
 #: engines/wintermute/keymapper_tables.h:313
diff --git a/po/be_BY.po b/po/be_BY.po
index fb8dc4e9b6f..c2155d758f9 100644
--- a/po/be_BY.po
+++ b/po/be_BY.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-11 13:12+0000\n"
+"POT-Creation-Date: 2026-06-11 22:10+0000\n"
 "PO-Revision-Date: 2021-03-21 08:24+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Belarusian <https://translations.scummvm.org/projects/scummvm/"
@@ -5876,7 +5876,7 @@ msgstr "R2"
 #: backends/platform/sdl/miyoo/miyoo.cpp:114
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
-#: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
+#: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:110
 #: engines/scumm/metaengine.cpp:1232 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "????????"
@@ -6268,7 +6268,7 @@ msgstr "??????????????"
 #: engines/grim/grim.cpp:621 engines/hdb/metaengine.cpp:251
 #: engines/hpl1/metaengine.cpp:94 engines/icb/icb.cpp:101
 #: engines/icb/icb.cpp:179 engines/petka/metaengine.cpp:173
-#: engines/phoenixvr/metaengine.cpp:73 engines/scumm/help.cpp:228
+#: engines/phoenixvr/metaengine.cpp:82 engines/scumm/help.cpp:228
 #: engines/scumm/help.cpp:237 engines/scumm/help.cpp:244
 #: engines/scumm/help.cpp:252 engines/sherlock/metaengine.cpp:435
 #: engines/sludge/keymapper_tables.h:58 engines/sludge/keymapper_tables.h:119
@@ -6415,7 +6415,7 @@ msgstr ""
 #: engines/got/metaengine.cpp:35 engines/groovie/metaengine.cpp:53
 #: engines/m4/metaengine.cpp:37 engines/mads/metaengine.cpp:116
 #: engines/nancy/metaengine.cpp:104 engines/neverhood/dialogs.cpp:47
-#: engines/pelrock/metaengine.cpp:35 engines/phoenixvr/metaengine.cpp:38
+#: engines/pelrock/metaengine.cpp:35 engines/phoenixvr/metaengine.cpp:47
 #: engines/sci/detection_options.h:105 engines/sherlock/metaengine.cpp:47
 #: engines/toltecs/metaengine.cpp:44 engines/trecision/metaengine.cpp:41
 #: engines/ultima/metaengine.cpp:56 engines/waynesworld/metaengine.cpp:37
@@ -6430,7 +6430,7 @@ msgstr "???????????????????????????? ???????????????????????? ???????????? ????
 #: engines/got/metaengine.cpp:36 engines/groovie/metaengine.cpp:54
 #: engines/m4/metaengine.cpp:38 engines/mads/metaengine.cpp:117
 #: engines/nancy/metaengine.cpp:105 engines/neverhood/dialogs.cpp:48
-#: engines/pelrock/metaengine.cpp:36 engines/phoenixvr/metaengine.cpp:39
+#: engines/pelrock/metaengine.cpp:36 engines/phoenixvr/metaengine.cpp:48
 #: engines/sci/detection_options.h:106 engines/sherlock/metaengine.cpp:48
 #: engines/toltecs/metaengine.cpp:45 engines/trecision/metaengine.cpp:42
 #: engines/ultima/metaengine.cpp:57 engines/waynesworld/metaengine.cpp:38
@@ -7037,7 +7037,7 @@ msgid "Show version"
 msgstr "???????????????? ??????????????????"
 
 #: engines/asylum/metaengine.cpp:120 engines/crab/input/input.cpp:271
-#: engines/phoenixvr/metaengine.cpp:96 engines/queen/metaengine.cpp:253
+#: engines/phoenixvr/metaengine.cpp:105 engines/queen/metaengine.cpp:253
 #: engines/sludge/keymapper_tables.h:308 engines/sludge/keymapper_tables.h:404
 #: engines/sludge/keymapper_tables.h:471 engines/sludge/keymapper_tables.h:502
 #: engines/ultima/nuvie/metaengine.cpp:92
@@ -7046,7 +7046,7 @@ msgid "Quick load"
 msgstr "???????????? ????????????????"
 
 #: engines/asylum/metaengine.cpp:125 engines/crab/input/input.cpp:266
-#: engines/phoenixvr/metaengine.cpp:91 engines/queen/metaengine.cpp:247
+#: engines/phoenixvr/metaengine.cpp:100 engines/queen/metaengine.cpp:247
 #: engines/sludge/keymapper_tables.h:64 engines/sludge/keymapper_tables.h:303
 #: engines/sludge/keymapper_tables.h:410 engines/sludge/keymapper_tables.h:465
 #: engines/sludge/keymapper_tables.h:496 engines/ultima/nuvie/metaengine.cpp:91
@@ -7381,7 +7381,7 @@ msgstr "?????????????????????? ??????"
 #. I18N: This keymap allows skipping video cutscenes
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
-#: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
+#: engines/phoenixvr/metaengine.cpp:94 engines/scumm/help.cpp:79
 #: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
@@ -7566,7 +7566,7 @@ msgstr ""
 #: engines/freescape/games/dark/dark.cpp:236
 #: engines/freescape/games/driller/driller.cpp:129
 #: engines/freescape/games/eclipse/eclipse.cpp:340
-#: engines/mads/metaengine.cpp:403 engines/phoenixvr/metaengine.cpp:80
+#: engines/mads/metaengine.cpp:403 engines/phoenixvr/metaengine.cpp:89
 #: engines/saga/metaengine.cpp:437 engines/sherlock/metaengine.cpp:359
 #: engines/sherlock/metaengine.cpp:618 engines/sherlock/metaengine.cpp:852
 #: engines/stark/metaengine.cpp:280 engines/sword1/metaengine.cpp:295
@@ -8304,7 +8304,7 @@ msgstr ""
 
 #. I18N: Perform action on object where cursor points
 #: engines/darkseed/metaengine.cpp:42 engines/dragons/metaengine.cpp:135
-#: engines/phoenixvr/metaengine.cpp:66 engines/tinsel/metaengine.cpp:294
+#: engines/phoenixvr/metaengine.cpp:75 engines/tinsel/metaengine.cpp:294
 #: engines/twp/metaengine.cpp:150
 msgid "Action"
 msgstr "??????????????"
@@ -12237,7 +12237,7 @@ msgstr "???????????????? / ???????????????? ??????????????"
 msgid "Close current interface"
 msgstr "???????????? ?????????????????? ????????"
 
-#: engines/phoenixvr/metaengine.cpp:107
+#: engines/phoenixvr/metaengine.cpp:116
 #: engines/wintermute/keymapper_tables.h:239
 #: engines/wintermute/keymapper_tables.h:262
 #: engines/wintermute/keymapper_tables.h:313
diff --git a/po/ca_ES.po b/po/ca_ES.po
index 8441b3851e7..8693317969b 100644
--- a/po/ca_ES.po
+++ b/po/ca_ES.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.6.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-11 13:12+0000\n"
+"POT-Creation-Date: 2026-06-11 22:10+0000\n"
 "PO-Revision-Date: 2026-05-26 04:22+0000\n"
 "Last-Translator: Xaviu <xaviu at xaviu.com>\n"
 "Language-Team: Catalan <https://translations.scummvm.org/projects/scummvm/"
@@ -5678,7 +5678,7 @@ msgstr "R2"
 #: backends/platform/sdl/miyoo/miyoo.cpp:114
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
-#: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
+#: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:110
 #: engines/scumm/metaengine.cpp:1232 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "Men??"
@@ -6054,7 +6054,7 @@ msgstr "Agafa"
 #: engines/grim/grim.cpp:621 engines/hdb/metaengine.cpp:251
 #: engines/hpl1/metaengine.cpp:94 engines/icb/icb.cpp:101
 #: engines/icb/icb.cpp:179 engines/petka/metaengine.cpp:173
-#: engines/phoenixvr/metaengine.cpp:73 engines/scumm/help.cpp:228
+#: engines/phoenixvr/metaengine.cpp:82 engines/scumm/help.cpp:228
 #: engines/scumm/help.cpp:237 engines/scumm/help.cpp:244
 #: engines/scumm/help.cpp:252 engines/sherlock/metaengine.cpp:435
 #: engines/sludge/keymapper_tables.h:58 engines/sludge/keymapper_tables.h:119
@@ -6200,7 +6200,7 @@ msgstr ""
 #: engines/got/metaengine.cpp:35 engines/groovie/metaengine.cpp:53
 #: engines/m4/metaengine.cpp:37 engines/mads/metaengine.cpp:116
 #: engines/nancy/metaengine.cpp:104 engines/neverhood/dialogs.cpp:47
-#: engines/pelrock/metaengine.cpp:35 engines/phoenixvr/metaengine.cpp:38
+#: engines/pelrock/metaengine.cpp:35 engines/phoenixvr/metaengine.cpp:47
 #: engines/sci/detection_options.h:105 engines/sherlock/metaengine.cpp:47
 #: engines/toltecs/metaengine.cpp:44 engines/trecision/metaengine.cpp:41
 #: engines/ultima/metaengine.cpp:56 engines/waynesworld/metaengine.cpp:37
@@ -6215,7 +6215,7 @@ msgstr "Utilitza les pantalles originals de desat/c??rrega"
 #: engines/got/metaengine.cpp:36 engines/groovie/metaengine.cpp:54
 #: engines/m4/metaengine.cpp:38 engines/mads/metaengine.cpp:117
 #: engines/nancy/metaengine.cpp:105 engines/neverhood/dialogs.cpp:48
-#: engines/pelrock/metaengine.cpp:36 engines/phoenixvr/metaengine.cpp:39
+#: engines/pelrock/metaengine.cpp:36 engines/phoenixvr/metaengine.cpp:48
 #: engines/sci/detection_options.h:106 engines/sherlock/metaengine.cpp:48
 #: engines/toltecs/metaengine.cpp:45 engines/trecision/metaengine.cpp:42
 #: engines/ultima/metaengine.cpp:57 engines/waynesworld/metaengine.cpp:38
@@ -6820,7 +6820,7 @@ msgid "Show version"
 msgstr "Mostra la versi??"
 
 #: engines/asylum/metaengine.cpp:120 engines/crab/input/input.cpp:271
-#: engines/phoenixvr/metaengine.cpp:96 engines/queen/metaengine.cpp:253
+#: engines/phoenixvr/metaengine.cpp:105 engines/queen/metaengine.cpp:253
 #: engines/sludge/keymapper_tables.h:308 engines/sludge/keymapper_tables.h:404
 #: engines/sludge/keymapper_tables.h:471 engines/sludge/keymapper_tables.h:502
 #: engines/ultima/nuvie/metaengine.cpp:92
@@ -6829,7 +6829,7 @@ msgid "Quick load"
 msgstr "C??rrega r??pida"
 
 #: engines/asylum/metaengine.cpp:125 engines/crab/input/input.cpp:266
-#: engines/phoenixvr/metaengine.cpp:91 engines/queen/metaengine.cpp:247
+#: engines/phoenixvr/metaengine.cpp:100 engines/queen/metaengine.cpp:247
 #: engines/sludge/keymapper_tables.h:64 engines/sludge/keymapper_tables.h:303
 #: engines/sludge/keymapper_tables.h:410 engines/sludge/keymapper_tables.h:465
 #: engines/sludge/keymapper_tables.h:496 engines/ultima/nuvie/metaengine.cpp:91
@@ -7143,7 +7143,7 @@ msgstr "Activa / desactiva el combat"
 #. I18N: This keymap allows skipping video cutscenes
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
-#: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
+#: engines/phoenixvr/metaengine.cpp:94 engines/scumm/help.cpp:79
 #: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
@@ -7323,7 +7323,7 @@ msgstr "Base de dades de pistes"
 #: engines/freescape/games/dark/dark.cpp:236
 #: engines/freescape/games/driller/driller.cpp:129
 #: engines/freescape/games/eclipse/eclipse.cpp:340
-#: engines/mads/metaengine.cpp:403 engines/phoenixvr/metaengine.cpp:80
+#: engines/mads/metaengine.cpp:403 engines/phoenixvr/metaengine.cpp:89
 #: engines/saga/metaengine.cpp:437 engines/sherlock/metaengine.cpp:359
 #: engines/sherlock/metaengine.cpp:618 engines/sherlock/metaengine.cpp:852
 #: engines/stark/metaengine.cpp:280 engines/sword1/metaengine.cpp:295
@@ -7967,7 +7967,7 @@ msgstr "Nom??s efectes de so de la versi?? de disquet"
 
 #. I18N: Perform action on object where cursor points
 #: engines/darkseed/metaengine.cpp:42 engines/dragons/metaengine.cpp:135
-#: engines/phoenixvr/metaengine.cpp:66 engines/tinsel/metaengine.cpp:294
+#: engines/phoenixvr/metaengine.cpp:75 engines/tinsel/metaengine.cpp:294
 #: engines/twp/metaengine.cpp:150
 msgid "Action"
 msgstr "Acci??"
@@ -11453,7 +11453,7 @@ msgstr "Mostra / Amaga la pantalla d'opcions"
 msgid "Close current interface"
 msgstr "Tanca la interf??cie actual"
 
-#: engines/phoenixvr/metaengine.cpp:107
+#: engines/phoenixvr/metaengine.cpp:116
 #: engines/wintermute/keymapper_tables.h:239
 #: engines/wintermute/keymapper_tables.h:262
 #: engines/wintermute/keymapper_tables.h:313
diff --git a/po/cs_CZ.po b/po/cs_CZ.po
index 95d0e162ff3..0e57bb3ab64 100644
--- a/po/cs_CZ.po
+++ b/po/cs_CZ.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.7.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-11 13:12+0000\n"
+"POT-Creation-Date: 2026-06-11 22:10+0000\n"
 "PO-Revision-Date: 2026-02-09 20:22+0000\n"
 "Last-Translator: AsciiWolf <mail at asciiwolf.com>\n"
 "Language-Team: Czech <https://translations.scummvm.org/projects/scummvm/"
@@ -5844,7 +5844,7 @@ msgstr ""
 #: backends/platform/sdl/miyoo/miyoo.cpp:114
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
-#: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
+#: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:110
 #: engines/scumm/metaengine.cpp:1232 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "Menu"
@@ -6236,7 +6236,7 @@ msgstr "Svl??ct"
 #: engines/grim/grim.cpp:621 engines/hdb/metaengine.cpp:251
 #: engines/hpl1/metaengine.cpp:94 engines/icb/icb.cpp:101
 #: engines/icb/icb.cpp:179 engines/petka/metaengine.cpp:173
-#: engines/phoenixvr/metaengine.cpp:73 engines/scumm/help.cpp:228
+#: engines/phoenixvr/metaengine.cpp:82 engines/scumm/help.cpp:228
 #: engines/scumm/help.cpp:237 engines/scumm/help.cpp:244
 #: engines/scumm/help.cpp:252 engines/sherlock/metaengine.cpp:435
 #: engines/sludge/keymapper_tables.h:58 engines/sludge/keymapper_tables.h:119
@@ -6384,7 +6384,7 @@ msgstr ""
 #: engines/got/metaengine.cpp:35 engines/groovie/metaengine.cpp:53
 #: engines/m4/metaengine.cpp:37 engines/mads/metaengine.cpp:116
 #: engines/nancy/metaengine.cpp:104 engines/neverhood/dialogs.cpp:47
-#: engines/pelrock/metaengine.cpp:35 engines/phoenixvr/metaengine.cpp:38
+#: engines/pelrock/metaengine.cpp:35 engines/phoenixvr/metaengine.cpp:47
 #: engines/sci/detection_options.h:105 engines/sherlock/metaengine.cpp:47
 #: engines/toltecs/metaengine.cpp:44 engines/trecision/metaengine.cpp:41
 #: engines/ultima/metaengine.cpp:56 engines/waynesworld/metaengine.cpp:37
@@ -6399,7 +6399,7 @@ msgstr "Pou????t p??vodn?? obrazovky na??ten??/ulo??en??"
 #: engines/got/metaengine.cpp:36 engines/groovie/metaengine.cpp:54
 #: engines/m4/metaengine.cpp:38 engines/mads/metaengine.cpp:117
 #: engines/nancy/metaengine.cpp:105 engines/neverhood/dialogs.cpp:48
-#: engines/pelrock/metaengine.cpp:36 engines/phoenixvr/metaengine.cpp:39
+#: engines/pelrock/metaengine.cpp:36 engines/phoenixvr/metaengine.cpp:48
 #: engines/sci/detection_options.h:106 engines/sherlock/metaengine.cpp:48
 #: engines/toltecs/metaengine.cpp:45 engines/trecision/metaengine.cpp:42
 #: engines/ultima/metaengine.cpp:57 engines/waynesworld/metaengine.cpp:38
@@ -7000,7 +7000,7 @@ msgid "Show version"
 msgstr "Zobrazit ????dek objekt??"
 
 #: engines/asylum/metaengine.cpp:120 engines/crab/input/input.cpp:271
-#: engines/phoenixvr/metaengine.cpp:96 engines/queen/metaengine.cpp:253
+#: engines/phoenixvr/metaengine.cpp:105 engines/queen/metaengine.cpp:253
 #: engines/sludge/keymapper_tables.h:308 engines/sludge/keymapper_tables.h:404
 #: engines/sludge/keymapper_tables.h:471 engines/sludge/keymapper_tables.h:502
 #: engines/ultima/nuvie/metaengine.cpp:92
@@ -7010,7 +7010,7 @@ msgid "Quick load"
 msgstr "Re??im kliknut??"
 
 #: engines/asylum/metaengine.cpp:125 engines/crab/input/input.cpp:266
-#: engines/phoenixvr/metaengine.cpp:91 engines/queen/metaengine.cpp:247
+#: engines/phoenixvr/metaengine.cpp:100 engines/queen/metaengine.cpp:247
 #: engines/sludge/keymapper_tables.h:64 engines/sludge/keymapper_tables.h:303
 #: engines/sludge/keymapper_tables.h:410 engines/sludge/keymapper_tables.h:465
 #: engines/sludge/keymapper_tables.h:496 engines/ultima/nuvie/metaengine.cpp:91
@@ -7335,7 +7335,7 @@ msgstr "Povolit zachycov??n?? my??i"
 #. I18N: This keymap allows skipping video cutscenes
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
-#: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
+#: engines/phoenixvr/metaengine.cpp:94 engines/scumm/help.cpp:79
 #: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
@@ -7522,7 +7522,7 @@ msgstr ""
 #: engines/freescape/games/dark/dark.cpp:236
 #: engines/freescape/games/driller/driller.cpp:129
 #: engines/freescape/games/eclipse/eclipse.cpp:340
-#: engines/mads/metaengine.cpp:403 engines/phoenixvr/metaengine.cpp:80
+#: engines/mads/metaengine.cpp:403 engines/phoenixvr/metaengine.cpp:89
 #: engines/saga/metaengine.cpp:437 engines/sherlock/metaengine.cpp:359
 #: engines/sherlock/metaengine.cpp:618 engines/sherlock/metaengine.cpp:852
 #: engines/stark/metaengine.cpp:280 engines/sword1/metaengine.cpp:295
@@ -8249,7 +8249,7 @@ msgstr ""
 
 #. I18N: Perform action on object where cursor points
 #: engines/darkseed/metaengine.cpp:42 engines/dragons/metaengine.cpp:135
-#: engines/phoenixvr/metaengine.cpp:66 engines/tinsel/metaengine.cpp:294
+#: engines/phoenixvr/metaengine.cpp:75 engines/tinsel/metaengine.cpp:294
 #: engines/twp/metaengine.cpp:150
 #, fuzzy
 msgid "Action"
@@ -12078,7 +12078,7 @@ msgstr "Uk??zat / Skr??t konzoli"
 msgid "Close current interface"
 msgstr "Jednoduch?? rozhran?? my??i"
 
-#: engines/phoenixvr/metaengine.cpp:107
+#: engines/phoenixvr/metaengine.cpp:116
 #: engines/wintermute/keymapper_tables.h:239
 #: engines/wintermute/keymapper_tables.h:262
 #: engines/wintermute/keymapper_tables.h:313
diff --git a/po/da.po b/po/da.po
index 2fc8c40a15c..0a4974d3fb7 100644
--- a/po/da.po
+++ b/po/da.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-11 13:12+0000\n"
+"POT-Creation-Date: 2026-06-11 22:10+0000\n"
 "PO-Revision-Date: 2023-09-07 17:04+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Danish <https://translations.scummvm.org/projects/scummvm/"
@@ -5863,7 +5863,7 @@ msgstr ""
 #: backends/platform/sdl/miyoo/miyoo.cpp:114
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
-#: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
+#: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:110
 #: engines/scumm/metaengine.cpp:1232 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "Menu"
@@ -6254,7 +6254,7 @@ msgstr "Tag af"
 #: engines/grim/grim.cpp:621 engines/hdb/metaengine.cpp:251
 #: engines/hpl1/metaengine.cpp:94 engines/icb/icb.cpp:101
 #: engines/icb/icb.cpp:179 engines/petka/metaengine.cpp:173
-#: engines/phoenixvr/metaengine.cpp:73 engines/scumm/help.cpp:228
+#: engines/phoenixvr/metaengine.cpp:82 engines/scumm/help.cpp:228
 #: engines/scumm/help.cpp:237 engines/scumm/help.cpp:244
 #: engines/scumm/help.cpp:252 engines/sherlock/metaengine.cpp:435
 #: engines/sludge/keymapper_tables.h:58 engines/sludge/keymapper_tables.h:119
@@ -6400,7 +6400,7 @@ msgstr ""
 #: engines/got/metaengine.cpp:35 engines/groovie/metaengine.cpp:53
 #: engines/m4/metaengine.cpp:37 engines/mads/metaengine.cpp:116
 #: engines/nancy/metaengine.cpp:104 engines/neverhood/dialogs.cpp:47
-#: engines/pelrock/metaengine.cpp:35 engines/phoenixvr/metaengine.cpp:38
+#: engines/pelrock/metaengine.cpp:35 engines/phoenixvr/metaengine.cpp:47
 #: engines/sci/detection_options.h:105 engines/sherlock/metaengine.cpp:47
 #: engines/toltecs/metaengine.cpp:44 engines/trecision/metaengine.cpp:41
 #: engines/ultima/metaengine.cpp:56 engines/waynesworld/metaengine.cpp:37
@@ -6415,7 +6415,7 @@ msgstr "Brug original gem/indl??s sk??rme"
 #: engines/got/metaengine.cpp:36 engines/groovie/metaengine.cpp:54
 #: engines/m4/metaengine.cpp:38 engines/mads/metaengine.cpp:117
 #: engines/nancy/metaengine.cpp:105 engines/neverhood/dialogs.cpp:48
-#: engines/pelrock/metaengine.cpp:36 engines/phoenixvr/metaengine.cpp:39
+#: engines/pelrock/metaengine.cpp:36 engines/phoenixvr/metaengine.cpp:48
 #: engines/sci/detection_options.h:106 engines/sherlock/metaengine.cpp:48
 #: engines/toltecs/metaengine.cpp:45 engines/trecision/metaengine.cpp:42
 #: engines/ultima/metaengine.cpp:57 engines/waynesworld/metaengine.cpp:38
@@ -7019,7 +7019,7 @@ msgid "Show version"
 msgstr "Vis objektlinje"
 
 #: engines/asylum/metaengine.cpp:120 engines/crab/input/input.cpp:271
-#: engines/phoenixvr/metaengine.cpp:96 engines/queen/metaengine.cpp:253
+#: engines/phoenixvr/metaengine.cpp:105 engines/queen/metaengine.cpp:253
 #: engines/sludge/keymapper_tables.h:308 engines/sludge/keymapper_tables.h:404
 #: engines/sludge/keymapper_tables.h:471 engines/sludge/keymapper_tables.h:502
 #: engines/ultima/nuvie/metaengine.cpp:92
@@ -7029,7 +7029,7 @@ msgid "Quick load"
 msgstr "Kliktilstand"
 
 #: engines/asylum/metaengine.cpp:125 engines/crab/input/input.cpp:266
-#: engines/phoenixvr/metaengine.cpp:91 engines/queen/metaengine.cpp:247
+#: engines/phoenixvr/metaengine.cpp:100 engines/queen/metaengine.cpp:247
 #: engines/sludge/keymapper_tables.h:64 engines/sludge/keymapper_tables.h:303
 #: engines/sludge/keymapper_tables.h:410 engines/sludge/keymapper_tables.h:465
 #: engines/sludge/keymapper_tables.h:496 engines/ultima/nuvie/metaengine.cpp:91
@@ -7362,7 +7362,7 @@ msgstr "Museindfangning til/fra"
 #. I18N: This keymap allows skipping video cutscenes
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
-#: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
+#: engines/phoenixvr/metaengine.cpp:94 engines/scumm/help.cpp:79
 #: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
@@ -7550,7 +7550,7 @@ msgstr ""
 #: engines/freescape/games/dark/dark.cpp:236
 #: engines/freescape/games/driller/driller.cpp:129
 #: engines/freescape/games/eclipse/eclipse.cpp:340
-#: engines/mads/metaengine.cpp:403 engines/phoenixvr/metaengine.cpp:80
+#: engines/mads/metaengine.cpp:403 engines/phoenixvr/metaengine.cpp:89
 #: engines/saga/metaengine.cpp:437 engines/sherlock/metaengine.cpp:359
 #: engines/sherlock/metaengine.cpp:618 engines/sherlock/metaengine.cpp:852
 #: engines/stark/metaengine.cpp:280 engines/sword1/metaengine.cpp:295
@@ -8276,7 +8276,7 @@ msgstr ""
 
 #. I18N: Perform action on object where cursor points
 #: engines/darkseed/metaengine.cpp:42 engines/dragons/metaengine.cpp:135
-#: engines/phoenixvr/metaengine.cpp:66 engines/tinsel/metaengine.cpp:294
+#: engines/phoenixvr/metaengine.cpp:75 engines/tinsel/metaengine.cpp:294
 #: engines/twp/metaengine.cpp:150
 #, fuzzy
 msgid "Action"
@@ -12146,7 +12146,7 @@ msgstr "Vis/skjul konsol"
 msgid "Close current interface"
 msgstr "Nem musebrugerflade"
 
-#: engines/phoenixvr/metaengine.cpp:107
+#: engines/phoenixvr/metaengine.cpp:116
 #: engines/wintermute/keymapper_tables.h:239
 #: engines/wintermute/keymapper_tables.h:262
 #: engines/wintermute/keymapper_tables.h:313
diff --git a/po/de_DE.po b/po/de_DE.po
index 990686868b1..ed8a61153fb 100644
--- a/po/de_DE.po
+++ b/po/de_DE.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.10.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-11 13:12+0000\n"
+"POT-Creation-Date: 2026-06-11 22:10+0000\n"
 "PO-Revision-Date: 2026-05-05 16:31+0000\n"
 "Last-Translator: Lothar Serra Mari <lothar.serramari at scummvm.org>\n"
 "Language-Team: German <https://translations.scummvm.org/projects/scummvm/"
@@ -6143,7 +6143,7 @@ msgstr "R2"
 #: backends/platform/sdl/miyoo/miyoo.cpp:114
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
-#: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
+#: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:110
 #: engines/scumm/metaengine.cpp:1232 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "Men??"
@@ -6519,7 +6519,7 @@ msgstr "Nehmen"
 #: engines/grim/grim.cpp:621 engines/hdb/metaengine.cpp:251
 #: engines/hpl1/metaengine.cpp:94 engines/icb/icb.cpp:101
 #: engines/icb/icb.cpp:179 engines/petka/metaengine.cpp:173
-#: engines/phoenixvr/metaengine.cpp:73 engines/scumm/help.cpp:228
+#: engines/phoenixvr/metaengine.cpp:82 engines/scumm/help.cpp:228
 #: engines/scumm/help.cpp:237 engines/scumm/help.cpp:244
 #: engines/scumm/help.cpp:252 engines/sherlock/metaengine.cpp:435
 #: engines/sludge/keymapper_tables.h:58 engines/sludge/keymapper_tables.h:119
@@ -6667,7 +6667,7 @@ msgstr ""
 #: engines/got/metaengine.cpp:35 engines/groovie/metaengine.cpp:53
 #: engines/m4/metaengine.cpp:37 engines/mads/metaengine.cpp:116
 #: engines/nancy/metaengine.cpp:104 engines/neverhood/dialogs.cpp:47
-#: engines/pelrock/metaengine.cpp:35 engines/phoenixvr/metaengine.cpp:38
+#: engines/pelrock/metaengine.cpp:35 engines/phoenixvr/metaengine.cpp:47
 #: engines/sci/detection_options.h:105 engines/sherlock/metaengine.cpp:47
 #: engines/toltecs/metaengine.cpp:44 engines/trecision/metaengine.cpp:41
 #: engines/ultima/metaengine.cpp:56 engines/waynesworld/metaengine.cpp:37
@@ -6682,7 +6682,7 @@ msgstr "Originale Spielstand-Men??s"
 #: engines/got/metaengine.cpp:36 engines/groovie/metaengine.cpp:54
 #: engines/m4/metaengine.cpp:38 engines/mads/metaengine.cpp:117
 #: engines/nancy/metaengine.cpp:105 engines/neverhood/dialogs.cpp:48
-#: engines/pelrock/metaengine.cpp:36 engines/phoenixvr/metaengine.cpp:39
+#: engines/pelrock/metaengine.cpp:36 engines/phoenixvr/metaengine.cpp:48
 #: engines/sci/detection_options.h:106 engines/sherlock/metaengine.cpp:48
 #: engines/toltecs/metaengine.cpp:45 engines/trecision/metaengine.cpp:42
 #: engines/ultima/metaengine.cpp:57 engines/waynesworld/metaengine.cpp:38
@@ -7297,7 +7297,7 @@ msgid "Show version"
 msgstr "Version anzeigen"
 
 #: engines/asylum/metaengine.cpp:120 engines/crab/input/input.cpp:271
-#: engines/phoenixvr/metaengine.cpp:96 engines/queen/metaengine.cpp:253
+#: engines/phoenixvr/metaengine.cpp:105 engines/queen/metaengine.cpp:253
 #: engines/sludge/keymapper_tables.h:308 engines/sludge/keymapper_tables.h:404
 #: engines/sludge/keymapper_tables.h:471 engines/sludge/keymapper_tables.h:502
 #: engines/ultima/nuvie/metaengine.cpp:92
@@ -7306,7 +7306,7 @@ msgid "Quick load"
 msgstr "Schnellladen"
 
 #: engines/asylum/metaengine.cpp:125 engines/crab/input/input.cpp:266
-#: engines/phoenixvr/metaengine.cpp:91 engines/queen/metaengine.cpp:247
+#: engines/phoenixvr/metaengine.cpp:100 engines/queen/metaengine.cpp:247
 #: engines/sludge/keymapper_tables.h:64 engines/sludge/keymapper_tables.h:303
 #: engines/sludge/keymapper_tables.h:410 engines/sludge/keymapper_tables.h:465
 #: engines/sludge/keymapper_tables.h:496 engines/ultima/nuvie/metaengine.cpp:91
@@ -7626,7 +7626,7 @@ msgstr "Kampf umschalten"
 #. I18N: This keymap allows skipping video cutscenes
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
-#: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
+#: engines/phoenixvr/metaengine.cpp:94 engines/scumm/help.cpp:79
 #: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
@@ -7806,7 +7806,7 @@ msgstr "Hinweis-Datenbank"
 #: engines/freescape/games/dark/dark.cpp:236
 #: engines/freescape/games/driller/driller.cpp:129
 #: engines/freescape/games/eclipse/eclipse.cpp:340
-#: engines/mads/metaengine.cpp:403 engines/phoenixvr/metaengine.cpp:80
+#: engines/mads/metaengine.cpp:403 engines/phoenixvr/metaengine.cpp:89
 #: engines/saga/metaengine.cpp:437 engines/sherlock/metaengine.cpp:359
 #: engines/sherlock/metaengine.cpp:618 engines/sherlock/metaengine.cpp:852
 #: engines/stark/metaengine.cpp:280 engines/sword1/metaengine.cpp:295
@@ -8449,7 +8449,7 @@ msgstr "Nur SFX-Version f??r Disketten"
 
 #. I18N: Perform action on object where cursor points
 #: engines/darkseed/metaengine.cpp:42 engines/dragons/metaengine.cpp:135
-#: engines/phoenixvr/metaengine.cpp:66 engines/tinsel/metaengine.cpp:294
+#: engines/phoenixvr/metaengine.cpp:75 engines/tinsel/metaengine.cpp:294
 #: engines/twp/metaengine.cpp:150
 msgid "Action"
 msgstr "Aktion"
@@ -12035,7 +12035,7 @@ msgstr "Optionen anzeigen/ausblenden"
 msgid "Close current interface"
 msgstr "Aktuelle Schnittstelle schlie??en"
 
-#: engines/phoenixvr/metaengine.cpp:107
+#: engines/phoenixvr/metaengine.cpp:116
 #: engines/wintermute/keymapper_tables.h:239
 #: engines/wintermute/keymapper_tables.h:262
 #: engines/wintermute/keymapper_tables.h:313
diff --git a/po/el.po b/po/el.po
index f7b5e6e22dc..df9e7486e49 100644
--- a/po/el.po
+++ b/po/el.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.10.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-11 13:12+0000\n"
+"POT-Creation-Date: 2026-06-11 22:10+0000\n"
 "PO-Revision-Date: 2026-04-23 00:22+0000\n"
 "Last-Translator: Antoniou Athanasios <a.antoniou79 at gmail.com>\n"
 "Language-Team: Greek <https://translations.scummvm.org/projects/scummvm/"
@@ -6267,7 +6267,7 @@ msgstr "R2"
 #: backends/platform/sdl/miyoo/miyoo.cpp:114
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
-#: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
+#: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:110
 #: engines/scumm/metaengine.cpp:1232 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "??????????"
@@ -6643,7 +6643,7 @@ msgstr "????????"
 #: engines/grim/grim.cpp:621 engines/hdb/metaengine.cpp:251
 #: engines/hpl1/metaengine.cpp:94 engines/icb/icb.cpp:101
 #: engines/icb/icb.cpp:179 engines/petka/metaengine.cpp:173
-#: engines/phoenixvr/metaengine.cpp:73 engines/scumm/help.cpp:228
+#: engines/phoenixvr/metaengine.cpp:82 engines/scumm/help.cpp:228
 #: engines/scumm/help.cpp:237 engines/scumm/help.cpp:244
 #: engines/scumm/help.cpp:252 engines/sherlock/metaengine.cpp:435
 #: engines/sludge/keymapper_tables.h:58 engines/sludge/keymapper_tables.h:119
@@ -6790,7 +6790,7 @@ msgstr ""
 #: engines/got/metaengine.cpp:35 engines/groovie/metaengine.cpp:53
 #: engines/m4/metaengine.cpp:37 engines/mads/metaengine.cpp:116
 #: engines/nancy/metaengine.cpp:104 engines/neverhood/dialogs.cpp:47
-#: engines/pelrock/metaengine.cpp:35 engines/phoenixvr/metaengine.cpp:38
+#: engines/pelrock/metaengine.cpp:35 engines/phoenixvr/metaengine.cpp:47
 #: engines/sci/detection_options.h:105 engines/sherlock/metaengine.cpp:47
 #: engines/toltecs/metaengine.cpp:44 engines/trecision/metaengine.cpp:41
 #: engines/ultima/metaengine.cpp:56 engines/waynesworld/metaengine.cpp:37
@@ -6805,7 +6805,7 @@ msgstr "?????????? ?????????????? ???????????? ??????????????????????/??????????
 #: engines/got/metaengine.cpp:36 engines/groovie/metaengine.cpp:54
 #: engines/m4/metaengine.cpp:38 engines/mads/metaengine.cpp:117
 #: engines/nancy/metaengine.cpp:105 engines/neverhood/dialogs.cpp:48
-#: engines/pelrock/metaengine.cpp:36 engines/phoenixvr/metaengine.cpp:39
+#: engines/pelrock/metaengine.cpp:36 engines/phoenixvr/metaengine.cpp:48
 #: engines/sci/detection_options.h:106 engines/sherlock/metaengine.cpp:48
 #: engines/toltecs/metaengine.cpp:45 engines/trecision/metaengine.cpp:42
 #: engines/ultima/metaengine.cpp:57 engines/waynesworld/metaengine.cpp:38
@@ -7422,7 +7422,7 @@ msgid "Show version"
 msgstr "?????????????? ??????????????"
 
 #: engines/asylum/metaengine.cpp:120 engines/crab/input/input.cpp:271
-#: engines/phoenixvr/metaengine.cpp:96 engines/queen/metaengine.cpp:253
+#: engines/phoenixvr/metaengine.cpp:105 engines/queen/metaengine.cpp:253
 #: engines/sludge/keymapper_tables.h:308 engines/sludge/keymapper_tables.h:404
 #: engines/sludge/keymapper_tables.h:471 engines/sludge/keymapper_tables.h:502
 #: engines/ultima/nuvie/metaengine.cpp:92
@@ -7431,7 +7431,7 @@ msgid "Quick load"
 msgstr "?????????????? ??????????????"
 
 #: engines/asylum/metaengine.cpp:125 engines/crab/input/input.cpp:266
-#: engines/phoenixvr/metaengine.cpp:91 engines/queen/metaengine.cpp:247
+#: engines/phoenixvr/metaengine.cpp:100 engines/queen/metaengine.cpp:247
 #: engines/sludge/keymapper_tables.h:64 engines/sludge/keymapper_tables.h:303
 #: engines/sludge/keymapper_tables.h:410 engines/sludge/keymapper_tables.h:465
 #: engines/sludge/keymapper_tables.h:496 engines/ultima/nuvie/metaengine.cpp:91
@@ -7754,7 +7754,7 @@ msgstr "???????????????? ?????????????????????? ?????? ????????"
 #. I18N: This keymap allows skipping video cutscenes
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
-#: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
+#: engines/phoenixvr/metaengine.cpp:94 engines/scumm/help.cpp:79
 #: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
@@ -7934,7 +7934,7 @@ msgstr "???????? ?????????????????? ?????????????????? ??????????????"
 #: engines/freescape/games/dark/dark.cpp:236
 #: engines/freescape/games/driller/driller.cpp:129
 #: engines/freescape/games/eclipse/eclipse.cpp:340
-#: engines/mads/metaengine.cpp:403 engines/phoenixvr/metaengine.cpp:80
+#: engines/mads/metaengine.cpp:403 engines/phoenixvr/metaengine.cpp:89
 #: engines/saga/metaengine.cpp:437 engines/sherlock/metaengine.cpp:359
 #: engines/sherlock/metaengine.cpp:618 engines/sherlock/metaengine.cpp:852
 #: engines/stark/metaengine.cpp:280 engines/sword1/metaengine.cpp:295
@@ -8585,7 +8585,7 @@ msgstr "?????? ???????? ?????? ???????????? ?????? ????????????????"
 
 #. I18N: Perform action on object where cursor points
 #: engines/darkseed/metaengine.cpp:42 engines/dragons/metaengine.cpp:135
-#: engines/phoenixvr/metaengine.cpp:66 engines/tinsel/metaengine.cpp:294
+#: engines/phoenixvr/metaengine.cpp:75 engines/tinsel/metaengine.cpp:294
 #: engines/twp/metaengine.cpp:150
 msgid "Action"
 msgstr "??????????"
@@ -12167,7 +12167,7 @@ msgstr "?????????????? / ???????????????? ???????????? ????????????????"
 msgid "Close current interface"
 msgstr "???????????????? ?????????????????? ????????????????"
 
-#: engines/phoenixvr/metaengine.cpp:107
+#: engines/phoenixvr/metaengine.cpp:116
 #: engines/wintermute/keymapper_tables.h:239
 #: engines/wintermute/keymapper_tables.h:262
 #: engines/wintermute/keymapper_tables.h:313
diff --git a/po/es_ES.po b/po/es_ES.po
index c7b9a405b9a..a09d3af21cf 100644
--- a/po/es_ES.po
+++ b/po/es_ES.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.4.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-11 13:12+0000\n"
+"POT-Creation-Date: 2026-06-11 22:10+0000\n"
 "PO-Revision-Date: 2026-06-10 11:58+0000\n"
 "Last-Translator: IlDucci <pajaroloco_2 at hotmail.com>\n"
 "Language-Team: Spanish <https://translations.scummvm.org/projects/scummvm/"
@@ -6165,7 +6165,7 @@ msgstr "R2"
 #: backends/platform/sdl/miyoo/miyoo.cpp:114
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
-#: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
+#: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:110
 #: engines/scumm/metaengine.cpp:1232 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "Men??"
@@ -6541,7 +6541,7 @@ msgstr "Tomar"
 #: engines/grim/grim.cpp:621 engines/hdb/metaengine.cpp:251
 #: engines/hpl1/metaengine.cpp:94 engines/icb/icb.cpp:101
 #: engines/icb/icb.cpp:179 engines/petka/metaengine.cpp:173
-#: engines/phoenixvr/metaengine.cpp:73 engines/scumm/help.cpp:228
+#: engines/phoenixvr/metaengine.cpp:82 engines/scumm/help.cpp:228
 #: engines/scumm/help.cpp:237 engines/scumm/help.cpp:244
 #: engines/scumm/help.cpp:252 engines/sherlock/metaengine.cpp:435
 #: engines/sludge/keymapper_tables.h:58 engines/sludge/keymapper_tables.h:119
@@ -6692,7 +6692,7 @@ msgstr ""
 #: engines/got/metaengine.cpp:35 engines/groovie/metaengine.cpp:53
 #: engines/m4/metaengine.cpp:37 engines/mads/metaengine.cpp:116
 #: engines/nancy/metaengine.cpp:104 engines/neverhood/dialogs.cpp:47
-#: engines/pelrock/metaengine.cpp:35 engines/phoenixvr/metaengine.cpp:38
+#: engines/pelrock/metaengine.cpp:35 engines/phoenixvr/metaengine.cpp:47
 #: engines/sci/detection_options.h:105 engines/sherlock/metaengine.cpp:47
 #: engines/toltecs/metaengine.cpp:44 engines/trecision/metaengine.cpp:41
 #: engines/ultima/metaengine.cpp:56 engines/waynesworld/metaengine.cpp:37
@@ -6707,7 +6707,7 @@ msgstr "Usar pantallas de guardado/carga originales"
 #: engines/got/metaengine.cpp:36 engines/groovie/metaengine.cpp:54
 #: engines/m4/metaengine.cpp:38 engines/mads/metaengine.cpp:117
 #: engines/nancy/metaengine.cpp:105 engines/neverhood/dialogs.cpp:48
-#: engines/pelrock/metaengine.cpp:36 engines/phoenixvr/metaengine.cpp:39
+#: engines/pelrock/metaengine.cpp:36 engines/phoenixvr/metaengine.cpp:48
 #: engines/sci/detection_options.h:106 engines/sherlock/metaengine.cpp:48
 #: engines/toltecs/metaengine.cpp:45 engines/trecision/metaengine.cpp:42
 #: engines/ultima/metaengine.cpp:57 engines/waynesworld/metaengine.cpp:38
@@ -7319,7 +7319,7 @@ msgid "Show version"
 msgstr "Mostrar versi??n"
 
 #: engines/asylum/metaengine.cpp:120 engines/crab/input/input.cpp:271
-#: engines/phoenixvr/metaengine.cpp:96 engines/queen/metaengine.cpp:253
+#: engines/phoenixvr/metaengine.cpp:105 engines/queen/metaengine.cpp:253
 #: engines/sludge/keymapper_tables.h:308 engines/sludge/keymapper_tables.h:404
 #: engines/sludge/keymapper_tables.h:471 engines/sludge/keymapper_tables.h:502
 #: engines/ultima/nuvie/metaengine.cpp:92
@@ -7328,7 +7328,7 @@ msgid "Quick load"
 msgstr "Carga r??pida"
 
 #: engines/asylum/metaengine.cpp:125 engines/crab/input/input.cpp:266
-#: engines/phoenixvr/metaengine.cpp:91 engines/queen/metaengine.cpp:247
+#: engines/phoenixvr/metaengine.cpp:100 engines/queen/metaengine.cpp:247
 #: engines/sludge/keymapper_tables.h:64 engines/sludge/keymapper_tables.h:303
 #: engines/sludge/keymapper_tables.h:410 engines/sludge/keymapper_tables.h:465
 #: engines/sludge/keymapper_tables.h:496 engines/ultima/nuvie/metaengine.cpp:91
@@ -7641,7 +7641,7 @@ msgstr "Entrar/Salir del modo de combate"
 #. I18N: This keymap allows skipping video cutscenes
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
-#: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
+#: engines/phoenixvr/metaengine.cpp:94 engines/scumm/help.cpp:79
 #: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
@@ -7821,7 +7821,7 @@ msgstr "Base de datos de pistas"
 #: engines/freescape/games/dark/dark.cpp:236
 #: engines/freescape/games/driller/driller.cpp:129
 #: engines/freescape/games/eclipse/eclipse.cpp:340
-#: engines/mads/metaengine.cpp:403 engines/phoenixvr/metaengine.cpp:80
+#: engines/mads/metaengine.cpp:403 engines/phoenixvr/metaengine.cpp:89
 #: engines/saga/metaengine.cpp:437 engines/sherlock/metaengine.cpp:359
 #: engines/sherlock/metaengine.cpp:618 engines/sherlock/metaengine.cpp:852
 #: engines/stark/metaengine.cpp:280 engines/sword1/metaengine.cpp:295
@@ -8467,7 +8467,7 @@ msgstr "Solo los efectos de la versi??n en disquetes"
 
 #. I18N: Perform action on object where cursor points
 #: engines/darkseed/metaengine.cpp:42 engines/dragons/metaengine.cpp:135
-#: engines/phoenixvr/metaengine.cpp:66 engines/tinsel/metaengine.cpp:294
+#: engines/phoenixvr/metaengine.cpp:75 engines/tinsel/metaengine.cpp:294
 #: engines/twp/metaengine.cpp:150
 msgid "Action"
 msgstr "Acci??n"
@@ -12014,7 +12014,7 @@ msgstr "Mostrar/Ocultar pantalla de opciones"
 msgid "Close current interface"
 msgstr "Cerrar interfaz actual"
 
-#: engines/phoenixvr/metaengine.cpp:107
+#: engines/phoenixvr/metaengine.cpp:116
 #: engines/wintermute/keymapper_tables.h:239
 #: engines/wintermute/keymapper_tables.h:262
 #: engines/wintermute/keymapper_tables.h:313
diff --git a/po/eu.po b/po/eu.po
index 3e1d4bb4a87..ec9dd5f5ca3 100644
--- a/po/eu.po
+++ b/po/eu.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.5.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-11 13:12+0000\n"
+"POT-Creation-Date: 2026-06-11 22:10+0000\n"
 "PO-Revision-Date: 2024-02-05 18:42+0000\n"
 "Last-Translator: Josu Igoa <josuigoa at ni.eus>\n"
 "Language-Team: Basque <https://translations.scummvm.org/projects/scummvm/"
@@ -5918,7 +5918,7 @@ msgstr ""
 #: backends/platform/sdl/miyoo/miyoo.cpp:114
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
-#: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
+#: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:110
 #: engines/scumm/metaengine.cpp:1232 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "Menua"
@@ -6309,7 +6309,7 @@ msgstr "Kendu"
 #: engines/grim/grim.cpp:621 engines/hdb/metaengine.cpp:251
 #: engines/hpl1/metaengine.cpp:94 engines/icb/icb.cpp:101
 #: engines/icb/icb.cpp:179 engines/petka/metaengine.cpp:173
-#: engines/phoenixvr/metaengine.cpp:73 engines/scumm/help.cpp:228
+#: engines/phoenixvr/metaengine.cpp:82 engines/scumm/help.cpp:228
 #: engines/scumm/help.cpp:237 engines/scumm/help.cpp:244
 #: engines/scumm/help.cpp:252 engines/sherlock/metaengine.cpp:435
 #: engines/sludge/keymapper_tables.h:58 engines/sludge/keymapper_tables.h:119
@@ -6456,7 +6456,7 @@ msgstr ""
 #: engines/got/metaengine.cpp:35 engines/groovie/metaengine.cpp:53
 #: engines/m4/metaengine.cpp:37 engines/mads/metaengine.cpp:116
 #: engines/nancy/metaengine.cpp:104 engines/neverhood/dialogs.cpp:47
-#: engines/pelrock/metaengine.cpp:35 engines/phoenixvr/metaengine.cpp:38
+#: engines/pelrock/metaengine.cpp:35 engines/phoenixvr/metaengine.cpp:47
 #: engines/sci/detection_options.h:105 engines/sherlock/metaengine.cpp:47
 #: engines/toltecs/metaengine.cpp:44 engines/trecision/metaengine.cpp:41
 #: engines/ultima/metaengine.cpp:56 engines/waynesworld/metaengine.cpp:37
@@ -6471,7 +6471,7 @@ msgstr "Erabili jatorrizko gorde/kargatu pantailak"
 #: engines/got/metaengine.cpp:36 engines/groovie/metaengine.cpp:54
 #: engines/m4/metaengine.cpp:38 engines/mads/metaengine.cpp:117
 #: engines/nancy/metaengine.cpp:105 engines/neverhood/dialogs.cpp:48
-#: engines/pelrock/metaengine.cpp:36 engines/phoenixvr/metaengine.cpp:39
+#: engines/pelrock/metaengine.cpp:36 engines/phoenixvr/metaengine.cpp:48
 #: engines/sci/detection_options.h:106 engines/sherlock/metaengine.cpp:48
 #: engines/toltecs/metaengine.cpp:45 engines/trecision/metaengine.cpp:42
 #: engines/ultima/metaengine.cpp:57 engines/waynesworld/metaengine.cpp:38
@@ -7072,7 +7072,7 @@ msgid "Show version"
 msgstr "Erakutsi objektuen etiketak"
 
 #: engines/asylum/metaengine.cpp:120 engines/crab/input/input.cpp:271
-#: engines/phoenixvr/metaengine.cpp:96 engines/queen/metaengine.cpp:253
+#: engines/phoenixvr/metaengine.cpp:105 engines/queen/metaengine.cpp:253
 #: engines/sludge/keymapper_tables.h:308 engines/sludge/keymapper_tables.h:404
 #: engines/sludge/keymapper_tables.h:471 engines/sludge/keymapper_tables.h:502
 #: engines/ultima/nuvie/metaengine.cpp:92
@@ -7082,7 +7082,7 @@ msgid "Quick load"
 msgstr "Klikatzeko modua"
 
 #: engines/asylum/metaengine.cpp:125 engines/crab/input/input.cpp:266
-#: engines/phoenixvr/metaengine.cpp:91 engines/queen/metaengine.cpp:247
+#: engines/phoenixvr/metaengine.cpp:100 engines/queen/metaengine.cpp:247
 #: engines/sludge/keymapper_tables.h:64 engines/sludge/keymapper_tables.h:303
 #: engines/sludge/keymapper_tables.h:410 engines/sludge/keymapper_tables.h:465
 #: engines/sludge/keymapper_tables.h:496 engines/ultima/nuvie/metaengine.cpp:91
@@ -7408,7 +7408,7 @@ msgstr "Mututu/Ez-mututu"
 #. I18N: This keymap allows skipping video cutscenes
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
-#: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
+#: engines/phoenixvr/metaengine.cpp:94 engines/scumm/help.cpp:79
 #: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
@@ -7597,7 +7597,7 @@ msgstr ""
 #: engines/freescape/games/dark/dark.cpp:236
 #: engines/freescape/games/driller/driller.cpp:129
 #: engines/freescape/games/eclipse/eclipse.cpp:340
-#: engines/mads/metaengine.cpp:403 engines/phoenixvr/metaengine.cpp:80
+#: engines/mads/metaengine.cpp:403 engines/phoenixvr/metaengine.cpp:89
 #: engines/saga/metaengine.cpp:437 engines/sherlock/metaengine.cpp:359
 #: engines/sherlock/metaengine.cpp:618 engines/sherlock/metaengine.cpp:852
 #: engines/stark/metaengine.cpp:280 engines/sword1/metaengine.cpp:295
@@ -8323,7 +8323,7 @@ msgstr ""
 
 #. I18N: Perform action on object where cursor points
 #: engines/darkseed/metaengine.cpp:42 engines/dragons/metaengine.cpp:135
-#: engines/phoenixvr/metaengine.cpp:66 engines/tinsel/metaengine.cpp:294
+#: engines/phoenixvr/metaengine.cpp:75 engines/tinsel/metaengine.cpp:294
 #: engines/twp/metaengine.cpp:150
 #, fuzzy
 msgid "Action"
@@ -12152,7 +12152,7 @@ msgstr "Kontsola erakutsi / ezkutatu"
 msgid "Close current interface"
 msgstr "Ahotsak bakarrik"
 
-#: engines/phoenixvr/metaengine.cpp:107
+#: engines/phoenixvr/metaengine.cpp:116
 #: engines/wintermute/keymapper_tables.h:239
 #: engines/wintermute/keymapper_tables.h:262
 #: engines/wintermute/keymapper_tables.h:313
diff --git a/po/fi_FI.po b/po/fi_FI.po
index 6f295df95e8..5e49f3c2b70 100644
--- a/po/fi_FI.po
+++ b/po/fi_FI.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.6.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-11 13:12+0000\n"
+"POT-Creation-Date: 2026-06-11 22:10+0000\n"
 "PO-Revision-Date: 2026-02-07 15:22+0000\n"
 "Last-Translator: Timo Mikkolainen <tmikkola at gmail.com>\n"
 "Language-Team: Finnish <https://translations.scummvm.org/projects/scummvm/"
@@ -6262,7 +6262,7 @@ msgstr "R2"
 #: backends/platform/sdl/miyoo/miyoo.cpp:114
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
-#: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
+#: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:110
 #: engines/scumm/metaengine.cpp:1232 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "Valikko"
@@ -6638,7 +6638,7 @@ msgstr "Ota"
 #: engines/grim/grim.cpp:621 engines/hdb/metaengine.cpp:251
 #: engines/hpl1/metaengine.cpp:94 engines/icb/icb.cpp:101
 #: engines/icb/icb.cpp:179 engines/petka/metaengine.cpp:173
-#: engines/phoenixvr/metaengine.cpp:73 engines/scumm/help.cpp:228
+#: engines/phoenixvr/metaengine.cpp:82 engines/scumm/help.cpp:228
 #: engines/scumm/help.cpp:237 engines/scumm/help.cpp:244
 #: engines/scumm/help.cpp:252 engines/sherlock/metaengine.cpp:435
 #: engines/sludge/keymapper_tables.h:58 engines/sludge/keymapper_tables.h:119
@@ -6784,7 +6784,7 @@ msgstr ""
 #: engines/got/metaengine.cpp:35 engines/groovie/metaengine.cpp:53
 #: engines/m4/metaengine.cpp:37 engines/mads/metaengine.cpp:116
 #: engines/nancy/metaengine.cpp:104 engines/neverhood/dialogs.cpp:47
-#: engines/pelrock/metaengine.cpp:35 engines/phoenixvr/metaengine.cpp:38
+#: engines/pelrock/metaengine.cpp:35 engines/phoenixvr/metaengine.cpp:47
 #: engines/sci/detection_options.h:105 engines/sherlock/metaengine.cpp:47
 #: engines/toltecs/metaengine.cpp:44 engines/trecision/metaengine.cpp:41
 #: engines/ultima/metaengine.cpp:56 engines/waynesworld/metaengine.cpp:37
@@ -6799,7 +6799,7 @@ msgstr "K??yt?? alkuper??isi?? tallenna/lataa valikkoja"
 #: engines/got/metaengine.cpp:36 engines/groovie/metaengine.cpp:54
 #: engines/m4/metaengine.cpp:38 engines/mads/metaengine.cpp:117
 #: engines/nancy/metaengine.cpp:105 engines/neverhood/dialogs.cpp:48
-#: engines/pelrock/metaengine.cpp:36 engines/phoenixvr/metaengine.cpp:39
+#: engines/pelrock/metaengine.cpp:36 engines/phoenixvr/metaengine.cpp:48
 #: engines/sci/detection_options.h:106 engines/sherlock/metaengine.cpp:48
 #: engines/toltecs/metaengine.cpp:45 engines/trecision/metaengine.cpp:42
 #: engines/ultima/metaengine.cpp:57 engines/waynesworld/metaengine.cpp:38
@@ -7397,7 +7397,7 @@ msgid "Show version"
 msgstr "N??yt?? versio"
 
 #: engines/asylum/metaengine.cpp:120 engines/crab/input/input.cpp:271
-#: engines/phoenixvr/metaengine.cpp:96 engines/queen/metaengine.cpp:253
+#: engines/phoenixvr/metaengine.cpp:105 engines/queen/metaengine.cpp:253
 #: engines/sludge/keymapper_tables.h:308 engines/sludge/keymapper_tables.h:404
 #: engines/sludge/keymapper_tables.h:471 engines/sludge/keymapper_tables.h:502
 #: engines/ultima/nuvie/metaengine.cpp:92
@@ -7406,7 +7406,7 @@ msgid "Quick load"
 msgstr "Pelitallenteen pikalataus"
 
 #: engines/asylum/metaengine.cpp:125 engines/crab/input/input.cpp:266
-#: engines/phoenixvr/metaengine.cpp:91 engines/queen/metaengine.cpp:247
+#: engines/phoenixvr/metaengine.cpp:100 engines/queen/metaengine.cpp:247
 #: engines/sludge/keymapper_tables.h:64 engines/sludge/keymapper_tables.h:303
 #: engines/sludge/keymapper_tables.h:410 engines/sludge/keymapper_tables.h:465
 #: engines/sludge/keymapper_tables.h:496 engines/ultima/nuvie/metaengine.cpp:91
@@ -7724,7 +7724,7 @@ msgstr "Taistelumoodi p????lle / pois"
 #. I18N: This keymap allows skipping video cutscenes
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
-#: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
+#: engines/phoenixvr/metaengine.cpp:94 engines/scumm/help.cpp:79
 #: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
@@ -7904,7 +7904,7 @@ msgstr "Vihjeiden tietokanta"
 #: engines/freescape/games/dark/dark.cpp:236
 #: engines/freescape/games/driller/driller.cpp:129
 #: engines/freescape/games/eclipse/eclipse.cpp:340
-#: engines/mads/metaengine.cpp:403 engines/phoenixvr/metaengine.cpp:80
+#: engines/mads/metaengine.cpp:403 engines/phoenixvr/metaengine.cpp:89
 #: engines/saga/metaengine.cpp:437 engines/sherlock/metaengine.cpp:359
 #: engines/sherlock/metaengine.cpp:618 engines/sherlock/metaengine.cpp:852
 #: engines/stark/metaengine.cpp:280 engines/sword1/metaengine.cpp:295
@@ -8574,7 +8574,7 @@ msgstr "Vain levykeversion ????nitehosteet"
 
 #. I18N: Perform action on object where cursor points
 #: engines/darkseed/metaengine.cpp:42 engines/dragons/metaengine.cpp:135
-#: engines/phoenixvr/metaengine.cpp:66 engines/tinsel/metaengine.cpp:294
+#: engines/phoenixvr/metaengine.cpp:75 engines/tinsel/metaengine.cpp:294
 #: engines/twp/metaengine.cpp:150
 msgid "Action"
 msgstr "Toiminta"
@@ -12243,7 +12243,7 @@ msgstr "N??yt?? / piilota konsoli"
 msgid "Close current interface"
 msgstr "Helppo hiirik??ytt??liittym??"
 
-#: engines/phoenixvr/metaengine.cpp:107
+#: engines/phoenixvr/metaengine.cpp:116
 #: engines/wintermute/keymapper_tables.h:239
 #: engines/wintermute/keymapper_tables.h:262
 #: engines/wintermute/keymapper_tables.h:313
diff --git a/po/fr_FR.po b/po/fr_FR.po
index a14a3fd61f4..1e7e32daa5d 100644
--- a/po/fr_FR.po
+++ b/po/fr_FR.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-11 13:12+0000\n"
+"POT-Creation-Date: 2026-06-11 22:10+0000\n"
 "PO-Revision-Date: 2026-06-08 16:22+0000\n"
 "Last-Translator: Purple T <ZEONK at hotmail.com>\n"
 "Language-Team: French <https://translations.scummvm.org/projects/scummvm/"
@@ -6180,7 +6180,7 @@ msgstr "R2"
 #: backends/platform/sdl/miyoo/miyoo.cpp:114
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
-#: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
+#: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:110
 #: engines/scumm/metaengine.cpp:1232 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "Menu"
@@ -6556,7 +6556,7 @@ msgstr "Prendre"
 #: engines/grim/grim.cpp:621 engines/hdb/metaengine.cpp:251
 #: engines/hpl1/metaengine.cpp:94 engines/icb/icb.cpp:101
 #: engines/icb/icb.cpp:179 engines/petka/metaengine.cpp:173
-#: engines/phoenixvr/metaengine.cpp:73 engines/scumm/help.cpp:228
+#: engines/phoenixvr/metaengine.cpp:82 engines/scumm/help.cpp:228
 #: engines/scumm/help.cpp:237 engines/scumm/help.cpp:244
 #: engines/scumm/help.cpp:252 engines/sherlock/metaengine.cpp:435
 #: engines/sludge/keymapper_tables.h:58 engines/sludge/keymapper_tables.h:119
@@ -6702,7 +6702,7 @@ msgstr ""
 #: engines/got/metaengine.cpp:35 engines/groovie/metaengine.cpp:53
 #: engines/m4/metaengine.cpp:37 engines/mads/metaengine.cpp:116
 #: engines/nancy/metaengine.cpp:104 engines/neverhood/dialogs.cpp:47
-#: engines/pelrock/metaengine.cpp:35 engines/phoenixvr/metaengine.cpp:38
+#: engines/pelrock/metaengine.cpp:35 engines/phoenixvr/metaengine.cpp:47
 #: engines/sci/detection_options.h:105 engines/sherlock/metaengine.cpp:47
 #: engines/toltecs/metaengine.cpp:44 engines/trecision/metaengine.cpp:41
 #: engines/ultima/metaengine.cpp:56 engines/waynesworld/metaengine.cpp:37
@@ -6717,7 +6717,7 @@ msgstr "??crans sauvegarder/charger d'origine"
 #: engines/got/metaengine.cpp:36 engines/groovie/metaengine.cpp:54
 #: engines/m4/metaengine.cpp:38 engines/mads/metaengine.cpp:117
 #: engines/nancy/metaengine.cpp:105 engines/neverhood/dialogs.cpp:48
-#: engines/pelrock/metaengine.cpp:36 engines/phoenixvr/metaengine.cpp:39
+#: engines/pelrock/metaengine.cpp:36 engines/phoenixvr/metaengine.cpp:48
 #: engines/sci/detection_options.h:106 engines/sherlock/metaengine.cpp:48
 #: engines/toltecs/metaengine.cpp:45 engines/trecision/metaengine.cpp:42
 #: engines/ultima/metaengine.cpp:57 engines/waynesworld/metaengine.cpp:38
@@ -7321,7 +7321,7 @@ msgid "Show version"
 msgstr "Afficher la version"
 
 #: engines/asylum/metaengine.cpp:120 engines/crab/input/input.cpp:271
-#: engines/phoenixvr/metaengine.cpp:96 engines/queen/metaengine.cpp:253
+#: engines/phoenixvr/metaengine.cpp:105 engines/queen/metaengine.cpp:253
 #: engines/sludge/keymapper_tables.h:308 engines/sludge/keymapper_tables.h:404
 #: engines/sludge/keymapper_tables.h:471 engines/sludge/keymapper_tables.h:502
 #: engines/ultima/nuvie/metaengine.cpp:92
@@ -7330,7 +7330,7 @@ msgid "Quick load"
 msgstr "Chargement rapide"
 
 #: engines/asylum/metaengine.cpp:125 engines/crab/input/input.cpp:266
-#: engines/phoenixvr/metaengine.cpp:91 engines/queen/metaengine.cpp:247
+#: engines/phoenixvr/metaengine.cpp:100 engines/queen/metaengine.cpp:247
 #: engines/sludge/keymapper_tables.h:64 engines/sludge/keymapper_tables.h:303
 #: engines/sludge/keymapper_tables.h:410 engines/sludge/keymapper_tables.h:465
 #: engines/sludge/keymapper_tables.h:496 engines/ultima/nuvie/metaengine.cpp:91
@@ -7646,7 +7646,7 @@ msgstr "Act. / D??sact. combat"
 #. I18N: This keymap allows skipping video cutscenes
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
-#: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
+#: engines/phoenixvr/metaengine.cpp:94 engines/scumm/help.cpp:79
 #: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
@@ -7826,7 +7826,7 @@ msgstr "Panneau des indices"
 #: engines/freescape/games/dark/dark.cpp:236
 #: engines/freescape/games/driller/driller.cpp:129
 #: engines/freescape/games/eclipse/eclipse.cpp:340
-#: engines/mads/metaengine.cpp:403 engines/phoenixvr/metaengine.cpp:80
+#: engines/mads/metaengine.cpp:403 engines/phoenixvr/metaengine.cpp:89
 #: engines/saga/metaengine.cpp:437 engines/sherlock/metaengine.cpp:359
 #: engines/sherlock/metaengine.cpp:618 engines/sherlock/metaengine.cpp:852
 #: engines/stark/metaengine.cpp:280 engines/sword1/metaengine.cpp:295
@@ -8467,7 +8467,7 @@ msgstr "Version Disquette seuls"
 
 #. I18N: Perform action on object where cursor points
 #: engines/darkseed/metaengine.cpp:42 engines/dragons/metaengine.cpp:135
-#: engines/phoenixvr/metaengine.cpp:66 engines/tinsel/metaengine.cpp:294
+#: engines/phoenixvr/metaengine.cpp:75 engines/tinsel/metaengine.cpp:294
 #: engines/twp/metaengine.cpp:150
 msgid "Action"
 msgstr "Action"
@@ -12036,7 +12036,7 @@ msgstr "Afficher/Cacher l'??cran des options"
 msgid "Close current interface"
 msgstr "Fermer l'interface actuelle"
 
-#: engines/phoenixvr/metaengine.cpp:107
+#: engines/phoenixvr/metaengine.cpp:116
 #: engines/wintermute/keymapper_tables.h:239
 #: engines/wintermute/keymapper_tables.h:262
 #: engines/wintermute/keymapper_tables.h:313
diff --git a/po/gl_ES.po b/po/gl_ES.po
index fa39ed3dcb7..8c39a5d25cb 100644
--- a/po/gl_ES.po
+++ b/po/gl_ES.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-11 13:12+0000\n"
+"POT-Creation-Date: 2026-06-11 22:10+0000\n"
 "PO-Revision-Date: 2020-09-26 21:13+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Galician <https://translations.scummvm.org/projects/scummvm/"
@@ -5852,7 +5852,7 @@ msgstr ""
 #: backends/platform/sdl/miyoo/miyoo.cpp:114
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
-#: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
+#: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:110
 #: engines/scumm/metaengine.cpp:1232 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "Men??"
@@ -6243,7 +6243,7 @@ msgstr "Quitar"
 #: engines/grim/grim.cpp:621 engines/hdb/metaengine.cpp:251
 #: engines/hpl1/metaengine.cpp:94 engines/icb/icb.cpp:101
 #: engines/icb/icb.cpp:179 engines/petka/metaengine.cpp:173
-#: engines/phoenixvr/metaengine.cpp:73 engines/scumm/help.cpp:228
+#: engines/phoenixvr/metaengine.cpp:82 engines/scumm/help.cpp:228
 #: engines/scumm/help.cpp:237 engines/scumm/help.cpp:244
 #: engines/scumm/help.cpp:252 engines/sherlock/metaengine.cpp:435
 #: engines/sludge/keymapper_tables.h:58 engines/sludge/keymapper_tables.h:119
@@ -6392,7 +6392,7 @@ msgstr ""
 #: engines/got/metaengine.cpp:35 engines/groovie/metaengine.cpp:53
 #: engines/m4/metaengine.cpp:37 engines/mads/metaengine.cpp:116
 #: engines/nancy/metaengine.cpp:104 engines/neverhood/dialogs.cpp:47
-#: engines/pelrock/metaengine.cpp:35 engines/phoenixvr/metaengine.cpp:38
+#: engines/pelrock/metaengine.cpp:35 engines/phoenixvr/metaengine.cpp:47
 #: engines/sci/detection_options.h:105 engines/sherlock/metaengine.cpp:47
 #: engines/toltecs/metaengine.cpp:44 engines/trecision/metaengine.cpp:41
 #: engines/ultima/metaengine.cpp:56 engines/waynesworld/metaengine.cpp:37
@@ -6407,7 +6407,7 @@ msgstr "Empregar pantallas orixinais de gardado e carga"
 #: engines/got/metaengine.cpp:36 engines/groovie/metaengine.cpp:54
 #: engines/m4/metaengine.cpp:38 engines/mads/metaengine.cpp:117
 #: engines/nancy/metaengine.cpp:105 engines/neverhood/dialogs.cpp:48
-#: engines/pelrock/metaengine.cpp:36 engines/phoenixvr/metaengine.cpp:39
+#: engines/pelrock/metaengine.cpp:36 engines/phoenixvr/metaengine.cpp:48
 #: engines/sci/detection_options.h:106 engines/sherlock/metaengine.cpp:48
 #: engines/toltecs/metaengine.cpp:45 engines/trecision/metaengine.cpp:42
 #: engines/ultima/metaengine.cpp:57 engines/waynesworld/metaengine.cpp:38
@@ -7007,7 +7007,7 @@ msgid "Show version"
 msgstr "Mostrar li??a de obsecto"
 
 #: engines/asylum/metaengine.cpp:120 engines/crab/input/input.cpp:271
-#: engines/phoenixvr/metaengine.cpp:96 engines/queen/metaengine.cpp:253
+#: engines/phoenixvr/metaengine.cpp:105 engines/queen/metaengine.cpp:253
 #: engines/sludge/keymapper_tables.h:308 engines/sludge/keymapper_tables.h:404
 #: engines/sludge/keymapper_tables.h:471 engines/sludge/keymapper_tables.h:502
 #: engines/ultima/nuvie/metaengine.cpp:92
@@ -7017,7 +7017,7 @@ msgid "Quick load"
 msgstr "Modo Rato"
 
 #: engines/asylum/metaengine.cpp:125 engines/crab/input/input.cpp:266
-#: engines/phoenixvr/metaengine.cpp:91 engines/queen/metaengine.cpp:247
+#: engines/phoenixvr/metaengine.cpp:100 engines/queen/metaengine.cpp:247
 #: engines/sludge/keymapper_tables.h:64 engines/sludge/keymapper_tables.h:303
 #: engines/sludge/keymapper_tables.h:410 engines/sludge/keymapper_tables.h:465
 #: engines/sludge/keymapper_tables.h:496 engines/ultima/nuvie/metaengine.cpp:91
@@ -7341,7 +7341,7 @@ msgstr "Activar/desactivar captura de rato"
 #. I18N: This keymap allows skipping video cutscenes
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
-#: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
+#: engines/phoenixvr/metaengine.cpp:94 engines/scumm/help.cpp:79
 #: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
@@ -7530,7 +7530,7 @@ msgstr ""
 #: engines/freescape/games/dark/dark.cpp:236
 #: engines/freescape/games/driller/driller.cpp:129
 #: engines/freescape/games/eclipse/eclipse.cpp:340
-#: engines/mads/metaengine.cpp:403 engines/phoenixvr/metaengine.cpp:80
+#: engines/mads/metaengine.cpp:403 engines/phoenixvr/metaengine.cpp:89
 #: engines/saga/metaengine.cpp:437 engines/sherlock/metaengine.cpp:359
 #: engines/sherlock/metaengine.cpp:618 engines/sherlock/metaengine.cpp:852
 #: engines/stark/metaengine.cpp:280 engines/sword1/metaengine.cpp:295
@@ -8256,7 +8256,7 @@ msgstr ""
 
 #. I18N: Perform action on object where cursor points
 #: engines/darkseed/metaengine.cpp:42 engines/dragons/metaengine.cpp:135
-#: engines/phoenixvr/metaengine.cpp:66 engines/tinsel/metaengine.cpp:294
+#: engines/phoenixvr/metaengine.cpp:75 engines/tinsel/metaengine.cpp:294
 #: engines/twp/metaengine.cpp:150
 #, fuzzy
 msgid "Action"
@@ -12085,7 +12085,7 @@ msgstr "Mostrar/ocultar consola"
 msgid "Close current interface"
 msgstr "Interface de rato sinxela"
 
-#: engines/phoenixvr/metaengine.cpp:107
+#: engines/phoenixvr/metaengine.cpp:116
 #: engines/wintermute/keymapper_tables.h:239
 #: engines/wintermute/keymapper_tables.h:262
 #: engines/wintermute/keymapper_tables.h:313
diff --git a/po/he.po b/po/he.po
index 6cbfaa0621c..c20bc299eb4 100644
--- a/po/he.po
+++ b/po/he.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.1.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-11 13:12+0000\n"
+"POT-Creation-Date: 2026-06-11 22:10+0000\n"
 "PO-Revision-Date: 2025-04-09 20:18+0000\n"
 "Last-Translator: Niv Baehr <bloop93 at gmail.com>\n"
 "Language-Team: Hebrew <https://translations.scummvm.org/projects/scummvm/"
@@ -5810,7 +5810,7 @@ msgstr "R2"
 #: backends/platform/sdl/miyoo/miyoo.cpp:114
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
-#: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
+#: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:110
 #: engines/scumm/metaengine.cpp:1232 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "??????????"
@@ -6202,7 +6202,7 @@ msgstr "????????????"
 #: engines/grim/grim.cpp:621 engines/hdb/metaengine.cpp:251
 #: engines/hpl1/metaengine.cpp:94 engines/icb/icb.cpp:101
 #: engines/icb/icb.cpp:179 engines/petka/metaengine.cpp:173
-#: engines/phoenixvr/metaengine.cpp:73 engines/scumm/help.cpp:228
+#: engines/phoenixvr/metaengine.cpp:82 engines/scumm/help.cpp:228
 #: engines/scumm/help.cpp:237 engines/scumm/help.cpp:244
 #: engines/scumm/help.cpp:252 engines/sherlock/metaengine.cpp:435
 #: engines/sludge/keymapper_tables.h:58 engines/sludge/keymapper_tables.h:119
@@ -6347,7 +6347,7 @@ msgstr ""
 #: engines/got/metaengine.cpp:35 engines/groovie/metaengine.cpp:53
 #: engines/m4/metaengine.cpp:37 engines/mads/metaengine.cpp:116
 #: engines/nancy/metaengine.cpp:104 engines/neverhood/dialogs.cpp:47
-#: engines/pelrock/metaengine.cpp:35 engines/phoenixvr/metaengine.cpp:38
+#: engines/pelrock/metaengine.cpp:35 engines/phoenixvr/metaengine.cpp:47
 #: engines/sci/detection_options.h:105 engines/sherlock/metaengine.cpp:47
 #: engines/toltecs/metaengine.cpp:44 engines/trecision/metaengine.cpp:41
 #: engines/ultima/metaengine.cpp:56 engines/waynesworld/metaengine.cpp:37
@@ -6362,7 +6362,7 @@ msgstr "???????????? ?????????? ?????????? / ?????????? ??????????????"
 #: engines/got/metaengine.cpp:36 engines/groovie/metaengine.cpp:54
 #: engines/m4/metaengine.cpp:38 engines/mads/metaengine.cpp:117
 #: engines/nancy/metaengine.cpp:105 engines/neverhood/dialogs.cpp:48
-#: engines/pelrock/metaengine.cpp:36 engines/phoenixvr/metaengine.cpp:39
+#: engines/pelrock/metaengine.cpp:36 engines/phoenixvr/metaengine.cpp:48
 #: engines/sci/detection_options.h:106 engines/sherlock/metaengine.cpp:48
 #: engines/toltecs/metaengine.cpp:45 engines/trecision/metaengine.cpp:42
 #: engines/ultima/metaengine.cpp:57 engines/waynesworld/metaengine.cpp:38
@@ -6984,7 +6984,7 @@ msgid "Show version"
 msgstr "???????? ????????"
 
 #: engines/asylum/metaengine.cpp:120 engines/crab/input/input.cpp:271
-#: engines/phoenixvr/metaengine.cpp:96 engines/queen/metaengine.cpp:253
+#: engines/phoenixvr/metaengine.cpp:105 engines/queen/metaengine.cpp:253
 #: engines/sludge/keymapper_tables.h:308 engines/sludge/keymapper_tables.h:404
 #: engines/sludge/keymapper_tables.h:471 engines/sludge/keymapper_tables.h:502
 #: engines/ultima/nuvie/metaengine.cpp:92
@@ -6993,7 +6993,7 @@ msgid "Quick load"
 msgstr "?????????? ??????????"
 
 #: engines/asylum/metaengine.cpp:125 engines/crab/input/input.cpp:266
-#: engines/phoenixvr/metaengine.cpp:91 engines/queen/metaengine.cpp:247
+#: engines/phoenixvr/metaengine.cpp:100 engines/queen/metaengine.cpp:247
 #: engines/sludge/keymapper_tables.h:64 engines/sludge/keymapper_tables.h:303
 #: engines/sludge/keymapper_tables.h:410 engines/sludge/keymapper_tables.h:465
 #: engines/sludge/keymapper_tables.h:496 engines/ultima/nuvie/metaengine.cpp:91
@@ -7324,7 +7324,7 @@ msgstr "?????????? ??????????"
 #. I18N: This keymap allows skipping video cutscenes
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
-#: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
+#: engines/phoenixvr/metaengine.cpp:94 engines/scumm/help.cpp:79
 #: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
@@ -7520,7 +7520,7 @@ msgstr "?????? ???????????? ???? ??????????"
 #: engines/freescape/games/dark/dark.cpp:236
 #: engines/freescape/games/driller/driller.cpp:129
 #: engines/freescape/games/eclipse/eclipse.cpp:340
-#: engines/mads/metaengine.cpp:403 engines/phoenixvr/metaengine.cpp:80
+#: engines/mads/metaengine.cpp:403 engines/phoenixvr/metaengine.cpp:89
 #: engines/saga/metaengine.cpp:437 engines/sherlock/metaengine.cpp:359
 #: engines/sherlock/metaengine.cpp:618 engines/sherlock/metaengine.cpp:852
 #: engines/stark/metaengine.cpp:280 engines/sword1/metaengine.cpp:295
@@ -8254,7 +8254,7 @@ msgstr ""
 
 #. I18N: Perform action on object where cursor points
 #: engines/darkseed/metaengine.cpp:42 engines/dragons/metaengine.cpp:135
-#: engines/phoenixvr/metaengine.cpp:66 engines/tinsel/metaengine.cpp:294
+#: engines/phoenixvr/metaengine.cpp:75 engines/tinsel/metaengine.cpp:294
 #: engines/twp/metaengine.cpp:150
 msgid "Action"
 msgstr "??????????"
@@ -12257,7 +12257,7 @@ msgstr "?????? / ???????? ??????????????"
 msgid "Close current interface"
 msgstr "???????? ???????? ????"
 
-#: engines/phoenixvr/metaengine.cpp:107
+#: engines/phoenixvr/metaengine.cpp:116
 #: engines/wintermute/keymapper_tables.h:239
 #: engines/wintermute/keymapper_tables.h:262
 #: engines/wintermute/keymapper_tables.h:313
diff --git a/po/hi.po b/po/hi.po
index d0cf650bc1e..4cc3e83ec18 100644
--- a/po/hi.po
+++ b/po/hi.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-11 13:12+0000\n"
+"POT-Creation-Date: 2026-06-11 22:10+0000\n"
 "PO-Revision-Date: 2026-05-12 22:22+0000\n"
 "Last-Translator: KARTIK SAXENA <kartiksaxena2005 at gmail.com>\n"
 "Language-Team: Hindi <https://translations.scummvm.org/projects/scummvm/"
@@ -5637,7 +5637,7 @@ msgstr ""
 #: backends/platform/sdl/miyoo/miyoo.cpp:114
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
-#: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
+#: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:110
 #: engines/scumm/metaengine.cpp:1232 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr ""
@@ -6026,7 +6026,7 @@ msgstr ""
 #: engines/grim/grim.cpp:621 engines/hdb/metaengine.cpp:251
 #: engines/hpl1/metaengine.cpp:94 engines/icb/icb.cpp:101
 #: engines/icb/icb.cpp:179 engines/petka/metaengine.cpp:173
-#: engines/phoenixvr/metaengine.cpp:73 engines/scumm/help.cpp:228
+#: engines/phoenixvr/metaengine.cpp:82 engines/scumm/help.cpp:228
 #: engines/scumm/help.cpp:237 engines/scumm/help.cpp:244
 #: engines/scumm/help.cpp:252 engines/sherlock/metaengine.cpp:435
 #: engines/sludge/keymapper_tables.h:58 engines/sludge/keymapper_tables.h:119
@@ -6167,7 +6167,7 @@ msgstr ""
 #: engines/got/metaengine.cpp:35 engines/groovie/metaengine.cpp:53
 #: engines/m4/metaengine.cpp:37 engines/mads/metaengine.cpp:116
 #: engines/nancy/metaengine.cpp:104 engines/neverhood/dialogs.cpp:47
-#: engines/pelrock/metaengine.cpp:35 engines/phoenixvr/metaengine.cpp:38
+#: engines/pelrock/metaengine.cpp:35 engines/phoenixvr/metaengine.cpp:47
 #: engines/sci/detection_options.h:105 engines/sherlock/metaengine.cpp:47
 #: engines/toltecs/metaengine.cpp:44 engines/trecision/metaengine.cpp:41
 #: engines/ultima/metaengine.cpp:56 engines/waynesworld/metaengine.cpp:37
@@ -6182,7 +6182,7 @@ msgstr ""
 #: engines/got/metaengine.cpp:36 engines/groovie/metaengine.cpp:54
 #: engines/m4/metaengine.cpp:38 engines/mads/metaengine.cpp:117
 #: engines/nancy/metaengine.cpp:105 engines/neverhood/dialogs.cpp:48
-#: engines/pelrock/metaengine.cpp:36 engines/phoenixvr/metaengine.cpp:39
+#: engines/pelrock/metaengine.cpp:36 engines/phoenixvr/metaengine.cpp:48
 #: engines/sci/detection_options.h:106 engines/sherlock/metaengine.cpp:48
 #: engines/toltecs/metaengine.cpp:45 engines/trecision/metaengine.cpp:42
 #: engines/ultima/metaengine.cpp:57 engines/waynesworld/metaengine.cpp:38
@@ -6738,7 +6738,7 @@ msgid "Show version"
 msgstr "version ??????????????????"
 
 #: engines/asylum/metaengine.cpp:120 engines/crab/input/input.cpp:271
-#: engines/phoenixvr/metaengine.cpp:96 engines/queen/metaengine.cpp:253
+#: engines/phoenixvr/metaengine.cpp:105 engines/queen/metaengine.cpp:253
 #: engines/sludge/keymapper_tables.h:308 engines/sludge/keymapper_tables.h:404
 #: engines/sludge/keymapper_tables.h:471 engines/sludge/keymapper_tables.h:502
 #: engines/ultima/nuvie/metaengine.cpp:92
@@ -6747,7 +6747,7 @@ msgid "Quick load"
 msgstr ""
 
 #: engines/asylum/metaengine.cpp:125 engines/crab/input/input.cpp:266
-#: engines/phoenixvr/metaengine.cpp:91 engines/queen/metaengine.cpp:247
+#: engines/phoenixvr/metaengine.cpp:100 engines/queen/metaengine.cpp:247
 #: engines/sludge/keymapper_tables.h:64 engines/sludge/keymapper_tables.h:303
 #: engines/sludge/keymapper_tables.h:410 engines/sludge/keymapper_tables.h:465
 #: engines/sludge/keymapper_tables.h:496 engines/ultima/nuvie/metaengine.cpp:91
@@ -7058,7 +7058,7 @@ msgstr "???????????????????????? ?????????"
 #. I18N: This keymap allows skipping video cutscenes
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
-#: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
+#: engines/phoenixvr/metaengine.cpp:94 engines/scumm/help.cpp:79
 #: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
@@ -7245,7 +7245,7 @@ msgstr ""
 #: engines/freescape/games/dark/dark.cpp:236
 #: engines/freescape/games/driller/driller.cpp:129
 #: engines/freescape/games/eclipse/eclipse.cpp:340
-#: engines/mads/metaengine.cpp:403 engines/phoenixvr/metaengine.cpp:80
+#: engines/mads/metaengine.cpp:403 engines/phoenixvr/metaengine.cpp:89
 #: engines/saga/metaengine.cpp:437 engines/sherlock/metaengine.cpp:359
 #: engines/sherlock/metaengine.cpp:618 engines/sherlock/metaengine.cpp:852
 #: engines/stark/metaengine.cpp:280 engines/sword1/metaengine.cpp:295
@@ -7938,7 +7938,7 @@ msgstr ""
 
 #. I18N: Perform action on object where cursor points
 #: engines/darkseed/metaengine.cpp:42 engines/dragons/metaengine.cpp:135
-#: engines/phoenixvr/metaengine.cpp:66 engines/tinsel/metaengine.cpp:294
+#: engines/phoenixvr/metaengine.cpp:75 engines/tinsel/metaengine.cpp:294
 #: engines/twp/metaengine.cpp:150
 msgid "Action"
 msgstr ""
@@ -11615,7 +11615,7 @@ msgstr "???????????? ????????? files ??????????????????"
 msgid "Close current interface"
 msgstr "???????????? ?????? ??????????????????(O)..."
 
-#: engines/phoenixvr/metaengine.cpp:107
+#: engines/phoenixvr/metaengine.cpp:116
 #: engines/wintermute/keymapper_tables.h:239
 #: engines/wintermute/keymapper_tables.h:262
 #: engines/wintermute/keymapper_tables.h:313
diff --git a/po/hu_HU.po b/po/hu_HU.po
index c6ff7f3e0ec..0ec54ddd0c0 100644
--- a/po/hu_HU.po
+++ b/po/hu_HU.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-11 13:12+0000\n"
+"POT-Creation-Date: 2026-06-11 22:10+0000\n"
 "PO-Revision-Date: 2025-03-07 00:18+0000\n"
 "Last-Translator: Hov??nszki Tam??s <gahex68379 at lofiey.com>\n"
 "Language-Team: Hungarian <https://translations.scummvm.org/projects/scummvm/"
@@ -6350,7 +6350,7 @@ msgstr "R2"
 #: backends/platform/sdl/miyoo/miyoo.cpp:114
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
-#: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
+#: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:110
 #: engines/scumm/metaengine.cpp:1232 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "Men??"
@@ -6730,7 +6730,7 @@ msgstr "Tesz"
 #: engines/grim/grim.cpp:621 engines/hdb/metaengine.cpp:251
 #: engines/hpl1/metaengine.cpp:94 engines/icb/icb.cpp:101
 #: engines/icb/icb.cpp:179 engines/petka/metaengine.cpp:173
-#: engines/phoenixvr/metaengine.cpp:73 engines/scumm/help.cpp:228
+#: engines/phoenixvr/metaengine.cpp:82 engines/scumm/help.cpp:228
 #: engines/scumm/help.cpp:237 engines/scumm/help.cpp:244
 #: engines/scumm/help.cpp:252 engines/sherlock/metaengine.cpp:435
 #: engines/sludge/keymapper_tables.h:58 engines/sludge/keymapper_tables.h:119
@@ -6878,7 +6878,7 @@ msgstr ""
 #: engines/got/metaengine.cpp:35 engines/groovie/metaengine.cpp:53
 #: engines/m4/metaengine.cpp:37 engines/mads/metaengine.cpp:116
 #: engines/nancy/metaengine.cpp:104 engines/neverhood/dialogs.cpp:47
-#: engines/pelrock/metaengine.cpp:35 engines/phoenixvr/metaengine.cpp:38
+#: engines/pelrock/metaengine.cpp:35 engines/phoenixvr/metaengine.cpp:47
 #: engines/sci/detection_options.h:105 engines/sherlock/metaengine.cpp:47
 #: engines/toltecs/metaengine.cpp:44 engines/trecision/metaengine.cpp:41
 #: engines/ultima/metaengine.cpp:56 engines/waynesworld/metaengine.cpp:37
@@ -6893,7 +6893,7 @@ msgstr "Eredeti ment??s/t??lt??s k??perny??k haszn??lata"
 #: engines/got/metaengine.cpp:36 engines/groovie/metaengine.cpp:54
 #: engines/m4/metaengine.cpp:38 engines/mads/metaengine.cpp:117
 #: engines/nancy/metaengine.cpp:105 engines/neverhood/dialogs.cpp:48
-#: engines/pelrock/metaengine.cpp:36 engines/phoenixvr/metaengine.cpp:39
+#: engines/pelrock/metaengine.cpp:36 engines/phoenixvr/metaengine.cpp:48
 #: engines/sci/detection_options.h:106 engines/sherlock/metaengine.cpp:48
 #: engines/toltecs/metaengine.cpp:45 engines/trecision/metaengine.cpp:42
 #: engines/ultima/metaengine.cpp:57 engines/waynesworld/metaengine.cpp:38
@@ -7508,7 +7508,7 @@ msgid "Show version"
 msgstr "Verzi?? megjelen??t??se"
 
 #: engines/asylum/metaengine.cpp:120 engines/crab/input/input.cpp:271
-#: engines/phoenixvr/metaengine.cpp:96 engines/queen/metaengine.cpp:253
+#: engines/phoenixvr/metaengine.cpp:105 engines/queen/metaengine.cpp:253
 #: engines/sludge/keymapper_tables.h:308 engines/sludge/keymapper_tables.h:404
 #: engines/sludge/keymapper_tables.h:471 engines/sludge/keymapper_tables.h:502
 #: engines/ultima/nuvie/metaengine.cpp:92
@@ -7517,7 +7517,7 @@ msgid "Quick load"
 msgstr "Gyorst??lt??s"
 
 #: engines/asylum/metaengine.cpp:125 engines/crab/input/input.cpp:266
-#: engines/phoenixvr/metaengine.cpp:91 engines/queen/metaengine.cpp:247
+#: engines/phoenixvr/metaengine.cpp:100 engines/queen/metaengine.cpp:247
 #: engines/sludge/keymapper_tables.h:64 engines/sludge/keymapper_tables.h:303
 #: engines/sludge/keymapper_tables.h:410 engines/sludge/keymapper_tables.h:465
 #: engines/sludge/keymapper_tables.h:496 engines/ultima/nuvie/metaengine.cpp:91
@@ -7843,7 +7843,7 @@ msgstr "Harcmodor V??lt??s"
 #. I18N: This keymap allows skipping video cutscenes
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
-#: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
+#: engines/phoenixvr/metaengine.cpp:94 engines/scumm/help.cpp:79
 #: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
@@ -8039,7 +8039,7 @@ msgstr "Nyom Adatb??zis"
 #: engines/freescape/games/dark/dark.cpp:236
 #: engines/freescape/games/driller/driller.cpp:129
 #: engines/freescape/games/eclipse/eclipse.cpp:340
-#: engines/mads/metaengine.cpp:403 engines/phoenixvr/metaengine.cpp:80
+#: engines/mads/metaengine.cpp:403 engines/phoenixvr/metaengine.cpp:89
 #: engines/saga/metaengine.cpp:437 engines/sherlock/metaengine.cpp:359
 #: engines/sherlock/metaengine.cpp:618 engines/sherlock/metaengine.cpp:852
 #: engines/stark/metaengine.cpp:280 engines/sword1/metaengine.cpp:295
@@ -8759,7 +8759,7 @@ msgstr ""
 
 #. I18N: Perform action on object where cursor points
 #: engines/darkseed/metaengine.cpp:42 engines/dragons/metaengine.cpp:135
-#: engines/phoenixvr/metaengine.cpp:66 engines/tinsel/metaengine.cpp:294
+#: engines/phoenixvr/metaengine.cpp:75 engines/tinsel/metaengine.cpp:294
 #: engines/twp/metaengine.cpp:150
 msgid "Action"
 msgstr "M??velet"
@@ -12717,7 +12717,7 @@ msgstr "Konzol megjelen??t??s / elrejt??s"
 msgid "Close current interface"
 msgstr "Egyszer?? eg??r interf??sz"
 
-#: engines/phoenixvr/metaengine.cpp:107
+#: engines/phoenixvr/metaengine.cpp:116
 #: engines/wintermute/keymapper_tables.h:239
 #: engines/wintermute/keymapper_tables.h:262
 #: engines/wintermute/keymapper_tables.h:313
diff --git a/po/it_IT.po b/po/it_IT.po
index d84f5497f65..59641737d7e 100644
--- a/po/it_IT.po
+++ b/po/it_IT.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-11 13:12+0000\n"
+"POT-Creation-Date: 2026-06-11 22:10+0000\n"
 "PO-Revision-Date: 2026-06-10 20:26+0000\n"
 "Last-Translator: Walter Agazzi <walter.agazzi at protonmail.com>\n"
 "Language-Team: Italian <https://translations.scummvm.org/projects/scummvm/"
@@ -6147,7 +6147,7 @@ msgstr "R2"
 #: backends/platform/sdl/miyoo/miyoo.cpp:114
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
-#: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
+#: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:110
 #: engines/scumm/metaengine.cpp:1232 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "Menu"
@@ -6523,7 +6523,7 @@ msgstr "Prendi"
 #: engines/grim/grim.cpp:621 engines/hdb/metaengine.cpp:251
 #: engines/hpl1/metaengine.cpp:94 engines/icb/icb.cpp:101
 #: engines/icb/icb.cpp:179 engines/petka/metaengine.cpp:173
-#: engines/phoenixvr/metaengine.cpp:73 engines/scumm/help.cpp:228
+#: engines/phoenixvr/metaengine.cpp:82 engines/scumm/help.cpp:228
 #: engines/scumm/help.cpp:237 engines/scumm/help.cpp:244
 #: engines/scumm/help.cpp:252 engines/sherlock/metaengine.cpp:435
 #: engines/sludge/keymapper_tables.h:58 engines/sludge/keymapper_tables.h:119
@@ -6672,7 +6672,7 @@ msgstr ""
 #: engines/got/metaengine.cpp:35 engines/groovie/metaengine.cpp:53
 #: engines/m4/metaengine.cpp:37 engines/mads/metaengine.cpp:116
 #: engines/nancy/metaengine.cpp:104 engines/neverhood/dialogs.cpp:47
-#: engines/pelrock/metaengine.cpp:35 engines/phoenixvr/metaengine.cpp:38
+#: engines/pelrock/metaengine.cpp:35 engines/phoenixvr/metaengine.cpp:47
 #: engines/sci/detection_options.h:105 engines/sherlock/metaengine.cpp:47
 #: engines/toltecs/metaengine.cpp:44 engines/trecision/metaengine.cpp:41
 #: engines/ultima/metaengine.cpp:56 engines/waynesworld/metaengine.cpp:37
@@ -6687,7 +6687,7 @@ msgstr "Usa schermate di salvataggio originali"
 #: engines/got/metaengine.cpp:36 engines/groovie/metaengine.cpp:54
 #: engines/m4/metaengine.cpp:38 engines/mads/metaengine.cpp:117
 #: engines/nancy/metaengine.cpp:105 engines/neverhood/dialogs.cpp:48
-#: engines/pelrock/metaengine.cpp:36 engines/phoenixvr/metaengine.cpp:39
+#: engines/pelrock/metaengine.cpp:36 engines/phoenixvr/metaengine.cpp:48
 #: engines/sci/detection_options.h:106 engines/sherlock/metaengine.cpp:48
 #: engines/toltecs/metaengine.cpp:45 engines/trecision/metaengine.cpp:42
 #: engines/ultima/metaengine.cpp:57 engines/waynesworld/metaengine.cpp:38
@@ -7298,7 +7298,7 @@ msgid "Show version"
 msgstr "Mostra versione"
 
 #: engines/asylum/metaengine.cpp:120 engines/crab/input/input.cpp:271
-#: engines/phoenixvr/metaengine.cpp:96 engines/queen/metaengine.cpp:253
+#: engines/phoenixvr/metaengine.cpp:105 engines/queen/metaengine.cpp:253
 #: engines/sludge/keymapper_tables.h:308 engines/sludge/keymapper_tables.h:404
 #: engines/sludge/keymapper_tables.h:471 engines/sludge/keymapper_tables.h:502
 #: engines/ultima/nuvie/metaengine.cpp:92
@@ -7307,7 +7307,7 @@ msgid "Quick load"
 msgstr "Carica Salvataggio rapido"
 
 #: engines/asylum/metaengine.cpp:125 engines/crab/input/input.cpp:266
-#: engines/phoenixvr/metaengine.cpp:91 engines/queen/metaengine.cpp:247
+#: engines/phoenixvr/metaengine.cpp:100 engines/queen/metaengine.cpp:247
 #: engines/sludge/keymapper_tables.h:64 engines/sludge/keymapper_tables.h:303
 #: engines/sludge/keymapper_tables.h:410 engines/sludge/keymapper_tables.h:465
 #: engines/sludge/keymapper_tables.h:496 engines/ultima/nuvie/metaengine.cpp:91
@@ -7624,7 +7624,7 @@ msgstr "Attiva / Disattiva modalit?? combattimento"
 #. I18N: This keymap allows skipping video cutscenes
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
-#: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
+#: engines/phoenixvr/metaengine.cpp:94 engines/scumm/help.cpp:79
 #: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
@@ -7804,7 +7804,7 @@ msgstr "Database indizi"
 #: engines/freescape/games/dark/dark.cpp:236
 #: engines/freescape/games/driller/driller.cpp:129
 #: engines/freescape/games/eclipse/eclipse.cpp:340
-#: engines/mads/metaengine.cpp:403 engines/phoenixvr/metaengine.cpp:80
+#: engines/mads/metaengine.cpp:403 engines/phoenixvr/metaengine.cpp:89
 #: engines/saga/metaengine.cpp:437 engines/sherlock/metaengine.cpp:359
 #: engines/sherlock/metaengine.cpp:618 engines/sherlock/metaengine.cpp:852
 #: engines/stark/metaengine.cpp:280 engines/sword1/metaengine.cpp:295
@@ -8442,7 +8442,7 @@ msgstr "Solo effetti della versione floppy"
 
 #. I18N: Perform action on object where cursor points
 #: engines/darkseed/metaengine.cpp:42 engines/dragons/metaengine.cpp:135
-#: engines/phoenixvr/metaengine.cpp:66 engines/tinsel/metaengine.cpp:294
+#: engines/phoenixvr/metaengine.cpp:75 engines/tinsel/metaengine.cpp:294
 #: engines/twp/metaengine.cpp:150
 msgid "Action"
 msgstr "Azione"
@@ -11993,7 +11993,7 @@ msgstr "Mostra/nascondi schermata opzioni"
 msgid "Close current interface"
 msgstr "Chiudi Interfaccia corrente"
 
-#: engines/phoenixvr/metaengine.cpp:107
+#: engines/phoenixvr/metaengine.cpp:116
 #: engines/wintermute/keymapper_tables.h:239
 #: engines/wintermute/keymapper_tables.h:262
 #: engines/wintermute/keymapper_tables.h:313
diff --git a/po/ja.po b/po/ja.po
index 10d68e213f2..4852d05d7fe 100644
--- a/po/ja.po
+++ b/po/ja.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-11 13:12+0000\n"
+"POT-Creation-Date: 2026-06-11 22:10+0000\n"
 "PO-Revision-Date: 2026-06-04 07:22+0000\n"
 "Last-Translator: VAN-Gluon <van.gluonp at gmail.com>\n"
 "Language-Team: Japanese <https://translations.scummvm.org/projects/scummvm/"
@@ -6001,7 +6001,7 @@ msgstr "R2"
 #: backends/platform/sdl/miyoo/miyoo.cpp:114
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
-#: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
+#: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:110
 #: engines/scumm/metaengine.cpp:1232 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "????????????"
@@ -6381,7 +6381,7 @@ msgstr "??????"
 #: engines/grim/grim.cpp:621 engines/hdb/metaengine.cpp:251
 #: engines/hpl1/metaengine.cpp:94 engines/icb/icb.cpp:101
 #: engines/icb/icb.cpp:179 engines/petka/metaengine.cpp:173
-#: engines/phoenixvr/metaengine.cpp:73 engines/scumm/help.cpp:228
+#: engines/phoenixvr/metaengine.cpp:82 engines/scumm/help.cpp:228
 #: engines/scumm/help.cpp:237 engines/scumm/help.cpp:244
 #: engines/scumm/help.cpp:252 engines/sherlock/metaengine.cpp:435
 #: engines/sludge/keymapper_tables.h:58 engines/sludge/keymapper_tables.h:119
@@ -6528,7 +6528,7 @@ msgstr ""
 #: engines/got/metaengine.cpp:35 engines/groovie/metaengine.cpp:53
 #: engines/m4/metaengine.cpp:37 engines/mads/metaengine.cpp:116
 #: engines/nancy/metaengine.cpp:104 engines/neverhood/dialogs.cpp:47
-#: engines/pelrock/metaengine.cpp:35 engines/phoenixvr/metaengine.cpp:38
+#: engines/pelrock/metaengine.cpp:35 engines/phoenixvr/metaengine.cpp:47
 #: engines/sci/detection_options.h:105 engines/sherlock/metaengine.cpp:47
 #: engines/toltecs/metaengine.cpp:44 engines/trecision/metaengine.cpp:41
 #: engines/ultima/metaengine.cpp:56 engines/waynesworld/metaengine.cpp:37
@@ -6543,7 +6543,7 @@ msgstr "???????????????????????????/??????????????????????????????"
 #: engines/got/metaengine.cpp:36 engines/groovie/metaengine.cpp:54
 #: engines/m4/metaengine.cpp:38 engines/mads/metaengine.cpp:117
 #: engines/nancy/metaengine.cpp:105 engines/neverhood/dialogs.cpp:48
-#: engines/pelrock/metaengine.cpp:36 engines/phoenixvr/metaengine.cpp:39
+#: engines/pelrock/metaengine.cpp:36 engines/phoenixvr/metaengine.cpp:48
 #: engines/sci/detection_options.h:106 engines/sherlock/metaengine.cpp:48
 #: engines/toltecs/metaengine.cpp:45 engines/trecision/metaengine.cpp:42
 #: engines/ultima/metaengine.cpp:57 engines/waynesworld/metaengine.cpp:38
@@ -7129,7 +7129,7 @@ msgid "Show version"
 msgstr "????????????????????????"
 
 #: engines/asylum/metaengine.cpp:120 engines/crab/input/input.cpp:271
-#: engines/phoenixvr/metaengine.cpp:96 engines/queen/metaengine.cpp:253
+#: engines/phoenixvr/metaengine.cpp:105 engines/queen/metaengine.cpp:253
 #: engines/sludge/keymapper_tables.h:308 engines/sludge/keymapper_tables.h:404
 #: engines/sludge/keymapper_tables.h:471 engines/sludge/keymapper_tables.h:502
 #: engines/ultima/nuvie/metaengine.cpp:92
@@ -7138,7 +7138,7 @@ msgid "Quick load"
 msgstr "?????????????????????"
 
 #: engines/asylum/metaengine.cpp:125 engines/crab/input/input.cpp:266
-#: engines/phoenixvr/metaengine.cpp:91 engines/queen/metaengine.cpp:247
+#: engines/phoenixvr/metaengine.cpp:100 engines/queen/metaengine.cpp:247
 #: engines/sludge/keymapper_tables.h:64 engines/sludge/keymapper_tables.h:303
 #: engines/sludge/keymapper_tables.h:410 engines/sludge/keymapper_tables.h:465
 #: engines/sludge/keymapper_tables.h:496 engines/ultima/nuvie/metaengine.cpp:91
@@ -7447,7 +7447,7 @@ msgstr "??????????????????"
 #. I18N: This keymap allows skipping video cutscenes
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
-#: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
+#: engines/phoenixvr/metaengine.cpp:94 engines/scumm/help.cpp:79
 #: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
@@ -7643,7 +7643,7 @@ msgstr "??????????????????????????????"
 #: engines/freescape/games/dark/dark.cpp:236
 #: engines/freescape/games/driller/driller.cpp:129
 #: engines/freescape/games/eclipse/eclipse.cpp:340
-#: engines/mads/metaengine.cpp:403 engines/phoenixvr/metaengine.cpp:80
+#: engines/mads/metaengine.cpp:403 engines/phoenixvr/metaengine.cpp:89
 #: engines/saga/metaengine.cpp:437 engines/sherlock/metaengine.cpp:359
 #: engines/sherlock/metaengine.cpp:618 engines/sherlock/metaengine.cpp:852
 #: engines/stark/metaengine.cpp:280 engines/sword1/metaengine.cpp:295
@@ -8364,7 +8364,7 @@ msgstr "FD??????SFX??????"
 
 #. I18N: Perform action on object where cursor points
 #: engines/darkseed/metaengine.cpp:42 engines/dragons/metaengine.cpp:135
-#: engines/phoenixvr/metaengine.cpp:66 engines/tinsel/metaengine.cpp:294
+#: engines/phoenixvr/metaengine.cpp:75 engines/tinsel/metaengine.cpp:294
 #: engines/twp/metaengine.cpp:150
 msgid "Action"
 msgstr "???????????????"
@@ -12347,7 +12347,7 @@ msgstr "????????????????????????/?????????"
 msgid "Close current interface"
 msgstr "?????????????????????????????????????????????"
 
-#: engines/phoenixvr/metaengine.cpp:107
+#: engines/phoenixvr/metaengine.cpp:116
 #: engines/wintermute/keymapper_tables.h:239
 #: engines/wintermute/keymapper_tables.h:262
 #: engines/wintermute/keymapper_tables.h:313
diff --git a/po/ka.po b/po/ka.po
index 230e77fc5bc..fa608855591 100644
--- a/po/ka.po
+++ b/po/ka.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-11 13:12+0000\n"
+"POT-Creation-Date: 2026-06-11 22:10+0000\n"
 "PO-Revision-Date: 2026-06-05 10:22+0000\n"
 "Last-Translator: NorwayFun <temuri.doghonadze at gmail.com>\n"
 "Language-Team: Georgian <https://translations.scummvm.org/projects/scummvm/"
@@ -5462,7 +5462,7 @@ msgstr "R2"
 #: backends/platform/sdl/miyoo/miyoo.cpp:114
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
-#: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
+#: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:110
 #: engines/scumm/metaengine.cpp:1232 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "???????????????"
@@ -5838,7 +5838,7 @@ msgstr "????????????"
 #: engines/grim/grim.cpp:621 engines/hdb/metaengine.cpp:251
 #: engines/hpl1/metaengine.cpp:94 engines/icb/icb.cpp:101
 #: engines/icb/icb.cpp:179 engines/petka/metaengine.cpp:173
-#: engines/phoenixvr/metaengine.cpp:73 engines/scumm/help.cpp:228
+#: engines/phoenixvr/metaengine.cpp:82 engines/scumm/help.cpp:228
 #: engines/scumm/help.cpp:237 engines/scumm/help.cpp:244
 #: engines/scumm/help.cpp:252 engines/sherlock/metaengine.cpp:435
 #: engines/sludge/keymapper_tables.h:58 engines/sludge/keymapper_tables.h:119
@@ -5977,7 +5977,7 @@ msgstr ""
 #: engines/got/metaengine.cpp:35 engines/groovie/metaengine.cpp:53
 #: engines/m4/metaengine.cpp:37 engines/mads/metaengine.cpp:116
 #: engines/nancy/metaengine.cpp:104 engines/neverhood/dialogs.cpp:47
-#: engines/pelrock/metaengine.cpp:35 engines/phoenixvr/metaengine.cpp:38
+#: engines/pelrock/metaengine.cpp:35 engines/phoenixvr/metaengine.cpp:47
 #: engines/sci/detection_options.h:105 engines/sherlock/metaengine.cpp:47
 #: engines/toltecs/metaengine.cpp:44 engines/trecision/metaengine.cpp:41
 #: engines/ultima/metaengine.cpp:56 engines/waynesworld/metaengine.cpp:37
@@ -5992,7 +5992,7 @@ msgstr "??????????????????????????? ?????????????????????/?????????????????????
 #: engines/got/metaengine.cpp:36 engines/groovie/metaengine.cpp:54
 #: engines/m4/metaengine.cpp:38 engines/mads/metaengine.cpp:117
 #: engines/nancy/metaengine.cpp:105 engines/neverhood/dialogs.cpp:48
-#: engines/pelrock/metaengine.cpp:36 engines/phoenixvr/metaengine.cpp:39
+#: engines/pelrock/metaengine.cpp:36 engines/phoenixvr/metaengine.cpp:48
 #: engines/sci/detection_options.h:106 engines/sherlock/metaengine.cpp:48
 #: engines/toltecs/metaengine.cpp:45 engines/trecision/metaengine.cpp:42
 #: engines/ultima/metaengine.cpp:57 engines/waynesworld/metaengine.cpp:38
@@ -6528,7 +6528,7 @@ msgid "Show version"
 msgstr "????????????????????? ?????????????????????"
 
 #: engines/asylum/metaengine.cpp:120 engines/crab/input/input.cpp:271
-#: engines/phoenixvr/metaengine.cpp:96 engines/queen/metaengine.cpp:253
+#: engines/phoenixvr/metaengine.cpp:105 engines/queen/metaengine.cpp:253
 #: engines/sludge/keymapper_tables.h:308 engines/sludge/keymapper_tables.h:404
 #: engines/sludge/keymapper_tables.h:471 engines/sludge/keymapper_tables.h:502
 #: engines/ultima/nuvie/metaengine.cpp:92
@@ -6537,7 +6537,7 @@ msgid "Quick load"
 msgstr "?????????????????? ???????????????????????????"
 
 #: engines/asylum/metaengine.cpp:125 engines/crab/input/input.cpp:266
-#: engines/phoenixvr/metaengine.cpp:91 engines/queen/metaengine.cpp:247
+#: engines/phoenixvr/metaengine.cpp:100 engines/queen/metaengine.cpp:247
 #: engines/sludge/keymapper_tables.h:64 engines/sludge/keymapper_tables.h:303
 #: engines/sludge/keymapper_tables.h:410 engines/sludge/keymapper_tables.h:465
 #: engines/sludge/keymapper_tables.h:496 engines/ultima/nuvie/metaengine.cpp:91
@@ -6829,7 +6829,7 @@ msgstr "????????????????????? ????????????????????????"
 #. I18N: This keymap allows skipping video cutscenes
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
-#: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
+#: engines/phoenixvr/metaengine.cpp:94 engines/scumm/help.cpp:79
 #: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
@@ -7009,7 +7009,7 @@ msgstr "?????????????????????????????? ????????????"
 #: engines/freescape/games/dark/dark.cpp:236
 #: engines/freescape/games/driller/driller.cpp:129
 #: engines/freescape/games/eclipse/eclipse.cpp:340
-#: engines/mads/metaengine.cpp:403 engines/phoenixvr/metaengine.cpp:80
+#: engines/mads/metaengine.cpp:403 engines/phoenixvr/metaengine.cpp:89
 #: engines/saga/metaengine.cpp:437 engines/sherlock/metaengine.cpp:359
 #: engines/sherlock/metaengine.cpp:618 engines/sherlock/metaengine.cpp:852
 #: engines/stark/metaengine.cpp:280 engines/sword1/metaengine.cpp:295
@@ -7626,7 +7626,7 @@ msgstr ""
 
 #. I18N: Perform action on object where cursor points
 #: engines/darkseed/metaengine.cpp:42 engines/dragons/metaengine.cpp:135
-#: engines/phoenixvr/metaengine.cpp:66 engines/tinsel/metaengine.cpp:294
+#: engines/phoenixvr/metaengine.cpp:75 engines/tinsel/metaengine.cpp:294
 #: engines/twp/metaengine.cpp:150
 msgid "Action"
 msgstr "?????????????????????"
@@ -10911,7 +10911,7 @@ msgstr "???????????????????????????????????? ????????????????????? ????????????
 msgid "Close current interface"
 msgstr "??????????????????????????? ????????????????????????????????? ?????????????????????"
 
-#: engines/phoenixvr/metaengine.cpp:107
+#: engines/phoenixvr/metaengine.cpp:116
 #: engines/wintermute/keymapper_tables.h:239
 #: engines/wintermute/keymapper_tables.h:262
 #: engines/wintermute/keymapper_tables.h:313
diff --git a/po/ko.po b/po/ko.po
index 12b4002b0c3..ad3398bfeb5 100644
--- a/po/ko.po
+++ b/po/ko.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-11 13:12+0000\n"
+"POT-Creation-Date: 2026-06-11 22:10+0000\n"
 "PO-Revision-Date: 2026-05-19 20:22+0000\n"
 "Last-Translator: Hoseok Seo <ddinghoya at gmail.com>\n"
 "Language-Team: Korean <https://translations.scummvm.org/projects/scummvm/"
@@ -6012,7 +6012,7 @@ msgstr "R2 ??????"
 #: backends/platform/sdl/miyoo/miyoo.cpp:114
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
-#: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
+#: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:110
 #: engines/scumm/metaengine.cpp:1232 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "??????"
@@ -6388,7 +6388,7 @@ msgstr "??????"
 #: engines/grim/grim.cpp:621 engines/hdb/metaengine.cpp:251
 #: engines/hpl1/metaengine.cpp:94 engines/icb/icb.cpp:101
 #: engines/icb/icb.cpp:179 engines/petka/metaengine.cpp:173
-#: engines/phoenixvr/metaengine.cpp:73 engines/scumm/help.cpp:228
+#: engines/phoenixvr/metaengine.cpp:82 engines/scumm/help.cpp:228
 #: engines/scumm/help.cpp:237 engines/scumm/help.cpp:244
 #: engines/scumm/help.cpp:252 engines/sherlock/metaengine.cpp:435
 #: engines/sludge/keymapper_tables.h:58 engines/sludge/keymapper_tables.h:119
@@ -6531,7 +6531,7 @@ msgstr ""
 #: engines/got/metaengine.cpp:35 engines/groovie/metaengine.cpp:53
 #: engines/m4/metaengine.cpp:37 engines/mads/metaengine.cpp:116
 #: engines/nancy/metaengine.cpp:104 engines/neverhood/dialogs.cpp:47
-#: engines/pelrock/metaengine.cpp:35 engines/phoenixvr/metaengine.cpp:38
+#: engines/pelrock/metaengine.cpp:35 engines/phoenixvr/metaengine.cpp:47
 #: engines/sci/detection_options.h:105 engines/sherlock/metaengine.cpp:47
 #: engines/toltecs/metaengine.cpp:44 engines/trecision/metaengine.cpp:41
 #: engines/ultima/metaengine.cpp:56 engines/waynesworld/metaengine.cpp:37
@@ -6546,7 +6546,7 @@ msgstr "?????? ??????/???????????? ?????? ??????"
 #: engines/got/metaengine.cpp:36 engines/groovie/metaengine.cpp:54
 #: engines/m4/metaengine.cpp:38 engines/mads/metaengine.cpp:117
 #: engines/nancy/metaengine.cpp:105 engines/neverhood/dialogs.cpp:48
-#: engines/pelrock/metaengine.cpp:36 engines/phoenixvr/metaengine.cpp:39
+#: engines/pelrock/metaengine.cpp:36 engines/phoenixvr/metaengine.cpp:48
 #: engines/sci/detection_options.h:106 engines/sherlock/metaengine.cpp:48
 #: engines/toltecs/metaengine.cpp:45 engines/trecision/metaengine.cpp:42
 #: engines/ultima/metaengine.cpp:57 engines/waynesworld/metaengine.cpp:38
@@ -7132,7 +7132,7 @@ msgid "Show version"
 msgstr "?????? ??????"
 
 #: engines/asylum/metaengine.cpp:120 engines/crab/input/input.cpp:271
-#: engines/phoenixvr/metaengine.cpp:96 engines/queen/metaengine.cpp:253
+#: engines/phoenixvr/metaengine.cpp:105 engines/queen/metaengine.cpp:253
 #: engines/sludge/keymapper_tables.h:308 engines/sludge/keymapper_tables.h:404
 #: engines/sludge/keymapper_tables.h:471 engines/sludge/keymapper_tables.h:502
 #: engines/ultima/nuvie/metaengine.cpp:92
@@ -7141,7 +7141,7 @@ msgid "Quick load"
 msgstr "?????? ????????????"
 
 #: engines/asylum/metaengine.cpp:125 engines/crab/input/input.cpp:266
-#: engines/phoenixvr/metaengine.cpp:91 engines/queen/metaengine.cpp:247
+#: engines/phoenixvr/metaengine.cpp:100 engines/queen/metaengine.cpp:247
 #: engines/sludge/keymapper_tables.h:64 engines/sludge/keymapper_tables.h:303
 #: engines/sludge/keymapper_tables.h:410 engines/sludge/keymapper_tables.h:465
 #: engines/sludge/keymapper_tables.h:496 engines/ultima/nuvie/metaengine.cpp:91
@@ -7443,7 +7443,7 @@ msgstr "?????? ??????"
 #. I18N: This keymap allows skipping video cutscenes
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
-#: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
+#: engines/phoenixvr/metaengine.cpp:94 engines/scumm/help.cpp:79
 #: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
@@ -7623,7 +7623,7 @@ msgstr "?????? ??????????????????"
 #: engines/freescape/games/dark/dark.cpp:236
 #: engines/freescape/games/driller/driller.cpp:129
 #: engines/freescape/games/eclipse/eclipse.cpp:340
-#: engines/mads/metaengine.cpp:403 engines/phoenixvr/metaengine.cpp:80
+#: engines/mads/metaengine.cpp:403 engines/phoenixvr/metaengine.cpp:89
 #: engines/saga/metaengine.cpp:437 engines/sherlock/metaengine.cpp:359
 #: engines/sherlock/metaengine.cpp:618 engines/sherlock/metaengine.cpp:852
 #: engines/stark/metaengine.cpp:280 engines/sword1/metaengine.cpp:295
@@ -8260,7 +8260,7 @@ msgstr "????????? ?????? SFX???"
 
 #. I18N: Perform action on object where cursor points
 #: engines/darkseed/metaengine.cpp:42 engines/dragons/metaengine.cpp:135
-#: engines/phoenixvr/metaengine.cpp:66 engines/tinsel/metaengine.cpp:294
+#: engines/phoenixvr/metaengine.cpp:75 engines/tinsel/metaengine.cpp:294
 #: engines/twp/metaengine.cpp:150
 msgid "Action"
 msgstr "??????"
@@ -11730,7 +11730,7 @@ msgstr "?????? ?????? ??????/?????????"
 msgid "Close current interface"
 msgstr "?????? ??????????????? ??????"
 
-#: engines/phoenixvr/metaengine.cpp:107
+#: engines/phoenixvr/metaengine.cpp:116
 #: engines/wintermute/keymapper_tables.h:239
 #: engines/wintermute/keymapper_tables.h:262
 #: engines/wintermute/keymapper_tables.h:313
diff --git a/po/nb_NO.po b/po/nb_NO.po
index 41d8ef8aadc..f3527d9f12f 100644
--- a/po/nb_NO.po
+++ b/po/nb_NO.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-11 13:12+0000\n"
+"POT-Creation-Date: 2026-06-11 22:10+0000\n"
 "PO-Revision-Date: 2026-02-07 15:22+0000\n"
 "Last-Translator: Einar Johan Tr??an S??m??en <einarjohants at gmail.com>\n"
 "Language-Team: Norwegian Bokm??l <https://translations.scummvm.org/projects/"
@@ -6411,7 +6411,7 @@ msgstr "R2"
 #: backends/platform/sdl/miyoo/miyoo.cpp:114
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
-#: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
+#: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:110
 #: engines/scumm/metaengine.cpp:1232 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "Meny"
@@ -6791,7 +6791,7 @@ msgstr "Ta"
 #: engines/grim/grim.cpp:621 engines/hdb/metaengine.cpp:251
 #: engines/hpl1/metaengine.cpp:94 engines/icb/icb.cpp:101
 #: engines/icb/icb.cpp:179 engines/petka/metaengine.cpp:173
-#: engines/phoenixvr/metaengine.cpp:73 engines/scumm/help.cpp:228
+#: engines/phoenixvr/metaengine.cpp:82 engines/scumm/help.cpp:228
 #: engines/scumm/help.cpp:237 engines/scumm/help.cpp:244
 #: engines/scumm/help.cpp:252 engines/sherlock/metaengine.cpp:435
 #: engines/sludge/keymapper_tables.h:58 engines/sludge/keymapper_tables.h:119
@@ -6941,7 +6941,7 @@ msgstr ""
 #: engines/got/metaengine.cpp:35 engines/groovie/metaengine.cpp:53
 #: engines/m4/metaengine.cpp:37 engines/mads/metaengine.cpp:116
 #: engines/nancy/metaengine.cpp:104 engines/neverhood/dialogs.cpp:47
-#: engines/pelrock/metaengine.cpp:35 engines/phoenixvr/metaengine.cpp:38
+#: engines/pelrock/metaengine.cpp:35 engines/phoenixvr/metaengine.cpp:47
 #: engines/sci/detection_options.h:105 engines/sherlock/metaengine.cpp:47
 #: engines/toltecs/metaengine.cpp:44 engines/trecision/metaengine.cpp:41
 #: engines/ultima/metaengine.cpp:56 engines/waynesworld/metaengine.cpp:37
@@ -6956,7 +6956,7 @@ msgstr "Bruk originale lagre/laste-skjermer"
 #: engines/got/metaengine.cpp:36 engines/groovie/metaengine.cpp:54
 #: engines/m4/metaengine.cpp:38 engines/mads/metaengine.cpp:117
 #: engines/nancy/metaengine.cpp:105 engines/neverhood/dialogs.cpp:48
-#: engines/pelrock/metaengine.cpp:36 engines/phoenixvr/metaengine.cpp:39
+#: engines/pelrock/metaengine.cpp:36 engines/phoenixvr/metaengine.cpp:48
 #: engines/sci/detection_options.h:106 engines/sherlock/metaengine.cpp:48
 #: engines/toltecs/metaengine.cpp:45 engines/trecision/metaengine.cpp:42
 #: engines/ultima/metaengine.cpp:57 engines/waynesworld/metaengine.cpp:38
@@ -7569,7 +7569,7 @@ msgid "Show version"
 msgstr "Vis versjonsnummer"
 
 #: engines/asylum/metaengine.cpp:120 engines/crab/input/input.cpp:271
-#: engines/phoenixvr/metaengine.cpp:96 engines/queen/metaengine.cpp:253
+#: engines/phoenixvr/metaengine.cpp:105 engines/queen/metaengine.cpp:253
 #: engines/sludge/keymapper_tables.h:308 engines/sludge/keymapper_tables.h:404
 #: engines/sludge/keymapper_tables.h:471 engines/sludge/keymapper_tables.h:502
 #: engines/ultima/nuvie/metaengine.cpp:92
@@ -7578,7 +7578,7 @@ msgid "Quick load"
 msgstr "Hurtiglasting"
 
 #: engines/asylum/metaengine.cpp:125 engines/crab/input/input.cpp:266
-#: engines/phoenixvr/metaengine.cpp:91 engines/queen/metaengine.cpp:247
+#: engines/phoenixvr/metaengine.cpp:100 engines/queen/metaengine.cpp:247
 #: engines/sludge/keymapper_tables.h:64 engines/sludge/keymapper_tables.h:303
 #: engines/sludge/keymapper_tables.h:410 engines/sludge/keymapper_tables.h:465
 #: engines/sludge/keymapper_tables.h:496 engines/ultima/nuvie/metaengine.cpp:91
@@ -7909,7 +7909,7 @@ msgstr "Skru kampmodus av/p??"
 #. I18N: This keymap allows skipping video cutscenes
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
-#: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
+#: engines/phoenixvr/metaengine.cpp:94 engines/scumm/help.cpp:79
 #: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
@@ -8105,7 +8105,7 @@ msgstr "Ledetr??dsdatabase"
 #: engines/freescape/games/dark/dark.cpp:236
 #: engines/freescape/games/driller/driller.cpp:129
 #: engines/freescape/games/eclipse/eclipse.cpp:340
-#: engines/mads/metaengine.cpp:403 engines/phoenixvr/metaengine.cpp:80
+#: engines/mads/metaengine.cpp:403 engines/phoenixvr/metaengine.cpp:89
 #: engines/saga/metaengine.cpp:437 engines/sherlock/metaengine.cpp:359
 #: engines/sherlock/metaengine.cpp:618 engines/sherlock/metaengine.cpp:852
 #: engines/stark/metaengine.cpp:280 engines/sword1/metaengine.cpp:295
@@ -8833,7 +8833,7 @@ msgstr ""
 
 #. I18N: Perform action on object where cursor points
 #: engines/darkseed/metaengine.cpp:42 engines/dragons/metaengine.cpp:135
-#: engines/phoenixvr/metaengine.cpp:66 engines/tinsel/metaengine.cpp:294
+#: engines/phoenixvr/metaengine.cpp:75 engines/tinsel/metaengine.cpp:294
 #: engines/twp/metaengine.cpp:150
 msgid "Action"
 msgstr "Handling"
@@ -12794,7 +12794,7 @@ msgstr "Vis / Skjul konsollen"
 msgid "Close current interface"
 msgstr "Enkelt grensesnitt for mus"
 
-#: engines/phoenixvr/metaengine.cpp:107
+#: engines/phoenixvr/metaengine.cpp:116
 #: engines/wintermute/keymapper_tables.h:239
 #: engines/wintermute/keymapper_tables.h:262
 #: engines/wintermute/keymapper_tables.h:313
diff --git a/po/nl_NL.po b/po/nl_NL.po
index 74592e5f266..474cce9c45e 100644
--- a/po/nl_NL.po
+++ b/po/nl_NL.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.9.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-11 13:12+0000\n"
+"POT-Creation-Date: 2026-06-11 22:10+0000\n"
 "PO-Revision-Date: 2026-02-11 14:23+0000\n"
 "Last-Translator: SecularSteve <fairfull.playing at gmail.com>\n"
 "Language-Team: Dutch <https://translations.scummvm.org/projects/scummvm/"
@@ -6086,7 +6086,7 @@ msgstr "R2"
 #: backends/platform/sdl/miyoo/miyoo.cpp:114
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
-#: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
+#: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:110
 #: engines/scumm/metaengine.cpp:1232 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "Menu"
@@ -6462,7 +6462,7 @@ msgstr "Pak"
 #: engines/grim/grim.cpp:621 engines/hdb/metaengine.cpp:251
 #: engines/hpl1/metaengine.cpp:94 engines/icb/icb.cpp:101
 #: engines/icb/icb.cpp:179 engines/petka/metaengine.cpp:173
-#: engines/phoenixvr/metaengine.cpp:73 engines/scumm/help.cpp:228
+#: engines/phoenixvr/metaengine.cpp:82 engines/scumm/help.cpp:228
 #: engines/scumm/help.cpp:237 engines/scumm/help.cpp:244
 #: engines/scumm/help.cpp:252 engines/sherlock/metaengine.cpp:435
 #: engines/sludge/keymapper_tables.h:58 engines/sludge/keymapper_tables.h:119
@@ -6609,7 +6609,7 @@ msgstr ""
 #: engines/got/metaengine.cpp:35 engines/groovie/metaengine.cpp:53
 #: engines/m4/metaengine.cpp:37 engines/mads/metaengine.cpp:116
 #: engines/nancy/metaengine.cpp:104 engines/neverhood/dialogs.cpp:47
-#: engines/pelrock/metaengine.cpp:35 engines/phoenixvr/metaengine.cpp:38
+#: engines/pelrock/metaengine.cpp:35 engines/phoenixvr/metaengine.cpp:47
 #: engines/sci/detection_options.h:105 engines/sherlock/metaengine.cpp:47
 #: engines/toltecs/metaengine.cpp:44 engines/trecision/metaengine.cpp:41
 #: engines/ultima/metaengine.cpp:56 engines/waynesworld/metaengine.cpp:37
@@ -6624,7 +6624,7 @@ msgstr "Gebruik originele opslaan/laad schermen"
 #: engines/got/metaengine.cpp:36 engines/groovie/metaengine.cpp:54
 #: engines/m4/metaengine.cpp:38 engines/mads/metaengine.cpp:117
 #: engines/nancy/metaengine.cpp:105 engines/neverhood/dialogs.cpp:48
-#: engines/pelrock/metaengine.cpp:36 engines/phoenixvr/metaengine.cpp:39
+#: engines/pelrock/metaengine.cpp:36 engines/phoenixvr/metaengine.cpp:48
 #: engines/sci/detection_options.h:106 engines/sherlock/metaengine.cpp:48
 #: engines/toltecs/metaengine.cpp:45 engines/trecision/metaengine.cpp:42
 #: engines/ultima/metaengine.cpp:57 engines/waynesworld/metaengine.cpp:38
@@ -7228,7 +7228,7 @@ msgid "Show version"
 msgstr "Toon versie"
 
 #: engines/asylum/metaengine.cpp:120 engines/crab/input/input.cpp:271
-#: engines/phoenixvr/metaengine.cpp:96 engines/queen/metaengine.cpp:253
+#: engines/phoenixvr/metaengine.cpp:105 engines/queen/metaengine.cpp:253
 #: engines/sludge/keymapper_tables.h:308 engines/sludge/keymapper_tables.h:404
 #: engines/sludge/keymapper_tables.h:471 engines/sludge/keymapper_tables.h:502
 #: engines/ultima/nuvie/metaengine.cpp:92
@@ -7237,7 +7237,7 @@ msgid "Quick load"
 msgstr "Snel laden"
 
 #: engines/asylum/metaengine.cpp:125 engines/crab/input/input.cpp:266
-#: engines/phoenixvr/metaengine.cpp:91 engines/queen/metaengine.cpp:247
+#: engines/phoenixvr/metaengine.cpp:100 engines/queen/metaengine.cpp:247
 #: engines/sludge/keymapper_tables.h:64 engines/sludge/keymapper_tables.h:303
 #: engines/sludge/keymapper_tables.h:410 engines/sludge/keymapper_tables.h:465
 #: engines/sludge/keymapper_tables.h:496 engines/ultima/nuvie/metaengine.cpp:91
@@ -7551,7 +7551,7 @@ msgstr "Schakel gevecht in/uit"
 #. I18N: This keymap allows skipping video cutscenes
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
-#: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
+#: engines/phoenixvr/metaengine.cpp:94 engines/scumm/help.cpp:79
 #: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
@@ -7731,7 +7731,7 @@ msgstr "Aanwijzingen database"
 #: engines/freescape/games/dark/dark.cpp:236
 #: engines/freescape/games/driller/driller.cpp:129
 #: engines/freescape/games/eclipse/eclipse.cpp:340
-#: engines/mads/metaengine.cpp:403 engines/phoenixvr/metaengine.cpp:80
+#: engines/mads/metaengine.cpp:403 engines/phoenixvr/metaengine.cpp:89
 #: engines/saga/metaengine.cpp:437 engines/sherlock/metaengine.cpp:359
 #: engines/sherlock/metaengine.cpp:618 engines/sherlock/metaengine.cpp:852
 #: engines/stark/metaengine.cpp:280 engines/sword1/metaengine.cpp:295
@@ -8405,7 +8405,7 @@ msgstr "Floppy-versie alleen SFX"
 
 #. I18N: Perform action on object where cursor points
 #: engines/darkseed/metaengine.cpp:42 engines/dragons/metaengine.cpp:135
-#: engines/phoenixvr/metaengine.cpp:66 engines/tinsel/metaengine.cpp:294
+#: engines/phoenixvr/metaengine.cpp:75 engines/tinsel/metaengine.cpp:294
 #: engines/twp/metaengine.cpp:150
 msgid "Action"
 msgstr "Actie"
@@ -12039,7 +12039,7 @@ msgstr "Optiescherm weergeven/verbergen"
 msgid "Close current interface"
 msgstr "Huidige interface sluiten"
 
-#: engines/phoenixvr/metaengine.cpp:107
+#: engines/phoenixvr/metaengine.cpp:116
 #: engines/wintermute/keymapper_tables.h:239
 #: engines/wintermute/keymapper_tables.h:262
 #: engines/wintermute/keymapper_tables.h:313
diff --git a/po/nn_NO.po b/po/nn_NO.po
index 2c21ef97cd2..2a925c388e5 100644
--- a/po/nn_NO.po
+++ b/po/nn_NO.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-11 13:12+0000\n"
+"POT-Creation-Date: 2026-06-11 22:10+0000\n"
 "PO-Revision-Date: 2023-12-14 14:28+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Norwegian Nynorsk <https://translations.scummvm.org/projects/"
@@ -5813,7 +5813,7 @@ msgstr ""
 #: backends/platform/sdl/miyoo/miyoo.cpp:114
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
-#: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
+#: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:110
 #: engines/scumm/metaengine.cpp:1232 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "Meny"
@@ -6206,7 +6206,7 @@ msgstr "Ta av (klede)"
 #: engines/grim/grim.cpp:621 engines/hdb/metaengine.cpp:251
 #: engines/hpl1/metaengine.cpp:94 engines/icb/icb.cpp:101
 #: engines/icb/icb.cpp:179 engines/petka/metaengine.cpp:173
-#: engines/phoenixvr/metaengine.cpp:73 engines/scumm/help.cpp:228
+#: engines/phoenixvr/metaengine.cpp:82 engines/scumm/help.cpp:228
 #: engines/scumm/help.cpp:237 engines/scumm/help.cpp:244
 #: engines/scumm/help.cpp:252 engines/sherlock/metaengine.cpp:435
 #: engines/sludge/keymapper_tables.h:58 engines/sludge/keymapper_tables.h:119
@@ -6353,7 +6353,7 @@ msgstr ""
 #: engines/got/metaengine.cpp:35 engines/groovie/metaengine.cpp:53
 #: engines/m4/metaengine.cpp:37 engines/mads/metaengine.cpp:116
 #: engines/nancy/metaengine.cpp:104 engines/neverhood/dialogs.cpp:47
-#: engines/pelrock/metaengine.cpp:35 engines/phoenixvr/metaengine.cpp:38
+#: engines/pelrock/metaengine.cpp:35 engines/phoenixvr/metaengine.cpp:47
 #: engines/sci/detection_options.h:105 engines/sherlock/metaengine.cpp:47
 #: engines/toltecs/metaengine.cpp:44 engines/trecision/metaengine.cpp:41
 #: engines/ultima/metaengine.cpp:56 engines/waynesworld/metaengine.cpp:37
@@ -6368,7 +6368,7 @@ msgstr "Nytt opprinnelege skjermar for lagring/lasting"
 #: engines/got/metaengine.cpp:36 engines/groovie/metaengine.cpp:54
 #: engines/m4/metaengine.cpp:38 engines/mads/metaengine.cpp:117
 #: engines/nancy/metaengine.cpp:105 engines/neverhood/dialogs.cpp:48
-#: engines/pelrock/metaengine.cpp:36 engines/phoenixvr/metaengine.cpp:39
+#: engines/pelrock/metaengine.cpp:36 engines/phoenixvr/metaengine.cpp:48
 #: engines/sci/detection_options.h:106 engines/sherlock/metaengine.cpp:48
 #: engines/toltecs/metaengine.cpp:45 engines/trecision/metaengine.cpp:42
 #: engines/ultima/metaengine.cpp:57 engines/waynesworld/metaengine.cpp:38
@@ -6952,7 +6952,7 @@ msgid "Show version"
 msgstr "Syn objektmerkelappar"
 
 #: engines/asylum/metaengine.cpp:120 engines/crab/input/input.cpp:271
-#: engines/phoenixvr/metaengine.cpp:96 engines/queen/metaengine.cpp:253
+#: engines/phoenixvr/metaengine.cpp:105 engines/queen/metaengine.cpp:253
 #: engines/sludge/keymapper_tables.h:308 engines/sludge/keymapper_tables.h:404
 #: engines/sludge/keymapper_tables.h:471 engines/sludge/keymapper_tables.h:502
 #: engines/ultima/nuvie/metaengine.cpp:92
@@ -6962,7 +6962,7 @@ msgid "Quick load"
 msgstr "Klikkmodus"
 
 #: engines/asylum/metaengine.cpp:125 engines/crab/input/input.cpp:266
-#: engines/phoenixvr/metaengine.cpp:91 engines/queen/metaengine.cpp:247
+#: engines/phoenixvr/metaengine.cpp:100 engines/queen/metaengine.cpp:247
 #: engines/sludge/keymapper_tables.h:64 engines/sludge/keymapper_tables.h:303
 #: engines/sludge/keymapper_tables.h:410 engines/sludge/keymapper_tables.h:465
 #: engines/sludge/keymapper_tables.h:496 engines/ultima/nuvie/metaengine.cpp:91
@@ -7287,7 +7287,7 @@ msgstr "Veksle musl??sing"
 #. I18N: This keymap allows skipping video cutscenes
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
-#: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
+#: engines/phoenixvr/metaengine.cpp:94 engines/scumm/help.cpp:79
 #: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
@@ -7477,7 +7477,7 @@ msgstr ""
 #: engines/freescape/games/dark/dark.cpp:236
 #: engines/freescape/games/driller/driller.cpp:129
 #: engines/freescape/games/eclipse/eclipse.cpp:340
-#: engines/mads/metaengine.cpp:403 engines/phoenixvr/metaengine.cpp:80
+#: engines/mads/metaengine.cpp:403 engines/phoenixvr/metaengine.cpp:89
 #: engines/saga/metaengine.cpp:437 engines/sherlock/metaengine.cpp:359
 #: engines/sherlock/metaengine.cpp:618 engines/sherlock/metaengine.cpp:852
 #: engines/stark/metaengine.cpp:280 engines/sword1/metaengine.cpp:295
@@ -8203,7 +8203,7 @@ msgstr ""
 
 #. I18N: Perform action on object where cursor points
 #: engines/darkseed/metaengine.cpp:42 engines/dragons/metaengine.cpp:135
-#: engines/phoenixvr/metaengine.cpp:66 engines/tinsel/metaengine.cpp:294
+#: engines/phoenixvr/metaengine.cpp:75 engines/tinsel/metaengine.cpp:294
 #: engines/twp/metaengine.cpp:150
 #, fuzzy
 msgid "Action"
@@ -12004,7 +12004,7 @@ msgstr "Vis / Skjul konsoll"
 msgid "Close current interface"
 msgstr "Berre Tale"
 
-#: engines/phoenixvr/metaengine.cpp:107
+#: engines/phoenixvr/metaengine.cpp:116
 #: engines/wintermute/keymapper_tables.h:239
 #: engines/wintermute/keymapper_tables.h:262
 #: engines/wintermute/keymapper_tables.h:313
diff --git a/po/pl_PL.po b/po/pl_PL.po
index f28023a042b..8c8f1586816 100644
--- a/po/pl_PL.po
+++ b/po/pl_PL.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-11 13:12+0000\n"
+"POT-Creation-Date: 2026-06-11 22:10+0000\n"
 "PO-Revision-Date: 2026-04-17 00:22+0000\n"
 "Last-Translator: SkiffPL <skiff at windowslive.com>\n"
 "Language-Team: Polish <https://translations.scummvm.org/projects/scummvm/"
@@ -6098,7 +6098,7 @@ msgstr "R2"
 #: backends/platform/sdl/miyoo/miyoo.cpp:114
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
-#: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
+#: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:110
 #: engines/scumm/metaengine.cpp:1232 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "Menu"
@@ -6474,7 +6474,7 @@ msgstr "We??"
 #: engines/grim/grim.cpp:621 engines/hdb/metaengine.cpp:251
 #: engines/hpl1/metaengine.cpp:94 engines/icb/icb.cpp:101
 #: engines/icb/icb.cpp:179 engines/petka/metaengine.cpp:173
-#: engines/phoenixvr/metaengine.cpp:73 engines/scumm/help.cpp:228
+#: engines/phoenixvr/metaengine.cpp:82 engines/scumm/help.cpp:228
 #: engines/scumm/help.cpp:237 engines/scumm/help.cpp:244
 #: engines/scumm/help.cpp:252 engines/sherlock/metaengine.cpp:435
 #: engines/sludge/keymapper_tables.h:58 engines/sludge/keymapper_tables.h:119
@@ -6623,7 +6623,7 @@ msgstr ""
 #: engines/got/metaengine.cpp:35 engines/groovie/metaengine.cpp:53
 #: engines/m4/metaengine.cpp:37 engines/mads/metaengine.cpp:116
 #: engines/nancy/metaengine.cpp:104 engines/neverhood/dialogs.cpp:47
-#: engines/pelrock/metaengine.cpp:35 engines/phoenixvr/metaengine.cpp:38
+#: engines/pelrock/metaengine.cpp:35 engines/phoenixvr/metaengine.cpp:47
 #: engines/sci/detection_options.h:105 engines/sherlock/metaengine.cpp:47
 #: engines/toltecs/metaengine.cpp:44 engines/trecision/metaengine.cpp:41
 #: engines/ultima/metaengine.cpp:56 engines/waynesworld/metaengine.cpp:37
@@ -6638,7 +6638,7 @@ msgstr "U??yj oryginalnych ekran??w wczytywania/zapisywania"
 #: engines/got/metaengine.cpp:36 engines/groovie/metaengine.cpp:54
 #: engines/m4/metaengine.cpp:38 engines/mads/metaengine.cpp:117
 #: engines/nancy/metaengine.cpp:105 engines/neverhood/dialogs.cpp:48
-#: engines/pelrock/metaengine.cpp:36 engines/phoenixvr/metaengine.cpp:39
+#: engines/pelrock/metaengine.cpp:36 engines/phoenixvr/metaengine.cpp:48
 #: engines/sci/detection_options.h:106 engines/sherlock/metaengine.cpp:48
 #: engines/toltecs/metaengine.cpp:45 engines/trecision/metaengine.cpp:42
 #: engines/ultima/metaengine.cpp:57 engines/waynesworld/metaengine.cpp:38
@@ -7241,7 +7241,7 @@ msgid "Show version"
 msgstr "Pokazuj wersj??"
 
 #: engines/asylum/metaengine.cpp:120 engines/crab/input/input.cpp:271
-#: engines/phoenixvr/metaengine.cpp:96 engines/queen/metaengine.cpp:253
+#: engines/phoenixvr/metaengine.cpp:105 engines/queen/metaengine.cpp:253
 #: engines/sludge/keymapper_tables.h:308 engines/sludge/keymapper_tables.h:404
 #: engines/sludge/keymapper_tables.h:471 engines/sludge/keymapper_tables.h:502
 #: engines/ultima/nuvie/metaengine.cpp:92
@@ -7250,7 +7250,7 @@ msgid "Quick load"
 msgstr "Szybkie wczytywanie"
 
 #: engines/asylum/metaengine.cpp:125 engines/crab/input/input.cpp:266
-#: engines/phoenixvr/metaengine.cpp:91 engines/queen/metaengine.cpp:247
+#: engines/phoenixvr/metaengine.cpp:100 engines/queen/metaengine.cpp:247
 #: engines/sludge/keymapper_tables.h:64 engines/sludge/keymapper_tables.h:303
 #: engines/sludge/keymapper_tables.h:410 engines/sludge/keymapper_tables.h:465
 #: engines/sludge/keymapper_tables.h:496 engines/ultima/nuvie/metaengine.cpp:91
@@ -7565,7 +7565,7 @@ msgstr "Prze????cz tryb walki"
 #. I18N: This keymap allows skipping video cutscenes
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
-#: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
+#: engines/phoenixvr/metaengine.cpp:94 engines/scumm/help.cpp:79
 #: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
@@ -7745,7 +7745,7 @@ msgstr "Baza danych wskaz??wek"
 #: engines/freescape/games/dark/dark.cpp:236
 #: engines/freescape/games/driller/driller.cpp:129
 #: engines/freescape/games/eclipse/eclipse.cpp:340
-#: engines/mads/metaengine.cpp:403 engines/phoenixvr/metaengine.cpp:80
+#: engines/mads/metaengine.cpp:403 engines/phoenixvr/metaengine.cpp:89
 #: engines/saga/metaengine.cpp:437 engines/sherlock/metaengine.cpp:359
 #: engines/sherlock/metaengine.cpp:618 engines/sherlock/metaengine.cpp:852
 #: engines/stark/metaengine.cpp:280 engines/sword1/metaengine.cpp:295
@@ -8385,7 +8385,7 @@ msgstr ""
 
 #. I18N: Perform action on object where cursor points
 #: engines/darkseed/metaengine.cpp:42 engines/dragons/metaengine.cpp:135
-#: engines/phoenixvr/metaengine.cpp:66 engines/tinsel/metaengine.cpp:294
+#: engines/phoenixvr/metaengine.cpp:75 engines/tinsel/metaengine.cpp:294
 #: engines/twp/metaengine.cpp:150
 msgid "Action"
 msgstr "Akcja"
@@ -12209,7 +12209,7 @@ msgstr "Schowaj / poka?? konsol??"
 msgid "Close current interface"
 msgstr "Prosty interfejs dla myszy"
 
-#: engines/phoenixvr/metaengine.cpp:107
+#: engines/phoenixvr/metaengine.cpp:116
 #: engines/wintermute/keymapper_tables.h:239
 #: engines/wintermute/keymapper_tables.h:262
 #: engines/wintermute/keymapper_tables.h:313
diff --git a/po/pt_BR.po b/po/pt_BR.po
index dd7f68ef5be..1486a7c841e 100644
--- a/po/pt_BR.po
+++ b/po/pt_BR.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-11 13:12+0000\n"
+"POT-Creation-Date: 2026-06-11 22:10+0000\n"
 "PO-Revision-Date: 2024-11-16 02:13+0000\n"
 "Last-Translator: Marcel Souza Lemes <marcosoutsider at gmail.com>\n"
 "Language-Team: Portuguese (Brazil) <https://translations.scummvm.org/"
@@ -6375,7 +6375,7 @@ msgstr "R2"
 #: backends/platform/sdl/miyoo/miyoo.cpp:114
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
-#: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
+#: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:110
 #: engines/scumm/metaengine.cpp:1232 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "Menu"
@@ -6755,7 +6755,7 @@ msgstr "Pegar"
 #: engines/grim/grim.cpp:621 engines/hdb/metaengine.cpp:251
 #: engines/hpl1/metaengine.cpp:94 engines/icb/icb.cpp:101
 #: engines/icb/icb.cpp:179 engines/petka/metaengine.cpp:173
-#: engines/phoenixvr/metaengine.cpp:73 engines/scumm/help.cpp:228
+#: engines/phoenixvr/metaengine.cpp:82 engines/scumm/help.cpp:228
 #: engines/scumm/help.cpp:237 engines/scumm/help.cpp:244
 #: engines/scumm/help.cpp:252 engines/sherlock/metaengine.cpp:435
 #: engines/sludge/keymapper_tables.h:58 engines/sludge/keymapper_tables.h:119
@@ -6905,7 +6905,7 @@ msgstr ""
 #: engines/got/metaengine.cpp:35 engines/groovie/metaengine.cpp:53
 #: engines/m4/metaengine.cpp:37 engines/mads/metaengine.cpp:116
 #: engines/nancy/metaengine.cpp:104 engines/neverhood/dialogs.cpp:47
-#: engines/pelrock/metaengine.cpp:35 engines/phoenixvr/metaengine.cpp:38
+#: engines/pelrock/metaengine.cpp:35 engines/phoenixvr/metaengine.cpp:47
 #: engines/sci/detection_options.h:105 engines/sherlock/metaengine.cpp:47
 #: engines/toltecs/metaengine.cpp:44 engines/trecision/metaengine.cpp:41
 #: engines/ultima/metaengine.cpp:56 engines/waynesworld/metaengine.cpp:37
@@ -6920,7 +6920,7 @@ msgstr "Utilizar telas de salvar/carregar originais"
 #: engines/got/metaengine.cpp:36 engines/groovie/metaengine.cpp:54
 #: engines/m4/metaengine.cpp:38 engines/mads/metaengine.cpp:117
 #: engines/nancy/metaengine.cpp:105 engines/neverhood/dialogs.cpp:48
-#: engines/pelrock/metaengine.cpp:36 engines/phoenixvr/metaengine.cpp:39
+#: engines/pelrock/metaengine.cpp:36 engines/phoenixvr/metaengine.cpp:48
 #: engines/sci/detection_options.h:106 engines/sherlock/metaengine.cpp:48
 #: engines/toltecs/metaengine.cpp:45 engines/trecision/metaengine.cpp:42
 #: engines/ultima/metaengine.cpp:57 engines/waynesworld/metaengine.cpp:38
@@ -7537,7 +7537,7 @@ msgid "Show version"
 msgstr "Exibir vers??o"
 
 #: engines/asylum/metaengine.cpp:120 engines/crab/input/input.cpp:271
-#: engines/phoenixvr/metaengine.cpp:96 engines/queen/metaengine.cpp:253
+#: engines/phoenixvr/metaengine.cpp:105 engines/queen/metaengine.cpp:253
 #: engines/sludge/keymapper_tables.h:308 engines/sludge/keymapper_tables.h:404
 #: engines/sludge/keymapper_tables.h:471 engines/sludge/keymapper_tables.h:502
 #: engines/ultima/nuvie/metaengine.cpp:92
@@ -7546,7 +7546,7 @@ msgid "Quick load"
 msgstr "Carregamento r??pido"
 
 #: engines/asylum/metaengine.cpp:125 engines/crab/input/input.cpp:266
-#: engines/phoenixvr/metaengine.cpp:91 engines/queen/metaengine.cpp:247
+#: engines/phoenixvr/metaengine.cpp:100 engines/queen/metaengine.cpp:247
 #: engines/sludge/keymapper_tables.h:64 engines/sludge/keymapper_tables.h:303
 #: engines/sludge/keymapper_tables.h:410 engines/sludge/keymapper_tables.h:465
 #: engines/sludge/keymapper_tables.h:496 engines/ultima/nuvie/metaengine.cpp:91
@@ -7880,7 +7880,7 @@ msgstr "Alternar Modo de Combate"
 #. I18N: This keymap allows skipping video cutscenes
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
-#: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
+#: engines/phoenixvr/metaengine.cpp:94 engines/scumm/help.cpp:79
 #: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
@@ -8076,7 +8076,7 @@ msgstr "Base de Dados de Pistas"
 #: engines/freescape/games/dark/dark.cpp:236
 #: engines/freescape/games/driller/driller.cpp:129
 #: engines/freescape/games/eclipse/eclipse.cpp:340
-#: engines/mads/metaengine.cpp:403 engines/phoenixvr/metaengine.cpp:80
+#: engines/mads/metaengine.cpp:403 engines/phoenixvr/metaengine.cpp:89
 #: engines/saga/metaengine.cpp:437 engines/sherlock/metaengine.cpp:359
 #: engines/sherlock/metaengine.cpp:618 engines/sherlock/metaengine.cpp:852
 #: engines/stark/metaengine.cpp:280 engines/sword1/metaengine.cpp:295
@@ -8800,7 +8800,7 @@ msgstr ""
 
 #. I18N: Perform action on object where cursor points
 #: engines/darkseed/metaengine.cpp:42 engines/dragons/metaengine.cpp:135
-#: engines/phoenixvr/metaengine.cpp:66 engines/tinsel/metaengine.cpp:294
+#: engines/phoenixvr/metaengine.cpp:75 engines/tinsel/metaengine.cpp:294
 #: engines/twp/metaengine.cpp:150
 msgid "Action"
 msgstr "A????o"
@@ -12765,7 +12765,7 @@ msgstr "Exibir / Ocultar console"
 msgid "Close current interface"
 msgstr "Interface f??cil do mouse"
 
-#: engines/phoenixvr/metaengine.cpp:107
+#: engines/phoenixvr/metaengine.cpp:116
 #: engines/wintermute/keymapper_tables.h:239
 #: engines/wintermute/keymapper_tables.h:262
 #: engines/wintermute/keymapper_tables.h:313
diff --git a/po/pt_PT.po b/po/pt_PT.po
index 356c46e7128..9da8c0cfb89 100644
--- a/po/pt_PT.po
+++ b/po/pt_PT.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.10.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-11 13:12+0000\n"
+"POT-Creation-Date: 2026-06-11 22:10+0000\n"
 "PO-Revision-Date: 2022-06-05 14:09+0000\n"
 "Last-Translator: Daniel <supsuper at gmail.com>\n"
 "Language-Team: Portuguese (Portugal) <https://translations.scummvm.org/"
@@ -5837,7 +5837,7 @@ msgstr "R2"
 #: backends/platform/sdl/miyoo/miyoo.cpp:114
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
-#: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
+#: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:110
 #: engines/scumm/metaengine.cpp:1232 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "Menu"
@@ -6228,7 +6228,7 @@ msgstr ""
 #: engines/grim/grim.cpp:621 engines/hdb/metaengine.cpp:251
 #: engines/hpl1/metaengine.cpp:94 engines/icb/icb.cpp:101
 #: engines/icb/icb.cpp:179 engines/petka/metaengine.cpp:173
-#: engines/phoenixvr/metaengine.cpp:73 engines/scumm/help.cpp:228
+#: engines/phoenixvr/metaengine.cpp:82 engines/scumm/help.cpp:228
 #: engines/scumm/help.cpp:237 engines/scumm/help.cpp:244
 #: engines/scumm/help.cpp:252 engines/sherlock/metaengine.cpp:435
 #: engines/sludge/keymapper_tables.h:58 engines/sludge/keymapper_tables.h:119
@@ -6372,7 +6372,7 @@ msgstr ""
 #: engines/got/metaengine.cpp:35 engines/groovie/metaengine.cpp:53
 #: engines/m4/metaengine.cpp:37 engines/mads/metaengine.cpp:116
 #: engines/nancy/metaengine.cpp:104 engines/neverhood/dialogs.cpp:47
-#: engines/pelrock/metaengine.cpp:35 engines/phoenixvr/metaengine.cpp:38
+#: engines/pelrock/metaengine.cpp:35 engines/phoenixvr/metaengine.cpp:47
 #: engines/sci/detection_options.h:105 engines/sherlock/metaengine.cpp:47
 #: engines/toltecs/metaengine.cpp:44 engines/trecision/metaengine.cpp:41
 #: engines/ultima/metaengine.cpp:56 engines/waynesworld/metaengine.cpp:37
@@ -6387,7 +6387,7 @@ msgstr "Usar ecr??s originais de Salvar/Carregar"
 #: engines/got/metaengine.cpp:36 engines/groovie/metaengine.cpp:54
 #: engines/m4/metaengine.cpp:38 engines/mads/metaengine.cpp:117
 #: engines/nancy/metaengine.cpp:105 engines/neverhood/dialogs.cpp:48
-#: engines/pelrock/metaengine.cpp:36 engines/phoenixvr/metaengine.cpp:39
+#: engines/pelrock/metaengine.cpp:36 engines/phoenixvr/metaengine.cpp:48
 #: engines/sci/detection_options.h:106 engines/sherlock/metaengine.cpp:48
 #: engines/toltecs/metaengine.cpp:45 engines/trecision/metaengine.cpp:42
 #: engines/ultima/metaengine.cpp:57 engines/waynesworld/metaengine.cpp:38
@@ -6980,7 +6980,7 @@ msgid "Show version"
 msgstr "Mostrar vers??o"
 
 #: engines/asylum/metaengine.cpp:120 engines/crab/input/input.cpp:271
-#: engines/phoenixvr/metaengine.cpp:96 engines/queen/metaengine.cpp:253
+#: engines/phoenixvr/metaengine.cpp:105 engines/queen/metaengine.cpp:253
 #: engines/sludge/keymapper_tables.h:308 engines/sludge/keymapper_tables.h:404
 #: engines/sludge/keymapper_tables.h:471 engines/sludge/keymapper_tables.h:502
 #: engines/ultima/nuvie/metaengine.cpp:92
@@ -6989,7 +6989,7 @@ msgid "Quick load"
 msgstr "Carregar r??pido"
 
 #: engines/asylum/metaengine.cpp:125 engines/crab/input/input.cpp:266
-#: engines/phoenixvr/metaengine.cpp:91 engines/queen/metaengine.cpp:247
+#: engines/phoenixvr/metaengine.cpp:100 engines/queen/metaengine.cpp:247
 #: engines/sludge/keymapper_tables.h:64 engines/sludge/keymapper_tables.h:303
 #: engines/sludge/keymapper_tables.h:410 engines/sludge/keymapper_tables.h:465
 #: engines/sludge/keymapper_tables.h:496 engines/ultima/nuvie/metaengine.cpp:91
@@ -7319,7 +7319,7 @@ msgstr "Ativar/desativar combate"
 #. I18N: This keymap allows skipping video cutscenes
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
-#: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
+#: engines/phoenixvr/metaengine.cpp:94 engines/scumm/help.cpp:79
 #: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
@@ -7506,7 +7506,7 @@ msgstr ""
 #: engines/freescape/games/dark/dark.cpp:236
 #: engines/freescape/games/driller/driller.cpp:129
 #: engines/freescape/games/eclipse/eclipse.cpp:340
-#: engines/mads/metaengine.cpp:403 engines/phoenixvr/metaengine.cpp:80
+#: engines/mads/metaengine.cpp:403 engines/phoenixvr/metaengine.cpp:89
 #: engines/saga/metaengine.cpp:437 engines/sherlock/metaengine.cpp:359
 #: engines/sherlock/metaengine.cpp:618 engines/sherlock/metaengine.cpp:852
 #: engines/stark/metaengine.cpp:280 engines/sword1/metaengine.cpp:295
@@ -8230,7 +8230,7 @@ msgstr ""
 
 #. I18N: Perform action on object where cursor points
 #: engines/darkseed/metaengine.cpp:42 engines/dragons/metaengine.cpp:135
-#: engines/phoenixvr/metaengine.cpp:66 engines/tinsel/metaengine.cpp:294
+#: engines/phoenixvr/metaengine.cpp:75 engines/tinsel/metaengine.cpp:294
 #: engines/twp/metaengine.cpp:150
 msgid "Action"
 msgstr "A????o"
@@ -12101,7 +12101,7 @@ msgstr "Mostrar / ocultar consola"
 msgid "Close current interface"
 msgstr "Interface de rato simples"
 
-#: engines/phoenixvr/metaengine.cpp:107
+#: engines/phoenixvr/metaengine.cpp:116
 #: engines/wintermute/keymapper_tables.h:239
 #: engines/wintermute/keymapper_tables.h:262
 #: engines/wintermute/keymapper_tables.h:313
diff --git a/po/ro.po b/po/ro.po
index 33a6ffc395e..9e5297972e8 100644
--- a/po/ro.po
+++ b/po/ro.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.10.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-11 13:12+0000\n"
+"POT-Creation-Date: 2026-06-11 22:10+0000\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: Automatically generated\n"
 "Language-Team: none\n"
@@ -5433,7 +5433,7 @@ msgstr ""
 #: backends/platform/sdl/miyoo/miyoo.cpp:114
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
-#: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
+#: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:110
 #: engines/scumm/metaengine.cpp:1232 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr ""
@@ -5809,7 +5809,7 @@ msgstr ""
 #: engines/grim/grim.cpp:621 engines/hdb/metaengine.cpp:251
 #: engines/hpl1/metaengine.cpp:94 engines/icb/icb.cpp:101
 #: engines/icb/icb.cpp:179 engines/petka/metaengine.cpp:173
-#: engines/phoenixvr/metaengine.cpp:73 engines/scumm/help.cpp:228
+#: engines/phoenixvr/metaengine.cpp:82 engines/scumm/help.cpp:228
 #: engines/scumm/help.cpp:237 engines/scumm/help.cpp:244
 #: engines/scumm/help.cpp:252 engines/sherlock/metaengine.cpp:435
 #: engines/sludge/keymapper_tables.h:58 engines/sludge/keymapper_tables.h:119
@@ -5948,7 +5948,7 @@ msgstr ""
 #: engines/got/metaengine.cpp:35 engines/groovie/metaengine.cpp:53
 #: engines/m4/metaengine.cpp:37 engines/mads/metaengine.cpp:116
 #: engines/nancy/metaengine.cpp:104 engines/neverhood/dialogs.cpp:47
-#: engines/pelrock/metaengine.cpp:35 engines/phoenixvr/metaengine.cpp:38
+#: engines/pelrock/metaengine.cpp:35 engines/phoenixvr/metaengine.cpp:47
 #: engines/sci/detection_options.h:105 engines/sherlock/metaengine.cpp:47
 #: engines/toltecs/metaengine.cpp:44 engines/trecision/metaengine.cpp:41
 #: engines/ultima/metaengine.cpp:56 engines/waynesworld/metaengine.cpp:37
@@ -5963,7 +5963,7 @@ msgstr ""
 #: engines/got/metaengine.cpp:36 engines/groovie/metaengine.cpp:54
 #: engines/m4/metaengine.cpp:38 engines/mads/metaengine.cpp:117
 #: engines/nancy/metaengine.cpp:105 engines/neverhood/dialogs.cpp:48
-#: engines/pelrock/metaengine.cpp:36 engines/phoenixvr/metaengine.cpp:39
+#: engines/pelrock/metaengine.cpp:36 engines/phoenixvr/metaengine.cpp:48
 #: engines/sci/detection_options.h:106 engines/sherlock/metaengine.cpp:48
 #: engines/toltecs/metaengine.cpp:45 engines/trecision/metaengine.cpp:42
 #: engines/ultima/metaengine.cpp:57 engines/waynesworld/metaengine.cpp:38
@@ -6493,7 +6493,7 @@ msgid "Show version"
 msgstr ""
 
 #: engines/asylum/metaengine.cpp:120 engines/crab/input/input.cpp:271
-#: engines/phoenixvr/metaengine.cpp:96 engines/queen/metaengine.cpp:253
+#: engines/phoenixvr/metaengine.cpp:105 engines/queen/metaengine.cpp:253
 #: engines/sludge/keymapper_tables.h:308 engines/sludge/keymapper_tables.h:404
 #: engines/sludge/keymapper_tables.h:471 engines/sludge/keymapper_tables.h:502
 #: engines/ultima/nuvie/metaengine.cpp:92
@@ -6502,7 +6502,7 @@ msgid "Quick load"
 msgstr ""
 
 #: engines/asylum/metaengine.cpp:125 engines/crab/input/input.cpp:266
-#: engines/phoenixvr/metaengine.cpp:91 engines/queen/metaengine.cpp:247
+#: engines/phoenixvr/metaengine.cpp:100 engines/queen/metaengine.cpp:247
 #: engines/sludge/keymapper_tables.h:64 engines/sludge/keymapper_tables.h:303
 #: engines/sludge/keymapper_tables.h:410 engines/sludge/keymapper_tables.h:465
 #: engines/sludge/keymapper_tables.h:496 engines/ultima/nuvie/metaengine.cpp:91
@@ -6794,7 +6794,7 @@ msgstr ""
 #. I18N: This keymap allows skipping video cutscenes
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
-#: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
+#: engines/phoenixvr/metaengine.cpp:94 engines/scumm/help.cpp:79
 #: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
@@ -6974,7 +6974,7 @@ msgstr ""
 #: engines/freescape/games/dark/dark.cpp:236
 #: engines/freescape/games/driller/driller.cpp:129
 #: engines/freescape/games/eclipse/eclipse.cpp:340
-#: engines/mads/metaengine.cpp:403 engines/phoenixvr/metaengine.cpp:80
+#: engines/mads/metaengine.cpp:403 engines/phoenixvr/metaengine.cpp:89
 #: engines/saga/metaengine.cpp:437 engines/sherlock/metaengine.cpp:359
 #: engines/sherlock/metaengine.cpp:618 engines/sherlock/metaengine.cpp:852
 #: engines/stark/metaengine.cpp:280 engines/sword1/metaengine.cpp:295
@@ -7591,7 +7591,7 @@ msgstr ""
 
 #. I18N: Perform action on object where cursor points
 #: engines/darkseed/metaengine.cpp:42 engines/dragons/metaengine.cpp:135
-#: engines/phoenixvr/metaengine.cpp:66 engines/tinsel/metaengine.cpp:294
+#: engines/phoenixvr/metaengine.cpp:75 engines/tinsel/metaengine.cpp:294
 #: engines/twp/metaengine.cpp:150
 msgid "Action"
 msgstr ""
@@ -10876,7 +10876,7 @@ msgstr ""
 msgid "Close current interface"
 msgstr ""
 
-#: engines/phoenixvr/metaengine.cpp:107
+#: engines/phoenixvr/metaengine.cpp:116
 #: engines/wintermute/keymapper_tables.h:239
 #: engines/wintermute/keymapper_tables.h:262
 #: engines/wintermute/keymapper_tables.h:313
diff --git a/po/ru_RU.po b/po/ru_RU.po
index 7b5e6b5c3bf..002ff58afdd 100644
--- a/po/ru_RU.po
+++ b/po/ru_RU.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.8.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-11 13:12+0000\n"
+"POT-Creation-Date: 2026-06-11 22:10+0000\n"
 "PO-Revision-Date: 2026-05-06 08:54+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Russian <https://translations.scummvm.org/projects/scummvm/"
@@ -6108,7 +6108,7 @@ msgstr "R2"
 #: backends/platform/sdl/miyoo/miyoo.cpp:114
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
-#: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
+#: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:110
 #: engines/scumm/metaengine.cpp:1232 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "????????"
@@ -6484,7 +6484,7 @@ msgstr "??????????????"
 #: engines/grim/grim.cpp:621 engines/hdb/metaengine.cpp:251
 #: engines/hpl1/metaengine.cpp:94 engines/icb/icb.cpp:101
 #: engines/icb/icb.cpp:179 engines/petka/metaengine.cpp:173
-#: engines/phoenixvr/metaengine.cpp:73 engines/scumm/help.cpp:228
+#: engines/phoenixvr/metaengine.cpp:82 engines/scumm/help.cpp:228
 #: engines/scumm/help.cpp:237 engines/scumm/help.cpp:244
 #: engines/scumm/help.cpp:252 engines/sherlock/metaengine.cpp:435
 #: engines/sludge/keymapper_tables.h:58 engines/sludge/keymapper_tables.h:119
@@ -6626,7 +6626,7 @@ msgstr ""
 #: engines/got/metaengine.cpp:35 engines/groovie/metaengine.cpp:53
 #: engines/m4/metaengine.cpp:37 engines/mads/metaengine.cpp:116
 #: engines/nancy/metaengine.cpp:104 engines/neverhood/dialogs.cpp:47
-#: engines/pelrock/metaengine.cpp:35 engines/phoenixvr/metaengine.cpp:38
+#: engines/pelrock/metaengine.cpp:35 engines/phoenixvr/metaengine.cpp:47
 #: engines/sci/detection_options.h:105 engines/sherlock/metaengine.cpp:47
 #: engines/toltecs/metaengine.cpp:44 engines/trecision/metaengine.cpp:41
 #: engines/ultima/metaengine.cpp:56 engines/waynesworld/metaengine.cpp:37
@@ -6641,7 +6641,7 @@ msgstr "???????????????????????? ???????????????????????? ???????????? ????????
 #: engines/got/metaengine.cpp:36 engines/groovie/metaengine.cpp:54
 #: engines/m4/metaengine.cpp:38 engines/mads/metaengine.cpp:117
 #: engines/nancy/metaengine.cpp:105 engines/neverhood/dialogs.cpp:48
-#: engines/pelrock/metaengine.cpp:36 engines/phoenixvr/metaengine.cpp:39
+#: engines/pelrock/metaengine.cpp:36 engines/phoenixvr/metaengine.cpp:48
 #: engines/sci/detection_options.h:106 engines/sherlock/metaengine.cpp:48
 #: engines/toltecs/metaengine.cpp:45 engines/trecision/metaengine.cpp:42
 #: engines/ultima/metaengine.cpp:57 engines/waynesworld/metaengine.cpp:38
@@ -7244,7 +7244,7 @@ msgid "Show version"
 msgstr "???????????????? ????????????"
 
 #: engines/asylum/metaengine.cpp:120 engines/crab/input/input.cpp:271
-#: engines/phoenixvr/metaengine.cpp:96 engines/queen/metaengine.cpp:253
+#: engines/phoenixvr/metaengine.cpp:105 engines/queen/metaengine.cpp:253
 #: engines/sludge/keymapper_tables.h:308 engines/sludge/keymapper_tables.h:404
 #: engines/sludge/keymapper_tables.h:471 engines/sludge/keymapper_tables.h:502
 #: engines/ultima/nuvie/metaengine.cpp:92
@@ -7253,7 +7253,7 @@ msgid "Quick load"
 msgstr "?????????????? ????????????????"
 
 #: engines/asylum/metaengine.cpp:125 engines/crab/input/input.cpp:266
-#: engines/phoenixvr/metaengine.cpp:91 engines/queen/metaengine.cpp:247
+#: engines/phoenixvr/metaengine.cpp:100 engines/queen/metaengine.cpp:247
 #: engines/sludge/keymapper_tables.h:64 engines/sludge/keymapper_tables.h:303
 #: engines/sludge/keymapper_tables.h:410 engines/sludge/keymapper_tables.h:465
 #: engines/sludge/keymapper_tables.h:496 engines/ultima/nuvie/metaengine.cpp:91
@@ -7565,7 +7565,7 @@ msgstr "?????????????????????? ??????"
 #. I18N: This keymap allows skipping video cutscenes
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
-#: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
+#: engines/phoenixvr/metaengine.cpp:94 engines/scumm/help.cpp:79
 #: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
@@ -7745,7 +7745,7 @@ msgstr "???????? ????????"
 #: engines/freescape/games/dark/dark.cpp:236
 #: engines/freescape/games/driller/driller.cpp:129
 #: engines/freescape/games/eclipse/eclipse.cpp:340
-#: engines/mads/metaengine.cpp:403 engines/phoenixvr/metaengine.cpp:80
+#: engines/mads/metaengine.cpp:403 engines/phoenixvr/metaengine.cpp:89
 #: engines/saga/metaengine.cpp:437 engines/sherlock/metaengine.cpp:359
 #: engines/sherlock/metaengine.cpp:618 engines/sherlock/metaengine.cpp:852
 #: engines/stark/metaengine.cpp:280 engines/sword1/metaengine.cpp:295
@@ -8384,7 +8384,7 @@ msgstr "???????????????? ?????????????? ???????????? ?? ??????????????"
 
 #. I18N: Perform action on object where cursor points
 #: engines/darkseed/metaengine.cpp:42 engines/dragons/metaengine.cpp:135
-#: engines/phoenixvr/metaengine.cpp:66 engines/tinsel/metaengine.cpp:294
+#: engines/phoenixvr/metaengine.cpp:75 engines/tinsel/metaengine.cpp:294
 #: engines/twp/metaengine.cpp:150
 msgid "Action"
 msgstr "????????????????"
@@ -11897,7 +11897,7 @@ msgstr "???????????????? / ???????????? ?????????? ????????????????"
 msgid "Close current interface"
 msgstr "?????????????? ?????????????? ??????????????????"
 
-#: engines/phoenixvr/metaengine.cpp:107
+#: engines/phoenixvr/metaengine.cpp:116
 #: engines/wintermute/keymapper_tables.h:239
 #: engines/wintermute/keymapper_tables.h:262
 #: engines/wintermute/keymapper_tables.h:313
diff --git a/po/sv_SE.po b/po/sv_SE.po
index 0f8af32ffe6..95ea05675f9 100644
--- a/po/sv_SE.po
+++ b/po/sv_SE.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.5.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-11 13:12+0000\n"
+"POT-Creation-Date: 2026-06-11 22:10+0000\n"
 "PO-Revision-Date: 2026-04-19 13:22+0000\n"
 "Last-Translator: Daniel Nylander <po at danielnylander.se>\n"
 "Language-Team: Swedish <https://translations.scummvm.org/projects/scummvm/"
@@ -6085,7 +6085,7 @@ msgstr "R2"
 #: backends/platform/sdl/miyoo/miyoo.cpp:114
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
-#: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
+#: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:110
 #: engines/scumm/metaengine.cpp:1232 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "Meny"
@@ -6461,7 +6461,7 @@ msgstr "Ta"
 #: engines/grim/grim.cpp:621 engines/hdb/metaengine.cpp:251
 #: engines/hpl1/metaengine.cpp:94 engines/icb/icb.cpp:101
 #: engines/icb/icb.cpp:179 engines/petka/metaengine.cpp:173
-#: engines/phoenixvr/metaengine.cpp:73 engines/scumm/help.cpp:228
+#: engines/phoenixvr/metaengine.cpp:82 engines/scumm/help.cpp:228
 #: engines/scumm/help.cpp:237 engines/scumm/help.cpp:244
 #: engines/scumm/help.cpp:252 engines/sherlock/metaengine.cpp:435
 #: engines/sludge/keymapper_tables.h:58 engines/sludge/keymapper_tables.h:119
@@ -6605,7 +6605,7 @@ msgstr ""
 #: engines/got/metaengine.cpp:35 engines/groovie/metaengine.cpp:53
 #: engines/m4/metaengine.cpp:37 engines/mads/metaengine.cpp:116
 #: engines/nancy/metaengine.cpp:104 engines/neverhood/dialogs.cpp:47
-#: engines/pelrock/metaengine.cpp:35 engines/phoenixvr/metaengine.cpp:38
+#: engines/pelrock/metaengine.cpp:35 engines/phoenixvr/metaengine.cpp:47
 #: engines/sci/detection_options.h:105 engines/sherlock/metaengine.cpp:47
 #: engines/toltecs/metaengine.cpp:44 engines/trecision/metaengine.cpp:41
 #: engines/ultima/metaengine.cpp:56 engines/waynesworld/metaengine.cpp:37
@@ -6620,7 +6620,7 @@ msgstr "Anv??nd originalsk??rmar f??r spara/l??s in"
 #: engines/got/metaengine.cpp:36 engines/groovie/metaengine.cpp:54
 #: engines/m4/metaengine.cpp:38 engines/mads/metaengine.cpp:117
 #: engines/nancy/metaengine.cpp:105 engines/neverhood/dialogs.cpp:48
-#: engines/pelrock/metaengine.cpp:36 engines/phoenixvr/metaengine.cpp:39
+#: engines/pelrock/metaengine.cpp:36 engines/phoenixvr/metaengine.cpp:48
 #: engines/sci/detection_options.h:106 engines/sherlock/metaengine.cpp:48
 #: engines/toltecs/metaengine.cpp:45 engines/trecision/metaengine.cpp:42
 #: engines/ultima/metaengine.cpp:57 engines/waynesworld/metaengine.cpp:38
@@ -7217,7 +7217,7 @@ msgid "Show version"
 msgstr "Visa version"
 
 #: engines/asylum/metaengine.cpp:120 engines/crab/input/input.cpp:271
-#: engines/phoenixvr/metaengine.cpp:96 engines/queen/metaengine.cpp:253
+#: engines/phoenixvr/metaengine.cpp:105 engines/queen/metaengine.cpp:253
 #: engines/sludge/keymapper_tables.h:308 engines/sludge/keymapper_tables.h:404
 #: engines/sludge/keymapper_tables.h:471 engines/sludge/keymapper_tables.h:502
 #: engines/ultima/nuvie/metaengine.cpp:92
@@ -7226,7 +7226,7 @@ msgid "Quick load"
 msgstr "Snabbinl??sning"
 
 #: engines/asylum/metaengine.cpp:125 engines/crab/input/input.cpp:266
-#: engines/phoenixvr/metaengine.cpp:91 engines/queen/metaengine.cpp:247
+#: engines/phoenixvr/metaengine.cpp:100 engines/queen/metaengine.cpp:247
 #: engines/sludge/keymapper_tables.h:64 engines/sludge/keymapper_tables.h:303
 #: engines/sludge/keymapper_tables.h:410 engines/sludge/keymapper_tables.h:465
 #: engines/sludge/keymapper_tables.h:496 engines/ultima/nuvie/metaengine.cpp:91
@@ -7540,7 +7540,7 @@ msgstr "V??xla strid"
 #. I18N: This keymap allows skipping video cutscenes
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
-#: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
+#: engines/phoenixvr/metaengine.cpp:94 engines/scumm/help.cpp:79
 #: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
@@ -7720,7 +7720,7 @@ msgstr "Databas f??r ledtr??dar"
 #: engines/freescape/games/dark/dark.cpp:236
 #: engines/freescape/games/driller/driller.cpp:129
 #: engines/freescape/games/eclipse/eclipse.cpp:340
-#: engines/mads/metaengine.cpp:403 engines/phoenixvr/metaengine.cpp:80
+#: engines/mads/metaengine.cpp:403 engines/phoenixvr/metaengine.cpp:89
 #: engines/saga/metaengine.cpp:437 engines/sherlock/metaengine.cpp:359
 #: engines/sherlock/metaengine.cpp:618 engines/sherlock/metaengine.cpp:852
 #: engines/stark/metaengine.cpp:280 engines/sword1/metaengine.cpp:295
@@ -8363,7 +8363,7 @@ msgstr "Endast diskettversionens SFX"
 
 #. I18N: Perform action on object where cursor points
 #: engines/darkseed/metaengine.cpp:42 engines/dragons/metaengine.cpp:135
-#: engines/phoenixvr/metaengine.cpp:66 engines/tinsel/metaengine.cpp:294
+#: engines/phoenixvr/metaengine.cpp:75 engines/tinsel/metaengine.cpp:294
 #: engines/twp/metaengine.cpp:150
 msgid "Action"
 msgstr "??tg??rd"
@@ -11957,7 +11957,7 @@ msgstr "Visa / d??lj alternativsk??rm"
 msgid "Close current interface"
 msgstr "St??ng aktuellt gr??nssnitt"
 
-#: engines/phoenixvr/metaengine.cpp:107
+#: engines/phoenixvr/metaengine.cpp:116
 #: engines/wintermute/keymapper_tables.h:239
 #: engines/wintermute/keymapper_tables.h:262
 #: engines/wintermute/keymapper_tables.h:313
diff --git a/po/tr.po b/po/tr.po
index 39747e32d91..c334aca2367 100644
--- a/po/tr.po
+++ b/po/tr.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-11 13:12+0000\n"
+"POT-Creation-Date: 2026-06-11 22:10+0000\n"
 "PO-Revision-Date: 2024-10-10 12:33+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Turkish <https://translations.scummvm.org/projects/scummvm/"
@@ -5761,7 +5761,7 @@ msgstr ""
 #: backends/platform/sdl/miyoo/miyoo.cpp:114
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
-#: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
+#: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:110
 #: engines/scumm/metaengine.cpp:1232 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr ""
@@ -6149,7 +6149,7 @@ msgstr ""
 #: engines/grim/grim.cpp:621 engines/hdb/metaengine.cpp:251
 #: engines/hpl1/metaengine.cpp:94 engines/icb/icb.cpp:101
 #: engines/icb/icb.cpp:179 engines/petka/metaengine.cpp:173
-#: engines/phoenixvr/metaengine.cpp:73 engines/scumm/help.cpp:228
+#: engines/phoenixvr/metaengine.cpp:82 engines/scumm/help.cpp:228
 #: engines/scumm/help.cpp:237 engines/scumm/help.cpp:244
 #: engines/scumm/help.cpp:252 engines/sherlock/metaengine.cpp:435
 #: engines/sludge/keymapper_tables.h:58 engines/sludge/keymapper_tables.h:119
@@ -6291,7 +6291,7 @@ msgstr ""
 #: engines/got/metaengine.cpp:35 engines/groovie/metaengine.cpp:53
 #: engines/m4/metaengine.cpp:37 engines/mads/metaengine.cpp:116
 #: engines/nancy/metaengine.cpp:104 engines/neverhood/dialogs.cpp:47
-#: engines/pelrock/metaengine.cpp:35 engines/phoenixvr/metaengine.cpp:38
+#: engines/pelrock/metaengine.cpp:35 engines/phoenixvr/metaengine.cpp:47
 #: engines/sci/detection_options.h:105 engines/sherlock/metaengine.cpp:47
 #: engines/toltecs/metaengine.cpp:44 engines/trecision/metaengine.cpp:41
 #: engines/ultima/metaengine.cpp:56 engines/waynesworld/metaengine.cpp:37
@@ -6306,7 +6306,7 @@ msgstr ""
 #: engines/got/metaengine.cpp:36 engines/groovie/metaengine.cpp:54
 #: engines/m4/metaengine.cpp:38 engines/mads/metaengine.cpp:117
 #: engines/nancy/metaengine.cpp:105 engines/neverhood/dialogs.cpp:48
-#: engines/pelrock/metaengine.cpp:36 engines/phoenixvr/metaengine.cpp:39
+#: engines/pelrock/metaengine.cpp:36 engines/phoenixvr/metaengine.cpp:48
 #: engines/sci/detection_options.h:106 engines/sherlock/metaengine.cpp:48
 #: engines/toltecs/metaengine.cpp:45 engines/trecision/metaengine.cpp:42
 #: engines/ultima/metaengine.cpp:57 engines/waynesworld/metaengine.cpp:38
@@ -6870,7 +6870,7 @@ msgid "Show version"
 msgstr "Gizli dosyalar?? g??ster"
 
 #: engines/asylum/metaengine.cpp:120 engines/crab/input/input.cpp:271
-#: engines/phoenixvr/metaengine.cpp:96 engines/queen/metaengine.cpp:253
+#: engines/phoenixvr/metaengine.cpp:105 engines/queen/metaengine.cpp:253
 #: engines/sludge/keymapper_tables.h:308 engines/sludge/keymapper_tables.h:404
 #: engines/sludge/keymapper_tables.h:471 engines/sludge/keymapper_tables.h:502
 #: engines/ultima/nuvie/metaengine.cpp:92
@@ -6879,7 +6879,7 @@ msgid "Quick load"
 msgstr ""
 
 #: engines/asylum/metaengine.cpp:125 engines/crab/input/input.cpp:266
-#: engines/phoenixvr/metaengine.cpp:91 engines/queen/metaengine.cpp:247
+#: engines/phoenixvr/metaengine.cpp:100 engines/queen/metaengine.cpp:247
 #: engines/sludge/keymapper_tables.h:64 engines/sludge/keymapper_tables.h:303
 #: engines/sludge/keymapper_tables.h:410 engines/sludge/keymapper_tables.h:465
 #: engines/sludge/keymapper_tables.h:496 engines/ultima/nuvie/metaengine.cpp:91
@@ -7193,7 +7193,7 @@ msgstr "Altyaz??lar"
 #. I18N: This keymap allows skipping video cutscenes
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
-#: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
+#: engines/phoenixvr/metaengine.cpp:94 engines/scumm/help.cpp:79
 #: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
@@ -7380,7 +7380,7 @@ msgstr ""
 #: engines/freescape/games/dark/dark.cpp:236
 #: engines/freescape/games/driller/driller.cpp:129
 #: engines/freescape/games/eclipse/eclipse.cpp:340
-#: engines/mads/metaengine.cpp:403 engines/phoenixvr/metaengine.cpp:80
+#: engines/mads/metaengine.cpp:403 engines/phoenixvr/metaengine.cpp:89
 #: engines/saga/metaengine.cpp:437 engines/sherlock/metaengine.cpp:359
 #: engines/sherlock/metaengine.cpp:618 engines/sherlock/metaengine.cpp:852
 #: engines/stark/metaengine.cpp:280 engines/sword1/metaengine.cpp:295
@@ -8060,7 +8060,7 @@ msgstr ""
 
 #. I18N: Perform action on object where cursor points
 #: engines/darkseed/metaengine.cpp:42 engines/dragons/metaengine.cpp:135
-#: engines/phoenixvr/metaengine.cpp:66 engines/tinsel/metaengine.cpp:294
+#: engines/phoenixvr/metaengine.cpp:75 engines/tinsel/metaengine.cpp:294
 #: engines/twp/metaengine.cpp:150
 #, fuzzy
 msgid "Action"
@@ -11658,7 +11658,7 @@ msgstr "Gizli dosyalar?? g??ster"
 msgid "Close current interface"
 msgstr "A~y~arlar..."
 
-#: engines/phoenixvr/metaengine.cpp:107
+#: engines/phoenixvr/metaengine.cpp:116
 #: engines/wintermute/keymapper_tables.h:239
 #: engines/wintermute/keymapper_tables.h:262
 #: engines/wintermute/keymapper_tables.h:313
diff --git a/po/uk_UA.po b/po/uk_UA.po
index 47fe76cb8f9..97264ca3701 100644
--- a/po/uk_UA.po
+++ b/po/uk_UA.po
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.9.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-11 13:12+0000\n"
+"POT-Creation-Date: 2026-06-11 22:10+0000\n"
 "PO-Revision-Date: 2026-05-06 08:54+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Ukrainian <https://translations.scummvm.org/projects/scummvm/"
@@ -6094,7 +6094,7 @@ msgstr "R2"
 #: backends/platform/sdl/miyoo/miyoo.cpp:114
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
-#: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
+#: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:110
 #: engines/scumm/metaengine.cpp:1232 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "????????"
@@ -6470,7 +6470,7 @@ msgstr "??????????"
 #: engines/grim/grim.cpp:621 engines/hdb/metaengine.cpp:251
 #: engines/hpl1/metaengine.cpp:94 engines/icb/icb.cpp:101
 #: engines/icb/icb.cpp:179 engines/petka/metaengine.cpp:173
-#: engines/phoenixvr/metaengine.cpp:73 engines/scumm/help.cpp:228
+#: engines/phoenixvr/metaengine.cpp:82 engines/scumm/help.cpp:228
 #: engines/scumm/help.cpp:237 engines/scumm/help.cpp:244
 #: engines/scumm/help.cpp:252 engines/sherlock/metaengine.cpp:435
 #: engines/sludge/keymapper_tables.h:58 engines/sludge/keymapper_tables.h:119
@@ -6614,7 +6614,7 @@ msgstr ""
 #: engines/got/metaengine.cpp:35 engines/groovie/metaengine.cpp:53
 #: engines/m4/metaengine.cpp:37 engines/mads/metaengine.cpp:116
 #: engines/nancy/metaengine.cpp:104 engines/neverhood/dialogs.cpp:47
-#: engines/pelrock/metaengine.cpp:35 engines/phoenixvr/metaengine.cpp:38
+#: engines/pelrock/metaengine.cpp:35 engines/phoenixvr/metaengine.cpp:47
 #: engines/sci/detection_options.h:105 engines/sherlock/metaengine.cpp:47
 #: engines/toltecs/metaengine.cpp:44 engines/trecision/metaengine.cpp:41
 #: engines/ultima/metaengine.cpp:56 engines/waynesworld/metaengine.cpp:37
@@ -6629,7 +6629,7 @@ msgstr "?????????????????????????????? ????????. ???????????? ??????????????????
 #: engines/got/metaengine.cpp:36 engines/groovie/metaengine.cpp:54
 #: engines/m4/metaengine.cpp:38 engines/mads/metaengine.cpp:117
 #: engines/nancy/metaengine.cpp:105 engines/neverhood/dialogs.cpp:48
-#: engines/pelrock/metaengine.cpp:36 engines/phoenixvr/metaengine.cpp:39
+#: engines/pelrock/metaengine.cpp:36 engines/phoenixvr/metaengine.cpp:48
 #: engines/sci/detection_options.h:106 engines/sherlock/metaengine.cpp:48
 #: engines/toltecs/metaengine.cpp:45 engines/trecision/metaengine.cpp:42
 #: engines/ultima/metaengine.cpp:57 engines/waynesworld/metaengine.cpp:38
@@ -7226,7 +7226,7 @@ msgid "Show version"
 msgstr "???????????????????? ????????????"
 
 #: engines/asylum/metaengine.cpp:120 engines/crab/input/input.cpp:271
-#: engines/phoenixvr/metaengine.cpp:96 engines/queen/metaengine.cpp:253
+#: engines/phoenixvr/metaengine.cpp:105 engines/queen/metaengine.cpp:253
 #: engines/sludge/keymapper_tables.h:308 engines/sludge/keymapper_tables.h:404
 #: engines/sludge/keymapper_tables.h:471 engines/sludge/keymapper_tables.h:502
 #: engines/ultima/nuvie/metaengine.cpp:92
@@ -7235,7 +7235,7 @@ msgid "Quick load"
 msgstr "???????????? ????????????????????????"
 
 #: engines/asylum/metaengine.cpp:125 engines/crab/input/input.cpp:266
-#: engines/phoenixvr/metaengine.cpp:91 engines/queen/metaengine.cpp:247
+#: engines/phoenixvr/metaengine.cpp:100 engines/queen/metaengine.cpp:247
 #: engines/sludge/keymapper_tables.h:64 engines/sludge/keymapper_tables.h:303
 #: engines/sludge/keymapper_tables.h:410 engines/sludge/keymapper_tables.h:465
 #: engines/sludge/keymapper_tables.h:496 engines/ultima/nuvie/metaengine.cpp:91
@@ -7545,7 +7545,7 @@ msgstr "???????????????????? ?????????? ??????"
 #. I18N: This keymap allows skipping video cutscenes
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
-#: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
+#: engines/phoenixvr/metaengine.cpp:94 engines/scumm/help.cpp:79
 #: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
@@ -7725,7 +7725,7 @@ msgstr "???????? ?????????? ??????????????"
 #: engines/freescape/games/dark/dark.cpp:236
 #: engines/freescape/games/driller/driller.cpp:129
 #: engines/freescape/games/eclipse/eclipse.cpp:340
-#: engines/mads/metaengine.cpp:403 engines/phoenixvr/metaengine.cpp:80
+#: engines/mads/metaengine.cpp:403 engines/phoenixvr/metaengine.cpp:89
 #: engines/saga/metaengine.cpp:437 engines/sherlock/metaengine.cpp:359
 #: engines/sherlock/metaengine.cpp:618 engines/sherlock/metaengine.cpp:852
 #: engines/stark/metaengine.cpp:280 engines/sword1/metaengine.cpp:295
@@ -8365,7 +8365,7 @@ msgstr "???????????? SFX ???????????? ?????? ????????????"
 
 #. I18N: Perform action on object where cursor points
 #: engines/darkseed/metaengine.cpp:42 engines/dragons/metaengine.cpp:135
-#: engines/phoenixvr/metaengine.cpp:66 engines/tinsel/metaengine.cpp:294
+#: engines/phoenixvr/metaengine.cpp:75 engines/tinsel/metaengine.cpp:294
 #: engines/twp/metaengine.cpp:150
 msgid "Action"
 msgstr "??????"
@@ -11867,7 +11867,7 @@ msgstr "???????????????? / ?????????????? ?????????? ??????????????????????"
 msgid "Close current interface"
 msgstr "?????????????? ???????????????? ??????????????????"
 
-#: engines/phoenixvr/metaengine.cpp:107
+#: engines/phoenixvr/metaengine.cpp:116
 #: engines/wintermute/keymapper_tables.h:239
 #: engines/wintermute/keymapper_tables.h:262
 #: engines/wintermute/keymapper_tables.h:313
diff --git a/po/zh.po b/po/zh.po
index d640d58469e..cfda05ac4eb 100644
--- a/po/zh.po
+++ b/po/zh.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-11 13:12+0000\n"
+"POT-Creation-Date: 2026-06-11 22:10+0000\n"
 "PO-Revision-Date: 2024-05-14 12:42+0000\n"
 "Last-Translator: ?????? <clonewith at qq.com>\n"
 "Language-Team: Chinese <https://translations.scummvm.org/projects/scummvm/"
@@ -5542,7 +5542,7 @@ msgstr ""
 #: backends/platform/sdl/miyoo/miyoo.cpp:114
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
-#: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
+#: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:110
 #: engines/scumm/metaengine.cpp:1232 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr ""
@@ -5924,7 +5924,7 @@ msgstr ""
 #: engines/grim/grim.cpp:621 engines/hdb/metaengine.cpp:251
 #: engines/hpl1/metaengine.cpp:94 engines/icb/icb.cpp:101
 #: engines/icb/icb.cpp:179 engines/petka/metaengine.cpp:173
-#: engines/phoenixvr/metaengine.cpp:73 engines/scumm/help.cpp:228
+#: engines/phoenixvr/metaengine.cpp:82 engines/scumm/help.cpp:228
 #: engines/scumm/help.cpp:237 engines/scumm/help.cpp:244
 #: engines/scumm/help.cpp:252 engines/sherlock/metaengine.cpp:435
 #: engines/sludge/keymapper_tables.h:58 engines/sludge/keymapper_tables.h:119
@@ -6065,7 +6065,7 @@ msgstr ""
 #: engines/got/metaengine.cpp:35 engines/groovie/metaengine.cpp:53
 #: engines/m4/metaengine.cpp:37 engines/mads/metaengine.cpp:116
 #: engines/nancy/metaengine.cpp:104 engines/neverhood/dialogs.cpp:47
-#: engines/pelrock/metaengine.cpp:35 engines/phoenixvr/metaengine.cpp:38
+#: engines/pelrock/metaengine.cpp:35 engines/phoenixvr/metaengine.cpp:47
 #: engines/sci/detection_options.h:105 engines/sherlock/metaengine.cpp:47
 #: engines/toltecs/metaengine.cpp:44 engines/trecision/metaengine.cpp:41
 #: engines/ultima/metaengine.cpp:56 engines/waynesworld/metaengine.cpp:37
@@ -6080,7 +6080,7 @@ msgstr ""
 #: engines/got/metaengine.cpp:36 engines/groovie/metaengine.cpp:54
 #: engines/m4/metaengine.cpp:38 engines/mads/metaengine.cpp:117
 #: engines/nancy/metaengine.cpp:105 engines/neverhood/dialogs.cpp:48
-#: engines/pelrock/metaengine.cpp:36 engines/phoenixvr/metaengine.cpp:39
+#: engines/pelrock/metaengine.cpp:36 engines/phoenixvr/metaengine.cpp:48
 #: engines/sci/detection_options.h:106 engines/sherlock/metaengine.cpp:48
 #: engines/toltecs/metaengine.cpp:45 engines/trecision/metaengine.cpp:42
 #: engines/ultima/metaengine.cpp:57 engines/waynesworld/metaengine.cpp:38
@@ -6638,7 +6638,7 @@ msgid "Show version"
 msgstr "????????????"
 
 #: engines/asylum/metaengine.cpp:120 engines/crab/input/input.cpp:271
-#: engines/phoenixvr/metaengine.cpp:96 engines/queen/metaengine.cpp:253
+#: engines/phoenixvr/metaengine.cpp:105 engines/queen/metaengine.cpp:253
 #: engines/sludge/keymapper_tables.h:308 engines/sludge/keymapper_tables.h:404
 #: engines/sludge/keymapper_tables.h:471 engines/sludge/keymapper_tables.h:502
 #: engines/ultima/nuvie/metaengine.cpp:92
@@ -6647,7 +6647,7 @@ msgid "Quick load"
 msgstr ""
 
 #: engines/asylum/metaengine.cpp:125 engines/crab/input/input.cpp:266
-#: engines/phoenixvr/metaengine.cpp:91 engines/queen/metaengine.cpp:247
+#: engines/phoenixvr/metaengine.cpp:100 engines/queen/metaengine.cpp:247
 #: engines/sludge/keymapper_tables.h:64 engines/sludge/keymapper_tables.h:303
 #: engines/sludge/keymapper_tables.h:410 engines/sludge/keymapper_tables.h:465
 #: engines/sludge/keymapper_tables.h:496 engines/ultima/nuvie/metaengine.cpp:91
@@ -6953,7 +6953,7 @@ msgstr "??????????????????"
 #. I18N: This keymap allows skipping video cutscenes
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
-#: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
+#: engines/phoenixvr/metaengine.cpp:94 engines/scumm/help.cpp:79
 #: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
@@ -7139,7 +7139,7 @@ msgstr ""
 #: engines/freescape/games/dark/dark.cpp:236
 #: engines/freescape/games/driller/driller.cpp:129
 #: engines/freescape/games/eclipse/eclipse.cpp:340
-#: engines/mads/metaengine.cpp:403 engines/phoenixvr/metaengine.cpp:80
+#: engines/mads/metaengine.cpp:403 engines/phoenixvr/metaengine.cpp:89
 #: engines/saga/metaengine.cpp:437 engines/sherlock/metaengine.cpp:359
 #: engines/sherlock/metaengine.cpp:618 engines/sherlock/metaengine.cpp:852
 #: engines/stark/metaengine.cpp:280 engines/sword1/metaengine.cpp:295
@@ -7828,7 +7828,7 @@ msgstr ""
 
 #. I18N: Perform action on object where cursor points
 #: engines/darkseed/metaengine.cpp:42 engines/dragons/metaengine.cpp:135
-#: engines/phoenixvr/metaengine.cpp:66 engines/tinsel/metaengine.cpp:294
+#: engines/phoenixvr/metaengine.cpp:75 engines/tinsel/metaengine.cpp:294
 #: engines/twp/metaengine.cpp:150
 msgid "Action"
 msgstr ""
@@ -11494,7 +11494,7 @@ msgstr "??????/?????? ??????"
 msgid "Close current interface"
 msgstr "????????????"
 
-#: engines/phoenixvr/metaengine.cpp:107
+#: engines/phoenixvr/metaengine.cpp:116
 #: engines/wintermute/keymapper_tables.h:239
 #: engines/wintermute/keymapper_tables.h:262
 #: engines/wintermute/keymapper_tables.h:313
diff --git a/po/zh_Hans.po b/po/zh_Hans.po
index 82e6f0977ef..aa9b5518f14 100644
--- a/po/zh_Hans.po
+++ b/po/zh_Hans.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.9.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-11 13:12+0000\n"
+"POT-Creation-Date: 2026-06-11 22:10+0000\n"
 "PO-Revision-Date: 2024-12-01 22:18+0000\n"
 "Last-Translator: ?????? <clonewith at qq.com>\n"
 "Language-Team: Chinese (Simplified Han script) <https://"
@@ -6178,7 +6178,7 @@ msgstr "R2"
 #: backends/platform/sdl/miyoo/miyoo.cpp:114
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
-#: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
+#: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:110
 #: engines/scumm/metaengine.cpp:1232 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr "??????"
@@ -6558,7 +6558,7 @@ msgstr "??????"
 #: engines/grim/grim.cpp:621 engines/hdb/metaengine.cpp:251
 #: engines/hpl1/metaengine.cpp:94 engines/icb/icb.cpp:101
 #: engines/icb/icb.cpp:179 engines/petka/metaengine.cpp:173
-#: engines/phoenixvr/metaengine.cpp:73 engines/scumm/help.cpp:228
+#: engines/phoenixvr/metaengine.cpp:82 engines/scumm/help.cpp:228
 #: engines/scumm/help.cpp:237 engines/scumm/help.cpp:244
 #: engines/scumm/help.cpp:252 engines/sherlock/metaengine.cpp:435
 #: engines/sludge/keymapper_tables.h:58 engines/sludge/keymapper_tables.h:119
@@ -6701,7 +6701,7 @@ msgstr ""
 #: engines/got/metaengine.cpp:35 engines/groovie/metaengine.cpp:53
 #: engines/m4/metaengine.cpp:37 engines/mads/metaengine.cpp:116
 #: engines/nancy/metaengine.cpp:104 engines/neverhood/dialogs.cpp:47
-#: engines/pelrock/metaengine.cpp:35 engines/phoenixvr/metaengine.cpp:38
+#: engines/pelrock/metaengine.cpp:35 engines/phoenixvr/metaengine.cpp:47
 #: engines/sci/detection_options.h:105 engines/sherlock/metaengine.cpp:47
 #: engines/toltecs/metaengine.cpp:44 engines/trecision/metaengine.cpp:41
 #: engines/ultima/metaengine.cpp:56 engines/waynesworld/metaengine.cpp:37
@@ -6716,7 +6716,7 @@ msgstr "??????????????????/????????????"
 #: engines/got/metaengine.cpp:36 engines/groovie/metaengine.cpp:54
 #: engines/m4/metaengine.cpp:38 engines/mads/metaengine.cpp:117
 #: engines/nancy/metaengine.cpp:105 engines/neverhood/dialogs.cpp:48
-#: engines/pelrock/metaengine.cpp:36 engines/phoenixvr/metaengine.cpp:39
+#: engines/pelrock/metaengine.cpp:36 engines/phoenixvr/metaengine.cpp:48
 #: engines/sci/detection_options.h:106 engines/sherlock/metaengine.cpp:48
 #: engines/toltecs/metaengine.cpp:45 engines/trecision/metaengine.cpp:42
 #: engines/ultima/metaengine.cpp:57 engines/waynesworld/metaengine.cpp:38
@@ -7306,7 +7306,7 @@ msgid "Show version"
 msgstr "????????????"
 
 #: engines/asylum/metaengine.cpp:120 engines/crab/input/input.cpp:271
-#: engines/phoenixvr/metaengine.cpp:96 engines/queen/metaengine.cpp:253
+#: engines/phoenixvr/metaengine.cpp:105 engines/queen/metaengine.cpp:253
 #: engines/sludge/keymapper_tables.h:308 engines/sludge/keymapper_tables.h:404
 #: engines/sludge/keymapper_tables.h:471 engines/sludge/keymapper_tables.h:502
 #: engines/ultima/nuvie/metaengine.cpp:92
@@ -7315,7 +7315,7 @@ msgid "Quick load"
 msgstr "????????????"
 
 #: engines/asylum/metaengine.cpp:125 engines/crab/input/input.cpp:266
-#: engines/phoenixvr/metaengine.cpp:91 engines/queen/metaengine.cpp:247
+#: engines/phoenixvr/metaengine.cpp:100 engines/queen/metaengine.cpp:247
 #: engines/sludge/keymapper_tables.h:64 engines/sludge/keymapper_tables.h:303
 #: engines/sludge/keymapper_tables.h:410 engines/sludge/keymapper_tables.h:465
 #: engines/sludge/keymapper_tables.h:496 engines/ultima/nuvie/metaengine.cpp:91
@@ -7628,7 +7628,7 @@ msgstr "????????????"
 #. I18N: This keymap allows skipping video cutscenes
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
-#: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
+#: engines/phoenixvr/metaengine.cpp:94 engines/scumm/help.cpp:79
 #: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
@@ -7824,7 +7824,7 @@ msgstr "???????????????"
 #: engines/freescape/games/dark/dark.cpp:236
 #: engines/freescape/games/driller/driller.cpp:129
 #: engines/freescape/games/eclipse/eclipse.cpp:340
-#: engines/mads/metaengine.cpp:403 engines/phoenixvr/metaengine.cpp:80
+#: engines/mads/metaengine.cpp:403 engines/phoenixvr/metaengine.cpp:89
 #: engines/saga/metaengine.cpp:437 engines/sherlock/metaengine.cpp:359
 #: engines/sherlock/metaengine.cpp:618 engines/sherlock/metaengine.cpp:852
 #: engines/stark/metaengine.cpp:280 engines/sword1/metaengine.cpp:295
@@ -8539,7 +8539,7 @@ msgstr ""
 
 #. I18N: Perform action on object where cursor points
 #: engines/darkseed/metaengine.cpp:42 engines/dragons/metaengine.cpp:135
-#: engines/phoenixvr/metaengine.cpp:66 engines/tinsel/metaengine.cpp:294
+#: engines/phoenixvr/metaengine.cpp:75 engines/tinsel/metaengine.cpp:294
 #: engines/twp/metaengine.cpp:150
 msgid "Action"
 msgstr "??????"
@@ -12405,7 +12405,7 @@ msgstr "??????/???????????????"
 msgid "Close current interface"
 msgstr "??????????????????"
 
-#: engines/phoenixvr/metaengine.cpp:107
+#: engines/phoenixvr/metaengine.cpp:116
 #: engines/wintermute/keymapper_tables.h:239
 #: engines/wintermute/keymapper_tables.h:262
 #: engines/wintermute/keymapper_tables.h:313
diff --git a/po/zh_Hant.po b/po/zh_Hant.po
index 343c57a37c6..13f55fee67b 100644
--- a/po/zh_Hant.po
+++ b/po/zh_Hant.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.9.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-11 13:12+0000\n"
+"POT-Creation-Date: 2026-06-11 22:10+0000\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: Automatically generated\n"
 "Language-Team: none\n"
@@ -5434,7 +5434,7 @@ msgstr ""
 #: backends/platform/sdl/miyoo/miyoo.cpp:114
 #: engines/alcachofa/metaengine.cpp:110 engines/grim/grim.cpp:545
 #: engines/grim/grim.cpp:645 engines/hdb/metaengine.cpp:264
-#: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:101
+#: engines/made/metaengine.cpp:186 engines/phoenixvr/metaengine.cpp:110
 #: engines/scumm/metaengine.cpp:1232 engines/toltecs/metaengine.cpp:252
 msgid "Menu"
 msgstr ""
@@ -5810,7 +5810,7 @@ msgstr ""
 #: engines/grim/grim.cpp:621 engines/hdb/metaengine.cpp:251
 #: engines/hpl1/metaengine.cpp:94 engines/icb/icb.cpp:101
 #: engines/icb/icb.cpp:179 engines/petka/metaengine.cpp:173
-#: engines/phoenixvr/metaengine.cpp:73 engines/scumm/help.cpp:228
+#: engines/phoenixvr/metaengine.cpp:82 engines/scumm/help.cpp:228
 #: engines/scumm/help.cpp:237 engines/scumm/help.cpp:244
 #: engines/scumm/help.cpp:252 engines/sherlock/metaengine.cpp:435
 #: engines/sludge/keymapper_tables.h:58 engines/sludge/keymapper_tables.h:119
@@ -5949,7 +5949,7 @@ msgstr ""
 #: engines/got/metaengine.cpp:35 engines/groovie/metaengine.cpp:53
 #: engines/m4/metaengine.cpp:37 engines/mads/metaengine.cpp:116
 #: engines/nancy/metaengine.cpp:104 engines/neverhood/dialogs.cpp:47
-#: engines/pelrock/metaengine.cpp:35 engines/phoenixvr/metaengine.cpp:38
+#: engines/pelrock/metaengine.cpp:35 engines/phoenixvr/metaengine.cpp:47
 #: engines/sci/detection_options.h:105 engines/sherlock/metaengine.cpp:47
 #: engines/toltecs/metaengine.cpp:44 engines/trecision/metaengine.cpp:41
 #: engines/ultima/metaengine.cpp:56 engines/waynesworld/metaengine.cpp:37
@@ -5964,7 +5964,7 @@ msgstr ""
 #: engines/got/metaengine.cpp:36 engines/groovie/metaengine.cpp:54
 #: engines/m4/metaengine.cpp:38 engines/mads/metaengine.cpp:117
 #: engines/nancy/metaengine.cpp:105 engines/neverhood/dialogs.cpp:48
-#: engines/pelrock/metaengine.cpp:36 engines/phoenixvr/metaengine.cpp:39
+#: engines/pelrock/metaengine.cpp:36 engines/phoenixvr/metaengine.cpp:48
 #: engines/sci/detection_options.h:106 engines/sherlock/metaengine.cpp:48
 #: engines/toltecs/metaengine.cpp:45 engines/trecision/metaengine.cpp:42
 #: engines/ultima/metaengine.cpp:57 engines/waynesworld/metaengine.cpp:38
@@ -6494,7 +6494,7 @@ msgid "Show version"
 msgstr ""
 
 #: engines/asylum/metaengine.cpp:120 engines/crab/input/input.cpp:271
-#: engines/phoenixvr/metaengine.cpp:96 engines/queen/metaengine.cpp:253
+#: engines/phoenixvr/metaengine.cpp:105 engines/queen/metaengine.cpp:253
 #: engines/sludge/keymapper_tables.h:308 engines/sludge/keymapper_tables.h:404
 #: engines/sludge/keymapper_tables.h:471 engines/sludge/keymapper_tables.h:502
 #: engines/ultima/nuvie/metaengine.cpp:92
@@ -6503,7 +6503,7 @@ msgid "Quick load"
 msgstr ""
 
 #: engines/asylum/metaengine.cpp:125 engines/crab/input/input.cpp:266
-#: engines/phoenixvr/metaengine.cpp:91 engines/queen/metaengine.cpp:247
+#: engines/phoenixvr/metaengine.cpp:100 engines/queen/metaengine.cpp:247
 #: engines/sludge/keymapper_tables.h:64 engines/sludge/keymapper_tables.h:303
 #: engines/sludge/keymapper_tables.h:410 engines/sludge/keymapper_tables.h:465
 #: engines/sludge/keymapper_tables.h:496 engines/ultima/nuvie/metaengine.cpp:91
@@ -6795,7 +6795,7 @@ msgstr ""
 #. I18N: This keymap allows skipping video cutscenes
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
-#: engines/phoenixvr/metaengine.cpp:85 engines/scumm/help.cpp:79
+#: engines/phoenixvr/metaengine.cpp:94 engines/scumm/help.cpp:79
 #: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
 #: engines/zvision/metaengine.cpp:304
@@ -6975,7 +6975,7 @@ msgstr ""
 #: engines/freescape/games/dark/dark.cpp:236
 #: engines/freescape/games/driller/driller.cpp:129
 #: engines/freescape/games/eclipse/eclipse.cpp:340
-#: engines/mads/metaengine.cpp:403 engines/phoenixvr/metaengine.cpp:80
+#: engines/mads/metaengine.cpp:403 engines/phoenixvr/metaengine.cpp:89
 #: engines/saga/metaengine.cpp:437 engines/sherlock/metaengine.cpp:359
 #: engines/sherlock/metaengine.cpp:618 engines/sherlock/metaengine.cpp:852
 #: engines/stark/metaengine.cpp:280 engines/sword1/metaengine.cpp:295
@@ -7592,7 +7592,7 @@ msgstr ""
 
 #. I18N: Perform action on object where cursor points
 #: engines/darkseed/metaengine.cpp:42 engines/dragons/metaengine.cpp:135
-#: engines/phoenixvr/metaengine.cpp:66 engines/tinsel/metaengine.cpp:294
+#: engines/phoenixvr/metaengine.cpp:75 engines/tinsel/metaengine.cpp:294
 #: engines/twp/metaengine.cpp:150
 msgid "Action"
 msgstr ""
@@ -10877,7 +10877,7 @@ msgstr ""
 msgid "Close current interface"
 msgstr ""
 
-#: engines/phoenixvr/metaengine.cpp:107
+#: engines/phoenixvr/metaengine.cpp:116
 #: engines/wintermute/keymapper_tables.h:239
 #: engines/wintermute/keymapper_tables.h:262
 #: engines/wintermute/keymapper_tables.h:313



From noreply at scummvm.org  Thu Jun 11 23:29:26 2026
From: noreply at scummvm.org (mduggan)
Date: Thu, 11 Jun 2026 23:29:26 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 39f57e021b07c9f20f7739a6393ad89ba342a604
Message-ID: <20260611232926.6F37F7005BC@ron.scummvm.net>

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

Summary:
39f57e021b ACCESS: Avoid double-free on Noctropolis vid audio


Commit: 39f57e021b07c9f20f7739a6393ad89ba342a604
    https://github.com/scummvm/scummvm/commit/39f57e021b07c9f20f7739a6393ad89ba342a604
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2026-06-12T09:29:14+10:00

Commit Message:
ACCESS: Avoid double-free on Noctropolis vid audio

Quitting the game when a video is in the stopped state causes close to be
called twice.  Clear the stream pointer so we don't try to double-free it.

Changed paths:
    engines/access/video.cpp


diff --git a/engines/access/video.cpp b/engines/access/video.cpp
index 56979a14830..403b20b809a 100644
--- a/engines/access/video.cpp
+++ b/engines/access/video.cpp
@@ -490,6 +490,8 @@ void VideoPlayer_v2::closeVideo() {
 	if (_audioStream) {
 		_audioStream->finish();
 		_vm->_mixer->stopHandle(_audioStreamHandle);
+		// stream is freed by the mixer.
+		_audioStream = nullptr;
 	}
 	delete _frame;
 	_frame = nullptr;



From noreply at scummvm.org  Fri Jun 12 06:39:01 2026
From: noreply at scummvm.org (neuromancer)
Date: Fri, 12 Jun 2026 06:39:01 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 d1bb58f3ed7276f6bfe7824a7d9ab72c13839884
Message-ID: <20260612063901.24D467005CC@ron.scummvm.net>

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

Summary:
d1bb58f3ed FREESCAPE: ignore fully transparent objects when shooting


Commit: d1bb58f3ed7276f6bfe7824a7d9ab72c13839884
    https://github.com/scummvm/scummvm/commit/d1bb58f3ed7276f6bfe7824a7d9ab72c13839884
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-12T08:38:29+02:00

Commit Message:
FREESCAPE: ignore fully transparent objects when shooting

Changed paths:
    engines/freescape/area.cpp
    engines/freescape/area.h
    engines/freescape/movement.cpp
    engines/freescape/objects/geometricobject.cpp
    engines/freescape/objects/geometricobject.h


diff --git a/engines/freescape/area.cpp b/engines/freescape/area.cpp
index 09de952404a..65b6d3b4fbd 100644
--- a/engines/freescape/area.cpp
+++ b/engines/freescape/area.cpp
@@ -751,19 +751,23 @@ bool Area::hasActiveGroups() {
 	return false;
 }
 
-Object *Area::checkCollisionRay(const Math::Ray &ray, int raySize) {
+Object *Area::checkCollisionRay(const Math::Ray &ray, int raySize, bool skipTransparent) {
 	float distance = 1.0;
 	float size = 16.0 * 8192.0; // TODO: check if this is the max size
 	Math::AABB boundingBox(ray.getOrigin(), ray.getOrigin());
 	Object *collided = nullptr;
 	for (auto &obj : _drawableObjects) {
-		if (obj->getType() == kLineType)
+		if (obj->getType() == kLineType) {
 			// If the line is not along an axis, the AABB is wildly inaccurate so we skip it
 			if (((GeometricObject *)obj)->isLineButNotStraight())
 				continue;
+		}
 
 		if (!obj->isDestroyed() && !obj->isInvisible() && obj->isGeometric()) {
 			GeometricObject *gobj = (GeometricObject *)obj;
+			if (skipTransparent && gobj->isFullyTransparent())
+				continue;
+
 			Math::Vector3d collidedNormal;
 			float collidedDistance = sweepAABB(boundingBox, gobj->_boundingBox, raySize * ray.getDirection(), collidedNormal);
 			debugC(1, kFreescapeDebugMove, "reached obj id: %d with distance %f", obj->getObjectID(), collidedDistance);
diff --git a/engines/freescape/area.h b/engines/freescape/area.h
index 4c6bb1ed9e0..18604f84736 100644
--- a/engines/freescape/area.h
+++ b/engines/freescape/area.h
@@ -69,7 +69,7 @@ public:
 	void drawGroup(Renderer *gfx, Group *group, bool runAnimation);
 	void show();
 
-	Object *checkCollisionRay(const Math::Ray &ray, int raySize);
+	Object *checkCollisionRay(const Math::Ray &ray, int raySize, bool skipTransparent = false);
 	bool checkInSight(const Math::Ray &ray, float maxDistance);
 	Math::Vector3d separateFromWall(const Math::Vector3d &position);
 	ObjectArray checkCollisions(const Math::AABB &boundingBox);
diff --git a/engines/freescape/movement.cpp b/engines/freescape/movement.cpp
index b694793a5bb..5303d8c79a5 100644
--- a/engines/freescape/movement.cpp
+++ b/engines/freescape/movement.cpp
@@ -327,7 +327,8 @@ void FreescapeEngine::shoot() {
 
 	Math::Vector3d direction = directionToVector(_pitch + angleOffsetY, _yaw - angleOffsetX, false);
 	Math::Ray ray(_position, direction);
-	Object *shot = _currentArea->checkCollisionRay(ray, 8192);
+	// Original shooting picks from rendered faces, so fully transparent geometry does not catch shots.
+	Object *shot = _currentArea->checkCollisionRay(ray, 8192, true);
 	if (shot) {
 		GeometricObject *gobj = (GeometricObject *)shot;
 		debugC(1, kFreescapeDebugMove, "Shot object %d with flags %x", gobj->getObjectID(), gobj->getObjectFlags());
diff --git a/engines/freescape/objects/geometricobject.cpp b/engines/freescape/objects/geometricobject.cpp
index ff9cad736d8..311f462698e 100644
--- a/engines/freescape/objects/geometricobject.cpp
+++ b/engines/freescape/objects/geometricobject.cpp
@@ -490,4 +490,19 @@ void GeometricObject::setColor(uint idx, int color) {
 	(*_colours)[idx] = color;
 }
 
+bool GeometricObject::isFullyTransparent() const {
+	if (!_colours || _colours->size() == 0)
+		return false;
+
+	for (uint i = 0; i < _colours->size(); i++) {
+		if ((*_colours)[i] != 0)
+			return false;
+
+		if (_ecolours && i < _ecolours->size() && (*_ecolours)[i] != 0)
+			return false;
+	}
+
+	return true;
+}
+
 } // End of namespace Freescape
diff --git a/engines/freescape/objects/geometricobject.h b/engines/freescape/objects/geometricobject.h
index 9a08ecf12b9..850d71eced1 100644
--- a/engines/freescape/objects/geometricobject.h
+++ b/engines/freescape/objects/geometricobject.h
@@ -60,6 +60,7 @@ public:
 	bool collides(const Math::AABB &boundingBox);
 	void draw(Freescape::Renderer *gfx, float offset = 0.0) override;
 	void setColor(uint idx, int color);
+	bool isFullyTransparent() const;
 	bool isDrawable() override;
 	bool isPlanar() override;
 	bool _cyclingColors;



From noreply at scummvm.org  Fri Jun 12 07:06:55 2026
From: noreply at scummvm.org (dreammaster)
Date: Fri, 12 Jun 2026 07:06:55 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 e70eae4652b69929d68e43205aa4b73e2fc4dea6
Message-ID: <20260612070655.13F187005CC@ron.scummvm.net>

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

Summary:
e70eae4652 MADS: FOREST: Implemented room 307


Commit: e70eae4652b69929d68e43205aa4b73e2fc4dea6
    https://github.com/scummvm/scummvm/commit/e70eae4652b69929d68e43205aa4b73e2fc4dea6
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-06-12T17:06:34+10:00

Commit Message:
MADS: FOREST: Implemented room 307

Changed paths:
    engines/mads/madsv2/forest/rooms/room307.cpp


diff --git a/engines/mads/madsv2/forest/rooms/room307.cpp b/engines/mads/madsv2/forest/rooms/room307.cpp
index bbe1745f3b4..fbac29ef51b 100644
--- a/engines/mads/madsv2/forest/rooms/room307.cpp
+++ b/engines/mads/madsv2/forest/rooms/room307.cpp
@@ -1,4 +1,4 @@
-???/* ScummVM - Graphic Adventure Engine
+/* 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
@@ -20,17 +20,23 @@
  */
 
 #include "mads/madsv2/core/conv.h"
+#include "mads/madsv2/core/digi.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/imath.h"
 #include "mads/madsv2/core/inter.h"
 #include "mads/madsv2/core/kernel.h"
+#include "mads/madsv2/core/midi.h"
+#include "mads/madsv2/core/object.h"
+#include "mads/madsv2/core/player.h"
 #include "mads/madsv2/core/sound.h"
+#include "mads/madsv2/core/sprite.h"
 #include "mads/madsv2/core/text.h"
 #include "mads/madsv2/forest/mads/inventory.h"
 #include "mads/madsv2/forest/mads/sounds.h"
 #include "mads/madsv2/forest/mads/words.h"
 #include "mads/madsv2/forest/global.h"
-#include "mads/madsv2/forest/rooms/section1.h"
+#include "mads/madsv2/forest/journal.h"
+#include "mads/madsv2/forest/rooms/section3.h"
 #include "mads/madsv2/forest/rooms/room307.h"
 
 namespace MADS {
@@ -39,10 +45,27 @@ namespace Forest {
 namespace Rooms {
 
 struct Scratch {
-	int16 sprite[10];       /* Sprite series handles */
-	int16 sequence[10];     /* Sequence handles      */
-	int16 animation[10];     /* Animation handles     */
-	AnimationInfo animation_info[10];
+	int16 sprite[10];            // 0x00???0x13
+	int16 sequence[10];          // 0x14???0x27
+	int16 animation[10];         // 0x28???0x3B
+	AnimationInfo animation_info[10]; // 0x3C???0x8B
+	int16 _pad8C[3];             // 0x8C???0x91
+	int16 _92;                   // 0x92
+	int16 _pad94;                // 0x94
+	int16 _96;                   // 0x96
+	int16 _pad98;                // 0x98
+	int16 _9a;                   // 0x9A
+	int16 _9c;                   // 0x9C
+	int16 _9e;                   // 0x9E
+	int16 _a0;                   // 0xA0
+	int16 _a2;                   // 0xA2
+	int16 _a4;                   // 0xA4
+	int16 _a6;                   // 0xA6
+	int16 _a8;                   // 0xA8
+	int16 _aa;                   // 0xAA
+	int16 _ac;                   // 0xAC
+	int16 _ae;                   // 0xAE
+	int16 _b0;                   // 0xB0
 };
 
 static Scratch scratch;
@@ -54,22 +77,883 @@ static Scratch scratch;
 #define aainfo scratch.animation_info
 
 
+static void room_307_init1() {
+	// No implementation
+}
+
+static void room_307_init2() {
+	aa[6] = kernel_run_animation(kernel_name('u', 1), 0);
+	kernel_translate_anim(aa[6], -75, 16, 0);
+	aainfo[6]._active = -1;
+	aainfo[6]._val3 = 0;
+
+	if (player.been_here_before != 0 && previous_room != 308) {
+		scratch._9c = kernel_run_animation_disp('r', 9, 0);
+		kernel_reset_animation(scratch._9c, 2);
+		kernel_position_anim(scratch._9c, 18, 130, 95, 3);
+		scratch._9a = kernel_run_animation_disp('e', 6, 0);
+		kernel_reset_animation(scratch._9a, 2);
+		kernel_position_anim(scratch._9a, 71, 81, 87, 7);
+		global[g131] = 0;
+		global[g141] = 0;
+		global[g133] = 1;
+		global[g143] = 1;
+	}
+
+	if (previous_room != KERNEL_RESTORING_GAME) {
+		player.x = 106;
+		player.y = 107;
+		player.facing = 3;
+	}
+
+	scratch._ac = 0;
+
+	switch (previous_room) {
+	case KERNEL_RESTORING_GAME:
+		global[g131] = -1;
+		global[g141] = -1;
+		kernel_reset_animation(scratch._9a, 2);
+		kernel_reset_animation(scratch._9c, 2);
+		global[g133] = 0;
+		global[g143] = 0;
+		return;
+	case 199:
+		global[g131] = -1;
+		global[g141] = -1;
+		kernel_reset_animation(scratch._9a, 2);
+		kernel_reset_animation(scratch._9c, 2);
+		global[g133] = 0;
+		global[g143] = 0;
+		restore_player();
+		player.commands_allowed = -1;
+		player.walker_visible = -1;
+		return;
+	case 210:
+		aa[0] = kernel_run_animation(kernel_name('y', 1), 100);
+		aainfo[0]._active = -1;
+		scratch._92 = 85;
+		return;
+	case 301:
+		aa[0] = kernel_run_animation(kernel_name('y', 2), 100);
+		aainfo[0]._active = -1;
+		scratch._92 = 40;
+		return;
+	case 308:
+		midi_stop();
+		aa[0] = kernel_run_animation(kernel_name('y', 3), 100);
+		aainfo[0]._active = -1;
+		scratch._92 = 109;
+		scratch._ac = -1;
+		return;
+	default:
+		global[g131] = -1;
+		global[g141] = -1;
+		kernel_reset_animation(scratch._9a, 2);
+		kernel_reset_animation(scratch._9c, 2);
+		global[g133] = 0;
+		global[g143] = 0;
+		player.commands_allowed = -1;
+		player.walker_visible = -1;
+		return;
+	}
+}
+
+static void room_307_anim1() {
+	int16 cur = kernel_anim[aa[0]].frame;
+	if (cur != aainfo[0]._frame)
+		aainfo[0]._frame = cur;
+	if (global[player_hyperwalked] == -1) {
+		aainfo[0]._frame = scratch._92 - 1;
+		kernel_reset_animation(aa[0], aainfo[0]._frame);
+	}
+}
+
+static void room_307_anim2() {
+	int16 cur = kernel_anim[aa[1]].frame;
+	if (cur != aainfo[1]._frame)
+		aainfo[1]._frame = cur;
+	if (global[player_hyperwalked] == -1) {
+		aainfo[1]._frame = 65;
+		kernel_reset_animation(aa[1], 65);
+	}
+}
+
+static void room_307_anim3() {
+	int16 var_4 = -1;
+	int16 cur = kernel_anim[aa[2]].frame;
+	if (cur == aainfo[2]._frame)
+		return;
+	aainfo[2]._frame = cur;
+	if (scratch._ac != 0) {
+		if (cur == 31) {
+			digi_play_build(307, '_', 2, 2);
+			scratch._aa = -1;
+		} else if (cur == 35) {
+			digi_play_build(101, '_', 1, 2);
+			scratch._aa = -1;
+		}
+	} else {
+		switch (cur) {
+		case 21: {
+			int16 old = seq[1];
+			kernel_seq_delete(seq[1]);
+			kernel_synch(KERNEL_SERIES, old, KERNEL_ANIM, aa[2]);
+			inter_move_object(14, 2);
+			break;
+		}
+		case 22: {
+			int16 old = seq[0];
+			kernel_seq_delete(seq[0]);
+			inter_move_object(4, 2);
+			kernel_synch(KERNEL_SERIES, old, KERNEL_ANIM, aa[2]);
+			kernel_flip_hotspot(113, 0);
+			break;
+		}
+		case 28:
+			digi_play_build(307, '_', 2, 2);
+			break;
+		case 31:
+			digi_play_build(101, '_', 1, 2);
+			break;
+		case 54:
+			dont_frag_the_palette();
+			kernel_abort_animation(aa[2]);
+			aainfo[2]._active = 0;
+			kernel_reset_animation(scratch._9c, 1);
+			kernel_synch(KERNEL_ANIM, scratch._9c, KERNEL_NOW, 0);
+			global[g143] = 0;
+			kernel_reset_animation(scratch._9a, 1);
+			kernel_synch(KERNEL_ANIM, scratch._9a, KERNEL_NOW, 0);
+			global[g133] = 0;
+			player.commands_allowed = -1;
+			break;
+		default:
+			break;
+		}
+	}
+	if (var_4 >= 0) {
+		kernel_reset_animation(aa[2], var_4);
+		aainfo[2]._frame = var_4;
+	}
+}
+
+static void room_307_anim4() {
+	int16 result = -1;
+	int16 cur = kernel_anim[aa[3]].frame;
+	if (cur == aainfo[3]._frame)
+		return;
+	aainfo[3]._frame = cur;
+	if (cur == 57) {
+		dont_frag_the_palette();
+		kernel_abort_animation(aa[3]);
+		aainfo[3]._active = 0;
+		kernel_reset_animation(scratch._9a, 1);
+		kernel_synch(KERNEL_ANIM, scratch._9a, KERNEL_NOW, 0);
+		global[g133] = 0;
+		player.commands_allowed = -1;
+	} else if (cur < 57) {
+		if (cur == 29) {
+			digi_play_build(305, 'e', 1, 1);
+			scratch._aa = 3;
+		} else if (cur >= 30 && cur <= 31) {
+			if (aainfo[3]._val3 == 1)
+				result = imath_random(29, 30);
+		}
+	}
+	if (result >= 0) {
+		kernel_reset_animation(aa[3], result);
+		aainfo[3]._frame = result;
+	}
+}
+
+static void room_307_anim5() {
+	if (scratch._aa == 0)
+		return;
+	if (kernel.trigger != 7 && kernel.trigger != 28)
+		return;
+
+	switch (scratch._aa) {
+	case 10:
+		global[g155] = 0;
+		// fall through
+	case 30:
+		kernel_timing_trigger(30, 28);
+		scratch._aa++;
+		break;
+	case 11:
+		aainfo[6]._val3 = 1;
+		scratch._aa++;
+		digi_play_build(307, 'U', 1, 1);
+		kernel_timing_trigger(30, 109);
+		kernel_timing_trigger(200, 114);
+		break;
+	case 12:
+	case 28:
+		aainfo[6]._val3 = 4;
+		scratch._aa++;
+		break;
+	case 14:
+	case 18:
+	case 24:
+		aainfo[6]._val3 = 0;
+		kernel_timing_trigger(30, 28);
+		scratch._aa++;
+		break;
+	case 15:
+		global[g156] = 2;
+		scratch._aa++;
+		digi_play_build(307, 'E', 1, 1);
+		break;
+	case 16:
+	case 22:
+	case 26:
+	case 32:
+		global[g156] = 0;
+		kernel_timing_trigger(30, 28);
+		scratch._aa++;
+		break;
+	case 17:
+		aainfo[6]._val3 = 1;
+		scratch._aa++;
+		digi_play_build(307, 'U', 3, 1);
+		break;
+	case 19:
+		scratch._aa++;
+		global[g156] = 2;
+		digi_play_build(307, 'E', 2, 1);
+		if (digi_val1 == 0)
+			digi_play_build(307, '_', 1, 2);
+		break;
+	case 20:
+		aainfo[6]._val3 = 4;
+		global[g156] = 0;
+		scratch._aa++;
+		break;
+	case 23:
+		aainfo[6]._val3 = 1;
+		scratch._aa++;
+		digi_play_build(307, 'u', 5, 1);
+		break;
+	case 25:
+		global[g156] = 2;
+		scratch._aa++;
+		digi_play_build(307, 'E', 3, 1);
+		break;
+	case 27:
+		aainfo[6]._val3 = 1;
+		scratch._aa++;
+		digi_play_build(307, 'U', 7, 1);
+		kernel_timing_trigger(200, 114);
+		break;
+	case 31:
+		global[g156] = 2;
+		scratch._aa++;
+		digi_play_build(307, 'e', 4, 1);
+		break;
+	case 33:
+		aainfo[6]._val3 = 1;
+		scratch._aa++;
+		digi_play_build(307, 'u', 9, 1);
+		kernel_timing_trigger(300, 114);
+		break;
+	case 34:
+		aainfo[6]._val3 = 3;
+		dont_frag_the_palette();
+		kernel_abort_animation(scratch._a4);
+		dont_frag_the_palette();
+		kernel_abort_animation(scratch._a8);
+		dont_frag_the_palette();
+		kernel_abort_animation(scratch._a6);
+		aainfo[4]._active = 0;
+		scratch._a0 = 0;
+		scratch._aa = -1;
+		player.walker_visible = -1;
+		global[g017] = -1;
+		kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
+		scratch._9c = kernel_run_animation_disp('r', 9, 0);
+		kernel_position_anim(scratch._9c, 18, 130, 95, 3);
+		global[g141] = -1;
+		kernel_reset_animation(scratch._9c, 2);
+		global[g143] = 0;
+		scratch._ae = kernel_run_animation_write(0);
+		kernel_reset_animation(scratch._ae, 1);
+		kernel_position_anim(scratch._ae, 71, 81, 87, 7);
+		global[walker_converse_now] = 1;
+		global[g007] = 6;
+		kernel_synch(KERNEL_ANIM, scratch._ae, KERNEL_NOW, 0);
+		break;
+	default:
+		break;
+	}
+}
+
+static void room_307_anim7() {
+	int16 result = -1;
+	int16 var_2;
+	int16 cur = kernel_anim[aa[6]].frame;
+	if (cur == aainfo[6]._frame)
+		return;
+	aainfo[6]._frame = cur;
+
+	bool dispatch = (cur == 1) || (cur >= 3 && cur <= 12) || (cur == 15);
+	if (!dispatch) {
+		if (result >= 0) {
+			kernel_reset_animation(aa[6], result);
+			aainfo[6]._frame = result;
+		}
+		return;
+	}
+
+	switch (aainfo[6]._val3) {
+	case 0:
+	case 3:
+	case 4:
+		if (cur >= 8 && cur <= 12) {
+			aainfo[6]._val4++;
+			if (imath_random(1, 6) < aainfo[6]._val4) {
+				digi_play_build(307, '_', 4, 2);
+				result = 0;
+				aainfo[6]._val4 = 0;
+				if (scratch._b0 != 0) {
+					player.commands_allowed = -1;
+					scratch._b0 = 0;
+				}
+				if (aainfo[6]._val3 == 4) {
+					aainfo[6]._val3 = 0;
+					scratch._aa++;
+					kernel_timing_trigger(1, 28);
+				}
+			} else {
+				do {
+					result = imath_random(7, 11);
+				} while (aainfo[6]._frame == result + 1);
+			}
+		} else {
+			aainfo[6]._val4++;
+			if (imath_random(10, 20) < aainfo[6]._val4) {
+				aainfo[6]._val4 = 0;
+				var_2 = imath_random(2, 4);
+			} else {
+				var_2 = 3;
+			}
+			if (aainfo[6]._val3 == 3) {
+				var_2 = 1;
+				aainfo[6]._val3 = 0;
+				aainfo[6]._val4 = 0;
+			}
+			if (aainfo[6]._val3 == 4) {
+				var_2 = 1;
+				aainfo[6]._val4 = 0;
+			}
+			switch (var_2) {
+			case 1:
+				result = imath_random(7, 11);
+				digi_play_build(307, '_', 1, 2);
+				break;
+			case 2:
+				result = 1;
+				break;
+			default:
+				result = 0;
+				break;
+			}
+		}
+		break;
+	case 1:
+		result = imath_random(3, 6);
+		break;
+	case 2:
+		result = 14;
+		break;
+	default:
+		break;
+	}
+
+	if (result >= 0) {
+		kernel_reset_animation(aa[6], result);
+		aainfo[6]._frame = result;
+	}
+}
+
+static void room_307_anim9() {
+	int16 cur = kernel_anim[aa[8]].frame;
+	if (cur != aainfo[8]._frame) {
+		aainfo[8]._frame = cur;
+		if (cur == 22 && scratch._9a == -1)
+			digi_play_build(101, '_', 2, 2);
+	}
+	if (global[player_hyperwalked] == -1) {
+		aainfo[8]._frame = scratch._92 - 1;
+		kernel_reset_animation(aa[8], aainfo[8]._frame);
+	}
+}
+
+static void room_307_anim10() {
+	int16 result = -1;
+	int16 cur = kernel_anim[aa[9]].frame;
+	if (cur == aainfo[9]._frame)
+		return;
+	aainfo[9]._frame = cur;
+	switch (cur) {
+	case 3:
+		digi_play_build(307, 'e', 5, 1);
+		scratch._aa = -1;
+		break;
+	case 20:
+		digi_play_build(307, 'r', 2, 1);
+		scratch._aa = -1;
+		break;
+	case 43: {
+		int16 old = seq[1];
+		kernel_seq_delete(seq[1]);
+		kernel_synch(KERNEL_SERIES, old, KERNEL_ANIM, aa[9]);
+		break;
+	}
+	default:
+		break;
+	}
+	if (result >= 0) {
+		kernel_reset_animation(aa[9], result);
+		aainfo[9]._frame = result;
+	}
+}
+
 static void room_307_init() {
+	object_set_quality(4, 0, -1);
+	object_set_quality(14, 0, -1);
+
+	scratch._9a = -1;
+	scratch._9c = -1;
+	scratch._ae = -1;
+	global[perform_displacements] = 120;
+	stop_speech_on_run_animation = false;
+	scratch._b0 = 0;
+	scratch._a0 = 0;
+	global[g067] = -1;
+	scratch._a2 = -1;
+
+	kernel_flip_hotspot(global[g075] != 0 ? 28 : 27, 0);
+
+	if (object_is_here(4)) {
+		ss[0] = kernel_load_series(kernel_name('p', 1), 0);
+		seq[0] = kernel_seq_stamp(ss[0], false, -1);
+		kernel_seq_depth(seq[0], 10);
+		kernel_seq_loc(seq[0], 184, 149);
+		kernel_seq_scale(seq[0], 98);
+	} else {
+		kernel_flip_hotspot(113, 0);
+	}
+
+	if (object_is_here(14)) {
+		ss[1] = kernel_load_series(kernel_name('p', 2), 0);
+		seq[1] = kernel_seq_stamp(ss[1], false, -1);
+		kernel_seq_depth(seq[1], 9);
+		kernel_seq_loc(seq[1], 146, 134);
+		kernel_seq_scale(seq[1], 100);
+	}
+
+	if (previous_room != KERNEL_RESTORING_GAME) {
+		if (previous_room != 199) {
+			player.walker_visible = 0;
+			player.commands_allowed = 0;
+			scratch._aa = 0;
+		}
+
+		for (int16 i = 0; i < 10; i++) {
+			aainfo[i]._active = 0;
+			aainfo[i]._frame = 1;
+			aainfo[i]._val3 = 0;
+			aainfo[i]._val4 = 0;
+		}
+
+		if (previous_room != 199) {
+			if (flags[21] != 3)
+				flags[21]++;
+		}
+	}
+
+	switch (flags[21]) {
+	case -3:
+		room_307_init1();
+		flags[21] = 1;
+		room_307_init2();
+		break;
+	default:
+		room_307_init2();
+		break;
+	}
 }
 
 static void room_307_daemon() {
+	switch (kernel.trigger) {
+	case 7: {
+		if (global[walker_converse_state] != 0) {
+			global[walker_converse_state] = 0;
+			close_journal(3);
+			player.commands_allowed = -1;
+		} else if (scratch._aa == 3) {
+			aainfo[3]._val3 = 2;
+			scratch._aa = -1;
+			scratch._a0 = 0;
+		} else if (scratch._aa == 4) {
+			dont_frag_the_palette();
+			kernel_abort_animation(scratch._a4);
+			scratch._aa = -1;
+			scratch._a0 = 0;
+			scratch._9e = kernel_run_animation_point(1, 'h');
+			kernel_position_anim(scratch._9e, player.x, player.y, player.scale, player.depth);
+			kernel_synch(KERNEL_ANIM, scratch._9e, KERNEL_NOW, 0);
+		} else if (scratch._aa == 40) {
+			aainfo[6]._val3 = 0;
+			scratch._aa = -1;
+			scratch._a0 = 0;
+			player.commands_allowed = -1;
+		} else if (scratch._aa == 41) {
+			dont_frag_the_palette();
+			kernel_abort_animation(scratch._a4);
+			scratch._aa = -1;
+			scratch._a0 = 0;
+			scratch._9e = kernel_run_animation_point(3, 'i');
+			kernel_position_anim(scratch._9e, player.x, player.y, player.scale, player.depth);
+			kernel_synch(KERNEL_ANIM, scratch._9e, KERNEL_NOW, 0);
+		}
+		break;
+	}
+
+	case 24:
+		aa[3] = kernel_run_animation(kernel_name('E', 1), 0);
+		aainfo[3]._active = -1;
+		aainfo[3]._val3 = 1;
+		kernel_reset_animation(scratch._9a, 0);
+		kernel_synch(KERNEL_ANIM, aa[3], KERNEL_ANIM, scratch._9a);
+		global[g133] = 1;
+		break;
+
+	case 26:
+		if (scratch._a2 == 1) {
+			aa[1] = kernel_run_animation(kernel_name('Z', 1), 102);
+			aainfo[1]._active = -1;
+			scratch._96 = 1;
+			player.commands_allowed = 0;
+			player.walker_visible = 0;
+			kernel_reset_animation(scratch._9a, 0);
+			kernel_reset_animation(scratch._9c, 0);
+			kernel_synch(KERNEL_ANIM, scratch._9a, KERNEL_ANIM, aa[1]);
+			kernel_synch(KERNEL_ANIM, scratch._9c, KERNEL_ANIM, aa[1]);
+			kernel_synch(KERNEL_PLAYER, 0, KERNEL_ANIM, aa[1]);
+			global[g133] = 1;
+			global[g143] = 1;
+		} else if (scratch._a2 == 2) {
+			dont_frag_the_palette();
+			kernel_abort_animation(scratch._9a);
+			dont_frag_the_palette();
+			kernel_abort_animation(scratch._9c);
+			scratch._9a = -1;
+			scratch._9c = -1;
+			if (player_has(4)) {
+				aa[8] = kernel_run_animation(kernel_name('R', 4), 111);
+				aainfo[8]._active = -1;
+				scratch._92 = 32;
+				kernel_synch(KERNEL_ANIM, aa[8], KERNEL_ANIM, scratch._9c);
+			} else {
+				aa[9] = kernel_run_animation(kernel_name('R', 3), 111);
+				aainfo[9]._active = -1;
+				kernel_synch(KERNEL_ANIM, aa[9], KERNEL_ANIM, scratch._9c);
+			}
+		} else if (scratch._a2 == 3) {
+			aa[2] = kernel_run_animation(kernel_name('R', 2), 0);
+			aainfo[2]._active = -1;
+			kernel_reset_animation(scratch._9c, 0);
+			kernel_synch(KERNEL_ANIM, aa[2], KERNEL_ANIM, scratch._9c);
+			global[g143] = 1;
+			kernel_reset_animation(scratch._9a, 0);
+			kernel_synch(KERNEL_ANIM, scratch._9a, KERNEL_ANIM, scratch._9c);
+			digi_play_build(307, 'e', 5, 1);
+			scratch._aa = -1;
+		}
+		break;
+
+	case 27:
+		dont_frag_the_palette();
+		kernel_abort_animation(scratch._ae);
+		scratch._ae = -1;
+		scratch._9a = kernel_run_animation_disp('e', 6, 0);
+		kernel_position_anim(scratch._9a, 71, 81, 87, 7);
+		global[g131] = -1;
+		kernel_reset_animation(scratch._9a, 2);
+		kernel_synch(KERNEL_ANIM, scratch._9a, KERNEL_NOW, 0);
+		global[g133] = 0;
+		player.commands_allowed = -1;
+		break;
+
+	case 100:
+		dont_frag_the_palette();
+		kernel_abort_animation(aa[0]);
+		player.walker_visible = -1;
+		aainfo[0]._active = 0;
+		kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
+		if (flags[21] == 1) {
+			global[g155] = 2;
+			aainfo[4]._active = -1;
+			scratch._a0 = -1;
+			global[g017] = 0;
+			global[g154] = 0;
+			global[g156] = 0;
+			scratch._a6 = kernel_run_animation_talk('r', 9, 0);
+			kernel_position_anim(scratch._a6, 18, 130, 95, 3);
+			scratch._a8 = kernel_run_animation_talk('e', 6, 0);
+			kernel_position_anim(scratch._a8, 71, 81, 87, 7);
+			kernel_synch(KERNEL_ANIM, scratch._a8, KERNEL_ANIM, scratch._9a);
+			kernel_synch(KERNEL_ANIM, scratch._a6, KERNEL_ANIM, scratch._9c);
+			kernel_synch(KERNEL_ANIM, scratch._a4, KERNEL_PLAYER, 0);
+			digi_play_build(307, 'r', 1, 1);
+			scratch._aa = 10;
+		} else if (previous_room == 308) {
+			aa[2] = kernel_run_animation(kernel_name('R', 6), 113);
+			aainfo[2]._active = -1;
+			kernel_synch(KERNEL_ANIM, aa[2], KERNEL_PLAYER, 0);
+		} else {
+			kernel_reset_animation(scratch._9c, 1);
+			kernel_synch(KERNEL_ANIM, scratch._9c, KERNEL_NOW, 0);
+			kernel_reset_animation(scratch._9a, 1);
+			kernel_synch(KERNEL_ANIM, scratch._9a, KERNEL_NOW, 0);
+			global[g131] = -1;
+			global[g141] = -1;
+			global[g133] = 0;
+			global[g143] = 0;
+			player.commands_allowed = 0;
+			digi_play_build(307, 'u', 11, 1);
+			scratch._aa = 40;
+			aainfo[6]._val3 = 1;
+			kernel_timing_trigger(60, 110);
+		}
+		break;
+
+	case 102:
+		global[g061] = -1;
+		new_room = 210;
+		break;
+
+	case 104:
+		dont_frag_the_palette();
+		kernel_abort_animation(scratch._9e);
+		kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
+		player.walker_visible = -1;
+		global[g135] = -1;
+		break;
+
+	case 105:
+		dont_frag_the_palette();
+		kernel_abort_animation(scratch._9e);
+		kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
+		player.walker_visible = -1;
+		global[g150] = -1;
+		scratch._a2 = 3;
+		break;
+
+	case 109:
+		player.walker_visible = 0;
+		scratch._a4 = kernel_run_animation_talk('b', 9, 0);
+		kernel_position_anim(scratch._a4, player.x, player.y, player.scale, player.depth);
+		kernel_synch(KERNEL_ANIM, scratch._a4, KERNEL_PLAYER, 0);
+		break;
+
+	case 110:
+		player_demand_facing(9);
+		break;
+
+	case 111: {
+		if (player_has(14)) {
+			dont_frag_the_palette();
+			kernel_abort_animation(aa[8]);
+		} else {
+			int16 old = seq[0];
+			kernel_seq_delete(seq[0]);
+			dont_frag_the_palette();
+			kernel_abort_animation(aa[9]);
+			kernel_synch(KERNEL_SERIES, old, KERNEL_NOW, 0);
+			aainfo[9]._active = 0;
+		}
+		scratch._9a = -2;
+		aa[8] = kernel_run_animation(kernel_name('Z', 3), 112);
+		aainfo[8]._active = -1;
+		scratch._92 = 100;
+		player.walker_visible = 0;
+		kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
+		if (player_has(14)) {
+			kernel_synch(KERNEL_ANIM, aa[8], KERNEL_PLAYER, 0);
+		} else {
+			kernel_synch(KERNEL_ANIM, aa[9], KERNEL_NOW, 0);
+			inter_move_object(14, 2);
+			inter_move_object(4, 2);
+		}
+		break;
+	}
+
+	case 112:
+		new_room = 308;
+		break;
+
+	case 113:
+		dont_frag_the_palette();
+		kernel_abort_animation(aa[2]);
+		aainfo[2]._active = 0;
+		scratch._9c = kernel_run_animation_disp('r', 9, 0);
+		kernel_reset_animation(scratch._9c, 2);
+		kernel_position_anim(scratch._9c, 18, 130, 95, 3);
+		kernel_synch(KERNEL_ANIM, scratch._9c, KERNEL_NOW, 0);
+		scratch._9a = kernel_run_animation_disp('e', 6, 0);
+		kernel_reset_animation(scratch._9a, 2);
+		kernel_position_anim(scratch._9a, 71, 81, 87, 7);
+		kernel_synch(KERNEL_ANIM, scratch._9a, KERNEL_NOW, 0);
+		global[g131] = -1;
+		global[g141] = -1;
+		global[g133] = 0;
+		global[g143] = 0;
+		player.commands_allowed = 0;
+		scratch._ac = 0;
+		digi_play_build(307, 'u', 11, 1);
+		scratch._aa = 40;
+		aainfo[6]._val3 = 1;
+		kernel_timing_trigger(60, 110);
+		break;
+
+	case 114:
+		digi_play_build(307, '_', 1, 2);
+		break;
+
+	}
+
+	if (scratch._9a >= 0)
+		global_anim1(6, scratch._9a, global[g131], &global[g132]);
+	if (scratch._9c >= 0)
+		global_anim2(9, scratch._9c, global[g141], &global[g142]);
+	if (scratch._ae >= 0)
+		global_anim3(scratch._ae, &global[g008]);
+
+	if (aainfo[0]._active != 0)  room_307_anim1();
+	if (aainfo[2]._active != 0)  room_307_anim3();
+	if (aainfo[8]._active != 0)  room_307_anim9();
+	if (aainfo[9]._active != 0)  room_307_anim10();
+	if (aainfo[1]._active != 0)  room_307_anim2();
+	if (aainfo[3]._active != 0)  room_307_anim4();
+	if (aainfo[6]._active != 0)  room_307_anim7();
+	if (aainfo[4]._active != 0)  room_307_anim5();
+
+	if (scratch._a0 != 0) {
+		kernel_random_frame(scratch._a4, &global[g151], global[g154]);
+		if (scratch._aa != 41 && scratch._aa != 4) {
+			kernel_random_frame(scratch._a6, &global[g152], global[g155]);
+			kernel_random_frame(scratch._a8, &global[g153], global[g156]);
+		}
+	}
 }
 
 static void room_307_pre_parser() {
+	if (player_parse(13, 34, 0))
+		player.walk_off_edge_to_room = 301;
+
+	if (player_parse(104, 0)) {
+		switch (kernel.trigger) {
+		case 0:
+			scratch._b0 = -1;
+			player.commands_allowed = 0;
+			digi_play_build(307, '_', 1, 2);
+			kernel_timing_trigger(120, 114);
+			break;
+		case 114:
+			aainfo[6]._val3 = 3;
+			break;
+		}
+	}
+
+	if (player_parse(41, 0)) {
+		player_walk(96, 120, 3);
+		global[g017] = 0;
+	}
 }
 
 static void room_307_parser() {
+	if (global[walker_converse_state] != 0) {
+		player.commands_allowed = 0;
+		digi_play_build_ii('c', 1, 1);
+		goto handled;
+	}
+
+	if (player_parse(113, 0)) {
+		global[g154] = 2;
+		player.walker_visible = 0;
+		player.commands_allowed = 0;
+		scratch._a4 = kernel_run_animation_talk('b', 2, 0);
+		kernel_position_anim(scratch._a4, player.x, player.y, player.scale, player.depth);
+		kernel_synch(KERNEL_ANIM, scratch._a4, KERNEL_PLAYER, 0);
+		digi_play_build_ii('b', 1, 1);
+		scratch._aa = 41;
+		scratch._a0 = -1;
+		goto handled;
+	}
+
+	if (player_parse(28, 0)) {
+		global[g154] = 2;
+		player.walker_visible = 0;
+		player.commands_allowed = 0;
+		scratch._a4 = kernel_run_animation_talk('b', 1, 0);
+		kernel_position_anim(scratch._a4, player.x, player.y, player.scale, player.depth);
+		kernel_synch(KERNEL_ANIM, scratch._a4, KERNEL_PLAYER, 0);
+		digi_play_build_ii('b', 1, 1);
+		scratch._aa = 4;
+		scratch._a0 = -1;
+		kernel_flip_hotspot(28, 0);
+		kernel_flip_hotspot(27, -1);
+		global[g075] = -1;
+		goto handled;
+	}
+
+	if (player_parse(27, 0)) {
+		global[g150] = -1;
+		scratch._a2 = 1;
+		goto handled;
+	}
+
+	if (player_parse(41, 0)) {
+		global[g150] = -1;
+		player.commands_allowed = 0;
+		scratch._a2 = 2;
+		goto handled;
+	}
+
+	if (global[walker_converse_state] != 0) {
+		global[walker_converse_state] = 0;
+		close_journal(3);
+		goto handled;
+	}
+
+	goto done;
+handled:
+	player.command_ready = false;
+done:
+	;
 }
 
 void room_307_synchronize(Common::Serializer &s) {
 	for (int16 &v : scratch.sprite)    s.syncAsSint16LE(v);
 	for (int16 &v : scratch.sequence)  s.syncAsSint16LE(v);
 	for (int16 &v : scratch.animation) s.syncAsSint16LE(v);
+	for (auto &ai : scratch.animation_info) ai.synchronize(s);
+	s.syncAsSint16LE(scratch._92);
+	s.syncAsSint16LE(scratch._96);
+	s.syncAsSint16LE(scratch._9a);
+	s.syncAsSint16LE(scratch._9c);
+	s.syncAsSint16LE(scratch._9e);
+	s.syncAsSint16LE(scratch._a0);
+	s.syncAsSint16LE(scratch._a2);
+	s.syncAsSint16LE(scratch._a4);
+	s.syncAsSint16LE(scratch._a6);
+	s.syncAsSint16LE(scratch._a8);
+	s.syncAsSint16LE(scratch._aa);
+	s.syncAsSint16LE(scratch._ac);
+	s.syncAsSint16LE(scratch._ae);
+	s.syncAsSint16LE(scratch._b0);
 }
 
 void room_307_preload() {



From noreply at scummvm.org  Fri Jun 12 08:51:05 2026
From: noreply at scummvm.org (whoozle)
Date: Fri, 12 Jun 2026 08:51:05 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 f2509c1836f4ab900941d2d8c083abda169d7007
Message-ID: <20260612085105.A61627005CC@ron.scummvm.net>

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

Summary:
f2509c1836 PHOENIXVR: Add subtitle support


Commit: f2509c1836f4ab900941d2d8c083abda169d7007
    https://github.com/scummvm/scummvm/commit/f2509c1836f4ab900941d2d8c083abda169d7007
Author: Scorp (scorp at mrs.mn)
Date: 2026-06-12T09:50:33+01:00

Commit Message:
PHOENIXVR: Add subtitle support

Changed paths:
    engines/phoenixvr/phoenixvr.cpp
    engines/phoenixvr/phoenixvr.h


diff --git a/engines/phoenixvr/phoenixvr.cpp b/engines/phoenixvr/phoenixvr.cpp
index 58672d84f74..cc0623bbf3c 100644
--- a/engines/phoenixvr/phoenixvr.cpp
+++ b/engines/phoenixvr/phoenixvr.cpp
@@ -26,6 +26,7 @@
 #include "common/config-manager.h"
 #include "common/events.h"
 #include "common/file.h"
+#include "common/language.h"
 #include "common/memstream.h"
 #include "common/savefile.h"
 #include "common/scummsys.h"
@@ -50,6 +51,7 @@
 #include "phoenixvr/vr.h"
 #include "video/4xm_decoder.h"
 #include "video/smk_decoder.h"
+#include "video/subtitles.h"
 
 namespace PhoenixVR {
 
@@ -589,6 +591,7 @@ void PhoenixVREngine::until(const Common::String &var, int value) {
 		// Delay for a bit. All events loops should have a delay
 		// to prevent the system being unduly loaded
 		_frameLimiter.delayBeforeSwap();
+		drawAudioSubtitles();
 		_screen->update();
 		frameDuration = _frameLimiter.startFrame();
 	}
@@ -620,6 +623,7 @@ void PhoenixVREngine::wait(float seconds) {
 		// Delay for a bit. All events loops should have a delay
 		// to prevent the system being unduly loaded
 		_frameLimiter.delayBeforeSwap();
+		drawAudioSubtitles();
 		_screen->update();
 		frameDuration = _frameLimiter.startFrame();
 	}
@@ -751,7 +755,11 @@ void PhoenixVREngine::playSound(const Common::String &sound, Audio::Mixer::Sound
 	_mixer->playStream(type, &h, Audio::makeWAVStream(stream.release(), DisposeAfterUse::YES), -1, volume);
 	if (loops < 0 || music)
 		_mixer->loopChannel(h);
-	_sounds[sound] = Sound{h, spatial, angle, volume, loops};
+	Common::SharedPtr<Video::Subtitles> subtitles;
+	if (!music)
+		subtitles = loadSubtitles(sound);
+
+	_sounds[sound] = Sound{h, spatial, angle, volume, loops, subtitles};
 }
 
 void PhoenixVREngine::stopSound(const Common::String &sound) {
@@ -761,6 +769,8 @@ void PhoenixVREngine::stopSound(const Common::String &sound) {
 	auto it = _sounds.find(sound);
 	if (it != _sounds.end()) {
 		_mixer->stopHandle(it->_value.handle);
+		if (it->_value.subtitles)
+			it->_value.subtitles->clearSubtitle();
 		_sounds.erase(it);
 	}
 }
@@ -768,9 +778,64 @@ void PhoenixVREngine::stopSound(const Common::String &sound) {
 void PhoenixVREngine::stopAllSounds() {
 	_mixer->stopAll();
 	_currentMusic.clear();
+	for (auto &kv : _sounds) {
+		if (kv._value.subtitles)
+			kv._value.subtitles->clearSubtitle();
+	}
 	_sounds.clear();
 }
 
+Common::Path PhoenixVREngine::getSubtitlePath(const Common::String &path) const {
+	Common::Path assetPath(removeDrive(path), '\\');
+	Common::String filename = assetPath.toString('/') + ".srt";
+	filename.replace('/', '_');
+	filename.replace('\\', '_');
+
+	Common::String language = Common::getLanguageCode(_gameDescription->language);
+	if (language == "us")
+		language = "en";
+
+	return Common::Path("subtitle").appendComponent(language).appendComponent(filename);
+}
+
+Common::SharedPtr<Video::Subtitles> PhoenixVREngine::loadSubtitles(const Common::String &path) const {
+	Common::SharedPtr<Video::Subtitles> subtitles;
+	if (!ConfMan.getBool("subtitles"))
+		return subtitles;
+
+	subtitles = Common::SharedPtr<Video::Subtitles>(new Video::Subtitles());
+	subtitles->loadSRTFile(getSubtitlePath(path));
+	if (!subtitles->isLoaded())
+		return Common::SharedPtr<Video::Subtitles>();
+
+	setupSubtitles(*subtitles);
+	return subtitles;
+}
+
+void PhoenixVREngine::setupSubtitles(Video::Subtitles &subtitles) const {
+	// Subtitle positioning constants (as percentages of screen height)
+	const int HORIZONTAL_MARGIN = 20;
+	const int MIN_BOTTOM_MARGIN = 4;
+	const int MIN_SUBTITLE_HEIGHT = 90;
+	const float BOTTOM_MARGIN_PERCENT = 0.01f;
+	const float SUBTITLE_HEIGHT_PERCENT = 0.2f;
+
+	// Font sizing constants (as percentage of screen height)
+	const int MIN_FONT_SIZE = 18;
+	const float BASE_FONT_SIZE_PERCENT = 1.0f / 36.0f;
+
+	int16 h = g_system->getOverlayHeight();
+	int16 w = g_system->getOverlayWidth();
+	int bottomMargin = MAX<int>(MIN_BOTTOM_MARGIN, int(h * BOTTOM_MARGIN_PERCENT));
+	int topOffset = MAX<int>(MIN_SUBTITLE_HEIGHT, int(h * SUBTITLE_HEIGHT_PERCENT));
+	int fontSize = MAX<int>(MIN_FONT_SIZE, int(h * BASE_FONT_SIZE_PERCENT));
+
+	subtitles.setBBox(Common::Rect(HORIZONTAL_MARGIN, h - topOffset, w - HORIZONTAL_MARGIN, h - bottomMargin));
+	subtitles.setColor(0xff, 0xff, 0x80);
+	subtitles.setFont("LiberationSans-Regular.ttf", fontSize, Video::Subtitles::kFontStyleRegular);
+	subtitles.setFont("LiberationSans-Italic.ttf", fontSize, Video::Subtitles::kFontStyleItalic);
+}
+
 void PhoenixVREngine::playMovie(const Common::String &movie) {
 	debug("playMovie %s", movie.c_str());
 	Common::ScopedPtr<Video::VideoDecoder> dec;
@@ -797,6 +862,12 @@ void PhoenixVREngine::playMovie(const Common::String &movie) {
 	_system->lockMouse(false);
 	dec->start();
 
+	Common::SharedPtr<Video::Subtitles> subtitles = loadSubtitles(movie);
+	if (subtitles) {
+		g_system->showOverlay(false);
+		g_system->clearOverlay();
+	}
+
 	bool playing = true;
 	Common::ScopedPtr<Graphics::Palette> palette;
 	while (!shouldQuit() && playing && !dec->endOfVideo()) {
@@ -828,9 +899,13 @@ void PhoenixVREngine::playMovie(const Common::String &movie) {
 		// Delay for a bit. All events loops should have a delay
 		// to prevent the system being unduly loaded
 		_frameLimiter.delayBeforeSwap();
+		if (subtitles && !dec->isPaused())
+			subtitles->drawSubtitle(dec->getTime(), false);
 		_screen->update();
 		_frameLimiter.startFrame();
 	}
+	if (subtitles)
+		g_system->hideOverlay();
 	_system->lockMouse(_vr.isVR());
 	_mixer->pauseAll(false);
 }
@@ -1134,7 +1209,12 @@ void PhoenixVREngine::tick(float dt) {
 	}
 	for (auto &sound : finishedSounds) {
 		debug("sound %s stopped", sound.c_str());
-		_sounds.erase(sound);
+		auto it = _sounds.find(sound);
+		if (it != _sounds.end()) {
+			if (it->_value.subtitles)
+				it->_value.subtitles->clearSubtitle();
+			_sounds.erase(it);
+		}
 	}
 
 	if (!_nextScript.empty()) {
@@ -1227,6 +1307,17 @@ void PhoenixVREngine::tick(float dt) {
 	}
 }
 
+void PhoenixVREngine::drawAudioSubtitles() {
+	if (!ConfMan.getBool("subtitles"))
+		return;
+
+	for (auto &kv : _sounds) {
+		auto &sound = kv._value;
+		if (sound.subtitles && _mixer->isSoundHandleActive(sound.handle))
+			sound.subtitles->drawSubtitle(_mixer->getElapsedTime(sound.handle).msecs(), false);
+	}
+}
+
 Common::Error PhoenixVREngine::run() {
 	initGraphics(640, 480, nullptr);
 
@@ -1426,6 +1517,7 @@ Common::Error PhoenixVREngine::run() {
 		// Delay for a bit. All events loops should have a delay
 		// to prevent the system being unduly loaded
 		_frameLimiter.delayBeforeSwap();
+		drawAudioSubtitles();
 		_screen->update();
 		frameDuration = _frameLimiter.startFrame();
 	}
diff --git a/engines/phoenixvr/phoenixvr.h b/engines/phoenixvr/phoenixvr.h
index 0860fe659c2..8261d67ac50 100644
--- a/engines/phoenixvr/phoenixvr.h
+++ b/engines/phoenixvr/phoenixvr.h
@@ -27,6 +27,7 @@
 #include "common/fs.h"
 #include "common/hash-str.h"
 #include "common/keyboard.h"
+#include "common/ptr.h"
 #include "common/random.h"
 #include "common/scummsys.h"
 #include "common/serializer.h"
@@ -48,6 +49,10 @@ namespace Graphics {
 class Font;
 }
 
+namespace Video {
+class Subtitles;
+}
+
 namespace PhoenixVR {
 
 class ARN;
@@ -96,6 +101,7 @@ public:
 	bool hasFeature(EngineFeature f) const override {
 		return (f == kSupportsLoadingDuringRuntime) ||
 			   (f == kSupportsSavingDuringRuntime) ||
+			   (f == kSupportsSubtitleOptions) ||
 			   (f == kSupportsReturnToLauncher);
 	};
 
@@ -217,6 +223,10 @@ private:
 	void renderFade(int color);
 	void resetState();
 	const Graphics::Font *getFont(int size, bool bold) const;
+	Common::Path getSubtitlePath(const Common::String &path) const;
+	Common::SharedPtr<Video::Subtitles> loadSubtitles(const Common::String &path) const;
+	void setupSubtitles(Video::Subtitles &subtitles) const;
+	void drawAudioSubtitles();
 
 private:
 	bool _hasFocus = true;
@@ -244,6 +254,7 @@ private:
 		float angle;
 		uint8 volume;
 		int loops;
+		Common::SharedPtr<Video::Subtitles> subtitles;
 	};
 	Common::HashMap<Common::String, Sound, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> _sounds;
 	Common::ScopedPtr<Script> _script;



From noreply at scummvm.org  Fri Jun 12 09:42:36 2026
From: noreply at scummvm.org (ScummVM-Translations)
Date: Fri, 12 Jun 2026 09:42:36 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm-web master ->
 eee0379685d567598b62fce5aef7670b739d9a97
Message-ID: <20260612094236.08C077005CC@ron.scummvm.net>

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

Summary:
eee0379685 L10N: Update translation (Italian) (News)


Commit: eee0379685d567598b62fce5aef7670b739d9a97
    https://github.com/scummvm/scummvm-web/commit/eee0379685d567598b62fce5aef7670b739d9a97
Author: Walter Agazzi (walter.agazzi at protonmail.com)
Date: 2026-06-12T09:42:30Z

Commit Message:
L10N: Update translation (Italian) (News)

Currently translated at 28.9% (253 of 874 strings)

Changed paths:
    data/it/news.json


diff --git a/data/it/news.json b/data/it/news.json
index 6dc5f4c0..032903eb 100644
--- a/data/it/news.json
+++ b/data/it/news.json
@@ -497,5 +497,13 @@
     "20260414": {
         "title": "Nancy Drew 8 e 9 si uniscono all'investigazione!",
         "content": "Il mistero si infittisce! ?? nuovamente il momento di calarsi nei panni di Nancy e seguire gli indizi...\n\nSiamo lieti di annunciare che ora ScummVM supporta due nuovi titoli, parte della longeva serie di avventure *Nancy Drew*.\n\nIn [_Nancy Drew: The Haunted Carousel_](https://wiki.scummvm.org/index.php?title=Nancy_Drew:_The_Haunted_Carousel), Nancy viene chiamata per investigare una sequenza di strani avvenimenti al luna park di Captain Cove, dove il cavallo di una giostra ?? sparito e hanno iniziato a circolare dicerie di una possibile maledizione. Si tratta di sabotaggio, superstizione o qualcosa di completamente diverso?\n\nIn [_Nancy Drew: Danger on Deception Island_](https://wiki.scummvm.org/index.php?title=Nancy_Drew:_Danger_on_Deception_Island), Nancy si reca nel Pacifico nord-occidentale per indagare su una serie di incidenti sospetti accaduti in un centro di ricerca marittimo, correlati a segnalazioni di orche dal comportamento aggressivo. Tuttavia, raramente le cose sono come appaiono...\n\nCome sempre, per giocare questi titoli avrai bisogno dei file originali. I giochi sono disponibili per l'acquisto tramite il [sito della HeR Interactive](https://www.herinteractive.com/shop-games/all-games/) o [Steam](https://store.steampowered.com/search/?term=nancy+drew). Avrai inoltre bisogno di una [daily build di sviluppo](https://www.scummvm.org/downloads/#daily) di ScummVM.\n\nIn caso di malfunzionamenti, cortesemente apri una segnalazione sul nostro [bug tracker](https://bugs.scummvm.org/).\n\nSii curioso, e buona investigazione!"
+    },
+    "20260502": {
+        "title": "Google Summer of Code 2026 ??? Diamo il benvenuto ai nostri nuovi studenti",
+        "content": "![GSoC Logo](https://www.scummvm.org/data/news/GSoC2016Logo.png)\n\nAnche quest'anno abbiamo avuto il privilegio di ricevere molte eccellenti candidature, e Google ci ha dato l'opportunit?? di ricoprire il ruolo di mentori per quattro di loro durante la [GSoC](https://summerofcode.withgoogle.com/) di quest'estate.\n\n* Ion Andrei Cristian and Mohit Bankar lavoreranno entrambi al completamento di alcuni motori che sono stati lasciati in sospeso, permettendoci di supportare altri giochi e garantendone la preservazione.\n\n* Priyanshu Kumar porter?? il motore iBASS su ScummVM. Questo motore ?? stato creato dalla Revolution Software per rilasciare una versione iOS di Beneath a Steel Sky con controlli migliorati e funzionalit?? aggiuntive. Questi miglioramenti verranno incorporati in ScummVM a beneficio di tutte le piattaforme.\n\n* Ramyak Sharma passer?? la sua estate concentrandosi sul motore DIRECTOR, aggiungendo funzionalit?? e migliorando la compatibilit??, contribuendo alla preservazione dei software creati con Macromedia Director. Potenzier?? inoltre il Visual Debugger.\n\nDiamo loro un caloroso benvenuto! Se desideri discutere dei loro progetti, puoi raggiungerci sul nostro [server Discord](https://discord.gg/4cDsMNtcpG).\n\nIl periodo di ambientamento inizia oggi, e la vera e propria fase di programmazione inizier?? il 25 maggio. Potrai leggere aggiornamenti regolari sui rispettivi blog presenti su [Planet](https://planet.scummvm.org/).\n\nCon i quattro studenti GSoC a bordo, il team ?? gi?? in modalit?? Summer of Coding ... unisciti a noi per il conto alla rovescia!\n\nVorremmo inoltre cogliere l'occasione per ringraziare tutti coloro che hanno inviato la propria candidatura, e ci auguriamo che continuerete a supportare il nostro progetto per renderlo sempre pi?? grande e migliore."
+    },
+    "20260503": {
+        "title": "5 milioni di linee di codice. 5 milioni di storie.",
+        "content": "Un paio di settimane fa abbiamo silenziosamente raggiunto un importante traguardo per il progetto ScummVM, un traguardo di cui siamo molto orgogliosi.\n\nSecondo l'analisi del repository [effettuata da Open Hub](https://openhub.net/p/scummvm/), la codebase di ScummVM contiene ora pi?? di **5 milioni di linee di codice**, scritte da pi?? di **900 collaboratori** nell'arco di circa 25 anni.\n\nCari collaboratori, a nome dell'intera community di ScummVM: Migliaia di persone utilizzano il vostro codice ogni giorno, rivivendo ricordi d'infanzia o esplorando storie ancora tutte da scoprire.\n\nLa vostra passione e dedizione hanno reso possibile tutto ci??. Non state solo scrivendo la storia, state contribuendo a preservarla.\n\nGrazie infinite!"
     }
 }



From noreply at scummvm.org  Fri Jun 12 09:54:05 2026
From: noreply at scummvm.org (dwatteau)
Date: Fri, 12 Jun 2026 09:54:05 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 bd55262fcac7dd5ebc67251e3465a8cc75124672
Message-ID: <20260612095405.AF6707005CC@ron.scummvm.net>

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

Summary:
bd55262fca SCUMM: RA: Sync detection entries with latest scumm-md5.txt changes


Commit: bd55262fcac7dd5ebc67251e3465a8cc75124672
    https://github.com/scummvm/scummvm/commit/bd55262fcac7dd5ebc67251e3465a8cc75124672
Author: Donovan Watteau (contrib at dwatteau.fr)
Date: 2026-06-12T11:48:53+02:00

Commit Message:
SCUMM: RA: Sync detection entries with latest scumm-md5.txt changes

Latest scumm-md5.txt changes (commit 904144411f57b697948e7083177cee23507cbe50)
weren't applied to scumm-md5.h.

Changed paths:
    engines/scumm/scumm-md5.h


diff --git a/engines/scumm/scumm-md5.h b/engines/scumm/scumm-md5.h
index 38d31631068..99156cabaae 100644
--- a/engines/scumm/scumm-md5.h
+++ b/engines/scumm/scumm-md5.h
@@ -1,5 +1,5 @@
 /*
-  This file was generated by the md5table tool on Thu Apr 30 19:26:15 2026
+  This file was generated by the md5table tool on Fri Jun 12 09:47:44 2026
   DO NOT EDIT MANUALLY!
  */
 
@@ -374,6 +374,7 @@ static const MD5Table md5table[] = {
 	{ "6b3ec67da214f558dc5ceaa2acd47453", "indy3", "EGA", "EGA", 5361, Common::EN_ANY, Common::kPlatformDOS },
 	{ "6b5a3fef241e90d4b2e77f1e222773ee", "maniac", "NES", "", 2082, Common::SV_SWE, Common::kPlatformNES },
 	{ "6b73a08c535c0544785d73ff812908a0", "rebel2", "", "", 9430, Common::EN_ANY, Common::kPlatformDOS },
+	{ "6bb0fe2d7816c5528c773f2898e4e0ff", "rebel1", "", "v1.7", 212915, Common::EN_ANY, Common::kPlatformDOS },
 	{ "6bca7a1a96d16e52b8f3c42b50dbdca3", "fbear", "HE 62", "", -1, Common::JA_JPN, Common::kPlatform3DO },
 	{ "6bf70eee5de3d24d2403e0dd3d267e8a", "spyfox", "", "", 49221, Common::EN_USA, Common::kPlatformWindows },
 	{ "6c2bff0e327f2962e809c2e1a82d7309", "monkey", "VGA", "", 8347, Common::EN_ANY, Common::kPlatformAmiga },
@@ -602,7 +603,7 @@ static const MD5Table md5table[] = {
 	{ "b886b0a5d909c7158a914e1d7c1c6c65", "loom", "EGA", "EGA", -1, Common::FR_FRA, Common::kPlatformDOS },
 	{ "b8955d7d23b4972229060d1592489fef", "freddicove", "HE 100", "", 41182, Common::NL_NLD, Common::kPlatformUnknown },
 	{ "b95347da0052a623f7a4bb15f425d897", "pajama3", "", "Demo", 13911, Common::IT_ITA, Common::kPlatformWindows },
-	{ "b9ac90e4411da1b6f7dd075cc4b03c2a", "rebel1", "", "", 214435, Common::EN_ANY, Common::kPlatformDOS },
+	{ "b9ac90e4411da1b6f7dd075cc4b03c2a", "rebel1", "", "v1.0", 214435, Common::EN_ANY, Common::kPlatformDOS },
 	{ "b9ba19ce376efc69be78ef3baef8d2b9", "monkey", "Mac", "", 8955, Common::EN_ANY, Common::kPlatformMacintosh },
 	{ "b9bb68c5d2c9b6e2d9c513a29a754a57", "puttmoon", "", "", 7828, Common::EN_ANY, Common::kPlatformDOS },
 	{ "b9fce749c3e9da0d41c78213c179631b", "samnmax", "CD", "Demo", 7505, Common::EN_ANY, Common::kPlatformDOS },
@@ -723,6 +724,7 @@ static const MD5Table md5table[] = {
 	{ "e144f5f49d9241d2a9dee2576b3d09cb", "airport", "", "Demo", 51152, Common::EN_ANY, Common::kPlatformWindows },
 	{ "e17db1ddf91b39ca6bbc8ad3ed19e883", "monkey", "FM-TOWNS", "", -1, Common::JA_JPN, Common::kPlatformFMTowns },
 	{ "e1c9998826ce7fa8bde5cc3a5023edec", "moonbase", "1.1", "1.1", -1, Common::EN_ANY, Common::kPlatformWindows },
+	{ "e210a57b756473be705d24b9378a8233", "rebel1", "", "v1.3", 212383, Common::EN_ANY, Common::kPlatformDOS },
 	{ "e222c2020a6e6a93bc13d01224ab73df", "monkey", "VGA", "VGA", 8357, Common::RU_RUS, Common::kPlatformDOS },
 	{ "e246e02db9630533a40d99c9f54a8e01", "monkey2", "Mac", "", 10740, Common::EN_ANY, Common::kPlatformMacintosh },
 	{ "e361a7058ed8e8ebb462663c0a3ae8d6", "puttputt", "HE 62", "", -1, Common::HE_ISR, Common::kPlatformDOS },



From noreply at scummvm.org  Fri Jun 12 09:54:58 2026
From: noreply at scummvm.org (sev-)
Date: Fri, 12 Jun 2026 09:54:58 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm-web master ->
 6fdef48d735e924538b3959a4e8dcf7258cdbaf7
Message-ID: <20260612095458.6D93B7005CC@ron.scummvm.net>

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

Summary:
6fdef48d73 NEWS: Announce Noctropolis support


Commit: 6fdef48d735e924538b3959a4e8dcf7258cdbaf7
    https://github.com/scummvm/scummvm-web/commit/6fdef48d735e924538b3959a4e8dcf7258cdbaf7
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2026-06-12T11:54:38+02:00

Commit Message:
NEWS: Announce Noctropolis support

Changed paths:
  A data/en/news/20260612.markdown


diff --git a/data/en/news/20260612.markdown b/data/en/news/20260612.markdown
new file mode 100644
index 00000000..a72aaf84
--- /dev/null
+++ b/data/en/news/20260612.markdown
@@ -0,0 +1,17 @@
+---
+title: "A darker shade of Grey: Noctropolis is Ready for Testing!"
+author: mduggan
+date: 1781257988
+---
+
+Tired of running a mundane bookstore and ready to dive into the gritty, neon-drenched pages of a comic book? Get your trench coat ready, because Flashpoint Productions??? 1994 dark adventure, _Noctropolis_ is now ready for public testing!
+
+In this interactive comic brought to life you play as Peter Grey, a lonely bookstore owner pulled into a dystopian superhero world where the sun never shines. With the legendary hero Darksheer retired, it is up to you to take on his role, solve the puzzles, and save the city from a cast of villains doing the bidding of a mysterious force.
+
+To help us test, you will need your copy of the [game data files](https://wiki.scummvm.org/index.php?title=Noctropolis#Required_Data) and a recent [daily development build](https://www.scummvm.org/downloads/#daily) of ScummVM.
+
+Support has been added for both the original DOS release (including demos) and the Nightdive Studios digital re-release, which is available from [GOG](https://www.gog.com/game/noctropolis) or [Steam](https://store.steampowered.com/app/377070).
+
+As you patrol the dangerous streets, please report any issues you find on our [bug tracker](https://bugs.scummvm.org/).
+
+The city needs a hero ??? will you answer the call? Happy crime-fighting!



From noreply at scummvm.org  Fri Jun 12 10:12:47 2026
From: noreply at scummvm.org (ScummVM git)
Date: Fri, 12 Jun 2026 10:12:47 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm-web
 dependabot/composer/patch-releases-cc17fff384 ->
 758e46ce2796fc0570d5b5581628cebc425cdedd
Message-ID: <20260612101247.93A607005CC@ron.scummvm.net>

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

Summary:
758e46ce27 Bump the patch-releases group with 2 updates


Commit: 758e46ce2796fc0570d5b5581628cebc425cdedd
    https://github.com/scummvm/scummvm-web/commit/758e46ce2796fc0570d5b5581628cebc425cdedd
Author: dependabot[bot] (49699333+dependabot[bot]@users.noreply.github.com)
Date: 2026-06-12T10:12:45Z

Commit Message:
Bump the patch-releases group with 2 updates

Bumps the patch-releases group with 2 updates: [predis/predis](https://github.com/predis/predis) and [guzzlehttp/guzzle](https://github.com/guzzle/guzzle).


Updates `predis/predis` from 3.5.0 to 3.5.1
- [Release notes](https://github.com/predis/predis/releases)
- [Changelog](https://github.com/predis/predis/blob/main/CHANGELOG.md)
- [Commits](https://github.com/predis/predis/compare/v3.5.0...v3.5.1)

Updates `guzzlehttp/guzzle` from 7.11.0 to 7.11.1
- [Release notes](https://github.com/guzzle/guzzle/releases)
- [Changelog](https://github.com/guzzle/guzzle/blob/7.11/CHANGELOG.md)
- [Commits](https://github.com/guzzle/guzzle/compare/7.11.0...7.11.1)

---
updated-dependencies:
- dependency-name: predis/predis
  dependency-version: 3.5.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: patch-releases
- dependency-name: guzzlehttp/guzzle
  dependency-version: 7.11.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: patch-releases
...

Signed-off-by: dependabot[bot] <support at github.com>

Changed paths:
    composer.lock


diff --git a/composer.lock b/composer.lock
index 3116c27c..5aaa8496 100644
--- a/composer.lock
+++ b/composer.lock
@@ -183,16 +183,16 @@
         },
         {
             "name": "guzzlehttp/guzzle",
-            "version": "7.11.0",
+            "version": "7.11.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/guzzle/guzzle.git",
-                "reference": "c987f8ce84b8434fa430795eca0f3430663da72b"
+                "reference": "5af96f374e0ab4ebd747b8310888c99d3adb0a8c"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/guzzle/guzzle/zipball/c987f8ce84b8434fa430795eca0f3430663da72b",
-                "reference": "c987f8ce84b8434fa430795eca0f3430663da72b",
+                "url": "https://api.github.com/repos/guzzle/guzzle/zipball/5af96f374e0ab4ebd747b8310888c99d3adb0a8c",
+                "reference": "5af96f374e0ab4ebd747b8310888c99d3adb0a8c",
                 "shasum": ""
             },
             "require": {
@@ -211,7 +211,7 @@
                 "bamarni/composer-bin-plugin": "^1.8.2",
                 "ext-curl": "*",
                 "guzzle/client-integration-tests": "3.0.2",
-                "guzzlehttp/test-server": "^0.4",
+                "guzzlehttp/test-server": "^0.5",
                 "php-http/message-factory": "^1.1",
                 "phpunit/phpunit": "^8.5.52 || ^9.6.34",
                 "psr/log": "^1.1 || ^2.0 || ^3.0"
@@ -291,7 +291,7 @@
             ],
             "support": {
                 "issues": "https://github.com/guzzle/guzzle/issues",
-                "source": "https://github.com/guzzle/guzzle/tree/7.11.0"
+                "source": "https://github.com/guzzle/guzzle/tree/7.11.1"
             },
             "funding": [
                 {
@@ -307,7 +307,7 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2026-06-02T12:40:51+00:00"
+            "time": "2026-06-07T22:54:06+00:00"
         },
         {
             "name": "guzzlehttp/promises",
@@ -860,16 +860,16 @@
         },
         {
             "name": "predis/predis",
-            "version": "v3.5.0",
+            "version": "v3.5.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/predis/predis.git",
-                "reference": "8cc4319c06924c8ff0c5c7eec4243a19e3be32f1"
+                "reference": "5c996db191ee2d9bafe651f454b1fca16754271b"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/predis/predis/zipball/8cc4319c06924c8ff0c5c7eec4243a19e3be32f1",
-                "reference": "8cc4319c06924c8ff0c5c7eec4243a19e3be32f1",
+                "url": "https://api.github.com/repos/predis/predis/zipball/5c996db191ee2d9bafe651f454b1fca16754271b",
+                "reference": "5c996db191ee2d9bafe651f454b1fca16754271b",
                 "shasum": ""
             },
             "require": {
@@ -911,7 +911,7 @@
             ],
             "support": {
                 "issues": "https://github.com/predis/predis/issues",
-                "source": "https://github.com/predis/predis/tree/v3.5.0"
+                "source": "https://github.com/predis/predis/tree/v3.5.1"
             },
             "funding": [
                 {
@@ -919,7 +919,7 @@
                     "type": "github"
                 }
             ],
-            "time": "2026-06-02T19:25:56+00:00"
+            "time": "2026-06-11T16:56:53+00:00"
         },
         {
             "name": "propel/propel",
@@ -3131,5 +3131,5 @@
     "platform-overrides": {
         "php": "8.2.0"
     },
-    "plugin-api-version": "2.6.0"
+    "plugin-api-version": "2.9.0"
 }



From noreply at scummvm.org  Fri Jun 12 10:12:56 2026
From: noreply at scummvm.org (dwatteau)
Date: Fri, 12 Jun 2026 10:12:56 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 3a719ea6bcc98bc8123bc8012ba9de814c9f8f34
Message-ID: <20260612101256.B190B7005CD@ron.scummvm.net>

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

Summary:
3a719ea6bc PELROCK: Escape non-ASCII characters in the game name string


Commit: 3a719ea6bcc98bc8123bc8012ba9de814c9f8f34
    https://github.com/scummvm/scummvm/commit/3a719ea6bcc98bc8123bc8012ba9de814c9f8f34
Author: Donovan Watteau (contrib at dwatteau.fr)
Date: 2026-06-12T12:04:04+02:00

Commit Message:
PELROCK: Escape non-ASCII characters in the game name string

Similar to previous commit 254a63bcf981709ccf13250d5635b98591d858f9, as
"there is no guarantee that the compiler will assume UTF-8 encoding for
the source code files."

Changed paths:
    engines/pelrock/detection_tables.h


diff --git a/engines/pelrock/detection_tables.h b/engines/pelrock/detection_tables.h
index 103e74e38c1..f9a59220b4f 100644
--- a/engines/pelrock/detection_tables.h
+++ b/engines/pelrock/detection_tables.h
@@ -22,7 +22,7 @@
 namespace Pelrock {
 
 const PlainGameDescriptor pelrockGames[] = {
-	{ "pelrock", "Alfred Pelrock: En Busca de un Sue??o" },
+	{ "pelrock", "Alfred Pelrock: En Busca de un Sue\303\261o" },
 	{ 0, 0 }
 };
 



From noreply at scummvm.org  Fri Jun 12 10:13:38 2026
From: noreply at scummvm.org (ScummVM git)
Date: Fri, 12 Jun 2026 10:13:38 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm-web
 dependabot/npm_and_yarn/dumper-companion/eslint-b8a4b0c404 ->
 7dd9219869d46fcfc35e11b78e0f102aff524193
Message-ID: <20260612101338.E6D7D7005CC@ron.scummvm.net>

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

Summary:
7dd9219869 Bump the eslint group across 1 directory with 3 updates


Commit: 7dd9219869d46fcfc35e11b78e0f102aff524193
    https://github.com/scummvm/scummvm-web/commit/7dd9219869d46fcfc35e11b78e0f102aff524193
Author: dependabot[bot] (49699333+dependabot[bot]@users.noreply.github.com)
Date: 2026-06-12T10:13:36Z

Commit Message:
Bump the eslint group across 1 directory with 3 updates

Bumps the eslint group with 3 updates in the /dumper-companion directory: [@eslint/js](https://github.com/eslint/eslint/tree/HEAD/packages/js), [eslint](https://github.com/eslint/eslint) and [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint).


Updates `@eslint/js` from 9.39.4 to 10.0.1
- [Release notes](https://github.com/eslint/eslint/releases)
- [Commits](https://github.com/eslint/eslint/commits/v10.0.1/packages/js)

Updates `eslint` from 9.39.4 to 10.4.1
- [Release notes](https://github.com/eslint/eslint/releases)
- [Commits](https://github.com/eslint/eslint/compare/v9.39.4...v10.4.1)

Updates `typescript-eslint` from 8.60.1 to 8.61.0
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/typescript-eslint/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.61.0/packages/typescript-eslint)

---
updated-dependencies:
- dependency-name: "@eslint/js"
  dependency-version: 10.0.1
  dependency-type: direct:development
  update-type: version-update:semver-major
  dependency-group: eslint
- dependency-name: eslint
  dependency-version: 10.4.1
  dependency-type: direct:development
  update-type: version-update:semver-major
  dependency-group: eslint
- dependency-name: typescript-eslint
  dependency-version: 8.61.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: eslint
...

Signed-off-by: dependabot[bot] <support at github.com>

Changed paths:
    dumper-companion/package-lock.json
    dumper-companion/package.json


diff --git a/dumper-companion/package-lock.json b/dumper-companion/package-lock.json
index e7324deb..5b9e9cb7 100644
--- a/dumper-companion/package-lock.json
+++ b/dumper-companion/package-lock.json
@@ -17,12 +17,12 @@
         "webpack-cli": "^7.0.2"
       },
       "devDependencies": {
-        "@eslint/js": "^9.39.4",
+        "@eslint/js": "^10.0.1",
         "@stylistic/eslint-plugin": "^5.10.0",
-        "eslint": "^9.39.4",
+        "eslint": "^10.4.1",
         "eslint-config-preact": "^2.0.0",
         "globals": "^17.5.0",
-        "typescript-eslint": "^8.58.2"
+        "typescript-eslint": "^8.61.0"
       }
     },
     "node_modules/@babel/code-frame": {
@@ -81,35 +81,6 @@
         "url": "https://opencollective.com/babel"
       }
     },
-    "node_modules/@babel/eslint-parser": {
-      "version": "7.29.7",
-      "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.29.7.tgz",
-      "integrity": "sha512-zxt+UJTOMKvUt3yOg+D58MLuz334pHp93qifMFcjIIO+9hN6t+ufw2gi7vDPMpxvfnHRR+3VVXvIjineCcgyXw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@nicolo-ribaudo/eslint-scope-5-internals": "5.1.1-v1",
-        "eslint-visitor-keys": "^2.1.0",
-        "semver": "^6.3.1"
-      },
-      "engines": {
-        "node": "^10.13.0 || ^12.13.0 || >=14.0.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.11.0",
-        "eslint": "^7.5.0 || ^8.0.0 || ^9.0.0"
-      }
-    },
-    "node_modules/@babel/eslint-parser/node_modules/eslint-visitor-keys": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz",
-      "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==",
-      "dev": true,
-      "license": "Apache-2.0",
-      "engines": {
-        "node": ">=10"
-      }
-    },
     "node_modules/@babel/generator": {
       "version": "7.29.7",
       "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.7.tgz",
@@ -385,118 +356,89 @@
       }
     },
     "node_modules/@eslint/config-array": {
-      "version": "0.21.2",
-      "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.2.tgz",
-      "integrity": "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==",
+      "version": "0.23.5",
+      "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.23.5.tgz",
+      "integrity": "sha512-Y3kKLvC1dvTOT+oGlqNQ1XLqK6D1HU2YXPc52NmAlJZbMMWDzGYXMiPRJ8TYD39muD/OTjlZmNJ4ib7dvSrMBA==",
       "dev": true,
       "license": "Apache-2.0",
       "dependencies": {
-        "@eslint/object-schema": "^2.1.7",
+        "@eslint/object-schema": "^3.0.5",
         "debug": "^4.3.1",
-        "minimatch": "^3.1.5"
+        "minimatch": "^10.2.4"
       },
       "engines": {
-        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+        "node": "^20.19.0 || ^22.13.0 || >=24"
       }
     },
     "node_modules/@eslint/config-helpers": {
-      "version": "0.4.2",
-      "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz",
-      "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==",
+      "version": "0.6.0",
+      "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.6.0.tgz",
+      "integrity": "sha512-ii6Bw9jJ2zi2cWA2Z+9/QZ/+3DX6kwaV5Q986D/CdP3Lap3w/pgQZ373FV7byY/i7L4IRH/G43I5dz1ClsCbpA==",
       "dev": true,
       "license": "Apache-2.0",
       "dependencies": {
-        "@eslint/core": "^0.17.0"
+        "@eslint/core": "^1.2.1"
       },
       "engines": {
-        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+        "node": "^20.19.0 || ^22.13.0 || >=24"
       }
     },
     "node_modules/@eslint/core": {
-      "version": "0.17.0",
-      "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz",
-      "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==",
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/@eslint/core/-/core-1.2.1.tgz",
+      "integrity": "sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ==",
       "dev": true,
       "license": "Apache-2.0",
       "dependencies": {
         "@types/json-schema": "^7.0.15"
       },
       "engines": {
-        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
-      }
-    },
-    "node_modules/@eslint/eslintrc": {
-      "version": "3.3.5",
-      "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.5.tgz",
-      "integrity": "sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "ajv": "^6.14.0",
-        "debug": "^4.3.2",
-        "espree": "^10.0.1",
-        "globals": "^14.0.0",
-        "ignore": "^5.2.0",
-        "import-fresh": "^3.2.1",
-        "js-yaml": "^4.1.1",
-        "minimatch": "^3.1.5",
-        "strip-json-comments": "^3.1.1"
-      },
-      "engines": {
-        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
-      },
-      "funding": {
-        "url": "https://opencollective.com/eslint"
-      }
-    },
-    "node_modules/@eslint/eslintrc/node_modules/globals": {
-      "version": "14.0.0",
-      "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz",
-      "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=18"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
+        "node": "^20.19.0 || ^22.13.0 || >=24"
       }
     },
     "node_modules/@eslint/js": {
-      "version": "9.39.4",
-      "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.4.tgz",
-      "integrity": "sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==",
+      "version": "10.0.1",
+      "resolved": "https://registry.npmjs.org/@eslint/js/-/js-10.0.1.tgz",
+      "integrity": "sha512-zeR9k5pd4gxjZ0abRoIaxdc7I3nDktoXZk2qOv9gCNWx3mVwEn32VRhyLaRsDiJjTs0xq/T8mfPtyuXu7GWBcA==",
       "dev": true,
       "license": "MIT",
       "engines": {
-        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+        "node": "^20.19.0 || ^22.13.0 || >=24"
       },
       "funding": {
         "url": "https://eslint.org/donate"
+      },
+      "peerDependencies": {
+        "eslint": "^10.0.0"
+      },
+      "peerDependenciesMeta": {
+        "eslint": {
+          "optional": true
+        }
       }
     },
     "node_modules/@eslint/object-schema": {
-      "version": "2.1.7",
-      "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz",
-      "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==",
+      "version": "3.0.5",
+      "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-3.0.5.tgz",
+      "integrity": "sha512-vqTaUEgxzm+YDSdElad6PiRoX4t8VGDjCtt05zn4nU810UIx/uNEV7/lZJ6KwFThKZOzOxzXy48da+No7HZaMw==",
       "dev": true,
       "license": "Apache-2.0",
       "engines": {
-        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+        "node": "^20.19.0 || ^22.13.0 || >=24"
       }
     },
     "node_modules/@eslint/plugin-kit": {
-      "version": "0.4.1",
-      "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz",
-      "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==",
+      "version": "0.7.2",
+      "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.7.2.tgz",
+      "integrity": "sha512-+CNAzxglkrpNf/kKywqQfk74QjtceuOE7Qm+AF8miRvPF/wmmK5+OJOgVh3AVTT3RP2mH3+FOaxlE5v72owk0A==",
       "dev": true,
       "license": "Apache-2.0",
       "dependencies": {
-        "@eslint/core": "^0.17.0",
+        "@eslint/core": "^1.2.1",
         "levn": "^0.4.1"
       },
       "engines": {
-        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+        "node": "^20.19.0 || ^22.13.0 || >=24"
       }
     },
     "node_modules/@humanfs/core": {
@@ -683,6 +625,13 @@
         "eslint": "^9.0.0 || ^10.0.0"
       }
     },
+    "node_modules/@types/esrecurse": {
+      "version": "4.3.1",
+      "resolved": "https://registry.npmjs.org/@types/esrecurse/-/esrecurse-4.3.1.tgz",
+      "integrity": "sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==",
+      "dev": true,
+      "license": "MIT"
+    },
     "node_modules/@types/estree": {
       "version": "1.0.9",
       "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz",
@@ -711,17 +660,17 @@
       "license": "MIT"
     },
     "node_modules/@typescript-eslint/eslint-plugin": {
-      "version": "8.60.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.60.1.tgz",
-      "integrity": "sha512-JQ4S5GB0tfjO8BuJ4fcX+HodkzJjYBV+7OJ+wLygaX7OGQ7FudyHL4NSCA6ob+w3Yn+5MkKIozOwQhXeM7opVg==",
+      "version": "8.61.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.61.0.tgz",
+      "integrity": "sha512-bFNvl9ZczlVb+wR2Akszf3gHfKVj/8WanXaGJ3UstTA7brNKg0cNdk6X1Psu5V7MZ2oQtzZKOEzIUehaoxbDGw==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
         "@eslint-community/regexpp": "^4.12.2",
-        "@typescript-eslint/scope-manager": "8.60.1",
-        "@typescript-eslint/type-utils": "8.60.1",
-        "@typescript-eslint/utils": "8.60.1",
-        "@typescript-eslint/visitor-keys": "8.60.1",
+        "@typescript-eslint/scope-manager": "8.61.0",
+        "@typescript-eslint/type-utils": "8.61.0",
+        "@typescript-eslint/utils": "8.61.0",
+        "@typescript-eslint/visitor-keys": "8.61.0",
         "ignore": "^7.0.5",
         "natural-compare": "^1.4.0",
         "ts-api-utils": "^2.5.0"
@@ -734,7 +683,7 @@
         "url": "https://opencollective.com/typescript-eslint"
       },
       "peerDependencies": {
-        "@typescript-eslint/parser": "^8.60.1",
+        "@typescript-eslint/parser": "^8.61.0",
         "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
         "typescript": ">=4.8.4 <6.1.0"
       }
@@ -750,16 +699,16 @@
       }
     },
     "node_modules/@typescript-eslint/parser": {
-      "version": "8.60.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.60.1.tgz",
-      "integrity": "sha512-A0M6ua6H252bVjPvvtSgl2QA4+ET9S5Mtkb2GDyTxIhH/C4qDItT7RQNO5PhMC6NXGYXOR9dIalcDDgBKT7oFA==",
+      "version": "8.61.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.61.0.tgz",
+      "integrity": "sha512-5B7PfA2e1NQGCnDHd/0lW7W3gvp3d59Ryw54FYO8Uswxo9f6ikw3AZV+Xj/TvpImmpsiYyUqAfhC6kJID1jF6w==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@typescript-eslint/scope-manager": "8.60.1",
-        "@typescript-eslint/types": "8.60.1",
-        "@typescript-eslint/typescript-estree": "8.60.1",
-        "@typescript-eslint/visitor-keys": "8.60.1",
+        "@typescript-eslint/scope-manager": "8.61.0",
+        "@typescript-eslint/types": "8.61.0",
+        "@typescript-eslint/typescript-estree": "8.61.0",
+        "@typescript-eslint/visitor-keys": "8.61.0",
         "debug": "^4.4.3"
       },
       "engines": {
@@ -775,14 +724,14 @@
       }
     },
     "node_modules/@typescript-eslint/project-service": {
-      "version": "8.60.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.60.1.tgz",
-      "integrity": "sha512-eXkTH2bxmXlqD1RnOPmLZ9ZM9D3VwSx04JOwBnP9RQ+yUA5a2Mu7SfW8uaV2Aon53NJzZlZYuX7tn91Izf+xaw==",
+      "version": "8.61.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.61.0.tgz",
+      "integrity": "sha512-DV42F7MLJO6Rax7SK1yg43tcnEfGUrurSpSxKuVX+a3RCTzBlH3fuxprrOJXKCJGAaw82xXocikJ0uQaqwXgGA==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@typescript-eslint/tsconfig-utils": "^8.60.1",
-        "@typescript-eslint/types": "^8.60.1",
+        "@typescript-eslint/tsconfig-utils": "^8.61.0",
+        "@typescript-eslint/types": "^8.61.0",
         "debug": "^4.4.3"
       },
       "engines": {
@@ -797,14 +746,14 @@
       }
     },
     "node_modules/@typescript-eslint/scope-manager": {
-      "version": "8.60.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.60.1.tgz",
-      "integrity": "sha512-gvI5OQoptnxQnchOirukCuQ55svJSTuD/4k5+pC267xyBtYry748R9/c3tYUzb/iE6RZfllRz2lVulLCHkTm4w==",
+      "version": "8.61.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.61.0.tgz",
+      "integrity": "sha512-IWdXFHFSb6mlC3HPc7QsLDm5zYEbUla6trDEHf32D3/dnuUyXd87plScSNXSbm0/RxMvObpI17sv/EDTGrGZkA==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@typescript-eslint/types": "8.60.1",
-        "@typescript-eslint/visitor-keys": "8.60.1"
+        "@typescript-eslint/types": "8.61.0",
+        "@typescript-eslint/visitor-keys": "8.61.0"
       },
       "engines": {
         "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -815,9 +764,9 @@
       }
     },
     "node_modules/@typescript-eslint/tsconfig-utils": {
-      "version": "8.60.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.60.1.tgz",
-      "integrity": "sha512-nh8w4qAteiKuZu3pSSzG/yGKpw0OlkrKnzFmbVRenKaD4qc+7i1GrmZaLVkr8rk4uipiPGMOW4YsM6WmKZ5CvA==",
+      "version": "8.61.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.61.0.tgz",
+      "integrity": "sha512-O5Amvdv9ztMpxpf+vmFULGG78IE6Qwdr3bCGvqwG4nwc9H2qXkOYJJnRbRHyMkQTjv1d03olqwwwzHLMqpFePQ==",
       "dev": true,
       "license": "MIT",
       "engines": {
@@ -832,15 +781,15 @@
       }
     },
     "node_modules/@typescript-eslint/type-utils": {
-      "version": "8.60.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.60.1.tgz",
-      "integrity": "sha512-sdwTrpjosW7ANQYJ39ZBF1ZyEMEGVB2UsikrserVM/30a/F1dTLnu9bGxEdosugyu5caigjLrR2qiD11asjI1A==",
+      "version": "8.61.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.61.0.tgz",
+      "integrity": "sha512-TuBiQYIkd97yBfInHCTKVYMbX4kvEmpOEuixIuzCU9p8BGT1SfyyO0d0IfDMbPIHcjn/hWnusUX5e8v5Xg+X8A==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@typescript-eslint/types": "8.60.1",
-        "@typescript-eslint/typescript-estree": "8.60.1",
-        "@typescript-eslint/utils": "8.60.1",
+        "@typescript-eslint/types": "8.61.0",
+        "@typescript-eslint/typescript-estree": "8.61.0",
+        "@typescript-eslint/utils": "8.61.0",
         "debug": "^4.4.3",
         "ts-api-utils": "^2.5.0"
       },
@@ -857,9 +806,9 @@
       }
     },
     "node_modules/@typescript-eslint/types": {
-      "version": "8.60.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.60.1.tgz",
-      "integrity": "sha512-4h0tY8ppCkdCzcrl2YM5M3my0xsE1Tf8om3owEu5oPWmXwkKRmk0j0LGDzYBGUcAlesEbxBhazqu/K4cu3Ug7w==",
+      "version": "8.61.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.61.0.tgz",
+      "integrity": "sha512-9QTQpZ5Iin4CdIodfbDQFSeiSJKidgYJYug1P9CC2xWgUTvlmixViqDZNciMjwLBZyJnG4tGmPl97rVAFb1AJg==",
       "dev": true,
       "license": "MIT",
       "engines": {
@@ -871,16 +820,16 @@
       }
     },
     "node_modules/@typescript-eslint/typescript-estree": {
-      "version": "8.60.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.60.1.tgz",
-      "integrity": "sha512-alpRkfG8hlVE5kdJW2GkfgDgXxold3e8e4l6EnmhRmRLbekgAPCCGDVD++sABy9FcgPFroq+uFcCSM1vR57Cew==",
+      "version": "8.61.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.61.0.tgz",
+      "integrity": "sha512-42zatd5qSvvcV1JdDBCLxYRznvP4eIHpPoZXdkPFnAmanA4FuZ5dibSnCBggY8hQnqajPpoGjXFdZ7fIJKQnlA==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@typescript-eslint/project-service": "8.60.1",
-        "@typescript-eslint/tsconfig-utils": "8.60.1",
-        "@typescript-eslint/types": "8.60.1",
-        "@typescript-eslint/visitor-keys": "8.60.1",
+        "@typescript-eslint/project-service": "8.61.0",
+        "@typescript-eslint/tsconfig-utils": "8.61.0",
+        "@typescript-eslint/types": "8.61.0",
+        "@typescript-eslint/visitor-keys": "8.61.0",
         "debug": "^4.4.3",
         "minimatch": "^10.2.2",
         "semver": "^7.7.3",
@@ -898,49 +847,10 @@
         "typescript": ">=4.8.4 <6.1.0"
       }
     },
-    "node_modules/@typescript-eslint/typescript-estree/node_modules/balanced-match": {
-      "version": "4.0.4",
-      "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
-      "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": "18 || 20 || >=22"
-      }
-    },
-    "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": {
-      "version": "5.0.6",
-      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz",
-      "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "balanced-match": "^4.0.2"
-      },
-      "engines": {
-        "node": "18 || 20 || >=22"
-      }
-    },
-    "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": {
-      "version": "10.2.5",
-      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz",
-      "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==",
-      "dev": true,
-      "license": "BlueOak-1.0.0",
-      "dependencies": {
-        "brace-expansion": "^5.0.5"
-      },
-      "engines": {
-        "node": "18 || 20 || >=22"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/isaacs"
-      }
-    },
     "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": {
-      "version": "7.8.2",
-      "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.2.tgz",
-      "integrity": "sha512-c8jsqUZm3omBOI66G90z1Dyw5z622G8oLG+omfsHBJf3CWQTlOcwOjvOG6wtiNfW6anKm/eA39LMwMtMez2TiQ==",
+      "version": "7.8.4",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.4.tgz",
+      "integrity": "sha512-rUCObTnP32Q08R2uuIrt7r9PlEonuTmtuXYcW6s5kjdlj3xbnwe+21yXptAUYcMAABLkYYTtnmzb3w3EDZfueA==",
       "dev": true,
       "license": "ISC",
       "bin": {
@@ -951,16 +861,16 @@
       }
     },
     "node_modules/@typescript-eslint/utils": {
-      "version": "8.60.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.60.1.tgz",
-      "integrity": "sha512-h2MPBLoNtjc3qZWfY3Tl51yPorQ2McHn8pJfcMNTcIvrrZrr90Ykffit0yjrPFWQcRcUxzH20+6OcVdW4yHtUg==",
+      "version": "8.61.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.61.0.tgz",
+      "integrity": "sha512-3bzFt7ImFMW/jVYwJamDoe/dMOdFLSC6pom6rRjdh4SZJEYupyMzem8e7vKZLclLfpHjlwSAXOUxtKxGXUiLqA==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
         "@eslint-community/eslint-utils": "^4.9.1",
-        "@typescript-eslint/scope-manager": "8.60.1",
-        "@typescript-eslint/types": "8.60.1",
-        "@typescript-eslint/typescript-estree": "8.60.1"
+        "@typescript-eslint/scope-manager": "8.61.0",
+        "@typescript-eslint/types": "8.61.0",
+        "@typescript-eslint/typescript-estree": "8.61.0"
       },
       "engines": {
         "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -975,13 +885,13 @@
       }
     },
     "node_modules/@typescript-eslint/visitor-keys": {
-      "version": "8.60.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.60.1.tgz",
-      "integrity": "sha512-EbGRQg4FhrmwLodl+t3JNAnXHWVr9Vp+Zl1QBZVPY4ByfkzIT8cX3K6QWODHtkIZqqJVEWvhHSx3v5PDHsaQag==",
+      "version": "8.61.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.61.0.tgz",
+      "integrity": "sha512-QVLZu3ZPQEE+HICQyAMZ2yLQhxf0meY/wx6Hx14YcTNj13JB3qHlX3lJ02L3fLGHgERRH71kvYDwiXIguT3AjQ==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@typescript-eslint/types": "8.60.1",
+        "@typescript-eslint/types": "8.61.0",
         "eslint-visitor-keys": "^5.0.0"
       },
       "engines": {
@@ -1279,13 +1189,6 @@
         "url": "https://github.com/chalk/ansi-styles?sponsor=1"
       }
     },
-    "node_modules/argparse": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
-      "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
-      "dev": true,
-      "license": "Python-2.0"
-    },
     "node_modules/array-buffer-byte-length": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz",
@@ -1468,11 +1371,14 @@
       }
     },
     "node_modules/balanced-match": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
-      "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+      "version": "4.0.4",
+      "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
+      "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==",
       "dev": true,
-      "license": "MIT"
+      "license": "MIT",
+      "engines": {
+        "node": "18 || 20 || >=22"
+      }
     },
     "node_modules/baseline-browser-mapping": {
       "version": "2.10.34",
@@ -1487,14 +1393,16 @@
       }
     },
     "node_modules/brace-expansion": {
-      "version": "1.1.15",
-      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz",
-      "integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==",
+      "version": "5.0.6",
+      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz",
+      "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "balanced-match": "^1.0.0",
-        "concat-map": "0.0.1"
+        "balanced-match": "^4.0.2"
+      },
+      "engines": {
+        "node": "18 || 20 || >=22"
       }
     },
     "node_modules/braces": {
@@ -1598,16 +1506,6 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
-    "node_modules/callsites": {
-      "version": "3.1.0",
-      "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
-      "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=6"
-      }
-    },
     "node_modules/caniuse-lite": {
       "version": "1.0.30001797",
       "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001797.tgz",
@@ -1982,9 +1880,9 @@
       }
     },
     "node_modules/es-iterator-helpers": {
-      "version": "1.3.2",
-      "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.3.2.tgz",
-      "integrity": "sha512-HVLACW1TppGYjJ8H6/jqH/pqOtKRw6wMlrB23xfExmFWxFquAIWCmwoLsOyN96K4a5KbmOf5At9ZUO3GZbetAw==",
+      "version": "1.3.3",
+      "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.3.3.tgz",
+      "integrity": "sha512-0PuBxFi+4uPanB97iDxCLWuHeYud2FALrw5HFZGtAF38UpJDbDC8frwp2cnDyae692CQ0dou60UwWfhgsa4U/g==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
@@ -2098,33 +1996,30 @@
       }
     },
     "node_modules/eslint": {
-      "version": "9.39.4",
-      "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.4.tgz",
-      "integrity": "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==",
+      "version": "10.4.1",
+      "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.4.1.tgz",
+      "integrity": "sha512-AyIKhnOBuOAdueD7RB3xB+YeAWScb9jHsJBgH2Hcde8InP5JYhqrRR6iTMHyTEwgENK54Cp44e4v8BwNhsuHuw==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
         "@eslint-community/eslint-utils": "^4.8.0",
-        "@eslint-community/regexpp": "^4.12.1",
-        "@eslint/config-array": "^0.21.2",
-        "@eslint/config-helpers": "^0.4.2",
-        "@eslint/core": "^0.17.0",
-        "@eslint/eslintrc": "^3.3.5",
-        "@eslint/js": "9.39.4",
-        "@eslint/plugin-kit": "^0.4.1",
+        "@eslint-community/regexpp": "^4.12.2",
+        "@eslint/config-array": "^0.23.5",
+        "@eslint/config-helpers": "^0.6.0",
+        "@eslint/core": "^1.2.1",
+        "@eslint/plugin-kit": "^0.7.2",
         "@humanfs/node": "^0.16.6",
         "@humanwhocodes/module-importer": "^1.0.1",
         "@humanwhocodes/retry": "^0.4.2",
         "@types/estree": "^1.0.6",
         "ajv": "^6.14.0",
-        "chalk": "^4.0.0",
         "cross-spawn": "^7.0.6",
         "debug": "^4.3.2",
         "escape-string-regexp": "^4.0.0",
-        "eslint-scope": "^8.4.0",
-        "eslint-visitor-keys": "^4.2.1",
-        "espree": "^10.4.0",
-        "esquery": "^1.5.0",
+        "eslint-scope": "^9.1.2",
+        "eslint-visitor-keys": "^5.0.1",
+        "espree": "^11.2.0",
+        "esquery": "^1.7.0",
         "esutils": "^2.0.2",
         "fast-deep-equal": "^3.1.3",
         "file-entry-cache": "^8.0.0",
@@ -2134,8 +2029,7 @@
         "imurmurhash": "^0.1.4",
         "is-glob": "^4.0.0",
         "json-stable-stringify-without-jsonify": "^1.0.1",
-        "lodash.merge": "^4.6.2",
-        "minimatch": "^3.1.5",
+        "minimatch": "^10.2.4",
         "natural-compare": "^1.4.0",
         "optionator": "^0.9.3"
       },
@@ -2143,7 +2037,7 @@
         "eslint": "bin/eslint.js"
       },
       "engines": {
-        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+        "node": "^20.19.0 || ^22.13.0 || >=24"
       },
       "funding": {
         "url": "https://eslint.org/donate"
@@ -2178,68 +2072,57 @@
         "eslint": "^8.57.1 || ^9.0.0"
       }
     },
-    "node_modules/eslint-config-preact/node_modules/globals": {
-      "version": "16.5.0",
-      "resolved": "https://registry.npmjs.org/globals/-/globals-16.5.0.tgz",
-      "integrity": "sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=18"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/eslint-plugin-compat": {
-      "version": "6.2.1",
-      "resolved": "https://registry.npmjs.org/eslint-plugin-compat/-/eslint-plugin-compat-6.2.1.tgz",
-      "integrity": "sha512-gLKqUH+lQcCL+HzsROUjBDvakc5Zaga51Y4ZAkPCXc41pzKBfyluqTr2j8zOx8QQQb7zyglu1LVoL5aSNWf2SQ==",
+    "node_modules/eslint-config-preact/node_modules/@babel/eslint-parser": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.29.7.tgz",
+      "integrity": "sha512-zxt+UJTOMKvUt3yOg+D58MLuz334pHp93qifMFcjIIO+9hN6t+ufw2gi7vDPMpxvfnHRR+3VVXvIjineCcgyXw==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@mdn/browser-compat-data": "^6.1.1",
-        "ast-metadata-inferer": "^0.8.1",
-        "browserslist": "^4.25.2",
-        "find-up": "^5.0.0",
-        "globals": "^15.7.0",
-        "lodash.memoize": "^4.1.2",
-        "semver": "^7.6.2"
+        "@nicolo-ribaudo/eslint-scope-5-internals": "5.1.1-v1",
+        "eslint-visitor-keys": "^2.1.0",
+        "semver": "^6.3.1"
       },
       "engines": {
-        "node": ">=18.x"
+        "node": "^10.13.0 || ^12.13.0 || >=14.0.0"
       },
       "peerDependencies": {
-        "eslint": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0"
+        "@babel/core": "^7.11.0",
+        "eslint": "^7.5.0 || ^8.0.0 || ^9.0.0"
       }
     },
-    "node_modules/eslint-plugin-compat/node_modules/globals": {
-      "version": "15.15.0",
-      "resolved": "https://registry.npmjs.org/globals/-/globals-15.15.0.tgz",
-      "integrity": "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==",
+    "node_modules/eslint-config-preact/node_modules/@eslint/js": {
+      "version": "9.39.4",
+      "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.4.tgz",
+      "integrity": "sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==",
       "dev": true,
       "license": "MIT",
       "engines": {
-        "node": ">=18"
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
       },
       "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
+        "url": "https://eslint.org/donate"
       }
     },
-    "node_modules/eslint-plugin-compat/node_modules/semver": {
-      "version": "7.8.2",
-      "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.2.tgz",
-      "integrity": "sha512-c8jsqUZm3omBOI66G90z1Dyw5z622G8oLG+omfsHBJf3CWQTlOcwOjvOG6wtiNfW6anKm/eA39LMwMtMez2TiQ==",
+    "node_modules/eslint-config-preact/node_modules/balanced-match": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+      "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
       "dev": true,
-      "license": "ISC",
-      "bin": {
-        "semver": "bin/semver.js"
-      },
-      "engines": {
-        "node": ">=10"
+      "license": "MIT"
+    },
+    "node_modules/eslint-config-preact/node_modules/brace-expansion": {
+      "version": "1.1.15",
+      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz",
+      "integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "balanced-match": "^1.0.0",
+        "concat-map": "0.0.1"
       }
     },
-    "node_modules/eslint-plugin-react": {
+    "node_modules/eslint-config-preact/node_modules/eslint-plugin-react": {
       "version": "7.37.5",
       "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.5.tgz",
       "integrity": "sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==",
@@ -2272,7 +2155,7 @@
         "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7"
       }
     },
-    "node_modules/eslint-plugin-react-hooks": {
+    "node_modules/eslint-config-preact/node_modules/eslint-plugin-react-hooks": {
       "version": "5.2.0",
       "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.2.0.tgz",
       "integrity": "sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==",
@@ -2285,18 +2168,104 @@
         "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0"
       }
     },
+    "node_modules/eslint-config-preact/node_modules/eslint-visitor-keys": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz",
+      "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/eslint-config-preact/node_modules/globals": {
+      "version": "16.5.0",
+      "resolved": "https://registry.npmjs.org/globals/-/globals-16.5.0.tgz",
+      "integrity": "sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/eslint-config-preact/node_modules/minimatch": {
+      "version": "3.1.5",
+      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
+      "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "brace-expansion": "^1.1.7"
+      },
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/eslint-plugin-compat": {
+      "version": "6.2.1",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-compat/-/eslint-plugin-compat-6.2.1.tgz",
+      "integrity": "sha512-gLKqUH+lQcCL+HzsROUjBDvakc5Zaga51Y4ZAkPCXc41pzKBfyluqTr2j8zOx8QQQb7zyglu1LVoL5aSNWf2SQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@mdn/browser-compat-data": "^6.1.1",
+        "ast-metadata-inferer": "^0.8.1",
+        "browserslist": "^4.25.2",
+        "find-up": "^5.0.0",
+        "globals": "^15.7.0",
+        "lodash.memoize": "^4.1.2",
+        "semver": "^7.6.2"
+      },
+      "engines": {
+        "node": ">=18.x"
+      },
+      "peerDependencies": {
+        "eslint": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0"
+      }
+    },
+    "node_modules/eslint-plugin-compat/node_modules/globals": {
+      "version": "15.15.0",
+      "resolved": "https://registry.npmjs.org/globals/-/globals-15.15.0.tgz",
+      "integrity": "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/eslint-plugin-compat/node_modules/semver": {
+      "version": "7.8.2",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.2.tgz",
+      "integrity": "sha512-c8jsqUZm3omBOI66G90z1Dyw5z622G8oLG+omfsHBJf3CWQTlOcwOjvOG6wtiNfW6anKm/eA39LMwMtMez2TiQ==",
+      "dev": true,
+      "license": "ISC",
+      "bin": {
+        "semver": "bin/semver.js"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
     "node_modules/eslint-scope": {
-      "version": "8.4.0",
-      "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz",
-      "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==",
+      "version": "9.1.2",
+      "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-9.1.2.tgz",
+      "integrity": "sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==",
       "dev": true,
       "license": "BSD-2-Clause",
       "dependencies": {
+        "@types/esrecurse": "^4.3.1",
+        "@types/estree": "^1.0.8",
         "esrecurse": "^4.3.0",
         "estraverse": "^5.2.0"
       },
       "engines": {
-        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+        "node": "^20.19.0 || ^22.13.0 || >=24"
       },
       "funding": {
         "url": "https://opencollective.com/eslint"
@@ -2315,6 +2284,37 @@
         "url": "https://opencollective.com/eslint"
       }
     },
+    "node_modules/eslint/node_modules/eslint-visitor-keys": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz",
+      "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "engines": {
+        "node": "^20.19.0 || ^22.13.0 || >=24"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint"
+      }
+    },
+    "node_modules/eslint/node_modules/espree": {
+      "version": "11.2.0",
+      "resolved": "https://registry.npmjs.org/espree/-/espree-11.2.0.tgz",
+      "integrity": "sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "dependencies": {
+        "acorn": "^8.16.0",
+        "acorn-jsx": "^5.3.2",
+        "eslint-visitor-keys": "^5.0.1"
+      },
+      "engines": {
+        "node": "^20.19.0 || ^22.13.0 || >=24"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint"
+      }
+    },
     "node_modules/espree": {
       "version": "10.4.0",
       "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz",
@@ -2538,18 +2538,21 @@
       }
     },
     "node_modules/function.prototype.name": {
-      "version": "1.1.8",
-      "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz",
-      "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==",
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.2.0.tgz",
+      "integrity": "sha512-jObKIik1P2QjPHP5nz5BaOtUlfgS0fWo8IUByNXkM+o+02sJOi94em77GwJKQSJ3gfPHdgzLNrHc1uokV4P/ew==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "call-bind": "^1.0.8",
-        "call-bound": "^1.0.3",
-        "define-properties": "^1.2.1",
+        "call-bind": "^1.0.9",
+        "call-bound": "^1.0.4",
+        "es-define-property": "^1.0.1",
+        "es-errors": "^1.3.0",
         "functions-have-names": "^1.2.3",
-        "hasown": "^2.0.2",
-        "is-callable": "^1.2.7"
+        "has-property-descriptors": "^1.0.2",
+        "hasown": "^2.0.4",
+        "is-callable": "^1.2.7",
+        "is-document.all": "^1.0.0"
       },
       "engines": {
         "node": ">= 0.4"
@@ -2815,23 +2818,6 @@
         "node": ">= 4"
       }
     },
-    "node_modules/import-fresh": {
-      "version": "3.3.1",
-      "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
-      "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "parent-module": "^1.0.0",
-        "resolve-from": "^4.0.0"
-      },
-      "engines": {
-        "node": ">=6"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
     "node_modules/import-local": {
       "version": "3.2.0",
       "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz",
@@ -3019,6 +3005,22 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
+    "node_modules/is-document.all": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/is-document.all/-/is-document.all-1.0.0.tgz",
+      "integrity": "sha512-+XSoyS05OdBbhFuELhgTCpFNHkpBOJqtsZfUFFpe5QTw+9Sjbh8zitxhQkYAo6wV7e1Vb8cAPvpCk9jGam/82g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "call-bound": "^1.0.4"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
     "node_modules/is-extglob": {
       "version": "2.1.1",
       "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
@@ -3363,29 +3365,6 @@
       "dev": true,
       "license": "MIT"
     },
-    "node_modules/js-yaml": {
-      "version": "4.2.0",
-      "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.2.0.tgz",
-      "integrity": "sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==",
-      "dev": true,
-      "funding": [
-        {
-          "type": "github",
-          "url": "https://github.com/sponsors/puzrin"
-        },
-        {
-          "type": "github",
-          "url": "https://github.com/sponsors/nodeca"
-        }
-      ],
-      "license": "MIT",
-      "dependencies": {
-        "argparse": "^2.0.1"
-      },
-      "bin": {
-        "js-yaml": "bin/js-yaml.js"
-      }
-    },
     "node_modules/jsesc": {
       "version": "3.1.0",
       "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",
@@ -3518,13 +3497,6 @@
       "dev": true,
       "license": "MIT"
     },
-    "node_modules/lodash.merge": {
-      "version": "4.6.2",
-      "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
-      "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
-      "dev": true,
-      "license": "MIT"
-    },
     "node_modules/loose-envify": {
       "version": "1.4.0",
       "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
@@ -3599,16 +3571,19 @@
       }
     },
     "node_modules/minimatch": {
-      "version": "3.1.5",
-      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
-      "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
+      "version": "10.2.5",
+      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz",
+      "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==",
       "dev": true,
-      "license": "ISC",
+      "license": "BlueOak-1.0.0",
       "dependencies": {
-        "brace-expansion": "^1.1.7"
+        "brace-expansion": "^5.0.5"
       },
       "engines": {
-        "node": "*"
+        "node": "18 || 20 || >=22"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
       }
     },
     "node_modules/ms": {
@@ -3844,19 +3819,6 @@
         "node": ">=6"
       }
     },
-    "node_modules/parent-module": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
-      "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "callsites": "^3.0.0"
-      },
-      "engines": {
-        "node": ">=6"
-      }
-    },
     "node_modules/path-exists": {
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
@@ -4153,16 +4115,6 @@
         "node": ">=8"
       }
     },
-    "node_modules/resolve-from": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
-      "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=4"
-      }
-    },
     "node_modules/safe-array-concat": {
       "version": "1.1.4",
       "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.4.tgz",
@@ -4364,15 +4316,15 @@
       }
     },
     "node_modules/side-channel": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
-      "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==",
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz",
+      "integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
         "es-errors": "^1.3.0",
-        "object-inspect": "^1.13.3",
-        "side-channel-list": "^1.0.0",
+        "object-inspect": "^1.13.4",
+        "side-channel-list": "^1.0.1",
         "side-channel-map": "^1.0.1",
         "side-channel-weakmap": "^1.0.2"
       },
@@ -4580,19 +4532,6 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
-    "node_modules/strip-json-comments": {
-      "version": "3.1.1",
-      "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
-      "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=8"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
     "node_modules/supports-color": {
       "version": "7.2.0",
       "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
@@ -4894,16 +4833,16 @@
       }
     },
     "node_modules/typescript-eslint": {
-      "version": "8.60.1",
-      "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.60.1.tgz",
-      "integrity": "sha512-6m5hkkRAp8lKvhVpcprAIn5KkehQEh+47oHH2VGnExEh7dhNxXlg6GPAOIu6TxbVQxhebrJDvjl3020ooiWCMA==",
+      "version": "8.61.0",
+      "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.61.0.tgz",
+      "integrity": "sha512-8y31Rd0eGTrDKqhy6vT0HtzhN+YLjQizwX3aA3hPXP/ynSfnrBXcQY5IzsP9/DM7+klX4IUncZZjkchP0z+rUw==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@typescript-eslint/eslint-plugin": "8.60.1",
-        "@typescript-eslint/parser": "8.60.1",
-        "@typescript-eslint/typescript-estree": "8.60.1",
-        "@typescript-eslint/utils": "8.60.1"
+        "@typescript-eslint/eslint-plugin": "8.61.0",
+        "@typescript-eslint/parser": "8.61.0",
+        "@typescript-eslint/typescript-estree": "8.61.0",
+        "@typescript-eslint/utils": "8.61.0"
       },
       "engines": {
         "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
diff --git a/dumper-companion/package.json b/dumper-companion/package.json
index 90e97964..41198ca5 100644
--- a/dumper-companion/package.json
+++ b/dumper-companion/package.json
@@ -16,11 +16,11 @@
     "webpack-cli": "^7.0.2"
   },
   "devDependencies": {
-    "@eslint/js": "^9.39.4",
+    "@eslint/js": "^10.0.1",
     "@stylistic/eslint-plugin": "^5.10.0",
-    "eslint": "^9.39.4",
+    "eslint": "^10.4.1",
     "eslint-config-preact": "^2.0.0",
     "globals": "^17.5.0",
-    "typescript-eslint": "^8.58.2"
+    "typescript-eslint": "^8.61.0"
   }
 }



From noreply at scummvm.org  Fri Jun 12 10:13:50 2026
From: noreply at scummvm.org (ScummVM git)
Date: Fri, 12 Jun 2026 10:13:50 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm-web dependabot/npm_and_yarn/sass-1.101.0
 -> 0b540fb092515053a0f10bfcc60c3d28b210f50f
Message-ID: <20260612101350.4053D7005CC@ron.scummvm.net>

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

Summary:
0b540fb092 Bump sass from 1.100.0 to 1.101.0


Commit: 0b540fb092515053a0f10bfcc60c3d28b210f50f
    https://github.com/scummvm/scummvm-web/commit/0b540fb092515053a0f10bfcc60c3d28b210f50f
Author: dependabot[bot] (49699333+dependabot[bot]@users.noreply.github.com)
Date: 2026-06-12T10:13:47Z

Commit Message:
Bump sass from 1.100.0 to 1.101.0

Bumps [sass](https://github.com/sass/dart-sass) from 1.100.0 to 1.101.0.
- [Release notes](https://github.com/sass/dart-sass/releases)
- [Changelog](https://github.com/sass/dart-sass/blob/main/CHANGELOG.md)
- [Commits](https://github.com/sass/dart-sass/compare/1.100.0...1.101.0)

---
updated-dependencies:
- dependency-name: sass
  dependency-version: 1.101.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support at github.com>

Changed paths:
    package-lock.json
    package.json


diff --git a/package-lock.json b/package-lock.json
index 17dc5924..88003fcd 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -11,7 +11,7 @@
       "dependencies": {
         "autoprefixer": "^10.5.0",
         "postcss": "^8.5.14",
-        "sass": "^1.100.0"
+        "sass": "^1.101.0"
       }
     },
     "node_modules/@parcel/watcher": {
@@ -594,9 +594,9 @@
       }
     },
     "node_modules/sass": {
-      "version": "1.100.0",
-      "resolved": "https://registry.npmjs.org/sass/-/sass-1.100.0.tgz",
-      "integrity": "sha512-B5j0rYMlinhhOo9tjQebMVVn0TfyXAF+wB3b2ggZUuJ/is/Y+7+JGjirAMxHZ9Z3hIP98NPfamlAkBHa1lAaXQ==",
+      "version": "1.101.0",
+      "resolved": "https://registry.npmjs.org/sass/-/sass-1.101.0.tgz",
+      "integrity": "sha512-OL3GoQyoUdDt843DpVmDO6y2k1sc5IhUDSpu8XucEI+35neq5QivZ1iuegnpraEVTJXlQGK1gl27zKcTLEPbQw==",
       "license": "MIT",
       "dependencies": {
         "chokidar": "^5.0.0",
diff --git a/package.json b/package.json
index b28c581b..47cfbf9b 100644
--- a/package.json
+++ b/package.json
@@ -5,7 +5,7 @@
   "dependencies": {
     "autoprefixer": "^10.5.0",
     "postcss": "^8.5.14",
-    "sass": "^1.100.0"
+    "sass": "^1.101.0"
   },
   "scripts": {
     "postinstall": "(cd dumper-companion && npm ci --omit=dev && npm run build) && node build.js",



From noreply at scummvm.org  Fri Jun 12 10:48:14 2026
From: noreply at scummvm.org (sev-)
Date: Fri, 12 Jun 2026 10:48:14 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 293de77aef9f7979918a4bc887286d869272540e
Message-ID: <20260612104814.452297005CC@ron.scummvm.net>

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

Summary:
293de77aef ACCESS: Added noctropolis second demo and the rerelease to the detection tables


Commit: 293de77aef9f7979918a4bc887286d869272540e
    https://github.com/scummvm/scummvm/commit/293de77aef9f7979918a4bc887286d869272540e
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2026-06-12T12:48:04+02:00

Commit Message:
ACCESS: Added noctropolis second demo and the rerelease to the detection tables

Changed paths:
    engines/access/detection_tables.h


diff --git a/engines/access/detection_tables.h b/engines/access/detection_tables.h
index 8ea38e57609..cf9398ab7a1 100644
--- a/engines/access/detection_tables.h
+++ b/engines/access/detection_tables.h
@@ -134,8 +134,25 @@ static const AccessGameDescription gameDescriptions[] = {
 		// Noctropolis Demo
 		{
 			"noctropolis",
-			"Demo",
-			AD_ENTRY1s("demo/scene01.ap", "3a154bf58e10cd7ace14cab1bf5adf4a", 145796),
+			"Demo/Sep 24",
+			AD_ENTRY2s("demo/scene01.ap", "3a154bf58e10cd7ace14cab1bf5adf4a", 145796,
+					   "demo/scene04.ap", "3ee5e24db63bf8029a6d0c91268f5e6e", 96268),
+			Common::EN_ANY,
+			Common::kPlatformWindows,
+			ADGF_TESTING | ADGF_DEMO,
+			GUIO1(GUIO_NONE)
+		},
+		kGameNoctropolis,
+		0
+	},
+
+	{
+		// Noctropolis Demo
+		{
+			"noctropolis",
+			"Demo/Sep 26",
+			AD_ENTRY2s("demo/scene01.ap", "3a154bf58e10cd7ace14cab1bf5adf4a", 145796,
+					   "demo/scene04.ap", "ee72734c5bdc62e1ef76841f5f451713", 96289),
 			Common::EN_ANY,
 			Common::kPlatformWindows,
 			ADGF_TESTING | ADGF_DEMO,
@@ -150,7 +167,24 @@ static const AccessGameDescription gameDescriptions[] = {
 		{
 			"noctropolis",
 			nullptr,
-			AD_ENTRY1s("dark/scene01.ap", "3a154bf58e10cd7ace14cab1bf5adf4a", 147954),
+			AD_ENTRY2s("dark/scene01.ap", "3a154bf58e10cd7ace14cab1bf5adf4a", 147954,
+					   "dark/scene13.ap", "0f02b3f1472beb7fb36e4293009baac0", 327959),
+			Common::EN_ANY,
+			Common::kPlatformWindows,
+			ADGF_TESTING,
+			GUIO1(GUIO_NONE)
+		},
+		kGameNoctropolis,
+		0
+	},
+
+	{
+		// Noctropolis - GOG/Steam version
+		{
+			"noctropolis",
+			"Rerelease",
+			AD_ENTRY2s("dark/scene01.ap", "3a154bf58e10cd7ace14cab1bf5adf4a", 147954,
+					   "dark/scene13.ap", "0d400713ed30e692d63af4d28ba42c5e", 327980),
 			Common::EN_ANY,
 			Common::kPlatformWindows,
 			ADGF_TESTING,



From noreply at scummvm.org  Fri Jun 12 10:50:01 2026
From: noreply at scummvm.org (mduggan)
Date: Fri, 12 Jun 2026 10:50:01 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 e96a35c34266575be56c043214102eb1ee9ed2cd
Message-ID: <20260612105001.A86257005CC@ron.scummvm.net>

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

Summary:
e96a35c342 ACCESS: Fix crashes in Noctropolis Demo 2


Commit: e96a35c34266575be56c043214102eb1ee9ed2cd
    https://github.com/scummvm/scummvm/commit/e96a35c34266575be56c043214102eb1ee9ed2cd
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2026-06-12T20:49:02+10:00

Commit Message:
ACCESS: Fix crashes in Noctropolis Demo 2

Changed paths:
    engines/access/noctropolis/noctropolis_comicviewer.cpp
    engines/access/sound.cpp


diff --git a/engines/access/noctropolis/noctropolis_comicviewer.cpp b/engines/access/noctropolis/noctropolis_comicviewer.cpp
index c099bea5a6a..30bc311df63 100644
--- a/engines/access/noctropolis/noctropolis_comicviewer.cpp
+++ b/engines/access/noctropolis/noctropolis_comicviewer.cpp
@@ -72,7 +72,15 @@ void ComicViewer::run(const ComicResource *comic) {
 PageResult ComicViewer::runPage(const ComicPage *page) {
 	PageResult result = kPageResultNone;
 
-	_vm->_files->loadScreen(Common::Path(page->filename));
+	Common::Path pagePath(page->filename);
+
+	if (!_vm->_files->existFile(pagePath)) {
+		// Happens in Demo 2 - no comic files
+		warning("Request to load missing comic page '%s'.", pagePath.toString().c_str());
+		return kPageResultExit;
+	}
+
+	_vm->_files->loadScreen(pagePath);
 
 	Resource *bubbleData = _vm->_files->loadRawFile("COMDATA/comic.ap");
 	_bubbleSprites = new SpriteResource(_vm, bubbleData);
diff --git a/engines/access/sound.cpp b/engines/access/sound.cpp
index b54bb14c776..214cfcf742d 100644
--- a/engines/access/sound.cpp
+++ b/engines/access/sound.cpp
@@ -120,6 +120,12 @@ void SoundManager::playSound(int soundIndex, bool loop /* = false */) {
 		// Prevent duplicate copies of a sound from being queued
 		return;
 
+	if (soundIndex >= (int)_soundTable.size()) {
+		// Happens in Noctropolis demo 2
+		warning("Request to play sound with invalid index %d.", soundIndex);
+		return;
+	}
+
 	int priority = _soundTable[soundIndex]._priority;
 	playSound(_soundTable[soundIndex]._res, priority, loop, soundIndex);
 }
@@ -249,6 +255,11 @@ void SoundManager::loadSounds(const Common::Array<RoomInfo::SoundIdent> &sounds)
 
 int SoundManager::loadRawSound(const Common::Path &path, int priority) {
 	debugC(1, kDebugSound, "loadRawSound(%s)", path.toString().c_str());
+	if (!_vm->_files->existFile(path)) {
+		// In Noctropolis demo 2 intro sounds are not included
+		warning("Request to load missing sound file '%s'.", path.toString().c_str());
+		return -1;
+	}
 	Resource *soundRes = _vm->_files->loadRawFile(path);
 	_soundTable.push_back(SoundEntry(soundRes, priority));
 	return _soundTable.size() - 1;



From noreply at scummvm.org  Fri Jun 12 10:59:21 2026
From: noreply at scummvm.org (lotharsm)
Date: Fri, 12 Jun 2026 10:59:21 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm-web master ->
 3ccd815e5cdfb11295d269c2d17b370991ee2135
Message-ID: <20260612105921.3A8097005CC@ron.scummvm.net>

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

Summary:
c2677e1940 L10N: Update base news translation file
3ccd815e5c DATA: Update data files


Commit: c2677e1940696017f06821b288325003b2299553
    https://github.com/scummvm/scummvm-web/commit/c2677e1940696017f06821b288325003b2299553
Author: sev- (sev at scummvm.org)
Date: 2026-06-12T10:59:16Z

Commit Message:
L10N: Update base news translation file

Changed paths:
    data/en/news.json


diff --git a/data/en/news.json b/data/en/news.json
index 57e562ed..3c3cb567 100644
--- a/data/en/news.json
+++ b/data/en/news.json
@@ -1746,5 +1746,9 @@
     "20260503": {
         "title": "5 Million Lines of Code. 5 Million Stories.",
         "content": "A couple of weeks ago, we silently crossed an important milestone for the ScummVM project; a milestone we are very proud of.\n\nAccording to repository analysis [performed by Open Hub](https://openhub.net/p/scummvm/), ScummVM???s code base now contains more than **5 Million Lines of Code**, crafted by more than **900 contributors** over almost 25 years.\n\nDear contributors, on behalf of the entire ScummVM community: Thousands of people are using your code every day, reliving their childhood memories or exploring stories they have yet to discover.\n\nYour passion and endurance are what makes this possible. You are not only writing history, you are preserving it.\n\nThank you!"
+    },
+    "20260612": {
+        "title": "A darker shade of Grey: Noctropolis is Ready for Testing!",
+        "content": "Tired of running a mundane bookstore and ready to dive into the gritty, neon-drenched pages of a comic book? Get your trench coat ready, because Flashpoint Productions??? 1994 dark adventure, _Noctropolis_ is now ready for public testing!\n\nIn this interactive comic brought to life you play as Peter Grey, a lonely bookstore owner pulled into a dystopian superhero world where the sun never shines. With the legendary hero Darksheer retired, it is up to you to take on his role, solve the puzzles, and save the city from a cast of villains doing the bidding of a mysterious force.\n\nTo help us test, you will need your copy of the [game data files](https://wiki.scummvm.org/index.php?title=Noctropolis#Required_Data) and a recent [daily development build](https://www.scummvm.org/downloads/#daily) of ScummVM.\n\nSupport has been added for both the original DOS release (including demos) and the Nightdive Studios digital re-release, which is available from [GOG](https://www.gog.com/game/noctropolis) or [Steam](https://store.steampowered.com/app/377070).\n\nAs you patrol the dangerous streets, please report any issues you find on our [bug tracker](https://bugs.scummvm.org/).\n\nThe city needs a hero ??? will you answer the call? Happy crime-fighting!"
     }
 }


Commit: 3ccd815e5cdfb11295d269c2d17b370991ee2135
    https://github.com/scummvm/scummvm-web/commit/3ccd815e5cdfb11295d269c2d17b370991ee2135
Author: sev- (sev at scummvm.org)
Date: 2026-06-12T10:59:16Z

Commit Message:
DATA: Update data files

Changed paths:
    data/en/compatibility.yaml
    data/en/game_demos.yaml


diff --git a/data/en/compatibility.yaml b/data/en/compatibility.yaml
index 2f5c7bb5..82c7a3bd 100644
--- a/data/en/compatibility.yaml
+++ b/data/en/compatibility.yaml
@@ -5791,7 +5791,7 @@
 -
     id: 'access:noctropolis'
     support: good
-    notes: '- Only EN language currently supported'
+    notes: '- Only English version is supported'
     since_version: DEV
     stable_platforms: win
     unstable_platforms: ''
diff --git a/data/en/game_demos.yaml b/data/en/game_demos.yaml
index 8c833f57..48f60f73 100644
--- a/data/en/game_demos.yaml
+++ b/data/en/game_demos.yaml
@@ -17,6 +17,24 @@
     language2: ''
     language3: ''
     url: /frs/demos/access/martian-demo.zip
+-
+    id: 'access:noctropolis'
+    platform: win
+    '': ''
+    category: 'September 24'
+    language: en
+    language2: ''
+    language3: ''
+    url: /frs/demos/access/noctropolis-demo1.zip
+-
+    id: 'access:noctropolis'
+    platform: win
+    '': ''
+    category: 'September 26'
+    language: en
+    language2: ''
+    language3: ''
+    url: /frs/demos/access/noctropolis-demo2.zip
 -
     id: 'agi:agidemo'
     platform: apple2gs
@@ -620,6 +638,15 @@
     language2: ''
     language3: ''
     url: /frs/demos/bladerunner/bladerunner-win-ni-demo-en.zip
+-
+    id: 'bolt:carnival'
+    platform: win
+    '': ''
+    category: ''
+    language: it
+    language2: ''
+    language3: ''
+    url: ''
 -
     id: 'buried:buried'
     platform: win



From noreply at scummvm.org  Fri Jun 12 11:00:15 2026
From: noreply at scummvm.org (sev-)
Date: Fri, 12 Jun 2026 11:00:15 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 e5d987bbf169e6f2cd107d37e779a2a8860d7f1f
Message-ID: <20260612110015.EA9197005CC@ron.scummvm.net>

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

Summary:
e5d987bbf1 NEWS: Sorted games according to their announcement order


Commit: e5d987bbf169e6f2cd107d37e779a2a8860d7f1f
    https://github.com/scummvm/scummvm/commit/e5d987bbf169e6f2cd107d37e779a2a8860d7f1f
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2026-06-12T13:00:09+02:00

Commit Message:
NEWS: Sorted games according to their announcement order

Changed paths:
    NEWS.md


diff --git a/NEWS.md b/NEWS.md
index 7c4c8e69855..2bedf86d620 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -6,9 +6,9 @@ For a more comprehensive changelog of the latest experimental code, see:
  New games:
    - Added support for Nancy Drew: The Haunted Carousel.
    - Added support for Nancy Drew: Danger on Deception Island.
-   - Added support for Cartoon Carnival.
-   - Added support for Alfred Pelrock: En Busca de un Sue??o.
    - Added support for Noctropolis.
+   - Added support for Alfred Pelrock: En Busca de un Sue??o.
+   - Added support for Cartoon Carnival.
 
  General:
    - Highlight clickable links in the GUI with a cursor change.



From noreply at scummvm.org  Fri Jun 12 11:07:50 2026
From: noreply at scummvm.org (mduggan)
Date: Fri, 12 Jun 2026 11:07:50 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm-web master ->
 71504f3e093ffa942e22d22712e8ad7c8c06d7ac
Message-ID: <20260612110750.9E8B37005CC@ron.scummvm.net>

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

Summary:
71504f3e09 SCREENSHOTS: Add Noctropolis screenshots


Commit: 71504f3e093ffa942e22d22712e8ad7c8c06d7ac
    https://github.com/scummvm/scummvm-web/commit/71504f3e093ffa942e22d22712e8ad7c8c06d7ac
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2026-06-12T21:07:19+10:00

Commit Message:
SCREENSHOTS: Add Noctropolis screenshots

Changed paths:
  A public_html/data/screenshots/access/noctropolis/noctropolis_win_en_1_1.png
  A public_html/data/screenshots/access/noctropolis/noctropolis_win_en_1_2.png
  A public_html/data/screenshots/access/noctropolis/noctropolis_win_en_1_3.png
  A public_html/data/screenshots/access/noctropolis/noctropolis_win_en_1_4.png


diff --git a/public_html/data/screenshots/access/noctropolis/noctropolis_win_en_1_1.png b/public_html/data/screenshots/access/noctropolis/noctropolis_win_en_1_1.png
new file mode 100644
index 00000000..e1853beb
Binary files /dev/null and b/public_html/data/screenshots/access/noctropolis/noctropolis_win_en_1_1.png differ
diff --git a/public_html/data/screenshots/access/noctropolis/noctropolis_win_en_1_2.png b/public_html/data/screenshots/access/noctropolis/noctropolis_win_en_1_2.png
new file mode 100644
index 00000000..3ea73704
Binary files /dev/null and b/public_html/data/screenshots/access/noctropolis/noctropolis_win_en_1_2.png differ
diff --git a/public_html/data/screenshots/access/noctropolis/noctropolis_win_en_1_3.png b/public_html/data/screenshots/access/noctropolis/noctropolis_win_en_1_3.png
new file mode 100644
index 00000000..4b43887e
Binary files /dev/null and b/public_html/data/screenshots/access/noctropolis/noctropolis_win_en_1_3.png differ
diff --git a/public_html/data/screenshots/access/noctropolis/noctropolis_win_en_1_4.png b/public_html/data/screenshots/access/noctropolis/noctropolis_win_en_1_4.png
new file mode 100644
index 00000000..522e9bf3
Binary files /dev/null and b/public_html/data/screenshots/access/noctropolis/noctropolis_win_en_1_4.png differ



From noreply at scummvm.org  Fri Jun 12 11:14:11 2026
From: noreply at scummvm.org (mduggan)
Date: Fri, 12 Jun 2026 11:14:11 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm-web master ->
 f1c0112a5e8b4a4111fcdb75ed404854e4595098
Message-ID: <20260612111411.CD1697005CC@ron.scummvm.net>

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

Summary:
f1c0112a5e ACCESS: Add Noctrpolis thumbnails


Commit: f1c0112a5e8b4a4111fcdb75ed404854e4595098
    https://github.com/scummvm/scummvm-web/commit/f1c0112a5e8b4a4111fcdb75ed404854e4595098
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2026-06-12T21:13:31+10:00

Commit Message:
ACCESS: Add Noctrpolis thumbnails

Changed paths:
  A public_html/data/screenshots/access/noctropolis/noctropolis_win_en_1_1.jpg
  A public_html/data/screenshots/access/noctropolis/noctropolis_win_en_1_1_full.png
  A public_html/data/screenshots/access/noctropolis/noctropolis_win_en_1_2.jpg
  A public_html/data/screenshots/access/noctropolis/noctropolis_win_en_1_2_full.png
  A public_html/data/screenshots/access/noctropolis/noctropolis_win_en_1_3.jpg
  A public_html/data/screenshots/access/noctropolis/noctropolis_win_en_1_3_full.png
  A public_html/data/screenshots/access/noctropolis/noctropolis_win_en_1_4.jpg
  A public_html/data/screenshots/access/noctropolis/noctropolis_win_en_1_4_full.png
  R public_html/data/screenshots/access/noctropolis/noctropolis_win_en_1_1.png
  R public_html/data/screenshots/access/noctropolis/noctropolis_win_en_1_2.png
  R public_html/data/screenshots/access/noctropolis/noctropolis_win_en_1_3.png
  R public_html/data/screenshots/access/noctropolis/noctropolis_win_en_1_4.png


diff --git a/public_html/data/screenshots/access/noctropolis/noctropolis_win_en_1_1.jpg b/public_html/data/screenshots/access/noctropolis/noctropolis_win_en_1_1.jpg
new file mode 100644
index 00000000..906a8479
Binary files /dev/null and b/public_html/data/screenshots/access/noctropolis/noctropolis_win_en_1_1.jpg differ
diff --git a/public_html/data/screenshots/access/noctropolis/noctropolis_win_en_1_1.png b/public_html/data/screenshots/access/noctropolis/noctropolis_win_en_1_1_full.png
similarity index 100%
rename from public_html/data/screenshots/access/noctropolis/noctropolis_win_en_1_1.png
rename to public_html/data/screenshots/access/noctropolis/noctropolis_win_en_1_1_full.png
diff --git a/public_html/data/screenshots/access/noctropolis/noctropolis_win_en_1_2.jpg b/public_html/data/screenshots/access/noctropolis/noctropolis_win_en_1_2.jpg
new file mode 100644
index 00000000..41f83682
Binary files /dev/null and b/public_html/data/screenshots/access/noctropolis/noctropolis_win_en_1_2.jpg differ
diff --git a/public_html/data/screenshots/access/noctropolis/noctropolis_win_en_1_2.png b/public_html/data/screenshots/access/noctropolis/noctropolis_win_en_1_2_full.png
similarity index 100%
rename from public_html/data/screenshots/access/noctropolis/noctropolis_win_en_1_2.png
rename to public_html/data/screenshots/access/noctropolis/noctropolis_win_en_1_2_full.png
diff --git a/public_html/data/screenshots/access/noctropolis/noctropolis_win_en_1_3.jpg b/public_html/data/screenshots/access/noctropolis/noctropolis_win_en_1_3.jpg
new file mode 100644
index 00000000..57fac706
Binary files /dev/null and b/public_html/data/screenshots/access/noctropolis/noctropolis_win_en_1_3.jpg differ
diff --git a/public_html/data/screenshots/access/noctropolis/noctropolis_win_en_1_3.png b/public_html/data/screenshots/access/noctropolis/noctropolis_win_en_1_3_full.png
similarity index 100%
rename from public_html/data/screenshots/access/noctropolis/noctropolis_win_en_1_3.png
rename to public_html/data/screenshots/access/noctropolis/noctropolis_win_en_1_3_full.png
diff --git a/public_html/data/screenshots/access/noctropolis/noctropolis_win_en_1_4.jpg b/public_html/data/screenshots/access/noctropolis/noctropolis_win_en_1_4.jpg
new file mode 100644
index 00000000..42e4f236
Binary files /dev/null and b/public_html/data/screenshots/access/noctropolis/noctropolis_win_en_1_4.jpg differ
diff --git a/public_html/data/screenshots/access/noctropolis/noctropolis_win_en_1_4.png b/public_html/data/screenshots/access/noctropolis/noctropolis_win_en_1_4_full.png
similarity index 100%
rename from public_html/data/screenshots/access/noctropolis/noctropolis_win_en_1_4.png
rename to public_html/data/screenshots/access/noctropolis/noctropolis_win_en_1_4_full.png



From noreply at scummvm.org  Fri Jun 12 11:31:10 2026
From: noreply at scummvm.org (dreammaster)
Date: Fri, 12 Jun 2026 11:31:10 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 5dd35734a4ddc32f88f80e36d14c865a7fb83207
Message-ID: <20260612113110.3D9A67005CC@ron.scummvm.net>

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:
75aae29046 MADS: FOREST: Implemented room 308
5dd35734a4 MADS: FOREST: Implemented room 321


Commit: 75aae2904673b3be51ba207cb14a588320ac0f65
    https://github.com/scummvm/scummvm/commit/75aae2904673b3be51ba207cb14a588320ac0f65
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-06-12T21:31:01+10:00

Commit Message:
MADS: FOREST: Implemented room 308

Changed paths:
    engines/mads/madsv2/forest/global.cpp
    engines/mads/madsv2/forest/global.h
    engines/mads/madsv2/forest/rooms/room308.cpp


diff --git a/engines/mads/madsv2/forest/global.cpp b/engines/mads/madsv2/forest/global.cpp
index 96b4c21eca6..476693e6ac2 100644
--- a/engines/mads/madsv2/forest/global.cpp
+++ b/engines/mads/madsv2/forest/global.cpp
@@ -36,6 +36,7 @@ namespace Forest {
 
 int16 flags[40];
 bool room_203_flag;
+bool inv_enable_command;
 
 namespace Rooms {
 
diff --git a/engines/mads/madsv2/forest/global.h b/engines/mads/madsv2/forest/global.h
index 7de3fd09301..f5db0c4fecb 100644
--- a/engines/mads/madsv2/forest/global.h
+++ b/engines/mads/madsv2/forest/global.h
@@ -256,6 +256,7 @@ struct AnimationInfo {
 
 extern int16 flags[40];
 extern bool room_203_flag;
+extern bool inv_enable_command;
 
 extern void global_section_constructor();
 extern void sync_room(Common::Serializer &s);
diff --git a/engines/mads/madsv2/forest/rooms/room308.cpp b/engines/mads/madsv2/forest/rooms/room308.cpp
index 247e246d499..b25277516da 100644
--- a/engines/mads/madsv2/forest/rooms/room308.cpp
+++ b/engines/mads/madsv2/forest/rooms/room308.cpp
@@ -1,4 +1,4 @@
-???/* ScummVM - Graphic Adventure Engine
+/* 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
@@ -19,18 +19,18 @@
  *
  */
 
-#include "mads/madsv2/core/conv.h"
+#include "mads/madsv2/core/digi.h"
+#include "mads/madsv2/forest/extra.h"
+#include "mads/madsv2/forest/journal.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/imath.h"
 #include "mads/madsv2/core/inter.h"
 #include "mads/madsv2/core/kernel.h"
-#include "mads/madsv2/core/sound.h"
-#include "mads/madsv2/core/text.h"
-#include "mads/madsv2/forest/mads/inventory.h"
-#include "mads/madsv2/forest/mads/sounds.h"
-#include "mads/madsv2/forest/mads/words.h"
+#include "mads/madsv2/core/object.h"
+#include "mads/madsv2/core/player.h"
+#include "mads/madsv2/core/sprite.h"
 #include "mads/madsv2/forest/global.h"
-#include "mads/madsv2/forest/rooms/section1.h"
+#include "mads/madsv2/forest/rooms/section3.h"
 #include "mads/madsv2/forest/rooms/room308.h"
 
 namespace MADS {
@@ -39,44 +39,1348 @@ namespace Forest {
 namespace Rooms {
 
 struct Scratch {
-	int16 sprite[10];       /* Sprite series handles */
-	int16 sequence[10];     /* Sequence handles      */
-	int16 animation[10];     /* Animation handles     */
-	AnimationInfo animation_info[10];
+	int16 sprite[10];                 // 0x00???0x13
+	int16 sequence[10];               // 0x14???0x27
+	int16 animation[10];              // 0x28???0x3B
+	AnimationInfo animation_info[10]; // 0x3C???0x8B
+	int16 _8c;                        // 0x8C
+	int16 _8e;                        // 0x8E
+	int16 _90;                        // 0x90
+	int16 _92;                        // 0x92
+	int16 _94;                        // 0x94
+	int16 _96;                        // 0x96
+	int16 _98;                        // 0x98
+	int16 _9a;                        // 0x9A
+	int16 _9c;                        // 0x9C
+	int16 _9e;                        // 0x9E
+	int16 _a0;                        // 0xA0
+	int16 _a2;                        // 0xA2
+	int16 _a4;                        // 0xA4
+	int16 _a6;                        // 0xA6
+	int16 _a8;                        // 0xA8
+	int16 _aa;                        // 0xAA
+	int16 _ac;                        // 0xAC
+	int16 _ae;                        // 0xAE
+	int16 _b0;                        // 0xB0
+	int16 _b2;                        // 0xB2
+	int16 _b4;                        // 0xB4
+	int16 _b6;                        // 0xB6
+	int16 _b8;                        // 0xB8
+	int16 _ba;                        // 0xBA
+	int16 _bc;                        // 0xBC
+	int16 _be;                        // 0xBE
+	int16 _c0;                        // 0xC0
+	byte  _c2;                        // 0xC2
+	byte  _c2hi;                      // 0xC3
+	byte  _c4;                        // 0xC4
+	byte  _c5;                        // 0xC5
+	byte  _c6;                        // 0xC6
 };
 
 static Scratch scratch;
 
-#define local (&scratch)
-#define ss    local->sprite
-#define seq   local->sequence
-#define aa    local->animation
+#define local  (&scratch)
+#define ss     local->sprite
+#define seq    local->sequence
+#define aa     local->animation
 #define aainfo scratch.animation_info
 
 
+static void room_308_anim1() {
+	int16 cur = kernel_anim[ss[4]].frame;
+	if (cur == aainfo[0]._active)
+		return;
+	aainfo[0]._active = cur;
+
+	switch (cur) {
+	case 40:
+		global[player_score] = 0;
+		break;
+	case 56:
+	case 84:
+		digi_initial_volume(60);
+		digi_play_build(308, '_', 1, 3);
+		break;
+	case 65:
+		digi_play_build(308, 'b', 1, 1);
+		scratch._b4 = -1;
+		break;
+	case 70:
+		global[g009] = 0;
+		global_digi_play(9);
+		break;
+	case 88:
+		digi_initial_volume(60);
+		digi_play_build(308, '_', 3, 2);
+		break;
+	case 102:
+		digi_play_build(308, '_', 6, 2);
+		break;
+	case 107:
+		digi_play_build(308, 'b', 2, 1);
+		scratch._b4 = -1;
+		break;
+	case 119:
+		digi_play_build(101, '_', 2, 2);
+		break;
+	case 121:
+		digi_play_build(308, 'e', 1, 1);
+		scratch._b4 = -1;
+		break;
+	default:
+		break;
+	}
+}
+
+static void room_308_anim2() {
+	int16 cur = kernel_anim[ss[5]].frame;
+	if (cur == aainfo[0]._val3)
+		return;
+	aainfo[0]._val3 = cur;
+	if (cur == 6) {
+		digi_play_build(308, 't', 1, 1);
+		scratch._b4 = -1;
+	}
+}
+
+static void room_308_anim3() {
+	int16 cur = kernel_anim[ss[6]].frame;
+	if (cur == aainfo[1]._active)
+		return;
+	aainfo[1]._active = cur;
+	switch (cur) {
+	case 2:
+		digi_play_build(308, 'r', 1, 1);
+		scratch._b4 = -1;
+		break;
+	case 4:
+	case 8:
+	case 13:
+		digi_initial_volume(60);
+		digi_play_build(308, '_', 1, 2);
+		break;
+	default:
+		break;
+	}
+}
+
+static void room_308_anim4() {
+	int16 cur = kernel_anim[ss[7]].frame;
+	if (cur == aainfo[1]._val3)
+		return;
+	aainfo[1]._val3 = cur;
+
+	if (cur == 13) {
+		digi_play_build(308, 't', 2, 1);
+		scratch._b4 = -1;
+		return;
+	}
+
+	bool in_e72 = (cur == 1 || cur == 5) ||
+	              (cur >= 10 && cur <= 58 && cur % 4 == 2);
+	if (in_e72 && (digi_val1 == 0 || (cur != 10 && cur != 14))) {
+		digi_initial_volume(60);
+		digi_play_build(308, '_', 1, 2);
+	}
+}
+
+static void room_308_anim5() {
+	int16 cur = kernel_anim[ss[8]].frame;
+	if (cur == aainfo[2]._active)
+		return;
+	aainfo[2]._active = cur;
+
+	if (cur == 30 || cur == 104) {
+		digi_initial_volume(60);
+		digi_play_build(308, '_', 2, 1);
+		scratch._b4 = -1;
+		return;
+	}
+	if (cur % 4 == 1 && cur <= 117) {
+		if (cur == 1) {
+			digi_play_build(308, 'e', 2, 1);
+			scratch._b4 = -1;
+		}
+		digi_initial_volume(60);
+		digi_play_build(308, '_', 1, 2);
+	}
+}
+
+static void room_308_anim7() {
+	int16 cur = kernel_anim[ss[9]].frame;
+	if (cur == aainfo[2]._val3)
+		return;
+	aainfo[2]._val3 = cur;
+	if (cur == 1 || cur == 5) {
+		digi_initial_volume(60);
+		digi_play_build(308, '_', 1, 2);
+	}
+}
+
+static void room_308_anim8() {
+	int16 cur = kernel_anim[seq[5]].frame;
+	if (cur == aainfo[6]._val4)
+		return;
+	aainfo[6]._val4 = cur;
+
+	if (cur == 57) {
+		player.commands_allowed = -1;
+		dont_frag_the_palette();
+		kernel_abort_animation(seq[5]);
+		aainfo[2]._frame = 0;
+
+		seq[0] = kernel_run_animation(kernel_name('t', 11), 0);
+		aainfo[2]._val4 = -1;
+		aainfo[3]._frame = 0;
+		kernel_synch(KERNEL_ANIM, seq[0], KERNEL_NOW, 0);
+
+		seq[1] = kernel_run_animation(kernel_name('t', 19), 0);
+		aainfo[3]._val3 = -1;
+		aainfo[4]._active = 0;
+		kernel_synch(KERNEL_ANIM, seq[1], KERNEL_NOW, 0);
+
+		seq[2] = kernel_run_animation(kernel_name('t', 13), 0);
+		aainfo[4]._frame = -1;
+		aainfo[4]._val4 = 0;
+		kernel_synch(KERNEL_ANIM, seq[2], KERNEL_NOW, 0);
+
+		seq[3] = kernel_run_animation(kernel_name('t', 18), 0);
+		aainfo[5]._active = -1;
+		aainfo[5]._val3 = 0;
+		kernel_synch(KERNEL_ANIM, seq[3], KERNEL_NOW, 0);
+
+		global[player_score] = -1;
+		return;
+	}
+	if (cur > 57)
+		return;
+	if (cur == 6) {
+		digi_play_build(308, 'e', 3, 1);
+		scratch._b4 = -1;
+		return;
+	}
+	if (cur == 27) {
+		digi_play_build(308, 'b', 3, 1);
+		scratch._b4 = -1;
+	}
+}
+
+static void room_308_anim9() {
+	int16 cur = kernel_anim[seq[0]].frame;
+	if (cur == aainfo[3]._active)
+		return;
+	aainfo[3]._active = cur;
+
+	int16 result = -1;
+	if (cur == 11) {
+		result = 10;
+	} else if (cur < 11) {
+		if (cur == 1) {
+			if (aainfo[5]._val3 == 1) {
+				result = 1;
+				aainfo[5]._val3 = 0;
+			} else {
+				result = 0;
+			}
+		} else if (cur == 5) {
+			result = 0;
+		}
+	}
+
+	if (result >= 0) {
+		kernel_reset_animation(seq[0], result);
+		aainfo[3]._active = result;
+	}
+}
+
+static void room_308_anim10() {
+	int16 cur = kernel_anim[seq[1]].frame;
+	if (cur == aainfo[3]._val4)
+		return;
+	aainfo[3]._val4 = cur;
+
+	int16 result = -1;
+	if (cur == 17) {
+		scratch._be++;
+		if (scratch._be == 4) {
+			kernel_timing_trigger(20, 108);
+			aainfo[4]._active = 0;
+			scratch._c2 = 4;
+			scratch._be++;
+			result = 0;
+		} else if (scratch._be > 4) {
+			result = 0;
+		} else {
+			scratch._c2 = 8;
+			result = 7;
+		}
+	} else if (cur < 17) {
+		if (cur == 1) {
+			if (scratch._c2 == 8)
+				scratch._c2 = 0;
+			int16 active = aainfo[4]._active;
+			if (active == 0) {
+				if (scratch._be == 0) {
+					result = 0;
+				} else if (imath_random(1, 15) == 1 && scratch._c2 == 0) {
+					result = 7;
+					scratch._c2 = 8;
+				} else {
+					result = 0;
+				}
+			} else if (active == 9) {
+				result = 7;
+			}
+		} else if (cur == 6) {
+			result = 5;
+		} else if (cur == 9) {
+			aainfo[5]._val3 = 4;
+		}
+	}
+
+	if (result >= 0) {
+		kernel_reset_animation(seq[1], result);
+		aainfo[3]._val4 = result;
+	}
+}
+
+static void room_308_anim11() {
+	int16 cur = kernel_anim[seq[2]].frame;
+	if (cur == aainfo[4]._val3)
+		return;
+	aainfo[4]._val3 = cur;
+
+	int16 result = -1;
+	if (cur == 6) {
+		result = 5;
+	} else if (cur == 1 || (cur >= 8 && cur <= 10)) {
+		int16 v = aainfo[4]._val4;
+		if (v == 0) {
+			result = 0;
+		} else if (v == 3) {
+			result = imath_random(7, 9);
+		} else if (v == 6) {
+			if (scratch._c2 == 0 || scratch._c2 == 7) {
+				aainfo[4]._val4 = 0;
+				kernel_reset_animation(seq[0], 10);
+				kernel_synch(KERNEL_ANIM, seq[0], KERNEL_ANIM, seq[2]);
+				kernel_reset_animation(seq[3], 14);
+				kernel_synch(KERNEL_ANIM, seq[3], KERNEL_ANIM, seq[2]);
+				result = 5;
+				seq[6] = kernel_run_animation(kernel_name('t', 10), 0);
+				aainfo[7]._active = -1;
+				kernel_synch(KERNEL_ANIM, seq[6], KERNEL_ANIM, seq[2]);
+			} else {
+				result = cur - 1;
+			}
+		} else if (v == 7) {
+			if (scratch._c2 == 0) {
+				result = cur - 1;
+			} else {
+				aainfo[4]._val4 = 0;
+				if (scratch._b8 == 0) {
+					kernel_reset_animation(seq[0], 10);
+					kernel_synch(KERNEL_ANIM, seq[0], KERNEL_ANIM, seq[2]);
+					kernel_reset_animation(seq[3], 14);
+					kernel_synch(KERNEL_ANIM, seq[3], KERNEL_ANIM, seq[2]);
+					result = 5;
+					seq[6] = kernel_run_animation(kernel_name('t', 10), 0);
+					aainfo[7]._active = -1;
+					kernel_synch(KERNEL_ANIM, seq[6], KERNEL_ANIM, seq[2]);
+				} else {
+					result = 5;
+					seq[7] = kernel_run_animation(kernel_name('t', 14), 0);
+					aainfo[7]._val4 = -1;
+					aainfo[8]._frame = 13;
+					kernel_synch(KERNEL_ANIM, seq[7], KERNEL_ANIM, seq[2]);
+					digi_play_build(101, '_', 1, 2);
+				}
+			}
+		}
+	}
+
+	if (result >= 0) {
+		kernel_reset_animation(seq[2], result);
+		aainfo[4]._val3 = result;
+	}
+}
+
+static void room_308_anim12() {
+	int16 cur = kernel_anim[seq[3]].frame;
+	if (cur == aainfo[5]._frame)
+		return;
+	aainfo[5]._frame = cur;
+
+	int16 result = -1;
+	bool do_e0 = false;
+
+	if (cur == 2) {
+		aainfo[5]._val3 = 1;
+	} else if (cur == 7 || cur == 13) {
+		result = 0;
+	} else if (cur == 14) {
+		result = 13;
+	} else if (cur == 1 || (cur >= 16 && cur <= 22)) {
+		if (scratch._c2 == 1 || scratch._c2 == 2 || scratch._c2 == 7 || scratch._c2 == 5)
+			scratch._c2 = 0;
+		if (scratch._ba == 0) {
+			do_e0 = true;
+		} else {
+			scratch._ba = 0;
+			if (global[walker_converse_state] == 0) {
+				aainfo[5]._val3 = (scratch._c0 != 0) ? 11 : 10;
+				do_e0 = true;
+			} else if (scratch._b8 == 0) {
+				aainfo[4]._val4 = 0;
+				scratch._b6 = 1;
+				aainfo[4]._val4 = 7;
+				kernel_reset_animation(seq[0], 10);
+				kernel_synch(KERNEL_ANIM, seq[0], KERNEL_ANIM, seq[2]);
+				kernel_reset_animation(seq[3], 13);
+				kernel_synch(KERNEL_ANIM, seq[3], KERNEL_ANIM, seq[2]);
+				aainfo[5]._frame = 13;
+				kernel_reset_animation(seq[2], 5);
+				seq[6] = kernel_run_animation(kernel_name('t', 10), 0);
+				aainfo[7]._active = -1;
+				kernel_synch(KERNEL_ANIM, seq[6], KERNEL_ANIM, seq[2]);
+				return;
+			} else if (scratch._c0 == 0) {
+				kernel_reset_animation(seq[2], 5);
+				seq[7] = kernel_run_animation(kernel_name('t', 14), 0);
+				aainfo[7]._val4 = -1;
+				aainfo[8]._frame = 13;
+				kernel_synch(KERNEL_ANIM, seq[7], KERNEL_ANIM, seq[2]);
+				aainfo[5]._val3 = 8;
+				do_e0 = true;
+			} else {
+				aainfo[5]._val3 = 11;
+				do_e0 = true;
+			}
+		}
+	}
+
+	if (do_e0) {
+		if (scratch._bc != 0) {
+			aainfo[5]._val3 = 12;
+			scratch._bc = 0;
+		}
+		switch (aainfo[5]._val3) {
+		case 0:
+			if (scratch._c2 == 0) {
+				if (imath_random(1, 15) == 1) {
+					result = 1;
+					scratch._c2 = 7;
+				} else {
+					result = 0;
+					scratch._c2 = 0;
+				}
+			} else {
+				result = aainfo[5]._frame - 1;
+			}
+			break;
+		case 3:
+			result = imath_random(15, 21);
+			scratch._c2 = 4;
+			break;
+		case 4:
+			result = 7;
+			aainfo[5]._val3 = 0;
+			scratch._c2 = 5;
+			break;
+		case 8:
+			result = 0;
+			scratch._c2 = 3;
+			break;
+		case 10:
+			seq[8] = kernel_run_animation(kernel_name('t', 21), 0);
+			aainfo[8]._val3 = -1;
+			kernel_synch(KERNEL_ANIM, seq[8], KERNEL_NOW, 0);
+			kernel_reset_animation(seq[1], 5);
+			kernel_synch(KERNEL_ANIM, seq[1], KERNEL_NOW, 0);
+			kernel_reset_animation(seq[3], 13);
+			kernel_synch(KERNEL_ANIM, seq[3], KERNEL_NOW, 0);
+			scratch._c2 = 2;
+			break;
+		case 11:
+			if (scratch._c0 != 0) {
+				dont_frag_the_palette();
+				kernel_abort_animation(seq[7]);
+				aainfo[7]._val4 = 0;
+			}
+			dont_frag_the_palette();
+			kernel_abort_animation(seq[3]);
+			aainfo[5]._active = 0;
+			seq[9] = kernel_run_animation(kernel_name('t', 20), 0);
+			aainfo[9]._active = -1;
+			kernel_synch(KERNEL_ANIM, seq[9], KERNEL_NOW, 0);
+			kernel_reset_animation(seq[1], 5);
+			kernel_synch(KERNEL_ANIM, seq[1], KERNEL_NOW, 0);
+			scratch._c2 = 9;
+			break;
+		case 12:
+			aa[0] = kernel_run_animation(kernel_name('t', 15), 0);
+			kernel_reset_animation(aa[0], 6);
+			aainfo[9]._val3 = -1;
+			kernel_synch(KERNEL_ANIM, aa[0], KERNEL_NOW, 0);
+			kernel_reset_animation(seq[0], 10);
+			kernel_synch(KERNEL_ANIM, seq[0], KERNEL_NOW, 0);
+			aainfo[5]._val3 = 0;
+			scratch._c2 = 1;
+			break;
+		default:
+			break;
+		}
+	}
+
+	if (result >= 0) {
+		kernel_reset_animation(seq[3], result);
+		aainfo[5]._frame = result;
+	}
+}
+
+static void room_308_anim13() {
+	int16 cur = kernel_anim[seq[4]].frame;
+	if (cur == aainfo[6]._active)
+		return;
+	aainfo[6]._active = cur;
+
+	int16 result = -1;
+	if (cur == 8) {
+		digi_stop(2);
+		digi_play_build(308, '_', 4, 2);
+	} else if (cur == 10) {
+		digi_stop(2);
+		digi_play_build(203, '_', 1, 2);
+	} else if (cur == 11) {
+		scratch._c2hi++;
+		if (scratch._c2hi == 3) {
+			dont_frag_the_palette();
+			kernel_abort_animation(seq[4]);
+			aainfo[5]._val4 = 0;
+			aa[1] = kernel_run_animation(kernel_name('t', 23), 0);
+			kernel_reset_animation(aa[1], 2);
+			scratch._8c = -1;
+			kernel_synch(KERNEL_ANIM, aa[1], KERNEL_NOW, 0);
+		} else {
+			result = 7;
+		}
+	}
+
+	if (result >= 0) {
+		kernel_reset_animation(seq[4], result);
+		aainfo[6]._active = result;
+	}
+}
+
+static void room_308_anim14() {
+	int16 cur = kernel_anim[seq[6]].frame;
+	if (cur == aainfo[7]._frame)
+		return;
+	aainfo[7]._frame = cur;
+
+	if (cur == 6) {
+		digi_play_build(101, '_', 1, 2);
+	} else if (cur == 9) {
+		ss[2] = kernel_seq_stamp(ss[0], 0, -1);
+		kernel_seq_depth(ss[2], 4);
+		kernel_seq_loc(ss[2], 99, 125);
+		kernel_synch(KERNEL_SERIES, ss[2], KERNEL_ANIM, seq[6]);
+		scratch._b8 = -1;
+		global[g072] = -1;
+	} else if (cur == 10) {
+		dont_frag_the_palette();
+		kernel_abort_animation(seq[6]);
+		aainfo[7]._active = 0;
+		if (scratch._b6 == 0) {
+			kernel_reset_animation(seq[1], 7);
+			kernel_synch(KERNEL_ANIM, seq[1], KERNEL_ANIM, seq[2]);
+			kernel_reset_animation(seq[2], 0);
+			kernel_synch(KERNEL_ANIM, seq[2], KERNEL_NOW, 0);
+			kernel_reset_animation(seq[0], 0);
+			kernel_synch(KERNEL_ANIM, seq[0], KERNEL_NOW, 0);
+			kernel_reset_animation(seq[3], 0);
+			kernel_synch(KERNEL_ANIM, seq[3], KERNEL_ANIM, seq[7]);
+		} else if (scratch._b6 == 1) {
+			seq[7] = kernel_run_animation(kernel_name('t', 14), 0);
+			aainfo[7]._val4 = -1;
+			aainfo[8]._frame = 13;
+			kernel_synch(KERNEL_ANIM, seq[7], KERNEL_NOW, 0);
+			kernel_reset_animation(seq[0], 0);
+			kernel_synch(KERNEL_ANIM, seq[0], KERNEL_NOW, 0);
+			kernel_reset_animation(seq[3], 0);
+			kernel_synch(KERNEL_ANIM, seq[3], KERNEL_ANIM, seq[7]);
+		}
+	}
+}
+
+static void room_308_anim15() {
+	int16 cur = kernel_anim[seq[7]].frame;
+	if (cur == aainfo[8]._active)
+		return;
+	aainfo[8]._active = cur;
+
+	int16 result = -1;
+	if (cur == 7) {
+		if (global[walker_converse_state] != 0) {
+			dont_frag_the_palette();
+			kernel_abort_animation(seq[7]);
+			aainfo[7]._val4 = 0;
+			scratch._c0 = 100;
+			dont_frag_the_palette();
+			kernel_abort_animation(seq[3]);
+			aainfo[5]._active = 0;
+			seq[9] = kernel_run_animation(kernel_name('t', 20), 0);
+			aainfo[9]._active = -1;
+			kernel_synch(KERNEL_ANIM, seq[9], KERNEL_NOW, 0);
+			kernel_reset_animation(seq[1], 5);
+			kernel_synch(KERNEL_ANIM, seq[1], KERNEL_NOW, 0);
+			return;
+		}
+		if (scratch._c2 == 6)
+			scratch._c2 = 0;
+		if (scratch._c0 <= 3) {
+			scratch._c2 = 6;
+		} else {
+			if (aainfo[8]._frame != 0 || scratch._c0 == 0 || scratch._c2 != 0 || imath_random(1, 15) != 1) {
+				result = 6;
+			} else {
+				result = 7;
+				scratch._c2 = 6;
+			}
+		}
+	} else if (cur == 10) {
+		aainfo[5]._val3 = 4;
+	} else if (cur == 12) {
+		digi_play_build(308, '_', 4, 2);
+	} else if (cur == 13) {
+		global[g073] = -1;
+		scratch._c0++;
+		if (scratch._c0 == 3) {
+			kernel_timing_trigger(20, 109);
+			aainfo[8]._frame = 0;
+			scratch._c2 = 4;
+			scratch._c0++;
+		}
+		result = 6;
+	}
+
+	if (result >= 0) {
+		kernel_reset_animation(seq[7], result);
+		aainfo[8]._active = result;
+	}
+}
+
+static void room_308_anim16() {
+	int16 cur = kernel_anim[seq[8]].frame;
+	if (cur == aainfo[8]._val4)
+		return;
+	aainfo[8]._val4 = cur;
+
+	if (cur == 1) {
+		digi_initial_volume(60);
+		digi_play_build(308, '_', 3, 2);
+	} else if (cur == 8) {
+		dont_frag_the_palette();
+		kernel_abort_animation(seq[8]);
+		aainfo[8]._val3 = 0;
+		kernel_reset_animation(seq[1], 0);
+		aainfo[4]._active = 0;
+		kernel_synch(KERNEL_ANIM, seq[1], KERNEL_NOW, 0);
+		kernel_reset_animation(seq[3], 0);
+		aainfo[5]._val3 = 0;
+		kernel_synch(KERNEL_ANIM, seq[3], KERNEL_NOW, 0);
+		player.commands_allowed = -1;
+	}
+}
+
+static void room_308_anim17() {
+	int16 cur = kernel_anim[seq[9]].frame;
+	if (cur == aainfo[9]._frame)
+		return;
+	aainfo[9]._frame = cur;
+
+	int16 result = -1;
+	if (cur == 1) {
+		digi_play_build(308, '_', 3, 2);
+	} else if (cur == 13) {
+		kernel_reset_animation(seq[0], 10);
+		kernel_synch(KERNEL_ANIM, seq[0], KERNEL_NOW, 0);
+		seq[4] = kernel_run_animation(kernel_name('t', 22), 0);
+		aainfo[5]._val4 = -1;
+		kernel_synch(KERNEL_ANIM, seq[4], KERNEL_ANIM, seq[9]);
+	} else if (cur == 14) {
+		result = 13;
+	} else if (cur == 23) {
+		dont_frag_the_palette();
+		kernel_abort_animation(seq[9]);
+		aainfo[9]._active = 0;
+		aa[2] = kernel_run_animation(kernel_name('t', 24), 0);
+		kernel_reset_animation(aa[2], 4);
+		scratch._90 = -1;
+		kernel_synch(KERNEL_ANIM, aa[2], KERNEL_NOW, 0);
+	}
+
+	if (result >= 0) {
+		kernel_reset_animation(seq[9], result);
+		aainfo[9]._frame = result;
+	}
+}
+
+static void room_308_anim18() {
+	int16 cur = kernel_anim[aa[0]].frame;
+	if (cur == aainfo[9]._val4)
+		return;
+	aainfo[9]._val4 = cur;
+
+	if (cur == 6) {
+		digi_play_build(203, '_', 4, 2);
+		kernel_timing_trigger(25, 111);
+	} else if (cur == 11) {
+		digi_play_build(308, '_', 4, 2);
+	} else if (cur == 15) {
+		dont_frag_the_palette();
+		kernel_abort_animation(aa[0]);
+		aainfo[9]._val3 = 0;
+		kernel_reset_animation(seq[0], 0);
+		aainfo[3]._frame = 0;
+		kernel_synch(KERNEL_ANIM, seq[0], KERNEL_NOW, 0);
+		player.commands_allowed = -1;
+	}
+}
+
+static void room_308_anim19() {
+	int16 cur = kernel_anim[aa[1]].frame;
+	if (cur == scratch._8e)
+		return;
+	scratch._8e = cur;
+
+	int16 result = -1;
+	if (cur == 2) {
+		digi_play_build(203, '_', 4, 2);
+		kernel_timing_trigger(20, 111);
+	} else if (cur == 9 || cur == 11 || cur == 13) {
+		digi_stop(2);
+		digi_play_build(308, '_', 5, 2);
+	} else if (cur == 14) {
+		scratch._c4++;
+		if (scratch._c4 == 2) {
+			dont_frag_the_palette();
+			kernel_abort_animation(aa[1]);
+			scratch._8c = 0;
+			dont_frag_the_palette();
+			kernel_abort_animation(seq[9]);
+			aainfo[9]._active = 0;
+			aa[2] = kernel_run_animation(kernel_name('t', 24), 0);
+			scratch._90 = -1;
+			kernel_synch(KERNEL_ANIM, aa[2], KERNEL_NOW, 0);
+		} else {
+			result = 8;
+		}
+	}
+
+	if (result >= 0) {
+		kernel_reset_animation(aa[1], result);
+		scratch._8e = result;
+	}
+}
+
+static void room_308_anim20() {
+	int16 cur = kernel_anim[aa[2]].frame;
+	if (cur == scratch._92)
+		return;
+	scratch._92 = cur;
+
+	int16 result = -1;
+	if (cur == 6) {
+		global[g009] = 0;
+		global_digi_play(5);
+		digi_play_build(308, 't', 5, 1);
+		scratch._94 = 3;
+		scratch._b4 = 3;
+	} else if (cur == 7 || cur == 8 || cur == 9) {
+		if (scratch._94 == 3)
+			result = imath_random(6, 8);
+	} else if (cur == 11) {
+		kernel_seq_delete(ss[2]);
+	} else if (cur == 17) {
+		digi_play_build(203, '_', 1, 2);
+	} else if (cur == 20) {
+		digi_stop(2);
+		digi_play_build(322, '_', 1, 2);
+	} else if (cur == 27) {
+		dont_frag_the_palette();
+		kernel_abort_animation(aa[2]);
+		scratch._90 = 0;
+		aa[3] = kernel_run_animation(kernel_name('t', 30), 0);
+		scratch._96 = -1;
+		kernel_synch(KERNEL_ANIM, aa[3], KERNEL_NOW, 0);
+	}
+
+	if (result >= 0) {
+		kernel_reset_animation(aa[2], result);
+		scratch._92 = result;
+	}
+}
+
+static void room_308_anim21() {
+	int16 cur = kernel_anim[aa[3]].frame;
+	if (cur == scratch._98)
+		return;
+	scratch._98 = cur;
+
+	int16 result = -1;
+	if (cur == 3) {
+		digi_play_build(308, 'r', 4, 1);
+		scratch._b4 = 4;
+		scratch._9a = 3;
+	} else if (cur >= 4 && cur <= 9) {
+		if (scratch._9a == 3)
+			result = imath_random(7, 9);
+	} else if (cur == 15) {
+		kernel_abort_animation(aa[3]);
+		scratch._96 = 0;
+		aa[4] = kernel_run_animation(kernel_name('t', 26), 0);
+		scratch._9c = -1;
+		kernel_synch(KERNEL_ANIM, aa[4], KERNEL_NOW, 0);
+		digi_play_build(308, 'b', 4, 1);
+		scratch._b4 = -1;
+	}
+
+	if (result >= 0) {
+		kernel_reset_animation(aa[3], result);
+		scratch._98 = result;
+	}
+}
+
+static void room_308_anim22() {
+	int16 cur = kernel_anim[aa[4]].frame;
+	if (cur == scratch._9e)
+		return;
+	scratch._9e = cur;
+
+	if (cur == 70) {
+		if (global[walker_converse_state] != 0) {
+			global[walker_converse_state] = 0;
+			close_journal(3);
+		}
+		object_set_quality(5, -1, -1);
+		object_set_quality(1, -1, -1);
+		object_set_quality(15, -1, -1);
+		inter_move_object(1, 1);
+		inter_move_object(15, 1);
+		inter_move_object(4, 2);
+		inter_move_object(14, 2);
+		global[player_score] = -1;
+		new_room = 401;
+	}
+}
+
+static void room_308_anim23() {
+	int16 cur = kernel_anim[aa[5]].frame;
+	if (cur == scratch._a4)
+		return;
+	scratch._a4 = cur;
+
+	if (cur == 47) {
+		aa[6] = kernel_run_animation(kernel_name('t', 25), 110);
+		scratch._a8 = -1;
+		scratch._ac = 3;
+		kernel_synch(KERNEL_ANIM, aa[6], KERNEL_NOW, 0);
+	} else if (cur == 48 || cur == 64 || cur == 88 || cur == 108 || cur == 140) {
+		digi_initial_volume(60);
+		digi_play_build(308, '_', 1, 2);
+	} else if (cur == 164) {
+		object_set_quality(5, -1, -1);
+		object_set_quality(1, -1, -1);
+		object_set_quality(15, -1, -1);
+		inter_move_object(4, 2);
+		inter_move_object(14, 2);
+		global[player_score] = -1;
+		if (global[g064] != 0)
+			new_room = 322;
+		else
+			new_room = 307;
+	}
+}
+
+static void room_308_anim24() {
+	int16 cur = kernel_anim[aa[6]].frame;
+	if (cur == scratch._aa)
+		return;
+	scratch._aa = cur;
+
+	int16 result = -1;
+	if (cur == 1 || cur == 28) {
+		digi_initial_volume(60);
+		digi_play_build(308, '_', 3, 1);
+		scratch._b4 = -1;
+	} else if (cur == 20) {
+		digi_play_build(308, 't', 7, 1);
+		scratch._b4 = 5;
+	} else if (cur >= 21 && cur <= 26) {
+		if (scratch._ac == 3)
+			result = imath_random(20, 25);
+	} else if (cur == 29) {
+		global[g009] = 0;
+		global_digi_play(5);
+	}
+
+	if (result >= 0) {
+		kernel_reset_animation(aa[6], result);
+		scratch._aa = result;
+	}
+}
+
+static void room_308_anim25() {
+	int16 cur = kernel_anim[aa[7]].frame;
+	if (cur == scratch._b0)
+		return;
+	scratch._b0 = cur;
+
+	if (cur == 64) {
+		aa[6] = kernel_run_animation(kernel_name('t', 25), 110);
+		scratch._a8 = -1;
+		scratch._ac = 3;
+		kernel_synch(KERNEL_ANIM, aa[6], KERNEL_NOW, 0);
+	} else if (cur == 67 || cur == 92 || cur == 116) {
+		digi_initial_volume(60);
+		digi_play_build(308, '_', 1, 2);
+	} else if (cur == 122) {
+		object_set_quality(5, -1, -1);
+		object_set_quality(1, -1, -1);
+		object_set_quality(15, -1, -1);
+		inter_move_object(1, 1);
+		inter_move_object(15, 1);
+		kernel_timing_trigger(140, 112);
+	}
+}
+
 static void room_308_init() {
+	inter_move_object(4, 1);
+	inter_move_object(14, 1);
+
+	if (!player_has_been_in_room(401)) {
+		if (previous_room == 307 || previous_room == 322) {
+			object_set_quality(5, 0, 0);
+			object_set_quality(1, 0, 0);
+			object_set_quality(15, 0, 0);
+		}
+	}
+
+	if (previous_room != 199 && flags[22] != 3)
+		flags[22]++;
+
+	global[perform_displacements] = 180;
+
+	if (player_has_been_in_room(401)) {
+		kernel_flip_hotspot(189, 0);
+		kernel_flip_hotspot(170, 0);
+	}
+
+	player.walker_visible = 0;
+
+	if (previous_room != KERNEL_RESTORING_GAME) {
+		aa[9] = 0;
+		aainfo[0]._frame  = 0;
+		aainfo[0]._val4   = 0;
+		aainfo[1]._frame  = 0;
+		aainfo[1]._val4   = 0;
+		aainfo[2]._frame  = 0;
+		aainfo[6]._val3   = 0;
+		aainfo[2]._val4   = 0;
+		aainfo[3]._val3   = 0;
+		aainfo[4]._frame  = 0;
+		aainfo[5]._active = 0;
+		aainfo[5]._val4   = 0;
+		aainfo[7]._active = 0;
+		aainfo[7]._val4   = 0;
+		aainfo[8]._val3   = 0;
+		aainfo[9]._active = 0;
+		aainfo[9]._val3   = 0;
+		scratch._8c = 0;
+		scratch._90 = 0;
+		scratch._96 = 0;
+		scratch._9c = 0;
+		scratch._a2 = 0;
+		scratch._a8 = 0;
+		scratch._ae = 0;
+		scratch._ba = 0;
+		scratch._c2 = 0;
+		scratch._c0 = 0;
+	}
+
+	scratch._c6 = 0;
+	scratch._b4 = -1;
+	scratch._b8 = -1;
+	scratch._b2 = 0;
+	scratch._bc = 0;
+	scratch._be = 0;
+	scratch._c4 = 0;
+	scratch._c5 = 0;
+	stop_speech_on_run_animation = true;
+
+	kernel_flip_hotspot(40, 0);
+	kernel_flip_hotspot(46, 0);
+
+	if (!player_has_been_in_room(401)) {
+		ss[0] = kernel_load_series(kernel_name('p', 3), 0);
+		if (global[g072] != 0) {
+			ss[2] = kernel_seq_stamp(ss[0], false, -1);
+			kernel_seq_depth(ss[2], 4);
+			kernel_seq_loc(ss[2], 99, 125);
+			kernel_synch(KERNEL_SERIES, ss[2], KERNEL_ANIM, seq[6]);
+			scratch._c0 = 100;
+			scratch._ba = -1;
+		}
+	}
+
+	if (previous_room == 401) {
+		player.commands_allowed = 0;
+		aa[5] = kernel_run_animation(kernel_name('t', 27), 0);
+		scratch._a2 = -1;
+		return;
+	}
+
+	if (previous_room == 307 || previous_room == 322) {
+		player.commands_allowed = 0;
+		if (player_has_been_in_room(401)) {
+			aa[7] = kernel_run_animation(kernel_name('t', 40), 0);
+			kernel_reset_animation(aa[7], 13);
+			scratch._ae = -1;
+			return;
+		}
+		if (player.been_here_before && player_has(5) && player_has(1) && player_has(15)) {
+			ss[7] = kernel_run_animation(kernel_name('t', 8), 103);
+			aainfo[1]._frame = -1;
+			kernel_synch(KERNEL_ANIM, ss[7], KERNEL_NOW, 0);
+			global[g009] = -1;
+			global_digi_play(15);
+			player.commands_allowed = 0;
+			return;
+		}
+		if (!player.been_here_before) {
+			ss[4] = kernel_run_animation(kernel_name('t', 1), 100);
+			kernel_reset_animation(ss[4], 13);
+			aa[9] = -1;
+			return;
+		}
+		if (!player_has(5) || !player_has(1)) {
+			ss[8] = kernel_run_animation(kernel_name('t', 6), 104);
+			aainfo[1]._val4 = -1;
+			kernel_synch(KERNEL_ANIM, ss[8], KERNEL_NOW, 0);
+			global[g009] = -1;
+			global_digi_play(15);
+			return;
+		}
+		if (player_has(15)) {
+			ss[4] = kernel_run_animation(kernel_name('t', 1), 100);
+			kernel_reset_animation(ss[4], 13);
+			aa[9] = -1;
+			return;
+		}
+		ss[8] = kernel_run_animation(kernel_name('t', 6), 104);
+		aainfo[1]._val4 = -1;
+		kernel_synch(KERNEL_ANIM, ss[8], KERNEL_NOW, 0);
+		global[g009] = -1;
+		global_digi_play(15);
+		return;
+	}
+
+	if (previous_room != KERNEL_RESTORING_GAME &&
+	    !player_has_been_in_room(401) &&
+	    previous_room != 199)
+		return;
+
+	seq[0] = kernel_run_animation(kernel_name('t', 11), 0);
+	aainfo[2]._val4   = -1;
+	aainfo[3]._frame  = 0;
+	kernel_synch(KERNEL_ANIM, seq[0], KERNEL_NOW, 0);
+
+	seq[1] = kernel_run_animation(kernel_name('t', 19), 0);
+	aainfo[3]._val3   = -1;
+	aainfo[4]._active = 0;
+	kernel_synch(KERNEL_ANIM, seq[1], KERNEL_NOW, 0);
+
+	seq[2] = kernel_run_animation(kernel_name('t', 13), 0);
+	aainfo[4]._frame  = -1;
+	aainfo[4]._val4   = 0;
+	kernel_synch(KERNEL_ANIM, seq[2], KERNEL_NOW, 0);
+
+	seq[3] = kernel_run_animation(kernel_name('t', 18), 0);
+	aainfo[5]._active = -1;
+	aainfo[5]._val3   = 0;
+	kernel_synch(KERNEL_ANIM, seq[3], KERNEL_NOW, 0);
+
+	if (global[g073] == 0) {
+		global[g009] = -1;
+		global_digi_play(15);
+		return;
+	}
+
+	kernel_reset_animation(seq[2], 6);
+	seq[7] = kernel_run_animation(kernel_name('t', 14), 0);
+	kernel_reset_animation(seq[7], 7);
+	aainfo[7]._val4  = -1;
+	aainfo[8]._frame = 0;
+	scratch._c2 = 100;
+	global[g009] = -1;
+	global_digi_play(15);
 }
 
 static void room_308_daemon() {
+	switch (kernel.trigger) {
+	case 7:
+		switch (scratch._b4) {
+		case 1:
+			display_interface();
+			aainfo[5]._val3 = 0;
+			scratch._b4 = -1;
+			player.commands_allowed = -1;
+			scratch._c2 = 0;
+			break;
+		case 2:
+			display_interface();
+			aainfo[5]._val3 = 0;
+			aainfo[8]._frame = 0;
+			scratch._b4 = -1;
+			player.commands_allowed = -1;
+			scratch._c2 = 0;
+			break;
+		case 3:
+			scratch._94 = 0;
+			break;
+		case 4:
+			scratch._9a = 0;
+			break;
+		case 5:
+			scratch._ac = 0;
+			break;
+		}
+		break;
+
+	case 100:
+		dont_frag_the_palette();
+		kernel_abort_animation(ss[4]);
+		aa[9] = 0;
+		ss[5] = kernel_run_animation(kernel_name('t', 3), 101);
+		aainfo[0]._frame = -1;
+		kernel_synch(KERNEL_ANIM, ss[5], KERNEL_NOW, 0);
+		break;
+
+	case 101:
+		dont_frag_the_palette();
+		kernel_abort_animation(ss[5]);
+		aainfo[0]._frame = 0;
+		ss[6] = kernel_run_animation(kernel_name('t', 5), 102);
+		aainfo[0]._val4 = -1;
+		kernel_synch(KERNEL_ANIM, ss[6], KERNEL_NOW, 0);
+		global[g009] = -1;
+		global_digi_play(15);
+		break;
+
+	case 102:
+		dont_frag_the_palette();
+		kernel_abort_animation(ss[6]);
+		aainfo[0]._val4 = 0;
+		if (player_has(5) && player_has(1) && player_has(15)) {
+			ss[7] = kernel_run_animation(kernel_name('t', 8), 103);
+			aainfo[1]._frame = -1;
+			kernel_synch(KERNEL_ANIM, ss[7], KERNEL_NOW, 0);
+		} else {
+			global[player_score] = -1;
+			ss[8] = kernel_run_animation(kernel_name('t', 6), 104);
+			aainfo[1]._val4 = -1;
+			kernel_synch(KERNEL_ANIM, ss[8], KERNEL_NOW, 0);
+		}
+		break;
+
+	case 103:
+		dont_frag_the_palette();
+		kernel_abort_animation(ss[7]);
+		aainfo[1]._frame = 0;
+		ss[9] = kernel_run_animation(kernel_name('t', 7), 105);
+		kernel_reset_animation(ss[9], 6);
+		aainfo[2]._frame = -1;
+		kernel_synch(KERNEL_ANIM, ss[9], KERNEL_NOW, 0);
+		break;
+
+	case 104:
+		object_set_quality(5, -1, -1);
+		object_set_quality(1, -1, -1);
+		object_set_quality(15, -1, -1);
+		inter_move_object(4, 2);
+		inter_move_object(14, 2);
+		global[player_score] = -1;
+		if (global[g064] != 0)
+			new_room = 322;
+		else
+			new_room = 307;
+		break;
+
+	case 105:
+		dont_frag_the_palette();
+		kernel_abort_animation(ss[9]);
+		aainfo[2]._frame = 0;
+		seq[5] = kernel_run_animation(kernel_name('t', 9), 0);
+		kernel_reset_animation(seq[5], 6);
+		aainfo[6]._val3 = -1;
+		kernel_synch(KERNEL_ANIM, seq[5], KERNEL_NOW, 0);
+		break;
+
+	case 108:
+		digi_play_build(308, 't', 3, 1);
+		aainfo[5]._val3 = 3;
+		scratch._b4 = 1;
+		break;
+
+	case 109:
+		digi_play_build(308, 't', 4, 1);
+		aainfo[5]._val3 = 3;
+		scratch._b4 = 2;
+		break;
+
+	case 110:
+		kernel_abort_animation(aa[6]);
+		scratch._a8 = 0;
+		break;
+
+	case 111:
+		digi_stop(2);
+		break;
+
+	case 112:
+		inter_move_object(4, 2);
+		inter_move_object(14, 2);
+		global[player_score] = -1;
+		new_room = 401;
+		break;
+
+	default:
+		break;
+	}
+
+	if (aa[9] != 0)              room_308_anim1();
+	if (aainfo[0]._frame != 0)   room_308_anim2();
+	if (aainfo[0]._val4 != 0)    room_308_anim3();
+	if (aainfo[1]._frame != 0)   room_308_anim4();
+	if (aainfo[1]._val4 != 0)    room_308_anim5();
+	if (aainfo[2]._frame != 0)   room_308_anim7();
+	if (aainfo[6]._val3 != 0)    room_308_anim8();
+	if (aainfo[2]._val4 != 0)    room_308_anim9();
+	if (aainfo[3]._val3 != 0)    room_308_anim10();
+	if (aainfo[4]._frame != 0)   room_308_anim11();
+	if (aainfo[5]._active != 0)  room_308_anim12();
+	if (aainfo[5]._val4 != 0)    room_308_anim13();
+	if (aainfo[7]._active != 0)  room_308_anim14();
+	if (aainfo[7]._val4 != 0)    room_308_anim15();
+	if (aainfo[8]._val3 != 0)    room_308_anim16();
+	if (aainfo[9]._active != 0)  room_308_anim17();
+	if (aainfo[9]._val3 != 0)    room_308_anim18();
+	if (scratch._8c != 0)        room_308_anim19();
+	if (scratch._90 != 0)        room_308_anim20();
+	if (scratch._96 != 0)        room_308_anim21();
+	if (scratch._9c != 0)        room_308_anim22();
+	if (scratch._a2 != 0)        room_308_anim23();
+	if (scratch._a8 != 0)        room_308_anim24();
+	if (scratch._ae != 0)        room_308_anim25();
 }
 
 static void room_308_pre_parser() {
+	if (player_parse(13, 40, 0)) {
+		if (global[g064] != 0)
+			player.walk_off_edge_to_room = 322;
+		else
+			player.walk_off_edge_to_room = 307;
+	}
+
+	if (player_parse(13, 46, 0))
+		player.walk_off_edge_to_room = 401;
 }
 
 static void room_308_parser() {
+	int sel;
+
+	if (global[walker_converse_state] != 0) {
+		scratch._bc = -1;
+		player.commands_allowed = 0;
+		aainfo[5]._val3 = 8;
+		goto handled;
+	}
+
+	if (player_parse(189, 0)) {
+		scratch._bc = -1;
+		player.commands_allowed = 0;
+		goto handled;
+	}
+
+	if (player_parse(170, 0)) {
+		scratch._be = -1;
+		player.commands_allowed = 0;
+		goto handled;
+	}
+
+	sel = global[player_selected_object];
+	if (sel < 0)
+		goto done;
+
+	if (sel == 15 || sel == 1) {
+		inv_enable_command = false;
+		object_set_quality(1, 0, -1);
+		object_set_quality(15, 0, -1);
+		object_set_quality(5, 0, -1);
+		scratch._b8 = 1;
+		aainfo[4]._val4 = 7;
+		aainfo[5]._val3 = 8;
+		goto handled;
+	}
+
+	if (sel == 5) {
+		inv_enable_command = false;
+		object_set_quality(5, 0, -1);
+		scratch._b8 = 0;
+		aainfo[4]._val4 = 6;
+		aainfo[5]._val3 = 8;
+		goto handled;
+	}
+
+	goto done;
+handled:
+	player.command_ready = false;
+done:
+	;
 }
 
 void room_308_synchronize(Common::Serializer &s) {
 	for (int16 &v : scratch.sprite)    s.syncAsSint16LE(v);
 	for (int16 &v : scratch.sequence)  s.syncAsSint16LE(v);
 	for (int16 &v : scratch.animation) s.syncAsSint16LE(v);
+	for (auto &ai : scratch.animation_info) ai.synchronize(s);
+	s.syncAsSint16LE(scratch._8c);
+	s.syncAsSint16LE(scratch._90);
+	s.syncAsSint16LE(scratch._96);
+	s.syncAsSint16LE(scratch._9c);
+	s.syncAsSint16LE(scratch._a2);
+	s.syncAsSint16LE(scratch._a8);
+	s.syncAsSint16LE(scratch._ae);
+	s.syncAsSint16LE(scratch._b2);
+	s.syncAsSint16LE(scratch._b4);
+	s.syncAsSint16LE(scratch._b8);
+	s.syncAsSint16LE(scratch._ba);
+	s.syncAsSint16LE(scratch._bc);
+	s.syncAsSint16LE(scratch._be);
+	s.syncAsSint16LE(scratch._c0);
+	s.syncAsByte(scratch._c2);
+	s.syncAsByte(scratch._c2hi);
+	s.syncAsByte(scratch._c4);
+	s.syncAsByte(scratch._c5);
+	s.syncAsByte(scratch._c6);
 }
 
 void room_308_preload() {
-	room_init_code_pointer = room_308_init;
+	room_init_code_pointer       = room_308_init;
 	room_pre_parser_code_pointer = room_308_pre_parser;
-	room_parser_code_pointer = room_308_parser;
-	room_daemon_code_pointer = room_308_daemon;
+	room_parser_code_pointer     = room_308_parser;
+	room_daemon_code_pointer     = room_308_daemon;
 
 	global_section_walker();
 	global_section_interface();


Commit: 5dd35734a4ddc32f88f80e36d14c865a7fb83207
    https://github.com/scummvm/scummvm/commit/5dd35734a4ddc32f88f80e36d14c865a7fb83207
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-06-12T21:31:01+10:00

Commit Message:
MADS: FOREST: Implemented room 321

Changed paths:
    engines/mads/madsv2/forest/rooms/room321.cpp


diff --git a/engines/mads/madsv2/forest/rooms/room321.cpp b/engines/mads/madsv2/forest/rooms/room321.cpp
index e4a6aeee089..2ce5ff1a7b7 100644
--- a/engines/mads/madsv2/forest/rooms/room321.cpp
+++ b/engines/mads/madsv2/forest/rooms/room321.cpp
@@ -1,4 +1,4 @@
-???/* ScummVM - Graphic Adventure Engine
+/* 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
@@ -20,16 +20,21 @@
  */
 
 #include "mads/madsv2/core/conv.h"
+#include "mads/madsv2/core/digi.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/imath.h"
 #include "mads/madsv2/core/inter.h"
 #include "mads/madsv2/core/kernel.h"
+#include "mads/madsv2/core/matte.h"
+#include "mads/madsv2/core/player.h"
 #include "mads/madsv2/core/sound.h"
+#include "mads/madsv2/core/sprite.h"
 #include "mads/madsv2/core/text.h"
 #include "mads/madsv2/forest/mads/inventory.h"
 #include "mads/madsv2/forest/mads/sounds.h"
 #include "mads/madsv2/forest/mads/words.h"
 #include "mads/madsv2/forest/global.h"
+#include "mads/madsv2/forest/journal.h"
 #include "mads/madsv2/forest/rooms/section1.h"
 #include "mads/madsv2/forest/rooms/room321.h"
 
@@ -39,10 +44,26 @@ namespace Forest {
 namespace Rooms {
 
 struct Scratch {
-	int16 sprite[10];       /* Sprite series handles */
-	int16 sequence[10];     /* Sequence handles      */
-	int16 animation[10];     /* Animation handles     */
+	int16 sprite[10];
+	int16 sequence[10];
+	int16 animation[10];
 	AnimationInfo animation_info[10];
+	int16 _8c;
+	int16 _8e;
+	int16 _90;
+	int16 _92;
+	int16 _94;
+	int16 _96;
+	int16 _98;
+	int16 _9a;
+	int16 _9c;
+	int16 _9e;
+	int16 _a0;
+	int16 _a2;
+	int16 _a4;
+	int16 _a6;
+	int16 _a8;
+	int16 _aa;
 };
 
 static Scratch scratch;
@@ -54,29 +75,826 @@ static Scratch scratch;
 #define aainfo scratch.animation_info
 
 
+static void room_321_init1() {
+	global[g131] = 0;
+	global[g141] = 0;
+
+	scratch._9c = kernel_run_animation_disp('r', 6, 0);
+	kernel_position_anim(scratch._9c, 83, 131, 92, 6);
+	scratch._9a = kernel_run_animation_disp('e', 6, 0);
+	kernel_position_anim(scratch._9a, 109, 139, 94, 5);
+
+	aa[4] = kernel_run_animation(kernel_name('L', 5), 104);
+	aainfo[4]._active = -1;
+	aa[6] = kernel_run_animation(kernel_name('L', 4), 104);
+	aainfo[6]._active = -1;
+
+	if (global[g071] != 0) {
+		aa[5] = kernel_run_animation(kernel_name('H', 1), 104);
+		aainfo[5]._active = -1;
+	}
+
+	aa[7] = kernel_run_animation(kernel_name('L', 3), 104);
+	aainfo[7]._active = -1;
+	aa[8] = kernel_run_animation(kernel_name('L', 2), 104);
+	aainfo[8]._active = -1;
+
+	if (previous_room != KERNEL_RESTORING_GAME) {
+		player.x = 138;
+		player.y = 130;
+		player.facing = 6;
+	}
+
+	if (previous_room == KERNEL_RESTORING_GAME || previous_room == 199) {
+		restore_player();
+	} else if (previous_room == 302) {
+		if (scratch._aa != 0) {
+			ss[5] = kernel_load_series(kernel_name('f', 2), 0);
+			seq[5] = kernel_seq_stamp(ss[5], false, -1);
+			kernel_seq_depth(seq[5], 3);
+			kernel_seq_loc(seq[5], 274, 154);
+			kernel_seq_scale(seq[5], 100);
+		}
+		aa[0] = kernel_run_animation("*RM303y1", 100);
+		aainfo[0]._active = -1;
+		scratch._92 = 51;
+		return;
+	}
+
+	global[g131] = -1;
+	global[g141] = -1;
+	kernel_reset_animation(scratch._9a, 2);
+	kernel_reset_animation(scratch._9c, 2);
+	global[g133] = 0;
+	global[g143] = 0;
+	player.commands_allowed = -1;
+	player.walker_visible = -1;
+}
+
 static void room_321_init() {
+	scratch._a4 = 0;
+	scratch._a6 = -1;
+	scratch._aa = 0;
+
+	if (player_has(6))
+		kernel_flip_hotspot(132, 0);
+
+	if (global[g068] == 0) {
+		ss[0] = kernel_load_series(kernel_name('p', 6), 0);
+		seq[0] = kernel_seq_stamp(ss[0], false, -1);
+		kernel_seq_depth(seq[0], 14);
+		kernel_seq_loc(seq[0], 278, 126);
+		kernel_seq_scale(seq[0], 91);
+	} else {
+		kernel_flip_hotspot(125, 0);
+	}
+
+	if (global[g071] == 0) {
+		ss[4] = kernel_load_series(kernel_name('p', 2), 0);
+		seq[4] = kernel_seq_stamp(ss[4], false, -1);
+		kernel_seq_depth(seq[4], 15);
+		kernel_seq_loc(seq[4], 230, 105);
+		kernel_seq_scale(seq[4], 83);
+		kernel_flip_hotspot(177, 0);
+		global_digi_play(13);
+	} else {
+		global_digi_play(3);
+	}
+
+	global[g009] = -1;
+
+	if (previous_room != KERNEL_RESTORING_GAME) {
+		if (previous_room != 199) {
+			player.walker_visible = 0;
+			player.commands_allowed = 0;
+		}
+
+		for (int16 i = 0; i < 10; i++) {
+			aainfo[i]._active = 0;
+			aainfo[i]._frame = 1;
+			aainfo[i]._val3 = 0;
+			aainfo[i]._val4 = 0;
+		}
+
+		if (previous_room != 199 && flags[24] != 3)
+			flags[24]++;
+	}
+
+	if (flags[24] == 1)
+		scratch._aa = -1;
+	room_321_init1();
+}
+
+static void room_321_anim1() {
+	int16 cur = kernel_anim[aa[0]].frame;
+	if (cur != aainfo[0]._frame)
+		aainfo[0]._frame = cur;
+
+	if (global[player_hyperwalked] != -1)
+		return;
+
+	aainfo[0]._frame = scratch._92 - 1;
+	kernel_reset_animation(aa[0], aainfo[0]._frame);
+}
+
+static void room_321_anim2() {
+	int16 cur = kernel_anim[aa[1]].frame;
+	if (cur != aainfo[1]._frame)
+		aainfo[1]._frame = cur;
+
+	if (global[player_hyperwalked] == -1) {
+		if (cur <= 23) {
+			aainfo[1]._frame = 23;
+			kernel_reset_animation(aa[1], 23);
+		} else {
+			aainfo[1]._frame = 44;
+			kernel_reset_animation(aa[1], 44);
+		}
+		kernel_synch(KERNEL_ANIM, aa[1], KERNEL_NOW, 0);
+	} else {
+		if (cur == 45) {
+			aainfo[1]._frame = 44;
+			kernel_reset_animation(aa[1], 44);
+			kernel_synch(KERNEL_ANIM, aa[1], KERNEL_NOW, 0);
+		}
+	}
+}
+
+static void room_321_anim3() {
+	int16 var_2;
+	int16 cur = kernel_anim[aa[2]].frame;
+	if (cur != aainfo[2]._frame) {
+		aainfo[2]._frame = cur;
+
+		if (scratch._a6 == 1) {
+			if (cur == 15) {
+				if (aainfo[2]._val3 == 13) {
+					aainfo[2]._frame = 13;
+					kernel_reset_animation(aa[2], 13);
+				}
+			} else if (cur < 15) {
+				if (cur == 7) {
+					aainfo[2]._val3 = 11;
+					digi_play_build_ii('e', 2, 1);
+					scratch._a8 = 7;
+				} else if (cur == 12) {
+					if (aainfo[2]._val3 == 11) {
+						aainfo[2]._frame = 7;
+						kernel_reset_animation(aa[2], 7);
+					}
+				}
+			}
+		} else if (scratch._a6 == 2) {
+			if (cur == 36) {
+				aainfo[2]._val3 = 11;
+				digi_play_build_ii('e', 4, 1);
+				scratch._a8 = 43;
+			} else if (cur == 41) {
+				if (aainfo[2]._val3 == 11) {
+					aainfo[2]._frame = 36;
+					kernel_reset_animation(aa[2], 36);
+				}
+			} else if (cur == 44) {
+				if (aainfo[2]._val3 == 13) {
+					aainfo[2]._frame = 42;
+					kernel_reset_animation(aa[2], 42);
+				}
+			}
+		}
+	}
+
+	if (kernel.trigger != 7 && kernel.trigger != 28)
+		return;
+
+	var_2 = scratch._a8;
+	if (var_2 == 45) {
+		aainfo[2]._val3 = 14;
+		aainfo[2]._frame = 43;
+		kernel_reset_animation(aa[2], 43);
+		scratch._a8 = -1;
+	} else if ((uint16)var_2 > 45) {
+		return;
+	} else if (var_2 == 9) {
+		aainfo[2]._val3 = 14;
+		aainfo[2]._frame = 13;
+		kernel_reset_animation(aa[2], 13);
+		scratch._a8 = -1;
+	} else if (var_2 > 9) {
+		if (var_2 == 43) {
+			kernel_timing_trigger(30, 28);
+			aainfo[2]._val3 = 13;
+			scratch._a8 = 44;
+		} else if (var_2 == 44) {
+			aainfo[2]._val3 = 11;
+			aainfo[2]._frame = 36;
+			kernel_reset_animation(aa[2], 36);
+			digi_play_build_ii('e', 10, 1);
+			scratch._a8 = 45;
+		}
+	} else if (var_2 == 7) {
+		kernel_timing_trigger(30, 28);
+		aainfo[2]._val3 = 13;
+		scratch._a8 = 8;
+	} else if (var_2 == 8) {
+		aainfo[2]._val3 = 11;
+		aainfo[2]._frame = 7;
+		kernel_reset_animation(aa[2], 7);
+		digi_play_build_ii('e', 10, 1);
+		scratch._a8 = 9;
+	}
+}
+
+static void room_321_anim4() {
+	int16 cur = kernel_anim[aa[3]].frame;
+	if (cur != aainfo[3]._frame) {
+		aainfo[3]._frame = cur;
+
+		if (scratch._a6 == 1) {
+			if (cur == 48) {
+				if (aainfo[3]._val3 == 13) {
+					digi_play_build(101, '_', 1, 2);
+					scratch._a8 = 48;
+				}
+			} else if (cur < 48) {
+				if (cur == 9) {
+					aainfo[3]._val3 = 11;
+					digi_play_build(303, 'e', 2, 1);
+					scratch._a8 = 9;
+				} else if (cur == 15) {
+					if (aainfo[3]._val3 == 11) {
+						aainfo[3]._frame = 9;
+						kernel_reset_animation(aa[3], 9);
+					}
+				} else if (cur == 35) {
+					if (aainfo[3]._val3 == 13) {
+						digi_play_build(303, '_', 1, 2);
+						scratch._a8 = 35;
+					}
+				}
+			}
+		} else if (scratch._a6 == 2) {
+			if (cur == 2) {
+				kernel_seq_delete(seq[0]);
+			} else if (cur == 4) {
+				aainfo[3]._val3 = 11;
+				digi_play_build(303, 'e', 700, 1);
+				scratch._a8 = 40;
+			} else if (cur == 9) {
+				if (aainfo[3]._val3 == 11) {
+					aainfo[3]._frame = 4;
+					kernel_reset_animation(aa[3], 4);
+				}
+			} else if (cur == 53) {
+				digi_play_build(101, '_', 1, 2);
+				scratch._a8 = 530;
+			}
+		}
+	}
+
+	if (kernel.trigger == 7 || kernel.trigger == 28) {
+		int16 a8 = scratch._a8;
+		if (a8 == 9) {
+			aainfo[3]._val3 = 13;
+			aainfo[3]._frame = 16;
+			kernel_reset_animation(aa[3], 16);
+			scratch._a8 = -1;
+			return;
+		} else if (a8 == 40) {
+			aainfo[3]._val3 = 13;
+			aainfo[3]._frame = 10;
+			kernel_reset_animation(aa[3], 10);
+			scratch._a8 = -1;
+			return;
+		}
+	}
+
+	if (kernel.trigger == 8 || kernel.trigger == 28) {
+		int16 a8 = scratch._a8;
+		if (a8 == 35 || a8 == 48 || a8 == 530)
+			scratch._a8 = -1;
+	}
+}
+
+static void room_321_anim5() {
+	int16 var_2 = -1;
+	int16 cur = kernel_anim[aa[4]].frame;
+	if (cur == aainfo[4]._frame)
+		return;
+	aainfo[4]._frame = cur;
+
+	int16 idx = cur - 5;
+	if (idx >= 0 && idx <= 13) {
+		switch (idx) {
+		case 0: case 7: case 13:
+			var_2 = 1;
+			break;
+		case 2:
+			digi_initial_volume(40);
+			digi_play_build(308, '_', 1, 2);
+			break;
+		case 4:
+			digi_play_build(321, '_', 503, 1);
+			scratch._a8 = -1;
+			break;
+		case 9:
+			digi_play_build(321, '_', 504, 2);
+			break;
+		default:
+			break;
+		}
+	}
+
+	if (var_2 >= 0) {
+		aainfo[4]._frame = var_2;
+		kernel_reset_animation(aa[4], var_2);
+	}
+}
+
+static void room_321_anim7() {
+	int16 var_2 = -1;
+	int16 cur = kernel_anim[aa[6]].frame;
+	if (cur == aainfo[6]._frame)
+		return;
+	aainfo[6]._frame = cur;
+
+	if (cur == 9) {
+		if (imath_random(0, 100) > 85)
+			var_2 = 6;
+		else
+			var_2 = 0;
+	}
+
+	if (var_2 >= 0) {
+		aainfo[6]._frame = var_2;
+		kernel_reset_animation(aa[6], var_2);
+	}
+}
+
+static void room_321_anim8() {
+	int16 var_2 = -1;
+	int16 cur = kernel_anim[aa[7]].frame;
+	if (cur == aainfo[7]._frame)
+		return;
+	aainfo[7]._frame = cur;
+
+	switch (cur) {
+	case 1:
+		var_2 = 0;
+		break;
+	case 6: case 18: case 30: case 40: case 53: case 63:
+		digi_play_build(321, '_', 2, 2);
+		break;
+	case 77:
+		digi_stop(2);
+		var_2 = 0;
+		break;
+	default:
+		break;
+	}
+
+	if (var_2 >= 0) {
+		aainfo[7]._frame = var_2;
+		kernel_reset_animation(aa[7], var_2);
+	}
+}
+
+static void room_321_anim6() {
+	int16 var_2 = -1;
+	int16 cur = kernel_anim[aa[5]].frame;
+	if (cur == aainfo[5]._frame)
+		return;
+	aainfo[5]._frame = cur;
+
+	switch (cur) {
+	case 1:
+		var_2 = 0;
+		break;
+	case 2:
+		digi_play_build(305, 'h', 4, 1);
+		scratch._a8 = 501;
+		break;
+	case 12:
+		var_2 = 10;
+		break;
+	case 17:
+		var_2 = 16;
+		break;
+	default:
+		break;
+	}
+
+	if (var_2 >= 0) {
+		aainfo[5]._frame = var_2;
+		kernel_reset_animation(aa[5], var_2);
+	}
+}
+
+static void room_321_anim9() {
+	int16 var_2 = -1;
+	int16 cur = kernel_anim[aa[8]].frame;
+	if (cur == aainfo[8]._frame)
+		return;
+	aainfo[8]._frame = cur;
+
+	switch (cur) {
+	case 1:
+		var_2 = 0;
+		break;
+	case 3: case 15:
+		digi_initial_volume(80);
+		digi_play_build(308, '_', 1, 2);
+		break;
+	case 17:
+		var_2 = 0;
+		player.commands_allowed = -1;
+		break;
+	default:
+		break;
+	}
+
+	if (var_2 >= 0) {
+		aainfo[8]._frame = var_2;
+		kernel_reset_animation(aa[8], var_2);
+	}
+}
+
+static void room_321_anim10() {
+	int16 var_2 = -1;
+	int16 cur = kernel_anim[aa[9]].frame;
+	if (cur == aainfo[9]._frame)
+		return;
+	aainfo[9]._frame = cur;
+
+	if (cur == 5) {
+		digi_play_build(321, 'f', 1, 1);
+		scratch._a8 = 500;
+	} else if (cur == 10) {
+		var_2 = 5;
+	}
+
+	if (var_2 >= 0) {
+		aainfo[9]._frame = var_2;
+		kernel_reset_animation(aa[9], var_2);
+	}
 }
 
 static void room_321_daemon() {
+	switch (kernel.trigger) {
+	case 7:
+		switch (scratch._a8) {
+		case -666:
+			global[walker_converse_state] = 0;
+			close_journal(3);
+			player.commands_allowed = -1;
+			break;
+
+		case 300:
+			dont_frag_the_palette();
+			kernel_abort_animation(scratch._9e);
+			scratch._a8 = -1;
+			scratch._a4 = 0;
+			kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
+			scratch._a6 = 2;
+			player.walker_visible = -1;
+			global[g150] = -1;
+			scratch._90 = 6;
+			break;
+
+		case 400:
+			dont_frag_the_palette();
+			kernel_abort_animation(scratch._9e);
+			scratch._a8 = -1;
+			scratch._a4 = 0;
+			kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
+			scratch._a6 = 1;
+			player.walker_visible = -1;
+			global[g150] = -1;
+			scratch._90 = 6;
+			break;
+
+		case 500:
+			scratch._a8 = -1;
+			aainfo[9]._frame = 10;
+			kernel_reset_animation(aa[9], 10);
+			player.commands_allowed = -1;
+			break;
+
+		case 501:
+			scratch._a8 = -1;
+			aainfo[5]._frame = 0;
+			kernel_reset_animation(aa[5], 0);
+			player.commands_allowed = -1;
+			break;
+
+		default:
+			break;
+		}
+		break;
+
+	case 24: {
+		int16 t = scratch._8c - 3;
+		if (t == 0) {
+			aa[2] = kernel_run_animation(kernel_name('E', 1), 102);
+			aainfo[2]._active = -1;
+			scratch._96 = 3;
+		} else if (t == 2) {
+			if (scratch._a6 == 1) {
+				aa[2] = kernel_run_animation(kernel_name('F', 1), 102);
+			} else if (scratch._a6 == 2) {
+				aa[2] = kernel_run_animation(kernel_name('F', 2), 102);
+			} else {
+				break;
+			}
+			aainfo[2]._active = -1;
+			scratch._96 = 2;
+		} else {
+			break;
+		}
+		kernel_reset_animation(scratch._9a, 0);
+		kernel_synch(KERNEL_ANIM, aa[2], KERNEL_ANIM, scratch._9a);
+		global[g133] = 1;
+		break;
+	}
+
+	case 25:
+		if (scratch._8e - 2 != 0)
+			break;
+		aa[1] = kernel_run_animation(kernel_name('R', 1), 101);
+		aainfo[1]._active = -1;
+		scratch._94 = 1;
+		kernel_reset_animation(scratch._9c, 0);
+		kernel_synch(KERNEL_ANIM, aa[1], KERNEL_ANIM, scratch._9c);
+		global[g143] = 1;
+		break;
+
+	case 26: {
+		int16 t = scratch._90 - 1;
+		if (t == 0) {
+			kernel_reset_animation(scratch._9a, 0);
+			kernel_reset_animation(scratch._9c, 0);
+			global[g133] = 1;
+			global[g143] = 1;
+		} else if (t == 5) {
+			if (scratch._a6 == 1) {
+				room_321_anim1();
+				aa[3] = kernel_run_animation(kernel_name('P', 1), 103);
+			} else if (scratch._a6 == 2) {
+				room_321_anim1();
+				aa[3] = kernel_run_animation(kernel_name('P', 2), 103);
+			} else {
+				break;
+			}
+			aainfo[3]._active = -1;
+			scratch._98 = 3;
+			kernel_reset_animation(scratch._9a, 0);
+			kernel_reset_animation(scratch._9c, 0);
+			kernel_synch(KERNEL_ANIM, aa[3], KERNEL_NOW, 0);
+			global[g133] = 1;
+			global[g143] = 1;
+		}
+		break;
+	}
+
+	case 100:
+		dont_frag_the_palette();
+		kernel_abort_animation(aa[0]);
+		aainfo[0]._active = 0;
+		global[g131] = -1;
+		global[g141] = -1;
+		kernel_reset_animation(scratch._9a, 1);
+		kernel_synch(KERNEL_ANIM, scratch._9a, KERNEL_NOW, 0);
+		kernel_reset_animation(scratch._9c, 1);
+		kernel_synch(KERNEL_ANIM, scratch._9c, KERNEL_NOW, 0);
+		player.walker_visible = -1;
+		global[g133] = 0;
+		global[g143] = 0;
+		kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
+		if (scratch._aa == 0) {
+			player.commands_allowed = -1;
+			break;
+		}
+		kernel_seq_delete(seq[5]);
+		matte_deallocate_series(ss[5], -1);
+		aa[9] = kernel_run_animation(kernel_name('L', 1), 105);
+		aainfo[9]._active = -1;
+		break;
+
+	case 101:
+		dont_frag_the_palette();
+		kernel_abort_animation(aa[scratch._94]);
+		aainfo[scratch._94]._active = 0;
+		kernel_reset_animation(scratch._9c, 1);
+		kernel_synch(KERNEL_ANIM, scratch._9c, KERNEL_NOW, 0);
+		global[g143] = 0;
+		player.commands_allowed = -1;
+		break;
+
+	case 102:
+		dont_frag_the_palette();
+		kernel_abort_animation(aa[scratch._96]);
+		aainfo[scratch._96]._active = 0;
+		kernel_reset_animation(scratch._9a, 1);
+		kernel_synch(KERNEL_ANIM, scratch._9a, KERNEL_NOW, 0);
+		global[g133] = 0;
+		player.commands_allowed = -1;
+		break;
+
+	case 103:
+		if (scratch._a6 == 1) {
+			dont_frag_the_palette();
+			kernel_abort_animation(aa[3]);
+			aainfo[3]._active = 0;
+			kernel_reset_animation(scratch._9c, 1);
+			kernel_synch(KERNEL_ANIM, scratch._9c, KERNEL_NOW, 0);
+			global[g143] = 0;
+			kernel_reset_animation(scratch._9a, 1);
+			kernel_synch(KERNEL_ANIM, scratch._9a, KERNEL_NOW, 0);
+			global[g133] = 0;
+			room_321_anim2();
+			kernel_flip_hotspot(132, 0);
+			inter_move_object(6, 2);
+			global[player_score] = -1;
+			player.commands_allowed = -1;
+		} else if (scratch._a6 == 2) {
+			dont_frag_the_palette();
+			kernel_abort_animation(aa[3]);
+			aainfo[3]._active = 0;
+			kernel_reset_animation(scratch._9c, 1);
+			kernel_synch(KERNEL_ANIM, scratch._9c, KERNEL_NOW, 0);
+			global[g143] = 0;
+			kernel_reset_animation(scratch._9a, 1);
+			kernel_synch(KERNEL_ANIM, scratch._9a, KERNEL_NOW, 0);
+			global[g133] = 0;
+			room_321_anim2();
+			kernel_flip_hotspot(125, 0);
+			if (!player_has(5))
+				inter_move_object(5, 2);
+			global[g068] = -1;
+			global[player_score] = -1;
+			player.commands_allowed = -1;
+		}
+		break;
+
+	case 105:
+		dont_frag_the_palette();
+		kernel_abort_animation(aa[9]);
+		aainfo[9]._active = 0;
+		player.commands_allowed = -1;
+		break;
+
+	default:
+		break;
+	}
+
+
+	global_anim1(6, scratch._9a, global[g131], &global[g132]);
+	global_anim2(6, scratch._9c, global[g141], &global[g142]);
+	if (scratch._a4 != 0)
+		kernel_random_frame(scratch._9e, &global[g151], global[g154]);
+	if (aainfo[0]._active != 0) room_321_anim1();
+	if (aainfo[1]._active != 0) room_321_anim2();
+	if (aainfo[2]._active != 0) room_321_anim3();
+	if (aainfo[3]._active != 0) room_321_anim4();
+	if (aainfo[4]._active != 0) room_321_anim5();
+	if (aainfo[5]._active != 0) room_321_anim6();
+	if (aainfo[6]._active != 0) room_321_anim7();
+	if (aainfo[7]._active != 0) room_321_anim8();
+	if (aainfo[8]._active != 0) room_321_anim9();
+	if (aainfo[9]._active != 0) room_321_anim10();
 }
 
 static void room_321_pre_parser() {
+	if (player_parse(13, 35, 0))
+		player.walk_off_edge_to_room = 302;
 }
 
 static void room_321_parser() {
+	if (global[walker_converse_state] != 0) {
+		player.commands_allowed = 0;
+		digi_play_build_ii('c', 1, 1);
+		scratch._a8 = -666;
+		goto handled;
+	}
+
+	if (global[player_selected_object] >= 0) {
+		goto handled;
+	}
+
+	if (player_parse(114, 77, 0)) {
+		global[player_score] = 0;
+		digi_stop(3);
+		player.commands_allowed = 0;
+		global[g135] = -1;
+		scratch._8c = 5;
+		scratch._a6 = 1;
+		goto handled;
+	}
+
+	if (player_parse(114, 103, 0)) {
+		global[player_score] = 0;
+		digi_stop(3);
+		player.commands_allowed = 0;
+		global[g135] = -1;
+		scratch._8c = 5;
+		scratch._a6 = 2;
+		goto handled;
+	}
+
+	if (player_parse(126, 132, 0)) {
+		global[g154] = 2;
+		global[player_score] = 0;
+		player.commands_allowed = 0;
+		player.walker_visible = 0;
+		scratch._9e = kernel_run_animation_talk('b', 1, 0);
+		kernel_position_anim(scratch._9e, player.x, player.y, player.scale, player.depth);
+		kernel_synch(KERNEL_ANIM, scratch._9e, KERNEL_PLAYER, 0);
+		digi_play_build_ii('b', 3, 1);
+		scratch._a8 = 400;
+		scratch._a4 = -1;
+		goto handled;
+	}
+
+	if (player_parse(126, 125, 0)) {
+		global[g154] = 2;
+		global[player_score] = 0;
+		player.commands_allowed = 0;
+		player.walker_visible = 0;
+		scratch._9e = kernel_run_animation_talk('b', 3, 0);
+		kernel_position_anim(scratch._9e, player.x, player.y, player.scale, player.depth);
+		kernel_synch(KERNEL_ANIM, scratch._9e, KERNEL_PLAYER, 0);
+		digi_play_build_ii('b', 3, 1);
+		scratch._a8 = 300;
+		scratch._a4 = -1;
+		goto handled;
+	}
+
+	if (player_parse(78, 181, 0)) {
+		aainfo[8]._frame = 1;
+		kernel_reset_animation(aa[8], 1);
+		player.commands_allowed = 0;
+		goto handled;
+	}
+
+	if (player_parse(78, 182, 0)) {
+		aainfo[7]._frame = 5;
+		kernel_reset_animation(aa[7], 5);
+		goto handled;
+	}
+
+	if (player_parse(78, 177, 0)) {
+		aainfo[5]._frame = 1;
+		kernel_reset_animation(aa[5], 1);
+		player.commands_allowed = 0;
+		goto handled;
+	}
+
+	if (player_parse(78, 185, 0)) {
+		aainfo[4]._frame = 5;
+		kernel_reset_animation(aa[4], 5);
+		goto handled;
+	}
+
+	if (player_parse(78, 184, 0)) {
+		aainfo[4]._frame = 12;
+		kernel_reset_animation(aa[4], 12);
+		goto handled;
+	}
+
+	goto done;
+handled:
+	player.command_ready = false;
+done:
+	;
 }
 
 void room_321_synchronize(Common::Serializer &s) {
 	for (int16 &v : scratch.sprite)    s.syncAsSint16LE(v);
 	for (int16 &v : scratch.sequence)  s.syncAsSint16LE(v);
 	for (int16 &v : scratch.animation) s.syncAsSint16LE(v);
+	for (auto &ai : scratch.animation_info) ai.synchronize(s);
+	s.syncAsSint16LE(scratch._8c);
+	s.syncAsSint16LE(scratch._8e);
+	s.syncAsSint16LE(scratch._90);
+	s.syncAsSint16LE(scratch._92);
+	s.syncAsSint16LE(scratch._94);
+	s.syncAsSint16LE(scratch._96);
+	s.syncAsSint16LE(scratch._98);
+	s.syncAsSint16LE(scratch._9a);
+	s.syncAsSint16LE(scratch._9c);
+	s.syncAsSint16LE(scratch._9e);
+	s.syncAsSint16LE(scratch._a0);
+	s.syncAsSint16LE(scratch._a2);
+	s.syncAsSint16LE(scratch._a4);
+	s.syncAsSint16LE(scratch._a6);
+	s.syncAsSint16LE(scratch._a8);
+	s.syncAsSint16LE(scratch._aa);
 }
 
 void room_321_preload() {
-	room_init_code_pointer = room_321_init;
+	room_init_code_pointer       = room_321_init;
 	room_pre_parser_code_pointer = room_321_pre_parser;
-	room_parser_code_pointer = room_321_parser;
-	room_daemon_code_pointer = room_321_daemon;
+	room_parser_code_pointer     = room_321_parser;
+	room_daemon_code_pointer     = room_321_daemon;
 
 	global_section_walker();
 	global_section_interface();



From noreply at scummvm.org  Fri Jun 12 11:40:30 2026
From: noreply at scummvm.org (neuromancer)
Date: Fri, 12 Jun 2026 11:40:30 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 c31beb5afe237d8fab6aab6d983c3eed47ce653d
Message-ID: <20260612114030.16D3B7005CC@ron.scummvm.net>

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

Summary:
c31beb5afe SCUMM: RA2: fixed UI in high resolution mode in L12


Commit: c31beb5afe237d8fab6aab6d983c3eed47ce653d
    https://github.com/scummvm/scummvm/commit/c31beb5afe237d8fab6aab6d983c3eed47ce653d
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-12T13:40:14+02:00

Commit Message:
SCUMM: RA2: fixed UI in high resolution mode in L12

Changed paths:
    engines/scumm/insane/rebel2/render.cpp


diff --git a/engines/scumm/insane/rebel2/render.cpp b/engines/scumm/insane/rebel2/render.cpp
index d1219967e57..6f3fa8ed5a7 100644
--- a/engines/scumm/insane/rebel2/render.cpp
+++ b/engines/scumm/insane/rebel2/render.cpp
@@ -3605,7 +3605,16 @@ void InsaneRebel2::renderHandler8Ship(byte *renderBitmap, int pitch, int width,
 	int drawX = displayOffsetX + spriteXOffset;
 	int drawY = displayOffsetY + spriteYOffset;
 
-	renderNutSprite(renderBitmap, pitch, width, height, drawX, drawY, _shipSprite, spriteIndex);
+	const bool renderHiRes = isHiRes() && width >= 640 && height >= 400;
+	const int renderScale = renderHiRes ? 2 : 1;
+	if (renderHiRes) {
+		renderNutSpriteScaledClipped(renderBitmap, pitch, width, height,
+			0, 0, width, height,
+			drawX * renderScale, drawY * renderScale, _shipSprite, spriteIndex,
+			false, renderScale, true);
+	} else {
+		renderNutSprite(renderBitmap, pitch, width, height, drawX, drawY, _shipSprite, spriteIndex);
+	}
 
 	// Shadow sprite (POV004 / DAT_0047e028): drawn at same position as primary ship.
 	// Original FUN_401CCF lines 91-92 uses param_5 & 1 (firing flag) as sprite index.
@@ -3616,16 +3625,23 @@ void InsaneRebel2::renderHandler8Ship(byte *renderBitmap, int pitch, int width,
 			int16 shadowYOff = _shipSprite2->getCharYOffset(shadowIndex);
 			int shadowX = displayOffsetX + shadowXOff;
 			int shadowY = displayOffsetY + shadowYOff;
-			renderNutSprite(renderBitmap, pitch, width, height, shadowX, shadowY, _shipSprite2, shadowIndex);
+			if (renderHiRes) {
+				renderNutSpriteScaledClipped(renderBitmap, pitch, width, height,
+					0, 0, width, height,
+					shadowX * renderScale, shadowY * renderScale, _shipSprite2, shadowIndex,
+					false, renderScale, true);
+			} else {
+				renderNutSprite(renderBitmap, pitch, width, height, shadowX, shadowY, _shipSprite2, shadowIndex);
+			}
 		}
 	}
 
 	int sprW = _shipSprite->getCharWidth(spriteIndex);
 	int sprH = _shipSprite->getCharHeight(spriteIndex);
-	debugC(DEBUG_INSANE, "Handler8: Ship at (%d,%d) raw(%d,%d) offset(%d,%d) nutOff(%d,%d) size(%d,%d) bottom=%d view(%d,%d) sprite=%d/%d",
+	debugC(DEBUG_INSANE, "Handler8: Ship at (%d,%d) raw(%d,%d) offset(%d,%d) nutOff(%d,%d) size(%d,%d) bottom=%d view(%d,%d) sprite=%d/%d scale=%d",
 		drawX, drawY, _shipPosX, _shipPosY, displayOffsetX, displayOffsetY,
 		spriteXOffset, spriteYOffset, sprW, sprH, drawY + sprH,
-		_viewX, _viewY, spriteIndex, numSprites);
+		_viewX, _viewY, spriteIndex, numSprites, renderScale);
 }
 
 // renderVehicleShotImpacts -- Handler 8 shot impact sprites (FUN_402DA8).



From noreply at scummvm.org  Fri Jun 12 12:05:56 2026
From: noreply at scummvm.org (kelmer44)
Date: Fri, 12 Jun 2026 12:05:56 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm-web master ->
 327258bf6415e3ff1202fbe681708a3a219a7f41
Message-ID: <20260612120556.5B5457005CC@ron.scummvm.net>

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

Summary:
327258bf64 SCREENSHOTS: Add Alfred Pelrock screenshots


Commit: 327258bf6415e3ff1202fbe681708a3a219a7f41
    https://github.com/scummvm/scummvm-web/commit/327258bf6415e3ff1202fbe681708a3a219a7f41
Author: gsanmartin (gabriel.sanmartin at dna.inc)
Date: 2026-06-12T14:05:46+02:00

Commit Message:
SCREENSHOTS: Add Alfred Pelrock screenshots

Changed paths:
  A public_html/data/screenshots/pelrock/pelrock_dos_es_1_1.jpg
  A public_html/data/screenshots/pelrock/pelrock_dos_es_1_1_full.png
  A public_html/data/screenshots/pelrock/pelrock_dos_es_1_2.jpg
  A public_html/data/screenshots/pelrock/pelrock_dos_es_1_2_full.png
  A public_html/data/screenshots/pelrock/pelrock_dos_es_1_3.jpg
  A public_html/data/screenshots/pelrock/pelrock_dos_es_1_3_full.png
  A public_html/data/screenshots/pelrock/pelrock_dos_es_1_4.jpg
  A public_html/data/screenshots/pelrock/pelrock_dos_es_1_4_full.png


diff --git a/public_html/data/screenshots/pelrock/pelrock_dos_es_1_1.jpg b/public_html/data/screenshots/pelrock/pelrock_dos_es_1_1.jpg
new file mode 100644
index 00000000..381f8090
Binary files /dev/null and b/public_html/data/screenshots/pelrock/pelrock_dos_es_1_1.jpg differ
diff --git a/public_html/data/screenshots/pelrock/pelrock_dos_es_1_1_full.png b/public_html/data/screenshots/pelrock/pelrock_dos_es_1_1_full.png
new file mode 100644
index 00000000..644a24dd
Binary files /dev/null and b/public_html/data/screenshots/pelrock/pelrock_dos_es_1_1_full.png differ
diff --git a/public_html/data/screenshots/pelrock/pelrock_dos_es_1_2.jpg b/public_html/data/screenshots/pelrock/pelrock_dos_es_1_2.jpg
new file mode 100644
index 00000000..215fa474
Binary files /dev/null and b/public_html/data/screenshots/pelrock/pelrock_dos_es_1_2.jpg differ
diff --git a/public_html/data/screenshots/pelrock/pelrock_dos_es_1_2_full.png b/public_html/data/screenshots/pelrock/pelrock_dos_es_1_2_full.png
new file mode 100644
index 00000000..5690f16b
Binary files /dev/null and b/public_html/data/screenshots/pelrock/pelrock_dos_es_1_2_full.png differ
diff --git a/public_html/data/screenshots/pelrock/pelrock_dos_es_1_3.jpg b/public_html/data/screenshots/pelrock/pelrock_dos_es_1_3.jpg
new file mode 100644
index 00000000..982a13ce
Binary files /dev/null and b/public_html/data/screenshots/pelrock/pelrock_dos_es_1_3.jpg differ
diff --git a/public_html/data/screenshots/pelrock/pelrock_dos_es_1_3_full.png b/public_html/data/screenshots/pelrock/pelrock_dos_es_1_3_full.png
new file mode 100644
index 00000000..222ac09e
Binary files /dev/null and b/public_html/data/screenshots/pelrock/pelrock_dos_es_1_3_full.png differ
diff --git a/public_html/data/screenshots/pelrock/pelrock_dos_es_1_4.jpg b/public_html/data/screenshots/pelrock/pelrock_dos_es_1_4.jpg
new file mode 100644
index 00000000..d7d47eab
Binary files /dev/null and b/public_html/data/screenshots/pelrock/pelrock_dos_es_1_4.jpg differ
diff --git a/public_html/data/screenshots/pelrock/pelrock_dos_es_1_4_full.png b/public_html/data/screenshots/pelrock/pelrock_dos_es_1_4_full.png
new file mode 100644
index 00000000..c735b7a0
Binary files /dev/null and b/public_html/data/screenshots/pelrock/pelrock_dos_es_1_4_full.png differ



From noreply at scummvm.org  Fri Jun 12 13:22:44 2026
From: noreply at scummvm.org (mduggan)
Date: Fri, 12 Jun 2026 13:22:44 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm-web master ->
 3eac4a31cbab8755823e53697a4d49b432f37363
Message-ID: <20260612132244.83C7B7005CC@ron.scummvm.net>

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

Summary:
3eac4a31cb SCREENSHOTS: Redo Nocotropolis screenshots in original resolution


Commit: 3eac4a31cbab8755823e53697a4d49b432f37363
    https://github.com/scummvm/scummvm-web/commit/3eac4a31cbab8755823e53697a4d49b432f37363
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2026-06-12T23:22:29+10:00

Commit Message:
SCREENSHOTS: Redo Nocotropolis screenshots in original resolution

Changed paths:
    public_html/data/screenshots/access/noctropolis/noctropolis_win_en_1_1.jpg
    public_html/data/screenshots/access/noctropolis/noctropolis_win_en_1_1_full.png
    public_html/data/screenshots/access/noctropolis/noctropolis_win_en_1_2.jpg
    public_html/data/screenshots/access/noctropolis/noctropolis_win_en_1_2_full.png
    public_html/data/screenshots/access/noctropolis/noctropolis_win_en_1_3.jpg
    public_html/data/screenshots/access/noctropolis/noctropolis_win_en_1_3_full.png
    public_html/data/screenshots/access/noctropolis/noctropolis_win_en_1_4.jpg
    public_html/data/screenshots/access/noctropolis/noctropolis_win_en_1_4_full.png


diff --git a/public_html/data/screenshots/access/noctropolis/noctropolis_win_en_1_1.jpg b/public_html/data/screenshots/access/noctropolis/noctropolis_win_en_1_1.jpg
index 906a8479..c00abd34 100644
Binary files a/public_html/data/screenshots/access/noctropolis/noctropolis_win_en_1_1.jpg and b/public_html/data/screenshots/access/noctropolis/noctropolis_win_en_1_1.jpg differ
diff --git a/public_html/data/screenshots/access/noctropolis/noctropolis_win_en_1_1_full.png b/public_html/data/screenshots/access/noctropolis/noctropolis_win_en_1_1_full.png
index e1853beb..2bf061b5 100644
Binary files a/public_html/data/screenshots/access/noctropolis/noctropolis_win_en_1_1_full.png and b/public_html/data/screenshots/access/noctropolis/noctropolis_win_en_1_1_full.png differ
diff --git a/public_html/data/screenshots/access/noctropolis/noctropolis_win_en_1_2.jpg b/public_html/data/screenshots/access/noctropolis/noctropolis_win_en_1_2.jpg
index 41f83682..5e235d2c 100644
Binary files a/public_html/data/screenshots/access/noctropolis/noctropolis_win_en_1_2.jpg and b/public_html/data/screenshots/access/noctropolis/noctropolis_win_en_1_2.jpg differ
diff --git a/public_html/data/screenshots/access/noctropolis/noctropolis_win_en_1_2_full.png b/public_html/data/screenshots/access/noctropolis/noctropolis_win_en_1_2_full.png
index 3ea73704..2d53b52d 100644
Binary files a/public_html/data/screenshots/access/noctropolis/noctropolis_win_en_1_2_full.png and b/public_html/data/screenshots/access/noctropolis/noctropolis_win_en_1_2_full.png differ
diff --git a/public_html/data/screenshots/access/noctropolis/noctropolis_win_en_1_3.jpg b/public_html/data/screenshots/access/noctropolis/noctropolis_win_en_1_3.jpg
index 57fac706..ac22978a 100644
Binary files a/public_html/data/screenshots/access/noctropolis/noctropolis_win_en_1_3.jpg and b/public_html/data/screenshots/access/noctropolis/noctropolis_win_en_1_3.jpg differ
diff --git a/public_html/data/screenshots/access/noctropolis/noctropolis_win_en_1_3_full.png b/public_html/data/screenshots/access/noctropolis/noctropolis_win_en_1_3_full.png
index 4b43887e..9513eea5 100644
Binary files a/public_html/data/screenshots/access/noctropolis/noctropolis_win_en_1_3_full.png and b/public_html/data/screenshots/access/noctropolis/noctropolis_win_en_1_3_full.png differ
diff --git a/public_html/data/screenshots/access/noctropolis/noctropolis_win_en_1_4.jpg b/public_html/data/screenshots/access/noctropolis/noctropolis_win_en_1_4.jpg
index 42e4f236..a0177568 100644
Binary files a/public_html/data/screenshots/access/noctropolis/noctropolis_win_en_1_4.jpg and b/public_html/data/screenshots/access/noctropolis/noctropolis_win_en_1_4.jpg differ
diff --git a/public_html/data/screenshots/access/noctropolis/noctropolis_win_en_1_4_full.png b/public_html/data/screenshots/access/noctropolis/noctropolis_win_en_1_4_full.png
index 522e9bf3..a21cc9a3 100644
Binary files a/public_html/data/screenshots/access/noctropolis/noctropolis_win_en_1_4_full.png and b/public_html/data/screenshots/access/noctropolis/noctropolis_win_en_1_4_full.png differ



From noreply at scummvm.org  Fri Jun 12 13:53:10 2026
From: noreply at scummvm.org (lotharsm)
Date: Fri, 12 Jun 2026 13:53:10 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm-web master ->
 45a901c6659db1ce731cc657ba7b1a5c106ce96a
Message-ID: <20260612135310.89DAC7005CC@ron.scummvm.net>

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

Summary:
45a901c665 DATA: Update data files


Commit: 45a901c6659db1ce731cc657ba7b1a5c106ce96a
    https://github.com/scummvm/scummvm-web/commit/45a901c6659db1ce731cc657ba7b1a5c106ce96a
Author: sev- (sev at scummvm.org)
Date: 2026-06-12T13:53:05Z

Commit Message:
DATA: Update data files

Changed paths:
    data/en/screenshots.yaml


diff --git a/data/en/screenshots.yaml b/data/en/screenshots.yaml
index 8e5dfcee..12bec326 100644
--- a/data/en/screenshots.yaml
+++ b/data/en/screenshots.yaml
@@ -11,6 +11,12 @@
     platform_id: dos
     language: en
     variant_id: '1'
+-
+    id: 'access:noctropolis'
+    variant: ''
+    platform_id: win
+    language: en
+    variant_id: '1'
 -
     id: 'adl:hires0'
     variant: ''



From noreply at scummvm.org  Fri Jun 12 16:03:11 2026
From: noreply at scummvm.org (neuromancer)
Date: Fri, 12 Jun 2026 16:03:11 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 19ac3bfcf2b743cd7533c42f04df8bc8a3e2c8dc
Message-ID: <20260612160311.251AB7005CC@ron.scummvm.net>

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:
0c1eb71a69 SCUMM: RA2: do not preserve explosion animations after game over/restart
19ac3bfcf2 FREESCAPE: correctly parsed/executed SWAPJET in Amiga/Atari


Commit: 0c1eb71a699699498092303788bc8c48d773089c
    https://github.com/scummvm/scummvm/commit/0c1eb71a699699498092303788bc8c48d773089c
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-12T14:18:20+02:00

Commit Message:
SCUMM: RA2: do not preserve explosion animations after game over/restart

Changed paths:
    engines/scumm/insane/rebel2/rebel.h
    engines/scumm/insane/rebel2/runlevels.cpp


diff --git a/engines/scumm/insane/rebel2/rebel.h b/engines/scumm/insane/rebel2/rebel.h
index 24810696bf1..ee2924471fa 100644
--- a/engines/scumm/insane/rebel2/rebel.h
+++ b/engines/scumm/insane/rebel2/rebel.h
@@ -435,6 +435,7 @@ public:
 	void resetLevelPhaseState(bool clearEnemies);
 	void clearEmbeddedHudFrames();
 	void resetLevelWaveState();
+	void resetExplosions();
 	void resetHandler7FlightState();
 
 	// Random number helper (emulates FUN_004233a0)
diff --git a/engines/scumm/insane/rebel2/runlevels.cpp b/engines/scumm/insane/rebel2/runlevels.cpp
index b0e9a677f18..09a450264a7 100644
--- a/engines/scumm/insane/rebel2/runlevels.cpp
+++ b/engines/scumm/insane/rebel2/runlevels.cpp
@@ -46,6 +46,7 @@ int InsaneRebel2::runLevel1() {
 		_playerShield = 255;
 		_playerDamage = 0;
 		_deathFrame = 0;
+		resetExplosions();
 
 		// Reset bit table before gameplay starts - FUN_00423880 calls FUN_00423a00(0)
 		// This ensures all enemies are visible (not skipped by SKIP chunks)
@@ -280,6 +281,7 @@ void InsaneRebel2::resetLevelAttemptState(int initialPhase) {
 	_rebelAutopilot = 0;
 	_rebelDamageLevel = 0;
 	_rebelControlMode = 0;
+	resetExplosions();
 
 	_enemies.clear();
 	for (int i = 0; i < 512; i++) {
@@ -330,6 +332,18 @@ void InsaneRebel2::resetLevelWaveState() {
 	_rebelWaveState = 0;
 }
 
+void InsaneRebel2::resetExplosions() {
+	for (uint i = 0; i < ARRAYSIZE(_explosions); ++i) {
+		_explosions[i].active = false;
+		_explosions[i].counter = 0;
+		_explosions[i].x = 0;
+		_explosions[i].y = 0;
+		_explosions[i].width = 0;
+		_explosions[i].height = 0;
+		_explosions[i].scale = 0;
+	}
+}
+
 void InsaneRebel2::resetHandler7FlightState() {
 	_hitCooldown = 0;
 	_flyShipScreenX = 0xd4;
@@ -674,6 +688,7 @@ int InsaneRebel2::runLevel3() {
 		_playerShield = 255;
 		_playerDamage = 0;
 		_currentPhase = 1;
+		resetExplosions();
 
 		clearEmbeddedHudFrames();
 
@@ -728,6 +743,7 @@ int InsaneRebel2::runLevel3() {
 		_playerShield = 255;
 		_playerDamage = 0;
 		_playerScore = phase1Score;
+		resetExplosions();
 
 		clearEmbeddedHudFrames();
 
@@ -791,6 +807,7 @@ int InsaneRebel2::runLevel4() {
 		_playerShield = 255;
 		_playerDamage = 0;
 		_currentPhase = 1;
+		resetExplosions();
 
 		// Reset bit table before gameplay starts
 		clearBit(0);
@@ -845,6 +862,7 @@ int InsaneRebel2::runLevel5() {
 		_playerShield = 255;
 		_playerDamage = 0;
 		_currentPhase = 1;
+		resetExplosions();
 
 		// Reset bit table before gameplay starts
 		clearBit(0);
@@ -910,6 +928,7 @@ int InsaneRebel2::runLevel6() {
 	while (!_vm->shouldQuit()) {
 		// FUN_00407d10 ??? reset shot/hit counters
 		clearBit(0);
+		resetExplosions();
 
 		// DAT_0047ab9c = 0xffffffff ??? init phase state
 		_rebelPhaseState = 0xffffffff;
@@ -958,6 +977,7 @@ int InsaneRebel2::runLevel6() {
 			_currentPhase = 2;
 			_playerScore = phase1Score;
 			clearBit(0);  // FUN_00407d10
+			resetExplosions();
 
 			debugC(DEBUG_INSANE, "Level 6 Phase 2");
 			if (!playLevelSegment("LEV06/06PLAY2.SAN", 0x28))
@@ -1023,6 +1043,7 @@ int InsaneRebel2::runLevel7() {
 		_playerDamage = 0;
 		_deathFrame = 0;
 		reachedFork = false;
+		resetExplosions();
 
 		clearBit(0);
 
@@ -1088,6 +1109,7 @@ int InsaneRebel2::runLevel8() {
 		_playerShield = 255;
 		_playerDamage = 0;
 		_deathFrame = 0;
+		resetExplosions();
 
 		clearBit(0);
 
@@ -1147,6 +1169,7 @@ int InsaneRebel2::runLevel9() {
 		_playerShield = 255;
 		_playerDamage = 0;
 		_deathFrame = 0;
+		resetExplosions();
 
 		clearBit(0);
 
@@ -1215,6 +1238,7 @@ int InsaneRebel2::runLevel10() {
 		_playerShield = 255;
 		_playerDamage = 0;
 		_deathFrame = 0;
+		resetExplosions();
 
 		clearBit(0);
 
@@ -1494,9 +1518,7 @@ int InsaneRebel2::runLevel11() {
 
 		// ----- PHASE 3 SECOND HALF: P3/11P03_X (G-L) -----
 		// Reset shots/explosions (FUN_0041ca6a equivalent)
-		for (int i = 0; i < 5; i++) {
-			_explosions[i].active = false;
-		}
+		resetExplosions();
 		_enemies.clear();
 
 		// Preserve only bits 1-3 of phase state (original: DAT_0047ab9c &= 0xe)
@@ -1885,6 +1907,7 @@ int InsaneRebel2::runLevel13() {
 		_playerShield = 255;
 		_playerDamage = 0;
 		_deathFrame = 0;
+		resetExplosions();
 
 		clearBit(0);
 
@@ -1954,6 +1977,7 @@ int InsaneRebel2::runLevel14() {
 		_playerShield = 255;
 		_playerDamage = 0;
 		_deathFrame = 0;
+		resetExplosions();
 
 		clearBit(0);
 
@@ -2021,6 +2045,7 @@ int InsaneRebel2::runLevel15() {
 		_playerShield = 255;
 		_playerDamage = 0;
 		_deathFrame = 0;
+		resetExplosions();
 
 		clearBit(0);
 


Commit: 19ac3bfcf2b743cd7533c42f04df8bc8a3e2c8dc
    https://github.com/scummvm/scummvm/commit/19ac3bfcf2b743cd7533c42f04df8bc8a3e2c8dc
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-12T18:02:53+02:00

Commit Message:
FREESCAPE: correctly parsed/executed SWAPJET in Amiga/Atari

Changed paths:
    engines/freescape/language/8bitDetokeniser.cpp
    engines/freescape/language/instruction.cpp


diff --git a/engines/freescape/language/8bitDetokeniser.cpp b/engines/freescape/language/8bitDetokeniser.cpp
index e78455e9a28..80a2f653346 100644
--- a/engines/freescape/language/8bitDetokeniser.cpp
+++ b/engines/freescape/language/8bitDetokeniser.cpp
@@ -364,7 +364,10 @@ Common::String detokenise8bitCondition(Common::Array<uint16> &tokenisedCondition
 			currentInstruction = FCLInstruction(Token::SWAPJET);
 			conditionalInstructions->push_back(currentInstruction);
 			currentInstruction = FCLInstruction(Token::UNKNOWN);
-			bytePointer++;
+			// The 16-bit Amiga/Atari token stream stores SWAPJET without a
+			// padding argument. The 8-bit data has one unused byte here.
+			if (!isAmigaAtari)
+				bytePointer++;
 			numberOfArguments = 0;
 			break;
 
diff --git a/engines/freescape/language/instruction.cpp b/engines/freescape/language/instruction.cpp
index ccff0398fba..4ecf47b01e9 100644
--- a/engines/freescape/language/instruction.cpp
+++ b/engines/freescape/language/instruction.cpp
@@ -860,7 +860,7 @@ void FreescapeEngine::executeSwapJet(FCLInstruction &instruction) {
 
 	if (_flyMode) {
 		debugC(1, kFreescapeDebugCode, "Swaping to ship mode");
-		if (areaID == 27) {
+		if (areaID == 27 && !(isAmiga() || isAtariST())) {
 			traverseEntrance(26);
 			_lastPosition = _position;
 		}
@@ -877,7 +877,7 @@ void FreescapeEngine::executeSwapJet(FCLInstruction &instruction) {
 	} else {
 		debugC(1, kFreescapeDebugCode, "Swaping to tank mode");
 		_playerHeightNumber = 0;
-		if (areaID == 27) {
+		if (areaID == 27 && !(isAmiga() || isAtariST())) {
 			traverseEntrance(27);
 			_lastPosition = _position;
 		}



From noreply at scummvm.org  Fri Jun 12 17:37:52 2026
From: noreply at scummvm.org (athrxx)
Date: Fri, 12 Jun 2026 17:37:52 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 321b113ae750279843190f2066c259d7e0cff851
Message-ID: <20260612173752.6C1707005CC@ron.scummvm.net>

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

Summary:
321b113ae7 SCUMM: (DOTT) - fix text speed setting corruption


Commit: 321b113ae750279843190f2066c259d7e0cff851
    https://github.com/scummvm/scummvm/commit/321b113ae750279843190f2066c259d7e0cff851
Author: athrxx (athrxx at scummvm.org)
Date: 2026-06-12T19:36:49+02:00

Commit Message:
SCUMM: (DOTT) - fix text speed setting corruption

For whatever reason, we let scripted VAR_CHARINC values
overwrite the "talkspeed" setting. I am not changing that,
since I do not know why we do that and if it is needed.
But at least, I restrict it to valid values. DOTT scripts set
VAR_CHARINC to 255 right before the final credits, which
would result in a "talkspeed" setting of -246.

Changed paths:
    engines/scumm/script.cpp


diff --git a/engines/scumm/script.cpp b/engines/scumm/script.cpp
index 31e0d846480..ff91a786ec1 100644
--- a/engines/scumm/script.cpp
+++ b/engines/scumm/script.cpp
@@ -740,8 +740,16 @@ void ScummEngine::writeVar(uint var, int value) {
 			// value is likely to be bogus. See also bug #4008.
 			if (_currentRoom == 0 && ConfMan.hasKey("talkspeed", _targetName)) {
 				value = 9 - getTalkSpeed();
-			} else {
-				// Save the new talkspeed value to ConfMan
+			} else if (value >= 0 && value <= 9) {
+				// Save the new talkspeed value to ConfMan.
+				// UPDATE: Only do this if the value is in valid range
+				// (e. g. DOTT, right before showing the final credits,
+				// will repeatedly set a value of 255, which would get
+				// stored as -246 and thus corrupt the text display in
+				// the next game session). I don't know why we do this
+				// at all, in my understanding, the script should not
+				// really change the user setting? Also, readVar()
+				// does not have an equivalent counterpart for this.
 				setTalkSpeed(9 - value);
 			}
 		}



From noreply at scummvm.org  Fri Jun 12 18:55:24 2026
From: noreply at scummvm.org (AndywinXp)
Date: Fri, 12 Jun 2026 18:55:24 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm-web master ->
 f91fe7d2da5598beeef13f3351ca445999449a36
Message-ID: <20260612185524.C4B177005CC@ron.scummvm.net>

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

Summary:
f91fe7d2da SCREENSHOTS: Add screenshots/thumbnails for Hanna-Barbera's Cartoon Carnival


Commit: f91fe7d2da5598beeef13f3351ca445999449a36
    https://github.com/scummvm/scummvm-web/commit/f91fe7d2da5598beeef13f3351ca445999449a36
Author: AndywinXp (andywinxp at gmail.com)
Date: 2026-06-12T20:47:39+02:00

Commit Message:
SCREENSHOTS: Add screenshots/thumbnails for Hanna-Barbera's Cartoon Carnival

Changed paths:
  A public_html/data/screenshots/bolt/carnival/carnival_win_en_1_1.jpg
  A public_html/data/screenshots/bolt/carnival/carnival_win_en_1_1_full.png
  A public_html/data/screenshots/bolt/carnival/carnival_win_en_1_2.jpg
  A public_html/data/screenshots/bolt/carnival/carnival_win_en_1_2_full.png
  A public_html/data/screenshots/bolt/carnival/carnival_win_en_1_3.jpg
  A public_html/data/screenshots/bolt/carnival/carnival_win_en_1_3_full.png
  A public_html/data/screenshots/bolt/carnival/carnival_win_en_1_4.jpg
  A public_html/data/screenshots/bolt/carnival/carnival_win_en_1_4_full.png
  A public_html/data/screenshots/bolt/carnival/carnival_win_en_1_5.jpg
  A public_html/data/screenshots/bolt/carnival/carnival_win_en_1_5_full.png
  A public_html/data/screenshots/bolt/carnival/carnival_win_en_1_6.jpg
  A public_html/data/screenshots/bolt/carnival/carnival_win_en_1_6_full.png
  A public_html/data/screenshots/bolt/carnival/carnival_win_en_1_7.jpg
  A public_html/data/screenshots/bolt/carnival/carnival_win_en_1_7_full.png


diff --git a/public_html/data/screenshots/bolt/carnival/carnival_win_en_1_1.jpg b/public_html/data/screenshots/bolt/carnival/carnival_win_en_1_1.jpg
new file mode 100644
index 00000000..1b3d80a9
Binary files /dev/null and b/public_html/data/screenshots/bolt/carnival/carnival_win_en_1_1.jpg differ
diff --git a/public_html/data/screenshots/bolt/carnival/carnival_win_en_1_1_full.png b/public_html/data/screenshots/bolt/carnival/carnival_win_en_1_1_full.png
new file mode 100644
index 00000000..478cc50f
Binary files /dev/null and b/public_html/data/screenshots/bolt/carnival/carnival_win_en_1_1_full.png differ
diff --git a/public_html/data/screenshots/bolt/carnival/carnival_win_en_1_2.jpg b/public_html/data/screenshots/bolt/carnival/carnival_win_en_1_2.jpg
new file mode 100644
index 00000000..b79a64aa
Binary files /dev/null and b/public_html/data/screenshots/bolt/carnival/carnival_win_en_1_2.jpg differ
diff --git a/public_html/data/screenshots/bolt/carnival/carnival_win_en_1_2_full.png b/public_html/data/screenshots/bolt/carnival/carnival_win_en_1_2_full.png
new file mode 100644
index 00000000..05b8546c
Binary files /dev/null and b/public_html/data/screenshots/bolt/carnival/carnival_win_en_1_2_full.png differ
diff --git a/public_html/data/screenshots/bolt/carnival/carnival_win_en_1_3.jpg b/public_html/data/screenshots/bolt/carnival/carnival_win_en_1_3.jpg
new file mode 100644
index 00000000..cdc1cfbd
Binary files /dev/null and b/public_html/data/screenshots/bolt/carnival/carnival_win_en_1_3.jpg differ
diff --git a/public_html/data/screenshots/bolt/carnival/carnival_win_en_1_3_full.png b/public_html/data/screenshots/bolt/carnival/carnival_win_en_1_3_full.png
new file mode 100644
index 00000000..fde3c78c
Binary files /dev/null and b/public_html/data/screenshots/bolt/carnival/carnival_win_en_1_3_full.png differ
diff --git a/public_html/data/screenshots/bolt/carnival/carnival_win_en_1_4.jpg b/public_html/data/screenshots/bolt/carnival/carnival_win_en_1_4.jpg
new file mode 100644
index 00000000..b4c3fb45
Binary files /dev/null and b/public_html/data/screenshots/bolt/carnival/carnival_win_en_1_4.jpg differ
diff --git a/public_html/data/screenshots/bolt/carnival/carnival_win_en_1_4_full.png b/public_html/data/screenshots/bolt/carnival/carnival_win_en_1_4_full.png
new file mode 100644
index 00000000..d615d43f
Binary files /dev/null and b/public_html/data/screenshots/bolt/carnival/carnival_win_en_1_4_full.png differ
diff --git a/public_html/data/screenshots/bolt/carnival/carnival_win_en_1_5.jpg b/public_html/data/screenshots/bolt/carnival/carnival_win_en_1_5.jpg
new file mode 100644
index 00000000..5e6de5b5
Binary files /dev/null and b/public_html/data/screenshots/bolt/carnival/carnival_win_en_1_5.jpg differ
diff --git a/public_html/data/screenshots/bolt/carnival/carnival_win_en_1_5_full.png b/public_html/data/screenshots/bolt/carnival/carnival_win_en_1_5_full.png
new file mode 100644
index 00000000..a6ef034e
Binary files /dev/null and b/public_html/data/screenshots/bolt/carnival/carnival_win_en_1_5_full.png differ
diff --git a/public_html/data/screenshots/bolt/carnival/carnival_win_en_1_6.jpg b/public_html/data/screenshots/bolt/carnival/carnival_win_en_1_6.jpg
new file mode 100644
index 00000000..a8df609b
Binary files /dev/null and b/public_html/data/screenshots/bolt/carnival/carnival_win_en_1_6.jpg differ
diff --git a/public_html/data/screenshots/bolt/carnival/carnival_win_en_1_6_full.png b/public_html/data/screenshots/bolt/carnival/carnival_win_en_1_6_full.png
new file mode 100644
index 00000000..1705bf19
Binary files /dev/null and b/public_html/data/screenshots/bolt/carnival/carnival_win_en_1_6_full.png differ
diff --git a/public_html/data/screenshots/bolt/carnival/carnival_win_en_1_7.jpg b/public_html/data/screenshots/bolt/carnival/carnival_win_en_1_7.jpg
new file mode 100644
index 00000000..96c9ed83
Binary files /dev/null and b/public_html/data/screenshots/bolt/carnival/carnival_win_en_1_7.jpg differ
diff --git a/public_html/data/screenshots/bolt/carnival/carnival_win_en_1_7_full.png b/public_html/data/screenshots/bolt/carnival/carnival_win_en_1_7_full.png
new file mode 100644
index 00000000..d7dfd5e5
Binary files /dev/null and b/public_html/data/screenshots/bolt/carnival/carnival_win_en_1_7_full.png differ



From noreply at scummvm.org  Sat Jun 13 06:14:05 2026
From: noreply at scummvm.org (neuromancer)
Date: Sat, 13 Jun 2026 06:14:05 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 36cfb5f6f932fb5ac259a43eb1c8256531e7d1ae
Message-ID: <20260613061405.C473B7005BC@ron.scummvm.net>

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

Summary:
36cfb5f6f9 PRIVATE: added option to enhance contrast of reading material


Commit: 36cfb5f6f932fb5ac259a43eb1c8256531e7d1ae
    https://github.com/scummvm/scummvm/commit/36cfb5f6f932fb5ac259a43eb1c8256531e7d1ae
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-13T08:10:25+02:00

Commit Message:
PRIVATE: added option to enhance contrast of reading material

Changed paths:
  A engines/private/paper.cpp
  A engines/private/paper.h
    engines/private/detection.cpp
    engines/private/detection.h
    engines/private/metaengine.cpp
    engines/private/module.mk
    engines/private/private.cpp
    engines/private/private.h


diff --git a/engines/private/detection.cpp b/engines/private/detection.cpp
index 29334ed0885..6650c63afd5 100644
--- a/engines/private/detection.cpp
+++ b/engines/private/detection.cpp
@@ -48,7 +48,7 @@ static const ADGameDescription gameDescriptions[] = {
 		Common::EN_USA,
 		Common::kPlatformWindows,
 		ADGF_NO_FLAGS,
-		GUIO3(GUIO_NOMIDI, GAMEOPTION_SFX_SUBTITLES, GAMEOPTION_HIGHLIGHT_MASKS)
+		GUIO4(GUIO_NOMIDI, GAMEOPTION_SFX_SUBTITLES, GAMEOPTION_HIGHLIGHT_MASKS, GAMEOPTION_READING_MATERIAL_CONTRAST)
 	},
 	{
 		"private-eye", // Demo from the US release v1.0.0.23
@@ -58,7 +58,7 @@ static const ADGameDescription gameDescriptions[] = {
 		Common::EN_USA,
 		Common::kPlatformWindows,
 		ADGF_DEMO,
-		GUIO3(GUIO_NOMIDI, GAMEOPTION_SFX_SUBTITLES, GAMEOPTION_HIGHLIGHT_MASKS)
+		GUIO4(GUIO_NOMIDI, GAMEOPTION_SFX_SUBTITLES, GAMEOPTION_HIGHLIGHT_MASKS, GAMEOPTION_READING_MATERIAL_CONTRAST)
 	},
 	{
 		"private-eye",  // EU release (UK)
@@ -68,7 +68,7 @@ static const ADGameDescription gameDescriptions[] = {
 		Common::EN_GRB,
 		Common::kPlatformWindows,
 		ADGF_NO_FLAGS,
-		GUIO3(GUIO_NOMIDI, GAMEOPTION_SFX_SUBTITLES, GAMEOPTION_HIGHLIGHT_MASKS)
+		GUIO4(GUIO_NOMIDI, GAMEOPTION_SFX_SUBTITLES, GAMEOPTION_HIGHLIGHT_MASKS, GAMEOPTION_READING_MATERIAL_CONTRAST)
 	},
 	{
 		"private-eye", // Demo from the EU release
@@ -78,7 +78,7 @@ static const ADGameDescription gameDescriptions[] = {
 		Common::EN_GRB,
 		Common::kPlatformWindows,
 		ADGF_DEMO,
-		GUIO3(GUIO_NOMIDI, GAMEOPTION_SFX_SUBTITLES, GAMEOPTION_HIGHLIGHT_MASKS)
+		GUIO4(GUIO_NOMIDI, GAMEOPTION_SFX_SUBTITLES, GAMEOPTION_HIGHLIGHT_MASKS, GAMEOPTION_READING_MATERIAL_CONTRAST)
 	},
 	{
 		"private-eye", // Demo from PCGamer Disc 2.6 JULY 1996 v1.0.0.12
@@ -88,7 +88,7 @@ static const ADGameDescription gameDescriptions[] = {
 		Common::EN_USA,
 		Common::kPlatformWindows,
 		ADGF_DEMO,
-		GUIO3(GUIO_NOMIDI, GAMEOPTION_SFX_SUBTITLES, GAMEOPTION_HIGHLIGHT_MASKS)
+		GUIO4(GUIO_NOMIDI, GAMEOPTION_SFX_SUBTITLES, GAMEOPTION_HIGHLIGHT_MASKS, GAMEOPTION_READING_MATERIAL_CONTRAST)
 	},
 	{
 		"private-eye", // Another demo
@@ -98,7 +98,7 @@ static const ADGameDescription gameDescriptions[] = {
 		Common::EN_USA,
 		Common::kPlatformWindows,
 		ADGF_DEMO,
-		GUIO3(GUIO_NOMIDI, GAMEOPTION_SFX_SUBTITLES, GAMEOPTION_HIGHLIGHT_MASKS)
+		GUIO4(GUIO_NOMIDI, GAMEOPTION_SFX_SUBTITLES, GAMEOPTION_HIGHLIGHT_MASKS, GAMEOPTION_READING_MATERIAL_CONTRAST)
 	},
 	{
 		"private-eye", // EU release (ES)
@@ -108,7 +108,7 @@ static const ADGameDescription gameDescriptions[] = {
 		Common::ES_ESP,
 		Common::kPlatformWindows,
 		ADGF_NO_FLAGS,
-		GUIO3(GUIO_NOMIDI, GAMEOPTION_SFX_SUBTITLES, GAMEOPTION_HIGHLIGHT_MASKS)
+		GUIO4(GUIO_NOMIDI, GAMEOPTION_SFX_SUBTITLES, GAMEOPTION_HIGHLIGHT_MASKS, GAMEOPTION_READING_MATERIAL_CONTRAST)
 	},
 	{
 		"private-eye", // Demo from the EU release (ES)
@@ -118,7 +118,7 @@ static const ADGameDescription gameDescriptions[] = {
 		Common::ES_ESP,
 		Common::kPlatformWindows,
 		ADGF_DEMO,
-		GUIO3(GUIO_NOMIDI, GAMEOPTION_SFX_SUBTITLES, GAMEOPTION_HIGHLIGHT_MASKS)
+		GUIO4(GUIO_NOMIDI, GAMEOPTION_SFX_SUBTITLES, GAMEOPTION_HIGHLIGHT_MASKS, GAMEOPTION_READING_MATERIAL_CONTRAST)
 	},
 	{
 		"private-eye", // EU release (FR)
@@ -128,7 +128,7 @@ static const ADGameDescription gameDescriptions[] = {
 		Common::FR_FRA,
 		Common::kPlatformWindows,
 		ADGF_NO_FLAGS,
-		GUIO2(GUIO_NOMIDI, GAMEOPTION_HIGHLIGHT_MASKS)
+		GUIO3(GUIO_NOMIDI, GAMEOPTION_HIGHLIGHT_MASKS, GAMEOPTION_READING_MATERIAL_CONTRAST)
 	},
 	{
 		"private-eye", // EU release (DE)
@@ -138,7 +138,7 @@ static const ADGameDescription gameDescriptions[] = {
 		Common::DE_DEU,
 		Common::kPlatformWindows,
 		ADGF_NO_FLAGS,
-		GUIO2(GUIO_NOMIDI, GAMEOPTION_HIGHLIGHT_MASKS)
+		GUIO3(GUIO_NOMIDI, GAMEOPTION_HIGHLIGHT_MASKS, GAMEOPTION_READING_MATERIAL_CONTRAST)
 	},
 	{
 		"private-eye", // Promotional demo disc
@@ -148,7 +148,7 @@ static const ADGameDescription gameDescriptions[] = {
 			Common::EN_USA,
 			Common::kPlatformWindows,
 			ADGF_DEMO,
-			GUIO2(GUIO_NOMIDI, GAMEOPTION_HIGHLIGHT_MASKS)
+			GUIO3(GUIO_NOMIDI, GAMEOPTION_HIGHLIGHT_MASKS, GAMEOPTION_READING_MATERIAL_CONTRAST)
 	},
 	{
 		"private-eye", // Demo from the EU release (DE)
@@ -158,7 +158,7 @@ static const ADGameDescription gameDescriptions[] = {
 		Common::DE_DEU,
 		Common::kPlatformWindows,
 		ADGF_DEMO,
-		GUIO2(GUIO_NOMIDI, GAMEOPTION_HIGHLIGHT_MASKS)
+		GUIO3(GUIO_NOMIDI, GAMEOPTION_HIGHLIGHT_MASKS, GAMEOPTION_READING_MATERIAL_CONTRAST)
 	},
 	{
 		"private-eye", // Demo from the EU release (FR)
@@ -168,7 +168,7 @@ static const ADGameDescription gameDescriptions[] = {
 		Common::FR_FRA,
 		Common::kPlatformWindows,
 		ADGF_DEMO,
-		GUIO2(GUIO_NOMIDI, GAMEOPTION_HIGHLIGHT_MASKS)
+		GUIO3(GUIO_NOMIDI, GAMEOPTION_HIGHLIGHT_MASKS, GAMEOPTION_READING_MATERIAL_CONTRAST)
 	},
 	{
 		"private-eye",  // RU release
@@ -178,7 +178,7 @@ static const ADGameDescription gameDescriptions[] = {
 		Common::RU_RUS,
 		Common::kPlatformWindows,
 		ADGF_NO_FLAGS,
-		GUIO2(GUIO_NOMIDI, GAMEOPTION_HIGHLIGHT_MASKS)
+		GUIO3(GUIO_NOMIDI, GAMEOPTION_HIGHLIGHT_MASKS, GAMEOPTION_READING_MATERIAL_CONTRAST)
 	},
 	{
 		"private-eye",  // KO release
@@ -188,7 +188,7 @@ static const ADGameDescription gameDescriptions[] = {
 		Common::KO_KOR,
 		Common::kPlatformWindows,
 		ADGF_NO_FLAGS,
-		GUIO2(GUIO_NOMIDI, GAMEOPTION_HIGHLIGHT_MASKS)
+		GUIO3(GUIO_NOMIDI, GAMEOPTION_HIGHLIGHT_MASKS, GAMEOPTION_READING_MATERIAL_CONTRAST)
 	},
 	{
 		"private-eye",  // JP release
@@ -198,7 +198,7 @@ static const ADGameDescription gameDescriptions[] = {
 		Common::JA_JPN,
 		Common::kPlatformWindows,
 		ADGF_NO_FLAGS,
-		GUIO2(GUIO_NOMIDI, GAMEOPTION_HIGHLIGHT_MASKS)
+		GUIO3(GUIO_NOMIDI, GAMEOPTION_HIGHLIGHT_MASKS, GAMEOPTION_READING_MATERIAL_CONTRAST)
 	},
 	{
 		"private-eye", // MacOS release (US)
@@ -208,7 +208,7 @@ static const ADGameDescription gameDescriptions[] = {
 		Common::EN_USA,
 		Common::kPlatformMacintosh,
 		ADGF_NO_FLAGS,
-		GUIO3(GUIO_NOMIDI, GAMEOPTION_SFX_SUBTITLES, GAMEOPTION_HIGHLIGHT_MASKS)
+		GUIO4(GUIO_NOMIDI, GAMEOPTION_SFX_SUBTITLES, GAMEOPTION_HIGHLIGHT_MASKS, GAMEOPTION_READING_MATERIAL_CONTRAST)
 	},
 	{
 		"private-eye", // MacOS release (US) uninstalled
@@ -217,7 +217,7 @@ static const ADGameDescription gameDescriptions[] = {
 		Common::EN_USA,
 		Common::kPlatformMacintosh,
 		ADGF_NO_FLAGS,
-		GUIO1(GUIO_NOMIDI)
+		GUIO2(GUIO_NOMIDI, GAMEOPTION_READING_MATERIAL_CONTRAST)
 	},
 	{
 		"private-eye", // MacOS release (JP) uninstalled
@@ -226,7 +226,7 @@ static const ADGameDescription gameDescriptions[] = {
 		Common::JA_JPN,
 		Common::kPlatformMacintosh,
 		ADGF_NO_FLAGS,
-		GUIO1(GUIO_NOMIDI)
+		GUIO2(GUIO_NOMIDI, GAMEOPTION_READING_MATERIAL_CONTRAST)
 	},
 	{
 		"private-eye", // MacOS demo (US)
@@ -236,7 +236,7 @@ static const ADGameDescription gameDescriptions[] = {
 		Common::EN_USA,
 		Common::kPlatformMacintosh,
 		ADGF_DEMO,
-		GUIO3(GUIO_NOMIDI, GAMEOPTION_SFX_SUBTITLES, GAMEOPTION_HIGHLIGHT_MASKS)
+		GUIO4(GUIO_NOMIDI, GAMEOPTION_SFX_SUBTITLES, GAMEOPTION_HIGHLIGHT_MASKS, GAMEOPTION_READING_MATERIAL_CONTRAST)
 	},
 	{
 		"private-eye", // MacOS demo (US) uninstalled
@@ -245,7 +245,7 @@ static const ADGameDescription gameDescriptions[] = {
 		Common::EN_USA,
 		Common::kPlatformMacintosh,
 		ADGF_DEMO,
-		GUIO1(GUIO_NOMIDI)
+		GUIO2(GUIO_NOMIDI, GAMEOPTION_READING_MATERIAL_CONTRAST)
 	},
 	AD_TABLE_END_MARKER
 };
diff --git a/engines/private/detection.h b/engines/private/detection.h
index 7e0d803276d..42e899394ab 100644
--- a/engines/private/detection.h
+++ b/engines/private/detection.h
@@ -3,5 +3,6 @@
 
 #define GAMEOPTION_SFX_SUBTITLES GUIO_GAMEOPTIONS1
 #define GAMEOPTION_HIGHLIGHT_MASKS GUIO_GAMEOPTIONS2
+#define GAMEOPTION_READING_MATERIAL_CONTRAST GUIO_GAMEOPTIONS3
 
 #endif // PRIVATE_DETECTION_H
diff --git a/engines/private/metaengine.cpp b/engines/private/metaengine.cpp
index bcf2aa24882..bdcab09986f 100644
--- a/engines/private/metaengine.cpp
+++ b/engines/private/metaengine.cpp
@@ -55,6 +55,17 @@ static const ADExtraGuiOptionsMap optionsList[] = {
 			0
 		}
 	},
+	{
+		GAMEOPTION_READING_MATERIAL_CONTRAST,
+		{
+			_s("Increased contrast for small print"),
+			_s("Enhance small printed paper close-ups for improved readability."),
+			"readingMaterialContrast",
+			false,
+			0,
+			0
+		}
+	},
 	AD_EXTRA_GUI_OPTIONS_TERMINATOR
 };
 
diff --git a/engines/private/module.mk b/engines/private/module.mk
index 44de2aabf26..300b695fbd5 100644
--- a/engines/private/module.mk
+++ b/engines/private/module.mk
@@ -8,6 +8,7 @@ MODULE_OBJS := \
 	grammar.o \
 	lexer.o \
 	metaengine.o \
+	paper.o \
 	private.o \
 	savegame.o \
 	symbol.o
diff --git a/engines/private/paper.cpp b/engines/private/paper.cpp
new file mode 100644
index 00000000000..cb7776cc896
--- /dev/null
+++ b/engines/private/paper.cpp
@@ -0,0 +1,274 @@
+/* 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/array.h"
+#include "common/path.h"
+#include "common/str.h"
+#include "common/util.h"
+#include "graphics/palette.h"
+#include "graphics/surface.h"
+
+#include "private/paper.h"
+
+namespace Private {
+
+const int kPaperScanTransparentBackgroundLuma = 180;
+
+// Nearest palette color in YCbCr space among the allowed entries. Chroma
+// errors are weighted heavier than luminance ones: a wrong brightness just
+// looks like paper grain, while a wrong hue stands out as colored speckle.
+byte findNearestUsedColor(const byte *luma, const byte *cb, const byte *cr,
+		const Common::Array<byte> &allowedColors, int targetY, int targetCb, int targetCr) {
+	byte bestColor = allowedColors[0];
+	int bestDistance = 0x7fffffff;
+
+	for (uint i = 0; i < allowedColors.size(); i++) {
+		byte color = allowedColors[i];
+		int dy = luma[color] - targetY;
+		int dcb = cb[color] - targetCb;
+		int dcr = cr[color] - targetCr;
+		int distance = 2 * dy * dy + 5 * (dcb * dcb + dcr * dcr);
+
+		if (distance < bestDistance) {
+			bestDistance = distance;
+			bestColor = color;
+		}
+	}
+
+	return bestColor;
+}
+
+void blurPlane(const Graphics::Surface &image, byte transparentColor, const byte *src, byte *dst, byte *tmp) {
+	const int kernel[5] = {1, 14, 34, 14, 1};
+	const int width = image.w;
+	const int height = image.h;
+
+	for (int y = 0; y < height; y++) {
+		const byte *row = (const byte *)image.getBasePtr(0, y);
+		const byte *srcRow = src + y * width;
+		byte *tmpRow = tmp + y * width;
+		for (int x = 0; x < width; x++) {
+			int center = srcRow[x];
+			int sum = 0;
+			for (int k = -2; k <= 2; k++) {
+				int xx = x + k;
+				bool outside = xx < 0 || xx >= width || row[xx] == transparentColor;
+				sum += kernel[k + 2] * (outside ? center : srcRow[xx]);
+			}
+			tmpRow[x] = (sum + 32) >> 6;
+		}
+	}
+
+	for (int y = 0; y < height; y++) {
+		const byte *tmpRow = tmp + y * width;
+		byte *dstRow = dst + y * width;
+		for (int x = 0; x < width; x++) {
+			int center = tmpRow[x];
+			int sum = 0;
+			for (int k = -2; k <= 2; k++) {
+				int yy = y + k;
+				bool outside = yy < 0 || yy >= height;
+				if (!outside) {
+					const byte *row = (const byte *)image.getBasePtr(0, yy);
+					outside = row[x] == transparentColor;
+				}
+				sum += kernel[k + 2] * (outside ? center : tmp[yy * width + x]);
+			}
+			dstRow[x] = (sum + 32) >> 6;
+		}
+	}
+}
+
+bool isPaperScanImage(const Common::Path &path) {
+	Common::String pathString = path.toString('/');
+	pathString.toLowercase();
+
+	size_t fileNamePos = pathString.findLastOf('/');
+	Common::String fileName = (fileNamePos == Common::String::npos) ?
+		pathString : pathString.substr(fileNamePos + 1);
+
+	if (!fileName.hasSuffix(".bmp"))
+		return false;
+
+	if (fileName.contains("mask") || fileName.contains("msk") || fileName.contains("mk"))
+		return false;
+
+	if (pathString.contains("inface/dossiers/") && fileName.hasPrefix("filec"))
+		return true;
+
+	if (!pathString.contains("/search_s/"))
+		return false;
+
+	return fileName.contains("nwsc") ||
+	       fileName.contains("newsc") ||
+	       fileName.contains("magc") ||
+	       fileName.contains("formcu") ||
+	       fileName.contains("ltrcu") ||
+	       fileName.contains("liccu") ||
+	       fileName.contains("billcu") ||
+	       fileName.contains("bilcu");
+}
+
+// Readability enhancement for document close-ups (newspapers, magazines,
+// letters): stretch the luminance histogram so it spans the full range
+// (clipping 1% of pixels at each end) and apply a gentle unsharp mask.
+//
+// The paper in these scans is dithered with alternately tinted entries
+// that read as colored speckle once the contrast is raised, so the
+// chroma is smoothed over a small neighborhood and every pixel is mapped
+// to the palette entry nearest to (new luminance, smoothed chroma),
+// which collapses the dither into a uniform paper tint.
+//
+// The palette itself is never modified, and pixels are only remapped to
+// palette indices the image already uses: the palette installed when the
+// image is drawn is not necessarily this image's own (scene images share
+// index conventions), so an index this image never referenced may show
+// up as an arbitrary color on screen even though it looks correct in the
+// image's own palette.
+bool enhancePaperScanImage(Graphics::Surface *image, const byte *palette, byte transparentColor) {
+	if (image->format.bytesPerPixel != 1)
+		return false;
+
+	const int width = image->w;
+	const int height = image->h;
+
+	byte luma[Graphics::PALETTE_COUNT], chromaB[Graphics::PALETTE_COUNT], chromaR[Graphics::PALETTE_COUNT];
+	for (uint32 color = 0; color < Graphics::PALETTE_COUNT; color++) {
+		int r = palette[3 * color + 0];
+		int g = palette[3 * color + 1];
+		int b = palette[3 * color + 2];
+		luma[color] = (77 * r + 150 * g + 29 * b) >> 8;
+		chromaB[color] = (byte)(128 + (-43 * r - 85 * g + 128 * b) / 256);
+		chromaR[color] = (byte)(128 + (128 * r - 107 * g - 21 * b) / 256);
+	}
+
+	uint32 histogram[Graphics::PALETTE_COUNT] = {};
+	bool used[Graphics::PALETTE_COUNT] = {};
+	uint32 opaquePixelCount = 0;
+	uint32 histogramPixelCount = 0;
+
+	for (int y = 0; y < height; y++) {
+		const byte *row = (const byte *)image->getBasePtr(0, y);
+		for (int x = 0; x < width; x++) {
+			byte color = row[x];
+			if (color == transparentColor) {
+				histogram[kPaperScanTransparentBackgroundLuma]++;
+				histogramPixelCount++;
+				continue;
+			}
+
+			histogram[luma[color]]++;
+			used[color] = true;
+			opaquePixelCount++;
+			histogramPixelCount++;
+		}
+	}
+
+	if (opaquePixelCount < (uint32)(width * height / 30))
+		return false;
+
+	Common::Array<byte> allowedColors;
+	allowedColors.reserve(Graphics::PALETTE_COUNT);
+	for (uint32 color = 0; color < Graphics::PALETTE_COUNT; color++) {
+		if (used[color] && color != transparentColor)
+			allowedColors.push_back(color);
+	}
+	if (allowedColors.empty())
+		return false;
+
+	uint32 cutoff = histogramPixelCount / 100;
+	uint32 accumulated = 0;
+	int lowCut = 0;
+	while (lowCut < 255 && accumulated + histogram[lowCut] <= cutoff)
+		accumulated += histogram[lowCut++];
+
+	accumulated = 0;
+	int highCut = 255;
+	while (highCut > lowCut && accumulated + histogram[highCut] <= cutoff)
+		accumulated += histogram[highCut--];
+
+	if (highCut <= lowCut) {
+		lowCut = 0;
+		highCut = 255;
+	}
+
+	byte stretched[Graphics::PALETTE_COUNT];
+	for (int level = 0; level < Graphics::PALETTE_COUNT; level++)
+		stretched[level] = (byte)CLIP((level - lowCut) * 255 / (highCut - lowCut), 0, 255);
+
+	const uint32 planeSize = width * height;
+	Common::Array<byte> yPlane(planeSize), yBlur(planeSize);
+	Common::Array<byte> cbPlane(planeSize), crPlane(planeSize);
+	Common::Array<byte> scratch(planeSize);
+	const uint32 cacheSize = Graphics::PALETTE_COUNT * 64 * 64;
+	Common::Array<int16> nearestCache(cacheSize, -1);
+
+	for (int y = 0; y < height; y++) {
+		const byte *row = (const byte *)image->getBasePtr(0, y);
+		byte *yRow = yPlane.data() + y * width;
+		byte *cbRow = cbPlane.data() + y * width;
+		byte *crRow = crPlane.data() + y * width;
+		for (int x = 0; x < width; x++) {
+			byte color = row[x];
+			yRow[x] = stretched[luma[color]];
+			cbRow[x] = chromaB[color];
+			crRow[x] = chromaR[color];
+		}
+	}
+
+	blurPlane(*image, transparentColor, yPlane.data(), yBlur.data(), scratch.data());
+	blurPlane(*image, transparentColor, cbPlane.data(), cbPlane.data(), scratch.data());
+	blurPlane(*image, transparentColor, cbPlane.data(), cbPlane.data(), scratch.data());
+	blurPlane(*image, transparentColor, crPlane.data(), crPlane.data(), scratch.data());
+	blurPlane(*image, transparentColor, crPlane.data(), crPlane.data(), scratch.data());
+
+	for (int y = 0; y < height; y++) {
+		byte *row = (byte *)image->getBasePtr(0, y);
+		const byte *yRow = yPlane.data() + y * width;
+		const byte *blurRow = yBlur.data() + y * width;
+		const byte *cbRow = cbPlane.data() + y * width;
+		const byte *crRow = crPlane.data() + y * width;
+		for (int x = 0; x < width; x++) {
+			byte color = row[x];
+			if (color == transparentColor)
+				continue;
+
+			int value = yRow[x];
+			int diff = value - blurRow[x];
+			if (diff > 2 || diff < -2)
+				value += diff * 120 / 100;
+			value = CLIP(value, 0, 255);
+
+			int targetCb = (cbRow[x] & ~3) + 2;
+			int targetCr = (crRow[x] & ~3) + 2;
+
+			uint32 cacheKey = (value << 12) | ((cbRow[x] >> 2) << 6) | (crRow[x] >> 2);
+			if (nearestCache[cacheKey] < 0)
+				nearestCache[cacheKey] = findNearestUsedColor(luma, chromaB, chromaR,
+					allowedColors, value, targetCb, targetCr);
+			row[x] = (byte)nearestCache[cacheKey];
+		}
+	}
+
+	return true;
+}
+
+} // End of namespace Private
diff --git a/engines/private/paper.h b/engines/private/paper.h
new file mode 100644
index 00000000000..c7b3d07bc03
--- /dev/null
+++ b/engines/private/paper.h
@@ -0,0 +1,42 @@
+/* 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 PRIVATE_PAPER_H
+#define PRIVATE_PAPER_H
+
+#include "common/scummsys.h"
+
+namespace Common {
+class Path;
+}
+
+namespace Graphics {
+struct Surface;
+}
+
+namespace Private {
+
+bool isPaperScanImage(const Common::Path &path);
+bool enhancePaperScanImage(Graphics::Surface *image, const byte *palette, byte transparentColor);
+
+} // End of namespace Private
+
+#endif // PRIVATE_PAPER_H
diff --git a/engines/private/private.cpp b/engines/private/private.cpp
index 45c82bcee1c..f833cd17e73 100644
--- a/engines/private/private.cpp
+++ b/engines/private/private.cpp
@@ -42,6 +42,7 @@
 
 #include "private/decompiler.h"
 #include "private/grammar.h"
+#include "private/paper.h"
 #include "private/private.h"
 #include "private/savegame.h"
 #include "private/tokens.h"
@@ -59,6 +60,9 @@ PrivateEngine::PrivateEngine(OSystem *syst, const ADGameDescription *gd)
 	  _defaultCursor(nullptr),
 	  _screenW(640), _screenH(480) {
 	_highlightMasks = false;
+	_readingMaterialContrast = false;
+	_paperScanFilteringActive = false;
+	_paperScanPreviousFiltering = false;
 	_rnd = new Common::RandomSource("private");
 
 	// Global object for external reference
@@ -130,6 +134,7 @@ PrivateEngine::PrivateEngine(OSystem *syst, const ADGameDescription *gd)
 }
 
 PrivateEngine::~PrivateEngine() {
+	setPaperScanFiltering(false);
 	destroyVideo();
 	destroySubtitles();
 
@@ -273,6 +278,9 @@ Common::Error PrivateEngine::run() {
 	if (!Common::parseBool(ConfMan.get("highlightMasks"), _shouldHighlightMasks))
 		warning("Failed to parse bool from highlightMasks options");
 
+	if (!Common::parseBool(ConfMan.get("readingMaterialContrast"), _readingMaterialContrast))
+		warning("Failed to parse bool from readingMaterialContrast options");
+
 	if (!_useSubtitles && _sfxSubtitles) {
 		warning("SFX subtitles are enabled, but no subtitles will be shown");
 	}
@@ -515,6 +523,8 @@ void PrivateEngine::initFuncs() {
 }
 
 void PrivateEngine::clearAreas() {
+	setPaperScanFiltering(false);
+
 	for (MaskList::const_iterator it = _masks.begin(); it != _masks.end(); ++it) {
 		const MaskInfo &m = *it;
 		if (m.surf != nullptr) {
@@ -2766,6 +2776,11 @@ Graphics::Surface *PrivateEngine::decodeImage(const Common::String &name, byte *
 		swapImageColors(newImage, currentPalette, maskTransparentColor, _transparentColor);
 	}
 
+	if (_readingMaterialContrast && isPaperScanImage(path)) {
+		if (enhancePaperScanImage(newImage, currentPalette, _transparentColor))
+			setPaperScanFiltering(true);
+	}
+
 	return newImage;
 }
 
@@ -2854,6 +2869,31 @@ void PrivateEngine::swapImageColors(Graphics::Surface *image, byte *palette, uin
 	}
 }
 
+void PrivateEngine::setPaperScanFiltering(bool enabled) {
+	if (!_system->hasFeature(OSystem::kFeatureFilteringMode))
+		return;
+
+	if (enabled) {
+		if (_paperScanFilteringActive)
+			return;
+
+		_paperScanPreviousFiltering = _system->getFeatureState(OSystem::kFeatureFilteringMode);
+		if (!_paperScanPreviousFiltering) {
+			_system->beginGFXTransaction();
+			_system->setFeatureState(OSystem::kFeatureFilteringMode, true);
+			_system->endGFXTransaction();
+		}
+		_paperScanFilteringActive = true;
+	} else if (_paperScanFilteringActive) {
+		if (_system->getFeatureState(OSystem::kFeatureFilteringMode) != _paperScanPreviousFiltering) {
+			_system->beginGFXTransaction();
+			_system->setFeatureState(OSystem::kFeatureFilteringMode, _paperScanPreviousFiltering);
+			_system->endGFXTransaction();
+		}
+		_paperScanFilteringActive = false;
+	}
+}
+
 void PrivateEngine::loadImage(const Common::String &name, int x, int y) {
 	debugC(1, kPrivateDebugFunction, "%s(%s,%d,%d)", __FUNCTION__, name.c_str(), x, y);
 	byte *palette;
diff --git a/engines/private/private.h b/engines/private/private.h
index d9f25a3c236..ee13e68f453 100644
--- a/engines/private/private.h
+++ b/engines/private/private.h
@@ -257,6 +257,7 @@ private:
 public:
 	bool _shouldHighlightMasks;
 	bool _highlightMasks;
+	bool _readingMaterialContrast;
 	PrivateEngine(OSystem *syst, const ADGameDescription *gd);
 	~PrivateEngine();
 
@@ -335,6 +336,7 @@ public:
 	void remapImage(uint16 ncolors, const Graphics::Surface *oldImage, const byte *oldPalette, Graphics::Surface *newImage, const byte *currentPalette);
 	static uint32 findMaskTransparentColor(const byte *palette, uint32 defaultColor);
 	static void swapImageColors(Graphics::Surface *image, byte *palette, uint32 a, uint32 b);
+	void setPaperScanFiltering(bool enabled);
 	void loadImage(const Common::String &file, int x, int y);
 	void drawScreenFrame(const byte *videoPalette);
 
@@ -365,6 +367,8 @@ public:
 	Common::Point _origin;
 	void drawScreen();
 	bool _needToDrawScreenFrame;
+	bool _paperScanFilteringActive;
+	bool _paperScanPreviousFiltering;
 
 	// settings
 	Common::String _nextSetting;



From noreply at scummvm.org  Sat Jun 13 06:14:12 2026
From: noreply at scummvm.org (ScummVM-Translations)
Date: Sat, 13 Jun 2026 06:14:12 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 eaa00958944cffe53911ffb6aa474bab54b947af
Message-ID: <20260613061412.635C07005CD@ron.scummvm.net>

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

Summary:
eaa0095894 I18N: Update translations templates


Commit: eaa00958944cffe53911ffb6aa474bab54b947af
    https://github.com/scummvm/scummvm/commit/eaa00958944cffe53911ffb6aa474bab54b947af
Author: Weblate (noreply at weblate.org)
Date: 2026-06-13T06:14:06Z

Commit Message:
I18N: Update translations templates

Changed paths:
    po/scummvm.pot


diff --git a/po/scummvm.pot b/po/scummvm.pot
index 6f77ee1f9cf..5f567894979 100644
--- a/po/scummvm.pot
+++ b/po/scummvm.pot
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2026.2.1git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-11 22:10+0000\n"
+"POT-Creation-Date: 2026-06-13 06:14+0000\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL at ADDRESS>\n"
 "Language-Team: LANGUAGE <LL at li.org>\n"
@@ -5637,7 +5637,7 @@ msgstr ""
 #: engines/lure/metaengine.cpp:168 engines/made/metaengine.cpp:132
 #: engines/mads/metaengine.cpp:316 engines/neverhood/metaengine.cpp:185
 #: engines/ngi/metaengine.cpp:115 engines/petka/metaengine.cpp:113
-#: engines/prince/metaengine.cpp:227 engines/private/metaengine.cpp:127
+#: engines/prince/metaengine.cpp:227 engines/private/metaengine.cpp:138
 #: engines/queen/metaengine.cpp:146 engines/sherlock/metaengine.cpp:313
 #: engines/stark/metaengine.cpp:184 engines/supernova/metaengine.cpp:217
 #: engines/sword1/metaengine.cpp:266 engines/sword25/metaengine.cpp:108
@@ -7028,7 +7028,7 @@ msgstr ""
 
 #: engines/buried/metaengine.cpp:141 engines/chamber/metaengine.cpp:65
 #: engines/chamber/metaengine.cpp:71 engines/chamber/metaengine.cpp:77
-#: engines/dm/metaengine.cpp:130 engines/private/metaengine.cpp:130
+#: engines/dm/metaengine.cpp:130 engines/private/metaengine.cpp:141
 #: engines/sherlock/metaengine.cpp:1187
 msgid "Select / Interact"
 msgstr ""
@@ -10974,7 +10974,15 @@ msgstr ""
 msgid "Highlight clickable areas in decision scenes."
 msgstr ""
 
-#: engines/private/metaengine.cpp:136 engines/trecision/metaengine.cpp:137
+#: engines/private/metaengine.cpp:61
+msgid "Increased contrast for small print"
+msgstr ""
+
+#: engines/private/metaengine.cpp:62
+msgid "Enhance small printed paper close-ups for improved readability."
+msgstr ""
+
+#: engines/private/metaengine.cpp:147 engines/trecision/metaengine.cpp:137
 msgid "Skip video"
 msgstr ""
 



From noreply at scummvm.org  Sat Jun 13 06:14:45 2026
From: noreply at scummvm.org (ScummVM-Translations)
Date: Sat, 13 Jun 2026 06:14:45 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 9e0ab5e725196a05fa4a5ce35c236f44ffc808d5
Message-ID: <20260613061445.045D57005BC@ron.scummvm.net>

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

Summary:
9e0ab5e725 I18N: Update translation files


Commit: 9e0ab5e725196a05fa4a5ce35c236f44ffc808d5
    https://github.com/scummvm/scummvm/commit/9e0ab5e725196a05fa4a5ce35c236f44ffc808d5
Author: Weblate (noreply at weblate.org)
Date: 2026-06-13T06:14:26Z

Commit Message:
I18N: Update translation files

Updated by "Update PO files to match POT (msgmerge)" hook in Weblate.

Translation: ScummVM/scummvm
Translate-URL: https://translations.scummvm.org/projects/scummvm/scummvm/

Changed paths:
    po/ar.po
    po/be-tarask.po
    po/be_BY.po
    po/ca_ES.po
    po/cs_CZ.po
    po/da.po
    po/de_DE.po
    po/el.po
    po/es_ES.po
    po/eu.po
    po/fi_FI.po
    po/fr_FR.po
    po/gl_ES.po
    po/he.po
    po/hi.po
    po/hu_HU.po
    po/it_IT.po
    po/ja.po
    po/ka.po
    po/ko.po
    po/nb_NO.po
    po/nl_NL.po
    po/nn_NO.po
    po/pl_PL.po
    po/pt_BR.po
    po/pt_PT.po
    po/ro.po
    po/ru_RU.po
    po/sv_SE.po
    po/tr.po
    po/uk_UA.po
    po/zh.po
    po/zh_Hans.po
    po/zh_Hant.po


diff --git a/po/ar.po b/po/ar.po
index 588695cc0a2..03a757f14ba 100644
--- a/po/ar.po
+++ b/po/ar.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-11 22:10+0000\n"
+"POT-Creation-Date: 2026-06-13 06:14+0000\n"
 "PO-Revision-Date: 2026-02-24 22:26+0000\n"
 "Last-Translator: Mohamed Shaaban <mohmedbashamedo at hotmail.com>\n"
 "Language-Team: Arabic <https://translations.scummvm.org/projects/scummvm/"
@@ -6137,7 +6137,7 @@ msgstr "???????? ???? ???????? ??????????????..."
 #: engines/lure/metaengine.cpp:168 engines/made/metaengine.cpp:132
 #: engines/mads/metaengine.cpp:316 engines/neverhood/metaengine.cpp:185
 #: engines/ngi/metaengine.cpp:115 engines/petka/metaengine.cpp:113
-#: engines/prince/metaengine.cpp:227 engines/private/metaengine.cpp:127
+#: engines/prince/metaengine.cpp:227 engines/private/metaengine.cpp:138
 #: engines/queen/metaengine.cpp:146 engines/sherlock/metaengine.cpp:313
 #: engines/stark/metaengine.cpp:184 engines/supernova/metaengine.cpp:217
 #: engines/sword1/metaengine.cpp:266 engines/sword25/metaengine.cpp:108
@@ -7717,7 +7717,7 @@ msgstr "???????????? ????????????"
 
 #: engines/buried/metaengine.cpp:141 engines/chamber/metaengine.cpp:65
 #: engines/chamber/metaengine.cpp:71 engines/chamber/metaengine.cpp:77
-#: engines/dm/metaengine.cpp:130 engines/private/metaengine.cpp:130
+#: engines/dm/metaengine.cpp:130 engines/private/metaengine.cpp:141
 #: engines/sherlock/metaengine.cpp:1187
 #, fuzzy
 #| msgid "Interact Mode"
@@ -12555,7 +12555,15 @@ msgstr "???? ???????????? ???????????? ????????????"
 msgid "Highlight clickable areas in decision scenes."
 msgstr ""
 
-#: engines/private/metaengine.cpp:136 engines/trecision/metaengine.cpp:137
+#: engines/private/metaengine.cpp:61
+msgid "Increased contrast for small print"
+msgstr ""
+
+#: engines/private/metaengine.cpp:62
+msgid "Enhance small printed paper close-ups for improved readability."
+msgstr ""
+
+#: engines/private/metaengine.cpp:147 engines/trecision/metaengine.cpp:137
 #, fuzzy
 #| msgid "Skip line"
 msgid "Skip video"
diff --git a/po/be-tarask.po b/po/be-tarask.po
index f1dd85b0479..4a5b048d5a1 100644
--- a/po/be-tarask.po
+++ b/po/be-tarask.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-11 22:10+0000\n"
+"POT-Creation-Date: 2026-06-13 06:14+0000\n"
 "PO-Revision-Date: 2023-02-26 15:01+0000\n"
 "Last-Translator: Kastu?? K. <oakum2334 at gmail.com>\n"
 "Language-Team: Belarusian (Tara??kievica) <https://translations.scummvm.org/"
@@ -6070,7 +6070,7 @@ msgstr "?????????????????? ????????????..."
 #: engines/lure/metaengine.cpp:168 engines/made/metaengine.cpp:132
 #: engines/mads/metaengine.cpp:316 engines/neverhood/metaengine.cpp:185
 #: engines/ngi/metaengine.cpp:115 engines/petka/metaengine.cpp:113
-#: engines/prince/metaengine.cpp:227 engines/private/metaengine.cpp:127
+#: engines/prince/metaengine.cpp:227 engines/private/metaengine.cpp:138
 #: engines/queen/metaengine.cpp:146 engines/sherlock/metaengine.cpp:313
 #: engines/stark/metaengine.cpp:184 engines/supernova/metaengine.cpp:217
 #: engines/sword1/metaengine.cpp:266 engines/sword25/metaengine.cpp:108
@@ -7659,7 +7659,7 @@ msgstr "~??~?????????? ????????????"
 
 #: engines/buried/metaengine.cpp:141 engines/chamber/metaengine.cpp:65
 #: engines/chamber/metaengine.cpp:71 engines/chamber/metaengine.cpp:77
-#: engines/dm/metaengine.cpp:130 engines/private/metaengine.cpp:130
+#: engines/dm/metaengine.cpp:130 engines/private/metaengine.cpp:141
 #: engines/sherlock/metaengine.cpp:1187
 #, fuzzy
 #| msgid "Interact"
@@ -12503,7 +12503,15 @@ msgstr "???????????????????? ???????????????? ????????????"
 msgid "Highlight clickable areas in decision scenes."
 msgstr ""
 
-#: engines/private/metaengine.cpp:136 engines/trecision/metaengine.cpp:137
+#: engines/private/metaengine.cpp:61
+msgid "Increased contrast for small print"
+msgstr ""
+
+#: engines/private/metaengine.cpp:62
+msgid "Enhance small printed paper close-ups for improved readability."
+msgstr ""
+
+#: engines/private/metaengine.cpp:147 engines/trecision/metaengine.cpp:137
 #, fuzzy
 #| msgid "Skip line"
 msgid "Skip video"
diff --git a/po/be_BY.po b/po/be_BY.po
index c2155d758f9..9ebe2b7bc29 100644
--- a/po/be_BY.po
+++ b/po/be_BY.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-11 22:10+0000\n"
+"POT-Creation-Date: 2026-06-13 06:14+0000\n"
 "PO-Revision-Date: 2021-03-21 08:24+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Belarusian <https://translations.scummvm.org/projects/scummvm/"
@@ -6079,7 +6079,7 @@ msgstr "???????????????? ????????????????????..."
 #: engines/lure/metaengine.cpp:168 engines/made/metaengine.cpp:132
 #: engines/mads/metaengine.cpp:316 engines/neverhood/metaengine.cpp:185
 #: engines/ngi/metaengine.cpp:115 engines/petka/metaengine.cpp:113
-#: engines/prince/metaengine.cpp:227 engines/private/metaengine.cpp:127
+#: engines/prince/metaengine.cpp:227 engines/private/metaengine.cpp:138
 #: engines/queen/metaengine.cpp:146 engines/sherlock/metaengine.cpp:313
 #: engines/stark/metaengine.cpp:184 engines/supernova/metaengine.cpp:217
 #: engines/sword1/metaengine.cpp:266 engines/sword25/metaengine.cpp:108
@@ -7628,7 +7628,7 @@ msgstr "~??~??????????..."
 
 #: engines/buried/metaengine.cpp:141 engines/chamber/metaengine.cpp:65
 #: engines/chamber/metaengine.cpp:71 engines/chamber/metaengine.cpp:77
-#: engines/dm/metaengine.cpp:130 engines/private/metaengine.cpp:130
+#: engines/dm/metaengine.cpp:130 engines/private/metaengine.cpp:141
 #: engines/sherlock/metaengine.cpp:1187
 #, fuzzy
 #| msgid "Interact"
@@ -12350,7 +12350,15 @@ msgstr "?????????????????????? ???????????????? ????????????"
 msgid "Highlight clickable areas in decision scenes."
 msgstr ""
 
-#: engines/private/metaengine.cpp:136 engines/trecision/metaengine.cpp:137
+#: engines/private/metaengine.cpp:61
+msgid "Increased contrast for small print"
+msgstr ""
+
+#: engines/private/metaengine.cpp:62
+msgid "Enhance small printed paper close-ups for improved readability."
+msgstr ""
+
+#: engines/private/metaengine.cpp:147 engines/trecision/metaengine.cpp:137
 #, fuzzy
 #| msgid "Skip line"
 msgid "Skip video"
diff --git a/po/ca_ES.po b/po/ca_ES.po
index 8693317969b..022f7ca4864 100644
--- a/po/ca_ES.po
+++ b/po/ca_ES.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.6.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-11 22:10+0000\n"
+"POT-Creation-Date: 2026-06-13 06:14+0000\n"
 "PO-Revision-Date: 2026-05-26 04:22+0000\n"
 "Last-Translator: Xaviu <xaviu at xaviu.com>\n"
 "Language-Team: Catalan <https://translations.scummvm.org/projects/scummvm/"
@@ -5881,7 +5881,7 @@ msgstr "Comprova si hi ha actualitzacions..."
 #: engines/lure/metaengine.cpp:168 engines/made/metaengine.cpp:132
 #: engines/mads/metaengine.cpp:316 engines/neverhood/metaengine.cpp:185
 #: engines/ngi/metaengine.cpp:115 engines/petka/metaengine.cpp:113
-#: engines/prince/metaengine.cpp:227 engines/private/metaengine.cpp:127
+#: engines/prince/metaengine.cpp:227 engines/private/metaengine.cpp:138
 #: engines/queen/metaengine.cpp:146 engines/sherlock/metaengine.cpp:313
 #: engines/stark/metaengine.cpp:184 engines/supernova/metaengine.cpp:217
 #: engines/sword1/metaengine.cpp:266 engines/sword25/metaengine.cpp:108
@@ -7378,7 +7378,7 @@ msgstr "Assignaci?? de tecles de l'inventari"
 
 #: engines/buried/metaengine.cpp:141 engines/chamber/metaengine.cpp:65
 #: engines/chamber/metaengine.cpp:71 engines/chamber/metaengine.cpp:77
-#: engines/dm/metaengine.cpp:130 engines/private/metaengine.cpp:130
+#: engines/dm/metaengine.cpp:130 engines/private/metaengine.cpp:141
 #: engines/sherlock/metaengine.cpp:1187
 msgid "Select / Interact"
 msgstr "Selecciona / Interactua"
@@ -11551,7 +11551,15 @@ msgstr "Ressalta les ??rees de decisi??"
 msgid "Highlight clickable areas in decision scenes."
 msgstr ""
 
-#: engines/private/metaengine.cpp:136 engines/trecision/metaengine.cpp:137
+#: engines/private/metaengine.cpp:61
+msgid "Increased contrast for small print"
+msgstr ""
+
+#: engines/private/metaengine.cpp:62
+msgid "Enhance small printed paper close-ups for improved readability."
+msgstr ""
+
+#: engines/private/metaengine.cpp:147 engines/trecision/metaengine.cpp:137
 msgid "Skip video"
 msgstr "Omet el v??deo"
 
diff --git a/po/cs_CZ.po b/po/cs_CZ.po
index 0e57bb3ab64..65956b21618 100644
--- a/po/cs_CZ.po
+++ b/po/cs_CZ.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.7.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-11 22:10+0000\n"
+"POT-Creation-Date: 2026-06-13 06:14+0000\n"
 "PO-Revision-Date: 2026-02-09 20:22+0000\n"
 "Last-Translator: AsciiWolf <mail at asciiwolf.com>\n"
 "Language-Team: Czech <https://translations.scummvm.org/projects/scummvm/"
@@ -6047,7 +6047,7 @@ msgstr "Zkontrolovat Aktualizace..."
 #: engines/lure/metaengine.cpp:168 engines/made/metaengine.cpp:132
 #: engines/mads/metaengine.cpp:316 engines/neverhood/metaengine.cpp:185
 #: engines/ngi/metaengine.cpp:115 engines/petka/metaengine.cpp:113
-#: engines/prince/metaengine.cpp:227 engines/private/metaengine.cpp:127
+#: engines/prince/metaengine.cpp:227 engines/private/metaengine.cpp:138
 #: engines/queen/metaengine.cpp:146 engines/sherlock/metaengine.cpp:313
 #: engines/stark/metaengine.cpp:184 engines/supernova/metaengine.cpp:217
 #: engines/sword1/metaengine.cpp:266 engines/sword25/metaengine.cpp:108
@@ -7583,7 +7583,7 @@ msgstr "~V~olby..."
 
 #: engines/buried/metaengine.cpp:141 engines/chamber/metaengine.cpp:65
 #: engines/chamber/metaengine.cpp:71 engines/chamber/metaengine.cpp:77
-#: engines/dm/metaengine.cpp:130 engines/private/metaengine.cpp:130
+#: engines/dm/metaengine.cpp:130 engines/private/metaengine.cpp:141
 #: engines/sherlock/metaengine.cpp:1187
 #, fuzzy
 #| msgid "Interact"
@@ -12190,7 +12190,15 @@ msgstr "Zv??raznit n??sleduj??c?? dialog"
 msgid "Highlight clickable areas in decision scenes."
 msgstr ""
 
-#: engines/private/metaengine.cpp:136 engines/trecision/metaengine.cpp:137
+#: engines/private/metaengine.cpp:61
+msgid "Increased contrast for small print"
+msgstr ""
+
+#: engines/private/metaengine.cpp:62
+msgid "Enhance small printed paper close-ups for improved readability."
+msgstr ""
+
+#: engines/private/metaengine.cpp:147 engines/trecision/metaengine.cpp:137
 #, fuzzy
 #| msgid "Skip line"
 msgid "Skip video"
diff --git a/po/da.po b/po/da.po
index 0a4974d3fb7..5e19064d9ed 100644
--- a/po/da.po
+++ b/po/da.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-11 22:10+0000\n"
+"POT-Creation-Date: 2026-06-13 06:14+0000\n"
 "PO-Revision-Date: 2023-09-07 17:04+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Danish <https://translations.scummvm.org/projects/scummvm/"
@@ -6066,7 +6066,7 @@ msgstr "S??g efter opdateringer ..."
 #: engines/lure/metaengine.cpp:168 engines/made/metaengine.cpp:132
 #: engines/mads/metaengine.cpp:316 engines/neverhood/metaengine.cpp:185
 #: engines/ngi/metaengine.cpp:115 engines/petka/metaengine.cpp:113
-#: engines/prince/metaengine.cpp:227 engines/private/metaengine.cpp:127
+#: engines/prince/metaengine.cpp:227 engines/private/metaengine.cpp:138
 #: engines/queen/metaengine.cpp:146 engines/sherlock/metaengine.cpp:313
 #: engines/stark/metaengine.cpp:184 engines/supernova/metaengine.cpp:217
 #: engines/sword1/metaengine.cpp:266 engines/sword25/metaengine.cpp:108
@@ -7610,7 +7610,7 @@ msgstr "~I~ndstillinger ..."
 
 #: engines/buried/metaengine.cpp:141 engines/chamber/metaengine.cpp:65
 #: engines/chamber/metaengine.cpp:71 engines/chamber/metaengine.cpp:77
-#: engines/dm/metaengine.cpp:130 engines/private/metaengine.cpp:130
+#: engines/dm/metaengine.cpp:130 engines/private/metaengine.cpp:141
 #: engines/sherlock/metaengine.cpp:1187
 #, fuzzy
 #| msgid "Interact"
@@ -12259,7 +12259,15 @@ msgstr "Fremh??v n??ste dialog"
 msgid "Highlight clickable areas in decision scenes."
 msgstr ""
 
-#: engines/private/metaengine.cpp:136 engines/trecision/metaengine.cpp:137
+#: engines/private/metaengine.cpp:61
+msgid "Increased contrast for small print"
+msgstr ""
+
+#: engines/private/metaengine.cpp:62
+msgid "Enhance small printed paper close-ups for improved readability."
+msgstr ""
+
+#: engines/private/metaengine.cpp:147 engines/trecision/metaengine.cpp:137
 #, fuzzy
 #| msgid "Skip line"
 msgid "Skip video"
diff --git a/po/de_DE.po b/po/de_DE.po
index ed8a61153fb..e641790a3f3 100644
--- a/po/de_DE.po
+++ b/po/de_DE.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.10.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-11 22:10+0000\n"
+"POT-Creation-Date: 2026-06-13 06:14+0000\n"
 "PO-Revision-Date: 2026-05-05 16:31+0000\n"
 "Last-Translator: Lothar Serra Mari <lothar.serramari at scummvm.org>\n"
 "Language-Team: German <https://translations.scummvm.org/projects/scummvm/"
@@ -6346,7 +6346,7 @@ msgstr "Suche nach Aktualisierungen..."
 #: engines/lure/metaengine.cpp:168 engines/made/metaengine.cpp:132
 #: engines/mads/metaengine.cpp:316 engines/neverhood/metaengine.cpp:185
 #: engines/ngi/metaengine.cpp:115 engines/petka/metaengine.cpp:113
-#: engines/prince/metaengine.cpp:227 engines/private/metaengine.cpp:127
+#: engines/prince/metaengine.cpp:227 engines/private/metaengine.cpp:138
 #: engines/queen/metaengine.cpp:146 engines/sherlock/metaengine.cpp:313
 #: engines/stark/metaengine.cpp:184 engines/supernova/metaengine.cpp:217
 #: engines/sword1/metaengine.cpp:266 engines/sword25/metaengine.cpp:108
@@ -7862,7 +7862,7 @@ msgstr "Bestands-Tastenbelegungen"
 
 #: engines/buried/metaengine.cpp:141 engines/chamber/metaengine.cpp:65
 #: engines/chamber/metaengine.cpp:71 engines/chamber/metaengine.cpp:77
-#: engines/dm/metaengine.cpp:130 engines/private/metaengine.cpp:130
+#: engines/dm/metaengine.cpp:130 engines/private/metaengine.cpp:141
 #: engines/sherlock/metaengine.cpp:1187
 msgid "Select / Interact"
 msgstr "Ausw??hlen / Interagieren"
@@ -12132,7 +12132,15 @@ msgstr "Entscheidungsbereiche hervorheben"
 msgid "Highlight clickable areas in decision scenes."
 msgstr "Markieren Sie anklickbare Bereiche in Entscheidungsszenen."
 
-#: engines/private/metaengine.cpp:136 engines/trecision/metaengine.cpp:137
+#: engines/private/metaengine.cpp:61
+msgid "Increased contrast for small print"
+msgstr ""
+
+#: engines/private/metaengine.cpp:62
+msgid "Enhance small printed paper close-ups for improved readability."
+msgstr ""
+
+#: engines/private/metaengine.cpp:147 engines/trecision/metaengine.cpp:137
 msgid "Skip video"
 msgstr "Video ??berspringen"
 
diff --git a/po/el.po b/po/el.po
index df9e7486e49..dcbe70a4014 100644
--- a/po/el.po
+++ b/po/el.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.10.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-11 22:10+0000\n"
+"POT-Creation-Date: 2026-06-13 06:14+0000\n"
 "PO-Revision-Date: 2026-04-23 00:22+0000\n"
 "Last-Translator: Antoniou Athanasios <a.antoniou79 at gmail.com>\n"
 "Language-Team: Greek <https://translations.scummvm.org/projects/scummvm/"
@@ -6470,7 +6470,7 @@ msgstr "?????????????? ??????????????????????..."
 #: engines/lure/metaengine.cpp:168 engines/made/metaengine.cpp:132
 #: engines/mads/metaengine.cpp:316 engines/neverhood/metaengine.cpp:185
 #: engines/ngi/metaengine.cpp:115 engines/petka/metaengine.cpp:113
-#: engines/prince/metaengine.cpp:227 engines/private/metaengine.cpp:127
+#: engines/prince/metaengine.cpp:227 engines/private/metaengine.cpp:138
 #: engines/queen/metaengine.cpp:146 engines/sherlock/metaengine.cpp:313
 #: engines/stark/metaengine.cpp:184 engines/supernova/metaengine.cpp:217
 #: engines/sword1/metaengine.cpp:266 engines/sword25/metaengine.cpp:108
@@ -7989,7 +7989,7 @@ msgstr "?????????????? ?????? ???? ??????????????????"
 
 #: engines/buried/metaengine.cpp:141 engines/chamber/metaengine.cpp:65
 #: engines/chamber/metaengine.cpp:71 engines/chamber/metaengine.cpp:77
-#: engines/dm/metaengine.cpp:130 engines/private/metaengine.cpp:130
+#: engines/dm/metaengine.cpp:130 engines/private/metaengine.cpp:141
 #: engines/sherlock/metaengine.cpp:1187
 msgid "Select / Interact"
 msgstr "?????????????? / ??????????????????????????"
@@ -12265,7 +12265,15 @@ msgstr "???????????????????? ???????????????? ????????????????"
 msgid "Highlight clickable areas in decision scenes."
 msgstr "???????????????????? ???????????????? ???????? ???????????? ???? ?????????? ???????? ???? ???????????? ????????????????."
 
-#: engines/private/metaengine.cpp:136 engines/trecision/metaengine.cpp:137
+#: engines/private/metaengine.cpp:61
+msgid "Increased contrast for small print"
+msgstr ""
+
+#: engines/private/metaengine.cpp:62
+msgid "Enhance small printed paper close-ups for improved readability."
+msgstr ""
+
+#: engines/private/metaengine.cpp:147 engines/trecision/metaengine.cpp:137
 msgid "Skip video"
 msgstr "?????????????????? ????????????"
 
diff --git a/po/es_ES.po b/po/es_ES.po
index a09d3af21cf..c1589bfee23 100644
--- a/po/es_ES.po
+++ b/po/es_ES.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.4.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-11 22:10+0000\n"
+"POT-Creation-Date: 2026-06-13 06:14+0000\n"
 "PO-Revision-Date: 2026-06-10 11:58+0000\n"
 "Last-Translator: IlDucci <pajaroloco_2 at hotmail.com>\n"
 "Language-Team: Spanish <https://translations.scummvm.org/projects/scummvm/"
@@ -6368,7 +6368,7 @@ msgstr "Buscar actualizaciones..."
 #: engines/lure/metaengine.cpp:168 engines/made/metaengine.cpp:132
 #: engines/mads/metaengine.cpp:316 engines/neverhood/metaengine.cpp:185
 #: engines/ngi/metaengine.cpp:115 engines/petka/metaengine.cpp:113
-#: engines/prince/metaengine.cpp:227 engines/private/metaengine.cpp:127
+#: engines/prince/metaengine.cpp:227 engines/private/metaengine.cpp:138
 #: engines/queen/metaengine.cpp:146 engines/sherlock/metaengine.cpp:313
 #: engines/stark/metaengine.cpp:184 engines/supernova/metaengine.cpp:217
 #: engines/sword1/metaengine.cpp:266 engines/sword25/metaengine.cpp:108
@@ -7876,7 +7876,7 @@ msgstr "Asignaciones del inventario"
 
 #: engines/buried/metaengine.cpp:141 engines/chamber/metaengine.cpp:65
 #: engines/chamber/metaengine.cpp:71 engines/chamber/metaengine.cpp:77
-#: engines/dm/metaengine.cpp:130 engines/private/metaengine.cpp:130
+#: engines/dm/metaengine.cpp:130 engines/private/metaengine.cpp:141
 #: engines/sherlock/metaengine.cpp:1187
 msgid "Select / Interact"
 msgstr "Seleccionar/Interactuar"
@@ -12113,7 +12113,15 @@ msgstr "Resaltar zonas de decisi??n"
 msgid "Highlight clickable areas in decision scenes."
 msgstr "Resalta las zonas interactuables durante las escenas de decisi??n."
 
-#: engines/private/metaengine.cpp:136 engines/trecision/metaengine.cpp:137
+#: engines/private/metaengine.cpp:61
+msgid "Increased contrast for small print"
+msgstr ""
+
+#: engines/private/metaengine.cpp:62
+msgid "Enhance small printed paper close-ups for improved readability."
+msgstr ""
+
+#: engines/private/metaengine.cpp:147 engines/trecision/metaengine.cpp:137
 msgid "Skip video"
 msgstr "Omitir v??deo"
 
diff --git a/po/eu.po b/po/eu.po
index ec9dd5f5ca3..e5275b31f55 100644
--- a/po/eu.po
+++ b/po/eu.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.5.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-11 22:10+0000\n"
+"POT-Creation-Date: 2026-06-13 06:14+0000\n"
 "PO-Revision-Date: 2024-02-05 18:42+0000\n"
 "Last-Translator: Josu Igoa <josuigoa at ni.eus>\n"
 "Language-Team: Basque <https://translations.scummvm.org/projects/scummvm/"
@@ -6121,7 +6121,7 @@ msgstr "Eguneraketak bilatzen..."
 #: engines/lure/metaengine.cpp:168 engines/made/metaengine.cpp:132
 #: engines/mads/metaengine.cpp:316 engines/neverhood/metaengine.cpp:185
 #: engines/ngi/metaengine.cpp:115 engines/petka/metaengine.cpp:113
-#: engines/prince/metaengine.cpp:227 engines/private/metaengine.cpp:127
+#: engines/prince/metaengine.cpp:227 engines/private/metaengine.cpp:138
 #: engines/queen/metaengine.cpp:146 engines/sherlock/metaengine.cpp:313
 #: engines/stark/metaengine.cpp:184 engines/supernova/metaengine.cpp:217
 #: engines/sword1/metaengine.cpp:266 engines/sword25/metaengine.cpp:108
@@ -7658,7 +7658,7 @@ msgstr "~A~ukerak..."
 
 #: engines/buried/metaengine.cpp:141 engines/chamber/metaengine.cpp:65
 #: engines/chamber/metaengine.cpp:71 engines/chamber/metaengine.cpp:77
-#: engines/dm/metaengine.cpp:130 engines/private/metaengine.cpp:130
+#: engines/dm/metaengine.cpp:130 engines/private/metaengine.cpp:141
 #: engines/sherlock/metaengine.cpp:1187
 #, fuzzy
 #| msgid "Interact"
@@ -12264,7 +12264,15 @@ msgstr "Hurrengo elkarrizketa aukeratu"
 msgid "Highlight clickable areas in decision scenes."
 msgstr ""
 
-#: engines/private/metaengine.cpp:136 engines/trecision/metaengine.cpp:137
+#: engines/private/metaengine.cpp:61
+msgid "Increased contrast for small print"
+msgstr ""
+
+#: engines/private/metaengine.cpp:62
+msgid "Enhance small printed paper close-ups for improved readability."
+msgstr ""
+
+#: engines/private/metaengine.cpp:147 engines/trecision/metaengine.cpp:137
 #, fuzzy
 #| msgid "Skip line"
 msgid "Skip video"
diff --git a/po/fi_FI.po b/po/fi_FI.po
index 5e49f3c2b70..a07206eb714 100644
--- a/po/fi_FI.po
+++ b/po/fi_FI.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.6.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-11 22:10+0000\n"
+"POT-Creation-Date: 2026-06-13 06:14+0000\n"
 "PO-Revision-Date: 2026-02-07 15:22+0000\n"
 "Last-Translator: Timo Mikkolainen <tmikkola at gmail.com>\n"
 "Language-Team: Finnish <https://translations.scummvm.org/projects/scummvm/"
@@ -6465,7 +6465,7 @@ msgstr "Tarkista p??ivitykset..."
 #: engines/lure/metaengine.cpp:168 engines/made/metaengine.cpp:132
 #: engines/mads/metaengine.cpp:316 engines/neverhood/metaengine.cpp:185
 #: engines/ngi/metaengine.cpp:115 engines/petka/metaengine.cpp:113
-#: engines/prince/metaengine.cpp:227 engines/private/metaengine.cpp:127
+#: engines/prince/metaengine.cpp:227 engines/private/metaengine.cpp:138
 #: engines/queen/metaengine.cpp:146 engines/sherlock/metaengine.cpp:313
 #: engines/stark/metaengine.cpp:184 engines/supernova/metaengine.cpp:217
 #: engines/sword1/metaengine.cpp:266 engines/sword25/metaengine.cpp:108
@@ -7961,7 +7961,7 @@ msgstr "Inventaarion n??pp??inkartoitukset"
 
 #: engines/buried/metaengine.cpp:141 engines/chamber/metaengine.cpp:65
 #: engines/chamber/metaengine.cpp:71 engines/chamber/metaengine.cpp:77
-#: engines/dm/metaengine.cpp:130 engines/private/metaengine.cpp:130
+#: engines/dm/metaengine.cpp:130 engines/private/metaengine.cpp:141
 #: engines/sherlock/metaengine.cpp:1187
 msgid "Select / Interact"
 msgstr "Valitse / K??yt??"
@@ -12356,7 +12356,15 @@ msgstr "Korosta esineit??"
 msgid "Highlight clickable areas in decision scenes."
 msgstr "Korosta klikattavia alueita p????t??ksentekon??kymiss??."
 
-#: engines/private/metaengine.cpp:136 engines/trecision/metaengine.cpp:137
+#: engines/private/metaengine.cpp:61
+msgid "Increased contrast for small print"
+msgstr ""
+
+#: engines/private/metaengine.cpp:62
+msgid "Enhance small printed paper close-ups for improved readability."
+msgstr ""
+
+#: engines/private/metaengine.cpp:147 engines/trecision/metaengine.cpp:137
 msgid "Skip video"
 msgstr "Ohita video"
 
diff --git a/po/fr_FR.po b/po/fr_FR.po
index 1e7e32daa5d..76a5d69a98d 100644
--- a/po/fr_FR.po
+++ b/po/fr_FR.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-11 22:10+0000\n"
+"POT-Creation-Date: 2026-06-13 06:14+0000\n"
 "PO-Revision-Date: 2026-06-08 16:22+0000\n"
 "Last-Translator: Purple T <ZEONK at hotmail.com>\n"
 "Language-Team: French <https://translations.scummvm.org/projects/scummvm/"
@@ -6383,7 +6383,7 @@ msgstr "Recherche des mises ?? jour..."
 #: engines/lure/metaengine.cpp:168 engines/made/metaengine.cpp:132
 #: engines/mads/metaengine.cpp:316 engines/neverhood/metaengine.cpp:185
 #: engines/ngi/metaengine.cpp:115 engines/petka/metaengine.cpp:113
-#: engines/prince/metaengine.cpp:227 engines/private/metaengine.cpp:127
+#: engines/prince/metaengine.cpp:227 engines/private/metaengine.cpp:138
 #: engines/queen/metaengine.cpp:146 engines/sherlock/metaengine.cpp:313
 #: engines/stark/metaengine.cpp:184 engines/supernova/metaengine.cpp:217
 #: engines/sword1/metaengine.cpp:266 engines/sword25/metaengine.cpp:108
@@ -7881,7 +7881,7 @@ msgstr "Conf. des touches Inventaire"
 
 #: engines/buried/metaengine.cpp:141 engines/chamber/metaengine.cpp:65
 #: engines/chamber/metaengine.cpp:71 engines/chamber/metaengine.cpp:77
-#: engines/dm/metaengine.cpp:130 engines/private/metaengine.cpp:130
+#: engines/dm/metaengine.cpp:130 engines/private/metaengine.cpp:141
 #: engines/sherlock/metaengine.cpp:1187
 msgid "Select / Interact"
 msgstr "S??lectionner / Interagir"
@@ -12134,7 +12134,15 @@ msgstr "Surligner les zones de d??cision"
 msgid "Highlight clickable areas in decision scenes."
 msgstr "Met en ??vidence les zones cliquables dans les sc??nes de d??cision."
 
-#: engines/private/metaengine.cpp:136 engines/trecision/metaengine.cpp:137
+#: engines/private/metaengine.cpp:61
+msgid "Increased contrast for small print"
+msgstr ""
+
+#: engines/private/metaengine.cpp:62
+msgid "Enhance small printed paper close-ups for improved readability."
+msgstr ""
+
+#: engines/private/metaengine.cpp:147 engines/trecision/metaengine.cpp:137
 msgid "Skip video"
 msgstr "Passer les vid??os"
 
diff --git a/po/gl_ES.po b/po/gl_ES.po
index 8c39a5d25cb..697849fee0c 100644
--- a/po/gl_ES.po
+++ b/po/gl_ES.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-11 22:10+0000\n"
+"POT-Creation-Date: 2026-06-13 06:14+0000\n"
 "PO-Revision-Date: 2020-09-26 21:13+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Galician <https://translations.scummvm.org/projects/scummvm/"
@@ -6055,7 +6055,7 @@ msgstr "Buscar actualizaci??ns..."
 #: engines/lure/metaengine.cpp:168 engines/made/metaengine.cpp:132
 #: engines/mads/metaengine.cpp:316 engines/neverhood/metaengine.cpp:185
 #: engines/ngi/metaengine.cpp:115 engines/petka/metaengine.cpp:113
-#: engines/prince/metaengine.cpp:227 engines/private/metaengine.cpp:127
+#: engines/prince/metaengine.cpp:227 engines/private/metaengine.cpp:138
 #: engines/queen/metaengine.cpp:146 engines/sherlock/metaengine.cpp:313
 #: engines/stark/metaengine.cpp:184 engines/supernova/metaengine.cpp:217
 #: engines/sword1/metaengine.cpp:266 engines/sword25/metaengine.cpp:108
@@ -7591,7 +7591,7 @@ msgstr "~O~pci??ns..."
 
 #: engines/buried/metaengine.cpp:141 engines/chamber/metaengine.cpp:65
 #: engines/chamber/metaengine.cpp:71 engines/chamber/metaengine.cpp:77
-#: engines/dm/metaengine.cpp:130 engines/private/metaengine.cpp:130
+#: engines/dm/metaengine.cpp:130 engines/private/metaengine.cpp:141
 #: engines/sherlock/metaengine.cpp:1187
 #, fuzzy
 msgid "Select / Interact"
@@ -12197,7 +12197,15 @@ msgstr "Destacar di??logo seguinte"
 msgid "Highlight clickable areas in decision scenes."
 msgstr ""
 
-#: engines/private/metaengine.cpp:136 engines/trecision/metaengine.cpp:137
+#: engines/private/metaengine.cpp:61
+msgid "Increased contrast for small print"
+msgstr ""
+
+#: engines/private/metaengine.cpp:62
+msgid "Enhance small printed paper close-ups for improved readability."
+msgstr ""
+
+#: engines/private/metaengine.cpp:147 engines/trecision/metaengine.cpp:137
 #, fuzzy
 #| msgid "Skip line"
 msgid "Skip video"
diff --git a/po/he.po b/po/he.po
index c20bc299eb4..9a4eef2ee96 100644
--- a/po/he.po
+++ b/po/he.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.1.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-11 22:10+0000\n"
+"POT-Creation-Date: 2026-06-13 06:14+0000\n"
 "PO-Revision-Date: 2025-04-09 20:18+0000\n"
 "Last-Translator: Niv Baehr <bloop93 at gmail.com>\n"
 "Language-Team: Hebrew <https://translations.scummvm.org/projects/scummvm/"
@@ -6013,7 +6013,7 @@ msgstr "?????????? ??????????????..."
 #: engines/lure/metaengine.cpp:168 engines/made/metaengine.cpp:132
 #: engines/mads/metaengine.cpp:316 engines/neverhood/metaengine.cpp:185
 #: engines/ngi/metaengine.cpp:115 engines/petka/metaengine.cpp:113
-#: engines/prince/metaengine.cpp:227 engines/private/metaengine.cpp:127
+#: engines/prince/metaengine.cpp:227 engines/private/metaengine.cpp:138
 #: engines/queen/metaengine.cpp:146 engines/sherlock/metaengine.cpp:313
 #: engines/stark/metaengine.cpp:184 engines/supernova/metaengine.cpp:217
 #: engines/sword1/metaengine.cpp:266 engines/sword25/metaengine.cpp:108
@@ -7583,7 +7583,7 @@ msgstr "??~??~???????????? ????????"
 
 #: engines/buried/metaengine.cpp:141 engines/chamber/metaengine.cpp:65
 #: engines/chamber/metaengine.cpp:71 engines/chamber/metaengine.cpp:77
-#: engines/dm/metaengine.cpp:130 engines/private/metaengine.cpp:130
+#: engines/dm/metaengine.cpp:130 engines/private/metaengine.cpp:141
 #: engines/sherlock/metaengine.cpp:1187
 #, fuzzy
 #| msgid "Interact"
@@ -12374,7 +12374,15 @@ msgstr "???????? ???? ?????????????? ??????"
 msgid "Highlight clickable areas in decision scenes."
 msgstr ""
 
-#: engines/private/metaengine.cpp:136 engines/trecision/metaengine.cpp:137
+#: engines/private/metaengine.cpp:61
+msgid "Increased contrast for small print"
+msgstr ""
+
+#: engines/private/metaengine.cpp:62
+msgid "Enhance small printed paper close-ups for improved readability."
+msgstr ""
+
+#: engines/private/metaengine.cpp:147 engines/trecision/metaengine.cpp:137
 #, fuzzy
 #| msgid "Skip line"
 msgid "Skip video"
diff --git a/po/hi.po b/po/hi.po
index 4cc3e83ec18..67e370a178f 100644
--- a/po/hi.po
+++ b/po/hi.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-11 22:10+0000\n"
+"POT-Creation-Date: 2026-06-13 06:14+0000\n"
 "PO-Revision-Date: 2026-05-12 22:22+0000\n"
 "Last-Translator: KARTIK SAXENA <kartiksaxena2005 at gmail.com>\n"
 "Language-Team: Hindi <https://translations.scummvm.org/projects/scummvm/"
@@ -5840,7 +5840,7 @@ msgstr ""
 #: engines/lure/metaengine.cpp:168 engines/made/metaengine.cpp:132
 #: engines/mads/metaengine.cpp:316 engines/neverhood/metaengine.cpp:185
 #: engines/ngi/metaengine.cpp:115 engines/petka/metaengine.cpp:113
-#: engines/prince/metaengine.cpp:227 engines/private/metaengine.cpp:127
+#: engines/prince/metaengine.cpp:227 engines/private/metaengine.cpp:138
 #: engines/queen/metaengine.cpp:146 engines/sherlock/metaengine.cpp:313
 #: engines/stark/metaengine.cpp:184 engines/supernova/metaengine.cpp:217
 #: engines/sword1/metaengine.cpp:266 engines/sword25/metaengine.cpp:108
@@ -7308,7 +7308,7 @@ msgstr "????????? ??????????????????(O)..."
 
 #: engines/buried/metaengine.cpp:141 engines/chamber/metaengine.cpp:65
 #: engines/chamber/metaengine.cpp:71 engines/chamber/metaengine.cpp:77
-#: engines/dm/metaengine.cpp:130 engines/private/metaengine.cpp:130
+#: engines/dm/metaengine.cpp:130 engines/private/metaengine.cpp:141
 #: engines/sherlock/metaengine.cpp:1187
 #, fuzzy
 #| msgid "Go to end of line"
@@ -11729,7 +11729,15 @@ msgstr ""
 msgid "Highlight clickable areas in decision scenes."
 msgstr ""
 
-#: engines/private/metaengine.cpp:136 engines/trecision/metaengine.cpp:137
+#: engines/private/metaengine.cpp:61
+msgid "Increased contrast for small print"
+msgstr ""
+
+#: engines/private/metaengine.cpp:62
+msgid "Enhance small printed paper close-ups for improved readability."
+msgstr ""
+
+#: engines/private/metaengine.cpp:147 engines/trecision/metaengine.cpp:137
 #, fuzzy
 #| msgid "Skip autosave"
 msgid "Skip video"
diff --git a/po/hu_HU.po b/po/hu_HU.po
index 0ec54ddd0c0..7238b8e6f7e 100644
--- a/po/hu_HU.po
+++ b/po/hu_HU.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-11 22:10+0000\n"
+"POT-Creation-Date: 2026-06-13 06:14+0000\n"
 "PO-Revision-Date: 2025-03-07 00:18+0000\n"
 "Last-Translator: Hov??nszki Tam??s <gahex68379 at lofiey.com>\n"
 "Language-Team: Hungarian <https://translations.scummvm.org/projects/scummvm/"
@@ -6553,7 +6553,7 @@ msgstr "Friss??t??sek keres??se..."
 #: engines/lure/metaengine.cpp:168 engines/made/metaengine.cpp:132
 #: engines/mads/metaengine.cpp:316 engines/neverhood/metaengine.cpp:185
 #: engines/ngi/metaengine.cpp:115 engines/petka/metaengine.cpp:113
-#: engines/prince/metaengine.cpp:227 engines/private/metaengine.cpp:127
+#: engines/prince/metaengine.cpp:227 engines/private/metaengine.cpp:138
 #: engines/queen/metaengine.cpp:146 engines/sherlock/metaengine.cpp:313
 #: engines/stark/metaengine.cpp:184 engines/supernova/metaengine.cpp:217
 #: engines/sword1/metaengine.cpp:266 engines/sword25/metaengine.cpp:108
@@ -8100,7 +8100,7 @@ msgstr "Index gombkioszt??s"
 
 #: engines/buried/metaengine.cpp:141 engines/chamber/metaengine.cpp:65
 #: engines/chamber/metaengine.cpp:71 engines/chamber/metaengine.cpp:77
-#: engines/dm/metaengine.cpp:130 engines/private/metaengine.cpp:130
+#: engines/dm/metaengine.cpp:130 engines/private/metaengine.cpp:141
 #: engines/sherlock/metaengine.cpp:1187
 #, fuzzy
 #| msgid "Interact Mode"
@@ -12838,7 +12838,15 @@ msgstr "K??vetkez?? dial??gus kiemel??se"
 msgid "Highlight clickable areas in decision scenes."
 msgstr ""
 
-#: engines/private/metaengine.cpp:136 engines/trecision/metaengine.cpp:137
+#: engines/private/metaengine.cpp:61
+msgid "Increased contrast for small print"
+msgstr ""
+
+#: engines/private/metaengine.cpp:62
+msgid "Enhance small printed paper close-ups for improved readability."
+msgstr ""
+
+#: engines/private/metaengine.cpp:147 engines/trecision/metaengine.cpp:137
 msgid "Skip video"
 msgstr "Vide?? ??tugr??sa"
 
diff --git a/po/it_IT.po b/po/it_IT.po
index 59641737d7e..a93740a6ae1 100644
--- a/po/it_IT.po
+++ b/po/it_IT.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-11 22:10+0000\n"
+"POT-Creation-Date: 2026-06-13 06:14+0000\n"
 "PO-Revision-Date: 2026-06-10 20:26+0000\n"
 "Last-Translator: Walter Agazzi <walter.agazzi at protonmail.com>\n"
 "Language-Team: Italian <https://translations.scummvm.org/projects/scummvm/"
@@ -6350,7 +6350,7 @@ msgstr "Cerca aggiornamenti..."
 #: engines/lure/metaengine.cpp:168 engines/made/metaengine.cpp:132
 #: engines/mads/metaengine.cpp:316 engines/neverhood/metaengine.cpp:185
 #: engines/ngi/metaengine.cpp:115 engines/petka/metaengine.cpp:113
-#: engines/prince/metaengine.cpp:227 engines/private/metaengine.cpp:127
+#: engines/prince/metaengine.cpp:227 engines/private/metaengine.cpp:138
 #: engines/queen/metaengine.cpp:146 engines/sherlock/metaengine.cpp:313
 #: engines/stark/metaengine.cpp:184 engines/supernova/metaengine.cpp:217
 #: engines/sword1/metaengine.cpp:266 engines/sword25/metaengine.cpp:108
@@ -7859,7 +7859,7 @@ msgstr "Mappature tasti inventario"
 
 #: engines/buried/metaengine.cpp:141 engines/chamber/metaengine.cpp:65
 #: engines/chamber/metaengine.cpp:71 engines/chamber/metaengine.cpp:77
-#: engines/dm/metaengine.cpp:130 engines/private/metaengine.cpp:130
+#: engines/dm/metaengine.cpp:130 engines/private/metaengine.cpp:141
 #: engines/sherlock/metaengine.cpp:1187
 msgid "Select / Interact"
 msgstr "Selezione / Interazione"
@@ -12091,7 +12091,15 @@ msgstr "Evidenzia aree decisionali"
 msgid "Highlight clickable areas in decision scenes."
 msgstr "Evidenzia le aree cliccabili nelle schermate delle decisioni."
 
-#: engines/private/metaengine.cpp:136 engines/trecision/metaengine.cpp:137
+#: engines/private/metaengine.cpp:61
+msgid "Increased contrast for small print"
+msgstr ""
+
+#: engines/private/metaengine.cpp:62
+msgid "Enhance small printed paper close-ups for improved readability."
+msgstr ""
+
+#: engines/private/metaengine.cpp:147 engines/trecision/metaengine.cpp:137
 msgid "Skip video"
 msgstr "Salta video"
 
diff --git a/po/ja.po b/po/ja.po
index 4852d05d7fe..65e256f6fc4 100644
--- a/po/ja.po
+++ b/po/ja.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-11 22:10+0000\n"
+"POT-Creation-Date: 2026-06-13 06:14+0000\n"
 "PO-Revision-Date: 2026-06-04 07:22+0000\n"
 "Last-Translator: VAN-Gluon <van.gluonp at gmail.com>\n"
 "Language-Team: Japanese <https://translations.scummvm.org/projects/scummvm/"
@@ -6204,7 +6204,7 @@ msgstr "??????????????????????????????..."
 #: engines/lure/metaengine.cpp:168 engines/made/metaengine.cpp:132
 #: engines/mads/metaengine.cpp:316 engines/neverhood/metaengine.cpp:185
 #: engines/ngi/metaengine.cpp:115 engines/petka/metaengine.cpp:113
-#: engines/prince/metaengine.cpp:227 engines/private/metaengine.cpp:127
+#: engines/prince/metaengine.cpp:227 engines/private/metaengine.cpp:138
 #: engines/queen/metaengine.cpp:146 engines/sherlock/metaengine.cpp:313
 #: engines/stark/metaengine.cpp:184 engines/supernova/metaengine.cpp:217
 #: engines/sword1/metaengine.cpp:266 engines/sword25/metaengine.cpp:108
@@ -7704,7 +7704,7 @@ msgstr "???????????????????????????"
 
 #: engines/buried/metaengine.cpp:141 engines/chamber/metaengine.cpp:65
 #: engines/chamber/metaengine.cpp:71 engines/chamber/metaengine.cpp:77
-#: engines/dm/metaengine.cpp:130 engines/private/metaengine.cpp:130
+#: engines/dm/metaengine.cpp:130 engines/private/metaengine.cpp:141
 #: engines/sherlock/metaengine.cpp:1187
 #, fuzzy
 #| msgid "Interact Mode"
@@ -12462,7 +12462,15 @@ msgstr "???????????????????????????????????????"
 msgid "Highlight clickable areas in decision scenes."
 msgstr ""
 
-#: engines/private/metaengine.cpp:136 engines/trecision/metaengine.cpp:137
+#: engines/private/metaengine.cpp:61
+msgid "Increased contrast for small print"
+msgstr ""
+
+#: engines/private/metaengine.cpp:62
+msgid "Enhance small printed paper close-ups for improved readability."
+msgstr ""
+
+#: engines/private/metaengine.cpp:147 engines/trecision/metaengine.cpp:137
 #, fuzzy
 #| msgid "Skip line"
 msgid "Skip video"
diff --git a/po/ka.po b/po/ka.po
index fa608855591..323041e087d 100644
--- a/po/ka.po
+++ b/po/ka.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-11 22:10+0000\n"
+"POT-Creation-Date: 2026-06-13 06:14+0000\n"
 "PO-Revision-Date: 2026-06-05 10:22+0000\n"
 "Last-Translator: NorwayFun <temuri.doghonadze at gmail.com>\n"
 "Language-Team: Georgian <https://translations.scummvm.org/projects/scummvm/"
@@ -5665,7 +5665,7 @@ msgstr "???????????????????????????????????? ???????????????????????????..."
 #: engines/lure/metaengine.cpp:168 engines/made/metaengine.cpp:132
 #: engines/mads/metaengine.cpp:316 engines/neverhood/metaengine.cpp:185
 #: engines/ngi/metaengine.cpp:115 engines/petka/metaengine.cpp:113
-#: engines/prince/metaengine.cpp:227 engines/private/metaengine.cpp:127
+#: engines/prince/metaengine.cpp:227 engines/private/metaengine.cpp:138
 #: engines/queen/metaengine.cpp:146 engines/sherlock/metaengine.cpp:313
 #: engines/stark/metaengine.cpp:184 engines/supernova/metaengine.cpp:217
 #: engines/sword1/metaengine.cpp:266 engines/sword25/metaengine.cpp:108
@@ -7062,7 +7062,7 @@ msgstr "?????????????????????????????? ????????????????????????????????? ??????
 
 #: engines/buried/metaengine.cpp:141 engines/chamber/metaengine.cpp:65
 #: engines/chamber/metaengine.cpp:71 engines/chamber/metaengine.cpp:77
-#: engines/dm/metaengine.cpp:130 engines/private/metaengine.cpp:130
+#: engines/dm/metaengine.cpp:130 engines/private/metaengine.cpp:141
 #: engines/sherlock/metaengine.cpp:1187
 msgid "Select / Interact"
 msgstr "?????????????????? / ??????????????????????????????"
@@ -11008,7 +11008,15 @@ msgstr "????????????????????????????????? ????????????????????? ???????????????
 msgid "Highlight clickable areas in decision scenes."
 msgstr ""
 
-#: engines/private/metaengine.cpp:136 engines/trecision/metaengine.cpp:137
+#: engines/private/metaengine.cpp:61
+msgid "Increased contrast for small print"
+msgstr ""
+
+#: engines/private/metaengine.cpp:62
+msgid "Enhance small printed paper close-ups for improved readability."
+msgstr ""
+
+#: engines/private/metaengine.cpp:147 engines/trecision/metaengine.cpp:137
 msgid "Skip video"
 msgstr "?????????????????? ??????????????????????????????"
 
diff --git a/po/ko.po b/po/ko.po
index ad3398bfeb5..6061674ff79 100644
--- a/po/ko.po
+++ b/po/ko.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-11 22:10+0000\n"
+"POT-Creation-Date: 2026-06-13 06:14+0000\n"
 "PO-Revision-Date: 2026-05-19 20:22+0000\n"
 "Last-Translator: Hoseok Seo <ddinghoya at gmail.com>\n"
 "Language-Team: Korean <https://translations.scummvm.org/projects/scummvm/"
@@ -6215,7 +6215,7 @@ msgstr "???????????? ?????? ???..."
 #: engines/lure/metaengine.cpp:168 engines/made/metaengine.cpp:132
 #: engines/mads/metaengine.cpp:316 engines/neverhood/metaengine.cpp:185
 #: engines/ngi/metaengine.cpp:115 engines/petka/metaengine.cpp:113
-#: engines/prince/metaengine.cpp:227 engines/private/metaengine.cpp:127
+#: engines/prince/metaengine.cpp:227 engines/private/metaengine.cpp:138
 #: engines/queen/metaengine.cpp:146 engines/sherlock/metaengine.cpp:313
 #: engines/stark/metaengine.cpp:184 engines/supernova/metaengine.cpp:217
 #: engines/sword1/metaengine.cpp:266 engines/sword25/metaengine.cpp:108
@@ -7678,7 +7678,7 @@ msgstr "???????????? ?????????"
 
 #: engines/buried/metaengine.cpp:141 engines/chamber/metaengine.cpp:65
 #: engines/chamber/metaengine.cpp:71 engines/chamber/metaengine.cpp:77
-#: engines/dm/metaengine.cpp:130 engines/private/metaengine.cpp:130
+#: engines/dm/metaengine.cpp:130 engines/private/metaengine.cpp:141
 #: engines/sherlock/metaengine.cpp:1187
 msgid "Select / Interact"
 msgstr "?????? / ??????"
@@ -11827,7 +11827,15 @@ msgstr "?????? ?????? ??????"
 msgid "Highlight clickable areas in decision scenes."
 msgstr "?????? ???????????? ?????? ????????? ????????? ?????? ???????????????."
 
-#: engines/private/metaengine.cpp:136 engines/trecision/metaengine.cpp:137
+#: engines/private/metaengine.cpp:61
+msgid "Increased contrast for small print"
+msgstr ""
+
+#: engines/private/metaengine.cpp:62
+msgid "Enhance small printed paper close-ups for improved readability."
+msgstr ""
+
+#: engines/private/metaengine.cpp:147 engines/trecision/metaengine.cpp:137
 msgid "Skip video"
 msgstr "????????? ????????????"
 
diff --git a/po/nb_NO.po b/po/nb_NO.po
index f3527d9f12f..1e0639502f2 100644
--- a/po/nb_NO.po
+++ b/po/nb_NO.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-11 22:10+0000\n"
+"POT-Creation-Date: 2026-06-13 06:14+0000\n"
 "PO-Revision-Date: 2026-02-07 15:22+0000\n"
 "Last-Translator: Einar Johan Tr??an S??m??en <einarjohants at gmail.com>\n"
 "Language-Team: Norwegian Bokm??l <https://translations.scummvm.org/projects/"
@@ -6614,7 +6614,7 @@ msgstr "Sjekk for oppdateringer..."
 #: engines/lure/metaengine.cpp:168 engines/made/metaengine.cpp:132
 #: engines/mads/metaengine.cpp:316 engines/neverhood/metaengine.cpp:185
 #: engines/ngi/metaengine.cpp:115 engines/petka/metaengine.cpp:113
-#: engines/prince/metaengine.cpp:227 engines/private/metaengine.cpp:127
+#: engines/prince/metaengine.cpp:227 engines/private/metaengine.cpp:138
 #: engines/queen/metaengine.cpp:146 engines/sherlock/metaengine.cpp:313
 #: engines/stark/metaengine.cpp:184 engines/supernova/metaengine.cpp:217
 #: engines/sword1/metaengine.cpp:266 engines/sword25/metaengine.cpp:108
@@ -8166,7 +8166,7 @@ msgstr "Indeks-tasteoppsett"
 
 #: engines/buried/metaengine.cpp:141 engines/chamber/metaengine.cpp:65
 #: engines/chamber/metaengine.cpp:71 engines/chamber/metaengine.cpp:77
-#: engines/dm/metaengine.cpp:130 engines/private/metaengine.cpp:130
+#: engines/dm/metaengine.cpp:130 engines/private/metaengine.cpp:141
 #: engines/sherlock/metaengine.cpp:1187
 #, fuzzy
 #| msgid "Interact Mode"
@@ -12917,7 +12917,15 @@ msgstr "Merk neste dialog"
 msgid "Highlight clickable areas in decision scenes."
 msgstr ""
 
-#: engines/private/metaengine.cpp:136 engines/trecision/metaengine.cpp:137
+#: engines/private/metaengine.cpp:61
+msgid "Increased contrast for small print"
+msgstr ""
+
+#: engines/private/metaengine.cpp:62
+msgid "Enhance small printed paper close-ups for improved readability."
+msgstr ""
+
+#: engines/private/metaengine.cpp:147 engines/trecision/metaengine.cpp:137
 #, fuzzy
 #| msgid "Skip line"
 msgid "Skip video"
diff --git a/po/nl_NL.po b/po/nl_NL.po
index 474cce9c45e..ef3c8ce5352 100644
--- a/po/nl_NL.po
+++ b/po/nl_NL.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.9.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-11 22:10+0000\n"
+"POT-Creation-Date: 2026-06-13 06:14+0000\n"
 "PO-Revision-Date: 2026-02-11 14:23+0000\n"
 "Last-Translator: SecularSteve <fairfull.playing at gmail.com>\n"
 "Language-Team: Dutch <https://translations.scummvm.org/projects/scummvm/"
@@ -6289,7 +6289,7 @@ msgstr "Controleren op updates..."
 #: engines/lure/metaengine.cpp:168 engines/made/metaengine.cpp:132
 #: engines/mads/metaengine.cpp:316 engines/neverhood/metaengine.cpp:185
 #: engines/ngi/metaengine.cpp:115 engines/petka/metaengine.cpp:113
-#: engines/prince/metaengine.cpp:227 engines/private/metaengine.cpp:127
+#: engines/prince/metaengine.cpp:227 engines/private/metaengine.cpp:138
 #: engines/queen/metaengine.cpp:146 engines/sherlock/metaengine.cpp:313
 #: engines/stark/metaengine.cpp:184 engines/supernova/metaengine.cpp:217
 #: engines/sword1/metaengine.cpp:266 engines/sword25/metaengine.cpp:108
@@ -7786,7 +7786,7 @@ msgstr "Inventaris toetsenbordindelingen"
 
 #: engines/buried/metaengine.cpp:141 engines/chamber/metaengine.cpp:65
 #: engines/chamber/metaengine.cpp:71 engines/chamber/metaengine.cpp:77
-#: engines/dm/metaengine.cpp:130 engines/private/metaengine.cpp:130
+#: engines/dm/metaengine.cpp:130 engines/private/metaengine.cpp:141
 #: engines/sherlock/metaengine.cpp:1187
 msgid "Select / Interact"
 msgstr "Selecteren / Interactie"
@@ -12136,7 +12136,15 @@ msgstr "Beslissingsgebieden markeren"
 msgid "Highlight clickable areas in decision scenes."
 msgstr "Markeer klikbare gebieden in beslissingssc??nes."
 
-#: engines/private/metaengine.cpp:136 engines/trecision/metaengine.cpp:137
+#: engines/private/metaengine.cpp:61
+msgid "Increased contrast for small print"
+msgstr ""
+
+#: engines/private/metaengine.cpp:62
+msgid "Enhance small printed paper close-ups for improved readability."
+msgstr ""
+
+#: engines/private/metaengine.cpp:147 engines/trecision/metaengine.cpp:137
 msgid "Skip video"
 msgstr "Video overslaan"
 
diff --git a/po/nn_NO.po b/po/nn_NO.po
index 2a925c388e5..3b1d24a3338 100644
--- a/po/nn_NO.po
+++ b/po/nn_NO.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-11 22:10+0000\n"
+"POT-Creation-Date: 2026-06-13 06:14+0000\n"
 "PO-Revision-Date: 2023-12-14 14:28+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Norwegian Nynorsk <https://translations.scummvm.org/projects/"
@@ -6017,7 +6017,7 @@ msgstr "SJ?? etter oppdateringar..."
 #: engines/lure/metaengine.cpp:168 engines/made/metaengine.cpp:132
 #: engines/mads/metaengine.cpp:316 engines/neverhood/metaengine.cpp:185
 #: engines/ngi/metaengine.cpp:115 engines/petka/metaengine.cpp:113
-#: engines/prince/metaengine.cpp:227 engines/private/metaengine.cpp:127
+#: engines/prince/metaengine.cpp:227 engines/private/metaengine.cpp:138
 #: engines/queen/metaengine.cpp:146 engines/sherlock/metaengine.cpp:313
 #: engines/stark/metaengine.cpp:184 engines/supernova/metaengine.cpp:217
 #: engines/sword1/metaengine.cpp:266 engines/sword25/metaengine.cpp:108
@@ -7541,7 +7541,7 @@ msgstr "Spelval"
 
 #: engines/buried/metaengine.cpp:141 engines/chamber/metaengine.cpp:65
 #: engines/chamber/metaengine.cpp:71 engines/chamber/metaengine.cpp:77
-#: engines/dm/metaengine.cpp:130 engines/private/metaengine.cpp:130
+#: engines/dm/metaengine.cpp:130 engines/private/metaengine.cpp:141
 #: engines/sherlock/metaengine.cpp:1187
 #, fuzzy
 msgid "Select / Interact"
@@ -12116,7 +12116,15 @@ msgstr "Merk neste dialog"
 msgid "Highlight clickable areas in decision scenes."
 msgstr ""
 
-#: engines/private/metaengine.cpp:136 engines/trecision/metaengine.cpp:137
+#: engines/private/metaengine.cpp:61
+msgid "Increased contrast for small print"
+msgstr ""
+
+#: engines/private/metaengine.cpp:62
+msgid "Enhance small printed paper close-ups for improved readability."
+msgstr ""
+
+#: engines/private/metaengine.cpp:147 engines/trecision/metaengine.cpp:137
 #, fuzzy
 #| msgid "Skip line"
 msgid "Skip video"
diff --git a/po/pl_PL.po b/po/pl_PL.po
index 8c8f1586816..6481d4d4c03 100644
--- a/po/pl_PL.po
+++ b/po/pl_PL.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-11 22:10+0000\n"
+"POT-Creation-Date: 2026-06-13 06:14+0000\n"
 "PO-Revision-Date: 2026-04-17 00:22+0000\n"
 "Last-Translator: SkiffPL <skiff at windowslive.com>\n"
 "Language-Team: Polish <https://translations.scummvm.org/projects/scummvm/"
@@ -6301,7 +6301,7 @@ msgstr "Szukaj aktualizacji..."
 #: engines/lure/metaengine.cpp:168 engines/made/metaengine.cpp:132
 #: engines/mads/metaengine.cpp:316 engines/neverhood/metaengine.cpp:185
 #: engines/ngi/metaengine.cpp:115 engines/petka/metaengine.cpp:113
-#: engines/prince/metaengine.cpp:227 engines/private/metaengine.cpp:127
+#: engines/prince/metaengine.cpp:227 engines/private/metaengine.cpp:138
 #: engines/queen/metaengine.cpp:146 engines/sherlock/metaengine.cpp:313
 #: engines/stark/metaengine.cpp:184 engines/supernova/metaengine.cpp:217
 #: engines/sword1/metaengine.cpp:266 engines/sword25/metaengine.cpp:108
@@ -7800,7 +7800,7 @@ msgstr "Mapowanie klawiszy ekwipunku"
 
 #: engines/buried/metaengine.cpp:141 engines/chamber/metaengine.cpp:65
 #: engines/chamber/metaengine.cpp:71 engines/chamber/metaengine.cpp:77
-#: engines/dm/metaengine.cpp:130 engines/private/metaengine.cpp:130
+#: engines/dm/metaengine.cpp:130 engines/private/metaengine.cpp:141
 #: engines/sherlock/metaengine.cpp:1187
 msgid "Select / Interact"
 msgstr "Zaznaczanie / Interkacja"
@@ -12323,7 +12323,15 @@ msgstr "Pod??wietl elementy"
 msgid "Highlight clickable areas in decision scenes."
 msgstr ""
 
-#: engines/private/metaengine.cpp:136 engines/trecision/metaengine.cpp:137
+#: engines/private/metaengine.cpp:61
+msgid "Increased contrast for small print"
+msgstr ""
+
+#: engines/private/metaengine.cpp:62
+msgid "Enhance small printed paper close-ups for improved readability."
+msgstr ""
+
+#: engines/private/metaengine.cpp:147 engines/trecision/metaengine.cpp:137
 msgid "Skip video"
 msgstr "Pomi?? wideo"
 
diff --git a/po/pt_BR.po b/po/pt_BR.po
index 1486a7c841e..336737a649a 100644
--- a/po/pt_BR.po
+++ b/po/pt_BR.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-11 22:10+0000\n"
+"POT-Creation-Date: 2026-06-13 06:14+0000\n"
 "PO-Revision-Date: 2024-11-16 02:13+0000\n"
 "Last-Translator: Marcel Souza Lemes <marcosoutsider at gmail.com>\n"
 "Language-Team: Portuguese (Brazil) <https://translations.scummvm.org/"
@@ -6578,7 +6578,7 @@ msgstr "Procurar por Atualiza????es..."
 #: engines/lure/metaengine.cpp:168 engines/made/metaengine.cpp:132
 #: engines/mads/metaengine.cpp:316 engines/neverhood/metaengine.cpp:185
 #: engines/ngi/metaengine.cpp:115 engines/petka/metaengine.cpp:113
-#: engines/prince/metaengine.cpp:227 engines/private/metaengine.cpp:127
+#: engines/prince/metaengine.cpp:227 engines/private/metaengine.cpp:138
 #: engines/queen/metaengine.cpp:146 engines/sherlock/metaengine.cpp:313
 #: engines/stark/metaengine.cpp:184 engines/supernova/metaengine.cpp:217
 #: engines/sword1/metaengine.cpp:266 engines/sword25/metaengine.cpp:108
@@ -8137,7 +8137,7 @@ msgstr "Mapeamentos de teclas de ??ndice"
 
 #: engines/buried/metaengine.cpp:141 engines/chamber/metaengine.cpp:65
 #: engines/chamber/metaengine.cpp:71 engines/chamber/metaengine.cpp:77
-#: engines/dm/metaengine.cpp:130 engines/private/metaengine.cpp:130
+#: engines/dm/metaengine.cpp:130 engines/private/metaengine.cpp:141
 #: engines/sherlock/metaengine.cpp:1187
 #, fuzzy
 #| msgid "Interact Mode"
@@ -12886,7 +12886,15 @@ msgstr "Destacar pr??ximo di??logo"
 msgid "Highlight clickable areas in decision scenes."
 msgstr ""
 
-#: engines/private/metaengine.cpp:136 engines/trecision/metaengine.cpp:137
+#: engines/private/metaengine.cpp:61
+msgid "Increased contrast for small print"
+msgstr ""
+
+#: engines/private/metaengine.cpp:62
+msgid "Enhance small printed paper close-ups for improved readability."
+msgstr ""
+
+#: engines/private/metaengine.cpp:147 engines/trecision/metaengine.cpp:137
 msgid "Skip video"
 msgstr "Pular v??deo"
 
diff --git a/po/pt_PT.po b/po/pt_PT.po
index 9da8c0cfb89..01e59142ec1 100644
--- a/po/pt_PT.po
+++ b/po/pt_PT.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.10.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-11 22:10+0000\n"
+"POT-Creation-Date: 2026-06-13 06:14+0000\n"
 "PO-Revision-Date: 2022-06-05 14:09+0000\n"
 "Last-Translator: Daniel <supsuper at gmail.com>\n"
 "Language-Team: Portuguese (Portugal) <https://translations.scummvm.org/"
@@ -6040,7 +6040,7 @@ msgstr "Procurar por Atualiza????es..."
 #: engines/lure/metaengine.cpp:168 engines/made/metaengine.cpp:132
 #: engines/mads/metaengine.cpp:316 engines/neverhood/metaengine.cpp:185
 #: engines/ngi/metaengine.cpp:115 engines/petka/metaengine.cpp:113
-#: engines/prince/metaengine.cpp:227 engines/private/metaengine.cpp:127
+#: engines/prince/metaengine.cpp:227 engines/private/metaengine.cpp:138
 #: engines/queen/metaengine.cpp:146 engines/sherlock/metaengine.cpp:313
 #: engines/stark/metaengine.cpp:184 engines/supernova/metaengine.cpp:217
 #: engines/sword1/metaengine.cpp:266 engines/sword25/metaengine.cpp:108
@@ -7570,7 +7570,7 @@ msgstr "Op????es do Jogo"
 
 #: engines/buried/metaengine.cpp:141 engines/chamber/metaengine.cpp:65
 #: engines/chamber/metaengine.cpp:71 engines/chamber/metaengine.cpp:77
-#: engines/dm/metaengine.cpp:130 engines/private/metaengine.cpp:130
+#: engines/dm/metaengine.cpp:130 engines/private/metaengine.cpp:141
 #: engines/sherlock/metaengine.cpp:1187
 #, fuzzy
 #| msgid "Interact"
@@ -12213,7 +12213,15 @@ msgstr ""
 msgid "Highlight clickable areas in decision scenes."
 msgstr ""
 
-#: engines/private/metaengine.cpp:136 engines/trecision/metaengine.cpp:137
+#: engines/private/metaengine.cpp:61
+msgid "Increased contrast for small print"
+msgstr ""
+
+#: engines/private/metaengine.cpp:62
+msgid "Enhance small printed paper close-ups for improved readability."
+msgstr ""
+
+#: engines/private/metaengine.cpp:147 engines/trecision/metaengine.cpp:137
 #, fuzzy
 #| msgid "Skip line"
 msgid "Skip video"
diff --git a/po/ro.po b/po/ro.po
index 9e5297972e8..409ab4699a3 100644
--- a/po/ro.po
+++ b/po/ro.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.10.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-11 22:10+0000\n"
+"POT-Creation-Date: 2026-06-13 06:14+0000\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: Automatically generated\n"
 "Language-Team: none\n"
@@ -5636,7 +5636,7 @@ msgstr ""
 #: engines/lure/metaengine.cpp:168 engines/made/metaengine.cpp:132
 #: engines/mads/metaengine.cpp:316 engines/neverhood/metaengine.cpp:185
 #: engines/ngi/metaengine.cpp:115 engines/petka/metaengine.cpp:113
-#: engines/prince/metaengine.cpp:227 engines/private/metaengine.cpp:127
+#: engines/prince/metaengine.cpp:227 engines/private/metaengine.cpp:138
 #: engines/queen/metaengine.cpp:146 engines/sherlock/metaengine.cpp:313
 #: engines/stark/metaengine.cpp:184 engines/supernova/metaengine.cpp:217
 #: engines/sword1/metaengine.cpp:266 engines/sword25/metaengine.cpp:108
@@ -7027,7 +7027,7 @@ msgstr ""
 
 #: engines/buried/metaengine.cpp:141 engines/chamber/metaengine.cpp:65
 #: engines/chamber/metaengine.cpp:71 engines/chamber/metaengine.cpp:77
-#: engines/dm/metaengine.cpp:130 engines/private/metaengine.cpp:130
+#: engines/dm/metaengine.cpp:130 engines/private/metaengine.cpp:141
 #: engines/sherlock/metaengine.cpp:1187
 msgid "Select / Interact"
 msgstr ""
@@ -10973,7 +10973,15 @@ msgstr ""
 msgid "Highlight clickable areas in decision scenes."
 msgstr ""
 
-#: engines/private/metaengine.cpp:136 engines/trecision/metaengine.cpp:137
+#: engines/private/metaengine.cpp:61
+msgid "Increased contrast for small print"
+msgstr ""
+
+#: engines/private/metaengine.cpp:62
+msgid "Enhance small printed paper close-ups for improved readability."
+msgstr ""
+
+#: engines/private/metaengine.cpp:147 engines/trecision/metaengine.cpp:137
 msgid "Skip video"
 msgstr ""
 
diff --git a/po/ru_RU.po b/po/ru_RU.po
index 002ff58afdd..9ee62e2593a 100644
--- a/po/ru_RU.po
+++ b/po/ru_RU.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.8.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-11 22:10+0000\n"
+"POT-Creation-Date: 2026-06-13 06:14+0000\n"
 "PO-Revision-Date: 2026-05-06 08:54+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Russian <https://translations.scummvm.org/projects/scummvm/"
@@ -6311,7 +6311,7 @@ msgstr "?????????????????? ????????????????????..."
 #: engines/lure/metaengine.cpp:168 engines/made/metaengine.cpp:132
 #: engines/mads/metaengine.cpp:316 engines/neverhood/metaengine.cpp:185
 #: engines/ngi/metaengine.cpp:115 engines/petka/metaengine.cpp:113
-#: engines/prince/metaengine.cpp:227 engines/private/metaengine.cpp:127
+#: engines/prince/metaengine.cpp:227 engines/private/metaengine.cpp:138
 #: engines/queen/metaengine.cpp:146 engines/sherlock/metaengine.cpp:313
 #: engines/stark/metaengine.cpp:184 engines/supernova/metaengine.cpp:217
 #: engines/sword1/metaengine.cpp:266 engines/sword25/metaengine.cpp:108
@@ -7800,7 +7800,7 @@ msgstr "?????????????????? ???????????? ??????????????????"
 
 #: engines/buried/metaengine.cpp:141 engines/chamber/metaengine.cpp:65
 #: engines/chamber/metaengine.cpp:71 engines/chamber/metaengine.cpp:77
-#: engines/dm/metaengine.cpp:130 engines/private/metaengine.cpp:130
+#: engines/dm/metaengine.cpp:130 engines/private/metaengine.cpp:141
 #: engines/sherlock/metaengine.cpp:1187
 msgid "Select / Interact"
 msgstr "?????????????? / ??????????????????????????????????"
@@ -11994,7 +11994,15 @@ msgstr "???????????????????? ?????????????? ??????????????"
 msgid "Highlight clickable areas in decision scenes."
 msgstr "???????????????? ??????????????, ???? ?????????????? ?????????? ???????????? ?? ???????????? ???????????????? ??????????????."
 
-#: engines/private/metaengine.cpp:136 engines/trecision/metaengine.cpp:137
+#: engines/private/metaengine.cpp:61
+msgid "Increased contrast for small print"
+msgstr ""
+
+#: engines/private/metaengine.cpp:62
+msgid "Enhance small printed paper close-ups for improved readability."
+msgstr ""
+
+#: engines/private/metaengine.cpp:147 engines/trecision/metaengine.cpp:137
 msgid "Skip video"
 msgstr "???????????????????? ??????????"
 
diff --git a/po/sv_SE.po b/po/sv_SE.po
index 95ea05675f9..fd3ffa163ab 100644
--- a/po/sv_SE.po
+++ b/po/sv_SE.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.5.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-11 22:10+0000\n"
+"POT-Creation-Date: 2026-06-13 06:14+0000\n"
 "PO-Revision-Date: 2026-04-19 13:22+0000\n"
 "Last-Translator: Daniel Nylander <po at danielnylander.se>\n"
 "Language-Team: Swedish <https://translations.scummvm.org/projects/scummvm/"
@@ -6288,7 +6288,7 @@ msgstr "Leta efter uppdateringar..."
 #: engines/lure/metaengine.cpp:168 engines/made/metaengine.cpp:132
 #: engines/mads/metaengine.cpp:316 engines/neverhood/metaengine.cpp:185
 #: engines/ngi/metaengine.cpp:115 engines/petka/metaengine.cpp:113
-#: engines/prince/metaengine.cpp:227 engines/private/metaengine.cpp:127
+#: engines/prince/metaengine.cpp:227 engines/private/metaengine.cpp:138
 #: engines/queen/metaengine.cpp:146 engines/sherlock/metaengine.cpp:313
 #: engines/stark/metaengine.cpp:184 engines/supernova/metaengine.cpp:217
 #: engines/sword1/metaengine.cpp:266 engines/sword25/metaengine.cpp:108
@@ -7775,7 +7775,7 @@ msgstr "Kortkommandon f??r inventarier"
 
 #: engines/buried/metaengine.cpp:141 engines/chamber/metaengine.cpp:65
 #: engines/chamber/metaengine.cpp:71 engines/chamber/metaengine.cpp:77
-#: engines/dm/metaengine.cpp:130 engines/private/metaengine.cpp:130
+#: engines/dm/metaengine.cpp:130 engines/private/metaengine.cpp:141
 #: engines/sherlock/metaengine.cpp:1187
 msgid "Select / Interact"
 msgstr "V??lj / Interagera"
@@ -12054,7 +12054,15 @@ msgstr "Markera beslutsomr??den"
 msgid "Highlight clickable areas in decision scenes."
 msgstr "Markera klickbara omr??den i beslutsscener."
 
-#: engines/private/metaengine.cpp:136 engines/trecision/metaengine.cpp:137
+#: engines/private/metaengine.cpp:61
+msgid "Increased contrast for small print"
+msgstr ""
+
+#: engines/private/metaengine.cpp:62
+msgid "Enhance small printed paper close-ups for improved readability."
+msgstr ""
+
+#: engines/private/metaengine.cpp:147 engines/trecision/metaengine.cpp:137
 msgid "Skip video"
 msgstr "Hoppa ??ver video"
 
diff --git a/po/tr.po b/po/tr.po
index c334aca2367..32f9df32be5 100644
--- a/po/tr.po
+++ b/po/tr.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-11 22:10+0000\n"
+"POT-Creation-Date: 2026-06-13 06:14+0000\n"
 "PO-Revision-Date: 2024-10-10 12:33+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Turkish <https://translations.scummvm.org/projects/scummvm/"
@@ -5966,7 +5966,7 @@ msgstr ""
 #: engines/lure/metaengine.cpp:168 engines/made/metaengine.cpp:132
 #: engines/mads/metaengine.cpp:316 engines/neverhood/metaengine.cpp:185
 #: engines/ngi/metaengine.cpp:115 engines/petka/metaengine.cpp:113
-#: engines/prince/metaengine.cpp:227 engines/private/metaengine.cpp:127
+#: engines/prince/metaengine.cpp:227 engines/private/metaengine.cpp:138
 #: engines/queen/metaengine.cpp:146 engines/sherlock/metaengine.cpp:313
 #: engines/stark/metaengine.cpp:184 engines/supernova/metaengine.cpp:217
 #: engines/sword1/metaengine.cpp:266 engines/sword25/metaengine.cpp:108
@@ -7440,7 +7440,7 @@ msgstr "A~y~arlar..."
 
 #: engines/buried/metaengine.cpp:141 engines/chamber/metaengine.cpp:65
 #: engines/chamber/metaengine.cpp:71 engines/chamber/metaengine.cpp:77
-#: engines/dm/metaengine.cpp:130 engines/private/metaengine.cpp:130
+#: engines/dm/metaengine.cpp:130 engines/private/metaengine.cpp:141
 #: engines/sherlock/metaengine.cpp:1187
 #, fuzzy
 #| msgid "Interact"
@@ -11768,7 +11768,15 @@ msgstr ""
 msgid "Highlight clickable areas in decision scenes."
 msgstr ""
 
-#: engines/private/metaengine.cpp:136 engines/trecision/metaengine.cpp:137
+#: engines/private/metaengine.cpp:61
+msgid "Increased contrast for small print"
+msgstr ""
+
+#: engines/private/metaengine.cpp:62
+msgid "Enhance small printed paper close-ups for improved readability."
+msgstr ""
+
+#: engines/private/metaengine.cpp:147 engines/trecision/metaengine.cpp:137
 #, fuzzy
 msgid "Skip video"
 msgstr "??ptal"
diff --git a/po/uk_UA.po b/po/uk_UA.po
index 97264ca3701..b331d661f51 100644
--- a/po/uk_UA.po
+++ b/po/uk_UA.po
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.9.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-11 22:10+0000\n"
+"POT-Creation-Date: 2026-06-13 06:14+0000\n"
 "PO-Revision-Date: 2026-05-06 08:54+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Ukrainian <https://translations.scummvm.org/projects/scummvm/"
@@ -6297,7 +6297,7 @@ msgstr "???????????????????? ??????????????????..."
 #: engines/lure/metaengine.cpp:168 engines/made/metaengine.cpp:132
 #: engines/mads/metaengine.cpp:316 engines/neverhood/metaengine.cpp:185
 #: engines/ngi/metaengine.cpp:115 engines/petka/metaengine.cpp:113
-#: engines/prince/metaengine.cpp:227 engines/private/metaengine.cpp:127
+#: engines/prince/metaengine.cpp:227 engines/private/metaengine.cpp:138
 #: engines/queen/metaengine.cpp:146 engines/sherlock/metaengine.cpp:313
 #: engines/stark/metaengine.cpp:184 engines/supernova/metaengine.cpp:217
 #: engines/sword1/metaengine.cpp:266 engines/sword25/metaengine.cpp:108
@@ -7780,7 +7780,7 @@ msgstr "???????? ???????????? ??????????????????"
 
 #: engines/buried/metaengine.cpp:141 engines/chamber/metaengine.cpp:65
 #: engines/chamber/metaengine.cpp:71 engines/chamber/metaengine.cpp:77
-#: engines/dm/metaengine.cpp:130 engines/private/metaengine.cpp:130
+#: engines/dm/metaengine.cpp:130 engines/private/metaengine.cpp:141
 #: engines/sherlock/metaengine.cpp:1187
 msgid "Select / Interact"
 msgstr "?????????????? / ??????????????????????"
@@ -11964,7 +11964,15 @@ msgstr "???????????????????? ?????????????? ????????????"
 msgid "Highlight clickable areas in decision scenes."
 msgstr "???????????????????? ?????????????????????? ?????????????? ?? ???????????? ?????????????????? ????????????."
 
-#: engines/private/metaengine.cpp:136 engines/trecision/metaengine.cpp:137
+#: engines/private/metaengine.cpp:61
+msgid "Increased contrast for small print"
+msgstr ""
+
+#: engines/private/metaengine.cpp:62
+msgid "Enhance small printed paper close-ups for improved readability."
+msgstr ""
+
+#: engines/private/metaengine.cpp:147 engines/trecision/metaengine.cpp:137
 msgid "Skip video"
 msgstr "???????????????????? ??????????"
 
diff --git a/po/zh.po b/po/zh.po
index cfda05ac4eb..336a61d7b0a 100644
--- a/po/zh.po
+++ b/po/zh.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-11 22:10+0000\n"
+"POT-Creation-Date: 2026-06-13 06:14+0000\n"
 "PO-Revision-Date: 2024-05-14 12:42+0000\n"
 "Last-Translator: ?????? <clonewith at qq.com>\n"
 "Language-Team: Chinese <https://translations.scummvm.org/projects/scummvm/"
@@ -5745,7 +5745,7 @@ msgstr ""
 #: engines/lure/metaengine.cpp:168 engines/made/metaengine.cpp:132
 #: engines/mads/metaengine.cpp:316 engines/neverhood/metaengine.cpp:185
 #: engines/ngi/metaengine.cpp:115 engines/petka/metaengine.cpp:113
-#: engines/prince/metaengine.cpp:227 engines/private/metaengine.cpp:127
+#: engines/prince/metaengine.cpp:227 engines/private/metaengine.cpp:138
 #: engines/queen/metaengine.cpp:146 engines/sherlock/metaengine.cpp:313
 #: engines/stark/metaengine.cpp:184 engines/supernova/metaengine.cpp:217
 #: engines/sword1/metaengine.cpp:266 engines/sword25/metaengine.cpp:108
@@ -7202,7 +7202,7 @@ msgstr "????????????"
 
 #: engines/buried/metaengine.cpp:141 engines/chamber/metaengine.cpp:65
 #: engines/chamber/metaengine.cpp:71 engines/chamber/metaengine.cpp:77
-#: engines/dm/metaengine.cpp:130 engines/private/metaengine.cpp:130
+#: engines/dm/metaengine.cpp:130 engines/private/metaengine.cpp:141
 #: engines/sherlock/metaengine.cpp:1187
 #, fuzzy
 #| msgid "Interact"
@@ -11605,7 +11605,15 @@ msgstr ""
 msgid "Highlight clickable areas in decision scenes."
 msgstr ""
 
-#: engines/private/metaengine.cpp:136 engines/trecision/metaengine.cpp:137
+#: engines/private/metaengine.cpp:61
+msgid "Increased contrast for small print"
+msgstr ""
+
+#: engines/private/metaengine.cpp:62
+msgid "Enhance small printed paper close-ups for improved readability."
+msgstr ""
+
+#: engines/private/metaengine.cpp:147 engines/trecision/metaengine.cpp:137
 #, fuzzy
 #| msgid "Skip autosave"
 msgid "Skip video"
diff --git a/po/zh_Hans.po b/po/zh_Hans.po
index aa9b5518f14..21f2e849c3b 100644
--- a/po/zh_Hans.po
+++ b/po/zh_Hans.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.9.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-11 22:10+0000\n"
+"POT-Creation-Date: 2026-06-13 06:14+0000\n"
 "PO-Revision-Date: 2024-12-01 22:18+0000\n"
 "Last-Translator: ?????? <clonewith at qq.com>\n"
 "Language-Team: Chinese (Simplified Han script) <https://"
@@ -6381,7 +6381,7 @@ msgstr "??????????????????..."
 #: engines/lure/metaengine.cpp:168 engines/made/metaengine.cpp:132
 #: engines/mads/metaengine.cpp:316 engines/neverhood/metaengine.cpp:185
 #: engines/ngi/metaengine.cpp:115 engines/petka/metaengine.cpp:113
-#: engines/prince/metaengine.cpp:227 engines/private/metaengine.cpp:127
+#: engines/prince/metaengine.cpp:227 engines/private/metaengine.cpp:138
 #: engines/queen/metaengine.cpp:146 engines/sherlock/metaengine.cpp:313
 #: engines/stark/metaengine.cpp:184 engines/supernova/metaengine.cpp:217
 #: engines/sword1/metaengine.cpp:266 engines/sword25/metaengine.cpp:108
@@ -7885,7 +7885,7 @@ msgstr "??????????????????"
 
 #: engines/buried/metaengine.cpp:141 engines/chamber/metaengine.cpp:65
 #: engines/chamber/metaengine.cpp:71 engines/chamber/metaengine.cpp:77
-#: engines/dm/metaengine.cpp:130 engines/private/metaengine.cpp:130
+#: engines/dm/metaengine.cpp:130 engines/private/metaengine.cpp:141
 #: engines/sherlock/metaengine.cpp:1187
 #, fuzzy
 #| msgid "Interact Mode"
@@ -12526,7 +12526,15 @@ msgstr "????????????????????????"
 msgid "Highlight clickable areas in decision scenes."
 msgstr ""
 
-#: engines/private/metaengine.cpp:136 engines/trecision/metaengine.cpp:137
+#: engines/private/metaengine.cpp:61
+msgid "Increased contrast for small print"
+msgstr ""
+
+#: engines/private/metaengine.cpp:62
+msgid "Enhance small printed paper close-ups for improved readability."
+msgstr ""
+
+#: engines/private/metaengine.cpp:147 engines/trecision/metaengine.cpp:137
 msgid "Skip video"
 msgstr "????????????"
 
diff --git a/po/zh_Hant.po b/po/zh_Hant.po
index 13f55fee67b..6fde097cb96 100644
--- a/po/zh_Hant.po
+++ b/po/zh_Hant.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.9.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-11 22:10+0000\n"
+"POT-Creation-Date: 2026-06-13 06:14+0000\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: Automatically generated\n"
 "Language-Team: none\n"
@@ -5637,7 +5637,7 @@ msgstr ""
 #: engines/lure/metaengine.cpp:168 engines/made/metaengine.cpp:132
 #: engines/mads/metaengine.cpp:316 engines/neverhood/metaengine.cpp:185
 #: engines/ngi/metaengine.cpp:115 engines/petka/metaengine.cpp:113
-#: engines/prince/metaengine.cpp:227 engines/private/metaengine.cpp:127
+#: engines/prince/metaengine.cpp:227 engines/private/metaengine.cpp:138
 #: engines/queen/metaengine.cpp:146 engines/sherlock/metaengine.cpp:313
 #: engines/stark/metaengine.cpp:184 engines/supernova/metaengine.cpp:217
 #: engines/sword1/metaengine.cpp:266 engines/sword25/metaengine.cpp:108
@@ -7028,7 +7028,7 @@ msgstr ""
 
 #: engines/buried/metaengine.cpp:141 engines/chamber/metaengine.cpp:65
 #: engines/chamber/metaengine.cpp:71 engines/chamber/metaengine.cpp:77
-#: engines/dm/metaengine.cpp:130 engines/private/metaengine.cpp:130
+#: engines/dm/metaengine.cpp:130 engines/private/metaengine.cpp:141
 #: engines/sherlock/metaengine.cpp:1187
 msgid "Select / Interact"
 msgstr ""
@@ -10974,7 +10974,15 @@ msgstr ""
 msgid "Highlight clickable areas in decision scenes."
 msgstr ""
 
-#: engines/private/metaengine.cpp:136 engines/trecision/metaengine.cpp:137
+#: engines/private/metaengine.cpp:61
+msgid "Increased contrast for small print"
+msgstr ""
+
+#: engines/private/metaengine.cpp:62
+msgid "Enhance small printed paper close-ups for improved readability."
+msgstr ""
+
+#: engines/private/metaengine.cpp:147 engines/trecision/metaengine.cpp:137
 msgid "Skip video"
 msgstr ""
 



From noreply at scummvm.org  Sat Jun 13 09:55:55 2026
From: noreply at scummvm.org (ScummVM-Translations)
Date: Sat, 13 Jun 2026 09:55:55 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 ca000f4d1fda3423b01bc8bb263a829e14f33ef3
Message-ID: <20260613095555.68DFB7005BC@ron.scummvm.net>

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

Summary:
ca000f4d1f I18N: Update translation (Italian)


Commit: ca000f4d1fda3423b01bc8bb263a829e14f33ef3
    https://github.com/scummvm/scummvm/commit/ca000f4d1fda3423b01bc8bb263a829e14f33ef3
Author: Walter Agazzi (walter.agazzi at protonmail.com)
Date: 2026-06-13T09:55:49Z

Commit Message:
I18N: Update translation (Italian)

Currently translated at 100.0% (3210 of 3210 strings)

Changed paths:
    po/it_IT.po


diff --git a/po/it_IT.po b/po/it_IT.po
index a93740a6ae1..86086a3b09d 100644
--- a/po/it_IT.po
+++ b/po/it_IT.po
@@ -8,7 +8,7 @@ msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
 "POT-Creation-Date: 2026-06-13 06:14+0000\n"
-"PO-Revision-Date: 2026-06-10 20:26+0000\n"
+"PO-Revision-Date: 2026-06-13 09:55+0000\n"
 "Last-Translator: Walter Agazzi <walter.agazzi at protonmail.com>\n"
 "Language-Team: Italian <https://translations.scummvm.org/projects/scummvm/"
 "scummvm/it/>\n"
@@ -12093,11 +12093,12 @@ msgstr "Evidenzia le aree cliccabili nelle schermate delle decisioni."
 
 #: engines/private/metaengine.cpp:61
 msgid "Increased contrast for small print"
-msgstr ""
+msgstr "Testi piccoli a contrasto aumentato"
 
 #: engines/private/metaengine.cpp:62
 msgid "Enhance small printed paper close-ups for improved readability."
 msgstr ""
+"Aumenta il contrasto dei testi dei giornali per migliorarne la leggibilit??."
 
 #: engines/private/metaengine.cpp:147 engines/trecision/metaengine.cpp:137
 msgid "Skip video"



From noreply at scummvm.org  Sat Jun 13 10:20:11 2026
From: noreply at scummvm.org (neuromancer)
Date: Sat, 13 Jun 2026 10:20:11 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 7cc2f23da62928e8403cfebc5749ea7858d1e523
Message-ID: <20260613102011.BAAF17005BC@ron.scummvm.net>

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:
60b2f4b28e HYPNO: improved gamepad support
7cc2f23da6 HYPNO: make sure keyboard navigation of level selection is correct


Commit: 60b2f4b28e217f1d9ff5b2a98ac95c6d3ac1f2e0
    https://github.com/scummvm/scummvm/commit/60b2f4b28e217f1d9ff5b2a98ac95c6d3ac1f2e0
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-13T12:20:01+02:00

Commit Message:
HYPNO: improved gamepad support

Changed paths:
    engines/hypno/arcade.cpp
    engines/hypno/hypno.cpp
    engines/hypno/hypno.h
    engines/hypno/metaengine.cpp
    engines/hypno/spider/arcade.cpp
    engines/hypno/wet/arcade.cpp


diff --git a/engines/hypno/arcade.cpp b/engines/hypno/arcade.cpp
index 4197c518711..6652223b902 100644
--- a/engines/hypno/arcade.cpp
+++ b/engines/hypno/arcade.cpp
@@ -19,8 +19,9 @@
  *
  */
 
-#include "common/tokenizer.h"
+#include "common/config-manager.h"
 #include "common/events.h"
+#include "common/tokenizer.h"
 #include "graphics/cursorman.h"
 #include "graphics/framelimiter.h"
 
@@ -182,6 +183,126 @@ void HypnoEngine::runAfterArcade(ArcadeShooting *arc) {}
 
 void HypnoEngine::pressedKey(const int keycode) {}
 
+void HypnoEngine::resetGamepadAim(const Common::Rect &mouseBox) {
+	Common::Point mousePos = g_system->getEventManager()->getMousePos();
+	_gamepadAimPosition.x = CLIP<int>(mousePos.x, mouseBox.left, mouseBox.right - 1);
+	_gamepadAimPosition.y = CLIP<int>(mousePos.y, mouseBox.top, mouseBox.bottom - 1);
+	_gamepadAxisX = 0;
+	_gamepadAxisY = 0;
+	_gamepadAimActive = false;
+	_gamepadAimLeft = false;
+	_gamepadAimDown = false;
+	_gamepadAimRight = false;
+	_gamepadAimUp = false;
+}
+
+void HypnoEngine::setGamepadAimActive(bool active) {
+	_gamepadAimActive = active;
+}
+
+bool HypnoEngine::isGamepadAimActive() const {
+	return _gamepadAimActive;
+}
+
+bool HypnoEngine::handleGamepadAimAction(const int action, bool pressed) {
+	switch (action) {
+	case kActionAimLeft:
+		_gamepadAimLeft = pressed;
+		break;
+	case kActionAimDown:
+		_gamepadAimDown = pressed;
+		break;
+	case kActionAimRight:
+		_gamepadAimRight = pressed;
+		break;
+	case kActionAimUp:
+		_gamepadAimUp = pressed;
+		break;
+	default:
+		return false;
+	}
+
+	if (pressed)
+		_gamepadAimActive = true;
+	return true;
+}
+
+bool HypnoEngine::handleGamepadAxisAction(const int action, int16 position) {
+	const int16 axisPosition = (position == Common::JOYAXIS_MIN) ? Common::JOYAXIS_MAX : position;
+
+	switch (action) {
+	case kActionAimLeft:
+		if (position == 0 && _gamepadAxisX > 0)
+			return true;
+		_gamepadAxisX = -axisPosition;
+		break;
+	case kActionAimDown:
+		if (position == 0 && _gamepadAxisY < 0)
+			return true;
+		_gamepadAxisY = axisPosition;
+		break;
+	case kActionAimRight:
+		if (position == 0 && _gamepadAxisX < 0)
+			return true;
+		_gamepadAxisX = axisPosition;
+		break;
+	case kActionAimUp:
+		if (position == 0 && _gamepadAxisY > 0)
+			return true;
+		_gamepadAxisY = -axisPosition;
+		break;
+	default:
+		return false;
+	}
+
+	return true;
+}
+
+bool HypnoEngine::updateGamepadAim(const Common::Rect &mouseBox) {
+	const int dpadX = (_gamepadAimRight ? 1 : 0) - (_gamepadAimLeft ? 1 : 0);
+	const int dpadY = (_gamepadAimDown ? 1 : 0) - (_gamepadAimUp ? 1 : 0);
+
+	int deltaX = 0;
+	int deltaY = 0;
+
+	if (dpadX || dpadY) {
+		const int kDigitalStep = 4;
+		deltaX = dpadX * kDigitalStep;
+		deltaY = dpadY * kDigitalStep;
+	} else {
+		const int deadZone = MAX(0, ConfMan.getInt("joystick_deadzone")) * 1000;
+		const int16 axisX = (ABS((int)_gamepadAxisX) <= deadZone) ? 0 : _gamepadAxisX;
+		const int16 axisY = (ABS((int)_gamepadAxisY) <= deadZone) ? 0 : _gamepadAxisY;
+		if (axisX || axisY) {
+			const int kAnalogMaxStep = 8;
+			deltaX = (int)axisX * kAnalogMaxStep / Common::JOYAXIS_MAX;
+			deltaY = (int)axisY * kAnalogMaxStep / Common::JOYAXIS_MAX;
+			if (axisX && deltaX == 0)
+				deltaX = (axisX > 0) ? 1 : -1;
+			if (axisY && deltaY == 0)
+				deltaY = (axisY > 0) ? 1 : -1;
+		}
+	}
+
+	if (deltaX == 0 && deltaY == 0)
+		return false;
+
+	if (!_gamepadAimActive) {
+		Common::Point mousePos = g_system->getEventManager()->getMousePos();
+		_gamepadAimPosition.x = CLIP<int>(mousePos.x, mouseBox.left, mouseBox.right - 1);
+		_gamepadAimPosition.y = CLIP<int>(mousePos.y, mouseBox.top, mouseBox.bottom - 1);
+	}
+
+	_gamepadAimActive = true;
+	Common::Point oldPos = _gamepadAimPosition;
+	_gamepadAimPosition.x = CLIP<int>(_gamepadAimPosition.x + deltaX, mouseBox.left, mouseBox.right - 1);
+	_gamepadAimPosition.y = CLIP<int>(_gamepadAimPosition.y + deltaY, mouseBox.top, mouseBox.bottom - 1);
+
+	if (oldPos.x != _gamepadAimPosition.x || oldPos.y != _gamepadAimPosition.y)
+		g_system->warpMouse(_gamepadAimPosition.x, _gamepadAimPosition.y);
+	return true;
+}
+
 void HypnoEngine::initSegment(ArcadeShooting *arc) { error("Function \"%s\" not implemented", __FUNCTION__); }
 void HypnoEngine::findNextSegment(ArcadeShooting *arc) { error("Function \"%s\" not implemented", __FUNCTION__); }
 
@@ -242,6 +363,8 @@ void HypnoEngine::runArcade(ArcadeShooting *arc) {
 	arc->mouseBox.moveTo(anchor.x, anchor.y);
 	_background = new MVideo(arc->backgroundVideo, anchor, false, false, false);
 
+	resetGamepadAim(arc->mouseBox);
+	mousePos = getPlayerPosition(false);
 	drawCursorArcade(mousePos);
 	playVideo(*_background);
 
@@ -292,6 +415,32 @@ void HypnoEngine::runArcade(ArcadeShooting *arc) {
 	limiter.startFrame();
 
 	Common::Event event;
+	auto handlePointerMotion = [&]() {
+		drawCursorArcade(mousePos);
+		if (mousePos.x >= arc->mouseBox.right - 1) {
+			g_system->warpMouse(arc->mouseBox.right - 1, mousePos.y);
+		} else if (mousePos.y < arc->mouseBox.top) { // Usually top is zero
+			g_system->warpMouse(mousePos.x, arc->mouseBox.top + 1);
+		} else if (mousePos.y >= arc->mouseBox.bottom - 1) {
+			g_system->warpMouse(mousePos.x, arc->mouseBox.bottom - 1);
+		} else if (mousePos.x <= 40 && offset.x < 0) {
+			for (Shoots::iterator it = _shoots.begin(); it != _shoots.end(); ++it) {
+				if (it->video && it->video->decoder)
+					it->video->position.x = it->video->position.x + 1;
+			}
+			offset.x = offset.x + 1;
+			needsUpdate = true;
+		} else if (mousePos.x >= 280 && offset.x > 320 - _background->decoder->getWidth()) {
+			for (Shoots::iterator it = _shoots.begin(); it != _shoots.end(); ++it) {
+				if (it->video && it->video->decoder)
+					it->video->position.x = it->video->position.x - 1;
+			}
+			offset.x = offset.x - 1;
+			needsUpdate = true;
+		}
+		_background->position.x = offset.x;
+	};
+
 	while (!shouldQuit()) {
 		if (_timerStarted) {
 			if (_countdown <= 0) {
@@ -303,8 +452,6 @@ void HypnoEngine::runArcade(ArcadeShooting *arc) {
 		}
 		needsUpdate = _background->decoder->needsUpdate();
 		while (g_system->getEventManager()->pollEvent(event)) {
-			mousePos = getPlayerPosition(false);
-
 			// Events
 			switch (event.type) {
 
@@ -312,54 +459,66 @@ void HypnoEngine::runArcade(ArcadeShooting *arc) {
 			case Common::EVENT_RETURN_TO_LAUNCHER:
 				break;
 			case Common::EVENT_CUSTOM_ENGINE_ACTION_START:
-				pressedKey(event.customType);
-				if (event.customType == kActionPrimaryShoot)
+				mousePos = getPlayerPosition(false);
+				if (handleGamepadAimAction(event.customType, true)) {
+					needsUpdate = true;
+				} else if (event.customType == kActionPrimaryShoot) {
 					if (clickedPrimaryShoot(mousePos))
 						shootingPrimary = true;
+				} else if (event.customType == kActionSecondaryShoot) {
+					setRButtonUp(false);
+					if (clickedSecondaryShoot(mousePos))
+						shootingSecondary = true;
+				} else {
+					pressedKey(event.customType);
+				}
+				break;
+
+			case Common::EVENT_CUSTOM_ENGINE_ACTION_END:
+				if (handleGamepadAimAction(event.customType, false)) {
+					needsUpdate = true;
+				} else if (event.customType == kActionSecondaryShoot) {
+					setRButtonUp(true);
+					shootingSecondary = false;
+				}
+				break;
+
+			case Common::EVENT_CUSTOM_BACKEND_ACTION_AXIS:
+				if (handleGamepadAxisAction(event.customType, event.joystick.position))
+					needsUpdate = true;
 				break;
 
 			case Common::EVENT_KEYDOWN:
+				mousePos = getPlayerPosition(false);
 				pressedKey(event.kbd.keycode);
 				break;
 
 			case Common::EVENT_LBUTTONDOWN:
+				setGamepadAimActive(false);
+				mousePos = g_system->getEventManager()->getMousePos();
 				if (clickedPrimaryShoot(mousePos))
 					shootingPrimary = true;
 				break;
 
 			case Common::EVENT_RBUTTONDOWN:
+				setGamepadAimActive(false);
+				setRButtonUp(false);
+				mousePos = g_system->getEventManager()->getMousePos();
 				if (clickedSecondaryShoot(mousePos))
 					shootingSecondary = true;
 				break;
 
 			case Common::EVENT_RBUTTONUP:
+				setRButtonUp(true);
 				shootingSecondary = false;
 				break;
 
 			case Common::EVENT_MOUSEMOVE:
-				drawCursorArcade(mousePos);
-				if (mousePos.x >= arc->mouseBox.right-1) {
-					g_system->warpMouse(arc->mouseBox.right-1, mousePos.y);
-				} else if (mousePos.y < arc->mouseBox.top) { // Usually top is zero
-					g_system->warpMouse(mousePos.x, arc->mouseBox.top + 1);
-				} else if (mousePos.y >= arc->mouseBox.bottom-1) {
-					g_system->warpMouse(mousePos.x, arc->mouseBox.bottom-1);
-				} else if (mousePos.x <= 40 && offset.x < 0) {
-					for (Shoots::iterator it = _shoots.begin(); it != _shoots.end(); ++it) {
-						if (it->video && it->video->decoder)
-							it->video->position.x = it->video->position.x + 1;
-					}
-					offset.x = offset.x + 1;
-					needsUpdate = true;
-				} else if (mousePos.x >= 280 && offset.x > 320 - _background->decoder->getWidth()) {
-					for (Shoots::iterator it = _shoots.begin(); it != _shoots.end(); ++it) {
-						if (it->video && it->video->decoder)
-							it->video->position.x = it->video->position.x - 1;
-					}
-					offset.x = offset.x - 1;
-					needsUpdate = true;
-				}
-				_background->position.x = offset.x;
+				setGamepadAimActive(false);
+				mousePos = g_system->getEventManager()->getMousePos();
+				_gamepadAimPosition.x = CLIP<int>(mousePos.x, arc->mouseBox.left, arc->mouseBox.right - 1);
+				_gamepadAimPosition.y = CLIP<int>(mousePos.y, arc->mouseBox.top, arc->mouseBox.bottom - 1);
+				handlePointerMotion();
 				break;
 
 			default:
@@ -367,8 +526,14 @@ void HypnoEngine::runArcade(ArcadeShooting *arc) {
 			}
 		}
 
+		if (updateGamepadAim(arc->mouseBox)) {
+			mousePos = getPlayerPosition(false);
+			handlePointerMotion();
+			needsUpdate = true;
+		}
+
 		if (needsUpdate) {
-			getPlayerPosition(true);
+			mousePos = getPlayerPosition(true);
 			if (_background->decoder->getCurFrame() > firstFrame)
 				drawScreen();
 			updateScreen(*_background);
@@ -614,6 +779,8 @@ Common::Point HypnoEngine::computeTargetPosition(const Common::Point &mousePos)
 }
 
 Common::Point HypnoEngine::getPlayerPosition(bool needsUpdate) {
+	if (_gamepadAimActive)
+		return _gamepadAimPosition;
 	return g_system->getEventManager()->getMousePos();
 }
 
@@ -833,4 +1000,3 @@ void HypnoEngine::enableGameKeymaps() {
 }
 
 } // End of namespace Hypno
-
diff --git a/engines/hypno/hypno.cpp b/engines/hypno/hypno.cpp
index f12bba6a6c7..14826226562 100644
--- a/engines/hypno/hypno.cpp
+++ b/engines/hypno/hypno.cpp
@@ -94,6 +94,15 @@ HypnoEngine::HypnoEngine(OSystem *syst, const ADGameDescription *gd)
 	if (!Common::parseBool(ConfMan.get("subtitles"), _useSubtitles))
 		warning("Failed to parse bool from subtitles options");
 
+	_gamepadAimPosition = Common::Point(0, 0);
+	_gamepadAxisX = 0;
+	_gamepadAxisY = 0;
+	_gamepadAimActive = false;
+	_gamepadAimLeft = false;
+	_gamepadAimDown = false;
+	_gamepadAimRight = false;
+	_gamepadAimUp = false;
+
 	// Add quit level
 	Hotspot q(MakeMenu);
 	Action *a = new Quit();
diff --git a/engines/hypno/hypno.h b/engines/hypno/hypno.h
index 43f6d93bb77..ac7de039d2d 100644
--- a/engines/hypno/hypno.h
+++ b/engines/hypno/hypno.h
@@ -107,6 +107,11 @@ enum HYPNOActions {
 	kActionQuit,
 	kActionCredits,
 	kActionSelect,
+	kActionSecondaryShoot,
+	kActionAimLeft,
+	kActionAimDown,
+	kActionAimRight,
+	kActionAimUp,
 };
 
 class HypnoEngine;
@@ -316,6 +321,12 @@ public:
 	virtual void pressedKey(const int keycode);
 	virtual bool clickedPrimaryShoot(const Common::Point &mousePos);
 	virtual bool clickedSecondaryShoot(const Common::Point &mousePos);
+	void resetGamepadAim(const Common::Rect &mouseBox);
+	void setGamepadAimActive(bool active);
+	bool isGamepadAimActive() const;
+	bool handleGamepadAimAction(const int action, bool pressed);
+	bool handleGamepadAxisAction(const int action, int16 position);
+	bool updateGamepadAim(const Common::Rect &mouseBox);
 	virtual void drawShoot(const Common::Point &mousePos);
 	virtual bool shoot(const Common::Point &mousePos, ArcadeShooting *arc, bool secondary);
 	virtual void hitPlayer();
@@ -361,6 +372,14 @@ public:
 	bool _loseLevel;
 	bool _skipDefeatVideo;
 	bool _skipNextVideo;
+	Common::Point _gamepadAimPosition;
+	int16 _gamepadAxisX;
+	int16 _gamepadAxisY;
+	bool _gamepadAimActive;
+	bool _gamepadAimLeft;
+	bool _gamepadAimDown;
+	bool _gamepadAimRight;
+	bool _gamepadAimUp;
 
 	virtual void drawCursorArcade(const Common::Point &mousePos);
 	virtual void drawPlayer();
diff --git a/engines/hypno/metaengine.cpp b/engines/hypno/metaengine.cpp
index 4b3ce6f641a..0b75135dfe6 100644
--- a/engines/hypno/metaengine.cpp
+++ b/engines/hypno/metaengine.cpp
@@ -134,25 +134,84 @@ Common::KeymapArray HypnoMetaEngine::initKeymaps(const char *target) const {
 	act = new Common::Action(kStandardActionLeftClick, _("Primary shoot"));
 	act->setLeftClickEvent();
 	act->addDefaultInputMapping("MOUSE_LEFT");
-	act->addDefaultInputMapping("JOY_A");
 	engineKeymap->addAction(act);
 
 	act = new Common::Action(kStandardActionRightClick, _("Secondary shoot"));
 	act->setRightClickEvent();
 	act->addDefaultInputMapping("MOUSE_RIGHT");
+	engineKeymap->addAction(act);
+
+	act = new Common::Action("PRIMARYSHOOT", _("Primary shoot"));
+	act->setCustomEngineActionEvent(kActionPrimaryShoot);
+	act->addDefaultInputMapping("JOY_A");
+	engineKeymap->addAction(act);
+
+	act = new Common::Action("SECONDARYSHOOT", _("Rapid fire / secondary action"));
+	act->setCustomEngineActionEvent(kActionSecondaryShoot);
 	act->addDefaultInputMapping("JOY_B");
+	act->addDefaultInputMapping("JOY_RIGHT_SHOULDER");
+	engineKeymap->addAction(act);
+
+	act = new Common::Action("AIMLEFT", _("Aim left"));
+	act->setCustomEngineActionEvent(kActionAimLeft);
+	act->addDefaultInputMapping("JOY_LEFT");
+	engineKeymap->addAction(act);
+
+	act = new Common::Action("AIMDOWN", _("Aim down"));
+	act->setCustomEngineActionEvent(kActionAimDown);
+	act->addDefaultInputMapping("JOY_DOWN");
+	engineKeymap->addAction(act);
+
+	act = new Common::Action("AIMRIGHT", _("Aim right"));
+	act->setCustomEngineActionEvent(kActionAimRight);
+	act->addDefaultInputMapping("JOY_RIGHT");
+	engineKeymap->addAction(act);
+
+	act = new Common::Action("AIMUP", _("Aim up"));
+	act->setCustomEngineActionEvent(kActionAimUp);
+	act->addDefaultInputMapping("JOY_UP");
+	engineKeymap->addAction(act);
+
+	act = new Common::Action("STICKLEFT", _("Stick left"));
+	act->setCustomBackendActionAxisEvent(kActionAimLeft);
+	act->addDefaultInputMapping("JOY_LEFT_STICK_X-");
+	act->addDefaultInputMapping("JOY_RIGHT_STICK_X-");
+	act->addDefaultInputMapping("JOY_HAT_X-");
+	engineKeymap->addAction(act);
+
+	act = new Common::Action("STICKDOWN", _("Stick down"));
+	act->setCustomBackendActionAxisEvent(kActionAimDown);
+	act->addDefaultInputMapping("JOY_LEFT_STICK_Y+");
+	act->addDefaultInputMapping("JOY_RIGHT_STICK_Y+");
+	act->addDefaultInputMapping("JOY_HAT_Y+");
+	engineKeymap->addAction(act);
+
+	act = new Common::Action("STICKRIGHT", _("Stick right"));
+	act->setCustomBackendActionAxisEvent(kActionAimRight);
+	act->addDefaultInputMapping("JOY_LEFT_STICK_X+");
+	act->addDefaultInputMapping("JOY_RIGHT_STICK_X+");
+	act->addDefaultInputMapping("JOY_HAT_X+");
+	engineKeymap->addAction(act);
+
+	act = new Common::Action("STICKUP", _("Stick up"));
+	act->setCustomBackendActionAxisEvent(kActionAimUp);
+	act->addDefaultInputMapping("JOY_LEFT_STICK_Y-");
+	act->addDefaultInputMapping("JOY_RIGHT_STICK_Y-");
+	act->addDefaultInputMapping("JOY_HAT_Y-");
 	engineKeymap->addAction(act);
 
 	act = new Common::Action("SKIPINTRO", _("Skip intro"));
 	act->setCustomEngineActionEvent(kActionSkipIntro);
 	act->addDefaultInputMapping("ESCAPE");
-	act->addDefaultInputMapping("JOY_Y");
+	act->addDefaultInputMapping("JOY_B");
+	act->addDefaultInputMapping("JOY_START");
 	introKeymap->addAction(act);
 
 	act = new Common::Action("SKIPCUTSCENE", _("Skip cutscene"));
 	act->setCustomEngineActionEvent(kActionSkipCutscene);
 	act->addDefaultInputMapping("ESCAPE");
-	act->addDefaultInputMapping("JOY_Y");
+	act->addDefaultInputMapping("JOY_B");
+	act->addDefaultInputMapping("JOY_START");
 	cutsceneKeymap->addAction(act);
 
 	keymaps.push_back(engineKeymap);
@@ -172,7 +231,6 @@ Common::KeymapArray HypnoMetaEngine::initKeymaps(const char *target) const {
 			act = new Common::Action("SKIPLEVEL", _("Skip level (cheat)"));
 			act->setCustomEngineActionEvent(kActionSkipLevel);
 			act->addDefaultInputMapping("c");
-			act->addDefaultInputMapping("JOY_X");
 			gameKeymap->addAction(act);
 		}
 
@@ -180,13 +238,12 @@ Common::KeymapArray HypnoMetaEngine::initKeymaps(const char *target) const {
 		act = new Common::Action("KILLPLAYER", _("Kill player"));
 		act->setCustomEngineActionEvent(kActionKillPlayer);
 		act->addDefaultInputMapping("k");
-		act->addDefaultInputMapping("JOY_LEFT");
 		gameKeymap->addAction(act);
 
 		act = new Common::Action("PAUSE", _("Pause"));
 		act->setCustomEngineActionEvent(kActionPause);
 		act->addDefaultInputMapping("ESCAPE");
-		act->addDefaultInputMapping("JOY_UP");
+		act->addDefaultInputMapping("JOY_START");
 		gameKeymap->addAction(act);
 
 		act = new Common::Action("YES", _("Yes"));
@@ -270,7 +327,6 @@ Common::KeymapArray HypnoMetaEngine::initKeymaps(const char *target) const {
 			act = new Common::Action("SKIPLEVEL", _("Skip level (cheat)"));
 			act->setCustomEngineActionEvent(kActionSkipLevel);
 			act->addDefaultInputMapping("c");
-			act->addDefaultInputMapping("JOY_X");
 			gameKeymap->addAction(act);
 		}
 
@@ -278,7 +334,12 @@ Common::KeymapArray HypnoMetaEngine::initKeymaps(const char *target) const {
 		act = new Common::Action("KILLPLAYER", _("Kill player"));
 		act->setCustomEngineActionEvent(kActionKillPlayer);
 		act->addDefaultInputMapping("k");
-		act->addDefaultInputMapping("JOY_Y");
+		gameKeymap->addAction(act);
+
+		act = new Common::Action("PAUSE", _("Pause"));
+		act->setCustomEngineActionEvent(kActionPause);
+		act->addDefaultInputMapping("ESCAPE");
+		act->addDefaultInputMapping("JOY_START");
 		gameKeymap->addAction(act);
 
 		act = new Common::Action("LEFT", _("Move left"));
@@ -316,60 +377,86 @@ Common::KeymapArray HypnoMetaEngine::initKeymaps(const char *target) const {
 		act = new Common::Action("CREDITS", _("Show credits"));
 		act->setCustomEngineActionEvent(kActionCredits);
 		act->addDefaultInputMapping("c");
-		act->addDefaultInputMapping("JOY_LEFT_TRIGGER");
 		gameKeymap->addAction(act);
 
 		act = new Common::Action("SKIPLEVEL", _("Skip level (cheat)"));
 		act->setCustomEngineActionEvent(kActionSkipLevel);
 		act->addDefaultInputMapping("s");
-		act->addDefaultInputMapping("JOY_X");
 		gameKeymap->addAction(act);
 
 		// I18N: (Game name: Wetlands) player refers to the users own character.
 		act = new Common::Action("KILLPLAYER", _("Kill player"));
 		act->setCustomEngineActionEvent(kActionKillPlayer);
 		act->addDefaultInputMapping("k");
-		act->addDefaultInputMapping("JOY_RIGHT_TRIGGER");
 		gameKeymap->addAction(act);
 
 		act = new Common::Action("LEFT", _("Move left"));
 		act->setCustomEngineActionEvent(kActionLeft);
 		act->allowKbdRepeats();
 		act->addDefaultInputMapping("LEFT");
-		act->addDefaultInputMapping("JOY_LEFT");
 		directionKeymap->addAction(act);
 
 		act = new Common::Action("DOWN", _("Move down"));
 		act->setCustomEngineActionEvent(kActionDown);
 		act->allowKbdRepeats();
 		act->addDefaultInputMapping("DOWN");
-		act->addDefaultInputMapping("JOY_DOWN");
 		directionKeymap->addAction(act);
 
 		act = new Common::Action("RIGHT", _("Move right"));
 		act->setCustomEngineActionEvent(kActionRight);
 		act->allowKbdRepeats();
 		act->addDefaultInputMapping("RIGHT");
-		act->addDefaultInputMapping("JOY_RIGHT");
 		directionKeymap->addAction(act);
 
 		act = new Common::Action("UP", _("Move up"));
 		act->setCustomEngineActionEvent(kActionUp);
 		act->allowKbdRepeats();
 		act->addDefaultInputMapping("UP");
-		act->addDefaultInputMapping("JOY_UP");
 		directionKeymap->addAction(act);
 
+		act = new Common::Action("MENULEFT", _("Menu left"));
+		act->setCustomEngineActionEvent(kActionLeft);
+		act->addDefaultInputMapping("LEFT");
+		act->addDefaultInputMapping("JOY_LEFT");
+		act->addDefaultInputMapping("JOY_LEFT_STICK_X-");
+		act->addDefaultInputMapping("JOY_HAT_X-");
+		menuKeymap->addAction(act);
+
+		act = new Common::Action("MENUDOWN", _("Menu down"));
+		act->setCustomEngineActionEvent(kActionDown);
+		act->addDefaultInputMapping("DOWN");
+		act->addDefaultInputMapping("JOY_DOWN");
+		act->addDefaultInputMapping("JOY_LEFT_STICK_Y+");
+		act->addDefaultInputMapping("JOY_HAT_Y+");
+		menuKeymap->addAction(act);
+
+		act = new Common::Action("MENURIGHT", _("Menu right"));
+		act->setCustomEngineActionEvent(kActionRight);
+		act->addDefaultInputMapping("RIGHT");
+		act->addDefaultInputMapping("JOY_RIGHT");
+		act->addDefaultInputMapping("JOY_LEFT_STICK_X+");
+		act->addDefaultInputMapping("JOY_HAT_X+");
+		menuKeymap->addAction(act);
+
+		act = new Common::Action("MENUUP", _("Menu up"));
+		act->setCustomEngineActionEvent(kActionUp);
+		act->addDefaultInputMapping("UP");
+		act->addDefaultInputMapping("JOY_UP");
+		act->addDefaultInputMapping("JOY_LEFT_STICK_Y-");
+		act->addDefaultInputMapping("JOY_HAT_Y-");
+		menuKeymap->addAction(act);
+
 		act = new Common::Action("SELECT", _("Select"));
 		act->setCustomEngineActionEvent(kActionSelect);
 		act->addDefaultInputMapping("RETURN");
+		act->addDefaultInputMapping("JOY_A");
 		act->addDefaultInputMapping("JOY_X");
 		menuKeymap->addAction(act);
 
 		act = new Common::Action("PAUSE", _("Pause"));
 		act->setCustomEngineActionEvent(kActionPause);
 		act->addDefaultInputMapping("ESCAPE");
-		act->addDefaultInputMapping("JOY_Y");
+		act->addDefaultInputMapping("JOY_START");
 		pauseKeymap->addAction(act);
 
 		keymaps.push_back(gameKeymap);
@@ -396,4 +483,3 @@ REGISTER_PLUGIN_DYNAMIC(HYPNO, PLUGIN_TYPE_ENGINE, HypnoMetaEngine);
 #else
 REGISTER_PLUGIN_STATIC(HYPNO, PLUGIN_TYPE_ENGINE, HypnoMetaEngine);
 #endif
-
diff --git a/engines/hypno/spider/arcade.cpp b/engines/hypno/spider/arcade.cpp
index 736ae2b4424..888040896b4 100644
--- a/engines/hypno/spider/arcade.cpp
+++ b/engines/hypno/spider/arcade.cpp
@@ -106,16 +106,18 @@ void SpiderEngine::pressedKey(const int keycode) {
 		return;
 	} else if (keycode == kActionKillPlayer) { // Added for testing
 		_health = 0;
-	} else if (keycode == kActionLeft) {
+	} else if (keycode == kActionPause) {
+		openMainMenuDialog();
+	} else if (keycode == kActionLeft && (_arcadeMode == "YC" || _arcadeMode == "YD")) {
 		_lastPlayerPosition = _currentPlayerPosition;
 		_currentPlayerPosition = kPlayerLeft;
-	} else if (keycode == kActionDown) {
+	} else if (keycode == kActionDown && (_arcadeMode == "YC" || _arcadeMode == "YD")) {
 		_lastPlayerPosition = _currentPlayerPosition;
 		_currentPlayerPosition = kPlayerBottom;
-	} else if (keycode == kActionRight) {
+	} else if (keycode == kActionRight && (_arcadeMode == "YC" || _arcadeMode == "YD")) {
 		_lastPlayerPosition = _currentPlayerPosition;
 		_currentPlayerPosition = kPlayerRight;
-	} else if (keycode == kActionUp) {
+	} else if (keycode == kActionUp && (_arcadeMode == "YC" || _arcadeMode == "YD")) {
 		_lastPlayerPosition = _currentPlayerPosition;
 		_currentPlayerPosition = kPlayerTop;
 	}
diff --git a/engines/hypno/wet/arcade.cpp b/engines/hypno/wet/arcade.cpp
index 8b68763d3f0..74be3622e4e 100644
--- a/engines/hypno/wet/arcade.cpp
+++ b/engines/hypno/wet/arcade.cpp
@@ -772,9 +772,12 @@ void WetEngine::pressedKey(const int keycode) {
 }
 
 Common::Point WetEngine::getPlayerPosition(bool needsUpdate) {
-	Common::Point mousePos = g_system->getEventManager()->getMousePos();
+	Common::Point mousePos = HypnoEngine::getPlayerPosition(false);
 	if (_arcadeMode == "YT") {
 		if (needsUpdate) {
+			if (isGamepadAimActive() && _c33PlayerDirection.empty())
+				_c33UseMouse = true;
+
 			if (!_c33UseMouse) {
 				disableCursor();
 				if (_c33PlayerDirection.size() == 0)
@@ -805,7 +808,7 @@ Common::Point WetEngine::getPlayerPosition(bool needsUpdate) {
 					diff.x = (diff.x / abs(diff.x)) * 10;
 
 				if (abs(diff.y) >= 10)
-					diff.y = (diff.x / abs(diff.x)) * 10;
+					diff.y = (diff.y / abs(diff.y)) * 10;
 
 				_c33PlayerPosition = _c33PlayerPosition + diff;
 
@@ -819,6 +822,8 @@ Common::Point WetEngine::getPlayerPosition(bool needsUpdate) {
 					drawImage(*_c33PlayerCursor[10], _c33PlayerPosition.x - 10, _c33PlayerPosition.y, true);
 			}
 		}
+		_c33PlayerPosition.x = CLIP<int>(_c33PlayerPosition.x, 0, _screenW - 1);
+		_c33PlayerPosition.y = CLIP<int>(_c33PlayerPosition.y, 0, _screenH - 1);
 		uint32 c = _compositeSurface->getPixel(_c33PlayerPosition.x, _c33PlayerPosition.y);
 		if (c >= 225 && c <= 231) {
 			if (!_infiniteHealthCheat)
@@ -827,7 +832,7 @@ Common::Point WetEngine::getPlayerPosition(bool needsUpdate) {
 		}
 		return _c33PlayerPosition;
 	}
-	return mousePos;
+	return HypnoEngine::getPlayerPosition(needsUpdate);
 }
 
 void WetEngine::drawCursorArcade(const Common::Point &mousePos) {


Commit: 7cc2f23da62928e8403cfebc5749ea7858d1e523
    https://github.com/scummvm/scummvm/commit/7cc2f23da62928e8403cfebc5749ea7858d1e523
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-13T12:20:01+02:00

Commit Message:
HYPNO: make sure keyboard navigation of level selection is correct

Changed paths:
    engines/hypno/wet/hard.cpp


diff --git a/engines/hypno/wet/hard.cpp b/engines/hypno/wet/hard.cpp
index 09dbb664748..9ff212713a9 100644
--- a/engines/hypno/wet/hard.cpp
+++ b/engines/hypno/wet/hard.cpp
@@ -91,6 +91,7 @@ void WetEngine::runLevelMenu(Code *code) {
 
 	Common::Keymapper *keymapper = g_system->getEventManager()->getKeymapper();
 	keymapper->getKeymap("game-shortcuts")->setEnabled(false);
+	keymapper->getKeymap("direction")->setEnabled(false);
 	keymapper->getKeymap("menu")->setEnabled(true);
 
 	while (!shouldQuit() && cont) {
@@ -137,6 +138,7 @@ void WetEngine::runLevelMenu(Code *code) {
 	}
 
 	keymapper->getKeymap("menu")->setEnabled(false);
+	keymapper->getKeymap("direction")->setEnabled(true);
 	keymapper->getKeymap("game-shortcuts")->setEnabled(true);
 
 	menu->free();
@@ -276,6 +278,7 @@ void WetEngine::runMainMenu(Code *code) {
 
 	keymapper->getKeymap("game-shortcuts")->setEnabled(false);
 	keymapper->getKeymap("pause")->setEnabled(false);
+	keymapper->getKeymap("direction")->setEnabled(false);
 	keymapper->getKeymap("menu")->setEnabled(true);
 
 	while (!shouldQuit() && cont) {
@@ -336,6 +339,7 @@ void WetEngine::runMainMenu(Code *code) {
 	}
 
 	keymapper->getKeymap("menu")->setEnabled(false);
+	keymapper->getKeymap("direction")->setEnabled(true);
 	keymapper->getKeymap("game-shortcuts")->setEnabled(true);
 	keymapper->getKeymap("pause")->setEnabled(true);
 



From noreply at scummvm.org  Sat Jun 13 10:20:18 2026
From: noreply at scummvm.org (ScummVM-Translations)
Date: Sat, 13 Jun 2026 10:20:18 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 20e63acea23e616d4f98b8f00cd28a001c5b568a
Message-ID: <20260613102018.1DAB57005CD@ron.scummvm.net>

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

Summary:
20e63acea2 I18N: Update translations templates


Commit: 20e63acea23e616d4f98b8f00cd28a001c5b568a
    https://github.com/scummvm/scummvm/commit/20e63acea23e616d4f98b8f00cd28a001c5b568a
Author: Weblate (noreply at weblate.org)
Date: 2026-06-13T10:20:13Z

Commit Message:
I18N: Update translations templates

Changed paths:
    po/scummvm.pot


diff --git a/po/scummvm.pot b/po/scummvm.pot
index 5f567894979..70927091701 100644
--- a/po/scummvm.pot
+++ b/po/scummvm.pot
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2026.2.1git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-13 06:14+0000\n"
+"POT-Creation-Date: 2026-06-13 10:20+0000\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL at ADDRESS>\n"
 "Language-Team: LANGUAGE <LL at li.org>\n"
@@ -130,7 +130,7 @@ msgstr ""
 #: engines/buried/saveload.cpp:83 engines/cine/saveload.cpp:857
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:123
 #: engines/drascula/saveload.cpp:51 engines/grim/inputdialog.cpp:84
-#: engines/hypno/metaengine.cpp:225 engines/mohawk/myst_stacks/menu.cpp:283
+#: engines/hypno/metaengine.cpp:282 engines/mohawk/myst_stacks/menu.cpp:283
 #: engines/mohawk/myst_stacks/menu.cpp:300
 #: engines/mohawk/myst_stacks/menu.cpp:318
 #: engines/mohawk/riven_stacks/aspit.cpp:349
@@ -326,7 +326,7 @@ msgstr ""
 #: engines/chamber/metaengine.cpp:91 engines/director/events.cpp:122
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:139
 #: engines/efh/metaengine.cpp:362 engines/grim/grim.cpp:366
-#: engines/hypno/metaengine.cpp:192 engines/kyra/gui/saveload_eob.cpp:611
+#: engines/hypno/metaengine.cpp:249 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:197
 #: engines/mm/mm1/metaengine.cpp:54 engines/parallaction/metaengine.cpp:169
 #: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
@@ -345,7 +345,7 @@ msgstr ""
 #: engines/chamber/metaengine.cpp:97 engines/director/events.cpp:122
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:139
 #: engines/efh/metaengine.cpp:368 engines/grim/grim.cpp:366
-#: engines/hypno/metaengine.cpp:198 engines/kyra/gui/saveload_eob.cpp:611
+#: engines/hypno/metaengine.cpp:255 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:204
 #: engines/mm/mm1/metaengine.cpp:52 engines/parallaction/metaengine.cpp:175
 #: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
@@ -3157,7 +3157,7 @@ msgstr ""
 #: backends/events/default/default-events.cpp:358
 #: engines/adl/metaengine.cpp:352 engines/buried/metaengine.cpp:154
 #: engines/chamber/metaengine.cpp:84 engines/drascula/metaengine.cpp:304
-#: engines/hdb/metaengine.cpp:275 engines/hypno/metaengine.cpp:251
+#: engines/hdb/metaengine.cpp:275 engines/hypno/metaengine.cpp:308
 #: engines/lab/metaengine.cpp:172 engines/mohawk/myst_stacks/menu.cpp:317
 #: engines/mohawk/riven_stacks/aspit.cpp:454
 #: engines/neverhood/metaengine.cpp:200 engines/saga/metaengine.cpp:449
@@ -3657,8 +3657,9 @@ msgstr ""
 #: engines/buried/metaengine.cpp:167 engines/crab/input/input.cpp:261
 #: engines/cruise/metaengine.cpp:217 engines/dm/metaengine.cpp:180
 #: engines/grim/grim.cpp:550 engines/hdb/metaengine.cpp:258
-#: engines/hypno/metaengine.cpp:186 engines/hypno/metaengine.cpp:369
-#: engines/icb/icb.cpp:137 engines/icb/icb.cpp:203 engines/mohawk/myst.cpp:595
+#: engines/hypno/metaengine.cpp:243 engines/hypno/metaengine.cpp:339
+#: engines/hypno/metaengine.cpp:456 engines/icb/icb.cpp:137
+#: engines/icb/icb.cpp:203 engines/mohawk/myst.cpp:595
 #: engines/mohawk/riven.cpp:876 engines/ngi/metaengine.cpp:136
 #: engines/sky/metaengine.cpp:126 engines/sludge/keymapper_tables.h:52
 #: engines/sludge/keymapper_tables.h:125 engines/sludge/keymapper_tables.h:187
@@ -4530,7 +4531,7 @@ msgstr ""
 #: backends/platform/sdl/opendingux/opendingux.cpp:57
 #: backends/platform/sdl/ps3/ps3.cpp:50 backends/platform/sdl/psp2/psp2.cpp:51
 #: engines/bagel/metaengine.cpp:64 engines/efh/metaengine.cpp:397
-#: engines/got/metaengine.cpp:76 engines/hypno/metaengine.cpp:363
+#: engines/got/metaengine.cpp:76 engines/hypno/metaengine.cpp:449
 #: engines/mm/mm1/metaengine.cpp:50 engines/mm/mm1/metaengine.cpp:97
 #: engines/sherlock/metaengine.cpp:660 engines/sherlock/metaengine.cpp:672
 #: engines/sherlock/metaengine.cpp:1014 engines/supernova/metaengine.cpp:303
@@ -5662,7 +5663,7 @@ msgstr ""
 #: engines/access/metaengine.cpp:237 engines/asylum/metaengine.cpp:171
 #: engines/asylum/metaengine.cpp:198 engines/cine/metaengine.cpp:377
 #: engines/efh/metaengine.cpp:286 engines/hdb/metaengine.cpp:213
-#: engines/hypno/metaengine.cpp:302 engines/hypno/metaengine.cpp:356
+#: engines/hypno/metaengine.cpp:363 engines/hypno/metaengine.cpp:411
 #: engines/nancy/input.cpp:195 engines/tinsel/metaengine.cpp:263
 #: engines/titanic/metaengine.cpp:324 engines/ultima/ultima8/metaengine.cpp:52
 msgid "Move up"
@@ -5672,7 +5673,7 @@ msgstr ""
 #: engines/access/metaengine.cpp:244 engines/asylum/metaengine.cpp:177
 #: engines/asylum/metaengine.cpp:205 engines/cine/metaengine.cpp:384
 #: engines/efh/metaengine.cpp:293 engines/hdb/metaengine.cpp:219
-#: engines/hypno/metaengine.cpp:290 engines/hypno/metaengine.cpp:342
+#: engines/hypno/metaengine.cpp:351 engines/hypno/metaengine.cpp:399
 #: engines/nancy/input.cpp:201 engines/tinsel/metaengine.cpp:269
 #: engines/titanic/metaengine.cpp:330 engines/ultima/ultima8/metaengine.cpp:53
 msgid "Move down"
@@ -5683,7 +5684,7 @@ msgstr ""
 #: engines/access/metaengine.cpp:251 engines/asylum/metaengine.cpp:183
 #: engines/asylum/metaengine.cpp:212 engines/cine/metaengine.cpp:391
 #: engines/efh/metaengine.cpp:300 engines/hdb/metaengine.cpp:225
-#: engines/hypno/metaengine.cpp:284 engines/hypno/metaengine.cpp:335
+#: engines/hypno/metaengine.cpp:345 engines/hypno/metaengine.cpp:393
 #: engines/kyra/engine/eobcommon.cpp:373 engines/lure/metaengine.cpp:218
 #: engines/nancy/input.cpp:207 engines/tinsel/metaengine.cpp:275
 #: engines/titanic/metaengine.cpp:312 engines/titanic/metaengine.cpp:390
@@ -5696,7 +5697,7 @@ msgstr ""
 #: engines/access/metaengine.cpp:258 engines/asylum/metaengine.cpp:189
 #: engines/asylum/metaengine.cpp:219 engines/cine/metaengine.cpp:398
 #: engines/efh/metaengine.cpp:307 engines/hdb/metaengine.cpp:231
-#: engines/hypno/metaengine.cpp:296 engines/hypno/metaengine.cpp:349
+#: engines/hypno/metaengine.cpp:357 engines/hypno/metaengine.cpp:405
 #: engines/kyra/engine/eobcommon.cpp:374 engines/lure/metaengine.cpp:226
 #: engines/nancy/input.cpp:213 engines/tinsel/metaengine.cpp:281
 #: engines/titanic/metaengine.cpp:318 engines/titanic/metaengine.cpp:396
@@ -6159,8 +6160,8 @@ msgstr ""
 #: engines/cine/metaengine.cpp:271 engines/cruise/metaengine.cpp:169
 #: engines/dm/metaengine.cpp:126 engines/draci/metaengine.cpp:196
 #: engines/drascula/metaengine.cpp:206 engines/hopkins/metaengine.cpp:207
-#: engines/hugo/metaengine.cpp:130 engines/hypno/metaengine.cpp:166
-#: engines/hypno/metaengine.cpp:267 engines/hypno/metaengine.cpp:311
+#: engines/hugo/metaengine.cpp:130 engines/hypno/metaengine.cpp:225
+#: engines/hypno/metaengine.cpp:324 engines/hypno/metaengine.cpp:372
 #: engines/illusions/metaengine.cpp:90 engines/lab/metaengine.cpp:152
 #: engines/lure/metaengine.cpp:169 engines/made/metaengine.cpp:133
 #: engines/mads/metaengine.cpp:317 engines/neverhood/metaengine.cpp:186
@@ -6794,7 +6795,7 @@ msgstr ""
 
 #. I18N: This keymap allows skipping video cutscenes
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
-#: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
+#: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:210
 #: engines/phoenixvr/metaengine.cpp:94 engines/scumm/help.cpp:79
 #: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
@@ -7897,7 +7898,7 @@ msgstr ""
 msgid "Select option 14"
 msgstr ""
 
-#: engines/efh/metaengine.cpp:257 engines/hypno/metaengine.cpp:312
+#: engines/efh/metaengine.cpp:257 engines/hypno/metaengine.cpp:373
 #: engines/sludge/keymapper_tables.h:35 engines/supernova/metaengine.cpp:218
 msgid "Menu keymappings"
 msgstr ""
@@ -9351,88 +9352,144 @@ msgstr ""
 msgid "Intro keymappings"
 msgstr ""
 
-#: engines/hypno/metaengine.cpp:134
+#: engines/hypno/metaengine.cpp:134 engines/hypno/metaengine.cpp:144
 msgid "Primary shoot"
 msgstr ""
 
-#: engines/hypno/metaengine.cpp:140
+#: engines/hypno/metaengine.cpp:139
 msgid "Secondary shoot"
 msgstr ""
 
-#: engines/hypno/metaengine.cpp:146 engines/lab/metaengine.cpp:209
+#: engines/hypno/metaengine.cpp:149
+msgid "Rapid fire / secondary action"
+msgstr ""
+
+#: engines/hypno/metaengine.cpp:155
+msgid "Aim left"
+msgstr ""
+
+#: engines/hypno/metaengine.cpp:160
+msgid "Aim down"
+msgstr ""
+
+#: engines/hypno/metaengine.cpp:165
+msgid "Aim right"
+msgstr ""
+
+#: engines/hypno/metaengine.cpp:170
+msgid "Aim up"
+msgstr ""
+
+#: engines/hypno/metaengine.cpp:175 engines/scumm/metaengine.cpp:1202
+#: engines/scumm/metaengine.cpp:1279
+msgid "Stick left"
+msgstr ""
+
+#: engines/hypno/metaengine.cpp:182 engines/scumm/metaengine.cpp:1195
+#: engines/scumm/metaengine.cpp:1272
+msgid "Stick down"
+msgstr ""
+
+#: engines/hypno/metaengine.cpp:189 engines/scumm/metaengine.cpp:1209
+#: engines/scumm/metaengine.cpp:1286
+msgid "Stick right"
+msgstr ""
+
+#: engines/hypno/metaengine.cpp:196 engines/scumm/metaengine.cpp:1188
+#: engines/scumm/metaengine.cpp:1265
+msgid "Stick up"
+msgstr ""
+
+#: engines/hypno/metaengine.cpp:203 engines/lab/metaengine.cpp:209
 #: engines/ngi/metaengine.cpp:168 engines/sherlock/metaengine.cpp:872
 #: engines/teenagent/metaengine.cpp:232 engines/toon/metaengine.cpp:98
 #: engines/voyeur/metaengine.cpp:235
 msgid "Skip intro"
 msgstr ""
 
-#: engines/hypno/metaengine.cpp:167
+#: engines/hypno/metaengine.cpp:226
 msgid "Exit menu keymappings"
 msgstr ""
 
-#: engines/hypno/metaengine.cpp:168
+#: engines/hypno/metaengine.cpp:227
 msgid "Difficulty selection menu keymappings"
 msgstr ""
 
-#: engines/hypno/metaengine.cpp:169
+#: engines/hypno/metaengine.cpp:228
 msgid "Retry menu keymappings"
 msgstr ""
 
-#: engines/hypno/metaengine.cpp:172 engines/hypno/metaengine.cpp:270
-#: engines/hypno/metaengine.cpp:322
+#: engines/hypno/metaengine.cpp:231 engines/hypno/metaengine.cpp:327
+#: engines/hypno/metaengine.cpp:382
 msgid "Skip level (cheat)"
 msgstr ""
 
 #. I18N: (Game name: Soldier Boyz) player refers to the users own character.
 #. I18N: (Game name: Marvel Comics Spider-Man: The Sinister Six) player refers to the users own character.
 #. I18N: (Game name: Wetlands) player refers to the users own character.
-#: engines/hypno/metaengine.cpp:180 engines/hypno/metaengine.cpp:278
-#: engines/hypno/metaengine.cpp:329
+#: engines/hypno/metaengine.cpp:238 engines/hypno/metaengine.cpp:334
+#: engines/hypno/metaengine.cpp:388
 msgid "Kill player"
 msgstr ""
 
 #. I18N: (Game name: Soldier Boyz) the game has 3 difficulty levels: Chump, Punk and Badass. Chump is the easy mode.
-#: engines/hypno/metaengine.cpp:205
+#: engines/hypno/metaengine.cpp:262
 msgid "Chump"
 msgstr ""
 
 #. I18N: (Game name: Soldier Boyz) the game has 3 difficulty levels: Chump, Punk and Badass. Punk is the medium mode.
-#: engines/hypno/metaengine.cpp:212
+#: engines/hypno/metaengine.cpp:269
 msgid "Punk"
 msgstr ""
 
 #. I18N: (Game name: Soldier Boyz) the game has 3 difficulty levels: Chump, Punk and Badass. Badass is the hard mode.
-#: engines/hypno/metaengine.cpp:219
+#: engines/hypno/metaengine.cpp:276
 msgid "Badass"
 msgstr ""
 
 #. I18N: (Game name: Soldier Boyz) This makes the player restart from the last checkpoint.
-#: engines/hypno/metaengine.cpp:232
+#: engines/hypno/metaengine.cpp:289
 msgid "Retry sector"
 msgstr ""
 
 #. I18N: (Game name: Soldier Boyz) This makes the player restart the current mission / level.
-#: engines/hypno/metaengine.cpp:239
+#: engines/hypno/metaengine.cpp:296
 msgid "Restart territory"
 msgstr ""
 
-#: engines/hypno/metaengine.cpp:245
+#: engines/hypno/metaengine.cpp:302
 msgid "Begin new mission"
 msgstr ""
 
-#: engines/hypno/metaengine.cpp:313
+#: engines/hypno/metaengine.cpp:374
 msgid "Pause keymappings"
 msgstr ""
 
-#: engines/hypno/metaengine.cpp:314
+#: engines/hypno/metaengine.cpp:375
 msgid "Direction keymappings"
 msgstr ""
 
-#: engines/hypno/metaengine.cpp:316 engines/mads/metaengine.cpp:391
+#: engines/hypno/metaengine.cpp:377 engines/mads/metaengine.cpp:391
 #: engines/tsage/metaengine.cpp:259
 msgid "Show credits"
 msgstr ""
 
+#: engines/hypno/metaengine.cpp:417
+msgid "Menu left"
+msgstr ""
+
+#: engines/hypno/metaengine.cpp:425
+msgid "Menu down"
+msgstr ""
+
+#: engines/hypno/metaengine.cpp:433
+msgid "Menu right"
+msgstr ""
+
+#: engines/hypno/metaengine.cpp:441
+msgid "Menu up"
+msgstr ""
+
 #. I18N: Action in In Cold Blood
 #: engines/icb/icb.cpp:107
 msgid "Arm"
@@ -12463,22 +12520,6 @@ msgstr ""
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1188 engines/scumm/metaengine.cpp:1265
-msgid "Stick up"
-msgstr ""
-
-#: engines/scumm/metaengine.cpp:1195 engines/scumm/metaengine.cpp:1272
-msgid "Stick down"
-msgstr ""
-
-#: engines/scumm/metaengine.cpp:1202 engines/scumm/metaengine.cpp:1279
-msgid "Stick left"
-msgstr ""
-
-#: engines/scumm/metaengine.cpp:1209 engines/scumm/metaengine.cpp:1286
-msgid "Stick right"
-msgstr ""
-
 #: engines/scumm/metaengine.cpp:1216 engines/scumm/metaengine.cpp:1293
 msgid "Fire / select"
 msgstr ""



From noreply at scummvm.org  Sat Jun 13 10:20:49 2026
From: noreply at scummvm.org (ScummVM-Translations)
Date: Sat, 13 Jun 2026 10:20:49 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 3a1c752079420a131c6f9cc84ef4618244511158
Message-ID: <20260613102049.E7CCC7005BC@ron.scummvm.net>

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

Summary:
3a1c752079 I18N: Update translation files


Commit: 3a1c752079420a131c6f9cc84ef4618244511158
    https://github.com/scummvm/scummvm/commit/3a1c752079420a131c6f9cc84ef4618244511158
Author: Weblate (noreply at weblate.org)
Date: 2026-06-13T10:20:30Z

Commit Message:
I18N: Update translation files

Updated by "Update PO files to match POT (msgmerge)" hook in Weblate.

Translation: ScummVM/scummvm
Translate-URL: https://translations.scummvm.org/projects/scummvm/scummvm/

Changed paths:
    po/ar.po
    po/be-tarask.po
    po/be_BY.po
    po/ca_ES.po
    po/cs_CZ.po
    po/da.po
    po/de_DE.po
    po/el.po
    po/es_ES.po
    po/eu.po
    po/fi_FI.po
    po/fr_FR.po
    po/gl_ES.po
    po/he.po
    po/hi.po
    po/hu_HU.po
    po/it_IT.po
    po/ja.po
    po/ka.po
    po/ko.po
    po/nb_NO.po
    po/nl_NL.po
    po/nn_NO.po
    po/pl_PL.po
    po/pt_BR.po
    po/pt_PT.po
    po/ro.po
    po/ru_RU.po
    po/sv_SE.po
    po/tr.po
    po/uk_UA.po
    po/zh.po
    po/zh_Hans.po
    po/zh_Hant.po


diff --git a/po/ar.po b/po/ar.po
index 03a757f14ba..dd6e474bcd6 100644
--- a/po/ar.po
+++ b/po/ar.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-13 06:14+0000\n"
+"POT-Creation-Date: 2026-06-13 10:20+0000\n"
 "PO-Revision-Date: 2026-02-24 22:26+0000\n"
 "Last-Translator: Mohamed Shaaban <mohmedbashamedo at hotmail.com>\n"
 "Language-Team: Arabic <https://translations.scummvm.org/projects/scummvm/"
@@ -141,7 +141,7 @@ msgstr "???????? ????????????"
 #: engines/buried/saveload.cpp:83 engines/cine/saveload.cpp:857
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:123
 #: engines/drascula/saveload.cpp:51 engines/grim/inputdialog.cpp:84
-#: engines/hypno/metaengine.cpp:225 engines/mohawk/myst_stacks/menu.cpp:283
+#: engines/hypno/metaengine.cpp:282 engines/mohawk/myst_stacks/menu.cpp:283
 #: engines/mohawk/myst_stacks/menu.cpp:300
 #: engines/mohawk/myst_stacks/menu.cpp:318
 #: engines/mohawk/riven_stacks/aspit.cpp:349
@@ -351,7 +351,7 @@ msgstr "?????????? ?????? ???????? ????????. ???? ???????? ????????????????"
 #: engines/chamber/metaengine.cpp:91 engines/director/events.cpp:122
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:139
 #: engines/efh/metaengine.cpp:362 engines/grim/grim.cpp:366
-#: engines/hypno/metaengine.cpp:192 engines/kyra/gui/saveload_eob.cpp:611
+#: engines/hypno/metaengine.cpp:249 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:197
 #: engines/mm/mm1/metaengine.cpp:54 engines/parallaction/metaengine.cpp:169
 #: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
@@ -370,7 +370,7 @@ msgstr "??????"
 #: engines/chamber/metaengine.cpp:97 engines/director/events.cpp:122
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:139
 #: engines/efh/metaengine.cpp:368 engines/grim/grim.cpp:366
-#: engines/hypno/metaengine.cpp:198 engines/kyra/gui/saveload_eob.cpp:611
+#: engines/hypno/metaengine.cpp:255 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:204
 #: engines/mm/mm1/metaengine.cpp:52 engines/parallaction/metaengine.cpp:175
 #: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
@@ -3388,7 +3388,7 @@ msgstr ""
 #: backends/events/default/default-events.cpp:358
 #: engines/adl/metaengine.cpp:352 engines/buried/metaengine.cpp:154
 #: engines/chamber/metaengine.cpp:84 engines/drascula/metaengine.cpp:304
-#: engines/hdb/metaengine.cpp:275 engines/hypno/metaengine.cpp:251
+#: engines/hdb/metaengine.cpp:275 engines/hypno/metaengine.cpp:308
 #: engines/lab/metaengine.cpp:172 engines/mohawk/myst_stacks/menu.cpp:317
 #: engines/mohawk/riven_stacks/aspit.cpp:454
 #: engines/neverhood/metaengine.cpp:200 engines/saga/metaengine.cpp:449
@@ -3988,8 +3988,9 @@ msgstr "???????? ?????? ????????????"
 #: engines/buried/metaengine.cpp:167 engines/crab/input/input.cpp:261
 #: engines/cruise/metaengine.cpp:217 engines/dm/metaengine.cpp:180
 #: engines/grim/grim.cpp:550 engines/hdb/metaengine.cpp:258
-#: engines/hypno/metaengine.cpp:186 engines/hypno/metaengine.cpp:369
-#: engines/icb/icb.cpp:137 engines/icb/icb.cpp:203 engines/mohawk/myst.cpp:595
+#: engines/hypno/metaengine.cpp:243 engines/hypno/metaengine.cpp:339
+#: engines/hypno/metaengine.cpp:456 engines/icb/icb.cpp:137
+#: engines/icb/icb.cpp:203 engines/mohawk/myst.cpp:595
 #: engines/mohawk/riven.cpp:876 engines/ngi/metaengine.cpp:136
 #: engines/sky/metaengine.cpp:126 engines/sludge/keymapper_tables.h:52
 #: engines/sludge/keymapper_tables.h:125 engines/sludge/keymapper_tables.h:187
@@ -4931,7 +4932,7 @@ msgstr "Y"
 #: backends/platform/sdl/opendingux/opendingux.cpp:57
 #: backends/platform/sdl/ps3/ps3.cpp:50 backends/platform/sdl/psp2/psp2.cpp:51
 #: engines/bagel/metaengine.cpp:64 engines/efh/metaengine.cpp:397
-#: engines/got/metaengine.cpp:76 engines/hypno/metaengine.cpp:363
+#: engines/got/metaengine.cpp:76 engines/hypno/metaengine.cpp:449
 #: engines/mm/mm1/metaengine.cpp:50 engines/mm/mm1/metaengine.cpp:97
 #: engines/sherlock/metaengine.cpp:660 engines/sherlock/metaengine.cpp:672
 #: engines/sherlock/metaengine.cpp:1014 engines/supernova/metaengine.cpp:303
@@ -6168,7 +6169,7 @@ msgstr "???????? ????????"
 #: engines/access/metaengine.cpp:237 engines/asylum/metaengine.cpp:171
 #: engines/asylum/metaengine.cpp:198 engines/cine/metaengine.cpp:377
 #: engines/efh/metaengine.cpp:286 engines/hdb/metaengine.cpp:213
-#: engines/hypno/metaengine.cpp:302 engines/hypno/metaengine.cpp:356
+#: engines/hypno/metaengine.cpp:363 engines/hypno/metaengine.cpp:411
 #: engines/nancy/input.cpp:195 engines/tinsel/metaengine.cpp:263
 #: engines/titanic/metaengine.cpp:324 engines/ultima/ultima8/metaengine.cpp:52
 msgid "Move up"
@@ -6178,7 +6179,7 @@ msgstr "????????"
 #: engines/access/metaengine.cpp:244 engines/asylum/metaengine.cpp:177
 #: engines/asylum/metaengine.cpp:205 engines/cine/metaengine.cpp:384
 #: engines/efh/metaengine.cpp:293 engines/hdb/metaengine.cpp:219
-#: engines/hypno/metaengine.cpp:290 engines/hypno/metaengine.cpp:342
+#: engines/hypno/metaengine.cpp:351 engines/hypno/metaengine.cpp:399
 #: engines/nancy/input.cpp:201 engines/tinsel/metaengine.cpp:269
 #: engines/titanic/metaengine.cpp:330 engines/ultima/ultima8/metaengine.cpp:53
 msgid "Move down"
@@ -6189,7 +6190,7 @@ msgstr "???????? ??????????"
 #: engines/access/metaengine.cpp:251 engines/asylum/metaengine.cpp:183
 #: engines/asylum/metaengine.cpp:212 engines/cine/metaengine.cpp:391
 #: engines/efh/metaengine.cpp:300 engines/hdb/metaengine.cpp:225
-#: engines/hypno/metaengine.cpp:284 engines/hypno/metaengine.cpp:335
+#: engines/hypno/metaengine.cpp:345 engines/hypno/metaengine.cpp:393
 #: engines/kyra/engine/eobcommon.cpp:373 engines/lure/metaengine.cpp:218
 #: engines/nancy/input.cpp:207 engines/tinsel/metaengine.cpp:275
 #: engines/titanic/metaengine.cpp:312 engines/titanic/metaengine.cpp:390
@@ -6202,7 +6203,7 @@ msgstr "???????? ??????????"
 #: engines/access/metaengine.cpp:258 engines/asylum/metaengine.cpp:189
 #: engines/asylum/metaengine.cpp:219 engines/cine/metaengine.cpp:398
 #: engines/efh/metaengine.cpp:307 engines/hdb/metaengine.cpp:231
-#: engines/hypno/metaengine.cpp:296 engines/hypno/metaengine.cpp:349
+#: engines/hypno/metaengine.cpp:357 engines/hypno/metaengine.cpp:405
 #: engines/kyra/engine/eobcommon.cpp:374 engines/lure/metaengine.cpp:226
 #: engines/nancy/input.cpp:213 engines/tinsel/metaengine.cpp:281
 #: engines/titanic/metaengine.cpp:318 engines/titanic/metaengine.cpp:396
@@ -6703,8 +6704,8 @@ msgstr ""
 #: engines/cine/metaengine.cpp:271 engines/cruise/metaengine.cpp:169
 #: engines/dm/metaengine.cpp:126 engines/draci/metaengine.cpp:196
 #: engines/drascula/metaengine.cpp:206 engines/hopkins/metaengine.cpp:207
-#: engines/hugo/metaengine.cpp:130 engines/hypno/metaengine.cpp:166
-#: engines/hypno/metaengine.cpp:267 engines/hypno/metaengine.cpp:311
+#: engines/hugo/metaengine.cpp:130 engines/hypno/metaengine.cpp:225
+#: engines/hypno/metaengine.cpp:324 engines/hypno/metaengine.cpp:372
 #: engines/illusions/metaengine.cpp:90 engines/lab/metaengine.cpp:152
 #: engines/lure/metaengine.cpp:169 engines/made/metaengine.cpp:133
 #: engines/mads/metaengine.cpp:317 engines/neverhood/metaengine.cpp:186
@@ -7457,7 +7458,7 @@ msgstr "?????????? ????????????"
 
 #. I18N: This keymap allows skipping video cutscenes
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
-#: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
+#: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:210
 #: engines/phoenixvr/metaengine.cpp:94 engines/scumm/help.cpp:79
 #: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
@@ -8813,7 +8814,7 @@ msgstr "?????? ?????????????? ??????"
 msgid "Select option 14"
 msgstr "?????? ?????????????? ??????"
 
-#: engines/efh/metaengine.cpp:257 engines/hypno/metaengine.cpp:312
+#: engines/efh/metaengine.cpp:257 engines/hypno/metaengine.cpp:373
 #: engines/sludge/keymapper_tables.h:35 engines/supernova/metaengine.cpp:218
 #, fuzzy
 #| msgid "Game Options"
@@ -10561,15 +10562,69 @@ msgstr "?????? ?????????? ?????????????? ???? ?????? ?????????? ?????????????? 
 msgid "Intro keymappings"
 msgstr "???????????? ????????????"
 
-#: engines/hypno/metaengine.cpp:134
+#: engines/hypno/metaengine.cpp:134 engines/hypno/metaengine.cpp:144
 msgid "Primary shoot"
 msgstr ""
 
-#: engines/hypno/metaengine.cpp:140
+#: engines/hypno/metaengine.cpp:139
 msgid "Secondary shoot"
 msgstr ""
 
-#: engines/hypno/metaengine.cpp:146 engines/lab/metaengine.cpp:209
+#: engines/hypno/metaengine.cpp:149
+msgid "Rapid fire / secondary action"
+msgstr ""
+
+#: engines/hypno/metaengine.cpp:155
+#, fuzzy
+#| msgid "Slide Left"
+msgid "Aim left"
+msgstr "?????????????? ?????? ????????????"
+
+#: engines/hypno/metaengine.cpp:160
+#, fuzzy
+#| msgid "Scroll down"
+msgid "Aim down"
+msgstr "?????? ???????????? ??????????"
+
+#: engines/hypno/metaengine.cpp:165
+#, fuzzy
+#| msgid "Up Right"
+msgid "Aim right"
+msgstr "???????? ????????????"
+
+#: engines/hypno/metaengine.cpp:170
+msgid "Aim up"
+msgstr ""
+
+#: engines/hypno/metaengine.cpp:175 engines/scumm/metaengine.cpp:1202
+#: engines/scumm/metaengine.cpp:1279
+#, fuzzy
+#| msgid "Slide Left"
+msgid "Stick left"
+msgstr "?????????????? ?????? ????????????"
+
+#: engines/hypno/metaengine.cpp:182 engines/scumm/metaengine.cpp:1195
+#: engines/scumm/metaengine.cpp:1272
+#, fuzzy
+#| msgid "Scroll down"
+msgid "Stick down"
+msgstr "?????? ???????????? ??????????"
+
+#: engines/hypno/metaengine.cpp:189 engines/scumm/metaengine.cpp:1209
+#: engines/scumm/metaengine.cpp:1286
+#, fuzzy
+#| msgid "Slide Right"
+msgid "Stick right"
+msgstr "???????????? ????????????"
+
+#: engines/hypno/metaengine.cpp:196 engines/scumm/metaengine.cpp:1188
+#: engines/scumm/metaengine.cpp:1265
+#, fuzzy
+#| msgid "Pick up"
+msgid "Stick up"
+msgstr "??????????"
+
+#: engines/hypno/metaengine.cpp:203 engines/lab/metaengine.cpp:209
 #: engines/ngi/metaengine.cpp:168 engines/sherlock/metaengine.cpp:872
 #: engines/teenagent/metaengine.cpp:232 engines/toon/metaengine.cpp:98
 #: engines/voyeur/metaengine.cpp:235
@@ -10578,26 +10633,26 @@ msgstr ""
 msgid "Skip intro"
 msgstr "???????? ????????"
 
-#: engines/hypno/metaengine.cpp:167
+#: engines/hypno/metaengine.cpp:226
 #, fuzzy
 #| msgid "Game Options"
 msgid "Exit menu keymappings"
 msgstr "???????????? ????????????"
 
-#: engines/hypno/metaengine.cpp:168
+#: engines/hypno/metaengine.cpp:227
 #, fuzzy
 #| msgid "Game Options"
 msgid "Difficulty selection menu keymappings"
 msgstr "???????????? ????????????"
 
-#: engines/hypno/metaengine.cpp:169
+#: engines/hypno/metaengine.cpp:228
 #, fuzzy
 #| msgid "Game Options"
 msgid "Retry menu keymappings"
 msgstr "???????????? ????????????"
 
-#: engines/hypno/metaengine.cpp:172 engines/hypno/metaengine.cpp:270
-#: engines/hypno/metaengine.cpp:322
+#: engines/hypno/metaengine.cpp:231 engines/hypno/metaengine.cpp:327
+#: engines/hypno/metaengine.cpp:382
 #, fuzzy
 #| msgid "Speech"
 msgid "Skip level (cheat)"
@@ -10606,61 +10661,85 @@ msgstr "????????"
 #. I18N: (Game name: Soldier Boyz) player refers to the users own character.
 #. I18N: (Game name: Marvel Comics Spider-Man: The Sinister Six) player refers to the users own character.
 #. I18N: (Game name: Wetlands) player refers to the users own character.
-#: engines/hypno/metaengine.cpp:180 engines/hypno/metaengine.cpp:278
-#: engines/hypno/metaengine.cpp:329
+#: engines/hypno/metaengine.cpp:238 engines/hypno/metaengine.cpp:334
+#: engines/hypno/metaengine.cpp:388
 msgid "Kill player"
 msgstr ""
 
 #. I18N: (Game name: Soldier Boyz) the game has 3 difficulty levels: Chump, Punk and Badass. Chump is the easy mode.
-#: engines/hypno/metaengine.cpp:205
+#: engines/hypno/metaengine.cpp:262
 msgid "Chump"
 msgstr ""
 
 #. I18N: (Game name: Soldier Boyz) the game has 3 difficulty levels: Chump, Punk and Badass. Punk is the medium mode.
-#: engines/hypno/metaengine.cpp:212
+#: engines/hypno/metaengine.cpp:269
 msgid "Punk"
 msgstr ""
 
 #. I18N: (Game name: Soldier Boyz) the game has 3 difficulty levels: Chump, Punk and Badass. Badass is the hard mode.
-#: engines/hypno/metaengine.cpp:219
+#: engines/hypno/metaengine.cpp:276
 msgid "Badass"
 msgstr ""
 
 #. I18N: (Game name: Soldier Boyz) This makes the player restart from the last checkpoint.
-#: engines/hypno/metaengine.cpp:232
+#: engines/hypno/metaengine.cpp:289
 #, fuzzy
 #| msgid "Retreat"
 msgid "Retry sector"
 msgstr "??????????"
 
 #. I18N: (Game name: Soldier Boyz) This makes the player restart the current mission / level.
-#: engines/hypno/metaengine.cpp:239
+#: engines/hypno/metaengine.cpp:296
 #, fuzzy
 #| msgid "Transitions:"
 msgid "Restart territory"
 msgstr "????????????????????:"
 
-#: engines/hypno/metaengine.cpp:245
+#: engines/hypno/metaengine.cpp:302
 msgid "Begin new mission"
 msgstr ""
 
-#: engines/hypno/metaengine.cpp:313
+#: engines/hypno/metaengine.cpp:374
 #, fuzzy
 #| msgid "Game Options"
 msgid "Pause keymappings"
 msgstr "???????????? ????????????"
 
-#: engines/hypno/metaengine.cpp:314
+#: engines/hypno/metaengine.cpp:375
 #, fuzzy
 #| msgid "Game Options"
 msgid "Direction keymappings"
 msgstr "???????????? ????????????"
 
-#: engines/hypno/metaengine.cpp:316 engines/mads/metaengine.cpp:391
+#: engines/hypno/metaengine.cpp:377 engines/mads/metaengine.cpp:391
 #: engines/tsage/metaengine.cpp:259
 msgid "Show credits"
 msgstr "?????????? ????????????????????"
 
+#: engines/hypno/metaengine.cpp:417
+#, fuzzy
+#| msgid "Move left"
+msgid "Menu left"
+msgstr "???????? ??????????"
+
+#: engines/hypno/metaengine.cpp:425
+#, fuzzy
+#| msgid "Move down"
+msgid "Menu down"
+msgstr "???????? ??????????"
+
+#: engines/hypno/metaengine.cpp:433
+#, fuzzy
+#| msgid "Move right"
+msgid "Menu right"
+msgstr "???????? ??????????"
+
+#: engines/hypno/metaengine.cpp:441
+#, fuzzy
+#| msgid "Menu"
+msgid "Menu up"
+msgstr "??????????????"
+
 #. I18N: Action in In Cold Blood
 #: engines/icb/icb.cpp:107
 msgid "Arm"
@@ -14258,30 +14337,6 @@ msgstr ""
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1188 engines/scumm/metaengine.cpp:1265
-#, fuzzy
-#| msgid "Pick up"
-msgid "Stick up"
-msgstr "??????????"
-
-#: engines/scumm/metaengine.cpp:1195 engines/scumm/metaengine.cpp:1272
-#, fuzzy
-#| msgid "Scroll down"
-msgid "Stick down"
-msgstr "?????? ???????????? ??????????"
-
-#: engines/scumm/metaengine.cpp:1202 engines/scumm/metaengine.cpp:1279
-#, fuzzy
-#| msgid "Slide Left"
-msgid "Stick left"
-msgstr "?????????????? ?????? ????????????"
-
-#: engines/scumm/metaengine.cpp:1209 engines/scumm/metaengine.cpp:1286
-#, fuzzy
-#| msgid "Slide Right"
-msgid "Stick right"
-msgstr "???????????? ????????????"
-
 #: engines/scumm/metaengine.cpp:1216 engines/scumm/metaengine.cpp:1293
 #, fuzzy
 #| msgid "Move left"
diff --git a/po/be-tarask.po b/po/be-tarask.po
index 4a5b048d5a1..f5cff15f155 100644
--- a/po/be-tarask.po
+++ b/po/be-tarask.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-13 06:14+0000\n"
+"POT-Creation-Date: 2026-06-13 10:20+0000\n"
 "PO-Revision-Date: 2023-02-26 15:01+0000\n"
 "Last-Translator: Kastu?? K. <oakum2334 at gmail.com>\n"
 "Language-Team: Belarusian (Tara??kievica) <https://translations.scummvm.org/"
@@ -141,7 +141,7 @@ msgstr "??????????"
 #: engines/buried/saveload.cpp:83 engines/cine/saveload.cpp:857
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:123
 #: engines/drascula/saveload.cpp:51 engines/grim/inputdialog.cpp:84
-#: engines/hypno/metaengine.cpp:225 engines/mohawk/myst_stacks/menu.cpp:283
+#: engines/hypno/metaengine.cpp:282 engines/mohawk/myst_stacks/menu.cpp:283
 #: engines/mohawk/myst_stacks/menu.cpp:300
 #: engines/mohawk/myst_stacks/menu.cpp:318
 #: engines/mohawk/riven_stacks/aspit.cpp:349
@@ -353,7 +353,7 @@ msgstr "?????????? ???????????????? ???????????? ??????????. ???? ???????????? 
 #: engines/chamber/metaengine.cpp:91 engines/director/events.cpp:122
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:139
 #: engines/efh/metaengine.cpp:362 engines/grim/grim.cpp:366
-#: engines/hypno/metaengine.cpp:192 engines/kyra/gui/saveload_eob.cpp:611
+#: engines/hypno/metaengine.cpp:249 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:197
 #: engines/mm/mm1/metaengine.cpp:54 engines/parallaction/metaengine.cpp:169
 #: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
@@ -372,7 +372,7 @@ msgstr "??????"
 #: engines/chamber/metaengine.cpp:97 engines/director/events.cpp:122
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:139
 #: engines/efh/metaengine.cpp:368 engines/grim/grim.cpp:366
-#: engines/hypno/metaengine.cpp:198 engines/kyra/gui/saveload_eob.cpp:611
+#: engines/hypno/metaengine.cpp:255 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:204
 #: engines/mm/mm1/metaengine.cpp:52 engines/parallaction/metaengine.cpp:175
 #: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
@@ -3355,7 +3355,7 @@ msgstr ""
 #: backends/events/default/default-events.cpp:358
 #: engines/adl/metaengine.cpp:352 engines/buried/metaengine.cpp:154
 #: engines/chamber/metaengine.cpp:84 engines/drascula/metaengine.cpp:304
-#: engines/hdb/metaengine.cpp:275 engines/hypno/metaengine.cpp:251
+#: engines/hdb/metaengine.cpp:275 engines/hypno/metaengine.cpp:308
 #: engines/lab/metaengine.cpp:172 engines/mohawk/myst_stacks/menu.cpp:317
 #: engines/mohawk/riven_stacks/aspit.cpp:454
 #: engines/neverhood/metaengine.cpp:200 engines/saga/metaengine.cpp:449
@@ -3944,8 +3944,9 @@ msgstr "?????????? ????????????"
 #: engines/buried/metaengine.cpp:167 engines/crab/input/input.cpp:261
 #: engines/cruise/metaengine.cpp:217 engines/dm/metaengine.cpp:180
 #: engines/grim/grim.cpp:550 engines/hdb/metaengine.cpp:258
-#: engines/hypno/metaengine.cpp:186 engines/hypno/metaengine.cpp:369
-#: engines/icb/icb.cpp:137 engines/icb/icb.cpp:203 engines/mohawk/myst.cpp:595
+#: engines/hypno/metaengine.cpp:243 engines/hypno/metaengine.cpp:339
+#: engines/hypno/metaengine.cpp:456 engines/icb/icb.cpp:137
+#: engines/icb/icb.cpp:203 engines/mohawk/myst.cpp:595
 #: engines/mohawk/riven.cpp:876 engines/ngi/metaengine.cpp:136
 #: engines/sky/metaengine.cpp:126 engines/sludge/keymapper_tables.h:52
 #: engines/sludge/keymapper_tables.h:125 engines/sludge/keymapper_tables.h:187
@@ -4893,7 +4894,7 @@ msgstr "Y"
 #: backends/platform/sdl/opendingux/opendingux.cpp:57
 #: backends/platform/sdl/ps3/ps3.cpp:50 backends/platform/sdl/psp2/psp2.cpp:51
 #: engines/bagel/metaengine.cpp:64 engines/efh/metaengine.cpp:397
-#: engines/got/metaengine.cpp:76 engines/hypno/metaengine.cpp:363
+#: engines/got/metaengine.cpp:76 engines/hypno/metaengine.cpp:449
 #: engines/mm/mm1/metaengine.cpp:50 engines/mm/mm1/metaengine.cpp:97
 #: engines/sherlock/metaengine.cpp:660 engines/sherlock/metaengine.cpp:672
 #: engines/sherlock/metaengine.cpp:1014 engines/supernova/metaengine.cpp:303
@@ -6101,7 +6102,7 @@ msgstr "?????????????????????? ??????????"
 #: engines/access/metaengine.cpp:237 engines/asylum/metaengine.cpp:171
 #: engines/asylum/metaengine.cpp:198 engines/cine/metaengine.cpp:377
 #: engines/efh/metaengine.cpp:286 engines/hdb/metaengine.cpp:213
-#: engines/hypno/metaengine.cpp:302 engines/hypno/metaengine.cpp:356
+#: engines/hypno/metaengine.cpp:363 engines/hypno/metaengine.cpp:411
 #: engines/nancy/input.cpp:195 engines/tinsel/metaengine.cpp:263
 #: engines/titanic/metaengine.cpp:324 engines/ultima/ultima8/metaengine.cpp:52
 msgid "Move up"
@@ -6111,7 +6112,7 @@ msgstr "?????????????? ??????????"
 #: engines/access/metaengine.cpp:244 engines/asylum/metaengine.cpp:177
 #: engines/asylum/metaengine.cpp:205 engines/cine/metaengine.cpp:384
 #: engines/efh/metaengine.cpp:293 engines/hdb/metaengine.cpp:219
-#: engines/hypno/metaengine.cpp:290 engines/hypno/metaengine.cpp:342
+#: engines/hypno/metaengine.cpp:351 engines/hypno/metaengine.cpp:399
 #: engines/nancy/input.cpp:201 engines/tinsel/metaengine.cpp:269
 #: engines/titanic/metaengine.cpp:330 engines/ultima/ultima8/metaengine.cpp:53
 msgid "Move down"
@@ -6122,7 +6123,7 @@ msgstr "?????????????? ????????"
 #: engines/access/metaengine.cpp:251 engines/asylum/metaengine.cpp:183
 #: engines/asylum/metaengine.cpp:212 engines/cine/metaengine.cpp:391
 #: engines/efh/metaengine.cpp:300 engines/hdb/metaengine.cpp:225
-#: engines/hypno/metaengine.cpp:284 engines/hypno/metaengine.cpp:335
+#: engines/hypno/metaengine.cpp:345 engines/hypno/metaengine.cpp:393
 #: engines/kyra/engine/eobcommon.cpp:373 engines/lure/metaengine.cpp:218
 #: engines/nancy/input.cpp:207 engines/tinsel/metaengine.cpp:275
 #: engines/titanic/metaengine.cpp:312 engines/titanic/metaengine.cpp:390
@@ -6135,7 +6136,7 @@ msgstr "?????????????? ????????????"
 #: engines/access/metaengine.cpp:258 engines/asylum/metaengine.cpp:189
 #: engines/asylum/metaengine.cpp:219 engines/cine/metaengine.cpp:398
 #: engines/efh/metaengine.cpp:307 engines/hdb/metaengine.cpp:231
-#: engines/hypno/metaengine.cpp:296 engines/hypno/metaengine.cpp:349
+#: engines/hypno/metaengine.cpp:357 engines/hypno/metaengine.cpp:405
 #: engines/kyra/engine/eobcommon.cpp:374 engines/lure/metaengine.cpp:226
 #: engines/nancy/input.cpp:213 engines/tinsel/metaengine.cpp:281
 #: engines/titanic/metaengine.cpp:318 engines/titanic/metaengine.cpp:396
@@ -6639,8 +6640,8 @@ msgstr ""
 #: engines/cine/metaengine.cpp:271 engines/cruise/metaengine.cpp:169
 #: engines/dm/metaengine.cpp:126 engines/draci/metaengine.cpp:196
 #: engines/drascula/metaengine.cpp:206 engines/hopkins/metaengine.cpp:207
-#: engines/hugo/metaengine.cpp:130 engines/hypno/metaengine.cpp:166
-#: engines/hypno/metaengine.cpp:267 engines/hypno/metaengine.cpp:311
+#: engines/hugo/metaengine.cpp:130 engines/hypno/metaengine.cpp:225
+#: engines/hypno/metaengine.cpp:324 engines/hypno/metaengine.cpp:372
 #: engines/illusions/metaengine.cpp:90 engines/lab/metaengine.cpp:152
 #: engines/lure/metaengine.cpp:169 engines/made/metaengine.cpp:133
 #: engines/mads/metaengine.cpp:317 engines/neverhood/metaengine.cpp:186
@@ -7399,7 +7400,7 @@ msgstr "???????????????????? ?????????? ??????"
 
 #. I18N: This keymap allows skipping video cutscenes
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
-#: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
+#: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:210
 #: engines/phoenixvr/metaengine.cpp:94 engines/scumm/help.cpp:79
 #: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
@@ -8767,7 +8768,7 @@ msgstr "?????????????? ??????????????????????"
 msgid "Select option 14"
 msgstr "?????????????? ??????????????????????"
 
-#: engines/efh/metaengine.cpp:257 engines/hypno/metaengine.cpp:312
+#: engines/efh/metaengine.cpp:257 engines/hypno/metaengine.cpp:373
 #: engines/sludge/keymapper_tables.h:35 engines/supernova/metaengine.cpp:218
 #, fuzzy
 #| msgid "Game Options"
@@ -10516,17 +10517,70 @@ msgstr "???????????? ????????????, ?????? ???? ???????????????? ?? ????????????
 msgid "Intro keymappings"
 msgstr "~??~?????????? ????????????"
 
-#: engines/hypno/metaengine.cpp:134
+#: engines/hypno/metaengine.cpp:134 engines/hypno/metaengine.cpp:144
 msgid "Primary shoot"
 msgstr ""
 
-#: engines/hypno/metaengine.cpp:140
+#: engines/hypno/metaengine.cpp:139
 #, fuzzy
 #| msgid "Seconds"
 msgid "Secondary shoot"
 msgstr "????????????????"
 
-#: engines/hypno/metaengine.cpp:146 engines/lab/metaengine.cpp:209
+#: engines/hypno/metaengine.cpp:149
+msgid "Rapid fire / secondary action"
+msgstr ""
+
+#: engines/hypno/metaengine.cpp:155
+#, fuzzy
+#| msgid "Slide Left"
+msgid "Aim left"
+msgstr "?????????????????????? ????????????"
+
+#: engines/hypno/metaengine.cpp:160
+#, fuzzy
+#| msgid "Scroll down"
+msgid "Aim down"
+msgstr "?????????????????????????? ????????"
+
+#: engines/hypno/metaengine.cpp:165
+#, fuzzy
+msgid "Aim right"
+msgstr "??????????????"
+
+#: engines/hypno/metaengine.cpp:170
+msgid "Aim up"
+msgstr ""
+
+#: engines/hypno/metaengine.cpp:175 engines/scumm/metaengine.cpp:1202
+#: engines/scumm/metaengine.cpp:1279
+#, fuzzy
+#| msgid "Slide Left"
+msgid "Stick left"
+msgstr "?????????????????????? ????????????"
+
+#: engines/hypno/metaengine.cpp:182 engines/scumm/metaengine.cpp:1195
+#: engines/scumm/metaengine.cpp:1272
+#, fuzzy
+#| msgid "Scroll down"
+msgid "Stick down"
+msgstr "?????????????????????????? ????????"
+
+#: engines/hypno/metaengine.cpp:189 engines/scumm/metaengine.cpp:1209
+#: engines/scumm/metaengine.cpp:1286
+#, fuzzy
+#| msgid "Slide Right"
+msgid "Stick right"
+msgstr "?????????????????????? ??????????????"
+
+#: engines/hypno/metaengine.cpp:196 engines/scumm/metaengine.cpp:1188
+#: engines/scumm/metaengine.cpp:1265
+#, fuzzy
+#| msgid "Pick up"
+msgid "Stick up"
+msgstr "??????????????"
+
+#: engines/hypno/metaengine.cpp:203 engines/lab/metaengine.cpp:209
 #: engines/ngi/metaengine.cpp:168 engines/sherlock/metaengine.cpp:872
 #: engines/teenagent/metaengine.cpp:232 engines/toon/metaengine.cpp:98
 #: engines/voyeur/metaengine.cpp:235
@@ -10535,26 +10589,26 @@ msgstr "????????????????"
 msgid "Skip intro"
 msgstr "?????????????????????? ??????????"
 
-#: engines/hypno/metaengine.cpp:167
+#: engines/hypno/metaengine.cpp:226
 #, fuzzy
 #| msgid "Game Options"
 msgid "Exit menu keymappings"
 msgstr "~??~?????????? ????????????"
 
-#: engines/hypno/metaengine.cpp:168
+#: engines/hypno/metaengine.cpp:227
 #, fuzzy
 #| msgid "Game Options"
 msgid "Difficulty selection menu keymappings"
 msgstr "~??~?????????? ????????????"
 
-#: engines/hypno/metaengine.cpp:169
+#: engines/hypno/metaengine.cpp:228
 #, fuzzy
 #| msgid "Game Options"
 msgid "Retry menu keymappings"
 msgstr "~??~?????????? ????????????"
 
-#: engines/hypno/metaengine.cpp:172 engines/hypno/metaengine.cpp:270
-#: engines/hypno/metaengine.cpp:322
+#: engines/hypno/metaengine.cpp:231 engines/hypno/metaengine.cpp:327
+#: engines/hypno/metaengine.cpp:382
 #, fuzzy
 #| msgid "Speech"
 msgid "Skip level (cheat)"
@@ -10563,59 +10617,83 @@ msgstr "????????????"
 #. I18N: (Game name: Soldier Boyz) player refers to the users own character.
 #. I18N: (Game name: Marvel Comics Spider-Man: The Sinister Six) player refers to the users own character.
 #. I18N: (Game name: Wetlands) player refers to the users own character.
-#: engines/hypno/metaengine.cpp:180 engines/hypno/metaengine.cpp:278
-#: engines/hypno/metaengine.cpp:329
+#: engines/hypno/metaengine.cpp:238 engines/hypno/metaengine.cpp:334
+#: engines/hypno/metaengine.cpp:388
 msgid "Kill player"
 msgstr ""
 
 #. I18N: (Game name: Soldier Boyz) the game has 3 difficulty levels: Chump, Punk and Badass. Chump is the easy mode.
-#: engines/hypno/metaengine.cpp:205
+#: engines/hypno/metaengine.cpp:262
 msgid "Chump"
 msgstr ""
 
 #. I18N: (Game name: Soldier Boyz) the game has 3 difficulty levels: Chump, Punk and Badass. Punk is the medium mode.
-#: engines/hypno/metaengine.cpp:212
+#: engines/hypno/metaengine.cpp:269
 msgid "Punk"
 msgstr ""
 
 #. I18N: (Game name: Soldier Boyz) the game has 3 difficulty levels: Chump, Punk and Badass. Badass is the hard mode.
-#: engines/hypno/metaengine.cpp:219
+#: engines/hypno/metaengine.cpp:276
 msgid "Badass"
 msgstr ""
 
 #. I18N: (Game name: Soldier Boyz) This makes the player restart from the last checkpoint.
-#: engines/hypno/metaengine.cpp:232
+#: engines/hypno/metaengine.cpp:289
 msgid "Retry sector"
 msgstr ""
 
 #. I18N: (Game name: Soldier Boyz) This makes the player restart the current mission / level.
-#: engines/hypno/metaengine.cpp:239
+#: engines/hypno/metaengine.cpp:296
 #, fuzzy
 #| msgid "Transitions:"
 msgid "Restart territory"
 msgstr "????????????????:"
 
-#: engines/hypno/metaengine.cpp:245
+#: engines/hypno/metaengine.cpp:302
 msgid "Begin new mission"
 msgstr ""
 
-#: engines/hypno/metaengine.cpp:313
+#: engines/hypno/metaengine.cpp:374
 #, fuzzy
 #| msgid "Game Options"
 msgid "Pause keymappings"
 msgstr "~??~?????????? ????????????"
 
-#: engines/hypno/metaengine.cpp:314
+#: engines/hypno/metaengine.cpp:375
 #, fuzzy
 #| msgid "Game Options"
 msgid "Direction keymappings"
 msgstr "~??~?????????? ????????????"
 
-#: engines/hypno/metaengine.cpp:316 engines/mads/metaengine.cpp:391
+#: engines/hypno/metaengine.cpp:377 engines/mads/metaengine.cpp:391
 #: engines/tsage/metaengine.cpp:259
 msgid "Show credits"
 msgstr "???????????????? ??????????"
 
+#: engines/hypno/metaengine.cpp:417
+#, fuzzy
+#| msgid "Move left"
+msgid "Menu left"
+msgstr "?????????????? ????????????"
+
+#: engines/hypno/metaengine.cpp:425
+#, fuzzy
+#| msgid "Move down"
+msgid "Menu down"
+msgstr "?????????????? ????????"
+
+#: engines/hypno/metaengine.cpp:433
+#, fuzzy
+#| msgid "Move right"
+msgid "Menu right"
+msgstr "?????????????? ??????????????"
+
+#: engines/hypno/metaengine.cpp:441
+#, fuzzy
+#| msgid "Menu"
+msgid "Menu up"
+msgstr "????????"
+
 #. I18N: Action in In Cold Blood
 #: engines/icb/icb.cpp:107
 msgid "Arm"
@@ -14185,30 +14263,6 @@ msgstr ""
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1188 engines/scumm/metaengine.cpp:1265
-#, fuzzy
-#| msgid "Pick up"
-msgid "Stick up"
-msgstr "??????????????"
-
-#: engines/scumm/metaengine.cpp:1195 engines/scumm/metaengine.cpp:1272
-#, fuzzy
-#| msgid "Scroll down"
-msgid "Stick down"
-msgstr "?????????????????????????? ????????"
-
-#: engines/scumm/metaengine.cpp:1202 engines/scumm/metaengine.cpp:1279
-#, fuzzy
-#| msgid "Slide Left"
-msgid "Stick left"
-msgstr "?????????????????????? ????????????"
-
-#: engines/scumm/metaengine.cpp:1209 engines/scumm/metaengine.cpp:1286
-#, fuzzy
-#| msgid "Slide Right"
-msgid "Stick right"
-msgstr "?????????????????????? ??????????????"
-
 #: engines/scumm/metaengine.cpp:1216 engines/scumm/metaengine.cpp:1293
 #, fuzzy
 #| msgid "Move left"
diff --git a/po/be_BY.po b/po/be_BY.po
index 9ebe2b7bc29..858f69bc54a 100644
--- a/po/be_BY.po
+++ b/po/be_BY.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-13 06:14+0000\n"
+"POT-Creation-Date: 2026-06-13 10:20+0000\n"
 "PO-Revision-Date: 2021-03-21 08:24+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Belarusian <https://translations.scummvm.org/projects/scummvm/"
@@ -139,7 +139,7 @@ msgstr "??????????"
 #: engines/buried/saveload.cpp:83 engines/cine/saveload.cpp:857
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:123
 #: engines/drascula/saveload.cpp:51 engines/grim/inputdialog.cpp:84
-#: engines/hypno/metaengine.cpp:225 engines/mohawk/myst_stacks/menu.cpp:283
+#: engines/hypno/metaengine.cpp:282 engines/mohawk/myst_stacks/menu.cpp:283
 #: engines/mohawk/myst_stacks/menu.cpp:300
 #: engines/mohawk/myst_stacks/menu.cpp:318
 #: engines/mohawk/riven_stacks/aspit.cpp:349
@@ -353,7 +353,7 @@ msgstr "?????????? ???????????? ?????????? ????????????????. ??????????????????
 #: engines/chamber/metaengine.cpp:91 engines/director/events.cpp:122
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:139
 #: engines/efh/metaengine.cpp:362 engines/grim/grim.cpp:366
-#: engines/hypno/metaengine.cpp:192 engines/kyra/gui/saveload_eob.cpp:611
+#: engines/hypno/metaengine.cpp:249 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:197
 #: engines/mm/mm1/metaengine.cpp:54 engines/parallaction/metaengine.cpp:169
 #: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
@@ -372,7 +372,7 @@ msgstr "??????"
 #: engines/chamber/metaengine.cpp:97 engines/director/events.cpp:122
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:139
 #: engines/efh/metaengine.cpp:368 engines/grim/grim.cpp:366
-#: engines/hypno/metaengine.cpp:198 engines/kyra/gui/saveload_eob.cpp:611
+#: engines/hypno/metaengine.cpp:255 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:204
 #: engines/mm/mm1/metaengine.cpp:52 engines/parallaction/metaengine.cpp:175
 #: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
@@ -3373,7 +3373,7 @@ msgstr ""
 #: backends/events/default/default-events.cpp:358
 #: engines/adl/metaengine.cpp:352 engines/buried/metaengine.cpp:154
 #: engines/chamber/metaengine.cpp:84 engines/drascula/metaengine.cpp:304
-#: engines/hdb/metaengine.cpp:275 engines/hypno/metaengine.cpp:251
+#: engines/hdb/metaengine.cpp:275 engines/hypno/metaengine.cpp:308
 #: engines/lab/metaengine.cpp:172 engines/mohawk/myst_stacks/menu.cpp:317
 #: engines/mohawk/riven_stacks/aspit.cpp:454
 #: engines/neverhood/metaengine.cpp:200 engines/saga/metaengine.cpp:449
@@ -3947,8 +3947,9 @@ msgstr "???????????? ????????????????"
 #: engines/buried/metaengine.cpp:167 engines/crab/input/input.cpp:261
 #: engines/cruise/metaengine.cpp:217 engines/dm/metaengine.cpp:180
 #: engines/grim/grim.cpp:550 engines/hdb/metaengine.cpp:258
-#: engines/hypno/metaengine.cpp:186 engines/hypno/metaengine.cpp:369
-#: engines/icb/icb.cpp:137 engines/icb/icb.cpp:203 engines/mohawk/myst.cpp:595
+#: engines/hypno/metaengine.cpp:243 engines/hypno/metaengine.cpp:339
+#: engines/hypno/metaengine.cpp:456 engines/icb/icb.cpp:137
+#: engines/icb/icb.cpp:203 engines/mohawk/myst.cpp:595
 #: engines/mohawk/riven.cpp:876 engines/ngi/metaengine.cpp:136
 #: engines/sky/metaengine.cpp:126 engines/sludge/keymapper_tables.h:52
 #: engines/sludge/keymapper_tables.h:125 engines/sludge/keymapper_tables.h:187
@@ -4898,7 +4899,7 @@ msgstr "Y"
 #: backends/platform/sdl/opendingux/opendingux.cpp:57
 #: backends/platform/sdl/ps3/ps3.cpp:50 backends/platform/sdl/psp2/psp2.cpp:51
 #: engines/bagel/metaengine.cpp:64 engines/efh/metaengine.cpp:397
-#: engines/got/metaengine.cpp:76 engines/hypno/metaengine.cpp:363
+#: engines/got/metaengine.cpp:76 engines/hypno/metaengine.cpp:449
 #: engines/mm/mm1/metaengine.cpp:50 engines/mm/mm1/metaengine.cpp:97
 #: engines/sherlock/metaengine.cpp:660 engines/sherlock/metaengine.cpp:672
 #: engines/sherlock/metaengine.cpp:1014 engines/supernova/metaengine.cpp:303
@@ -6110,7 +6111,7 @@ msgstr "???????????????????? ??????????"
 #: engines/access/metaengine.cpp:237 engines/asylum/metaengine.cpp:171
 #: engines/asylum/metaengine.cpp:198 engines/cine/metaengine.cpp:377
 #: engines/efh/metaengine.cpp:286 engines/hdb/metaengine.cpp:213
-#: engines/hypno/metaengine.cpp:302 engines/hypno/metaengine.cpp:356
+#: engines/hypno/metaengine.cpp:363 engines/hypno/metaengine.cpp:411
 #: engines/nancy/input.cpp:195 engines/tinsel/metaengine.cpp:263
 #: engines/titanic/metaengine.cpp:324 engines/ultima/ultima8/metaengine.cpp:52
 msgid "Move up"
@@ -6120,7 +6121,7 @@ msgstr "?????????????? ??????????"
 #: engines/access/metaengine.cpp:244 engines/asylum/metaengine.cpp:177
 #: engines/asylum/metaengine.cpp:205 engines/cine/metaengine.cpp:384
 #: engines/efh/metaengine.cpp:293 engines/hdb/metaengine.cpp:219
-#: engines/hypno/metaengine.cpp:290 engines/hypno/metaengine.cpp:342
+#: engines/hypno/metaengine.cpp:351 engines/hypno/metaengine.cpp:399
 #: engines/nancy/input.cpp:201 engines/tinsel/metaengine.cpp:269
 #: engines/titanic/metaengine.cpp:330 engines/ultima/ultima8/metaengine.cpp:53
 msgid "Move down"
@@ -6131,7 +6132,7 @@ msgstr "?????????????? ????????"
 #: engines/access/metaengine.cpp:251 engines/asylum/metaengine.cpp:183
 #: engines/asylum/metaengine.cpp:212 engines/cine/metaengine.cpp:391
 #: engines/efh/metaengine.cpp:300 engines/hdb/metaengine.cpp:225
-#: engines/hypno/metaengine.cpp:284 engines/hypno/metaengine.cpp:335
+#: engines/hypno/metaengine.cpp:345 engines/hypno/metaengine.cpp:393
 #: engines/kyra/engine/eobcommon.cpp:373 engines/lure/metaengine.cpp:218
 #: engines/nancy/input.cpp:207 engines/tinsel/metaengine.cpp:275
 #: engines/titanic/metaengine.cpp:312 engines/titanic/metaengine.cpp:390
@@ -6144,7 +6145,7 @@ msgstr "?????????????? ????????????"
 #: engines/access/metaengine.cpp:258 engines/asylum/metaengine.cpp:189
 #: engines/asylum/metaengine.cpp:219 engines/cine/metaengine.cpp:398
 #: engines/efh/metaengine.cpp:307 engines/hdb/metaengine.cpp:231
-#: engines/hypno/metaengine.cpp:296 engines/hypno/metaengine.cpp:349
+#: engines/hypno/metaengine.cpp:357 engines/hypno/metaengine.cpp:405
 #: engines/kyra/engine/eobcommon.cpp:374 engines/lure/metaengine.cpp:226
 #: engines/nancy/input.cpp:213 engines/tinsel/metaengine.cpp:281
 #: engines/titanic/metaengine.cpp:318 engines/titanic/metaengine.cpp:396
@@ -6645,8 +6646,8 @@ msgstr ""
 #: engines/cine/metaengine.cpp:271 engines/cruise/metaengine.cpp:169
 #: engines/dm/metaengine.cpp:126 engines/draci/metaengine.cpp:196
 #: engines/drascula/metaengine.cpp:206 engines/hopkins/metaengine.cpp:207
-#: engines/hugo/metaengine.cpp:130 engines/hypno/metaengine.cpp:166
-#: engines/hypno/metaengine.cpp:267 engines/hypno/metaengine.cpp:311
+#: engines/hugo/metaengine.cpp:130 engines/hypno/metaengine.cpp:225
+#: engines/hypno/metaengine.cpp:324 engines/hypno/metaengine.cpp:372
 #: engines/illusions/metaengine.cpp:90 engines/lab/metaengine.cpp:152
 #: engines/lure/metaengine.cpp:169 engines/made/metaengine.cpp:133
 #: engines/mads/metaengine.cpp:317 engines/neverhood/metaengine.cpp:186
@@ -7380,7 +7381,7 @@ msgstr "?????????????????????? ??????"
 
 #. I18N: This keymap allows skipping video cutscenes
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
-#: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
+#: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:210
 #: engines/phoenixvr/metaengine.cpp:94 engines/scumm/help.cpp:79
 #: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
@@ -8715,7 +8716,7 @@ msgstr "??????????"
 msgid "Select option 14"
 msgstr "??????????"
 
-#: engines/efh/metaengine.cpp:257 engines/hypno/metaengine.cpp:312
+#: engines/efh/metaengine.cpp:257 engines/hypno/metaengine.cpp:373
 #: engines/sludge/keymapper_tables.h:35 engines/supernova/metaengine.cpp:218
 #, fuzzy
 msgid "Menu keymappings"
@@ -10415,16 +10416,71 @@ msgstr ""
 msgid "Intro keymappings"
 msgstr "~??~??????????..."
 
-#: engines/hypno/metaengine.cpp:134
+#: engines/hypno/metaengine.cpp:134 engines/hypno/metaengine.cpp:144
 msgid "Primary shoot"
 msgstr ""
 
-#: engines/hypno/metaengine.cpp:140
+#: engines/hypno/metaengine.cpp:139
 #, fuzzy
 msgid "Secondary shoot"
 msgstr "?????????? ??????????"
 
-#: engines/hypno/metaengine.cpp:146 engines/lab/metaengine.cpp:209
+#: engines/hypno/metaengine.cpp:149
+#, fuzzy
+#| msgid "Map hide toolbar action"
+msgid "Rapid fire / secondary action"
+msgstr "???????????????????? ?????????????? \"?????????????? ???????????? ????????????????????????\""
+
+#: engines/hypno/metaengine.cpp:155
+#, fuzzy
+#| msgid "Slide Left"
+msgid "Aim left"
+msgstr "???????????????? ????????????"
+
+#: engines/hypno/metaengine.cpp:160
+#, fuzzy
+#| msgid "Scroll down"
+msgid "Aim down"
+msgstr "???????????????????? ????????"
+
+#: engines/hypno/metaengine.cpp:165
+#, fuzzy
+msgid "Aim right"
+msgstr "??????????????"
+
+#: engines/hypno/metaengine.cpp:170
+msgid "Aim up"
+msgstr ""
+
+#: engines/hypno/metaengine.cpp:175 engines/scumm/metaengine.cpp:1202
+#: engines/scumm/metaengine.cpp:1279
+#, fuzzy
+#| msgid "Slide Left"
+msgid "Stick left"
+msgstr "???????????????? ????????????"
+
+#: engines/hypno/metaengine.cpp:182 engines/scumm/metaengine.cpp:1195
+#: engines/scumm/metaengine.cpp:1272
+#, fuzzy
+#| msgid "Scroll down"
+msgid "Stick down"
+msgstr "???????????????????? ????????"
+
+#: engines/hypno/metaengine.cpp:189 engines/scumm/metaengine.cpp:1209
+#: engines/scumm/metaengine.cpp:1286
+#, fuzzy
+#| msgid "Slide Right"
+msgid "Stick right"
+msgstr "???????????????? ??????????????"
+
+#: engines/hypno/metaengine.cpp:196 engines/scumm/metaengine.cpp:1188
+#: engines/scumm/metaengine.cpp:1265
+#, fuzzy
+#| msgid "Pick up"
+msgid "Stick up"
+msgstr "??????????????"
+
+#: engines/hypno/metaengine.cpp:203 engines/lab/metaengine.cpp:209
 #: engines/ngi/metaengine.cpp:168 engines/sherlock/metaengine.cpp:872
 #: engines/teenagent/metaengine.cpp:232 engines/toon/metaengine.cpp:98
 #: engines/voyeur/metaengine.cpp:235
@@ -10433,23 +10489,23 @@ msgstr "?????????? ??????????"
 msgid "Skip intro"
 msgstr "???????????????????? ??????????"
 
-#: engines/hypno/metaengine.cpp:167
+#: engines/hypno/metaengine.cpp:226
 #, fuzzy
 msgid "Exit menu keymappings"
 msgstr "~??~??????????..."
 
-#: engines/hypno/metaengine.cpp:168
+#: engines/hypno/metaengine.cpp:227
 #, fuzzy
 msgid "Difficulty selection menu keymappings"
 msgstr "~??~??????????..."
 
-#: engines/hypno/metaengine.cpp:169
+#: engines/hypno/metaengine.cpp:228
 #, fuzzy
 msgid "Retry menu keymappings"
 msgstr "~??~??????????..."
 
-#: engines/hypno/metaengine.cpp:172 engines/hypno/metaengine.cpp:270
-#: engines/hypno/metaengine.cpp:322
+#: engines/hypno/metaengine.cpp:231 engines/hypno/metaengine.cpp:327
+#: engines/hypno/metaengine.cpp:382
 #, fuzzy
 #| msgid "Speech"
 msgid "Skip level (cheat)"
@@ -10458,58 +10514,82 @@ msgstr "????????????"
 #. I18N: (Game name: Soldier Boyz) player refers to the users own character.
 #. I18N: (Game name: Marvel Comics Spider-Man: The Sinister Six) player refers to the users own character.
 #. I18N: (Game name: Wetlands) player refers to the users own character.
-#: engines/hypno/metaengine.cpp:180 engines/hypno/metaengine.cpp:278
-#: engines/hypno/metaengine.cpp:329
+#: engines/hypno/metaengine.cpp:238 engines/hypno/metaengine.cpp:334
+#: engines/hypno/metaengine.cpp:388
 msgid "Kill player"
 msgstr ""
 
 #. I18N: (Game name: Soldier Boyz) the game has 3 difficulty levels: Chump, Punk and Badass. Chump is the easy mode.
-#: engines/hypno/metaengine.cpp:205
+#: engines/hypno/metaengine.cpp:262
 msgid "Chump"
 msgstr ""
 
 #. I18N: (Game name: Soldier Boyz) the game has 3 difficulty levels: Chump, Punk and Badass. Punk is the medium mode.
-#: engines/hypno/metaengine.cpp:212
+#: engines/hypno/metaengine.cpp:269
 msgid "Punk"
 msgstr ""
 
 #. I18N: (Game name: Soldier Boyz) the game has 3 difficulty levels: Chump, Punk and Badass. Badass is the hard mode.
-#: engines/hypno/metaengine.cpp:219
+#: engines/hypno/metaengine.cpp:276
 msgid "Badass"
 msgstr ""
 
 #. I18N: (Game name: Soldier Boyz) This makes the player restart from the last checkpoint.
-#: engines/hypno/metaengine.cpp:232
+#: engines/hypno/metaengine.cpp:289
 msgid "Retry sector"
 msgstr ""
 
 #. I18N: (Game name: Soldier Boyz) This makes the player restart the current mission / level.
-#: engines/hypno/metaengine.cpp:239
+#: engines/hypno/metaengine.cpp:296
 #, fuzzy
 #| msgid "Transitions:"
 msgid "Restart territory"
 msgstr "????????????????:"
 
-#: engines/hypno/metaengine.cpp:245
+#: engines/hypno/metaengine.cpp:302
 msgid "Begin new mission"
 msgstr ""
 
-#: engines/hypno/metaengine.cpp:313
+#: engines/hypno/metaengine.cpp:374
 #, fuzzy
 msgid "Pause keymappings"
 msgstr "~??~??????????..."
 
-#: engines/hypno/metaengine.cpp:314
+#: engines/hypno/metaengine.cpp:375
 #, fuzzy
 msgid "Direction keymappings"
 msgstr "~??~??????????..."
 
-#: engines/hypno/metaengine.cpp:316 engines/mads/metaengine.cpp:391
+#: engines/hypno/metaengine.cpp:377 engines/mads/metaengine.cpp:391
 #: engines/tsage/metaengine.cpp:259
 #, fuzzy
 msgid "Show credits"
 msgstr "???????????????? ?????????????? ????????????"
 
+#: engines/hypno/metaengine.cpp:417
+#, fuzzy
+#| msgid "Move left"
+msgid "Menu left"
+msgstr "?????????????? ????????????"
+
+#: engines/hypno/metaengine.cpp:425
+#, fuzzy
+#| msgid "Move down"
+msgid "Menu down"
+msgstr "?????????????? ????????"
+
+#: engines/hypno/metaengine.cpp:433
+#, fuzzy
+#| msgid "Move right"
+msgid "Menu right"
+msgstr "?????????????? ??????????????"
+
+#: engines/hypno/metaengine.cpp:441
+#, fuzzy
+#| msgid "Menu"
+msgid "Menu up"
+msgstr "????????"
+
 #. I18N: Action in In Cold Blood
 #: engines/icb/icb.cpp:107
 msgid "Arm"
@@ -13993,30 +14073,6 @@ msgstr ""
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1188 engines/scumm/metaengine.cpp:1265
-#, fuzzy
-#| msgid "Pick up"
-msgid "Stick up"
-msgstr "??????????????"
-
-#: engines/scumm/metaengine.cpp:1195 engines/scumm/metaengine.cpp:1272
-#, fuzzy
-#| msgid "Scroll down"
-msgid "Stick down"
-msgstr "???????????????????? ????????"
-
-#: engines/scumm/metaengine.cpp:1202 engines/scumm/metaengine.cpp:1279
-#, fuzzy
-#| msgid "Slide Left"
-msgid "Stick left"
-msgstr "???????????????? ????????????"
-
-#: engines/scumm/metaengine.cpp:1209 engines/scumm/metaengine.cpp:1286
-#, fuzzy
-#| msgid "Slide Right"
-msgid "Stick right"
-msgstr "???????????????? ??????????????"
-
 #: engines/scumm/metaengine.cpp:1216 engines/scumm/metaengine.cpp:1293
 #, fuzzy
 #| msgid "Move left"
@@ -19262,9 +19318,6 @@ msgstr ""
 #~ "???? ?????????????? ???????????????????? ?????????????? ???? ?????????????? \"???????????? ????????????????\" ?????? ?????????? "
 #~ "????????????"
 
-#~ msgid "Map hide toolbar action"
-#~ msgstr "???????????????????? ?????????????? \"?????????????? ???????????? ????????????????????????\""
-
 #~ msgid "You must map a key to the 'Hide toolbar' action to play this game"
 #~ msgstr ""
 #~ "???? ?????????????? ???????????????????? ?????????????? ???? ?????????????? \"?????????????? ???????????? ????????????????????????\" "
diff --git a/po/ca_ES.po b/po/ca_ES.po
index 022f7ca4864..81f24ab0972 100644
--- a/po/ca_ES.po
+++ b/po/ca_ES.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.6.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-13 06:14+0000\n"
+"POT-Creation-Date: 2026-06-13 10:20+0000\n"
 "PO-Revision-Date: 2026-05-26 04:22+0000\n"
 "Last-Translator: Xaviu <xaviu at xaviu.com>\n"
 "Language-Team: Catalan <https://translations.scummvm.org/projects/scummvm/"
@@ -133,7 +133,7 @@ msgstr "Amunt"
 #: engines/buried/saveload.cpp:83 engines/cine/saveload.cpp:857
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:123
 #: engines/drascula/saveload.cpp:51 engines/grim/inputdialog.cpp:84
-#: engines/hypno/metaengine.cpp:225 engines/mohawk/myst_stacks/menu.cpp:283
+#: engines/hypno/metaengine.cpp:282 engines/mohawk/myst_stacks/menu.cpp:283
 #: engines/mohawk/myst_stacks/menu.cpp:300
 #: engines/mohawk/myst_stacks/menu.cpp:318
 #: engines/mohawk/riven_stacks/aspit.cpp:349
@@ -338,7 +338,7 @@ msgstr "Un altre Emmagatzematge est?? actiu ara mateix. Voleu interrompre'l?"
 #: engines/chamber/metaengine.cpp:91 engines/director/events.cpp:122
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:139
 #: engines/efh/metaengine.cpp:362 engines/grim/grim.cpp:366
-#: engines/hypno/metaengine.cpp:192 engines/kyra/gui/saveload_eob.cpp:611
+#: engines/hypno/metaengine.cpp:249 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:197
 #: engines/mm/mm1/metaengine.cpp:54 engines/parallaction/metaengine.cpp:169
 #: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
@@ -357,7 +357,7 @@ msgstr "S??"
 #: engines/chamber/metaengine.cpp:97 engines/director/events.cpp:122
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:139
 #: engines/efh/metaengine.cpp:368 engines/grim/grim.cpp:366
-#: engines/hypno/metaengine.cpp:198 engines/kyra/gui/saveload_eob.cpp:611
+#: engines/hypno/metaengine.cpp:255 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:204
 #: engines/mm/mm1/metaengine.cpp:52 engines/parallaction/metaengine.cpp:175
 #: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
@@ -3289,7 +3289,7 @@ msgstr "Motor de jocs d'aventura gr??fica"
 #: backends/events/default/default-events.cpp:358
 #: engines/adl/metaengine.cpp:352 engines/buried/metaengine.cpp:154
 #: engines/chamber/metaengine.cpp:84 engines/drascula/metaengine.cpp:304
-#: engines/hdb/metaengine.cpp:275 engines/hypno/metaengine.cpp:251
+#: engines/hdb/metaengine.cpp:275 engines/hypno/metaengine.cpp:308
 #: engines/lab/metaengine.cpp:172 engines/mohawk/myst_stacks/menu.cpp:317
 #: engines/mohawk/riven_stacks/aspit.cpp:454
 #: engines/neverhood/metaengine.cpp:200 engines/saga/metaengine.cpp:449
@@ -3864,8 +3864,9 @@ msgstr "Clic dret"
 #: engines/buried/metaengine.cpp:167 engines/crab/input/input.cpp:261
 #: engines/cruise/metaengine.cpp:217 engines/dm/metaengine.cpp:180
 #: engines/grim/grim.cpp:550 engines/hdb/metaengine.cpp:258
-#: engines/hypno/metaengine.cpp:186 engines/hypno/metaengine.cpp:369
-#: engines/icb/icb.cpp:137 engines/icb/icb.cpp:203 engines/mohawk/myst.cpp:595
+#: engines/hypno/metaengine.cpp:243 engines/hypno/metaengine.cpp:339
+#: engines/hypno/metaengine.cpp:456 engines/icb/icb.cpp:137
+#: engines/icb/icb.cpp:203 engines/mohawk/myst.cpp:595
 #: engines/mohawk/riven.cpp:876 engines/ngi/metaengine.cpp:136
 #: engines/sky/metaengine.cpp:126 engines/sludge/keymapper_tables.h:52
 #: engines/sludge/keymapper_tables.h:125 engines/sludge/keymapper_tables.h:187
@@ -4762,7 +4763,7 @@ msgstr "Y"
 #: backends/platform/sdl/opendingux/opendingux.cpp:57
 #: backends/platform/sdl/ps3/ps3.cpp:50 backends/platform/sdl/psp2/psp2.cpp:51
 #: engines/bagel/metaengine.cpp:64 engines/efh/metaengine.cpp:397
-#: engines/got/metaengine.cpp:76 engines/hypno/metaengine.cpp:363
+#: engines/got/metaengine.cpp:76 engines/hypno/metaengine.cpp:449
 #: engines/mm/mm1/metaengine.cpp:50 engines/mm/mm1/metaengine.cpp:97
 #: engines/sherlock/metaengine.cpp:660 engines/sherlock/metaengine.cpp:672
 #: engines/sherlock/metaengine.cpp:1014 engines/supernova/metaengine.cpp:303
@@ -5906,7 +5907,7 @@ msgstr "Omet la pel??l??cula"
 #: engines/access/metaengine.cpp:237 engines/asylum/metaengine.cpp:171
 #: engines/asylum/metaengine.cpp:198 engines/cine/metaengine.cpp:377
 #: engines/efh/metaengine.cpp:286 engines/hdb/metaengine.cpp:213
-#: engines/hypno/metaengine.cpp:302 engines/hypno/metaengine.cpp:356
+#: engines/hypno/metaengine.cpp:363 engines/hypno/metaengine.cpp:411
 #: engines/nancy/input.cpp:195 engines/tinsel/metaengine.cpp:263
 #: engines/titanic/metaengine.cpp:324 engines/ultima/ultima8/metaengine.cpp:52
 msgid "Move up"
@@ -5916,7 +5917,7 @@ msgstr "Mou amunt"
 #: engines/access/metaengine.cpp:244 engines/asylum/metaengine.cpp:177
 #: engines/asylum/metaengine.cpp:205 engines/cine/metaengine.cpp:384
 #: engines/efh/metaengine.cpp:293 engines/hdb/metaengine.cpp:219
-#: engines/hypno/metaengine.cpp:290 engines/hypno/metaengine.cpp:342
+#: engines/hypno/metaengine.cpp:351 engines/hypno/metaengine.cpp:399
 #: engines/nancy/input.cpp:201 engines/tinsel/metaengine.cpp:269
 #: engines/titanic/metaengine.cpp:330 engines/ultima/ultima8/metaengine.cpp:53
 msgid "Move down"
@@ -5927,7 +5928,7 @@ msgstr "Mou avall"
 #: engines/access/metaengine.cpp:251 engines/asylum/metaengine.cpp:183
 #: engines/asylum/metaengine.cpp:212 engines/cine/metaengine.cpp:391
 #: engines/efh/metaengine.cpp:300 engines/hdb/metaengine.cpp:225
-#: engines/hypno/metaengine.cpp:284 engines/hypno/metaengine.cpp:335
+#: engines/hypno/metaengine.cpp:345 engines/hypno/metaengine.cpp:393
 #: engines/kyra/engine/eobcommon.cpp:373 engines/lure/metaengine.cpp:218
 #: engines/nancy/input.cpp:207 engines/tinsel/metaengine.cpp:275
 #: engines/titanic/metaengine.cpp:312 engines/titanic/metaengine.cpp:390
@@ -5940,7 +5941,7 @@ msgstr "Mou a l'esquerra"
 #: engines/access/metaengine.cpp:258 engines/asylum/metaengine.cpp:189
 #: engines/asylum/metaengine.cpp:219 engines/cine/metaengine.cpp:398
 #: engines/efh/metaengine.cpp:307 engines/hdb/metaengine.cpp:231
-#: engines/hypno/metaengine.cpp:296 engines/hypno/metaengine.cpp:349
+#: engines/hypno/metaengine.cpp:357 engines/hypno/metaengine.cpp:405
 #: engines/kyra/engine/eobcommon.cpp:374 engines/lure/metaengine.cpp:226
 #: engines/nancy/input.cpp:213 engines/tinsel/metaengine.cpp:281
 #: engines/titanic/metaengine.cpp:318 engines/titanic/metaengine.cpp:396
@@ -6438,8 +6439,8 @@ msgstr "AGOS principal"
 #: engines/cine/metaengine.cpp:271 engines/cruise/metaengine.cpp:169
 #: engines/dm/metaengine.cpp:126 engines/draci/metaengine.cpp:196
 #: engines/drascula/metaengine.cpp:206 engines/hopkins/metaengine.cpp:207
-#: engines/hugo/metaengine.cpp:130 engines/hypno/metaengine.cpp:166
-#: engines/hypno/metaengine.cpp:267 engines/hypno/metaengine.cpp:311
+#: engines/hugo/metaengine.cpp:130 engines/hypno/metaengine.cpp:225
+#: engines/hypno/metaengine.cpp:324 engines/hypno/metaengine.cpp:372
 #: engines/illusions/metaengine.cpp:90 engines/lab/metaengine.cpp:152
 #: engines/lure/metaengine.cpp:169 engines/made/metaengine.cpp:133
 #: engines/mads/metaengine.cpp:317 engines/neverhood/metaengine.cpp:186
@@ -7142,7 +7143,7 @@ msgstr "Activa / desactiva el combat"
 
 #. I18N: This keymap allows skipping video cutscenes
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
-#: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
+#: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:210
 #: engines/phoenixvr/metaengine.cpp:94 engines/scumm/help.cpp:79
 #: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
@@ -8295,7 +8296,7 @@ msgstr "Selecciona l'opci?? 13"
 msgid "Select option 14"
 msgstr "Selecciona l'opci?? 14"
 
-#: engines/efh/metaengine.cpp:257 engines/hypno/metaengine.cpp:312
+#: engines/efh/metaengine.cpp:257 engines/hypno/metaengine.cpp:373
 #: engines/sludge/keymapper_tables.h:35 engines/supernova/metaengine.cpp:218
 msgid "Menu keymappings"
 msgstr "Assignacions del men??"
@@ -9804,88 +9805,168 @@ msgstr ""
 msgid "Intro keymappings"
 msgstr "Assignaci?? de tecles d'introducci??"
 
-#: engines/hypno/metaengine.cpp:134
+#: engines/hypno/metaengine.cpp:134 engines/hypno/metaengine.cpp:144
 msgid "Primary shoot"
 msgstr "Tret principal"
 
-#: engines/hypno/metaengine.cpp:140
+#: engines/hypno/metaengine.cpp:139
 msgid "Secondary shoot"
 msgstr "Tret secundari"
 
-#: engines/hypno/metaengine.cpp:146 engines/lab/metaengine.cpp:209
+#: engines/hypno/metaengine.cpp:149
+#, fuzzy
+#| msgid "Map hide toolbar action"
+msgid "Rapid fire / secondary action"
+msgstr "Assigna l'acci?? d'ocultar la barra d'eines"
+
+#: engines/hypno/metaengine.cpp:155
+#, fuzzy
+#| msgid "Slide left"
+msgid "Aim left"
+msgstr "Llisca cap a l'esquerra"
+
+#: engines/hypno/metaengine.cpp:160
+#, fuzzy
+#| msgid "Scroll down"
+msgid "Aim down"
+msgstr "Despla??ament avall"
+
+#: engines/hypno/metaengine.cpp:165
+#, fuzzy
+#| msgid "Up right"
+msgid "Aim right"
+msgstr "Amunt a la dreta"
+
+#: engines/hypno/metaengine.cpp:170
+msgid "Aim up"
+msgstr ""
+
+#: engines/hypno/metaengine.cpp:175 engines/scumm/metaengine.cpp:1202
+#: engines/scumm/metaengine.cpp:1279
+#, fuzzy
+#| msgid "Slide left"
+msgid "Stick left"
+msgstr "Llisca cap a l'esquerra"
+
+#: engines/hypno/metaengine.cpp:182 engines/scumm/metaengine.cpp:1195
+#: engines/scumm/metaengine.cpp:1272
+#, fuzzy
+#| msgid "Scroll down"
+msgid "Stick down"
+msgstr "Despla??ament avall"
+
+#: engines/hypno/metaengine.cpp:189 engines/scumm/metaengine.cpp:1209
+#: engines/scumm/metaengine.cpp:1286
+#, fuzzy
+#| msgid "Slide right"
+msgid "Stick right"
+msgstr "Llisca cap a la dreta"
+
+#: engines/hypno/metaengine.cpp:196 engines/scumm/metaengine.cpp:1188
+#: engines/scumm/metaengine.cpp:1265
+#, fuzzy
+#| msgid "Pick up"
+msgid "Stick up"
+msgstr "Recull"
+
+#: engines/hypno/metaengine.cpp:203 engines/lab/metaengine.cpp:209
 #: engines/ngi/metaengine.cpp:168 engines/sherlock/metaengine.cpp:872
 #: engines/teenagent/metaengine.cpp:232 engines/toon/metaengine.cpp:98
 #: engines/voyeur/metaengine.cpp:235
 msgid "Skip intro"
 msgstr "Omet la introducci??"
 
-#: engines/hypno/metaengine.cpp:167
+#: engines/hypno/metaengine.cpp:226
 msgid "Exit menu keymappings"
 msgstr "Assignaci?? de tecles del men?? de sortida"
 
-#: engines/hypno/metaengine.cpp:168
+#: engines/hypno/metaengine.cpp:227
 msgid "Difficulty selection menu keymappings"
 msgstr "Assignaci?? de tecles del men?? de selecci?? de dificultat"
 
-#: engines/hypno/metaengine.cpp:169
+#: engines/hypno/metaengine.cpp:228
 msgid "Retry menu keymappings"
 msgstr "Assignaci?? de tecles del men?? de reintent"
 
-#: engines/hypno/metaengine.cpp:172 engines/hypno/metaengine.cpp:270
-#: engines/hypno/metaengine.cpp:322
+#: engines/hypno/metaengine.cpp:231 engines/hypno/metaengine.cpp:327
+#: engines/hypno/metaengine.cpp:382
 msgid "Skip level (cheat)"
 msgstr "Omet el nivell (truc)"
 
 #. I18N: (Game name: Soldier Boyz) player refers to the users own character.
 #. I18N: (Game name: Marvel Comics Spider-Man: The Sinister Six) player refers to the users own character.
 #. I18N: (Game name: Wetlands) player refers to the users own character.
-#: engines/hypno/metaengine.cpp:180 engines/hypno/metaengine.cpp:278
-#: engines/hypno/metaengine.cpp:329
+#: engines/hypno/metaengine.cpp:238 engines/hypno/metaengine.cpp:334
+#: engines/hypno/metaengine.cpp:388
 msgid "Kill player"
 msgstr "Mata el jugador"
 
 #. I18N: (Game name: Soldier Boyz) the game has 3 difficulty levels: Chump, Punk and Badass. Chump is the easy mode.
-#: engines/hypno/metaengine.cpp:205
+#: engines/hypno/metaengine.cpp:262
 msgid "Chump"
 msgstr "Chump (f??cil)"
 
 #. I18N: (Game name: Soldier Boyz) the game has 3 difficulty levels: Chump, Punk and Badass. Punk is the medium mode.
-#: engines/hypno/metaengine.cpp:212
+#: engines/hypno/metaengine.cpp:269
 msgid "Punk"
 msgstr "Punk (normal)"
 
 #. I18N: (Game name: Soldier Boyz) the game has 3 difficulty levels: Chump, Punk and Badass. Badass is the hard mode.
-#: engines/hypno/metaengine.cpp:219
+#: engines/hypno/metaengine.cpp:276
 msgid "Badass"
 msgstr "Badass (dif??cil)"
 
 #. I18N: (Game name: Soldier Boyz) This makes the player restart from the last checkpoint.
-#: engines/hypno/metaengine.cpp:232
+#: engines/hypno/metaengine.cpp:289
 msgid "Retry sector"
 msgstr "Reintenta el sector"
 
 #. I18N: (Game name: Soldier Boyz) This makes the player restart the current mission / level.
-#: engines/hypno/metaengine.cpp:239
+#: engines/hypno/metaengine.cpp:296
 msgid "Restart territory"
 msgstr "Reinicia el territori"
 
-#: engines/hypno/metaengine.cpp:245
+#: engines/hypno/metaengine.cpp:302
 msgid "Begin new mission"
 msgstr "Comen??a una nova missi??"
 
-#: engines/hypno/metaengine.cpp:313
+#: engines/hypno/metaengine.cpp:374
 msgid "Pause keymappings"
 msgstr "Assignacions de pausa"
 
-#: engines/hypno/metaengine.cpp:314
+#: engines/hypno/metaengine.cpp:375
 msgid "Direction keymappings"
 msgstr "Assignaci?? de tecles de direcci??"
 
-#: engines/hypno/metaengine.cpp:316 engines/mads/metaengine.cpp:391
+#: engines/hypno/metaengine.cpp:377 engines/mads/metaengine.cpp:391
 #: engines/tsage/metaengine.cpp:259
 msgid "Show credits"
 msgstr "Mostra els cr??dits"
 
+#: engines/hypno/metaengine.cpp:417
+#, fuzzy
+#| msgid "Move left"
+msgid "Menu left"
+msgstr "Mou a l'esquerra"
+
+#: engines/hypno/metaengine.cpp:425
+#, fuzzy
+#| msgid "Move down"
+msgid "Menu down"
+msgstr "Mou avall"
+
+#: engines/hypno/metaengine.cpp:433
+#, fuzzy
+#| msgid "Move right"
+msgid "Menu right"
+msgstr "Mou a la dreta"
+
+#: engines/hypno/metaengine.cpp:441
+#, fuzzy
+#| msgid "Menu"
+msgid "Menu up"
+msgstr "Men??"
+
 #. I18N: Action in In Cold Blood
 #: engines/icb/icb.cpp:107
 msgid "Arm"
@@ -13062,30 +13143,6 @@ msgstr ""
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1188 engines/scumm/metaengine.cpp:1265
-#, fuzzy
-#| msgid "Pick up"
-msgid "Stick up"
-msgstr "Recull"
-
-#: engines/scumm/metaengine.cpp:1195 engines/scumm/metaengine.cpp:1272
-#, fuzzy
-#| msgid "Scroll down"
-msgid "Stick down"
-msgstr "Despla??ament avall"
-
-#: engines/scumm/metaengine.cpp:1202 engines/scumm/metaengine.cpp:1279
-#, fuzzy
-#| msgid "Slide left"
-msgid "Stick left"
-msgstr "Llisca cap a l'esquerra"
-
-#: engines/scumm/metaengine.cpp:1209 engines/scumm/metaengine.cpp:1286
-#, fuzzy
-#| msgid "Slide right"
-msgid "Stick right"
-msgstr "Llisca cap a la dreta"
-
 #: engines/scumm/metaengine.cpp:1216 engines/scumm/metaengine.cpp:1293
 #, fuzzy
 #| msgid "Move / Select"
@@ -17595,9 +17652,6 @@ msgstr ""
 #~ msgstr ""
 #~ "Heu d'assignar una tecla a l'acci?? 'Clic dret' per jugar a aquest joc"
 
-#~ msgid "Map hide toolbar action"
-#~ msgstr "Assigna l'acci?? d'ocultar la barra d'eines"
-
 #~ msgid "You must map a key to the 'Hide toolbar' action to play this game"
 #~ msgstr ""
 #~ "Heu d'assignar una tecla a l'acci?? 'Ocultar la barra d'eines' per jugar a "
diff --git a/po/cs_CZ.po b/po/cs_CZ.po
index 65956b21618..29c7e1a0662 100644
--- a/po/cs_CZ.po
+++ b/po/cs_CZ.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.7.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-13 06:14+0000\n"
+"POT-Creation-Date: 2026-06-13 10:20+0000\n"
 "PO-Revision-Date: 2026-02-09 20:22+0000\n"
 "Last-Translator: AsciiWolf <mail at asciiwolf.com>\n"
 "Language-Team: Czech <https://translations.scummvm.org/projects/scummvm/"
@@ -142,7 +142,7 @@ msgstr "J??t nahoru"
 #: engines/buried/saveload.cpp:83 engines/cine/saveload.cpp:857
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:123
 #: engines/drascula/saveload.cpp:51 engines/grim/inputdialog.cpp:84
-#: engines/hypno/metaengine.cpp:225 engines/mohawk/myst_stacks/menu.cpp:283
+#: engines/hypno/metaengine.cpp:282 engines/mohawk/myst_stacks/menu.cpp:283
 #: engines/mohawk/myst_stacks/menu.cpp:300
 #: engines/mohawk/myst_stacks/menu.cpp:318
 #: engines/mohawk/riven_stacks/aspit.cpp:349
@@ -354,7 +354,7 @@ msgstr "Jin?? ??lo??i??t?? pr??v?? pracuje. Chcete ho p??eru??it?"
 #: engines/chamber/metaengine.cpp:91 engines/director/events.cpp:122
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:139
 #: engines/efh/metaengine.cpp:362 engines/grim/grim.cpp:366
-#: engines/hypno/metaengine.cpp:192 engines/kyra/gui/saveload_eob.cpp:611
+#: engines/hypno/metaengine.cpp:249 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:197
 #: engines/mm/mm1/metaengine.cpp:54 engines/parallaction/metaengine.cpp:169
 #: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
@@ -373,7 +373,7 @@ msgstr "Ano"
 #: engines/chamber/metaengine.cpp:97 engines/director/events.cpp:122
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:139
 #: engines/efh/metaengine.cpp:368 engines/grim/grim.cpp:366
-#: engines/hypno/metaengine.cpp:198 engines/kyra/gui/saveload_eob.cpp:611
+#: engines/hypno/metaengine.cpp:255 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:204
 #: engines/mm/mm1/metaengine.cpp:52 engines/parallaction/metaengine.cpp:175
 #: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
@@ -3354,7 +3354,7 @@ msgstr "Grafick?? engine pro hry ????nru adventur"
 #: backends/events/default/default-events.cpp:358
 #: engines/adl/metaengine.cpp:352 engines/buried/metaengine.cpp:154
 #: engines/chamber/metaengine.cpp:84 engines/drascula/metaengine.cpp:304
-#: engines/hdb/metaengine.cpp:275 engines/hypno/metaengine.cpp:251
+#: engines/hdb/metaengine.cpp:275 engines/hypno/metaengine.cpp:308
 #: engines/lab/metaengine.cpp:172 engines/mohawk/myst_stacks/menu.cpp:317
 #: engines/mohawk/riven_stacks/aspit.cpp:454
 #: engines/neverhood/metaengine.cpp:200 engines/saga/metaengine.cpp:449
@@ -3935,8 +3935,9 @@ msgstr "Prav?? kliknut??"
 #: engines/buried/metaengine.cpp:167 engines/crab/input/input.cpp:261
 #: engines/cruise/metaengine.cpp:217 engines/dm/metaengine.cpp:180
 #: engines/grim/grim.cpp:550 engines/hdb/metaengine.cpp:258
-#: engines/hypno/metaengine.cpp:186 engines/hypno/metaengine.cpp:369
-#: engines/icb/icb.cpp:137 engines/icb/icb.cpp:203 engines/mohawk/myst.cpp:595
+#: engines/hypno/metaengine.cpp:243 engines/hypno/metaengine.cpp:339
+#: engines/hypno/metaengine.cpp:456 engines/icb/icb.cpp:137
+#: engines/icb/icb.cpp:203 engines/mohawk/myst.cpp:595
 #: engines/mohawk/riven.cpp:876 engines/ngi/metaengine.cpp:136
 #: engines/sky/metaengine.cpp:126 engines/sludge/keymapper_tables.h:52
 #: engines/sludge/keymapper_tables.h:125 engines/sludge/keymapper_tables.h:187
@@ -4891,7 +4892,7 @@ msgstr ""
 #: backends/platform/sdl/opendingux/opendingux.cpp:57
 #: backends/platform/sdl/ps3/ps3.cpp:50 backends/platform/sdl/psp2/psp2.cpp:51
 #: engines/bagel/metaengine.cpp:64 engines/efh/metaengine.cpp:397
-#: engines/got/metaengine.cpp:76 engines/hypno/metaengine.cpp:363
+#: engines/got/metaengine.cpp:76 engines/hypno/metaengine.cpp:449
 #: engines/mm/mm1/metaengine.cpp:50 engines/mm/mm1/metaengine.cpp:97
 #: engines/sherlock/metaengine.cpp:660 engines/sherlock/metaengine.cpp:672
 #: engines/sherlock/metaengine.cpp:1014 engines/supernova/metaengine.cpp:303
@@ -6078,7 +6079,7 @@ msgstr "P??esko??it ????dek"
 #: engines/access/metaengine.cpp:237 engines/asylum/metaengine.cpp:171
 #: engines/asylum/metaengine.cpp:198 engines/cine/metaengine.cpp:377
 #: engines/efh/metaengine.cpp:286 engines/hdb/metaengine.cpp:213
-#: engines/hypno/metaengine.cpp:302 engines/hypno/metaengine.cpp:356
+#: engines/hypno/metaengine.cpp:363 engines/hypno/metaengine.cpp:411
 #: engines/nancy/input.cpp:195 engines/tinsel/metaengine.cpp:263
 #: engines/titanic/metaengine.cpp:324 engines/ultima/ultima8/metaengine.cpp:52
 #, fuzzy
@@ -6089,7 +6090,7 @@ msgstr "Vzad"
 #: engines/access/metaengine.cpp:244 engines/asylum/metaengine.cpp:177
 #: engines/asylum/metaengine.cpp:205 engines/cine/metaengine.cpp:384
 #: engines/efh/metaengine.cpp:293 engines/hdb/metaengine.cpp:219
-#: engines/hypno/metaengine.cpp:290 engines/hypno/metaengine.cpp:342
+#: engines/hypno/metaengine.cpp:351 engines/hypno/metaengine.cpp:399
 #: engines/nancy/input.cpp:201 engines/tinsel/metaengine.cpp:269
 #: engines/titanic/metaengine.cpp:330 engines/ultima/ultima8/metaengine.cpp:53
 #, fuzzy
@@ -6101,7 +6102,7 @@ msgstr "Zru??it stahov??n??"
 #: engines/access/metaengine.cpp:251 engines/asylum/metaengine.cpp:183
 #: engines/asylum/metaengine.cpp:212 engines/cine/metaengine.cpp:391
 #: engines/efh/metaengine.cpp:300 engines/hdb/metaengine.cpp:225
-#: engines/hypno/metaengine.cpp:284 engines/hypno/metaengine.cpp:335
+#: engines/hypno/metaengine.cpp:345 engines/hypno/metaengine.cpp:393
 #: engines/kyra/engine/eobcommon.cpp:373 engines/lure/metaengine.cpp:218
 #: engines/nancy/input.cpp:207 engines/tinsel/metaengine.cpp:275
 #: engines/titanic/metaengine.cpp:312 engines/titanic/metaengine.cpp:390
@@ -6115,7 +6116,7 @@ msgstr "P??esunout se doleva"
 #: engines/access/metaengine.cpp:258 engines/asylum/metaengine.cpp:189
 #: engines/asylum/metaengine.cpp:219 engines/cine/metaengine.cpp:398
 #: engines/efh/metaengine.cpp:307 engines/hdb/metaengine.cpp:231
-#: engines/hypno/metaengine.cpp:296 engines/hypno/metaengine.cpp:349
+#: engines/hypno/metaengine.cpp:357 engines/hypno/metaengine.cpp:405
 #: engines/kyra/engine/eobcommon.cpp:374 engines/lure/metaengine.cpp:226
 #: engines/nancy/input.cpp:213 engines/tinsel/metaengine.cpp:281
 #: engines/titanic/metaengine.cpp:318 engines/titanic/metaengine.cpp:396
@@ -6610,8 +6611,8 @@ msgstr ""
 #: engines/cine/metaengine.cpp:271 engines/cruise/metaengine.cpp:169
 #: engines/dm/metaengine.cpp:126 engines/draci/metaengine.cpp:196
 #: engines/drascula/metaengine.cpp:206 engines/hopkins/metaengine.cpp:207
-#: engines/hugo/metaengine.cpp:130 engines/hypno/metaengine.cpp:166
-#: engines/hypno/metaengine.cpp:267 engines/hypno/metaengine.cpp:311
+#: engines/hugo/metaengine.cpp:130 engines/hypno/metaengine.cpp:225
+#: engines/hypno/metaengine.cpp:324 engines/hypno/metaengine.cpp:372
 #: engines/illusions/metaengine.cpp:90 engines/lab/metaengine.cpp:152
 #: engines/lure/metaengine.cpp:169 engines/made/metaengine.cpp:133
 #: engines/mads/metaengine.cpp:317 engines/neverhood/metaengine.cpp:186
@@ -7334,7 +7335,7 @@ msgstr "Povolit zachycov??n?? my??i"
 
 #. I18N: This keymap allows skipping video cutscenes
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
-#: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
+#: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:210
 #: engines/phoenixvr/metaengine.cpp:94 engines/scumm/help.cpp:79
 #: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
@@ -8628,7 +8629,7 @@ msgstr "??innost/Vybrat"
 msgid "Select option 14"
 msgstr "??innost/Vybrat"
 
-#: engines/efh/metaengine.cpp:257 engines/hypno/metaengine.cpp:312
+#: engines/efh/metaengine.cpp:257 engines/hypno/metaengine.cpp:373
 #: engines/sludge/keymapper_tables.h:35 engines/supernova/metaengine.cpp:218
 #, fuzzy
 msgid "Menu keymappings"
@@ -10304,16 +10305,69 @@ msgstr ""
 msgid "Intro keymappings"
 msgstr "~V~olby..."
 
-#: engines/hypno/metaengine.cpp:134
+#: engines/hypno/metaengine.cpp:134 engines/hypno/metaengine.cpp:144
 msgid "Primary shoot"
 msgstr ""
 
-#: engines/hypno/metaengine.cpp:140
+#: engines/hypno/metaengine.cpp:139
 #, fuzzy
 msgid "Secondary shoot"
 msgstr "Druh?? d??t??"
 
-#: engines/hypno/metaengine.cpp:146 engines/lab/metaengine.cpp:209
+#: engines/hypno/metaengine.cpp:149
+#, fuzzy
+#| msgid "Map hide toolbar action"
+msgid "Rapid fire / secondary action"
+msgstr "Mapovat ??innost skr??t panel n??stroj??"
+
+#: engines/hypno/metaengine.cpp:155
+#, fuzzy
+#| msgid "Slide Left"
+msgid "Aim left"
+msgstr "P??esunout se Doleva"
+
+#: engines/hypno/metaengine.cpp:160
+#, fuzzy
+msgid "Aim down"
+msgstr "Posunout seznam dolu"
+
+#: engines/hypno/metaengine.cpp:165
+#, fuzzy
+msgid "Aim right"
+msgstr "Doprava"
+
+#: engines/hypno/metaengine.cpp:170
+msgid "Aim up"
+msgstr ""
+
+#: engines/hypno/metaengine.cpp:175 engines/scumm/metaengine.cpp:1202
+#: engines/scumm/metaengine.cpp:1279
+#, fuzzy
+#| msgid "Slide Left"
+msgid "Stick left"
+msgstr "P??esunout se Doleva"
+
+#: engines/hypno/metaengine.cpp:182 engines/scumm/metaengine.cpp:1195
+#: engines/scumm/metaengine.cpp:1272
+#, fuzzy
+msgid "Stick down"
+msgstr "Posunout seznam dolu"
+
+#: engines/hypno/metaengine.cpp:189 engines/scumm/metaengine.cpp:1209
+#: engines/scumm/metaengine.cpp:1286
+#, fuzzy
+#| msgid "Slide Right"
+msgid "Stick right"
+msgstr "P??esunout se Doprava"
+
+#: engines/hypno/metaengine.cpp:196 engines/scumm/metaengine.cpp:1188
+#: engines/scumm/metaengine.cpp:1265
+#, fuzzy
+#| msgid "Pick up"
+msgid "Stick up"
+msgstr "Sebrat"
+
+#: engines/hypno/metaengine.cpp:203 engines/lab/metaengine.cpp:209
 #: engines/ngi/metaengine.cpp:168 engines/sherlock/metaengine.cpp:872
 #: engines/teenagent/metaengine.cpp:232 engines/toon/metaengine.cpp:98
 #: engines/voyeur/metaengine.cpp:235
@@ -10322,23 +10376,23 @@ msgstr "Druh?? d??t??"
 msgid "Skip intro"
 msgstr "P??esko??it ????dek"
 
-#: engines/hypno/metaengine.cpp:167
+#: engines/hypno/metaengine.cpp:226
 #, fuzzy
 msgid "Exit menu keymappings"
 msgstr "~V~olby..."
 
-#: engines/hypno/metaengine.cpp:168
+#: engines/hypno/metaengine.cpp:227
 #, fuzzy
 msgid "Difficulty selection menu keymappings"
 msgstr "~V~olby..."
 
-#: engines/hypno/metaengine.cpp:169
+#: engines/hypno/metaengine.cpp:228
 #, fuzzy
 msgid "Retry menu keymappings"
 msgstr "~V~olby..."
 
-#: engines/hypno/metaengine.cpp:172 engines/hypno/metaengine.cpp:270
-#: engines/hypno/metaengine.cpp:322
+#: engines/hypno/metaengine.cpp:231 engines/hypno/metaengine.cpp:327
+#: engines/hypno/metaengine.cpp:382
 #, fuzzy
 #| msgid "Speech"
 msgid "Skip level (cheat)"
@@ -10347,58 +10401,79 @@ msgstr "??e??"
 #. I18N: (Game name: Soldier Boyz) player refers to the users own character.
 #. I18N: (Game name: Marvel Comics Spider-Man: The Sinister Six) player refers to the users own character.
 #. I18N: (Game name: Wetlands) player refers to the users own character.
-#: engines/hypno/metaengine.cpp:180 engines/hypno/metaengine.cpp:278
-#: engines/hypno/metaengine.cpp:329
+#: engines/hypno/metaengine.cpp:238 engines/hypno/metaengine.cpp:334
+#: engines/hypno/metaengine.cpp:388
 msgid "Kill player"
 msgstr ""
 
 #. I18N: (Game name: Soldier Boyz) the game has 3 difficulty levels: Chump, Punk and Badass. Chump is the easy mode.
-#: engines/hypno/metaengine.cpp:205
+#: engines/hypno/metaengine.cpp:262
 msgid "Chump"
 msgstr ""
 
 #. I18N: (Game name: Soldier Boyz) the game has 3 difficulty levels: Chump, Punk and Badass. Punk is the medium mode.
-#: engines/hypno/metaengine.cpp:212
+#: engines/hypno/metaengine.cpp:269
 msgid "Punk"
 msgstr ""
 
 #. I18N: (Game name: Soldier Boyz) the game has 3 difficulty levels: Chump, Punk and Badass. Badass is the hard mode.
-#: engines/hypno/metaengine.cpp:219
+#: engines/hypno/metaengine.cpp:276
 msgid "Badass"
 msgstr ""
 
 #. I18N: (Game name: Soldier Boyz) This makes the player restart from the last checkpoint.
-#: engines/hypno/metaengine.cpp:232
+#: engines/hypno/metaengine.cpp:289
 msgid "Retry sector"
 msgstr ""
 
 #. I18N: (Game name: Soldier Boyz) This makes the player restart the current mission / level.
-#: engines/hypno/metaengine.cpp:239
+#: engines/hypno/metaengine.cpp:296
 #, fuzzy
 #| msgid "Transitions:"
 msgid "Restart territory"
 msgstr "P??echody:"
 
-#: engines/hypno/metaengine.cpp:245
+#: engines/hypno/metaengine.cpp:302
 msgid "Begin new mission"
 msgstr ""
 
-#: engines/hypno/metaengine.cpp:313
+#: engines/hypno/metaengine.cpp:374
 #, fuzzy
 msgid "Pause keymappings"
 msgstr "~V~olby..."
 
-#: engines/hypno/metaengine.cpp:314
+#: engines/hypno/metaengine.cpp:375
 #, fuzzy
 msgid "Direction keymappings"
 msgstr "~V~olby..."
 
-#: engines/hypno/metaengine.cpp:316 engines/mads/metaengine.cpp:391
+#: engines/hypno/metaengine.cpp:377 engines/mads/metaengine.cpp:391
 #: engines/tsage/metaengine.cpp:259
 #, fuzzy
 msgid "Show credits"
 msgstr "Zobrazit kurzor my??i"
 
+#: engines/hypno/metaengine.cpp:417
+#, fuzzy
+msgid "Menu left"
+msgstr "P??esunout se doleva"
+
+#: engines/hypno/metaengine.cpp:425
+#, fuzzy
+msgid "Menu down"
+msgstr "Zru??it stahov??n??"
+
+#: engines/hypno/metaengine.cpp:433
+#, fuzzy
+msgid "Menu right"
+msgstr "P??esunout se doprava"
+
+#: engines/hypno/metaengine.cpp:441
+#, fuzzy
+#| msgid "Menu"
+msgid "Menu up"
+msgstr "Menu"
+
 #. I18N: Action in In Cold Blood
 #: engines/icb/icb.cpp:107
 msgid "Arm"
@@ -13803,29 +13878,6 @@ msgstr ""
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1188 engines/scumm/metaengine.cpp:1265
-#, fuzzy
-#| msgid "Pick up"
-msgid "Stick up"
-msgstr "Sebrat"
-
-#: engines/scumm/metaengine.cpp:1195 engines/scumm/metaengine.cpp:1272
-#, fuzzy
-msgid "Stick down"
-msgstr "Posunout seznam dolu"
-
-#: engines/scumm/metaengine.cpp:1202 engines/scumm/metaengine.cpp:1279
-#, fuzzy
-#| msgid "Slide Left"
-msgid "Stick left"
-msgstr "P??esunout se Doleva"
-
-#: engines/scumm/metaengine.cpp:1209 engines/scumm/metaengine.cpp:1286
-#, fuzzy
-#| msgid "Slide Right"
-msgid "Stick right"
-msgstr "P??esunout se Doprava"
-
 #: engines/scumm/metaengine.cpp:1216 engines/scumm/metaengine.cpp:1293
 #, fuzzy
 msgid "Fire / select"
@@ -18922,9 +18974,6 @@ msgstr ""
 #~ "Mus??te namapovat kl??vesu pro ??innost 'Prav?? Kliknut??', abyste tuto hru "
 #~ "mohli hr??t"
 
-#~ msgid "Map hide toolbar action"
-#~ msgstr "Mapovat ??innost skr??t panel n??stroj??"
-
 #~ msgid "You must map a key to the 'Hide toolbar' action to play this game"
 #~ msgstr ""
 #~ "Mus??te namapovat kl??vesu pro ??innost 'Skr??t Panel n??stroj??', abyste tuto "
diff --git a/po/da.po b/po/da.po
index 5e19064d9ed..bb53ec1f7fa 100644
--- a/po/da.po
+++ b/po/da.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-13 06:14+0000\n"
+"POT-Creation-Date: 2026-06-13 10:20+0000\n"
 "PO-Revision-Date: 2023-09-07 17:04+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Danish <https://translations.scummvm.org/projects/scummvm/"
@@ -140,7 +140,7 @@ msgstr "G?? op"
 #: engines/buried/saveload.cpp:83 engines/cine/saveload.cpp:857
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:123
 #: engines/drascula/saveload.cpp:51 engines/grim/inputdialog.cpp:84
-#: engines/hypno/metaengine.cpp:225 engines/mohawk/myst_stacks/menu.cpp:283
+#: engines/hypno/metaengine.cpp:282 engines/mohawk/myst_stacks/menu.cpp:283
 #: engines/mohawk/myst_stacks/menu.cpp:300
 #: engines/mohawk/myst_stacks/menu.cpp:318
 #: engines/mohawk/riven_stacks/aspit.cpp:349
@@ -355,7 +355,7 @@ msgstr "Et andet lager er aktivt nu. Vil du afbryde det?"
 #: engines/chamber/metaengine.cpp:91 engines/director/events.cpp:122
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:139
 #: engines/efh/metaengine.cpp:362 engines/grim/grim.cpp:366
-#: engines/hypno/metaengine.cpp:192 engines/kyra/gui/saveload_eob.cpp:611
+#: engines/hypno/metaengine.cpp:249 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:197
 #: engines/mm/mm1/metaengine.cpp:54 engines/parallaction/metaengine.cpp:169
 #: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
@@ -374,7 +374,7 @@ msgstr "Ja"
 #: engines/chamber/metaengine.cpp:97 engines/director/events.cpp:122
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:139
 #: engines/efh/metaengine.cpp:368 engines/grim/grim.cpp:366
-#: engines/hypno/metaengine.cpp:198 engines/kyra/gui/saveload_eob.cpp:611
+#: engines/hypno/metaengine.cpp:255 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:204
 #: engines/mm/mm1/metaengine.cpp:52 engines/parallaction/metaengine.cpp:175
 #: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
@@ -3371,7 +3371,7 @@ msgstr ""
 #: backends/events/default/default-events.cpp:358
 #: engines/adl/metaengine.cpp:352 engines/buried/metaengine.cpp:154
 #: engines/chamber/metaengine.cpp:84 engines/drascula/metaengine.cpp:304
-#: engines/hdb/metaengine.cpp:275 engines/hypno/metaengine.cpp:251
+#: engines/hdb/metaengine.cpp:275 engines/hypno/metaengine.cpp:308
 #: engines/lab/metaengine.cpp:172 engines/mohawk/myst_stacks/menu.cpp:317
 #: engines/mohawk/riven_stacks/aspit.cpp:454
 #: engines/neverhood/metaengine.cpp:200 engines/saga/metaengine.cpp:449
@@ -3945,8 +3945,9 @@ msgstr "H??jreklik"
 #: engines/buried/metaengine.cpp:167 engines/crab/input/input.cpp:261
 #: engines/cruise/metaengine.cpp:217 engines/dm/metaengine.cpp:180
 #: engines/grim/grim.cpp:550 engines/hdb/metaengine.cpp:258
-#: engines/hypno/metaengine.cpp:186 engines/hypno/metaengine.cpp:369
-#: engines/icb/icb.cpp:137 engines/icb/icb.cpp:203 engines/mohawk/myst.cpp:595
+#: engines/hypno/metaengine.cpp:243 engines/hypno/metaengine.cpp:339
+#: engines/hypno/metaengine.cpp:456 engines/icb/icb.cpp:137
+#: engines/icb/icb.cpp:203 engines/mohawk/myst.cpp:595
 #: engines/mohawk/riven.cpp:876 engines/ngi/metaengine.cpp:136
 #: engines/sky/metaengine.cpp:126 engines/sludge/keymapper_tables.h:52
 #: engines/sludge/keymapper_tables.h:125 engines/sludge/keymapper_tables.h:187
@@ -4886,7 +4887,7 @@ msgstr ""
 #: backends/platform/sdl/opendingux/opendingux.cpp:57
 #: backends/platform/sdl/ps3/ps3.cpp:50 backends/platform/sdl/psp2/psp2.cpp:51
 #: engines/bagel/metaengine.cpp:64 engines/efh/metaengine.cpp:397
-#: engines/got/metaengine.cpp:76 engines/hypno/metaengine.cpp:363
+#: engines/got/metaengine.cpp:76 engines/hypno/metaengine.cpp:449
 #: engines/mm/mm1/metaengine.cpp:50 engines/mm/mm1/metaengine.cpp:97
 #: engines/sherlock/metaengine.cpp:660 engines/sherlock/metaengine.cpp:672
 #: engines/sherlock/metaengine.cpp:1014 engines/supernova/metaengine.cpp:303
@@ -6096,7 +6097,7 @@ msgstr "Spring linje over"
 #: engines/access/metaengine.cpp:237 engines/asylum/metaengine.cpp:171
 #: engines/asylum/metaengine.cpp:198 engines/cine/metaengine.cpp:377
 #: engines/efh/metaengine.cpp:286 engines/hdb/metaengine.cpp:213
-#: engines/hypno/metaengine.cpp:302 engines/hypno/metaengine.cpp:356
+#: engines/hypno/metaengine.cpp:363 engines/hypno/metaengine.cpp:411
 #: engines/nancy/input.cpp:195 engines/tinsel/metaengine.cpp:263
 #: engines/titanic/metaengine.cpp:324 engines/ultima/ultima8/metaengine.cpp:52
 #, fuzzy
@@ -6107,7 +6108,7 @@ msgstr "Flyt bagud"
 #: engines/access/metaengine.cpp:244 engines/asylum/metaengine.cpp:177
 #: engines/asylum/metaengine.cpp:205 engines/cine/metaengine.cpp:384
 #: engines/efh/metaengine.cpp:293 engines/hdb/metaengine.cpp:219
-#: engines/hypno/metaengine.cpp:290 engines/hypno/metaengine.cpp:342
+#: engines/hypno/metaengine.cpp:351 engines/hypno/metaengine.cpp:399
 #: engines/nancy/input.cpp:201 engines/tinsel/metaengine.cpp:269
 #: engines/titanic/metaengine.cpp:330 engines/ultima/ultima8/metaengine.cpp:53
 #, fuzzy
@@ -6119,7 +6120,7 @@ msgstr "Annuller download"
 #: engines/access/metaengine.cpp:251 engines/asylum/metaengine.cpp:183
 #: engines/asylum/metaengine.cpp:212 engines/cine/metaengine.cpp:391
 #: engines/efh/metaengine.cpp:300 engines/hdb/metaengine.cpp:225
-#: engines/hypno/metaengine.cpp:284 engines/hypno/metaengine.cpp:335
+#: engines/hypno/metaengine.cpp:345 engines/hypno/metaengine.cpp:393
 #: engines/kyra/engine/eobcommon.cpp:373 engines/lure/metaengine.cpp:218
 #: engines/nancy/input.cpp:207 engines/tinsel/metaengine.cpp:275
 #: engines/titanic/metaengine.cpp:312 engines/titanic/metaengine.cpp:390
@@ -6133,7 +6134,7 @@ msgstr "Flyt til venstre"
 #: engines/access/metaengine.cpp:258 engines/asylum/metaengine.cpp:189
 #: engines/asylum/metaengine.cpp:219 engines/cine/metaengine.cpp:398
 #: engines/efh/metaengine.cpp:307 engines/hdb/metaengine.cpp:231
-#: engines/hypno/metaengine.cpp:296 engines/hypno/metaengine.cpp:349
+#: engines/hypno/metaengine.cpp:357 engines/hypno/metaengine.cpp:405
 #: engines/kyra/engine/eobcommon.cpp:374 engines/lure/metaengine.cpp:226
 #: engines/nancy/input.cpp:213 engines/tinsel/metaengine.cpp:281
 #: engines/titanic/metaengine.cpp:318 engines/titanic/metaengine.cpp:396
@@ -6624,8 +6625,8 @@ msgstr ""
 #: engines/cine/metaengine.cpp:271 engines/cruise/metaengine.cpp:169
 #: engines/dm/metaengine.cpp:126 engines/draci/metaengine.cpp:196
 #: engines/drascula/metaengine.cpp:206 engines/hopkins/metaengine.cpp:207
-#: engines/hugo/metaengine.cpp:130 engines/hypno/metaengine.cpp:166
-#: engines/hypno/metaengine.cpp:267 engines/hypno/metaengine.cpp:311
+#: engines/hugo/metaengine.cpp:130 engines/hypno/metaengine.cpp:225
+#: engines/hypno/metaengine.cpp:324 engines/hypno/metaengine.cpp:372
 #: engines/illusions/metaengine.cpp:90 engines/lab/metaengine.cpp:152
 #: engines/lure/metaengine.cpp:169 engines/made/metaengine.cpp:133
 #: engines/mads/metaengine.cpp:317 engines/neverhood/metaengine.cpp:186
@@ -7361,7 +7362,7 @@ msgstr "Museindfangning til/fra"
 
 #. I18N: This keymap allows skipping video cutscenes
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
-#: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
+#: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:210
 #: engines/phoenixvr/metaengine.cpp:94 engines/scumm/help.cpp:79
 #: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
@@ -8658,7 +8659,7 @@ msgstr "Handling/v??lg"
 msgid "Select option 14"
 msgstr "Handling/v??lg"
 
-#: engines/efh/metaengine.cpp:257 engines/hypno/metaengine.cpp:312
+#: engines/efh/metaengine.cpp:257 engines/hypno/metaengine.cpp:373
 #: engines/sludge/keymapper_tables.h:35 engines/supernova/metaengine.cpp:218
 #, fuzzy
 msgid "Menu keymappings"
@@ -10325,16 +10326,69 @@ msgstr ""
 msgid "Intro keymappings"
 msgstr "~I~ndstillinger ..."
 
-#: engines/hypno/metaengine.cpp:134
+#: engines/hypno/metaengine.cpp:134 engines/hypno/metaengine.cpp:144
 msgid "Primary shoot"
 msgstr ""
 
-#: engines/hypno/metaengine.cpp:140
+#: engines/hypno/metaengine.cpp:139
 #, fuzzy
 msgid "Secondary shoot"
 msgstr "Andet barn"
 
-#: engines/hypno/metaengine.cpp:146 engines/lab/metaengine.cpp:209
+#: engines/hypno/metaengine.cpp:149
+#, fuzzy
+#| msgid "Map hide toolbar action"
+msgid "Rapid fire / secondary action"
+msgstr "Tildel \"skjul v??rkt??jslinje\"-handling"
+
+#: engines/hypno/metaengine.cpp:155
+#, fuzzy
+#| msgid "Slide Left"
+msgid "Aim left"
+msgstr "Flyt til venstre"
+
+#: engines/hypno/metaengine.cpp:160
+#, fuzzy
+msgid "Aim down"
+msgstr "Rul liste ned"
+
+#: engines/hypno/metaengine.cpp:165
+#, fuzzy
+msgid "Aim right"
+msgstr "H??jre"
+
+#: engines/hypno/metaengine.cpp:170
+msgid "Aim up"
+msgstr ""
+
+#: engines/hypno/metaengine.cpp:175 engines/scumm/metaengine.cpp:1202
+#: engines/scumm/metaengine.cpp:1279
+#, fuzzy
+#| msgid "Slide Left"
+msgid "Stick left"
+msgstr "Flyt til venstre"
+
+#: engines/hypno/metaengine.cpp:182 engines/scumm/metaengine.cpp:1195
+#: engines/scumm/metaengine.cpp:1272
+#, fuzzy
+msgid "Stick down"
+msgstr "Rul liste ned"
+
+#: engines/hypno/metaengine.cpp:189 engines/scumm/metaengine.cpp:1209
+#: engines/scumm/metaengine.cpp:1286
+#, fuzzy
+#| msgid "Slide Right"
+msgid "Stick right"
+msgstr "Flyt til h??jre"
+
+#: engines/hypno/metaengine.cpp:196 engines/scumm/metaengine.cpp:1188
+#: engines/scumm/metaengine.cpp:1265
+#, fuzzy
+#| msgid "Pick up"
+msgid "Stick up"
+msgstr "Tag op"
+
+#: engines/hypno/metaengine.cpp:203 engines/lab/metaengine.cpp:209
 #: engines/ngi/metaengine.cpp:168 engines/sherlock/metaengine.cpp:872
 #: engines/teenagent/metaengine.cpp:232 engines/toon/metaengine.cpp:98
 #: engines/voyeur/metaengine.cpp:235
@@ -10343,23 +10397,23 @@ msgstr "Andet barn"
 msgid "Skip intro"
 msgstr "Spring linje over"
 
-#: engines/hypno/metaengine.cpp:167
+#: engines/hypno/metaengine.cpp:226
 #, fuzzy
 msgid "Exit menu keymappings"
 msgstr "~I~ndstillinger ..."
 
-#: engines/hypno/metaengine.cpp:168
+#: engines/hypno/metaengine.cpp:227
 #, fuzzy
 msgid "Difficulty selection menu keymappings"
 msgstr "~I~ndstillinger ..."
 
-#: engines/hypno/metaengine.cpp:169
+#: engines/hypno/metaengine.cpp:228
 #, fuzzy
 msgid "Retry menu keymappings"
 msgstr "~I~ndstillinger ..."
 
-#: engines/hypno/metaengine.cpp:172 engines/hypno/metaengine.cpp:270
-#: engines/hypno/metaengine.cpp:322
+#: engines/hypno/metaengine.cpp:231 engines/hypno/metaengine.cpp:327
+#: engines/hypno/metaengine.cpp:382
 #, fuzzy
 #| msgid "Speech"
 msgid "Skip level (cheat)"
@@ -10368,58 +10422,79 @@ msgstr "Tale"
 #. I18N: (Game name: Soldier Boyz) player refers to the users own character.
 #. I18N: (Game name: Marvel Comics Spider-Man: The Sinister Six) player refers to the users own character.
 #. I18N: (Game name: Wetlands) player refers to the users own character.
-#: engines/hypno/metaengine.cpp:180 engines/hypno/metaengine.cpp:278
-#: engines/hypno/metaengine.cpp:329
+#: engines/hypno/metaengine.cpp:238 engines/hypno/metaengine.cpp:334
+#: engines/hypno/metaengine.cpp:388
 msgid "Kill player"
 msgstr ""
 
 #. I18N: (Game name: Soldier Boyz) the game has 3 difficulty levels: Chump, Punk and Badass. Chump is the easy mode.
-#: engines/hypno/metaengine.cpp:205
+#: engines/hypno/metaengine.cpp:262
 msgid "Chump"
 msgstr ""
 
 #. I18N: (Game name: Soldier Boyz) the game has 3 difficulty levels: Chump, Punk and Badass. Punk is the medium mode.
-#: engines/hypno/metaengine.cpp:212
+#: engines/hypno/metaengine.cpp:269
 msgid "Punk"
 msgstr ""
 
 #. I18N: (Game name: Soldier Boyz) the game has 3 difficulty levels: Chump, Punk and Badass. Badass is the hard mode.
-#: engines/hypno/metaengine.cpp:219
+#: engines/hypno/metaengine.cpp:276
 msgid "Badass"
 msgstr ""
 
 #. I18N: (Game name: Soldier Boyz) This makes the player restart from the last checkpoint.
-#: engines/hypno/metaengine.cpp:232
+#: engines/hypno/metaengine.cpp:289
 msgid "Retry sector"
 msgstr ""
 
 #. I18N: (Game name: Soldier Boyz) This makes the player restart the current mission / level.
-#: engines/hypno/metaengine.cpp:239
+#: engines/hypno/metaengine.cpp:296
 #, fuzzy
 #| msgid "Transitions:"
 msgid "Restart territory"
 msgstr "Overgange:"
 
-#: engines/hypno/metaengine.cpp:245
+#: engines/hypno/metaengine.cpp:302
 msgid "Begin new mission"
 msgstr ""
 
-#: engines/hypno/metaengine.cpp:313
+#: engines/hypno/metaengine.cpp:374
 #, fuzzy
 msgid "Pause keymappings"
 msgstr "~I~ndstillinger ..."
 
-#: engines/hypno/metaengine.cpp:314
+#: engines/hypno/metaengine.cpp:375
 #, fuzzy
 msgid "Direction keymappings"
 msgstr "~I~ndstillinger ..."
 
-#: engines/hypno/metaengine.cpp:316 engines/mads/metaengine.cpp:391
+#: engines/hypno/metaengine.cpp:377 engines/mads/metaengine.cpp:391
 #: engines/tsage/metaengine.cpp:259
 #, fuzzy
 msgid "Show credits"
 msgstr "Vis muse mark??r"
 
+#: engines/hypno/metaengine.cpp:417
+#, fuzzy
+msgid "Menu left"
+msgstr "Flyt til venstre"
+
+#: engines/hypno/metaengine.cpp:425
+#, fuzzy
+msgid "Menu down"
+msgstr "Annuller download"
+
+#: engines/hypno/metaengine.cpp:433
+#, fuzzy
+msgid "Menu right"
+msgstr "Flyt til h??jre"
+
+#: engines/hypno/metaengine.cpp:441
+#, fuzzy
+#| msgid "Menu"
+msgid "Menu up"
+msgstr "Menu"
+
 #. I18N: Action in In Cold Blood
 #: engines/icb/icb.cpp:107
 msgid "Arm"
@@ -13894,29 +13969,6 @@ msgstr ""
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1188 engines/scumm/metaengine.cpp:1265
-#, fuzzy
-#| msgid "Pick up"
-msgid "Stick up"
-msgstr "Tag op"
-
-#: engines/scumm/metaengine.cpp:1195 engines/scumm/metaengine.cpp:1272
-#, fuzzy
-msgid "Stick down"
-msgstr "Rul liste ned"
-
-#: engines/scumm/metaengine.cpp:1202 engines/scumm/metaengine.cpp:1279
-#, fuzzy
-#| msgid "Slide Left"
-msgid "Stick left"
-msgstr "Flyt til venstre"
-
-#: engines/scumm/metaengine.cpp:1209 engines/scumm/metaengine.cpp:1286
-#, fuzzy
-#| msgid "Slide Right"
-msgid "Stick right"
-msgstr "Flyt til h??jre"
-
 #: engines/scumm/metaengine.cpp:1216 engines/scumm/metaengine.cpp:1293
 #, fuzzy
 msgid "Fire / select"
@@ -19150,9 +19202,6 @@ msgstr ""
 #~ msgstr ""
 #~ "Du skal tildele en tast til 'H??jreklik'-handlingen for at spille spillet"
 
-#~ msgid "Map hide toolbar action"
-#~ msgstr "Tildel \"skjul v??rkt??jslinje\"-handling"
-
 #~ msgid "You must map a key to the 'Hide toolbar' action to play this game"
 #~ msgstr ""
 #~ "Du skal tildele en tast til 'Skjul v??rkt??jslinje'-handlingen for at "
diff --git a/po/de_DE.po b/po/de_DE.po
index e641790a3f3..005c24faf7a 100644
--- a/po/de_DE.po
+++ b/po/de_DE.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.10.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-13 06:14+0000\n"
+"POT-Creation-Date: 2026-06-13 10:20+0000\n"
 "PO-Revision-Date: 2026-05-05 16:31+0000\n"
 "Last-Translator: Lothar Serra Mari <lothar.serramari at scummvm.org>\n"
 "Language-Team: German <https://translations.scummvm.org/projects/scummvm/"
@@ -134,7 +134,7 @@ msgstr "Pfad hoch"
 #: engines/buried/saveload.cpp:83 engines/cine/saveload.cpp:857
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:123
 #: engines/drascula/saveload.cpp:51 engines/grim/inputdialog.cpp:84
-#: engines/hypno/metaengine.cpp:225 engines/mohawk/myst_stacks/menu.cpp:283
+#: engines/hypno/metaengine.cpp:282 engines/mohawk/myst_stacks/menu.cpp:283
 #: engines/mohawk/myst_stacks/menu.cpp:300
 #: engines/mohawk/myst_stacks/menu.cpp:318
 #: engines/mohawk/riven_stacks/aspit.cpp:349
@@ -340,7 +340,7 @@ msgstr ""
 #: engines/chamber/metaengine.cpp:91 engines/director/events.cpp:122
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:139
 #: engines/efh/metaengine.cpp:362 engines/grim/grim.cpp:366
-#: engines/hypno/metaengine.cpp:192 engines/kyra/gui/saveload_eob.cpp:611
+#: engines/hypno/metaengine.cpp:249 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:197
 #: engines/mm/mm1/metaengine.cpp:54 engines/parallaction/metaengine.cpp:169
 #: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
@@ -359,7 +359,7 @@ msgstr "Ja"
 #: engines/chamber/metaengine.cpp:97 engines/director/events.cpp:122
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:139
 #: engines/efh/metaengine.cpp:368 engines/grim/grim.cpp:366
-#: engines/hypno/metaengine.cpp:198 engines/kyra/gui/saveload_eob.cpp:611
+#: engines/hypno/metaengine.cpp:255 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:204
 #: engines/mm/mm1/metaengine.cpp:52 engines/parallaction/metaengine.cpp:175
 #: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
@@ -3421,7 +3421,7 @@ msgstr "Spiele-Engine f??r Grafik-Adventures"
 #: backends/events/default/default-events.cpp:358
 #: engines/adl/metaengine.cpp:352 engines/buried/metaengine.cpp:154
 #: engines/chamber/metaengine.cpp:84 engines/drascula/metaengine.cpp:304
-#: engines/hdb/metaengine.cpp:275 engines/hypno/metaengine.cpp:251
+#: engines/hdb/metaengine.cpp:275 engines/hypno/metaengine.cpp:308
 #: engines/lab/metaengine.cpp:172 engines/mohawk/myst_stacks/menu.cpp:317
 #: engines/mohawk/riven_stacks/aspit.cpp:454
 #: engines/neverhood/metaengine.cpp:200 engines/saga/metaengine.cpp:449
@@ -3997,8 +3997,9 @@ msgstr "Rechtsklick"
 #: engines/buried/metaengine.cpp:167 engines/crab/input/input.cpp:261
 #: engines/cruise/metaengine.cpp:217 engines/dm/metaengine.cpp:180
 #: engines/grim/grim.cpp:550 engines/hdb/metaengine.cpp:258
-#: engines/hypno/metaengine.cpp:186 engines/hypno/metaengine.cpp:369
-#: engines/icb/icb.cpp:137 engines/icb/icb.cpp:203 engines/mohawk/myst.cpp:595
+#: engines/hypno/metaengine.cpp:243 engines/hypno/metaengine.cpp:339
+#: engines/hypno/metaengine.cpp:456 engines/icb/icb.cpp:137
+#: engines/icb/icb.cpp:203 engines/mohawk/myst.cpp:595
 #: engines/mohawk/riven.cpp:876 engines/ngi/metaengine.cpp:136
 #: engines/sky/metaengine.cpp:126 engines/sludge/keymapper_tables.h:52
 #: engines/sludge/keymapper_tables.h:125 engines/sludge/keymapper_tables.h:187
@@ -4894,7 +4895,7 @@ msgstr "Y"
 #: backends/platform/sdl/opendingux/opendingux.cpp:57
 #: backends/platform/sdl/ps3/ps3.cpp:50 backends/platform/sdl/psp2/psp2.cpp:51
 #: engines/bagel/metaengine.cpp:64 engines/efh/metaengine.cpp:397
-#: engines/got/metaengine.cpp:76 engines/hypno/metaengine.cpp:363
+#: engines/got/metaengine.cpp:76 engines/hypno/metaengine.cpp:449
 #: engines/mm/mm1/metaengine.cpp:50 engines/mm/mm1/metaengine.cpp:97
 #: engines/sherlock/metaengine.cpp:660 engines/sherlock/metaengine.cpp:672
 #: engines/sherlock/metaengine.cpp:1014 engines/supernova/metaengine.cpp:303
@@ -6371,7 +6372,7 @@ msgstr "Film ??berspringen"
 #: engines/access/metaengine.cpp:237 engines/asylum/metaengine.cpp:171
 #: engines/asylum/metaengine.cpp:198 engines/cine/metaengine.cpp:377
 #: engines/efh/metaengine.cpp:286 engines/hdb/metaengine.cpp:213
-#: engines/hypno/metaengine.cpp:302 engines/hypno/metaengine.cpp:356
+#: engines/hypno/metaengine.cpp:363 engines/hypno/metaengine.cpp:411
 #: engines/nancy/input.cpp:195 engines/tinsel/metaengine.cpp:263
 #: engines/titanic/metaengine.cpp:324 engines/ultima/ultima8/metaengine.cpp:52
 msgid "Move up"
@@ -6381,7 +6382,7 @@ msgstr "Nach oben bewegen"
 #: engines/access/metaengine.cpp:244 engines/asylum/metaengine.cpp:177
 #: engines/asylum/metaengine.cpp:205 engines/cine/metaengine.cpp:384
 #: engines/efh/metaengine.cpp:293 engines/hdb/metaengine.cpp:219
-#: engines/hypno/metaengine.cpp:290 engines/hypno/metaengine.cpp:342
+#: engines/hypno/metaengine.cpp:351 engines/hypno/metaengine.cpp:399
 #: engines/nancy/input.cpp:201 engines/tinsel/metaengine.cpp:269
 #: engines/titanic/metaengine.cpp:330 engines/ultima/ultima8/metaengine.cpp:53
 msgid "Move down"
@@ -6392,7 +6393,7 @@ msgstr "Nach unten bewegen"
 #: engines/access/metaengine.cpp:251 engines/asylum/metaengine.cpp:183
 #: engines/asylum/metaengine.cpp:212 engines/cine/metaengine.cpp:391
 #: engines/efh/metaengine.cpp:300 engines/hdb/metaengine.cpp:225
-#: engines/hypno/metaengine.cpp:284 engines/hypno/metaengine.cpp:335
+#: engines/hypno/metaengine.cpp:345 engines/hypno/metaengine.cpp:393
 #: engines/kyra/engine/eobcommon.cpp:373 engines/lure/metaengine.cpp:218
 #: engines/nancy/input.cpp:207 engines/tinsel/metaengine.cpp:275
 #: engines/titanic/metaengine.cpp:312 engines/titanic/metaengine.cpp:390
@@ -6405,7 +6406,7 @@ msgstr "Nach links bewegen"
 #: engines/access/metaengine.cpp:258 engines/asylum/metaengine.cpp:189
 #: engines/asylum/metaengine.cpp:219 engines/cine/metaengine.cpp:398
 #: engines/efh/metaengine.cpp:307 engines/hdb/metaengine.cpp:231
-#: engines/hypno/metaengine.cpp:296 engines/hypno/metaengine.cpp:349
+#: engines/hypno/metaengine.cpp:357 engines/hypno/metaengine.cpp:405
 #: engines/kyra/engine/eobcommon.cpp:374 engines/lure/metaengine.cpp:226
 #: engines/nancy/input.cpp:213 engines/tinsel/metaengine.cpp:281
 #: engines/titanic/metaengine.cpp:318 engines/titanic/metaengine.cpp:396
@@ -6906,8 +6907,8 @@ msgstr "AGOS"
 #: engines/cine/metaengine.cpp:271 engines/cruise/metaengine.cpp:169
 #: engines/dm/metaengine.cpp:126 engines/draci/metaengine.cpp:196
 #: engines/drascula/metaengine.cpp:206 engines/hopkins/metaengine.cpp:207
-#: engines/hugo/metaengine.cpp:130 engines/hypno/metaengine.cpp:166
-#: engines/hypno/metaengine.cpp:267 engines/hypno/metaengine.cpp:311
+#: engines/hugo/metaengine.cpp:130 engines/hypno/metaengine.cpp:225
+#: engines/hypno/metaengine.cpp:324 engines/hypno/metaengine.cpp:372
 #: engines/illusions/metaengine.cpp:90 engines/lab/metaengine.cpp:152
 #: engines/lure/metaengine.cpp:169 engines/made/metaengine.cpp:133
 #: engines/mads/metaengine.cpp:317 engines/neverhood/metaengine.cpp:186
@@ -7625,7 +7626,7 @@ msgstr "Kampf umschalten"
 
 #. I18N: This keymap allows skipping video cutscenes
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
-#: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
+#: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:210
 #: engines/phoenixvr/metaengine.cpp:94 engines/scumm/help.cpp:79
 #: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
@@ -8781,7 +8782,7 @@ msgstr "Option 13 ausw??hlen"
 msgid "Select option 14"
 msgstr "Option 14 ausw??hlen"
 
-#: engines/efh/metaengine.cpp:257 engines/hypno/metaengine.cpp:312
+#: engines/efh/metaengine.cpp:257 engines/hypno/metaengine.cpp:373
 #: engines/sludge/keymapper_tables.h:35 engines/supernova/metaengine.cpp:218
 msgid "Menu keymappings"
 msgstr "Men??-Tastenbelegungen"
@@ -10319,88 +10320,168 @@ msgstr ""
 msgid "Intro keymappings"
 msgstr "Einf??hrung in die Tastenbelegung"
 
-#: engines/hypno/metaengine.cpp:134
+#: engines/hypno/metaengine.cpp:134 engines/hypno/metaengine.cpp:144
 msgid "Primary shoot"
 msgstr "Prim??rschoss"
 
-#: engines/hypno/metaengine.cpp:140
+#: engines/hypno/metaengine.cpp:139
 msgid "Secondary shoot"
 msgstr "Sekund??rer Schuss"
 
-#: engines/hypno/metaengine.cpp:146 engines/lab/metaengine.cpp:209
+#: engines/hypno/metaengine.cpp:149
+#, fuzzy
+#| msgid "Map hide toolbar action"
+msgid "Rapid fire / secondary action"
+msgstr "Aktion \"Werkzeugleiste verbergen\" zuweisen"
+
+#: engines/hypno/metaengine.cpp:155
+#, fuzzy
+#| msgid "Slide left"
+msgid "Aim left"
+msgstr "Slide nach links"
+
+#: engines/hypno/metaengine.cpp:160
+#, fuzzy
+#| msgid "Scroll down"
+msgid "Aim down"
+msgstr "Nach unten scrollen"
+
+#: engines/hypno/metaengine.cpp:165
+#, fuzzy
+#| msgid "Up right"
+msgid "Aim right"
+msgstr "Oben rechts"
+
+#: engines/hypno/metaengine.cpp:170
+msgid "Aim up"
+msgstr ""
+
+#: engines/hypno/metaengine.cpp:175 engines/scumm/metaengine.cpp:1202
+#: engines/scumm/metaengine.cpp:1279
+#, fuzzy
+#| msgid "Slide left"
+msgid "Stick left"
+msgstr "Slide nach links"
+
+#: engines/hypno/metaengine.cpp:182 engines/scumm/metaengine.cpp:1195
+#: engines/scumm/metaengine.cpp:1272
+#, fuzzy
+#| msgid "Scroll down"
+msgid "Stick down"
+msgstr "Nach unten scrollen"
+
+#: engines/hypno/metaengine.cpp:189 engines/scumm/metaengine.cpp:1209
+#: engines/scumm/metaengine.cpp:1286
+#, fuzzy
+#| msgid "Slide right"
+msgid "Stick right"
+msgstr "Slide nach rechts"
+
+#: engines/hypno/metaengine.cpp:196 engines/scumm/metaengine.cpp:1188
+#: engines/scumm/metaengine.cpp:1265
+#, fuzzy
+#| msgid "Pick up"
+msgid "Stick up"
+msgstr "Nimm"
+
+#: engines/hypno/metaengine.cpp:203 engines/lab/metaengine.cpp:209
 #: engines/ngi/metaengine.cpp:168 engines/sherlock/metaengine.cpp:872
 #: engines/teenagent/metaengine.cpp:232 engines/toon/metaengine.cpp:98
 #: engines/voyeur/metaengine.cpp:235
 msgid "Skip intro"
 msgstr "Intro ??berspringen"
 
-#: engines/hypno/metaengine.cpp:167
+#: engines/hypno/metaengine.cpp:226
 msgid "Exit menu keymappings"
 msgstr "Beenden Sie die Tastenbelegung des Men??s"
 
-#: engines/hypno/metaengine.cpp:168
+#: engines/hypno/metaengine.cpp:227
 msgid "Difficulty selection menu keymappings"
 msgstr "Tastenbelegung f??r das Men?? zur Schwierigkeitsauswahl"
 
-#: engines/hypno/metaengine.cpp:169
+#: engines/hypno/metaengine.cpp:228
 msgid "Retry menu keymappings"
 msgstr "Men??-Tastenbelegungen erneut versuchen"
 
-#: engines/hypno/metaengine.cpp:172 engines/hypno/metaengine.cpp:270
-#: engines/hypno/metaengine.cpp:322
+#: engines/hypno/metaengine.cpp:231 engines/hypno/metaengine.cpp:327
+#: engines/hypno/metaengine.cpp:382
 msgid "Skip level (cheat)"
 msgstr "Ebene ??berspringen (Cheat)"
 
 #. I18N: (Game name: Soldier Boyz) player refers to the users own character.
 #. I18N: (Game name: Marvel Comics Spider-Man: The Sinister Six) player refers to the users own character.
 #. I18N: (Game name: Wetlands) player refers to the users own character.
-#: engines/hypno/metaengine.cpp:180 engines/hypno/metaengine.cpp:278
-#: engines/hypno/metaengine.cpp:329
+#: engines/hypno/metaengine.cpp:238 engines/hypno/metaengine.cpp:334
+#: engines/hypno/metaengine.cpp:388
 msgid "Kill player"
 msgstr "Spieler t??ten"
 
 #. I18N: (Game name: Soldier Boyz) the game has 3 difficulty levels: Chump, Punk and Badass. Chump is the easy mode.
-#: engines/hypno/metaengine.cpp:205
+#: engines/hypno/metaengine.cpp:262
 msgid "Chump"
 msgstr "Trottel"
 
 #. I18N: (Game name: Soldier Boyz) the game has 3 difficulty levels: Chump, Punk and Badass. Punk is the medium mode.
-#: engines/hypno/metaengine.cpp:212
+#: engines/hypno/metaengine.cpp:269
 msgid "Punk"
 msgstr "Punk"
 
 #. I18N: (Game name: Soldier Boyz) the game has 3 difficulty levels: Chump, Punk and Badass. Badass is the hard mode.
-#: engines/hypno/metaengine.cpp:219
+#: engines/hypno/metaengine.cpp:276
 msgid "Badass"
 msgstr "Badass"
 
 #. I18N: (Game name: Soldier Boyz) This makes the player restart from the last checkpoint.
-#: engines/hypno/metaengine.cpp:232
+#: engines/hypno/metaengine.cpp:289
 msgid "Retry sector"
 msgstr "Sektor wiederholen"
 
 #. I18N: (Game name: Soldier Boyz) This makes the player restart the current mission / level.
-#: engines/hypno/metaengine.cpp:239
+#: engines/hypno/metaengine.cpp:296
 msgid "Restart territory"
 msgstr "Gebiet neu starten"
 
-#: engines/hypno/metaengine.cpp:245
+#: engines/hypno/metaengine.cpp:302
 msgid "Begin new mission"
 msgstr "Neue Mission beginnen"
 
-#: engines/hypno/metaengine.cpp:313
+#: engines/hypno/metaengine.cpp:374
 msgid "Pause keymappings"
 msgstr "Tastaturbelegungen pausieren"
 
-#: engines/hypno/metaengine.cpp:314
+#: engines/hypno/metaengine.cpp:375
 msgid "Direction keymappings"
 msgstr "Richtungs-Tastenbelegungen"
 
-#: engines/hypno/metaengine.cpp:316 engines/mads/metaengine.cpp:391
+#: engines/hypno/metaengine.cpp:377 engines/mads/metaengine.cpp:391
 #: engines/tsage/metaengine.cpp:259
 msgid "Show credits"
 msgstr "Abspann anzeigen"
 
+#: engines/hypno/metaengine.cpp:417
+#, fuzzy
+#| msgid "Move left"
+msgid "Menu left"
+msgstr "Nach links bewegen"
+
+#: engines/hypno/metaengine.cpp:425
+#, fuzzy
+#| msgid "Move down"
+msgid "Menu down"
+msgstr "Nach unten bewegen"
+
+#: engines/hypno/metaengine.cpp:433
+#, fuzzy
+#| msgid "Move right"
+msgid "Menu right"
+msgstr "Nach rechts bewegen"
+
+#: engines/hypno/metaengine.cpp:441
+#, fuzzy
+#| msgid "Menu"
+msgid "Menu up"
+msgstr "Men??"
+
 #. I18N: Action in In Cold Blood
 #: engines/icb/icb.cpp:107
 msgid "Arm"
@@ -13793,30 +13874,6 @@ msgstr ""
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1188 engines/scumm/metaengine.cpp:1265
-#, fuzzy
-#| msgid "Pick up"
-msgid "Stick up"
-msgstr "Nimm"
-
-#: engines/scumm/metaengine.cpp:1195 engines/scumm/metaengine.cpp:1272
-#, fuzzy
-#| msgid "Scroll down"
-msgid "Stick down"
-msgstr "Nach unten scrollen"
-
-#: engines/scumm/metaengine.cpp:1202 engines/scumm/metaengine.cpp:1279
-#, fuzzy
-#| msgid "Slide left"
-msgid "Stick left"
-msgstr "Slide nach links"
-
-#: engines/scumm/metaengine.cpp:1209 engines/scumm/metaengine.cpp:1286
-#, fuzzy
-#| msgid "Slide right"
-msgid "Stick right"
-msgstr "Slide nach rechts"
-
 #: engines/scumm/metaengine.cpp:1216 engines/scumm/metaengine.cpp:1293
 #, fuzzy
 #| msgid "Move / Select"
@@ -19081,9 +19138,6 @@ msgstr ""
 #~ "Sie m??ssen der Aktion \"Rechtsklick\" eine Taste zuweisen, um dieses "
 #~ "Spiel spielen zu k??nnen"
 
-#~ msgid "Map hide toolbar action"
-#~ msgstr "Aktion \"Werkzeugleiste verbergen\" zuweisen"
-
 #~ msgid "You must map a key to the 'Hide toolbar' action to play this game"
 #~ msgstr ""
 #~ "Sie m??ssen der Aktion \"Werkzeugleiste verbergen\" eine Taste zuweisen, "
diff --git a/po/el.po b/po/el.po
index dcbe70a4014..b828bd4fedb 100644
--- a/po/el.po
+++ b/po/el.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.10.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-13 06:14+0000\n"
+"POT-Creation-Date: 2026-06-13 10:20+0000\n"
 "PO-Revision-Date: 2026-04-23 00:22+0000\n"
 "Last-Translator: Antoniou Athanasios <a.antoniou79 at gmail.com>\n"
 "Language-Team: Greek <https://translations.scummvm.org/projects/scummvm/"
@@ -132,7 +132,7 @@ msgstr "???????????????? ????????"
 #: engines/buried/saveload.cpp:83 engines/cine/saveload.cpp:857
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:123
 #: engines/drascula/saveload.cpp:51 engines/grim/inputdialog.cpp:84
-#: engines/hypno/metaengine.cpp:225 engines/mohawk/myst_stacks/menu.cpp:283
+#: engines/hypno/metaengine.cpp:282 engines/mohawk/myst_stacks/menu.cpp:283
 #: engines/mohawk/myst_stacks/menu.cpp:300
 #: engines/mohawk/myst_stacks/menu.cpp:318
 #: engines/mohawk/riven_stacks/aspit.cpp:349
@@ -339,7 +339,7 @@ msgstr ""
 #: engines/chamber/metaengine.cpp:91 engines/director/events.cpp:122
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:139
 #: engines/efh/metaengine.cpp:362 engines/grim/grim.cpp:366
-#: engines/hypno/metaengine.cpp:192 engines/kyra/gui/saveload_eob.cpp:611
+#: engines/hypno/metaengine.cpp:249 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:197
 #: engines/mm/mm1/metaengine.cpp:54 engines/parallaction/metaengine.cpp:169
 #: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
@@ -358,7 +358,7 @@ msgstr "??????"
 #: engines/chamber/metaengine.cpp:97 engines/director/events.cpp:122
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:139
 #: engines/efh/metaengine.cpp:368 engines/grim/grim.cpp:366
-#: engines/hypno/metaengine.cpp:198 engines/kyra/gui/saveload_eob.cpp:611
+#: engines/hypno/metaengine.cpp:255 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:204
 #: engines/mm/mm1/metaengine.cpp:52 engines/parallaction/metaengine.cpp:175
 #: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
@@ -3476,7 +3476,7 @@ msgstr "???????????? ???????????????????? ??????????????????????"
 #: backends/events/default/default-events.cpp:358
 #: engines/adl/metaengine.cpp:352 engines/buried/metaengine.cpp:154
 #: engines/chamber/metaengine.cpp:84 engines/drascula/metaengine.cpp:304
-#: engines/hdb/metaengine.cpp:275 engines/hypno/metaengine.cpp:251
+#: engines/hdb/metaengine.cpp:275 engines/hypno/metaengine.cpp:308
 #: engines/lab/metaengine.cpp:172 engines/mohawk/myst_stacks/menu.cpp:317
 #: engines/mohawk/riven_stacks/aspit.cpp:454
 #: engines/neverhood/metaengine.cpp:200 engines/saga/metaengine.cpp:449
@@ -4068,8 +4068,9 @@ msgstr "???????? click"
 #: engines/buried/metaengine.cpp:167 engines/crab/input/input.cpp:261
 #: engines/cruise/metaengine.cpp:217 engines/dm/metaengine.cpp:180
 #: engines/grim/grim.cpp:550 engines/hdb/metaengine.cpp:258
-#: engines/hypno/metaengine.cpp:186 engines/hypno/metaengine.cpp:369
-#: engines/icb/icb.cpp:137 engines/icb/icb.cpp:203 engines/mohawk/myst.cpp:595
+#: engines/hypno/metaengine.cpp:243 engines/hypno/metaengine.cpp:339
+#: engines/hypno/metaengine.cpp:456 engines/icb/icb.cpp:137
+#: engines/icb/icb.cpp:203 engines/mohawk/myst.cpp:595
 #: engines/mohawk/riven.cpp:876 engines/ngi/metaengine.cpp:136
 #: engines/sky/metaengine.cpp:126 engines/sludge/keymapper_tables.h:52
 #: engines/sludge/keymapper_tables.h:125 engines/sludge/keymapper_tables.h:187
@@ -4968,7 +4969,7 @@ msgstr "Y"
 #: backends/platform/sdl/opendingux/opendingux.cpp:57
 #: backends/platform/sdl/ps3/ps3.cpp:50 backends/platform/sdl/psp2/psp2.cpp:51
 #: engines/bagel/metaengine.cpp:64 engines/efh/metaengine.cpp:397
-#: engines/got/metaengine.cpp:76 engines/hypno/metaengine.cpp:363
+#: engines/got/metaengine.cpp:76 engines/hypno/metaengine.cpp:449
 #: engines/mm/mm1/metaengine.cpp:50 engines/mm/mm1/metaengine.cpp:97
 #: engines/sherlock/metaengine.cpp:660 engines/sherlock/metaengine.cpp:672
 #: engines/sherlock/metaengine.cpp:1014 engines/supernova/metaengine.cpp:303
@@ -6495,7 +6496,7 @@ msgstr "?????????????????? ??????????????"
 #: engines/access/metaengine.cpp:237 engines/asylum/metaengine.cpp:171
 #: engines/asylum/metaengine.cpp:198 engines/cine/metaengine.cpp:377
 #: engines/efh/metaengine.cpp:286 engines/hdb/metaengine.cpp:213
-#: engines/hypno/metaengine.cpp:302 engines/hypno/metaengine.cpp:356
+#: engines/hypno/metaengine.cpp:363 engines/hypno/metaengine.cpp:411
 #: engines/nancy/input.cpp:195 engines/tinsel/metaengine.cpp:263
 #: engines/titanic/metaengine.cpp:324 engines/ultima/ultima8/metaengine.cpp:52
 msgid "Move up"
@@ -6505,7 +6506,7 @@ msgstr "???????????? ????????"
 #: engines/access/metaengine.cpp:244 engines/asylum/metaengine.cpp:177
 #: engines/asylum/metaengine.cpp:205 engines/cine/metaengine.cpp:384
 #: engines/efh/metaengine.cpp:293 engines/hdb/metaengine.cpp:219
-#: engines/hypno/metaengine.cpp:290 engines/hypno/metaengine.cpp:342
+#: engines/hypno/metaengine.cpp:351 engines/hypno/metaengine.cpp:399
 #: engines/nancy/input.cpp:201 engines/tinsel/metaengine.cpp:269
 #: engines/titanic/metaengine.cpp:330 engines/ultima/ultima8/metaengine.cpp:53
 msgid "Move down"
@@ -6516,7 +6517,7 @@ msgstr "???????????? ????????"
 #: engines/access/metaengine.cpp:251 engines/asylum/metaengine.cpp:183
 #: engines/asylum/metaengine.cpp:212 engines/cine/metaengine.cpp:391
 #: engines/efh/metaengine.cpp:300 engines/hdb/metaengine.cpp:225
-#: engines/hypno/metaengine.cpp:284 engines/hypno/metaengine.cpp:335
+#: engines/hypno/metaengine.cpp:345 engines/hypno/metaengine.cpp:393
 #: engines/kyra/engine/eobcommon.cpp:373 engines/lure/metaengine.cpp:218
 #: engines/nancy/input.cpp:207 engines/tinsel/metaengine.cpp:275
 #: engines/titanic/metaengine.cpp:312 engines/titanic/metaengine.cpp:390
@@ -6529,7 +6530,7 @@ msgstr "???????????? ????????????????"
 #: engines/access/metaengine.cpp:258 engines/asylum/metaengine.cpp:189
 #: engines/asylum/metaengine.cpp:219 engines/cine/metaengine.cpp:398
 #: engines/efh/metaengine.cpp:307 engines/hdb/metaengine.cpp:231
-#: engines/hypno/metaengine.cpp:296 engines/hypno/metaengine.cpp:349
+#: engines/hypno/metaengine.cpp:357 engines/hypno/metaengine.cpp:405
 #: engines/kyra/engine/eobcommon.cpp:374 engines/lure/metaengine.cpp:226
 #: engines/nancy/input.cpp:213 engines/tinsel/metaengine.cpp:281
 #: engines/titanic/metaengine.cpp:318 engines/titanic/metaengine.cpp:396
@@ -7034,8 +7035,8 @@ msgstr "AGOS - ??????????"
 #: engines/cine/metaengine.cpp:271 engines/cruise/metaengine.cpp:169
 #: engines/dm/metaengine.cpp:126 engines/draci/metaengine.cpp:196
 #: engines/drascula/metaengine.cpp:206 engines/hopkins/metaengine.cpp:207
-#: engines/hugo/metaengine.cpp:130 engines/hypno/metaengine.cpp:166
-#: engines/hypno/metaengine.cpp:267 engines/hypno/metaengine.cpp:311
+#: engines/hugo/metaengine.cpp:130 engines/hypno/metaengine.cpp:225
+#: engines/hypno/metaengine.cpp:324 engines/hypno/metaengine.cpp:372
 #: engines/illusions/metaengine.cpp:90 engines/lab/metaengine.cpp:152
 #: engines/lure/metaengine.cpp:169 engines/made/metaengine.cpp:133
 #: engines/mads/metaengine.cpp:317 engines/neverhood/metaengine.cpp:186
@@ -7753,7 +7754,7 @@ msgstr "???????????????? ?????????????????????? ?????? ????????"
 
 #. I18N: This keymap allows skipping video cutscenes
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
-#: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
+#: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:210
 #: engines/phoenixvr/metaengine.cpp:94 engines/scumm/help.cpp:79
 #: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
@@ -8915,7 +8916,7 @@ msgstr "?????????????? 13"
 msgid "Select option 14"
 msgstr "?????????????? 14"
 
-#: engines/efh/metaengine.cpp:257 engines/hypno/metaengine.cpp:312
+#: engines/efh/metaengine.cpp:257 engines/hypno/metaengine.cpp:373
 #: engines/sludge/keymapper_tables.h:35 engines/supernova/metaengine.cpp:218
 msgid "Menu keymappings"
 msgstr "?????????????? ??????????"
@@ -10448,88 +10449,168 @@ msgstr ""
 msgid "Intro keymappings"
 msgstr "?????????????? ??????????????"
 
-#: engines/hypno/metaengine.cpp:134
+#: engines/hypno/metaengine.cpp:134 engines/hypno/metaengine.cpp:144
 msgid "Primary shoot"
 msgstr "???????????????? ??????"
 
-#: engines/hypno/metaengine.cpp:140
+#: engines/hypno/metaengine.cpp:139
 msgid "Secondary shoot"
 msgstr "???????????????????? ??????"
 
-#: engines/hypno/metaengine.cpp:146 engines/lab/metaengine.cpp:209
+#: engines/hypno/metaengine.cpp:149
+#, fuzzy
+#| msgid "Map hide toolbar action"
+msgid "Rapid fire / secondary action"
+msgstr "???????????????????????? ?????????????????? ?????????????????? ?????????????? ??????????????????"
+
+#: engines/hypno/metaengine.cpp:155
+#, fuzzy
+#| msgid "Slide left"
+msgid "Aim left"
+msgstr "???????????????? ????????????????"
+
+#: engines/hypno/metaengine.cpp:160
+#, fuzzy
+#| msgid "Scroll down"
+msgid "Aim down"
+msgstr "???????????? ????????"
+
+#: engines/hypno/metaengine.cpp:165
+#, fuzzy
+#| msgid "Up right"
+msgid "Aim right"
+msgstr "???????? ??????????"
+
+#: engines/hypno/metaengine.cpp:170
+msgid "Aim up"
+msgstr ""
+
+#: engines/hypno/metaengine.cpp:175 engines/scumm/metaengine.cpp:1202
+#: engines/scumm/metaengine.cpp:1279
+#, fuzzy
+#| msgid "Slide left"
+msgid "Stick left"
+msgstr "???????????????? ????????????????"
+
+#: engines/hypno/metaengine.cpp:182 engines/scumm/metaengine.cpp:1195
+#: engines/scumm/metaengine.cpp:1272
+#, fuzzy
+#| msgid "Scroll down"
+msgid "Stick down"
+msgstr "???????????? ????????"
+
+#: engines/hypno/metaengine.cpp:189 engines/scumm/metaengine.cpp:1209
+#: engines/scumm/metaengine.cpp:1286
+#, fuzzy
+#| msgid "Slide right"
+msgid "Stick right"
+msgstr "???????????????? ??????????"
+
+#: engines/hypno/metaengine.cpp:196 engines/scumm/metaengine.cpp:1188
+#: engines/scumm/metaengine.cpp:1265
+#, fuzzy
+#| msgid "Pick up"
+msgid "Stick up"
+msgstr "????????????"
+
+#: engines/hypno/metaengine.cpp:203 engines/lab/metaengine.cpp:209
 #: engines/ngi/metaengine.cpp:168 engines/sherlock/metaengine.cpp:872
 #: engines/teenagent/metaengine.cpp:232 engines/toon/metaengine.cpp:98
 #: engines/voyeur/metaengine.cpp:235
 msgid "Skip intro"
 msgstr "?????????????????? ??????????????????"
 
-#: engines/hypno/metaengine.cpp:167
+#: engines/hypno/metaengine.cpp:226
 msgid "Exit menu keymappings"
 msgstr "?????????????? ?????????? ????????????"
 
-#: engines/hypno/metaengine.cpp:168
+#: engines/hypno/metaengine.cpp:227
 msgid "Difficulty selection menu keymappings"
 msgstr "?????????????? ?????????? ???????????????? ???????????? ??????????????????"
 
-#: engines/hypno/metaengine.cpp:169
+#: engines/hypno/metaengine.cpp:228
 msgid "Retry menu keymappings"
 msgstr "?????????????? ?????????? ????????????????????????????????"
 
-#: engines/hypno/metaengine.cpp:172 engines/hypno/metaengine.cpp:270
-#: engines/hypno/metaengine.cpp:322
+#: engines/hypno/metaengine.cpp:231 engines/hypno/metaengine.cpp:327
+#: engines/hypno/metaengine.cpp:382
 msgid "Skip level (cheat)"
 msgstr "?????????????????? ???????????????? (??????????)"
 
 #. I18N: (Game name: Soldier Boyz) player refers to the users own character.
 #. I18N: (Game name: Marvel Comics Spider-Man: The Sinister Six) player refers to the users own character.
 #. I18N: (Game name: Wetlands) player refers to the users own character.
-#: engines/hypno/metaengine.cpp:180 engines/hypno/metaengine.cpp:278
-#: engines/hypno/metaengine.cpp:329
+#: engines/hypno/metaengine.cpp:238 engines/hypno/metaengine.cpp:334
+#: engines/hypno/metaengine.cpp:388
 msgid "Kill player"
 msgstr "?????????? ????????????"
 
 #. I18N: (Game name: Soldier Boyz) the game has 3 difficulty levels: Chump, Punk and Badass. Chump is the easy mode.
-#: engines/hypno/metaengine.cpp:205
+#: engines/hypno/metaengine.cpp:262
 msgid "Chump"
 msgstr "??????????????"
 
 #. I18N: (Game name: Soldier Boyz) the game has 3 difficulty levels: Chump, Punk and Badass. Punk is the medium mode.
-#: engines/hypno/metaengine.cpp:212
+#: engines/hypno/metaengine.cpp:269
 msgid "Punk"
 msgstr "??????????????????????"
 
 #. I18N: (Game name: Soldier Boyz) the game has 3 difficulty levels: Chump, Punk and Badass. Badass is the hard mode.
-#: engines/hypno/metaengine.cpp:219
+#: engines/hypno/metaengine.cpp:276
 msgid "Badass"
 msgstr "???????????? ????????????"
 
 #. I18N: (Game name: Soldier Boyz) This makes the player restart from the last checkpoint.
-#: engines/hypno/metaengine.cpp:232
+#: engines/hypno/metaengine.cpp:289
 msgid "Retry sector"
 msgstr "?????????????????? ??????????"
 
 #. I18N: (Game name: Soldier Boyz) This makes the player restart the current mission / level.
-#: engines/hypno/metaengine.cpp:239
+#: engines/hypno/metaengine.cpp:296
 msgid "Restart territory"
 msgstr "???????????????????? ??????????????????"
 
-#: engines/hypno/metaengine.cpp:245
+#: engines/hypno/metaengine.cpp:302
 msgid "Begin new mission"
 msgstr "???????????? ???????? ??????????????????"
 
-#: engines/hypno/metaengine.cpp:313
+#: engines/hypno/metaengine.cpp:374
 msgid "Pause keymappings"
 msgstr "?????????????? ????????????????"
 
-#: engines/hypno/metaengine.cpp:314
+#: engines/hypno/metaengine.cpp:375
 msgid "Direction keymappings"
 msgstr "?????????????? ??????????????????????"
 
-#: engines/hypno/metaengine.cpp:316 engines/mads/metaengine.cpp:391
+#: engines/hypno/metaengine.cpp:377 engines/mads/metaengine.cpp:391
 #: engines/tsage/metaengine.cpp:259
 msgid "Show credits"
 msgstr "?????????????? ??????????????????????"
 
+#: engines/hypno/metaengine.cpp:417
+#, fuzzy
+#| msgid "Move left"
+msgid "Menu left"
+msgstr "???????????? ????????????????"
+
+#: engines/hypno/metaengine.cpp:425
+#, fuzzy
+#| msgid "Move down"
+msgid "Menu down"
+msgstr "???????????? ????????"
+
+#: engines/hypno/metaengine.cpp:433
+#, fuzzy
+#| msgid "Move right"
+msgid "Menu right"
+msgstr "???????????? ??????????"
+
+#: engines/hypno/metaengine.cpp:441
+#, fuzzy
+#| msgid "Menu"
+msgid "Menu up"
+msgstr "??????????"
+
 #. I18N: Action in In Cold Blood
 #: engines/icb/icb.cpp:107
 msgid "Arm"
@@ -13948,30 +14029,6 @@ msgstr ""
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1188 engines/scumm/metaengine.cpp:1265
-#, fuzzy
-#| msgid "Pick up"
-msgid "Stick up"
-msgstr "????????????"
-
-#: engines/scumm/metaengine.cpp:1195 engines/scumm/metaengine.cpp:1272
-#, fuzzy
-#| msgid "Scroll down"
-msgid "Stick down"
-msgstr "???????????? ????????"
-
-#: engines/scumm/metaengine.cpp:1202 engines/scumm/metaengine.cpp:1279
-#, fuzzy
-#| msgid "Slide left"
-msgid "Stick left"
-msgstr "???????????????? ????????????????"
-
-#: engines/scumm/metaengine.cpp:1209 engines/scumm/metaengine.cpp:1286
-#, fuzzy
-#| msgid "Slide right"
-msgid "Stick right"
-msgstr "???????????????? ??????????"
-
 #: engines/scumm/metaengine.cpp:1216 engines/scumm/metaengine.cpp:1293
 #, fuzzy
 #| msgid "Move / Select"
@@ -19577,9 +19634,6 @@ msgstr ""
 #~ "???? ???????????? ???? ???????????????????????????? ?????? ?????????????? ???????? ???????????????? \"???????? click\" ?????? "
 #~ "???? ?????????????? ???????? ???? ????????????????"
 
-#~ msgid "Map hide toolbar action"
-#~ msgstr "???????????????????????? ?????????????????? ?????????????????? ?????????????? ??????????????????"
-
 #~ msgid "You must map a key to the 'Hide toolbar' action to play this game"
 #~ msgstr ""
 #~ "???? ???????????? ???? ???????????????????????????? ?????? ?????????????? ???????? ???????????????? \"???????????????? ?????????????? "
diff --git a/po/es_ES.po b/po/es_ES.po
index c1589bfee23..3df68e35c2a 100644
--- a/po/es_ES.po
+++ b/po/es_ES.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.4.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-13 06:14+0000\n"
+"POT-Creation-Date: 2026-06-13 10:20+0000\n"
 "PO-Revision-Date: 2026-06-10 11:58+0000\n"
 "Last-Translator: IlDucci <pajaroloco_2 at hotmail.com>\n"
 "Language-Team: Spanish <https://translations.scummvm.org/projects/scummvm/"
@@ -133,7 +133,7 @@ msgstr "Arriba"
 #: engines/buried/saveload.cpp:83 engines/cine/saveload.cpp:857
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:123
 #: engines/drascula/saveload.cpp:51 engines/grim/inputdialog.cpp:84
-#: engines/hypno/metaengine.cpp:225 engines/mohawk/myst_stacks/menu.cpp:283
+#: engines/hypno/metaengine.cpp:282 engines/mohawk/myst_stacks/menu.cpp:283
 #: engines/mohawk/myst_stacks/menu.cpp:300
 #: engines/mohawk/myst_stacks/menu.cpp:318
 #: engines/mohawk/riven_stacks/aspit.cpp:349
@@ -336,7 +336,7 @@ msgstr "Ya hay otro almacenamiento activo. ??Quieres interrumpirlo?"
 #: engines/chamber/metaengine.cpp:91 engines/director/events.cpp:122
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:139
 #: engines/efh/metaengine.cpp:362 engines/grim/grim.cpp:366
-#: engines/hypno/metaengine.cpp:192 engines/kyra/gui/saveload_eob.cpp:611
+#: engines/hypno/metaengine.cpp:249 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:197
 #: engines/mm/mm1/metaengine.cpp:54 engines/parallaction/metaengine.cpp:169
 #: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
@@ -355,7 +355,7 @@ msgstr "S??"
 #: engines/chamber/metaengine.cpp:97 engines/director/events.cpp:122
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:139
 #: engines/efh/metaengine.cpp:368 engines/grim/grim.cpp:366
-#: engines/hypno/metaengine.cpp:198 engines/kyra/gui/saveload_eob.cpp:611
+#: engines/hypno/metaengine.cpp:255 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:204
 #: engines/mm/mm1/metaengine.cpp:52 engines/parallaction/metaengine.cpp:175
 #: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
@@ -3424,7 +3424,7 @@ msgstr "Motor de aventuras gr??ficas"
 #: backends/events/default/default-events.cpp:358
 #: engines/adl/metaengine.cpp:352 engines/buried/metaengine.cpp:154
 #: engines/chamber/metaengine.cpp:84 engines/drascula/metaengine.cpp:304
-#: engines/hdb/metaengine.cpp:275 engines/hypno/metaengine.cpp:251
+#: engines/hdb/metaengine.cpp:275 engines/hypno/metaengine.cpp:308
 #: engines/lab/metaengine.cpp:172 engines/mohawk/myst_stacks/menu.cpp:317
 #: engines/mohawk/riven_stacks/aspit.cpp:454
 #: engines/neverhood/metaengine.cpp:200 engines/saga/metaengine.cpp:449
@@ -4000,8 +4000,9 @@ msgstr "Clic derecho"
 #: engines/buried/metaengine.cpp:167 engines/crab/input/input.cpp:261
 #: engines/cruise/metaengine.cpp:217 engines/dm/metaengine.cpp:180
 #: engines/grim/grim.cpp:550 engines/hdb/metaengine.cpp:258
-#: engines/hypno/metaengine.cpp:186 engines/hypno/metaengine.cpp:369
-#: engines/icb/icb.cpp:137 engines/icb/icb.cpp:203 engines/mohawk/myst.cpp:595
+#: engines/hypno/metaengine.cpp:243 engines/hypno/metaengine.cpp:339
+#: engines/hypno/metaengine.cpp:456 engines/icb/icb.cpp:137
+#: engines/icb/icb.cpp:203 engines/mohawk/myst.cpp:595
 #: engines/mohawk/riven.cpp:876 engines/ngi/metaengine.cpp:136
 #: engines/sky/metaengine.cpp:126 engines/sludge/keymapper_tables.h:52
 #: engines/sludge/keymapper_tables.h:125 engines/sludge/keymapper_tables.h:187
@@ -4899,7 +4900,7 @@ msgstr "Y"
 #: backends/platform/sdl/opendingux/opendingux.cpp:57
 #: backends/platform/sdl/ps3/ps3.cpp:50 backends/platform/sdl/psp2/psp2.cpp:51
 #: engines/bagel/metaengine.cpp:64 engines/efh/metaengine.cpp:397
-#: engines/got/metaengine.cpp:76 engines/hypno/metaengine.cpp:363
+#: engines/got/metaengine.cpp:76 engines/hypno/metaengine.cpp:449
 #: engines/mm/mm1/metaengine.cpp:50 engines/mm/mm1/metaengine.cpp:97
 #: engines/sherlock/metaengine.cpp:660 engines/sherlock/metaengine.cpp:672
 #: engines/sherlock/metaengine.cpp:1014 engines/supernova/metaengine.cpp:303
@@ -6393,7 +6394,7 @@ msgstr "Omitir v??deo"
 #: engines/access/metaengine.cpp:237 engines/asylum/metaengine.cpp:171
 #: engines/asylum/metaengine.cpp:198 engines/cine/metaengine.cpp:377
 #: engines/efh/metaengine.cpp:286 engines/hdb/metaengine.cpp:213
-#: engines/hypno/metaengine.cpp:302 engines/hypno/metaengine.cpp:356
+#: engines/hypno/metaengine.cpp:363 engines/hypno/metaengine.cpp:411
 #: engines/nancy/input.cpp:195 engines/tinsel/metaengine.cpp:263
 #: engines/titanic/metaengine.cpp:324 engines/ultima/ultima8/metaengine.cpp:52
 msgid "Move up"
@@ -6403,7 +6404,7 @@ msgstr "Mover hacia arriba"
 #: engines/access/metaengine.cpp:244 engines/asylum/metaengine.cpp:177
 #: engines/asylum/metaengine.cpp:205 engines/cine/metaengine.cpp:384
 #: engines/efh/metaengine.cpp:293 engines/hdb/metaengine.cpp:219
-#: engines/hypno/metaengine.cpp:290 engines/hypno/metaengine.cpp:342
+#: engines/hypno/metaengine.cpp:351 engines/hypno/metaengine.cpp:399
 #: engines/nancy/input.cpp:201 engines/tinsel/metaengine.cpp:269
 #: engines/titanic/metaengine.cpp:330 engines/ultima/ultima8/metaengine.cpp:53
 msgid "Move down"
@@ -6414,7 +6415,7 @@ msgstr "Mover hacia abajo"
 #: engines/access/metaengine.cpp:251 engines/asylum/metaengine.cpp:183
 #: engines/asylum/metaengine.cpp:212 engines/cine/metaengine.cpp:391
 #: engines/efh/metaengine.cpp:300 engines/hdb/metaengine.cpp:225
-#: engines/hypno/metaengine.cpp:284 engines/hypno/metaengine.cpp:335
+#: engines/hypno/metaengine.cpp:345 engines/hypno/metaengine.cpp:393
 #: engines/kyra/engine/eobcommon.cpp:373 engines/lure/metaengine.cpp:218
 #: engines/nancy/input.cpp:207 engines/tinsel/metaengine.cpp:275
 #: engines/titanic/metaengine.cpp:312 engines/titanic/metaengine.cpp:390
@@ -6427,7 +6428,7 @@ msgstr "Mover hacia la izquierda"
 #: engines/access/metaengine.cpp:258 engines/asylum/metaengine.cpp:189
 #: engines/asylum/metaengine.cpp:219 engines/cine/metaengine.cpp:398
 #: engines/efh/metaengine.cpp:307 engines/hdb/metaengine.cpp:231
-#: engines/hypno/metaengine.cpp:296 engines/hypno/metaengine.cpp:349
+#: engines/hypno/metaengine.cpp:357 engines/hypno/metaengine.cpp:405
 #: engines/kyra/engine/eobcommon.cpp:374 engines/lure/metaengine.cpp:226
 #: engines/nancy/input.cpp:213 engines/tinsel/metaengine.cpp:281
 #: engines/titanic/metaengine.cpp:318 engines/titanic/metaengine.cpp:396
@@ -6928,8 +6929,8 @@ msgstr "AGOS - Principal"
 #: engines/cine/metaengine.cpp:271 engines/cruise/metaengine.cpp:169
 #: engines/dm/metaengine.cpp:126 engines/draci/metaengine.cpp:196
 #: engines/drascula/metaengine.cpp:206 engines/hopkins/metaengine.cpp:207
-#: engines/hugo/metaengine.cpp:130 engines/hypno/metaengine.cpp:166
-#: engines/hypno/metaengine.cpp:267 engines/hypno/metaengine.cpp:311
+#: engines/hugo/metaengine.cpp:130 engines/hypno/metaengine.cpp:225
+#: engines/hypno/metaengine.cpp:324 engines/hypno/metaengine.cpp:372
 #: engines/illusions/metaengine.cpp:90 engines/lab/metaengine.cpp:152
 #: engines/lure/metaengine.cpp:169 engines/made/metaengine.cpp:133
 #: engines/mads/metaengine.cpp:317 engines/neverhood/metaengine.cpp:186
@@ -7640,7 +7641,7 @@ msgstr "Entrar/Salir del modo de combate"
 
 #. I18N: This keymap allows skipping video cutscenes
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
-#: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
+#: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:210
 #: engines/phoenixvr/metaengine.cpp:94 engines/scumm/help.cpp:79
 #: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
@@ -8797,7 +8798,7 @@ msgstr "Seleccionar opci??n 13"
 msgid "Select option 14"
 msgstr "Seleccionar opci??n 14"
 
-#: engines/efh/metaengine.cpp:257 engines/hypno/metaengine.cpp:312
+#: engines/efh/metaengine.cpp:257 engines/hypno/metaengine.cpp:373
 #: engines/sludge/keymapper_tables.h:35 engines/supernova/metaengine.cpp:218
 msgid "Menu keymappings"
 msgstr "Asignaciones del men??"
@@ -10324,88 +10325,160 @@ msgstr ""
 msgid "Intro keymappings"
 msgstr "Asignaciones de introducci??n"
 
-#: engines/hypno/metaengine.cpp:134
+#: engines/hypno/metaengine.cpp:134 engines/hypno/metaengine.cpp:144
 msgid "Primary shoot"
 msgstr "Disparo principal"
 
-#: engines/hypno/metaengine.cpp:140
+#: engines/hypno/metaengine.cpp:139
 msgid "Secondary shoot"
 msgstr "Disparo secundario"
 
-#: engines/hypno/metaengine.cpp:146 engines/lab/metaengine.cpp:209
+#: engines/hypno/metaengine.cpp:149
+#, fuzzy
+#| msgid "Map hide toolbar action"
+msgid "Rapid fire / secondary action"
+msgstr "Asignar acci??n 'Ocultar barra de tareas'"
+
+#: engines/hypno/metaengine.cpp:155
+#, fuzzy
+#| msgid "Slide left"
+msgid "Aim left"
+msgstr "Deslizarse a la izquierda"
+
+#: engines/hypno/metaengine.cpp:160
+#, fuzzy
+#| msgid "Stick down"
+msgid "Aim down"
+msgstr "Stick hacia abajo"
+
+#: engines/hypno/metaengine.cpp:165
+#, fuzzy
+#| msgid "Up right"
+msgid "Aim right"
+msgstr "Subir a la derecha"
+
+#: engines/hypno/metaengine.cpp:170
+msgid "Aim up"
+msgstr ""
+
+#: engines/hypno/metaengine.cpp:175 engines/scumm/metaengine.cpp:1202
+#: engines/scumm/metaengine.cpp:1279
+msgid "Stick left"
+msgstr "Stick hacia la izquierda"
+
+#: engines/hypno/metaengine.cpp:182 engines/scumm/metaengine.cpp:1195
+#: engines/scumm/metaengine.cpp:1272
+msgid "Stick down"
+msgstr "Stick hacia abajo"
+
+#: engines/hypno/metaengine.cpp:189 engines/scumm/metaengine.cpp:1209
+#: engines/scumm/metaengine.cpp:1286
+msgid "Stick right"
+msgstr "Stick hacia la derecha"
+
+#: engines/hypno/metaengine.cpp:196 engines/scumm/metaengine.cpp:1188
+#: engines/scumm/metaengine.cpp:1265
+msgid "Stick up"
+msgstr "Stick hacia arriba"
+
+#: engines/hypno/metaengine.cpp:203 engines/lab/metaengine.cpp:209
 #: engines/ngi/metaengine.cpp:168 engines/sherlock/metaengine.cpp:872
 #: engines/teenagent/metaengine.cpp:232 engines/toon/metaengine.cpp:98
 #: engines/voyeur/metaengine.cpp:235
 msgid "Skip intro"
 msgstr "Omitir introducci??n"
 
-#: engines/hypno/metaengine.cpp:167
+#: engines/hypno/metaengine.cpp:226
 msgid "Exit menu keymappings"
 msgstr "Asignaciones del men?? de salida"
 
-#: engines/hypno/metaengine.cpp:168
+#: engines/hypno/metaengine.cpp:227
 msgid "Difficulty selection menu keymappings"
 msgstr "Asignaciones del men?? de sel. dificultad"
 
-#: engines/hypno/metaengine.cpp:169
+#: engines/hypno/metaengine.cpp:228
 msgid "Retry menu keymappings"
 msgstr "Asignaciones del men?? de reintentado"
 
-#: engines/hypno/metaengine.cpp:172 engines/hypno/metaengine.cpp:270
-#: engines/hypno/metaengine.cpp:322
+#: engines/hypno/metaengine.cpp:231 engines/hypno/metaengine.cpp:327
+#: engines/hypno/metaengine.cpp:382
 msgid "Skip level (cheat)"
 msgstr "Omitir nivel (truco)"
 
 #. I18N: (Game name: Soldier Boyz) player refers to the users own character.
 #. I18N: (Game name: Marvel Comics Spider-Man: The Sinister Six) player refers to the users own character.
 #. I18N: (Game name: Wetlands) player refers to the users own character.
-#: engines/hypno/metaengine.cpp:180 engines/hypno/metaengine.cpp:278
-#: engines/hypno/metaengine.cpp:329
+#: engines/hypno/metaengine.cpp:238 engines/hypno/metaengine.cpp:334
+#: engines/hypno/metaengine.cpp:388
 msgid "Kill player"
 msgstr "Matar a jugador"
 
 #. I18N: (Game name: Soldier Boyz) the game has 3 difficulty levels: Chump, Punk and Badass. Chump is the easy mode.
-#: engines/hypno/metaengine.cpp:205
+#: engines/hypno/metaengine.cpp:262
 msgid "Chump"
 msgstr "Modo f??cil"
 
 #. I18N: (Game name: Soldier Boyz) the game has 3 difficulty levels: Chump, Punk and Badass. Punk is the medium mode.
-#: engines/hypno/metaengine.cpp:212
+#: engines/hypno/metaengine.cpp:269
 msgid "Punk"
 msgstr "Modo normal"
 
 #. I18N: (Game name: Soldier Boyz) the game has 3 difficulty levels: Chump, Punk and Badass. Badass is the hard mode.
-#: engines/hypno/metaengine.cpp:219
+#: engines/hypno/metaengine.cpp:276
 msgid "Badass"
 msgstr "Modo dif??cil"
 
 #. I18N: (Game name: Soldier Boyz) This makes the player restart from the last checkpoint.
-#: engines/hypno/metaengine.cpp:232
+#: engines/hypno/metaengine.cpp:289
 msgid "Retry sector"
 msgstr "Reintentar sector"
 
 #. I18N: (Game name: Soldier Boyz) This makes the player restart the current mission / level.
-#: engines/hypno/metaengine.cpp:239
+#: engines/hypno/metaengine.cpp:296
 msgid "Restart territory"
 msgstr "Reintentar territorio"
 
-#: engines/hypno/metaengine.cpp:245
+#: engines/hypno/metaengine.cpp:302
 msgid "Begin new mission"
 msgstr "Iniciar misi??n nueva"
 
-#: engines/hypno/metaengine.cpp:313
+#: engines/hypno/metaengine.cpp:374
 msgid "Pause keymappings"
 msgstr "Asignaciones de pausa"
 
-#: engines/hypno/metaengine.cpp:314
+#: engines/hypno/metaengine.cpp:375
 msgid "Direction keymappings"
 msgstr "Asignaciones de direcciones"
 
-#: engines/hypno/metaengine.cpp:316 engines/mads/metaengine.cpp:391
+#: engines/hypno/metaengine.cpp:377 engines/mads/metaengine.cpp:391
 #: engines/tsage/metaengine.cpp:259
 msgid "Show credits"
 msgstr "Mostrar cr??ditos"
 
+#: engines/hypno/metaengine.cpp:417
+#, fuzzy
+#| msgid "Move left"
+msgid "Menu left"
+msgstr "Mover hacia la izquierda"
+
+#: engines/hypno/metaengine.cpp:425
+#, fuzzy
+#| msgid "Move down"
+msgid "Menu down"
+msgstr "Mover hacia abajo"
+
+#: engines/hypno/metaengine.cpp:433
+#, fuzzy
+#| msgid "Move right"
+msgid "Menu right"
+msgstr "Mover hacia la derecha"
+
+#: engines/hypno/metaengine.cpp:441
+#, fuzzy
+#| msgid "Menu"
+msgid "Menu up"
+msgstr "Men??"
+
 #. I18N: Action in In Cold Blood
 #: engines/icb/icb.cpp:107
 msgid "Arm"
@@ -13762,22 +13835,6 @@ msgstr "Apuntar a izda./Men?? a izda."
 msgid "Aim right / menu right"
 msgstr "Apuntar a dcha./Men?? a dcha."
 
-#: engines/scumm/metaengine.cpp:1188 engines/scumm/metaengine.cpp:1265
-msgid "Stick up"
-msgstr "Stick hacia arriba"
-
-#: engines/scumm/metaengine.cpp:1195 engines/scumm/metaengine.cpp:1272
-msgid "Stick down"
-msgstr "Stick hacia abajo"
-
-#: engines/scumm/metaengine.cpp:1202 engines/scumm/metaengine.cpp:1279
-msgid "Stick left"
-msgstr "Stick hacia la izquierda"
-
-#: engines/scumm/metaengine.cpp:1209 engines/scumm/metaengine.cpp:1286
-msgid "Stick right"
-msgstr "Stick hacia la derecha"
-
 #: engines/scumm/metaengine.cpp:1216 engines/scumm/metaengine.cpp:1293
 msgid "Fire / select"
 msgstr "Disparar/Seleccionar"
@@ -19122,9 +19179,6 @@ msgstr ""
 #~ msgstr ""
 #~ "Debes asignar una tecla a la acci??n 'Clic derecho' para jugar a este juego"
 
-#~ msgid "Map hide toolbar action"
-#~ msgstr "Asignar acci??n 'Ocultar barra de tareas'"
-
 #~ msgid "You must map a key to the 'Hide toolbar' action to play this game"
 #~ msgstr ""
 #~ "Debes asignar una tecla a la acci??n 'Ocultar barra de tareas' para jugar "
diff --git a/po/eu.po b/po/eu.po
index e5275b31f55..a96588ac1f5 100644
--- a/po/eu.po
+++ b/po/eu.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.5.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-13 06:14+0000\n"
+"POT-Creation-Date: 2026-06-13 10:20+0000\n"
 "PO-Revision-Date: 2024-02-05 18:42+0000\n"
 "Last-Translator: Josu Igoa <josuigoa at ni.eus>\n"
 "Language-Team: Basque <https://translations.scummvm.org/projects/scummvm/"
@@ -134,7 +134,7 @@ msgstr "Joan gora"
 #: engines/buried/saveload.cpp:83 engines/cine/saveload.cpp:857
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:123
 #: engines/drascula/saveload.cpp:51 engines/grim/inputdialog.cpp:84
-#: engines/hypno/metaengine.cpp:225 engines/mohawk/myst_stacks/menu.cpp:283
+#: engines/hypno/metaengine.cpp:282 engines/mohawk/myst_stacks/menu.cpp:283
 #: engines/mohawk/myst_stacks/menu.cpp:300
 #: engines/mohawk/myst_stacks/menu.cpp:318
 #: engines/mohawk/riven_stacks/aspit.cpp:349
@@ -337,7 +337,7 @@ msgstr "Beste biltegiratzen bat dago orain martxan. Gelditu nahi al duzu?"
 #: engines/chamber/metaengine.cpp:91 engines/director/events.cpp:122
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:139
 #: engines/efh/metaengine.cpp:362 engines/grim/grim.cpp:366
-#: engines/hypno/metaengine.cpp:192 engines/kyra/gui/saveload_eob.cpp:611
+#: engines/hypno/metaengine.cpp:249 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:197
 #: engines/mm/mm1/metaengine.cpp:54 engines/parallaction/metaengine.cpp:169
 #: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
@@ -356,7 +356,7 @@ msgstr "Bai"
 #: engines/chamber/metaengine.cpp:97 engines/director/events.cpp:122
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:139
 #: engines/efh/metaengine.cpp:368 engines/grim/grim.cpp:366
-#: engines/hypno/metaengine.cpp:198 engines/kyra/gui/saveload_eob.cpp:611
+#: engines/hypno/metaengine.cpp:255 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:204
 #: engines/mm/mm1/metaengine.cpp:52 engines/parallaction/metaengine.cpp:175
 #: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
@@ -3405,7 +3405,7 @@ msgstr "Abendura grafikoen motorea"
 #: backends/events/default/default-events.cpp:358
 #: engines/adl/metaengine.cpp:352 engines/buried/metaengine.cpp:154
 #: engines/chamber/metaengine.cpp:84 engines/drascula/metaengine.cpp:304
-#: engines/hdb/metaengine.cpp:275 engines/hypno/metaengine.cpp:251
+#: engines/hdb/metaengine.cpp:275 engines/hypno/metaengine.cpp:308
 #: engines/lab/metaengine.cpp:172 engines/mohawk/myst_stacks/menu.cpp:317
 #: engines/mohawk/riven_stacks/aspit.cpp:454
 #: engines/neverhood/metaengine.cpp:200 engines/saga/metaengine.cpp:449
@@ -3971,8 +3971,9 @@ msgstr "Eskuin klik-a"
 #: engines/buried/metaengine.cpp:167 engines/crab/input/input.cpp:261
 #: engines/cruise/metaengine.cpp:217 engines/dm/metaengine.cpp:180
 #: engines/grim/grim.cpp:550 engines/hdb/metaengine.cpp:258
-#: engines/hypno/metaengine.cpp:186 engines/hypno/metaengine.cpp:369
-#: engines/icb/icb.cpp:137 engines/icb/icb.cpp:203 engines/mohawk/myst.cpp:595
+#: engines/hypno/metaengine.cpp:243 engines/hypno/metaengine.cpp:339
+#: engines/hypno/metaengine.cpp:456 engines/icb/icb.cpp:137
+#: engines/icb/icb.cpp:203 engines/mohawk/myst.cpp:595
 #: engines/mohawk/riven.cpp:876 engines/ngi/metaengine.cpp:136
 #: engines/sky/metaengine.cpp:126 engines/sludge/keymapper_tables.h:52
 #: engines/sludge/keymapper_tables.h:125 engines/sludge/keymapper_tables.h:187
@@ -4935,7 +4936,7 @@ msgstr ""
 #: backends/platform/sdl/opendingux/opendingux.cpp:57
 #: backends/platform/sdl/ps3/ps3.cpp:50 backends/platform/sdl/psp2/psp2.cpp:51
 #: engines/bagel/metaengine.cpp:64 engines/efh/metaengine.cpp:397
-#: engines/got/metaengine.cpp:76 engines/hypno/metaengine.cpp:363
+#: engines/got/metaengine.cpp:76 engines/hypno/metaengine.cpp:449
 #: engines/mm/mm1/metaengine.cpp:50 engines/mm/mm1/metaengine.cpp:97
 #: engines/sherlock/metaengine.cpp:660 engines/sherlock/metaengine.cpp:672
 #: engines/sherlock/metaengine.cpp:1014 engines/supernova/metaengine.cpp:303
@@ -6151,7 +6152,7 @@ msgstr "Lerroa saltatu"
 #: engines/access/metaengine.cpp:237 engines/asylum/metaengine.cpp:171
 #: engines/asylum/metaengine.cpp:198 engines/cine/metaengine.cpp:377
 #: engines/efh/metaengine.cpp:286 engines/hdb/metaengine.cpp:213
-#: engines/hypno/metaengine.cpp:302 engines/hypno/metaengine.cpp:356
+#: engines/hypno/metaengine.cpp:363 engines/hypno/metaengine.cpp:411
 #: engines/nancy/input.cpp:195 engines/tinsel/metaengine.cpp:263
 #: engines/titanic/metaengine.cpp:324 engines/ultima/ultima8/metaengine.cpp:52
 #, fuzzy
@@ -6162,7 +6163,7 @@ msgstr "Atzera mugitu"
 #: engines/access/metaengine.cpp:244 engines/asylum/metaengine.cpp:177
 #: engines/asylum/metaengine.cpp:205 engines/cine/metaengine.cpp:384
 #: engines/efh/metaengine.cpp:293 engines/hdb/metaengine.cpp:219
-#: engines/hypno/metaengine.cpp:290 engines/hypno/metaengine.cpp:342
+#: engines/hypno/metaengine.cpp:351 engines/hypno/metaengine.cpp:399
 #: engines/nancy/input.cpp:201 engines/tinsel/metaengine.cpp:269
 #: engines/titanic/metaengine.cpp:330 engines/ultima/ultima8/metaengine.cpp:53
 #, fuzzy
@@ -6174,7 +6175,7 @@ msgstr "Eskuinera irristatu"
 #: engines/access/metaengine.cpp:251 engines/asylum/metaengine.cpp:183
 #: engines/asylum/metaengine.cpp:212 engines/cine/metaengine.cpp:391
 #: engines/efh/metaengine.cpp:300 engines/hdb/metaengine.cpp:225
-#: engines/hypno/metaengine.cpp:284 engines/hypno/metaengine.cpp:335
+#: engines/hypno/metaengine.cpp:345 engines/hypno/metaengine.cpp:393
 #: engines/kyra/engine/eobcommon.cpp:373 engines/lure/metaengine.cpp:218
 #: engines/nancy/input.cpp:207 engines/tinsel/metaengine.cpp:275
 #: engines/titanic/metaengine.cpp:312 engines/titanic/metaengine.cpp:390
@@ -6188,7 +6189,7 @@ msgstr "Ezkerrera irristatu"
 #: engines/access/metaengine.cpp:258 engines/asylum/metaengine.cpp:189
 #: engines/asylum/metaengine.cpp:219 engines/cine/metaengine.cpp:398
 #: engines/efh/metaengine.cpp:307 engines/hdb/metaengine.cpp:231
-#: engines/hypno/metaengine.cpp:296 engines/hypno/metaengine.cpp:349
+#: engines/hypno/metaengine.cpp:357 engines/hypno/metaengine.cpp:405
 #: engines/kyra/engine/eobcommon.cpp:374 engines/lure/metaengine.cpp:226
 #: engines/nancy/input.cpp:213 engines/tinsel/metaengine.cpp:281
 #: engines/titanic/metaengine.cpp:318 engines/titanic/metaengine.cpp:396
@@ -6682,8 +6683,8 @@ msgstr ""
 #: engines/cine/metaengine.cpp:271 engines/cruise/metaengine.cpp:169
 #: engines/dm/metaengine.cpp:126 engines/draci/metaengine.cpp:196
 #: engines/drascula/metaengine.cpp:206 engines/hopkins/metaengine.cpp:207
-#: engines/hugo/metaengine.cpp:130 engines/hypno/metaengine.cpp:166
-#: engines/hypno/metaengine.cpp:267 engines/hypno/metaengine.cpp:311
+#: engines/hugo/metaengine.cpp:130 engines/hypno/metaengine.cpp:225
+#: engines/hypno/metaengine.cpp:324 engines/hypno/metaengine.cpp:372
 #: engines/illusions/metaengine.cpp:90 engines/lab/metaengine.cpp:152
 #: engines/lure/metaengine.cpp:169 engines/made/metaengine.cpp:133
 #: engines/mads/metaengine.cpp:317 engines/neverhood/metaengine.cpp:186
@@ -7407,7 +7408,7 @@ msgstr "Mututu/Ez-mututu"
 
 #. I18N: This keymap allows skipping video cutscenes
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
-#: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
+#: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:210
 #: engines/phoenixvr/metaengine.cpp:94 engines/scumm/help.cpp:79
 #: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
@@ -8712,7 +8713,7 @@ msgstr "Aukeratu Taldea honen bidez"
 msgid "Select option 14"
 msgstr "Aukeratu Taldea honen bidez"
 
-#: engines/efh/metaengine.cpp:257 engines/hypno/metaengine.cpp:312
+#: engines/efh/metaengine.cpp:257 engines/hypno/metaengine.cpp:373
 #: engines/sludge/keymapper_tables.h:35 engines/supernova/metaengine.cpp:218
 #, fuzzy
 msgid "Menu keymappings"
@@ -10397,16 +10398,69 @@ msgstr ""
 msgid "Intro keymappings"
 msgstr "~A~ukerak..."
 
-#: engines/hypno/metaengine.cpp:134
+#: engines/hypno/metaengine.cpp:134 engines/hypno/metaengine.cpp:144
 msgid "Primary shoot"
 msgstr ""
 
-#: engines/hypno/metaengine.cpp:140
+#: engines/hypno/metaengine.cpp:139
 #, fuzzy
 msgid "Secondary shoot"
 msgstr "Bigarren gaztea"
 
-#: engines/hypno/metaengine.cpp:146 engines/lab/metaengine.cpp:209
+#: engines/hypno/metaengine.cpp:149
+#, fuzzy
+#| msgid "Map hide toolbar action"
+msgid "Rapid fire / secondary action"
+msgstr "'Tresna-barra ezkutatu' ekintza esleitu"
+
+#: engines/hypno/metaengine.cpp:155
+#, fuzzy
+#| msgid "Slide Left"
+msgid "Aim left"
+msgstr "Ezkerrera irristatu"
+
+#: engines/hypno/metaengine.cpp:160
+#, fuzzy
+msgid "Aim down"
+msgstr "Behera"
+
+#: engines/hypno/metaengine.cpp:165
+#, fuzzy
+msgid "Aim right"
+msgstr "Eskuin"
+
+#: engines/hypno/metaengine.cpp:170
+msgid "Aim up"
+msgstr ""
+
+#: engines/hypno/metaengine.cpp:175 engines/scumm/metaengine.cpp:1202
+#: engines/scumm/metaengine.cpp:1279
+#, fuzzy
+#| msgid "Slide Left"
+msgid "Stick left"
+msgstr "Ezkerrera irristatu"
+
+#: engines/hypno/metaengine.cpp:182 engines/scumm/metaengine.cpp:1195
+#: engines/scumm/metaengine.cpp:1272
+#, fuzzy
+msgid "Stick down"
+msgstr "Behera"
+
+#: engines/hypno/metaengine.cpp:189 engines/scumm/metaengine.cpp:1209
+#: engines/scumm/metaengine.cpp:1286
+#, fuzzy
+#| msgid "Slide Right"
+msgid "Stick right"
+msgstr "Eskuinera irristatu"
+
+#: engines/hypno/metaengine.cpp:196 engines/scumm/metaengine.cpp:1188
+#: engines/scumm/metaengine.cpp:1265
+#, fuzzy
+#| msgid "Pick up"
+msgid "Stick up"
+msgstr "Jaso"
+
+#: engines/hypno/metaengine.cpp:203 engines/lab/metaengine.cpp:209
 #: engines/ngi/metaengine.cpp:168 engines/sherlock/metaengine.cpp:872
 #: engines/teenagent/metaengine.cpp:232 engines/toon/metaengine.cpp:98
 #: engines/voyeur/metaengine.cpp:235
@@ -10415,23 +10469,23 @@ msgstr "Bigarren gaztea"
 msgid "Skip intro"
 msgstr "Lerroa saltatu"
 
-#: engines/hypno/metaengine.cpp:167
+#: engines/hypno/metaengine.cpp:226
 #, fuzzy
 msgid "Exit menu keymappings"
 msgstr "~A~ukerak..."
 
-#: engines/hypno/metaengine.cpp:168
+#: engines/hypno/metaengine.cpp:227
 #, fuzzy
 msgid "Difficulty selection menu keymappings"
 msgstr "~A~ukerak..."
 
-#: engines/hypno/metaengine.cpp:169
+#: engines/hypno/metaengine.cpp:228
 #, fuzzy
 msgid "Retry menu keymappings"
 msgstr "~A~ukerak..."
 
-#: engines/hypno/metaengine.cpp:172 engines/hypno/metaengine.cpp:270
-#: engines/hypno/metaengine.cpp:322
+#: engines/hypno/metaengine.cpp:231 engines/hypno/metaengine.cpp:327
+#: engines/hypno/metaengine.cpp:382
 #, fuzzy
 #| msgid "Speech"
 msgid "Skip level (cheat)"
@@ -10440,57 +10494,78 @@ msgstr "Ahotsa"
 #. I18N: (Game name: Soldier Boyz) player refers to the users own character.
 #. I18N: (Game name: Marvel Comics Spider-Man: The Sinister Six) player refers to the users own character.
 #. I18N: (Game name: Wetlands) player refers to the users own character.
-#: engines/hypno/metaengine.cpp:180 engines/hypno/metaengine.cpp:278
-#: engines/hypno/metaengine.cpp:329
+#: engines/hypno/metaengine.cpp:238 engines/hypno/metaengine.cpp:334
+#: engines/hypno/metaengine.cpp:388
 msgid "Kill player"
 msgstr ""
 
 #. I18N: (Game name: Soldier Boyz) the game has 3 difficulty levels: Chump, Punk and Badass. Chump is the easy mode.
-#: engines/hypno/metaengine.cpp:205
+#: engines/hypno/metaengine.cpp:262
 msgid "Chump"
 msgstr ""
 
 #. I18N: (Game name: Soldier Boyz) the game has 3 difficulty levels: Chump, Punk and Badass. Punk is the medium mode.
-#: engines/hypno/metaengine.cpp:212
+#: engines/hypno/metaengine.cpp:269
 msgid "Punk"
 msgstr ""
 
 #. I18N: (Game name: Soldier Boyz) the game has 3 difficulty levels: Chump, Punk and Badass. Badass is the hard mode.
-#: engines/hypno/metaengine.cpp:219
+#: engines/hypno/metaengine.cpp:276
 msgid "Badass"
 msgstr ""
 
 #. I18N: (Game name: Soldier Boyz) This makes the player restart from the last checkpoint.
-#: engines/hypno/metaengine.cpp:232
+#: engines/hypno/metaengine.cpp:289
 msgid "Retry sector"
 msgstr ""
 
 #. I18N: (Game name: Soldier Boyz) This makes the player restart the current mission / level.
-#: engines/hypno/metaengine.cpp:239
+#: engines/hypno/metaengine.cpp:296
 #, fuzzy
 msgid "Restart territory"
 msgstr "~T~rantsizioak gaituta"
 
-#: engines/hypno/metaengine.cpp:245
+#: engines/hypno/metaengine.cpp:302
 msgid "Begin new mission"
 msgstr ""
 
-#: engines/hypno/metaengine.cpp:313
+#: engines/hypno/metaengine.cpp:374
 #, fuzzy
 msgid "Pause keymappings"
 msgstr "~A~ukerak..."
 
-#: engines/hypno/metaengine.cpp:314
+#: engines/hypno/metaengine.cpp:375
 #, fuzzy
 msgid "Direction keymappings"
 msgstr "~A~ukerak..."
 
-#: engines/hypno/metaengine.cpp:316 engines/mads/metaengine.cpp:391
+#: engines/hypno/metaengine.cpp:377 engines/mads/metaengine.cpp:391
 #: engines/tsage/metaengine.cpp:259
 #, fuzzy
 msgid "Show credits"
 msgstr "Saguaren kurtsorea erakutsi"
 
+#: engines/hypno/metaengine.cpp:417
+#, fuzzy
+msgid "Menu left"
+msgstr "Ezkerrera irristatu"
+
+#: engines/hypno/metaengine.cpp:425
+#, fuzzy
+msgid "Menu down"
+msgstr "Eskuinera irristatu"
+
+#: engines/hypno/metaengine.cpp:433
+#, fuzzy
+msgid "Menu right"
+msgstr "Eskuinera irristatu"
+
+#: engines/hypno/metaengine.cpp:441
+#, fuzzy
+#| msgid "Menu"
+msgid "Menu up"
+msgstr "Menua"
+
 #. I18N: Action in In Cold Blood
 #: engines/icb/icb.cpp:107
 msgid "Arm"
@@ -13861,29 +13936,6 @@ msgstr ""
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1188 engines/scumm/metaengine.cpp:1265
-#, fuzzy
-#| msgid "Pick up"
-msgid "Stick up"
-msgstr "Jaso"
-
-#: engines/scumm/metaengine.cpp:1195 engines/scumm/metaengine.cpp:1272
-#, fuzzy
-msgid "Stick down"
-msgstr "Behera"
-
-#: engines/scumm/metaengine.cpp:1202 engines/scumm/metaengine.cpp:1279
-#, fuzzy
-#| msgid "Slide Left"
-msgid "Stick left"
-msgstr "Ezkerrera irristatu"
-
-#: engines/scumm/metaengine.cpp:1209 engines/scumm/metaengine.cpp:1286
-#, fuzzy
-#| msgid "Slide Right"
-msgid "Stick right"
-msgstr "Eskuinera irristatu"
-
 #: engines/scumm/metaengine.cpp:1216 engines/scumm/metaengine.cpp:1293
 #, fuzzy
 msgid "Fire / select"
@@ -18926,9 +18978,6 @@ msgstr ""
 #~ "'Eskuin klika' ekintza esleitu behar diozu tekla bati joko honetara "
 #~ "jolasteko"
 
-#~ msgid "Map hide toolbar action"
-#~ msgstr "'Tresna-barra ezkutatu' ekintza esleitu"
-
 #~ msgid "You must map a key to the 'Hide toolbar' action to play this game"
 #~ msgstr ""
 #~ "'Tresna-barra ezkutatu' ekintza tekla bati esleitu behar diozu joko "
diff --git a/po/fi_FI.po b/po/fi_FI.po
index a07206eb714..05342bf529c 100644
--- a/po/fi_FI.po
+++ b/po/fi_FI.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.6.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-13 06:14+0000\n"
+"POT-Creation-Date: 2026-06-13 10:20+0000\n"
 "PO-Revision-Date: 2026-02-07 15:22+0000\n"
 "Last-Translator: Timo Mikkolainen <tmikkola at gmail.com>\n"
 "Language-Team: Finnish <https://translations.scummvm.org/projects/scummvm/"
@@ -134,7 +134,7 @@ msgstr "Siirry yl??s"
 #: engines/buried/saveload.cpp:83 engines/cine/saveload.cpp:857
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:123
 #: engines/drascula/saveload.cpp:51 engines/grim/inputdialog.cpp:84
-#: engines/hypno/metaengine.cpp:225 engines/mohawk/myst_stacks/menu.cpp:283
+#: engines/hypno/metaengine.cpp:282 engines/mohawk/myst_stacks/menu.cpp:283
 #: engines/mohawk/myst_stacks/menu.cpp:300
 #: engines/mohawk/myst_stacks/menu.cpp:318
 #: engines/mohawk/riven_stacks/aspit.cpp:349
@@ -337,7 +337,7 @@ msgstr "Toinen tallennustila on aktiivinen. Haluatko keskeytt???? sen?"
 #: engines/chamber/metaengine.cpp:91 engines/director/events.cpp:122
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:139
 #: engines/efh/metaengine.cpp:362 engines/grim/grim.cpp:366
-#: engines/hypno/metaengine.cpp:192 engines/kyra/gui/saveload_eob.cpp:611
+#: engines/hypno/metaengine.cpp:249 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:197
 #: engines/mm/mm1/metaengine.cpp:54 engines/parallaction/metaengine.cpp:169
 #: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
@@ -356,7 +356,7 @@ msgstr "Kyll??"
 #: engines/chamber/metaengine.cpp:97 engines/director/events.cpp:122
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:139
 #: engines/efh/metaengine.cpp:368 engines/grim/grim.cpp:366
-#: engines/hypno/metaengine.cpp:198 engines/kyra/gui/saveload_eob.cpp:611
+#: engines/hypno/metaengine.cpp:255 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:204
 #: engines/mm/mm1/metaengine.cpp:52 engines/parallaction/metaengine.cpp:175
 #: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
@@ -3448,7 +3448,7 @@ msgstr "Pelimoottori graafisille seikkailupeleille"
 #: backends/events/default/default-events.cpp:358
 #: engines/adl/metaengine.cpp:352 engines/buried/metaengine.cpp:154
 #: engines/chamber/metaengine.cpp:84 engines/drascula/metaengine.cpp:304
-#: engines/hdb/metaengine.cpp:275 engines/hypno/metaengine.cpp:251
+#: engines/hdb/metaengine.cpp:275 engines/hypno/metaengine.cpp:308
 #: engines/lab/metaengine.cpp:172 engines/mohawk/myst_stacks/menu.cpp:317
 #: engines/mohawk/riven_stacks/aspit.cpp:454
 #: engines/neverhood/metaengine.cpp:200 engines/saga/metaengine.cpp:449
@@ -4020,8 +4020,9 @@ msgstr "Oikea klikkaus"
 #: engines/buried/metaengine.cpp:167 engines/crab/input/input.cpp:261
 #: engines/cruise/metaengine.cpp:217 engines/dm/metaengine.cpp:180
 #: engines/grim/grim.cpp:550 engines/hdb/metaengine.cpp:258
-#: engines/hypno/metaengine.cpp:186 engines/hypno/metaengine.cpp:369
-#: engines/icb/icb.cpp:137 engines/icb/icb.cpp:203 engines/mohawk/myst.cpp:595
+#: engines/hypno/metaengine.cpp:243 engines/hypno/metaengine.cpp:339
+#: engines/hypno/metaengine.cpp:456 engines/icb/icb.cpp:137
+#: engines/icb/icb.cpp:203 engines/mohawk/myst.cpp:595
 #: engines/mohawk/riven.cpp:876 engines/ngi/metaengine.cpp:136
 #: engines/sky/metaengine.cpp:126 engines/sludge/keymapper_tables.h:52
 #: engines/sludge/keymapper_tables.h:125 engines/sludge/keymapper_tables.h:187
@@ -4916,7 +4917,7 @@ msgstr "Y"
 #: backends/platform/sdl/opendingux/opendingux.cpp:57
 #: backends/platform/sdl/ps3/ps3.cpp:50 backends/platform/sdl/psp2/psp2.cpp:51
 #: engines/bagel/metaengine.cpp:64 engines/efh/metaengine.cpp:397
-#: engines/got/metaengine.cpp:76 engines/hypno/metaengine.cpp:363
+#: engines/got/metaengine.cpp:76 engines/hypno/metaengine.cpp:449
 #: engines/mm/mm1/metaengine.cpp:50 engines/mm/mm1/metaengine.cpp:97
 #: engines/sherlock/metaengine.cpp:660 engines/sherlock/metaengine.cpp:672
 #: engines/sherlock/metaengine.cpp:1014 engines/supernova/metaengine.cpp:303
@@ -6490,7 +6491,7 @@ msgstr "Ohita video"
 #: engines/access/metaengine.cpp:237 engines/asylum/metaengine.cpp:171
 #: engines/asylum/metaengine.cpp:198 engines/cine/metaengine.cpp:377
 #: engines/efh/metaengine.cpp:286 engines/hdb/metaengine.cpp:213
-#: engines/hypno/metaengine.cpp:302 engines/hypno/metaengine.cpp:356
+#: engines/hypno/metaengine.cpp:363 engines/hypno/metaengine.cpp:411
 #: engines/nancy/input.cpp:195 engines/tinsel/metaengine.cpp:263
 #: engines/titanic/metaengine.cpp:324 engines/ultima/ultima8/metaengine.cpp:52
 msgid "Move up"
@@ -6500,7 +6501,7 @@ msgstr "Liiku yl??sp??in"
 #: engines/access/metaengine.cpp:244 engines/asylum/metaengine.cpp:177
 #: engines/asylum/metaengine.cpp:205 engines/cine/metaengine.cpp:384
 #: engines/efh/metaengine.cpp:293 engines/hdb/metaengine.cpp:219
-#: engines/hypno/metaengine.cpp:290 engines/hypno/metaengine.cpp:342
+#: engines/hypno/metaengine.cpp:351 engines/hypno/metaengine.cpp:399
 #: engines/nancy/input.cpp:201 engines/tinsel/metaengine.cpp:269
 #: engines/titanic/metaengine.cpp:330 engines/ultima/ultima8/metaengine.cpp:53
 msgid "Move down"
@@ -6511,7 +6512,7 @@ msgstr "Liiku alasp??in"
 #: engines/access/metaengine.cpp:251 engines/asylum/metaengine.cpp:183
 #: engines/asylum/metaengine.cpp:212 engines/cine/metaengine.cpp:391
 #: engines/efh/metaengine.cpp:300 engines/hdb/metaengine.cpp:225
-#: engines/hypno/metaengine.cpp:284 engines/hypno/metaengine.cpp:335
+#: engines/hypno/metaengine.cpp:345 engines/hypno/metaengine.cpp:393
 #: engines/kyra/engine/eobcommon.cpp:373 engines/lure/metaengine.cpp:218
 #: engines/nancy/input.cpp:207 engines/tinsel/metaengine.cpp:275
 #: engines/titanic/metaengine.cpp:312 engines/titanic/metaengine.cpp:390
@@ -6524,7 +6525,7 @@ msgstr "Liiku vasemmalle"
 #: engines/access/metaengine.cpp:258 engines/asylum/metaengine.cpp:189
 #: engines/asylum/metaengine.cpp:219 engines/cine/metaengine.cpp:398
 #: engines/efh/metaengine.cpp:307 engines/hdb/metaengine.cpp:231
-#: engines/hypno/metaengine.cpp:296 engines/hypno/metaengine.cpp:349
+#: engines/hypno/metaengine.cpp:357 engines/hypno/metaengine.cpp:405
 #: engines/kyra/engine/eobcommon.cpp:374 engines/lure/metaengine.cpp:226
 #: engines/nancy/input.cpp:213 engines/tinsel/metaengine.cpp:281
 #: engines/titanic/metaengine.cpp:318 engines/titanic/metaengine.cpp:396
@@ -7017,8 +7018,8 @@ msgstr "AGOS p????taso"
 #: engines/cine/metaengine.cpp:271 engines/cruise/metaengine.cpp:169
 #: engines/dm/metaengine.cpp:126 engines/draci/metaengine.cpp:196
 #: engines/drascula/metaengine.cpp:206 engines/hopkins/metaengine.cpp:207
-#: engines/hugo/metaengine.cpp:130 engines/hypno/metaengine.cpp:166
-#: engines/hypno/metaengine.cpp:267 engines/hypno/metaengine.cpp:311
+#: engines/hugo/metaengine.cpp:130 engines/hypno/metaengine.cpp:225
+#: engines/hypno/metaengine.cpp:324 engines/hypno/metaengine.cpp:372
 #: engines/illusions/metaengine.cpp:90 engines/lab/metaengine.cpp:152
 #: engines/lure/metaengine.cpp:169 engines/made/metaengine.cpp:133
 #: engines/mads/metaengine.cpp:317 engines/neverhood/metaengine.cpp:186
@@ -7723,7 +7724,7 @@ msgstr "Taistelumoodi p????lle / pois"
 
 #. I18N: This keymap allows skipping video cutscenes
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
-#: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
+#: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:210
 #: engines/phoenixvr/metaengine.cpp:94 engines/scumm/help.cpp:79
 #: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
@@ -8915,7 +8916,7 @@ msgstr "Valitse vaihtoehto 13"
 msgid "Select option 14"
 msgstr "Valitse vaihtoehto 14"
 
-#: engines/efh/metaengine.cpp:257 engines/hypno/metaengine.cpp:312
+#: engines/efh/metaengine.cpp:257 engines/hypno/metaengine.cpp:373
 #: engines/sludge/keymapper_tables.h:35 engines/supernova/metaengine.cpp:218
 msgid "Menu keymappings"
 msgstr "Valikon n??pp??inkartoitukset"
@@ -10519,104 +10520,184 @@ msgstr "Lis???? sis??lt???? joka ei ole n??ht??viss?? alkuper??isess?? toteutuks
 msgid "Intro keymappings"
 msgstr "Intron n??pp??inkartoitukset"
 
-#: engines/hypno/metaengine.cpp:134
+#: engines/hypno/metaengine.cpp:134 engines/hypno/metaengine.cpp:144
 msgid "Primary shoot"
 msgstr ""
 
-#: engines/hypno/metaengine.cpp:140
+#: engines/hypno/metaengine.cpp:139
 #, fuzzy
 #| msgid "Seconds"
 msgid "Secondary shoot"
 msgstr "Sekuntia"
 
-#: engines/hypno/metaengine.cpp:146 engines/lab/metaengine.cpp:209
+#: engines/hypno/metaengine.cpp:149
+#, fuzzy
+#| msgid "Map hide toolbar action"
+msgid "Rapid fire / secondary action"
+msgstr "M????rit?? n??pp??in ty??kalupalkin piilottamiselle"
+
+#: engines/hypno/metaengine.cpp:155
+#, fuzzy
+#| msgid "Slide left"
+msgid "Aim left"
+msgstr "Liu'u vasemmalle"
+
+#: engines/hypno/metaengine.cpp:160
+#, fuzzy
+#| msgid "Scroll down"
+msgid "Aim down"
+msgstr "Vierit?? alas"
+
+#: engines/hypno/metaengine.cpp:165
+#, fuzzy
+#| msgid "Up right"
+msgid "Aim right"
+msgstr "Yl??oikealle"
+
+#: engines/hypno/metaengine.cpp:170
+msgid "Aim up"
+msgstr ""
+
+#: engines/hypno/metaengine.cpp:175 engines/scumm/metaengine.cpp:1202
+#: engines/scumm/metaengine.cpp:1279
+#, fuzzy
+#| msgid "Slide left"
+msgid "Stick left"
+msgstr "Liu'u vasemmalle"
+
+#: engines/hypno/metaengine.cpp:182 engines/scumm/metaengine.cpp:1195
+#: engines/scumm/metaengine.cpp:1272
+#, fuzzy
+#| msgid "Scroll down"
+msgid "Stick down"
+msgstr "Vierit?? alas"
+
+#: engines/hypno/metaengine.cpp:189 engines/scumm/metaengine.cpp:1209
+#: engines/scumm/metaengine.cpp:1286
+#, fuzzy
+#| msgid "Slide right"
+msgid "Stick right"
+msgstr "Liu'u oikealle"
+
+#: engines/hypno/metaengine.cpp:196 engines/scumm/metaengine.cpp:1188
+#: engines/scumm/metaengine.cpp:1265
+#, fuzzy
+#| msgid "Pick up"
+msgid "Stick up"
+msgstr "Ota"
+
+#: engines/hypno/metaengine.cpp:203 engines/lab/metaengine.cpp:209
 #: engines/ngi/metaengine.cpp:168 engines/sherlock/metaengine.cpp:872
 #: engines/teenagent/metaengine.cpp:232 engines/toon/metaengine.cpp:98
 #: engines/voyeur/metaengine.cpp:235
 msgid "Skip intro"
 msgstr "Ohita intro"
 
-#: engines/hypno/metaengine.cpp:167
+#: engines/hypno/metaengine.cpp:226
 #, fuzzy
 #| msgid "PET keymappings"
 msgid "Exit menu keymappings"
 msgstr "PET-n??pp??inkartoitukset"
 
-#: engines/hypno/metaengine.cpp:168
+#: engines/hypno/metaengine.cpp:227
 #, fuzzy
 #| msgid "Intro keymappings"
 msgid "Difficulty selection menu keymappings"
 msgstr "Intron n??pp??inkartoitukset"
 
-#: engines/hypno/metaengine.cpp:169
+#: engines/hypno/metaengine.cpp:228
 #, fuzzy
 #| msgid "Start menu keymappings"
 msgid "Retry menu keymappings"
 msgstr "K??ynnistysvalikon n??pp??inkartoitukset"
 
-#: engines/hypno/metaengine.cpp:172 engines/hypno/metaengine.cpp:270
-#: engines/hypno/metaengine.cpp:322
+#: engines/hypno/metaengine.cpp:231 engines/hypno/metaengine.cpp:327
+#: engines/hypno/metaengine.cpp:382
 msgid "Skip level (cheat)"
 msgstr "Ohita taso (huijaa)"
 
 #. I18N: (Game name: Soldier Boyz) player refers to the users own character.
 #. I18N: (Game name: Marvel Comics Spider-Man: The Sinister Six) player refers to the users own character.
 #. I18N: (Game name: Wetlands) player refers to the users own character.
-#: engines/hypno/metaengine.cpp:180 engines/hypno/metaengine.cpp:278
-#: engines/hypno/metaengine.cpp:329
+#: engines/hypno/metaengine.cpp:238 engines/hypno/metaengine.cpp:334
+#: engines/hypno/metaengine.cpp:388
 msgid "Kill player"
 msgstr ""
 
 #. I18N: (Game name: Soldier Boyz) the game has 3 difficulty levels: Chump, Punk and Badass. Chump is the easy mode.
-#: engines/hypno/metaengine.cpp:205
+#: engines/hypno/metaengine.cpp:262
 msgid "Chump"
 msgstr "P??lv??sti"
 
 #. I18N: (Game name: Soldier Boyz) the game has 3 difficulty levels: Chump, Punk and Badass. Punk is the medium mode.
-#: engines/hypno/metaengine.cpp:212
+#: engines/hypno/metaengine.cpp:269
 msgid "Punk"
 msgstr "Punkkari"
 
 #. I18N: (Game name: Soldier Boyz) the game has 3 difficulty levels: Chump, Punk and Badass. Badass is the hard mode.
-#: engines/hypno/metaengine.cpp:219
+#: engines/hypno/metaengine.cpp:276
 msgid "Badass"
 msgstr "Kovis"
 
 #. I18N: (Game name: Soldier Boyz) This makes the player restart from the last checkpoint.
-#: engines/hypno/metaengine.cpp:232
+#: engines/hypno/metaengine.cpp:289
 #, fuzzy
 #| msgid "Retreat"
 msgid "Retry sector"
 msgstr "Per????nny"
 
 #. I18N: (Game name: Soldier Boyz) This makes the player restart the current mission / level.
-#: engines/hypno/metaengine.cpp:239
+#: engines/hypno/metaengine.cpp:296
 #, fuzzy
 #| msgid "Restart animation"
 msgid "Restart territory"
 msgstr "Uudelleenk??ynnist?? animaatio"
 
-#: engines/hypno/metaengine.cpp:245
+#: engines/hypno/metaengine.cpp:302
 msgid "Begin new mission"
 msgstr "Aloita uusi teht??v??"
 
-#: engines/hypno/metaengine.cpp:313
+#: engines/hypno/metaengine.cpp:374
 #, fuzzy
 #| msgid "Menu keymappings"
 msgid "Pause keymappings"
 msgstr "Valikon n??pp??inkartoitukset"
 
-#: engines/hypno/metaengine.cpp:314
+#: engines/hypno/metaengine.cpp:375
 #, fuzzy
 #| msgid "Intro keymappings"
 msgid "Direction keymappings"
 msgstr "Intron n??pp??inkartoitukset"
 
-#: engines/hypno/metaengine.cpp:316 engines/mads/metaengine.cpp:391
+#: engines/hypno/metaengine.cpp:377 engines/mads/metaengine.cpp:391
 #: engines/tsage/metaengine.cpp:259
 msgid "Show credits"
 msgstr "N??yt?? lopputekstit"
 
+#: engines/hypno/metaengine.cpp:417
+#, fuzzy
+#| msgid "Move left"
+msgid "Menu left"
+msgstr "Liiku vasemmalle"
+
+#: engines/hypno/metaengine.cpp:425
+#, fuzzy
+#| msgid "Move down"
+msgid "Menu down"
+msgstr "Liiku alasp??in"
+
+#: engines/hypno/metaengine.cpp:433
+#, fuzzy
+#| msgid "Move right"
+msgid "Menu right"
+msgstr "Liiku oikealle"
+
+#: engines/hypno/metaengine.cpp:441
+#, fuzzy
+#| msgid "Menu"
+msgid "Menu up"
+msgstr "Valikko"
+
 #. I18N: Action in In Cold Blood
 #: engines/icb/icb.cpp:107
 msgid "Arm"
@@ -13993,30 +14074,6 @@ msgstr ""
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1188 engines/scumm/metaengine.cpp:1265
-#, fuzzy
-#| msgid "Pick up"
-msgid "Stick up"
-msgstr "Ota"
-
-#: engines/scumm/metaengine.cpp:1195 engines/scumm/metaengine.cpp:1272
-#, fuzzy
-#| msgid "Scroll down"
-msgid "Stick down"
-msgstr "Vierit?? alas"
-
-#: engines/scumm/metaengine.cpp:1202 engines/scumm/metaengine.cpp:1279
-#, fuzzy
-#| msgid "Slide left"
-msgid "Stick left"
-msgstr "Liu'u vasemmalle"
-
-#: engines/scumm/metaengine.cpp:1209 engines/scumm/metaengine.cpp:1286
-#, fuzzy
-#| msgid "Slide right"
-msgid "Stick right"
-msgstr "Liu'u oikealle"
-
 #: engines/scumm/metaengine.cpp:1216 engines/scumm/metaengine.cpp:1293
 #, fuzzy
 #| msgid "Move / Select"
@@ -19549,9 +19606,6 @@ msgstr ""
 #~ "Hiiren oikealle klikkaukselle on m????ritelt??v?? n??pp??in, ennen kuin t??t?? "
 #~ "peli?? voi pelata"
 
-#~ msgid "Map hide toolbar action"
-#~ msgstr "M????rit?? n??pp??in ty??kalupalkin piilottamiselle"
-
 #~ msgid "You must map a key to the 'Hide toolbar' action to play this game"
 #~ msgstr ""
 #~ "Ty??kalupalkin piilottamiselle on m????ritelt??v?? n??pp??in, ennen kuin t??t?? "
diff --git a/po/fr_FR.po b/po/fr_FR.po
index 76a5d69a98d..50a61499044 100644
--- a/po/fr_FR.po
+++ b/po/fr_FR.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-13 06:14+0000\n"
+"POT-Creation-Date: 2026-06-13 10:20+0000\n"
 "PO-Revision-Date: 2026-06-08 16:22+0000\n"
 "Last-Translator: Purple T <ZEONK at hotmail.com>\n"
 "Language-Team: French <https://translations.scummvm.org/projects/scummvm/"
@@ -133,7 +133,7 @@ msgstr "R??p. pr??c."
 #: engines/buried/saveload.cpp:83 engines/cine/saveload.cpp:857
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:123
 #: engines/drascula/saveload.cpp:51 engines/grim/inputdialog.cpp:84
-#: engines/hypno/metaengine.cpp:225 engines/mohawk/myst_stacks/menu.cpp:283
+#: engines/hypno/metaengine.cpp:282 engines/mohawk/myst_stacks/menu.cpp:283
 #: engines/mohawk/myst_stacks/menu.cpp:300
 #: engines/mohawk/myst_stacks/menu.cpp:318
 #: engines/mohawk/riven_stacks/aspit.cpp:349
@@ -338,7 +338,7 @@ msgstr ""
 #: engines/chamber/metaengine.cpp:91 engines/director/events.cpp:122
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:139
 #: engines/efh/metaengine.cpp:362 engines/grim/grim.cpp:366
-#: engines/hypno/metaengine.cpp:192 engines/kyra/gui/saveload_eob.cpp:611
+#: engines/hypno/metaengine.cpp:249 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:197
 #: engines/mm/mm1/metaengine.cpp:54 engines/parallaction/metaengine.cpp:169
 #: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
@@ -357,7 +357,7 @@ msgstr "Oui"
 #: engines/chamber/metaengine.cpp:97 engines/director/events.cpp:122
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:139
 #: engines/efh/metaengine.cpp:368 engines/grim/grim.cpp:366
-#: engines/hypno/metaengine.cpp:198 engines/kyra/gui/saveload_eob.cpp:611
+#: engines/hypno/metaengine.cpp:255 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:204
 #: engines/mm/mm1/metaengine.cpp:52 engines/parallaction/metaengine.cpp:175
 #: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
@@ -3439,7 +3439,7 @@ msgstr "Moteur de jeux d'aventure graphiques"
 #: backends/events/default/default-events.cpp:358
 #: engines/adl/metaengine.cpp:352 engines/buried/metaengine.cpp:154
 #: engines/chamber/metaengine.cpp:84 engines/drascula/metaengine.cpp:304
-#: engines/hdb/metaengine.cpp:275 engines/hypno/metaengine.cpp:251
+#: engines/hdb/metaengine.cpp:275 engines/hypno/metaengine.cpp:308
 #: engines/lab/metaengine.cpp:172 engines/mohawk/myst_stacks/menu.cpp:317
 #: engines/mohawk/riven_stacks/aspit.cpp:454
 #: engines/neverhood/metaengine.cpp:200 engines/saga/metaengine.cpp:449
@@ -4023,8 +4023,9 @@ msgstr "Clic droit"
 #: engines/buried/metaengine.cpp:167 engines/crab/input/input.cpp:261
 #: engines/cruise/metaengine.cpp:217 engines/dm/metaengine.cpp:180
 #: engines/grim/grim.cpp:550 engines/hdb/metaengine.cpp:258
-#: engines/hypno/metaengine.cpp:186 engines/hypno/metaengine.cpp:369
-#: engines/icb/icb.cpp:137 engines/icb/icb.cpp:203 engines/mohawk/myst.cpp:595
+#: engines/hypno/metaengine.cpp:243 engines/hypno/metaengine.cpp:339
+#: engines/hypno/metaengine.cpp:456 engines/icb/icb.cpp:137
+#: engines/icb/icb.cpp:203 engines/mohawk/myst.cpp:595
 #: engines/mohawk/riven.cpp:876 engines/ngi/metaengine.cpp:136
 #: engines/sky/metaengine.cpp:126 engines/sludge/keymapper_tables.h:52
 #: engines/sludge/keymapper_tables.h:125 engines/sludge/keymapper_tables.h:187
@@ -4923,7 +4924,7 @@ msgstr "Y"
 #: backends/platform/sdl/opendingux/opendingux.cpp:57
 #: backends/platform/sdl/ps3/ps3.cpp:50 backends/platform/sdl/psp2/psp2.cpp:51
 #: engines/bagel/metaengine.cpp:64 engines/efh/metaengine.cpp:397
-#: engines/got/metaengine.cpp:76 engines/hypno/metaengine.cpp:363
+#: engines/got/metaengine.cpp:76 engines/hypno/metaengine.cpp:449
 #: engines/mm/mm1/metaengine.cpp:50 engines/mm/mm1/metaengine.cpp:97
 #: engines/sherlock/metaengine.cpp:660 engines/sherlock/metaengine.cpp:672
 #: engines/sherlock/metaengine.cpp:1014 engines/supernova/metaengine.cpp:303
@@ -6408,7 +6409,7 @@ msgstr "Passer le film"
 #: engines/access/metaengine.cpp:237 engines/asylum/metaengine.cpp:171
 #: engines/asylum/metaengine.cpp:198 engines/cine/metaengine.cpp:377
 #: engines/efh/metaengine.cpp:286 engines/hdb/metaengine.cpp:213
-#: engines/hypno/metaengine.cpp:302 engines/hypno/metaengine.cpp:356
+#: engines/hypno/metaengine.cpp:363 engines/hypno/metaengine.cpp:411
 #: engines/nancy/input.cpp:195 engines/tinsel/metaengine.cpp:263
 #: engines/titanic/metaengine.cpp:324 engines/ultima/ultima8/metaengine.cpp:52
 msgid "Move up"
@@ -6418,7 +6419,7 @@ msgstr "Se d??placer vers le haut"
 #: engines/access/metaengine.cpp:244 engines/asylum/metaengine.cpp:177
 #: engines/asylum/metaengine.cpp:205 engines/cine/metaengine.cpp:384
 #: engines/efh/metaengine.cpp:293 engines/hdb/metaengine.cpp:219
-#: engines/hypno/metaengine.cpp:290 engines/hypno/metaengine.cpp:342
+#: engines/hypno/metaengine.cpp:351 engines/hypno/metaengine.cpp:399
 #: engines/nancy/input.cpp:201 engines/tinsel/metaengine.cpp:269
 #: engines/titanic/metaengine.cpp:330 engines/ultima/ultima8/metaengine.cpp:53
 msgid "Move down"
@@ -6429,7 +6430,7 @@ msgstr "Se d??placer vers le bas"
 #: engines/access/metaengine.cpp:251 engines/asylum/metaengine.cpp:183
 #: engines/asylum/metaengine.cpp:212 engines/cine/metaengine.cpp:391
 #: engines/efh/metaengine.cpp:300 engines/hdb/metaengine.cpp:225
-#: engines/hypno/metaengine.cpp:284 engines/hypno/metaengine.cpp:335
+#: engines/hypno/metaengine.cpp:345 engines/hypno/metaengine.cpp:393
 #: engines/kyra/engine/eobcommon.cpp:373 engines/lure/metaengine.cpp:218
 #: engines/nancy/input.cpp:207 engines/tinsel/metaengine.cpp:275
 #: engines/titanic/metaengine.cpp:312 engines/titanic/metaengine.cpp:390
@@ -6442,7 +6443,7 @@ msgstr "Se d??placer vers la gauche"
 #: engines/access/metaengine.cpp:258 engines/asylum/metaengine.cpp:189
 #: engines/asylum/metaengine.cpp:219 engines/cine/metaengine.cpp:398
 #: engines/efh/metaengine.cpp:307 engines/hdb/metaengine.cpp:231
-#: engines/hypno/metaengine.cpp:296 engines/hypno/metaengine.cpp:349
+#: engines/hypno/metaengine.cpp:357 engines/hypno/metaengine.cpp:405
 #: engines/kyra/engine/eobcommon.cpp:374 engines/lure/metaengine.cpp:226
 #: engines/nancy/input.cpp:213 engines/tinsel/metaengine.cpp:281
 #: engines/titanic/metaengine.cpp:318 engines/titanic/metaengine.cpp:396
@@ -6940,8 +6941,8 @@ msgstr "AGOS principal"
 #: engines/cine/metaengine.cpp:271 engines/cruise/metaengine.cpp:169
 #: engines/dm/metaengine.cpp:126 engines/draci/metaengine.cpp:196
 #: engines/drascula/metaengine.cpp:206 engines/hopkins/metaengine.cpp:207
-#: engines/hugo/metaengine.cpp:130 engines/hypno/metaengine.cpp:166
-#: engines/hypno/metaengine.cpp:267 engines/hypno/metaengine.cpp:311
+#: engines/hugo/metaengine.cpp:130 engines/hypno/metaengine.cpp:225
+#: engines/hypno/metaengine.cpp:324 engines/hypno/metaengine.cpp:372
 #: engines/illusions/metaengine.cpp:90 engines/lab/metaengine.cpp:152
 #: engines/lure/metaengine.cpp:169 engines/made/metaengine.cpp:133
 #: engines/mads/metaengine.cpp:317 engines/neverhood/metaengine.cpp:186
@@ -7645,7 +7646,7 @@ msgstr "Act. / D??sact. combat"
 
 #. I18N: This keymap allows skipping video cutscenes
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
-#: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
+#: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:210
 #: engines/phoenixvr/metaengine.cpp:94 engines/scumm/help.cpp:79
 #: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
@@ -8800,7 +8801,7 @@ msgstr "S??lectionner option 13"
 msgid "Select option 14"
 msgstr "S??lectionner option 14"
 
-#: engines/efh/metaengine.cpp:257 engines/hypno/metaengine.cpp:312
+#: engines/efh/metaengine.cpp:257 engines/hypno/metaengine.cpp:373
 #: engines/sludge/keymapper_tables.h:35 engines/supernova/metaengine.cpp:218
 msgid "Menu keymappings"
 msgstr "Conf. des touches des menus"
@@ -10326,88 +10327,160 @@ msgstr ""
 msgid "Intro keymappings"
 msgstr "Conf. des touches Intro"
 
-#: engines/hypno/metaengine.cpp:134
+#: engines/hypno/metaengine.cpp:134 engines/hypno/metaengine.cpp:144
 msgid "Primary shoot"
 msgstr "Tir primaire"
 
-#: engines/hypno/metaengine.cpp:140
+#: engines/hypno/metaengine.cpp:139
 msgid "Secondary shoot"
 msgstr "Tir secondaire"
 
-#: engines/hypno/metaengine.cpp:146 engines/lab/metaengine.cpp:209
+#: engines/hypno/metaengine.cpp:149
+#, fuzzy
+#| msgid "Map hide toolbar action"
+msgid "Rapid fire / secondary action"
+msgstr "Affecter l'action 'Cacher Barre d'Outils'"
+
+#: engines/hypno/metaengine.cpp:155
+#, fuzzy
+#| msgid "Slide left"
+msgid "Aim left"
+msgstr "Faire un pas vers la gauche"
+
+#: engines/hypno/metaengine.cpp:160
+#, fuzzy
+#| msgid "Stick down"
+msgid "Aim down"
+msgstr "Manche bas"
+
+#: engines/hypno/metaengine.cpp:165
+#, fuzzy
+#| msgid "Up right"
+msgid "Aim right"
+msgstr "Haut droite"
+
+#: engines/hypno/metaengine.cpp:170
+msgid "Aim up"
+msgstr ""
+
+#: engines/hypno/metaengine.cpp:175 engines/scumm/metaengine.cpp:1202
+#: engines/scumm/metaengine.cpp:1279
+msgid "Stick left"
+msgstr "Manche gauche"
+
+#: engines/hypno/metaengine.cpp:182 engines/scumm/metaengine.cpp:1195
+#: engines/scumm/metaengine.cpp:1272
+msgid "Stick down"
+msgstr "Manche bas"
+
+#: engines/hypno/metaengine.cpp:189 engines/scumm/metaengine.cpp:1209
+#: engines/scumm/metaengine.cpp:1286
+msgid "Stick right"
+msgstr "Manche droite"
+
+#: engines/hypno/metaengine.cpp:196 engines/scumm/metaengine.cpp:1188
+#: engines/scumm/metaengine.cpp:1265
+msgid "Stick up"
+msgstr "Manche haut"
+
+#: engines/hypno/metaengine.cpp:203 engines/lab/metaengine.cpp:209
 #: engines/ngi/metaengine.cpp:168 engines/sherlock/metaengine.cpp:872
 #: engines/teenagent/metaengine.cpp:232 engines/toon/metaengine.cpp:98
 #: engines/voyeur/metaengine.cpp:235
 msgid "Skip intro"
 msgstr "Passer l'intro"
 
-#: engines/hypno/metaengine.cpp:167
+#: engines/hypno/metaengine.cpp:226
 msgid "Exit menu keymappings"
 msgstr "Conf. des touches Menu Sortir"
 
-#: engines/hypno/metaengine.cpp:168
+#: engines/hypno/metaengine.cpp:227
 msgid "Difficulty selection menu keymappings"
 msgstr "Conf. des touches Menu S??lection de la difficult??"
 
-#: engines/hypno/metaengine.cpp:169
+#: engines/hypno/metaengine.cpp:228
 msgid "Retry menu keymappings"
 msgstr "Conf. des touches Menu Recommencer"
 
-#: engines/hypno/metaengine.cpp:172 engines/hypno/metaengine.cpp:270
-#: engines/hypno/metaengine.cpp:322
+#: engines/hypno/metaengine.cpp:231 engines/hypno/metaengine.cpp:327
+#: engines/hypno/metaengine.cpp:382
 msgid "Skip level (cheat)"
 msgstr "Passer le niveau (triche)"
 
 #. I18N: (Game name: Soldier Boyz) player refers to the users own character.
 #. I18N: (Game name: Marvel Comics Spider-Man: The Sinister Six) player refers to the users own character.
 #. I18N: (Game name: Wetlands) player refers to the users own character.
-#: engines/hypno/metaengine.cpp:180 engines/hypno/metaengine.cpp:278
-#: engines/hypno/metaengine.cpp:329
+#: engines/hypno/metaengine.cpp:238 engines/hypno/metaengine.cpp:334
+#: engines/hypno/metaengine.cpp:388
 msgid "Kill player"
 msgstr "Tuer le joueur"
 
 #. I18N: (Game name: Soldier Boyz) the game has 3 difficulty levels: Chump, Punk and Badass. Chump is the easy mode.
-#: engines/hypno/metaengine.cpp:205
+#: engines/hypno/metaengine.cpp:262
 msgid "Chump"
 msgstr "Chump"
 
 #. I18N: (Game name: Soldier Boyz) the game has 3 difficulty levels: Chump, Punk and Badass. Punk is the medium mode.
-#: engines/hypno/metaengine.cpp:212
+#: engines/hypno/metaengine.cpp:269
 msgid "Punk"
 msgstr "Punk"
 
 #. I18N: (Game name: Soldier Boyz) the game has 3 difficulty levels: Chump, Punk and Badass. Badass is the hard mode.
-#: engines/hypno/metaengine.cpp:219
+#: engines/hypno/metaengine.cpp:276
 msgid "Badass"
 msgstr "Badass"
 
 #. I18N: (Game name: Soldier Boyz) This makes the player restart from the last checkpoint.
-#: engines/hypno/metaengine.cpp:232
+#: engines/hypno/metaengine.cpp:289
 msgid "Retry sector"
 msgstr "Recommencer le secteur"
 
 #. I18N: (Game name: Soldier Boyz) This makes the player restart the current mission / level.
-#: engines/hypno/metaengine.cpp:239
+#: engines/hypno/metaengine.cpp:296
 msgid "Restart territory"
 msgstr "Red??marrer le territoire"
 
-#: engines/hypno/metaengine.cpp:245
+#: engines/hypno/metaengine.cpp:302
 msgid "Begin new mission"
 msgstr "Commencer une nouvelle mission"
 
-#: engines/hypno/metaengine.cpp:313
+#: engines/hypno/metaengine.cpp:374
 msgid "Pause keymappings"
 msgstr "Conf. des touches Pause"
 
-#: engines/hypno/metaengine.cpp:314
+#: engines/hypno/metaengine.cpp:375
 msgid "Direction keymappings"
 msgstr "Conf. des touches Directions"
 
-#: engines/hypno/metaengine.cpp:316 engines/mads/metaengine.cpp:391
+#: engines/hypno/metaengine.cpp:377 engines/mads/metaengine.cpp:391
 #: engines/tsage/metaengine.cpp:259
 msgid "Show credits"
 msgstr "Afficher les cr??dits"
 
+#: engines/hypno/metaengine.cpp:417
+#, fuzzy
+#| msgid "Move left"
+msgid "Menu left"
+msgstr "Se d??placer vers la gauche"
+
+#: engines/hypno/metaengine.cpp:425
+#, fuzzy
+#| msgid "Move down"
+msgid "Menu down"
+msgstr "Se d??placer vers le bas"
+
+#: engines/hypno/metaengine.cpp:433
+#, fuzzy
+#| msgid "Move right"
+msgid "Menu right"
+msgstr "Se d??placer vers la droite"
+
+#: engines/hypno/metaengine.cpp:441
+#, fuzzy
+#| msgid "Menu"
+msgid "Menu up"
+msgstr "Menu"
+
 #. I18N: Action in In Cold Blood
 #: engines/icb/icb.cpp:107
 msgid "Arm"
@@ -13787,22 +13860,6 @@ msgstr "Vis??e gauche / menu gauche"
 msgid "Aim right / menu right"
 msgstr "Vis??e droite / Menu droite"
 
-#: engines/scumm/metaengine.cpp:1188 engines/scumm/metaengine.cpp:1265
-msgid "Stick up"
-msgstr "Manche haut"
-
-#: engines/scumm/metaengine.cpp:1195 engines/scumm/metaengine.cpp:1272
-msgid "Stick down"
-msgstr "Manche bas"
-
-#: engines/scumm/metaengine.cpp:1202 engines/scumm/metaengine.cpp:1279
-msgid "Stick left"
-msgstr "Manche gauche"
-
-#: engines/scumm/metaengine.cpp:1209 engines/scumm/metaengine.cpp:1286
-msgid "Stick right"
-msgstr "Manche droite"
-
 #: engines/scumm/metaengine.cpp:1216 engines/scumm/metaengine.cpp:1293
 msgid "Fire / select"
 msgstr "Tirer / S??lectionner"
@@ -19173,9 +19230,6 @@ msgstr ""
 #~ "Vous devez affecter une touche ?? l'action de 'Clic Droit' pour pouvoir "
 #~ "jouer ?? ce jeu"
 
-#~ msgid "Map hide toolbar action"
-#~ msgstr "Affecter l'action 'Cacher Barre d'Outils'"
-
 #~ msgid "You must map a key to the 'Hide toolbar' action to play this game"
 #~ msgstr ""
 #~ "Vous devez affecter une touche ?? l'action 'Cacher Barre d'Outils' pour "
diff --git a/po/gl_ES.po b/po/gl_ES.po
index 697849fee0c..7a483b90dbf 100644
--- a/po/gl_ES.po
+++ b/po/gl_ES.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-13 06:14+0000\n"
+"POT-Creation-Date: 2026-06-13 10:20+0000\n"
 "PO-Revision-Date: 2020-09-26 21:13+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Galician <https://translations.scummvm.org/projects/scummvm/"
@@ -139,7 +139,7 @@ msgstr "Arriba"
 #: engines/buried/saveload.cpp:83 engines/cine/saveload.cpp:857
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:123
 #: engines/drascula/saveload.cpp:51 engines/grim/inputdialog.cpp:84
-#: engines/hypno/metaengine.cpp:225 engines/mohawk/myst_stacks/menu.cpp:283
+#: engines/hypno/metaengine.cpp:282 engines/mohawk/myst_stacks/menu.cpp:283
 #: engines/mohawk/myst_stacks/menu.cpp:300
 #: engines/mohawk/myst_stacks/menu.cpp:318
 #: engines/mohawk/riven_stacks/aspit.cpp:349
@@ -348,7 +348,7 @@ msgstr "Hai outro almacenamento activo. Seguro que queres interrompelo?"
 #: engines/chamber/metaengine.cpp:91 engines/director/events.cpp:122
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:139
 #: engines/efh/metaengine.cpp:362 engines/grim/grim.cpp:366
-#: engines/hypno/metaengine.cpp:192 engines/kyra/gui/saveload_eob.cpp:611
+#: engines/hypno/metaengine.cpp:249 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:197
 #: engines/mm/mm1/metaengine.cpp:54 engines/parallaction/metaengine.cpp:169
 #: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
@@ -367,7 +367,7 @@ msgstr "Si"
 #: engines/chamber/metaengine.cpp:97 engines/director/events.cpp:122
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:139
 #: engines/efh/metaengine.cpp:368 engines/grim/grim.cpp:366
-#: engines/hypno/metaengine.cpp:198 engines/kyra/gui/saveload_eob.cpp:611
+#: engines/hypno/metaengine.cpp:255 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:204
 #: engines/mm/mm1/metaengine.cpp:52 engines/parallaction/metaengine.cpp:175
 #: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
@@ -3361,7 +3361,7 @@ msgstr ""
 #: backends/events/default/default-events.cpp:358
 #: engines/adl/metaengine.cpp:352 engines/buried/metaengine.cpp:154
 #: engines/chamber/metaengine.cpp:84 engines/drascula/metaengine.cpp:304
-#: engines/hdb/metaengine.cpp:275 engines/hypno/metaengine.cpp:251
+#: engines/hdb/metaengine.cpp:275 engines/hypno/metaengine.cpp:308
 #: engines/lab/metaengine.cpp:172 engines/mohawk/myst_stacks/menu.cpp:317
 #: engines/mohawk/riven_stacks/aspit.cpp:454
 #: engines/neverhood/metaengine.cpp:200 engines/saga/metaengine.cpp:449
@@ -3932,8 +3932,9 @@ msgstr "Bot??n secundario"
 #: engines/buried/metaengine.cpp:167 engines/crab/input/input.cpp:261
 #: engines/cruise/metaengine.cpp:217 engines/dm/metaengine.cpp:180
 #: engines/grim/grim.cpp:550 engines/hdb/metaengine.cpp:258
-#: engines/hypno/metaengine.cpp:186 engines/hypno/metaengine.cpp:369
-#: engines/icb/icb.cpp:137 engines/icb/icb.cpp:203 engines/mohawk/myst.cpp:595
+#: engines/hypno/metaengine.cpp:243 engines/hypno/metaengine.cpp:339
+#: engines/hypno/metaengine.cpp:456 engines/icb/icb.cpp:137
+#: engines/icb/icb.cpp:203 engines/mohawk/myst.cpp:595
 #: engines/mohawk/riven.cpp:876 engines/ngi/metaengine.cpp:136
 #: engines/sky/metaengine.cpp:126 engines/sludge/keymapper_tables.h:52
 #: engines/sludge/keymapper_tables.h:125 engines/sludge/keymapper_tables.h:187
@@ -4903,7 +4904,7 @@ msgstr ""
 #: backends/platform/sdl/opendingux/opendingux.cpp:57
 #: backends/platform/sdl/ps3/ps3.cpp:50 backends/platform/sdl/psp2/psp2.cpp:51
 #: engines/bagel/metaengine.cpp:64 engines/efh/metaengine.cpp:397
-#: engines/got/metaengine.cpp:76 engines/hypno/metaengine.cpp:363
+#: engines/got/metaengine.cpp:76 engines/hypno/metaengine.cpp:449
 #: engines/mm/mm1/metaengine.cpp:50 engines/mm/mm1/metaengine.cpp:97
 #: engines/sherlock/metaengine.cpp:660 engines/sherlock/metaengine.cpp:672
 #: engines/sherlock/metaengine.cpp:1014 engines/supernova/metaengine.cpp:303
@@ -6085,7 +6086,7 @@ msgstr "Omitir li??a"
 #: engines/access/metaengine.cpp:237 engines/asylum/metaengine.cpp:171
 #: engines/asylum/metaengine.cpp:198 engines/cine/metaengine.cpp:377
 #: engines/efh/metaengine.cpp:286 engines/hdb/metaengine.cpp:213
-#: engines/hypno/metaengine.cpp:302 engines/hypno/metaengine.cpp:356
+#: engines/hypno/metaengine.cpp:363 engines/hypno/metaengine.cpp:411
 #: engines/nancy/input.cpp:195 engines/tinsel/metaengine.cpp:263
 #: engines/titanic/metaengine.cpp:324 engines/ultima/ultima8/metaengine.cpp:52
 #, fuzzy
@@ -6096,7 +6097,7 @@ msgstr "Mover cara atr??s"
 #: engines/access/metaengine.cpp:244 engines/asylum/metaengine.cpp:177
 #: engines/asylum/metaengine.cpp:205 engines/cine/metaengine.cpp:384
 #: engines/efh/metaengine.cpp:293 engines/hdb/metaengine.cpp:219
-#: engines/hypno/metaengine.cpp:290 engines/hypno/metaengine.cpp:342
+#: engines/hypno/metaengine.cpp:351 engines/hypno/metaengine.cpp:399
 #: engines/nancy/input.cpp:201 engines/tinsel/metaengine.cpp:269
 #: engines/titanic/metaengine.cpp:330 engines/ultima/ultima8/metaengine.cpp:53
 #, fuzzy
@@ -6108,7 +6109,7 @@ msgstr "Cancelar descarga"
 #: engines/access/metaengine.cpp:251 engines/asylum/metaengine.cpp:183
 #: engines/asylum/metaengine.cpp:212 engines/cine/metaengine.cpp:391
 #: engines/efh/metaengine.cpp:300 engines/hdb/metaengine.cpp:225
-#: engines/hypno/metaengine.cpp:284 engines/hypno/metaengine.cpp:335
+#: engines/hypno/metaengine.cpp:345 engines/hypno/metaengine.cpp:393
 #: engines/kyra/engine/eobcommon.cpp:373 engines/lure/metaengine.cpp:218
 #: engines/nancy/input.cpp:207 engines/tinsel/metaengine.cpp:275
 #: engines/titanic/metaengine.cpp:312 engines/titanic/metaengine.cpp:390
@@ -6122,7 +6123,7 @@ msgstr "Mover ?? esquerda"
 #: engines/access/metaengine.cpp:258 engines/asylum/metaengine.cpp:189
 #: engines/asylum/metaengine.cpp:219 engines/cine/metaengine.cpp:398
 #: engines/efh/metaengine.cpp:307 engines/hdb/metaengine.cpp:231
-#: engines/hypno/metaengine.cpp:296 engines/hypno/metaengine.cpp:349
+#: engines/hypno/metaengine.cpp:357 engines/hypno/metaengine.cpp:405
 #: engines/kyra/engine/eobcommon.cpp:374 engines/lure/metaengine.cpp:226
 #: engines/nancy/input.cpp:213 engines/tinsel/metaengine.cpp:281
 #: engines/titanic/metaengine.cpp:318 engines/titanic/metaengine.cpp:396
@@ -6617,8 +6618,8 @@ msgstr ""
 #: engines/cine/metaengine.cpp:271 engines/cruise/metaengine.cpp:169
 #: engines/dm/metaengine.cpp:126 engines/draci/metaengine.cpp:196
 #: engines/drascula/metaengine.cpp:206 engines/hopkins/metaengine.cpp:207
-#: engines/hugo/metaengine.cpp:130 engines/hypno/metaengine.cpp:166
-#: engines/hypno/metaengine.cpp:267 engines/hypno/metaengine.cpp:311
+#: engines/hugo/metaengine.cpp:130 engines/hypno/metaengine.cpp:225
+#: engines/hypno/metaengine.cpp:324 engines/hypno/metaengine.cpp:372
 #: engines/illusions/metaengine.cpp:90 engines/lab/metaengine.cpp:152
 #: engines/lure/metaengine.cpp:169 engines/made/metaengine.cpp:133
 #: engines/mads/metaengine.cpp:317 engines/neverhood/metaengine.cpp:186
@@ -7340,7 +7341,7 @@ msgstr "Activar/desactivar captura de rato"
 
 #. I18N: This keymap allows skipping video cutscenes
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
-#: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
+#: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:210
 #: engines/phoenixvr/metaengine.cpp:94 engines/scumm/help.cpp:79
 #: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
@@ -8634,7 +8635,7 @@ msgstr "Acci??n/Seleccionar"
 msgid "Select option 14"
 msgstr "Acci??n/Seleccionar"
 
-#: engines/efh/metaengine.cpp:257 engines/hypno/metaengine.cpp:312
+#: engines/efh/metaengine.cpp:257 engines/hypno/metaengine.cpp:373
 #: engines/sludge/keymapper_tables.h:35 engines/supernova/metaengine.cpp:218
 #, fuzzy
 msgid "Menu keymappings"
@@ -10304,16 +10305,69 @@ msgstr ""
 msgid "Intro keymappings"
 msgstr "~O~pci??ns..."
 
-#: engines/hypno/metaengine.cpp:134
+#: engines/hypno/metaengine.cpp:134 engines/hypno/metaengine.cpp:144
 msgid "Primary shoot"
 msgstr ""
 
-#: engines/hypno/metaengine.cpp:140
+#: engines/hypno/metaengine.cpp:139
 #, fuzzy
 msgid "Secondary shoot"
 msgstr "Rapaz 2"
 
-#: engines/hypno/metaengine.cpp:146 engines/lab/metaengine.cpp:209
+#: engines/hypno/metaengine.cpp:149
+#, fuzzy
+#| msgid "Map hide toolbar action"
+msgid "Rapid fire / secondary action"
+msgstr "Asignar acci??n Ocultar barra de ferramentas"
+
+#: engines/hypno/metaengine.cpp:155
+#, fuzzy
+#| msgid "Slide Left"
+msgid "Aim left"
+msgstr "Esvarar ?? esquerda"
+
+#: engines/hypno/metaengine.cpp:160
+#, fuzzy
+msgid "Aim down"
+msgstr "Baixar lista"
+
+#: engines/hypno/metaengine.cpp:165
+#, fuzzy
+msgid "Aim right"
+msgstr "Dereita"
+
+#: engines/hypno/metaengine.cpp:170
+msgid "Aim up"
+msgstr ""
+
+#: engines/hypno/metaengine.cpp:175 engines/scumm/metaengine.cpp:1202
+#: engines/scumm/metaengine.cpp:1279
+#, fuzzy
+#| msgid "Slide Left"
+msgid "Stick left"
+msgstr "Esvarar ?? esquerda"
+
+#: engines/hypno/metaengine.cpp:182 engines/scumm/metaengine.cpp:1195
+#: engines/scumm/metaengine.cpp:1272
+#, fuzzy
+msgid "Stick down"
+msgstr "Baixar lista"
+
+#: engines/hypno/metaengine.cpp:189 engines/scumm/metaengine.cpp:1209
+#: engines/scumm/metaengine.cpp:1286
+#, fuzzy
+#| msgid "Slide Right"
+msgid "Stick right"
+msgstr "Esvarar ?? dereita"
+
+#: engines/hypno/metaengine.cpp:196 engines/scumm/metaengine.cpp:1188
+#: engines/scumm/metaengine.cpp:1265
+#, fuzzy
+#| msgid "Pick up"
+msgid "Stick up"
+msgstr "Coller"
+
+#: engines/hypno/metaengine.cpp:203 engines/lab/metaengine.cpp:209
 #: engines/ngi/metaengine.cpp:168 engines/sherlock/metaengine.cpp:872
 #: engines/teenagent/metaengine.cpp:232 engines/toon/metaengine.cpp:98
 #: engines/voyeur/metaengine.cpp:235
@@ -10322,23 +10376,23 @@ msgstr "Rapaz 2"
 msgid "Skip intro"
 msgstr "Omitir li??a"
 
-#: engines/hypno/metaengine.cpp:167
+#: engines/hypno/metaengine.cpp:226
 #, fuzzy
 msgid "Exit menu keymappings"
 msgstr "~O~pci??ns..."
 
-#: engines/hypno/metaengine.cpp:168
+#: engines/hypno/metaengine.cpp:227
 #, fuzzy
 msgid "Difficulty selection menu keymappings"
 msgstr "~O~pci??ns..."
 
-#: engines/hypno/metaengine.cpp:169
+#: engines/hypno/metaengine.cpp:228
 #, fuzzy
 msgid "Retry menu keymappings"
 msgstr "~O~pci??ns..."
 
-#: engines/hypno/metaengine.cpp:172 engines/hypno/metaengine.cpp:270
-#: engines/hypno/metaengine.cpp:322
+#: engines/hypno/metaengine.cpp:231 engines/hypno/metaengine.cpp:327
+#: engines/hypno/metaengine.cpp:382
 #, fuzzy
 #| msgid "Speech"
 msgid "Skip level (cheat)"
@@ -10347,58 +10401,79 @@ msgstr "Voz"
 #. I18N: (Game name: Soldier Boyz) player refers to the users own character.
 #. I18N: (Game name: Marvel Comics Spider-Man: The Sinister Six) player refers to the users own character.
 #. I18N: (Game name: Wetlands) player refers to the users own character.
-#: engines/hypno/metaengine.cpp:180 engines/hypno/metaengine.cpp:278
-#: engines/hypno/metaengine.cpp:329
+#: engines/hypno/metaengine.cpp:238 engines/hypno/metaengine.cpp:334
+#: engines/hypno/metaengine.cpp:388
 msgid "Kill player"
 msgstr ""
 
 #. I18N: (Game name: Soldier Boyz) the game has 3 difficulty levels: Chump, Punk and Badass. Chump is the easy mode.
-#: engines/hypno/metaengine.cpp:205
+#: engines/hypno/metaengine.cpp:262
 msgid "Chump"
 msgstr ""
 
 #. I18N: (Game name: Soldier Boyz) the game has 3 difficulty levels: Chump, Punk and Badass. Punk is the medium mode.
-#: engines/hypno/metaengine.cpp:212
+#: engines/hypno/metaengine.cpp:269
 msgid "Punk"
 msgstr ""
 
 #. I18N: (Game name: Soldier Boyz) the game has 3 difficulty levels: Chump, Punk and Badass. Badass is the hard mode.
-#: engines/hypno/metaengine.cpp:219
+#: engines/hypno/metaengine.cpp:276
 msgid "Badass"
 msgstr ""
 
 #. I18N: (Game name: Soldier Boyz) This makes the player restart from the last checkpoint.
-#: engines/hypno/metaengine.cpp:232
+#: engines/hypno/metaengine.cpp:289
 msgid "Retry sector"
 msgstr ""
 
 #. I18N: (Game name: Soldier Boyz) This makes the player restart the current mission / level.
-#: engines/hypno/metaengine.cpp:239
+#: engines/hypno/metaengine.cpp:296
 #, fuzzy
 #| msgid "Transitions:"
 msgid "Restart territory"
 msgstr "Transici??ns:"
 
-#: engines/hypno/metaengine.cpp:245
+#: engines/hypno/metaengine.cpp:302
 msgid "Begin new mission"
 msgstr ""
 
-#: engines/hypno/metaengine.cpp:313
+#: engines/hypno/metaengine.cpp:374
 #, fuzzy
 msgid "Pause keymappings"
 msgstr "~O~pci??ns..."
 
-#: engines/hypno/metaengine.cpp:314
+#: engines/hypno/metaengine.cpp:375
 #, fuzzy
 msgid "Direction keymappings"
 msgstr "~O~pci??ns..."
 
-#: engines/hypno/metaengine.cpp:316 engines/mads/metaengine.cpp:391
+#: engines/hypno/metaengine.cpp:377 engines/mads/metaengine.cpp:391
 #: engines/tsage/metaengine.cpp:259
 #, fuzzy
 msgid "Show credits"
 msgstr "Mostrar cursor do rato"
 
+#: engines/hypno/metaengine.cpp:417
+#, fuzzy
+msgid "Menu left"
+msgstr "Mover ?? esquerda"
+
+#: engines/hypno/metaengine.cpp:425
+#, fuzzy
+msgid "Menu down"
+msgstr "Cancelar descarga"
+
+#: engines/hypno/metaengine.cpp:433
+#, fuzzy
+msgid "Menu right"
+msgstr "Mover ?? dereita"
+
+#: engines/hypno/metaengine.cpp:441
+#, fuzzy
+#| msgid "Menu"
+msgid "Menu up"
+msgstr "Men??"
+
 #. I18N: Action in In Cold Blood
 #: engines/icb/icb.cpp:107
 msgid "Arm"
@@ -13817,29 +13892,6 @@ msgstr ""
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1188 engines/scumm/metaengine.cpp:1265
-#, fuzzy
-#| msgid "Pick up"
-msgid "Stick up"
-msgstr "Coller"
-
-#: engines/scumm/metaengine.cpp:1195 engines/scumm/metaengine.cpp:1272
-#, fuzzy
-msgid "Stick down"
-msgstr "Baixar lista"
-
-#: engines/scumm/metaengine.cpp:1202 engines/scumm/metaengine.cpp:1279
-#, fuzzy
-#| msgid "Slide Left"
-msgid "Stick left"
-msgstr "Esvarar ?? esquerda"
-
-#: engines/scumm/metaengine.cpp:1209 engines/scumm/metaengine.cpp:1286
-#, fuzzy
-#| msgid "Slide Right"
-msgid "Stick right"
-msgstr "Esvarar ?? dereita"
-
 #: engines/scumm/metaengine.cpp:1216 engines/scumm/metaengine.cpp:1293
 #, fuzzy
 msgid "Fire / select"
@@ -18908,9 +18960,6 @@ msgstr ""
 #~ msgstr ""
 #~ "Debes asignar unha tecla ?? acci??n do bot??n secundario do rato para xogar"
 
-#~ msgid "Map hide toolbar action"
-#~ msgstr "Asignar acci??n Ocultar barra de ferramentas"
-
 #~ msgid "You must map a key to the 'Hide toolbar' action to play this game"
 #~ msgstr ""
 #~ "Debes asignar unha tecla ?? acci??n Ocultar barra de ferramentas para xogar"
diff --git a/po/he.po b/po/he.po
index 9a4eef2ee96..cec05fcaeae 100644
--- a/po/he.po
+++ b/po/he.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.1.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-13 06:14+0000\n"
+"POT-Creation-Date: 2026-06-13 10:20+0000\n"
 "PO-Revision-Date: 2025-04-09 20:18+0000\n"
 "Last-Translator: Niv Baehr <bloop93 at gmail.com>\n"
 "Language-Team: Hebrew <https://translations.scummvm.org/projects/scummvm/"
@@ -135,7 +135,7 @@ msgstr "??????????"
 #: engines/buried/saveload.cpp:83 engines/cine/saveload.cpp:857
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:123
 #: engines/drascula/saveload.cpp:51 engines/grim/inputdialog.cpp:84
-#: engines/hypno/metaengine.cpp:225 engines/mohawk/myst_stacks/menu.cpp:283
+#: engines/hypno/metaengine.cpp:282 engines/mohawk/myst_stacks/menu.cpp:283
 #: engines/mohawk/myst_stacks/menu.cpp:300
 #: engines/mohawk/myst_stacks/menu.cpp:318
 #: engines/mohawk/riven_stacks/aspit.cpp:349
@@ -338,7 +338,7 @@ msgstr "?????????? ?????? ???????? ??????. ?????? ???????????? ?????"
 #: engines/chamber/metaengine.cpp:91 engines/director/events.cpp:122
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:139
 #: engines/efh/metaengine.cpp:362 engines/grim/grim.cpp:366
-#: engines/hypno/metaengine.cpp:192 engines/kyra/gui/saveload_eob.cpp:611
+#: engines/hypno/metaengine.cpp:249 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:197
 #: engines/mm/mm1/metaengine.cpp:54 engines/parallaction/metaengine.cpp:169
 #: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
@@ -357,7 +357,7 @@ msgstr "????"
 #: engines/chamber/metaengine.cpp:97 engines/director/events.cpp:122
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:139
 #: engines/efh/metaengine.cpp:368 engines/grim/grim.cpp:366
-#: engines/hypno/metaengine.cpp:198 engines/kyra/gui/saveload_eob.cpp:611
+#: engines/hypno/metaengine.cpp:255 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:204
 #: engines/mm/mm1/metaengine.cpp:52 engines/parallaction/metaengine.cpp:175
 #: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
@@ -3300,7 +3300,7 @@ msgstr "???????? ???????? ???????????? ????????"
 #: backends/events/default/default-events.cpp:358
 #: engines/adl/metaengine.cpp:352 engines/buried/metaengine.cpp:154
 #: engines/chamber/metaengine.cpp:84 engines/drascula/metaengine.cpp:304
-#: engines/hdb/metaengine.cpp:275 engines/hypno/metaengine.cpp:251
+#: engines/hdb/metaengine.cpp:275 engines/hypno/metaengine.cpp:308
 #: engines/lab/metaengine.cpp:172 engines/mohawk/myst_stacks/menu.cpp:317
 #: engines/mohawk/riven_stacks/aspit.cpp:454
 #: engines/neverhood/metaengine.cpp:200 engines/saga/metaengine.cpp:449
@@ -3881,8 +3881,9 @@ msgstr "?????? ????????"
 #: engines/buried/metaengine.cpp:167 engines/crab/input/input.cpp:261
 #: engines/cruise/metaengine.cpp:217 engines/dm/metaengine.cpp:180
 #: engines/grim/grim.cpp:550 engines/hdb/metaengine.cpp:258
-#: engines/hypno/metaengine.cpp:186 engines/hypno/metaengine.cpp:369
-#: engines/icb/icb.cpp:137 engines/icb/icb.cpp:203 engines/mohawk/myst.cpp:595
+#: engines/hypno/metaengine.cpp:243 engines/hypno/metaengine.cpp:339
+#: engines/hypno/metaengine.cpp:456 engines/icb/icb.cpp:137
+#: engines/icb/icb.cpp:203 engines/mohawk/myst.cpp:595
 #: engines/mohawk/riven.cpp:876 engines/ngi/metaengine.cpp:136
 #: engines/sky/metaengine.cpp:126 engines/sludge/keymapper_tables.h:52
 #: engines/sludge/keymapper_tables.h:125 engines/sludge/keymapper_tables.h:187
@@ -4816,7 +4817,7 @@ msgstr "Y"
 #: backends/platform/sdl/opendingux/opendingux.cpp:57
 #: backends/platform/sdl/ps3/ps3.cpp:50 backends/platform/sdl/psp2/psp2.cpp:51
 #: engines/bagel/metaengine.cpp:64 engines/efh/metaengine.cpp:397
-#: engines/got/metaengine.cpp:76 engines/hypno/metaengine.cpp:363
+#: engines/got/metaengine.cpp:76 engines/hypno/metaengine.cpp:449
 #: engines/mm/mm1/metaengine.cpp:50 engines/mm/mm1/metaengine.cpp:97
 #: engines/sherlock/metaengine.cpp:660 engines/sherlock/metaengine.cpp:672
 #: engines/sherlock/metaengine.cpp:1014 engines/supernova/metaengine.cpp:303
@@ -6044,7 +6045,7 @@ msgstr "?????????? ???? ????????"
 #: engines/access/metaengine.cpp:237 engines/asylum/metaengine.cpp:171
 #: engines/asylum/metaengine.cpp:198 engines/cine/metaengine.cpp:377
 #: engines/efh/metaengine.cpp:286 engines/hdb/metaengine.cpp:213
-#: engines/hypno/metaengine.cpp:302 engines/hypno/metaengine.cpp:356
+#: engines/hypno/metaengine.cpp:363 engines/hypno/metaengine.cpp:411
 #: engines/nancy/input.cpp:195 engines/tinsel/metaengine.cpp:263
 #: engines/titanic/metaengine.cpp:324 engines/ultima/ultima8/metaengine.cpp:52
 msgid "Move up"
@@ -6054,7 +6055,7 @@ msgstr "?????????? ????????"
 #: engines/access/metaengine.cpp:244 engines/asylum/metaengine.cpp:177
 #: engines/asylum/metaengine.cpp:205 engines/cine/metaengine.cpp:384
 #: engines/efh/metaengine.cpp:293 engines/hdb/metaengine.cpp:219
-#: engines/hypno/metaengine.cpp:290 engines/hypno/metaengine.cpp:342
+#: engines/hypno/metaengine.cpp:351 engines/hypno/metaengine.cpp:399
 #: engines/nancy/input.cpp:201 engines/tinsel/metaengine.cpp:269
 #: engines/titanic/metaengine.cpp:330 engines/ultima/ultima8/metaengine.cpp:53
 msgid "Move down"
@@ -6065,7 +6066,7 @@ msgstr "?????????? ??????"
 #: engines/access/metaengine.cpp:251 engines/asylum/metaengine.cpp:183
 #: engines/asylum/metaengine.cpp:212 engines/cine/metaengine.cpp:391
 #: engines/efh/metaengine.cpp:300 engines/hdb/metaengine.cpp:225
-#: engines/hypno/metaengine.cpp:284 engines/hypno/metaengine.cpp:335
+#: engines/hypno/metaengine.cpp:345 engines/hypno/metaengine.cpp:393
 #: engines/kyra/engine/eobcommon.cpp:373 engines/lure/metaengine.cpp:218
 #: engines/nancy/input.cpp:207 engines/tinsel/metaengine.cpp:275
 #: engines/titanic/metaengine.cpp:312 engines/titanic/metaengine.cpp:390
@@ -6078,7 +6079,7 @@ msgstr "?????????? ??????????"
 #: engines/access/metaengine.cpp:258 engines/asylum/metaengine.cpp:189
 #: engines/asylum/metaengine.cpp:219 engines/cine/metaengine.cpp:398
 #: engines/efh/metaengine.cpp:307 engines/hdb/metaengine.cpp:231
-#: engines/hypno/metaengine.cpp:296 engines/hypno/metaengine.cpp:349
+#: engines/hypno/metaengine.cpp:357 engines/hypno/metaengine.cpp:405
 #: engines/kyra/engine/eobcommon.cpp:374 engines/lure/metaengine.cpp:226
 #: engines/nancy/input.cpp:213 engines/tinsel/metaengine.cpp:281
 #: engines/titanic/metaengine.cpp:318 engines/titanic/metaengine.cpp:396
@@ -6573,8 +6574,8 @@ msgstr ""
 #: engines/cine/metaengine.cpp:271 engines/cruise/metaengine.cpp:169
 #: engines/dm/metaengine.cpp:126 engines/draci/metaengine.cpp:196
 #: engines/drascula/metaengine.cpp:206 engines/hopkins/metaengine.cpp:207
-#: engines/hugo/metaengine.cpp:130 engines/hypno/metaengine.cpp:166
-#: engines/hypno/metaengine.cpp:267 engines/hypno/metaengine.cpp:311
+#: engines/hugo/metaengine.cpp:130 engines/hypno/metaengine.cpp:225
+#: engines/hypno/metaengine.cpp:324 engines/hypno/metaengine.cpp:372
 #: engines/illusions/metaengine.cpp:90 engines/lab/metaengine.cpp:152
 #: engines/lure/metaengine.cpp:169 engines/made/metaengine.cpp:133
 #: engines/mads/metaengine.cpp:317 engines/neverhood/metaengine.cpp:186
@@ -7323,7 +7324,7 @@ msgstr "?????????? ??????????"
 
 #. I18N: This keymap allows skipping video cutscenes
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
-#: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
+#: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:210
 #: engines/phoenixvr/metaengine.cpp:94 engines/scumm/help.cpp:79
 #: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
@@ -8673,7 +8674,7 @@ msgstr "???? ?????????? ?????????? ??????"
 msgid "Select option 14"
 msgstr "???? ?????????? ?????????? ??????"
 
-#: engines/efh/metaengine.cpp:257 engines/hypno/metaengine.cpp:312
+#: engines/efh/metaengine.cpp:257 engines/hypno/metaengine.cpp:373
 #: engines/sludge/keymapper_tables.h:35 engines/supernova/metaengine.cpp:218
 #, fuzzy
 #| msgid "Game Options"
@@ -10415,17 +10416,72 @@ msgstr "?????????? ???????? ???????? ?????? ???????????? ???????????? ??????????
 msgid "Intro keymappings"
 msgstr "??~??~???????????? ????????"
 
-#: engines/hypno/metaengine.cpp:134
+#: engines/hypno/metaengine.cpp:134 engines/hypno/metaengine.cpp:144
 msgid "Primary shoot"
 msgstr ""
 
-#: engines/hypno/metaengine.cpp:140
+#: engines/hypno/metaengine.cpp:139
 #, fuzzy
 #| msgid "Seconds"
 msgid "Secondary shoot"
 msgstr "??????????"
 
-#: engines/hypno/metaengine.cpp:146 engines/lab/metaengine.cpp:209
+#: engines/hypno/metaengine.cpp:149
+#, fuzzy
+#| msgid "Map hide toolbar action"
+msgid "Rapid fire / secondary action"
+msgstr "?????????? ???????? ?????????? ????????"
+
+#: engines/hypno/metaengine.cpp:155
+#, fuzzy
+#| msgid "Slide Left"
+msgid "Aim left"
+msgstr "???????? ??????????"
+
+#: engines/hypno/metaengine.cpp:160
+#, fuzzy
+#| msgid "Scroll down"
+msgid "Aim down"
+msgstr "?????????? ??????"
+
+#: engines/hypno/metaengine.cpp:165
+#, fuzzy
+msgid "Aim right"
+msgstr "??????????"
+
+#: engines/hypno/metaengine.cpp:170
+msgid "Aim up"
+msgstr ""
+
+#: engines/hypno/metaengine.cpp:175 engines/scumm/metaengine.cpp:1202
+#: engines/scumm/metaengine.cpp:1279
+#, fuzzy
+#| msgid "Slide Left"
+msgid "Stick left"
+msgstr "???????? ??????????"
+
+#: engines/hypno/metaengine.cpp:182 engines/scumm/metaengine.cpp:1195
+#: engines/scumm/metaengine.cpp:1272
+#, fuzzy
+#| msgid "Scroll down"
+msgid "Stick down"
+msgstr "?????????? ??????"
+
+#: engines/hypno/metaengine.cpp:189 engines/scumm/metaengine.cpp:1209
+#: engines/scumm/metaengine.cpp:1286
+#, fuzzy
+#| msgid "Slide Right"
+msgid "Stick right"
+msgstr "???????? ??????????"
+
+#: engines/hypno/metaengine.cpp:196 engines/scumm/metaengine.cpp:1188
+#: engines/scumm/metaengine.cpp:1265
+#, fuzzy
+#| msgid "Pick up"
+msgid "Stick up"
+msgstr "??????????"
+
+#: engines/hypno/metaengine.cpp:203 engines/lab/metaengine.cpp:209
 #: engines/ngi/metaengine.cpp:168 engines/sherlock/metaengine.cpp:872
 #: engines/teenagent/metaengine.cpp:232 engines/toon/metaengine.cpp:98
 #: engines/voyeur/metaengine.cpp:235
@@ -10434,26 +10490,26 @@ msgstr "??????????"
 msgid "Skip intro"
 msgstr "?????????? ???? ????????"
 
-#: engines/hypno/metaengine.cpp:167
+#: engines/hypno/metaengine.cpp:226
 #, fuzzy
 #| msgid "Game Options"
 msgid "Exit menu keymappings"
 msgstr "??~??~???????????? ????????"
 
-#: engines/hypno/metaengine.cpp:168
+#: engines/hypno/metaengine.cpp:227
 #, fuzzy
 #| msgid "Game Options"
 msgid "Difficulty selection menu keymappings"
 msgstr "??~??~???????????? ????????"
 
-#: engines/hypno/metaengine.cpp:169
+#: engines/hypno/metaengine.cpp:228
 #, fuzzy
 #| msgid "Game Options"
 msgid "Retry menu keymappings"
 msgstr "??~??~???????????? ????????"
 
-#: engines/hypno/metaengine.cpp:172 engines/hypno/metaengine.cpp:270
-#: engines/hypno/metaengine.cpp:322
+#: engines/hypno/metaengine.cpp:231 engines/hypno/metaengine.cpp:327
+#: engines/hypno/metaengine.cpp:382
 #, fuzzy
 #| msgid "Speech"
 msgid "Skip level (cheat)"
@@ -10462,59 +10518,83 @@ msgstr "??????????"
 #. I18N: (Game name: Soldier Boyz) player refers to the users own character.
 #. I18N: (Game name: Marvel Comics Spider-Man: The Sinister Six) player refers to the users own character.
 #. I18N: (Game name: Wetlands) player refers to the users own character.
-#: engines/hypno/metaengine.cpp:180 engines/hypno/metaengine.cpp:278
-#: engines/hypno/metaengine.cpp:329
+#: engines/hypno/metaengine.cpp:238 engines/hypno/metaengine.cpp:334
+#: engines/hypno/metaengine.cpp:388
 msgid "Kill player"
 msgstr ""
 
 #. I18N: (Game name: Soldier Boyz) the game has 3 difficulty levels: Chump, Punk and Badass. Chump is the easy mode.
-#: engines/hypno/metaengine.cpp:205
+#: engines/hypno/metaengine.cpp:262
 msgid "Chump"
 msgstr ""
 
 #. I18N: (Game name: Soldier Boyz) the game has 3 difficulty levels: Chump, Punk and Badass. Punk is the medium mode.
-#: engines/hypno/metaengine.cpp:212
+#: engines/hypno/metaengine.cpp:269
 msgid "Punk"
 msgstr ""
 
 #. I18N: (Game name: Soldier Boyz) the game has 3 difficulty levels: Chump, Punk and Badass. Badass is the hard mode.
-#: engines/hypno/metaengine.cpp:219
+#: engines/hypno/metaengine.cpp:276
 msgid "Badass"
 msgstr ""
 
 #. I18N: (Game name: Soldier Boyz) This makes the player restart from the last checkpoint.
-#: engines/hypno/metaengine.cpp:232
+#: engines/hypno/metaengine.cpp:289
 msgid "Retry sector"
 msgstr ""
 
 #. I18N: (Game name: Soldier Boyz) This makes the player restart the current mission / level.
-#: engines/hypno/metaengine.cpp:239
+#: engines/hypno/metaengine.cpp:296
 #, fuzzy
 #| msgid "Faster animations"
 msgid "Restart territory"
 msgstr "???????????? ????????????"
 
-#: engines/hypno/metaengine.cpp:245
+#: engines/hypno/metaengine.cpp:302
 msgid "Begin new mission"
 msgstr ""
 
-#: engines/hypno/metaengine.cpp:313
+#: engines/hypno/metaengine.cpp:374
 #, fuzzy
 #| msgid "Game Options"
 msgid "Pause keymappings"
 msgstr "??~??~???????????? ????????"
 
-#: engines/hypno/metaengine.cpp:314
+#: engines/hypno/metaengine.cpp:375
 #, fuzzy
 #| msgid "Game Options"
 msgid "Direction keymappings"
 msgstr "??~??~???????????? ????????"
 
-#: engines/hypno/metaengine.cpp:316 engines/mads/metaengine.cpp:391
+#: engines/hypno/metaengine.cpp:377 engines/mads/metaengine.cpp:391
 #: engines/tsage/metaengine.cpp:259
 msgid "Show credits"
 msgstr "???????? ?????????????? ????????"
 
+#: engines/hypno/metaengine.cpp:417
+#, fuzzy
+#| msgid "Move left"
+msgid "Menu left"
+msgstr "?????????? ??????????"
+
+#: engines/hypno/metaengine.cpp:425
+#, fuzzy
+#| msgid "Move down"
+msgid "Menu down"
+msgstr "?????????? ??????"
+
+#: engines/hypno/metaengine.cpp:433
+#, fuzzy
+#| msgid "Move right"
+msgid "Menu right"
+msgstr "?????????? ??????????"
+
+#: engines/hypno/metaengine.cpp:441
+#, fuzzy
+#| msgid "Menu"
+msgid "Menu up"
+msgstr "??????????"
+
 #. I18N: Action in In Cold Blood
 #: engines/icb/icb.cpp:107
 msgid "Arm"
@@ -14040,30 +14120,6 @@ msgstr ""
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1188 engines/scumm/metaengine.cpp:1265
-#, fuzzy
-#| msgid "Pick up"
-msgid "Stick up"
-msgstr "??????????"
-
-#: engines/scumm/metaengine.cpp:1195 engines/scumm/metaengine.cpp:1272
-#, fuzzy
-#| msgid "Scroll down"
-msgid "Stick down"
-msgstr "?????????? ??????"
-
-#: engines/scumm/metaengine.cpp:1202 engines/scumm/metaengine.cpp:1279
-#, fuzzy
-#| msgid "Slide Left"
-msgid "Stick left"
-msgstr "???????? ??????????"
-
-#: engines/scumm/metaengine.cpp:1209 engines/scumm/metaengine.cpp:1286
-#, fuzzy
-#| msgid "Slide Right"
-msgid "Stick right"
-msgstr "???????? ??????????"
-
 #: engines/scumm/metaengine.cpp:1216 engines/scumm/metaengine.cpp:1293
 #, fuzzy
 #| msgid "Move left"
@@ -19625,9 +19681,6 @@ msgstr ""
 #~ msgid "You must map a key to the 'Right Click' action to play this game"
 #~ msgstr "???????? ?????????? ???????? ???????????? '?????? ?????????? ??????????' ?????? ???????? ???? ?????????? ??????"
 
-#~ msgid "Map hide toolbar action"
-#~ msgstr "?????????? ???????? ?????????? ????????"
-
 #~ msgid "You must map a key to the 'Hide toolbar' action to play this game"
 #~ msgstr "???????? ?????????? ???????? ???????????? '???????? ???? ???????? ??????????' ?????? ???????? ???? ?????????? ??????"
 
diff --git a/po/hi.po b/po/hi.po
index 67e370a178f..c04b1e8320b 100644
--- a/po/hi.po
+++ b/po/hi.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-13 06:14+0000\n"
+"POT-Creation-Date: 2026-06-13 10:20+0000\n"
 "PO-Revision-Date: 2026-05-12 22:22+0000\n"
 "Last-Translator: KARTIK SAXENA <kartiksaxena2005 at gmail.com>\n"
 "Language-Team: Hindi <https://translations.scummvm.org/projects/scummvm/"
@@ -133,7 +133,7 @@ msgstr "????????? ????????????"
 #: engines/buried/saveload.cpp:83 engines/cine/saveload.cpp:857
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:123
 #: engines/drascula/saveload.cpp:51 engines/grim/inputdialog.cpp:84
-#: engines/hypno/metaengine.cpp:225 engines/mohawk/myst_stacks/menu.cpp:283
+#: engines/hypno/metaengine.cpp:282 engines/mohawk/myst_stacks/menu.cpp:283
 #: engines/mohawk/myst_stacks/menu.cpp:300
 #: engines/mohawk/myst_stacks/menu.cpp:318
 #: engines/mohawk/riven_stacks/aspit.cpp:349
@@ -336,7 +336,7 @@ msgstr "?????? ???????????? ????????????????????? ????????? ????????? ?????? ???
 #: engines/chamber/metaengine.cpp:91 engines/director/events.cpp:122
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:139
 #: engines/efh/metaengine.cpp:362 engines/grim/grim.cpp:366
-#: engines/hypno/metaengine.cpp:192 engines/kyra/gui/saveload_eob.cpp:611
+#: engines/hypno/metaengine.cpp:249 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:197
 #: engines/mm/mm1/metaengine.cpp:54 engines/parallaction/metaengine.cpp:169
 #: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
@@ -355,7 +355,7 @@ msgstr "?????????"
 #: engines/chamber/metaengine.cpp:97 engines/director/events.cpp:122
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:139
 #: engines/efh/metaengine.cpp:368 engines/grim/grim.cpp:366
-#: engines/hypno/metaengine.cpp:198 engines/kyra/gui/saveload_eob.cpp:611
+#: engines/hypno/metaengine.cpp:255 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:204
 #: engines/mm/mm1/metaengine.cpp:52 engines/parallaction/metaengine.cpp:175
 #: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
@@ -3274,7 +3274,7 @@ msgstr ""
 #: backends/events/default/default-events.cpp:358
 #: engines/adl/metaengine.cpp:352 engines/buried/metaengine.cpp:154
 #: engines/chamber/metaengine.cpp:84 engines/drascula/metaengine.cpp:304
-#: engines/hdb/metaengine.cpp:275 engines/hypno/metaengine.cpp:251
+#: engines/hdb/metaengine.cpp:275 engines/hypno/metaengine.cpp:308
 #: engines/lab/metaengine.cpp:172 engines/mohawk/myst_stacks/menu.cpp:317
 #: engines/mohawk/riven_stacks/aspit.cpp:454
 #: engines/neverhood/metaengine.cpp:200 engines/saga/metaengine.cpp:449
@@ -3810,8 +3810,9 @@ msgstr "?????????????????? ???????????????"
 #: engines/buried/metaengine.cpp:167 engines/crab/input/input.cpp:261
 #: engines/cruise/metaengine.cpp:217 engines/dm/metaengine.cpp:180
 #: engines/grim/grim.cpp:550 engines/hdb/metaengine.cpp:258
-#: engines/hypno/metaengine.cpp:186 engines/hypno/metaengine.cpp:369
-#: engines/icb/icb.cpp:137 engines/icb/icb.cpp:203 engines/mohawk/myst.cpp:595
+#: engines/hypno/metaengine.cpp:243 engines/hypno/metaengine.cpp:339
+#: engines/hypno/metaengine.cpp:456 engines/icb/icb.cpp:137
+#: engines/icb/icb.cpp:203 engines/mohawk/myst.cpp:595
 #: engines/mohawk/riven.cpp:876 engines/ngi/metaengine.cpp:136
 #: engines/sky/metaengine.cpp:126 engines/sludge/keymapper_tables.h:52
 #: engines/sludge/keymapper_tables.h:125 engines/sludge/keymapper_tables.h:187
@@ -4715,7 +4716,7 @@ msgstr ""
 #: backends/platform/sdl/opendingux/opendingux.cpp:57
 #: backends/platform/sdl/ps3/ps3.cpp:50 backends/platform/sdl/psp2/psp2.cpp:51
 #: engines/bagel/metaengine.cpp:64 engines/efh/metaengine.cpp:397
-#: engines/got/metaengine.cpp:76 engines/hypno/metaengine.cpp:363
+#: engines/got/metaengine.cpp:76 engines/hypno/metaengine.cpp:449
 #: engines/mm/mm1/metaengine.cpp:50 engines/mm/mm1/metaengine.cpp:97
 #: engines/sherlock/metaengine.cpp:660 engines/sherlock/metaengine.cpp:672
 #: engines/sherlock/metaengine.cpp:1014 engines/supernova/metaengine.cpp:303
@@ -5871,7 +5872,7 @@ msgstr "?????????????????? ??????????????? ????????????"
 #: engines/access/metaengine.cpp:237 engines/asylum/metaengine.cpp:171
 #: engines/asylum/metaengine.cpp:198 engines/cine/metaengine.cpp:377
 #: engines/efh/metaengine.cpp:286 engines/hdb/metaengine.cpp:213
-#: engines/hypno/metaengine.cpp:302 engines/hypno/metaengine.cpp:356
+#: engines/hypno/metaengine.cpp:363 engines/hypno/metaengine.cpp:411
 #: engines/nancy/input.cpp:195 engines/tinsel/metaengine.cpp:263
 #: engines/titanic/metaengine.cpp:324 engines/ultima/ultima8/metaengine.cpp:52
 msgid "Move up"
@@ -5881,7 +5882,7 @@ msgstr "????????? ?????? ????????????"
 #: engines/access/metaengine.cpp:244 engines/asylum/metaengine.cpp:177
 #: engines/asylum/metaengine.cpp:205 engines/cine/metaengine.cpp:384
 #: engines/efh/metaengine.cpp:293 engines/hdb/metaengine.cpp:219
-#: engines/hypno/metaengine.cpp:290 engines/hypno/metaengine.cpp:342
+#: engines/hypno/metaengine.cpp:351 engines/hypno/metaengine.cpp:399
 #: engines/nancy/input.cpp:201 engines/tinsel/metaengine.cpp:269
 #: engines/titanic/metaengine.cpp:330 engines/ultima/ultima8/metaengine.cpp:53
 msgid "Move down"
@@ -5892,7 +5893,7 @@ msgstr "???????????? ?????? ????????????"
 #: engines/access/metaengine.cpp:251 engines/asylum/metaengine.cpp:183
 #: engines/asylum/metaengine.cpp:212 engines/cine/metaengine.cpp:391
 #: engines/efh/metaengine.cpp:300 engines/hdb/metaengine.cpp:225
-#: engines/hypno/metaengine.cpp:284 engines/hypno/metaengine.cpp:335
+#: engines/hypno/metaengine.cpp:345 engines/hypno/metaengine.cpp:393
 #: engines/kyra/engine/eobcommon.cpp:373 engines/lure/metaengine.cpp:218
 #: engines/nancy/input.cpp:207 engines/tinsel/metaengine.cpp:275
 #: engines/titanic/metaengine.cpp:312 engines/titanic/metaengine.cpp:390
@@ -5905,7 +5906,7 @@ msgstr ""
 #: engines/access/metaengine.cpp:258 engines/asylum/metaengine.cpp:189
 #: engines/asylum/metaengine.cpp:219 engines/cine/metaengine.cpp:398
 #: engines/efh/metaengine.cpp:307 engines/hdb/metaengine.cpp:231
-#: engines/hypno/metaengine.cpp:296 engines/hypno/metaengine.cpp:349
+#: engines/hypno/metaengine.cpp:357 engines/hypno/metaengine.cpp:405
 #: engines/kyra/engine/eobcommon.cpp:374 engines/lure/metaengine.cpp:226
 #: engines/nancy/input.cpp:213 engines/tinsel/metaengine.cpp:281
 #: engines/titanic/metaengine.cpp:318 engines/titanic/metaengine.cpp:396
@@ -6381,8 +6382,8 @@ msgstr ""
 #: engines/cine/metaengine.cpp:271 engines/cruise/metaengine.cpp:169
 #: engines/dm/metaengine.cpp:126 engines/draci/metaengine.cpp:196
 #: engines/drascula/metaengine.cpp:206 engines/hopkins/metaengine.cpp:207
-#: engines/hugo/metaengine.cpp:130 engines/hypno/metaengine.cpp:166
-#: engines/hypno/metaengine.cpp:267 engines/hypno/metaengine.cpp:311
+#: engines/hugo/metaengine.cpp:130 engines/hypno/metaengine.cpp:225
+#: engines/hypno/metaengine.cpp:324 engines/hypno/metaengine.cpp:372
 #: engines/illusions/metaengine.cpp:90 engines/lab/metaengine.cpp:152
 #: engines/lure/metaengine.cpp:169 engines/made/metaengine.cpp:133
 #: engines/mads/metaengine.cpp:317 engines/neverhood/metaengine.cpp:186
@@ -7057,7 +7058,7 @@ msgstr "???????????????????????? ?????????"
 
 #. I18N: This keymap allows skipping video cutscenes
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
-#: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
+#: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:210
 #: engines/phoenixvr/metaengine.cpp:94 engines/scumm/help.cpp:79
 #: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
@@ -8312,7 +8313,7 @@ msgstr "Shader ???????????????"
 msgid "Select option 14"
 msgstr "Shader ???????????????"
 
-#: engines/efh/metaengine.cpp:257 engines/hypno/metaengine.cpp:312
+#: engines/efh/metaengine.cpp:257 engines/hypno/metaengine.cpp:373
 #: engines/sludge/keymapper_tables.h:35 engines/supernova/metaengine.cpp:218
 #, fuzzy
 #| msgid "Game Options"
@@ -9929,15 +9930,65 @@ msgstr ""
 msgid "Intro keymappings"
 msgstr "????????? ??????????????????(O)..."
 
-#: engines/hypno/metaengine.cpp:134
+#: engines/hypno/metaengine.cpp:134 engines/hypno/metaengine.cpp:144
 msgid "Primary shoot"
 msgstr ""
 
-#: engines/hypno/metaengine.cpp:140
+#: engines/hypno/metaengine.cpp:139
 msgid "Secondary shoot"
 msgstr ""
 
-#: engines/hypno/metaengine.cpp:146 engines/lab/metaengine.cpp:209
+#: engines/hypno/metaengine.cpp:149
+msgid "Rapid fire / secondary action"
+msgstr ""
+
+#: engines/hypno/metaengine.cpp:155
+#, fuzzy
+#| msgid "Slide Right"
+msgid "Aim left"
+msgstr "?????????????????? ?????????????????? ?????????"
+
+#: engines/hypno/metaengine.cpp:160
+#, fuzzy
+msgid "Aim down"
+msgstr "???????????? ????????????"
+
+#: engines/hypno/metaengine.cpp:165
+#, fuzzy
+#| msgid "Up Right"
+msgid "Aim right"
+msgstr "????????????"
+
+#: engines/hypno/metaengine.cpp:170
+msgid "Aim up"
+msgstr ""
+
+#: engines/hypno/metaengine.cpp:175 engines/scumm/metaengine.cpp:1202
+#: engines/scumm/metaengine.cpp:1279
+#, fuzzy
+#| msgid "Slide Right"
+msgid "Stick left"
+msgstr "?????????????????? ?????????????????? ?????????"
+
+#: engines/hypno/metaengine.cpp:182 engines/scumm/metaengine.cpp:1195
+#: engines/scumm/metaengine.cpp:1272
+#, fuzzy
+msgid "Stick down"
+msgstr "???????????? ????????????"
+
+#: engines/hypno/metaengine.cpp:189 engines/scumm/metaengine.cpp:1209
+#: engines/scumm/metaengine.cpp:1286
+#, fuzzy
+#| msgid "Slide Right"
+msgid "Stick right"
+msgstr "?????????????????? ?????????????????? ?????????"
+
+#: engines/hypno/metaengine.cpp:196 engines/scumm/metaengine.cpp:1188
+#: engines/scumm/metaengine.cpp:1265
+msgid "Stick up"
+msgstr ""
+
+#: engines/hypno/metaengine.cpp:203 engines/lab/metaengine.cpp:209
 #: engines/ngi/metaengine.cpp:168 engines/sherlock/metaengine.cpp:872
 #: engines/teenagent/metaengine.cpp:232 engines/toon/metaengine.cpp:98
 #: engines/voyeur/metaengine.cpp:235
@@ -9945,86 +9996,109 @@ msgstr ""
 msgid "Skip intro"
 msgstr "???????????? ????????? files ??????????????????"
 
-#: engines/hypno/metaengine.cpp:167
+#: engines/hypno/metaengine.cpp:226
 #, fuzzy
 #| msgid "Game Options"
 msgid "Exit menu keymappings"
 msgstr "????????? ??????????????????(O)..."
 
-#: engines/hypno/metaengine.cpp:168
+#: engines/hypno/metaengine.cpp:227
 #, fuzzy
 #| msgid "Game Options"
 msgid "Difficulty selection menu keymappings"
 msgstr "????????? ??????????????????(O)..."
 
-#: engines/hypno/metaengine.cpp:169
+#: engines/hypno/metaengine.cpp:228
 #, fuzzy
 #| msgid "Game Options"
 msgid "Retry menu keymappings"
 msgstr "????????? ??????????????????(O)..."
 
-#: engines/hypno/metaengine.cpp:172 engines/hypno/metaengine.cpp:270
-#: engines/hypno/metaengine.cpp:322
+#: engines/hypno/metaengine.cpp:231 engines/hypno/metaengine.cpp:327
+#: engines/hypno/metaengine.cpp:382
 msgid "Skip level (cheat)"
 msgstr ""
 
 #. I18N: (Game name: Soldier Boyz) player refers to the users own character.
 #. I18N: (Game name: Marvel Comics Spider-Man: The Sinister Six) player refers to the users own character.
 #. I18N: (Game name: Wetlands) player refers to the users own character.
-#: engines/hypno/metaengine.cpp:180 engines/hypno/metaengine.cpp:278
-#: engines/hypno/metaengine.cpp:329
+#: engines/hypno/metaengine.cpp:238 engines/hypno/metaengine.cpp:334
+#: engines/hypno/metaengine.cpp:388
 msgid "Kill player"
 msgstr ""
 
 #. I18N: (Game name: Soldier Boyz) the game has 3 difficulty levels: Chump, Punk and Badass. Chump is the easy mode.
-#: engines/hypno/metaengine.cpp:205
+#: engines/hypno/metaengine.cpp:262
 msgid "Chump"
 msgstr ""
 
 #. I18N: (Game name: Soldier Boyz) the game has 3 difficulty levels: Chump, Punk and Badass. Punk is the medium mode.
-#: engines/hypno/metaengine.cpp:212
+#: engines/hypno/metaengine.cpp:269
 msgid "Punk"
 msgstr ""
 
 #. I18N: (Game name: Soldier Boyz) the game has 3 difficulty levels: Chump, Punk and Badass. Badass is the hard mode.
-#: engines/hypno/metaengine.cpp:219
+#: engines/hypno/metaengine.cpp:276
 msgid "Badass"
 msgstr ""
 
 #. I18N: (Game name: Soldier Boyz) This makes the player restart from the last checkpoint.
-#: engines/hypno/metaengine.cpp:232
+#: engines/hypno/metaengine.cpp:289
 msgid "Retry sector"
 msgstr ""
 
 #. I18N: (Game name: Soldier Boyz) This makes the player restart the current mission / level.
-#: engines/hypno/metaengine.cpp:239
+#: engines/hypno/metaengine.cpp:296
 #, fuzzy
 #| msgid "Saved games"
 msgid "Restart territory"
 msgstr "????????? ???????????????"
 
-#: engines/hypno/metaengine.cpp:245
+#: engines/hypno/metaengine.cpp:302
 msgid "Begin new mission"
 msgstr ""
 
-#: engines/hypno/metaengine.cpp:313
+#: engines/hypno/metaengine.cpp:374
 #, fuzzy
 #| msgid "Game Options"
 msgid "Pause keymappings"
 msgstr "????????? ??????????????????(O)..."
 
-#: engines/hypno/metaengine.cpp:314
+#: engines/hypno/metaengine.cpp:375
 #, fuzzy
 #| msgid "Game Options"
 msgid "Direction keymappings"
 msgstr "????????? ??????????????????(O)..."
 
-#: engines/hypno/metaengine.cpp:316 engines/mads/metaengine.cpp:391
+#: engines/hypno/metaengine.cpp:377 engines/mads/metaengine.cpp:391
 #: engines/tsage/metaengine.cpp:259
 #, fuzzy
 msgid "Show credits"
 msgstr "???????????? ????????? files ??????????????????"
 
+#: engines/hypno/metaengine.cpp:417
+#, fuzzy
+#| msgid "Lean Left"
+msgid "Menu left"
+msgstr "???????????? lean ????????????"
+
+#: engines/hypno/metaengine.cpp:425
+#, fuzzy
+#| msgid "Move down"
+msgid "Menu down"
+msgstr "???????????? ?????? ????????????"
+
+#: engines/hypno/metaengine.cpp:433
+#, fuzzy
+msgid "Menu right"
+msgstr "??????????????? ??????????????????"
+
+#: engines/hypno/metaengine.cpp:441
+#, fuzzy
+#| msgid "Game Options"
+msgid "Menu up"
+msgstr "????????? ??????????????????(O)..."
+
 #. I18N: Action in In Cold Blood
 #: engines/icb/icb.cpp:107
 msgid "Arm"
@@ -13296,27 +13370,6 @@ msgstr ""
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1188 engines/scumm/metaengine.cpp:1265
-msgid "Stick up"
-msgstr ""
-
-#: engines/scumm/metaengine.cpp:1195 engines/scumm/metaengine.cpp:1272
-#, fuzzy
-msgid "Stick down"
-msgstr "???????????? ????????????"
-
-#: engines/scumm/metaengine.cpp:1202 engines/scumm/metaengine.cpp:1279
-#, fuzzy
-#| msgid "Slide Right"
-msgid "Stick left"
-msgstr "?????????????????? ?????????????????? ?????????"
-
-#: engines/scumm/metaengine.cpp:1209 engines/scumm/metaengine.cpp:1286
-#, fuzzy
-#| msgid "Slide Right"
-msgid "Stick right"
-msgstr "?????????????????? ?????????????????? ?????????"
-
 #: engines/scumm/metaengine.cpp:1216 engines/scumm/metaengine.cpp:1293
 #, fuzzy
 #| msgid "Move Left"
diff --git a/po/hu_HU.po b/po/hu_HU.po
index 7238b8e6f7e..083374929eb 100644
--- a/po/hu_HU.po
+++ b/po/hu_HU.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-13 06:14+0000\n"
+"POT-Creation-Date: 2026-06-13 10:20+0000\n"
 "PO-Revision-Date: 2025-03-07 00:18+0000\n"
 "Last-Translator: Hov??nszki Tam??s <gahex68379 at lofiey.com>\n"
 "Language-Team: Hungarian <https://translations.scummvm.org/projects/scummvm/"
@@ -134,7 +134,7 @@ msgstr "Fentebb l??p??s"
 #: engines/buried/saveload.cpp:83 engines/cine/saveload.cpp:857
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:123
 #: engines/drascula/saveload.cpp:51 engines/grim/inputdialog.cpp:84
-#: engines/hypno/metaengine.cpp:225 engines/mohawk/myst_stacks/menu.cpp:283
+#: engines/hypno/metaengine.cpp:282 engines/mohawk/myst_stacks/menu.cpp:283
 #: engines/mohawk/myst_stacks/menu.cpp:300
 #: engines/mohawk/myst_stacks/menu.cpp:318
 #: engines/mohawk/riven_stacks/aspit.cpp:349
@@ -340,7 +340,7 @@ msgstr ""
 #: engines/chamber/metaengine.cpp:91 engines/director/events.cpp:122
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:139
 #: engines/efh/metaengine.cpp:362 engines/grim/grim.cpp:366
-#: engines/hypno/metaengine.cpp:192 engines/kyra/gui/saveload_eob.cpp:611
+#: engines/hypno/metaengine.cpp:249 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:197
 #: engines/mm/mm1/metaengine.cpp:54 engines/parallaction/metaengine.cpp:169
 #: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
@@ -359,7 +359,7 @@ msgstr "Igen"
 #: engines/chamber/metaengine.cpp:97 engines/director/events.cpp:122
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:139
 #: engines/efh/metaengine.cpp:368 engines/grim/grim.cpp:366
-#: engines/hypno/metaengine.cpp:198 engines/kyra/gui/saveload_eob.cpp:611
+#: engines/hypno/metaengine.cpp:255 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:204
 #: engines/mm/mm1/metaengine.cpp:52 engines/parallaction/metaengine.cpp:175
 #: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
@@ -3452,7 +3452,7 @@ msgstr "Grafikus kalandj??t??k motor"
 #: backends/events/default/default-events.cpp:358
 #: engines/adl/metaengine.cpp:352 engines/buried/metaengine.cpp:154
 #: engines/chamber/metaengine.cpp:84 engines/drascula/metaengine.cpp:304
-#: engines/hdb/metaengine.cpp:275 engines/hypno/metaengine.cpp:251
+#: engines/hdb/metaengine.cpp:275 engines/hypno/metaengine.cpp:308
 #: engines/lab/metaengine.cpp:172 engines/mohawk/myst_stacks/menu.cpp:317
 #: engines/mohawk/riven_stacks/aspit.cpp:454
 #: engines/neverhood/metaengine.cpp:200 engines/saga/metaengine.cpp:449
@@ -4035,8 +4035,9 @@ msgstr "Jobb katt"
 #: engines/buried/metaengine.cpp:167 engines/crab/input/input.cpp:261
 #: engines/cruise/metaengine.cpp:217 engines/dm/metaengine.cpp:180
 #: engines/grim/grim.cpp:550 engines/hdb/metaengine.cpp:258
-#: engines/hypno/metaengine.cpp:186 engines/hypno/metaengine.cpp:369
-#: engines/icb/icb.cpp:137 engines/icb/icb.cpp:203 engines/mohawk/myst.cpp:595
+#: engines/hypno/metaengine.cpp:243 engines/hypno/metaengine.cpp:339
+#: engines/hypno/metaengine.cpp:456 engines/icb/icb.cpp:137
+#: engines/icb/icb.cpp:203 engines/mohawk/myst.cpp:595
 #: engines/mohawk/riven.cpp:876 engines/ngi/metaengine.cpp:136
 #: engines/sky/metaengine.cpp:126 engines/sludge/keymapper_tables.h:52
 #: engines/sludge/keymapper_tables.h:125 engines/sludge/keymapper_tables.h:187
@@ -4978,7 +4979,7 @@ msgstr "Y"
 #: backends/platform/sdl/opendingux/opendingux.cpp:57
 #: backends/platform/sdl/ps3/ps3.cpp:50 backends/platform/sdl/psp2/psp2.cpp:51
 #: engines/bagel/metaengine.cpp:64 engines/efh/metaengine.cpp:397
-#: engines/got/metaengine.cpp:76 engines/hypno/metaengine.cpp:363
+#: engines/got/metaengine.cpp:76 engines/hypno/metaengine.cpp:449
 #: engines/mm/mm1/metaengine.cpp:50 engines/mm/mm1/metaengine.cpp:97
 #: engines/sherlock/metaengine.cpp:660 engines/sherlock/metaengine.cpp:672
 #: engines/sherlock/metaengine.cpp:1014 engines/supernova/metaengine.cpp:303
@@ -6582,7 +6583,7 @@ msgstr "Vide?? ??tugr??sa"
 #: engines/access/metaengine.cpp:237 engines/asylum/metaengine.cpp:171
 #: engines/asylum/metaengine.cpp:198 engines/cine/metaengine.cpp:377
 #: engines/efh/metaengine.cpp:286 engines/hdb/metaengine.cpp:213
-#: engines/hypno/metaengine.cpp:302 engines/hypno/metaengine.cpp:356
+#: engines/hypno/metaengine.cpp:363 engines/hypno/metaengine.cpp:411
 #: engines/nancy/input.cpp:195 engines/tinsel/metaengine.cpp:263
 #: engines/titanic/metaengine.cpp:324 engines/ultima/ultima8/metaengine.cpp:52
 msgid "Move up"
@@ -6592,7 +6593,7 @@ msgstr "Mozg??s fel"
 #: engines/access/metaengine.cpp:244 engines/asylum/metaengine.cpp:177
 #: engines/asylum/metaengine.cpp:205 engines/cine/metaengine.cpp:384
 #: engines/efh/metaengine.cpp:293 engines/hdb/metaengine.cpp:219
-#: engines/hypno/metaengine.cpp:290 engines/hypno/metaengine.cpp:342
+#: engines/hypno/metaengine.cpp:351 engines/hypno/metaengine.cpp:399
 #: engines/nancy/input.cpp:201 engines/tinsel/metaengine.cpp:269
 #: engines/titanic/metaengine.cpp:330 engines/ultima/ultima8/metaengine.cpp:53
 msgid "Move down"
@@ -6603,7 +6604,7 @@ msgstr "Mozg??s le"
 #: engines/access/metaengine.cpp:251 engines/asylum/metaengine.cpp:183
 #: engines/asylum/metaengine.cpp:212 engines/cine/metaengine.cpp:391
 #: engines/efh/metaengine.cpp:300 engines/hdb/metaengine.cpp:225
-#: engines/hypno/metaengine.cpp:284 engines/hypno/metaengine.cpp:335
+#: engines/hypno/metaengine.cpp:345 engines/hypno/metaengine.cpp:393
 #: engines/kyra/engine/eobcommon.cpp:373 engines/lure/metaengine.cpp:218
 #: engines/nancy/input.cpp:207 engines/tinsel/metaengine.cpp:275
 #: engines/titanic/metaengine.cpp:312 engines/titanic/metaengine.cpp:390
@@ -6616,7 +6617,7 @@ msgstr "Mozg??s balra"
 #: engines/access/metaengine.cpp:258 engines/asylum/metaengine.cpp:189
 #: engines/asylum/metaengine.cpp:219 engines/cine/metaengine.cpp:398
 #: engines/efh/metaengine.cpp:307 engines/hdb/metaengine.cpp:231
-#: engines/hypno/metaengine.cpp:296 engines/hypno/metaengine.cpp:349
+#: engines/hypno/metaengine.cpp:357 engines/hypno/metaengine.cpp:405
 #: engines/kyra/engine/eobcommon.cpp:374 engines/lure/metaengine.cpp:226
 #: engines/nancy/input.cpp:213 engines/tinsel/metaengine.cpp:281
 #: engines/titanic/metaengine.cpp:318 engines/titanic/metaengine.cpp:396
@@ -7110,8 +7111,8 @@ msgstr "F?? AGOS"
 #: engines/cine/metaengine.cpp:271 engines/cruise/metaengine.cpp:169
 #: engines/dm/metaengine.cpp:126 engines/draci/metaengine.cpp:196
 #: engines/drascula/metaengine.cpp:206 engines/hopkins/metaengine.cpp:207
-#: engines/hugo/metaengine.cpp:130 engines/hypno/metaengine.cpp:166
-#: engines/hypno/metaengine.cpp:267 engines/hypno/metaengine.cpp:311
+#: engines/hugo/metaengine.cpp:130 engines/hypno/metaengine.cpp:225
+#: engines/hypno/metaengine.cpp:324 engines/hypno/metaengine.cpp:372
 #: engines/illusions/metaengine.cpp:90 engines/lab/metaengine.cpp:152
 #: engines/lure/metaengine.cpp:169 engines/made/metaengine.cpp:133
 #: engines/mads/metaengine.cpp:317 engines/neverhood/metaengine.cpp:186
@@ -7842,7 +7843,7 @@ msgstr "Harcmodor V??lt??s"
 
 #. I18N: This keymap allows skipping video cutscenes
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
-#: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
+#: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:210
 #: engines/phoenixvr/metaengine.cpp:94 engines/scumm/help.cpp:79
 #: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
@@ -9160,7 +9161,7 @@ msgstr "1. szerepl?? kiv??laszt??sa"
 msgid "Select option 14"
 msgstr "1. szerepl?? kiv??laszt??sa"
 
-#: engines/efh/metaengine.cpp:257 engines/hypno/metaengine.cpp:312
+#: engines/efh/metaengine.cpp:257 engines/hypno/metaengine.cpp:373
 #: engines/sludge/keymapper_tables.h:35 engines/supernova/metaengine.cpp:218
 #, fuzzy
 #| msgid "Game keymappings"
@@ -10873,43 +10874,99 @@ msgstr ""
 msgid "Intro keymappings"
 msgstr "Index gombkioszt??s"
 
-#: engines/hypno/metaengine.cpp:134
+#: engines/hypno/metaengine.cpp:134 engines/hypno/metaengine.cpp:144
 msgid "Primary shoot"
 msgstr ""
 
-#: engines/hypno/metaengine.cpp:140
+#: engines/hypno/metaengine.cpp:139
 #, fuzzy
 #| msgid "Seconds"
 msgid "Secondary shoot"
 msgstr "M??sodpercek"
 
-#: engines/hypno/metaengine.cpp:146 engines/lab/metaengine.cpp:209
+#: engines/hypno/metaengine.cpp:149
+#, fuzzy
+#| msgid "Map hide toolbar action"
+msgid "Rapid fire / secondary action"
+msgstr "Eszk??zt??r rejt??s gomb"
+
+#: engines/hypno/metaengine.cpp:155
+#, fuzzy
+#| msgid "Slide Left"
+msgid "Aim left"
+msgstr "Cs??sz??s balra"
+
+#: engines/hypno/metaengine.cpp:160
+#, fuzzy
+#| msgid "Scroll down"
+msgid "Aim down"
+msgstr "G??rget??s LE"
+
+#: engines/hypno/metaengine.cpp:165
+#, fuzzy
+#| msgid "Up Right"
+msgid "Aim right"
+msgstr "Jobbra Fel"
+
+#: engines/hypno/metaengine.cpp:170
+msgid "Aim up"
+msgstr ""
+
+#: engines/hypno/metaengine.cpp:175 engines/scumm/metaengine.cpp:1202
+#: engines/scumm/metaengine.cpp:1279
+#, fuzzy
+#| msgid "Slide Left"
+msgid "Stick left"
+msgstr "Cs??sz??s balra"
+
+#: engines/hypno/metaengine.cpp:182 engines/scumm/metaengine.cpp:1195
+#: engines/scumm/metaengine.cpp:1272
+#, fuzzy
+#| msgid "Scroll down"
+msgid "Stick down"
+msgstr "G??rget??s LE"
+
+#: engines/hypno/metaengine.cpp:189 engines/scumm/metaengine.cpp:1209
+#: engines/scumm/metaengine.cpp:1286
+#, fuzzy
+#| msgid "Slide Right"
+msgid "Stick right"
+msgstr "Cs??sz??s jobbra"
+
+#: engines/hypno/metaengine.cpp:196 engines/scumm/metaengine.cpp:1188
+#: engines/scumm/metaengine.cpp:1265
+#, fuzzy
+#| msgid "Pick up"
+msgid "Stick up"
+msgstr "Felvesz"
+
+#: engines/hypno/metaengine.cpp:203 engines/lab/metaengine.cpp:209
 #: engines/ngi/metaengine.cpp:168 engines/sherlock/metaengine.cpp:872
 #: engines/teenagent/metaengine.cpp:232 engines/toon/metaengine.cpp:98
 #: engines/voyeur/metaengine.cpp:235
 msgid "Skip intro"
 msgstr "Nyit??vide?? ??tugr??sa"
 
-#: engines/hypno/metaengine.cpp:167
+#: engines/hypno/metaengine.cpp:226
 #, fuzzy
 #| msgid "Escape keymappings"
 msgid "Exit menu keymappings"
 msgstr "Kil??p??si kioszt??sok"
 
-#: engines/hypno/metaengine.cpp:168
+#: engines/hypno/metaengine.cpp:227
 #, fuzzy
 #| msgid "Index keymappings"
 msgid "Difficulty selection menu keymappings"
 msgstr "Index gombkioszt??s"
 
-#: engines/hypno/metaengine.cpp:169
+#: engines/hypno/metaengine.cpp:228
 #, fuzzy
 #| msgid "Start menu keymappings"
 msgid "Retry menu keymappings"
 msgstr "Start men?? gombkioszt??sok"
 
-#: engines/hypno/metaengine.cpp:172 engines/hypno/metaengine.cpp:270
-#: engines/hypno/metaengine.cpp:322
+#: engines/hypno/metaengine.cpp:231 engines/hypno/metaengine.cpp:327
+#: engines/hypno/metaengine.cpp:382
 #, fuzzy
 #| msgid "Skip speech"
 msgid "Skip level (cheat)"
@@ -10918,61 +10975,85 @@ msgstr "Besz??d ??tugr??s"
 #. I18N: (Game name: Soldier Boyz) player refers to the users own character.
 #. I18N: (Game name: Marvel Comics Spider-Man: The Sinister Six) player refers to the users own character.
 #. I18N: (Game name: Wetlands) player refers to the users own character.
-#: engines/hypno/metaengine.cpp:180 engines/hypno/metaengine.cpp:278
-#: engines/hypno/metaengine.cpp:329
+#: engines/hypno/metaengine.cpp:238 engines/hypno/metaengine.cpp:334
+#: engines/hypno/metaengine.cpp:388
 msgid "Kill player"
 msgstr ""
 
 #. I18N: (Game name: Soldier Boyz) the game has 3 difficulty levels: Chump, Punk and Badass. Chump is the easy mode.
-#: engines/hypno/metaengine.cpp:205
+#: engines/hypno/metaengine.cpp:262
 msgid "Chump"
 msgstr ""
 
 #. I18N: (Game name: Soldier Boyz) the game has 3 difficulty levels: Chump, Punk and Badass. Punk is the medium mode.
-#: engines/hypno/metaengine.cpp:212
+#: engines/hypno/metaengine.cpp:269
 msgid "Punk"
 msgstr ""
 
 #. I18N: (Game name: Soldier Boyz) the game has 3 difficulty levels: Chump, Punk and Badass. Badass is the hard mode.
-#: engines/hypno/metaengine.cpp:219
+#: engines/hypno/metaengine.cpp:276
 msgid "Badass"
 msgstr ""
 
 #. I18N: (Game name: Soldier Boyz) This makes the player restart from the last checkpoint.
-#: engines/hypno/metaengine.cpp:232
+#: engines/hypno/metaengine.cpp:289
 #, fuzzy
 #| msgid "Retreat"
 msgid "Retry sector"
 msgstr "Visszavonul??s"
 
 #. I18N: (Game name: Soldier Boyz) This makes the player restart the current mission / level.
-#: engines/hypno/metaengine.cpp:239
+#: engines/hypno/metaengine.cpp:296
 #, fuzzy
 #| msgid "Restart animation"
 msgid "Restart territory"
 msgstr "Anim??ci?? ??jraind??t??sa"
 
-#: engines/hypno/metaengine.cpp:245
+#: engines/hypno/metaengine.cpp:302
 msgid "Begin new mission"
 msgstr ""
 
-#: engines/hypno/metaengine.cpp:313
+#: engines/hypno/metaengine.cpp:374
 #, fuzzy
 #| msgid "Game keymappings"
 msgid "Pause keymappings"
 msgstr "J??t??k gombkioszt??sok"
 
-#: engines/hypno/metaengine.cpp:314
+#: engines/hypno/metaengine.cpp:375
 #, fuzzy
 #| msgid "Index keymappings"
 msgid "Direction keymappings"
 msgstr "Index gombkioszt??s"
 
-#: engines/hypno/metaengine.cpp:316 engines/mads/metaengine.cpp:391
+#: engines/hypno/metaengine.cpp:377 engines/mads/metaengine.cpp:391
 #: engines/tsage/metaengine.cpp:259
 msgid "Show credits"
 msgstr "St??blista megjelen??t??se"
 
+#: engines/hypno/metaengine.cpp:417
+#, fuzzy
+#| msgid "Move left"
+msgid "Menu left"
+msgstr "Mozg??s balra"
+
+#: engines/hypno/metaengine.cpp:425
+#, fuzzy
+#| msgid "Move down"
+msgid "Menu down"
+msgstr "Mozg??s le"
+
+#: engines/hypno/metaengine.cpp:433
+#, fuzzy
+#| msgid "Move right"
+msgid "Menu right"
+msgstr "Mozg??s jobbra"
+
+#: engines/hypno/metaengine.cpp:441
+#, fuzzy
+#| msgid "Menu"
+msgid "Menu up"
+msgstr "Men??"
+
 #. I18N: Action in In Cold Blood
 #: engines/icb/icb.cpp:107
 msgid "Arm"
@@ -14500,30 +14581,6 @@ msgstr ""
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1188 engines/scumm/metaengine.cpp:1265
-#, fuzzy
-#| msgid "Pick up"
-msgid "Stick up"
-msgstr "Felvesz"
-
-#: engines/scumm/metaengine.cpp:1195 engines/scumm/metaengine.cpp:1272
-#, fuzzy
-#| msgid "Scroll down"
-msgid "Stick down"
-msgstr "G??rget??s LE"
-
-#: engines/scumm/metaengine.cpp:1202 engines/scumm/metaengine.cpp:1279
-#, fuzzy
-#| msgid "Slide Left"
-msgid "Stick left"
-msgstr "Cs??sz??s balra"
-
-#: engines/scumm/metaengine.cpp:1209 engines/scumm/metaengine.cpp:1286
-#, fuzzy
-#| msgid "Slide Right"
-msgid "Stick right"
-msgstr "Cs??sz??s jobbra"
-
 #: engines/scumm/metaengine.cpp:1216 engines/scumm/metaengine.cpp:1293
 #, fuzzy
 #| msgid "Move left"
@@ -20242,9 +20299,6 @@ msgstr ""
 #~ msgid "You must map a key to the 'Right Click' action to play this game"
 #~ msgstr "V??lassz egy billenty??t a 'Jobbkatt' m??velethez"
 
-#~ msgid "Map hide toolbar action"
-#~ msgstr "Eszk??zt??r rejt??s gomb"
-
 #~ msgid "You must map a key to the 'Hide toolbar' action to play this game"
 #~ msgstr "V??lassz egy billenty??t az 'Eszk??zt??r rejt??s' m??velethez"
 
diff --git a/po/it_IT.po b/po/it_IT.po
index 86086a3b09d..2347c68ed82 100644
--- a/po/it_IT.po
+++ b/po/it_IT.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-13 06:14+0000\n"
+"POT-Creation-Date: 2026-06-13 10:20+0000\n"
 "PO-Revision-Date: 2026-06-13 09:55+0000\n"
 "Last-Translator: Walter Agazzi <walter.agazzi at protonmail.com>\n"
 "Language-Team: Italian <https://translations.scummvm.org/projects/scummvm/"
@@ -133,7 +133,7 @@ msgstr "Su"
 #: engines/buried/saveload.cpp:83 engines/cine/saveload.cpp:857
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:123
 #: engines/drascula/saveload.cpp:51 engines/grim/inputdialog.cpp:84
-#: engines/hypno/metaengine.cpp:225 engines/mohawk/myst_stacks/menu.cpp:283
+#: engines/hypno/metaengine.cpp:282 engines/mohawk/myst_stacks/menu.cpp:283
 #: engines/mohawk/myst_stacks/menu.cpp:300
 #: engines/mohawk/myst_stacks/menu.cpp:318
 #: engines/mohawk/riven_stacks/aspit.cpp:349
@@ -337,7 +337,7 @@ msgstr "Un altro servizio di archiviazione ?? attivo. Vuoi interromperlo?"
 #: engines/chamber/metaengine.cpp:91 engines/director/events.cpp:122
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:139
 #: engines/efh/metaengine.cpp:362 engines/grim/grim.cpp:366
-#: engines/hypno/metaengine.cpp:192 engines/kyra/gui/saveload_eob.cpp:611
+#: engines/hypno/metaengine.cpp:249 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:197
 #: engines/mm/mm1/metaengine.cpp:54 engines/parallaction/metaengine.cpp:169
 #: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
@@ -356,7 +356,7 @@ msgstr "S??"
 #: engines/chamber/metaengine.cpp:97 engines/director/events.cpp:122
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:139
 #: engines/efh/metaengine.cpp:368 engines/grim/grim.cpp:366
-#: engines/hypno/metaengine.cpp:198 engines/kyra/gui/saveload_eob.cpp:611
+#: engines/hypno/metaengine.cpp:255 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:204
 #: engines/mm/mm1/metaengine.cpp:52 engines/parallaction/metaengine.cpp:175
 #: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
@@ -3413,7 +3413,7 @@ msgstr "Motore per avventure grafiche"
 #: backends/events/default/default-events.cpp:358
 #: engines/adl/metaengine.cpp:352 engines/buried/metaengine.cpp:154
 #: engines/chamber/metaengine.cpp:84 engines/drascula/metaengine.cpp:304
-#: engines/hdb/metaengine.cpp:275 engines/hypno/metaengine.cpp:251
+#: engines/hdb/metaengine.cpp:275 engines/hypno/metaengine.cpp:308
 #: engines/lab/metaengine.cpp:172 engines/mohawk/myst_stacks/menu.cpp:317
 #: engines/mohawk/riven_stacks/aspit.cpp:454
 #: engines/neverhood/metaengine.cpp:200 engines/saga/metaengine.cpp:449
@@ -3991,8 +3991,9 @@ msgstr "Clic destro"
 #: engines/buried/metaengine.cpp:167 engines/crab/input/input.cpp:261
 #: engines/cruise/metaengine.cpp:217 engines/dm/metaengine.cpp:180
 #: engines/grim/grim.cpp:550 engines/hdb/metaengine.cpp:258
-#: engines/hypno/metaengine.cpp:186 engines/hypno/metaengine.cpp:369
-#: engines/icb/icb.cpp:137 engines/icb/icb.cpp:203 engines/mohawk/myst.cpp:595
+#: engines/hypno/metaengine.cpp:243 engines/hypno/metaengine.cpp:339
+#: engines/hypno/metaengine.cpp:456 engines/icb/icb.cpp:137
+#: engines/icb/icb.cpp:203 engines/mohawk/myst.cpp:595
 #: engines/mohawk/riven.cpp:876 engines/ngi/metaengine.cpp:136
 #: engines/sky/metaengine.cpp:126 engines/sludge/keymapper_tables.h:52
 #: engines/sludge/keymapper_tables.h:125 engines/sludge/keymapper_tables.h:187
@@ -4890,7 +4891,7 @@ msgstr "Y"
 #: backends/platform/sdl/opendingux/opendingux.cpp:57
 #: backends/platform/sdl/ps3/ps3.cpp:50 backends/platform/sdl/psp2/psp2.cpp:51
 #: engines/bagel/metaengine.cpp:64 engines/efh/metaengine.cpp:397
-#: engines/got/metaengine.cpp:76 engines/hypno/metaengine.cpp:363
+#: engines/got/metaengine.cpp:76 engines/hypno/metaengine.cpp:449
 #: engines/mm/mm1/metaengine.cpp:50 engines/mm/mm1/metaengine.cpp:97
 #: engines/sherlock/metaengine.cpp:660 engines/sherlock/metaengine.cpp:672
 #: engines/sherlock/metaengine.cpp:1014 engines/supernova/metaengine.cpp:303
@@ -6375,7 +6376,7 @@ msgstr "Salta filmato"
 #: engines/access/metaengine.cpp:237 engines/asylum/metaengine.cpp:171
 #: engines/asylum/metaengine.cpp:198 engines/cine/metaengine.cpp:377
 #: engines/efh/metaengine.cpp:286 engines/hdb/metaengine.cpp:213
-#: engines/hypno/metaengine.cpp:302 engines/hypno/metaengine.cpp:356
+#: engines/hypno/metaengine.cpp:363 engines/hypno/metaengine.cpp:411
 #: engines/nancy/input.cpp:195 engines/tinsel/metaengine.cpp:263
 #: engines/titanic/metaengine.cpp:324 engines/ultima/ultima8/metaengine.cpp:52
 msgid "Move up"
@@ -6385,7 +6386,7 @@ msgstr "Movimento verso l'alto"
 #: engines/access/metaengine.cpp:244 engines/asylum/metaengine.cpp:177
 #: engines/asylum/metaengine.cpp:205 engines/cine/metaengine.cpp:384
 #: engines/efh/metaengine.cpp:293 engines/hdb/metaengine.cpp:219
-#: engines/hypno/metaengine.cpp:290 engines/hypno/metaengine.cpp:342
+#: engines/hypno/metaengine.cpp:351 engines/hypno/metaengine.cpp:399
 #: engines/nancy/input.cpp:201 engines/tinsel/metaengine.cpp:269
 #: engines/titanic/metaengine.cpp:330 engines/ultima/ultima8/metaengine.cpp:53
 msgid "Move down"
@@ -6396,7 +6397,7 @@ msgstr "Movimento verso il basso"
 #: engines/access/metaengine.cpp:251 engines/asylum/metaengine.cpp:183
 #: engines/asylum/metaengine.cpp:212 engines/cine/metaengine.cpp:391
 #: engines/efh/metaengine.cpp:300 engines/hdb/metaengine.cpp:225
-#: engines/hypno/metaengine.cpp:284 engines/hypno/metaengine.cpp:335
+#: engines/hypno/metaengine.cpp:345 engines/hypno/metaengine.cpp:393
 #: engines/kyra/engine/eobcommon.cpp:373 engines/lure/metaengine.cpp:218
 #: engines/nancy/input.cpp:207 engines/tinsel/metaengine.cpp:275
 #: engines/titanic/metaengine.cpp:312 engines/titanic/metaengine.cpp:390
@@ -6409,7 +6410,7 @@ msgstr "Movimento verso sinistra"
 #: engines/access/metaengine.cpp:258 engines/asylum/metaengine.cpp:189
 #: engines/asylum/metaengine.cpp:219 engines/cine/metaengine.cpp:398
 #: engines/efh/metaengine.cpp:307 engines/hdb/metaengine.cpp:231
-#: engines/hypno/metaengine.cpp:296 engines/hypno/metaengine.cpp:349
+#: engines/hypno/metaengine.cpp:357 engines/hypno/metaengine.cpp:405
 #: engines/kyra/engine/eobcommon.cpp:374 engines/lure/metaengine.cpp:226
 #: engines/nancy/input.cpp:213 engines/tinsel/metaengine.cpp:281
 #: engines/titanic/metaengine.cpp:318 engines/titanic/metaengine.cpp:396
@@ -6912,8 +6913,8 @@ msgstr "AGOS - Principali"
 #: engines/cine/metaengine.cpp:271 engines/cruise/metaengine.cpp:169
 #: engines/dm/metaengine.cpp:126 engines/draci/metaengine.cpp:196
 #: engines/drascula/metaengine.cpp:206 engines/hopkins/metaengine.cpp:207
-#: engines/hugo/metaengine.cpp:130 engines/hypno/metaengine.cpp:166
-#: engines/hypno/metaengine.cpp:267 engines/hypno/metaengine.cpp:311
+#: engines/hugo/metaengine.cpp:130 engines/hypno/metaengine.cpp:225
+#: engines/hypno/metaengine.cpp:324 engines/hypno/metaengine.cpp:372
 #: engines/illusions/metaengine.cpp:90 engines/lab/metaengine.cpp:152
 #: engines/lure/metaengine.cpp:169 engines/made/metaengine.cpp:133
 #: engines/mads/metaengine.cpp:317 engines/neverhood/metaengine.cpp:186
@@ -7623,7 +7624,7 @@ msgstr "Attiva / Disattiva modalit?? combattimento"
 
 #. I18N: This keymap allows skipping video cutscenes
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
-#: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
+#: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:210
 #: engines/phoenixvr/metaengine.cpp:94 engines/scumm/help.cpp:79
 #: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
@@ -8773,7 +8774,7 @@ msgstr "Seleziona opzione 13"
 msgid "Select option 14"
 msgstr "Seleziona opzione 14"
 
-#: engines/efh/metaengine.cpp:257 engines/hypno/metaengine.cpp:312
+#: engines/efh/metaengine.cpp:257 engines/hypno/metaengine.cpp:373
 #: engines/sludge/keymapper_tables.h:35 engines/supernova/metaengine.cpp:218
 msgid "Menu keymappings"
 msgstr "Mappature tasti men??"
@@ -10289,88 +10290,160 @@ msgstr "Abilita alcune parti che sono state escluse dal gioco originale."
 msgid "Intro keymappings"
 msgstr "Mappatura tasti dell'introduzione"
 
-#: engines/hypno/metaengine.cpp:134
+#: engines/hypno/metaengine.cpp:134 engines/hypno/metaengine.cpp:144
 msgid "Primary shoot"
 msgstr "Sparo arma principale"
 
-#: engines/hypno/metaengine.cpp:140
+#: engines/hypno/metaengine.cpp:139
 msgid "Secondary shoot"
 msgstr "Sparo arma secondaria"
 
-#: engines/hypno/metaengine.cpp:146 engines/lab/metaengine.cpp:209
+#: engines/hypno/metaengine.cpp:149
+#, fuzzy
+#| msgid "Map hide toolbar action"
+msgid "Rapid fire / secondary action"
+msgstr "Mappa l'azione 'Nascondi barra degli strumenti'"
+
+#: engines/hypno/metaengine.cpp:155
+#, fuzzy
+#| msgid "Slide left"
+msgid "Aim left"
+msgstr "Scorri a sinistra"
+
+#: engines/hypno/metaengine.cpp:160
+#, fuzzy
+#| msgid "Stick down"
+msgid "Aim down"
+msgstr "Joystick gi??"
+
+#: engines/hypno/metaengine.cpp:165
+#, fuzzy
+#| msgid "Up right"
+msgid "Aim right"
+msgstr "Su destra"
+
+#: engines/hypno/metaengine.cpp:170
+msgid "Aim up"
+msgstr ""
+
+#: engines/hypno/metaengine.cpp:175 engines/scumm/metaengine.cpp:1202
+#: engines/scumm/metaengine.cpp:1279
+msgid "Stick left"
+msgstr "Joystick sinistra"
+
+#: engines/hypno/metaengine.cpp:182 engines/scumm/metaengine.cpp:1195
+#: engines/scumm/metaengine.cpp:1272
+msgid "Stick down"
+msgstr "Joystick gi??"
+
+#: engines/hypno/metaengine.cpp:189 engines/scumm/metaengine.cpp:1209
+#: engines/scumm/metaengine.cpp:1286
+msgid "Stick right"
+msgstr "Joystick destra"
+
+#: engines/hypno/metaengine.cpp:196 engines/scumm/metaengine.cpp:1188
+#: engines/scumm/metaengine.cpp:1265
+msgid "Stick up"
+msgstr "Joystick su"
+
+#: engines/hypno/metaengine.cpp:203 engines/lab/metaengine.cpp:209
 #: engines/ngi/metaengine.cpp:168 engines/sherlock/metaengine.cpp:872
 #: engines/teenagent/metaengine.cpp:232 engines/toon/metaengine.cpp:98
 #: engines/voyeur/metaengine.cpp:235
 msgid "Skip intro"
 msgstr "Salta introduzione"
 
-#: engines/hypno/metaengine.cpp:167
+#: engines/hypno/metaengine.cpp:226
 msgid "Exit menu keymappings"
 msgstr "Mappatura tasti men?? di uscita"
 
-#: engines/hypno/metaengine.cpp:168
+#: engines/hypno/metaengine.cpp:227
 msgid "Difficulty selection menu keymappings"
 msgstr "Mappatura tasti men?? selezione difficolt??"
 
-#: engines/hypno/metaengine.cpp:169
+#: engines/hypno/metaengine.cpp:228
 msgid "Retry menu keymappings"
 msgstr "Mappatura tasti men?? Ritenta"
 
-#: engines/hypno/metaengine.cpp:172 engines/hypno/metaengine.cpp:270
-#: engines/hypno/metaengine.cpp:322
+#: engines/hypno/metaengine.cpp:231 engines/hypno/metaengine.cpp:327
+#: engines/hypno/metaengine.cpp:382
 msgid "Skip level (cheat)"
 msgstr "Salta livello (cheat)"
 
 #. I18N: (Game name: Soldier Boyz) player refers to the users own character.
 #. I18N: (Game name: Marvel Comics Spider-Man: The Sinister Six) player refers to the users own character.
 #. I18N: (Game name: Wetlands) player refers to the users own character.
-#: engines/hypno/metaengine.cpp:180 engines/hypno/metaengine.cpp:278
-#: engines/hypno/metaengine.cpp:329
+#: engines/hypno/metaengine.cpp:238 engines/hypno/metaengine.cpp:334
+#: engines/hypno/metaengine.cpp:388
 msgid "Kill player"
 msgstr "Sacrifica giocatore"
 
 #. I18N: (Game name: Soldier Boyz) the game has 3 difficulty levels: Chump, Punk and Badass. Chump is the easy mode.
-#: engines/hypno/metaengine.cpp:205
+#: engines/hypno/metaengine.cpp:262
 msgid "Chump"
 msgstr "Chump (facile)"
 
 #. I18N: (Game name: Soldier Boyz) the game has 3 difficulty levels: Chump, Punk and Badass. Punk is the medium mode.
-#: engines/hypno/metaengine.cpp:212
+#: engines/hypno/metaengine.cpp:269
 msgid "Punk"
 msgstr "Punk (normale)"
 
 #. I18N: (Game name: Soldier Boyz) the game has 3 difficulty levels: Chump, Punk and Badass. Badass is the hard mode.
-#: engines/hypno/metaengine.cpp:219
+#: engines/hypno/metaengine.cpp:276
 msgid "Badass"
 msgstr "Badass (difficile)"
 
 #. I18N: (Game name: Soldier Boyz) This makes the player restart from the last checkpoint.
-#: engines/hypno/metaengine.cpp:232
+#: engines/hypno/metaengine.cpp:289
 msgid "Retry sector"
 msgstr "Riprova segmento"
 
 #. I18N: (Game name: Soldier Boyz) This makes the player restart the current mission / level.
-#: engines/hypno/metaengine.cpp:239
+#: engines/hypno/metaengine.cpp:296
 msgid "Restart territory"
 msgstr "Riprova missione"
 
-#: engines/hypno/metaengine.cpp:245
+#: engines/hypno/metaengine.cpp:302
 msgid "Begin new mission"
 msgstr "Inizia nuova missione"
 
-#: engines/hypno/metaengine.cpp:313
+#: engines/hypno/metaengine.cpp:374
 msgid "Pause keymappings"
 msgstr "Mappatura tasti pausa"
 
-#: engines/hypno/metaengine.cpp:314
+#: engines/hypno/metaengine.cpp:375
 msgid "Direction keymappings"
 msgstr "Mappatura tasti di direzione"
 
-#: engines/hypno/metaengine.cpp:316 engines/mads/metaengine.cpp:391
+#: engines/hypno/metaengine.cpp:377 engines/mads/metaengine.cpp:391
 #: engines/tsage/metaengine.cpp:259
 msgid "Show credits"
 msgstr "Mostra riconoscimenti"
 
+#: engines/hypno/metaengine.cpp:417
+#, fuzzy
+#| msgid "Move left"
+msgid "Menu left"
+msgstr "Movimento verso sinistra"
+
+#: engines/hypno/metaengine.cpp:425
+#, fuzzy
+#| msgid "Move down"
+msgid "Menu down"
+msgstr "Movimento verso il basso"
+
+#: engines/hypno/metaengine.cpp:433
+#, fuzzy
+#| msgid "Move right"
+msgid "Menu right"
+msgstr "Movimento verso destra"
+
+#: engines/hypno/metaengine.cpp:441
+#, fuzzy
+#| msgid "Menu"
+msgid "Menu up"
+msgstr "Menu"
+
 #. I18N: Action in In Cold Blood
 #: engines/icb/icb.cpp:107
 msgid "Arm"
@@ -13734,22 +13807,6 @@ msgstr "Mira a sinistra / men?? sinistra"
 msgid "Aim right / menu right"
 msgstr "Mira a destra / men?? destra"
 
-#: engines/scumm/metaengine.cpp:1188 engines/scumm/metaengine.cpp:1265
-msgid "Stick up"
-msgstr "Joystick su"
-
-#: engines/scumm/metaengine.cpp:1195 engines/scumm/metaengine.cpp:1272
-msgid "Stick down"
-msgstr "Joystick gi??"
-
-#: engines/scumm/metaengine.cpp:1202 engines/scumm/metaengine.cpp:1279
-msgid "Stick left"
-msgstr "Joystick sinistra"
-
-#: engines/scumm/metaengine.cpp:1209 engines/scumm/metaengine.cpp:1286
-msgid "Stick right"
-msgstr "Joystick destra"
-
 #: engines/scumm/metaengine.cpp:1216 engines/scumm/metaengine.cpp:1293
 msgid "Fire / select"
 msgstr "Spara / seleziona"
@@ -18963,9 +19020,6 @@ msgstr ""
 #~ msgid "You must map a key to the 'Right Click' action to play this game"
 #~ msgstr "Devi mappare un tasto per l'azione \"Tasto destro\" per giocare"
 
-#~ msgid "Map hide toolbar action"
-#~ msgstr "Mappa l'azione 'Nascondi barra degli strumenti'"
-
 #~ msgid "You must map a key to the 'Hide toolbar' action to play this game"
 #~ msgstr ""
 #~ "Devi mappare un tasto per l'azione \"Nascondi barra degli strumenti\" per "
diff --git a/po/ja.po b/po/ja.po
index 65e256f6fc4..44e71e308b4 100644
--- a/po/ja.po
+++ b/po/ja.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-13 06:14+0000\n"
+"POT-Creation-Date: 2026-06-13 10:20+0000\n"
 "PO-Revision-Date: 2026-06-04 07:22+0000\n"
 "Last-Translator: VAN-Gluon <van.gluonp at gmail.com>\n"
 "Language-Team: Japanese <https://translations.scummvm.org/projects/scummvm/"
@@ -133,7 +133,7 @@ msgstr "??????"
 #: engines/buried/saveload.cpp:83 engines/cine/saveload.cpp:857
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:123
 #: engines/drascula/saveload.cpp:51 engines/grim/inputdialog.cpp:84
-#: engines/hypno/metaengine.cpp:225 engines/mohawk/myst_stacks/menu.cpp:283
+#: engines/hypno/metaengine.cpp:282 engines/mohawk/myst_stacks/menu.cpp:283
 #: engines/mohawk/myst_stacks/menu.cpp:300
 #: engines/mohawk/myst_stacks/menu.cpp:318
 #: engines/mohawk/riven_stacks/aspit.cpp:349
@@ -336,7 +336,7 @@ msgstr "????????????????????????????????????????????????????????????????????????
 #: engines/chamber/metaengine.cpp:91 engines/director/events.cpp:122
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:139
 #: engines/efh/metaengine.cpp:362 engines/grim/grim.cpp:366
-#: engines/hypno/metaengine.cpp:192 engines/kyra/gui/saveload_eob.cpp:611
+#: engines/hypno/metaengine.cpp:249 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:197
 #: engines/mm/mm1/metaengine.cpp:54 engines/parallaction/metaengine.cpp:169
 #: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
@@ -355,7 +355,7 @@ msgstr "??????"
 #: engines/chamber/metaengine.cpp:97 engines/director/events.cpp:122
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:139
 #: engines/efh/metaengine.cpp:368 engines/grim/grim.cpp:366
-#: engines/hypno/metaengine.cpp:198 engines/kyra/gui/saveload_eob.cpp:611
+#: engines/hypno/metaengine.cpp:255 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:204
 #: engines/mm/mm1/metaengine.cpp:52 engines/parallaction/metaengine.cpp:175
 #: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
@@ -3301,7 +3301,7 @@ msgstr "???????????????????????????????????????????????????"
 #: backends/events/default/default-events.cpp:358
 #: engines/adl/metaengine.cpp:352 engines/buried/metaengine.cpp:154
 #: engines/chamber/metaengine.cpp:84 engines/drascula/metaengine.cpp:304
-#: engines/hdb/metaengine.cpp:275 engines/hypno/metaengine.cpp:251
+#: engines/hdb/metaengine.cpp:275 engines/hypno/metaengine.cpp:308
 #: engines/lab/metaengine.cpp:172 engines/mohawk/myst_stacks/menu.cpp:317
 #: engines/mohawk/riven_stacks/aspit.cpp:454
 #: engines/neverhood/metaengine.cpp:200 engines/saga/metaengine.cpp:449
@@ -3849,8 +3849,9 @@ msgstr "???????????????"
 #: engines/buried/metaengine.cpp:167 engines/crab/input/input.cpp:261
 #: engines/cruise/metaengine.cpp:217 engines/dm/metaengine.cpp:180
 #: engines/grim/grim.cpp:550 engines/hdb/metaengine.cpp:258
-#: engines/hypno/metaengine.cpp:186 engines/hypno/metaengine.cpp:369
-#: engines/icb/icb.cpp:137 engines/icb/icb.cpp:203 engines/mohawk/myst.cpp:595
+#: engines/hypno/metaengine.cpp:243 engines/hypno/metaengine.cpp:339
+#: engines/hypno/metaengine.cpp:456 engines/icb/icb.cpp:137
+#: engines/icb/icb.cpp:203 engines/mohawk/myst.cpp:595
 #: engines/mohawk/riven.cpp:876 engines/ngi/metaengine.cpp:136
 #: engines/sky/metaengine.cpp:126 engines/sludge/keymapper_tables.h:52
 #: engines/sludge/keymapper_tables.h:125 engines/sludge/keymapper_tables.h:187
@@ -4747,7 +4748,7 @@ msgstr "Y"
 #: backends/platform/sdl/opendingux/opendingux.cpp:57
 #: backends/platform/sdl/ps3/ps3.cpp:50 backends/platform/sdl/psp2/psp2.cpp:51
 #: engines/bagel/metaengine.cpp:64 engines/efh/metaengine.cpp:397
-#: engines/got/metaengine.cpp:76 engines/hypno/metaengine.cpp:363
+#: engines/got/metaengine.cpp:76 engines/hypno/metaengine.cpp:449
 #: engines/mm/mm1/metaengine.cpp:50 engines/mm/mm1/metaengine.cpp:97
 #: engines/sherlock/metaengine.cpp:660 engines/sherlock/metaengine.cpp:672
 #: engines/sherlock/metaengine.cpp:1014 engines/supernova/metaengine.cpp:303
@@ -6233,7 +6234,7 @@ msgstr "????????????????????????"
 #: engines/access/metaengine.cpp:237 engines/asylum/metaengine.cpp:171
 #: engines/asylum/metaengine.cpp:198 engines/cine/metaengine.cpp:377
 #: engines/efh/metaengine.cpp:286 engines/hdb/metaengine.cpp:213
-#: engines/hypno/metaengine.cpp:302 engines/hypno/metaengine.cpp:356
+#: engines/hypno/metaengine.cpp:363 engines/hypno/metaengine.cpp:411
 #: engines/nancy/input.cpp:195 engines/tinsel/metaengine.cpp:263
 #: engines/titanic/metaengine.cpp:324 engines/ultima/ultima8/metaengine.cpp:52
 msgid "Move up"
@@ -6243,7 +6244,7 @@ msgstr "???"
 #: engines/access/metaengine.cpp:244 engines/asylum/metaengine.cpp:177
 #: engines/asylum/metaengine.cpp:205 engines/cine/metaengine.cpp:384
 #: engines/efh/metaengine.cpp:293 engines/hdb/metaengine.cpp:219
-#: engines/hypno/metaengine.cpp:290 engines/hypno/metaengine.cpp:342
+#: engines/hypno/metaengine.cpp:351 engines/hypno/metaengine.cpp:399
 #: engines/nancy/input.cpp:201 engines/tinsel/metaengine.cpp:269
 #: engines/titanic/metaengine.cpp:330 engines/ultima/ultima8/metaengine.cpp:53
 msgid "Move down"
@@ -6254,7 +6255,7 @@ msgstr "???"
 #: engines/access/metaengine.cpp:251 engines/asylum/metaengine.cpp:183
 #: engines/asylum/metaengine.cpp:212 engines/cine/metaengine.cpp:391
 #: engines/efh/metaengine.cpp:300 engines/hdb/metaengine.cpp:225
-#: engines/hypno/metaengine.cpp:284 engines/hypno/metaengine.cpp:335
+#: engines/hypno/metaengine.cpp:345 engines/hypno/metaengine.cpp:393
 #: engines/kyra/engine/eobcommon.cpp:373 engines/lure/metaengine.cpp:218
 #: engines/nancy/input.cpp:207 engines/tinsel/metaengine.cpp:275
 #: engines/titanic/metaengine.cpp:312 engines/titanic/metaengine.cpp:390
@@ -6267,7 +6268,7 @@ msgstr "????????????"
 #: engines/access/metaengine.cpp:258 engines/asylum/metaengine.cpp:189
 #: engines/asylum/metaengine.cpp:219 engines/cine/metaengine.cpp:398
 #: engines/efh/metaengine.cpp:307 engines/hdb/metaengine.cpp:231
-#: engines/hypno/metaengine.cpp:296 engines/hypno/metaengine.cpp:349
+#: engines/hypno/metaengine.cpp:357 engines/hypno/metaengine.cpp:405
 #: engines/kyra/engine/eobcommon.cpp:374 engines/lure/metaengine.cpp:226
 #: engines/nancy/input.cpp:213 engines/tinsel/metaengine.cpp:281
 #: engines/titanic/metaengine.cpp:318 engines/titanic/metaengine.cpp:396
@@ -6753,8 +6754,8 @@ msgstr "AGOS?????????"
 #: engines/cine/metaengine.cpp:271 engines/cruise/metaengine.cpp:169
 #: engines/dm/metaengine.cpp:126 engines/draci/metaengine.cpp:196
 #: engines/drascula/metaengine.cpp:206 engines/hopkins/metaengine.cpp:207
-#: engines/hugo/metaengine.cpp:130 engines/hypno/metaengine.cpp:166
-#: engines/hypno/metaengine.cpp:267 engines/hypno/metaengine.cpp:311
+#: engines/hugo/metaengine.cpp:130 engines/hypno/metaengine.cpp:225
+#: engines/hypno/metaengine.cpp:324 engines/hypno/metaengine.cpp:372
 #: engines/illusions/metaengine.cpp:90 engines/lab/metaengine.cpp:152
 #: engines/lure/metaengine.cpp:169 engines/made/metaengine.cpp:133
 #: engines/mads/metaengine.cpp:317 engines/neverhood/metaengine.cpp:186
@@ -7446,7 +7447,7 @@ msgstr "??????????????????"
 
 #. I18N: This keymap allows skipping video cutscenes
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
-#: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
+#: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:210
 #: engines/phoenixvr/metaengine.cpp:94 engines/scumm/help.cpp:79
 #: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
@@ -8770,7 +8771,7 @@ msgstr "??????1?????????"
 msgid "Select option 14"
 msgstr "??????1?????????"
 
-#: engines/efh/metaengine.cpp:257 engines/hypno/metaengine.cpp:312
+#: engines/efh/metaengine.cpp:257 engines/hypno/metaengine.cpp:373
 #: engines/sludge/keymapper_tables.h:35 engines/supernova/metaengine.cpp:218
 #, fuzzy
 #| msgid "Game keymappings"
@@ -10486,17 +10487,71 @@ msgstr "????????????????????????????????????????????????????????????????????????
 msgid "Intro keymappings"
 msgstr "???????????????????????????"
 
-#: engines/hypno/metaengine.cpp:134
+#: engines/hypno/metaengine.cpp:134 engines/hypno/metaengine.cpp:144
 msgid "Primary shoot"
 msgstr "1???????????????"
 
-#: engines/hypno/metaengine.cpp:140
+#: engines/hypno/metaengine.cpp:139
 #, fuzzy
 #| msgid "Seconds"
 msgid "Secondary shoot"
 msgstr "???"
 
-#: engines/hypno/metaengine.cpp:146 engines/lab/metaengine.cpp:209
+#: engines/hypno/metaengine.cpp:149
+msgid "Rapid fire / secondary action"
+msgstr ""
+
+#: engines/hypno/metaengine.cpp:155
+#, fuzzy
+#| msgid "Slide Left"
+msgid "Aim left"
+msgstr "??????????????????"
+
+#: engines/hypno/metaengine.cpp:160
+#, fuzzy
+#| msgid "Scroll down"
+msgid "Aim down"
+msgstr "?????????????????????"
+
+#: engines/hypno/metaengine.cpp:165
+#, fuzzy
+#| msgid "Up Right"
+msgid "Aim right"
+msgstr "??????"
+
+#: engines/hypno/metaengine.cpp:170
+msgid "Aim up"
+msgstr ""
+
+#: engines/hypno/metaengine.cpp:175 engines/scumm/metaengine.cpp:1202
+#: engines/scumm/metaengine.cpp:1279
+#, fuzzy
+#| msgid "Slide Left"
+msgid "Stick left"
+msgstr "??????????????????"
+
+#: engines/hypno/metaengine.cpp:182 engines/scumm/metaengine.cpp:1195
+#: engines/scumm/metaengine.cpp:1272
+#, fuzzy
+#| msgid "Scroll down"
+msgid "Stick down"
+msgstr "?????????????????????"
+
+#: engines/hypno/metaengine.cpp:189 engines/scumm/metaengine.cpp:1209
+#: engines/scumm/metaengine.cpp:1286
+#, fuzzy
+#| msgid "Slide Right"
+msgid "Stick right"
+msgstr "??????????????????"
+
+#: engines/hypno/metaengine.cpp:196 engines/scumm/metaengine.cpp:1188
+#: engines/scumm/metaengine.cpp:1265
+#, fuzzy
+#| msgid "Pick up"
+msgid "Stick up"
+msgstr "??????"
+
+#: engines/hypno/metaengine.cpp:203 engines/lab/metaengine.cpp:209
 #: engines/ngi/metaengine.cpp:168 engines/sherlock/metaengine.cpp:872
 #: engines/teenagent/metaengine.cpp:232 engines/toon/metaengine.cpp:98
 #: engines/voyeur/metaengine.cpp:235
@@ -10505,26 +10560,26 @@ msgstr "???"
 msgid "Skip intro"
 msgstr "????????????????????????"
 
-#: engines/hypno/metaengine.cpp:167
+#: engines/hypno/metaengine.cpp:226
 #, fuzzy
 #| msgid "Escape keymappings"
 msgid "Exit menu keymappings"
 msgstr "???????????????????????????"
 
-#: engines/hypno/metaengine.cpp:168
+#: engines/hypno/metaengine.cpp:227
 #, fuzzy
 #| msgid "Game keymappings"
 msgid "Difficulty selection menu keymappings"
 msgstr "???????????????????????????"
 
-#: engines/hypno/metaengine.cpp:169
+#: engines/hypno/metaengine.cpp:228
 #, fuzzy
 #| msgid "Game keymappings"
 msgid "Retry menu keymappings"
 msgstr "???????????????????????????"
 
-#: engines/hypno/metaengine.cpp:172 engines/hypno/metaengine.cpp:270
-#: engines/hypno/metaengine.cpp:322
+#: engines/hypno/metaengine.cpp:231 engines/hypno/metaengine.cpp:327
+#: engines/hypno/metaengine.cpp:382
 #, fuzzy
 #| msgid "Speech"
 msgid "Skip level (cheat)"
@@ -10533,61 +10588,85 @@ msgstr "??????"
 #. I18N: (Game name: Soldier Boyz) player refers to the users own character.
 #. I18N: (Game name: Marvel Comics Spider-Man: The Sinister Six) player refers to the users own character.
 #. I18N: (Game name: Wetlands) player refers to the users own character.
-#: engines/hypno/metaengine.cpp:180 engines/hypno/metaengine.cpp:278
-#: engines/hypno/metaengine.cpp:329
+#: engines/hypno/metaengine.cpp:238 engines/hypno/metaengine.cpp:334
+#: engines/hypno/metaengine.cpp:388
 msgid "Kill player"
 msgstr "PK?????????"
 
 #. I18N: (Game name: Soldier Boyz) the game has 3 difficulty levels: Chump, Punk and Badass. Chump is the easy mode.
-#: engines/hypno/metaengine.cpp:205
+#: engines/hypno/metaengine.cpp:262
 msgid "Chump"
 msgstr "????????????"
 
 #. I18N: (Game name: Soldier Boyz) the game has 3 difficulty levels: Chump, Punk and Badass. Punk is the medium mode.
-#: engines/hypno/metaengine.cpp:212
+#: engines/hypno/metaengine.cpp:269
 msgid "Punk"
 msgstr "????????????"
 
 #. I18N: (Game name: Soldier Boyz) the game has 3 difficulty levels: Chump, Punk and Badass. Badass is the hard mode.
-#: engines/hypno/metaengine.cpp:219
+#: engines/hypno/metaengine.cpp:276
 msgid "Badass"
 msgstr "?????????"
 
 #. I18N: (Game name: Soldier Boyz) This makes the player restart from the last checkpoint.
-#: engines/hypno/metaengine.cpp:232
+#: engines/hypno/metaengine.cpp:289
 #, fuzzy
 #| msgid "Retreat"
 msgid "Retry sector"
 msgstr "??????"
 
 #. I18N: (Game name: Soldier Boyz) This makes the player restart the current mission / level.
-#: engines/hypno/metaengine.cpp:239
+#: engines/hypno/metaengine.cpp:296
 #, fuzzy
 #| msgid "Faster animations"
 msgid "Restart territory"
 msgstr "?????????????????????"
 
-#: engines/hypno/metaengine.cpp:245
+#: engines/hypno/metaengine.cpp:302
 msgid "Begin new mission"
 msgstr "?????????????????????"
 
-#: engines/hypno/metaengine.cpp:313
+#: engines/hypno/metaengine.cpp:374
 #, fuzzy
 #| msgid "Game keymappings"
 msgid "Pause keymappings"
 msgstr "???????????????????????????"
 
-#: engines/hypno/metaengine.cpp:314
+#: engines/hypno/metaengine.cpp:375
 #, fuzzy
 #| msgid "Game keymappings"
 msgid "Direction keymappings"
 msgstr "???????????????????????????"
 
-#: engines/hypno/metaengine.cpp:316 engines/mads/metaengine.cpp:391
+#: engines/hypno/metaengine.cpp:377 engines/mads/metaengine.cpp:391
 #: engines/tsage/metaengine.cpp:259
 msgid "Show credits"
 msgstr "????????????????????????"
 
+#: engines/hypno/metaengine.cpp:417
+#, fuzzy
+#| msgid "Move left"
+msgid "Menu left"
+msgstr "????????????"
+
+#: engines/hypno/metaengine.cpp:425
+#, fuzzy
+#| msgid "Move down"
+msgid "Menu down"
+msgstr "???"
+
+#: engines/hypno/metaengine.cpp:433
+#, fuzzy
+#| msgid "Move right"
+msgid "Menu right"
+msgstr "????????????"
+
+#: engines/hypno/metaengine.cpp:441
+#, fuzzy
+#| msgid "Menu"
+msgid "Menu up"
+msgstr "????????????"
+
 #. I18N: Action in In Cold Blood
 #: engines/icb/icb.cpp:107
 msgid "Arm"
@@ -14118,30 +14197,6 @@ msgstr ""
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1188 engines/scumm/metaengine.cpp:1265
-#, fuzzy
-#| msgid "Pick up"
-msgid "Stick up"
-msgstr "??????"
-
-#: engines/scumm/metaengine.cpp:1195 engines/scumm/metaengine.cpp:1272
-#, fuzzy
-#| msgid "Scroll down"
-msgid "Stick down"
-msgstr "?????????????????????"
-
-#: engines/scumm/metaengine.cpp:1202 engines/scumm/metaengine.cpp:1279
-#, fuzzy
-#| msgid "Slide Left"
-msgid "Stick left"
-msgstr "??????????????????"
-
-#: engines/scumm/metaengine.cpp:1209 engines/scumm/metaengine.cpp:1286
-#, fuzzy
-#| msgid "Slide Right"
-msgid "Stick right"
-msgstr "??????????????????"
-
 #: engines/scumm/metaengine.cpp:1216 engines/scumm/metaengine.cpp:1293
 #, fuzzy
 #| msgid "Move left"
diff --git a/po/ka.po b/po/ka.po
index 323041e087d..e6b82d62742 100644
--- a/po/ka.po
+++ b/po/ka.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-13 06:14+0000\n"
+"POT-Creation-Date: 2026-06-13 10:20+0000\n"
 "PO-Revision-Date: 2026-06-05 10:22+0000\n"
 "Last-Translator: NorwayFun <temuri.doghonadze at gmail.com>\n"
 "Language-Team: Georgian <https://translations.scummvm.org/projects/scummvm/"
@@ -133,7 +133,7 @@ msgstr "??????????????? ???????????????"
 #: engines/buried/saveload.cpp:83 engines/cine/saveload.cpp:857
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:123
 #: engines/drascula/saveload.cpp:51 engines/grim/inputdialog.cpp:84
-#: engines/hypno/metaengine.cpp:225 engines/mohawk/myst_stacks/menu.cpp:283
+#: engines/hypno/metaengine.cpp:282 engines/mohawk/myst_stacks/menu.cpp:283
 #: engines/mohawk/myst_stacks/menu.cpp:300
 #: engines/mohawk/myst_stacks/menu.cpp:318
 #: engines/mohawk/riven_stacks/aspit.cpp:349
@@ -329,7 +329,7 @@ msgstr "???????????? ???????????? ?????????????????? ?????????????????????. ???
 #: engines/chamber/metaengine.cpp:91 engines/director/events.cpp:122
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:139
 #: engines/efh/metaengine.cpp:362 engines/grim/grim.cpp:366
-#: engines/hypno/metaengine.cpp:192 engines/kyra/gui/saveload_eob.cpp:611
+#: engines/hypno/metaengine.cpp:249 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:197
 #: engines/mm/mm1/metaengine.cpp:54 engines/parallaction/metaengine.cpp:169
 #: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
@@ -348,7 +348,7 @@ msgstr "????????????"
 #: engines/chamber/metaengine.cpp:97 engines/director/events.cpp:122
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:139
 #: engines/efh/metaengine.cpp:368 engines/grim/grim.cpp:366
-#: engines/hypno/metaengine.cpp:198 engines/kyra/gui/saveload_eob.cpp:611
+#: engines/hypno/metaengine.cpp:255 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:204
 #: engines/mm/mm1/metaengine.cpp:52 engines/parallaction/metaengine.cpp:175
 #: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
@@ -3170,7 +3170,7 @@ msgstr "????????????????????????????????????????????? ????????????????????? ???
 #: backends/events/default/default-events.cpp:358
 #: engines/adl/metaengine.cpp:352 engines/buried/metaengine.cpp:154
 #: engines/chamber/metaengine.cpp:84 engines/drascula/metaengine.cpp:304
-#: engines/hdb/metaengine.cpp:275 engines/hypno/metaengine.cpp:251
+#: engines/hdb/metaengine.cpp:275 engines/hypno/metaengine.cpp:308
 #: engines/lab/metaengine.cpp:172 engines/mohawk/myst_stacks/menu.cpp:317
 #: engines/mohawk/riven_stacks/aspit.cpp:454
 #: engines/neverhood/metaengine.cpp:200 engines/saga/metaengine.cpp:449
@@ -3674,8 +3674,9 @@ msgstr "???????????????????????? ???????????????"
 #: engines/buried/metaengine.cpp:167 engines/crab/input/input.cpp:261
 #: engines/cruise/metaengine.cpp:217 engines/dm/metaengine.cpp:180
 #: engines/grim/grim.cpp:550 engines/hdb/metaengine.cpp:258
-#: engines/hypno/metaengine.cpp:186 engines/hypno/metaengine.cpp:369
-#: engines/icb/icb.cpp:137 engines/icb/icb.cpp:203 engines/mohawk/myst.cpp:595
+#: engines/hypno/metaengine.cpp:243 engines/hypno/metaengine.cpp:339
+#: engines/hypno/metaengine.cpp:456 engines/icb/icb.cpp:137
+#: engines/icb/icb.cpp:203 engines/mohawk/myst.cpp:595
 #: engines/mohawk/riven.cpp:876 engines/ngi/metaengine.cpp:136
 #: engines/sky/metaengine.cpp:126 engines/sludge/keymapper_tables.h:52
 #: engines/sludge/keymapper_tables.h:125 engines/sludge/keymapper_tables.h:187
@@ -4558,7 +4559,7 @@ msgstr "Y"
 #: backends/platform/sdl/opendingux/opendingux.cpp:57
 #: backends/platform/sdl/ps3/ps3.cpp:50 backends/platform/sdl/psp2/psp2.cpp:51
 #: engines/bagel/metaengine.cpp:64 engines/efh/metaengine.cpp:397
-#: engines/got/metaengine.cpp:76 engines/hypno/metaengine.cpp:363
+#: engines/got/metaengine.cpp:76 engines/hypno/metaengine.cpp:449
 #: engines/mm/mm1/metaengine.cpp:50 engines/mm/mm1/metaengine.cpp:97
 #: engines/sherlock/metaengine.cpp:660 engines/sherlock/metaengine.cpp:672
 #: engines/sherlock/metaengine.cpp:1014 engines/supernova/metaengine.cpp:303
@@ -5690,7 +5691,7 @@ msgstr "?????????????????? ??????????????????????????????"
 #: engines/access/metaengine.cpp:237 engines/asylum/metaengine.cpp:171
 #: engines/asylum/metaengine.cpp:198 engines/cine/metaengine.cpp:377
 #: engines/efh/metaengine.cpp:286 engines/hdb/metaengine.cpp:213
-#: engines/hypno/metaengine.cpp:302 engines/hypno/metaengine.cpp:356
+#: engines/hypno/metaengine.cpp:363 engines/hypno/metaengine.cpp:411
 #: engines/nancy/input.cpp:195 engines/tinsel/metaengine.cpp:263
 #: engines/titanic/metaengine.cpp:324 engines/ultima/ultima8/metaengine.cpp:52
 msgid "Move up"
@@ -5700,7 +5701,7 @@ msgstr "???????????????"
 #: engines/access/metaengine.cpp:244 engines/asylum/metaengine.cpp:177
 #: engines/asylum/metaengine.cpp:205 engines/cine/metaengine.cpp:384
 #: engines/efh/metaengine.cpp:293 engines/hdb/metaengine.cpp:219
-#: engines/hypno/metaengine.cpp:290 engines/hypno/metaengine.cpp:342
+#: engines/hypno/metaengine.cpp:351 engines/hypno/metaengine.cpp:399
 #: engines/nancy/input.cpp:201 engines/tinsel/metaengine.cpp:269
 #: engines/titanic/metaengine.cpp:330 engines/ultima/ultima8/metaengine.cpp:53
 msgid "Move down"
@@ -5711,7 +5712,7 @@ msgstr "?????????????????? ????????????????????????"
 #: engines/access/metaengine.cpp:251 engines/asylum/metaengine.cpp:183
 #: engines/asylum/metaengine.cpp:212 engines/cine/metaengine.cpp:391
 #: engines/efh/metaengine.cpp:300 engines/hdb/metaengine.cpp:225
-#: engines/hypno/metaengine.cpp:284 engines/hypno/metaengine.cpp:335
+#: engines/hypno/metaengine.cpp:345 engines/hypno/metaengine.cpp:393
 #: engines/kyra/engine/eobcommon.cpp:373 engines/lure/metaengine.cpp:218
 #: engines/nancy/input.cpp:207 engines/tinsel/metaengine.cpp:275
 #: engines/titanic/metaengine.cpp:312 engines/titanic/metaengine.cpp:390
@@ -5724,7 +5725,7 @@ msgstr "???????????????????????? ??????????????????"
 #: engines/access/metaengine.cpp:258 engines/asylum/metaengine.cpp:189
 #: engines/asylum/metaengine.cpp:219 engines/cine/metaengine.cpp:398
 #: engines/efh/metaengine.cpp:307 engines/hdb/metaengine.cpp:231
-#: engines/hypno/metaengine.cpp:296 engines/hypno/metaengine.cpp:349
+#: engines/hypno/metaengine.cpp:357 engines/hypno/metaengine.cpp:405
 #: engines/kyra/engine/eobcommon.cpp:374 engines/lure/metaengine.cpp:226
 #: engines/nancy/input.cpp:213 engines/tinsel/metaengine.cpp:281
 #: engines/titanic/metaengine.cpp:318 engines/titanic/metaengine.cpp:396
@@ -6187,8 +6188,8 @@ msgstr "????????????????????? AGOS"
 #: engines/cine/metaengine.cpp:271 engines/cruise/metaengine.cpp:169
 #: engines/dm/metaengine.cpp:126 engines/draci/metaengine.cpp:196
 #: engines/drascula/metaengine.cpp:206 engines/hopkins/metaengine.cpp:207
-#: engines/hugo/metaengine.cpp:130 engines/hypno/metaengine.cpp:166
-#: engines/hypno/metaengine.cpp:267 engines/hypno/metaengine.cpp:311
+#: engines/hugo/metaengine.cpp:130 engines/hypno/metaengine.cpp:225
+#: engines/hypno/metaengine.cpp:324 engines/hypno/metaengine.cpp:372
 #: engines/illusions/metaengine.cpp:90 engines/lab/metaengine.cpp:152
 #: engines/lure/metaengine.cpp:169 engines/made/metaengine.cpp:133
 #: engines/mads/metaengine.cpp:317 engines/neverhood/metaengine.cpp:186
@@ -6828,7 +6829,7 @@ msgstr "????????????????????? ????????????????????????"
 
 #. I18N: This keymap allows skipping video cutscenes
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
-#: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
+#: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:210
 #: engines/phoenixvr/metaengine.cpp:94 engines/scumm/help.cpp:79
 #: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
@@ -7931,7 +7932,7 @@ msgstr "13 ?????????????????????????????? ??????????????????"
 msgid "Select option 14"
 msgstr "14 ?????????????????????????????? ??????????????????"
 
-#: engines/efh/metaengine.cpp:257 engines/hypno/metaengine.cpp:312
+#: engines/efh/metaengine.cpp:257 engines/hypno/metaengine.cpp:373
 #: engines/sludge/keymapper_tables.h:35 engines/supernova/metaengine.cpp:218
 msgid "Menu keymappings"
 msgstr "?????????????????? ????????????????????????????????? ????????????????????????????????????"
@@ -9385,88 +9386,158 @@ msgstr ""
 msgid "Intro keymappings"
 msgstr "???????????????????????? ????????????????????????????????? ????????????????????????????????????"
 
-#: engines/hypno/metaengine.cpp:134
+#: engines/hypno/metaengine.cpp:134 engines/hypno/metaengine.cpp:144
 msgid "Primary shoot"
 msgstr "???????????????????????? ???????????????"
 
-#: engines/hypno/metaengine.cpp:140
+#: engines/hypno/metaengine.cpp:139
 msgid "Secondary shoot"
 msgstr "????????????????????? ???????????????"
 
-#: engines/hypno/metaengine.cpp:146 engines/lab/metaengine.cpp:209
+#: engines/hypno/metaengine.cpp:149
+msgid "Rapid fire / secondary action"
+msgstr ""
+
+#: engines/hypno/metaengine.cpp:155
+#, fuzzy
+#| msgid "Slide left"
+msgid "Aim left"
+msgstr "?????????????????????????????? ????????????????????????"
+
+#: engines/hypno/metaengine.cpp:160
+#, fuzzy
+#| msgid "Stick down"
+msgid "Aim down"
+msgstr "????????????????????????"
+
+#: engines/hypno/metaengine.cpp:165
+#, fuzzy
+#| msgid "Up right"
+msgid "Aim right"
+msgstr "??????????????? ????????????????????????"
+
+#: engines/hypno/metaengine.cpp:170
+msgid "Aim up"
+msgstr ""
+
+#: engines/hypno/metaengine.cpp:175 engines/scumm/metaengine.cpp:1202
+#: engines/scumm/metaengine.cpp:1279
+msgid "Stick left"
+msgstr "??????????????????????????? ????????????????????????"
+
+#: engines/hypno/metaengine.cpp:182 engines/scumm/metaengine.cpp:1195
+#: engines/scumm/metaengine.cpp:1272
+msgid "Stick down"
+msgstr "????????????????????????"
+
+#: engines/hypno/metaengine.cpp:189 engines/scumm/metaengine.cpp:1209
+#: engines/scumm/metaengine.cpp:1286
+msgid "Stick right"
+msgstr "??????????????????????????? ????????????????????????"
+
+#: engines/hypno/metaengine.cpp:196 engines/scumm/metaengine.cpp:1188
+#: engines/scumm/metaengine.cpp:1265
+msgid "Stick up"
+msgstr "???????????????"
+
+#: engines/hypno/metaengine.cpp:203 engines/lab/metaengine.cpp:209
 #: engines/ngi/metaengine.cpp:168 engines/sherlock/metaengine.cpp:872
 #: engines/teenagent/metaengine.cpp:232 engines/toon/metaengine.cpp:98
 #: engines/voyeur/metaengine.cpp:235
 msgid "Skip intro"
 msgstr "???????????????????????? ??????????????????????????????"
 
-#: engines/hypno/metaengine.cpp:167
+#: engines/hypno/metaengine.cpp:226
 msgid "Exit menu keymappings"
 msgstr "?????????????????? ????????????????????????????????? ????????????????????????????????????????????? ??????????????????"
 
-#: engines/hypno/metaengine.cpp:168
+#: engines/hypno/metaengine.cpp:227
 msgid "Difficulty selection menu keymappings"
 msgstr "???????????????????????? ???????????????????????? ?????????????????? ????????????????????????????????? ????????????????????????????????????"
 
-#: engines/hypno/metaengine.cpp:169
+#: engines/hypno/metaengine.cpp:228
 msgid "Retry menu keymappings"
 msgstr "????????????????????? ???????????? ?????????????????? ????????????????????????????????? ????????????????????????????????????"
 
-#: engines/hypno/metaengine.cpp:172 engines/hypno/metaengine.cpp:270
-#: engines/hypno/metaengine.cpp:322
+#: engines/hypno/metaengine.cpp:231 engines/hypno/metaengine.cpp:327
+#: engines/hypno/metaengine.cpp:382
 msgid "Skip level (cheat)"
 msgstr "??????????????? ?????????????????????????????? (????????????)"
 
 #. I18N: (Game name: Soldier Boyz) player refers to the users own character.
 #. I18N: (Game name: Marvel Comics Spider-Man: The Sinister Six) player refers to the users own character.
 #. I18N: (Game name: Wetlands) player refers to the users own character.
-#: engines/hypno/metaengine.cpp:180 engines/hypno/metaengine.cpp:278
-#: engines/hypno/metaengine.cpp:329
+#: engines/hypno/metaengine.cpp:238 engines/hypno/metaengine.cpp:334
+#: engines/hypno/metaengine.cpp:388
 msgid "Kill player"
 msgstr "??????????????????????????? ??????????????????"
 
 #. I18N: (Game name: Soldier Boyz) the game has 3 difficulty levels: Chump, Punk and Badass. Chump is the easy mode.
-#: engines/hypno/metaengine.cpp:205
+#: engines/hypno/metaengine.cpp:262
 msgid "Chump"
 msgstr "??????????????????"
 
 #. I18N: (Game name: Soldier Boyz) the game has 3 difficulty levels: Chump, Punk and Badass. Punk is the medium mode.
-#: engines/hypno/metaengine.cpp:212
+#: engines/hypno/metaengine.cpp:269
 msgid "Punk"
 msgstr "???????????????"
 
 #. I18N: (Game name: Soldier Boyz) the game has 3 difficulty levels: Chump, Punk and Badass. Badass is the hard mode.
-#: engines/hypno/metaengine.cpp:219
+#: engines/hypno/metaengine.cpp:276
 msgid "Badass"
 msgstr "?????????????????? ????????????"
 
 #. I18N: (Game name: Soldier Boyz) This makes the player restart from the last checkpoint.
-#: engines/hypno/metaengine.cpp:232
+#: engines/hypno/metaengine.cpp:289
 msgid "Retry sector"
 msgstr "???????????????????????? ????????????????????? ?????????"
 
 #. I18N: (Game name: Soldier Boyz) This makes the player restart the current mission / level.
-#: engines/hypno/metaengine.cpp:239
+#: engines/hypno/metaengine.cpp:296
 msgid "Restart territory"
 msgstr "?????????????????????????????? ???????????????????????? ?????????????????????"
 
-#: engines/hypno/metaengine.cpp:245
+#: engines/hypno/metaengine.cpp:302
 msgid "Begin new mission"
 msgstr "??????????????? ?????????????????? ?????????????????????"
 
-#: engines/hypno/metaengine.cpp:313
+#: engines/hypno/metaengine.cpp:374
 msgid "Pause keymappings"
 msgstr "????????????????????????????????? ??????????????????????????????????????? ????????????????????????"
 
-#: engines/hypno/metaengine.cpp:314
+#: engines/hypno/metaengine.cpp:375
 msgid "Direction keymappings"
 msgstr "???????????????????????????????????? ????????????????????????????????? ????????????????????????????????????"
 
-#: engines/hypno/metaengine.cpp:316 engines/mads/metaengine.cpp:391
+#: engines/hypno/metaengine.cpp:377 engines/mads/metaengine.cpp:391
 #: engines/tsage/metaengine.cpp:259
 msgid "Show credits"
 msgstr "?????????????????????????????? ?????????????????????"
 
+#: engines/hypno/metaengine.cpp:417
+#, fuzzy
+#| msgid "Move left"
+msgid "Menu left"
+msgstr "???????????????????????? ??????????????????"
+
+#: engines/hypno/metaengine.cpp:425
+#, fuzzy
+#| msgid "Move down"
+msgid "Menu down"
+msgstr "?????????????????? ????????????????????????"
+
+#: engines/hypno/metaengine.cpp:433
+#, fuzzy
+#| msgid "Move right"
+msgid "Menu right"
+msgstr "???????????????????????? ??????????????????"
+
+#: engines/hypno/metaengine.cpp:441
+#, fuzzy
+#| msgid "Menu"
+msgid "Menu up"
+msgstr "???????????????"
+
 #. I18N: Action in In Cold Blood
 #: engines/icb/icb.cpp:107
 msgid "Arm"
@@ -12503,22 +12574,6 @@ msgstr ""
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1188 engines/scumm/metaengine.cpp:1265
-msgid "Stick up"
-msgstr "???????????????"
-
-#: engines/scumm/metaengine.cpp:1195 engines/scumm/metaengine.cpp:1272
-msgid "Stick down"
-msgstr "????????????????????????"
-
-#: engines/scumm/metaengine.cpp:1202 engines/scumm/metaengine.cpp:1279
-msgid "Stick left"
-msgstr "??????????????????????????? ????????????????????????"
-
-#: engines/scumm/metaengine.cpp:1209 engines/scumm/metaengine.cpp:1286
-msgid "Stick right"
-msgstr "??????????????????????????? ????????????????????????"
-
 #: engines/scumm/metaengine.cpp:1216 engines/scumm/metaengine.cpp:1293
 msgid "Fire / select"
 msgstr "?????????????????? / ????????????????????????"
diff --git a/po/ko.po b/po/ko.po
index 6061674ff79..f32cfa7332e 100644
--- a/po/ko.po
+++ b/po/ko.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-13 06:14+0000\n"
+"POT-Creation-Date: 2026-06-13 10:20+0000\n"
 "PO-Revision-Date: 2026-05-19 20:22+0000\n"
 "Last-Translator: Hoseok Seo <ddinghoya at gmail.com>\n"
 "Language-Team: Korean <https://translations.scummvm.org/projects/scummvm/"
@@ -133,7 +133,7 @@ msgstr "??????"
 #: engines/buried/saveload.cpp:83 engines/cine/saveload.cpp:857
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:123
 #: engines/drascula/saveload.cpp:51 engines/grim/inputdialog.cpp:84
-#: engines/hypno/metaengine.cpp:225 engines/mohawk/myst_stacks/menu.cpp:283
+#: engines/hypno/metaengine.cpp:282 engines/mohawk/myst_stacks/menu.cpp:283
 #: engines/mohawk/myst_stacks/menu.cpp:300
 #: engines/mohawk/myst_stacks/menu.cpp:318
 #: engines/mohawk/riven_stacks/aspit.cpp:349
@@ -336,7 +336,7 @@ msgstr "?????? ???????????? ?????? ?????? ????????????. ????????????????"
 #: engines/chamber/metaengine.cpp:91 engines/director/events.cpp:122
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:139
 #: engines/efh/metaengine.cpp:362 engines/grim/grim.cpp:366
-#: engines/hypno/metaengine.cpp:192 engines/kyra/gui/saveload_eob.cpp:611
+#: engines/hypno/metaengine.cpp:249 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:197
 #: engines/mm/mm1/metaengine.cpp:54 engines/parallaction/metaengine.cpp:169
 #: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
@@ -355,7 +355,7 @@ msgstr "???"
 #: engines/chamber/metaengine.cpp:97 engines/director/events.cpp:122
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:139
 #: engines/efh/metaengine.cpp:368 engines/grim/grim.cpp:366
-#: engines/hypno/metaengine.cpp:198 engines/kyra/gui/saveload_eob.cpp:611
+#: engines/hypno/metaengine.cpp:255 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:204
 #: engines/mm/mm1/metaengine.cpp:52 engines/parallaction/metaengine.cpp:175
 #: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
@@ -3347,7 +3347,7 @@ msgstr "????????? ?????? ?????? ??????"
 #: backends/events/default/default-events.cpp:358
 #: engines/adl/metaengine.cpp:352 engines/buried/metaengine.cpp:154
 #: engines/chamber/metaengine.cpp:84 engines/drascula/metaengine.cpp:304
-#: engines/hdb/metaengine.cpp:275 engines/hypno/metaengine.cpp:251
+#: engines/hdb/metaengine.cpp:275 engines/hypno/metaengine.cpp:308
 #: engines/lab/metaengine.cpp:172 engines/mohawk/myst_stacks/menu.cpp:317
 #: engines/mohawk/riven_stacks/aspit.cpp:454
 #: engines/neverhood/metaengine.cpp:200 engines/saga/metaengine.cpp:449
@@ -3903,8 +3903,9 @@ msgstr "????????? ??????"
 #: engines/buried/metaengine.cpp:167 engines/crab/input/input.cpp:261
 #: engines/cruise/metaengine.cpp:217 engines/dm/metaengine.cpp:180
 #: engines/grim/grim.cpp:550 engines/hdb/metaengine.cpp:258
-#: engines/hypno/metaengine.cpp:186 engines/hypno/metaengine.cpp:369
-#: engines/icb/icb.cpp:137 engines/icb/icb.cpp:203 engines/mohawk/myst.cpp:595
+#: engines/hypno/metaengine.cpp:243 engines/hypno/metaengine.cpp:339
+#: engines/hypno/metaengine.cpp:456 engines/icb/icb.cpp:137
+#: engines/icb/icb.cpp:203 engines/mohawk/myst.cpp:595
 #: engines/mohawk/riven.cpp:876 engines/ngi/metaengine.cpp:136
 #: engines/sky/metaengine.cpp:126 engines/sludge/keymapper_tables.h:52
 #: engines/sludge/keymapper_tables.h:125 engines/sludge/keymapper_tables.h:187
@@ -4800,7 +4801,7 @@ msgstr "Y ??????"
 #: backends/platform/sdl/opendingux/opendingux.cpp:57
 #: backends/platform/sdl/ps3/ps3.cpp:50 backends/platform/sdl/psp2/psp2.cpp:51
 #: engines/bagel/metaengine.cpp:64 engines/efh/metaengine.cpp:397
-#: engines/got/metaengine.cpp:76 engines/hypno/metaengine.cpp:363
+#: engines/got/metaengine.cpp:76 engines/hypno/metaengine.cpp:449
 #: engines/mm/mm1/metaengine.cpp:50 engines/mm/mm1/metaengine.cpp:97
 #: engines/sherlock/metaengine.cpp:660 engines/sherlock/metaengine.cpp:672
 #: engines/sherlock/metaengine.cpp:1014 engines/supernova/metaengine.cpp:303
@@ -6240,7 +6241,7 @@ msgstr "????????? ????????????"
 #: engines/access/metaengine.cpp:237 engines/asylum/metaengine.cpp:171
 #: engines/asylum/metaengine.cpp:198 engines/cine/metaengine.cpp:377
 #: engines/efh/metaengine.cpp:286 engines/hdb/metaengine.cpp:213
-#: engines/hypno/metaengine.cpp:302 engines/hypno/metaengine.cpp:356
+#: engines/hypno/metaengine.cpp:363 engines/hypno/metaengine.cpp:411
 #: engines/nancy/input.cpp:195 engines/tinsel/metaengine.cpp:263
 #: engines/titanic/metaengine.cpp:324 engines/ultima/ultima8/metaengine.cpp:52
 msgid "Move up"
@@ -6250,7 +6251,7 @@ msgstr "?????? ??????"
 #: engines/access/metaengine.cpp:244 engines/asylum/metaengine.cpp:177
 #: engines/asylum/metaengine.cpp:205 engines/cine/metaengine.cpp:384
 #: engines/efh/metaengine.cpp:293 engines/hdb/metaengine.cpp:219
-#: engines/hypno/metaengine.cpp:290 engines/hypno/metaengine.cpp:342
+#: engines/hypno/metaengine.cpp:351 engines/hypno/metaengine.cpp:399
 #: engines/nancy/input.cpp:201 engines/tinsel/metaengine.cpp:269
 #: engines/titanic/metaengine.cpp:330 engines/ultima/ultima8/metaengine.cpp:53
 msgid "Move down"
@@ -6261,7 +6262,7 @@ msgstr "????????? ??????"
 #: engines/access/metaengine.cpp:251 engines/asylum/metaengine.cpp:183
 #: engines/asylum/metaengine.cpp:212 engines/cine/metaengine.cpp:391
 #: engines/efh/metaengine.cpp:300 engines/hdb/metaengine.cpp:225
-#: engines/hypno/metaengine.cpp:284 engines/hypno/metaengine.cpp:335
+#: engines/hypno/metaengine.cpp:345 engines/hypno/metaengine.cpp:393
 #: engines/kyra/engine/eobcommon.cpp:373 engines/lure/metaengine.cpp:218
 #: engines/nancy/input.cpp:207 engines/tinsel/metaengine.cpp:275
 #: engines/titanic/metaengine.cpp:312 engines/titanic/metaengine.cpp:390
@@ -6274,7 +6275,7 @@ msgstr "???????????? ??????"
 #: engines/access/metaengine.cpp:258 engines/asylum/metaengine.cpp:189
 #: engines/asylum/metaengine.cpp:219 engines/cine/metaengine.cpp:398
 #: engines/efh/metaengine.cpp:307 engines/hdb/metaengine.cpp:231
-#: engines/hypno/metaengine.cpp:296 engines/hypno/metaengine.cpp:349
+#: engines/hypno/metaengine.cpp:357 engines/hypno/metaengine.cpp:405
 #: engines/kyra/engine/eobcommon.cpp:374 engines/lure/metaengine.cpp:226
 #: engines/nancy/input.cpp:213 engines/tinsel/metaengine.cpp:281
 #: engines/titanic/metaengine.cpp:318 engines/titanic/metaengine.cpp:396
@@ -6759,8 +6760,8 @@ msgstr "AGOS ??????"
 #: engines/cine/metaengine.cpp:271 engines/cruise/metaengine.cpp:169
 #: engines/dm/metaengine.cpp:126 engines/draci/metaengine.cpp:196
 #: engines/drascula/metaengine.cpp:206 engines/hopkins/metaengine.cpp:207
-#: engines/hugo/metaengine.cpp:130 engines/hypno/metaengine.cpp:166
-#: engines/hypno/metaengine.cpp:267 engines/hypno/metaengine.cpp:311
+#: engines/hugo/metaengine.cpp:130 engines/hypno/metaengine.cpp:225
+#: engines/hypno/metaengine.cpp:324 engines/hypno/metaengine.cpp:372
 #: engines/illusions/metaengine.cpp:90 engines/lab/metaengine.cpp:152
 #: engines/lure/metaengine.cpp:169 engines/made/metaengine.cpp:133
 #: engines/mads/metaengine.cpp:317 engines/neverhood/metaengine.cpp:186
@@ -7442,7 +7443,7 @@ msgstr "?????? ??????"
 
 #. I18N: This keymap allows skipping video cutscenes
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
-#: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
+#: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:210
 #: engines/phoenixvr/metaengine.cpp:94 engines/scumm/help.cpp:79
 #: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
@@ -8584,7 +8585,7 @@ msgstr "?????? 13 ??????"
 msgid "Select option 14"
 msgstr "?????? 14 ??????"
 
-#: engines/efh/metaengine.cpp:257 engines/hypno/metaengine.cpp:312
+#: engines/efh/metaengine.cpp:257 engines/hypno/metaengine.cpp:373
 #: engines/sludge/keymapper_tables.h:35 engines/supernova/metaengine.cpp:218
 msgid "Menu keymappings"
 msgstr "?????? ?????????"
@@ -10077,88 +10078,166 @@ msgstr "?????? ???????????? ??????????????? ?????? ?????? ???????????? ?????????
 msgid "Intro keymappings"
 msgstr "????????? ?????????"
 
-#: engines/hypno/metaengine.cpp:134
+#: engines/hypno/metaengine.cpp:134 engines/hypno/metaengine.cpp:144
 msgid "Primary shoot"
 msgstr "1??? ??????"
 
-#: engines/hypno/metaengine.cpp:140
+#: engines/hypno/metaengine.cpp:139
 msgid "Secondary shoot"
 msgstr "2??? ??????"
 
-#: engines/hypno/metaengine.cpp:146 engines/lab/metaengine.cpp:209
+#: engines/hypno/metaengine.cpp:149
+msgid "Rapid fire / secondary action"
+msgstr ""
+
+#: engines/hypno/metaengine.cpp:155
+#, fuzzy
+#| msgid "Slide left"
+msgid "Aim left"
+msgstr "???????????? ????????????"
+
+#: engines/hypno/metaengine.cpp:160
+#, fuzzy
+#| msgid "Scroll down"
+msgid "Aim down"
+msgstr "????????? ?????????"
+
+#: engines/hypno/metaengine.cpp:165
+#, fuzzy
+#| msgid "Up right"
+msgid "Aim right"
+msgstr "????????? ??????"
+
+#: engines/hypno/metaengine.cpp:170
+msgid "Aim up"
+msgstr ""
+
+#: engines/hypno/metaengine.cpp:175 engines/scumm/metaengine.cpp:1202
+#: engines/scumm/metaengine.cpp:1279
+#, fuzzy
+#| msgid "Slide left"
+msgid "Stick left"
+msgstr "???????????? ????????????"
+
+#: engines/hypno/metaengine.cpp:182 engines/scumm/metaengine.cpp:1195
+#: engines/scumm/metaengine.cpp:1272
+#, fuzzy
+#| msgid "Scroll down"
+msgid "Stick down"
+msgstr "????????? ?????????"
+
+#: engines/hypno/metaengine.cpp:189 engines/scumm/metaengine.cpp:1209
+#: engines/scumm/metaengine.cpp:1286
+#, fuzzy
+#| msgid "Slide right"
+msgid "Stick right"
+msgstr "????????? ????????????"
+
+#: engines/hypno/metaengine.cpp:196 engines/scumm/metaengine.cpp:1188
+#: engines/scumm/metaengine.cpp:1265
+#, fuzzy
+#| msgid "Pick up"
+msgid "Stick up"
+msgstr "??????"
+
+#: engines/hypno/metaengine.cpp:203 engines/lab/metaengine.cpp:209
 #: engines/ngi/metaengine.cpp:168 engines/sherlock/metaengine.cpp:872
 #: engines/teenagent/metaengine.cpp:232 engines/toon/metaengine.cpp:98
 #: engines/voyeur/metaengine.cpp:235
 msgid "Skip intro"
 msgstr "????????? ????????????"
 
-#: engines/hypno/metaengine.cpp:167
+#: engines/hypno/metaengine.cpp:226
 msgid "Exit menu keymappings"
 msgstr "????????? ?????? ?????????"
 
-#: engines/hypno/metaengine.cpp:168
+#: engines/hypno/metaengine.cpp:227
 msgid "Difficulty selection menu keymappings"
 msgstr "????????? ?????? ?????? ?????????"
 
-#: engines/hypno/metaengine.cpp:169
+#: engines/hypno/metaengine.cpp:228
 msgid "Retry menu keymappings"
 msgstr "?????? ????????? ?????????"
 
-#: engines/hypno/metaengine.cpp:172 engines/hypno/metaengine.cpp:270
-#: engines/hypno/metaengine.cpp:322
+#: engines/hypno/metaengine.cpp:231 engines/hypno/metaengine.cpp:327
+#: engines/hypno/metaengine.cpp:382
 msgid "Skip level (cheat)"
 msgstr "?????? ???????????? (??????)"
 
 #. I18N: (Game name: Soldier Boyz) player refers to the users own character.
 #. I18N: (Game name: Marvel Comics Spider-Man: The Sinister Six) player refers to the users own character.
 #. I18N: (Game name: Wetlands) player refers to the users own character.
-#: engines/hypno/metaengine.cpp:180 engines/hypno/metaengine.cpp:278
-#: engines/hypno/metaengine.cpp:329
+#: engines/hypno/metaengine.cpp:238 engines/hypno/metaengine.cpp:334
+#: engines/hypno/metaengine.cpp:388
 msgid "Kill player"
 msgstr "???????????? ?????????"
 
 #. I18N: (Game name: Soldier Boyz) the game has 3 difficulty levels: Chump, Punk and Badass. Chump is the easy mode.
-#: engines/hypno/metaengine.cpp:205
+#: engines/hypno/metaengine.cpp:262
 msgid "Chump"
 msgstr "??????"
 
 #. I18N: (Game name: Soldier Boyz) the game has 3 difficulty levels: Chump, Punk and Badass. Punk is the medium mode.
-#: engines/hypno/metaengine.cpp:212
+#: engines/hypno/metaengine.cpp:269
 msgid "Punk"
 msgstr "?????????"
 
 #. I18N: (Game name: Soldier Boyz) the game has 3 difficulty levels: Chump, Punk and Badass. Badass is the hard mode.
-#: engines/hypno/metaengine.cpp:219
+#: engines/hypno/metaengine.cpp:276
 msgid "Badass"
 msgstr "??????"
 
 #. I18N: (Game name: Soldier Boyz) This makes the player restart from the last checkpoint.
-#: engines/hypno/metaengine.cpp:232
+#: engines/hypno/metaengine.cpp:289
 msgid "Retry sector"
 msgstr "????????? ??????"
 
 #. I18N: (Game name: Soldier Boyz) This makes the player restart the current mission / level.
-#: engines/hypno/metaengine.cpp:239
+#: engines/hypno/metaengine.cpp:296
 msgid "Restart territory"
 msgstr "?????? ?????????"
 
-#: engines/hypno/metaengine.cpp:245
+#: engines/hypno/metaengine.cpp:302
 msgid "Begin new mission"
 msgstr "????????? ?????? ??????"
 
-#: engines/hypno/metaengine.cpp:313
+#: engines/hypno/metaengine.cpp:374
 msgid "Pause keymappings"
 msgstr "?????? ?????? ?????????"
 
-#: engines/hypno/metaengine.cpp:314
+#: engines/hypno/metaengine.cpp:375
 msgid "Direction keymappings"
 msgstr "?????? ?????????"
 
-#: engines/hypno/metaengine.cpp:316 engines/mads/metaengine.cpp:391
+#: engines/hypno/metaengine.cpp:377 engines/mads/metaengine.cpp:391
 #: engines/tsage/metaengine.cpp:259
 msgid "Show credits"
 msgstr "????????? ??????"
 
+#: engines/hypno/metaengine.cpp:417
+#, fuzzy
+#| msgid "Move left"
+msgid "Menu left"
+msgstr "???????????? ??????"
+
+#: engines/hypno/metaengine.cpp:425
+#, fuzzy
+#| msgid "Move down"
+msgid "Menu down"
+msgstr "????????? ??????"
+
+#: engines/hypno/metaengine.cpp:433
+#, fuzzy
+#| msgid "Move right"
+msgid "Menu right"
+msgstr "??????????????? ??????"
+
+#: engines/hypno/metaengine.cpp:441
+#, fuzzy
+#| msgid "Menu"
+msgid "Menu up"
+msgstr "??????"
+
 #. I18N: Action in In Cold Blood
 #: engines/icb/icb.cpp:107
 msgid "Arm"
@@ -13421,30 +13500,6 @@ msgstr ""
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1188 engines/scumm/metaengine.cpp:1265
-#, fuzzy
-#| msgid "Pick up"
-msgid "Stick up"
-msgstr "??????"
-
-#: engines/scumm/metaengine.cpp:1195 engines/scumm/metaengine.cpp:1272
-#, fuzzy
-#| msgid "Scroll down"
-msgid "Stick down"
-msgstr "????????? ?????????"
-
-#: engines/scumm/metaengine.cpp:1202 engines/scumm/metaengine.cpp:1279
-#, fuzzy
-#| msgid "Slide left"
-msgid "Stick left"
-msgstr "???????????? ????????????"
-
-#: engines/scumm/metaengine.cpp:1209 engines/scumm/metaengine.cpp:1286
-#, fuzzy
-#| msgid "Slide right"
-msgid "Stick right"
-msgstr "????????? ????????????"
-
 #: engines/scumm/metaengine.cpp:1216 engines/scumm/metaengine.cpp:1293
 #, fuzzy
 #| msgid "Move / Select"
diff --git a/po/nb_NO.po b/po/nb_NO.po
index 1e0639502f2..4ddfe236855 100644
--- a/po/nb_NO.po
+++ b/po/nb_NO.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-13 06:14+0000\n"
+"POT-Creation-Date: 2026-06-13 10:20+0000\n"
 "PO-Revision-Date: 2026-02-07 15:22+0000\n"
 "Last-Translator: Einar Johan Tr??an S??m??en <einarjohants at gmail.com>\n"
 "Language-Team: Norwegian Bokm??l <https://translations.scummvm.org/projects/"
@@ -135,7 +135,7 @@ msgstr "Oppover"
 #: engines/buried/saveload.cpp:83 engines/cine/saveload.cpp:857
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:123
 #: engines/drascula/saveload.cpp:51 engines/grim/inputdialog.cpp:84
-#: engines/hypno/metaengine.cpp:225 engines/mohawk/myst_stacks/menu.cpp:283
+#: engines/hypno/metaengine.cpp:282 engines/mohawk/myst_stacks/menu.cpp:283
 #: engines/mohawk/myst_stacks/menu.cpp:300
 #: engines/mohawk/myst_stacks/menu.cpp:318
 #: engines/mohawk/riven_stacks/aspit.cpp:349
@@ -338,7 +338,7 @@ msgstr "Et annet fillager er aktivt. ??nsker du ?? avbryte det?"
 #: engines/chamber/metaengine.cpp:91 engines/director/events.cpp:122
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:139
 #: engines/efh/metaengine.cpp:362 engines/grim/grim.cpp:366
-#: engines/hypno/metaengine.cpp:192 engines/kyra/gui/saveload_eob.cpp:611
+#: engines/hypno/metaengine.cpp:249 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:197
 #: engines/mm/mm1/metaengine.cpp:54 engines/parallaction/metaengine.cpp:169
 #: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
@@ -357,7 +357,7 @@ msgstr "Ja"
 #: engines/chamber/metaengine.cpp:97 engines/director/events.cpp:122
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:139
 #: engines/efh/metaengine.cpp:368 engines/grim/grim.cpp:366
-#: engines/hypno/metaengine.cpp:198 engines/kyra/gui/saveload_eob.cpp:611
+#: engines/hypno/metaengine.cpp:255 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:204
 #: engines/mm/mm1/metaengine.cpp:52 engines/parallaction/metaengine.cpp:175
 #: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
@@ -3456,7 +3456,7 @@ msgstr "Spillmotor for grafikkbaserte eventyr"
 #: backends/events/default/default-events.cpp:358
 #: engines/adl/metaengine.cpp:352 engines/buried/metaengine.cpp:154
 #: engines/chamber/metaengine.cpp:84 engines/drascula/metaengine.cpp:304
-#: engines/hdb/metaengine.cpp:275 engines/hypno/metaengine.cpp:251
+#: engines/hdb/metaengine.cpp:275 engines/hypno/metaengine.cpp:308
 #: engines/lab/metaengine.cpp:172 engines/mohawk/myst_stacks/menu.cpp:317
 #: engines/mohawk/riven_stacks/aspit.cpp:454
 #: engines/neverhood/metaengine.cpp:200 engines/saga/metaengine.cpp:449
@@ -4037,8 +4037,9 @@ msgstr "H??yreklikk"
 #: engines/buried/metaengine.cpp:167 engines/crab/input/input.cpp:261
 #: engines/cruise/metaengine.cpp:217 engines/dm/metaengine.cpp:180
 #: engines/grim/grim.cpp:550 engines/hdb/metaengine.cpp:258
-#: engines/hypno/metaengine.cpp:186 engines/hypno/metaengine.cpp:369
-#: engines/icb/icb.cpp:137 engines/icb/icb.cpp:203 engines/mohawk/myst.cpp:595
+#: engines/hypno/metaengine.cpp:243 engines/hypno/metaengine.cpp:339
+#: engines/hypno/metaengine.cpp:456 engines/icb/icb.cpp:137
+#: engines/icb/icb.cpp:203 engines/mohawk/myst.cpp:595
 #: engines/mohawk/riven.cpp:876 engines/ngi/metaengine.cpp:136
 #: engines/sky/metaengine.cpp:126 engines/sludge/keymapper_tables.h:52
 #: engines/sludge/keymapper_tables.h:125 engines/sludge/keymapper_tables.h:187
@@ -4982,7 +4983,7 @@ msgstr "Y"
 #: backends/platform/sdl/opendingux/opendingux.cpp:57
 #: backends/platform/sdl/ps3/ps3.cpp:50 backends/platform/sdl/psp2/psp2.cpp:51
 #: engines/bagel/metaengine.cpp:64 engines/efh/metaengine.cpp:397
-#: engines/got/metaengine.cpp:76 engines/hypno/metaengine.cpp:363
+#: engines/got/metaengine.cpp:76 engines/hypno/metaengine.cpp:449
 #: engines/mm/mm1/metaengine.cpp:50 engines/mm/mm1/metaengine.cpp:97
 #: engines/sherlock/metaengine.cpp:660 engines/sherlock/metaengine.cpp:672
 #: engines/sherlock/metaengine.cpp:1014 engines/supernova/metaengine.cpp:303
@@ -6643,7 +6644,7 @@ msgstr "Hopp over linje"
 #: engines/access/metaengine.cpp:237 engines/asylum/metaengine.cpp:171
 #: engines/asylum/metaengine.cpp:198 engines/cine/metaengine.cpp:377
 #: engines/efh/metaengine.cpp:286 engines/hdb/metaengine.cpp:213
-#: engines/hypno/metaengine.cpp:302 engines/hypno/metaengine.cpp:356
+#: engines/hypno/metaengine.cpp:363 engines/hypno/metaengine.cpp:411
 #: engines/nancy/input.cpp:195 engines/tinsel/metaengine.cpp:263
 #: engines/titanic/metaengine.cpp:324 engines/ultima/ultima8/metaengine.cpp:52
 msgid "Move up"
@@ -6653,7 +6654,7 @@ msgstr "G?? oppover"
 #: engines/access/metaengine.cpp:244 engines/asylum/metaengine.cpp:177
 #: engines/asylum/metaengine.cpp:205 engines/cine/metaengine.cpp:384
 #: engines/efh/metaengine.cpp:293 engines/hdb/metaengine.cpp:219
-#: engines/hypno/metaengine.cpp:290 engines/hypno/metaengine.cpp:342
+#: engines/hypno/metaengine.cpp:351 engines/hypno/metaengine.cpp:399
 #: engines/nancy/input.cpp:201 engines/tinsel/metaengine.cpp:269
 #: engines/titanic/metaengine.cpp:330 engines/ultima/ultima8/metaengine.cpp:53
 msgid "Move down"
@@ -6664,7 +6665,7 @@ msgstr "G?? nedover"
 #: engines/access/metaengine.cpp:251 engines/asylum/metaengine.cpp:183
 #: engines/asylum/metaengine.cpp:212 engines/cine/metaengine.cpp:391
 #: engines/efh/metaengine.cpp:300 engines/hdb/metaengine.cpp:225
-#: engines/hypno/metaengine.cpp:284 engines/hypno/metaengine.cpp:335
+#: engines/hypno/metaengine.cpp:345 engines/hypno/metaengine.cpp:393
 #: engines/kyra/engine/eobcommon.cpp:373 engines/lure/metaengine.cpp:218
 #: engines/nancy/input.cpp:207 engines/tinsel/metaengine.cpp:275
 #: engines/titanic/metaengine.cpp:312 engines/titanic/metaengine.cpp:390
@@ -6677,7 +6678,7 @@ msgstr "G?? til venstre"
 #: engines/access/metaengine.cpp:258 engines/asylum/metaengine.cpp:189
 #: engines/asylum/metaengine.cpp:219 engines/cine/metaengine.cpp:398
 #: engines/efh/metaengine.cpp:307 engines/hdb/metaengine.cpp:231
-#: engines/hypno/metaengine.cpp:296 engines/hypno/metaengine.cpp:349
+#: engines/hypno/metaengine.cpp:357 engines/hypno/metaengine.cpp:405
 #: engines/kyra/engine/eobcommon.cpp:374 engines/lure/metaengine.cpp:226
 #: engines/nancy/input.cpp:213 engines/tinsel/metaengine.cpp:281
 #: engines/titanic/metaengine.cpp:318 engines/titanic/metaengine.cpp:396
@@ -7170,8 +7171,8 @@ msgstr "AGOS prim??r"
 #: engines/cine/metaengine.cpp:271 engines/cruise/metaengine.cpp:169
 #: engines/dm/metaengine.cpp:126 engines/draci/metaengine.cpp:196
 #: engines/drascula/metaengine.cpp:206 engines/hopkins/metaengine.cpp:207
-#: engines/hugo/metaengine.cpp:130 engines/hypno/metaengine.cpp:166
-#: engines/hypno/metaengine.cpp:267 engines/hypno/metaengine.cpp:311
+#: engines/hugo/metaengine.cpp:130 engines/hypno/metaengine.cpp:225
+#: engines/hypno/metaengine.cpp:324 engines/hypno/metaengine.cpp:372
 #: engines/illusions/metaengine.cpp:90 engines/lab/metaengine.cpp:152
 #: engines/lure/metaengine.cpp:169 engines/made/metaengine.cpp:133
 #: engines/mads/metaengine.cpp:317 engines/neverhood/metaengine.cpp:186
@@ -7908,7 +7909,7 @@ msgstr "Skru kampmodus av/p??"
 
 #. I18N: This keymap allows skipping video cutscenes
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
-#: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
+#: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:210
 #: engines/phoenixvr/metaengine.cpp:94 engines/scumm/help.cpp:79
 #: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
@@ -9244,7 +9245,7 @@ msgstr "Velg Gruppere etter"
 msgid "Select option 14"
 msgstr "Velg Gruppere etter"
 
-#: engines/efh/metaengine.cpp:257 engines/hypno/metaengine.cpp:312
+#: engines/efh/metaengine.cpp:257 engines/hypno/metaengine.cpp:373
 #: engines/sludge/keymapper_tables.h:35 engines/supernova/metaengine.cpp:218
 #, fuzzy
 #| msgid "Game keymappings"
@@ -10954,17 +10955,73 @@ msgstr ""
 msgid "Intro keymappings"
 msgstr "Indeks-tasteoppsett"
 
-#: engines/hypno/metaengine.cpp:134
+#: engines/hypno/metaengine.cpp:134 engines/hypno/metaengine.cpp:144
 msgid "Primary shoot"
 msgstr ""
 
-#: engines/hypno/metaengine.cpp:140
+#: engines/hypno/metaengine.cpp:139
 #, fuzzy
 #| msgid "Seconds"
 msgid "Secondary shoot"
 msgstr "Sekunder"
 
-#: engines/hypno/metaengine.cpp:146 engines/lab/metaengine.cpp:209
+#: engines/hypno/metaengine.cpp:149
+#, fuzzy
+#| msgid "Map hide toolbar action"
+msgid "Rapid fire / secondary action"
+msgstr "Koble skjul-verkt??ylinje-handlingen"
+
+#: engines/hypno/metaengine.cpp:155
+#, fuzzy
+#| msgid "Slide Left"
+msgid "Aim left"
+msgstr "Skli mot Venstre"
+
+#: engines/hypno/metaengine.cpp:160
+#, fuzzy
+#| msgid "Scroll down"
+msgid "Aim down"
+msgstr "Bla ned"
+
+#: engines/hypno/metaengine.cpp:165
+#, fuzzy
+#| msgid "Up Right"
+msgid "Aim right"
+msgstr "Opp til h??yre"
+
+#: engines/hypno/metaengine.cpp:170
+msgid "Aim up"
+msgstr ""
+
+#: engines/hypno/metaengine.cpp:175 engines/scumm/metaengine.cpp:1202
+#: engines/scumm/metaengine.cpp:1279
+#, fuzzy
+#| msgid "Slide Left"
+msgid "Stick left"
+msgstr "Skli mot Venstre"
+
+#: engines/hypno/metaengine.cpp:182 engines/scumm/metaengine.cpp:1195
+#: engines/scumm/metaengine.cpp:1272
+#, fuzzy
+#| msgid "Scroll down"
+msgid "Stick down"
+msgstr "Bla ned"
+
+#: engines/hypno/metaengine.cpp:189 engines/scumm/metaengine.cpp:1209
+#: engines/scumm/metaengine.cpp:1286
+#, fuzzy
+#| msgid "Slide Right"
+msgid "Stick right"
+msgstr "Skli mot H??yre"
+
+#: engines/hypno/metaengine.cpp:196 engines/scumm/metaengine.cpp:1188
+#: engines/scumm/metaengine.cpp:1265
+#, fuzzy
+#| msgid "Pick up"
+msgid "Stick up"
+msgstr "Plukk opp"
+
+#: engines/hypno/metaengine.cpp:203 engines/lab/metaengine.cpp:209
 #: engines/ngi/metaengine.cpp:168 engines/sherlock/metaengine.cpp:872
 #: engines/teenagent/metaengine.cpp:232 engines/toon/metaengine.cpp:98
 #: engines/voyeur/metaengine.cpp:235
@@ -10973,26 +11030,26 @@ msgstr "Sekunder"
 msgid "Skip intro"
 msgstr "Hopp over linje"
 
-#: engines/hypno/metaengine.cpp:167
+#: engines/hypno/metaengine.cpp:226
 #, fuzzy
 #| msgid "Escape keymappings"
 msgid "Exit menu keymappings"
 msgstr "Escape-tastoppsett"
 
-#: engines/hypno/metaengine.cpp:168
+#: engines/hypno/metaengine.cpp:227
 #, fuzzy
 #| msgid "Index keymappings"
 msgid "Difficulty selection menu keymappings"
 msgstr "Indeks-tasteoppsett"
 
-#: engines/hypno/metaengine.cpp:169
+#: engines/hypno/metaengine.cpp:228
 #, fuzzy
 #| msgid "Start menu keymappings"
 msgid "Retry menu keymappings"
 msgstr "Startmeny-tasteoppsett"
 
-#: engines/hypno/metaengine.cpp:172 engines/hypno/metaengine.cpp:270
-#: engines/hypno/metaengine.cpp:322
+#: engines/hypno/metaengine.cpp:231 engines/hypno/metaengine.cpp:327
+#: engines/hypno/metaengine.cpp:382
 #, fuzzy
 #| msgid "Speech"
 msgid "Skip level (cheat)"
@@ -11001,61 +11058,85 @@ msgstr "Tale"
 #. I18N: (Game name: Soldier Boyz) player refers to the users own character.
 #. I18N: (Game name: Marvel Comics Spider-Man: The Sinister Six) player refers to the users own character.
 #. I18N: (Game name: Wetlands) player refers to the users own character.
-#: engines/hypno/metaengine.cpp:180 engines/hypno/metaengine.cpp:278
-#: engines/hypno/metaengine.cpp:329
+#: engines/hypno/metaengine.cpp:238 engines/hypno/metaengine.cpp:334
+#: engines/hypno/metaengine.cpp:388
 msgid "Kill player"
 msgstr "Drep spiller"
 
 #. I18N: (Game name: Soldier Boyz) the game has 3 difficulty levels: Chump, Punk and Badass. Chump is the easy mode.
-#: engines/hypno/metaengine.cpp:205
+#: engines/hypno/metaengine.cpp:262
 msgid "Chump"
 msgstr ""
 
 #. I18N: (Game name: Soldier Boyz) the game has 3 difficulty levels: Chump, Punk and Badass. Punk is the medium mode.
-#: engines/hypno/metaengine.cpp:212
+#: engines/hypno/metaengine.cpp:269
 msgid "Punk"
 msgstr ""
 
 #. I18N: (Game name: Soldier Boyz) the game has 3 difficulty levels: Chump, Punk and Badass. Badass is the hard mode.
-#: engines/hypno/metaengine.cpp:219
+#: engines/hypno/metaengine.cpp:276
 msgid "Badass"
 msgstr "T??ffing"
 
 #. I18N: (Game name: Soldier Boyz) This makes the player restart from the last checkpoint.
-#: engines/hypno/metaengine.cpp:232
+#: engines/hypno/metaengine.cpp:289
 #, fuzzy
 #| msgid "Retreat"
 msgid "Retry sector"
 msgstr "Trekk tilbake"
 
 #. I18N: (Game name: Soldier Boyz) This makes the player restart the current mission / level.
-#: engines/hypno/metaengine.cpp:239
+#: engines/hypno/metaengine.cpp:296
 #, fuzzy
 #| msgid "Restart animation"
 msgid "Restart territory"
 msgstr "Start animasjon p?? nytt"
 
-#: engines/hypno/metaengine.cpp:245
+#: engines/hypno/metaengine.cpp:302
 msgid "Begin new mission"
 msgstr "Begynn nytt oppdrag"
 
-#: engines/hypno/metaengine.cpp:313
+#: engines/hypno/metaengine.cpp:374
 #, fuzzy
 #| msgid "Game keymappings"
 msgid "Pause keymappings"
 msgstr "Spilltasteoppsett"
 
-#: engines/hypno/metaengine.cpp:314
+#: engines/hypno/metaengine.cpp:375
 #, fuzzy
 #| msgid "Index keymappings"
 msgid "Direction keymappings"
 msgstr "Indeks-tasteoppsett"
 
-#: engines/hypno/metaengine.cpp:316 engines/mads/metaengine.cpp:391
+#: engines/hypno/metaengine.cpp:377 engines/mads/metaengine.cpp:391
 #: engines/tsage/metaengine.cpp:259
 msgid "Show credits"
 msgstr "Vis medvirkende"
 
+#: engines/hypno/metaengine.cpp:417
+#, fuzzy
+#| msgid "Move left"
+msgid "Menu left"
+msgstr "G?? til venstre"
+
+#: engines/hypno/metaengine.cpp:425
+#, fuzzy
+#| msgid "Move down"
+msgid "Menu down"
+msgstr "G?? nedover"
+
+#: engines/hypno/metaengine.cpp:433
+#, fuzzy
+#| msgid "Move right"
+msgid "Menu right"
+msgstr "G?? til h??yre"
+
+#: engines/hypno/metaengine.cpp:441
+#, fuzzy
+#| msgid "Menu"
+msgid "Menu up"
+msgstr "Meny"
+
 #. I18N: Action in In Cold Blood
 #: engines/icb/icb.cpp:107
 msgid "Arm"
@@ -14591,30 +14672,6 @@ msgstr ""
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1188 engines/scumm/metaengine.cpp:1265
-#, fuzzy
-#| msgid "Pick up"
-msgid "Stick up"
-msgstr "Plukk opp"
-
-#: engines/scumm/metaengine.cpp:1195 engines/scumm/metaengine.cpp:1272
-#, fuzzy
-#| msgid "Scroll down"
-msgid "Stick down"
-msgstr "Bla ned"
-
-#: engines/scumm/metaengine.cpp:1202 engines/scumm/metaengine.cpp:1279
-#, fuzzy
-#| msgid "Slide Left"
-msgid "Stick left"
-msgstr "Skli mot Venstre"
-
-#: engines/scumm/metaengine.cpp:1209 engines/scumm/metaengine.cpp:1286
-#, fuzzy
-#| msgid "Slide Right"
-msgid "Stick right"
-msgstr "Skli mot H??yre"
-
 #: engines/scumm/metaengine.cpp:1216 engines/scumm/metaengine.cpp:1293
 #, fuzzy
 #| msgid "Move left"
@@ -20273,9 +20330,6 @@ msgstr ""
 #~ msgstr ""
 #~ "Du m?? koble en tast til handlingen 'H??yreklikk' for ?? spille dette spillet"
 
-#~ msgid "Map hide toolbar action"
-#~ msgstr "Koble skjul-verkt??ylinje-handlingen"
-
 #~ msgid "You must map a key to the 'Hide toolbar' action to play this game"
 #~ msgstr ""
 #~ "Du m?? koble en tast til 'Skjul verkt??ylinje'-handlingen for ?? spille "
diff --git a/po/nl_NL.po b/po/nl_NL.po
index ef3c8ce5352..ecbc342a222 100644
--- a/po/nl_NL.po
+++ b/po/nl_NL.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.9.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-13 06:14+0000\n"
+"POT-Creation-Date: 2026-06-13 10:20+0000\n"
 "PO-Revision-Date: 2026-02-11 14:23+0000\n"
 "Last-Translator: SecularSteve <fairfull.playing at gmail.com>\n"
 "Language-Team: Dutch <https://translations.scummvm.org/projects/scummvm/"
@@ -135,7 +135,7 @@ msgstr "Ga omhoog"
 #: engines/buried/saveload.cpp:83 engines/cine/saveload.cpp:857
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:123
 #: engines/drascula/saveload.cpp:51 engines/grim/inputdialog.cpp:84
-#: engines/hypno/metaengine.cpp:225 engines/mohawk/myst_stacks/menu.cpp:283
+#: engines/hypno/metaengine.cpp:282 engines/mohawk/myst_stacks/menu.cpp:283
 #: engines/mohawk/myst_stacks/menu.cpp:300
 #: engines/mohawk/myst_stacks/menu.cpp:318
 #: engines/mohawk/riven_stacks/aspit.cpp:349
@@ -339,7 +339,7 @@ msgstr "Een andere opslag is actief. Wilt u die onderbreken?"
 #: engines/chamber/metaengine.cpp:91 engines/director/events.cpp:122
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:139
 #: engines/efh/metaengine.cpp:362 engines/grim/grim.cpp:366
-#: engines/hypno/metaengine.cpp:192 engines/kyra/gui/saveload_eob.cpp:611
+#: engines/hypno/metaengine.cpp:249 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:197
 #: engines/mm/mm1/metaengine.cpp:54 engines/parallaction/metaengine.cpp:169
 #: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
@@ -358,7 +358,7 @@ msgstr "Ja"
 #: engines/chamber/metaengine.cpp:97 engines/director/events.cpp:122
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:139
 #: engines/efh/metaengine.cpp:368 engines/grim/grim.cpp:366
-#: engines/hypno/metaengine.cpp:198 engines/kyra/gui/saveload_eob.cpp:611
+#: engines/hypno/metaengine.cpp:255 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:204
 #: engines/mm/mm1/metaengine.cpp:52 engines/parallaction/metaengine.cpp:175
 #: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
@@ -3388,7 +3388,7 @@ msgstr "Grafische avonturen spel engine"
 #: backends/events/default/default-events.cpp:358
 #: engines/adl/metaengine.cpp:352 engines/buried/metaengine.cpp:154
 #: engines/chamber/metaengine.cpp:84 engines/drascula/metaengine.cpp:304
-#: engines/hdb/metaengine.cpp:275 engines/hypno/metaengine.cpp:251
+#: engines/hdb/metaengine.cpp:275 engines/hypno/metaengine.cpp:308
 #: engines/lab/metaengine.cpp:172 engines/mohawk/myst_stacks/menu.cpp:317
 #: engines/mohawk/riven_stacks/aspit.cpp:454
 #: engines/neverhood/metaengine.cpp:200 engines/saga/metaengine.cpp:449
@@ -3964,8 +3964,9 @@ msgstr "Rechter klik"
 #: engines/buried/metaengine.cpp:167 engines/crab/input/input.cpp:261
 #: engines/cruise/metaengine.cpp:217 engines/dm/metaengine.cpp:180
 #: engines/grim/grim.cpp:550 engines/hdb/metaengine.cpp:258
-#: engines/hypno/metaengine.cpp:186 engines/hypno/metaengine.cpp:369
-#: engines/icb/icb.cpp:137 engines/icb/icb.cpp:203 engines/mohawk/myst.cpp:595
+#: engines/hypno/metaengine.cpp:243 engines/hypno/metaengine.cpp:339
+#: engines/hypno/metaengine.cpp:456 engines/icb/icb.cpp:137
+#: engines/icb/icb.cpp:203 engines/mohawk/myst.cpp:595
 #: engines/mohawk/riven.cpp:876 engines/ngi/metaengine.cpp:136
 #: engines/sky/metaengine.cpp:126 engines/sludge/keymapper_tables.h:52
 #: engines/sludge/keymapper_tables.h:125 engines/sludge/keymapper_tables.h:187
@@ -4861,7 +4862,7 @@ msgstr "Y"
 #: backends/platform/sdl/opendingux/opendingux.cpp:57
 #: backends/platform/sdl/ps3/ps3.cpp:50 backends/platform/sdl/psp2/psp2.cpp:51
 #: engines/bagel/metaengine.cpp:64 engines/efh/metaengine.cpp:397
-#: engines/got/metaengine.cpp:76 engines/hypno/metaengine.cpp:363
+#: engines/got/metaengine.cpp:76 engines/hypno/metaengine.cpp:449
 #: engines/mm/mm1/metaengine.cpp:50 engines/mm/mm1/metaengine.cpp:97
 #: engines/sherlock/metaengine.cpp:660 engines/sherlock/metaengine.cpp:672
 #: engines/sherlock/metaengine.cpp:1014 engines/supernova/metaengine.cpp:303
@@ -6314,7 +6315,7 @@ msgstr "Film overslaan"
 #: engines/access/metaengine.cpp:237 engines/asylum/metaengine.cpp:171
 #: engines/asylum/metaengine.cpp:198 engines/cine/metaengine.cpp:377
 #: engines/efh/metaengine.cpp:286 engines/hdb/metaengine.cpp:213
-#: engines/hypno/metaengine.cpp:302 engines/hypno/metaengine.cpp:356
+#: engines/hypno/metaengine.cpp:363 engines/hypno/metaengine.cpp:411
 #: engines/nancy/input.cpp:195 engines/tinsel/metaengine.cpp:263
 #: engines/titanic/metaengine.cpp:324 engines/ultima/ultima8/metaengine.cpp:52
 msgid "Move up"
@@ -6324,7 +6325,7 @@ msgstr "Beweeg omhoog"
 #: engines/access/metaengine.cpp:244 engines/asylum/metaengine.cpp:177
 #: engines/asylum/metaengine.cpp:205 engines/cine/metaengine.cpp:384
 #: engines/efh/metaengine.cpp:293 engines/hdb/metaengine.cpp:219
-#: engines/hypno/metaengine.cpp:290 engines/hypno/metaengine.cpp:342
+#: engines/hypno/metaengine.cpp:351 engines/hypno/metaengine.cpp:399
 #: engines/nancy/input.cpp:201 engines/tinsel/metaengine.cpp:269
 #: engines/titanic/metaengine.cpp:330 engines/ultima/ultima8/metaengine.cpp:53
 msgid "Move down"
@@ -6335,7 +6336,7 @@ msgstr "Beweeg omlaag"
 #: engines/access/metaengine.cpp:251 engines/asylum/metaengine.cpp:183
 #: engines/asylum/metaengine.cpp:212 engines/cine/metaengine.cpp:391
 #: engines/efh/metaengine.cpp:300 engines/hdb/metaengine.cpp:225
-#: engines/hypno/metaengine.cpp:284 engines/hypno/metaengine.cpp:335
+#: engines/hypno/metaengine.cpp:345 engines/hypno/metaengine.cpp:393
 #: engines/kyra/engine/eobcommon.cpp:373 engines/lure/metaengine.cpp:218
 #: engines/nancy/input.cpp:207 engines/tinsel/metaengine.cpp:275
 #: engines/titanic/metaengine.cpp:312 engines/titanic/metaengine.cpp:390
@@ -6348,7 +6349,7 @@ msgstr "Beweeg naar links"
 #: engines/access/metaengine.cpp:258 engines/asylum/metaengine.cpp:189
 #: engines/asylum/metaengine.cpp:219 engines/cine/metaengine.cpp:398
 #: engines/efh/metaengine.cpp:307 engines/hdb/metaengine.cpp:231
-#: engines/hypno/metaengine.cpp:296 engines/hypno/metaengine.cpp:349
+#: engines/hypno/metaengine.cpp:357 engines/hypno/metaengine.cpp:405
 #: engines/kyra/engine/eobcommon.cpp:374 engines/lure/metaengine.cpp:226
 #: engines/nancy/input.cpp:213 engines/tinsel/metaengine.cpp:281
 #: engines/titanic/metaengine.cpp:318 engines/titanic/metaengine.cpp:396
@@ -6847,8 +6848,8 @@ msgstr "AGOS basis"
 #: engines/cine/metaengine.cpp:271 engines/cruise/metaengine.cpp:169
 #: engines/dm/metaengine.cpp:126 engines/draci/metaengine.cpp:196
 #: engines/drascula/metaengine.cpp:206 engines/hopkins/metaengine.cpp:207
-#: engines/hugo/metaengine.cpp:130 engines/hypno/metaengine.cpp:166
-#: engines/hypno/metaengine.cpp:267 engines/hypno/metaengine.cpp:311
+#: engines/hugo/metaengine.cpp:130 engines/hypno/metaengine.cpp:225
+#: engines/hypno/metaengine.cpp:324 engines/hypno/metaengine.cpp:372
 #: engines/illusions/metaengine.cpp:90 engines/lab/metaengine.cpp:152
 #: engines/lure/metaengine.cpp:169 engines/made/metaengine.cpp:133
 #: engines/mads/metaengine.cpp:317 engines/neverhood/metaengine.cpp:186
@@ -7550,7 +7551,7 @@ msgstr "Schakel gevecht in/uit"
 
 #. I18N: This keymap allows skipping video cutscenes
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
-#: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
+#: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:210
 #: engines/phoenixvr/metaengine.cpp:94 engines/scumm/help.cpp:79
 #: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
@@ -8738,7 +8739,7 @@ msgstr "Selecteer optie 13"
 msgid "Select option 14"
 msgstr "Selecteer optie 14"
 
-#: engines/efh/metaengine.cpp:257 engines/hypno/metaengine.cpp:312
+#: engines/efh/metaengine.cpp:257 engines/hypno/metaengine.cpp:373
 #: engines/sludge/keymapper_tables.h:35 engines/supernova/metaengine.cpp:218
 msgid "Menu keymappings"
 msgstr "Menu-toetsencombinaties"
@@ -10339,88 +10340,168 @@ msgstr ""
 msgid "Intro keymappings"
 msgstr "Intro toetsenbordindelingen"
 
-#: engines/hypno/metaengine.cpp:134
+#: engines/hypno/metaengine.cpp:134 engines/hypno/metaengine.cpp:144
 msgid "Primary shoot"
 msgstr "Primaire scheut"
 
-#: engines/hypno/metaengine.cpp:140
+#: engines/hypno/metaengine.cpp:139
 msgid "Secondary shoot"
 msgstr "Secundaire opname"
 
-#: engines/hypno/metaengine.cpp:146 engines/lab/metaengine.cpp:209
+#: engines/hypno/metaengine.cpp:149
+#, fuzzy
+#| msgid "Map hide toolbar action"
+msgid "Rapid fire / secondary action"
+msgstr "Koppel 'Verberg werkbalk' actie"
+
+#: engines/hypno/metaengine.cpp:155
+#, fuzzy
+#| msgid "Slide left"
+msgid "Aim left"
+msgstr "Schuif naar link"
+
+#: engines/hypno/metaengine.cpp:160
+#, fuzzy
+#| msgid "Scroll down"
+msgid "Aim down"
+msgstr "Scroll omlaag"
+
+#: engines/hypno/metaengine.cpp:165
+#, fuzzy
+#| msgid "Up right"
+msgid "Aim right"
+msgstr "Rechtop"
+
+#: engines/hypno/metaengine.cpp:170
+msgid "Aim up"
+msgstr ""
+
+#: engines/hypno/metaengine.cpp:175 engines/scumm/metaengine.cpp:1202
+#: engines/scumm/metaengine.cpp:1279
+#, fuzzy
+#| msgid "Slide left"
+msgid "Stick left"
+msgstr "Schuif naar link"
+
+#: engines/hypno/metaengine.cpp:182 engines/scumm/metaengine.cpp:1195
+#: engines/scumm/metaengine.cpp:1272
+#, fuzzy
+#| msgid "Scroll down"
+msgid "Stick down"
+msgstr "Scroll omlaag"
+
+#: engines/hypno/metaengine.cpp:189 engines/scumm/metaengine.cpp:1209
+#: engines/scumm/metaengine.cpp:1286
+#, fuzzy
+#| msgid "Slide right"
+msgid "Stick right"
+msgstr "Schuif naar rechts"
+
+#: engines/hypno/metaengine.cpp:196 engines/scumm/metaengine.cpp:1188
+#: engines/scumm/metaengine.cpp:1265
+#, fuzzy
+#| msgid "Pick up"
+msgid "Stick up"
+msgstr "Pak op"
+
+#: engines/hypno/metaengine.cpp:203 engines/lab/metaengine.cpp:209
 #: engines/ngi/metaengine.cpp:168 engines/sherlock/metaengine.cpp:872
 #: engines/teenagent/metaengine.cpp:232 engines/toon/metaengine.cpp:98
 #: engines/voyeur/metaengine.cpp:235
 msgid "Skip intro"
 msgstr "Intro overslaan"
 
-#: engines/hypno/metaengine.cpp:167
+#: engines/hypno/metaengine.cpp:226
 msgid "Exit menu keymappings"
 msgstr "Menu-toetsencombinaties afsluiten"
 
-#: engines/hypno/metaengine.cpp:168
+#: engines/hypno/metaengine.cpp:227
 msgid "Difficulty selection menu keymappings"
 msgstr "Toetsenbordindeling voor moeilijkheidsgraadkeuzemenu"
 
-#: engines/hypno/metaengine.cpp:169
+#: engines/hypno/metaengine.cpp:228
 msgid "Retry menu keymappings"
 msgstr "Menu-toetsenindelingen opnieuw proberen"
 
-#: engines/hypno/metaengine.cpp:172 engines/hypno/metaengine.cpp:270
-#: engines/hypno/metaengine.cpp:322
+#: engines/hypno/metaengine.cpp:231 engines/hypno/metaengine.cpp:327
+#: engines/hypno/metaengine.cpp:382
 msgid "Skip level (cheat)"
 msgstr "Niveau overslaan (cheat)"
 
 #. I18N: (Game name: Soldier Boyz) player refers to the users own character.
 #. I18N: (Game name: Marvel Comics Spider-Man: The Sinister Six) player refers to the users own character.
 #. I18N: (Game name: Wetlands) player refers to the users own character.
-#: engines/hypno/metaengine.cpp:180 engines/hypno/metaengine.cpp:278
-#: engines/hypno/metaengine.cpp:329
+#: engines/hypno/metaengine.cpp:238 engines/hypno/metaengine.cpp:334
+#: engines/hypno/metaengine.cpp:388
 msgid "Kill player"
 msgstr "Speler doden"
 
 #. I18N: (Game name: Soldier Boyz) the game has 3 difficulty levels: Chump, Punk and Badass. Chump is the easy mode.
-#: engines/hypno/metaengine.cpp:205
+#: engines/hypno/metaengine.cpp:262
 msgid "Chump"
 msgstr "Chump"
 
 #. I18N: (Game name: Soldier Boyz) the game has 3 difficulty levels: Chump, Punk and Badass. Punk is the medium mode.
-#: engines/hypno/metaengine.cpp:212
+#: engines/hypno/metaengine.cpp:269
 msgid "Punk"
 msgstr "Punk"
 
 #. I18N: (Game name: Soldier Boyz) the game has 3 difficulty levels: Chump, Punk and Badass. Badass is the hard mode.
-#: engines/hypno/metaengine.cpp:219
+#: engines/hypno/metaengine.cpp:276
 msgid "Badass"
 msgstr "Stoere vent"
 
 #. I18N: (Game name: Soldier Boyz) This makes the player restart from the last checkpoint.
-#: engines/hypno/metaengine.cpp:232
+#: engines/hypno/metaengine.cpp:289
 msgid "Retry sector"
 msgstr "Sector opnieuw proberen"
 
 #. I18N: (Game name: Soldier Boyz) This makes the player restart the current mission / level.
-#: engines/hypno/metaengine.cpp:239
+#: engines/hypno/metaengine.cpp:296
 msgid "Restart territory"
 msgstr "Territorium opnieuw opstarten"
 
-#: engines/hypno/metaengine.cpp:245
+#: engines/hypno/metaengine.cpp:302
 msgid "Begin new mission"
 msgstr "Begin nieuwe missie"
 
-#: engines/hypno/metaengine.cpp:313
+#: engines/hypno/metaengine.cpp:374
 msgid "Pause keymappings"
 msgstr "Toetsencombinaties pauzeren"
 
-#: engines/hypno/metaengine.cpp:314
+#: engines/hypno/metaengine.cpp:375
 msgid "Direction keymappings"
 msgstr "Richtingstoetsindelingen"
 
-#: engines/hypno/metaengine.cpp:316 engines/mads/metaengine.cpp:391
+#: engines/hypno/metaengine.cpp:377 engines/mads/metaengine.cpp:391
 #: engines/tsage/metaengine.cpp:259
 msgid "Show credits"
 msgstr "Tool credits"
 
+#: engines/hypno/metaengine.cpp:417
+#, fuzzy
+#| msgid "Move left"
+msgid "Menu left"
+msgstr "Beweeg naar links"
+
+#: engines/hypno/metaengine.cpp:425
+#, fuzzy
+#| msgid "Move down"
+msgid "Menu down"
+msgstr "Beweeg omlaag"
+
+#: engines/hypno/metaengine.cpp:433
+#, fuzzy
+#| msgid "Move right"
+msgid "Menu right"
+msgstr "Beweeg naar rechts"
+
+#: engines/hypno/metaengine.cpp:441
+#, fuzzy
+#| msgid "Menu"
+msgid "Menu up"
+msgstr "Menu"
+
 #. I18N: Action in In Cold Blood
 #: engines/icb/icb.cpp:107
 msgid "Arm"
@@ -13784,30 +13865,6 @@ msgstr ""
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1188 engines/scumm/metaengine.cpp:1265
-#, fuzzy
-#| msgid "Pick up"
-msgid "Stick up"
-msgstr "Pak op"
-
-#: engines/scumm/metaengine.cpp:1195 engines/scumm/metaengine.cpp:1272
-#, fuzzy
-#| msgid "Scroll down"
-msgid "Stick down"
-msgstr "Scroll omlaag"
-
-#: engines/scumm/metaengine.cpp:1202 engines/scumm/metaengine.cpp:1279
-#, fuzzy
-#| msgid "Slide left"
-msgid "Stick left"
-msgstr "Schuif naar link"
-
-#: engines/scumm/metaengine.cpp:1209 engines/scumm/metaengine.cpp:1286
-#, fuzzy
-#| msgid "Slide right"
-msgid "Stick right"
-msgstr "Schuif naar rechts"
-
 #: engines/scumm/metaengine.cpp:1216 engines/scumm/metaengine.cpp:1293
 #, fuzzy
 #| msgid "Move / Select"
@@ -18950,9 +19007,6 @@ msgstr ""
 #~ "U moet een toets koppelen aan de 'Rechter klik' actie om dit spel te "
 #~ "kunnen spelen"
 
-#~ msgid "Map hide toolbar action"
-#~ msgstr "Koppel 'Verberg werkbalk' actie"
-
 #~ msgid "You must map a key to the 'Hide toolbar' action to play this game"
 #~ msgstr ""
 #~ "U moet een toets koppelen aan de 'Verberg werkbalk' actie om dit spel te "
diff --git a/po/nn_NO.po b/po/nn_NO.po
index 3b1d24a3338..2f2dd647cdd 100644
--- a/po/nn_NO.po
+++ b/po/nn_NO.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-13 06:14+0000\n"
+"POT-Creation-Date: 2026-06-13 10:20+0000\n"
 "PO-Revision-Date: 2023-12-14 14:28+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Norwegian Nynorsk <https://translations.scummvm.org/projects/"
@@ -138,7 +138,7 @@ msgstr "Oppover"
 #: engines/buried/saveload.cpp:83 engines/cine/saveload.cpp:857
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:123
 #: engines/drascula/saveload.cpp:51 engines/grim/inputdialog.cpp:84
-#: engines/hypno/metaengine.cpp:225 engines/mohawk/myst_stacks/menu.cpp:283
+#: engines/hypno/metaengine.cpp:282 engines/mohawk/myst_stacks/menu.cpp:283
 #: engines/mohawk/myst_stacks/menu.cpp:300
 #: engines/mohawk/myst_stacks/menu.cpp:318
 #: engines/mohawk/riven_stacks/aspit.cpp:349
@@ -341,7 +341,7 @@ msgstr "Ein anna lagringsteneste arbeider nett no. Vill du abryta han?"
 #: engines/chamber/metaengine.cpp:91 engines/director/events.cpp:122
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:139
 #: engines/efh/metaengine.cpp:362 engines/grim/grim.cpp:366
-#: engines/hypno/metaengine.cpp:192 engines/kyra/gui/saveload_eob.cpp:611
+#: engines/hypno/metaengine.cpp:249 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:197
 #: engines/mm/mm1/metaengine.cpp:54 engines/parallaction/metaengine.cpp:169
 #: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
@@ -360,7 +360,7 @@ msgstr "Ja"
 #: engines/chamber/metaengine.cpp:97 engines/director/events.cpp:122
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:139
 #: engines/efh/metaengine.cpp:368 engines/grim/grim.cpp:366
-#: engines/hypno/metaengine.cpp:198 engines/kyra/gui/saveload_eob.cpp:611
+#: engines/hypno/metaengine.cpp:255 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:204
 #: engines/mm/mm1/metaengine.cpp:52 engines/parallaction/metaengine.cpp:175
 #: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
@@ -3331,7 +3331,7 @@ msgstr ""
 #: backends/events/default/default-events.cpp:358
 #: engines/adl/metaengine.cpp:352 engines/buried/metaengine.cpp:154
 #: engines/chamber/metaengine.cpp:84 engines/drascula/metaengine.cpp:304
-#: engines/hdb/metaengine.cpp:275 engines/hypno/metaengine.cpp:251
+#: engines/hdb/metaengine.cpp:275 engines/hypno/metaengine.cpp:308
 #: engines/lab/metaengine.cpp:172 engines/mohawk/myst_stacks/menu.cpp:317
 #: engines/mohawk/riven_stacks/aspit.cpp:454
 #: engines/neverhood/metaengine.cpp:200 engines/saga/metaengine.cpp:449
@@ -3896,8 +3896,9 @@ msgstr "H??greklikk"
 #: engines/buried/metaengine.cpp:167 engines/crab/input/input.cpp:261
 #: engines/cruise/metaengine.cpp:217 engines/dm/metaengine.cpp:180
 #: engines/grim/grim.cpp:550 engines/hdb/metaengine.cpp:258
-#: engines/hypno/metaengine.cpp:186 engines/hypno/metaengine.cpp:369
-#: engines/icb/icb.cpp:137 engines/icb/icb.cpp:203 engines/mohawk/myst.cpp:595
+#: engines/hypno/metaengine.cpp:243 engines/hypno/metaengine.cpp:339
+#: engines/hypno/metaengine.cpp:456 engines/icb/icb.cpp:137
+#: engines/icb/icb.cpp:203 engines/mohawk/myst.cpp:595
 #: engines/mohawk/riven.cpp:876 engines/ngi/metaengine.cpp:136
 #: engines/sky/metaengine.cpp:126 engines/sludge/keymapper_tables.h:52
 #: engines/sludge/keymapper_tables.h:125 engines/sludge/keymapper_tables.h:187
@@ -4866,7 +4867,7 @@ msgstr ""
 #: backends/platform/sdl/opendingux/opendingux.cpp:57
 #: backends/platform/sdl/ps3/ps3.cpp:50 backends/platform/sdl/psp2/psp2.cpp:51
 #: engines/bagel/metaengine.cpp:64 engines/efh/metaengine.cpp:397
-#: engines/got/metaengine.cpp:76 engines/hypno/metaengine.cpp:363
+#: engines/got/metaengine.cpp:76 engines/hypno/metaengine.cpp:449
 #: engines/mm/mm1/metaengine.cpp:50 engines/mm/mm1/metaengine.cpp:97
 #: engines/sherlock/metaengine.cpp:660 engines/sherlock/metaengine.cpp:672
 #: engines/sherlock/metaengine.cpp:1014 engines/supernova/metaengine.cpp:303
@@ -6048,7 +6049,7 @@ msgstr "Hopp over linje"
 #: engines/access/metaengine.cpp:237 engines/asylum/metaengine.cpp:171
 #: engines/asylum/metaengine.cpp:198 engines/cine/metaengine.cpp:377
 #: engines/efh/metaengine.cpp:286 engines/hdb/metaengine.cpp:213
-#: engines/hypno/metaengine.cpp:302 engines/hypno/metaengine.cpp:356
+#: engines/hypno/metaengine.cpp:363 engines/hypno/metaengine.cpp:411
 #: engines/nancy/input.cpp:195 engines/tinsel/metaengine.cpp:263
 #: engines/titanic/metaengine.cpp:324 engines/ultima/ultima8/metaengine.cpp:52
 #, fuzzy
@@ -6059,7 +6060,7 @@ msgstr "Beveg Bakover"
 #: engines/access/metaengine.cpp:244 engines/asylum/metaengine.cpp:177
 #: engines/asylum/metaengine.cpp:205 engines/cine/metaengine.cpp:384
 #: engines/efh/metaengine.cpp:293 engines/hdb/metaengine.cpp:219
-#: engines/hypno/metaengine.cpp:290 engines/hypno/metaengine.cpp:342
+#: engines/hypno/metaengine.cpp:351 engines/hypno/metaengine.cpp:399
 #: engines/nancy/input.cpp:201 engines/tinsel/metaengine.cpp:269
 #: engines/titanic/metaengine.cpp:330 engines/ultima/ultima8/metaengine.cpp:53
 #, fuzzy
@@ -6071,7 +6072,7 @@ msgstr "Skli til H??yre"
 #: engines/access/metaengine.cpp:251 engines/asylum/metaengine.cpp:183
 #: engines/asylum/metaengine.cpp:212 engines/cine/metaengine.cpp:391
 #: engines/efh/metaengine.cpp:300 engines/hdb/metaengine.cpp:225
-#: engines/hypno/metaengine.cpp:284 engines/hypno/metaengine.cpp:335
+#: engines/hypno/metaengine.cpp:345 engines/hypno/metaengine.cpp:393
 #: engines/kyra/engine/eobcommon.cpp:373 engines/lure/metaengine.cpp:218
 #: engines/nancy/input.cpp:207 engines/tinsel/metaengine.cpp:275
 #: engines/titanic/metaengine.cpp:312 engines/titanic/metaengine.cpp:390
@@ -6085,7 +6086,7 @@ msgstr "Skli til Venstre"
 #: engines/access/metaengine.cpp:258 engines/asylum/metaengine.cpp:189
 #: engines/asylum/metaengine.cpp:219 engines/cine/metaengine.cpp:398
 #: engines/efh/metaengine.cpp:307 engines/hdb/metaengine.cpp:231
-#: engines/hypno/metaengine.cpp:296 engines/hypno/metaengine.cpp:349
+#: engines/hypno/metaengine.cpp:357 engines/hypno/metaengine.cpp:405
 #: engines/kyra/engine/eobcommon.cpp:374 engines/lure/metaengine.cpp:226
 #: engines/nancy/input.cpp:213 engines/tinsel/metaengine.cpp:281
 #: engines/titanic/metaengine.cpp:318 engines/titanic/metaengine.cpp:396
@@ -6576,8 +6577,8 @@ msgstr ""
 #: engines/cine/metaengine.cpp:271 engines/cruise/metaengine.cpp:169
 #: engines/dm/metaengine.cpp:126 engines/draci/metaengine.cpp:196
 #: engines/drascula/metaengine.cpp:206 engines/hopkins/metaengine.cpp:207
-#: engines/hugo/metaengine.cpp:130 engines/hypno/metaengine.cpp:166
-#: engines/hypno/metaengine.cpp:267 engines/hypno/metaengine.cpp:311
+#: engines/hugo/metaengine.cpp:130 engines/hypno/metaengine.cpp:225
+#: engines/hypno/metaengine.cpp:324 engines/hypno/metaengine.cpp:372
 #: engines/illusions/metaengine.cpp:90 engines/lab/metaengine.cpp:152
 #: engines/lure/metaengine.cpp:169 engines/made/metaengine.cpp:133
 #: engines/mads/metaengine.cpp:317 engines/neverhood/metaengine.cpp:186
@@ -7286,7 +7287,7 @@ msgstr "Veksle musl??sing"
 
 #. I18N: This keymap allows skipping video cutscenes
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
-#: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
+#: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:210
 #: engines/phoenixvr/metaengine.cpp:94 engines/scumm/help.cpp:79
 #: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
@@ -8589,7 +8590,7 @@ msgstr "Grupp??r etter"
 msgid "Select option 14"
 msgstr "Grupp??r etter"
 
-#: engines/efh/metaengine.cpp:257 engines/hypno/metaengine.cpp:312
+#: engines/efh/metaengine.cpp:257 engines/hypno/metaengine.cpp:373
 #: engines/sludge/keymapper_tables.h:35 engines/supernova/metaengine.cpp:218
 #, fuzzy
 #| msgid "Game Options"
@@ -10274,16 +10275,69 @@ msgstr ""
 msgid "Intro keymappings"
 msgstr "Spelval"
 
-#: engines/hypno/metaengine.cpp:134
+#: engines/hypno/metaengine.cpp:134 engines/hypno/metaengine.cpp:144
 msgid "Primary shoot"
 msgstr ""
 
-#: engines/hypno/metaengine.cpp:140
+#: engines/hypno/metaengine.cpp:139
 #, fuzzy
 msgid "Secondary shoot"
 msgstr "Andre unge"
 
-#: engines/hypno/metaengine.cpp:146 engines/lab/metaengine.cpp:209
+#: engines/hypno/metaengine.cpp:149
+#, fuzzy
+#| msgid "Map hide toolbar action"
+msgid "Rapid fire / secondary action"
+msgstr "Kople skjul-verkt??ylinje-handlinga"
+
+#: engines/hypno/metaengine.cpp:155
+#, fuzzy
+#| msgid "Slide Left"
+msgid "Aim left"
+msgstr "Skli til Venstre"
+
+#: engines/hypno/metaengine.cpp:160
+#, fuzzy
+msgid "Aim down"
+msgstr "Bla liste ned"
+
+#: engines/hypno/metaengine.cpp:165
+#, fuzzy
+msgid "Aim right"
+msgstr "H??gre"
+
+#: engines/hypno/metaengine.cpp:170
+msgid "Aim up"
+msgstr ""
+
+#: engines/hypno/metaengine.cpp:175 engines/scumm/metaengine.cpp:1202
+#: engines/scumm/metaengine.cpp:1279
+#, fuzzy
+#| msgid "Slide Left"
+msgid "Stick left"
+msgstr "Skli til Venstre"
+
+#: engines/hypno/metaengine.cpp:182 engines/scumm/metaengine.cpp:1195
+#: engines/scumm/metaengine.cpp:1272
+#, fuzzy
+msgid "Stick down"
+msgstr "Bla liste ned"
+
+#: engines/hypno/metaengine.cpp:189 engines/scumm/metaengine.cpp:1209
+#: engines/scumm/metaengine.cpp:1286
+#, fuzzy
+#| msgid "Slide Right"
+msgid "Stick right"
+msgstr "Skli til H??yre"
+
+#: engines/hypno/metaengine.cpp:196 engines/scumm/metaengine.cpp:1188
+#: engines/scumm/metaengine.cpp:1265
+#, fuzzy
+#| msgid "Pick up"
+msgid "Stick up"
+msgstr "Plukk opp"
+
+#: engines/hypno/metaengine.cpp:203 engines/lab/metaengine.cpp:209
 #: engines/ngi/metaengine.cpp:168 engines/sherlock/metaengine.cpp:872
 #: engines/teenagent/metaengine.cpp:232 engines/toon/metaengine.cpp:98
 #: engines/voyeur/metaengine.cpp:235
@@ -10292,26 +10346,26 @@ msgstr "Andre unge"
 msgid "Skip intro"
 msgstr "Hopp over linje"
 
-#: engines/hypno/metaengine.cpp:167
+#: engines/hypno/metaengine.cpp:226
 #, fuzzy
 #| msgid "Game Options"
 msgid "Exit menu keymappings"
 msgstr "Spelval"
 
-#: engines/hypno/metaengine.cpp:168
+#: engines/hypno/metaengine.cpp:227
 #, fuzzy
 #| msgid "Game Options"
 msgid "Difficulty selection menu keymappings"
 msgstr "Spelval"
 
-#: engines/hypno/metaengine.cpp:169
+#: engines/hypno/metaengine.cpp:228
 #, fuzzy
 #| msgid "Game Options"
 msgid "Retry menu keymappings"
 msgstr "Spelval"
 
-#: engines/hypno/metaengine.cpp:172 engines/hypno/metaengine.cpp:270
-#: engines/hypno/metaengine.cpp:322
+#: engines/hypno/metaengine.cpp:231 engines/hypno/metaengine.cpp:327
+#: engines/hypno/metaengine.cpp:382
 #, fuzzy
 #| msgid "Speech"
 msgid "Skip level (cheat)"
@@ -10320,59 +10374,80 @@ msgstr "Tale"
 #. I18N: (Game name: Soldier Boyz) player refers to the users own character.
 #. I18N: (Game name: Marvel Comics Spider-Man: The Sinister Six) player refers to the users own character.
 #. I18N: (Game name: Wetlands) player refers to the users own character.
-#: engines/hypno/metaengine.cpp:180 engines/hypno/metaengine.cpp:278
-#: engines/hypno/metaengine.cpp:329
+#: engines/hypno/metaengine.cpp:238 engines/hypno/metaengine.cpp:334
+#: engines/hypno/metaengine.cpp:388
 msgid "Kill player"
 msgstr ""
 
 #. I18N: (Game name: Soldier Boyz) the game has 3 difficulty levels: Chump, Punk and Badass. Chump is the easy mode.
-#: engines/hypno/metaengine.cpp:205
+#: engines/hypno/metaengine.cpp:262
 msgid "Chump"
 msgstr ""
 
 #. I18N: (Game name: Soldier Boyz) the game has 3 difficulty levels: Chump, Punk and Badass. Punk is the medium mode.
-#: engines/hypno/metaengine.cpp:212
+#: engines/hypno/metaengine.cpp:269
 msgid "Punk"
 msgstr ""
 
 #. I18N: (Game name: Soldier Boyz) the game has 3 difficulty levels: Chump, Punk and Badass. Badass is the hard mode.
-#: engines/hypno/metaengine.cpp:219
+#: engines/hypno/metaengine.cpp:276
 msgid "Badass"
 msgstr ""
 
 #. I18N: (Game name: Soldier Boyz) This makes the player restart from the last checkpoint.
-#: engines/hypno/metaengine.cpp:232
+#: engines/hypno/metaengine.cpp:289
 msgid "Retry sector"
 msgstr ""
 
 #. I18N: (Game name: Soldier Boyz) This makes the player restart the current mission / level.
-#: engines/hypno/metaengine.cpp:239
+#: engines/hypno/metaengine.cpp:296
 #, fuzzy
 msgid "Restart territory"
 msgstr "~O~vergangar aktivert"
 
-#: engines/hypno/metaengine.cpp:245
+#: engines/hypno/metaengine.cpp:302
 msgid "Begin new mission"
 msgstr ""
 
-#: engines/hypno/metaengine.cpp:313
+#: engines/hypno/metaengine.cpp:374
 #, fuzzy
 #| msgid "Game Options"
 msgid "Pause keymappings"
 msgstr "Spelval"
 
-#: engines/hypno/metaengine.cpp:314
+#: engines/hypno/metaengine.cpp:375
 #, fuzzy
 #| msgid "Game Options"
 msgid "Direction keymappings"
 msgstr "Spelval"
 
-#: engines/hypno/metaengine.cpp:316 engines/mads/metaengine.cpp:391
+#: engines/hypno/metaengine.cpp:377 engines/mads/metaengine.cpp:391
 #: engines/tsage/metaengine.cpp:259
 #, fuzzy
 msgid "Show credits"
 msgstr "Vis muspeikar"
 
+#: engines/hypno/metaengine.cpp:417
+#, fuzzy
+msgid "Menu left"
+msgstr "Skli til Venstre"
+
+#: engines/hypno/metaengine.cpp:425
+#, fuzzy
+msgid "Menu down"
+msgstr "Skli til H??yre"
+
+#: engines/hypno/metaengine.cpp:433
+#, fuzzy
+msgid "Menu right"
+msgstr "Skli til H??yre"
+
+#: engines/hypno/metaengine.cpp:441
+#, fuzzy
+#| msgid "Menu"
+msgid "Menu up"
+msgstr "Meny"
+
 #. I18N: Action in In Cold Blood
 #: engines/icb/icb.cpp:107
 msgid "Arm"
@@ -13714,29 +13789,6 @@ msgstr ""
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1188 engines/scumm/metaengine.cpp:1265
-#, fuzzy
-#| msgid "Pick up"
-msgid "Stick up"
-msgstr "Plukk opp"
-
-#: engines/scumm/metaengine.cpp:1195 engines/scumm/metaengine.cpp:1272
-#, fuzzy
-msgid "Stick down"
-msgstr "Bla liste ned"
-
-#: engines/scumm/metaengine.cpp:1202 engines/scumm/metaengine.cpp:1279
-#, fuzzy
-#| msgid "Slide Left"
-msgid "Stick left"
-msgstr "Skli til Venstre"
-
-#: engines/scumm/metaengine.cpp:1209 engines/scumm/metaengine.cpp:1286
-#, fuzzy
-#| msgid "Slide Right"
-msgid "Stick right"
-msgstr "Skli til H??yre"
-
 #: engines/scumm/metaengine.cpp:1216 engines/scumm/metaengine.cpp:1293
 #, fuzzy
 msgid "Fire / select"
@@ -18733,9 +18785,6 @@ msgstr ""
 #~ msgstr ""
 #~ "Du m?? kople ein tast til 'H??greklikk'-handlinga for ?? spele dette spelet"
 
-#~ msgid "Map hide toolbar action"
-#~ msgstr "Kople skjul-verkt??ylinje-handlinga"
-
 #~ msgid "You must map a key to the 'Hide toolbar' action to play this game"
 #~ msgstr ""
 #~ "Du m?? kople ein tast til 'Skjul verkt??ylinje' for ?? spele dette spelet"
diff --git a/po/pl_PL.po b/po/pl_PL.po
index 6481d4d4c03..dea39dd2a94 100644
--- a/po/pl_PL.po
+++ b/po/pl_PL.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-13 06:14+0000\n"
+"POT-Creation-Date: 2026-06-13 10:20+0000\n"
 "PO-Revision-Date: 2026-04-17 00:22+0000\n"
 "Last-Translator: SkiffPL <skiff at windowslive.com>\n"
 "Language-Team: Polish <https://translations.scummvm.org/projects/scummvm/"
@@ -138,7 +138,7 @@ msgstr "W g??r??"
 #: engines/buried/saveload.cpp:83 engines/cine/saveload.cpp:857
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:123
 #: engines/drascula/saveload.cpp:51 engines/grim/inputdialog.cpp:84
-#: engines/hypno/metaengine.cpp:225 engines/mohawk/myst_stacks/menu.cpp:283
+#: engines/hypno/metaengine.cpp:282 engines/mohawk/myst_stacks/menu.cpp:283
 #: engines/mohawk/myst_stacks/menu.cpp:300
 #: engines/mohawk/myst_stacks/menu.cpp:318
 #: engines/mohawk/riven_stacks/aspit.cpp:349
@@ -341,7 +341,7 @@ msgstr "Inna us??uga jest aktywna. Czy chcesz j?? przerwa???"
 #: engines/chamber/metaengine.cpp:91 engines/director/events.cpp:122
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:139
 #: engines/efh/metaengine.cpp:362 engines/grim/grim.cpp:366
-#: engines/hypno/metaengine.cpp:192 engines/kyra/gui/saveload_eob.cpp:611
+#: engines/hypno/metaengine.cpp:249 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:197
 #: engines/mm/mm1/metaengine.cpp:54 engines/parallaction/metaengine.cpp:169
 #: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
@@ -360,7 +360,7 @@ msgstr "Tak"
 #: engines/chamber/metaengine.cpp:97 engines/director/events.cpp:122
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:139
 #: engines/efh/metaengine.cpp:368 engines/grim/grim.cpp:366
-#: engines/hypno/metaengine.cpp:198 engines/kyra/gui/saveload_eob.cpp:611
+#: engines/hypno/metaengine.cpp:255 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:204
 #: engines/mm/mm1/metaengine.cpp:52 engines/parallaction/metaengine.cpp:175
 #: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
@@ -3391,7 +3391,7 @@ msgstr "Silnik graficzny gier przygodowych"
 #: backends/events/default/default-events.cpp:358
 #: engines/adl/metaengine.cpp:352 engines/buried/metaengine.cpp:154
 #: engines/chamber/metaengine.cpp:84 engines/drascula/metaengine.cpp:304
-#: engines/hdb/metaengine.cpp:275 engines/hypno/metaengine.cpp:251
+#: engines/hdb/metaengine.cpp:275 engines/hypno/metaengine.cpp:308
 #: engines/lab/metaengine.cpp:172 engines/mohawk/myst_stacks/menu.cpp:317
 #: engines/mohawk/riven_stacks/aspit.cpp:454
 #: engines/neverhood/metaengine.cpp:200 engines/saga/metaengine.cpp:449
@@ -3955,8 +3955,9 @@ msgstr "Klikni??cie PPM"
 #: engines/buried/metaengine.cpp:167 engines/crab/input/input.cpp:261
 #: engines/cruise/metaengine.cpp:217 engines/dm/metaengine.cpp:180
 #: engines/grim/grim.cpp:550 engines/hdb/metaengine.cpp:258
-#: engines/hypno/metaengine.cpp:186 engines/hypno/metaengine.cpp:369
-#: engines/icb/icb.cpp:137 engines/icb/icb.cpp:203 engines/mohawk/myst.cpp:595
+#: engines/hypno/metaengine.cpp:243 engines/hypno/metaengine.cpp:339
+#: engines/hypno/metaengine.cpp:456 engines/icb/icb.cpp:137
+#: engines/icb/icb.cpp:203 engines/mohawk/myst.cpp:595
 #: engines/mohawk/riven.cpp:876 engines/ngi/metaengine.cpp:136
 #: engines/sky/metaengine.cpp:126 engines/sludge/keymapper_tables.h:52
 #: engines/sludge/keymapper_tables.h:125 engines/sludge/keymapper_tables.h:187
@@ -4852,7 +4853,7 @@ msgstr "Y"
 #: backends/platform/sdl/opendingux/opendingux.cpp:57
 #: backends/platform/sdl/ps3/ps3.cpp:50 backends/platform/sdl/psp2/psp2.cpp:51
 #: engines/bagel/metaengine.cpp:64 engines/efh/metaengine.cpp:397
-#: engines/got/metaengine.cpp:76 engines/hypno/metaengine.cpp:363
+#: engines/got/metaengine.cpp:76 engines/hypno/metaengine.cpp:449
 #: engines/mm/mm1/metaengine.cpp:50 engines/mm/mm1/metaengine.cpp:97
 #: engines/sherlock/metaengine.cpp:660 engines/sherlock/metaengine.cpp:672
 #: engines/sherlock/metaengine.cpp:1014 engines/supernova/metaengine.cpp:303
@@ -6326,7 +6327,7 @@ msgstr "Pomi?? film"
 #: engines/access/metaengine.cpp:237 engines/asylum/metaengine.cpp:171
 #: engines/asylum/metaengine.cpp:198 engines/cine/metaengine.cpp:377
 #: engines/efh/metaengine.cpp:286 engines/hdb/metaengine.cpp:213
-#: engines/hypno/metaengine.cpp:302 engines/hypno/metaengine.cpp:356
+#: engines/hypno/metaengine.cpp:363 engines/hypno/metaengine.cpp:411
 #: engines/nancy/input.cpp:195 engines/tinsel/metaengine.cpp:263
 #: engines/titanic/metaengine.cpp:324 engines/ultima/ultima8/metaengine.cpp:52
 msgid "Move up"
@@ -6336,7 +6337,7 @@ msgstr "Ruch w g??r??"
 #: engines/access/metaengine.cpp:244 engines/asylum/metaengine.cpp:177
 #: engines/asylum/metaengine.cpp:205 engines/cine/metaengine.cpp:384
 #: engines/efh/metaengine.cpp:293 engines/hdb/metaengine.cpp:219
-#: engines/hypno/metaengine.cpp:290 engines/hypno/metaengine.cpp:342
+#: engines/hypno/metaengine.cpp:351 engines/hypno/metaengine.cpp:399
 #: engines/nancy/input.cpp:201 engines/tinsel/metaengine.cpp:269
 #: engines/titanic/metaengine.cpp:330 engines/ultima/ultima8/metaengine.cpp:53
 msgid "Move down"
@@ -6347,7 +6348,7 @@ msgstr "Ruch w d????"
 #: engines/access/metaengine.cpp:251 engines/asylum/metaengine.cpp:183
 #: engines/asylum/metaengine.cpp:212 engines/cine/metaengine.cpp:391
 #: engines/efh/metaengine.cpp:300 engines/hdb/metaengine.cpp:225
-#: engines/hypno/metaengine.cpp:284 engines/hypno/metaengine.cpp:335
+#: engines/hypno/metaengine.cpp:345 engines/hypno/metaengine.cpp:393
 #: engines/kyra/engine/eobcommon.cpp:373 engines/lure/metaengine.cpp:218
 #: engines/nancy/input.cpp:207 engines/tinsel/metaengine.cpp:275
 #: engines/titanic/metaengine.cpp:312 engines/titanic/metaengine.cpp:390
@@ -6360,7 +6361,7 @@ msgstr "Ruch w lewo"
 #: engines/access/metaengine.cpp:258 engines/asylum/metaengine.cpp:189
 #: engines/asylum/metaengine.cpp:219 engines/cine/metaengine.cpp:398
 #: engines/efh/metaengine.cpp:307 engines/hdb/metaengine.cpp:231
-#: engines/hypno/metaengine.cpp:296 engines/hypno/metaengine.cpp:349
+#: engines/hypno/metaengine.cpp:357 engines/hypno/metaengine.cpp:405
 #: engines/kyra/engine/eobcommon.cpp:374 engines/lure/metaengine.cpp:226
 #: engines/nancy/input.cpp:213 engines/tinsel/metaengine.cpp:281
 #: engines/titanic/metaengine.cpp:318 engines/titanic/metaengine.cpp:396
@@ -6860,8 +6861,8 @@ msgstr "G????wny AGOS"
 #: engines/cine/metaengine.cpp:271 engines/cruise/metaengine.cpp:169
 #: engines/dm/metaengine.cpp:126 engines/draci/metaengine.cpp:196
 #: engines/drascula/metaengine.cpp:206 engines/hopkins/metaengine.cpp:207
-#: engines/hugo/metaengine.cpp:130 engines/hypno/metaengine.cpp:166
-#: engines/hypno/metaengine.cpp:267 engines/hypno/metaengine.cpp:311
+#: engines/hugo/metaengine.cpp:130 engines/hypno/metaengine.cpp:225
+#: engines/hypno/metaengine.cpp:324 engines/hypno/metaengine.cpp:372
 #: engines/illusions/metaengine.cpp:90 engines/lab/metaengine.cpp:152
 #: engines/lure/metaengine.cpp:169 engines/made/metaengine.cpp:133
 #: engines/mads/metaengine.cpp:317 engines/neverhood/metaengine.cpp:186
@@ -7564,7 +7565,7 @@ msgstr "Prze????cz tryb walki"
 
 #. I18N: This keymap allows skipping video cutscenes
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
-#: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
+#: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:210
 #: engines/phoenixvr/metaengine.cpp:94 engines/scumm/help.cpp:79
 #: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
@@ -8779,7 +8780,7 @@ msgstr "Wybierz aktora 1"
 msgid "Select option 14"
 msgstr "Wybierz aktora 1"
 
-#: engines/efh/metaengine.cpp:257 engines/hypno/metaengine.cpp:312
+#: engines/efh/metaengine.cpp:257 engines/hypno/metaengine.cpp:373
 #: engines/sludge/keymapper_tables.h:35 engines/supernova/metaengine.cpp:218
 msgid "Menu keymappings"
 msgstr "Mapowanie klawiszy menu"
@@ -10450,42 +10451,98 @@ msgstr ""
 msgid "Intro keymappings"
 msgstr "Mapowanie klawiszy intra"
 
-#: engines/hypno/metaengine.cpp:134
+#: engines/hypno/metaengine.cpp:134 engines/hypno/metaengine.cpp:144
 msgid "Primary shoot"
 msgstr ""
 
-#: engines/hypno/metaengine.cpp:140
+#: engines/hypno/metaengine.cpp:139
 #, fuzzy
 msgid "Secondary shoot"
 msgstr "Drugi dzieciak"
 
-#: engines/hypno/metaengine.cpp:146 engines/lab/metaengine.cpp:209
+#: engines/hypno/metaengine.cpp:149
+#, fuzzy
+#| msgid "Map hide toolbar action"
+msgid "Rapid fire / secondary action"
+msgstr "Przypisz chowanie paska narz??dzi"
+
+#: engines/hypno/metaengine.cpp:155
+#, fuzzy
+#| msgid "Slide left"
+msgid "Aim left"
+msgstr "??lizg w lewo"
+
+#: engines/hypno/metaengine.cpp:160
+#, fuzzy
+#| msgid "Scroll down"
+msgid "Aim down"
+msgstr "Przewi?? w d????"
+
+#: engines/hypno/metaengine.cpp:165
+#, fuzzy
+#| msgid "Up right"
+msgid "Aim right"
+msgstr "W g??r?? w prawo"
+
+#: engines/hypno/metaengine.cpp:170
+msgid "Aim up"
+msgstr ""
+
+#: engines/hypno/metaengine.cpp:175 engines/scumm/metaengine.cpp:1202
+#: engines/scumm/metaengine.cpp:1279
+#, fuzzy
+#| msgid "Slide left"
+msgid "Stick left"
+msgstr "??lizg w lewo"
+
+#: engines/hypno/metaengine.cpp:182 engines/scumm/metaengine.cpp:1195
+#: engines/scumm/metaengine.cpp:1272
+#, fuzzy
+#| msgid "Scroll down"
+msgid "Stick down"
+msgstr "Przewi?? w d????"
+
+#: engines/hypno/metaengine.cpp:189 engines/scumm/metaengine.cpp:1209
+#: engines/scumm/metaengine.cpp:1286
+#, fuzzy
+#| msgid "Slide right"
+msgid "Stick right"
+msgstr "??lizg w prawo"
+
+#: engines/hypno/metaengine.cpp:196 engines/scumm/metaengine.cpp:1188
+#: engines/scumm/metaengine.cpp:1265
+#, fuzzy
+#| msgid "Pick up"
+msgid "Stick up"
+msgstr "Podnie??"
+
+#: engines/hypno/metaengine.cpp:203 engines/lab/metaengine.cpp:209
 #: engines/ngi/metaengine.cpp:168 engines/sherlock/metaengine.cpp:872
 #: engines/teenagent/metaengine.cpp:232 engines/toon/metaengine.cpp:98
 #: engines/voyeur/metaengine.cpp:235
 msgid "Skip intro"
 msgstr "Pomi?? intro"
 
-#: engines/hypno/metaengine.cpp:167
+#: engines/hypno/metaengine.cpp:226
 #, fuzzy
 #| msgid "PET keymappings"
 msgid "Exit menu keymappings"
 msgstr "Mapowanie klawiszy PET"
 
-#: engines/hypno/metaengine.cpp:168
+#: engines/hypno/metaengine.cpp:227
 #, fuzzy
 #| msgid "Intro keymappings"
 msgid "Difficulty selection menu keymappings"
 msgstr "Mapowanie klawiszy intra"
 
-#: engines/hypno/metaengine.cpp:169
+#: engines/hypno/metaengine.cpp:228
 #, fuzzy
 #| msgid "Start menu keymappings"
 msgid "Retry menu keymappings"
 msgstr "Mapowanie klawiszy menu start"
 
-#: engines/hypno/metaengine.cpp:172 engines/hypno/metaengine.cpp:270
-#: engines/hypno/metaengine.cpp:322
+#: engines/hypno/metaengine.cpp:231 engines/hypno/metaengine.cpp:327
+#: engines/hypno/metaengine.cpp:382
 #, fuzzy
 #| msgid "Skip speech"
 msgid "Skip level (cheat)"
@@ -10494,61 +10551,85 @@ msgstr "Pomi?? rozmow??"
 #. I18N: (Game name: Soldier Boyz) player refers to the users own character.
 #. I18N: (Game name: Marvel Comics Spider-Man: The Sinister Six) player refers to the users own character.
 #. I18N: (Game name: Wetlands) player refers to the users own character.
-#: engines/hypno/metaengine.cpp:180 engines/hypno/metaengine.cpp:278
-#: engines/hypno/metaengine.cpp:329
+#: engines/hypno/metaengine.cpp:238 engines/hypno/metaengine.cpp:334
+#: engines/hypno/metaengine.cpp:388
 msgid "Kill player"
 msgstr ""
 
 #. I18N: (Game name: Soldier Boyz) the game has 3 difficulty levels: Chump, Punk and Badass. Chump is the easy mode.
-#: engines/hypno/metaengine.cpp:205
+#: engines/hypno/metaengine.cpp:262
 msgid "Chump"
 msgstr ""
 
 #. I18N: (Game name: Soldier Boyz) the game has 3 difficulty levels: Chump, Punk and Badass. Punk is the medium mode.
-#: engines/hypno/metaengine.cpp:212
+#: engines/hypno/metaengine.cpp:269
 msgid "Punk"
 msgstr ""
 
 #. I18N: (Game name: Soldier Boyz) the game has 3 difficulty levels: Chump, Punk and Badass. Badass is the hard mode.
-#: engines/hypno/metaengine.cpp:219
+#: engines/hypno/metaengine.cpp:276
 msgid "Badass"
 msgstr ""
 
 #. I18N: (Game name: Soldier Boyz) This makes the player restart from the last checkpoint.
-#: engines/hypno/metaengine.cpp:232
+#: engines/hypno/metaengine.cpp:289
 #, fuzzy
 #| msgid "Retreat"
 msgid "Retry sector"
 msgstr "Ucieczka"
 
 #. I18N: (Game name: Soldier Boyz) This makes the player restart the current mission / level.
-#: engines/hypno/metaengine.cpp:239
+#: engines/hypno/metaengine.cpp:296
 #, fuzzy
 #| msgid "Restart animation"
 msgid "Restart territory"
 msgstr "Uruchom animacj?? ponownie"
 
-#: engines/hypno/metaengine.cpp:245
+#: engines/hypno/metaengine.cpp:302
 msgid "Begin new mission"
 msgstr ""
 
-#: engines/hypno/metaengine.cpp:313
+#: engines/hypno/metaengine.cpp:374
 #, fuzzy
 #| msgid "Pause menu keymappings"
 msgid "Pause keymappings"
 msgstr "Mapowanie klawiszy menu pauzy"
 
-#: engines/hypno/metaengine.cpp:314
+#: engines/hypno/metaengine.cpp:375
 #, fuzzy
 #| msgid "Intro keymappings"
 msgid "Direction keymappings"
 msgstr "Mapowanie klawiszy intra"
 
-#: engines/hypno/metaengine.cpp:316 engines/mads/metaengine.cpp:391
+#: engines/hypno/metaengine.cpp:377 engines/mads/metaengine.cpp:391
 #: engines/tsage/metaengine.cpp:259
 msgid "Show credits"
 msgstr "Wy??wietl autor??w"
 
+#: engines/hypno/metaengine.cpp:417
+#, fuzzy
+#| msgid "Move left"
+msgid "Menu left"
+msgstr "Ruch w lewo"
+
+#: engines/hypno/metaengine.cpp:425
+#, fuzzy
+#| msgid "Move down"
+msgid "Menu down"
+msgstr "Ruch w d????"
+
+#: engines/hypno/metaengine.cpp:433
+#, fuzzy
+#| msgid "Move right"
+msgid "Menu right"
+msgstr "Ruch w prawo"
+
+#: engines/hypno/metaengine.cpp:441
+#, fuzzy
+#| msgid "Menu"
+msgid "Menu up"
+msgstr "Menu"
+
 #. I18N: Action in In Cold Blood
 #: engines/icb/icb.cpp:107
 msgid "Arm"
@@ -13963,30 +14044,6 @@ msgstr ""
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1188 engines/scumm/metaengine.cpp:1265
-#, fuzzy
-#| msgid "Pick up"
-msgid "Stick up"
-msgstr "Podnie??"
-
-#: engines/scumm/metaengine.cpp:1195 engines/scumm/metaengine.cpp:1272
-#, fuzzy
-#| msgid "Scroll down"
-msgid "Stick down"
-msgstr "Przewi?? w d????"
-
-#: engines/scumm/metaengine.cpp:1202 engines/scumm/metaengine.cpp:1279
-#, fuzzy
-#| msgid "Slide left"
-msgid "Stick left"
-msgstr "??lizg w lewo"
-
-#: engines/scumm/metaengine.cpp:1209 engines/scumm/metaengine.cpp:1286
-#, fuzzy
-#| msgid "Slide right"
-msgid "Stick right"
-msgstr "??lizg w prawo"
-
 #: engines/scumm/metaengine.cpp:1216 engines/scumm/metaengine.cpp:1293
 #, fuzzy
 #| msgid "Move / Select"
@@ -18946,9 +19003,6 @@ msgstr ""
 #~ msgid "You must map a key to the 'Right Click' action to play this game"
 #~ msgstr "Musisz przypisa?? klawisz do 'PPM', by zagra?? w t?? gr??"
 
-#~ msgid "Map hide toolbar action"
-#~ msgstr "Przypisz chowanie paska narz??dzi"
-
 #~ msgid "You must map a key to the 'Hide toolbar' action to play this game"
 #~ msgstr ""
 #~ "Musisz przypisa?? przycisk 'Schowaj pasek narz??dzi', by zagra?? w t?? gr??"
diff --git a/po/pt_BR.po b/po/pt_BR.po
index 336737a649a..41e2cceb1d4 100644
--- a/po/pt_BR.po
+++ b/po/pt_BR.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-13 06:14+0000\n"
+"POT-Creation-Date: 2026-06-13 10:20+0000\n"
 "PO-Revision-Date: 2024-11-16 02:13+0000\n"
 "Last-Translator: Marcel Souza Lemes <marcosoutsider at gmail.com>\n"
 "Language-Team: Portuguese (Brazil) <https://translations.scummvm.org/"
@@ -137,7 +137,7 @@ msgstr "Para cima"
 #: engines/buried/saveload.cpp:83 engines/cine/saveload.cpp:857
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:123
 #: engines/drascula/saveload.cpp:51 engines/grim/inputdialog.cpp:84
-#: engines/hypno/metaengine.cpp:225 engines/mohawk/myst_stacks/menu.cpp:283
+#: engines/hypno/metaengine.cpp:282 engines/mohawk/myst_stacks/menu.cpp:283
 #: engines/mohawk/myst_stacks/menu.cpp:300
 #: engines/mohawk/myst_stacks/menu.cpp:318
 #: engines/mohawk/riven_stacks/aspit.cpp:349
@@ -342,7 +342,7 @@ msgstr "Outro Armazenamento est?? ativo no momento. Deseja interromp??-lo?"
 #: engines/chamber/metaengine.cpp:91 engines/director/events.cpp:122
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:139
 #: engines/efh/metaengine.cpp:362 engines/grim/grim.cpp:366
-#: engines/hypno/metaengine.cpp:192 engines/kyra/gui/saveload_eob.cpp:611
+#: engines/hypno/metaengine.cpp:249 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:197
 #: engines/mm/mm1/metaengine.cpp:54 engines/parallaction/metaengine.cpp:169
 #: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
@@ -361,7 +361,7 @@ msgstr "Sim"
 #: engines/chamber/metaengine.cpp:97 engines/director/events.cpp:122
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:139
 #: engines/efh/metaengine.cpp:368 engines/grim/grim.cpp:366
-#: engines/hypno/metaengine.cpp:198 engines/kyra/gui/saveload_eob.cpp:611
+#: engines/hypno/metaengine.cpp:255 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:204
 #: engines/mm/mm1/metaengine.cpp:52 engines/parallaction/metaengine.cpp:175
 #: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
@@ -3470,7 +3470,7 @@ msgstr "Engine de jogos de aventura"
 #: backends/events/default/default-events.cpp:358
 #: engines/adl/metaengine.cpp:352 engines/buried/metaengine.cpp:154
 #: engines/chamber/metaengine.cpp:84 engines/drascula/metaengine.cpp:304
-#: engines/hdb/metaengine.cpp:275 engines/hypno/metaengine.cpp:251
+#: engines/hdb/metaengine.cpp:275 engines/hypno/metaengine.cpp:308
 #: engines/lab/metaengine.cpp:172 engines/mohawk/myst_stacks/menu.cpp:317
 #: engines/mohawk/riven_stacks/aspit.cpp:454
 #: engines/neverhood/metaengine.cpp:200 engines/saga/metaengine.cpp:449
@@ -4051,8 +4051,9 @@ msgstr "Clique com o bot??o direito"
 #: engines/buried/metaengine.cpp:167 engines/crab/input/input.cpp:261
 #: engines/cruise/metaengine.cpp:217 engines/dm/metaengine.cpp:180
 #: engines/grim/grim.cpp:550 engines/hdb/metaengine.cpp:258
-#: engines/hypno/metaengine.cpp:186 engines/hypno/metaengine.cpp:369
-#: engines/icb/icb.cpp:137 engines/icb/icb.cpp:203 engines/mohawk/myst.cpp:595
+#: engines/hypno/metaengine.cpp:243 engines/hypno/metaengine.cpp:339
+#: engines/hypno/metaengine.cpp:456 engines/icb/icb.cpp:137
+#: engines/icb/icb.cpp:203 engines/mohawk/myst.cpp:595
 #: engines/mohawk/riven.cpp:876 engines/ngi/metaengine.cpp:136
 #: engines/sky/metaengine.cpp:126 engines/sludge/keymapper_tables.h:52
 #: engines/sludge/keymapper_tables.h:125 engines/sludge/keymapper_tables.h:187
@@ -4995,7 +4996,7 @@ msgstr "Y"
 #: backends/platform/sdl/opendingux/opendingux.cpp:57
 #: backends/platform/sdl/ps3/ps3.cpp:50 backends/platform/sdl/psp2/psp2.cpp:51
 #: engines/bagel/metaengine.cpp:64 engines/efh/metaengine.cpp:397
-#: engines/got/metaengine.cpp:76 engines/hypno/metaengine.cpp:363
+#: engines/got/metaengine.cpp:76 engines/hypno/metaengine.cpp:449
 #: engines/mm/mm1/metaengine.cpp:50 engines/mm/mm1/metaengine.cpp:97
 #: engines/sherlock/metaengine.cpp:660 engines/sherlock/metaengine.cpp:672
 #: engines/sherlock/metaengine.cpp:1014 engines/supernova/metaengine.cpp:303
@@ -6607,7 +6608,7 @@ msgstr "Pular v??deo"
 #: engines/access/metaengine.cpp:237 engines/asylum/metaengine.cpp:171
 #: engines/asylum/metaengine.cpp:198 engines/cine/metaengine.cpp:377
 #: engines/efh/metaengine.cpp:286 engines/hdb/metaengine.cpp:213
-#: engines/hypno/metaengine.cpp:302 engines/hypno/metaengine.cpp:356
+#: engines/hypno/metaengine.cpp:363 engines/hypno/metaengine.cpp:411
 #: engines/nancy/input.cpp:195 engines/tinsel/metaengine.cpp:263
 #: engines/titanic/metaengine.cpp:324 engines/ultima/ultima8/metaengine.cpp:52
 msgid "Move up"
@@ -6617,7 +6618,7 @@ msgstr "Mover para cima"
 #: engines/access/metaengine.cpp:244 engines/asylum/metaengine.cpp:177
 #: engines/asylum/metaengine.cpp:205 engines/cine/metaengine.cpp:384
 #: engines/efh/metaengine.cpp:293 engines/hdb/metaengine.cpp:219
-#: engines/hypno/metaengine.cpp:290 engines/hypno/metaengine.cpp:342
+#: engines/hypno/metaengine.cpp:351 engines/hypno/metaengine.cpp:399
 #: engines/nancy/input.cpp:201 engines/tinsel/metaengine.cpp:269
 #: engines/titanic/metaengine.cpp:330 engines/ultima/ultima8/metaengine.cpp:53
 msgid "Move down"
@@ -6628,7 +6629,7 @@ msgstr "Mover para baixo"
 #: engines/access/metaengine.cpp:251 engines/asylum/metaengine.cpp:183
 #: engines/asylum/metaengine.cpp:212 engines/cine/metaengine.cpp:391
 #: engines/efh/metaengine.cpp:300 engines/hdb/metaengine.cpp:225
-#: engines/hypno/metaengine.cpp:284 engines/hypno/metaengine.cpp:335
+#: engines/hypno/metaengine.cpp:345 engines/hypno/metaengine.cpp:393
 #: engines/kyra/engine/eobcommon.cpp:373 engines/lure/metaengine.cpp:218
 #: engines/nancy/input.cpp:207 engines/tinsel/metaengine.cpp:275
 #: engines/titanic/metaengine.cpp:312 engines/titanic/metaengine.cpp:390
@@ -6641,7 +6642,7 @@ msgstr "Mover para esquerda"
 #: engines/access/metaengine.cpp:258 engines/asylum/metaengine.cpp:189
 #: engines/asylum/metaengine.cpp:219 engines/cine/metaengine.cpp:398
 #: engines/efh/metaengine.cpp:307 engines/hdb/metaengine.cpp:231
-#: engines/hypno/metaengine.cpp:296 engines/hypno/metaengine.cpp:349
+#: engines/hypno/metaengine.cpp:357 engines/hypno/metaengine.cpp:405
 #: engines/kyra/engine/eobcommon.cpp:374 engines/lure/metaengine.cpp:226
 #: engines/nancy/input.cpp:213 engines/tinsel/metaengine.cpp:281
 #: engines/titanic/metaengine.cpp:318 engines/titanic/metaengine.cpp:396
@@ -7138,8 +7139,8 @@ msgstr "AGOS principal"
 #: engines/cine/metaengine.cpp:271 engines/cruise/metaengine.cpp:169
 #: engines/dm/metaengine.cpp:126 engines/draci/metaengine.cpp:196
 #: engines/drascula/metaengine.cpp:206 engines/hopkins/metaengine.cpp:207
-#: engines/hugo/metaengine.cpp:130 engines/hypno/metaengine.cpp:166
-#: engines/hypno/metaengine.cpp:267 engines/hypno/metaengine.cpp:311
+#: engines/hugo/metaengine.cpp:130 engines/hypno/metaengine.cpp:225
+#: engines/hypno/metaengine.cpp:324 engines/hypno/metaengine.cpp:372
 #: engines/illusions/metaengine.cpp:90 engines/lab/metaengine.cpp:152
 #: engines/lure/metaengine.cpp:169 engines/made/metaengine.cpp:133
 #: engines/mads/metaengine.cpp:317 engines/neverhood/metaengine.cpp:186
@@ -7879,7 +7880,7 @@ msgstr "Alternar Modo de Combate"
 
 #. I18N: This keymap allows skipping video cutscenes
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
-#: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
+#: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:210
 #: engines/phoenixvr/metaengine.cpp:94 engines/scumm/help.cpp:79
 #: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
@@ -9204,7 +9205,7 @@ msgstr "Selecionar Ator 1"
 msgid "Select option 14"
 msgstr "Selecionar Ator 1"
 
-#: engines/efh/metaengine.cpp:257 engines/hypno/metaengine.cpp:312
+#: engines/efh/metaengine.cpp:257 engines/hypno/metaengine.cpp:373
 #: engines/sludge/keymapper_tables.h:35 engines/supernova/metaengine.cpp:218
 #, fuzzy
 #| msgid "Game keymappings"
@@ -10914,43 +10915,99 @@ msgstr ""
 msgid "Intro keymappings"
 msgstr "Mapeamentos de teclas de ??ndice"
 
-#: engines/hypno/metaengine.cpp:134
+#: engines/hypno/metaengine.cpp:134 engines/hypno/metaengine.cpp:144
 msgid "Primary shoot"
 msgstr ""
 
-#: engines/hypno/metaengine.cpp:140
+#: engines/hypno/metaengine.cpp:139
 #, fuzzy
 #| msgid "Seconds"
 msgid "Secondary shoot"
 msgstr "Segundos"
 
-#: engines/hypno/metaengine.cpp:146 engines/lab/metaengine.cpp:209
+#: engines/hypno/metaengine.cpp:149
+#, fuzzy
+#| msgid "Map hide toolbar action"
+msgid "Rapid fire / secondary action"
+msgstr "Mapear \"Ocultar barra de ferramentas\""
+
+#: engines/hypno/metaengine.cpp:155
+#, fuzzy
+#| msgid "Slide Left"
+msgid "Aim left"
+msgstr "Deslizar para Esquerda"
+
+#: engines/hypno/metaengine.cpp:160
+#, fuzzy
+#| msgid "Scroll down"
+msgid "Aim down"
+msgstr "Rolar para baixo"
+
+#: engines/hypno/metaengine.cpp:165
+#, fuzzy
+#| msgid "Up Right"
+msgid "Aim right"
+msgstr "Para Cima ?? Direita"
+
+#: engines/hypno/metaengine.cpp:170
+msgid "Aim up"
+msgstr ""
+
+#: engines/hypno/metaengine.cpp:175 engines/scumm/metaengine.cpp:1202
+#: engines/scumm/metaengine.cpp:1279
+#, fuzzy
+#| msgid "Slide Left"
+msgid "Stick left"
+msgstr "Deslizar para Esquerda"
+
+#: engines/hypno/metaengine.cpp:182 engines/scumm/metaengine.cpp:1195
+#: engines/scumm/metaengine.cpp:1272
+#, fuzzy
+#| msgid "Scroll down"
+msgid "Stick down"
+msgstr "Rolar para baixo"
+
+#: engines/hypno/metaengine.cpp:189 engines/scumm/metaengine.cpp:1209
+#: engines/scumm/metaengine.cpp:1286
+#, fuzzy
+#| msgid "Slide Right"
+msgid "Stick right"
+msgstr "Deslizar para Direita"
+
+#: engines/hypno/metaengine.cpp:196 engines/scumm/metaengine.cpp:1188
+#: engines/scumm/metaengine.cpp:1265
+#, fuzzy
+#| msgid "Pick up"
+msgid "Stick up"
+msgstr "Pegar"
+
+#: engines/hypno/metaengine.cpp:203 engines/lab/metaengine.cpp:209
 #: engines/ngi/metaengine.cpp:168 engines/sherlock/metaengine.cpp:872
 #: engines/teenagent/metaengine.cpp:232 engines/toon/metaengine.cpp:98
 #: engines/voyeur/metaengine.cpp:235
 msgid "Skip intro"
 msgstr "Pular introdu????o"
 
-#: engines/hypno/metaengine.cpp:167
+#: engines/hypno/metaengine.cpp:226
 #, fuzzy
 #| msgid "Escape keymappings"
 msgid "Exit menu keymappings"
 msgstr "Mapeamentos da tecla ESC"
 
-#: engines/hypno/metaengine.cpp:168
+#: engines/hypno/metaengine.cpp:227
 #, fuzzy
 #| msgid "Index keymappings"
 msgid "Difficulty selection menu keymappings"
 msgstr "Mapeamentos de teclas de ??ndice"
 
-#: engines/hypno/metaengine.cpp:169
+#: engines/hypno/metaengine.cpp:228
 #, fuzzy
 #| msgid "Start menu keymappings"
 msgid "Retry menu keymappings"
 msgstr "Mapeamento de teclas do menu Iniciar"
 
-#: engines/hypno/metaengine.cpp:172 engines/hypno/metaengine.cpp:270
-#: engines/hypno/metaengine.cpp:322
+#: engines/hypno/metaengine.cpp:231 engines/hypno/metaengine.cpp:327
+#: engines/hypno/metaengine.cpp:382
 #, fuzzy
 #| msgid "Skip speech"
 msgid "Skip level (cheat)"
@@ -10959,61 +11016,85 @@ msgstr "Pular fala"
 #. I18N: (Game name: Soldier Boyz) player refers to the users own character.
 #. I18N: (Game name: Marvel Comics Spider-Man: The Sinister Six) player refers to the users own character.
 #. I18N: (Game name: Wetlands) player refers to the users own character.
-#: engines/hypno/metaengine.cpp:180 engines/hypno/metaengine.cpp:278
-#: engines/hypno/metaengine.cpp:329
+#: engines/hypno/metaengine.cpp:238 engines/hypno/metaengine.cpp:334
+#: engines/hypno/metaengine.cpp:388
 msgid "Kill player"
 msgstr ""
 
 #. I18N: (Game name: Soldier Boyz) the game has 3 difficulty levels: Chump, Punk and Badass. Chump is the easy mode.
-#: engines/hypno/metaengine.cpp:205
+#: engines/hypno/metaengine.cpp:262
 msgid "Chump"
 msgstr ""
 
 #. I18N: (Game name: Soldier Boyz) the game has 3 difficulty levels: Chump, Punk and Badass. Punk is the medium mode.
-#: engines/hypno/metaengine.cpp:212
+#: engines/hypno/metaengine.cpp:269
 msgid "Punk"
 msgstr ""
 
 #. I18N: (Game name: Soldier Boyz) the game has 3 difficulty levels: Chump, Punk and Badass. Badass is the hard mode.
-#: engines/hypno/metaengine.cpp:219
+#: engines/hypno/metaengine.cpp:276
 msgid "Badass"
 msgstr ""
 
 #. I18N: (Game name: Soldier Boyz) This makes the player restart from the last checkpoint.
-#: engines/hypno/metaengine.cpp:232
+#: engines/hypno/metaengine.cpp:289
 #, fuzzy
 #| msgid "Retreat"
 msgid "Retry sector"
 msgstr "Recuar"
 
 #. I18N: (Game name: Soldier Boyz) This makes the player restart the current mission / level.
-#: engines/hypno/metaengine.cpp:239
+#: engines/hypno/metaengine.cpp:296
 #, fuzzy
 #| msgid "Restart animation"
 msgid "Restart territory"
 msgstr "Reiniciar anima????o"
 
-#: engines/hypno/metaengine.cpp:245
+#: engines/hypno/metaengine.cpp:302
 msgid "Begin new mission"
 msgstr ""
 
-#: engines/hypno/metaengine.cpp:313
+#: engines/hypno/metaengine.cpp:374
 #, fuzzy
 #| msgid "Game keymappings"
 msgid "Pause keymappings"
 msgstr "Mapeamento de teclas do jogo"
 
-#: engines/hypno/metaengine.cpp:314
+#: engines/hypno/metaengine.cpp:375
 #, fuzzy
 #| msgid "Index keymappings"
 msgid "Direction keymappings"
 msgstr "Mapeamentos de teclas de ??ndice"
 
-#: engines/hypno/metaengine.cpp:316 engines/mads/metaengine.cpp:391
+#: engines/hypno/metaengine.cpp:377 engines/mads/metaengine.cpp:391
 #: engines/tsage/metaengine.cpp:259
 msgid "Show credits"
 msgstr "Exibir cr??ditos"
 
+#: engines/hypno/metaengine.cpp:417
+#, fuzzy
+#| msgid "Move left"
+msgid "Menu left"
+msgstr "Mover para esquerda"
+
+#: engines/hypno/metaengine.cpp:425
+#, fuzzy
+#| msgid "Move down"
+msgid "Menu down"
+msgstr "Mover para baixo"
+
+#: engines/hypno/metaengine.cpp:433
+#, fuzzy
+#| msgid "Move right"
+msgid "Menu right"
+msgstr "Mover para direita"
+
+#: engines/hypno/metaengine.cpp:441
+#, fuzzy
+#| msgid "Menu"
+msgid "Menu up"
+msgstr "Menu"
+
 #. I18N: Action in In Cold Blood
 #: engines/icb/icb.cpp:107
 msgid "Arm"
@@ -14563,30 +14644,6 @@ msgstr ""
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1188 engines/scumm/metaengine.cpp:1265
-#, fuzzy
-#| msgid "Pick up"
-msgid "Stick up"
-msgstr "Pegar"
-
-#: engines/scumm/metaengine.cpp:1195 engines/scumm/metaengine.cpp:1272
-#, fuzzy
-#| msgid "Scroll down"
-msgid "Stick down"
-msgstr "Rolar para baixo"
-
-#: engines/scumm/metaengine.cpp:1202 engines/scumm/metaengine.cpp:1279
-#, fuzzy
-#| msgid "Slide Left"
-msgid "Stick left"
-msgstr "Deslizar para Esquerda"
-
-#: engines/scumm/metaengine.cpp:1209 engines/scumm/metaengine.cpp:1286
-#, fuzzy
-#| msgid "Slide Right"
-msgid "Stick right"
-msgstr "Deslizar para Direita"
-
 #: engines/scumm/metaengine.cpp:1216 engines/scumm/metaengine.cpp:1293
 #, fuzzy
 #| msgid "Move left"
@@ -20557,9 +20614,6 @@ msgstr ""
 #~ "Voc?? precisa mapear uma tecla para a????o do \"Clique da Direita\" nesse "
 #~ "jogo"
 
-#~ msgid "Map hide toolbar action"
-#~ msgstr "Mapear \"Ocultar barra de ferramentas\""
-
 #~ msgid "You must map a key to the 'Hide toolbar' action to play this game"
 #~ msgstr ""
 #~ "Voc?? precisa mapear uma tecla para a????o do \"Ocultar barra de "
diff --git a/po/pt_PT.po b/po/pt_PT.po
index 01e59142ec1..de747786e7e 100644
--- a/po/pt_PT.po
+++ b/po/pt_PT.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.10.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-13 06:14+0000\n"
+"POT-Creation-Date: 2026-06-13 10:20+0000\n"
 "PO-Revision-Date: 2022-06-05 14:09+0000\n"
 "Last-Translator: Daniel <supsuper at gmail.com>\n"
 "Language-Team: Portuguese (Portugal) <https://translations.scummvm.org/"
@@ -140,7 +140,7 @@ msgstr "Subir"
 #: engines/buried/saveload.cpp:83 engines/cine/saveload.cpp:857
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:123
 #: engines/drascula/saveload.cpp:51 engines/grim/inputdialog.cpp:84
-#: engines/hypno/metaengine.cpp:225 engines/mohawk/myst_stacks/menu.cpp:283
+#: engines/hypno/metaengine.cpp:282 engines/mohawk/myst_stacks/menu.cpp:283
 #: engines/mohawk/myst_stacks/menu.cpp:300
 #: engines/mohawk/myst_stacks/menu.cpp:318
 #: engines/mohawk/riven_stacks/aspit.cpp:349
@@ -354,7 +354,7 @@ msgstr "Outro armazenamento est?? ocupado. Deseja interromp??-lo?"
 #: engines/chamber/metaengine.cpp:91 engines/director/events.cpp:122
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:139
 #: engines/efh/metaengine.cpp:362 engines/grim/grim.cpp:366
-#: engines/hypno/metaengine.cpp:192 engines/kyra/gui/saveload_eob.cpp:611
+#: engines/hypno/metaengine.cpp:249 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:197
 #: engines/mm/mm1/metaengine.cpp:54 engines/parallaction/metaengine.cpp:169
 #: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
@@ -373,7 +373,7 @@ msgstr "Sim"
 #: engines/chamber/metaengine.cpp:97 engines/director/events.cpp:122
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:139
 #: engines/efh/metaengine.cpp:368 engines/grim/grim.cpp:366
-#: engines/hypno/metaengine.cpp:198 engines/kyra/gui/saveload_eob.cpp:611
+#: engines/hypno/metaengine.cpp:255 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:204
 #: engines/mm/mm1/metaengine.cpp:52 engines/parallaction/metaengine.cpp:175
 #: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
@@ -3362,7 +3362,7 @@ msgstr ""
 #: backends/events/default/default-events.cpp:358
 #: engines/adl/metaengine.cpp:352 engines/buried/metaengine.cpp:154
 #: engines/chamber/metaengine.cpp:84 engines/drascula/metaengine.cpp:304
-#: engines/hdb/metaengine.cpp:275 engines/hypno/metaengine.cpp:251
+#: engines/hdb/metaengine.cpp:275 engines/hypno/metaengine.cpp:308
 #: engines/lab/metaengine.cpp:172 engines/mohawk/myst_stacks/menu.cpp:317
 #: engines/mohawk/riven_stacks/aspit.cpp:454
 #: engines/neverhood/metaengine.cpp:200 engines/saga/metaengine.cpp:449
@@ -3929,8 +3929,9 @@ msgstr "Clique direito"
 #: engines/buried/metaengine.cpp:167 engines/crab/input/input.cpp:261
 #: engines/cruise/metaengine.cpp:217 engines/dm/metaengine.cpp:180
 #: engines/grim/grim.cpp:550 engines/hdb/metaengine.cpp:258
-#: engines/hypno/metaengine.cpp:186 engines/hypno/metaengine.cpp:369
-#: engines/icb/icb.cpp:137 engines/icb/icb.cpp:203 engines/mohawk/myst.cpp:595
+#: engines/hypno/metaengine.cpp:243 engines/hypno/metaengine.cpp:339
+#: engines/hypno/metaengine.cpp:456 engines/icb/icb.cpp:137
+#: engines/icb/icb.cpp:203 engines/mohawk/myst.cpp:595
 #: engines/mohawk/riven.cpp:876 engines/ngi/metaengine.cpp:136
 #: engines/sky/metaengine.cpp:126 engines/sludge/keymapper_tables.h:52
 #: engines/sludge/keymapper_tables.h:125 engines/sludge/keymapper_tables.h:187
@@ -4867,7 +4868,7 @@ msgstr "Y"
 #: backends/platform/sdl/opendingux/opendingux.cpp:57
 #: backends/platform/sdl/ps3/ps3.cpp:50 backends/platform/sdl/psp2/psp2.cpp:51
 #: engines/bagel/metaengine.cpp:64 engines/efh/metaengine.cpp:397
-#: engines/got/metaengine.cpp:76 engines/hypno/metaengine.cpp:363
+#: engines/got/metaengine.cpp:76 engines/hypno/metaengine.cpp:449
 #: engines/mm/mm1/metaengine.cpp:50 engines/mm/mm1/metaengine.cpp:97
 #: engines/sherlock/metaengine.cpp:660 engines/sherlock/metaengine.cpp:672
 #: engines/sherlock/metaengine.cpp:1014 engines/supernova/metaengine.cpp:303
@@ -6070,7 +6071,7 @@ msgstr "Ignorar linha"
 #: engines/access/metaengine.cpp:237 engines/asylum/metaengine.cpp:171
 #: engines/asylum/metaengine.cpp:198 engines/cine/metaengine.cpp:377
 #: engines/efh/metaengine.cpp:286 engines/hdb/metaengine.cpp:213
-#: engines/hypno/metaengine.cpp:302 engines/hypno/metaengine.cpp:356
+#: engines/hypno/metaengine.cpp:363 engines/hypno/metaengine.cpp:411
 #: engines/nancy/input.cpp:195 engines/tinsel/metaengine.cpp:263
 #: engines/titanic/metaengine.cpp:324 engines/ultima/ultima8/metaengine.cpp:52
 msgid "Move up"
@@ -6080,7 +6081,7 @@ msgstr "Mover para cima"
 #: engines/access/metaengine.cpp:244 engines/asylum/metaengine.cpp:177
 #: engines/asylum/metaengine.cpp:205 engines/cine/metaengine.cpp:384
 #: engines/efh/metaengine.cpp:293 engines/hdb/metaengine.cpp:219
-#: engines/hypno/metaengine.cpp:290 engines/hypno/metaengine.cpp:342
+#: engines/hypno/metaengine.cpp:351 engines/hypno/metaengine.cpp:399
 #: engines/nancy/input.cpp:201 engines/tinsel/metaengine.cpp:269
 #: engines/titanic/metaengine.cpp:330 engines/ultima/ultima8/metaengine.cpp:53
 msgid "Move down"
@@ -6091,7 +6092,7 @@ msgstr "Mover para baixo"
 #: engines/access/metaengine.cpp:251 engines/asylum/metaengine.cpp:183
 #: engines/asylum/metaengine.cpp:212 engines/cine/metaengine.cpp:391
 #: engines/efh/metaengine.cpp:300 engines/hdb/metaengine.cpp:225
-#: engines/hypno/metaengine.cpp:284 engines/hypno/metaengine.cpp:335
+#: engines/hypno/metaengine.cpp:345 engines/hypno/metaengine.cpp:393
 #: engines/kyra/engine/eobcommon.cpp:373 engines/lure/metaengine.cpp:218
 #: engines/nancy/input.cpp:207 engines/tinsel/metaengine.cpp:275
 #: engines/titanic/metaengine.cpp:312 engines/titanic/metaengine.cpp:390
@@ -6104,7 +6105,7 @@ msgstr "Mover para a esquerda"
 #: engines/access/metaengine.cpp:258 engines/asylum/metaengine.cpp:189
 #: engines/asylum/metaengine.cpp:219 engines/cine/metaengine.cpp:398
 #: engines/efh/metaengine.cpp:307 engines/hdb/metaengine.cpp:231
-#: engines/hypno/metaengine.cpp:296 engines/hypno/metaengine.cpp:349
+#: engines/hypno/metaengine.cpp:357 engines/hypno/metaengine.cpp:405
 #: engines/kyra/engine/eobcommon.cpp:374 engines/lure/metaengine.cpp:226
 #: engines/nancy/input.cpp:213 engines/tinsel/metaengine.cpp:281
 #: engines/titanic/metaengine.cpp:318 engines/titanic/metaengine.cpp:396
@@ -6597,8 +6598,8 @@ msgstr ""
 #: engines/cine/metaengine.cpp:271 engines/cruise/metaengine.cpp:169
 #: engines/dm/metaengine.cpp:126 engines/draci/metaengine.cpp:196
 #: engines/drascula/metaengine.cpp:206 engines/hopkins/metaengine.cpp:207
-#: engines/hugo/metaengine.cpp:130 engines/hypno/metaengine.cpp:166
-#: engines/hypno/metaengine.cpp:267 engines/hypno/metaengine.cpp:311
+#: engines/hugo/metaengine.cpp:130 engines/hypno/metaengine.cpp:225
+#: engines/hypno/metaengine.cpp:324 engines/hypno/metaengine.cpp:372
 #: engines/illusions/metaengine.cpp:90 engines/lab/metaengine.cpp:152
 #: engines/lure/metaengine.cpp:169 engines/made/metaengine.cpp:133
 #: engines/mads/metaengine.cpp:317 engines/neverhood/metaengine.cpp:186
@@ -7318,7 +7319,7 @@ msgstr "Ativar/desativar combate"
 
 #. I18N: This keymap allows skipping video cutscenes
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
-#: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
+#: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:210
 #: engines/phoenixvr/metaengine.cpp:94 engines/scumm/help.cpp:79
 #: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
@@ -8632,7 +8633,7 @@ msgstr "Agrupar por"
 msgid "Select option 14"
 msgstr "Agrupar por"
 
-#: engines/efh/metaengine.cpp:257 engines/hypno/metaengine.cpp:312
+#: engines/efh/metaengine.cpp:257 engines/hypno/metaengine.cpp:373
 #: engines/sludge/keymapper_tables.h:35 engines/supernova/metaengine.cpp:218
 #, fuzzy
 #| msgid "Game Options"
@@ -10323,15 +10324,70 @@ msgstr ""
 msgid "Intro keymappings"
 msgstr "Op????es do Jogo"
 
-#: engines/hypno/metaengine.cpp:134
+#: engines/hypno/metaengine.cpp:134 engines/hypno/metaengine.cpp:144
 msgid "Primary shoot"
 msgstr ""
 
-#: engines/hypno/metaengine.cpp:140
+#: engines/hypno/metaengine.cpp:139
 msgid "Secondary shoot"
 msgstr ""
 
-#: engines/hypno/metaengine.cpp:146 engines/lab/metaengine.cpp:209
+#: engines/hypno/metaengine.cpp:149
+#, fuzzy
+#| msgid "Map hide toolbar action"
+msgid "Rapid fire / secondary action"
+msgstr "Mapear ac????o \"Esconder barra de ferramentas\""
+
+#: engines/hypno/metaengine.cpp:155
+#, fuzzy
+#| msgid "Slide Left"
+msgid "Aim left"
+msgstr "Deslizar ?? esquerda"
+
+#: engines/hypno/metaengine.cpp:160
+#, fuzzy
+#| msgid "Scroll down"
+msgid "Aim down"
+msgstr "Deslizar para baixo"
+
+#: engines/hypno/metaengine.cpp:165
+#, fuzzy
+msgid "Aim right"
+msgstr "Direita"
+
+#: engines/hypno/metaengine.cpp:170
+msgid "Aim up"
+msgstr ""
+
+#: engines/hypno/metaengine.cpp:175 engines/scumm/metaengine.cpp:1202
+#: engines/scumm/metaengine.cpp:1279
+#, fuzzy
+#| msgid "Slide Left"
+msgid "Stick left"
+msgstr "Deslizar ?? esquerda"
+
+#: engines/hypno/metaengine.cpp:182 engines/scumm/metaengine.cpp:1195
+#: engines/scumm/metaengine.cpp:1272
+#, fuzzy
+#| msgid "Scroll down"
+msgid "Stick down"
+msgstr "Deslizar para baixo"
+
+#: engines/hypno/metaengine.cpp:189 engines/scumm/metaengine.cpp:1209
+#: engines/scumm/metaengine.cpp:1286
+#, fuzzy
+#| msgid "Slide Right"
+msgid "Stick right"
+msgstr "Deslizar ?? direita"
+
+#: engines/hypno/metaengine.cpp:196 engines/scumm/metaengine.cpp:1188
+#: engines/scumm/metaengine.cpp:1265
+#, fuzzy
+#| msgid "Left Stick X"
+msgid "Stick up"
+msgstr "Anal??gico Esquerdo X"
+
+#: engines/hypno/metaengine.cpp:203 engines/lab/metaengine.cpp:209
 #: engines/ngi/metaengine.cpp:168 engines/sherlock/metaengine.cpp:872
 #: engines/teenagent/metaengine.cpp:232 engines/toon/metaengine.cpp:98
 #: engines/voyeur/metaengine.cpp:235
@@ -10340,26 +10396,26 @@ msgstr ""
 msgid "Skip intro"
 msgstr "Ignorar linha"
 
-#: engines/hypno/metaengine.cpp:167
+#: engines/hypno/metaengine.cpp:226
 #, fuzzy
 #| msgid "Game Options"
 msgid "Exit menu keymappings"
 msgstr "Op????es do Jogo"
 
-#: engines/hypno/metaengine.cpp:168
+#: engines/hypno/metaengine.cpp:227
 #, fuzzy
 #| msgid "Game Options"
 msgid "Difficulty selection menu keymappings"
 msgstr "Op????es do Jogo"
 
-#: engines/hypno/metaengine.cpp:169
+#: engines/hypno/metaengine.cpp:228
 #, fuzzy
 #| msgid "Game Options"
 msgid "Retry menu keymappings"
 msgstr "Op????es do Jogo"
 
-#: engines/hypno/metaengine.cpp:172 engines/hypno/metaengine.cpp:270
-#: engines/hypno/metaengine.cpp:322
+#: engines/hypno/metaengine.cpp:231 engines/hypno/metaengine.cpp:327
+#: engines/hypno/metaengine.cpp:382
 #, fuzzy
 #| msgid "Speech"
 msgid "Skip level (cheat)"
@@ -10368,60 +10424,84 @@ msgstr "Di??logo"
 #. I18N: (Game name: Soldier Boyz) player refers to the users own character.
 #. I18N: (Game name: Marvel Comics Spider-Man: The Sinister Six) player refers to the users own character.
 #. I18N: (Game name: Wetlands) player refers to the users own character.
-#: engines/hypno/metaengine.cpp:180 engines/hypno/metaengine.cpp:278
-#: engines/hypno/metaengine.cpp:329
+#: engines/hypno/metaengine.cpp:238 engines/hypno/metaengine.cpp:334
+#: engines/hypno/metaengine.cpp:388
 msgid "Kill player"
 msgstr ""
 
 #. I18N: (Game name: Soldier Boyz) the game has 3 difficulty levels: Chump, Punk and Badass. Chump is the easy mode.
-#: engines/hypno/metaengine.cpp:205
+#: engines/hypno/metaengine.cpp:262
 msgid "Chump"
 msgstr ""
 
 #. I18N: (Game name: Soldier Boyz) the game has 3 difficulty levels: Chump, Punk and Badass. Punk is the medium mode.
-#: engines/hypno/metaengine.cpp:212
+#: engines/hypno/metaengine.cpp:269
 msgid "Punk"
 msgstr ""
 
 #. I18N: (Game name: Soldier Boyz) the game has 3 difficulty levels: Chump, Punk and Badass. Badass is the hard mode.
-#: engines/hypno/metaengine.cpp:219
+#: engines/hypno/metaengine.cpp:276
 msgid "Badass"
 msgstr ""
 
 #. I18N: (Game name: Soldier Boyz) This makes the player restart from the last checkpoint.
-#: engines/hypno/metaengine.cpp:232
+#: engines/hypno/metaengine.cpp:289
 msgid "Retry sector"
 msgstr ""
 
 #. I18N: (Game name: Soldier Boyz) This makes the player restart the current mission / level.
-#: engines/hypno/metaengine.cpp:239
+#: engines/hypno/metaengine.cpp:296
 #, fuzzy
 #| msgid "Transitions:"
 msgid "Restart territory"
 msgstr "Transi????es:"
 
-#: engines/hypno/metaengine.cpp:245
+#: engines/hypno/metaengine.cpp:302
 msgid "Begin new mission"
 msgstr ""
 
-#: engines/hypno/metaengine.cpp:313
+#: engines/hypno/metaengine.cpp:374
 #, fuzzy
 #| msgid "Game Options"
 msgid "Pause keymappings"
 msgstr "Op????es do Jogo"
 
-#: engines/hypno/metaengine.cpp:314
+#: engines/hypno/metaengine.cpp:375
 #, fuzzy
 #| msgid "Game Options"
 msgid "Direction keymappings"
 msgstr "Op????es do Jogo"
 
-#: engines/hypno/metaengine.cpp:316 engines/mads/metaengine.cpp:391
+#: engines/hypno/metaengine.cpp:377 engines/mads/metaengine.cpp:391
 #: engines/tsage/metaengine.cpp:259
 #, fuzzy
 msgid "Show credits"
 msgstr "Mostrar cr??ditos do jogo"
 
+#: engines/hypno/metaengine.cpp:417
+#, fuzzy
+#| msgid "Move left"
+msgid "Menu left"
+msgstr "Mover para a esquerda"
+
+#: engines/hypno/metaengine.cpp:425
+#, fuzzy
+#| msgid "Move down"
+msgid "Menu down"
+msgstr "Mover para baixo"
+
+#: engines/hypno/metaengine.cpp:433
+#, fuzzy
+#| msgid "Move right"
+msgid "Menu right"
+msgstr "Mover para a direita"
+
+#: engines/hypno/metaengine.cpp:441
+#, fuzzy
+#| msgid "Menu"
+msgid "Menu up"
+msgstr "Menu"
+
 #. I18N: Action in In Cold Blood
 #: engines/icb/icb.cpp:107
 msgid "Arm"
@@ -13836,30 +13916,6 @@ msgstr ""
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1188 engines/scumm/metaengine.cpp:1265
-#, fuzzy
-#| msgid "Left Stick X"
-msgid "Stick up"
-msgstr "Anal??gico Esquerdo X"
-
-#: engines/scumm/metaengine.cpp:1195 engines/scumm/metaengine.cpp:1272
-#, fuzzy
-#| msgid "Scroll down"
-msgid "Stick down"
-msgstr "Deslizar para baixo"
-
-#: engines/scumm/metaengine.cpp:1202 engines/scumm/metaengine.cpp:1279
-#, fuzzy
-#| msgid "Slide Left"
-msgid "Stick left"
-msgstr "Deslizar ?? esquerda"
-
-#: engines/scumm/metaengine.cpp:1209 engines/scumm/metaengine.cpp:1286
-#, fuzzy
-#| msgid "Slide Right"
-msgid "Stick right"
-msgstr "Deslizar ?? direita"
-
 #: engines/scumm/metaengine.cpp:1216 engines/scumm/metaengine.cpp:1293
 #, fuzzy
 #| msgid "Move left"
@@ -19004,9 +19060,6 @@ msgstr ""
 #~ msgstr ""
 #~ "Deve mapear uma tecla para a ac????o \"Clique Direito\" para jogar este jogo"
 
-#~ msgid "Map hide toolbar action"
-#~ msgstr "Mapear ac????o \"Esconder barra de ferramentas\""
-
 #~ msgid "You must map a key to the 'Hide toolbar' action to play this game"
 #~ msgstr ""
 #~ "Deve mapear uma tecla para a fun????o \"Esconder barra de ferramentas\" "
diff --git a/po/ro.po b/po/ro.po
index 409ab4699a3..7eb626aa59c 100644
--- a/po/ro.po
+++ b/po/ro.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.10.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-13 06:14+0000\n"
+"POT-Creation-Date: 2026-06-13 10:20+0000\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: Automatically generated\n"
 "Language-Team: none\n"
@@ -129,7 +129,7 @@ msgstr ""
 #: engines/buried/saveload.cpp:83 engines/cine/saveload.cpp:857
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:123
 #: engines/drascula/saveload.cpp:51 engines/grim/inputdialog.cpp:84
-#: engines/hypno/metaengine.cpp:225 engines/mohawk/myst_stacks/menu.cpp:283
+#: engines/hypno/metaengine.cpp:282 engines/mohawk/myst_stacks/menu.cpp:283
 #: engines/mohawk/myst_stacks/menu.cpp:300
 #: engines/mohawk/myst_stacks/menu.cpp:318
 #: engines/mohawk/riven_stacks/aspit.cpp:349
@@ -325,7 +325,7 @@ msgstr ""
 #: engines/chamber/metaengine.cpp:91 engines/director/events.cpp:122
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:139
 #: engines/efh/metaengine.cpp:362 engines/grim/grim.cpp:366
-#: engines/hypno/metaengine.cpp:192 engines/kyra/gui/saveload_eob.cpp:611
+#: engines/hypno/metaengine.cpp:249 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:197
 #: engines/mm/mm1/metaengine.cpp:54 engines/parallaction/metaengine.cpp:169
 #: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
@@ -344,7 +344,7 @@ msgstr ""
 #: engines/chamber/metaengine.cpp:97 engines/director/events.cpp:122
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:139
 #: engines/efh/metaengine.cpp:368 engines/grim/grim.cpp:366
-#: engines/hypno/metaengine.cpp:198 engines/kyra/gui/saveload_eob.cpp:611
+#: engines/hypno/metaengine.cpp:255 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:204
 #: engines/mm/mm1/metaengine.cpp:52 engines/parallaction/metaengine.cpp:175
 #: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
@@ -3156,7 +3156,7 @@ msgstr ""
 #: backends/events/default/default-events.cpp:358
 #: engines/adl/metaengine.cpp:352 engines/buried/metaengine.cpp:154
 #: engines/chamber/metaengine.cpp:84 engines/drascula/metaengine.cpp:304
-#: engines/hdb/metaengine.cpp:275 engines/hypno/metaengine.cpp:251
+#: engines/hdb/metaengine.cpp:275 engines/hypno/metaengine.cpp:308
 #: engines/lab/metaengine.cpp:172 engines/mohawk/myst_stacks/menu.cpp:317
 #: engines/mohawk/riven_stacks/aspit.cpp:454
 #: engines/neverhood/metaengine.cpp:200 engines/saga/metaengine.cpp:449
@@ -3656,8 +3656,9 @@ msgstr ""
 #: engines/buried/metaengine.cpp:167 engines/crab/input/input.cpp:261
 #: engines/cruise/metaengine.cpp:217 engines/dm/metaengine.cpp:180
 #: engines/grim/grim.cpp:550 engines/hdb/metaengine.cpp:258
-#: engines/hypno/metaengine.cpp:186 engines/hypno/metaengine.cpp:369
-#: engines/icb/icb.cpp:137 engines/icb/icb.cpp:203 engines/mohawk/myst.cpp:595
+#: engines/hypno/metaengine.cpp:243 engines/hypno/metaengine.cpp:339
+#: engines/hypno/metaengine.cpp:456 engines/icb/icb.cpp:137
+#: engines/icb/icb.cpp:203 engines/mohawk/myst.cpp:595
 #: engines/mohawk/riven.cpp:876 engines/ngi/metaengine.cpp:136
 #: engines/sky/metaengine.cpp:126 engines/sludge/keymapper_tables.h:52
 #: engines/sludge/keymapper_tables.h:125 engines/sludge/keymapper_tables.h:187
@@ -4529,7 +4530,7 @@ msgstr ""
 #: backends/platform/sdl/opendingux/opendingux.cpp:57
 #: backends/platform/sdl/ps3/ps3.cpp:50 backends/platform/sdl/psp2/psp2.cpp:51
 #: engines/bagel/metaengine.cpp:64 engines/efh/metaengine.cpp:397
-#: engines/got/metaengine.cpp:76 engines/hypno/metaengine.cpp:363
+#: engines/got/metaengine.cpp:76 engines/hypno/metaengine.cpp:449
 #: engines/mm/mm1/metaengine.cpp:50 engines/mm/mm1/metaengine.cpp:97
 #: engines/sherlock/metaengine.cpp:660 engines/sherlock/metaengine.cpp:672
 #: engines/sherlock/metaengine.cpp:1014 engines/supernova/metaengine.cpp:303
@@ -5661,7 +5662,7 @@ msgstr ""
 #: engines/access/metaengine.cpp:237 engines/asylum/metaengine.cpp:171
 #: engines/asylum/metaengine.cpp:198 engines/cine/metaengine.cpp:377
 #: engines/efh/metaengine.cpp:286 engines/hdb/metaengine.cpp:213
-#: engines/hypno/metaengine.cpp:302 engines/hypno/metaengine.cpp:356
+#: engines/hypno/metaengine.cpp:363 engines/hypno/metaengine.cpp:411
 #: engines/nancy/input.cpp:195 engines/tinsel/metaengine.cpp:263
 #: engines/titanic/metaengine.cpp:324 engines/ultima/ultima8/metaengine.cpp:52
 msgid "Move up"
@@ -5671,7 +5672,7 @@ msgstr ""
 #: engines/access/metaengine.cpp:244 engines/asylum/metaengine.cpp:177
 #: engines/asylum/metaengine.cpp:205 engines/cine/metaengine.cpp:384
 #: engines/efh/metaengine.cpp:293 engines/hdb/metaengine.cpp:219
-#: engines/hypno/metaengine.cpp:290 engines/hypno/metaengine.cpp:342
+#: engines/hypno/metaengine.cpp:351 engines/hypno/metaengine.cpp:399
 #: engines/nancy/input.cpp:201 engines/tinsel/metaengine.cpp:269
 #: engines/titanic/metaengine.cpp:330 engines/ultima/ultima8/metaengine.cpp:53
 msgid "Move down"
@@ -5682,7 +5683,7 @@ msgstr ""
 #: engines/access/metaengine.cpp:251 engines/asylum/metaengine.cpp:183
 #: engines/asylum/metaengine.cpp:212 engines/cine/metaengine.cpp:391
 #: engines/efh/metaengine.cpp:300 engines/hdb/metaengine.cpp:225
-#: engines/hypno/metaengine.cpp:284 engines/hypno/metaengine.cpp:335
+#: engines/hypno/metaengine.cpp:345 engines/hypno/metaengine.cpp:393
 #: engines/kyra/engine/eobcommon.cpp:373 engines/lure/metaengine.cpp:218
 #: engines/nancy/input.cpp:207 engines/tinsel/metaengine.cpp:275
 #: engines/titanic/metaengine.cpp:312 engines/titanic/metaengine.cpp:390
@@ -5695,7 +5696,7 @@ msgstr ""
 #: engines/access/metaengine.cpp:258 engines/asylum/metaengine.cpp:189
 #: engines/asylum/metaengine.cpp:219 engines/cine/metaengine.cpp:398
 #: engines/efh/metaengine.cpp:307 engines/hdb/metaengine.cpp:231
-#: engines/hypno/metaengine.cpp:296 engines/hypno/metaengine.cpp:349
+#: engines/hypno/metaengine.cpp:357 engines/hypno/metaengine.cpp:405
 #: engines/kyra/engine/eobcommon.cpp:374 engines/lure/metaengine.cpp:226
 #: engines/nancy/input.cpp:213 engines/tinsel/metaengine.cpp:281
 #: engines/titanic/metaengine.cpp:318 engines/titanic/metaengine.cpp:396
@@ -6158,8 +6159,8 @@ msgstr ""
 #: engines/cine/metaengine.cpp:271 engines/cruise/metaengine.cpp:169
 #: engines/dm/metaengine.cpp:126 engines/draci/metaengine.cpp:196
 #: engines/drascula/metaengine.cpp:206 engines/hopkins/metaengine.cpp:207
-#: engines/hugo/metaengine.cpp:130 engines/hypno/metaengine.cpp:166
-#: engines/hypno/metaengine.cpp:267 engines/hypno/metaengine.cpp:311
+#: engines/hugo/metaengine.cpp:130 engines/hypno/metaengine.cpp:225
+#: engines/hypno/metaengine.cpp:324 engines/hypno/metaengine.cpp:372
 #: engines/illusions/metaengine.cpp:90 engines/lab/metaengine.cpp:152
 #: engines/lure/metaengine.cpp:169 engines/made/metaengine.cpp:133
 #: engines/mads/metaengine.cpp:317 engines/neverhood/metaengine.cpp:186
@@ -6793,7 +6794,7 @@ msgstr ""
 
 #. I18N: This keymap allows skipping video cutscenes
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
-#: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
+#: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:210
 #: engines/phoenixvr/metaengine.cpp:94 engines/scumm/help.cpp:79
 #: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
@@ -7896,7 +7897,7 @@ msgstr ""
 msgid "Select option 14"
 msgstr ""
 
-#: engines/efh/metaengine.cpp:257 engines/hypno/metaengine.cpp:312
+#: engines/efh/metaengine.cpp:257 engines/hypno/metaengine.cpp:373
 #: engines/sludge/keymapper_tables.h:35 engines/supernova/metaengine.cpp:218
 msgid "Menu keymappings"
 msgstr ""
@@ -9350,88 +9351,144 @@ msgstr ""
 msgid "Intro keymappings"
 msgstr ""
 
-#: engines/hypno/metaengine.cpp:134
+#: engines/hypno/metaengine.cpp:134 engines/hypno/metaengine.cpp:144
 msgid "Primary shoot"
 msgstr ""
 
-#: engines/hypno/metaengine.cpp:140
+#: engines/hypno/metaengine.cpp:139
 msgid "Secondary shoot"
 msgstr ""
 
-#: engines/hypno/metaengine.cpp:146 engines/lab/metaengine.cpp:209
+#: engines/hypno/metaengine.cpp:149
+msgid "Rapid fire / secondary action"
+msgstr ""
+
+#: engines/hypno/metaengine.cpp:155
+msgid "Aim left"
+msgstr ""
+
+#: engines/hypno/metaengine.cpp:160
+msgid "Aim down"
+msgstr ""
+
+#: engines/hypno/metaengine.cpp:165
+msgid "Aim right"
+msgstr ""
+
+#: engines/hypno/metaengine.cpp:170
+msgid "Aim up"
+msgstr ""
+
+#: engines/hypno/metaengine.cpp:175 engines/scumm/metaengine.cpp:1202
+#: engines/scumm/metaengine.cpp:1279
+msgid "Stick left"
+msgstr ""
+
+#: engines/hypno/metaengine.cpp:182 engines/scumm/metaengine.cpp:1195
+#: engines/scumm/metaengine.cpp:1272
+msgid "Stick down"
+msgstr ""
+
+#: engines/hypno/metaengine.cpp:189 engines/scumm/metaengine.cpp:1209
+#: engines/scumm/metaengine.cpp:1286
+msgid "Stick right"
+msgstr ""
+
+#: engines/hypno/metaengine.cpp:196 engines/scumm/metaengine.cpp:1188
+#: engines/scumm/metaengine.cpp:1265
+msgid "Stick up"
+msgstr ""
+
+#: engines/hypno/metaengine.cpp:203 engines/lab/metaengine.cpp:209
 #: engines/ngi/metaengine.cpp:168 engines/sherlock/metaengine.cpp:872
 #: engines/teenagent/metaengine.cpp:232 engines/toon/metaengine.cpp:98
 #: engines/voyeur/metaengine.cpp:235
 msgid "Skip intro"
 msgstr ""
 
-#: engines/hypno/metaengine.cpp:167
+#: engines/hypno/metaengine.cpp:226
 msgid "Exit menu keymappings"
 msgstr ""
 
-#: engines/hypno/metaengine.cpp:168
+#: engines/hypno/metaengine.cpp:227
 msgid "Difficulty selection menu keymappings"
 msgstr ""
 
-#: engines/hypno/metaengine.cpp:169
+#: engines/hypno/metaengine.cpp:228
 msgid "Retry menu keymappings"
 msgstr ""
 
-#: engines/hypno/metaengine.cpp:172 engines/hypno/metaengine.cpp:270
-#: engines/hypno/metaengine.cpp:322
+#: engines/hypno/metaengine.cpp:231 engines/hypno/metaengine.cpp:327
+#: engines/hypno/metaengine.cpp:382
 msgid "Skip level (cheat)"
 msgstr ""
 
 #. I18N: (Game name: Soldier Boyz) player refers to the users own character.
 #. I18N: (Game name: Marvel Comics Spider-Man: The Sinister Six) player refers to the users own character.
 #. I18N: (Game name: Wetlands) player refers to the users own character.
-#: engines/hypno/metaengine.cpp:180 engines/hypno/metaengine.cpp:278
-#: engines/hypno/metaengine.cpp:329
+#: engines/hypno/metaengine.cpp:238 engines/hypno/metaengine.cpp:334
+#: engines/hypno/metaengine.cpp:388
 msgid "Kill player"
 msgstr ""
 
 #. I18N: (Game name: Soldier Boyz) the game has 3 difficulty levels: Chump, Punk and Badass. Chump is the easy mode.
-#: engines/hypno/metaengine.cpp:205
+#: engines/hypno/metaengine.cpp:262
 msgid "Chump"
 msgstr ""
 
 #. I18N: (Game name: Soldier Boyz) the game has 3 difficulty levels: Chump, Punk and Badass. Punk is the medium mode.
-#: engines/hypno/metaengine.cpp:212
+#: engines/hypno/metaengine.cpp:269
 msgid "Punk"
 msgstr ""
 
 #. I18N: (Game name: Soldier Boyz) the game has 3 difficulty levels: Chump, Punk and Badass. Badass is the hard mode.
-#: engines/hypno/metaengine.cpp:219
+#: engines/hypno/metaengine.cpp:276
 msgid "Badass"
 msgstr ""
 
 #. I18N: (Game name: Soldier Boyz) This makes the player restart from the last checkpoint.
-#: engines/hypno/metaengine.cpp:232
+#: engines/hypno/metaengine.cpp:289
 msgid "Retry sector"
 msgstr ""
 
 #. I18N: (Game name: Soldier Boyz) This makes the player restart the current mission / level.
-#: engines/hypno/metaengine.cpp:239
+#: engines/hypno/metaengine.cpp:296
 msgid "Restart territory"
 msgstr ""
 
-#: engines/hypno/metaengine.cpp:245
+#: engines/hypno/metaengine.cpp:302
 msgid "Begin new mission"
 msgstr ""
 
-#: engines/hypno/metaengine.cpp:313
+#: engines/hypno/metaengine.cpp:374
 msgid "Pause keymappings"
 msgstr ""
 
-#: engines/hypno/metaengine.cpp:314
+#: engines/hypno/metaengine.cpp:375
 msgid "Direction keymappings"
 msgstr ""
 
-#: engines/hypno/metaengine.cpp:316 engines/mads/metaengine.cpp:391
+#: engines/hypno/metaengine.cpp:377 engines/mads/metaengine.cpp:391
 #: engines/tsage/metaengine.cpp:259
 msgid "Show credits"
 msgstr ""
 
+#: engines/hypno/metaengine.cpp:417
+msgid "Menu left"
+msgstr ""
+
+#: engines/hypno/metaengine.cpp:425
+msgid "Menu down"
+msgstr ""
+
+#: engines/hypno/metaengine.cpp:433
+msgid "Menu right"
+msgstr ""
+
+#: engines/hypno/metaengine.cpp:441
+msgid "Menu up"
+msgstr ""
+
 #. I18N: Action in In Cold Blood
 #: engines/icb/icb.cpp:107
 msgid "Arm"
@@ -12462,22 +12519,6 @@ msgstr ""
 msgid "Aim right / menu right"
 msgstr ""
 
-#: engines/scumm/metaengine.cpp:1188 engines/scumm/metaengine.cpp:1265
-msgid "Stick up"
-msgstr ""
-
-#: engines/scumm/metaengine.cpp:1195 engines/scumm/metaengine.cpp:1272
-msgid "Stick down"
-msgstr ""
-
-#: engines/scumm/metaengine.cpp:1202 engines/scumm/metaengine.cpp:1279
-msgid "Stick left"
-msgstr ""
-
-#: engines/scumm/metaengine.cpp:1209 engines/scumm/metaengine.cpp:1286
-msgid "Stick right"
-msgstr ""
-
 #: engines/scumm/metaengine.cpp:1216 engines/scumm/metaengine.cpp:1293
 msgid "Fire / select"
 msgstr ""
diff --git a/po/ru_RU.po b/po/ru_RU.po
index 9ee62e2593a..5a7601d8f2b 100644
--- a/po/ru_RU.po
+++ b/po/ru_RU.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.8.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-13 06:14+0000\n"
+"POT-Creation-Date: 2026-06-13 10:20+0000\n"
 "PO-Revision-Date: 2026-05-06 08:54+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Russian <https://translations.scummvm.org/projects/scummvm/"
@@ -133,7 +133,7 @@ msgstr "??????????"
 #: engines/buried/saveload.cpp:83 engines/cine/saveload.cpp:857
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:123
 #: engines/drascula/saveload.cpp:51 engines/grim/inputdialog.cpp:84
-#: engines/hypno/metaengine.cpp:225 engines/mohawk/myst_stacks/menu.cpp:283
+#: engines/hypno/metaengine.cpp:282 engines/mohawk/myst_stacks/menu.cpp:283
 #: engines/mohawk/myst_stacks/menu.cpp:300
 #: engines/mohawk/myst_stacks/menu.cpp:318
 #: engines/mohawk/riven_stacks/aspit.cpp:349
@@ -336,7 +336,7 @@ msgstr "???????????? ???????????????? ???????????? ??????????????????. ???? ????
 #: engines/chamber/metaengine.cpp:91 engines/director/events.cpp:122
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:139
 #: engines/efh/metaengine.cpp:362 engines/grim/grim.cpp:366
-#: engines/hypno/metaengine.cpp:192 engines/kyra/gui/saveload_eob.cpp:611
+#: engines/hypno/metaengine.cpp:249 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:197
 #: engines/mm/mm1/metaengine.cpp:54 engines/parallaction/metaengine.cpp:169
 #: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
@@ -355,7 +355,7 @@ msgstr "????"
 #: engines/chamber/metaengine.cpp:97 engines/director/events.cpp:122
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:139
 #: engines/efh/metaengine.cpp:368 engines/grim/grim.cpp:366
-#: engines/hypno/metaengine.cpp:198 engines/kyra/gui/saveload_eob.cpp:611
+#: engines/hypno/metaengine.cpp:255 engines/kyra/gui/saveload_eob.cpp:611
 #: engines/kyra/gui/saveload_eob.cpp:644 engines/lab/metaengine.cpp:204
 #: engines/mm/mm1/metaengine.cpp:52 engines/parallaction/metaengine.cpp:175
 #: engines/sci/graphics/controls32.cpp:937 engines/scumm/scumm.cpp:4478
@@ -3395,7 +3395,7 @@ msgstr "???????????? ?????????????????????? ??????????????????"
 #: backends/events/default/default-events.cpp:358
 #: engines/adl/metaengine.cpp:352 engines/buried/metaengine.cpp:154
 #: engines/chamber/metaengine.cpp:84 engines/drascula/metaengine.cpp:304
-#: engines/hdb/metaengine.cpp:275 engines/hypno/metaengine.cpp:251
+#: engines/hdb/metaengine.cpp:275 engines/hypno/metaengine.cpp:308
 #: engines/lab/metaengine.cpp:172 engines/mohawk/myst_stacks/menu.cpp:317
 #: engines/mohawk/riven_stacks/aspit.cpp:454
 #: engines/neverhood/metaengine.cpp:200 engines/saga/metaengine.cpp:449
@@ -3964,8 +3964,9 @@ msgstr "???????????? ????????????"
 #: engines/buried/metaengine.cpp:167 engines/crab/input/input.cpp:261
 #: engines/cruise/metaengine.cpp:217 engines/dm/metaengine.cpp:180
 #: engines/grim/grim.cpp:550 engines/hdb/metaengine.cpp:258
-#: engines/hypno/metaengine.cpp:186 engines/hypno/metaengine.cpp:369
-#: engines/icb/icb.cpp:137 engines/icb/icb.cpp:203 engines/mohawk/myst.cpp:595
+#: engines/hypno/metaengine.cpp:243 engines/hypno/metaengine.cpp:339
+#: engines/hypno/metaengine.cpp:456 engines/icb/icb.cpp:137
+#: engines/icb/icb.cpp:203 engines/mohawk/myst.cpp:595
 #: engines/mohawk/riven.cpp:876 engines/ngi/metaengine.cpp:136
 #: engines/sky/metaengine.cpp:126 engines/sludge/keymapper_tables.h:52
 #: engines/sludge/keymapper_tables.h:125 engines/sludge/keymapper_tables.h:187
@@ -4860,7 +4861,7 @@ msgstr "Y"
 #: backends/platform/sdl/opendingux/opendingux.cpp:57
 #: backends/platform/sdl/ps3/ps3.cpp:50 backends/platform/sdl/psp2/psp2.cpp:51
 #: engines/bagel/metaengine.cpp:64 engines/efh/metaengine.cpp:397
-#: engines/got/metaengine.cpp:76 engines/hypno/metaengine.cpp:363
+#: engines/got/metaengine.cpp:76 engines/hypno/metaengine.cpp:449
 #: engines/mm/mm1/metaengine.cpp:50 engines/mm/mm1/metaengine.cpp:97
 #: engines/sherlock/metaengine.cpp:660 engines/sherlock/metaengine.cpp:672
 #: engines/sherlock/metaengine.cpp:1014 engines/supernova/metaengine.cpp:303
@@ -6336,7 +6337,7 @@ msgstr "???????????????????? ??????????"
 #: engines/access/metaengine.cpp:237 engines/asylum/metaengine.cpp:171
 #: engines/asylum/metaengine.cpp:198 engines/cine/metaengine.cpp:377
 #: engines/efh/metaengine.cpp:286 engines/hdb/metaengine.cpp:213
-#: engines/hypno/metaengine.cpp:302 engines/hypno/metaengine.cpp:356
+#: engines/hypno/metaengine.cpp:363 engines/hypno/metaengine.cpp:411
 #: engines/nancy/input.cpp:195 engines/tinsel/metaengine.cpp:263
 #: engines/titanic/metaengine.cpp:324 engines/ultima/ultima8/metaengine.cpp:52
 msgid "Move up"
@@ -6346,7 +6347,7 @@ msgstr "???????? ??????????"
 #: engines/access/metaengine.cpp:244 engines/asylum/metaengine.cpp:177
 #: engines/asylum/metaengine.cpp:205 engines/cine/metaengine.cpp:384
 #: engines/efh/metaengine.cpp:293 engines/hdb/metaengine.cpp:219
-#: engines/hypno/metaengine.cpp:290 engines/hypno/metaengine.cpp:342
+#: engines/hypno/metaengine.cpp:351 engines/hypno/metaengine.cpp:399
 #: engines/nancy/input.cpp:201 engines/tinsel/metaengine.cpp:269
 #: engines/titanic/metaengine.cpp:330 engines/ultima/ultima8/metaengine.cpp:53
 msgid "Move down"
@@ -6357,7 +6358,7 @@ msgstr "???????? ????????"
 #: engines/access/metaengine.cpp:251 engines/asylum/metaengine.cpp:183
 #: engines/asylum/metaengine.cpp:212 engines/cine/metaengine.cpp:391
 #: engines/efh/metaengine.cpp:300 engines/hdb/metaengine.cpp:225
-#: engines/hypno/metaengine.cpp:284 engines/hypno/metaengine.cpp:335
+#: engines/hypno/metaengine.cpp:345 engines/hypno/metaengine.cpp:393
 #: engines/kyra/engine/eobcommon.cpp:373 engines/lure/metaengine.cpp:218
 #: engines/nancy/input.cpp:207 engines/tinsel/metaengine.cpp:275
 #: engines/titanic/metaengine.cpp:312 engines/titanic/metaengine.cpp:390
@@ -6370,7 +6371,7 @@ msgstr "???????? ??????????"
 #: engines/access/metaengine.cpp:258 engines/asylum/metaengine.cpp:189
 #: engines/asylum/metaengine.cpp:219 engines/cine/metaengine.cpp:398
 #: engines/efh/metaengine.cpp:307 engines/hdb/metaengine.cpp:231
-#: engines/hypno/metaengine.cpp:296 engines/hypno/metaengine.cpp:349
+#: engines/hypno/metaengine.cpp:357 engines/hypno/metaengine.cpp:405
 #: engines/kyra/engine/eobcommon.cpp:374 engines/lure/metaengine.cpp:226
 #: engines/nancy/input.cpp:213 engines/tinsel/metaengine.cpp:281
 #: engines/titanic/metaengine.cpp:318 engines/titanic/metaengine.cpp:396
@@ -6862,8 +6863,8 @@ msgstr "?????????????? AGOS"
 #: engines/cine/metaengine.cpp:271 engines/cruise/metaengine.cpp:169
 #: engines/dm/metaengine.cpp:126 engines/draci/metaengine.cpp:196
 #: engines/drascula/metaengine.cpp:206 engines/hopkins/metaengine.cpp:207
-#: engines/hugo/metaengine.cpp:130 engines/hypno/metaengine.cpp:166
-#: engines/hypno/metaengine.cpp:267 engines/hypno/metaengine.cpp:311
+#: engines/hugo/metaengine.cpp:130 engines/hypno/metaengine.cpp:225
+#: engines/hypno/metaengine.cpp:324 engines/hypno/metaengine.cpp:372
 #: engines/illusions/metaengine.cpp:90 engines/lab/metaengine.cpp:152
 #: engines/lure/metaengine.cpp:169 engines/made/metaengine.cpp:133
 #: engines/mads/metaengine.cpp:317 engines/neverhood/metaengine.cpp:186
@@ -7564,7 +7565,7 @@ msgstr "?????????????????????? ??????"
 
 #. I18N: This keymap allows skipping video cutscenes
 #: engines/bladerunner/metaengine.cpp:229 engines/buried/metaengine.cpp:160
-#: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:152
+#: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:210
 #: engines/phoenixvr/metaengine.cpp:94 engines/scumm/help.cpp:79
 #: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
 #: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
@@ -8711,7 +8712,7 @@ msgstr "?????????????? ?????????????? 13"
 msgid "Select option 14"
 msgstr "?????????????? ?????????????? 14"
 
-#: engines/efh/metaengine.cpp:257 engines/hypno/metaengine.cpp:312
+#: engines/efh/metaengine.cpp:257 engines/hypno/metaengine.cpp:373
 #: engines/sludge/keymapper_tables.h:35 engines/supernova/metaengine.cpp:218
 msgid "Menu keymappings"
 msgstr "?????????????????? ???????????? ????????"
@@ -10218,88 +10219,168 @@ msgstr "???????????????? ????????????, ?????????????? ?????? ???????????? ?? ??
 msgid "Intro keymappings"
 msgstr "?????????????????? ???????????? ????????????????????"
 
-#: engines/hypno/metaengine.cpp:134
+#: engines/hypno/metaengine.cpp:134 engines/hypno/metaengine.cpp:144
 msgid "Primary shoot"
 msgstr "???????????????? ??????????????"
 
-#: engines/hypno/metaengine.cpp:140
+#: engines/hypno/metaengine.cpp:139
 msgid "Secondary shoot"
 msgstr "?????????????????? ??????????????"
 



From noreply at scummvm.org  Sat Jun 13 11:38:07 2026
From: noreply at scummvm.org (ScummVM-Translations)
Date: Sat, 13 Jun 2026 11:38:07 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 b3d592578f331758d1a7b11e43b3924b5cfe4047
Message-ID: <20260613113807.5151C7005BC@ron.scummvm.net>

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

Summary:
b3d592578f I18N: Update translation (Spanish)


Commit: b3d592578f331758d1a7b11e43b3924b5cfe4047
    https://github.com/scummvm/scummvm/commit/b3d592578f331758d1a7b11e43b3924b5cfe4047
Author: IlDucci (pajaroloco_2 at hotmail.com)
Date: 2026-06-13T11:38:00Z

Commit Message:
I18N: Update translation (Spanish)

Currently translated at 100.0% (3219 of 3219 strings)

Changed paths:
    po/es_ES.po


diff --git a/po/es_ES.po b/po/es_ES.po
index 3df68e35c2a..0bd39446d63 100644
--- a/po/es_ES.po
+++ b/po/es_ES.po
@@ -8,7 +8,7 @@ msgstr ""
 "Project-Id-Version: ScummVM 1.4.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
 "POT-Creation-Date: 2026-06-13 10:20+0000\n"
-"PO-Revision-Date: 2026-06-10 11:58+0000\n"
+"PO-Revision-Date: 2026-06-13 11:38+0000\n"
 "Last-Translator: IlDucci <pajaroloco_2 at hotmail.com>\n"
 "Language-Team: Spanish <https://translations.scummvm.org/projects/scummvm/"
 "scummvm/es/>\n"
@@ -10334,32 +10334,24 @@ msgid "Secondary shoot"
 msgstr "Disparo secundario"
 
 #: engines/hypno/metaengine.cpp:149
-#, fuzzy
-#| msgid "Map hide toolbar action"
 msgid "Rapid fire / secondary action"
-msgstr "Asignar acci??n 'Ocultar barra de tareas'"
+msgstr "Disparo autom??tico/Acci??n secundaria"
 
 #: engines/hypno/metaengine.cpp:155
-#, fuzzy
-#| msgid "Slide left"
 msgid "Aim left"
-msgstr "Deslizarse a la izquierda"
+msgstr "Apuntar hacia la izquierda"
 
 #: engines/hypno/metaengine.cpp:160
-#, fuzzy
-#| msgid "Stick down"
 msgid "Aim down"
-msgstr "Stick hacia abajo"
+msgstr "Apuntar hacia abajo"
 
 #: engines/hypno/metaengine.cpp:165
-#, fuzzy
-#| msgid "Up right"
 msgid "Aim right"
-msgstr "Subir a la derecha"
+msgstr "Apuntar hacia la derecha"
 
 #: engines/hypno/metaengine.cpp:170
 msgid "Aim up"
-msgstr ""
+msgstr "Apuntar hacia arriba"
 
 #: engines/hypno/metaengine.cpp:175 engines/scumm/metaengine.cpp:1202
 #: engines/scumm/metaengine.cpp:1279
@@ -10456,28 +10448,20 @@ msgid "Show credits"
 msgstr "Mostrar cr??ditos"
 
 #: engines/hypno/metaengine.cpp:417
-#, fuzzy
-#| msgid "Move left"
 msgid "Menu left"
-msgstr "Mover hacia la izquierda"
+msgstr "Men?? hacia la izquierda"
 
 #: engines/hypno/metaengine.cpp:425
-#, fuzzy
-#| msgid "Move down"
 msgid "Menu down"
-msgstr "Mover hacia abajo"
+msgstr "Men?? hacia abajo"
 
 #: engines/hypno/metaengine.cpp:433
-#, fuzzy
-#| msgid "Move right"
 msgid "Menu right"
-msgstr "Mover hacia la derecha"
+msgstr "Men?? hacia la derecha"
 
 #: engines/hypno/metaengine.cpp:441
-#, fuzzy
-#| msgid "Menu"
 msgid "Menu up"
-msgstr "Men??"
+msgstr "Men?? hacia arriba"
 
 #. I18N: Action in In Cold Blood
 #: engines/icb/icb.cpp:107
@@ -12188,11 +12172,12 @@ msgstr "Resalta las zonas interactuables durante las escenas de decisi??n."
 
 #: engines/private/metaengine.cpp:61
 msgid "Increased contrast for small print"
-msgstr ""
+msgstr "Aumentar contraste para letras peque??as"
 
 #: engines/private/metaengine.cpp:62
 msgid "Enhance small printed paper close-ups for improved readability."
 msgstr ""
+"Amplifica los primeros planos de los peri??dicos para mejorar su legibilidad."
 
 #: engines/private/metaengine.cpp:147 engines/trecision/metaengine.cpp:137
 msgid "Skip video"



From noreply at scummvm.org  Sat Jun 13 11:46:46 2026
From: noreply at scummvm.org (ScummVM-Translations)
Date: Sat, 13 Jun 2026 11:46:46 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm-web master ->
 22cae86b6fbec771339cf00501593863883d29ab
Message-ID: <20260613114646.B1F327005BC@ron.scummvm.net>

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

Summary:
22cae86b6f L10N: Update translation (Spanish) (News)


Commit: 22cae86b6fbec771339cf00501593863883d29ab
    https://github.com/scummvm/scummvm-web/commit/22cae86b6fbec771339cf00501593863883d29ab
Author: IlDucci (pajaroloco_2 at hotmail.com)
Date: 2026-06-13T11:46:41Z

Commit Message:
L10N: Update translation (Spanish) (News)

Currently translated at 94.2% (826 of 876 strings)

Changed paths:
    data/es/news.json


diff --git a/data/es/news.json b/data/es/news.json
index e31e50ba..414200ee 100644
--- a/data/es/news.json
+++ b/data/es/news.json
@@ -1746,5 +1746,9 @@
     "20260503": {
         "title": "5 millones de l??neas de c??digo. 5 millones de historias.",
         "content": "Hace un par de semanas alcanzamos sin darnos cuenta un hito importante para el proyecto ScummVM, uno del que nos sentimos tremendamente orgullosos.\n\nSeg??n el an??lisis de nuestro repositorio [que ha hecho Open Hub](https://openhub.net/p/scummvm/), la base de c??digo de ScummVM contiene actualmente m??s de **cinco millones de l??neas de c??digo**, escritas por m??s de **900 colaboradores** a lo largo de casi 25 a??os.\n\nQueridos colaboradores, en nombre de toda la comunidad de ScummVM: miles de personas utilizan vuestro c??digo todos los d??as para revivir los recuerdos de sus infancias o para explorar historias que a??n tienen que descubrir.\n\nVuestra pasi??n y resiliencia ha hecho que esto sea posible. No solo est??is escribiendo la historia: la est??is preservando.\n\n??Gracias!"
+    },
+    "20260612": {
+        "title": "Sombras m??s oscuras de gris: ??Noctropolis est?? listo para ser probado!",
+        "content": "??Os hab??is cansado de regentar una librer??a mundana y ten??is ganas de sumergiros en las crudas p??ginas repletas de neones de un c??mic? Buscad una gabardina, ??porque la aventura *noir* de Flashpoint Productions de 1994, _Noctropolis_ ya est?? lista para ser probada!\n\nEn este c??mic interactivo que cobra vida controlar??is a Peter Grey, un solitario due??o de una librer??a que se ve arrastrado a un mundo dist??pico de superh??roes en el que el sol nunca brilla. Tras el retiro del legendario h??roe Darksheer, tendr??is que ocupar su lugar, resolver los enigmas y salvar a la ciudad de un plantel de villanos a ??rdenes de una fuerza misteriosa.\n\nPara ayudarnos con las pruebas necesitar??is vuestras copias de [los archivos de datos del juego](https://wiki.scummvm.org/index.php?title=Noctropolis#Required_Data) y una [compilaci??n diaria de desarrollo](https://www.scummvm.org/downloads/#daily) reciente de ScummVM.\n\nActualmente son compatibles la versi??n original para DOS (demos incluidas) y el relanzamiento digital de Nightdive Studios, que est?? disponible en [GOG](https://www.gog.com/game/noctropolis) o en [Steam](https://store.steampowered.com/app/377070).\n\nOs rogamos que, durante vuestras patrullas por estas peligrosas calles, inform??is de cualquier fallo que encontr??is en nuestro [gestor de fallos](https://bugs.scummvm.org/).\n\nLa ciudad necesita a un h??roe... ??Estar??is a la altura? ??Pasadlo bien luchando contra el crimen!"
     }
 }



From noreply at scummvm.org  Sat Jun 13 13:45:05 2026
From: noreply at scummvm.org (lephilousophe)
Date: Sat, 13 Jun 2026 13:45:05 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm-web master ->
 47f28f3d51939667e9f2ed4faf3af476ec687aed
Message-ID: <20260613134505.52CE97005BC@ron.scummvm.net>

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

Summary:
47f28f3d51 CI: Update repos before installing from them


Commit: 47f28f3d51939667e9f2ed4faf3af476ec687aed
    https://github.com/scummvm/scummvm-web/commit/47f28f3d51939667e9f2ed4faf3af476ec687aed
Author: Le Philousophe (lephilousophe at users.noreply.github.com)
Date: 2026-06-13T15:29:49+02:00

Commit Message:
CI: Update repos before installing from them

Changed paths:
    .github/workflows/ci.yml


diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index e234b6ac..5c37947a 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -14,6 +14,7 @@ jobs:
     - name: Install glue
       # Patch Glue to let it run on modern OS
       run: |
+        sudo apt update && \
         sudo apt install -y glue-sprite && \
         sudo ln -s glue-sprite /usr/bin/glue && \
         sudo sed -i -e 's/PILImage\.ANTIALIAS/PILImage.LANCZOS/' /usr/lib/python3/dist-packages/glue/formats/img.py && \



From noreply at scummvm.org  Sat Jun 13 13:47:01 2026
From: noreply at scummvm.org (ScummVM git)
Date: Sat, 13 Jun 2026 13:47:01 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm-web dependabot/npm_and_yarn/sass-1.101.0
 -> 9a0e670b2a9921a3a7e326b02fd5b1825781440b
Message-ID: <20260613134701.EE3A57005BC@ron.scummvm.net>

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

Summary:
9a0e670b2a Bump sass from 1.100.0 to 1.101.0


Commit: 9a0e670b2a9921a3a7e326b02fd5b1825781440b
    https://github.com/scummvm/scummvm-web/commit/9a0e670b2a9921a3a7e326b02fd5b1825781440b
Author: dependabot[bot] (49699333+dependabot[bot]@users.noreply.github.com)
Date: 2026-06-13T13:46:59Z

Commit Message:
Bump sass from 1.100.0 to 1.101.0

Bumps [sass](https://github.com/sass/dart-sass) from 1.100.0 to 1.101.0.
- [Release notes](https://github.com/sass/dart-sass/releases)
- [Changelog](https://github.com/sass/dart-sass/blob/main/CHANGELOG.md)
- [Commits](https://github.com/sass/dart-sass/compare/1.100.0...1.101.0)

---
updated-dependencies:
- dependency-name: sass
  dependency-version: 1.101.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support at github.com>

Changed paths:
    package-lock.json
    package.json


diff --git a/package-lock.json b/package-lock.json
index 17dc5924..88003fcd 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -11,7 +11,7 @@
       "dependencies": {
         "autoprefixer": "^10.5.0",
         "postcss": "^8.5.14",
-        "sass": "^1.100.0"
+        "sass": "^1.101.0"
       }
     },
     "node_modules/@parcel/watcher": {
@@ -594,9 +594,9 @@
       }
     },
     "node_modules/sass": {
-      "version": "1.100.0",
-      "resolved": "https://registry.npmjs.org/sass/-/sass-1.100.0.tgz",
-      "integrity": "sha512-B5j0rYMlinhhOo9tjQebMVVn0TfyXAF+wB3b2ggZUuJ/is/Y+7+JGjirAMxHZ9Z3hIP98NPfamlAkBHa1lAaXQ==",
+      "version": "1.101.0",
+      "resolved": "https://registry.npmjs.org/sass/-/sass-1.101.0.tgz",
+      "integrity": "sha512-OL3GoQyoUdDt843DpVmDO6y2k1sc5IhUDSpu8XucEI+35neq5QivZ1iuegnpraEVTJXlQGK1gl27zKcTLEPbQw==",
       "license": "MIT",
       "dependencies": {
         "chokidar": "^5.0.0",
diff --git a/package.json b/package.json
index b28c581b..47cfbf9b 100644
--- a/package.json
+++ b/package.json
@@ -5,7 +5,7 @@
   "dependencies": {
     "autoprefixer": "^10.5.0",
     "postcss": "^8.5.14",
-    "sass": "^1.100.0"
+    "sass": "^1.101.0"
   },
   "scripts": {
     "postinstall": "(cd dumper-companion && npm ci --omit=dev && npm run build) && node build.js",



From noreply at scummvm.org  Sat Jun 13 13:47:12 2026
From: noreply at scummvm.org (ScummVM git)
Date: Sat, 13 Jun 2026 13:47:12 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm-web
 dependabot/composer/patch-releases-cc17fff384 ->
 d8b3066af79b56a05a54518c1146627eac077794
Message-ID: <20260613134712.13B207005BC@ron.scummvm.net>

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

Summary:
d8b3066af7 Bump the patch-releases group across 1 directory with 2 updates


Commit: d8b3066af79b56a05a54518c1146627eac077794
    https://github.com/scummvm/scummvm-web/commit/d8b3066af79b56a05a54518c1146627eac077794
Author: dependabot[bot] (49699333+dependabot[bot]@users.noreply.github.com)
Date: 2026-06-13T13:47:09Z

Commit Message:
Bump the patch-releases group across 1 directory with 2 updates

Bumps the patch-releases group with 2 updates in the / directory: [predis/predis](https://github.com/predis/predis) and [guzzlehttp/guzzle](https://github.com/guzzle/guzzle).


Updates `predis/predis` from 3.5.0 to 3.5.1
- [Release notes](https://github.com/predis/predis/releases)
- [Changelog](https://github.com/predis/predis/blob/main/CHANGELOG.md)
- [Commits](https://github.com/predis/predis/compare/v3.5.0...v3.5.1)

Updates `guzzlehttp/guzzle` from 7.11.0 to 7.11.1
- [Release notes](https://github.com/guzzle/guzzle/releases)
- [Changelog](https://github.com/guzzle/guzzle/blob/7.11/CHANGELOG.md)
- [Commits](https://github.com/guzzle/guzzle/compare/7.11.0...7.11.1)

---
updated-dependencies:
- dependency-name: guzzlehttp/guzzle
  dependency-version: 7.11.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: patch-releases
- dependency-name: predis/predis
  dependency-version: 3.5.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: patch-releases
...

Signed-off-by: dependabot[bot] <support at github.com>

Changed paths:
    composer.lock


diff --git a/composer.lock b/composer.lock
index 3116c27c..5aaa8496 100644
--- a/composer.lock
+++ b/composer.lock
@@ -183,16 +183,16 @@
         },
         {
             "name": "guzzlehttp/guzzle",
-            "version": "7.11.0",
+            "version": "7.11.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/guzzle/guzzle.git",
-                "reference": "c987f8ce84b8434fa430795eca0f3430663da72b"
+                "reference": "5af96f374e0ab4ebd747b8310888c99d3adb0a8c"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/guzzle/guzzle/zipball/c987f8ce84b8434fa430795eca0f3430663da72b",
-                "reference": "c987f8ce84b8434fa430795eca0f3430663da72b",
+                "url": "https://api.github.com/repos/guzzle/guzzle/zipball/5af96f374e0ab4ebd747b8310888c99d3adb0a8c",
+                "reference": "5af96f374e0ab4ebd747b8310888c99d3adb0a8c",
                 "shasum": ""
             },
             "require": {
@@ -211,7 +211,7 @@
                 "bamarni/composer-bin-plugin": "^1.8.2",
                 "ext-curl": "*",
                 "guzzle/client-integration-tests": "3.0.2",
-                "guzzlehttp/test-server": "^0.4",
+                "guzzlehttp/test-server": "^0.5",
                 "php-http/message-factory": "^1.1",
                 "phpunit/phpunit": "^8.5.52 || ^9.6.34",
                 "psr/log": "^1.1 || ^2.0 || ^3.0"
@@ -291,7 +291,7 @@
             ],
             "support": {
                 "issues": "https://github.com/guzzle/guzzle/issues",
-                "source": "https://github.com/guzzle/guzzle/tree/7.11.0"
+                "source": "https://github.com/guzzle/guzzle/tree/7.11.1"
             },
             "funding": [
                 {
@@ -307,7 +307,7 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2026-06-02T12:40:51+00:00"
+            "time": "2026-06-07T22:54:06+00:00"
         },
         {
             "name": "guzzlehttp/promises",
@@ -860,16 +860,16 @@
         },
         {
             "name": "predis/predis",
-            "version": "v3.5.0",
+            "version": "v3.5.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/predis/predis.git",
-                "reference": "8cc4319c06924c8ff0c5c7eec4243a19e3be32f1"
+                "reference": "5c996db191ee2d9bafe651f454b1fca16754271b"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/predis/predis/zipball/8cc4319c06924c8ff0c5c7eec4243a19e3be32f1",
-                "reference": "8cc4319c06924c8ff0c5c7eec4243a19e3be32f1",
+                "url": "https://api.github.com/repos/predis/predis/zipball/5c996db191ee2d9bafe651f454b1fca16754271b",
+                "reference": "5c996db191ee2d9bafe651f454b1fca16754271b",
                 "shasum": ""
             },
             "require": {
@@ -911,7 +911,7 @@
             ],
             "support": {
                 "issues": "https://github.com/predis/predis/issues",
-                "source": "https://github.com/predis/predis/tree/v3.5.0"
+                "source": "https://github.com/predis/predis/tree/v3.5.1"
             },
             "funding": [
                 {
@@ -919,7 +919,7 @@
                     "type": "github"
                 }
             ],
-            "time": "2026-06-02T19:25:56+00:00"
+            "time": "2026-06-11T16:56:53+00:00"
         },
         {
             "name": "propel/propel",
@@ -3131,5 +3131,5 @@
     "platform-overrides": {
         "php": "8.2.0"
     },
-    "plugin-api-version": "2.6.0"
+    "plugin-api-version": "2.9.0"
 }



From noreply at scummvm.org  Sat Jun 13 13:47:50 2026
From: noreply at scummvm.org (ScummVM git)
Date: Sat, 13 Jun 2026 13:47:50 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm-web
 dependabot/npm_and_yarn/dumper-companion/eslint-b8a4b0c404 ->
 8f5088bf9b7463485016a460f84eee42c0ad22b0
Message-ID: <20260613134750.35F747005BC@ron.scummvm.net>

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

Summary:
8f5088bf9b Bump the eslint group across 1 directory with 3 updates


Commit: 8f5088bf9b7463485016a460f84eee42c0ad22b0
    https://github.com/scummvm/scummvm-web/commit/8f5088bf9b7463485016a460f84eee42c0ad22b0
Author: dependabot[bot] (49699333+dependabot[bot]@users.noreply.github.com)
Date: 2026-06-13T13:47:47Z

Commit Message:
Bump the eslint group across 1 directory with 3 updates

Bumps the eslint group with 3 updates in the /dumper-companion directory: [@eslint/js](https://github.com/eslint/eslint/tree/HEAD/packages/js), [eslint](https://github.com/eslint/eslint) and [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint).


Updates `@eslint/js` from 9.39.4 to 10.0.1
- [Release notes](https://github.com/eslint/eslint/releases)
- [Commits](https://github.com/eslint/eslint/commits/v10.0.1/packages/js)

Updates `eslint` from 9.39.4 to 10.5.0
- [Release notes](https://github.com/eslint/eslint/releases)
- [Commits](https://github.com/eslint/eslint/compare/v9.39.4...v10.5.0)

Updates `typescript-eslint` from 8.60.1 to 8.61.0
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/typescript-eslint/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.61.0/packages/typescript-eslint)

---
updated-dependencies:
- dependency-name: "@eslint/js"
  dependency-version: 10.0.1
  dependency-type: direct:development
  update-type: version-update:semver-major
  dependency-group: eslint
- dependency-name: eslint
  dependency-version: 10.4.1
  dependency-type: direct:development
  update-type: version-update:semver-major
  dependency-group: eslint
- dependency-name: typescript-eslint
  dependency-version: 8.61.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: eslint
...

Signed-off-by: dependabot[bot] <support at github.com>

Changed paths:
    dumper-companion/package-lock.json
    dumper-companion/package.json


diff --git a/dumper-companion/package-lock.json b/dumper-companion/package-lock.json
index e7324deb..bc3ba984 100644
--- a/dumper-companion/package-lock.json
+++ b/dumper-companion/package-lock.json
@@ -17,12 +17,12 @@
         "webpack-cli": "^7.0.2"
       },
       "devDependencies": {
-        "@eslint/js": "^9.39.4",
+        "@eslint/js": "^10.0.1",
         "@stylistic/eslint-plugin": "^5.10.0",
-        "eslint": "^9.39.4",
+        "eslint": "^10.5.0",
         "eslint-config-preact": "^2.0.0",
         "globals": "^17.5.0",
-        "typescript-eslint": "^8.58.2"
+        "typescript-eslint": "^8.61.0"
       }
     },
     "node_modules/@babel/code-frame": {
@@ -81,35 +81,6 @@
         "url": "https://opencollective.com/babel"
       }
     },
-    "node_modules/@babel/eslint-parser": {
-      "version": "7.29.7",
-      "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.29.7.tgz",
-      "integrity": "sha512-zxt+UJTOMKvUt3yOg+D58MLuz334pHp93qifMFcjIIO+9hN6t+ufw2gi7vDPMpxvfnHRR+3VVXvIjineCcgyXw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@nicolo-ribaudo/eslint-scope-5-internals": "5.1.1-v1",
-        "eslint-visitor-keys": "^2.1.0",
-        "semver": "^6.3.1"
-      },
-      "engines": {
-        "node": "^10.13.0 || ^12.13.0 || >=14.0.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.11.0",
-        "eslint": "^7.5.0 || ^8.0.0 || ^9.0.0"
-      }
-    },
-    "node_modules/@babel/eslint-parser/node_modules/eslint-visitor-keys": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz",
-      "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==",
-      "dev": true,
-      "license": "Apache-2.0",
-      "engines": {
-        "node": ">=10"
-      }
-    },
     "node_modules/@babel/generator": {
       "version": "7.29.7",
       "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.7.tgz",
@@ -385,118 +356,89 @@
       }
     },
     "node_modules/@eslint/config-array": {
-      "version": "0.21.2",
-      "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.2.tgz",
-      "integrity": "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==",
+      "version": "0.23.5",
+      "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.23.5.tgz",
+      "integrity": "sha512-Y3kKLvC1dvTOT+oGlqNQ1XLqK6D1HU2YXPc52NmAlJZbMMWDzGYXMiPRJ8TYD39muD/OTjlZmNJ4ib7dvSrMBA==",
       "dev": true,
       "license": "Apache-2.0",
       "dependencies": {
-        "@eslint/object-schema": "^2.1.7",
+        "@eslint/object-schema": "^3.0.5",
         "debug": "^4.3.1",
-        "minimatch": "^3.1.5"
+        "minimatch": "^10.2.4"
       },
       "engines": {
-        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+        "node": "^20.19.0 || ^22.13.0 || >=24"
       }
     },
     "node_modules/@eslint/config-helpers": {
-      "version": "0.4.2",
-      "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz",
-      "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==",
+      "version": "0.6.0",
+      "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.6.0.tgz",
+      "integrity": "sha512-ii6Bw9jJ2zi2cWA2Z+9/QZ/+3DX6kwaV5Q986D/CdP3Lap3w/pgQZ373FV7byY/i7L4IRH/G43I5dz1ClsCbpA==",
       "dev": true,
       "license": "Apache-2.0",
       "dependencies": {
-        "@eslint/core": "^0.17.0"
+        "@eslint/core": "^1.2.1"
       },
       "engines": {
-        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+        "node": "^20.19.0 || ^22.13.0 || >=24"
       }
     },
     "node_modules/@eslint/core": {
-      "version": "0.17.0",
-      "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz",
-      "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==",
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/@eslint/core/-/core-1.2.1.tgz",
+      "integrity": "sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ==",
       "dev": true,
       "license": "Apache-2.0",
       "dependencies": {
         "@types/json-schema": "^7.0.15"
       },
       "engines": {
-        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
-      }
-    },
-    "node_modules/@eslint/eslintrc": {
-      "version": "3.3.5",
-      "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.5.tgz",
-      "integrity": "sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "ajv": "^6.14.0",
-        "debug": "^4.3.2",
-        "espree": "^10.0.1",
-        "globals": "^14.0.0",
-        "ignore": "^5.2.0",
-        "import-fresh": "^3.2.1",
-        "js-yaml": "^4.1.1",
-        "minimatch": "^3.1.5",
-        "strip-json-comments": "^3.1.1"
-      },
-      "engines": {
-        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
-      },
-      "funding": {
-        "url": "https://opencollective.com/eslint"
-      }
-    },
-    "node_modules/@eslint/eslintrc/node_modules/globals": {
-      "version": "14.0.0",
-      "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz",
-      "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=18"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
+        "node": "^20.19.0 || ^22.13.0 || >=24"
       }
     },
     "node_modules/@eslint/js": {
-      "version": "9.39.4",
-      "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.4.tgz",
-      "integrity": "sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==",
+      "version": "10.0.1",
+      "resolved": "https://registry.npmjs.org/@eslint/js/-/js-10.0.1.tgz",
+      "integrity": "sha512-zeR9k5pd4gxjZ0abRoIaxdc7I3nDktoXZk2qOv9gCNWx3mVwEn32VRhyLaRsDiJjTs0xq/T8mfPtyuXu7GWBcA==",
       "dev": true,
       "license": "MIT",
       "engines": {
-        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+        "node": "^20.19.0 || ^22.13.0 || >=24"
       },
       "funding": {
         "url": "https://eslint.org/donate"
+      },
+      "peerDependencies": {
+        "eslint": "^10.0.0"
+      },
+      "peerDependenciesMeta": {
+        "eslint": {
+          "optional": true
+        }
       }
     },
     "node_modules/@eslint/object-schema": {
-      "version": "2.1.7",
-      "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz",
-      "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==",
+      "version": "3.0.5",
+      "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-3.0.5.tgz",
+      "integrity": "sha512-vqTaUEgxzm+YDSdElad6PiRoX4t8VGDjCtt05zn4nU810UIx/uNEV7/lZJ6KwFThKZOzOxzXy48da+No7HZaMw==",
       "dev": true,
       "license": "Apache-2.0",
       "engines": {
-        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+        "node": "^20.19.0 || ^22.13.0 || >=24"
       }
     },
     "node_modules/@eslint/plugin-kit": {
-      "version": "0.4.1",
-      "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz",
-      "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==",
+      "version": "0.7.2",
+      "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.7.2.tgz",
+      "integrity": "sha512-+CNAzxglkrpNf/kKywqQfk74QjtceuOE7Qm+AF8miRvPF/wmmK5+OJOgVh3AVTT3RP2mH3+FOaxlE5v72owk0A==",
       "dev": true,
       "license": "Apache-2.0",
       "dependencies": {
-        "@eslint/core": "^0.17.0",
+        "@eslint/core": "^1.2.1",
         "levn": "^0.4.1"
       },
       "engines": {
-        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+        "node": "^20.19.0 || ^22.13.0 || >=24"
       }
     },
     "node_modules/@humanfs/core": {
@@ -683,6 +625,13 @@
         "eslint": "^9.0.0 || ^10.0.0"
       }
     },
+    "node_modules/@types/esrecurse": {
+      "version": "4.3.1",
+      "resolved": "https://registry.npmjs.org/@types/esrecurse/-/esrecurse-4.3.1.tgz",
+      "integrity": "sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==",
+      "dev": true,
+      "license": "MIT"
+    },
     "node_modules/@types/estree": {
       "version": "1.0.9",
       "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz",
@@ -711,17 +660,17 @@
       "license": "MIT"
     },
     "node_modules/@typescript-eslint/eslint-plugin": {
-      "version": "8.60.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.60.1.tgz",
-      "integrity": "sha512-JQ4S5GB0tfjO8BuJ4fcX+HodkzJjYBV+7OJ+wLygaX7OGQ7FudyHL4NSCA6ob+w3Yn+5MkKIozOwQhXeM7opVg==",
+      "version": "8.61.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.61.0.tgz",
+      "integrity": "sha512-bFNvl9ZczlVb+wR2Akszf3gHfKVj/8WanXaGJ3UstTA7brNKg0cNdk6X1Psu5V7MZ2oQtzZKOEzIUehaoxbDGw==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
         "@eslint-community/regexpp": "^4.12.2",
-        "@typescript-eslint/scope-manager": "8.60.1",
-        "@typescript-eslint/type-utils": "8.60.1",
-        "@typescript-eslint/utils": "8.60.1",
-        "@typescript-eslint/visitor-keys": "8.60.1",
+        "@typescript-eslint/scope-manager": "8.61.0",
+        "@typescript-eslint/type-utils": "8.61.0",
+        "@typescript-eslint/utils": "8.61.0",
+        "@typescript-eslint/visitor-keys": "8.61.0",
         "ignore": "^7.0.5",
         "natural-compare": "^1.4.0",
         "ts-api-utils": "^2.5.0"
@@ -734,7 +683,7 @@
         "url": "https://opencollective.com/typescript-eslint"
       },
       "peerDependencies": {
-        "@typescript-eslint/parser": "^8.60.1",
+        "@typescript-eslint/parser": "^8.61.0",
         "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
         "typescript": ">=4.8.4 <6.1.0"
       }
@@ -750,16 +699,16 @@
       }
     },
     "node_modules/@typescript-eslint/parser": {
-      "version": "8.60.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.60.1.tgz",
-      "integrity": "sha512-A0M6ua6H252bVjPvvtSgl2QA4+ET9S5Mtkb2GDyTxIhH/C4qDItT7RQNO5PhMC6NXGYXOR9dIalcDDgBKT7oFA==",
+      "version": "8.61.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.61.0.tgz",
+      "integrity": "sha512-5B7PfA2e1NQGCnDHd/0lW7W3gvp3d59Ryw54FYO8Uswxo9f6ikw3AZV+Xj/TvpImmpsiYyUqAfhC6kJID1jF6w==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@typescript-eslint/scope-manager": "8.60.1",
-        "@typescript-eslint/types": "8.60.1",
-        "@typescript-eslint/typescript-estree": "8.60.1",
-        "@typescript-eslint/visitor-keys": "8.60.1",
+        "@typescript-eslint/scope-manager": "8.61.0",
+        "@typescript-eslint/types": "8.61.0",
+        "@typescript-eslint/typescript-estree": "8.61.0",
+        "@typescript-eslint/visitor-keys": "8.61.0",
         "debug": "^4.4.3"
       },
       "engines": {
@@ -775,14 +724,14 @@
       }
     },
     "node_modules/@typescript-eslint/project-service": {
-      "version": "8.60.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.60.1.tgz",
-      "integrity": "sha512-eXkTH2bxmXlqD1RnOPmLZ9ZM9D3VwSx04JOwBnP9RQ+yUA5a2Mu7SfW8uaV2Aon53NJzZlZYuX7tn91Izf+xaw==",
+      "version": "8.61.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.61.0.tgz",
+      "integrity": "sha512-DV42F7MLJO6Rax7SK1yg43tcnEfGUrurSpSxKuVX+a3RCTzBlH3fuxprrOJXKCJGAaw82xXocikJ0uQaqwXgGA==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@typescript-eslint/tsconfig-utils": "^8.60.1",
-        "@typescript-eslint/types": "^8.60.1",
+        "@typescript-eslint/tsconfig-utils": "^8.61.0",
+        "@typescript-eslint/types": "^8.61.0",
         "debug": "^4.4.3"
       },
       "engines": {
@@ -797,14 +746,14 @@
       }
     },
     "node_modules/@typescript-eslint/scope-manager": {
-      "version": "8.60.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.60.1.tgz",
-      "integrity": "sha512-gvI5OQoptnxQnchOirukCuQ55svJSTuD/4k5+pC267xyBtYry748R9/c3tYUzb/iE6RZfllRz2lVulLCHkTm4w==",
+      "version": "8.61.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.61.0.tgz",
+      "integrity": "sha512-IWdXFHFSb6mlC3HPc7QsLDm5zYEbUla6trDEHf32D3/dnuUyXd87plScSNXSbm0/RxMvObpI17sv/EDTGrGZkA==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@typescript-eslint/types": "8.60.1",
-        "@typescript-eslint/visitor-keys": "8.60.1"
+        "@typescript-eslint/types": "8.61.0",
+        "@typescript-eslint/visitor-keys": "8.61.0"
       },
       "engines": {
         "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -815,9 +764,9 @@
       }
     },
     "node_modules/@typescript-eslint/tsconfig-utils": {
-      "version": "8.60.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.60.1.tgz",
-      "integrity": "sha512-nh8w4qAteiKuZu3pSSzG/yGKpw0OlkrKnzFmbVRenKaD4qc+7i1GrmZaLVkr8rk4uipiPGMOW4YsM6WmKZ5CvA==",
+      "version": "8.61.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.61.0.tgz",
+      "integrity": "sha512-O5Amvdv9ztMpxpf+vmFULGG78IE6Qwdr3bCGvqwG4nwc9H2qXkOYJJnRbRHyMkQTjv1d03olqwwwzHLMqpFePQ==",
       "dev": true,
       "license": "MIT",
       "engines": {
@@ -832,15 +781,15 @@
       }
     },
     "node_modules/@typescript-eslint/type-utils": {
-      "version": "8.60.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.60.1.tgz",
-      "integrity": "sha512-sdwTrpjosW7ANQYJ39ZBF1ZyEMEGVB2UsikrserVM/30a/F1dTLnu9bGxEdosugyu5caigjLrR2qiD11asjI1A==",
+      "version": "8.61.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.61.0.tgz",
+      "integrity": "sha512-TuBiQYIkd97yBfInHCTKVYMbX4kvEmpOEuixIuzCU9p8BGT1SfyyO0d0IfDMbPIHcjn/hWnusUX5e8v5Xg+X8A==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@typescript-eslint/types": "8.60.1",
-        "@typescript-eslint/typescript-estree": "8.60.1",
-        "@typescript-eslint/utils": "8.60.1",
+        "@typescript-eslint/types": "8.61.0",
+        "@typescript-eslint/typescript-estree": "8.61.0",
+        "@typescript-eslint/utils": "8.61.0",
         "debug": "^4.4.3",
         "ts-api-utils": "^2.5.0"
       },
@@ -857,9 +806,9 @@
       }
     },
     "node_modules/@typescript-eslint/types": {
-      "version": "8.60.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.60.1.tgz",
-      "integrity": "sha512-4h0tY8ppCkdCzcrl2YM5M3my0xsE1Tf8om3owEu5oPWmXwkKRmk0j0LGDzYBGUcAlesEbxBhazqu/K4cu3Ug7w==",
+      "version": "8.61.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.61.0.tgz",
+      "integrity": "sha512-9QTQpZ5Iin4CdIodfbDQFSeiSJKidgYJYug1P9CC2xWgUTvlmixViqDZNciMjwLBZyJnG4tGmPl97rVAFb1AJg==",
       "dev": true,
       "license": "MIT",
       "engines": {
@@ -871,16 +820,16 @@
       }
     },
     "node_modules/@typescript-eslint/typescript-estree": {
-      "version": "8.60.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.60.1.tgz",
-      "integrity": "sha512-alpRkfG8hlVE5kdJW2GkfgDgXxold3e8e4l6EnmhRmRLbekgAPCCGDVD++sABy9FcgPFroq+uFcCSM1vR57Cew==",
+      "version": "8.61.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.61.0.tgz",
+      "integrity": "sha512-42zatd5qSvvcV1JdDBCLxYRznvP4eIHpPoZXdkPFnAmanA4FuZ5dibSnCBggY8hQnqajPpoGjXFdZ7fIJKQnlA==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@typescript-eslint/project-service": "8.60.1",
-        "@typescript-eslint/tsconfig-utils": "8.60.1",
-        "@typescript-eslint/types": "8.60.1",
-        "@typescript-eslint/visitor-keys": "8.60.1",
+        "@typescript-eslint/project-service": "8.61.0",
+        "@typescript-eslint/tsconfig-utils": "8.61.0",
+        "@typescript-eslint/types": "8.61.0",
+        "@typescript-eslint/visitor-keys": "8.61.0",
         "debug": "^4.4.3",
         "minimatch": "^10.2.2",
         "semver": "^7.7.3",
@@ -898,49 +847,10 @@
         "typescript": ">=4.8.4 <6.1.0"
       }
     },
-    "node_modules/@typescript-eslint/typescript-estree/node_modules/balanced-match": {
-      "version": "4.0.4",
-      "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
-      "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": "18 || 20 || >=22"
-      }
-    },
-    "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": {
-      "version": "5.0.6",
-      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz",
-      "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "balanced-match": "^4.0.2"
-      },
-      "engines": {
-        "node": "18 || 20 || >=22"
-      }
-    },
-    "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": {
-      "version": "10.2.5",
-      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz",
-      "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==",
-      "dev": true,
-      "license": "BlueOak-1.0.0",
-      "dependencies": {
-        "brace-expansion": "^5.0.5"
-      },
-      "engines": {
-        "node": "18 || 20 || >=22"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/isaacs"
-      }
-    },
     "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": {
-      "version": "7.8.2",
-      "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.2.tgz",
-      "integrity": "sha512-c8jsqUZm3omBOI66G90z1Dyw5z622G8oLG+omfsHBJf3CWQTlOcwOjvOG6wtiNfW6anKm/eA39LMwMtMez2TiQ==",
+      "version": "7.8.4",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.4.tgz",
+      "integrity": "sha512-rUCObTnP32Q08R2uuIrt7r9PlEonuTmtuXYcW6s5kjdlj3xbnwe+21yXptAUYcMAABLkYYTtnmzb3w3EDZfueA==",
       "dev": true,
       "license": "ISC",
       "bin": {
@@ -951,16 +861,16 @@
       }
     },
     "node_modules/@typescript-eslint/utils": {
-      "version": "8.60.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.60.1.tgz",
-      "integrity": "sha512-h2MPBLoNtjc3qZWfY3Tl51yPorQ2McHn8pJfcMNTcIvrrZrr90Ykffit0yjrPFWQcRcUxzH20+6OcVdW4yHtUg==",
+      "version": "8.61.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.61.0.tgz",
+      "integrity": "sha512-3bzFt7ImFMW/jVYwJamDoe/dMOdFLSC6pom6rRjdh4SZJEYupyMzem8e7vKZLclLfpHjlwSAXOUxtKxGXUiLqA==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
         "@eslint-community/eslint-utils": "^4.9.1",
-        "@typescript-eslint/scope-manager": "8.60.1",
-        "@typescript-eslint/types": "8.60.1",
-        "@typescript-eslint/typescript-estree": "8.60.1"
+        "@typescript-eslint/scope-manager": "8.61.0",
+        "@typescript-eslint/types": "8.61.0",
+        "@typescript-eslint/typescript-estree": "8.61.0"
       },
       "engines": {
         "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -975,13 +885,13 @@
       }
     },
     "node_modules/@typescript-eslint/visitor-keys": {
-      "version": "8.60.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.60.1.tgz",
-      "integrity": "sha512-EbGRQg4FhrmwLodl+t3JNAnXHWVr9Vp+Zl1QBZVPY4ByfkzIT8cX3K6QWODHtkIZqqJVEWvhHSx3v5PDHsaQag==",
+      "version": "8.61.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.61.0.tgz",
+      "integrity": "sha512-QVLZu3ZPQEE+HICQyAMZ2yLQhxf0meY/wx6Hx14YcTNj13JB3qHlX3lJ02L3fLGHgERRH71kvYDwiXIguT3AjQ==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@typescript-eslint/types": "8.60.1",
+        "@typescript-eslint/types": "8.61.0",
         "eslint-visitor-keys": "^5.0.0"
       },
       "engines": {
@@ -1279,13 +1189,6 @@
         "url": "https://github.com/chalk/ansi-styles?sponsor=1"
       }
     },
-    "node_modules/argparse": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
-      "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
-      "dev": true,
-      "license": "Python-2.0"
-    },
     "node_modules/array-buffer-byte-length": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz",
@@ -1468,11 +1371,14 @@
       }
     },
     "node_modules/balanced-match": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
-      "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+      "version": "4.0.4",
+      "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
+      "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==",
       "dev": true,
-      "license": "MIT"
+      "license": "MIT",
+      "engines": {
+        "node": "18 || 20 || >=22"
+      }
     },
     "node_modules/baseline-browser-mapping": {
       "version": "2.10.34",
@@ -1487,14 +1393,16 @@
       }
     },
     "node_modules/brace-expansion": {
-      "version": "1.1.15",
-      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz",
-      "integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==",
+      "version": "5.0.6",
+      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz",
+      "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "balanced-match": "^1.0.0",
-        "concat-map": "0.0.1"
+        "balanced-match": "^4.0.2"
+      },
+      "engines": {
+        "node": "18 || 20 || >=22"
       }
     },
     "node_modules/braces": {
@@ -1598,16 +1506,6 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
-    "node_modules/callsites": {
-      "version": "3.1.0",
-      "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
-      "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=6"
-      }
-    },
     "node_modules/caniuse-lite": {
       "version": "1.0.30001797",
       "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001797.tgz",
@@ -1982,9 +1880,9 @@
       }
     },
     "node_modules/es-iterator-helpers": {
-      "version": "1.3.2",
-      "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.3.2.tgz",
-      "integrity": "sha512-HVLACW1TppGYjJ8H6/jqH/pqOtKRw6wMlrB23xfExmFWxFquAIWCmwoLsOyN96K4a5KbmOf5At9ZUO3GZbetAw==",
+      "version": "1.3.3",
+      "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.3.3.tgz",
+      "integrity": "sha512-0PuBxFi+4uPanB97iDxCLWuHeYud2FALrw5HFZGtAF38UpJDbDC8frwp2cnDyae692CQ0dou60UwWfhgsa4U/g==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
@@ -2098,33 +1996,33 @@
       }
     },
     "node_modules/eslint": {
-      "version": "9.39.4",
-      "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.4.tgz",
-      "integrity": "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==",
+      "version": "10.5.0",
+      "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.5.0.tgz",
+      "integrity": "sha512-1y+7C+vi12bUK1IpZeaV3gsH9fHLBmPvYmPx42pvT/E9yG0IC8g3PUZZgp0+JLJl7ZDK0flc2gc+Aw9dpCvIsQ==",
       "dev": true,
       "license": "MIT",
+      "workspaces": [
+        "packages/*"
+      ],
       "dependencies": {
         "@eslint-community/eslint-utils": "^4.8.0",
-        "@eslint-community/regexpp": "^4.12.1",
-        "@eslint/config-array": "^0.21.2",
-        "@eslint/config-helpers": "^0.4.2",
-        "@eslint/core": "^0.17.0",
-        "@eslint/eslintrc": "^3.3.5",
-        "@eslint/js": "9.39.4",
-        "@eslint/plugin-kit": "^0.4.1",
+        "@eslint-community/regexpp": "^4.12.2",
+        "@eslint/config-array": "^0.23.5",
+        "@eslint/config-helpers": "^0.6.0",
+        "@eslint/core": "^1.2.1",
+        "@eslint/plugin-kit": "^0.7.2",
         "@humanfs/node": "^0.16.6",
         "@humanwhocodes/module-importer": "^1.0.1",
         "@humanwhocodes/retry": "^0.4.2",
         "@types/estree": "^1.0.6",
         "ajv": "^6.14.0",
-        "chalk": "^4.0.0",
         "cross-spawn": "^7.0.6",
         "debug": "^4.3.2",
         "escape-string-regexp": "^4.0.0",
-        "eslint-scope": "^8.4.0",
-        "eslint-visitor-keys": "^4.2.1",
-        "espree": "^10.4.0",
-        "esquery": "^1.5.0",
+        "eslint-scope": "^9.1.2",
+        "eslint-visitor-keys": "^5.0.1",
+        "espree": "^11.2.0",
+        "esquery": "^1.7.0",
         "esutils": "^2.0.2",
         "fast-deep-equal": "^3.1.3",
         "file-entry-cache": "^8.0.0",
@@ -2134,8 +2032,7 @@
         "imurmurhash": "^0.1.4",
         "is-glob": "^4.0.0",
         "json-stable-stringify-without-jsonify": "^1.0.1",
-        "lodash.merge": "^4.6.2",
-        "minimatch": "^3.1.5",
+        "minimatch": "^10.2.4",
         "natural-compare": "^1.4.0",
         "optionator": "^0.9.3"
       },
@@ -2143,7 +2040,7 @@
         "eslint": "bin/eslint.js"
       },
       "engines": {
-        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+        "node": "^20.19.0 || ^22.13.0 || >=24"
       },
       "funding": {
         "url": "https://eslint.org/donate"
@@ -2178,68 +2075,57 @@
         "eslint": "^8.57.1 || ^9.0.0"
       }
     },
-    "node_modules/eslint-config-preact/node_modules/globals": {
-      "version": "16.5.0",
-      "resolved": "https://registry.npmjs.org/globals/-/globals-16.5.0.tgz",
-      "integrity": "sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=18"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/eslint-plugin-compat": {
-      "version": "6.2.1",
-      "resolved": "https://registry.npmjs.org/eslint-plugin-compat/-/eslint-plugin-compat-6.2.1.tgz",
-      "integrity": "sha512-gLKqUH+lQcCL+HzsROUjBDvakc5Zaga51Y4ZAkPCXc41pzKBfyluqTr2j8zOx8QQQb7zyglu1LVoL5aSNWf2SQ==",
+    "node_modules/eslint-config-preact/node_modules/@babel/eslint-parser": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.29.7.tgz",
+      "integrity": "sha512-zxt+UJTOMKvUt3yOg+D58MLuz334pHp93qifMFcjIIO+9hN6t+ufw2gi7vDPMpxvfnHRR+3VVXvIjineCcgyXw==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@mdn/browser-compat-data": "^6.1.1",
-        "ast-metadata-inferer": "^0.8.1",
-        "browserslist": "^4.25.2",
-        "find-up": "^5.0.0",
-        "globals": "^15.7.0",
-        "lodash.memoize": "^4.1.2",
-        "semver": "^7.6.2"
+        "@nicolo-ribaudo/eslint-scope-5-internals": "5.1.1-v1",
+        "eslint-visitor-keys": "^2.1.0",
+        "semver": "^6.3.1"
       },
       "engines": {
-        "node": ">=18.x"
+        "node": "^10.13.0 || ^12.13.0 || >=14.0.0"
       },
       "peerDependencies": {
-        "eslint": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0"
+        "@babel/core": "^7.11.0",
+        "eslint": "^7.5.0 || ^8.0.0 || ^9.0.0"
       }
     },
-    "node_modules/eslint-plugin-compat/node_modules/globals": {
-      "version": "15.15.0",
-      "resolved": "https://registry.npmjs.org/globals/-/globals-15.15.0.tgz",
-      "integrity": "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==",
+    "node_modules/eslint-config-preact/node_modules/@eslint/js": {
+      "version": "9.39.4",
+      "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.4.tgz",
+      "integrity": "sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==",
       "dev": true,
       "license": "MIT",
       "engines": {
-        "node": ">=18"
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
       },
       "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
+        "url": "https://eslint.org/donate"
       }
     },
-    "node_modules/eslint-plugin-compat/node_modules/semver": {
-      "version": "7.8.2",
-      "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.2.tgz",
-      "integrity": "sha512-c8jsqUZm3omBOI66G90z1Dyw5z622G8oLG+omfsHBJf3CWQTlOcwOjvOG6wtiNfW6anKm/eA39LMwMtMez2TiQ==",
+    "node_modules/eslint-config-preact/node_modules/balanced-match": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+      "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
       "dev": true,
-      "license": "ISC",
-      "bin": {
-        "semver": "bin/semver.js"
-      },
-      "engines": {
-        "node": ">=10"
+      "license": "MIT"
+    },
+    "node_modules/eslint-config-preact/node_modules/brace-expansion": {
+      "version": "1.1.15",
+      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz",
+      "integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "balanced-match": "^1.0.0",
+        "concat-map": "0.0.1"
       }
     },
-    "node_modules/eslint-plugin-react": {
+    "node_modules/eslint-config-preact/node_modules/eslint-plugin-react": {
       "version": "7.37.5",
       "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.5.tgz",
       "integrity": "sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==",
@@ -2272,7 +2158,7 @@
         "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7"
       }
     },
-    "node_modules/eslint-plugin-react-hooks": {
+    "node_modules/eslint-config-preact/node_modules/eslint-plugin-react-hooks": {
       "version": "5.2.0",
       "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.2.0.tgz",
       "integrity": "sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==",
@@ -2285,18 +2171,104 @@
         "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0"
       }
     },
+    "node_modules/eslint-config-preact/node_modules/eslint-visitor-keys": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz",
+      "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/eslint-config-preact/node_modules/globals": {
+      "version": "16.5.0",
+      "resolved": "https://registry.npmjs.org/globals/-/globals-16.5.0.tgz",
+      "integrity": "sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/eslint-config-preact/node_modules/minimatch": {
+      "version": "3.1.5",
+      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
+      "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "brace-expansion": "^1.1.7"
+      },
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/eslint-plugin-compat": {
+      "version": "6.2.1",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-compat/-/eslint-plugin-compat-6.2.1.tgz",
+      "integrity": "sha512-gLKqUH+lQcCL+HzsROUjBDvakc5Zaga51Y4ZAkPCXc41pzKBfyluqTr2j8zOx8QQQb7zyglu1LVoL5aSNWf2SQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@mdn/browser-compat-data": "^6.1.1",
+        "ast-metadata-inferer": "^0.8.1",
+        "browserslist": "^4.25.2",
+        "find-up": "^5.0.0",
+        "globals": "^15.7.0",
+        "lodash.memoize": "^4.1.2",
+        "semver": "^7.6.2"
+      },
+      "engines": {
+        "node": ">=18.x"
+      },
+      "peerDependencies": {
+        "eslint": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0"
+      }
+    },
+    "node_modules/eslint-plugin-compat/node_modules/globals": {
+      "version": "15.15.0",
+      "resolved": "https://registry.npmjs.org/globals/-/globals-15.15.0.tgz",
+      "integrity": "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/eslint-plugin-compat/node_modules/semver": {
+      "version": "7.8.2",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.2.tgz",
+      "integrity": "sha512-c8jsqUZm3omBOI66G90z1Dyw5z622G8oLG+omfsHBJf3CWQTlOcwOjvOG6wtiNfW6anKm/eA39LMwMtMez2TiQ==",
+      "dev": true,
+      "license": "ISC",
+      "bin": {
+        "semver": "bin/semver.js"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
     "node_modules/eslint-scope": {
-      "version": "8.4.0",
-      "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz",
-      "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==",
+      "version": "9.1.2",
+      "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-9.1.2.tgz",
+      "integrity": "sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==",
       "dev": true,
       "license": "BSD-2-Clause",
       "dependencies": {
+        "@types/esrecurse": "^4.3.1",
+        "@types/estree": "^1.0.8",
         "esrecurse": "^4.3.0",
         "estraverse": "^5.2.0"
       },
       "engines": {
-        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+        "node": "^20.19.0 || ^22.13.0 || >=24"
       },
       "funding": {
         "url": "https://opencollective.com/eslint"
@@ -2315,6 +2287,37 @@
         "url": "https://opencollective.com/eslint"
       }
     },
+    "node_modules/eslint/node_modules/eslint-visitor-keys": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz",
+      "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "engines": {
+        "node": "^20.19.0 || ^22.13.0 || >=24"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint"
+      }
+    },
+    "node_modules/eslint/node_modules/espree": {
+      "version": "11.2.0",
+      "resolved": "https://registry.npmjs.org/espree/-/espree-11.2.0.tgz",
+      "integrity": "sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "dependencies": {
+        "acorn": "^8.16.0",
+        "acorn-jsx": "^5.3.2",
+        "eslint-visitor-keys": "^5.0.1"
+      },
+      "engines": {
+        "node": "^20.19.0 || ^22.13.0 || >=24"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint"
+      }
+    },
     "node_modules/espree": {
       "version": "10.4.0",
       "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz",
@@ -2538,18 +2541,21 @@
       }
     },
     "node_modules/function.prototype.name": {
-      "version": "1.1.8",
-      "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz",
-      "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==",
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.2.0.tgz",
+      "integrity": "sha512-jObKIik1P2QjPHP5nz5BaOtUlfgS0fWo8IUByNXkM+o+02sJOi94em77GwJKQSJ3gfPHdgzLNrHc1uokV4P/ew==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "call-bind": "^1.0.8",
-        "call-bound": "^1.0.3",
-        "define-properties": "^1.2.1",
+        "call-bind": "^1.0.9",
+        "call-bound": "^1.0.4",
+        "es-define-property": "^1.0.1",
+        "es-errors": "^1.3.0",
         "functions-have-names": "^1.2.3",
-        "hasown": "^2.0.2",
-        "is-callable": "^1.2.7"
+        "has-property-descriptors": "^1.0.2",
+        "hasown": "^2.0.4",
+        "is-callable": "^1.2.7",
+        "is-document.all": "^1.0.0"
       },
       "engines": {
         "node": ">= 0.4"
@@ -2815,23 +2821,6 @@
         "node": ">= 4"
       }
     },
-    "node_modules/import-fresh": {
-      "version": "3.3.1",
-      "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
-      "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "parent-module": "^1.0.0",
-        "resolve-from": "^4.0.0"
-      },
-      "engines": {
-        "node": ">=6"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
     "node_modules/import-local": {
       "version": "3.2.0",
       "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz",
@@ -3019,6 +3008,22 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
+    "node_modules/is-document.all": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/is-document.all/-/is-document.all-1.0.0.tgz",
+      "integrity": "sha512-+XSoyS05OdBbhFuELhgTCpFNHkpBOJqtsZfUFFpe5QTw+9Sjbh8zitxhQkYAo6wV7e1Vb8cAPvpCk9jGam/82g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "call-bound": "^1.0.4"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
     "node_modules/is-extglob": {
       "version": "2.1.1",
       "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
@@ -3363,29 +3368,6 @@
       "dev": true,
       "license": "MIT"
     },
-    "node_modules/js-yaml": {
-      "version": "4.2.0",
-      "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.2.0.tgz",
-      "integrity": "sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==",
-      "dev": true,
-      "funding": [
-        {
-          "type": "github",
-          "url": "https://github.com/sponsors/puzrin"
-        },
-        {
-          "type": "github",
-          "url": "https://github.com/sponsors/nodeca"
-        }
-      ],
-      "license": "MIT",
-      "dependencies": {
-        "argparse": "^2.0.1"
-      },
-      "bin": {
-        "js-yaml": "bin/js-yaml.js"
-      }
-    },
     "node_modules/jsesc": {
       "version": "3.1.0",
       "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",
@@ -3518,13 +3500,6 @@
       "dev": true,
       "license": "MIT"
     },
-    "node_modules/lodash.merge": {
-      "version": "4.6.2",
-      "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
-      "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
-      "dev": true,
-      "license": "MIT"
-    },
     "node_modules/loose-envify": {
       "version": "1.4.0",
       "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
@@ -3599,16 +3574,19 @@
       }
     },
     "node_modules/minimatch": {
-      "version": "3.1.5",
-      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
-      "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
+      "version": "10.2.5",
+      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz",
+      "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==",
       "dev": true,
-      "license": "ISC",
+      "license": "BlueOak-1.0.0",
       "dependencies": {
-        "brace-expansion": "^1.1.7"
+        "brace-expansion": "^5.0.5"
       },
       "engines": {
-        "node": "*"
+        "node": "18 || 20 || >=22"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
       }
     },
     "node_modules/ms": {
@@ -3844,19 +3822,6 @@
         "node": ">=6"
       }
     },
-    "node_modules/parent-module": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
-      "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "callsites": "^3.0.0"
-      },
-      "engines": {
-        "node": ">=6"
-      }
-    },
     "node_modules/path-exists": {
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
@@ -4153,16 +4118,6 @@
         "node": ">=8"
       }
     },
-    "node_modules/resolve-from": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
-      "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=4"
-      }
-    },
     "node_modules/safe-array-concat": {
       "version": "1.1.4",
       "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.4.tgz",
@@ -4364,15 +4319,15 @@
       }
     },
     "node_modules/side-channel": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
-      "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==",
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz",
+      "integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
         "es-errors": "^1.3.0",
-        "object-inspect": "^1.13.3",
-        "side-channel-list": "^1.0.0",
+        "object-inspect": "^1.13.4",
+        "side-channel-list": "^1.0.1",
         "side-channel-map": "^1.0.1",
         "side-channel-weakmap": "^1.0.2"
       },
@@ -4580,19 +4535,6 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
-    "node_modules/strip-json-comments": {
-      "version": "3.1.1",
-      "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
-      "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=8"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
     "node_modules/supports-color": {
       "version": "7.2.0",
       "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
@@ -4894,16 +4836,16 @@
       }
     },
     "node_modules/typescript-eslint": {
-      "version": "8.60.1",
-      "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.60.1.tgz",
-      "integrity": "sha512-6m5hkkRAp8lKvhVpcprAIn5KkehQEh+47oHH2VGnExEh7dhNxXlg6GPAOIu6TxbVQxhebrJDvjl3020ooiWCMA==",
+      "version": "8.61.0",
+      "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.61.0.tgz",
+      "integrity": "sha512-8y31Rd0eGTrDKqhy6vT0HtzhN+YLjQizwX3aA3hPXP/ynSfnrBXcQY5IzsP9/DM7+klX4IUncZZjkchP0z+rUw==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@typescript-eslint/eslint-plugin": "8.60.1",
-        "@typescript-eslint/parser": "8.60.1",
-        "@typescript-eslint/typescript-estree": "8.60.1",
-        "@typescript-eslint/utils": "8.60.1"
+        "@typescript-eslint/eslint-plugin": "8.61.0",
+        "@typescript-eslint/parser": "8.61.0",
+        "@typescript-eslint/typescript-estree": "8.61.0",
+        "@typescript-eslint/utils": "8.61.0"
       },
       "engines": {
         "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
diff --git a/dumper-companion/package.json b/dumper-companion/package.json
index 90e97964..d3bee6a3 100644
--- a/dumper-companion/package.json
+++ b/dumper-companion/package.json
@@ -16,11 +16,11 @@
     "webpack-cli": "^7.0.2"
   },
   "devDependencies": {
-    "@eslint/js": "^9.39.4",
+    "@eslint/js": "^10.0.1",
     "@stylistic/eslint-plugin": "^5.10.0",
-    "eslint": "^9.39.4",
+    "eslint": "^10.5.0",
     "eslint-config-preact": "^2.0.0",
     "globals": "^17.5.0",
-    "typescript-eslint": "^8.58.2"
+    "typescript-eslint": "^8.61.0"
   }
 }



From noreply at scummvm.org  Sat Jun 13 13:48:56 2026
From: noreply at scummvm.org (lephilousophe)
Date: Sat, 13 Jun 2026 13:48:56 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm-web master ->
 4446b63486f314388eb4cd3eb6a3ed8011610542
Message-ID: <20260613134856.BEBDE7005BC@ron.scummvm.net>

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

Summary:
4446b63486 Bump the patch-releases group across 1 directory with 2 updates


Commit: 4446b63486f314388eb4cd3eb6a3ed8011610542
    https://github.com/scummvm/scummvm-web/commit/4446b63486f314388eb4cd3eb6a3ed8011610542
Author: dependabot[bot] (49699333+dependabot[bot]@users.noreply.github.com)
Date: 2026-06-13T15:48:53+02:00

Commit Message:
Bump the patch-releases group across 1 directory with 2 updates

Bumps the patch-releases group with 2 updates in the / directory: [predis/predis](https://github.com/predis/predis) and [guzzlehttp/guzzle](https://github.com/guzzle/guzzle).


Updates `predis/predis` from 3.5.0 to 3.5.1
- [Release notes](https://github.com/predis/predis/releases)
- [Changelog](https://github.com/predis/predis/blob/main/CHANGELOG.md)
- [Commits](https://github.com/predis/predis/compare/v3.5.0...v3.5.1)

Updates `guzzlehttp/guzzle` from 7.11.0 to 7.11.1
- [Release notes](https://github.com/guzzle/guzzle/releases)
- [Changelog](https://github.com/guzzle/guzzle/blob/7.11/CHANGELOG.md)
- [Commits](https://github.com/guzzle/guzzle/compare/7.11.0...7.11.1)

---
updated-dependencies:
- dependency-name: guzzlehttp/guzzle
  dependency-version: 7.11.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: patch-releases
- dependency-name: predis/predis
  dependency-version: 3.5.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: patch-releases
...

Signed-off-by: dependabot[bot] <support at github.com>

Changed paths:
    composer.lock


diff --git a/composer.lock b/composer.lock
index 3116c27c..5aaa8496 100644
--- a/composer.lock
+++ b/composer.lock
@@ -183,16 +183,16 @@
         },
         {
             "name": "guzzlehttp/guzzle",
-            "version": "7.11.0",
+            "version": "7.11.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/guzzle/guzzle.git",
-                "reference": "c987f8ce84b8434fa430795eca0f3430663da72b"
+                "reference": "5af96f374e0ab4ebd747b8310888c99d3adb0a8c"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/guzzle/guzzle/zipball/c987f8ce84b8434fa430795eca0f3430663da72b",
-                "reference": "c987f8ce84b8434fa430795eca0f3430663da72b",
+                "url": "https://api.github.com/repos/guzzle/guzzle/zipball/5af96f374e0ab4ebd747b8310888c99d3adb0a8c",
+                "reference": "5af96f374e0ab4ebd747b8310888c99d3adb0a8c",
                 "shasum": ""
             },
             "require": {
@@ -211,7 +211,7 @@
                 "bamarni/composer-bin-plugin": "^1.8.2",
                 "ext-curl": "*",
                 "guzzle/client-integration-tests": "3.0.2",
-                "guzzlehttp/test-server": "^0.4",
+                "guzzlehttp/test-server": "^0.5",
                 "php-http/message-factory": "^1.1",
                 "phpunit/phpunit": "^8.5.52 || ^9.6.34",
                 "psr/log": "^1.1 || ^2.0 || ^3.0"
@@ -291,7 +291,7 @@
             ],
             "support": {
                 "issues": "https://github.com/guzzle/guzzle/issues",
-                "source": "https://github.com/guzzle/guzzle/tree/7.11.0"
+                "source": "https://github.com/guzzle/guzzle/tree/7.11.1"
             },
             "funding": [
                 {
@@ -307,7 +307,7 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2026-06-02T12:40:51+00:00"
+            "time": "2026-06-07T22:54:06+00:00"
         },
         {
             "name": "guzzlehttp/promises",
@@ -860,16 +860,16 @@
         },
         {
             "name": "predis/predis",
-            "version": "v3.5.0",
+            "version": "v3.5.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/predis/predis.git",
-                "reference": "8cc4319c06924c8ff0c5c7eec4243a19e3be32f1"
+                "reference": "5c996db191ee2d9bafe651f454b1fca16754271b"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/predis/predis/zipball/8cc4319c06924c8ff0c5c7eec4243a19e3be32f1",
-                "reference": "8cc4319c06924c8ff0c5c7eec4243a19e3be32f1",
+                "url": "https://api.github.com/repos/predis/predis/zipball/5c996db191ee2d9bafe651f454b1fca16754271b",
+                "reference": "5c996db191ee2d9bafe651f454b1fca16754271b",
                 "shasum": ""
             },
             "require": {
@@ -911,7 +911,7 @@
             ],
             "support": {
                 "issues": "https://github.com/predis/predis/issues",
-                "source": "https://github.com/predis/predis/tree/v3.5.0"
+                "source": "https://github.com/predis/predis/tree/v3.5.1"
             },
             "funding": [
                 {
@@ -919,7 +919,7 @@
                     "type": "github"
                 }
             ],
-            "time": "2026-06-02T19:25:56+00:00"
+            "time": "2026-06-11T16:56:53+00:00"
         },
         {
             "name": "propel/propel",
@@ -3131,5 +3131,5 @@
     "platform-overrides": {
         "php": "8.2.0"
     },
-    "plugin-api-version": "2.6.0"
+    "plugin-api-version": "2.9.0"
 }



From noreply at scummvm.org  Sat Jun 13 13:49:21 2026
From: noreply at scummvm.org (lephilousophe)
Date: Sat, 13 Jun 2026 13:49:21 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm-web master ->
 3436f1bf991f60ff9fdbde48417f96179d30879f
Message-ID: <20260613134921.7E2CC7005BC@ron.scummvm.net>

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

Summary:
3436f1bf99 Bump sass from 1.100.0 to 1.101.0


Commit: 3436f1bf991f60ff9fdbde48417f96179d30879f
    https://github.com/scummvm/scummvm-web/commit/3436f1bf991f60ff9fdbde48417f96179d30879f
Author: dependabot[bot] (49699333+dependabot[bot]@users.noreply.github.com)
Date: 2026-06-13T15:49:18+02:00

Commit Message:
Bump sass from 1.100.0 to 1.101.0

Bumps [sass](https://github.com/sass/dart-sass) from 1.100.0 to 1.101.0.
- [Release notes](https://github.com/sass/dart-sass/releases)
- [Changelog](https://github.com/sass/dart-sass/blob/main/CHANGELOG.md)
- [Commits](https://github.com/sass/dart-sass/compare/1.100.0...1.101.0)

---
updated-dependencies:
- dependency-name: sass
  dependency-version: 1.101.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support at github.com>

Changed paths:
    package-lock.json
    package.json


diff --git a/package-lock.json b/package-lock.json
index 17dc5924..88003fcd 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -11,7 +11,7 @@
       "dependencies": {
         "autoprefixer": "^10.5.0",
         "postcss": "^8.5.14",
-        "sass": "^1.100.0"
+        "sass": "^1.101.0"
       }
     },
     "node_modules/@parcel/watcher": {
@@ -594,9 +594,9 @@
       }
     },
     "node_modules/sass": {
-      "version": "1.100.0",
-      "resolved": "https://registry.npmjs.org/sass/-/sass-1.100.0.tgz",
-      "integrity": "sha512-B5j0rYMlinhhOo9tjQebMVVn0TfyXAF+wB3b2ggZUuJ/is/Y+7+JGjirAMxHZ9Z3hIP98NPfamlAkBHa1lAaXQ==",
+      "version": "1.101.0",
+      "resolved": "https://registry.npmjs.org/sass/-/sass-1.101.0.tgz",
+      "integrity": "sha512-OL3GoQyoUdDt843DpVmDO6y2k1sc5IhUDSpu8XucEI+35neq5QivZ1iuegnpraEVTJXlQGK1gl27zKcTLEPbQw==",
       "license": "MIT",
       "dependencies": {
         "chokidar": "^5.0.0",
diff --git a/package.json b/package.json
index b28c581b..47cfbf9b 100644
--- a/package.json
+++ b/package.json
@@ -5,7 +5,7 @@
   "dependencies": {
     "autoprefixer": "^10.5.0",
     "postcss": "^8.5.14",
-    "sass": "^1.100.0"
+    "sass": "^1.101.0"
   },
   "scripts": {
     "postinstall": "(cd dumper-companion && npm ci --omit=dev && npm run build) && node build.js",



From noreply at scummvm.org  Sat Jun 13 13:52:56 2026
From: noreply at scummvm.org (sev-)
Date: Sat, 13 Jun 2026 13:52:56 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 fba6cfd6e5cbab2e8cfc6f9b4b42d65f880cdb99
Message-ID: <20260613135256.1D59F7005BC@ron.scummvm.net>

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

Summary:
6c3c158ae3 GUI: Fix collapsed group state lost when switching launcher views
1a83c9a0cf GUI: Fix wrong group collapsed after Launcher view change
59a71948da GUI: Sort groups with no attribute value last in GridWidget
25930ec055 GUI: Add configuration option to toggle kinetic scrolling support
387085c700 GUI: Stop fluid animation on click
3f1e93fb37 GUI: Preserve selection states in list widgets when applying search filters
e6c2e82c19 GUI: Preserve launcher multi-selections when switching views
02e4a55c52 GUI: Preserve launcher grid selections when changing grouping
09c15f3672 JANITORIAL: Remove trailing spaces
a9e0481ffa BASE: Enable kinetic scrolling by default
fba6cfd6e5 GUI: Added kinetic scrolling checkbox to GUI Options. Bump theme version


Commit: 6c3c158ae30e7dd815fa69685b889c1437e8542b
    https://github.com/scummvm/scummvm/commit/6c3c158ae30e7dd815fa69685b889c1437e8542b
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-06-13T14:09:00+02:00

Commit Message:
GUI: Fix collapsed group state lost when switching launcher views

Fixes #16772
Prevent saving closed groups in LauncherSimple::updateListing() on the initial build to avoid overwriting the config saved by the previous view's destructor.

Changed paths:
    gui/launcher.cpp
    gui/launcher.h


diff --git a/gui/launcher.cpp b/gui/launcher.cpp
index e0b5157e3b0..6351969fea0 100644
--- a/gui/launcher.cpp
+++ b/gui/launcher.cpp
@@ -1111,7 +1111,7 @@ static const int kSelPosGroupingChange = -2;
 
 LauncherSimple::LauncherSimple(const Common::String &title)
 	: LauncherDialog(title),
-	_list(nullptr) {
+	_list(nullptr), _listInitialized(false) {
 	build();
 }
 
@@ -1255,10 +1255,13 @@ void LauncherSimple::updateListing(int selPos) {
 		savedSelection = _list->saveSelection();
 	}
 
-	// Preserve the current collapsed groups before rebuilding the grouped list
-	if (_groupBy != kGroupByNone) {
+	/* Preserve the current collapsed groups before rebuilding the grouped list
+	 * Skip this on the very first call as it would overwrite the config state
+	 * saved by the previous view's destructor */
+	if (_groupBy != kGroupByNone && _listInitialized) {
 		_list->saveClosedGroups(Common::U32String(groupingModes[_groupBy].name));
 	}
+	_listInitialized = true;
 
 	_list->setList(l);
 
diff --git a/gui/launcher.h b/gui/launcher.h
index 0c3b6295e2e..427b6a8daf6 100644
--- a/gui/launcher.h
+++ b/gui/launcher.h
@@ -323,6 +323,7 @@ protected:
 
 private:
 	GroupedListWidget *_list;
+	bool _listInitialized; // Whether the list has already been initialized
 };
 
 } // End of namespace GUI


Commit: 1a83c9a0cf8b132874899ac3bb0494eccf6eb616
    https://github.com/scummvm/scummvm/commit/1a83c9a0cf8b132874899ac3bb0494eccf6eb616
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-06-13T14:09:01+02:00

Commit Message:
GUI: Fix wrong group collapsed after Launcher view change

_groupHeaders is sorted alphabetically, but _groupExpanded is indexed by groupID. Use _groupValueIndex to get the right index.

Changed paths:
    gui/widgets/grid.cpp
    gui/widgets/groupedlist.cpp


diff --git a/gui/widgets/grid.cpp b/gui/widgets/grid.cpp
index a7aa469a2be..94d2d9604f6 100644
--- a/gui/widgets/grid.cpp
+++ b/gui/widgets/grid.cpp
@@ -276,7 +276,8 @@ void GridWidget::loadClosedGroups(const Common::U32String &groupName) {
 			// See if the hidden group is in our group headers still, if so, hide it
 			for (Common::U32StringArray::size_type i = 0; i < _groupHeaders.size(); ++i) {
 				if (_groupHeaders[i] == tok || (tok == "unnamed" && _groupHeaders[i].size() == 0)) {
-					_groupExpanded[i] = false;
+					uint groupID = _groupValueIndex[_groupHeaders[i]];
+					_groupExpanded[groupID] = false;
 					break;
 				}
 			}
@@ -291,7 +292,8 @@ void GridWidget::saveClosedGroups(const Common::U32String &groupName) {
 	// Save the hidden groups to the config
 	Common::String hiddenGroups;
 	for (Common::U32StringArray::size_type i = 0; i < _groupHeaders.size(); ++i) {
-		if (!_groupExpanded[i]) {
+		uint groupID = _groupValueIndex[_groupHeaders[i]];
+		if (!_groupExpanded[groupID]) {
 			if (_groupHeaders[i].size()) {
 				hiddenGroups += _groupHeaders[i];
 			} else {
diff --git a/gui/widgets/groupedlist.cpp b/gui/widgets/groupedlist.cpp
index e2a89be7ca3..988bbc3ff1a 100644
--- a/gui/widgets/groupedlist.cpp
+++ b/gui/widgets/groupedlist.cpp
@@ -171,7 +171,8 @@ void GroupedListWidget::loadClosedGroups(const Common::U32String &groupName) {
 			// See if the hidden group is in our group headers still, if so, hide it
 			for (Common::U32StringArray::size_type i = 0; i < _groupHeaders.size(); ++i) {
 				if (_groupHeaders[i] == tok || (tok == "unnamed" && _groupHeaders[i].size() == 0)) {
-					_groupExpanded[i] = false;
+					uint groupID = _groupValueIndex[_groupHeaders[i]];
+					_groupExpanded[groupID] = false;
 					break;
 				}
 			}
@@ -184,7 +185,8 @@ void GroupedListWidget::saveClosedGroups(const Common::U32String &groupName) {
 	// Save the hidden groups to the config
 	Common::String hiddenGroups;
 	for (Common::U32StringArray::size_type i = 0; i < _groupHeaders.size(); ++i) {
-		if (!_groupExpanded[i]) {
+		uint groupID = _groupValueIndex[_groupHeaders[i]];
+		if (!_groupExpanded[groupID]) {
 			if (_groupHeaders[i].size()) {
 				hiddenGroups += _groupHeaders[i];
 			} else {


Commit: 59a71948da54e6467620defeaeb38851296641ea
    https://github.com/scummvm/scummvm/commit/59a71948da54e6467620defeaeb38851296641ea
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-06-13T14:09:01+02:00

Commit Message:
GUI: Sort groups with no attribute value last in GridWidget

Matches with ListWidget sorting

Changed paths:
    gui/widgets/grid.cpp


diff --git a/gui/widgets/grid.cpp b/gui/widgets/grid.cpp
index 94d2d9604f6..f27acdec152 100644
--- a/gui/widgets/grid.cpp
+++ b/gui/widgets/grid.cpp
@@ -653,7 +653,10 @@ void GridWidget::sortGroups() {
 
 	if (_filter.empty()) {
 		// No filter -> display everything with group headers
-		Common::sort(_groupHeaders.begin(), _groupHeaders.end());
+		Common::sort(_groupHeaders.begin(), _groupHeaders.end(),
+			[](const Common::U32String &first, const Common::U32String &second) {
+				return first.empty() ? 0 : second.empty() ? 1 : first < second;
+			});
 
 		// Avoid reallocation during iteration: that would invalidate our _sortedEntryList items
 		_headerEntryList.reserve(_groupHeaders.size());


Commit: 25930ec055b3e258689f5a9a799e05345e0bc14c
    https://github.com/scummvm/scummvm/commit/25930ec055b3e258689f5a9a799e05345e0bc14c
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-06-13T14:09:01+02:00

Commit Message:
GUI: Add configuration option to toggle kinetic scrolling support

Changed paths:
    base/commandLine.cpp
    gui/animation/FluidScroll.cpp


diff --git a/base/commandLine.cpp b/base/commandLine.cpp
index 1f5ae0583c6..04e8182e085 100644
--- a/base/commandLine.cpp
+++ b/base/commandLine.cpp
@@ -402,6 +402,7 @@ void registerDefaults() {
 	ConfMan.registerDefault("gui_return_to_launcher_at_exit", false);
 	ConfMan.registerDefault("gui_launcher_chooser", "list");
 	ConfMan.registerDefault("grid_items_per_row", 4);
+	ConfMan.registerDefault("gui_kinetic_scrolling", false);
 	// Specify threshold for scanning directories in the launcher
 	// If number of game entries in scummvm.ini exceeds the specified
 	// number, then skip scanning. -1 = scan always
diff --git a/gui/animation/FluidScroll.cpp b/gui/animation/FluidScroll.cpp
index 060a1603360..3605f0d3386 100644
--- a/gui/animation/FluidScroll.cpp
+++ b/gui/animation/FluidScroll.cpp
@@ -26,6 +26,7 @@
 
 #include "common/system.h"
 #include "common/util.h"
+#include "common/config-manager.h"
 #include "gui/animation/FluidScroll.h"
 
 namespace GUI {
@@ -127,6 +128,10 @@ void FluidScroller::stopAnimation() {
 
 void FluidScroller::feedDrag(uint32 time, int deltaY) {
 	_scrollPosRaw += (float)deltaY;
+	if (!ConfMan.getBool("gui_kinetic_scrolling")) {
+		_scrollPosRaw = CLIP(_scrollPosRaw, 0.0f, _maxScroll);
+		return;
+	}
 	_velocityTracker.addPoint(time, _scrollPosRaw);
 }
 
@@ -142,7 +147,7 @@ void FluidScroller::startFling() {
 }
 
 void FluidScroller::startFling(float velocity) {
-	if (fabsf(velocity) < 0.1f) {
+	if (!ConfMan.getBool("gui_kinetic_scrolling") || fabsf(velocity) < 0.1f) {
 		checkBoundaries();
 		return;
 	}
@@ -183,6 +188,11 @@ void FluidScroller::handleMouseWheel(int direction, float multiplier) {
 	if (stepping == 0.0f)
 		return;
 
+	if (!ConfMan.getBool("gui_kinetic_scrolling")) {
+		_scrollPosRaw = CLIP(_scrollPosRaw + stepping, 0.0f, _maxScroll);
+		_mode = kModeNone;
+		return;
+	}
 	feedWheel(g_system->getMillis(), stepping);
 }
 
@@ -198,8 +208,15 @@ void FluidScroller::absorb(float velocity, float distance) {
 }
 
 bool FluidScroller::update(uint32 time, float &outVisualPos) {
+	if (!ConfMan.getBool("gui_kinetic_scrolling")) {
+		_mode = kModeNone;
+	}
 	if (_mode == kModeNone) {
-		outVisualPos = getVisualPosition();
+		float visualPos = getVisualPosition();
+		if (outVisualPos != visualPos) {
+			outVisualPos = visualPos;
+			return true;
+		}
 		return false;
 	}
 
@@ -248,6 +265,8 @@ bool FluidScroller::update(uint32 time, float &outVisualPos) {
 }
 
 float FluidScroller::getVisualPosition() const {
+	if (!ConfMan.getBool("gui_kinetic_scrolling"))
+		return CLIP(_scrollPosRaw, 0.0f, _maxScroll);
 	float rubberBandRange = (float)_viewportHeight * kRubberBandStretchFraction;
 
 	if (_scrollPosRaw < 0)
@@ -259,6 +278,11 @@ float FluidScroller::getVisualPosition() const {
 }
 
 void FluidScroller::checkBoundaries() {
+	if (!ConfMan.getBool("gui_kinetic_scrolling")) {
+		_scrollPosRaw = CLIP(_scrollPosRaw, 0.0f, _maxScroll);
+		_mode = kModeNone;
+		return;
+	}
 	if (_scrollPosRaw < 0) {
 		absorb(0, _scrollPosRaw);
 		_animationOffset = 0;


Commit: 387085c700964ed3ea35ff5c60df70ee3a7777c8
    https://github.com/scummvm/scummvm/commit/387085c700964ed3ea35ff5c60df70ee3a7777c8
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-06-13T14:09:01+02:00

Commit Message:
GUI: Stop fluid animation on click

Stop fluid animation on a mouse left button click in List, Grid and ScrollContainer widgets

Changed paths:
    gui/widgets/grid.cpp
    gui/widgets/grid.h
    gui/widgets/groupedlist.cpp
    gui/widgets/list.cpp
    gui/widgets/list.h
    gui/widgets/scrollcontainer.cpp
    gui/widgets/scrollcontainer.h


diff --git a/gui/widgets/grid.cpp b/gui/widgets/grid.cpp
index f27acdec152..41d1ef4b1f6 100644
--- a/gui/widgets/grid.cpp
+++ b/gui/widgets/grid.cpp
@@ -539,6 +539,7 @@ GridWidget::GridWidget(GuiObject *boss, const Common::String &name)
 	_dragLastY = 0;
 
 	_fluidScroller = new FluidScroller();
+	_wasAnimating = false;
 
 	_filterMatcher = GridWidgetDefaultMatcher;
 	_filterMatcherArg = nullptr;
@@ -1046,6 +1047,7 @@ void GridWidget::handleMouseDown(int x, int y, int button, int clickCount) {
 		_dragLastY = y;
 	}
 	_selectionPending = true;
+	_wasAnimating = _fluidScroller->isAnimating();
 	_fluidScroller->stopAnimation();
 }
 
@@ -1057,7 +1059,7 @@ void GridWidget::handleMouseUp(int x, int y, int button, int clickCount) {
 	_isDragging = false;
 	_selectionPending = false;
 
-	if (wasPending && !wasDragging) {
+	if (wasPending && !wasDragging && !_wasAnimating) {
 		// Find which item was clicked and select it
 		Widget *w = findWidget(x, y);
 		if (w && w != this && w->getType() == kContainerWidget)
@@ -1066,6 +1068,7 @@ void GridWidget::handleMouseUp(int x, int y, int button, int clickCount) {
 
 	if (wasDragging)
 		_fluidScroller->startFling();
+	_wasAnimating = false;
 }
 
 void GridWidget::handleMouseMoved(int x, int y, int button) {
@@ -1117,6 +1120,7 @@ bool GridWidget::handleKeyUp(Common::KeyState state) {
 void GridWidget::lostFocusWidget() {
 	_isMouseDown = _isDragging = false;
 	_dragStartY = _dragLastY = 0;
+	_wasAnimating = false;
 }
 
 void GridWidget::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) {
diff --git a/gui/widgets/grid.h b/gui/widgets/grid.h
index 920550f4764..e2187eb294b 100644
--- a/gui/widgets/grid.h
+++ b/gui/widgets/grid.h
@@ -180,6 +180,7 @@ protected:
 	static const int kDragThreshold = 5;
 
 	FluidScroller *_fluidScroller;
+	bool _wasAnimating;
 	GridItemWidget *_highlightedItem = nullptr;
 
 public:
diff --git a/gui/widgets/groupedlist.cpp b/gui/widgets/groupedlist.cpp
index 988bbc3ff1a..c827b37cea5 100644
--- a/gui/widgets/groupedlist.cpp
+++ b/gui/widgets/groupedlist.cpp
@@ -268,6 +268,7 @@ void GroupedListWidget::handleMouseDown(int x, int y, int button, int clickCount
 	if (button == 1) {
 		_dragStartY = y;
 		_dragLastY = y;
+		_wasAnimating = _fluidScroller->isAnimating();
 		_fluidScroller->stopAnimation();
 	}
 
@@ -283,7 +284,7 @@ void GroupedListWidget::handleMouseUp(int x, int y, int button, int clickCount)
 		if (_isMouseDown && button == 1 && _isDragging)
 			_fluidScroller->startFling();
 
-		if (_isMouseDown && !_isDragging) {
+		if (_isMouseDown && !_isDragging && !_wasAnimating) {
 			int newSelectedItem = findItem(x, y);
 			if (newSelectedItem != -1) {
 				if (isGroupHeader(_listIndex[newSelectedItem])) {
@@ -349,12 +350,13 @@ void GroupedListWidget::handleMouseUp(int x, int y, int button, int clickCount)
 
 	// If this was a double click and the mouse is still over
 	// the selected item, send the double click command
-	if (clickCount == 2 && (_selectedItem == findItem(x, y))) {
+	if (!_wasAnimating && clickCount == 2 && (_selectedItem == findItem(x, y))) {
 		int selectID = getSelected();
 		if (selectID >= 0) {
 			sendCommand(kListItemDoubleClickedCmd, _selectedItem);
 		}
 	}
+	_wasAnimating = false;
 }
 
 void GroupedListWidget::handleMouseWheel(int x, int y, int direction) {
diff --git a/gui/widgets/list.cpp b/gui/widgets/list.cpp
index c3edc794a9f..67454c0caae 100644
--- a/gui/widgets/list.cpp
+++ b/gui/widgets/list.cpp
@@ -83,6 +83,7 @@ ListWidget::ListWidget(Dialog *boss, const Common::String &name, const Common::U
 
 	_scrollPos = 0.0f;
 	_fluidScroller = new FluidScroller();
+	_wasAnimating = false;
 	_isMouseDown = false;
 	_isDragging = false;
 	_dragStartY = _dragLastY = 0;
@@ -136,6 +137,7 @@ ListWidget::ListWidget(Dialog *boss, int x, int y, int w, int h, bool scale, con
 
 	_scrollPos = 0.0f;
 	_fluidScroller = new FluidScroller();
+	_wasAnimating = false;
 	_isMouseDown = false;
 	_isDragging = false;
 	_dragStartY = _dragLastY = 0;
@@ -383,6 +385,7 @@ void ListWidget::handleMouseDown(int x, int y, int button, int clickCount) {
 	_isMouseDown = true;
 	_isDragging = false;
 	_dragLastY = 0;
+	_wasAnimating = _fluidScroller->isAnimating();
 	_fluidScroller->stopAnimation();
 
 	if (button == 1) {
@@ -399,7 +402,7 @@ void ListWidget::handleMouseUp(int x, int y, int button, int clickCount) {
 		if (_isMouseDown && button == 1 && _isDragging)
 			_fluidScroller->startFling();
 
-		if (_isMouseDown && !_isDragging) {
+		if (_isMouseDown && !_isDragging && !_wasAnimating) {
 			// Perform selection
 			int newSelectedItem = findItem(x, y);
 			if (newSelectedItem != -1) {
@@ -453,10 +456,11 @@ void ListWidget::handleMouseUp(int x, int y, int button, int clickCount) {
 
 	// If this was a double click and the mouse is still over
 	// the selected item, send the double click command
-	if (clickCount == 2 && (_selectedItem == findItem(x, y)) &&
+	if (!_wasAnimating && clickCount == 2 && (_selectedItem == findItem(x, y)) &&
 		_selectedItem >= 0) {
 		sendCommand(kListItemDoubleClickedCmd, _selectedItem);
 	}
+	_wasAnimating = false;
 }
 
 void ListWidget::handleMouseWheel(int x, int y, int direction) {
diff --git a/gui/widgets/list.h b/gui/widgets/list.h
index c0196d58d44..42fcbf89f69 100644
--- a/gui/widgets/list.h
+++ b/gui/widgets/list.h
@@ -81,6 +81,7 @@ protected:
 
 	float			_scrollPos;
 	FluidScroller	*_fluidScroller;
+	bool			_wasAnimating;
 	bool			_isMouseDown;
 	bool			_isDragging;
 	int				_dragStartY;
diff --git a/gui/widgets/scrollcontainer.cpp b/gui/widgets/scrollcontainer.cpp
index 9fca3580f44..352e5c4690a 100644
--- a/gui/widgets/scrollcontainer.cpp
+++ b/gui/widgets/scrollcontainer.cpp
@@ -49,6 +49,7 @@ void ScrollContainerWidget::init() {
 	_scrollPos = 0.0f;
 	_limitH = 140;
 	_fluidScroller = new FluidScroller();
+	_wasAnimating = false;
 	recalc();
 }
 
@@ -62,7 +63,11 @@ void ScrollContainerWidget::handleMouseWheel(int x, int y, int direction) {
 void ScrollContainerWidget::handleMouseDown(int x, int y, int button, int clickCount) {
 	_mouseDownY = _mouseDownStartY = y;
 	_isMouseDown = true;
+	_wasAnimating = _fluidScroller->isAnimating();
 	_fluidScroller->stopAnimation();
+	if (_wasAnimating)
+		return;
+
 	Widget *child = _childUnderMouse;
 	if (child) {
 		int childX = x - (child->getAbsX() - getAbsX());
@@ -124,11 +129,12 @@ void ScrollContainerWidget::handleMouseUp(int x, int y, int button, int clickCou
 	_isDragging = false;
 	_childUnderMouse = nullptr;
 
-	if (!isDragging && child) {
+	if (!isDragging && child && !_wasAnimating) {
 		int childX = x - (child->getAbsX() - getAbsX());
 		int childY = y - (child->getAbsY() - getAbsY());
 		child->handleMouseUp(childX, childY, button, clickCount);
 	}
+	_wasAnimating = false;
 }
 
 void ScrollContainerWidget::recalc() {
@@ -251,6 +257,7 @@ void ScrollContainerWidget::markAsDirty() {
 void ScrollContainerWidget::lostFocusWidget() {
 	_isMouseDown = _isDragging = false;
 	_mouseDownY = _mouseDownStartY = 0;
+	_wasAnimating = false;
 }
 
 bool ScrollContainerWidget::containsWidget(Widget *w) const {
diff --git a/gui/widgets/scrollcontainer.h b/gui/widgets/scrollcontainer.h
index 46ac530dd17..d62f4a31beb 100644
--- a/gui/widgets/scrollcontainer.h
+++ b/gui/widgets/scrollcontainer.h
@@ -42,6 +42,7 @@ class ScrollContainerWidget: public Widget, public CommandSender {
 	int _mouseDownStartY = 0;
 	bool _isMouseDown = false;
 	bool _isDragging = false;
+	bool _wasAnimating = false;
 	float _scrollPos = 0.0f;
 	FluidScroller *_fluidScroller = nullptr;
 	Widget *_childUnderMouse = nullptr;


Commit: 3f1e93fb378f0da41421127a32b7c20e669e2003
    https://github.com/scummvm/scummvm/commit/3f1e93fb378f0da41421127a32b7c20e669e2003
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-06-13T14:09:02+02:00

Commit Message:
GUI: Preserve selection states in list widgets when applying search filters

Changed paths:
    gui/widgets/groupedlist.cpp
    gui/widgets/list.cpp


diff --git a/gui/widgets/groupedlist.cpp b/gui/widgets/groupedlist.cpp
index c827b37cea5..ea166077f2e 100644
--- a/gui/widgets/groupedlist.cpp
+++ b/gui/widgets/groupedlist.cpp
@@ -541,8 +541,6 @@ void GroupedListWidget::setFilter(const Common::U32String &filter, bool redraw)
 	if (_filter == filt) // Filter was not changed
 		return;
 
-	int selectedItem = getSelected();
-
 	_filter = filt;
 
 	if (_filter.empty()) {
@@ -582,9 +580,7 @@ void GroupedListWidget::setFilter(const Common::U32String &filter, bool redraw)
 	_scrollPos = 0.0f;
 	_fluidScroller->setPosition(_scrollPos);
 	_selectedItem = -1;
-	// Try to preserve the previous selection
-	if (selectedItem != -1)
-		setSelected(selectedItem);
+	_lastSelectionStartItem = -1;
 
 	if (redraw) {
 		scrollBarRecalc();
diff --git a/gui/widgets/list.cpp b/gui/widgets/list.cpp
index 67454c0caae..697d0625acf 100644
--- a/gui/widgets/list.cpp
+++ b/gui/widgets/list.cpp
@@ -1136,6 +1136,7 @@ void ListWidget::setFilter(const Common::U32String &filter, bool redraw) {
 	_scrollPos = 0.0f;
 	_fluidScroller->setPosition(_scrollPos);
 	_selectedItem = -1;
+	_lastSelectionStartItem = -1;
 
 	if (redraw) {
 		scrollBarRecalc();


Commit: e6c2e82c195920c9942292be0c1a41b4cf90cf28
    https://github.com/scummvm/scummvm/commit/e6c2e82c195920c9942292be0c1a41b4cf90cf28
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-06-13T14:09:02+02:00

Commit Message:
GUI: Preserve launcher multi-selections when switching views

Changed paths:
    gui/launcher.cpp
    gui/launcher.h


diff --git a/gui/launcher.cpp b/gui/launcher.cpp
index 6351969fea0..ad1f4344124 100644
--- a/gui/launcher.cpp
+++ b/gui/launcher.cpp
@@ -1099,7 +1099,30 @@ int LauncherChooser::runModal() {
 	do {
 		ret = _impl->run();
 		if (ret == kSwitchLauncherDialog) {
+			Common::StringArray selectedDomains;
+			const Common::Array<bool> &selections = _impl->getSelectedItems();
+			const Common::StringArray &domains = _impl->getDomains();
+			for (uint i = 0; i < selections.size() && i < domains.size(); ++i) {
+				if (selections[i]) {
+					selectedDomains.push_back(domains[i]);
+				}
+			}
+
 			selectLauncher();
+
+			if (_impl && !selectedDomains.empty()) {
+				const Common::StringArray &newDomains = _impl->getDomains();
+				Common::Array<bool> &newSelections = const_cast<Common::Array<bool>&>(_impl->getSelectedItems());
+				for (const auto &domain : selectedDomains) {
+					for (uint i = 0; i < newDomains.size(); ++i) {
+						if (newDomains[i] == domain) {
+							if (i < newSelections.size())
+								newSelections[i] = true;
+							break;
+						}
+					}
+				}
+			}
 		}
 	} while (ret < -1);
 	return ret;
diff --git a/gui/launcher.h b/gui/launcher.h
index 427b6a8daf6..a56e6add26d 100644
--- a/gui/launcher.h
+++ b/gui/launcher.h
@@ -123,6 +123,8 @@ public:
 	void handleOtherEvent(const Common::Event &evt) override;
 	bool doGameDetection(const Common::Path &path);
 	Common::String getGameConfig(int item, Common::String key);
+	virtual const Common::Array<bool>& getSelectedItems() const = 0; // Get the selected items from the current view (list or grid)
+	const Common::StringArray& getDomains() const { return _domains; }
 protected:
 	EditTextWidget  *_searchWidget;
 #ifndef DISABLE_FANCY_THEMES
@@ -219,8 +221,6 @@ protected:
 	 */
 	bool hasAnySelection(const Common::Array<bool> &selectedItems) const;
 
-	// Get the selected items from the current view (list or grid).
-	virtual const Common::Array<bool>& getSelectedItems() const = 0;
 
 	/**
 	 * Handle "Edit game..." button.


Commit: 02e4a55c528d4db934d3348a56c90c9dc2cf068e
    https://github.com/scummvm/scummvm/commit/02e4a55c528d4db934d3348a56c90c9dc2cf068e
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-06-13T14:09:02+02:00

Commit Message:
GUI: Preserve launcher grid selections when changing grouping

Changed paths:
    gui/launcher.cpp


diff --git a/gui/launcher.cpp b/gui/launcher.cpp
index ad1f4344124..dfca4a7d991 100644
--- a/gui/launcher.cpp
+++ b/gui/launcher.cpp
@@ -1730,7 +1730,7 @@ void LauncherGrid::handleCommand(CommandSender *sender, uint32 cmd, uint32 data)
 				}
 				++mode;
 			}
-			updateListing();
+			updateListing(kSelPosGroupingChange);
 		}
 		break;
 	}
@@ -1785,12 +1785,18 @@ void LauncherGrid::updateListing(int selPos) {
 
 	const int oldSel = _grid->getSelected();
 
+	Common::Array<bool> savedSelection;
+	const bool restoringGroupedSelection = (selPos == kSelPosGroupingChange);
+	if (restoringGroupedSelection)
+		savedSelection = _grid->getSelectedItems();
 	_grid->setEntryList(&gridList);
 	groupEntries(domainList);
 
-	if (_groupBy != kGroupByNone && selPos != -1) {
+	if (restoringGroupedSelection)
+		const_cast<Common::Array<bool>&>(_grid->getSelectedItems()) = savedSelection;
+	else if (_groupBy != kGroupByNone && selPos != -1)
 		_grid->setSelected(_grid->getNewSel(selPos));
-	} else if (oldSel < (int)gridList.size() && oldSel >= 0)
+	else if (oldSel < (int)gridList.size() && oldSel >= 0)
 		_grid->setSelected(oldSel);	// Restore the old selection
 	else if (oldSel != -1)
 		// Select the last entry if the list has been reduced


Commit: 09c15f36720fb60d81eb49d792a3ac598ec76bdf
    https://github.com/scummvm/scummvm/commit/09c15f36720fb60d81eb49d792a3ac598ec76bdf
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2026-06-13T15:25:32+02:00

Commit Message:
JANITORIAL: Remove trailing spaces

Changed paths:
    gui/animation/FluidScroll.cpp


diff --git a/gui/animation/FluidScroll.cpp b/gui/animation/FluidScroll.cpp
index 3605f0d3386..a1b677c90cb 100644
--- a/gui/animation/FluidScroll.cpp
+++ b/gui/animation/FluidScroll.cpp
@@ -67,7 +67,7 @@ float FluidScroller::VelocityTracker::calculateVelocity() const {
 	for (int i = 0; i < 4 && i < count - 1; ++i) {
 		int i1 = (index + kHistorySize - 1 - i) % kHistorySize; // current point
 		int i2 = (index + kHistorySize - 2 - i) % kHistorySize; // previous point
-		
+
 		uint32 dt = samples[i1].time - samples[i2].time;
 
 		if (dt > 0)
@@ -78,7 +78,7 @@ float FluidScroller::VelocityTracker::calculateVelocity() const {
 
 	if (validVelocities == 0)
 		return 0.0f;
-	
+
 	// Weighted average of historical velocities
 	float totalVelocity = 0.0f;
 	float totalWeight = 0.0f;
@@ -91,12 +91,12 @@ float FluidScroller::VelocityTracker::calculateVelocity() const {
 	return totalVelocity / totalWeight;
 }
 
-FluidScroller::FluidScroller() : 
-	_mode(kModeNone), 
-	_startTime(0), 
-	_scrollPosRaw(0.0f), 
-	_animationOffset(0.0f), 
-	_maxScroll(0.0f), 
+FluidScroller::FluidScroller() :
+	_mode(kModeNone),
+	_startTime(0),
+	_scrollPosRaw(0.0f),
+	_animationOffset(0.0f),
+	_maxScroll(0.0f),
 	_stepSize(1.0f),
 	_viewportHeight(0),
 	_lastWheelTime(0),
@@ -165,7 +165,7 @@ void FluidScroller::feedWheel(uint32 time, float deltaY) {
 	/*
 	 * Cap the duration to prevent extreme high/low velocity
 	 * Otherwise use the actual interval
-	 */ 
+	 */
 	uint32 effectiveDt = dt;
 	if (dt > 200)
 		effectiveDt = 200;
@@ -199,7 +199,7 @@ void FluidScroller::handleMouseWheel(int direction, float multiplier) {
 void FluidScroller::absorb(float velocity, float distance) {
 	_mode = kModeSpringBack;
 	_startTime = g_system->getMillis();
-	
+
 	_lambda = 2.0f * (float)M_PI / kDefaultSpringResponse;
 	_stretchDistance = distance;
 
@@ -273,7 +273,7 @@ float FluidScroller::getVisualPosition() const {
 		return -calculateRubberBandOffset(-_scrollPosRaw, rubberBandRange);
 	else if (_scrollPosRaw > _maxScroll)
 		return _maxScroll + calculateRubberBandOffset(_scrollPosRaw - _maxScroll, rubberBandRange);
-	
+
 	return _scrollPosRaw;
 }
 


Commit: a9e0481ffa4bdfcd4652c758b28da6bcd7eccfca
    https://github.com/scummvm/scummvm/commit/a9e0481ffa4bdfcd4652c758b28da6bcd7eccfca
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2026-06-13T15:26:02+02:00

Commit Message:
BASE: Enable kinetic scrolling by default

Changed paths:
    base/commandLine.cpp


diff --git a/base/commandLine.cpp b/base/commandLine.cpp
index 04e8182e085..c29d1eb09ac 100644
--- a/base/commandLine.cpp
+++ b/base/commandLine.cpp
@@ -402,7 +402,7 @@ void registerDefaults() {
 	ConfMan.registerDefault("gui_return_to_launcher_at_exit", false);
 	ConfMan.registerDefault("gui_launcher_chooser", "list");
 	ConfMan.registerDefault("grid_items_per_row", 4);
-	ConfMan.registerDefault("gui_kinetic_scrolling", false);
+	ConfMan.registerDefault("gui_kinetic_scrolling", true);
 	// Specify threshold for scanning directories in the launcher
 	// If number of game entries in scummvm.ini exceeds the specified
 	// number, then skip scanning. -1 = scan always


Commit: fba6cfd6e5cbab2e8cfc6f9b4b42d65f880cdb99
    https://github.com/scummvm/scummvm/commit/fba6cfd6e5cbab2e8cfc6f9b4b42d65f880cdb99
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2026-06-13T15:36:30+02:00

Commit Message:
GUI: Added kinetic scrolling checkbox to GUI Options. Bump theme version

Changed paths:
    gui/ThemeEngine.h
    gui/options.cpp
    gui/options.h
    gui/themes/common/highres_layout.stx
    gui/themes/common/lowres_layout.stx
    gui/themes/default.inc
    gui/themes/residualvm.zip
    gui/themes/residualvm/THEMERC
    gui/themes/scummclassic.zip
    gui/themes/scummclassic/THEMERC
    gui/themes/scummclassic/classic_layout.stx
    gui/themes/scummclassic/classic_layout_lowres.stx
    gui/themes/scummmodern.zip
    gui/themes/scummmodern/THEMERC
    gui/themes/scummremastered.zip
    gui/themes/scummremastered/THEMERC


diff --git a/gui/ThemeEngine.h b/gui/ThemeEngine.h
index 199d0f5984b..48d47c5d176 100644
--- a/gui/ThemeEngine.h
+++ b/gui/ThemeEngine.h
@@ -36,7 +36,7 @@
 #include "graphics/pixelformat.h"
 
 
-#define SCUMMVM_THEME_VERSION_STR "SCUMMVM_STX0.9.22"
+#define SCUMMVM_THEME_VERSION_STR "SCUMMVM_STX0.9.23"
 
 class OSystem;
 
diff --git a/gui/options.cpp b/gui/options.cpp
index 895a3aa1428..7b971f05ab1 100644
--- a/gui/options.cpp
+++ b/gui/options.cpp
@@ -2186,6 +2186,8 @@ GlobalOptionsDialog::GlobalOptionsDialog(LauncherDialog *launcher)
 	_guiReturnToLauncherAtExit = nullptr;
 	_guiConfirmExit = nullptr;
 	_guiDisableBDFScaling = nullptr;
+	_guiKineticScrolling = nullptr;
+
 #ifdef USE_UPDATES
 	_updatesPopUpDesc = nullptr;
 	_updatesPopUp = nullptr;
@@ -2662,11 +2664,18 @@ void GlobalOptionsDialog::addGUIControls(GuiObject *boss, const Common::String &
 
 	_guiDisableBDFScaling = new CheckboxWidget(boss, prefix + "DisableBDFScaling",
 		_("Disable fixed font scaling"),
-		_("Do not upscale fixed size fonts in the GUI. This reduces artefacts on low resolution screens")
+		_("Do not upscale fixed size fonts in the GUI. This reduces artefacts on low resolution screens.")
 	);
 
 	_guiDisableBDFScaling->setState(ConfMan.getBool("gui_disable_fixed_font_scaling", _domain));
 
+	_guiKineticScrolling = new CheckboxWidget(boss, prefix + "KineticScrolling",
+		_("Enable kinetic scrolling in lists"),
+		_("Enable smooth, momentum-based scrolling in list widgets.")
+	);
+
+	_guiKineticScrolling->setState(ConfMan.getBool("gui_kinetic_scrolling", _domain));
+
 #ifdef USE_TRANSLATION
 	_guiLanguagePopUpDesc = new StaticTextWidget(boss, prefix + "GuiLanguagePopupDesc", _("GUI language:"), _("Language of ScummVM GUI"));
 	_guiLanguagePopUp = new PopUpWidget(boss, prefix + "GuiLanguagePopup");
@@ -3139,6 +3148,10 @@ void GlobalOptionsDialog::apply() {
 		ConfMan.setBool("gui_disable_fixed_font_scaling", _guiDisableBDFScaling->getState(), _domain);
 	}
 
+	if (_guiKineticScrolling) {
+		ConfMan.setBool("gui_kinetic_scrolling", _guiKineticScrolling->getState(), _domain);
+	}
+
 #ifdef USE_DISCORD
 	if (_discordRpcCheckbox) {
 		ConfMan.setBool("discord_rpc", _discordRpcCheckbox->getState(), _domain);
diff --git a/gui/options.h b/gui/options.h
index 37509a3d303..f9041f62cec 100644
--- a/gui/options.h
+++ b/gui/options.h
@@ -317,6 +317,7 @@ protected:
 	CheckboxWidget *_guiReturnToLauncherAtExit;
 	CheckboxWidget *_guiConfirmExit;
 	CheckboxWidget *_guiDisableBDFScaling;
+	CheckboxWidget *_guiKineticScrolling;
 
 	void addGUIControls(GuiObject *boss, const Common::String &prefix, bool lowres);
 
diff --git a/gui/themes/common/highres_layout.stx b/gui/themes/common/highres_layout.stx
index fd5d3359a5d..c8f8d872fd2 100644
--- a/gui/themes/common/highres_layout.stx
+++ b/gui/themes/common/highres_layout.stx
@@ -1165,6 +1165,11 @@
 						type = 'Checkbox'
 				/>
 			</layout>
+			<layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '10' align = 'center'>
+				<widget name = 'KineticScrolling'
+						type = 'Checkbox'
+				/>
+			</layout>
 			<layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '10' align = 'center'>
 				<widget name = 'UseSystemDialogs'
 						type = 'Checkbox'
diff --git a/gui/themes/common/lowres_layout.stx b/gui/themes/common/lowres_layout.stx
index aa47897d488..ae6311cebe3 100644
--- a/gui/themes/common/lowres_layout.stx
+++ b/gui/themes/common/lowres_layout.stx
@@ -995,6 +995,11 @@
 						type = 'Checkbox'
 				/>
 			</layout>
+			<layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '6' align = 'center'>
+				<widget name = 'KineticScrolling'
+						type = 'Checkbox'
+				/>
+			</layout>
 			<layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '6' align = 'center'>
 				<widget name = 'UseSystemDialogs'
 						type = 'Checkbox'
diff --git a/gui/themes/default.inc b/gui/themes/default.inc
index 00deb393158..49ef03e6c50 100644
--- a/gui/themes/default.inc
+++ b/gui/themes/default.inc
@@ -2201,6 +2201,11 @@ const char *defaultXML1 = "<?xml version = '1.0'?>"
 "/>"
 "</layout>"
 "<layout type='horizontal' padding='0,0,0,0' spacing='10' align='center'>"
+"<widget name='KineticScrolling' "
+"type='Checkbox' "
+"/>"
+"</layout>"
+"<layout type='horizontal' padding='0,0,0,0' spacing='10' align='center'>"
 "<widget name='UseSystemDialogs' "
 "type='Checkbox' "
 "/>"
@@ -4662,6 +4667,11 @@ const char *defaultXML1 = "<?xml version = '1.0'?>"
 "/>"
 "</layout>"
 "<layout type='horizontal' padding='0,0,0,0' spacing='6' align='center'>"
+"<widget name='KineticScrolling' "
+"type='Checkbox' "
+"/>"
+"</layout>"
+"<layout type='horizontal' padding='0,0,0,0' spacing='6' align='center'>"
 "<widget name='UseSystemDialogs' "
 "type='Checkbox' "
 "/>"
diff --git a/gui/themes/residualvm.zip b/gui/themes/residualvm.zip
index 08d43e5f105..0d57f6e477a 100644
Binary files a/gui/themes/residualvm.zip and b/gui/themes/residualvm.zip differ
diff --git a/gui/themes/residualvm/THEMERC b/gui/themes/residualvm/THEMERC
index a89a97066f7..442f37e083e 100644
--- a/gui/themes/residualvm/THEMERC
+++ b/gui/themes/residualvm/THEMERC
@@ -1,3 +1,3 @@
-[SCUMMVM_STX0.9.22:ResidualVM Modern Theme Remastered:No Author]
+[SCUMMVM_STX0.9.23:ResidualVM Modern Theme Remastered:No Author]
 %using ../common
 %using ../common-svg
diff --git a/gui/themes/scummclassic.zip b/gui/themes/scummclassic.zip
index 2f2df889c07..ca2607b382f 100644
Binary files a/gui/themes/scummclassic.zip and b/gui/themes/scummclassic.zip differ
diff --git a/gui/themes/scummclassic/THEMERC b/gui/themes/scummclassic/THEMERC
index 433b25c75f0..d3a8ae3af49 100644
--- a/gui/themes/scummclassic/THEMERC
+++ b/gui/themes/scummclassic/THEMERC
@@ -1 +1 @@
-[SCUMMVM_STX0.9.22:ScummVM Classic Theme:No Author]
+[SCUMMVM_STX0.9.23:ScummVM Classic Theme:No Author]
diff --git a/gui/themes/scummclassic/classic_layout.stx b/gui/themes/scummclassic/classic_layout.stx
index abc6f9fd891..ceafccb9459 100644
--- a/gui/themes/scummclassic/classic_layout.stx
+++ b/gui/themes/scummclassic/classic_layout.stx
@@ -819,6 +819,11 @@
 						type = 'Checkbox'
 				/>
 			</layout>
+			<layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '10' align = 'center'>
+				<widget name = 'KineticScrolling'
+						type = 'Checkbox'
+				/>
+			</layout>
 			<layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '10' align = 'center'>
 				<widget name = 'UseSystemDialogs'
 						type = 'Checkbox'
diff --git a/gui/themes/scummclassic/classic_layout_lowres.stx b/gui/themes/scummclassic/classic_layout_lowres.stx
index 8c93d4daf83..0fe3d149386 100644
--- a/gui/themes/scummclassic/classic_layout_lowres.stx
+++ b/gui/themes/scummclassic/classic_layout_lowres.stx
@@ -828,6 +828,11 @@
 						type = 'Checkbox'
 				/>
 			</layout>
+			<layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '6' align = 'center'>
+				<widget name = 'KineticScrolling'
+						type = 'Checkbox'
+				/>
+			</layout>
 			<layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '6' align = 'center'>
 				<widget name = 'UseSystemDialogs'
 						type = 'Checkbox'
diff --git a/gui/themes/scummmodern.zip b/gui/themes/scummmodern.zip
index b4a333f2f34..756addce923 100644
Binary files a/gui/themes/scummmodern.zip and b/gui/themes/scummmodern.zip differ
diff --git a/gui/themes/scummmodern/THEMERC b/gui/themes/scummmodern/THEMERC
index a8ff598f81a..f56e57a3da9 100644
--- a/gui/themes/scummmodern/THEMERC
+++ b/gui/themes/scummmodern/THEMERC
@@ -1,2 +1,2 @@
-[SCUMMVM_STX0.9.22:ScummVM Modern Theme:No Author]
+[SCUMMVM_STX0.9.23:ScummVM Modern Theme:No Author]
 %using ../common
diff --git a/gui/themes/scummremastered.zip b/gui/themes/scummremastered.zip
index 5391be7e260..6abf823e5d5 100644
Binary files a/gui/themes/scummremastered.zip and b/gui/themes/scummremastered.zip differ
diff --git a/gui/themes/scummremastered/THEMERC b/gui/themes/scummremastered/THEMERC
index 3c2b6baa640..2fffe8f54ca 100644
--- a/gui/themes/scummremastered/THEMERC
+++ b/gui/themes/scummremastered/THEMERC
@@ -1,3 +1,3 @@
-[SCUMMVM_STX0.9.22:ScummVM Modern Theme Remastered:No Author]
+[SCUMMVM_STX0.9.23:ScummVM Modern Theme Remastered:No Author]
 %using ../common
 %using ../common-svg



From noreply at scummvm.org  Sat Jun 13 13:52:56 2026
From: noreply at scummvm.org (ScummVM-Translations)
Date: Sat, 13 Jun 2026 13:52:56 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 39cf746353d59d2676491112508e4b3774171f8e
Message-ID: <20260613135256.E6DBD7005CF@ron.scummvm.net>

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

Summary:
39cf746353 I18N: Update translations templates


Commit: 39cf746353d59d2676491112508e4b3774171f8e
    https://github.com/scummvm/scummvm/commit/39cf746353d59d2676491112508e4b3774171f8e
Author: Weblate (noreply at weblate.org)
Date: 2026-06-13T13:52:52Z

Commit Message:
I18N: Update translations templates

Changed paths:
    po/scummvm.pot


diff --git a/po/scummvm.pot b/po/scummvm.pot
index 70927091701..e0050144bc5 100644
--- a/po/scummvm.pot
+++ b/po/scummvm.pot
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2026.2.1git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-13 10:20+0000\n"
+"POT-Creation-Date: 2026-06-13 13:52+0000\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL at ADDRESS>\n"
 "Language-Team: LANGUAGE <LL at li.org>\n"
@@ -113,7 +113,7 @@ msgstr ""
 #: gui/downloadpacksdialog.cpp:465 gui/editgamedialog.cpp:340
 #: gui/editrecorddialog.cpp:66 gui/integrity-dialog.cpp:122
 #: gui/integrity-dialog.cpp:253 gui/filebrowser-dialog.cpp:68
-#: gui/fluidsynth-dialog.cpp:153 gui/massadd.cpp:93 gui/options.cpp:2416
+#: gui/fluidsynth-dialog.cpp:153 gui/massadd.cpp:93 gui/options.cpp:2418
 #: gui/predictivedialog.cpp:68 gui/printing-dialog.cpp:96
 #: gui/recorderdialog.cpp:72 gui/recorderdialog.cpp:211
 #: gui/remotebrowser.cpp:59 gui/saveload-dialog.cpp:64
@@ -182,23 +182,23 @@ msgid ""
 "so your browser can forward data to ScummVM"
 msgstr ""
 
-#: gui/cloudconnectionwizard.cpp:257 gui/options.cpp:3776
+#: gui/cloudconnectionwizard.cpp:257 gui/options.cpp:3789
 msgid "Stop server"
 msgstr ""
 
-#: gui/cloudconnectionwizard.cpp:257 gui/options.cpp:2840
+#: gui/cloudconnectionwizard.cpp:257 gui/options.cpp:2849
 msgid "Run server"
 msgstr ""
 
-#: gui/cloudconnectionwizard.cpp:258 gui/options.cpp:3777
+#: gui/cloudconnectionwizard.cpp:258 gui/options.cpp:3790
 msgid "Stop local webserver"
 msgstr ""
 
-#: gui/cloudconnectionwizard.cpp:258 gui/options.cpp:2840
+#: gui/cloudconnectionwizard.cpp:258 gui/options.cpp:2849
 msgid "Run local webserver"
 msgstr ""
 
-#: gui/cloudconnectionwizard.cpp:266 gui/options.cpp:2841 gui/options.cpp:3784
+#: gui/cloudconnectionwizard.cpp:266 gui/options.cpp:2850 gui/options.cpp:3797
 msgid "Not running"
 msgstr ""
 
@@ -320,8 +320,8 @@ msgstr ""
 #: gui/cloudconnectionwizard.cpp:530 gui/downloaddialog.cpp:118
 #: gui/downloaddialog.cpp:152 gui/filebrowser-dialog.cpp:135
 #: gui/fluidsynth-dialog.cpp:210 gui/launcher.cpp:428 gui/launcher.cpp:457
-#: gui/launcher.cpp:534 gui/launcher.cpp:1938 gui/options.cpp:3485
-#: gui/options.cpp:3859 base/main.cpp:608
+#: gui/launcher.cpp:534 gui/launcher.cpp:1970 gui/options.cpp:3498
+#: gui/options.cpp:3872 base/main.cpp:608
 #: backends/events/default/default-events.cpp:193 engines/buried/buried.cpp:598
 #: engines/chamber/metaengine.cpp:91 engines/director/events.cpp:122
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:139
@@ -340,8 +340,8 @@ msgstr ""
 #: gui/cloudconnectionwizard.cpp:530 gui/downloaddialog.cpp:118
 #: gui/downloaddialog.cpp:153 gui/filebrowser-dialog.cpp:135
 #: gui/fluidsynth-dialog.cpp:210 gui/launcher.cpp:428 gui/launcher.cpp:457
-#: gui/launcher.cpp:534 gui/launcher.cpp:1939 gui/options.cpp:3485
-#: gui/options.cpp:3859 engines/buried/buried.cpp:598
+#: gui/launcher.cpp:534 gui/launcher.cpp:1971 gui/options.cpp:3498
+#: gui/options.cpp:3872 engines/buried/buried.cpp:598
 #: engines/chamber/metaengine.cpp:97 engines/director/events.cpp:122
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:139
 #: engines/efh/metaengine.cpp:368 engines/grim/grim.cpp:366
@@ -368,7 +368,7 @@ msgstr ""
 #: gui/editrecorddialog.cpp:67 gui/integrity-dialog.cpp:260
 #: gui/fluidsynth-dialog.cpp:154 gui/massadd.cpp:90 gui/message.cpp:156
 #: gui/message.cpp:160 gui/message.cpp:231 gui/message.cpp:270
-#: gui/message.cpp:274 gui/options.cpp:2418 gui/predictivedialog.cpp:69
+#: gui/message.cpp:274 gui/options.cpp:2420 gui/predictivedialog.cpp:69
 #: gui/saveload-dialog.cpp:1244 base/main.cpp:675 base/main.cpp:700
 #: dists/android.strings.xml.cpp:33 engines/engine.cpp:846
 #: backends/platform/wii/options.cpp:46 engines/buried/buried.cpp:622
@@ -549,11 +549,11 @@ msgstr ""
 msgid "Proceed"
 msgstr ""
 
-#: gui/dump-all-dialogs.cpp:123 gui/options.cpp:3347
+#: gui/dump-all-dialogs.cpp:123 gui/options.cpp:3360
 msgid "icon packs"
 msgstr ""
 
-#: gui/dump-all-dialogs.cpp:127 gui/options.cpp:3358
+#: gui/dump-all-dialogs.cpp:127 gui/options.cpp:3371
 msgid "shader packs"
 msgstr ""
 
@@ -565,11 +565,11 @@ msgstr ""
 msgid "Game"
 msgstr ""
 
-#: gui/editgamedialog.cpp:164 gui/options.cpp:2075 gui/options.cpp:2254
+#: gui/editgamedialog.cpp:164 gui/options.cpp:2075 gui/options.cpp:2256
 msgid "GFX"
 msgstr ""
 
-#: gui/editgamedialog.cpp:164 gui/options.cpp:2075 gui/options.cpp:2254
+#: gui/editgamedialog.cpp:164 gui/options.cpp:2075 gui/options.cpp:2256
 #: backends/graphics/atari/atari-graphics.cpp:1056
 #: backends/graphics/sdl/sdl-graphics.cpp:487
 msgid "Graphics"
@@ -584,11 +584,11 @@ msgctxt "lowres"
 msgid "Override global graphic settings"
 msgstr ""
 
-#: gui/editgamedialog.cpp:185 gui/options.cpp:2288 engines/dialogs.cpp:354
+#: gui/editgamedialog.cpp:185 gui/options.cpp:2290 engines/dialogs.cpp:354
 msgid "Keymaps"
 msgstr ""
 
-#: gui/editgamedialog.cpp:197 gui/options.cpp:2300 engines/dialogs.cpp:366
+#: gui/editgamedialog.cpp:197 gui/options.cpp:2302 engines/dialogs.cpp:366
 msgid "Backend"
 msgstr ""
 
@@ -601,7 +601,7 @@ msgctxt "lowres"
 msgid "Override global backend settings"
 msgstr ""
 
-#: gui/editgamedialog.cpp:220 gui/options.cpp:2318 engines/dialogs.cpp:329
+#: gui/editgamedialog.cpp:220 gui/options.cpp:2320 engines/dialogs.cpp:329
 msgid "Audio"
 msgstr ""
 
@@ -614,11 +614,11 @@ msgctxt "lowres"
 msgid "Override global audio settings"
 msgstr ""
 
-#: gui/editgamedialog.cpp:234 gui/options.cpp:2323
+#: gui/editgamedialog.cpp:234 gui/options.cpp:2325
 msgid "Volume"
 msgstr ""
 
-#: gui/editgamedialog.cpp:236 gui/options.cpp:2325
+#: gui/editgamedialog.cpp:236 gui/options.cpp:2327
 msgctxt "lowres"
 msgid "Volume"
 msgstr ""
@@ -632,7 +632,7 @@ msgctxt "lowres"
 msgid "Override global volume settings"
 msgstr ""
 
-#: gui/editgamedialog.cpp:252 gui/options.cpp:2333
+#: gui/editgamedialog.cpp:252 gui/options.cpp:2335
 msgid "MIDI"
 msgstr ""
 
@@ -645,7 +645,7 @@ msgctxt "lowres"
 msgid "Override global MIDI settings"
 msgstr ""
 
-#: gui/editgamedialog.cpp:267 gui/options.cpp:2339
+#: gui/editgamedialog.cpp:267 gui/options.cpp:2341
 msgid "MT-32"
 msgstr ""
 
@@ -658,11 +658,11 @@ msgctxt "lowres"
 msgid "Override global MT-32 settings"
 msgstr ""
 
-#: gui/editgamedialog.cpp:281 gui/options.cpp:2346
+#: gui/editgamedialog.cpp:281 gui/options.cpp:2348
 msgid "Paths"
 msgstr ""
 
-#: gui/editgamedialog.cpp:283 gui/options.cpp:2348
+#: gui/editgamedialog.cpp:283 gui/options.cpp:2350
 msgctxt "lowres"
 msgid "Paths"
 msgstr ""
@@ -684,7 +684,7 @@ msgctxt "lowres"
 msgid "Game Path:"
 msgstr ""
 
-#: gui/editgamedialog.cpp:303 gui/options.cpp:2555
+#: gui/editgamedialog.cpp:303 gui/options.cpp:2557
 msgid "Extra Path:"
 msgstr ""
 
@@ -693,33 +693,33 @@ msgstr ""
 msgid "Specifies path to additional data used by the game"
 msgstr ""
 
-#: gui/editgamedialog.cpp:305 gui/options.cpp:2557
+#: gui/editgamedialog.cpp:305 gui/options.cpp:2559
 msgctxt "lowres"
 msgid "Extra Path:"
 msgstr ""
 
-#: gui/editgamedialog.cpp:306 gui/options.cpp:2530 gui/options.cpp:2558
-#: gui/options.cpp:2567 gui/options.cpp:2848
+#: gui/editgamedialog.cpp:306 gui/options.cpp:2532 gui/options.cpp:2560
+#: gui/options.cpp:2569 gui/options.cpp:2857
 #: backends/platform/libretro/src/libretro-options-widget.cpp:277
 msgctxt "path"
 msgid "None"
 msgstr ""
 
-#: gui/editgamedialog.cpp:312 gui/options.cpp:2519
+#: gui/editgamedialog.cpp:312 gui/options.cpp:2521
 msgid "Save Path:"
 msgstr ""
 
 #: gui/editgamedialog.cpp:312 gui/editgamedialog.cpp:314
-#: gui/editgamedialog.cpp:315 gui/options.cpp:2519 gui/options.cpp:2521
+#: gui/editgamedialog.cpp:315 gui/options.cpp:2521 gui/options.cpp:2523
 msgid "Specifies where your saved games are put"
 msgstr ""
 
-#: gui/editgamedialog.cpp:314 gui/options.cpp:2521
+#: gui/editgamedialog.cpp:314 gui/options.cpp:2523
 msgctxt "lowres"
 msgid "Save Path:"
 msgstr ""
 
-#: gui/editgamedialog.cpp:315 gui/options.cpp:1844 gui/options.cpp:2522
+#: gui/editgamedialog.cpp:315 gui/options.cpp:1844 gui/options.cpp:2524
 #: backends/platform/wii/options.cpp:55
 msgid "Default"
 msgstr ""
@@ -776,8 +776,8 @@ msgstr ""
 #: gui/editgamedialog.cpp:365 gui/editgamedialog.cpp:380 gui/options.cpp:1613
 #: gui/options.cpp:1627 gui/options.cpp:1644 gui/options.cpp:1658
 #: gui/options.cpp:1675 gui/options.cpp:1713 gui/options.cpp:1729
-#: gui/options.cpp:2144 gui/options.cpp:2600 gui/options.cpp:2674
-#: gui/options.cpp:2888 gui/options.cpp:3389 audio/null.cpp:45
+#: gui/options.cpp:2144 gui/options.cpp:2602 gui/options.cpp:2683
+#: gui/options.cpp:2897 gui/options.cpp:3402 audio/null.cpp:45
 #: backends/platform/android/options.cpp:157
 #: backends/platform/android/options.cpp:158
 #: backends/platform/android/options.cpp:186
@@ -804,7 +804,7 @@ msgctxt "lowres"
 msgid "Platform:"
 msgstr ""
 
-#: gui/editgamedialog.cpp:566 gui/options.cpp:3413
+#: gui/editgamedialog.cpp:566 gui/options.cpp:3426
 msgid "Select SoundFont"
 msgstr ""
 
@@ -812,11 +812,11 @@ msgstr ""
 msgid "Select additional game directory"
 msgstr ""
 
-#: gui/editgamedialog.cpp:618 gui/options.cpp:3261
+#: gui/editgamedialog.cpp:618 gui/options.cpp:3274
 msgid "Select directory for saved games"
 msgstr ""
 
-#: gui/editgamedialog.cpp:625 gui/options.cpp:3268
+#: gui/editgamedialog.cpp:625 gui/options.cpp:3281
 msgid "The chosen directory cannot be written to. Please select another one."
 msgstr ""
 
@@ -955,7 +955,7 @@ msgstr ""
 msgid "Triangle"
 msgstr ""
 
-#: gui/fluidsynth-dialog.cpp:139 gui/options.cpp:2366
+#: gui/fluidsynth-dialog.cpp:139 gui/options.cpp:2368
 msgid "Misc"
 msgstr ""
 
@@ -993,7 +993,7 @@ msgid ""
 "Do you really want to reset all FluidSynth settings to their default values?"
 msgstr ""
 
-#: gui/gui-manager.cpp:159 gui/options.cpp:2357
+#: gui/gui-manager.cpp:159 gui/options.cpp:2359
 msgid "GUI"
 msgstr ""
 
@@ -1200,7 +1200,7 @@ msgid ""
 "and illegal game copies.\n"
 msgstr ""
 
-#: gui/helpdialog.cpp:68 gui/options.cpp:2377
+#: gui/helpdialog.cpp:68 gui/options.cpp:2379
 msgid "Cloud"
 msgstr ""
 
@@ -1485,85 +1485,85 @@ msgstr ""
 msgid "Grid view"
 msgstr ""
 
-#: gui/launcher.cpp:1145
+#: gui/launcher.cpp:1168
 msgid "~S~tart"
 msgstr ""
 
-#: gui/launcher.cpp:1145
+#: gui/launcher.cpp:1168
 msgid "Start selected game"
 msgstr ""
 
-#: gui/launcher.cpp:1148
+#: gui/launcher.cpp:1171
 msgid "~L~oad..."
 msgstr ""
 
-#: gui/launcher.cpp:1148
+#: gui/launcher.cpp:1171
 msgid "Load saved game for selected game"
 msgstr ""
 
-#: gui/launcher.cpp:1150
+#: gui/launcher.cpp:1173
 msgid "Record..."
 msgstr ""
 
-#: gui/launcher.cpp:1156
+#: gui/launcher.cpp:1179
 msgid "~G~ame Options..."
 msgstr ""
 
-#: gui/launcher.cpp:1156
+#: gui/launcher.cpp:1179
 msgid "Change game options"
 msgstr ""
 
-#: gui/launcher.cpp:1156
+#: gui/launcher.cpp:1179
 msgctxt "lowres"
 msgid "~G~ame Opts..."
 msgstr ""
 
-#: gui/launcher.cpp:1242
+#: gui/launcher.cpp:1265
 msgid "Add-on"
 msgstr ""
 
 #. I18N: List grouping when no engine is specified
-#: gui/launcher.cpp:1309 gui/launcher.cpp:1554
+#: gui/launcher.cpp:1335 gui/launcher.cpp:1580
 msgid "Unknown Engine"
 msgstr ""
 
 #. I18N: List grouping when no publisher is specified
 #. I18N: List group when no publisher is specified
-#: gui/launcher.cpp:1326 gui/launcher.cpp:1571
+#: gui/launcher.cpp:1352 gui/launcher.cpp:1597
 msgid "Unknown Publisher"
 msgstr ""
 
 #. I18N: List group when no game series is specified
 #. I18N: List grouping when no game series is specified
-#: gui/launcher.cpp:1343 gui/launcher.cpp:1588
+#: gui/launcher.cpp:1369 gui/launcher.cpp:1614
 msgid "No Series"
 msgstr ""
 
 #. I18N: List group when no language is specified
-#: gui/launcher.cpp:1357 gui/launcher.cpp:1602
+#: gui/launcher.cpp:1383 gui/launcher.cpp:1628
 msgid "Language not detected"
 msgstr ""
 
 #. I18N: List group when no platform is specified
-#: gui/launcher.cpp:1371 gui/launcher.cpp:1616
+#: gui/launcher.cpp:1397 gui/launcher.cpp:1642
 msgid "Platform not detected"
 msgstr ""
 
 #. I18N: List group when no year is specified
-#: gui/launcher.cpp:1388 gui/launcher.cpp:1633
+#: gui/launcher.cpp:1414 gui/launcher.cpp:1659
 msgid "Unknown Year"
 msgstr ""
 
 #. I18N: Group for All items
-#: gui/launcher.cpp:1640 gui/widgets/groupedlist.cpp:82
+#: gui/launcher.cpp:1666 gui/widgets/groupedlist.cpp:82
 msgid "All"
 msgstr ""
 
-#: gui/launcher.cpp:1812
+#: gui/launcher.cpp:1844
 msgid "Icons per row:"
 msgstr ""
 
-#: gui/launcher.cpp:1869
+#: gui/launcher.cpp:1901
 #, c-format
 msgid ""
 "Do you really want to remove the following %d game configurations?\n"
@@ -2043,439 +2043,447 @@ msgctxt "lowres"
 msgid "Speech volume:"
 msgstr ""
 
-#: gui/options.cpp:2266
+#: gui/options.cpp:2268
 msgid "Control"
 msgstr ""
 
-#: gui/options.cpp:2379
+#: gui/options.cpp:2381
 msgctxt "lowres"
 msgid "Cloud"
 msgstr ""
 
-#: gui/options.cpp:2394
+#: gui/options.cpp:2396
 msgid "LAN"
 msgstr ""
 
-#: gui/options.cpp:2396
+#: gui/options.cpp:2398
 msgctxt "lowres"
 msgid "LAN"
 msgstr ""
 
-#: gui/options.cpp:2405
+#: gui/options.cpp:2407
 msgid "Accessibility"
 msgstr ""
 
-#: gui/options.cpp:2407
+#: gui/options.cpp:2409
 msgctxt "lowres"
 msgid "Accessibility"
 msgstr ""
 
-#: gui/options.cpp:2416
+#: gui/options.cpp:2418
 msgid "Discard changes and close the dialog"
 msgstr ""
 
-#: gui/options.cpp:2417
+#: gui/options.cpp:2419
 msgid "Apply"
 msgstr ""
 
-#: gui/options.cpp:2417
+#: gui/options.cpp:2419
 msgid "Apply changes without closing the dialog"
 msgstr ""
 
-#: gui/options.cpp:2418
+#: gui/options.cpp:2420
 msgid "Apply changes and close the dialog"
 msgstr ""
 
-#: gui/options.cpp:2508
+#: gui/options.cpp:2510
 msgid "FluidSynth Settings"
 msgstr ""
 
-#: gui/options.cpp:2522
+#: gui/options.cpp:2524
 msgid ""
 "Specifies where your saved games are put. A red coloring indicates the value "
 "is temporary and will not get saved"
 msgstr ""
 
-#: gui/options.cpp:2527
+#: gui/options.cpp:2529
 msgid "Theme Path:"
 msgstr ""
 
-#: gui/options.cpp:2529
+#: gui/options.cpp:2531
 msgctxt "lowres"
 msgid "Theme Path:"
 msgstr ""
 
-#: gui/options.cpp:2535
+#: gui/options.cpp:2537
 msgid "Icon Path:"
 msgstr ""
 
-#: gui/options.cpp:2537
+#: gui/options.cpp:2539
 msgctxt "lowres"
 msgid "Icon Path:"
 msgstr ""
 
-#: gui/options.cpp:2538 gui/options.cpp:2548
+#: gui/options.cpp:2540 gui/options.cpp:2550
 msgctxt "path"
 msgid "Default"
 msgstr ""
 
-#: gui/options.cpp:2545
+#: gui/options.cpp:2547
 msgid "DLC Path:"
 msgstr ""
 
-#: gui/options.cpp:2547
+#: gui/options.cpp:2549
 msgctxt "lowres"
 msgid "DLC Path:"
 msgstr ""
 
-#: gui/options.cpp:2555 gui/options.cpp:2557 gui/options.cpp:2558
+#: gui/options.cpp:2557 gui/options.cpp:2559 gui/options.cpp:2560
 msgid "Specifies path to additional data used by all games or ScummVM"
 msgstr ""
 
-#: gui/options.cpp:2564
+#: gui/options.cpp:2566
 msgid "Plugins Path:"
 msgstr ""
 
-#: gui/options.cpp:2566
+#: gui/options.cpp:2568
 msgctxt "lowres"
 msgid "Plugins Path:"
 msgstr ""
 
-#: gui/options.cpp:2576
+#: gui/options.cpp:2578
 msgid "ScummVM config path: "
 msgstr ""
 
-#: gui/options.cpp:2590
+#: gui/options.cpp:2592
 msgid "ScummVM log path: "
 msgstr ""
 
-#: gui/options.cpp:2595
+#: gui/options.cpp:2597
 msgid "View"
 msgstr ""
 
 #. I18N: Referring to the last path memorized when adding a game
-#: gui/options.cpp:2605 gui/options.cpp:3389
+#: gui/options.cpp:2607 gui/options.cpp:3402
 msgid "Last browser path: "
 msgstr ""
 
-#: gui/options.cpp:2610
+#: gui/options.cpp:2612
 msgid "Theme:"
 msgstr ""
 
-#: gui/options.cpp:2615
+#: gui/options.cpp:2617
 msgid "GUI scale:"
 msgstr ""
 
-#: gui/options.cpp:2632
+#: gui/options.cpp:2634
 #, c-format
 msgid "%d%% - Custom"
 msgstr ""
 
-#: gui/options.cpp:2636
+#: gui/options.cpp:2638
 msgid "GUI renderer:"
 msgstr ""
 
-#: gui/options.cpp:2649
+#: gui/options.cpp:2651
 msgid "Return to the launcher when leaving a game"
 msgstr ""
 
-#: gui/options.cpp:2650
+#: gui/options.cpp:2652
 msgid ""
 "Return to the launcher when leaving a game instead of closing ScummVM\n"
 "(this feature is not supported by all games)."
 msgstr ""
 
-#: gui/options.cpp:2657
+#: gui/options.cpp:2659
 msgid "Ask for confirmation on exit"
 msgstr ""
 
-#: gui/options.cpp:2658
+#: gui/options.cpp:2660
 msgid "Ask for permission when closing ScummVM or leaving a game."
 msgstr ""
 
-#: gui/options.cpp:2664
+#: gui/options.cpp:2666
 msgid "Disable fixed font scaling"
 msgstr ""
 
-#: gui/options.cpp:2665
+#: gui/options.cpp:2667
 msgid ""
 "Do not upscale fixed size fonts in the GUI. This reduces artefacts on low "
-"resolution screens"
+"resolution screens."
 msgstr ""
 
-#: gui/options.cpp:2671
+#: gui/options.cpp:2673
+msgid "Enable kinetic scrolling in lists"
+msgstr ""
+
+#: gui/options.cpp:2674
+msgid "Enable smooth, momentum-based scrolling in list widgets."
+msgstr ""
+
+#: gui/options.cpp:2680
 msgid "GUI language:"
 msgstr ""
 
-#: gui/options.cpp:2671
+#: gui/options.cpp:2680
 msgid "Language of ScummVM GUI"
 msgstr ""
 
-#: gui/options.cpp:2697
+#: gui/options.cpp:2706
 msgid "Switch the ScummVM GUI language to the game language"
 msgstr ""
 
-#: gui/options.cpp:2698
+#: gui/options.cpp:2707
 msgid ""
 "When starting a game, change the ScummVM GUI language to the game language. "
 "That way, if a game uses the ScummVM save and load dialogs, they are in the "
 "same language as the game."
 msgstr ""
 
-#: gui/options.cpp:2710
+#: gui/options.cpp:2719
 msgid "Use native system file browser"
 msgstr ""
 
-#: gui/options.cpp:2711
+#: gui/options.cpp:2720
 msgid ""
 "Use the native system file browser instead of the ScummVM one to select a "
 "file or directory."
 msgstr ""
 
-#: gui/options.cpp:2718
+#: gui/options.cpp:2727
 msgid "Download Icons"
 msgstr ""
 
-#: gui/options.cpp:2718
+#: gui/options.cpp:2727
 msgid "Check on the scummvm.org website for updates of icon packs"
 msgstr ""
 
-#: gui/options.cpp:2724
+#: gui/options.cpp:2733
 msgid "Autosave:"
 msgstr ""
 
-#: gui/options.cpp:2726
+#: gui/options.cpp:2735
 msgctxt "lowres"
 msgid "Autosave:"
 msgstr ""
 
-#: gui/options.cpp:2737
+#: gui/options.cpp:2746
 msgid "Random seed:"
 msgstr ""
 
-#: gui/options.cpp:2737
+#: gui/options.cpp:2746
 msgid "Seed for initializing all random number generators"
 msgstr ""
 
-#: gui/options.cpp:2745
+#: gui/options.cpp:2754
 msgid "Debug level:"
 msgstr ""
 
 #. I18N: Debug level -1, no messages
-#: gui/options.cpp:2749 gui/options.cpp:2886 audio/fmopl.cpp:83
+#: gui/options.cpp:2758 gui/options.cpp:2895 audio/fmopl.cpp:83
 msgid "None"
 msgstr ""
 
 #. I18N: Debug level 11, all messages
-#: gui/options.cpp:2755
+#: gui/options.cpp:2764
 msgid "11 (all)"
 msgstr ""
 
-#: gui/options.cpp:2759
+#: gui/options.cpp:2768
 msgid "Enable Discord integration"
 msgstr ""
 
-#: gui/options.cpp:2760
+#: gui/options.cpp:2769
 msgid ""
 "Show information about the games you are playing on Discord if the Discord "
 "client is running."
 msgstr ""
 
-#: gui/options.cpp:2770 gui/updates-dialog.cpp:87
+#: gui/options.cpp:2779 gui/updates-dialog.cpp:87
 msgid "Update check:"
 msgstr ""
 
-#: gui/options.cpp:2770
+#: gui/options.cpp:2779
 msgid "How often to check ScummVM updates"
 msgstr ""
 
-#: gui/options.cpp:2781
+#: gui/options.cpp:2790
 msgid "Check now"
 msgstr ""
 
-#: gui/options.cpp:2788
+#: gui/options.cpp:2797
 msgid "Active storage:"
 msgstr ""
 
-#: gui/options.cpp:2788
+#: gui/options.cpp:2797
 msgid "Active cloud storage"
 msgstr ""
 
-#: gui/options.cpp:2797
+#: gui/options.cpp:2806
 msgctxt "lowres"
 msgid ""
 "Storage is not yet enabled. Verify that username is correct and enable it:"
 msgstr ""
 
-#: gui/options.cpp:2799
+#: gui/options.cpp:2808
 msgid ""
 "Storage is not yet enabled. Verify that username is correct and enable it:"
 msgstr ""
 
-#: gui/options.cpp:2800
+#: gui/options.cpp:2809
 msgid "Enable storage"
 msgstr ""
 
-#: gui/options.cpp:2800
+#: gui/options.cpp:2809
 msgid "Confirm you want to use this account for this storage"
 msgstr ""
 
-#: gui/options.cpp:2802 backends/platform/wii/options.cpp:113
+#: gui/options.cpp:2811 backends/platform/wii/options.cpp:113
 msgid "Username:"
 msgstr ""
 
-#: gui/options.cpp:2802
+#: gui/options.cpp:2811
 msgid "Username used by this storage"
 msgstr ""
 
-#: gui/options.cpp:2803
+#: gui/options.cpp:2812
 msgid "<none>"
 msgstr ""
 
-#: gui/options.cpp:2805
+#: gui/options.cpp:2814
 msgid "Used space:"
 msgstr ""
 
-#: gui/options.cpp:2805
+#: gui/options.cpp:2814
 msgid "Space used by ScummVM's saved games on this storage"
 msgstr ""
 
-#: gui/options.cpp:2808
+#: gui/options.cpp:2817
 msgid "Last sync:"
 msgstr ""
 
-#: gui/options.cpp:2808
+#: gui/options.cpp:2817
 msgid "When was the last time saved games were synced with this storage"
 msgstr ""
 
-#: gui/options.cpp:2809 gui/options.cpp:3693
+#: gui/options.cpp:2818 gui/options.cpp:3706
 msgid "<never>"
 msgstr ""
 
-#: gui/options.cpp:2811
+#: gui/options.cpp:2820
 msgctxt "lowres"
 msgid "Saved games sync automatically on launch, after saving and on loading."
 msgstr ""
 
-#: gui/options.cpp:2813
+#: gui/options.cpp:2822
 msgid "Saved games sync automatically on launch, after saving and on loading."
 msgstr ""
 
-#: gui/options.cpp:2814
+#: gui/options.cpp:2823
 msgid "Sync now"
 msgstr ""
 
-#: gui/options.cpp:2814
+#: gui/options.cpp:2823
 msgid "Start saved games sync"
 msgstr ""
 
-#: gui/options.cpp:2817
+#: gui/options.cpp:2826
 msgctxt "lowres"
 msgid "You can download game files from your cloud ScummVM folder:"
 msgstr ""
 
-#: gui/options.cpp:2819
+#: gui/options.cpp:2828
 msgid "You can download game files from your cloud ScummVM folder:"
 msgstr ""
 
-#: gui/options.cpp:2820
+#: gui/options.cpp:2829
 msgid "Download game files"
 msgstr ""
 
-#: gui/options.cpp:2820
+#: gui/options.cpp:2829
 msgid "Open downloads manager dialog"
 msgstr ""
 
-#: gui/options.cpp:2823
+#: gui/options.cpp:2832
 msgctxt "lowres"
 msgid "To change account for this storage, disconnect and connect again:"
 msgstr ""
 
-#: gui/options.cpp:2825
+#: gui/options.cpp:2834
 msgid "To change account for this storage, disconnect and connect again:"
 msgstr ""
 
-#: gui/options.cpp:2826
+#: gui/options.cpp:2835
 msgid "Disconnect"
 msgstr ""
 
-#: gui/options.cpp:2826
+#: gui/options.cpp:2835
 msgid "Stop using this storage on this device"
 msgstr ""
 
-#: gui/options.cpp:2829
+#: gui/options.cpp:2838
 msgctxt "lowres"
 msgid "This storage is not connected yet!"
 msgstr ""
 
-#: gui/options.cpp:2831
+#: gui/options.cpp:2840
 msgid "This storage is not connected yet!"
 msgstr ""
 
-#: gui/options.cpp:2832
+#: gui/options.cpp:2841
 msgid "Connect"
 msgstr ""
 
-#: gui/options.cpp:2832
+#: gui/options.cpp:2841
 msgid "Connect your cloud storage account"
 msgstr ""
 
-#: gui/options.cpp:2845
+#: gui/options.cpp:2854
 msgctxt "lowres"
 msgid "/root/ Path:"
 msgstr ""
 
-#: gui/options.cpp:2845 gui/options.cpp:2847 gui/options.cpp:2848
+#: gui/options.cpp:2854 gui/options.cpp:2856 gui/options.cpp:2857
 msgid "Select which directory will be shown as /root/ in the Files Manager"
 msgstr ""
 
-#: gui/options.cpp:2847
+#: gui/options.cpp:2856
 msgid "/root/ Path:"
 msgstr ""
 
-#: gui/options.cpp:2853
+#: gui/options.cpp:2862
 msgid "Server's port:"
 msgstr ""
 
-#: gui/options.cpp:2853
+#: gui/options.cpp:2862
 msgid "Port for server to use"
 msgstr ""
 
-#: gui/options.cpp:2858
+#: gui/options.cpp:2867
 msgctxt "lowres"
 msgid "Run server to manage files with browser (in the same network)."
 msgstr ""
 
-#: gui/options.cpp:2859
+#: gui/options.cpp:2868
 msgctxt "lowres"
 msgid "Closing options dialog will stop the server."
 msgstr ""
 
-#: gui/options.cpp:2861
+#: gui/options.cpp:2870
 msgid "Run server to manage files with browser (in the same network)."
 msgstr ""
 
-#: gui/options.cpp:2862
+#: gui/options.cpp:2871
 msgid "Closing options dialog will stop the server."
 msgstr ""
 
-#: gui/options.cpp:2873
+#: gui/options.cpp:2882
 msgid "Use Text to speech"
 msgstr ""
 
-#: gui/options.cpp:2873
+#: gui/options.cpp:2882
 msgid "Will read text in gui on mouse over."
 msgstr ""
 
-#: gui/options.cpp:2954
+#: gui/options.cpp:2963
 msgid "Ignore"
 msgstr ""
 
-#: gui/options.cpp:2955
+#: gui/options.cpp:2964
 msgid "Disable autosave"
 msgstr ""
 
-#: gui/options.cpp:2956
+#: gui/options.cpp:2965
 msgid ""
 "WARNING: Autosave was enabled. Some of your games have existing saved games "
 "on the autosave slot. You can either move the existing saves to new slots, "
@@ -2484,94 +2492,94 @@ msgid ""
 "List of games:\n"
 msgstr ""
 
-#: gui/options.cpp:2965
+#: gui/options.cpp:2974
 msgid ""
 "\n"
 "And more..."
 msgstr ""
 
-#: gui/options.cpp:2966 engines/engine.cpp:647
+#: gui/options.cpp:2975 engines/engine.cpp:647
 #: engines/access/metaengine.cpp:298 engines/drascula/metaengine.cpp:255
 #: engines/queen/metaengine.cpp:269 engines/sherlock/metaengine.cpp:410
 #: engines/ultima/nuvie/metaengine.cpp:71
 msgid "Move"
 msgstr ""
 
-#: gui/options.cpp:2985
+#: gui/options.cpp:2994
 msgid "ERROR: Failed to move the following saved games:\n"
 msgstr ""
 
-#: gui/options.cpp:3081
+#: gui/options.cpp:3090
 msgid "Failed to change cloud storage!"
 msgstr ""
 
-#: gui/options.cpp:3084
+#: gui/options.cpp:3093
 msgid "Another cloud storage is already active."
 msgstr ""
 
-#: gui/options.cpp:3199
+#: gui/options.cpp:3212
 msgid "Theme does not support selected language!"
 msgstr ""
 
-#: gui/options.cpp:3202
+#: gui/options.cpp:3215
 msgid "Theme cannot be loaded!"
 msgstr ""
 
-#: gui/options.cpp:3205
+#: gui/options.cpp:3218
 msgid ""
 "\n"
 "Misc settings will be restored."
 msgstr ""
 
-#: gui/options.cpp:3277
+#: gui/options.cpp:3290
 msgid "Select directory for GUI themes"
 msgstr ""
 
-#: gui/options.cpp:3287
+#: gui/options.cpp:3300
 msgid "Select directory for GUI launcher thumbnails"
 msgstr ""
 
-#: gui/options.cpp:3298
+#: gui/options.cpp:3311
 msgid "Select directory for DLC downloads"
 msgstr ""
 
-#: gui/options.cpp:3309
+#: gui/options.cpp:3322
 msgid "Select directory for extra files"
 msgstr ""
 
-#: gui/options.cpp:3320
+#: gui/options.cpp:3333
 msgid "Select directory for plugins"
 msgstr ""
 
-#: gui/options.cpp:3333
+#: gui/options.cpp:3346
 msgid "Select directory for Files Manager /root/"
 msgstr ""
 
-#: gui/options.cpp:3485
+#: gui/options.cpp:3498
 msgid "This Storage is working now. Do you want to interrupt it?"
 msgstr ""
 
-#: gui/options.cpp:3494
+#: gui/options.cpp:3507
 msgid "Wait until current Storage finishes up and try again."
 msgstr ""
 
-#: gui/options.cpp:3691 backends/cloud/cloudmanager.cpp:157
+#: gui/options.cpp:3704 backends/cloud/cloudmanager.cpp:157
 msgid "<right now>"
 msgstr ""
 
-#: gui/options.cpp:3834
+#: gui/options.cpp:3847
 msgid ""
 "Request failed.\n"
 "Check your Internet connection."
 msgstr ""
 
-#: gui/options.cpp:3857
+#: gui/options.cpp:3870
 msgid ""
 "A test pattern should be displayed.\n"
 "Do you want to keep these shader scaler settings?"
 msgstr ""
 
-#: gui/options.cpp:3860
+#: gui/options.cpp:3873
 #, c-format
 msgid "Reverting automatically in %d seconds"
 msgstr ""
@@ -2656,7 +2664,7 @@ msgstr ""
 msgid "Playback"
 msgstr ""
 
-#: gui/recorderdialog.cpp:76 gui/widgets/grid.cpp:341
+#: gui/recorderdialog.cpp:76 gui/widgets/grid.cpp:343
 msgid "Edit"
 msgstr ""
 
@@ -2861,11 +2869,11 @@ msgstr ""
 msgid "Clear value"
 msgstr ""
 
-#: gui/widgets/grid.cpp:335
+#: gui/widgets/grid.cpp:337
 msgid "Play"
 msgstr ""
 
-#: gui/widgets/grid.cpp:338
+#: gui/widgets/grid.cpp:340
 msgid "Saves"
 msgstr ""
 



From noreply at scummvm.org  Sat Jun 13 13:53:28 2026
From: noreply at scummvm.org (ScummVM-Translations)
Date: Sat, 13 Jun 2026 13:53:28 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 abd5eddf096bdf350c886b265ff3156f192052d8
Message-ID: <20260613135328.E16F37005BC@ron.scummvm.net>

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

Summary:
abd5eddf09 I18N: Update translation files


Commit: abd5eddf096bdf350c886b265ff3156f192052d8
    https://github.com/scummvm/scummvm/commit/abd5eddf096bdf350c886b265ff3156f192052d8
Author: Weblate (noreply at weblate.org)
Date: 2026-06-13T13:53:10Z

Commit Message:
I18N: Update translation files

Updated by "Update PO files to match POT (msgmerge)" hook in Weblate.

Translation: ScummVM/scummvm
Translate-URL: https://translations.scummvm.org/projects/scummvm/scummvm/

Changed paths:
    po/ar.po
    po/be-tarask.po
    po/be_BY.po
    po/ca_ES.po
    po/cs_CZ.po
    po/da.po
    po/de_DE.po
    po/el.po
    po/es_ES.po
    po/eu.po
    po/fi_FI.po
    po/fr_FR.po
    po/gl_ES.po
    po/he.po
    po/hi.po
    po/hu_HU.po
    po/it_IT.po
    po/ja.po
    po/ka.po
    po/ko.po
    po/nb_NO.po
    po/nl_NL.po
    po/nn_NO.po
    po/pl_PL.po
    po/pt_BR.po
    po/pt_PT.po
    po/ro.po
    po/ru_RU.po
    po/sv_SE.po
    po/tr.po
    po/uk_UA.po
    po/zh.po
    po/zh_Hans.po
    po/zh_Hant.po


diff --git a/po/ar.po b/po/ar.po
index dd6e474bcd6..32d9f9b1ed7 100644
--- a/po/ar.po
+++ b/po/ar.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-13 10:20+0000\n"
+"POT-Creation-Date: 2026-06-13 13:52+0000\n"
 "PO-Revision-Date: 2026-02-24 22:26+0000\n"
 "Last-Translator: Mohamed Shaaban <mohmedbashamedo at hotmail.com>\n"
 "Language-Team: Arabic <https://translations.scummvm.org/projects/scummvm/"
@@ -124,7 +124,7 @@ msgstr "???????? ????????????"
 #: gui/downloadpacksdialog.cpp:465 gui/editgamedialog.cpp:340
 #: gui/editrecorddialog.cpp:66 gui/integrity-dialog.cpp:122
 #: gui/integrity-dialog.cpp:253 gui/filebrowser-dialog.cpp:68
-#: gui/fluidsynth-dialog.cpp:153 gui/massadd.cpp:93 gui/options.cpp:2416
+#: gui/fluidsynth-dialog.cpp:153 gui/massadd.cpp:93 gui/options.cpp:2418
 #: gui/predictivedialog.cpp:68 gui/printing-dialog.cpp:96
 #: gui/recorderdialog.cpp:72 gui/recorderdialog.cpp:211
 #: gui/remotebrowser.cpp:59 gui/saveload-dialog.cpp:64
@@ -197,23 +197,23 @@ msgid ""
 "so your browser can forward data to ScummVM"
 msgstr ""
 
-#: gui/cloudconnectionwizard.cpp:257 gui/options.cpp:3776
+#: gui/cloudconnectionwizard.cpp:257 gui/options.cpp:3789
 msgid "Stop server"
 msgstr "?????????? ????????????"
 
-#: gui/cloudconnectionwizard.cpp:257 gui/options.cpp:2840
+#: gui/cloudconnectionwizard.cpp:257 gui/options.cpp:2849
 msgid "Run server"
 msgstr "?????????? ????????????"
 
-#: gui/cloudconnectionwizard.cpp:258 gui/options.cpp:3777
+#: gui/cloudconnectionwizard.cpp:258 gui/options.cpp:3790
 msgid "Stop local webserver"
 msgstr "???????? ???????? ?????????? ????????????"
 
-#: gui/cloudconnectionwizard.cpp:258 gui/options.cpp:2840
+#: gui/cloudconnectionwizard.cpp:258 gui/options.cpp:2849
 msgid "Run local webserver"
 msgstr "???? ???????????? ???????? ?????????? ????????????"
 
-#: gui/cloudconnectionwizard.cpp:266 gui/options.cpp:2841 gui/options.cpp:3784
+#: gui/cloudconnectionwizard.cpp:266 gui/options.cpp:2850 gui/options.cpp:3797
 msgid "Not running"
 msgstr "???? ????????"
 
@@ -345,8 +345,8 @@ msgstr "?????????? ?????? ???????? ????????. ???? ???????? ????????????????"
 #: gui/cloudconnectionwizard.cpp:530 gui/downloaddialog.cpp:118
 #: gui/downloaddialog.cpp:152 gui/filebrowser-dialog.cpp:135
 #: gui/fluidsynth-dialog.cpp:210 gui/launcher.cpp:428 gui/launcher.cpp:457
-#: gui/launcher.cpp:534 gui/launcher.cpp:1938 gui/options.cpp:3485
-#: gui/options.cpp:3859 base/main.cpp:608
+#: gui/launcher.cpp:534 gui/launcher.cpp:1970 gui/options.cpp:3498
+#: gui/options.cpp:3872 base/main.cpp:608
 #: backends/events/default/default-events.cpp:193 engines/buried/buried.cpp:598
 #: engines/chamber/metaengine.cpp:91 engines/director/events.cpp:122
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:139
@@ -365,8 +365,8 @@ msgstr "??????"
 #: gui/cloudconnectionwizard.cpp:530 gui/downloaddialog.cpp:118
 #: gui/downloaddialog.cpp:153 gui/filebrowser-dialog.cpp:135
 #: gui/fluidsynth-dialog.cpp:210 gui/launcher.cpp:428 gui/launcher.cpp:457
-#: gui/launcher.cpp:534 gui/launcher.cpp:1939 gui/options.cpp:3485
-#: gui/options.cpp:3859 engines/buried/buried.cpp:598
+#: gui/launcher.cpp:534 gui/launcher.cpp:1971 gui/options.cpp:3498
+#: gui/options.cpp:3872 engines/buried/buried.cpp:598
 #: engines/chamber/metaengine.cpp:97 engines/director/events.cpp:122
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:139
 #: engines/efh/metaengine.cpp:368 engines/grim/grim.cpp:366
@@ -395,7 +395,7 @@ msgstr ""
 #: gui/editrecorddialog.cpp:67 gui/integrity-dialog.cpp:260
 #: gui/fluidsynth-dialog.cpp:154 gui/massadd.cpp:90 gui/message.cpp:156
 #: gui/message.cpp:160 gui/message.cpp:231 gui/message.cpp:270
-#: gui/message.cpp:274 gui/options.cpp:2418 gui/predictivedialog.cpp:69
+#: gui/message.cpp:274 gui/options.cpp:2420 gui/predictivedialog.cpp:69
 #: gui/saveload-dialog.cpp:1244 base/main.cpp:675 base/main.cpp:700
 #: dists/android.strings.xml.cpp:33 engines/engine.cpp:846
 #: backends/platform/wii/options.cpp:46 engines/buried/buried.cpp:622
@@ -590,11 +590,11 @@ msgstr ""
 msgid "Proceed"
 msgstr "????????"
 
-#: gui/dump-all-dialogs.cpp:123 gui/options.cpp:3347
+#: gui/dump-all-dialogs.cpp:123 gui/options.cpp:3360
 msgid "icon packs"
 msgstr "?????? ????????????"
 
-#: gui/dump-all-dialogs.cpp:127 gui/options.cpp:3358
+#: gui/dump-all-dialogs.cpp:127 gui/options.cpp:3371
 msgid "shader packs"
 msgstr "?????? ??????????"
 
@@ -606,11 +606,11 @@ msgstr "???????? ????????????:"
 msgid "Game"
 msgstr "????????????"
 
-#: gui/editgamedialog.cpp:164 gui/options.cpp:2075 gui/options.cpp:2254
+#: gui/editgamedialog.cpp:164 gui/options.cpp:2075 gui/options.cpp:2256
 msgid "GFX"
 msgstr "GFX"
 
-#: gui/editgamedialog.cpp:164 gui/options.cpp:2075 gui/options.cpp:2254
+#: gui/editgamedialog.cpp:164 gui/options.cpp:2075 gui/options.cpp:2256
 #: backends/graphics/atari/atari-graphics.cpp:1056
 #: backends/graphics/sdl/sdl-graphics.cpp:487
 msgid "Graphics"
@@ -625,11 +625,11 @@ msgctxt "lowres"
 msgid "Override global graphic settings"
 msgstr "?????????? ?????????????? ???????????? ????????????"
 
-#: gui/editgamedialog.cpp:185 gui/options.cpp:2288 engines/dialogs.cpp:354
+#: gui/editgamedialog.cpp:185 gui/options.cpp:2290 engines/dialogs.cpp:354
 msgid "Keymaps"
 msgstr "?????????? ????????????????"
 
-#: gui/editgamedialog.cpp:197 gui/options.cpp:2300 engines/dialogs.cpp:366
+#: gui/editgamedialog.cpp:197 gui/options.cpp:2302 engines/dialogs.cpp:366
 msgid "Backend"
 msgstr "??????????????"
 
@@ -642,7 +642,7 @@ msgctxt "lowres"
 msgid "Override global backend settings"
 msgstr "?????????? ?????????????? ?????????????? ????????????"
 
-#: gui/editgamedialog.cpp:220 gui/options.cpp:2318 engines/dialogs.cpp:329
+#: gui/editgamedialog.cpp:220 gui/options.cpp:2320 engines/dialogs.cpp:329
 msgid "Audio"
 msgstr "??????????"
 
@@ -655,11 +655,11 @@ msgctxt "lowres"
 msgid "Override global audio settings"
 msgstr "?????????? ?????????????? ?????????? ????????????"
 
-#: gui/editgamedialog.cpp:234 gui/options.cpp:2323
+#: gui/editgamedialog.cpp:234 gui/options.cpp:2325
 msgid "Volume"
 msgstr "?????????? ??????????"
 
-#: gui/editgamedialog.cpp:236 gui/options.cpp:2325
+#: gui/editgamedialog.cpp:236 gui/options.cpp:2327
 msgctxt "lowres"
 msgid "Volume"
 msgstr "?????????? ??????????"
@@ -673,7 +673,7 @@ msgctxt "lowres"
 msgid "Override global volume settings"
 msgstr "?????????? ?????????????? ?????????? ?????????? ????????????"
 
-#: gui/editgamedialog.cpp:252 gui/options.cpp:2333
+#: gui/editgamedialog.cpp:252 gui/options.cpp:2335
 msgid "MIDI"
 msgstr "MIDI"
 
@@ -686,7 +686,7 @@ msgctxt "lowres"
 msgid "Override global MIDI settings"
 msgstr "?????????? ?????????????? MIDI ????????????"
 
-#: gui/editgamedialog.cpp:267 gui/options.cpp:2339
+#: gui/editgamedialog.cpp:267 gui/options.cpp:2341
 msgid "MT-32"
 msgstr "MT-32"
 
@@ -699,11 +699,11 @@ msgctxt "lowres"
 msgid "Override global MT-32 settings"
 msgstr "?????????? ?????????????? MT-32 ????????????"
 
-#: gui/editgamedialog.cpp:281 gui/options.cpp:2346
+#: gui/editgamedialog.cpp:281 gui/options.cpp:2348
 msgid "Paths"
 msgstr "??????????"
 
-#: gui/editgamedialog.cpp:283 gui/options.cpp:2348
+#: gui/editgamedialog.cpp:283 gui/options.cpp:2350
 msgctxt "lowres"
 msgid "Paths"
 msgstr "??????????"
@@ -725,7 +725,7 @@ msgctxt "lowres"
 msgid "Game Path:"
 msgstr "???????? ????????????:"
 
-#: gui/editgamedialog.cpp:303 gui/options.cpp:2555
+#: gui/editgamedialog.cpp:303 gui/options.cpp:2557
 msgid "Extra Path:"
 msgstr "???????? ??????????:"
 
@@ -734,33 +734,33 @@ msgstr "???????? ??????????:"
 msgid "Specifies path to additional data used by the game"
 msgstr "???????? ???????? ???????????????? ???????????????? ???????? ???????????????? ????????????"
 
-#: gui/editgamedialog.cpp:305 gui/options.cpp:2557
+#: gui/editgamedialog.cpp:305 gui/options.cpp:2559
 msgctxt "lowres"
 msgid "Extra Path:"
 msgstr "???????? ??????????:"
 
-#: gui/editgamedialog.cpp:306 gui/options.cpp:2530 gui/options.cpp:2558
-#: gui/options.cpp:2567 gui/options.cpp:2848
+#: gui/editgamedialog.cpp:306 gui/options.cpp:2532 gui/options.cpp:2560
+#: gui/options.cpp:2569 gui/options.cpp:2857
 #: backends/platform/libretro/src/libretro-options-widget.cpp:277
 msgctxt "path"
 msgid "None"
 msgstr "???? ????"
 
-#: gui/editgamedialog.cpp:312 gui/options.cpp:2519
+#: gui/editgamedialog.cpp:312 gui/options.cpp:2521
 msgid "Save Path:"
 msgstr "?????? ????????:"
 
 #: gui/editgamedialog.cpp:312 gui/editgamedialog.cpp:314
-#: gui/editgamedialog.cpp:315 gui/options.cpp:2519 gui/options.cpp:2521
+#: gui/editgamedialog.cpp:315 gui/options.cpp:2521 gui/options.cpp:2523
 msgid "Specifies where your saved games are put"
 msgstr "???????? ???????? ?????? ?????????????? ????????????????"
 
-#: gui/editgamedialog.cpp:314 gui/options.cpp:2521
+#: gui/editgamedialog.cpp:314 gui/options.cpp:2523
 msgctxt "lowres"
 msgid "Save Path:"
 msgstr "?????? ????????:"
 
-#: gui/editgamedialog.cpp:315 gui/options.cpp:1844 gui/options.cpp:2522
+#: gui/editgamedialog.cpp:315 gui/options.cpp:1844 gui/options.cpp:2524
 #: backends/platform/wii/options.cpp:55
 msgid "Default"
 msgstr "??????????????????"
@@ -820,8 +820,8 @@ msgstr ""
 #: gui/editgamedialog.cpp:365 gui/editgamedialog.cpp:380 gui/options.cpp:1613
 #: gui/options.cpp:1627 gui/options.cpp:1644 gui/options.cpp:1658
 #: gui/options.cpp:1675 gui/options.cpp:1713 gui/options.cpp:1729
-#: gui/options.cpp:2144 gui/options.cpp:2600 gui/options.cpp:2674
-#: gui/options.cpp:2888 gui/options.cpp:3389 audio/null.cpp:45
+#: gui/options.cpp:2144 gui/options.cpp:2602 gui/options.cpp:2683
+#: gui/options.cpp:2897 gui/options.cpp:3402 audio/null.cpp:45
 #: backends/platform/android/options.cpp:157
 #: backends/platform/android/options.cpp:158
 #: backends/platform/android/options.cpp:186
@@ -848,7 +848,7 @@ msgctxt "lowres"
 msgid "Platform:"
 msgstr "????????????:"
 
-#: gui/editgamedialog.cpp:566 gui/options.cpp:3413
+#: gui/editgamedialog.cpp:566 gui/options.cpp:3426
 msgid "Select SoundFont"
 msgstr "?????? SoundFont"
 
@@ -856,11 +856,11 @@ msgstr "?????? SoundFont"
 msgid "Select additional game directory"
 msgstr "?????? ???????? ?????????? ??????????????"
 
-#: gui/editgamedialog.cpp:618 gui/options.cpp:3261
+#: gui/editgamedialog.cpp:618 gui/options.cpp:3274
 msgid "Select directory for saved games"
 msgstr "?????? ???????? ?????????????? ????????????????"
 
-#: gui/editgamedialog.cpp:625 gui/options.cpp:3268
+#: gui/editgamedialog.cpp:625 gui/options.cpp:3281
 msgid "The chosen directory cannot be written to. Please select another one."
 msgstr "???? ???????? ?????????????? ?????? ???????????? ??????????????. ???????????? ?????????? ???????? ??????."
 
@@ -1001,7 +1001,7 @@ msgstr "??????"
 msgid "Triangle"
 msgstr "????????????"
 
-#: gui/fluidsynth-dialog.cpp:139 gui/options.cpp:2366
+#: gui/fluidsynth-dialog.cpp:139 gui/options.cpp:2368
 msgid "Misc"
 msgstr "???? ?????????? ????????????"
 
@@ -1039,7 +1039,7 @@ msgid ""
 "Do you really want to reset all FluidSynth settings to their default values?"
 msgstr "???? ???????? ???????? ?????????? ?????????? ???????? ?????????????? FluidSynth ?????? ?????????? ??????????????????????"
 
-#: gui/gui-manager.cpp:159 gui/options.cpp:2357
+#: gui/gui-manager.cpp:159 gui/options.cpp:2359
 msgid "GUI"
 msgstr "??????????????"
 
@@ -1254,7 +1254,7 @@ msgid ""
 "and illegal game copies.\n"
 msgstr ""
 
-#: gui/helpdialog.cpp:68 gui/options.cpp:2377
+#: gui/helpdialog.cpp:68 gui/options.cpp:2379
 msgid "Cloud"
 msgstr "????????"
 
@@ -1549,87 +1549,87 @@ msgstr "?????? ??????????????"
 msgid "Grid view"
 msgstr "?????? ????????"
 
-#: gui/launcher.cpp:1145
+#: gui/launcher.cpp:1168
 msgid "~S~tart"
 msgstr "~??~??????"
 
-#: gui/launcher.cpp:1145
+#: gui/launcher.cpp:1168
 msgid "Start selected game"
 msgstr "???????? ???????????? ????????????????"
 
-#: gui/launcher.cpp:1148
+#: gui/launcher.cpp:1171
 msgid "~L~oad..."
 msgstr "~??~????..."
 
-#: gui/launcher.cpp:1148
+#: gui/launcher.cpp:1171
 msgid "Load saved game for selected game"
 msgstr "?????????? ???????????? ???????????????? ?????????? ??????????????"
 
-#: gui/launcher.cpp:1150
+#: gui/launcher.cpp:1173
 msgid "Record..."
 msgstr "????????????..."
 
-#: gui/launcher.cpp:1156
+#: gui/launcher.cpp:1179
 msgid "~G~ame Options..."
 msgstr "~??~?????????? ????????????..."
 
-#: gui/launcher.cpp:1156
+#: gui/launcher.cpp:1179
 msgid "Change game options"
 msgstr "?????????? ?????????????? ????????????"
 
-#: gui/launcher.cpp:1156
+#: gui/launcher.cpp:1179
 msgctxt "lowres"
 msgid "~G~ame Opts..."
 msgstr "~??~?????????? ????????????..."
 
-#: gui/launcher.cpp:1242
+#: gui/launcher.cpp:1265
 msgid "Add-on"
 msgstr ""
 
 #. I18N: List grouping when no engine is specified
-#: gui/launcher.cpp:1309 gui/launcher.cpp:1554
+#: gui/launcher.cpp:1335 gui/launcher.cpp:1580
 msgid "Unknown Engine"
 msgstr "???????? ?????? ??????????"
 
 #. I18N: List grouping when no publisher is specified
 #. I18N: List group when no publisher is specified
-#: gui/launcher.cpp:1326 gui/launcher.cpp:1571
+#: gui/launcher.cpp:1352 gui/launcher.cpp:1597
 msgid "Unknown Publisher"
 msgstr "???????? ?????? ??????????"
 
 #. I18N: List group when no game series is specified
 #. I18N: List grouping when no game series is specified
-#: gui/launcher.cpp:1343 gui/launcher.cpp:1588
+#: gui/launcher.cpp:1369 gui/launcher.cpp:1614
 msgid "No Series"
 msgstr "???? ???????? ??????????"
 
 #. I18N: List group when no language is specified
-#: gui/launcher.cpp:1357 gui/launcher.cpp:1602
+#: gui/launcher.cpp:1383 gui/launcher.cpp:1628
 msgid "Language not detected"
 msgstr "???? ?????? ?????????? ???? ??????????"
 
 #. I18N: List group when no platform is specified
-#: gui/launcher.cpp:1371 gui/launcher.cpp:1616
+#: gui/launcher.cpp:1397 gui/launcher.cpp:1642
 msgid "Platform not detected"
 msgstr "???????????? ?????????????? ?????? ??????????"
 
 #. I18N: List group when no year is specified
-#: gui/launcher.cpp:1388 gui/launcher.cpp:1633
+#: gui/launcher.cpp:1414 gui/launcher.cpp:1659
 #, fuzzy
 #| msgid "Unknown error"
 msgid "Unknown Year"
 msgstr "?????? ?????? ??????????"
 
 #. I18N: Group for All items
-#: gui/launcher.cpp:1640 gui/widgets/groupedlist.cpp:82
+#: gui/launcher.cpp:1666 gui/widgets/groupedlist.cpp:82
 msgid "All"
 msgstr "????????????"
 
-#: gui/launcher.cpp:1812
+#: gui/launcher.cpp:1844
 msgid "Icons per row:"
 msgstr "???????????? ?????? ????:"
 
-#: gui/launcher.cpp:1869
+#: gui/launcher.cpp:1901
 #, fuzzy, c-format
 #| msgid ""
 #| "Do you really want to remove the following %d game configuration(s)?\n"
@@ -2133,54 +2133,54 @@ msgctxt "lowres"
 msgid "Speech volume:"
 msgstr "?????? ?????????? ????????????:"
 
-#: gui/options.cpp:2266
+#: gui/options.cpp:2268
 msgid "Control"
 msgstr "????????"
 
-#: gui/options.cpp:2379
+#: gui/options.cpp:2381
 msgctxt "lowres"
 msgid "Cloud"
 msgstr "????????"
 
-#: gui/options.cpp:2394
+#: gui/options.cpp:2396
 msgid "LAN"
 msgstr "LAN"
 
-#: gui/options.cpp:2396
+#: gui/options.cpp:2398
 msgctxt "lowres"
 msgid "LAN"
 msgstr "LAN"
 
-#: gui/options.cpp:2405
+#: gui/options.cpp:2407
 msgid "Accessibility"
 msgstr "?????????? ????????????"
 
-#: gui/options.cpp:2407
+#: gui/options.cpp:2409
 msgctxt "lowres"
 msgid "Accessibility"
 msgstr "?????????? ????????????"
 
-#: gui/options.cpp:2416
+#: gui/options.cpp:2418
 msgid "Discard changes and close the dialog"
 msgstr "?????????? ?????????????????? ?????????? ???????? ????????????"
 
-#: gui/options.cpp:2417
+#: gui/options.cpp:2419
 msgid "Apply"
 msgstr "????????"
 
-#: gui/options.cpp:2417
+#: gui/options.cpp:2419
 msgid "Apply changes without closing the dialog"
 msgstr "?????? ?????????????????? ?????? ?????????? ???????? ????????????"
 
-#: gui/options.cpp:2418
+#: gui/options.cpp:2420
 msgid "Apply changes and close the dialog"
 msgstr "???? ???????????? ?????????????????? ?????????? ???????? ????????????"
 
-#: gui/options.cpp:2508
+#: gui/options.cpp:2510
 msgid "FluidSynth Settings"
 msgstr "?????????????? FluidSynth"
 
-#: gui/options.cpp:2522
+#: gui/options.cpp:2524
 msgid ""
 "Specifies where your saved games are put. A red coloring indicates the value "
 "is temporary and will not get saved"
@@ -2188,94 +2188,94 @@ msgstr ""
 "???????? ???????? ?????? ?????????????? ????????????????. ???????? ?????????? ???????????? ?????? ???? ???????????? ?????????? ?????? "
 "?????? ??????????"
 
-#: gui/options.cpp:2527
+#: gui/options.cpp:2529
 msgid "Theme Path:"
 msgstr "???????? ??????????????:"
 
-#: gui/options.cpp:2529
+#: gui/options.cpp:2531
 msgctxt "lowres"
 msgid "Theme Path:"
 msgstr "???????? ??????????????:"
 
-#: gui/options.cpp:2535
+#: gui/options.cpp:2537
 msgid "Icon Path:"
 msgstr "???????? ??????????:"
 
-#: gui/options.cpp:2537
+#: gui/options.cpp:2539
 msgctxt "lowres"
 msgid "Icon Path:"
 msgstr "???????? ??????????:"
 
-#: gui/options.cpp:2538 gui/options.cpp:2548
+#: gui/options.cpp:2540 gui/options.cpp:2550
 msgctxt "path"
 msgid "Default"
 msgstr "??????????????????"
 
-#: gui/options.cpp:2545
+#: gui/options.cpp:2547
 #, fuzzy
 #| msgid "Game Path:"
 msgid "DLC Path:"
 msgstr "???????? ????????????:"
 
-#: gui/options.cpp:2547
+#: gui/options.cpp:2549
 #, fuzzy
 #| msgid "Game Path:"
 msgctxt "lowres"
 msgid "DLC Path:"
 msgstr "???????? ????????????:"
 
-#: gui/options.cpp:2555 gui/options.cpp:2557 gui/options.cpp:2558
+#: gui/options.cpp:2557 gui/options.cpp:2559 gui/options.cpp:2560
 msgid "Specifies path to additional data used by all games or ScummVM"
 msgstr "???????? ???????? ???????????????? ???????????????? ???????? ???????????????? ???????? ?????????????? ???? ScummVM"
 
-#: gui/options.cpp:2564
+#: gui/options.cpp:2566
 msgid "Plugins Path:"
 msgstr "???????? ????????????????:"
 
-#: gui/options.cpp:2566
+#: gui/options.cpp:2568
 msgctxt "lowres"
 msgid "Plugins Path:"
 msgstr "???????? ????????????????:"
 
-#: gui/options.cpp:2576
+#: gui/options.cpp:2578
 msgid "ScummVM config path: "
 msgstr "???????? ?????????? ScummVM: "
 
-#: gui/options.cpp:2590
+#: gui/options.cpp:2592
 msgid "ScummVM log path: "
 msgstr "???????? ?????? ScummVM: "
 
-#: gui/options.cpp:2595
+#: gui/options.cpp:2597
 msgid "View"
 msgstr "????????"
 
 #. I18N: Referring to the last path memorized when adding a game
-#: gui/options.cpp:2605 gui/options.cpp:3389
+#: gui/options.cpp:2607 gui/options.cpp:3402
 msgid "Last browser path: "
 msgstr "?????? ???????? ??????????????: "
 
-#: gui/options.cpp:2610
+#: gui/options.cpp:2612
 msgid "Theme:"
 msgstr "???????????? ????????????????:"
 
-#: gui/options.cpp:2615
+#: gui/options.cpp:2617
 msgid "GUI scale:"
 msgstr "?????????? GUI:"
 
-#: gui/options.cpp:2632
+#: gui/options.cpp:2634
 #, c-format
 msgid "%d%% - Custom"
 msgstr ""
 
-#: gui/options.cpp:2636
+#: gui/options.cpp:2638
 msgid "GUI renderer:"
 msgstr "????????GUI:"
 
-#: gui/options.cpp:2649
+#: gui/options.cpp:2651
 msgid "Return to the launcher when leaving a game"
 msgstr "???????? ?????? ?????????????? ?????? ???????????? ????????????"
 
-#: gui/options.cpp:2650
+#: gui/options.cpp:2652
 msgid ""
 "Return to the launcher when leaving a game instead of closing ScummVM\n"
 "(this feature is not supported by all games)."
@@ -2283,39 +2283,51 @@ msgstr ""
 "???????? ?????? ???????????? ?????? ???????????? ???????? ?????????? ???? ?????????? ScummVM\n"
 "(?????? ???????????? ???? ???????? ???????? ??????????????)."
 
-#: gui/options.cpp:2657
+#: gui/options.cpp:2659
 msgid "Ask for confirmation on exit"
 msgstr "???????? ?????????????? ?????? ????????????"
 
-#: gui/options.cpp:2658
+#: gui/options.cpp:2660
 msgid "Ask for permission when closing ScummVM or leaving a game."
 msgstr "???????? ?????????? ?????? ?????????? ScummVM ???? ???????????? ????????????."
 
-#: gui/options.cpp:2664
+#: gui/options.cpp:2666
 #, fuzzy
 #| msgid "Disable falling"
 msgid "Disable fixed font scaling"
 msgstr "?????????? ????????????"
 
-#: gui/options.cpp:2665
+#: gui/options.cpp:2667
 msgid ""
 "Do not upscale fixed size fonts in the GUI. This reduces artefacts on low "
-"resolution screens"
+"resolution screens."
 msgstr ""
 
-#: gui/options.cpp:2671
+#: gui/options.cpp:2673
+#, fuzzy
+#| msgid "Enable smooth scrolling when walking"
+msgid "Enable kinetic scrolling in lists"
+msgstr "?????????? ?????????????? ?????????? ?????? ??????????"
+
+#: gui/options.cpp:2674
+#, fuzzy
+#| msgid "Enable smooth scrolling when walking"
+msgid "Enable smooth, momentum-based scrolling in list widgets."
+msgstr "?????????? ?????????????? ?????????? ?????? ??????????"
+
+#: gui/options.cpp:2680
 msgid "GUI language:"
 msgstr "?????? GUI:"
 
-#: gui/options.cpp:2671
+#: gui/options.cpp:2680
 msgid "Language of ScummVM GUI"
 msgstr "?????? ScummVM GUI"
 
-#: gui/options.cpp:2697
+#: gui/options.cpp:2706
 msgid "Switch the ScummVM GUI language to the game language"
 msgstr "???? ???????????? ?????? ScummVM GUI ?????? ?????? ????????????"
 
-#: gui/options.cpp:2698
+#: gui/options.cpp:2707
 msgid ""
 "When starting a game, change the ScummVM GUI language to the game language. "
 "That way, if a game uses the ScummVM save and load dialogs, they are in the "
@@ -2325,65 +2337,65 @@ msgstr ""
 "?????? ???????? ???????? ?????????????? ???????????? ???????????? ?????? ???????????? ScummVM ?? ???????????? ???????? ?????? "
 "????????????."
 
-#: gui/options.cpp:2710
+#: gui/options.cpp:2719
 msgid "Use native system file browser"
 msgstr "?????????? ?????????? ?????????? ???????????? ????????????"
 
-#: gui/options.cpp:2711
+#: gui/options.cpp:2720
 msgid ""
 "Use the native system file browser instead of the ScummVM one to select a "
 "file or directory."
 msgstr ""
 "???????????? ?????????? ?????????? ???????????? ???????????? ?????????? ???? ?????????? ScummVM ???????????? ?????? ???? ????????."
 
-#: gui/options.cpp:2718
+#: gui/options.cpp:2727
 #, fuzzy
 #| msgid "Download"
 msgid "Download Icons"
 msgstr "??????????"
 
-#: gui/options.cpp:2718
+#: gui/options.cpp:2727
 #, fuzzy
 #| msgid "Check for updates of icon packs"
 msgid "Check on the scummvm.org website for updates of icon packs"
 msgstr "???????? ???? ???????? ?????????????? ???????? ????????????"
 
-#: gui/options.cpp:2724
+#: gui/options.cpp:2733
 msgid "Autosave:"
 msgstr "?????????? ????????????????:"
 
-#: gui/options.cpp:2726
+#: gui/options.cpp:2735
 msgctxt "lowres"
 msgid "Autosave:"
 msgstr "?????????? ????????????????:"
 
-#: gui/options.cpp:2737
+#: gui/options.cpp:2746
 msgid "Random seed:"
 msgstr "???????????? ????????????:"
 
-#: gui/options.cpp:2737
+#: gui/options.cpp:2746
 msgid "Seed for initializing all random number generators"
 msgstr "???????????? ???????????? ???????? ???????????? ?????????????? ??????????????????"
 
-#: gui/options.cpp:2745
+#: gui/options.cpp:2754
 msgid "Debug level:"
 msgstr "?????????? ??????????????:"
 
 #. I18N: Debug level -1, no messages
-#: gui/options.cpp:2749 gui/options.cpp:2886 audio/fmopl.cpp:83
+#: gui/options.cpp:2758 gui/options.cpp:2895 audio/fmopl.cpp:83
 msgid "None"
 msgstr "???? ????"
 
 #. I18N: Debug level 11, all messages
-#: gui/options.cpp:2755
+#: gui/options.cpp:2764
 msgid "11 (all)"
 msgstr "11 (????????)"
 
-#: gui/options.cpp:2759
+#: gui/options.cpp:2768
 msgid "Enable Discord integration"
 msgstr "?????????? ?????????? Discord"
 
-#: gui/options.cpp:2760
+#: gui/options.cpp:2769
 msgid ""
 "Show information about the games you are playing on Discord if the Discord "
 "client is running."
@@ -2391,27 +2403,27 @@ msgstr ""
 "???????? ?????????????? ?????? ?????????????? ???????? ???????????? ?????? Discord ?????? ?????? ???????? Discord ?????? "
 "??????????????."
 
-#: gui/options.cpp:2770 gui/updates-dialog.cpp:87
+#: gui/options.cpp:2779 gui/updates-dialog.cpp:87
 msgid "Update check:"
 msgstr "???????????? ???? ??????????????:"
 
-#: gui/options.cpp:2770
+#: gui/options.cpp:2779
 msgid "How often to check ScummVM updates"
 msgstr "???? ?????? ???????????? ???? ?????????????? ScummVM"
 
-#: gui/options.cpp:2781
+#: gui/options.cpp:2790
 msgid "Check now"
 msgstr "???????? ????????"
 
-#: gui/options.cpp:2788
+#: gui/options.cpp:2797
 msgid "Active storage:"
 msgstr "?????????????? ????????????:"
 
-#: gui/options.cpp:2788
+#: gui/options.cpp:2797
 msgid "Active cloud storage"
 msgstr "?????????????? ?????????????? ????????????"
 
-#: gui/options.cpp:2797
+#: gui/options.cpp:2806
 #, fuzzy
 #| msgctxt "lowres"
 #| msgid ""
@@ -2422,7 +2434,7 @@ msgid ""
 "Storage is not yet enabled. Verify that username is correct and enable it:"
 msgstr "4. ???? ?????? ?????????? ?????????????? ??????. ???????? ???? ?????? ?????? ???????????????? ?????? ??????????????:"
 
-#: gui/options.cpp:2799
+#: gui/options.cpp:2808
 #, fuzzy
 #| msgid ""
 #| "4. Storage is not yet enabled. Verify that username is correct and enable "
@@ -2431,98 +2443,98 @@ msgid ""
 "Storage is not yet enabled. Verify that username is correct and enable it:"
 msgstr "4. ???? ?????? ?????????? ?????????????? ??????. ???????? ???? ?????? ?????? ???????????????? ?????? ??????????????:"
 
-#: gui/options.cpp:2800
+#: gui/options.cpp:2809
 msgid "Enable storage"
 msgstr "?????????? ??????????????"
 
-#: gui/options.cpp:2800
+#: gui/options.cpp:2809
 msgid "Confirm you want to use this account for this storage"
 msgstr "?????? ?????? ???????? ?????????????? ?????? ???????????? ???????? ??????????????"
 
-#: gui/options.cpp:2802 backends/platform/wii/options.cpp:113
+#: gui/options.cpp:2811 backends/platform/wii/options.cpp:113
 msgid "Username:"
 msgstr "?????? ????????????????:"
 
-#: gui/options.cpp:2802
+#: gui/options.cpp:2811
 msgid "Username used by this storage"
 msgstr "?????? ???????????????? ???????????? ???????????? ?????? ??????????????"
 
-#: gui/options.cpp:2803
+#: gui/options.cpp:2812
 msgid "<none>"
 msgstr "<???? ??????>"
 
-#: gui/options.cpp:2805
+#: gui/options.cpp:2814
 msgid "Used space:"
 msgstr "?????????????? ??????????????????:"
 
-#: gui/options.cpp:2805
+#: gui/options.cpp:2814
 msgid "Space used by ScummVM's saved games on this storage"
 msgstr "?????????????? ?????????????????? ???????????? ?????????? ScummVM ???????????????? ?????? ???????? ?????????????? ??????"
 
-#: gui/options.cpp:2808
+#: gui/options.cpp:2817
 msgid "Last sync:"
 msgstr "?????? ????????????:"
 
-#: gui/options.cpp:2808
+#: gui/options.cpp:2817
 msgid "When was the last time saved games were synced with this storage"
 msgstr "?????? ???????? ?????? ?????? ?????? ???????? ???????????? ?????????????? ???????????????? ???? ???????? ?????????????? ??????"
 
-#: gui/options.cpp:2809 gui/options.cpp:3693
+#: gui/options.cpp:2818 gui/options.cpp:3706
 msgid "<never>"
 msgstr "<????????>"
 
-#: gui/options.cpp:2811
+#: gui/options.cpp:2820
 msgctxt "lowres"
 msgid "Saved games sync automatically on launch, after saving and on loading."
 msgstr "?????? ???????????? ?????????????? ???????????????? ???????????????? ?????? ?????????????? ???????? ?????????? ????????????????."
 
-#: gui/options.cpp:2813
+#: gui/options.cpp:2822
 msgid "Saved games sync automatically on launch, after saving and on loading."
 msgstr "?????? ???????????? ?????????????? ???????????????? ???????????????? ?????? ?????????????? ???????? ?????????? ????????????????."
 
-#: gui/options.cpp:2814
+#: gui/options.cpp:2823
 msgid "Sync now"
 msgstr "???????? ????????"
 
-#: gui/options.cpp:2814
+#: gui/options.cpp:2823
 msgid "Start saved games sync"
 msgstr "???????? ???????????? ?????????????? ????????????????"
 
-#: gui/options.cpp:2817
+#: gui/options.cpp:2826
 msgctxt "lowres"
 msgid "You can download game files from your cloud ScummVM folder:"
 msgstr "?????????? ?????????? ?????????? ???????????? ???? ???????? ScummVM ?????? ??????????????:"
 
-#: gui/options.cpp:2819
+#: gui/options.cpp:2828
 msgid "You can download game files from your cloud ScummVM folder:"
 msgstr "?????????? ?????????? ?????????? ???????????? ???? ???????? ScummVM ?????? ??????????????:"
 
-#: gui/options.cpp:2820
+#: gui/options.cpp:2829
 msgid "Download game files"
 msgstr "?????????? ?????????? ????????????"
 
-#: gui/options.cpp:2820
+#: gui/options.cpp:2829
 msgid "Open downloads manager dialog"
 msgstr "???????? ???????? ???????? ???????? ??????????????????"
 
-#: gui/options.cpp:2823
+#: gui/options.cpp:2832
 msgctxt "lowres"
 msgid "To change account for this storage, disconnect and connect again:"
 msgstr "???????????? ???????? ???????? ?????????????? ?????? ?? ???????? ?????????????? ???? ???????? ?????? ????????:"
 
-#: gui/options.cpp:2825
+#: gui/options.cpp:2834
 msgid "To change account for this storage, disconnect and connect again:"
 msgstr "???????????? ???????? ???????? ?????????????? ?????? ?? ???????? ?????????????? ???? ???????? ?????? ????????:"
 
-#: gui/options.cpp:2826
+#: gui/options.cpp:2835
 msgid "Disconnect"
 msgstr "?????? ??????????????"
 
-#: gui/options.cpp:2826
+#: gui/options.cpp:2835
 msgid "Stop using this storage on this device"
 msgstr "???????? ???? ?????????????? ?????? ?????????????? ?????? ?????? ????????????"
 
-#: gui/options.cpp:2829
+#: gui/options.cpp:2838
 #, fuzzy
 #| msgctxt "lowres"
 #| msgid "This storage is not connected yet! To connect,"
@@ -2530,78 +2542,78 @@ msgctxt "lowres"
 msgid "This storage is not connected yet!"
 msgstr "?????? ?????????????? ?????? ???????? ??????! ????????????????"
 
-#: gui/options.cpp:2831
+#: gui/options.cpp:2840
 #, fuzzy
 #| msgid "This storage is not connected yet! To connect,"
 msgid "This storage is not connected yet!"
 msgstr "?????? ?????????????? ?????? ???????? ??????! ????????????????"
 
-#: gui/options.cpp:2832
+#: gui/options.cpp:2841
 #, fuzzy
 #| msgid "3. Connect"
 msgid "Connect"
 msgstr "3. ????????"
 
-#: gui/options.cpp:2832
+#: gui/options.cpp:2841
 msgid "Connect your cloud storage account"
 msgstr "???? ???????????? ???????? ?????????????? ?????????????? ?????????? ????"
 
-#: gui/options.cpp:2845
+#: gui/options.cpp:2854
 msgctxt "lowres"
 msgid "/root/ Path:"
 msgstr "/ ?????????? / ????????????:"
 
-#: gui/options.cpp:2845 gui/options.cpp:2847 gui/options.cpp:2848
+#: gui/options.cpp:2854 gui/options.cpp:2856 gui/options.cpp:2857
 msgid "Select which directory will be shown as /root/ in the Files Manager"
 msgstr "?????? ???????????? ???????? ???????? ???????? ?? / ?????? / ???? ???????? ??????????????"
 
-#: gui/options.cpp:2847
+#: gui/options.cpp:2856
 msgid "/root/ Path:"
 msgstr "/ ?????????? / ????????????:"
 
-#: gui/options.cpp:2853
+#: gui/options.cpp:2862
 msgid "Server's port:"
 msgstr "???????? ????????????:"
 
-#: gui/options.cpp:2853
+#: gui/options.cpp:2862
 msgid "Port for server to use"
 msgstr "???????? ???????????? ??????????????????"
 
-#: gui/options.cpp:2858
+#: gui/options.cpp:2867
 msgctxt "lowres"
 msgid "Run server to manage files with browser (in the same network)."
 msgstr "???? ???????????? ???????????? ???????????? ?????????????? ???????????????? ?????????????? (???? ?????? ????????????)."
 
-#: gui/options.cpp:2859
+#: gui/options.cpp:2868
 msgctxt "lowres"
 msgid "Closing options dialog will stop the server."
 msgstr "?????????? ???????????? ???????????? ?????????? ????????????."
 
-#: gui/options.cpp:2861
+#: gui/options.cpp:2870
 msgid "Run server to manage files with browser (in the same network)."
 msgstr "???? ???????????? ???????????? ???????????? ?????????????? ???????????????? ?????????????? (???? ?????? ????????????)."
 
-#: gui/options.cpp:2862
+#: gui/options.cpp:2871
 msgid "Closing options dialog will stop the server."
 msgstr "?????????? ???????????? ???????????? ?????????? ????????????."
 
-#: gui/options.cpp:2873
+#: gui/options.cpp:2882
 msgid "Use Text to speech"
 msgstr "???????????? ?????????? ???????? ?????? ????????"
 
-#: gui/options.cpp:2873
+#: gui/options.cpp:2882
 msgid "Will read text in gui on mouse over."
 msgstr "?????? ???????? ???????? ???? ?????????? ???????????????? ???????????????? ?????? ????????????."
 
-#: gui/options.cpp:2954
+#: gui/options.cpp:2963
 msgid "Ignore"
 msgstr "??????????"
 
-#: gui/options.cpp:2955
+#: gui/options.cpp:2964
 msgid "Disable autosave"
 msgstr "?????????? ?????????? ????????????????"
 
-#: gui/options.cpp:2956
+#: gui/options.cpp:2965
 msgid ""
 "WARNING: Autosave was enabled. Some of your games have existing saved games "
 "on the autosave slot. You can either move the existing saves to new slots, "
@@ -2615,7 +2627,7 @@ msgstr ""
 "?????? ?????? ?????????????? ?????? ??????????).\n"
 "?????????? ??????????????:\n"
 
-#: gui/options.cpp:2965
+#: gui/options.cpp:2974
 msgid ""
 "\n"
 "And more..."
@@ -2623,34 +2635,34 @@ msgstr ""
 "\n"
 "?????????????? ..."
 
-#: gui/options.cpp:2966 engines/engine.cpp:647
+#: gui/options.cpp:2975 engines/engine.cpp:647
 #: engines/access/metaengine.cpp:298 engines/drascula/metaengine.cpp:255
 #: engines/queen/metaengine.cpp:269 engines/sherlock/metaengine.cpp:410
 #: engines/ultima/nuvie/metaengine.cpp:71
 msgid "Move"
 msgstr "????????"
 
-#: gui/options.cpp:2985
+#: gui/options.cpp:2994
 msgid "ERROR: Failed to move the following saved games:\n"
 msgstr "??????: ?????? ???? ?????? ?????????????? ???????????????? ??????????????:\n"
 
-#: gui/options.cpp:3081
+#: gui/options.cpp:3090
 msgid "Failed to change cloud storage!"
 msgstr "?????? ???? ?????????? ?????????????? ??????????????!"
 
-#: gui/options.cpp:3084
+#: gui/options.cpp:3093
 msgid "Another cloud storage is already active."
 msgstr "?????????????? ?????????????? ?????????? ???????? ????????????."
 
-#: gui/options.cpp:3199
+#: gui/options.cpp:3212
 msgid "Theme does not support selected language!"
 msgstr "?????????????? ???? ???????? ?????????? ????????????????!"
 
-#: gui/options.cpp:3202
+#: gui/options.cpp:3215
 msgid "Theme cannot be loaded!"
 msgstr "???? ???????? ?????????? ????????????!"
 
-#: gui/options.cpp:3205
+#: gui/options.cpp:3218
 msgid ""
 "\n"
 "Misc settings will be restored."
@@ -2658,45 +2670,45 @@ msgstr ""
 "\n"
 "???????? ?????????????? ?????????????????? ????????????????."
 
-#: gui/options.cpp:3277
+#: gui/options.cpp:3290
 msgid "Select directory for GUI themes"
 msgstr "?????? ???????????? ???????????????? ?????????? ???????????????? ????????????????"
 
-#: gui/options.cpp:3287
+#: gui/options.cpp:3300
 msgid "Select directory for GUI launcher thumbnails"
 msgstr "?????? ???????????? ?????????? ?????????????? ?????????? ?????????? ???????????????? ????????????????"
 
-#: gui/options.cpp:3298
+#: gui/options.cpp:3311
 #, fuzzy
 #| msgid "Select directory for plugins"
 msgid "Select directory for DLC downloads"
 msgstr "?????? ???????? ???????????????? ????????????????"
 
-#: gui/options.cpp:3309
+#: gui/options.cpp:3322
 msgid "Select directory for extra files"
 msgstr "?????? ???????????? ?????????????? ????????????????"
 
-#: gui/options.cpp:3320
+#: gui/options.cpp:3333
 msgid "Select directory for plugins"
 msgstr "?????? ???????? ???????????????? ????????????????"
 
-#: gui/options.cpp:3333
+#: gui/options.cpp:3346
 msgid "Select directory for Files Manager /root/"
 msgstr "?????? ???????????? ?????????? ?????????????? / ?????????? /"
 
-#: gui/options.cpp:3485
+#: gui/options.cpp:3498
 msgid "This Storage is working now. Do you want to interrupt it?"
 msgstr "?????? ?????????????? ???????? ????????. ???? ???????? ????????????????"
 
-#: gui/options.cpp:3494
+#: gui/options.cpp:3507
 msgid "Wait until current Storage finishes up and try again."
 msgstr "?????????? ?????? ???????????? ?????????????? ???????????? ?????????? ?????? ????????."
 
-#: gui/options.cpp:3691 backends/cloud/cloudmanager.cpp:157
+#: gui/options.cpp:3704 backends/cloud/cloudmanager.cpp:157
 msgid "<right now>"
 msgstr "<????????>"
 
-#: gui/options.cpp:3834
+#: gui/options.cpp:3847
 msgid ""
 "Request failed.\n"
 "Check your Internet connection."
@@ -2704,7 +2716,7 @@ msgstr ""
 "?????????? ??????.\n"
 "???????? ???? ???????????? ??????????????????."
 
-#: gui/options.cpp:3857
+#: gui/options.cpp:3870
 msgid ""
 "A test pattern should be displayed.\n"
 "Do you want to keep these shader scaler settings?"
@@ -2712,7 +2724,7 @@ msgstr ""
 "???? ?????? ?????? ????????????????.\n"
 "???? ???????? ???????????????? ???????????????? ?????????? ???????? ????????"
 
-#: gui/options.cpp:3860
+#: gui/options.cpp:3873
 #, c-format
 msgid "Reverting automatically in %d seconds"
 msgstr "?????? ?????????????? ???????????????? ???????? %d ??????????"
@@ -2805,7 +2817,7 @@ msgstr "????????????"
 msgid "Playback"
 msgstr "??????????"
 
-#: gui/recorderdialog.cpp:76 gui/widgets/grid.cpp:341
+#: gui/recorderdialog.cpp:76 gui/widgets/grid.cpp:343
 msgid "Edit"
 msgstr "????????"
 
@@ -3017,11 +3029,11 @@ msgstr ""
 msgid "Clear value"
 msgstr "???????? ??????????"
 
-#: gui/widgets/grid.cpp:335
+#: gui/widgets/grid.cpp:337
 msgid "Play"
 msgstr "????????"
 
-#: gui/widgets/grid.cpp:338
+#: gui/widgets/grid.cpp:340
 msgid "Saves"
 msgstr "????????"
 
diff --git a/po/be-tarask.po b/po/be-tarask.po
index f5cff15f155..2c930975d58 100644
--- a/po/be-tarask.po
+++ b/po/be-tarask.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-13 10:20+0000\n"
+"POT-Creation-Date: 2026-06-13 13:52+0000\n"
 "PO-Revision-Date: 2023-02-26 15:01+0000\n"
 "Last-Translator: Kastu?? K. <oakum2334 at gmail.com>\n"
 "Language-Team: Belarusian (Tara??kievica) <https://translations.scummvm.org/"
@@ -124,7 +124,7 @@ msgstr "??????????"
 #: gui/downloadpacksdialog.cpp:465 gui/editgamedialog.cpp:340
 #: gui/editrecorddialog.cpp:66 gui/integrity-dialog.cpp:122
 #: gui/integrity-dialog.cpp:253 gui/filebrowser-dialog.cpp:68
-#: gui/fluidsynth-dialog.cpp:153 gui/massadd.cpp:93 gui/options.cpp:2416
+#: gui/fluidsynth-dialog.cpp:153 gui/massadd.cpp:93 gui/options.cpp:2418
 #: gui/predictivedialog.cpp:68 gui/printing-dialog.cpp:96
 #: gui/recorderdialog.cpp:72 gui/recorderdialog.cpp:211
 #: gui/remotebrowser.cpp:59 gui/saveload-dialog.cpp:64
@@ -197,23 +197,23 @@ msgid ""
 "so your browser can forward data to ScummVM"
 msgstr ""
 
-#: gui/cloudconnectionwizard.cpp:257 gui/options.cpp:3776
+#: gui/cloudconnectionwizard.cpp:257 gui/options.cpp:3789
 msgid "Stop server"
 msgstr "?????????????? ????????????"
 
-#: gui/cloudconnectionwizard.cpp:257 gui/options.cpp:2840
+#: gui/cloudconnectionwizard.cpp:257 gui/options.cpp:2849
 msgid "Run server"
 msgstr "???????????????????? ????????????"
 
-#: gui/cloudconnectionwizard.cpp:258 gui/options.cpp:3777
+#: gui/cloudconnectionwizard.cpp:258 gui/options.cpp:3790
 msgid "Stop local webserver"
 msgstr "???????? ?????????????? ???????????????? ??????-????????????"
 
-#: gui/cloudconnectionwizard.cpp:258 gui/options.cpp:2840
+#: gui/cloudconnectionwizard.cpp:258 gui/options.cpp:2849
 msgid "Run local webserver"
 msgstr "???????????????????? ???????????????? ??????-????????????"
 
-#: gui/cloudconnectionwizard.cpp:266 gui/options.cpp:2841 gui/options.cpp:3784
+#: gui/cloudconnectionwizard.cpp:266 gui/options.cpp:2850 gui/options.cpp:3797
 msgid "Not running"
 msgstr "???? ??????????????????"
 
@@ -347,8 +347,8 @@ msgstr "?????????? ???????????????? ???????????? ??????????. ???? ???????????? 
 #: gui/cloudconnectionwizard.cpp:530 gui/downloaddialog.cpp:118
 #: gui/downloaddialog.cpp:152 gui/filebrowser-dialog.cpp:135
 #: gui/fluidsynth-dialog.cpp:210 gui/launcher.cpp:428 gui/launcher.cpp:457
-#: gui/launcher.cpp:534 gui/launcher.cpp:1938 gui/options.cpp:3485
-#: gui/options.cpp:3859 base/main.cpp:608
+#: gui/launcher.cpp:534 gui/launcher.cpp:1970 gui/options.cpp:3498
+#: gui/options.cpp:3872 base/main.cpp:608
 #: backends/events/default/default-events.cpp:193 engines/buried/buried.cpp:598
 #: engines/chamber/metaengine.cpp:91 engines/director/events.cpp:122
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:139
@@ -367,8 +367,8 @@ msgstr "??????"
 #: gui/cloudconnectionwizard.cpp:530 gui/downloaddialog.cpp:118
 #: gui/downloaddialog.cpp:153 gui/filebrowser-dialog.cpp:135
 #: gui/fluidsynth-dialog.cpp:210 gui/launcher.cpp:428 gui/launcher.cpp:457
-#: gui/launcher.cpp:534 gui/launcher.cpp:1939 gui/options.cpp:3485
-#: gui/options.cpp:3859 engines/buried/buried.cpp:598
+#: gui/launcher.cpp:534 gui/launcher.cpp:1971 gui/options.cpp:3498
+#: gui/options.cpp:3872 engines/buried/buried.cpp:598
 #: engines/chamber/metaengine.cpp:97 engines/director/events.cpp:122
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:139
 #: engines/efh/metaengine.cpp:368 engines/grim/grim.cpp:366
@@ -398,7 +398,7 @@ msgstr ""
 #: gui/editrecorddialog.cpp:67 gui/integrity-dialog.cpp:260
 #: gui/fluidsynth-dialog.cpp:154 gui/massadd.cpp:90 gui/message.cpp:156
 #: gui/message.cpp:160 gui/message.cpp:231 gui/message.cpp:270
-#: gui/message.cpp:274 gui/options.cpp:2418 gui/predictivedialog.cpp:69
+#: gui/message.cpp:274 gui/options.cpp:2420 gui/predictivedialog.cpp:69
 #: gui/saveload-dialog.cpp:1244 base/main.cpp:675 base/main.cpp:700
 #: dists/android.strings.xml.cpp:33 engines/engine.cpp:846
 #: backends/platform/wii/options.cpp:46 engines/buried/buried.cpp:622
@@ -599,11 +599,11 @@ msgstr ""
 msgid "Proceed"
 msgstr "????????????????????"
 
-#: gui/dump-all-dialogs.cpp:123 gui/options.cpp:3347
+#: gui/dump-all-dialogs.cpp:123 gui/options.cpp:3360
 msgid "icon packs"
 msgstr ""
 
-#: gui/dump-all-dialogs.cpp:127 gui/options.cpp:3358
+#: gui/dump-all-dialogs.cpp:127 gui/options.cpp:3371
 msgid "shader packs"
 msgstr ""
 
@@ -615,11 +615,11 @@ msgstr "???????????????? ????????????:"
 msgid "Game"
 msgstr "????????????"
 
-#: gui/editgamedialog.cpp:164 gui/options.cpp:2075 gui/options.cpp:2254
+#: gui/editgamedialog.cpp:164 gui/options.cpp:2075 gui/options.cpp:2256
 msgid "GFX"
 msgstr "GFX"
 
-#: gui/editgamedialog.cpp:164 gui/options.cpp:2075 gui/options.cpp:2254
+#: gui/editgamedialog.cpp:164 gui/options.cpp:2075 gui/options.cpp:2256
 #: backends/graphics/atari/atari-graphics.cpp:1056
 #: backends/graphics/sdl/sdl-graphics.cpp:487
 msgid "Graphics"
@@ -634,11 +634,11 @@ msgctxt "lowres"
 msgid "Override global graphic settings"
 msgstr "???????????????? ???????????????????? ???????????? ??????????????"
 
-#: gui/editgamedialog.cpp:185 gui/options.cpp:2288 engines/dialogs.cpp:354
+#: gui/editgamedialog.cpp:185 gui/options.cpp:2290 engines/dialogs.cpp:354
 msgid "Keymaps"
 msgstr "??????????????"
 
-#: gui/editgamedialog.cpp:197 gui/options.cpp:2300 engines/dialogs.cpp:366
+#: gui/editgamedialog.cpp:197 gui/options.cpp:2302 engines/dialogs.cpp:366
 msgid "Backend"
 msgstr "????????????"
 
@@ -651,7 +651,7 @@ msgctxt "lowres"
 msgid "Override global backend settings"
 msgstr "???????????????? ???????????????????? ???????????? ??????????????"
 
-#: gui/editgamedialog.cpp:220 gui/options.cpp:2318 engines/dialogs.cpp:329
+#: gui/editgamedialog.cpp:220 gui/options.cpp:2320 engines/dialogs.cpp:329
 msgid "Audio"
 msgstr "??????"
 
@@ -664,11 +664,11 @@ msgctxt "lowres"
 msgid "Override global audio settings"
 msgstr "???????????????? ???????????????????? ???????????? ????????"
 
-#: gui/editgamedialog.cpp:234 gui/options.cpp:2323
+#: gui/editgamedialog.cpp:234 gui/options.cpp:2325
 msgid "Volume"
 msgstr "??????????????????"
 
-#: gui/editgamedialog.cpp:236 gui/options.cpp:2325
+#: gui/editgamedialog.cpp:236 gui/options.cpp:2327
 msgctxt "lowres"
 msgid "Volume"
 msgstr "??????????????????"
@@ -682,7 +682,7 @@ msgctxt "lowres"
 msgid "Override global volume settings"
 msgstr "???????????????? ???????????????????? ???????????? ??????????????????"
 
-#: gui/editgamedialog.cpp:252 gui/options.cpp:2333
+#: gui/editgamedialog.cpp:252 gui/options.cpp:2335
 msgid "MIDI"
 msgstr "MIDI"
 
@@ -695,7 +695,7 @@ msgctxt "lowres"
 msgid "Override global MIDI settings"
 msgstr "???????????????? ???????????????????? ???????????? MIDI"
 
-#: gui/editgamedialog.cpp:267 gui/options.cpp:2339
+#: gui/editgamedialog.cpp:267 gui/options.cpp:2341
 msgid "MT-32"
 msgstr "MT-32"
 
@@ -708,11 +708,11 @@ msgctxt "lowres"
 msgid "Override global MT-32 settings"
 msgstr "???????????????? ???????????????????? ???????????? MT-32"
 
-#: gui/editgamedialog.cpp:281 gui/options.cpp:2346
+#: gui/editgamedialog.cpp:281 gui/options.cpp:2348
 msgid "Paths"
 msgstr "??????????"
 
-#: gui/editgamedialog.cpp:283 gui/options.cpp:2348
+#: gui/editgamedialog.cpp:283 gui/options.cpp:2350
 msgctxt "lowres"
 msgid "Paths"
 msgstr "??????????"
@@ -734,7 +734,7 @@ msgctxt "lowres"
 msgid "Game Path:"
 msgstr "???????? ???? ????????????:"
 
-#: gui/editgamedialog.cpp:303 gui/options.cpp:2555
+#: gui/editgamedialog.cpp:303 gui/options.cpp:2557
 msgid "Extra Path:"
 msgstr "??????. ????????:"
 
@@ -743,33 +743,33 @@ msgstr "??????. ????????:"
 msgid "Specifies path to additional data used by the game"
 msgstr "???????? ???? ???????????????????? ???????????????? ????????????"
 
-#: gui/editgamedialog.cpp:305 gui/options.cpp:2557
+#: gui/editgamedialog.cpp:305 gui/options.cpp:2559
 msgctxt "lowres"
 msgid "Extra Path:"
 msgstr "??????. ????????:"
 
-#: gui/editgamedialog.cpp:306 gui/options.cpp:2530 gui/options.cpp:2558
-#: gui/options.cpp:2567 gui/options.cpp:2848
+#: gui/editgamedialog.cpp:306 gui/options.cpp:2532 gui/options.cpp:2560
+#: gui/options.cpp:2569 gui/options.cpp:2857
 #: backends/platform/libretro/src/libretro-options-widget.cpp:277
 msgctxt "path"
 msgid "None"
 msgstr "????????"
 
-#: gui/editgamedialog.cpp:312 gui/options.cpp:2519
+#: gui/editgamedialog.cpp:312 gui/options.cpp:2521
 msgid "Save Path:"
 msgstr "???????? ??????????????:"
 
 #: gui/editgamedialog.cpp:312 gui/editgamedialog.cpp:314
-#: gui/editgamedialog.cpp:315 gui/options.cpp:2519 gui/options.cpp:2521
+#: gui/editgamedialog.cpp:315 gui/options.cpp:2521 gui/options.cpp:2523
 msgid "Specifies where your saved games are put"
 msgstr "???????? ???? ?????????? ?????????????????? ??????????????"
 
-#: gui/editgamedialog.cpp:314 gui/options.cpp:2521
+#: gui/editgamedialog.cpp:314 gui/options.cpp:2523
 msgctxt "lowres"
 msgid "Save Path:"
 msgstr "???????? ??????????????:"
 
-#: gui/editgamedialog.cpp:315 gui/options.cpp:1844 gui/options.cpp:2522
+#: gui/editgamedialog.cpp:315 gui/options.cpp:1844 gui/options.cpp:2524
 #: backends/platform/wii/options.cpp:55
 msgid "Default"
 msgstr "????????????????????"
@@ -828,8 +828,8 @@ msgstr "???????? ????????????. ???????? ???? ?????????????????????? ????????????
 #: gui/editgamedialog.cpp:365 gui/editgamedialog.cpp:380 gui/options.cpp:1613
 #: gui/options.cpp:1627 gui/options.cpp:1644 gui/options.cpp:1658
 #: gui/options.cpp:1675 gui/options.cpp:1713 gui/options.cpp:1729
-#: gui/options.cpp:2144 gui/options.cpp:2600 gui/options.cpp:2674
-#: gui/options.cpp:2888 gui/options.cpp:3389 audio/null.cpp:45
+#: gui/options.cpp:2144 gui/options.cpp:2602 gui/options.cpp:2683
+#: gui/options.cpp:2897 gui/options.cpp:3402 audio/null.cpp:45
 #: backends/platform/android/options.cpp:157
 #: backends/platform/android/options.cpp:158
 #: backends/platform/android/options.cpp:186
@@ -856,7 +856,7 @@ msgctxt "lowres"
 msgid "Platform:"
 msgstr "??????????????????:"
 
-#: gui/editgamedialog.cpp:566 gui/options.cpp:3413
+#: gui/editgamedialog.cpp:566 gui/options.cpp:3426
 msgid "Select SoundFont"
 msgstr "???????????????? SoundFont"
 
@@ -864,11 +864,11 @@ msgstr "???????????????? SoundFont"
 msgid "Select additional game directory"
 msgstr "???????????????? ???????????????????? ?????????? ????????????"
 
-#: gui/editgamedialog.cpp:618 gui/options.cpp:3261
+#: gui/editgamedialog.cpp:618 gui/options.cpp:3274
 msgid "Select directory for saved games"
 msgstr "???????????????? ?????????? ?????? ??????????????"
 
-#: gui/editgamedialog.cpp:625 gui/options.cpp:3268
+#: gui/editgamedialog.cpp:625 gui/options.cpp:3281
 msgid "The chosen directory cannot be written to. Please select another one."
 msgstr "?????????????????? ???????????????????? ?????????? ??????????. ???????? ??????????, ???????????????? ??????????."
 
@@ -1012,7 +1012,7 @@ msgstr "??????????"
 msgid "Triangle"
 msgstr "??????????????????"
 
-#: gui/fluidsynth-dialog.cpp:139 gui/options.cpp:2366
+#: gui/fluidsynth-dialog.cpp:139 gui/options.cpp:2368
 msgid "Misc"
 msgstr "????????????"
 
@@ -1050,7 +1050,7 @@ msgid ""
 "Do you really want to reset all FluidSynth settings to their default values?"
 msgstr "???? ???????????????? ???? ???????????? ???????????????? ?????? ?????????????????????? ???????????? FluidSynth?"
 
-#: gui/gui-manager.cpp:159 gui/options.cpp:2357
+#: gui/gui-manager.cpp:159 gui/options.cpp:2359
 msgid "GUI"
 msgstr "???????????????? ??????????????????"
 
@@ -1265,7 +1265,7 @@ msgid ""
 "and illegal game copies.\n"
 msgstr ""
 
-#: gui/helpdialog.cpp:68 gui/options.cpp:2377
+#: gui/helpdialog.cpp:68 gui/options.cpp:2379
 msgid "Cloud"
 msgstr "??????????????"
 
@@ -1562,88 +1562,88 @@ msgstr "??????????"
 msgid "Grid view"
 msgstr "??????????"
 
-#: gui/launcher.cpp:1145
+#: gui/launcher.cpp:1168
 msgid "~S~tart"
 msgstr "~??~??????????"
 
-#: gui/launcher.cpp:1145
+#: gui/launcher.cpp:1168
 msgid "Start selected game"
 msgstr "?????????????????? ???????????????? ????????????"
 
-#: gui/launcher.cpp:1148
+#: gui/launcher.cpp:1171
 msgid "~L~oad..."
 msgstr "~??~??????????????????..."
 
-#: gui/launcher.cpp:1148
+#: gui/launcher.cpp:1171
 msgid "Load saved game for selected game"
 msgstr "???????????????????? ?????????? ?????? ???????????????? ????????????"
 
-#: gui/launcher.cpp:1150
+#: gui/launcher.cpp:1173
 msgid "Record..."
 msgstr "?????????????"
 
-#: gui/launcher.cpp:1156
+#: gui/launcher.cpp:1179
 msgid "~G~ame Options..."
 msgstr "~??~?????????? ????????????..."
 
-#: gui/launcher.cpp:1156
+#: gui/launcher.cpp:1179
 msgid "Change game options"
 msgstr "???????????????? ???????????? ????????????"
 
-#: gui/launcher.cpp:1156
+#: gui/launcher.cpp:1179
 #, fuzzy
 msgctxt "lowres"
 msgid "~G~ame Opts..."
 msgstr "~??~?????????? ????????????..."
 
-#: gui/launcher.cpp:1242
+#: gui/launcher.cpp:1265
 msgid "Add-on"
 msgstr ""
 
 #. I18N: List grouping when no engine is specified
-#: gui/launcher.cpp:1309 gui/launcher.cpp:1554
+#: gui/launcher.cpp:1335 gui/launcher.cpp:1580
 msgid "Unknown Engine"
 msgstr "???????????????? ??????????????"
 
 #. I18N: List grouping when no publisher is specified
 #. I18N: List group when no publisher is specified
-#: gui/launcher.cpp:1326 gui/launcher.cpp:1571
+#: gui/launcher.cpp:1352 gui/launcher.cpp:1597
 msgid "Unknown Publisher"
 msgstr "?????????????????? ????????????????????"
 
 #. I18N: List group when no game series is specified
 #. I18N: List grouping when no game series is specified
-#: gui/launcher.cpp:1343 gui/launcher.cpp:1588
+#: gui/launcher.cpp:1369 gui/launcher.cpp:1614
 msgid "No Series"
 msgstr "?????? ??????????"
 
 #. I18N: List group when no language is specified
-#: gui/launcher.cpp:1357 gui/launcher.cpp:1602
+#: gui/launcher.cpp:1383 gui/launcher.cpp:1628
 msgid "Language not detected"
 msgstr "???????????????????????? ????????"
 
 #. I18N: List group when no platform is specified
-#: gui/launcher.cpp:1371 gui/launcher.cpp:1616
+#: gui/launcher.cpp:1397 gui/launcher.cpp:1642
 msgid "Platform not detected"
 msgstr "???????????????????????? ??????????????????"
 
 #. I18N: List group when no year is specified
-#: gui/launcher.cpp:1388 gui/launcher.cpp:1633
+#: gui/launcher.cpp:1414 gui/launcher.cpp:1659
 #, fuzzy
 #| msgid "Unknown error"
 msgid "Unknown Year"
 msgstr "?????????????????? ??????????????"
 
 #. I18N: Group for All items
-#: gui/launcher.cpp:1640 gui/widgets/groupedlist.cpp:82
+#: gui/launcher.cpp:1666 gui/widgets/groupedlist.cpp:82
 msgid "All"
 msgstr "??????"
 
-#: gui/launcher.cpp:1812
+#: gui/launcher.cpp:1844
 msgid "Icons per row:"
 msgstr ""
 
-#: gui/launcher.cpp:1869
+#: gui/launcher.cpp:1901
 #, fuzzy, c-format
 #| msgid "Do you really want to remove this game configuration?"
 msgid ""
@@ -2154,150 +2154,150 @@ msgctxt "lowres"
 msgid "Speech volume:"
 msgstr "?????????????????? ????????:"
 
-#: gui/options.cpp:2266
+#: gui/options.cpp:2268
 msgid "Control"
 msgstr "????????????????????"
 
-#: gui/options.cpp:2379
+#: gui/options.cpp:2381
 msgctxt "lowres"
 msgid "Cloud"
 msgstr "??????????????"
 
-#: gui/options.cpp:2394
+#: gui/options.cpp:2396
 msgid "LAN"
 msgstr "LAN"
 
-#: gui/options.cpp:2396
+#: gui/options.cpp:2398
 msgctxt "lowres"
 msgid "LAN"
 msgstr "LAN"
 
-#: gui/options.cpp:2405
+#: gui/options.cpp:2407
 msgid "Accessibility"
 msgstr "????????????????????????"
 
-#: gui/options.cpp:2407
+#: gui/options.cpp:2409
 msgctxt "lowres"
 msgid "Accessibility"
 msgstr "????????????????????????"
 
-#: gui/options.cpp:2416
+#: gui/options.cpp:2418
 msgid "Discard changes and close the dialog"
 msgstr "?????????????????? ???????????? ?? ?????????????? ????????????"
 
-#: gui/options.cpp:2417
+#: gui/options.cpp:2419
 msgid "Apply"
 msgstr "??????????"
 
-#: gui/options.cpp:2417
+#: gui/options.cpp:2419
 msgid "Apply changes without closing the dialog"
 msgstr "?????????? ???????????? ?? ???? ?????????????????? ????????????"
 
-#: gui/options.cpp:2418
+#: gui/options.cpp:2420
 msgid "Apply changes and close the dialog"
 msgstr "?????????? ???????????? ?? ?????????????? ????????????"
 
-#: gui/options.cpp:2508
+#: gui/options.cpp:2510
 msgid "FluidSynth Settings"
 msgstr "???????????? FluidSynth"
 
-#: gui/options.cpp:2522
+#: gui/options.cpp:2524
 msgid ""
 "Specifies where your saved games are put. A red coloring indicates the value "
 "is temporary and will not get saved"
 msgstr ""
 
-#: gui/options.cpp:2527
+#: gui/options.cpp:2529
 msgid "Theme Path:"
 msgstr "???????? ???? ??????????:"
 
-#: gui/options.cpp:2529
+#: gui/options.cpp:2531
 msgctxt "lowres"
 msgid "Theme Path:"
 msgstr "???????? ???? ??????????:"
 
-#: gui/options.cpp:2535
+#: gui/options.cpp:2537
 msgid "Icon Path:"
 msgstr "???????? ???? ??????????????:"
 
-#: gui/options.cpp:2537
+#: gui/options.cpp:2539
 msgctxt "lowres"
 msgid "Icon Path:"
 msgstr "???????? ???? ??????????????:"
 
-#: gui/options.cpp:2538 gui/options.cpp:2548
+#: gui/options.cpp:2540 gui/options.cpp:2550
 msgctxt "path"
 msgid "Default"
 msgstr "????????????????????"
 
-#: gui/options.cpp:2545
+#: gui/options.cpp:2547
 #, fuzzy
 #| msgid "Game Path:"
 msgid "DLC Path:"
 msgstr "???????? ???? ????????????:"
 
-#: gui/options.cpp:2547
+#: gui/options.cpp:2549
 #, fuzzy
 #| msgid "Game Path:"
 msgctxt "lowres"
 msgid "DLC Path:"
 msgstr "???????? ???? ????????????:"
 
-#: gui/options.cpp:2555 gui/options.cpp:2557 gui/options.cpp:2558
+#: gui/options.cpp:2557 gui/options.cpp:2559 gui/options.cpp:2560
 msgid "Specifies path to additional data used by all games or ScummVM"
 msgstr "???????? ???? ???????????????????? ???????????????? ???????? ?????????????? ???? ScummVM"
 
-#: gui/options.cpp:2564
+#: gui/options.cpp:2566
 msgid "Plugins Path:"
 msgstr "???????? ???? ????????????????:"
 
-#: gui/options.cpp:2566
+#: gui/options.cpp:2568
 msgctxt "lowres"
 msgid "Plugins Path:"
 msgstr "???????? ???? ????????????????:"
 
-#: gui/options.cpp:2576
+#: gui/options.cpp:2578
 msgid "ScummVM config path: "
 msgstr "???????? ???? ?????????????? ScummVM: "
 
-#: gui/options.cpp:2590
+#: gui/options.cpp:2592
 #, fuzzy
 #| msgid "ScummVM config path: "
 msgid "ScummVM log path: "
 msgstr "???????? ???? ?????????????? ScummVM: "
 
-#: gui/options.cpp:2595
+#: gui/options.cpp:2597
 msgid "View"
 msgstr ""
 
 #. I18N: Referring to the last path memorized when adding a game
-#: gui/options.cpp:2605 gui/options.cpp:3389
+#: gui/options.cpp:2607 gui/options.cpp:3402
 msgid "Last browser path: "
 msgstr "???????????? ????????: "
 
-#: gui/options.cpp:2610
+#: gui/options.cpp:2612
 msgid "Theme:"
 msgstr "????????:"
 
-#: gui/options.cpp:2615
+#: gui/options.cpp:2617
 msgid "GUI scale:"
 msgstr "?????????? ????????????????????:"
 
-#: gui/options.cpp:2632
+#: gui/options.cpp:2634
 #, c-format
 msgid "%d%% - Custom"
 msgstr ""
 
-#: gui/options.cpp:2636
+#: gui/options.cpp:2638
 msgid "GUI renderer:"
 msgstr "???????????? ????????????????????:"
 
-#: gui/options.cpp:2649
+#: gui/options.cpp:2651
 #, fuzzy
 msgid "Return to the launcher when leaving a game"
 msgstr "???????????? ???????????????? ?? ScummVM ???????????? ???????????? ?? ????????????"
 
-#: gui/options.cpp:2650
+#: gui/options.cpp:2652
 #, fuzzy
 msgid ""
 "Return to the launcher when leaving a game instead of closing ScummVM\n"
@@ -2305,38 +2305,50 @@ msgid ""
 msgstr ""
 "???? ?????????????????? ScummVM, ?? ???????????? ???????????????? ???? ?????? ???????????? ?????????????? ?? ????????????."
 
-#: gui/options.cpp:2657
+#: gui/options.cpp:2659
 msgid "Ask for confirmation on exit"
 msgstr "?????????????????? ?????????????????????????????? ??????????????"
 
-#: gui/options.cpp:2658
+#: gui/options.cpp:2660
 msgid "Ask for permission when closing ScummVM or leaving a game."
 msgstr "?????????????????? ?????????????????????????????? ?????? ?????????????????? ScummVM ???? ?????????????? ?? ????????????."
 
-#: gui/options.cpp:2664
+#: gui/options.cpp:2666
 #, fuzzy
 msgid "Disable fixed font scaling"
 msgstr "???????????????? ???????? ????????????????????"
 
-#: gui/options.cpp:2665
+#: gui/options.cpp:2667
 msgid ""
 "Do not upscale fixed size fonts in the GUI. This reduces artefacts on low "
-"resolution screens"
+"resolution screens."
 msgstr ""
 
-#: gui/options.cpp:2671
+#: gui/options.cpp:2673
+#, fuzzy
+#| msgid "Enable smooth scrolling when walking"
+msgid "Enable kinetic scrolling in lists"
+msgstr "???????????????? ?????????????? ?????????????????????????? ???????????? ????????"
+
+#: gui/options.cpp:2674
+#, fuzzy
+#| msgid "Enable smooth scrolling when walking"
+msgid "Enable smooth, momentum-based scrolling in list widgets."
+msgstr "???????????????? ?????????????? ?????????????????????????? ???????????? ????????"
+
+#: gui/options.cpp:2680
 msgid "GUI language:"
 msgstr "???????? ????????????????????:"
 
-#: gui/options.cpp:2671
+#: gui/options.cpp:2680
 msgid "Language of ScummVM GUI"
 msgstr "???????? ???????????????????? ???????????????????? ScummVM"
 
-#: gui/options.cpp:2697
+#: gui/options.cpp:2706
 msgid "Switch the ScummVM GUI language to the game language"
 msgstr "???????????? ???????? ???????????????????? ScummVM ???? ???????? ????????????"
 
-#: gui/options.cpp:2698
+#: gui/options.cpp:2707
 msgid ""
 "When starting a game, change the ScummVM GUI language to the game language. "
 "That way, if a game uses the ScummVM save and load dialogs, they are in the "
@@ -2346,11 +2358,11 @@ msgstr ""
 "?????????? ??????????, ???????? ???????????? ?????????????????????????? ?????????????? ???????????????????? ?? ???????????????????????? ???? "
 "ScummVM, ?????? ???????????? ?????? ???? ??????????, ?????? ?? ????????????."
 
-#: gui/options.cpp:2710
+#: gui/options.cpp:2719
 msgid "Use native system file browser"
 msgstr "???????????????????????????? ???????????????? ?????????????? ??????????????"
 
-#: gui/options.cpp:2711
+#: gui/options.cpp:2720
 msgid ""
 "Use the native system file browser instead of the ScummVM one to select a "
 "file or directory."
@@ -2358,81 +2370,81 @@ msgstr ""
 "???????????????????????????? ???????????????? ?????????????? ?????????????? ?????? ???????????? ???????????? ?? ??????????, ???????????? "
 "???????????????? ScummVM."
 
-#: gui/options.cpp:2718
+#: gui/options.cpp:2727
 #, fuzzy
 #| msgid "Download"
 msgid "Download Icons"
 msgstr "????????????????????"
 
-#: gui/options.cpp:2718
+#: gui/options.cpp:2727
 #, fuzzy
 msgid "Check on the scummvm.org website for updates of icon packs"
 msgstr "?????????????????? ????????????..."
 
-#: gui/options.cpp:2724
+#: gui/options.cpp:2733
 msgid "Autosave:"
 msgstr "????????????????????????????:"
 
-#: gui/options.cpp:2726
+#: gui/options.cpp:2735
 msgctxt "lowres"
 msgid "Autosave:"
 msgstr "????????????????????????????:"
 
-#: gui/options.cpp:2737
+#: gui/options.cpp:2746
 msgid "Random seed:"
 msgstr ""
 
-#: gui/options.cpp:2737
+#: gui/options.cpp:2746
 msgid "Seed for initializing all random number generators"
 msgstr ""
 
-#: gui/options.cpp:2745
+#: gui/options.cpp:2754
 #, fuzzy
 #| msgid "Debug Menu"
 msgid "Debug level:"
 msgstr "???????? ???????????????????????? ??????????"
 
 #. I18N: Debug level -1, no messages
-#: gui/options.cpp:2749 gui/options.cpp:2886 audio/fmopl.cpp:83
+#: gui/options.cpp:2758 gui/options.cpp:2895 audio/fmopl.cpp:83
 msgid "None"
 msgstr "????????"
 
 #. I18N: Debug level 11, all messages
-#: gui/options.cpp:2755
+#: gui/options.cpp:2764
 msgid "11 (all)"
 msgstr ""
 
-#: gui/options.cpp:2759
+#: gui/options.cpp:2768
 msgid "Enable Discord integration"
 msgstr "???????????????? ???????????????????? ?? Discord"
 
-#: gui/options.cpp:2760
+#: gui/options.cpp:2769
 msgid ""
 "Show information about the games you are playing on Discord if the Discord "
 "client is running."
 msgstr "?????????????????? ????????????, ?? ???????? ???? ?????????????? ?? Discord, ???????? ???? ??????????????????."
 
-#: gui/options.cpp:2770 gui/updates-dialog.cpp:87
+#: gui/options.cpp:2779 gui/updates-dialog.cpp:87
 msgid "Update check:"
 msgstr "?????????????????? ????????????:"
 
-#: gui/options.cpp:2770
+#: gui/options.cpp:2779
 msgid "How often to check ScummVM updates"
 msgstr "???? ?????????? ?????????????????? ???????????? ScummVM"
 
-#: gui/options.cpp:2781
+#: gui/options.cpp:2790
 msgid "Check now"
 msgstr "?????????????????? ??????????"
 
-#: gui/options.cpp:2788
+#: gui/options.cpp:2797
 msgid "Active storage:"
 msgstr "?????????????? ????????????????:"
 
-#: gui/options.cpp:2788
+#: gui/options.cpp:2797
 msgid "Active cloud storage"
 msgstr "?????????????? ?????????????????? ????????????????"
 
-#: gui/options.cpp:2797
+#: gui/options.cpp:2806
 #, fuzzy
 #| msgctxt "lowres"
 #| msgid ""
@@ -2444,7 +2456,7 @@ msgid ""
 msgstr ""
 "4. ???????????????? ?????? ???? ????????????????. ????????????????, ???? ?????? ?????????????????? ?????????????????? ?? ????????????????:"
 
-#: gui/options.cpp:2799
+#: gui/options.cpp:2808
 #, fuzzy
 #| msgid ""
 #| "4. Storage is not yet enabled. Verify that username is correct and enable "
@@ -2454,102 +2466,102 @@ msgid ""
 msgstr ""
 "4. ???????????????? ?????? ???? ????????????????. ????????????????, ???? ?????? ?????????????????? ?????????????????? ?? ????????????????:"
 
-#: gui/options.cpp:2800
+#: gui/options.cpp:2809
 msgid "Enable storage"
 msgstr "???????????????? ????????????????"
 
-#: gui/options.cpp:2800
+#: gui/options.cpp:2809
 msgid "Confirm you want to use this account for this storage"
 msgstr "????????????????????????, ?????? ???????????? ???????????????????????????? ???????? ???????? ?????? ???????????? ????????????????"
 
-#: gui/options.cpp:2802 backends/platform/wii/options.cpp:113
+#: gui/options.cpp:2811 backends/platform/wii/options.cpp:113
 msgid "Username:"
 msgstr "????????????????:"
 
-#: gui/options.cpp:2802
+#: gui/options.cpp:2811
 msgid "Username used by this storage"
 msgstr "?????? ?????????????????? ?????? ???????????? ????????????????"
 
-#: gui/options.cpp:2803
+#: gui/options.cpp:2812
 msgid "<none>"
 msgstr "<????????>"
 
-#: gui/options.cpp:2805
+#: gui/options.cpp:2814
 msgid "Used space:"
 msgstr "?????????????????????? ??????????:"
 
-#: gui/options.cpp:2805
+#: gui/options.cpp:2814
 msgid "Space used by ScummVM's saved games on this storage"
 msgstr "??????????, ?????????????? ???????????????????? ???????????????? ScummVM ???? ?????????? ????????????????"
 
-#: gui/options.cpp:2808
+#: gui/options.cpp:2817
 msgid "Last sync:"
 msgstr "?????????????? ????????????????????????????:"
 
-#: gui/options.cpp:2808
+#: gui/options.cpp:2817
 msgid "When was the last time saved games were synced with this storage"
 msgstr "???????? ???????? ?????????????? ???????????????????????????? ?????????????????? ?????????????? ?? ?????????? ??????????????????"
 
-#: gui/options.cpp:2809 gui/options.cpp:3693
+#: gui/options.cpp:2818 gui/options.cpp:3706
 msgid "<never>"
 msgstr "<??????????????>"
 
-#: gui/options.cpp:2811
+#: gui/options.cpp:2820
 msgctxt "lowres"
 msgid "Saved games sync automatically on launch, after saving and on loading."
 msgstr ""
 "?????????????????? ???????????? ???????????????????????? ?????????????????????? ???????????? ??????????????, ???????????????????? ?? "
 "????????????????????????."
 
-#: gui/options.cpp:2813
+#: gui/options.cpp:2822
 msgid "Saved games sync automatically on launch, after saving and on loading."
 msgstr ""
 "?????????????????? ???????????? ???????????????????????? ?????????????????????? ???????????? ??????????????, ???????????????????? ?? "
 "????????????????????????."
 
-#: gui/options.cpp:2814
+#: gui/options.cpp:2823
 msgid "Sync now"
 msgstr "???????????????????????? ??????????"
 
-#: gui/options.cpp:2814
+#: gui/options.cpp:2823
 msgid "Start saved games sync"
 msgstr "???????????? ???????????????????????????? ?????????????????? ??????????????"
 
-#: gui/options.cpp:2817
+#: gui/options.cpp:2826
 msgctxt "lowres"
 msgid "You can download game files from your cloud ScummVM folder:"
 msgstr "???? ???????????? ???????????????????? ?????????????????? ?????????? ?? ?????????? ?????????????????? ?????????? ScummVM:"
 
-#: gui/options.cpp:2819
+#: gui/options.cpp:2828
 msgid "You can download game files from your cloud ScummVM folder:"
 msgstr "???? ???????????? ???????????????????? ?????????????????? ?????????? ?? ?????????? ?????????????????? ?????????? ScummVM:"
 
-#: gui/options.cpp:2820
+#: gui/options.cpp:2829
 msgid "Download game files"
 msgstr "???????????????????? ?????????????????? ??????????"
 
-#: gui/options.cpp:2820
+#: gui/options.cpp:2829
 msgid "Open downloads manager dialog"
 msgstr "?????????????? ???????????????????? ????????????????????????????"
 
-#: gui/options.cpp:2823
+#: gui/options.cpp:2832
 msgctxt "lowres"
 msgid "To change account for this storage, disconnect and connect again:"
 msgstr "?????? ???????????????? ???????? ???????????? ????????????????, ???????????????????? ?? ???????????????????? ????????????:"
 
-#: gui/options.cpp:2825
+#: gui/options.cpp:2834
 msgid "To change account for this storage, disconnect and connect again:"
 msgstr "?????? ???????????????? ???????? ???????????? ????????????????, ???????????????????? ?? ???????????????????? ????????????:"
 
-#: gui/options.cpp:2826
+#: gui/options.cpp:2835
 msgid "Disconnect"
 msgstr "????????????????"
 
-#: gui/options.cpp:2826
+#: gui/options.cpp:2835
 msgid "Stop using this storage on this device"
 msgstr "?????????????? ?????????????????????? ?????????? ?????????????????? ???? ?????????? ????????????????"
 
-#: gui/options.cpp:2829
+#: gui/options.cpp:2838
 #, fuzzy
 #| msgctxt "lowres"
 #| msgid "This storage is not connected yet! To connect,"
@@ -2557,78 +2569,78 @@ msgctxt "lowres"
 msgid "This storage is not connected yet!"
 msgstr "?????????? ???????????????? ?????? ???? ????????????????! ?????? ????????????????,"
 
-#: gui/options.cpp:2831
+#: gui/options.cpp:2840
 #, fuzzy
 #| msgid "This storage is not connected yet! To connect,"
 msgid "This storage is not connected yet!"
 msgstr "?????????? ???????????????? ?????? ???? ????????????????! ?????? ????????????????,"
 
-#: gui/options.cpp:2832
+#: gui/options.cpp:2841
 #, fuzzy
 #| msgid "3. Connect"
 msgid "Connect"
 msgstr "3. ????????????????"
 
-#: gui/options.cpp:2832
+#: gui/options.cpp:2841
 msgid "Connect your cloud storage account"
 msgstr "???????????????? ???????? ???????????? ???????????????????? ????????????????"
 
-#: gui/options.cpp:2845
+#: gui/options.cpp:2854
 msgctxt "lowres"
 msgid "/root/ Path:"
 msgstr "???????? ???? ??????????:"
 
-#: gui/options.cpp:2845 gui/options.cpp:2847 gui/options.cpp:2848
+#: gui/options.cpp:2854 gui/options.cpp:2856 gui/options.cpp:2857
 msgid "Select which directory will be shown as /root/ in the Files Manager"
 msgstr "????????????????, ???????? ?????????? ?????????? ???????????????????? ???? ?????????????????? ?? ???????????????????? ????????????"
 
-#: gui/options.cpp:2847
+#: gui/options.cpp:2856
 msgid "/root/ Path:"
 msgstr "???????? ???? ??????????:"
 
-#: gui/options.cpp:2853
+#: gui/options.cpp:2862
 msgid "Server's port:"
 msgstr "???????? ??????????????:"
 
-#: gui/options.cpp:2853
+#: gui/options.cpp:2862
 msgid "Port for server to use"
 msgstr "???????? ?????? ?????????????????????????? ????????????????"
 
-#: gui/options.cpp:2858
+#: gui/options.cpp:2867
 msgctxt "lowres"
 msgid "Run server to manage files with browser (in the same network)."
 msgstr "???????????????????? ????????????, ?????? ???????????????? ?????????????? ?? ???????????????? (?? ?????????? ??????????)."
 
-#: gui/options.cpp:2859
+#: gui/options.cpp:2868
 msgctxt "lowres"
 msgid "Closing options dialog will stop the server."
 msgstr "?????????????????? ?????????????? ?? ???????????????? ?????????????? ????????????."
 
-#: gui/options.cpp:2861
+#: gui/options.cpp:2870
 msgid "Run server to manage files with browser (in the same network)."
 msgstr "???????????????????? ????????????, ?????? ???????????????? ?????????????? ?? ???????????????? (?? ?????????? ??????????)."
 
-#: gui/options.cpp:2862
+#: gui/options.cpp:2871
 msgid "Closing options dialog will stop the server."
 msgstr "?????????????????? ?????????????? ?? ???????????????? ?????????????? ????????????."
 
-#: gui/options.cpp:2873
+#: gui/options.cpp:2882
 msgid "Use Text to speech"
 msgstr "?????????????????????? ?????????? ?? ????????"
 
-#: gui/options.cpp:2873
+#: gui/options.cpp:2882
 msgid "Will read text in gui on mouse over."
 msgstr "???????????????? ?????????? ???????????????????? ?????? ?????????????????????? ????????."
 
-#: gui/options.cpp:2954
+#: gui/options.cpp:2963
 msgid "Ignore"
 msgstr "????????????????????"
 
-#: gui/options.cpp:2955
+#: gui/options.cpp:2964
 msgid "Disable autosave"
 msgstr "???????????????? ????????????????????????????"
 
-#: gui/options.cpp:2956
+#: gui/options.cpp:2965
 msgid ""
 "WARNING: Autosave was enabled. Some of your games have existing saved games "
 "on the autosave slot. You can either move the existing saves to new slots, "
@@ -2642,7 +2654,7 @@ msgstr ""
 "???????????????????????????? ???????????? ???????????????????????? ??????????).\n"
 "?????????? ??????????????:\n"
 
-#: gui/options.cpp:2965
+#: gui/options.cpp:2974
 msgid ""
 "\n"
 "And more..."
@@ -2650,34 +2662,34 @@ msgstr ""
 "\n"
 "???? ?????????????"
 
-#: gui/options.cpp:2966 engines/engine.cpp:647
+#: gui/options.cpp:2975 engines/engine.cpp:647
 #: engines/access/metaengine.cpp:298 engines/drascula/metaengine.cpp:255
 #: engines/queen/metaengine.cpp:269 engines/sherlock/metaengine.cpp:410
 #: engines/ultima/nuvie/metaengine.cpp:71
 msgid "Move"
 msgstr "????????????????????????"
 
-#: gui/options.cpp:2985
+#: gui/options.cpp:2994
 msgid "ERROR: Failed to move the following saved games:\n"
 msgstr "??????????????: ???? ???????????? ???????????????????????? ?????????? ?????????????????? ????????????:\n"
 
-#: gui/options.cpp:3081
+#: gui/options.cpp:3090
 msgid "Failed to change cloud storage!"
 msgstr "???? ???????????? ???????????????? ?????????????????? ????????????????!"
 
-#: gui/options.cpp:3084
+#: gui/options.cpp:3093
 msgid "Another cloud storage is already active."
 msgstr "?????? ?????????????? ?????????? ?????????????????? ????????????????."
 
-#: gui/options.cpp:3199
+#: gui/options.cpp:3212
 msgid "Theme does not support selected language!"
 msgstr "???????? ???? ???????????????????????? ???????????????? ????????!"
 
-#: gui/options.cpp:3202
+#: gui/options.cpp:3215
 msgid "Theme cannot be loaded!"
 msgstr "?????????????????? ???????????????????? ????????!"
 
-#: gui/options.cpp:3205
+#: gui/options.cpp:3218
 msgid ""
 "\n"
 "Misc settings will be restored."
@@ -2685,46 +2697,46 @@ msgstr ""
 "\n"
 "???????????? ?? ?????????????????? ???????????? ??????????????????."
 
-#: gui/options.cpp:3277
+#: gui/options.cpp:3290
 msgid "Select directory for GUI themes"
 msgstr "???????????????? ?????????? ?????? ?????? ????????????????????"
 
-#: gui/options.cpp:3287
+#: gui/options.cpp:3300
 msgid "Select directory for GUI launcher thumbnails"
 msgstr "???????????????? ?????????? ?????? ?????????????? ????????????????????"
 
-#: gui/options.cpp:3298
+#: gui/options.cpp:3311
 #, fuzzy
 #| msgid "Select directory for plugins"
 msgid "Select directory for DLC downloads"
 msgstr "???????????????? ?????????? ?????? ????????????????"
 
-#: gui/options.cpp:3309
+#: gui/options.cpp:3322
 msgid "Select directory for extra files"
 msgstr "???????????????? ?????????? ?????? ???????????????????? ????????????"
 
-#: gui/options.cpp:3320
+#: gui/options.cpp:3333
 msgid "Select directory for plugins"
 msgstr "???????????????? ?????????? ?????? ????????????????"
 
-#: gui/options.cpp:3333
+#: gui/options.cpp:3346
 msgid "Select directory for Files Manager /root/"
 msgstr "???????????????? ?????????????????? ?????????? ?????? ?????????????????????? ????????????"
 
-#: gui/options.cpp:3485
+#: gui/options.cpp:3498
 msgid "This Storage is working now. Do you want to interrupt it?"
 msgstr "?????????? ???????????????? ???????????? ??????????. ???? ???????????? ???? ???????????????? ???????"
 
-#: gui/options.cpp:3494
+#: gui/options.cpp:3507
 msgid "Wait until current Storage finishes up and try again."
 msgstr ""
 "??????????????????, ???????????? ???????????????????? ???????????????? ???? ???????????????? ?????????? ?? ?????????????????????? ????????????."
 
-#: gui/options.cpp:3691 backends/cloud/cloudmanager.cpp:157
+#: gui/options.cpp:3704 backends/cloud/cloudmanager.cpp:157
 msgid "<right now>"
 msgstr "<??????????>"
 
-#: gui/options.cpp:3834
+#: gui/options.cpp:3847
 msgid ""
 "Request failed.\n"
 "Check your Internet connection."
@@ -2732,13 +2744,13 @@ msgstr ""
 "?????????????? ????????????.\n"
 "???????????????? ?????????? ?????????????????? ?? ????????????????????."
 
-#: gui/options.cpp:3857
+#: gui/options.cpp:3870
 msgid ""
 "A test pattern should be displayed.\n"
 "Do you want to keep these shader scaler settings?"
 msgstr ""
 
-#: gui/options.cpp:3860
+#: gui/options.cpp:3873
 #, c-format
 msgid "Reverting automatically in %d seconds"
 msgstr ""
@@ -2831,7 +2843,7 @@ msgstr "????????????????"
 msgid "Playback"
 msgstr "??????????????????"
 
-#: gui/recorderdialog.cpp:76 gui/widgets/grid.cpp:341
+#: gui/recorderdialog.cpp:76 gui/widgets/grid.cpp:343
 msgid "Edit"
 msgstr "????????????????????"
 
@@ -3046,11 +3058,11 @@ msgstr ""
 msgid "Clear value"
 msgstr "?????????????????? ??????????????????"
 
-#: gui/widgets/grid.cpp:335
+#: gui/widgets/grid.cpp:337
 msgid "Play"
 msgstr "????????????"
 
-#: gui/widgets/grid.cpp:338
+#: gui/widgets/grid.cpp:340
 msgid "Saves"
 msgstr "????????????"
 
diff --git a/po/be_BY.po b/po/be_BY.po
index 858f69bc54a..0469eb04636 100644
--- a/po/be_BY.po
+++ b/po/be_BY.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-13 10:20+0000\n"
+"POT-Creation-Date: 2026-06-13 13:52+0000\n"
 "PO-Revision-Date: 2021-03-21 08:24+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Belarusian <https://translations.scummvm.org/projects/scummvm/"
@@ -122,7 +122,7 @@ msgstr "??????????"
 #: gui/downloadpacksdialog.cpp:465 gui/editgamedialog.cpp:340
 #: gui/editrecorddialog.cpp:66 gui/integrity-dialog.cpp:122
 #: gui/integrity-dialog.cpp:253 gui/filebrowser-dialog.cpp:68
-#: gui/fluidsynth-dialog.cpp:153 gui/massadd.cpp:93 gui/options.cpp:2416
+#: gui/fluidsynth-dialog.cpp:153 gui/massadd.cpp:93 gui/options.cpp:2418
 #: gui/predictivedialog.cpp:68 gui/printing-dialog.cpp:96
 #: gui/recorderdialog.cpp:72 gui/recorderdialog.cpp:211
 #: gui/remotebrowser.cpp:59 gui/saveload-dialog.cpp:64
@@ -197,23 +197,23 @@ msgid ""
 "so your browser can forward data to ScummVM"
 msgstr ""
 
-#: gui/cloudconnectionwizard.cpp:257 gui/options.cpp:3776
+#: gui/cloudconnectionwizard.cpp:257 gui/options.cpp:3789
 msgid "Stop server"
 msgstr "?????????????? ????????????"
 
-#: gui/cloudconnectionwizard.cpp:257 gui/options.cpp:2840
+#: gui/cloudconnectionwizard.cpp:257 gui/options.cpp:2849
 msgid "Run server"
 msgstr "?????????????????? ????????????"
 
-#: gui/cloudconnectionwizard.cpp:258 gui/options.cpp:3777
+#: gui/cloudconnectionwizard.cpp:258 gui/options.cpp:3790
 msgid "Stop local webserver"
 msgstr "???????????? ???????????????? ??????-????????????"
 
-#: gui/cloudconnectionwizard.cpp:258 gui/options.cpp:2840
+#: gui/cloudconnectionwizard.cpp:258 gui/options.cpp:2849
 msgid "Run local webserver"
 msgstr "???????????????? ???????????????? ??????-????????????"
 
-#: gui/cloudconnectionwizard.cpp:266 gui/options.cpp:2841 gui/options.cpp:3784
+#: gui/cloudconnectionwizard.cpp:266 gui/options.cpp:2850 gui/options.cpp:3797
 msgid "Not running"
 msgstr "???? ??????????????????"
 
@@ -347,8 +347,8 @@ msgstr "?????????? ???????????? ?????????? ????????????????. ??????????????????
 #: gui/cloudconnectionwizard.cpp:530 gui/downloaddialog.cpp:118
 #: gui/downloaddialog.cpp:152 gui/filebrowser-dialog.cpp:135
 #: gui/fluidsynth-dialog.cpp:210 gui/launcher.cpp:428 gui/launcher.cpp:457
-#: gui/launcher.cpp:534 gui/launcher.cpp:1938 gui/options.cpp:3485
-#: gui/options.cpp:3859 base/main.cpp:608
+#: gui/launcher.cpp:534 gui/launcher.cpp:1970 gui/options.cpp:3498
+#: gui/options.cpp:3872 base/main.cpp:608
 #: backends/events/default/default-events.cpp:193 engines/buried/buried.cpp:598
 #: engines/chamber/metaengine.cpp:91 engines/director/events.cpp:122
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:139
@@ -367,8 +367,8 @@ msgstr "??????"
 #: gui/cloudconnectionwizard.cpp:530 gui/downloaddialog.cpp:118
 #: gui/downloaddialog.cpp:153 gui/filebrowser-dialog.cpp:135
 #: gui/fluidsynth-dialog.cpp:210 gui/launcher.cpp:428 gui/launcher.cpp:457
-#: gui/launcher.cpp:534 gui/launcher.cpp:1939 gui/options.cpp:3485
-#: gui/options.cpp:3859 engines/buried/buried.cpp:598
+#: gui/launcher.cpp:534 gui/launcher.cpp:1971 gui/options.cpp:3498
+#: gui/options.cpp:3872 engines/buried/buried.cpp:598
 #: engines/chamber/metaengine.cpp:97 engines/director/events.cpp:122
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:139
 #: engines/efh/metaengine.cpp:368 engines/grim/grim.cpp:366
@@ -397,7 +397,7 @@ msgstr ""
 #: gui/editrecorddialog.cpp:67 gui/integrity-dialog.cpp:260
 #: gui/fluidsynth-dialog.cpp:154 gui/massadd.cpp:90 gui/message.cpp:156
 #: gui/message.cpp:160 gui/message.cpp:231 gui/message.cpp:270
-#: gui/message.cpp:274 gui/options.cpp:2418 gui/predictivedialog.cpp:69
+#: gui/message.cpp:274 gui/options.cpp:2420 gui/predictivedialog.cpp:69
 #: gui/saveload-dialog.cpp:1244 base/main.cpp:675 base/main.cpp:700
 #: dists/android.strings.xml.cpp:33 engines/engine.cpp:846
 #: backends/platform/wii/options.cpp:46 engines/buried/buried.cpp:622
@@ -597,11 +597,11 @@ msgstr ""
 msgid "Proceed"
 msgstr "????????????????????"
 
-#: gui/dump-all-dialogs.cpp:123 gui/options.cpp:3347
+#: gui/dump-all-dialogs.cpp:123 gui/options.cpp:3360
 msgid "icon packs"
 msgstr ""
 
-#: gui/dump-all-dialogs.cpp:127 gui/options.cpp:3358
+#: gui/dump-all-dialogs.cpp:127 gui/options.cpp:3371
 msgid "shader packs"
 msgstr ""
 
@@ -613,11 +613,11 @@ msgstr "?????????????? ????????????:"
 msgid "Game"
 msgstr "????????????"
 
-#: gui/editgamedialog.cpp:164 gui/options.cpp:2075 gui/options.cpp:2254
+#: gui/editgamedialog.cpp:164 gui/options.cpp:2075 gui/options.cpp:2256
 msgid "GFX"
 msgstr "??????"
 
-#: gui/editgamedialog.cpp:164 gui/options.cpp:2075 gui/options.cpp:2254
+#: gui/editgamedialog.cpp:164 gui/options.cpp:2075 gui/options.cpp:2256
 #: backends/graphics/atari/atari-graphics.cpp:1056
 #: backends/graphics/sdl/sdl-graphics.cpp:487
 msgid "Graphics"
@@ -632,11 +632,11 @@ msgctxt "lowres"
 msgid "Override global graphic settings"
 msgstr "?????????????????? ???????????????????? ???????????? ??????????????"
 
-#: gui/editgamedialog.cpp:185 gui/options.cpp:2288 engines/dialogs.cpp:354
+#: gui/editgamedialog.cpp:185 gui/options.cpp:2290 engines/dialogs.cpp:354
 msgid "Keymaps"
 msgstr "?????????????? ????????????"
 
-#: gui/editgamedialog.cpp:197 gui/options.cpp:2300 engines/dialogs.cpp:366
+#: gui/editgamedialog.cpp:197 gui/options.cpp:2302 engines/dialogs.cpp:366
 msgid "Backend"
 msgstr ""
 
@@ -651,7 +651,7 @@ msgctxt "lowres"
 msgid "Override global backend settings"
 msgstr "?????????????????? ???????????????????? ???????????? ??????????"
 
-#: gui/editgamedialog.cpp:220 gui/options.cpp:2318 engines/dialogs.cpp:329
+#: gui/editgamedialog.cpp:220 gui/options.cpp:2320 engines/dialogs.cpp:329
 msgid "Audio"
 msgstr "??????????"
 
@@ -664,11 +664,11 @@ msgctxt "lowres"
 msgid "Override global audio settings"
 msgstr "?????????????????? ???????????????????? ???????????? ??????????"
 
-#: gui/editgamedialog.cpp:234 gui/options.cpp:2323
+#: gui/editgamedialog.cpp:234 gui/options.cpp:2325
 msgid "Volume"
 msgstr "????????????????"
 
-#: gui/editgamedialog.cpp:236 gui/options.cpp:2325
+#: gui/editgamedialog.cpp:236 gui/options.cpp:2327
 msgctxt "lowres"
 msgid "Volume"
 msgstr "????????????????"
@@ -682,7 +682,7 @@ msgctxt "lowres"
 msgid "Override global volume settings"
 msgstr "?????????????????? ???????????????????? ???????????? ????????????????"
 
-#: gui/editgamedialog.cpp:252 gui/options.cpp:2333
+#: gui/editgamedialog.cpp:252 gui/options.cpp:2335
 msgid "MIDI"
 msgstr "MIDI"
 
@@ -695,7 +695,7 @@ msgctxt "lowres"
 msgid "Override global MIDI settings"
 msgstr "?????????????????? ???????????????????? ???????????? MIDI"
 
-#: gui/editgamedialog.cpp:267 gui/options.cpp:2339
+#: gui/editgamedialog.cpp:267 gui/options.cpp:2341
 msgid "MT-32"
 msgstr "MT-32"
 
@@ -708,11 +708,11 @@ msgctxt "lowres"
 msgid "Override global MT-32 settings"
 msgstr "?????????????????? ???????????????????? ???????????? MT-32"
 
-#: gui/editgamedialog.cpp:281 gui/options.cpp:2346
+#: gui/editgamedialog.cpp:281 gui/options.cpp:2348
 msgid "Paths"
 msgstr "??????????"
 
-#: gui/editgamedialog.cpp:283 gui/options.cpp:2348
+#: gui/editgamedialog.cpp:283 gui/options.cpp:2350
 msgctxt "lowres"
 msgid "Paths"
 msgstr "??????????"
@@ -734,7 +734,7 @@ msgctxt "lowres"
 msgid "Game Path:"
 msgstr "???????? ???? ????????????:"
 
-#: gui/editgamedialog.cpp:303 gui/options.cpp:2555
+#: gui/editgamedialog.cpp:303 gui/options.cpp:2557
 msgid "Extra Path:"
 msgstr "??????. ????????:"
 
@@ -743,33 +743,33 @@ msgstr "??????. ????????:"
 msgid "Specifies path to additional data used by the game"
 msgstr "???????????????? ???????? ???? ???????????????????? ????????????, ???????????????? ?????? ????????????"
 
-#: gui/editgamedialog.cpp:305 gui/options.cpp:2557
+#: gui/editgamedialog.cpp:305 gui/options.cpp:2559
 msgctxt "lowres"
 msgid "Extra Path:"
 msgstr "??????. ????????:"
 
-#: gui/editgamedialog.cpp:306 gui/options.cpp:2530 gui/options.cpp:2558
-#: gui/options.cpp:2567 gui/options.cpp:2848
+#: gui/editgamedialog.cpp:306 gui/options.cpp:2532 gui/options.cpp:2560
+#: gui/options.cpp:2569 gui/options.cpp:2857
 #: backends/platform/libretro/src/libretro-options-widget.cpp:277
 msgctxt "path"
 msgid "None"
 msgstr "???? ??????????????????"
 
-#: gui/editgamedialog.cpp:312 gui/options.cpp:2519
+#: gui/editgamedialog.cpp:312 gui/options.cpp:2521
 msgid "Save Path:"
 msgstr "?????????????????? ??????????????:"
 
 #: gui/editgamedialog.cpp:312 gui/editgamedialog.cpp:314
-#: gui/editgamedialog.cpp:315 gui/options.cpp:2519 gui/options.cpp:2521
+#: gui/editgamedialog.cpp:315 gui/options.cpp:2521 gui/options.cpp:2523
 msgid "Specifies where your saved games are put"
 msgstr "???????????????? ???????? ???? ???????????????????? ????????????"
 
-#: gui/editgamedialog.cpp:314 gui/options.cpp:2521
+#: gui/editgamedialog.cpp:314 gui/options.cpp:2523
 msgctxt "lowres"
 msgid "Save Path:"
 msgstr "?????????????????? ??????????????:"
 
-#: gui/editgamedialog.cpp:315 gui/options.cpp:1844 gui/options.cpp:2522
+#: gui/editgamedialog.cpp:315 gui/options.cpp:1844 gui/options.cpp:2524
 #: backends/platform/wii/options.cpp:55
 msgid "Default"
 msgstr "??????????????"
@@ -830,8 +830,8 @@ msgstr ""
 #: gui/editgamedialog.cpp:365 gui/editgamedialog.cpp:380 gui/options.cpp:1613
 #: gui/options.cpp:1627 gui/options.cpp:1644 gui/options.cpp:1658
 #: gui/options.cpp:1675 gui/options.cpp:1713 gui/options.cpp:1729
-#: gui/options.cpp:2144 gui/options.cpp:2600 gui/options.cpp:2674
-#: gui/options.cpp:2888 gui/options.cpp:3389 audio/null.cpp:45
+#: gui/options.cpp:2144 gui/options.cpp:2602 gui/options.cpp:2683
+#: gui/options.cpp:2897 gui/options.cpp:3402 audio/null.cpp:45
 #: backends/platform/android/options.cpp:157
 #: backends/platform/android/options.cpp:158
 #: backends/platform/android/options.cpp:186
@@ -858,7 +858,7 @@ msgctxt "lowres"
 msgid "Platform:"
 msgstr "??????????????????:"
 
-#: gui/editgamedialog.cpp:566 gui/options.cpp:3413
+#: gui/editgamedialog.cpp:566 gui/options.cpp:3426
 msgid "Select SoundFont"
 msgstr "?????????????? SoundFont"
 
@@ -866,11 +866,11 @@ msgstr "?????????????? SoundFont"
 msgid "Select additional game directory"
 msgstr "?????????????? ???????????????????? ???????????????????? ????????????"
 
-#: gui/editgamedialog.cpp:618 gui/options.cpp:3261
+#: gui/editgamedialog.cpp:618 gui/options.cpp:3274
 msgid "Select directory for saved games"
 msgstr "?????????????? ???????????????????? ?????? ????????????????????"
 
-#: gui/editgamedialog.cpp:625 gui/options.cpp:3268
+#: gui/editgamedialog.cpp:625 gui/options.cpp:3281
 msgid "The chosen directory cannot be written to. Please select another one."
 msgstr "???? ???????? ???????????? ?? ?????????????? ????????????????????. ???????? ??????????, ?????????????? ??????????."
 
@@ -1015,7 +1015,7 @@ msgstr "??????????????????"
 msgid "Triangle"
 msgstr "????????????????????"
 
-#: gui/fluidsynth-dialog.cpp:139 gui/options.cpp:2366
+#: gui/fluidsynth-dialog.cpp:139 gui/options.cpp:2368
 msgid "Misc"
 msgstr "????????????"
 
@@ -1053,7 +1053,7 @@ msgid ""
 "Do you really want to reset all FluidSynth settings to their default values?"
 msgstr "???? ???????????????? ?????????????? ?????????????? ???????????? FluidSynth ?? ?????????????????"
 
-#: gui/gui-manager.cpp:159 gui/options.cpp:2357
+#: gui/gui-manager.cpp:159 gui/options.cpp:2359
 msgid "GUI"
 msgstr "???????????????? ??????????????????"
 
@@ -1268,7 +1268,7 @@ msgid ""
 "and illegal game copies.\n"
 msgstr ""
 
-#: gui/helpdialog.cpp:68 gui/options.cpp:2377
+#: gui/helpdialog.cpp:68 gui/options.cpp:2379
 msgid "Cloud"
 msgstr "??????????????"
 
@@ -1574,92 +1574,92 @@ msgstr "???????????? ??????????"
 msgid "Grid view"
 msgstr "???????????? ??????????"
 
-#: gui/launcher.cpp:1145
+#: gui/launcher.cpp:1168
 msgid "~S~tart"
 msgstr "??~??~????"
 
-#: gui/launcher.cpp:1145
+#: gui/launcher.cpp:1168
 msgid "Start selected game"
 msgstr "?????????????????? ?????????????? ????????????"
 
-#: gui/launcher.cpp:1148
+#: gui/launcher.cpp:1171
 msgid "~L~oad..."
 msgstr "~??~????????????????..."
 
-#: gui/launcher.cpp:1148
+#: gui/launcher.cpp:1171
 msgid "Load saved game for selected game"
 msgstr "?????????????????? ?????????????????? ?????? ?????????????? ????????????"
 
-#: gui/launcher.cpp:1150
+#: gui/launcher.cpp:1173
 msgid "Record..."
 msgstr "??????????..."
 
-#: gui/launcher.cpp:1156
+#: gui/launcher.cpp:1179
 #, fuzzy
 msgid "~G~ame Options..."
 msgstr "~??~??????????..."
 
-#: gui/launcher.cpp:1156
+#: gui/launcher.cpp:1179
 msgid "Change game options"
 msgstr "?????????????? ???????????? ????????????"
 
-#: gui/launcher.cpp:1156
+#: gui/launcher.cpp:1179
 #, fuzzy
 msgctxt "lowres"
 msgid "~G~ame Opts..."
 msgstr "~??~??????????..."
 
-#: gui/launcher.cpp:1242
+#: gui/launcher.cpp:1265
 msgid "Add-on"
 msgstr ""
 
 #. I18N: List grouping when no engine is specified
-#: gui/launcher.cpp:1309 gui/launcher.cpp:1554
+#: gui/launcher.cpp:1335 gui/launcher.cpp:1580
 #, fuzzy
 msgid "Unknown Engine"
 msgstr "???????????????? ??????????????"
 
 #. I18N: List grouping when no publisher is specified
 #. I18N: List group when no publisher is specified
-#: gui/launcher.cpp:1326 gui/launcher.cpp:1571
+#: gui/launcher.cpp:1352 gui/launcher.cpp:1597
 #, fuzzy
 msgid "Unknown Publisher"
 msgstr "???????????????? ??????????"
 
 #. I18N: List group when no game series is specified
 #. I18N: List grouping when no game series is specified
-#: gui/launcher.cpp:1343 gui/launcher.cpp:1588
+#: gui/launcher.cpp:1369 gui/launcher.cpp:1614
 msgid "No Series"
 msgstr ""
 
 #. I18N: List group when no language is specified
-#: gui/launcher.cpp:1357 gui/launcher.cpp:1602
+#: gui/launcher.cpp:1383 gui/launcher.cpp:1628
 msgid "Language not detected"
 msgstr ""
 
 #. I18N: List group when no platform is specified
-#: gui/launcher.cpp:1371 gui/launcher.cpp:1616
+#: gui/launcher.cpp:1397 gui/launcher.cpp:1642
 msgid "Platform not detected"
 msgstr ""
 
 #. I18N: List group when no year is specified
-#: gui/launcher.cpp:1388 gui/launcher.cpp:1633
+#: gui/launcher.cpp:1414 gui/launcher.cpp:1659
 #, fuzzy
 #| msgid "Unknown error"
 msgid "Unknown Year"
 msgstr "?????????????????? ??????????????"
 
 #. I18N: Group for All items
-#: gui/launcher.cpp:1640 gui/widgets/groupedlist.cpp:82
+#: gui/launcher.cpp:1666 gui/widgets/groupedlist.cpp:82
 #, fuzzy
 msgid "All"
 msgstr "Alt"
 
-#: gui/launcher.cpp:1812
+#: gui/launcher.cpp:1844
 msgid "Icons per row:"
 msgstr ""
 
-#: gui/launcher.cpp:1869
+#: gui/launcher.cpp:1901
 #, fuzzy, c-format
 #| msgid "Do you really want to remove this game configuration?"
 msgid ""
@@ -2162,190 +2162,202 @@ msgctxt "lowres"
 msgid "Speech volume:"
 msgstr "????????. ????????????:"
 
-#: gui/options.cpp:2266
+#: gui/options.cpp:2268
 msgid "Control"
 msgstr "??????????????????"
 
-#: gui/options.cpp:2379
+#: gui/options.cpp:2381
 msgctxt "lowres"
 msgid "Cloud"
 msgstr "??????????????"
 
-#: gui/options.cpp:2394
+#: gui/options.cpp:2396
 msgid "LAN"
 msgstr "LAN"
 
-#: gui/options.cpp:2396
+#: gui/options.cpp:2398
 msgctxt "lowres"
 msgid "LAN"
 msgstr "LAN"
 
-#: gui/options.cpp:2405
+#: gui/options.cpp:2407
 msgid "Accessibility"
 msgstr "??????????????????????"
 
-#: gui/options.cpp:2407
+#: gui/options.cpp:2409
 msgctxt "lowres"
 msgid "Accessibility"
 msgstr "??????????????????????"
 
-#: gui/options.cpp:2416
+#: gui/options.cpp:2418
 msgid "Discard changes and close the dialog"
 msgstr ""
 
-#: gui/options.cpp:2417
+#: gui/options.cpp:2419
 msgid "Apply"
 msgstr "??????????"
 
-#: gui/options.cpp:2417
+#: gui/options.cpp:2419
 msgid "Apply changes without closing the dialog"
 msgstr ""
 
-#: gui/options.cpp:2418
+#: gui/options.cpp:2420
 msgid "Apply changes and close the dialog"
 msgstr ""
 
-#: gui/options.cpp:2508
+#: gui/options.cpp:2510
 msgid "FluidSynth Settings"
 msgstr "???????????? FluidSynth"
 
-#: gui/options.cpp:2522
+#: gui/options.cpp:2524
 msgid ""
 "Specifies where your saved games are put. A red coloring indicates the value "
 "is temporary and will not get saved"
 msgstr ""
 
-#: gui/options.cpp:2527
+#: gui/options.cpp:2529
 msgid "Theme Path:"
 msgstr "???????? ???? ??????:"
 
-#: gui/options.cpp:2529
+#: gui/options.cpp:2531
 msgctxt "lowres"
 msgid "Theme Path:"
 msgstr "???????? ???? ??????:"
 
-#: gui/options.cpp:2535
+#: gui/options.cpp:2537
 #, fuzzy
 msgid "Icon Path:"
 msgstr "?????????????????? ????????????????????:"
 
-#: gui/options.cpp:2537
+#: gui/options.cpp:2539
 #, fuzzy
 msgctxt "lowres"
 msgid "Icon Path:"
 msgstr "????????????:"
 
-#: gui/options.cpp:2538 gui/options.cpp:2548
+#: gui/options.cpp:2540 gui/options.cpp:2550
 #, fuzzy
 msgctxt "path"
 msgid "Default"
 msgstr "??????????????"
 
-#: gui/options.cpp:2545
+#: gui/options.cpp:2547
 #, fuzzy
 #| msgid "Game Path:"
 msgid "DLC Path:"
 msgstr "???????? ???? ????????????:"
 
-#: gui/options.cpp:2547
+#: gui/options.cpp:2549
 #, fuzzy
 #| msgid "Game Path:"
 msgctxt "lowres"
 msgid "DLC Path:"
 msgstr "???????? ???? ????????????:"
 
-#: gui/options.cpp:2555 gui/options.cpp:2557 gui/options.cpp:2558
+#: gui/options.cpp:2557 gui/options.cpp:2559 gui/options.cpp:2560
 msgid "Specifies path to additional data used by all games or ScummVM"
 msgstr ""
 "???????????????? ???????? ???? ???????????????????? ???????????? ????????????????, ?????????????????????????????? ?????????? ???????????????? "
 "?????? ScummVM"
 
-#: gui/options.cpp:2564
+#: gui/options.cpp:2566
 msgid "Plugins Path:"
 msgstr "???????? ???? ????????????????:"
 
-#: gui/options.cpp:2566
+#: gui/options.cpp:2568
 msgctxt "lowres"
 msgid "Plugins Path:"
 msgstr "???????? ???? ????????????????:"
 
-#: gui/options.cpp:2576
+#: gui/options.cpp:2578
 msgid "ScummVM config path: "
 msgstr ""
 
-#: gui/options.cpp:2590
+#: gui/options.cpp:2592
 msgid "ScummVM log path: "
 msgstr ""
 
-#: gui/options.cpp:2595
+#: gui/options.cpp:2597
 msgid "View"
 msgstr ""
 
 #. I18N: Referring to the last path memorized when adding a game
-#: gui/options.cpp:2605 gui/options.cpp:3389
+#: gui/options.cpp:2607 gui/options.cpp:3402
 msgid "Last browser path: "
 msgstr ""
 
-#: gui/options.cpp:2610
+#: gui/options.cpp:2612
 msgid "Theme:"
 msgstr "????????:"
 
-#: gui/options.cpp:2615
+#: gui/options.cpp:2617
 #, fuzzy
 msgid "GUI scale:"
 msgstr "?????????????????????? ??????????"
 
-#: gui/options.cpp:2632
+#: gui/options.cpp:2634
 #, c-format
 msgid "%d%% - Custom"
 msgstr ""
 
-#: gui/options.cpp:2636
+#: gui/options.cpp:2638
 msgid "GUI renderer:"
 msgstr "???????????????????????? GUI:"
 
-#: gui/options.cpp:2649
+#: gui/options.cpp:2651
 msgid "Return to the launcher when leaving a game"
 msgstr ""
 
-#: gui/options.cpp:2650
+#: gui/options.cpp:2652
 msgid ""
 "Return to the launcher when leaving a game instead of closing ScummVM\n"
 "(this feature is not supported by all games)."
 msgstr ""
 
-#: gui/options.cpp:2657
+#: gui/options.cpp:2659
 msgid "Ask for confirmation on exit"
 msgstr ""
 
-#: gui/options.cpp:2658
+#: gui/options.cpp:2660
 msgid "Ask for permission when closing ScummVM or leaving a game."
 msgstr ""
 
-#: gui/options.cpp:2664
+#: gui/options.cpp:2666
 #, fuzzy
 msgid "Disable fixed font scaling"
 msgstr "???? ???????????????????? ???????????????? ????????????"
 
-#: gui/options.cpp:2665
+#: gui/options.cpp:2667
 msgid ""
 "Do not upscale fixed size fonts in the GUI. This reduces artefacts on low "
-"resolution screens"
+"resolution screens."
 msgstr ""
 
-#: gui/options.cpp:2671
+#: gui/options.cpp:2673
+#, fuzzy
+#| msgid "Enable smooth scrolling when walking"
+msgid "Enable kinetic scrolling in lists"
+msgstr "???????????????? ?????????????? ?????????????????? ???????????? ????????"
+
+#: gui/options.cpp:2674
+#, fuzzy
+#| msgid "Enable smooth scrolling when walking"
+msgid "Enable smooth, momentum-based scrolling in list widgets."
+msgstr "???????????????? ?????????????? ?????????????????? ???????????? ????????"
+
+#: gui/options.cpp:2680
 msgid "GUI language:"
 msgstr "???????? ????????????????????:"
 
-#: gui/options.cpp:2671
+#: gui/options.cpp:2680
 msgid "Language of ScummVM GUI"
 msgstr "???????? ???????????????????? ???????????????????? ScummVM"
 
-#: gui/options.cpp:2697
+#: gui/options.cpp:2706
 msgid "Switch the ScummVM GUI language to the game language"
 msgstr "???????????????? ???? ???????? ???????????????????? ScummVM ???? ???????? ????????????"
 
-#: gui/options.cpp:2698
+#: gui/options.cpp:2707
 msgid ""
 "When starting a game, change the ScummVM GUI language to the game language. "
 "That way, if a game uses the ScummVM save and load dialogs, they are in the "
@@ -2355,11 +2367,11 @@ msgstr ""
 "??????????????, ???????? ???????????? ?????????????????????????? ?????????????? ?????????????????? ?? ???????????????? ScummVM, ?????? "
 "???????????? ???? ?????? ???? ????????, ?????? ?? ????????????."
 
-#: gui/options.cpp:2710
+#: gui/options.cpp:2719
 msgid "Use native system file browser"
 msgstr "?????????????? ???????????????? ?????????????? ????????????"
 
-#: gui/options.cpp:2711
+#: gui/options.cpp:2720
 msgid ""
 "Use the native system file browser instead of the ScummVM one to select a "
 "file or directory."
@@ -2367,80 +2379,80 @@ msgstr ""
 "?????????????? ???????????????? ?????????????? ???????????? ???????????? ???????????????? ScummVM ?????? ???????????? ?????????? ???? "
 "????????????????."
 
-#: gui/options.cpp:2718
+#: gui/options.cpp:2727
 #, fuzzy
 msgid "Download Icons"
 msgstr "???????????????????? ????????"
 
-#: gui/options.cpp:2718
+#: gui/options.cpp:2727
 #, fuzzy
 msgid "Check on the scummvm.org website for updates of icon packs"
 msgstr "???????????????? ????????????????????..."
 
-#: gui/options.cpp:2724
+#: gui/options.cpp:2733
 msgid "Autosave:"
 msgstr "??????????????????????????:"
 
-#: gui/options.cpp:2726
+#: gui/options.cpp:2735
 msgctxt "lowres"
 msgid "Autosave:"
 msgstr "??????????????????????????:"
 
-#: gui/options.cpp:2737
+#: gui/options.cpp:2746
 msgid "Random seed:"
 msgstr ""
 
-#: gui/options.cpp:2737
+#: gui/options.cpp:2746
 msgid "Seed for initializing all random number generators"
 msgstr ""
 
-#: gui/options.cpp:2745
+#: gui/options.cpp:2754
 #, fuzzy
 msgid "Debug level:"
 msgstr "?????????????????? ????????"
 
 #. I18N: Debug level -1, no messages
-#: gui/options.cpp:2749 gui/options.cpp:2886 audio/fmopl.cpp:83
+#: gui/options.cpp:2758 gui/options.cpp:2895 audio/fmopl.cpp:83
 msgid "None"
 msgstr "????????"
 
 #. I18N: Debug level 11, all messages
-#: gui/options.cpp:2755
+#: gui/options.cpp:2764
 msgid "11 (all)"
 msgstr ""
 
-#: gui/options.cpp:2759
+#: gui/options.cpp:2768
 #, fuzzy
 msgid "Enable Discord integration"
 msgstr "???????????????? ?????????????? ????????????????"
 
-#: gui/options.cpp:2760
+#: gui/options.cpp:2769
 msgid ""
 "Show information about the games you are playing on Discord if the Discord "
 "client is running."
 msgstr ""
 
-#: gui/options.cpp:2770 gui/updates-dialog.cpp:87
+#: gui/options.cpp:2779 gui/updates-dialog.cpp:87
 msgid "Update check:"
 msgstr "?????????????????? ????????????????????:"
 
-#: gui/options.cpp:2770
+#: gui/options.cpp:2779
 msgid "How often to check ScummVM updates"
 msgstr "???? ?????????? ?????????????????? ???????????????????? ScummVM"
 
-#: gui/options.cpp:2781
+#: gui/options.cpp:2790
 msgid "Check now"
 msgstr "?????????????????? ??????????"
 
-#: gui/options.cpp:2788
+#: gui/options.cpp:2797
 msgid "Active storage:"
 msgstr "???????????????? ????????????????:"
 
-#: gui/options.cpp:2788
+#: gui/options.cpp:2797
 msgid "Active cloud storage"
 msgstr "???????????????? ?????????????????? ????????????????"
 
-#: gui/options.cpp:2797
+#: gui/options.cpp:2806
 #, fuzzy
 #| msgctxt "lowres"
 #| msgid ""
@@ -2453,7 +2465,7 @@ msgstr ""
 "4. ???????????????? ???????? ???? ????????????????. ????????????????, ?????? ?????? ?????????????????? ????????????, ?? ???????????????? "
 "??????:"
 
-#: gui/options.cpp:2799
+#: gui/options.cpp:2808
 #, fuzzy
 #| msgid ""
 #| "4. Storage is not yet enabled. Verify that username is correct and enable "
@@ -2464,107 +2476,107 @@ msgstr ""
 "4. ???????????????? ???????? ???? ????????????????. ????????????????, ?????? ?????? ?????????????????? ????????????, ?? ???????????????? "
 "??????:"
 
-#: gui/options.cpp:2800
+#: gui/options.cpp:2809
 msgid "Enable storage"
 msgstr "???????????????? ????????????????"
 
-#: gui/options.cpp:2800
+#: gui/options.cpp:2809
 msgid "Confirm you want to use this account for this storage"
 msgstr ""
 "??????????????????????, ?????? ???????????? ???????????????????????????? ???????? ?????????????? ?????????? ?????? ???????????? ????????????????"
 
-#: gui/options.cpp:2802 backends/platform/wii/options.cpp:113
+#: gui/options.cpp:2811 backends/platform/wii/options.cpp:113
 msgid "Username:"
 msgstr "????????????????:"
 
-#: gui/options.cpp:2802
+#: gui/options.cpp:2811
 msgid "Username used by this storage"
 msgstr "?????? ?????????????????? ?? ?????????? ??????????????"
 
-#: gui/options.cpp:2803
+#: gui/options.cpp:2812
 msgid "<none>"
 msgstr "<????????>"
 
-#: gui/options.cpp:2805
+#: gui/options.cpp:2814
 msgid "Used space:"
 msgstr "?????????????????? ????'????:"
 
-#: gui/options.cpp:2805
+#: gui/options.cpp:2814
 msgid "Space used by ScummVM's saved games on this storage"
 msgstr "????'????, ???????????? ?????????????????????? ?????????????? ScummVM ???? ?????????? ??????????????"
 
-#: gui/options.cpp:2808
+#: gui/options.cpp:2817
 msgid "Last sync:"
 msgstr "?????????????? ??????????????????????????:"
 
-#: gui/options.cpp:2808
+#: gui/options.cpp:2817
 msgid "When was the last time saved games were synced with this storage"
 msgstr "???????? ???????????????? ?????????????? ?????????????????????????? ???????????????? ?????????????? ?? ?????????? ??????????????????"
 
-#: gui/options.cpp:2809 gui/options.cpp:3693
+#: gui/options.cpp:2818 gui/options.cpp:3706
 msgid "<never>"
 msgstr "<????????????>"
 
-#: gui/options.cpp:2811
+#: gui/options.cpp:2820
 msgctxt "lowres"
 msgid "Saved games sync automatically on launch, after saving and on loading."
 msgstr ""
 "?????????????????? ???????????? ???????????????????????????? ?????????????????????? ???????????? ??????????????, ?????????????????? ?? "
 "????????????????."
 
-#: gui/options.cpp:2813
+#: gui/options.cpp:2822
 msgid "Saved games sync automatically on launch, after saving and on loading."
 msgstr ""
 "?????????????????? ???????????? ???????????????????????????? ?????????????????????? ???????????? ??????????????, ?????????????????? ?? "
 "????????????????."
 
-#: gui/options.cpp:2814
+#: gui/options.cpp:2823
 msgid "Sync now"
 msgstr "???????????????????????????? ??????????"
 
-#: gui/options.cpp:2814
+#: gui/options.cpp:2823
 msgid "Start saved games sync"
 msgstr "???????????? ?????????????????????????? ?????????????????? ??????????????"
 
-#: gui/options.cpp:2817
+#: gui/options.cpp:2826
 msgctxt "lowres"
 msgid "You can download game files from your cloud ScummVM folder:"
 msgstr "?????????? ???????????????????? ?????????? ???????????? ?? ?????????? ?????????????? ?????????? ScummVM:"
 
-#: gui/options.cpp:2819
+#: gui/options.cpp:2828
 msgid "You can download game files from your cloud ScummVM folder:"
 msgstr "?????????? ???????????????????? ?????????? ???????????? ?? ?????????? ?????????????? ?????????? ScummVM:"
 
-#: gui/options.cpp:2820
+#: gui/options.cpp:2829
 msgid "Download game files"
 msgstr "???????????????????? ?????????? ????????????"
 
-#: gui/options.cpp:2820
+#: gui/options.cpp:2829
 msgid "Open downloads manager dialog"
 msgstr "???????????????? ???????????????? ????????????????"
 
-#: gui/options.cpp:2823
+#: gui/options.cpp:2832
 msgctxt "lowres"
 msgid "To change account for this storage, disconnect and connect again:"
 msgstr ""
 "?????? ?????????????? ?????????????? ?????????? ?????? ???????????? ????????????????, ?????????????????????? ?? ???????????????????????? "
 "????????:"
 
-#: gui/options.cpp:2825
+#: gui/options.cpp:2834
 msgid "To change account for this storage, disconnect and connect again:"
 msgstr ""
 "?????? ?????????????? ?????????????? ?????????? ?????? ???????????? ????????????????, ?????????????????????? ?? ???????????????????????? "
 "????????:"
 
-#: gui/options.cpp:2826
+#: gui/options.cpp:2835
 msgid "Disconnect"
 msgstr "??????????????????"
 
-#: gui/options.cpp:2826
+#: gui/options.cpp:2835
 msgid "Stop using this storage on this device"
 msgstr "?????????????? ???????????????????? ?????????????????? ???? ?????????? ????????????????"
 
-#: gui/options.cpp:2829
+#: gui/options.cpp:2838
 #, fuzzy
 #| msgctxt "lowres"
 #| msgid "This storage is not connected yet! To connect,"
@@ -2572,79 +2584,79 @@ msgctxt "lowres"
 msgid "This storage is not connected yet!"
 msgstr "???????????????? ???????? ???? ????????????????????! ?????? ????????????????????,"
 
-#: gui/options.cpp:2831
+#: gui/options.cpp:2840
 #, fuzzy
 #| msgid "This storage is not connected yet! To connect,"
 msgid "This storage is not connected yet!"
 msgstr "???????????????? ???????? ???? ????????????????????! ?????? ????????????????????,"
 
-#: gui/options.cpp:2832
+#: gui/options.cpp:2841
 #, fuzzy
 #| msgid "3. Connect"
 msgid "Connect"
 msgstr "3. ????????????????????"
 
-#: gui/options.cpp:2832
+#: gui/options.cpp:2841
 msgid "Connect your cloud storage account"
 msgstr "???????????????????? ?????????????? ?????????? ???????????????? ????????????????"
 
-#: gui/options.cpp:2845
+#: gui/options.cpp:2854
 msgctxt "lowres"
 msgid "/root/ Path:"
 msgstr "????????????:"
 
-#: gui/options.cpp:2845 gui/options.cpp:2847 gui/options.cpp:2848
+#: gui/options.cpp:2854 gui/options.cpp:2856 gui/options.cpp:2857
 msgid "Select which directory will be shown as /root/ in the Files Manager"
 msgstr "????????????????, ?????? ?????????????? ?????????????????? ???? /root/ ?? ???????????????????? ????????????"
 
-#: gui/options.cpp:2847
+#: gui/options.cpp:2856
 msgid "/root/ Path:"
 msgstr "?????????????????? ????????????????????:"
 
-#: gui/options.cpp:2853
+#: gui/options.cpp:2862
 msgid "Server's port:"
 msgstr "???????? ??????????????:"
 
-#: gui/options.cpp:2853
+#: gui/options.cpp:2862
 msgid "Port for server to use"
 msgstr "???????? ?????? ???????????????????????? ????????????????"
 
-#: gui/options.cpp:2858
+#: gui/options.cpp:2867
 msgctxt "lowres"
 msgid "Run server to manage files with browser (in the same network)."
 msgstr "?????????????????? ????????????, ?????? ???????????????? ?????????????? ?? ???????????????? (?? ?????????? ??????????)."
 
-#: gui/options.cpp:2859
+#: gui/options.cpp:2868
 msgctxt "lowres"
 msgid "Closing options dialog will stop the server."
 msgstr "?????????????????? ???????? ???????????????????? ?????????????? ????????????."
 
-#: gui/options.cpp:2861
+#: gui/options.cpp:2870
 msgid "Run server to manage files with browser (in the same network)."
 msgstr "?????????????????? ????????????, ?????? ???????????????? ?????????????? ?? ???????????????? (?? ?????????? ??????????)."
 
-#: gui/options.cpp:2862
+#: gui/options.cpp:2871
 msgid "Closing options dialog will stop the server."
 msgstr "?????????????????? ?????????????????????? ???????? ???????????????????? ?????????????? ????????????."
 
-#: gui/options.cpp:2873
+#: gui/options.cpp:2882
 msgid "Use Text to speech"
 msgstr "?????????????? ?????????????????? ????????"
 
-#: gui/options.cpp:2873
+#: gui/options.cpp:2882
 msgid "Will read text in gui on mouse over."
 msgstr "???????????????? ?????????? ?? ???????????????????? ?????? ???????????????????? ????????."
 
-#: gui/options.cpp:2954
+#: gui/options.cpp:2963
 msgid "Ignore"
 msgstr ""
 
-#: gui/options.cpp:2955
+#: gui/options.cpp:2964
 #, fuzzy
 msgid "Disable autosave"
 msgstr "???? ???????????????????? ???????????????? ????????????"
 
-#: gui/options.cpp:2956
+#: gui/options.cpp:2965
 msgid ""
 "WARNING: Autosave was enabled. Some of your games have existing saved games "
 "on the autosave slot. You can either move the existing saves to new slots, "
@@ -2653,14 +2665,14 @@ msgid ""
 "List of games:\n"
 msgstr ""
 
-#: gui/options.cpp:2965
+#: gui/options.cpp:2974
 #, fuzzy
 msgid ""
 "\n"
 "And more..."
 msgstr "???????????????? ????????..."
 
-#: gui/options.cpp:2966 engines/engine.cpp:647
+#: gui/options.cpp:2975 engines/engine.cpp:647
 #: engines/access/metaengine.cpp:298 engines/drascula/metaengine.cpp:255
 #: engines/queen/metaengine.cpp:269 engines/sherlock/metaengine.cpp:410
 #: engines/ultima/nuvie/metaengine.cpp:71
@@ -2668,27 +2680,27 @@ msgstr "???????????????? ????????..."
 msgid "Move"
 msgstr "?????????????? ??????????"
 
-#: gui/options.cpp:2985
+#: gui/options.cpp:2994
 msgid "ERROR: Failed to move the following saved games:\n"
 msgstr ""
 
-#: gui/options.cpp:3081
+#: gui/options.cpp:3090
 msgid "Failed to change cloud storage!"
 msgstr "???? ???????????????????? ?????????????? ??????????????!"
 
-#: gui/options.cpp:3084
+#: gui/options.cpp:3093
 msgid "Another cloud storage is already active."
 msgstr "?????? ?????????????? ?????????? ?????????????????? ????????????????."
 
-#: gui/options.cpp:3199
+#: gui/options.cpp:3212
 msgid "Theme does not support selected language!"
 msgstr "???????? ???? ???????????????????? ?????????????? ????????!"
 
-#: gui/options.cpp:3202
+#: gui/options.cpp:3215
 msgid "Theme cannot be loaded!"
 msgstr "?????????????????? ?????????????????? ????????!"
 
-#: gui/options.cpp:3205
+#: gui/options.cpp:3218
 msgid ""
 "\n"
 "Misc settings will be restored."
@@ -2696,46 +2708,46 @@ msgstr ""
 "\n"
 "???????????? ???? ???????????????? \"????????????\" ???????????? ??????????????????."
 
-#: gui/options.cpp:3277
+#: gui/options.cpp:3290
 msgid "Select directory for GUI themes"
 msgstr "?????????????? ???????????????????? ?????? ?????? GUI"
 
-#: gui/options.cpp:3287
+#: gui/options.cpp:3300
 #, fuzzy
 msgid "Select directory for GUI launcher thumbnails"
 msgstr "?????????????? ???????????????????? ?????? ?????? GUI"
 
-#: gui/options.cpp:3298
+#: gui/options.cpp:3311
 #, fuzzy
 #| msgid "Select directory for plugins"
 msgid "Select directory for DLC downloads"
 msgstr "?????????????? ???????????????????? ?? ??????????????????"
 
-#: gui/options.cpp:3309
+#: gui/options.cpp:3322
 msgid "Select directory for extra files"
 msgstr "?????????????? ???????????????????? ?? ?????????????????????? ??????????????"
 
-#: gui/options.cpp:3320
+#: gui/options.cpp:3333
 msgid "Select directory for plugins"
 msgstr "?????????????? ???????????????????? ?? ??????????????????"
 
-#: gui/options.cpp:3333
+#: gui/options.cpp:3346
 msgid "Select directory for Files Manager /root/"
 msgstr "?????????????? ???????????????????? ?????? ???????????? ?? ?????????????????? ????????????"
 
-#: gui/options.cpp:3485
+#: gui/options.cpp:3498
 msgid "This Storage is working now. Do you want to interrupt it?"
 msgstr "?????????? ???????????????? ?????????? ????????????. ???????????????????? ???????"
 
-#: gui/options.cpp:3494
+#: gui/options.cpp:3507
 msgid "Wait until current Storage finishes up and try again."
 msgstr "??????????????????, ???????????? ?????????????? ?????????????? ???????????????? ????????????, ?? ?????????????????????? ????????."
 
-#: gui/options.cpp:3691 backends/cloud/cloudmanager.cpp:157
+#: gui/options.cpp:3704 backends/cloud/cloudmanager.cpp:157
 msgid "<right now>"
 msgstr "<??????????>"
 
-#: gui/options.cpp:3834
+#: gui/options.cpp:3847
 msgid ""
 "Request failed.\n"
 "Check your Internet connection."
@@ -2743,13 +2755,13 @@ msgstr ""
 "?????????????? ????????????.\n"
 "???????????????? ???????? ????????????????-????????????????."
 
-#: gui/options.cpp:3857
+#: gui/options.cpp:3870
 msgid ""
 "A test pattern should be displayed.\n"
 "Do you want to keep these shader scaler settings?"
 msgstr ""
 
-#: gui/options.cpp:3860
+#: gui/options.cpp:3873
 #, c-format
 msgid "Reverting automatically in %d seconds"
 msgstr ""
@@ -2843,7 +2855,7 @@ msgstr "????????????????"
 msgid "Playback"
 msgstr "??????????????????"
 
-#: gui/recorderdialog.cpp:76 gui/widgets/grid.cpp:341
+#: gui/recorderdialog.cpp:76 gui/widgets/grid.cpp:343
 msgid "Edit"
 msgstr "????????????????????"
 
@@ -3060,11 +3072,11 @@ msgstr ""
 msgid "Clear value"
 msgstr "???????????????? ????????????????"
 
-#: gui/widgets/grid.cpp:335
+#: gui/widgets/grid.cpp:337
 msgid "Play"
 msgstr "????????????"
 
-#: gui/widgets/grid.cpp:338
+#: gui/widgets/grid.cpp:340
 #, fuzzy
 msgid "Saves"
 msgstr "????????????????"
diff --git a/po/ca_ES.po b/po/ca_ES.po
index 81f24ab0972..5256e61fac0 100644
--- a/po/ca_ES.po
+++ b/po/ca_ES.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.6.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-13 10:20+0000\n"
+"POT-Creation-Date: 2026-06-13 13:52+0000\n"
 "PO-Revision-Date: 2026-05-26 04:22+0000\n"
 "Last-Translator: Xaviu <xaviu at xaviu.com>\n"
 "Language-Team: Catalan <https://translations.scummvm.org/projects/scummvm/"
@@ -116,7 +116,7 @@ msgstr "Amunt"
 #: gui/downloadpacksdialog.cpp:465 gui/editgamedialog.cpp:340
 #: gui/editrecorddialog.cpp:66 gui/integrity-dialog.cpp:122
 #: gui/integrity-dialog.cpp:253 gui/filebrowser-dialog.cpp:68
-#: gui/fluidsynth-dialog.cpp:153 gui/massadd.cpp:93 gui/options.cpp:2416
+#: gui/fluidsynth-dialog.cpp:153 gui/massadd.cpp:93 gui/options.cpp:2418
 #: gui/predictivedialog.cpp:68 gui/printing-dialog.cpp:96
 #: gui/recorderdialog.cpp:72 gui/recorderdialog.cpp:211
 #: gui/remotebrowser.cpp:59 gui/saveload-dialog.cpp:64
@@ -187,23 +187,23 @@ msgstr ""
 "En aquest mode, el servidor web local ha d'estar executant-se,\n"
 "de manera que el vostre navegador pot enviar dades a ScummVM"
 
-#: gui/cloudconnectionwizard.cpp:257 gui/options.cpp:3776
+#: gui/cloudconnectionwizard.cpp:257 gui/options.cpp:3789
 msgid "Stop server"
 msgstr "Aturar servidor"
 
-#: gui/cloudconnectionwizard.cpp:257 gui/options.cpp:2840
+#: gui/cloudconnectionwizard.cpp:257 gui/options.cpp:2849
 msgid "Run server"
 msgstr "Executa el servidor"
 
-#: gui/cloudconnectionwizard.cpp:258 gui/options.cpp:3777
+#: gui/cloudconnectionwizard.cpp:258 gui/options.cpp:3790
 msgid "Stop local webserver"
 msgstr "Atura el servidor web local"
 
-#: gui/cloudconnectionwizard.cpp:258 gui/options.cpp:2840
+#: gui/cloudconnectionwizard.cpp:258 gui/options.cpp:2849
 msgid "Run local webserver"
 msgstr "Executa el servidor web local"
 
-#: gui/cloudconnectionwizard.cpp:266 gui/options.cpp:2841 gui/options.cpp:3784
+#: gui/cloudconnectionwizard.cpp:266 gui/options.cpp:2850 gui/options.cpp:3797
 msgid "Not running"
 msgstr "Aturat"
 
@@ -332,8 +332,8 @@ msgstr "Un altre Emmagatzematge est?? actiu ara mateix. Voleu interrompre'l?"
 #: gui/cloudconnectionwizard.cpp:530 gui/downloaddialog.cpp:118
 #: gui/downloaddialog.cpp:152 gui/filebrowser-dialog.cpp:135
 #: gui/fluidsynth-dialog.cpp:210 gui/launcher.cpp:428 gui/launcher.cpp:457
-#: gui/launcher.cpp:534 gui/launcher.cpp:1938 gui/options.cpp:3485
-#: gui/options.cpp:3859 base/main.cpp:608
+#: gui/launcher.cpp:534 gui/launcher.cpp:1970 gui/options.cpp:3498
+#: gui/options.cpp:3872 base/main.cpp:608
 #: backends/events/default/default-events.cpp:193 engines/buried/buried.cpp:598
 #: engines/chamber/metaengine.cpp:91 engines/director/events.cpp:122
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:139
@@ -352,8 +352,8 @@ msgstr "S??"
 #: gui/cloudconnectionwizard.cpp:530 gui/downloaddialog.cpp:118
 #: gui/downloaddialog.cpp:153 gui/filebrowser-dialog.cpp:135
 #: gui/fluidsynth-dialog.cpp:210 gui/launcher.cpp:428 gui/launcher.cpp:457
-#: gui/launcher.cpp:534 gui/launcher.cpp:1939 gui/options.cpp:3485
-#: gui/options.cpp:3859 engines/buried/buried.cpp:598
+#: gui/launcher.cpp:534 gui/launcher.cpp:1971 gui/options.cpp:3498
+#: gui/options.cpp:3872 engines/buried/buried.cpp:598
 #: engines/chamber/metaengine.cpp:97 engines/director/events.cpp:122
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:139
 #: engines/efh/metaengine.cpp:368 engines/grim/grim.cpp:366
@@ -380,7 +380,7 @@ msgstr "El contingut del codi JSON est?? mal format."
 #: gui/editrecorddialog.cpp:67 gui/integrity-dialog.cpp:260
 #: gui/fluidsynth-dialog.cpp:154 gui/massadd.cpp:90 gui/message.cpp:156
 #: gui/message.cpp:160 gui/message.cpp:231 gui/message.cpp:270
-#: gui/message.cpp:274 gui/options.cpp:2418 gui/predictivedialog.cpp:69
+#: gui/message.cpp:274 gui/options.cpp:2420 gui/predictivedialog.cpp:69
 #: gui/saveload-dialog.cpp:1244 base/main.cpp:675 base/main.cpp:700
 #: dists/android.strings.xml.cpp:33 engines/engine.cpp:846
 #: backends/platform/wii/options.cpp:46 engines/buried/buried.cpp:622
@@ -571,11 +571,11 @@ msgstr ""
 msgid "Proceed"
 msgstr "Continua"
 
-#: gui/dump-all-dialogs.cpp:123 gui/options.cpp:3347
+#: gui/dump-all-dialogs.cpp:123 gui/options.cpp:3360
 msgid "icon packs"
 msgstr "paquets d'icones"
 
-#: gui/dump-all-dialogs.cpp:127 gui/options.cpp:3358
+#: gui/dump-all-dialogs.cpp:127 gui/options.cpp:3371
 msgid "shader packs"
 msgstr "paquets de shaders"
 
@@ -587,11 +587,11 @@ msgstr "Seleccioneu el joc:"
 msgid "Game"
 msgstr "Joc"
 
-#: gui/editgamedialog.cpp:164 gui/options.cpp:2075 gui/options.cpp:2254
+#: gui/editgamedialog.cpp:164 gui/options.cpp:2075 gui/options.cpp:2256
 msgid "GFX"
 msgstr "GFX"
 
-#: gui/editgamedialog.cpp:164 gui/options.cpp:2075 gui/options.cpp:2254
+#: gui/editgamedialog.cpp:164 gui/options.cpp:2075 gui/options.cpp:2256
 #: backends/graphics/atari/atari-graphics.cpp:1056
 #: backends/graphics/sdl/sdl-graphics.cpp:487
 msgid "Graphics"
@@ -606,11 +606,11 @@ msgctxt "lowres"
 msgid "Override global graphic settings"
 msgstr "Canvia les opcions gr??fiques"
 
-#: gui/editgamedialog.cpp:185 gui/options.cpp:2288 engines/dialogs.cpp:354
+#: gui/editgamedialog.cpp:185 gui/options.cpp:2290 engines/dialogs.cpp:354
 msgid "Keymaps"
 msgstr "Assignacions de teclat"
 
-#: gui/editgamedialog.cpp:197 gui/options.cpp:2300 engines/dialogs.cpp:366
+#: gui/editgamedialog.cpp:197 gui/options.cpp:2302 engines/dialogs.cpp:366
 msgid "Backend"
 msgstr "de fondo"
 
@@ -623,7 +623,7 @@ msgctxt "lowres"
 msgid "Override global backend settings"
 msgstr "F??s canvis sobre les opcions globals d'??udio"
 
-#: gui/editgamedialog.cpp:220 gui/options.cpp:2318 engines/dialogs.cpp:329
+#: gui/editgamedialog.cpp:220 gui/options.cpp:2320 engines/dialogs.cpp:329
 msgid "Audio"
 msgstr "??udio"
 
@@ -636,11 +636,11 @@ msgctxt "lowres"
 msgid "Override global audio settings"
 msgstr "Canvia les opcions d'??udio"
 
-#: gui/editgamedialog.cpp:234 gui/options.cpp:2323
+#: gui/editgamedialog.cpp:234 gui/options.cpp:2325
 msgid "Volume"
 msgstr "Volum"
 
-#: gui/editgamedialog.cpp:236 gui/options.cpp:2325
+#: gui/editgamedialog.cpp:236 gui/options.cpp:2327
 msgctxt "lowres"
 msgid "Volume"
 msgstr "Volum"
@@ -654,7 +654,7 @@ msgctxt "lowres"
 msgid "Override global volume settings"
 msgstr "Fes canvis sobre les opcions globals de volum"
 
-#: gui/editgamedialog.cpp:252 gui/options.cpp:2333
+#: gui/editgamedialog.cpp:252 gui/options.cpp:2335
 msgid "MIDI"
 msgstr "MIDI"
 
@@ -667,7 +667,7 @@ msgctxt "lowres"
 msgid "Override global MIDI settings"
 msgstr "Canvia les opcions de MIDI"
 
-#: gui/editgamedialog.cpp:267 gui/options.cpp:2339
+#: gui/editgamedialog.cpp:267 gui/options.cpp:2341
 msgid "MT-32"
 msgstr "MT-32"
 
@@ -680,11 +680,11 @@ msgctxt "lowres"
 msgid "Override global MT-32 settings"
 msgstr "Canvia les opcions de MT-32"
 
-#: gui/editgamedialog.cpp:281 gui/options.cpp:2346
+#: gui/editgamedialog.cpp:281 gui/options.cpp:2348
 msgid "Paths"
 msgstr "Camins"
 
-#: gui/editgamedialog.cpp:283 gui/options.cpp:2348
+#: gui/editgamedialog.cpp:283 gui/options.cpp:2350
 msgctxt "lowres"
 msgid "Paths"
 msgstr "Camins"
@@ -706,7 +706,7 @@ msgctxt "lowres"
 msgid "Game Path:"
 msgstr "Cam?? joc:"
 
-#: gui/editgamedialog.cpp:303 gui/options.cpp:2555
+#: gui/editgamedialog.cpp:303 gui/options.cpp:2557
 msgid "Extra Path:"
 msgstr "Cam?? extra:"
 
@@ -715,33 +715,33 @@ msgstr "Cam?? extra:"
 msgid "Specifies path to additional data used by the game"
 msgstr "Indica el cam?? de dades addicionals utilitzades pel joc"
 
-#: gui/editgamedialog.cpp:305 gui/options.cpp:2557
+#: gui/editgamedialog.cpp:305 gui/options.cpp:2559
 msgctxt "lowres"
 msgid "Extra Path:"
 msgstr "Cam?? extra:"
 
-#: gui/editgamedialog.cpp:306 gui/options.cpp:2530 gui/options.cpp:2558
-#: gui/options.cpp:2567 gui/options.cpp:2848
+#: gui/editgamedialog.cpp:306 gui/options.cpp:2532 gui/options.cpp:2560
+#: gui/options.cpp:2569 gui/options.cpp:2857
 #: backends/platform/libretro/src/libretro-options-widget.cpp:277
 msgctxt "path"
 msgid "None"
 msgstr "Cap"
 
-#: gui/editgamedialog.cpp:312 gui/options.cpp:2519
+#: gui/editgamedialog.cpp:312 gui/options.cpp:2521
 msgid "Save Path:"
 msgstr "Cam?? de partides:"
 
 #: gui/editgamedialog.cpp:312 gui/editgamedialog.cpp:314
-#: gui/editgamedialog.cpp:315 gui/options.cpp:2519 gui/options.cpp:2521
+#: gui/editgamedialog.cpp:315 gui/options.cpp:2521 gui/options.cpp:2523
 msgid "Specifies where your saved games are put"
 msgstr "Indica on es desaran les partides"
 
-#: gui/editgamedialog.cpp:314 gui/options.cpp:2521
+#: gui/editgamedialog.cpp:314 gui/options.cpp:2523
 msgctxt "lowres"
 msgid "Save Path:"
 msgstr "Partides desades:"
 
-#: gui/editgamedialog.cpp:315 gui/options.cpp:1844 gui/options.cpp:2522
+#: gui/editgamedialog.cpp:315 gui/options.cpp:1844 gui/options.cpp:2524
 #: backends/platform/wii/options.cpp:55
 msgid "Default"
 msgstr "Per defecte"
@@ -801,8 +801,8 @@ msgstr ""
 #: gui/editgamedialog.cpp:365 gui/editgamedialog.cpp:380 gui/options.cpp:1613
 #: gui/options.cpp:1627 gui/options.cpp:1644 gui/options.cpp:1658
 #: gui/options.cpp:1675 gui/options.cpp:1713 gui/options.cpp:1729
-#: gui/options.cpp:2144 gui/options.cpp:2600 gui/options.cpp:2674
-#: gui/options.cpp:2888 gui/options.cpp:3389 audio/null.cpp:45
+#: gui/options.cpp:2144 gui/options.cpp:2602 gui/options.cpp:2683
+#: gui/options.cpp:2897 gui/options.cpp:3402 audio/null.cpp:45
 #: backends/platform/android/options.cpp:157
 #: backends/platform/android/options.cpp:158
 #: backends/platform/android/options.cpp:186
@@ -829,7 +829,7 @@ msgctxt "lowres"
 msgid "Platform:"
 msgstr "Platafor.:"
 
-#: gui/editgamedialog.cpp:566 gui/options.cpp:3413
+#: gui/editgamedialog.cpp:566 gui/options.cpp:3426
 msgid "Select SoundFont"
 msgstr "Seleccioneu el fitxer SoundFont"
 
@@ -837,11 +837,11 @@ msgstr "Seleccioneu el fitxer SoundFont"
 msgid "Select additional game directory"
 msgstr "Seleccioneu el directori addicional del joc"
 
-#: gui/editgamedialog.cpp:618 gui/options.cpp:3261
+#: gui/editgamedialog.cpp:618 gui/options.cpp:3274
 msgid "Select directory for saved games"
 msgstr "Seleccioneu el directori de les partides desades"
 
-#: gui/editgamedialog.cpp:625 gui/options.cpp:3268
+#: gui/editgamedialog.cpp:625 gui/options.cpp:3281
 msgid "The chosen directory cannot be written to. Please select another one."
 msgstr ""
 "No es pot escriure al directori seleccionat. Si us plau, escolliu-ne un "
@@ -996,7 +996,7 @@ msgstr "Sinus"
 msgid "Triangle"
 msgstr "Triangle"
 
-#: gui/fluidsynth-dialog.cpp:139 gui/options.cpp:2366
+#: gui/fluidsynth-dialog.cpp:139 gui/options.cpp:2368
 msgid "Misc"
 msgstr "Misc"
 
@@ -1036,7 +1036,7 @@ msgstr ""
 "Realment voleu retorna tots els ajustos de FluidSynth als seus valors per "
 "defecte?"
 
-#: gui/gui-manager.cpp:159 gui/options.cpp:2357
+#: gui/gui-manager.cpp:159 gui/options.cpp:2359
 msgid "GUI"
 msgstr "interf??cie gr??fica d'usuari"
 
@@ -1243,7 +1243,7 @@ msgid ""
 "and illegal game copies.\n"
 msgstr ""
 
-#: gui/helpdialog.cpp:68 gui/options.cpp:2377
+#: gui/helpdialog.cpp:68 gui/options.cpp:2379
 msgid "Cloud"
 msgstr "N??vol"
 
@@ -1536,85 +1536,85 @@ msgstr "Vista de llistat"
 msgid "Grid view"
 msgstr "Vista de quadr??cula"
 
-#: gui/launcher.cpp:1145
+#: gui/launcher.cpp:1168
 msgid "~S~tart"
 msgstr "~I~nicia"
 
-#: gui/launcher.cpp:1145
+#: gui/launcher.cpp:1168
 msgid "Start selected game"
 msgstr "Iniciant el joc seleccionat"
 
-#: gui/launcher.cpp:1148
+#: gui/launcher.cpp:1171
 msgid "~L~oad..."
 msgstr "~C~arrega..."
 
-#: gui/launcher.cpp:1148
+#: gui/launcher.cpp:1171
 msgid "Load saved game for selected game"
 msgstr "Carrega una partida pel joc seleccionat"
 
-#: gui/launcher.cpp:1150
+#: gui/launcher.cpp:1173
 msgid "Record..."
 msgstr "Enregistra..."
 
-#: gui/launcher.cpp:1156
+#: gui/launcher.cpp:1179
 msgid "~G~ame Options..."
 msgstr "~O~pcions del jocs..."
 
-#: gui/launcher.cpp:1156
+#: gui/launcher.cpp:1179
 msgid "Change game options"
 msgstr "Canvia les opcions del joc"
 
-#: gui/launcher.cpp:1156
+#: gui/launcher.cpp:1179
 msgctxt "lowres"
 msgid "~G~ame Opts..."
 msgstr "Opcs. del ~j~oc..."
 
-#: gui/launcher.cpp:1242
+#: gui/launcher.cpp:1265
 msgid "Add-on"
 msgstr "Complement"
 
 #. I18N: List grouping when no engine is specified
-#: gui/launcher.cpp:1309 gui/launcher.cpp:1554
+#: gui/launcher.cpp:1335 gui/launcher.cpp:1580
 msgid "Unknown Engine"
 msgstr "Motor desconegut"
 
 #. I18N: List grouping when no publisher is specified
 #. I18N: List group when no publisher is specified
-#: gui/launcher.cpp:1326 gui/launcher.cpp:1571
+#: gui/launcher.cpp:1352 gui/launcher.cpp:1597
 msgid "Unknown Publisher"
 msgstr "Distribu??dora desconeguda"
 
 #. I18N: List group when no game series is specified
 #. I18N: List grouping when no game series is specified
-#: gui/launcher.cpp:1343 gui/launcher.cpp:1588
+#: gui/launcher.cpp:1369 gui/launcher.cpp:1614
 msgid "No Series"
 msgstr "Sense s??rie"
 
 #. I18N: List group when no language is specified
-#: gui/launcher.cpp:1357 gui/launcher.cpp:1602
+#: gui/launcher.cpp:1383 gui/launcher.cpp:1628
 msgid "Language not detected"
 msgstr "No s'ha detectat l'idioma"
 
 #. I18N: List group when no platform is specified
-#: gui/launcher.cpp:1371 gui/launcher.cpp:1616
+#: gui/launcher.cpp:1397 gui/launcher.cpp:1642
 msgid "Platform not detected"
 msgstr "No s'ha detectat la plataforma"
 
 #. I18N: List group when no year is specified
-#: gui/launcher.cpp:1388 gui/launcher.cpp:1633
+#: gui/launcher.cpp:1414 gui/launcher.cpp:1659
 msgid "Unknown Year"
 msgstr "Any desconegut"
 
 #. I18N: Group for All items
-#: gui/launcher.cpp:1640 gui/widgets/groupedlist.cpp:82
+#: gui/launcher.cpp:1666 gui/widgets/groupedlist.cpp:82
 msgid "All"
 msgstr "Tots"
 
-#: gui/launcher.cpp:1812
+#: gui/launcher.cpp:1844
 msgid "Icons per row:"
 msgstr "Icones per l??nia:"
 
-#: gui/launcher.cpp:1869
+#: gui/launcher.cpp:1901
 #, c-format
 msgid ""
 "Do you really want to remove the following %d game configurations?\n"
@@ -2109,54 +2109,54 @@ msgctxt "lowres"
 msgid "Speech volume:"
 msgstr "Volum de veus:"
 
-#: gui/options.cpp:2266
+#: gui/options.cpp:2268
 msgid "Control"
 msgstr "Control"
 
-#: gui/options.cpp:2379
+#: gui/options.cpp:2381
 msgctxt "lowres"
 msgid "Cloud"
 msgstr "N??vol"
 
-#: gui/options.cpp:2394
+#: gui/options.cpp:2396
 msgid "LAN"
 msgstr "Xarxa Local"
 
-#: gui/options.cpp:2396
+#: gui/options.cpp:2398
 msgctxt "lowres"
 msgid "LAN"
 msgstr "Xarxa Local"
 
-#: gui/options.cpp:2405
+#: gui/options.cpp:2407
 msgid "Accessibility"
 msgstr "Accessibilitat"
 
-#: gui/options.cpp:2407
+#: gui/options.cpp:2409
 msgctxt "lowres"
 msgid "Accessibility"
 msgstr "Accessibilitat"
 
-#: gui/options.cpp:2416
+#: gui/options.cpp:2418
 msgid "Discard changes and close the dialog"
 msgstr "Descarta els canvis i tanca el quadre de di??leg"
 
-#: gui/options.cpp:2417
+#: gui/options.cpp:2419
 msgid "Apply"
 msgstr "Aplicar"
 
-#: gui/options.cpp:2417
+#: gui/options.cpp:2419
 msgid "Apply changes without closing the dialog"
 msgstr "Aplica els canvis sense tancar el quadre de di??leg"
 
-#: gui/options.cpp:2418
+#: gui/options.cpp:2420
 msgid "Apply changes and close the dialog"
 msgstr "Aplica els canvis i tanca el quadre de di??leg"
 
-#: gui/options.cpp:2508
+#: gui/options.cpp:2510
 msgid "FluidSynth Settings"
 msgstr "Configuraci?? de FluidSynth"
 
-#: gui/options.cpp:2522
+#: gui/options.cpp:2524
 msgid ""
 "Specifies where your saved games are put. A red coloring indicates the value "
 "is temporary and will not get saved"
@@ -2164,92 +2164,92 @@ msgstr ""
 "Indica on es col??loquen les partides desades. Una coloraci?? vermella indica "
 "que el valor ??s temporal i no es desar??"
 
-#: gui/options.cpp:2527
+#: gui/options.cpp:2529
 msgid "Theme Path:"
 msgstr "Cam?? dels temes:"
 
-#: gui/options.cpp:2529
+#: gui/options.cpp:2531
 msgctxt "lowres"
 msgid "Theme Path:"
 msgstr "Cam?? temes:"
 
-#: gui/options.cpp:2535
+#: gui/options.cpp:2537
 msgid "Icon Path:"
 msgstr "Directori d'icones:"
 
-#: gui/options.cpp:2537
+#: gui/options.cpp:2539
 msgctxt "lowres"
 msgid "Icon Path:"
 msgstr "Directori d'icones:"
 
-#: gui/options.cpp:2538 gui/options.cpp:2548
+#: gui/options.cpp:2540 gui/options.cpp:2550
 msgctxt "path"
 msgid "Default"
 msgstr "Per defecte"
 
-#: gui/options.cpp:2545
+#: gui/options.cpp:2547
 msgid "DLC Path:"
 msgstr "Cam?? del DLC:"
 
-#: gui/options.cpp:2547
+#: gui/options.cpp:2549
 msgctxt "lowres"
 msgid "DLC Path:"
 msgstr "Cam?? del DLC:"
 
-#: gui/options.cpp:2555 gui/options.cpp:2557 gui/options.cpp:2558
+#: gui/options.cpp:2557 gui/options.cpp:2559 gui/options.cpp:2560
 msgid "Specifies path to additional data used by all games or ScummVM"
 msgstr ""
 "Especifica el cam?? de les dades addicionals utilitzades per tots els jocs o "
 "pel ScummVM"
 
-#: gui/options.cpp:2564
+#: gui/options.cpp:2566
 msgid "Plugins Path:"
 msgstr "Cam?? dels connectors:"
 
-#: gui/options.cpp:2566
+#: gui/options.cpp:2568
 msgctxt "lowres"
 msgid "Plugins Path:"
 msgstr "Cam?? de connectors:"
 
-#: gui/options.cpp:2576
+#: gui/options.cpp:2578
 msgid "ScummVM config path: "
 msgstr "Directori de configuraci?? de ScummVM: "
 
-#: gui/options.cpp:2590
+#: gui/options.cpp:2592
 msgid "ScummVM log path: "
 msgstr "Directori de registre de ScummVM: "
 
-#: gui/options.cpp:2595
+#: gui/options.cpp:2597
 msgid "View"
 msgstr "Visualitza"
 
 #. I18N: Referring to the last path memorized when adding a game
-#: gui/options.cpp:2605 gui/options.cpp:3389
+#: gui/options.cpp:2607 gui/options.cpp:3402
 msgid "Last browser path: "
 msgstr "Darrer directori de l'explorador: "
 
-#: gui/options.cpp:2610
+#: gui/options.cpp:2612
 msgid "Theme:"
 msgstr "Tema:"
 
-#: gui/options.cpp:2615
+#: gui/options.cpp:2617
 msgid "GUI scale:"
 msgstr "Escala de la interf??cie:"
 
-#: gui/options.cpp:2632
+#: gui/options.cpp:2634
 #, c-format
 msgid "%d%% - Custom"
 msgstr "%d%% - Personalitzat"
 
-#: gui/options.cpp:2636
+#: gui/options.cpp:2638
 msgid "GUI renderer:"
 msgstr "Render GUI:"
 
-#: gui/options.cpp:2649
+#: gui/options.cpp:2651
 msgid "Return to the launcher when leaving a game"
 msgstr "Torna al llan??ador en sortir d'un joc"
 
-#: gui/options.cpp:2650
+#: gui/options.cpp:2652
 msgid ""
 "Return to the launcher when leaving a game instead of closing ScummVM\n"
 "(this feature is not supported by all games)."
@@ -2257,39 +2257,55 @@ msgstr ""
 "Torna al llan??ador en sortir d'un joc en lloc de tancar ScummVM\n"
 "(aquesta caracter??stica no ??s compatible amb tots els jocs)."
 
-#: gui/options.cpp:2657
+#: gui/options.cpp:2659
 msgid "Ask for confirmation on exit"
 msgstr "Demana confirmaci?? en sortir"
 
-#: gui/options.cpp:2658
+#: gui/options.cpp:2660
 msgid "Ask for permission when closing ScummVM or leaving a game."
 msgstr "Demana perm??s en tancar ScummVM o sortir d'un joc."
 
-#: gui/options.cpp:2664
+#: gui/options.cpp:2666
 msgid "Disable fixed font scaling"
 msgstr "Desactiva l'escalat de tipus de lletra fixa"
 
-#: gui/options.cpp:2665
+#: gui/options.cpp:2667
+#, fuzzy
+#| msgid ""
+#| "Do not upscale fixed size fonts in the GUI. This reduces artefacts on low "
+#| "resolution screens"
 msgid ""
 "Do not upscale fixed size fonts in the GUI. This reduces artefacts on low "
-"resolution screens"
+"resolution screens."
 msgstr ""
 "No escalar tipus de lletra de mida fixa a la IGU. Aix?? redueix els "
 "artefactes en pantalles de baixa resoluci??"
 
-#: gui/options.cpp:2671
+#: gui/options.cpp:2673
+#, fuzzy
+#| msgid "Enable smooth scrolling when walking"
+msgid "Enable kinetic scrolling in lists"
+msgstr "Activa el despla??ament suau al caminar"
+
+#: gui/options.cpp:2674
+#, fuzzy
+#| msgid "Enable smooth scrolling when walking"
+msgid "Enable smooth, momentum-based scrolling in list widgets."
+msgstr "Activa el despla??ament suau al caminar"
+
+#: gui/options.cpp:2680
 msgid "GUI language:"
 msgstr "Idioma de la interf??cie:"
 
-#: gui/options.cpp:2671
+#: gui/options.cpp:2680
 msgid "Language of ScummVM GUI"
 msgstr "Idioma de la interf??cie d'usuari de ScummVM"
 
-#: gui/options.cpp:2697
+#: gui/options.cpp:2706
 msgid "Switch the ScummVM GUI language to the game language"
 msgstr "Canvia l'idioma de la interf??cie a l'idioma del joc"
 
-#: gui/options.cpp:2698
+#: gui/options.cpp:2707
 msgid ""
 "When starting a game, change the ScummVM GUI language to the game language. "
 "That way, if a game uses the ScummVM save and load dialogs, they are in the "
@@ -2299,11 +2315,11 @@ msgstr ""
 "joc. D'aquesta manera, si el joc fa servir les opcions de guardar i carregar "
 "de pr??pies de ScummVM, aquestes seran en el mateix idioma del joc."
 
-#: gui/options.cpp:2710
+#: gui/options.cpp:2719
 msgid "Use native system file browser"
 msgstr "Fer servir l'explorador d'arxius del sistema"
 
-#: gui/options.cpp:2711
+#: gui/options.cpp:2720
 msgid ""
 "Use the native system file browser instead of the ScummVM one to select a "
 "file or directory."
@@ -2311,51 +2327,51 @@ msgstr ""
 "Fer servir l'explorador d'arxius del sistema en lloc del propi de ScummVM "
 "per seleccionar un fitxer o carpeta."
 
-#: gui/options.cpp:2718
+#: gui/options.cpp:2727
 msgid "Download Icons"
 msgstr "Baixa icones"
 
-#: gui/options.cpp:2718
+#: gui/options.cpp:2727
 msgid "Check on the scummvm.org website for updates of icon packs"
 msgstr ""
 "Comprova al web de scummvm.org si hi ha actualitzacions dels paquets d'icones"
 
-#: gui/options.cpp:2724
+#: gui/options.cpp:2733
 msgid "Autosave:"
 msgstr "Desat autom??tic:"
 
-#: gui/options.cpp:2726
+#: gui/options.cpp:2735
 msgctxt "lowres"
 msgid "Autosave:"
 msgstr "Auto-desat:"
 
-#: gui/options.cpp:2737
+#: gui/options.cpp:2746
 msgid "Random seed:"
 msgstr "Llavor aleat??ria:"
 
-#: gui/options.cpp:2737
+#: gui/options.cpp:2746
 msgid "Seed for initializing all random number generators"
 msgstr "Llavor per a inicialitzar tots els generadors de nombres aleatoris"
 
-#: gui/options.cpp:2745
+#: gui/options.cpp:2754
 msgid "Debug level:"
 msgstr "Nivell de depuraci??:"
 
 #. I18N: Debug level -1, no messages
-#: gui/options.cpp:2749 gui/options.cpp:2886 audio/fmopl.cpp:83
+#: gui/options.cpp:2758 gui/options.cpp:2895 audio/fmopl.cpp:83
 msgid "None"
 msgstr "Cap"
 
 #. I18N: Debug level 11, all messages
-#: gui/options.cpp:2755
+#: gui/options.cpp:2764
 msgid "11 (all)"
 msgstr "11 (tot)"
 
-#: gui/options.cpp:2759
+#: gui/options.cpp:2768
 msgid "Enable Discord integration"
 msgstr "Habilita la integraci?? amb Discord"
 
-#: gui/options.cpp:2760
+#: gui/options.cpp:2769
 msgid ""
 "Show information about the games you are playing on Discord if the Discord "
 "client is running."
@@ -2363,27 +2379,27 @@ msgstr ""
 "Mostra informaci?? al Discord sobre els jocs que esteu jugant si el client de "
 "Discord s'est?? executant."
 
-#: gui/options.cpp:2770 gui/updates-dialog.cpp:87
+#: gui/options.cpp:2779 gui/updates-dialog.cpp:87
 msgid "Update check:"
 msgstr "Comprovaci?? d'actualitzacions:"
 
-#: gui/options.cpp:2770
+#: gui/options.cpp:2779
 msgid "How often to check ScummVM updates"
 msgstr "Cada quan ScummVM comprova les actualitzacions"
 
-#: gui/options.cpp:2781
+#: gui/options.cpp:2790
 msgid "Check now"
 msgstr "Comprova ara"
 
-#: gui/options.cpp:2788
+#: gui/options.cpp:2797
 msgid "Active storage:"
 msgstr "Emmagatzematge actiu:"
 
-#: gui/options.cpp:2788
+#: gui/options.cpp:2797
 msgid "Active cloud storage"
 msgstr "Activar salvar al n??vol"
 
-#: gui/options.cpp:2797
+#: gui/options.cpp:2806
 msgctxt "lowres"
 msgid ""
 "Storage is not yet enabled. Verify that username is correct and enable it:"
@@ -2391,196 +2407,196 @@ msgstr ""
 "L'emmagatzematge encara no est?? habilitat. Verifiqueu que el nom d'usuari ??s "
 "correcte i habiliteu-lo:"
 
-#: gui/options.cpp:2799
+#: gui/options.cpp:2808
 msgid ""
 "Storage is not yet enabled. Verify that username is correct and enable it:"
 msgstr ""
 "L'emmagatzematge encara no est?? habilitat. Verifiqueu que el nom d'usuari ??s "
 "correcte i habiliteu-lo:"
 
-#: gui/options.cpp:2800
+#: gui/options.cpp:2809
 msgid "Enable storage"
 msgstr "Habilita l'emmagatzematge"
 
-#: gui/options.cpp:2800
+#: gui/options.cpp:2809
 msgid "Confirm you want to use this account for this storage"
 msgstr ""
 "Confirmeu que voleu utilitzar aquest compte per a aquest emmagatzematge"
 
-#: gui/options.cpp:2802 backends/platform/wii/options.cpp:113
+#: gui/options.cpp:2811 backends/platform/wii/options.cpp:113
 msgid "Username:"
 msgstr "Nom d'usuari:"
 
-#: gui/options.cpp:2802
+#: gui/options.cpp:2811
 msgid "Username used by this storage"
 msgstr "Usuari usat per aquest emmagatzematge"
 
-#: gui/options.cpp:2803
+#: gui/options.cpp:2812
 msgid "<none>"
 msgstr "<cap>"
 
-#: gui/options.cpp:2805
+#: gui/options.cpp:2814
 msgid "Used space:"
 msgstr "Espai usat:"
 
-#: gui/options.cpp:2805
+#: gui/options.cpp:2814
 msgid "Space used by ScummVM's saved games on this storage"
 msgstr ""
 "Espai usat per les partides guardades de ScummVM en aquest emmagatzematge"
 
-#: gui/options.cpp:2808
+#: gui/options.cpp:2817
 msgid "Last sync:"
 msgstr "??ltima sincronitzaci??:"
 
-#: gui/options.cpp:2808
+#: gui/options.cpp:2817
 msgid "When was the last time saved games were synced with this storage"
 msgstr ""
 "Quan es van sincronitzar per ??ltima vegada les partides guardades amb aquest "
 "emmagatzematge"
 
-#: gui/options.cpp:2809 gui/options.cpp:3693
+#: gui/options.cpp:2818 gui/options.cpp:3706
 msgid "<never>"
 msgstr "<mai>"
 
-#: gui/options.cpp:2811
+#: gui/options.cpp:2820
 msgctxt "lowres"
 msgid "Saved games sync automatically on launch, after saving and on loading."
 msgstr ""
 "Les partides desades es sincronitzen autom??ticament en l'inici, despr??s de "
 "desar i en carregar."
 
-#: gui/options.cpp:2813
+#: gui/options.cpp:2822
 msgid "Saved games sync automatically on launch, after saving and on loading."
 msgstr ""
 "Les partides desades es sincronitzen autom??ticament en l'inici, despr??s de "
 "desar i en carregar."
 
-#: gui/options.cpp:2814
+#: gui/options.cpp:2823
 msgid "Sync now"
 msgstr "Sincronitza ara"
 
-#: gui/options.cpp:2814
+#: gui/options.cpp:2823
 msgid "Start saved games sync"
 msgstr "Inicia la sincronitzaci?? de les partides desades"
 
-#: gui/options.cpp:2817
+#: gui/options.cpp:2826
 msgctxt "lowres"
 msgid "You can download game files from your cloud ScummVM folder:"
 msgstr ""
 "Es pot descarregar els fitxers del joc de la carpeta al n??vol de ScummVM:"
 
-#: gui/options.cpp:2819
+#: gui/options.cpp:2828
 msgid "You can download game files from your cloud ScummVM folder:"
 msgstr ""
 "Es pot descarregar els fitxers del joc de la carpeta al n??vol de ScummVM:"
 
-#: gui/options.cpp:2820
+#: gui/options.cpp:2829
 msgid "Download game files"
 msgstr "Baixa els fitxers del joc"
 
-#: gui/options.cpp:2820
+#: gui/options.cpp:2829
 msgid "Open downloads manager dialog"
 msgstr "Obre el quadre de di??leg del gestor de baixades"
 
-#: gui/options.cpp:2823
+#: gui/options.cpp:2832
 msgctxt "lowres"
 msgid "To change account for this storage, disconnect and connect again:"
 msgstr ""
 "Per canviar el compte d'aquest emmagatzematge, desconnecteu-lo i connecteu-"
 "lo de nou:"
 
-#: gui/options.cpp:2825
+#: gui/options.cpp:2834
 msgid "To change account for this storage, disconnect and connect again:"
 msgstr ""
 "Per canviar el compte d'aquest emmagatzematge, desconnecteu-lo i connecteu-"
 "lo de nou:"
 
-#: gui/options.cpp:2826
+#: gui/options.cpp:2835
 msgid "Disconnect"
 msgstr "Desconnecta"
 
-#: gui/options.cpp:2826
+#: gui/options.cpp:2835
 msgid "Stop using this storage on this device"
 msgstr "Atura l'??s d'aquest emmagatzematge en aquest dispositiu"
 
-#: gui/options.cpp:2829
+#: gui/options.cpp:2838
 msgctxt "lowres"
 msgid "This storage is not connected yet!"
 msgstr "Aquest emmagatzematge encara no est?? connectat!"
 
-#: gui/options.cpp:2831
+#: gui/options.cpp:2840
 msgid "This storage is not connected yet!"
 msgstr "Aquest emmagatzematge encara no est?? connectat!"
 
-#: gui/options.cpp:2832
+#: gui/options.cpp:2841
 msgid "Connect"
 msgstr "Connecta"
 
-#: gui/options.cpp:2832
+#: gui/options.cpp:2841
 msgid "Connect your cloud storage account"
 msgstr "Connecteu el vostre compte d'emmagatzematge al n??vol"
 
-#: gui/options.cpp:2845
+#: gui/options.cpp:2854
 msgctxt "lowres"
 msgid "/root/ Path:"
 msgstr "Directori /arrel/:"
 
-#: gui/options.cpp:2845 gui/options.cpp:2847 gui/options.cpp:2848
+#: gui/options.cpp:2854 gui/options.cpp:2856 gui/options.cpp:2857
 msgid "Select which directory will be shown as /root/ in the Files Manager"
 msgstr ""
 "Seleccioneu quin directori es mostrar?? com a /arrel/ al Gestor de fitxers"
 
-#: gui/options.cpp:2847
+#: gui/options.cpp:2856
 msgid "/root/ Path:"
 msgstr "Directori /arrel/:"
 
-#: gui/options.cpp:2853
+#: gui/options.cpp:2862
 msgid "Server's port:"
 msgstr "Port del servidor:"
 
-#: gui/options.cpp:2853
+#: gui/options.cpp:2862
 msgid "Port for server to use"
 msgstr "Port que utilitzar?? el servidor"
 
-#: gui/options.cpp:2858
+#: gui/options.cpp:2867
 msgctxt "lowres"
 msgid "Run server to manage files with browser (in the same network)."
 msgstr ""
 "Executa el servidor per gestionar fitxers amb l'explorador (a la mateixa "
 "xarxa)."
 
-#: gui/options.cpp:2859
+#: gui/options.cpp:2868
 msgctxt "lowres"
 msgid "Closing options dialog will stop the server."
 msgstr "Al tancar el quadre de di??leg d'opcions s'aturar?? el servidor."
 
-#: gui/options.cpp:2861
+#: gui/options.cpp:2870
 msgid "Run server to manage files with browser (in the same network)."
 msgstr ""
 "Executa el servidor per gestionar fitxers amb l'explorador (a la mateixa "
 "xarxa)."
 
-#: gui/options.cpp:2862
+#: gui/options.cpp:2871
 msgid "Closing options dialog will stop the server."
 msgstr "Al tancar el quadre de di??leg d'opcions s'aturar?? el servidor."
 
-#: gui/options.cpp:2873
+#: gui/options.cpp:2882
 msgid "Use Text to speech"
 msgstr "Conversi?? de text a veu"
 
-#: gui/options.cpp:2873
+#: gui/options.cpp:2882
 msgid "Will read text in gui on mouse over."
 msgstr "Es llegir?? el text de la interf??cie en passar per sobre el ratol??."
 
-#: gui/options.cpp:2954
+#: gui/options.cpp:2963
 msgid "Ignore"
 msgstr "Ignora"
 
-#: gui/options.cpp:2955
+#: gui/options.cpp:2964
 msgid "Disable autosave"
 msgstr "Inhabilita el desament autom??tic"
 
-#: gui/options.cpp:2956
+#: gui/options.cpp:2965
 msgid ""
 "WARNING: Autosave was enabled. Some of your games have existing saved games "
 "on the autosave slot. You can either move the existing saves to new slots, "
@@ -2595,7 +2611,7 @@ msgstr ""
 "sobreescriure una partida desada).\n"
 "Llista de jocs:\n"
 
-#: gui/options.cpp:2965
+#: gui/options.cpp:2974
 msgid ""
 "\n"
 "And more..."
@@ -2603,34 +2619,34 @@ msgstr ""
 "\n"
 "I m??s..."
 
-#: gui/options.cpp:2966 engines/engine.cpp:647
+#: gui/options.cpp:2975 engines/engine.cpp:647
 #: engines/access/metaengine.cpp:298 engines/drascula/metaengine.cpp:255
 #: engines/queen/metaengine.cpp:269 engines/sherlock/metaengine.cpp:410
 #: engines/ultima/nuvie/metaengine.cpp:71
 msgid "Move"
 msgstr "Mou"
 
-#: gui/options.cpp:2985
+#: gui/options.cpp:2994
 msgid "ERROR: Failed to move the following saved games:\n"
 msgstr "ERROR: No s'han pogut moure les seg??ents partides desades:\n"
 
-#: gui/options.cpp:3081
+#: gui/options.cpp:3090
 msgid "Failed to change cloud storage!"
 msgstr "Fallada al canviar l'emmagatzematge en l??nia!"
 
-#: gui/options.cpp:3084
+#: gui/options.cpp:3093
 msgid "Another cloud storage is already active."
 msgstr "Un altre emmagatzematge al n??vol ja est?? actiu."
 
-#: gui/options.cpp:3199
+#: gui/options.cpp:3212
 msgid "Theme does not support selected language!"
 msgstr "El tema no admet l'idioma seleccionat!"
 
-#: gui/options.cpp:3202
+#: gui/options.cpp:3215
 msgid "Theme cannot be loaded!"
 msgstr "No es pot carregar el tema!"
 
-#: gui/options.cpp:3205
+#: gui/options.cpp:3218
 msgid ""
 "\n"
 "Misc settings will be restored."
@@ -2638,44 +2654,44 @@ msgstr ""
 "\n"
 "Es restauraran diversos par??metres."
 
-#: gui/options.cpp:3277
+#: gui/options.cpp:3290
 msgid "Select directory for GUI themes"
 msgstr "Seleccioneu el directori dels temes"
 
-#: gui/options.cpp:3287
+#: gui/options.cpp:3300
 msgid "Select directory for GUI launcher thumbnails"
 msgstr ""
 "Seleccioneu el directori per a les miniatures del llan??ador de la interf??cie"
 
-#: gui/options.cpp:3298
+#: gui/options.cpp:3311
 msgid "Select directory for DLC downloads"
 msgstr "Seleccioneu el directori per a les baixades de DLC"
 
-#: gui/options.cpp:3309
+#: gui/options.cpp:3322
 msgid "Select directory for extra files"
 msgstr "Seleccioneu el directori dels fitxers extra"
 
-#: gui/options.cpp:3320
+#: gui/options.cpp:3333
 msgid "Select directory for plugins"
 msgstr "Seleccioneu el directori dels connectors"
 
-#: gui/options.cpp:3333
+#: gui/options.cpp:3346
 msgid "Select directory for Files Manager /root/"
 msgstr "Selecciona el directori /arrel/ per al gestor d'arxius"
 
-#: gui/options.cpp:3485
+#: gui/options.cpp:3498
 msgid "This Storage is working now. Do you want to interrupt it?"
 msgstr "Aquest Emmagatzematge est?? funcionant ara. Voleu interrompre'l?"
 
-#: gui/options.cpp:3494
+#: gui/options.cpp:3507
 msgid "Wait until current Storage finishes up and try again."
 msgstr "Espereu fins que l'Emmagatzematge actual acabi i torneu a provar-ho."
 
-#: gui/options.cpp:3691 backends/cloud/cloudmanager.cpp:157
+#: gui/options.cpp:3704 backends/cloud/cloudmanager.cpp:157
 msgid "<right now>"
 msgstr "<ara mateix>"
 
-#: gui/options.cpp:3834
+#: gui/options.cpp:3847
 msgid ""
 "Request failed.\n"
 "Check your Internet connection."
@@ -2683,7 +2699,7 @@ msgstr ""
 "La sol??licitud ha fallat.\n"
 "Comproveu la connexi?? a Internet."
 
-#: gui/options.cpp:3857
+#: gui/options.cpp:3870
 msgid ""
 "A test pattern should be displayed.\n"
 "Do you want to keep these shader scaler settings?"
@@ -2691,7 +2707,7 @@ msgstr ""
 "S'ha de mostrar un patr?? de prova.\n"
 "Voleu mantenir aquesta configuraci?? de l'escalador de l'ombrejat?"
 
-#: gui/options.cpp:3860
+#: gui/options.cpp:3873
 #, c-format
 msgid "Reverting automatically in %d seconds"
 msgstr "S'est?? revertint autom??ticament en %d segons"
@@ -2776,7 +2792,7 @@ msgstr "Enregistra"
 msgid "Playback"
 msgstr "Reproducci??"
 
-#: gui/recorderdialog.cpp:76 gui/widgets/grid.cpp:341
+#: gui/recorderdialog.cpp:76 gui/widgets/grid.cpp:343
 msgid "Edit"
 msgstr "Edita"
 
@@ -2990,11 +3006,11 @@ msgstr ""
 msgid "Clear value"
 msgstr "Neteja el valor"
 
-#: gui/widgets/grid.cpp:335
+#: gui/widgets/grid.cpp:337
 msgid "Play"
 msgstr "Jugar"
 
-#: gui/widgets/grid.cpp:338
+#: gui/widgets/grid.cpp:340
 msgid "Saves"
 msgstr "Partides desades"
 
diff --git a/po/cs_CZ.po b/po/cs_CZ.po
index 29c7e1a0662..80dcb8556b6 100644
--- a/po/cs_CZ.po
+++ b/po/cs_CZ.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.7.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-13 10:20+0000\n"
+"POT-Creation-Date: 2026-06-13 13:52+0000\n"
 "PO-Revision-Date: 2026-02-09 20:22+0000\n"
 "Last-Translator: AsciiWolf <mail at asciiwolf.com>\n"
 "Language-Team: Czech <https://translations.scummvm.org/projects/scummvm/"
@@ -125,7 +125,7 @@ msgstr "J??t nahoru"
 #: gui/downloadpacksdialog.cpp:465 gui/editgamedialog.cpp:340
 #: gui/editrecorddialog.cpp:66 gui/integrity-dialog.cpp:122
 #: gui/integrity-dialog.cpp:253 gui/filebrowser-dialog.cpp:68
-#: gui/fluidsynth-dialog.cpp:153 gui/massadd.cpp:93 gui/options.cpp:2416
+#: gui/fluidsynth-dialog.cpp:153 gui/massadd.cpp:93 gui/options.cpp:2418
 #: gui/predictivedialog.cpp:68 gui/printing-dialog.cpp:96
 #: gui/recorderdialog.cpp:72 gui/recorderdialog.cpp:211
 #: gui/remotebrowser.cpp:59 gui/saveload-dialog.cpp:64
@@ -199,23 +199,23 @@ msgid ""
 "so your browser can forward data to ScummVM"
 msgstr ""
 
-#: gui/cloudconnectionwizard.cpp:257 gui/options.cpp:3776
+#: gui/cloudconnectionwizard.cpp:257 gui/options.cpp:3789
 msgid "Stop server"
 msgstr "Zastavit server"
 
-#: gui/cloudconnectionwizard.cpp:257 gui/options.cpp:2840
+#: gui/cloudconnectionwizard.cpp:257 gui/options.cpp:2849
 msgid "Run server"
 msgstr "Spustit server"
 
-#: gui/cloudconnectionwizard.cpp:258 gui/options.cpp:3777
+#: gui/cloudconnectionwizard.cpp:258 gui/options.cpp:3790
 msgid "Stop local webserver"
 msgstr "Zastavit m??stn?? internetov?? server"
 
-#: gui/cloudconnectionwizard.cpp:258 gui/options.cpp:2840
+#: gui/cloudconnectionwizard.cpp:258 gui/options.cpp:2849
 msgid "Run local webserver"
 msgstr "Spustit m??stn?? internetov?? server"
 
-#: gui/cloudconnectionwizard.cpp:266 gui/options.cpp:2841 gui/options.cpp:3784
+#: gui/cloudconnectionwizard.cpp:266 gui/options.cpp:2850 gui/options.cpp:3797
 msgid "Not running"
 msgstr "Nespu??t??no"
 
@@ -348,8 +348,8 @@ msgstr "Jin?? ??lo??i??t?? pr??v?? pracuje. Chcete ho p??eru??it?"
 #: gui/cloudconnectionwizard.cpp:530 gui/downloaddialog.cpp:118
 #: gui/downloaddialog.cpp:152 gui/filebrowser-dialog.cpp:135
 #: gui/fluidsynth-dialog.cpp:210 gui/launcher.cpp:428 gui/launcher.cpp:457
-#: gui/launcher.cpp:534 gui/launcher.cpp:1938 gui/options.cpp:3485
-#: gui/options.cpp:3859 base/main.cpp:608
+#: gui/launcher.cpp:534 gui/launcher.cpp:1970 gui/options.cpp:3498
+#: gui/options.cpp:3872 base/main.cpp:608
 #: backends/events/default/default-events.cpp:193 engines/buried/buried.cpp:598
 #: engines/chamber/metaengine.cpp:91 engines/director/events.cpp:122
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:139
@@ -368,8 +368,8 @@ msgstr "Ano"
 #: gui/cloudconnectionwizard.cpp:530 gui/downloaddialog.cpp:118
 #: gui/downloaddialog.cpp:153 gui/filebrowser-dialog.cpp:135
 #: gui/fluidsynth-dialog.cpp:210 gui/launcher.cpp:428 gui/launcher.cpp:457
-#: gui/launcher.cpp:534 gui/launcher.cpp:1939 gui/options.cpp:3485
-#: gui/options.cpp:3859 engines/buried/buried.cpp:598
+#: gui/launcher.cpp:534 gui/launcher.cpp:1971 gui/options.cpp:3498
+#: gui/options.cpp:3872 engines/buried/buried.cpp:598
 #: engines/chamber/metaengine.cpp:97 engines/director/events.cpp:122
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:139
 #: engines/efh/metaengine.cpp:368 engines/grim/grim.cpp:366
@@ -400,7 +400,7 @@ msgstr ""
 #: gui/editrecorddialog.cpp:67 gui/integrity-dialog.cpp:260
 #: gui/fluidsynth-dialog.cpp:154 gui/massadd.cpp:90 gui/message.cpp:156
 #: gui/message.cpp:160 gui/message.cpp:231 gui/message.cpp:270
-#: gui/message.cpp:274 gui/options.cpp:2418 gui/predictivedialog.cpp:69
+#: gui/message.cpp:274 gui/options.cpp:2420 gui/predictivedialog.cpp:69
 #: gui/saveload-dialog.cpp:1244 base/main.cpp:675 base/main.cpp:700
 #: dists/android.strings.xml.cpp:33 engines/engine.cpp:846
 #: backends/platform/wii/options.cpp:46 engines/buried/buried.cpp:622
@@ -598,11 +598,11 @@ msgstr ""
 msgid "Proceed"
 msgstr "Pokra??ovat"
 
-#: gui/dump-all-dialogs.cpp:123 gui/options.cpp:3347
+#: gui/dump-all-dialogs.cpp:123 gui/options.cpp:3360
 msgid "icon packs"
 msgstr "bal????ek ikon"
 
-#: gui/dump-all-dialogs.cpp:127 gui/options.cpp:3358
+#: gui/dump-all-dialogs.cpp:127 gui/options.cpp:3371
 msgid "shader packs"
 msgstr "q"
 
@@ -614,11 +614,11 @@ msgstr "Vybrat hru:"
 msgid "Game"
 msgstr "Hra"
 
-#: gui/editgamedialog.cpp:164 gui/options.cpp:2075 gui/options.cpp:2254
+#: gui/editgamedialog.cpp:164 gui/options.cpp:2075 gui/options.cpp:2256
 msgid "GFX"
 msgstr "GFX"
 
-#: gui/editgamedialog.cpp:164 gui/options.cpp:2075 gui/options.cpp:2254
+#: gui/editgamedialog.cpp:164 gui/options.cpp:2075 gui/options.cpp:2256
 #: backends/graphics/atari/atari-graphics.cpp:1056
 #: backends/graphics/sdl/sdl-graphics.cpp:487
 msgid "Graphics"
@@ -633,11 +633,11 @@ msgctxt "lowres"
 msgid "Override global graphic settings"
 msgstr "Potla??it glob??ln?? nastaven?? obrazu"
 
-#: gui/editgamedialog.cpp:185 gui/options.cpp:2288 engines/dialogs.cpp:354
+#: gui/editgamedialog.cpp:185 gui/options.cpp:2290 engines/dialogs.cpp:354
 msgid "Keymaps"
 msgstr "Rozlo??en?? kl??vesnice"
 
-#: gui/editgamedialog.cpp:197 gui/options.cpp:2300 engines/dialogs.cpp:366
+#: gui/editgamedialog.cpp:197 gui/options.cpp:2302 engines/dialogs.cpp:366
 msgid "Backend"
 msgstr ""
 
@@ -652,7 +652,7 @@ msgctxt "lowres"
 msgid "Override global backend settings"
 msgstr "Potla??it glob??ln?? nastaven?? zvuku"
 
-#: gui/editgamedialog.cpp:220 gui/options.cpp:2318 engines/dialogs.cpp:329
+#: gui/editgamedialog.cpp:220 gui/options.cpp:2320 engines/dialogs.cpp:329
 msgid "Audio"
 msgstr "Zvuk"
 
@@ -665,11 +665,11 @@ msgctxt "lowres"
 msgid "Override global audio settings"
 msgstr "Potla??it glob??ln?? nastaven?? zvuku"
 
-#: gui/editgamedialog.cpp:234 gui/options.cpp:2323
+#: gui/editgamedialog.cpp:234 gui/options.cpp:2325
 msgid "Volume"
 msgstr "Hlasitost"
 
-#: gui/editgamedialog.cpp:236 gui/options.cpp:2325
+#: gui/editgamedialog.cpp:236 gui/options.cpp:2327
 msgctxt "lowres"
 msgid "Volume"
 msgstr "Hlasitost"
@@ -683,7 +683,7 @@ msgctxt "lowres"
 msgid "Override global volume settings"
 msgstr "Potla??it glob??ln?? nastaven?? hlasitosti"
 
-#: gui/editgamedialog.cpp:252 gui/options.cpp:2333
+#: gui/editgamedialog.cpp:252 gui/options.cpp:2335
 msgid "MIDI"
 msgstr "MIDI"
 
@@ -696,7 +696,7 @@ msgctxt "lowres"
 msgid "Override global MIDI settings"
 msgstr "Potla??it glob??ln?? nastaven?? MIDI"
 
-#: gui/editgamedialog.cpp:267 gui/options.cpp:2339
+#: gui/editgamedialog.cpp:267 gui/options.cpp:2341
 msgid "MT-32"
 msgstr "MT-32"
 
@@ -709,11 +709,11 @@ msgctxt "lowres"
 msgid "Override global MT-32 settings"
 msgstr "Potla??it glob??ln?? nastaven?? MT-32"
 
-#: gui/editgamedialog.cpp:281 gui/options.cpp:2346
+#: gui/editgamedialog.cpp:281 gui/options.cpp:2348
 msgid "Paths"
 msgstr "Cesty"
 
-#: gui/editgamedialog.cpp:283 gui/options.cpp:2348
+#: gui/editgamedialog.cpp:283 gui/options.cpp:2350
 msgctxt "lowres"
 msgid "Paths"
 msgstr "Cesty"
@@ -735,7 +735,7 @@ msgctxt "lowres"
 msgid "Game Path:"
 msgstr "Cesta Hry:"
 
-#: gui/editgamedialog.cpp:303 gui/options.cpp:2555
+#: gui/editgamedialog.cpp:303 gui/options.cpp:2557
 msgid "Extra Path:"
 msgstr "Dodate??n?? Cesta:"
 
@@ -744,33 +744,33 @@ msgstr "Dodate??n?? Cesta:"
 msgid "Specifies path to additional data used by the game"
 msgstr "Stanov?? cestu pro dodate??n?? data pou??it?? ve h??e"
 
-#: gui/editgamedialog.cpp:305 gui/options.cpp:2557
+#: gui/editgamedialog.cpp:305 gui/options.cpp:2559
 msgctxt "lowres"
 msgid "Extra Path:"
 msgstr "Dodate??n?? Cesta:"
 
-#: gui/editgamedialog.cpp:306 gui/options.cpp:2530 gui/options.cpp:2558
-#: gui/options.cpp:2567 gui/options.cpp:2848
+#: gui/editgamedialog.cpp:306 gui/options.cpp:2532 gui/options.cpp:2560
+#: gui/options.cpp:2569 gui/options.cpp:2857
 #: backends/platform/libretro/src/libretro-options-widget.cpp:277
 msgctxt "path"
 msgid "None"
 msgstr "????dn??"
 
-#: gui/editgamedialog.cpp:312 gui/options.cpp:2519
+#: gui/editgamedialog.cpp:312 gui/options.cpp:2521
 msgid "Save Path:"
 msgstr "Cesta pro ulo??en??:"
 
 #: gui/editgamedialog.cpp:312 gui/editgamedialog.cpp:314
-#: gui/editgamedialog.cpp:315 gui/options.cpp:2519 gui/options.cpp:2521
+#: gui/editgamedialog.cpp:315 gui/options.cpp:2521 gui/options.cpp:2523
 msgid "Specifies where your saved games are put"
 msgstr "Stanovuje, kam jsou um??st??ny va??e ulo??en?? hry"
 
-#: gui/editgamedialog.cpp:314 gui/options.cpp:2521
+#: gui/editgamedialog.cpp:314 gui/options.cpp:2523
 msgctxt "lowres"
 msgid "Save Path:"
 msgstr "Cesta pro ulo??en??:"
 
-#: gui/editgamedialog.cpp:315 gui/options.cpp:1844 gui/options.cpp:2522
+#: gui/editgamedialog.cpp:315 gui/options.cpp:1844 gui/options.cpp:2524
 #: backends/platform/wii/options.cpp:55
 msgid "Default"
 msgstr "V??choz??"
@@ -829,8 +829,8 @@ msgstr "Jazyk hry. Toto z va???? ??pan??lsk?? verze neud??l?? Anglickou"
 #: gui/editgamedialog.cpp:365 gui/editgamedialog.cpp:380 gui/options.cpp:1613
 #: gui/options.cpp:1627 gui/options.cpp:1644 gui/options.cpp:1658
 #: gui/options.cpp:1675 gui/options.cpp:1713 gui/options.cpp:1729
-#: gui/options.cpp:2144 gui/options.cpp:2600 gui/options.cpp:2674
-#: gui/options.cpp:2888 gui/options.cpp:3389 audio/null.cpp:45
+#: gui/options.cpp:2144 gui/options.cpp:2602 gui/options.cpp:2683
+#: gui/options.cpp:2897 gui/options.cpp:3402 audio/null.cpp:45
 #: backends/platform/android/options.cpp:157
 #: backends/platform/android/options.cpp:158
 #: backends/platform/android/options.cpp:186
@@ -857,7 +857,7 @@ msgctxt "lowres"
 msgid "Platform:"
 msgstr "Platforma:"
 
-#: gui/editgamedialog.cpp:566 gui/options.cpp:3413
+#: gui/editgamedialog.cpp:566 gui/options.cpp:3426
 msgid "Select SoundFont"
 msgstr "Vybrat SoundFont"
 
@@ -865,11 +865,11 @@ msgstr "Vybrat SoundFont"
 msgid "Select additional game directory"
 msgstr "Vyberte dodate??n?? adres???? hry"
 
-#: gui/editgamedialog.cpp:618 gui/options.cpp:3261
+#: gui/editgamedialog.cpp:618 gui/options.cpp:3274
 msgid "Select directory for saved games"
 msgstr "Vyberte adres???? pro ulo??en?? hry"
 
-#: gui/editgamedialog.cpp:625 gui/options.cpp:3268
+#: gui/editgamedialog.cpp:625 gui/options.cpp:3281
 msgid "The chosen directory cannot be written to. Please select another one."
 msgstr "Do zvolen??ho adres????e nelze zapisovat. Vyberte, pros??m, jin??."
 
@@ -1013,7 +1013,7 @@ msgstr "Sinus"
 msgid "Triangle"
 msgstr "Troj??heln??k"
 
-#: gui/fluidsynth-dialog.cpp:139 gui/options.cpp:2366
+#: gui/fluidsynth-dialog.cpp:139 gui/options.cpp:2368
 msgid "Misc"
 msgstr "R??zn??"
 
@@ -1053,7 +1053,7 @@ msgstr ""
 "Opravdu chcete resetovat ve??ker?? nastaven?? FluidSynth na jejich v??choz?? "
 "hodnoty?"
 
-#: gui/gui-manager.cpp:159 gui/options.cpp:2357
+#: gui/gui-manager.cpp:159 gui/options.cpp:2359
 msgid "GUI"
 msgstr "GUI"
 
@@ -1267,7 +1267,7 @@ msgid ""
 "and illegal game copies.\n"
 msgstr ""
 
-#: gui/helpdialog.cpp:68 gui/options.cpp:2377
+#: gui/helpdialog.cpp:68 gui/options.cpp:2379
 msgid "Cloud"
 msgstr "Cloud"
 
@@ -1569,90 +1569,90 @@ msgstr "Seznam"
 msgid "Grid view"
 msgstr "M??????ka"
 
-#: gui/launcher.cpp:1145
+#: gui/launcher.cpp:1168
 msgid "~S~tart"
 msgstr "~S~pustit"
 
-#: gui/launcher.cpp:1145
+#: gui/launcher.cpp:1168
 msgid "Start selected game"
 msgstr "Spustit zvolenou hru"
 
-#: gui/launcher.cpp:1148
+#: gui/launcher.cpp:1171
 msgid "~L~oad..."
 msgstr "~N~ahr??t..."
 
-#: gui/launcher.cpp:1148
+#: gui/launcher.cpp:1171
 msgid "Load saved game for selected game"
 msgstr "Nahr??t ulo??enou pozici pro zvolenou hru"
 
-#: gui/launcher.cpp:1150
+#: gui/launcher.cpp:1173
 msgid "Record..."
 msgstr "Nahr??t..."
 
-#: gui/launcher.cpp:1156
+#: gui/launcher.cpp:1179
 msgid "~G~ame Options..."
 msgstr "Volby ~h~ry..."
 
-#: gui/launcher.cpp:1156
+#: gui/launcher.cpp:1179
 msgid "Change game options"
 msgstr "Zm??nit volby hry"
 
-#: gui/launcher.cpp:1156
+#: gui/launcher.cpp:1179
 msgctxt "lowres"
 msgid "~G~ame Opts..."
 msgstr "Volby ~h~ry..."
 
-#: gui/launcher.cpp:1242
+#: gui/launcher.cpp:1265
 msgid "Add-on"
 msgstr ""
 
 #. I18N: List grouping when no engine is specified
-#: gui/launcher.cpp:1309 gui/launcher.cpp:1554
+#: gui/launcher.cpp:1335 gui/launcher.cpp:1580
 #, fuzzy
 msgid "Unknown Engine"
 msgstr "Nezn??m?? varianta"
 
 #. I18N: List grouping when no publisher is specified
 #. I18N: List group when no publisher is specified
-#: gui/launcher.cpp:1326 gui/launcher.cpp:1571
+#: gui/launcher.cpp:1352 gui/launcher.cpp:1597
 #, fuzzy
 msgid "Unknown Publisher"
 msgstr "Nezn??m?? autor"
 
 #. I18N: List group when no game series is specified
 #. I18N: List grouping when no game series is specified
-#: gui/launcher.cpp:1343 gui/launcher.cpp:1588
+#: gui/launcher.cpp:1369 gui/launcher.cpp:1614
 msgid "No Series"
 msgstr ""
 
 #. I18N: List group when no language is specified
-#: gui/launcher.cpp:1357 gui/launcher.cpp:1602
+#: gui/launcher.cpp:1383 gui/launcher.cpp:1628
 msgid "Language not detected"
 msgstr ""
 
 #. I18N: List group when no platform is specified
-#: gui/launcher.cpp:1371 gui/launcher.cpp:1616
+#: gui/launcher.cpp:1397 gui/launcher.cpp:1642
 msgid "Platform not detected"
 msgstr ""
 
 #. I18N: List group when no year is specified
-#: gui/launcher.cpp:1388 gui/launcher.cpp:1633
+#: gui/launcher.cpp:1414 gui/launcher.cpp:1659
 #, fuzzy
 #| msgid "Unknown error"
 msgid "Unknown Year"
 msgstr "Nezn??m?? chyba"
 
 #. I18N: Group for All items
-#: gui/launcher.cpp:1640 gui/widgets/groupedlist.cpp:82
+#: gui/launcher.cpp:1666 gui/widgets/groupedlist.cpp:82
 #, fuzzy
 msgid "All"
 msgstr "Alt"
 
-#: gui/launcher.cpp:1812
+#: gui/launcher.cpp:1844
 msgid "Icons per row:"
 msgstr ""
 
-#: gui/launcher.cpp:1869
+#: gui/launcher.cpp:1901
 #, fuzzy, c-format
 #| msgid ""
 #| "Do you really want to remove the following %d game configuration(s)?\n"
@@ -2160,189 +2160,201 @@ msgctxt "lowres"
 msgid "Speech volume:"
 msgstr "Hlasitost ??e??i:"
 
-#: gui/options.cpp:2266
+#: gui/options.cpp:2268
 msgid "Control"
 msgstr "Ovl??d??n??"
 
-#: gui/options.cpp:2379
+#: gui/options.cpp:2381
 msgctxt "lowres"
 msgid "Cloud"
 msgstr "Cloud"
 
-#: gui/options.cpp:2394
+#: gui/options.cpp:2396
 msgid "LAN"
 msgstr "LAN"
 
-#: gui/options.cpp:2396
+#: gui/options.cpp:2398
 msgctxt "lowres"
 msgid "LAN"
 msgstr "LAN"
 
-#: gui/options.cpp:2405
+#: gui/options.cpp:2407
 msgid "Accessibility"
 msgstr "P????stupnost"
 
-#: gui/options.cpp:2407
+#: gui/options.cpp:2409
 msgctxt "lowres"
 msgid "Accessibility"
 msgstr "P????stupnost"
 
-#: gui/options.cpp:2416
+#: gui/options.cpp:2418
 msgid "Discard changes and close the dialog"
 msgstr ""
 
-#: gui/options.cpp:2417
+#: gui/options.cpp:2419
 msgid "Apply"
 msgstr "Pou????t"
 
-#: gui/options.cpp:2417
+#: gui/options.cpp:2419
 msgid "Apply changes without closing the dialog"
 msgstr ""
 
-#: gui/options.cpp:2418
+#: gui/options.cpp:2420
 msgid "Apply changes and close the dialog"
 msgstr ""
 
-#: gui/options.cpp:2508
+#: gui/options.cpp:2510
 msgid "FluidSynth Settings"
 msgstr "Nastaven?? FluidSynth"
 
-#: gui/options.cpp:2522
+#: gui/options.cpp:2524
 msgid ""
 "Specifies where your saved games are put. A red coloring indicates the value "
 "is temporary and will not get saved"
 msgstr ""
 
-#: gui/options.cpp:2527
+#: gui/options.cpp:2529
 msgid "Theme Path:"
 msgstr "Cesta ke Vzhledu:"
 
-#: gui/options.cpp:2529
+#: gui/options.cpp:2531
 msgctxt "lowres"
 msgid "Theme Path:"
 msgstr "Cesta ke Vzhledu:"
 
-#: gui/options.cpp:2535
+#: gui/options.cpp:2537
 #, fuzzy
 msgid "Icon Path:"
 msgstr "Cesta /root/:"
 
-#: gui/options.cpp:2537
+#: gui/options.cpp:2539
 #, fuzzy
 msgctxt "lowres"
 msgid "Icon Path:"
 msgstr "Cesta /root/:"
 
-#: gui/options.cpp:2538 gui/options.cpp:2548
+#: gui/options.cpp:2540 gui/options.cpp:2550
 #, fuzzy
 msgctxt "path"
 msgid "Default"
 msgstr "V??choz??"
 
-#: gui/options.cpp:2545
+#: gui/options.cpp:2547
 #, fuzzy
 #| msgid "Game Path:"
 msgid "DLC Path:"
 msgstr "Cesta Hry:"
 
-#: gui/options.cpp:2547
+#: gui/options.cpp:2549
 #, fuzzy
 #| msgid "Game Path:"
 msgctxt "lowres"
 msgid "DLC Path:"
 msgstr "Cesta Hry:"
 
-#: gui/options.cpp:2555 gui/options.cpp:2557 gui/options.cpp:2558
+#: gui/options.cpp:2557 gui/options.cpp:2559 gui/options.cpp:2560
 msgid "Specifies path to additional data used by all games or ScummVM"
 msgstr "Stanov?? cestu k dodate??n??m dat??m pou????van?? v??emi hrami nebo ScummVM"
 
-#: gui/options.cpp:2564
+#: gui/options.cpp:2566
 msgid "Plugins Path:"
 msgstr "Cesta k Plugin??m:"
 
-#: gui/options.cpp:2566
+#: gui/options.cpp:2568
 msgctxt "lowres"
 msgid "Plugins Path:"
 msgstr "Cesta k Plugin??m:"
 
-#: gui/options.cpp:2576
+#: gui/options.cpp:2578
 msgid "ScummVM config path: "
 msgstr ""
 
-#: gui/options.cpp:2590
+#: gui/options.cpp:2592
 msgid "ScummVM log path: "
 msgstr ""
 
-#: gui/options.cpp:2595
+#: gui/options.cpp:2597
 msgid "View"
 msgstr ""
 
 #. I18N: Referring to the last path memorized when adding a game
-#: gui/options.cpp:2605 gui/options.cpp:3389
+#: gui/options.cpp:2607 gui/options.cpp:3402
 msgid "Last browser path: "
 msgstr ""
 
-#: gui/options.cpp:2610
+#: gui/options.cpp:2612
 msgid "Theme:"
 msgstr "Vzhled:"
 
-#: gui/options.cpp:2615
+#: gui/options.cpp:2617
 #, fuzzy
 msgid "GUI scale:"
 msgstr "Jazyk GUI:"
 
-#: gui/options.cpp:2632
+#: gui/options.cpp:2634
 #, c-format
 msgid "%d%% - Custom"
 msgstr ""
 
-#: gui/options.cpp:2636
+#: gui/options.cpp:2638
 msgid "GUI renderer:"
 msgstr "Vykreslova?? GUI:"
 
-#: gui/options.cpp:2649
+#: gui/options.cpp:2651
 #, fuzzy
 msgid "Return to the launcher when leaving a game"
 msgstr "P??i opu??t??n?? hry se v??dy se vra??te ke spou??t????i"
 
-#: gui/options.cpp:2650
+#: gui/options.cpp:2652
 msgid ""
 "Return to the launcher when leaving a game instead of closing ScummVM\n"
 "(this feature is not supported by all games)."
 msgstr ""
 
-#: gui/options.cpp:2657
+#: gui/options.cpp:2659
 msgid "Ask for confirmation on exit"
 msgstr "????dat potvrzen?? opu??t??n??"
 
-#: gui/options.cpp:2658
+#: gui/options.cpp:2660
 msgid "Ask for permission when closing ScummVM or leaving a game."
 msgstr "po????dat o povolen?? p??i uzav??en?? ScummVM nebo opu??t??n?? hry."
 
-#: gui/options.cpp:2664
+#: gui/options.cpp:2666
 #, fuzzy
 msgid "Disable fixed font scaling"
 msgstr "Nelze ulo??it hru"
 
-#: gui/options.cpp:2665
+#: gui/options.cpp:2667
 msgid ""
 "Do not upscale fixed size fonts in the GUI. This reduces artefacts on low "
-"resolution screens"
+"resolution screens."
 msgstr ""
 
-#: gui/options.cpp:2671
+#: gui/options.cpp:2673
+#, fuzzy
+#| msgid "Enable smooth scrolling when walking"
+msgid "Enable kinetic scrolling in lists"
+msgstr "Povolit plynul?? posunov??n?? p??i ch??zi"
+
+#: gui/options.cpp:2674
+#, fuzzy
+#| msgid "Enable smooth scrolling when walking"
+msgid "Enable smooth, momentum-based scrolling in list widgets."
+msgstr "Povolit plynul?? posunov??n?? p??i ch??zi"
+
+#: gui/options.cpp:2680
 msgid "GUI language:"
 msgstr "Jazyk GUI:"
 
-#: gui/options.cpp:2671
+#: gui/options.cpp:2680
 msgid "Language of ScummVM GUI"
 msgstr "Jazyk GUI ScummVM"
 
-#: gui/options.cpp:2697
+#: gui/options.cpp:2706
 msgid "Switch the ScummVM GUI language to the game language"
 msgstr "P??epnout jazyk GUI do jazyka hry"
 
-#: gui/options.cpp:2698
+#: gui/options.cpp:2707
 msgid ""
 "When starting a game, change the ScummVM GUI language to the game language. "
 "That way, if a game uses the ScummVM save and load dialogs, they are in the "
@@ -2351,11 +2363,11 @@ msgstr ""
 "P??i spou??t??n?? hry p??epne jazyk GUI do jazyka hry. ????m?? zajist??, ??e dialogov?? "
 "okna ScummVM budou zobrazena ve stejn??m jazyce."
 
-#: gui/options.cpp:2710
+#: gui/options.cpp:2719
 msgid "Use native system file browser"
 msgstr "Pou????t syst??mov?? prohl????e?? soubor??"
 
-#: gui/options.cpp:2711
+#: gui/options.cpp:2720
 msgid ""
 "Use the native system file browser instead of the ScummVM one to select a "
 "file or directory."
@@ -2363,81 +2375,81 @@ msgstr ""
 "Pou??it?? syst??mov??ho prohl????e??e soubor?? k v??b??ru souboru nebo adres????e m??sto "
 "prohl????e??e ScummVM."
 
-#: gui/options.cpp:2718
+#: gui/options.cpp:2727
 #, fuzzy
 #| msgid "Download"
 msgid "Download Icons"
 msgstr "St??hnout"
 
-#: gui/options.cpp:2718
+#: gui/options.cpp:2727
 #, fuzzy
 msgid "Check on the scummvm.org website for updates of icon packs"
 msgstr "Zkontrolovat Aktualizace..."
 
-#: gui/options.cpp:2724
+#: gui/options.cpp:2733
 msgid "Autosave:"
 msgstr "Autoukl??d??n??:"
 
-#: gui/options.cpp:2726
+#: gui/options.cpp:2735
 msgctxt "lowres"
 msgid "Autosave:"
 msgstr "Autoukl??d??n??:"
 
-#: gui/options.cpp:2737
+#: gui/options.cpp:2746
 msgid "Random seed:"
 msgstr ""
 
-#: gui/options.cpp:2737
+#: gui/options.cpp:2746
 msgid "Seed for initializing all random number generators"
 msgstr ""
 
-#: gui/options.cpp:2745
+#: gui/options.cpp:2754
 #, fuzzy
 msgid "Debug level:"
 msgstr "Lad??c?? program"
 
 #. I18N: Debug level -1, no messages
-#: gui/options.cpp:2749 gui/options.cpp:2886 audio/fmopl.cpp:83
+#: gui/options.cpp:2758 gui/options.cpp:2895 audio/fmopl.cpp:83
 msgid "None"
 msgstr "Pr??zdn??"
 
 #. I18N: Debug level 11, all messages
-#: gui/options.cpp:2755
+#: gui/options.cpp:2764
 msgid "11 (all)"
 msgstr ""
 
-#: gui/options.cpp:2759
+#: gui/options.cpp:2768
 #, fuzzy
 msgid "Enable Discord integration"
 msgstr "Povolit cenzuru obsahu"
 
-#: gui/options.cpp:2760
+#: gui/options.cpp:2769
 msgid ""
 "Show information about the games you are playing on Discord if the Discord "
 "client is running."
 msgstr ""
 
-#: gui/options.cpp:2770 gui/updates-dialog.cpp:87
+#: gui/options.cpp:2779 gui/updates-dialog.cpp:87
 msgid "Update check:"
 msgstr "Ov????en?? aktualizac??:"
 
-#: gui/options.cpp:2770
+#: gui/options.cpp:2779
 msgid "How often to check ScummVM updates"
 msgstr "Jak ??asto ScummVM kontroluje aktualizace"
 
-#: gui/options.cpp:2781
+#: gui/options.cpp:2790
 msgid "Check now"
 msgstr "Zkontrolovat nyn??"
 
-#: gui/options.cpp:2788
+#: gui/options.cpp:2797
 msgid "Active storage:"
 msgstr "Aktivn?? ??lo??i??t??:"
 
-#: gui/options.cpp:2788
+#: gui/options.cpp:2797
 msgid "Active cloud storage"
 msgstr "Aktivn?? cloudov?? ??lo??i??t??"
 
-#: gui/options.cpp:2797
+#: gui/options.cpp:2806
 #, fuzzy
 #| msgctxt "lowres"
 #| msgid ""
@@ -2450,7 +2462,7 @@ msgstr ""
 "4. ??lo??i??t?? je??t?? nen?? povoleno. Ov????te spr??vnost u??ivatelsk??ho jm??na a "
 "povolte jej:"
 
-#: gui/options.cpp:2799
+#: gui/options.cpp:2808
 #, fuzzy
 #| msgid ""
 #| "4. Storage is not yet enabled. Verify that username is correct and enable "
@@ -2461,103 +2473,103 @@ msgstr ""
 "4. ??lo??i??t?? nen?? je??t?? povoleno. Ov????te spr??vnost u??ivatelsk??ho jm??na a "
 "povolte jej:"
 
-#: gui/options.cpp:2800
+#: gui/options.cpp:2809
 msgid "Enable storage"
 msgstr "Povolit ??lo??i??t??"
 
-#: gui/options.cpp:2800
+#: gui/options.cpp:2809
 msgid "Confirm you want to use this account for this storage"
 msgstr "Potvr??te, ??e chcete pou????t tento ????et pro p????stup k tomuto ??lo??i??ti"
 
-#: gui/options.cpp:2802 backends/platform/wii/options.cpp:113
+#: gui/options.cpp:2811 backends/platform/wii/options.cpp:113
 msgid "Username:"
 msgstr "U??ivatelsk?? jm??no:"
 
-#: gui/options.cpp:2802
+#: gui/options.cpp:2811
 msgid "Username used by this storage"
 msgstr "U??ivatelsk?? jm??na kter?? toto ??lo??i??t?? vyu????vaj??"
 
-#: gui/options.cpp:2803
+#: gui/options.cpp:2812
 msgid "<none>"
 msgstr "<????dn??>"
 
-#: gui/options.cpp:2805
+#: gui/options.cpp:2814
 msgid "Used space:"
 msgstr "Vyu??it?? prostor:"
 
-#: gui/options.cpp:2805
+#: gui/options.cpp:2814
 msgid "Space used by ScummVM's saved games on this storage"
 msgstr "Prostor vyu??it?? ulo??en??mi hrami ScummVM v tomto ??lo??i??ti"
 
-#: gui/options.cpp:2808
+#: gui/options.cpp:2817
 msgid "Last sync:"
 msgstr "Posledn?? synchronizace:"
 
-#: gui/options.cpp:2808
+#: gui/options.cpp:2817
 msgid "When was the last time saved games were synced with this storage"
 msgstr ""
 "Kdy byla provedena posledn?? synchronizace ulo??en??ch her s t??mto ulo??i??t??m"
 
-#: gui/options.cpp:2809 gui/options.cpp:3693
+#: gui/options.cpp:2818 gui/options.cpp:3706
 msgid "<never>"
 msgstr "<nikdy>"
 
-#: gui/options.cpp:2811
+#: gui/options.cpp:2820
 msgctxt "lowres"
 msgid "Saved games sync automatically on launch, after saving and on loading."
 msgstr ""
 "Ulo??en?? hry jsou automaticky synchronizov??ny p??i spu??t??n?? a po ulo??en?? ??i "
 "na??ten?? hry."
 
-#: gui/options.cpp:2813
+#: gui/options.cpp:2822
 msgid "Saved games sync automatically on launch, after saving and on loading."
 msgstr ""
 "Ulo??en?? hry jsou automaticky synchronizov??ny p??i spu??t??n?? a po ulo??en?? ??i "
 "na??ten?? hry."
 
-#: gui/options.cpp:2814
+#: gui/options.cpp:2823
 msgid "Sync now"
 msgstr "Synchronizovat nyn??"
 
-#: gui/options.cpp:2814
+#: gui/options.cpp:2823
 msgid "Start saved games sync"
 msgstr "Spustit synchronizaci ulo??en??ch her"
 
-#: gui/options.cpp:2817
+#: gui/options.cpp:2826
 msgctxt "lowres"
 msgid "You can download game files from your cloud ScummVM folder:"
 msgstr "Soubory hry m????ete st??hnout z va???? cloud slo??ky ScummVM:"
 
-#: gui/options.cpp:2819
+#: gui/options.cpp:2828
 msgid "You can download game files from your cloud ScummVM folder:"
 msgstr "Soubory hry m????ete st??hnout z va???? cloud slo??ky ScummVM:"
 
-#: gui/options.cpp:2820
+#: gui/options.cpp:2829
 msgid "Download game files"
 msgstr "St??hnout soubory hry"
 
-#: gui/options.cpp:2820
+#: gui/options.cpp:2829
 msgid "Open downloads manager dialog"
 msgstr "Otev????t dialogov?? okno spr??vce stahovan??"
 
-#: gui/options.cpp:2823
+#: gui/options.cpp:2832
 msgctxt "lowres"
 msgid "To change account for this storage, disconnect and connect again:"
 msgstr "Pro zm??nu ????tu k tomuto ulo??i??ti se odpojte a znovu p??ipojte:"
 
-#: gui/options.cpp:2825
+#: gui/options.cpp:2834
 msgid "To change account for this storage, disconnect and connect again:"
 msgstr "Pro zm??nu ????tu pro toto ??lo??i??t?? se odpojte a znovu p??ipojte:"
 
-#: gui/options.cpp:2826
+#: gui/options.cpp:2835
 msgid "Disconnect"
 msgstr "Odpojit"
 
-#: gui/options.cpp:2826
+#: gui/options.cpp:2835
 msgid "Stop using this storage on this device"
 msgstr "P??estat pou????vat toto ??lo??i??t?? na tomto za????zen??"
 
-#: gui/options.cpp:2829
+#: gui/options.cpp:2838
 #, fuzzy
 #| msgctxt "lowres"
 #| msgid "This storage is not connected yet! To connect,"
@@ -2565,79 +2577,79 @@ msgctxt "lowres"
 msgid "This storage is not connected yet!"
 msgstr "Toto ??lo??i??t?? nen?? je??t?? p??ipojeno! Pro p??ipojen??"
 
-#: gui/options.cpp:2831
+#: gui/options.cpp:2840
 #, fuzzy
 #| msgid "This storage is not connected yet! To connect,"
 msgid "This storage is not connected yet!"
 msgstr "Toto ??lo??i??t?? nen?? je??t?? p??ipojeno! Pro p??ipojen??"
 
-#: gui/options.cpp:2832
+#: gui/options.cpp:2841
 #, fuzzy
 #| msgid "3. Connect"
 msgid "Connect"
 msgstr "3. P??ipojit"
 
-#: gui/options.cpp:2832
+#: gui/options.cpp:2841
 msgid "Connect your cloud storage account"
 msgstr "P??ipojen?? ????tu cloudov??ho ??lo??i??t??"
 
-#: gui/options.cpp:2845
+#: gui/options.cpp:2854
 msgctxt "lowres"
 msgid "/root/ Path:"
 msgstr "Cesta /root/:"
 
-#: gui/options.cpp:2845 gui/options.cpp:2847 gui/options.cpp:2848
+#: gui/options.cpp:2854 gui/options.cpp:2856 gui/options.cpp:2857
 msgid "Select which directory will be shown as /root/ in the Files Manager"
 msgstr "Vyberte slo??ku, kter?? se zobraz?? jako /root/ ve Spr??vci soubor??"
 
-#: gui/options.cpp:2847
+#: gui/options.cpp:2856
 msgid "/root/ Path:"
 msgstr "Cesta /root/:"
 
-#: gui/options.cpp:2853
+#: gui/options.cpp:2862
 msgid "Server's port:"
 msgstr "Port serveru:"
 
-#: gui/options.cpp:2853
+#: gui/options.cpp:2862
 msgid "Port for server to use"
 msgstr "Port u????van?? serverem"
 
-#: gui/options.cpp:2858
+#: gui/options.cpp:2867
 msgctxt "lowres"
 msgid "Run server to manage files with browser (in the same network)."
 msgstr "Spustit server pro spr??vu soubor?? v prohl????e??i (ve stejn?? s??ti)."
 
-#: gui/options.cpp:2859
+#: gui/options.cpp:2868
 msgctxt "lowres"
 msgid "Closing options dialog will stop the server."
 msgstr "Zav??en?? okna mo??nost?? zastav?? server."
 
-#: gui/options.cpp:2861
+#: gui/options.cpp:2870
 msgid "Run server to manage files with browser (in the same network)."
 msgstr "Spustit server pro spr??vu soubor?? pomoc?? prohl????e??e (ve stejn?? s??ti)."
 
-#: gui/options.cpp:2862
+#: gui/options.cpp:2871
 msgid "Closing options dialog will stop the server."
 msgstr "Zav??en?? dialogu mo??nost?? zastav?? server."
 
-#: gui/options.cpp:2873
+#: gui/options.cpp:2882
 msgid "Use Text to speech"
 msgstr "Pou????t Text ke ??ten??"
 
-#: gui/options.cpp:2873
+#: gui/options.cpp:2882
 msgid "Will read text in gui on mouse over."
 msgstr "Bude ????st text GUI p??i najet?? my??i."
 
-#: gui/options.cpp:2954
+#: gui/options.cpp:2963
 msgid "Ignore"
 msgstr ""
 
-#: gui/options.cpp:2955
+#: gui/options.cpp:2964
 #, fuzzy
 msgid "Disable autosave"
 msgstr "Nelze ulo??it hru"
 
-#: gui/options.cpp:2956
+#: gui/options.cpp:2965
 msgid ""
 "WARNING: Autosave was enabled. Some of your games have existing saved games "
 "on the autosave slot. You can either move the existing saves to new slots, "
@@ -2646,14 +2658,14 @@ msgid ""
 "List of games:\n"
 msgstr ""
 
-#: gui/options.cpp:2965
+#: gui/options.cpp:2974
 #, fuzzy
 msgid ""
 "\n"
 "And more..."
 msgstr "P??idat Hru..."
 
-#: gui/options.cpp:2966 engines/engine.cpp:647
+#: gui/options.cpp:2975 engines/engine.cpp:647
 #: engines/access/metaengine.cpp:298 engines/drascula/metaengine.cpp:255
 #: engines/queen/metaengine.cpp:269 engines/sherlock/metaengine.cpp:410
 #: engines/ultima/nuvie/metaengine.cpp:71
@@ -2661,27 +2673,27 @@ msgstr "P??idat Hru..."
 msgid "Move"
 msgstr "Vzad"
 
-#: gui/options.cpp:2985
+#: gui/options.cpp:2994
 msgid "ERROR: Failed to move the following saved games:\n"
 msgstr ""
 
-#: gui/options.cpp:3081
+#: gui/options.cpp:3090
 msgid "Failed to change cloud storage!"
 msgstr "Nelze zm??nit cloudov?? ??lo??i??t??!"
 
-#: gui/options.cpp:3084
+#: gui/options.cpp:3093
 msgid "Another cloud storage is already active."
 msgstr "Jin?? cloudov?? ??lo??i??t?? ji?? je aktivn??."
 
-#: gui/options.cpp:3199
+#: gui/options.cpp:3212
 msgid "Theme does not support selected language!"
 msgstr "Vzhled nepodporuje zvolen?? jazyk!"
 
-#: gui/options.cpp:3202
+#: gui/options.cpp:3215
 msgid "Theme cannot be loaded!"
 msgstr "Vzhled nelze na????st!"
 
-#: gui/options.cpp:3205
+#: gui/options.cpp:3218
 msgid ""
 "\n"
 "Misc settings will be restored."
@@ -2689,48 +2701,48 @@ msgstr ""
 "\n"
 "Nastaven?? v R??zn?? budou obnovena."
 
-#: gui/options.cpp:3277
+#: gui/options.cpp:3290
 msgid "Select directory for GUI themes"
 msgstr "Vyberte adres???? pro vhledy GUI"
 
-#: gui/options.cpp:3287
+#: gui/options.cpp:3300
 #, fuzzy
 msgid "Select directory for GUI launcher thumbnails"
 msgstr "Vyberte adres???? pro vhledy GUI"
 
-#: gui/options.cpp:3298
+#: gui/options.cpp:3311
 #, fuzzy
 #| msgid "Select directory for plugins"
 msgid "Select directory for DLC downloads"
 msgstr "Vyberte adres???? pro z??suvn?? moduly"
 
-#: gui/options.cpp:3309
+#: gui/options.cpp:3322
 msgid "Select directory for extra files"
 msgstr "Vyberte adres???? pro dodate??n?? soubory"
 
-#: gui/options.cpp:3320
+#: gui/options.cpp:3333
 msgid "Select directory for plugins"
 msgstr "Vyberte adres???? pro z??suvn?? moduly"
 
-#: gui/options.cpp:3333
+#: gui/options.cpp:3346
 msgid "Select directory for Files Manager /root/"
 msgstr "Vyberte adres???? pro ko??en Spr??vce soubor??"
 
-#: gui/options.cpp:3485
+#: gui/options.cpp:3498
 msgid "This Storage is working now. Do you want to interrupt it?"
 msgstr "Toto ??lo??i??t?? pr??v?? pracuje. Chcete ho p??eru??it?"
 
-#: gui/options.cpp:3494
+#: gui/options.cpp:3507
 msgid "Wait until current Storage finishes up and try again."
 msgstr ""
 "Vy??kejte, ne?? operace se sou??asn??m ??lo??i??t??m jsou dokon??eny, a pak to zkuste "
 "znova."
 
-#: gui/options.cpp:3691 backends/cloud/cloudmanager.cpp:157
+#: gui/options.cpp:3704 backends/cloud/cloudmanager.cpp:157
 msgid "<right now>"
 msgstr "<pr??v?? nyn??>"
 
-#: gui/options.cpp:3834
+#: gui/options.cpp:3847
 msgid ""
 "Request failed.\n"
 "Check your Internet connection."
@@ -2738,13 +2750,13 @@ msgstr ""
 "????dost selhala.\n"
 "Zkontrolujte sv?? internetov?? p??ipojen??."
 
-#: gui/options.cpp:3857
+#: gui/options.cpp:3870
 msgid ""
 "A test pattern should be displayed.\n"
 "Do you want to keep these shader scaler settings?"
 msgstr ""
 
-#: gui/options.cpp:3860
+#: gui/options.cpp:3873
 #, c-format
 msgid "Reverting automatically in %d seconds"
 msgstr ""
@@ -2836,7 +2848,7 @@ msgstr "Nahr??t"
 msgid "Playback"
 msgstr "P??ehr??t"
 
-#: gui/recorderdialog.cpp:76 gui/widgets/grid.cpp:341
+#: gui/recorderdialog.cpp:76 gui/widgets/grid.cpp:343
 msgid "Edit"
 msgstr "Upravit"
 
@@ -3049,11 +3061,11 @@ msgstr ""
 msgid "Clear value"
 msgstr "Vymazat hodnotu"
 
-#: gui/widgets/grid.cpp:335
+#: gui/widgets/grid.cpp:337
 msgid "Play"
 msgstr "Hr??t"
 
-#: gui/widgets/grid.cpp:338
+#: gui/widgets/grid.cpp:340
 #, fuzzy
 msgid "Saves"
 msgstr "Ulo??it"
diff --git a/po/da.po b/po/da.po
index bb53ec1f7fa..cc202c52036 100644
--- a/po/da.po
+++ b/po/da.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-13 10:20+0000\n"
+"POT-Creation-Date: 2026-06-13 13:52+0000\n"
 "PO-Revision-Date: 2023-09-07 17:04+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Danish <https://translations.scummvm.org/projects/scummvm/"
@@ -123,7 +123,7 @@ msgstr "G?? op"
 #: gui/downloadpacksdialog.cpp:465 gui/editgamedialog.cpp:340
 #: gui/editrecorddialog.cpp:66 gui/integrity-dialog.cpp:122
 #: gui/integrity-dialog.cpp:253 gui/filebrowser-dialog.cpp:68
-#: gui/fluidsynth-dialog.cpp:153 gui/massadd.cpp:93 gui/options.cpp:2416
+#: gui/fluidsynth-dialog.cpp:153 gui/massadd.cpp:93 gui/options.cpp:2418
 #: gui/predictivedialog.cpp:68 gui/printing-dialog.cpp:96
 #: gui/recorderdialog.cpp:72 gui/recorderdialog.cpp:211
 #: gui/remotebrowser.cpp:59 gui/saveload-dialog.cpp:64
@@ -197,23 +197,23 @@ msgstr ""
 "Denne tilstand kr??ver at den lokale Webserver k??rer\n"
 "s?? din browser kan videresende data til ScummVM"
 
-#: gui/cloudconnectionwizard.cpp:257 gui/options.cpp:3776
+#: gui/cloudconnectionwizard.cpp:257 gui/options.cpp:3789
 msgid "Stop server"
 msgstr "Stop server"
 
-#: gui/cloudconnectionwizard.cpp:257 gui/options.cpp:2840
+#: gui/cloudconnectionwizard.cpp:257 gui/options.cpp:2849
 msgid "Run server"
 msgstr "K??r server"
 
-#: gui/cloudconnectionwizard.cpp:258 gui/options.cpp:3777
+#: gui/cloudconnectionwizard.cpp:258 gui/options.cpp:3790
 msgid "Stop local webserver"
 msgstr "Stop lokal webserver"
 
-#: gui/cloudconnectionwizard.cpp:258 gui/options.cpp:2840
+#: gui/cloudconnectionwizard.cpp:258 gui/options.cpp:2849
 msgid "Run local webserver"
 msgstr "K??r lokal webserver"
 
-#: gui/cloudconnectionwizard.cpp:266 gui/options.cpp:2841 gui/options.cpp:3784
+#: gui/cloudconnectionwizard.cpp:266 gui/options.cpp:2850 gui/options.cpp:3797
 msgid "Not running"
 msgstr "K??rer ikke"
 
@@ -349,8 +349,8 @@ msgstr "Et andet lager er aktivt nu. Vil du afbryde det?"
 #: gui/cloudconnectionwizard.cpp:530 gui/downloaddialog.cpp:118
 #: gui/downloaddialog.cpp:152 gui/filebrowser-dialog.cpp:135
 #: gui/fluidsynth-dialog.cpp:210 gui/launcher.cpp:428 gui/launcher.cpp:457
-#: gui/launcher.cpp:534 gui/launcher.cpp:1938 gui/options.cpp:3485
-#: gui/options.cpp:3859 base/main.cpp:608
+#: gui/launcher.cpp:534 gui/launcher.cpp:1970 gui/options.cpp:3498
+#: gui/options.cpp:3872 base/main.cpp:608
 #: backends/events/default/default-events.cpp:193 engines/buried/buried.cpp:598
 #: engines/chamber/metaengine.cpp:91 engines/director/events.cpp:122
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:139
@@ -369,8 +369,8 @@ msgstr "Ja"
 #: gui/cloudconnectionwizard.cpp:530 gui/downloaddialog.cpp:118
 #: gui/downloaddialog.cpp:153 gui/filebrowser-dialog.cpp:135
 #: gui/fluidsynth-dialog.cpp:210 gui/launcher.cpp:428 gui/launcher.cpp:457
-#: gui/launcher.cpp:534 gui/launcher.cpp:1939 gui/options.cpp:3485
-#: gui/options.cpp:3859 engines/buried/buried.cpp:598
+#: gui/launcher.cpp:534 gui/launcher.cpp:1971 gui/options.cpp:3498
+#: gui/options.cpp:3872 engines/buried/buried.cpp:598
 #: engines/chamber/metaengine.cpp:97 engines/director/events.cpp:122
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:139
 #: engines/efh/metaengine.cpp:368 engines/grim/grim.cpp:366
@@ -399,7 +399,7 @@ msgstr ""
 #: gui/editrecorddialog.cpp:67 gui/integrity-dialog.cpp:260
 #: gui/fluidsynth-dialog.cpp:154 gui/massadd.cpp:90 gui/message.cpp:156
 #: gui/message.cpp:160 gui/message.cpp:231 gui/message.cpp:270
-#: gui/message.cpp:274 gui/options.cpp:2418 gui/predictivedialog.cpp:69
+#: gui/message.cpp:274 gui/options.cpp:2420 gui/predictivedialog.cpp:69
 #: gui/saveload-dialog.cpp:1244 base/main.cpp:675 base/main.cpp:700
 #: dists/android.strings.xml.cpp:33 engines/engine.cpp:846
 #: backends/platform/wii/options.cpp:46 engines/buried/buried.cpp:622
@@ -600,11 +600,11 @@ msgstr ""
 msgid "Proceed"
 msgstr "Forts??t"
 
-#: gui/dump-all-dialogs.cpp:123 gui/options.cpp:3347
+#: gui/dump-all-dialogs.cpp:123 gui/options.cpp:3360
 msgid "icon packs"
 msgstr ""
 
-#: gui/dump-all-dialogs.cpp:127 gui/options.cpp:3358
+#: gui/dump-all-dialogs.cpp:127 gui/options.cpp:3371
 msgid "shader packs"
 msgstr ""
 
@@ -616,11 +616,11 @@ msgstr "V??lg spillet:"
 msgid "Game"
 msgstr "Spil"
 
-#: gui/editgamedialog.cpp:164 gui/options.cpp:2075 gui/options.cpp:2254
+#: gui/editgamedialog.cpp:164 gui/options.cpp:2075 gui/options.cpp:2256
 msgid "GFX"
 msgstr "GFX"
 
-#: gui/editgamedialog.cpp:164 gui/options.cpp:2075 gui/options.cpp:2254
+#: gui/editgamedialog.cpp:164 gui/options.cpp:2075 gui/options.cpp:2256
 #: backends/graphics/atari/atari-graphics.cpp:1056
 #: backends/graphics/sdl/sdl-graphics.cpp:487
 msgid "Graphics"
@@ -635,11 +635,11 @@ msgctxt "lowres"
 msgid "Override global graphic settings"
 msgstr "Tilsides??t globale grafikindstillinger"
 
-#: gui/editgamedialog.cpp:185 gui/options.cpp:2288 engines/dialogs.cpp:354
+#: gui/editgamedialog.cpp:185 gui/options.cpp:2290 engines/dialogs.cpp:354
 msgid "Keymaps"
 msgstr "Tasteoversigt"
 
-#: gui/editgamedialog.cpp:197 gui/options.cpp:2300 engines/dialogs.cpp:366
+#: gui/editgamedialog.cpp:197 gui/options.cpp:2302 engines/dialogs.cpp:366
 msgid "Backend"
 msgstr "Backend"
 
@@ -654,7 +654,7 @@ msgctxt "lowres"
 msgid "Override global backend settings"
 msgstr "Tilsides??t globale lydindstillinger"
 
-#: gui/editgamedialog.cpp:220 gui/options.cpp:2318 engines/dialogs.cpp:329
+#: gui/editgamedialog.cpp:220 gui/options.cpp:2320 engines/dialogs.cpp:329
 msgid "Audio"
 msgstr "Lyd"
 
@@ -667,11 +667,11 @@ msgctxt "lowres"
 msgid "Override global audio settings"
 msgstr "Tilsides??t globale lydindstillinger"
 
-#: gui/editgamedialog.cpp:234 gui/options.cpp:2323
+#: gui/editgamedialog.cpp:234 gui/options.cpp:2325
 msgid "Volume"
 msgstr "Lydstyrke"
 
-#: gui/editgamedialog.cpp:236 gui/options.cpp:2325
+#: gui/editgamedialog.cpp:236 gui/options.cpp:2327
 msgctxt "lowres"
 msgid "Volume"
 msgstr "Lydstyrke"
@@ -685,7 +685,7 @@ msgctxt "lowres"
 msgid "Override global volume settings"
 msgstr "Tilsides??t globale lydstyrkeindstillinger"
 
-#: gui/editgamedialog.cpp:252 gui/options.cpp:2333
+#: gui/editgamedialog.cpp:252 gui/options.cpp:2335
 msgid "MIDI"
 msgstr "MIDI"
 
@@ -698,7 +698,7 @@ msgctxt "lowres"
 msgid "Override global MIDI settings"
 msgstr "Tilsides??t globale MIDI-indstillinger"
 
-#: gui/editgamedialog.cpp:267 gui/options.cpp:2339
+#: gui/editgamedialog.cpp:267 gui/options.cpp:2341
 msgid "MT-32"
 msgstr "MT-32"
 
@@ -711,11 +711,11 @@ msgctxt "lowres"
 msgid "Override global MT-32 settings"
 msgstr "Tilsides??t globale MT-32 indstillinger"
 
-#: gui/editgamedialog.cpp:281 gui/options.cpp:2346
+#: gui/editgamedialog.cpp:281 gui/options.cpp:2348
 msgid "Paths"
 msgstr "Stier"
 
-#: gui/editgamedialog.cpp:283 gui/options.cpp:2348
+#: gui/editgamedialog.cpp:283 gui/options.cpp:2350
 msgctxt "lowres"
 msgid "Paths"
 msgstr "Stier"
@@ -737,7 +737,7 @@ msgctxt "lowres"
 msgid "Game Path:"
 msgstr "Spilsti:"
 
-#: gui/editgamedialog.cpp:303 gui/options.cpp:2555
+#: gui/editgamedialog.cpp:303 gui/options.cpp:2557
 msgid "Extra Path:"
 msgstr "Ekstrasti:"
 
@@ -746,33 +746,33 @@ msgstr "Ekstrasti:"
 msgid "Specifies path to additional data used by the game"
 msgstr "Angiver sti til ekstra data der bruges i spillet"
 
-#: gui/editgamedialog.cpp:305 gui/options.cpp:2557
+#: gui/editgamedialog.cpp:305 gui/options.cpp:2559
 msgctxt "lowres"
 msgid "Extra Path:"
 msgstr "Ekstrasti:"
 
-#: gui/editgamedialog.cpp:306 gui/options.cpp:2530 gui/options.cpp:2558
-#: gui/options.cpp:2567 gui/options.cpp:2848
+#: gui/editgamedialog.cpp:306 gui/options.cpp:2532 gui/options.cpp:2560
+#: gui/options.cpp:2569 gui/options.cpp:2857
 #: backends/platform/libretro/src/libretro-options-widget.cpp:277
 msgctxt "path"
 msgid "None"
 msgstr "Ingen"
 
-#: gui/editgamedialog.cpp:312 gui/options.cpp:2519
+#: gui/editgamedialog.cpp:312 gui/options.cpp:2521
 msgid "Save Path:"
 msgstr "Gemmesti:"
 
 #: gui/editgamedialog.cpp:312 gui/editgamedialog.cpp:314
-#: gui/editgamedialog.cpp:315 gui/options.cpp:2519 gui/options.cpp:2521
+#: gui/editgamedialog.cpp:315 gui/options.cpp:2521 gui/options.cpp:2523
 msgid "Specifies where your saved games are put"
 msgstr "Angiver hvor dine gemte spil bliver lagt"
 
-#: gui/editgamedialog.cpp:314 gui/options.cpp:2521
+#: gui/editgamedialog.cpp:314 gui/options.cpp:2523
 msgctxt "lowres"
 msgid "Save Path:"
 msgstr "Gemmesti:"
 
-#: gui/editgamedialog.cpp:315 gui/options.cpp:1844 gui/options.cpp:2522
+#: gui/editgamedialog.cpp:315 gui/options.cpp:1844 gui/options.cpp:2524
 #: backends/platform/wii/options.cpp:55
 msgid "Default"
 msgstr "Standard"
@@ -832,8 +832,8 @@ msgstr ""
 #: gui/editgamedialog.cpp:365 gui/editgamedialog.cpp:380 gui/options.cpp:1613
 #: gui/options.cpp:1627 gui/options.cpp:1644 gui/options.cpp:1658
 #: gui/options.cpp:1675 gui/options.cpp:1713 gui/options.cpp:1729
-#: gui/options.cpp:2144 gui/options.cpp:2600 gui/options.cpp:2674
-#: gui/options.cpp:2888 gui/options.cpp:3389 audio/null.cpp:45
+#: gui/options.cpp:2144 gui/options.cpp:2602 gui/options.cpp:2683
+#: gui/options.cpp:2897 gui/options.cpp:3402 audio/null.cpp:45
 #: backends/platform/android/options.cpp:157
 #: backends/platform/android/options.cpp:158
 #: backends/platform/android/options.cpp:186
@@ -860,7 +860,7 @@ msgctxt "lowres"
 msgid "Platform:"
 msgstr "Platform:"
 
-#: gui/editgamedialog.cpp:566 gui/options.cpp:3413
+#: gui/editgamedialog.cpp:566 gui/options.cpp:3426
 msgid "Select SoundFont"
 msgstr "V??lg SoundFont"
 
@@ -868,11 +868,11 @@ msgstr "V??lg SoundFont"
 msgid "Select additional game directory"
 msgstr "V??lg ekstra spilmappe"
 
-#: gui/editgamedialog.cpp:618 gui/options.cpp:3261
+#: gui/editgamedialog.cpp:618 gui/options.cpp:3274
 msgid "Select directory for saved games"
 msgstr "V??lg mappe til gemte spil"
 
-#: gui/editgamedialog.cpp:625 gui/options.cpp:3268
+#: gui/editgamedialog.cpp:625 gui/options.cpp:3281
 msgid "The chosen directory cannot be written to. Please select another one."
 msgstr "Der kan ikke skrives til den valgte mappe. V??lg venligst et andet."
 
@@ -1016,7 +1016,7 @@ msgstr "Sinus"
 msgid "Triangle"
 msgstr "Triangul??r"
 
-#: gui/fluidsynth-dialog.cpp:139 gui/options.cpp:2366
+#: gui/fluidsynth-dialog.cpp:139 gui/options.cpp:2368
 msgid "Misc"
 msgstr "Diverse"
 
@@ -1056,7 +1056,7 @@ msgstr ""
 "Vil du virkelig nulstille alle FluidSynth-indstillinger til deres standard "
 "v??rdier?"
 
-#: gui/gui-manager.cpp:159 gui/options.cpp:2357
+#: gui/gui-manager.cpp:159 gui/options.cpp:2359
 msgid "GUI"
 msgstr ""
 
@@ -1271,7 +1271,7 @@ msgid ""
 "and illegal game copies.\n"
 msgstr ""
 
-#: gui/helpdialog.cpp:68 gui/options.cpp:2377
+#: gui/helpdialog.cpp:68 gui/options.cpp:2379
 msgid "Cloud"
 msgstr "Skyen"
 
@@ -1575,92 +1575,92 @@ msgstr "Liste visning"
 msgid "Grid view"
 msgstr "Gitter visning"
 
-#: gui/launcher.cpp:1145
+#: gui/launcher.cpp:1168
 msgid "~S~tart"
 msgstr "~S~tart"
 
-#: gui/launcher.cpp:1145
+#: gui/launcher.cpp:1168
 msgid "Start selected game"
 msgstr "Start det valgte spil"
 
-#: gui/launcher.cpp:1148
+#: gui/launcher.cpp:1171
 msgid "~L~oad..."
 msgstr "Ind~l~??s ..."
 
-#: gui/launcher.cpp:1148
+#: gui/launcher.cpp:1171
 msgid "Load saved game for selected game"
 msgstr "Indl??s gemte spil for det valgte spil"
 
-#: gui/launcher.cpp:1150
+#: gui/launcher.cpp:1173
 msgid "Record..."
 msgstr "Optag ..."
 
-#: gui/launcher.cpp:1156
+#: gui/launcher.cpp:1179
 #, fuzzy
 msgid "~G~ame Options..."
 msgstr "~I~ndstillinger ..."
 
-#: gui/launcher.cpp:1156
+#: gui/launcher.cpp:1179
 msgid "Change game options"
 msgstr "??ndr spilindstillinger"
 
-#: gui/launcher.cpp:1156
+#: gui/launcher.cpp:1179
 #, fuzzy
 msgctxt "lowres"
 msgid "~G~ame Opts..."
 msgstr "~I~ndstillinger ..."
 
-#: gui/launcher.cpp:1242
+#: gui/launcher.cpp:1265
 msgid "Add-on"
 msgstr ""
 
 #. I18N: List grouping when no engine is specified
-#: gui/launcher.cpp:1309 gui/launcher.cpp:1554
+#: gui/launcher.cpp:1335 gui/launcher.cpp:1580
 #, fuzzy
 msgid "Unknown Engine"
 msgstr "Ukendt variant"
 
 #. I18N: List grouping when no publisher is specified
 #. I18N: List group when no publisher is specified
-#: gui/launcher.cpp:1326 gui/launcher.cpp:1571
+#: gui/launcher.cpp:1352 gui/launcher.cpp:1597
 #, fuzzy
 msgid "Unknown Publisher"
 msgstr "Ukendt forfatter"
 
 #. I18N: List group when no game series is specified
 #. I18N: List grouping when no game series is specified
-#: gui/launcher.cpp:1343 gui/launcher.cpp:1588
+#: gui/launcher.cpp:1369 gui/launcher.cpp:1614
 msgid "No Series"
 msgstr ""
 
 #. I18N: List group when no language is specified
-#: gui/launcher.cpp:1357 gui/launcher.cpp:1602
+#: gui/launcher.cpp:1383 gui/launcher.cpp:1628
 msgid "Language not detected"
 msgstr ""
 
 #. I18N: List group when no platform is specified
-#: gui/launcher.cpp:1371 gui/launcher.cpp:1616
+#: gui/launcher.cpp:1397 gui/launcher.cpp:1642
 msgid "Platform not detected"
 msgstr ""
 
 #. I18N: List group when no year is specified
-#: gui/launcher.cpp:1388 gui/launcher.cpp:1633
+#: gui/launcher.cpp:1414 gui/launcher.cpp:1659
 #, fuzzy
 #| msgid "Unknown error"
 msgid "Unknown Year"
 msgstr "Ukendt fejl"
 
 #. I18N: Group for All items
-#: gui/launcher.cpp:1640 gui/widgets/groupedlist.cpp:82
+#: gui/launcher.cpp:1666 gui/widgets/groupedlist.cpp:82
 #, fuzzy
 msgid "All"
 msgstr "Alt"
 
-#: gui/launcher.cpp:1812
+#: gui/launcher.cpp:1844
 msgid "Icons per row:"
 msgstr ""
 
-#: gui/launcher.cpp:1869
+#: gui/launcher.cpp:1901
 #, fuzzy, c-format
 #| msgid "Do you really want to remove this game configuration?"
 msgid ""
@@ -2162,189 +2162,201 @@ msgctxt "lowres"
 msgid "Speech volume:"
 msgstr "Lydstyrke (tale):"
 
-#: gui/options.cpp:2266
+#: gui/options.cpp:2268
 msgid "Control"
 msgstr "Styring"
 
-#: gui/options.cpp:2379
+#: gui/options.cpp:2381
 msgctxt "lowres"
 msgid "Cloud"
 msgstr "Skyen"
 
-#: gui/options.cpp:2394
+#: gui/options.cpp:2396
 msgid "LAN"
 msgstr "LAN"
 
-#: gui/options.cpp:2396
+#: gui/options.cpp:2398
 msgctxt "lowres"
 msgid "LAN"
 msgstr "LAN"
 
-#: gui/options.cpp:2405
+#: gui/options.cpp:2407
 msgid "Accessibility"
 msgstr "Tilg??ngelighed"
 
-#: gui/options.cpp:2407
+#: gui/options.cpp:2409
 msgctxt "lowres"
 msgid "Accessibility"
 msgstr "Tilg??ngelighed"
 
-#: gui/options.cpp:2416
+#: gui/options.cpp:2418
 msgid "Discard changes and close the dialog"
 msgstr ""
 
-#: gui/options.cpp:2417
+#: gui/options.cpp:2419
 msgid "Apply"
 msgstr "Anvend"
 
-#: gui/options.cpp:2417
+#: gui/options.cpp:2419
 msgid "Apply changes without closing the dialog"
 msgstr ""
 
-#: gui/options.cpp:2418
+#: gui/options.cpp:2420
 msgid "Apply changes and close the dialog"
 msgstr ""
 
-#: gui/options.cpp:2508
+#: gui/options.cpp:2510
 msgid "FluidSynth Settings"
 msgstr "FluidSynth-indstillinger"
 
-#: gui/options.cpp:2522
+#: gui/options.cpp:2524
 msgid ""
 "Specifies where your saved games are put. A red coloring indicates the value "
 "is temporary and will not get saved"
 msgstr ""
 
-#: gui/options.cpp:2527
+#: gui/options.cpp:2529
 msgid "Theme Path:"
 msgstr "Temasti:"
 
-#: gui/options.cpp:2529
+#: gui/options.cpp:2531
 msgctxt "lowres"
 msgid "Theme Path:"
 msgstr "Temasti:"
 
-#: gui/options.cpp:2535
+#: gui/options.cpp:2537
 #, fuzzy
 msgid "Icon Path:"
 msgstr "/root/-sti:"
 
-#: gui/options.cpp:2537
+#: gui/options.cpp:2539
 #, fuzzy
 msgctxt "lowres"
 msgid "Icon Path:"
 msgstr "/root/-sti:"
 
-#: gui/options.cpp:2538 gui/options.cpp:2548
+#: gui/options.cpp:2540 gui/options.cpp:2550
 #, fuzzy
 msgctxt "path"
 msgid "Default"
 msgstr "Standard"
 
-#: gui/options.cpp:2545
+#: gui/options.cpp:2547
 #, fuzzy
 #| msgid "Game Path:"
 msgid "DLC Path:"
 msgstr "Spilsti:"
 
-#: gui/options.cpp:2547
+#: gui/options.cpp:2549
 #, fuzzy
 #| msgid "Game Path:"
 msgctxt "lowres"
 msgid "DLC Path:"
 msgstr "Spilsti:"
 
-#: gui/options.cpp:2555 gui/options.cpp:2557 gui/options.cpp:2558
+#: gui/options.cpp:2557 gui/options.cpp:2559 gui/options.cpp:2560
 msgid "Specifies path to additional data used by all games or ScummVM"
 msgstr "Angiver sti til ekstra data brugt af alle spil eller ScummVM"
 
-#: gui/options.cpp:2564
+#: gui/options.cpp:2566
 msgid "Plugins Path:"
 msgstr "Plugin sti:"
 
-#: gui/options.cpp:2566
+#: gui/options.cpp:2568
 msgctxt "lowres"
 msgid "Plugins Path:"
 msgstr "Plugin sti:"
 
-#: gui/options.cpp:2576
+#: gui/options.cpp:2578
 msgid "ScummVM config path: "
 msgstr ""
 
-#: gui/options.cpp:2590
+#: gui/options.cpp:2592
 msgid "ScummVM log path: "
 msgstr ""
 
-#: gui/options.cpp:2595
+#: gui/options.cpp:2597
 msgid "View"
 msgstr ""
 
 #. I18N: Referring to the last path memorized when adding a game
-#: gui/options.cpp:2605 gui/options.cpp:3389
+#: gui/options.cpp:2607 gui/options.cpp:3402
 msgid "Last browser path: "
 msgstr ""
 
-#: gui/options.cpp:2610
+#: gui/options.cpp:2612
 msgid "Theme:"
 msgstr "Tema:"
 
-#: gui/options.cpp:2615
+#: gui/options.cpp:2617
 #, fuzzy
 msgid "GUI scale:"
 msgstr "Opskal??r videoer"
 
-#: gui/options.cpp:2632
+#: gui/options.cpp:2634
 #, c-format
 msgid "%d%% - Custom"
 msgstr ""
 
-#: gui/options.cpp:2636
+#: gui/options.cpp:2638
 msgid "GUI renderer:"
 msgstr "GUI-gengiver:"
 
-#: gui/options.cpp:2649
+#: gui/options.cpp:2651
 msgid "Return to the launcher when leaving a game"
 msgstr ""
 
-#: gui/options.cpp:2650
+#: gui/options.cpp:2652
 msgid ""
 "Return to the launcher when leaving a game instead of closing ScummVM\n"
 "(this feature is not supported by all games)."
 msgstr ""
 
-#: gui/options.cpp:2657
+#: gui/options.cpp:2659
 msgid "Ask for confirmation on exit"
 msgstr ""
 
-#: gui/options.cpp:2658
+#: gui/options.cpp:2660
 msgid "Ask for permission when closing ScummVM or leaving a game."
 msgstr ""
 
-#: gui/options.cpp:2664
+#: gui/options.cpp:2666
 #, fuzzy
 msgid "Disable fixed font scaling"
 msgstr "Kunne ikke gemme spil"
 
-#: gui/options.cpp:2665
+#: gui/options.cpp:2667
 msgid ""
 "Do not upscale fixed size fonts in the GUI. This reduces artefacts on low "
-"resolution screens"
+"resolution screens."
 msgstr ""
 
-#: gui/options.cpp:2671
+#: gui/options.cpp:2673
+#, fuzzy
+#| msgid "Enable smooth scrolling when walking"
+msgid "Enable kinetic scrolling in lists"
+msgstr "Aktiv??r j??vn bev??gelse n??r du g??r"
+
+#: gui/options.cpp:2674
+#, fuzzy
+#| msgid "Enable smooth scrolling when walking"
+msgid "Enable smooth, momentum-based scrolling in list widgets."
+msgstr "Aktiv??r j??vn bev??gelse n??r du g??r"
+
+#: gui/options.cpp:2680
 msgid "GUI language:"
 msgstr "GUI-sprog:"
 
-#: gui/options.cpp:2671
+#: gui/options.cpp:2680
 msgid "Language of ScummVM GUI"
 msgstr "Sprog for ScummVM-brugerflade"
 
-#: gui/options.cpp:2697
+#: gui/options.cpp:2706
 #, fuzzy
 msgid "Switch the ScummVM GUI language to the game language"
 msgstr "Skift GUI-sproget til spilsproget"
 
-#: gui/options.cpp:2698
+#: gui/options.cpp:2707
 #, fuzzy
 msgid ""
 "When starting a game, change the ScummVM GUI language to the game language. "
@@ -2355,11 +2367,11 @@ msgstr ""
 "bruger ScummVMs gem- og indl??se-dialoger, s?? er de p?? det samme sprog som "
 "spillet."
 
-#: gui/options.cpp:2710
+#: gui/options.cpp:2719
 msgid "Use native system file browser"
 msgstr "Brug systemets egen filbrowser"
 
-#: gui/options.cpp:2711
+#: gui/options.cpp:2720
 msgid ""
 "Use the native system file browser instead of the ScummVM one to select a "
 "file or directory."
@@ -2367,81 +2379,81 @@ msgstr ""
 "Brug systemets egen filbrowser i stedet for ScummVMs til at v??lge en fil "
 "eller mappe."
 
-#: gui/options.cpp:2718
+#: gui/options.cpp:2727
 #, fuzzy
 msgid "Download Icons"
 msgstr "Download"
 
-#: gui/options.cpp:2718
+#: gui/options.cpp:2727
 #, fuzzy
 msgid "Check on the scummvm.org website for updates of icon packs"
 msgstr "S??g efter opdateringer ..."
 
-#: gui/options.cpp:2724
+#: gui/options.cpp:2733
 msgid "Autosave:"
 msgstr "Autogem:"
 
-#: gui/options.cpp:2726
+#: gui/options.cpp:2735
 msgctxt "lowres"
 msgid "Autosave:"
 msgstr "Autogem:"
 
-#: gui/options.cpp:2737
+#: gui/options.cpp:2746
 msgid "Random seed:"
 msgstr ""
 
-#: gui/options.cpp:2737
+#: gui/options.cpp:2746
 msgid "Seed for initializing all random number generators"
 msgstr ""
 
-#: gui/options.cpp:2745
+#: gui/options.cpp:2754
 #, fuzzy
 msgid "Debug level:"
 msgstr "Fejls??ger"
 
 #. I18N: Debug level -1, no messages
-#: gui/options.cpp:2749 gui/options.cpp:2886 audio/fmopl.cpp:83
+#: gui/options.cpp:2758 gui/options.cpp:2895 audio/fmopl.cpp:83
 #, fuzzy
 msgid "None"
 msgstr "Ingen"
 
 #. I18N: Debug level 11, all messages
-#: gui/options.cpp:2755
+#: gui/options.cpp:2764
 msgid "11 (all)"
 msgstr ""
 
-#: gui/options.cpp:2759
+#: gui/options.cpp:2768
 #, fuzzy
 msgid "Enable Discord integration"
 msgstr "Aktiv??r indholdscensurering"
 
-#: gui/options.cpp:2760
+#: gui/options.cpp:2769
 msgid ""
 "Show information about the games you are playing on Discord if the Discord "
 "client is running."
 msgstr ""
 
-#: gui/options.cpp:2770 gui/updates-dialog.cpp:87
+#: gui/options.cpp:2779 gui/updates-dialog.cpp:87
 msgid "Update check:"
 msgstr "Opdateringss??gning:"
 
-#: gui/options.cpp:2770
+#: gui/options.cpp:2779
 msgid "How often to check ScummVM updates"
 msgstr "Hvor ofte skal der s??ges efter opdateringer til ScummVM"
 
-#: gui/options.cpp:2781
+#: gui/options.cpp:2790
 msgid "Check now"
 msgstr "S??g nu"
 
-#: gui/options.cpp:2788
+#: gui/options.cpp:2797
 msgid "Active storage:"
 msgstr "Aktivt lager:"
 
-#: gui/options.cpp:2788
+#: gui/options.cpp:2797
 msgid "Active cloud storage"
 msgstr "Aktiv??r lagring i skyen"
 
-#: gui/options.cpp:2797
+#: gui/options.cpp:2806
 #, fuzzy
 #| msgctxt "lowres"
 #| msgid ""
@@ -2454,7 +2466,7 @@ msgstr ""
 "4. Lageret er endnu ikke aktiveret. Bekr??ft at brugernavnet er korrekt og "
 "aktiv??r det:"
 
-#: gui/options.cpp:2799
+#: gui/options.cpp:2808
 #, fuzzy
 #| msgid ""
 #| "4. Storage is not yet enabled. Verify that username is correct and enable "
@@ -2465,104 +2477,104 @@ msgstr ""
 "4. Lageret er endnu ikke aktiveret. Bekr??ft at brugernavnet er korrekt og "
 "aktiv??r det:"
 
-#: gui/options.cpp:2800
+#: gui/options.cpp:2809
 msgid "Enable storage"
 msgstr "Aktiv??r lager"
 
-#: gui/options.cpp:2800
+#: gui/options.cpp:2809
 msgid "Confirm you want to use this account for this storage"
 msgstr "Bekr??ft at du vil bruge kontoen til dette lager"
 
-#: gui/options.cpp:2802 backends/platform/wii/options.cpp:113
+#: gui/options.cpp:2811 backends/platform/wii/options.cpp:113
 msgid "Username:"
 msgstr "Bruger:"
 
-#: gui/options.cpp:2802
+#: gui/options.cpp:2811
 msgid "Username used by this storage"
 msgstr "Brugernavn for lagringen"
 
-#: gui/options.cpp:2803
+#: gui/options.cpp:2812
 msgid "<none>"
 msgstr "<ingen>"
 
-#: gui/options.cpp:2805
+#: gui/options.cpp:2814
 msgid "Used space:"
 msgstr "Udnyttet plads:"
 
-#: gui/options.cpp:2805
+#: gui/options.cpp:2814
 msgid "Space used by ScummVM's saved games on this storage"
 msgstr "Plads brugt af ScummVM's gemte spil p?? lagringen"
 
-#: gui/options.cpp:2808
+#: gui/options.cpp:2817
 msgid "Last sync:"
 msgstr "Sidste synkronisering:"
 
-#: gui/options.cpp:2808
+#: gui/options.cpp:2817
 msgid "When was the last time saved games were synced with this storage"
 msgstr "Sidste gang gemte spil blev synkroniseret med dette lager"
 
-#: gui/options.cpp:2809 gui/options.cpp:3693
+#: gui/options.cpp:2818 gui/options.cpp:3706
 msgid "<never>"
 msgstr "<aldrig>"
 
-#: gui/options.cpp:2811
+#: gui/options.cpp:2820
 msgctxt "lowres"
 msgid "Saved games sync automatically on launch, after saving and on loading."
 msgstr ""
 "Gemte spil synkroniseres automatisk ved start, efter gemning og ved "
 "indl??sning."
 
-#: gui/options.cpp:2813
+#: gui/options.cpp:2822
 msgid "Saved games sync automatically on launch, after saving and on loading."
 msgstr ""
 "Gemte spil synkroniseres automatisk ved start, efter gemning og ved "
 "indl??sning."
 
-#: gui/options.cpp:2814
+#: gui/options.cpp:2823
 msgid "Sync now"
 msgstr "Synkroniser nu"
 
-#: gui/options.cpp:2814
+#: gui/options.cpp:2823
 msgid "Start saved games sync"
 msgstr "Start synkronisering af gemte spil"
 
-#: gui/options.cpp:2817
+#: gui/options.cpp:2826
 msgctxt "lowres"
 msgid "You can download game files from your cloud ScummVM folder:"
 msgstr "Du kan downloade spilfiler fra din ScummVM-mappe i skyen:"
 
-#: gui/options.cpp:2819
+#: gui/options.cpp:2828
 msgid "You can download game files from your cloud ScummVM folder:"
 msgstr "Du kan downloade spilfiler fra din ScummVM-mappe i skyen:"
 
-#: gui/options.cpp:2820
+#: gui/options.cpp:2829
 msgid "Download game files"
 msgstr "Download spilfiler"
 
-#: gui/options.cpp:2820
+#: gui/options.cpp:2829
 msgid "Open downloads manager dialog"
 msgstr "??bn downloadmanager-dialog"
 
-#: gui/options.cpp:2823
+#: gui/options.cpp:2832
 msgctxt "lowres"
 msgid "To change account for this storage, disconnect and connect again:"
 msgstr ""
 "Skift konto for dette lager ved at afbryde og oprette forbindelse igen:"
 
-#: gui/options.cpp:2825
+#: gui/options.cpp:2834
 msgid "To change account for this storage, disconnect and connect again:"
 msgstr ""
 "Skift konto for dette lager ved at afbryde og oprette forbindelse igen:"
 
-#: gui/options.cpp:2826
+#: gui/options.cpp:2835
 msgid "Disconnect"
 msgstr "Afbryd forbindelsen"
 
-#: gui/options.cpp:2826
+#: gui/options.cpp:2835
 msgid "Stop using this storage on this device"
 msgstr "Stop med at bruge lageret p?? denne enhed"
 
-#: gui/options.cpp:2829
+#: gui/options.cpp:2838
 #, fuzzy
 #| msgctxt "lowres"
 #| msgid "This storage is not connected yet! To connect,"
@@ -2570,78 +2582,78 @@ msgctxt "lowres"
 msgid "This storage is not connected yet!"
 msgstr "Lageret er endnu ikke tilsluttet! Tilslut ved at,"
 
-#: gui/options.cpp:2831
+#: gui/options.cpp:2840
 #, fuzzy
 #| msgid "This storage is not connected yet! To connect,"
 msgid "This storage is not connected yet!"
 msgstr "Lageret er endnu ikke tilsluttet! Tilslut ved at,"
 
-#: gui/options.cpp:2832
+#: gui/options.cpp:2841
 #, fuzzy
 #| msgid "3. Connect"
 msgid "Connect"
 msgstr "3. Tilslut"
 
-#: gui/options.cpp:2832
+#: gui/options.cpp:2841
 msgid "Connect your cloud storage account"
 msgstr "Forbind din lagringskonto i skyen"
 
-#: gui/options.cpp:2845
+#: gui/options.cpp:2854
 msgctxt "lowres"
 msgid "/root/ Path:"
 msgstr "/root/-sti:"
 
-#: gui/options.cpp:2845 gui/options.cpp:2847 gui/options.cpp:2848
+#: gui/options.cpp:2854 gui/options.cpp:2856 gui/options.cpp:2857
 msgid "Select which directory will be shown as /root/ in the Files Manager"
 msgstr "V??lg hvilken mappe som skal vises som /root/ i filh??ndteringen"
 
-#: gui/options.cpp:2847
+#: gui/options.cpp:2856
 msgid "/root/ Path:"
 msgstr "/root/-sti:"
 
-#: gui/options.cpp:2853
+#: gui/options.cpp:2862
 msgid "Server's port:"
 msgstr "Serverens port:"
 
-#: gui/options.cpp:2853
+#: gui/options.cpp:2862
 msgid "Port for server to use"
 msgstr "Port som serveren skal bruge"
 
-#: gui/options.cpp:2858
+#: gui/options.cpp:2867
 msgctxt "lowres"
 msgid "Run server to manage files with browser (in the same network)."
 msgstr "K??r server for at h??ndtere filer med browser (i det samme netv??rk)."
 
-#: gui/options.cpp:2859
+#: gui/options.cpp:2868
 msgctxt "lowres"
 msgid "Closing options dialog will stop the server."
 msgstr "Serveren stopper hvis indstillingsdialogen lukkes."
 
-#: gui/options.cpp:2861
+#: gui/options.cpp:2870
 msgid "Run server to manage files with browser (in the same network)."
 msgstr "K??r server for at h??ndtere filer med browser (i det samme netv??rk)."
 
-#: gui/options.cpp:2862
+#: gui/options.cpp:2871
 msgid "Closing options dialog will stop the server."
 msgstr "Serveren stopper hvis indstillingsdialogen lukkes."
 
-#: gui/options.cpp:2873
+#: gui/options.cpp:2882
 msgid "Use Text to speech"
 msgstr "Brug tekst til tale"
 
-#: gui/options.cpp:2873
+#: gui/options.cpp:2882
 msgid "Will read text in gui on mouse over."
 msgstr "L??ser teksten i brugerfladen n??r musemark??ren holdes over."
 
-#: gui/options.cpp:2954
+#: gui/options.cpp:2963
 msgid "Ignore"
 msgstr "Ignorere"
 
-#: gui/options.cpp:2955
+#: gui/options.cpp:2964
 msgid "Disable autosave"
 msgstr "Sl?? autogem fra"
 
-#: gui/options.cpp:2956
+#: gui/options.cpp:2965
 msgid ""
 "WARNING: Autosave was enabled. Some of your games have existing saved games "
 "on the autosave slot. You can either move the existing saves to new slots, "
@@ -2655,7 +2667,7 @@ msgstr ""
 "ved at overskrive dit gemte spil).\n"
 "Liste med spil:\n"
 
-#: gui/options.cpp:2965
+#: gui/options.cpp:2974
 msgid ""
 "\n"
 "And more..."
@@ -2663,34 +2675,34 @@ msgstr ""
 "\n"
 "Og mere..."
 
-#: gui/options.cpp:2966 engines/engine.cpp:647
+#: gui/options.cpp:2975 engines/engine.cpp:647
 #: engines/access/metaengine.cpp:298 engines/drascula/metaengine.cpp:255
 #: engines/queen/metaengine.cpp:269 engines/sherlock/metaengine.cpp:410
 #: engines/ultima/nuvie/metaengine.cpp:71
 msgid "Move"
 msgstr "Flyt"
 
-#: gui/options.cpp:2985
+#: gui/options.cpp:2994
 msgid "ERROR: Failed to move the following saved games:\n"
 msgstr "FEJL: Kunne ikke flytte f??lgende gemte spil:\n"
 
-#: gui/options.cpp:3081
+#: gui/options.cpp:3090
 msgid "Failed to change cloud storage!"
 msgstr "Kunne ikke ??ndre lagring i sky!"
 
-#: gui/options.cpp:3084
+#: gui/options.cpp:3093
 msgid "Another cloud storage is already active."
 msgstr "En anden lagring i skyen er allerede aktiv."
 
-#: gui/options.cpp:3199
+#: gui/options.cpp:3212
 msgid "Theme does not support selected language!"
 msgstr "Tema underst??tter ikke valgte sprog!"
 
-#: gui/options.cpp:3202
+#: gui/options.cpp:3215
 msgid "Theme cannot be loaded!"
 msgstr "Tema kunne ikke indl??ses!"
 
-#: gui/options.cpp:3205
+#: gui/options.cpp:3218
 msgid ""
 "\n"
 "Misc settings will be restored."
@@ -2698,46 +2710,46 @@ msgstr ""
 "\n"
 "Diverse indstillinger vil blive genskabt."
 
-#: gui/options.cpp:3277
+#: gui/options.cpp:3290
 msgid "Select directory for GUI themes"
 msgstr "V??lg mappe for GUI-temaer"
 
-#: gui/options.cpp:3287
+#: gui/options.cpp:3300
 #, fuzzy
 msgid "Select directory for GUI launcher thumbnails"
 msgstr "V??lg mappe for GUI-temaer"
 
-#: gui/options.cpp:3298
+#: gui/options.cpp:3311
 #, fuzzy
 #| msgid "Select directory for plugins"
 msgid "Select directory for DLC downloads"
 msgstr "V??lg mappe for plugins"
 
-#: gui/options.cpp:3309
+#: gui/options.cpp:3322
 msgid "Select directory for extra files"
 msgstr "V??lg mappe for ekstra filer"
 
-#: gui/options.cpp:3320
+#: gui/options.cpp:3333
 msgid "Select directory for plugins"
 msgstr "V??lg mappe for plugins"
 
-#: gui/options.cpp:3333
+#: gui/options.cpp:3346
 msgid "Select directory for Files Manager /root/"
 msgstr "V??lg mappe for filh??ndteringen /root/"
 
-#: gui/options.cpp:3485
+#: gui/options.cpp:3498
 msgid "This Storage is working now. Do you want to interrupt it?"
 msgstr "Lageret arbejder nu. Vil du afbryde det?"
 
-#: gui/options.cpp:3494
+#: gui/options.cpp:3507
 msgid "Wait until current Storage finishes up and try again."
 msgstr "Vent indtil aktuel lagring er f??rdig og pr??v igen."
 
-#: gui/options.cpp:3691 backends/cloud/cloudmanager.cpp:157
+#: gui/options.cpp:3704 backends/cloud/cloudmanager.cpp:157
 msgid "<right now>"
 msgstr "<lige nu>"
 
-#: gui/options.cpp:3834
+#: gui/options.cpp:3847
 msgid ""
 "Request failed.\n"
 "Check your Internet connection."
@@ -2745,13 +2757,13 @@ msgstr ""
 "Anmodning mislykkedes.\n"
 "Tjek din internetforbindelse."
 
-#: gui/options.cpp:3857
+#: gui/options.cpp:3870
 msgid ""
 "A test pattern should be displayed.\n"
 "Do you want to keep these shader scaler settings?"
 msgstr ""
 
-#: gui/options.cpp:3860
+#: gui/options.cpp:3873
 #, c-format
 msgid "Reverting automatically in %d seconds"
 msgstr ""
@@ -2842,7 +2854,7 @@ msgstr "Optag"
 msgid "Playback"
 msgstr "Afspil"
 
-#: gui/recorderdialog.cpp:76 gui/widgets/grid.cpp:341
+#: gui/recorderdialog.cpp:76 gui/widgets/grid.cpp:343
 msgid "Edit"
 msgstr "Ret"
 
@@ -3060,11 +3072,11 @@ msgstr ""
 msgid "Clear value"
 msgstr "Slet v??rdi"
 
-#: gui/widgets/grid.cpp:335
+#: gui/widgets/grid.cpp:337
 msgid "Play"
 msgstr "Spil"
 
-#: gui/widgets/grid.cpp:338
+#: gui/widgets/grid.cpp:340
 #, fuzzy
 msgid "Saves"
 msgstr "Gem"
diff --git a/po/de_DE.po b/po/de_DE.po
index 005c24faf7a..0255134aa89 100644
--- a/po/de_DE.po
+++ b/po/de_DE.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.10.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-13 10:20+0000\n"
+"POT-Creation-Date: 2026-06-13 13:52+0000\n"
 "PO-Revision-Date: 2026-05-05 16:31+0000\n"
 "Last-Translator: Lothar Serra Mari <lothar.serramari at scummvm.org>\n"
 "Language-Team: German <https://translations.scummvm.org/projects/scummvm/"
@@ -117,7 +117,7 @@ msgstr "Pfad hoch"
 #: gui/downloadpacksdialog.cpp:465 gui/editgamedialog.cpp:340
 #: gui/editrecorddialog.cpp:66 gui/integrity-dialog.cpp:122
 #: gui/integrity-dialog.cpp:253 gui/filebrowser-dialog.cpp:68
-#: gui/fluidsynth-dialog.cpp:153 gui/massadd.cpp:93 gui/options.cpp:2416
+#: gui/fluidsynth-dialog.cpp:153 gui/massadd.cpp:93 gui/options.cpp:2418
 #: gui/predictivedialog.cpp:68 gui/printing-dialog.cpp:96
 #: gui/recorderdialog.cpp:72 gui/recorderdialog.cpp:211
 #: gui/remotebrowser.cpp:59 gui/saveload-dialog.cpp:64
@@ -188,23 +188,23 @@ msgstr ""
 "In diesem Modus muss der lokale Webserver laufen,\n"
 "damit dein Browser die Daten zu ScummVM weiterleiten kann"
 
-#: gui/cloudconnectionwizard.cpp:257 gui/options.cpp:3776
+#: gui/cloudconnectionwizard.cpp:257 gui/options.cpp:3789
 msgid "Stop server"
 msgstr "Server anhalten"
 
-#: gui/cloudconnectionwizard.cpp:257 gui/options.cpp:2840
+#: gui/cloudconnectionwizard.cpp:257 gui/options.cpp:2849
 msgid "Run server"
 msgstr "Server starten"
 
-#: gui/cloudconnectionwizard.cpp:258 gui/options.cpp:3777
+#: gui/cloudconnectionwizard.cpp:258 gui/options.cpp:3790
 msgid "Stop local webserver"
 msgstr "Lokalen Webserver anhalten"
 
-#: gui/cloudconnectionwizard.cpp:258 gui/options.cpp:2840
+#: gui/cloudconnectionwizard.cpp:258 gui/options.cpp:2849
 msgid "Run local webserver"
 msgstr "Startet den lokalen Webserver"
 
-#: gui/cloudconnectionwizard.cpp:266 gui/options.cpp:2841 gui/options.cpp:3784
+#: gui/cloudconnectionwizard.cpp:266 gui/options.cpp:2850 gui/options.cpp:3797
 msgid "Not running"
 msgstr "Nicht gestartet"
 
@@ -334,8 +334,8 @@ msgstr ""
 #: gui/cloudconnectionwizard.cpp:530 gui/downloaddialog.cpp:118
 #: gui/downloaddialog.cpp:152 gui/filebrowser-dialog.cpp:135
 #: gui/fluidsynth-dialog.cpp:210 gui/launcher.cpp:428 gui/launcher.cpp:457
-#: gui/launcher.cpp:534 gui/launcher.cpp:1938 gui/options.cpp:3485
-#: gui/options.cpp:3859 base/main.cpp:608
+#: gui/launcher.cpp:534 gui/launcher.cpp:1970 gui/options.cpp:3498
+#: gui/options.cpp:3872 base/main.cpp:608
 #: backends/events/default/default-events.cpp:193 engines/buried/buried.cpp:598
 #: engines/chamber/metaengine.cpp:91 engines/director/events.cpp:122
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:139
@@ -354,8 +354,8 @@ msgstr "Ja"
 #: gui/cloudconnectionwizard.cpp:530 gui/downloaddialog.cpp:118
 #: gui/downloaddialog.cpp:153 gui/filebrowser-dialog.cpp:135
 #: gui/fluidsynth-dialog.cpp:210 gui/launcher.cpp:428 gui/launcher.cpp:457
-#: gui/launcher.cpp:534 gui/launcher.cpp:1939 gui/options.cpp:3485
-#: gui/options.cpp:3859 engines/buried/buried.cpp:598
+#: gui/launcher.cpp:534 gui/launcher.cpp:1971 gui/options.cpp:3498
+#: gui/options.cpp:3872 engines/buried/buried.cpp:598
 #: engines/chamber/metaengine.cpp:97 engines/director/events.cpp:122
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:139
 #: engines/efh/metaengine.cpp:368 engines/grim/grim.cpp:366
@@ -382,7 +382,7 @@ msgstr "JSON-Code ist fehlerhaft."
 #: gui/editrecorddialog.cpp:67 gui/integrity-dialog.cpp:260
 #: gui/fluidsynth-dialog.cpp:154 gui/massadd.cpp:90 gui/message.cpp:156
 #: gui/message.cpp:160 gui/message.cpp:231 gui/message.cpp:270
-#: gui/message.cpp:274 gui/options.cpp:2418 gui/predictivedialog.cpp:69
+#: gui/message.cpp:274 gui/options.cpp:2420 gui/predictivedialog.cpp:69
 #: gui/saveload-dialog.cpp:1244 base/main.cpp:675 base/main.cpp:700
 #: dists/android.strings.xml.cpp:33 engines/engine.cpp:846
 #: backends/platform/wii/options.cpp:46 engines/buried/buried.cpp:622
@@ -575,11 +575,11 @@ msgstr ""
 msgid "Proceed"
 msgstr "Fortfahren"
 
-#: gui/dump-all-dialogs.cpp:123 gui/options.cpp:3347
+#: gui/dump-all-dialogs.cpp:123 gui/options.cpp:3360
 msgid "icon packs"
 msgstr "Icon-Packs"
 
-#: gui/dump-all-dialogs.cpp:127 gui/options.cpp:3358
+#: gui/dump-all-dialogs.cpp:127 gui/options.cpp:3371
 msgid "shader packs"
 msgstr "Shader-Packs"
 
@@ -591,11 +591,11 @@ msgstr "Spiel ausw??hlen:"
 msgid "Game"
 msgstr "Spiel"
 
-#: gui/editgamedialog.cpp:164 gui/options.cpp:2075 gui/options.cpp:2254
+#: gui/editgamedialog.cpp:164 gui/options.cpp:2075 gui/options.cpp:2256
 msgid "GFX"
 msgstr "GFX"
 
-#: gui/editgamedialog.cpp:164 gui/options.cpp:2075 gui/options.cpp:2254
+#: gui/editgamedialog.cpp:164 gui/options.cpp:2075 gui/options.cpp:2256
 #: backends/graphics/atari/atari-graphics.cpp:1056
 #: backends/graphics/sdl/sdl-graphics.cpp:487
 msgid "Graphics"
@@ -610,11 +610,11 @@ msgctxt "lowres"
 msgid "Override global graphic settings"
 msgstr "Globale Grafik-Einstellungen ??bergehen"
 
-#: gui/editgamedialog.cpp:185 gui/options.cpp:2288 engines/dialogs.cpp:354
+#: gui/editgamedialog.cpp:185 gui/options.cpp:2290 engines/dialogs.cpp:354
 msgid "Keymaps"
 msgstr "Tastaturbelegung"
 
-#: gui/editgamedialog.cpp:197 gui/options.cpp:2300 engines/dialogs.cpp:366
+#: gui/editgamedialog.cpp:197 gui/options.cpp:2302 engines/dialogs.cpp:366
 msgid "Backend"
 msgstr "Backend"
 
@@ -627,7 +627,7 @@ msgctxt "lowres"
 msgid "Override global backend settings"
 msgstr "Globale Backend-Einstellungen ??bergehen"
 
-#: gui/editgamedialog.cpp:220 gui/options.cpp:2318 engines/dialogs.cpp:329
+#: gui/editgamedialog.cpp:220 gui/options.cpp:2320 engines/dialogs.cpp:329
 msgid "Audio"
 msgstr "Audio"
 
@@ -640,11 +640,11 @@ msgctxt "lowres"
 msgid "Override global audio settings"
 msgstr "Globale Audio-Einstellungen ??bergehen"
 
-#: gui/editgamedialog.cpp:234 gui/options.cpp:2323
+#: gui/editgamedialog.cpp:234 gui/options.cpp:2325
 msgid "Volume"
 msgstr "Lautst??rke"
 
-#: gui/editgamedialog.cpp:236 gui/options.cpp:2325
+#: gui/editgamedialog.cpp:236 gui/options.cpp:2327
 msgctxt "lowres"
 msgid "Volume"
 msgstr "Lautst??rke"
@@ -658,7 +658,7 @@ msgctxt "lowres"
 msgid "Override global volume settings"
 msgstr "Globale Lautst??rke-Einstellungen ??bergehen"
 
-#: gui/editgamedialog.cpp:252 gui/options.cpp:2333
+#: gui/editgamedialog.cpp:252 gui/options.cpp:2335
 msgid "MIDI"
 msgstr "MIDI"
 
@@ -671,7 +671,7 @@ msgctxt "lowres"
 msgid "Override global MIDI settings"
 msgstr "Globale MIDI-Einstellungen ??bergehen"
 
-#: gui/editgamedialog.cpp:267 gui/options.cpp:2339
+#: gui/editgamedialog.cpp:267 gui/options.cpp:2341
 msgid "MT-32"
 msgstr "MT-32"
 
@@ -684,11 +684,11 @@ msgctxt "lowres"
 msgid "Override global MT-32 settings"
 msgstr "Globale MT-32-Einstellungen ??bergehen"
 
-#: gui/editgamedialog.cpp:281 gui/options.cpp:2346
+#: gui/editgamedialog.cpp:281 gui/options.cpp:2348
 msgid "Paths"
 msgstr "Pfade"
 
-#: gui/editgamedialog.cpp:283 gui/options.cpp:2348
+#: gui/editgamedialog.cpp:283 gui/options.cpp:2350
 msgctxt "lowres"
 msgid "Paths"
 msgstr "Pfade"
@@ -710,7 +710,7 @@ msgctxt "lowres"
 msgid "Game Path:"
 msgstr "Spielpfad:"
 
-#: gui/editgamedialog.cpp:303 gui/options.cpp:2555
+#: gui/editgamedialog.cpp:303 gui/options.cpp:2557
 msgid "Extra Path:"
 msgstr "Extras:"
 
@@ -719,33 +719,33 @@ msgstr "Extras:"
 msgid "Specifies path to additional data used by the game"
 msgstr "Legt das Verzeichnis f??r zus??tzliche Spieldateien fest"
 
-#: gui/editgamedialog.cpp:305 gui/options.cpp:2557
+#: gui/editgamedialog.cpp:305 gui/options.cpp:2559
 msgctxt "lowres"
 msgid "Extra Path:"
 msgstr "Extras:"
 
-#: gui/editgamedialog.cpp:306 gui/options.cpp:2530 gui/options.cpp:2558
-#: gui/options.cpp:2567 gui/options.cpp:2848
+#: gui/editgamedialog.cpp:306 gui/options.cpp:2532 gui/options.cpp:2560
+#: gui/options.cpp:2569 gui/options.cpp:2857
 #: backends/platform/libretro/src/libretro-options-widget.cpp:277
 msgctxt "path"
 msgid "None"
 msgstr "Standard"
 
-#: gui/editgamedialog.cpp:312 gui/options.cpp:2519
+#: gui/editgamedialog.cpp:312 gui/options.cpp:2521
 msgid "Save Path:"
 msgstr "Spielst??nde:"
 
 #: gui/editgamedialog.cpp:312 gui/editgamedialog.cpp:314
-#: gui/editgamedialog.cpp:315 gui/options.cpp:2519 gui/options.cpp:2521
+#: gui/editgamedialog.cpp:315 gui/options.cpp:2521 gui/options.cpp:2523
 msgid "Specifies where your saved games are put"
 msgstr "Legt fest, wo die Spielst??nde gespeichert werden"
 
-#: gui/editgamedialog.cpp:314 gui/options.cpp:2521
+#: gui/editgamedialog.cpp:314 gui/options.cpp:2523
 msgctxt "lowres"
 msgid "Save Path:"
 msgstr "Spielst??nde:"
 
-#: gui/editgamedialog.cpp:315 gui/options.cpp:1844 gui/options.cpp:2522
+#: gui/editgamedialog.cpp:315 gui/options.cpp:1844 gui/options.cpp:2524
 #: backends/platform/wii/options.cpp:55
 msgid "Default"
 msgstr "Standard"
@@ -806,8 +806,8 @@ msgstr ""
 #: gui/editgamedialog.cpp:365 gui/editgamedialog.cpp:380 gui/options.cpp:1613
 #: gui/options.cpp:1627 gui/options.cpp:1644 gui/options.cpp:1658
 #: gui/options.cpp:1675 gui/options.cpp:1713 gui/options.cpp:1729
-#: gui/options.cpp:2144 gui/options.cpp:2600 gui/options.cpp:2674
-#: gui/options.cpp:2888 gui/options.cpp:3389 audio/null.cpp:45
+#: gui/options.cpp:2144 gui/options.cpp:2602 gui/options.cpp:2683
+#: gui/options.cpp:2897 gui/options.cpp:3402 audio/null.cpp:45
 #: backends/platform/android/options.cpp:157
 #: backends/platform/android/options.cpp:158
 #: backends/platform/android/options.cpp:186
@@ -834,7 +834,7 @@ msgctxt "lowres"
 msgid "Platform:"
 msgstr "Plattform:"
 
-#: gui/editgamedialog.cpp:566 gui/options.cpp:3413
+#: gui/editgamedialog.cpp:566 gui/options.cpp:3426
 msgid "Select SoundFont"
 msgstr "SoundFont ausw??hlen"
 
@@ -842,11 +842,11 @@ msgstr "SoundFont ausw??hlen"
 msgid "Select additional game directory"
 msgstr "Verzeichnis mit zus??tzlichen Dateien ausw??hlen"
 
-#: gui/editgamedialog.cpp:618 gui/options.cpp:3261
+#: gui/editgamedialog.cpp:618 gui/options.cpp:3274
 msgid "Select directory for saved games"
 msgstr "Verzeichnis f??r Spielst??nde ausw??hlen"
 
-#: gui/editgamedialog.cpp:625 gui/options.cpp:3268
+#: gui/editgamedialog.cpp:625 gui/options.cpp:3281
 msgid "The chosen directory cannot be written to. Please select another one."
 msgstr ""
 "In das gew??hlte Verzeichnis kann nicht geschrieben werden. Bitte ein anderes "
@@ -1003,7 +1003,7 @@ msgstr "Sinus"
 msgid "Triangle"
 msgstr "Dreieck"
 
-#: gui/fluidsynth-dialog.cpp:139 gui/options.cpp:2366
+#: gui/fluidsynth-dialog.cpp:139 gui/options.cpp:2368
 msgid "Misc"
 msgstr "Sonstiges"
 
@@ -1043,7 +1043,7 @@ msgstr ""
 "M??chten Sie wirklich alle FluidSynth-Einstellungen auf ihre Standard-Werte "
 "zur??cksetzen?"
 
-#: gui/gui-manager.cpp:159 gui/options.cpp:2357
+#: gui/gui-manager.cpp:159 gui/options.cpp:2359
 msgid "GUI"
 msgstr "Benutzeroberfl??che"
 
@@ -1290,7 +1290,7 @@ msgstr ""
 "Trading Zone und andere Auktions-Seiten eine gute Anlaufstelle. Achte auf "
 "fehlerhafte Spiele und illegale Kopien.\n"
 
-#: gui/helpdialog.cpp:68 gui/options.cpp:2377
+#: gui/helpdialog.cpp:68 gui/options.cpp:2379
 msgid "Cloud"
 msgstr "Cloud"
 
@@ -1638,85 +1638,85 @@ msgstr "Listenansicht"
 msgid "Grid view"
 msgstr "Rasteransicht"
 
-#: gui/launcher.cpp:1145
+#: gui/launcher.cpp:1168
 msgid "~S~tart"
 msgstr "~S~tarten"
 
-#: gui/launcher.cpp:1145
+#: gui/launcher.cpp:1168
 msgid "Start selected game"
 msgstr "Ausgew??hltes Spiel starten"
 
-#: gui/launcher.cpp:1148
+#: gui/launcher.cpp:1171
 msgid "~L~oad..."
 msgstr "~L~aden..."
 
-#: gui/launcher.cpp:1148
+#: gui/launcher.cpp:1171
 msgid "Load saved game for selected game"
 msgstr "Spielstand f??r ausgew??hltes Spiel laden"
 
-#: gui/launcher.cpp:1150
+#: gui/launcher.cpp:1173
 msgid "Record..."
 msgstr "Aufzeichnen..."
 
-#: gui/launcher.cpp:1156
+#: gui/launcher.cpp:1179
 msgid "~G~ame Options..."
 msgstr "Spiel-~O~ptionen..."
 
-#: gui/launcher.cpp:1156
+#: gui/launcher.cpp:1179
 msgid "Change game options"
 msgstr "Spieloptionen ??ndern"
 
-#: gui/launcher.cpp:1156
+#: gui/launcher.cpp:1179
 msgctxt "lowres"
 msgid "~G~ame Opts..."
 msgstr "~S~piel-Opts..."
 
-#: gui/launcher.cpp:1242
+#: gui/launcher.cpp:1265
 msgid "Add-on"
 msgstr "Add-On"
 
 #. I18N: List grouping when no engine is specified
-#: gui/launcher.cpp:1309 gui/launcher.cpp:1554
+#: gui/launcher.cpp:1335 gui/launcher.cpp:1580
 msgid "Unknown Engine"
 msgstr "Unbekannte Engine"
 
 #. I18N: List grouping when no publisher is specified
 #. I18N: List group when no publisher is specified
-#: gui/launcher.cpp:1326 gui/launcher.cpp:1571
+#: gui/launcher.cpp:1352 gui/launcher.cpp:1597
 msgid "Unknown Publisher"
 msgstr "Unbekannter Publisher"
 
 #. I18N: List group when no game series is specified
 #. I18N: List grouping when no game series is specified
-#: gui/launcher.cpp:1343 gui/launcher.cpp:1588
+#: gui/launcher.cpp:1369 gui/launcher.cpp:1614
 msgid "No Series"
 msgstr "Keine Serie"
 
 #. I18N: List group when no language is specified
-#: gui/launcher.cpp:1357 gui/launcher.cpp:1602
+#: gui/launcher.cpp:1383 gui/launcher.cpp:1628
 msgid "Language not detected"
 msgstr "Sprache nicht erkannt"
 
 #. I18N: List group when no platform is specified
-#: gui/launcher.cpp:1371 gui/launcher.cpp:1616
+#: gui/launcher.cpp:1397 gui/launcher.cpp:1642
 msgid "Platform not detected"
 msgstr "Plattform nicht erkannt"
 
 #. I18N: List group when no year is specified
-#: gui/launcher.cpp:1388 gui/launcher.cpp:1633
+#: gui/launcher.cpp:1414 gui/launcher.cpp:1659
 msgid "Unknown Year"
 msgstr "Unbekanntes Jahr"
 
 #. I18N: Group for All items
-#: gui/launcher.cpp:1640 gui/widgets/groupedlist.cpp:82
+#: gui/launcher.cpp:1666 gui/widgets/groupedlist.cpp:82
 msgid "All"
 msgstr "Alle"
 
-#: gui/launcher.cpp:1812
+#: gui/launcher.cpp:1844
 msgid "Icons per row:"
 msgstr "Icons pro Reihe:"
 
-#: gui/launcher.cpp:1869
+#: gui/launcher.cpp:1901
 #, c-format
 msgid ""
 "Do you really want to remove the following %d game configurations?\n"
@@ -2209,54 +2209,54 @@ msgctxt "lowres"
 msgid "Speech volume:"
 msgstr "Sprachlautst.:"
 
-#: gui/options.cpp:2266
+#: gui/options.cpp:2268
 msgid "Control"
 msgstr "Steuerung"
 
-#: gui/options.cpp:2379
+#: gui/options.cpp:2381
 msgctxt "lowres"
 msgid "Cloud"
 msgstr "Cloud"
 
-#: gui/options.cpp:2394
+#: gui/options.cpp:2396
 msgid "LAN"
 msgstr "LAN"
 
-#: gui/options.cpp:2396
+#: gui/options.cpp:2398
 msgctxt "lowres"
 msgid "LAN"
 msgstr "LAN"
 
-#: gui/options.cpp:2405
+#: gui/options.cpp:2407
 msgid "Accessibility"
 msgstr "Eingabehilfen"
 
-#: gui/options.cpp:2407
+#: gui/options.cpp:2409
 msgctxt "lowres"
 msgid "Accessibility"
 msgstr "Eingabehilfen"
 
-#: gui/options.cpp:2416
+#: gui/options.cpp:2418
 msgid "Discard changes and close the dialog"
 msgstr "??nderungen verwerfen und diesen Dialog schlie??en"
 
-#: gui/options.cpp:2417
+#: gui/options.cpp:2419
 msgid "Apply"
 msgstr "??bernehmen"
 
-#: gui/options.cpp:2417
+#: gui/options.cpp:2419
 msgid "Apply changes without closing the dialog"
 msgstr "Anderungen anwenden, ohne diesen Dialog zu schlie??en"
 
-#: gui/options.cpp:2418
+#: gui/options.cpp:2420
 msgid "Apply changes and close the dialog"
 msgstr "??nderungen anwenden und Dialog schlie??en"
 
-#: gui/options.cpp:2508
+#: gui/options.cpp:2510
 msgid "FluidSynth Settings"
 msgstr "FluidSynth-Einstellungen"
 
-#: gui/options.cpp:2522
+#: gui/options.cpp:2524
 msgid ""
 "Specifies where your saved games are put. A red coloring indicates the value "
 "is temporary and will not get saved"
@@ -2265,92 +2265,92 @@ msgstr ""
 "rote Markierung bedeutet, dass das Verzeichnis nur tempor??r gesetzt ist und "
 "nicht gespeichert wird"
 
-#: gui/options.cpp:2527
+#: gui/options.cpp:2529
 msgid "Theme Path:"
 msgstr "Themes:"
 
-#: gui/options.cpp:2529
+#: gui/options.cpp:2531
 msgctxt "lowres"
 msgid "Theme Path:"
 msgstr "Themes:"
 
-#: gui/options.cpp:2535
+#: gui/options.cpp:2537
 msgid "Icon Path:"
 msgstr "Icon-Pfad:"
 
-#: gui/options.cpp:2537
+#: gui/options.cpp:2539
 msgctxt "lowres"
 msgid "Icon Path:"
 msgstr "Icon-Pfad:"
 
-#: gui/options.cpp:2538 gui/options.cpp:2548
+#: gui/options.cpp:2540 gui/options.cpp:2550
 msgctxt "path"
 msgid "Default"
 msgstr "Standard"
 
-#: gui/options.cpp:2545
+#: gui/options.cpp:2547
 msgid "DLC Path:"
 msgstr "DLC-Pfad:"
 
-#: gui/options.cpp:2547
+#: gui/options.cpp:2549
 msgctxt "lowres"
 msgid "DLC Path:"
 msgstr "DLC-Pfad:"
 
-#: gui/options.cpp:2555 gui/options.cpp:2557 gui/options.cpp:2558
+#: gui/options.cpp:2557 gui/options.cpp:2559 gui/options.cpp:2560
 msgid "Specifies path to additional data used by all games or ScummVM"
 msgstr ""
 "Legt das Verzeichnis f??r zus??tzliche Spieldateien f??r alle Spiele in ScummVM "
 "fest"
 
-#: gui/options.cpp:2564
+#: gui/options.cpp:2566
 msgid "Plugins Path:"
 msgstr "Plugins:"
 
-#: gui/options.cpp:2566
+#: gui/options.cpp:2568
 msgctxt "lowres"
 msgid "Plugins Path:"
 msgstr "Plugins:"
 
-#: gui/options.cpp:2576
+#: gui/options.cpp:2578
 msgid "ScummVM config path: "
 msgstr "ScummVM-Konfiguration: "
 
-#: gui/options.cpp:2590
+#: gui/options.cpp:2592
 msgid "ScummVM log path: "
 msgstr "Pfad zum ScummVM-Log: "
 
-#: gui/options.cpp:2595
+#: gui/options.cpp:2597
 msgid "View"
 msgstr "Log ??ffnen"
 
 #. I18N: Referring to the last path memorized when adding a game
-#: gui/options.cpp:2605 gui/options.cpp:3389
+#: gui/options.cpp:2607 gui/options.cpp:3402
 msgid "Last browser path: "
 msgstr "Zuletzt aufgerufener Datei-Pfad: "
 
-#: gui/options.cpp:2610
+#: gui/options.cpp:2612
 msgid "Theme:"
 msgstr "Theme:"
 
-#: gui/options.cpp:2615
+#: gui/options.cpp:2617
 msgid "GUI scale:"
 msgstr "GUI-Skalierung:"
 
-#: gui/options.cpp:2632
+#: gui/options.cpp:2634
 #, c-format
 msgid "%d%% - Custom"
 msgstr "%d%% - Benutzerdefiniert"
 
-#: gui/options.cpp:2636
+#: gui/options.cpp:2638
 msgid "GUI renderer:"
 msgstr "GUI-Renderer:"
 
-#: gui/options.cpp:2649
+#: gui/options.cpp:2651
 msgid "Return to the launcher when leaving a game"
 msgstr "Beim Verlassen eines Spiels zum Launcher zur??ckkehren"
 
-#: gui/options.cpp:2650
+#: gui/options.cpp:2652
 msgid ""
 "Return to the launcher when leaving a game instead of closing ScummVM\n"
 "(this feature is not supported by all games)."
@@ -2359,42 +2359,58 @@ msgstr ""
 "beenden.\n"
 "Dieses Feature wird nicht von allen Spielen unterst??tzt."
 
-#: gui/options.cpp:2657
+#: gui/options.cpp:2659
 msgid "Ask for confirmation on exit"
 msgstr "Beim Verlassen um Best??tigung bitten"
 
-#: gui/options.cpp:2658
+#: gui/options.cpp:2660
 msgid "Ask for permission when closing ScummVM or leaving a game."
 msgstr ""
 "Bittet beim Verlassen eines Spiels oder von ScummVM um eine zus??tzliche "
 "Best??tigung."
 
-#: gui/options.cpp:2664
+#: gui/options.cpp:2666
 msgid "Disable fixed font scaling"
 msgstr "Schrift-Skalierung deaktivieren"
 
-#: gui/options.cpp:2665
+#: gui/options.cpp:2667
+#, fuzzy
+#| msgid ""
+#| "Do not upscale fixed size fonts in the GUI. This reduces artefacts on low "
+#| "resolution screens"
 msgid ""
 "Do not upscale fixed size fonts in the GUI. This reduces artefacts on low "
-"resolution screens"
+"resolution screens."
 msgstr ""
 "Dicktengleiche Schriften werden in der Benutzeroberfl??che nicht skaliert. "
 "Diese Einstellung reduziert Darstellungs-Artefakte auf niedrig aufl??senden "
 "Bildschirmen"
 
-#: gui/options.cpp:2671
+#: gui/options.cpp:2673
+#, fuzzy
+#| msgid "Enable smooth scrolling when walking"
+msgid "Enable kinetic scrolling in lists"
+msgstr "Aktiviert gleichm????iges Scrollen beim Gehen"
+
+#: gui/options.cpp:2674
+#, fuzzy
+#| msgid "Enable smooth scrolling when walking"
+msgid "Enable smooth, momentum-based scrolling in list widgets."
+msgstr "Aktiviert gleichm????iges Scrollen beim Gehen"
+
+#: gui/options.cpp:2680
 msgid "GUI language:"
 msgstr "Sprache:"
 
-#: gui/options.cpp:2671
+#: gui/options.cpp:2680
 msgid "Language of ScummVM GUI"
 msgstr "Sprache der ScummVM-Oberfl??che"
 
-#: gui/options.cpp:2697
+#: gui/options.cpp:2706
 msgid "Switch the ScummVM GUI language to the game language"
 msgstr "Sprache der ScummVM-Oberfl??che auf Spielsprache einstellen"
 
-#: gui/options.cpp:2698
+#: gui/options.cpp:2707
 msgid ""
 "When starting a game, change the ScummVM GUI language to the game language. "
 "That way, if a game uses the ScummVM save and load dialogs, they are in the "
@@ -2405,11 +2421,11 @@ msgstr ""
 "Verwendung der ScummVM-eigenen \"Speichern und Laden\"-Funktionalit??t "
 "gleiche Sprachen verwendet werden."
 
-#: gui/options.cpp:2710
+#: gui/options.cpp:2719
 msgid "Use native system file browser"
 msgstr "Verwende betriebssystemeigenen Dateimanager"
 
-#: gui/options.cpp:2711
+#: gui/options.cpp:2720
 msgid ""
 "Use the native system file browser instead of the ScummVM one to select a "
 "file or directory."
@@ -2417,50 +2433,50 @@ msgstr ""
 "Verwende zur Auswahl einer Datei oder eines Verzeichnisses den "
 "betriebssystemeigenen Dateimanager anstelle des ScummVM-eigenen Auswahlmen??s."
 
-#: gui/options.cpp:2718
+#: gui/options.cpp:2727
 msgid "Download Icons"
 msgstr "Icons herunterladen"
 
-#: gui/options.cpp:2718
+#: gui/options.cpp:2727
 msgid "Check on the scummvm.org website for updates of icon packs"
 msgstr "Pr??ft die scummvm.org-Webseite auf Aktualisierungen f??r Icon-Packs"
 
-#: gui/options.cpp:2724
+#: gui/options.cpp:2733
 msgid "Autosave:"
 msgstr "Autom. Speichern:"
 
-#: gui/options.cpp:2726
+#: gui/options.cpp:2735
 msgctxt "lowres"
 msgid "Autosave:"
 msgstr "Autospeichern:"
 
-#: gui/options.cpp:2737
+#: gui/options.cpp:2746
 msgid "Random seed:"
 msgstr "RNG-Seed:"
 
-#: gui/options.cpp:2737
+#: gui/options.cpp:2746
 msgid "Seed for initializing all random number generators"
 msgstr "Seed, um alle Zufallszahlen-Generatoren zu initialisieren"
 
-#: gui/options.cpp:2745
+#: gui/options.cpp:2754
 msgid "Debug level:"
 msgstr "Debug-Level:"
 
 #. I18N: Debug level -1, no messages
-#: gui/options.cpp:2749 gui/options.cpp:2886 audio/fmopl.cpp:83
+#: gui/options.cpp:2758 gui/options.cpp:2895 audio/fmopl.cpp:83
 msgid "None"
 msgstr "Nichts"
 
 #. I18N: Debug level 11, all messages
-#: gui/options.cpp:2755
+#: gui/options.cpp:2764
 msgid "11 (all)"
 msgstr "11 (alles)"
 
-#: gui/options.cpp:2759
+#: gui/options.cpp:2768
 msgid "Enable Discord integration"
 msgstr "Discord-Integration aktivieren"
 
-#: gui/options.cpp:2760
+#: gui/options.cpp:2769
 msgid ""
 "Show information about the games you are playing on Discord if the Discord "
 "client is running."
@@ -2468,27 +2484,27 @@ msgstr ""
 "Zeige Informationen ??ber die Spiele, die Du gerade spielst, auf Discord an, "
 "wenn der Discord-Client l??uft."
 
-#: gui/options.cpp:2770 gui/updates-dialog.cpp:87
+#: gui/options.cpp:2779 gui/updates-dialog.cpp:87
 msgid "Update check:"
 msgstr "Updates suchen:"
 
-#: gui/options.cpp:2770
+#: gui/options.cpp:2779
 msgid "How often to check ScummVM updates"
 msgstr "Legt fest, wie oft nach Aktualisierungen f??r ScummVM gesucht wird"
 
-#: gui/options.cpp:2781
+#: gui/options.cpp:2790
 msgid "Check now"
 msgstr "Jetzt pr??fen"
 
-#: gui/options.cpp:2788
+#: gui/options.cpp:2797
 msgid "Active storage:"
 msgstr "Aktive Cloud:"
 
-#: gui/options.cpp:2788
+#: gui/options.cpp:2797
 msgid "Active cloud storage"
 msgstr "Aktiver Cloud-Speicher"
 
-#: gui/options.cpp:2797
+#: gui/options.cpp:2806
 msgctxt "lowres"
 msgid ""
 "Storage is not yet enabled. Verify that username is correct and enable it:"
@@ -2496,183 +2512,183 @@ msgstr ""
 "Der Cloud-Speicher ist noch nicht aktiv. Benutzernamen ??berpr??fen und "
 "aktivieren:"
 
-#: gui/options.cpp:2799
+#: gui/options.cpp:2808
 msgid ""
 "Storage is not yet enabled. Verify that username is correct and enable it:"
 msgstr ""
 "Der Cloud-Speicher ist noch nicht aktiv. Benutzernamen ??berpr??fen und "
 "aktivieren:"
 
-#: gui/options.cpp:2800
+#: gui/options.cpp:2809
 msgid "Enable storage"
 msgstr "Cloud aktivieren"
 
-#: gui/options.cpp:2800
+#: gui/options.cpp:2809
 msgid "Confirm you want to use this account for this storage"
 msgstr ""
 "Bitte best??tigen Sie, dass Sie dieses Konto f??r diesen Cloud-Speicher "
 "verwenden m??chten"
 
-#: gui/options.cpp:2802 backends/platform/wii/options.cpp:113
+#: gui/options.cpp:2811 backends/platform/wii/options.cpp:113
 msgid "Username:"
 msgstr "Benutzername:"
 
-#: gui/options.cpp:2802
+#: gui/options.cpp:2811
 msgid "Username used by this storage"
 msgstr "Benutzername, der von diesem Cloud-Speicher verwendet wird"
 
-#: gui/options.cpp:2803
+#: gui/options.cpp:2812
 msgid "<none>"
 msgstr "<keine>"
 
-#: gui/options.cpp:2805
+#: gui/options.cpp:2814
 msgid "Used space:"
 msgstr "Belegter Speicher:"
 
-#: gui/options.cpp:2805
+#: gui/options.cpp:2814
 msgid "Space used by ScummVM's saved games on this storage"
 msgstr ""
 "Von ScummVM-Spielst??nden belegter Speicherplatz auf diesem Cloud-Speicher"
 
-#: gui/options.cpp:2808
+#: gui/options.cpp:2817
 msgid "Last sync:"
 msgstr "Letzte Synchronisation:"
 
-#: gui/options.cpp:2808
+#: gui/options.cpp:2817
 msgid "When was the last time saved games were synced with this storage"
 msgstr "Zeitpunkt der letzten Spielstand-Synchronisierung"
 
-#: gui/options.cpp:2809 gui/options.cpp:3693
+#: gui/options.cpp:2818 gui/options.cpp:3706
 msgid "<never>"
 msgstr "<nie>"
 
-#: gui/options.cpp:2811
+#: gui/options.cpp:2820
 msgctxt "lowres"
 msgid "Saved games sync automatically on launch, after saving and on loading."
 msgstr "Spielst??nde werden automatisch synchronisiert."
 
-#: gui/options.cpp:2813
+#: gui/options.cpp:2822
 msgid "Saved games sync automatically on launch, after saving and on loading."
 msgstr "Spielst??nde werden automatisch synchronisiert."
 
-#: gui/options.cpp:2814
+#: gui/options.cpp:2823
 msgid "Sync now"
 msgstr "Synchronisieren"
 
-#: gui/options.cpp:2814
+#: gui/options.cpp:2823
 msgid "Start saved games sync"
 msgstr "Starte Spielstand-Synchronisation"
 
-#: gui/options.cpp:2817
+#: gui/options.cpp:2826
 msgctxt "lowres"
 msgid "You can download game files from your cloud ScummVM folder:"
 msgstr "Laden Sie die Spieldateien des ScummVM-Ordners Ihrer Cloud herunter:"
 
-#: gui/options.cpp:2819
+#: gui/options.cpp:2828
 msgid "You can download game files from your cloud ScummVM folder:"
 msgstr "Laden Sie die Spieldateien des ScummVM-Ordners Ihrer Cloud herunter:"
 
-#: gui/options.cpp:2820
+#: gui/options.cpp:2829
 msgid "Download game files"
 msgstr "Spieldateien herunterladen"
 
-#: gui/options.cpp:2820
+#: gui/options.cpp:2829
 msgid "Open downloads manager dialog"
 msgstr "??ffnet den Download-Manager"
 
-#: gui/options.cpp:2823
+#: gui/options.cpp:2832
 msgctxt "lowres"
 msgid "To change account for this storage, disconnect and connect again:"
 msgstr "Cloud trennen und neu verbinden, um ein anderes Konto zu w??hlen:"
 
-#: gui/options.cpp:2825
+#: gui/options.cpp:2834
 msgid "To change account for this storage, disconnect and connect again:"
 msgstr "Cloud trennen und neu verbinden, um ein anderes Konto zu w??hlen:"
 
-#: gui/options.cpp:2826
+#: gui/options.cpp:2835
 msgid "Disconnect"
 msgstr "Trennen"
 
-#: gui/options.cpp:2826
+#: gui/options.cpp:2835
 msgid "Stop using this storage on this device"
 msgstr "Diesen Cloud-Speicher nicht mehr auf diesem Ger??t verwenden"
 
-#: gui/options.cpp:2829
+#: gui/options.cpp:2838
 msgctxt "lowres"
 msgid "This storage is not connected yet!"
 msgstr "Dieser Cloud-Speicher ist noch nicht verbunden!"
 
-#: gui/options.cpp:2831
+#: gui/options.cpp:2840
 msgid "This storage is not connected yet!"
 msgstr "Dieser Cloud-Speicher ist noch nicht verbunden!"
 
-#: gui/options.cpp:2832
+#: gui/options.cpp:2841
 msgid "Connect"
 msgstr "Verbinden"
 
-#: gui/options.cpp:2832
+#: gui/options.cpp:2841
 msgid "Connect your cloud storage account"
 msgstr "Verbinden Sie Ihr Konto Ihres Cloud-Anbieters"
 
-#: gui/options.cpp:2845
+#: gui/options.cpp:2854
 msgctxt "lowres"
 msgid "/root/ Path:"
 msgstr "/root/-Pfad:"
 
-#: gui/options.cpp:2845 gui/options.cpp:2847 gui/options.cpp:2848
+#: gui/options.cpp:2854 gui/options.cpp:2856 gui/options.cpp:2857
 msgid "Select which directory will be shown as /root/ in the Files Manager"
 msgstr ""
 "Legt fest, welches Verzeichnis als /root/ im Dateimanager angezeigt wird"
 
-#: gui/options.cpp:2847
+#: gui/options.cpp:2856
 msgid "/root/ Path:"
 msgstr "/root/-Pfad:"
 
-#: gui/options.cpp:2853
+#: gui/options.cpp:2862
 msgid "Server's port:"
 msgstr "Server-Port:"
 
-#: gui/options.cpp:2853
+#: gui/options.cpp:2862
 msgid "Port for server to use"
 msgstr "Port, der f??r den Server genutzt wird"
 
-#: gui/options.cpp:2858
+#: gui/options.cpp:2867
 msgctxt "lowres"
 msgid "Run server to manage files with browser (in the same network)."
 msgstr "Erlaubt die Dateiverwaltung ??ber einen Browser (im gleichen Netzwerk)."
 
-#: gui/options.cpp:2859
+#: gui/options.cpp:2868
 msgctxt "lowres"
 msgid "Closing options dialog will stop the server."
 msgstr "Das Schlie??en des Optionen-Dialogs wird den Server beenden."
 
-#: gui/options.cpp:2861
+#: gui/options.cpp:2870
 msgid "Run server to manage files with browser (in the same network)."
 msgstr "Erlaubt die Dateiverwaltung ??ber einen Browser (im gleichen Netzwerk)."
 
-#: gui/options.cpp:2862
+#: gui/options.cpp:2871
 msgid "Closing options dialog will stop the server."
 msgstr "Das Schlie??en des Optionen-Dialogs wird den Server beenden."
 
-#: gui/options.cpp:2873
+#: gui/options.cpp:2882
 msgid "Use Text to speech"
 msgstr "Verwende Sprachausgabe"
 
-#: gui/options.cpp:2873
+#: gui/options.cpp:2882
 msgid "Will read text in gui on mouse over."
 msgstr ""
 "Liest den Text der Benutzeroberfl??che vor, wenn mit der Maus dar??ber "
 "gefahren wird."
 
-#: gui/options.cpp:2954
+#: gui/options.cpp:2963
 msgid "Ignore"
 msgstr "Ignorieren"
 
-#: gui/options.cpp:2955
+#: gui/options.cpp:2964
 msgid "Disable autosave"
 msgstr "Autosave deaktivieren"
 
-#: gui/options.cpp:2956
+#: gui/options.cpp:2965
 msgid ""
 "WARNING: Autosave was enabled. Some of your games have existing saved games "
 "on the autosave slot. You can either move the existing saves to new slots, "
@@ -2688,7 +2704,7 @@ msgstr ""
 "erscheinen).\n"
 "Betroffene Spiele:\n"
 
-#: gui/options.cpp:2965
+#: gui/options.cpp:2974
 msgid ""
 "\n"
 "And more..."
@@ -2696,36 +2712,36 @@ msgstr ""
 "\n"
 "Und weitere..."
 
-#: gui/options.cpp:2966 engines/engine.cpp:647
+#: gui/options.cpp:2975 engines/engine.cpp:647
 #: engines/access/metaengine.cpp:298 engines/drascula/metaengine.cpp:255
 #: engines/queen/metaengine.cpp:269 engines/sherlock/metaengine.cpp:410
 #: engines/ultima/nuvie/metaengine.cpp:71
 msgid "Move"
 msgstr "Verschieben"
 
-#: gui/options.cpp:2985
+#: gui/options.cpp:2994
 msgid "ERROR: Failed to move the following saved games:\n"
 msgstr ""
 "FEHLER: Beim Verschieben der folgenden Spielst??nde ist ein Fehler "
 "aufgetreten:\n"
 
-#: gui/options.cpp:3081
+#: gui/options.cpp:3090
 msgid "Failed to change cloud storage!"
 msgstr "Konnte den Cloud-Speicher nicht ??ndern!"
 
-#: gui/options.cpp:3084
+#: gui/options.cpp:3093
 msgid "Another cloud storage is already active."
 msgstr "Ein anderer Cloud-Speicher arbeitet gerade."
 
-#: gui/options.cpp:3199
+#: gui/options.cpp:3212
 msgid "Theme does not support selected language!"
 msgstr "Das Theme unterst??tzt die gew??hlte Sprache nicht!"
 
-#: gui/options.cpp:3202
+#: gui/options.cpp:3215
 msgid "Theme cannot be loaded!"
 msgstr "Theme konnte nicht geladen werden!"
 
-#: gui/options.cpp:3205
+#: gui/options.cpp:3218
 msgid ""
 "\n"
 "Misc settings will be restored."
@@ -2733,45 +2749,45 @@ msgstr ""
 "\n"
 "Verschiedene Einstellungen werden wiederhergestellt."
 
-#: gui/options.cpp:3277
+#: gui/options.cpp:3290
 msgid "Select directory for GUI themes"
 msgstr "Verzeichnis f??r Oberfl??chen-Themes"
 
-#: gui/options.cpp:3287
+#: gui/options.cpp:3300
 msgid "Select directory for GUI launcher thumbnails"
 msgstr "Verzeichnis f??r Spiele-Thumbnails in der GUI"
 
-#: gui/options.cpp:3298
+#: gui/options.cpp:3311
 msgid "Select directory for DLC downloads"
 msgstr "Verzeichnis f??r DLC-Inhalte ausw??hlen"
 
-#: gui/options.cpp:3309
+#: gui/options.cpp:3322
 msgid "Select directory for extra files"
 msgstr "Verzeichnis f??r zus??tzliche Dateien ausw??hlen"
 
-#: gui/options.cpp:3320
+#: gui/options.cpp:3333
 msgid "Select directory for plugins"
 msgstr "Verzeichnis f??r Plugins ausw??hlen"
 
-#: gui/options.cpp:3333
+#: gui/options.cpp:3346
 msgid "Select directory for Files Manager /root/"
 msgstr ""
 "W??hlen Sie das Verzeichnis aus, welches als Stammverzeichnis ('root') dient"
 
-#: gui/options.cpp:3485
+#: gui/options.cpp:3498
 msgid "This Storage is working now. Do you want to interrupt it?"
 msgstr "Dieser Cloud-Speicher arbeitet gerade. M??chten Sie ihn unterbrechen?"
 
-#: gui/options.cpp:3494
+#: gui/options.cpp:3507
 msgid "Wait until current Storage finishes up and try again."
 msgstr ""
 "Warten Sie, bis der Cloud-Speicher fertig ist und versuchen Sie es erneut."
 
-#: gui/options.cpp:3691 backends/cloud/cloudmanager.cpp:157
+#: gui/options.cpp:3704 backends/cloud/cloudmanager.cpp:157
 msgid "<right now>"
 msgstr "<gerade eben>"
 
-#: gui/options.cpp:3834
+#: gui/options.cpp:3847
 msgid ""
 "Request failed.\n"
 "Check your Internet connection."
@@ -2779,7 +2795,7 @@ msgstr ""
 "Anfrage fehlgeschlagen.\n"
 "Bitte ??berpr??fen Sie Ihre Internetverbindung."
 
-#: gui/options.cpp:3857
+#: gui/options.cpp:3870
 msgid ""
 "A test pattern should be displayed.\n"
 "Do you want to keep these shader scaler settings?"
@@ -2787,7 +2803,7 @@ msgstr ""
 "Ein Testbild sollte nun angezeigt werden.\n"
 "M??chtest Du diese Shader-Einstellungen beibehalten?"
 
-#: gui/options.cpp:3860
+#: gui/options.cpp:3873
 #, c-format
 msgid "Reverting automatically in %d seconds"
 msgstr "Einstellungen werden in %d Sekunden zur??ckgesetzt"
@@ -2872,7 +2888,7 @@ msgstr "Aufnahme"
 msgid "Playback"
 msgstr "Wiedergabe"
 
-#: gui/recorderdialog.cpp:76 gui/widgets/grid.cpp:341
+#: gui/recorderdialog.cpp:76 gui/widgets/grid.cpp:343
 msgid "Edit"
 msgstr "Bearbeiten"
 
@@ -3086,11 +3102,11 @@ msgstr ""
 msgid "Clear value"
 msgstr "Wert l??schen"
 
-#: gui/widgets/grid.cpp:335
+#: gui/widgets/grid.cpp:337
 msgid "Play"
 msgstr "Spielen"
 
-#: gui/widgets/grid.cpp:338
+#: gui/widgets/grid.cpp:340
 msgid "Saves"
 msgstr "Spielst??nde"
 
diff --git a/po/el.po b/po/el.po
index b828bd4fedb..5957434ace0 100644
--- a/po/el.po
+++ b/po/el.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.10.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-13 10:20+0000\n"
+"POT-Creation-Date: 2026-06-13 13:52+0000\n"
 "PO-Revision-Date: 2026-04-23 00:22+0000\n"
 "Last-Translator: Antoniou Athanasios <a.antoniou79 at gmail.com>\n"
 "Language-Team: Greek <https://translations.scummvm.org/projects/scummvm/"
@@ -115,7 +115,7 @@ msgstr "???????????????? ????????"
 #: gui/downloadpacksdialog.cpp:465 gui/editgamedialog.cpp:340
 #: gui/editrecorddialog.cpp:66 gui/integrity-dialog.cpp:122
 #: gui/integrity-dialog.cpp:253 gui/filebrowser-dialog.cpp:68
-#: gui/fluidsynth-dialog.cpp:153 gui/massadd.cpp:93 gui/options.cpp:2416
+#: gui/fluidsynth-dialog.cpp:153 gui/massadd.cpp:93 gui/options.cpp:2418
 #: gui/predictivedialog.cpp:68 gui/printing-dialog.cpp:96
 #: gui/recorderdialog.cpp:72 gui/recorderdialog.cpp:211
 #: gui/remotebrowser.cpp:59 gui/saveload-dialog.cpp:64
@@ -186,23 +186,23 @@ msgstr ""
 "???? ???????? ???? ????????????????????, ???????? ?????????????? ?????????????????????? ?????????? ???????????? ???? ????????????????????,\n"
 "???????? ?? ?????????????????? ?????? ???? ???????????? ???? ?????????????? ???????????????? ?????? ScummVM"
 
-#: gui/cloudconnectionwizard.cpp:257 gui/options.cpp:3776
+#: gui/cloudconnectionwizard.cpp:257 gui/options.cpp:3789
 msgid "Stop server"
 msgstr "?????????????? ????????????????????"
 
-#: gui/cloudconnectionwizard.cpp:257 gui/options.cpp:2840
+#: gui/cloudconnectionwizard.cpp:257 gui/options.cpp:2849
 msgid "Run server"
 msgstr "???????????????? ????????????????????"
 
-#: gui/cloudconnectionwizard.cpp:258 gui/options.cpp:3777
+#: gui/cloudconnectionwizard.cpp:258 gui/options.cpp:3790
 msgid "Stop local webserver"
 msgstr "?????????????? ?????????????? ???????????????????? ??????????"
 
-#: gui/cloudconnectionwizard.cpp:258 gui/options.cpp:2840
+#: gui/cloudconnectionwizard.cpp:258 gui/options.cpp:2849
 msgid "Run local webserver"
 msgstr "???????????????? ?????????????? ???????????????????? ??????????"
 
-#: gui/cloudconnectionwizard.cpp:266 gui/options.cpp:2841 gui/options.cpp:3784
+#: gui/cloudconnectionwizard.cpp:266 gui/options.cpp:2850 gui/options.cpp:3797
 msgid "Not running"
 msgstr "?????? ????????????????????"
 
@@ -333,8 +333,8 @@ msgstr ""
 #: gui/cloudconnectionwizard.cpp:530 gui/downloaddialog.cpp:118
 #: gui/downloaddialog.cpp:152 gui/filebrowser-dialog.cpp:135
 #: gui/fluidsynth-dialog.cpp:210 gui/launcher.cpp:428 gui/launcher.cpp:457
-#: gui/launcher.cpp:534 gui/launcher.cpp:1938 gui/options.cpp:3485
-#: gui/options.cpp:3859 base/main.cpp:608
+#: gui/launcher.cpp:534 gui/launcher.cpp:1970 gui/options.cpp:3498
+#: gui/options.cpp:3872 base/main.cpp:608
 #: backends/events/default/default-events.cpp:193 engines/buried/buried.cpp:598
 #: engines/chamber/metaengine.cpp:91 engines/director/events.cpp:122
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:139
@@ -353,8 +353,8 @@ msgstr "??????"
 #: gui/cloudconnectionwizard.cpp:530 gui/downloaddialog.cpp:118
 #: gui/downloaddialog.cpp:153 gui/filebrowser-dialog.cpp:135
 #: gui/fluidsynth-dialog.cpp:210 gui/launcher.cpp:428 gui/launcher.cpp:457
-#: gui/launcher.cpp:534 gui/launcher.cpp:1939 gui/options.cpp:3485
-#: gui/options.cpp:3859 engines/buried/buried.cpp:598
+#: gui/launcher.cpp:534 gui/launcher.cpp:1971 gui/options.cpp:3498
+#: gui/options.cpp:3872 engines/buried/buried.cpp:598
 #: engines/chamber/metaengine.cpp:97 engines/director/events.cpp:122
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:139
 #: engines/efh/metaengine.cpp:368 engines/grim/grim.cpp:366
@@ -381,7 +381,7 @@ msgstr "???? ?????????????????????? ?????? ???????????? JSON ?????? ?????????? 
 #: gui/editrecorddialog.cpp:67 gui/integrity-dialog.cpp:260
 #: gui/fluidsynth-dialog.cpp:154 gui/massadd.cpp:90 gui/message.cpp:156
 #: gui/message.cpp:160 gui/message.cpp:231 gui/message.cpp:270
-#: gui/message.cpp:274 gui/options.cpp:2418 gui/predictivedialog.cpp:69
+#: gui/message.cpp:274 gui/options.cpp:2420 gui/predictivedialog.cpp:69
 #: gui/saveload-dialog.cpp:1244 base/main.cpp:675 base/main.cpp:700
 #: dists/android.strings.xml.cpp:33 engines/engine.cpp:846
 #: backends/platform/wii/options.cpp:46 engines/buried/buried.cpp:622
@@ -573,11 +573,11 @@ msgstr ""
 msgid "Proceed"
 msgstr "??????????????"
 
-#: gui/dump-all-dialogs.cpp:123 gui/options.cpp:3347
+#: gui/dump-all-dialogs.cpp:123 gui/options.cpp:3360
 msgid "icon packs"
 msgstr "???????????? ????????????????????"
 
-#: gui/dump-all-dialogs.cpp:127 gui/options.cpp:3358
+#: gui/dump-all-dialogs.cpp:127 gui/options.cpp:3371
 msgid "shader packs"
 msgstr "???????????? ????????????????"
 
@@ -589,11 +589,11 @@ msgstr "???????????????? ???? ????????????????:"
 msgid "Game"
 msgstr "????????????????"
 
-#: gui/editgamedialog.cpp:164 gui/options.cpp:2075 gui/options.cpp:2254
+#: gui/editgamedialog.cpp:164 gui/options.cpp:2075 gui/options.cpp:2256
 msgid "GFX"
 msgstr "????????."
 
-#: gui/editgamedialog.cpp:164 gui/options.cpp:2075 gui/options.cpp:2254
+#: gui/editgamedialog.cpp:164 gui/options.cpp:2075 gui/options.cpp:2256
 #: backends/graphics/atari/atari-graphics.cpp:1056
 #: backends/graphics/sdl/sdl-graphics.cpp:487
 msgid "Graphics"
@@ -608,11 +608,11 @@ msgctxt "lowres"
 msgid "Override global graphic settings"
 msgstr "?????????????????? ?????????????????? ????????. ????????????????"
 
-#: gui/editgamedialog.cpp:185 gui/options.cpp:2288 engines/dialogs.cpp:354
+#: gui/editgamedialog.cpp:185 gui/options.cpp:2290 engines/dialogs.cpp:354
 msgid "Keymaps"
 msgstr "???????????????????????????? ????????????????"
 
-#: gui/editgamedialog.cpp:197 gui/options.cpp:2300 engines/dialogs.cpp:366
+#: gui/editgamedialog.cpp:197 gui/options.cpp:2302 engines/dialogs.cpp:366
 msgid "Backend"
 msgstr "Backend"
 
@@ -625,7 +625,7 @@ msgctxt "lowres"
 msgid "Override global backend settings"
 msgstr "?????????????????? ??????. ????????. backend"
 
-#: gui/editgamedialog.cpp:220 gui/options.cpp:2318 engines/dialogs.cpp:329
+#: gui/editgamedialog.cpp:220 gui/options.cpp:2320 engines/dialogs.cpp:329
 msgid "Audio"
 msgstr "????????"
 
@@ -638,11 +638,11 @@ msgctxt "lowres"
 msgid "Override global audio settings"
 msgstr "?????????????????? ??????. ????????. ????????"
 
-#: gui/editgamedialog.cpp:234 gui/options.cpp:2323
+#: gui/editgamedialog.cpp:234 gui/options.cpp:2325
 msgid "Volume"
 msgstr "????????????"
 
-#: gui/editgamedialog.cpp:236 gui/options.cpp:2325
+#: gui/editgamedialog.cpp:236 gui/options.cpp:2327
 msgctxt "lowres"
 msgid "Volume"
 msgstr "????????????"
@@ -656,7 +656,7 @@ msgctxt "lowres"
 msgid "Override global volume settings"
 msgstr "?????????????????? ??????. ????????. ??????????????"
 
-#: gui/editgamedialog.cpp:252 gui/options.cpp:2333
+#: gui/editgamedialog.cpp:252 gui/options.cpp:2335
 msgid "MIDI"
 msgstr "MIDI"
 
@@ -669,7 +669,7 @@ msgctxt "lowres"
 msgid "Override global MIDI settings"
 msgstr "?????????????????? ??????. ????????. MIDI"
 
-#: gui/editgamedialog.cpp:267 gui/options.cpp:2339
+#: gui/editgamedialog.cpp:267 gui/options.cpp:2341
 msgid "MT-32"
 msgstr "MT-32"
 
@@ -682,11 +682,11 @@ msgctxt "lowres"
 msgid "Override global MT-32 settings"
 msgstr "?????????????????? ??????. ????????. MT-32"
 
-#: gui/editgamedialog.cpp:281 gui/options.cpp:2346
+#: gui/editgamedialog.cpp:281 gui/options.cpp:2348
 msgid "Paths"
 msgstr "??????????????"
 
-#: gui/editgamedialog.cpp:283 gui/options.cpp:2348
+#: gui/editgamedialog.cpp:283 gui/options.cpp:2350
 msgctxt "lowres"
 msgid "Paths"
 msgstr "??????????????"
@@ -708,7 +708,7 @@ msgctxt "lowres"
 msgid "Game Path:"
 msgstr "?????????????? ????????????????????:"
 
-#: gui/editgamedialog.cpp:303 gui/options.cpp:2555
+#: gui/editgamedialog.cpp:303 gui/options.cpp:2557
 msgid "Extra Path:"
 msgstr "????????????????:"
 
@@ -718,33 +718,33 @@ msgid "Specifies path to additional data used by the game"
 msgstr ""
 "?????????????????? ???????????????? ?????? ???????????????? ???????????????? ?????? ???????????????????????????????? ?????? ???? ????????????????"
 
-#: gui/editgamedialog.cpp:305 gui/options.cpp:2557
+#: gui/editgamedialog.cpp:305 gui/options.cpp:2559
 msgctxt "lowres"
 msgid "Extra Path:"
 msgstr "????????????????:"
 
-#: gui/editgamedialog.cpp:306 gui/options.cpp:2530 gui/options.cpp:2558
-#: gui/options.cpp:2567 gui/options.cpp:2848
+#: gui/editgamedialog.cpp:306 gui/options.cpp:2532 gui/options.cpp:2560
+#: gui/options.cpp:2569 gui/options.cpp:2857
 #: backends/platform/libretro/src/libretro-options-widget.cpp:277
 msgctxt "path"
 msgid "None"
 msgstr "?????? ???????? ??????????????????????????"
 
-#: gui/editgamedialog.cpp:312 gui/options.cpp:2519
+#: gui/editgamedialog.cpp:312 gui/options.cpp:2521
 msgid "Save Path:"
 msgstr "?????????????? ??????????????????????:"
 
 #: gui/editgamedialog.cpp:312 gui/editgamedialog.cpp:314
-#: gui/editgamedialog.cpp:315 gui/options.cpp:2519 gui/options.cpp:2521
+#: gui/editgamedialog.cpp:315 gui/options.cpp:2521 gui/options.cpp:2523
 msgid "Specifies where your saved games are put"
 msgstr "?????????????????? ?????? ?????????????????? ?????? ?????????????????????????? ???????????????????? ??????"
 
-#: gui/editgamedialog.cpp:314 gui/options.cpp:2521
+#: gui/editgamedialog.cpp:314 gui/options.cpp:2523
 msgctxt "lowres"
 msgid "Save Path:"
 msgstr "?????????????? ????????.:"
 
-#: gui/editgamedialog.cpp:315 gui/options.cpp:1844 gui/options.cpp:2522
+#: gui/editgamedialog.cpp:315 gui/options.cpp:1844 gui/options.cpp:2524
 #: backends/platform/wii/options.cpp:55
 msgid "Default"
 msgstr "????????????????????????????"
@@ -806,8 +806,8 @@ msgstr ""
 #: gui/editgamedialog.cpp:365 gui/editgamedialog.cpp:380 gui/options.cpp:1613
 #: gui/options.cpp:1627 gui/options.cpp:1644 gui/options.cpp:1658
 #: gui/options.cpp:1675 gui/options.cpp:1713 gui/options.cpp:1729
-#: gui/options.cpp:2144 gui/options.cpp:2600 gui/options.cpp:2674
-#: gui/options.cpp:2888 gui/options.cpp:3389 audio/null.cpp:45
+#: gui/options.cpp:2144 gui/options.cpp:2602 gui/options.cpp:2683
+#: gui/options.cpp:2897 gui/options.cpp:3402 audio/null.cpp:45
 #: backends/platform/android/options.cpp:157
 #: backends/platform/android/options.cpp:158
 #: backends/platform/android/options.cpp:186
@@ -834,7 +834,7 @@ msgctxt "lowres"
 msgid "Platform:"
 msgstr "??????????????????:"
 
-#: gui/editgamedialog.cpp:566 gui/options.cpp:3413
+#: gui/editgamedialog.cpp:566 gui/options.cpp:3426
 msgid "Select SoundFont"
 msgstr "???????????????? SoundFont"
 
@@ -842,11 +842,11 @@ msgstr "???????????????? SoundFont"
 msgid "Select additional game directory"
 msgstr "???????????????? ???????????????? ???????????? ????????????????????"
 
-#: gui/editgamedialog.cpp:618 gui/options.cpp:3261
+#: gui/editgamedialog.cpp:618 gui/options.cpp:3274
 msgid "Select directory for saved games"
 msgstr "???????????????? ???????????? ?????? ???????????????????????? ??????????????????"
 
-#: gui/editgamedialog.cpp:625 gui/options.cpp:3268
+#: gui/editgamedialog.cpp:625 gui/options.cpp:3281
 msgid "The chosen directory cannot be written to. Please select another one."
 msgstr ""
 "???????????????? ???????????????? ???????? ???????????????????? ????????????. ???????????????? ???????????????? ?????????????? ????????."
@@ -1005,7 +1005,7 @@ msgstr "??????????????"
 msgid "Triangle"
 msgstr "??????????????"
 
-#: gui/fluidsynth-dialog.cpp:139 gui/options.cpp:2366
+#: gui/fluidsynth-dialog.cpp:139 gui/options.cpp:2368
 msgid "Misc"
 msgstr "??????????????"
 
@@ -1046,7 +1046,7 @@ msgstr ""
 "???????????? ?????????????? ???? ?????????????????????? ???????? ?????? ?????????????????? ?????? FluidSynth ???????? "
 "?????????????????????????????? ???????? ??????????;"
 
-#: gui/gui-manager.cpp:159 gui/options.cpp:2357
+#: gui/gui-manager.cpp:159 gui/options.cpp:2359
 msgid "GUI"
 msgstr "??????"
 
@@ -1297,7 +1297,7 @@ msgstr ""
 "???????????????????? ???? ??????????????????????. ???? ???????? ?????? ?????????????????? ???????????????????? ?????????????? ?????? "
 "???????????????? ?????????????????? ???????????????????? ?? ???? ???????????????????????? ???????????????????????????? ??????????????????.\n"
 
-#: gui/helpdialog.cpp:68 gui/options.cpp:2377
+#: gui/helpdialog.cpp:68 gui/options.cpp:2379
 msgid "Cloud"
 msgstr "??????????"
 
@@ -1651,85 +1651,85 @@ msgstr "?????????????? ???? ??????????"
 msgid "Grid view"
 msgstr "?????????????? ???? ????????????"
 
-#: gui/launcher.cpp:1145
+#: gui/launcher.cpp:1168
 msgid "~S~tart"
 msgstr "~??~??????????????"
 
-#: gui/launcher.cpp:1145
+#: gui/launcher.cpp:1168
 msgid "Start selected game"
 msgstr "???????????????? ?????????????????????? ????????????????????"
 
-#: gui/launcher.cpp:1148
+#: gui/launcher.cpp:1171
 msgid "~L~oad..."
 msgstr "~??~????????????..."
 
-#: gui/launcher.cpp:1148
+#: gui/launcher.cpp:1171
 msgid "Load saved game for selected game"
 msgstr "?????????????? ?????????????????????????? ???????????????????? ?????? ???? ???????????????????? ????????????????"
 
-#: gui/launcher.cpp:1150
+#: gui/launcher.cpp:1173
 msgid "Record..."
 msgstr "??????????????..."
 
-#: gui/launcher.cpp:1156
+#: gui/launcher.cpp:1179
 msgid "~G~ame Options..."
 msgstr "~??~???????????????? ????????????????????..."
 
-#: gui/launcher.cpp:1156
+#: gui/launcher.cpp:1179
 msgid "Change game options"
 msgstr "???????????? ???????????????? ????????????????????"
 
-#: gui/launcher.cpp:1156
+#: gui/launcher.cpp:1179
 msgctxt "lowres"
 msgid "~G~ame Opts..."
 msgstr "~??~?????? ??????????..."
 
-#: gui/launcher.cpp:1242
+#: gui/launcher.cpp:1265
 msgid "Add-on"
 msgstr "????????????????"
 
 #. I18N: List grouping when no engine is specified
-#: gui/launcher.cpp:1309 gui/launcher.cpp:1554
+#: gui/launcher.cpp:1335 gui/launcher.cpp:1580
 msgid "Unknown Engine"
 msgstr "?????????????? ????????????"
 
 #. I18N: List grouping when no publisher is specified
 #. I18N: List group when no publisher is specified
-#: gui/launcher.cpp:1326 gui/launcher.cpp:1571
+#: gui/launcher.cpp:1352 gui/launcher.cpp:1597
 msgid "Unknown Publisher"
 msgstr "???????????????? ??????????????"
 
 #. I18N: List group when no game series is specified
 #. I18N: List grouping when no game series is specified
-#: gui/launcher.cpp:1343 gui/launcher.cpp:1588
+#: gui/launcher.cpp:1369 gui/launcher.cpp:1614
 msgid "No Series"
 msgstr "?????? ?????????????? ???? ??????????"
 
 #. I18N: List group when no language is specified
-#: gui/launcher.cpp:1357 gui/launcher.cpp:1602
+#: gui/launcher.cpp:1383 gui/launcher.cpp:1628
 msgid "Language not detected"
 msgstr "?????? ?????????????????????? ?? ????????????"
 
 #. I18N: List group when no platform is specified
-#: gui/launcher.cpp:1371 gui/launcher.cpp:1616
+#: gui/launcher.cpp:1397 gui/launcher.cpp:1642
 msgid "Platform not detected"
 msgstr "?????? ?????????????????????? ?? ??????????????????"
 
 #. I18N: List group when no year is specified
-#: gui/launcher.cpp:1388 gui/launcher.cpp:1633
+#: gui/launcher.cpp:1414 gui/launcher.cpp:1659
 msgid "Unknown Year"
 msgstr "?????????????? ????????"
 
 #. I18N: Group for All items
-#: gui/launcher.cpp:1640 gui/widgets/groupedlist.cpp:82
+#: gui/launcher.cpp:1666 gui/widgets/groupedlist.cpp:82
 msgid "All"
 msgstr "??????"
 
-#: gui/launcher.cpp:1812
+#: gui/launcher.cpp:1844
 msgid "Icons per row:"
 msgstr "?????????????????? ?????? ??????????:"
 
-#: gui/launcher.cpp:1869
+#: gui/launcher.cpp:1901
 #, c-format
 msgid ""
 "Do you really want to remove the following %d game configurations?\n"
@@ -2232,54 +2232,54 @@ msgctxt "lowres"
 msgid "Speech volume:"
 msgstr "???????????? ??????????????:"
 
-#: gui/options.cpp:2266
+#: gui/options.cpp:2268
 msgid "Control"
 msgstr "??????????????????"
 
-#: gui/options.cpp:2379
+#: gui/options.cpp:2381
 msgctxt "lowres"
 msgid "Cloud"
 msgstr "??????????"
 
-#: gui/options.cpp:2394
+#: gui/options.cpp:2396
 msgid "LAN"
 msgstr "LAN"
 
-#: gui/options.cpp:2396
+#: gui/options.cpp:2398
 msgctxt "lowres"
 msgid "LAN"
 msgstr "LAN"
 
-#: gui/options.cpp:2405
+#: gui/options.cpp:2407
 msgid "Accessibility"
 msgstr "????????????????????????????"
 
-#: gui/options.cpp:2407
+#: gui/options.cpp:2409
 msgctxt "lowres"
 msgid "Accessibility"
 msgstr "????????????????????????????"
 
-#: gui/options.cpp:2416
+#: gui/options.cpp:2418
 msgid "Discard changes and close the dialog"
 msgstr "???????????????? ?????? ?????????????? ?????? ???????????????? ?????? ??????????????????"
 
-#: gui/options.cpp:2417
+#: gui/options.cpp:2419
 msgid "Apply"
 msgstr "????????????????"
 
-#: gui/options.cpp:2417
+#: gui/options.cpp:2419
 msgid "Apply changes without closing the dialog"
 msgstr "???????????????? ?????? ?????????????? ?????????? ???? ?????????????? ???? ????????????????"
 
-#: gui/options.cpp:2418
+#: gui/options.cpp:2420
 msgid "Apply changes and close the dialog"
 msgstr "???????????????? ?????? ?????????????? ?????? ???????????????? ?????? ??????????????????"
 
-#: gui/options.cpp:2508
+#: gui/options.cpp:2510
 msgid "FluidSynth Settings"
 msgstr "?????????????????? FluidSynth"
 
-#: gui/options.cpp:2522
+#: gui/options.cpp:2524
 msgid ""
 "Specifies where your saved games are put. A red coloring indicates the value "
 "is temporary and will not get saved"
@@ -2288,92 +2288,92 @@ msgstr ""
 "games). ?? ???????????????? ?????????????????????? ?????????????????????? ?????? ?? ???????? ???????? ?????????? ?????????????????? "
 "?????? ?????? ???? ??????????????????????"
 
-#: gui/options.cpp:2527
+#: gui/options.cpp:2529
 msgid "Theme Path:"
 msgstr "?????????????? ??????????????:"
 
-#: gui/options.cpp:2529
+#: gui/options.cpp:2531
 msgctxt "lowres"
 msgid "Theme Path:"
 msgstr "?????????????? ??????????????:"
 
-#: gui/options.cpp:2535
+#: gui/options.cpp:2537
 msgid "Icon Path:"
 msgstr "???????????????? ????????????????????:"
 
-#: gui/options.cpp:2537
+#: gui/options.cpp:2539
 msgctxt "lowres"
 msgid "Icon Path:"
 msgstr "???????????????? ????????????????????:"
 
-#: gui/options.cpp:2538 gui/options.cpp:2548
+#: gui/options.cpp:2540 gui/options.cpp:2550
 msgctxt "path"
 msgid "Default"
 msgstr "????????????????????????????"
 
-#: gui/options.cpp:2545
+#: gui/options.cpp:2547
 msgid "DLC Path:"
 msgstr "?????????????? ?????? DLC:"
 
-#: gui/options.cpp:2547
+#: gui/options.cpp:2549
 msgctxt "lowres"
 msgid "DLC Path:"
 msgstr "?????????????? ?????? DLC:"
 
-#: gui/options.cpp:2555 gui/options.cpp:2557 gui/options.cpp:2558
+#: gui/options.cpp:2557 gui/options.cpp:2559 gui/options.cpp:2560
 msgid "Specifies path to additional data used by all games or ScummVM"
 msgstr ""
 "?????????????????? ???? ???????????????? ?????? ???????????????? ???????????????? ?????? ???????????????????????????????? ?????? ?????? ???? "
 "?????????????????? ?? ???? ScummVM"
 
-#: gui/options.cpp:2564
+#: gui/options.cpp:2566
 msgid "Plugins Path:"
 msgstr "???????????????? ??????????????????:"
 
-#: gui/options.cpp:2566
+#: gui/options.cpp:2568
 msgctxt "lowres"
 msgid "Plugins Path:"
 msgstr "??????????. ??????????.:"
 
-#: gui/options.cpp:2576
+#: gui/options.cpp:2578
 msgid "ScummVM config path: "
 msgstr "???????????????? ??????????????????: "
 
-#: gui/options.cpp:2590
+#: gui/options.cpp:2592
 msgid "ScummVM log path: "
 msgstr "???????????????? ?????????????? ????????????????????: "
 
-#: gui/options.cpp:2595
+#: gui/options.cpp:2597
 msgid "View"
 msgstr "????????????????????"
 
 #. I18N: Referring to the last path memorized when adding a game
-#: gui/options.cpp:2605 gui/options.cpp:3389
+#: gui/options.cpp:2607 gui/options.cpp:3402
 msgid "Last browser path: "
 msgstr "???????????????? ???????????????? ????????????????: "
 
-#: gui/options.cpp:2610
+#: gui/options.cpp:2612
 msgid "Theme:"
 msgstr "????????:"
 
-#: gui/options.cpp:2615
+#: gui/options.cpp:2617
 msgid "GUI scale:"
 msgstr "?????????????????? ??????:"
 
-#: gui/options.cpp:2632
+#: gui/options.cpp:2634
 #, c-format
 msgid "%d%% - Custom"
 msgstr "%d%% - ??????????????????????????"
 
-#: gui/options.cpp:2636
+#: gui/options.cpp:2638
 msgid "GUI renderer:"
 msgstr "?????????????? ?????????????? ??????:"
 
-#: gui/options.cpp:2649
+#: gui/options.cpp:2651
 msgid "Return to the launcher when leaving a game"
 msgstr "?????????????????? ???????? ???????????????? (launcher) ???????? ?????? ?????????? ?????? ????????????????"
 
-#: gui/options.cpp:2650
+#: gui/options.cpp:2652
 msgid ""
 "Return to the launcher when leaving a game instead of closing ScummVM\n"
 "(this feature is not supported by all games)."
@@ -2382,42 +2382,58 @@ msgstr ""
 "????????????????, ???????? ???? ?????????????? ?? ???????????????? ?????? ScummVM.\n"
 "(?????????? ?????? ???????????????????? ?????? ?????? ???????????????????????? ?????? ???? ??????????????????)."
 
-#: gui/options.cpp:2657
+#: gui/options.cpp:2659
 msgid "Ask for confirmation on exit"
 msgstr "???? ???????????????? ?????????????????????? ???????? ?????? ??????????"
 
-#: gui/options.cpp:2658
+#: gui/options.cpp:2660
 msgid "Ask for permission when closing ScummVM or leaving a game."
 msgstr ""
 "???? ???????????????? ?????????????????????? ?????? ?????? ???????????? ???????? ???? ???????????????? ?????? ScummVM ?? ?????? "
 "?????????? ?????? ?????? ????????????????."
 
-#: gui/options.cpp:2664
+#: gui/options.cpp:2666
 msgid "Disable fixed font scaling"
 msgstr "?????????? ?????????????????? ?????? ???????????????????????????? ???????????????? ????????????????????"
 
-#: gui/options.cpp:2665
+#: gui/options.cpp:2667
+#, fuzzy
+#| msgid ""
+#| "Do not upscale fixed size fonts in the GUI. This reduces artefacts on low "
+#| "resolution screens"
 msgid ""
 "Do not upscale fixed size fonts in the GUI. This reduces artefacts on low "
-"resolution screens"
+"resolution screens."
 msgstr ""
 "???? ?????? ?????????????? ?????????????????? (upscale) ?????? ???????????????????????????? ???????????????? ???????????????? ?????? "
 "???????????????????? ???????????????? (GUI). ???????? ?????????????????????????? ???? \"??????????????????????\" (artefacts) "
 "???? ???????????? ?????????????? ????????????????"
 
-#: gui/options.cpp:2671
+#: gui/options.cpp:2673
+#, fuzzy
+#| msgid "Enable smooth scrolling when walking"
+msgid "Enable kinetic scrolling in lists"
+msgstr "???????????????????????? ???????????? ?????????????? ???????? ???? ??????????????????"
+
+#: gui/options.cpp:2674
+#, fuzzy
+#| msgid "Enable smooth scrolling when walking"
+msgid "Enable smooth, momentum-based scrolling in list widgets."
+msgstr "???????????????????????? ???????????? ?????????????? ???????? ???? ??????????????????"
+
+#: gui/options.cpp:2680
 msgid "GUI language:"
 msgstr "???????????? ??????:"
 
-#: gui/options.cpp:2671
+#: gui/options.cpp:2680
 msgid "Language of ScummVM GUI"
 msgstr "???????????? ?????? ???? ?????? (GUI) ?????? ScummVM"
 
-#: gui/options.cpp:2697
+#: gui/options.cpp:2706
 msgid "Switch the ScummVM GUI language to the game language"
 msgstr "?????????? ?????????????? ???????????????????? ???? ???????????? ?????? ???? ?????? (GUI) ?????? ScummVM"
 
-#: gui/options.cpp:2698
+#: gui/options.cpp:2707
 msgid ""
 "When starting a game, change the ScummVM GUI language to the game language. "
 "That way, if a game uses the ScummVM save and load dialogs, they are in the "
@@ -2428,11 +2444,11 @@ msgstr ""
 "???????????????????????? ???? ???????????????? ???????????????? ?????? ScummVM ?????? ???????????????????? ?????? ??????????????, "
 "???????? ???? ?????????? ???????? ???????? ???????????? ???? ???? ????????????????."
 
-#: gui/options.cpp:2710
+#: gui/options.cpp:2719
 msgid "Use native system file browser"
 msgstr "?????????? ?????? ???????????????? ?????????????????? ??????????????"
 
-#: gui/options.cpp:2711
+#: gui/options.cpp:2720
 msgid ""
 "Use the native system file browser instead of the ScummVM one to select a "
 "file or directory."
@@ -2440,50 +2456,50 @@ msgstr ""
 "?????????? ?????? ???????????????? ?????????????????? ?????????????? ?????? ?????????????? ???? ?????????????????????? ?????????????? ???????? "
 "?????????? ?????? ???? ScummVM ?????? ?????? ?????????????? ?????????????? ?? ??????????????????."
 
-#: gui/options.cpp:2718
+#: gui/options.cpp:2727
 msgid "Download Icons"
 msgstr "???????? ????????????????????"
 
-#: gui/options.cpp:2718
+#: gui/options.cpp:2727
 msgid "Check on the scummvm.org website for updates of icon packs"
 msgstr "?????????????? ???????? ???????????????? scummvm.org ?????? ?????????????????????? ???? ???????????? ????????????????????"
 
-#: gui/options.cpp:2724
+#: gui/options.cpp:2733
 msgid "Autosave:"
 msgstr "???????????????? ????????????????????:"
 
-#: gui/options.cpp:2726
+#: gui/options.cpp:2735
 msgctxt "lowres"
 msgid "Autosave:"
 msgstr "??????????. ????????.:"
 
-#: gui/options.cpp:2737
+#: gui/options.cpp:2746
 msgid "Random seed:"
 msgstr "?????????? ?????????????? ??????????????:"
 
-#: gui/options.cpp:2737
+#: gui/options.cpp:2746
 msgid "Seed for initializing all random number generators"
 msgstr "?????????? (seed) ?????? ?????? ???????????????????????? ???????? ?????? ???????????????????? ?????????????? ??????????????"
 
-#: gui/options.cpp:2745
+#: gui/options.cpp:2754
 msgid "Debug level:"
 msgstr "?????????????? ????????????????????????????:"
 
 #. I18N: Debug level -1, no messages
-#: gui/options.cpp:2749 gui/options.cpp:2886 audio/fmopl.cpp:83
+#: gui/options.cpp:2758 gui/options.cpp:2895 audio/fmopl.cpp:83
 msgid "None"
 msgstr "?????? ???????? ??????????????????????????"
 
 #. I18N: Debug level 11, all messages
-#: gui/options.cpp:2755
+#: gui/options.cpp:2764
 msgid "11 (all)"
 msgstr "11 (??????)"
 
-#: gui/options.cpp:2759
+#: gui/options.cpp:2768
 msgid "Enable Discord integration"
 msgstr "???????????????????????? ?????????????????????? ???? DIscord"
 
-#: gui/options.cpp:2760
+#: gui/options.cpp:2769
 msgid ""
 "Show information about the games you are playing on Discord if the Discord "
 "client is running."
@@ -2491,27 +2507,27 @@ msgstr ""
 "?????????????????? ?????? Discord ?????????????????????? ?????? ???? ?????????????????? ?????? ??????????????, ???? ???? "
 "?????????????????? Discord ????????????????????."
 
-#: gui/options.cpp:2770 gui/updates-dialog.cpp:87
+#: gui/options.cpp:2779 gui/updates-dialog.cpp:87
 msgid "Update check:"
 msgstr "?????????????? ????????????????????????:"
 
-#: gui/options.cpp:2770
+#: gui/options.cpp:2779
 msgid "How often to check ScummVM updates"
 msgstr "?????????????????? ?????????????? ?????????????????????? ?????? ScummVM"
 
-#: gui/options.cpp:2781
+#: gui/options.cpp:2790
 msgid "Check now"
 msgstr "?????????????? ????????"
 
-#: gui/options.cpp:2788
+#: gui/options.cpp:2797
 msgid "Active storage:"
 msgstr "???????????? ??????????????:"
 
-#: gui/options.cpp:2788
+#: gui/options.cpp:2797
 msgid "Active cloud storage"
 msgstr "???????????? ?????????????? ????????????"
 
-#: gui/options.cpp:2797
+#: gui/options.cpp:2806
 msgctxt "lowres"
 msgid ""
 "Storage is not yet enabled. Verify that username is correct and enable it:"
@@ -2519,202 +2535,202 @@ msgstr ""
 "?? ???????????????????? ?????????? ????????????????????????????????. ???????????????????????? ?????? ???? ?????????? ???????????? ?????????? "
 "?????????? ?????? ?????????????????????????? ????:"
 
-#: gui/options.cpp:2799
+#: gui/options.cpp:2808
 msgid ""
 "Storage is not yet enabled. Verify that username is correct and enable it:"
 msgstr ""
 "?? ???????????????????? ?????? ?????????? ?????????? ????????????????????????????. ???????????????????????? ?????? ???? ?????????? "
 "???????????? ?????????? ?????????? ?????? ?????????????????????????? ????:"
 
-#: gui/options.cpp:2800
+#: gui/options.cpp:2809
 msgid "Enable storage"
 msgstr "???????????????????????? ??????????????????????"
 
-#: gui/options.cpp:2800
+#: gui/options.cpp:2809
 msgid "Confirm you want to use this account for this storage"
 msgstr ""
 "???????????????????????? ?????? ???????????????????? ???? ?????????????????????????????? ?????????? ?????? ???????????????????? ?????? ???????? "
 "?????? ????????????????????"
 
-#: gui/options.cpp:2802 backends/platform/wii/options.cpp:113
+#: gui/options.cpp:2811 backends/platform/wii/options.cpp:113
 msgid "Username:"
 msgstr "?????????? ????????????:"
 
-#: gui/options.cpp:2802
+#: gui/options.cpp:2811
 msgid "Username used by this storage"
 msgstr "???? ?????????? ???????????? ?????? ???????????????????????? ???????? ?? ??????????????"
 
-#: gui/options.cpp:2803
+#: gui/options.cpp:2812
 msgid "<none>"
 msgstr "<?????? ???????? ??????????>"
 
-#: gui/options.cpp:2805
+#: gui/options.cpp:2814
 msgid "Used space:"
 msgstr "???????????????????????????????? ??????????:"
 
-#: gui/options.cpp:2805
+#: gui/options.cpp:2814
 msgid "Space used by ScummVM's saved games on this storage"
 msgstr ""
 "?????????? ?????? ?????????????????????????????? ?????? ???? ???????????????????????? ?????????????????? ?????? ScummVM ???? ???????? "
 "?????? ?????????????? ????????????"
 
-#: gui/options.cpp:2808
+#: gui/options.cpp:2817
 msgid "Last sync:"
 msgstr "?????????????????? ????????????????????????:"
 
-#: gui/options.cpp:2808
+#: gui/options.cpp:2817
 msgid "When was the last time saved games were synced with this storage"
 msgstr ""
 "???????? ???????????????????????????????? ?? ?????? ?????????????????? ???????????????????????? ?????????????????????????? ????????????????????"
 
-#: gui/options.cpp:2809 gui/options.cpp:3693
+#: gui/options.cpp:2818 gui/options.cpp:3706
 msgid "<never>"
 msgstr "<????????>"
 
-#: gui/options.cpp:2811
+#: gui/options.cpp:2820
 msgctxt "lowres"
 msgid "Saved games sync automatically on launch, after saving and on loading."
 msgstr ""
 "???? ???????????????????????? ?????????????????? ???????????????????????????? ???????????????? ???????? ?????? ????????????????, ???????? "
 "?????? ???????????????????? ?????? ???????? ???? ??????????????."
 
-#: gui/options.cpp:2813
+#: gui/options.cpp:2822
 msgid "Saved games sync automatically on launch, after saving and on loading."
 msgstr ""
 "???? ???????????????????????? ?????????????????? ???????????????????????????? ???????????????? ???????? ?????? ????????????????, ???????? "
 "?????? ???????????????????? ?????? ???????? ???? ??????????????."
 
-#: gui/options.cpp:2814
+#: gui/options.cpp:2823
 msgid "Sync now"
 msgstr "???????????????????????? ????????"
 
-#: gui/options.cpp:2814
+#: gui/options.cpp:2823
 msgid "Start saved games sync"
 msgstr "???????????????? ???????????????????????? ?????????????????????????? ????????????????????"
 
-#: gui/options.cpp:2817
+#: gui/options.cpp:2826
 msgctxt "lowres"
 msgid "You can download game files from your cloud ScummVM folder:"
 msgstr ""
 "???????????????? ???? ?????????????????????????? ???????????? ???????????????????? ?????? ?????? ???????? ?????? ???????????? ???????????? "
 "?????? ???? ScummVM:"
 
-#: gui/options.cpp:2819
+#: gui/options.cpp:2828
 msgid "You can download game files from your cloud ScummVM folder:"
 msgstr ""
 "???????????????? ???? ?????????????????????????? ???????????? ???????????????????? ?????? ?????? ???????? ?????? ???????????? ???????????? "
 "?????? ???? ScummVM:"
 
-#: gui/options.cpp:2820
+#: gui/options.cpp:2829
 msgid "Download game files"
 msgstr "?????????????????? ???????????? ????????????????????"
 
-#: gui/options.cpp:2820
+#: gui/options.cpp:2829
 msgid "Open downloads manager dialog"
 msgstr "?????????????? ???????????????? ?????????????????????? ??????????????????????????"
 
-#: gui/options.cpp:2823
+#: gui/options.cpp:2832
 msgctxt "lowres"
 msgid "To change account for this storage, disconnect and connect again:"
 msgstr ""
 "?????? ???? ???????????????? ???? ???????????????????? ?????? ???????? ?????? ??????????????, ?????????????????????????? ?????? "
 "???????????????????? ???? ????????:"
 
-#: gui/options.cpp:2825
+#: gui/options.cpp:2834
 msgid "To change account for this storage, disconnect and connect again:"
 msgstr ""
 "?????? ???? ???????????????? ???? ???????????????????? ?????? ???????? ?????? ??????????????, ?????????????????????????? ?????? "
 "???????????????????? ???? ????????:"
 
-#: gui/options.cpp:2826
+#: gui/options.cpp:2835
 msgid "Disconnect"
 msgstr "????????????????????"
 
-#: gui/options.cpp:2826
+#: gui/options.cpp:2835
 msgid "Stop using this storage on this device"
 msgstr "?????????????? ???????????? ?????????? ?????? ???????????????? ???? ???????? ???? ??????????????"
 
-#: gui/options.cpp:2829
+#: gui/options.cpp:2838
 msgctxt "lowres"
 msgid "This storage is not connected yet!"
 msgstr "?? ?????????????? ???????? ?????? ?????????? ?????????? ??????????????????????!"
 
-#: gui/options.cpp:2831
+#: gui/options.cpp:2840
 msgid "This storage is not connected yet!"
 msgstr "?? ?????????????? ???????? ?????? ?????????? ?????????? ??????????????????????!"
 
-#: gui/options.cpp:2832
+#: gui/options.cpp:2841
 msgid "Connect"
 msgstr "??????????????"
 
-#: gui/options.cpp:2832
+#: gui/options.cpp:2841
 msgid "Connect your cloud storage account"
 msgstr "???????????????? ?????? ???????????????????? ?????? ?????????? ?????? ???????????????? ?????? ??????????"
 
-#: gui/options.cpp:2845
+#: gui/options.cpp:2854
 msgctxt "lowres"
 msgid "/root/ Path:"
 msgstr "/root/ ????????????????:"
 
-#: gui/options.cpp:2845 gui/options.cpp:2847 gui/options.cpp:2848
+#: gui/options.cpp:2854 gui/options.cpp:2856 gui/options.cpp:2857
 msgid "Select which directory will be shown as /root/ in the Files Manager"
 msgstr ""
 "???????????????? ???? ???????????? ?????? ???? ?????????????????????? ???? ?????????????????? /root/ ?????? ???????????????????? "
 "??????????????"
 
-#: gui/options.cpp:2847
+#: gui/options.cpp:2856
 msgid "/root/ Path:"
 msgstr "/root/ ????????????????:"
 
-#: gui/options.cpp:2853
+#: gui/options.cpp:2862
 msgid "Server's port:"
 msgstr "???????? ????????????????????:"
 
-#: gui/options.cpp:2853
+#: gui/options.cpp:2862
 msgid "Port for server to use"
 msgstr "?? ???????? (port) ?????? ???? ???????????????????????? ?? ??????????????????????"
 
-#: gui/options.cpp:2858
+#: gui/options.cpp:2867
 msgctxt "lowres"
 msgid "Run server to manage files with browser (in the same network)."
 msgstr ""
 "?????????????????? ?????? ???????????????????? ?????? ???? ???????????????????????????? ???????????? ???? ???????????? browser "
 "(?????????? ?????? ?????????? ??????????????)."
 
-#: gui/options.cpp:2859
+#: gui/options.cpp:2868
 msgctxt "lowres"
 msgid "Closing options dialog will stop the server."
 msgstr "???????????????????? ???? ?????????? ???????????????? ???? ???????????????????? ?? ??????????????????????."
 
-#: gui/options.cpp:2861
+#: gui/options.cpp:2870
 msgid "Run server to manage files with browser (in the same network)."
 msgstr ""
 "?????????????????? ?????? ???????????????????? ?????? ???? ???????????????????????????? ???????????? ???? ???????????? browser "
 "(?????????? ?????? ?????????? ??????????????)."
 
-#: gui/options.cpp:2862
+#: gui/options.cpp:2871
 msgid "Closing options dialog will stop the server."
 msgstr "???????????????????? ???? ?????????? ???????????????? ???? ???????????????????? ?? ??????????????????????."
 
-#: gui/options.cpp:2873
+#: gui/options.cpp:2882
 msgid "Use Text to speech"
 msgstr "?????????????????? ???????????????? ???? ????????????"
 
-#: gui/options.cpp:2873
+#: gui/options.cpp:2882
 msgid "Will read text in gui on mouse over."
 msgstr ""
 "???? ???????????????? ???? ?????????????? ?????? ?????????????????? ???????? ?????? ???? ?????????????? ?????? ?????????????? ?????????????? "
 "???????????? (?????? ?? ???????????? GUI)."
 
-#: gui/options.cpp:2954
+#: gui/options.cpp:2963
 msgid "Ignore"
 msgstr "??????????????"
 
-#: gui/options.cpp:2955
+#: gui/options.cpp:2964
 msgid "Disable autosave"
 msgstr "?????????? ???????????????? ????????????????????"
 
-#: gui/options.cpp:2956
+#: gui/options.cpp:2965
 msgid ""
 "WARNING: Autosave was enabled. Some of your games have existing saved games "
 "on the autosave slot. You can either move the existing saves to new slots, "
@@ -2730,7 +2746,7 @@ msgstr ""
 "???????????????????????????? ???????????? ?????????????? ???????????????????????? ????????????????).\n"
 "?????????? ????????????????????:\n"
 
-#: gui/options.cpp:2965
+#: gui/options.cpp:2974
 msgid ""
 "\n"
 "And more..."
@@ -2738,34 +2754,34 @@ msgstr ""
 "\n"
 "?????? ????????..."
 
-#: gui/options.cpp:2966 engines/engine.cpp:647
+#: gui/options.cpp:2975 engines/engine.cpp:647
 #: engines/access/metaengine.cpp:298 engines/drascula/metaengine.cpp:255
 #: engines/queen/metaengine.cpp:269 engines/sherlock/metaengine.cpp:410
 #: engines/ultima/nuvie/metaengine.cpp:71
 msgid "Move"
 msgstr "????????????????????"
 
-#: gui/options.cpp:2985
+#: gui/options.cpp:2994
 msgid "ERROR: Failed to move the following saved games:\n"
 msgstr "????????????: ???????????????? ?????????????????????? ?????? ???????????????? ???????????????????????? ????????????????????:\n"
 
-#: gui/options.cpp:3081
+#: gui/options.cpp:3090
 msgid "Failed to change cloud storage!"
 msgstr "???????????????? ?????????????? ???????????????? ????????????!"
 
-#: gui/options.cpp:3084
+#: gui/options.cpp:3093
 msgid "Another cloud storage is already active."
 msgstr "?????? ???????? ?????????????? ???????????? ?????????? ?????? ????????????."
 
-#: gui/options.cpp:3199
+#: gui/options.cpp:3212
 msgid "Theme does not support selected language!"
 msgstr "???? ???????? ?????? ?????????????????????? ?????? ???????????????????? ????????????!"
 
-#: gui/options.cpp:3202
+#: gui/options.cpp:3215
 msgid "Theme cannot be loaded!"
 msgstr "???? ???????? ?????? ??????????????????!"
 
-#: gui/options.cpp:3205
+#: gui/options.cpp:3218
 msgid ""
 "\n"
 "Misc settings will be restored."
@@ -2773,49 +2789,49 @@ msgstr ""
 "\n"
 "?????????????????? ?????????????? ?????????? ???????? ???????????? ??????????????????."
 
-#: gui/options.cpp:3277
+#: gui/options.cpp:3290
 msgid "Select directory for GUI themes"
 msgstr ""
 "???????????????? ???????????? ?????????????? (themes) ?????? ???? ?????????????? ?????????????? ???????????? (?????? ?? ???????????? "
 "GUI)"
 
-#: gui/options.cpp:3287
+#: gui/options.cpp:3300
 msgid "Select directory for GUI launcher thumbnails"
 msgstr ""
 "???????????????? ???????????? ?????? ???? ?????????????????? ???????????????????? ?????? ???? ???????????????????????? ?????? ?????????????? "
 "?????????????? ???????????? (?????? ?? ???????????? GUI) ?????? ???????????????? (launcher)"
 
-#: gui/options.cpp:3298
+#: gui/options.cpp:3311
 msgid "Select directory for DLC downloads"
 msgstr "???????????????? ???????????? ?????? DLC"
 
-#: gui/options.cpp:3309
+#: gui/options.cpp:3322
 msgid "Select directory for extra files"
 msgstr "???????????????? ???????????? ?????? ???????????????? ????????????"
 
-#: gui/options.cpp:3320
+#: gui/options.cpp:3333
 msgid "Select directory for plugins"
 msgstr "???????????????? ???????????? ?????? ????????????????"
 
-#: gui/options.cpp:3333
+#: gui/options.cpp:3346
 msgid "Select directory for Files Manager /root/"
 msgstr "???????????????? ???????????? ?????? ???? /root/ ?????? ?????????????????????? ??????????????"
 
-#: gui/options.cpp:3485
+#: gui/options.cpp:3498
 msgid "This Storage is working now. Do you want to interrupt it?"
 msgstr ""
 "???????? ?? ?????????????? ???????????? ???????? ?????????????? ???? ?????????????? ???????? ???? ????????????. ???????????????????? ???? "
 "???? ??????????????????;"
 
-#: gui/options.cpp:3494
+#: gui/options.cpp:3507
 msgid "Wait until current Storage finishes up and try again."
 msgstr "???????????????????? ???? ?????????????????????? ?? ???????????????? ???????????????????? ?????? ?????????????????? ????????."
 
-#: gui/options.cpp:3691 backends/cloud/cloudmanager.cpp:157
+#: gui/options.cpp:3704 backends/cloud/cloudmanager.cpp:157
 msgid "<right now>"
 msgstr "<??????????>"
 
-#: gui/options.cpp:3834
+#: gui/options.cpp:3847
 msgid ""
 "Request failed.\n"
 "Check your Internet connection."
@@ -2823,7 +2839,7 @@ msgstr ""
 "???? ???????????? ??????????????.\n"
 "?????????????? ???? ?????????????? ?????? ?????? ??????????????????."
 
-#: gui/options.cpp:3857
+#: gui/options.cpp:3870
 msgid ""
 "A test pattern should be displayed.\n"
 "Do you want to keep these shader scaler settings?"
@@ -2831,7 +2847,7 @@ msgstr ""
 "???? ?????????????????????? ???????????? ?????? ?????????????????????? ???????????????? ?????????????????????????? ?????? ??????????????.\n"
 "???????????? ???? ?????????????????????? ?????? ???????? ?????????????????? ?????????????? ???????????????????? (shader scaler);"
 
-#: gui/options.cpp:3860
+#: gui/options.cpp:3873
 #, c-format
 msgid "Reverting automatically in %d seconds"
 msgstr "???????????????? ?????????????????? ?????? ???????????????????????? ?????????????????? ???? %d ????????????????????????"
@@ -2916,7 +2932,7 @@ msgstr "??????????????"
 msgid "Playback"
 msgstr "??????????????????????"
 
-#: gui/recorderdialog.cpp:76 gui/widgets/grid.cpp:341
+#: gui/recorderdialog.cpp:76 gui/widgets/grid.cpp:343
 msgid "Edit"
 msgstr "??????????????????????"
 
@@ -3131,11 +3147,11 @@ msgstr ""
 msgid "Clear value"
 msgstr "?????????????? ??????????"
 
-#: gui/widgets/grid.cpp:335
+#: gui/widgets/grid.cpp:337
 msgid "Play"
 msgstr "??????????"
 
-#: gui/widgets/grid.cpp:338
+#: gui/widgets/grid.cpp:340
 msgid "Saves"
 msgstr "???????????????????????? ??????????????????"
 
diff --git a/po/es_ES.po b/po/es_ES.po
index 0bd39446d63..0ebcf5c019d 100644
--- a/po/es_ES.po
+++ b/po/es_ES.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.4.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-13 10:20+0000\n"
+"POT-Creation-Date: 2026-06-13 13:52+0000\n"
 "PO-Revision-Date: 2026-06-13 11:38+0000\n"
 "Last-Translator: IlDucci <pajaroloco_2 at hotmail.com>\n"
 "Language-Team: Spanish <https://translations.scummvm.org/projects/scummvm/"
@@ -116,7 +116,7 @@ msgstr "Arriba"
 #: gui/downloadpacksdialog.cpp:465 gui/editgamedialog.cpp:340
 #: gui/editrecorddialog.cpp:66 gui/integrity-dialog.cpp:122
 #: gui/integrity-dialog.cpp:253 gui/filebrowser-dialog.cpp:68
-#: gui/fluidsynth-dialog.cpp:153 gui/massadd.cpp:93 gui/options.cpp:2416
+#: gui/fluidsynth-dialog.cpp:153 gui/massadd.cpp:93 gui/options.cpp:2418
 #: gui/predictivedialog.cpp:68 gui/printing-dialog.cpp:96
 #: gui/recorderdialog.cpp:72 gui/recorderdialog.cpp:211
 #: gui/remotebrowser.cpp:59 gui/saveload-dialog.cpp:64
@@ -187,23 +187,23 @@ msgstr ""
 "En este modo debes tener en marcha el servidor web\n"
 "para que tu navegador pueda enviar datos a ScummVM"
 
-#: gui/cloudconnectionwizard.cpp:257 gui/options.cpp:3776
+#: gui/cloudconnectionwizard.cpp:257 gui/options.cpp:3789
 msgid "Stop server"
 msgstr "Detener servidor"
 
-#: gui/cloudconnectionwizard.cpp:257 gui/options.cpp:2840
+#: gui/cloudconnectionwizard.cpp:257 gui/options.cpp:2849
 msgid "Run server"
 msgstr "Habilitar servidor"
 
-#: gui/cloudconnectionwizard.cpp:258 gui/options.cpp:3777
+#: gui/cloudconnectionwizard.cpp:258 gui/options.cpp:3790
 msgid "Stop local webserver"
 msgstr "Detiene el servidor local"
 
-#: gui/cloudconnectionwizard.cpp:258 gui/options.cpp:2840
+#: gui/cloudconnectionwizard.cpp:258 gui/options.cpp:2849
 msgid "Run local webserver"
 msgstr "Arranca el servidor local"
 
-#: gui/cloudconnectionwizard.cpp:266 gui/options.cpp:2841 gui/options.cpp:3784
+#: gui/cloudconnectionwizard.cpp:266 gui/options.cpp:2850 gui/options.cpp:3797
 msgid "Not running"
 msgstr "Detenido"
 
@@ -330,8 +330,8 @@ msgstr "Ya hay otro almacenamiento activo. ??Quieres interrumpirlo?"
 #: gui/cloudconnectionwizard.cpp:530 gui/downloaddialog.cpp:118
 #: gui/downloaddialog.cpp:152 gui/filebrowser-dialog.cpp:135
 #: gui/fluidsynth-dialog.cpp:210 gui/launcher.cpp:428 gui/launcher.cpp:457
-#: gui/launcher.cpp:534 gui/launcher.cpp:1938 gui/options.cpp:3485
-#: gui/options.cpp:3859 base/main.cpp:608
+#: gui/launcher.cpp:534 gui/launcher.cpp:1970 gui/options.cpp:3498
+#: gui/options.cpp:3872 base/main.cpp:608
 #: backends/events/default/default-events.cpp:193 engines/buried/buried.cpp:598
 #: engines/chamber/metaengine.cpp:91 engines/director/events.cpp:122
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:139
@@ -350,8 +350,8 @@ msgstr "S??"
 #: gui/cloudconnectionwizard.cpp:530 gui/downloaddialog.cpp:118
 #: gui/downloaddialog.cpp:153 gui/filebrowser-dialog.cpp:135
 #: gui/fluidsynth-dialog.cpp:210 gui/launcher.cpp:428 gui/launcher.cpp:457
-#: gui/launcher.cpp:534 gui/launcher.cpp:1939 gui/options.cpp:3485
-#: gui/options.cpp:3859 engines/buried/buried.cpp:598
+#: gui/launcher.cpp:534 gui/launcher.cpp:1971 gui/options.cpp:3498
+#: gui/options.cpp:3872 engines/buried/buried.cpp:598
 #: engines/chamber/metaengine.cpp:97 engines/director/events.cpp:122
 #: engines/director/lingo/xlibs/j/jitdraw3.cpp:139
 #: engines/efh/metaengine.cpp:368 engines/grim/grim.cpp:366
@@ -378,7 +378,7 @@ msgstr "Los contenidos del c??digo JSON no son v??lidos."
 #: gui/editrecorddialog.cpp:67 gui/integrity-dialog.cpp:260
 #: gui/fluidsynth-dialog.cpp:154 gui/massadd.cpp:90 gui/message.cpp:156
 #: gui/message.cpp:160 gui/message.cpp:231 gui/message.cpp:270
-#: gui/message.cpp:274 gui/options.cpp:2418 gui/predictivedialog.cpp:69
+#: gui/message.cpp:274 gui/options.cpp:2420 gui/predictivedialog.cpp:69
 #: gui/saveload-dialog.cpp:1244 base/main.cpp:675 base/main.cpp:700
 #: dists/android.strings.xml.cpp:33 engines/engine.cpp:846
 #: backends/platform/wii/options.cpp:46 engines/buried/buried.cpp:622
@@ -569,11 +569,11 @@ msgstr ""
 msgid "Proceed"
 msgstr "Aplicar"
 
-#: gui/dump-all-dialogs.cpp:123 gui/options.cpp:3347
+#: gui/dump-all-dialogs.cpp:123 gui/options.cpp:3360
 msgid "icon packs"
 msgstr "paquetes de iconos"
 
-#: gui/dump-all-dialogs.cpp:127 gui/options.cpp:3358
+#: gui/dump-all-dialogs.cpp:127 gui/options.cpp:3371
 msgid "shader packs"
 msgstr "paquetes de shaders"
 
@@ -585,11 +585,11 @@ msgstr "Elige el juego:"
 msgid "Game"
 msgstr "Juego"
 
-#: gui/editgamedialog.cpp:164 gui/options.cpp:2075 gui/options.cpp:2254
+#: gui/editgamedialog.cpp:164 gui/options.cpp:2075 gui/options.cpp:2256
 msgid "GFX"
 msgstr "Gr??ficos"
 
-#: gui/editgamedialog.cpp:164 gui/options.cpp:2075 gui/options.cpp:2254
+#: gui/editgamedialog.cpp:164 gui/options.cpp:2075 gui/options.cpp:2256
 #: backends/graphics/atari/atari-graphics.cpp:1056
 #: backends/graphics/sdl/sdl-graphics.cpp:487
 msgid "Graphics"
@@ -604,11 +604,11 @@ msgctxt "lowres"
 msgid "Override global graphic settings"
 msgstr "Opciones gr??ficas generales"
 
-#: gui/editgamedialog.cpp:185 gui/options.cpp:2288 engines/dialogs.cpp:354
+#: gui/editgamedialog.cpp:185 gui/options.cpp:2290 engines/dialogs.cpp:354
 msgid "Keymaps"
 msgstr "Asignaciones"
 
-#: gui/editgamedialog.cpp:197 gui/options.cpp:2300 engines/dialogs.cpp:366
+#: gui/editgamedialog.cpp:197 gui/options.cpp:2302 engines/dialogs.cpp:366
 msgid "Backend"
 msgstr "Back-end"
 
@@ -621,7 +621,7 @@ msgctxt "lowres"
 msgid "Override global backend settings"
 msgstr "Opciones generales del back-end"
 
-#: gui/editgamedialog.cpp:220 gui/options.cpp:2318 engines/dialogs.cpp:329
+#: gui/editgamedialog.cpp:220 gui/options.cpp:2320 engines/dialogs.cpp:329
 msgid "Audio"
 msgstr "Sonido"
 
@@ -634,11 +634,11 @@ msgctxt "lowres"
 msgid "Override global audio settings"
 msgstr "Opciones de sonido espec??ficas"
 
-#: gui/editgamedialog.cpp:234 gui/options.cpp:2323
+#: gui/editgamedialog.cpp:234 gui/options.cpp:2325
 msgid "Volume"
 msgstr "Volumen"
 
-#: gui/editgamedialog.cpp:236 gui/options.cpp:2325
+#: gui/editgamedialog.cpp:236 gui/options.cpp:2327
 msgctxt "lowres"
 msgid "Volume"
 msgstr "Volumen"
@@ -652,7 +652,7 @@ msgctxt "lowres"
 msgid "Override global volume settings"
 msgstr "Opciones de volumen espec??ficas"
 
-#: gui/editgamedialog.cpp:252 gui/options.cpp:2333
+#: gui/editgamedialog.cpp:252 gui/options.cpp:2335
 msgid "MIDI"
 msgstr "MIDI"
 
@@ -665,7 +665,7 @@ msgctxt "lowres"
 msgid "Override global MIDI settings"
 msgstr "Opciones de MIDI espec??ficas"
 
-#: gui/editgamedialog.cpp:267 gui/options.cpp:2339
+#: gui/editgamedialog.cpp:267 gui/options.cpp:2341
 msgid "MT-32"
 msgstr "MT-32"
 
@@ -678,11 +678,11 @@ msgctxt "lowres"
 msgid "Override global MT-32 settings"
 msgstr "Opciones de MT-32 espec??ficas"
 
-#: gui/editgamedialog.cpp:281 gui/options.cpp:2346
+#: gui/editgamedialog.cpp:281 gui/options.cpp:2348
 msgid "Paths"
 msgstr "Rutas"
 
-#: gui/editgamedialog.cpp:283 gui/options.cpp:2348
+#: gui/editgamedialog.cpp:283 gui/options.cpp:2350
 msgctxt "lowres"
 msgid "Paths"
 msgstr "Rutas"
@@ -704,7 +704,7 @@ msgctxt "lowres"
 msgid "Game Path:"
 msgstr "Juego:"
 
-#: gui/editgamedialog.cpp:303 gui/options.cpp:2555
+#: gui/editgamedialog.cpp:303 gui/options.cpp:2557
 msgid "Extra Path:"
 msgstr "Adicional:"
 
@@ -713,33 +713,33 @@ msgstr "Adicional:"
 msgid "Specifies path to additional data used by the game"
 msgstr "Especifica un directorio para datos adicionales del juego"
 
-#: gui/editgamedialog.cpp:305 gui/options.cpp:2557
+#: gui/editgamedialog.cpp:305 gui/options.cpp:2559
 msgctxt "lowres"
 msgid "Extra Path:"
 msgstr "Adicional:"
 
-#: gui/editgamedialog.cpp:306 gui/options.cpp:2530 gui/options.cpp:2558
-#: gui/options.cpp:2567 gui/options.cpp:2848
+#: gui/editgamedialog.cpp:306 gui/options.cpp:2532 gui/options.cpp:2560
+#: gui/options.cpp:2569 gui/options.cpp:2857
 #: backends/platform/libretro/src/libretro-options-widget.cpp:277
 msgctxt "path"
 msgid "None"
 msgstr "Ninguna"
 
-#: gui/editgamedialog.cpp:312 gui/options.cpp:2519
+#: gui/editgamedialog.cpp:312 gui/options.cpp:2521
 msgid "Save Path:"
 msgstr "Partidas:"
 
 #: gui/editgamedialog.cpp:312 gui/editgamedialog.cpp:314
-#: gui/editgamedialog.cpp:315 gui/options.cpp:2519 gui/options.cpp:2521
+#: gui/editgamedialog.cpp:315 gui/options.cpp:2521 gui/options.cpp:2523
 msgid "Specifies where your saved games are put"
 msgstr "Especifica d??nde se guardar??n tus partidas"
 
-#: gui/editgamedialog.cpp:314 gui/options.cpp:2521
+#: gui/editgamedialog.cpp:314 gui/options.cpp:2523
 msgctxt "lowres"
 msgid "Save Path:"
 msgstr "Partidas:"
 
-#: gui/editgamedialog.cpp:315 gui/options.cpp:1844 gui/options.cpp:2522
+#: gui/editgamedialog.cpp:315 gui/options.cpp:1844 gui/options.cpp:2524
 #: backends/platform/wii/options.cpp:55
 msgid "Default"
 msgstr "Por defecto"
@@ -800,8 +800,8 @@ msgstr ""
 #: gui/editgamedialog.cpp:365 gui/editgamedialog.cpp:380 gui/options.cpp:1613
 #: gui/options.cpp:1627 gui/options.cpp:1644 gui/options.cpp:1658
 #: gui/options.cpp:1675 gui/options.cpp:1713 gui/options.cpp:1729
-#: gui/options.cpp:2144 gui/options.cpp:2600 gui/options.cpp:2674
-#: gui/options.cpp:2888 gui/options.cpp:3389 audio/null.cpp:45
+#: gui/options.cpp:2144 gui/options.cpp:2602 gui/options.cpp:2683
+#: gui/options.cpp:2897 gui/options.cpp:3402 audio/null.cpp:45
 #: backends/platform/android/options.cpp:157
 #: backends/platform/android/options.cpp:158
 #: backends/platform/android/options.cpp:186
@@ -828,7 +828,7 @@ msgctxt "lowres"
 msgid "Platform:"
 msgstr "Plataforma:"
 
-#: gui/editgamedialog.cpp:566 gui/options.cpp:3413
+#: gui/editgamedialog.cpp:566 gui/options.cpp:3426
 msgid "Select SoundFont"
 msgstr "Selecciona una SoundFont"
 
@@ -836,11 +836,11 @@ msgstr "Selecciona una SoundFont"
 msgid "Select additional game directory"
 msgstr "Selecciona el directorio adicional"
 
-#: gui/editgamedialog.cpp:618 gui/options.cpp:3261
+#: gui/editgamedialog.cpp:618 gui/options.cpp:3274
 msgid "Select directory for saved games"
 msgstr "Selecciona el directorio para partidas guardadas"
 
-#: gui/editgamedialog.cpp:625 gui/options.cpp:3268
+#: gui/editgamedialog.cpp:625 gui/options.cpp:3281
 msgid "The chosen directory cannot be written to. Please select another one."
 msgstr "No se puede escribir en el directorio elegido. Selecciona otro."
 
@@ -991,7 +991,7 @@ msgstr "Seno"
 msgid "Triangle"
 msgstr "Tri??ngulo"
 
-#: gui/fluidsynth-dialog.cpp:139 gui/options.cpp:2366
+#: gui/fluidsynth-dialog.cpp:139 gui/options.cpp:2368
 msgid "Misc"
 msgstr "Otras"
 
@@ -1031,7 +1031,7 @@ msgstr ""
 "??Seguro que quieres volver a los valores por defecto de las opciones de "
 "FluidSynth?"
 
-#: gui/gui-manager.cpp:159 gui/options.cpp:2357
+#: gui/gui-manager.cpp:159 gui/options.cpp:2359
 msgid "GUI"
 msgstr "Interfaz"
 
@@ -1279,7 +1279,7 @@ msgstr ""
 "p??ginas de subastas. Ten cuidado con los juegos en mal estado y las copias "
 "ilegales.\n"
 
-#: gui/helpdialog.cpp:68 gui/options.cpp:2377
+#: gui/helpdialog.cpp:68 gui/options.cpp:2379
 msgid "Cloud"
 msgstr "Nube"
 
@@ -1624,85 +1624,85 @@ msgstr "Modo lista"
 msgid "Grid view"
 msgstr "Modo mosaico"
 
-#: gui/launcher.cpp:1145
+#: gui/launcher.cpp:1168
 msgid "~S~tart"
 msgstr "~J~ugar"
 
-#: gui/launcher.cpp:1145
+#: gui/launcher.cpp:1168
 msgid "Start selected game"
 msgstr "Ejecuta el juego seleccionado"
 
-#: gui/launcher.cpp:1148
+#: gui/launcher.cpp:1171
 msgid "~L~oad..."
 msgstr "~C~argar..."
 
-#: gui/launcher.cpp:1148
+#: gui/launcher.cpp:1171
 msgid "Load saved game for selected game"
 msgstr "Carga una partida del juego seleccionado"
 
-#: gui/launcher.cpp:1150
+#: gui/launcher.cpp:1173
 msgid "Record..."
 msgstr "Grabar..."
 
-#: gui/launcher.cpp:1156
+#: gui/launcher.cpp:1179
 msgid "~G~ame Options..."
 msgstr "Opciones del j~u~ego..."
 
-#: gui/launcher.cpp:1156
+#: gui/launcher.cpp:1179
 msgid "Change game options"
 msgstr "Cambia las opciones del juego"
 
-#: gui/launcher.cpp:1156
+#: gui/launcher.cpp:1179
 msgctxt "lowres"
 msgid "~G~ame Opts..."
 msgstr "Opc. j~u~ego..."
 
-#: gui/launcher.cpp:1242
+#: gui/launcher.cpp:1265
 msgid "Add-on"
 msgstr "Expansiones"
 
 #. I18N: List grouping when no engine is specified
-#: gui/launcher.cpp:1309 gui/launcher.cpp:1554
+#: gui/launcher.cpp:1335 gui/launcher.cpp:1580
 msgid "Unknown Engine"
 msgstr "Motor desconocido"
 
 #. I18N: List grouping when no publisher is specified
 #. I18N: List group when no publisher is specified
-#: gui/launcher.cpp:1326 gui/launcher.cpp:1571
+#: gui/launcher.cpp:1352 gui/launcher.cpp:1597
 msgid "Unknown Publisher"
 msgstr "Distribuidora desconocida"
 
 #. I18N: List group when no game series is specified
 #. I18N: List grouping when no game series is specified
-#: gui/launcher.cpp:1343 gui/launcher.cpp:1588
+#: gui/launcher.cpp:1369 gui/launcher.cpp:1614
 msgid "No Series"
 msgstr "No hay series"
 
 #. I18N: List group when no language is specified
-#: gui/launcher.cpp:1357 gui/launcher.cpp:1602
+#: gui/launcher.cpp:1383 gui/launcher.cpp:1628
 msgid "Language not detected"
 msgstr "Idioma no detectado"
 
 #. I18N: List group when no platform is specified
-#: gui/launcher.cpp:1371 gui/launcher.cpp:1616
+#: gui/launcher.cpp:1397 gui/launcher.cpp:1642
 msgid "Platform not detected"
 msgstr "Plataforma no detectada"
 
 #. I18N: List group when no year is specified
-#: gui/launcher.cpp:1388 gui/launcher.cpp:1633
+#: gui/launcher.cpp:1414 gui/launcher.cpp:1659
 msgid "Unknown Year"
 msgstr "A??o desconocido"
 
 #. I18N: Group for All items
-#: gui/launcher.cpp:1640 gui/widgets/groupedlist.cpp:82
+#: gui/launcher.cpp:1666 gui/widgets/groupedlist.cpp:82
 msgid "All"
 msgstr "Todo"
 
-#: gui/launcher.cpp:1812
+#: gui/launcher.cpp:1844
 msgid "Icons per row:"
 msgstr "Iconos por fila:"
 
-#: gui/launcher.cpp:1869
+#: gui/launcher.cpp:1901
 #, c-format
 msgid ""
 "Do you really want to remove the following %d game configurations?\n"
@@ -2204,54 +2204,54 @@ msgctxt "lowres"
 msgid "Speech volume:"
 msgstr "Voces:"
 
-#: gui/options.cpp:2266
+#: gui/options.cpp:2268
 msgid "Control"
 msgstr "Controles"
 
-#: gui/options.cpp:2379
+#: gui/options.cpp:2381
 msgctxt "lowres"
 msgid "Cloud"
 msgstr "Nube"
 
-#: gui/options.cpp:2394
+#: gui/options.cpp:2396
 msgid "LAN"
 msgstr "Red local"
 
-#: gui/options.cpp:2396
+#: gui/options.cpp:2398
 msgctxt "lowres"
 msgid "LAN"
 msgstr "Red local"
 
-#: gui/options.cpp:2405
+#: gui/options.cpp:2407
 msgid "Accessibility"
 msgstr "Accesibilidad"
 
-#: gui/options.cpp:2407
+#: gui/options.cpp:2409
 msgctxt "lowres"
 msgid "Accessibility"
 msgstr "Accesibilidad"
 
-#: gui/options.cpp:2416
+#: gui/options.cpp:2418
 msgid "Discard changes and close the dialog"
 msgstr "Descarta los cambios y cierra la ventana"
 
-#: gui/options.cpp:2417
+#: gui/options.cpp:2419
 msgid "Apply"
 msgstr "Aplicar"
 
-#: gui/options.cpp:2417
+#: gui/options.cpp:2419
 msgid "Apply changes without closing the dialog"
 msgstr "Aplica los cambios sin cerrar la ventana"
 
-#: gui/options.cpp:2418
+#: gui/options.cpp:2420
 msgid "Apply changes and close the dialog"
 msgstr "Aplica los cambios y cierra la ventana"
 
-#: gui/options.cpp:2508
+#: gui/options.cpp:2510
 msgid "FluidSynth Settings"
 msgstr "Opciones de FluidSynth"
 
-#: gui/options.cpp:2522
+#: gui/options.cpp:2524
 msgid ""
 "Specifies where your saved games are put. A red coloring indicates the value "
 "is temporary and will not get saved"
@@ -2259,90 +2259,90 @@ msgstr ""
 "Especifca el lugar donde ir??n tus partidas guardadas. Si el texto es de "
 "color rojo, es que el valor es temporal y no se guardar??"
 
-#: gui/options.cpp:2527
+#: gui/options.cpp:2529
 msgid "Theme Path:"
 msgstr "Temas:"
 
-#: gui/options.cpp:2529
+#: gui/options.cpp:2531
 msgctxt "lowres"
 msgid "Theme Path:"
 msgstr "Temas:"
 
-#: gui/options.cpp:2535
+#: gui/options.cpp:2537
 msgid "Icon Path:"
 msgstr "Iconos:"
 
-#: gui/options.cpp:2537
+#: gui/options.cpp:2539
 msgctxt "lowres"
 msgid "Icon Path:"
 msgstr "Iconos:"
 
-#: gui/options.cpp:2538 gui/options.cpp:2548
+#: gui/options.cpp:2540 gui/options.cpp:2550
 msgctxt "path"
 msgid "Default"
 msgstr "Por defecto"
 
-#: gui/options.cpp:2545
+#: gui/options.cpp:2547
 msgid "DLC Path:"
 msgstr "DLC:"
 
-#: gui/options.cpp:2547
+#: gui/options.cpp:2549
 msgctxt "lowres"
 msgid "DLC Path:"
 msgstr "DLC:"
 
-#: gui/options.cpp:2555 gui/options.cpp:2557 gui/options.cpp:2558
+#: gui/options.cpp:2557 gui/options.cpp:2559 gui/options.cpp:2560
 msgid "Specifies path to additional data used by all games or ScummVM"
 msgstr "Especifica el directorio adicional usado por los juegos y ScummVM"
 
-#: gui/options.cpp:2564
+#: gui/options.cpp:2566
 msgid "Plugins Path:"
 msgstr "Plugins:"
 
-#: gui/options.cpp:2566
+#: gui/options.cpp:2568
 msgctxt "lowres"
 msgid "Plugins Path:"
 msgstr "Plugins:"
 
-#: gui/options.cpp:2576
+#: gui/options.cpp:2578
 msgid "ScummVM config path: "
 msgstr "Config. de ScummVM: "
 
-#: gui/options.cpp:2590
+#: gui/options.cpp:2592
 msgid "ScummVM log path: "
 msgstr "Registros de ScummVM: "
 
-#: gui/options.cpp:2595
+#: gui/options.cpp:2597
 msgid "View"
 msgstr "Ver"
 
 #. I18N: Referring to the last path memorized when adding a game
-#: gui/options.cpp:2605 gui/options.cpp:3389
+#: gui/options.cpp:2607 gui/options.cpp:3402
 msgid "Last browser path: "
 msgstr "??ltima ruta del explorador: "
 
-#: gui/options.cpp:2610
+#: gui/options.cpp:2612
 msgid "Theme:"
 msgstr "Tema:"
 
-#: gui/options.cpp:2615
+#: gui/options.cpp:2617
 msgid "GUI scale:"
 msgstr "Escala de interfaz:"
 
-#: gui/options.cpp:2632
+#: gui/options.cpp:2634
 #, c-format
 msgid "%d%% - Custom"
 msgstr "%d??%% - Personalizada"
 
-#: gui/options.cpp:2636
+#: gui/options.cpp:2638
 msgid "GUI renderer:"
 msgstr "Render. de interfaz:"
 
-#: gui/options.cpp:2649
+#: gui/options.cpp:2651
 msgid "Return to the launcher when leaving a game"
 msgstr "Volver al iniciador tras salir de un juego"
 
-#: gui/options.cpp:2650
+#: gui/options.cpp:2652
 msgid ""
 "Return to the launcher when leaving a game instead of closing ScummVM\n"
 "(this feature is not supported by all games)."
@@ -2350,39 +2350,55 @@ msgstr ""
 "Cuando se abandone un juego, ScummVM regresar?? al iniciador y no se cerrar??\n"
 "(esta funci??n no est?? disponible en todos los juegos)."
 
-#: gui/options.cpp:2657
+#: gui/options.cpp:2659
 msgid "Ask for confirmation on exit"
 msgstr "Confirmar al salir"
 
-#: gui/options.cpp:2658
+#: gui/options.cpp:2660
 msgid "Ask for permission when closing ScummVM or leaving a game."
 msgstr "Pide permiso para cerrar ScumMVM o salir de un juego."
 
-#: gui/options.cpp:2664
+#: gui/options.cpp:2666
 msgid "Disable fixed font scaling"
 msgstr "Desactivar escalado de fuentes fijas"
 
-#: gui/options.cpp:2665
+#: gui/options.cpp:2667
+#, fuzzy
+#| msgid ""
+#| "Do not upscale fixed size fonts in the GUI. This reduces artefacts on low "
+#| "resolution screens"
 msgid ""
 "Do not upscale fixed size fonts in the GUI. This reduces artefacts on low "
-"resolution screens"
+"resolution screens."
 msgstr ""
 "No se escalar??n las fuentes de ancho fijo dentro de la interfaz, lo que "
 "reducir?? los artefactos en pantallas de baja resoluci??n"
 
-#: gui/options.cpp:2671
+#: gui/options.cpp:2673
+#, fuzzy
+#| msgid "Enable smooth scrolling when walking"
+msgid "Enable kinetic scrolling in lists"
+msgstr "Activar el desplazamiento de pantalla suave al caminar"
+
+#: gui/options.cpp:2674
+#, fuzzy
+#| msgid "Enable smooth scrolling when walking"
+msgid "Enable smooth, momentum-based scrolling in list widgets."
+msgstr "Activar el desplazamiento de pantalla suave al caminar"
+
+#: gui/options.cpp:2680
 msgid "GUI language:"
 msgstr "Idioma de interfaz:"
 
-#: gui/options.cpp:2671
+#: gui/options.cpp:2680
 msgid "Language of ScummVM GUI"
 msgstr "Idioma del interfaz de ScummVM"
 
-#: gui/options.cpp:2697
+#: gui/options.cpp:2706
 msgid "Switch the ScummVM GUI language to the game language"
 msgstr "Cambiar el idioma de la interfaz de ScummVM al idioma del juego"
 
-#: gui/options.cpp:2698
+#: gui/options.cpp:2707
 msgid ""
 "When starting a game, change the ScummVM GUI language to the game language. "
 "That way, if a game uses the ScummVM save and load dialogs, they are in the "
@@ -2392,11 +2408,11 @@ msgstr ""
 "del juego. De esa manera, si un juego utiliza las ventanas de guardado y "
 "carga de ScummVM, estar??n en el mismo idioma que el juego."
 
-#: gui/options.cpp:2710
+#: gui/options.cpp:2719
 msgid "Use native system file browser"
 msgstr "Usar el explorador de archivos nativo"
 
-#: gui/options.cpp:2711
+#: gui/options.cpp:2720
 msgid ""
 "Use the native system file browser instead of the ScummVM one to select a "
 "file or directory."
@@ -2404,53 +2420,53 @@ msgstr ""
 "Utiliza el explorador de archivos del sistema nativo en lugar del de ScummVM "
 "para seleccionar un archivo o directorio."
 
-#: gui/options.cpp:2718
+#: gui/options.cpp:2727
 msgid "Download Icons"
 msgstr "Descargar iconos"
 
-#: gui/options.cpp:2718
+#: gui/options.cpp:2727
 msgid "Check on the scummvm.org website for updates of icon packs"
 msgstr ""
 "Ve a la p??gina scummvm.org para buscar actualizaciones de paquetes de iconos"
 
-#: gui/options.cpp:2724
+#: gui/options.cpp:2733
 msgid "Autosave:"
 msgstr "Autoguardado:"
 
-#: gui/options.cpp:2726
+#: gui/options.cpp:2735
 msgctxt "lowres"
 msgid "Autosave:"
 msgstr "Autoguardado:"
 
-#: gui/options.cpp:2737
+#: gui/options.cpp:2746
 msgid "Random seed:"
 msgstr "Valor de inicial.:"
 
-#: gui/options.cpp:2737
+#: gui/options.cpp:2746
 msgid "Seed for initializing all random number generators"
 msgstr ""
 "El valor de inicializaci??n que utilizar??n todos los generadores de n??meros "
 "aleatorios"
 
-#: gui/options.cpp:2745
+#: gui/options.cpp:2754
 msgid "Debug level:"
 msgstr "Niv. de depuraci??n:"
 
 #. I18N: Debug level -1, no messages
-#: gui/options.cpp:2749 gui/options.cpp:2886 audio/fmopl.cpp:83
+#: gui/options.cpp:2758 gui/options.cpp:2895 audio/fmopl.cpp:83
 msgid "None"
 msgstr "Ninguno"
 
 #. I18N: Debug level 11, all messages
-#: gui/options.cpp:2755
+#: gui/options.cpp:2764
 msgid "11 (all)"
 msgstr "11 (todo)"
 
-#: gui/options.cpp:2759
+#: gui/options.cpp:2768
 msgid "Enable Discord integration"
 msgstr "Activar integraci??n con Discord"
 
-#: gui/options.cpp:2760
+#: gui/options.cpp:2769
 msgid ""
 "Show information about the games you are playing on Discord if the Discord "
 "client is running."
@@ -2458,27 +2474,27 @@ msgstr ""
 "Si el cliente de Discord est?? ejecut??ndose, muestra en Discord informaci??n "
 "sobre los juegos a los que est??s jugando."
 
-#: gui/options.cpp:2770 gui/updates-dialog.cpp:87
+#: gui/options.cpp:2779 gui/updates-dialog.cpp:87
 msgid "Update check:"
 msgstr "Actualizar:"
 
-#: gui/options.cpp:2770
+#: gui/options.cpp:2779
 msgid "How often to check ScummVM updates"
 msgstr "Frecuencia con la que se buscan actualizaciones"
 
-#: gui/options.cpp:2781
+#: gui/options.cpp:2790
 msgid "Check now"
 msgstr "Buscar ahora"
 
-#: gui/options.cpp:2788
+#: gui/options.cpp:2797
 msgid "Active storage:"
 msgstr "Alm. activo:"
 
-#: gui/options.cpp:2788
+#: gui/options.cpp:2797
 msgid "Active cloud storage"
 msgstr "Almacenamiento en la nube activo"
 
-#: gui/options.cpp:2797
+#: gui/options.cpp:2806
 msgctxt "lowres"
 msgid ""
 "Storage is not yet enabled. Verify that username is correct and enable it:"
@@ -2486,198 +2502,198 @@ msgstr ""
 "El almacenamiento no est?? activado todav??a. Verifica que el nombre de "
 "usuario es correcto y act??valo:"
 
-#: gui/options.cpp:2799
+#: gui/options.cpp:2808
 msgid ""
 "Storage is not yet enabled. Verify that username is correct and enable it:"
 msgstr ""
 "El almacenamiento no est?? activado todav??a. Verifica que el nombre de "
 "usuario es correcto y act??valo:"
 
-#: gui/options.cpp:2800
+#: gui/options.cpp:2809
 msgid "Enable storage"
 msgstr "Activar almacenamiento"
 
-#: gui/options.cpp:2800
+#: gui/options.cpp:2809
 msgid "Confirm you want to use this account for this storage"
 msgstr "Confirma que quieres usar esta cuenta como almacenamiento"
 
-#: gui/options.cpp:2802 backends/platform/wii/options.cpp:113
+#: gui/options.cpp:2811 backends/platform/wii/options.cpp:113
 msgid "Username:"
 msgstr "Usuario:"
 
-#: gui/options.cpp:2802
+#: gui/options.cpp:2811
 msgid "Username used by this storage"
 msgstr "Nombre de usuario utilizado para este almacenamiento"
 
-#: gui/options.cpp:2803
+#: gui/options.cpp:2812
 msgid "<none>"
 msgstr "<ninguno>"
 
-#: gui/options.cpp:2805
+#: gui/options.cpp:2814
 msgid "Used space:"
 msgstr "Espacio utilizado:"
 
-#: gui/options.cpp:2805
+#: gui/options.cpp:2814
 msgid "Space used by ScummVM's saved games on this storage"
 msgstr ""
 "Espacio utilizado por las partidas guardadas de ScummVM en este "
 "almacenamiento"
 
-#: gui/options.cpp:2808
+#: gui/options.cpp:2817
 msgid "Last sync:"
 msgstr "??ltima sincronizaci??n:"
 
-#: gui/options.cpp:2808
+#: gui/options.cpp:2817
 msgid "When was the last time saved games were synced with this storage"
 msgstr ""
 "Fecha en la que se ha producido la ??ltima sincronizaci??n de partidas "
 "guardadas con este almacenamiento"
 
-#: gui/options.cpp:2809 gui/options.cpp:3693
+#: gui/options.cpp:2818 gui/options.cpp:3706
 msgid "<never>"
 msgstr "<nunca>"
 
-#: gui/options.cpp:2811
+#: gui/options.cpp:2820
 msgctxt "lowres"
 msgid "Saved games sync automatically on launch, after saving and on loading."
 msgstr ""
 "Las partidas guardadas se sincronizan autom??ticamente al iniciar el juego, "
 "al guardar y al cargar."
 
-#: gui/options.cpp:2813
+#: gui/options.cpp:2822
 msgid "Saved games sync automatically on launch, after saving and on loading."
 msgstr ""
 "Las partidas guardadas se sincronizan autom??ticamente al iniciar el juego, "
 "al guardar y al cargar."
 
-#: gui/options.cpp:2814
+#: gui/options.cpp:2823
 msgid "Sync now"
 msgstr "Sincronizar ahora"
 
-#: gui/options.cpp:2814
+#: gui/options.cpp:2823
 msgid "Start saved games sync"
 msgstr "Inicia la sincronizaci??n de las partidas guardadas"
 
-#: gui/options.cpp:2817
+#: gui/options.cpp:2826
 msgctxt "lowres"
 msgid "You can download game files from your cloud ScummVM folder:"
 msgstr ""
 "Puedes descargar archivos de juego desde tu carpeta en la nube de ScummVM:"
 
-#: gui/options.cpp:2819
+#: gui/options.cpp:2828
 msgid "You can download game files from your cloud ScummVM folder:"
 msgstr ""
 "Puedes descargar archivos de juego desde tu carpeta en la nube de ScummVM:"
 
-#: gui/options.cpp:2820
+#: gui/options.cpp:2829
 msgid "Download game files"
 msgstr "Descargar archivos de juego"
 
-#: gui/options.cpp:2820
+#: gui/options.cpp:2829
 msgid "Open downloads manager dialog"
 msgstr "Abre el gestor de descargas"
 
-#: gui/options.cpp:2823
+#: gui/options.cpp:2832
 msgctxt "lowres"
 msgid "To change account for this storage, disconnect and connect again:"
 msgstr ""
 "Para cambiar la cuenta para este almacenamiento, desconecta y conecta de "
 "nuevo:"
 
-#: gui/options.cpp:2825
+#: gui/options.cpp:2834
 msgid "To change account for this storage, disconnect and connect again:"
 msgstr ""
 "Para cambiar la cuenta para este almacenamiento, desconecta y conecta de "
 "nuevo:"
 
-#: gui/options.cpp:2826
+#: gui/options.cpp:2835
 msgid "Disconnect"
 msgstr "Desconectar"
 
-#: gui/options.cpp:2826
+#: gui/options.cpp:2835
 msgid "Stop using this storage on this device"
 msgstr "Deja de utilizar este almacenamiento en este dispositivo"
 
-#: gui/options.cpp:2829
+#: gui/options.cpp:2838
 msgctxt "lowres"
 msgid "This storage is not connected yet!"
 msgstr "??Este almacenamiento no est?? conectado todav??a!"
 
-#: gui/options.cpp:2831
+#: gui/options.cpp:2840
 msgid "This storage is not connected yet!"
 msgstr "??Este almacenamiento no est?? conectado todav??a!"
 
-#: gui/options.cpp:2832
+#: gui/options.cpp:2841
 msgid "Connect"
 msgstr "Conectar"
 
-#: gui/options.cpp:2832
+#: gui/options.cpp:2841
 msgid "Connect your cloud storage account"
 msgstr "Conecta con tu cuenta de almacenamiento en la nube"
 
-#: gui/options.cpp:2845
+#: gui/options.cpp:2854
 msgctxt "lowres"
 msgid "/root/ Path:"
 msgstr "/ra??z/:"
 
-#: gui/options.cpp:2845 gui/options.cpp:2847 gui/options.cpp:2848



From noreply at scummvm.org  Sat Jun 13 15:36:23 2026
From: noreply at scummvm.org (sev-)
Date: Sat, 13 Jun 2026 15:36:23 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 da5cac58ed3524bfafb6d1a97cc94edf3b1ee2a1
Message-ID: <20260613153623.AFBB67005BC@ron.scummvm.net>

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:
8732f79410 PELROCK: Clarified engine name
802cd10b66 PELROCK: Clarified engine dependenciss
14284726c4 PELROCK: Fix include
5bfc5b7334 PELROCK: Fix warnings. PVS-STudio V560, V575
da5cac58ed PELROCK: Comment out unused variables


Commit: 8732f794106148f3d1f75854a3a66e355891e4ea
    https://github.com/scummvm/scummvm/commit/8732f794106148f3d1f75854a3a66e355891e4ea
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2026-06-13T17:36:01+02:00

Commit Message:
PELROCK: Clarified engine name

Changed paths:
    engines/pelrock/configure.engine


diff --git a/engines/pelrock/configure.engine b/engines/pelrock/configure.engine
index df86a2957c3..ae99adf616c 100644
--- a/engines/pelrock/configure.engine
+++ b/engines/pelrock/configure.engine
@@ -1,3 +1,3 @@
 # This file is included from the main "configure" script
 # add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps] [components]
-add_engine pelrock "Pelrock" yes "" "" "" ""
+add_engine pelrock "Alfred Pelrock" yes "" "" "" ""


Commit: 802cd10b6662f1856b8a10eda2f97ae64ca20ba9
    https://github.com/scummvm/scummvm/commit/802cd10b6662f1856b8a10eda2f97ae64ca20ba9
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2026-06-13T17:36:02+02:00

Commit Message:
PELROCK: Clarified engine dependenciss

Changed paths:
    engines/pelrock/configure.engine


diff --git a/engines/pelrock/configure.engine b/engines/pelrock/configure.engine
index ae99adf616c..f175fe347e0 100644
--- a/engines/pelrock/configure.engine
+++ b/engines/pelrock/configure.engine
@@ -1,3 +1,3 @@
 # This file is included from the main "configure" script
 # add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps] [components]
-add_engine pelrock "Alfred Pelrock" yes "" "" "" ""
+add_engine pelrock "Alfred Pelrock" yes "" "" "highres png" ""


Commit: 14284726c420330fd41209b75bafc6a98b380c3f
    https://github.com/scummvm/scummvm/commit/14284726c420330fd41209b75bafc6a98b380c3f
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2026-06-13T17:36:02+02:00

Commit Message:
PELROCK: Fix include

Changed paths:
    engines/pelrock/room.cpp


diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index 894e325a14c..23a69838860 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -23,7 +23,7 @@
 #include "pelrock/pelrock.h"
 #include "pelrock/room.h"
 #include "pelrock/util.h"
-#include "room.h"
+#include "pelrock/room.h"
 
 namespace Pelrock {
 


Commit: 5bfc5b7334cabcb6b733fa12f6900e9449d38d63
    https://github.com/scummvm/scummvm/commit/5bfc5b7334cabcb6b733fa12f6900e9449d38d63
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2026-06-13T17:36:03+02:00

Commit Message:
PELROCK: Fix warnings. PVS-STudio V560, V575

Changed paths:
    engines/pelrock/sound.cpp


diff --git a/engines/pelrock/sound.cpp b/engines/pelrock/sound.cpp
index b6cdf46135a..5457c06fd11 100644
--- a/engines/pelrock/sound.cpp
+++ b/engines/pelrock/sound.cpp
@@ -204,9 +204,8 @@ int SoundManager::playSound(SonidoFile sound, int channel, int loopCount) {
 
 		uint32 pcmSize = sound.size - headerSize;
 		byte *pcmData = (byte *)malloc(pcmSize);
-		if (!pcmData || !data) {
+		if (!pcmData) {
 			free(data);
-			free(pcmData);
 			return -1;
 		}
 		memcpy(pcmData, data + headerSize, pcmSize);


Commit: da5cac58ed3524bfafb6d1a97cc94edf3b1ee2a1
    https://github.com/scummvm/scummvm/commit/da5cac58ed3524bfafb6d1a97cc94edf3b1ee2a1
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2026-06-13T17:36:03+02:00

Commit Message:
PELROCK: Comment out unused variables

Changed paths:
    engines/pelrock/room.cpp


diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index 23a69838860..dda7365bb17 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -612,7 +612,7 @@ void RoomManager::resetConversationStates(byte roomNumber, byte *conversationDat
 		debug("Could not open ALFRED.B to reset conversation states!");
 		return;
 	}
-	bool roomDone = false;
+	//bool roomDone = false;
 	while (!alfredB.eos()) {
 		ResetEntry entry;
 		entry.room = alfredB.readUint16LE();
@@ -622,7 +622,7 @@ void RoomManager::resetConversationStates(byte roomNumber, byte *conversationDat
 		alfredB.read(entry.data, entry.dataSize);
 		if (roomNumber < entry.room) {
 			// We've passed the room we care about
-			roomDone = true;
+			//roomDone = true;
 			delete[] entry.data;
 			break;
 		}
@@ -1157,7 +1157,7 @@ void RoomManager::resetMetadataDefaults(byte room, byte *&data, size_t size) {
 	if (!alfred8.open("ALFRED.8")) {
 		error("RoomManager::resetMetadataDefaults(): Couldnt find file ALFRED.8");
 	}
-	bool roomDone = false;
+	//bool roomDone = false;
 	while (!alfred8.eos()) {
 		ResetEntry entry;
 		entry.room = alfred8.readUint16LE();
@@ -1167,7 +1167,7 @@ void RoomManager::resetMetadataDefaults(byte room, byte *&data, size_t size) {
 		alfred8.read(entry.data, entry.dataSize);
 		if (room < entry.room) {
 			// We've passed the room we care about
-			roomDone = true;
+			//roomDone = true;
 			delete[] entry.data;
 			break;
 		}



From noreply at scummvm.org  Sat Jun 13 17:03:41 2026
From: noreply at scummvm.org (sev-)
Date: Sat, 13 Jun 2026 17:03:41 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 605061c7249391aa95d086c7c4d2536eed283b4c
Message-ID: <20260613170341.A9A107005BC@ron.scummvm.net>

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:
11657be06d PELROCK: Reduce header dependency
d543f07c85 PELROCK: Further reducing of header dependency
605061c724 PELROCK: Final pass on reducing header dependencies


Commit: 11657be06d4c65c84745a0cdfdb8c02b95f1c331
    https://github.com/scummvm/scummvm/commit/11657be06d4c65c84745a0cdfdb8c02b95f1c331
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2026-06-13T18:14:37+02:00

Commit Message:
PELROCK: Reduce header dependency

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/actions.h
    engines/pelrock/backgroundbook.cpp
    engines/pelrock/backgroundbook.h
    engines/pelrock/cdplayer.cpp
    engines/pelrock/chrono.cpp
    engines/pelrock/computer.cpp
    engines/pelrock/console.cpp
    engines/pelrock/dialog.cpp
    engines/pelrock/events.cpp
    engines/pelrock/graphics.cpp
    engines/pelrock/menu.cpp
    engines/pelrock/menu.h
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/resources.cpp
    engines/pelrock/room.cpp
    engines/pelrock/saveload.cpp
    engines/pelrock/slidingpuzzle.cpp
    engines/pelrock/spellbook.cpp
    engines/pelrock/util.cpp
    engines/pelrock/video.cpp


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index 8324af1ae82..56d98fd2ada 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -27,8 +27,13 @@
 #include "pelrock/actions.h"
 #include "pelrock/backgroundbook.h"
 #include "pelrock/cdplayer.h"
+#include "pelrock/chrono.h"
+#include "pelrock/dialog.h"
 #include "pelrock/offsets.h"
 #include "pelrock/pelrock.h"
+#include "pelrock/resources.h"
+#include "pelrock/room.h"
+#include "pelrock/sound.h"
 #include "pelrock/spellbook.h"
 #include "pelrock/util.h"
 
diff --git a/engines/pelrock/actions.h b/engines/pelrock/actions.h
index a5eb7a62804..b1b29305706 100644
--- a/engines/pelrock/actions.h
+++ b/engines/pelrock/actions.h
@@ -22,7 +22,6 @@
 #define PELROCK_ACTIONS_H
 
 #include "pelrock/pelrock.h"
-#include "pelrock/types.h"
 
 namespace Pelrock {
 
diff --git a/engines/pelrock/backgroundbook.cpp b/engines/pelrock/backgroundbook.cpp
index bcf50b15a8c..37e0c81e990 100644
--- a/engines/pelrock/backgroundbook.cpp
+++ b/engines/pelrock/backgroundbook.cpp
@@ -21,10 +21,15 @@
 
 #include "common/events.h"
 #include "graphics/paletteman.h"
+#include "graphics/screen.h"
 
+#include "pelrock/pelrock.h"
 #include "pelrock/backgroundbook.h"
+#include "pelrock/events.h"
+#include "pelrock/resources.h"
 #include "pelrock/room.h"
 #include "pelrock/util.h"
+#include "pelrock/fonts/small_font.h"
 
 namespace Pelrock {
 
diff --git a/engines/pelrock/backgroundbook.h b/engines/pelrock/backgroundbook.h
index f037f4d32ab..32b8b8e86a3 100644
--- a/engines/pelrock/backgroundbook.h
+++ b/engines/pelrock/backgroundbook.h
@@ -22,14 +22,10 @@
 #ifndef PELROCK_BACKGROUNDBOOK_H
 #define PELROCK_BACKGROUNDBOOK_H
 
-#include "common/array.h"
-#include "common/rect.h"
-#include "common/str.h"
-#include "graphics/managed_surface.h"
-
-#include "pelrock/pelrock.h"
-
 namespace Pelrock {
+class PelrockEventManager;
+class ResourceManager;
+class RoomManager;
 
 class BackgroundBook {
 
diff --git a/engines/pelrock/cdplayer.cpp b/engines/pelrock/cdplayer.cpp
index c51c3fe1b9c..17bbccee018 100644
--- a/engines/pelrock/cdplayer.cpp
+++ b/engines/pelrock/cdplayer.cpp
@@ -21,10 +21,15 @@
 
 #include "common/events.h"
 #include "graphics/paletteman.h"
+#include "graphics/screen.h"
 
 #include "pelrock/cdplayer.h"
+#include "pelrock/events.h"
+#include "pelrock/resources.h"
 #include "pelrock/room.h"
+#include "pelrock/sound.h"
 #include "pelrock/util.h"
+#include "pelrock/fonts/small_font.h"
 
 namespace Pelrock {
 
diff --git a/engines/pelrock/chrono.cpp b/engines/pelrock/chrono.cpp
index 73f091dc016..c586912e7f4 100644
--- a/engines/pelrock/chrono.cpp
+++ b/engines/pelrock/chrono.cpp
@@ -22,6 +22,8 @@
 #include "common/events.h"
 #include "common/system.h"
 
+#include "graphics/screen.h"
+
 #include "pelrock/chrono.h"
 #include "pelrock/pelrock.h"
 
diff --git a/engines/pelrock/computer.cpp b/engines/pelrock/computer.cpp
index 3f4b407ffdd..1799456bccf 100644
--- a/engines/pelrock/computer.cpp
+++ b/engines/pelrock/computer.cpp
@@ -21,11 +21,17 @@
 
 #include "common/events.h"
 #include "common/system.h"
+
 #include "graphics/cursorman.h"
 #include "graphics/paletteman.h"
+#include "graphics/screen.h"
 
 #include "pelrock/computer.h"
+#include "pelrock/graphics.h"
 #include "pelrock/pelrock.h"
+#include "pelrock/resources.h"
+#include "pelrock/room.h"
+#include "pelrock/fonts/small_font.h"
 
 namespace Pelrock {
 
diff --git a/engines/pelrock/console.cpp b/engines/pelrock/console.cpp
index b8240c13e6c..3546269451b 100644
--- a/engines/pelrock/console.cpp
+++ b/engines/pelrock/console.cpp
@@ -22,6 +22,7 @@
 #include "console.h"
 
 #include "pelrock/console.h"
+#include "pelrock/room.h"
 #include "pelrock/types.h"
 
 namespace Pelrock {
diff --git a/engines/pelrock/dialog.cpp b/engines/pelrock/dialog.cpp
index 774dc0c5622..56850a3b36d 100644
--- a/engines/pelrock/dialog.cpp
+++ b/engines/pelrock/dialog.cpp
@@ -20,10 +20,14 @@
  */
 #include "common/stack.h"
 
+#include "pelrock/chrono.h"
 #include "pelrock/dialog.h"
 #include "pelrock/offsets.h"
 #include "pelrock/pelrock.h"
+#include "pelrock/resources.h"
+#include "pelrock/room.h"
 #include "pelrock/util.h"
+#include "pelrock/fonts/small_font_double.h"
 
 namespace Pelrock {
 
diff --git a/engines/pelrock/events.cpp b/engines/pelrock/events.cpp
index 81558395938..8acd092359b 100644
--- a/engines/pelrock/events.cpp
+++ b/engines/pelrock/events.cpp
@@ -20,6 +20,8 @@
  */
 #include "common/events.h"
 
+#include "graphics/screen.h"
+
 #include "pelrock/events.h"
 #include "pelrock/pelrock.h"
 #include "pelrock/util.h"
diff --git a/engines/pelrock/graphics.cpp b/engines/pelrock/graphics.cpp
index 863d107e4cb..37e8844a060 100644
--- a/engines/pelrock/graphics.cpp
+++ b/engines/pelrock/graphics.cpp
@@ -21,11 +21,16 @@
 
 #include "common/scummsys.h"
 #include "engines/util.h"
+
 #include "graphics/cursorman.h"
 #include "graphics/paletteman.h"
+#include "graphics/screen.h"
 
+#include "pelrock/chrono.h"
+#include "pelrock/events.h"
 #include "pelrock/graphics.h"
 #include "pelrock/pelrock.h"
+#include "pelrock/room.h"
 #include "pelrock/util.h"
 
 namespace Pelrock {
diff --git a/engines/pelrock/menu.cpp b/engines/pelrock/menu.cpp
index 537055817e9..708dba6b571 100644
--- a/engines/pelrock/menu.cpp
+++ b/engines/pelrock/menu.cpp
@@ -26,10 +26,16 @@
 #include "graphics/paletteman.h"
 #include "graphics/thumbnail.h"
 
+#include "pelrock/events.h"
+#include "pelrock/graphics.h"
 #include "pelrock/menu.h"
 #include "pelrock/offsets.h"
 #include "pelrock/pelrock.h"
+#include "pelrock/resources.h"
+#include "pelrock/room.h"
+#include "pelrock/sound.h"
 #include "pelrock/util.h"
+#include "pelrock/fonts/small_font.h"
 
 namespace Pelrock {
 
diff --git a/engines/pelrock/menu.h b/engines/pelrock/menu.h
index a8952090c57..260b3585d75 100644
--- a/engines/pelrock/menu.h
+++ b/engines/pelrock/menu.h
@@ -21,16 +21,18 @@
 #ifndef PELROCK_MENU_H
 #define PELROCK_MENU_H
 
-#include "graphics/font.h"
-#include "graphics/managed_surface.h"
-#include "graphics/screen.h"
-
-#include "pelrock/events.h"
-#include "pelrock/resources.h"
-#include "pelrock/sound.h"
+#include "common/keyboard.h"
 
+#include "graphics/screen.h"
+namespace Graphics {
+class Screen;
+}
 namespace Pelrock {
 
+class PelrockEventManager;
+class ResourceManager;
+class SoundManager;
+
 enum MainMenuButton {
 	QUESTION_MARK_BUTTON,
 	INVENTORY_PREV_BUTTON,
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index de64f32b278..71b460193c7 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -28,15 +28,22 @@
 #include "graphics/paletteman.h"
 
 #include "pelrock/actions.h"
+#include "pelrock/chrono.h"
 #include "pelrock/computer.h"
 #include "pelrock/console.h"
 #include "pelrock/detection.h"
+#include "pelrock/dialog.h"
 #include "pelrock/fonts/small_font.h"
+#include "pelrock/fonts/small_font_double.h"
+#include "pelrock/menu.h"
 #include "pelrock/offsets.h"
 #include "pelrock/pathfinding.h"
 #include "pelrock/pelrock.h"
+#include "pelrock/resources.h"
+#include "pelrock/room.h"
 #include "pelrock/slidingpuzzle.h"
 #include "pelrock/util.h"
+#include "pelrock/video.h"
 
 namespace Pelrock {
 
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 91254d1e19f..3583cd02364 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -22,40 +22,36 @@
 #ifndef PELROCK_H
 #define PELROCK_H
 
-#include "common/error.h"
-#include "common/file.h"
-#include "common/fs.h"
-#include "common/hash-str.h"
 #include "common/random.h"
-#include "common/scummsys.h"
 #include "common/serializer.h"
-#include "common/system.h"
-#include "common/util.h"
-#include "engines/engine.h"
-#include "engines/savestate.h"
+
 #include "graphics/managed_surface.h"
-#include "graphics/screen.h"
-#include "image/png.h"
 
-#include "pelrock/chrono.h"
 #include "pelrock/detection.h"
-#include "pelrock/dialog.h"
-#include "pelrock/events.h"
-#include "pelrock/fonts/large_font.h"
-#include "pelrock/fonts/small_font.h"
-#include "pelrock/fonts/small_font_double.h"
-#include "pelrock/graphics.h"
-#include "pelrock/menu.h"
-#include "pelrock/resources.h"
-#include "pelrock/room.h"
-#include "pelrock/sound.h"
 #include "pelrock/types.h"
-#include "pelrock/video.h"
+
+namespace Graphics {
+class Screen;
+}
 
 namespace Pelrock {
 
 struct PelrockGameDescription;
 
+class ChronoManager;
+class DialogManager;
+class DoubleSmallFont;
+class GraphicsManager;
+class LargeFont;
+class MenuManager;
+class PelrockEventManager;
+class RoomManager;
+class ResourceManager;
+class SmallFont;
+class SoundManager;
+class VideoManager;
+
+
 class PelrockEngine : public Engine {
 private:
 	const ADGameDescription *_gameDescription;
diff --git a/engines/pelrock/resources.cpp b/engines/pelrock/resources.cpp
index c196cb3cb71..d14cc818e86 100644
--- a/engines/pelrock/resources.cpp
+++ b/engines/pelrock/resources.cpp
@@ -19,6 +19,7 @@
  *
  */
 
+#include "pelrock/dialog.h"
 #include "pelrock/resources.h"
 #include "pelrock/offsets.h"
 #include "pelrock/pelrock.h"
diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index dda7365bb17..43f2b60a581 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -20,7 +20,10 @@
  */
 #include "common/scummsys.h"
 
+#include "graphics/screen.h"
+
 #include "pelrock/pelrock.h"
+#include "pelrock/resources.h"
 #include "pelrock/room.h"
 #include "pelrock/util.h"
 #include "pelrock/room.h"
diff --git a/engines/pelrock/saveload.cpp b/engines/pelrock/saveload.cpp
index bd94497a05e..98926962aa0 100644
--- a/engines/pelrock/saveload.cpp
+++ b/engines/pelrock/saveload.cpp
@@ -21,6 +21,8 @@
 #include "common/savefile.h"
 
 #include "pelrock/pelrock.h"
+#include "pelrock/room.h"
+#include "pelrock/resources.h"
 #include "pelrock/types.h"
 
 namespace Pelrock {
diff --git a/engines/pelrock/slidingpuzzle.cpp b/engines/pelrock/slidingpuzzle.cpp
index 0e0b0ab8d57..e8bdf8cea0f 100644
--- a/engines/pelrock/slidingpuzzle.cpp
+++ b/engines/pelrock/slidingpuzzle.cpp
@@ -20,7 +20,9 @@
  */
 
 #include "common/system.h"
+
 #include "graphics/paletteman.h"
+#include "graphics/screen.h"
 
 #include "pelrock/events.h"
 #include "pelrock/pelrock.h"
diff --git a/engines/pelrock/spellbook.cpp b/engines/pelrock/spellbook.cpp
index b8f3cdf4b8a..21198e927f4 100644
--- a/engines/pelrock/spellbook.cpp
+++ b/engines/pelrock/spellbook.cpp
@@ -22,10 +22,13 @@
 #include "common/events.h"
 #include "graphics/paletteman.h"
 
+#include "pelrock/events.h"
 #include "pelrock/graphics.h"
 #include "pelrock/room.h"
+#include "pelrock/resources.h"
 #include "pelrock/spellbook.h"
 #include "pelrock/util.h"
+#include "pelrock/fonts/small_font.h"
 
 namespace Pelrock {
 
diff --git a/engines/pelrock/util.cpp b/engines/pelrock/util.cpp
index 2af0fd7f944..592fa3f99c7 100644
--- a/engines/pelrock/util.cpp
+++ b/engines/pelrock/util.cpp
@@ -22,8 +22,10 @@
 
 #include "graphics/cursorman.h"
 #include "graphics/font.h"
+#include "graphics/screen.h"
 
 #include "pelrock/pelrock.h"
+#include "pelrock/chrono.h"
 #include "pelrock/types.h"
 #include "pelrock/util.h"
 
diff --git a/engines/pelrock/video.cpp b/engines/pelrock/video.cpp
index 2fed22ef082..447a91ca1d1 100644
--- a/engines/pelrock/video.cpp
+++ b/engines/pelrock/video.cpp
@@ -24,7 +24,9 @@
 #include "graphics/screen.h"
 
 #include "pelrock/chrono.h"
+#include "pelrock/dialog.h"
 #include "pelrock/pelrock.h"
+#include "pelrock/sound.h"
 #include "pelrock/util.h"
 #include "pelrock/video.h"
 


Commit: d543f07c851c9a2890f6386e62a24b852b09dd40
    https://github.com/scummvm/scummvm/commit/d543f07c851c9a2890f6386e62a24b852b09dd40
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2026-06-13T18:43:30+02:00

Commit Message:
PELROCK: Further reducing of header dependency

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/backgroundbook.cpp
    engines/pelrock/cdplayer.cpp
    engines/pelrock/cdplayer.h
    engines/pelrock/chrono.h
    engines/pelrock/computer.cpp
    engines/pelrock/computer.h
    engines/pelrock/dialog.cpp
    engines/pelrock/dialog.h
    engines/pelrock/events.h
    engines/pelrock/fonts/large_font.cpp
    engines/pelrock/fonts/large_font.h
    engines/pelrock/fonts/small_font.cpp
    engines/pelrock/fonts/small_font.h
    engines/pelrock/fonts/small_font_double.cpp
    engines/pelrock/fonts/small_font_double.h
    engines/pelrock/graphics.h
    engines/pelrock/menu.cpp
    engines/pelrock/menu.h
    engines/pelrock/metaengine.h
    engines/pelrock/offsets.h
    engines/pelrock/pathfinding.h
    engines/pelrock/pelrock.cpp
    engines/pelrock/resources.cpp
    engines/pelrock/resources.h
    engines/pelrock/room.cpp
    engines/pelrock/room.h
    engines/pelrock/slidingpuzzle.h
    engines/pelrock/sound.h
    engines/pelrock/spellbook.cpp
    engines/pelrock/spellbook.h
    engines/pelrock/types.h
    engines/pelrock/util.h
    engines/pelrock/video.cpp
    engines/pelrock/video.h


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index 56d98fd2ada..ce475dd995c 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -22,13 +22,19 @@
 #include "audio/audiostream.h"
 #include "audio/decoders/raw.h"
 #include "audio/mixer.h"
+
+#include "common/file.h"
+
 #include "graphics/paletteman.h"
+#include "graphics/screen.h"
 
 #include "pelrock/actions.h"
 #include "pelrock/backgroundbook.h"
 #include "pelrock/cdplayer.h"
 #include "pelrock/chrono.h"
 #include "pelrock/dialog.h"
+#include "pelrock/events.h"
+#include "pelrock/graphics.h"
 #include "pelrock/offsets.h"
 #include "pelrock/pelrock.h"
 #include "pelrock/resources.h"
@@ -36,6 +42,7 @@
 #include "pelrock/sound.h"
 #include "pelrock/spellbook.h"
 #include "pelrock/util.h"
+#include "pelrock/fonts/large_font.h"
 
 namespace Pelrock {
 
diff --git a/engines/pelrock/backgroundbook.cpp b/engines/pelrock/backgroundbook.cpp
index 37e0c81e990..089ba924331 100644
--- a/engines/pelrock/backgroundbook.cpp
+++ b/engines/pelrock/backgroundbook.cpp
@@ -20,6 +20,7 @@
  */
 
 #include "common/events.h"
+#include "common/file.h"
 #include "graphics/paletteman.h"
 #include "graphics/screen.h"
 
diff --git a/engines/pelrock/cdplayer.cpp b/engines/pelrock/cdplayer.cpp
index 17bbccee018..52cddb2efff 100644
--- a/engines/pelrock/cdplayer.cpp
+++ b/engines/pelrock/cdplayer.cpp
@@ -20,9 +20,12 @@
  */
 
 #include "common/events.h"
+#include "common/file.h"
+
 #include "graphics/paletteman.h"
 #include "graphics/screen.h"
 
+#include "pelrock/pelrock.h"
 #include "pelrock/cdplayer.h"
 #include "pelrock/events.h"
 #include "pelrock/resources.h"
diff --git a/engines/pelrock/cdplayer.h b/engines/pelrock/cdplayer.h
index afc519f36f3..5ca1410d7c5 100644
--- a/engines/pelrock/cdplayer.h
+++ b/engines/pelrock/cdplayer.h
@@ -21,14 +21,12 @@
 #ifndef PELROCK_CDPLAYER_H
 #define PELROCK_CDPLAYER_H
 
-#include "common/rect.h"
-#include "common/str.h"
-#include "graphics/managed_surface.h"
-
-#include "pelrock/pelrock.h"
-
 namespace Pelrock {
 
+class PelrockEventManager;
+class ResourceManager;
+class SoundManager;
+
 class CDPlayer {
 
 	enum CDControls {
diff --git a/engines/pelrock/chrono.h b/engines/pelrock/chrono.h
index cbd348868ab..143c555ca96 100644
--- a/engines/pelrock/chrono.h
+++ b/engines/pelrock/chrono.h
@@ -21,8 +21,6 @@
 #ifndef PELROCK_CHRONO_H
 #define PELROCK_CHRONO_H
 
-#include "common/scummsys.h"
-
 namespace Pelrock {
 
 const uint32 kTickMs = 55;
diff --git a/engines/pelrock/computer.cpp b/engines/pelrock/computer.cpp
index 1799456bccf..394b1183015 100644
--- a/engines/pelrock/computer.cpp
+++ b/engines/pelrock/computer.cpp
@@ -20,6 +20,7 @@
  */
 
 #include "common/events.h"
+#include "common/file.h"
 #include "common/system.h"
 
 #include "graphics/cursorman.h"
@@ -27,6 +28,7 @@
 #include "graphics/screen.h"
 
 #include "pelrock/computer.h"
+#include "pelrock/events.h"
 #include "pelrock/graphics.h"
 #include "pelrock/pelrock.h"
 #include "pelrock/resources.h"
diff --git a/engines/pelrock/computer.h b/engines/pelrock/computer.h
index 3c5603bcd02..3acb7b04299 100644
--- a/engines/pelrock/computer.h
+++ b/engines/pelrock/computer.h
@@ -22,14 +22,10 @@
 #ifndef PELROCK_COMPUTER_H
 #define PELROCK_COMPUTER_H
 
-#include "common/array.h"
-#include "common/str.h"
-#include "graphics/managed_surface.h"
-
-#include "pelrock/events.h"
-
 namespace Pelrock {
 
+class PelrockEventManager;
+
 // Book data location in ALFRED.7
 static const uint32 kBookDataOffset = 0x309E0;
 static const uint32 kBookDataEnd = 0x33F05;
diff --git a/engines/pelrock/dialog.cpp b/engines/pelrock/dialog.cpp
index 56850a3b36d..1a80b2100c9 100644
--- a/engines/pelrock/dialog.cpp
+++ b/engines/pelrock/dialog.cpp
@@ -20,13 +20,18 @@
  */
 #include "common/stack.h"
 
+#include "graphics/screen.h"
+
 #include "pelrock/chrono.h"
 #include "pelrock/dialog.h"
+#include "pelrock/events.h"
+#include "pelrock/graphics.h"
 #include "pelrock/offsets.h"
 #include "pelrock/pelrock.h"
 #include "pelrock/resources.h"
 #include "pelrock/room.h"
 #include "pelrock/util.h"
+#include "pelrock/fonts/large_font.h"
 #include "pelrock/fonts/small_font_double.h"
 
 namespace Pelrock {
diff --git a/engines/pelrock/dialog.h b/engines/pelrock/dialog.h
index 045da583084..e4c99c150fe 100644
--- a/engines/pelrock/dialog.h
+++ b/engines/pelrock/dialog.h
@@ -21,19 +21,21 @@
 #ifndef PELROCK_DIALOG_H
 #define PELROCK_DIALOG_H
 
-#include "common/scummsys.h"
 #include "common/stack.h"
-#include "graphics/managed_surface.h"
-#include "graphics/screen.h"
 
-#include "pelrock/events.h"
-#include "pelrock/fonts/large_font.h"
-#include "pelrock/fonts/small_font.h"
-#include "pelrock/graphics.h"
+#include "graphics/font.h"
+
 #include "pelrock/types.h"
 
+namespace Graphics {
+class Screen;
+}
+
 namespace Pelrock {
 
+class GraphicsManager;
+class PelrockEventManager;
+
 // Control character codes
 const byte kCtrlSpace                = 0x20; /* ' ' */
 const byte kCtrlSpeakerId            = 0x08; /* Next byte is speaker ID (color) */
diff --git a/engines/pelrock/events.h b/engines/pelrock/events.h
index 3198089d687..0f184a1b15b 100644
--- a/engines/pelrock/events.h
+++ b/engines/pelrock/events.h
@@ -22,7 +22,6 @@
 #define PELROCK_EVENTS_H
 
 #include "common/events.h"
-#include "common/scummsys.h"
 
 namespace Pelrock {
 static const int kDoubleClickDelay = 300; // in milliseconds
diff --git a/engines/pelrock/fonts/large_font.cpp b/engines/pelrock/fonts/large_font.cpp
index 2228bda30c9..b34692d0230 100644
--- a/engines/pelrock/fonts/large_font.cpp
+++ b/engines/pelrock/fonts/large_font.cpp
@@ -19,6 +19,10 @@
  *
  */
 
+#include "common/file.h"
+
+#include "graphics/surface.h"
+
 #include "pelrock/fonts/large_font.h"
 
 namespace Pelrock {
diff --git a/engines/pelrock/fonts/large_font.h b/engines/pelrock/fonts/large_font.h
index fc6e39ebeea..8f89d508ac2 100644
--- a/engines/pelrock/fonts/large_font.h
+++ b/engines/pelrock/fonts/large_font.h
@@ -21,10 +21,7 @@
 #ifndef PELROCK_LARGEFONT_H
 #define PELROCK_LARGEFONT_H
 
-#include "common/file.h"
-#include "common/str.h"
 #include "graphics/font.h"
-#include "graphics/surface.h"
 
 namespace Pelrock {
 class LargeFont : public Graphics::Font {
diff --git a/engines/pelrock/fonts/small_font.cpp b/engines/pelrock/fonts/small_font.cpp
index 083c9adc3f5..fed8b97796c 100644
--- a/engines/pelrock/fonts/small_font.cpp
+++ b/engines/pelrock/fonts/small_font.cpp
@@ -19,6 +19,10 @@
  *
  */
 
+#include "common/file.h"
+
+#include "graphics/surface.h"
+
 #include "pelrock/fonts/small_font.h"
 
 namespace Pelrock {
diff --git a/engines/pelrock/fonts/small_font.h b/engines/pelrock/fonts/small_font.h
index f64e3fa4a19..927a9dce280 100644
--- a/engines/pelrock/fonts/small_font.h
+++ b/engines/pelrock/fonts/small_font.h
@@ -21,10 +21,7 @@
 #ifndef PELROCK_SMALLFONT_H
 #define PELROCK_SMALLFONT_H
 
-#include "common/file.h"
-#include "common/str.h"
 #include "graphics/font.h"
-#include "graphics/surface.h"
 
 namespace Pelrock {
 
diff --git a/engines/pelrock/fonts/small_font_double.cpp b/engines/pelrock/fonts/small_font_double.cpp
index 4f1484685ef..e55f29be74e 100644
--- a/engines/pelrock/fonts/small_font_double.cpp
+++ b/engines/pelrock/fonts/small_font_double.cpp
@@ -18,8 +18,11 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
  */
-#include "pelrock/fonts/small_font_double.h"
+
 #include "common/debug.h"
+#include "graphics/surface.h"
+
+#include "pelrock/fonts/small_font_double.h"
 
 namespace Pelrock {
 
diff --git a/engines/pelrock/fonts/small_font_double.h b/engines/pelrock/fonts/small_font_double.h
index 68a603ff28e..9fa79eef5a6 100644
--- a/engines/pelrock/fonts/small_font_double.h
+++ b/engines/pelrock/fonts/small_font_double.h
@@ -21,11 +21,6 @@
 #ifndef PELROCK_SMALLFONTDOUBLE_H
 #define PELROCK_SMALLFONTDOUBLE_H
 
-#include "common/file.h"
-#include "common/str.h"
-#include "graphics/font.h"
-#include "graphics/surface.h"
-
 #include "pelrock/fonts/small_font.h"
 
 namespace Pelrock {
diff --git a/engines/pelrock/graphics.h b/engines/pelrock/graphics.h
index 910c4d87eb2..1e557623860 100644
--- a/engines/pelrock/graphics.h
+++ b/engines/pelrock/graphics.h
@@ -21,16 +21,12 @@
 #ifndef PELROCK_GRAPHICS_H
 #define PELROCK_GRAPHICS_H
 
-#include "common/array.h"
-#include "common/scummsys.h"
-#include "common/str-array.h"
-
-#include "graphics/font.h"
-#include "graphics/managed_surface.h"
-#include "graphics/screen.h"
-
 #include "pelrock/types.h"
 
+namespace Graphics {
+class Font;
+}
+
 namespace Pelrock {
 
 class GraphicsManager {
diff --git a/engines/pelrock/menu.cpp b/engines/pelrock/menu.cpp
index 708dba6b571..1eaec4922d8 100644
--- a/engines/pelrock/menu.cpp
+++ b/engines/pelrock/menu.cpp
@@ -24,6 +24,7 @@
 #include "common/file.h"
 #include "engines/metaengine.h"
 #include "graphics/paletteman.h"
+#include "graphics/screen.h"
 #include "graphics/thumbnail.h"
 
 #include "pelrock/events.h"
diff --git a/engines/pelrock/menu.h b/engines/pelrock/menu.h
index 260b3585d75..5ff0e1b624f 100644
--- a/engines/pelrock/menu.h
+++ b/engines/pelrock/menu.h
@@ -21,9 +21,6 @@
 #ifndef PELROCK_MENU_H
 #define PELROCK_MENU_H
 
-#include "common/keyboard.h"
-
-#include "graphics/screen.h"
 namespace Graphics {
 class Screen;
 }
diff --git a/engines/pelrock/metaengine.h b/engines/pelrock/metaengine.h
index 596f967bf63..19bccc24c81 100644
--- a/engines/pelrock/metaengine.h
+++ b/engines/pelrock/metaengine.h
@@ -23,7 +23,6 @@
 #define PELROCK_METAENGINE_H
 
 #include "engines/advancedDetector.h"
-#include "graphics/surface.h"
 
 class PelrockMetaEngine : public AdvancedMetaEngine<ADGameDescription> {
 public:
diff --git a/engines/pelrock/offsets.h b/engines/pelrock/offsets.h
index 770032ee022..2eb8e116442 100644
--- a/engines/pelrock/offsets.h
+++ b/engines/pelrock/offsets.h
@@ -21,10 +21,6 @@
 #ifndef PELROCK_OFFSETS_H
 #define PELROCK_OFFSETS_H
 
-#include "common/scummsys.h"
-
-#include "pelrock/types.h"
-
 namespace Pelrock {
 
 // Indices to in game text responses in JUEGO.EXE
diff --git a/engines/pelrock/pathfinding.h b/engines/pelrock/pathfinding.h
index 6775cf3a4a9..79f9e21e52b 100644
--- a/engines/pelrock/pathfinding.h
+++ b/engines/pelrock/pathfinding.h
@@ -21,9 +21,6 @@
 #ifndef PELROCK_PATHFINDING_H
 #define PELROCK_PATHFINDING_H
 
-#include "common/scummsys.h"
-#include "graphics/screen.h"
-
 #include "pelrock/types.h"
 
 namespace Pelrock {
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 71b460193c7..1d08953a428 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -23,9 +23,12 @@
 #include "common/events.h"
 #include "common/file.h"
 #include "common/scummsys.h"
+
 #include "engines/util.h"
+
 #include "graphics/cursorman.h"
 #include "graphics/paletteman.h"
+#include "graphics/screen.h"
 
 #include "pelrock/actions.h"
 #include "pelrock/chrono.h"
@@ -33,8 +36,11 @@
 #include "pelrock/console.h"
 #include "pelrock/detection.h"
 #include "pelrock/dialog.h"
+#include "pelrock/events.h"
+#include "pelrock/fonts/large_font.h"
 #include "pelrock/fonts/small_font.h"
 #include "pelrock/fonts/small_font_double.h"
+#include "pelrock/graphics.h"
 #include "pelrock/menu.h"
 #include "pelrock/offsets.h"
 #include "pelrock/pathfinding.h"
@@ -42,6 +48,7 @@
 #include "pelrock/resources.h"
 #include "pelrock/room.h"
 #include "pelrock/slidingpuzzle.h"
+#include "pelrock/sound.h"
 #include "pelrock/util.h"
 #include "pelrock/video.h"
 
diff --git a/engines/pelrock/resources.cpp b/engines/pelrock/resources.cpp
index d14cc818e86..ebd91933a37 100644
--- a/engines/pelrock/resources.cpp
+++ b/engines/pelrock/resources.cpp
@@ -19,6 +19,8 @@
  *
  */
 
+#include "common/file.h"
+
 #include "pelrock/dialog.h"
 #include "pelrock/resources.h"
 #include "pelrock/offsets.h"
diff --git a/engines/pelrock/resources.h b/engines/pelrock/resources.h
index 315fa895a4a..3436d769e30 100644
--- a/engines/pelrock/resources.h
+++ b/engines/pelrock/resources.h
@@ -21,12 +21,6 @@
 #ifndef PELROCK_RESOURCES_H
 #define PELROCK_RESOURCES_H
 
-#include "common/scummsys.h"
-#include "common/stream.h"
-
-#include "pelrock/offsets.h"
-#include "pelrock/types.h"
-
 namespace Pelrock {
 
 static const int walkingAnimLengths[4] = {8, 8, 4, 4}; // size of each inner array
diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index 43f2b60a581..61d6c332e32 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -19,6 +19,7 @@
  *
  */
 #include "common/scummsys.h"
+#include "common/file.h"
 
 #include "graphics/screen.h"
 
diff --git a/engines/pelrock/room.h b/engines/pelrock/room.h
index 93bfd8e24a2..c187ae617c2 100644
--- a/engines/pelrock/room.h
+++ b/engines/pelrock/room.h
@@ -21,11 +21,9 @@
 #ifndef PELROCK_ROOM_H
 #define PELROCK_ROOM_H
 
-#include "common/array.h"
-#include "common/file.h"
-#include "common/scummsys.h"
-
-#include "pelrock/types.h"
+namespace Common {
+class File;
+}
 
 namespace Pelrock {
 
diff --git a/engines/pelrock/slidingpuzzle.h b/engines/pelrock/slidingpuzzle.h
index e7f37cada51..af9bcbe83ab 100644
--- a/engines/pelrock/slidingpuzzle.h
+++ b/engines/pelrock/slidingpuzzle.h
@@ -22,11 +22,6 @@
 #ifndef PELROCK_SLIDINGPUZZLE_H
 #define PELROCK_SLIDINGPUZZLE_H
 
-#include "graphics/managed_surface.h"
-
-#include "pelrock/events.h"
-#include "pelrock/sound.h"
-
 namespace Pelrock {
 
 static const int kPuzzleScreenWidth = 640;
diff --git a/engines/pelrock/sound.h b/engines/pelrock/sound.h
index 0d93194bfa4..98e36a44f9d 100644
--- a/engines/pelrock/sound.h
+++ b/engines/pelrock/sound.h
@@ -23,9 +23,6 @@
 #define PELROCK_SOUND_H
 
 #include "audio/mixer.h"
-#include "common/file.h"
-#include "common/scummsys.h"
-#include "common/str.h"
 
 namespace Pelrock {
 
diff --git a/engines/pelrock/spellbook.cpp b/engines/pelrock/spellbook.cpp
index 21198e927f4..84f32a0d466 100644
--- a/engines/pelrock/spellbook.cpp
+++ b/engines/pelrock/spellbook.cpp
@@ -20,8 +20,12 @@
  */
 
 #include "common/events.h"
+#include "common/file.h"
+
 #include "graphics/paletteman.h"
+#include "graphics/screen.h"
 
+#include "pelrock/pelrock.h"
 #include "pelrock/events.h"
 #include "pelrock/graphics.h"
 #include "pelrock/room.h"
diff --git a/engines/pelrock/spellbook.h b/engines/pelrock/spellbook.h
index 9d944fd7d37..e32f0f1f072 100644
--- a/engines/pelrock/spellbook.h
+++ b/engines/pelrock/spellbook.h
@@ -22,10 +22,6 @@
 #ifndef PELROCK_SPELLBOOK_H
 #define PELROCK_SPELLBOOK_H
 
-#include "graphics/managed_surface.h"
-
-#include "pelrock/pelrock.h"
-
 namespace Pelrock {
 
 struct Spell {
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index e5b6b797ee2..c3c2c21227a 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -21,11 +21,8 @@
 #ifndef PELROCK_TYPES_H
 #define PELROCK_TYPES_H
 
-#include "common/debug.h"
 #include "common/rect.h"
-#include "common/scummsys.h"
 #include "common/system.h"
-#include "common/types.h"
 
 namespace Pelrock {
 
diff --git a/engines/pelrock/util.h b/engines/pelrock/util.h
index 865558294e7..836f304af51 100644
--- a/engines/pelrock/util.h
+++ b/engines/pelrock/util.h
@@ -22,12 +22,8 @@
 #define PELROCK_UTIL_H
 
 #include "common/events.h"
-#include "common/stream.h"
-#include "common/types.h"
 
 #include "graphics/font.h"
-#include "graphics/managed_surface.h"
-#include "graphics/surface.h"
 
 namespace Pelrock {
 
diff --git a/engines/pelrock/video.cpp b/engines/pelrock/video.cpp
index 447a91ca1d1..dea213120b5 100644
--- a/engines/pelrock/video.cpp
+++ b/engines/pelrock/video.cpp
@@ -25,6 +25,7 @@
 
 #include "pelrock/chrono.h"
 #include "pelrock/dialog.h"
+#include "pelrock/events.h"
 #include "pelrock/pelrock.h"
 #include "pelrock/sound.h"
 #include "pelrock/util.h"
diff --git a/engines/pelrock/video.h b/engines/pelrock/video.h
index e3ed6b7bc9b..ccdaab53fd1 100644
--- a/engines/pelrock/video.h
+++ b/engines/pelrock/video.h
@@ -22,11 +22,6 @@
 #ifndef PELROCK_VIDEO_H
 #define PELROCK_VIDEO_H
 
-#include "graphics/surface.h"
-
-#include "pelrock/events.h"
-#include "pelrock/fonts/large_font.h"
-
 namespace Pelrock {
 
 struct ChunkHeader {


Commit: 605061c7249391aa95d086c7c4d2536eed283b4c
    https://github.com/scummvm/scummvm/commit/605061c7249391aa95d086c7c4d2536eed283b4c
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2026-06-13T19:03:09+02:00

Commit Message:
PELROCK: Final pass on reducing header dependencies

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/backgroundbook.cpp
    engines/pelrock/cdplayer.cpp
    engines/pelrock/chrono.cpp
    engines/pelrock/computer.cpp
    engines/pelrock/console.cpp
    engines/pelrock/detection.cpp
    engines/pelrock/events.cpp
    engines/pelrock/graphics.cpp
    engines/pelrock/menu.cpp
    engines/pelrock/pathfinding.cpp
    engines/pelrock/pelrock.cpp
    engines/pelrock/resources.cpp
    engines/pelrock/room.cpp
    engines/pelrock/saveload.cpp
    engines/pelrock/slidingpuzzle.cpp
    engines/pelrock/sound.cpp
    engines/pelrock/spellbook.cpp
    engines/pelrock/util.cpp
    engines/pelrock/video.cpp


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index ce475dd995c..d4a847eaf63 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -21,7 +21,6 @@
 
 #include "audio/audiostream.h"
 #include "audio/decoders/raw.h"
-#include "audio/mixer.h"
 
 #include "common/file.h"
 
@@ -36,7 +35,6 @@
 #include "pelrock/events.h"
 #include "pelrock/graphics.h"
 #include "pelrock/offsets.h"
-#include "pelrock/pelrock.h"
 #include "pelrock/resources.h"
 #include "pelrock/room.h"
 #include "pelrock/sound.h"
diff --git a/engines/pelrock/backgroundbook.cpp b/engines/pelrock/backgroundbook.cpp
index 089ba924331..dafa1bc2685 100644
--- a/engines/pelrock/backgroundbook.cpp
+++ b/engines/pelrock/backgroundbook.cpp
@@ -19,7 +19,6 @@
  *
  */
 
-#include "common/events.h"
 #include "common/file.h"
 #include "graphics/paletteman.h"
 #include "graphics/screen.h"
diff --git a/engines/pelrock/cdplayer.cpp b/engines/pelrock/cdplayer.cpp
index 52cddb2efff..dd1f516ba3b 100644
--- a/engines/pelrock/cdplayer.cpp
+++ b/engines/pelrock/cdplayer.cpp
@@ -19,7 +19,6 @@
  *
  */
 
-#include "common/events.h"
 #include "common/file.h"
 
 #include "graphics/paletteman.h"
diff --git a/engines/pelrock/chrono.cpp b/engines/pelrock/chrono.cpp
index c586912e7f4..e02f7e62126 100644
--- a/engines/pelrock/chrono.cpp
+++ b/engines/pelrock/chrono.cpp
@@ -20,7 +20,6 @@
  */
 
 #include "common/events.h"
-#include "common/system.h"
 
 #include "graphics/screen.h"
 
diff --git a/engines/pelrock/computer.cpp b/engines/pelrock/computer.cpp
index 394b1183015..c07b00f6c19 100644
--- a/engines/pelrock/computer.cpp
+++ b/engines/pelrock/computer.cpp
@@ -19,9 +19,7 @@
  *
  */
 
-#include "common/events.h"
 #include "common/file.h"
-#include "common/system.h"
 
 #include "graphics/cursorman.h"
 #include "graphics/paletteman.h"
diff --git a/engines/pelrock/console.cpp b/engines/pelrock/console.cpp
index 3546269451b..1ab35dea772 100644
--- a/engines/pelrock/console.cpp
+++ b/engines/pelrock/console.cpp
@@ -19,8 +19,6 @@
  *
  */
 
-#include "console.h"
-
 #include "pelrock/console.h"
 #include "pelrock/room.h"
 #include "pelrock/types.h"
diff --git a/engines/pelrock/detection.cpp b/engines/pelrock/detection.cpp
index 90c368e4324..24cf5c212b3 100644
--- a/engines/pelrock/detection.cpp
+++ b/engines/pelrock/detection.cpp
@@ -19,14 +19,6 @@
  *
  */
 
-#include "base/plugins.h"
-#include "common/config-manager.h"
-#include "common/file.h"
-#include "common/md5.h"
-#include "common/str-array.h"
-#include "common/translation.h"
-#include "common/util.h"
-
 #include "pelrock/detection.h"
 #include "pelrock/detection_tables.h"
 
diff --git a/engines/pelrock/events.cpp b/engines/pelrock/events.cpp
index 8acd092359b..1fa11e21cef 100644
--- a/engines/pelrock/events.cpp
+++ b/engines/pelrock/events.cpp
@@ -18,8 +18,6 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
  */
-#include "common/events.h"
-
 #include "graphics/screen.h"
 
 #include "pelrock/events.h"
diff --git a/engines/pelrock/graphics.cpp b/engines/pelrock/graphics.cpp
index 37e8844a060..43b8895ece6 100644
--- a/engines/pelrock/graphics.cpp
+++ b/engines/pelrock/graphics.cpp
@@ -19,10 +19,6 @@
  *
  */
 
-#include "common/scummsys.h"
-#include "engines/util.h"
-
-#include "graphics/cursorman.h"
 #include "graphics/paletteman.h"
 #include "graphics/screen.h"
 
diff --git a/engines/pelrock/menu.cpp b/engines/pelrock/menu.cpp
index 1eaec4922d8..0523b391b7e 100644
--- a/engines/pelrock/menu.cpp
+++ b/engines/pelrock/menu.cpp
@@ -20,9 +20,8 @@
  */
 
 #include "common/config-manager.h"
-#include "common/debug.h"
 #include "common/file.h"
-#include "engines/metaengine.h"
+
 #include "graphics/paletteman.h"
 #include "graphics/screen.h"
 #include "graphics/thumbnail.h"
@@ -30,7 +29,6 @@
 #include "pelrock/events.h"
 #include "pelrock/graphics.h"
 #include "pelrock/menu.h"
-#include "pelrock/offsets.h"
 #include "pelrock/pelrock.h"
 #include "pelrock/resources.h"
 #include "pelrock/room.h"
diff --git a/engines/pelrock/pathfinding.cpp b/engines/pelrock/pathfinding.cpp
index 49fe3446ec8..bdfa93ef377 100644
--- a/engines/pelrock/pathfinding.cpp
+++ b/engines/pelrock/pathfinding.cpp
@@ -18,12 +18,7 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
  */
-#include "common/debug.h"
-#include "common/scummsys.h"
-
 #include "pelrock/pathfinding.h"
-#include "pelrock/types.h"
-#include "pelrock/util.h"
 
 namespace Pelrock {
 
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 1d08953a428..dfb92fd487e 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -20,9 +20,7 @@
  */
 
 #include "common/config-manager.h"
-#include "common/events.h"
 #include "common/file.h"
-#include "common/scummsys.h"
 
 #include "engines/util.h"
 
@@ -34,7 +32,6 @@
 #include "pelrock/chrono.h"
 #include "pelrock/computer.h"
 #include "pelrock/console.h"
-#include "pelrock/detection.h"
 #include "pelrock/dialog.h"
 #include "pelrock/events.h"
 #include "pelrock/fonts/large_font.h"
@@ -44,7 +41,6 @@
 #include "pelrock/menu.h"
 #include "pelrock/offsets.h"
 #include "pelrock/pathfinding.h"
-#include "pelrock/pelrock.h"
 #include "pelrock/resources.h"
 #include "pelrock/room.h"
 #include "pelrock/slidingpuzzle.h"
diff --git a/engines/pelrock/resources.cpp b/engines/pelrock/resources.cpp
index ebd91933a37..311d9cdf1f2 100644
--- a/engines/pelrock/resources.cpp
+++ b/engines/pelrock/resources.cpp
@@ -24,8 +24,6 @@
 #include "pelrock/dialog.h"
 #include "pelrock/resources.h"
 #include "pelrock/offsets.h"
-#include "pelrock/pelrock.h"
-#include "pelrock/room.h"
 #include "pelrock/util.h"
 
 namespace Pelrock {
diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index 61d6c332e32..b787a94ad74 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -18,16 +18,14 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
  */
-#include "common/scummsys.h"
 #include "common/file.h"
 
 #include "graphics/screen.h"
 
 #include "pelrock/pelrock.h"
-#include "pelrock/resources.h"
 #include "pelrock/room.h"
+#include "pelrock/resources.h"
 #include "pelrock/util.h"
-#include "pelrock/room.h"
 
 namespace Pelrock {
 
diff --git a/engines/pelrock/saveload.cpp b/engines/pelrock/saveload.cpp
index 98926962aa0..b8b3d2a3ebd 100644
--- a/engines/pelrock/saveload.cpp
+++ b/engines/pelrock/saveload.cpp
@@ -23,7 +23,6 @@
 #include "pelrock/pelrock.h"
 #include "pelrock/room.h"
 #include "pelrock/resources.h"
-#include "pelrock/types.h"
 
 namespace Pelrock {
 
diff --git a/engines/pelrock/slidingpuzzle.cpp b/engines/pelrock/slidingpuzzle.cpp
index e8bdf8cea0f..0ee4e96a70f 100644
--- a/engines/pelrock/slidingpuzzle.cpp
+++ b/engines/pelrock/slidingpuzzle.cpp
@@ -19,14 +19,10 @@
  *
  */
 
-#include "common/system.h"
-
-#include "graphics/paletteman.h"
 #include "graphics/screen.h"
 
 #include "pelrock/events.h"
 #include "pelrock/pelrock.h"
-#include "pelrock/room.h"
 #include "pelrock/slidingpuzzle.h"
 #include "pelrock/sound.h"
 
diff --git a/engines/pelrock/sound.cpp b/engines/pelrock/sound.cpp
index 5457c06fd11..54f1fdb0c55 100644
--- a/engines/pelrock/sound.cpp
+++ b/engines/pelrock/sound.cpp
@@ -25,17 +25,13 @@
 #include "audio/mixer.h"
 
 #include "common/config-manager.h"
-#include "common/debug.h"
-#include "common/endian.h"
 #include "common/file.h"
 #include "common/memstream.h"
-#include "common/scummsys.h"
 
 #include "backends/audiocd/audiocd.h"
 
 #include "pelrock/pelrock.h"
 #include "pelrock/sound.h"
-#include "sound.h"
 
 namespace Pelrock {
 
diff --git a/engines/pelrock/spellbook.cpp b/engines/pelrock/spellbook.cpp
index 84f32a0d466..5b2fba910bb 100644
--- a/engines/pelrock/spellbook.cpp
+++ b/engines/pelrock/spellbook.cpp
@@ -19,7 +19,6 @@
  *
  */
 
-#include "common/events.h"
 #include "common/file.h"
 
 #include "graphics/paletteman.h"
diff --git a/engines/pelrock/util.cpp b/engines/pelrock/util.cpp
index 592fa3f99c7..e824b2e4bf6 100644
--- a/engines/pelrock/util.cpp
+++ b/engines/pelrock/util.cpp
@@ -18,15 +18,11 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
  */
-#include "common/stream.h"
 
-#include "graphics/cursorman.h"
-#include "graphics/font.h"
 #include "graphics/screen.h"
 
 #include "pelrock/pelrock.h"
 #include "pelrock/chrono.h"
-#include "pelrock/types.h"
 #include "pelrock/util.h"
 
 namespace Pelrock {
diff --git a/engines/pelrock/video.cpp b/engines/pelrock/video.cpp
index dea213120b5..00917ec87a5 100644
--- a/engines/pelrock/video.cpp
+++ b/engines/pelrock/video.cpp
@@ -20,6 +20,7 @@
  */
 
 #include "common/file.h"
+
 #include "graphics/paletteman.h"
 #include "graphics/screen.h"
 
@@ -28,7 +29,6 @@
 #include "pelrock/events.h"
 #include "pelrock/pelrock.h"
 #include "pelrock/sound.h"
-#include "pelrock/util.h"
 #include "pelrock/video.h"
 
 namespace Pelrock {



From noreply at scummvm.org  Sat Jun 13 18:00:48 2026
From: noreply at scummvm.org (sev-)
Date: Sat, 13 Jun 2026 18:00:48 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm-sites director-buildbot ->
 c9bbcf1f1fd0bb98ca8b765df1e87fc4d2f4b44b
Message-ID: <20260613180048.2E71E7005BC@ron.scummvm.net>

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

Summary:
881dbb814a BUILDBOT: Add imagediff tool
29eed1586c BUILDBOT: Add flask to pyproject.toml and update uv.lock
c9bbcf1f1f BUILDBOT: Fix imagediff import and move utility functions to imagediff


Commit: 881dbb814a0244e7dd248f778feac95728399711
    https://github.com/scummvm/scummvm-sites/commit/881dbb814a0244e7dd248f778feac95728399711
Author: ramyak-sharma (ramyaksharma1 at gmail.com)
Date: 2026-06-13T20:00:44+02:00

Commit Message:
BUILDBOT: Add imagediff tool

Changed paths:
  A director/screenshot_diff.py
    director/env.py
    director/steps.py
    director/targets.py
    pyproject.toml


diff --git a/director/env.py b/director/env.py
index 39abdf8..290a46e 100644
--- a/director/env.py
+++ b/director/env.py
@@ -23,6 +23,10 @@ default_vars = {
     # base directory which contains all the targets
     "TARGETS_BASEDIR": "~/wb1/",
     "UI": False,
+    # Screenshot comparison (ImageDiff integration)
+    "SCREENSHOTS_DIR": "/home/director-buildbot/screenshots",
+    "IMAGEDIFF_URL": "",
+    "SCREENSHOT_DIFF_STRICT": False,
 }
 
 
diff --git a/director/screenshot_diff.py b/director/screenshot_diff.py
new file mode 100644
index 0000000..6173c7e
--- /dev/null
+++ b/director/screenshot_diff.py
@@ -0,0 +1,267 @@
+"""Screenshot comparison for Director buildbot tests.
+
+Uses comparison logic from imagediff/imagediff.py. Invoked on the worker by
+ScreenshotDiffStep after ScummVMTest when screenshot debugflags are enabled.
+"""
+
+from __future__ import annotations
+
+import argparse
+import importlib.util
+import os
+import sys
+import urllib.parse
+from typing import Callable, Optional
+
+EXIT_OK = 0
+EXIT_DIFF = 1
+EXIT_NO_BASELINE = 2
+EXIT_ERROR = 3
+
+_repo_root = os.path.dirname(os.path.dirname(__file__))
+_seen_prefixes_name = ".screenshot_prefixes_seen"
+
+
+def _load_movie_diff(screenshots_dir: str) -> Callable[..., bool]:
+    """Load movie_diff from the vendored imagediff package."""
+    os.environ["SCREENSHOTS_DIR"] = screenshots_dir
+    imagediff_dir = os.path.join(_repo_root, "imagediff")
+
+    config_spec = importlib.util.spec_from_file_location(
+        "imagediff_config", os.path.join(imagediff_dir, "config.py")
+    )
+    if config_spec is None or config_spec.loader is None:
+        raise ImportError("unable to load imagediff config")
+    config_module = importlib.util.module_from_spec(config_spec)
+    sys.modules["config"] = config_module
+    config_spec.loader.exec_module(config_module)
+
+    core_spec = importlib.util.spec_from_file_location(
+        "imagediff_core", os.path.join(imagediff_dir, "imagediff.py")
+    )
+    if core_spec is None or core_spec.loader is None:
+        raise ImportError("unable to load imagediff core")
+    core_module = importlib.util.module_from_spec(core_spec)
+    core_spec.loader.exec_module(core_module)
+    return core_module.movie_diff
+
+
+def list_movie_prefixes(build_path: str) -> list[str]:
+    """Return sorted movie filename prefixes in a build directory."""
+    prefixes: set[str] = set()
+    if not os.path.isdir(build_path):
+        return []
+
+    for name in os.listdir(build_path):
+        if not name.endswith(".png") or "-" not in name:
+            continue
+        prefixes.add(name.rsplit("-", 1)[0])
+
+    return sorted(prefixes)
+
+
+def _seen_prefixes_path(screenshots_dir: str, target: str, build: str) -> str:
+    return os.path.join(screenshots_dir, target, build, _seen_prefixes_name)
+
+
+def _read_seen_prefixes(path: str) -> set[str]:
+    if not os.path.isfile(path):
+        return set()
+    with open(path, encoding="utf-8") as handle:
+        return {line.strip() for line in handle if line.strip()}
+
+
+def _write_seen_prefixes(path: str, prefixes: set[str]) -> None:
+    with open(path, "w", encoding="utf-8") as handle:
+        for prefix in sorted(prefixes):
+            handle.write(f"{prefix}\n")
+
+
+def prefixes_for_step(
+    screenshots_dir: str,
+    target: str,
+    build: str,
+    *,
+    only_new: bool,
+) -> list[str]:
+    """Return prefixes to compare for this step."""
+    build_path = os.path.join(screenshots_dir, target, build)
+    current = set(list_movie_prefixes(build_path))
+    if not only_new:
+        return sorted(current)
+
+    seen_path = _seen_prefixes_path(screenshots_dir, target, build)
+    seen = _read_seen_prefixes(seen_path)
+    new_prefixes = sorted(current - seen)
+    _write_seen_prefixes(seen_path, seen | set(new_prefixes))
+    return new_prefixes
+
+
+def find_baseline_build(
+    screenshots_dir: str, target: str, current_build: str
+) -> Optional[str]:
+    """Return the highest prior build number that has stored screenshots."""
+    target_path = os.path.join(screenshots_dir, target)
+    if not os.path.isdir(target_path):
+        return None
+
+    try:
+        current_number = int(current_build)
+    except ValueError:
+        return None
+
+    candidates: list[int] = []
+    for entry in os.listdir(target_path):
+        path = os.path.join(target_path, entry)
+        if not os.path.isdir(path):
+            continue
+        try:
+            build_number = int(entry)
+        except ValueError:
+            continue
+        if build_number < current_number:
+            candidates.append(build_number)
+
+    if not candidates:
+        return None
+
+    return str(max(candidates))
+
+
+def compare_url(
+    imagediff_url: str,
+    current_build: str,
+    baseline_build: str,
+    target: str,
+    movie_prefix: str,
+) -> str:
+    """Build an ImageDiff compare URL for a movie prefix."""
+    base = imagediff_url.rstrip("/")
+    movie = urllib.parse.quote(movie_prefix, safe="")
+    return f"{base}/compare/{current_build}/{baseline_build}/{target}/{movie}"
+
+
+def check_screenshots(
+    screenshots_dir: str,
+    target: str,
+    current_build: str,
+    baseline_build: Optional[str] = None,
+    movie_prefix: Optional[str] = None,
+    *,
+    only_new: bool = True,
+) -> tuple[int, str]:
+    """Compare screenshots and return an exit code plus a human-readable summary."""
+    current_path = os.path.join(screenshots_dir, target, current_build)
+    if not os.path.isdir(current_path):
+        return EXIT_NO_BASELINE, (
+            f"No screenshots found for target '{target}' build {current_build} "
+            f"under {screenshots_dir}"
+        )
+
+    if baseline_build is None:
+        baseline_build = find_baseline_build(screenshots_dir, target, current_build)
+
+    if baseline_build is None:
+        return EXIT_NO_BASELINE, (
+            f"No baseline screenshots for target '{target}' before build {current_build}"
+        )
+
+    if movie_prefix:
+        prefixes = [movie_prefix]
+    elif only_new:
+        prefixes = prefixes_for_step(
+            screenshots_dir, target, current_build, only_new=True
+        )
+    else:
+        prefixes = list_movie_prefixes(current_path)
+
+    if not prefixes:
+        return EXIT_NO_BASELINE, (
+            f"No new screenshot frames to compare for target '{target}' "
+            f"build {current_build}"
+        )
+
+    try:
+        movie_diff = _load_movie_diff(screenshots_dir)
+    except ImportError as exc:
+        return EXIT_ERROR, f"Failed to load imagediff: {exc}"
+
+    diff_prefixes = [
+        prefix
+        for prefix in prefixes
+        if movie_diff(current_build, baseline_build, target, prefix)
+    ]
+
+    if not diff_prefixes:
+        return EXIT_OK, (
+            f"Screenshots match baseline build {baseline_build} "
+            f"for target '{target}' build {current_build}"
+        )
+
+    lines = [
+        "Screenshot differences detected:",
+        f"  target: {target}",
+        f"  current build: {current_build}",
+        f"  baseline build: {baseline_build}",
+        "  changed movies:",
+    ]
+    lines.extend(f"    - {prefix}" for prefix in diff_prefixes)
+    return EXIT_DIFF, "\n".join(lines)
+
+
+def main(argv: Optional[list[str]] = None) -> int:
+    parser = argparse.ArgumentParser(description="Compare Director buildbot screenshots")
+    parser.add_argument("--screenshots-dir", default=os.environ.get("SCREENSHOTS_DIR", ""))
+    parser.add_argument("--target", required=True, help="ScummVM target name (game_id)")
+    parser.add_argument("--build", required=True, help="Current build number")
+    parser.add_argument("--baseline", help="Baseline build number (default: previous build)")
+    parser.add_argument("--movie-prefix", help="Compare only this ScummVM movie prefix")
+    parser.add_argument(
+        "--all-prefixes",
+        action="store_true",
+        help="Compare every prefix in the build directory, not only new ones",
+    )
+    parser.add_argument("--imagediff-url", default=os.environ.get("IMAGEDIFF_URL", ""))
+    args = parser.parse_args(argv)
+
+    if not args.screenshots_dir:
+        print("SCREENSHOTS_DIR is not configured", file=sys.stderr)
+        return EXIT_ERROR
+
+    exit_code, message = check_screenshots(
+        args.screenshots_dir,
+        args.target,
+        args.build,
+        baseline_build=args.baseline,
+        movie_prefix=args.movie_prefix,
+        only_new=not args.all_prefixes,
+    )
+
+    print(message)
+    if exit_code == EXIT_DIFF and args.imagediff_url:
+        baseline = args.baseline or find_baseline_build(
+            args.screenshots_dir, args.target, args.build
+        )
+        if baseline:
+            diff_prefixes = [
+                line.removeprefix("    - ")
+                for line in message.splitlines()
+                if line.startswith("    - ")
+            ]
+            print("\nCompare in ImageDiff:")
+            for prefix in diff_prefixes:
+                print(
+                    compare_url(
+                        args.imagediff_url,
+                        args.build,
+                        baseline,
+                        args.target,
+                        prefix,
+                    )
+                )
+
+    return exit_code
+
+
+if __name__ == "__main__":
+    sys.exit(main())
diff --git a/director/steps.py b/director/steps.py
index 95c2110..bbf98c0 100644
--- a/director/steps.py
+++ b/director/steps.py
@@ -1,15 +1,18 @@
+import os
 import re
 from typing import Any, Optional
 
 from buildbot import config
 from buildbot.plugins import steps, util
 from buildbot.process import buildstep, logobserver
-from buildbot.process.properties import Property
-from buildbot.process.results import FAILURE, worst_status
+from buildbot.process.results import FAILURE, SUCCESS, WARNINGS, worst_status
 from twisted.internet import defer
 
 from .build_factory import default_env, master_file, worker_file
 from .env import settings
+from .screenshot_diff import EXIT_DIFF, EXIT_ERROR, EXIT_NO_BASELINE
+
+_master_dir = os.path.dirname(os.path.dirname(__file__))
 
 download_step = steps.FileDownload(
     mastersrc=master_file,
@@ -103,6 +106,70 @@ class ScummVMTest(steps.WarningCountingShellCommand):
         return result
 
 
+class ScreenshotDiffStep(steps.ShellCommand):
+    """Compare screenshots from the current build against the previous build."""
+
+    renderables = ["command", "env"]
+
+    def __init__(
+        self,
+        target_key: str,
+        movie_prefix: Optional[str] = None,
+        strict: Optional[bool] = None,
+        **kwargs: Any,
+    ):
+        if strict is None:
+            strict_value = settings["SCREENSHOT_DIFF_STRICT"]
+            if isinstance(strict_value, bool):
+                strict = strict_value
+            else:
+                strict = str(strict_value).lower() in ("1", "true", "yes")
+        self.strict = strict
+
+        env = default_env.copy()
+        imagediff_dir = os.path.join(_master_dir, "imagediff")
+        env["PYTHONPATH"] = os.pathsep.join([_master_dir, imagediff_dir])
+        env["SCREENSHOTS_DIR"] = settings["SCREENSHOTS_DIR"]
+        if settings["IMAGEDIFF_URL"]:
+            env["IMAGEDIFF_URL"] = settings["IMAGEDIFF_URL"]
+
+        command = [
+            "python3",
+            "-m",
+            "director.screenshot_diff",
+            "--screenshots-dir",
+            settings["SCREENSHOTS_DIR"],
+            "--target",
+            target_key,
+            "--build",
+            util.Interpolate("%(prop:buildnumber)s"),
+        ]
+        if movie_prefix:
+            command.extend(["--movie-prefix", movie_prefix])
+        if settings["IMAGEDIFF_URL"]:
+            command.extend(["--imagediff-url", settings["IMAGEDIFF_URL"]])
+
+        kwargs.setdefault("haltOnFailure", False)
+        kwargs.setdefault("flunkOnFailure", False)
+        kwargs.setdefault("warnOnFailure", False)
+        kwargs["command"] = command
+        kwargs["env"] = env
+        kwargs["logEnviron"] = False
+        super().__init__(**kwargs)
+
+    def evaluateCommand(self, cmd):
+        exit_code = cmd.exitCode
+        if exit_code in (0, None):
+            return SUCCESS
+        if exit_code == EXIT_NO_BASELINE:
+            return SUCCESS
+        if exit_code == EXIT_DIFF:
+            return FAILURE if self.strict else WARNINGS
+        if exit_code == EXIT_ERROR:
+            return FAILURE
+        return super().evaluateCommand(cmd)
+
+
 class GenerateStartMovieCommands(buildstep.ShellMixin, steps.BuildStep):
     """Generate the steps to build all lingo files."""
 
diff --git a/director/targets.py b/director/targets.py
index 1e0535e..8a07925 100644
--- a/director/targets.py
+++ b/director/targets.py
@@ -7,7 +7,7 @@ from buildbot.plugins import steps, util
 
 from .build_factory import default_env
 from .env import settings
-from .steps import ScummVMTest, download_step
+from .steps import ScummVMTest, ScreenshotDiffStep, download_step
 
 
 @dataclass(frozen=True)
@@ -61,7 +61,7 @@ def generate_command(target: TestTarget, moviename: str) -> list[str]:
         "../scummvm",
         "-c",
         "scummvm.conf",
-        "--screenshotpath=/home/director-buildbot/screenshots",
+        f"--screenshotpath={settings['SCREENSHOTS_DIR']}",
         f"--start-movie={moviename}",
     ]
     if target.debugflags:
@@ -96,6 +96,7 @@ def generate_builder(target: TestTarget, workernames: list[str]) -> BuilderConfi
             logEnviron=False,
         )
     )
+    screenshot_enabled = "screenshot" in target.debugflags
     for moviename in target.movienames:
         name = moviename
         env = default_env.copy()
@@ -120,6 +121,18 @@ def generate_builder(target: TestTarget, workernames: list[str]) -> BuilderConfi
                 logEnviron=False,
             )
         )
+        if screenshot_enabled:
+            diff_name = f"{name} screenshot diff"
+            if len(diff_name) > 49:
+                diff_name = diff_name[-49:]
+            factory.addStep(
+                ScreenshotDiffStep(
+                    name=diff_name,
+                    description=f"Compare screenshots for {moviename}",
+                    descriptionDone=f"Compared screenshots for {moviename}",
+                    target_key=target.game_id,
+                )
+            )
 
     return BuilderConfig(
         name=target.builder_name, workernames=workernames, factory=factory
diff --git a/pyproject.toml b/pyproject.toml
index d445571..04a4306 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -10,6 +10,7 @@ dependencies = [
   "buildbot[bundle]<4.4.0",
   "psycopg2>=2.9.5,<3.0.0",
   "certifi",
+  "Pillow>=10.0.0",
 ]
 
 [dependency-groups]


Commit: 29eed1586cf022ee2b2a903a498a2478d24dd9f8
    https://github.com/scummvm/scummvm-sites/commit/29eed1586cf022ee2b2a903a498a2478d24dd9f8
Author: ramyak-sharma (ramyaksharma1 at gmail.com)
Date: 2026-06-13T20:00:44+02:00

Commit Message:
BUILDBOT: Add flask to pyproject.toml and update uv.lock

Changed paths:
    pyproject.toml
    uv.lock


diff --git a/pyproject.toml b/pyproject.toml
index 04a4306..027b0ee 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -11,6 +11,7 @@ dependencies = [
   "psycopg2>=2.9.5,<3.0.0",
   "certifi",
   "Pillow>=10.0.0",
+  "flask>=3.0.0",
 ]
 
 [dependency-groups]
diff --git a/uv.lock b/uv.lock
index 59ec676..9bc047d 100644
--- a/uv.lock
+++ b/uv.lock
@@ -238,6 +238,15 @@ wheels = [
     { url = "https://files.pythonhosted.org/packages/94/51/f975cae76d44274cc2868dc9040ac5d58d464784610234455b4e7b19c6ef/black-26.5.1-py3-none-any.whl", hash = "sha256:4ed7f7da04046d2e488437170797d3b4a4ad83906683bcb7dfc68b673bbce5e2", size = 213693, upload-time = "2026-05-18T16:53:33.964Z" },
 ]
 
+[[package]]
+name = "blinker"
+version = "1.9.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/21/28/9b3f50ce0e048515135495f198351908d99540d69bfdc8c1d15b73dc55ce/blinker-1.9.0.tar.gz", hash = "sha256:b4ce2265a7abece45e7cc896e98dbebe6cead56bcf805a3d23136d145f5445bf", size = 22460, upload-time = "2024-11-08T17:25:47.436Z" }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/10/cb/f2ad4230dc2eb1a74edf38f1a38b9b52277f75bef262d8908e60d957e13c/blinker-1.9.0-py3-none-any.whl", hash = "sha256:ba0efaa9080b619ff2f3459d1d500c57bddea4a6b424b60a91141db6fd2f08bc", size = 8458, upload-time = "2024-11-08T17:25:46.184Z" },
+]
+
 [[package]]
 name = "buildbot"
 version = "4.3.0"
@@ -293,6 +302,8 @@ dependencies = [
     { name = "buildbot", extra = ["bundle"] },
     { name = "certifi" },
     { name = "environs" },
+    { name = "flask" },
+    { name = "pillow" },
     { name = "psycopg2" },
     { name = "treq" },
 ]
@@ -311,6 +322,8 @@ requires-dist = [
     { name = "buildbot", extras = ["bundle"], specifier = "<4.4.0" },
     { name = "certifi" },
     { name = "environs" },
+    { name = "flask", specifier = ">=3.0.0" },
+    { name = "pillow", specifier = ">=10.0.0" },
     { name = "psycopg2", specifier = ">=2.9.5,<3.0.0" },
     { name = "treq" },
 ]
@@ -776,6 +789,23 @@ wheels = [
     { url = "https://files.pythonhosted.org/packages/c1/ea/53f2148663b321f21b5a606bd5f191517cf40b7072c0497d3c92c4a13b1e/executing-2.2.1-py2.py3-none-any.whl", hash = "sha256:760643d3452b4d777d295bb167ccc74c64a81df23fb5e08eff250c425a4b2017", size = 28317, upload-time = "2025-09-01T09:48:08.5Z" },
 ]
 
+[[package]]
+name = "flask"
+version = "3.1.3"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "blinker" },
+    { name = "click" },
+    { name = "itsdangerous" },
+    { name = "jinja2" },
+    { name = "markupsafe" },
+    { name = "werkzeug" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/26/00/35d85dcce6c57fdc871f3867d465d780f302a175ea360f62533f12b27e2b/flask-3.1.3.tar.gz", hash = "sha256:0ef0e52b8a9cd932855379197dd8f94047b359ca0a78695144304cb45f87c9eb", size = 759004, upload-time = "2026-02-19T05:00:57.678Z" }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/7f/9c/34f6962f9b9e9c71f6e5ed806e0d0ff03c9d1b0b2340088a0cf4bce09b18/flask-3.1.3-py3-none-any.whl", hash = "sha256:f4bcbefc124291925f1a26446da31a5178f9483862233b23c0c96a20701f670c", size = 103424, upload-time = "2026-02-19T05:00:56.027Z" },
+]
+
 [[package]]
 name = "greenlet"
 version = "3.5.1"
@@ -969,6 +999,15 @@ wheels = [
     { url = "https://files.pythonhosted.org/packages/3e/95/c7c34aa53c16353c56d0b802fba48d5f5caa2cdee7958acbcb795c830416/isort-8.0.1-py3-none-any.whl", hash = "sha256:28b89bc70f751b559aeca209e6120393d43fbe2490de0559662be7a9787e3d75", size = 89733, upload-time = "2026-02-28T10:08:19.466Z" },
 ]
 
+[[package]]
+name = "itsdangerous"
+version = "2.2.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/9c/cb/8ac0172223afbccb63986cc25049b154ecfb5e85932587206f42317be31d/itsdangerous-2.2.0.tar.gz", hash = "sha256:e0050c0b7da1eea53ffaf149c0cfbb5c6e2e2b69c4bef22c81fa6eb73e5f6173", size = 54410, upload-time = "2024-04-16T21:28:15.614Z" }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/04/96/92447566d16df59b2a776c0fb82dbc4d9e07cd95062562af01e408583fc4/itsdangerous-2.2.0-py3-none-any.whl", hash = "sha256:c6242fc49e35958c8b15141343aa660db5fc54d4f13a1db01a3f5891b98700ef", size = 16234, upload-time = "2024-04-16T21:28:14.499Z" },
+]
+
 [[package]]
 name = "jedi"
 version = "0.20.0"
@@ -1386,6 +1425,104 @@ wheels = [
     { url = "https://files.pythonhosted.org/packages/9e/c3/059298687310d527a58bb01f3b1965787ee3b40dce76752eda8b44e9a2c5/pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523", size = 63772, upload-time = "2023-11-25T06:56:14.81Z" },
 ]
 
+[[package]]
+name = "pillow"
+version = "12.2.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/8c/21/c2bcdd5906101a30244eaffc1b6e6ce71a31bd0742a01eb89e660ebfac2d/pillow-12.2.0.tar.gz", hash = "sha256:a830b1a40919539d07806aa58e1b114df53ddd43213d9c8b75847eee6c0182b5", size = 46987819, upload-time = "2026-04-01T14:46:17.687Z" }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/3a/aa/d0b28e1c811cd4d5f5c2bfe2e022292bd255ae5744a3b9ac7d6c8f72dd75/pillow-12.2.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:a4e8f36e677d3336f35089648c8955c51c6d386a13cf6ee9c189c5f5bd713a9f", size = 5354355, upload-time = "2026-04-01T14:42:15.402Z" },
+    { url = "https://files.pythonhosted.org/packages/27/8e/1d5b39b8ae2bd7650d0c7b6abb9602d16043ead9ebbfef4bc4047454da2a/pillow-12.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2e589959f10d9824d39b350472b92f0ce3b443c0a3442ebf41c40cb8361c5b97", size = 4695871, upload-time = "2026-04-01T14:42:18.234Z" },
+    { url = "https://files.pythonhosted.org/packages/f0/c5/dcb7a6ca6b7d3be41a76958e90018d56c8462166b3ef223150360850c8da/pillow-12.2.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:a52edc8bfff4429aaabdf4d9ee0daadbbf8562364f940937b941f87a4290f5ff", size = 6269734, upload-time = "2026-04-01T14:42:20.608Z" },
+    { url = "https://files.pythonhosted.org/packages/ea/f1/aa1bb13b2f4eba914e9637893c73f2af8e48d7d4023b9d3750d4c5eb2d0c/pillow-12.2.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:975385f4776fafde056abb318f612ef6285b10a1f12b8570f3647ad0d74b48ec", size = 8076080, upload-time = "2026-04-01T14:42:23.095Z" },
+    { url = "https://files.pythonhosted.org/packages/a1/2a/8c79d6a53169937784604a8ae8d77e45888c41537f7f6f65ed1f407fe66d/pillow-12.2.0-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bd9c0c7a0c681a347b3194c500cb1e6ca9cab053ea4d82a5cf45b6b754560136", size = 6382236, upload-time = "2026-04-01T14:42:25.82Z" },
+    { url = "https://files.pythonhosted.org/packages/b5/42/bbcb6051030e1e421d103ce7a8ecadf837aa2f39b8f82ef1a8d37c3d4ebc/pillow-12.2.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:88d387ff40b3ff7c274947ed3125dedf5262ec6919d83946753b5f3d7c67ea4c", size = 7070220, upload-time = "2026-04-01T14:42:28.68Z" },
+    { url = "https://files.pythonhosted.org/packages/3f/e1/c2a7d6dd8cfa6b231227da096fd2d58754bab3603b9d73bf609d3c18b64f/pillow-12.2.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:51c4167c34b0d8ba05b547a3bb23578d0ba17b80a5593f93bd8ecb123dd336a3", size = 6493124, upload-time = "2026-04-01T14:42:31.579Z" },
+    { url = "https://files.pythonhosted.org/packages/5f/41/7c8617da5d32e1d2f026e509484fdb6f3ad7efaef1749a0c1928adbb099e/pillow-12.2.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:34c0d99ecccea270c04882cb3b86e7b57296079c9a4aff88cb3b33563d95afaa", size = 7194324, upload-time = "2026-04-01T14:42:34.615Z" },
+    { url = "https://files.pythonhosted.org/packages/2d/de/a777627e19fd6d62f84070ee1521adde5eeda4855b5cf60fe0b149118bca/pillow-12.2.0-cp310-cp310-win32.whl", hash = "sha256:b85f66ae9eb53e860a873b858b789217ba505e5e405a24b85c0464822fe88032", size = 6376363, upload-time = "2026-04-01T14:42:37.19Z" },
+    { url = "https://files.pythonhosted.org/packages/e7/34/fc4cb5204896465842767b96d250c08410f01f2f28afc43b257de842eed5/pillow-12.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:673aa32138f3e7531ccdbca7b3901dba9b70940a19ccecc6a37c77d5fdeb05b5", size = 7083523, upload-time = "2026-04-01T14:42:39.62Z" },
+    { url = "https://files.pythonhosted.org/packages/2d/a0/32852d36bc7709f14dc3f64f929a275e958ad8c19a6deba9610d458e28b3/pillow-12.2.0-cp310-cp310-win_arm64.whl", hash = "sha256:3e080565d8d7c671db5802eedfb438e5565ffa40115216eabb8cd52d0ecce024", size = 2463318, upload-time = "2026-04-01T14:42:42.063Z" },
+    { url = "https://files.pythonhosted.org/packages/68/e1/748f5663efe6edcfc4e74b2b93edfb9b8b99b67f21a854c3ae416500a2d9/pillow-12.2.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:8be29e59487a79f173507c30ddf57e733a357f67881430449bb32614075a40ab", size = 5354347, upload-time = "2026-04-01T14:42:44.255Z" },
+    { url = "https://files.pythonhosted.org/packages/47/a1/d5ff69e747374c33a3b53b9f98cca7889fce1fd03d79cdc4e1bccc6c5a87/pillow-12.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:71cde9a1e1551df7d34a25462fc60325e8a11a82cc2e2f54578e5e9a1e153d65", size = 4695873, upload-time = "2026-04-01T14:42:46.452Z" },
+    { url = "https://files.pythonhosted.org/packages/df/21/e3fbdf54408a973c7f7f89a23b2cb97a7ef30c61ab4142af31eee6aebc88/pillow-12.2.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f490f9368b6fc026f021db16d7ec2fbf7d89e2edb42e8ec09d2c60505f5729c7", size = 6280168, upload-time = "2026-04-01T14:42:49.228Z" },
+    { url = "https://files.pythonhosted.org/packages/d3/f1/00b7278c7dd52b17ad4329153748f87b6756ec195ff786c2bdf12518337d/pillow-12.2.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8bd7903a5f2a4545f6fd5935c90058b89d30045568985a71c79f5fd6edf9b91e", size = 8088188, upload-time = "2026-04-01T14:42:51.735Z" },
+    { url = "https://files.pythonhosted.org/packages/ad/cf/220a5994ef1b10e70e85748b75649d77d506499352be135a4989c957b701/pillow-12.2.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3997232e10d2920a68d25191392e3a4487d8183039e1c74c2297f00ed1c50705", size = 6394401, upload-time = "2026-04-01T14:42:54.343Z" },
+    { url = "https://files.pythonhosted.org/packages/e9/bd/e51a61b1054f09437acfbc2ff9106c30d1eb76bc1453d428399946781253/pillow-12.2.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e74473c875d78b8e9d5da2a70f7099549f9eb37ded4e2f6a463e60125bccd176", size = 7079655, upload-time = "2026-04-01T14:42:56.954Z" },
+    { url = "https://files.pythonhosted.org/packages/6b/3d/45132c57d5fb4b5744567c3817026480ac7fc3ce5d4c47902bc0e7f6f853/pillow-12.2.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:56a3f9c60a13133a98ecff6197af34d7824de9b7b38c3654861a725c970c197b", size = 6503105, upload-time = "2026-04-01T14:42:59.847Z" },
+    { url = "https://files.pythonhosted.org/packages/7d/2e/9df2fc1e82097b1df3dce58dc43286aa01068e918c07574711fcc53e6fb4/pillow-12.2.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:90e6f81de50ad6b534cab6e5aef77ff6e37722b2f5d908686f4a5c9eba17a909", size = 7203402, upload-time = "2026-04-01T14:43:02.664Z" },
+    { url = "https://files.pythonhosted.org/packages/bd/2e/2941e42858ebb67e50ae741473de81c2984e6eff7b397017623c676e2e8d/pillow-12.2.0-cp311-cp311-win32.whl", hash = "sha256:8c984051042858021a54926eb597d6ee3012393ce9c181814115df4c60b9a808", size = 6378149, upload-time = "2026-04-01T14:43:05.274Z" },
+    { url = "https://files.pythonhosted.org/packages/69/42/836b6f3cd7f3e5fa10a1f1a5420447c17966044c8fbf589cc0452d5502db/pillow-12.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:6e6b2a0c538fc200b38ff9eb6628228b77908c319a005815f2dde585a0664b60", size = 7082626, upload-time = "2026-04-01T14:43:08.557Z" },
+    { url = "https://files.pythonhosted.org/packages/c2/88/549194b5d6f1f494b485e493edc6693c0a16f4ada488e5bd974ed1f42fad/pillow-12.2.0-cp311-cp311-win_arm64.whl", hash = "sha256:9a8a34cc89c67a65ea7437ce257cea81a9dad65b29805f3ecee8c8fe8ff25ffe", size = 2463531, upload-time = "2026-04-01T14:43:10.743Z" },
+    { url = "https://files.pythonhosted.org/packages/58/be/7482c8a5ebebbc6470b3eb791812fff7d5e0216c2be3827b30b8bb6603ed/pillow-12.2.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2d192a155bbcec180f8564f693e6fd9bccff5a7af9b32e2e4bf8c9c69dbad6b5", size = 5308279, upload-time = "2026-04-01T14:43:13.246Z" },
+    { url = "https://files.pythonhosted.org/packages/d8/95/0a351b9289c2b5cbde0bacd4a83ebc44023e835490a727b2a3bd60ddc0f4/pillow-12.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f3f40b3c5a968281fd507d519e444c35f0ff171237f4fdde090dd60699458421", size = 4695490, upload-time = "2026-04-01T14:43:15.584Z" },
+    { url = "https://files.pythonhosted.org/packages/de/af/4e8e6869cbed569d43c416fad3dc4ecb944cb5d9492defaed89ddd6fe871/pillow-12.2.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:03e7e372d5240cc23e9f07deca4d775c0817bffc641b01e9c3af208dbd300987", size = 6284462, upload-time = "2026-04-01T14:43:18.268Z" },
+    { url = "https://files.pythonhosted.org/packages/e9/9e/c05e19657fd57841e476be1ab46c4d501bffbadbafdc31a6d665f8b737b6/pillow-12.2.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b86024e52a1b269467a802258c25521e6d742349d760728092e1bc2d135b4d76", size = 8094744, upload-time = "2026-04-01T14:43:20.716Z" },
+    { url = "https://files.pythonhosted.org/packages/2b/54/1789c455ed10176066b6e7e6da1b01e50e36f94ba584dc68d9eebfe9156d/pillow-12.2.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7371b48c4fa448d20d2714c9a1f775a81155050d383333e0a6c15b1123dda005", size = 6398371, upload-time = "2026-04-01T14:43:23.443Z" },
+    { url = "https://files.pythonhosted.org/packages/43/e3/fdc657359e919462369869f1c9f0e973f353f9a9ee295a39b1fea8ee1a77/pillow-12.2.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:62f5409336adb0663b7caa0da5c7d9e7bdbaae9ce761d34669420c2a801b2780", size = 7087215, upload-time = "2026-04-01T14:43:26.758Z" },
+    { url = "https://files.pythonhosted.org/packages/8b/f8/2f6825e441d5b1959d2ca5adec984210f1ec086435b0ed5f52c19b3b8a6e/pillow-12.2.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:01afa7cf67f74f09523699b4e88c73fb55c13346d212a59a2db1f86b0a63e8c5", size = 6509783, upload-time = "2026-04-01T14:43:29.56Z" },
+    { url = "https://files.pythonhosted.org/packages/67/f9/029a27095ad20f854f9dba026b3ea6428548316e057e6fc3545409e86651/pillow-12.2.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fc3d34d4a8fbec3e88a79b92e5465e0f9b842b628675850d860b8bd300b159f5", size = 7212112, upload-time = "2026-04-01T14:43:32.091Z" },
+    { url = "https://files.pythonhosted.org/packages/be/42/025cfe05d1be22dbfdb4f264fe9de1ccda83f66e4fc3aac94748e784af04/pillow-12.2.0-cp312-cp312-win32.whl", hash = "sha256:58f62cc0f00fd29e64b29f4fd923ffdb3859c9f9e6105bfc37ba1d08994e8940", size = 6378489, upload-time = "2026-04-01T14:43:34.601Z" },
+    { url = "https://files.pythonhosted.org/packages/5d/7b/25a221d2c761c6a8ae21bfa3874988ff2583e19cf8a27bf2fee358df7942/pillow-12.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:7f84204dee22a783350679a0333981df803dac21a0190d706a50475e361c93f5", size = 7084129, upload-time = "2026-04-01T14:43:37.213Z" },
+    { url = "https://files.pythonhosted.org/packages/10/e1/542a474affab20fd4a0f1836cb234e8493519da6b76899e30bcc5d990b8b/pillow-12.2.0-cp312-cp312-win_arm64.whl", hash = "sha256:af73337013e0b3b46f175e79492d96845b16126ddf79c438d7ea7ff27783a414", size = 2463612, upload-time = "2026-04-01T14:43:39.421Z" },
+    { url = "https://files.pythonhosted.org/packages/4a/01/53d10cf0dbad820a8db274d259a37ba50b88b24768ddccec07355382d5ad/pillow-12.2.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:8297651f5b5679c19968abefd6bb84d95fe30ef712eb1b2d9b2d31ca61267f4c", size = 4100837, upload-time = "2026-04-01T14:43:41.506Z" },
+    { url = "https://files.pythonhosted.org/packages/0f/98/f3a6657ecb698c937f6c76ee564882945f29b79bad496abcba0e84659ec5/pillow-12.2.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:50d8520da2a6ce0af445fa6d648c4273c3eeefbc32d7ce049f22e8b5c3daecc2", size = 4176528, upload-time = "2026-04-01T14:43:43.773Z" },
+    { url = "https://files.pythonhosted.org/packages/69/bc/8986948f05e3ea490b8442ea1c1d4d990b24a7e43d8a51b2c7d8b1dced36/pillow-12.2.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:766cef22385fa1091258ad7e6216792b156dc16d8d3fa607e7545b2b72061f1c", size = 3640401, upload-time = "2026-04-01T14:43:45.87Z" },
+    { url = "https://files.pythonhosted.org/packages/34/46/6c717baadcd62bc8ed51d238d521ab651eaa74838291bda1f86fe1f864c9/pillow-12.2.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5d2fd0fa6b5d9d1de415060363433f28da8b1526c1c129020435e186794b3795", size = 5308094, upload-time = "2026-04-01T14:43:48.438Z" },
+    { url = "https://files.pythonhosted.org/packages/71/43/905a14a8b17fdb1ccb58d282454490662d2cb89a6bfec26af6d3520da5ec/pillow-12.2.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:56b25336f502b6ed02e889f4ece894a72612fe885889a6e8c4c80239ff6e5f5f", size = 4695402, upload-time = "2026-04-01T14:43:51.292Z" },
+    { url = "https://files.pythonhosted.org/packages/73/dd/42107efcb777b16fa0393317eac58f5b5cf30e8392e266e76e51cff28c3d/pillow-12.2.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f1c943e96e85df3d3478f7b691f229887e143f81fedab9b20205349ab04d73ed", size = 6280005, upload-time = "2026-04-01T14:43:54.242Z" },
+    { url = "https://files.pythonhosted.org/packages/a8/68/b93e09e5e8549019e61acf49f65b1a8530765a7f812c77a7461bca7e4494/pillow-12.2.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:03f6fab9219220f041c74aeaa2939ff0062bd5c364ba9ce037197f4c6d498cd9", size = 8090669, upload-time = "2026-04-01T14:43:57.335Z" },
+    { url = "https://files.pythonhosted.org/packages/4b/6e/3ccb54ce8ec4ddd1accd2d89004308b7b0b21c4ac3d20fa70af4760a4330/pillow-12.2.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5cdfebd752ec52bf5bb4e35d9c64b40826bc5b40a13df7c3cda20a2c03a0f5ed", size = 6395194, upload-time = "2026-04-01T14:43:59.864Z" },
+    { url = "https://files.pythonhosted.org/packages/67/ee/21d4e8536afd1a328f01b359b4d3997b291ffd35a237c877b331c1c3b71c/pillow-12.2.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:eedf4b74eda2b5a4b2b2fb4c006d6295df3bf29e459e198c90ea48e130dc75c3", size = 7082423, upload-time = "2026-04-01T14:44:02.74Z" },
+    { url = "https://files.pythonhosted.org/packages/78/5f/e9f86ab0146464e8c133fe85df987ed9e77e08b29d8d35f9f9f4d6f917ba/pillow-12.2.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:00a2865911330191c0b818c59103b58a5e697cae67042366970a6b6f1b20b7f9", size = 6505667, upload-time = "2026-04-01T14:44:05.381Z" },
+    { url = "https://files.pythonhosted.org/packages/ed/1e/409007f56a2fdce61584fd3acbc2bbc259857d555196cedcadc68c015c82/pillow-12.2.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1e1757442ed87f4912397c6d35a0db6a7b52592156014706f17658ff58bbf795", size = 7208580, upload-time = "2026-04-01T14:44:08.39Z" },
+    { url = "https://files.pythonhosted.org/packages/23/c4/7349421080b12fb35414607b8871e9534546c128a11965fd4a7002ccfbee/pillow-12.2.0-cp313-cp313-win32.whl", hash = "sha256:144748b3af2d1b358d41286056d0003f47cb339b8c43a9ea42f5fea4d8c66b6e", size = 6375896, upload-time = "2026-04-01T14:44:11.197Z" },
+    { url = "https://files.pythonhosted.org/packages/3f/82/8a3739a5e470b3c6cbb1d21d315800d8e16bff503d1f16b03a4ec3212786/pillow-12.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:390ede346628ccc626e5730107cde16c42d3836b89662a115a921f28440e6a3b", size = 7081266, upload-time = "2026-04-01T14:44:13.947Z" },
+    { url = "https://files.pythonhosted.org/packages/c3/25/f968f618a062574294592f668218f8af564830ccebdd1fa6200f598e65c5/pillow-12.2.0-cp313-cp313-win_arm64.whl", hash = "sha256:8023abc91fba39036dbce14a7d6535632f99c0b857807cbbbf21ecc9f4717f06", size = 2463508, upload-time = "2026-04-01T14:44:16.312Z" },
+    { url = "https://files.pythonhosted.org/packages/4d/a4/b342930964e3cb4dce5038ae34b0eab4653334995336cd486c5a8c25a00c/pillow-12.2.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:042db20a421b9bafecc4b84a8b6e444686bd9d836c7fd24542db3e7df7baad9b", size = 5309927, upload-time = "2026-04-01T14:44:18.89Z" },
+    { url = "https://files.pythonhosted.org/packages/9f/de/23198e0a65a9cf06123f5435a5d95cea62a635697f8f03d134d3f3a96151/pillow-12.2.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:dd025009355c926a84a612fecf58bb315a3f6814b17ead51a8e48d3823d9087f", size = 4698624, upload-time = "2026-04-01T14:44:21.115Z" },
+    { url = "https://files.pythonhosted.org/packages/01/a6/1265e977f17d93ea37aa28aa81bad4fa597933879fac2520d24e021c8da3/pillow-12.2.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:88ddbc66737e277852913bd1e07c150cc7bb124539f94c4e2df5344494e0a612", size = 6321252, upload-time = "2026-04-01T14:44:23.663Z" },
+    { url = "https://files.pythonhosted.org/packages/3c/83/5982eb4a285967baa70340320be9f88e57665a387e3a53a7f0db8231a0cd/pillow-12.2.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d362d1878f00c142b7e1a16e6e5e780f02be8195123f164edf7eddd911eefe7c", size = 8126550, upload-time = "2026-04-01T14:44:26.772Z" },
+    { url = "https://files.pythonhosted.org/packages/4e/48/6ffc514adce69f6050d0753b1a18fd920fce8cac87620d5a31231b04bfc5/pillow-12.2.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2c727a6d53cb0018aadd8018c2b938376af27914a68a492f59dfcaca650d5eea", size = 6433114, upload-time = "2026-04-01T14:44:29.615Z" },
+    { url = "https://files.pythonhosted.org/packages/36/a3/f9a77144231fb8d40ee27107b4463e205fa4677e2ca2548e14da5cf18dce/pillow-12.2.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:efd8c21c98c5cc60653bcb311bef2ce0401642b7ce9d09e03a7da87c878289d4", size = 7115667, upload-time = "2026-04-01T14:44:32.773Z" },
+    { url = "https://files.pythonhosted.org/packages/c1/fc/ac4ee3041e7d5a565e1c4fd72a113f03b6394cc72ab7089d27608f8aaccb/pillow-12.2.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9f08483a632889536b8139663db60f6724bfcb443c96f1b18855860d7d5c0fd4", size = 6538966, upload-time = "2026-04-01T14:44:35.252Z" },
+    { url = "https://files.pythonhosted.org/packages/c0/a8/27fb307055087f3668f6d0a8ccb636e7431d56ed0750e07a60547b1e083e/pillow-12.2.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:dac8d77255a37e81a2efcbd1fc05f1c15ee82200e6c240d7e127e25e365c39ea", size = 7238241, upload-time = "2026-04-01T14:44:37.875Z" },
+    { url = "https://files.pythonhosted.org/packages/ad/4b/926ab182c07fccae9fcb120043464e1ff1564775ec8864f21a0ebce6ac25/pillow-12.2.0-cp313-cp313t-win32.whl", hash = "sha256:ee3120ae9dff32f121610bb08e4313be87e03efeadfc6c0d18f89127e24d0c24", size = 6379592, upload-time = "2026-04-01T14:44:40.336Z" },
+    { url = "https://files.pythonhosted.org/packages/c2/c4/f9e476451a098181b30050cc4c9a3556b64c02cf6497ea421ac047e89e4b/pillow-12.2.0-cp313-cp313t-win_amd64.whl", hash = "sha256:325ca0528c6788d2a6c3d40e3568639398137346c3d6e66bb61db96b96511c98", size = 7085542, upload-time = "2026-04-01T14:44:43.251Z" },
+    { url = "https://files.pythonhosted.org/packages/00/a4/285f12aeacbe2d6dc36c407dfbbe9e96d4a80b0fb710a337f6d2ad978c75/pillow-12.2.0-cp313-cp313t-win_arm64.whl", hash = "sha256:2e5a76d03a6c6dcef67edabda7a52494afa4035021a79c8558e14af25313d453", size = 2465765, upload-time = "2026-04-01T14:44:45.996Z" },
+    { url = "https://files.pythonhosted.org/packages/bf/98/4595daa2365416a86cb0d495248a393dfc84e96d62ad080c8546256cb9c0/pillow-12.2.0-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:3adc9215e8be0448ed6e814966ecf3d9952f0ea40eb14e89a102b87f450660d8", size = 4100848, upload-time = "2026-04-01T14:44:48.48Z" },
+    { url = "https://files.pythonhosted.org/packages/0b/79/40184d464cf89f6663e18dfcf7ca21aae2491fff1a16127681bf1fa9b8cf/pillow-12.2.0-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:6a9adfc6d24b10f89588096364cc726174118c62130c817c2837c60cf08a392b", size = 4176515, upload-time = "2026-04-01T14:44:51.353Z" },
+    { url = "https://files.pythonhosted.org/packages/b0/63/703f86fd4c422a9cf722833670f4f71418fb116b2853ff7da722ea43f184/pillow-12.2.0-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:6a6e67ea2e6feda684ed370f9a1c52e7a243631c025ba42149a2cc5934dec295", size = 3640159, upload-time = "2026-04-01T14:44:53.588Z" },
+    { url = "https://files.pythonhosted.org/packages/71/e0/fb22f797187d0be2270f83500aab851536101b254bfa1eae10795709d283/pillow-12.2.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:2bb4a8d594eacdfc59d9e5ad972aa8afdd48d584ffd5f13a937a664c3e7db0ed", size = 5312185, upload-time = "2026-04-01T14:44:56.039Z" },
+    { url = "https://files.pythonhosted.org/packages/ba/8c/1a9e46228571de18f8e28f16fabdfc20212a5d019f3e3303452b3f0a580d/pillow-12.2.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:80b2da48193b2f33ed0c32c38140f9d3186583ce7d516526d462645fd98660ae", size = 4695386, upload-time = "2026-04-01T14:44:58.663Z" },
+    { url = "https://files.pythonhosted.org/packages/70/62/98f6b7f0c88b9addd0e87c217ded307b36be024d4ff8869a812b241d1345/pillow-12.2.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:22db17c68434de69d8ecfc2fe821569195c0c373b25cccb9cbdacf2c6e53c601", size = 6280384, upload-time = "2026-04-01T14:45:01.5Z" },
+    { url = "https://files.pythonhosted.org/packages/5e/03/688747d2e91cfbe0e64f316cd2e8005698f76ada3130d0194664174fa5de/pillow-12.2.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7b14cc0106cd9aecda615dd6903840a058b4700fcb817687d0ee4fc8b6e389be", size = 8091599, upload-time = "2026-04-01T14:45:04.5Z" },
+    { url = "https://files.pythonhosted.org/packages/f6/35/577e22b936fcdd66537329b33af0b4ccfefaeabd8aec04b266528cddb33c/pillow-12.2.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8cbeb542b2ebc6fcdacabf8aca8c1a97c9b3ad3927d46b8723f9d4f033288a0f", size = 6396021, upload-time = "2026-04-01T14:45:07.117Z" },
+    { url = "https://files.pythonhosted.org/packages/11/8d/d2532ad2a603ca2b93ad9f5135732124e57811d0168155852f37fbce2458/pillow-12.2.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4bfd07bc812fbd20395212969e41931001fd59eb55a60658b0e5710872e95286", size = 7083360, upload-time = "2026-04-01T14:45:09.763Z" },
+    { url = "https://files.pythonhosted.org/packages/5e/26/d325f9f56c7e039034897e7380e9cc202b1e368bfd04d4cbe6a441f02885/pillow-12.2.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:9aba9a17b623ef750a4d11b742cbafffeb48a869821252b30ee21b5e91392c50", size = 6507628, upload-time = "2026-04-01T14:45:12.378Z" },
+    { url = "https://files.pythonhosted.org/packages/5f/f7/769d5632ffb0988f1c5e7660b3e731e30f7f8ec4318e94d0a5d674eb65a4/pillow-12.2.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:deede7c263feb25dba4e82ea23058a235dcc2fe1f6021025dc71f2b618e26104", size = 7209321, upload-time = "2026-04-01T14:45:15.122Z" },
+    { url = "https://files.pythonhosted.org/packages/6a/7a/c253e3c645cd47f1aceea6a8bacdba9991bf45bb7dfe927f7c893e89c93c/pillow-12.2.0-cp314-cp314-win32.whl", hash = "sha256:632ff19b2778e43162304d50da0181ce24ac5bb8180122cbe1bf4673428328c7", size = 6479723, upload-time = "2026-04-01T14:45:17.797Z" },
+    { url = "https://files.pythonhosted.org/packages/cd/8b/601e6566b957ca50e28725cb6c355c59c2c8609751efbecd980db44e0349/pillow-12.2.0-cp314-cp314-win_amd64.whl", hash = "sha256:4e6c62e9d237e9b65fac06857d511e90d8461a32adcc1b9065ea0c0fa3a28150", size = 7217400, upload-time = "2026-04-01T14:45:20.529Z" },
+    { url = "https://files.pythonhosted.org/packages/d6/94/220e46c73065c3e2951bb91c11a1fb636c8c9ad427ac3ce7d7f3359b9b2f/pillow-12.2.0-cp314-cp314-win_arm64.whl", hash = "sha256:b1c1fbd8a5a1af3412a0810d060a78b5136ec0836c8a4ef9aa11807f2a22f4e1", size = 2554835, upload-time = "2026-04-01T14:45:23.162Z" },
+    { url = "https://files.pythonhosted.org/packages/b6/ab/1b426a3974cb0e7da5c29ccff4807871d48110933a57207b5a676cccc155/pillow-12.2.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:57850958fe9c751670e49b2cecf6294acc99e562531f4bd317fa5ddee2068463", size = 5314225, upload-time = "2026-04-01T14:45:25.637Z" },
+    { url = "https://files.pythonhosted.org/packages/19/1e/dce46f371be2438eecfee2a1960ee2a243bbe5e961890146d2dee1ff0f12/pillow-12.2.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:d5d38f1411c0ed9f97bcb49b7bd59b6b7c314e0e27420e34d99d844b9ce3b6f3", size = 4698541, upload-time = "2026-04-01T14:45:28.355Z" },
+    { url = "https://files.pythonhosted.org/packages/55/c3/7fbecf70adb3a0c33b77a300dc52e424dc22ad8cdc06557a2e49523b703d/pillow-12.2.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5c0a9f29ca8e79f09de89293f82fc9b0270bb4af1d58bc98f540cc4aedf03166", size = 6322251, upload-time = "2026-04-01T14:45:30.924Z" },
+    { url = "https://files.pythonhosted.org/packages/1c/3c/7fbc17cfb7e4fe0ef1642e0abc17fc6c94c9f7a16be41498e12e2ba60408/pillow-12.2.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1610dd6c61621ae1cf811bef44d77e149ce3f7b95afe66a4512f8c59f25d9ebe", size = 8127807, upload-time = "2026-04-01T14:45:33.908Z" },
+    { url = "https://files.pythonhosted.org/packages/ff/c3/a8ae14d6defd2e448493ff512fae903b1e9bd40b72efb6ec55ce0048c8ce/pillow-12.2.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0a34329707af4f73cf1782a36cd2289c0368880654a2c11f027bcee9052d35dd", size = 6433935, upload-time = "2026-04-01T14:45:36.623Z" },
+    { url = "https://files.pythonhosted.org/packages/6e/32/2880fb3a074847ac159d8f902cb43278a61e85f681661e7419e6596803ed/pillow-12.2.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8e9c4f5b3c546fa3458a29ab22646c1c6c787ea8f5ef51300e5a60300736905e", size = 7116720, upload-time = "2026-04-01T14:45:39.258Z" },
+    { url = "https://files.pythonhosted.org/packages/46/87/495cc9c30e0129501643f24d320076f4cc54f718341df18cc70ec94c44e1/pillow-12.2.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:fb043ee2f06b41473269765c2feae53fc2e2fbf96e5e22ca94fb5ad677856f06", size = 6540498, upload-time = "2026-04-01T14:45:41.879Z" },
+    { url = "https://files.pythonhosted.org/packages/18/53/773f5edca692009d883a72211b60fdaf8871cbef075eaa9d577f0a2f989e/pillow-12.2.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:f278f034eb75b4e8a13a54a876cc4a5ab39173d2cdd93a638e1b467fc545ac43", size = 7239413, upload-time = "2026-04-01T14:45:44.705Z" },
+    { url = "https://files.pythonhosted.org/packages/c9/e4/4b64a97d71b2a83158134abbb2f5bd3f8a2ea691361282f010998f339ec7/pillow-12.2.0-cp314-cp314t-win32.whl", hash = "sha256:6bb77b2dcb06b20f9f4b4a8454caa581cd4dd0643a08bacf821216a16d9c8354", size = 6482084, upload-time = "2026-04-01T14:45:47.568Z" },
+    { url = "https://files.pythonhosted.org/packages/ba/13/306d275efd3a3453f72114b7431c877d10b1154014c1ebbedd067770d629/pillow-12.2.0-cp314-cp314t-win_amd64.whl", hash = "sha256:6562ace0d3fb5f20ed7290f1f929cae41b25ae29528f2af1722966a0a02e2aa1", size = 7225152, upload-time = "2026-04-01T14:45:50.032Z" },
+    { url = "https://files.pythonhosted.org/packages/ff/6e/cf826fae916b8658848d7b9f38d88da6396895c676e8086fc0988073aaf8/pillow-12.2.0-cp314-cp314t-win_arm64.whl", hash = "sha256:aa88ccfe4e32d362816319ed727a004423aab09c5cea43c01a4b435643fa34eb", size = 2556579, upload-time = "2026-04-01T14:45:52.529Z" },
+    { url = "https://files.pythonhosted.org/packages/4e/b7/2437044fb910f499610356d1352e3423753c98e34f915252aafecc64889f/pillow-12.2.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0538bd5e05efec03ae613fd89c4ce0368ecd2ba239cc25b9f9be7ed426b0af1f", size = 5273969, upload-time = "2026-04-01T14:45:55.538Z" },
+    { url = "https://files.pythonhosted.org/packages/f6/f4/8316e31de11b780f4ac08ef3654a75555e624a98db1056ecb2122d008d5a/pillow-12.2.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:394167b21da716608eac917c60aa9b969421b5dcbbe02ae7f013e7b85811c69d", size = 4659674, upload-time = "2026-04-01T14:45:58.093Z" },
+    { url = "https://files.pythonhosted.org/packages/d4/37/664fca7201f8bb2aa1d20e2c3d5564a62e6ae5111741966c8319ca802361/pillow-12.2.0-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5d04bfa02cc2d23b497d1e90a0f927070043f6cbf303e738300532379a4b4e0f", size = 5288479, upload-time = "2026-04-01T14:46:01.141Z" },
+    { url = "https://files.pythonhosted.org/packages/49/62/5b0ed78fce87346be7a5cfcfaaad91f6a1f98c26f86bdbafa2066c647ef6/pillow-12.2.0-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0c838a5125cee37e68edec915651521191cef1e6aa336b855f495766e77a366e", size = 7032230, upload-time = "2026-04-01T14:46:03.874Z" },
+    { url = "https://files.pythonhosted.org/packages/c3/28/ec0fc38107fc32536908034e990c47914c57cd7c5a3ece4d8d8f7ffd7e27/pillow-12.2.0-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4a6c9fa44005fa37a91ebfc95d081e8079757d2e904b27103f4f5fa6f0bf78c0", size = 5355404, upload-time = "2026-04-01T14:46:06.33Z" },
+    { url = "https://files.pythonhosted.org/packages/5e/8b/51b0eddcfa2180d60e41f06bd6d0a62202b20b59c68f5a132e615b75aecf/pillow-12.2.0-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:25373b66e0dd5905ed63fa3cae13c82fbddf3079f2c8bf15c6fb6a35586324c1", size = 6002215, upload-time = "2026-04-01T14:46:08.83Z" },
+    { url = "https://files.pythonhosted.org/packages/bc/60/5382c03e1970de634027cee8e1b7d39776b778b81812aaf45b694dfe9e28/pillow-12.2.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:bfa9c230d2fe991bed5318a5f119bd6780cda2915cca595393649fc118ab895e", size = 7080946, upload-time = "2026-04-01T14:46:11.734Z" },
+]
+
 [[package]]
 name = "platformdirs"
 version = "4.9.6"
@@ -2070,6 +2207,18 @@ wheels = [
     { url = "https://files.pythonhosted.org/packages/41/52/e465037f5375f43533d1a80b6923955201596a99142ed524d77b571a1418/wcwidth-0.7.0-py3-none-any.whl", hash = "sha256:5d69154c429a82910e241c738cd0e2976fac8a2dd47a1a805f4afed1c0f136f2", size = 110825, upload-time = "2026-05-02T16:04:11.033Z" },
 ]
 
+[[package]]
+name = "werkzeug"
+version = "3.1.8"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "markupsafe" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/dd/b2/381be8cfdee792dd117872481b6e378f85c957dd7c5bca38897b08f765fd/werkzeug-3.1.8.tar.gz", hash = "sha256:9bad61a4268dac112f1c5cd4630a56ede601b6ed420300677a869083d70a4c44", size = 875852, upload-time = "2026-04-02T18:49:14.268Z" }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/93/8c/2e650f2afeb7ee576912636c23ddb621c91ac6a98e66dc8d29c3c69446e1/werkzeug-3.1.8-py3-none-any.whl", hash = "sha256:63a77fb8892bf28ebc3178683445222aa500e48ebad5ec77b0ad80f8726b1f50", size = 226459, upload-time = "2026-04-02T18:49:12.72Z" },
+]
+
 [[package]]
 name = "zope-interface"
 version = "8.4"


Commit: c9bbcf1f1fd0bb98ca8b765df1e87fc4d2f4b44b
    https://github.com/scummvm/scummvm-sites/commit/c9bbcf1f1fd0bb98ca8b765df1e87fc4d2f4b44b
Author: ramyak-sharma (ramyaksharma1 at gmail.com)
Date: 2026-06-13T20:00:44+02:00

Commit Message:
BUILDBOT: Fix imagediff import and move utility functions to imagediff

imagediff/imagediff.py no uses imagediff/ by inserting into sys.path before importing, replacing the previous importlib workaround.

Also moved list_movie_prefixes and find_baseline_build into imagediff/imagediff.py and added a sceenshots_dir parameter to movie_diff.

Changed paths:
    director/screenshot_diff.py
    imagediff/imagediff.py


diff --git a/director/screenshot_diff.py b/director/screenshot_diff.py
index 6173c7e..4259b5c 100644
--- a/director/screenshot_diff.py
+++ b/director/screenshot_diff.py
@@ -7,11 +7,10 @@ ScreenshotDiffStep after ScummVMTest when screenshot debugflags are enabled.
 from __future__ import annotations
 
 import argparse
-import importlib.util
 import os
 import sys
 import urllib.parse
-from typing import Callable, Optional
+from typing import Optional
 
 EXIT_OK = 0
 EXIT_DIFF = 1
@@ -19,45 +18,15 @@ EXIT_NO_BASELINE = 2
 EXIT_ERROR = 3
 
 _repo_root = os.path.dirname(os.path.dirname(__file__))
+_imagediff_dir = os.path.join(_repo_root, "imagediff")
 _seen_prefixes_name = ".screenshot_prefixes_seen"
 
+# imagediff/imagediff.py does `from config import SCREENSHOTS_DIR` at module
+# level, so imagediff/ must be on sys.path before we import it.
+if _imagediff_dir not in sys.path:
+    sys.path.insert(0, _imagediff_dir)
 
-def _load_movie_diff(screenshots_dir: str) -> Callable[..., bool]:
-    """Load movie_diff from the vendored imagediff package."""
-    os.environ["SCREENSHOTS_DIR"] = screenshots_dir
-    imagediff_dir = os.path.join(_repo_root, "imagediff")
-
-    config_spec = importlib.util.spec_from_file_location(
-        "imagediff_config", os.path.join(imagediff_dir, "config.py")
-    )
-    if config_spec is None or config_spec.loader is None:
-        raise ImportError("unable to load imagediff config")
-    config_module = importlib.util.module_from_spec(config_spec)
-    sys.modules["config"] = config_module
-    config_spec.loader.exec_module(config_module)
-
-    core_spec = importlib.util.spec_from_file_location(
-        "imagediff_core", os.path.join(imagediff_dir, "imagediff.py")
-    )
-    if core_spec is None or core_spec.loader is None:
-        raise ImportError("unable to load imagediff core")
-    core_module = importlib.util.module_from_spec(core_spec)
-    core_spec.loader.exec_module(core_module)
-    return core_module.movie_diff
-
-
-def list_movie_prefixes(build_path: str) -> list[str]:
-    """Return sorted movie filename prefixes in a build directory."""
-    prefixes: set[str] = set()
-    if not os.path.isdir(build_path):
-        return []
-
-    for name in os.listdir(build_path):
-        if not name.endswith(".png") or "-" not in name:
-            continue
-        prefixes.add(name.rsplit("-", 1)[0])
-
-    return sorted(prefixes)
+import imagediff as _imagediff  # imagediff/imagediff.py
 
 
 def _seen_prefixes_path(screenshots_dir: str, target: str, build: str) -> str:
@@ -86,7 +55,7 @@ def prefixes_for_step(
 ) -> list[str]:
     """Return prefixes to compare for this step."""
     build_path = os.path.join(screenshots_dir, target, build)
-    current = set(list_movie_prefixes(build_path))
+    current = set(_imagediff.list_movie_prefixes(build_path))
     if not only_new:
         return sorted(current)
 
@@ -97,37 +66,6 @@ def prefixes_for_step(
     return new_prefixes
 
 
-def find_baseline_build(
-    screenshots_dir: str, target: str, current_build: str
-) -> Optional[str]:
-    """Return the highest prior build number that has stored screenshots."""
-    target_path = os.path.join(screenshots_dir, target)
-    if not os.path.isdir(target_path):
-        return None
-
-    try:
-        current_number = int(current_build)
-    except ValueError:
-        return None
-
-    candidates: list[int] = []
-    for entry in os.listdir(target_path):
-        path = os.path.join(target_path, entry)
-        if not os.path.isdir(path):
-            continue
-        try:
-            build_number = int(entry)
-        except ValueError:
-            continue
-        if build_number < current_number:
-            candidates.append(build_number)
-
-    if not candidates:
-        return None
-
-    return str(max(candidates))
-
-
 def compare_url(
     imagediff_url: str,
     current_build: str,
@@ -159,7 +97,9 @@ def check_screenshots(
         )
 
     if baseline_build is None:
-        baseline_build = find_baseline_build(screenshots_dir, target, current_build)
+        baseline_build = _imagediff.find_baseline_build(
+            screenshots_dir, target, current_build
+        )
 
     if baseline_build is None:
         return EXIT_NO_BASELINE, (
@@ -168,12 +108,10 @@ def check_screenshots(
 
     if movie_prefix:
         prefixes = [movie_prefix]
-    elif only_new:
+    else:
         prefixes = prefixes_for_step(
-            screenshots_dir, target, current_build, only_new=True
+            screenshots_dir, target, current_build, only_new=only_new
         )
-    else:
-        prefixes = list_movie_prefixes(current_path)
 
     if not prefixes:
         return EXIT_NO_BASELINE, (
@@ -181,15 +119,12 @@ def check_screenshots(
             f"build {current_build}"
         )
 
-    try:
-        movie_diff = _load_movie_diff(screenshots_dir)
-    except ImportError as exc:
-        return EXIT_ERROR, f"Failed to load imagediff: {exc}"
-
     diff_prefixes = [
         prefix
         for prefix in prefixes
-        if movie_diff(current_build, baseline_build, target, prefix)
+        if _imagediff.movie_diff(
+            current_build, baseline_build, target, prefix, screenshots_dir
+        )
     ]
 
     if not diff_prefixes:
@@ -239,7 +174,7 @@ def main(argv: Optional[list[str]] = None) -> int:
 
     print(message)
     if exit_code == EXIT_DIFF and args.imagediff_url:
-        baseline = args.baseline or find_baseline_build(
+        baseline = args.baseline or _imagediff.find_baseline_build(
             args.screenshots_dir, args.target, args.build
         )
         if baseline:
diff --git a/imagediff/imagediff.py b/imagediff/imagediff.py
index 8609ab1..40232c3 100644
--- a/imagediff/imagediff.py
+++ b/imagediff/imagediff.py
@@ -1,7 +1,9 @@
-from PIL import Image, ImageChops
 import os
 import base64
 from io import BytesIO
+
+from PIL import Image, ImageChops
+
 from config import SCREENSHOTS_DIR
 
 def encode_image(image):
@@ -30,7 +32,40 @@ def image_diff(src_img_path, cmp_img_path):
     except IOError:
         return {}
 
-def movie_diff(src_build, cmp_build, target, movie):
+def list_movie_prefixes(build_path):
+    """Return sorted movie filename prefixes present in a build directory."""
+    prefixes = set()
+    if not os.path.isdir(build_path):
+        return []
+    for name in os.listdir(build_path):
+        if name.endswith(".png") and "-" in name:
+            prefixes.add(name.rsplit("-", 1)[0])
+    return sorted(prefixes)
+
+
+def find_baseline_build(screenshots_dir, target, current_build):
+    """Return the highest prior build number that has stored screenshots."""
+    target_path = os.path.join(screenshots_dir, target)
+    if not os.path.isdir(target_path):
+        return None
+    try:
+        current_number = int(current_build)
+    except ValueError:
+        return None
+    candidates = []
+    for entry in os.listdir(target_path):
+        if not os.path.isdir(os.path.join(target_path, entry)):
+            continue
+        try:
+            build_number = int(entry)
+        except ValueError:
+            continue
+        if build_number < current_number:
+            candidates.append(build_number)
+    return str(max(candidates)) if candidates else None
+
+
+def movie_diff(src_build, cmp_build, target, movie, screenshots_dir=None):
     """
     Compare all frames of a movie between two builds and determine if there are any differences.
 
@@ -39,12 +74,14 @@ def movie_diff(src_build, cmp_build, target, movie):
         cmp_build (str): The comparison build name
         target (str): The target name
         movie (str): The movie name
+        screenshots_dir (str): Override for SCREENSHOTS_DIR from config
 
     Returns:
         bool: True if any frame has differences, False otherwise
     """
-    src_build_path = os.path.join(SCREENSHOTS_DIR, target, src_build)
-    cmp_build_path = os.path.join(SCREENSHOTS_DIR, target, cmp_build)
+    screenshots_dir = screenshots_dir or SCREENSHOTS_DIR
+    src_build_path = os.path.join(screenshots_dir, target, src_build)
+    cmp_build_path = os.path.join(screenshots_dir, target, cmp_build)
 
     # Ensure both build paths exist
     if not os.path.exists(src_build_path) or not os.path.exists(cmp_build_path):



From noreply at scummvm.org  Sat Jun 13 18:09:26 2026
From: noreply at scummvm.org (sev-)
Date: Sat, 13 Jun 2026 18:09:26 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm-web master ->
 c4592a145b2f3f1150eb648e66cdc1e1eb9424d5
Message-ID: <20260613180926.8A6B37005BC@ron.scummvm.net>

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

Summary:
c4592a145b NEWS: Announce Cartoon Carnival


Commit: c4592a145b2f3f1150eb648e66cdc1e1eb9424d5
    https://github.com/scummvm/scummvm-web/commit/c4592a145b2f3f1150eb648e66cdc1e1eb9424d5
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2026-06-13T20:07:41+02:00

Commit Message:
NEWS: Announce Cartoon Carnival

Changed paths:
  A data/en/news/20260613.markdown


diff --git a/data/en/news/20260613.markdown b/data/en/news/20260613.markdown
new file mode 100644
index 00000000..e69de29b



From noreply at scummvm.org  Sat Jun 13 18:10:22 2026
From: noreply at scummvm.org (lotharsm)
Date: Sat, 13 Jun 2026 18:10:22 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm-web master ->
 35dba03179751c97ff2394095fc9a61f9d06210b
Message-ID: <20260613181022.C89827005BC@ron.scummvm.net>

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

Summary:
35dba03179 DATA: Update data files


Commit: 35dba03179751c97ff2394095fc9a61f9d06210b
    https://github.com/scummvm/scummvm-web/commit/35dba03179751c97ff2394095fc9a61f9d06210b
Author: sev- (sev at scummvm.org)
Date: 2026-06-13T18:10:18Z

Commit Message:
DATA: Update data files

Changed paths:
    data/en/companies.yaml
    data/en/compatibility.yaml
    data/en/engines.yaml
    data/en/games.yaml
    data/en/screenshots.yaml


diff --git a/data/en/companies.yaml b/data/en/companies.yaml
index 959d391b..c610914e 100644
--- a/data/en/companies.yaml
+++ b/data/en/companies.yaml
@@ -439,6 +439,10 @@
     id: darkling
     name: 'Darkling Room'
     alt_name: ''
+-
+    id: ddm
+    name: 'Digital Dreams Multimedia'
+    alt_name: ''
 -
     id: deadcode
     name: 'Dead:Code Software'
diff --git a/data/en/compatibility.yaml b/data/en/compatibility.yaml
index 82c7a3bd..c37762d0 100644
--- a/data/en/compatibility.yaml
+++ b/data/en/compatibility.yaml
@@ -5781,13 +5781,6 @@
     since_version: DEV
     stable_platforms: win
     unstable_platforms: ''
--
-    id: 'bolt:carnival'
-    support: excellent
-    notes: '- Only Windows/Macintosh version is supported'
-    since_version: DEV
-    stable_platforms: 'win,mac'
-    unstable_platforms: ''
 -
     id: 'access:noctropolis'
     support: good
@@ -5795,3 +5788,10 @@
     since_version: DEV
     stable_platforms: win
     unstable_platforms: ''
+-
+    id: 'bolt:carnival'
+    support: excellent
+    notes: '- Only Windows/Macintosh version is supported'
+    since_version: DEV
+    stable_platforms: 'win,mac'
+    unstable_platforms: ''
diff --git a/data/en/engines.yaml b/data/en/engines.yaml
index f68e9e70..645d069e 100644
--- a/data/en/engines.yaml
+++ b/data/en/engines.yaml
@@ -369,6 +369,11 @@
     name: 'The Journeyman Project: Pegasus Prime'
     alt_name: ''
     enabled: true
+-
+    id: pelrock
+    name: 'Alfred Pelrock'
+    alt_name: ''
+    enabled: false
 -
     id: petka
     name: 'Red Comrades'
diff --git a/data/en/games.yaml b/data/en/games.yaml
index 4bb07ace..ea738dc7 100644
--- a/data/en/games.yaml
+++ b/data/en/games.yaml
@@ -10443,6 +10443,20 @@
     datafiles: 'The_Journeyman_Project:_Pegasus_Prime'
     wikipedia_page: 'The_Journeyman_Project:_Pegasus_Prime'
     series_id: journeyman
+-
+    id: 'pelrock:pelrock'
+    name: 'Alfred Pelrock: En Busca de un Sue??o'
+    engine_id: pelrock
+    company_id: ddm
+    year: '1996'
+    moby_id: '14161'
+    steam_id: ''
+    gog_id: ''
+    zoom_id: ''
+    additional_stores: ''
+    datafiles: ''
+    wikipedia_page: ''
+    series_id: ''
 -
     id: 'petka:petka1'
     name: 'Red Comrades 1: Save the Galaxy'
diff --git a/data/en/screenshots.yaml b/data/en/screenshots.yaml
index 12bec326..9d68f1de 100644
--- a/data/en/screenshots.yaml
+++ b/data/en/screenshots.yaml
@@ -653,6 +653,12 @@
     platform_id: win
     language: en
     variant_id: '1'
+-
+    id: 'bolt:carnival'
+    variant: ''
+    platform_id: win
+    language: en
+    variant_id: '1'
 -
     id: 'buried:buried'
     variant: 8BPP
@@ -2039,6 +2045,12 @@
     platform_id: mac
     language: en
     variant_id: '1'
+-
+    id: 'pelrock:pelrock'
+    variant: ''
+    platform_id: dos
+    language: es
+    variant_id: '1'
 -
     id: 'petka:petka1'
     variant: ''



From noreply at scummvm.org  Sat Jun 13 18:11:55 2026
From: noreply at scummvm.org (sev-)
Date: Sat, 13 Jun 2026 18:11:55 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm-web master ->
 aa0e6f6d368b602282de0b888f51011374aedd77
Message-ID: <20260613181155.6320D7005BC@ron.scummvm.net>

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

Summary:
aa0e6f6d36 NEWS: Fix syntax


Commit: aa0e6f6d368b602282de0b888f51011374aedd77
    https://github.com/scummvm/scummvm-web/commit/aa0e6f6d368b602282de0b888f51011374aedd77
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2026-06-13T20:11:49+02:00

Commit Message:
NEWS: Fix syntax

Changed paths:
    data/en/news/20260613.markdown


diff --git a/data/en/news/20260613.markdown b/data/en/news/20260613.markdown
index e69de29b..3a79ad56 100644
--- a/data/en/news/20260613.markdown
+++ b/data/en/news/20260613.markdown
@@ -0,0 +1,18 @@
+---
+title: "The Cartoon Carnival opens its gates!"
+author: Bosca
+date: 1781371291
+---
+Come one, come all! _Hanna-Barbera's Cartoon Carnival_ is now ready for public testing in ScummVM.
+
+Originally developed and released for Philips CD-i in 1993 by Funhouse Design and then ported to Windows and Macintosh in 1995, _Cartoon Carnival_ brings together a cast of familiar Hanna-Barbera characters in a collection of short arcade-style minigames. Scooby-Doo, Fred Flintstone, George Jetson, Top Cat and friends each get their turn in different challenges, ranging from mazes and pattern matching games to balloon catching and trivia quizzes.
+
+Rather than a traditional adventure game, this is a colorful piece of mid-90???s multimedia entertainment, full of animation, voices and music. It is also one of those licensed titles that can easily slip through the cracks, so we are especially happy to help keep it playable on modern systems.
+
+At this time, only the Windows and Macintosh versions are supported. The original CD-i version is not supported.
+
+To test the game, you will need the latest [daily development build](https://www.scummvm.org/downloads/#daily) and either an original copy of the Windows or Macintosh release, or its demo (which we were only able to source in Italian). Please check the [wiki page](https://wiki.scummvm.org/index.php?title=Cartoon_Carnival) for the required data files.
+
+As usual, please report any issues to our [bug tracker](https://bugs.scummvm.org/), including the version you tested, your platform, and any useful screenshots or save files.
+
+Step right up, pick a game, and help us make sure this carnival runs properly!



From noreply at scummvm.org  Sat Jun 13 18:12:44 2026
From: noreply at scummvm.org (lotharsm)
Date: Sat, 13 Jun 2026 18:12:44 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm-web master ->
 8bf53875d8e54dd8fdd760d31b86db15f7ba822c
Message-ID: <20260613181244.489737005BC@ron.scummvm.net>

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

Summary:
8bf53875d8 L10N: Update base news translation file


Commit: 8bf53875d8e54dd8fdd760d31b86db15f7ba822c
    https://github.com/scummvm/scummvm-web/commit/8bf53875d8e54dd8fdd760d31b86db15f7ba822c
Author: sev- (sev at scummvm.org)
Date: 2026-06-13T18:12:40Z

Commit Message:
L10N: Update base news translation file

Changed paths:
    data/en/news.json


diff --git a/data/en/news.json b/data/en/news.json
index 3c3cb567..c459df45 100644
--- a/data/en/news.json
+++ b/data/en/news.json
@@ -1750,5 +1750,9 @@
     "20260612": {
         "title": "A darker shade of Grey: Noctropolis is Ready for Testing!",
         "content": "Tired of running a mundane bookstore and ready to dive into the gritty, neon-drenched pages of a comic book? Get your trench coat ready, because Flashpoint Productions??? 1994 dark adventure, _Noctropolis_ is now ready for public testing!\n\nIn this interactive comic brought to life you play as Peter Grey, a lonely bookstore owner pulled into a dystopian superhero world where the sun never shines. With the legendary hero Darksheer retired, it is up to you to take on his role, solve the puzzles, and save the city from a cast of villains doing the bidding of a mysterious force.\n\nTo help us test, you will need your copy of the [game data files](https://wiki.scummvm.org/index.php?title=Noctropolis#Required_Data) and a recent [daily development build](https://www.scummvm.org/downloads/#daily) of ScummVM.\n\nSupport has been added for both the original DOS release (including demos) and the Nightdive Studios digital re-release, which is available from [GOG](https://www.gog.com/game/noctropolis) or [Steam](https://store.steampowered.com/app/377070).\n\nAs you patrol the dangerous streets, please report any issues you find on our [bug tracker](https://bugs.scummvm.org/).\n\nThe city needs a hero ??? will you answer the call? Happy crime-fighting!"
+    },
+    "20260613": {
+        "title": "The Cartoon Carnival opens its gates!",
+        "content": "Come one, come all! _Hanna-Barbera's Cartoon Carnival_ is now ready for public testing in ScummVM.\n\nOriginally developed and released for Philips CD-i in 1993 by Funhouse Design and then ported to Windows and Macintosh in 1995, _Cartoon Carnival_ brings together a cast of familiar Hanna-Barbera characters in a collection of short arcade-style minigames. Scooby-Doo, Fred Flintstone, George Jetson, Top Cat and friends each get their turn in different challenges, ranging from mazes and pattern matching games to balloon catching and trivia quizzes.\n\nRather than a traditional adventure game, this is a colorful piece of mid-90???s multimedia entertainment, full of animation, voices and music. It is also one of those licensed titles that can easily slip through the cracks, so we are especially happy to help keep it playable on modern systems.\n\nAt this time, only the Windows and Macintosh versions are supported. The original CD-i version is not supported.\n\nTo test the game, you will need the latest [daily development build](https://www.scummvm.org/downloads/#daily) and either an original copy of the Windows or Macintosh release, or its demo (which we were only able to source in Italian). Please check the [wiki page](https://wiki.scummvm.org/index.php?title=Cartoon_Carnival) for the required data files.\n\nAs usual, please report any issues to our [bug tracker](https://bugs.scummvm.org/), including the version you tested, your platform, and any useful screenshots or save files.\n\nStep right up, pick a game, and help us make sure this carnival runs properly!"
     }
 }



From noreply at scummvm.org  Sat Jun 13 18:27:03 2026
From: noreply at scummvm.org (lotharsm)
Date: Sat, 13 Jun 2026 18:27:03 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm-web master ->
 94d54115f18880ee3f1c9b377c8bf04a216601ad
Message-ID: <20260613182703.92FB47005BC@ron.scummvm.net>

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

Summary:
94d54115f1 DATA: Update data files


Commit: 94d54115f18880ee3f1c9b377c8bf04a216601ad
    https://github.com/scummvm/scummvm-web/commit/94d54115f18880ee3f1c9b377c8bf04a216601ad
Author: sev- (sev at scummvm.org)
Date: 2026-06-13T18:27:00Z

Commit Message:
DATA: Update data files

Changed paths:
    data/en/game_demos.yaml


diff --git a/data/en/game_demos.yaml b/data/en/game_demos.yaml
index 48f60f73..49047c01 100644
--- a/data/en/game_demos.yaml
+++ b/data/en/game_demos.yaml
@@ -646,7 +646,7 @@
     language: it
     language2: ''
     language3: ''
-    url: ''
+    url: /frs/demos/bolt/carnival-demo-it.zip
 -
     id: 'buried:buried'
     platform: win



From noreply at scummvm.org  Sat Jun 13 20:39:18 2026
From: noreply at scummvm.org (sev-)
Date: Sat, 13 Jun 2026 20:39:18 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 edc6a54044e14879d84d8d1a9f74e37fb1dd8507
Message-ID: <20260613203918.0B2F57005BC@ron.scummvm.net>

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:
8e87557657 BOLT: Sort files in the module.mk
e5804e3251 NEWS: Cartoon Carnival is released before pelrock
a2c66abebb DIRECTOR: LINGO: Fix warning
9c98dc3ed2 DIRECTOR: LINGO: Fix warning
edc6a54044 DIRECTOR: Make savegames mixed-in to SearchSet case-insensitive


Commit: 8e87557657ba17814f3bf6d81e4b1df5771db070
    https://github.com/scummvm/scummvm/commit/8e87557657ba17814f3bf6d81e4b1df5771db070
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2026-06-13T19:08:35+02:00

Commit Message:
BOLT: Sort files in the module.mk

Changed paths:
    engines/bolt/module.mk


diff --git a/engines/bolt/module.mk b/engines/bolt/module.mk
index 8c904d6c540..fc3a3cb2e78 100644
--- a/engines/bolt/module.mk
+++ b/engines/bolt/module.mk
@@ -3,8 +3,8 @@ MODULE := engines/bolt
 MODULE_OBJS = \
 	anim.o \
 	av.o \
-	carnival/barker.o \
 	bolt.o \
+	carnival/barker.o \
 	carnival/booth.o \
 	carnival/booths/fred.o \
 	carnival/booths/george.o \


Commit: e5804e3251831e71e0c1f1d6f15795bfb1f2cbdd
    https://github.com/scummvm/scummvm/commit/e5804e3251831e71e0c1f1d6f15795bfb1f2cbdd
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2026-06-13T19:09:58+02:00

Commit Message:
NEWS: Cartoon Carnival is released before pelrock

Changed paths:
    NEWS.md


diff --git a/NEWS.md b/NEWS.md
index 2bedf86d620..edd83a15104 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -7,8 +7,8 @@ For a more comprehensive changelog of the latest experimental code, see:
    - Added support for Nancy Drew: The Haunted Carousel.
    - Added support for Nancy Drew: Danger on Deception Island.
    - Added support for Noctropolis.
-   - Added support for Alfred Pelrock: En Busca de un Sue??o.
    - Added support for Cartoon Carnival.
+   - Added support for Alfred Pelrock: En Busca de un Sue??o.
 
  General:
    - Highlight clickable links in the GUI with a cursor change.


Commit: a2c66abebb71a7c20229f4193101d1b4318910da
    https://github.com/scummvm/scummvm/commit/a2c66abebb71a7c20229f4193101d1b4318910da
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2026-06-13T21:39:02+02:00

Commit Message:
DIRECTOR: LINGO: Fix warning

Changed paths:
    engines/director/lingo/lingo-builtins.cpp


diff --git a/engines/director/lingo/lingo-builtins.cpp b/engines/director/lingo/lingo-builtins.cpp
index 8126a6843e7..6b97ddeba8d 100644
--- a/engines/director/lingo/lingo-builtins.cpp
+++ b/engines/director/lingo/lingo-builtins.cpp
@@ -633,7 +633,7 @@ void LB::b_integer(int nargs) {
 		//   put i & " = " & integer("12345" & numToChar(i))
 		// end repeat
 		if (endPtr && endPtr != src.c_str() && (
-			(*endPtr >= 0 && *endPtr < 45) ||
+			(*endPtr < 45) ||
 			(*endPtr == 47) ||
 			(*endPtr >= 58 && *endPtr < 65) ||
 			(*endPtr >= 91 && *endPtr < 95) ||


Commit: 9c98dc3ed2b5d4c0e847ab4ff1704f4fc463b02d
    https://github.com/scummvm/scummvm/commit/9c98dc3ed2b5d4c0e847ab4ff1704f4fc463b02d
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2026-06-13T21:42:13+02:00

Commit Message:
DIRECTOR: LINGO: Fix warning

Changed paths:
    engines/director/lingo/lingo-events.cpp


diff --git a/engines/director/lingo/lingo-events.cpp b/engines/director/lingo/lingo-events.cpp
index cd47531690c..d44bc5b28fe 100644
--- a/engines/director/lingo/lingo-events.cpp
+++ b/engines/director/lingo/lingo-events.cpp
@@ -576,6 +576,7 @@ void Movie::queueEvent(Common::Queue<LingoEvent> &queue, LEvent event, int targe
 				queue.push(LingoEvent(kEventMouseUp, mouseUpEventId, kCastHandler, false, pos, spriteId));
 				break;
 			}
+			// fall through
 		case kEventRightMouseUp:
 		case kEventRightMouseDown:
 		case kEventBeginSprite:


Commit: edc6a54044e14879d84d8d1a9f74e37fb1dd8507
    https://github.com/scummvm/scummvm/commit/edc6a54044e14879d84d8d1a9f74e37fb1dd8507
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2026-06-13T22:38:11+02:00

Commit Message:
DIRECTOR: Make savegames mixed-in to SearchSet case-insensitive

This matches the original behavior and prevents bizarre bugs when
we cannot find files, while they're still on disk

Changed paths:
    engines/director/archive.h


diff --git a/engines/director/archive.h b/engines/director/archive.h
index 38ca6e9f68a..071c4fa8d44 100644
--- a/engines/director/archive.h
+++ b/engines/director/archive.h
@@ -283,7 +283,7 @@ public:
 	Common::SeekableReadStream *createReadStreamForMember(const Common::Path &path) const override;
 
 private:
-	typedef Common::HashMap<Common::String, Common::String> FileMap;
+	typedef Common::HashMap<Common::String, Common::String, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> FileMap;
 	FileMap _files;
 };
 



From noreply at scummvm.org  Sat Jun 13 21:19:17 2026
From: noreply at scummvm.org (sev-)
Date: Sat, 13 Jun 2026 21:19:17 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 9686459bb5b18924f5d1e11a9d44a7d5341c5d4b
Message-ID: <20260613211917.09E927005BC@ron.scummvm.net>

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

Summary:
9686459bb5 GRAPHICSL MACGUI: Plug memory leak


Commit: 9686459bb5b18924f5d1e11a9d44a7d5341c5d4b
    https://github.com/scummvm/scummvm/commit/9686459bb5b18924f5d1e11a9d44a7d5341c5d4b
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2026-06-13T23:18:50+02:00

Commit Message:
GRAPHICSL MACGUI: Plug memory leak

Changed paths:
    graphics/macgui/macfontmanager.cpp


diff --git a/graphics/macgui/macfontmanager.cpp b/graphics/macgui/macfontmanager.cpp
index a4a94fae3e3..2e2fde1b9e0 100644
--- a/graphics/macgui/macfontmanager.cpp
+++ b/graphics/macgui/macfontmanager.cpp
@@ -442,6 +442,10 @@ void MacFontManager::loadFonts(Common::MacResManager *fontFile) {
 
 				FontMan.assignFontToName(fontName, font);
 				macfont->setFont(font, false);
+				if (_fontRegistry.contains(fontName)) {
+					warning("MacFontManager: Overwriting font %s", fontName.c_str());
+					delete _fontRegistry.getVal(fontName);
+				}
 				_fontRegistry.setVal(fontName, macfont);
 
 				debugC(5, kDebugLevelMacGUI, " %s", fontName.c_str());



From noreply at scummvm.org  Sat Jun 13 21:58:53 2026
From: noreply at scummvm.org (sev-)
Date: Sat, 13 Jun 2026 21:58:53 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 ed8d6d8fcbed9927ac98588f48e682c68ad8adc2
Message-ID: <20260613215853.D647E7005BC@ron.scummvm.net>

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

Summary:
ed8d6d8fcb DIRECTOR: LINGO: Fix tests to make buildbot happy


Commit: ed8d6d8fcbed9927ac98588f48e682c68ad8adc2
    https://github.com/scummvm/scummvm/commit/ed8d6d8fcbed9927ac98588f48e682c68ad8adc2
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2026-06-13T23:58:19+02:00

Commit Message:
DIRECTOR: LINGO: Fix tests to make buildbot happy

Changed paths:
    engines/director/lingo/tests/builtin.lingo
    engines/director/lingo/tests/tell.lingo


diff --git a/engines/director/lingo/tests/builtin.lingo b/engines/director/lingo/tests/builtin.lingo
index 94348eb2a88..c27faa79d66 100644
--- a/engines/director/lingo/tests/builtin.lingo
+++ b/engines/director/lingo/tests/builtin.lingo
@@ -46,8 +46,6 @@ set the scummvmVersion to 310
 scummvmAssert(version = "3.1")
 set the scummvmVersion to 404
 scummvmAssert(version = "4.0.4")
-set the scummvmVersion to 1201
-scummvmAssert(version = "12.0.1")
 
 set the scummvmVersion to save
 
diff --git a/engines/director/lingo/tests/tell.lingo b/engines/director/lingo/tests/tell.lingo
index f724611dde4..ff31067de6b 100644
--- a/engines/director/lingo/tests/tell.lingo
+++ b/engines/director/lingo/tests/tell.lingo
@@ -3,7 +3,6 @@ tell window "ball" to puppettempo 5
 tell window "hello" to continue
 
 tell window childMovie
-	go to frame 50
 	set the stageColor to 100
 	updateStage
 end tell



From noreply at scummvm.org  Sat Jun 13 22:08:28 2026
From: noreply at scummvm.org (bluegr)
Date: Sat, 13 Jun 2026 22:08:28 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 1c5a9ed526efec93b1a9d837faa2cfecdd957652
Message-ID: <20260613220828.B89E37005BC@ron.scummvm.net>

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:
e36beef173 NANCY: Implement video start flags for secondary movies in Nancy10+
2ae68a2390 NANCY: Implement autotext lookup for sound names
1c5a9ed526 NANCY: Fix regression with font height calculation in Nancy6+


Commit: e36beef17375dd450ca84f25b9aa12c909eafb00
    https://github.com/scummvm/scummvm/commit/e36beef17375dd450ca84f25b9aa12c909eafb00
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-06-14T01:08:17+03:00

Commit Message:
NANCY: Implement video start flags for secondary movies in Nancy10+

Fixes:
- Meeting Dave in the cellar (hack no longer needed)
- Partial fix for the feed bins puzzle

Changed paths:
    engines/nancy/action/secondarymovie.cpp
    engines/nancy/action/secondarymovie.h
    engines/nancy/state/scene.cpp


diff --git a/engines/nancy/action/secondarymovie.cpp b/engines/nancy/action/secondarymovie.cpp
index c8194710fc5..0aef5ece44b 100644
--- a/engines/nancy/action/secondarymovie.cpp
+++ b/engines/nancy/action/secondarymovie.cpp
@@ -69,7 +69,12 @@ void PlaySecondaryMovie::readData(Common::SeekableReadStream &stream) {
 	ser.syncAsUint16LE(_lastFrame);
 
 	ser.syncAsSint16LE(_sceneChange.sceneID, kGameTypeNancy10);
-	ser.skip(5 * 2, kGameTypeNancy10);	// TODO
+	ser.skip(3 * 2, kGameTypeNancy10);	// TODO
+
+	if (g_nancy->getGameType() >= kGameTypeNancy10) {
+		ser.syncAsSint16LE(_videoStartFlag.label);
+		ser.syncAsUint16LE(_videoStartFlag.flag);
+	}
 
 	if (ser.getVersion() >= kGameTypeNancy1) {
 		uint size = 15;
@@ -170,6 +175,9 @@ void PlaySecondaryMovie::execute() {
 
 		NancySceneState.setActiveMovie(this);
 
+		if (g_nancy->getGameType() >= kGameTypeNancy10)
+			NancySceneState.setEventFlag(_videoStartFlag);
+
 		_state = kRun;
 
 		if (Common::Rect(_decoder->getWidth(), _decoder->getHeight()) == NancySceneState.getViewport().getBounds()) {
diff --git a/engines/nancy/action/secondarymovie.h b/engines/nancy/action/secondarymovie.h
index 92078cd92fb..555706712b8 100644
--- a/engines/nancy/action/secondarymovie.h
+++ b/engines/nancy/action/secondarymovie.h
@@ -83,6 +83,7 @@ public:
 	uint16 _lastFrame = 0;
 	Common::Array<FlagAtFrame> _frameFlags;
 	MultiEventFlagDescription _triggerFlags;
+	FlagDescription _videoStartFlag;
 
 	SoundDescription _sound;
 
diff --git a/engines/nancy/state/scene.cpp b/engines/nancy/state/scene.cpp
index 808785d48a7..f3f856c11ce 100644
--- a/engines/nancy/state/scene.cpp
+++ b/engines/nancy/state/scene.cpp
@@ -250,14 +250,6 @@ void Scene::changeScene(const SceneChangeDescription &sceneDescription) {
 		return;
 	}
 
-	// HACK: The event flag for meeting Dave in the cellar is not set
-	// correctly in Nancy 10, so we hardcode the flag change here to
-	// avoid talking with him over and over again.
-	// TODO: Find out why this flag is not set correctly and implement a
-	// proper solution for it.
-	if (g_nancy->getGameType() == kGameTypeNancy10 && sceneDescription.sceneID == 3996)
-		NancySceneState.setEventFlag(314, g_nancy->_true);	// EV_Met_DG_Cellar
-
 	_sceneState.nextScene = sceneDescription;
 	_state = kLoad;
 }


Commit: 2ae68a2390a0b7162203c4e5072c9d27cd21ff2c
    https://github.com/scummvm/scummvm/commit/2ae68a2390a0b7162203c4e5072c9d27cd21ff2c
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-06-14T01:08:18+03:00

Commit Message:
NANCY: Implement autotext lookup for sound names

Changed paths:
    engines/nancy/action/soundrecords.cpp
    engines/nancy/commontypes.cpp
    engines/nancy/util.cpp
    engines/nancy/util.h


diff --git a/engines/nancy/action/soundrecords.cpp b/engines/nancy/action/soundrecords.cpp
index c27435c3a5d..43559183a8c 100644
--- a/engines/nancy/action/soundrecords.cpp
+++ b/engines/nancy/action/soundrecords.cpp
@@ -259,6 +259,9 @@ void StopSound::execute() {
 void PlayRandomSound::readData(Common::SeekableReadStream &stream) {
 	uint16 numSounds = stream.readUint16LE();
 	readFilenameArray(stream, _soundNames, numSounds - 1);
+	for (Common::String &n : _soundNames) {
+		resolveSoundNameAutoText(n);
+	}
 
 	PlaySound::readData(stream);
 	_soundNames.push_back(_sound.name);
@@ -275,6 +278,9 @@ void PlayRandomSound::execute() {
 void PlayRandomSoundTerse::readData(Common::SeekableReadStream &stream) {
 	uint16 numSounds = stream.readUint16LE();
 	readFilenameArray(stream, _soundNames, numSounds - 1);
+	for (Common::String &n : _soundNames) {
+		resolveSoundNameAutoText(n);
+	}
 
 	PlaySoundTerse::readData(stream);
 
diff --git a/engines/nancy/commontypes.cpp b/engines/nancy/commontypes.cpp
index 512502da263..15c8887f2f7 100644
--- a/engines/nancy/commontypes.cpp
+++ b/engines/nancy/commontypes.cpp
@@ -194,6 +194,7 @@ void SoundDescription::readDIGI(Common::SeekableReadStream &stream) {
 	s.setVersion(g_nancy->getGameType());
 
 	readFilename(s, name);
+	resolveSoundNameAutoText(name);
 
 	s.syncAsUint16LE(channelID);
 
@@ -261,6 +262,7 @@ void SoundDescription::readScene(Common::SeekableReadStream &stream) {
 
 void SoundDescription::readTerse(Common::SeekableReadStream &stream) {
 	readFilename(stream, name);
+	resolveSoundNameAutoText(name);
 	channelID = stream.readUint16LE();
 	numLoops = stream.readUint32LE();
 	volume = stream.readUint16LE();
diff --git a/engines/nancy/util.cpp b/engines/nancy/util.cpp
index 6c9518635f1..6ec9e89a96b 100644
--- a/engines/nancy/util.cpp
+++ b/engines/nancy/util.cpp
@@ -248,6 +248,16 @@ void readFilenameArray(Common::Serializer &stream, Common::Array<Common::Path> &
 	}
 }
 
+// Names with a leading '*' are CVTX (AUTOTEXT) keys for the real filename.
+void resolveSoundNameAutoText(Common::String &name) {
+	if (name.empty() || name.firstChar() != '*') {
+		return;
+	}
+	const CVTX *autotext = (const CVTX *)g_nancy->getEngineData("AUTOTEXT");
+	name.deleteChar(0);
+	name = getTextFromCaseInsensitiveKey(autotext->texts, name);
+}
+
 void readUIButton(Common::SeekableReadStream &stream, UIButtonRecord &dst) {
 	// Read common fields for both buttons and sliders
 	readFilename(stream, dst.primaryImageName);
diff --git a/engines/nancy/util.h b/engines/nancy/util.h
index 0977792e16f..5a96d20e29e 100644
--- a/engines/nancy/util.h
+++ b/engines/nancy/util.h
@@ -58,6 +58,8 @@ void readFilenameArray(Common::Serializer &stream, Common::Array<Common::String>
 void readFilenameArray(Common::SeekableReadStream &stream, Common::Array<Common::Path> &inArray, uint num);
 void readFilenameArray(Common::Serializer &stream, Common::Array<Common::Path> &inArray, uint num, Common::Serializer::Version minVersion = 0, Common::Serializer::Version maxVersion = Common::Serializer::kLastVersion);
 
+void resolveSoundNameAutoText(Common::String &name);
+
 void assembleTextLine(char *rawCaption, Common::String &output, uint size);
 
 void readUIButton(Common::SeekableReadStream &stream, UIButtonRecord &dst);


Commit: 1c5a9ed526efec93b1a9d837faa2cfecdd957652
    https://github.com/scummvm/scummvm/commit/1c5a9ed526efec93b1a9d837faa2cfecdd957652
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-06-14T01:08:19+03:00

Commit Message:
NANCY: Fix regression with font height calculation in Nancy6+

A regression from 24dda66e7783fad9d73b5f71dbfe73cea2e0b05b.

Fix #16855

Changed paths:
    engines/nancy/font.cpp


diff --git a/engines/nancy/font.cpp b/engines/nancy/font.cpp
index 2235f9cc837..e247e5ede9b 100644
--- a/engines/nancy/font.cpp
+++ b/engines/nancy/font.cpp
@@ -147,7 +147,7 @@ void Font::read(Common::SeekableReadStream &stream) {
 	}
 
 	if (g_nancy->getGameType() >= kGameTypeNancy6) {
-		_fontHeight = getCharWidth('o') * 2 - 1;
+		_fontHeight = (getCharWidth('o') - 1) * 2;
 	}
 
 	_textboxData = GetEngineData(TBOX);



From noreply at scummvm.org  Sat Jun 13 23:15:27 2026
From: noreply at scummvm.org (dreammaster)
Date: Sat, 13 Jun 2026 23:15:27 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 5535579583457b410b391a9d29f1b13e8022d082
Message-ID: <20260613231527.2BB317005BC@ron.scummvm.net>

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:
31238da81e MADS: FOREST: Implemented room 322
5535579583 MADS: FOREST: Implemented room 401


Commit: 31238da81ef7fda4dfb12072c69f06d79ae6aa67
    https://github.com/scummvm/scummvm/commit/31238da81ef7fda4dfb12072c69f06d79ae6aa67
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-06-14T09:14:53+10:00

Commit Message:
MADS: FOREST: Implemented room 322

Changed paths:
    engines/mads/madsv2/forest/rooms/room322.cpp


diff --git a/engines/mads/madsv2/forest/rooms/room322.cpp b/engines/mads/madsv2/forest/rooms/room322.cpp
index a5ca31e41d6..863509384a3 100644
--- a/engines/mads/madsv2/forest/rooms/room322.cpp
+++ b/engines/mads/madsv2/forest/rooms/room322.cpp
@@ -24,8 +24,14 @@
 #include "mads/madsv2/core/imath.h"
 #include "mads/madsv2/core/inter.h"
 #include "mads/madsv2/core/kernel.h"
+#include "mads/madsv2/core/digi.h"
+#include "mads/madsv2/core/midi.h"
+#include "mads/madsv2/core/object.h"
+#include "mads/madsv2/core/player.h"
 #include "mads/madsv2/core/sound.h"
+#include "mads/madsv2/core/sprite.h"
 #include "mads/madsv2/core/text.h"
+#include "mads/madsv2/forest/journal.h"
 #include "mads/madsv2/forest/mads/inventory.h"
 #include "mads/madsv2/forest/mads/sounds.h"
 #include "mads/madsv2/forest/mads/words.h"
@@ -39,10 +45,28 @@ namespace Forest {
 namespace Rooms {
 
 struct Scratch {
-	int16 sprite[10];       /* Sprite series handles */
-	int16 sequence[10];     /* Sequence handles      */
-	int16 animation[10];     /* Animation handles     */
-	AnimationInfo animation_info[10];
+	int16 sprite[10];                  // 0x00
+	int16 sequence[10];                // 0x14
+	int16 animation[10];               // 0x28
+	AnimationInfo animation_info[11];  // 0x3C
+	int16 _94;
+	int16 _96;
+	int16 _98;
+	int16 _9a;
+	int16 _9c;
+	int16 _9e;
+	int16 _a0;
+	int16 _a2;
+	int16 _a4;
+	int16 _a6;
+	int16 _a8;
+	int16 _aa;  // 0xAA
+	int16 _ac;  // 0xAC
+	int16 _ae;
+	int16 _b0;
+	int16 _b2;
+	int16 _b4;  // 0xB4
+	int16 _b6;  // 0xB6
 };
 
 static Scratch scratch;
@@ -54,22 +78,738 @@ static Scratch scratch;
 #define aainfo scratch.animation_info
 
 
+static void room_322_init1() {
+	global[g131] = 0;
+	global[g141] = 0;
+
+	if (previous_room != 308 && previous_room != 301) {
+		scratch._a6 = kernel_run_animation_disp('r', 9, 0);
+		kernel_position_anim(scratch._a6, 18, 130, 95, 3);
+		scratch._a4 = kernel_run_animation_disp('e', 6, 0);
+		kernel_position_anim(scratch._a4, 71, 81, 87, 7);
+	} else {
+		scratch._a4 = -1;
+	}
+
+	if (previous_room != KERNEL_RESTORING_GAME) {
+		player.x = 106;
+		player.y = 107;
+		player.facing = 3;
+	}
+
+	scratch._b6 = 0;
+
+	if (previous_room == KERNEL_RESTORING_GAME) {
+		global[g131] = -1;
+		global[g141] = -1;
+		kernel_reset_animation(scratch._a4, 2);
+		kernel_reset_animation(scratch._a6, 2);
+		global[g133] = 0;
+		global[g143] = 0;
+		return;
+	}
+
+	if (previous_room == 199) {
+		global[g131] = -1;
+		global[g141] = -1;
+		kernel_reset_animation(scratch._a4, 2);
+		kernel_reset_animation(scratch._a6, 2);
+		global[g133] = 0;
+		global[g143] = 0;
+		restore_player();
+		player.commands_allowed = -1;
+		player.walker_visible = -1;
+		return;
+	}
+
+	if (previous_room == 210) {
+		aa[0] = kernel_run_animation("*rm307y1", 100);
+		aainfo[0]._frame = -1;
+		scratch._9c = 85;
+		return;
+	}
+
+	if (previous_room == 301) {
+		aa[0] = kernel_run_animation("*rm307y2", 100);
+		aainfo[0]._frame = -1;
+		scratch._9c = 40;
+		return;
+	}
+
+	if (previous_room == 308) {
+		midi_stop();
+		aa[0] = kernel_run_animation("*rm307y3", 100);
+		aainfo[0]._frame = -1;
+		scratch._9c = 109;
+		scratch._b6 = -1;
+		return;
+	}
+
+	global[g131] = -1;
+	global[g141] = -1;
+	kernel_reset_animation(scratch._a4, 2);
+	kernel_reset_animation(scratch._a6, 2);
+	global[g133] = 0;
+	global[g143] = 0;
+	player.commands_allowed = -1;
+	player.walker_visible = -1;
+}
+
+static void room_322_anim2() {
+	int16 result = -1;
+	int16 seq_handle;
+
+	int16 cur = kernel_anim[aa[2]].frame;
+	if (cur == aainfo[2]._val3)
+		return;
+
+	aainfo[2]._val3 = cur;
+
+	if (scratch._b6 == 100) {
+		if (cur == 31) {
+			digi_play_build(307, '_', 2, 2);
+			scratch._b4 = -1;
+		} else if (cur == 35) {
+			digi_play_build(101, '_', 1, 2);
+			scratch._b4 = -1;
+		}
+	} else if (scratch._b6 == 0) {
+		if (cur == 54) {
+			dont_frag_the_palette();
+			kernel_abort_animation(aa[2]);
+			aainfo[2]._frame = 0;
+			kernel_reset_animation(scratch._a6, 1);
+			kernel_synch(KERNEL_ANIM, scratch._a6, KERNEL_NOW, 0);
+			global[g143] = 0;
+			kernel_reset_animation(scratch._a4, 1);
+			kernel_synch(KERNEL_ANIM, scratch._a4, KERNEL_NOW, 0);
+			global[g133] = 0;
+			player.commands_allowed = -1;
+		} else if (cur < 54) {
+			if (cur == 21) {
+				seq_handle = seq[1];
+				kernel_seq_delete(seq_handle);
+				kernel_synch(KERNEL_SERIES, seq_handle, KERNEL_ANIM, aa[2]);
+				inter_move_object(14, 2);
+			} else if (cur == 22) {
+				seq_handle = seq[0];
+				kernel_seq_delete(seq_handle);
+				inter_move_object(4, 2);
+				kernel_synch(KERNEL_SERIES, seq_handle, KERNEL_ANIM, aa[2]);
+				kernel_flip_hotspot(113, 0);
+			} else if (cur == 28) {
+				digi_play_build(307, '_', 2, 2);
+			} else if (cur == 31) {
+				digi_play_build(101, '_', 1, 2);
+			}
+		}
+	} else {
+		if (cur == 48) {
+			digi_play_build(307, '_', 2, 2);
+			scratch._b4 = -1;
+		} else if (cur == 53) {
+			digi_play_build(101, '_', 1, 2);
+			scratch._b4 = -1;
+		}
+	}
+
+	if (result >= 0) {
+		kernel_reset_animation(aa[2], result);
+		aainfo[2]._val3 = result;
+	}
+}
+
+static void room_322_anim3() {
+	int16 cur = kernel_anim[aa[8]].frame;
+	if (cur != aainfo[8]._val3) {
+		aainfo[8]._val3 = cur;
+		if (cur == 22 && scratch._a4 == -1)
+			digi_play_build(101, '_', 2, 2);
+	}
+
+	if (global[player_hyperwalked] != -1)
+		return;
+
+	aainfo[8]._val3 = scratch._9c - 1;
+	kernel_reset_animation(aa[8], aainfo[8]._val3);
+}
+
+static void room_322_anim4() {
+	int16 result = -1;
+	int16 var_2;
+
+	int16 cur = kernel_anim[aa[9]].frame;
+	if (cur == aainfo[9]._val3)
+		return;
+
+	aainfo[9]._val3 = cur;
+
+	if (cur == 43) {
+		var_2 = seq[1];
+		kernel_seq_delete(var_2);
+		kernel_synch(KERNEL_SERIES, var_2, KERNEL_ANIM, aa[9]);
+	} else if (cur <= 43) {
+		if (cur == 3) {
+			digi_play_build(307, 'e', 5, 1);
+			scratch._b4 = -1;
+		} else if (cur == 20) {
+			digi_play_build(307, 'r', 2, 1);
+			scratch._b4 = -1;
+		}
+	}
+
+	if (result >= 0) {
+		kernel_reset_animation(aa[9], result);
+		aainfo[9]._val3 = result;
+	}
+}
+
+static void room_322_anim5() {
+	int16 cur = kernel_anim[aa[1]].frame;
+	if (cur != aainfo[1]._val3)
+		aainfo[1]._val3 = cur;
+
+	if (global[player_hyperwalked] != -1)
+		return;
+
+	aainfo[1]._val3 = 65;
+	kernel_reset_animation(aa[1], 65);
+}
+
+static void room_322_anim6() {
+	int16 var_2 = -1;
+
+	int16 cur = kernel_anim[aa[3]].frame;
+	if (cur == aainfo[3]._val3)
+		return;
+
+	aainfo[3]._val3 = cur;
+
+	if (cur == 57) {
+		dont_frag_the_palette();
+		kernel_abort_animation(aa[3]);
+		aainfo[3]._frame = 0;
+		kernel_reset_animation(scratch._a4, 1);
+		kernel_synch(KERNEL_ANIM, scratch._a4, KERNEL_NOW, 0);
+		global[g133] = 0;
+		player.commands_allowed = -1;
+	} else if (cur == 29) {
+		digi_play_build(305, 'e', 1, 1);
+		scratch._b4 = 3;
+	} else if (cur == 30 || cur == 31) {
+		if (aainfo[3]._val4 == 1)
+			var_2 = imath_random(29, 30);
+	}
+
+	if (var_2 >= 0) {
+		kernel_reset_animation(aa[3], var_2);
+		aainfo[3]._val3 = var_2;
+	}
+}
+
+static void room_322_anim7() {
+	int16 result = -1;
+
+	int16 cur = kernel_anim[aa[4]].frame;
+	if (cur == aainfo[4]._val3)
+		return;
+
+	aainfo[4]._val3 = cur;
+
+	if (cur == 29) {
+		digi_play_build(322, 'b', 1, 1);
+		scratch._b4 = -1;
+	}
+
+	if (result >= 0) {
+		kernel_reset_animation(aa[4], result);
+		aainfo[4]._val3 = result;
+	}
+}
+
+static void room_322_anim8() {
+	int16 result = -1;
+
+	int16 cur = kernel_anim[aainfo[0]._active].frame;
+	if (cur == aainfo[10]._val3)
+		return;
+
+	aainfo[10]._val3 = cur;
+
+	if (cur == 17) {
+		digi_play_build(322, '_', 1, 2);
+	} else if (cur == 27) {
+		global[g009] = 0;
+		global_digi_play(4);
+	}
+
+	if (result >= 0) {
+		kernel_reset_animation(aainfo[0]._active, result);
+		aainfo[10]._val3 = result;
+	}
+}
+
+static void room_322_anim1() {
+	int16 cur = kernel_anim[aa[0]].frame;
+	if (cur != aainfo[0]._val3)
+		aainfo[0]._val3 = cur;
+
+	if (global[player_hyperwalked] != -1)
+		return;
+
+	aainfo[0]._val3 = scratch._9c - 1;
+	kernel_reset_animation(aa[0], aainfo[0]._val3);
+}
+
 static void room_322_init() {
+	scratch._aa = 0;
+	scratch._ac = -1;
+
+	object_set_quality(4, 0, -1L);
+	object_set_quality(14, 0, -1L);
+
+	kernel_flip_hotspot(global[g075] ? 28 : 27, 0);
+
+	if (object[4].location == 307) {
+		ss[0] = kernel_load_series("*rm307p1", 0);
+		seq[0] = kernel_seq_stamp(ss[0], false, -1);
+		kernel_seq_depth(seq[0], 10);
+		kernel_seq_loc(seq[0], 184, 149);
+		kernel_seq_scale(seq[0], 98);
+	} else {
+		kernel_flip_hotspot(113, 0);
+	}
+
+	if (object[14].location == 307) {
+		ss[1] = kernel_load_series("*rm307p2", 0);
+		seq[1] = kernel_seq_stamp(ss[1], false, -1);
+		kernel_seq_depth(seq[1], 9);
+		kernel_seq_loc(seq[1], 146, 134);
+		kernel_seq_scale(seq[1], 100);
+	}
+
+	ss[2] = kernel_load_series(kernel_name('u', 2), 0);
+	seq[2] = kernel_seq_stamp(ss[2], false, -1);
+	kernel_seq_depth(seq[2], 14);
+	kernel_seq_loc(seq[2], 227, 77);
+	kernel_seq_scale(seq[2], 90);
+
+	if (previous_room != KERNEL_RESTORING_GAME) {
+		if (previous_room != 199) {
+			player.walker_visible = 0;
+			player.commands_allowed = 0;
+			scratch._b4 = 0;
+		}
+
+		for (int count = 0; count < 11; count++) {
+			aainfo[count]._frame = 0;
+			aainfo[count]._val3 = 1;
+			aainfo[count]._val4 = 0;
+			aainfo[count]._active = 0;
+		}
+
+		if (previous_room != 199 && flags[25] != 3)
+			flags[25]++;
+	}
+
+	bool was_neg3 = (flags[25] == -3);
+	room_322_init1();
+	if (was_neg3)
+		flags[25] = 0;
 }
 
 static void room_322_daemon() {
+	switch (kernel.trigger) {
+	case 7:
+		if (global[walker_converse_state]) {
+			global[walker_converse_state] = 0;
+			close_journal(3);
+			player.commands_allowed = -1;
+		} else if (scratch._b4 == 1) {
+			global[g150] = -1;
+			dont_frag_the_palette();
+			kernel_abort_animation(scratch._ae);
+			player.walker_visible = -1;
+			kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
+		} else if (scratch._b4 == 2) {
+			kernel_timing_trigger(40, 114);
+		} else if (scratch._b4 == 3) {
+			aainfo[3]._val4 = 2;
+			scratch._b4 = -1;
+			scratch._aa = 0;
+		} else if (scratch._b4 == 4) {
+			dont_frag_the_palette();
+			kernel_abort_animation(scratch._ae);
+			scratch._b4 = -1;
+			scratch._aa = 0;
+			scratch._a8 = kernel_run_animation_point(1, 104);
+			kernel_position_anim(scratch._a8, player.x, player.y, player.scale, player.depth);
+			kernel_synch(KERNEL_ANIM, scratch._a8, KERNEL_NOW, 0);
+		} else if (scratch._b4 == 41) {
+			dont_frag_the_palette();
+			kernel_abort_animation(scratch._ae);
+			scratch._b4 = -1;
+			scratch._aa = 0;
+			scratch._a8 = kernel_run_animation_point(3, 105);
+			kernel_position_anim(scratch._a8, player.x, player.y, player.scale, player.depth);
+			kernel_synch(KERNEL_ANIM, scratch._a8, KERNEL_NOW, 0);
+		}
+		break;
+
+	case 24:
+		aa[3] = kernel_run_animation("*rm307e1", 0);
+		aainfo[3]._frame = -1;
+		aainfo[3]._val4 = 1;
+		kernel_reset_animation(scratch._a4, 0);
+		kernel_synch(KERNEL_ANIM, aa[3], KERNEL_ANIM, scratch._a4);
+		global[g133] = 1;
+		break;
+
+	case 26:
+		if (scratch._ac == 1) {
+			aa[1] = kernel_run_animation("*rm307z1", 118);
+			aainfo[1]._frame = -1;
+			scratch._a0 = 1;
+			player.commands_allowed = 0;
+			player.walker_visible = 0;
+			kernel_reset_animation(scratch._a4, 0);
+			kernel_reset_animation(scratch._a6, 0);
+			kernel_synch(KERNEL_ANIM, scratch._a4, KERNEL_ANIM, aa[1]);
+			kernel_synch(KERNEL_ANIM, scratch._a6, KERNEL_ANIM, aa[1]);
+			kernel_synch(KERNEL_PLAYER, 0, KERNEL_ANIM, aa[1]);
+			global[g133] = 1;
+			global[g143] = 1;
+		} else if (scratch._ac == 2) {
+			dont_frag_the_palette();
+			kernel_abort_animation(scratch._a4);
+			dont_frag_the_palette();
+			kernel_abort_animation(scratch._a6);
+			scratch._a4 = -1;
+			kernel_reset_animation(scratch._a6, 0);
+			if (player_has(4)) {
+				aa[8] = kernel_run_animation("*rm307r4", 111);
+				aainfo[8]._frame = -1;
+				scratch._9c = 32;
+				kernel_synch(KERNEL_ANIM, aa[8], KERNEL_ANIM, scratch._a6);
+			} else {
+				aa[9] = kernel_run_animation("*rm307r3", 111);
+				aainfo[9]._frame = -1;
+				kernel_synch(KERNEL_ANIM, aa[9], KERNEL_ANIM, scratch._a6);
+			}
+		} else if (scratch._ac == 3) {
+			aa[7] = kernel_run_animation(kernel_name('z', 2), 116);
+			player.commands_allowed = 0;
+			player.walker_visible = 0;
+			kernel_reset_animation(scratch._a4, 0);
+			kernel_reset_animation(scratch._a6, 0);
+			kernel_synch(KERNEL_ANIM, scratch._a4, KERNEL_ANIM, aa[7]);
+			kernel_synch(KERNEL_ANIM, scratch._a6, KERNEL_ANIM, aa[7]);
+			kernel_synch(KERNEL_PLAYER, 0, KERNEL_ANIM, aa[7]);
+			global[g133] = 1;
+			global[g143] = 1;
+		} else if (scratch._ac == 4) {
+			aa[2] = kernel_run_animation("*rm307r2", 0);
+			aainfo[2]._frame = -1;
+			kernel_reset_animation(scratch._a6, 0);
+			kernel_synch(KERNEL_ANIM, aa[2], KERNEL_ANIM, scratch._a6);
+			global[g143] = 1;
+			kernel_reset_animation(scratch._a4, 0);
+			kernel_synch(KERNEL_ANIM, scratch._a4, KERNEL_ANIM, scratch._a6);
+			digi_play_build(307, 'e', 5, 1);
+			scratch._b4 = -1;
+		}
+		break;
+
+	case 100:
+		dont_frag_the_palette();
+		kernel_abort_animation(aa[0]);
+		aainfo[0]._frame = 0;
+		if (previous_room == 308) {
+			aa[2] = kernel_run_animation("*rm307r6", 113);
+			aainfo[2]._frame = -1;
+			kernel_synch(KERNEL_ANIM, aa[2], KERNEL_NOW, 0);
+			scratch._b6 = 100;
+		} else {
+			aa[4] = kernel_run_animation(kernel_name('s', 1), 115);
+			aainfo[4]._frame = -1;
+			kernel_synch(KERNEL_ANIM, aa[4], KERNEL_NOW, 0);
+		}
+		break;
+
+	case 104:
+		dont_frag_the_palette();
+		kernel_abort_animation(scratch._a8);
+		kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
+		player.walker_visible = -1;
+		global[g135] = -1;
+		break;
+
+	case 105:
+		dont_frag_the_palette();
+		kernel_abort_animation(scratch._a8);
+		kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
+		player.walker_visible = -1;
+		global[g150] = -1;
+		scratch._ac = 4;
+		break;
+
+	case 111: {
+		int16 seq_handle;
+		if (player_has(14)) {
+			dont_frag_the_palette();
+			kernel_abort_animation(aa[8]);
+		} else {
+			seq_handle = seq[0];
+			kernel_seq_delete(seq_handle);
+			dont_frag_the_palette();
+			kernel_abort_animation(aa[9]);
+			kernel_synch(KERNEL_SERIES, seq_handle, KERNEL_NOW, 0);
+			aainfo[9]._frame = 0;
+		}
+
+		scratch._a4 = -2;
+		aa[8] = kernel_run_animation("*rm307z3", 112);
+		aainfo[8]._frame = -1;
+		scratch._9c = 100;
+		player.walker_visible = 0;
+		kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
+
+		if (player_has(14)) {
+			kernel_synch(KERNEL_ANIM, aa[8], KERNEL_NOW, 0);
+		} else {
+			kernel_synch(KERNEL_ANIM, aa[9], KERNEL_NOW, 0);
+			inter_move_object(14, 2);
+			inter_move_object(4, 2);
+		}
+		break;
+	}
+
+	case 112:
+		dont_frag_the_palette();
+		kernel_abort_animation(aa[8]);
+		aainfo[8]._frame = 0;
+		scratch._b4 = -1;
+		kernel_seq_delete(seq[2]);
+		aainfo[0]._active = kernel_run_animation(kernel_name('j', 1), 114);
+		aainfo[10]._frame = -1;
+		digi_play_build(322, 'u', 1, 1);
+		break;
+
+	case 113:
+		dont_frag_the_palette();
+		kernel_abort_animation(aa[2]);
+		aainfo[2]._frame = 0;
+		aa[4] = kernel_run_animation(kernel_name('s', 1), 115);
+		aainfo[4]._frame = -1;
+		kernel_synch(KERNEL_ANIM, aa[4], KERNEL_NOW, 0);
+		break;
+
+	case 114:
+		new_room = 308;
+		midi_stop();
+		break;
+
+	case 115:
+		dont_frag_the_palette();
+		kernel_abort_animation(aa[4]);
+		aainfo[4]._frame = 0;
+		if (previous_room == 210) {
+			global[g141] = -1;
+			global[g143] = 0;
+			kernel_reset_animation(scratch._a6, 1);
+			kernel_synch(KERNEL_ANIM, scratch._a6, KERNEL_NOW, 0);
+			global[g131] = -1;
+			global[g133] = 0;
+		} else {
+			scratch._a6 = kernel_run_animation_disp('r', 9, 0);
+			global[g141] = -1;
+			global[g143] = 0;
+			kernel_position_anim(scratch._a6, 18, 130, 95, 3);
+			kernel_reset_animation(scratch._a6, 1);
+			kernel_synch(KERNEL_ANIM, scratch._a6, KERNEL_NOW, 0);
+			scratch._a4 = kernel_run_animation_disp('e', 6, 0);
+			global[g131] = -1;
+			global[g133] = 0;
+			kernel_position_anim(scratch._a4, 71, 81, 87, 7);
+		}
+		kernel_reset_animation(scratch._a4, 1);
+		kernel_synch(KERNEL_ANIM, scratch._a4, KERNEL_NOW, 0);
+		player.commands_allowed = -1;
+		player.walker_visible = -1;
+		kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
+		break;
+
+	case 116:
+		dont_frag_the_palette();
+		kernel_abort_animation(aa[7]);
+		scratch._b4 = -1;
+		kernel_seq_delete(seq[2]);
+		aainfo[0]._active = kernel_run_animation(kernel_name('j', 1), 117);
+		aainfo[10]._frame = -1;
+		digi_play_build(322, 'u', 1, 1);
+		break;
+
+	case 117:
+		new_room = 301;
+		midi_stop();
+		break;
+
+	case 118:
+		dont_frag_the_palette();
+		kernel_abort_animation(aa[1]);
+		aainfo[1]._frame = 0;
+		scratch._b4 = -1;
+		kernel_seq_delete(seq[2]);
+		aainfo[0]._active = kernel_run_animation(kernel_name('j', 1), 119);
+		aainfo[10]._frame = -1;
+		digi_play_build(322, 'u', 1, 1);
+		break;
+
+	case 119:
+		global[g061] = -1;
+		new_room = 210;
+		midi_stop();
+		break;
+
+	default:
+		break;
+	}
+
+	if (scratch._a4 >= 0) {
+		global_anim1(6, scratch._a4, global[g131], &global[g132]);
+		global_anim2(9, scratch._a6, global[g141], &global[g142]);
+	}
+
+	if (aainfo[0]._frame)
+		room_322_anim1();
+	if (aainfo[2]._frame)
+		room_322_anim2();
+	if (aainfo[8]._frame)
+		room_322_anim3();
+	if (aainfo[9]._frame)
+		room_322_anim4();
+	if (aainfo[1]._frame)
+		room_322_anim5();
+	if (aainfo[3]._frame)
+		room_322_anim6();
+	if (aainfo[4]._frame)
+		room_322_anim7();
+	if (aainfo[10]._frame)
+		room_322_anim8();
+
+	if (!scratch._aa)
+		return;
+
+	kernel_random_frame(scratch._ae, &global[g151], global[g154]);
+	if (scratch._b4 == 41 || scratch._b4 == 4)
+		return;
+	kernel_random_frame(scratch._b0, &global[g152], global[g155]);
+	kernel_random_frame(scratch._b2, &global[g153], global[g156]);
 }
 
 static void room_322_pre_parser() {
+	if (player_parse(34, 0))
+		player_walk(21, 101, 4);
+
+	if (player_parse(41, 0)) {
+		player_walk(96, 120, 3);
+		global[g017] = 0;
+	}
 }
 
 static void room_322_parser() {
+	if (global[walker_converse_state]) {
+		player.commands_allowed = 0;
+		digi_play_build_ii('c', 1, 1);
+		player.command_ready = 0;
+		return;
+	}
+
+	if (player_parse(34, 0)) {
+		global[g150] = -1;
+		scratch._ac = 3;
+		player.commands_allowed = 0;
+		player.command_ready = 0;
+		return;
+	}
+
+	if (player_parse(113, 0)) {
+		global[g154] = 2;
+		player.walker_visible = 0;
+		player.commands_allowed = 0;
+		scratch._ae = kernel_run_animation_talk('b', 2, 0);
+		kernel_position_anim(scratch._ae, player.x, player.y, player.scale, player.depth);
+		kernel_synch(KERNEL_ANIM, scratch._ae, KERNEL_PLAYER, 0);
+		digi_play_build_ii('b', 1, 1);
+		scratch._b4 = 41;
+		scratch._aa = -1;
+		player.command_ready = 0;
+		return;
+	}
+
+	if (player_parse(28, 0)) {
+		global[g154] = 2;
+		player.walker_visible = 0;
+		player.commands_allowed = 0;
+		scratch._ae = kernel_run_animation_talk('b', 1, 0);
+		kernel_position_anim(scratch._ae, player.x, player.y, player.scale, player.depth);
+		kernel_synch(KERNEL_ANIM, scratch._ae, KERNEL_PLAYER, 0);
+		digi_play_build_ii('b', 1, 1);
+		scratch._b4 = 4;
+		scratch._aa = -1;
+		kernel_flip_hotspot(28, 0);
+		kernel_flip_hotspot(27, -1);
+		global[g075] = -1;
+		player.command_ready = 0;
+		return;
+	}
+
+	if (player_parse(27, 0)) {
+		global[g150] = -1;
+		scratch._ac = 1;
+		player.commands_allowed = 0;
+		player.command_ready = 0;
+		return;
+	}
+
+	if (player_parse(41, 0)) {
+		global[g150] = -1;
+		player.commands_allowed = 0;
+		scratch._ac = 2;
+		player.command_ready = 0;
+		return;
+	}
+
+	if (global[walker_converse_state]) {
+		global[walker_converse_state] = 0;
+		close_journal(3);
+		player.command_ready = 0;
+	}
 }
 
 void room_322_synchronize(Common::Serializer &s) {
 	for (int16 &v : scratch.sprite)    s.syncAsSint16LE(v);
 	for (int16 &v : scratch.sequence)  s.syncAsSint16LE(v);
 	for (int16 &v : scratch.animation) s.syncAsSint16LE(v);
+	for (AnimationInfo &ai : scratch.animation_info) ai.synchronize(s);
+	s.syncAsSint16LE(scratch._94);
+	s.syncAsSint16LE(scratch._96);
+	s.syncAsSint16LE(scratch._98);
+	s.syncAsSint16LE(scratch._9a);
+	s.syncAsSint16LE(scratch._9c);
+	s.syncAsSint16LE(scratch._9e);
+	s.syncAsSint16LE(scratch._a0);
+	s.syncAsSint16LE(scratch._a2);
+	s.syncAsSint16LE(scratch._a4);
+	s.syncAsSint16LE(scratch._a6);
+	s.syncAsSint16LE(scratch._a8);
+	s.syncAsSint16LE(scratch._aa);
+	s.syncAsSint16LE(scratch._ac);
+	s.syncAsSint16LE(scratch._ae);
+	s.syncAsSint16LE(scratch._b0);
+	s.syncAsSint16LE(scratch._b2);
+	s.syncAsSint16LE(scratch._b4);
+	s.syncAsSint16LE(scratch._b6);
 }
 
 void room_322_preload() {


Commit: 5535579583457b410b391a9d29f1b13e8022d082
    https://github.com/scummvm/scummvm/commit/5535579583457b410b391a9d29f1b13e8022d082
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-06-14T09:14:53+10:00

Commit Message:
MADS: FOREST: Implemented room 401

Changed paths:
    engines/mads/madsv2/forest/rooms/room401.cpp


diff --git a/engines/mads/madsv2/forest/rooms/room401.cpp b/engines/mads/madsv2/forest/rooms/room401.cpp
index 63aa362b881..433b39a8ff7 100644
--- a/engines/mads/madsv2/forest/rooms/room401.cpp
+++ b/engines/mads/madsv2/forest/rooms/room401.cpp
@@ -24,8 +24,13 @@
 #include "mads/madsv2/core/imath.h"
 #include "mads/madsv2/core/inter.h"
 #include "mads/madsv2/core/kernel.h"
+#include "mads/madsv2/core/digi.h"
+#include "mads/madsv2/core/sprite.h"
+#include "mads/madsv2/core/midi.h"
+#include "mads/madsv2/core/player.h"
 #include "mads/madsv2/core/sound.h"
 #include "mads/madsv2/core/text.h"
+#include "mads/madsv2/forest/journal.h"
 #include "mads/madsv2/forest/mads/inventory.h"
 #include "mads/madsv2/forest/mads/sounds.h"
 #include "mads/madsv2/forest/mads/words.h"
@@ -39,10 +44,22 @@ namespace Forest {
 namespace Rooms {
 
 struct Scratch {
-	int16 sprite[10];       /* Sprite series handles */
-	int16 sequence[10];     /* Sequence handles      */
-	int16 animation[10];     /* Animation handles     */
-	AnimationInfo animation_info[10];
+	int16 sprite[10];                  // 0x00
+	int16 sequence[10];                // 0x14
+	int16 animation[10];               // 0x28
+	AnimationInfo animation_info[10];  // 0x3C (80 bytes, ends at 0x8B)
+	int16 _8c;                         // 0x8C
+	int16 _8e;                         // 0x8E
+	int16 _90;                         // 0x90
+	int16 _92;                         // 0x92 ??? hotspot ID for incoming transition
+	int16 _94;                         // 0x94
+	int16 _96;                         // 0x96
+	int16 _98;                         // 0x98 ??? room state / variant index
+	int16 _9a;                         // 0x9A ??? background anim handle (e-series)
+	int16 _9c;                         // 0x9C ??? background anim handle (r-series)
+	int16 _9e;                         // 0x9E
+	int16 _a0;                         // 0xA0
+	int16 _a2;                         // 0xA2
 };
 
 static Scratch scratch;
@@ -53,23 +70,593 @@ static Scratch scratch;
 #define aa    local->animation
 #define aainfo scratch.animation_info
 
+static void room_401_anim_state(int16 state) {
+	switch (state) {
+	case 15:
+		aa[1] = kernel_run_animation(kernel_name('y', 3), 103);
+		aainfo[1]._active = -1;
+		aainfo[1]._frame = 0;
+		scratch._98 = 19;
+		kernel_timing_trigger(1, 106);
+		break;
+	case 16:
+		aa[1] = kernel_run_animation(kernel_name('z', 2), 103);
+		aainfo[1]._active = -1;
+		aainfo[1]._frame = 0;
+		scratch._98 = 21;
+		kernel_timing_trigger(1, 106);
+		break;
+	case 17:
+		aa[1] = kernel_run_animation(kernel_name('t', 2), 103);
+		aainfo[1]._active = -1;
+		aainfo[1]._frame = 0;
+		scratch._98 = 18;
+		kernel_timing_trigger(1, 106);
+		break;
+	case 18:
+	case 19:
+	case 25:
+		global[g131] = -1;
+		global[g141] = -1;
+		kernel_reset_animation(scratch._9a, 2);
+		kernel_reset_animation(scratch._9c, 2);
+		global[g133] = 0;
+		global[g143] = 0;
+		kernel_synch(KERNEL_ANIM, scratch._9a, KERNEL_NOW, 0);
+		kernel_synch(KERNEL_ANIM, scratch._9c, KERNEL_NOW, 0);
+		kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
+		player.commands_allowed = -1;
+		player.walker_visible = -1;
+		if (digi_val1) {
+			digi_stop(1);
+			kernel_timing_trigger(1, 106);
+		}
+		break;
+	case 21:
+		new_room = 308;
+		break;
+	case 22:
+		if (flags[26] == 1) {
+			aa[1] = kernel_run_animation(kernel_name('t', 1), 103);
+			scratch._98 = 17;
+		} else {
+			aa[1] = kernel_run_animation(kernel_name('y', 2), 103);
+			scratch._98 = 15;
+		}
+		aainfo[1]._active = -1;
+		aainfo[1]._frame = 0;
+		kernel_timing_trigger(1, 106);
+		break;
+	case 23:
+		aa[1] = kernel_run_animation(kernel_name('n', 2), 103);
+		aainfo[1]._active = -1;
+		aainfo[1]._frame = 0;
+		scratch._98 = 24;
+		kernel_timing_trigger(1, 106);
+		break;
+	case 24:
+		aa[1] = kernel_run_animation(kernel_name('n', 3), 103);
+		aainfo[1]._active = -1;
+		aainfo[1]._frame = 0;
+		scratch._98 = 25;
+		kernel_timing_trigger(1, 106);
+		break;
+	default:
+		break;
+	}
+}
+
+static void room_401_anim1() {
+	int16 cur = kernel_anim[aa[0]].frame;
+	if (cur != aainfo[0]._frame)
+		aainfo[0]._frame = cur;
+
+	if (global[player_hyperwalked] != -1)
+		return;
+
+	aainfo[0]._frame = scratch._92 - 1;
+	kernel_reset_animation(aa[0], aainfo[0]._frame);
+}
+
+static void room_401_anim2() {
+	int16 result = -1;
+
+	if (global[player_hyperwalked] == -1) {
+		int16 st = scratch._98;
+		if (st == 15 || st == 17) {
+			if (aainfo[1]._frame < 49)
+				result = 49;
+		} else if (st == 16) {
+			if (aainfo[1]._frame < 32)
+				result = 32;
+		} else if (st == 21) {
+			if (aainfo[1]._frame < 116)
+				result = 116;
+		}
+		if (result >= 0) {
+			aainfo[1]._frame = result;
+			kernel_reset_animation(aa[1], result);
+			result = -1;
+		}
+	}
+
+	int16 cur = kernel_anim[aa[1]].frame;
+	if (cur != aainfo[1]._frame) {
+		aainfo[1]._frame = cur;
+
+		switch (scratch._98) {
+		case 15:
+		case 17:
+			if (aainfo[1]._frame == 42) {
+				digi_play_build(307, '_', 2, 1);
+				scratch._a0 = -1;
+			}
+			break;
+		case 16:
+			if (aainfo[1]._frame == 23) {
+				digi_play_build(307, '_', 3, 1);
+				scratch._a0 = -1;
+			}
+			break;
+		case 18: {
+			int16 frame = aainfo[1]._frame;
+			if (frame == 96) {
+				digi_play_build(301, '_', 1, 1);
+				scratch._a0 = -1;
+			} else if (frame < 96) {
+				if (frame == 25) {
+					result = 22;
+				} else if (frame < 25) {
+					if (frame == 15) {
+						if (!global[g066])
+							digi_play_build(401, 'r', 1, 1);
+						else
+							digi_play_build(401, 'r', 2, 1);
+						scratch._a0 = 60;
+					} else if (frame == 18) {
+						result = 15;
+					} else if (frame == 20) {
+						result = 19;
+					} else if (frame == 22) {
+						digi_play_build(401, 'b', 1, 1);
+						scratch._a0 = 62;
+					}
+				} else {
+					if (frame == 27) {
+						result = 26;
+					} else if (frame == 29) {
+						digi_play_build(401, 'r', 3, 1);
+						scratch._a0 = 64;
+					} else if (frame == 32) {
+						result = 29;
+					} else if (frame == 35) {
+						if (digi_val1)
+							kernel_timing_trigger(1, 106);
+					}
+				}
+			}
+			break;
+		}
+		case 23:
+			if (aainfo[1]._frame == 1)
+				result = 0;
+			break;
+		case 24:
+			if (aainfo[1]._frame == 1) {
+				digi_play_build(220, '_', 4, 1);
+				scratch._a0 = 70;
+			} else if (aainfo[1]._frame == 16) {
+				result = 1;
+			}
+			break;
+		default:
+			break;
+		}
+
+		if (result >= 0) {
+			aainfo[1]._frame = result;
+			kernel_reset_animation(aa[1], result);
+		}
+	}
+
+	if (kernel.trigger != 7 && kernel.trigger != 28)
+		return;
+
+	result = -1;
+	int16 a0 = scratch._a0 - 60;
+	if (a0 >= 0 && a0 <= 10) {
+		switch (a0) {
+		case 0:
+			kernel_timing_trigger(30, 28);
+			result = 19;
+			scratch._a0 = 61;
+			break;
+		case 1:
+			result = 21;
+			scratch._a0 = 62;
+			break;
+		case 2:
+			kernel_timing_trigger(30, 28);
+			result = 26;
+			scratch._a0 = 63;
+			break;
+		case 3:
+			result = 28;
+			scratch._a0 = 64;
+			break;
+		case 4:
+			result = 33;
+			scratch._a0 = -1;
+			break;
+		case 10:
+			result = 16;
+			scratch._a0 = -1;
+			break;
+		default:
+			break;
+		}
+	}
+
+	if (result >= 0) {
+		aainfo[1]._frame = result;
+		kernel_reset_animation(aa[1], result);
+	}
+}
+
+static void room_401_anim3() {
+	int16 cur = kernel_anim[aa[3]].frame;
+	if (cur != aainfo[3]._frame)
+		aainfo[3]._frame = cur;
+}
+
+static void room_401_anim4() {
+	int16 result = -1;
+
+	int16 cur = kernel_anim[aa[2]].frame;
+	if (cur != aainfo[2]._frame) {
+		aainfo[2]._frame = cur;
+
+		if (scratch._a2 == 1) {
+			if (cur == 61) {
+				if (aainfo[2]._val3 == 13)
+					result = 59;
+			} else if (cur < 61) {
+				if (cur == 53) {
+					aainfo[2]._val3 = 11;
+					digi_play_build_ii('e', 8, 1);
+					scratch._a0 = 53;
+				} else if (cur == 58) {
+					if (aainfo[2]._val3 == 11)
+						result = 53;
+				}
+			}
+		}
+
+		if (result >= 0) {
+			aainfo[2]._frame = result;
+			kernel_reset_animation(aa[2], result);
+			result = -1;
+		}
+	}
+
+	if (kernel.trigger != 7 && kernel.trigger != 28)
+		return;
+
+	result = -1;
+	int16 a0 = scratch._a0 - 53;
+	if (a0 == 0) {
+		kernel_timing_trigger(30, 28);
+		aainfo[2]._val3 = 13;
+		scratch._a0 = 54;
+	} else if (a0 == 1) {
+		aainfo[2]._val3 = 11;
+		result = 53;
+		digi_play_build_ii('e', 10, 1);
+		scratch._a0 = 55;
+	} else if (a0 == 2) {
+		aainfo[2]._val3 = 14;
+		result = 59;
+		scratch._a0 = -1;
+	}
+
+	if (result >= 0) {
+		aainfo[2]._frame = result;
+		kernel_reset_animation(aa[2], result);
+	}
+}
+
+static void room_401_init1() {
+	global[g131] = 0;
+	global[g141] = 0;
+
+	scratch._9c = kernel_run_animation_disp('r', 3, 0);
+	kernel_position_anim(scratch._9c, 228, 132, 82, 4);
+	scratch._9a = kernel_run_animation_disp('e', 2, 0);
+	kernel_position_anim(scratch._9a, 252, 115, 76, 7);
+
+	if (previous_room != KERNEL_RESTORING_GAME && previous_room != 199) {
+		player_demand_facing(7);
+		player_demand_location(300, 138);
+	}
+
+	if (previous_room == 199) {
+		global[g131] = -1;
+		global[g141] = -1;
+		kernel_reset_animation(scratch._9a, 2);
+		kernel_reset_animation(scratch._9c, 2);
+		global[g133] = 0;
+		global[g143] = 0;
+		restore_player();
+		player.commands_allowed = -1;
+		player.walker_visible = -1;
+		return;
+	}
+
+	if (previous_room == 308) {
+		aa[0] = kernel_run_animation(kernel_name('y', 1), 103);
+		aainfo[0]._active = -1;
+		scratch._92 = 49;
+		scratch._98 = 22;
+		return;
+	}
+
+	if (previous_room == 402) {
+		aa[0] = kernel_run_animation(kernel_name('y', 4), 100);
+		aainfo[0]._active = -1;
+		scratch._92 = 100;
+		return;
+	}
+
+	if (previous_room == 404) {
+		aa[0] = kernel_run_animation(kernel_name('y', 5), 100);
+		aainfo[0]._active = -1;
+		scratch._92 = 85;
+		return;
+	}
+
+	if (previous_room == 420) {
+		aa[1] = kernel_run_animation(kernel_name('n', 1), 103);
+		aainfo[1]._active = -1;
+		aainfo[1]._frame = 0;
+		scratch._98 = 23;
+		kernel_timing_trigger(120, 105);
+		return;
+	}
+
+	global[g131] = -1;
+	global[g141] = -1;
+	kernel_reset_animation(scratch._9a, 2);
+	kernel_reset_animation(scratch._9c, 2);
+	global[g133] = 0;
+	global[g143] = 0;
+	player.commands_allowed = -1;
+	player.walker_visible = -1;
+}
 
 static void room_401_init() {
+	scratch._a2 = -1;
+	midi_stop();
+
+	if (!flags[31] || !global[g066])
+		kernel_flip_hotspot(49, 0);
+
+	kernel_timing_trigger(1, 106);
+	global[player_score] = 0;
+	scratch._98 = 0;
+
+	if (previous_room != KERNEL_RESTORING_GAME) {
+		if (previous_room != 199) {
+			player.walker_visible = 0;
+			player.commands_allowed = 0;
+		}
+
+		for (int16 count = 0; count < 10; count++) {
+			aainfo[count]._active = 0;
+			aainfo[count]._frame = -1;
+			aainfo[count]._val3 = 0;
+			aainfo[count]._val4 = 0;
+		}
+
+		if (previous_room != 199 && flags[26] != 3)
+			flags[26]++;
+	}
+
+	room_401_init1();
 }
 
 static void room_401_daemon() {
+	switch (kernel.trigger) {
+	case 7:
+		if (scratch._a0 != 668)
+			break;
+		global[walker_converse_state] = 0;
+		close_journal(3);
+		player.commands_allowed = -1;
+		if (digi_val1)
+			kernel_timing_trigger(1, 106);
+		break;
+
+	case 24:
+		if (scratch._8c != 20)
+			break;
+		if (scratch._a2 != 1)
+			break;
+		aa[2] = kernel_run_animation(kernel_name('F', 1), 102);
+		aainfo[2]._active = -1;
+		scratch._96 = 2;
+		kernel_reset_animation(scratch._9a, 0);
+		kernel_synch(KERNEL_ANIM, scratch._9a, KERNEL_ANIM, aa[2]);
+		global[g133] = 1;
+		kernel_timing_trigger(1, 106);
+		break;
+
+	case 25:
+		if (scratch._8e != 2)
+			break;
+		aa[3] = kernel_run_animation(kernel_name('R', 1), 101);
+		aainfo[3]._active = -1;
+		scratch._94 = 3;
+		kernel_reset_animation(scratch._9c, 0);
+		kernel_synch(KERNEL_ANIM, aa[3], KERNEL_ANIM, scratch._9c);
+		global[g143] = 1;
+		kernel_timing_trigger(1, 106);
+		break;
+
+	case 26:
+		kernel_reset_animation(scratch._9a, 0);
+		kernel_reset_animation(scratch._9c, 0);
+		player.commands_allowed = 0;
+		player.walker_visible = 0;
+		kernel_synch(KERNEL_ANIM, scratch._9a, KERNEL_ANIM, aa[1]);
+		kernel_synch(KERNEL_ANIM, scratch._9c, KERNEL_ANIM, aa[1]);
+		kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
+		global[g133] = 1;
+		global[g143] = 1;
+		if (scratch._90 == 16) {
+			aa[1] = kernel_run_animation(kernel_name('z', 1), 103);
+			aainfo[1]._active = -1;
+			aainfo[1]._frame = 0;
+			scratch._98 = 16;
+			kernel_timing_trigger(1, 106);
+		}
+		break;
+
+	case 100:
+		dont_frag_the_palette();
+		kernel_abort_animation(aa[0]);
+		aainfo[0]._active = 0;
+		global[g131] = -1;
+		global[g141] = -1;
+		kernel_reset_animation(scratch._9a, 1);
+		kernel_synch(KERNEL_ANIM, scratch._9a, KERNEL_NOW, 0);
+		kernel_reset_animation(scratch._9c, 1);
+		kernel_synch(KERNEL_ANIM, scratch._9c, KERNEL_NOW, 0);
+		player.walker_visible = -1;
+		global[g133] = 0;
+		global[g143] = 0;
+		kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
+		player.commands_allowed = -1;
+		break;
+
+	case 101:
+		dont_frag_the_palette();
+		kernel_abort_animation(aa[scratch._94]);
+		aainfo[scratch._94]._active = 0;
+		kernel_reset_animation(scratch._9c, 1);
+		kernel_synch(KERNEL_ANIM, scratch._9c, KERNEL_NOW, 0);
+		global[g143] = 0;
+		player.commands_allowed = -1;
+		kernel_timing_trigger(1, 106);
+		break;
+
+	case 102:
+		dont_frag_the_palette();
+		kernel_abort_animation(aa[scratch._96]);
+		aainfo[scratch._96]._active = 0;
+		kernel_reset_animation(scratch._9a, 1);
+		kernel_synch(KERNEL_ANIM, scratch._9a, KERNEL_NOW, 0);
+		global[g133] = 0;
+		player.commands_allowed = -1;
+		kernel_timing_trigger(1, 106);
+		break;
+
+	case 103:
+		if (scratch._98 == 22) {
+			dont_frag_the_palette();
+			kernel_abort_animation(aa[0]);
+			aainfo[0]._active = 0;
+		} else {
+			dont_frag_the_palette();
+			kernel_abort_animation(aa[1]);
+			aainfo[1]._active = 0;
+		}
+		room_401_anim_state(scratch._98);
+		break;
+
+	case 105:
+		aainfo[1]._frame = 1;
+		kernel_reset_animation(aa[1], 1);
+		break;
+
+	case 106:
+		digi_initial_volume(40);
+		digi_play_build(401, '_', 1, 3);
+		digi_val2 = -1;
+		break;
+
+	default:
+		break;
+	}
+
+	global_anim1(2, scratch._9a, global[g131], &global[g132]);
+	global_anim2(3, scratch._9c, global[g141], &global[g142]);
+
+	if (aainfo[0]._active)
+		room_401_anim1();
+	if (aainfo[1]._active)
+		room_401_anim2();
+	if (aainfo[3]._active)
+		room_401_anim3();
+	if (aainfo[2]._active)
+		room_401_anim4();
 }
 
 static void room_401_pre_parser() {
+	if (player_parse(13, 47, 0))
+		player.walk_off_edge_to_room = 402;
+
+	if (player_parse(13, 49, 0))
+		player.walk_off_edge_to_room = 404;
 }
 
 static void room_401_parser() {
+	if (global[walker_converse_state]) {
+		player.commands_allowed = 0;
+		digi_play_build_ii('c', 1, 1);
+		scratch._a0 = 668;
+		player.command_ready = 0;
+		return;
+	}
+
+	if (player_parse(13, 41, 0)) {
+		global[g150] = -1;
+		scratch._90 = 16;
+		player.commands_allowed = 0;
+		player.command_ready = 0;
+		return;
+	}
+
+	if (global[player_selected_object] >= 0) {
+		player.command_ready = 0;
+		return;
+	}
+
+	if (player_parse(114, 150, 0)) {
+		player.commands_allowed = 0;
+		global[g135] = -1;
+		scratch._8c = 20;
+		scratch._a2 = 1;
+		player.command_ready = 0;
+	}
 }
 
 void room_401_synchronize(Common::Serializer &s) {
 	for (int16 &v : scratch.sprite)    s.syncAsSint16LE(v);
 	for (int16 &v : scratch.sequence)  s.syncAsSint16LE(v);
 	for (int16 &v : scratch.animation) s.syncAsSint16LE(v);
+	for (AnimationInfo &ai : scratch.animation_info) ai.synchronize(s);
+	s.syncAsSint16LE(scratch._8c);
+	s.syncAsSint16LE(scratch._8e);
+	s.syncAsSint16LE(scratch._90);
+	s.syncAsSint16LE(scratch._92);
+	s.syncAsSint16LE(scratch._94);
+	s.syncAsSint16LE(scratch._96);
+	s.syncAsSint16LE(scratch._98);
+	s.syncAsSint16LE(scratch._9a);
+	s.syncAsSint16LE(scratch._9c);
+	s.syncAsSint16LE(scratch._9e);
+	s.syncAsSint16LE(scratch._a0);
+	s.syncAsSint16LE(scratch._a2);
 }
 
 void room_401_preload() {



From noreply at scummvm.org  Sun Jun 14 02:51:44 2026
From: noreply at scummvm.org (dreammaster)
Date: Sun, 14 Jun 2026 02:51:44 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 333107760b0817adb9e247520035cede86e18e40
Message-ID: <20260614025144.932587005CC@ron.scummvm.net>

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

Summary:
333107760b MADS: FOREST: Implemented room 402


Commit: 333107760b0817adb9e247520035cede86e18e40
    https://github.com/scummvm/scummvm/commit/333107760b0817adb9e247520035cede86e18e40
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-06-14T12:51:32+10:00

Commit Message:
MADS: FOREST: Implemented room 402

Changed paths:
    engines/mads/madsv2/forest/rooms/room402.cpp


diff --git a/engines/mads/madsv2/forest/rooms/room402.cpp b/engines/mads/madsv2/forest/rooms/room402.cpp
index 4956ac04328..f8fec0ca4fa 100644
--- a/engines/mads/madsv2/forest/rooms/room402.cpp
+++ b/engines/mads/madsv2/forest/rooms/room402.cpp
@@ -20,10 +20,15 @@
  */
 
 #include "mads/madsv2/core/conv.h"
+#include "mads/madsv2/core/digi.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/imath.h"
 #include "mads/madsv2/core/inter.h"
 #include "mads/madsv2/core/kernel.h"
+#include "mads/madsv2/core/midi.h"
+#include "mads/madsv2/core/player.h"
+#include "mads/madsv2/core/sprite.h"
+#include "mads/madsv2/forest/journal.h"
 #include "mads/madsv2/core/sound.h"
 #include "mads/madsv2/core/text.h"
 #include "mads/madsv2/forest/mads/inventory.h"
@@ -39,10 +44,22 @@ namespace Forest {
 namespace Rooms {
 
 struct Scratch {
-	int16 sprite[10];       /* Sprite series handles */
-	int16 sequence[10];     /* Sequence handles      */
-	int16 animation[10];     /* Animation handles     */
+	int16 sprite[10];
+	int16 sequence[10];
+	int16 animation[10];
 	AnimationInfo animation_info[10];
+	int16 _8c;
+	int16 _8e;
+	int16 _90;
+	int16 _92;
+	int16 _94;
+	int16 _96;
+	int16 _98;
+	int16 _9a;
+	int16 _9c;
+	int16 _9e;
+	int16 _a0;
+	int16 _a2;
 };
 
 static Scratch scratch;
@@ -54,22 +71,787 @@ static Scratch scratch;
 #define aainfo scratch.animation_info
 
 
+static void room_402_anim6() {
+	scratch._9e = -1;
+	room_spots[3].cursor_number = 8;
+	room_spots[4].cursor_number = 8;
+	room_spots[7].cursor_number = 8;
+	room_spots[8].cursor_number = 8;
+	room_spots[9].cursor_number = 8;
+	room_spots[10].cursor_number = 8;
+	room_spots[11].cursor_number = 8;
+}
+
+static void room_402_init2() {
+	room_402_anim6();
+}
+
+static void room_402_init1() {
+	global[g131] = 0;
+	global[g141] = 0;
+
+	scratch._9c = kernel_run_animation_disp('r', 1, 0);
+	kernel_position_anim(scratch._9c, 71, 128, 63, 5);
+	scratch._9a = kernel_run_animation_disp('e', 2, 0);
+	kernel_position_anim(scratch._9a, 112, 131, 65, 5);
+
+	if (previous_room != KERNEL_RESTORING_GAME && previous_room != 199) {
+		player_demand_facing(3);
+		player_demand_location(136, 142);
+	}
+
+	if (previous_room == 199) {
+		restore_player();
+	} else if (previous_room == 401) {
+		if (global[g083] != 3) {
+			aa[0] = kernel_run_animation(kernel_name('t', 1), 103);
+			aainfo[0]._active = -1;
+			scratch._92 = 115;
+			scratch._98 = 1;
+			return;
+		} else {
+			aa[0] = kernel_run_animation(kernel_name('y', 1), 100);
+			aainfo[0]._active = -1;
+			scratch._92 = 131;
+			return;
+		}
+	} else if (previous_room == 403) {
+		aa[0] = kernel_run_animation(kernel_name('y', 2), 100);
+		aainfo[0]._active = -1;
+		scratch._92 = 58;
+		return;
+	}
+
+	switch (global[g083]) {
+	case 0:
+		aa[1] = kernel_run_animation(kernel_name('t', 4), 103);
+		aainfo[1]._active = -1;
+		aainfo[1]._frame = 0;
+		scratch._98 = 20;
+		room_402_init2();
+		global[player_score] = 0;
+		return;
+	case 1:
+		aa[1] = kernel_run_animation(kernel_name('L', 2), 103);
+		aainfo[1]._active = -1;
+		aainfo[1]._frame = 0;
+		scratch._98 = 6;
+		break;
+	case 2:
+		kernel_flip_hotspot(46, 0);
+		aa[1] = kernel_run_animation(kernel_name('N', 1), 103);
+		aainfo[1]._active = -1;
+		aainfo[1]._frame = 0;
+		scratch._98 = 15;
+		kernel_flip_hotspot(187, -1);
+		player.commands_allowed = -1;
+		break;
+	case 3:
+		kernel_flip_hotspot(48, -1);
+		aa[1] = kernel_run_animation(kernel_name('L', 14), 103);
+		aainfo[1]._active = -1;
+		aainfo[1]._frame = 0;
+		scratch._98 = 19;
+		global[g131] = -1;
+		global[g141] = -1;
+		kernel_reset_animation(scratch._9a, 1);
+		kernel_synch(KERNEL_ANIM, scratch._9a, KERNEL_NOW, 0);
+		kernel_reset_animation(scratch._9c, 1);
+		kernel_synch(KERNEL_ANIM, scratch._9c, KERNEL_NOW, 0);
+		player.walker_visible = -1;
+		global[g133] = 0;
+		global[g143] = 0;
+		kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
+		player.commands_allowed = -1;
+		return;
+	default:
+		return;
+	}
+
+	global[player_score] = 0;
+	global_digi_play(15);
+}
+
 static void room_402_init() {
+	scratch._90 = 0;
+	scratch._a0 = 0;
+	global[player_score] = -1;
+
+	if (previous_room == 403) {
+		global[g009] = 0;
+	} else {
+		midi_stop();
+		global[g009] = -1;
+	}
+
+	player.walker_visible = 0;
+	player.commands_allowed = 0;
+
+	for (int16 count = 0; count < 10; count++) {
+		aainfo[count]._active = 0;
+		aainfo[count]._frame = 1;
+		aainfo[count]._val3 = 0;
+		aainfo[count]._val4 = 0;
+	}
+
+	if (previous_room != KERNEL_RESTORING_GAME && previous_room != 199 && flags[27] != 3)
+		flags[27]++;
+
+	kernel_flip_hotspot(187, 0);
+	kernel_flip_hotspot(48, 0);
+
+	room_402_init1();
+}
+
+static void room_402_anim5() {
+}
+
+static void room_402_anim_case7() {
+	int16 result = -1;
+	int16 a2 = scratch._a2;
+
+	if (a2 == -668) {
+		global[walker_converse_state] = 0;
+		close_journal(3);
+		player.commands_allowed = -1;
+	} else if (a2 == 1) {
+		result = 14;
+		kernel_timing_trigger(30, 7);
+		scratch._a2 = 2;
+	} else if (a2 == 2) {
+		result = 16;
+	} else if (a2 == 3) {
+		result = 23;
+		kernel_timing_trigger(30, 7);
+		scratch._a2 = 4;
+	} else if (a2 == 5) {
+		result = 39;
+		kernel_timing_trigger(30, 7);
+		scratch._a2 = 6;
+	} else if (a2 == 6) {
+		result = 42;
+	} else if (a2 == 7) {
+		result = 49;
+	} else if (a2 == 8) {
+		result = 9;
+	} else if (a2 == 10) {
+		result = 11;
+	} else if (a2 == 11) {
+		result = 36;
+	} else if (a2 == 12) {
+		result = 19;
+	} else if (a2 == 13) {
+		result = 31;
+	} else if (a2 == 14) {
+		result = 38;
+	} else if (a2 == 16) {
+		result = 37;
+	}
+
+	if (result >= 0) {
+		aainfo[1]._frame = result;
+		kernel_reset_animation(aa[1], result);
+	}
+}
+
+static void room_402_anim_state() {
+	switch (scratch._98) {
+	case 1:
+		aa[1] = kernel_run_animation(kernel_name('t', 2), 103);
+		aainfo[1]._active = -1;
+		aainfo[1]._frame = 0;
+		scratch._98 = 2;
+		break;
+	case 2:
+		aa[1] = kernel_run_animation(kernel_name('t', 3), 103);
+		aainfo[1]._active = -1;
+		aainfo[1]._frame = 0;
+		scratch._98 = 3;
+		break;
+	case 3:
+		aa[1] = kernel_run_animation(kernel_name('t', 4), 103);
+		aainfo[1]._active = -1;
+		aainfo[1]._frame = 0;
+		scratch._98 = 20;
+		break;
+	case 4:
+		new_room = 401;
+		break;
+	case 5:
+		global[g083] = 1;
+		global[g070] = -1;
+		display_journal();
+		break;
+	case 6:
+		aa[1] = kernel_run_animation(kernel_name('L', 22), 103);
+		aainfo[1]._active = -1;
+		aainfo[1]._frame = 0;
+		scratch._98 = 7;
+		break;
+	case 7:
+		aa[1] = kernel_run_animation(kernel_name('L', 23), 103);
+		aainfo[1]._active = -1;
+		aainfo[1]._frame = 0;
+		scratch._98 = 8;
+		break;
+	case 8:
+		aa[1] = kernel_run_animation(kernel_name('L', 5), 103);
+		aainfo[1]._active = -1;
+		aainfo[1]._frame = 0;
+		scratch._98 = 9;
+		break;
+	case 9:
+		aa[1] = kernel_run_animation(kernel_name('L', 6), 103);
+		aainfo[1]._active = -1;
+		aainfo[1]._frame = 0;
+		scratch._98 = 10;
+		break;
+	case 10:
+		global[g083] = 2;
+		aa[1] = kernel_run_animation(kernel_name('L', 7), 103);
+		aainfo[1]._active = -1;
+		aainfo[1]._frame = 0;
+		scratch._98 = 11;
+		break;
+	case 11:
+		aa[1] = kernel_run_animation(kernel_name('L', 8), 103);
+		aainfo[1]._active = -1;
+		aainfo[1]._frame = 0;
+		scratch._98 = 12;
+		break;
+	case 12:
+		aa[1] = kernel_run_animation(kernel_name('L', 9), 103);
+		aainfo[1]._active = -1;
+		aainfo[1]._frame = 0;
+		scratch._98 = 13;
+		break;
+	case 13:
+		aa[1] = kernel_run_animation(kernel_name('L', 10), 103);
+		aainfo[1]._active = -1;
+		aainfo[1]._frame = 0;
+		scratch._98 = 14;
+		break;
+	case 14:
+		aa[1] = kernel_run_animation(kernel_name('N', 1), 103);
+		aainfo[1]._active = -1;
+		aainfo[1]._frame = 0;
+		scratch._98 = 15;
+		room_402_anim5();
+		global[walker_converse_state] = 0;
+		close_journal(3);
+		kernel_flip_hotspot(187, -1);
+		kernel_flip_hotspot(46, 0);
+		player.commands_allowed = -1;
+		break;
+	case 15:
+		aa[1] = kernel_run_animation(kernel_name('L', 11), 103);
+		aainfo[1]._active = -1;
+		aainfo[1]._frame = 0;
+		scratch._98 = 16;
+		break;
+	case 16:
+		aa[1] = kernel_run_animation(kernel_name('L', 12), 103);
+		aainfo[1]._active = -1;
+		aainfo[1]._frame = 0;
+		scratch._98 = 17;
+		kernel_flip_hotspot(187, 0);
+		break;
+	case 17:
+		global_digi_play(5);
+		global[g009] = 0;
+		aa[1] = kernel_run_animation(kernel_name('L', 13), 103);
+		aainfo[1]._active = -1;
+		aainfo[1]._frame = 0;
+		scratch._98 = 18;
+		break;
+	case 18:
+		aa[1] = kernel_run_animation(kernel_name('L', 14), 103);
+		aainfo[1]._active = -1;
+		aainfo[1]._frame = 0;
+		scratch._98 = 19;
+		global[g083] = 3;
+		kernel_flip_hotspot(48, -1);
+		kernel_flip_hotspot(46, -1);
+		kernel_position_anim(scratch._9c, 71, 128, 63, 5);
+		kernel_position_anim(scratch._9a, 112, 131, 65, 5);
+		global[g131] = -1;
+		global[g141] = -1;
+		kernel_reset_animation(scratch._9a, 1);
+		kernel_synch(KERNEL_ANIM, scratch._9a, KERNEL_NOW, 0);
+		kernel_reset_animation(scratch._9c, 1);
+		kernel_synch(KERNEL_ANIM, scratch._9c, KERNEL_NOW, 0);
+		global[g133] = 0;
+		global[g143] = 0;
+		player_demand_facing(3);
+		player_demand_location(136, 142);
+		player.walker_visible = -1;
+		kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
+		player.commands_allowed = -1;
+		global[walker_converse_state] = 0;
+		close_journal(3);
+		global[player_score] = -1;
+		break;
+	case 20:
+		aa[1] = kernel_run_animation(kernel_name('L', 1), 103);
+		aainfo[1]._active = -1;
+		aainfo[1]._frame = 0;
+		scratch._98 = 5;
+		break;
+	default:
+		break;
+	}
+}
+
+static void room_402_anim1() {
+	int16 cur = kernel_anim[aa[0]].frame;
+	if (cur != aainfo[0]._frame)
+		aainfo[0]._frame = cur;
+
+	if (global[player_hyperwalked] != -1)
+		return;
+
+	aainfo[0]._frame = scratch._92 - 1;
+	kernel_reset_animation(aa[0], aainfo[0]._frame);
+}
+
+static void room_402_anim2() {
+	int16 result = -1;
+
+	int16 cur = kernel_anim[aa[1]].frame;
+	if (cur == aainfo[1]._frame)
+		return;
+	aainfo[1]._frame = cur;
+
+	switch (scratch._98) {
+	case 3: {
+		int16 frame = aainfo[1]._frame;
+		if (frame == 49) {
+			result = 43;
+		} else if (frame < 49) {
+			if (frame == 23) {
+				result = 18;
+			} else if (frame < 23) {
+				if (frame == 5) {
+					if (flags[27] != 1)
+						result = 23;
+				} else if (frame == 9) {
+					digi_play_build(402, 'b', 1, 1);
+					scratch._a2 = 1;
+				} else if (frame == 14) {
+					result = 9;
+				} else if (frame == 16) {
+					result = 15;
+				} else if (frame == 18) {
+					digi_play_build(402, 'x', 1, 1);
+					scratch._a2 = 3;
+				}
+			} else {
+				if (frame == 31) {
+					if (flags[27] != 1)
+						result = 51;
+				} else if (frame == 33) {
+					digi_play_build(402, 'e', 1, 1);
+					scratch._a2 = 5;
+				} else if (frame == 39) {
+					result = 33;
+				} else if (frame == 42) {
+					result = 41;
+				} else if (frame == 43) {
+					digi_play_build(402, 'b', 2, 1);
+					scratch._a2 = 7;
+				}
+			}
+		}
+		break;
+	}
+
+	case 5: {
+		int16 frame = aainfo[1]._frame;
+		if (frame < 22) {
+			if (frame == 4) {
+				digi_play_build(402, 'e', 2, 1);
+				scratch._a2 = 8;
+			} else if (frame == 9) {
+				result = 4;
+			}
+		}
+		break;
+	}
+
+	case 8: {
+		int16 frame = aainfo[1]._frame;
+		if (frame == 36) {
+			result = 31;
+		} else if (frame < 36) {
+			if (frame == 5) {
+				digi_play_build(402, 'b', 3, 1);
+				scratch._a2 = 10;
+			} else if (frame == 11) {
+				result = 5;
+			} else if (frame == 31) {
+				digi_play_build(402, 'b', 4, 1);
+				scratch._a2 = 11;
+			}
+		}
+		break;
+	}
+
+	case 10: {
+		int16 frame = aainfo[1]._frame;
+		if (frame == 10) {
+			digi_play_build(402, '_', 1, 2);
+		} else if (frame == 14) {
+			digi_play_build(402, '_', 2, 2);
+		}
+		break;
+	}
+
+	case 14: {
+		int16 frame = aainfo[1]._frame;
+		if (frame == 16) {
+			digi_play_build(402, 'x', 2, 1);
+			scratch._a2 = 12;
+		} else if (frame == 18) {
+			result = 16;
+		}
+		break;
+	}
+
+	case 15: {
+		int16 frame = aainfo[1]._frame;
+		if (frame == 22 || frame == 51) {
+			if (scratch._90 == 17) {
+				if (flags[27] != 1)
+					result = 51;
+			} else {
+				result = (imath_random(0, 100) > 50) ? 0 : 23;
+			}
+		}
+		break;
+	}
+
+	case 16: {
+		int16 frame = aainfo[1]._frame;
+		if (frame == 38) {
+			result = 35;
+		} else if (frame < 38) {
+			if (frame == 25) {
+				digi_play_build(402, 'x', 3, 1);
+				scratch._a2 = 13;
+			} else if (frame == 31) {
+				result = 25;
+			} else if (frame == 35) {
+				digi_play_build(402, 'e', 4, 1);
+				scratch._a2 = 14;
+			}
+		}
+		break;
+	}
+
+	case 17: {
+		int16 frame = aainfo[1]._frame;
+		if (frame == 37) {
+			result = 36;
+		} else if (frame < 37) {
+			if (frame == 1) {
+				digi_play_build(402, 'x', 4, 1);
+				scratch._a2 = 15;
+			} else if (frame == 4) {
+				digi_play_build(101, '_', 2, 2);
+			} else if (frame == 10) {
+				digi_play_build(402, '_', 1, 2);
+			} else if (frame == 17) {
+				digi_play_build(402, 'x', 5, 1);
+				scratch._a2 = 16;
+			}
+		}
+		break;
+	}
+
+	case 18: {
+		int16 frame = aainfo[1]._frame;
+		if (frame <= 74) {
+			if (frame == 27) {
+				kernel_timing_trigger(120, 105);
+			} else if (frame == 28) {
+				result = 27;
+			} else if (frame == 71 || frame == 74) {
+				digi_play_build(402, '_', 3, 2);
+			}
+		}
+		break;
+	}
+
+	case 20: {
+		int16 frame = aainfo[1]._frame;
+		if (frame == 2) {
+			global_digi_play(15);
+			player.commands_allowed = -1;
+			global[g083] = 0;
+			room_402_anim6();
+		} else if (frame == 4) {
+			if (scratch._a0 == 0) {
+				result = 3;
+			} else {
+				kernel_abort_animation(aa[1]);
+				aainfo[1]._active = 0;
+				aa[1] = kernel_run_animation(kernel_name('z', 1), 103);
+				aainfo[1]._active = -1;
+				aainfo[1]._frame = 0;
+				scratch._98 = 4;
+			}
+		}
+		break;
+	}
+
+	case 19: {
+		if (aainfo[1]._frame == 2)
+			result = 1;
+		break;
+	}
+
+	case 2: {
+		int16 frame = aainfo[1]._frame;
+		if (frame == 57) {
+			global_digi_play(10);
+			global[player_score] = 0;
+		} else if (frame < 57) {
+			if (frame == 37) {
+				if (flags[27] != 1)
+					result = 56;
+			} else if (frame == 43) {
+				digi_play_build(301, '_', 1, 2);
+			}
+		}
+		break;
+	}
+
+	default:
+		break;
+	}
+
+	if (result >= 0) {
+		aainfo[1]._frame = result;
+		kernel_reset_animation(aa[1], result);
+	}
+}
+
+static void room_402_anim3() {
+	int16 cur = kernel_anim[aa[3]].frame;
+	if (cur != aainfo[3]._frame)
+		aainfo[3]._frame = cur;
+}
+
+static void room_402_anim4() {
+	int16 cur = kernel_anim[aa[2]].frame;
+	if (cur != aainfo[2]._frame)
+		aainfo[2]._frame = cur;
 }
 
 static void room_402_daemon() {
+	switch (kernel.trigger) {
+	case 7:
+		room_402_anim_case7();
+		break;
+
+	case 24:
+		kernel_reset_animation(scratch._9a, 0);
+		global[g133] = 1;
+		global[g154] = 2;
+		global[g156] = 0;
+		player.walker_visible = 0;
+		player.commands_allowed = 0;
+		break;
+
+	case 25:
+		if (scratch._8e != 2)
+			break;
+		aa[3] = kernel_run_animation(kernel_name('R', 1), 101);
+		aainfo[3]._active = -1;
+		scratch._94 = 2;
+		kernel_reset_animation(scratch._9c, 0);
+		kernel_synch(KERNEL_ANIM, aa[3], KERNEL_ANIM, scratch._9c);
+		global[g143] = 1;
+		break;
+
+	case 26:
+		kernel_reset_animation(scratch._9a, 0);
+		kernel_reset_animation(scratch._9c, 0);
+		global[g133] = 1;
+		global[g143] = 1;
+		kernel_synch(KERNEL_ANIM, scratch._9a, KERNEL_ANIM, aa[1]);
+		kernel_synch(KERNEL_ANIM, scratch._9c, KERNEL_ANIM, aa[1]);
+		break;
+
+	case 100:
+		kernel_abort_animation(aa[0]);
+		aainfo[0]._active = 0;
+		if (global[g083] == -1) {
+			aa[1] = kernel_run_animation(kernel_name('t', 2), 103);
+			aainfo[1]._active = -1;
+			scratch._98 = 3;
+			break;
+		}
+		kernel_flip_hotspot(48, -1);
+		aa[1] = kernel_run_animation(kernel_name('L', 14), 103);
+		aainfo[1]._active = -1;
+		aainfo[1]._frame = 0;
+		scratch._98 = 19;
+		global[g131] = -1;
+		global[g141] = -1;
+		kernel_reset_animation(scratch._9a, 1);
+		kernel_synch(KERNEL_ANIM, scratch._9a, KERNEL_NOW, 0);
+		kernel_reset_animation(scratch._9c, 1);
+		kernel_synch(KERNEL_ANIM, scratch._9c, KERNEL_NOW, 0);
+		player.walker_visible = -1;
+		global[g133] = 0;
+		global[g143] = 0;
+		kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
+		player.commands_allowed = -1;
+		break;
+
+	case 101:
+		kernel_abort_animation(aa[3]);
+		aainfo[3]._active = 0;
+		kernel_reset_animation(scratch._9c, 1);
+		kernel_synch(KERNEL_ANIM, scratch._9c, KERNEL_NOW, 0);
+		global[g143] = 0;
+		player.commands_allowed = -1;
+		break;
+
+	case 102:
+		kernel_abort_animation(aa[2]);
+		aainfo[2]._active = 0;
+		kernel_reset_animation(scratch._9a, 1);
+		kernel_synch(KERNEL_ANIM, scratch._9a, KERNEL_NOW, 0);
+		global[g133] = 0;
+		player.commands_allowed = -1;
+		break;
+
+	case 103:
+		if (scratch._98 == 1) {
+			kernel_abort_animation(aa[0]);
+			aainfo[0]._active = 0;
+		} else {
+			dont_frag_the_palette();
+			kernel_abort_animation(aa[1]);
+			aainfo[1]._active = 0;
+		}
+		room_402_anim_state();
+		break;
+
+	case 105:
+		aainfo[1]._frame = 28;
+		kernel_reset_animation(aa[1], 28);
+		break;
+
+	default:
+		break;
+	}
+
+	global_anim1(2, scratch._9a, global[g131], &global[g132]);
+	global_anim2(1, scratch._9c, global[g141], &global[g142]);
+
+	if (aainfo[0]._active)
+		room_402_anim1();
+	if (aainfo[1]._active)
+		room_402_anim2();
+	if (aainfo[3]._active)
+		room_402_anim3();
+	if (aainfo[2]._active)
+		room_402_anim4();
 }
 
 static void room_402_pre_parser() {
+	if (global[g083] == 0) {
+		if (player_parse(13, 186, 0)) {
+			player.need_to_walk = 0;
+			return;
+		}
+		scratch._a0 = -1;
+		player.commands_allowed = 0;
+		return;
+	}
+
+	if (global[g083] == 2) {
+		player.need_to_walk = 0;
+		return;
+	}
+
+	if (player_parse(13, 46, 0)) {
+		if (global[g080] == 0 || flags[31] != 0)
+			player.walk_off_edge_to_room = 401;
+		else
+			player.walk_off_edge_to_room = 420;
+	}
+
+	if (player_parse(13, 48, 0))
+		player.walk_off_edge_to_room = 403;
 }
 
 static void room_402_parser() {
+	if (global[walker_converse_state] != 0) {
+		if (global[g083] == 0) {
+			player.commands_allowed = 0;
+			aainfo[1]._frame = 4;
+			kernel_reset_animation(aa[1], 4);
+		} else if (global[g083] == 2) {
+			player.commands_allowed = 0;
+			scratch._90 = 17;
+		} else {
+			player.commands_allowed = 0;
+			digi_play_build_ii('c', 1, 1);
+			scratch._a2 = -668;
+		}
+		player.command_ready = 0;
+		return;
+	}
+
+	if (global[player_selected_object] >= 0) {
+		player.command_ready = 0;
+		return;
+	}
+
+	if (global[g083] == 0) {
+		if (player_parse(13, 186, 0)) {
+			player.commands_allowed = 0;
+			aainfo[1]._frame = 4;
+			kernel_reset_animation(aa[1], 4);
+		} else {
+			scratch._a0 = -1;
+			player.commands_allowed = 0;
+		}
+		player.command_ready = 0;
+		return;
+	}
+
+	if (global[g083] == 2) {
+		if (player_parse(78, 187, 0)) {
+			player.commands_allowed = 0;
+			scratch._90 = 17;
+		}
+		player.command_ready = 0;
+		return;
+	}
 }
 
 void room_402_synchronize(Common::Serializer &s) {
 	for (int16 &v : scratch.sprite)    s.syncAsSint16LE(v);
 	for (int16 &v : scratch.sequence)  s.syncAsSint16LE(v);
 	for (int16 &v : scratch.animation) s.syncAsSint16LE(v);
+	for (AnimationInfo &ai : scratch.animation_info) ai.synchronize(s);
+	s.syncAsSint16LE(scratch._8c);
+	s.syncAsSint16LE(scratch._8e);
+	s.syncAsSint16LE(scratch._90);
+	s.syncAsSint16LE(scratch._92);
+	s.syncAsSint16LE(scratch._94);
+	s.syncAsSint16LE(scratch._96);
+	s.syncAsSint16LE(scratch._98);
+	s.syncAsSint16LE(scratch._9a);
+	s.syncAsSint16LE(scratch._9c);
+	s.syncAsSint16LE(scratch._9e);
+	s.syncAsSint16LE(scratch._a0);
+	s.syncAsSint16LE(scratch._a2);
 }
 
 void room_402_preload() {



From noreply at scummvm.org  Sun Jun 14 07:37:53 2026
From: noreply at scummvm.org (lephilousophe)
Date: Sun, 14 Jun 2026 07:37:53 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 3a2415dab491302c618cfe30c20767c8e41896ff
Message-ID: <20260614073753.AF0147005CC@ron.scummvm.net>

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:
3c823e8be3 GRAPHICS: OPENGL: Forbid creation of incompatible contexts
3a2415dab4 GRAPHICS: OPENGL: Fix GLES build without GLAD


Commit: 3c823e8be30615d9a462033927567d66d7e2e466
    https://github.com/scummvm/scummvm/commit/3c823e8be30615d9a462033927567d66d7e2e466
Author: Le Philousophe (lephilousophe at users.noreply.github.com)
Date: 2026-06-14T09:36:51+02:00

Commit Message:
GRAPHICS: OPENGL: Forbid creation of incompatible contexts

When building with a forced OpenGL mode, other modes must fail.
This allows to call specific GL functions in the context detection code
while not using GLAD.

Changed paths:
    graphics/opengl/context.cpp


diff --git a/graphics/opengl/context.cpp b/graphics/opengl/context.cpp
index a04c9546d8f..996b7c681fc 100644
--- a/graphics/opengl/context.cpp
+++ b/graphics/opengl/context.cpp
@@ -168,8 +168,10 @@ void Context::initialize(ContextType contextType) {
 		extString = "";
 	}
 
+#if !USE_FORCED_GLES && !USE_FORCED_GLES2
 	bool EXTFramebufferMultisample = false;
 	bool EXTFramebufferBlit = false;
+#endif
 
 	Common::StringTokenizer tokenizer(extString, " ");
 	while (!tokenizer.empty()) {
@@ -191,10 +193,12 @@ void Context::initialize(ContextType contextType) {
 			packedDepthStencilSupported = true;
 		} else if (token == "GL_EXT_unpack_subimage") {
 			unpackSubImageSupported = true;
+#if !USE_FORCED_GLES && !USE_FORCED_GLES2
 		} else if (token == "GL_EXT_framebuffer_multisample") {
 			EXTFramebufferMultisample = true;
 		} else if (token == "GL_EXT_framebuffer_blit") {
 			EXTFramebufferBlit = true;
+#endif
 		} else if (token == "GL_OES_depth24") {
 			OESDepth24 = true;
 		} else if (token == "GL_SGIS_texture_edge_clamp") {
@@ -210,6 +214,9 @@ void Context::initialize(ContextType contextType) {
 	}
 
 	if (type == kContextGLES2) {
+#if USE_FORCED_GL || USE_FORCED_GLES
+		error("Creating GLES2 context with non-GLES2 build");
+#else
 // OGLES2 on AmigaOS reports GLSL version as 0.9 but we do what is needed to make it work
 // so let's pretend it supports 1.00
 #if defined(__amigaos4__)
@@ -258,7 +265,11 @@ void Context::initialize(ContextType contextType) {
 		textureLookupPrecision = precision;
 
 		debug(5, "OpenGL: GLES2 context initialized");
+#endif
 	} else if (type == kContextGLES) {
+#if USE_FORCED_GL || USE_FORCED_GLES2
+		error("Creating GLES context with non-GLES build");
+#else
 		// GLES doesn't support shaders natively
 
 		// ScummVM does not support multisample FBOs with GLES for now
@@ -274,7 +285,11 @@ void Context::initialize(ContextType contextType) {
 		// Precision is irrelevant (shaders) in GLES
 
 		debug(5, "OpenGL: GLES context initialized");
+#endif
 	} else if (type == kContextGL) {
+#if USE_FORCED_GLES || USE_FORCED_GLES2
+		error("Creating GL context with non-GL build");
+#else
 		// Official support of shaders starts with version 110
 		// Older versions didn't support the #version directive and were only available through
 		// ARB extensions which we removed support for
@@ -309,6 +324,7 @@ void Context::initialize(ContextType contextType) {
 		textureLookupPrecision = UINT_MAX;
 
 		debug(5, "OpenGL: GL context initialized");
+#endif
 	} else {
 		warning("OpenGL: Unknown context initialized");
 	}


Commit: 3a2415dab491302c618cfe30c20767c8e41896ff
    https://github.com/scummvm/scummvm/commit/3a2415dab491302c618cfe30c20767c8e41896ff
Author: Le Philousophe (lephilousophe at users.noreply.github.com)
Date: 2026-06-14T09:36:57+02:00

Commit Message:
GRAPHICS: OPENGL: Fix GLES build without GLAD

Don't use functions or defines not available

Changed paths:
    backends/graphics/opengl/framebuffer.cpp
    graphics/opengl/context.cpp


diff --git a/backends/graphics/opengl/framebuffer.cpp b/backends/graphics/opengl/framebuffer.cpp
index c3b373da163..a762b157746 100644
--- a/backends/graphics/opengl/framebuffer.cpp
+++ b/backends/graphics/opengl/framebuffer.cpp
@@ -113,6 +113,7 @@ void Framebuffer::applyBlendState() {
 			GL_CALL(glDisable(GL_BLEND));
 			break;
 		case kBlendModeOpaque:
+#if !USE_FORCED_GLES
 			if (!glBlendColor) {
 				// If glBlendColor is not available (old OpenGL) fallback on disabling blending
 				GL_CALL(glDisable(GL_BLEND));
@@ -121,6 +122,10 @@ void Framebuffer::applyBlendState() {
 			GL_CALL(glEnable(GL_BLEND));
 			GL_CALL(glBlendColor(1.f, 1.f, 1.f, 0.f));
 			GL_CALL(glBlendFunc(GL_CONSTANT_COLOR, GL_ONE_MINUS_CONSTANT_COLOR));
+#else
+			// GLES has no glBlendColor
+			GL_CALL(glDisable(GL_BLEND));
+#endif
 			break;
 		case kBlendModeTraditionalTransparency:
 			GL_CALL(glEnable(GL_BLEND));
diff --git a/graphics/opengl/context.cpp b/graphics/opengl/context.cpp
index 996b7c681fc..b4e218ede29 100644
--- a/graphics/opengl/context.cpp
+++ b/graphics/opengl/context.cpp
@@ -333,7 +333,12 @@ void Context::initialize(ContextType contextType) {
 		glGetIntegerv(GL_MAX_SAMPLES, (GLint *)&multisampleMaxSamples);
 	}
 
-	const char *glslVersionString = glslVersion ? (const char *)glGetString(GL_SHADING_LANGUAGE_VERSION) : "";
+	const char *glslVersionString =
+#if !USE_FORCED_GLES
+		glslVersion ? (const char *)glGetString(GL_SHADING_LANGUAGE_VERSION) : "";
+#else
+		"";
+#endif
 
 	// Log features supported by GL context.
 	debug(5, "OpenGL version: %s", glGetString(GL_VERSION));



From noreply at scummvm.org  Sun Jun 14 09:41:46 2026
From: noreply at scummvm.org (ScummVM-Translations)
Date: Sun, 14 Jun 2026 09:41:46 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 3ccf84903072a318c986a1f31fb1fafa040a2122
Message-ID: <20260614094146.731467005CC@ron.scummvm.net>

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:
56fd2b86c7 I18N: Update translation (French)
3ccf849030 I18N: Update translation (Italian)


Commit: 56fd2b86c7c49806ccb5b3816e592f505781ab57
    https://github.com/scummvm/scummvm/commit/56fd2b86c7c49806ccb5b3816e592f505781ab57
Author: Purple T (ZEONK at hotmail.com)
Date: 2026-06-14T09:41:38Z

Commit Message:
I18N: Update translation (French)

Currently translated at 100.0% (3221 of 3221 strings)

Changed paths:
    po/fr_FR.po


diff --git a/po/fr_FR.po b/po/fr_FR.po
index 6bcd8d3669c..e4bd2fb0d71 100644
--- a/po/fr_FR.po
+++ b/po/fr_FR.po
@@ -8,7 +8,7 @@ msgstr ""
 "Project-Id-Version: ScummVM 1.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
 "POT-Creation-Date: 2026-06-13 13:52+0000\n"
-"PO-Revision-Date: 2026-06-08 16:22+0000\n"
+"PO-Revision-Date: 2026-06-14 09:41+0000\n"
 "Last-Translator: Purple T <ZEONK at hotmail.com>\n"
 "Language-Team: French <https://translations.scummvm.org/projects/scummvm/"
 "scummvm/fr/>\n"
@@ -2377,28 +2377,21 @@ msgid "Disable fixed font scaling"
 msgstr "D??sactiver le redimensionnement des polices fixes"
 
 #: gui/options.cpp:2667
-#, fuzzy
-#| msgid ""
-#| "Do not upscale fixed size fonts in the GUI. This reduces artefacts on low "
-#| "resolution screens"
 msgid ""
 "Do not upscale fixed size fonts in the GUI. This reduces artefacts on low "
 "resolution screens."
 msgstr ""
 "N'agrandit pas les polices de largeur fixe dans l'interface graphique. Cela "
-"permet de r??duire les artefacts sur les ??crans ?? faible r??solution"
+"permet de r??duire les artefacts sur les ??crans ?? faible r??solution."
 
 #: gui/options.cpp:2673
-#, fuzzy
-#| msgid "Enable smooth scrolling when walking"
 msgid "Enable kinetic scrolling in lists"
-msgstr "Active le d??filement fluide en marchant"
+msgstr "Activer le d??filement cin??tique dans les listes"
 
 #: gui/options.cpp:2674
-#, fuzzy
-#| msgid "Enable smooth scrolling when walking"
 msgid "Enable smooth, momentum-based scrolling in list widgets."
-msgstr "Active le d??filement fluide en marchant"
+msgstr ""
+"Active le d??filement fluide bas?? sur la vitesse dans les widgets de liste."
 
 #: gui/options.cpp:2680
 msgid "GUI language:"
@@ -10352,32 +10345,24 @@ msgid "Secondary shoot"
 msgstr "Tir secondaire"
 
 #: engines/hypno/metaengine.cpp:149
-#, fuzzy
-#| msgid "Map hide toolbar action"
 msgid "Rapid fire / secondary action"
-msgstr "Affecter l'action 'Cacher Barre d'Outils'"
+msgstr "Tir rapide / action secondaire"
 
 #: engines/hypno/metaengine.cpp:155
-#, fuzzy
-#| msgid "Slide left"
 msgid "Aim left"
-msgstr "Faire un pas vers la gauche"
+msgstr "Viser ?? gauche"
 
 #: engines/hypno/metaengine.cpp:160
-#, fuzzy
-#| msgid "Stick down"
 msgid "Aim down"
-msgstr "Manche bas"
+msgstr "Viser en bas"
 
 #: engines/hypno/metaengine.cpp:165
-#, fuzzy
-#| msgid "Up right"
 msgid "Aim right"
-msgstr "Haut droite"
+msgstr "Viser ?? droite"
 
 #: engines/hypno/metaengine.cpp:170
 msgid "Aim up"
-msgstr ""
+msgstr "Viser en haut"
 
 #: engines/hypno/metaengine.cpp:175 engines/scumm/metaengine.cpp:1202
 #: engines/scumm/metaengine.cpp:1279
@@ -10474,28 +10459,20 @@ msgid "Show credits"
 msgstr "Afficher les cr??dits"
 
 #: engines/hypno/metaengine.cpp:417
-#, fuzzy
-#| msgid "Move left"
 msgid "Menu left"
-msgstr "Se d??placer vers la gauche"
+msgstr "Menu gauche"
 
 #: engines/hypno/metaengine.cpp:425
-#, fuzzy
-#| msgid "Move down"
 msgid "Menu down"
-msgstr "Se d??placer vers le bas"
+msgstr "Menu bas"
 
 #: engines/hypno/metaengine.cpp:433
-#, fuzzy
-#| msgid "Move right"
 msgid "Menu right"
-msgstr "Se d??placer vers la droite"
+msgstr "Menu droite"
 
 #: engines/hypno/metaengine.cpp:441
-#, fuzzy
-#| msgid "Menu"
 msgid "Menu up"
-msgstr "Menu"
+msgstr "Menu haut"
 
 #. I18N: Action in In Cold Blood
 #: engines/icb/icb.cpp:107
@@ -12225,11 +12202,13 @@ msgstr "Met en ??vidence les zones cliquables dans les sc??nes de d??cision."
 
 #: engines/private/metaengine.cpp:61
 msgid "Increased contrast for small print"
-msgstr ""
+msgstr "Contraste accru pour les petits imprim??s"
 
 #: engines/private/metaengine.cpp:62
 msgid "Enhance small printed paper close-ups for improved readability."
 msgstr ""
+"Agrandit les gros plans sur les petits documents imprim??s pour am??liorer la "
+"lisibilit??."
 
 #: engines/private/metaengine.cpp:147 engines/trecision/metaengine.cpp:137
 msgid "Skip video"
@@ -13822,14 +13801,14 @@ msgid "Disable player damage"
 msgstr "D??sactive les d??g??ts re??us par le joueur"
 
 #: engines/scumm/metaengine.cpp:909
-#, fuzzy
-#| msgid "Fast mode"
 msgid "Yoda mode"
-msgstr "Mode rapide"
+msgstr "Mode Yoda"
 
 #: engines/scumm/metaengine.cpp:910
 msgid "Enable original Yoda mode shortcuts, including movie mode and auto play"
 msgstr ""
+"Active les raccourcis d'origine du mode Yoda, y compris les modes "
+"cin??matique et jeu auto"
 
 #: engines/scumm/metaengine.cpp:1085
 msgid "Down left"


Commit: 3ccf84903072a318c986a1f31fb1fafa040a2122
    https://github.com/scummvm/scummvm/commit/3ccf84903072a318c986a1f31fb1fafa040a2122
Author: Walter Agazzi (walter.agazzi at protonmail.com)
Date: 2026-06-14T09:41:39Z

Commit Message:
I18N: Update translation (Italian)

Currently translated at 100.0% (3221 of 3221 strings)

Changed paths:
    po/it_IT.po


diff --git a/po/it_IT.po b/po/it_IT.po
index e6474ad5571..639cba0d201 100644
--- a/po/it_IT.po
+++ b/po/it_IT.po
@@ -8,7 +8,7 @@ msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
 "POT-Creation-Date: 2026-06-13 13:52+0000\n"
-"PO-Revision-Date: 2026-06-13 09:55+0000\n"
+"PO-Revision-Date: 2026-06-14 09:41+0000\n"
 "Last-Translator: Walter Agazzi <walter.agazzi at protonmail.com>\n"
 "Language-Team: Italian <https://translations.scummvm.org/projects/scummvm/"
 "scummvm/it/>\n"
@@ -2360,28 +2360,20 @@ msgid "Disable fixed font scaling"
 msgstr "Disabilita ridimensionamento dei caratteri monospazio"
 
 #: gui/options.cpp:2667
-#, fuzzy
-#| msgid ""
-#| "Do not upscale fixed size fonts in the GUI. This reduces artefacts on low "
-#| "resolution screens"
 msgid ""
 "Do not upscale fixed size fonts in the GUI. This reduces artefacts on low "
 "resolution screens."
 msgstr ""
 "Non ingrandisce i caratteri monospazio dell'interfaccia, prevenendo "
-"artefatti su schermi a bassa risoluzione"
+"artefatti su schermi a bassa risoluzione."
 
 #: gui/options.cpp:2673
-#, fuzzy
-#| msgid "Enable smooth scrolling when walking"
 msgid "Enable kinetic scrolling in lists"
-msgstr "Attiva lo scorrimento fluido durante gli spostamenti"
+msgstr "Abilita scorrimento cinetico nelle liste"
 
 #: gui/options.cpp:2674
-#, fuzzy
-#| msgid "Enable smooth scrolling when walking"
 msgid "Enable smooth, momentum-based scrolling in list widgets."
-msgstr "Attiva lo scorrimento fluido durante gli spostamenti"
+msgstr "Attiva lo scorrimento fluido (con momento) dei widget a lista"
 
 #: gui/options.cpp:2680
 msgid "GUI language:"
@@ -10315,32 +10307,24 @@ msgid "Secondary shoot"
 msgstr "Sparo arma secondaria"
 
 #: engines/hypno/metaengine.cpp:149
-#, fuzzy
-#| msgid "Map hide toolbar action"
 msgid "Rapid fire / secondary action"
-msgstr "Mappa l'azione 'Nascondi barra degli strumenti'"
+msgstr "Fuoco rapido / azione secondaria"
 
 #: engines/hypno/metaengine.cpp:155
-#, fuzzy
-#| msgid "Slide left"
 msgid "Aim left"
-msgstr "Scorri a sinistra"
+msgstr "Mira a sinistra"
 
 #: engines/hypno/metaengine.cpp:160
-#, fuzzy
-#| msgid "Stick down"
 msgid "Aim down"
-msgstr "Joystick gi??"
+msgstr "Mira in basso"
 
 #: engines/hypno/metaengine.cpp:165
-#, fuzzy
-#| msgid "Up right"
 msgid "Aim right"
-msgstr "Su destra"
+msgstr "Mira a destra"
 
 #: engines/hypno/metaengine.cpp:170
 msgid "Aim up"
-msgstr ""
+msgstr "Mira in alto"
 
 #: engines/hypno/metaengine.cpp:175 engines/scumm/metaengine.cpp:1202
 #: engines/scumm/metaengine.cpp:1279
@@ -10437,28 +10421,20 @@ msgid "Show credits"
 msgstr "Mostra riconoscimenti"
 
 #: engines/hypno/metaengine.cpp:417
-#, fuzzy
-#| msgid "Move left"
 msgid "Menu left"
-msgstr "Movimento verso sinistra"
+msgstr "Men?? sinistra"
 
 #: engines/hypno/metaengine.cpp:425
-#, fuzzy
-#| msgid "Move down"
 msgid "Menu down"
-msgstr "Movimento verso il basso"
+msgstr "Men?? gi??"
 
 #: engines/hypno/metaengine.cpp:433
-#, fuzzy
-#| msgid "Move right"
 msgid "Menu right"
-msgstr "Movimento verso destra"
+msgstr "Men?? destra"
 
 #: engines/hypno/metaengine.cpp:441
-#, fuzzy
-#| msgid "Menu"
 msgid "Menu up"
-msgstr "Menu"
+msgstr "Men?? su"
 
 #. I18N: Action in In Cold Blood
 #: engines/icb/icb.cpp:107



From noreply at scummvm.org  Sun Jun 14 09:53:08 2026
From: noreply at scummvm.org (ScummVM-Translations)
Date: Sun, 14 Jun 2026 09:53:08 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 6a7469f726b858e612d87f72d4cb62f18cda236f
Message-ID: <20260614095308.924AD7005CC@ron.scummvm.net>

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

Summary:
6a7469f726 I18N: Update translation (Spanish)


Commit: 6a7469f726b858e612d87f72d4cb62f18cda236f
    https://github.com/scummvm/scummvm/commit/6a7469f726b858e612d87f72d4cb62f18cda236f
Author: IlDucci (pajaroloco_2 at hotmail.com)
Date: 2026-06-14T09:53:02Z

Commit Message:
I18N: Update translation (Spanish)

Currently translated at 100.0% (3221 of 3221 strings)

Changed paths:
    po/es_ES.po


diff --git a/po/es_ES.po b/po/es_ES.po
index 0ebcf5c019d..0f2e696e028 100644
--- a/po/es_ES.po
+++ b/po/es_ES.po
@@ -8,7 +8,7 @@ msgstr ""
 "Project-Id-Version: ScummVM 1.4.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
 "POT-Creation-Date: 2026-06-13 13:52+0000\n"
-"PO-Revision-Date: 2026-06-13 11:38+0000\n"
+"PO-Revision-Date: 2026-06-14 09:53+0000\n"
 "Last-Translator: IlDucci <pajaroloco_2 at hotmail.com>\n"
 "Language-Team: Spanish <https://translations.scummvm.org/projects/scummvm/"
 "scummvm/es/>\n"
@@ -2363,28 +2363,20 @@ msgid "Disable fixed font scaling"
 msgstr "Desactivar escalado de fuentes fijas"
 
 #: gui/options.cpp:2667
-#, fuzzy
-#| msgid ""
-#| "Do not upscale fixed size fonts in the GUI. This reduces artefacts on low "
-#| "resolution screens"
 msgid ""
 "Do not upscale fixed size fonts in the GUI. This reduces artefacts on low "
 "resolution screens."
 msgstr ""
 "No se escalar??n las fuentes de ancho fijo dentro de la interfaz, lo que "
-"reducir?? los artefactos en pantallas de baja resoluci??n"
+"reducir?? los artefactos en pantallas de baja resoluci??n."
 
 #: gui/options.cpp:2673
-#, fuzzy
-#| msgid "Enable smooth scrolling when walking"
 msgid "Enable kinetic scrolling in lists"
-msgstr "Activar el desplazamiento de pantalla suave al caminar"
+msgstr "Activar desplazamiento cin??tico en listas"
 
 #: gui/options.cpp:2674
-#, fuzzy
-#| msgid "Enable smooth scrolling when walking"
 msgid "Enable smooth, momentum-based scrolling in list widgets."
-msgstr "Activar el desplazamiento de pantalla suave al caminar"
+msgstr "Activa un desplazamiento suave y con impulso en los widgets de listas."
 
 #: gui/options.cpp:2680
 msgid "GUI language:"



From noreply at scummvm.org  Sun Jun 14 09:57:35 2026
From: noreply at scummvm.org (dreammaster)
Date: Sun, 14 Jun 2026 09:57:35 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 d4c9344ba754a4d2ae9d8daa0a25c28e2788bbba
Message-ID: <20260614095735.D582E7005CC@ron.scummvm.net>

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

Summary:
d4c9344ba7 MADS: FOREST: Implemented room 403


Commit: d4c9344ba754a4d2ae9d8daa0a25c28e2788bbba
    https://github.com/scummvm/scummvm/commit/d4c9344ba754a4d2ae9d8daa0a25c28e2788bbba
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-06-14T19:57:23+10:00

Commit Message:
MADS: FOREST: Implemented room 403

Changed paths:
    engines/mads/madsv2/forest/rooms/room403.cpp


diff --git a/engines/mads/madsv2/forest/rooms/room403.cpp b/engines/mads/madsv2/forest/rooms/room403.cpp
index e0ddcbd5450..c0699dca9c4 100644
--- a/engines/mads/madsv2/forest/rooms/room403.cpp
+++ b/engines/mads/madsv2/forest/rooms/room403.cpp
@@ -1,4 +1,4 @@
-???/* ScummVM - Graphic Adventure Engine
+/* 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
@@ -20,17 +20,23 @@
  */
 
 #include "mads/madsv2/core/conv.h"
+#include "mads/madsv2/core/digi.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/imath.h"
 #include "mads/madsv2/core/inter.h"
 #include "mads/madsv2/core/kernel.h"
+#include "mads/madsv2/core/midi.h"
+#include "mads/madsv2/core/object.h"
+#include "mads/madsv2/core/player.h"
+#include "mads/madsv2/core/sprite.h"
 #include "mads/madsv2/core/sound.h"
 #include "mads/madsv2/core/text.h"
 #include "mads/madsv2/forest/mads/inventory.h"
 #include "mads/madsv2/forest/mads/sounds.h"
 #include "mads/madsv2/forest/mads/words.h"
+#include "mads/madsv2/forest/extra.h"
 #include "mads/madsv2/forest/global.h"
-#include "mads/madsv2/forest/rooms/section1.h"
+#include "mads/madsv2/forest/journal.h"
 #include "mads/madsv2/forest/rooms/room403.h"
 
 namespace MADS {
@@ -39,10 +45,25 @@ namespace Forest {
 namespace Rooms {
 
 struct Scratch {
-	int16 sprite[10];       /* Sprite series handles */
-	int16 sequence[10];     /* Sequence handles      */
-	int16 animation[10];     /* Animation handles     */
+	int16 sprite[10];
+	int16 sequence[10];
+	int16 animation[10];
 	AnimationInfo animation_info[10];
+	int16 _8c;
+	int16 _8e;
+	int16 _90;
+	int16 _92;
+	int16 _94;
+	int16 _96;
+	int16 _98;
+	int16 _9a;
+	int16 _9c;
+	int16 _9e;
+	int16 _a0;
+	int16 _a2;
+	int16 _a4;
+	int16 _a6;
+	int16 _a8;
 };
 
 static Scratch scratch;
@@ -54,22 +75,749 @@ static Scratch scratch;
 #define aainfo scratch.animation_info
 
 
+static void room_403_init1() {
+	global[g131] = 0;
+	global[g141] = 0;
+
+	scratch._9c = kernel_run_animation_disp('r', 1, 0);
+	kernel_position_anim(scratch._9c, 159, 99, 81, 12);
+	scratch._9a = kernel_run_animation_disp('e', 3, 0);
+	kernel_position_anim(scratch._9a, 129, 98, 81, 12);
+
+	if (previous_room != KERNEL_RESTORING_GAME) {
+		player.x = 104;
+		player.y = 103;
+		player.facing = 3;
+	}
+
+	if (previous_room == KERNEL_RESTORING_GAME) {
+		global[g131] = -1;
+		global[g141] = -1;
+		kernel_reset_animation(scratch._9a, 2);
+		kernel_reset_animation(scratch._9c, 2);
+		global[g133] = 0;
+		global[g143] = 0;
+		return;
+	}
+
+	if (previous_room == 199) {
+		global[g131] = -1;
+		global[g141] = -1;
+		kernel_reset_animation(scratch._9a, 2);
+		kernel_reset_animation(scratch._9c, 2);
+		global[g133] = 0;
+		global[g143] = 0;
+		restore_player();
+		player.commands_allowed = -1;
+		player.walker_visible = -1;
+		return;
+	}
+
+	if (previous_room == 402) {
+		if (flags[28] == 1) {
+			aa[6] = kernel_run_animation(kernel_name('t', 1), 104);
+			aainfo[6]._active = -1;
+		} else {
+			aa[0] = kernel_run_animation(kernel_name('y', 1), 100);
+			aainfo[0]._active = -1;
+		}
+		scratch._92 = 72;
+		return;
+	}
+
+	global[g131] = -1;
+	global[g141] = -1;
+	kernel_reset_animation(scratch._9a, 2);
+	kernel_reset_animation(scratch._9c, 2);
+	global[g133] = 0;
+	global[g143] = 0;
+	player.commands_allowed = -1;
+	player.walker_visible = -1;
+}
+
 static void room_403_init() {
+	scratch._a0 = 0;
+	scratch._a2 = -1;
+	midi_stop();
+	global[g009] = 0;
+	global[player_score] = -1;
+	kernel_timing_trigger(1, 105);
+
+	if (object_is_here(13)) {
+		ss[0] = kernel_load_series(kernel_name('p', 2), 0);
+		seq[0] = kernel_seq_stamp(ss[0], 0, -1);
+		kernel_seq_depth(seq[0], 15);
+		kernel_seq_loc(seq[0], 276, 154);
+		kernel_seq_scale(seq[0], 99);
+	} else {
+		kernel_flip_hotspot(166, 0);
+	}
+
+	if (global[g080] == 0) {
+		ss[1] = kernel_load_series(kernel_name('p', 1), 0);
+		seq[1] = kernel_seq_stamp(ss[1], -1, -1);
+		kernel_seq_depth(seq[1], 6);
+		kernel_seq_loc(seq[1], 51, 154);
+		kernel_seq_scale(seq[1], 100);
+	} else {
+		kernel_flip_hotspot(95, 0);
+	}
+
+	if (previous_room != KERNEL_RESTORING_GAME) {
+		if (previous_room != 199) {
+			player.walker_visible = 0;
+			player.commands_allowed = 0;
+		}
+
+		for (int16 count = 0; count < 10; count++) {
+			aainfo[count]._active = 0;
+			aainfo[count]._frame = 1;
+			aainfo[count]._val3 = 0;
+			aainfo[count]._val4 = 0;
+		}
+
+		if (previous_room != 199 && flags[28] != 3)
+			flags[28]++;
+	}
+
+	room_403_init1();
+}
+
+static void room_403_anim1() {
+	if (kernel_anim[aa[0]].frame == aainfo[0]._frame)
+		return;
+	aainfo[0]._frame = kernel_anim[aa[0]].frame;
+	if (global[player_hyperwalked] != -1)
+		return;
+	int16 ax = scratch._92 - 1;
+	aainfo[0]._frame = ax;
+	kernel_reset_animation(aa[0], ax);
+}
+
+static void room_403_anim2() {
+	if (kernel_anim[aa[6]].frame != aainfo[6]._frame) {
+		int16 frame = kernel_anim[aa[6]].frame;
+		aainfo[6]._frame = frame;
+		if (frame == 75) {
+			digi_play_build(403, 'e', 1, 1);
+			scratch._a4 = 1;
+		} else if (frame == 80) {
+			aainfo[6]._frame = 76;
+			kernel_reset_animation(aa[6], 76);
+		}
+	}
+
+	if (kernel.trigger == 7 || kernel.trigger == 28) {
+		if (scratch._a4 == 1) {
+			kernel_timing_trigger(30, 28);
+			scratch._a4 = 0;
+			aainfo[6]._frame = 81;
+			kernel_reset_animation(aa[6], 81);
+		}
+	}
+
+	if (global[player_hyperwalked] != -1)
+		return;
+	int16 ax = scratch._92 - 1;
+	if (ax <= aainfo[6]._frame)
+		return;
+	aainfo[6]._frame = ax;
+	kernel_reset_animation(aa[6], ax);
+}
+
+static void room_403_anim3() {
+	if (kernel_anim[aa[1]].frame == aainfo[1]._frame)
+		return;
+	aainfo[1]._frame = kernel_anim[aa[1]].frame;
+}
+
+static void room_403_anim4() {
+	if (kernel_anim[aa[3]].frame != aainfo[3]._frame) {
+		int16 ax = kernel_anim[aa[3]].frame;
+		aainfo[3]._frame = ax;
+
+		if (scratch._a2 == 1) {
+			if (ax == 28) {
+				aainfo[3]._val3 = 21;
+				digi_play_build_ii('e', 3, 1);
+				scratch._a4 = 28;
+			} else if (ax == 33) {
+				if (aainfo[3]._val3 == 21) {
+					aainfo[3]._frame = 28;
+					kernel_reset_animation(aa[3], 28);
+				}
+			} else if (ax == 37) {
+				if (aainfo[3]._val3 == 23) {
+					aainfo[3]._frame = 34;
+					kernel_reset_animation(aa[3], 34);
+				}
+			}
+		} else if (scratch._a2 == 2) {
+			if (ax == 45) {
+				aainfo[3]._val3 = 21;
+				digi_play_build_ii('e', 7, 1);
+				scratch._a4 = 450;
+			} else if (ax == 50) {
+				if (aainfo[3]._val3 == 21) {
+					aainfo[3]._frame = 45;
+					kernel_reset_animation(aa[3], 45);
+				}
+			} else if (ax == 54) {
+				if (aainfo[3]._val3 == 23) {
+					aainfo[3]._frame = 51;
+					kernel_reset_animation(aa[3], 51);
+				}
+			}
+		} else if (scratch._a2 == 3) {
+			if (ax == 49) {
+				aainfo[3]._val3 = 21;
+				digi_play_build_ii('e', 5, 1);
+				scratch._a4 = 4900;
+			} else if (ax == 54) {
+				if (aainfo[3]._val3 == 21) {
+					aainfo[3]._frame = 49;
+					kernel_reset_animation(aa[3], 49);
+				}
+			} else if (ax == 57) {
+				if (aainfo[3]._val3 == 23) {
+					aainfo[3]._frame = 55;
+					kernel_reset_animation(aa[3], 55);
+				}
+			}
+		} else if (scratch._a2 == 4) {
+			if (ax == 45) {
+				aainfo[3]._val3 = 21;
+				digi_play_build_ii('e', 6, 1);
+				scratch._a4 = 45;
+			} else if (ax == 50) {
+				if (aainfo[3]._val3 == 21) {
+					aainfo[3]._frame = 45;
+					kernel_reset_animation(aa[3], 45);
+				}
+			} else if (ax == 54) {
+				if (aainfo[3]._val3 == 23) {
+					aainfo[3]._frame = 51;
+					kernel_reset_animation(aa[3], 51);
+				}
+			}
+		}
+	}
+
+	if (kernel.trigger != 7 && kernel.trigger != 28)
+		return;
+
+	int16 a4 = scratch._a4;
+	if (a4 == 28) {
+		kernel_timing_trigger(30, 28);
+		aainfo[3]._val3 = 23;
+		scratch._a4 = 29;
+	} else if (a4 == 29) {
+		aainfo[3]._val3 = 21;
+		aainfo[3]._frame = 28;
+		kernel_reset_animation(aa[3], 28);
+		digi_play_build_ii('e', 10, 1);
+		scratch._a4 = 30;
+	} else if (a4 == 30) {
+		aainfo[3]._val3 = 19;
+		aainfo[3]._frame = 34;
+		kernel_reset_animation(aa[3], 34);
+		scratch._a4 = -1;
+	} else if (a4 == 45) {
+		kernel_timing_trigger(30, 28);
+		aainfo[3]._val3 = 23;
+		scratch._a4 = 46;
+	} else if (a4 == 46) {
+		aainfo[3]._val3 = 21;
+		aainfo[3]._frame = 45;
+		kernel_reset_animation(aa[3], 45);
+		digi_play_build_ii('e', 10, 1);
+		scratch._a4 = 47;
+	} else if (a4 == 47) {
+		aainfo[3]._val3 = 19;
+		aainfo[3]._frame = 51;
+		kernel_reset_animation(aa[3], 51);
+		scratch._a4 = -1;
+	} else if (a4 == 450) {
+		kernel_timing_trigger(30, 28);
+		aainfo[3]._val3 = 23;
+		scratch._a4 = 451;
+	} else if (a4 == 451) {
+		aainfo[3]._val3 = 21;
+		aainfo[3]._frame = 45;
+		kernel_reset_animation(aa[3], 45);
+		digi_play_build_ii('e', 10, 1);
+		scratch._a4 = 452;
+	} else if (a4 == 452) {
+		aainfo[3]._val3 = 19;
+		aainfo[3]._frame = 51;
+		kernel_reset_animation(aa[3], 51);
+		scratch._a4 = -1;
+	} else if (a4 == 4900) {
+		kernel_timing_trigger(30, 28);
+		aainfo[3]._val3 = 23;
+		scratch._a4 = 4901;
+	} else if (a4 == 4901) {
+		aainfo[3]._val3 = 21;
+		aainfo[3]._frame = 49;
+		kernel_reset_animation(aa[3], 49);
+		digi_play_build_ii('e', 10, 1);
+		scratch._a4 = 4902;
+	} else if (a4 == 4902) {
+		aainfo[3]._val3 = 19;
+		aainfo[3]._frame = 55;
+		kernel_reset_animation(aa[3], 55);
+		scratch._a4 = -1;
+	}
+}
+
+static void room_403_anim5() {
+	if (kernel_anim[aa[2]].frame == aainfo[2]._frame)
+		return;
+	aainfo[2]._frame = kernel_anim[aa[2]].frame;
+}
+
+static void room_403_anim6() {
+	if (kernel_anim[aa[5]].frame != aainfo[5]._frame) {
+		int16 ax = kernel_anim[aa[5]].frame;
+		aainfo[5]._frame = ax;
+
+		if (scratch._a2 == 1) {
+			if (ax == 5) {
+				aainfo[5]._val3 = 21;
+				digi_play_build(403, 'e', 3, 1);
+				scratch._a4 = 5;
+			} else if (ax == 10) {
+				if (aainfo[5]._val3 == 21) {
+					aainfo[5]._frame = 5;
+					kernel_reset_animation(aa[5], 5);
+				}
+			} else if (ax == 42) {
+				if (aainfo[5]._val3 == 23) {
+					digi_play_build(101, '_', 1, 2);
+					scratch._a6 = 42;
+				}
+			}
+		} else if (scratch._a2 == 2) {
+			if (ax == 6) {
+				aainfo[5]._val3 = 21;
+				digi_play_build(403, 'e', 2, 1);
+				scratch._a4 = 60;
+			} else if (ax == 11) {
+				if (aainfo[5]._val3 == 21) {
+					aainfo[5]._frame = 6;
+					kernel_reset_animation(aa[5], 6);
+				}
+			} else if (ax == 53) {
+				if (aainfo[5]._val3 == 23) {
+					digi_play_build(101, '_', 1, 2);
+					scratch._a6 = 53;
+				}
+			} else if (ax == 89) {
+				aainfo[5]._val3 = 21;
+				digi_play_build(403, 'b', 1, 1);
+				scratch._a4 = 89;
+			} else if (ax == 95) {
+				if (aainfo[5]._val3 == 21) {
+					aainfo[5]._frame = 89;
+					kernel_reset_animation(aa[5], 89);
+				}
+			}
+		}
+	}
+
+	if (kernel.trigger == 7 || kernel.trigger == 28) {
+		int16 a4 = scratch._a4;
+		if (a4 == 5) {
+			aainfo[5]._val3 = 23;
+			aainfo[5]._frame = 11;
+			kernel_reset_animation(aa[5], 11);
+			scratch._a4 = -1;
+			kernel_seq_delete(seq[0]);
+		} else if (a4 == 60) {
+			aainfo[5]._val3 = 23;
+			aainfo[5]._frame = 12;
+			kernel_reset_animation(aa[5], 12);
+			scratch._a4 = -1;
+			global_digi_play(14);
+			kernel_seq_delete(seq[1]);
+			global[g080] = -1;
+		} else if (a4 == 89) {
+			aainfo[5]._val3 = 23;
+			aainfo[5]._frame = 96;
+			kernel_reset_animation(aa[5], 96);
+			scratch._a4 = -1;
+		}
+	}
+
+	if (kernel.trigger == 8 || kernel.trigger == 28) {
+		int16 a6 = scratch._a6;
+		if (a6 == 42 || a6 == 53)
+			scratch._a6 = -1;
+	}
 }
 
 static void room_403_daemon() {
+	switch (kernel.trigger) {
+	case 7:
+		if (global[walker_converse_state] != 0) {
+			global[walker_converse_state] = 0;
+			close_journal(3);
+			player.commands_allowed = -1;
+			if (digi_val1 != 0)
+				kernel_timing_trigger(1, 105);
+		} else if (scratch._a4 == 300) {
+			kernel_abort_animation(scratch._9e);
+			scratch._a4 = -1;
+			scratch._a0 = 0;
+			kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
+			scratch._a2 = 2;
+			player.walker_visible = -1;
+			global[g150] = -1;
+			scratch._90 = 6;
+		} else if (scratch._a4 == 400) {
+			kernel_abort_animation(scratch._9e);
+			scratch._a4 = -1;
+			scratch._a0 = 0;
+			kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
+			scratch._a2 = 1;
+			player.walker_visible = -1;
+			global[g150] = -1;
+			scratch._90 = 6;
+		}
+		break;
+
+	case 17: {
+		if (scratch._8c == 3) {
+			aa[1] = kernel_run_animation(kernel_name('E', 1), 102);
+			aainfo[1]._active = -1;
+			scratch._96 = 1;
+			kernel_reset_animation(scratch._9a, 0);
+			kernel_synch(KERNEL_ANIM, aa[1], KERNEL_ANIM, scratch._9a);
+			global[g133] = 1;
+			kernel_timing_trigger(1, 105);
+		} else if (scratch._8c == 5) {
+			int16 fnum;
+			if (scratch._a2 == 1)      fnum = 3;
+			else if (scratch._a2 == 2) fnum = 2;
+			else if (scratch._a2 == 3) fnum = 4;
+			else if (scratch._a2 == 4) fnum = 5;
+			else break;
+			aa[3] = kernel_run_animation(kernel_name('F', fnum), 102);
+			aainfo[3]._active = -1;
+			scratch._96 = 3;
+			kernel_reset_animation(scratch._9a, 0);
+			kernel_synch(KERNEL_ANIM, aa[3], KERNEL_ANIM, scratch._9a);
+			global[g133] = 1;
+			kernel_timing_trigger(1, 105);
+		}
+		break;
+	}
+
+	case 18:
+		if (scratch._8e == 2) {
+			aa[2] = kernel_run_animation(kernel_name('R', 1), 101);
+			aainfo[2]._active = -1;
+			scratch._94 = 2;
+			kernel_reset_animation(scratch._9c, 0);
+			kernel_synch(KERNEL_ANIM, aa[2], KERNEL_ANIM, scratch._9c);
+			global[g143] = 1;
+			kernel_timing_trigger(1, 105);
+		}
+		break;
+
+	case 19:
+		if (scratch._90 == 1) {
+			kernel_reset_animation(scratch._9a, 0);
+			kernel_reset_animation(scratch._9c, 0);
+			global[g133] = 1;
+			global[g143] = 1;
+		} else if (scratch._90 == 6) {
+			if (scratch._a2 == 1) {
+				aa[5] = kernel_run_animation(kernel_name('P', 1), 103);
+				aainfo[5]._active = -1;
+				scratch._98 = 5;
+				kernel_reset_animation(scratch._9a, 0);
+				kernel_reset_animation(scratch._9c, 0);
+				kernel_synch(KERNEL_ANIM, scratch._9a, KERNEL_ANIM, aa[5]);
+				kernel_synch(KERNEL_ANIM, scratch._9c, KERNEL_ANIM, aa[5]);
+				global[g133] = 1;
+				global[g143] = 1;
+				kernel_timing_trigger(1, 105);
+			} else if (scratch._a2 == 2) {
+				aa[5] = kernel_run_animation(kernel_name('F', 1), 103);
+				aainfo[5]._active = -1;
+				scratch._98 = 5;
+				kernel_reset_animation(scratch._9a, 0);
+				kernel_reset_animation(scratch._9c, 0);
+				kernel_synch(KERNEL_ANIM, scratch._9a, KERNEL_ANIM, aa[5]);
+				kernel_synch(KERNEL_ANIM, scratch._9c, KERNEL_ANIM, aa[5]);
+				player.walker_visible = 0;
+				global[g133] = 1;
+				global[g143] = 1;
+				kernel_timing_trigger(1, 105);
+			}
+		}
+		break;
+
+	case 27:
+		global[walker_converse_now] = 0;
+		kernel_abort_animation(scratch._a8);
+		global[g131] = -1;
+		kernel_reset_animation(scratch._9a, 1);
+		kernel_synch(KERNEL_ANIM, scratch._9a, KERNEL_NOW, 0);
+		global[g133] = 0;
+		player.commands_allowed = -1;
+		if (digi_val1 != 0)
+			kernel_timing_trigger(1, 105);
+		break;
+
+	case 100:
+		kernel_abort_animation(aa[0]);
+		aainfo[0]._active = 0;
+		global[g131] = -1;
+		global[g141] = -1;
+		kernel_reset_animation(scratch._9a, 1);
+		kernel_synch(KERNEL_ANIM, scratch._9a, KERNEL_NOW, 0);
+		kernel_reset_animation(scratch._9c, 1);
+		kernel_synch(KERNEL_ANIM, scratch._9c, KERNEL_NOW, 0);
+		player.walker_visible = -1;
+		global[g133] = 0;
+		global[g143] = 0;
+		kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
+		player.commands_allowed = -1;
+		if (digi_val1 != 0)
+			kernel_timing_trigger(1, 105);
+		break;
+
+	case 101:
+		kernel_abort_animation(aa[scratch._94]);
+		aainfo[scratch._94]._active = 0;
+		kernel_reset_animation(scratch._9c, 1);
+		kernel_synch(KERNEL_ANIM, scratch._9c, KERNEL_NOW, 0);
+		global[g143] = 0;
+		player.commands_allowed = -1;
+		if (digi_val1 != 0)
+			kernel_timing_trigger(1, 105);
+		break;
+
+	case 102:
+		kernel_abort_animation(aa[scratch._96]);
+		aainfo[scratch._96]._active = 0;
+		kernel_reset_animation(scratch._9a, 1);
+		kernel_synch(KERNEL_ANIM, scratch._9a, KERNEL_NOW, 0);
+		global[g133] = 0;
+		player.commands_allowed = -1;
+		if (digi_val1 != 0)
+			kernel_timing_trigger(1, 105);
+		break;
+
+	case 103:
+		if (scratch._a2 == 1) {
+			kernel_abort_animation(aa[5]);
+			aainfo[5]._active = 0;
+			kernel_reset_animation(scratch._9c, 1);
+			kernel_synch(KERNEL_ANIM, scratch._9c, KERNEL_NOW, 0);
+			global[g143] = 0;
+			kernel_reset_animation(scratch._9a, 1);
+			kernel_synch(KERNEL_ANIM, scratch._9a, KERNEL_NOW, 0);
+			global[g133] = 0;
+			kernel_flip_hotspot(166, 0);
+			inter_move_object(13, 2);
+			display_interface();
+			player.commands_allowed = -1;
+			if (digi_val1 != 0)
+				kernel_timing_trigger(1, 105);
+		} else if (scratch._a2 == 2) {
+			kernel_abort_animation(aa[5]);
+			aainfo[5]._active = 0;
+			kernel_reset_animation(scratch._9c, 1);
+			kernel_synch(KERNEL_ANIM, scratch._9c, KERNEL_NOW, 0);
+			global[g143] = 0;
+			kernel_reset_animation(scratch._9a, 1);
+			kernel_synch(KERNEL_ANIM, scratch._9a, KERNEL_NOW, 0);
+			player.walker_visible = -1;
+			global[g133] = 0;
+			kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
+			kernel_flip_hotspot(95, 0);
+			player.commands_allowed = -1;
+			if (digi_val1 != 0)
+				kernel_timing_trigger(1, 105);
+		}
+		break;
+
+	case 104:
+		kernel_abort_animation(aa[6]);
+		aainfo[6]._active = 0;
+		global[g141] = -1;
+		kernel_reset_animation(scratch._9c, 1);
+		kernel_synch(KERNEL_ANIM, scratch._9c, KERNEL_NOW, 0);
+		player.walker_visible = -1;
+		global[g143] = 0;
+		kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
+		scratch._a8 = kernel_run_animation_write(0);
+		kernel_reset_animation(scratch._a8, 3);
+		kernel_position_anim(scratch._a8, 129, 98, 81, 12);
+		global[walker_converse_now] = 1;
+		global[g007] = 3;
+		kernel_synch(KERNEL_ANIM, scratch._a8, KERNEL_NOW, 0);
+		kernel_timing_trigger(1, 105);
+		break;
+
+	case 105:
+		digi_initial_volume(15);
+		digi_play_build(306, '_', 2, 3);
+		digi_val2 = -1;
+		break;
+
+	default:
+		break;
+	}
+
+	global_anim1(3, scratch._9a, global[g131], &global[g132]);
+	global_anim2(1, scratch._9c, global[g141], &global[g142]);
+
+	if (scratch._a0 != 0)
+		kernel_random_frame(scratch._9e, &global[g151], global[g154]);
+
+	if (aainfo[0]._active)
+		room_403_anim1();
+	if (aainfo[6]._active)
+		room_403_anim2();
+	if (aainfo[1]._active)
+		room_403_anim3();
+	if (aainfo[3]._active)
+		room_403_anim4();
+	if (aainfo[2]._active)
+		room_403_anim5();
+	if (aainfo[5]._active)
+		room_403_anim6();
+
+	if (global[walker_converse_now] != 0)
+		global_anim3(scratch._a8, &global[g008]);
 }
 
 static void room_403_pre_parser() {
+	if (player_parse(13, 47, 0))
+		player.walk_off_edge_to_room = 402;
 }
 
 static void room_403_parser() {
+	if (global[walker_converse_state] != 0) {
+		player.commands_allowed = 0;
+		digi_play_build_ii('c', 1, 1);
+		player.command_ready = 0;
+		return;
+	}
+
+	if (global[player_selected_object] >= 0) {
+		player.command_ready = 0;
+		return;
+	}
+
+	if (player_parse(114, 142, 0)) {
+		player.commands_allowed = 0;
+		global[g135] = -1;
+		scratch._8c = 5;
+		scratch._a2 = 1;
+		player.command_ready = 0;
+		return;
+	}
+
+	if (player_parse(114, 83, 0)) {
+		player.commands_allowed = 0;
+		global[g135] = -1;
+		scratch._8c = 5;
+		scratch._a2 = 2;
+		player.command_ready = 0;
+		return;
+	}
+
+	if (player_parse(114, 131, 0)) {
+		player.commands_allowed = 0;
+		global[g135] = -1;
+		scratch._8c = 5;
+		scratch._a2 = 3;
+		player.command_ready = 0;
+		return;
+	}
+
+	if (player_parse(114, 146, 0)) {
+		player.commands_allowed = 0;
+		global[g135] = -1;
+		scratch._8c = 5;
+		scratch._a2 = 4;
+		player.command_ready = 0;
+		return;
+	}
+
+	if (player_parse(114, 116, 0)) {
+		player.commands_allowed = 0;
+		global[g145] = -1;
+		scratch._8e = 2;
+		player.command_ready = 0;
+		return;
+	}
+
+	if (player_parse(126, 166, 0)) {
+		global[g154] = 2;
+		player.walker_visible = 0;
+		player.commands_allowed = 0;
+		scratch._9e = kernel_run_animation_talk('b', 2, 0);
+		kernel_position_anim(scratch._9e, player.x, player.y, player.scale, player.depth);
+		kernel_synch(KERNEL_ANIM, scratch._9e, KERNEL_PLAYER, 0);
+		digi_play_build_ii('b', 1, 1);
+		scratch._a4 = 400;
+		scratch._a0 = -1;
+		kernel.trigger_setup_mode = 1;
+		kernel_timing_trigger(1, 105);
+		player.command_ready = 0;
+		return;
+	}
+
+	if (player_parse(126, 95, 0)) {
+		global[g154] = 2;
+		player.walker_visible = 0;
+		player.commands_allowed = 0;
+		scratch._9e = kernel_run_animation_talk('b', 4, 0);
+		kernel_position_anim(scratch._9e, player.x, player.y, player.scale, player.depth);
+		kernel_synch(KERNEL_ANIM, scratch._9e, KERNEL_PLAYER, 0);
+		digi_play_build_ii('b', 1, 1);
+		scratch._a4 = 300;
+		scratch._a0 = -1;
+		kernel.trigger_setup_mode = 1;
+		kernel_timing_trigger(1, 105);
+		player.command_ready = 0;
+		return;
+	}
+
+	if (player_parse(126, 99, 0) || player_parse(78, 119, 0)) {
+		player.commands_allowed = 0;
+		global[g135] = -1;
+		scratch._8c = 3;
+		player.command_ready = 0;
+	}
 }
 
 void room_403_synchronize(Common::Serializer &s) {
 	for (int16 &v : scratch.sprite)    s.syncAsSint16LE(v);
 	for (int16 &v : scratch.sequence)  s.syncAsSint16LE(v);
 	for (int16 &v : scratch.animation) s.syncAsSint16LE(v);
+	for (AnimationInfo &ai : scratch.animation_info) ai.synchronize(s);
+	s.syncAsSint16LE(scratch._8c);
+	s.syncAsSint16LE(scratch._8e);
+	s.syncAsSint16LE(scratch._90);
+	s.syncAsSint16LE(scratch._92);
+	s.syncAsSint16LE(scratch._94);
+	s.syncAsSint16LE(scratch._96);
+	s.syncAsSint16LE(scratch._98);
+	s.syncAsSint16LE(scratch._9a);
+	s.syncAsSint16LE(scratch._9c);
+	s.syncAsSint16LE(scratch._9e);
+	s.syncAsSint16LE(scratch._a0);
+	s.syncAsSint16LE(scratch._a2);
+	s.syncAsSint16LE(scratch._a4);
+	s.syncAsSint16LE(scratch._a6);
+	s.syncAsSint16LE(scratch._a8);
 }
 
 void room_403_preload() {



From noreply at scummvm.org  Sun Jun 14 10:00:42 2026
From: noreply at scummvm.org (bluegr)
Date: Sun, 14 Jun 2026 10:00:42 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 0d527c60b8029770ffccdae3631efa7eff19b5dc
Message-ID: <20260614100042.098D17005CC@ron.scummvm.net>

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:
d4a8400a96 MOHAWK: LB - Cleanup ~4000 LoC in main LB (I).
7d5666a9f5 MOHAWK: LB - move tryOpenPage function to header
0d527c60b8 MOHAWK: LB - Cleanup ~4000 LoC in main LB (II).


Commit: d4a8400a9655b25b5d0472fd47cefd845b0e45b8
    https://github.com/scummvm/scummvm/commit/d4a8400a9655b25b5d0472fd47cefd845b0e45b8
Author: Alstruit (34786806+Alstruit at users.noreply.github.com)
Date: 2026-06-14T13:00:37+03:00

Commit Message:
MOHAWK: LB - Cleanup ~4000 LoC in main LB (I).

Move bookinfo and UI menu related into their respective files.
Move LBAnimationNode into its own file.
Move LBPage, BITL into livingbooks_page.cpp
Create livingbooks_itemtypes.cpp to handle misc item types.

Changed paths:
  A engines/mohawk/livingbooks_animation.cpp
  A engines/mohawk/livingbooks_bookinfo.cpp
  A engines/mohawk/livingbooks_itemtypes.cpp
  A engines/mohawk/livingbooks_menus.cpp
  A engines/mohawk/livingbooks_page.cpp
    engines/mohawk/livingbooks.cpp
    engines/mohawk/module.mk


diff --git a/engines/mohawk/livingbooks.cpp b/engines/mohawk/livingbooks.cpp
index 6bf80dfba0a..fdcd4f1e2bd 100644
--- a/engines/mohawk/livingbooks.cpp
+++ b/engines/mohawk/livingbooks.cpp
@@ -75,62 +75,6 @@ Common::Rect MohawkEngine_LivingBooks::readRect(Common::ReadStreamEndian *stream
 	return rect;
 }
 
-LBPage::LBPage(MohawkEngine_LivingBooks *vm) : _vm(vm) {
-	_code = nullptr;
-	_mhk = nullptr;
-
-	_baseId = 0;
-	_cascade = false;
-}
-
-void LBPage::open(Archive *mhk, uint16 baseId) {
-	_mhk = mhk;
-	_baseId = baseId;
-
-	_vm->addArchive(_mhk);
-	if (!_vm->hasResource(ID_BCOD, baseId)) {
-		// assume that BCOD is mandatory for v4/v5
-		if (_vm->getGameType() == GType_LIVINGBOOKSV4 || _vm->getGameType() == GType_LIVINGBOOKSV5)
-			error("missing BCOD resource (id %d)", baseId);
-		_code = new LBCode(_vm, 0);
-	} else {
-		_code = new LBCode(_vm, baseId);
-	}
-
-	loadBITL(baseId);
-	for (uint i = 0; i < _items.size(); i++)
-		_vm->addItem(_items[i]);
-
-	for (uint32 i = 0; i < _items.size(); i++)
-		_items[i]->init();
-
-	for (uint32 i = 0; i < _items.size(); i++)
-		_items[i]->startPhase(kLBPhaseLoad);
-}
-
-void LBPage::addClonedItem(LBItem *item) {
-	_vm->addItem(item);
-	_items.push_back(item);
-}
-
-void LBPage::itemDestroyed(LBItem *item) {
-	for (uint i = 0; i < _items.size(); i++)
-		if (item == _items[i]) {
-			_items.remove_at(i);
-			return;
-		}
-	error("itemDestroyed didn't find item");
-}
-
-LBPage::~LBPage() {
-	delete _code;
-	_vm->removeItems(_items);
-	for (uint i = 0; i < _items.size(); i++)
-		delete _items[i];
-	_vm->removeArchive(_mhk);
-	delete _mhk;
-}
-
 MohawkEngine_LivingBooks::MohawkEngine_LivingBooks(OSystem *syst, const MohawkGameDescription *gamedesc) : MohawkEngine(syst, gamedesc) {
 	DebugMan.addDebugChannel(kDebugCode, "Code", "Track Script Execution");
 
@@ -305,65 +249,6 @@ void MohawkEngine_LivingBooks::pauseEngineIntern(bool pause) {
 	}
 }
 
-void MohawkEngine_LivingBooks::loadBookInfo(const Common::Path &filename) {
-	_bookInfoFile.allowNonEnglishCharacters();
-	if (!_bookInfoFile.loadFromFile(filename))
-		error("Could not open %s as a config file", filename.toString().c_str());
-
-	_title = getStringFromConfig("BookInfo", "title");
-	_copyright = getStringFromConfig("BookInfo", "copyright");
-
-	_numPages = getIntFromConfig("BookInfo", "nPages");
-	_numLanguages = getIntFromConfig("BookInfo", "nLanguages");
-	_screenWidth = getIntFromConfig("BookInfo", "xRes");
-	_screenHeight = getIntFromConfig("BookInfo", "yRes");
-	// nColors is here too, but it's always 256 anyway...
-
-	// this is 1 in The New Kid on the Block, changes the hardcoded UI
-	// v2 games changed the flag name to fPoetry
-	if (getGameType() == GType_LIVINGBOOKSV1)
-		_poetryMode = (getIntFromConfig("BookInfo", "poetry") == 1);
-	else
-		_poetryMode = (getIntFromConfig("BookInfo", "fPoetry") == 1);
-
-	// The later Living Books games add some more options:
-	//     - fNeedPalette                (always true?)
-	//     - fUse254ColorPalette         (always true?)
-	//     - nKBRequired                 (4096, RAM requirement?)
-	//     - fDebugWindow                (always 0?)
-
-	if (_bookInfoFile.hasSection("Globals")) {
-		const Common::INIFile::SectionKeyList globals = _bookInfoFile.getKeys("Globals");
-		for (Common::INIFile::SectionKeyList::const_iterator i = globals.begin(); i != globals.end(); i++) {
-			Common::String command = Common::String::format("%s = %s", i->key.c_str(), i->value.c_str());
-			LBCode tempCode(this, 0);
-			uint offset = tempCode.parseCode(command);
-			tempCode.runCode(nullptr, offset);
-		}
-	}
-}
-
-Common::String MohawkEngine_LivingBooks::stringForMode(LBMode mode) {
-	Common::String language = getStringFromConfig("Languages", Common::String::format("Language%d", _curLanguage));
-
-	switch (mode) {
-	case kLBIntroMode:
-		return "Intro";
-	case kLBControlMode:
-		return "Control";
-	case kLBCreditsMode:
-		return "Credits";
-	case kLBPreviewMode:
-		return "Preview";
-	case kLBReadMode:
-		return language + ".Read";
-	case kLBPlayMode:
-		return language + ".Play";
-	default:
-		error("unknown game mode %d", (int)mode);
-	}
-}
-
 void MohawkEngine_LivingBooks::destroyPage() {
 	_sound->stopSound();
 	_lastSoundOwner = 0;
@@ -798,169 +683,11 @@ void MohawkEngine_LivingBooks::lockSound(LBItem *owner, bool lock) {
 	}
 }
 
-// Only 1 VSRN resource per page
-uint16 LBPage::getResourceVersion() {
-	Common::SeekableReadStream *versionStream = _vm->getResource(ID_VRSN, _baseId);
-
-	// FIXME: some V2 games have very strange version entries
-	if (versionStream->size() != 2)
-		debug(1, "Version Record size mismatch");
-
-	uint16 version = versionStream->readUint16BE();
-
-	delete versionStream;
-	return version;
-}
-
-void LBPage::loadBITL(uint16 resourceId) {
-	Common::SeekableReadStreamEndian *bitlStream = _vm->wrapStreamEndian(ID_BITL, resourceId);
-
-	while (true) {
-		Common::Rect rect = _vm->readRect(bitlStream);
-		uint16 type = bitlStream->readUint16();
-
-		LBItem *res;
-		switch (type) {
-		case kLBPictureItem:
-			res = new LBPictureItem(_vm, this, rect);
-			break;
-		case kLBAnimationItem:
-			res = new LBAnimationItem(_vm, this, rect);
-			break;
-		case kLBPaletteItem:
-			res = new LBPaletteItem(_vm, this, rect);
-			break;
-		case kLBGroupItem:
-			res = new LBGroupItem(_vm, this, rect);
-			break;
-		case kLBSoundItem:
-			res = new LBSoundItem(_vm, this, rect);
-			break;
-		case kLBLiveTextItem:
-			res = new LBLiveTextItem(_vm, this, rect);
-			break;
-		case kLBMovieItem:
-			res = new LBMovieItem(_vm, this, rect);
-			break;
-		case kLBMiniGameItem:
-			res = new LBMiniGameItem(_vm, this, rect);
-			break;
-		case kLBProxyItem:
-			res = new LBProxyItem(_vm, this, rect);
-			break;
-		default:
-			warning("Unknown item type %04x", type);
-			// fall through
-		case 3: // often used for buttons
-			res = new LBItem(_vm, this, rect);
-			break;
-		}
-
-		res->readFrom(bitlStream);
-		_items.push_back(res);
-
-		if (bitlStream->size() == bitlStream->pos())
-			break;
-	}
-
-	delete bitlStream;
-}
-
 Common::SeekableReadStreamEndian *MohawkEngine_LivingBooks::wrapStreamEndian(uint32 tag, uint16 id) {
 	Common::SeekableReadStream *dataStream = getResource(tag, id);
 	return new Common::SeekableReadStreamEndianWrapper(dataStream, isBigEndian(), DisposeAfterUse::YES);
 }
 
-Common::String MohawkEngine_LivingBooks::getStringFromConfig(const Common::String &section, const Common::String &key) {
-	Common::String x, leftover;
-	_bookInfoFile.getKey(key, section, x);
-	Common::String tmp = removeQuotesFromString(x, leftover);
-	if (!leftover.empty())
-		warning("while parsing config key '%s' from section '%s', string '%s' was left after '%s'",
-			key.c_str(), section.c_str(), leftover.c_str(), tmp.c_str());
-	return tmp;
-}
-
-Common::String MohawkEngine_LivingBooks::getStringFromConfig(const Common::String &section, const Common::String &key, Common::String &leftover) {
-	Common::String x;
-	_bookInfoFile.getKey(key, section, x);
-	return removeQuotesFromString(x, leftover);
-}
-
-int MohawkEngine_LivingBooks::getIntFromConfig(const Common::String &section, const Common::String &key) {
-	return atoi(getStringFromConfig(section, key).c_str());
-}
-
-Common::String MohawkEngine_LivingBooks::getFileNameFromConfig(const Common::String &section, const Common::String &key, Common::String &leftover) {
-	Common::String string = getStringFromConfig(section, key, leftover);
-
-	if (string.hasPrefix("//")) {
-		// skip "//CD-ROM Title/" prefixes which we don't care about
-		uint i = 3;
-		while (i < string.size() && string[i - 1] != '/')
-			i++;
-
-		// Already uses slashes, no need to convert further
-		return string.c_str() + i;
-	}
-
-	return (getPlatform() == Common::kPlatformMacintosh) ? convertMacFileName(string) : convertWinFileName(string);
-}
-
-Common::String MohawkEngine_LivingBooks::removeQuotesFromString(const Common::String &string, Common::String &leftover) {
-	if (string.empty())
-		return string;
-
-	char quoteChar = string[0];
-	if (quoteChar != '\"' && quoteChar != '\'')
-		return string;
-
-	Common::String tmp;
-	bool inLeftover = false;
-	for (uint32 i = 1; i < string.size(); i++) {
-		if (inLeftover)
-			leftover += string[i];
-		else if (string[i] == quoteChar)
-			inLeftover = true;
-		else
-			tmp += string[i];
-	}
-
-	return tmp;
-}
-
-Common::String MohawkEngine_LivingBooks::convertMacFileName(const Common::String &string) {
-	Common::String filename;
-
-	for (uint32 i = 0; i < string.size(); i++) {
-		if (i == 0 && string[i] == ':') // First character should be ignored (another colon)
-			continue;
-		if (string[i] == ':') // Directory separator
-			filename += '/';
-		else if (string[i] == '/') // Literal slash
-			filename += ':'; // Replace by colon, as used by macOS for slash
-		else
-			filename += string[i];
-	}
-
-	return filename;
-}
-
-Common::String MohawkEngine_LivingBooks::convertWinFileName(const Common::String &string) {
-	Common::String filename;
-
-	for (uint32 i = 0; i < string.size(); i++) {
-		if (i == 0 && (string[i] == '/' || string[i] == '\\')) // ignore slashes at start
-			continue;
-		if (string[i] == '\\')
-			filename += '/';
-		else
-			filename += string[i];
-	}
-
-	return filename;
-}
-
 Archive *MohawkEngine_LivingBooks::createArchive() const {
 	if (isPreMohawk())
 		return new LivingBooksArchive_v1();
@@ -1032,285 +759,6 @@ void MohawkEngine_LivingBooks::nextPage() {
 	error("Could not find page after %d.%d for mode %d", _curPage, _curSubPage, (int)_curMode);
 }
 
-void MohawkEngine_LivingBooks::handleUIMenuClick(uint controlId) {
-	LBItem *item;
-
-	switch (controlId) {
-	case 1:
-		if (getFeatures() & GF_LB_10) {
-			if (!tryLoadPageStart(kLBControlMode, 2))
-				error("couldn't load options page");
-		} else {
-			if (!tryLoadPageStart(kLBControlMode, 3))
-				error("couldn't load options page");
-		}
-		break;
-
-	case 2:
-		item = getItemById(10);
-		if (item)
-			item->destroySelf();
-		item = getItemById(11);
-		if (item)
-			item->destroySelf();
-		item = getItemById(199 + _curLanguage);
-		if (item) {
-			item->setVisible(true);
-			item->togglePlaying(false, true);
-		}
-		break;
-
-	case 3:
-		item = getItemById(10);
-		if (item)
-			item->destroySelf();
-		item = getItemById(11);
-		if (item)
-			item->destroySelf();
-		item = getItemById(12);
-		if (item) {
-			item->setVisible(true);
-			item->togglePlaying(false, true);
-		}
-		break;
-
-	case 4:
-		if (getFeatures() & GF_LB_10) {
-			if (!tryLoadPageStart(kLBControlMode, 3))
-				error("couldn't load quit page");
-		} else {
-			if (!tryLoadPageStart(kLBControlMode, 2))
-				error("couldn't load quit page");
-		}
-		break;
-
-	case 10:
-		item = getItemById(10);
-		if (item)
-			item->destroySelf();
-		item = getItemById(11);
-		if (item) {
-			item->setVisible(true);
-			item->togglePlaying(false);
-		}
-		break;
-
-	case 11:
-		item = getItemById(11);
-		if (item)
-			item->togglePlaying(false, true);
-		break;
-
-	case 12:
-		// start game, in play mode
-		if (!tryLoadPageStart(kLBPlayMode, 1))
-			error("couldn't start play mode");
-		break;
-
-	default:
-		if (controlId >= 100 && controlId < 100 + (uint)_numLanguages) {
-			uint newLanguage = controlId - 99;
-			if (newLanguage == _curLanguage)
-				break;
-			item = getItemById(99 + _curLanguage);
-			if (item)
-				item->seek(1);
-			_curLanguage = newLanguage;
-		} else if (controlId >= 200 && controlId < 200 + (uint)_numLanguages) {
-			// start game, in read mode
-			if (!tryLoadPageStart(kLBReadMode, 1))
-				error("couldn't start read mode");
-		}
-		break;
-	}
-}
-
-void MohawkEngine_LivingBooks::handleUIPoetryMenuClick(uint controlId) {
-	LBItem *item;
-
-	// the menu UI in New Kid on the Block is a hybrid of the normal menu
-	// and the normal options screen
-
-	// TODO: this is mostly untested
-
-	switch (controlId) {
-	case 2:
-	case 3:
-		handleUIOptionsClick(controlId);
-		break;
-
-	case 4:
-		handleUIMenuClick(controlId);
-		break;
-
-	case 6:
-		handleUIMenuClick(2);
-		break;
-
-	case 7:
-		item = getItemById(10);
-		if (item)
-			item->destroySelf();
-		item = getItemById(11);
-		if (item)
-			item->destroySelf();
-		item = getItemById(12);
-		if (item) {
-			item->setVisible(true);
-			item->togglePlaying(false, true);
-		}
-		break;
-
-	case 0xA:
-		item = getItemById(10);
-		if (item)
-			item->destroySelf();
-		item = getItemById(11);
-		if (item) {
-			item->setVisible(true);
-			item->togglePlaying(false);
-		}
-		break;
-
-	case 0xB:
-		item = getItemById(11);
-		if (item)
-			item->togglePlaying(false, true);
-		break;
-
-	case 0xC:
-		if (!tryLoadPageStart(kLBPlayMode, _curSelectedPage))
-			error("failed to load page %d", _curSelectedPage);
-		break;
-
-	default:
-		if (controlId < 100) {
-			handleUIMenuClick(controlId);
-		} else {
-			if (!tryLoadPageStart(kLBReadMode, _curSelectedPage))
-				error("failed to load page %d", _curSelectedPage);
-		}
-	}
-}
-
-void MohawkEngine_LivingBooks::handleUIQuitClick(uint controlId) {
-	LBItem *item;
-
-	switch (controlId) {
-	case 1:
-	case 2:
-		// button clicked, run animation
-		item = getItemById(10);
-		if (item)
-			item->destroySelf();
-		item = getItemById(11);
-		if (item)
-			item->destroySelf();
-		item = getItemById((controlId == 1) ? 12 : 13);
-		if (item) {
-			item->setVisible(true);
-			item->togglePlaying(false);
-		}
-		break;
-
-	case 10:
-	case 11:
-		item = getItemById(11);
-		if (item)
-			item->togglePlaying(false, true);
-		break;
-
-	case 12:
-		// 'yes', I want to quit
-		quitGame();
-		break;
-
-	case 13:
-		// 'no', go back to menu
-		if (!tryLoadPageStart(kLBControlMode, 1))
-			error("couldn't return to menu");
-		break;
-
-	default:
-		break;
-	}
-}
-
-void MohawkEngine_LivingBooks::handleUIOptionsClick(uint controlId) {
-	LBItem *item;
-
-	switch (controlId) {
-	case 1:
-		item = getItemById(10);
-		if (item)
-			item->destroySelf();
-		item = getItemById(202);
-		if (item) {
-			item->setVisible(true);
-			item->togglePlaying(false, true);
-		}
-		break;
-
-	case 2:
-		// back
-		item = getItemById(2);
-		if (item)
-			item->seek(1);
-		if (_curSelectedPage == 1) {
-			_curSelectedPage = _numPages;
-		} else {
-			_curSelectedPage--;
-		}
-		for (uint i = 0; i < _numPages; i++) {
-			item = getItemById(1000 + i);
-			if (item)
-				item->setVisible(_curSelectedPage == i + 1);
-			item = getItemById(1100 + i);
-			if (item)
-				item->setVisible(_curSelectedPage == i + 1);
-		}
-		break;
-
-	case 3:
-		// forward
-		item = getItemById(3);
-		if (item)
-			item->seek(1);
-		if (_curSelectedPage == _numPages) {
-			_curSelectedPage = 1;
-		} else {
-			_curSelectedPage++;
-		}
-		for (uint i = 0; i < _numPages; i++) {
-			item = getItemById(1000 + i);
-			if (item)
-				item->setVisible(_curSelectedPage == i + 1);
-			item = getItemById(1100 + i);
-			if (item)
-				item->setVisible(_curSelectedPage == i + 1);
-		}
-		break;
-
-	case 4:
-		if (!tryLoadPageStart(kLBCreditsMode, 1))
-			error("failed to start credits");
-		break;
-
-	case 5:
-		if (!tryLoadPageStart(kLBPreviewMode, 1))
-			error("failed to start preview");
-		break;
-
-	case 202:
-		if (!tryLoadPageStart(kLBPlayMode, _curSelectedPage))
-			error("failed to load page %d", _curSelectedPage);
-		break;
-
-	default:
-		break;
-	}
-}
-
 void MohawkEngine_LivingBooks::handleNotify(NotifyEvent &event) {
 	// hard-coded behavior (GUI/navigation)
 
@@ -1469,610 +917,49 @@ void MohawkEngine_LivingBooks::handleNotify(NotifyEvent &event) {
 	}
 }
 
-LBAnimationNode::LBAnimationNode(MohawkEngine_LivingBooks *vm, LBAnimation *parent, uint16 scriptResourceId) : _vm(vm), _parent(parent) {
-	loadScript(scriptResourceId);
-}
-
-LBAnimationNode::~LBAnimationNode() {
-	for (uint32 i = 0; i < _scriptEntries.size(); i++)
-		delete[] _scriptEntries[i].data;
+LBScriptEntry::LBScriptEntry() {
+	state = 0;
+	data = nullptr;
+	argvParam = nullptr;
+	argvTarget = nullptr;
 }
 
-void LBAnimationNode::loadScript(uint16 resourceId) {
-	Common::SeekableReadStreamEndian *scriptStream = _vm->wrapStreamEndian(ID_SCRP, resourceId);
-
-	reset();
-
-	while (byte opcodeId = scriptStream->readByte()) {
-		byte size = scriptStream->readByte();
-
-		LBAnimScriptEntry entry;
-		entry.opcode = opcodeId;
-		entry.size = size;
-
-		if (!size) {
-			entry.data = nullptr;
-		} else {
-			entry.data = new byte[entry.size];
-			scriptStream->read(entry.data, entry.size);
-		}
-
-		_scriptEntries.push_back(entry);
-	}
-
-	byte size = scriptStream->readByte();
-	if (size != 0 || scriptStream->pos() != scriptStream->size())
-		error("Failed to read script correctly");
+LBScriptEntry::~LBScriptEntry() {
+	delete[] argvParam;
+	delete[] argvTarget;
+	delete[] data;
 
-	delete scriptStream;
+	for (uint i = 0; i < subentries.size(); i++)
+		delete subentries[i];
 }
 
-void LBAnimationNode::draw(const Common::Rect &_bounds) {
-	if (!_currentCel)
-		return;
-
-	// this is also checked in SetCel, below
-	if (_currentCel > _parent->getNumResources())
-		error("Animation cel %d was too high, this shouldn't happen!", _currentCel);
-
-	int16 xOffset = _xPos + _bounds.left;
-	int16 yOffset = _yPos + _bounds.top;
+LBItem::LBItem(MohawkEngine_LivingBooks *vm, LBPage *page, Common::Rect rect) : _vm(vm), _page(page), _rect(rect) {
+	if (_vm->getGameType() == GType_LIVINGBOOKSV1 || _vm->getGameType() == GType_LIVINGBOOKSV2)
+		_phase = kLBPhaseInit;
+	else
+		_phase = kLBPhaseLoad;
 
-	uint16 resourceId = _parent->getResource(_currentCel - 1);
+	_loopMode = 0;
+	_delayMin = 0;
+	_delayMax = 0;
+	_timingMode = kLBAutoNone;
+	_periodMin = 0;
+	_periodMax = 0;
+	_controlMode = kLBControlNone;
+	_soundMode = 0;
 
-	if (!_vm->isPreMohawk()) {
-		Common::Point offset = _parent->getOffset(_currentCel - 1);
-		xOffset -= offset.x;
-		yOffset -= offset.y;
-	}
+	_loaded = false;
+	_enabled = false;
+	_visible = true;
+	_playing = false;
+	_globalEnabled = true;
+	_globalVisible = true;
+	_nextTime = 0;
+	_startTime = 0;
+	_loops = 0;
 
-	_vm->_gfx->copyOffsetAnimImageToScreen(resourceId, xOffset, yOffset);
-}
-
-void LBAnimationNode::reset() {
-	// TODO: this causes stupid flickering
-	//if (_currentCel)
-	//	_vm->_needsRedraw = true;
-
-	_currentCel = 0;
-	_currentEntry = 0;
-	_delay = 0;
-
-	_xPos = 0;
-	_yPos = 0;
-}
-
-NodeState LBAnimationNode::update(bool seeking) {
-	if (_currentEntry == _scriptEntries.size())
-		return kLBNodeDone;
-
-	if (_delay > 0 && --_delay)
-		return kLBNodeRunning;
-
-	while (_currentEntry < _scriptEntries.size()) {
-		LBAnimScriptEntry &entry = _scriptEntries[_currentEntry];
-		_currentEntry++;
-		debug(5, "Running script entry %d of %d", _currentEntry, _scriptEntries.size());
-
-		switch (entry.opcode) {
-		case kLBAnimOpPlaySound:
-		case kLBAnimOpWaitForSound:
-		case kLBAnimOpReleaseSound:
-		case kLBAnimOpResetSound:
-			{
-			uint16 soundResourceId = READ_BE_UINT16(entry.data);
-
-			if (!soundResourceId) {
-				error("Unhandled named wave file, tell clone2727 where you found this");
-				break;
-			}
-
-			Common::String cue;
-			uint pos = 2;
-			while (pos < entry.size) {
-				char in = entry.data[pos];
-				if (!in)
-					break;
-				pos++;
-				cue += in;
-			}
-			if (pos == entry.size)
-				error("Cue in sound kLBAnimOp wasn't null-terminated");
-
-			switch (entry.opcode) {
-			case kLBAnimOpPlaySound:
-				if (seeking)
-					break;
-				debug(4, "a: PlaySound(%0d)", soundResourceId);
-				_parent->playSound(soundResourceId);
-				break;
-			case kLBAnimOpWaitForSound:
-				if (seeking)
-					break;
-				debug(4, "b: WaitForSound(%0d)", soundResourceId);
-				if (!_parent->soundPlaying(soundResourceId, cue))
-					break;
-				_currentEntry--;
-				return kLBNodeWaiting;
-			case kLBAnimOpReleaseSound:
-				debug(4, "c: ReleaseSound(%0d)", soundResourceId);
-				// TODO
-				_vm->_sound->stopSound(soundResourceId);
-				break;
-			case kLBAnimOpResetSound:
-				debug(4, "d: ResetSound(%0d)", soundResourceId);
-				// TODO
-				_vm->_sound->stopSound(soundResourceId);
-				break;
-			default:
-				break;
-			}
-			}
-			break;
-
-		case kLBAnimOpSetTempo:
-		case kLBAnimOpSetTempoDiv:
-			{
-			assert(entry.size == 2);
-			uint16 tempo = (int16)READ_BE_UINT16(entry.data);
-
-			// TODO: LB 3 uses fixed-point here.
-			if (entry.opcode == kLBAnimOpSetTempo) {
-				debug(4, "3: SetTempo(%d)", tempo);
-				// TODO: LB 3 uses (tempo * 1000) / 60, while
-				// the original divides the system time by 16.
-				_parent->setTempo(tempo * 16);
-			} else {
-				// LB 3.0+ only.
-				debug(4, "E: SetTempoDiv(%d)", tempo);
-				_parent->setTempo(1000 / tempo);
-			}
-
-			}
-			break;
-
-		case kLBAnimOpWait:
-			assert(entry.size == 0);
-			debug(5, "6: Wait()");
-			return kLBNodeRunning;
-
-		case kLBAnimOpMoveTo:
-			{
-			assert(entry.size == 4);
-			int16 x = (int16)READ_BE_UINT16(entry.data);
-			int16 y = (int16)READ_BE_UINT16(entry.data + 2);
-			debug(4, "5: MoveTo(%d, %d)", x, y);
-
-			_xPos = x;
-			_yPos = y;
-			_vm->_needsRedraw = true;
-			}
-			break;
-
-		case kLBAnimOpDrawMode:
-			{
-			assert(entry.size == 2);
-			uint16 mode = (int16)READ_BE_UINT16(entry.data);
-			debug(4, "9: DrawMode(%d)", mode);
-
-			// TODO
-			}
-			break;
-
-		case kLBAnimOpSetCel:
-			{
-			assert(entry.size == 2);
-			uint16 cel = (int16)READ_BE_UINT16(entry.data);
-			debug(4, "7: SetCel(%d)", cel);
-
-			_currentCel = cel;
-			if (_currentCel > _parent->getNumResources())
-				error("SetCel set current cel to %d, but we only have %d cels", _currentCel, _parent->getNumResources());
-			_vm->_needsRedraw = true;
-			}
-			break;
-
-		case kLBAnimOpNotify:
-			{
-			assert(entry.size == 2);
-			uint16 data = (int16)READ_BE_UINT16(entry.data);
-
-			if (seeking)
-				break;
-
-			debug(4, "2: Notify(%d)", data);
-			_vm->notifyAll(data, _parent->getParentId());
-			}
-			break;
-
-		case kLBAnimOpSleepUntil:
-			{
-			assert(entry.size == 4);
-			uint32 frame = READ_BE_UINT32(entry.data);
-			debug(4, "8: SleepUntil(%d)", frame);
-
-			if (frame > _parent->getCurrentFrame()) {
-				// *not* kLBNodeWaiting
-				_currentEntry--;
-				return kLBNodeRunning;
-			}
-			}
-			break;
-
-		case kLBAnimOpDelay:
-			{
-			assert(entry.size == 4);
-			uint32 delay = READ_BE_UINT32(entry.data);
-			debug(4, "f: Delay(%d)", delay);
-			_delay = delay;
-			return kLBNodeRunning;
-			}
-			break;
-
-		default:
-			error("Unknown opcode id %02x (size %d)", entry.opcode, entry.size);
-			break;
-		}
-	}
-
-	return kLBNodeRunning;
-}
-
-bool LBAnimationNode::transparentAt(int x, int y) {
-	if (!_currentCel)
-		return true;
-
-	uint16 resourceId = _parent->getResource(_currentCel - 1);
-
-	if (!_vm->isPreMohawk()) {
-		Common::Point offset = _parent->getOffset(_currentCel - 1);
-		x += offset.x;
-		y += offset.y;
-	}
-
-	// TODO: only check pixels if necessary
-	return _vm->_gfx->imageIsTransparentAt(resourceId, true, x - _xPos, y - _yPos);
-}
-
-LBAnimation::LBAnimation(MohawkEngine_LivingBooks *vm, LBAnimationItem *parent, uint16 resourceId) : _vm(vm), _parent(parent) {
-	Common::SeekableReadStreamEndian *aniStream = _vm->wrapStreamEndian(ID_ANI, resourceId);
-
-	// ANI records in the Wanderful sampler are 32 bytes, extra bytes are just NULs
-	if (aniStream->size() != 30 && aniStream->size() != 32)
-		warning("ANI Record size mismatch");
-
-	uint16 version = aniStream->readUint16();
-	if (version != 1)
-		warning("ANI version not 1");
-
-	_bounds = _vm->readRect(aniStream);
-	_clip = _vm->readRect(aniStream);
-	// TODO: what is colorId for?
-	uint32 colorId = aniStream->readUint32();
-	uint32 sprResourceId = aniStream->readUint32();
-	uint32 sprResourceOffset = aniStream->readUint32();
-
-	debug(5, "ANI bounds: (%d, %d), (%d, %d)", _bounds.left, _bounds.top, _bounds.right, _bounds.bottom);
-	debug(5, "ANI clip: (%d, %d), (%d, %d)", _clip.left, _clip.top, _clip.right, _clip.bottom);
-	debug(5, "ANI color id: %d", colorId);
-	debug(5, "ANI SPRResourceId: %d, offset %d", sprResourceId, sprResourceOffset);
-	if (aniStream->size() == 32) {
-		debug(5, "ANI extra bytes: (%d)", aniStream->readUint16());
-	}
-
-	if (aniStream->pos() != aniStream->size())
-		error("Still %d bytes at the end of anim stream", (int)(aniStream->size() - aniStream->pos()));
-
-	delete aniStream;
-
-	if (sprResourceOffset)
-		error("Cannot handle non-zero ANI offset yet");
-
-	Common::SeekableReadStreamEndian *sprStream = _vm->wrapStreamEndian(ID_SPR, sprResourceId);
-
-	uint16 numBackNodes = sprStream->readUint16();
-	uint16 numFrontNodes = sprStream->readUint16();
-	uint32 shapeResourceID = sprStream->readUint32();
-	uint32 shapeResourceOffset = sprStream->readUint32();
-	uint32 scriptResourceID = sprStream->readUint32();
-	uint32 scriptResourceOffset = sprStream->readUint32();
-	uint32 scriptResourceLength = sprStream->readUint32();
-	debug(5, "SPR# stream: %d front, %d background", numFrontNodes, numBackNodes);
-	debug(5, "Shape ID %d (offset 0x%04x), script ID %d (offset 0x%04x, length %d)", shapeResourceID, shapeResourceOffset,
-		scriptResourceID, scriptResourceOffset, scriptResourceLength);
-
-	Common::Array<uint16> scriptIDs;
-	for (uint16 i = 0; i < numFrontNodes; i++) {
-		uint32 unknown1 = sprStream->readUint32();
-		uint32 unknown2 = sprStream->readUint32();
-		uint32 unknown3 = sprStream->readUint32();
-		uint16 scriptID = sprStream->readUint32();
-		uint32 unknown4 = sprStream->readUint32();
-		uint32 unknown5 = sprStream->readUint32();
-		scriptIDs.push_back(scriptID);
-		debug(6, "Front node %d: script ID %d", i, scriptID);
-		if (unknown1 != 0 || unknown2 != 0 || unknown3 != 0 || unknown4 != 0 || unknown5 != 0)
-			error("Anim node %d had non-zero unknowns %08x, %08x, %08x, %08x, %08x",
-				i, unknown1, unknown2, unknown3, unknown4, unknown5);
-	}
-
-	if (numBackNodes)
-		error("Ignoring %d back nodes", numBackNodes);
-
-	if (sprStream->pos() != sprStream->size())
-		error("Still %d bytes at the end of sprite stream", (int)(sprStream->size() - sprStream->pos()));
-
-	delete sprStream;
-
-	loadShape(shapeResourceID);
-
-	_nodes.push_back(new LBAnimationNode(_vm, this, scriptResourceID));
-	for (uint16 i = 0; i < scriptIDs.size(); i++)
-		_nodes.push_back(new LBAnimationNode(_vm, this, scriptIDs[i]));
-
-	_currentFrame = 0;
-	_currentSound = 0xffff;
-	_running = false;
-	_tempo = 1;
-}
-
-LBAnimation::~LBAnimation() {
-	for (uint32 i = 0; i < _nodes.size(); i++)
-		delete _nodes[i];
-	if (_currentSound != 0xffff)
-		_vm->_sound->stopSound(_currentSound);
-}
-
-void LBAnimation::loadShape(uint16 resourceId) {
-	if (resourceId == 0)
-		return;
-
-	Common::SeekableReadStreamEndian *shapeStream = _vm->wrapStreamEndian(ID_SHP, resourceId);
-
-	if (_vm->isPreMohawk()) {
-		if (shapeStream->size() < 6)
-			error("V1 SHP Record size too short (%d)", (int)shapeStream->size());
-
-		uint16 u0 = shapeStream->readUint16();
-		if (u0 != 3)
-			error("V1 SHP Record u0 is %04x, not 3", u0);
-
-		uint16 u1 = shapeStream->readUint16();
-		if (u1 != 0)
-			error("V1 SHP Record u1 is %04x, not 0", u1);
-
-		uint16 idCount = shapeStream->readUint16();
-		debug(8, "V1 SHP: idCount: %d", idCount);
-
-		if (shapeStream->size() != (idCount * 2) + 6)
-			error("V1 SHP Record size mismatch (%d)", (int)shapeStream->size());
-
-		for (uint16 i = 0; i < idCount; i++) {
-			_shapeResources.push_back(shapeStream->readUint16());
-			debug(8, "V1 SHP: BMAP Resource Id %d: %d", i, _shapeResources[i]);
-		}
-	} else {
-		uint16 idCount = shapeStream->readUint16();
-		debug(8, "SHP: idCount: %d", idCount);
-
-		if (shapeStream->size() != (idCount * 6) + 2)
-			error("SHP Record size mismatch (%d)", (int)shapeStream->size());
-
-		for (uint16 i = 0; i < idCount; i++) {
-			_shapeResources.push_back(shapeStream->readUint16());
-			int16 x = shapeStream->readSint16();
-			int16 y = shapeStream->readSint16();
-			_shapeOffsets.push_back(Common::Point(x, y));
-			debug(8, "SHP: tBMP Resource Id %d: %d, at (%d, %d)", i, _shapeResources[i], x, y);
-		}
-	}
-
-	for (uint16 i = 0; i < _shapeResources.size(); i++)
-		_vm->_gfx->preloadImage(_shapeResources[i]);
-
-	delete shapeStream;
-}
-
-void LBAnimation::draw() {
-	for (uint32 i = 0; i < _nodes.size(); i++)
-		_nodes[i]->draw(_bounds);
-}
-
-bool LBAnimation::update() {
-	if (!_running)
-		return false;
-
-	if (_vm->_system->getMillis() <= _lastTime + (uint32)_tempo)
-		return false;
-
-	// the second check is to try 'catching up' with lagged animations, might be crazy
-	if (_lastTime == 0 || (_vm->_system->getMillis()) > _lastTime + (uint32)(_tempo * 2))
-		_lastTime = _vm->_system->getMillis();
-	else
-		_lastTime += _tempo;
-
-	if (_currentSound != 0xffff && !_vm->_sound->isPlaying(_currentSound)) {
-		_currentSound = 0xffff;
-	}
-
-	NodeState state = kLBNodeDone;
-	for (uint32 i = 0; i < _nodes.size(); i++) {
-		NodeState s = _nodes[i]->update();
-		if (s == kLBNodeWaiting) {
-			state = kLBNodeWaiting;
-			if (i != 0)
-				warning("non-primary node was waiting");
-			break;
-		}
-		if (s == kLBNodeRunning)
-			state = kLBNodeRunning;
-	}
-
-	if (state == kLBNodeRunning) {
-		_currentFrame++;
-	} else if (state == kLBNodeDone) {
-		if (_currentSound == 0xffff) {
-			_running = false;
-			return true;
-		}
-	}
-
-	return false;
-}
-
-void LBAnimation::start() {
-	_lastTime = 0;
-	_running = true;
-}
-
-void LBAnimation::seek(uint16 pos) {
-	_lastTime = 0;
-	_currentFrame = 0;
-
-	if (_currentSound != 0xffff) {
-		_vm->_sound->stopSound(_currentSound);
-		_currentSound = 0xffff;
-	}
-
-	for (uint32 i = 0; i < _nodes.size(); i++)
-		_nodes[i]->reset();
-
-	for (uint16 n = 0; n < pos; n++) {
-		bool ranSomething = false;
-		// nodes don't wait while seeking
-		for (uint32 i = 0; i < _nodes.size(); i++)
-			ranSomething |= (_nodes[i]->update(true) != kLBNodeDone);
-
-		_currentFrame++;
-
-		if (!ranSomething) {
-			_running = false;
-			break;
-		}
-	}
-}
-
-void LBAnimation::seekToTime(uint32 time) {
-	_lastTime = 0;
-	_currentFrame = 0;
-
-	if (_currentSound != 0xffff) {
-		_vm->_sound->stopSound(_currentSound);
-		_currentSound = 0xffff;
-	}
-
-	for (uint32 i = 0; i < _nodes.size(); i++)
-		_nodes[i]->reset();
-
-	uint32 elapsed = 0;
-	while (elapsed <= time) {
-		bool ranSomething = false;
-		// nodes don't wait while seeking
-		for (uint32 i = 0; i < _nodes.size(); i++)
-			ranSomething |= (_nodes[i]->update(true) != kLBNodeDone);
-
-		elapsed += _tempo;
-		_currentFrame++;
-
-		if (!ranSomething) {
-			_running = false;
-			break;
-		}
-	}
-}
-
-void LBAnimation::stop() {
-	_running = false;
-	if (_currentSound != 0xffff) {
-		_vm->_sound->stopSound(_currentSound);
-		_currentSound = 0xffff;
-	}
-}
-
-void LBAnimation::playSound(uint16 resourceId) {
-	_currentSound = resourceId;
-	_vm->_sound->playSound(_currentSound, Audio::Mixer::kMaxChannelVolume, false, &_cueList);
-}
-
-bool LBAnimation::soundPlaying(uint16 resourceId, const Common::String &cue) {
-	if (_currentSound != resourceId)
-		return false;
-	if (!_vm->_sound->isPlaying(_currentSound))
-		return false;
-
-	if (cue.empty())
-		return true;
-
-	uint samples = _vm->_sound->getNumSamplesPlayed(_currentSound);
-	for (uint i = 0; i < _cueList.pointCount; i++) {
-		if (_cueList.points[i].sampleFrame > samples)
-			break;
-		if (_cueList.points[i].name == cue)
-			return false;
-	}
-
-	return true;
-}
-
-bool LBAnimation::transparentAt(int x, int y) {
-	for (uint32 i = 0; i < _nodes.size(); i++)
-		if (!_nodes[i]->transparentAt(x - _bounds.left, y - _bounds.top))
-			return false;
-
-	return true;
-}
-
-void LBAnimation::setTempo(uint16 tempo) {
-	_tempo = tempo;
-}
-
-uint16 LBAnimation::getParentId() {
-	return _parent->getId();
-}
-
-LBScriptEntry::LBScriptEntry() {
-	state = 0;
-	data = nullptr;
-	argvParam = nullptr;
-	argvTarget = nullptr;
-}
-
-LBScriptEntry::~LBScriptEntry() {
-	delete[] argvParam;
-	delete[] argvTarget;
-	delete[] data;
-
-	for (uint i = 0; i < subentries.size(); i++)
-		delete subentries[i];
-}
-
-LBItem::LBItem(MohawkEngine_LivingBooks *vm, LBPage *page, Common::Rect rect) : _vm(vm), _page(page), _rect(rect) {
-	if (_vm->getGameType() == GType_LIVINGBOOKSV1 || _vm->getGameType() == GType_LIVINGBOOKSV2)
-		_phase = kLBPhaseInit;
-	else
-		_phase = kLBPhaseLoad;
-
-	_loopMode = 0;
-	_delayMin = 0;
-	_delayMax = 0;
-	_timingMode = kLBAutoNone;
-	_periodMin = 0;
-	_periodMax = 0;
-	_controlMode = kLBControlNone;
-	_soundMode = 0;
-
-	_loaded = false;
-	_enabled = false;
-	_visible = true;
-	_playing = false;
-	_globalEnabled = true;
-	_globalVisible = true;
-	_nextTime = 0;
-	_startTime = 0;
-	_loops = 0;
-
-	_isAmbient = false;
-	_doHitTest = true;
+	_isAmbient = false;
+	_doHitTest = true;
 }
 
 LBItem::~LBItem() {
@@ -3146,857 +2033,4 @@ bool LBItem::checkCondition(const Common::String &condition) {
 	return result.toInt();
 }
 
-LBSoundItem::LBSoundItem(MohawkEngine_LivingBooks *vm, LBPage *page, Common::Rect rect) : LBItem(vm, page, rect) {
-	debug(3, "new LBSoundItem");
-	_running = false;
-}
-
-LBSoundItem::~LBSoundItem() {
-	if (_running)
-		_vm->_sound->stopSound(_resourceId);
-}
-
-void LBSoundItem::update() {
-	if (_running && !_vm->_sound->isPlaying(_resourceId)) {
-		_running = false;
-		done(true);
-	}
-
-	LBItem::update();
-}
-
-bool LBSoundItem::togglePlaying(bool playing, bool restart) {
-	if (!playing)
-		return LBItem::togglePlaying(playing, restart);
-
-	if (_running) {
-		_running = false;
-		_vm->_sound->stopSound(_resourceId);
-	}
-
-	if (!_loaded || !_enabled || !_globalEnabled)
-		return false;
-
-	_running = true;
-	debug(4, "sound %d play for item %d (%s)", _resourceId, _itemId, _desc.c_str());
-	_vm->playSound(this, _resourceId);
-	return true;
-}
-
-void LBSoundItem::stop() {
-	if (_running) {
-		_running = false;
-		_vm->_sound->stopSound(_resourceId);
-	}
-
-	LBItem::stop();
-}
-
-LBItem *LBSoundItem::createClone() {
-	return new LBSoundItem(_vm, _page, _rect);
-}
-
-LBGroupItem::LBGroupItem(MohawkEngine_LivingBooks *vm, LBPage *page, Common::Rect rect) : LBItem(vm, page, rect) {
-	debug(3, "new LBGroupItem");
-	_starting = false;
-}
-
-void LBGroupItem::readData(uint16 type, uint16 size, Common::MemoryReadStreamEndian *stream) {
-	switch (type) {
-	case kLBGroupData:
-		{
-		_groupEntries.clear();
-		uint16 count = stream->readUint16();
-		debug(3, "Group data: %d entries", count);
-
-		if (size != 2 + count * 4)
-			error("kLBGroupData was wrong size (%d, for %d entries)", size, count);
-
-		for (uint i = 0; i < count; i++) {
-			GroupEntry entry;
-			// TODO: is type important for any game? at the moment, we ignore it
-			entry.entryType = stream->readUint16();
-			entry.entryId = stream->readUint16();
-			// HACK: The Living Books v3 sampler includes the ID for the group as
-			// one of the entries in the Green Eggs and Ham section, which leads
-			// to infinite recursion when the group is loaded.
-			if (entry.entryId != getId())
-				_groupEntries.push_back(entry);
-			debug(3, "group entry: id %d, type %d", entry.entryId, entry.entryType);
-		}
-		}
-		break;
-
-	default:
-		LBItem::readData(type, size, stream);
-	}
-}
-
-void LBGroupItem::destroySelf() {
-	LBItem::destroySelf();
-
-	for (uint i = 0; i < _groupEntries.size(); i++) {
-		LBItem *item = _vm->getItemById(_groupEntries[i].entryId);
-		if (item)
-			item->destroySelf();
-	}
-}
-
-void LBGroupItem::setEnabled(bool enabled) {
-	if (_starting) {
-		_starting = false;
-		LBItem::setEnabled(enabled);
-	} else {
-		for (uint i = 0; i < _groupEntries.size(); i++) {
-			LBItem *item = _vm->getItemById(_groupEntries[i].entryId);
-			if (item)
-				item->setEnabled(enabled);
-		}
-	}
-}
-
-void LBGroupItem::setGlobalEnabled(bool enabled) {
-	for (uint i = 0; i < _groupEntries.size(); i++) {
-		LBItem *item = _vm->getItemById(_groupEntries[i].entryId);
-		if (item)
-			item->setGlobalEnabled(enabled);
-	}
-}
-
-bool LBGroupItem::contains(Common::Point point) {
-	return false;
-}
-
-bool LBGroupItem::togglePlaying(bool playing, bool restart) {
-	for (uint i = 0; i < _groupEntries.size(); i++) {
-		LBItem *item = _vm->getItemById(_groupEntries[i].entryId);
-		if (item)
-			item->togglePlaying(playing, restart);
-	}
-
-	return false;
-}
-
-void LBGroupItem::seek(uint16 pos) {
-	for (uint i = 0; i < _groupEntries.size(); i++) {
-		LBItem *item = _vm->getItemById(_groupEntries[i].entryId);
-		if (item)
-			item->seek(pos);
-	}
-}
-
-void LBGroupItem::setVisible(bool visible) {
-	for (uint i = 0; i < _groupEntries.size(); i++) {
-		LBItem *item = _vm->getItemById(_groupEntries[i].entryId);
-		if (item)
-			item->setVisible(visible);
-	}
-}
-
-void LBGroupItem::setGlobalVisible(bool visible) {
-	for (uint i = 0; i < _groupEntries.size(); i++) {
-		LBItem *item = _vm->getItemById(_groupEntries[i].entryId);
-		if (item)
-			item->setGlobalVisible(visible);
-	}
-}
-
-void LBGroupItem::startPhase(uint phase) {
-	_starting = true;
-	LBItem::startPhase(phase);
-	_starting = false;
-}
-
-void LBGroupItem::stop() {
-	for (uint i = 0; i < _groupEntries.size(); i++) {
-		LBItem *item = _vm->getItemById(_groupEntries[i].entryId);
-		if (item)
-			item->stop();
-	}
-}
-
-void LBGroupItem::load() {
-	for (uint i = 0; i < _groupEntries.size(); i++) {
-		LBItem *item = _vm->getItemById(_groupEntries[i].entryId);
-		if (item)
-			item->load();
-	}
-}
-
-void LBGroupItem::unload() {
-	for (uint i = 0; i < _groupEntries.size(); i++) {
-		LBItem *item = _vm->getItemById(_groupEntries[i].entryId);
-		if (item)
-			item->unload();
-	}
-}
-
-void LBGroupItem::moveBy(const Common::Point &pos) {
-	for (uint i = 0; i < _groupEntries.size(); i++) {
-		LBItem *item = _vm->getItemById(_groupEntries[i].entryId);
-		if (item)
-			item->moveBy(pos);
-	}
-}
-
-void LBGroupItem::moveTo(const Common::Point &pos) {
-	for (uint i = 0; i < _groupEntries.size(); i++) {
-		LBItem *item = _vm->getItemById(_groupEntries[i].entryId);
-		if (item)
-			item->moveTo(pos);
-	}
-}
-
-LBItem *LBGroupItem::createClone() {
-	// TODO: needed?
-	error("LBGroupItem::createClone unimplemented");
-	return new LBGroupItem(_vm, _page, _rect);
-}
-
-LBPaletteItem::LBPaletteItem(MohawkEngine_LivingBooks *vm, LBPage *page, Common::Rect rect) : LBItem(vm, page, rect) {
-	debug(3, "new LBPaletteItem");
-
-	_fadeInStart = 0;
-	_palette = nullptr;
-}
-
-LBPaletteItem::~LBPaletteItem() {
-	delete[] _palette;
-}
-
-void LBPaletteItem::readData(uint16 type, uint16 size, Common::MemoryReadStreamEndian *stream) {
-	switch (type) {
-	case kLBPaletteXData:
-		{
-		assert(size >= 8);
-		_fadeInPeriod = stream->readUint16();
-		_fadeInStep = stream->readUint16();
-		_drawStart = stream->readUint16();
-		_drawCount = stream->readUint16();
-		if (_drawStart + _drawCount > 256)
-			error("encountered palette trying to set more than 256 colors");
-		assert(size == 8 + _drawCount * 4);
-
-		// TODO: _drawCount is really more like _drawEnd, so once we're sure that
-		// there's really no use for the palette entries before _drawCount, we
-		// might want to just discard them here, at load time.
-		_palette = new byte[_drawCount * 3];
-		for (uint i = 0; i < _drawCount; i++) {
-			_palette[i*3 + 0] = stream->readByte();
-			_palette[i*3 + 1] = stream->readByte();
-			_palette[i*3 + 2] = stream->readByte();
-			stream->readByte();
-		}
-		}
-		break;
-
-	default:
-		LBItem::readData(type, size, stream);
-	}
-}
-
-bool LBPaletteItem::togglePlaying(bool playing, bool restart) {
-	// TODO: this likely isn't the right place
-
-	if (playing) {
-		_fadeInStart = _vm->_system->getMillis();
-		_fadeInCurrent = 0;
-
-		return true;
-	}
-
-	return LBItem::togglePlaying(playing, restart);
-}
-
-void LBPaletteItem::update() {
-	if (_fadeInStart) {
-		if (!_palette)
-			error("LBPaletteItem had no palette on startup");
-
-		uint32 elapsedTime = _vm->_system->getMillis() - _fadeInStart;
-		uint32 divTime = elapsedTime / _fadeInStep;
-
-		if (divTime > _fadeInPeriod)
-			divTime = _fadeInPeriod;
-
-		if (_fadeInCurrent != divTime) {
-			_fadeInCurrent = divTime;
-
-			// TODO: actual fading-in
-			if (_visible && _globalVisible) {
-				_vm->_system->getPaletteManager()->setPalette(_palette + _drawStart * 3, _drawStart, _drawCount - _drawStart);
-				_vm->_needsRedraw = true;
-			}
-		}
-
-		if (elapsedTime >= (uint32)_fadeInPeriod * (uint32)_fadeInStep) {
-			// TODO: correct?
-			_fadeInStart = 0;
-		}
-	}
-
-	LBItem::update();
-}
-
-LBItem *LBPaletteItem::createClone() {
-	error("can't clone LBPaletteItem");
-}
-
-LBLiveTextItem::LBLiveTextItem(MohawkEngine_LivingBooks *vm, LBPage *page, Common::Rect rect) : LBItem(vm, page, rect) {
-	_currentPhrase = 0xFFFF;
-	_currentWord = 0xFFFF;
-	debug(3, "new LBLiveTextItem");
-}
-
-void LBLiveTextItem::readData(uint16 type, uint16 size, Common::MemoryReadStreamEndian *stream) {
-	switch (type) {
-	case kLBLiveTextData:
-		{
-		stream->read(_backgroundColor, 4); // unused?
-		stream->read(_foregroundColor, 4);
-		stream->read(_highlightColor, 4);
-		_paletteIndex = stream->readUint16();
-		uint16 phraseCount = stream->readUint16();
-		uint16 wordCount = stream->readUint16();
-
-		debug(3, "LiveText has %d words in %d phrases, palette index 0x%04x", wordCount, phraseCount, _paletteIndex);
-		debug(3, "LiveText colors: background %02x%02x%02x%02x, foreground %02x%02x%02x%02x, highlight %02x%02x%02x%02x",
-			_backgroundColor[0], _backgroundColor[1], _backgroundColor[2], _backgroundColor[3],
-			_foregroundColor[0], _foregroundColor[1], _foregroundColor[2], _foregroundColor[3],
-			_highlightColor[0], _highlightColor[1], _highlightColor[2], _highlightColor[3]);
-
-		if (size != 18 + 14 * wordCount + 18 * phraseCount)
-			error("Bad Live Text data size (got %d, wanted %d words and %d phrases)", size, wordCount, phraseCount);
-
-		_words.clear();
-		for (uint i = 0; i < wordCount; i++) {
-			LiveTextWord word;
-			word.bounds = _vm->readRect(stream);
-			word.soundId = stream->readUint16();
-			word.itemType = stream->readUint16();
-			word.itemId = stream->readUint16();
-			debug(4, "Word: (%d, %d) to (%d, %d), sound %d, item %d (type %d)",
-				word.bounds.left, word.bounds.top, word.bounds.right, word.bounds.bottom, word.soundId, word.itemId, word.itemType);
-			_words.push_back(word);
-		}
-
-		_phrases.clear();
-		for (uint i = 0; i < phraseCount; i++) {
-			LiveTextPhrase phrase;
-			phrase.wordStart = stream->readUint16();
-			phrase.wordCount = stream->readUint16();
-			phrase.highlightStart = stream->readUint16();
-			phrase.startId = stream->readUint16();
-			phrase.highlightEnd = stream->readUint16();
-			phrase.endId = stream->readUint16();
-
-			// The original stored the values in uint32's so we need to swap here
-			if (_vm->isBigEndian()) {
-				SWAP(phrase.highlightStart, phrase.startId);
-				SWAP(phrase.highlightEnd, phrase.endId);
-			}
-
-			uint32 unknown1 = stream->readUint16();
-			uint16 unknown2 = stream->readUint32();
-
-			if (unknown1 != 0 || unknown2 != 0)
-				error("Unexpected unknowns %08x/%04x in LiveText word", unknown1, unknown2);
-
-			debug(4, "Phrase: start %d, count %d, start at %d (from %d), end at %d (from %d)",
-				phrase.wordStart, phrase.wordCount, phrase.highlightStart, phrase.startId, phrase.highlightEnd, phrase.endId);
-
-			_phrases.push_back(phrase);
-		}
-		}
-		break;
-
-	default:
-		LBItem::readData(type, size, stream);
-	}
-}
-
-bool LBLiveTextItem::contains(Common::Point point) {
-	if (!LBItem::contains(point))
-		return false;
-
-	point.x -= _rect.left;
-	point.y -= _rect.top;
-
-	for (uint i = 0; i < _words.size(); i++) {
-		if (_words[i].bounds.contains(point))
-			return true;
-	}
-
-	return false;
-}
-
-void LBLiveTextItem::paletteUpdate(uint16 word, bool on) {
-	_vm->_needsRedraw = true;
-
-	// Sometimes the last phrase goes out-of-bounds, the original engine
-	// only checks the words which are valid in the palette updating code.
-	if (word >= _words.size())
-		return;
-
-	if (_resourceId) {
-		// with a resource, we draw a bitmap in draw() rather than changing the palette
-		return;
-	}
-
-	if (on) {
-		_vm->_system->getPaletteManager()->setPalette(_highlightColor, _paletteIndex + word, 1);
-	} else {
-		_vm->_system->getPaletteManager()->setPalette(_foregroundColor, _paletteIndex + word, 1);
-	}
-}
-
-void LBLiveTextItem::update() {
-	if (_currentWord != 0xFFFF) {
-		uint16 soundId = _words[_currentWord].soundId;
-		if (soundId && !_vm->_sound->isPlaying(soundId)) {
-			paletteUpdate(_currentWord, false);
-
-			// TODO: check this in RE
-			LBItem *item = _vm->getItemById(_words[_currentWord].itemId);
-			if (item)
-				item->togglePlaying(false, true);
-
-			_currentWord = 0xFFFF;
-		}
-	}
-
-	LBItem::update();
-}
-
-void LBLiveTextItem::draw() {
-	// this is only necessary when we are drawing using a bitmap
-	if (!_resourceId)
-		return;
-
-	if (_currentWord != 0xFFFF) {
-		uint yPos = 0;
-		if (_currentWord > 0) {
-			for (uint i = 0; i < _currentWord; i++) {
-				yPos += (_words[i].bounds.bottom - _words[i].bounds.top);
-			}
-		}
-		drawWord(_currentWord, yPos);
-		return;
-	}
-
-	if (_currentPhrase == 0xFFFF)
-		return;
-
-	uint wordStart = _phrases[_currentPhrase].wordStart;
-	uint wordCount = _phrases[_currentPhrase].wordCount;
-	if (wordStart + wordCount > _words.size())
-		error("phrase %d was invalid (%d words, from %d, out of only %d total)",
-			_currentPhrase, wordCount, wordStart, _words.size());
-
-	uint yPos = 0;
-	for (uint i = 0; i < wordStart + wordCount; i++) {
-		if (i >= wordStart)
-			drawWord(i, yPos);
-		yPos += (_words[i].bounds.bottom - _words[i].bounds.top);
-	}
-}
-
-void LBLiveTextItem::drawWord(uint word, uint yPos) {
-	Common::Rect srcRect(0, yPos, _words[word].bounds.right - _words[word].bounds.left,
-		yPos + _words[word].bounds.bottom - _words[word].bounds.top);
-	Common::Rect dstRect = _words[word].bounds;
-	dstRect.translate(_rect.left, _rect.top);
-	_vm->_gfx->copyAnimImageSectionToScreen(_resourceId, srcRect, dstRect);
-}
-
-void LBLiveTextItem::handleMouseDown(Common::Point pos) {
-	if (!_loaded || !_enabled || !_globalEnabled || _playing)
-		return LBItem::handleMouseDown(pos);
-
-	pos.x -= _rect.left;
-	pos.y -= _rect.top;
-
-	for (uint i = 0; i < _words.size(); i++) {
-		if (_words[i].bounds.contains(pos)) {
-			if (_currentWord != 0xFFFF) {
-				paletteUpdate(_currentWord, false);
-				_currentWord = 0xFFFF;
-			}
-			uint16 soundId = _words[i].soundId;
-			if (!soundId) {
-				// TODO: can we be smarter here, using timing?
-				warning("ignoring click due to no soundId");
-				return;
-			}
-			_currentWord = i;
-			_vm->playSound(this, soundId);
-			paletteUpdate(_currentWord, true);
-			return;
-		}
-	}
-
-	return LBItem::handleMouseDown(pos);
-}
-
-bool LBLiveTextItem::togglePlaying(bool playing, bool restart) {
-	if (!playing)
-		return LBItem::togglePlaying(playing, restart);
-	if (!_loaded || !_enabled || !_globalEnabled)
-		return _playing;
-
-	// TODO: handle this properly
-	_vm->_sound->stopSound();
-
-	_currentWord = 0xFFFF;
-	_currentPhrase = 0xFFFF;
-
-	return true;
-}
-
-void LBLiveTextItem::stop() {
-	// TODO: stop sound, refresh palette
-
-	LBItem::stop();
-}
-
-void LBLiveTextItem::notify(uint16 data, uint16 from) {
-	if (!_loaded || !_enabled || !_globalEnabled || !_playing)
-		return LBItem::notify(data, from);
-
-	if (_currentWord != 0xFFFF) {
-		// TODO: handle this properly
-		_vm->_sound->stopSound();
-		paletteUpdate(_currentWord, false);
-		_currentWord = 0xFFFF;
-	}
-
-	for (uint i = 0; i < _phrases.size(); i++) {
-		if (_phrases[i].highlightStart == data && _phrases[i].startId == from) {
-			debug(2, "Enabling phrase %d", i);
-			for (uint j = 0; j < _phrases[i].wordCount; j++) {
-				paletteUpdate(_phrases[i].wordStart + j, true);
-			}
-			_currentPhrase = i;
-			// TODO: not sure this is the correct logic
-			if (i == _phrases.size() - 1) {
-				_currentPhrase = 0xFFFF;
-				done(true);
-			}
-		} else if (_phrases[i].highlightEnd == data && _phrases[i].endId == from) {
-			debug(2, "Disabling phrase %d", i);
-			for (uint j = 0; j < _phrases[i].wordCount; j++) {
-				paletteUpdate(_phrases[i].wordStart + j, false);
-			}
-			_currentPhrase = 0xFFFF;
-		}
-	}
-
-	LBItem::notify(data, from);
-}
-
-LBItem *LBLiveTextItem::createClone() {
-	error("can't clone LBLiveTextItem");
-}
-
-LBPictureItem::LBPictureItem(MohawkEngine_LivingBooks *vm, LBPage *page, Common::Rect rect) : LBItem(vm, page, rect) {
-	debug(3, "new LBPictureItem");
-}
-
-void LBPictureItem::readData(uint16 type, uint16 size, Common::MemoryReadStreamEndian *stream) {
-	switch (type) {
-	case kLBSetDrawMode:
-		{
-		assert(size == 2);
-		// TODO: this probably sets whether points are always contained (0x10)
-		// or whether the bitmap contents are checked (00, or anything else?)
-		uint16 val = stream->readUint16();
-		debug(2, "LBPictureItem: kLBSetDrawMode: %04x", val);
-		}
-		break;
-
-	default:
-		LBItem::readData(type, size, stream);
-	}
-}
-
-bool LBPictureItem::contains(Common::Point point) {
-	if (!LBItem::contains(point))
-		return false;
-
-	if (!_doHitTest)
-		return true;
-
-	// TODO: only check pixels if necessary
-	return !_vm->_gfx->imageIsTransparentAt(_resourceId, false, point.x - _rect.left, point.y - _rect.top);
-}
-
-void LBPictureItem::init() {
-	_vm->_gfx->preloadImage(_resourceId);
-
-	LBItem::init();
-}
-
-void LBPictureItem::draw() {
-	if (!_loaded || !_visible || !_globalVisible)
-		return;
-
-	_vm->_gfx->copyAnimImageToScreen(_resourceId, _rect.left, _rect.top);
-}
-
-LBItem *LBPictureItem::createClone() {
-	return new LBPictureItem(_vm, _page, _rect);
-}
-
-LBAnimationItem::LBAnimationItem(MohawkEngine_LivingBooks *vm, LBPage *page, Common::Rect rect) : LBItem(vm, page, rect) {
-	_anim = nullptr;
-	_running = false;
-	debug(3, "new LBAnimationItem");
-}
-
-LBAnimationItem::~LBAnimationItem() {
-	delete _anim;
-}
-
-void LBAnimationItem::setEnabled(bool enabled) {
-	if (_running) {
-		if (enabled && _globalEnabled && !_loaded)
-			_anim->start();
-		else if (_loaded && !enabled && _enabled && _globalEnabled)
-			_anim->stop();
-	}
-
-	return LBItem::setEnabled(enabled);
-}
-
-bool LBAnimationItem::contains(Common::Point point) {
-	if (!LBItem::contains(point))
-		return false;
-
-	if (!_doHitTest)
-		return true;
-
-	return !_anim->transparentAt(point.x, point.y);
-}
-
-void LBAnimationItem::update() {
-	if (_loaded && _enabled && _globalEnabled && _running) {
-		bool wasDone = _anim->update();
-		if (wasDone) {
-			_running = false;
-			done(true);
-		}
-	}
-
-	LBItem::update();
-}
-
-bool LBAnimationItem::togglePlaying(bool playing, bool restart) {
-	if (playing) {
-		if (_loaded && _enabled && _globalEnabled) {
-			if (restart)
-				seek(1);
-			_running = true;
-			_anim->start();
-		}
-
-		return _running;
-	}
-
-	return LBItem::togglePlaying(playing, restart);
-}
-
-void LBAnimationItem::done(bool onlyNotify) {
-	if (!onlyNotify) {
-		_anim->stop();
-	}
-
-	LBItem::done(onlyNotify);
-}
-
-void LBAnimationItem::init() {
-	_anim = new LBAnimation(_vm, this, _resourceId);
-
-	LBItem::init();
-}
-
-void LBAnimationItem::stop() {
-	if (_running) {
-		_anim->stop();
-		seek(0xFFFF);
-	}
-
-	_running = false;
-
-	LBItem::stop();
-}
-
-void LBAnimationItem::seek(uint16 pos) {
-	_anim->seek(pos);
-}
-
-void LBAnimationItem::seekToTime(uint32 time) {
-	_anim->seekToTime(time);
-}
-
-void LBAnimationItem::startPhase(uint phase) {
-	if (phase == _phase)
-		seek(1);
-
-	LBItem::startPhase(phase);
-}
-
-void LBAnimationItem::draw() {
-	if (!_visible || !_globalVisible)
-		return;
-
-	_anim->draw();
-}
-
-LBItem *LBAnimationItem::createClone() {
-	LBAnimationItem *item = new LBAnimationItem(_vm, _page, _rect);
-	item->_anim = new LBAnimation(_vm, item, _resourceId);
-	return item;
-}
-
-LBMovieItem::LBMovieItem(MohawkEngine_LivingBooks *vm, LBPage *page, Common::Rect rect) : LBItem(vm, page, rect) {
-	debug(3, "new LBMovieItem");
-}
-
-LBMovieItem::~LBMovieItem() {
-}
-
-void LBMovieItem::update() {
-	if (_playing) {
-		VideoEntryPtr video = _vm->_video->findVideo(_resourceId);
-		if (!video || video->endOfVideo())
-			done(true);
-	}
-
-	LBItem::update();
-}
-
-bool LBMovieItem::togglePlaying(bool playing, bool restart) {
-	if (playing) {
-		if ((_loaded && _enabled && _globalEnabled) || _phase == kLBPhaseNone) {
-			debug("toggled video for phase %d", _phase);
-			VideoEntryPtr video = _vm->_video->playMovie(_resourceId);
-			if (!video)
-				error("Failed to open tMOV %d", _resourceId);
-
-			video->moveTo(_rect.left, _rect.top);
-			return true;
-		}
-	}
-
-	return LBItem::togglePlaying(playing, restart);
-}
-
-LBItem *LBMovieItem::createClone() {
-	return new LBMovieItem(_vm, _page, _rect);
-}
-
-LBMiniGameItem::LBMiniGameItem(MohawkEngine_LivingBooks *vm, LBPage *page, Common::Rect rect) : LBItem(vm, page, rect) {
-	debug(3, "new LBMiniGameItem");
-}
-
-LBMiniGameItem::~LBMiniGameItem() {
-}
-
-bool LBMiniGameItem::togglePlaying(bool playing, bool restart) {
-	// HACK: Since we don't support any of these hardcoded mini games yet,
-	// just skip to the most logical page. For optional minigames, this
-	// will return the player to the previous page. For mandatory minigames,
-	// this will send the player to the next page.
-
-	uint16 destPage = 0;
-	bool returnToMenu = false;
-
-	// Figure out what minigame we have and bring us back to a page where
-	// the player can continue
-	if (_desc == "Kitch")     // Green Eggs and Ham: Kitchen minigame
-		destPage = 4;
-	else if (_desc == "Eggs") // Green Eggs and Ham: Eggs minigame
-		destPage = 5;
-	else if (_desc == "Fall") // Green Eggs and Ham: Fall minigame
-		destPage = 13;
-	else if (_desc == "MagicWrite3") // Arthur's Reading Race: "Let Me Write" minigame (Page 3)
-		destPage = 3;
-	else if (_desc == "MagicWrite4") // Arthur's Reading Race: "Let Me Write" minigame (Page 4)
-		destPage = 4;
-	else if (_desc == "MagicSpy5") // Arthur's Reading Race: "I Spy" minigame (Page 5)
-		destPage = 5;
-	else if (_desc == "MagicSpy6") // Arthur's Reading Race: "I Spy" minigame (Page 6)
-		destPage = 6;
-	else if (_desc == "MagicWrite7") // Arthur's Reading Race: "Let Me Write" minigame (Page 7)
-		destPage = 7;
-	else if (_desc == "MagicSpy8") // Arthur's Reading Race: "I Spy" minigame (Page 8)
-		destPage = 8;
-	else if (_desc == "MagicRace") // Arthur's Reading Race: Race minigame
-		returnToMenu = true;
-	else
-		error("Unknown minigame '%s'", _desc.c_str());
-
-	GUI::MessageDialog dialog(Common::String::format("The '%s' minigame is not supported yet.", _desc.c_str()));
-	dialog.runModal();
-
-	// Go back to the menu if requested, otherwise go to the requested page
-	if (returnToMenu)
-		_vm->addNotifyEvent(NotifyEvent(kLBNotifyGoToControls, 1));
-	else
-		_vm->addNotifyEvent(NotifyEvent(kLBNotifyChangePage, destPage));
-
-	return false;
-}
-
-LBItem *LBMiniGameItem::createClone() {
-	error("can't clone LBMiniGameItem");
-}
-
-LBProxyItem::LBProxyItem(MohawkEngine_LivingBooks *vm, LBPage *page, Common::Rect rect) : LBItem(vm, page, rect) {
-	debug(3, "new LBProxyItem");
-
-	_page = nullptr;
-}
-
-LBProxyItem::~LBProxyItem() {
-	delete _page;
-}
-
-void LBProxyItem::load() {
-	if (_loaded)
-		return;
-
-	Common::String leftover;
-	Common::String filename = _vm->getFileNameFromConfig("Proxies", _desc.c_str(), leftover);
-	if (!leftover.empty())
-		error("LBProxyItem tried loading proxy '%s' but got leftover '%s'", _desc.c_str(), leftover.c_str());
-	uint16 baseId = 0;
-	for (uint i = 0; i < filename.size(); i++) {
-		if (filename[i] == ';') {
-			baseId = atoi(filename.c_str() + i + 1);
-			filename = Common::String(filename.c_str(), i);
-		}
-	}
-
-	debug(1, "LBProxyItem loading archive '%s' with id %d", filename.c_str(), baseId);
-	Archive *pageArchive = _vm->createArchive();
-	if (!tryOpenPage(pageArchive, filename))
-		error("failed to open archive '%s' (for proxy '%s')", filename.c_str(), _desc.c_str());
-	_page = new LBPage(_vm);
-	_page->open(pageArchive, baseId);
-
-	LBItem::load();
-}
-
-void LBProxyItem::unload() {
-	delete _page;
-	_page = nullptr;
-
-	LBItem::unload();
-}
-
-LBItem *LBProxyItem::createClone() {
-	return new LBProxyItem(_vm, _page, _rect);
-}
-
 } // End of namespace Mohawk
diff --git a/engines/mohawk/livingbooks_animation.cpp b/engines/mohawk/livingbooks_animation.cpp
new file mode 100644
index 00000000000..33c904c85fd
--- /dev/null
+++ b/engines/mohawk/livingbooks_animation.cpp
@@ -0,0 +1,704 @@
+/* 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 "mohawk/livingbooks.h"
+#include "mohawk/resource.h"
+
+#include "common/system.h"
+#include "common/textconsole.h"
+
+namespace Mohawk {
+
+// SCRP - Animation sprites/background
+
+LBAnimationNode::LBAnimationNode(MohawkEngine_LivingBooks *vm, LBAnimation *parent, uint16 scriptResourceId) : _vm(vm), _parent(parent) {
+	loadScript(scriptResourceId);
+}
+
+LBAnimationNode::~LBAnimationNode() {
+	for (uint32 i = 0; i < _scriptEntries.size(); i++)
+		delete[] _scriptEntries[i].data;
+}
+
+void LBAnimationNode::loadScript(uint16 resourceId) {
+	Common::SeekableReadStreamEndian *scriptStream = _vm->wrapStreamEndian(ID_SCRP, resourceId);
+
+	reset();
+
+	while (byte opcodeId = scriptStream->readByte()) {
+		byte size = scriptStream->readByte();
+
+		LBAnimScriptEntry entry;
+		entry.opcode = opcodeId;
+		entry.size = size;
+
+		if (!size) {
+			entry.data = nullptr;
+		} else {
+			entry.data = new byte[entry.size];
+			scriptStream->read(entry.data, entry.size);
+		}
+
+		_scriptEntries.push_back(entry);
+	}
+
+	byte size = scriptStream->readByte();
+	if (size != 0 || scriptStream->pos() != scriptStream->size())
+		error("Failed to read script correctly");
+
+	delete scriptStream;
+}
+
+void LBAnimationNode::draw(const Common::Rect &_bounds) {
+	if (!_currentCel)
+		return;
+
+	// this is also checked in SetCel, below
+	if (_currentCel > _parent->getNumResources())
+		error("Animation cel %d was too high, this shouldn't happen!", _currentCel);
+
+	int16 xOffset = _xPos + _bounds.left;
+	int16 yOffset = _yPos + _bounds.top;
+
+	uint16 resourceId = _parent->getResource(_currentCel - 1);
+
+	if (!_vm->isPreMohawk()) {
+		Common::Point offset = _parent->getOffset(_currentCel - 1);
+		xOffset -= offset.x;
+		yOffset -= offset.y;
+	}
+
+	_vm->_gfx->copyOffsetAnimImageToScreen(resourceId, xOffset, yOffset);
+}
+
+void LBAnimationNode::reset() {
+	// TODO: this causes stupid flickering
+	//if (_currentCel)
+	//	_vm->_needsRedraw = true;
+
+	_currentCel = 0;
+	_currentEntry = 0;
+	_delay = 0;
+
+	_xPos = 0;
+	_yPos = 0;
+}
+
+NodeState LBAnimationNode::update(bool seeking) {
+	if (_currentEntry == _scriptEntries.size())
+		return kLBNodeDone;
+
+	if (_delay > 0 && --_delay)
+		return kLBNodeRunning;
+
+	while (_currentEntry < _scriptEntries.size()) {
+		LBAnimScriptEntry &entry = _scriptEntries[_currentEntry];
+		_currentEntry++;
+		debug(5, "Running script entry %d of %d", _currentEntry, _scriptEntries.size());
+
+		switch (entry.opcode) {
+		case kLBAnimOpPlaySound:
+		case kLBAnimOpWaitForSound:
+		case kLBAnimOpReleaseSound:
+		case kLBAnimOpResetSound:
+			{
+			uint16 soundResourceId = READ_BE_UINT16(entry.data);
+
+			if (!soundResourceId) {
+				error("Unhandled named wave file, tell clone2727 where you found this");
+				break;
+			}
+
+			Common::String cue;
+			uint pos = 2;
+			while (pos < entry.size) {
+				char in = entry.data[pos];
+				if (!in)
+					break;
+				pos++;
+				cue += in;
+			}
+			if (pos == entry.size)
+				error("Cue in sound kLBAnimOp wasn't null-terminated");
+
+			switch (entry.opcode) {
+			case kLBAnimOpPlaySound:
+				if (seeking)
+					break;
+				debug(4, "a: PlaySound(%0d)", soundResourceId);
+				_parent->playSound(soundResourceId);
+				break;
+			case kLBAnimOpWaitForSound:
+				if (seeking)
+					break;
+				debug(4, "b: WaitForSound(%0d)", soundResourceId);
+				if (!_parent->soundPlaying(soundResourceId, cue))
+					break;
+				_currentEntry--;
+				return kLBNodeWaiting;
+			case kLBAnimOpReleaseSound:
+				debug(4, "c: ReleaseSound(%0d)", soundResourceId);
+				// TODO
+				_vm->_sound->stopSound(soundResourceId);
+				break;
+			case kLBAnimOpResetSound:
+				debug(4, "d: ResetSound(%0d)", soundResourceId);
+				// TODO
+				_vm->_sound->stopSound(soundResourceId);
+				break;
+			default:
+				break;
+			}
+			}
+			break;
+
+		case kLBAnimOpSetTempo:
+		case kLBAnimOpSetTempoDiv:
+			{
+			assert(entry.size == 2);
+			uint16 tempo = (int16)READ_BE_UINT16(entry.data);
+
+			// TODO: LB 3 uses fixed-point here.
+			if (entry.opcode == kLBAnimOpSetTempo) {
+				debug(4, "3: SetTempo(%d)", tempo);
+				// TODO: LB 3 uses (tempo * 1000) / 60, while
+				// the original divides the system time by 16.
+				_parent->setTempo(tempo * 16);
+			} else {
+				// LB 3.0+ only.
+				debug(4, "E: SetTempoDiv(%d)", tempo);
+				_parent->setTempo(1000 / tempo);
+			}
+
+			}
+			break;
+
+		case kLBAnimOpWait:
+			assert(entry.size == 0);
+			debug(5, "6: Wait()");
+			return kLBNodeRunning;
+
+		case kLBAnimOpMoveTo:
+			{
+			assert(entry.size == 4);
+			int16 x = (int16)READ_BE_UINT16(entry.data);
+			int16 y = (int16)READ_BE_UINT16(entry.data + 2);
+			debug(4, "5: MoveTo(%d, %d)", x, y);
+
+			_xPos = x;
+			_yPos = y;
+			_vm->_needsRedraw = true;
+			}
+			break;
+
+		case kLBAnimOpDrawMode:
+			{
+			assert(entry.size == 2);
+			uint16 mode = (int16)READ_BE_UINT16(entry.data);
+			debug(4, "9: DrawMode(%d)", mode);
+
+			// TODO
+			}
+			break;
+
+		case kLBAnimOpSetCel:
+			{
+			assert(entry.size == 2);
+			uint16 cel = (int16)READ_BE_UINT16(entry.data);
+			debug(4, "7: SetCel(%d)", cel);
+
+			_currentCel = cel;
+			if (_currentCel > _parent->getNumResources())
+				error("SetCel set current cel to %d, but we only have %d cels", _currentCel, _parent->getNumResources());
+			_vm->_needsRedraw = true;
+			}
+			break;
+
+		case kLBAnimOpNotify:
+			{
+			assert(entry.size == 2);
+			uint16 data = (int16)READ_BE_UINT16(entry.data);
+
+			if (seeking)
+				break;
+
+			debug(4, "2: Notify(%d)", data);
+			_vm->notifyAll(data, _parent->getParentId());
+			}
+			break;
+
+		case kLBAnimOpSleepUntil:
+			{
+			assert(entry.size == 4);
+			uint32 frame = READ_BE_UINT32(entry.data);
+			debug(4, "8: SleepUntil(%d)", frame);
+
+			if (frame > _parent->getCurrentFrame()) {
+				// *not* kLBNodeWaiting
+				_currentEntry--;
+				return kLBNodeRunning;
+			}
+			}
+			break;
+
+		case kLBAnimOpDelay:
+			{
+			assert(entry.size == 4);
+			uint32 delay = READ_BE_UINT32(entry.data);
+			debug(4, "f: Delay(%d)", delay);
+			_delay = delay;
+			return kLBNodeRunning;
+			}
+			break;
+
+		default:
+			error("Unknown opcode id %02x (size %d)", entry.opcode, entry.size);
+			break;
+		}
+	}
+
+	return kLBNodeRunning;
+}
+
+bool LBAnimationNode::transparentAt(int x, int y) {
+	if (!_currentCel)
+		return true;
+
+	uint16 resourceId = _parent->getResource(_currentCel - 1);
+
+	if (!_vm->isPreMohawk()) {
+		Common::Point offset = _parent->getOffset(_currentCel - 1);
+		x += offset.x;
+		y += offset.y;
+	}
+
+	// TODO: only check pixels if necessary
+	return _vm->_gfx->imageIsTransparentAt(resourceId, true, x - _xPos, y - _yPos);
+}
+
+LBAnimation::LBAnimation(MohawkEngine_LivingBooks *vm, LBAnimationItem *parent, uint16 resourceId) : _vm(vm), _parent(parent) {
+	Common::SeekableReadStreamEndian *aniStream = _vm->wrapStreamEndian(ID_ANI, resourceId);
+
+	// ANI records in the Wanderful sampler are 32 bytes, extra bytes are just NULs
+	if (aniStream->size() != 30 && aniStream->size() != 32)
+		warning("ANI Record size mismatch");
+
+	uint16 version = aniStream->readUint16();
+	if (version != 1)
+		warning("ANI version not 1");
+
+	_bounds = _vm->readRect(aniStream);
+	_clip = _vm->readRect(aniStream);
+	// TODO: what is colorId for?
+	uint32 colorId = aniStream->readUint32();
+	uint32 sprResourceId = aniStream->readUint32();
+	uint32 sprResourceOffset = aniStream->readUint32();
+
+	debug(5, "ANI bounds: (%d, %d), (%d, %d)", _bounds.left, _bounds.top, _bounds.right, _bounds.bottom);
+	debug(5, "ANI clip: (%d, %d), (%d, %d)", _clip.left, _clip.top, _clip.right, _clip.bottom);
+	debug(5, "ANI color id: %d", colorId);
+	debug(5, "ANI SPRResourceId: %d, offset %d", sprResourceId, sprResourceOffset);
+	if (aniStream->size() == 32) {
+		debug(5, "ANI extra bytes: (%d)", aniStream->readUint16());
+	}
+
+	if (aniStream->pos() != aniStream->size())
+		error("Still %d bytes at the end of anim stream", (int)(aniStream->size() - aniStream->pos()));
+
+	delete aniStream;
+
+	if (sprResourceOffset)
+		error("Cannot handle non-zero ANI offset yet");
+
+	Common::SeekableReadStreamEndian *sprStream = _vm->wrapStreamEndian(ID_SPR, sprResourceId);
+
+	uint16 numBackNodes = sprStream->readUint16();
+	uint16 numFrontNodes = sprStream->readUint16();
+	uint32 shapeResourceID = sprStream->readUint32();
+	uint32 shapeResourceOffset = sprStream->readUint32();
+	uint32 scriptResourceID = sprStream->readUint32();
+	uint32 scriptResourceOffset = sprStream->readUint32();
+	uint32 scriptResourceLength = sprStream->readUint32();
+	debug(5, "SPR# stream: %d front, %d background", numFrontNodes, numBackNodes);
+	debug(5, "Shape ID %d (offset 0x%04x), script ID %d (offset 0x%04x, length %d)", shapeResourceID, shapeResourceOffset,
+		scriptResourceID, scriptResourceOffset, scriptResourceLength);
+
+	Common::Array<uint16> scriptIDs;
+	for (uint16 i = 0; i < numFrontNodes; i++) {
+		uint32 unknown1 = sprStream->readUint32();
+		uint32 unknown2 = sprStream->readUint32();
+		uint32 unknown3 = sprStream->readUint32();
+		uint16 scriptID = sprStream->readUint32();
+		uint32 unknown4 = sprStream->readUint32();
+		uint32 unknown5 = sprStream->readUint32();
+		scriptIDs.push_back(scriptID);
+		debug(6, "Front node %d: script ID %d", i, scriptID);
+		if (unknown1 != 0 || unknown2 != 0 || unknown3 != 0 || unknown4 != 0 || unknown5 != 0)
+			error("Anim node %d had non-zero unknowns %08x, %08x, %08x, %08x, %08x",
+				i, unknown1, unknown2, unknown3, unknown4, unknown5);
+	}
+
+	if (numBackNodes)
+		error("Ignoring %d back nodes", numBackNodes);
+
+	if (sprStream->pos() != sprStream->size())
+		error("Still %d bytes at the end of sprite stream", (int)(sprStream->size() - sprStream->pos()));
+
+	delete sprStream;
+
+	loadShape(shapeResourceID);
+
+	_nodes.push_back(new LBAnimationNode(_vm, this, scriptResourceID));
+	for (uint16 i = 0; i < scriptIDs.size(); i++)
+		_nodes.push_back(new LBAnimationNode(_vm, this, scriptIDs[i]));
+
+	_currentFrame = 0;
+	_currentSound = 0xffff;
+	_running = false;
+	_tempo = 1;
+}
+
+LBAnimation::~LBAnimation() {
+	for (uint32 i = 0; i < _nodes.size(); i++)
+		delete _nodes[i];
+	if (_currentSound != 0xffff)
+		_vm->_sound->stopSound(_currentSound);
+}
+
+void LBAnimation::loadShape(uint16 resourceId) {
+	if (resourceId == 0)
+		return;
+
+	Common::SeekableReadStreamEndian *shapeStream = _vm->wrapStreamEndian(ID_SHP, resourceId);
+
+	if (_vm->isPreMohawk()) {
+		if (shapeStream->size() < 6)
+			error("V1 SHP Record size too short (%d)", (int)shapeStream->size());
+
+		uint16 u0 = shapeStream->readUint16();
+		if (u0 != 3)
+			error("V1 SHP Record u0 is %04x, not 3", u0);
+
+		uint16 u1 = shapeStream->readUint16();
+		if (u1 != 0)
+			error("V1 SHP Record u1 is %04x, not 0", u1);
+
+		uint16 idCount = shapeStream->readUint16();
+		debug(8, "V1 SHP: idCount: %d", idCount);
+
+		if (shapeStream->size() != (idCount * 2) + 6)
+			error("V1 SHP Record size mismatch (%d)", (int)shapeStream->size());
+
+		for (uint16 i = 0; i < idCount; i++) {
+			_shapeResources.push_back(shapeStream->readUint16());
+			debug(8, "V1 SHP: BMAP Resource Id %d: %d", i, _shapeResources[i]);
+		}
+	} else {
+		uint16 idCount = shapeStream->readUint16();
+		debug(8, "SHP: idCount: %d", idCount);
+
+		if (shapeStream->size() != (idCount * 6) + 2)
+			error("SHP Record size mismatch (%d)", (int)shapeStream->size());
+
+		for (uint16 i = 0; i < idCount; i++) {
+			_shapeResources.push_back(shapeStream->readUint16());
+			int16 x = shapeStream->readSint16();
+			int16 y = shapeStream->readSint16();
+			_shapeOffsets.push_back(Common::Point(x, y));
+			debug(8, "SHP: tBMP Resource Id %d: %d, at (%d, %d)", i, _shapeResources[i], x, y);
+		}
+	}
+
+	for (uint16 i = 0; i < _shapeResources.size(); i++)
+		_vm->_gfx->preloadImage(_shapeResources[i]);
+
+	delete shapeStream;
+}
+
+void LBAnimation::draw() {
+	for (uint32 i = 0; i < _nodes.size(); i++)
+		_nodes[i]->draw(_bounds);
+}
+
+bool LBAnimation::update() {
+	if (!_running)
+		return false;
+
+	if (_vm->_system->getMillis() <= _lastTime + (uint32)_tempo)
+		return false;
+
+	// the second check is to try 'catching up' with lagged animations, might be crazy
+	if (_lastTime == 0 || (_vm->_system->getMillis()) > _lastTime + (uint32)(_tempo * 2))
+		_lastTime = _vm->_system->getMillis();
+	else
+		_lastTime += _tempo;
+
+	if (_currentSound != 0xffff && !_vm->_sound->isPlaying(_currentSound)) {
+		_currentSound = 0xffff;
+	}
+
+	NodeState state = kLBNodeDone;
+	for (uint32 i = 0; i < _nodes.size(); i++) {
+		NodeState s = _nodes[i]->update();
+		if (s == kLBNodeWaiting) {
+			state = kLBNodeWaiting;
+			if (i != 0)
+				warning("non-primary node was waiting");
+			break;
+		}
+		if (s == kLBNodeRunning)
+			state = kLBNodeRunning;
+	}
+
+	if (state == kLBNodeRunning) {
+		_currentFrame++;
+	} else if (state == kLBNodeDone) {
+		if (_currentSound == 0xffff) {
+			_running = false;
+			return true;
+		}
+	}
+
+	return false;
+}
+
+void LBAnimation::start() {
+	_lastTime = 0;
+	_running = true;
+}
+
+void LBAnimation::seek(uint16 pos) {
+	_lastTime = 0;
+	_currentFrame = 0;
+
+	if (_currentSound != 0xffff) {
+		_vm->_sound->stopSound(_currentSound);
+		_currentSound = 0xffff;
+	}
+
+	for (uint32 i = 0; i < _nodes.size(); i++)
+		_nodes[i]->reset();
+
+	for (uint16 n = 0; n < pos; n++) {
+		bool ranSomething = false;
+		// nodes don't wait while seeking
+		for (uint32 i = 0; i < _nodes.size(); i++)
+			ranSomething |= (_nodes[i]->update(true) != kLBNodeDone);
+
+		_currentFrame++;
+
+		if (!ranSomething) {
+			_running = false;
+			break;
+		}
+	}
+}
+
+void LBAnimation::seekToTime(uint32 time) {
+	_lastTime = 0;
+	_currentFrame = 0;
+
+	if (_currentSound != 0xffff) {
+		_vm->_sound->stopSound(_currentSound);
+		_currentSound = 0xffff;
+	}
+
+	for (uint32 i = 0; i < _nodes.size(); i++)
+		_nodes[i]->reset();
+
+	uint32 elapsed = 0;
+	while (elapsed <= time) {
+		bool ranSomething = false;
+		// nodes don't wait while seeking
+		for (uint32 i = 0; i < _nodes.size(); i++)
+			ranSomething |= (_nodes[i]->update(true) != kLBNodeDone);
+
+		elapsed += _tempo;
+		_currentFrame++;
+
+		if (!ranSomething) {
+			_running = false;
+			break;
+		}
+	}
+}
+
+void LBAnimation::stop() {
+	_running = false;
+	if (_currentSound != 0xffff) {
+		_vm->_sound->stopSound(_currentSound);
+		_currentSound = 0xffff;
+	}
+}
+
+void LBAnimation::playSound(uint16 resourceId) {
+	_currentSound = resourceId;
+	_vm->_sound->playSound(_currentSound, Audio::Mixer::kMaxChannelVolume, false, &_cueList);
+}
+
+bool LBAnimation::soundPlaying(uint16 resourceId, const Common::String &cue) {
+	if (_currentSound != resourceId)
+		return false;
+	if (!_vm->_sound->isPlaying(_currentSound))
+		return false;
+
+	if (cue.empty())
+		return true;
+
+	uint samples = _vm->_sound->getNumSamplesPlayed(_currentSound);
+	for (uint i = 0; i < _cueList.pointCount; i++) {
+		if (_cueList.points[i].sampleFrame > samples)
+			break;
+		if (_cueList.points[i].name == cue)
+			return false;
+	}
+
+	return true;
+}
+
+bool LBAnimation::transparentAt(int x, int y) {
+	for (uint32 i = 0; i < _nodes.size(); i++)
+		if (!_nodes[i]->transparentAt(x - _bounds.left, y - _bounds.top))
+			return false;
+
+	return true;
+}
+
+void LBAnimation::setTempo(uint16 tempo) {
+	_tempo = tempo;
+}
+
+uint16 LBAnimation::getParentId() {
+	return _parent->getId();
+}
+
+LBAnimationItem::LBAnimationItem(MohawkEngine_LivingBooks *vm, LBPage *page, Common::Rect rect) : LBItem(vm, page, rect) {
+	_anim = nullptr;
+	_running = false;
+	debug(3, "new LBAnimationItem");
+}
+
+LBAnimationItem::~LBAnimationItem() {
+	delete _anim;
+}
+
+void LBAnimationItem::setEnabled(bool enabled) {
+	if (_running) {
+		if (enabled && _globalEnabled && !_loaded)
+			_anim->start();
+		else if (_loaded && !enabled && _enabled && _globalEnabled)
+			_anim->stop();
+	}
+
+	return LBItem::setEnabled(enabled);
+}
+
+bool LBAnimationItem::contains(Common::Point point) {
+	if (!LBItem::contains(point))
+		return false;
+
+	if (!_doHitTest)
+		return true;
+
+	return !_anim->transparentAt(point.x, point.y);
+}
+
+void LBAnimationItem::update() {
+	if (_loaded && _enabled && _globalEnabled && _running) {
+		bool wasDone = _anim->update();
+		if (wasDone) {
+			_running = false;
+			done(true);
+		}
+	}
+
+	LBItem::update();
+}
+
+bool LBAnimationItem::togglePlaying(bool playing, bool restart) {
+	if (playing) {
+		if (_loaded && _enabled && _globalEnabled) {
+			if (restart)
+				seek(1);
+			_running = true;
+			_anim->start();
+		}
+
+		return _running;
+	}
+
+	return LBItem::togglePlaying(playing, restart);
+}
+
+void LBAnimationItem::done(bool onlyNotify) {
+	if (!onlyNotify) {
+		_anim->stop();
+	}
+
+	LBItem::done(onlyNotify);
+}
+
+void LBAnimationItem::init() {
+	_anim = new LBAnimation(_vm, this, _resourceId);
+
+	LBItem::init();
+}
+
+void LBAnimationItem::stop() {
+	if (_running) {
+		_anim->stop();
+		seek(0xFFFF);
+	}
+
+	_running = false;
+
+	LBItem::stop();
+}
+
+void LBAnimationItem::seek(uint16 pos) {
+	_anim->seek(pos);
+}
+
+void LBAnimationItem::seekToTime(uint32 time) {
+	_anim->seekToTime(time);
+}
+
+void LBAnimationItem::startPhase(uint phase) {
+	if (phase == _phase)
+		seek(1);
+
+	LBItem::startPhase(phase);
+}
+
+void LBAnimationItem::draw() {
+	if (!_visible || !_globalVisible)
+		return;
+
+	_anim->draw();
+}
+
+LBItem *LBAnimationItem::createClone() {
+	LBAnimationItem *item = new LBAnimationItem(_vm, _page, _rect);
+	item->_anim = new LBAnimation(_vm, item, _resourceId);
+	return item;
+}
+
+} // End of namespace Mohawk
diff --git a/engines/mohawk/livingbooks_bookinfo.cpp b/engines/mohawk/livingbooks_bookinfo.cpp
new file mode 100644
index 00000000000..1e7988016a4
--- /dev/null
+++ b/engines/mohawk/livingbooks_bookinfo.cpp
@@ -0,0 +1,177 @@
+/* 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 "mohawk/livingbooks.h"
+
+#include "common/textconsole.h"
+
+namespace Mohawk {
+
+void MohawkEngine_LivingBooks::loadBookInfo(const Common::Path &filename) {
+	_bookInfoFile.allowNonEnglishCharacters();
+	if (!_bookInfoFile.loadFromFile(filename))
+		error("Could not open %s as a config file", filename.toString().c_str());
+
+	_title = getStringFromConfig("BookInfo", "title");
+	_copyright = getStringFromConfig("BookInfo", "copyright");
+
+	_numPages = getIntFromConfig("BookInfo", "nPages");
+	_numLanguages = getIntFromConfig("BookInfo", "nLanguages");
+	_screenWidth = getIntFromConfig("BookInfo", "xRes");
+	_screenHeight = getIntFromConfig("BookInfo", "yRes");
+	// nColors is here too, but it's always 256 anyway...
+
+	// this is 1 in The New Kid on the Block, changes the hardcoded UI
+	// v2 games changed the flag name to fPoetry
+	if (getGameType() == GType_LIVINGBOOKSV1)
+		_poetryMode = (getIntFromConfig("BookInfo", "poetry") == 1);
+	else
+		_poetryMode = (getIntFromConfig("BookInfo", "fPoetry") == 1);
+
+	// The later Living Books games add some more options:
+	//     - fNeedPalette                (always true?)
+	//     - fUse254ColorPalette         (always true?)
+	//     - nKBRequired                 (4096, RAM requirement?)
+	//     - fDebugWindow                (always 0?)
+
+	if (_bookInfoFile.hasSection("Globals")) {
+		const Common::INIFile::SectionKeyList globals = _bookInfoFile.getKeys("Globals");
+		for (Common::INIFile::SectionKeyList::const_iterator i = globals.begin(); i != globals.end(); i++) {
+			Common::String command = Common::String::format("%s = %s", i->key.c_str(), i->value.c_str());
+			LBCode tempCode(this, 0);
+			uint offset = tempCode.parseCode(command);
+			tempCode.runCode(nullptr, offset);
+		}
+	}
+}
+
+Common::String MohawkEngine_LivingBooks::stringForMode(LBMode mode) {
+	Common::String language = getStringFromConfig("Languages", Common::String::format("Language%d", _curLanguage));
+
+	switch (mode) {
+	case kLBIntroMode:
+		return "Intro";
+	case kLBControlMode:
+		return "Control";
+	case kLBCreditsMode:
+		return "Credits";
+	case kLBPreviewMode:
+		return "Preview";
+	case kLBReadMode:
+		return language + ".Read";
+	case kLBPlayMode:
+		return language + ".Play";
+	default:
+		error("unknown game mode %d", (int)mode);
+	}
+}
+
+Common::String MohawkEngine_LivingBooks::getStringFromConfig(const Common::String &section, const Common::String &key) {
+	Common::String x, leftover;
+	_bookInfoFile.getKey(key, section, x);
+	Common::String tmp = removeQuotesFromString(x, leftover);
+	if (!leftover.empty())
+		warning("while parsing config key '%s' from section '%s', string '%s' was left after '%s'",
+			key.c_str(), section.c_str(), leftover.c_str(), tmp.c_str());
+	return tmp;
+}
+
+Common::String MohawkEngine_LivingBooks::getStringFromConfig(const Common::String &section, const Common::String &key, Common::String &leftover) {
+	Common::String x;
+	_bookInfoFile.getKey(key, section, x);
+	return removeQuotesFromString(x, leftover);
+}
+
+int MohawkEngine_LivingBooks::getIntFromConfig(const Common::String &section, const Common::String &key) {
+	return atoi(getStringFromConfig(section, key).c_str());
+}
+
+Common::String MohawkEngine_LivingBooks::getFileNameFromConfig(const Common::String &section, const Common::String &key, Common::String &leftover) {
+	Common::String string = getStringFromConfig(section, key, leftover);
+
+	if (string.hasPrefix("//")) {
+		// skip "//CD-ROM Title/" prefixes which we don't care about
+		uint i = 3;
+		while (i < string.size() && string[i - 1] != '/')
+			i++;
+
+		// Already uses slashes, no need to convert further
+		return string.c_str() + i;
+	}
+
+	return (getPlatform() == Common::kPlatformMacintosh) ? convertMacFileName(string) : convertWinFileName(string);
+}
+
+Common::String MohawkEngine_LivingBooks::removeQuotesFromString(const Common::String &string, Common::String &leftover) {
+	if (string.empty())
+		return string;
+
+	char quoteChar = string[0];
+	if (quoteChar != '\"' && quoteChar != '\'')
+		return string;
+
+	Common::String tmp;
+	bool inLeftover = false;
+	for (uint32 i = 1; i < string.size(); i++) {
+		if (inLeftover)
+			leftover += string[i];
+		else if (string[i] == quoteChar)
+			inLeftover = true;
+		else
+			tmp += string[i];
+	}
+
+	return tmp;
+}
+
+Common::String MohawkEngine_LivingBooks::convertMacFileName(const Common::String &string) {
+	Common::String filename;
+
+	for (uint32 i = 0; i < string.size(); i++) {
+		if (i == 0 && string[i] == ':') // First character should be ignored (another colon)
+			continue;
+		if (string[i] == ':') // Directory separator
+			filename += '/';
+		else if (string[i] == '/') // Literal slash
+			filename += ':'; // Replace by colon, as used by macOS for slash
+		else
+			filename += string[i];
+	}
+
+	return filename;
+}
+
+Common::String MohawkEngine_LivingBooks::convertWinFileName(const Common::String &string) {
+	Common::String filename;
+
+	for (uint32 i = 0; i < string.size(); i++) {
+		if (i == 0 && (string[i] == '/' || string[i] == '\\')) // ignore slashes at start
+			continue;
+		if (string[i] == '\\')
+			filename += '/';
+		else
+			filename += string[i];
+	}
+
+	return filename;
+}
+
+} // End of namespace Mohawk
diff --git a/engines/mohawk/livingbooks_itemtypes.cpp b/engines/mohawk/livingbooks_itemtypes.cpp
new file mode 100644
index 00000000000..b1ee4d10772
--- /dev/null
+++ b/engines/mohawk/livingbooks_itemtypes.cpp
@@ -0,0 +1,778 @@
+/* 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 "mohawk/livingbooks.h"
+#include "mohawk/resource.h"
+#include "mohawk/video.h"
+
+#include "common/memstream.h"
+#include "common/system.h"
+#include "common/textconsole.h"
+
+#include "graphics/paletteman.h"
+
+#include "gui/message.h"
+
+namespace Mohawk {
+
+LBSoundItem::LBSoundItem(MohawkEngine_LivingBooks *vm, LBPage *page, Common::Rect rect) : LBItem(vm, page, rect) {
+	debug(3, "new LBSoundItem");
+	_running = false;
+}
+
+LBSoundItem::~LBSoundItem() {
+	if (_running)
+		_vm->_sound->stopSound(_resourceId);
+}
+
+void LBSoundItem::update() {
+	if (_running && !_vm->_sound->isPlaying(_resourceId)) {
+		_running = false;
+		done(true);
+	}
+
+	LBItem::update();
+}
+
+bool LBSoundItem::togglePlaying(bool playing, bool restart) {
+	if (!playing)
+		return LBItem::togglePlaying(playing, restart);
+
+	if (_running) {
+		_running = false;
+		_vm->_sound->stopSound(_resourceId);
+	}
+
+	if (!_loaded || !_enabled || !_globalEnabled)
+		return false;
+
+	_running = true;
+	debug(4, "sound %d play for item %d (%s)", _resourceId, _itemId, _desc.c_str());
+	_vm->playSound(this, _resourceId);
+	return true;
+}
+
+void LBSoundItem::stop() {
+	if (_running) {
+		_running = false;
+		_vm->_sound->stopSound(_resourceId);
+	}
+
+	LBItem::stop();
+}
+
+LBItem *LBSoundItem::createClone() {
+	return new LBSoundItem(_vm, _page, _rect);
+}
+
+LBGroupItem::LBGroupItem(MohawkEngine_LivingBooks *vm, LBPage *page, Common::Rect rect) : LBItem(vm, page, rect) {
+	debug(3, "new LBGroupItem");
+	_starting = false;
+}
+
+void LBGroupItem::readData(uint16 type, uint16 size, Common::MemoryReadStreamEndian *stream) {
+	switch (type) {
+	case kLBGroupData:
+		{
+		_groupEntries.clear();
+		uint16 count = stream->readUint16();
+		debug(3, "Group data: %d entries", count);
+
+		if (size != 2 + count * 4)
+			error("kLBGroupData was wrong size (%d, for %d entries)", size, count);
+
+		for (uint i = 0; i < count; i++) {
+			GroupEntry entry;
+			// TODO: is type important for any game? at the moment, we ignore it
+			entry.entryType = stream->readUint16();
+			entry.entryId = stream->readUint16();
+			// HACK: The Living Books v3 sampler includes the ID for the group as
+			// one of the entries in the Green Eggs and Ham section, which leads
+			// to infinite recursion when the group is loaded.
+			if (entry.entryId != getId())
+				_groupEntries.push_back(entry);
+			debug(3, "group entry: id %d, type %d", entry.entryId, entry.entryType);
+		}
+		}
+		break;
+
+	default:
+		LBItem::readData(type, size, stream);
+	}
+}
+
+void LBGroupItem::destroySelf() {
+	LBItem::destroySelf();
+
+	for (uint i = 0; i < _groupEntries.size(); i++) {
+		LBItem *item = _vm->getItemById(_groupEntries[i].entryId);
+		if (item)
+			item->destroySelf();
+	}
+}
+
+void LBGroupItem::setEnabled(bool enabled) {
+	if (_starting) {
+		_starting = false;
+		LBItem::setEnabled(enabled);
+	} else {
+		for (uint i = 0; i < _groupEntries.size(); i++) {
+			LBItem *item = _vm->getItemById(_groupEntries[i].entryId);
+			if (item)
+				item->setEnabled(enabled);
+		}
+	}
+}
+
+void LBGroupItem::setGlobalEnabled(bool enabled) {
+	for (uint i = 0; i < _groupEntries.size(); i++) {
+		LBItem *item = _vm->getItemById(_groupEntries[i].entryId);
+		if (item)
+			item->setGlobalEnabled(enabled);
+	}
+}
+
+bool LBGroupItem::contains(Common::Point point) {
+	return false;
+}
+
+bool LBGroupItem::togglePlaying(bool playing, bool restart) {
+	for (uint i = 0; i < _groupEntries.size(); i++) {
+		LBItem *item = _vm->getItemById(_groupEntries[i].entryId);
+		if (item)
+			item->togglePlaying(playing, restart);
+	}
+
+	return false;
+}
+
+void LBGroupItem::seek(uint16 pos) {
+	for (uint i = 0; i < _groupEntries.size(); i++) {
+		LBItem *item = _vm->getItemById(_groupEntries[i].entryId);
+		if (item)
+			item->seek(pos);
+	}
+}
+
+void LBGroupItem::setVisible(bool visible) {
+	for (uint i = 0; i < _groupEntries.size(); i++) {
+		LBItem *item = _vm->getItemById(_groupEntries[i].entryId);
+		if (item)
+			item->setVisible(visible);
+	}
+}
+
+void LBGroupItem::setGlobalVisible(bool visible) {
+	for (uint i = 0; i < _groupEntries.size(); i++) {
+		LBItem *item = _vm->getItemById(_groupEntries[i].entryId);
+		if (item)
+			item->setGlobalVisible(visible);
+	}
+}
+
+void LBGroupItem::startPhase(uint phase) {
+	_starting = true;
+	LBItem::startPhase(phase);
+	_starting = false;
+}
+
+void LBGroupItem::stop() {
+	for (uint i = 0; i < _groupEntries.size(); i++) {
+		LBItem *item = _vm->getItemById(_groupEntries[i].entryId);
+		if (item)
+			item->stop();
+	}
+}
+
+void LBGroupItem::load() {
+	for (uint i = 0; i < _groupEntries.size(); i++) {
+		LBItem *item = _vm->getItemById(_groupEntries[i].entryId);
+		if (item)
+			item->load();
+	}
+}
+
+void LBGroupItem::unload() {
+	for (uint i = 0; i < _groupEntries.size(); i++) {
+		LBItem *item = _vm->getItemById(_groupEntries[i].entryId);
+		if (item)
+			item->unload();
+	}
+}
+
+void LBGroupItem::moveBy(const Common::Point &pos) {
+	for (uint i = 0; i < _groupEntries.size(); i++) {
+		LBItem *item = _vm->getItemById(_groupEntries[i].entryId);
+		if (item)
+			item->moveBy(pos);
+	}
+}
+
+void LBGroupItem::moveTo(const Common::Point &pos) {
+	for (uint i = 0; i < _groupEntries.size(); i++) {
+		LBItem *item = _vm->getItemById(_groupEntries[i].entryId);
+		if (item)
+			item->moveTo(pos);
+	}
+}
+
+LBItem *LBGroupItem::createClone() {
+	// TODO: needed?
+	error("LBGroupItem::createClone unimplemented");
+	return new LBGroupItem(_vm, _page, _rect);
+}
+
+LBPaletteItem::LBPaletteItem(MohawkEngine_LivingBooks *vm, LBPage *page, Common::Rect rect) : LBItem(vm, page, rect) {
+	debug(3, "new LBPaletteItem");
+
+	_fadeInStart = 0;
+	_palette = nullptr;
+}
+
+LBPaletteItem::~LBPaletteItem() {
+	delete[] _palette;
+}
+
+void LBPaletteItem::readData(uint16 type, uint16 size, Common::MemoryReadStreamEndian *stream) {
+	switch (type) {
+	case kLBPaletteXData:
+		{
+		assert(size >= 8);
+		_fadeInPeriod = stream->readUint16();
+		_fadeInStep = stream->readUint16();
+		_drawStart = stream->readUint16();
+		_drawCount = stream->readUint16();
+		if (_drawStart + _drawCount > 256)
+			error("encountered palette trying to set more than 256 colors");
+		assert(size == 8 + _drawCount * 4);
+
+		// TODO: _drawCount is really more like _drawEnd, so once we're sure that
+		// there's really no use for the palette entries before _drawCount, we
+		// might want to just discard them here, at load time.
+		_palette = new byte[_drawCount * 3];
+		for (uint i = 0; i < _drawCount; i++) {
+			_palette[i*3 + 0] = stream->readByte();
+			_palette[i*3 + 1] = stream->readByte();
+			_palette[i*3 + 2] = stream->readByte();
+			stream->readByte();
+		}
+		}
+		break;
+
+	default:
+		LBItem::readData(type, size, stream);
+	}
+}
+
+bool LBPaletteItem::togglePlaying(bool playing, bool restart) {
+	// TODO: this likely isn't the right place
+
+	if (playing) {
+		_fadeInStart = _vm->_system->getMillis();
+		_fadeInCurrent = 0;
+
+		return true;
+	}
+
+	return LBItem::togglePlaying(playing, restart);
+}
+
+void LBPaletteItem::update() {
+	if (_fadeInStart) {
+		if (!_palette)
+			error("LBPaletteItem had no palette on startup");
+
+		uint32 elapsedTime = _vm->_system->getMillis() - _fadeInStart;
+		uint32 divTime = elapsedTime / _fadeInStep;
+
+		if (divTime > _fadeInPeriod)
+			divTime = _fadeInPeriod;
+
+		if (_fadeInCurrent != divTime) {
+			_fadeInCurrent = divTime;
+
+			// TODO: actual fading-in
+			if (_visible && _globalVisible) {
+				_vm->_system->getPaletteManager()->setPalette(_palette + _drawStart * 3, _drawStart, _drawCount - _drawStart);
+				_vm->_needsRedraw = true;
+			}
+		}
+
+		if (elapsedTime >= (uint32)_fadeInPeriod * (uint32)_fadeInStep) {
+			// TODO: correct?
+			_fadeInStart = 0;
+		}
+	}
+
+	LBItem::update();
+}
+
+LBItem *LBPaletteItem::createClone() {
+	error("can't clone LBPaletteItem");
+}
+
+LBLiveTextItem::LBLiveTextItem(MohawkEngine_LivingBooks *vm, LBPage *page, Common::Rect rect) : LBItem(vm, page, rect) {
+	_currentPhrase = 0xFFFF;
+	_currentWord = 0xFFFF;
+	debug(3, "new LBLiveTextItem");
+}
+
+void LBLiveTextItem::readData(uint16 type, uint16 size, Common::MemoryReadStreamEndian *stream) {
+	switch (type) {
+	case kLBLiveTextData:
+		{
+		stream->read(_backgroundColor, 4); // unused?
+		stream->read(_foregroundColor, 4);
+		stream->read(_highlightColor, 4);
+		_paletteIndex = stream->readUint16();
+		uint16 phraseCount = stream->readUint16();
+		uint16 wordCount = stream->readUint16();
+
+		debug(3, "LiveText has %d words in %d phrases, palette index 0x%04x", wordCount, phraseCount, _paletteIndex);
+		debug(3, "LiveText colors: background %02x%02x%02x%02x, foreground %02x%02x%02x%02x, highlight %02x%02x%02x%02x",
+			_backgroundColor[0], _backgroundColor[1], _backgroundColor[2], _backgroundColor[3],
+			_foregroundColor[0], _foregroundColor[1], _foregroundColor[2], _foregroundColor[3],
+			_highlightColor[0], _highlightColor[1], _highlightColor[2], _highlightColor[3]);
+
+		if (size != 18 + 14 * wordCount + 18 * phraseCount)
+			error("Bad Live Text data size (got %d, wanted %d words and %d phrases)", size, wordCount, phraseCount);
+
+		_words.clear();
+		for (uint i = 0; i < wordCount; i++) {
+			LiveTextWord word;
+			word.bounds = _vm->readRect(stream);
+			word.soundId = stream->readUint16();
+			word.itemType = stream->readUint16();
+			word.itemId = stream->readUint16();
+			debug(4, "Word: (%d, %d) to (%d, %d), sound %d, item %d (type %d)",
+				word.bounds.left, word.bounds.top, word.bounds.right, word.bounds.bottom, word.soundId, word.itemId, word.itemType);
+			_words.push_back(word);
+		}
+
+		_phrases.clear();
+		for (uint i = 0; i < phraseCount; i++) {
+			LiveTextPhrase phrase;
+			phrase.wordStart = stream->readUint16();
+			phrase.wordCount = stream->readUint16();
+			phrase.highlightStart = stream->readUint16();
+			phrase.startId = stream->readUint16();
+			phrase.highlightEnd = stream->readUint16();
+			phrase.endId = stream->readUint16();
+
+			// The original stored the values in uint32's so we need to swap here
+			if (_vm->isBigEndian()) {
+				SWAP(phrase.highlightStart, phrase.startId);
+				SWAP(phrase.highlightEnd, phrase.endId);
+			}
+
+			uint32 unknown1 = stream->readUint16();
+			uint16 unknown2 = stream->readUint32();
+
+			if (unknown1 != 0 || unknown2 != 0)
+				error("Unexpected unknowns %08x/%04x in LiveText word", unknown1, unknown2);
+
+			debug(4, "Phrase: start %d, count %d, start at %d (from %d), end at %d (from %d)",
+				phrase.wordStart, phrase.wordCount, phrase.highlightStart, phrase.startId, phrase.highlightEnd, phrase.endId);
+
+			_phrases.push_back(phrase);
+		}
+		}
+		break;
+
+	default:
+		LBItem::readData(type, size, stream);
+	}
+}
+
+bool LBLiveTextItem::contains(Common::Point point) {
+	if (!LBItem::contains(point))
+		return false;
+
+	point.x -= _rect.left;
+	point.y -= _rect.top;
+
+	for (uint i = 0; i < _words.size(); i++) {
+		if (_words[i].bounds.contains(point))
+			return true;
+	}
+
+	return false;
+}
+
+void LBLiveTextItem::paletteUpdate(uint16 word, bool on) {
+	_vm->_needsRedraw = true;
+
+	// Sometimes the last phrase goes out-of-bounds, the original engine
+	// only checks the words which are valid in the palette updating code.
+	if (word >= _words.size())
+		return;
+
+	if (_resourceId) {
+		// with a resource, we draw a bitmap in draw() rather than changing the palette
+		return;
+	}
+
+	if (on) {
+		_vm->_system->getPaletteManager()->setPalette(_highlightColor, _paletteIndex + word, 1);
+	} else {
+		_vm->_system->getPaletteManager()->setPalette(_foregroundColor, _paletteIndex + word, 1);
+	}
+}
+
+void LBLiveTextItem::update() {
+	if (_currentWord != 0xFFFF) {
+		uint16 soundId = _words[_currentWord].soundId;
+		if (soundId && !_vm->_sound->isPlaying(soundId)) {
+			paletteUpdate(_currentWord, false);
+
+			// TODO: check this in RE
+			LBItem *item = _vm->getItemById(_words[_currentWord].itemId);
+			if (item)
+				item->togglePlaying(false, true);
+
+			_currentWord = 0xFFFF;
+		}
+	}
+
+	LBItem::update();
+}
+
+void LBLiveTextItem::draw() {
+	// this is only necessary when we are drawing using a bitmap
+	if (!_resourceId)
+		return;
+
+	if (_currentWord != 0xFFFF) {
+		uint yPos = 0;
+		if (_currentWord > 0) {
+			for (uint i = 0; i < _currentWord; i++) {
+				yPos += (_words[i].bounds.bottom - _words[i].bounds.top);
+			}
+		}
+		drawWord(_currentWord, yPos);
+		return;
+	}
+
+	if (_currentPhrase == 0xFFFF)
+		return;
+
+	uint wordStart = _phrases[_currentPhrase].wordStart;
+	uint wordCount = _phrases[_currentPhrase].wordCount;
+	if (wordStart + wordCount > _words.size())
+		error("phrase %d was invalid (%d words, from %d, out of only %d total)",
+			_currentPhrase, wordCount, wordStart, _words.size());
+
+	uint yPos = 0;
+	for (uint i = 0; i < wordStart + wordCount; i++) {
+		if (i >= wordStart)
+			drawWord(i, yPos);
+		yPos += (_words[i].bounds.bottom - _words[i].bounds.top);
+	}
+}
+
+void LBLiveTextItem::drawWord(uint word, uint yPos) {
+	Common::Rect srcRect(0, yPos, _words[word].bounds.right - _words[word].bounds.left,
+		yPos + _words[word].bounds.bottom - _words[word].bounds.top);
+	Common::Rect dstRect = _words[word].bounds;
+	dstRect.translate(_rect.left, _rect.top);
+	_vm->_gfx->copyAnimImageSectionToScreen(_resourceId, srcRect, dstRect);
+}
+
+void LBLiveTextItem::handleMouseDown(Common::Point pos) {
+	if (!_loaded || !_enabled || !_globalEnabled || _playing)
+		return LBItem::handleMouseDown(pos);
+
+	pos.x -= _rect.left;
+	pos.y -= _rect.top;
+
+	for (uint i = 0; i < _words.size(); i++) {
+		if (_words[i].bounds.contains(pos)) {
+			if (_currentWord != 0xFFFF) {
+				paletteUpdate(_currentWord, false);
+				_currentWord = 0xFFFF;
+			}
+			uint16 soundId = _words[i].soundId;
+			if (!soundId) {
+				// TODO: can we be smarter here, using timing?
+				warning("ignoring click due to no soundId");
+				return;
+			}
+			_currentWord = i;
+			_vm->playSound(this, soundId);
+			paletteUpdate(_currentWord, true);
+			return;
+		}
+	}
+
+	return LBItem::handleMouseDown(pos);
+}
+
+bool LBLiveTextItem::togglePlaying(bool playing, bool restart) {
+	if (!playing)
+		return LBItem::togglePlaying(playing, restart);
+	if (!_loaded || !_enabled || !_globalEnabled)
+		return _playing;
+
+	// TODO: handle this properly
+	_vm->_sound->stopSound();
+
+	_currentWord = 0xFFFF;
+	_currentPhrase = 0xFFFF;
+
+	return true;
+}
+
+void LBLiveTextItem::stop() {
+	// TODO: stop sound, refresh palette
+
+	LBItem::stop();
+}
+
+void LBLiveTextItem::notify(uint16 data, uint16 from) {
+	if (!_loaded || !_enabled || !_globalEnabled || !_playing)
+		return LBItem::notify(data, from);
+
+	if (_currentWord != 0xFFFF) {
+		// TODO: handle this properly
+		_vm->_sound->stopSound();
+		paletteUpdate(_currentWord, false);
+		_currentWord = 0xFFFF;
+	}
+
+	for (uint i = 0; i < _phrases.size(); i++) {
+		if (_phrases[i].highlightStart == data && _phrases[i].startId == from) {
+			debug(2, "Enabling phrase %d", i);
+			for (uint j = 0; j < _phrases[i].wordCount; j++) {
+				paletteUpdate(_phrases[i].wordStart + j, true);
+			}
+			_currentPhrase = i;
+			// TODO: not sure this is the correct logic
+			if (i == _phrases.size() - 1) {
+				_currentPhrase = 0xFFFF;
+				done(true);
+			}
+		} else if (_phrases[i].highlightEnd == data && _phrases[i].endId == from) {
+			debug(2, "Disabling phrase %d", i);
+			for (uint j = 0; j < _phrases[i].wordCount; j++) {
+				paletteUpdate(_phrases[i].wordStart + j, false);
+			}
+			_currentPhrase = 0xFFFF;
+		}
+	}
+
+	LBItem::notify(data, from);
+}
+
+LBItem *LBLiveTextItem::createClone() {
+	error("can't clone LBLiveTextItem");
+}
+
+LBPictureItem::LBPictureItem(MohawkEngine_LivingBooks *vm, LBPage *page, Common::Rect rect) : LBItem(vm, page, rect) {
+	debug(3, "new LBPictureItem");
+}
+
+void LBPictureItem::readData(uint16 type, uint16 size, Common::MemoryReadStreamEndian *stream) {
+	switch (type) {
+	case kLBSetDrawMode:
+		{
+		assert(size == 2);
+		// TODO: this probably sets whether points are always contained (0x10)
+		// or whether the bitmap contents are checked (00, or anything else?)
+		uint16 val = stream->readUint16();
+		debug(2, "LBPictureItem: kLBSetDrawMode: %04x", val);
+		}
+		break;
+
+	default:
+		LBItem::readData(type, size, stream);
+	}
+}
+
+bool LBPictureItem::contains(Common::Point point) {
+	if (!LBItem::contains(point))
+		return false;
+
+	if (!_doHitTest)
+		return true;
+
+	// TODO: only check pixels if necessary
+	return !_vm->_gfx->imageIsTransparentAt(_resourceId, false, point.x - _rect.left, point.y - _rect.top);
+}
+
+void LBPictureItem::init() {
+	_vm->_gfx->preloadImage(_resourceId);
+
+	LBItem::init();
+}
+
+void LBPictureItem::draw() {
+	if (!_loaded || !_visible || !_globalVisible)
+		return;
+
+	_vm->_gfx->copyAnimImageToScreen(_resourceId, _rect.left, _rect.top);
+}
+
+LBItem *LBPictureItem::createClone() {
+	return new LBPictureItem(_vm, _page, _rect);
+}
+
+LBMovieItem::LBMovieItem(MohawkEngine_LivingBooks *vm, LBPage *page, Common::Rect rect) : LBItem(vm, page, rect) {
+	debug(3, "new LBMovieItem");
+}
+
+LBMovieItem::~LBMovieItem() {
+}
+
+void LBMovieItem::update() {
+	if (_playing) {
+		VideoEntryPtr video = _vm->_video->findVideo(_resourceId);
+		if (!video || video->endOfVideo())
+			done(true);
+	}
+
+	LBItem::update();
+}
+
+bool LBMovieItem::togglePlaying(bool playing, bool restart) {
+	if (playing) {
+		if ((_loaded && _enabled && _globalEnabled) || _phase == kLBPhaseNone) {
+			debug("toggled video for phase %d", _phase);
+			VideoEntryPtr video = _vm->_video->playMovie(_resourceId);
+			if (!video)
+				error("Failed to open tMOV %d", _resourceId);
+
+			video->moveTo(_rect.left, _rect.top);
+			return true;
+		}
+	}
+
+	return LBItem::togglePlaying(playing, restart);
+}
+
+LBItem *LBMovieItem::createClone() {
+	return new LBMovieItem(_vm, _page, _rect);
+}
+
+LBMiniGameItem::LBMiniGameItem(MohawkEngine_LivingBooks *vm, LBPage *page, Common::Rect rect) : LBItem(vm, page, rect) {
+	debug(3, "new LBMiniGameItem");
+}
+
+LBMiniGameItem::~LBMiniGameItem() {
+}
+
+bool LBMiniGameItem::togglePlaying(bool playing, bool restart) {
+	// HACK: Since we don't support any of these hardcoded mini games yet,
+	// just skip to the most logical page. For optional minigames, this
+	// will return the player to the previous page. For mandatory minigames,
+	// this will send the player to the next page.
+
+	uint16 destPage = 0;
+	bool returnToMenu = false;
+
+	// Figure out what minigame we have and bring us back to a page where
+	// the player can continue
+	if (_desc == "Kitch")     // Green Eggs and Ham: Kitchen minigame
+		destPage = 4;
+	else if (_desc == "Eggs") // Green Eggs and Ham: Eggs minigame
+		destPage = 5;
+	else if (_desc == "Fall") // Green Eggs and Ham: Fall minigame
+		destPage = 13;
+	else if (_desc == "MagicWrite3") // Arthur's Reading Race: "Let Me Write" minigame (Page 3)
+		destPage = 3;
+	else if (_desc == "MagicWrite4") // Arthur's Reading Race: "Let Me Write" minigame (Page 4)
+		destPage = 4;
+	else if (_desc == "MagicSpy5") // Arthur's Reading Race: "I Spy" minigame (Page 5)
+		destPage = 5;
+	else if (_desc == "MagicSpy6") // Arthur's Reading Race: "I Spy" minigame (Page 6)
+		destPage = 6;
+	else if (_desc == "MagicWrite7") // Arthur's Reading Race: "Let Me Write" minigame (Page 7)
+		destPage = 7;
+	else if (_desc == "MagicSpy8") // Arthur's Reading Race: "I Spy" minigame (Page 8)
+		destPage = 8;
+	else if (_desc == "MagicRace") // Arthur's Reading Race: Race minigame
+		returnToMenu = true;
+	else
+		error("Unknown minigame '%s'", _desc.c_str());
+
+	GUI::MessageDialog dialog(Common::String::format("The '%s' minigame is not supported yet.", _desc.c_str()));
+	dialog.runModal();
+
+	// Go back to the menu if requested, otherwise go to the requested page
+	if (returnToMenu)
+		_vm->addNotifyEvent(NotifyEvent(kLBNotifyGoToControls, 1));
+	else
+		_vm->addNotifyEvent(NotifyEvent(kLBNotifyChangePage, destPage));
+
+	return false;
+}
+
+LBItem *LBMiniGameItem::createClone() {
+	error("can't clone LBMiniGameItem");
+}
+
+LBProxyItem::LBProxyItem(MohawkEngine_LivingBooks *vm, LBPage *page, Common::Rect rect) : LBItem(vm, page, rect) {
+	debug(3, "new LBProxyItem");
+
+	_page = nullptr;
+}
+
+LBProxyItem::~LBProxyItem() {
+	delete _page;
+}
+
+void LBProxyItem::load() {
+	if (_loaded)
+		return;
+
+	Common::String leftover;
+	Common::String filename = _vm->getFileNameFromConfig("Proxies", _desc.c_str(), leftover);
+	if (!leftover.empty())
+		error("LBProxyItem tried loading proxy '%s' but got leftover '%s'", _desc.c_str(), leftover.c_str());
+	uint16 baseId = 0;
+	for (uint i = 0; i < filename.size(); i++) {
+		if (filename[i] == ';') {
+			baseId = atoi(filename.c_str() + i + 1);
+			filename = Common::String(filename.c_str(), i);
+		}
+	}
+
+	debug(1, "LBProxyItem loading archive '%s' with id %d", filename.c_str(), baseId);
+	Archive *pageArchive = _vm->createArchive();
+	if (!tryOpenPage(pageArchive, filename))
+		error("failed to open archive '%s' (for proxy '%s')", filename.c_str(), _desc.c_str());
+	_page = new LBPage(_vm);
+	_page->open(pageArchive, baseId);
+
+	LBItem::load();
+}
+
+void LBProxyItem::unload() {
+	delete _page;
+	_page = nullptr;
+
+	LBItem::unload();
+}
+
+LBItem *LBProxyItem::createClone() {
+	return new LBProxyItem(_vm, _page, _rect);
+}
+
+} // End of namespace Mohawk
diff --git a/engines/mohawk/livingbooks_menus.cpp b/engines/mohawk/livingbooks_menus.cpp
new file mode 100644
index 00000000000..0764364ce1c
--- /dev/null
+++ b/engines/mohawk/livingbooks_menus.cpp
@@ -0,0 +1,307 @@
+/* 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 "mohawk/livingbooks.h"
+
+#include "common/textconsole.h"
+
+namespace Mohawk {
+
+void MohawkEngine_LivingBooks::handleUIMenuClick(uint controlId) {
+	LBItem *item;
+
+	switch (controlId) {
+	case 1:
+		if (getFeatures() & GF_LB_10) {
+			if (!tryLoadPageStart(kLBControlMode, 2))
+				error("couldn't load options page");
+		} else {
+			if (!tryLoadPageStart(kLBControlMode, 3))
+				error("couldn't load options page");
+		}
+		break;
+
+	case 2:
+		item = getItemById(10);
+		if (item)
+			item->destroySelf();
+		item = getItemById(11);
+		if (item)
+			item->destroySelf();
+		item = getItemById(199 + _curLanguage);
+		if (item) {
+			item->setVisible(true);
+			item->togglePlaying(false, true);
+		}
+		break;
+
+	case 3:
+		item = getItemById(10);
+		if (item)
+			item->destroySelf();
+		item = getItemById(11);
+		if (item)
+			item->destroySelf();
+		item = getItemById(12);
+		if (item) {
+			item->setVisible(true);
+			item->togglePlaying(false, true);
+		}
+		break;
+
+	case 4:
+		if (getFeatures() & GF_LB_10) {
+			if (!tryLoadPageStart(kLBControlMode, 3))
+				error("couldn't load quit page");
+		} else {
+			if (!tryLoadPageStart(kLBControlMode, 2))
+				error("couldn't load quit page");
+		}
+		break;
+
+	case 10:
+		item = getItemById(10);
+		if (item)
+			item->destroySelf();
+		item = getItemById(11);
+		if (item) {
+			item->setVisible(true);
+			item->togglePlaying(false);
+		}
+		break;
+
+	case 11:
+		item = getItemById(11);
+		if (item)
+			item->togglePlaying(false, true);
+		break;
+
+	case 12:
+		// start game, in play mode
+		if (!tryLoadPageStart(kLBPlayMode, 1))
+			error("couldn't start play mode");
+		break;
+
+	default:
+		if (controlId >= 100 && controlId < 100 + (uint)_numLanguages) {
+			uint newLanguage = controlId - 99;
+			if (newLanguage == _curLanguage)
+				break;
+			item = getItemById(99 + _curLanguage);
+			if (item)
+				item->seek(1);
+			_curLanguage = newLanguage;
+		} else if (controlId >= 200 && controlId < 200 + (uint)_numLanguages) {
+			// start game, in read mode
+			if (!tryLoadPageStart(kLBReadMode, 1))
+				error("couldn't start read mode");
+		}
+		break;
+	}
+}
+
+void MohawkEngine_LivingBooks::handleUIPoetryMenuClick(uint controlId) {
+	LBItem *item;
+
+	// the menu UI in New Kid on the Block is a hybrid of the normal menu
+	// and the normal options screen
+
+	// TODO: this is mostly untested
+
+	switch (controlId) {
+	case 2:
+	case 3:
+		handleUIOptionsClick(controlId);
+		break;
+
+	case 4:
+		handleUIMenuClick(controlId);
+		break;
+
+	case 6:
+		handleUIMenuClick(2);
+		break;
+
+	case 7:
+		item = getItemById(10);
+		if (item)
+			item->destroySelf();
+		item = getItemById(11);
+		if (item)
+			item->destroySelf();
+		item = getItemById(12);
+		if (item) {
+			item->setVisible(true);
+			item->togglePlaying(false, true);
+		}
+		break;
+
+	case 0xA:
+		item = getItemById(10);
+		if (item)
+			item->destroySelf();
+		item = getItemById(11);
+		if (item) {
+			item->setVisible(true);
+			item->togglePlaying(false);
+		}
+		break;
+
+	case 0xB:
+		item = getItemById(11);
+		if (item)
+			item->togglePlaying(false, true);
+		break;
+
+	case 0xC:
+		if (!tryLoadPageStart(kLBPlayMode, _curSelectedPage))
+			error("failed to load page %d", _curSelectedPage);
+		break;
+
+	default:
+		if (controlId < 100) {
+			handleUIMenuClick(controlId);
+		} else {
+			if (!tryLoadPageStart(kLBReadMode, _curSelectedPage))
+				error("failed to load page %d", _curSelectedPage);
+		}
+	}
+}
+
+void MohawkEngine_LivingBooks::handleUIQuitClick(uint controlId) {
+	LBItem *item;
+
+	switch (controlId) {
+	case 1:
+	case 2:
+		// button clicked, run animation
+		item = getItemById(10);
+		if (item)
+			item->destroySelf();
+		item = getItemById(11);
+		if (item)
+			item->destroySelf();
+		item = getItemById((controlId == 1) ? 12 : 13);
+		if (item) {
+			item->setVisible(true);
+			item->togglePlaying(false);
+		}
+		break;
+
+	case 10:
+	case 11:
+		item = getItemById(11);
+		if (item)
+			item->togglePlaying(false, true);
+		break;
+
+	case 12:
+		// 'yes', I want to quit
+		quitGame();
+		break;
+
+	case 13:
+		// 'no', go back to menu
+		if (!tryLoadPageStart(kLBControlMode, 1))
+			error("couldn't return to menu");
+		break;
+
+	default:
+		break;
+	}
+}
+
+void MohawkEngine_LivingBooks::handleUIOptionsClick(uint controlId) {
+	LBItem *item;
+
+	switch (controlId) {
+	case 1:
+		item = getItemById(10);
+		if (item)
+			item->destroySelf();
+		item = getItemById(202);
+		if (item) {
+			item->setVisible(true);
+			item->togglePlaying(false, true);
+		}
+		break;
+
+	case 2:
+		// back
+		item = getItemById(2);
+		if (item)
+			item->seek(1);
+		if (_curSelectedPage == 1) {
+			_curSelectedPage = _numPages;
+		} else {
+			_curSelectedPage--;
+		}
+		for (uint i = 0; i < _numPages; i++) {
+			item = getItemById(1000 + i);
+			if (item)
+				item->setVisible(_curSelectedPage == i + 1);
+			item = getItemById(1100 + i);
+			if (item)
+				item->setVisible(_curSelectedPage == i + 1);
+		}
+		break;
+
+	case 3:
+		// forward
+		item = getItemById(3);
+		if (item)
+			item->seek(1);
+		if (_curSelectedPage == _numPages) {
+			_curSelectedPage = 1;
+		} else {
+			_curSelectedPage++;
+		}
+		for (uint i = 0; i < _numPages; i++) {
+			item = getItemById(1000 + i);
+			if (item)
+				item->setVisible(_curSelectedPage == i + 1);
+			item = getItemById(1100 + i);
+			if (item)
+				item->setVisible(_curSelectedPage == i + 1);
+		}
+		break;
+
+	case 4:
+		if (!tryLoadPageStart(kLBCreditsMode, 1))
+			error("failed to start credits");
+		break;
+
+	case 5:
+		if (!tryLoadPageStart(kLBPreviewMode, 1))
+			error("failed to start preview");
+		break;
+
+	case 202:
+		if (!tryLoadPageStart(kLBPlayMode, _curSelectedPage))
+			error("failed to load page %d", _curSelectedPage);
+		break;
+
+	default:
+		break;
+	}
+}
+
+} // End of namespace Mohawk
diff --git a/engines/mohawk/livingbooks_page.cpp b/engines/mohawk/livingbooks_page.cpp
new file mode 100644
index 00000000000..775a82cc088
--- /dev/null
+++ b/engines/mohawk/livingbooks_page.cpp
@@ -0,0 +1,153 @@
+/* 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 "mohawk/livingbooks.h"
+#include "mohawk/resource.h"
+
+#include "common/textconsole.h"
+
+namespace Mohawk {
+
+LBPage::LBPage(MohawkEngine_LivingBooks *vm) : _vm(vm) {
+	_code = nullptr;
+	_mhk = nullptr;
+
+	_baseId = 0;
+	_cascade = false;
+}
+
+void LBPage::open(Archive *mhk, uint16 baseId) {
+	_mhk = mhk;
+	_baseId = baseId;
+
+	_vm->addArchive(_mhk);
+	if (!_vm->hasResource(ID_BCOD, baseId)) {
+		// assume that BCOD is mandatory for v4/v5
+		if (_vm->getGameType() == GType_LIVINGBOOKSV4 || _vm->getGameType() == GType_LIVINGBOOKSV5)
+			error("missing BCOD resource (id %d)", baseId);
+		_code = new LBCode(_vm, 0);
+	} else {
+		_code = new LBCode(_vm, baseId);
+	}
+
+	loadBITL(baseId);
+	for (uint i = 0; i < _items.size(); i++)
+		_vm->addItem(_items[i]);
+
+	for (uint32 i = 0; i < _items.size(); i++)
+		_items[i]->init();
+
+	for (uint32 i = 0; i < _items.size(); i++)
+		_items[i]->startPhase(kLBPhaseLoad);
+}
+
+void LBPage::addClonedItem(LBItem *item) {
+	_vm->addItem(item);
+	_items.push_back(item);
+}
+
+void LBPage::itemDestroyed(LBItem *item) {
+	for (uint i = 0; i < _items.size(); i++)
+		if (item == _items[i]) {
+			_items.remove_at(i);
+			return;
+		}
+	error("itemDestroyed didn't find item");
+}
+
+LBPage::~LBPage() {
+	delete _code;
+	_vm->removeItems(_items);
+	for (uint i = 0; i < _items.size(); i++)
+		delete _items[i];
+	_vm->removeArchive(_mhk);
+	delete _mhk;
+}
+
+// Only 1 VSRN resource per page
+uint16 LBPage::getResourceVersion() {
+	Common::SeekableReadStream *versionStream = _vm->getResource(ID_VRSN, _baseId);
+
+	// FIXME: some V2 games have very strange version entries
+	if (versionStream->size() != 2)
+		debug(1, "Version Record size mismatch");
+
+	uint16 version = versionStream->readUint16BE();
+
+	delete versionStream;
+	return version;
+}
+
+void LBPage::loadBITL(uint16 resourceId) {
+	Common::SeekableReadStreamEndian *bitlStream = _vm->wrapStreamEndian(ID_BITL, resourceId);
+
+	while (true) {
+		Common::Rect rect = _vm->readRect(bitlStream);
+		uint16 type = bitlStream->readUint16();
+
+		LBItem *res;
+		switch (type) {
+		case kLBPictureItem:
+			res = new LBPictureItem(_vm, this, rect);
+			break;
+		case kLBAnimationItem:
+			res = new LBAnimationItem(_vm, this, rect);
+			break;
+		case kLBPaletteItem:
+			res = new LBPaletteItem(_vm, this, rect);
+			break;
+		case kLBGroupItem:
+			res = new LBGroupItem(_vm, this, rect);
+			break;
+		case kLBSoundItem:
+			res = new LBSoundItem(_vm, this, rect);
+			break;
+		case kLBLiveTextItem:
+			res = new LBLiveTextItem(_vm, this, rect);
+			break;
+		case kLBMovieItem:
+			res = new LBMovieItem(_vm, this, rect);
+			break;
+		case kLBMiniGameItem:
+			res = new LBMiniGameItem(_vm, this, rect);
+			break;
+		case kLBProxyItem:
+			res = new LBProxyItem(_vm, this, rect);
+			break;
+		default:
+			warning("Unknown item type %04x", type);
+			// fall through
+		case 3: // often used for buttons
+			res = new LBItem(_vm, this, rect);
+			break;
+		}
+
+		res->readFrom(bitlStream);
+		_items.push_back(res);
+
+		if (bitlStream->size() == bitlStream->pos())
+			break;
+	}
+
+	delete bitlStream;
+}
+
+} // End of namespace Mohawk
diff --git a/engines/mohawk/module.mk b/engines/mohawk/module.mk
index edb3923172f..b264987cae7 100644
--- a/engines/mohawk/module.mk
+++ b/engines/mohawk/module.mk
@@ -8,9 +8,14 @@ MODULE_OBJS = \
 	graphics.o \
 	installer_archive.o \
 	livingbooks.o \
+	livingbooks_animation.o \
+	livingbooks_bookinfo.o \
 	livingbooks_code.o \
 	livingbooks_graphics.o \
+	livingbooks_itemtypes.o \
 	livingbooks_lbx.o \
+	livingbooks_menus.o \
+	livingbooks_page.o \
 	metaengine.o \
 	riven_metaengine.o \
 	myst_metaengine.o \


Commit: 7d5666a9f5619e70726468ae2f61f6c8556b4648
    https://github.com/scummvm/scummvm/commit/7d5666a9f5619e70726468ae2f61f6c8556b4648
Author: Alstruit (34786806+Alstruit at users.noreply.github.com)
Date: 2026-06-14T13:00:37+03:00

Commit Message:
MOHAWK: LB - move tryOpenPage function to header

Duplicate function.

Changed paths:
    engines/mohawk/livingbooks.cpp
    engines/mohawk/livingbooks.h


diff --git a/engines/mohawk/livingbooks.cpp b/engines/mohawk/livingbooks.cpp
index fdcd4f1e2bd..9c186ec7eca 100644
--- a/engines/mohawk/livingbooks.cpp
+++ b/engines/mohawk/livingbooks.cpp
@@ -285,7 +285,7 @@ static Common::String replaceColons(const Common::String &in, char replace) {
 }
 
 // Helper function to assist in opening pages
-static bool tryOpenPage(Archive *archive, const Common::String &fileName) {
+bool tryOpenPage(Archive *archive, const Common::String &fileName) {
 	// Try the plain file name first
 	if (archive->openFile(Common::Path(fileName)))
 		return true;
diff --git a/engines/mohawk/livingbooks.h b/engines/mohawk/livingbooks.h
index 7374c7199fd..a9819b3db25 100644
--- a/engines/mohawk/livingbooks.h
+++ b/engines/mohawk/livingbooks.h
@@ -262,6 +262,8 @@ class LBPage;
 class LBGraphics;
 class LBAnimation;
 
+bool tryOpenPage(Archive *archive, const Common::String &fileName);
+
 struct LBScriptEntry {
 	LBScriptEntry();
 	~LBScriptEntry();


Commit: 0d527c60b8029770ffccdae3631efa7eff19b5dc
    https://github.com/scummvm/scummvm/commit/0d527c60b8029770ffccdae3631efa7eff19b5dc
Author: Alstruit (34786806+Alstruit at users.noreply.github.com)
Date: 2026-06-14T13:00:37+03:00

Commit Message:
MOHAWK: LB - Cleanup ~4000 LoC in main LB (II).

Create livingbooks_constants.h to handle lots of kLBs.
Move LBItem respectively. Move LBSCriptEntry to itemscript.
Define header files: item,itemscript,itemtypes,aniation,page.

Changed paths:
  A engines/mohawk/livingbooks_animation.h
  A engines/mohawk/livingbooks_constants.h
  A engines/mohawk/livingbooks_item.cpp
  A engines/mohawk/livingbooks_item.h
  A engines/mohawk/livingbooks_itemscript.cpp
  A engines/mohawk/livingbooks_itemscript.h
  A engines/mohawk/livingbooks_itemtypes.h
  A engines/mohawk/livingbooks_page.h
    engines/mohawk/livingbooks.cpp
    engines/mohawk/livingbooks.h
    engines/mohawk/module.mk


diff --git a/engines/mohawk/livingbooks.cpp b/engines/mohawk/livingbooks.cpp
index 9c186ec7eca..8aa6688eec1 100644
--- a/engines/mohawk/livingbooks.cpp
+++ b/engines/mohawk/livingbooks.cpp
@@ -917,1120 +917,4 @@ void MohawkEngine_LivingBooks::handleNotify(NotifyEvent &event) {
 	}
 }
 
-LBScriptEntry::LBScriptEntry() {
-	state = 0;
-	data = nullptr;
-	argvParam = nullptr;
-	argvTarget = nullptr;
-}
-
-LBScriptEntry::~LBScriptEntry() {
-	delete[] argvParam;
-	delete[] argvTarget;
-	delete[] data;
-
-	for (uint i = 0; i < subentries.size(); i++)
-		delete subentries[i];
-}
-
-LBItem::LBItem(MohawkEngine_LivingBooks *vm, LBPage *page, Common::Rect rect) : _vm(vm), _page(page), _rect(rect) {
-	if (_vm->getGameType() == GType_LIVINGBOOKSV1 || _vm->getGameType() == GType_LIVINGBOOKSV2)
-		_phase = kLBPhaseInit;
-	else
-		_phase = kLBPhaseLoad;
-
-	_loopMode = 0;
-	_delayMin = 0;
-	_delayMax = 0;
-	_timingMode = kLBAutoNone;
-	_periodMin = 0;
-	_periodMax = 0;
-	_controlMode = kLBControlNone;
-	_soundMode = 0;
-
-	_loaded = false;
-	_enabled = false;
-	_visible = true;
-	_playing = false;
-	_globalEnabled = true;
-	_globalVisible = true;
-	_nextTime = 0;
-	_startTime = 0;
-	_loops = 0;
-
-	_isAmbient = false;
-	_doHitTest = true;
-}
-
-LBItem::~LBItem() {
-	for (uint i = 0; i < _scriptEntries.size(); i++)
-		delete _scriptEntries[i];
-}
-
-void LBItem::readFrom(Common::SeekableReadStreamEndian *stream) {
-	_resourceId = stream->readUint16();
-	_itemId = stream->readUint16();
-	uint16 size = stream->readUint16();
-	_desc = _vm->readString(stream);
-
-	debug(2, "Item: size %d, resource %d, id %d", size, _resourceId, _itemId);
-	debug(2, "Coords: %d, %d, %d, %d", _rect.left, _rect.top, _rect.right, _rect.bottom);
-	debug(2, "String: '%s'", _desc.c_str());
-
-	if (!_itemId)
-		error("Item had invalid item id");
-
-	int endPos = stream->pos() + size;
-	if (endPos > stream->size())
-		error("Item is larger (should end at %d) than stream (size %d)", endPos, (int)stream->size());
-
-	while (true) {
-		if (stream->pos() == endPos)
-			break;
-
-		uint oldPos = stream->pos();
-
-		uint16 dataType = stream->readUint16();
-		uint16 dataSize = stream->readUint16();
-
-		debug(4, "Data type %04x, size %d", dataType, dataSize);
-		byte *buf = new byte[dataSize];
-		stream->read(buf, dataSize);
-		readData(dataType, dataSize, buf);
-		delete[] buf;
-
-		if ((uint)stream->pos() != oldPos + 4 + (uint)dataSize)
-			error("Failed to read correct number of bytes (off by %d) for data type %04x (size %d)",
-				(int)stream->pos() - (int)(oldPos + 4 + (uint)dataSize), dataType, dataSize);
-
-		if (stream->pos() > endPos)
-			error("Read off the end (at %d) of data (ends at %d)", (int)stream->pos(), endPos);
-
-		assert(!stream->eos());
-	}
-}
-
-LBScriptEntry *LBItem::parseScriptEntry(uint16 type, uint16 &size, Common::MemoryReadStreamEndian *stream, bool isSubentry) {
-	if (size < 6)
-		error("Script entry of type 0x%04x was too small (%d)", type, size);
-
-	uint16 expectedEndSize = 0;
-
-	LBScriptEntry *entry = new LBScriptEntry;
-	entry->type = type;
-	if (isSubentry) {
-		expectedEndSize = size - (stream->readUint16() + 2);
-		entry->event = 0xffff;
-	} else
-		entry->event = stream->readUint16();
-	entry->opcode = stream->readUint16();
-	entry->param = stream->readUint16();
-	debug(4, "Script entry: type 0x%04x, event 0x%04x, opcode 0x%04x, param 0x%04x",
-			entry->type, entry->event, entry->opcode, entry->param);
-	size -= 6;
-
-	// TODO: read as bytes, if this is correct (but beware endianism)
-	byte conditionTag = (entry->event & 0xff00) >> 8;
-	entry->event = entry->event & 0xff;
-
-	if (type == kLBMsgListScript && entry->opcode == kLBOpRunSubentries) {
-		debug(4, "%d script subentries:", entry->param);
-		entry->argc = 0;
-		for (uint i = 0; i < entry->param; i++) {
-			LBScriptEntry *subentry = parseScriptEntry(type, size, stream, true);
-			entry->subentries.push_back(subentry);
-
-			// subentries are aligned
-			if (i + 1 < entry->param && size % 2 == 1) {
-				stream->skip(1);
-				size--;
-			}
-		}
-	} else if (type == kLBMsgListScript) {
-		if (size < 2)
-			error("Script entry of type 0x%04x was too small (%d)", type, size);
-
-		entry->argc = stream->readUint16();
-		size -= 2;
-
-		entry->targetingType = 0;
-
-		uint16 targetingType = entry->argc;
-		if (targetingType == kTargetTypeExpression || targetingType == kTargetTypeCode
-			|| targetingType == kTargetTypeName) {
-			entry->targetingType = targetingType;
-
-			// FIXME
-			if (targetingType == kTargetTypeCode)
-				error("encountered kTargetTypeCode");
-
-			if (size < 2)
-				error("not enough bytes (%d) reading special targeting", size);
-			uint16 count = stream->readUint16();
-			size -= 2;
-
-			debug(4, "%d targets with targeting type %04x", count, targetingType);
-
-			uint oldAlign = size % 2;
-			for (uint i = 0; i < count; i++) {
-				Common::String target = _vm->readString(stream);
-				debug(4, "target '%s'", target.c_str());
-				entry->targets.push_back(target);
-				if (target.size() + 1 > size)
-					error("failed to read target (ran out of stream)");
-				size -= target.size() + 1;
-			}
-			entry->argc = entry->targets.size();
-
-			if ((uint)(size % 2) != oldAlign) {
-				stream->skip(1);
-				size--;
-			}
-		} else if (entry->argc) {
-			entry->argvParam = new uint16[entry->argc];
-			entry->argvTarget = new uint16[entry->argc];
-			debug(4, "With %d targets:", entry->argc);
-
-			if (size < (entry->argc * 4))
-				error("Script entry of type 0x%04x was too small (%d)", type, size);
-
-			for (uint i = 0; i < entry->argc; i++) {
-				entry->argvParam[i] = stream->readUint16();
-				entry->argvTarget[i] = stream->readUint16();
-				debug(4, "Target %d, param 0x%04x", entry->argvTarget[i], entry->argvParam[i]);
-			}
-
-			size -= (entry->argc * 4);
-		}
-	}
-
-	if (type == kLBMsgListScript && entry->opcode == kLBOpJumpUnlessExpression) {
-		if (size < 6)
-			error("not enough bytes (%d) in kLBOpJumpUnlessExpression, event 0x%04x", size, entry->event);
-		entry->offset = stream->readUint32();
-		entry->target = stream->readUint16();
-		debug(4, "kLBOpJumpUnlessExpression: offset %08x, target %d", entry->offset, entry->target);
-		size -= 6;
-	}
-	if (type == kLBMsgListScript && entry->opcode == kLBOpJumpToExpression) {
-		if (size < 4)
-			error("not enough bytes (%d) in kLBOpJumpToExpression, event 0x%04x", size, entry->event);
-		entry->offset = stream->readUint32();
-		debug(4, "kLBOpJumpToExpression: offset %08x", entry->offset);
-		size -= 4;
-	}
-
-	if (type == kLBNotifyScript && entry->opcode == kLBNotifyChangeMode && _vm->getGameType() != GType_LIVINGBOOKSV1) {
-		switch (entry->param) {
-		case 1:
-			if (size < 8)
-				error("%d unknown bytes in notify entry kLBNotifyChangeMode", size);
-			entry->newUnknown = stream->readUint16();
-			entry->newMode = stream->readUint16();
-			entry->newPage = stream->readUint16();
-			entry->newSubpage = stream->readUint16();
-			debug(4, "kLBNotifyChangeMode: unknown %04x, mode %d, page %d.%d",
-				entry->newUnknown, entry->newMode, entry->newPage, entry->newSubpage);
-			size -= 8;
-			break;
-		case 3:
-			{
-			Common::String newCursor = _vm->readString(stream);
-			entry->newCursor = newCursor;
-			if (size < newCursor.size() + 1)
-				error("failed to read newCursor in notify entry");
-			size -= newCursor.size() + 1;
-			debug(4, "kLBNotifyChangeMode: new cursor '%s'", newCursor.c_str());
-			}
-			break;
-		default:
-			// the original engine also does something when param==2 (but not a notify)
-			error("unknown v2 kLBNotifyChangeMode type %d", entry->param);
-		}
-	}
-	if (entry->opcode == kLBOpSendExpression) {
-		if (size < 4)
-			error("not enough bytes (%d) in kLBOpSendExpression, event 0x%04x", size, entry->event);
-		entry->offset = stream->readUint32();
-		debug(4, "kLBOpSendExpression: offset %08x", entry->offset);
-		size -= 4;
-	}
-	if (entry->opcode == kLBOpRunData) {
-		if (size < 4)
-			error("didn't get enough bytes (%d) to read data header in script entry", size);
-		entry->dataType = stream->readUint16();
-		entry->dataLen = stream->readUint16();
-		size -= 4;
-
-		if (size < entry->dataLen)
-			error("didn't get enough bytes (%d) to read data in script entry", size);
-
-		if (entry->dataType == kLBCommand) {
-			Common::String command = _vm->readString(stream);
-			uint commandSize = command.size() + 1;
-			if (commandSize > entry->dataLen)
-				error("failed to read command in script entry: dataLen %d, command '%s' (%d chars)",
-					 entry->dataLen, command.c_str(), commandSize);
-			entry->dataLen = commandSize;
-			entry->data = new byte[commandSize];
-			memcpy(entry->data, command.c_str(), commandSize);
-			size -= commandSize;
-		} else {
-			if (conditionTag)
-				error("kLBOpRunData had unexpected conditionTag");
-			entry->data = new byte[entry->dataLen];
-			stream->read(entry->data, entry->dataLen);
-			size -= entry->dataLen;
-		}
-	}
-	if (entry->event == kLBEventNotified) {
-		if (size < 4)
-			error("not enough bytes (%d) in kLBEventNotified, opcode 0x%04x", size, entry->opcode);
-		entry->matchFrom = stream->readUint16();
-		entry->matchNotify = stream->readUint16();
-		debug(4, "kLBEventNotified: matches %04x (from %04x)",
-			entry->matchNotify, entry->matchFrom);
-		size -= 4;
-	}
-
-	if (isSubentry) {
-		// TODO: subentries may be aligned, so this check is a bit too relaxed
-		if (size != expectedEndSize && size != expectedEndSize + 1)
-			error("expected %d bytes left at end of subentry, but had %d",
-				expectedEndSize, size);
-		return entry;
-	}
-
-	if (conditionTag == 1) {
-		if (!size)
-			error("failed to read condition (empty stream)");
-		Common::String condition = _vm->readString(stream);
-		if (condition.size() == 0) {
-			size--;
-			if (!size)
-				error("failed to read condition (null byte, then ran out of stream)");
-			condition = _vm->readString(stream);
-		}
-		if (condition.size() + 1 > size)
-			error("failed to read condition (ran out of stream)");
-		size -= (condition.size() + 1);
-
-		entry->conditions.push_back(condition);
-		debug(4, "script entry condition '%s'", condition.c_str());
-	} else if (conditionTag == 2) {
-		if (size < 4)
-			error("expected more than %d bytes for conditionTag 2", size);
-		// FIXME
-		stream->skip(4);
-		size -= 4;
-	}
-
-	if (size == 1) {
-		// FIXME: this is alignment, but why?
-		stream->skip(1);
-		size--;
-	} else if (size)
-		error("failed to read script entry correctly (%d bytes left): type 0x%04x, event 0x%04x, opcode 0x%04x, param 0x%04x",
-			size, entry->type, entry->event, entry->opcode, entry->param);
-
-	return entry;
-}
-
-void LBItem::readData(uint16 type, uint16 size, byte *data) {
-	Common::MemoryReadStreamEndian stream(data, size, _vm->isBigEndian());
-	readData(type, size, &stream);
-}
-
-void LBItem::readData(uint16 type, uint16 size, Common::MemoryReadStreamEndian *stream) {
-	switch (type) {
-	case kLBMsgListScript:
-	case kLBNotifyScript:
-		_scriptEntries.push_back(parseScriptEntry(type, size, stream));
-		break;
-
-	case kLBSetPlayInfo:
-		{
-		if (size != 20)
-			error("kLBSetPlayInfo had wrong size (%d)", size);
-
-		_loopMode = stream->readUint16();
-		_delayMin = stream->readUint16();
-		_delayMax = stream->readUint16();
-		_timingMode = stream->readUint16();
-		if (_timingMode > 7)
-			error("encountered timing mode %04x", _timingMode);
-		_periodMin = stream->readUint16();
-		_periodMax = stream->readUint16();
-		_relocPoint.x = stream->readSint16();
-		_relocPoint.y = stream->readSint16();
-		_controlMode = stream->readUint16();
-		_soundMode = stream->readUint16();
-
-		debug(2, "kLBSetPlayInfo: loop mode %d (%d to %d), timing mode %d (%d to %d), reloc (%d, %d), control mode %04x, sound mode %04x",
-			_loopMode, _delayMin, _delayMax,
-			_timingMode, _periodMin, _periodMax,
-			_relocPoint.x, _relocPoint.y,
-			_controlMode, _soundMode);
-		}
-		break;
-
-	case kLBSetPlayPhase:
-		if (size != 2)
-			error("SetPlayPhase had wrong size (%d)", size);
-		_phase = stream->readUint16();
-		debug(2, "kLBSetPlayPhase: %d", _phase);
-		break;
-
-	case kLBSetKeyNotify:
-		{
-		// FIXME: variable-size notifies, targets
-		if (size != 18)
-			error("0x6f had wrong size (%d)", size);
-		uint event = stream->readUint16();
-		LBKey key;
-		stream->read(&key, 4);
-		uint opcode = stream->readUint16();
-		uint param = stream->readUint16();
-		uint u6 = stream->readUint16();
-		uint u7 = stream->readUint16();
-		uint u8 = stream->readUint16();
-		uint u9 = stream->readUint16();
-		warning("ignoring kLBSetKeyNotify: item %s, key code %02x (modifier mask %d, char %d, repeat %d), event %04x, opcode %04x, param %04x, unknowns %04x, %04x, %04x, %04x",
-			_desc.c_str(), key.code, key.modifiers, key.char_, key.repeats, event, opcode, param, u6, u7, u8, u9);
-		}
-		break;
-
-	case kLBCommand:
-		{
-			Common::String command = _vm->readString(stream);
-			if (size != command.size() + 1)
-				error("failed to read command string");
-
-			runCommand(command);
-		}
-		break;
-
-	case kLBSetNotVisible:
-		assert(size == 0);
-		_visible = false;
-		break;
-
-	case kLBGlobalDisable:
-		assert(size == 0);
-		_globalEnabled = false;
-		break;
-
-	case kLBGlobalSetNotVisible:
-		assert(size == 0);
-		_globalVisible = false;
-		break;
-
-	case kLBSetAmbient:
-		assert(size == 0);
-		_isAmbient = true;
-		break;
-
-	case kLBSetKeyEvent:
-		{
-		// FIXME: targets
-		if (size != 10)
-			error("kLBSetKeyEvent had wrong size (%d)", size);
-		uint u3 = stream->readUint16();
-		LBKey key;
-		stream->read(&key, 4);
-		uint target = stream->readUint16();
-		uint16 event = stream->readUint16();
-		// FIXME: this is scripting stuff: what to run when key is pressed
-		warning("ignoring kLBSetKeyEvent: item %s, key code %02x (modifier mask %d, char %d, repeat %d) unknown %04x, target %d, event %04x",
-			_desc.c_str(), key.code, key.modifiers, key.char_, key.repeats, u3, target, event);
-		}
-		break;
-
-	case kLBSetHitTest:
-		{
-		assert(size == 2);
-		uint val = stream->readUint16();
-		_doHitTest = (bool)val;
-		debug(2, "kLBSetHitTest (on %s): value %04x", _desc.c_str(), val);
-		}
-		break;
-
-	case kLBSetRolloverData:
-		{
-		assert(size == 2);
-		uint16 flag = stream->readUint16();
-		warning("ignoring kLBSetRolloverData: item %s, flag %d", _desc.c_str(), flag);
-		}
-		break;
-
-	case kLBSetParent:
-		{
-		assert(size == 2);
-		uint16 parent = stream->readUint16();
-		warning("ignoring kLBSetParent: item %s, parent id %d", _desc.c_str(), parent);
-		}
-		break;
-
-	case kLBUnknown194:
-		{
-		assert(size == 4);
-		uint offset = stream->readUint32();
-		_page->_code->runCode(this, offset);
-		}
-		break;
-
-	default:
-		error("Unknown message %04x (size 0x%04x)", type, size);
-		//for (uint i = 0; i < size; i++)
-		//	debugN("%02x ", stream->readByte());
-		//debugN("\n");
-		break;
-	}
-}
-
-void LBItem::destroySelf() {
-	if (!this->_itemId)
-		error("destroySelf() on an item which was already dead");
-
-	_vm->queueDelayedEvent(DelayedEvent(this, kLBDelayedEventDestroy));
-
-	_itemId = 0;
-}
-
-void LBItem::setEnabled(bool enabled) {
-	if (enabled && !_loaded && !_playing) {
-		if (_timingMode == kLBAutoUserIdle) {
-			setNextTime(_periodMin, _periodMax);
-			debug(2, "Enable time startup");
-		}
-	}
-
-	_enabled = enabled;
-}
-
-void LBItem::setGlobalEnabled(bool enabled) {
-	bool wasEnabled = _loaded && _enabled && _globalEnabled;
-	_globalEnabled = enabled;
-	if (wasEnabled != (_loaded && _enabled && _globalEnabled))
-		setEnabled(enabled);
-}
-
-bool LBItem::contains(Common::Point point) {
-	if (!_loaded)
-		return false;
-
-	if (_playing && _loopMode == 0xFFFF)
-		stop();
-
-	if (!_playing && _timingMode == kLBAutoUserIdle)
-		setNextTime(_periodMin, _periodMax);
-
-	return _visible && _globalVisible && _rect.contains(point);
-}
-
-void LBItem::update() {
-	if (_phase != kLBPhaseNone && (!_loaded || !_enabled || !_globalEnabled))
-		return;
-
-	if (_nextTime == 0 || _nextTime > (uint32)(_vm->_system->getMillis() / 16))
-		return;
-
-	if (togglePlaying(_playing, true)) {
-		_nextTime = 0;
-	} else if (_loops == 0 && _timingMode == kLBAutoUserIdle) {
-		debug(9, "Looping in update()");
-		setNextTime(_periodMin, _periodMax);
-	}
-}
-
-void LBItem::handleMouseDown(Common::Point pos) {
-	if (!_loaded || !_enabled || !_globalEnabled)
-		return;
-
-	_vm->setFocus(this);
-	runScript(kLBEventMouseDown);
-	runScript(kLBEventMouseTrackIn);
-}
-
-void LBItem::handleMouseMove(Common::Point pos) {
-	// TODO: handle drag
-}
-
-void LBItem::handleMouseUp(Common::Point pos) {
-	_vm->setFocus(nullptr);
-	runScript(kLBEventMouseUp);
-	runScript(kLBEventMouseUpIn);
-}
-
-bool LBItem::togglePlaying(bool playing, bool restart) {
-	if (playing) {
-		_vm->queueDelayedEvent(DelayedEvent(this, kLBDelayedEventDone));
-		return true;
-	}
-	if (((_loaded && _enabled && _globalEnabled) || _phase == kLBPhaseNone) && !_playing) {
-		_playing = togglePlaying(true, restart);
-		if (_playing) {
-			_nextTime = 0;
-			_startTime = _vm->_system->getMillis() / 16;
-
-			if (_loopMode == 0xFFFF || _loopMode == 0xFFFE)
-				_loops = 0xFFFF;
-			else
-				_loops = _loopMode;
-
-			if (_controlMode >= kLBControlHideMouse) {
-				debug(2, "Hiding cursor");
-				_vm->_cursor->hideCursor();
-				_vm->lockSound(this, true);
-
-				if (_controlMode >= kLBControlPauseItems) {
-					debug(2, "Disabling all");
-					_vm->setEnableForAll(false, this);
-				}
-			}
-
-			runScript(kLBEventStarted);
-			notify(0, _itemId);
-		}
-	}
-	return _playing;
-}
-
-void LBItem::done(bool onlyNotify) {
-	if (onlyNotify) {
-		if (_relocPoint.x || _relocPoint.y) {
-			_rect.translate(_relocPoint.x, _relocPoint.y);
-			// TODO: does drag box need adjusting?
-		}
-
-		if (_loops && --_loops) {
-			debug(9, "Real looping (now 0x%04x left)", _loops);
-			setNextTime(_delayMin, _delayMax, _startTime);
-		} else
-			done(false);
-
-		return;
-	}
-
-	_playing = false;
-	_loops = 0;
-	_startTime = 0;
-
-	if (_controlMode >= kLBControlHideMouse) {
-		debug(2, "Showing cursor");
-		_vm->_cursor->showCursor();
-		_vm->lockSound(this, false);
-
-		if (_controlMode >= kLBControlPauseItems) {
-			debug(2, "Enabling all");
-			_vm->setEnableForAll(true, this);
-		}
-	}
-
-	if (_timingMode == kLBAutoUserIdle) {
-		debug(9, "Looping in done() - %d to %d", _periodMin, _periodMax);
-		setNextTime(_periodMin, _periodMax);
-	}
-
-	runScript(kLBEventDone);
-	notify(0xFFFF, _itemId);
-}
-
-void LBItem::init() {
-	runScript(kLBEventInit);
-}
-
-void LBItem::setVisible(bool visible) {
-	if (visible == _visible)
-		return;
-
-	_visible = visible;
-	_vm->_needsRedraw = true;
-}
-
-void LBItem::setGlobalVisible(bool visible) {
-	bool wasEnabled = _visible && _globalVisible;
-	_globalVisible = visible;
-	if (wasEnabled != (_visible && _globalVisible))
-		_vm->_needsRedraw = true;
-}
-
-void LBItem::startPhase(uint phase) {
-	if (_phase == phase) {
-		if (_phase != kLBPhaseNone) {
-			setEnabled(true);
-		}
-
-		load();
-	}
-
-	switch (phase) {
-	case kLBPhaseLoad:
-		runScript(kLBEventListLoad);
-		break;
-	case kLBPhaseCreate:
-		runScript(kLBEventPhaseCreate);
-		if (_timingMode == kLBAutoCreate) {
-			debug(2, "Phase create: time startup");
-			setNextTime(_periodMin, _periodMax);
-		}
-		break;
-	case kLBPhaseInit:
-		runScript(kLBEventPhaseInit);
-		if (_timingMode == kLBAutoInit) {
-			debug(2, "Phase init: time startup");
-			setNextTime(_periodMin, _periodMax);
-		}
-		break;
-	case kLBPhaseIntro:
-		runScript(kLBEventPhaseIntro);
-		if (_timingMode == kLBAutoIntro || _timingMode == kLBAutoUserIdle) {
-			debug(2, "Phase intro: time startup");
-			setNextTime(_periodMin, _periodMax);
-		}
-		break;
-	case kLBPhaseMain:
-		runScript(kLBEventPhaseMain);
-		if (_timingMode == kLBAutoUserIdle || _timingMode == kLBAutoMain) {
-			debug(2, "Phase main: time startup");
-			setNextTime(_periodMin, _periodMax);
-		}
-		break;
-	default:
-		break;
-	}
-}
-
-void LBItem::stop() {
-	if (!_playing)
-		return;
-
-	_loops = 0;
-	seek(0xFFFF);
-	done(true);
-}
-
-void LBItem::notify(uint16 data, uint16 from) {
-	if (_timingMode == kLBAutoSync) {
-		// TODO: is this correct?
-		if (_periodMin == data && _periodMax == from) {
-			debug(2, "Handling notify 0x%04x (from %d)", data, from);
-			setNextTime(0, 0);
-		}
-	}
-
-	runScript(kLBEventNotified, data, from);
-}
-
-void LBItem::load() {
-	if (_loaded)
-		return;
-
-	_loaded = true;
-
-	// FIXME: events etc
-	if (_timingMode == kLBAutoLoad) {
-		debug(2, "Load: time startup");
-		setNextTime(_periodMin, _periodMax);
-	}
-}
-
-void LBItem::unload() {
-	if (!_loaded)
-		return;
-
-	_loaded = false;
-
-	// FIXME: stuff
-}
-
-void LBItem::moveBy(const Common::Point &pos) {
-	_rect.translate(pos.x, pos.y);
-}
-
-void LBItem::moveTo(const Common::Point &pos) {
-	_rect.moveTo(pos);
-}
-
-LBItem *LBItem::clone(uint16 newId, const Common::String &newName) {
-	LBItem *item = createClone();
-
-	item->_itemId = newId;
-	item->_desc = newName;
-
-	item->_resourceId = _resourceId;
-	// FIXME: the rest
-
-	_page->addClonedItem(item);
-	// FIXME: zorder?
-	return item;
-}
-
-LBItem *LBItem::createClone() {
-	return new LBItem(_vm, _page, _rect);
-}
-
-void LBItem::runScript(uint event, uint16 data, uint16 from) {
-	for (uint i = 0; i < _scriptEntries.size(); i++) {
-		LBScriptEntry *entry = _scriptEntries[i];
-
-		if (entry->event != event)
-			continue;
-
-		if (event == kLBEventNotified) {
-			if ((entry->matchFrom && entry->matchFrom != from) || entry->matchNotify != data)
-				continue;
-		}
-
-		bool conditionsMatch = true;
-		for (uint n = 0; n < entry->conditions.size(); n++) {
-			if (!checkCondition(entry->conditions[n])) {
-				conditionsMatch = false;
-				break;
-			}
-		}
-		if (!conditionsMatch)
-			continue;
-
-		if (entry->type == kLBNotifyScript) {
-			debug(2, "Notify: event 0x%04x, opcode 0x%04x, param 0x%04x",
-				entry->event, entry->opcode, entry->param);
-
-			if (entry->opcode == kLBNotifyGUIAction)
-				_vm->addNotifyEvent(NotifyEvent(entry->opcode, _itemId));
-			else if (entry->opcode == kLBNotifyChangeMode && _vm->getGameType() != GType_LIVINGBOOKSV1) {
-				NotifyEvent notifyEvent(entry->opcode, entry->param);
-				notifyEvent.newUnknown = entry->newUnknown;
-				notifyEvent.newMode = entry->newMode;
-				notifyEvent.newPage = entry->newPage;
-				notifyEvent.newSubpage = entry->newSubpage;
-				notifyEvent.newCursor = entry->newCursor;
-				_vm->addNotifyEvent(notifyEvent);
-			} else
-				_vm->addNotifyEvent(NotifyEvent(entry->opcode, entry->param));
-		} else
-			runScriptEntry(entry);
-	}
-}
-
-int LBItem::runScriptEntry(LBScriptEntry *entry) {
-	if (entry->state == 0xffff)
-		return 0;
-
-	uint start = 0;
-	uint count = entry->argc;
-	// zero targets = apply to self
-	if (!count)
-		count = 1;
-
-	if (entry->opcode != kLBOpRunSubentries) switch (entry->param) {
-	case 0xfffe:
-		// Run once (disable self after run).
-		entry->state = 0xffff;
-		break;
-	case 0xffff:
-		break;
-	case 0:
-	case 1:
-	case 2:
-		start = entry->state;
-		entry->state++;
-		if (entry->state >= count) {
-			switch (entry->param) {
-			case 0:
-				// Disable..
-				entry->state = 0xffff;
-				return 0;
-			case 1:
-				// Stay at the end.
-				entry->state = count - 1;
-				break;
-			case 2:
-				// Loop.
-				entry->state = 0;
-				break;
-			default:
-				break;
-			}
-		}
-		count = 1;
-		break;
-	case 3:
-		// Pick random target.
-		start = _vm->_rnd->getRandomNumberRng(0, count);
-		count = 1;
-		break;
-	default:
-		warning("Weird param for script entry (type 0x%04x, event 0x%04x, opcode 0x%04x, param 0x%04x)",
-			entry->type, entry->event, entry->opcode, entry->param);
-	}
-
-	for (uint n = start; n < count; n++) {
-		LBItem *target;
-
-		debug(2, "Script run: type 0x%04x, event 0x%04x, opcode 0x%04x, param 0x%04x",
-			entry->type, entry->event, entry->opcode, entry->param);
-
-		if (entry->argc) {
-			switch (entry->targetingType) {
-			case kTargetTypeExpression:
-				{
-				// FIXME: this should be EVALUATED
-				LBValue &tgt = _vm->_variables[entry->targets[n]];
-				switch (tgt.type) {
-				case kLBValueItemPtr:
-					target = tgt.item;
-					break;
-				case kLBValueString:
-					// FIXME: handle 'self', at least
-					// TODO: correct otherwise? or only self?
-					target = _vm->getItemByName(tgt.string);
-					break;
-				case kLBValueInteger:
-					target = _vm->getItemById(tgt.integer);
-					break;
-				default:
-					// FIXME: handle list
-					warning("Target '%s' (by expression) resulted in unknown type, skipping", entry->targets[n].c_str());
-					continue;
-				}
-				}
-				if (!target) {
-					debug(2, "Target '%s' (by expression) doesn't exist, skipping", entry->targets[n].c_str());
-					continue;
-				}
-				debug(2, "Target: '%s' (expression '%s')", target->_desc.c_str(), entry->targets[n].c_str());
-				break;
-			case kTargetTypeCode:
-				// FIXME
-				error("encountered kTargetTypeCode");
-				break;
-			case kTargetTypeName:
-				// FIXME: handle 'self'
-				target = _vm->getItemByName(entry->targets[n]);
-				if (!target) {
-					debug(2, "Target '%s' (by name) doesn't exist, skipping", entry->targets[n].c_str());
-					continue;
-				}
-				debug(2, "Target: '%s' (by name)", target->_desc.c_str());
-				break;
-			default:
-				uint16 targetId = entry->argvTarget[n];
-				// TODO: is this type, perhaps?
-				uint16 param = entry->argvParam[n];
-				target = _vm->getItemById(targetId);
-				if (!target) {
-					debug(2, "Target %04x (%04x) doesn't exist, skipping", targetId, param);
-					continue;
-				}
-				debug(2, "Target: %04x (%04x) '%s'", targetId, param, target->_desc.c_str());
-			}
-		} else {
-			target = this;
-			debug(2, "Self-target on '%s'", _desc.c_str());
-		}
-
-		// an opcode in the form 0x1xx means to run the script for event 0xx
-		if ((entry->opcode & 0xff00) == 0x0100) {
-			// FIXME: pass on param
-			target->runScript(entry->opcode & 0xff);
-			break;
-		}
-
-		switch (entry->opcode) {
-		case kLBOpNone:
-			warning("ignoring kLBOpNone (event 0x%04x, param 0x%04x, target '%s')",
-					entry->event, entry->param, target->_desc.c_str());
-			break;
-
-		case kLBOpXShow:
-			// TODO: should be setVisible(true) - not a delayed event -
-			// when we're doing the param 1/2/3 stuff above?
-			// and in modern LB this is perhaps just a direct target->setVisible(true)..
-			if (_vm->getGameType() != GType_LIVINGBOOKSV1)
-				warning("kLBOpXShow on '%s' is probably broken", target->_desc.c_str());
-			_vm->queueDelayedEvent(DelayedEvent(this, kLBDelayedEventSetNotVisible));
-			break;
-
-		case kLBOpTogglePlay:
-			target->togglePlaying(false, true);
-			break;
-
-		case kLBOpSetNotVisible:
-			target->setVisible(false);
-			break;
-
-		case kLBOpSetVisible:
-			target->setVisible(true);
-			break;
-
-		case kLBOpDestroy:
-			target->destroySelf();
-			break;
-
-		case kLBOpRewind:
-			target->seek(1);
-			break;
-
-		case kLBOpStop:
-			target->stop();
-			break;
-
-		case kLBOpDisable:
-			target->setEnabled(false);
-			break;
-
-		case kLBOpEnable:
-			target->setEnabled(true);
-			break;
-
-		case kLBOpGlobalSetNotVisible:
-			target->setGlobalVisible(false);
-			break;
-
-		case kLBOpGlobalSetVisible:
-			target->setGlobalVisible(true);
-			break;
-
-		case kLBOpGlobalDisable:
-			target->setGlobalEnabled(false);
-			break;
-
-		case kLBOpGlobalEnable:
-			target->setGlobalEnabled(true);
-			break;
-
-		case kLBOpSeekToEnd:
-			target->seek(0xFFFF);
-			break;
-
-		case kLBOpMute:
-		case kLBOpUnmute:
-			// FIXME
-			warning("ignoring kLBOpMute/Unmute (event 0x%04x, param 0x%04x, target '%s')",
-				entry->event, entry->param, target->_desc.c_str());
-			break;
-
-		case kLBOpLoad:
-			target->load();
-			break;
-
-		case kLBOpPreload:
-			// FIXME
-			warning("ignoring kLBOpPreload (event 0x%04x, param 0x%04x, target '%s')",
-				entry->event, entry->param, target->_desc.c_str());
-			break;
-
-		case kLBOpUnload:
-			target->unload();
-			break;
-
-		case kLBOpSeekToPrev:
-		case kLBOpSeekToNext:
-			// FIXME
-			warning("ignoring kLBOpSeekToPrev/Next (event 0x%04x, param 0x%04x, target '%s')",
-				entry->event, entry->param, target->_desc.c_str());
-			break;
-
-		case kLBOpDragBegin:
-		case kLBOpDragEnd:
-			// FIXME
-			warning("ignoring kLBOpDragBegin/End (event 0x%04x, param 0x%04x, target '%s')",
-				entry->event, entry->param, target->_desc.c_str());
-			break;
-
-		case kLBOpScriptDisable:
-		case kLBOpScriptEnable:
-			// FIXME
-			warning("ignoring kLBOpScriptDisable/Enable (event 0x%04x, param 0x%04x, target '%s')",
-				entry->event, entry->param, target->_desc.c_str());
-			break;
-
-		case kLBOpUnknown1C:
-			// FIXME
-			warning("ignoring kLBOpUnknown1C (event 0x%04x, param 0x%04x, target '%s')",
-				entry->event, entry->param, target->_desc.c_str());
-			break;
-
-		case kLBOpSendExpression:
-			_page->_code->runCode(this, entry->offset);
-			break;
-
-		case kLBOpRunSubentries:
-			for (uint i = 0; i < entry->subentries.size(); i++) {
-				LBScriptEntry *subentry = entry->subentries[i];
-
-				int e = runScriptEntry(subentry);
-
-				switch (subentry->opcode) {
-				case kLBOpJumpUnlessExpression:
-					debug(2, "JumpUnless got %d (to %d, on %d, of %d)", e, subentry->target, i, entry->subentries.size());
-					if (!e)
-						i = subentry->target - 1;
-					break;
-				case kLBOpBreakExpression:
-					debug(2, "BreakExpression");
-					i = entry->subentries.size();
-					break;
-				case kLBOpJumpToExpression:
-					debug(2, "JumpToExpression got %d (on %d, of %d)", e, i, entry->subentries.size());
-					i = e - 1;
-					break;
-				default:
-					break;
-				}
-			}
-			break;
-
-		case kLBOpRunData:
-			readData(entry->dataType, entry->dataLen, entry->data);
-			break;
-
-		case kLBOpJumpUnlessExpression:
-		case kLBOpBreakExpression:
-		case kLBOpJumpToExpression:
-			{
-			LBValue r = _page->_code->runCode(this, entry->offset);
-			// FIXME
-			return r.integer;
-			}
-
-		default:
-			error("Unknown script opcode (type 0x%04x, event 0x%04x, opcode 0x%04x, param 0x%04x, target '%s')",
-				entry->type, entry->event, entry->opcode, entry->param, target->_desc.c_str());
-		}
-	}
-
-	return 0;
-}
-
-void LBItem::setNextTime(uint16 min, uint16 max) {
-	setNextTime(min, max, _vm->_system->getMillis() / 16);
-}
-
-void LBItem::setNextTime(uint16 min, uint16 max, uint32 start) {
-	_nextTime = start + _vm->_rnd->getRandomNumberRng((uint)min, (uint)max);
-	debug(9, "nextTime is now %d frames away", _nextTime - (uint)(_vm->_system->getMillis() / 16));
-}
-
-void LBItem::runCommand(const Common::String &command) {
-	LBCode tempCode(_vm, 0);
-
-	debug(2, "running command '%s'", command.c_str());
-
-	uint offset = tempCode.parseCode(command);
-	tempCode.runCode(this, offset);
-}
-
-bool LBItem::checkCondition(const Common::String &condition) {
-	LBCode tempCode(_vm, 0);
-
-	debug(3, "checking condition '%s'", condition.c_str());
-
-	uint offset = tempCode.parseCode(condition);
-	LBValue result = tempCode.runCode(this, offset);
-
-	return result.toInt();
-}
-
 } // End of namespace Mohawk
diff --git a/engines/mohawk/livingbooks.h b/engines/mohawk/livingbooks.h
index a9819b3db25..6ba5f90572c 100644
--- a/engines/mohawk/livingbooks.h
+++ b/engines/mohawk/livingbooks.h
@@ -25,6 +25,7 @@
 #include "mohawk/mohawk.h"
 #include "mohawk/console.h"
 #include "mohawk/livingbooks_graphics.h"
+#include "mohawk/livingbooks_constants.h"
 #include "mohawk/sound.h"
 #include "mohawk/video.h"
 
@@ -35,6 +36,12 @@
 
 #include "livingbooks_code.h"
 
+#include "mohawk/livingbooks_itemscript.h"
+#include "mohawk/livingbooks_item.h"
+#include "mohawk/livingbooks_itemtypes.h"
+#include "mohawk/livingbooks_animation.h"
+#include "mohawk/livingbooks_page.h"
+
 namespace Common {
 	class SeekableReadStreamEndian;
 	class MemoryReadStreamEndian;
@@ -42,221 +49,6 @@ namespace Common {
 
 namespace Mohawk {
 
-// Engine Debug Flags
-enum {
-	kDebugCode = 1,
-};
-
-#define LBKEY_MOD_CTRL 1
-#define LBKEY_MOD_ALT 2
-#define LBKEY_MOD_SHIFT 4
-
-struct LBKey {
-	byte code;
-	byte modifiers;
-	byte char_;
-	byte repeats;
-};
-
-enum NodeState {
-	kLBNodeDone = 0,
-	kLBNodeRunning = 1,
-	kLBNodeWaiting = 2
-};
-
-enum LBMode {
-	kLBIntroMode = 1,
-	kLBControlMode = 2,
-	kLBCreditsMode = 3,
-	kLBPreviewMode = 4,
-	kLBReadMode = 5,
-	kLBPlayMode = 6
-};
-
-enum {
-	kLBPhaseInit = 0x0,
-	kLBPhaseIntro = 0x1,
-	kLBPhaseMain = 0x2,
-	kLBPhaseNone = 0x7fff,
-	kLBPhaseLoad = 0xfffe,
-	kLBPhaseCreate = 0xffff
-};
-
-// automatic modes used in _timingMode
-enum {
-	kLBAutoNone = 0,
-	kLBAutoIntro = 1,
-	kLBAutoUserIdle = 2,
-	kLBAutoMain = 3,
-	kLBAutoSync = 4,
-	kLBAutoInit = 5,
-	kLBAutoCreate = 6,
-	kLBAutoLoad = 7
-};
-
-// control modes used in _controlMode
-enum {
-	kLBControlNone = 0,
-	kLBControlHideMouse = 1,
-	kLBControlPauseItems = 2
-};
-
-enum {
-	kLBStaticTextItem = 0x1,
-	kLBPictureItem = 0x2,
-	kLBEditTextItem = 0x14,
-	kLBLiveTextItem = 0x15,
-	kLBAnimationItem = 0x40,
-	kLBSoundItem = 0x41,
-	kLBGroupItem = 0x42,
-	kLBMovieItem = 0x43,
-	kLBPaletteAItem = 0x44, // unused?
-	kLBPaletteItem = 0x45,
-	kLBProxyItem = 0x46,
-	kLBMiniGameItem = 666, // EVIL!!!!
-	kLBXDataFileItem = 0x3e9,
-	kLBDiscDectectorItem = 0xfa1
-};
-
-enum {
-	// no 0x1?
-	kLBAnimOpNotify = 0x2,
-	kLBAnimOpSetTempo = 0x3,
-	// no 0x4?
-	kLBAnimOpMoveTo = 0x5,
-	kLBAnimOpWait = 0x6,
-	kLBAnimOpSetCel = 0x7,
-	kLBAnimOpSleepUntil = 0x8,
-	kLBAnimOpDrawMode = 0x9,
-	kLBAnimOpPlaySound = 0xa,
-	kLBAnimOpWaitForSound = 0xb,
-	kLBAnimOpReleaseSound = 0xc,
-	kLBAnimOpResetSound = 0xd,
-	kLBAnimOpSetTempoDiv = 0xe,
-	kLBAnimOpDelay = 0xf
-};
-
-enum {
-	kLBEventPhaseInit = 0,
-	kLBEventPhaseIntro = 1,
-	kLBEventMouseDown = 2,
-	kLBEventStarted = 3,
-	kLBEventDone = 4,
-	kLBEventMouseUp = 5,
-	kLBEventPhaseMain = 6,
-	kLBEventNotified = 7,
-	kLBEventDragStart = 8,
-	kLBEventDragMove = 9,
-	kLBEventDragEnd = 0xa,
-	kLBEventRolloverBegin = 0xb,
-	kLBEventRolloverMove = 0xc,
-	kLBEventRolloverEnd = 0xd,
-	kLBEventMouseUpIn = 0xe,
-	kLBEventMouseUpOut = 0xf,
-	kLBEventMouseTrackIn = 0x10,
-	kLBEventMouseTrackMove = 0x11,
-	kLBEventMouseTrackMoveIn = 0x12,
-	kLBEventMouseTrackMoveOut = 0x13,
-	kLBEventMouseTrackOut = 0x14,
-	kLBEventFocusBegin = 0x15,
-	kLBEventFocusEnd = 0x16,
-	kLBEventInit = 0x17,
-	kLBEventLoad = 0x1a,
-	kLBEventListLoad = 0x1b,
-	kLBEventPhaseCreate = 0xff
-};
-
-enum {
-	kLBGroupData = 0x64,
-	kLBLiveTextData = 0x65,
-	kLBMsgListScript = 0x66,
-	kLBNotifyScript = 0x67,
-	kLBSetPlayInfo = 0x68,
-	kLBSetRandomLoc = 0x69,  // unused?
-	kLBSetDrag = 0x6a,       // unused?
-	kLBSetDrawMode = 0x6b,
-	kLBSetFont = 0x6c,       // unused?
-	kLBSetOneShot = 0x6d,    // unused?
-	kLBSetPlayPhase = 0x6e,
-	// from here, 2.x+
-	kLBSetKeyNotify = 0x6f,
-	kLBCommand = 0x70,
-	kLBPaletteAData = 0x71,  // unused?
-	kLBPaletteXData = 0x72,
-	kLBDisable = 0x73,       // unused?
-	kLBEnable = 0x74,        // unused?
-	kLBSetNotVisible = 0x75,
-	kLBSetVisible = 0x76,    // unused?
-	kLBGlobalDisable = 0x77,
-	kLBGlobalEnable = 0x78,  // unused?
-	kLBGlobalSetNotVisible = 0x79,
-	kLBGlobalSetVisible = 0x7a, // unused?
-	kLBSetAmbient = 0x7b,
-	kLBSetDragParams = 0x7c,
-	kLBSetKeyEvent = 0x7d,
-	kLBSetRolloverData = 0x7e,
-	kLBSetParent = 0x7f,
-	kLBSetHitTest = 0x80,
-	// from here, rugrats
-	kLBUnknown194 = 0x194
-};
-
-enum {
-	kLBOpNone = 0x0,
-	kLBOpXShow = 0x1,
-	kLBOpTogglePlay = 0x2,
-	kLBOpSetNotVisible = 0x3,
-	kLBOpSetVisible = 0x4,
-	kLBOpDestroy = 0x5,
-	kLBOpRewind = 0x6,
-	kLBOpStop = 0x7,
-	kLBOpDisable = 0x8,
-	kLBOpEnable = 0x9,
-	// (no 0xa)
-	kLBOpGlobalSetNotVisible = 0xb,
-	kLBOpGlobalSetVisible = 0xc,
-	kLBOpGlobalDisable = 0xd,
-	kLBOpGlobalEnable = 0xe,
-	kLBOpSeekToEnd = 0xf,
-	// (no 0x10)
-	kLBOpMute = 0x11,
-	kLBOpUnmute = 0x12,
-	kLBOpLoad = 0x13,
-	kLBOpPreload = 0x14,
-	kLBOpUnload = 0x15,
-	kLBOpSeekToNext = 0x16,
-	kLBOpSeekToPrev = 0x17,
-	kLBOpDragBegin = 0x18,
-	kLBOpDragEnd = 0x19,
-	kLBOpScriptDisable = 0x1a,
-	kLBOpScriptEnable = 0x1b,
-	kLBOpUnknown1C = 0x1c,
-	kLBOpSendExpression = 0x1d,
-	kLBOpJumpUnlessExpression = 0xfffb,
-	kLBOpBreakExpression = 0xfffc,
-	kLBOpJumpToExpression = 0xfffd,
-	kLBOpRunSubentries = 0xfffe,
-	kLBOpRunData = 0xffff
-};
-
-enum {
-	kLBNotifyGUIAction = 1,
-	kLBNotifyGoToControls = 2,
-	kLBNotifyChangePage = 3,
-	kLBNotifyGotoQuit = 4,
-	kLBNotifyIntroDone = 5,
-	kLBNotifyChangeMode = 6,
-	kLBNotifyCursorChange = 7,
-	kLBNotifyPrintPage = 0xc,
-	kLBNotifyQuit = 0xd
-};
-
-enum {
-	kTargetTypeExpression = 0x3f3f,
-	kTargetTypeCode = 0xfffe,
-	kTargetTypeName = 0xffff
-};
-
 class MohawkEngine_LivingBooks;
 class LBPage;
 class LBGraphics;
@@ -264,403 +56,6 @@ class LBAnimation;
 
 bool tryOpenPage(Archive *archive, const Common::String &fileName);
 
-struct LBScriptEntry {
-	LBScriptEntry();
-	~LBScriptEntry();
-
-	uint16 state;
-
-	uint16 type;
-	uint16 event;
-	uint16 opcode;
-	uint16 param;
-
-	uint16 argc;
-	uint16 *argvParam;
-	uint16 *argvTarget;
-
-	uint16 targetingType;
-	Common::Array<Common::String> targets;
-
-	// kLBNotifyChangeMode
-	uint16 newUnknown;
-	uint16 newMode;
-	uint16 newPage;
-	uint16 newSubpage;
-	Common::String newCursor;
-
-	// kLBEventNotified
-	uint16 matchFrom;
-	uint16 matchNotify;
-
-	// kLBOpSendExpression
-	uint32 offset;
-	// kLBOpJumpUnlessExpression
-	uint16 target;
-
-	uint16 dataType;
-	uint16 dataLen;
-	byte *data;
-
-	Common::Array<Common::String> conditions;
-	Common::Array<LBScriptEntry *> subentries;
-};
-
-struct LBAnimScriptEntry {
-	byte opcode;
-	byte size;
-	byte *data;
-};
-
-class LBAnimationNode {
-public:
-	LBAnimationNode(MohawkEngine_LivingBooks *vm, LBAnimation *parent, uint16 scriptResourceId);
-	~LBAnimationNode();
-
-	void draw(const Common::Rect &_bounds);
-	void reset();
-	NodeState update(bool seeking = false);
-	bool transparentAt(int x, int y);
-
-protected:
-	MohawkEngine_LivingBooks *_vm;
-	LBAnimation *_parent;
-
-	void loadScript(uint16 resourceId);
-	uint _currentEntry;
-	Common::Array<LBAnimScriptEntry> _scriptEntries;
-
-	uint _currentCel;
-	int16 _xPos, _yPos;
-	uint32 _delay;
-};
-
-class LBAnimationItem;
-
-class LBAnimation {
-public:
-	LBAnimation(MohawkEngine_LivingBooks *vm, LBAnimationItem *parent, uint16 resourceId);
-	~LBAnimation();
-
-	void draw();
-	bool update();
-
-	void start();
-	void seek(uint16 pos);
-	void seekToTime(uint32 time);
-	void stop();
-
-	void playSound(uint16 resourceId);
-	bool soundPlaying(uint16 resourceId, const Common::String &cue);
-
-	bool transparentAt(int x, int y);
-
-	void setTempo(uint16 tempo);
-
-	uint getNumResources() { return _shapeResources.size(); }
-	uint16 getResource(uint num) { return _shapeResources[num]; }
-	Common::Point getOffset(uint num) { return _shapeOffsets[num]; }
-
-	uint32 getCurrentFrame() { return _currentFrame; }
-
-	uint16 getParentId();
-
-protected:
-	MohawkEngine_LivingBooks *_vm;
-	LBAnimationItem *_parent;
-
-	Common::Rect _bounds, _clip;
-	Common::Array<LBAnimationNode *> _nodes;
-
-	uint16 _tempo;
-
-	uint16 _currentSound;
-	CueList _cueList;
-
-	uint32 _lastTime, _currentFrame;
-	bool _running;
-
-	void loadShape(uint16 resourceId);
-	Common::Array<uint16> _shapeResources;
-	Common::Array<Common::Point> _shapeOffsets;
-};
-
-class LBItem {
-	friend class LBCode;
-
-public:
-	LBItem(MohawkEngine_LivingBooks *vm, LBPage *page, Common::Rect rect);
-	virtual ~LBItem();
-
-	void readFrom(Common::SeekableReadStreamEndian *stream);
-	void readData(uint16 type, uint16 size, byte *data);
-	virtual void readData(uint16 type, uint16 size, Common::MemoryReadStreamEndian *stream);
-
-	virtual void destroySelf(); // 0x2
-	virtual void setEnabled(bool enabled); // 0x3
-	virtual void setGlobalEnabled(bool enabled);
-	virtual bool contains(Common::Point point); // 0x7
-	virtual void update(); // 0x8
-	virtual void draw() { } // 0x9
-	virtual void handleKeyChar(Common::Point pos) { } // 0xA
-	virtual void handleMouseDown(Common::Point pos); // 0xB
-	virtual void handleMouseMove(Common::Point pos); // 0xC
-	virtual void handleMouseUp(Common::Point pos); // 0xD
-	virtual bool togglePlaying(bool playing, bool restart = false); // 0xF
-	virtual void done(bool onlyNotify); // 0x10
-	virtual void init(); // 0x11
-	virtual void seek(uint16 pos) { } // 0x13
-	virtual void seekToTime(uint32 time) { }
-	virtual void setFocused(bool focused) { } // 0x14
-	virtual void setVisible(bool visible); // 0x17
-	virtual void setGlobalVisible(bool enabled);
-	virtual void startPhase(uint phase); // 0x18
-	virtual void stop(); // 0x19
-	virtual void notify(uint16 data, uint16 from); // 0x1A
-	virtual void load();
-	virtual void unload();
-	virtual void moveBy(const Common::Point &pos);
-	virtual void moveTo(const Common::Point &pos);
-
-	LBItem *clone(uint16 newId, const Common::String &newName);
-
-	uint16 getId() { return _itemId; }
-	const Common::String &getName() { return _desc; }
-	const Common::Rect &getRect() { return _rect; }
-	uint16 getSoundPriority() { return _soundMode; }
-	bool isLoaded() { return _loaded; }
-	bool isAmbient() { return _isAmbient; }
-
-	Common::List<LBItem *>::iterator _iterator;
-
-	// TODO: make private
-	Common::HashMap<Common::String, LBValue, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> _variables;
-
-protected:
-	MohawkEngine_LivingBooks *_vm;
-	LBPage *_page;
-
-	void setNextTime(uint16 min, uint16 max);
-	void setNextTime(uint16 min, uint16 max, uint32 start);
-
-	Common::Rect _rect;
-	Common::String _desc;
-	uint16 _resourceId;
-	uint16 _itemId;
-
-	bool _loaded, _visible, _globalVisible, _playing, _enabled, _globalEnabled;
-
-	uint32 _nextTime, _startTime;
-	uint16 _loops;
-
-	uint16 _phase, _timingMode, _delayMin, _delayMax;
-	uint16 _loopMode, _periodMin, _periodMax;
-	uint16 _controlMode, _soundMode;
-	Common::Point _relocPoint;
-
-	bool _isAmbient;
-	bool _doHitTest;
-
-	virtual LBItem *createClone();
-
-	Common::Array<LBScriptEntry *> _scriptEntries;
-	void runScript(uint event, uint16 data = 0, uint16 from = 0);
-	int runScriptEntry(LBScriptEntry *entry);
-
-	void runCommand(const Common::String &command);
-	bool checkCondition(const Common::String &condition);
-
-	LBScriptEntry *parseScriptEntry(uint16 type, uint16 &size, Common::MemoryReadStreamEndian *stream, bool isSubentry = false);
-};
-
-class LBSoundItem : public LBItem {
-public:
-	LBSoundItem(MohawkEngine_LivingBooks *_vm, LBPage *page, Common::Rect rect);
-	~LBSoundItem() override;
-
-	void update() override;
-	bool togglePlaying(bool playing, bool restart) override;
-	void stop() override;
-
-protected:
-	LBItem *createClone() override;
-
-	bool _running;
-};
-
-struct GroupEntry {
-	uint entryId;
-	uint entryType;
-};
-
-class LBGroupItem : public LBItem {
-public:
-	LBGroupItem(MohawkEngine_LivingBooks *_vm, LBPage *page, Common::Rect rect);
-
-	void readData(uint16 type, uint16 size, Common::MemoryReadStreamEndian *stream) override;
-
-	void destroySelf() override;
-	void setEnabled(bool enabled) override;
-	void setGlobalEnabled(bool enabled) override;
-	bool contains(Common::Point point) override;
-	bool togglePlaying(bool playing, bool restart) override;
-	// 0x12
-	void seek(uint16 pos) override;
-	void setVisible(bool visible) override;
-	void setGlobalVisible(bool visible) override;
-	void startPhase(uint phase) override;
-	void stop() override;
-	void load() override;
-	void unload() override;
-	void moveBy(const Common::Point &pos) override;
-	void moveTo(const Common::Point &pos) override;
-
-protected:
-	LBItem *createClone() override;
-
-	bool _starting;
-
-	Common::Array<GroupEntry> _groupEntries;
-};
-
-class LBPaletteItem : public LBItem {
-public:
-	LBPaletteItem(MohawkEngine_LivingBooks *_vm, LBPage *page, Common::Rect rect);
-	~LBPaletteItem() override;
-
-	void readData(uint16 type, uint16 size, Common::MemoryReadStreamEndian *stream) override;
-
-	bool togglePlaying(bool playing, bool restart) override;
-	void update() override;
-
-protected:
-	LBItem *createClone() override;
-
-	uint16 _fadeInPeriod, _fadeInStep, _drawStart, _drawCount;
-	uint32 _fadeInStart, _fadeInCurrent;
-	byte *_palette;
-};
-
-struct LiveTextWord {
-	Common::Rect bounds;
-	uint16 soundId;
-
-	uint16 itemType;
-	uint16 itemId;
-};
-
-struct LiveTextPhrase {
-	uint16 wordStart, wordCount;
-	uint16 highlightStart, highlightEnd;
-	uint16 startId, endId;
-};
-
-class LBLiveTextItem : public LBItem {
-public:
-	LBLiveTextItem(MohawkEngine_LivingBooks *_vm, LBPage *page, Common::Rect rect);
-
-	void readData(uint16 type, uint16 size, Common::MemoryReadStreamEndian *stream) override;
-
-	bool contains(Common::Point point) override;
-	void update() override;
-	void draw() override;
-	void handleMouseDown(Common::Point pos) override;
-	bool togglePlaying(bool playing, bool restart) override;
-	void stop() override;
-	void notify(uint16 data, uint16 from) override;
-
-protected:
-	LBItem *createClone() override;
-
-	void paletteUpdate(uint16 word, bool on);
-	void drawWord(uint word, uint yPos);
-
-	uint16 _currentPhrase, _currentWord;
-
-	byte _backgroundColor[4];
-	byte _foregroundColor[4];
-	byte _highlightColor[4];
-	uint16 _paletteIndex;
-
-	Common::Array<LiveTextWord> _words;
-	Common::Array<LiveTextPhrase> _phrases;
-};
-
-class LBPictureItem : public LBItem {
-public:
-	LBPictureItem(MohawkEngine_LivingBooks *_vm, LBPage *page, Common::Rect rect);
-
-	void readData(uint16 type, uint16 size, Common::MemoryReadStreamEndian *stream) override;
-
-	bool contains(Common::Point point) override;
-	void draw() override;
-	void init() override;
-
-protected:
-	LBItem *createClone() override;
-};
-
-class LBAnimationItem : public LBItem {
-public:
-	LBAnimationItem(MohawkEngine_LivingBooks *_vm, LBPage *page, Common::Rect rect);
-	~LBAnimationItem() override;
-
-	void setEnabled(bool enabled) override;
-	bool contains(Common::Point point) override;
-	void update() override;
-	void draw() override;
-	bool togglePlaying(bool playing, bool restart) override;
-	void done(bool onlyNotify) override;
-	void init() override;
-	void seek(uint16 pos) override;
-	void seekToTime(uint32 time) override;
-	void startPhase(uint phase) override;
-	void stop() override;
-
-protected:
-	LBItem *createClone() override;
-
-	LBAnimation *_anim;
-	bool _running;
-};
-
-class LBMovieItem : public LBItem {
-public:
-	LBMovieItem(MohawkEngine_LivingBooks *_vm, LBPage *page, Common::Rect rect);
-	~LBMovieItem() override;
-
-	void update() override;
-	bool togglePlaying(bool playing, bool restart) override;
-
-protected:
-	LBItem *createClone() override;
-};
-
-class LBMiniGameItem : public LBItem {
-public:
-	LBMiniGameItem(MohawkEngine_LivingBooks *_vm, LBPage *page, Common::Rect rect);
-	~LBMiniGameItem() override;
-
-	bool togglePlaying(bool playing, bool restart) override;
-
-protected:
-	LBItem *createClone() override;
-};
-
-class LBProxyItem : public LBItem {
-public:
-	LBProxyItem(MohawkEngine_LivingBooks *_vm, LBPage *page, Common::Rect rect);
-	~LBProxyItem() override;
-
-	void load() override;
-	void unload() override;
-
-protected:
-	LBItem *createClone() override;
-
-	class LBPage *_page;
-};
-
 struct NotifyEvent {
 	NotifyEvent(uint t, uint p) : type(t), param(p), newUnknown(0), newMode(0), newPage(0), newSubpage(0) { }
 	uint type;
@@ -686,31 +81,6 @@ struct DelayedEvent {
 	DelayedEventType type;
 };
 
-class LBPage {
-public:
-	LBPage(MohawkEngine_LivingBooks *vm);
-	~LBPage();
-
-	void open(Archive *mhk, uint16 baseId);
-	uint16 getResourceVersion();
-
-	void addClonedItem(LBItem *item);
-	void itemDestroyed(LBItem *item);
-
-	LBCode *_code;
-
-protected:
-	MohawkEngine_LivingBooks *_vm;
-
-	Archive *_mhk;
-	Common::Array<LBItem *> _items;
-
-	uint16 _baseId;
-	bool _cascade;
-
-	void loadBITL(uint16 resourceId);
-};
-
 class MohawkEngine_LivingBooks : public MohawkEngine {
 protected:
 	Common::Error run() override;
diff --git a/engines/mohawk/livingbooks_animation.h b/engines/mohawk/livingbooks_animation.h
new file mode 100644
index 00000000000..73aca01952e
--- /dev/null
+++ b/engines/mohawk/livingbooks_animation.h
@@ -0,0 +1,143 @@
+/* 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 MOHAWK_LIVINGBOOKS_ANIMATION_H
+#define MOHAWK_LIVINGBOOKS_ANIMATION_H
+
+#include "mohawk/livingbooks_constants.h"
+#include "mohawk/livingbooks_item.h"
+#include "mohawk/sound.h"
+
+#include "common/array.h"
+#include "common/rect.h"
+
+namespace Mohawk {
+
+class MohawkEngine_LivingBooks;
+class LBPage;
+class LBAnimation;
+
+struct LBAnimScriptEntry {
+	byte opcode;
+	byte size;
+	byte *data;
+};
+
+class LBAnimationNode {
+public:
+	LBAnimationNode(MohawkEngine_LivingBooks *vm, LBAnimation *parent, uint16 scriptResourceId);
+	~LBAnimationNode();
+
+	void draw(const Common::Rect &_bounds);
+	void reset();
+	NodeState update(bool seeking = false);
+	bool transparentAt(int x, int y);
+
+protected:
+	MohawkEngine_LivingBooks *_vm;
+	LBAnimation *_parent;
+
+	void loadScript(uint16 resourceId);
+	uint _currentEntry;
+	Common::Array<LBAnimScriptEntry> _scriptEntries;
+
+	uint _currentCel;
+	int16 _xPos, _yPos;
+	uint32 _delay;
+};
+
+class LBAnimationItem;
+
+class LBAnimation {
+public:
+	LBAnimation(MohawkEngine_LivingBooks *vm, LBAnimationItem *parent, uint16 resourceId);
+	~LBAnimation();
+
+	void draw();
+	bool update();
+
+	void start();
+	void seek(uint16 pos);
+	void seekToTime(uint32 time);
+	void stop();
+
+	void playSound(uint16 resourceId);
+	bool soundPlaying(uint16 resourceId, const Common::String &cue);
+
+	bool transparentAt(int x, int y);
+
+	void setTempo(uint16 tempo);
+
+	uint getNumResources() { return _shapeResources.size(); }
+	uint16 getResource(uint num) { return _shapeResources[num]; }
+	Common::Point getOffset(uint num) { return _shapeOffsets[num]; }
+
+	uint32 getCurrentFrame() { return _currentFrame; }
+
+	uint16 getParentId();
+
+protected:
+	MohawkEngine_LivingBooks *_vm;
+	LBAnimationItem *_parent;
+
+	Common::Rect _bounds, _clip;
+	Common::Array<LBAnimationNode *> _nodes;
+
+	uint16 _tempo;
+
+	uint16 _currentSound;
+	CueList _cueList;
+
+	uint32 _lastTime, _currentFrame;
+	bool _running;
+
+	void loadShape(uint16 resourceId);
+	Common::Array<uint16> _shapeResources;
+	Common::Array<Common::Point> _shapeOffsets;
+};
+
+class LBAnimationItem : public LBItem {
+public:
+	LBAnimationItem(MohawkEngine_LivingBooks *_vm, LBPage *page, Common::Rect rect);
+	~LBAnimationItem() override;
+
+	void setEnabled(bool enabled) override;
+	bool contains(Common::Point point) override;
+	void update() override;
+	void draw() override;
+	bool togglePlaying(bool playing, bool restart) override;
+	void done(bool onlyNotify) override;
+	void init() override;
+	void seek(uint16 pos) override;
+	void seekToTime(uint32 time) override;
+	void startPhase(uint phase) override;
+	void stop() override;
+
+protected:
+	LBItem *createClone() override;
+
+	LBAnimation *_anim;
+	bool _running;
+};
+
+} // End of namespace Mohawk
+
+#endif
diff --git a/engines/mohawk/livingbooks_constants.h b/engines/mohawk/livingbooks_constants.h
new file mode 100644
index 00000000000..82a6e25f331
--- /dev/null
+++ b/engines/mohawk/livingbooks_constants.h
@@ -0,0 +1,242 @@
+/* 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 MOHAWK_LIVINGBOOKS_CONSTANTS_H
+#define MOHAWK_LIVINGBOOKS_CONSTANTS_H
+
+namespace Mohawk {
+
+/* Some disambiguations:
+ *  _soundMode is a priority (see LBItem::getSoundPriority), not a kLBControlMode.
+ *  _controlMode (item flags) is unrelated to kLBControlMode (the menu screens).
+ *  LBProxyItem::_page shadows LBItem::_page (preexisting, intentional-looking, untouched).
+ */
+
+// Engine Debug Flags
+enum {
+	kDebugCode = 1,
+};
+
+#define LBKEY_MOD_CTRL 1
+#define LBKEY_MOD_ALT 2
+#define LBKEY_MOD_SHIFT 4
+enum NodeState {
+	kLBNodeDone = 0,
+	kLBNodeRunning = 1,
+	kLBNodeWaiting = 2
+};
+
+enum LBMode {
+	kLBIntroMode = 1,
+	kLBControlMode = 2,
+	kLBCreditsMode = 3,
+	kLBPreviewMode = 4,
+	kLBReadMode = 5,
+	kLBPlayMode = 6
+};
+
+enum {
+	kLBPhaseInit = 0x0,
+	kLBPhaseIntro = 0x1,
+	kLBPhaseMain = 0x2,
+	kLBPhaseNone = 0x7fff,
+	kLBPhaseLoad = 0xfffe,
+	kLBPhaseCreate = 0xffff
+};
+
+// automatic modes used in _timingMode
+enum {
+	kLBAutoNone = 0,
+	kLBAutoIntro = 1,
+	kLBAutoUserIdle = 2,
+	kLBAutoMain = 3,
+	kLBAutoSync = 4,
+	kLBAutoInit = 5,
+	kLBAutoCreate = 6,
+	kLBAutoLoad = 7
+};
+
+// control modes used in _controlMode
+enum {
+	kLBControlNone = 0,
+	kLBControlHideMouse = 1,
+	kLBControlPauseItems = 2
+};
+
+enum {
+	kLBStaticTextItem = 0x1,
+	kLBPictureItem = 0x2,
+	kLBEditTextItem = 0x14,
+	kLBLiveTextItem = 0x15,
+	kLBAnimationItem = 0x40,
+	kLBSoundItem = 0x41,
+	kLBGroupItem = 0x42,
+	kLBMovieItem = 0x43,
+	kLBPaletteAItem = 0x44, // unused?
+	kLBPaletteItem = 0x45,
+	kLBProxyItem = 0x46,
+	kLBMiniGameItem = 666, // EVIL!!!!
+	kLBXDataFileItem = 0x3e9,
+	kLBDiscDectectorItem = 0xfa1
+};
+
+enum {
+	// no 0x1?
+	kLBAnimOpNotify = 0x2,
+	kLBAnimOpSetTempo = 0x3,
+	// no 0x4?
+	kLBAnimOpMoveTo = 0x5,
+	kLBAnimOpWait = 0x6,
+	kLBAnimOpSetCel = 0x7,
+	kLBAnimOpSleepUntil = 0x8,
+	kLBAnimOpDrawMode = 0x9,
+	kLBAnimOpPlaySound = 0xa,
+	kLBAnimOpWaitForSound = 0xb,
+	kLBAnimOpReleaseSound = 0xc,
+	kLBAnimOpResetSound = 0xd,
+	kLBAnimOpSetTempoDiv = 0xe,
+	kLBAnimOpDelay = 0xf
+};
+
+enum {
+	kLBEventPhaseInit = 0,
+	kLBEventPhaseIntro = 1,
+	kLBEventMouseDown = 2,
+	kLBEventStarted = 3,
+	kLBEventDone = 4,
+	kLBEventMouseUp = 5,
+	kLBEventPhaseMain = 6,
+	kLBEventNotified = 7,
+	kLBEventDragStart = 8,
+	kLBEventDragMove = 9,
+	kLBEventDragEnd = 0xa,
+	kLBEventRolloverBegin = 0xb,
+	kLBEventRolloverMove = 0xc,
+	kLBEventRolloverEnd = 0xd,
+	kLBEventMouseUpIn = 0xe,
+	kLBEventMouseUpOut = 0xf,
+	kLBEventMouseTrackIn = 0x10,
+	kLBEventMouseTrackMove = 0x11,
+	kLBEventMouseTrackMoveIn = 0x12,
+	kLBEventMouseTrackMoveOut = 0x13,
+	kLBEventMouseTrackOut = 0x14,
+	kLBEventFocusBegin = 0x15,
+	kLBEventFocusEnd = 0x16,
+	kLBEventInit = 0x17,
+	kLBEventLoad = 0x1a,
+	kLBEventListLoad = 0x1b,
+	kLBEventPhaseCreate = 0xff
+};
+
+enum {
+	kLBGroupData = 0x64,
+	kLBLiveTextData = 0x65,
+	kLBMsgListScript = 0x66,
+	kLBNotifyScript = 0x67,
+	kLBSetPlayInfo = 0x68,
+	kLBSetRandomLoc = 0x69,  // unused?
+	kLBSetDrag = 0x6a,       // unused?
+	kLBSetDrawMode = 0x6b,
+	kLBSetFont = 0x6c,       // unused?
+	kLBSetOneShot = 0x6d,    // unused?
+	kLBSetPlayPhase = 0x6e,
+	// from here, 2.x+
+	kLBSetKeyNotify = 0x6f,
+	kLBCommand = 0x70,
+	kLBPaletteAData = 0x71,  // unused?
+	kLBPaletteXData = 0x72,
+	kLBDisable = 0x73,       // unused?
+	kLBEnable = 0x74,        // unused?
+	kLBSetNotVisible = 0x75,
+	kLBSetVisible = 0x76,    // unused?
+	kLBGlobalDisable = 0x77,
+	kLBGlobalEnable = 0x78,  // unused?
+	kLBGlobalSetNotVisible = 0x79,
+	kLBGlobalSetVisible = 0x7a, // unused?
+	kLBSetAmbient = 0x7b,
+	kLBSetDragParams = 0x7c,
+	kLBSetKeyEvent = 0x7d,
+	kLBSetRolloverData = 0x7e,
+	kLBSetParent = 0x7f,
+	kLBSetHitTest = 0x80,
+	// from here, rugrats
+	kLBUnknown194 = 0x194
+};
+
+enum {
+	kLBOpNone = 0x0,
+	kLBOpXShow = 0x1,
+	kLBOpTogglePlay = 0x2,
+	kLBOpSetNotVisible = 0x3,
+	kLBOpSetVisible = 0x4,
+	kLBOpDestroy = 0x5,
+	kLBOpRewind = 0x6,
+	kLBOpStop = 0x7,
+	kLBOpDisable = 0x8,
+	kLBOpEnable = 0x9,
+	// (no 0xa)
+	kLBOpGlobalSetNotVisible = 0xb,
+	kLBOpGlobalSetVisible = 0xc,
+	kLBOpGlobalDisable = 0xd,
+	kLBOpGlobalEnable = 0xe,
+	kLBOpSeekToEnd = 0xf,
+	// (no 0x10)
+	kLBOpMute = 0x11,
+	kLBOpUnmute = 0x12,
+	kLBOpLoad = 0x13,
+	kLBOpPreload = 0x14,
+	kLBOpUnload = 0x15,
+	kLBOpSeekToNext = 0x16,
+	kLBOpSeekToPrev = 0x17,
+	kLBOpDragBegin = 0x18,
+	kLBOpDragEnd = 0x19,
+	kLBOpScriptDisable = 0x1a,
+	kLBOpScriptEnable = 0x1b,
+	kLBOpUnknown1C = 0x1c,
+	kLBOpSendExpression = 0x1d,
+	kLBOpJumpUnlessExpression = 0xfffb,
+	kLBOpBreakExpression = 0xfffc,
+	kLBOpJumpToExpression = 0xfffd,
+	kLBOpRunSubentries = 0xfffe,
+	kLBOpRunData = 0xffff
+};
+
+enum {
+	kLBNotifyGUIAction = 1,
+	kLBNotifyGoToControls = 2,
+	kLBNotifyChangePage = 3,
+	kLBNotifyGotoQuit = 4,
+	kLBNotifyIntroDone = 5,
+	kLBNotifyChangeMode = 6,
+	kLBNotifyCursorChange = 7,
+	kLBNotifyPrintPage = 0xc,
+	kLBNotifyQuit = 0xd
+};
+
+enum {
+	kTargetTypeExpression = 0x3f3f,
+	kTargetTypeCode = 0xfffe,
+	kTargetTypeName = 0xffff
+};
+
+} // End of namespace Mohawk
+
+#endif
diff --git a/engines/mohawk/livingbooks_item.cpp b/engines/mohawk/livingbooks_item.cpp
new file mode 100644
index 00000000000..e6c776d980a
--- /dev/null
+++ b/engines/mohawk/livingbooks_item.cpp
@@ -0,0 +1,343 @@
+/* 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 "mohawk/livingbooks.h"
+#include "mohawk/cursors.h"
+
+#include "common/system.h"
+#include "common/textconsole.h"
+
+namespace Mohawk {
+
+LBItem::LBItem(MohawkEngine_LivingBooks *vm, LBPage *page, Common::Rect rect) : _vm(vm), _page(page), _rect(rect) {
+	if (_vm->getGameType() == GType_LIVINGBOOKSV1 || _vm->getGameType() == GType_LIVINGBOOKSV2)
+		_phase = kLBPhaseInit;
+	else
+		_phase = kLBPhaseLoad;
+
+	_loopMode = 0;
+	_delayMin = 0;
+	_delayMax = 0;
+	_timingMode = kLBAutoNone;
+	_periodMin = 0;
+	_periodMax = 0;
+	_controlMode = kLBControlNone;
+	_soundMode = 0;
+
+	_loaded = false;
+	_enabled = false;
+	_visible = true;
+	_playing = false;
+	_globalEnabled = true;
+	_globalVisible = true;
+	_nextTime = 0;
+	_startTime = 0;
+	_loops = 0;
+
+	_isAmbient = false;
+	_doHitTest = true;
+}
+
+LBItem::~LBItem() {
+	for (uint i = 0; i < _scriptEntries.size(); i++)
+		delete _scriptEntries[i];
+}
+
+void LBItem::destroySelf() {
+	if (!this->_itemId)
+		error("destroySelf() on an item which was already dead");
+
+	_vm->queueDelayedEvent(DelayedEvent(this, kLBDelayedEventDestroy));
+
+	_itemId = 0;
+}
+
+void LBItem::setEnabled(bool enabled) {
+	if (enabled && !_loaded && !_playing) {
+		if (_timingMode == kLBAutoUserIdle) {
+			setNextTime(_periodMin, _periodMax);
+			debug(2, "Enable time startup");
+		}
+	}
+
+	_enabled = enabled;
+}
+
+void LBItem::setGlobalEnabled(bool enabled) {
+	bool wasEnabled = _loaded && _enabled && _globalEnabled;
+	_globalEnabled = enabled;
+	if (wasEnabled != (_loaded && _enabled && _globalEnabled))
+		setEnabled(enabled);
+}
+
+bool LBItem::contains(Common::Point point) {
+	if (!_loaded)
+		return false;
+
+	if (_playing && _loopMode == 0xFFFF)
+		stop();
+
+	if (!_playing && _timingMode == kLBAutoUserIdle)
+		setNextTime(_periodMin, _periodMax);
+
+	return _visible && _globalVisible && _rect.contains(point);
+}
+
+void LBItem::update() {
+	if (_phase != kLBPhaseNone && (!_loaded || !_enabled || !_globalEnabled))
+		return;
+
+	if (_nextTime == 0 || _nextTime > (uint32)(_vm->_system->getMillis() / 16))
+		return;
+
+	if (togglePlaying(_playing, true)) {
+		_nextTime = 0;
+	} else if (_loops == 0 && _timingMode == kLBAutoUserIdle) {
+		debug(9, "Looping in update()");
+		setNextTime(_periodMin, _periodMax);
+	}
+}
+
+void LBItem::handleMouseDown(Common::Point pos) {
+	if (!_loaded || !_enabled || !_globalEnabled)
+		return;
+
+	_vm->setFocus(this);
+	runScript(kLBEventMouseDown);
+	runScript(kLBEventMouseTrackIn);
+}
+
+void LBItem::handleMouseMove(Common::Point pos) {
+	// TODO: handle drag
+}
+
+void LBItem::handleMouseUp(Common::Point pos) {
+	_vm->setFocus(nullptr);
+	runScript(kLBEventMouseUp);
+	runScript(kLBEventMouseUpIn);
+}
+
+bool LBItem::togglePlaying(bool playing, bool restart) {
+	if (playing) {
+		_vm->queueDelayedEvent(DelayedEvent(this, kLBDelayedEventDone));
+		return true;
+	}
+	if (((_loaded && _enabled && _globalEnabled) || _phase == kLBPhaseNone) && !_playing) {
+		_playing = togglePlaying(true, restart);
+		if (_playing) {
+			_nextTime = 0;
+			_startTime = _vm->_system->getMillis() / 16;
+
+			if (_loopMode == 0xFFFF || _loopMode == 0xFFFE)
+				_loops = 0xFFFF;
+			else
+				_loops = _loopMode;
+
+			if (_controlMode >= kLBControlHideMouse) {
+				debug(2, "Hiding cursor");
+				_vm->_cursor->hideCursor();
+				_vm->lockSound(this, true);
+
+				if (_controlMode >= kLBControlPauseItems) {
+					debug(2, "Disabling all");
+					_vm->setEnableForAll(false, this);
+				}
+			}
+
+			runScript(kLBEventStarted);
+			notify(0, _itemId);
+		}
+	}
+	return _playing;
+}
+
+void LBItem::done(bool onlyNotify) {
+	if (onlyNotify) {
+		if (_relocPoint.x || _relocPoint.y) {
+			_rect.translate(_relocPoint.x, _relocPoint.y);
+			// TODO: does drag box need adjusting?
+		}
+
+		if (_loops && --_loops) {
+			debug(9, "Real looping (now 0x%04x left)", _loops);
+			setNextTime(_delayMin, _delayMax, _startTime);
+		} else
+			done(false);
+
+		return;
+	}
+
+	_playing = false;
+	_loops = 0;
+	_startTime = 0;
+
+	if (_controlMode >= kLBControlHideMouse) {
+		debug(2, "Showing cursor");
+		_vm->_cursor->showCursor();
+		_vm->lockSound(this, false);
+
+		if (_controlMode >= kLBControlPauseItems) {
+			debug(2, "Enabling all");
+			_vm->setEnableForAll(true, this);
+		}
+	}
+
+	if (_timingMode == kLBAutoUserIdle) {
+		debug(9, "Looping in done() - %d to %d", _periodMin, _periodMax);
+		setNextTime(_periodMin, _periodMax);
+	}
+
+	runScript(kLBEventDone);
+	notify(0xFFFF, _itemId);
+}
+
+void LBItem::init() {
+	runScript(kLBEventInit);
+}
+
+void LBItem::setVisible(bool visible) {
+	if (visible == _visible)
+		return;
+
+	_visible = visible;
+	_vm->_needsRedraw = true;
+}
+
+void LBItem::setGlobalVisible(bool visible) {
+	bool wasEnabled = _visible && _globalVisible;
+	_globalVisible = visible;
+	if (wasEnabled != (_visible && _globalVisible))
+		_vm->_needsRedraw = true;
+}
+
+void LBItem::startPhase(uint phase) {
+	if (_phase == phase) {
+		if (_phase != kLBPhaseNone) {
+			setEnabled(true);
+		}
+
+		load();
+	}
+
+	switch (phase) {
+	case kLBPhaseLoad:
+		runScript(kLBEventListLoad);
+		break;
+	case kLBPhaseCreate:
+		runScript(kLBEventPhaseCreate);
+		if (_timingMode == kLBAutoCreate) {
+			debug(2, "Phase create: time startup");
+			setNextTime(_periodMin, _periodMax);
+		}
+		break;
+	case kLBPhaseInit:
+		runScript(kLBEventPhaseInit);
+		if (_timingMode == kLBAutoInit) {
+			debug(2, "Phase init: time startup");
+			setNextTime(_periodMin, _periodMax);
+		}
+		break;
+	case kLBPhaseIntro:
+		runScript(kLBEventPhaseIntro);
+		if (_timingMode == kLBAutoIntro || _timingMode == kLBAutoUserIdle) {
+			debug(2, "Phase intro: time startup");
+			setNextTime(_periodMin, _periodMax);
+		}
+		break;
+	case kLBPhaseMain:
+		runScript(kLBEventPhaseMain);
+		if (_timingMode == kLBAutoUserIdle || _timingMode == kLBAutoMain) {
+			debug(2, "Phase main: time startup");
+			setNextTime(_periodMin, _periodMax);
+		}
+		break;
+	default:
+		break;
+	}
+}
+
+void LBItem::stop() {
+	if (!_playing)
+		return;
+
+	_loops = 0;
+	seek(0xFFFF);
+	done(true);
+}
+
+void LBItem::load() {
+	if (_loaded)
+		return;
+
+	_loaded = true;
+
+	// FIXME: events etc
+	if (_timingMode == kLBAutoLoad) {
+		debug(2, "Load: time startup");
+		setNextTime(_periodMin, _periodMax);
+	}
+}
+
+void LBItem::unload() {
+	if (!_loaded)
+		return;
+
+	_loaded = false;
+
+	// FIXME: stuff
+}
+
+void LBItem::moveBy(const Common::Point &pos) {
+	_rect.translate(pos.x, pos.y);
+}
+
+void LBItem::moveTo(const Common::Point &pos) {
+	_rect.moveTo(pos);
+}
+
+LBItem *LBItem::clone(uint16 newId, const Common::String &newName) {
+	LBItem *item = createClone();
+
+	item->_itemId = newId;
+	item->_desc = newName;
+
+	item->_resourceId = _resourceId;
+	// FIXME: the rest
+
+	_page->addClonedItem(item);
+	// FIXME: zorder?
+	return item;
+}
+
+LBItem *LBItem::createClone() {
+	return new LBItem(_vm, _page, _rect);
+}
+
+void LBItem::setNextTime(uint16 min, uint16 max) {
+	setNextTime(min, max, _vm->_system->getMillis() / 16);
+}
+
+void LBItem::setNextTime(uint16 min, uint16 max, uint32 start) {
+	_nextTime = start + _vm->_rnd->getRandomNumberRng((uint)min, (uint)max);
+	debug(9, "nextTime is now %d frames away", _nextTime - (uint)(_vm->_system->getMillis() / 16));
+}
+
+} // End of namespace Mohawk
diff --git a/engines/mohawk/livingbooks_item.h b/engines/mohawk/livingbooks_item.h
new file mode 100644
index 00000000000..448bdf5a942
--- /dev/null
+++ b/engines/mohawk/livingbooks_item.h
@@ -0,0 +1,135 @@
+/* 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 MOHAWK_LIVINGBOOKS_ITEM_H
+#define MOHAWK_LIVINGBOOKS_ITEM_H
+
+#include "livingbooks_code.h"
+
+#include "common/array.h"
+#include "common/hash-str.h"
+#include "common/hashmap.h"
+#include "common/list.h"
+#include "common/rect.h"
+#include "common/str.h"
+
+namespace Common {
+	class SeekableReadStreamEndian;
+	class MemoryReadStreamEndian;
+}
+
+namespace Mohawk {
+
+class MohawkEngine_LivingBooks;
+class LBPage;
+struct LBScriptEntry;
+
+class LBItem {
+	friend class LBCode;
+
+public:
+	LBItem(MohawkEngine_LivingBooks *vm, LBPage *page, Common::Rect rect);
+	virtual ~LBItem();
+
+	void readFrom(Common::SeekableReadStreamEndian *stream);
+	void readData(uint16 type, uint16 size, byte *data);
+	virtual void readData(uint16 type, uint16 size, Common::MemoryReadStreamEndian *stream);
+
+	virtual void destroySelf(); // 0x2
+	virtual void setEnabled(bool enabled); // 0x3
+	virtual void setGlobalEnabled(bool enabled);
+	virtual bool contains(Common::Point point); // 0x7
+	virtual void update(); // 0x8
+	virtual void draw() { } // 0x9
+	virtual void handleKeyChar(Common::Point pos) { } // 0xA
+	virtual void handleMouseDown(Common::Point pos); // 0xB
+	virtual void handleMouseMove(Common::Point pos); // 0xC
+	virtual void handleMouseUp(Common::Point pos); // 0xD
+	virtual bool togglePlaying(bool playing, bool restart = false); // 0xF
+	virtual void done(bool onlyNotify); // 0x10
+	virtual void init(); // 0x11
+	virtual void seek(uint16 pos) { } // 0x13
+	virtual void seekToTime(uint32 time) { }
+	virtual void setFocused(bool focused) { } // 0x14
+	virtual void setVisible(bool visible); // 0x17
+	virtual void setGlobalVisible(bool enabled);
+	virtual void startPhase(uint phase); // 0x18
+	virtual void stop(); // 0x19
+	virtual void notify(uint16 data, uint16 from); // 0x1A
+	virtual void load();
+	virtual void unload();
+	virtual void moveBy(const Common::Point &pos);
+	virtual void moveTo(const Common::Point &pos);
+
+	LBItem *clone(uint16 newId, const Common::String &newName);
+
+	uint16 getId() { return _itemId; }
+	const Common::String &getName() { return _desc; }
+	const Common::Rect &getRect() { return _rect; }
+	uint16 getSoundPriority() { return _soundMode; }
+	bool isLoaded() { return _loaded; }
+	bool isAmbient() { return _isAmbient; }
+
+	Common::List<LBItem *>::iterator _iterator;
+
+	// TODO: make private
+	Common::HashMap<Common::String, LBValue, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> _variables;
+
+protected:
+	MohawkEngine_LivingBooks *_vm;
+	LBPage *_page;
+
+	void setNextTime(uint16 min, uint16 max);
+	void setNextTime(uint16 min, uint16 max, uint32 start);
+
+	Common::Rect _rect;
+	Common::String _desc;
+	uint16 _resourceId;
+	uint16 _itemId;
+
+	bool _loaded, _visible, _globalVisible, _playing, _enabled, _globalEnabled;
+
+	uint32 _nextTime, _startTime;
+	uint16 _loops;
+
+	uint16 _phase, _timingMode, _delayMin, _delayMax;
+	uint16 _loopMode, _periodMin, _periodMax;
+	uint16 _controlMode, _soundMode;
+	Common::Point _relocPoint;
+
+	bool _isAmbient;
+	bool _doHitTest;
+
+	virtual LBItem *createClone();
+
+	Common::Array<LBScriptEntry *> _scriptEntries;
+	void runScript(uint event, uint16 data = 0, uint16 from = 0);
+	int runScriptEntry(LBScriptEntry *entry);
+
+	void runCommand(const Common::String &command);
+	bool checkCondition(const Common::String &condition);
+
+	LBScriptEntry *parseScriptEntry(uint16 type, uint16 &size, Common::MemoryReadStreamEndian *stream, bool isSubentry = false);
+};
+
+} // End of namespace Mohawk
+
+#endif
diff --git a/engines/mohawk/livingbooks_itemscript.cpp b/engines/mohawk/livingbooks_itemscript.cpp
new file mode 100644
index 00000000000..73f83f50d5e
--- /dev/null
+++ b/engines/mohawk/livingbooks_itemscript.cpp
@@ -0,0 +1,834 @@
+/* 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 "mohawk/livingbooks.h"
+
+#include "common/memstream.h"
+#include "common/textconsole.h"
+
+namespace Mohawk {
+
+// Not to be confused with SCRP as "scripts". SCRP is for LBAnimation
+
+LBScriptEntry::LBScriptEntry() {
+	state = 0;
+	data = nullptr;
+	argvParam = nullptr;
+	argvTarget = nullptr;
+}
+
+LBScriptEntry::~LBScriptEntry() {
+	delete[] argvParam;
+	delete[] argvTarget;
+	delete[] data;
+
+	for (uint i = 0; i < subentries.size(); i++)
+		delete subentries[i];
+}
+
+void LBItem::readFrom(Common::SeekableReadStreamEndian *stream) {
+	_resourceId = stream->readUint16();
+	_itemId = stream->readUint16();
+	uint16 size = stream->readUint16();
+	_desc = _vm->readString(stream);
+
+	debug(2, "Item: size %d, resource %d, id %d", size, _resourceId, _itemId);
+	debug(2, "Coords: %d, %d, %d, %d", _rect.left, _rect.top, _rect.right, _rect.bottom);
+	debug(2, "String: '%s'", _desc.c_str());
+
+	if (!_itemId)
+		error("Item had invalid item id");
+
+	int endPos = stream->pos() + size;
+	if (endPos > stream->size())
+		error("Item is larger (should end at %d) than stream (size %d)", endPos, (int)stream->size());
+
+	while (true) {
+		if (stream->pos() == endPos)
+			break;
+
+		uint oldPos = stream->pos();
+
+		uint16 dataType = stream->readUint16();
+		uint16 dataSize = stream->readUint16();
+
+		debug(4, "Data type %04x, size %d", dataType, dataSize);
+		byte *buf = new byte[dataSize];
+		stream->read(buf, dataSize);
+		readData(dataType, dataSize, buf);
+		delete[] buf;
+
+		if ((uint)stream->pos() != oldPos + 4 + (uint)dataSize)
+			error("Failed to read correct number of bytes (off by %d) for data type %04x (size %d)",
+				(int)stream->pos() - (int)(oldPos + 4 + (uint)dataSize), dataType, dataSize);
+
+		if (stream->pos() > endPos)
+			error("Read off the end (at %d) of data (ends at %d)", (int)stream->pos(), endPos);
+
+		assert(!stream->eos());
+	}
+}
+
+LBScriptEntry *LBItem::parseScriptEntry(uint16 type, uint16 &size, Common::MemoryReadStreamEndian *stream, bool isSubentry) {
+	if (size < 6)
+		error("Script entry of type 0x%04x was too small (%d)", type, size);
+
+	uint16 expectedEndSize = 0;
+
+	LBScriptEntry *entry = new LBScriptEntry;
+	entry->type = type;
+	if (isSubentry) {
+		expectedEndSize = size - (stream->readUint16() + 2);
+		entry->event = 0xffff;
+	} else
+		entry->event = stream->readUint16();
+	entry->opcode = stream->readUint16();
+	entry->param = stream->readUint16();
+	debug(4, "Script entry: type 0x%04x, event 0x%04x, opcode 0x%04x, param 0x%04x",
+			entry->type, entry->event, entry->opcode, entry->param);
+	size -= 6;
+
+	// TODO: read as bytes, if this is correct (but beware endianism)
+	byte conditionTag = (entry->event & 0xff00) >> 8;
+	entry->event = entry->event & 0xff;
+
+	if (type == kLBMsgListScript && entry->opcode == kLBOpRunSubentries) {
+		debug(4, "%d script subentries:", entry->param);
+		entry->argc = 0;
+		for (uint i = 0; i < entry->param; i++) {
+			LBScriptEntry *subentry = parseScriptEntry(type, size, stream, true);
+			entry->subentries.push_back(subentry);
+
+			// subentries are aligned
+			if (i + 1 < entry->param && size % 2 == 1) {
+				stream->skip(1);
+				size--;
+			}
+		}
+	} else if (type == kLBMsgListScript) {
+		if (size < 2)
+			error("Script entry of type 0x%04x was too small (%d)", type, size);
+
+		entry->argc = stream->readUint16();
+		size -= 2;
+
+		entry->targetingType = 0;
+
+		uint16 targetingType = entry->argc;
+		if (targetingType == kTargetTypeExpression || targetingType == kTargetTypeCode
+			|| targetingType == kTargetTypeName) {
+			entry->targetingType = targetingType;
+
+			// FIXME
+			if (targetingType == kTargetTypeCode)
+				error("encountered kTargetTypeCode");
+
+			if (size < 2)
+				error("not enough bytes (%d) reading special targeting", size);
+			uint16 count = stream->readUint16();
+			size -= 2;
+
+			debug(4, "%d targets with targeting type %04x", count, targetingType);
+
+			uint oldAlign = size % 2;
+			for (uint i = 0; i < count; i++) {
+				Common::String target = _vm->readString(stream);
+				debug(4, "target '%s'", target.c_str());
+				entry->targets.push_back(target);
+				if (target.size() + 1 > size)
+					error("failed to read target (ran out of stream)");
+				size -= target.size() + 1;
+			}
+			entry->argc = entry->targets.size();
+
+			if ((uint)(size % 2) != oldAlign) {
+				stream->skip(1);
+				size--;
+			}
+		} else if (entry->argc) {
+			entry->argvParam = new uint16[entry->argc];
+			entry->argvTarget = new uint16[entry->argc];
+			debug(4, "With %d targets:", entry->argc);
+
+			if (size < (entry->argc * 4))
+				error("Script entry of type 0x%04x was too small (%d)", type, size);
+
+			for (uint i = 0; i < entry->argc; i++) {
+				entry->argvParam[i] = stream->readUint16();
+				entry->argvTarget[i] = stream->readUint16();
+				debug(4, "Target %d, param 0x%04x", entry->argvTarget[i], entry->argvParam[i]);
+			}
+
+			size -= (entry->argc * 4);
+		}
+	}
+
+	if (type == kLBMsgListScript && entry->opcode == kLBOpJumpUnlessExpression) {
+		if (size < 6)
+			error("not enough bytes (%d) in kLBOpJumpUnlessExpression, event 0x%04x", size, entry->event);
+		entry->offset = stream->readUint32();
+		entry->target = stream->readUint16();
+		debug(4, "kLBOpJumpUnlessExpression: offset %08x, target %d", entry->offset, entry->target);
+		size -= 6;
+	}
+	if (type == kLBMsgListScript && entry->opcode == kLBOpJumpToExpression) {
+		if (size < 4)
+			error("not enough bytes (%d) in kLBOpJumpToExpression, event 0x%04x", size, entry->event);
+		entry->offset = stream->readUint32();
+		debug(4, "kLBOpJumpToExpression: offset %08x", entry->offset);
+		size -= 4;
+	}
+
+	if (type == kLBNotifyScript && entry->opcode == kLBNotifyChangeMode && _vm->getGameType() != GType_LIVINGBOOKSV1) {
+		switch (entry->param) {
+		case 1:
+			if (size < 8)
+				error("%d unknown bytes in notify entry kLBNotifyChangeMode", size);
+			entry->newUnknown = stream->readUint16();
+			entry->newMode = stream->readUint16();
+			entry->newPage = stream->readUint16();
+			entry->newSubpage = stream->readUint16();
+			debug(4, "kLBNotifyChangeMode: unknown %04x, mode %d, page %d.%d",
+				entry->newUnknown, entry->newMode, entry->newPage, entry->newSubpage);
+			size -= 8;
+			break;
+		case 3:
+			{
+			Common::String newCursor = _vm->readString(stream);
+			entry->newCursor = newCursor;
+			if (size < newCursor.size() + 1)
+				error("failed to read newCursor in notify entry");
+			size -= newCursor.size() + 1;
+			debug(4, "kLBNotifyChangeMode: new cursor '%s'", newCursor.c_str());
+			}
+			break;
+		default:
+			// the original engine also does something when param==2 (but not a notify)
+			error("unknown v2 kLBNotifyChangeMode type %d", entry->param);
+		}
+	}
+	if (entry->opcode == kLBOpSendExpression) {
+		if (size < 4)
+			error("not enough bytes (%d) in kLBOpSendExpression, event 0x%04x", size, entry->event);
+		entry->offset = stream->readUint32();
+		debug(4, "kLBOpSendExpression: offset %08x", entry->offset);
+		size -= 4;
+	}
+	if (entry->opcode == kLBOpRunData) {
+		if (size < 4)
+			error("didn't get enough bytes (%d) to read data header in script entry", size);
+		entry->dataType = stream->readUint16();
+		entry->dataLen = stream->readUint16();
+		size -= 4;
+
+		if (size < entry->dataLen)
+			error("didn't get enough bytes (%d) to read data in script entry", size);
+
+		if (entry->dataType == kLBCommand) {
+			Common::String command = _vm->readString(stream);
+			uint commandSize = command.size() + 1;
+			if (commandSize > entry->dataLen)
+				error("failed to read command in script entry: dataLen %d, command '%s' (%d chars)",
+					 entry->dataLen, command.c_str(), commandSize);
+			entry->dataLen = commandSize;
+			entry->data = new byte[commandSize];
+			memcpy(entry->data, command.c_str(), commandSize);
+			size -= commandSize;
+		} else {
+			if (conditionTag)
+				error("kLBOpRunData had unexpected conditionTag");
+			entry->data = new byte[entry->dataLen];
+			stream->read(entry->data, entry->dataLen);
+			size -= entry->dataLen;
+		}
+	}
+	if (entry->event == kLBEventNotified) {
+		if (size < 4)
+			error("not enough bytes (%d) in kLBEventNotified, opcode 0x%04x", size, entry->opcode);
+		entry->matchFrom = stream->readUint16();
+		entry->matchNotify = stream->readUint16();
+		debug(4, "kLBEventNotified: matches %04x (from %04x)",
+			entry->matchNotify, entry->matchFrom);
+		size -= 4;
+	}
+
+	if (isSubentry) {
+		// TODO: subentries may be aligned, so this check is a bit too relaxed
+		if (size != expectedEndSize && size != expectedEndSize + 1)
+			error("expected %d bytes left at end of subentry, but had %d",
+				expectedEndSize, size);
+		return entry;
+	}
+
+	if (conditionTag == 1) {
+		if (!size)
+			error("failed to read condition (empty stream)");
+		Common::String condition = _vm->readString(stream);
+		if (condition.size() == 0) {
+			size--;
+			if (!size)
+				error("failed to read condition (null byte, then ran out of stream)");
+			condition = _vm->readString(stream);
+		}
+		if (condition.size() + 1 > size)
+			error("failed to read condition (ran out of stream)");
+		size -= (condition.size() + 1);
+
+		entry->conditions.push_back(condition);
+		debug(4, "script entry condition '%s'", condition.c_str());
+	} else if (conditionTag == 2) {
+		if (size < 4)
+			error("expected more than %d bytes for conditionTag 2", size);
+		// FIXME
+		stream->skip(4);
+		size -= 4;
+	}
+
+	if (size == 1) {
+		// FIXME: this is alignment, but why?
+		stream->skip(1);
+		size--;
+	} else if (size)
+		error("failed to read script entry correctly (%d bytes left): type 0x%04x, event 0x%04x, opcode 0x%04x, param 0x%04x",
+			size, entry->type, entry->event, entry->opcode, entry->param);
+
+	return entry;
+}
+
+void LBItem::readData(uint16 type, uint16 size, byte *data) {
+	Common::MemoryReadStreamEndian stream(data, size, _vm->isBigEndian());
+	readData(type, size, &stream);
+}
+
+void LBItem::readData(uint16 type, uint16 size, Common::MemoryReadStreamEndian *stream) {
+	switch (type) {
+	case kLBMsgListScript:
+	case kLBNotifyScript:
+		_scriptEntries.push_back(parseScriptEntry(type, size, stream));
+		break;
+
+	case kLBSetPlayInfo:
+		{
+		if (size != 20)
+			error("kLBSetPlayInfo had wrong size (%d)", size);
+
+		_loopMode = stream->readUint16();
+		_delayMin = stream->readUint16();
+		_delayMax = stream->readUint16();
+		_timingMode = stream->readUint16();
+		if (_timingMode > 7)
+			error("encountered timing mode %04x", _timingMode);
+		_periodMin = stream->readUint16();
+		_periodMax = stream->readUint16();
+		_relocPoint.x = stream->readSint16();
+		_relocPoint.y = stream->readSint16();
+		_controlMode = stream->readUint16();
+		_soundMode = stream->readUint16();
+
+		debug(2, "kLBSetPlayInfo: loop mode %d (%d to %d), timing mode %d (%d to %d), reloc (%d, %d), control mode %04x, sound mode %04x",
+			_loopMode, _delayMin, _delayMax,
+			_timingMode, _periodMin, _periodMax,
+			_relocPoint.x, _relocPoint.y,
+			_controlMode, _soundMode);
+		}
+		break;
+
+	case kLBSetPlayPhase:
+		if (size != 2)
+			error("SetPlayPhase had wrong size (%d)", size);
+		_phase = stream->readUint16();
+		debug(2, "kLBSetPlayPhase: %d", _phase);
+		break;
+
+	case kLBSetKeyNotify:
+		{
+		// FIXME: variable-size notifies, targets
+		if (size != 18)
+			error("0x6f had wrong size (%d)", size);
+		uint event = stream->readUint16();
+		LBKey key;
+		stream->read(&key, 4);
+		uint opcode = stream->readUint16();
+		uint param = stream->readUint16();
+		uint u6 = stream->readUint16();
+		uint u7 = stream->readUint16();
+		uint u8 = stream->readUint16();
+		uint u9 = stream->readUint16();
+		warning("ignoring kLBSetKeyNotify: item %s, key code %02x (modifier mask %d, char %d, repeat %d), event %04x, opcode %04x, param %04x, unknowns %04x, %04x, %04x, %04x",
+			_desc.c_str(), key.code, key.modifiers, key.char_, key.repeats, event, opcode, param, u6, u7, u8, u9);
+		}
+		break;
+
+	case kLBCommand:
+		{
+			Common::String command = _vm->readString(stream);
+			if (size != command.size() + 1)
+				error("failed to read command string");
+
+			runCommand(command);
+		}
+		break;
+
+	case kLBSetNotVisible:
+		assert(size == 0);
+		_visible = false;
+		break;
+
+	case kLBGlobalDisable:
+		assert(size == 0);
+		_globalEnabled = false;
+		break;
+
+	case kLBGlobalSetNotVisible:
+		assert(size == 0);
+		_globalVisible = false;
+		break;
+
+	case kLBSetAmbient:
+		assert(size == 0);
+		_isAmbient = true;
+		break;
+
+	case kLBSetKeyEvent:
+		{
+		// FIXME: targets
+		if (size != 10)
+			error("kLBSetKeyEvent had wrong size (%d)", size);
+		uint u3 = stream->readUint16();
+		LBKey key;
+		stream->read(&key, 4);
+		uint target = stream->readUint16();
+		uint16 event = stream->readUint16();
+		// FIXME: this is scripting stuff: what to run when key is pressed
+		warning("ignoring kLBSetKeyEvent: item %s, key code %02x (modifier mask %d, char %d, repeat %d) unknown %04x, target %d, event %04x",
+			_desc.c_str(), key.code, key.modifiers, key.char_, key.repeats, u3, target, event);
+		}
+		break;
+
+	case kLBSetHitTest:
+		{
+		assert(size == 2);
+		uint val = stream->readUint16();
+		_doHitTest = (bool)val;
+		debug(2, "kLBSetHitTest (on %s): value %04x", _desc.c_str(), val);
+		}
+		break;
+
+	case kLBSetRolloverData:
+		{
+		assert(size == 2);
+		uint16 flag = stream->readUint16();
+		warning("ignoring kLBSetRolloverData: item %s, flag %d", _desc.c_str(), flag);
+		}
+		break;
+
+	case kLBSetParent:
+		{
+		assert(size == 2);
+		uint16 parent = stream->readUint16();
+		warning("ignoring kLBSetParent: item %s, parent id %d", _desc.c_str(), parent);
+		}
+		break;
+
+	case kLBUnknown194:
+		{
+		assert(size == 4);
+		uint offset = stream->readUint32();
+		_page->_code->runCode(this, offset);
+		}
+		break;
+
+	default:
+		error("Unknown message %04x (size 0x%04x)", type, size);
+		//for (uint i = 0; i < size; i++)
+		//	debugN("%02x ", stream->readByte());
+		//debugN("\n");
+		break;
+	}
+}
+
+void LBItem::notify(uint16 data, uint16 from) {
+	if (_timingMode == kLBAutoSync) {
+		// TODO: is this correct?
+		if (_periodMin == data && _periodMax == from) {
+			debug(2, "Handling notify 0x%04x (from %d)", data, from);
+			setNextTime(0, 0);
+		}
+	}
+
+	runScript(kLBEventNotified, data, from);
+}
+
+void LBItem::runScript(uint event, uint16 data, uint16 from) {
+	for (uint i = 0; i < _scriptEntries.size(); i++) {
+		LBScriptEntry *entry = _scriptEntries[i];
+
+		if (entry->event != event)
+			continue;
+
+		if (event == kLBEventNotified) {
+			if ((entry->matchFrom && entry->matchFrom != from) || entry->matchNotify != data)
+				continue;
+		}
+
+		bool conditionsMatch = true;
+		for (uint n = 0; n < entry->conditions.size(); n++) {
+			if (!checkCondition(entry->conditions[n])) {
+				conditionsMatch = false;
+				break;
+			}
+		}
+		if (!conditionsMatch)
+			continue;
+
+		if (entry->type == kLBNotifyScript) {
+			debug(2, "Notify: event 0x%04x, opcode 0x%04x, param 0x%04x",
+				entry->event, entry->opcode, entry->param);
+
+			if (entry->opcode == kLBNotifyGUIAction)
+				_vm->addNotifyEvent(NotifyEvent(entry->opcode, _itemId));
+			else if (entry->opcode == kLBNotifyChangeMode && _vm->getGameType() != GType_LIVINGBOOKSV1) {
+				NotifyEvent notifyEvent(entry->opcode, entry->param);
+				notifyEvent.newUnknown = entry->newUnknown;
+				notifyEvent.newMode = entry->newMode;
+				notifyEvent.newPage = entry->newPage;
+				notifyEvent.newSubpage = entry->newSubpage;
+				notifyEvent.newCursor = entry->newCursor;
+				_vm->addNotifyEvent(notifyEvent);
+			} else
+				_vm->addNotifyEvent(NotifyEvent(entry->opcode, entry->param));
+		} else
+			runScriptEntry(entry);
+	}
+}
+
+int LBItem::runScriptEntry(LBScriptEntry *entry) {
+	if (entry->state == 0xffff)
+		return 0;
+
+	uint start = 0;
+	uint count = entry->argc;
+	// zero targets = apply to self
+	if (!count)
+		count = 1;
+
+	if (entry->opcode != kLBOpRunSubentries) switch (entry->param) {
+	case 0xfffe:
+		// Run once (disable self after run).
+		entry->state = 0xffff;
+		break;
+	case 0xffff:
+		break;
+	case 0:
+	case 1:
+	case 2:
+		start = entry->state;
+		entry->state++;
+		if (entry->state >= count) {
+			switch (entry->param) {
+			case 0:
+				// Disable..
+				entry->state = 0xffff;
+				return 0;
+			case 1:
+				// Stay at the end.
+				entry->state = count - 1;
+				break;
+			case 2:
+				// Loop.
+				entry->state = 0;
+				break;
+			default:
+				break;
+			}
+		}
+		count = 1;
+		break;
+	case 3:
+		// Pick random target.
+		start = _vm->_rnd->getRandomNumberRng(0, count);
+		count = 1;
+		break;
+	default:
+		warning("Weird param for script entry (type 0x%04x, event 0x%04x, opcode 0x%04x, param 0x%04x)",
+			entry->type, entry->event, entry->opcode, entry->param);
+	}
+
+	for (uint n = start; n < count; n++) {
+		LBItem *target;
+
+		debug(2, "Script run: type 0x%04x, event 0x%04x, opcode 0x%04x, param 0x%04x",
+			entry->type, entry->event, entry->opcode, entry->param);
+
+		if (entry->argc) {
+			switch (entry->targetingType) {
+			case kTargetTypeExpression:
+				{
+				// FIXME: this should be EVALUATED
+				LBValue &tgt = _vm->_variables[entry->targets[n]];
+				switch (tgt.type) {
+				case kLBValueItemPtr:
+					target = tgt.item;
+					break;
+				case kLBValueString:
+					// FIXME: handle 'self', at least
+					// TODO: correct otherwise? or only self?
+					target = _vm->getItemByName(tgt.string);
+					break;
+				case kLBValueInteger:
+					target = _vm->getItemById(tgt.integer);
+					break;
+				default:
+					// FIXME: handle list
+					warning("Target '%s' (by expression) resulted in unknown type, skipping", entry->targets[n].c_str());
+					continue;
+				}
+				}
+				if (!target) {
+					debug(2, "Target '%s' (by expression) doesn't exist, skipping", entry->targets[n].c_str());
+					continue;
+				}
+				debug(2, "Target: '%s' (expression '%s')", target->_desc.c_str(), entry->targets[n].c_str());
+				break;
+			case kTargetTypeCode:
+				// FIXME
+				error("encountered kTargetTypeCode");
+				break;
+			case kTargetTypeName:
+				// FIXME: handle 'self'
+				target = _vm->getItemByName(entry->targets[n]);
+				if (!target) {
+					debug(2, "Target '%s' (by name) doesn't exist, skipping", entry->targets[n].c_str());
+					continue;
+				}
+				debug(2, "Target: '%s' (by name)", target->_desc.c_str());
+				break;
+			default:
+				uint16 targetId = entry->argvTarget[n];
+				// TODO: is this type, perhaps?
+				uint16 param = entry->argvParam[n];
+				target = _vm->getItemById(targetId);
+				if (!target) {
+					debug(2, "Target %04x (%04x) doesn't exist, skipping", targetId, param);
+					continue;
+				}
+				debug(2, "Target: %04x (%04x) '%s'", targetId, param, target->_desc.c_str());
+			}
+		} else {
+			target = this;
+			debug(2, "Self-target on '%s'", _desc.c_str());
+		}
+
+		// an opcode in the form 0x1xx means to run the script for event 0xx
+		if ((entry->opcode & 0xff00) == 0x0100) {
+			// FIXME: pass on param
+			target->runScript(entry->opcode & 0xff);
+			break;
+		}
+
+		switch (entry->opcode) {
+		case kLBOpNone:
+			warning("ignoring kLBOpNone (event 0x%04x, param 0x%04x, target '%s')",
+					entry->event, entry->param, target->_desc.c_str());
+			break;
+
+		case kLBOpXShow:
+			// TODO: should be setVisible(true) - not a delayed event -
+			// when we're doing the param 1/2/3 stuff above?
+			// and in modern LB this is perhaps just a direct target->setVisible(true)..
+			if (_vm->getGameType() != GType_LIVINGBOOKSV1)
+				warning("kLBOpXShow on '%s' is probably broken", target->_desc.c_str());
+			_vm->queueDelayedEvent(DelayedEvent(this, kLBDelayedEventSetNotVisible));
+			break;
+
+		case kLBOpTogglePlay:
+			target->togglePlaying(false, true);
+			break;
+
+		case kLBOpSetNotVisible:
+			target->setVisible(false);
+			break;
+
+		case kLBOpSetVisible:
+			target->setVisible(true);
+			break;
+
+		case kLBOpDestroy:
+			target->destroySelf();
+			break;
+
+		case kLBOpRewind:
+			target->seek(1);
+			break;
+
+		case kLBOpStop:
+			target->stop();
+			break;
+
+		case kLBOpDisable:
+			target->setEnabled(false);
+			break;
+
+		case kLBOpEnable:
+			target->setEnabled(true);
+			break;
+
+		case kLBOpGlobalSetNotVisible:
+			target->setGlobalVisible(false);
+			break;
+
+		case kLBOpGlobalSetVisible:
+			target->setGlobalVisible(true);
+			break;
+
+		case kLBOpGlobalDisable:
+			target->setGlobalEnabled(false);
+			break;
+
+		case kLBOpGlobalEnable:
+			target->setGlobalEnabled(true);
+			break;
+
+		case kLBOpSeekToEnd:
+			target->seek(0xFFFF);
+			break;
+
+		case kLBOpMute:
+		case kLBOpUnmute:
+			// FIXME
+			warning("ignoring kLBOpMute/Unmute (event 0x%04x, param 0x%04x, target '%s')",
+				entry->event, entry->param, target->_desc.c_str());
+			break;
+
+		case kLBOpLoad:
+			target->load();
+			break;
+
+		case kLBOpPreload:
+			// FIXME
+			warning("ignoring kLBOpPreload (event 0x%04x, param 0x%04x, target '%s')",
+				entry->event, entry->param, target->_desc.c_str());
+			break;
+
+		case kLBOpUnload:
+			target->unload();
+			break;
+
+		case kLBOpSeekToPrev:
+		case kLBOpSeekToNext:
+			// FIXME
+			warning("ignoring kLBOpSeekToPrev/Next (event 0x%04x, param 0x%04x, target '%s')",
+				entry->event, entry->param, target->_desc.c_str());
+			break;
+
+		case kLBOpDragBegin:
+		case kLBOpDragEnd:
+			// FIXME
+			warning("ignoring kLBOpDragBegin/End (event 0x%04x, param 0x%04x, target '%s')",
+				entry->event, entry->param, target->_desc.c_str());
+			break;
+
+		case kLBOpScriptDisable:
+		case kLBOpScriptEnable:
+			// FIXME
+			warning("ignoring kLBOpScriptDisable/Enable (event 0x%04x, param 0x%04x, target '%s')",
+				entry->event, entry->param, target->_desc.c_str());
+			break;
+
+		case kLBOpUnknown1C:
+			// FIXME
+			warning("ignoring kLBOpUnknown1C (event 0x%04x, param 0x%04x, target '%s')",
+				entry->event, entry->param, target->_desc.c_str());
+			break;
+
+		case kLBOpSendExpression:
+			_page->_code->runCode(this, entry->offset);
+			break;
+
+		case kLBOpRunSubentries:
+			for (uint i = 0; i < entry->subentries.size(); i++) {
+				LBScriptEntry *subentry = entry->subentries[i];
+
+				int e = runScriptEntry(subentry);
+
+				switch (subentry->opcode) {
+				case kLBOpJumpUnlessExpression:
+					debug(2, "JumpUnless got %d (to %d, on %d, of %d)", e, subentry->target, i, entry->subentries.size());
+					if (!e)
+						i = subentry->target - 1;
+					break;
+				case kLBOpBreakExpression:
+					debug(2, "BreakExpression");
+					i = entry->subentries.size();
+					break;
+				case kLBOpJumpToExpression:
+					debug(2, "JumpToExpression got %d (on %d, of %d)", e, i, entry->subentries.size());
+					i = e - 1;
+					break;
+				default:
+					break;
+				}
+			}
+			break;
+
+		case kLBOpRunData:
+			readData(entry->dataType, entry->dataLen, entry->data);
+			break;
+
+		case kLBOpJumpUnlessExpression:
+		case kLBOpBreakExpression:
+		case kLBOpJumpToExpression:
+			{
+			LBValue r = _page->_code->runCode(this, entry->offset);
+			// FIXME
+			return r.integer;
+			}
+
+		default:
+			error("Unknown script opcode (type 0x%04x, event 0x%04x, opcode 0x%04x, param 0x%04x, target '%s')",
+				entry->type, entry->event, entry->opcode, entry->param, target->_desc.c_str());
+		}
+	}
+
+	return 0;
+}
+
+void LBItem::runCommand(const Common::String &command) {
+	LBCode tempCode(_vm, 0);
+
+	debug(2, "running command '%s'", command.c_str());
+
+	uint offset = tempCode.parseCode(command);
+	tempCode.runCode(this, offset);
+}
+
+bool LBItem::checkCondition(const Common::String &condition) {
+	LBCode tempCode(_vm, 0);
+
+	debug(3, "checking condition '%s'", condition.c_str());
+
+	uint offset = tempCode.parseCode(condition);
+	LBValue result = tempCode.runCode(this, offset);
+
+	return result.toInt();
+}
+
+} // End of namespace Mohawk
diff --git a/engines/mohawk/livingbooks_itemscript.h b/engines/mohawk/livingbooks_itemscript.h
new file mode 100644
index 00000000000..620d241aa45
--- /dev/null
+++ b/engines/mohawk/livingbooks_itemscript.h
@@ -0,0 +1,82 @@
+/* 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 MOHAWK_LIVINGBOOKS_ITEMSCRIPT_H
+#define MOHAWK_LIVINGBOOKS_ITEMSCRIPT_H
+
+#include "common/scummsys.h"
+#include "common/array.h"
+#include "common/str.h"
+
+namespace Mohawk {
+
+struct LBKey {
+	byte code;
+	byte modifiers;
+	byte char_;
+	byte repeats;
+};
+
+struct LBScriptEntry {
+	LBScriptEntry();
+	~LBScriptEntry();
+
+	uint16 state;
+
+	uint16 type;
+	uint16 event;
+	uint16 opcode;
+	uint16 param;
+
+	uint16 argc;
+	uint16 *argvParam;
+	uint16 *argvTarget;
+
+	uint16 targetingType;
+	Common::Array<Common::String> targets;
+
+	// kLBNotifyChangeMode
+	uint16 newUnknown;
+	uint16 newMode;
+	uint16 newPage;
+	uint16 newSubpage;
+	Common::String newCursor;
+
+	// kLBEventNotified
+	uint16 matchFrom;
+	uint16 matchNotify;
+
+	// kLBOpSendExpression
+	uint32 offset;
+	// kLBOpJumpUnlessExpression
+	uint16 target;
+
+	uint16 dataType;
+	uint16 dataLen;
+	byte *data;
+
+	Common::Array<Common::String> conditions;
+	Common::Array<LBScriptEntry *> subentries;
+};
+
+} // End of namespace Mohawk
+
+#endif
diff --git a/engines/mohawk/livingbooks_itemtypes.h b/engines/mohawk/livingbooks_itemtypes.h
new file mode 100644
index 00000000000..2d1cc52aa31
--- /dev/null
+++ b/engines/mohawk/livingbooks_itemtypes.h
@@ -0,0 +1,205 @@
+/* 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 MOHAWK_LIVINGBOOKS_ITEMTYPES_H
+#define MOHAWK_LIVINGBOOKS_ITEMTYPES_H
+
+#include "mohawk/livingbooks_item.h"
+
+#include "common/array.h"
+#include "common/rect.h"
+
+namespace Common {
+	class MemoryReadStreamEndian;
+}
+
+namespace Mohawk {
+
+class MohawkEngine_LivingBooks;
+class LBPage;
+
+class LBSoundItem : public LBItem {
+public:
+	LBSoundItem(MohawkEngine_LivingBooks *_vm, LBPage *page, Common::Rect rect);
+	~LBSoundItem() override;
+
+	void update() override;
+	bool togglePlaying(bool playing, bool restart) override;
+	void stop() override;
+
+protected:
+	LBItem *createClone() override;
+
+	bool _running;
+};
+
+struct GroupEntry {
+	uint entryId;
+	uint entryType;
+};
+
+class LBGroupItem : public LBItem {
+public:
+	LBGroupItem(MohawkEngine_LivingBooks *_vm, LBPage *page, Common::Rect rect);
+
+	void readData(uint16 type, uint16 size, Common::MemoryReadStreamEndian *stream) override;
+
+	void destroySelf() override;
+	void setEnabled(bool enabled) override;
+	void setGlobalEnabled(bool enabled) override;
+	bool contains(Common::Point point) override;
+	bool togglePlaying(bool playing, bool restart) override;
+	// 0x12
+	void seek(uint16 pos) override;
+	void setVisible(bool visible) override;
+	void setGlobalVisible(bool visible) override;
+	void startPhase(uint phase) override;
+	void stop() override;
+	void load() override;
+	void unload() override;
+	void moveBy(const Common::Point &pos) override;
+	void moveTo(const Common::Point &pos) override;
+
+protected:
+	LBItem *createClone() override;
+
+	bool _starting;
+
+	Common::Array<GroupEntry> _groupEntries;
+};
+
+class LBPaletteItem : public LBItem {
+public:
+	LBPaletteItem(MohawkEngine_LivingBooks *_vm, LBPage *page, Common::Rect rect);
+	~LBPaletteItem() override;
+
+	void readData(uint16 type, uint16 size, Common::MemoryReadStreamEndian *stream) override;
+
+	bool togglePlaying(bool playing, bool restart) override;
+	void update() override;
+
+protected:
+	LBItem *createClone() override;
+
+	uint16 _fadeInPeriod, _fadeInStep, _drawStart, _drawCount;
+	uint32 _fadeInStart, _fadeInCurrent;
+	byte *_palette;
+};
+
+struct LiveTextWord {
+	Common::Rect bounds;
+	uint16 soundId;
+
+	uint16 itemType;
+	uint16 itemId;
+};
+
+struct LiveTextPhrase {
+	uint16 wordStart, wordCount;
+	uint16 highlightStart, highlightEnd;
+	uint16 startId, endId;
+};
+
+class LBLiveTextItem : public LBItem {
+public:
+	LBLiveTextItem(MohawkEngine_LivingBooks *_vm, LBPage *page, Common::Rect rect);
+
+	void readData(uint16 type, uint16 size, Common::MemoryReadStreamEndian *stream) override;
+
+	bool contains(Common::Point point) override;
+	void update() override;
+	void draw() override;
+	void handleMouseDown(Common::Point pos) override;
+	bool togglePlaying(bool playing, bool restart) override;
+	void stop() override;
+	void notify(uint16 data, uint16 from) override;
+
+protected:
+	LBItem *createClone() override;
+
+	void paletteUpdate(uint16 word, bool on);
+	void drawWord(uint word, uint yPos);
+
+	uint16 _currentPhrase, _currentWord;
+
+	byte _backgroundColor[4];
+	byte _foregroundColor[4];
+	byte _highlightColor[4];
+	uint16 _paletteIndex;
+
+	Common::Array<LiveTextWord> _words;
+	Common::Array<LiveTextPhrase> _phrases;
+};
+
+class LBPictureItem : public LBItem {
+public:
+	LBPictureItem(MohawkEngine_LivingBooks *_vm, LBPage *page, Common::Rect rect);
+
+	void readData(uint16 type, uint16 size, Common::MemoryReadStreamEndian *stream) override;
+
+	bool contains(Common::Point point) override;
+	void draw() override;
+	void init() override;
+
+protected:
+	LBItem *createClone() override;
+};
+
+class LBMovieItem : public LBItem {
+public:
+	LBMovieItem(MohawkEngine_LivingBooks *_vm, LBPage *page, Common::Rect rect);
+	~LBMovieItem() override;
+
+	void update() override;
+	bool togglePlaying(bool playing, bool restart) override;
+
+protected:
+	LBItem *createClone() override;
+};
+
+class LBMiniGameItem : public LBItem {
+public:
+	LBMiniGameItem(MohawkEngine_LivingBooks *_vm, LBPage *page, Common::Rect rect);
+	~LBMiniGameItem() override;
+
+	bool togglePlaying(bool playing, bool restart) override;
+
+protected:
+	LBItem *createClone() override;
+};
+
+class LBProxyItem : public LBItem {
+public:
+	LBProxyItem(MohawkEngine_LivingBooks *_vm, LBPage *page, Common::Rect rect);
+	~LBProxyItem() override;
+
+	void load() override;
+	void unload() override;
+
+protected:
+	LBItem *createClone() override;
+
+	class LBPage *_page;
+};
+
+} // End of namespace Mohawk
+
+#endif
diff --git a/engines/mohawk/livingbooks_page.h b/engines/mohawk/livingbooks_page.h
new file mode 100644
index 00000000000..02e095964b7
--- /dev/null
+++ b/engines/mohawk/livingbooks_page.h
@@ -0,0 +1,61 @@
+/* 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 MOHAWK_LIVINGBOOKS_PAGE_H
+#define MOHAWK_LIVINGBOOKS_PAGE_H
+
+#include "common/array.h"
+
+namespace Mohawk {
+
+class MohawkEngine_LivingBooks;
+class LBItem;
+class LBCode;
+class Archive;
+
+class LBPage {
+public:
+	LBPage(MohawkEngine_LivingBooks *vm);
+	~LBPage();
+
+	void open(Archive *mhk, uint16 baseId);
+	uint16 getResourceVersion();
+
+	void addClonedItem(LBItem *item);
+	void itemDestroyed(LBItem *item);
+
+	LBCode *_code;
+
+protected:
+	MohawkEngine_LivingBooks *_vm;
+
+	Archive *_mhk;
+	Common::Array<LBItem *> _items;
+
+	uint16 _baseId;
+	bool _cascade;
+
+	void loadBITL(uint16 resourceId);
+};
+
+} // End of namespace Mohawk
+
+#endif
diff --git a/engines/mohawk/module.mk b/engines/mohawk/module.mk
index b264987cae7..c7119b0f973 100644
--- a/engines/mohawk/module.mk
+++ b/engines/mohawk/module.mk
@@ -12,6 +12,8 @@ MODULE_OBJS = \
 	livingbooks_bookinfo.o \
 	livingbooks_code.o \
 	livingbooks_graphics.o \
+	livingbooks_item.o \
+	livingbooks_itemscript.o \
 	livingbooks_itemtypes.o \
 	livingbooks_lbx.o \
 	livingbooks_menus.o \



From noreply at scummvm.org  Sun Jun 14 10:01:52 2026
From: noreply at scummvm.org (ScummVM-Translations)
Date: Sun, 14 Jun 2026 10:01:52 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm-web master ->
 17b015843095131c5ed2af0b79f7bbf5ec4cf0ba
Message-ID: <20260614100152.7E1527005CC@ron.scummvm.net>

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

Summary:
33ba5af70c L10N: Update translation (French) (News)
17b0158430 L10N: Update translation (Spanish) (News)


Commit: 33ba5af70c609c605e059a79119e3c5083d69002
    https://github.com/scummvm/scummvm-web/commit/33ba5af70c609c605e059a79119e3c5083d69002
Author: Purple T (ZEONK at hotmail.com)
Date: 2026-06-14T10:01:45Z

Commit Message:
L10N: Update translation (French) (News)

Currently translated at 100.0% (878 of 878 strings)

Changed paths:
    data/fr/news.json


diff --git a/data/fr/news.json b/data/fr/news.json
index 97642cc7..d5ce6d05 100644
--- a/data/fr/news.json
+++ b/data/fr/news.json
@@ -1746,5 +1746,13 @@
     "20260503": {
         "title": "5 millions de lignes de code. 5 millions d'histoires.",
         "content": "Il y a quelques semaines, nous avons franchi discr??tement une ??tape importante pour le projet ScummVM ; une ??tape dont nous sommes tr??s fiers.\n\nD'apr??s l'analyse du d??p??t [r??alis??e par Open Hub](https://openhub.net/p/scummvm/), la base de code de ScummVM contient d??sormais plus de **5 millions de lignes de code**, r??dig??es par plus de **900 contributeurs** sur pr??s de 25 ans.\n\nChers contributeurs, au nom de toute la communaut?? ScummVM : des milliers de personnes utilisent votre code chaque jour, revivant leurs souvenirs d???enfance ou explorant des histoires qu???elles n???ont pas encore d??couvertes.\n\nC???est votre passion et votre pers??v??rance qui rendent cela possible. Vous n?????crivez pas seulement l???histoire, vous la pr??servez.\n\nMerci !"
+    },
+    "20260612": {
+        "title": "Une nuance de Grey plus sombre??: Noctropolis est pr??t pour les tests???!",
+        "content": "Vous en avez assez de tenir une banale librairie et vous voulez vous plonger dans les pages sombres et baign??es de n??ons d???une bande dessin??e ? Pr??parez votre trench-coat, car *Noctropolis*, le jeu d'aventure sombre de Flashpoint Productions datant de 1994, est d??sormais pr??te pour la phase de test public !\n\nDans cette bande dessin??e interactive qui prend vie, vous incarnez Peter Grey, un libraire solitaire entra??n?? dans un monde dystopique de super-h??ros o?? le soleil ne brille jamais. Le l??gendaire h??ros Darksheer ayant pris sa retraite, c'est ?? vous de reprendre son r??le, de r??soudre les ??nigmes et de sauver la ville d'une bande de m??chants agissant au service d'une force myst??rieuse.\n\nPour nous aider ?? tester le jeu, vous aurez besoin de votre copie des [fichiers de donn??es du jeu](https://wiki.scummvm.org/index.php?title=Noctropolis#Required_Data) et d'une [compilation quotidienne r??cente](https://www.scummvm.org/downloads/#daily) de ScummVM.\n\nLa prise en charge a ??t?? ajout??e ?? la fois pour la version DOS originale (d??mos comprises) et pour la r????dition num??rique de Nightdive Studios, disponible sur [GOG](https://www.gog.com/game/noctropolis) ou [Steam](https://store.steampowered.com/app/377070).\n\nPendant que vous patrouillez dans les rues dangereuses, merci de signaler tout probl??me rencontr?? sur notre [outil de suivi de bugs](https://bugs.scummvm.org/).\n\nLa ville a besoin d'un h??ros, r??pondrez-vous ?? l'appel ? Bonne lutte contre le crime !"
+    },
+    "20260613": {
+        "title": "La F??te foraine des Cartoons ouvre ses portes !",
+        "content": "Approchez ! Approchez ! _Hanna-Barbera's Cartoon Carnival_ est d??sormais pr??t pour les tests publics sur ScummVM.\n\nD??velopp?? et commercialis?? ?? l'origine pour le Philips CD-i en 1993 par Funhouse Design, puis port?? sur Windows et Macintosh en 1995, _Cartoon Carnival_ rassemble une galerie de personnages connus de Hanna-Barbera dans une collection de mini-jeux courts de type arcade. Scooby-Doo, Fred Pierrafeu, George Jetson, le Pacha et leurs amis se relaient pour relever diff??rents d??fis, allant des labyrinthes et des jeux de correspondance de motifs ?? la capture de ballons et aux quiz.\n\nPlut??t qu'un jeu d'aventure traditionnel, il s'agit d'un divertissement multim??dia haut en couleur du milieu des ann??es 90, riche en animations, voix et musique. C'est ??galement l'un de ces titres sous licence qui peuvent facilement passer inaper??us, c'est pourquoi nous sommes particuli??rement heureux de contribuer ?? le rendre jouable sur les syst??mes modernes.\n\nPour l'instant, seules les versions Windows et Macintosh sont prises en charge. La version CD-i originale n'est pas prise en charge.\n\nPour tester le jeu, vous aurez besoin de la derni??re [compilation quotidienne](https://www.scummvm.org/downloads/#daily) et soit d'une copie originale de la version Windows ou Macintosh, soit de sa d??mo (que nous n'avons pu trouver qu'en italien). Veuillez consulter la [page wiki](https://wiki.scummvm.org/index.php?title=Cartoon_Carnival) pour conna??tre les fichiers de donn??es requis.\n\nComme d'habitude, merci de signaler tout probl??me sur notre [bug tracker](https://bugs.scummvm.org/), en pr??cisant la version que vous avez test??e, votre plateforme, ainsi que toute capture d'??cran ou fichier de sauvegarde utile.\n\nAlors approchez, choisissez un jeu et aidez-nous ?? nous assurer que cette f??te foraine fonctionne correctement !"
     }
 }


Commit: 17b015843095131c5ed2af0b79f7bbf5ec4cf0ba
    https://github.com/scummvm/scummvm-web/commit/17b015843095131c5ed2af0b79f7bbf5ec4cf0ba
Author: IlDucci (pajaroloco_2 at hotmail.com)
Date: 2026-06-14T10:01:46Z

Commit Message:
L10N: Update translation (Spanish) (News)

Currently translated at 94.3% (828 of 878 strings)

Changed paths:
    data/es/news.json


diff --git a/data/es/news.json b/data/es/news.json
index 414200ee..832bb44b 100644
--- a/data/es/news.json
+++ b/data/es/news.json
@@ -1750,5 +1750,9 @@
     "20260612": {
         "title": "Sombras m??s oscuras de gris: ??Noctropolis est?? listo para ser probado!",
         "content": "??Os hab??is cansado de regentar una librer??a mundana y ten??is ganas de sumergiros en las crudas p??ginas repletas de neones de un c??mic? Buscad una gabardina, ??porque la aventura *noir* de Flashpoint Productions de 1994, _Noctropolis_ ya est?? lista para ser probada!\n\nEn este c??mic interactivo que cobra vida controlar??is a Peter Grey, un solitario due??o de una librer??a que se ve arrastrado a un mundo dist??pico de superh??roes en el que el sol nunca brilla. Tras el retiro del legendario h??roe Darksheer, tendr??is que ocupar su lugar, resolver los enigmas y salvar a la ciudad de un plantel de villanos a ??rdenes de una fuerza misteriosa.\n\nPara ayudarnos con las pruebas necesitar??is vuestras copias de [los archivos de datos del juego](https://wiki.scummvm.org/index.php?title=Noctropolis#Required_Data) y una [compilaci??n diaria de desarrollo](https://www.scummvm.org/downloads/#daily) reciente de ScummVM.\n\nActualmente son compatibles la versi??n original para DOS (demos incluidas) y el relanzamiento digital de Nightdive Studios, que est?? disponible en [GOG](https://www.gog.com/game/noctropolis) o en [Steam](https://store.steampowered.com/app/377070).\n\nOs rogamos que, durante vuestras patrullas por estas peligrosas calles, inform??is de cualquier fallo que encontr??is en nuestro [gestor de fallos](https://bugs.scummvm.org/).\n\nLa ciudad necesita a un h??roe... ??Estar??is a la altura? ??Pasadlo bien luchando contra el crimen!"
+    },
+    "20260613": {
+        "title": "??El carnaval animado abre sus puertas!",
+        "content": "??Pasen y vean, ni??os y ni??as! _Hanna-Barbera's Cartoon Carnival_ ya est?? abierto a pruebas p??blicas en ScummVM.\n\n_Cartoon Carnival_ fue dise??ado y publicado originalmente por Funhouse Design para la Philips CD-i en 1993 y posteriormente convertido a Windows y Macintosh en 1995, y os presenta a un buen plantel de conocidos personajes de Hanna-Barbera en una colecci??n de minijuegos estilo arcade. Scooby-Doo, Pedro Picapiedra, S??per S??nico, Don Gato y sus amigos participar??n en toda clase de pruebas, pasando por laberintos y juegos de encontrar patrones hasta llegar a capturas de globos y rondas de preguntas y respuestas.\n\nM??s que una aventura tradicional, esta es una obra de entretenimiento multimedia de mediados de la d??cada de 1990 completa con animaciones, voces y m??sicas. Tambi??n es uno de esos juegos licenciados que pueden olvidarse f??cilmente, as?? que estamos especialmente contentos de poder hacer que sea jugable en sistemas modernos.\n\nEn estos momentos solo son compatibles las versiones para Windows y Macintosh. La versi??n original para CD-i no es compatible.\n\nPara probar el juego necesitar??is la [??ltima compilaci??n diaria](https://www.scummvm.org/downloads/#daily) y o bien una copia original de la versi??n para Windows o para Macintosh, o bien de su demo (que solo hemos podido localizar en italiano). Para saber cu??les son los archivos de datos necesarios, leed [su p??gina en la wiki](https://wiki.scummvm.org/index.php?title=Cartoon_Carnival).\n\nComo de costumbre, os rogamos que inform??is de cualquier fallo en [nuestro gestor de fallos (en ingl??s)](https://bugs.scummvm.org/) e indiqu??is tambi??n la versi??n que hab??is probado, vuestra plataforma, y que adjunt??is cualquier captura de pantalla o partida guardada que puedan ser ??tiles.\n\n??Pasen y vean, elijan juegos y ay??dennos a que el carnaval vaya como la seda!"
     }
 }



From noreply at scummvm.org  Sun Jun 14 10:03:33 2026
From: noreply at scummvm.org (bluegr)
Date: Sun, 14 Jun 2026 10:03:33 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 75d4ccb0c2dcec21b62aebb29921cdc5b2736e79
Message-ID: <20260614100333.8D91D7005CC@ron.scummvm.net>

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

Summary:
75d4ccb0c2 NANCY: Implement new functionality for percentage values in ValueTest


Commit: 75d4ccb0c2dcec21b62aebb29921cdc5b2736e79
    https://github.com/scummvm/scummvm/commit/75d4ccb0c2dcec21b62aebb29921cdc5b2736e79
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-06-14T13:03:26+03:00

Commit Message:
NANCY: Implement new functionality for percentage values in ValueTest

Fixes the feed bin gauge and puzzle in Nancy 10

Changed paths:
    engines/nancy/action/datarecords.cpp


diff --git a/engines/nancy/action/datarecords.cpp b/engines/nancy/action/datarecords.cpp
index 500ab2797af..fec5fbc1676 100644
--- a/engines/nancy/action/datarecords.cpp
+++ b/engines/nancy/action/datarecords.cpp
@@ -216,11 +216,14 @@ static const byte kTestAllSingle			= 1;
 static const byte kTestSome					= 2;
 static const byte kTestActualValue			= 3;
 
-static const byte kTestEqualTo				= 0;
-static const byte kTestLessThan				= 1;
-static const byte kTestGreaterThan			= 2;
-static const byte kTestGreaterThanOrEqual	= 3;
-static const byte kTestLessThanOrEqual		= 4;
+static const byte kTestEqualTo                      = 0;
+static const byte kTestLessThan                     = 1;
+static const byte kTestGreaterThan                  = 2;
+static const byte kTestGreaterThanOrEqual           = 3;
+static const byte kTestLessThanOrEqual              = 4;
+static const byte kTestPercentEqualTo               = 5;
+static const byte kTestPercentGreaterThanOrEqual    = 6;
+static const byte kTestPercentLessThanOrEqual       = 7;
 
 void ValueTest::execute() {
 	TableData *playerTable = (TableData *)NancySceneState.getPuzzleData(TableData::getTag());
@@ -262,6 +265,7 @@ void ValueTest::execute() {
 	}
 
 	bool satisfied = false;
+
 	for (uint i = 0; i < testedIndices.size(); ++i) {
 		if (testedIndices[i] == kNoTableIndex) {
 			continue;
@@ -270,6 +274,8 @@ void ValueTest::execute() {
 		float otherValue = 0;
 		if (_testType == kTestActualValue) {
 			otherValue = testedIndices[i];
+			if (_condition >= kTestPercentEqualTo)
+				otherValue = ((float)_indicesToTest[0] * (float)_indicesToTest[1]) / 100.0f;
 		} else {
 			if (testedIndices[i] < numSingleValues) {
 				// Test against single value
@@ -286,34 +292,22 @@ void ValueTest::execute() {
 
 		switch (_condition) {
 		case kTestEqualTo:
-			if (testedValue == otherValue) {
-				satisfied = true;
-			}
-
+		case kTestPercentEqualTo:
+			satisfied = (testedValue == otherValue);
 			break;
 		case kTestLessThan:
-			if (testedValue < otherValue) {
-				satisfied = true;
-			}
-
+			satisfied = (testedValue < otherValue);
 			break;
 		case kTestGreaterThan:
-			if (testedValue > otherValue) {
-				satisfied = true;
-			}
-
+			satisfied = (testedValue > otherValue);
 			break;
 		case kTestGreaterThanOrEqual:
-			if (testedValue >= otherValue) {
-				satisfied = true;
-			}
-
+		case kTestPercentGreaterThanOrEqual:
+			satisfied = (testedValue >= otherValue);
 			break;
 		case kTestLessThanOrEqual:
-			if (testedValue <= otherValue) {
-				satisfied = true;
-			}
-
+		case kTestPercentLessThanOrEqual:
+			satisfied = (testedValue <= otherValue);
 			break;
 		}
 



From noreply at scummvm.org  Sun Jun 14 10:42:03 2026
From: noreply at scummvm.org (tag2015)
Date: Sun, 14 Jun 2026 10:42:03 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 a87fc1f141db3e95d9f2780ade776993bd16dbbb
Message-ID: <20260614104203.8BEB57005CC@ron.scummvm.net>

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:
b3b35fd05c WINTERMUTE: Set tehran2016 as stable
a87fc1f141 WINTERMUTE: Add 1/2 ritter english fanmade


Commit: b3b35fd05c199ac7481d546eda39bdff04de9562
    https://github.com/scummvm/scummvm/commit/b3b35fd05c199ac7481d546eda39bdff04de9562
Author: Walter Agazzi (walter.agazzi at protonmail.com)
Date: 2026-06-14T12:41:17+02:00

Commit Message:
WINTERMUTE: Set tehran2016 as stable

Game is completable

Changed paths:
    engines/wintermute/detection_tables.h


diff --git a/engines/wintermute/detection_tables.h b/engines/wintermute/detection_tables.h
index d47b2c83b41..d9d86812e4e 100644
--- a/engines/wintermute/detection_tables.h
+++ b/engines/wintermute/detection_tables.h
@@ -5621,7 +5621,7 @@ static const WMEGameDescription gameDescriptions[] = {
 
 	// Murder In Tehran's Alleys 2016 (Steam, June 2017) (English)
 	WME_WINENTRY("tehran2016", "",
-		WME_ENTRY1s("data.dcp", "952346c4d7a08986688b3cc583215d33", 1163612836), Common::EN_ANY, ADGF_UNSTABLE | GF_3D, WME_1_9_3),
+		WME_ENTRY1s("data.dcp", "952346c4d7a08986688b3cc583215d33", 1163612836), Common::EN_ANY, GF_3D, WME_1_9_3),
 
 	// The Ancient Mark - Episode 1
 	WME_WINENTRY("theancientmark1", "",


Commit: a87fc1f141db3e95d9f2780ade776993bd16dbbb
    https://github.com/scummvm/scummvm/commit/a87fc1f141db3e95d9f2780ade776993bd16dbbb
Author: Walter Agazzi (walter.agazzi at protonmail.com)
Date: 2026-06-14T12:41:17+02:00

Commit Message:
WINTERMUTE: Add 1/2 ritter english fanmade

Changed paths:
    engines/wintermute/detection_tables.h


diff --git a/engines/wintermute/detection_tables.h b/engines/wintermute/detection_tables.h
index d9d86812e4e..0681daf3109 100644
--- a/engines/wintermute/detection_tables.h
+++ b/engines/wintermute/detection_tables.h
@@ -4314,6 +4314,11 @@ static const WMEGameDescription gameDescriptions[] = {
 		WME_ENTRY2s("data.dcp", "5ac416cee605d3a30f4d59687b1cdab2", 364260278,
 					"string3.tab", "a20120c9a45f189321924adb54f3515f", 527627), Common::ES_ESP, ADGF_NO_FLAGS, WME_1_8_6),
 
+	// 1 1/2 Ritter: Auf der Suche nach der hinreissenden Herzelinde (English fanmade)
+	WME_WINENTRY("ritter", "Fanmade",
+		WME_ENTRY2s("data.dcp", "5ac416cee605d3a30f4d59687b1cdab2", 364260278,
+					"string3.tab", "413ee9f47f52567d0ac22e34485cacfa", 513012), Common::EN_ANY, ADGF_NO_FLAGS, WME_1_8_6),
+
 	// 1 1/2 Ritter: Auf der Suche nach der hinreissenden Herzelinde (Czech fanmade)
 	WME_WINENTRY("ritter", "",
 		WME_ENTRY1s("data.dcp", "1138bca234b7e81e11128c4da2c283b0", 364067513), Common::CS_CZE, ADGF_NO_FLAGS, WME_1_8_6),



From noreply at scummvm.org  Sun Jun 14 17:09:39 2026
From: noreply at scummvm.org (ScummVM-Translations)
Date: Sun, 14 Jun 2026 17:09:39 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm-web master ->
 2161d8cbe88660928855d041a674b0c40ca84a92
Message-ID: <20260614170939.CA3CC7005CC@ron.scummvm.net>

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

Summary:
2161d8cbe8 L10N: Update translation (Italian) (News)


Commit: 2161d8cbe88660928855d041a674b0c40ca84a92
    https://github.com/scummvm/scummvm-web/commit/2161d8cbe88660928855d041a674b0c40ca84a92
Author: Walter Agazzi (walter.agazzi at protonmail.com)
Date: 2026-06-14T17:09:33Z

Commit Message:
L10N: Update translation (Italian) (News)

Currently translated at 29.0% (255 of 878 strings)

Changed paths:
    data/it/news.json


diff --git a/data/it/news.json b/data/it/news.json
index 032903eb..ec014124 100644
--- a/data/it/news.json
+++ b/data/it/news.json
@@ -505,5 +505,9 @@
     "20260503": {
         "title": "5 milioni di linee di codice. 5 milioni di storie.",
         "content": "Un paio di settimane fa abbiamo silenziosamente raggiunto un importante traguardo per il progetto ScummVM, un traguardo di cui siamo molto orgogliosi.\n\nSecondo l'analisi del repository [effettuata da Open Hub](https://openhub.net/p/scummvm/), la codebase di ScummVM contiene ora pi?? di **5 milioni di linee di codice**, scritte da pi?? di **900 collaboratori** nell'arco di circa 25 anni.\n\nCari collaboratori, a nome dell'intera community di ScummVM: Migliaia di persone utilizzano il vostro codice ogni giorno, rivivendo ricordi d'infanzia o esplorando storie ancora tutte da scoprire.\n\nLa vostra passione e dedizione hanno reso possibile tutto ci??. Non state solo scrivendo la storia, state contribuendo a preservarla.\n\nGrazie infinite!"
+    },
+    "20260612": {
+        "title": "Il lato oscuro di Grey: Noctropolis ?? pronto per la fase di test!",
+        "content": "Stanco di gestire una monotona libreria? Pronto a immergerti nelle pagine decadenti e illuminate da neon colorati di un fumetto di supereroi? Prepara l'impermeabile, perch?? _Noctropolis_ , un'avventura dark del 1994 rilasciata dalla Flashpoint Productions, ?? pronta per la fase di test pubblica!\n\nIn questo fumetto interattivo impersoni Peter Grey, un solitario libraio che viene trascinato in un mondo distopico dove il sole non sorge mai. Dopo il ritiro del leggendario eroe Darksheer, toccher?? a te ereditarne il ruolo, risolvendo puzzle e salvando la citt?? da un manipolo di malvagi al soldo di una entit?? misteriosa.\n\nPer aiutarci nella fase di test, recupera i [file dati](https://wiki.scummvm.org/index.php?title=Noctropolis#Required_Data) dalla tua copia del gioco e scarica una [build di sviluppo](https://www.scummvm.org/downloads/#daily) recente di ScummVM.\n\nIl supporto ?? stato aggiunto sia per la versione originale per DOS (demo compresi) che per la riedizione digitale della Nightdive Studios, disponibile su [GOG](https://www.gog.com/game/noctropolis) o [Steam](https://store.steampowered.com/app/377070).\n\nMentre pattugli le strade pi?? pericolose, assicurati di segnalare qualsiasi inconveniente sul nostro [bug tracker](https://bugs.scummvm.org/).\n\nLa citt?? ha bisogno di un eroe - sarai proprio tu? Buona lotta al crimine!"
     }
 }



From noreply at scummvm.org  Sun Jun 14 17:32:32 2026
From: noreply at scummvm.org (sev-)
Date: Sun, 14 Jun 2026 17:32:32 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm-web master ->
 0bf615e6783888277d4c18fb8018de9f7cd2dac4
Message-ID: <20260614173232.DB0007005CC@ron.scummvm.net>

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

Summary:
0bf615e678 NEWS: Announce Alfred pelrock support


Commit: 0bf615e6783888277d4c18fb8018de9f7cd2dac4
    https://github.com/scummvm/scummvm-web/commit/0bf615e6783888277d4c18fb8018de9f7cd2dac4
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2026-06-14T19:31:18+02:00

Commit Message:
NEWS: Announce Alfred pelrock support

Changed paths:
  A data/en/news/20260614.markdown


diff --git a/data/en/news/20260614.markdown b/data/en/news/20260614.markdown
new file mode 100644
index 00000000..e69de29b



From noreply at scummvm.org  Sun Jun 14 17:34:11 2026
From: noreply at scummvm.org (lotharsm)
Date: Sun, 14 Jun 2026 17:34:11 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm-web master ->
 35e7817a2a2421fb15bda76727030e000761cc16
Message-ID: <20260614173411.082907005CC@ron.scummvm.net>

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

Summary:
35e7817a2a DATA: Update data files


Commit: 35e7817a2a2421fb15bda76727030e000761cc16
    https://github.com/scummvm/scummvm-web/commit/35e7817a2a2421fb15bda76727030e000761cc16
Author: sev- (sev at scummvm.org)
Date: 2026-06-14T17:34:06Z

Commit Message:
DATA: Update data files

Changed paths:
    data/en/compatibility.yaml
    data/en/engines.yaml


diff --git a/data/en/compatibility.yaml b/data/en/compatibility.yaml
index c37762d0..71575298 100644
--- a/data/en/compatibility.yaml
+++ b/data/en/compatibility.yaml
@@ -5795,3 +5795,10 @@
     since_version: DEV
     stable_platforms: 'win,mac'
     unstable_platforms: ''
+-
+    id: 'pelrock:pelrock'
+    support: good
+    notes: ''
+    since_version: DEV
+    stable_platforms: dos
+    unstable_platforms: ''
diff --git a/data/en/engines.yaml b/data/en/engines.yaml
index 645d069e..17c42d3c 100644
--- a/data/en/engines.yaml
+++ b/data/en/engines.yaml
@@ -373,7 +373,7 @@
     id: pelrock
     name: 'Alfred Pelrock'
     alt_name: ''
-    enabled: false
+    enabled: true
 -
     id: petka
     name: 'Red Comrades'



From noreply at scummvm.org  Sun Jun 14 17:36:00 2026
From: noreply at scummvm.org (sev-)
Date: Sun, 14 Jun 2026 17:36:00 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm-web master ->
 73de5bc51c24528108cd3b2de591bffc09800239
Message-ID: <20260614173600.8C29D7005CC@ron.scummvm.net>

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

Summary:
73de5bc51c NEWS: Added the actual news post


Commit: 73de5bc51c24528108cd3b2de591bffc09800239
    https://github.com/scummvm/scummvm-web/commit/73de5bc51c24528108cd3b2de591bffc09800239
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2026-06-14T19:35:48+02:00

Commit Message:
NEWS: Added the actual news post

Changed paths:
    data/en/news/20260614.markdown


diff --git a/data/en/news/20260614.markdown b/data/en/news/20260614.markdown
index e69de29b..a691ec4c 100644
--- a/data/en/news/20260614.markdown
+++ b/data/en/news/20260614.markdown
@@ -0,0 +1,12 @@
+---
+title: "Alfred Pelrock joins ScummVM"
+author: kelmer
+date: 1781458091
+---
+Good news for adventure game fans: _Alfred Pelrock_ is now supported in ScummVM!
+
+Originally released in 1996, _Alfred Pelrock_ is a humorous point-and-click adventure featuring cartoon-style graphics, quirky puzzles, and an irreverent sense of humor. After dreaming of an Egyptian princess imprisoned in the distant past, Alfred embarks on a time-travelling adventure to Ancient Egypt to rescue her, encountering bizarre situations and eccentric characters along the way.
+
+While the game is packed with comedy and satire, players should be aware that it also includes mildly suggestive themes and occasional adult-oriented humor typical of its era. It is also worth noting that the game was released exclusively in Spanish and has no official translations.
+
+As always, if you own an original copy of the game and speak Spanish, grab the latest [daily development build](https://www.scummvm.org/downloads/#daily), add the game???s [required data files](https://wiki.scummvm.org/index.php?title=Alfred_Pelrock:_En_Busca_de_un_Sue%C3%B1o) and enjoy. We also welcome [bug reports](https://bugs.scummvm.org/). and feedback from players to help improve compatibility and preserve this quirky piece of adventure gaming history.



From noreply at scummvm.org  Sun Jun 14 17:36:47 2026
From: noreply at scummvm.org (lotharsm)
Date: Sun, 14 Jun 2026 17:36:47 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm-web master ->
 015f62bb1dc4fd9f2777b4bd428ccde006517e0b
Message-ID: <20260614173647.811CB7005CC@ron.scummvm.net>

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

Summary:
015f62bb1d L10N: Update base news translation file


Commit: 015f62bb1dc4fd9f2777b4bd428ccde006517e0b
    https://github.com/scummvm/scummvm-web/commit/015f62bb1dc4fd9f2777b4bd428ccde006517e0b
Author: sev- (sev at scummvm.org)
Date: 2026-06-14T17:36:43Z

Commit Message:
L10N: Update base news translation file

Changed paths:
    data/en/news.json


diff --git a/data/en/news.json b/data/en/news.json
index c459df45..db5a30a8 100644
--- a/data/en/news.json
+++ b/data/en/news.json
@@ -1754,5 +1754,9 @@
     "20260613": {
         "title": "The Cartoon Carnival opens its gates!",
         "content": "Come one, come all! _Hanna-Barbera's Cartoon Carnival_ is now ready for public testing in ScummVM.\n\nOriginally developed and released for Philips CD-i in 1993 by Funhouse Design and then ported to Windows and Macintosh in 1995, _Cartoon Carnival_ brings together a cast of familiar Hanna-Barbera characters in a collection of short arcade-style minigames. Scooby-Doo, Fred Flintstone, George Jetson, Top Cat and friends each get their turn in different challenges, ranging from mazes and pattern matching games to balloon catching and trivia quizzes.\n\nRather than a traditional adventure game, this is a colorful piece of mid-90???s multimedia entertainment, full of animation, voices and music. It is also one of those licensed titles that can easily slip through the cracks, so we are especially happy to help keep it playable on modern systems.\n\nAt this time, only the Windows and Macintosh versions are supported. The original CD-i version is not supported.\n\nTo test the game, you will need the latest [daily development build](https://www.scummvm.org/downloads/#daily) and either an original copy of the Windows or Macintosh release, or its demo (which we were only able to source in Italian). Please check the [wiki page](https://wiki.scummvm.org/index.php?title=Cartoon_Carnival) for the required data files.\n\nAs usual, please report any issues to our [bug tracker](https://bugs.scummvm.org/), including the version you tested, your platform, and any useful screenshots or save files.\n\nStep right up, pick a game, and help us make sure this carnival runs properly!"
+    },
+    "20260614": {
+        "title": "Alfred Pelrock joins ScummVM",
+        "content": "Good news for adventure game fans: _Alfred Pelrock_ is now supported in ScummVM!\n\nOriginally released in 1996, _Alfred Pelrock_ is a humorous point-and-click adventure featuring cartoon-style graphics, quirky puzzles, and an irreverent sense of humor. After dreaming of an Egyptian princess imprisoned in the distant past, Alfred embarks on a time-travelling adventure to Ancient Egypt to rescue her, encountering bizarre situations and eccentric characters along the way.\n\nWhile the game is packed with comedy and satire, players should be aware that it also includes mildly suggestive themes and occasional adult-oriented humor typical of its era. It is also worth noting that the game was released exclusively in Spanish and has no official translations.\n\nAs always, if you own an original copy of the game and speak Spanish, grab the latest [daily development build](https://www.scummvm.org/downloads/#daily), add the game???s [required data files](https://wiki.scummvm.org/index.php?title=Alfred_Pelrock:_En_Busca_de_un_Sue%C3%B1o) and enjoy. We also welcome [bug reports](https://bugs.scummvm.org/). and feedback from players to help improve compatibility and preserve this quirky piece of adventure gaming history."
     }
 }



From noreply at scummvm.org  Sun Jun 14 17:36:51 2026
From: noreply at scummvm.org (ScummVM-Translations)
Date: Sun, 14 Jun 2026 17:36:51 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm-web master ->
 04a5d74d5e5b52ec694a174935e7ebf8862a6c54
Message-ID: <20260614173651.AAD387005CF@ron.scummvm.net>

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

Summary:
04a5d74d5e L10N: Update translation (Italian) (News)


Commit: 04a5d74d5e5b52ec694a174935e7ebf8862a6c54
    https://github.com/scummvm/scummvm-web/commit/04a5d74d5e5b52ec694a174935e7ebf8862a6c54
Author: Walter Agazzi (walter.agazzi at protonmail.com)
Date: 2026-06-14T17:36:48Z

Commit Message:
L10N: Update translation (Italian) (News)

Currently translated at 29.1% (256 of 878 strings)

Changed paths:
    data/it/news.json


diff --git a/data/it/news.json b/data/it/news.json
index ec014124..e486cce4 100644
--- a/data/it/news.json
+++ b/data/it/news.json
@@ -509,5 +509,8 @@
     "20260612": {
         "title": "Il lato oscuro di Grey: Noctropolis ?? pronto per la fase di test!",
         "content": "Stanco di gestire una monotona libreria? Pronto a immergerti nelle pagine decadenti e illuminate da neon colorati di un fumetto di supereroi? Prepara l'impermeabile, perch?? _Noctropolis_ , un'avventura dark del 1994 rilasciata dalla Flashpoint Productions, ?? pronta per la fase di test pubblica!\n\nIn questo fumetto interattivo impersoni Peter Grey, un solitario libraio che viene trascinato in un mondo distopico dove il sole non sorge mai. Dopo il ritiro del leggendario eroe Darksheer, toccher?? a te ereditarne il ruolo, risolvendo puzzle e salvando la citt?? da un manipolo di malvagi al soldo di una entit?? misteriosa.\n\nPer aiutarci nella fase di test, recupera i [file dati](https://wiki.scummvm.org/index.php?title=Noctropolis#Required_Data) dalla tua copia del gioco e scarica una [build di sviluppo](https://www.scummvm.org/downloads/#daily) recente di ScummVM.\n\nIl supporto ?? stato aggiunto sia per la versione originale per DOS (demo compresi) che per la riedizione digitale della Nightdive Studios, disponibile su [GOG](https://www.gog.com/game/noctropolis) o [Steam](https://store.steampowered.com/app/377070).\n\nMentre pattugli le strade pi?? pericolose, assicurati di segnalare qualsiasi inconveniente sul nostro [bug tracker](https://bugs.scummvm.org/).\n\nLa citt?? ha bisogno di un eroe - sarai proprio tu? Buona lotta al crimine!"
+    },
+    "20260613": {
+        "title": "Le porte del Cartoon Carnival sono aperte!"
     }
 }



From noreply at scummvm.org  Sun Jun 14 17:40:47 2026
From: noreply at scummvm.org (sev-)
Date: Sun, 14 Jun 2026 17:40:47 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm-web master ->
 ba82e3af23f0b9f56e66120b600c56cbf48d528d
Message-ID: <20260614174047.D6B107005CC@ron.scummvm.net>

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

Summary:
ba82e3af23 WEB: Put pelrock screenshots to theor respective hierarchy


Commit: ba82e3af23f0b9f56e66120b600c56cbf48d528d
    https://github.com/scummvm/scummvm-web/commit/ba82e3af23f0b9f56e66120b600c56cbf48d528d
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2026-06-14T19:40:34+02:00

Commit Message:
WEB: Put pelrock screenshots to theor respective hierarchy

Changed paths:
  A public_html/data/screenshots/pelrock/pelrock/pelrock_dos_es_1_1.jpg
  A public_html/data/screenshots/pelrock/pelrock/pelrock_dos_es_1_1_full.png
  A public_html/data/screenshots/pelrock/pelrock/pelrock_dos_es_1_2.jpg
  A public_html/data/screenshots/pelrock/pelrock/pelrock_dos_es_1_2_full.png
  A public_html/data/screenshots/pelrock/pelrock/pelrock_dos_es_1_3.jpg
  A public_html/data/screenshots/pelrock/pelrock/pelrock_dos_es_1_3_full.png
  A public_html/data/screenshots/pelrock/pelrock/pelrock_dos_es_1_4.jpg
  A public_html/data/screenshots/pelrock/pelrock/pelrock_dos_es_1_4_full.png
  R public_html/data/screenshots/pelrock/pelrock_dos_es_1_1.jpg
  R public_html/data/screenshots/pelrock/pelrock_dos_es_1_1_full.png
  R public_html/data/screenshots/pelrock/pelrock_dos_es_1_2.jpg
  R public_html/data/screenshots/pelrock/pelrock_dos_es_1_2_full.png
  R public_html/data/screenshots/pelrock/pelrock_dos_es_1_3.jpg
  R public_html/data/screenshots/pelrock/pelrock_dos_es_1_3_full.png
  R public_html/data/screenshots/pelrock/pelrock_dos_es_1_4.jpg
  R public_html/data/screenshots/pelrock/pelrock_dos_es_1_4_full.png


diff --git a/public_html/data/screenshots/pelrock/pelrock_dos_es_1_1.jpg b/public_html/data/screenshots/pelrock/pelrock/pelrock_dos_es_1_1.jpg
similarity index 100%
rename from public_html/data/screenshots/pelrock/pelrock_dos_es_1_1.jpg
rename to public_html/data/screenshots/pelrock/pelrock/pelrock_dos_es_1_1.jpg
diff --git a/public_html/data/screenshots/pelrock/pelrock_dos_es_1_1_full.png b/public_html/data/screenshots/pelrock/pelrock/pelrock_dos_es_1_1_full.png
similarity index 100%
rename from public_html/data/screenshots/pelrock/pelrock_dos_es_1_1_full.png
rename to public_html/data/screenshots/pelrock/pelrock/pelrock_dos_es_1_1_full.png
diff --git a/public_html/data/screenshots/pelrock/pelrock_dos_es_1_2.jpg b/public_html/data/screenshots/pelrock/pelrock/pelrock_dos_es_1_2.jpg
similarity index 100%
rename from public_html/data/screenshots/pelrock/pelrock_dos_es_1_2.jpg
rename to public_html/data/screenshots/pelrock/pelrock/pelrock_dos_es_1_2.jpg
diff --git a/public_html/data/screenshots/pelrock/pelrock_dos_es_1_2_full.png b/public_html/data/screenshots/pelrock/pelrock/pelrock_dos_es_1_2_full.png
similarity index 100%
rename from public_html/data/screenshots/pelrock/pelrock_dos_es_1_2_full.png
rename to public_html/data/screenshots/pelrock/pelrock/pelrock_dos_es_1_2_full.png
diff --git a/public_html/data/screenshots/pelrock/pelrock_dos_es_1_3.jpg b/public_html/data/screenshots/pelrock/pelrock/pelrock_dos_es_1_3.jpg
similarity index 100%
rename from public_html/data/screenshots/pelrock/pelrock_dos_es_1_3.jpg
rename to public_html/data/screenshots/pelrock/pelrock/pelrock_dos_es_1_3.jpg
diff --git a/public_html/data/screenshots/pelrock/pelrock_dos_es_1_3_full.png b/public_html/data/screenshots/pelrock/pelrock/pelrock_dos_es_1_3_full.png
similarity index 100%
rename from public_html/data/screenshots/pelrock/pelrock_dos_es_1_3_full.png
rename to public_html/data/screenshots/pelrock/pelrock/pelrock_dos_es_1_3_full.png
diff --git a/public_html/data/screenshots/pelrock/pelrock_dos_es_1_4.jpg b/public_html/data/screenshots/pelrock/pelrock/pelrock_dos_es_1_4.jpg
similarity index 100%
rename from public_html/data/screenshots/pelrock/pelrock_dos_es_1_4.jpg
rename to public_html/data/screenshots/pelrock/pelrock/pelrock_dos_es_1_4.jpg
diff --git a/public_html/data/screenshots/pelrock/pelrock_dos_es_1_4_full.png b/public_html/data/screenshots/pelrock/pelrock/pelrock_dos_es_1_4_full.png
similarity index 100%
rename from public_html/data/screenshots/pelrock/pelrock_dos_es_1_4_full.png
rename to public_html/data/screenshots/pelrock/pelrock/pelrock_dos_es_1_4_full.png



From noreply at scummvm.org  Sun Jun 14 17:53:05 2026
From: noreply at scummvm.org (ScummVM-Translations)
Date: Sun, 14 Jun 2026 17:53:05 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm-web master ->
 d2246f8b170820950b8b2098a47a87af98350eb9
Message-ID: <20260614175305.438A97005CC@ron.scummvm.net>

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

Summary:
d2246f8b17 L10N: Update translation (Italian) (News)


Commit: d2246f8b170820950b8b2098a47a87af98350eb9
    https://github.com/scummvm/scummvm-web/commit/d2246f8b170820950b8b2098a47a87af98350eb9
Author: Walter Agazzi (walter.agazzi at protonmail.com)
Date: 2026-06-14T17:53:00Z

Commit Message:
L10N: Update translation (Italian) (News)

Currently translated at 29.2% (257 of 880 strings)

Changed paths:
    data/it/news.json


diff --git a/data/it/news.json b/data/it/news.json
index e486cce4..0302ebd1 100644
--- a/data/it/news.json
+++ b/data/it/news.json
@@ -511,6 +511,7 @@
         "content": "Stanco di gestire una monotona libreria? Pronto a immergerti nelle pagine decadenti e illuminate da neon colorati di un fumetto di supereroi? Prepara l'impermeabile, perch?? _Noctropolis_ , un'avventura dark del 1994 rilasciata dalla Flashpoint Productions, ?? pronta per la fase di test pubblica!\n\nIn questo fumetto interattivo impersoni Peter Grey, un solitario libraio che viene trascinato in un mondo distopico dove il sole non sorge mai. Dopo il ritiro del leggendario eroe Darksheer, toccher?? a te ereditarne il ruolo, risolvendo puzzle e salvando la citt?? da un manipolo di malvagi al soldo di una entit?? misteriosa.\n\nPer aiutarci nella fase di test, recupera i [file dati](https://wiki.scummvm.org/index.php?title=Noctropolis#Required_Data) dalla tua copia del gioco e scarica una [build di sviluppo](https://www.scummvm.org/downloads/#daily) recente di ScummVM.\n\nIl supporto ?? stato aggiunto sia per la versione originale per DOS (demo compresi) che per la riedizione digitale della Nightdive Studios, disponibile su [GOG](https://www.gog.com/game/noctropolis) o [Steam](https://store.steampowered.com/app/377070).\n\nMentre pattugli le strade pi?? pericolose, assicurati di segnalare qualsiasi inconveniente sul nostro [bug tracker](https://bugs.scummvm.org/).\n\nLa citt?? ha bisogno di un eroe - sarai proprio tu? Buona lotta al crimine!"
     },
     "20260613": {
-        "title": "Le porte del Cartoon Carnival sono aperte!"
+        "title": "Le porte del Cartoon Carnival sono aperte!",
+        "content": "Siete tutti invitati! _Hanna-Barbera's Cartoon Carnival_ ?? pronto per la fase di test pubblica di ScummVM.\n\nOriginariamente sviluppato e rilasciato nel 1993 dalla Funhouse Design, esclusivamente per Philips CD-i, ?? stato poi portato su Windows e Macintosh nel 1995. _Cartoon Carnival_ raccoglie un cast di popolari personaggi di Hanna-Barbera in una collezione di brevi mini-giochi in stile arcade. Scooby-Doo, Fred Flintstone, George Jetson, Top Cat e compagnia partecipano a turno a diverse sfide, spaziando tra labirinti, puzzle visuali, giochi con i palloni e quiz testuali.\n\nPi?? che un tradizionale gioco di avventura, si tratta di un perfetto esempio di intrattenimento multimediale anni '90, ricco di animazioni, voci e musica. Inoltre, ?? un titolo su licenza che pu?? facilmente venire dimenticato col passare degli anni, e pertanto siamo particolarmente felici di preservarlo per i sistemi contemporanei.\n\nAttualmente, solo le versioni Windows e Macintosh sono supportate. La versione originale per CD-i non ?? supportata.\n\nPer testare il gioco, avrai bisogno dell'ultimo [daily build di sviluppo](https://www.scummvm.org/downloads/#daily) e la versione originale per Windows o Macintosh, o la versione dimostrativa (per ora, abbiamo reperito solo quella in italiano). Consulta la [pagina wiki](https://wiki.scummvm.org/index.php?title=Cartoon_Carnival) per l'elenco dei file necessari.\n\nCome sempre, cortesemente segnala qualsiasi malfunzionamento al nostro [bug tracker](https://bugs.scummvm.org/), specificando la versione testata, la piattaforma, ed eventualmente allegando screenshot utili o salvataggi.\n\nFatti avanti, scegli un gioco, e aiutaci a far funzionare questa festa al meglio!"
     }
 }



From noreply at scummvm.org  Sun Jun 14 18:41:57 2026
From: noreply at scummvm.org (sev-)
Date: Sun, 14 Jun 2026 18:41:57 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 71f3bee330047c5dbad2ae2ed04a0f7622af4685
Message-ID: <20260614184157.1BBBC7005CC@ron.scummvm.net>

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:
cca0e910ad TWP: Remove time.h dependency
71f3bee330 COMMON: Utility functions for converting a TimeDate into an integer and vice versa


Commit: cca0e910adcd13998b1fb71a8bc7b516d067ceda
    https://github.com/scummvm/scummvm/commit/cca0e910adcd13998b1fb71a8bc7b516d067ceda
Author: scemino (scemino74 at gmail.com)
Date: 2026-06-14T20:41:52+02:00

Commit Message:
TWP: Remove time.h dependency

Changed paths:
    engines/twp/metaengine.cpp
    engines/twp/time.cpp
    engines/twp/time.h


diff --git a/engines/twp/metaengine.cpp b/engines/twp/metaengine.cpp
index f905d80246b..3c09db4ef93 100644
--- a/engines/twp/metaengine.cpp
+++ b/engines/twp/metaengine.cpp
@@ -83,7 +83,7 @@ const Common::AchievementDescriptionList *TwpMetaEngine::getAchievementDescripti
 }
 
 static Common::String getDesc(const Twp::SaveGame &savegame) {
-	Common::String desc = Twp::formatTime(savegame.time, "%b %d at %H:%M");
+	Common::String desc = Twp::formatTime(savegame.time);
 	if (savegame.easyMode)
 		desc += " (casual)";
 	return desc;
@@ -115,11 +115,11 @@ SaveStateDescriptor TwpMetaEngine::querySaveMetaInfos(const char *target, int sl
 		Twp::SaveGame savegame;
 		Twp::SaveGameManager::getSaveGame(f, savegame);
 		Common::String savegameDesc(getDesc(savegame));
-		Twp::DateTime dt = Twp::toDateTime(savegame.time);
+		TimeDate dt = Twp::intToTimeDate(savegame.time);
 		desc.setDescription(savegameDesc);
 		desc.setPlayTime(savegame.gameTime * 1000);
-		desc.setSaveDate(dt.year, dt.month, dt.day);
-		desc.setSaveTime(dt.hour, dt.min);
+		desc.setSaveDate(dt.tm_year, dt.tm_mon, dt.tm_mday);
+		desc.setSaveTime(dt.tm_hour, dt.tm_min);
 
 		return desc;
 	}
diff --git a/engines/twp/time.cpp b/engines/twp/time.cpp
index c645070b2c3..6dd2de9efed 100644
--- a/engines/twp/time.cpp
+++ b/engines/twp/time.cpp
@@ -19,34 +19,118 @@
  *
  */
 
-#define FORBIDDEN_SYMBOL_ALLOW_ALL
-#include <time.h>
+#include "common/system.h"
 #include "twp/time.h"
 
 namespace Twp {
 
-Common::String formatTime(int64 t, const char *format) {
-	time_t time = (time_t)t;
-	struct tm *tm = localtime(&time);
-	char buf[64];
-	strftime(buf, 64, format, tm);
-	return Common::String(buf);
+enum {
+	SECONDS_IN_DAY = 86400, // number of seconds in a day
+	SECONDS_IN_HOUR = 3600,// number of seconds in an hour
+	SECONDS_IN_MINUTE = 60
+};
+
+Common::String formatTime(int64 t) {
+	const char* monthList[12] = {
+		"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
+	};
+	const TimeDate timeDate = intToTimeDate(t);
+	return Common::String::format( "%s %d at %.2d:%.2d", monthList[timeDate.tm_mon], timeDate.tm_mday, timeDate.tm_hour, timeDate.tm_min);
+}
+
+static bool isLeapYear(int year) {
+	return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
 }
 
-DateTime toDateTime(int64 t) {
-	time_t time = (time_t)t;
-	struct tm *tm = localtime(&time);
-	DateTime dateTime;
-	dateTime.year = 1900 + tm->tm_year;
-	dateTime.month = 1 + tm->tm_mon;
-	dateTime.day = tm->tm_mday;
-	dateTime.hour = tm->tm_hour;
-	dateTime.min = tm->tm_min;
+static int64_t dateTimeToInt64(const TimeDate &timeDate) {
+	const int64_t year  = (int64_t)timeDate.tm_year + 1900; // fixup year
+	const int month = timeDate.tm_mon;
+	const int days_in_month[2][12] = {
+		{ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, // non-leap year
+		{ 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } // leap year, February has 29 days
+	};
+
+	// Count days from the Unix epoch (1970-01-01) to Jan 1 of `year`
+	/*
+	 * For a year Y, the number of leap years in [1, Y-1] is:
+	 *   floor((Y-1)/4) - floor((Y-1)/100) + floor((Y-1)/400)
+	 * Days from year 0 to Jan 1 of Y = 365*Y + leaps
+	 * Subtract the same value for 1970 to get the offset.
+	 */
+	const int64_t y0     = year  - 1;
+	const int64_t y1970  = 1969; // 1970 - 1
+
+	const int64_t leaps0    = y0    / 4 - y0    / 100 + y0    / 400;
+	const int64_t leaps1970 = y1970 / 4 - y1970 / 100 + y1970 / 400;
+
+	int64_t days = 365 * (year - 1970) + (leaps0 - leaps1970);
+
+	// add days for the months elapsed in the current year
+	const int leap = isLeapYear((int)year);
+	for (int m = 0; m < month; m++)
+		days += days_in_month[leap][m];
+
+	// add remaining date/time fields
+	days += (int64_t)timeDate.tm_mday - 1;
+
+	const int64_t seconds = days * SECONDS_IN_DAY
+		+ timeDate.tm_hour * SECONDS_IN_HOUR
+		+ timeDate.tm_min  * SECONDS_IN_MINUTE
+		+ timeDate.tm_sec;
+
+	return seconds;
+}
+
+TimeDate intToTimeDate(int64 t) {
+	TimeDate dateTime;
+	int64_t seconds = t;
+	dateTime.tm_sec = seconds % SECONDS_IN_MINUTE;
+	seconds /= SECONDS_IN_MINUTE;
+	dateTime.tm_min = seconds % 60;
+	seconds /= 60;
+	dateTime.tm_hour = seconds % 24;
+	seconds /= 24;
+
+	// Now `seconds` is the number of days since the Unix epoch (1970-01-01)
+	// We can find the year by subtracting the number of days in each year until we find the correct one.
+	int year = 1970;
+	while (true) {
+		const int days_in_year = isLeapYear(year) ? 366 : 365;
+		if (seconds >= days_in_year) {
+			seconds -= days_in_year;
+			year++;
+			continue;
+		}
+		break;
+	}
+	dateTime.tm_year = year - 1900; // fixup year
+
+	// Now `seconds` is the number of days elapsed in the current year. We can find the month and day similarly.
+	const int days_in_month[2][12] = {
+		{ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, // non-leap year
+		{ 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }  // leap-year, February has 29 days
+	};
+	const int leap = isLeapYear(year);
+	int month = 0;
+	while (month < 12) {
+		const int dim = days_in_month[leap][month];
+		if (seconds >= dim) {
+			seconds -= dim;
+			month++;
+		} else {
+			break;
+		}
+	}
+	dateTime.tm_mon = month; // tm_mon is 0-based
+	dateTime.tm_mday = (int)seconds + 1;
+
 	return dateTime;
 }
 
 int64 getTime() {
-	return (int64)time(NULL);
+	TimeDate dateTimeTm;
+	g_system->getTimeAndDate(dateTimeTm);
+	return dateTimeToInt64(dateTimeTm);
 }
 
 } // namespace Twp
diff --git a/engines/twp/time.h b/engines/twp/time.h
index a7bbfb00391..822bcb237ab 100644
--- a/engines/twp/time.h
+++ b/engines/twp/time.h
@@ -26,13 +26,18 @@
 
 namespace Twp {
 
-struct DateTime {
-	int year, month, day;
-	int hour, min;
-};
+/// @brief Convert a time value (number of seconds since the Unix epoch) to a human-readable string format.
+/// @param t The time value to format, as a number of seconds since the Unix epoch (1970-01-01 00:00:00 UTC).
+/// @return A formatted string representing the date and time, in the format "Mon DD at HH:MM", where Mon is the three-letter month abbreviation, DD is the day of the month, HH is the hour (00-23), and MM is the minute (00-59).
+Common::String formatTime(int64 t);
 
-Common::String formatTime(int64 time, const char *format);
-DateTime toDateTime(int64 time);
+/// @brief Convert a time value (number of seconds since the Unix epoch) to a TimeDate struct representing the corresponding date and time components.
+/// @param t The time value to convert, as a number of seconds since the Unix epoch (1970-01-01 00:00:00 UTC).
+/// @return A TimeDate struct representing the date and time components.
+TimeDate intToTimeDate(int64 t);
+
+/// @brief Get the current time as a time value (number of seconds since the Unix epoch).
+/// @return The current time as a number of seconds since the Unix epoch (1970-01-01 00:00:00 UTC).
 int64 getTime();
 
 } // namespace Twp


Commit: 71f3bee330047c5dbad2ae2ed04a0f7622af4685
    https://github.com/scummvm/scummvm/commit/71f3bee330047c5dbad2ae2ed04a0f7622af4685
Author: scemino (scemino74 at gmail.com)
Date: 2026-06-14T20:41:52+02:00

Commit Message:
COMMON: Utility functions for converting a TimeDate into an integer and vice versa

Changed paths:
  R engines/twp/time.cpp
  R engines/twp/time.h
    common/util.cpp
    common/util.h
    engines/twp/metaengine.cpp
    engines/twp/module.mk
    engines/twp/savegame.cpp


diff --git a/common/util.cpp b/common/util.cpp
index 4540f3bad33..b3c39aceff9 100644
--- a/common/util.cpp
+++ b/common/util.cpp
@@ -23,8 +23,15 @@
 
 #include "common/util.h"
 #include "common/debug.h"
+#include "common/system.h"
 #include "common/translation.h"
 
+enum {
+	SECONDS_IN_DAY = 86400, // number of seconds in a day
+	SECONDS_IN_HOUR = 3600, // number of seconds in an hour
+	SECONDS_IN_MINUTE = 60  // number of seconds in a minute
+};
+
 namespace Common {
 
 //
@@ -210,4 +217,112 @@ Common::String getHumanReadableBytes(uint64 bytes, const char *&unitsOut) {
 	return Common::String::format("%.1f", floating);
 }
 
+namespace DateTime {
+
+Common::String formatTime(int64 t) {
+	const char* monthList[12] = {
+		"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
+	};
+	const TimeDate timeDate = int64ToTimeDate(t);
+	return Common::String::format("%s %d at %.2d:%.2d", monthList[timeDate.tm_mon], timeDate.tm_mday, timeDate.tm_hour, timeDate.tm_min);
+}
+
+static bool isLeapYear(int year) {
+	// https://learn.microsoft.com/en-us/troubleshoot/microsoft-365-apps/excel/determine-a-leap-year
+	return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
+}
+
+int64_t dateTimeToInt64(const TimeDate &timeDate) {
+	const int64_t year  = (int64_t)timeDate.tm_year + 1900; // fixup year
+	const int month = timeDate.tm_mon;
+	const int days_in_month[2][12] = {
+		{ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, // non-leap year
+		{ 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } // leap year, February has 29 days
+	};
+
+	// Count days from the Unix epoch (1970-01-01) to Jan 1 of `year`
+	/*
+	 * For a year Y, the number of leap years in [1, Y-1] is:
+	 *   floor((Y-1)/4) - floor((Y-1)/100) + floor((Y-1)/400)
+	 * Days from year 0 to Jan 1 of Y = 365*Y + leaps
+	 * Subtract the same value for 1970 to get the offset.
+	 */
+	const int64_t y0     = year  - 1;
+	const int64_t y1970  = 1969; // 1970 - 1
+
+	const int64_t leaps0    = y0    / 4 - y0    / 100 + y0    / 400;
+	const int64_t leaps1970 = y1970 / 4 - y1970 / 100 + y1970 / 400;
+
+	int64_t days = 365 * (year - 1970) + (leaps0 - leaps1970);
+
+	// add days for the months elapsed in the current year
+	const int leap = isLeapYear((int)year);
+	for (int m = 0; m < month; m++)
+		days += days_in_month[leap][m];
+
+	// add remaining date/time fields
+	days += (int64_t)timeDate.tm_mday - 1;
+
+	const int64_t seconds = days * SECONDS_IN_DAY
+		+ timeDate.tm_hour * SECONDS_IN_HOUR
+		+ timeDate.tm_min  * SECONDS_IN_MINUTE
+		+ timeDate.tm_sec;
+
+	return seconds;
+}
+
+TimeDate int64ToTimeDate(int64 t) {
+	TimeDate dateTime;
+	int64_t seconds = t;
+	dateTime.tm_sec = seconds % SECONDS_IN_MINUTE;
+	seconds /= SECONDS_IN_MINUTE;
+	dateTime.tm_min = seconds % 60;
+	seconds /= 60;
+	dateTime.tm_hour = seconds % 24;
+	seconds /= 24;
+
+	// Now `seconds` is the number of days since the Unix epoch (1970-01-01)
+	// We can find the year by subtracting the number of days in each year until we find the correct one.
+	int year = 1970;
+	while (true) {
+		const int days_in_year = isLeapYear(year) ? 366 : 365;
+		if (seconds >= days_in_year) {
+			seconds -= days_in_year;
+			year++;
+			continue;
+		}
+		break;
+	}
+	dateTime.tm_year = year - 1900; // fixup year
+
+	// Now `seconds` is the number of days elapsed in the current year. We can find the month and day similarly.
+	const int days_in_month[2][12] = {
+		{ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, // non-leap year
+		{ 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }  // leap-year, February has 29 days
+	};
+	const int leap = isLeapYear(year);
+	int month = 0;
+	while (month < 12) {
+		const int dim = days_in_month[leap][month];
+		if (seconds >= dim) {
+			seconds -= dim;
+			month++;
+			continue;
+		}
+		break;
+	}
+	dateTime.tm_mon = month; // tm_mon is 0-based
+	dateTime.tm_mday = (int)seconds + 1;
+
+	return dateTime;
+}
+
+int64 getTime() {
+	TimeDate dateTimeTm;
+	g_system->getTimeAndDate(dateTimeTm);
+	return dateTimeToInt64(dateTimeTm);
+}
+
+} // End of namespace DateTime
+
 } // End of namespace Common
diff --git a/common/util.h b/common/util.h
index e43460c4c10..517b65e3f03 100644
--- a/common/util.h
+++ b/common/util.h
@@ -26,6 +26,8 @@
 
 #include "common/type_traits.h"
 
+struct TimeDate;
+
 /**
  * @defgroup common_util Util
  * @ingroup common
@@ -411,6 +413,32 @@ bool isBlank(int c);
  */
 Common::String getHumanReadableBytes(uint64 bytes, const char *&unitsOut);
 
+/**
+ * Utility functions for converting a TimeDate structure into an integer representation of the time, and vice versa.
+ * The integer representation is the number of seconds since the Unix epoch (1970-01-01 00:00:00 UTC).
+ */
+namespace DateTime {
+	/**
+	 * Convert a time value (number of seconds since the Unix epoch) to a TimeDate struct representing the corresponding date and time components.
+	 */
+	TimeDate int64ToTimeDate(int64 integer);
+
+	/**
+	 * Convert a TimeDate struct representing date and time components to a time value (number of seconds since the Unix epoch).
+	 */
+	int64_t dateTimeToInt64(const TimeDate &timeDate);
+
+	/**
+	 * Convert a time value (number of seconds since the Unix epoch) to a human-readable string format.
+	 */
+	Common::String formatTime(int64 integer);
+
+	/**
+	 * Get the current time as a time value (number of seconds since the Unix epoch).
+	 */
+	int64 getTime();
+}
+
 /** @} */
 
 } // End of namespace Common
diff --git a/engines/twp/metaengine.cpp b/engines/twp/metaengine.cpp
index 3c09db4ef93..bc422cc8b03 100644
--- a/engines/twp/metaengine.cpp
+++ b/engines/twp/metaengine.cpp
@@ -37,7 +37,6 @@
 #include "twp/metaengine.h"
 #include "twp/detection.h"
 #include "twp/savegame.h"
-#include "twp/time.h"
 #include "twp/actions.h"
 #include "twp/debugtools.h"
 #include "twp/dialogs.h"
@@ -83,7 +82,7 @@ const Common::AchievementDescriptionList *TwpMetaEngine::getAchievementDescripti
 }
 
 static Common::String getDesc(const Twp::SaveGame &savegame) {
-	Common::String desc = Twp::formatTime(savegame.time);
+	Common::String desc = Common::DateTime::formatTime(savegame.time);
 	if (savegame.easyMode)
 		desc += " (casual)";
 	return desc;
@@ -115,7 +114,7 @@ SaveStateDescriptor TwpMetaEngine::querySaveMetaInfos(const char *target, int sl
 		Twp::SaveGame savegame;
 		Twp::SaveGameManager::getSaveGame(f, savegame);
 		Common::String savegameDesc(getDesc(savegame));
-		TimeDate dt = Twp::intToTimeDate(savegame.time);
+		TimeDate dt = Common::DateTime::int64ToTimeDate(savegame.time);
 		desc.setDescription(savegameDesc);
 		desc.setPlayTime(savegame.gameTime * 1000);
 		desc.setSaveDate(dt.tm_year, dt.tm_mon, dt.tm_mday);
diff --git a/engines/twp/module.mk b/engines/twp/module.mk
index fe7af221890..9375566c696 100644
--- a/engines/twp/module.mk
+++ b/engines/twp/module.mk
@@ -36,7 +36,6 @@ MODULE_OBJS = \
 	squtil.o \
 	syslib.o \
 	thread.o \
-	time.o \
 	tsv.o \
 	twp.o \
 	util.o \
diff --git a/engines/twp/savegame.cpp b/engines/twp/savegame.cpp
index 7c290a9a3ff..7d4a8157a67 100644
--- a/engines/twp/savegame.cpp
+++ b/engines/twp/savegame.cpp
@@ -32,7 +32,6 @@
 #include "twp/room.h"
 #include "twp/savegame.h"
 #include "twp/squtil.h"
-#include "twp/time.h"
 
 namespace Twp {
 
@@ -1090,7 +1089,7 @@ static Common::JSONValue *createSaveGame() {
 	json["objects"] = createJObjects();
 	json["rooms"] = createJRooms();
 	json["savebuild"] = new Common::JSONValue(958LL);
-	json["savetime"] = new Common::JSONValue((long long)getTime());
+	json["savetime"] = new Common::JSONValue((long long)Common::DateTime::getTime());
 	json["selectedActor"] = new Common::JSONValue(g_twp->_actor ? g_twp->_actor->_key : "");
 	json["version"] = new Common::JSONValue((long long int)2);
 	return new Common::JSONValue(json);
diff --git a/engines/twp/time.cpp b/engines/twp/time.cpp
deleted file mode 100644
index 6dd2de9efed..00000000000
--- a/engines/twp/time.cpp
+++ /dev/null
@@ -1,136 +0,0 @@
-/* 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/system.h"
-#include "twp/time.h"
-
-namespace Twp {
-
-enum {
-	SECONDS_IN_DAY = 86400, // number of seconds in a day
-	SECONDS_IN_HOUR = 3600,// number of seconds in an hour
-	SECONDS_IN_MINUTE = 60
-};
-
-Common::String formatTime(int64 t) {
-	const char* monthList[12] = {
-		"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
-	};
-	const TimeDate timeDate = intToTimeDate(t);
-	return Common::String::format( "%s %d at %.2d:%.2d", monthList[timeDate.tm_mon], timeDate.tm_mday, timeDate.tm_hour, timeDate.tm_min);
-}
-
-static bool isLeapYear(int year) {
-	return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
-}
-
-static int64_t dateTimeToInt64(const TimeDate &timeDate) {
-	const int64_t year  = (int64_t)timeDate.tm_year + 1900; // fixup year
-	const int month = timeDate.tm_mon;
-	const int days_in_month[2][12] = {
-		{ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, // non-leap year
-		{ 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } // leap year, February has 29 days
-	};
-
-	// Count days from the Unix epoch (1970-01-01) to Jan 1 of `year`
-	/*
-	 * For a year Y, the number of leap years in [1, Y-1] is:
-	 *   floor((Y-1)/4) - floor((Y-1)/100) + floor((Y-1)/400)
-	 * Days from year 0 to Jan 1 of Y = 365*Y + leaps
-	 * Subtract the same value for 1970 to get the offset.
-	 */
-	const int64_t y0     = year  - 1;
-	const int64_t y1970  = 1969; // 1970 - 1
-
-	const int64_t leaps0    = y0    / 4 - y0    / 100 + y0    / 400;
-	const int64_t leaps1970 = y1970 / 4 - y1970 / 100 + y1970 / 400;
-
-	int64_t days = 365 * (year - 1970) + (leaps0 - leaps1970);
-
-	// add days for the months elapsed in the current year
-	const int leap = isLeapYear((int)year);
-	for (int m = 0; m < month; m++)
-		days += days_in_month[leap][m];
-
-	// add remaining date/time fields
-	days += (int64_t)timeDate.tm_mday - 1;
-
-	const int64_t seconds = days * SECONDS_IN_DAY
-		+ timeDate.tm_hour * SECONDS_IN_HOUR
-		+ timeDate.tm_min  * SECONDS_IN_MINUTE
-		+ timeDate.tm_sec;
-
-	return seconds;
-}
-
-TimeDate intToTimeDate(int64 t) {
-	TimeDate dateTime;
-	int64_t seconds = t;
-	dateTime.tm_sec = seconds % SECONDS_IN_MINUTE;
-	seconds /= SECONDS_IN_MINUTE;
-	dateTime.tm_min = seconds % 60;
-	seconds /= 60;
-	dateTime.tm_hour = seconds % 24;
-	seconds /= 24;
-
-	// Now `seconds` is the number of days since the Unix epoch (1970-01-01)
-	// We can find the year by subtracting the number of days in each year until we find the correct one.
-	int year = 1970;
-	while (true) {
-		const int days_in_year = isLeapYear(year) ? 366 : 365;
-		if (seconds >= days_in_year) {
-			seconds -= days_in_year;
-			year++;
-			continue;
-		}
-		break;
-	}
-	dateTime.tm_year = year - 1900; // fixup year
-
-	// Now `seconds` is the number of days elapsed in the current year. We can find the month and day similarly.
-	const int days_in_month[2][12] = {
-		{ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, // non-leap year
-		{ 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }  // leap-year, February has 29 days
-	};
-	const int leap = isLeapYear(year);
-	int month = 0;
-	while (month < 12) {
-		const int dim = days_in_month[leap][month];
-		if (seconds >= dim) {
-			seconds -= dim;
-			month++;
-		} else {
-			break;
-		}
-	}
-	dateTime.tm_mon = month; // tm_mon is 0-based
-	dateTime.tm_mday = (int)seconds + 1;
-
-	return dateTime;
-}
-
-int64 getTime() {
-	TimeDate dateTimeTm;
-	g_system->getTimeAndDate(dateTimeTm);
-	return dateTimeToInt64(dateTimeTm);
-}
-
-} // namespace Twp
diff --git a/engines/twp/time.h b/engines/twp/time.h
deleted file mode 100644
index 822bcb237ab..00000000000
--- a/engines/twp/time.h
+++ /dev/null
@@ -1,45 +0,0 @@
-/* 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 TWP_TIME_H
-#define TWP_TIME_H
-
-#include "common/str.h"
-
-namespace Twp {
-
-/// @brief Convert a time value (number of seconds since the Unix epoch) to a human-readable string format.
-/// @param t The time value to format, as a number of seconds since the Unix epoch (1970-01-01 00:00:00 UTC).
-/// @return A formatted string representing the date and time, in the format "Mon DD at HH:MM", where Mon is the three-letter month abbreviation, DD is the day of the month, HH is the hour (00-23), and MM is the minute (00-59).
-Common::String formatTime(int64 t);
-
-/// @brief Convert a time value (number of seconds since the Unix epoch) to a TimeDate struct representing the corresponding date and time components.
-/// @param t The time value to convert, as a number of seconds since the Unix epoch (1970-01-01 00:00:00 UTC).
-/// @return A TimeDate struct representing the date and time components.
-TimeDate intToTimeDate(int64 t);
-
-/// @brief Get the current time as a time value (number of seconds since the Unix epoch).
-/// @return The current time as a number of seconds since the Unix epoch (1970-01-01 00:00:00 UTC).
-int64 getTime();
-
-} // namespace Twp
-
-#endif



From noreply at scummvm.org  Sun Jun 14 18:42:01 2026
From: noreply at scummvm.org (ScummVM-Translations)
Date: Sun, 14 Jun 2026 18:42:01 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 45cdeaa66f83659f3903dca6621212a6e538bcd1
Message-ID: <20260614184201.E5E7E7005CF@ron.scummvm.net>

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

Summary:
45cdeaa66f I18N: Update translations templates


Commit: 45cdeaa66f83659f3903dca6621212a6e538bcd1
    https://github.com/scummvm/scummvm/commit/45cdeaa66f83659f3903dca6621212a6e538bcd1
Author: Weblate (noreply at weblate.org)
Date: 2026-06-14T18:41:57Z

Commit Message:
I18N: Update translations templates

Changed paths:
    po/scummvm.pot


diff --git a/po/scummvm.pot b/po/scummvm.pot
index e0050144bc5..db3a10793f5 100644
--- a/po/scummvm.pot
+++ b/po/scummvm.pot
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2026.2.1git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-13 13:52+0000\n"
+"POT-Creation-Date: 2026-06-14 18:41+0000\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL at ADDRESS>\n"
 "Language-Team: LANGUAGE <LL at li.org>\n"
@@ -69,7 +69,7 @@ msgstr ""
 #: engines/scumm/help.cpp:141 engines/scumm/help.cpp:166
 #: engines/scumm/help.cpp:194 engines/scumm/help.cpp:212
 #: engines/sherlock/metaengine.cpp:430 engines/supernova/metaengine.cpp:341
-#: engines/tot/metaengine.cpp:149 engines/twp/metaengine.cpp:179
+#: engines/tot/metaengine.cpp:149 engines/twp/metaengine.cpp:178
 msgid "Close"
 msgstr ""
 
@@ -1016,7 +1016,7 @@ msgstr ""
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
 #: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1124
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
-#: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
+#: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:153
 #: engines/ultima/ultima0/metaengine.cpp:45
 #: engines/ultima/ultima4/metaengine.cpp:44
 #: engines/ultima/ultima4/metaengine.cpp:139
@@ -1037,7 +1037,7 @@ msgstr ""
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
 #: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1091
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
-#: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
+#: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:45
 #: engines/ultima/ultima4/metaengine.cpp:140
@@ -1058,7 +1058,7 @@ msgstr ""
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
 #: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1104
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
-#: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
+#: engines/twp/metaengine.cpp:151 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
 #: engines/ultima/ultima4/metaengine.cpp:151
 #: engines/wintermute/keymapper_tables.h:320
@@ -1078,7 +1078,7 @@ msgstr ""
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
 #: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1111
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
-#: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
+#: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
 #: engines/ultima/ultima4/metaengine.cpp:152
 #: engines/wintermute/keymapper_tables.h:326
@@ -3099,7 +3099,7 @@ msgstr ""
 
 #. I18N: Abbreviation for 'bytes' as data size
 #. I18N: Hardware key
-#: common/util.cpp:183 backends/platform/3ds/osystem-events.cpp:46
+#: common/util.cpp:190 backends/platform/3ds/osystem-events.cpp:46
 #: backends/platform/ds/osystem_ds.cpp:178
 #: backends/platform/sdl/miyoo/miyoo.cpp:61
 #: backends/platform/sdl/miyoo/miyoo.cpp:88
@@ -3109,22 +3109,22 @@ msgid "B"
 msgstr ""
 
 #. I18N: Abbreviation for 'kilobytes' as data size
-#: common/util.cpp:189
+#: common/util.cpp:196
 msgid "KB"
 msgstr ""
 
 #. I18N: Abbreviation for 'megabytes' as data size
-#: common/util.cpp:194
+#: common/util.cpp:201
 msgid "MB"
 msgstr ""
 
 #. I18N: Abbreviation for 'gigabytes' as data size
-#: common/util.cpp:200
+#: common/util.cpp:207
 msgid "GB"
 msgstr ""
 
 #. I18N: Abbreviation for 'terabytes' as data size
-#: common/util.cpp:206
+#: common/util.cpp:213
 msgid "TB"
 msgstr ""
 
@@ -5755,7 +5755,7 @@ msgstr ""
 #: engines/scumm/help.cpp:165 engines/scumm/help.cpp:193
 #: engines/scumm/help.cpp:211 engines/sherlock/metaengine.cpp:425
 #: engines/supernova/metaengine.cpp:336 engines/tot/metaengine.cpp:144
-#: engines/twp/metaengine.cpp:178
+#: engines/twp/metaengine.cpp:177
 msgid "Open"
 msgstr ""
 
@@ -5777,7 +5777,7 @@ msgstr ""
 #: engines/sherlock/metaengine.cpp:440 engines/sherlock/metaengine.cpp:561
 #: engines/sky/metaengine.cpp:95 engines/sludge/keymapper_tables.h:333
 #: engines/supernova/metaengine.cpp:356 engines/tot/metaengine.cpp:139
-#: engines/tsage/metaengine.cpp:211 engines/twp/metaengine.cpp:186
+#: engines/tsage/metaengine.cpp:211 engines/twp/metaengine.cpp:185
 #: engines/ultima/ultima4/metaengine.cpp:74
 #: engines/ultima/nuvie/metaengine.cpp:69
 #: engines/wintermute/keymapper_tables.h:140
@@ -6477,7 +6477,7 @@ msgstr ""
 
 #: engines/alcachofa/metaengine.cpp:104 engines/queen/metaengine.cpp:279
 #: engines/saga/metaengine.cpp:354 engines/scumm/help.cpp:202
-#: engines/scumm/help.cpp:214 engines/twp/metaengine.cpp:182
+#: engines/scumm/help.cpp:214 engines/twp/metaengine.cpp:181
 #: engines/wintermute/keymapper_tables.h:122
 #: engines/wintermute/keymapper_tables.h:1534
 msgid "Look at"
@@ -6806,7 +6806,7 @@ msgstr ""
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:210
 #: engines/phoenixvr/metaengine.cpp:94 engines/scumm/help.cpp:79
 #: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
-#: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
+#: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:161
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
 msgstr ""
@@ -7602,7 +7602,7 @@ msgstr ""
 #. I18N: Perform action on object where cursor points
 #: engines/darkseed/metaengine.cpp:42 engines/dragons/metaengine.cpp:135
 #: engines/phoenixvr/metaengine.cpp:75 engines/tinsel/metaengine.cpp:294
-#: engines/twp/metaengine.cpp:150
+#: engines/twp/metaengine.cpp:149
 msgid "Action"
 msgstr ""
 
@@ -7997,7 +7997,7 @@ msgstr ""
 #: engines/scumm/help.cpp:139 engines/scumm/help.cpp:164
 #: engines/scumm/help.cpp:200 engines/scumm/help.cpp:210
 #: engines/sherlock/metaengine.cpp:445 engines/sherlock/metaengine.cpp:566
-#: engines/supernova/metaengine.cpp:366 engines/twp/metaengine.cpp:180
+#: engines/supernova/metaengine.cpp:366 engines/twp/metaengine.cpp:179
 msgid "Give"
 msgstr ""
 
@@ -11079,7 +11079,7 @@ msgstr ""
 msgid "Move / Skip / Use default action with inventory item"
 msgstr ""
 
-#: engines/queen/metaengine.cpp:198 engines/twp/metaengine.cpp:176
+#: engines/queen/metaengine.cpp:198 engines/twp/metaengine.cpp:175
 msgid "Skip text"
 msgstr ""
 
@@ -11119,7 +11119,7 @@ msgstr ""
 #: engines/scumm/help.cpp:144 engines/scumm/help.cpp:169
 #: engines/scumm/help.cpp:198 engines/scumm/help.cpp:213
 #: engines/scumm/help.cpp:230 engines/sherlock/metaengine.cpp:420
-#: engines/sludge/keymapper_tables.h:318 engines/twp/metaengine.cpp:181
+#: engines/sludge/keymapper_tables.h:318 engines/twp/metaengine.cpp:180
 #: engines/wintermute/keymapper_tables.h:134
 #: engines/wintermute/keymapper_tables.h:1546
 msgid "Pick up"
@@ -11128,7 +11128,7 @@ msgstr ""
 #: engines/queen/metaengine.cpp:289 engines/saga/metaengine.cpp:364
 #: engines/scumm/help.cpp:199 engines/scumm/help.cpp:215
 #: engines/scumm/help.cpp:251 engines/sludge/keymapper_tables.h:323
-#: engines/twp/metaengine.cpp:183 engines/wintermute/keymapper_tables.h:128
+#: engines/twp/metaengine.cpp:182 engines/wintermute/keymapper_tables.h:128
 #: engines/wintermute/keymapper_tables.h:1558
 msgid "Talk to"
 msgstr ""
@@ -11223,7 +11223,7 @@ msgstr ""
 
 #: engines/saga/metaengine.cpp:389 engines/scumm/help.cpp:122
 #: engines/scumm/help.cpp:137 engines/scumm/help.cpp:162
-#: engines/supernova/metaengine.cpp:346 engines/twp/metaengine.cpp:184
+#: engines/supernova/metaengine.cpp:346 engines/twp/metaengine.cpp:183
 msgid "Push"
 msgstr ""
 
@@ -11973,7 +11973,7 @@ msgstr ""
 
 #: engines/scumm/help.cpp:123 engines/scumm/help.cpp:138
 #: engines/scumm/help.cpp:163 engines/supernova/metaengine.cpp:351
-#: engines/twp/metaengine.cpp:185
+#: engines/twp/metaengine.cpp:184
 msgid "Pull"
 msgstr ""
 
@@ -14083,87 +14083,87 @@ msgstr ""
 msgid "Ransome *unbeeped* (DLC)"
 msgstr ""
 
-#: engines/twp/metaengine.cpp:151
+#: engines/twp/metaengine.cpp:150
 msgid "Select highlighted verb"
 msgstr ""
 
-#: engines/twp/metaengine.cpp:156
+#: engines/twp/metaengine.cpp:155
 msgid "Go to previous object"
 msgstr ""
 
-#: engines/twp/metaengine.cpp:157
+#: engines/twp/metaengine.cpp:156
 msgid "Go to next object"
 msgstr ""
 
-#: engines/twp/metaengine.cpp:158
+#: engines/twp/metaengine.cpp:157
 msgid "Select verb / item left"
 msgstr ""
 
-#: engines/twp/metaengine.cpp:159
+#: engines/twp/metaengine.cpp:158
 msgid "Select verb / item right"
 msgstr ""
 
-#: engines/twp/metaengine.cpp:160
+#: engines/twp/metaengine.cpp:159
 msgid "Select verb / item up"
 msgstr ""
 
-#: engines/twp/metaengine.cpp:161
+#: engines/twp/metaengine.cpp:160
 msgid "Select verb / item down"
 msgstr ""
 
-#: engines/twp/metaengine.cpp:163
+#: engines/twp/metaengine.cpp:162
 msgid "Select actor 1"
 msgstr ""
 
-#: engines/twp/metaengine.cpp:164
+#: engines/twp/metaengine.cpp:163
 msgid "Select actor 2"
 msgstr ""
 
-#: engines/twp/metaengine.cpp:165
+#: engines/twp/metaengine.cpp:164
 msgid "Select actor 3"
 msgstr ""
 
-#: engines/twp/metaengine.cpp:166
+#: engines/twp/metaengine.cpp:165
 msgid "Select actor 4"
 msgstr ""
 
-#: engines/twp/metaengine.cpp:167
+#: engines/twp/metaengine.cpp:166
 msgid "Select actor 5"
 msgstr ""
 
-#: engines/twp/metaengine.cpp:168
+#: engines/twp/metaengine.cpp:167
 msgid "Select choice 1"
 msgstr ""
 
-#: engines/twp/metaengine.cpp:169
+#: engines/twp/metaengine.cpp:168
 msgid "Select choice 2"
 msgstr ""
 
-#: engines/twp/metaengine.cpp:170
+#: engines/twp/metaengine.cpp:169
 msgid "Select choice 3"
 msgstr ""
 
-#: engines/twp/metaengine.cpp:171
+#: engines/twp/metaengine.cpp:170
 msgid "Select choice 4"
 msgstr ""
 
-#: engines/twp/metaengine.cpp:172
+#: engines/twp/metaengine.cpp:171
 msgid "Select choice 5"
 msgstr ""
 
-#: engines/twp/metaengine.cpp:173
+#: engines/twp/metaengine.cpp:172
 msgid "Select choice 6"
 msgstr ""
 
-#: engines/twp/metaengine.cpp:174
+#: engines/twp/metaengine.cpp:173
 msgid "Select next Actor"
 msgstr ""
 
-#: engines/twp/metaengine.cpp:175
+#: engines/twp/metaengine.cpp:174
 msgid "Select previous actor"
 msgstr ""
 
-#: engines/twp/metaengine.cpp:177
+#: engines/twp/metaengine.cpp:176
 msgid "Show hotspots"
 msgstr ""
 



From noreply at scummvm.org  Sun Jun 14 18:42:33 2026
From: noreply at scummvm.org (ScummVM-Translations)
Date: Sun, 14 Jun 2026 18:42:33 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 7e890167e9dbac380684744acd59b220d22030dc
Message-ID: <20260614184233.AEB6F7005CC@ron.scummvm.net>

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

Summary:
7e890167e9 I18N: Update translation files


Commit: 7e890167e9dbac380684744acd59b220d22030dc
    https://github.com/scummvm/scummvm/commit/7e890167e9dbac380684744acd59b220d22030dc
Author: Weblate (noreply at weblate.org)
Date: 2026-06-14T18:42:14Z

Commit Message:
I18N: Update translation files

Updated by "Update PO files to match POT (msgmerge)" hook in Weblate.

Translation: ScummVM/scummvm
Translate-URL: https://translations.scummvm.org/projects/scummvm/scummvm/

Changed paths:
    po/ar.po
    po/be-tarask.po
    po/be_BY.po
    po/ca_ES.po
    po/cs_CZ.po
    po/da.po
    po/de_DE.po
    po/el.po
    po/es_ES.po
    po/eu.po
    po/fi_FI.po
    po/fr_FR.po
    po/gl_ES.po
    po/he.po
    po/hi.po
    po/hu_HU.po
    po/it_IT.po
    po/ja.po
    po/ka.po
    po/ko.po
    po/nb_NO.po
    po/nl_NL.po
    po/nn_NO.po
    po/pl_PL.po
    po/pt_BR.po
    po/pt_PT.po
    po/ro.po
    po/ru_RU.po
    po/sv_SE.po
    po/tr.po
    po/uk_UA.po
    po/zh.po
    po/zh_Hans.po
    po/zh_Hant.po


diff --git a/po/ar.po b/po/ar.po
index 32d9f9b1ed7..9d26847556e 100644
--- a/po/ar.po
+++ b/po/ar.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-13 13:52+0000\n"
+"POT-Creation-Date: 2026-06-14 18:41+0000\n"
 "PO-Revision-Date: 2026-02-24 22:26+0000\n"
 "Last-Translator: Mohamed Shaaban <mohmedbashamedo at hotmail.com>\n"
 "Language-Team: Arabic <https://translations.scummvm.org/projects/scummvm/"
@@ -80,7 +80,7 @@ msgstr "???????????????? ????????????????:"
 #: engines/scumm/help.cpp:141 engines/scumm/help.cpp:166
 #: engines/scumm/help.cpp:194 engines/scumm/help.cpp:212
 #: engines/sherlock/metaengine.cpp:430 engines/supernova/metaengine.cpp:341
-#: engines/tot/metaengine.cpp:149 engines/twp/metaengine.cpp:179
+#: engines/tot/metaengine.cpp:149 engines/twp/metaengine.cpp:178
 msgid "Close"
 msgstr "??????????"
 
@@ -1062,7 +1062,7 @@ msgstr "??????????????"
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
 #: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1124
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
-#: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
+#: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:153
 #: engines/ultima/ultima0/metaengine.cpp:45
 #: engines/ultima/ultima4/metaengine.cpp:44
 #: engines/ultima/ultima4/metaengine.cpp:139
@@ -1083,7 +1083,7 @@ msgstr "????????"
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
 #: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1091
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
-#: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
+#: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:45
 #: engines/ultima/ultima4/metaengine.cpp:140
@@ -1104,7 +1104,7 @@ msgstr "????????"
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
 #: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1104
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
-#: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
+#: engines/twp/metaengine.cpp:151 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
 #: engines/ultima/ultima4/metaengine.cpp:151
 #: engines/wintermute/keymapper_tables.h:320
@@ -1124,7 +1124,7 @@ msgstr "????????"
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
 #: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1111
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
-#: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
+#: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
 #: engines/ultima/ultima4/metaengine.cpp:152
 #: engines/wintermute/keymapper_tables.h:326
@@ -3330,7 +3330,7 @@ msgstr "<???????? ????????>"
 
 #. I18N: Abbreviation for 'bytes' as data size
 #. I18N: Hardware key
-#: common/util.cpp:183 backends/platform/3ds/osystem-events.cpp:46
+#: common/util.cpp:190 backends/platform/3ds/osystem-events.cpp:46
 #: backends/platform/ds/osystem_ds.cpp:178
 #: backends/platform/sdl/miyoo/miyoo.cpp:61
 #: backends/platform/sdl/miyoo/miyoo.cpp:88
@@ -3340,22 +3340,22 @@ msgid "B"
 msgstr "B"
 
 #. I18N: Abbreviation for 'kilobytes' as data size
-#: common/util.cpp:189
+#: common/util.cpp:196
 msgid "KB"
 msgstr "KB"
 
 #. I18N: Abbreviation for 'megabytes' as data size
-#: common/util.cpp:194
+#: common/util.cpp:201
 msgid "MB"
 msgstr "MB"
 
 #. I18N: Abbreviation for 'gigabytes' as data size
-#: common/util.cpp:200
+#: common/util.cpp:207
 msgid "GB"
 msgstr "GB"
 
 #. I18N: Abbreviation for 'terabytes' as data size
-#: common/util.cpp:206
+#: common/util.cpp:213
 msgid "TB"
 msgstr "TB"
 
@@ -6273,7 +6273,7 @@ msgstr "????????"
 #: engines/scumm/help.cpp:165 engines/scumm/help.cpp:193
 #: engines/scumm/help.cpp:211 engines/sherlock/metaengine.cpp:425
 #: engines/supernova/metaengine.cpp:336 engines/tot/metaengine.cpp:144
-#: engines/twp/metaengine.cpp:178
+#: engines/twp/metaengine.cpp:177
 msgid "Open"
 msgstr "????????"
 
@@ -6295,7 +6295,7 @@ msgstr "????????"
 #: engines/sherlock/metaengine.cpp:440 engines/sherlock/metaengine.cpp:561
 #: engines/sky/metaengine.cpp:95 engines/sludge/keymapper_tables.h:333
 #: engines/supernova/metaengine.cpp:356 engines/tot/metaengine.cpp:139
-#: engines/tsage/metaengine.cpp:211 engines/twp/metaengine.cpp:186
+#: engines/tsage/metaengine.cpp:211 engines/twp/metaengine.cpp:185
 #: engines/ultima/ultima4/metaengine.cpp:74
 #: engines/ultima/nuvie/metaengine.cpp:69
 #: engines/wintermute/keymapper_tables.h:140
@@ -7104,7 +7104,7 @@ msgstr "????????????"
 
 #: engines/alcachofa/metaengine.cpp:104 engines/queen/metaengine.cpp:279
 #: engines/saga/metaengine.cpp:354 engines/scumm/help.cpp:202
-#: engines/scumm/help.cpp:214 engines/twp/metaengine.cpp:182
+#: engines/scumm/help.cpp:214 engines/twp/metaengine.cpp:181
 #: engines/wintermute/keymapper_tables.h:122
 #: engines/wintermute/keymapper_tables.h:1534
 msgid "Look at"
@@ -7473,7 +7473,7 @@ msgstr "?????????? ????????????"
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:210
 #: engines/phoenixvr/metaengine.cpp:94 engines/scumm/help.cpp:79
 #: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
-#: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
+#: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:161
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
 msgstr "???????? ????????????"
@@ -8406,7 +8406,7 @@ msgstr ""
 #. I18N: Perform action on object where cursor points
 #: engines/darkseed/metaengine.cpp:42 engines/dragons/metaengine.cpp:135
 #: engines/phoenixvr/metaengine.cpp:75 engines/tinsel/metaengine.cpp:294
-#: engines/twp/metaengine.cpp:150
+#: engines/twp/metaengine.cpp:149
 msgid "Action"
 msgstr "??????"
 
@@ -8953,7 +8953,7 @@ msgstr ""
 #: engines/scumm/help.cpp:139 engines/scumm/help.cpp:164
 #: engines/scumm/help.cpp:200 engines/scumm/help.cpp:210
 #: engines/sherlock/metaengine.cpp:445 engines/sherlock/metaengine.cpp:566
-#: engines/supernova/metaengine.cpp:366 engines/twp/metaengine.cpp:180
+#: engines/supernova/metaengine.cpp:366 engines/twp/metaengine.cpp:179
 msgid "Give"
 msgstr "????????"
 
@@ -12690,7 +12690,7 @@ msgstr "?????????????? ???????? ???????????? ???????? ????????????"
 msgid "Move / Skip / Use default action with inventory item"
 msgstr ""
 
-#: engines/queen/metaengine.cpp:198 engines/twp/metaengine.cpp:176
+#: engines/queen/metaengine.cpp:198 engines/twp/metaengine.cpp:175
 #, fuzzy
 #| msgid "Skip line"
 msgid "Skip text"
@@ -12748,7 +12748,7 @@ msgstr "?????? ????????????"
 #: engines/scumm/help.cpp:144 engines/scumm/help.cpp:169
 #: engines/scumm/help.cpp:198 engines/scumm/help.cpp:213
 #: engines/scumm/help.cpp:230 engines/sherlock/metaengine.cpp:420
-#: engines/sludge/keymapper_tables.h:318 engines/twp/metaengine.cpp:181
+#: engines/sludge/keymapper_tables.h:318 engines/twp/metaengine.cpp:180
 #: engines/wintermute/keymapper_tables.h:134
 #: engines/wintermute/keymapper_tables.h:1546
 msgid "Pick up"
@@ -12757,7 +12757,7 @@ msgstr "??????????"
 #: engines/queen/metaengine.cpp:289 engines/saga/metaengine.cpp:364
 #: engines/scumm/help.cpp:199 engines/scumm/help.cpp:215
 #: engines/scumm/help.cpp:251 engines/sludge/keymapper_tables.h:323
-#: engines/twp/metaengine.cpp:183 engines/wintermute/keymapper_tables.h:128
+#: engines/twp/metaengine.cpp:182 engines/wintermute/keymapper_tables.h:128
 #: engines/wintermute/keymapper_tables.h:1558
 msgid "Talk to"
 msgstr "???????? ??????"
@@ -12872,7 +12872,7 @@ msgstr "???????? ??????"
 
 #: engines/saga/metaengine.cpp:389 engines/scumm/help.cpp:122
 #: engines/scumm/help.cpp:137 engines/scumm/help.cpp:162
-#: engines/supernova/metaengine.cpp:346 engines/twp/metaengine.cpp:184
+#: engines/supernova/metaengine.cpp:346 engines/twp/metaengine.cpp:183
 msgid "Push"
 msgstr "Push"
 
@@ -13738,7 +13738,7 @@ msgstr "?????????? ???????????? ????????????????:"
 
 #: engines/scumm/help.cpp:123 engines/scumm/help.cpp:138
 #: engines/scumm/help.cpp:163 engines/supernova/metaengine.cpp:351
-#: engines/twp/metaengine.cpp:185
+#: engines/twp/metaengine.cpp:184
 msgid "Pull"
 msgstr "????????"
 
@@ -16282,127 +16282,127 @@ msgstr ""
 msgid "Ransome *unbeeped* (DLC)"
 msgstr ""
 
-#: engines/twp/metaengine.cpp:151
+#: engines/twp/metaengine.cpp:150
 #, fuzzy
 #| msgid "Select shader"
 msgid "Select highlighted verb"
 msgstr "?????? ??????????"
 
-#: engines/twp/metaengine.cpp:156
+#: engines/twp/metaengine.cpp:155
 #, fuzzy
 #| msgid "Go to previous directory level"
 msgid "Go to previous object"
 msgstr "?????????? ?????? ?????????? ???????????? ????????????"
 
-#: engines/twp/metaengine.cpp:157
+#: engines/twp/metaengine.cpp:156
 #, fuzzy
 #| msgid "Go to setup menu"
 msgid "Go to next object"
 msgstr "???????? ?????? ?????????? ??????????????"
 
-#: engines/twp/metaengine.cpp:158
+#: engines/twp/metaengine.cpp:157
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Select verb / item left"
 msgstr "???????????? ????????????"
 
-#: engines/twp/metaengine.cpp:159
+#: engines/twp/metaengine.cpp:158
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Select verb / item right"
 msgstr "???????????? ????????????"
 
-#: engines/twp/metaengine.cpp:160
+#: engines/twp/metaengine.cpp:159
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Select verb / item up"
 msgstr "???????????? ????????????"
 
-#: engines/twp/metaengine.cpp:161
+#: engines/twp/metaengine.cpp:160
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Select verb / item down"
 msgstr "???????????? ????????????"
 
-#: engines/twp/metaengine.cpp:163
+#: engines/twp/metaengine.cpp:162
 #, fuzzy
 #| msgid "Select Group by"
 msgid "Select actor 1"
 msgstr "?????? ?????????????? ??????"
 
-#: engines/twp/metaengine.cpp:164
+#: engines/twp/metaengine.cpp:163
 #, fuzzy
 #| msgid "Select Group by"
 msgid "Select actor 2"
 msgstr "?????? ?????????????? ??????"
 
-#: engines/twp/metaengine.cpp:165
+#: engines/twp/metaengine.cpp:164
 #, fuzzy
 #| msgid "Select Group by"
 msgid "Select actor 3"
 msgstr "?????? ?????????????? ??????"
 
-#: engines/twp/metaengine.cpp:166
+#: engines/twp/metaengine.cpp:165
 #, fuzzy
 #| msgid "Select Group by"
 msgid "Select actor 4"
 msgstr "?????? ?????????????? ??????"
 
-#: engines/twp/metaengine.cpp:167
+#: engines/twp/metaengine.cpp:166
 #, fuzzy
 #| msgid "Select Group by"
 msgid "Select actor 5"
 msgstr "?????? ?????????????? ??????"
 
-#: engines/twp/metaengine.cpp:168
+#: engines/twp/metaengine.cpp:167
 #, fuzzy
 #| msgid "Select shader"
 msgid "Select choice 1"
 msgstr "?????? ??????????"
 
-#: engines/twp/metaengine.cpp:169
+#: engines/twp/metaengine.cpp:168
 #, fuzzy
 #| msgid "Select shader"
 msgid "Select choice 2"
 msgstr "?????? ??????????"
 
-#: engines/twp/metaengine.cpp:170
+#: engines/twp/metaengine.cpp:169
 #, fuzzy
 #| msgid "Select shader"
 msgid "Select choice 3"
 msgstr "?????? ??????????"
 
-#: engines/twp/metaengine.cpp:171
+#: engines/twp/metaengine.cpp:170
 #, fuzzy
 #| msgid "Select shader"
 msgid "Select choice 4"
 msgstr "?????? ??????????"
 
-#: engines/twp/metaengine.cpp:172
+#: engines/twp/metaengine.cpp:171
 #, fuzzy
 #| msgid "Select shader"
 msgid "Select choice 5"
 msgstr "?????? ??????????"
 
-#: engines/twp/metaengine.cpp:173
+#: engines/twp/metaengine.cpp:172
 #, fuzzy
 #| msgid "Select shader"
 msgid "Select choice 6"
 msgstr "?????? ??????????"
 
-#: engines/twp/metaengine.cpp:174
+#: engines/twp/metaengine.cpp:173
 #, fuzzy
 #| msgid "Select next music record"
 msgid "Select next Actor"
 msgstr "?????? ?????????????? ???????????????? ????????????"
 
-#: engines/twp/metaengine.cpp:175
+#: engines/twp/metaengine.cpp:174
 #, fuzzy
 #| msgid "Previous action"
 msgid "Select previous actor"
 msgstr "?????????????? ????????????"
 
-#: engines/twp/metaengine.cpp:177
+#: engines/twp/metaengine.cpp:176
 #, fuzzy
 #| msgid "Show hints"
 msgid "Show hotspots"
diff --git a/po/be-tarask.po b/po/be-tarask.po
index 2c930975d58..8d1bbd87697 100644
--- a/po/be-tarask.po
+++ b/po/be-tarask.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-13 13:52+0000\n"
+"POT-Creation-Date: 2026-06-14 18:41+0000\n"
 "PO-Revision-Date: 2023-02-26 15:01+0000\n"
 "Last-Translator: Kastu?? K. <oakum2334 at gmail.com>\n"
 "Language-Team: Belarusian (Tara??kievica) <https://translations.scummvm.org/"
@@ -80,7 +80,7 @@ msgstr "?????????????????? ????????????????:"
 #: engines/scumm/help.cpp:141 engines/scumm/help.cpp:166
 #: engines/scumm/help.cpp:194 engines/scumm/help.cpp:212
 #: engines/sherlock/metaengine.cpp:430 engines/supernova/metaengine.cpp:341
-#: engines/tot/metaengine.cpp:149 engines/twp/metaengine.cpp:179
+#: engines/tot/metaengine.cpp:149 engines/twp/metaengine.cpp:178
 msgid "Close"
 msgstr "??????????????"
 
@@ -1073,7 +1073,7 @@ msgstr "????????????????????"
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
 #: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1124
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
-#: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
+#: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:153
 #: engines/ultima/ultima0/metaengine.cpp:45
 #: engines/ultima/ultima4/metaengine.cpp:44
 #: engines/ultima/ultima4/metaengine.cpp:139
@@ -1094,7 +1094,7 @@ msgstr "??????????"
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
 #: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1091
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
-#: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
+#: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:45
 #: engines/ultima/ultima4/metaengine.cpp:140
@@ -1115,7 +1115,7 @@ msgstr "????????"
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
 #: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1104
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
-#: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
+#: engines/twp/metaengine.cpp:151 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
 #: engines/ultima/ultima4/metaengine.cpp:151
 #: engines/wintermute/keymapper_tables.h:320
@@ -1135,7 +1135,7 @@ msgstr "????????????"
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
 #: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1111
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
-#: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
+#: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
 #: engines/ultima/ultima4/metaengine.cpp:152
 #: engines/wintermute/keymapper_tables.h:326
@@ -3297,7 +3297,7 @@ msgstr "<???????????????? ??????????????????>"
 
 #. I18N: Abbreviation for 'bytes' as data size
 #. I18N: Hardware key
-#: common/util.cpp:183 backends/platform/3ds/osystem-events.cpp:46
+#: common/util.cpp:190 backends/platform/3ds/osystem-events.cpp:46
 #: backends/platform/ds/osystem_ds.cpp:178
 #: backends/platform/sdl/miyoo/miyoo.cpp:61
 #: backends/platform/sdl/miyoo/miyoo.cpp:88
@@ -3307,22 +3307,22 @@ msgid "B"
 msgstr "??"
 
 #. I18N: Abbreviation for 'kilobytes' as data size
-#: common/util.cpp:189
+#: common/util.cpp:196
 msgid "KB"
 msgstr "????"
 
 #. I18N: Abbreviation for 'megabytes' as data size
-#: common/util.cpp:194
+#: common/util.cpp:201
 msgid "MB"
 msgstr "????"
 
 #. I18N: Abbreviation for 'gigabytes' as data size
-#: common/util.cpp:200
+#: common/util.cpp:207
 msgid "GB"
 msgstr "????"
 
 #. I18N: Abbreviation for 'terabytes' as data size
-#: common/util.cpp:206
+#: common/util.cpp:213
 msgid "TB"
 msgstr "????"
 
@@ -6206,7 +6206,7 @@ msgstr "????????????????"
 #: engines/scumm/help.cpp:165 engines/scumm/help.cpp:193
 #: engines/scumm/help.cpp:211 engines/sherlock/metaengine.cpp:425
 #: engines/supernova/metaengine.cpp:336 engines/tot/metaengine.cpp:144
-#: engines/twp/metaengine.cpp:178
+#: engines/twp/metaengine.cpp:177
 msgid "Open"
 msgstr "??????????????"
 
@@ -6228,7 +6228,7 @@ msgstr "??????????"
 #: engines/sherlock/metaengine.cpp:440 engines/sherlock/metaengine.cpp:561
 #: engines/sky/metaengine.cpp:95 engines/sludge/keymapper_tables.h:333
 #: engines/supernova/metaengine.cpp:356 engines/tot/metaengine.cpp:139
-#: engines/tsage/metaengine.cpp:211 engines/twp/metaengine.cpp:186
+#: engines/tsage/metaengine.cpp:211 engines/twp/metaengine.cpp:185
 #: engines/ultima/ultima4/metaengine.cpp:74
 #: engines/ultima/nuvie/metaengine.cpp:69
 #: engines/wintermute/keymapper_tables.h:140
@@ -7044,7 +7044,7 @@ msgstr "????????????"
 
 #: engines/alcachofa/metaengine.cpp:104 engines/queen/metaengine.cpp:279
 #: engines/saga/metaengine.cpp:354 engines/scumm/help.cpp:202
-#: engines/scumm/help.cpp:214 engines/twp/metaengine.cpp:182
+#: engines/scumm/help.cpp:214 engines/twp/metaengine.cpp:181
 #: engines/wintermute/keymapper_tables.h:122
 #: engines/wintermute/keymapper_tables.h:1534
 msgid "Look at"
@@ -7415,7 +7415,7 @@ msgstr "???????????????????? ?????????? ??????"
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:210
 #: engines/phoenixvr/metaengine.cpp:94 engines/scumm/help.cpp:79
 #: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
-#: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
+#: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:161
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
 msgstr "?????????????????????? ????????????"
@@ -8355,7 +8355,7 @@ msgstr ""
 #. I18N: Perform action on object where cursor points
 #: engines/darkseed/metaengine.cpp:42 engines/dragons/metaengine.cpp:135
 #: engines/phoenixvr/metaengine.cpp:75 engines/tinsel/metaengine.cpp:294
-#: engines/twp/metaengine.cpp:150
+#: engines/twp/metaengine.cpp:149
 msgid "Action"
 msgstr "????????"
 
@@ -8907,7 +8907,7 @@ msgstr ""
 #: engines/scumm/help.cpp:139 engines/scumm/help.cpp:164
 #: engines/scumm/help.cpp:200 engines/scumm/help.cpp:210
 #: engines/sherlock/metaengine.cpp:445 engines/sherlock/metaengine.cpp:566
-#: engines/supernova/metaengine.cpp:366 engines/twp/metaengine.cpp:180
+#: engines/supernova/metaengine.cpp:366 engines/twp/metaengine.cpp:179
 msgid "Give"
 msgstr "????????"
 
@@ -12639,7 +12639,7 @@ msgstr ""
 msgid "Move / Skip / Use default action with inventory item"
 msgstr ""
 
-#: engines/queen/metaengine.cpp:198 engines/twp/metaengine.cpp:176
+#: engines/queen/metaengine.cpp:198 engines/twp/metaengine.cpp:175
 #, fuzzy
 #| msgid "Skip line"
 msgid "Skip text"
@@ -12697,7 +12697,7 @@ msgstr "???????????????? ????????????????"
 #: engines/scumm/help.cpp:144 engines/scumm/help.cpp:169
 #: engines/scumm/help.cpp:198 engines/scumm/help.cpp:213
 #: engines/scumm/help.cpp:230 engines/sherlock/metaengine.cpp:420
-#: engines/sludge/keymapper_tables.h:318 engines/twp/metaengine.cpp:181
+#: engines/sludge/keymapper_tables.h:318 engines/twp/metaengine.cpp:180
 #: engines/wintermute/keymapper_tables.h:134
 #: engines/wintermute/keymapper_tables.h:1546
 msgid "Pick up"
@@ -12706,7 +12706,7 @@ msgstr "??????????????"
 #: engines/queen/metaengine.cpp:289 engines/saga/metaengine.cpp:364
 #: engines/scumm/help.cpp:199 engines/scumm/help.cpp:215
 #: engines/scumm/help.cpp:251 engines/sludge/keymapper_tables.h:323
-#: engines/twp/metaengine.cpp:183 engines/wintermute/keymapper_tables.h:128
+#: engines/twp/metaengine.cpp:182 engines/wintermute/keymapper_tables.h:128
 #: engines/wintermute/keymapper_tables.h:1558
 msgid "Talk to"
 msgstr "???????????????? ??"
@@ -12821,7 +12821,7 @@ msgstr "?????????? ????"
 
 #: engines/saga/metaengine.cpp:389 engines/scumm/help.cpp:122
 #: engines/scumm/help.cpp:137 engines/scumm/help.cpp:162
-#: engines/supernova/metaengine.cpp:346 engines/twp/metaengine.cpp:184
+#: engines/supernova/metaengine.cpp:346 engines/twp/metaengine.cpp:183
 msgid "Push"
 msgstr "??????????"
 
@@ -13680,7 +13680,7 @@ msgstr "???????????????? ?????????????? ????????????:"
 
 #: engines/scumm/help.cpp:123 engines/scumm/help.cpp:138
 #: engines/scumm/help.cpp:163 engines/supernova/metaengine.cpp:351
-#: engines/twp/metaengine.cpp:185
+#: engines/twp/metaengine.cpp:184
 msgid "Pull"
 msgstr "??????????????"
 
@@ -16212,120 +16212,120 @@ msgstr ""
 msgid "Ransome *unbeeped* (DLC)"
 msgstr ""
 
-#: engines/twp/metaengine.cpp:151
+#: engines/twp/metaengine.cpp:150
 #, fuzzy
 msgid "Select highlighted verb"
 msgstr "???????????????? ????????"
 
-#: engines/twp/metaengine.cpp:156
+#: engines/twp/metaengine.cpp:155
 #, fuzzy
 #| msgid "Go to previous directory level"
 msgid "Go to previous object"
 msgstr "???????????????? ???? ???????????????????? ??????????"
 
-#: engines/twp/metaengine.cpp:157
+#: engines/twp/metaengine.cpp:156
 #, fuzzy
 #| msgid "Go to setup menu"
 msgid "Go to next object"
 msgstr "???? ???????? ??????????????"
 
-#: engines/twp/metaengine.cpp:158
+#: engines/twp/metaengine.cpp:157
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Select verb / item left"
 msgstr "?????????????????????? ??????????????"
 
-#: engines/twp/metaengine.cpp:159
+#: engines/twp/metaengine.cpp:158
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Select verb / item right"
 msgstr "?????????????????????? ??????????????"
 
-#: engines/twp/metaengine.cpp:160
+#: engines/twp/metaengine.cpp:159
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Select verb / item up"
 msgstr "?????????????????????? ??????????????"
 
-#: engines/twp/metaengine.cpp:161
+#: engines/twp/metaengine.cpp:160
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Select verb / item down"
 msgstr "?????????????????????? ??????????????"
 
-#: engines/twp/metaengine.cpp:163
+#: engines/twp/metaengine.cpp:162
 #, fuzzy
 #| msgid "Select Group by"
 msgid "Select actor 1"
 msgstr "?????????????? ??????????????????????"
 
-#: engines/twp/metaengine.cpp:164
+#: engines/twp/metaengine.cpp:163
 #, fuzzy
 #| msgid "Select Group by"
 msgid "Select actor 2"
 msgstr "?????????????? ??????????????????????"
 
-#: engines/twp/metaengine.cpp:165
+#: engines/twp/metaengine.cpp:164
 #, fuzzy
 #| msgid "Select Group by"
 msgid "Select actor 3"
 msgstr "?????????????? ??????????????????????"
 
-#: engines/twp/metaengine.cpp:166
+#: engines/twp/metaengine.cpp:165
 #, fuzzy
 #| msgid "Select Group by"
 msgid "Select actor 4"
 msgstr "?????????????? ??????????????????????"
 
-#: engines/twp/metaengine.cpp:167
+#: engines/twp/metaengine.cpp:166
 #, fuzzy
 #| msgid "Select Group by"
 msgid "Select actor 5"
 msgstr "?????????????? ??????????????????????"
 
-#: engines/twp/metaengine.cpp:168
+#: engines/twp/metaengine.cpp:167
 #, fuzzy
 msgid "Select choice 1"
 msgstr "???????????????? ????????"
 
-#: engines/twp/metaengine.cpp:169
+#: engines/twp/metaengine.cpp:168
 #, fuzzy
 msgid "Select choice 2"
 msgstr "???????????????? ????????"
 
-#: engines/twp/metaengine.cpp:170
+#: engines/twp/metaengine.cpp:169
 #, fuzzy
 msgid "Select choice 3"
 msgstr "???????????????? ????????"
 
-#: engines/twp/metaengine.cpp:171
+#: engines/twp/metaengine.cpp:170
 #, fuzzy
 msgid "Select choice 4"
 msgstr "???????????????? ????????"
 
-#: engines/twp/metaengine.cpp:172
+#: engines/twp/metaengine.cpp:171
 #, fuzzy
 msgid "Select choice 5"
 msgstr "???????????????? ????????"
 
-#: engines/twp/metaengine.cpp:173
+#: engines/twp/metaengine.cpp:172
 #, fuzzy
 msgid "Select choice 6"
 msgstr "???????????????? ????????"
 
-#: engines/twp/metaengine.cpp:174
+#: engines/twp/metaengine.cpp:173
 #, fuzzy
 #| msgid "Select next music record"
 msgid "Select next Actor"
 msgstr "?????????????? ???????????????? ?????????????? ??????????"
 
-#: engines/twp/metaengine.cpp:175
+#: engines/twp/metaengine.cpp:174
 #, fuzzy
 #| msgid "Previous action"
 msgid "Select previous actor"
 msgstr "???????????????????? ????????"
 
-#: engines/twp/metaengine.cpp:177
+#: engines/twp/metaengine.cpp:176
 #, fuzzy
 #| msgid "Show hints"
 msgid "Show hotspots"
diff --git a/po/be_BY.po b/po/be_BY.po
index 0469eb04636..3c526d0399c 100644
--- a/po/be_BY.po
+++ b/po/be_BY.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-13 13:52+0000\n"
+"POT-Creation-Date: 2026-06-14 18:41+0000\n"
 "PO-Revision-Date: 2021-03-21 08:24+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Belarusian <https://translations.scummvm.org/projects/scummvm/"
@@ -78,7 +78,7 @@ msgstr "?????????????????? ??????????????????:"
 #: engines/scumm/help.cpp:141 engines/scumm/help.cpp:166
 #: engines/scumm/help.cpp:194 engines/scumm/help.cpp:212
 #: engines/sherlock/metaengine.cpp:430 engines/supernova/metaengine.cpp:341
-#: engines/tot/metaengine.cpp:149 engines/twp/metaengine.cpp:179
+#: engines/tot/metaengine.cpp:149 engines/twp/metaengine.cpp:178
 msgid "Close"
 msgstr "??????????????"
 
@@ -1076,7 +1076,7 @@ msgstr "??????????????????????????"
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
 #: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1124
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
-#: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
+#: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:153
 #: engines/ultima/ultima0/metaengine.cpp:45
 #: engines/ultima/ultima4/metaengine.cpp:44
 #: engines/ultima/ultima4/metaengine.cpp:139
@@ -1097,7 +1097,7 @@ msgstr "??????????"
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
 #: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1091
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
-#: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
+#: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:45
 #: engines/ultima/ultima4/metaengine.cpp:140
@@ -1118,7 +1118,7 @@ msgstr "????????"
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
 #: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1104
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
-#: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
+#: engines/twp/metaengine.cpp:151 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
 #: engines/ultima/ultima4/metaengine.cpp:151
 #: engines/wintermute/keymapper_tables.h:320
@@ -1138,7 +1138,7 @@ msgstr "????????????"
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
 #: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1111
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
-#: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
+#: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
 #: engines/ultima/ultima4/metaengine.cpp:152
 #: engines/wintermute/keymapper_tables.h:326
@@ -3311,7 +3311,7 @@ msgstr "<???????????? ????????????????>"
 
 #. I18N: Abbreviation for 'bytes' as data size
 #. I18N: Hardware key
-#: common/util.cpp:183 backends/platform/3ds/osystem-events.cpp:46
+#: common/util.cpp:190 backends/platform/3ds/osystem-events.cpp:46
 #: backends/platform/ds/osystem_ds.cpp:178
 #: backends/platform/sdl/miyoo/miyoo.cpp:61
 #: backends/platform/sdl/miyoo/miyoo.cpp:88
@@ -3321,25 +3321,25 @@ msgid "B"
 msgstr "B"
 
 #. I18N: Abbreviation for 'kilobytes' as data size
-#: common/util.cpp:189
+#: common/util.cpp:196
 #, fuzzy
 msgid "KB"
 msgstr "B"
 
 #. I18N: Abbreviation for 'megabytes' as data size
-#: common/util.cpp:194
+#: common/util.cpp:201
 #, fuzzy
 msgid "MB"
 msgstr "SMB"
 
 #. I18N: Abbreviation for 'gigabytes' as data size
-#: common/util.cpp:200
+#: common/util.cpp:207
 #, fuzzy
 msgid "GB"
 msgstr "B"
 
 #. I18N: Abbreviation for 'terabytes' as data size
-#: common/util.cpp:206
+#: common/util.cpp:213
 #, fuzzy
 msgid "TB"
 msgstr "B"
@@ -6215,7 +6215,7 @@ msgstr "????????????????"
 #: engines/scumm/help.cpp:165 engines/scumm/help.cpp:193
 #: engines/scumm/help.cpp:211 engines/sherlock/metaengine.cpp:425
 #: engines/supernova/metaengine.cpp:336 engines/tot/metaengine.cpp:144
-#: engines/twp/metaengine.cpp:178
+#: engines/twp/metaengine.cpp:177
 msgid "Open"
 msgstr "????????????????"
 
@@ -6237,7 +6237,7 @@ msgstr "??????????"
 #: engines/sherlock/metaengine.cpp:440 engines/sherlock/metaengine.cpp:561
 #: engines/sky/metaengine.cpp:95 engines/sludge/keymapper_tables.h:333
 #: engines/supernova/metaengine.cpp:356 engines/tot/metaengine.cpp:139
-#: engines/tsage/metaengine.cpp:211 engines/twp/metaengine.cpp:186
+#: engines/tsage/metaengine.cpp:211 engines/twp/metaengine.cpp:185
 #: engines/ultima/ultima4/metaengine.cpp:74
 #: engines/ultima/nuvie/metaengine.cpp:69
 #: engines/wintermute/keymapper_tables.h:140
@@ -7022,7 +7022,7 @@ msgstr "??????????????"
 
 #: engines/alcachofa/metaengine.cpp:104 engines/queen/metaengine.cpp:279
 #: engines/saga/metaengine.cpp:354 engines/scumm/help.cpp:202
-#: engines/scumm/help.cpp:214 engines/twp/metaengine.cpp:182
+#: engines/scumm/help.cpp:214 engines/twp/metaengine.cpp:181
 #: engines/wintermute/keymapper_tables.h:122
 #: engines/wintermute/keymapper_tables.h:1534
 msgid "Look at"
@@ -7396,7 +7396,7 @@ msgstr "?????????????????????? ??????"
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:210
 #: engines/phoenixvr/metaengine.cpp:94 engines/scumm/help.cpp:79
 #: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
-#: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
+#: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:161
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
 msgstr "???????????????????? ????????????????"
@@ -8318,7 +8318,7 @@ msgstr ""
 #. I18N: Perform action on object where cursor points
 #: engines/darkseed/metaengine.cpp:42 engines/dragons/metaengine.cpp:135
 #: engines/phoenixvr/metaengine.cpp:75 engines/tinsel/metaengine.cpp:294
-#: engines/twp/metaengine.cpp:150
+#: engines/twp/metaengine.cpp:149
 msgid "Action"
 msgstr "??????????????"
 
@@ -8843,7 +8843,7 @@ msgstr ""
 #: engines/scumm/help.cpp:139 engines/scumm/help.cpp:164
 #: engines/scumm/help.cpp:200 engines/scumm/help.cpp:210
 #: engines/sherlock/metaengine.cpp:445 engines/sherlock/metaengine.cpp:566
-#: engines/supernova/metaengine.cpp:366 engines/twp/metaengine.cpp:180
+#: engines/supernova/metaengine.cpp:366 engines/twp/metaengine.cpp:179
 msgid "Give"
 msgstr "????????"
 
@@ -12486,7 +12486,7 @@ msgstr ""
 msgid "Move / Skip / Use default action with inventory item"
 msgstr ""
 
-#: engines/queen/metaengine.cpp:198 engines/twp/metaengine.cpp:176
+#: engines/queen/metaengine.cpp:198 engines/twp/metaengine.cpp:175
 #, fuzzy
 #| msgid "Skip text"
 msgid "Skip text"
@@ -12543,7 +12543,7 @@ msgstr "???????????????? ????????????"
 #: engines/scumm/help.cpp:144 engines/scumm/help.cpp:169
 #: engines/scumm/help.cpp:198 engines/scumm/help.cpp:213
 #: engines/scumm/help.cpp:230 engines/sherlock/metaengine.cpp:420
-#: engines/sludge/keymapper_tables.h:318 engines/twp/metaengine.cpp:181
+#: engines/sludge/keymapper_tables.h:318 engines/twp/metaengine.cpp:180
 #: engines/wintermute/keymapper_tables.h:134
 #: engines/wintermute/keymapper_tables.h:1546
 msgid "Pick up"
@@ -12552,7 +12552,7 @@ msgstr "??????????????"
 #: engines/queen/metaengine.cpp:289 engines/saga/metaengine.cpp:364
 #: engines/scumm/help.cpp:199 engines/scumm/help.cpp:215
 #: engines/scumm/help.cpp:251 engines/sludge/keymapper_tables.h:323
-#: engines/twp/metaengine.cpp:183 engines/wintermute/keymapper_tables.h:128
+#: engines/twp/metaengine.cpp:182 engines/wintermute/keymapper_tables.h:128
 #: engines/wintermute/keymapper_tables.h:1558
 msgid "Talk to"
 msgstr "???????????????? ??"
@@ -12663,7 +12663,7 @@ msgstr "???????? ????"
 
 #: engines/saga/metaengine.cpp:389 engines/scumm/help.cpp:122
 #: engines/scumm/help.cpp:137 engines/scumm/help.cpp:162
-#: engines/supernova/metaengine.cpp:346 engines/twp/metaengine.cpp:184
+#: engines/supernova/metaengine.cpp:346 engines/twp/metaengine.cpp:183
 msgid "Push"
 msgstr "??????????"
 
@@ -13502,7 +13502,7 @@ msgstr "???????????????? ?????????????????? ??????????????:"
 
 #: engines/scumm/help.cpp:123 engines/scumm/help.cpp:138
 #: engines/scumm/help.cpp:163 engines/supernova/metaengine.cpp:351
-#: engines/twp/metaengine.cpp:185
+#: engines/twp/metaengine.cpp:184
 msgid "Pull"
 msgstr "??????????????"
 
@@ -15956,119 +15956,119 @@ msgstr ""
 msgid "Ransome *unbeeped* (DLC)"
 msgstr ""
 
-#: engines/twp/metaengine.cpp:151
+#: engines/twp/metaengine.cpp:150
 #, fuzzy
 msgid "Select highlighted verb"
 msgstr "?????????????? ????????"
 
-#: engines/twp/metaengine.cpp:156
+#: engines/twp/metaengine.cpp:155
 #, fuzzy
 #| msgid "Go to previous directory level"
 msgid "Go to previous object"
 msgstr "???????????????? ???? ???????????????????? ???????????????? ??????????"
 
-#: engines/twp/metaengine.cpp:157
+#: engines/twp/metaengine.cpp:156
 #, fuzzy
 msgid "Go to next object"
 msgstr "???????????????? ???????? ??????????"
 
-#: engines/twp/metaengine.cpp:158
+#: engines/twp/metaengine.cpp:157
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Select verb / item left"
 msgstr "???????????????? ??????????????"
 
-#: engines/twp/metaengine.cpp:159
+#: engines/twp/metaengine.cpp:158
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Select verb / item right"
 msgstr "???????????????? ??????????????"
 
-#: engines/twp/metaengine.cpp:160
+#: engines/twp/metaengine.cpp:159
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Select verb / item up"
 msgstr "???????????????? ??????????????"
 
-#: engines/twp/metaengine.cpp:161
+#: engines/twp/metaengine.cpp:160
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Select verb / item down"
 msgstr "???????????????? ??????????????"
 
-#: engines/twp/metaengine.cpp:163
+#: engines/twp/metaengine.cpp:162
 #, fuzzy
 #| msgid "Select"
 msgid "Select actor 1"
 msgstr "??????????"
 
-#: engines/twp/metaengine.cpp:164
+#: engines/twp/metaengine.cpp:163
 #, fuzzy
 #| msgid "Select"
 msgid "Select actor 2"
 msgstr "??????????"
 
-#: engines/twp/metaengine.cpp:165
+#: engines/twp/metaengine.cpp:164
 #, fuzzy
 #| msgid "Select"
 msgid "Select actor 3"
 msgstr "??????????"
 
-#: engines/twp/metaengine.cpp:166
+#: engines/twp/metaengine.cpp:165
 #, fuzzy
 #| msgid "Select"
 msgid "Select actor 4"
 msgstr "??????????"
 
-#: engines/twp/metaengine.cpp:167
+#: engines/twp/metaengine.cpp:166
 #, fuzzy
 #| msgid "Select"
 msgid "Select actor 5"
 msgstr "??????????"
 
-#: engines/twp/metaengine.cpp:168
+#: engines/twp/metaengine.cpp:167
 #, fuzzy
 msgid "Select choice 1"
 msgstr "?????????????? ????????"
 
-#: engines/twp/metaengine.cpp:169
+#: engines/twp/metaengine.cpp:168
 #, fuzzy
 msgid "Select choice 2"
 msgstr "?????????????? ????????"
 
-#: engines/twp/metaengine.cpp:170
+#: engines/twp/metaengine.cpp:169
 #, fuzzy
 msgid "Select choice 3"
 msgstr "?????????????? ????????"
 
-#: engines/twp/metaengine.cpp:171
+#: engines/twp/metaengine.cpp:170
 #, fuzzy
 msgid "Select choice 4"
 msgstr "?????????????? ????????"
 
-#: engines/twp/metaengine.cpp:172
+#: engines/twp/metaengine.cpp:171
 #, fuzzy
 msgid "Select choice 5"
 msgstr "?????????????? ????????"
 
-#: engines/twp/metaengine.cpp:173
+#: engines/twp/metaengine.cpp:172
 #, fuzzy
 msgid "Select choice 6"
 msgstr "?????????????? ????????"
 
-#: engines/twp/metaengine.cpp:174
+#: engines/twp/metaengine.cpp:173
 #, fuzzy
 #| msgid "Select next music record"
 msgid "Select next Actor"
 msgstr "?????????????????? ???????????????? ?????????????? ??????????"
 
-#: engines/twp/metaengine.cpp:175
+#: engines/twp/metaengine.cpp:174
 #, fuzzy
 #| msgid "Previous action"
 msgid "Select previous actor"
 msgstr "???????????????????? ??????????????"
 
-#: engines/twp/metaengine.cpp:177
+#: engines/twp/metaengine.cpp:176
 #, fuzzy
 #| msgid "Show hints"
 msgid "Show hotspots"
diff --git a/po/ca_ES.po b/po/ca_ES.po
index 5256e61fac0..7570b3b7540 100644
--- a/po/ca_ES.po
+++ b/po/ca_ES.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.6.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-13 13:52+0000\n"
+"POT-Creation-Date: 2026-06-14 18:41+0000\n"
 "PO-Revision-Date: 2026-05-26 04:22+0000\n"
 "Last-Translator: Xaviu <xaviu at xaviu.com>\n"
 "Language-Team: Catalan <https://translations.scummvm.org/projects/scummvm/"
@@ -72,7 +72,7 @@ msgstr "Motors disponibles:"
 #: engines/scumm/help.cpp:141 engines/scumm/help.cpp:166
 #: engines/scumm/help.cpp:194 engines/scumm/help.cpp:212
 #: engines/sherlock/metaengine.cpp:430 engines/supernova/metaengine.cpp:341
-#: engines/tot/metaengine.cpp:149 engines/twp/metaengine.cpp:179
+#: engines/tot/metaengine.cpp:149 engines/twp/metaengine.cpp:178
 msgid "Close"
 msgstr "Tanca"
 
@@ -1059,7 +1059,7 @@ msgstr "Interaccionar"
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
 #: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1124
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
-#: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
+#: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:153
 #: engines/ultima/ultima0/metaengine.cpp:45
 #: engines/ultima/ultima4/metaengine.cpp:44
 #: engines/ultima/ultima4/metaengine.cpp:139
@@ -1080,7 +1080,7 @@ msgstr "Amunt"
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
 #: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1091
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
-#: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
+#: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:45
 #: engines/ultima/ultima4/metaengine.cpp:140
@@ -1101,7 +1101,7 @@ msgstr "Avall"
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
 #: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1104
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
-#: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
+#: engines/twp/metaengine.cpp:151 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
 #: engines/ultima/ultima4/metaengine.cpp:151
 #: engines/wintermute/keymapper_tables.h:320
@@ -1121,7 +1121,7 @@ msgstr "Esquerra"
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
 #: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1111
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
-#: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
+#: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
 #: engines/ultima/ultima4/metaengine.cpp:152
 #: engines/wintermute/keymapper_tables.h:326
@@ -3239,7 +3239,7 @@ msgstr "<Valor incorrecte>"
 
 #. I18N: Abbreviation for 'bytes' as data size
 #. I18N: Hardware key
-#: common/util.cpp:183 backends/platform/3ds/osystem-events.cpp:46
+#: common/util.cpp:190 backends/platform/3ds/osystem-events.cpp:46
 #: backends/platform/ds/osystem_ds.cpp:178
 #: backends/platform/sdl/miyoo/miyoo.cpp:61
 #: backends/platform/sdl/miyoo/miyoo.cpp:88
@@ -3249,22 +3249,22 @@ msgid "B"
 msgstr "B"
 
 #. I18N: Abbreviation for 'kilobytes' as data size
-#: common/util.cpp:189
+#: common/util.cpp:196
 msgid "KB"
 msgstr "KB"
 
 #. I18N: Abbreviation for 'megabytes' as data size
-#: common/util.cpp:194
+#: common/util.cpp:201
 msgid "MB"
 msgstr "MB"
 
 #. I18N: Abbreviation for 'gigabytes' as data size
-#: common/util.cpp:200
+#: common/util.cpp:207
 msgid "GB"
 msgstr "GB"
 
 #. I18N: Abbreviation for 'terabytes' as data size
-#: common/util.cpp:206
+#: common/util.cpp:213
 msgid "TB"
 msgstr "TB"
 
@@ -6007,7 +6007,7 @@ msgstr "Mira"
 #: engines/scumm/help.cpp:165 engines/scumm/help.cpp:193
 #: engines/scumm/help.cpp:211 engines/sherlock/metaengine.cpp:425
 #: engines/supernova/metaengine.cpp:336 engines/tot/metaengine.cpp:144
-#: engines/twp/metaengine.cpp:178
+#: engines/twp/metaengine.cpp:177
 msgid "Open"
 msgstr "Obre"
 
@@ -6029,7 +6029,7 @@ msgstr "Obt??"
 #: engines/sherlock/metaengine.cpp:440 engines/sherlock/metaengine.cpp:561
 #: engines/sky/metaengine.cpp:95 engines/sludge/keymapper_tables.h:333
 #: engines/supernova/metaengine.cpp:356 engines/tot/metaengine.cpp:139
-#: engines/tsage/metaengine.cpp:211 engines/twp/metaengine.cpp:186
+#: engines/tsage/metaengine.cpp:211 engines/twp/metaengine.cpp:185
 #: engines/ultima/ultima4/metaengine.cpp:74
 #: engines/ultima/nuvie/metaengine.cpp:69
 #: engines/wintermute/keymapper_tables.h:140
@@ -6809,7 +6809,7 @@ msgstr "Activa"
 
 #: engines/alcachofa/metaengine.cpp:104 engines/queen/metaengine.cpp:279
 #: engines/saga/metaengine.cpp:354 engines/scumm/help.cpp:202
-#: engines/scumm/help.cpp:214 engines/twp/metaengine.cpp:182
+#: engines/scumm/help.cpp:214 engines/twp/metaengine.cpp:181
 #: engines/wintermute/keymapper_tables.h:122
 #: engines/wintermute/keymapper_tables.h:1534
 msgid "Look at"
@@ -7162,7 +7162,7 @@ msgstr "Activa / desactiva el combat"
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:210
 #: engines/phoenixvr/metaengine.cpp:94 engines/scumm/help.cpp:79
 #: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
-#: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
+#: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:161
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
 msgstr "Salta la seq????ncia de video"
@@ -7985,7 +7985,7 @@ msgstr "Nom??s efectes de so de la versi?? de disquet"
 #. I18N: Perform action on object where cursor points
 #: engines/darkseed/metaengine.cpp:42 engines/dragons/metaengine.cpp:135
 #: engines/phoenixvr/metaengine.cpp:75 engines/tinsel/metaengine.cpp:294
-#: engines/twp/metaengine.cpp:150
+#: engines/twp/metaengine.cpp:149
 msgid "Action"
 msgstr "Acci??"
 
@@ -8403,7 +8403,7 @@ msgstr "Equipa"
 #: engines/scumm/help.cpp:139 engines/scumm/help.cpp:164
 #: engines/scumm/help.cpp:200 engines/scumm/help.cpp:210
 #: engines/sherlock/metaengine.cpp:445 engines/sherlock/metaengine.cpp:566
-#: engines/supernova/metaengine.cpp:366 engines/twp/metaengine.cpp:180
+#: engines/supernova/metaengine.cpp:366 engines/twp/metaengine.cpp:179
 msgid "Give"
 msgstr "D??na"
 
@@ -11688,7 +11688,7 @@ msgstr ""
 msgid "Move / Skip / Use default action with inventory item"
 msgstr ""
 
-#: engines/queen/metaengine.cpp:198 engines/twp/metaengine.cpp:176
+#: engines/queen/metaengine.cpp:198 engines/twp/metaengine.cpp:175
 msgid "Skip text"
 msgstr "Omet el text"
 
@@ -11728,7 +11728,7 @@ msgstr "Utilitza el diari"
 #: engines/scumm/help.cpp:144 engines/scumm/help.cpp:169
 #: engines/scumm/help.cpp:198 engines/scumm/help.cpp:213
 #: engines/scumm/help.cpp:230 engines/sherlock/metaengine.cpp:420
-#: engines/sludge/keymapper_tables.h:318 engines/twp/metaengine.cpp:181
+#: engines/sludge/keymapper_tables.h:318 engines/twp/metaengine.cpp:180
 #: engines/wintermute/keymapper_tables.h:134
 #: engines/wintermute/keymapper_tables.h:1546
 msgid "Pick up"
@@ -11737,7 +11737,7 @@ msgstr "Recull"
 #: engines/queen/metaengine.cpp:289 engines/saga/metaengine.cpp:364
 #: engines/scumm/help.cpp:199 engines/scumm/help.cpp:215
 #: engines/scumm/help.cpp:251 engines/sludge/keymapper_tables.h:323
-#: engines/twp/metaengine.cpp:183 engines/wintermute/keymapper_tables.h:128
+#: engines/twp/metaengine.cpp:182 engines/wintermute/keymapper_tables.h:128
 #: engines/wintermute/keymapper_tables.h:1558
 msgid "Talk to"
 msgstr "Parla amb"
@@ -11832,7 +11832,7 @@ msgstr "V??s a"
 
 #: engines/saga/metaengine.cpp:389 engines/scumm/help.cpp:122
 #: engines/scumm/help.cpp:137 engines/scumm/help.cpp:162
-#: engines/supernova/metaengine.cpp:346 engines/twp/metaengine.cpp:184
+#: engines/supernova/metaengine.cpp:346 engines/twp/metaengine.cpp:183
 msgid "Push"
 msgstr "Empeny"
 
@@ -12593,7 +12593,7 @@ msgstr "Controls principals del joc:"
 
 #: engines/scumm/help.cpp:123 engines/scumm/help.cpp:138
 #: engines/scumm/help.cpp:163 engines/supernova/metaengine.cpp:351
-#: engines/twp/metaengine.cpp:185
+#: engines/twp/metaengine.cpp:184
 msgid "Pull"
 msgstr "Estira"
 
@@ -14746,87 +14746,87 @@ msgstr "Sent??ncia cl??ssica"
 msgid "Ransome *unbeeped* (DLC)"
 msgstr "Ransome *unbeeped* (DLC)"
 
-#: engines/twp/metaengine.cpp:151
+#: engines/twp/metaengine.cpp:150
 msgid "Select highlighted verb"
 msgstr "Selecciona el verb ressaltat"
 
-#: engines/twp/metaengine.cpp:156
+#: engines/twp/metaengine.cpp:155
 msgid "Go to previous object"
 msgstr "Ves a l'objecte anterior"
 
-#: engines/twp/metaengine.cpp:157
+#: engines/twp/metaengine.cpp:156
 msgid "Go to next object"
 msgstr "Ves al seg??ent objecte"
 
-#: engines/twp/metaengine.cpp:158
+#: engines/twp/metaengine.cpp:157
 msgid "Select verb / item left"
 msgstr "Selecciona un verb / element a l'esquerra"
 
-#: engines/twp/metaengine.cpp:159
+#: engines/twp/metaengine.cpp:158
 msgid "Select verb / item right"
 msgstr "Selecciona un verb / element a la dreta"
 
-#: engines/twp/metaengine.cpp:160
+#: engines/twp/metaengine.cpp:159
 msgid "Select verb / item up"
 msgstr "Selecciona un verb / element amunt"
 
-#: engines/twp/metaengine.cpp:161
+#: engines/twp/metaengine.cpp:160
 msgid "Select verb / item down"
 msgstr "Selecciona un verb / element avall"
 
-#: engines/twp/metaengine.cpp:163
+#: engines/twp/metaengine.cpp:162
 msgid "Select actor 1"
 msgstr "Selecciona l'actor 1"
 
-#: engines/twp/metaengine.cpp:164
+#: engines/twp/metaengine.cpp:163
 msgid "Select actor 2"
 msgstr "Selecciona l'actor 2"
 
-#: engines/twp/metaengine.cpp:165
+#: engines/twp/metaengine.cpp:164
 msgid "Select actor 3"
 msgstr "Selecciona l'actor 3"
 
-#: engines/twp/metaengine.cpp:166
+#: engines/twp/metaengine.cpp:165
 msgid "Select actor 4"
 msgstr "Selecciona l'actor 4"
 
-#: engines/twp/metaengine.cpp:167
+#: engines/twp/metaengine.cpp:166
 msgid "Select actor 5"
 msgstr "Selecciona l'actor 5"
 
-#: engines/twp/metaengine.cpp:168
+#: engines/twp/metaengine.cpp:167
 msgid "Select choice 1"
 msgstr "Selecciona la selecci?? 1"
 
-#: engines/twp/metaengine.cpp:169
+#: engines/twp/metaengine.cpp:168
 msgid "Select choice 2"
 msgstr "Selecciona la selecci?? 2"
 
-#: engines/twp/metaengine.cpp:170
+#: engines/twp/metaengine.cpp:169
 msgid "Select choice 3"
 msgstr "Selecciona la selecci?? 3"
 
-#: engines/twp/metaengine.cpp:171
+#: engines/twp/metaengine.cpp:170
 msgid "Select choice 4"
 msgstr "Selecciona la selecci?? 4"
 
-#: engines/twp/metaengine.cpp:172
+#: engines/twp/metaengine.cpp:171
 msgid "Select choice 5"
 msgstr "Selecciona la selecci?? 5"
 
-#: engines/twp/metaengine.cpp:173
+#: engines/twp/metaengine.cpp:172
 msgid "Select choice 6"
 msgstr "Selecciona la selecci?? 6"
 
-#: engines/twp/metaengine.cpp:174
+#: engines/twp/metaengine.cpp:173
 msgid "Select next Actor"
 msgstr "Selecciona l'actor seg??ent"
 
-#: engines/twp/metaengine.cpp:175
+#: engines/twp/metaengine.cpp:174
 msgid "Select previous actor"
 msgstr "Selecciona l'actor anterior"
 
-#: engines/twp/metaengine.cpp:177
+#: engines/twp/metaengine.cpp:176
 msgid "Show hotspots"
 msgstr "Mostra punts d'inter??s"
 
diff --git a/po/cs_CZ.po b/po/cs_CZ.po
index 80dcb8556b6..9b0f2147bad 100644
--- a/po/cs_CZ.po
+++ b/po/cs_CZ.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.7.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-13 13:52+0000\n"
+"POT-Creation-Date: 2026-06-14 18:41+0000\n"
 "PO-Revision-Date: 2026-02-09 20:22+0000\n"
 "Last-Translator: AsciiWolf <mail at asciiwolf.com>\n"
 "Language-Team: Czech <https://translations.scummvm.org/projects/scummvm/"
@@ -81,7 +81,7 @@ msgstr "Dostupn?? j??dra:"
 #: engines/scumm/help.cpp:141 engines/scumm/help.cpp:166
 #: engines/scumm/help.cpp:194 engines/scumm/help.cpp:212
 #: engines/sherlock/metaengine.cpp:430 engines/supernova/metaengine.cpp:341
-#: engines/tot/metaengine.cpp:149 engines/twp/metaengine.cpp:179
+#: engines/tot/metaengine.cpp:149 engines/twp/metaengine.cpp:178
 msgid "Close"
 msgstr "Zav????t"
 
@@ -1076,7 +1076,7 @@ msgstr "Interakce"
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
 #: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1124
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
-#: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
+#: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:153
 #: engines/ultima/ultima0/metaengine.cpp:45
 #: engines/ultima/ultima4/metaengine.cpp:44
 #: engines/ultima/ultima4/metaengine.cpp:139
@@ -1097,7 +1097,7 @@ msgstr "Nahoru"
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
 #: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1091
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
-#: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
+#: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:45
 #: engines/ultima/ultima4/metaengine.cpp:140
@@ -1118,7 +1118,7 @@ msgstr "Dol??"
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
 #: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1104
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
-#: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
+#: engines/twp/metaengine.cpp:151 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
 #: engines/ultima/ultima4/metaengine.cpp:151
 #: engines/wintermute/keymapper_tables.h:320
@@ -1138,7 +1138,7 @@ msgstr "Doleva"
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
 #: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1111
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
-#: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
+#: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
 #: engines/ultima/ultima4/metaengine.cpp:152
 #: engines/wintermute/keymapper_tables.h:326
@@ -3299,7 +3299,7 @@ msgstr "<??patn?? hodnota>"
 
 #. I18N: Abbreviation for 'bytes' as data size
 #. I18N: Hardware key
-#: common/util.cpp:183 backends/platform/3ds/osystem-events.cpp:46
+#: common/util.cpp:190 backends/platform/3ds/osystem-events.cpp:46
 #: backends/platform/ds/osystem_ds.cpp:178
 #: backends/platform/sdl/miyoo/miyoo.cpp:61
 #: backends/platform/sdl/miyoo/miyoo.cpp:88
@@ -3309,23 +3309,23 @@ msgid "B"
 msgstr ""
 
 #. I18N: Abbreviation for 'kilobytes' as data size
-#: common/util.cpp:189
+#: common/util.cpp:196
 msgid "KB"
 msgstr ""
 
 #. I18N: Abbreviation for 'megabytes' as data size
-#: common/util.cpp:194
+#: common/util.cpp:201
 #, fuzzy
 msgid "MB"
 msgstr "SMB"
 
 #. I18N: Abbreviation for 'gigabytes' as data size
-#: common/util.cpp:200
+#: common/util.cpp:207
 msgid "GB"
 msgstr ""
 
 #. I18N: Abbreviation for 'terabytes' as data size
-#: common/util.cpp:206
+#: common/util.cpp:213
 msgid "TB"
 msgstr ""
 
@@ -6183,7 +6183,7 @@ msgstr "D??vat se"
 #: engines/scumm/help.cpp:165 engines/scumm/help.cpp:193
 #: engines/scumm/help.cpp:211 engines/sherlock/metaengine.cpp:425
 #: engines/supernova/metaengine.cpp:336 engines/tot/metaengine.cpp:144
-#: engines/twp/metaengine.cpp:178
+#: engines/twp/metaengine.cpp:177
 msgid "Open"
 msgstr "Otev????t"
 
@@ -6205,7 +6205,7 @@ msgstr "Vz??t"
 #: engines/sherlock/metaengine.cpp:440 engines/sherlock/metaengine.cpp:561
 #: engines/sky/metaengine.cpp:95 engines/sludge/keymapper_tables.h:333
 #: engines/supernova/metaengine.cpp:356 engines/tot/metaengine.cpp:139
-#: engines/tsage/metaengine.cpp:211 engines/twp/metaengine.cpp:186
+#: engines/tsage/metaengine.cpp:211 engines/twp/metaengine.cpp:185
 #: engines/ultima/ultima4/metaengine.cpp:74
 #: engines/ultima/nuvie/metaengine.cpp:69
 #: engines/wintermute/keymapper_tables.h:140
@@ -6985,7 +6985,7 @@ msgstr "Aktivn??"
 
 #: engines/alcachofa/metaengine.cpp:104 engines/queen/metaengine.cpp:279
 #: engines/saga/metaengine.cpp:354 engines/scumm/help.cpp:202
-#: engines/scumm/help.cpp:214 engines/twp/metaengine.cpp:182
+#: engines/scumm/help.cpp:214 engines/twp/metaengine.cpp:181
 #: engines/wintermute/keymapper_tables.h:122
 #: engines/wintermute/keymapper_tables.h:1534
 msgid "Look at"
@@ -7350,7 +7350,7 @@ msgstr "Povolit zachycov??n?? my??i"
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:210
 #: engines/phoenixvr/metaengine.cpp:94 engines/scumm/help.cpp:79
 #: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
-#: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
+#: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:161
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
 msgstr "P??esko??it video"
@@ -8263,7 +8263,7 @@ msgstr ""
 #. I18N: Perform action on object where cursor points
 #: engines/darkseed/metaengine.cpp:42 engines/dragons/metaengine.cpp:135
 #: engines/phoenixvr/metaengine.cpp:75 engines/tinsel/metaengine.cpp:294
-#: engines/twp/metaengine.cpp:150
+#: engines/twp/metaengine.cpp:149
 #, fuzzy
 msgid "Action"
 msgstr "Aktivn??"
@@ -8757,7 +8757,7 @@ msgstr ""
 #: engines/scumm/help.cpp:139 engines/scumm/help.cpp:164
 #: engines/scumm/help.cpp:200 engines/scumm/help.cpp:210
 #: engines/sherlock/metaengine.cpp:445 engines/sherlock/metaengine.cpp:566
-#: engines/supernova/metaengine.cpp:366 engines/twp/metaengine.cpp:180
+#: engines/supernova/metaengine.cpp:366 engines/twp/metaengine.cpp:179
 msgid "Give"
 msgstr "D??t"
 
@@ -12321,7 +12321,7 @@ msgstr ""
 msgid "Move / Skip / Use default action with inventory item"
 msgstr ""
 
-#: engines/queen/metaengine.cpp:198 engines/twp/metaengine.cpp:176
+#: engines/queen/metaengine.cpp:198 engines/twp/metaengine.cpp:175
 #, fuzzy
 #| msgid "Skip text"
 msgid "Skip text"
@@ -12377,7 +12377,7 @@ msgstr "Zobrazit V??e"
 #: engines/scumm/help.cpp:144 engines/scumm/help.cpp:169
 #: engines/scumm/help.cpp:198 engines/scumm/help.cpp:213
 #: engines/scumm/help.cpp:230 engines/sherlock/metaengine.cpp:420
-#: engines/sludge/keymapper_tables.h:318 engines/twp/metaengine.cpp:181
+#: engines/sludge/keymapper_tables.h:318 engines/twp/metaengine.cpp:180
 #: engines/wintermute/keymapper_tables.h:134
 #: engines/wintermute/keymapper_tables.h:1546
 msgid "Pick up"
@@ -12386,7 +12386,7 @@ msgstr "Sebrat"
 #: engines/queen/metaengine.cpp:289 engines/saga/metaengine.cpp:364
 #: engines/scumm/help.cpp:199 engines/scumm/help.cpp:215
 #: engines/scumm/help.cpp:251 engines/sludge/keymapper_tables.h:323
-#: engines/twp/metaengine.cpp:183 engines/wintermute/keymapper_tables.h:128
+#: engines/twp/metaengine.cpp:182 engines/wintermute/keymapper_tables.h:128
 #: engines/wintermute/keymapper_tables.h:1558
 msgid "Talk to"
 msgstr "Mluvit s"
@@ -12495,7 +12495,7 @@ msgstr "P??ej??t na"
 
 #: engines/saga/metaengine.cpp:389 engines/scumm/help.cpp:122
 #: engines/scumm/help.cpp:137 engines/scumm/help.cpp:162
-#: engines/supernova/metaengine.cpp:346 engines/twp/metaengine.cpp:184
+#: engines/supernova/metaengine.cpp:346 engines/twp/metaengine.cpp:183
 msgid "Push"
 msgstr "Tla??it"
 
@@ -13307,7 +13307,7 @@ msgstr "Hlavn?? ovl??dac?? prvky:"
 
 #: engines/scumm/help.cpp:123 engines/scumm/help.cpp:138
 #: engines/scumm/help.cpp:163 engines/supernova/metaengine.cpp:351
-#: engines/twp/metaengine.cpp:185
+#: engines/twp/metaengine.cpp:184
 msgid "Pull"
 msgstr "T??hnout"
 
@@ -15713,112 +15713,112 @@ msgstr ""
 msgid "Ransome *unbeeped* (DLC)"
 msgstr ""
 
-#: engines/twp/metaengine.cpp:151
+#: engines/twp/metaengine.cpp:150
 #, fuzzy
 msgid "Select highlighted verb"
 msgstr "Vyberte Vzhled"
 
-#: engines/twp/metaengine.cpp:156
+#: engines/twp/metaengine.cpp:155
 #, fuzzy
 #| msgid "Go to previous directory level"
 msgid "Go to previous object"
 msgstr "J??t na p??edchoz?? ??rove?? adres????e"
 
-#: engines/twp/metaengine.cpp:157
+#: engines/twp/metaengine.cpp:156
 #, fuzzy
 msgid "Go to next object"
 msgstr "Volby"
 
-#: engines/twp/metaengine.cpp:158
+#: engines/twp/metaengine.cpp:157
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Select verb / item left"
 msgstr "P??esunout se Doprava"
 
-#: engines/twp/metaengine.cpp:159
+#: engines/twp/metaengine.cpp:158
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Select verb / item right"
 msgstr "P??esunout se Doprava"
 
-#: engines/twp/metaengine.cpp:160
+#: engines/twp/metaengine.cpp:159
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Select verb / item up"
 msgstr "P??esunout se Doprava"
 
-#: engines/twp/metaengine.cpp:161
+#: engines/twp/metaengine.cpp:160
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Select verb / item down"
 msgstr "P??esunout se Doprava"
 
-#: engines/twp/metaengine.cpp:163
+#: engines/twp/metaengine.cpp:162
 #, fuzzy
 msgid "Select actor 1"
 msgstr "??innost/Vybrat"
 
-#: engines/twp/metaengine.cpp:164
+#: engines/twp/metaengine.cpp:163
 #, fuzzy
 msgid "Select actor 2"
 msgstr "??innost/Vybrat"
 
-#: engines/twp/metaengine.cpp:165
+#: engines/twp/metaengine.cpp:164
 #, fuzzy
 msgid "Select actor 3"
 msgstr "??innost/Vybrat"
 
-#: engines/twp/metaengine.cpp:166
+#: engines/twp/metaengine.cpp:165
 #, fuzzy
 msgid "Select actor 4"
 msgstr "??innost/Vybrat"
 
-#: engines/twp/metaengine.cpp:167
+#: engines/twp/metaengine.cpp:166
 #, fuzzy
 msgid "Select actor 5"
 msgstr "??innost/Vybrat"
 
-#: engines/twp/metaengine.cpp:168
+#: engines/twp/metaengine.cpp:167
 #, fuzzy
 msgid "Select choice 1"
 msgstr "Vyberte Vzhled"
 
-#: engines/twp/metaengine.cpp:169
+#: engines/twp/metaengine.cpp:168
 #, fuzzy
 msgid "Select choice 2"
 msgstr "Vyberte Vzhled"
 
-#: engines/twp/metaengine.cpp:170
+#: engines/twp/metaengine.cpp:169
 #, fuzzy
 msgid "Select choice 3"
 msgstr "Vyberte Vzhled"
 
-#: engines/twp/metaengine.cpp:171
+#: engines/twp/metaengine.cpp:170
 #, fuzzy
 msgid "Select choice 4"
 msgstr "Vyberte Vzhled"
 
-#: engines/twp/metaengine.cpp:172
+#: engines/twp/metaengine.cpp:171
 #, fuzzy
 msgid "Select choice 5"
 msgstr "Vyberte Vzhled"
 
-#: engines/twp/metaengine.cpp:173
+#: engines/twp/metaengine.cpp:172
 #, fuzzy
 msgid "Select choice 6"
 msgstr "Vyberte Vzhled"
 
-#: engines/twp/metaengine.cpp:174
+#: engines/twp/metaengine.cpp:173
 #, fuzzy
 msgid "Select next Actor"
 msgstr "Vyberte Vzhled"
 
-#: engines/twp/metaengine.cpp:175
+#: engines/twp/metaengine.cpp:174
 #, fuzzy
 msgid "Select previous actor"
 msgstr "~P~??edchoz??"
 
-#: engines/twp/metaengine.cpp:177
+#: engines/twp/metaengine.cpp:176
 #, fuzzy
 msgid "Show hotspots"
 msgstr "Zobrazit skryt?? soubory"
diff --git a/po/da.po b/po/da.po
index cc202c52036..42dbb04d6c8 100644
--- a/po/da.po
+++ b/po/da.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-13 13:52+0000\n"
+"POT-Creation-Date: 2026-06-14 18:41+0000\n"
 "PO-Revision-Date: 2023-09-07 17:04+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Danish <https://translations.scummvm.org/projects/scummvm/"
@@ -79,7 +79,7 @@ msgstr "Tilg??ngelige motorer:"
 #: engines/scumm/help.cpp:141 engines/scumm/help.cpp:166
 #: engines/scumm/help.cpp:194 engines/scumm/help.cpp:212
 #: engines/sherlock/metaengine.cpp:430 engines/supernova/metaengine.cpp:341
-#: engines/tot/metaengine.cpp:149 engines/twp/metaengine.cpp:179
+#: engines/tot/metaengine.cpp:149 engines/twp/metaengine.cpp:178
 msgid "Close"
 msgstr "Luk"
 
@@ -1079,7 +1079,7 @@ msgstr "interagere"
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
 #: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1124
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
-#: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
+#: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:153
 #: engines/ultima/ultima0/metaengine.cpp:45
 #: engines/ultima/ultima4/metaengine.cpp:44
 #: engines/ultima/ultima4/metaengine.cpp:139
@@ -1100,7 +1100,7 @@ msgstr "Op"
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
 #: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1091
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
-#: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
+#: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:45
 #: engines/ultima/ultima4/metaengine.cpp:140
@@ -1121,7 +1121,7 @@ msgstr "Ned"
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
 #: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1104
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
-#: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
+#: engines/twp/metaengine.cpp:151 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
 #: engines/ultima/ultima4/metaengine.cpp:151
 #: engines/wintermute/keymapper_tables.h:320
@@ -1141,7 +1141,7 @@ msgstr "Venstre"
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
 #: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1111
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
-#: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
+#: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
 #: engines/ultima/ultima4/metaengine.cpp:152
 #: engines/wintermute/keymapper_tables.h:326
@@ -3312,7 +3312,7 @@ msgstr "<Forkert v??rdi>"
 
 #. I18N: Abbreviation for 'bytes' as data size
 #. I18N: Hardware key
-#: common/util.cpp:183 backends/platform/3ds/osystem-events.cpp:46
+#: common/util.cpp:190 backends/platform/3ds/osystem-events.cpp:46
 #: backends/platform/ds/osystem_ds.cpp:178
 #: backends/platform/sdl/miyoo/miyoo.cpp:61
 #: backends/platform/sdl/miyoo/miyoo.cpp:88
@@ -3322,23 +3322,23 @@ msgid "B"
 msgstr ""
 
 #. I18N: Abbreviation for 'kilobytes' as data size
-#: common/util.cpp:189
+#: common/util.cpp:196
 msgid "KB"
 msgstr ""
 
 #. I18N: Abbreviation for 'megabytes' as data size
-#: common/util.cpp:194
+#: common/util.cpp:201
 #, fuzzy
 msgid "MB"
 msgstr "SMB"
 
 #. I18N: Abbreviation for 'gigabytes' as data size
-#: common/util.cpp:200
+#: common/util.cpp:207
 msgid "GB"
 msgstr ""
 
 #. I18N: Abbreviation for 'terabytes' as data size
-#: common/util.cpp:206
+#: common/util.cpp:213
 msgid "TB"
 msgstr ""
 
@@ -6201,7 +6201,7 @@ msgstr "Se"
 #: engines/scumm/help.cpp:165 engines/scumm/help.cpp:193
 #: engines/scumm/help.cpp:211 engines/sherlock/metaengine.cpp:425
 #: engines/supernova/metaengine.cpp:336 engines/tot/metaengine.cpp:144
-#: engines/twp/metaengine.cpp:178
+#: engines/twp/metaengine.cpp:177
 msgid "Open"
 msgstr "??bn"
 
@@ -6223,7 +6223,7 @@ msgstr "Tag"
 #: engines/sherlock/metaengine.cpp:440 engines/sherlock/metaengine.cpp:561
 #: engines/sky/metaengine.cpp:95 engines/sludge/keymapper_tables.h:333
 #: engines/supernova/metaengine.cpp:356 engines/tot/metaengine.cpp:139
-#: engines/tsage/metaengine.cpp:211 engines/twp/metaengine.cpp:186
+#: engines/tsage/metaengine.cpp:211 engines/twp/metaengine.cpp:185
 #: engines/ultima/ultima4/metaengine.cpp:74
 #: engines/ultima/nuvie/metaengine.cpp:69
 #: engines/wintermute/keymapper_tables.h:140
@@ -7005,7 +7005,7 @@ msgstr "Aktiv"
 
 #: engines/alcachofa/metaengine.cpp:104 engines/queen/metaengine.cpp:279
 #: engines/saga/metaengine.cpp:354 engines/scumm/help.cpp:202
-#: engines/scumm/help.cpp:214 engines/twp/metaengine.cpp:182
+#: engines/scumm/help.cpp:214 engines/twp/metaengine.cpp:181
 #: engines/wintermute/keymapper_tables.h:122
 #: engines/wintermute/keymapper_tables.h:1534
 msgid "Look at"
@@ -7377,7 +7377,7 @@ msgstr "Museindfangning til/fra"
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:210
 #: engines/phoenixvr/metaengine.cpp:94 engines/scumm/help.cpp:79
 #: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
-#: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
+#: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:161
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
 msgstr "Spring mellemscene over"
@@ -8290,7 +8290,7 @@ msgstr ""
 #. I18N: Perform action on object where cursor points
 #: engines/darkseed/metaengine.cpp:42 engines/dragons/metaengine.cpp:135
 #: engines/phoenixvr/metaengine.cpp:75 engines/tinsel/metaengine.cpp:294
-#: engines/twp/metaengine.cpp:150
+#: engines/twp/metaengine.cpp:149
 #, fuzzy
 msgid "Action"
 msgstr "Aktiv"
@@ -8787,7 +8787,7 @@ msgstr ""
 #: engines/scumm/help.cpp:139 engines/scumm/help.cpp:164
 #: engines/scumm/help.cpp:200 engines/scumm/help.cpp:210
 #: engines/sherlock/metaengine.cpp:445 engines/sherlock/metaengine.cpp:566
-#: engines/supernova/metaengine.cpp:366 engines/twp/metaengine.cpp:180
+#: engines/supernova/metaengine.cpp:366 engines/twp/metaengine.cpp:179
 msgid "Give"
 msgstr "Giv"
 
@@ -12390,7 +12390,7 @@ msgstr ""
 msgid "Move / Skip / Use default action with inventory item"
 msgstr ""
 
-#: engines/queen/metaengine.cpp:198 engines/twp/metaengine.cpp:176
+#: engines/queen/metaengine.cpp:198 engines/twp/metaengine.cpp:175
 #, fuzzy
 #| msgid "Skip text"
 msgid "Skip text"
@@ -12447,7 +12447,7 @@ msgstr "Vis journal"
 #: engines/scumm/help.cpp:144 engines/scumm/help.cpp:169
 #: engines/scumm/help.cpp:198 engines/scumm/help.cpp:213
 #: engines/scumm/help.cpp:230 engines/sherlock/metaengine.cpp:420
-#: engines/sludge/keymapper_tables.h:318 engines/twp/metaengine.cpp:181
+#: engines/sludge/keymapper_tables.h:318 engines/twp/metaengine.cpp:180
 #: engines/wintermute/keymapper_tables.h:134
 #: engines/wintermute/keymapper_tables.h:1546
 msgid "Pick up"
@@ -12456,7 +12456,7 @@ msgstr "Tag op"
 #: engines/queen/metaengine.cpp:289 engines/saga/metaengine.cpp:364
 #: engines/scumm/help.cpp:199 engines/scumm/help.cpp:215
 #: engines/scumm/help.cpp:251 engines/sludge/keymapper_tables.h:323
-#: engines/twp/metaengine.cpp:183 engines/wintermute/keymapper_tables.h:128
+#: engines/twp/metaengine.cpp:182 engines/wintermute/keymapper_tables.h:128
 #: engines/wintermute/keymapper_tables.h:1558
 msgid "Talk to"
 msgstr "Tal til"
@@ -12567,7 +12567,7 @@ msgstr "G?? til"
 
 #: engines/saga/metaengine.cpp:389 engines/scumm/help.cpp:122
 #: engines/scumm/help.cpp:137 engines/scumm/help.cpp:162
-#: engines/supernova/metaengine.cpp:346 engines/twp/metaengine.cpp:184
+#: engines/supernova/metaengine.cpp:346 engines/twp/metaengine.cpp:183
 msgid "Push"
 msgstr "Skub"
 
@@ -13398,7 +13398,7 @@ msgstr "Vigtigste spilstyring:"
 
 #: engines/scumm/help.cpp:123 engines/scumm/help.cpp:138
 #: engines/scumm/help.cpp:163 engines/supernova/metaengine.cpp:351
-#: engines/twp/metaengine.cpp:185
+#: engines/twp/metaengine.cpp:184
 msgid "Pull"
 msgstr "Tr??k"
 
@@ -15823,112 +15823,112 @@ msgstr ""
 msgid "Ransome *unbeeped* (DLC)"
 msgstr ""
 
-#: engines/twp/metaengine.cpp:151
+#: engines/twp/metaengine.cpp:150
 #, fuzzy
 msgid "Select highlighted verb"
 msgstr "V??lg et tema"
 
-#: engines/twp/metaengine.cpp:156
+#: engines/twp/metaengine.cpp:155
 #, fuzzy
 #| msgid "Go to previous directory level"
 msgid "Go to previous object"
 msgstr "G?? til forrige mappeniveau"
 
-#: engines/twp/metaengine.cpp:157
+#: engines/twp/metaengine.cpp:156
 #, fuzzy
 msgid "Go to next object"
 msgstr "Indstillinger"
 
-#: engines/twp/metaengine.cpp:158
+#: engines/twp/metaengine.cpp:157
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Select verb / item left"
 msgstr "Flyt til h??jre"
 
-#: engines/twp/metaengine.cpp:159
+#: engines/twp/metaengine.cpp:158
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Select verb / item right"
 msgstr "Flyt til h??jre"
 
-#: engines/twp/metaengine.cpp:160
+#: engines/twp/metaengine.cpp:159
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Select verb / item up"
 msgstr "Flyt til h??jre"
 
-#: engines/twp/metaengine.cpp:161
+#: engines/twp/metaengine.cpp:160
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Select verb / item down"
 msgstr "Flyt til h??jre"
 
-#: engines/twp/metaengine.cpp:163
+#: engines/twp/metaengine.cpp:162
 #, fuzzy
 msgid "Select actor 1"
 msgstr "Handling/v??lg"
 
-#: engines/twp/metaengine.cpp:164
+#: engines/twp/metaengine.cpp:163
 #, fuzzy
 msgid "Select actor 2"
 msgstr "Handling/v??lg"
 
-#: engines/twp/metaengine.cpp:165
+#: engines/twp/metaengine.cpp:164
 #, fuzzy
 msgid "Select actor 3"
 msgstr "Handling/v??lg"
 
-#: engines/twp/metaengine.cpp:166
+#: engines/twp/metaengine.cpp:165
 #, fuzzy
 msgid "Select actor 4"
 msgstr "Handling/v??lg"
 
-#: engines/twp/metaengine.cpp:167
+#: engines/twp/metaengine.cpp:166
 #, fuzzy
 msgid "Select actor 5"
 msgstr "Handling/v??lg"
 
-#: engines/twp/metaengine.cpp:168
+#: engines/twp/metaengine.cpp:167
 #, fuzzy
 msgid "Select choice 1"
 msgstr "V??lg et tema"
 
-#: engines/twp/metaengine.cpp:169
+#: engines/twp/metaengine.cpp:168
 #, fuzzy
 msgid "Select choice 2"
 msgstr "V??lg et tema"
 
-#: engines/twp/metaengine.cpp:170
+#: engines/twp/metaengine.cpp:169
 #, fuzzy
 msgid "Select choice 3"
 msgstr "V??lg et tema"
 
-#: engines/twp/metaengine.cpp:171
+#: engines/twp/metaengine.cpp:170
 #, fuzzy
 msgid "Select choice 4"
 msgstr "V??lg et tema"
 
-#: engines/twp/metaengine.cpp:172
+#: engines/twp/metaengine.cpp:171
 #, fuzzy
 msgid "Select choice 5"
 msgstr "V??lg et tema"
 
-#: engines/twp/metaengine.cpp:173
+#: engines/twp/metaengine.cpp:172
 #, fuzzy
 msgid "Select choice 6"
 msgstr "V??lg et tema"
 
-#: engines/twp/metaengine.cpp:174
+#: engines/twp/metaengine.cpp:173
 #, fuzzy
 msgid "Select next Actor"
 msgstr "V??lg et tema"
 
-#: engines/twp/metaengine.cpp:175
+#: engines/twp/metaengine.cpp:174
 #, fuzzy
 msgid "Select previous actor"
 msgstr "Fo~r~rige"
 
-#: engines/twp/metaengine.cpp:177
+#: engines/twp/metaengine.cpp:176
 #, fuzzy
 msgid "Show hotspots"
 msgstr "Vis skjulte filer"
diff --git a/po/de_DE.po b/po/de_DE.po
index 0255134aa89..2b7e710e576 100644
--- a/po/de_DE.po
+++ b/po/de_DE.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.10.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-13 13:52+0000\n"
+"POT-Creation-Date: 2026-06-14 18:41+0000\n"
 "PO-Revision-Date: 2026-05-05 16:31+0000\n"
 "Last-Translator: Lothar Serra Mari <lothar.serramari at scummvm.org>\n"
 "Language-Team: German <https://translations.scummvm.org/projects/scummvm/"
@@ -72,7 +72,7 @@ msgstr "Verf??gbare Spiele-Engines:"
 #: engines/scumm/help.cpp:141 engines/scumm/help.cpp:166
 #: engines/scumm/help.cpp:194 engines/scumm/help.cpp:212
 #: engines/sherlock/metaengine.cpp:430 engines/supernova/metaengine.cpp:341
-#: engines/tot/metaengine.cpp:149 engines/twp/metaengine.cpp:179
+#: engines/tot/metaengine.cpp:149 engines/twp/metaengine.cpp:178
 msgid "Close"
 msgstr "Schlie??en"
 
@@ -1066,7 +1066,7 @@ msgstr "Interagieren"
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
 #: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1124
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
-#: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
+#: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:153
 #: engines/ultima/ultima0/metaengine.cpp:45
 #: engines/ultima/ultima4/metaengine.cpp:44
 #: engines/ultima/ultima4/metaengine.cpp:139
@@ -1087,7 +1087,7 @@ msgstr "Hoch"
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
 #: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1091
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
-#: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
+#: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:45
 #: engines/ultima/ultima4/metaengine.cpp:140
@@ -1108,7 +1108,7 @@ msgstr "Runter"
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
 #: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1104
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
-#: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
+#: engines/twp/metaengine.cpp:151 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
 #: engines/ultima/ultima4/metaengine.cpp:151
 #: engines/wintermute/keymapper_tables.h:320
@@ -1128,7 +1128,7 @@ msgstr "Links"
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
 #: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1111
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
-#: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
+#: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
 #: engines/ultima/ultima4/metaengine.cpp:152
 #: engines/wintermute/keymapper_tables.h:326
@@ -3371,7 +3371,7 @@ msgstr "<Fehlerhafter Wert>"
 
 #. I18N: Abbreviation for 'bytes' as data size
 #. I18N: Hardware key
-#: common/util.cpp:183 backends/platform/3ds/osystem-events.cpp:46
+#: common/util.cpp:190 backends/platform/3ds/osystem-events.cpp:46
 #: backends/platform/ds/osystem_ds.cpp:178
 #: backends/platform/sdl/miyoo/miyoo.cpp:61
 #: backends/platform/sdl/miyoo/miyoo.cpp:88
@@ -3381,22 +3381,22 @@ msgid "B"
 msgstr "B"
 
 #. I18N: Abbreviation for 'kilobytes' as data size
-#: common/util.cpp:189
+#: common/util.cpp:196
 msgid "KB"
 msgstr "KB"
 
 #. I18N: Abbreviation for 'megabytes' as data size
-#: common/util.cpp:194
+#: common/util.cpp:201
 msgid "MB"
 msgstr "MB"
 
 #. I18N: Abbreviation for 'gigabytes' as data size
-#: common/util.cpp:200
+#: common/util.cpp:207
 msgid "GB"
 msgstr "GB"
 
 #. I18N: Abbreviation for 'terabytes' as data size
-#: common/util.cpp:206
+#: common/util.cpp:213
 msgid "TB"
 msgstr "TB"
 
@@ -6472,7 +6472,7 @@ msgstr "Schau"
 #: engines/scumm/help.cpp:165 engines/scumm/help.cpp:193
 #: engines/scumm/help.cpp:211 engines/sherlock/metaengine.cpp:425
 #: engines/supernova/metaengine.cpp:336 engines/tot/metaengine.cpp:144
-#: engines/twp/metaengine.cpp:178
+#: engines/twp/metaengine.cpp:177
 msgid "Open"
 msgstr "??ffne"
 
@@ -6494,7 +6494,7 @@ msgstr "Nimm"
 #: engines/sherlock/metaengine.cpp:440 engines/sherlock/metaengine.cpp:561
 #: engines/sky/metaengine.cpp:95 engines/sludge/keymapper_tables.h:333
 #: engines/supernova/metaengine.cpp:356 engines/tot/metaengine.cpp:139
-#: engines/tsage/metaengine.cpp:211 engines/twp/metaengine.cpp:186
+#: engines/tsage/metaengine.cpp:211 engines/twp/metaengine.cpp:185
 #: engines/ultima/ultima4/metaengine.cpp:74
 #: engines/ultima/nuvie/metaengine.cpp:69
 #: engines/wintermute/keymapper_tables.h:140
@@ -7284,7 +7284,7 @@ msgstr "Aktivieren"
 
 #: engines/alcachofa/metaengine.cpp:104 engines/queen/metaengine.cpp:279
 #: engines/saga/metaengine.cpp:354 engines/scumm/help.cpp:202
-#: engines/scumm/help.cpp:214 engines/twp/metaengine.cpp:182
+#: engines/scumm/help.cpp:214 engines/twp/metaengine.cpp:181
 #: engines/wintermute/keymapper_tables.h:122
 #: engines/wintermute/keymapper_tables.h:1534
 msgid "Look at"
@@ -7645,7 +7645,7 @@ msgstr "Kampf umschalten"
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:210
 #: engines/phoenixvr/metaengine.cpp:94 engines/scumm/help.cpp:79
 #: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
-#: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
+#: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:161
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
 msgstr "Zwischensequenz ??berspringen"
@@ -8467,7 +8467,7 @@ msgstr "Nur SFX-Version f??r Disketten"
 #. I18N: Perform action on object where cursor points
 #: engines/darkseed/metaengine.cpp:42 engines/dragons/metaengine.cpp:135
 #: engines/phoenixvr/metaengine.cpp:75 engines/tinsel/metaengine.cpp:294
-#: engines/twp/metaengine.cpp:150
+#: engines/twp/metaengine.cpp:149
 msgid "Action"
 msgstr "Aktion"
 
@@ -8889,7 +8889,7 @@ msgstr "Ausr??sten"
 #: engines/scumm/help.cpp:139 engines/scumm/help.cpp:164
 #: engines/scumm/help.cpp:200 engines/scumm/help.cpp:210
 #: engines/sherlock/metaengine.cpp:445 engines/sherlock/metaengine.cpp:566
-#: engines/supernova/metaengine.cpp:366 engines/twp/metaengine.cpp:180
+#: engines/supernova/metaengine.cpp:366 engines/twp/metaengine.cpp:179
 msgid "Give"
 msgstr "Gib"
 
@@ -12272,7 +12272,7 @@ msgid "Move / Skip / Use default action with inventory item"
 msgstr ""
 "Verschieben / ??berspringen / Standardaktion mit Inventargegenstand verwenden"
 
-#: engines/queen/metaengine.cpp:198 engines/twp/metaengine.cpp:176
+#: engines/queen/metaengine.cpp:198 engines/twp/metaengine.cpp:175
 msgid "Skip text"
 msgstr "Text ??berspringen"
 
@@ -12312,7 +12312,7 @@ msgstr "Verwendungsprotokoll"
 #: engines/scumm/help.cpp:144 engines/scumm/help.cpp:169
 #: engines/scumm/help.cpp:198 engines/scumm/help.cpp:213
 #: engines/scumm/help.cpp:230 engines/sherlock/metaengine.cpp:420
-#: engines/sludge/keymapper_tables.h:318 engines/twp/metaengine.cpp:181
+#: engines/sludge/keymapper_tables.h:318 engines/twp/metaengine.cpp:180
 #: engines/wintermute/keymapper_tables.h:134
 #: engines/wintermute/keymapper_tables.h:1546
 msgid "Pick up"
@@ -12321,7 +12321,7 @@ msgstr "Nimm"
 #: engines/queen/metaengine.cpp:289 engines/saga/metaengine.cpp:364
 #: engines/scumm/help.cpp:199 engines/scumm/help.cpp:215
 #: engines/scumm/help.cpp:251 engines/sludge/keymapper_tables.h:323
-#: engines/twp/metaengine.cpp:183 engines/wintermute/keymapper_tables.h:128
+#: engines/twp/metaengine.cpp:182 engines/wintermute/keymapper_tables.h:128
 #: engines/wintermute/keymapper_tables.h:1558
 msgid "Talk to"
 msgstr "Rede mit"
@@ -12416,7 +12416,7 @@ msgstr "Gehe zu"
 
 #: engines/saga/metaengine.cpp:389 engines/scumm/help.cpp:122
 #: engines/scumm/help.cpp:137 engines/scumm/help.cpp:162
-#: engines/supernova/metaengine.cpp:346 engines/twp/metaengine.cpp:184
+#: engines/supernova/metaengine.cpp:346 engines/twp/metaengine.cpp:183
 msgid "Push"
 msgstr "Dr??cke"
 
@@ -13294,7 +13294,7 @@ msgstr "Hauptspielsteuerung:"
 
 #: engines/scumm/help.cpp:123 engines/scumm/help.cpp:138
 #: engines/scumm/help.cpp:163 engines/supernova/metaengine.cpp:351
-#: engines/twp/metaengine.cpp:185
+#: engines/twp/metaengine.cpp:184
 msgid "Pull"
 msgstr "Ziehe"
 
@@ -15580,87 +15580,87 @@ msgstr "Klassische S??tze"
 msgid "Ransome *unbeeped* (DLC)"
 msgstr "Ransome *unbeeped* (DLC)"
 
-#: engines/twp/metaengine.cpp:151
+#: engines/twp/metaengine.cpp:150
 msgid "Select highlighted verb"
 msgstr "Hervorgehobenes Verb ausw??hlen"
 
-#: engines/twp/metaengine.cpp:156
+#: engines/twp/metaengine.cpp:155
 msgid "Go to previous object"
 msgstr "Gehe zum vorherigen Objekt"
 
-#: engines/twp/metaengine.cpp:157
+#: engines/twp/metaengine.cpp:156
 msgid "Go to next object"
 msgstr "Gehe zum n??chsten Objekt"
 
-#: engines/twp/metaengine.cpp:158
+#: engines/twp/metaengine.cpp:157
 msgid "Select verb / item left"
 msgstr "Verb/Element links ausw??hlen"
 
-#: engines/twp/metaengine.cpp:159
+#: engines/twp/metaengine.cpp:158
 msgid "Select verb / item right"
 msgstr "Verb/Element rechts ausw??hlen"
 
-#: engines/twp/metaengine.cpp:160
+#: engines/twp/metaengine.cpp:159
 msgid "Select verb / item up"
 msgstr "Verb/Element ausw??hlen"
 
-#: engines/twp/metaengine.cpp:161
+#: engines/twp/metaengine.cpp:160
 msgid "Select verb / item down"
 msgstr "Verb/Element ausw??hlen"
 
-#: engines/twp/metaengine.cpp:163
+#: engines/twp/metaengine.cpp:162
 msgid "Select actor 1"
 msgstr "Schauspieler 1 ausw??hlen"
 
-#: engines/twp/metaengine.cpp:164
+#: engines/twp/metaengine.cpp:163
 msgid "Select actor 2"
 msgstr "Schauspieler 2 ausw??hlen"
 
-#: engines/twp/metaengine.cpp:165
+#: engines/twp/metaengine.cpp:164
 msgid "Select actor 3"
 msgstr "Schauspieler 3 ausw??hlen"
 
-#: engines/twp/metaengine.cpp:166
+#: engines/twp/metaengine.cpp:165
 msgid "Select actor 4"
 msgstr "Schauspieler 4 ausw??hlen"
 
-#: engines/twp/metaengine.cpp:167
+#: engines/twp/metaengine.cpp:166
 msgid "Select actor 5"
 msgstr "Schauspieler 5 ausw??hlen"
 
-#: engines/twp/metaengine.cpp:168
+#: engines/twp/metaengine.cpp:167
 msgid "Select choice 1"
 msgstr "W??hlen Sie Option 1"
 
-#: engines/twp/metaengine.cpp:169
+#: engines/twp/metaengine.cpp:168
 msgid "Select choice 2"
 msgstr "W??hlen Sie Option 2"
 
-#: engines/twp/metaengine.cpp:170
+#: engines/twp/metaengine.cpp:169
 msgid "Select choice 3"
 msgstr "W??hlen Sie Option 3"
 
-#: engines/twp/metaengine.cpp:171
+#: engines/twp/metaengine.cpp:170
 msgid "Select choice 4"
 msgstr "W??hlen Sie Option 4"
 
-#: engines/twp/metaengine.cpp:172
+#: engines/twp/metaengine.cpp:171
 msgid "Select choice 5"
 msgstr "W??hlen Sie Option 5"
 
-#: engines/twp/metaengine.cpp:173
+#: engines/twp/metaengine.cpp:172
 msgid "Select choice 6"
 msgstr "W??hlen Sie Option 6"
 
-#: engines/twp/metaengine.cpp:174
+#: engines/twp/metaengine.cpp:173
 msgid "Select next Actor"
 msgstr "N??chsten Akteur ausw??hlen"
 
-#: engines/twp/metaengine.cpp:175
+#: engines/twp/metaengine.cpp:174
 msgid "Select previous actor"
 msgstr "Vorherigen Darsteller ausw??hlen"
 
-#: engines/twp/metaengine.cpp:177
+#: engines/twp/metaengine.cpp:176
 msgid "Show hotspots"
 msgstr "Hotspots anzeigen"
 
diff --git a/po/el.po b/po/el.po
index 5957434ace0..64bb52ffc0b 100644
--- a/po/el.po
+++ b/po/el.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.10.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-13 13:52+0000\n"
+"POT-Creation-Date: 2026-06-14 18:41+0000\n"
 "PO-Revision-Date: 2026-04-23 00:22+0000\n"
 "Last-Translator: Antoniou Athanasios <a.antoniou79 at gmail.com>\n"
 "Language-Team: Greek <https://translations.scummvm.org/projects/scummvm/"
@@ -71,7 +71,7 @@ msgstr "???????????????????? ??????????????:"
 #: engines/scumm/help.cpp:141 engines/scumm/help.cpp:166
 #: engines/scumm/help.cpp:194 engines/scumm/help.cpp:212
 #: engines/sherlock/metaengine.cpp:430 engines/supernova/metaengine.cpp:341
-#: engines/tot/metaengine.cpp:149 engines/twp/metaengine.cpp:179
+#: engines/tot/metaengine.cpp:149 engines/twp/metaengine.cpp:178
 msgid "Close"
 msgstr "????????????????"
 
@@ -1069,7 +1069,7 @@ msgstr "??????????????????????????"
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
 #: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1124
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
-#: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
+#: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:153
 #: engines/ultima/ultima0/metaengine.cpp:45
 #: engines/ultima/ultima4/metaengine.cpp:44
 #: engines/ultima/ultima4/metaengine.cpp:139
@@ -1090,7 +1090,7 @@ msgstr "????????"
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
 #: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1091
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
-#: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
+#: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:45
 #: engines/ultima/ultima4/metaengine.cpp:140
@@ -1111,7 +1111,7 @@ msgstr "????????"
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
 #: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1104
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
-#: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
+#: engines/twp/metaengine.cpp:151 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
 #: engines/ultima/ultima4/metaengine.cpp:151
 #: engines/wintermute/keymapper_tables.h:320
@@ -1131,7 +1131,7 @@ msgstr "????????????????"
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
 #: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1111
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
-#: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
+#: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
 #: engines/ultima/ultima4/metaengine.cpp:152
 #: engines/wintermute/keymapper_tables.h:326
@@ -3426,7 +3426,7 @@ msgstr "<?????????????????? ????????>"
 
 #. I18N: Abbreviation for 'bytes' as data size
 #. I18N: Hardware key
-#: common/util.cpp:183 backends/platform/3ds/osystem-events.cpp:46
+#: common/util.cpp:190 backends/platform/3ds/osystem-events.cpp:46
 #: backends/platform/ds/osystem_ds.cpp:178
 #: backends/platform/sdl/miyoo/miyoo.cpp:61
 #: backends/platform/sdl/miyoo/miyoo.cpp:88
@@ -3436,22 +3436,22 @@ msgid "B"
 msgstr "B"
 
 #. I18N: Abbreviation for 'kilobytes' as data size
-#: common/util.cpp:189
+#: common/util.cpp:196
 msgid "KB"
 msgstr "KB"
 
 #. I18N: Abbreviation for 'megabytes' as data size
-#: common/util.cpp:194
+#: common/util.cpp:201
 msgid "MB"
 msgstr "MB"
 
 #. I18N: Abbreviation for 'gigabytes' as data size
-#: common/util.cpp:200
+#: common/util.cpp:207
 msgid "GB"
 msgstr "GB"
 
 #. I18N: Abbreviation for 'terabytes' as data size
-#: common/util.cpp:206
+#: common/util.cpp:213
 msgid "TB"
 msgstr "TB"
 
@@ -6596,7 +6596,7 @@ msgstr "??????????????"
 #: engines/scumm/help.cpp:165 engines/scumm/help.cpp:193
 #: engines/scumm/help.cpp:211 engines/sherlock/metaengine.cpp:425
 #: engines/supernova/metaengine.cpp:336 engines/tot/metaengine.cpp:144
-#: engines/twp/metaengine.cpp:178
+#: engines/twp/metaengine.cpp:177
 msgid "Open"
 msgstr "????????????"
 
@@ -6618,7 +6618,7 @@ msgstr "????????"
 #: engines/sherlock/metaengine.cpp:440 engines/sherlock/metaengine.cpp:561
 #: engines/sky/metaengine.cpp:95 engines/sludge/keymapper_tables.h:333
 #: engines/supernova/metaengine.cpp:356 engines/tot/metaengine.cpp:139
-#: engines/tsage/metaengine.cpp:211 engines/twp/metaengine.cpp:186
+#: engines/tsage/metaengine.cpp:211 engines/twp/metaengine.cpp:185
 #: engines/ultima/ultima4/metaengine.cpp:74
 #: engines/ultima/nuvie/metaengine.cpp:69
 #: engines/wintermute/keymapper_tables.h:140
@@ -7411,7 +7411,7 @@ msgstr "????????????????????????"
 
 #: engines/alcachofa/metaengine.cpp:104 engines/queen/metaengine.cpp:279
 #: engines/saga/metaengine.cpp:354 engines/scumm/help.cpp:202
-#: engines/scumm/help.cpp:214 engines/twp/metaengine.cpp:182
+#: engines/scumm/help.cpp:214 engines/twp/metaengine.cpp:181
 #: engines/wintermute/keymapper_tables.h:122
 #: engines/wintermute/keymapper_tables.h:1534
 msgid "Look at"
@@ -7773,7 +7773,7 @@ msgstr "???????????????? ?????????????????????? ?????? ????????"
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:210
 #: engines/phoenixvr/metaengine.cpp:94 engines/scumm/help.cpp:79
 #: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
-#: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
+#: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:161
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
 msgstr "?????????????????? ??????????????"
@@ -8603,7 +8603,7 @@ msgstr "?????? ???????? ?????? ???????????? ?????? ????????????????"
 #. I18N: Perform action on object where cursor points
 #: engines/darkseed/metaengine.cpp:42 engines/dragons/metaengine.cpp:135
 #: engines/phoenixvr/metaengine.cpp:75 engines/tinsel/metaengine.cpp:294
-#: engines/twp/metaengine.cpp:150
+#: engines/twp/metaengine.cpp:149
 msgid "Action"
 msgstr "??????????"
 
@@ -9023,7 +9023,7 @@ msgstr "????????????????????"
 #: engines/scumm/help.cpp:139 engines/scumm/help.cpp:164
 #: engines/scumm/help.cpp:200 engines/scumm/help.cpp:210
 #: engines/sherlock/metaengine.cpp:445 engines/sherlock/metaengine.cpp:566
-#: engines/supernova/metaengine.cpp:366 engines/twp/metaengine.cpp:180
+#: engines/supernova/metaengine.cpp:366 engines/twp/metaengine.cpp:179
 msgid "Give"
 msgstr "????????"
 
@@ -12404,7 +12404,7 @@ msgstr "?????????? ???????? ?????? ????????????????????????, ??????????????????
 msgid "Move / Skip / Use default action with inventory item"
 msgstr "???????????? / ?????????????????? / ?????????????????????????? ???????????????? ???? ?????????????????????? ????????????????????"
 
-#: engines/queen/metaengine.cpp:198 engines/twp/metaengine.cpp:176
+#: engines/queen/metaengine.cpp:198 engines/twp/metaengine.cpp:175
 msgid "Skip text"
 msgstr "?????????????????? ????????????????"
 
@@ -12444,7 +12444,7 @@ msgstr "?????????? ??????????????????????"
 #: engines/scumm/help.cpp:144 engines/scumm/help.cpp:169
 #: engines/scumm/help.cpp:198 engines/scumm/help.cpp:213
 #: engines/scumm/help.cpp:230 engines/sherlock/metaengine.cpp:420
-#: engines/sludge/keymapper_tables.h:318 engines/twp/metaengine.cpp:181
+#: engines/sludge/keymapper_tables.h:318 engines/twp/metaengine.cpp:180
 #: engines/wintermute/keymapper_tables.h:134
 #: engines/wintermute/keymapper_tables.h:1546
 msgid "Pick up"
@@ -12453,7 +12453,7 @@ msgstr "????????????"
 #: engines/queen/metaengine.cpp:289 engines/saga/metaengine.cpp:364
 #: engines/scumm/help.cpp:199 engines/scumm/help.cpp:215
 #: engines/scumm/help.cpp:251 engines/sludge/keymapper_tables.h:323
-#: engines/twp/metaengine.cpp:183 engines/wintermute/keymapper_tables.h:128
+#: engines/twp/metaengine.cpp:182 engines/wintermute/keymapper_tables.h:128
 #: engines/wintermute/keymapper_tables.h:1558
 msgid "Talk to"
 msgstr "???????? ????"
@@ -12551,7 +12551,7 @@ msgstr "?????????????????? ????"
 
 #: engines/saga/metaengine.cpp:389 engines/scumm/help.cpp:122
 #: engines/scumm/help.cpp:137 engines/scumm/help.cpp:162
-#: engines/supernova/metaengine.cpp:346 engines/twp/metaengine.cpp:184
+#: engines/supernova/metaengine.cpp:346 engines/twp/metaengine.cpp:183
 msgid "Push"
 msgstr "????????????"
 
@@ -13443,7 +13443,7 @@ msgstr "???????????? ?????????????????? ????????????????????:"
 
 #: engines/scumm/help.cpp:123 engines/scumm/help.cpp:138
 #: engines/scumm/help.cpp:163 engines/supernova/metaengine.cpp:351
-#: engines/twp/metaengine.cpp:185
+#: engines/twp/metaengine.cpp:184
 msgid "Pull"
 msgstr "??????????????"
 
@@ -15769,87 +15769,87 @@ msgstr "???????????????? ??????????????"
 msgid "Ransome *unbeeped* (DLC)"
 msgstr "Ransome *?????????? ????????* (DLC)"
 
-#: engines/twp/metaengine.cpp:151
+#: engines/twp/metaengine.cpp:150
 msgid "Select highlighted verb"
 msgstr "?????????????? ?????????????????????????? ??????????????"
 
-#: engines/twp/metaengine.cpp:156
+#: engines/twp/metaengine.cpp:155
 msgid "Go to previous object"
 msgstr "?????? ?????????????????????? ??????????????????????"
 
-#: engines/twp/metaengine.cpp:157
+#: engines/twp/metaengine.cpp:156
 msgid "Go to next object"
 msgstr "?????? ?????????????? ??????????????????????"
 
-#: engines/twp/metaengine.cpp:158
+#: engines/twp/metaengine.cpp:157
 msgid "Select verb / item left"
 msgstr "?????????????? ?????????????????? / ???????????????????????? ?????? ????????????????"
 
-#: engines/twp/metaengine.cpp:159
+#: engines/twp/metaengine.cpp:158
 msgid "Select verb / item right"
 msgstr "?????????????? ?????????????????? / ???????????????????????? ?????? ??????????"
 
-#: engines/twp/metaengine.cpp:160
+#: engines/twp/metaengine.cpp:159
 msgid "Select verb / item up"
 msgstr "?????????????? ?????????????????? / ???????????????????????? ????????"
 
-#: engines/twp/metaengine.cpp:161
+#: engines/twp/metaengine.cpp:160
 msgid "Select verb / item down"
 msgstr "?????????????? ?????????????????? / ???????????????????????? ????????"
 
-#: engines/twp/metaengine.cpp:163
+#: engines/twp/metaengine.cpp:162
 msgid "Select actor 1"
 msgstr "?????????????? ???????????????? 1"
 
-#: engines/twp/metaengine.cpp:164
+#: engines/twp/metaengine.cpp:163
 msgid "Select actor 2"
 msgstr "?????????????? ???????????????? 2"
 
-#: engines/twp/metaengine.cpp:165
+#: engines/twp/metaengine.cpp:164
 msgid "Select actor 3"
 msgstr "?????????????? ???????????????? 3"
 
-#: engines/twp/metaengine.cpp:166
+#: engines/twp/metaengine.cpp:165
 msgid "Select actor 4"
 msgstr "?????????????? ???????????????? 4"
 
-#: engines/twp/metaengine.cpp:167
+#: engines/twp/metaengine.cpp:166
 msgid "Select actor 5"
 msgstr "?????????????? ???????????????? 5"
 
-#: engines/twp/metaengine.cpp:168
+#: engines/twp/metaengine.cpp:167
 msgid "Select choice 1"
 msgstr "?????????????? 1"
 
-#: engines/twp/metaengine.cpp:169
+#: engines/twp/metaengine.cpp:168
 msgid "Select choice 2"
 msgstr "?????????????? 2"
 
-#: engines/twp/metaengine.cpp:170
+#: engines/twp/metaengine.cpp:169
 msgid "Select choice 3"
 msgstr "?????????????? 3"
 
-#: engines/twp/metaengine.cpp:171
+#: engines/twp/metaengine.cpp:170
 msgid "Select choice 4"
 msgstr "?????????????? 4"
 
-#: engines/twp/metaengine.cpp:172
+#: engines/twp/metaengine.cpp:171
 msgid "Select choice 5"
 msgstr "?????????????? 5"
 
-#: engines/twp/metaengine.cpp:173
+#: engines/twp/metaengine.cpp:172
 msgid "Select choice 6"
 msgstr "?????????????? 6"
 
-#: engines/twp/metaengine.cpp:174
+#: engines/twp/metaengine.cpp:173
 msgid "Select next Actor"
 msgstr "???????????????? ????????????????"
 
-#: engines/twp/metaengine.cpp:175
+#: engines/twp/metaengine.cpp:174
 msgid "Select previous actor"
 msgstr "???????????????????????? ????????????????"
 
-#: engines/twp/metaengine.cpp:177
+#: engines/twp/metaengine.cpp:176
 msgid "Show hotspots"
 msgstr "?????????????? ?????????????? ????????????????????????????"
 
diff --git a/po/es_ES.po b/po/es_ES.po
index 0f2e696e028..81e4ae0d4ee 100644
--- a/po/es_ES.po
+++ b/po/es_ES.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.4.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-13 13:52+0000\n"
+"POT-Creation-Date: 2026-06-14 18:41+0000\n"
 "PO-Revision-Date: 2026-06-14 09:53+0000\n"
 "Last-Translator: IlDucci <pajaroloco_2 at hotmail.com>\n"
 "Language-Team: Spanish <https://translations.scummvm.org/projects/scummvm/"
@@ -72,7 +72,7 @@ msgstr "Motores disponibles:"
 #: engines/scumm/help.cpp:141 engines/scumm/help.cpp:166
 #: engines/scumm/help.cpp:194 engines/scumm/help.cpp:212
 #: engines/sherlock/metaengine.cpp:430 engines/supernova/metaengine.cpp:341
-#: engines/tot/metaengine.cpp:149 engines/twp/metaengine.cpp:179
+#: engines/tot/metaengine.cpp:149 engines/twp/metaengine.cpp:178
 msgid "Close"
 msgstr "Cerrar"
 
@@ -1054,7 +1054,7 @@ msgstr "Interactuar"
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
 #: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1124
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
-#: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
+#: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:153
 #: engines/ultima/ultima0/metaengine.cpp:45
 #: engines/ultima/ultima4/metaengine.cpp:44
 #: engines/ultima/ultima4/metaengine.cpp:139
@@ -1075,7 +1075,7 @@ msgstr "Arriba"
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
 #: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1091
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
-#: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
+#: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:45
 #: engines/ultima/ultima4/metaengine.cpp:140
@@ -1096,7 +1096,7 @@ msgstr "Abajo"
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
 #: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1104
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
-#: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
+#: engines/twp/metaengine.cpp:151 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
 #: engines/ultima/ultima4/metaengine.cpp:151
 #: engines/wintermute/keymapper_tables.h:320
@@ -1116,7 +1116,7 @@ msgstr "Izquierda"
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
 #: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1111
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
-#: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
+#: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
 #: engines/ultima/ultima4/metaengine.cpp:152
 #: engines/wintermute/keymapper_tables.h:326
@@ -3366,7 +3366,7 @@ msgstr "<Valor incorrecto>"
 
 #. I18N: Abbreviation for 'bytes' as data size
 #. I18N: Hardware key
-#: common/util.cpp:183 backends/platform/3ds/osystem-events.cpp:46
+#: common/util.cpp:190 backends/platform/3ds/osystem-events.cpp:46
 #: backends/platform/ds/osystem_ds.cpp:178
 #: backends/platform/sdl/miyoo/miyoo.cpp:61
 #: backends/platform/sdl/miyoo/miyoo.cpp:88
@@ -3376,22 +3376,22 @@ msgid "B"
 msgstr "B"
 
 #. I18N: Abbreviation for 'kilobytes' as data size
-#: common/util.cpp:189
+#: common/util.cpp:196
 msgid "KB"
 msgstr "KB"
 
 #. I18N: Abbreviation for 'megabytes' as data size
-#: common/util.cpp:194
+#: common/util.cpp:201
 msgid "MB"
 msgstr "MB"
 
 #. I18N: Abbreviation for 'gigabytes' as data size
-#: common/util.cpp:200
+#: common/util.cpp:207
 msgid "GB"
 msgstr "GB"
 
 #. I18N: Abbreviation for 'terabytes' as data size
-#: common/util.cpp:206
+#: common/util.cpp:213
 msgid "TB"
 msgstr "TB"
 
@@ -6486,7 +6486,7 @@ msgstr "Mirar"
 #: engines/scumm/help.cpp:165 engines/scumm/help.cpp:193
 #: engines/scumm/help.cpp:211 engines/sherlock/metaengine.cpp:425
 #: engines/supernova/metaengine.cpp:336 engines/tot/metaengine.cpp:144
-#: engines/twp/metaengine.cpp:178
+#: engines/twp/metaengine.cpp:177
 msgid "Open"
 msgstr "Abrir"
 
@@ -6508,7 +6508,7 @@ msgstr "Coger"
 #: engines/sherlock/metaengine.cpp:440 engines/sherlock/metaengine.cpp:561
 #: engines/sky/metaengine.cpp:95 engines/sludge/keymapper_tables.h:333
 #: engines/supernova/metaengine.cpp:356 engines/tot/metaengine.cpp:139
-#: engines/tsage/metaengine.cpp:211 engines/twp/metaengine.cpp:186
+#: engines/tsage/metaengine.cpp:211 engines/twp/metaengine.cpp:185
 #: engines/ultima/ultima4/metaengine.cpp:74
 #: engines/ultima/nuvie/metaengine.cpp:69
 #: engines/wintermute/keymapper_tables.h:140
@@ -7300,7 +7300,7 @@ msgstr "Activar"
 
 #: engines/alcachofa/metaengine.cpp:104 engines/queen/metaengine.cpp:279
 #: engines/saga/metaengine.cpp:354 engines/scumm/help.cpp:202
-#: engines/scumm/help.cpp:214 engines/twp/metaengine.cpp:182
+#: engines/scumm/help.cpp:214 engines/twp/metaengine.cpp:181
 #: engines/wintermute/keymapper_tables.h:122
 #: engines/wintermute/keymapper_tables.h:1534
 msgid "Look at"
@@ -7652,7 +7652,7 @@ msgstr "Entrar/Salir del modo de combate"
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:210
 #: engines/phoenixvr/metaengine.cpp:94 engines/scumm/help.cpp:79
 #: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
-#: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
+#: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:161
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
 msgstr "Omitir escena"
@@ -8477,7 +8477,7 @@ msgstr "Solo los efectos de la versi??n en disquetes"
 #. I18N: Perform action on object where cursor points
 #: engines/darkseed/metaengine.cpp:42 engines/dragons/metaengine.cpp:135
 #: engines/phoenixvr/metaengine.cpp:75 engines/tinsel/metaengine.cpp:294
-#: engines/twp/metaengine.cpp:150
+#: engines/twp/metaengine.cpp:149
 msgid "Action"
 msgstr "Acci??n"
 
@@ -8897,7 +8897,7 @@ msgstr "Equipar"
 #: engines/scumm/help.cpp:139 engines/scumm/help.cpp:164
 #: engines/scumm/help.cpp:200 engines/scumm/help.cpp:210
 #: engines/sherlock/metaengine.cpp:445 engines/sherlock/metaengine.cpp:566
-#: engines/supernova/metaengine.cpp:366 engines/twp/metaengine.cpp:180
+#: engines/supernova/metaengine.cpp:366 engines/twp/metaengine.cpp:179
 msgid "Give"
 msgstr "Dar"
 
@@ -12221,7 +12221,7 @@ msgstr "Utiliza una fuente personalizada que es m??s f??cil de leer"
 msgid "Move / Skip / Use default action with inventory item"
 msgstr "Mover/Omitir/Acci??n predet. en objeto de inventario"
 
-#: engines/queen/metaengine.cpp:198 engines/twp/metaengine.cpp:176
+#: engines/queen/metaengine.cpp:198 engines/twp/metaengine.cpp:175
 msgid "Skip text"
 msgstr "Omitir texto"
 
@@ -12261,7 +12261,7 @@ msgstr "Usar diario"
 #: engines/scumm/help.cpp:144 engines/scumm/help.cpp:169
 #: engines/scumm/help.cpp:198 engines/scumm/help.cpp:213
 #: engines/scumm/help.cpp:230 engines/sherlock/metaengine.cpp:420
-#: engines/sludge/keymapper_tables.h:318 engines/twp/metaengine.cpp:181
+#: engines/sludge/keymapper_tables.h:318 engines/twp/metaengine.cpp:180
 #: engines/wintermute/keymapper_tables.h:134
 #: engines/wintermute/keymapper_tables.h:1546
 msgid "Pick up"
@@ -12270,7 +12270,7 @@ msgstr "Recoger"
 #: engines/queen/metaengine.cpp:289 engines/saga/metaengine.cpp:364
 #: engines/scumm/help.cpp:199 engines/scumm/help.cpp:215
 #: engines/scumm/help.cpp:251 engines/sludge/keymapper_tables.h:323
-#: engines/twp/metaengine.cpp:183 engines/wintermute/keymapper_tables.h:128
+#: engines/twp/metaengine.cpp:182 engines/wintermute/keymapper_tables.h:128
 #: engines/wintermute/keymapper_tables.h:1558
 msgid "Talk to"
 msgstr "Hablar con"
@@ -12365,7 +12365,7 @@ msgstr "Ir a"
 
 #: engines/saga/metaengine.cpp:389 engines/scumm/help.cpp:122
 #: engines/scumm/help.cpp:137 engines/scumm/help.cpp:162
-#: engines/supernova/metaengine.cpp:346 engines/twp/metaengine.cpp:184
+#: engines/supernova/metaengine.cpp:346 engines/twp/metaengine.cpp:183
 msgid "Push"
 msgstr "Empujar"
 
@@ -13239,7 +13239,7 @@ msgstr "Controles b??sicos:"
 
 #: engines/scumm/help.cpp:123 engines/scumm/help.cpp:138
 #: engines/scumm/help.cpp:163 engines/supernova/metaengine.cpp:351
-#: engines/twp/metaengine.cpp:185
+#: engines/twp/metaengine.cpp:184
 msgid "Pull"
 msgstr "Tirar"
 
@@ -15491,87 +15491,87 @@ msgstr "Acciones centradas"
 msgid "Ransome *unbeeped* (DLC)"
 msgstr "Ransome *desbiipado* (DLC)"
 
-#: engines/twp/metaengine.cpp:151
+#: engines/twp/metaengine.cpp:150
 msgid "Select highlighted verb"
 msgstr "Seleccionar verbo resaltado"
 
-#: engines/twp/metaengine.cpp:156
+#: engines/twp/metaengine.cpp:155
 msgid "Go to previous object"
 msgstr "Ir a objeto anterior"
 
-#: engines/twp/metaengine.cpp:157
+#: engines/twp/metaengine.cpp:156
 msgid "Go to next object"
 msgstr "Ir a siguiente objeto"
 
-#: engines/twp/metaengine.cpp:158
+#: engines/twp/metaengine.cpp:157
 msgid "Select verb / item left"
 msgstr "Seleccionar verbo/objeto a izquierda"
 
-#: engines/twp/metaengine.cpp:159
+#: engines/twp/metaengine.cpp:158
 msgid "Select verb / item right"
 msgstr "Seleccionar verbo/objeto a derecha"
 
-#: engines/twp/metaengine.cpp:160
+#: engines/twp/metaengine.cpp:159
 msgid "Select verb / item up"
 msgstr "Seleccionar verbo/objeto de arriba"
 
-#: engines/twp/metaengine.cpp:161
+#: engines/twp/metaengine.cpp:160
 msgid "Select verb / item down"
 msgstr "Seleccionar verbo/objeto de abajo"
 
-#: engines/twp/metaengine.cpp:163
+#: engines/twp/metaengine.cpp:162
 msgid "Select actor 1"
 msgstr "Seleccionar actor 1"
 
-#: engines/twp/metaengine.cpp:164
+#: engines/twp/metaengine.cpp:163
 msgid "Select actor 2"
 msgstr "Seleccionar actor 2"
 
-#: engines/twp/metaengine.cpp:165
+#: engines/twp/metaengine.cpp:164
 msgid "Select actor 3"
 msgstr "Seleccionar actor 3"
 
-#: engines/twp/metaengine.cpp:166
+#: engines/twp/metaengine.cpp:165
 msgid "Select actor 4"
 msgstr "Seleccionar actor 4"
 
-#: engines/twp/metaengine.cpp:167
+#: engines/twp/metaengine.cpp:166
 msgid "Select actor 5"
 msgstr "Seleccionar actor 5"
 
-#: engines/twp/metaengine.cpp:168
+#: engines/twp/metaengine.cpp:167
 msgid "Select choice 1"
 msgstr "Seleccionar opci??n 1"
 
-#: engines/twp/metaengine.cpp:169
+#: engines/twp/metaengine.cpp:168
 msgid "Select choice 2"
 msgstr "Seleccionar opci??n 2"
 
-#: engines/twp/metaengine.cpp:170
+#: engines/twp/metaengine.cpp:169
 msgid "Select choice 3"
 msgstr "Seleccionar opci??n 3"
 
-#: engines/twp/metaengine.cpp:171
+#: engines/twp/metaengine.cpp:170
 msgid "Select choice 4"
 msgstr "Seleccionar opci??n 4"
 
-#: engines/twp/metaengine.cpp:172
+#: engines/twp/metaengine.cpp:171
 msgid "Select choice 5"
 msgstr "Seleccionar opci??n 5"
 
-#: engines/twp/metaengine.cpp:173
+#: engines/twp/metaengine.cpp:172
 msgid "Select choice 6"
 msgstr "Seleccionar opci??n 6"
 
-#: engines/twp/metaengine.cpp:174
+#: engines/twp/metaengine.cpp:173
 msgid "Select next Actor"
 msgstr "Seleccionar al siguiente actor"
 
-#: engines/twp/metaengine.cpp:175
+#: engines/twp/metaengine.cpp:174
 msgid "Select previous actor"
 msgstr "Seleccionar al actor anterior"
 
-#: engines/twp/metaengine.cpp:177
+#: engines/twp/metaengine.cpp:176
 msgid "Show hotspots"
 msgstr "Mostrar zonas interactivas"
 
diff --git a/po/eu.po b/po/eu.po
index 6a5309ec675..7c9b9440f54 100644
--- a/po/eu.po
+++ b/po/eu.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.5.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-13 13:52+0000\n"
+"POT-Creation-Date: 2026-06-14 18:41+0000\n"
 "PO-Revision-Date: 2024-02-05 18:42+0000\n"
 "Last-Translator: Josu Igoa <josuigoa at ni.eus>\n"
 "Language-Team: Basque <https://translations.scummvm.org/projects/scummvm/"
@@ -73,7 +73,7 @@ msgstr "Motore erabilgarriak:"
 #: engines/scumm/help.cpp:141 engines/scumm/help.cpp:166
 #: engines/scumm/help.cpp:194 engines/scumm/help.cpp:212
 #: engines/sherlock/metaengine.cpp:430 engines/supernova/metaengine.cpp:341
-#: engines/tot/metaengine.cpp:149 engines/twp/metaengine.cpp:179
+#: engines/tot/metaengine.cpp:149 engines/twp/metaengine.cpp:178
 msgid "Close"
 msgstr "Itxi"
 
@@ -1046,7 +1046,7 @@ msgstr "Elkarreragin"
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
 #: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1124
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
-#: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
+#: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:153
 #: engines/ultima/ultima0/metaengine.cpp:45
 #: engines/ultima/ultima4/metaengine.cpp:44
 #: engines/ultima/ultima4/metaengine.cpp:139
@@ -1067,7 +1067,7 @@ msgstr "Gora"
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
 #: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1091
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
-#: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
+#: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:45
 #: engines/ultima/ultima4/metaengine.cpp:140
@@ -1088,7 +1088,7 @@ msgstr "Behera"
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
 #: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1104
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
-#: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
+#: engines/twp/metaengine.cpp:151 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
 #: engines/ultima/ultima4/metaengine.cpp:151
 #: engines/wintermute/keymapper_tables.h:320
@@ -1108,7 +1108,7 @@ msgstr "Ezker"
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
 #: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1111
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
-#: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
+#: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
 #: engines/ultima/ultima4/metaengine.cpp:152
 #: engines/wintermute/keymapper_tables.h:326
@@ -3353,7 +3353,7 @@ msgstr "<Balio okerra>"
 
 #. I18N: Abbreviation for 'bytes' as data size
 #. I18N: Hardware key
-#: common/util.cpp:183 backends/platform/3ds/osystem-events.cpp:46
+#: common/util.cpp:190 backends/platform/3ds/osystem-events.cpp:46
 #: backends/platform/ds/osystem_ds.cpp:178
 #: backends/platform/sdl/miyoo/miyoo.cpp:61
 #: backends/platform/sdl/miyoo/miyoo.cpp:88
@@ -3363,22 +3363,22 @@ msgid "B"
 msgstr "B"
 
 #. I18N: Abbreviation for 'kilobytes' as data size
-#: common/util.cpp:189
+#: common/util.cpp:196
 msgid "KB"
 msgstr "KB"
 
 #. I18N: Abbreviation for 'megabytes' as data size
-#: common/util.cpp:194
+#: common/util.cpp:201
 msgid "MB"
 msgstr "MB"
 
 #. I18N: Abbreviation for 'gigabytes' as data size
-#: common/util.cpp:200
+#: common/util.cpp:207
 msgid "GB"
 msgstr "GB"
 
 #. I18N: Abbreviation for 'terabytes' as data size
-#: common/util.cpp:206
+#: common/util.cpp:213
 msgid "TB"
 msgstr "TB"
 
@@ -6260,7 +6260,7 @@ msgstr "Begiratu"
 #: engines/scumm/help.cpp:165 engines/scumm/help.cpp:193
 #: engines/scumm/help.cpp:211 engines/sherlock/metaengine.cpp:425
 #: engines/supernova/metaengine.cpp:336 engines/tot/metaengine.cpp:144
-#: engines/twp/metaengine.cpp:178
+#: engines/twp/metaengine.cpp:177
 msgid "Open"
 msgstr "Ireki"
 
@@ -6282,7 +6282,7 @@ msgstr "Jaso"
 #: engines/sherlock/metaengine.cpp:440 engines/sherlock/metaengine.cpp:561
 #: engines/sky/metaengine.cpp:95 engines/sludge/keymapper_tables.h:333
 #: engines/supernova/metaengine.cpp:356 engines/tot/metaengine.cpp:139
-#: engines/tsage/metaengine.cpp:211 engines/twp/metaengine.cpp:186
+#: engines/tsage/metaengine.cpp:211 engines/twp/metaengine.cpp:185
 #: engines/ultima/ultima4/metaengine.cpp:74
 #: engines/ultima/nuvie/metaengine.cpp:69
 #: engines/wintermute/keymapper_tables.h:140
@@ -7061,7 +7061,7 @@ msgstr "Aktiboa"
 
 #: engines/alcachofa/metaengine.cpp:104 engines/queen/metaengine.cpp:279
 #: engines/saga/metaengine.cpp:354 engines/scumm/help.cpp:202
-#: engines/scumm/help.cpp:214 engines/twp/metaengine.cpp:182
+#: engines/scumm/help.cpp:214 engines/twp/metaengine.cpp:181
 #: engines/wintermute/keymapper_tables.h:122
 #: engines/wintermute/keymapper_tables.h:1534
 msgid "Look at"
@@ -7427,7 +7427,7 @@ msgstr "Mututu/Ez-mututu"
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:210
 #: engines/phoenixvr/metaengine.cpp:94 engines/scumm/help.cpp:79
 #: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
-#: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
+#: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:161
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
 msgstr "Eszena saltatu"
@@ -8341,7 +8341,7 @@ msgstr ""
 #. I18N: Perform action on object where cursor points
 #: engines/darkseed/metaengine.cpp:42 engines/dragons/metaengine.cpp:135
 #: engines/phoenixvr/metaengine.cpp:75 engines/tinsel/metaengine.cpp:294
-#: engines/twp/metaengine.cpp:150
+#: engines/twp/metaengine.cpp:149
 #, fuzzy
 msgid "Action"
 msgstr "Aktiboa"
@@ -8845,7 +8845,7 @@ msgstr ""
 #: engines/scumm/help.cpp:139 engines/scumm/help.cpp:164
 #: engines/scumm/help.cpp:200 engines/scumm/help.cpp:210
 #: engines/sherlock/metaengine.cpp:445 engines/sherlock/metaengine.cpp:566
-#: engines/supernova/metaengine.cpp:366 engines/twp/metaengine.cpp:180
+#: engines/supernova/metaengine.cpp:366 engines/twp/metaengine.cpp:179
 msgid "Give"
 msgstr "Eman"
 
@@ -12399,7 +12399,7 @@ msgstr ""
 msgid "Move / Skip / Use default action with inventory item"
 msgstr ""
 
-#: engines/queen/metaengine.cpp:198 engines/twp/metaengine.cpp:176
+#: engines/queen/metaengine.cpp:198 engines/twp/metaengine.cpp:175
 #, fuzzy
 #| msgid "Skip text"
 msgid "Skip text"
@@ -12454,7 +12454,7 @@ msgstr "Denak erakutsi"
 #: engines/scumm/help.cpp:144 engines/scumm/help.cpp:169
 #: engines/scumm/help.cpp:198 engines/scumm/help.cpp:213
 #: engines/scumm/help.cpp:230 engines/sherlock/metaengine.cpp:420
-#: engines/sludge/keymapper_tables.h:318 engines/twp/metaengine.cpp:181
+#: engines/sludge/keymapper_tables.h:318 engines/twp/metaengine.cpp:180
 #: engines/wintermute/keymapper_tables.h:134
 #: engines/wintermute/keymapper_tables.h:1546
 msgid "Pick up"
@@ -12463,7 +12463,7 @@ msgstr "Jaso"
 #: engines/queen/metaengine.cpp:289 engines/saga/metaengine.cpp:364
 #: engines/scumm/help.cpp:199 engines/scumm/help.cpp:215
 #: engines/scumm/help.cpp:251 engines/sludge/keymapper_tables.h:323
-#: engines/twp/metaengine.cpp:183 engines/wintermute/keymapper_tables.h:128
+#: engines/twp/metaengine.cpp:182 engines/wintermute/keymapper_tables.h:128
 #: engines/wintermute/keymapper_tables.h:1558
 msgid "Talk to"
 msgstr "Hitz egin"
@@ -12573,7 +12573,7 @@ msgstr "Joan"
 
 #: engines/saga/metaengine.cpp:389 engines/scumm/help.cpp:122
 #: engines/scumm/help.cpp:137 engines/scumm/help.cpp:162
-#: engines/supernova/metaengine.cpp:346 engines/twp/metaengine.cpp:184
+#: engines/supernova/metaengine.cpp:346 engines/twp/metaengine.cpp:183
 msgid "Push"
 msgstr "Bultzatu"
 
@@ -13371,7 +13371,7 @@ msgstr "Joko kontrol nagusiak:"
 
 #: engines/scumm/help.cpp:123 engines/scumm/help.cpp:138
 #: engines/scumm/help.cpp:163 engines/supernova/metaengine.cpp:351
-#: engines/twp/metaengine.cpp:185
+#: engines/twp/metaengine.cpp:184
 msgid "Pull"
 msgstr "Tiratu"
 
@@ -15781,125 +15781,125 @@ msgstr ""
 msgid "Ransome *unbeeped* (DLC)"
 msgstr ""
 
-#: engines/twp/metaengine.cpp:151
+#: engines/twp/metaengine.cpp:150
 #, fuzzy
 #| msgid "Select shader"
 msgid "Select highlighted verb"
 msgstr "Aukeratu shader-a"
 
-#: engines/twp/metaengine.cpp:156
+#: engines/twp/metaengine.cpp:155
 #, fuzzy
 #| msgid "Go to previous directory level"
 msgid "Go to previous object"
 msgstr "Igo aurreko direktorio-mailara"
 
-#: engines/twp/metaengine.cpp:157
+#: engines/twp/metaengine.cpp:156
 #, fuzzy
 msgid "Go to next object"
 msgstr "Aukerak"
 
-#: engines/twp/metaengine.cpp:158
+#: engines/twp/metaengine.cpp:157
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Select verb / item left"
 msgstr "Eskuinera irristatu"
 
-#: engines/twp/metaengine.cpp:159
+#: engines/twp/metaengine.cpp:158
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Select verb / item right"
 msgstr "Eskuinera irristatu"
 
-#: engines/twp/metaengine.cpp:160
+#: engines/twp/metaengine.cpp:159
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Select verb / item up"
 msgstr "Eskuinera irristatu"
 
-#: engines/twp/metaengine.cpp:161
+#: engines/twp/metaengine.cpp:160
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Select verb / item down"
 msgstr "Eskuinera irristatu"
 
-#: engines/twp/metaengine.cpp:163
+#: engines/twp/metaengine.cpp:162
 #, fuzzy
 #| msgid "Select Group by"
 msgid "Select actor 1"
 msgstr "Aukeratu Taldea honen bidez"
 
-#: engines/twp/metaengine.cpp:164
+#: engines/twp/metaengine.cpp:163
 #, fuzzy
 #| msgid "Select Group by"
 msgid "Select actor 2"
 msgstr "Aukeratu Taldea honen bidez"
 
-#: engines/twp/metaengine.cpp:165
+#: engines/twp/metaengine.cpp:164
 #, fuzzy
 #| msgid "Select Group by"
 msgid "Select actor 3"
 msgstr "Aukeratu Taldea honen bidez"
 
-#: engines/twp/metaengine.cpp:166
+#: engines/twp/metaengine.cpp:165
 #, fuzzy
 #| msgid "Select Group by"
 msgid "Select actor 4"
 msgstr "Aukeratu Taldea honen bidez"
 
-#: engines/twp/metaengine.cpp:167
+#: engines/twp/metaengine.cpp:166
 #, fuzzy
 #| msgid "Select Group by"
 msgid "Select actor 5"
 msgstr "Aukeratu Taldea honen bidez"
 
-#: engines/twp/metaengine.cpp:168
+#: engines/twp/metaengine.cpp:167
 #, fuzzy
 #| msgid "Select shader"
 msgid "Select choice 1"
 msgstr "Aukeratu shader-a"
 
-#: engines/twp/metaengine.cpp:169
+#: engines/twp/metaengine.cpp:168
 #, fuzzy
 #| msgid "Select shader"
 msgid "Select choice 2"
 msgstr "Aukeratu shader-a"
 
-#: engines/twp/metaengine.cpp:170
+#: engines/twp/metaengine.cpp:169
 #, fuzzy
 #| msgid "Select shader"
 msgid "Select choice 3"
 msgstr "Aukeratu shader-a"
 
-#: engines/twp/metaengine.cpp:171
+#: engines/twp/metaengine.cpp:170
 #, fuzzy
 #| msgid "Select shader"
 msgid "Select choice 4"
 msgstr "Aukeratu shader-a"
 
-#: engines/twp/metaengine.cpp:172
+#: engines/twp/metaengine.cpp:171
 #, fuzzy
 #| msgid "Select shader"
 msgid "Select choice 5"
 msgstr "Aukeratu shader-a"
 
-#: engines/twp/metaengine.cpp:173
+#: engines/twp/metaengine.cpp:172
 #, fuzzy
 #| msgid "Select shader"
 msgid "Select choice 6"
 msgstr "Aukeratu shader-a"
 
-#: engines/twp/metaengine.cpp:174
+#: engines/twp/metaengine.cpp:173
 #, fuzzy
 #| msgid "Select shader"
 msgid "Select next Actor"
 msgstr "Aukeratu shader-a"
 
-#: engines/twp/metaengine.cpp:175
+#: engines/twp/metaengine.cpp:174
 #, fuzzy
 msgid "Select previous actor"
 msgstr "~A~urrekoa"
 
-#: engines/twp/metaengine.cpp:177
+#: engines/twp/metaengine.cpp:176
 #, fuzzy
 msgid "Show hotspots"
 msgstr "Erakutsi fitxategi ezkutuak"
diff --git a/po/fi_FI.po b/po/fi_FI.po
index 8b0368b3130..6bb351f6ab4 100644
--- a/po/fi_FI.po
+++ b/po/fi_FI.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.6.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-13 13:52+0000\n"
+"POT-Creation-Date: 2026-06-14 18:41+0000\n"
 "PO-Revision-Date: 2026-02-07 15:22+0000\n"
 "Last-Translator: Timo Mikkolainen <tmikkola at gmail.com>\n"
 "Language-Team: Finnish <https://translations.scummvm.org/projects/scummvm/"
@@ -73,7 +73,7 @@ msgstr "Tuetut pelimoottorit:"
 #: engines/scumm/help.cpp:141 engines/scumm/help.cpp:166
 #: engines/scumm/help.cpp:194 engines/scumm/help.cpp:212
 #: engines/sherlock/metaengine.cpp:430 engines/supernova/metaengine.cpp:341
-#: engines/tot/metaengine.cpp:149 engines/twp/metaengine.cpp:179
+#: engines/tot/metaengine.cpp:149 engines/twp/metaengine.cpp:178
 msgid "Close"
 msgstr "Sulje"
 
@@ -1054,7 +1054,7 @@ msgstr "K??yt??"
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
 #: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1124
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
-#: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
+#: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:153
 #: engines/ultima/ultima0/metaengine.cpp:45
 #: engines/ultima/ultima4/metaengine.cpp:44
 #: engines/ultima/ultima4/metaengine.cpp:139
@@ -1075,7 +1075,7 @@ msgstr "Yl??s"
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
 #: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1091
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
-#: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
+#: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:45
 #: engines/ultima/ultima4/metaengine.cpp:140
@@ -1096,7 +1096,7 @@ msgstr "Alas"
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
 #: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1104
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
-#: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
+#: engines/twp/metaengine.cpp:151 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
 #: engines/ultima/ultima4/metaengine.cpp:151
 #: engines/wintermute/keymapper_tables.h:320
@@ -1116,7 +1116,7 @@ msgstr "Vasen"
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
 #: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1111
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
-#: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
+#: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
 #: engines/ultima/ultima4/metaengine.cpp:152
 #: engines/wintermute/keymapper_tables.h:326
@@ -3398,7 +3398,7 @@ msgstr "<Huono arvo>"
 
 #. I18N: Abbreviation for 'bytes' as data size
 #. I18N: Hardware key
-#: common/util.cpp:183 backends/platform/3ds/osystem-events.cpp:46
+#: common/util.cpp:190 backends/platform/3ds/osystem-events.cpp:46
 #: backends/platform/ds/osystem_ds.cpp:178
 #: backends/platform/sdl/miyoo/miyoo.cpp:61
 #: backends/platform/sdl/miyoo/miyoo.cpp:88
@@ -3408,22 +3408,22 @@ msgid "B"
 msgstr "B"
 
 #. I18N: Abbreviation for 'kilobytes' as data size
-#: common/util.cpp:189
+#: common/util.cpp:196
 msgid "KB"
 msgstr "KB"
 
 #. I18N: Abbreviation for 'megabytes' as data size
-#: common/util.cpp:194
+#: common/util.cpp:201
 msgid "MB"
 msgstr "MB"
 
 #. I18N: Abbreviation for 'gigabytes' as data size
-#: common/util.cpp:200
+#: common/util.cpp:207
 msgid "GB"
 msgstr "GB"
 
 #. I18N: Abbreviation for 'terabytes' as data size
-#: common/util.cpp:206
+#: common/util.cpp:213
 msgid "TB"
 msgstr "TB"
 
@@ -6591,7 +6591,7 @@ msgstr "Katso"
 #: engines/scumm/help.cpp:165 engines/scumm/help.cpp:193
 #: engines/scumm/help.cpp:211 engines/sherlock/metaengine.cpp:425
 #: engines/supernova/metaengine.cpp:336 engines/tot/metaengine.cpp:144
-#: engines/twp/metaengine.cpp:178
+#: engines/twp/metaengine.cpp:177
 msgid "Open"
 msgstr "Avaa"
 
@@ -6613,7 +6613,7 @@ msgstr "Ota"
 #: engines/sherlock/metaengine.cpp:440 engines/sherlock/metaengine.cpp:561
 #: engines/sky/metaengine.cpp:95 engines/sludge/keymapper_tables.h:333
 #: engines/supernova/metaengine.cpp:356 engines/tot/metaengine.cpp:139
-#: engines/tsage/metaengine.cpp:211 engines/twp/metaengine.cpp:186
+#: engines/tsage/metaengine.cpp:211 engines/twp/metaengine.cpp:185
 #: engines/ultima/ultima4/metaengine.cpp:74
 #: engines/ultima/nuvie/metaengine.cpp:69
 #: engines/wintermute/keymapper_tables.h:140
@@ -7385,7 +7385,7 @@ msgstr "Toimeenpane"
 
 #: engines/alcachofa/metaengine.cpp:104 engines/queen/metaengine.cpp:279
 #: engines/saga/metaengine.cpp:354 engines/scumm/help.cpp:202
-#: engines/scumm/help.cpp:214 engines/twp/metaengine.cpp:182
+#: engines/scumm/help.cpp:214 engines/twp/metaengine.cpp:181
 #: engines/wintermute/keymapper_tables.h:122
 #: engines/wintermute/keymapper_tables.h:1534
 msgid "Look at"
@@ -7743,7 +7743,7 @@ msgstr "Taistelumoodi p????lle / pois"
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:210
 #: engines/phoenixvr/metaengine.cpp:94 engines/scumm/help.cpp:79
 #: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
-#: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
+#: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:161
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
 msgstr "Ohita video"
@@ -8592,7 +8592,7 @@ msgstr "Vain levykeversion ????nitehosteet"
 #. I18N: Perform action on object where cursor points
 #: engines/darkseed/metaengine.cpp:42 engines/dragons/metaengine.cpp:135
 #: engines/phoenixvr/metaengine.cpp:75 engines/tinsel/metaengine.cpp:294
-#: engines/twp/metaengine.cpp:150
+#: engines/twp/metaengine.cpp:149
 msgid "Action"
 msgstr "Toiminta"
 
@@ -9033,7 +9033,7 @@ msgstr "Varusta"
 #: engines/scumm/help.cpp:139 engines/scumm/help.cpp:164
 #: engines/scumm/help.cpp:200 engines/scumm/help.cpp:210
 #: engines/sherlock/metaengine.cpp:445 engines/sherlock/metaengine.cpp:566
-#: engines/supernova/metaengine.cpp:366 engines/twp/metaengine.cpp:180
+#: engines/supernova/metaengine.cpp:366 engines/twp/metaengine.cpp:179
 msgid "Give"
 msgstr "Anna"
 
@@ -12495,7 +12495,7 @@ msgstr "K??yt?? helppolukuisempaa r????t??l??ity?? kirjasinmallia"
 msgid "Move / Skip / Use default action with inventory item"
 msgstr ""
 
-#: engines/queen/metaengine.cpp:198 engines/twp/metaengine.cpp:176
+#: engines/queen/metaengine.cpp:198 engines/twp/metaengine.cpp:175
 msgid "Skip text"
 msgstr "Ohita teksti"
 
@@ -12537,7 +12537,7 @@ msgstr "K??yt?? p??iv??kirjaa"
 #: engines/scumm/help.cpp:144 engines/scumm/help.cpp:169
 #: engines/scumm/help.cpp:198 engines/scumm/help.cpp:213
 #: engines/scumm/help.cpp:230 engines/sherlock/metaengine.cpp:420
-#: engines/sludge/keymapper_tables.h:318 engines/twp/metaengine.cpp:181
+#: engines/sludge/keymapper_tables.h:318 engines/twp/metaengine.cpp:180
 #: engines/wintermute/keymapper_tables.h:134
 #: engines/wintermute/keymapper_tables.h:1546
 msgid "Pick up"
@@ -12546,7 +12546,7 @@ msgstr "Ota"
 #: engines/queen/metaengine.cpp:289 engines/saga/metaengine.cpp:364
 #: engines/scumm/help.cpp:199 engines/scumm/help.cpp:215
 #: engines/scumm/help.cpp:251 engines/sludge/keymapper_tables.h:323
-#: engines/twp/metaengine.cpp:183 engines/wintermute/keymapper_tables.h:128
+#: engines/twp/metaengine.cpp:182 engines/wintermute/keymapper_tables.h:128
 #: engines/wintermute/keymapper_tables.h:1558
 msgid "Talk to"
 msgstr "Puhu"
@@ -12651,7 +12651,7 @@ msgstr "K??vele"
 
 #: engines/saga/metaengine.cpp:389 engines/scumm/help.cpp:122
 #: engines/scumm/help.cpp:137 engines/scumm/help.cpp:162
-#: engines/supernova/metaengine.cpp:346 engines/twp/metaengine.cpp:184
+#: engines/supernova/metaengine.cpp:346 engines/twp/metaengine.cpp:183
 msgid "Push"
 msgstr "Ty??nn??"
 
@@ -13503,7 +13503,7 @@ msgstr "Pelin t??rkeimm??t kontrollit:"
 
 #: engines/scumm/help.cpp:123 engines/scumm/help.cpp:138
 #: engines/scumm/help.cpp:163 engines/supernova/metaengine.cpp:351
-#: engines/twp/metaengine.cpp:185
+#: engines/twp/metaengine.cpp:184
 msgid "Pull"
 msgstr "Ved??"
 
@@ -15810,87 +15810,87 @@ msgstr "Klassinen lause"
 msgid "Ransome *unbeeped* (DLC)"
 msgstr "Ransome *unbeeped* (DLC)"
 
-#: engines/twp/metaengine.cpp:151
+#: engines/twp/metaengine.cpp:150
 msgid "Select highlighted verb"
 msgstr "Valitse korostettu verbi"
 
-#: engines/twp/metaengine.cpp:156
+#: engines/twp/metaengine.cpp:155
 msgid "Go to previous object"
 msgstr "Mene edelliseen objektiin"
 
-#: engines/twp/metaengine.cpp:157
+#: engines/twp/metaengine.cpp:156
 msgid "Go to next object"
 msgstr "Mene seuraavaan objektiin"
 
-#: engines/twp/metaengine.cpp:158
+#: engines/twp/metaengine.cpp:157
 msgid "Select verb / item left"
 msgstr "Valitse vasen verbi / esine"
 
-#: engines/twp/metaengine.cpp:159
+#: engines/twp/metaengine.cpp:158
 msgid "Select verb / item right"
 msgstr "Valitse oikea verbi / esine"
 
-#: engines/twp/metaengine.cpp:160
+#: engines/twp/metaengine.cpp:159
 msgid "Select verb / item up"
 msgstr "Valitse ylempi verbi / esine"
 
-#: engines/twp/metaengine.cpp:161
+#: engines/twp/metaengine.cpp:160
 msgid "Select verb / item down"
 msgstr "Valitse alempi verbi / esine"
 
-#: engines/twp/metaengine.cpp:163
+#: engines/twp/metaengine.cpp:162
 msgid "Select actor 1"
 msgstr "Valitse n??yttelij?? 1"
 
-#: engines/twp/metaengine.cpp:164
+#: engines/twp/metaengine.cpp:163
 msgid "Select actor 2"
 msgstr "Valitse n??yttelij?? 2"
 
-#: engines/twp/metaengine.cpp:165
+#: engines/twp/metaengine.cpp:164
 msgid "Select actor 3"
 msgstr "Valitse n??yttelij?? 3"
 
-#: engines/twp/metaengine.cpp:166
+#: engines/twp/metaengine.cpp:165
 msgid "Select actor 4"
 msgstr "Valitse n??yttelij?? 4"
 
-#: engines/twp/metaengine.cpp:167
+#: engines/twp/metaengine.cpp:166
 msgid "Select actor 5"
 msgstr "Valitse n??yttelij?? 5"
 
-#: engines/twp/metaengine.cpp:168
+#: engines/twp/metaengine.cpp:167
 msgid "Select choice 1"
 msgstr "Valitse vaihtoehto 1"
 
-#: engines/twp/metaengine.cpp:169
+#: engines/twp/metaengine.cpp:168
 msgid "Select choice 2"
 msgstr "Valitse vaihtoehto 2"
 
-#: engines/twp/metaengine.cpp:170
+#: engines/twp/metaengine.cpp:169
 msgid "Select choice 3"
 msgstr "Valitse vaihtoehto 3"
 
-#: engines/twp/metaengine.cpp:171
+#: engines/twp/metaengine.cpp:170
 msgid "Select choice 4"
 msgstr "Valitse vaihtoehto 4"
 
-#: engines/twp/metaengine.cpp:172
+#: engines/twp/metaengine.cpp:171
 msgid "Select choice 5"
 msgstr "Valitse vaihtoehto 5"
 
-#: engines/twp/metaengine.cpp:173
+#: engines/twp/metaengine.cpp:172
 msgid "Select choice 6"
 msgstr "Valitse vaihtoehto 6"
 
-#: engines/twp/metaengine.cpp:174
+#: engines/twp/metaengine.cpp:173
 msgid "Select next Actor"
 msgstr "Valitse seuraava n??yttelij??"
 
-#: engines/twp/metaengine.cpp:175
+#: engines/twp/metaengine.cpp:174
 msgid "Select previous actor"
 msgstr "Valitse edellinen n??yttelij??"
 
-#: engines/twp/metaengine.cpp:177
+#: engines/twp/metaengine.cpp:176
 msgid "Show hotspots"
 msgstr "N??yt?? kohdealueet"
 
diff --git a/po/fr_FR.po b/po/fr_FR.po
index e4bd2fb0d71..e2b478f04c6 100644
--- a/po/fr_FR.po
+++ b/po/fr_FR.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-13 13:52+0000\n"
+"POT-Creation-Date: 2026-06-14 18:41+0000\n"
 "PO-Revision-Date: 2026-06-14 09:41+0000\n"
 "Last-Translator: Purple T <ZEONK at hotmail.com>\n"
 "Language-Team: French <https://translations.scummvm.org/projects/scummvm/"
@@ -72,7 +72,7 @@ msgstr "Moteurs disponibles :"
 #: engines/scumm/help.cpp:141 engines/scumm/help.cpp:166
 #: engines/scumm/help.cpp:194 engines/scumm/help.cpp:212
 #: engines/sherlock/metaengine.cpp:430 engines/supernova/metaengine.cpp:341
-#: engines/tot/metaengine.cpp:149 engines/twp/metaengine.cpp:179
+#: engines/tot/metaengine.cpp:149 engines/twp/metaengine.cpp:178
 msgid "Close"
 msgstr "Fermer"
 
@@ -1065,7 +1065,7 @@ msgstr "Interagir"
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
 #: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1124
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
-#: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
+#: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:153
 #: engines/ultima/ultima0/metaengine.cpp:45
 #: engines/ultima/ultima4/metaengine.cpp:44
 #: engines/ultima/ultima4/metaengine.cpp:139
@@ -1086,7 +1086,7 @@ msgstr "Haut"
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
 #: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1091
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
-#: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
+#: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:45
 #: engines/ultima/ultima4/metaengine.cpp:140
@@ -1107,7 +1107,7 @@ msgstr "Bas"
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
 #: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1104
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
-#: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
+#: engines/twp/metaengine.cpp:151 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
 #: engines/ultima/ultima4/metaengine.cpp:151
 #: engines/wintermute/keymapper_tables.h:320
@@ -1127,7 +1127,7 @@ msgstr "Gauche"
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
 #: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1111
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
-#: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
+#: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
 #: engines/ultima/ultima4/metaengine.cpp:152
 #: engines/wintermute/keymapper_tables.h:326
@@ -3382,7 +3382,7 @@ msgstr "<Valeur invalide>"
 
 #. I18N: Abbreviation for 'bytes' as data size
 #. I18N: Hardware key
-#: common/util.cpp:183 backends/platform/3ds/osystem-events.cpp:46
+#: common/util.cpp:190 backends/platform/3ds/osystem-events.cpp:46
 #: backends/platform/ds/osystem_ds.cpp:178
 #: backends/platform/sdl/miyoo/miyoo.cpp:61
 #: backends/platform/sdl/miyoo/miyoo.cpp:88
@@ -3392,22 +3392,22 @@ msgid "B"
 msgstr "B"
 
 #. I18N: Abbreviation for 'kilobytes' as data size
-#: common/util.cpp:189
+#: common/util.cpp:196
 msgid "KB"
 msgstr "ko"
 
 #. I18N: Abbreviation for 'megabytes' as data size
-#: common/util.cpp:194
+#: common/util.cpp:201
 msgid "MB"
 msgstr "Mo"
 
 #. I18N: Abbreviation for 'gigabytes' as data size
-#: common/util.cpp:200
+#: common/util.cpp:207
 msgid "GB"
 msgstr "Go"
 
 #. I18N: Abbreviation for 'terabytes' as data size
-#: common/util.cpp:206
+#: common/util.cpp:213
 msgid "TB"
 msgstr "To"
 
@@ -6502,7 +6502,7 @@ msgstr "Examiner"
 #: engines/scumm/help.cpp:165 engines/scumm/help.cpp:193
 #: engines/scumm/help.cpp:211 engines/sherlock/metaengine.cpp:425
 #: engines/supernova/metaengine.cpp:336 engines/tot/metaengine.cpp:144
-#: engines/twp/metaengine.cpp:178
+#: engines/twp/metaengine.cpp:177
 msgid "Open"
 msgstr "Ouvrir"
 
@@ -6524,7 +6524,7 @@ msgstr "Prendre"
 #: engines/sherlock/metaengine.cpp:440 engines/sherlock/metaengine.cpp:561
 #: engines/sky/metaengine.cpp:95 engines/sludge/keymapper_tables.h:333
 #: engines/supernova/metaengine.cpp:356 engines/tot/metaengine.cpp:139
-#: engines/tsage/metaengine.cpp:211 engines/twp/metaengine.cpp:186
+#: engines/tsage/metaengine.cpp:211 engines/twp/metaengine.cpp:185
 #: engines/ultima/ultima4/metaengine.cpp:74
 #: engines/ultima/nuvie/metaengine.cpp:69
 #: engines/wintermute/keymapper_tables.h:140
@@ -7303,7 +7303,7 @@ msgstr "Activer"
 
 #: engines/alcachofa/metaengine.cpp:104 engines/queen/metaengine.cpp:279
 #: engines/saga/metaengine.cpp:354 engines/scumm/help.cpp:202
-#: engines/scumm/help.cpp:214 engines/twp/metaengine.cpp:182
+#: engines/scumm/help.cpp:214 engines/twp/metaengine.cpp:181
 #: engines/wintermute/keymapper_tables.h:122
 #: engines/wintermute/keymapper_tables.h:1534
 msgid "Look at"
@@ -7658,7 +7658,7 @@ msgstr "Act. / D??sact. combat"
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:210
 #: engines/phoenixvr/metaengine.cpp:94 engines/scumm/help.cpp:79
 #: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
-#: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
+#: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:161
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
 msgstr "Passer la cin??matique"
@@ -8478,7 +8478,7 @@ msgstr "Version Disquette seuls"
 #. I18N: Perform action on object where cursor points
 #: engines/darkseed/metaengine.cpp:42 engines/dragons/metaengine.cpp:135
 #: engines/phoenixvr/metaengine.cpp:75 engines/tinsel/metaengine.cpp:294
-#: engines/twp/metaengine.cpp:150
+#: engines/twp/metaengine.cpp:149
 msgid "Action"
 msgstr "Action"
 
@@ -8901,7 +8901,7 @@ msgstr "Equiper"
 #: engines/scumm/help.cpp:139 engines/scumm/help.cpp:164
 #: engines/scumm/help.cpp:200 engines/scumm/help.cpp:210
 #: engines/sherlock/metaengine.cpp:445 engines/sherlock/metaengine.cpp:566
-#: engines/supernova/metaengine.cpp:366 engines/twp/metaengine.cpp:180
+#: engines/supernova/metaengine.cpp:366 engines/twp/metaengine.cpp:179
 msgid "Give"
 msgstr "Donner"
 
@@ -12245,7 +12245,7 @@ msgid "Move / Skip / Use default action with inventory item"
 msgstr ""
 "D??placer / Passer / Utiliser l'action par d??faut avec l'item de l'inventaire"
 
-#: engines/queen/metaengine.cpp:198 engines/twp/metaengine.cpp:176
+#: engines/queen/metaengine.cpp:198 engines/twp/metaengine.cpp:175
 msgid "Skip text"
 msgstr "Passer le texte"
 
@@ -12285,7 +12285,7 @@ msgstr "Utiliser le journal"
 #: engines/scumm/help.cpp:144 engines/scumm/help.cpp:169
 #: engines/scumm/help.cpp:198 engines/scumm/help.cpp:213
 #: engines/scumm/help.cpp:230 engines/sherlock/metaengine.cpp:420
-#: engines/sludge/keymapper_tables.h:318 engines/twp/metaengine.cpp:181
+#: engines/sludge/keymapper_tables.h:318 engines/twp/metaengine.cpp:180
 #: engines/wintermute/keymapper_tables.h:134
 #: engines/wintermute/keymapper_tables.h:1546
 msgid "Pick up"
@@ -12294,7 +12294,7 @@ msgstr "Prendre"
 #: engines/queen/metaengine.cpp:289 engines/saga/metaengine.cpp:364
 #: engines/scumm/help.cpp:199 engines/scumm/help.cpp:215
 #: engines/scumm/help.cpp:251 engines/sludge/keymapper_tables.h:323
-#: engines/twp/metaengine.cpp:183 engines/wintermute/keymapper_tables.h:128
+#: engines/twp/metaengine.cpp:182 engines/wintermute/keymapper_tables.h:128
 #: engines/wintermute/keymapper_tables.h:1558
 msgid "Talk to"
 msgstr "Parler ??"
@@ -12390,7 +12390,7 @@ msgstr "Aller"
 
 #: engines/saga/metaengine.cpp:389 engines/scumm/help.cpp:122
 #: engines/scumm/help.cpp:137 engines/scumm/help.cpp:162
-#: engines/supernova/metaengine.cpp:346 engines/twp/metaengine.cpp:184
+#: engines/supernova/metaengine.cpp:346 engines/twp/metaengine.cpp:183
 msgid "Push"
 msgstr "Pousser"
 
@@ -13264,7 +13264,7 @@ msgstr "Contr??les principaux du jeu :"
 
 #: engines/scumm/help.cpp:123 engines/scumm/help.cpp:138
 #: engines/scumm/help.cpp:163 engines/supernova/metaengine.cpp:351
-#: engines/twp/metaengine.cpp:185
+#: engines/twp/metaengine.cpp:184
 msgid "Pull"
 msgstr "Tirer"
 
@@ -15536,87 +15536,87 @@ msgstr "Phrases classiques"
 msgid "Ransome *unbeeped* (DLC)"
 msgstr "Ransome *sans bip* (DLC)"
 
-#: engines/twp/metaengine.cpp:151
+#: engines/twp/metaengine.cpp:150
 msgid "Select highlighted verb"
 msgstr "S??lectionner la commande surlign??e"
 
-#: engines/twp/metaengine.cpp:156
+#: engines/twp/metaengine.cpp:155
 msgid "Go to previous object"
 msgstr "Objet pr??c??dent"
 
-#: engines/twp/metaengine.cpp:157
+#: engines/twp/metaengine.cpp:156
 msgid "Go to next object"
 msgstr "Objet suivant"
 
-#: engines/twp/metaengine.cpp:158
+#: engines/twp/metaengine.cpp:157
 msgid "Select verb / item left"
 msgstr "S??lectionner commande / ??l??ment gauche"
 
-#: engines/twp/metaengine.cpp:159
+#: engines/twp/metaengine.cpp:158
 msgid "Select verb / item right"
 msgstr "S??lectionner commande / ??l??ment droite"
 
-#: engines/twp/metaengine.cpp:160
+#: engines/twp/metaengine.cpp:159
 msgid "Select verb / item up"
 msgstr "S??lectionner commande / ??l??ment haut"
 
-#: engines/twp/metaengine.cpp:161
+#: engines/twp/metaengine.cpp:160
 msgid "Select verb / item down"
 msgstr "S??lectionner commande / ??l??ment bas"
 
-#: engines/twp/metaengine.cpp:163
+#: engines/twp/metaengine.cpp:162
 msgid "Select actor 1"
 msgstr "S??lectionner acteur 1"
 
-#: engines/twp/metaengine.cpp:164
+#: engines/twp/metaengine.cpp:163
 msgid "Select actor 2"
 msgstr "S??lectionner acteur 2"
 
-#: engines/twp/metaengine.cpp:165
+#: engines/twp/metaengine.cpp:164
 msgid "Select actor 3"
 msgstr "S??lectionner acteur 3"
 
-#: engines/twp/metaengine.cpp:166
+#: engines/twp/metaengine.cpp:165
 msgid "Select actor 4"
 msgstr "S??lectionner acteur 4"
 
-#: engines/twp/metaengine.cpp:167
+#: engines/twp/metaengine.cpp:166
 msgid "Select actor 5"
 msgstr "S??lectionner acteur 5"
 
-#: engines/twp/metaengine.cpp:168
+#: engines/twp/metaengine.cpp:167
 msgid "Select choice 1"
 msgstr "S??lectionner choix 1"
 
-#: engines/twp/metaengine.cpp:169
+#: engines/twp/metaengine.cpp:168
 msgid "Select choice 2"
 msgstr "S??lectionner choix 2"
 
-#: engines/twp/metaengine.cpp:170
+#: engines/twp/metaengine.cpp:169
 msgid "Select choice 3"
 msgstr "S??lectionner choix 3"
 
-#: engines/twp/metaengine.cpp:171
+#: engines/twp/metaengine.cpp:170
 msgid "Select choice 4"
 msgstr "S??lectionner choix 4"
 
-#: engines/twp/metaengine.cpp:172
+#: engines/twp/metaengine.cpp:171
 msgid "Select choice 5"
 msgstr "S??lectionner choix 5"
 
-#: engines/twp/metaengine.cpp:173
+#: engines/twp/metaengine.cpp:172
 msgid "Select choice 6"
 msgstr "S??lectionner choix 6"
 
-#: engines/twp/metaengine.cpp:174
+#: engines/twp/metaengine.cpp:173
 msgid "Select next Actor"
 msgstr "S??lectionner acteur suivant"
 
-#: engines/twp/metaengine.cpp:175
+#: engines/twp/metaengine.cpp:174
 msgid "Select previous actor"
 msgstr "S??lectionner acteur pr??c??dent"
 
-#: engines/twp/metaengine.cpp:177
+#: engines/twp/metaengine.cpp:176
 msgid "Show hotspots"
 msgstr "Afficher les points sensibles"
 
diff --git a/po/gl_ES.po b/po/gl_ES.po
index 7ace54dad7a..006236a1a7a 100644
--- a/po/gl_ES.po
+++ b/po/gl_ES.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-13 13:52+0000\n"
+"POT-Creation-Date: 2026-06-14 18:41+0000\n"
 "PO-Revision-Date: 2020-09-26 21:13+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Galician <https://translations.scummvm.org/projects/scummvm/"
@@ -78,7 +78,7 @@ msgstr "Motores dispo??ibles:"
 #: engines/scumm/help.cpp:141 engines/scumm/help.cpp:166
 #: engines/scumm/help.cpp:194 engines/scumm/help.cpp:212
 #: engines/sherlock/metaengine.cpp:430 engines/supernova/metaengine.cpp:341
-#: engines/tot/metaengine.cpp:149 engines/twp/metaengine.cpp:179
+#: engines/tot/metaengine.cpp:149 engines/twp/metaengine.cpp:178
 msgid "Close"
 msgstr "Pechar"
 
@@ -1072,7 +1072,7 @@ msgstr "Interpolaci??n:"
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
 #: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1124
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
-#: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
+#: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:153
 #: engines/ultima/ultima0/metaengine.cpp:45
 #: engines/ultima/ultima4/metaengine.cpp:44
 #: engines/ultima/ultima4/metaengine.cpp:139
@@ -1093,7 +1093,7 @@ msgstr "Arriba"
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
 #: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1091
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
-#: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
+#: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:45
 #: engines/ultima/ultima4/metaengine.cpp:140
@@ -1114,7 +1114,7 @@ msgstr "Abaixo"
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
 #: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1104
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
-#: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
+#: engines/twp/metaengine.cpp:151 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
 #: engines/ultima/ultima4/metaengine.cpp:151
 #: engines/wintermute/keymapper_tables.h:320
@@ -1134,7 +1134,7 @@ msgstr "Esquerda"
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
 #: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1111
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
-#: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
+#: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
 #: engines/ultima/ultima4/metaengine.cpp:152
 #: engines/wintermute/keymapper_tables.h:326
@@ -3302,7 +3302,7 @@ msgstr "<Valor err??neo>"
 
 #. I18N: Abbreviation for 'bytes' as data size
 #. I18N: Hardware key
-#: common/util.cpp:183 backends/platform/3ds/osystem-events.cpp:46
+#: common/util.cpp:190 backends/platform/3ds/osystem-events.cpp:46
 #: backends/platform/ds/osystem_ds.cpp:178
 #: backends/platform/sdl/miyoo/miyoo.cpp:61
 #: backends/platform/sdl/miyoo/miyoo.cpp:88
@@ -3312,23 +3312,23 @@ msgid "B"
 msgstr ""
 
 #. I18N: Abbreviation for 'kilobytes' as data size
-#: common/util.cpp:189
+#: common/util.cpp:196
 msgid "KB"
 msgstr ""
 
 #. I18N: Abbreviation for 'megabytes' as data size
-#: common/util.cpp:194
+#: common/util.cpp:201
 #, fuzzy
 msgid "MB"
 msgstr "SMB"
 
 #. I18N: Abbreviation for 'gigabytes' as data size
-#: common/util.cpp:200
+#: common/util.cpp:207
 msgid "GB"
 msgstr ""
 
 #. I18N: Abbreviation for 'terabytes' as data size
-#: common/util.cpp:206
+#: common/util.cpp:213
 msgid "TB"
 msgstr ""
 
@@ -6190,7 +6190,7 @@ msgstr "Mirar"
 #: engines/scumm/help.cpp:165 engines/scumm/help.cpp:193
 #: engines/scumm/help.cpp:211 engines/sherlock/metaengine.cpp:425
 #: engines/supernova/metaengine.cpp:336 engines/tot/metaengine.cpp:144
-#: engines/twp/metaengine.cpp:178
+#: engines/twp/metaengine.cpp:177
 msgid "Open"
 msgstr "Abrir"
 
@@ -6212,7 +6212,7 @@ msgstr "Coller"
 #: engines/sherlock/metaengine.cpp:440 engines/sherlock/metaengine.cpp:561
 #: engines/sky/metaengine.cpp:95 engines/sludge/keymapper_tables.h:333
 #: engines/supernova/metaengine.cpp:356 engines/tot/metaengine.cpp:139
-#: engines/tsage/metaengine.cpp:211 engines/twp/metaengine.cpp:186
+#: engines/tsage/metaengine.cpp:211 engines/twp/metaengine.cpp:185
 #: engines/ultima/ultima4/metaengine.cpp:74
 #: engines/ultima/nuvie/metaengine.cpp:69
 #: engines/wintermute/keymapper_tables.h:140
@@ -6992,7 +6992,7 @@ msgstr "Activa"
 
 #: engines/alcachofa/metaengine.cpp:104 engines/queen/metaengine.cpp:279
 #: engines/saga/metaengine.cpp:354 engines/scumm/help.cpp:202
-#: engines/scumm/help.cpp:214 engines/twp/metaengine.cpp:182
+#: engines/scumm/help.cpp:214 engines/twp/metaengine.cpp:181
 #: engines/wintermute/keymapper_tables.h:122
 #: engines/wintermute/keymapper_tables.h:1534
 msgid "Look at"
@@ -7356,7 +7356,7 @@ msgstr "Activar/desactivar captura de rato"
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:210
 #: engines/phoenixvr/metaengine.cpp:94 engines/scumm/help.cpp:79
 #: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
-#: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
+#: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:161
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
 msgstr "Omitir secuencia"
@@ -8270,7 +8270,7 @@ msgstr ""
 #. I18N: Perform action on object where cursor points
 #: engines/darkseed/metaengine.cpp:42 engines/dragons/metaengine.cpp:135
 #: engines/phoenixvr/metaengine.cpp:75 engines/tinsel/metaengine.cpp:294
-#: engines/twp/metaengine.cpp:150
+#: engines/twp/metaengine.cpp:149
 #, fuzzy
 msgid "Action"
 msgstr "Activa"
@@ -8763,7 +8763,7 @@ msgstr ""
 #: engines/scumm/help.cpp:139 engines/scumm/help.cpp:164
 #: engines/scumm/help.cpp:200 engines/scumm/help.cpp:210
 #: engines/sherlock/metaengine.cpp:445 engines/sherlock/metaengine.cpp:566
-#: engines/supernova/metaengine.cpp:366 engines/twp/metaengine.cpp:180
+#: engines/supernova/metaengine.cpp:366 engines/twp/metaengine.cpp:179
 msgid "Give"
 msgstr "Dar"
 
@@ -12328,7 +12328,7 @@ msgstr ""
 msgid "Move / Skip / Use default action with inventory item"
 msgstr ""
 
-#: engines/queen/metaengine.cpp:198 engines/twp/metaengine.cpp:176
+#: engines/queen/metaengine.cpp:198 engines/twp/metaengine.cpp:175
 #, fuzzy
 #| msgid "Skip text"
 msgid "Skip text"
@@ -12384,7 +12384,7 @@ msgstr "Mostrar todo"
 #: engines/scumm/help.cpp:144 engines/scumm/help.cpp:169
 #: engines/scumm/help.cpp:198 engines/scumm/help.cpp:213
 #: engines/scumm/help.cpp:230 engines/sherlock/metaengine.cpp:420
-#: engines/sludge/keymapper_tables.h:318 engines/twp/metaengine.cpp:181
+#: engines/sludge/keymapper_tables.h:318 engines/twp/metaengine.cpp:180
 #: engines/wintermute/keymapper_tables.h:134
 #: engines/wintermute/keymapper_tables.h:1546
 msgid "Pick up"
@@ -12393,7 +12393,7 @@ msgstr "Coller"
 #: engines/queen/metaengine.cpp:289 engines/saga/metaengine.cpp:364
 #: engines/scumm/help.cpp:199 engines/scumm/help.cpp:215
 #: engines/scumm/help.cpp:251 engines/sludge/keymapper_tables.h:323
-#: engines/twp/metaengine.cpp:183 engines/wintermute/keymapper_tables.h:128
+#: engines/twp/metaengine.cpp:182 engines/wintermute/keymapper_tables.h:128
 #: engines/wintermute/keymapper_tables.h:1558
 msgid "Talk to"
 msgstr "Falar con"
@@ -12503,7 +12503,7 @@ msgstr "Ir a"
 
 #: engines/saga/metaengine.cpp:389 engines/scumm/help.cpp:122
 #: engines/scumm/help.cpp:137 engines/scumm/help.cpp:162
-#: engines/supernova/metaengine.cpp:346 engines/twp/metaengine.cpp:184
+#: engines/supernova/metaengine.cpp:346 engines/twp/metaengine.cpp:183
 msgid "Push"
 msgstr "Empuxar"
 
@@ -13321,7 +13321,7 @@ msgstr "Controis principais de xogo:"
 
 #: engines/scumm/help.cpp:123 engines/scumm/help.cpp:138
 #: engines/scumm/help.cpp:163 engines/supernova/metaengine.cpp:351
-#: engines/twp/metaengine.cpp:185
+#: engines/twp/metaengine.cpp:184
 msgid "Pull"
 msgstr "Tirar de"
 
@@ -15725,112 +15725,112 @@ msgstr ""
 msgid "Ransome *unbeeped* (DLC)"
 msgstr ""
 
-#: engines/twp/metaengine.cpp:151
+#: engines/twp/metaengine.cpp:150
 #, fuzzy
 msgid "Select highlighted verb"
 msgstr "Seleccionar tema"
 
-#: engines/twp/metaengine.cpp:156
+#: engines/twp/metaengine.cpp:155
 #, fuzzy
 #| msgid "Go to previous directory level"
 msgid "Go to previous object"
 msgstr "Ir ao directorio superior"
 
-#: engines/twp/metaengine.cpp:157
+#: engines/twp/metaengine.cpp:156
 #, fuzzy
 msgid "Go to next object"
 msgstr "Opci??ns"
 
-#: engines/twp/metaengine.cpp:158
+#: engines/twp/metaengine.cpp:157
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Select verb / item left"
 msgstr "Esvarar ?? dereita"
 
-#: engines/twp/metaengine.cpp:159
+#: engines/twp/metaengine.cpp:158
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Select verb / item right"
 msgstr "Esvarar ?? dereita"
 
-#: engines/twp/metaengine.cpp:160
+#: engines/twp/metaengine.cpp:159
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Select verb / item up"
 msgstr "Esvarar ?? dereita"
 
-#: engines/twp/metaengine.cpp:161
+#: engines/twp/metaengine.cpp:160
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Select verb / item down"
 msgstr "Esvarar ?? dereita"
 
-#: engines/twp/metaengine.cpp:163
+#: engines/twp/metaengine.cpp:162
 #, fuzzy
 msgid "Select actor 1"
 msgstr "Acci??n/Seleccionar"
 
-#: engines/twp/metaengine.cpp:164
+#: engines/twp/metaengine.cpp:163
 #, fuzzy
 msgid "Select actor 2"
 msgstr "Acci??n/Seleccionar"
 
-#: engines/twp/metaengine.cpp:165
+#: engines/twp/metaengine.cpp:164
 #, fuzzy
 msgid "Select actor 3"
 msgstr "Acci??n/Seleccionar"
 
-#: engines/twp/metaengine.cpp:166
+#: engines/twp/metaengine.cpp:165
 #, fuzzy
 msgid "Select actor 4"
 msgstr "Acci??n/Seleccionar"
 
-#: engines/twp/metaengine.cpp:167
+#: engines/twp/metaengine.cpp:166
 #, fuzzy
 msgid "Select actor 5"
 msgstr "Acci??n/Seleccionar"
 
-#: engines/twp/metaengine.cpp:168
+#: engines/twp/metaengine.cpp:167
 #, fuzzy
 msgid "Select choice 1"
 msgstr "Seleccionar tema"
 
-#: engines/twp/metaengine.cpp:169
+#: engines/twp/metaengine.cpp:168
 #, fuzzy
 msgid "Select choice 2"
 msgstr "Seleccionar tema"
 
-#: engines/twp/metaengine.cpp:170
+#: engines/twp/metaengine.cpp:169
 #, fuzzy
 msgid "Select choice 3"
 msgstr "Seleccionar tema"
 
-#: engines/twp/metaengine.cpp:171
+#: engines/twp/metaengine.cpp:170
 #, fuzzy
 msgid "Select choice 4"
 msgstr "Seleccionar tema"
 
-#: engines/twp/metaengine.cpp:172
+#: engines/twp/metaengine.cpp:171
 #, fuzzy
 msgid "Select choice 5"
 msgstr "Seleccionar tema"
 
-#: engines/twp/metaengine.cpp:173
+#: engines/twp/metaengine.cpp:172
 #, fuzzy
 msgid "Select choice 6"
 msgstr "Seleccionar tema"
 
-#: engines/twp/metaengine.cpp:174
+#: engines/twp/metaengine.cpp:173
 #, fuzzy
 msgid "Select next Actor"
 msgstr "Seleccionar tema"
 
-#: engines/twp/metaengine.cpp:175
+#: engines/twp/metaengine.cpp:174
 #, fuzzy
 msgid "Select previous actor"
 msgstr "~A~nterior"
 
-#: engines/twp/metaengine.cpp:177
+#: engines/twp/metaengine.cpp:176
 #, fuzzy
 msgid "Show hotspots"
 msgstr "Mostrar ficheiros ocultos"
diff --git a/po/he.po b/po/he.po
index 058995fa892..3b7433e729f 100644
--- a/po/he.po
+++ b/po/he.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.1.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-13 13:52+0000\n"
+"POT-Creation-Date: 2026-06-14 18:41+0000\n"
 "PO-Revision-Date: 2025-04-09 20:18+0000\n"
 "Last-Translator: Niv Baehr <bloop93 at gmail.com>\n"
 "Language-Team: Hebrew <https://translations.scummvm.org/projects/scummvm/"
@@ -74,7 +74,7 @@ msgstr "???????????? ????????????:"
 #: engines/scumm/help.cpp:141 engines/scumm/help.cpp:166
 #: engines/scumm/help.cpp:194 engines/scumm/help.cpp:212
 #: engines/sherlock/metaengine.cpp:430 engines/supernova/metaengine.cpp:341
-#: engines/tot/metaengine.cpp:149 engines/twp/metaengine.cpp:179
+#: engines/tot/metaengine.cpp:149 engines/twp/metaengine.cpp:178
 msgid "Close"
 msgstr "??????????"
 
@@ -1035,7 +1035,7 @@ msgstr "??????????"
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
 #: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1124
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
-#: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
+#: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:153
 #: engines/ultima/ultima0/metaengine.cpp:45
 #: engines/ultima/ultima4/metaengine.cpp:44
 #: engines/ultima/ultima4/metaengine.cpp:139
@@ -1056,7 +1056,7 @@ msgstr "??????????"
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
 #: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1091
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
-#: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
+#: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:45
 #: engines/ultima/ultima4/metaengine.cpp:140
@@ -1077,7 +1077,7 @@ msgstr "????????"
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
 #: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1104
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
-#: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
+#: engines/twp/metaengine.cpp:151 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
 #: engines/ultima/ultima4/metaengine.cpp:151
 #: engines/wintermute/keymapper_tables.h:320
@@ -1097,7 +1097,7 @@ msgstr "??????????"
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
 #: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1111
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
-#: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
+#: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
 #: engines/ultima/ultima4/metaengine.cpp:152
 #: engines/wintermute/keymapper_tables.h:326
@@ -3244,7 +3244,7 @@ msgstr "<?????? ???? ????????>"
 
 #. I18N: Abbreviation for 'bytes' as data size
 #. I18N: Hardware key
-#: common/util.cpp:183 backends/platform/3ds/osystem-events.cpp:46
+#: common/util.cpp:190 backends/platform/3ds/osystem-events.cpp:46
 #: backends/platform/ds/osystem_ds.cpp:178
 #: backends/platform/sdl/miyoo/miyoo.cpp:61
 #: backends/platform/sdl/miyoo/miyoo.cpp:88
@@ -3254,22 +3254,22 @@ msgid "B"
 msgstr "B"
 
 #. I18N: Abbreviation for 'kilobytes' as data size
-#: common/util.cpp:189
+#: common/util.cpp:196
 msgid "KB"
 msgstr "KB"
 
 #. I18N: Abbreviation for 'megabytes' as data size
-#: common/util.cpp:194
+#: common/util.cpp:201
 msgid "MB"
 msgstr "MB"
 
 #. I18N: Abbreviation for 'gigabytes' as data size
-#: common/util.cpp:200
+#: common/util.cpp:207
 msgid "GB"
 msgstr "GB"
 
 #. I18N: Abbreviation for 'terabytes' as data size
-#: common/util.cpp:206
+#: common/util.cpp:213
 msgid "TB"
 msgstr "TB"
 
@@ -6149,7 +6149,7 @@ msgstr "????????"
 #: engines/scumm/help.cpp:165 engines/scumm/help.cpp:193
 #: engines/scumm/help.cpp:211 engines/sherlock/metaengine.cpp:425
 #: engines/supernova/metaengine.cpp:336 engines/tot/metaengine.cpp:144
-#: engines/twp/metaengine.cpp:178
+#: engines/twp/metaengine.cpp:177
 msgid "Open"
 msgstr "????????"
 
@@ -6171,7 +6171,7 @@ msgstr "????????"
 #: engines/sherlock/metaengine.cpp:440 engines/sherlock/metaengine.cpp:561
 #: engines/sky/metaengine.cpp:95 engines/sludge/keymapper_tables.h:333
 #: engines/supernova/metaengine.cpp:356 engines/tot/metaengine.cpp:139
-#: engines/tsage/metaengine.cpp:211 engines/twp/metaengine.cpp:186
+#: engines/tsage/metaengine.cpp:211 engines/twp/metaengine.cpp:185
 #: engines/ultima/ultima4/metaengine.cpp:74
 #: engines/ultima/nuvie/metaengine.cpp:69
 #: engines/wintermute/keymapper_tables.h:140
@@ -6971,7 +6971,7 @@ msgstr "????????"
 
 #: engines/alcachofa/metaengine.cpp:104 engines/queen/metaengine.cpp:279
 #: engines/saga/metaengine.cpp:354 engines/scumm/help.cpp:202
-#: engines/scumm/help.cpp:214 engines/twp/metaengine.cpp:182
+#: engines/scumm/help.cpp:214 engines/twp/metaengine.cpp:181
 #: engines/wintermute/keymapper_tables.h:122
 #: engines/wintermute/keymapper_tables.h:1534
 msgid "Look at"
@@ -7339,7 +7339,7 @@ msgstr "?????????? ??????????"
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:210
 #: engines/phoenixvr/metaengine.cpp:94 engines/scumm/help.cpp:79
 #: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
-#: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
+#: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:161
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
 msgstr "?????????? ???? ???????????? ????????"
@@ -8268,7 +8268,7 @@ msgstr ""
 #. I18N: Perform action on object where cursor points
 #: engines/darkseed/metaengine.cpp:42 engines/dragons/metaengine.cpp:135
 #: engines/phoenixvr/metaengine.cpp:75 engines/tinsel/metaengine.cpp:294
-#: engines/twp/metaengine.cpp:150
+#: engines/twp/metaengine.cpp:149
 msgid "Action"
 msgstr "??????????"
 
@@ -8813,7 +8813,7 @@ msgstr ""
 #: engines/scumm/help.cpp:139 engines/scumm/help.cpp:164
 #: engines/scumm/help.cpp:200 engines/scumm/help.cpp:210
 #: engines/sherlock/metaengine.cpp:445 engines/sherlock/metaengine.cpp:566
-#: engines/supernova/metaengine.cpp:366 engines/twp/metaengine.cpp:180
+#: engines/supernova/metaengine.cpp:366 engines/twp/metaengine.cpp:179
 msgid "Give"
 msgstr "????"
 
@@ -12510,7 +12510,7 @@ msgstr "?????????? ?????????? ?????????? ?????????? ????????????"
 msgid "Move / Skip / Use default action with inventory item"
 msgstr ""
 
-#: engines/queen/metaengine.cpp:198 engines/twp/metaengine.cpp:176
+#: engines/queen/metaengine.cpp:198 engines/twp/metaengine.cpp:175
 #, fuzzy
 #| msgid "Skip text"
 msgid "Skip text"
@@ -12568,7 +12568,7 @@ msgstr "???????? ????????"
 #: engines/scumm/help.cpp:144 engines/scumm/help.cpp:169
 #: engines/scumm/help.cpp:198 engines/scumm/help.cpp:213
 #: engines/scumm/help.cpp:230 engines/sherlock/metaengine.cpp:420
-#: engines/sludge/keymapper_tables.h:318 engines/twp/metaengine.cpp:181
+#: engines/sludge/keymapper_tables.h:318 engines/twp/metaengine.cpp:180
 #: engines/wintermute/keymapper_tables.h:134
 #: engines/wintermute/keymapper_tables.h:1546
 msgid "Pick up"
@@ -12577,7 +12577,7 @@ msgstr "??????????"
 #: engines/queen/metaengine.cpp:289 engines/saga/metaengine.cpp:364
 #: engines/scumm/help.cpp:199 engines/scumm/help.cpp:215
 #: engines/scumm/help.cpp:251 engines/sludge/keymapper_tables.h:323
-#: engines/twp/metaengine.cpp:183 engines/wintermute/keymapper_tables.h:128
+#: engines/twp/metaengine.cpp:182 engines/wintermute/keymapper_tables.h:128
 #: engines/wintermute/keymapper_tables.h:1558
 msgid "Talk to"
 msgstr "???????? ??"
@@ -12692,7 +12692,7 @@ msgstr "???????? ??"
 
 #: engines/saga/metaengine.cpp:389 engines/scumm/help.cpp:122
 #: engines/scumm/help.cpp:137 engines/scumm/help.cpp:162
-#: engines/supernova/metaengine.cpp:346 engines/twp/metaengine.cpp:184
+#: engines/supernova/metaengine.cpp:346 engines/twp/metaengine.cpp:183
 msgid "Push"
 msgstr "??????????"
 
@@ -13532,7 +13532,7 @@ msgstr "?????????? ???????? ??????????????:"
 
 #: engines/scumm/help.cpp:123 engines/scumm/help.cpp:138
 #: engines/scumm/help.cpp:163 engines/supernova/metaengine.cpp:351
-#: engines/twp/metaengine.cpp:185
+#: engines/twp/metaengine.cpp:184
 msgid "Pull"
 msgstr "????????"
 
@@ -16065,127 +16065,127 @@ msgstr ""
 msgid "Ransome *unbeeped* (DLC)"
 msgstr ""
 
-#: engines/twp/metaengine.cpp:151
+#: engines/twp/metaengine.cpp:150
 #, fuzzy
 #| msgid "Select shader"
 msgid "Select highlighted verb"
 msgstr "?????????? ????????"
 
-#: engines/twp/metaengine.cpp:156
+#: engines/twp/metaengine.cpp:155
 #, fuzzy
 #| msgid "Go to previous directory level"
 msgid "Go to previous object"
 msgstr "???????? ???? ?????? ???????????? ????????????"
 
-#: engines/twp/metaengine.cpp:157
+#: engines/twp/metaengine.cpp:156
 #, fuzzy
 #| msgid "Go to setup menu"
 msgid "Go to next object"
 msgstr "???????? ???????????? ??????????"
 
-#: engines/twp/metaengine.cpp:158
+#: engines/twp/metaengine.cpp:157
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Select verb / item left"
 msgstr "???????? ??????????"
 
-#: engines/twp/metaengine.cpp:159
+#: engines/twp/metaengine.cpp:158
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Select verb / item right"
 msgstr "???????? ??????????"
 
-#: engines/twp/metaengine.cpp:160
+#: engines/twp/metaengine.cpp:159
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Select verb / item up"
 msgstr "???????? ??????????"
 
-#: engines/twp/metaengine.cpp:161
+#: engines/twp/metaengine.cpp:160
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Select verb / item down"
 msgstr "???????? ??????????"
 
-#: engines/twp/metaengine.cpp:163
+#: engines/twp/metaengine.cpp:162
 #, fuzzy
 #| msgid "Select Group by"
 msgid "Select actor 1"
 msgstr "???? ?????????? ?????????? ??????"
 
-#: engines/twp/metaengine.cpp:164
+#: engines/twp/metaengine.cpp:163
 #, fuzzy
 #| msgid "Select Group by"
 msgid "Select actor 2"
 msgstr "???? ?????????? ?????????? ??????"
 
-#: engines/twp/metaengine.cpp:165
+#: engines/twp/metaengine.cpp:164
 #, fuzzy
 #| msgid "Select Group by"
 msgid "Select actor 3"
 msgstr "???? ?????????? ?????????? ??????"
 
-#: engines/twp/metaengine.cpp:166
+#: engines/twp/metaengine.cpp:165
 #, fuzzy
 #| msgid "Select Group by"
 msgid "Select actor 4"
 msgstr "???? ?????????? ?????????? ??????"
 
-#: engines/twp/metaengine.cpp:167
+#: engines/twp/metaengine.cpp:166
 #, fuzzy
 #| msgid "Select Group by"
 msgid "Select actor 5"
 msgstr "???? ?????????? ?????????? ??????"
 
-#: engines/twp/metaengine.cpp:168
+#: engines/twp/metaengine.cpp:167
 #, fuzzy
 #| msgid "Select shader"
 msgid "Select choice 1"
 msgstr "?????????? ????????"
 
-#: engines/twp/metaengine.cpp:169
+#: engines/twp/metaengine.cpp:168
 #, fuzzy
 #| msgid "Select shader"
 msgid "Select choice 2"
 msgstr "?????????? ????????"
 
-#: engines/twp/metaengine.cpp:170
+#: engines/twp/metaengine.cpp:169
 #, fuzzy
 #| msgid "Select shader"
 msgid "Select choice 3"
 msgstr "?????????? ????????"
 
-#: engines/twp/metaengine.cpp:171
+#: engines/twp/metaengine.cpp:170
 #, fuzzy
 #| msgid "Select shader"
 msgid "Select choice 4"
 msgstr "?????????? ????????"
 
-#: engines/twp/metaengine.cpp:172
+#: engines/twp/metaengine.cpp:171
 #, fuzzy
 #| msgid "Select shader"
 msgid "Select choice 5"
 msgstr "?????????? ????????"
 
-#: engines/twp/metaengine.cpp:173
+#: engines/twp/metaengine.cpp:172
 #, fuzzy
 #| msgid "Select shader"
 msgid "Select choice 6"
 msgstr "?????????? ????????"
 
-#: engines/twp/metaengine.cpp:174
+#: engines/twp/metaengine.cpp:173
 #, fuzzy
 #| msgid "Select next music record"
 msgid "Select next Actor"
 msgstr "?????????? ?????????? ?????????????? ??????"
 
-#: engines/twp/metaengine.cpp:175
+#: engines/twp/metaengine.cpp:174
 #, fuzzy
 #| msgid "Previous action"
 msgid "Select previous actor"
 msgstr "?????????? ??????????"
 
-#: engines/twp/metaengine.cpp:177
+#: engines/twp/metaengine.cpp:176
 #, fuzzy
 #| msgid "Show hints"
 msgid "Show hotspots"
diff --git a/po/hi.po b/po/hi.po
index 93938829f5d..e74f55d3f4b 100644
--- a/po/hi.po
+++ b/po/hi.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-13 13:52+0000\n"
+"POT-Creation-Date: 2026-06-14 18:41+0000\n"
 "PO-Revision-Date: 2026-05-12 22:22+0000\n"
 "Last-Translator: KARTIK SAXENA <kartiksaxena2005 at gmail.com>\n"
 "Language-Team: Hindi <https://translations.scummvm.org/projects/scummvm/"
@@ -72,7 +72,7 @@ msgstr "?????????????????? ???????????? :"
 #: engines/scumm/help.cpp:141 engines/scumm/help.cpp:166
 #: engines/scumm/help.cpp:194 engines/scumm/help.cpp:212
 #: engines/sherlock/metaengine.cpp:430 engines/supernova/metaengine.cpp:341
-#: engines/tot/metaengine.cpp:149 engines/twp/metaengine.cpp:179
+#: engines/tot/metaengine.cpp:149 engines/twp/metaengine.cpp:178
 msgid "Close"
 msgstr "????????? ????????????"
 
@@ -1044,7 +1044,7 @@ msgstr ""
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
 #: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1124
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
-#: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
+#: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:153
 #: engines/ultima/ultima0/metaengine.cpp:45
 #: engines/ultima/ultima4/metaengine.cpp:44
 #: engines/ultima/ultima4/metaengine.cpp:139
@@ -1065,7 +1065,7 @@ msgstr "?????????"
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
 #: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1091
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
-#: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
+#: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:45
 #: engines/ultima/ultima4/metaengine.cpp:140
@@ -1086,7 +1086,7 @@ msgstr "????????????"
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
 #: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1104
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
-#: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
+#: engines/twp/metaengine.cpp:151 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
 #: engines/ultima/ultima4/metaengine.cpp:151
 #: engines/wintermute/keymapper_tables.h:320
@@ -1106,7 +1106,7 @@ msgstr "????????????"
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
 #: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1111
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
-#: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
+#: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
 #: engines/ultima/ultima4/metaengine.cpp:152
 #: engines/wintermute/keymapper_tables.h:326
@@ -3212,7 +3212,7 @@ msgstr ""
 
 #. I18N: Abbreviation for 'bytes' as data size
 #. I18N: Hardware key
-#: common/util.cpp:183 backends/platform/3ds/osystem-events.cpp:46
+#: common/util.cpp:190 backends/platform/3ds/osystem-events.cpp:46
 #: backends/platform/ds/osystem_ds.cpp:178
 #: backends/platform/sdl/miyoo/miyoo.cpp:61
 #: backends/platform/sdl/miyoo/miyoo.cpp:88
@@ -3222,22 +3222,22 @@ msgid "B"
 msgstr ""
 
 #. I18N: Abbreviation for 'kilobytes' as data size
-#: common/util.cpp:189
+#: common/util.cpp:196
 msgid "KB"
 msgstr ""
 
 #. I18N: Abbreviation for 'megabytes' as data size
-#: common/util.cpp:194
+#: common/util.cpp:201
 msgid "MB"
 msgstr ""
 
 #. I18N: Abbreviation for 'gigabytes' as data size
-#: common/util.cpp:200
+#: common/util.cpp:207
 msgid "GB"
 msgstr ""
 
 #. I18N: Abbreviation for 'terabytes' as data size
-#: common/util.cpp:206
+#: common/util.cpp:213
 msgid "TB"
 msgstr ""
 
@@ -5971,7 +5971,7 @@ msgstr "????????????"
 #: engines/scumm/help.cpp:165 engines/scumm/help.cpp:193
 #: engines/scumm/help.cpp:211 engines/sherlock/metaengine.cpp:425
 #: engines/supernova/metaengine.cpp:336 engines/tot/metaengine.cpp:144
-#: engines/twp/metaengine.cpp:178
+#: engines/twp/metaengine.cpp:177
 msgid "Open"
 msgstr ""
 
@@ -5993,7 +5993,7 @@ msgstr ""
 #: engines/sherlock/metaengine.cpp:440 engines/sherlock/metaengine.cpp:561
 #: engines/sky/metaengine.cpp:95 engines/sludge/keymapper_tables.h:333
 #: engines/supernova/metaengine.cpp:356 engines/tot/metaengine.cpp:139
-#: engines/tsage/metaengine.cpp:211 engines/twp/metaengine.cpp:186
+#: engines/tsage/metaengine.cpp:211 engines/twp/metaengine.cpp:185
 #: engines/ultima/ultima4/metaengine.cpp:74
 #: engines/ultima/nuvie/metaengine.cpp:69
 #: engines/wintermute/keymapper_tables.h:140
@@ -6721,7 +6721,7 @@ msgstr "??????????????????"
 
 #: engines/alcachofa/metaengine.cpp:104 engines/queen/metaengine.cpp:279
 #: engines/saga/metaengine.cpp:354 engines/scumm/help.cpp:202
-#: engines/scumm/help.cpp:214 engines/twp/metaengine.cpp:182
+#: engines/scumm/help.cpp:214 engines/twp/metaengine.cpp:181
 #: engines/wintermute/keymapper_tables.h:122
 #: engines/wintermute/keymapper_tables.h:1534
 msgid "Look at"
@@ -7069,7 +7069,7 @@ msgstr "???????????????????????? ?????????"
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:210
 #: engines/phoenixvr/metaengine.cpp:94 engines/scumm/help.cpp:79
 #: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
-#: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
+#: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:161
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
 msgstr ""
@@ -7948,7 +7948,7 @@ msgstr ""
 #. I18N: Perform action on object where cursor points
 #: engines/darkseed/metaengine.cpp:42 engines/dragons/metaengine.cpp:135
 #: engines/phoenixvr/metaengine.cpp:75 engines/tinsel/metaengine.cpp:294
-#: engines/twp/metaengine.cpp:150
+#: engines/twp/metaengine.cpp:149
 msgid "Action"
 msgstr ""
 
@@ -8441,7 +8441,7 @@ msgstr ""
 #: engines/scumm/help.cpp:139 engines/scumm/help.cpp:164
 #: engines/scumm/help.cpp:200 engines/scumm/help.cpp:210
 #: engines/sherlock/metaengine.cpp:445 engines/sherlock/metaengine.cpp:566
-#: engines/supernova/metaengine.cpp:366 engines/twp/metaengine.cpp:180
+#: engines/supernova/metaengine.cpp:366 engines/twp/metaengine.cpp:179
 msgid "Give"
 msgstr ""
 
@@ -11855,7 +11855,7 @@ msgstr ""
 msgid "Move / Skip / Use default action with inventory item"
 msgstr ""
 
-#: engines/queen/metaengine.cpp:198 engines/twp/metaengine.cpp:176
+#: engines/queen/metaengine.cpp:198 engines/twp/metaengine.cpp:175
 #, fuzzy
 #| msgid "Skip autosave"
 msgid "Skip text"
@@ -11905,7 +11905,7 @@ msgstr ""
 #: engines/scumm/help.cpp:144 engines/scumm/help.cpp:169
 #: engines/scumm/help.cpp:198 engines/scumm/help.cpp:213
 #: engines/scumm/help.cpp:230 engines/sherlock/metaengine.cpp:420
-#: engines/sludge/keymapper_tables.h:318 engines/twp/metaengine.cpp:181
+#: engines/sludge/keymapper_tables.h:318 engines/twp/metaengine.cpp:180
 #: engines/wintermute/keymapper_tables.h:134
 #: engines/wintermute/keymapper_tables.h:1546
 msgid "Pick up"
@@ -11914,7 +11914,7 @@ msgstr ""
 #: engines/queen/metaengine.cpp:289 engines/saga/metaengine.cpp:364
 #: engines/scumm/help.cpp:199 engines/scumm/help.cpp:215
 #: engines/scumm/help.cpp:251 engines/sludge/keymapper_tables.h:323
-#: engines/twp/metaengine.cpp:183 engines/wintermute/keymapper_tables.h:128
+#: engines/twp/metaengine.cpp:182 engines/wintermute/keymapper_tables.h:128
 #: engines/wintermute/keymapper_tables.h:1558
 msgid "Talk to"
 msgstr ""
@@ -12025,7 +12025,7 @@ msgstr ""
 
 #: engines/saga/metaengine.cpp:389 engines/scumm/help.cpp:122
 #: engines/scumm/help.cpp:137 engines/scumm/help.cpp:162
-#: engines/supernova/metaengine.cpp:346 engines/twp/metaengine.cpp:184
+#: engines/supernova/metaengine.cpp:346 engines/twp/metaengine.cpp:183
 msgid "Push"
 msgstr ""
 
@@ -12803,7 +12803,7 @@ msgstr "??????????????? ????????? ?????? ?????????????????????:"
 
 #: engines/scumm/help.cpp:123 engines/scumm/help.cpp:138
 #: engines/scumm/help.cpp:163 engines/supernova/metaengine.cpp:351
-#: engines/twp/metaengine.cpp:185
+#: engines/twp/metaengine.cpp:184
 msgid "Pull"
 msgstr ""
 
@@ -15167,124 +15167,124 @@ msgstr ""
 msgid "Ransome *unbeeped* (DLC)"
 msgstr ""
 
-#: engines/twp/metaengine.cpp:151
+#: engines/twp/metaengine.cpp:150
 #, fuzzy
 #| msgid "Select shader"
 msgid "Select highlighted verb"
 msgstr "Shader ???????????????"
 
-#: engines/twp/metaengine.cpp:156
+#: engines/twp/metaengine.cpp:155
 #, fuzzy
 #| msgid "Go to previous directory level"
 msgid "Go to previous object"
 msgstr "??????????????? ?????????????????? ???????????? ?????? ????????????"
 
-#: engines/twp/metaengine.cpp:157
+#: engines/twp/metaengine.cpp:156
 msgid "Go to next object"
 msgstr ""
 
-#: engines/twp/metaengine.cpp:158
+#: engines/twp/metaengine.cpp:157
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Select verb / item left"
 msgstr "?????????????????? ?????????????????? ?????????"
 
-#: engines/twp/metaengine.cpp:159
+#: engines/twp/metaengine.cpp:158
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Select verb / item right"
 msgstr "?????????????????? ?????????????????? ?????????"
 
-#: engines/twp/metaengine.cpp:160
+#: engines/twp/metaengine.cpp:159
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Select verb / item up"
 msgstr "?????????????????? ?????????????????? ?????????"
 
-#: engines/twp/metaengine.cpp:161
+#: engines/twp/metaengine.cpp:160
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Select verb / item down"
 msgstr "?????????????????? ?????????????????? ?????????"
 
-#: engines/twp/metaengine.cpp:163
+#: engines/twp/metaengine.cpp:162
 #, fuzzy
 #| msgid "Select shader"
 msgid "Select actor 1"
 msgstr "Shader ???????????????"
 
-#: engines/twp/metaengine.cpp:164
+#: engines/twp/metaengine.cpp:163
 #, fuzzy
 #| msgid "Select shader"
 msgid "Select actor 2"
 msgstr "Shader ???????????????"
 
-#: engines/twp/metaengine.cpp:165
+#: engines/twp/metaengine.cpp:164
 #, fuzzy
 #| msgid "Select shader"
 msgid "Select actor 3"
 msgstr "Shader ???????????????"
 
-#: engines/twp/metaengine.cpp:166
+#: engines/twp/metaengine.cpp:165
 #, fuzzy
 #| msgid "Select shader"
 msgid "Select actor 4"
 msgstr "Shader ???????????????"
 
-#: engines/twp/metaengine.cpp:167
+#: engines/twp/metaengine.cpp:166
 #, fuzzy
 #| msgid "Select shader"
 msgid "Select actor 5"
 msgstr "Shader ???????????????"
 
-#: engines/twp/metaengine.cpp:168
+#: engines/twp/metaengine.cpp:167
 #, fuzzy
 #| msgid "Select shader"
 msgid "Select choice 1"
 msgstr "Shader ???????????????"
 
-#: engines/twp/metaengine.cpp:169
+#: engines/twp/metaengine.cpp:168
 #, fuzzy
 #| msgid "Select shader"
 msgid "Select choice 2"
 msgstr "Shader ???????????????"
 
-#: engines/twp/metaengine.cpp:170
+#: engines/twp/metaengine.cpp:169
 #, fuzzy
 #| msgid "Select shader"
 msgid "Select choice 3"
 msgstr "Shader ???????????????"
 
-#: engines/twp/metaengine.cpp:171
+#: engines/twp/metaengine.cpp:170
 #, fuzzy
 #| msgid "Select shader"
 msgid "Select choice 4"
 msgstr "Shader ???????????????"
 
-#: engines/twp/metaengine.cpp:172
+#: engines/twp/metaengine.cpp:171
 #, fuzzy
 #| msgid "Select shader"
 msgid "Select choice 5"
 msgstr "Shader ???????????????"
 
-#: engines/twp/metaengine.cpp:173
+#: engines/twp/metaengine.cpp:172
 #, fuzzy
 #| msgid "Select shader"
 msgid "Select choice 6"
 msgstr "Shader ???????????????"
 
-#: engines/twp/metaengine.cpp:174
+#: engines/twp/metaengine.cpp:173
 #, fuzzy
 #| msgid "Select shader"
 msgid "Select next Actor"
 msgstr "Shader ???????????????"
 
-#: engines/twp/metaengine.cpp:175
+#: engines/twp/metaengine.cpp:174
 #, fuzzy
 msgid "Select previous actor"
 msgstr "??????????????? (P)"
 
-#: engines/twp/metaengine.cpp:177
+#: engines/twp/metaengine.cpp:176
 #, fuzzy
 msgid "Show hotspots"
 msgstr "???????????? ????????? files ??????????????????"
diff --git a/po/hu_HU.po b/po/hu_HU.po
index dbf5182102a..8e2b4d8fb24 100644
--- a/po/hu_HU.po
+++ b/po/hu_HU.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-13 13:52+0000\n"
+"POT-Creation-Date: 2026-06-14 18:41+0000\n"
 "PO-Revision-Date: 2025-03-07 00:18+0000\n"
 "Last-Translator: Hov??nszki Tam??s <gahex68379 at lofiey.com>\n"
 "Language-Team: Hungarian <https://translations.scummvm.org/projects/scummvm/"
@@ -73,7 +73,7 @@ msgstr "T??mogatott j??t??kmotorok:"
 #: engines/scumm/help.cpp:141 engines/scumm/help.cpp:166
 #: engines/scumm/help.cpp:194 engines/scumm/help.cpp:212
 #: engines/sherlock/metaengine.cpp:430 engines/supernova/metaengine.cpp:341
-#: engines/tot/metaengine.cpp:149 engines/twp/metaengine.cpp:179
+#: engines/tot/metaengine.cpp:149 engines/twp/metaengine.cpp:178
 msgid "Close"
 msgstr "Bez??r??s"
 
@@ -1055,7 +1055,7 @@ msgstr "Interakci??"
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
 #: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1124
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
-#: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
+#: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:153
 #: engines/ultima/ultima0/metaengine.cpp:45
 #: engines/ultima/ultima4/metaengine.cpp:44
 #: engines/ultima/ultima4/metaengine.cpp:139
@@ -1076,7 +1076,7 @@ msgstr "Fel"
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
 #: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1091
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
-#: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
+#: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:45
 #: engines/ultima/ultima4/metaengine.cpp:140
@@ -1097,7 +1097,7 @@ msgstr "Le"
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
 #: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1104
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
-#: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
+#: engines/twp/metaengine.cpp:151 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
 #: engines/ultima/ultima4/metaengine.cpp:151
 #: engines/wintermute/keymapper_tables.h:320
@@ -1117,7 +1117,7 @@ msgstr "Bal"
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
 #: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1111
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
-#: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
+#: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
 #: engines/ultima/ultima4/metaengine.cpp:152
 #: engines/wintermute/keymapper_tables.h:326
@@ -3402,7 +3402,7 @@ msgstr "<Rossz ??rt??k>"
 
 #. I18N: Abbreviation for 'bytes' as data size
 #. I18N: Hardware key
-#: common/util.cpp:183 backends/platform/3ds/osystem-events.cpp:46
+#: common/util.cpp:190 backends/platform/3ds/osystem-events.cpp:46
 #: backends/platform/ds/osystem_ds.cpp:178
 #: backends/platform/sdl/miyoo/miyoo.cpp:61
 #: backends/platform/sdl/miyoo/miyoo.cpp:88
@@ -3412,22 +3412,22 @@ msgid "B"
 msgstr "B"
 
 #. I18N: Abbreviation for 'kilobytes' as data size
-#: common/util.cpp:189
+#: common/util.cpp:196
 msgid "KB"
 msgstr "KB"
 
 #. I18N: Abbreviation for 'megabytes' as data size
-#: common/util.cpp:194
+#: common/util.cpp:201
 msgid "MB"
 msgstr "MB"
 
 #. I18N: Abbreviation for 'gigabytes' as data size
-#: common/util.cpp:200
+#: common/util.cpp:207
 msgid "GB"
 msgstr "GB"
 
 #. I18N: Abbreviation for 'terabytes' as data size
-#: common/util.cpp:206
+#: common/util.cpp:213
 msgid "TB"
 msgstr "TB"
 
@@ -6683,7 +6683,7 @@ msgstr "Megn??z"
 #: engines/scumm/help.cpp:165 engines/scumm/help.cpp:193
 #: engines/scumm/help.cpp:211 engines/sherlock/metaengine.cpp:425
 #: engines/supernova/metaengine.cpp:336 engines/tot/metaengine.cpp:144
-#: engines/twp/metaengine.cpp:178
+#: engines/twp/metaengine.cpp:177
 msgid "Open"
 msgstr "Kinyit"
 
@@ -6705,7 +6705,7 @@ msgstr "Vesz"
 #: engines/sherlock/metaengine.cpp:440 engines/sherlock/metaengine.cpp:561
 #: engines/sky/metaengine.cpp:95 engines/sludge/keymapper_tables.h:333
 #: engines/supernova/metaengine.cpp:356 engines/tot/metaengine.cpp:139
-#: engines/tsage/metaengine.cpp:211 engines/twp/metaengine.cpp:186
+#: engines/tsage/metaengine.cpp:211 engines/twp/metaengine.cpp:185
 #: engines/ultima/ultima4/metaengine.cpp:74
 #: engines/ultima/nuvie/metaengine.cpp:69
 #: engines/wintermute/keymapper_tables.h:140
@@ -7499,7 +7499,7 @@ msgstr "Aktiv??l"
 
 #: engines/alcachofa/metaengine.cpp:104 engines/queen/metaengine.cpp:279
 #: engines/saga/metaengine.cpp:354 engines/scumm/help.cpp:202
-#: engines/scumm/help.cpp:214 engines/twp/metaengine.cpp:182
+#: engines/scumm/help.cpp:214 engines/twp/metaengine.cpp:181
 #: engines/wintermute/keymapper_tables.h:122
 #: engines/wintermute/keymapper_tables.h:1534
 msgid "Look at"
@@ -7862,7 +7862,7 @@ msgstr "Harcmodor V??lt??s"
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:210
 #: engines/phoenixvr/metaengine.cpp:94 engines/scumm/help.cpp:79
 #: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
-#: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
+#: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:161
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
 msgstr "??tvezet?? ??tugr??sa"
@@ -8777,7 +8777,7 @@ msgstr ""
 #. I18N: Perform action on object where cursor points
 #: engines/darkseed/metaengine.cpp:42 engines/dragons/metaengine.cpp:135
 #: engines/phoenixvr/metaengine.cpp:75 engines/tinsel/metaengine.cpp:294
-#: engines/twp/metaengine.cpp:150
+#: engines/twp/metaengine.cpp:149
 msgid "Action"
 msgstr "M??velet"
 
@@ -9304,7 +9304,7 @@ msgstr ""
 #: engines/scumm/help.cpp:139 engines/scumm/help.cpp:164
 #: engines/scumm/help.cpp:200 engines/scumm/help.cpp:210
 #: engines/sherlock/metaengine.cpp:445 engines/sherlock/metaengine.cpp:566
-#: engines/supernova/metaengine.cpp:366 engines/twp/metaengine.cpp:180
+#: engines/supernova/metaengine.cpp:366 engines/twp/metaengine.cpp:179
 msgid "Give"
 msgstr "Ad"
 
@@ -12977,7 +12977,7 @@ msgstr "Haszn??ljon k??nnyebben olvashat?? egy??ni bet??t??pust"
 msgid "Move / Skip / Use default action with inventory item"
 msgstr ""
 
-#: engines/queen/metaengine.cpp:198 engines/twp/metaengine.cpp:176
+#: engines/queen/metaengine.cpp:198 engines/twp/metaengine.cpp:175
 #, fuzzy
 #| msgid "Skip Text"
 msgid "Skip text"
@@ -13035,7 +13035,7 @@ msgstr "Foly??irat megjelen??t??se"
 #: engines/scumm/help.cpp:144 engines/scumm/help.cpp:169
 #: engines/scumm/help.cpp:198 engines/scumm/help.cpp:213
 #: engines/scumm/help.cpp:230 engines/sherlock/metaengine.cpp:420
-#: engines/sludge/keymapper_tables.h:318 engines/twp/metaengine.cpp:181
+#: engines/sludge/keymapper_tables.h:318 engines/twp/metaengine.cpp:180
 #: engines/wintermute/keymapper_tables.h:134
 #: engines/wintermute/keymapper_tables.h:1546
 msgid "Pick up"
@@ -13044,7 +13044,7 @@ msgstr "Felvesz"
 #: engines/queen/metaengine.cpp:289 engines/saga/metaengine.cpp:364
 #: engines/scumm/help.cpp:199 engines/scumm/help.cpp:215
 #: engines/scumm/help.cpp:251 engines/sludge/keymapper_tables.h:323
-#: engines/twp/metaengine.cpp:183 engines/wintermute/keymapper_tables.h:128
+#: engines/twp/metaengine.cpp:182 engines/wintermute/keymapper_tables.h:128
 #: engines/wintermute/keymapper_tables.h:1558
 msgid "Talk to"
 msgstr "Besz??l"
@@ -13153,7 +13153,7 @@ msgstr "Odamegy"
 
 #: engines/saga/metaengine.cpp:389 engines/scumm/help.cpp:122
 #: engines/scumm/help.cpp:137 engines/scumm/help.cpp:162
-#: engines/supernova/metaengine.cpp:346 engines/twp/metaengine.cpp:184
+#: engines/supernova/metaengine.cpp:346 engines/twp/metaengine.cpp:183
 msgid "Push"
 msgstr "Tol"
 
@@ -14003,7 +14003,7 @@ msgstr "F?? j??t??kvez??rl??k:"
 
 #: engines/scumm/help.cpp:123 engines/scumm/help.cpp:138
 #: engines/scumm/help.cpp:163 engines/supernova/metaengine.cpp:351
-#: engines/twp/metaengine.cpp:185
+#: engines/twp/metaengine.cpp:184
 msgid "Pull"
 msgstr "H??z"
 
@@ -16500,121 +16500,121 @@ msgstr "Klasszikus Mondat"
 msgid "Ransome *unbeeped* (DLC)"
 msgstr "V??lts??gd??j *kis??pol??s* (DLC)"
 
-#: engines/twp/metaengine.cpp:151
+#: engines/twp/metaengine.cpp:150
 msgid "Select highlighted verb"
 msgstr "Kiemelt ige kiv??laszt??sa"
 
-#: engines/twp/metaengine.cpp:156
+#: engines/twp/metaengine.cpp:155
 msgid "Go to previous object"
 msgstr "Vissza az el??z?? t??rgyhoz"
 
-#: engines/twp/metaengine.cpp:157
+#: engines/twp/metaengine.cpp:156
 msgid "Go to next object"
 msgstr "Tov??bb a k??vetkez?? t??rgyhoz"
 
-#: engines/twp/metaengine.cpp:158
+#: engines/twp/metaengine.cpp:157
 #, fuzzy
 #| msgid "Select verb/item Left"
 msgid "Select verb / item left"
 msgstr "Ige/t??rgy kiv??laszt??sa Balra"
 
-#: engines/twp/metaengine.cpp:159
+#: engines/twp/metaengine.cpp:158
 #, fuzzy
 #| msgid "Select verb/item Right"
 msgid "Select verb / item right"
 msgstr "Ige/t??rgy kiv??laszt??sa Jobbra"
 
-#: engines/twp/metaengine.cpp:160
+#: engines/twp/metaengine.cpp:159
 #, fuzzy
 #| msgid "Select verb/item Up"
 msgid "Select verb / item up"
 msgstr "Ige/t??rgy kiv??laszt??sa Fel"
 
-#: engines/twp/metaengine.cpp:161
+#: engines/twp/metaengine.cpp:160
 #, fuzzy
 #| msgid "Select verb/item Down"
 msgid "Select verb / item down"
 msgstr "Ige/t??rgy kiv??laszt??sa Le"
 
-#: engines/twp/metaengine.cpp:163
+#: engines/twp/metaengine.cpp:162
 #, fuzzy
 #| msgid "Select Actor 1"
 msgid "Select actor 1"
 msgstr "1. szerepl?? kiv??laszt??sa"
 
-#: engines/twp/metaengine.cpp:164
+#: engines/twp/metaengine.cpp:163
 #, fuzzy
 #| msgid "Select Actor 2"
 msgid "Select actor 2"
 msgstr "2. szerepl?? kiv??laszt??sa"
 
-#: engines/twp/metaengine.cpp:165
+#: engines/twp/metaengine.cpp:164
 #, fuzzy
 #| msgid "Select Actor 3"
 msgid "Select actor 3"
 msgstr "3. szerepl?? kiv??laszt??sa"
 
-#: engines/twp/metaengine.cpp:166
+#: engines/twp/metaengine.cpp:165
 #, fuzzy
 #| msgid "Select Actor 4"
 msgid "Select actor 4"
 msgstr "4. szerepl?? kiv??laszt??sa"
 
-#: engines/twp/metaengine.cpp:167
+#: engines/twp/metaengine.cpp:166
 #, fuzzy
 #| msgid "Select Actor 5"
 msgid "Select actor 5"
 msgstr "5. szerepl?? kiv??laszt??sa"
 
-#: engines/twp/metaengine.cpp:168
+#: engines/twp/metaengine.cpp:167
 #, fuzzy
 #| msgid "Select Choice 1"
 msgid "Select choice 1"
 msgstr "1. d??nt??s kiv??laszt??sa"
 
-#: engines/twp/metaengine.cpp:169
+#: engines/twp/metaengine.cpp:168
 #, fuzzy
 #| msgid "Select Choice 2"
 msgid "Select choice 2"
 msgstr "2. d??nt??s kiv??laszt??sa"
 
-#: engines/twp/metaengine.cpp:170
+#: engines/twp/metaengine.cpp:169
 #, fuzzy
 #| msgid "Select Choice 3"
 msgid "Select choice 3"
 msgstr "3. d??nt??s kiv??laszt??sa"
 
-#: engines/twp/metaengine.cpp:171
+#: engines/twp/metaengine.cpp:170
 #, fuzzy
 #| msgid "Select Choice 4"
 msgid "Select choice 4"
 msgstr "4. d??nt??s kiv??laszt??sa"
 
-#: engines/twp/metaengine.cpp:172
+#: engines/twp/metaengine.cpp:171
 #, fuzzy
 #| msgid "Select Choice 5"
 msgid "Select choice 5"
 msgstr "5. d??nt??s kiv??laszt??sa"
 
-#: engines/twp/metaengine.cpp:173
+#: engines/twp/metaengine.cpp:172
 #, fuzzy
 #| msgid "Select Choice 6"
 msgid "Select choice 6"
 msgstr "6. d??nt??s kiv??laszt??sa"
 
-#: engines/twp/metaengine.cpp:174
+#: engines/twp/metaengine.cpp:173
 #, fuzzy
 #| msgid "Select Next Actor"
 msgid "Select next Actor"
 msgstr "??j szerepl?? kiv??laszt??sa"
 
-#: engines/twp/metaengine.cpp:175
+#: engines/twp/metaengine.cpp:174
 #, fuzzy
 #| msgid "Select Previous Actor"
 msgid "Select previous actor"
 msgstr "El??z?? szerepl?? kiv??laszt??sa"
 
-#: engines/twp/metaengine.cpp:177
+#: engines/twp/metaengine.cpp:176
 msgid "Show hotspots"
 msgstr "Forr??pontok mutat??sa"
 
diff --git a/po/it_IT.po b/po/it_IT.po
index 639cba0d201..54d15a948ab 100644
--- a/po/it_IT.po
+++ b/po/it_IT.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-13 13:52+0000\n"
+"POT-Creation-Date: 2026-06-14 18:41+0000\n"
 "PO-Revision-Date: 2026-06-14 09:41+0000\n"
 "Last-Translator: Walter Agazzi <walter.agazzi at protonmail.com>\n"
 "Language-Team: Italian <https://translations.scummvm.org/projects/scummvm/"
@@ -72,7 +72,7 @@ msgstr "Motori disponibili:"
 #: engines/scumm/help.cpp:141 engines/scumm/help.cpp:166
 #: engines/scumm/help.cpp:194 engines/scumm/help.cpp:212
 #: engines/sherlock/metaengine.cpp:430 engines/supernova/metaengine.cpp:341
-#: engines/tot/metaengine.cpp:149 engines/twp/metaengine.cpp:179
+#: engines/tot/metaengine.cpp:149 engines/twp/metaengine.cpp:178
 msgid "Close"
 msgstr "Chiudi"
 
@@ -1058,7 +1058,7 @@ msgstr "Interazione"
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
 #: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1124
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
-#: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
+#: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:153
 #: engines/ultima/ultima0/metaengine.cpp:45
 #: engines/ultima/ultima4/metaengine.cpp:44
 #: engines/ultima/ultima4/metaengine.cpp:139
@@ -1079,7 +1079,7 @@ msgstr "Su"
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
 #: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1091
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
-#: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
+#: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:45
 #: engines/ultima/ultima4/metaengine.cpp:140
@@ -1100,7 +1100,7 @@ msgstr "Gi??"
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
 #: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1104
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
-#: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
+#: engines/twp/metaengine.cpp:151 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
 #: engines/ultima/ultima4/metaengine.cpp:151
 #: engines/wintermute/keymapper_tables.h:320
@@ -1120,7 +1120,7 @@ msgstr "Sinistra"
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
 #: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1111
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
-#: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
+#: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
 #: engines/ultima/ultima4/metaengine.cpp:152
 #: engines/wintermute/keymapper_tables.h:326
@@ -3355,7 +3355,7 @@ msgstr "<Valore non valido>"
 
 #. I18N: Abbreviation for 'bytes' as data size
 #. I18N: Hardware key
-#: common/util.cpp:183 backends/platform/3ds/osystem-events.cpp:46
+#: common/util.cpp:190 backends/platform/3ds/osystem-events.cpp:46
 #: backends/platform/ds/osystem_ds.cpp:178
 #: backends/platform/sdl/miyoo/miyoo.cpp:61
 #: backends/platform/sdl/miyoo/miyoo.cpp:88
@@ -3365,22 +3365,22 @@ msgid "B"
 msgstr "B"
 
 #. I18N: Abbreviation for 'kilobytes' as data size
-#: common/util.cpp:189
+#: common/util.cpp:196
 msgid "KB"
 msgstr "KB"
 
 #. I18N: Abbreviation for 'megabytes' as data size
-#: common/util.cpp:194
+#: common/util.cpp:201
 msgid "MB"
 msgstr "MB"
 
 #. I18N: Abbreviation for 'gigabytes' as data size
-#: common/util.cpp:200
+#: common/util.cpp:207
 msgid "GB"
 msgstr "GB"
 
 #. I18N: Abbreviation for 'terabytes' as data size
-#: common/util.cpp:206
+#: common/util.cpp:213
 msgid "TB"
 msgstr "TB"
 
@@ -6468,7 +6468,7 @@ msgstr "Guarda"
 #: engines/scumm/help.cpp:165 engines/scumm/help.cpp:193
 #: engines/scumm/help.cpp:211 engines/sherlock/metaengine.cpp:425
 #: engines/supernova/metaengine.cpp:336 engines/tot/metaengine.cpp:144
-#: engines/twp/metaengine.cpp:178
+#: engines/twp/metaengine.cpp:177
 msgid "Open"
 msgstr "Apri"
 
@@ -6490,7 +6490,7 @@ msgstr "Prendi"
 #: engines/sherlock/metaengine.cpp:440 engines/sherlock/metaengine.cpp:561
 #: engines/sky/metaengine.cpp:95 engines/sludge/keymapper_tables.h:333
 #: engines/supernova/metaengine.cpp:356 engines/tot/metaengine.cpp:139
-#: engines/tsage/metaengine.cpp:211 engines/twp/metaengine.cpp:186
+#: engines/tsage/metaengine.cpp:211 engines/twp/metaengine.cpp:185
 #: engines/ultima/ultima4/metaengine.cpp:74
 #: engines/ultima/nuvie/metaengine.cpp:69
 #: engines/wintermute/keymapper_tables.h:140
@@ -7279,7 +7279,7 @@ msgstr "Attiva"
 
 #: engines/alcachofa/metaengine.cpp:104 engines/queen/metaengine.cpp:279
 #: engines/saga/metaengine.cpp:354 engines/scumm/help.cpp:202
-#: engines/scumm/help.cpp:214 engines/twp/metaengine.cpp:182
+#: engines/scumm/help.cpp:214 engines/twp/metaengine.cpp:181
 #: engines/wintermute/keymapper_tables.h:122
 #: engines/wintermute/keymapper_tables.h:1534
 msgid "Look at"
@@ -7635,7 +7635,7 @@ msgstr "Attiva / Disattiva modalit?? combattimento"
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:210
 #: engines/phoenixvr/metaengine.cpp:94 engines/scumm/help.cpp:79
 #: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
-#: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
+#: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:161
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
 msgstr "Salta scena di intermezzo"
@@ -8452,7 +8452,7 @@ msgstr "Solo effetti della versione floppy"
 #. I18N: Perform action on object where cursor points
 #: engines/darkseed/metaengine.cpp:42 engines/dragons/metaengine.cpp:135
 #: engines/phoenixvr/metaengine.cpp:75 engines/tinsel/metaengine.cpp:294
-#: engines/twp/metaengine.cpp:150
+#: engines/twp/metaengine.cpp:149
 msgid "Action"
 msgstr "Azione"
 
@@ -8873,7 +8873,7 @@ msgstr "Equipaggia"
 #: engines/scumm/help.cpp:139 engines/scumm/help.cpp:164
 #: engines/scumm/help.cpp:200 engines/scumm/help.cpp:210
 #: engines/sherlock/metaengine.cpp:445 engines/sherlock/metaengine.cpp:566
-#: engines/supernova/metaengine.cpp:366 engines/twp/metaengine.cpp:180
+#: engines/supernova/metaengine.cpp:366 engines/twp/metaengine.cpp:179
 msgid "Give"
 msgstr "Dai"
 
@@ -12199,7 +12199,7 @@ msgstr "Utilizza un font alternativo, pi?? leggibile"
 msgid "Move / Skip / Use default action with inventory item"
 msgstr "Spostamento / Salta/ Esegui azione predefinita dell'oggetto"
 
-#: engines/queen/metaengine.cpp:198 engines/twp/metaengine.cpp:176
+#: engines/queen/metaengine.cpp:198 engines/twp/metaengine.cpp:175
 msgid "Skip text"
 msgstr "Salta battuta"
 
@@ -12239,7 +12239,7 @@ msgstr "Usa diario"
 #: engines/scumm/help.cpp:144 engines/scumm/help.cpp:169
 #: engines/scumm/help.cpp:198 engines/scumm/help.cpp:213
 #: engines/scumm/help.cpp:230 engines/sherlock/metaengine.cpp:420
-#: engines/sludge/keymapper_tables.h:318 engines/twp/metaengine.cpp:181
+#: engines/sludge/keymapper_tables.h:318 engines/twp/metaengine.cpp:180
 #: engines/wintermute/keymapper_tables.h:134
 #: engines/wintermute/keymapper_tables.h:1546
 msgid "Pick up"
@@ -12248,7 +12248,7 @@ msgstr "Raccogli"
 #: engines/queen/metaengine.cpp:289 engines/saga/metaengine.cpp:364
 #: engines/scumm/help.cpp:199 engines/scumm/help.cpp:215
 #: engines/scumm/help.cpp:251 engines/sludge/keymapper_tables.h:323
-#: engines/twp/metaengine.cpp:183 engines/wintermute/keymapper_tables.h:128
+#: engines/twp/metaengine.cpp:182 engines/wintermute/keymapper_tables.h:128
 #: engines/wintermute/keymapper_tables.h:1558
 msgid "Talk to"
 msgstr "Parla con"
@@ -12343,7 +12343,7 @@ msgstr "Cammina verso"
 
 #: engines/saga/metaengine.cpp:389 engines/scumm/help.cpp:122
 #: engines/scumm/help.cpp:137 engines/scumm/help.cpp:162
-#: engines/supernova/metaengine.cpp:346 engines/twp/metaengine.cpp:184
+#: engines/supernova/metaengine.cpp:346 engines/twp/metaengine.cpp:183
 msgid "Push"
 msgstr "Premi"
 
@@ -13214,7 +13214,7 @@ msgstr "Controlli principali di gioco:"
 
 #: engines/scumm/help.cpp:123 engines/scumm/help.cpp:138
 #: engines/scumm/help.cpp:163 engines/supernova/metaengine.cpp:351
-#: engines/twp/metaengine.cpp:185
+#: engines/twp/metaengine.cpp:184
 msgid "Pull"
 msgstr "Tira"
 
@@ -15468,87 +15468,87 @@ msgstr "Frase classica"
 msgid "Ransome *unbeeped* (DLC)"
 msgstr "Ransome *debippato* (DLC)"
 
-#: engines/twp/metaengine.cpp:151
+#: engines/twp/metaengine.cpp:150
 msgid "Select highlighted verb"
 msgstr "Seleziona verbo evidenziato"
 
-#: engines/twp/metaengine.cpp:156
+#: engines/twp/metaengine.cpp:155
 msgid "Go to previous object"
 msgstr "Seleziona oggetto precedente"
 
-#: engines/twp/metaengine.cpp:157
+#: engines/twp/metaengine.cpp:156
 msgid "Go to next object"
 msgstr "Seleziona oggetto successivo"
 
-#: engines/twp/metaengine.cpp:158
+#: engines/twp/metaengine.cpp:157
 msgid "Select verb / item left"
 msgstr "Seleziona verbo / oggetto a sinistra"
 
-#: engines/twp/metaengine.cpp:159
+#: engines/twp/metaengine.cpp:158
 msgid "Select verb / item right"
 msgstr "Seleziona verbo / oggetto a destra"
 
-#: engines/twp/metaengine.cpp:160
+#: engines/twp/metaengine.cpp:159
 msgid "Select verb / item up"
 msgstr "Seleziona verbo / oggetto superiore"
 
-#: engines/twp/metaengine.cpp:161
+#: engines/twp/metaengine.cpp:160
 msgid "Select verb / item down"
 msgstr "Seleziona verbo / oggetto inferiore"
 
-#: engines/twp/metaengine.cpp:163
+#: engines/twp/metaengine.cpp:162
 msgid "Select actor 1"
 msgstr "Seleziona personaggio 1"
 
-#: engines/twp/metaengine.cpp:164
+#: engines/twp/metaengine.cpp:163
 msgid "Select actor 2"
 msgstr "Seleziona personaggio 2"
 
-#: engines/twp/metaengine.cpp:165
+#: engines/twp/metaengine.cpp:164
 msgid "Select actor 3"
 msgstr "Seleziona personaggio 3"
 
-#: engines/twp/metaengine.cpp:166
+#: engines/twp/metaengine.cpp:165
 msgid "Select actor 4"
 msgstr "Seleziona personaggio 4"
 
-#: engines/twp/metaengine.cpp:167
+#: engines/twp/metaengine.cpp:166
 msgid "Select actor 5"
 msgstr "Seleziona personaggio 5"
 
-#: engines/twp/metaengine.cpp:168
+#: engines/twp/metaengine.cpp:167
 msgid "Select choice 1"
 msgstr "Seleziona opzione 1"
 
-#: engines/twp/metaengine.cpp:169
+#: engines/twp/metaengine.cpp:168
 msgid "Select choice 2"
 msgstr "Seleziona opzione 2"
 
-#: engines/twp/metaengine.cpp:170
+#: engines/twp/metaengine.cpp:169
 msgid "Select choice 3"
 msgstr "Seleziona opzione 3"
 
-#: engines/twp/metaengine.cpp:171
+#: engines/twp/metaengine.cpp:170
 msgid "Select choice 4"
 msgstr "Seleziona opzione 4"
 
-#: engines/twp/metaengine.cpp:172
+#: engines/twp/metaengine.cpp:171
 msgid "Select choice 5"
 msgstr "Seleziona opzione 5"
 
-#: engines/twp/metaengine.cpp:173
+#: engines/twp/metaengine.cpp:172
 msgid "Select choice 6"
 msgstr "Seleziona opzione 6"
 
-#: engines/twp/metaengine.cpp:174
+#: engines/twp/metaengine.cpp:173
 msgid "Select next Actor"
 msgstr "Seleziona personaggio successivo"
 
-#: engines/twp/metaengine.cpp:175
+#: engines/twp/metaengine.cpp:174
 msgid "Select previous actor"
 msgstr "Seleziona personaggio precedente"
 
-#: engines/twp/metaengine.cpp:177
+#: engines/twp/metaengine.cpp:176
 msgid "Show hotspots"
 msgstr "Mostra aree interattive"
 
diff --git a/po/ja.po b/po/ja.po
index 3e1ae5ba6b6..4baeed986fd 100644
--- a/po/ja.po
+++ b/po/ja.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-13 13:52+0000\n"
+"POT-Creation-Date: 2026-06-14 18:41+0000\n"
 "PO-Revision-Date: 2026-06-04 07:22+0000\n"
 "Last-Translator: VAN-Gluon <van.gluonp at gmail.com>\n"
 "Language-Team: Japanese <https://translations.scummvm.org/projects/scummvm/"
@@ -72,7 +72,7 @@ msgstr "??????????????????????????????"
 #: engines/scumm/help.cpp:141 engines/scumm/help.cpp:166
 #: engines/scumm/help.cpp:194 engines/scumm/help.cpp:212
 #: engines/sherlock/metaengine.cpp:430 engines/supernova/metaengine.cpp:341
-#: engines/tot/metaengine.cpp:149 engines/twp/metaengine.cpp:179
+#: engines/tot/metaengine.cpp:149 engines/twp/metaengine.cpp:178
 msgid "Close"
 msgstr "?????????"
 
@@ -1044,7 +1044,7 @@ msgstr "??????"
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
 #: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1124
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
-#: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
+#: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:153
 #: engines/ultima/ultima0/metaengine.cpp:45
 #: engines/ultima/ultima4/metaengine.cpp:44
 #: engines/ultima/ultima4/metaengine.cpp:139
@@ -1065,7 +1065,7 @@ msgstr "???"
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
 #: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1091
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
-#: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
+#: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:45
 #: engines/ultima/ultima4/metaengine.cpp:140
@@ -1086,7 +1086,7 @@ msgstr "???"
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
 #: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1104
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
-#: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
+#: engines/twp/metaengine.cpp:151 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
 #: engines/ultima/ultima4/metaengine.cpp:151
 #: engines/wintermute/keymapper_tables.h:320
@@ -1106,7 +1106,7 @@ msgstr "???"
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
 #: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1111
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
-#: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
+#: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
 #: engines/ultima/ultima4/metaengine.cpp:152
 #: engines/wintermute/keymapper_tables.h:326
@@ -3251,7 +3251,7 @@ msgstr "<????????????>"
 
 #. I18N: Abbreviation for 'bytes' as data size
 #. I18N: Hardware key
-#: common/util.cpp:183 backends/platform/3ds/osystem-events.cpp:46
+#: common/util.cpp:190 backends/platform/3ds/osystem-events.cpp:46
 #: backends/platform/ds/osystem_ds.cpp:178
 #: backends/platform/sdl/miyoo/miyoo.cpp:61
 #: backends/platform/sdl/miyoo/miyoo.cpp:88
@@ -3261,22 +3261,22 @@ msgid "B"
 msgstr "B"
 
 #. I18N: Abbreviation for 'kilobytes' as data size
-#: common/util.cpp:189
+#: common/util.cpp:196
 msgid "KB"
 msgstr "KB"
 
 #. I18N: Abbreviation for 'megabytes' as data size
-#: common/util.cpp:194
+#: common/util.cpp:201
 msgid "MB"
 msgstr "MB"
 
 #. I18N: Abbreviation for 'gigabytes' as data size
-#: common/util.cpp:200
+#: common/util.cpp:207
 msgid "GB"
 msgstr "GB"
 
 #. I18N: Abbreviation for 'terabytes' as data size
-#: common/util.cpp:206
+#: common/util.cpp:213
 msgid "TB"
 msgstr "TB"
 
@@ -6334,7 +6334,7 @@ msgstr "??????"
 #: engines/scumm/help.cpp:165 engines/scumm/help.cpp:193
 #: engines/scumm/help.cpp:211 engines/sherlock/metaengine.cpp:425
 #: engines/supernova/metaengine.cpp:336 engines/tot/metaengine.cpp:144
-#: engines/twp/metaengine.cpp:178
+#: engines/twp/metaengine.cpp:177
 msgid "Open"
 msgstr "??????"
 
@@ -6356,7 +6356,7 @@ msgstr "??????"
 #: engines/sherlock/metaengine.cpp:440 engines/sherlock/metaengine.cpp:561
 #: engines/sky/metaengine.cpp:95 engines/sludge/keymapper_tables.h:333
 #: engines/supernova/metaengine.cpp:356 engines/tot/metaengine.cpp:139
-#: engines/tsage/metaengine.cpp:211 engines/twp/metaengine.cpp:186
+#: engines/tsage/metaengine.cpp:211 engines/twp/metaengine.cpp:185
 #: engines/ultima/ultima4/metaengine.cpp:74
 #: engines/ultima/nuvie/metaengine.cpp:69
 #: engines/wintermute/keymapper_tables.h:140
@@ -7118,7 +7118,7 @@ msgstr "??????"
 
 #: engines/alcachofa/metaengine.cpp:104 engines/queen/metaengine.cpp:279
 #: engines/saga/metaengine.cpp:354 engines/scumm/help.cpp:202
-#: engines/scumm/help.cpp:214 engines/twp/metaengine.cpp:182
+#: engines/scumm/help.cpp:214 engines/twp/metaengine.cpp:181
 #: engines/wintermute/keymapper_tables.h:122
 #: engines/wintermute/keymapper_tables.h:1534
 msgid "Look at"
@@ -7466,7 +7466,7 @@ msgstr "??????????????????"
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:210
 #: engines/phoenixvr/metaengine.cpp:94 engines/scumm/help.cpp:79
 #: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
-#: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
+#: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:161
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
 msgstr "???????????????????????????"
@@ -8382,7 +8382,7 @@ msgstr "FD??????SFX??????"
 #. I18N: Perform action on object where cursor points
 #: engines/darkseed/metaengine.cpp:42 engines/dragons/metaengine.cpp:135
 #: engines/phoenixvr/metaengine.cpp:75 engines/tinsel/metaengine.cpp:294
-#: engines/twp/metaengine.cpp:150
+#: engines/twp/metaengine.cpp:149
 msgid "Action"
 msgstr "???????????????"
 
@@ -8914,7 +8914,7 @@ msgstr "??????"
 #: engines/scumm/help.cpp:139 engines/scumm/help.cpp:164
 #: engines/scumm/help.cpp:200 engines/scumm/help.cpp:210
 #: engines/sherlock/metaengine.cpp:445 engines/sherlock/metaengine.cpp:566
-#: engines/supernova/metaengine.cpp:366 engines/twp/metaengine.cpp:180
+#: engines/supernova/metaengine.cpp:366 engines/twp/metaengine.cpp:179
 msgid "Give"
 msgstr "?????????"
 
@@ -12601,7 +12601,7 @@ msgstr "????????????????????????????????????????????????"
 msgid "Move / Skip / Use default action with inventory item"
 msgstr "??????/????????????/??????????????????????????????(??????????????????)"
 
-#: engines/queen/metaengine.cpp:198 engines/twp/metaengine.cpp:176
+#: engines/queen/metaengine.cpp:198 engines/twp/metaengine.cpp:175
 #, fuzzy
 #| msgid "Skip Text"
 msgid "Skip text"
@@ -12659,7 +12659,7 @@ msgstr "???????????????"
 #: engines/scumm/help.cpp:144 engines/scumm/help.cpp:169
 #: engines/scumm/help.cpp:198 engines/scumm/help.cpp:213
 #: engines/scumm/help.cpp:230 engines/sherlock/metaengine.cpp:420
-#: engines/sludge/keymapper_tables.h:318 engines/twp/metaengine.cpp:181
+#: engines/sludge/keymapper_tables.h:318 engines/twp/metaengine.cpp:180
 #: engines/wintermute/keymapper_tables.h:134
 #: engines/wintermute/keymapper_tables.h:1546
 msgid "Pick up"
@@ -12668,7 +12668,7 @@ msgstr "??????"
 #: engines/queen/metaengine.cpp:289 engines/saga/metaengine.cpp:364
 #: engines/scumm/help.cpp:199 engines/scumm/help.cpp:215
 #: engines/scumm/help.cpp:251 engines/sludge/keymapper_tables.h:323
-#: engines/twp/metaengine.cpp:183 engines/wintermute/keymapper_tables.h:128
+#: engines/twp/metaengine.cpp:182 engines/wintermute/keymapper_tables.h:128
 #: engines/wintermute/keymapper_tables.h:1558
 msgid "Talk to"
 msgstr "??????"
@@ -12783,7 +12783,7 @@ msgstr "??????"
 
 #: engines/saga/metaengine.cpp:389 engines/scumm/help.cpp:122
 #: engines/scumm/help.cpp:137 engines/scumm/help.cpp:162
-#: engines/supernova/metaengine.cpp:346 engines/twp/metaengine.cpp:184
+#: engines/supernova/metaengine.cpp:346 engines/twp/metaengine.cpp:183
 msgid "Push"
 msgstr "??????"
 
@@ -13610,7 +13610,7 @@ msgstr "???????????????????????????:"
 
 #: engines/scumm/help.cpp:123 engines/scumm/help.cpp:138
 #: engines/scumm/help.cpp:163 engines/supernova/metaengine.cpp:351
-#: engines/twp/metaengine.cpp:185
+#: engines/twp/metaengine.cpp:184
 msgid "Pull"
 msgstr "??????"
 
@@ -16158,121 +16158,121 @@ msgstr "???????????????"
 msgid "Ransome *unbeeped* (DLC)"
 msgstr "*?????????*??????(DLC)"
 
-#: engines/twp/metaengine.cpp:151
+#: engines/twp/metaengine.cpp:150
 msgid "Select highlighted verb"
 msgstr "???????????????????????????????????????"
 
-#: engines/twp/metaengine.cpp:156
+#: engines/twp/metaengine.cpp:155
 msgid "Go to previous object"
 msgstr "???????????????????????????"
 
-#: engines/twp/metaengine.cpp:157
+#: engines/twp/metaengine.cpp:156
 msgid "Go to next object"
 msgstr "???????????????????????????"
 
-#: engines/twp/metaengine.cpp:158
+#: engines/twp/metaengine.cpp:157
 #, fuzzy
 #| msgid "Select verb/item Left"
 msgid "Select verb / item left"
 msgstr "????????????/?????????????????????"
 
-#: engines/twp/metaengine.cpp:159
+#: engines/twp/metaengine.cpp:158
 #, fuzzy
 #| msgid "Select verb/item Right"
 msgid "Select verb / item right"
 msgstr "????????????/?????????????????????"
 
-#: engines/twp/metaengine.cpp:160
+#: engines/twp/metaengine.cpp:159
 #, fuzzy
 #| msgid "Select verb/item Up"
 msgid "Select verb / item up"
 msgstr "????????????/?????????????????????"
 
-#: engines/twp/metaengine.cpp:161
+#: engines/twp/metaengine.cpp:160
 #, fuzzy
 #| msgid "Select verb/item Down"
 msgid "Select verb / item down"
 msgstr "????????????/?????????????????????"
 
-#: engines/twp/metaengine.cpp:163
+#: engines/twp/metaengine.cpp:162
 #, fuzzy
 #| msgid "Select Actor 1"
 msgid "Select actor 1"
 msgstr "??????1?????????"
 
-#: engines/twp/metaengine.cpp:164
+#: engines/twp/metaengine.cpp:163
 #, fuzzy
 #| msgid "Select Actor 2"
 msgid "Select actor 2"
 msgstr "??????2?????????"
 
-#: engines/twp/metaengine.cpp:165
+#: engines/twp/metaengine.cpp:164
 #, fuzzy
 #| msgid "Select Actor 3"
 msgid "Select actor 3"
 msgstr "??????3?????????"
 
-#: engines/twp/metaengine.cpp:166
+#: engines/twp/metaengine.cpp:165
 #, fuzzy
 #| msgid "Select Actor 4"
 msgid "Select actor 4"
 msgstr "??????4?????????"
 
-#: engines/twp/metaengine.cpp:167
+#: engines/twp/metaengine.cpp:166
 #, fuzzy
 #| msgid "Select Actor 5"
 msgid "Select actor 5"
 msgstr "??????5?????????"
 
-#: engines/twp/metaengine.cpp:168
+#: engines/twp/metaengine.cpp:167
 #, fuzzy
 #| msgid "Select Choice 1"
 msgid "Select choice 1"
 msgstr "1?????????"
 
-#: engines/twp/metaengine.cpp:169
+#: engines/twp/metaengine.cpp:168
 #, fuzzy
 #| msgid "Select Choice 2"
 msgid "Select choice 2"
 msgstr "2?????????"
 
-#: engines/twp/metaengine.cpp:170
+#: engines/twp/metaengine.cpp:169
 #, fuzzy
 #| msgid "Select Choice 3"
 msgid "Select choice 3"
 msgstr "3?????????"
 
-#: engines/twp/metaengine.cpp:171
+#: engines/twp/metaengine.cpp:170
 #, fuzzy
 #| msgid "Select Choice 4"
 msgid "Select choice 4"
 msgstr "4?????????"
 
-#: engines/twp/metaengine.cpp:172
+#: engines/twp/metaengine.cpp:171
 #, fuzzy
 #| msgid "Select Choice 5"
 msgid "Select choice 5"
 msgstr "5?????????"
 
-#: engines/twp/metaengine.cpp:173
+#: engines/twp/metaengine.cpp:172
 #, fuzzy
 #| msgid "Select Choice 6"
 msgid "Select choice 6"
 msgstr "6?????????"
 
-#: engines/twp/metaengine.cpp:174
+#: engines/twp/metaengine.cpp:173
 #, fuzzy
 #| msgid "Select Next Actor"
 msgid "Select next Actor"
 msgstr "?????????????????????"
 
-#: engines/twp/metaengine.cpp:175
+#: engines/twp/metaengine.cpp:174
 #, fuzzy
 #| msgid "Select Previous Actor"
 msgid "Select previous actor"
 msgstr "?????????????????????"
 
-#: engines/twp/metaengine.cpp:177
+#: engines/twp/metaengine.cpp:176
 msgid "Show hotspots"
 msgstr "??????????????????????????????"
 
diff --git a/po/ka.po b/po/ka.po
index df485887b4a..a8b2f4f5a64 100644
--- a/po/ka.po
+++ b/po/ka.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-13 13:52+0000\n"
+"POT-Creation-Date: 2026-06-14 18:41+0000\n"
 "PO-Revision-Date: 2026-06-05 10:22+0000\n"
 "Last-Translator: NorwayFun <temuri.doghonadze at gmail.com>\n"
 "Language-Team: Georgian <https://translations.scummvm.org/projects/scummvm/"
@@ -72,7 +72,7 @@ msgstr "??????????????????????????????????????? ?????????????????????:"
 #: engines/scumm/help.cpp:141 engines/scumm/help.cpp:166
 #: engines/scumm/help.cpp:194 engines/scumm/help.cpp:212
 #: engines/sherlock/metaengine.cpp:430 engines/supernova/metaengine.cpp:341
-#: engines/tot/metaengine.cpp:149 engines/twp/metaengine.cpp:179
+#: engines/tot/metaengine.cpp:149 engines/twp/metaengine.cpp:178
 msgid "Close"
 msgstr "?????????????????????"
 
@@ -1023,7 +1023,7 @@ msgstr "??????????????????????????????"
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
 #: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1124
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
-#: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
+#: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:153
 #: engines/ultima/ultima0/metaengine.cpp:45
 #: engines/ultima/ultima4/metaengine.cpp:44
 #: engines/ultima/ultima4/metaengine.cpp:139
@@ -1044,7 +1044,7 @@ msgstr "?????????????????????"
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
 #: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1091
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
-#: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
+#: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:45
 #: engines/ultima/ultima4/metaengine.cpp:140
@@ -1065,7 +1065,7 @@ msgstr "??????????????????"
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
 #: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1104
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
-#: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
+#: engines/twp/metaengine.cpp:151 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
 #: engines/ultima/ultima4/metaengine.cpp:151
 #: engines/wintermute/keymapper_tables.h:320
@@ -1085,7 +1085,7 @@ msgstr "????????????????????????"
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
 #: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1111
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
-#: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
+#: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
 #: engines/ultima/ultima4/metaengine.cpp:152
 #: engines/wintermute/keymapper_tables.h:326
@@ -3114,7 +3114,7 @@ msgstr "<???????????????????????? ?????????????????????????????????>"
 
 #. I18N: Abbreviation for 'bytes' as data size
 #. I18N: Hardware key
-#: common/util.cpp:183 backends/platform/3ds/osystem-events.cpp:46
+#: common/util.cpp:190 backends/platform/3ds/osystem-events.cpp:46
 #: backends/platform/ds/osystem_ds.cpp:178
 #: backends/platform/sdl/miyoo/miyoo.cpp:61
 #: backends/platform/sdl/miyoo/miyoo.cpp:88
@@ -3124,22 +3124,22 @@ msgid "B"
 msgstr "???"
 
 #. I18N: Abbreviation for 'kilobytes' as data size
-#: common/util.cpp:189
+#: common/util.cpp:196
 msgid "KB"
 msgstr "??????"
 
 #. I18N: Abbreviation for 'megabytes' as data size
-#: common/util.cpp:194
+#: common/util.cpp:201
 msgid "MB"
 msgstr "??????"
 
 #. I18N: Abbreviation for 'gigabytes' as data size
-#: common/util.cpp:200
+#: common/util.cpp:207
 msgid "GB"
 msgstr "??????"
 
 #. I18N: Abbreviation for 'terabytes' as data size
-#: common/util.cpp:206
+#: common/util.cpp:213
 msgid "TB"
 msgstr "??????"
 
@@ -5785,7 +5785,7 @@ msgstr "???????????????????????????"
 #: engines/scumm/help.cpp:165 engines/scumm/help.cpp:193
 #: engines/scumm/help.cpp:211 engines/sherlock/metaengine.cpp:425
 #: engines/supernova/metaengine.cpp:336 engines/tot/metaengine.cpp:144
-#: engines/twp/metaengine.cpp:178
+#: engines/twp/metaengine.cpp:177
 msgid "Open"
 msgstr "??????????????????"
 
@@ -5807,7 +5807,7 @@ msgstr "??????????????????"
 #: engines/sherlock/metaengine.cpp:440 engines/sherlock/metaengine.cpp:561
 #: engines/sky/metaengine.cpp:95 engines/sludge/keymapper_tables.h:333
 #: engines/supernova/metaengine.cpp:356 engines/tot/metaengine.cpp:139
-#: engines/tsage/metaengine.cpp:211 engines/twp/metaengine.cpp:186
+#: engines/tsage/metaengine.cpp:211 engines/twp/metaengine.cpp:185
 #: engines/ultima/ultima4/metaengine.cpp:74
 #: engines/ultima/nuvie/metaengine.cpp:69
 #: engines/wintermute/keymapper_tables.h:140
@@ -6513,7 +6513,7 @@ msgstr "?????????????????????????????????"
 
 #: engines/alcachofa/metaengine.cpp:104 engines/queen/metaengine.cpp:279
 #: engines/saga/metaengine.cpp:354 engines/scumm/help.cpp:202
-#: engines/scumm/help.cpp:214 engines/twp/metaengine.cpp:182
+#: engines/scumm/help.cpp:214 engines/twp/metaengine.cpp:181
 #: engines/wintermute/keymapper_tables.h:122
 #: engines/wintermute/keymapper_tables.h:1534
 msgid "Look at"
@@ -6842,7 +6842,7 @@ msgstr "????????????????????? ????????????????????????"
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:210
 #: engines/phoenixvr/metaengine.cpp:94 engines/scumm/help.cpp:79
 #: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
-#: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
+#: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:161
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
 msgstr "????????????????????? ????????????????????? ??????????????????????????????"
@@ -7638,7 +7638,7 @@ msgstr ""
 #. I18N: Perform action on object where cursor points
 #: engines/darkseed/metaengine.cpp:42 engines/dragons/metaengine.cpp:135
 #: engines/phoenixvr/metaengine.cpp:75 engines/tinsel/metaengine.cpp:294
-#: engines/twp/metaengine.cpp:150
+#: engines/twp/metaengine.cpp:149
 msgid "Action"
 msgstr "?????????????????????"
 
@@ -8033,7 +8033,7 @@ msgstr "???????????????"
 #: engines/scumm/help.cpp:139 engines/scumm/help.cpp:164
 #: engines/scumm/help.cpp:200 engines/scumm/help.cpp:210
 #: engines/sherlock/metaengine.cpp:445 engines/sherlock/metaengine.cpp:566
-#: engines/supernova/metaengine.cpp:366 engines/twp/metaengine.cpp:180
+#: engines/supernova/metaengine.cpp:366 engines/twp/metaengine.cpp:179
 msgid "Give"
 msgstr "??????????????????"
 
@@ -11129,7 +11129,7 @@ msgstr ""
 msgid "Move / Skip / Use default action with inventory item"
 msgstr ""
 
-#: engines/queen/metaengine.cpp:198 engines/twp/metaengine.cpp:176
+#: engines/queen/metaengine.cpp:198 engines/twp/metaengine.cpp:175
 msgid "Skip text"
 msgstr "???????????????????????? ??????????????????????????????"
 
@@ -11169,7 +11169,7 @@ msgstr "???????????????????????? ??????????????????????????????"
 #: engines/scumm/help.cpp:144 engines/scumm/help.cpp:169
 #: engines/scumm/help.cpp:198 engines/scumm/help.cpp:213
 #: engines/scumm/help.cpp:230 engines/sherlock/metaengine.cpp:420
-#: engines/sludge/keymapper_tables.h:318 engines/twp/metaengine.cpp:181
+#: engines/sludge/keymapper_tables.h:318 engines/twp/metaengine.cpp:180
 #: engines/wintermute/keymapper_tables.h:134
 #: engines/wintermute/keymapper_tables.h:1546
 msgid "Pick up"
@@ -11178,7 +11178,7 @@ msgstr "???????????????"
 #: engines/queen/metaengine.cpp:289 engines/saga/metaengine.cpp:364
 #: engines/scumm/help.cpp:199 engines/scumm/help.cpp:215
 #: engines/scumm/help.cpp:251 engines/sludge/keymapper_tables.h:323
-#: engines/twp/metaengine.cpp:183 engines/wintermute/keymapper_tables.h:128
+#: engines/twp/metaengine.cpp:182 engines/wintermute/keymapper_tables.h:128
 #: engines/wintermute/keymapper_tables.h:1558
 msgid "Talk to"
 msgstr "?????????????????????????????????"
@@ -11273,7 +11273,7 @@ msgstr "??????????????????"
 
 #: engines/saga/metaengine.cpp:389 engines/scumm/help.cpp:122
 #: engines/scumm/help.cpp:137 engines/scumm/help.cpp:162
-#: engines/supernova/metaengine.cpp:346 engines/twp/metaengine.cpp:184
+#: engines/supernova/metaengine.cpp:346 engines/twp/metaengine.cpp:183
 msgid "Push"
 msgstr "????????????????????????"
 
@@ -12023,7 +12023,7 @@ msgstr "????????????????????? ???????????????????????? ??????????????????:"
 
 #: engines/scumm/help.cpp:123 engines/scumm/help.cpp:138
 #: engines/scumm/help.cpp:163 engines/supernova/metaengine.cpp:351
-#: engines/twp/metaengine.cpp:185
+#: engines/twp/metaengine.cpp:184
 msgid "Pull"
 msgstr "??????????????????"
 
@@ -14147,87 +14147,87 @@ msgstr "??????????????????????????? ??????????????????????????????"
 msgid "Ransome *unbeeped* (DLC)"
 msgstr "Ransome *unbeeped* (DLC)"
 
-#: engines/twp/metaengine.cpp:151
+#: engines/twp/metaengine.cpp:150
 msgid "Select highlighted verb"
 msgstr "????????????????????????????????? ??????????????? ??????????????????"
 
-#: engines/twp/metaengine.cpp:156
+#: engines/twp/metaengine.cpp:155
 msgid "Go to previous object"
 msgstr "???????????????????????? ???????????? ????????????????????????"
 
-#: engines/twp/metaengine.cpp:157
+#: engines/twp/metaengine.cpp:156
 msgid "Go to next object"
 msgstr ""
 
-#: engines/twp/metaengine.cpp:158
+#: engines/twp/metaengine.cpp:157
 msgid "Select verb / item left"
 msgstr "???????????????????????? ??????????????????????????????/??????????????????????????? ????????????????????????"
 
-#: engines/twp/metaengine.cpp:159
+#: engines/twp/metaengine.cpp:158
 msgid "Select verb / item right"
 msgstr "???????????????/??????????????????????????? ???????????????????????? ????????????????????????"
 
-#: engines/twp/metaengine.cpp:160
+#: engines/twp/metaengine.cpp:159
 msgid "Select verb / item up"
 msgstr "???????????? ??????????????????????????????/??????????????????????????? ????????????????????????"
 
-#: engines/twp/metaengine.cpp:161
+#: engines/twp/metaengine.cpp:160
 msgid "Select verb / item down"
 msgstr "??????????????? ??????????????????????????????/??????????????????????????? ????????????????????????"
 
-#: engines/twp/metaengine.cpp:163
+#: engines/twp/metaengine.cpp:162
 msgid "Select actor 1"
 msgstr "????????????????????? ???????????????????????? 1"
 
-#: engines/twp/metaengine.cpp:164
+#: engines/twp/metaengine.cpp:163
 msgid "Select actor 2"
 msgstr "????????????????????? ???????????????????????? 2"
 
-#: engines/twp/metaengine.cpp:165
+#: engines/twp/metaengine.cpp:164
 msgid "Select actor 3"
 msgstr "????????????????????? ???????????????????????? 3"
 
-#: engines/twp/metaengine.cpp:166
+#: engines/twp/metaengine.cpp:165
 msgid "Select actor 4"
 msgstr "????????????????????? ???????????????????????? 4"
 
-#: engines/twp/metaengine.cpp:167
+#: engines/twp/metaengine.cpp:166
 msgid "Select actor 5"
 msgstr "????????????????????? ???????????????????????? 5"
 
-#: engines/twp/metaengine.cpp:168
+#: engines/twp/metaengine.cpp:167
 msgid "Select choice 1"
 msgstr "????????????????????? ???????????????????????? 1"
 
-#: engines/twp/metaengine.cpp:169
+#: engines/twp/metaengine.cpp:168
 msgid "Select choice 2"
 msgstr "????????????????????? ???????????????????????? 2"
 
-#: engines/twp/metaengine.cpp:170
+#: engines/twp/metaengine.cpp:169
 msgid "Select choice 3"
 msgstr "????????????????????? ???????????????????????? 3"
 
-#: engines/twp/metaengine.cpp:171
+#: engines/twp/metaengine.cpp:170
 msgid "Select choice 4"
 msgstr "????????????????????? ???????????????????????? 4"
 
-#: engines/twp/metaengine.cpp:172
+#: engines/twp/metaengine.cpp:171
 msgid "Select choice 5"
 msgstr "????????????????????? ???????????????????????? 5"
 
-#: engines/twp/metaengine.cpp:173
+#: engines/twp/metaengine.cpp:172
 msgid "Select choice 6"
 msgstr "????????????????????? ???????????????????????? 6"
 
-#: engines/twp/metaengine.cpp:174
+#: engines/twp/metaengine.cpp:173
 msgid "Select next Actor"
 msgstr "????????????????????? ????????????????????? ????????????????????????"
 
-#: engines/twp/metaengine.cpp:175
+#: engines/twp/metaengine.cpp:174
 msgid "Select previous actor"
 msgstr "????????????????????? ???????????? ????????????????????????"
 
-#: engines/twp/metaengine.cpp:177
+#: engines/twp/metaengine.cpp:176
 msgid "Show hotspots"
 msgstr "????????????????????????????????? ?????????????????????"
 
diff --git a/po/ko.po b/po/ko.po
index bb64ce621f7..e1585fba45e 100644
--- a/po/ko.po
+++ b/po/ko.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-13 13:52+0000\n"
+"POT-Creation-Date: 2026-06-14 18:41+0000\n"
 "PO-Revision-Date: 2026-05-19 20:22+0000\n"
 "Last-Translator: Hoseok Seo <ddinghoya at gmail.com>\n"
 "Language-Team: Korean <https://translations.scummvm.org/projects/scummvm/"
@@ -72,7 +72,7 @@ msgstr "?????? ????????? ??????:"
 #: engines/scumm/help.cpp:141 engines/scumm/help.cpp:166
 #: engines/scumm/help.cpp:194 engines/scumm/help.cpp:212
 #: engines/sherlock/metaengine.cpp:430 engines/supernova/metaengine.cpp:341
-#: engines/tot/metaengine.cpp:149 engines/twp/metaengine.cpp:179
+#: engines/tot/metaengine.cpp:149 engines/twp/metaengine.cpp:178
 msgid "Close"
 msgstr "??????"
 
@@ -1046,7 +1046,7 @@ msgstr "??????"
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
 #: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1124
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
-#: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
+#: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:153
 #: engines/ultima/ultima0/metaengine.cpp:45
 #: engines/ultima/ultima4/metaengine.cpp:44
 #: engines/ultima/ultima4/metaengine.cpp:139
@@ -1067,7 +1067,7 @@ msgstr "??????"
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
 #: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1091
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
-#: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
+#: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:45
 #: engines/ultima/ultima4/metaengine.cpp:140
@@ -1088,7 +1088,7 @@ msgstr "?????????"
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
 #: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1104
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
-#: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
+#: engines/twp/metaengine.cpp:151 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
 #: engines/ultima/ultima4/metaengine.cpp:151
 #: engines/wintermute/keymapper_tables.h:320
@@ -1108,7 +1108,7 @@ msgstr "??????"
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
 #: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1111
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
-#: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
+#: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
 #: engines/ultima/ultima4/metaengine.cpp:152
 #: engines/wintermute/keymapper_tables.h:326
@@ -3297,7 +3297,7 @@ msgstr "<????????? ???>"
 
 #. I18N: Abbreviation for 'bytes' as data size
 #. I18N: Hardware key
-#: common/util.cpp:183 backends/platform/3ds/osystem-events.cpp:46
+#: common/util.cpp:190 backends/platform/3ds/osystem-events.cpp:46
 #: backends/platform/ds/osystem_ds.cpp:178
 #: backends/platform/sdl/miyoo/miyoo.cpp:61
 #: backends/platform/sdl/miyoo/miyoo.cpp:88
@@ -3307,22 +3307,22 @@ msgid "B"
 msgstr "B"
 
 #. I18N: Abbreviation for 'kilobytes' as data size
-#: common/util.cpp:189
+#: common/util.cpp:196
 msgid "KB"
 msgstr "KB"
 
 #. I18N: Abbreviation for 'megabytes' as data size
-#: common/util.cpp:194
+#: common/util.cpp:201
 msgid "MB"
 msgstr "MB"
 
 #. I18N: Abbreviation for 'gigabytes' as data size
-#: common/util.cpp:200
+#: common/util.cpp:207
 msgid "GB"
 msgstr "GB"
 
 #. I18N: Abbreviation for 'terabytes' as data size
-#: common/util.cpp:206
+#: common/util.cpp:213
 msgid "TB"
 msgstr "TB"
 
@@ -6341,7 +6341,7 @@ msgstr "??????"
 #: engines/scumm/help.cpp:165 engines/scumm/help.cpp:193
 #: engines/scumm/help.cpp:211 engines/sherlock/metaengine.cpp:425
 #: engines/supernova/metaengine.cpp:336 engines/tot/metaengine.cpp:144
-#: engines/twp/metaengine.cpp:178
+#: engines/twp/metaengine.cpp:177
 msgid "Open"
 msgstr "??????"
 
@@ -6363,7 +6363,7 @@ msgstr "??????"
 #: engines/sherlock/metaengine.cpp:440 engines/sherlock/metaengine.cpp:561
 #: engines/sky/metaengine.cpp:95 engines/sludge/keymapper_tables.h:333
 #: engines/supernova/metaengine.cpp:356 engines/tot/metaengine.cpp:139
-#: engines/tsage/metaengine.cpp:211 engines/twp/metaengine.cpp:186
+#: engines/tsage/metaengine.cpp:211 engines/twp/metaengine.cpp:185
 #: engines/ultima/ultima4/metaengine.cpp:74
 #: engines/ultima/nuvie/metaengine.cpp:69
 #: engines/wintermute/keymapper_tables.h:140
@@ -7122,7 +7122,7 @@ msgstr "?????????"
 
 #: engines/alcachofa/metaengine.cpp:104 engines/queen/metaengine.cpp:279
 #: engines/saga/metaengine.cpp:354 engines/scumm/help.cpp:202
-#: engines/scumm/help.cpp:214 engines/twp/metaengine.cpp:182
+#: engines/scumm/help.cpp:214 engines/twp/metaengine.cpp:181
 #: engines/wintermute/keymapper_tables.h:122
 #: engines/wintermute/keymapper_tables.h:1534
 msgid "Look at"
@@ -7462,7 +7462,7 @@ msgstr "?????? ??????"
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:210
 #: engines/phoenixvr/metaengine.cpp:94 engines/scumm/help.cpp:79
 #: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
-#: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
+#: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:161
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
 msgstr "?????? ????????????"
@@ -8278,7 +8278,7 @@ msgstr "????????? ?????? SFX???"
 #. I18N: Perform action on object where cursor points
 #: engines/darkseed/metaengine.cpp:42 engines/dragons/metaengine.cpp:135
 #: engines/phoenixvr/metaengine.cpp:75 engines/tinsel/metaengine.cpp:294
-#: engines/twp/metaengine.cpp:150
+#: engines/twp/metaengine.cpp:149
 msgid "Action"
 msgstr "??????"
 
@@ -8692,7 +8692,7 @@ msgstr "??????"
 #: engines/scumm/help.cpp:139 engines/scumm/help.cpp:164
 #: engines/scumm/help.cpp:200 engines/scumm/help.cpp:210
 #: engines/sherlock/metaengine.cpp:445 engines/sherlock/metaengine.cpp:566
-#: engines/supernova/metaengine.cpp:366 engines/twp/metaengine.cpp:180
+#: engines/supernova/metaengine.cpp:366 engines/twp/metaengine.cpp:179
 msgid "Give"
 msgstr "??????"
 
@@ -11962,7 +11962,7 @@ msgstr "?????? ?????? ????????? ????????? ??????"
 msgid "Move / Skip / Use default action with inventory item"
 msgstr "???????????? ????????? ?????? ??????/????????????/?????? ?????? ??????"
 
-#: engines/queen/metaengine.cpp:198 engines/twp/metaengine.cpp:176
+#: engines/queen/metaengine.cpp:198 engines/twp/metaengine.cpp:175
 msgid "Skip text"
 msgstr "?????? ????????????"
 
@@ -12002,7 +12002,7 @@ msgstr "?????? ??????"
 #: engines/scumm/help.cpp:144 engines/scumm/help.cpp:169
 #: engines/scumm/help.cpp:198 engines/scumm/help.cpp:213
 #: engines/scumm/help.cpp:230 engines/sherlock/metaengine.cpp:420
-#: engines/sludge/keymapper_tables.h:318 engines/twp/metaengine.cpp:181
+#: engines/sludge/keymapper_tables.h:318 engines/twp/metaengine.cpp:180
 #: engines/wintermute/keymapper_tables.h:134
 #: engines/wintermute/keymapper_tables.h:1546
 msgid "Pick up"
@@ -12011,7 +12011,7 @@ msgstr "??????"
 #: engines/queen/metaengine.cpp:289 engines/saga/metaengine.cpp:364
 #: engines/scumm/help.cpp:199 engines/scumm/help.cpp:215
 #: engines/scumm/help.cpp:251 engines/sludge/keymapper_tables.h:323
-#: engines/twp/metaengine.cpp:183 engines/wintermute/keymapper_tables.h:128
+#: engines/twp/metaengine.cpp:182 engines/wintermute/keymapper_tables.h:128
 #: engines/wintermute/keymapper_tables.h:1558
 msgid "Talk to"
 msgstr "????????????"
@@ -12106,7 +12106,7 @@ msgstr "??????"
 
 #: engines/saga/metaengine.cpp:389 engines/scumm/help.cpp:122
 #: engines/scumm/help.cpp:137 engines/scumm/help.cpp:162
-#: engines/supernova/metaengine.cpp:346 engines/twp/metaengine.cpp:184
+#: engines/supernova/metaengine.cpp:346 engines/twp/metaengine.cpp:183
 msgid "Push"
 msgstr "??????"
 
@@ -12930,7 +12930,7 @@ msgstr "?????? ?????? ??????:"
 
 #: engines/scumm/help.cpp:123 engines/scumm/help.cpp:138
 #: engines/scumm/help.cpp:163 engines/supernova/metaengine.cpp:351
-#: engines/twp/metaengine.cpp:185
+#: engines/twp/metaengine.cpp:184
 msgid "Pull"
 msgstr "?????????"
 
@@ -15156,87 +15156,87 @@ msgstr "???????????? ??????"
 msgid "Ransome *unbeeped* (DLC)"
 msgstr "?????? *??? ?????? ??????* (DLC)"
 
-#: engines/twp/metaengine.cpp:151
+#: engines/twp/metaengine.cpp:150
 msgid "Select highlighted verb"
 msgstr "????????? ?????? ??????"
 
-#: engines/twp/metaengine.cpp:156
+#: engines/twp/metaengine.cpp:155
 msgid "Go to previous object"
 msgstr "?????? ????????? ??????"
 
-#: engines/twp/metaengine.cpp:157
+#: engines/twp/metaengine.cpp:156
 msgid "Go to next object"
 msgstr "?????? ????????? ??????"
 
-#: engines/twp/metaengine.cpp:158
+#: engines/twp/metaengine.cpp:157
 msgid "Select verb / item left"
 msgstr "?????? ?????? / ????????? ??????"
 
-#: engines/twp/metaengine.cpp:159
+#: engines/twp/metaengine.cpp:158
 msgid "Select verb / item right"
 msgstr "????????? ?????? / ????????? ??????"
 
-#: engines/twp/metaengine.cpp:160
+#: engines/twp/metaengine.cpp:159
 msgid "Select verb / item up"
 msgstr "?????? ?????? / ????????? ??????"
 
-#: engines/twp/metaengine.cpp:161
+#: engines/twp/metaengine.cpp:160
 msgid "Select verb / item down"
 msgstr "????????? ?????? / ????????? ??????"
 
-#: engines/twp/metaengine.cpp:163
+#: engines/twp/metaengine.cpp:162
 msgid "Select actor 1"
 msgstr "?????? 1 ??????"
 
-#: engines/twp/metaengine.cpp:164
+#: engines/twp/metaengine.cpp:163
 msgid "Select actor 2"
 msgstr "?????? 2 ??????"
 
-#: engines/twp/metaengine.cpp:165
+#: engines/twp/metaengine.cpp:164
 msgid "Select actor 3"
 msgstr "?????? 3 ??????"
 
-#: engines/twp/metaengine.cpp:166
+#: engines/twp/metaengine.cpp:165
 msgid "Select actor 4"
 msgstr "?????? 4 ??????"
 
-#: engines/twp/metaengine.cpp:167
+#: engines/twp/metaengine.cpp:166
 msgid "Select actor 5"
 msgstr "?????? 5 ??????"
 
-#: engines/twp/metaengine.cpp:168
+#: engines/twp/metaengine.cpp:167
 msgid "Select choice 1"
 msgstr "?????? 1 ??????"
 
-#: engines/twp/metaengine.cpp:169
+#: engines/twp/metaengine.cpp:168
 msgid "Select choice 2"
 msgstr "?????? 2 ??????"
 
-#: engines/twp/metaengine.cpp:170
+#: engines/twp/metaengine.cpp:169
 msgid "Select choice 3"
 msgstr "?????? 3 ??????"
 
-#: engines/twp/metaengine.cpp:171
+#: engines/twp/metaengine.cpp:170
 msgid "Select choice 4"
 msgstr "?????? 4 ??????"
 
-#: engines/twp/metaengine.cpp:172
+#: engines/twp/metaengine.cpp:171
 msgid "Select choice 5"
 msgstr "?????? 5 ??????"
 
-#: engines/twp/metaengine.cpp:173
+#: engines/twp/metaengine.cpp:172
 msgid "Select choice 6"
 msgstr "?????? 6 ??????"
 
-#: engines/twp/metaengine.cpp:174
+#: engines/twp/metaengine.cpp:173
 msgid "Select next Actor"
 msgstr "?????? ?????? ??????"
 
-#: engines/twp/metaengine.cpp:175
+#: engines/twp/metaengine.cpp:174
 msgid "Select previous actor"
 msgstr "?????? ?????? ??????"
 
-#: engines/twp/metaengine.cpp:177
+#: engines/twp/metaengine.cpp:176
 msgid "Show hotspots"
 msgstr "????????? ??????"
 
diff --git a/po/nb_NO.po b/po/nb_NO.po
index e374b68255e..682a2db8bd1 100644
--- a/po/nb_NO.po
+++ b/po/nb_NO.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-13 13:52+0000\n"
+"POT-Creation-Date: 2026-06-14 18:41+0000\n"
 "PO-Revision-Date: 2026-02-07 15:22+0000\n"
 "Last-Translator: Einar Johan Tr??an S??m??en <einarjohants at gmail.com>\n"
 "Language-Team: Norwegian Bokm??l <https://translations.scummvm.org/projects/"
@@ -74,7 +74,7 @@ msgstr "Tilgjengelige motorer:"
 #: engines/scumm/help.cpp:141 engines/scumm/help.cpp:166
 #: engines/scumm/help.cpp:194 engines/scumm/help.cpp:212
 #: engines/sherlock/metaengine.cpp:430 engines/supernova/metaengine.cpp:341
-#: engines/tot/metaengine.cpp:149 engines/twp/metaengine.cpp:179
+#: engines/tot/metaengine.cpp:149 engines/twp/metaengine.cpp:178
 msgid "Close"
 msgstr "Lukk"
 
@@ -1054,7 +1054,7 @@ msgstr "Samhandle"
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
 #: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1124
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
-#: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
+#: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:153
 #: engines/ultima/ultima0/metaengine.cpp:45
 #: engines/ultima/ultima4/metaengine.cpp:44
 #: engines/ultima/ultima4/metaengine.cpp:139
@@ -1075,7 +1075,7 @@ msgstr "Opp"
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
 #: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1091
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
-#: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
+#: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:45
 #: engines/ultima/ultima4/metaengine.cpp:140
@@ -1096,7 +1096,7 @@ msgstr "Ned"
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
 #: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1104
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
-#: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
+#: engines/twp/metaengine.cpp:151 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
 #: engines/ultima/ultima4/metaengine.cpp:151
 #: engines/wintermute/keymapper_tables.h:320
@@ -1116,7 +1116,7 @@ msgstr "Venstre"
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
 #: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1111
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
-#: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
+#: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
 #: engines/ultima/ultima4/metaengine.cpp:152
 #: engines/wintermute/keymapper_tables.h:326
@@ -3406,7 +3406,7 @@ msgstr "<Ugyldig verdi>"
 
 #. I18N: Abbreviation for 'bytes' as data size
 #. I18N: Hardware key
-#: common/util.cpp:183 backends/platform/3ds/osystem-events.cpp:46
+#: common/util.cpp:190 backends/platform/3ds/osystem-events.cpp:46
 #: backends/platform/ds/osystem_ds.cpp:178
 #: backends/platform/sdl/miyoo/miyoo.cpp:61
 #: backends/platform/sdl/miyoo/miyoo.cpp:88
@@ -3416,22 +3416,22 @@ msgid "B"
 msgstr "B"
 
 #. I18N: Abbreviation for 'kilobytes' as data size
-#: common/util.cpp:189
+#: common/util.cpp:196
 msgid "KB"
 msgstr "kB"
 
 #. I18N: Abbreviation for 'megabytes' as data size
-#: common/util.cpp:194
+#: common/util.cpp:201
 msgid "MB"
 msgstr "MB"
 
 #. I18N: Abbreviation for 'gigabytes' as data size
-#: common/util.cpp:200
+#: common/util.cpp:207
 msgid "GB"
 msgstr "GB"
 
 #. I18N: Abbreviation for 'terabytes' as data size
-#: common/util.cpp:206
+#: common/util.cpp:213
 msgid "TB"
 msgstr "TB"
 
@@ -6744,7 +6744,7 @@ msgstr "Kikk"
 #: engines/scumm/help.cpp:165 engines/scumm/help.cpp:193
 #: engines/scumm/help.cpp:211 engines/sherlock/metaengine.cpp:425
 #: engines/supernova/metaengine.cpp:336 engines/tot/metaengine.cpp:144
-#: engines/twp/metaengine.cpp:178
+#: engines/twp/metaengine.cpp:177
 msgid "Open"
 msgstr "??pne"
 
@@ -6766,7 +6766,7 @@ msgstr "F??"
 #: engines/sherlock/metaengine.cpp:440 engines/sherlock/metaengine.cpp:561
 #: engines/sky/metaengine.cpp:95 engines/sludge/keymapper_tables.h:333
 #: engines/supernova/metaengine.cpp:356 engines/tot/metaengine.cpp:139
-#: engines/tsage/metaengine.cpp:211 engines/twp/metaengine.cpp:186
+#: engines/tsage/metaengine.cpp:211 engines/twp/metaengine.cpp:185
 #: engines/ultima/ultima4/metaengine.cpp:74
 #: engines/ultima/nuvie/metaengine.cpp:69
 #: engines/wintermute/keymapper_tables.h:140
@@ -7558,7 +7558,7 @@ msgstr "Aktivere"
 
 #: engines/alcachofa/metaengine.cpp:104 engines/queen/metaengine.cpp:279
 #: engines/saga/metaengine.cpp:354 engines/scumm/help.cpp:202
-#: engines/scumm/help.cpp:214 engines/twp/metaengine.cpp:182
+#: engines/scumm/help.cpp:214 engines/twp/metaengine.cpp:181
 #: engines/wintermute/keymapper_tables.h:122
 #: engines/wintermute/keymapper_tables.h:1534
 msgid "Look at"
@@ -7928,7 +7928,7 @@ msgstr "Skru kampmodus av/p??"
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:210
 #: engines/phoenixvr/metaengine.cpp:94 engines/scumm/help.cpp:79
 #: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
-#: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
+#: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:161
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
 msgstr "Hopp over mellomsekvens"
@@ -8851,7 +8851,7 @@ msgstr ""
 #. I18N: Perform action on object where cursor points
 #: engines/darkseed/metaengine.cpp:42 engines/dragons/metaengine.cpp:135
 #: engines/phoenixvr/metaengine.cpp:75 engines/tinsel/metaengine.cpp:294
-#: engines/twp/metaengine.cpp:150
+#: engines/twp/metaengine.cpp:149
 msgid "Action"
 msgstr "Handling"
 
@@ -9388,7 +9388,7 @@ msgstr "Utrust"
 #: engines/scumm/help.cpp:139 engines/scumm/help.cpp:164
 #: engines/scumm/help.cpp:200 engines/scumm/help.cpp:210
 #: engines/sherlock/metaengine.cpp:445 engines/sherlock/metaengine.cpp:566
-#: engines/supernova/metaengine.cpp:366 engines/twp/metaengine.cpp:180
+#: engines/supernova/metaengine.cpp:366 engines/twp/metaengine.cpp:179
 msgid "Give"
 msgstr "Gi"
 
@@ -13058,7 +13058,7 @@ msgstr "Bruk en tilpasset font som er enklere ?? lese."
 msgid "Move / Skip / Use default action with inventory item"
 msgstr ""
 
-#: engines/queen/metaengine.cpp:198 engines/twp/metaengine.cpp:176
+#: engines/queen/metaengine.cpp:198 engines/twp/metaengine.cpp:175
 #, fuzzy
 #| msgid "Skip text"
 msgid "Skip text"
@@ -13116,7 +13116,7 @@ msgstr "Vis journal"
 #: engines/scumm/help.cpp:144 engines/scumm/help.cpp:169
 #: engines/scumm/help.cpp:198 engines/scumm/help.cpp:213
 #: engines/scumm/help.cpp:230 engines/sherlock/metaengine.cpp:420
-#: engines/sludge/keymapper_tables.h:318 engines/twp/metaengine.cpp:181
+#: engines/sludge/keymapper_tables.h:318 engines/twp/metaengine.cpp:180
 #: engines/wintermute/keymapper_tables.h:134
 #: engines/wintermute/keymapper_tables.h:1546
 msgid "Pick up"
@@ -13125,7 +13125,7 @@ msgstr "Plukk opp"
 #: engines/queen/metaengine.cpp:289 engines/saga/metaengine.cpp:364
 #: engines/scumm/help.cpp:199 engines/scumm/help.cpp:215
 #: engines/scumm/help.cpp:251 engines/sludge/keymapper_tables.h:323
-#: engines/twp/metaengine.cpp:183 engines/wintermute/keymapper_tables.h:128
+#: engines/twp/metaengine.cpp:182 engines/wintermute/keymapper_tables.h:128
 #: engines/wintermute/keymapper_tables.h:1558
 msgid "Talk to"
 msgstr "Snakk til"
@@ -13234,7 +13234,7 @@ msgstr "G?? til"
 
 #: engines/saga/metaengine.cpp:389 engines/scumm/help.cpp:122
 #: engines/scumm/help.cpp:137 engines/scumm/help.cpp:162
-#: engines/supernova/metaengine.cpp:346 engines/twp/metaengine.cpp:184
+#: engines/supernova/metaengine.cpp:346 engines/twp/metaengine.cpp:183
 msgid "Push"
 msgstr "Dytt"
 
@@ -14085,7 +14085,7 @@ msgstr "Hovedkontroller for spill:"
 
 #: engines/scumm/help.cpp:123 engines/scumm/help.cpp:138
 #: engines/scumm/help.cpp:163 engines/supernova/metaengine.cpp:351
-#: engines/twp/metaengine.cpp:185
+#: engines/twp/metaengine.cpp:184
 msgid "Pull"
 msgstr "Dra"
 
@@ -16655,127 +16655,127 @@ msgstr "Klassisk setning"
 msgid "Ransome *unbeeped* (DLC)"
 msgstr ""
 
-#: engines/twp/metaengine.cpp:151
+#: engines/twp/metaengine.cpp:150
 #, fuzzy
 #| msgid "Select shader"
 msgid "Select highlighted verb"
 msgstr "Velg shader"
 
-#: engines/twp/metaengine.cpp:156
+#: engines/twp/metaengine.cpp:155
 #, fuzzy
 #| msgid "Go to previous directory level"
 msgid "Go to previous object"
 msgstr "G?? til forrige mappeniv??"
 
-#: engines/twp/metaengine.cpp:157
+#: engines/twp/metaengine.cpp:156
 #, fuzzy
 #| msgid "Go to setup menu"
 msgid "Go to next object"
 msgstr "G?? til konfigurasjonsmeny"
 
-#: engines/twp/metaengine.cpp:158
+#: engines/twp/metaengine.cpp:157
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Select verb / item left"
 msgstr "Skli mot H??yre"
 
-#: engines/twp/metaengine.cpp:159
+#: engines/twp/metaengine.cpp:158
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Select verb / item right"
 msgstr "Skli mot H??yre"
 
-#: engines/twp/metaengine.cpp:160
+#: engines/twp/metaengine.cpp:159
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Select verb / item up"
 msgstr "Skli mot H??yre"
 
-#: engines/twp/metaengine.cpp:161
+#: engines/twp/metaengine.cpp:160
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Select verb / item down"
 msgstr "Skli mot H??yre"
 
-#: engines/twp/metaengine.cpp:163
+#: engines/twp/metaengine.cpp:162
 #, fuzzy
 #| msgid "Select Group by"
 msgid "Select actor 1"
 msgstr "Velg Gruppere etter"
 
-#: engines/twp/metaengine.cpp:164
+#: engines/twp/metaengine.cpp:163
 #, fuzzy
 #| msgid "Select Group by"
 msgid "Select actor 2"
 msgstr "Velg Gruppere etter"
 
-#: engines/twp/metaengine.cpp:165
+#: engines/twp/metaengine.cpp:164
 #, fuzzy
 #| msgid "Select Group by"
 msgid "Select actor 3"
 msgstr "Velg Gruppere etter"
 
-#: engines/twp/metaengine.cpp:166
+#: engines/twp/metaengine.cpp:165
 #, fuzzy
 #| msgid "Select Group by"
 msgid "Select actor 4"
 msgstr "Velg Gruppere etter"
 
-#: engines/twp/metaengine.cpp:167
+#: engines/twp/metaengine.cpp:166
 #, fuzzy
 #| msgid "Select Group by"
 msgid "Select actor 5"
 msgstr "Velg Gruppere etter"
 
-#: engines/twp/metaengine.cpp:168
+#: engines/twp/metaengine.cpp:167
 #, fuzzy
 #| msgid "Select shader"
 msgid "Select choice 1"
 msgstr "Velg shader"
 
-#: engines/twp/metaengine.cpp:169
+#: engines/twp/metaengine.cpp:168
 #, fuzzy
 #| msgid "Select shader"
 msgid "Select choice 2"
 msgstr "Velg shader"
 
-#: engines/twp/metaengine.cpp:170
+#: engines/twp/metaengine.cpp:169
 #, fuzzy
 #| msgid "Select shader"
 msgid "Select choice 3"
 msgstr "Velg shader"
 
-#: engines/twp/metaengine.cpp:171
+#: engines/twp/metaengine.cpp:170
 #, fuzzy
 #| msgid "Select shader"
 msgid "Select choice 4"
 msgstr "Velg shader"
 
-#: engines/twp/metaengine.cpp:172
+#: engines/twp/metaengine.cpp:171
 #, fuzzy
 #| msgid "Select shader"
 msgid "Select choice 5"
 msgstr "Velg shader"
 
-#: engines/twp/metaengine.cpp:173
+#: engines/twp/metaengine.cpp:172
 #, fuzzy
 #| msgid "Select shader"
 msgid "Select choice 6"
 msgstr "Velg shader"
 
-#: engines/twp/metaengine.cpp:174
+#: engines/twp/metaengine.cpp:173
 #, fuzzy
 #| msgid "Select next music record"
 msgid "Select next Actor"
 msgstr "Velg neste musikkopptak"
 
-#: engines/twp/metaengine.cpp:175
+#: engines/twp/metaengine.cpp:174
 #, fuzzy
 #| msgid "Previous action"
 msgid "Select previous actor"
 msgstr "Forrige handling"
 
-#: engines/twp/metaengine.cpp:177
+#: engines/twp/metaengine.cpp:176
 #, fuzzy
 #| msgid "Show hints"
 msgid "Show hotspots"
diff --git a/po/nl_NL.po b/po/nl_NL.po
index 1e2b3c4cfcd..d62530323f5 100644
--- a/po/nl_NL.po
+++ b/po/nl_NL.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.9.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-13 13:52+0000\n"
+"POT-Creation-Date: 2026-06-14 18:41+0000\n"
 "PO-Revision-Date: 2026-02-11 14:23+0000\n"
 "Last-Translator: SecularSteve <fairfull.playing at gmail.com>\n"
 "Language-Team: Dutch <https://translations.scummvm.org/projects/scummvm/"
@@ -74,7 +74,7 @@ msgstr "Beschikbare engines:"
 #: engines/scumm/help.cpp:141 engines/scumm/help.cpp:166
 #: engines/scumm/help.cpp:194 engines/scumm/help.cpp:212
 #: engines/sherlock/metaengine.cpp:430 engines/supernova/metaengine.cpp:341
-#: engines/tot/metaengine.cpp:149 engines/twp/metaengine.cpp:179
+#: engines/tot/metaengine.cpp:149 engines/twp/metaengine.cpp:178
 msgid "Close"
 msgstr "Sluiten"
 
@@ -1055,7 +1055,7 @@ msgstr "Interactie"
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
 #: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1124
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
-#: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
+#: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:153
 #: engines/ultima/ultima0/metaengine.cpp:45
 #: engines/ultima/ultima4/metaengine.cpp:44
 #: engines/ultima/ultima4/metaengine.cpp:139
@@ -1076,7 +1076,7 @@ msgstr "Omhoog"
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
 #: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1091
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
-#: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
+#: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:45
 #: engines/ultima/ultima4/metaengine.cpp:140
@@ -1097,7 +1097,7 @@ msgstr "Omlaag"
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
 #: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1104
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
-#: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
+#: engines/twp/metaengine.cpp:151 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
 #: engines/ultima/ultima4/metaengine.cpp:151
 #: engines/wintermute/keymapper_tables.h:320
@@ -1117,7 +1117,7 @@ msgstr "Links"
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
 #: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1111
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
-#: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
+#: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
 #: engines/ultima/ultima4/metaengine.cpp:152
 #: engines/wintermute/keymapper_tables.h:326
@@ -3338,7 +3338,7 @@ msgstr "<Ongeldige waarde>"
 
 #. I18N: Abbreviation for 'bytes' as data size
 #. I18N: Hardware key
-#: common/util.cpp:183 backends/platform/3ds/osystem-events.cpp:46
+#: common/util.cpp:190 backends/platform/3ds/osystem-events.cpp:46
 #: backends/platform/ds/osystem_ds.cpp:178
 #: backends/platform/sdl/miyoo/miyoo.cpp:61
 #: backends/platform/sdl/miyoo/miyoo.cpp:88
@@ -3348,22 +3348,22 @@ msgid "B"
 msgstr "B"
 
 #. I18N: Abbreviation for 'kilobytes' as data size
-#: common/util.cpp:189
+#: common/util.cpp:196
 msgid "KB"
 msgstr "KB"
 
 #. I18N: Abbreviation for 'megabytes' as data size
-#: common/util.cpp:194
+#: common/util.cpp:201
 msgid "MB"
 msgstr "MB"
 
 #. I18N: Abbreviation for 'gigabytes' as data size
-#: common/util.cpp:200
+#: common/util.cpp:207
 msgid "GB"
 msgstr "GB"
 
 #. I18N: Abbreviation for 'terabytes' as data size
-#: common/util.cpp:206
+#: common/util.cpp:213
 msgid "TB"
 msgstr "TB"
 
@@ -6415,7 +6415,7 @@ msgstr "Kijk"
 #: engines/scumm/help.cpp:165 engines/scumm/help.cpp:193
 #: engines/scumm/help.cpp:211 engines/sherlock/metaengine.cpp:425
 #: engines/supernova/metaengine.cpp:336 engines/tot/metaengine.cpp:144
-#: engines/twp/metaengine.cpp:178
+#: engines/twp/metaengine.cpp:177
 msgid "Open"
 msgstr "Open"
 
@@ -6437,7 +6437,7 @@ msgstr "Pak"
 #: engines/sherlock/metaengine.cpp:440 engines/sherlock/metaengine.cpp:561
 #: engines/sky/metaengine.cpp:95 engines/sludge/keymapper_tables.h:333
 #: engines/supernova/metaengine.cpp:356 engines/tot/metaengine.cpp:139
-#: engines/tsage/metaengine.cpp:211 engines/twp/metaengine.cpp:186
+#: engines/tsage/metaengine.cpp:211 engines/twp/metaengine.cpp:185
 #: engines/ultima/ultima4/metaengine.cpp:74
 #: engines/ultima/nuvie/metaengine.cpp:69
 #: engines/wintermute/keymapper_tables.h:140
@@ -7217,7 +7217,7 @@ msgstr "Activeren"
 
 #: engines/alcachofa/metaengine.cpp:104 engines/queen/metaengine.cpp:279
 #: engines/saga/metaengine.cpp:354 engines/scumm/help.cpp:202
-#: engines/scumm/help.cpp:214 engines/twp/metaengine.cpp:182
+#: engines/scumm/help.cpp:214 engines/twp/metaengine.cpp:181
 #: engines/wintermute/keymapper_tables.h:122
 #: engines/wintermute/keymapper_tables.h:1534
 msgid "Look at"
@@ -7570,7 +7570,7 @@ msgstr "Schakel gevecht in/uit"
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:210
 #: engines/phoenixvr/metaengine.cpp:94 engines/scumm/help.cpp:79
 #: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
-#: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
+#: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:161
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
 msgstr "Cutscene overslaan"
@@ -8423,7 +8423,7 @@ msgstr "Floppy-versie alleen SFX"
 #. I18N: Perform action on object where cursor points
 #: engines/darkseed/metaengine.cpp:42 engines/dragons/metaengine.cpp:135
 #: engines/phoenixvr/metaengine.cpp:75 engines/tinsel/metaengine.cpp:294
-#: engines/twp/metaengine.cpp:150
+#: engines/twp/metaengine.cpp:149
 msgid "Action"
 msgstr "Actie"
 
@@ -8846,7 +8846,7 @@ msgstr "Uitrusten"
 #: engines/scumm/help.cpp:139 engines/scumm/help.cpp:164
 #: engines/scumm/help.cpp:200 engines/scumm/help.cpp:210
 #: engines/sherlock/metaengine.cpp:445 engines/sherlock/metaengine.cpp:566
-#: engines/supernova/metaengine.cpp:366 engines/twp/metaengine.cpp:180
+#: engines/supernova/metaengine.cpp:366 engines/twp/metaengine.cpp:179
 msgid "Give"
 msgstr "Geef"
 
@@ -12276,7 +12276,7 @@ msgstr "Gebruik een beter leesbaar aangepast lettertype"
 msgid "Move / Skip / Use default action with inventory item"
 msgstr "Verplaatsen / Overslaan / Standaardactie gebruiken met inventarisitem"
 
-#: engines/queen/metaengine.cpp:198 engines/twp/metaengine.cpp:176
+#: engines/queen/metaengine.cpp:198 engines/twp/metaengine.cpp:175
 msgid "Skip text"
 msgstr "Tekst overslaan"
 
@@ -12316,7 +12316,7 @@ msgstr "Gebruik dagboek"
 #: engines/scumm/help.cpp:144 engines/scumm/help.cpp:169
 #: engines/scumm/help.cpp:198 engines/scumm/help.cpp:213
 #: engines/scumm/help.cpp:230 engines/sherlock/metaengine.cpp:420
-#: engines/sludge/keymapper_tables.h:318 engines/twp/metaengine.cpp:181
+#: engines/sludge/keymapper_tables.h:318 engines/twp/metaengine.cpp:180
 #: engines/wintermute/keymapper_tables.h:134
 #: engines/wintermute/keymapper_tables.h:1546
 msgid "Pick up"
@@ -12325,7 +12325,7 @@ msgstr "Pak op"
 #: engines/queen/metaengine.cpp:289 engines/saga/metaengine.cpp:364
 #: engines/scumm/help.cpp:199 engines/scumm/help.cpp:215
 #: engines/scumm/help.cpp:251 engines/sludge/keymapper_tables.h:323
-#: engines/twp/metaengine.cpp:183 engines/wintermute/keymapper_tables.h:128
+#: engines/twp/metaengine.cpp:182 engines/wintermute/keymapper_tables.h:128
 #: engines/wintermute/keymapper_tables.h:1558
 msgid "Talk to"
 msgstr "Praat met"
@@ -12420,7 +12420,7 @@ msgstr "Loop naar"
 
 #: engines/saga/metaengine.cpp:389 engines/scumm/help.cpp:122
 #: engines/scumm/help.cpp:137 engines/scumm/help.cpp:162
-#: engines/supernova/metaengine.cpp:346 engines/twp/metaengine.cpp:184
+#: engines/supernova/metaengine.cpp:346 engines/twp/metaengine.cpp:183
 msgid "Push"
 msgstr "Duw"
 
@@ -13288,7 +13288,7 @@ msgstr "Hoofd spel besturing:"
 
 #: engines/scumm/help.cpp:123 engines/scumm/help.cpp:138
 #: engines/scumm/help.cpp:163 engines/supernova/metaengine.cpp:351
-#: engines/twp/metaengine.cpp:185
+#: engines/twp/metaengine.cpp:184
 msgid "Pull"
 msgstr "Trek"
 
@@ -15561,87 +15561,87 @@ msgstr "Klassieke zin"
 msgid "Ransome *unbeeped* (DLC)"
 msgstr "Ransome *niet gepiept* (DLC)"
 
-#: engines/twp/metaengine.cpp:151
+#: engines/twp/metaengine.cpp:150
 msgid "Select highlighted verb"
 msgstr "Selecteer gemarkeerd werkwoord"
 
-#: engines/twp/metaengine.cpp:156
+#: engines/twp/metaengine.cpp:155
 msgid "Go to previous object"
 msgstr "Ga naar vorig object"
 
-#: engines/twp/metaengine.cpp:157
+#: engines/twp/metaengine.cpp:156
 msgid "Go to next object"
 msgstr "Ga naar het volgende object"
 
-#: engines/twp/metaengine.cpp:158
+#: engines/twp/metaengine.cpp:157
 msgid "Select verb / item left"
 msgstr "Selecteer werkwoord / item links"
 
-#: engines/twp/metaengine.cpp:159
+#: engines/twp/metaengine.cpp:158
 msgid "Select verb / item right"
 msgstr "Selecteer werkwoord / item rechts"
 
-#: engines/twp/metaengine.cpp:160
+#: engines/twp/metaengine.cpp:159
 msgid "Select verb / item up"
 msgstr "Werkwoord/item selecteren"
 
-#: engines/twp/metaengine.cpp:161
+#: engines/twp/metaengine.cpp:160
 msgid "Select verb / item down"
 msgstr "Werkwoord/item selecteren"
 
-#: engines/twp/metaengine.cpp:163
+#: engines/twp/metaengine.cpp:162
 msgid "Select actor 1"
 msgstr "Selecteer acteur 1"
 
-#: engines/twp/metaengine.cpp:164
+#: engines/twp/metaengine.cpp:163
 msgid "Select actor 2"
 msgstr "Selecteer acteur 2"
 
-#: engines/twp/metaengine.cpp:165
+#: engines/twp/metaengine.cpp:164
 msgid "Select actor 3"
 msgstr "Selecteer acteur 3"
 
-#: engines/twp/metaengine.cpp:166
+#: engines/twp/metaengine.cpp:165
 msgid "Select actor 4"
 msgstr "Selecteer acteur 4"
 
-#: engines/twp/metaengine.cpp:167
+#: engines/twp/metaengine.cpp:166
 msgid "Select actor 5"
 msgstr "Selecteer acteur 5"
 
-#: engines/twp/metaengine.cpp:168
+#: engines/twp/metaengine.cpp:167
 msgid "Select choice 1"
 msgstr "Selecteer keuze 1"
 
-#: engines/twp/metaengine.cpp:169
+#: engines/twp/metaengine.cpp:168
 msgid "Select choice 2"
 msgstr "Selecteer keuze 2"
 
-#: engines/twp/metaengine.cpp:170
+#: engines/twp/metaengine.cpp:169
 msgid "Select choice 3"
 msgstr "Selecteer keuze 3"
 
-#: engines/twp/metaengine.cpp:171
+#: engines/twp/metaengine.cpp:170
 msgid "Select choice 4"
 msgstr "Selecteer keuze 4"
 
-#: engines/twp/metaengine.cpp:172
+#: engines/twp/metaengine.cpp:171
 msgid "Select choice 5"
 msgstr "Selecteer keuze 5"
 
-#: engines/twp/metaengine.cpp:173
+#: engines/twp/metaengine.cpp:172
 msgid "Select choice 6"
 msgstr "Selecteer keuze 6"
 
-#: engines/twp/metaengine.cpp:174
+#: engines/twp/metaengine.cpp:173
 msgid "Select next Actor"
 msgstr "Selecteer volgende acteur"
 
-#: engines/twp/metaengine.cpp:175
+#: engines/twp/metaengine.cpp:174
 msgid "Select previous actor"
 msgstr "Vorige acteur selecteren"
 
-#: engines/twp/metaengine.cpp:177
+#: engines/twp/metaengine.cpp:176
 msgid "Show hotspots"
 msgstr "Toon hotspots"
 
diff --git a/po/nn_NO.po b/po/nn_NO.po
index 53fdc1dd67c..b796e548b76 100644
--- a/po/nn_NO.po
+++ b/po/nn_NO.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-13 13:52+0000\n"
+"POT-Creation-Date: 2026-06-14 18:41+0000\n"
 "PO-Revision-Date: 2023-12-14 14:28+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Norwegian Nynorsk <https://translations.scummvm.org/projects/"
@@ -77,7 +77,7 @@ msgstr "Tilgjengelege motorar:"
 #: engines/scumm/help.cpp:141 engines/scumm/help.cpp:166
 #: engines/scumm/help.cpp:194 engines/scumm/help.cpp:212
 #: engines/sherlock/metaengine.cpp:430 engines/supernova/metaengine.cpp:341
-#: engines/tot/metaengine.cpp:149 engines/twp/metaengine.cpp:179
+#: engines/tot/metaengine.cpp:149 engines/twp/metaengine.cpp:178
 msgid "Close"
 msgstr "Steng"
 
@@ -1059,7 +1059,7 @@ msgstr "Interager"
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
 #: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1124
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
-#: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
+#: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:153
 #: engines/ultima/ultima0/metaengine.cpp:45
 #: engines/ultima/ultima4/metaengine.cpp:44
 #: engines/ultima/ultima4/metaengine.cpp:139
@@ -1080,7 +1080,7 @@ msgstr "Opp"
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
 #: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1091
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
-#: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
+#: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:45
 #: engines/ultima/ultima4/metaengine.cpp:140
@@ -1101,7 +1101,7 @@ msgstr "Ned"
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
 #: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1104
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
-#: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
+#: engines/twp/metaengine.cpp:151 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
 #: engines/ultima/ultima4/metaengine.cpp:151
 #: engines/wintermute/keymapper_tables.h:320
@@ -1121,7 +1121,7 @@ msgstr "Venstre"
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
 #: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1111
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
-#: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
+#: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
 #: engines/ultima/ultima4/metaengine.cpp:152
 #: engines/wintermute/keymapper_tables.h:326
@@ -3272,7 +3272,7 @@ msgstr "T??m verdi"
 
 #. I18N: Abbreviation for 'bytes' as data size
 #. I18N: Hardware key
-#: common/util.cpp:183 backends/platform/3ds/osystem-events.cpp:46
+#: common/util.cpp:190 backends/platform/3ds/osystem-events.cpp:46
 #: backends/platform/ds/osystem_ds.cpp:178
 #: backends/platform/sdl/miyoo/miyoo.cpp:61
 #: backends/platform/sdl/miyoo/miyoo.cpp:88
@@ -3282,23 +3282,23 @@ msgid "B"
 msgstr ""
 
 #. I18N: Abbreviation for 'kilobytes' as data size
-#: common/util.cpp:189
+#: common/util.cpp:196
 msgid "KB"
 msgstr ""
 
 #. I18N: Abbreviation for 'megabytes' as data size
-#: common/util.cpp:194
+#: common/util.cpp:201
 #, fuzzy
 msgid "MB"
 msgstr "SMB"
 
 #. I18N: Abbreviation for 'gigabytes' as data size
-#: common/util.cpp:200
+#: common/util.cpp:207
 msgid "GB"
 msgstr ""
 
 #. I18N: Abbreviation for 'terabytes' as data size
-#: common/util.cpp:206
+#: common/util.cpp:213
 msgid "TB"
 msgstr ""
 
@@ -6153,7 +6153,7 @@ msgstr "Kikk"
 #: engines/scumm/help.cpp:165 engines/scumm/help.cpp:193
 #: engines/scumm/help.cpp:211 engines/sherlock/metaengine.cpp:425
 #: engines/supernova/metaengine.cpp:336 engines/tot/metaengine.cpp:144
-#: engines/twp/metaengine.cpp:178
+#: engines/twp/metaengine.cpp:177
 msgid "Open"
 msgstr "??pne"
 
@@ -6175,7 +6175,7 @@ msgstr "F??"
 #: engines/sherlock/metaengine.cpp:440 engines/sherlock/metaengine.cpp:561
 #: engines/sky/metaengine.cpp:95 engines/sludge/keymapper_tables.h:333
 #: engines/supernova/metaengine.cpp:356 engines/tot/metaengine.cpp:139
-#: engines/tsage/metaengine.cpp:211 engines/twp/metaengine.cpp:186
+#: engines/tsage/metaengine.cpp:211 engines/twp/metaengine.cpp:185
 #: engines/ultima/ultima4/metaengine.cpp:74
 #: engines/ultima/nuvie/metaengine.cpp:69
 #: engines/wintermute/keymapper_tables.h:140
@@ -6937,7 +6937,7 @@ msgstr "Aktiv"
 
 #: engines/alcachofa/metaengine.cpp:104 engines/queen/metaengine.cpp:279
 #: engines/saga/metaengine.cpp:354 engines/scumm/help.cpp:202
-#: engines/scumm/help.cpp:214 engines/twp/metaengine.cpp:182
+#: engines/scumm/help.cpp:214 engines/twp/metaengine.cpp:181
 #: engines/wintermute/keymapper_tables.h:122
 #: engines/wintermute/keymapper_tables.h:1534
 msgid "Look at"
@@ -7302,7 +7302,7 @@ msgstr "Veksle musl??sing"
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:210
 #: engines/phoenixvr/metaengine.cpp:94 engines/scumm/help.cpp:79
 #: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
-#: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
+#: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:161
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
 msgstr "Hopp over cutscene"
@@ -8217,7 +8217,7 @@ msgstr ""
 #. I18N: Perform action on object where cursor points
 #: engines/darkseed/metaengine.cpp:42 engines/dragons/metaengine.cpp:135
 #: engines/phoenixvr/metaengine.cpp:75 engines/tinsel/metaengine.cpp:294
-#: engines/twp/metaengine.cpp:150
+#: engines/twp/metaengine.cpp:149
 #, fuzzy
 msgid "Action"
 msgstr "Aktiv"
@@ -8730,7 +8730,7 @@ msgstr ""
 #: engines/scumm/help.cpp:139 engines/scumm/help.cpp:164
 #: engines/scumm/help.cpp:200 engines/scumm/help.cpp:210
 #: engines/sherlock/metaengine.cpp:445 engines/sherlock/metaengine.cpp:566
-#: engines/supernova/metaengine.cpp:366 engines/twp/metaengine.cpp:180
+#: engines/supernova/metaengine.cpp:366 engines/twp/metaengine.cpp:179
 msgid "Give"
 msgstr "Gi"
 
@@ -12247,7 +12247,7 @@ msgstr ""
 msgid "Move / Skip / Use default action with inventory item"
 msgstr ""
 
-#: engines/queen/metaengine.cpp:198 engines/twp/metaengine.cpp:176
+#: engines/queen/metaengine.cpp:198 engines/twp/metaengine.cpp:175
 #, fuzzy
 #| msgid "Skip text"
 msgid "Skip text"
@@ -12302,7 +12302,7 @@ msgstr "Syn alle"
 #: engines/scumm/help.cpp:144 engines/scumm/help.cpp:169
 #: engines/scumm/help.cpp:198 engines/scumm/help.cpp:213
 #: engines/scumm/help.cpp:230 engines/sherlock/metaengine.cpp:420
-#: engines/sludge/keymapper_tables.h:318 engines/twp/metaengine.cpp:181
+#: engines/sludge/keymapper_tables.h:318 engines/twp/metaengine.cpp:180
 #: engines/wintermute/keymapper_tables.h:134
 #: engines/wintermute/keymapper_tables.h:1546
 msgid "Pick up"
@@ -12311,7 +12311,7 @@ msgstr "Plukk opp"
 #: engines/queen/metaengine.cpp:289 engines/saga/metaengine.cpp:364
 #: engines/scumm/help.cpp:199 engines/scumm/help.cpp:215
 #: engines/scumm/help.cpp:251 engines/sludge/keymapper_tables.h:323
-#: engines/twp/metaengine.cpp:183 engines/wintermute/keymapper_tables.h:128
+#: engines/twp/metaengine.cpp:182 engines/wintermute/keymapper_tables.h:128
 #: engines/wintermute/keymapper_tables.h:1558
 msgid "Talk to"
 msgstr "Snakk til"
@@ -12426,7 +12426,7 @@ msgstr "G?? til"
 
 #: engines/saga/metaengine.cpp:389 engines/scumm/help.cpp:122
 #: engines/scumm/help.cpp:137 engines/scumm/help.cpp:162
-#: engines/supernova/metaengine.cpp:346 engines/twp/metaengine.cpp:184
+#: engines/supernova/metaengine.cpp:346 engines/twp/metaengine.cpp:183
 msgid "Push"
 msgstr "Dytt"
 
@@ -13219,7 +13219,7 @@ msgstr "Hovedkontrollar for spel:"
 
 #: engines/scumm/help.cpp:123 engines/scumm/help.cpp:138
 #: engines/scumm/help.cpp:163 engines/supernova/metaengine.cpp:351
-#: engines/twp/metaengine.cpp:185
+#: engines/twp/metaengine.cpp:184
 msgid "Pull"
 msgstr "Dra"
 
@@ -15641,117 +15641,117 @@ msgstr ""
 msgid "Ransome *unbeeped* (DLC)"
 msgstr ""
 
-#: engines/twp/metaengine.cpp:151
+#: engines/twp/metaengine.cpp:150
 #, fuzzy
 msgid "Select highlighted verb"
 msgstr "Vel eit tema"
 
-#: engines/twp/metaengine.cpp:156
+#: engines/twp/metaengine.cpp:155
 #, fuzzy
 #| msgid "Go to previous directory level"
 msgid "Go to previous object"
 msgstr "G?? til forrige mappeniv??"
 
-#: engines/twp/metaengine.cpp:157
+#: engines/twp/metaengine.cpp:156
 #, fuzzy
 msgid "Go to next object"
 msgstr "Val"
 
-#: engines/twp/metaengine.cpp:158
+#: engines/twp/metaengine.cpp:157
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Select verb / item left"
 msgstr "Skli til H??yre"
 
-#: engines/twp/metaengine.cpp:159
+#: engines/twp/metaengine.cpp:158
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Select verb / item right"
 msgstr "Skli til H??yre"
 
-#: engines/twp/metaengine.cpp:160
+#: engines/twp/metaengine.cpp:159
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Select verb / item up"
 msgstr "Skli til H??yre"
 
-#: engines/twp/metaengine.cpp:161
+#: engines/twp/metaengine.cpp:160
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Select verb / item down"
 msgstr "Skli til H??yre"
 
-#: engines/twp/metaengine.cpp:163
+#: engines/twp/metaengine.cpp:162
 #, fuzzy
 #| msgid "Select Group by"
 msgid "Select actor 1"
 msgstr "Grupp??r etter"
 
-#: engines/twp/metaengine.cpp:164
+#: engines/twp/metaengine.cpp:163
 #, fuzzy
 #| msgid "Select Group by"
 msgid "Select actor 2"
 msgstr "Grupp??r etter"
 
-#: engines/twp/metaengine.cpp:165
+#: engines/twp/metaengine.cpp:164
 #, fuzzy
 #| msgid "Select Group by"
 msgid "Select actor 3"
 msgstr "Grupp??r etter"
 
-#: engines/twp/metaengine.cpp:166
+#: engines/twp/metaengine.cpp:165
 #, fuzzy
 #| msgid "Select Group by"
 msgid "Select actor 4"
 msgstr "Grupp??r etter"
 
-#: engines/twp/metaengine.cpp:167
+#: engines/twp/metaengine.cpp:166
 #, fuzzy
 #| msgid "Select Group by"
 msgid "Select actor 5"
 msgstr "Grupp??r etter"
 
-#: engines/twp/metaengine.cpp:168
+#: engines/twp/metaengine.cpp:167
 #, fuzzy
 msgid "Select choice 1"
 msgstr "Vel eit tema"
 
-#: engines/twp/metaengine.cpp:169
+#: engines/twp/metaengine.cpp:168
 #, fuzzy
 msgid "Select choice 2"
 msgstr "Vel eit tema"
 
-#: engines/twp/metaengine.cpp:170
+#: engines/twp/metaengine.cpp:169
 #, fuzzy
 msgid "Select choice 3"
 msgstr "Vel eit tema"
 
-#: engines/twp/metaengine.cpp:171
+#: engines/twp/metaengine.cpp:170
 #, fuzzy
 msgid "Select choice 4"
 msgstr "Vel eit tema"
 
-#: engines/twp/metaengine.cpp:172
+#: engines/twp/metaengine.cpp:171
 #, fuzzy
 msgid "Select choice 5"
 msgstr "Vel eit tema"
 
-#: engines/twp/metaengine.cpp:173
+#: engines/twp/metaengine.cpp:172
 #, fuzzy
 msgid "Select choice 6"
 msgstr "Vel eit tema"
 
-#: engines/twp/metaengine.cpp:174
+#: engines/twp/metaengine.cpp:173
 #, fuzzy
 msgid "Select next Actor"
 msgstr "Vel eit tema"
 
-#: engines/twp/metaengine.cpp:175
+#: engines/twp/metaengine.cpp:174
 #, fuzzy
 msgid "Select previous actor"
 msgstr "~F~orrige"
 
-#: engines/twp/metaengine.cpp:177
+#: engines/twp/metaengine.cpp:176
 #, fuzzy
 msgid "Show hotspots"
 msgstr "Vis skjulte filer"
diff --git a/po/pl_PL.po b/po/pl_PL.po
index cab1d3bf619..07a79411249 100644
--- a/po/pl_PL.po
+++ b/po/pl_PL.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-13 13:52+0000\n"
+"POT-Creation-Date: 2026-06-14 18:41+0000\n"
 "PO-Revision-Date: 2026-04-17 00:22+0000\n"
 "Last-Translator: SkiffPL <skiff at windowslive.com>\n"
 "Language-Team: Polish <https://translations.scummvm.org/projects/scummvm/"
@@ -77,7 +77,7 @@ msgstr "Dost??pne silniki:"
 #: engines/scumm/help.cpp:141 engines/scumm/help.cpp:166
 #: engines/scumm/help.cpp:194 engines/scumm/help.cpp:212
 #: engines/sherlock/metaengine.cpp:430 engines/supernova/metaengine.cpp:341
-#: engines/tot/metaengine.cpp:149 engines/twp/metaengine.cpp:179
+#: engines/tot/metaengine.cpp:149 engines/twp/metaengine.cpp:178
 msgid "Close"
 msgstr "Zamknij"
 
@@ -1056,7 +1056,7 @@ msgstr "Interakcja"
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
 #: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1124
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
-#: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
+#: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:153
 #: engines/ultima/ultima0/metaengine.cpp:45
 #: engines/ultima/ultima4/metaengine.cpp:44
 #: engines/ultima/ultima4/metaengine.cpp:139
@@ -1077,7 +1077,7 @@ msgstr "W g??r??"
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
 #: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1091
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
-#: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
+#: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:45
 #: engines/ultima/ultima4/metaengine.cpp:140
@@ -1098,7 +1098,7 @@ msgstr "W d????"
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
 #: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1104
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
-#: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
+#: engines/twp/metaengine.cpp:151 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
 #: engines/ultima/ultima4/metaengine.cpp:151
 #: engines/wintermute/keymapper_tables.h:320
@@ -1118,7 +1118,7 @@ msgstr "W lewo"
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
 #: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1111
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
-#: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
+#: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
 #: engines/ultima/ultima4/metaengine.cpp:152
 #: engines/wintermute/keymapper_tables.h:326
@@ -3341,7 +3341,7 @@ msgstr "<Nieprawid??owa warto????>"
 
 #. I18N: Abbreviation for 'bytes' as data size
 #. I18N: Hardware key
-#: common/util.cpp:183 backends/platform/3ds/osystem-events.cpp:46
+#: common/util.cpp:190 backends/platform/3ds/osystem-events.cpp:46
 #: backends/platform/ds/osystem_ds.cpp:178
 #: backends/platform/sdl/miyoo/miyoo.cpp:61
 #: backends/platform/sdl/miyoo/miyoo.cpp:88
@@ -3351,22 +3351,22 @@ msgid "B"
 msgstr "B"
 
 #. I18N: Abbreviation for 'kilobytes' as data size
-#: common/util.cpp:189
+#: common/util.cpp:196
 msgid "KB"
 msgstr "KB"
 
 #. I18N: Abbreviation for 'megabytes' as data size
-#: common/util.cpp:194
+#: common/util.cpp:201
 msgid "MB"
 msgstr "MB"
 
 #. I18N: Abbreviation for 'gigabytes' as data size
-#: common/util.cpp:200
+#: common/util.cpp:207
 msgid "GB"
 msgstr "GB"
 
 #. I18N: Abbreviation for 'terabytes' as data size
-#: common/util.cpp:206
+#: common/util.cpp:213
 msgid "TB"
 msgstr "TB"
 
@@ -6427,7 +6427,7 @@ msgstr "Sp??jrz"
 #: engines/scumm/help.cpp:165 engines/scumm/help.cpp:193
 #: engines/scumm/help.cpp:211 engines/sherlock/metaengine.cpp:425
 #: engines/supernova/metaengine.cpp:336 engines/tot/metaengine.cpp:144
-#: engines/twp/metaengine.cpp:178
+#: engines/twp/metaengine.cpp:177
 msgid "Open"
 msgstr "Otw??rz"
 
@@ -6449,7 +6449,7 @@ msgstr "We??"
 #: engines/sherlock/metaengine.cpp:440 engines/sherlock/metaengine.cpp:561
 #: engines/sky/metaengine.cpp:95 engines/sludge/keymapper_tables.h:333
 #: engines/supernova/metaengine.cpp:356 engines/tot/metaengine.cpp:139
-#: engines/tsage/metaengine.cpp:211 engines/twp/metaengine.cpp:186
+#: engines/tsage/metaengine.cpp:211 engines/twp/metaengine.cpp:185
 #: engines/ultima/ultima4/metaengine.cpp:74
 #: engines/ultima/nuvie/metaengine.cpp:69
 #: engines/wintermute/keymapper_tables.h:140
@@ -7230,7 +7230,7 @@ msgstr "Aktywuj"
 
 #: engines/alcachofa/metaengine.cpp:104 engines/queen/metaengine.cpp:279
 #: engines/saga/metaengine.cpp:354 engines/scumm/help.cpp:202
-#: engines/scumm/help.cpp:214 engines/twp/metaengine.cpp:182
+#: engines/scumm/help.cpp:214 engines/twp/metaengine.cpp:181
 #: engines/wintermute/keymapper_tables.h:122
 #: engines/wintermute/keymapper_tables.h:1534
 msgid "Look at"
@@ -7584,7 +7584,7 @@ msgstr "Prze????cz tryb walki"
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:210
 #: engines/phoenixvr/metaengine.cpp:94 engines/scumm/help.cpp:79
 #: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
-#: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
+#: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:161
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
 msgstr "Pomi?? scenk??"
@@ -8403,7 +8403,7 @@ msgstr ""
 #. I18N: Perform action on object where cursor points
 #: engines/darkseed/metaengine.cpp:42 engines/dragons/metaengine.cpp:135
 #: engines/phoenixvr/metaengine.cpp:75 engines/tinsel/metaengine.cpp:294
-#: engines/twp/metaengine.cpp:150
+#: engines/twp/metaengine.cpp:149
 msgid "Action"
 msgstr "Akcja"
 
@@ -8915,7 +8915,7 @@ msgstr ""
 #: engines/scumm/help.cpp:139 engines/scumm/help.cpp:164
 #: engines/scumm/help.cpp:200 engines/scumm/help.cpp:210
 #: engines/sherlock/metaengine.cpp:445 engines/sherlock/metaengine.cpp:566
-#: engines/supernova/metaengine.cpp:366 engines/twp/metaengine.cpp:180
+#: engines/supernova/metaengine.cpp:366 engines/twp/metaengine.cpp:179
 msgid "Give"
 msgstr "Daj"
 
@@ -12462,7 +12462,7 @@ msgstr "U??yj ??atwiejszej do odczytania czcionki"
 msgid "Move / Skip / Use default action with inventory item"
 msgstr "Ruch / Pomijanie / Domy??lna akcja dla tego elementu ekwipunku"
 
-#: engines/queen/metaengine.cpp:198 engines/twp/metaengine.cpp:176
+#: engines/queen/metaengine.cpp:198 engines/twp/metaengine.cpp:175
 msgid "Skip text"
 msgstr "Pomi?? tekst"
 
@@ -12502,7 +12502,7 @@ msgstr "U??yj dziennik"
 #: engines/scumm/help.cpp:144 engines/scumm/help.cpp:169
 #: engines/scumm/help.cpp:198 engines/scumm/help.cpp:213
 #: engines/scumm/help.cpp:230 engines/sherlock/metaengine.cpp:420
-#: engines/sludge/keymapper_tables.h:318 engines/twp/metaengine.cpp:181
+#: engines/sludge/keymapper_tables.h:318 engines/twp/metaengine.cpp:180
 #: engines/wintermute/keymapper_tables.h:134
 #: engines/wintermute/keymapper_tables.h:1546
 msgid "Pick up"
@@ -12511,7 +12511,7 @@ msgstr "Podnie??"
 #: engines/queen/metaengine.cpp:289 engines/saga/metaengine.cpp:364
 #: engines/scumm/help.cpp:199 engines/scumm/help.cpp:215
 #: engines/scumm/help.cpp:251 engines/sludge/keymapper_tables.h:323
-#: engines/twp/metaengine.cpp:183 engines/wintermute/keymapper_tables.h:128
+#: engines/twp/metaengine.cpp:182 engines/wintermute/keymapper_tables.h:128
 #: engines/wintermute/keymapper_tables.h:1558
 msgid "Talk to"
 msgstr "Rozmawiaj z"
@@ -12616,7 +12616,7 @@ msgstr "Podejd?? do"
 
 #: engines/saga/metaengine.cpp:389 engines/scumm/help.cpp:122
 #: engines/scumm/help.cpp:137 engines/scumm/help.cpp:162
-#: engines/supernova/metaengine.cpp:346 engines/twp/metaengine.cpp:184
+#: engines/supernova/metaengine.cpp:346 engines/twp/metaengine.cpp:183
 msgid "Push"
 msgstr "Pchnij"
 
@@ -13470,7 +13470,7 @@ msgstr "G????wne sterowanie gry:"
 
 #: engines/scumm/help.cpp:123 engines/scumm/help.cpp:138
 #: engines/scumm/help.cpp:163 engines/supernova/metaengine.cpp:351
-#: engines/twp/metaengine.cpp:185
+#: engines/twp/metaengine.cpp:184
 msgid "Pull"
 msgstr "Poci??gnij"
 
@@ -15832,87 +15832,87 @@ msgstr "Zdania klasyczne"
 msgid "Ransome *unbeeped* (DLC)"
 msgstr "DLC Ransome *unbeeped*"
 
-#: engines/twp/metaengine.cpp:151
+#: engines/twp/metaengine.cpp:150
 msgid "Select highlighted verb"
 msgstr "Zaznacz pod??wietlony czasownik"
 
-#: engines/twp/metaengine.cpp:156
+#: engines/twp/metaengine.cpp:155
 msgid "Go to previous object"
 msgstr "Id?? do poprzedniego obiektu"
 
-#: engines/twp/metaengine.cpp:157
+#: engines/twp/metaengine.cpp:156
 msgid "Go to next object"
 msgstr "Id?? do nast??pnego obiektu"
 
-#: engines/twp/metaengine.cpp:158
+#: engines/twp/metaengine.cpp:157
 msgid "Select verb / item left"
 msgstr "Wybierz czasownik/element z lewej"
 
-#: engines/twp/metaengine.cpp:159
+#: engines/twp/metaengine.cpp:158
 msgid "Select verb / item right"
 msgstr "Wybierz czasownik/element z prawej"
 
-#: engines/twp/metaengine.cpp:160
+#: engines/twp/metaengine.cpp:159
 msgid "Select verb / item up"
 msgstr "Wybierz czasownik/element u g??ry"
 
-#: engines/twp/metaengine.cpp:161
+#: engines/twp/metaengine.cpp:160
 msgid "Select verb / item down"
 msgstr "Wybierz czasownik/element u do??u"
 
-#: engines/twp/metaengine.cpp:163
+#: engines/twp/metaengine.cpp:162
 msgid "Select actor 1"
 msgstr "Wybierz aktora 1"
 
-#: engines/twp/metaengine.cpp:164
+#: engines/twp/metaengine.cpp:163
 msgid "Select actor 2"
 msgstr "Wybierz aktora 2"
 
-#: engines/twp/metaengine.cpp:165
+#: engines/twp/metaengine.cpp:164
 msgid "Select actor 3"
 msgstr "Wybierz aktora 3"
 
-#: engines/twp/metaengine.cpp:166
+#: engines/twp/metaengine.cpp:165
 msgid "Select actor 4"
 msgstr "Wybierz aktora 4"
 
-#: engines/twp/metaengine.cpp:167
+#: engines/twp/metaengine.cpp:166
 msgid "Select actor 5"
 msgstr "Wybierz aktora 5"
 
-#: engines/twp/metaengine.cpp:168
+#: engines/twp/metaengine.cpp:167
 msgid "Select choice 1"
 msgstr "Zaznacz wyb??r 1"
 
-#: engines/twp/metaengine.cpp:169
+#: engines/twp/metaengine.cpp:168
 msgid "Select choice 2"
 msgstr "Zaznacz wyb??r 2"
 
-#: engines/twp/metaengine.cpp:170
+#: engines/twp/metaengine.cpp:169
 msgid "Select choice 3"
 msgstr "Zaznacz wyb??r 3"
 
-#: engines/twp/metaengine.cpp:171
+#: engines/twp/metaengine.cpp:170
 msgid "Select choice 4"
 msgstr "Zaznacz wyb??r 4"
 
-#: engines/twp/metaengine.cpp:172
+#: engines/twp/metaengine.cpp:171
 msgid "Select choice 5"
 msgstr "Zaznacz wyb??r 5"
 
-#: engines/twp/metaengine.cpp:173
+#: engines/twp/metaengine.cpp:172
 msgid "Select choice 6"
 msgstr "Zaznacz wyb??r 6"
 
-#: engines/twp/metaengine.cpp:174
+#: engines/twp/metaengine.cpp:173
 msgid "Select next Actor"
 msgstr "Wybierz nast??pnego aktora"
 
-#: engines/twp/metaengine.cpp:175
+#: engines/twp/metaengine.cpp:174
 msgid "Select previous actor"
 msgstr "Wybierz poprzedniego aktora"
 
-#: engines/twp/metaengine.cpp:177
+#: engines/twp/metaengine.cpp:176
 msgid "Show hotspots"
 msgstr "Pokazuj punkty aktywne"
 
diff --git a/po/pt_BR.po b/po/pt_BR.po
index 2bb78d67ad6..aec48ccc5bb 100644
--- a/po/pt_BR.po
+++ b/po/pt_BR.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-13 13:52+0000\n"
+"POT-Creation-Date: 2026-06-14 18:41+0000\n"
 "PO-Revision-Date: 2024-11-16 02:13+0000\n"
 "Last-Translator: Marcel Souza Lemes <marcosoutsider at gmail.com>\n"
 "Language-Team: Portuguese (Brazil) <https://translations.scummvm.org/"
@@ -76,7 +76,7 @@ msgstr "Engines dispon??veis:"
 #: engines/scumm/help.cpp:141 engines/scumm/help.cpp:166
 #: engines/scumm/help.cpp:194 engines/scumm/help.cpp:212
 #: engines/sherlock/metaengine.cpp:430 engines/supernova/metaengine.cpp:341
-#: engines/tot/metaengine.cpp:149 engines/twp/metaengine.cpp:179
+#: engines/tot/metaengine.cpp:149 engines/twp/metaengine.cpp:178
 msgid "Close"
 msgstr "Fechar"
 
@@ -1061,7 +1061,7 @@ msgstr "Interagir"
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
 #: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1124
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
-#: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
+#: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:153
 #: engines/ultima/ultima0/metaengine.cpp:45
 #: engines/ultima/ultima4/metaengine.cpp:44
 #: engines/ultima/ultima4/metaengine.cpp:139
@@ -1082,7 +1082,7 @@ msgstr "Cima"
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
 #: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1091
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
-#: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
+#: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:45
 #: engines/ultima/ultima4/metaengine.cpp:140
@@ -1103,7 +1103,7 @@ msgstr "Baixo"
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
 #: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1104
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
-#: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
+#: engines/twp/metaengine.cpp:151 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
 #: engines/ultima/ultima4/metaengine.cpp:151
 #: engines/wintermute/keymapper_tables.h:320
@@ -1123,7 +1123,7 @@ msgstr "Esquerda"
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
 #: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1111
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
-#: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
+#: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
 #: engines/ultima/ultima4/metaengine.cpp:152
 #: engines/wintermute/keymapper_tables.h:326
@@ -3420,7 +3420,7 @@ msgstr "<Valor inv??lido>"
 
 #. I18N: Abbreviation for 'bytes' as data size
 #. I18N: Hardware key
-#: common/util.cpp:183 backends/platform/3ds/osystem-events.cpp:46
+#: common/util.cpp:190 backends/platform/3ds/osystem-events.cpp:46
 #: backends/platform/ds/osystem_ds.cpp:178
 #: backends/platform/sdl/miyoo/miyoo.cpp:61
 #: backends/platform/sdl/miyoo/miyoo.cpp:88
@@ -3430,22 +3430,22 @@ msgid "B"
 msgstr "B"
 
 #. I18N: Abbreviation for 'kilobytes' as data size
-#: common/util.cpp:189
+#: common/util.cpp:196
 msgid "KB"
 msgstr "KB"
 
 #. I18N: Abbreviation for 'megabytes' as data size
-#: common/util.cpp:194
+#: common/util.cpp:201
 msgid "MB"
 msgstr "MB"
 
 #. I18N: Abbreviation for 'gigabytes' as data size
-#: common/util.cpp:200
+#: common/util.cpp:207
 msgid "GB"
 msgstr "GB"
 
 #. I18N: Abbreviation for 'terabytes' as data size
-#: common/util.cpp:206
+#: common/util.cpp:213
 msgid "TB"
 msgstr "TB"
 
@@ -6708,7 +6708,7 @@ msgstr "Olhar"
 #: engines/scumm/help.cpp:165 engines/scumm/help.cpp:193
 #: engines/scumm/help.cpp:211 engines/sherlock/metaengine.cpp:425
 #: engines/supernova/metaengine.cpp:336 engines/tot/metaengine.cpp:144
-#: engines/twp/metaengine.cpp:178
+#: engines/twp/metaengine.cpp:177
 msgid "Open"
 msgstr "Abrir"
 
@@ -6730,7 +6730,7 @@ msgstr "Pegar"
 #: engines/sherlock/metaengine.cpp:440 engines/sherlock/metaengine.cpp:561
 #: engines/sky/metaengine.cpp:95 engines/sludge/keymapper_tables.h:333
 #: engines/supernova/metaengine.cpp:356 engines/tot/metaengine.cpp:139
-#: engines/tsage/metaengine.cpp:211 engines/twp/metaengine.cpp:186
+#: engines/tsage/metaengine.cpp:211 engines/twp/metaengine.cpp:185
 #: engines/ultima/ultima4/metaengine.cpp:74
 #: engines/ultima/nuvie/metaengine.cpp:69
 #: engines/wintermute/keymapper_tables.h:140
@@ -7528,7 +7528,7 @@ msgstr "Ativar"
 
 #: engines/alcachofa/metaengine.cpp:104 engines/queen/metaengine.cpp:279
 #: engines/saga/metaengine.cpp:354 engines/scumm/help.cpp:202
-#: engines/scumm/help.cpp:214 engines/twp/metaengine.cpp:182
+#: engines/scumm/help.cpp:214 engines/twp/metaengine.cpp:181
 #: engines/wintermute/keymapper_tables.h:122
 #: engines/wintermute/keymapper_tables.h:1534
 msgid "Look at"
@@ -7899,7 +7899,7 @@ msgstr "Alternar Modo de Combate"
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:210
 #: engines/phoenixvr/metaengine.cpp:94 engines/scumm/help.cpp:79
 #: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
-#: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
+#: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:161
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
 msgstr "Pular cena"
@@ -8818,7 +8818,7 @@ msgstr ""
 #. I18N: Perform action on object where cursor points
 #: engines/darkseed/metaengine.cpp:42 engines/dragons/metaengine.cpp:135
 #: engines/phoenixvr/metaengine.cpp:75 engines/tinsel/metaengine.cpp:294
-#: engines/twp/metaengine.cpp:150
+#: engines/twp/metaengine.cpp:149
 msgid "Action"
 msgstr "A????o"
 
@@ -9348,7 +9348,7 @@ msgstr ""
 #: engines/scumm/help.cpp:139 engines/scumm/help.cpp:164
 #: engines/scumm/help.cpp:200 engines/scumm/help.cpp:210
 #: engines/sherlock/metaengine.cpp:445 engines/sherlock/metaengine.cpp:566
-#: engines/supernova/metaengine.cpp:366 engines/twp/metaengine.cpp:180
+#: engines/supernova/metaengine.cpp:366 engines/twp/metaengine.cpp:179
 msgid "Give"
 msgstr "Dar"
 
@@ -13025,7 +13025,7 @@ msgstr "Utiliza uma fonte personalizada mais f??cil de ler"
 msgid "Move / Skip / Use default action with inventory item"
 msgstr ""
 
-#: engines/queen/metaengine.cpp:198 engines/twp/metaengine.cpp:176
+#: engines/queen/metaengine.cpp:198 engines/twp/metaengine.cpp:175
 #, fuzzy
 #| msgid "Skip Text"
 msgid "Skip text"
@@ -13083,7 +13083,7 @@ msgstr "Exibir di??rio"
 #: engines/scumm/help.cpp:144 engines/scumm/help.cpp:169
 #: engines/scumm/help.cpp:198 engines/scumm/help.cpp:213
 #: engines/scumm/help.cpp:230 engines/sherlock/metaengine.cpp:420
-#: engines/sludge/keymapper_tables.h:318 engines/twp/metaengine.cpp:181
+#: engines/sludge/keymapper_tables.h:318 engines/twp/metaengine.cpp:180
 #: engines/wintermute/keymapper_tables.h:134
 #: engines/wintermute/keymapper_tables.h:1546
 msgid "Pick up"
@@ -13092,7 +13092,7 @@ msgstr "Pegar"
 #: engines/queen/metaengine.cpp:289 engines/saga/metaengine.cpp:364
 #: engines/scumm/help.cpp:199 engines/scumm/help.cpp:215
 #: engines/scumm/help.cpp:251 engines/sludge/keymapper_tables.h:323
-#: engines/twp/metaengine.cpp:183 engines/wintermute/keymapper_tables.h:128
+#: engines/twp/metaengine.cpp:182 engines/wintermute/keymapper_tables.h:128
 #: engines/wintermute/keymapper_tables.h:1558
 msgid "Talk to"
 msgstr "Falar"
@@ -13201,7 +13201,7 @@ msgstr "Andar at??"
 
 #: engines/saga/metaengine.cpp:389 engines/scumm/help.cpp:122
 #: engines/scumm/help.cpp:137 engines/scumm/help.cpp:162
-#: engines/supernova/metaengine.cpp:346 engines/twp/metaengine.cpp:184
+#: engines/supernova/metaengine.cpp:346 engines/twp/metaengine.cpp:183
 msgid "Push"
 msgstr "Empurar"
 
@@ -14060,7 +14060,7 @@ msgstr "Controles principais do jogo:"
 
 #: engines/scumm/help.cpp:123 engines/scumm/help.cpp:138
 #: engines/scumm/help.cpp:163 engines/supernova/metaengine.cpp:351
-#: engines/twp/metaengine.cpp:185
+#: engines/twp/metaengine.cpp:184
 msgid "Pull"
 msgstr "Puxar"
 
@@ -16588,121 +16588,121 @@ msgstr "Senten??a Cl??ssica"
 msgid "Ransome *unbeeped* (DLC)"
 msgstr "Ransome *sem censura* (DLC)"
 
-#: engines/twp/metaengine.cpp:151
+#: engines/twp/metaengine.cpp:150
 msgid "Select highlighted verb"
 msgstr "Selecionar verbo em destaque"
 
-#: engines/twp/metaengine.cpp:156
+#: engines/twp/metaengine.cpp:155
 msgid "Go to previous object"
 msgstr "Voltar para o objeto anterior"
 
-#: engines/twp/metaengine.cpp:157
+#: engines/twp/metaengine.cpp:156
 msgid "Go to next object"
 msgstr "Ir para pr??ximo objeto"
 
-#: engines/twp/metaengine.cpp:158
+#: engines/twp/metaengine.cpp:157
 #, fuzzy
 #| msgid "Select verb/item Left"
 msgid "Select verb / item left"
 msgstr "Selecionar verbo/item a Esquerda"
 
-#: engines/twp/metaengine.cpp:159
+#: engines/twp/metaengine.cpp:158
 #, fuzzy
 #| msgid "Select verb/item Right"
 msgid "Select verb / item right"
 msgstr "Selecionar verbo/item a Direita"
 
-#: engines/twp/metaengine.cpp:160
+#: engines/twp/metaengine.cpp:159
 #, fuzzy
 #| msgid "Select verb/item Up"
 msgid "Select verb / item up"
 msgstr "Selecionar verbo/item de Cima"
 
-#: engines/twp/metaengine.cpp:161
+#: engines/twp/metaengine.cpp:160
 #, fuzzy
 #| msgid "Select verb/item Down"
 msgid "Select verb / item down"
 msgstr "Selecionar verbo/item de Baixo"
 
-#: engines/twp/metaengine.cpp:163
+#: engines/twp/metaengine.cpp:162
 #, fuzzy
 #| msgid "Select Actor 1"
 msgid "Select actor 1"
 msgstr "Selecionar Ator 1"
 
-#: engines/twp/metaengine.cpp:164
+#: engines/twp/metaengine.cpp:163
 #, fuzzy
 #| msgid "Select Actor 2"
 msgid "Select actor 2"
 msgstr "Selecionar Ator 2"
 
-#: engines/twp/metaengine.cpp:165
+#: engines/twp/metaengine.cpp:164
 #, fuzzy
 #| msgid "Select Actor 3"
 msgid "Select actor 3"
 msgstr "Selecionar Ator 3"
 
-#: engines/twp/metaengine.cpp:166
+#: engines/twp/metaengine.cpp:165
 #, fuzzy
 #| msgid "Select Actor 4"
 msgid "Select actor 4"
 msgstr "Selecionar Ator 4"
 
-#: engines/twp/metaengine.cpp:167
+#: engines/twp/metaengine.cpp:166
 #, fuzzy
 #| msgid "Select Actor 5"
 msgid "Select actor 5"
 msgstr "Selecionar Ator 5"
 
-#: engines/twp/metaengine.cpp:168
+#: engines/twp/metaengine.cpp:167
 #, fuzzy
 #| msgid "Select Choice 1"
 msgid "Select choice 1"
 msgstr "Selecionar Escolha 1"
 
-#: engines/twp/metaengine.cpp:169
+#: engines/twp/metaengine.cpp:168
 #, fuzzy
 #| msgid "Select Choice 2"
 msgid "Select choice 2"
 msgstr "Selecionar Escolha 2"
 
-#: engines/twp/metaengine.cpp:170
+#: engines/twp/metaengine.cpp:169
 #, fuzzy
 #| msgid "Select Choice 3"
 msgid "Select choice 3"
 msgstr "Selecionar Escolha 3"
 
-#: engines/twp/metaengine.cpp:171
+#: engines/twp/metaengine.cpp:170
 #, fuzzy
 #| msgid "Select Choice 4"
 msgid "Select choice 4"
 msgstr "Selecionar Escolha 4"
 
-#: engines/twp/metaengine.cpp:172
+#: engines/twp/metaengine.cpp:171
 #, fuzzy
 #| msgid "Select Choice 5"
 msgid "Select choice 5"
 msgstr "Selecionar Escolha 5"
 
-#: engines/twp/metaengine.cpp:173
+#: engines/twp/metaengine.cpp:172
 #, fuzzy
 #| msgid "Select Choice 6"
 msgid "Select choice 6"
 msgstr "Selecionar Escolha 6"
 
-#: engines/twp/metaengine.cpp:174
+#: engines/twp/metaengine.cpp:173
 #, fuzzy
 #| msgid "Select Next Actor"
 msgid "Select next Actor"
 msgstr "Selecionar Pr??ximo Ator"
 
-#: engines/twp/metaengine.cpp:175
+#: engines/twp/metaengine.cpp:174
 #, fuzzy
 #| msgid "Select Previous Actor"
 msgid "Select previous actor"
 msgstr "Selecionar Ator Anterior"
 
-#: engines/twp/metaengine.cpp:177
+#: engines/twp/metaengine.cpp:176
 msgid "Show hotspots"
 msgstr "Exibir pontos de acesso"
 
diff --git a/po/pt_PT.po b/po/pt_PT.po
index 26ef1ffd3e1..67a746c6d3b 100644
--- a/po/pt_PT.po
+++ b/po/pt_PT.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.10.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-13 13:52+0000\n"
+"POT-Creation-Date: 2026-06-14 18:41+0000\n"
 "PO-Revision-Date: 2022-06-05 14:09+0000\n"
 "Last-Translator: Daniel <supsuper at gmail.com>\n"
 "Language-Team: Portuguese (Portugal) <https://translations.scummvm.org/"
@@ -79,7 +79,7 @@ msgstr "Motores de jogo dispon??veis:"
 #: engines/scumm/help.cpp:141 engines/scumm/help.cpp:166
 #: engines/scumm/help.cpp:194 engines/scumm/help.cpp:212
 #: engines/sherlock/metaengine.cpp:430 engines/supernova/metaengine.cpp:341
-#: engines/tot/metaengine.cpp:149 engines/twp/metaengine.cpp:179
+#: engines/tot/metaengine.cpp:149 engines/twp/metaengine.cpp:178
 msgid "Close"
 msgstr "Fechar"
 
@@ -1074,7 +1074,7 @@ msgstr "Interagir"
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
 #: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1124
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
-#: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
+#: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:153
 #: engines/ultima/ultima0/metaengine.cpp:45
 #: engines/ultima/ultima4/metaengine.cpp:44
 #: engines/ultima/ultima4/metaengine.cpp:139
@@ -1095,7 +1095,7 @@ msgstr "Cima"
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
 #: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1091
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
-#: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
+#: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:45
 #: engines/ultima/ultima4/metaengine.cpp:140
@@ -1116,7 +1116,7 @@ msgstr "Baixo"
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
 #: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1104
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
-#: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
+#: engines/twp/metaengine.cpp:151 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
 #: engines/ultima/ultima4/metaengine.cpp:151
 #: engines/wintermute/keymapper_tables.h:320
@@ -1136,7 +1136,7 @@ msgstr "Esquerda"
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
 #: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1111
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
-#: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
+#: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
 #: engines/ultima/ultima4/metaengine.cpp:152
 #: engines/wintermute/keymapper_tables.h:326
@@ -3304,7 +3304,7 @@ msgstr "<Valor incorreto>"
 
 #. I18N: Abbreviation for 'bytes' as data size
 #. I18N: Hardware key
-#: common/util.cpp:183 backends/platform/3ds/osystem-events.cpp:46
+#: common/util.cpp:190 backends/platform/3ds/osystem-events.cpp:46
 #: backends/platform/ds/osystem_ds.cpp:178
 #: backends/platform/sdl/miyoo/miyoo.cpp:61
 #: backends/platform/sdl/miyoo/miyoo.cpp:88
@@ -3314,22 +3314,22 @@ msgid "B"
 msgstr "B"
 
 #. I18N: Abbreviation for 'kilobytes' as data size
-#: common/util.cpp:189
+#: common/util.cpp:196
 msgid "KB"
 msgstr "KB"
 
 #. I18N: Abbreviation for 'megabytes' as data size
-#: common/util.cpp:194
+#: common/util.cpp:201
 msgid "MB"
 msgstr "MB"
 
 #. I18N: Abbreviation for 'gigabytes' as data size
-#: common/util.cpp:200
+#: common/util.cpp:207
 msgid "GB"
 msgstr "GB"
 
 #. I18N: Abbreviation for 'terabytes' as data size
-#: common/util.cpp:206
+#: common/util.cpp:213
 msgid "TB"
 msgstr "TB"
 
@@ -6176,7 +6176,7 @@ msgstr "Olhar para Cima"
 #: engines/scumm/help.cpp:165 engines/scumm/help.cpp:193
 #: engines/scumm/help.cpp:211 engines/sherlock/metaengine.cpp:425
 #: engines/supernova/metaengine.cpp:336 engines/tot/metaengine.cpp:144
-#: engines/twp/metaengine.cpp:178
+#: engines/twp/metaengine.cpp:177
 #, fuzzy
 msgid "Open"
 msgstr "OpenGL"
@@ -6199,7 +6199,7 @@ msgstr ""
 #: engines/sherlock/metaengine.cpp:440 engines/sherlock/metaengine.cpp:561
 #: engines/sky/metaengine.cpp:95 engines/sludge/keymapper_tables.h:333
 #: engines/supernova/metaengine.cpp:356 engines/tot/metaengine.cpp:139
-#: engines/tsage/metaengine.cpp:211 engines/twp/metaengine.cpp:186
+#: engines/tsage/metaengine.cpp:211 engines/twp/metaengine.cpp:185
 #: engines/ultima/ultima4/metaengine.cpp:74
 #: engines/ultima/nuvie/metaengine.cpp:69
 #: engines/wintermute/keymapper_tables.h:140
@@ -6965,7 +6965,7 @@ msgstr "Activo"
 
 #: engines/alcachofa/metaengine.cpp:104 engines/queen/metaengine.cpp:279
 #: engines/saga/metaengine.cpp:354 engines/scumm/help.cpp:202
-#: engines/scumm/help.cpp:214 engines/twp/metaengine.cpp:182
+#: engines/scumm/help.cpp:214 engines/twp/metaengine.cpp:181
 #: engines/wintermute/keymapper_tables.h:122
 #: engines/wintermute/keymapper_tables.h:1534
 #, fuzzy
@@ -7334,7 +7334,7 @@ msgstr "Ativar/desativar combate"
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:210
 #: engines/phoenixvr/metaengine.cpp:94 engines/scumm/help.cpp:79
 #: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
-#: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
+#: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:161
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
 msgstr "Ignorar filme"
@@ -8244,7 +8244,7 @@ msgstr ""
 #. I18N: Perform action on object where cursor points
 #: engines/darkseed/metaengine.cpp:42 engines/dragons/metaengine.cpp:135
 #: engines/phoenixvr/metaengine.cpp:75 engines/tinsel/metaengine.cpp:294
-#: engines/twp/metaengine.cpp:150
+#: engines/twp/metaengine.cpp:149
 msgid "Action"
 msgstr "A????o"
 
@@ -8769,7 +8769,7 @@ msgstr ""
 #: engines/scumm/help.cpp:139 engines/scumm/help.cpp:164
 #: engines/scumm/help.cpp:200 engines/scumm/help.cpp:210
 #: engines/sherlock/metaengine.cpp:445 engines/sherlock/metaengine.cpp:566
-#: engines/supernova/metaengine.cpp:366 engines/twp/metaengine.cpp:180
+#: engines/supernova/metaengine.cpp:366 engines/twp/metaengine.cpp:179
 msgid "Give"
 msgstr ""
 
@@ -12349,7 +12349,7 @@ msgstr ""
 msgid "Move / Skip / Use default action with inventory item"
 msgstr ""
 
-#: engines/queen/metaengine.cpp:198 engines/twp/metaengine.cpp:176
+#: engines/queen/metaengine.cpp:198 engines/twp/metaengine.cpp:175
 #, fuzzy
 #| msgid "Skip text"
 msgid "Skip text"
@@ -12402,7 +12402,7 @@ msgstr "Mostrar di??rio"
 #: engines/scumm/help.cpp:144 engines/scumm/help.cpp:169
 #: engines/scumm/help.cpp:198 engines/scumm/help.cpp:213
 #: engines/scumm/help.cpp:230 engines/sherlock/metaengine.cpp:420
-#: engines/sludge/keymapper_tables.h:318 engines/twp/metaengine.cpp:181
+#: engines/sludge/keymapper_tables.h:318 engines/twp/metaengine.cpp:180
 #: engines/wintermute/keymapper_tables.h:134
 #: engines/wintermute/keymapper_tables.h:1546
 msgid "Pick up"
@@ -12411,7 +12411,7 @@ msgstr ""
 #: engines/queen/metaengine.cpp:289 engines/saga/metaengine.cpp:364
 #: engines/scumm/help.cpp:199 engines/scumm/help.cpp:215
 #: engines/scumm/help.cpp:251 engines/sludge/keymapper_tables.h:323
-#: engines/twp/metaengine.cpp:183 engines/wintermute/keymapper_tables.h:128
+#: engines/twp/metaengine.cpp:182 engines/wintermute/keymapper_tables.h:128
 #: engines/wintermute/keymapper_tables.h:1558
 msgid "Talk to"
 msgstr ""
@@ -12529,7 +12529,7 @@ msgstr "Avan??ar"
 
 #: engines/saga/metaengine.cpp:389 engines/scumm/help.cpp:122
 #: engines/scumm/help.cpp:137 engines/scumm/help.cpp:162
-#: engines/supernova/metaengine.cpp:346 engines/twp/metaengine.cpp:184
+#: engines/supernova/metaengine.cpp:346 engines/twp/metaengine.cpp:183
 msgid "Push"
 msgstr ""
 
@@ -13325,7 +13325,7 @@ msgstr "Alterar op????es do jogo"
 
 #: engines/scumm/help.cpp:123 engines/scumm/help.cpp:138
 #: engines/scumm/help.cpp:163 engines/supernova/metaengine.cpp:351
-#: engines/twp/metaengine.cpp:185
+#: engines/twp/metaengine.cpp:184
 msgid "Pull"
 msgstr ""
 
@@ -15791,117 +15791,117 @@ msgstr ""
 msgid "Ransome *unbeeped* (DLC)"
 msgstr ""
 
-#: engines/twp/metaengine.cpp:151
+#: engines/twp/metaengine.cpp:150
 #, fuzzy
 msgid "Select highlighted verb"
 msgstr "Selecione um tema"
 
-#: engines/twp/metaengine.cpp:156
+#: engines/twp/metaengine.cpp:155
 #, fuzzy
 #| msgid "Go to previous directory level"
 msgid "Go to previous object"
 msgstr "Ir para a pasta acima"
 
-#: engines/twp/metaengine.cpp:157
+#: engines/twp/metaengine.cpp:156
 #, fuzzy
 msgid "Go to next object"
 msgstr "Menu de Op????es da DS"
 
-#: engines/twp/metaengine.cpp:158
+#: engines/twp/metaengine.cpp:157
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Select verb / item left"
 msgstr "Deslizar ?? direita"
 
-#: engines/twp/metaengine.cpp:159
+#: engines/twp/metaengine.cpp:158
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Select verb / item right"
 msgstr "Deslizar ?? direita"
 
-#: engines/twp/metaengine.cpp:160
+#: engines/twp/metaengine.cpp:159
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Select verb / item up"
 msgstr "Deslizar ?? direita"
 
-#: engines/twp/metaengine.cpp:161
+#: engines/twp/metaengine.cpp:160
 #, fuzzy
 #| msgid "Slide Right"
 msgid "Select verb / item down"
 msgstr "Deslizar ?? direita"
 
-#: engines/twp/metaengine.cpp:163
+#: engines/twp/metaengine.cpp:162
 #, fuzzy
 #| msgid "Select Group by"
 msgid "Select actor 1"
 msgstr "Agrupar por"
 
-#: engines/twp/metaengine.cpp:164
+#: engines/twp/metaengine.cpp:163
 #, fuzzy
 #| msgid "Select Group by"
 msgid "Select actor 2"
 msgstr "Agrupar por"
 
-#: engines/twp/metaengine.cpp:165
+#: engines/twp/metaengine.cpp:164
 #, fuzzy
 #| msgid "Select Group by"
 msgid "Select actor 3"
 msgstr "Agrupar por"
 
-#: engines/twp/metaengine.cpp:166
+#: engines/twp/metaengine.cpp:165
 #, fuzzy
 #| msgid "Select Group by"
 msgid "Select actor 4"
 msgstr "Agrupar por"
 
-#: engines/twp/metaengine.cpp:167
+#: engines/twp/metaengine.cpp:166
 #, fuzzy
 #| msgid "Select Group by"
 msgid "Select actor 5"
 msgstr "Agrupar por"
 
-#: engines/twp/metaengine.cpp:168
+#: engines/twp/metaengine.cpp:167
 #, fuzzy
 msgid "Select choice 1"
 msgstr "Selecione um tema"
 
-#: engines/twp/metaengine.cpp:169
+#: engines/twp/metaengine.cpp:168
 #, fuzzy
 msgid "Select choice 2"
 msgstr "Selecione um tema"
 
-#: engines/twp/metaengine.cpp:170
+#: engines/twp/metaengine.cpp:169
 #, fuzzy
 msgid "Select choice 3"
 msgstr "Selecione um tema"
 
-#: engines/twp/metaengine.cpp:171
+#: engines/twp/metaengine.cpp:170
 #, fuzzy
 msgid "Select choice 4"
 msgstr "Selecione um tema"
 
-#: engines/twp/metaengine.cpp:172
+#: engines/twp/metaengine.cpp:171
 #, fuzzy
 msgid "Select choice 5"
 msgstr "Selecione um tema"
 
-#: engines/twp/metaengine.cpp:173
+#: engines/twp/metaengine.cpp:172
 #, fuzzy
 msgid "Select choice 6"
 msgstr "Selecione um tema"
 
-#: engines/twp/metaengine.cpp:174
+#: engines/twp/metaengine.cpp:173
 #, fuzzy
 msgid "Select next Actor"
 msgstr "Selecione um tema"
 
-#: engines/twp/metaengine.cpp:175
+#: engines/twp/metaengine.cpp:174
 #, fuzzy
 msgid "Select previous actor"
 msgstr "A????o seguinte"
 
-#: engines/twp/metaengine.cpp:177
+#: engines/twp/metaengine.cpp:176
 #, fuzzy
 #| msgid "Show hints"
 msgid "Show hotspots"
diff --git a/po/ro.po b/po/ro.po
index 8d8bf3367fa..9e516a8147e 100644
--- a/po/ro.po
+++ b/po/ro.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.10.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-13 13:52+0000\n"
+"POT-Creation-Date: 2026-06-14 18:41+0000\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: Automatically generated\n"
 "Language-Team: none\n"
@@ -68,7 +68,7 @@ msgstr ""
 #: engines/scumm/help.cpp:141 engines/scumm/help.cpp:166
 #: engines/scumm/help.cpp:194 engines/scumm/help.cpp:212
 #: engines/sherlock/metaengine.cpp:430 engines/supernova/metaengine.cpp:341
-#: engines/tot/metaengine.cpp:149 engines/twp/metaengine.cpp:179
+#: engines/tot/metaengine.cpp:149 engines/twp/metaengine.cpp:178
 msgid "Close"
 msgstr ""
 
@@ -1015,7 +1015,7 @@ msgstr ""
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
 #: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1124
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
-#: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
+#: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:153
 #: engines/ultima/ultima0/metaengine.cpp:45
 #: engines/ultima/ultima4/metaengine.cpp:44
 #: engines/ultima/ultima4/metaengine.cpp:139
@@ -1036,7 +1036,7 @@ msgstr ""
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
 #: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1091
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
-#: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
+#: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:45
 #: engines/ultima/ultima4/metaengine.cpp:140
@@ -1057,7 +1057,7 @@ msgstr ""
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
 #: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1104
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
-#: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
+#: engines/twp/metaengine.cpp:151 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
 #: engines/ultima/ultima4/metaengine.cpp:151
 #: engines/wintermute/keymapper_tables.h:320
@@ -1077,7 +1077,7 @@ msgstr ""
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
 #: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1111
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
-#: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
+#: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
 #: engines/ultima/ultima4/metaengine.cpp:152
 #: engines/wintermute/keymapper_tables.h:326
@@ -3098,7 +3098,7 @@ msgstr ""
 
 #. I18N: Abbreviation for 'bytes' as data size
 #. I18N: Hardware key
-#: common/util.cpp:183 backends/platform/3ds/osystem-events.cpp:46
+#: common/util.cpp:190 backends/platform/3ds/osystem-events.cpp:46
 #: backends/platform/ds/osystem_ds.cpp:178
 #: backends/platform/sdl/miyoo/miyoo.cpp:61
 #: backends/platform/sdl/miyoo/miyoo.cpp:88
@@ -3108,22 +3108,22 @@ msgid "B"
 msgstr ""
 
 #. I18N: Abbreviation for 'kilobytes' as data size
-#: common/util.cpp:189
+#: common/util.cpp:196
 msgid "KB"
 msgstr ""
 
 #. I18N: Abbreviation for 'megabytes' as data size
-#: common/util.cpp:194
+#: common/util.cpp:201
 msgid "MB"
 msgstr ""
 
 #. I18N: Abbreviation for 'gigabytes' as data size
-#: common/util.cpp:200
+#: common/util.cpp:207
 msgid "GB"
 msgstr ""
 
 #. I18N: Abbreviation for 'terabytes' as data size
-#: common/util.cpp:206
+#: common/util.cpp:213
 msgid "TB"
 msgstr ""
 
@@ -5754,7 +5754,7 @@ msgstr ""
 #: engines/scumm/help.cpp:165 engines/scumm/help.cpp:193
 #: engines/scumm/help.cpp:211 engines/sherlock/metaengine.cpp:425
 #: engines/supernova/metaengine.cpp:336 engines/tot/metaengine.cpp:144
-#: engines/twp/metaengine.cpp:178
+#: engines/twp/metaengine.cpp:177
 msgid "Open"
 msgstr ""
 
@@ -5776,7 +5776,7 @@ msgstr ""
 #: engines/sherlock/metaengine.cpp:440 engines/sherlock/metaengine.cpp:561
 #: engines/sky/metaengine.cpp:95 engines/sludge/keymapper_tables.h:333
 #: engines/supernova/metaengine.cpp:356 engines/tot/metaengine.cpp:139
-#: engines/tsage/metaengine.cpp:211 engines/twp/metaengine.cpp:186
+#: engines/tsage/metaengine.cpp:211 engines/twp/metaengine.cpp:185
 #: engines/ultima/ultima4/metaengine.cpp:74
 #: engines/ultima/nuvie/metaengine.cpp:69
 #: engines/wintermute/keymapper_tables.h:140
@@ -6476,7 +6476,7 @@ msgstr ""
 
 #: engines/alcachofa/metaengine.cpp:104 engines/queen/metaengine.cpp:279
 #: engines/saga/metaengine.cpp:354 engines/scumm/help.cpp:202
-#: engines/scumm/help.cpp:214 engines/twp/metaengine.cpp:182
+#: engines/scumm/help.cpp:214 engines/twp/metaengine.cpp:181
 #: engines/wintermute/keymapper_tables.h:122
 #: engines/wintermute/keymapper_tables.h:1534
 msgid "Look at"
@@ -6805,7 +6805,7 @@ msgstr ""
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:210
 #: engines/phoenixvr/metaengine.cpp:94 engines/scumm/help.cpp:79
 #: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
-#: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
+#: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:161
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
 msgstr ""
@@ -7601,7 +7601,7 @@ msgstr ""
 #. I18N: Perform action on object where cursor points
 #: engines/darkseed/metaengine.cpp:42 engines/dragons/metaengine.cpp:135
 #: engines/phoenixvr/metaengine.cpp:75 engines/tinsel/metaengine.cpp:294
-#: engines/twp/metaengine.cpp:150
+#: engines/twp/metaengine.cpp:149
 msgid "Action"
 msgstr ""
 
@@ -7996,7 +7996,7 @@ msgstr ""
 #: engines/scumm/help.cpp:139 engines/scumm/help.cpp:164
 #: engines/scumm/help.cpp:200 engines/scumm/help.cpp:210
 #: engines/sherlock/metaengine.cpp:445 engines/sherlock/metaengine.cpp:566
-#: engines/supernova/metaengine.cpp:366 engines/twp/metaengine.cpp:180
+#: engines/supernova/metaengine.cpp:366 engines/twp/metaengine.cpp:179
 msgid "Give"
 msgstr ""
 
@@ -11078,7 +11078,7 @@ msgstr ""
 msgid "Move / Skip / Use default action with inventory item"
 msgstr ""
 
-#: engines/queen/metaengine.cpp:198 engines/twp/metaengine.cpp:176
+#: engines/queen/metaengine.cpp:198 engines/twp/metaengine.cpp:175
 msgid "Skip text"
 msgstr ""
 
@@ -11118,7 +11118,7 @@ msgstr ""
 #: engines/scumm/help.cpp:144 engines/scumm/help.cpp:169
 #: engines/scumm/help.cpp:198 engines/scumm/help.cpp:213
 #: engines/scumm/help.cpp:230 engines/sherlock/metaengine.cpp:420
-#: engines/sludge/keymapper_tables.h:318 engines/twp/metaengine.cpp:181
+#: engines/sludge/keymapper_tables.h:318 engines/twp/metaengine.cpp:180
 #: engines/wintermute/keymapper_tables.h:134
 #: engines/wintermute/keymapper_tables.h:1546
 msgid "Pick up"
@@ -11127,7 +11127,7 @@ msgstr ""
 #: engines/queen/metaengine.cpp:289 engines/saga/metaengine.cpp:364
 #: engines/scumm/help.cpp:199 engines/scumm/help.cpp:215
 #: engines/scumm/help.cpp:251 engines/sludge/keymapper_tables.h:323
-#: engines/twp/metaengine.cpp:183 engines/wintermute/keymapper_tables.h:128
+#: engines/twp/metaengine.cpp:182 engines/wintermute/keymapper_tables.h:128
 #: engines/wintermute/keymapper_tables.h:1558
 msgid "Talk to"
 msgstr ""
@@ -11222,7 +11222,7 @@ msgstr ""
 
 #: engines/saga/metaengine.cpp:389 engines/scumm/help.cpp:122
 #: engines/scumm/help.cpp:137 engines/scumm/help.cpp:162
-#: engines/supernova/metaengine.cpp:346 engines/twp/metaengine.cpp:184
+#: engines/supernova/metaengine.cpp:346 engines/twp/metaengine.cpp:183
 msgid "Push"
 msgstr ""
 
@@ -11972,7 +11972,7 @@ msgstr ""
 
 #: engines/scumm/help.cpp:123 engines/scumm/help.cpp:138
 #: engines/scumm/help.cpp:163 engines/supernova/metaengine.cpp:351
-#: engines/twp/metaengine.cpp:185
+#: engines/twp/metaengine.cpp:184
 msgid "Pull"
 msgstr ""
 
@@ -14082,87 +14082,87 @@ msgstr ""
 msgid "Ransome *unbeeped* (DLC)"
 msgstr ""
 
-#: engines/twp/metaengine.cpp:151
+#: engines/twp/metaengine.cpp:150
 msgid "Select highlighted verb"
 msgstr ""
 
-#: engines/twp/metaengine.cpp:156
+#: engines/twp/metaengine.cpp:155
 msgid "Go to previous object"
 msgstr ""
 
-#: engines/twp/metaengine.cpp:157
+#: engines/twp/metaengine.cpp:156
 msgid "Go to next object"
 msgstr ""
 
-#: engines/twp/metaengine.cpp:158
+#: engines/twp/metaengine.cpp:157
 msgid "Select verb / item left"
 msgstr ""
 
-#: engines/twp/metaengine.cpp:159
+#: engines/twp/metaengine.cpp:158
 msgid "Select verb / item right"
 msgstr ""
 
-#: engines/twp/metaengine.cpp:160
+#: engines/twp/metaengine.cpp:159
 msgid "Select verb / item up"
 msgstr ""
 
-#: engines/twp/metaengine.cpp:161
+#: engines/twp/metaengine.cpp:160
 msgid "Select verb / item down"
 msgstr ""
 
-#: engines/twp/metaengine.cpp:163
+#: engines/twp/metaengine.cpp:162
 msgid "Select actor 1"
 msgstr ""
 
-#: engines/twp/metaengine.cpp:164
+#: engines/twp/metaengine.cpp:163
 msgid "Select actor 2"
 msgstr ""
 
-#: engines/twp/metaengine.cpp:165
+#: engines/twp/metaengine.cpp:164
 msgid "Select actor 3"
 msgstr ""
 
-#: engines/twp/metaengine.cpp:166
+#: engines/twp/metaengine.cpp:165
 msgid "Select actor 4"
 msgstr ""
 
-#: engines/twp/metaengine.cpp:167
+#: engines/twp/metaengine.cpp:166
 msgid "Select actor 5"
 msgstr ""
 
-#: engines/twp/metaengine.cpp:168
+#: engines/twp/metaengine.cpp:167
 msgid "Select choice 1"
 msgstr ""
 
-#: engines/twp/metaengine.cpp:169
+#: engines/twp/metaengine.cpp:168
 msgid "Select choice 2"
 msgstr ""
 
-#: engines/twp/metaengine.cpp:170
+#: engines/twp/metaengine.cpp:169
 msgid "Select choice 3"
 msgstr ""
 
-#: engines/twp/metaengine.cpp:171
+#: engines/twp/metaengine.cpp:170
 msgid "Select choice 4"
 msgstr ""
 
-#: engines/twp/metaengine.cpp:172
+#: engines/twp/metaengine.cpp:171
 msgid "Select choice 5"
 msgstr ""
 
-#: engines/twp/metaengine.cpp:173
+#: engines/twp/metaengine.cpp:172
 msgid "Select choice 6"
 msgstr ""
 
-#: engines/twp/metaengine.cpp:174
+#: engines/twp/metaengine.cpp:173
 msgid "Select next Actor"
 msgstr ""
 
-#: engines/twp/metaengine.cpp:175
+#: engines/twp/metaengine.cpp:174
 msgid "Select previous actor"
 msgstr ""
 
-#: engines/twp/metaengine.cpp:177
+#: engines/twp/metaengine.cpp:176
 msgid "Show hotspots"
 msgstr ""
 
diff --git a/po/ru_RU.po b/po/ru_RU.po
index ba36a7d1f50..3533884de1f 100644
--- a/po/ru_RU.po
+++ b/po/ru_RU.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.8.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-13 13:52+0000\n"
+"POT-Creation-Date: 2026-06-14 18:41+0000\n"
 "PO-Revision-Date: 2026-05-06 08:54+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Russian <https://translations.scummvm.org/projects/scummvm/"
@@ -72,7 +72,7 @@ msgstr "?????????????????? ????????????:"
 #: engines/scumm/help.cpp:141 engines/scumm/help.cpp:166
 #: engines/scumm/help.cpp:194 engines/scumm/help.cpp:212
 #: engines/sherlock/metaengine.cpp:430 engines/supernova/metaengine.cpp:341
-#: engines/tot/metaengine.cpp:149 engines/twp/metaengine.cpp:179
+#: engines/tot/metaengine.cpp:149 engines/twp/metaengine.cpp:178
 msgid "Close"
 msgstr "??????????????"
 
@@ -1053,7 +1053,7 @@ msgstr "????????????????????????????"
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
 #: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1124
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
-#: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
+#: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:153
 #: engines/ultima/ultima0/metaengine.cpp:45
 #: engines/ultima/ultima4/metaengine.cpp:44
 #: engines/ultima/ultima4/metaengine.cpp:139
@@ -1074,7 +1074,7 @@ msgstr "??????????"
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
 #: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1091
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
-#: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
+#: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:45
 #: engines/ultima/ultima4/metaengine.cpp:140
@@ -1095,7 +1095,7 @@ msgstr "????????"
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
 #: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1104
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
-#: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
+#: engines/twp/metaengine.cpp:151 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
 #: engines/ultima/ultima4/metaengine.cpp:151
 #: engines/wintermute/keymapper_tables.h:320
@@ -1115,7 +1115,7 @@ msgstr "??????????"
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
 #: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1111
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
-#: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
+#: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
 #: engines/ultima/ultima4/metaengine.cpp:152
 #: engines/wintermute/keymapper_tables.h:326
@@ -3345,7 +3345,7 @@ msgstr "<???????????????????????? ????????????????>"
 
 #. I18N: Abbreviation for 'bytes' as data size
 #. I18N: Hardware key
-#: common/util.cpp:183 backends/platform/3ds/osystem-events.cpp:46
+#: common/util.cpp:190 backends/platform/3ds/osystem-events.cpp:46
 #: backends/platform/ds/osystem_ds.cpp:178
 #: backends/platform/sdl/miyoo/miyoo.cpp:61
 #: backends/platform/sdl/miyoo/miyoo.cpp:88
@@ -3355,22 +3355,22 @@ msgid "B"
 msgstr "B"
 
 #. I18N: Abbreviation for 'kilobytes' as data size
-#: common/util.cpp:189
+#: common/util.cpp:196
 msgid "KB"
 msgstr "????"
 
 #. I18N: Abbreviation for 'megabytes' as data size
-#: common/util.cpp:194
+#: common/util.cpp:201
 msgid "MB"
 msgstr "????"
 
 #. I18N: Abbreviation for 'gigabytes' as data size
-#: common/util.cpp:200
+#: common/util.cpp:207
 msgid "GB"
 msgstr "????"
 
 #. I18N: Abbreviation for 'terabytes' as data size
-#: common/util.cpp:206
+#: common/util.cpp:213
 msgid "TB"
 msgstr "????"
 
@@ -6437,7 +6437,7 @@ msgstr "????????????????"
 #: engines/scumm/help.cpp:165 engines/scumm/help.cpp:193
 #: engines/scumm/help.cpp:211 engines/sherlock/metaengine.cpp:425
 #: engines/supernova/metaengine.cpp:336 engines/tot/metaengine.cpp:144
-#: engines/twp/metaengine.cpp:178
+#: engines/twp/metaengine.cpp:177
 msgid "Open"
 msgstr "??????????????"
 
@@ -6459,7 +6459,7 @@ msgstr "??????????"
 #: engines/sherlock/metaengine.cpp:440 engines/sherlock/metaengine.cpp:561
 #: engines/sky/metaengine.cpp:95 engines/sludge/keymapper_tables.h:333
 #: engines/supernova/metaengine.cpp:356 engines/tot/metaengine.cpp:139
-#: engines/tsage/metaengine.cpp:211 engines/twp/metaengine.cpp:186
+#: engines/tsage/metaengine.cpp:211 engines/twp/metaengine.cpp:185
 #: engines/ultima/ultima4/metaengine.cpp:74
 #: engines/ultima/nuvie/metaengine.cpp:69
 #: engines/wintermute/keymapper_tables.h:140
@@ -7230,7 +7230,7 @@ msgstr "????????????????????????"
 
 #: engines/alcachofa/metaengine.cpp:104 engines/queen/metaengine.cpp:279
 #: engines/saga/metaengine.cpp:354 engines/scumm/help.cpp:202
-#: engines/scumm/help.cpp:214 engines/twp/metaengine.cpp:182
+#: engines/scumm/help.cpp:214 engines/twp/metaengine.cpp:181
 #: engines/wintermute/keymapper_tables.h:122
 #: engines/wintermute/keymapper_tables.h:1534
 msgid "Look at"
@@ -7584,7 +7584,7 @@ msgstr "?????????????????????? ??????"
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:210
 #: engines/phoenixvr/metaengine.cpp:94 engines/scumm/help.cpp:79
 #: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
-#: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
+#: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:161
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
 msgstr "???????????????????? ????????????????"
@@ -8402,7 +8402,7 @@ msgstr "???????????????? ?????????????? ???????????? ?? ??????????????"
 #. I18N: Perform action on object where cursor points
 #: engines/darkseed/metaengine.cpp:42 engines/dragons/metaengine.cpp:135
 #: engines/phoenixvr/metaengine.cpp:75 engines/tinsel/metaengine.cpp:294
-#: engines/twp/metaengine.cpp:150
+#: engines/twp/metaengine.cpp:149
 msgid "Action"
 msgstr "????????????????"
 
@@ -8819,7 +8819,7 @@ msgstr "????????????"
 #: engines/scumm/help.cpp:139 engines/scumm/help.cpp:164
 #: engines/scumm/help.cpp:200 engines/scumm/help.cpp:210
 #: engines/sherlock/metaengine.cpp:445 engines/sherlock/metaengine.cpp:566
-#: engines/supernova/metaengine.cpp:366 engines/twp/metaengine.cpp:180
+#: engines/supernova/metaengine.cpp:366 engines/twp/metaengine.cpp:179
 msgid "Give"
 msgstr "????????"
 
@@ -12135,7 +12135,7 @@ msgstr ""
 "?????????????????? / ???????????????????? / ???????????????????????? ???????????????? ???? ?????????????????? ?????? ???????????????? "
 "??????????????????"
 
-#: engines/queen/metaengine.cpp:198 engines/twp/metaengine.cpp:176
+#: engines/queen/metaengine.cpp:198 engines/twp/metaengine.cpp:175
 msgid "Skip text"
 msgstr "???????????????????? ??????????"
 
@@ -12175,7 +12175,7 @@ msgstr "???????????????????????? ????????????"
 #: engines/scumm/help.cpp:144 engines/scumm/help.cpp:169
 #: engines/scumm/help.cpp:198 engines/scumm/help.cpp:213
 #: engines/scumm/help.cpp:230 engines/sherlock/metaengine.cpp:420
-#: engines/sludge/keymapper_tables.h:318 engines/twp/metaengine.cpp:181
+#: engines/sludge/keymapper_tables.h:318 engines/twp/metaengine.cpp:180
 #: engines/wintermute/keymapper_tables.h:134
 #: engines/wintermute/keymapper_tables.h:1546
 msgid "Pick up"
@@ -12184,7 +12184,7 @@ msgstr "??????????????"
 #: engines/queen/metaengine.cpp:289 engines/saga/metaengine.cpp:364
 #: engines/scumm/help.cpp:199 engines/scumm/help.cpp:215
 #: engines/scumm/help.cpp:251 engines/sludge/keymapper_tables.h:323
-#: engines/twp/metaengine.cpp:183 engines/wintermute/keymapper_tables.h:128
+#: engines/twp/metaengine.cpp:182 engines/wintermute/keymapper_tables.h:128
 #: engines/wintermute/keymapper_tables.h:1558
 msgid "Talk to"
 msgstr "???????????????? ??"
@@ -12279,7 +12279,7 @@ msgstr "???????? ??"
 
 #: engines/saga/metaengine.cpp:389 engines/scumm/help.cpp:122
 #: engines/scumm/help.cpp:137 engines/scumm/help.cpp:162
-#: engines/supernova/metaengine.cpp:346 engines/twp/metaengine.cpp:184
+#: engines/supernova/metaengine.cpp:346 engines/twp/metaengine.cpp:183
 msgid "Push"
 msgstr "??????????????"
 
@@ -13137,7 +13137,7 @@ msgstr "???????????????? ???????????????????? ??????????:"
 
 #: engines/scumm/help.cpp:123 engines/scumm/help.cpp:138
 #: engines/scumm/help.cpp:163 engines/supernova/metaengine.cpp:351
-#: engines/twp/metaengine.cpp:185
+#: engines/twp/metaengine.cpp:184
 msgid "Pull"
 msgstr "????????????"
 
@@ -15389,87 +15389,87 @@ msgstr "???????????????????????? ??????????????????????"
 msgid "Ransome *unbeeped* (DLC)"
 msgstr "Ransome *unbeeped* (DLC)"
 
-#: engines/twp/metaengine.cpp:151
+#: engines/twp/metaengine.cpp:150
 msgid "Select highlighted verb"
 msgstr "?????????????? ???????????????????? ????????????"
 
-#: engines/twp/metaengine.cpp:156
+#: engines/twp/metaengine.cpp:155
 msgid "Go to previous object"
 msgstr "?????????????? ???? ???????????????????? ????????????"
 
-#: engines/twp/metaengine.cpp:157
+#: engines/twp/metaengine.cpp:156
 msgid "Go to next object"
 msgstr "?????????????? ???? ?????????????????? ????????????"
 
-#: engines/twp/metaengine.cpp:158
+#: engines/twp/metaengine.cpp:157
 msgid "Select verb / item left"
 msgstr "?????????????? ???????????????? / ?????????????? ??????????"
 
-#: engines/twp/metaengine.cpp:159
+#: engines/twp/metaengine.cpp:158
 msgid "Select verb / item right"
 msgstr "?????????????? ???????????????? / ?????????????? ????????????"
 
-#: engines/twp/metaengine.cpp:160
+#: engines/twp/metaengine.cpp:159
 msgid "Select verb / item up"
 msgstr "?????????????? ???????????????? / ?????????????? ????????????"
 
-#: engines/twp/metaengine.cpp:161
+#: engines/twp/metaengine.cpp:160
 msgid "Select verb / item down"
 msgstr "?????????????? ???????????????? / ?????????????? ??????????"
 
-#: engines/twp/metaengine.cpp:163
+#: engines/twp/metaengine.cpp:162
 msgid "Select actor 1"
 msgstr "?????????????? ???????????? 1"
 
-#: engines/twp/metaengine.cpp:164
+#: engines/twp/metaengine.cpp:163
 msgid "Select actor 2"
 msgstr "?????????????? ???????????? 2"
 
-#: engines/twp/metaengine.cpp:165
+#: engines/twp/metaengine.cpp:164
 msgid "Select actor 3"
 msgstr "?????????????? ???????????? 3"
 
-#: engines/twp/metaengine.cpp:166
+#: engines/twp/metaengine.cpp:165
 msgid "Select actor 4"
 msgstr "?????????????? ???????????? 4"
 
-#: engines/twp/metaengine.cpp:167
+#: engines/twp/metaengine.cpp:166
 msgid "Select actor 5"
 msgstr "?????????????? ???????????? 5"
 
-#: engines/twp/metaengine.cpp:168
+#: engines/twp/metaengine.cpp:167
 msgid "Select choice 1"
 msgstr "?????????????? ?????????? 1"
 
-#: engines/twp/metaengine.cpp:169
+#: engines/twp/metaengine.cpp:168
 msgid "Select choice 2"
 msgstr "?????????????? ?????????? 2"
 
-#: engines/twp/metaengine.cpp:170
+#: engines/twp/metaengine.cpp:169
 msgid "Select choice 3"
 msgstr "?????????????? ?????????? 3"
 
-#: engines/twp/metaengine.cpp:171
+#: engines/twp/metaengine.cpp:170
 msgid "Select choice 4"
 msgstr "?????????????? ?????????? 4"
 
-#: engines/twp/metaengine.cpp:172
+#: engines/twp/metaengine.cpp:171
 msgid "Select choice 5"
 msgstr "?????????????? ?????????? 5"
 
-#: engines/twp/metaengine.cpp:173
+#: engines/twp/metaengine.cpp:172
 msgid "Select choice 6"
 msgstr "?????????????? ?????????? 6"
 
-#: engines/twp/metaengine.cpp:174
+#: engines/twp/metaengine.cpp:173
 msgid "Select next Actor"
 msgstr "?????????????? ???????????????????? ????????????"
 
-#: engines/twp/metaengine.cpp:175
+#: engines/twp/metaengine.cpp:174
 msgid "Select previous actor"
 msgstr "?????????????? ?????????????????????? ????????????"
 
-#: engines/twp/metaengine.cpp:177
+#: engines/twp/metaengine.cpp:176
 msgid "Show hotspots"
 msgstr "???????????????? ?????????????? ??????????"
 
diff --git a/po/sv_SE.po b/po/sv_SE.po
index 27c3f20e90c..2b30da89ce6 100644
--- a/po/sv_SE.po
+++ b/po/sv_SE.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.5.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-13 13:52+0000\n"
+"POT-Creation-Date: 2026-06-14 18:41+0000\n"
 "PO-Revision-Date: 2026-04-19 13:22+0000\n"
 "Last-Translator: Daniel Nylander <po at danielnylander.se>\n"
 "Language-Team: Swedish <https://translations.scummvm.org/projects/scummvm/"
@@ -73,7 +73,7 @@ msgstr "Tillg??ngliga motorer:"
 #: engines/scumm/help.cpp:141 engines/scumm/help.cpp:166
 #: engines/scumm/help.cpp:194 engines/scumm/help.cpp:212
 #: engines/sherlock/metaengine.cpp:430 engines/supernova/metaengine.cpp:341
-#: engines/tot/metaengine.cpp:149 engines/twp/metaengine.cpp:179
+#: engines/tot/metaengine.cpp:149 engines/twp/metaengine.cpp:178
 msgid "Close"
 msgstr "St??ng"
 
@@ -1055,7 +1055,7 @@ msgstr "Interagera"
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
 #: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1124
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
-#: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
+#: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:153
 #: engines/ultima/ultima0/metaengine.cpp:45
 #: engines/ultima/ultima4/metaengine.cpp:44
 #: engines/ultima/ultima4/metaengine.cpp:139
@@ -1076,7 +1076,7 @@ msgstr "Upp"
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
 #: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1091
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
-#: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
+#: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:45
 #: engines/ultima/ultima4/metaengine.cpp:140
@@ -1097,7 +1097,7 @@ msgstr "Ner"
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
 #: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1104
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
-#: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
+#: engines/twp/metaengine.cpp:151 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
 #: engines/ultima/ultima4/metaengine.cpp:151
 #: engines/wintermute/keymapper_tables.h:320
@@ -1117,7 +1117,7 @@ msgstr "V??nster"
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
 #: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1111
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
-#: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
+#: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
 #: engines/ultima/ultima4/metaengine.cpp:152
 #: engines/wintermute/keymapper_tables.h:326
@@ -3335,7 +3335,7 @@ msgstr "<Felaktigt v??rde>"
 
 #. I18N: Abbreviation for 'bytes' as data size
 #. I18N: Hardware key
-#: common/util.cpp:183 backends/platform/3ds/osystem-events.cpp:46
+#: common/util.cpp:190 backends/platform/3ds/osystem-events.cpp:46
 #: backends/platform/ds/osystem_ds.cpp:178
 #: backends/platform/sdl/miyoo/miyoo.cpp:61
 #: backends/platform/sdl/miyoo/miyoo.cpp:88
@@ -3345,22 +3345,22 @@ msgid "B"
 msgstr "B"
 
 #. I18N: Abbreviation for 'kilobytes' as data size
-#: common/util.cpp:189
+#: common/util.cpp:196
 msgid "KB"
 msgstr "KB"
 
 #. I18N: Abbreviation for 'megabytes' as data size
-#: common/util.cpp:194
+#: common/util.cpp:201
 msgid "MB"
 msgstr "MB"
 
 #. I18N: Abbreviation for 'gigabytes' as data size
-#: common/util.cpp:200
+#: common/util.cpp:207
 msgid "GB"
 msgstr "GB"
 
 #. I18N: Abbreviation for 'terabytes' as data size
-#: common/util.cpp:206
+#: common/util.cpp:213
 msgid "TB"
 msgstr "TB"
 
@@ -6414,7 +6414,7 @@ msgstr "Titta"
 #: engines/scumm/help.cpp:165 engines/scumm/help.cpp:193
 #: engines/scumm/help.cpp:211 engines/sherlock/metaengine.cpp:425
 #: engines/supernova/metaengine.cpp:336 engines/tot/metaengine.cpp:144
-#: engines/twp/metaengine.cpp:178
+#: engines/twp/metaengine.cpp:177
 msgid "Open"
 msgstr "??ppna"
 
@@ -6436,7 +6436,7 @@ msgstr "Ta emot"
 #: engines/sherlock/metaengine.cpp:440 engines/sherlock/metaengine.cpp:561
 #: engines/sky/metaengine.cpp:95 engines/sludge/keymapper_tables.h:333
 #: engines/supernova/metaengine.cpp:356 engines/tot/metaengine.cpp:139
-#: engines/tsage/metaengine.cpp:211 engines/twp/metaengine.cpp:186
+#: engines/tsage/metaengine.cpp:211 engines/twp/metaengine.cpp:185
 #: engines/ultima/ultima4/metaengine.cpp:74
 #: engines/ultima/nuvie/metaengine.cpp:69
 #: engines/wintermute/keymapper_tables.h:140
@@ -7206,7 +7206,7 @@ msgstr "Aktivera"
 
 #: engines/alcachofa/metaengine.cpp:104 engines/queen/metaengine.cpp:279
 #: engines/saga/metaengine.cpp:354 engines/scumm/help.cpp:202
-#: engines/scumm/help.cpp:214 engines/twp/metaengine.cpp:182
+#: engines/scumm/help.cpp:214 engines/twp/metaengine.cpp:181
 #: engines/wintermute/keymapper_tables.h:122
 #: engines/wintermute/keymapper_tables.h:1534
 msgid "Look at"
@@ -7559,7 +7559,7 @@ msgstr "V??xla strid"
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:210
 #: engines/phoenixvr/metaengine.cpp:94 engines/scumm/help.cpp:79
 #: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
-#: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
+#: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:161
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
 msgstr "Hoppa ??ver mellansekvens"
@@ -8381,7 +8381,7 @@ msgstr "Endast diskettversionens SFX"
 #. I18N: Perform action on object where cursor points
 #: engines/darkseed/metaengine.cpp:42 engines/dragons/metaengine.cpp:135
 #: engines/phoenixvr/metaengine.cpp:75 engines/tinsel/metaengine.cpp:294
-#: engines/twp/metaengine.cpp:150
+#: engines/twp/metaengine.cpp:149
 msgid "Action"
 msgstr "??tg??rd"
 
@@ -8802,7 +8802,7 @@ msgstr "Utrusta"
 #: engines/scumm/help.cpp:139 engines/scumm/help.cpp:164
 #: engines/scumm/help.cpp:200 engines/scumm/help.cpp:210
 #: engines/sherlock/metaengine.cpp:445 engines/sherlock/metaengine.cpp:566
-#: engines/supernova/metaengine.cpp:366 engines/twp/metaengine.cpp:180
+#: engines/supernova/metaengine.cpp:366 engines/twp/metaengine.cpp:179
 msgid "Give"
 msgstr "Ge"
 
@@ -12193,7 +12193,7 @@ msgstr "Anv??nd ett anpassat typsnitt som ??r enklare att l??sa"
 msgid "Move / Skip / Use default action with inventory item"
 msgstr "Flytta / Hoppa ??ver / Anv??nd standard??tg??rd med inventarieobjekt"
 
-#: engines/queen/metaengine.cpp:198 engines/twp/metaengine.cpp:176
+#: engines/queen/metaengine.cpp:198 engines/twp/metaengine.cpp:175
 msgid "Skip text"
 msgstr "Hoppa ??ver text"
 
@@ -12233,7 +12233,7 @@ msgstr "Anv??nd dagbok"
 #: engines/scumm/help.cpp:144 engines/scumm/help.cpp:169
 #: engines/scumm/help.cpp:198 engines/scumm/help.cpp:213
 #: engines/scumm/help.cpp:230 engines/sherlock/metaengine.cpp:420
-#: engines/sludge/keymapper_tables.h:318 engines/twp/metaengine.cpp:181
+#: engines/sludge/keymapper_tables.h:318 engines/twp/metaengine.cpp:180
 #: engines/wintermute/keymapper_tables.h:134
 #: engines/wintermute/keymapper_tables.h:1546
 msgid "Pick up"
@@ -12242,7 +12242,7 @@ msgstr "Ta"
 #: engines/queen/metaengine.cpp:289 engines/saga/metaengine.cpp:364
 #: engines/scumm/help.cpp:199 engines/scumm/help.cpp:215
 #: engines/scumm/help.cpp:251 engines/sludge/keymapper_tables.h:323
-#: engines/twp/metaengine.cpp:183 engines/wintermute/keymapper_tables.h:128
+#: engines/twp/metaengine.cpp:182 engines/wintermute/keymapper_tables.h:128
 #: engines/wintermute/keymapper_tables.h:1558
 msgid "Talk to"
 msgstr "Tala med"
@@ -12337,7 +12337,7 @@ msgstr "G?? till"
 
 #: engines/saga/metaengine.cpp:389 engines/scumm/help.cpp:122
 #: engines/scumm/help.cpp:137 engines/scumm/help.cpp:162
-#: engines/supernova/metaengine.cpp:346 engines/twp/metaengine.cpp:184
+#: engines/supernova/metaengine.cpp:346 engines/twp/metaengine.cpp:183
 msgid "Push"
 msgstr "Tryck"
 
@@ -13186,7 +13186,7 @@ msgstr "Huvudkontroller:"
 
 #: engines/scumm/help.cpp:123 engines/scumm/help.cpp:138
 #: engines/scumm/help.cpp:163 engines/supernova/metaengine.cpp:351
-#: engines/twp/metaengine.cpp:185
+#: engines/twp/metaengine.cpp:184
 msgid "Pull"
 msgstr "Dra"
 
@@ -15434,87 +15434,87 @@ msgstr "Klassisk mening"
 msgid "Ransome *unbeeped* (DLC)"
 msgstr "Ransome *unbeeped* (DLC)"
 
-#: engines/twp/metaengine.cpp:151
+#: engines/twp/metaengine.cpp:150
 msgid "Select highlighted verb"
 msgstr "V??lj f??rgmarkerat verb"
 
-#: engines/twp/metaengine.cpp:156
+#: engines/twp/metaengine.cpp:155
 msgid "Go to previous object"
 msgstr "G?? till f??reg??ende objekt"
 
-#: engines/twp/metaengine.cpp:157
+#: engines/twp/metaengine.cpp:156
 msgid "Go to next object"
 msgstr "G?? till n??sta objekt"
 
-#: engines/twp/metaengine.cpp:158
+#: engines/twp/metaengine.cpp:157
 msgid "Select verb / item left"
 msgstr "V??lj verb/objekt v??nster"
 
-#: engines/twp/metaengine.cpp:159
+#: engines/twp/metaengine.cpp:158
 msgid "Select verb / item right"
 msgstr "V??lj verb/objekt h??ger"
 
-#: engines/twp/metaengine.cpp:160
+#: engines/twp/metaengine.cpp:159
 msgid "Select verb / item up"
 msgstr "V??lj verb/objekt upp"
 
-#: engines/twp/metaengine.cpp:161
+#: engines/twp/metaengine.cpp:160
 msgid "Select verb / item down"
 msgstr "V??lj verb/objekt ner"
 
-#: engines/twp/metaengine.cpp:163
+#: engines/twp/metaengine.cpp:162
 msgid "Select actor 1"
 msgstr "V??lj sk??despelare 1"
 
-#: engines/twp/metaengine.cpp:164
+#: engines/twp/metaengine.cpp:163
 msgid "Select actor 2"
 msgstr "V??lj sk??despelare 2"
 
-#: engines/twp/metaengine.cpp:165
+#: engines/twp/metaengine.cpp:164
 msgid "Select actor 3"
 msgstr "V??lj sk??despelare 3"
 
-#: engines/twp/metaengine.cpp:166
+#: engines/twp/metaengine.cpp:165
 msgid "Select actor 4"
 msgstr "V??lj sk??despelare 4"
 
-#: engines/twp/metaengine.cpp:167
+#: engines/twp/metaengine.cpp:166
 msgid "Select actor 5"
 msgstr "V??lj sk??despelare 5"
 
-#: engines/twp/metaengine.cpp:168
+#: engines/twp/metaengine.cpp:167
 msgid "Select choice 1"
 msgstr "V??lj val 1"
 
-#: engines/twp/metaengine.cpp:169
+#: engines/twp/metaengine.cpp:168
 msgid "Select choice 2"
 msgstr "V??lj val 2"
 
-#: engines/twp/metaengine.cpp:170
+#: engines/twp/metaengine.cpp:169
 msgid "Select choice 3"
 msgstr "V??lj val 3"
 
-#: engines/twp/metaengine.cpp:171
+#: engines/twp/metaengine.cpp:170
 msgid "Select choice 4"
 msgstr "V??lj val 4"
 
-#: engines/twp/metaengine.cpp:172
+#: engines/twp/metaengine.cpp:171
 msgid "Select choice 5"
 msgstr "V??lj val 5"
 
-#: engines/twp/metaengine.cpp:173
+#: engines/twp/metaengine.cpp:172
 msgid "Select choice 6"
 msgstr "V??lj val 6"
 
-#: engines/twp/metaengine.cpp:174
+#: engines/twp/metaengine.cpp:173
 msgid "Select next Actor"
 msgstr "V??lj n??sta sk??despelare"
 
-#: engines/twp/metaengine.cpp:175
+#: engines/twp/metaengine.cpp:174
 msgid "Select previous actor"
 msgstr "V??lj f??reg??ende sk??despelare"
 
-#: engines/twp/metaengine.cpp:177
+#: engines/twp/metaengine.cpp:176
 msgid "Show hotspots"
 msgstr "Visa k??nsliga punkter"
 
diff --git a/po/tr.po b/po/tr.po
index 3aae84f6645..1e61d40cff3 100644
--- a/po/tr.po
+++ b/po/tr.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-13 13:52+0000\n"
+"POT-Creation-Date: 2026-06-14 18:41+0000\n"
 "PO-Revision-Date: 2024-10-10 12:33+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Turkish <https://translations.scummvm.org/projects/scummvm/"
@@ -75,7 +75,7 @@ msgstr "Etkin motorlar:"
 #: engines/scumm/help.cpp:141 engines/scumm/help.cpp:166
 #: engines/scumm/help.cpp:194 engines/scumm/help.cpp:212
 #: engines/sherlock/metaengine.cpp:430 engines/supernova/metaengine.cpp:341
-#: engines/tot/metaengine.cpp:149 engines/twp/metaengine.cpp:179
+#: engines/tot/metaengine.cpp:149 engines/twp/metaengine.cpp:178
 msgid "Close"
 msgstr "Kapat"
 
@@ -1059,7 +1059,7 @@ msgstr "Etkile??im"
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
 #: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1124
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
-#: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
+#: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:153
 #: engines/ultima/ultima0/metaengine.cpp:45
 #: engines/ultima/ultima4/metaengine.cpp:44
 #: engines/ultima/ultima4/metaengine.cpp:139
@@ -1080,7 +1080,7 @@ msgstr "Yukar??"
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
 #: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1091
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
-#: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
+#: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:45
 #: engines/ultima/ultima4/metaengine.cpp:140
@@ -1101,7 +1101,7 @@ msgstr "A??a????"
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
 #: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1104
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
-#: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
+#: engines/twp/metaengine.cpp:151 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
 #: engines/ultima/ultima4/metaengine.cpp:151
 #: engines/wintermute/keymapper_tables.h:320
@@ -1121,7 +1121,7 @@ msgstr "Sol"
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
 #: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1111
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
-#: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
+#: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
 #: engines/ultima/ultima4/metaengine.cpp:152
 #: engines/wintermute/keymapper_tables.h:326
@@ -3291,7 +3291,7 @@ msgstr "<K??t?? de??er>"
 
 #. I18N: Abbreviation for 'bytes' as data size
 #. I18N: Hardware key
-#: common/util.cpp:183 backends/platform/3ds/osystem-events.cpp:46
+#: common/util.cpp:190 backends/platform/3ds/osystem-events.cpp:46
 #: backends/platform/ds/osystem_ds.cpp:178
 #: backends/platform/sdl/miyoo/miyoo.cpp:61
 #: backends/platform/sdl/miyoo/miyoo.cpp:88
@@ -3301,22 +3301,22 @@ msgid "B"
 msgstr ""
 
 #. I18N: Abbreviation for 'kilobytes' as data size
-#: common/util.cpp:189
+#: common/util.cpp:196
 msgid "KB"
 msgstr ""
 
 #. I18N: Abbreviation for 'megabytes' as data size
-#: common/util.cpp:194
+#: common/util.cpp:201
 msgid "MB"
 msgstr ""
 
 #. I18N: Abbreviation for 'gigabytes' as data size
-#: common/util.cpp:200
+#: common/util.cpp:207
 msgid "GB"
 msgstr ""
 
 #. I18N: Abbreviation for 'terabytes' as data size
-#: common/util.cpp:206
+#: common/util.cpp:213
 msgid "TB"
 msgstr ""
 
@@ -6093,7 +6093,7 @@ msgstr "A??a????"
 #: engines/scumm/help.cpp:165 engines/scumm/help.cpp:193
 #: engines/scumm/help.cpp:211 engines/sherlock/metaengine.cpp:425
 #: engines/supernova/metaengine.cpp:336 engines/tot/metaengine.cpp:144
-#: engines/twp/metaengine.cpp:178
+#: engines/twp/metaengine.cpp:177
 #, fuzzy
 msgid "Open"
 msgstr "URL'yi a??"
@@ -6116,7 +6116,7 @@ msgstr ""
 #: engines/sherlock/metaengine.cpp:440 engines/sherlock/metaengine.cpp:561
 #: engines/sky/metaengine.cpp:95 engines/sludge/keymapper_tables.h:333
 #: engines/supernova/metaengine.cpp:356 engines/tot/metaengine.cpp:139
-#: engines/tsage/metaengine.cpp:211 engines/twp/metaengine.cpp:186
+#: engines/tsage/metaengine.cpp:211 engines/twp/metaengine.cpp:185
 #: engines/ultima/ultima4/metaengine.cpp:74
 #: engines/ultima/nuvie/metaengine.cpp:69
 #: engines/wintermute/keymapper_tables.h:140
@@ -6850,7 +6850,7 @@ msgstr "Aktif"
 
 #: engines/alcachofa/metaengine.cpp:104 engines/queen/metaengine.cpp:279
 #: engines/saga/metaengine.cpp:354 engines/scumm/help.cpp:202
-#: engines/scumm/help.cpp:214 engines/twp/metaengine.cpp:182
+#: engines/scumm/help.cpp:214 engines/twp/metaengine.cpp:181
 #: engines/wintermute/keymapper_tables.h:122
 #: engines/wintermute/keymapper_tables.h:1534
 #, fuzzy
@@ -7204,7 +7204,7 @@ msgstr "Altyaz??lar"
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:210
 #: engines/phoenixvr/metaengine.cpp:94 engines/scumm/help.cpp:79
 #: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
-#: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
+#: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:161
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
 msgstr ""
@@ -8070,7 +8070,7 @@ msgstr ""
 #. I18N: Perform action on object where cursor points
 #: engines/darkseed/metaengine.cpp:42 engines/dragons/metaengine.cpp:135
 #: engines/phoenixvr/metaengine.cpp:75 engines/tinsel/metaengine.cpp:294
-#: engines/twp/metaengine.cpp:150
+#: engines/twp/metaengine.cpp:149
 #, fuzzy
 msgid "Action"
 msgstr "Aktif"
@@ -8529,7 +8529,7 @@ msgstr ""
 #: engines/scumm/help.cpp:139 engines/scumm/help.cpp:164
 #: engines/scumm/help.cpp:200 engines/scumm/help.cpp:210
 #: engines/sherlock/metaengine.cpp:445 engines/sherlock/metaengine.cpp:566
-#: engines/supernova/metaengine.cpp:366 engines/twp/metaengine.cpp:180
+#: engines/supernova/metaengine.cpp:366 engines/twp/metaengine.cpp:179
 msgid "Give"
 msgstr ""
 
@@ -11885,7 +11885,7 @@ msgstr ""
 msgid "Move / Skip / Use default action with inventory item"
 msgstr ""
 
-#: engines/queen/metaengine.cpp:198 engines/twp/metaengine.cpp:176
+#: engines/queen/metaengine.cpp:198 engines/twp/metaengine.cpp:175
 #, fuzzy
 msgid "Skip text"
 msgstr "??ptal"
@@ -11934,7 +11934,7 @@ msgstr ""
 #: engines/scumm/help.cpp:144 engines/scumm/help.cpp:169
 #: engines/scumm/help.cpp:198 engines/scumm/help.cpp:213
 #: engines/scumm/help.cpp:230 engines/sherlock/metaengine.cpp:420
-#: engines/sludge/keymapper_tables.h:318 engines/twp/metaengine.cpp:181
+#: engines/sludge/keymapper_tables.h:318 engines/twp/metaengine.cpp:180
 #: engines/wintermute/keymapper_tables.h:134
 #: engines/wintermute/keymapper_tables.h:1546
 msgid "Pick up"
@@ -11943,7 +11943,7 @@ msgstr ""
 #: engines/queen/metaengine.cpp:289 engines/saga/metaengine.cpp:364
 #: engines/scumm/help.cpp:199 engines/scumm/help.cpp:215
 #: engines/scumm/help.cpp:251 engines/sludge/keymapper_tables.h:323
-#: engines/twp/metaengine.cpp:183 engines/wintermute/keymapper_tables.h:128
+#: engines/twp/metaengine.cpp:182 engines/wintermute/keymapper_tables.h:128
 #: engines/wintermute/keymapper_tables.h:1558
 msgid "Talk to"
 msgstr ""
@@ -12056,7 +12056,7 @@ msgstr ""
 
 #: engines/saga/metaengine.cpp:389 engines/scumm/help.cpp:122
 #: engines/scumm/help.cpp:137 engines/scumm/help.cpp:162
-#: engines/supernova/metaengine.cpp:346 engines/twp/metaengine.cpp:184
+#: engines/supernova/metaengine.cpp:346 engines/twp/metaengine.cpp:183
 msgid "Push"
 msgstr ""
 
@@ -12838,7 +12838,7 @@ msgstr "Oyun se??eneklerini de??i??tirme"
 
 #: engines/scumm/help.cpp:123 engines/scumm/help.cpp:138
 #: engines/scumm/help.cpp:163 engines/supernova/metaengine.cpp:351
-#: engines/twp/metaengine.cpp:185
+#: engines/twp/metaengine.cpp:184
 msgid "Pull"
 msgstr ""
 
@@ -15156,107 +15156,107 @@ msgstr ""
 msgid "Ransome *unbeeped* (DLC)"
 msgstr ""
 
-#: engines/twp/metaengine.cpp:151
+#: engines/twp/metaengine.cpp:150
 #, fuzzy
 msgid "Select highlighted verb"
 msgstr "Tema se??"
 
-#: engines/twp/metaengine.cpp:156
+#: engines/twp/metaengine.cpp:155
 #, fuzzy
 #| msgid "Go to previous directory level"
 msgid "Go to previous object"
 msgstr "Bir ??nceki klas??r seviyesine git"
 
-#: engines/twp/metaengine.cpp:157
+#: engines/twp/metaengine.cpp:156
 msgid "Go to next object"
 msgstr ""
 
-#: engines/twp/metaengine.cpp:158
+#: engines/twp/metaengine.cpp:157
 #, fuzzy
 msgid "Select verb / item left"
 msgstr "Sa??"
 
-#: engines/twp/metaengine.cpp:159
+#: engines/twp/metaengine.cpp:158
 #, fuzzy
 msgid "Select verb / item right"
 msgstr "Sa??"
 
-#: engines/twp/metaengine.cpp:160
+#: engines/twp/metaengine.cpp:159
 #, fuzzy
 msgid "Select verb / item up"
 msgstr "Sa??"
 
-#: engines/twp/metaengine.cpp:161
+#: engines/twp/metaengine.cpp:160
 #, fuzzy
 msgid "Select verb / item down"
 msgstr "Sa??"
 
-#: engines/twp/metaengine.cpp:163
+#: engines/twp/metaengine.cpp:162
 #, fuzzy
 msgid "Select actor 1"
 msgstr "Tema se??"
 
-#: engines/twp/metaengine.cpp:164
+#: engines/twp/metaengine.cpp:163
 #, fuzzy
 msgid "Select actor 2"
 msgstr "Tema se??"
 
-#: engines/twp/metaengine.cpp:165
+#: engines/twp/metaengine.cpp:164
 #, fuzzy
 msgid "Select actor 3"
 msgstr "Tema se??"
 
-#: engines/twp/metaengine.cpp:166
+#: engines/twp/metaengine.cpp:165
 #, fuzzy
 msgid "Select actor 4"
 msgstr "Tema se??"
 
-#: engines/twp/metaengine.cpp:167
+#: engines/twp/metaengine.cpp:166
 #, fuzzy
 msgid "Select actor 5"
 msgstr "Tema se??"
 
-#: engines/twp/metaengine.cpp:168
+#: engines/twp/metaengine.cpp:167
 #, fuzzy
 msgid "Select choice 1"
 msgstr "Tema se??"
 
-#: engines/twp/metaengine.cpp:169
+#: engines/twp/metaengine.cpp:168
 #, fuzzy
 msgid "Select choice 2"
 msgstr "Tema se??"
 
-#: engines/twp/metaengine.cpp:170
+#: engines/twp/metaengine.cpp:169
 #, fuzzy
 msgid "Select choice 3"
 msgstr "Tema se??"
 
-#: engines/twp/metaengine.cpp:171
+#: engines/twp/metaengine.cpp:170
 #, fuzzy
 msgid "Select choice 4"
 msgstr "Tema se??"
 
-#: engines/twp/metaengine.cpp:172
+#: engines/twp/metaengine.cpp:171
 #, fuzzy
 msgid "Select choice 5"
 msgstr "Tema se??"
 
-#: engines/twp/metaengine.cpp:173
+#: engines/twp/metaengine.cpp:172
 #, fuzzy
 msgid "Select choice 6"
 msgstr "Tema se??"
 
-#: engines/twp/metaengine.cpp:174
+#: engines/twp/metaengine.cpp:173
 #, fuzzy
 msgid "Select next Actor"
 msgstr "Tema se??"
 
-#: engines/twp/metaengine.cpp:175
+#: engines/twp/metaengine.cpp:174
 #, fuzzy
 msgid "Select previous actor"
 msgstr "Tema se??"
 
-#: engines/twp/metaengine.cpp:177
+#: engines/twp/metaengine.cpp:176
 #, fuzzy
 msgid "Show hotspots"
 msgstr "Gizli dosyalar?? g??ster"
diff --git a/po/uk_UA.po b/po/uk_UA.po
index f29e573df53..aaf50074c37 100644
--- a/po/uk_UA.po
+++ b/po/uk_UA.po
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.9.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-13 13:52+0000\n"
+"POT-Creation-Date: 2026-06-14 18:41+0000\n"
 "PO-Revision-Date: 2026-05-06 08:54+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Ukrainian <https://translations.scummvm.org/projects/scummvm/"
@@ -74,7 +74,7 @@ msgstr "???????????????? ????????????:"
 #: engines/scumm/help.cpp:141 engines/scumm/help.cpp:166
 #: engines/scumm/help.cpp:194 engines/scumm/help.cpp:212
 #: engines/sherlock/metaengine.cpp:430 engines/supernova/metaengine.cpp:341
-#: engines/tot/metaengine.cpp:149 engines/twp/metaengine.cpp:179
+#: engines/tot/metaengine.cpp:149 engines/twp/metaengine.cpp:178
 msgid "Close"
 msgstr "??????????????"
 
@@ -1057,7 +1057,7 @@ msgstr "??????????????????"
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
 #: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1124
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
-#: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
+#: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:153
 #: engines/ultima/ultima0/metaengine.cpp:45
 #: engines/ultima/ultima4/metaengine.cpp:44
 #: engines/ultima/ultima4/metaengine.cpp:139
@@ -1078,7 +1078,7 @@ msgstr "????????????"
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
 #: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1091
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
-#: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
+#: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:45
 #: engines/ultima/ultima4/metaengine.cpp:140
@@ -1099,7 +1099,7 @@ msgstr "????????????"
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
 #: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1104
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
-#: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
+#: engines/twp/metaengine.cpp:151 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
 #: engines/ultima/ultima4/metaengine.cpp:151
 #: engines/wintermute/keymapper_tables.h:320
@@ -1119,7 +1119,7 @@ msgstr "????????????"
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
 #: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1111
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
-#: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
+#: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
 #: engines/ultima/ultima4/metaengine.cpp:152
 #: engines/wintermute/keymapper_tables.h:326
@@ -3343,7 +3343,7 @@ msgstr "<?????????????? ????????????????>"
 
 #. I18N: Abbreviation for 'bytes' as data size
 #. I18N: Hardware key
-#: common/util.cpp:183 backends/platform/3ds/osystem-events.cpp:46
+#: common/util.cpp:190 backends/platform/3ds/osystem-events.cpp:46
 #: backends/platform/ds/osystem_ds.cpp:178
 #: backends/platform/sdl/miyoo/miyoo.cpp:61
 #: backends/platform/sdl/miyoo/miyoo.cpp:88
@@ -3353,22 +3353,22 @@ msgid "B"
 msgstr "B"
 
 #. I18N: Abbreviation for 'kilobytes' as data size
-#: common/util.cpp:189
+#: common/util.cpp:196
 msgid "KB"
 msgstr "????"
 
 #. I18N: Abbreviation for 'megabytes' as data size
-#: common/util.cpp:194
+#: common/util.cpp:201
 msgid "MB"
 msgstr "????"
 
 #. I18N: Abbreviation for 'gigabytes' as data size
-#: common/util.cpp:200
+#: common/util.cpp:207
 msgid "GB"
 msgstr "????"
 
 #. I18N: Abbreviation for 'terabytes' as data size
-#: common/util.cpp:206
+#: common/util.cpp:213
 msgid "TB"
 msgstr "????"
 
@@ -6423,7 +6423,7 @@ msgstr "??????????????"
 #: engines/scumm/help.cpp:165 engines/scumm/help.cpp:193
 #: engines/scumm/help.cpp:211 engines/sherlock/metaengine.cpp:425
 #: engines/supernova/metaengine.cpp:336 engines/tot/metaengine.cpp:144
-#: engines/twp/metaengine.cpp:178
+#: engines/twp/metaengine.cpp:177
 msgid "Open"
 msgstr "????????????????"
 
@@ -6445,7 +6445,7 @@ msgstr "????????????????"
 #: engines/sherlock/metaengine.cpp:440 engines/sherlock/metaengine.cpp:561
 #: engines/sky/metaengine.cpp:95 engines/sludge/keymapper_tables.h:333
 #: engines/supernova/metaengine.cpp:356 engines/tot/metaengine.cpp:139
-#: engines/tsage/metaengine.cpp:211 engines/twp/metaengine.cpp:186
+#: engines/tsage/metaengine.cpp:211 engines/twp/metaengine.cpp:185
 #: engines/ultima/ultima4/metaengine.cpp:74
 #: engines/ultima/nuvie/metaengine.cpp:69
 #: engines/wintermute/keymapper_tables.h:140
@@ -7214,7 +7214,7 @@ msgstr "????????????????????"
 
 #: engines/alcachofa/metaengine.cpp:104 engines/queen/metaengine.cpp:279
 #: engines/saga/metaengine.cpp:354 engines/scumm/help.cpp:202
-#: engines/scumm/help.cpp:214 engines/twp/metaengine.cpp:182
+#: engines/scumm/help.cpp:214 engines/twp/metaengine.cpp:181
 #: engines/wintermute/keymapper_tables.h:122
 #: engines/wintermute/keymapper_tables.h:1534
 msgid "Look at"
@@ -7564,7 +7564,7 @@ msgstr "???????????????????? ?????????? ??????"
 #: engines/darkseed/metaengine.cpp:65 engines/hypno/metaengine.cpp:210
 #: engines/phoenixvr/metaengine.cpp:94 engines/scumm/help.cpp:79
 #: engines/scumm/metaengine.cpp:1150 engines/supernova/metaengine.cpp:309
-#: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:162
+#: engines/toltecs/metaengine.cpp:246 engines/twp/metaengine.cpp:161
 #: engines/zvision/metaengine.cpp:304
 msgid "Skip cutscene"
 msgstr "???????????????????? ??????????"
@@ -8383,7 +8383,7 @@ msgstr "???????????? SFX ???????????? ?????? ????????????"
 #. I18N: Perform action on object where cursor points
 #: engines/darkseed/metaengine.cpp:42 engines/dragons/metaengine.cpp:135
 #: engines/phoenixvr/metaengine.cpp:75 engines/tinsel/metaengine.cpp:294
-#: engines/twp/metaengine.cpp:150
+#: engines/twp/metaengine.cpp:149
 msgid "Action"
 msgstr "??????"
 
@@ -8798,7 +8798,7 @@ msgstr "????????????"
 #: engines/scumm/help.cpp:139 engines/scumm/help.cpp:164
 #: engines/scumm/help.cpp:200 engines/scumm/help.cpp:210
 #: engines/sherlock/metaengine.cpp:445 engines/sherlock/metaengine.cpp:566
-#: engines/supernova/metaengine.cpp:366 engines/twp/metaengine.cpp:180
+#: engines/supernova/metaengine.cpp:366 engines/twp/metaengine.cpp:179
 msgid "Give"
 msgstr "????????"
 
@@ -12103,7 +12103,7 @@ msgid "Move / Skip / Use default action with inventory item"
 msgstr ""
 "?????????????? / ???????????????????? / ???????????????????????????? ?????? ???? ???????????????????? ???? ???????????????? ??????????????????"
 
-#: engines/queen/metaengine.cpp:198 engines/twp/metaengine.cpp:176
+#: engines/queen/metaengine.cpp:198 engines/twp/metaengine.cpp:175
 msgid "Skip text"
 msgstr "???????????????????? ??????????"
 
@@ -12143,7 +12143,7 @@ msgstr "?????????????????????? ????????????"
 #: engines/scumm/help.cpp:144 engines/scumm/help.cpp:169
 #: engines/scumm/help.cpp:198 engines/scumm/help.cpp:213
 #: engines/scumm/help.cpp:230 engines/sherlock/metaengine.cpp:420
-#: engines/sludge/keymapper_tables.h:318 engines/twp/metaengine.cpp:181
+#: engines/sludge/keymapper_tables.h:318 engines/twp/metaengine.cpp:180
 #: engines/wintermute/keymapper_tables.h:134
 #: engines/wintermute/keymapper_tables.h:1546
 msgid "Pick up"
@@ -12152,7 +12152,7 @@ msgstr "??????????????????"
 #: engines/queen/metaengine.cpp:289 engines/saga/metaengine.cpp:364
 #: engines/scumm/help.cpp:199 engines/scumm/help.cpp:215
 #: engines/scumm/help.cpp:251 engines/sludge/keymapper_tables.h:323
-#: engines/twp/metaengine.cpp:183 engines/wintermute/keymapper_tables.h:128
+#: engines/twp/metaengine.cpp:182 engines/wintermute/keymapper_tables.h:128
 #: engines/wintermute/keymapper_tables.h:1558
 msgid "Talk to"
 msgstr "???????????????? ????"
@@ -12247,7 +12247,7 @@ msgstr "?????? ????"
 
 #: engines/saga/metaengine.cpp:389 engines/scumm/help.cpp:122
 #: engines/scumm/help.cpp:137 engines/scumm/help.cpp:162
-#: engines/supernova/metaengine.cpp:346 engines/twp/metaengine.cpp:184
+#: engines/supernova/metaengine.cpp:346 engines/twp/metaengine.cpp:183
 msgid "Push"
 msgstr "????????????????"
 
@@ -13100,7 +13100,7 @@ msgstr "?????????????? ?????????? ??????????????????:"
 
 #: engines/scumm/help.cpp:123 engines/scumm/help.cpp:138
 #: engines/scumm/help.cpp:163 engines/supernova/metaengine.cpp:351
-#: engines/twp/metaengine.cpp:185
+#: engines/twp/metaengine.cpp:184
 msgid "Pull"
 msgstr "??????????????"
 
@@ -15343,87 +15343,87 @@ msgstr "???????????????? ??????????????"
 msgid "Ransome *unbeeped* (DLC)"
 msgstr "Ransome *unbeeped* (DLC)"
 
-#: engines/twp/metaengine.cpp:151
+#: engines/twp/metaengine.cpp:150
 msgid "Select highlighted verb"
 msgstr "???????????????? ???????????????? ????????????????"
 
-#: engines/twp/metaengine.cpp:156
+#: engines/twp/metaengine.cpp:155
 msgid "Go to previous object"
 msgstr "?????????????? ???? ???????????????????? ????'??????"
 
-#: engines/twp/metaengine.cpp:157
+#: engines/twp/metaengine.cpp:156
 msgid "Go to next object"
 msgstr "?????????????? ???? ?????????????????? ????'??????"
 
-#: engines/twp/metaengine.cpp:158
+#: engines/twp/metaengine.cpp:157
 msgid "Select verb / item left"
 msgstr "?????????????? ?????? / ?????????????? ??????????"
 
-#: engines/twp/metaengine.cpp:159
+#: engines/twp/metaengine.cpp:158
 msgid "Select verb / item right"
 msgstr "?????????????? ?????? / ?????????????? ????????????"
 
-#: engines/twp/metaengine.cpp:160
+#: engines/twp/metaengine.cpp:159
 msgid "Select verb / item up"
 msgstr "?????????????? ?????? / ?????????????? ??????????"
 
-#: engines/twp/metaengine.cpp:161
+#: engines/twp/metaengine.cpp:160
 msgid "Select verb / item down"
 msgstr "?????????????? ?????? / ?????????????? ??????????"
 
-#: engines/twp/metaengine.cpp:163
+#: engines/twp/metaengine.cpp:162
 msgid "Select actor 1"
 msgstr "?????????????? ???????????? 1"
 
-#: engines/twp/metaengine.cpp:164
+#: engines/twp/metaengine.cpp:163
 msgid "Select actor 2"
 msgstr "?????????????? ???????????? 2"
 
-#: engines/twp/metaengine.cpp:165
+#: engines/twp/metaengine.cpp:164
 msgid "Select actor 3"
 msgstr "?????????????? ???????????? 3"
 
-#: engines/twp/metaengine.cpp:166
+#: engines/twp/metaengine.cpp:165
 msgid "Select actor 4"
 msgstr "?????????????? ???????????? 4"
 
-#: engines/twp/metaengine.cpp:167
+#: engines/twp/metaengine.cpp:166
 msgid "Select actor 5"
 msgstr "?????????????? ???????????? 5"
 
-#: engines/twp/metaengine.cpp:168
+#: engines/twp/metaengine.cpp:167
 msgid "Select choice 1"
 msgstr "?????????????? ?????????? 1"
 
-#: engines/twp/metaengine.cpp:169
+#: engines/twp/metaengine.cpp:168
 msgid "Select choice 2"
 msgstr "?????????????? ?????????? 2"
 
-#: engines/twp/metaengine.cpp:170
+#: engines/twp/metaengine.cpp:169
 msgid "Select choice 3"
 msgstr "?????????????? ?????????? 3"
 
-#: engines/twp/metaengine.cpp:171
+#: engines/twp/metaengine.cpp:170
 msgid "Select choice 4"
 msgstr "?????????????? ?????????? 4"
 
-#: engines/twp/metaengine.cpp:172
+#: engines/twp/metaengine.cpp:171
 msgid "Select choice 5"
 msgstr "?????????????? ?????????? 5"
 
-#: engines/twp/metaengine.cpp:173
+#: engines/twp/metaengine.cpp:172
 msgid "Select choice 6"
 msgstr "?????????????? ?????????? 6"
 
-#: engines/twp/metaengine.cpp:174
+#: engines/twp/metaengine.cpp:173
 msgid "Select next Actor"
 msgstr "?????????????? ???????????????????? ????????????"
 
-#: engines/twp/metaengine.cpp:175
+#: engines/twp/metaengine.cpp:174
 msgid "Select previous actor"
 msgstr "?????????????? ???????????????????????? ????????????"
 
-#: engines/twp/metaengine.cpp:177
+#: engines/twp/metaengine.cpp:176
 msgid "Show hotspots"
 msgstr "???????????????????? ???????????? ??????????"
 
diff --git a/po/zh.po b/po/zh.po
index 699f563de97..1a48b7f4a83 100644
--- a/po/zh.po
+++ b/po/zh.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-13 13:52+0000\n"
+"POT-Creation-Date: 2026-06-14 18:41+0000\n"
 "PO-Revision-Date: 2024-05-14 12:42+0000\n"
 "Last-Translator: ?????? <clonewith at qq.com>\n"
 "Language-Team: Chinese <https://translations.scummvm.org/projects/scummvm/"
@@ -73,7 +73,7 @@ msgstr "???????????????"
 #: engines/scumm/help.cpp:141 engines/scumm/help.cpp:166
 #: engines/scumm/help.cpp:194 engines/scumm/help.cpp:212
 #: engines/sherlock/metaengine.cpp:430 engines/supernova/metaengine.cpp:341
-#: engines/tot/metaengine.cpp:149 engines/twp/metaengine.cpp:179
+#: engines/tot/metaengine.cpp:149 engines/twp/metaengine.cpp:178
 msgid "Close"
 msgstr "??????"
 
@@ -1035,7 +1035,7 @@ msgstr "??????"
 #: engines/grim/grim.cpp:481 engines/grim/grim.cpp:563 engines/icb/icb.cpp:68
 #: engines/icb/icb.cpp:152 engines/scumm/metaengine.cpp:1124
 #: engines/supernova/metaengine.cpp:273 engines/twine/metaengine.cpp:430
-#: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:154
+#: engines/twine/metaengine.cpp:497 engines/twp/metaengine.cpp:153
 #: engines/ultima/ultima0/metaengine.cpp:45
 #: engines/ultima/ultima4/metaengine.cpp:44
 #: engines/ultima/ultima4/metaengine.cpp:139
@@ -1056,7 +1056,7 @@ msgstr "??????"
 #: engines/grim/grim.cpp:486 engines/grim/grim.cpp:568 engines/icb/icb.cpp:73
 #: engines/icb/icb.cpp:157 engines/scumm/metaengine.cpp:1091
 #: engines/supernova/metaengine.cpp:279 engines/twine/metaengine.cpp:437
-#: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:155
+#: engines/twine/metaengine.cpp:505 engines/twp/metaengine.cpp:154
 #: engines/ultima/ultima0/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:45
 #: engines/ultima/ultima4/metaengine.cpp:140
@@ -1077,7 +1077,7 @@ msgstr "??????"
 #: engines/grim/grim.cpp:491 engines/grim/grim.cpp:573 engines/icb/icb.cpp:78
 #: engines/icb/icb.cpp:162 engines/scumm/metaengine.cpp:1104
 #: engines/twine/metaengine.cpp:452 engines/twine/metaengine.cpp:520
-#: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:46
+#: engines/twp/metaengine.cpp:151 engines/ultima/ultima4/metaengine.cpp:46
 #: engines/ultima/ultima4/metaengine.cpp:141
 #: engines/ultima/ultima4/metaengine.cpp:151
 #: engines/wintermute/keymapper_tables.h:320
@@ -1097,7 +1097,7 @@ msgstr "??????"
 #: engines/grim/grim.cpp:496 engines/grim/grim.cpp:578 engines/icb/icb.cpp:83
 #: engines/icb/icb.cpp:167 engines/scumm/metaengine.cpp:1111
 #: engines/twine/metaengine.cpp:444 engines/twine/metaengine.cpp:513
-#: engines/twp/metaengine.cpp:153 engines/ultima/ultima4/metaengine.cpp:47
+#: engines/twp/metaengine.cpp:152 engines/ultima/ultima4/metaengine.cpp:47
 #: engines/ultima/ultima4/metaengine.cpp:142
 #: engines/ultima/ultima4/metaengine.cpp:152
 #: engines/wintermute/keymapper_tables.h:326
@@ -3141,7 +3141,7 @@ msgstr ""
 
 #. I18N: Abbreviation for 'bytes' as data size
 #. I18N: Hardware key
-#: common/util.cpp:183 backends/platform/3ds/osystem-events.cpp:46
+#: common/util.cpp:190 backends/platform/3ds/osystem-events.cpp:46
 #: backends/platform/ds/osystem_ds.cpp:178
 #: backends/platform/sdl/miyoo/miyoo.cpp:61
 #: backends/platform/sdl/miyoo/miyoo.cpp:88
@@ -3151,22 +3151,22 @@ msgid "B"
 msgstr ""
 
 #. I18N: Abbreviation for 'kilobytes' as data size
-#: common/util.cpp:189
+#: common/util.cpp:196
 msgid "KB"
 msgstr ""
 
 #. I18N: Abbreviation for 'megabytes' as data size
-#: common/util.cpp:194
+#: common/util.cpp:201
 msgid "MB"
 msgstr ""
 
 #. I18N: Abbreviation for 'gigabytes' as data size
-#: common/util.cpp:200
+#: common/util.cpp:207
 msgid "GB"
 msgstr ""
 
 #. I18N: Abbreviation for 'terabytes' as data size
-#: common/util.cpp:206
+#: common/util.cpp:213
 msgid "TB"
 msgstr ""
 
@@ -5869,7 +5869,7 @@ msgstr ""
 #: engines/scumm/help.cpp:165 engines/scumm/help.cpp:193
 #: engines/scumm/help.cpp:211 engines/sherlock/metaengine.cpp:425
 #: engines/supernova/metaengine.cpp:336 engines/tot/metaengine.cpp:144
-#: engines/twp/metaengine.cpp:178
+#: engines/twp/metaengine.cpp:177
 msgid "Open"
 msgstr ""
 
@@ -5891,7 +5891,7 @@ msgstr ""
 #: engines/sherlock/metaengine.cpp:440 engines/sherlock/metaengine.cpp:561
 #: engines/sky/metaengine.cpp:95 engines/sludge/keymapper_tables.h:333
 #: engines/supernova/metaengine.cpp:356 engines/tot/metaengine.cpp:139
-#: engines/tsage/metaengine.cpp:211 engines/twp/metaengine.cpp:186
+#: engines/tsage/metaengine.cpp:211 engines/twp/metaengine.cpp:185
 #: engines/ultima/ultima4/metaengine.cpp:74
 #: engines/ultima/nuvie/metaengine.cpp:69
 #: engines/wintermute/keymapper_tables.h:140
@@ -6621,7 +6621,7 @@ msgstr "??????"
 



From noreply at scummvm.org  Sun Jun 14 19:13:50 2026
From: noreply at scummvm.org (sev-)
Date: Sun, 14 Jun 2026 19:13:50 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 6ac56c5cf8bb76166b57087644b68b4781bc0876
Message-ID: <20260614191350.4C70D7005CC@ron.scummvm.net>

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

Summary:
d1c4b02b6a CHAMBER: Fix infinite failed-ordeals death loop and ordeal timer bleed
cfdf32e5df CHAMBER: Seed RNG and fix stale rand_value in prepareAspirant
565af35a80 CHAMBER: Fix EGA lutin scratch overflow corrupting sprites_list
48ab13e0da CHAMBER: Drop stale actor/command on door room swap
ed6b4260f2 CHAMBER: Fix EGA opcode 0x68 PlaySfx operand width
b528e1ca20 CHAMBER: Fix endgame confrontation menu re-prompt after winning flask
6ac56c5cf8 CHAMBER: Make RNG seed overridable via random_seed config key


Commit: d1c4b02b6a0ccfa14277bfe80b309236c3fcc1fb
    https://github.com/scummvm/scummvm/commit/d1c4b02b6a0ccfa14277bfe80b309236c3fcc1fb
Author: Ion Andrei Cristian (lecturatul2017 at gmail.com)
Date: 2026-06-14T21:13:43+02:00

Commit Message:
CHAMBER: Fix infinite failed-ordeals death loop and ordeal timer bleed

The Master's Orbit endlessly replayed the failed-ordeals death scene.
Two distinct issues were behind it:

- checkGameTimeLimit() queues next_protozorqs_cmd=0xC012 (the death
  dispatcher) when the one-hour ordeal timer expires, but the command
  was never cleared: updateProtozorqs() early-returns at bvar_26 >= 63
  before its own clear, so the scene re-fired every frame. Consume the
  command when dispatched so it fires once.

- timer_ticks2 (the ordeal clock) is real wall-clock time gated only by
  game_paused, which was never set during the intro or the slow zone
  load/room transitions. Those bled real seconds into the one-hour
  budget. Freeze the timer across the intro and SCR_42_LoadZone.

Changed paths:
    engines/chamber/kult.cpp
    engines/chamber/script.cpp


diff --git a/engines/chamber/kult.cpp b/engines/chamber/kult.cpp
index 32b9445be78..b8d4d42061a 100644
--- a/engines/chamber/kult.cpp
+++ b/engines/chamber/kult.cpp
@@ -175,8 +175,15 @@ void gameLoop(byte *target) {
 				continue;
 
 			the_command = Swap16(script_word_vars.next_protozorqs_cmd);
-			if (the_command)
+			if (the_command) {
+				// Consume the queued command so a terminal command (e.g. the
+				// "failed the ordeals" death scene queued by checkGameTimeLimit)
+				// fires once instead of every frame. updateProtozorqs() re-queues
+				// live protozorq AI each frame, but it early-returns once
+				// bvar_26 >= 63 and would otherwise leave this set forever.
+				script_word_vars.next_protozorqs_cmd = 0;
 				goto process;
+			}
 
 			if (Swap16(next_vorts_ticks) < script_word_vars.timer_ticks2) { /*TODO: is this ok? ticks2 is BE, ticks3 is LE*/
 				the_command = next_vorts_cmd;
@@ -467,8 +474,13 @@ Common::Error ChamberEngine::execute() {
 	//ResetInput();
 
 	/* Play introduction sequence and initialize game */
+	// Freeze the ordeal timer during the intro/setup: it is installed before this
+	// point (initTimer), so the non-interactive intro would otherwise start the
+	// player's one-hour ordeal budget early.
+	script_byte_vars.game_paused = 1;
 	the_command = 0xC001;
 	runCommand();
+	script_byte_vars.game_paused = 0;
 
 	if (_shouldQuit)
 		return Common::kNoError;
diff --git a/engines/chamber/script.cpp b/engines/chamber/script.cpp
index 1468a83e52a..11b0fc6a966 100644
--- a/engines/chamber/script.cpp
+++ b/engines/chamber/script.cpp
@@ -1517,6 +1517,13 @@ If go through a door, play door's opening animation
 uint16 SCR_42_LoadZone(void) {
 	byte index;
 	bool door_animated = false;
+	// Freeze the ordeal timer (timer_ticks2) while we load the zone and play the
+	// room transition. These are far slower under ScummVM than on DOS and would
+	// otherwise bleed real seconds into the one-hour ordeal budget. timer_ticks
+	// (animation pacing) keeps running. Save/restore so we don't unpause an
+	// already-paused state (e.g. a zone load during the game-over sequence).
+	byte saved_paused = script_byte_vars.game_paused;
+	script_byte_vars.game_paused = 1;
 
 	script_ptr++;
 	index = *script_ptr++;
@@ -1558,6 +1565,7 @@ uint16 SCR_42_LoadZone(void) {
 	if (door_animated)
 		g_vm->_renderer->backBufferToRealFull();
 
+	script_byte_vars.game_paused = saved_paused;
 	return 0;
 }
 


Commit: cfdf32e5dfa6caac37568dd42610d8ca8dc9b592
    https://github.com/scummvm/scummvm/commit/cfdf32e5dfa6caac37568dd42610d8ca8dc9b592
Author: Ion Andrei Cristian (lecturatul2017 at gmail.com)
Date: 2026-06-14T21:13:43+02:00

Commit Message:
CHAMBER: Seed RNG and fix stale rand_value in prepareAspirant

randomize() was a stub leaving rand_seed=0, making every game roll the
same deterministic sequence (guaranteed-hostile Aspirant, fixed starting
item). Seed rand_seed from the host millisecond timer and prime the
stream. Also pull a fresh getRand() for Aspirant hostility instead of
reusing the stale rand_value.

Changed paths:
    engines/chamber/kult.cpp
    engines/chamber/room.cpp


diff --git a/engines/chamber/kult.cpp b/engines/chamber/kult.cpp
index b8d4d42061a..fc3b1c2ab77 100644
--- a/engines/chamber/kult.cpp
+++ b/engines/chamber/kult.cpp
@@ -111,15 +111,10 @@ uint16 benchmarkCpu(void) {
 }
 
 void randomize(void) {
-	warning("STUB: Randomize()");
-#if 0
-	union REGS reg;
-
-	reg.h.ah = 0;
-	int86(0x1A, &reg, &reg);
-	rand_seed = reg.h.dl;
-	Rand();
-#endif
+	// Original read the low byte of the BIOS timer-tick count (int 0x1A) into
+	// rand_seed. Use the host millisecond timer as an equivalent entropy source.
+	rand_seed = (byte)(g_system->getMillis());
+	getRand();
 }
 
 void TRAP() {
diff --git a/engines/chamber/room.cpp b/engines/chamber/room.cpp
index a4d0081ec50..974540e3fe4 100644
--- a/engines/chamber/room.cpp
+++ b/engines/chamber/room.cpp
@@ -1309,7 +1309,7 @@ void prepareAspirant(void) {
 	if (aspirant_ptr->flags & PERSFLG_40)
 		return;
 
-	hostility = script_byte_vars.rand_value;
+	hostility = getRand();
 	appearance = getRand();
 	flags = 0;
 	/*


Commit: 565af35a8062c45d2eb7c36a7210e6acd5bd7d5f
    https://github.com/scummvm/scummvm/commit/565af35a8062c45d2eb7c36a7210e6acd5bd7d5f
Author: Ion Andrei Cristian (lecturatul2017 at gmail.com)
Date: 2026-06-14T21:13:43+02:00

Commit Message:
CHAMBER: Fix EGA lutin scratch overflow corrupting sprites_list

The EGA path decodes each lutin to CLUT8 (4 bytes per CGA byte), twice the
CGA footprint, but getScratchBuffer kept the CGA slot strides and scratch_mem1
stayed at 8010 bytes. A large lutin (e.g. the Vort animation in The Return)
overran its slot past the end of scratch_mem1 into the adjacent sprites_list[],
corrupting it and later crashing in blitSpritesToBackBuffer/restoreImage.
Double the partition strides in EGA and size scratch_mem1 for the EGA worst
case.

Changed paths:
    engines/chamber/anim.cpp
    engines/chamber/room.cpp
    engines/chamber/room.h


diff --git a/engines/chamber/anim.cpp b/engines/chamber/anim.cpp
index 43119446cba..8ba0b2d9e9f 100644
--- a/engines/chamber/anim.cpp
+++ b/engines/chamber/anim.cpp
@@ -55,10 +55,16 @@ extern void loadLutinSprite(uint16 lutidx);
 void getScratchBuffer(byte mode) {
 	byte *buffer = scratch_mem2;
 	uint16 offs = 0;
+	// EGA decodes each lutin to CLUT8 (1 byte per pixel = 4 bytes per CGA byte),
+	// so a slot is twice the CGA footprint. Double the partition strides in EGA,
+	// otherwise a large lutin overruns its slot into the next one - and the top
+	// slot overruns the end of scratch_mem1 into the adjacent sprites_list[],
+	// corrupting it (later crashing in blitSpritesToBackBuffer/restoreImage).
+	uint16 slot = (g_vm->_videoMode == Common::kRenderEGA) ? 3200 : 1600;
 	if (mode & 0x80)
-		offs += 3200;
+		offs += slot * 2;
 	if (mode & 0x40)
-		offs += 1600;
+		offs += slot;
 	lutin_mem = buffer + offs;
 }
 
diff --git a/engines/chamber/room.cpp b/engines/chamber/room.cpp
index 974540e3fe4..c72977ba08e 100644
--- a/engines/chamber/room.cpp
+++ b/engines/chamber/room.cpp
@@ -40,7 +40,12 @@
 
 namespace Chamber {
 
-byte scratch_mem1[8010];
+// 1500-byte spot-backup region, then the lutin/anim scratch (scratch_mem2).
+// The scratch holds up to four simultaneous lutin slots (see getScratchBuffer).
+// CGA needs 4*1600 = 6400 there; EGA decodes lutins to CLUT8 (4 bytes per CGA
+// byte) so it needs 4*3200 = 12800. Sized for the EGA worst case so a big lutin
+// can't overrun into the adjacent sprites_list[].
+byte scratch_mem1[14400];
 byte *scratch_mem2 = scratch_mem1 + 1500;
 
 rect_t room_bounds_rect = {0, 0, 0, 0};
diff --git a/engines/chamber/room.h b/engines/chamber/room.h
index 8211089e028..263e1ea4667 100644
--- a/engines/chamber/room.h
+++ b/engines/chamber/room.h
@@ -90,7 +90,7 @@ typedef struct turkeyanims_t {
 	animdesc_t field_4;
 } turkeyanims_t;
 
-extern byte scratch_mem1[8010];
+extern byte scratch_mem1[14400];
 extern byte *scratch_mem2;
 
 extern rect_t room_bounds_rect;


Commit: 48ab13e0da3d1fd3624a8c253a8f2ebdf8c605de
    https://github.com/scummvm/scummvm/commit/48ab13e0da3d1fd3624a8c253a8f2ebdf8c605de
Author: Ion Andrei Cristian (lecturatul2017 at gmail.com)
Date: 2026-06-14T21:13:43+02:00

Commit Message:
CHAMBER: Drop stale actor/command on door room swap

Going through a door must end any interaction in progress: abort the command
chain and reset CurrentPers in SCR_42_LoadZone so a queued command (e.g. an
Aspirant trade) can't run in a zone with no matching spawn spot. Also correct
the DEBUG_QUEST script offset (0x4F->0x5B) to the quest-selection branch.

Changed paths:
    engines/chamber/script.cpp


diff --git a/engines/chamber/script.cpp b/engines/chamber/script.cpp
index 11b0fc6a966..0affa6388c0 100644
--- a/engines/chamber/script.cpp
+++ b/engines/chamber/script.cpp
@@ -1544,6 +1544,15 @@ uint16 SCR_42_LoadZone(void) {
 	}
 	beforeChangeZone(index);
 	changeZone(index);
+
+	// End any in-flight interaction when going through a door: an Aspirant trade
+	// can still be mid-flight (runCommand ScriptRerun loop, CurrentPers still on
+	// the old room's actor), so abort the chain and drop the stale actor here.
+	// prepareVorts/Turkey/Aspirant below repopulate CurrentPers. Done here, not in
+	// changeZone(), since SCR_25_ChangeZoneOnly's in-place swaps must keep both.
+	the_command = 0;
+	script_vars[kScrPool8_CurrentPers] = pers_list;
+
 	script_byte_vars.zone_area_copy = script_byte_vars.zone_area;
 	script_byte_vars.cur_spot_idx = findInitialSpot();
 	skip_zone_transition |= script_byte_vars.cur_spot_idx;
@@ -4441,8 +4450,12 @@ uint16 RunScript(byte *code) {
 #endif
 
 #ifdef DEBUG_QUEST
-		if (script_ptr - templ_data == 0x4F) {
+		if (script_ptr - templ_data == 0x5B) {
 			/*manipulate rand_value to get a quest item we need*/
+			// 0x5B is the EGA (kultega.bin) offset of the quest-selection branch
+			// 'if rand_value < 0x40'; the old 0x4F was mid-instruction so it never
+			// fired. Forcing rand_value here picks the quest: 0x00=Rope/De Profundis,
+			// 0x40=Knife/The Wall, 0x80=Goblet/Twins, 0xC0=Fly/Scorpion's.
 			script_byte_vars.rand_value = DEBUG_QUEST;
 		}
 #endif


Commit: ed6b4260f2318df0f67524f9fe6f899d38c02542
    https://github.com/scummvm/scummvm/commit/ed6b4260f2318df0f67524f9fe6f899d38c02542
Author: Ion Andrei Cristian (lecturatul2017 at gmail.com)
Date: 2026-06-14T21:13:43+02:00

Commit Message:
CHAMBER: Fix EGA opcode 0x68 PlaySfx operand width

0x68 has a single sfx-index operand in kultega.bin (unlike 0x69 which
has a pad byte). The stray pad read desynced script_ptr - e.g. in the
Scorpion ordeal it ate the setVar that unlocks the door.

Changed paths:
    engines/chamber/script.cpp


diff --git a/engines/chamber/script.cpp b/engines/chamber/script.cpp
index 0affa6388c0..7abd9a61999 100644
--- a/engines/chamber/script.cpp
+++ b/engines/chamber/script.cpp
@@ -3272,12 +3272,15 @@ uint16 SCR_67_Unused(void) {
 /*
 Play Sfx
 NB! Do nothing in EU PC/CGA version
+EGA (kultega.bin) encodes this as a single operand byte (the sfx index) - unlike
+SCR_69 below which has a trailing pad byte. Reading a pad here too would consume
+the following opcode and desync script_ptr (e.g. eats the setVar that unlocks the
+Scorpion ordeal door, leaving its speech bubble stuck on screen).
 */
 uint16 SCR_68_PlaySfx(void) {
 	byte index;
 	script_ptr++;
 	index = *script_ptr++;
-	script_ptr++;
 	IFGM_PlaySfx(index);
 	return 0;
 }


Commit: b528e1ca20780f2feb2f59f82f6d1e01cf17526c
    https://github.com/scummvm/scummvm/commit/b528e1ca20780f2feb2f59f82f6d1e01cf17526c
Author: Ion Andrei Cristian (lecturatul2017 at gmail.com)
Date: 2026-06-14T21:13:43+02:00

Commit Message:
CHAMBER: Fix endgame confrontation menu re-prompt after winning flask

Changed paths:
    engines/chamber/kult.cpp
    engines/chamber/script.cpp


diff --git a/engines/chamber/kult.cpp b/engines/chamber/kult.cpp
index fc3b1c2ab77..084c28b39c8 100644
--- a/engines/chamber/kult.cpp
+++ b/engines/chamber/kult.cpp
@@ -198,7 +198,11 @@ process:
 			;
 			updateUndrawCursor(target);
 			refreshSpritesData();
-			runCommand();
+			// Drain priority commands at this main-loop baseline too: a queued
+			// AI command (e.g. the timed "failed the ordeals" death scene) may
+			// fire a priority command, which runCommand now propagates up to a
+			// runCommandKeepSp anchor instead of running it nested.
+			runCommandKeepSp();
 			if (g_vm->_shouldRestart)
 				return;
 			blitSpritesToBackBuffer();
diff --git a/engines/chamber/script.cpp b/engines/chamber/script.cpp
index 7abd9a61999..9359788b66b 100644
--- a/engines/chamber/script.cpp
+++ b/engines/chamber/script.cpp
@@ -3332,6 +3332,7 @@ uint16 CMD_2_PsiPowers(void) {
 	/*Psi powers bar*/
 	g_vm->_renderer->backupAndShowSprite(3, 280 / 4, 40);
 	processInput();
+	clearButtons();
 	do {
 		pollInput();
 		g_vm->_renderer->selectCursor(CURSOR_FINGER);
@@ -4535,9 +4536,17 @@ again:;
 		break;
 	case 0xF000:
 		/*restore sp from keep_sp then run script*/
-		/*currently only supposed to work correctly from the SCR_4D_PriorityCommand handler*/
+		// A priority command (SCR_4D_PriorityCommand) discards the current
+		// callchain. The original restored the script stack pointer to the
+		// main-loop baseline (keep_sp); here that baseline is an empty stack.
+		// Without this, a priority command fired from inside a call'd
+		// subroutine breaks out via ScriptRerun before the matching ret runs,
+		// leaking a script_stack frame each time. In the endgame confrontation
+		// repeated PSI POWERS use overflows the 5-frame script_stack array,
+		// corrupting adjacent globals (sprite/rendering glitches) and script
+		// state (looping menu + forced game-over).
 		debug("Restore: $%X 0x%X", the_command, cmd);
-	/*TODO("SCR_RESTORE\n");*/
+		script_stack_ptr = script_stack;
 	/*fall through*/
 	default:
 		res = RunScript(getScriptSubroutine(cmd - 1));
@@ -4559,12 +4568,18 @@ again:;
 	if (g_vm->_shouldRestart)
 		return runCommandKeepSp();
 
-	if (g_vm->_prioritycommand_1 && !(g_vm->_prioritycommand_2))
+	// A priority command (SCR_4D_PriorityCommand) discards the entire current
+	// callchain and re-runs from the main-loop baseline. Propagate the pending
+	// flag straight up to the runCommandKeepSp anchor instead of re-entering
+	// here: re-entering at this (possibly deeply nested) level ran the priority
+	// script but left the outer ActionsMenu frames alive on the C stack. In the
+	// endgame confrontation each PSI POWERS use fired a priority command and
+	// nested one menu level deeper; after the winning flask the stack unwound
+	// only one level into a stale confrontation menu, which re-prompted and (the
+	// win state already consumed) forced a game-over.
+	if (g_vm->_prioritycommand_1)
 		return res;
 
-	if (g_vm->_prioritycommand_1 && g_vm->_prioritycommand_2)
-		return runCommandKeepSp();
-
 	/*TODO: this is pretty hacky, original code manipulates the stack to discard old script invocation*/
 	if (res == ScriptRerun)
 		goto again;
@@ -4574,11 +4589,21 @@ again:;
 
 uint16 runCommandKeepSp(void) {
 	/*keep_sp = sp;*/
-	g_vm->_prioritycommand_1 = false;
-	g_vm->_prioritycommand_2 = false;
-	if (g_vm->_shouldRestart)
-		return RUNCOMMAND_RESTART;
-	return runCommand();
+	// Anchor for priority commands. The original engine restored the stack
+	// pointer to this baseline (keep_sp) on every priority command, collapsing
+	// any nested script/menu callchain. Emulate that by draining every pending
+	// priority command here in a loop: nested frames propagate the flag up to
+	// this point (see runCommand) and we re-run from the baseline until none
+	// remain, so menu frames never accumulate on the call stack.
+	uint16 res = 0;
+	do {
+		g_vm->_prioritycommand_1 = false;
+		g_vm->_prioritycommand_2 = false;
+		if (g_vm->_shouldRestart)
+			return RUNCOMMAND_RESTART;
+		res = runCommand();
+	} while (g_vm->_prioritycommand_1);
+	return res;
 }
 
 } // End of namespace Chamber


Commit: 6ac56c5cf8bb76166b57087644b68b4781bc0876
    https://github.com/scummvm/scummvm/commit/6ac56c5cf8bb76166b57087644b68b4781bc0876
Author: Ion Andrei Cristian (lecturatul2017 at gmail.com)
Date: 2026-06-14T21:13:43+02:00

Commit Message:
CHAMBER: Make RNG seed overridable via random_seed config key

Changed paths:
    engines/chamber/kult.cpp


diff --git a/engines/chamber/kult.cpp b/engines/chamber/kult.cpp
index 084c28b39c8..58907294db0 100644
--- a/engines/chamber/kult.cpp
+++ b/engines/chamber/kult.cpp
@@ -19,6 +19,7 @@
  *
  */
 
+#include "common/config-manager.h"
 #include "common/error.h"
 #include "common/system.h"
 #include "engines/advancedDetector.h"
@@ -113,7 +114,10 @@ uint16 benchmarkCpu(void) {
 void randomize(void) {
 	// Original read the low byte of the BIOS timer-tick count (int 0x1A) into
 	// rand_seed. Use the host millisecond timer as an equivalent entropy source.
-	rand_seed = (byte)(g_system->getMillis());
+	if (ConfMan.hasKey("random_seed"))
+		rand_seed = (byte)ConfMan.getInt("random_seed");
+	else
+		rand_seed = (byte)(g_system->getMillis());
 	getRand();
 }
 



From noreply at scummvm.org  Sun Jun 14 19:59:33 2026
From: noreply at scummvm.org (sev-)
Date: Sun, 14 Jun 2026 19:59:33 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 da99ff1efdf80d4a564ea3efca10855f325dccfd
Message-ID: <20260614195933.091D97005CC@ron.scummvm.net>

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

Summary:
84056c68ba SCUMM: Create base-costume-optimised.cpp
55c6fddccf SCUMM: Optimise only paintCelByleRLECommon & byleRLEDecode
4e469cf33d SCUMM: Introduce BaseCostumeRenderer::byleRLEDecodeFast
8b49d1b5a0 SCUMM: byleRLEDecodeFast: Don't calculate masking for transparent color
041c9eb7d6 SCUMM: byleRLEDecodeFast: Check for height == 0 every repLen pixels
546fa65d45 SCUMM: byleRLEDecodeFast: Remove linesToSkip kludge
c5e04bec37 SCUMM: byleRLEDecodeFast: Introduce 10 identical copies
27a71d8195 SCUMM: byleRLEDecodeFast: Remove dead code
f0011e4729 SCUMM: byleRLEDecodeFast: Optimise non-scaled modes
ad29b12637 SCUMM: byleRLEDecodeFast: Move Y clipping to caller
907150addb SCUMM: byleRLEDecodeFast: Optimise non-scaled modes even more
783339e94c SCUMM: byleRLEDecodeFast: Proper Y clipping in scaled modes
2390a071b9 SCUMM: byleRLEDecodeFast: Fast path for Mode1
4f5361a2f7 SCUMM: byleRLEDecodeFast: m68k assembly functions
16b6d2a0ae SCUMM: byleRLEDecodeFast: Implement HE>=90 Mode3
ff472311ac SCUMM: byleRLEDecodeFast: Prefer non-exported labels with .L
de32e66a2a SCUMM: byleRLEDecodeFast: Optimise lastColumnX code
fded2d4203 SCUMM: byleRLEDecodeFast: Introduce specialised Mode0&1 Y clipping functions
7a854a8bd7 SCUMM: Optimise BaseCostumeRenderer::skipCelLines
da99ff1efd SCUMM: Rewrite asmDrawStripToScreen into m68k assembly


Commit: 84056c68ba0a79adb875fd7ab3c7033c5a0a16ba
    https://github.com/scummvm/scummvm/commit/84056c68ba0a79adb875fd7ab3c7033c5a0a16ba
Author: Miro Kropacek (miro.kropacek at gmail.com)
Date: 2026-06-14T21:59:20+02:00

Commit Message:
SCUMM: Create base-costume-optimised.cpp

This is a duplicate of base-costume.cpp and is enabled by
SCUMM_OPTIMISED_CODE. Currently enabled only for m68k-atari-mint* builds
(Atari Lite, Atari Full, FireBee).

Changed paths:
  A engines/scumm/base-costume-optimised.cpp
    configure
    engines/scumm/base-costume.cpp
    engines/scumm/module.mk


diff --git a/configure b/configure
index b91d60bdb76..b454d2dcf41 100755
--- a/configure
+++ b/configure
@@ -3886,6 +3886,7 @@ if test -n "$_host"; then
 			#	# --disable-debug
 			#	append_var LDFLAGS "-s"
 			#fi
+			append_var DEFINES "-DSCUMM_OPTIMISED_CODE"
 
 			# auto -> no
 			if test "$_optimizations" = "yes"; then
diff --git a/engines/scumm/base-costume-optimised.cpp b/engines/scumm/base-costume-optimised.cpp
new file mode 100644
index 00000000000..17c19a38767
--- /dev/null
+++ b/engines/scumm/base-costume-optimised.cpp
@@ -0,0 +1,456 @@
+/* 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 "scumm/base-costume.h"
+#include "scumm/util.h"
+
+namespace Scumm {
+
+#ifdef SCUMM_OPTIMISED_CODE
+byte BaseCostumeRenderer::drawCostume(const VirtScreen &vs, int numStrips, const Actor *a, bool drawToBackBuf) {
+	int i;
+	byte result = 0;
+
+	_out = vs;
+	if (drawToBackBuf)
+		_out.setPixels(vs.getBackPixels(0, 0));
+	else
+		_out.setPixels(vs.getPixels(0, 0));
+
+	_actorX += _vm->_virtscr[kMainVirtScreen].xstart & 7;
+	_out.w = _out.pitch / _vm->_bytesPerPixel;
+	// We do not use getBasePtr here because the offset to pixels never used
+	// _vm->_bytesPerPixel, but it seems unclear why.
+	_out.setPixels((byte *)_out.getPixels() - (_vm->_virtscr[kMainVirtScreen].xstart & 7));
+
+	_numStrips = numStrips;
+
+	if (_vm->_game.version <= 1) {
+		_xMove = 0;
+		_yMove = 0;
+	} else if (_vm->_game.features & GF_OLD_BUNDLE) {
+		_xMove = -72;
+		_yMove = -100;
+	} else {
+		_xMove = _yMove = 0;
+	}
+	for (i = 0; i < 16; i++)
+		result |= drawLimb(a, i);
+	return result;
+}
+
+byte BaseCostumeRenderer::paintCelByleRLECommon(
+	int xMoveCur,
+	int yMoveCur,
+	int numColors,
+	int scaletableSize,
+	bool amiOrPcEngCost,
+	bool c64Cost,
+	ByleRLEData &compData,
+	bool &decode) {
+
+	bool actorIsScaled;
+	int i, j;
+	int linesToSkip = 0, startScaleIndexX, startScaleIndexY;
+	Common::Rect rect;
+	int step;
+	byte drawFlag = 1;
+
+	// Setup color decoding variables
+	if (numColors == 32) {
+		compData.mask = 7;
+		compData.shr = 3;
+	} else if (numColors == 64) {
+		compData.mask = 3;
+		compData.shr = 2;
+	} else {
+		compData.mask = 15;
+		compData.shr = 4;
+	}
+
+	actorIsScaled = (_scaleX != 0xFF) || (_scaleY != 0xFF);
+
+	compData.boundsRect.left = 0;
+	compData.boundsRect.top = 0;
+	compData.boundsRect.right = _out.w;
+	compData.boundsRect.bottom = _out.h;
+
+	if (actorIsScaled) {
+
+		/* Scale direction */
+		compData.scaleXStep = -1;
+		if (xMoveCur < 0) {
+			xMoveCur = -xMoveCur;
+			compData.scaleXStep = 1;
+		}
+
+		if (_drawActorToRight) {
+			/* Adjust X position */
+			startScaleIndexX = j = (scaletableSize - xMoveCur) & compData.scaleIndexMask;
+			for (i = 0; i < xMoveCur; i++) {
+				if (compData.scaleTable[j++ & compData.scaleIndexMask] < _scaleX)
+					compData.x -= compData.scaleXStep;
+			}
+
+			rect.left = rect.right = compData.x;
+
+			j = startScaleIndexX;
+			for (i = 0; i < _width; i++) {
+				if (rect.right < 0) {
+					linesToSkip++;
+					startScaleIndexX = j;
+				}
+				if (compData.scaleTable[j++ & compData.scaleIndexMask] < _scaleX)
+					rect.right++;
+			}
+		} else {
+			/* No mirror */
+			/* Adjust X position */
+			startScaleIndexX = j = (scaletableSize + xMoveCur) & compData.scaleIndexMask;
+			for (i = 0; i < xMoveCur; i++) {
+				if (compData.scaleTable[j-- & compData.scaleIndexMask] < _scaleX)
+					compData.x += compData.scaleXStep;
+			}
+
+			rect.left = rect.right = compData.x;
+
+			j = startScaleIndexX;
+			for (i = 0; i < _width; i++) {
+				if (rect.left >= compData.boundsRect.right) {
+					startScaleIndexX = j;
+					linesToSkip++;
+				}
+				if (compData.scaleTable[j-- & compData.scaleIndexMask] < _scaleX)
+					rect.left--;
+			}
+		}
+
+		if (linesToSkip)
+			linesToSkip--;
+
+		step = -1;
+		if (yMoveCur < 0) {
+			yMoveCur = -yMoveCur;
+			step = -step;
+		}
+
+		startScaleIndexY = j = (scaletableSize - yMoveCur) & compData.scaleIndexMask;
+		for (i = 0; i < yMoveCur; i++) {
+			if (compData.scaleTable[j++ & compData.scaleIndexMask] < _scaleY)
+				compData.y -= step;
+		}
+
+		rect.top = rect.bottom = compData.y;
+
+		j = startScaleIndexY;
+		for (i = 0; i < _height; i++) {
+			if (compData.scaleTable[j++ & compData.scaleIndexMask] < _scaleY)
+				rect.bottom++;
+		}
+	} else {
+		if (!_drawActorToRight)
+			xMoveCur = -xMoveCur;
+
+		compData.x += xMoveCur;
+		compData.y += yMoveCur;
+
+		if (_drawActorToRight) {
+			rect.left = compData.x;
+			rect.right = compData.x + _width;
+		} else {
+			rect.left = compData.x - _width;
+			rect.right = compData.x;
+		}
+
+		rect.top = compData.y;
+		rect.bottom = rect.top + _height;
+
+		startScaleIndexX = scaletableSize;
+		startScaleIndexY = scaletableSize;
+	}
+
+	compData.scaleXIndex = startScaleIndexX;
+	compData.scaleYIndex = startScaleIndexY;
+	compData.skipWidth = _width;
+	compData.scaleXStep = _drawActorToRight ? 1 : -1;
+
+	markAsDirty(rect, compData, decode);
+	if (!decode)
+		return 0;
+
+	if (rect.top >= compData.boundsRect.bottom || rect.bottom <= compData.boundsRect.top) {
+		decode = false;
+		return 0;
+	}
+
+	if (rect.left >= compData.boundsRect.right || rect.right <= compData.boundsRect.left) {
+		decode = false;
+		return 0;
+	}
+
+	compData.repLen = 0;
+
+	if (_drawActorToRight) {
+		if (!actorIsScaled)
+			linesToSkip = compData.boundsRect.left - compData.x;
+		if (linesToSkip > 0) {
+			if (!amiOrPcEngCost && !c64Cost) {
+				compData.skipWidth -= linesToSkip;
+				skipCelLines(compData, linesToSkip);
+				compData.x = compData.boundsRect.left;
+			}
+		} else {
+			linesToSkip = rect.right - compData.boundsRect.right;
+			if (linesToSkip <= 0) {
+				drawFlag = 2;
+			} else {
+				compData.skipWidth -= linesToSkip;
+			}
+		}
+	} else {
+		if (!actorIsScaled) {
+			if (_akosRendering)
+				linesToSkip = rect.right - compData.boundsRect.right + 1;
+			else
+				linesToSkip = rect.right - compData.boundsRect.right;
+		}
+		if (linesToSkip > 0) {
+			if (!amiOrPcEngCost && !c64Cost) {
+				compData.skipWidth -= linesToSkip;
+				skipCelLines(compData, linesToSkip);
+				compData.x = compData.boundsRect.right - 1;
+			}
+		} else {
+			// V1 games uses 8 x 8 pixels for actors
+			if (c64Cost)
+				linesToSkip = (compData.boundsRect.left - 8) - rect.left;
+			else
+				linesToSkip = (compData.boundsRect.left - 1) - rect.left;
+			if (linesToSkip <= 0)
+				drawFlag = 2;
+			else
+				compData.skipWidth -= linesToSkip;
+		}
+	}
+
+	if (compData.skipWidth <= 0) {
+		decode = false;
+		return 0;
+	}
+
+	if (rect.left < compData.boundsRect.left)
+		rect.left = compData.boundsRect.left;
+
+	if (rect.top < compData.boundsRect.top)
+		rect.top = compData.boundsRect.top;
+
+	if (rect.top > compData.boundsRect.bottom)
+		rect.top = compData.boundsRect.bottom;
+
+	if (rect.bottom > compData.boundsRect.bottom)
+		rect.bottom = compData.boundsRect.bottom;
+
+	if (_drawTop > rect.top)
+		_drawTop = rect.top;
+	if (_drawBottom < rect.bottom)
+		_drawBottom = rect.bottom;
+
+	if (!_akosRendering && (_height + rect.top >= 256)) {
+		decode = false;
+		return 2;
+	}
+
+	compData.destPtr = (byte *)_out.getBasePtr(compData.x, compData.y);
+
+	return drawFlag;
+}
+
+void BaseCostumeRenderer::byleRLEDecode(ByleRLEData &compData, int16 actorHitX, int16 actorHitY, bool *actorHitResult, const uint8 *xmap) {
+	const byte *src = _srcPtr;
+	byte *dst = compData.destPtr;
+
+	byte len = compData.repLen;
+	uint16 color = compData.repColor;
+
+	// reset every column
+	int lastColumnX = -1;
+	int y = compData.y;
+	uint16 height = _height;
+	int scaleIndexY = compData.scaleYIndex;
+
+	byte maskbit = revBitMask(compData.x & 7);
+	const byte *mask = compData.maskPtr + compData.x / 8;
+
+	if (len)
+		goto StartPos;
+
+	do {
+		len = *src++;
+		color = len >> compData.shr;
+		len &= compData.mask;
+		if (!len)
+			len = *src++;
+
+		do {
+			if (_scaleY == 255 || compData.scaleTable[scaleIndexY++ & compData.scaleIndexMask] < _scaleY) {
+				if (actorHitResult) {
+					if (color && y == actorHitY && compData.x == actorHitX) {
+						*actorHitResult = true;
+						return;
+					}
+				} else {
+					const bool masked = (y < compData.boundsRect.top || y >= compData.boundsRect.bottom)
+						|| (compData.x < compData.boundsRect.left || compData.x >= compData.boundsRect.right)
+						|| (compData.maskPtr && (*mask & maskbit));
+					bool skipColumn = false;
+
+					if (color && !masked) {
+						uint16 pcolor;
+
+						if (!_akosRendering) {
+							if (_shadowMode & 0x20) {
+								pcolor = _shadowTable[*dst];
+							} else {
+								pcolor = _palette[color];
+								if (pcolor == 13 && _shadowTable)
+									pcolor = _shadowTable[*dst];
+							}
+						} else {
+							pcolor = _palette[color];
+
+							if (_shadowMode == 1) {
+								if (pcolor == 13) {
+									// In shadow mode 1 skipColumn works more or less the same way as in shadow
+									// mode 3. It is only ever checked and applied if pcolor is 13.
+									skipColumn = (lastColumnX == compData.x);
+									pcolor = _shadowTable[*dst];
+								}
+							} else if (_shadowMode == 2) {
+								error("AkosRenderer::byleRLEDecode(): shadowMode 2 not implemented."); // TODO
+							} else if (_shadowMode == 3) {
+								if (_vm->_game.features & GF_16BIT_COLOR) {
+									// I add the column skip here, too, although I don't know whether it always
+									// applies. But this is the only way to prevent recursive shading of pixels.
+									// This might need more fine tuning...
+									skipColumn = (lastColumnX == compData.x);
+									uint16 srcColor = (pcolor >> 1) & 0x7DEF;
+									uint16 dstColor = (READ_UINT16(dst) >> 1) & 0x7DEF;
+									pcolor = srcColor + dstColor;
+								} else if (_vm->_game.heversion >= 90) {
+									// I add the column skip here, too, although I don't know whether it always
+									// applies. But this is the only way to prevent recursive shading of pixels.
+									// This might need more fine tuning...
+									skipColumn = (lastColumnX == compData.x);
+									pcolor = (pcolor << 8) + *dst;
+									pcolor = xmap[pcolor];
+								} else if (pcolor < 8) {
+									// This mode is used in COMI. The column skip only takes place when the shading
+									// is actually applied (for pcolor < 8). The skip avoids shading of pixels that
+									// already have been shaded.
+									skipColumn = (lastColumnX == compData.x);
+									pcolor = (pcolor << 8) + *dst;
+									pcolor = _shadowTable[pcolor];
+								}
+							}
+						}
+						if (!skipColumn) {
+							if (_vm->_bytesPerPixel == 2) {
+								WRITE_UINT16(dst, pcolor);
+							} else {
+								*dst = pcolor;
+							}
+						}
+					}
+				}
+				dst += _out.pitch;
+				mask += _numStrips;
+				y++;
+			}
+			if (!--height) {
+				if (!--compData.skipWidth)
+					return;
+				height = _height;
+				y = compData.y;
+
+				scaleIndexY = compData.scaleYIndex;
+				lastColumnX = compData.x;
+
+				if (_scaleX == 255 || compData.scaleTable[compData.scaleXIndex] < _scaleX) {
+					compData.x += compData.scaleXStep;
+					if (compData.x < compData.boundsRect.left || compData.x >= compData.boundsRect.right)
+						return;
+					maskbit = revBitMask(compData.x & 7);
+					compData.destPtr += compData.scaleXStep * _vm->_bytesPerPixel;
+				}
+
+				// From MONKEY1 EGA disasm: we only increment by 1.
+				// This accurately produces the original wonky scaling
+				// for the floppy editions of Monkey Island 1.
+				// Also valid for all other v4 games (this code is
+				// also in the executable for LOOM CD).
+				if (_vm->_game.version == 4) {
+					compData.scaleXIndex = (compData.scaleXIndex + 1) & compData.scaleIndexMask;
+				} else {
+					compData.scaleXIndex = (compData.scaleXIndex + compData.scaleXStep) & compData.scaleIndexMask;
+				}
+
+				dst = compData.destPtr;
+				mask = compData.maskPtr + compData.x / 8;
+			}
+		StartPos:;
+		} while (--len);
+	} while (true);
+}
+
+void BaseCostumeRenderer::skipCelLines(ByleRLEData &compData, int num) {
+	num *= _height;
+
+	do {
+		compData.repLen = *_srcPtr++;
+		compData.repColor = compData.repLen >> compData.shr;
+		compData.repLen &= compData.mask;
+
+		if (!compData.repLen)
+			compData.repLen = *_srcPtr++;
+
+		do {
+			if (!--num)
+				return;
+		} while (--compData.repLen);
+	} while (true);
+}
+
+bool ScummEngine::isCostumeInUse(int cost) const {
+	Actor *a;
+
+	if (_roomResource != 0)
+		for (int i = 1; i < _numActors; i++) {
+			a = derefActor(i);
+			if (a->isInCurrentRoom() && a->_costume == cost)
+				return true;
+		}
+
+	return false;
+}
+#endif
+
+} // End of namespace Scumm
diff --git a/engines/scumm/base-costume.cpp b/engines/scumm/base-costume.cpp
index e4106fa343d..9f47d3fc864 100644
--- a/engines/scumm/base-costume.cpp
+++ b/engines/scumm/base-costume.cpp
@@ -25,6 +25,7 @@
 
 namespace Scumm {
 
+#ifndef SCUMM_OPTIMISED_CODE
 byte BaseCostumeRenderer::drawCostume(const VirtScreen &vs, int numStrips, const Actor *a, bool drawToBackBuf) {
 	int i;
 	byte result = 0;
@@ -450,5 +451,6 @@ bool ScummEngine::isCostumeInUse(int cost) const {
 
 	return false;
 }
+#endif
 
 } // End of namespace Scumm
diff --git a/engines/scumm/module.mk b/engines/scumm/module.mk
index c85694c0f0b..819efda0e67 100644
--- a/engines/scumm/module.mk
+++ b/engines/scumm/module.mk
@@ -4,6 +4,7 @@ MODULE_OBJS := \
 	actor.o \
 	akos.o \
 	base-costume.o \
+	base-costume-optimised.o \
 	bomp.o \
 	boxes.o \
 	camera.o \


Commit: 55c6fddccf2be886c33ac7de68ef6856aee48dbf
    https://github.com/scummvm/scummvm/commit/55c6fddccf2be886c33ac7de68ef6856aee48dbf
Author: Miro Kropacek (miro.kropacek at gmail.com)
Date: 2026-06-14T21:59:20+02:00

Commit Message:
SCUMM: Optimise only paintCelByleRLECommon & byleRLEDecode

Other functions are shared with base-costume.cpp.

Changed paths:
    engines/scumm/base-costume-optimised.cpp
    engines/scumm/base-costume.cpp


diff --git a/engines/scumm/base-costume-optimised.cpp b/engines/scumm/base-costume-optimised.cpp
index 17c19a38767..e1567f33331 100644
--- a/engines/scumm/base-costume-optimised.cpp
+++ b/engines/scumm/base-costume-optimised.cpp
@@ -26,38 +26,6 @@
 namespace Scumm {
 
 #ifdef SCUMM_OPTIMISED_CODE
-byte BaseCostumeRenderer::drawCostume(const VirtScreen &vs, int numStrips, const Actor *a, bool drawToBackBuf) {
-	int i;
-	byte result = 0;
-
-	_out = vs;
-	if (drawToBackBuf)
-		_out.setPixels(vs.getBackPixels(0, 0));
-	else
-		_out.setPixels(vs.getPixels(0, 0));
-
-	_actorX += _vm->_virtscr[kMainVirtScreen].xstart & 7;
-	_out.w = _out.pitch / _vm->_bytesPerPixel;
-	// We do not use getBasePtr here because the offset to pixels never used
-	// _vm->_bytesPerPixel, but it seems unclear why.
-	_out.setPixels((byte *)_out.getPixels() - (_vm->_virtscr[kMainVirtScreen].xstart & 7));
-
-	_numStrips = numStrips;
-
-	if (_vm->_game.version <= 1) {
-		_xMove = 0;
-		_yMove = 0;
-	} else if (_vm->_game.features & GF_OLD_BUNDLE) {
-		_xMove = -72;
-		_yMove = -100;
-	} else {
-		_xMove = _yMove = 0;
-	}
-	for (i = 0; i < 16; i++)
-		result |= drawLimb(a, i);
-	return result;
-}
-
 byte BaseCostumeRenderer::paintCelByleRLECommon(
 	int xMoveCur,
 	int yMoveCur,
@@ -420,37 +388,6 @@ void BaseCostumeRenderer::byleRLEDecode(ByleRLEData &compData, int16 actorHitX,
 		} while (--len);
 	} while (true);
 }
-
-void BaseCostumeRenderer::skipCelLines(ByleRLEData &compData, int num) {
-	num *= _height;
-
-	do {
-		compData.repLen = *_srcPtr++;
-		compData.repColor = compData.repLen >> compData.shr;
-		compData.repLen &= compData.mask;
-
-		if (!compData.repLen)
-			compData.repLen = *_srcPtr++;
-
-		do {
-			if (!--num)
-				return;
-		} while (--compData.repLen);
-	} while (true);
-}
-
-bool ScummEngine::isCostumeInUse(int cost) const {
-	Actor *a;
-
-	if (_roomResource != 0)
-		for (int i = 1; i < _numActors; i++) {
-			a = derefActor(i);
-			if (a->isInCurrentRoom() && a->_costume == cost)
-				return true;
-		}
-
-	return false;
-}
 #endif
 
 } // End of namespace Scumm
diff --git a/engines/scumm/base-costume.cpp b/engines/scumm/base-costume.cpp
index 9f47d3fc864..a43f2d0302b 100644
--- a/engines/scumm/base-costume.cpp
+++ b/engines/scumm/base-costume.cpp
@@ -25,7 +25,6 @@
 
 namespace Scumm {
 
-#ifndef SCUMM_OPTIMISED_CODE
 byte BaseCostumeRenderer::drawCostume(const VirtScreen &vs, int numStrips, const Actor *a, bool drawToBackBuf) {
 	int i;
 	byte result = 0;
@@ -58,6 +57,7 @@ byte BaseCostumeRenderer::drawCostume(const VirtScreen &vs, int numStrips, const
 	return result;
 }
 
+#ifndef SCUMM_OPTIMISED_CODE
 byte BaseCostumeRenderer::paintCelByleRLECommon(
 	int xMoveCur,
 	int yMoveCur,
@@ -420,6 +420,7 @@ void BaseCostumeRenderer::byleRLEDecode(ByleRLEData &compData, int16 actorHitX,
 		} while (--len);
 	} while (true);
 }
+#endif
 
 void BaseCostumeRenderer::skipCelLines(ByleRLEData &compData, int num) {
 	num *= _height;
@@ -451,6 +452,5 @@ bool ScummEngine::isCostumeInUse(int cost) const {
 
 	return false;
 }
-#endif
 
 } // End of namespace Scumm


Commit: 4e469cf33d812b368d459eb8b629f79d4ed1df95
    https://github.com/scummvm/scummvm/commit/4e469cf33d812b368d459eb8b629f79d4ed1df95
Author: Miro Kropacek (miro.kropacek at gmail.com)
Date: 2026-06-14T21:59:20+02:00

Commit Message:
SCUMM: Introduce BaseCostumeRenderer::byleRLEDecodeFast

Use this code path if:

- _vm->_bytesPerPixel == 1

- (!_akosRendering || _shadowMode != 3 || _vm->_game.heversion < 90) (to be implemented later)

- !(_vm->_game.features & GF_16BIT_COLOR)

- actorHitResult == NULL

- compData.maskPtr != NULL

Otherwise use original BaseCostumeRenderer::byleRLEDecode code.

The fast version also contains two functional changes:

- Monkey 1 EGA uses proper scaling

- double-shadowing is not done in the classic renderer

Changed paths:
    engines/scumm/base-costume-optimised.cpp
    engines/scumm/base-costume.cpp
    engines/scumm/base-costume.h


diff --git a/engines/scumm/base-costume-optimised.cpp b/engines/scumm/base-costume-optimised.cpp
index e1567f33331..c268fd0d326 100644
--- a/engines/scumm/base-costume-optimised.cpp
+++ b/engines/scumm/base-costume-optimised.cpp
@@ -252,7 +252,14 @@ byte BaseCostumeRenderer::paintCelByleRLECommon(
 	return drawFlag;
 }
 
-void BaseCostumeRenderer::byleRLEDecode(ByleRLEData &compData, int16 actorHitX, int16 actorHitY, bool *actorHitResult, const uint8 *xmap) {
+enum class ShadowMode : int {
+	Mode0,
+	Mode1,
+	Mode3,
+	Classic
+};
+
+void BaseCostumeRenderer::byleRLEDecodeFast(ByleRLEData &compData) {
 	const byte *src = _srcPtr;
 	byte *dst = compData.destPtr;
 
@@ -268,6 +275,19 @@ void BaseCostumeRenderer::byleRLEDecode(ByleRLEData &compData, int16 actorHitX,
 	byte maskbit = revBitMask(compData.x & 7);
 	const byte *mask = compData.maskPtr + compData.x / 8;
 
+	ShadowMode shadowMode = ShadowMode::Mode0;
+	if (!_akosRendering) {
+		if (_shadowMode & 0x20)
+			shadowMode = ShadowMode::Classic;
+		else
+			shadowMode = ShadowMode::Mode1;
+	} else {
+		if (_shadowMode == 1)
+			shadowMode = ShadowMode::Mode1;
+		else if (_shadowMode == 3)
+			shadowMode = ShadowMode::Mode3;
+	}
+
 	if (len)
 		goto StartPos;
 
@@ -280,73 +300,43 @@ void BaseCostumeRenderer::byleRLEDecode(ByleRLEData &compData, int16 actorHitX,
 
 		do {
 			if (_scaleY == 255 || compData.scaleTable[scaleIndexY++ & compData.scaleIndexMask] < _scaleY) {
-				if (actorHitResult) {
-					if (color && y == actorHitY && compData.x == actorHitX) {
-						*actorHitResult = true;
-						return;
-					}
-				} else {
-					const bool masked = (y < compData.boundsRect.top || y >= compData.boundsRect.bottom)
-						|| (compData.x < compData.boundsRect.left || compData.x >= compData.boundsRect.right)
-						|| (compData.maskPtr && (*mask & maskbit));
-					bool skipColumn = false;
-
-					if (color && !masked) {
-						uint16 pcolor;
-
-						if (!_akosRendering) {
-							if (_shadowMode & 0x20) {
-								pcolor = _shadowTable[*dst];
-							} else {
-								pcolor = _palette[color];
-								if (pcolor == 13 && _shadowTable)
-									pcolor = _shadowTable[*dst];
-							}
+				const bool masked = (y < compData.boundsRect.top || y >= compData.boundsRect.bottom)
+				|| (compData.x < compData.boundsRect.left || compData.x >= compData.boundsRect.right)
+					|| (*mask & maskbit);
+				if (color && !masked) {
+					uint16 pcolor;
+
+					switch(shadowMode) {
+					case ShadowMode::Mode0:
+						*dst = _palette[color];
+						break;
+
+					case ShadowMode::Classic:
+						if (lastColumnX != compData.x)
+							*dst = _shadowTable[*dst];
+						break;
+
+					case ShadowMode::Mode1:
+						pcolor = _palette[color];
+						if (pcolor == 13 && _shadowTable) {
+							if (lastColumnX != compData.x)
+								*dst = _shadowTable[*dst];
 						} else {
-							pcolor = _palette[color];
-
-							if (_shadowMode == 1) {
-								if (pcolor == 13) {
-									// In shadow mode 1 skipColumn works more or less the same way as in shadow
-									// mode 3. It is only ever checked and applied if pcolor is 13.
-									skipColumn = (lastColumnX == compData.x);
-									pcolor = _shadowTable[*dst];
-								}
-							} else if (_shadowMode == 2) {
-								error("AkosRenderer::byleRLEDecode(): shadowMode 2 not implemented."); // TODO
-							} else if (_shadowMode == 3) {
-								if (_vm->_game.features & GF_16BIT_COLOR) {
-									// I add the column skip here, too, although I don't know whether it always
-									// applies. But this is the only way to prevent recursive shading of pixels.
-									// This might need more fine tuning...
-									skipColumn = (lastColumnX == compData.x);
-									uint16 srcColor = (pcolor >> 1) & 0x7DEF;
-									uint16 dstColor = (READ_UINT16(dst) >> 1) & 0x7DEF;
-									pcolor = srcColor + dstColor;
-								} else if (_vm->_game.heversion >= 90) {
-									// I add the column skip here, too, although I don't know whether it always
-									// applies. But this is the only way to prevent recursive shading of pixels.
-									// This might need more fine tuning...
-									skipColumn = (lastColumnX == compData.x);
-									pcolor = (pcolor << 8) + *dst;
-									pcolor = xmap[pcolor];
-								} else if (pcolor < 8) {
-									// This mode is used in COMI. The column skip only takes place when the shading
-									// is actually applied (for pcolor < 8). The skip avoids shading of pixels that
-									// already have been shaded.
-									skipColumn = (lastColumnX == compData.x);
-									pcolor = (pcolor << 8) + *dst;
-									pcolor = _shadowTable[pcolor];
-								}
-							}
+							*dst = pcolor;
 						}
-						if (!skipColumn) {
-							if (_vm->_bytesPerPixel == 2) {
-								WRITE_UINT16(dst, pcolor);
-							} else {
-								*dst = pcolor;
+						break;
+
+					case ShadowMode::Mode3:
+						pcolor = _palette[color];
+						if (pcolor < 8) {
+							if (lastColumnX != compData.x) {
+								pcolor = (pcolor << 8) + *dst;
+								*dst = _shadowTable[pcolor];
 							}
+						} else {
+							*dst = pcolor;
 						}
+						break;
 					}
 				}
 				dst += _out.pitch;
@@ -367,19 +357,10 @@ void BaseCostumeRenderer::byleRLEDecode(ByleRLEData &compData, int16 actorHitX,
 					if (compData.x < compData.boundsRect.left || compData.x >= compData.boundsRect.right)
 						return;
 					maskbit = revBitMask(compData.x & 7);
-					compData.destPtr += compData.scaleXStep * _vm->_bytesPerPixel;
+					compData.destPtr += compData.scaleXStep;
 				}
 
-				// From MONKEY1 EGA disasm: we only increment by 1.
-				// This accurately produces the original wonky scaling
-				// for the floppy editions of Monkey Island 1.
-				// Also valid for all other v4 games (this code is
-				// also in the executable for LOOM CD).
-				if (_vm->_game.version == 4) {
-					compData.scaleXIndex = (compData.scaleXIndex + 1) & compData.scaleIndexMask;
-				} else {
-					compData.scaleXIndex = (compData.scaleXIndex + compData.scaleXStep) & compData.scaleIndexMask;
-				}
+				compData.scaleXIndex = (compData.scaleXIndex + compData.scaleXStep) & compData.scaleIndexMask;
 
 				dst = compData.destPtr;
 				mask = compData.maskPtr + compData.x / 8;
diff --git a/engines/scumm/base-costume.cpp b/engines/scumm/base-costume.cpp
index a43f2d0302b..cfe12d5a481 100644
--- a/engines/scumm/base-costume.cpp
+++ b/engines/scumm/base-costume.cpp
@@ -283,8 +283,18 @@ byte BaseCostumeRenderer::paintCelByleRLECommon(
 
 	return drawFlag;
 }
+#endif
 
 void BaseCostumeRenderer::byleRLEDecode(ByleRLEData &compData, int16 actorHitX, int16 actorHitY, bool *actorHitResult, const uint8 *xmap) {
+#ifdef SCUMM_OPTIMISED_CODE
+	if ((_vm->_bytesPerPixel == 1) &&
+		(!_akosRendering || _shadowMode != 3 || (!(_vm->_game.features & GF_16BIT_COLOR) && _vm->_game.heversion < 90)) &&
+		(actorHitResult == NULL) &&
+		(compData.maskPtr != NULL)) {
+		byleRLEDecodeFast(compData);
+		return;
+	}
+#endif
 	const byte *src = _srcPtr;
 	byte *dst = compData.destPtr;
 
@@ -420,7 +430,6 @@ void BaseCostumeRenderer::byleRLEDecode(ByleRLEData &compData, int16 actorHitX,
 		} while (--len);
 	} while (true);
 }
-#endif
 
 void BaseCostumeRenderer::skipCelLines(ByleRLEData &compData, int num) {
 	num *= _height;
diff --git a/engines/scumm/base-costume.h b/engines/scumm/base-costume.h
index e568c075d45..9ffa861c5af 100644
--- a/engines/scumm/base-costume.h
+++ b/engines/scumm/base-costume.h
@@ -173,7 +173,9 @@ protected:
 		bool &decode);
 
 	void byleRLEDecode(ByleRLEData &compData, int16 actorHitX = 0, int16 actorHitY = 0, bool *actorHitResult = nullptr, const uint8 *xmap = nullptr);
-
+#ifdef SCUMM_OPTIMISED_CODE
+	void byleRLEDecodeFast(ByleRLEData &compData);
+#endif
 	void skipCelLines(ByleRLEData &compData, int num);
 
 private:


Commit: 8b49d1b5a03177eeeac2de43830b8cfd220f2be0
    https://github.com/scummvm/scummvm/commit/8b49d1b5a03177eeeac2de43830b8cfd220f2be0
Author: Miro Kropacek (miro.kropacek at gmail.com)
Date: 2026-06-14T21:59:20+02:00

Commit Message:
SCUMM: byleRLEDecodeFast: Don't calculate masking for transparent color

Changed paths:
    engines/scumm/base-costume-optimised.cpp


diff --git a/engines/scumm/base-costume-optimised.cpp b/engines/scumm/base-costume-optimised.cpp
index c268fd0d326..422582777e1 100644
--- a/engines/scumm/base-costume-optimised.cpp
+++ b/engines/scumm/base-costume-optimised.cpp
@@ -300,51 +300,54 @@ void BaseCostumeRenderer::byleRLEDecodeFast(ByleRLEData &compData) {
 
 		do {
 			if (_scaleY == 255 || compData.scaleTable[scaleIndexY++ & compData.scaleIndexMask] < _scaleY) {
-				const bool masked = (y < compData.boundsRect.top || y >= compData.boundsRect.bottom)
-				|| (compData.x < compData.boundsRect.left || compData.x >= compData.boundsRect.right)
-					|| (*mask & maskbit);
-				if (color && !masked) {
-					uint16 pcolor;
-
-					switch(shadowMode) {
-					case ShadowMode::Mode0:
-						*dst = _palette[color];
-						break;
-
-					case ShadowMode::Classic:
-						if (lastColumnX != compData.x)
-							*dst = _shadowTable[*dst];
-						break;
-
-					case ShadowMode::Mode1:
-						pcolor = _palette[color];
-						if (pcolor == 13 && _shadowTable) {
+				if (color) {
+					const bool masked = (y < compData.boundsRect.top || y >= compData.boundsRect.bottom)
+					|| (compData.x < compData.boundsRect.left || compData.x >= compData.boundsRect.right)
+						|| (*mask & maskbit);
+
+					if (!masked) {
+						uint16 pcolor;
+
+						switch(shadowMode) {
+						case ShadowMode::Mode0:
+							*dst = _palette[color];
+							break;
+
+						case ShadowMode::Classic:
 							if (lastColumnX != compData.x)
 								*dst = _shadowTable[*dst];
-						} else {
-							*dst = pcolor;
-						}
-						break;
-
-					case ShadowMode::Mode3:
-						pcolor = _palette[color];
-						if (pcolor < 8) {
-							if (lastColumnX != compData.x) {
-								pcolor = (pcolor << 8) + *dst;
-								*dst = _shadowTable[pcolor];
+							break;
+
+						case ShadowMode::Mode1:
+							pcolor = _palette[color];
+							if (pcolor == 13 && _shadowTable) {
+								if (lastColumnX != compData.x)
+									*dst = _shadowTable[*dst];
+							} else {
+								*dst = pcolor;
+							}
+							break;
+
+						case ShadowMode::Mode3:
+							pcolor = _palette[color];
+							if (pcolor < 8) {
+								if (lastColumnX != compData.x) {
+									pcolor = (pcolor << 8) + *dst;
+									*dst = _shadowTable[pcolor];
+								}
+							} else {
+								*dst = pcolor;
 							}
-						} else {
-							*dst = pcolor;
+							break;
 						}
-						break;
 					}
 				}
 				dst += _out.pitch;
 				mask += _numStrips;
 				y++;
 			}
-			if (!--height) {
-				if (!--compData.skipWidth)
+			if (--height == 0) {
+				if (--compData.skipWidth == 0)
 					return;
 				height = _height;
 				y = compData.y;


Commit: 041c9eb7d616fff8680d5efcb633195cc1068d6d
    https://github.com/scummvm/scummvm/commit/041c9eb7d616fff8680d5efcb633195cc1068d6d
Author: Miro Kropacek (miro.kropacek at gmail.com)
Date: 2026-06-14T21:59:20+02:00

Commit Message:
SCUMM: byleRLEDecodeFast: Check for height == 0 every repLen pixels

Instead of every pixel.

Changed paths:
    engines/scumm/base-costume-optimised.cpp


diff --git a/engines/scumm/base-costume-optimised.cpp b/engines/scumm/base-costume-optimised.cpp
index 422582777e1..80be54069b9 100644
--- a/engines/scumm/base-costume-optimised.cpp
+++ b/engines/scumm/base-costume-optimised.cpp
@@ -288,8 +288,11 @@ void BaseCostumeRenderer::byleRLEDecodeFast(ByleRLEData &compData) {
 			shadowMode = ShadowMode::Mode3;
 	}
 
-	if (len)
+	byte batch;
+	if (len) {
+		--len;
 		goto StartPos;
+	}
 
 	do {
 		len = *src++;
@@ -299,54 +302,61 @@ void BaseCostumeRenderer::byleRLEDecodeFast(ByleRLEData &compData) {
 			len = *src++;
 
 		do {
-			if (_scaleY == 255 || compData.scaleTable[scaleIndexY++ & compData.scaleIndexMask] < _scaleY) {
-				if (color) {
-					const bool masked = (y < compData.boundsRect.top || y >= compData.boundsRect.bottom)
-					|| (compData.x < compData.boundsRect.left || compData.x >= compData.boundsRect.right)
-						|| (*mask & maskbit);
-
-					if (!masked) {
-						uint16 pcolor;
-
-						switch(shadowMode) {
-						case ShadowMode::Mode0:
-							*dst = _palette[color];
-							break;
-
-						case ShadowMode::Classic:
-							if (lastColumnX != compData.x)
-								*dst = _shadowTable[*dst];
-							break;
-
-						case ShadowMode::Mode1:
-							pcolor = _palette[color];
-							if (pcolor == 13 && _shadowTable) {
+			batch = height < len ? (byte)height : len;
+			len -= batch;
+			height -= batch;
+
+			do {
+				if (_scaleY == 255 || compData.scaleTable[scaleIndexY++ & compData.scaleIndexMask] < _scaleY) {
+					if (color) {
+						const bool masked = (y < compData.boundsRect.top || y >= compData.boundsRect.bottom)
+						|| (compData.x < compData.boundsRect.left || compData.x >= compData.boundsRect.right)
+							|| (*mask & maskbit);
+
+						if (!masked) {
+							uint16 pcolor;
+
+							switch(shadowMode) {
+							case ShadowMode::Mode0:
+								*dst = _palette[color];
+								break;
+
+							case ShadowMode::Classic:
 								if (lastColumnX != compData.x)
 									*dst = _shadowTable[*dst];
-							} else {
-								*dst = pcolor;
-							}
-							break;
-
-						case ShadowMode::Mode3:
-							pcolor = _palette[color];
-							if (pcolor < 8) {
-								if (lastColumnX != compData.x) {
-									pcolor = (pcolor << 8) + *dst;
-									*dst = _shadowTable[pcolor];
+								break;
+
+							case ShadowMode::Mode1:
+								pcolor = _palette[color];
+								if (pcolor == 13 && _shadowTable) {
+									if (lastColumnX != compData.x)
+										*dst = _shadowTable[*dst];
+								} else {
+									*dst = pcolor;
+								}
+								break;
+
+							case ShadowMode::Mode3:
+								pcolor = _palette[color];
+								if (pcolor < 8) {
+									if (lastColumnX != compData.x) {
+										pcolor = (pcolor << 8) + *dst;
+										*dst = _shadowTable[pcolor];
+									}
+								} else {
+									*dst = pcolor;
 								}
-							} else {
-								*dst = pcolor;
+								break;
 							}
-							break;
 						}
 					}
+					dst += _out.pitch;
+					mask += _numStrips;
+					y++;
 				}
-				dst += _out.pitch;
-				mask += _numStrips;
-				y++;
-			}
-			if (--height == 0) {
+			} while (--batch);
+
+			if (height == 0) {
 				if (--compData.skipWidth == 0)
 					return;
 				height = _height;
@@ -369,7 +379,7 @@ void BaseCostumeRenderer::byleRLEDecodeFast(ByleRLEData &compData) {
 				mask = compData.maskPtr + compData.x / 8;
 			}
 		StartPos:;
-		} while (--len);
+		} while (len > 0);
 	} while (true);
 }
 #endif


Commit: 546fa65d4549f62ecdf9debca145717f29e1e81a
    https://github.com/scummvm/scummvm/commit/546fa65d4549f62ecdf9debca145717f29e1e81a
Author: Miro Kropacek (miro.kropacek at gmail.com)
Date: 2026-06-14T21:59:20+02:00

Commit Message:
SCUMM: byleRLEDecodeFast: Remove linesToSkip kludge

Original SCUMM code had a bug in startScaleIndexX calculation in the
scaled mode: in the last loop's iteration, it wasn't updated to the
final value but kept lowered by 1.

The linesToSkip-- kludge was implemented in SCUMM to work around but it
introduced another bug, x could become < 0 when entering byleRLEDecode
(which was addressed in ScummVM by commit 863bed3f to fix
https://bugs.scummvm.org/ticket/2700).

With the kludge out of the way, the initial xMasked is not needed. Also,
clipping coordinates are now deterministic, so we got a clearer picture
of needed changes to the dirty rectangle.

IMPORTANT: causes a visual change against original SCUMM. Actors on the
left and right edge are no more shifted by one column, i.e. the
correct(ed) code draws one columns less on each edge. Visible in e.g.
Full Throttle's initial screen when the actor goes out and returns back
to the scene with the trash bin.

Changed paths:
    engines/scumm/base-costume-optimised.cpp


diff --git a/engines/scumm/base-costume-optimised.cpp b/engines/scumm/base-costume-optimised.cpp
index 80be54069b9..4ceae657b76 100644
--- a/engines/scumm/base-costume-optimised.cpp
+++ b/engines/scumm/base-costume-optimised.cpp
@@ -38,7 +38,7 @@ byte BaseCostumeRenderer::paintCelByleRLECommon(
 
 	bool actorIsScaled;
 	int i, j;
-	int linesToSkip = 0, startScaleIndexX, startScaleIndexY;
+	int linesToSkip = 0, trailingLinesToSkip = 0, startScaleIndexX, startScaleIndexY;
 	Common::Rect rect;
 	int step;
 	byte drawFlag = 1;
@@ -63,12 +63,11 @@ byte BaseCostumeRenderer::paintCelByleRLECommon(
 	compData.boundsRect.bottom = _out.h;
 
 	if (actorIsScaled) {
-
 		/* Scale direction */
 		compData.scaleXStep = -1;
 		if (xMoveCur < 0) {
 			xMoveCur = -xMoveCur;
-			compData.scaleXStep = 1;
+			compData.scaleXStep = -compData.scaleXStep;
 		}
 
 		if (_drawActorToRight) {
@@ -83,13 +82,15 @@ byte BaseCostumeRenderer::paintCelByleRLECommon(
 
 			j = startScaleIndexX;
 			for (i = 0; i < _width; i++) {
-				if (rect.right < 0) {
+				if (rect.right < compData.boundsRect.left) {
 					linesToSkip++;
-					startScaleIndexX = j;
+				} else if (rect.right >= compData.boundsRect.right) {
+					trailingLinesToSkip++;
 				}
 				if (compData.scaleTable[j++ & compData.scaleIndexMask] < _scaleX)
 					rect.right++;
 			}
+			startScaleIndexX += linesToSkip;
 		} else {
 			/* No mirror */
 			/* Adjust X position */
@@ -104,17 +105,16 @@ byte BaseCostumeRenderer::paintCelByleRLECommon(
 			j = startScaleIndexX;
 			for (i = 0; i < _width; i++) {
 				if (rect.left >= compData.boundsRect.right) {
-					startScaleIndexX = j;
 					linesToSkip++;
+				} else if (rect.left < compData.boundsRect.left) {
+					trailingLinesToSkip++;
 				}
 				if (compData.scaleTable[j-- & compData.scaleIndexMask] < _scaleX)
 					rect.left--;
 			}
+			startScaleIndexX -= linesToSkip;
 		}
 
-		if (linesToSkip)
-			linesToSkip--;
-
 		step = -1;
 		if (yMoveCur < 0) {
 			yMoveCur = -yMoveCur;
@@ -144,9 +144,18 @@ byte BaseCostumeRenderer::paintCelByleRLECommon(
 		if (_drawActorToRight) {
 			rect.left = compData.x;
 			rect.right = compData.x + _width;
+
+			linesToSkip = compData.boundsRect.left - compData.x;
+			trailingLinesToSkip = rect.right - compData.boundsRect.right;
 		} else {
 			rect.left = compData.x - _width;
 			rect.right = compData.x;
+
+			linesToSkip = rect.right - compData.boundsRect.right + 1;
+			if (c64Cost)
+				trailingLinesToSkip = (compData.boundsRect.left - 8) - rect.left;
+			else
+				trailingLinesToSkip = (compData.boundsRect.left - 1) - rect.left;
 		}
 
 		rect.top = compData.y;
@@ -161,7 +170,29 @@ byte BaseCostumeRenderer::paintCelByleRLECommon(
 	compData.skipWidth = _width;
 	compData.scaleXStep = _drawActorToRight ? 1 : -1;
 
-	markAsDirty(rect, compData, decode);
+	// All the important 'rect' values. scale sequence = 'compData.scaleTable[i] < _scaleX' result)
+	//
+	// rendering dir | scaled | scale sequence | drawn columns | dirty columns          | ideal fix
+	// --------------+--------+----------------+---------------+------------------------+----------------
+	// left-to-right | no     | N/A            | 10, 11, 12    | [10, 13) = 10, 11, 12  | none needed
+	// left-to-right | yes    | T, T, T        | 10, 11, 12    | [10, 13) = 10, 11, 12  | none needed
+	// left-to-right | yes    | T, F, T        | 10, 11        | [10, 12) = 10, 11      | none needed
+	// left-to-right | yes    | T, T, F        | 10, 11, 12    | [10, 12) = 10, 11      | right++
+	// right-to-left | no     | N/A            | 10,  9,  8    | [ 7, 10) =  9,  8,  7  | left++, right++
+	// right-to-left | yes    | T, T, T        | 10,  9,  8    | [ 7, 10) =  9,  8,  7  | left++, right++
+	// right-to-left | yes    | T, F, T        | 10,  9        | [ 8, 10) =  9,  8      | left++, right++
+	// right-to-left | yes    | T, T, F        | 10,  9,  8    | [ 8, 10) =  9,  8      | right++
+	//
+	// Considering how complex would be to handle all left/right adjustments precisely, go with what the old
+	// costume renderer did, just add +1 to the right. That sometimes extends the dirty rect by one or two
+	// columns but definitely fixes all edge cases with zero effort.
+	Common::Rect dirtyRect = rect;
+	if (_akosRendering) {
+		// ClassicCostumeRenderer::markAsDirty already does that
+		dirtyRect.right++;
+	}
+
+	markAsDirty(dirtyRect, compData, decode);
 	if (!decode)
 		return 0;
 
@@ -177,49 +208,22 @@ byte BaseCostumeRenderer::paintCelByleRLECommon(
 
 	compData.repLen = 0;
 
-	if (_drawActorToRight) {
-		if (!actorIsScaled)
-			linesToSkip = compData.boundsRect.left - compData.x;
-		if (linesToSkip > 0) {
-			if (!amiOrPcEngCost && !c64Cost) {
-				compData.skipWidth -= linesToSkip;
-				skipCelLines(compData, linesToSkip);
-				compData.x = compData.boundsRect.left;
-			}
-		} else {
-			linesToSkip = rect.right - compData.boundsRect.right;
-			if (linesToSkip <= 0) {
-				drawFlag = 2;
-			} else {
-				compData.skipWidth -= linesToSkip;
-			}
-		}
-	} else {
-		if (!actorIsScaled) {
-			if (_akosRendering)
-				linesToSkip = rect.right - compData.boundsRect.right + 1;
-			else
-				linesToSkip = rect.right - compData.boundsRect.right;
-		}
-		if (linesToSkip > 0) {
-			if (!amiOrPcEngCost && !c64Cost) {
-				compData.skipWidth -= linesToSkip;
-				skipCelLines(compData, linesToSkip);
-				compData.x = compData.boundsRect.right - 1;
-			}
-		} else {
-			// V1 games uses 8 x 8 pixels for actors
-			if (c64Cost)
-				linesToSkip = (compData.boundsRect.left - 8) - rect.left;
-			else
-				linesToSkip = (compData.boundsRect.left - 1) - rect.left;
-			if (linesToSkip <= 0)
-				drawFlag = 2;
-			else
-				compData.skipWidth -= linesToSkip;
+	if (linesToSkip > 0) {
+		if (!amiOrPcEngCost && !c64Cost) {
+			compData.skipWidth -= linesToSkip;
+			skipCelLines(compData, linesToSkip);
+			compData.x = _drawActorToRight ? compData.boundsRect.left : (compData.boundsRect.right - 1);
 		}
 	}
 
+	if (trailingLinesToSkip > 0) {
+		compData.skipWidth -= trailingLinesToSkip;
+	}
+
+	if (linesToSkip <= 0 && trailingLinesToSkip <= 0) {
+		drawFlag = 2;
+	}
+
 	if (compData.skipWidth <= 0) {
 		decode = false;
 		return 0;
@@ -306,11 +310,12 @@ void BaseCostumeRenderer::byleRLEDecodeFast(ByleRLEData &compData) {
 			len -= batch;
 			height -= batch;
 
+			assert(compData.x >= compData.boundsRect.left && compData.x < compData.boundsRect.right);
+
 			do {
 				if (_scaleY == 255 || compData.scaleTable[scaleIndexY++ & compData.scaleIndexMask] < _scaleY) {
 					if (color) {
 						const bool masked = (y < compData.boundsRect.top || y >= compData.boundsRect.bottom)
-						|| (compData.x < compData.boundsRect.left || compData.x >= compData.boundsRect.right)
 							|| (*mask & maskbit);
 
 						if (!masked) {
@@ -367,8 +372,6 @@ void BaseCostumeRenderer::byleRLEDecodeFast(ByleRLEData &compData) {
 
 				if (_scaleX == 255 || compData.scaleTable[compData.scaleXIndex] < _scaleX) {
 					compData.x += compData.scaleXStep;
-					if (compData.x < compData.boundsRect.left || compData.x >= compData.boundsRect.right)
-						return;
 					maskbit = revBitMask(compData.x & 7);
 					compData.destPtr += compData.scaleXStep;
 				}


Commit: c5e04bec37cdc2a0faf6e276b32648d097a8def1
    https://github.com/scummvm/scummvm/commit/c5e04bec37cdc2a0faf6e276b32648d097a8def1
Author: Miro Kropacek (miro.kropacek at gmail.com)
Date: 2026-06-14T21:59:20+02:00

Commit Message:
SCUMM: byleRLEDecodeFast: Introduce 10 identical copies

For easier tracking of what has changed.

Changed paths:
    engines/scumm/base-costume-optimised.cpp


diff --git a/engines/scumm/base-costume-optimised.cpp b/engines/scumm/base-costume-optimised.cpp
index 4ceae657b76..3317bdea9ce 100644
--- a/engines/scumm/base-costume-optimised.cpp
+++ b/engines/scumm/base-costume-optimised.cpp
@@ -263,7 +263,1127 @@ enum class ShadowMode : int {
 	Classic
 };
 
-void BaseCostumeRenderer::byleRLEDecodeFast(ByleRLEData &compData) {
+void ByleRLEDecode_Mode0(
+	BaseCostumeRenderer::ByleRLEData *pcompData,
+	const byte _scaleX,
+	const byte _scaleY,
+	const int _height,
+	const int pitch,
+	const int _numStrips,
+	const ShadowMode shadowMode,
+	const byte *_srcPtr,
+	const byte *_shadowTable,
+	const uint16 *_palette) {
+
+	BaseCostumeRenderer::ByleRLEData &compData = *pcompData;
+
+	const byte *src = _srcPtr;
+	byte *dst = compData.destPtr;
+
+	byte len = compData.repLen;
+	uint16 color = compData.repColor;
+
+	// reset every column
+	int lastColumnX = -1;
+	int y = compData.y;
+	uint16 height = _height;
+	int scaleIndexY = compData.scaleYIndex;
+
+	byte maskbit = revBitMask(compData.x & 7);
+	const byte *mask = compData.maskPtr + compData.x / 8;
+
+	byte batch;
+	if (len) {
+		--len;
+		goto StartPos;
+	}
+
+	do {
+		len = *src++;
+		color = len >> compData.shr;
+		len &= compData.mask;
+		if (!len)
+			len = *src++;
+
+		do {
+			batch = height < len ? (byte)height : len;
+			len -= batch;
+			height -= batch;
+
+			assert(compData.x >= compData.boundsRect.left && compData.x < compData.boundsRect.right);
+
+			do {
+				if (_scaleY == 255 || compData.scaleTable[scaleIndexY++ & compData.scaleIndexMask] < _scaleY) {
+					if (color) {
+						const bool masked = (y < compData.boundsRect.top || y >= compData.boundsRect.bottom)
+							|| (*mask & maskbit);
+
+						if (!masked) {
+							uint16 pcolor;
+
+							switch(shadowMode) {
+							case ShadowMode::Mode0:
+								*dst = _palette[color];
+								break;
+
+							case ShadowMode::Classic:
+								if (lastColumnX != compData.x)
+									*dst = _shadowTable[*dst];
+								break;
+
+							case ShadowMode::Mode1:
+								pcolor = _palette[color];
+								if (pcolor == 13 && _shadowTable) {
+									if (lastColumnX != compData.x)
+										*dst = _shadowTable[*dst];
+								} else {
+									*dst = pcolor;
+								}
+								break;
+
+							case ShadowMode::Mode3:
+								pcolor = _palette[color];
+								if (pcolor < 8) {
+									if (lastColumnX != compData.x) {
+										pcolor = (pcolor << 8) + *dst;
+										*dst = _shadowTable[pcolor];
+									}
+								} else {
+									*dst = pcolor;
+								}
+								break;
+							}
+						}
+					}
+					dst += pitch;
+					mask += _numStrips;
+					y++;
+				}
+			} while (--batch);
+
+			if (height == 0) {
+				if (--compData.skipWidth == 0)
+					return;
+				height = _height;
+				y = compData.y;
+
+				scaleIndexY = compData.scaleYIndex;
+				lastColumnX = compData.x;
+
+				if (_scaleX == 255 || compData.scaleTable[compData.scaleXIndex] < _scaleX) {
+					compData.x += compData.scaleXStep;
+					maskbit = revBitMask(compData.x & 7);
+					compData.destPtr += compData.scaleXStep;
+				}
+
+				compData.scaleXIndex = (compData.scaleXIndex + compData.scaleXStep) & compData.scaleIndexMask;
+
+				dst = compData.destPtr;
+				mask = compData.maskPtr + compData.x / 8;
+			}
+		StartPos:;
+		} while (len > 0);
+	} while (true);
+}
+
+void ByleRLEDecode_Mode1(
+	BaseCostumeRenderer::ByleRLEData *pcompData,
+	const byte _scaleX,
+	const byte _scaleY,
+	const int _height,
+	const int pitch,
+	const int _numStrips,
+	const ShadowMode shadowMode,
+	const byte *_srcPtr,
+	const byte *_shadowTable,
+	const uint16 *_palette) {
+
+	BaseCostumeRenderer::ByleRLEData &compData = *pcompData;
+
+	const byte *src = _srcPtr;
+	byte *dst = compData.destPtr;
+
+	byte len = compData.repLen;
+	uint16 color = compData.repColor;
+
+	// reset every column
+	int lastColumnX = -1;
+	int y = compData.y;
+	uint16 height = _height;
+	int scaleIndexY = compData.scaleYIndex;
+
+	byte maskbit = revBitMask(compData.x & 7);
+	const byte *mask = compData.maskPtr + compData.x / 8;
+
+	byte batch;
+	if (len) {
+		--len;
+		goto StartPos;
+	}
+
+	do {
+		len = *src++;
+		color = len >> compData.shr;
+		len &= compData.mask;
+		if (!len)
+			len = *src++;
+
+		do {
+			batch = height < len ? (byte)height : len;
+			len -= batch;
+			height -= batch;
+
+			assert(compData.x >= compData.boundsRect.left && compData.x < compData.boundsRect.right);
+
+			do {
+				if (_scaleY == 255 || compData.scaleTable[scaleIndexY++ & compData.scaleIndexMask] < _scaleY) {
+					if (color) {
+						const bool masked = (y < compData.boundsRect.top || y >= compData.boundsRect.bottom)
+							|| (*mask & maskbit);
+
+						if (!masked) {
+							uint16 pcolor;
+
+							switch(shadowMode) {
+							case ShadowMode::Mode0:
+								*dst = _palette[color];
+								break;
+
+							case ShadowMode::Classic:
+								if (lastColumnX != compData.x)
+									*dst = _shadowTable[*dst];
+								break;
+
+							case ShadowMode::Mode1:
+								pcolor = _palette[color];
+								if (pcolor == 13 && _shadowTable) {
+									if (lastColumnX != compData.x)
+										*dst = _shadowTable[*dst];
+								} else {
+									*dst = pcolor;
+								}
+								break;
+
+							case ShadowMode::Mode3:
+								pcolor = _palette[color];
+								if (pcolor < 8) {
+									if (lastColumnX != compData.x) {
+										pcolor = (pcolor << 8) + *dst;
+										*dst = _shadowTable[pcolor];
+									}
+								} else {
+									*dst = pcolor;
+								}
+								break;
+							}
+						}
+					}
+					dst += pitch;
+					mask += _numStrips;
+					y++;
+				}
+			} while (--batch);
+
+			if (height == 0) {
+				if (--compData.skipWidth == 0)
+					return;
+				height = _height;
+				y = compData.y;
+
+				scaleIndexY = compData.scaleYIndex;
+				lastColumnX = compData.x;
+
+				if (_scaleX == 255 || compData.scaleTable[compData.scaleXIndex] < _scaleX) {
+					compData.x += compData.scaleXStep;
+					maskbit = revBitMask(compData.x & 7);
+					compData.destPtr += compData.scaleXStep;
+				}
+
+				compData.scaleXIndex = (compData.scaleXIndex + compData.scaleXStep) & compData.scaleIndexMask;
+
+				dst = compData.destPtr;
+				mask = compData.maskPtr + compData.x / 8;
+			}
+		StartPos:;
+		} while (len > 0);
+	} while (true);
+}
+
+void ByleRLEDecode_Mode3(
+	BaseCostumeRenderer::ByleRLEData *pcompData,
+	const byte _scaleX,
+	const byte _scaleY,
+	const int _height,
+	const int pitch,
+	const int _numStrips,
+	const ShadowMode shadowMode,
+	const byte *_srcPtr,
+	const byte *_shadowTable,
+	const uint16 *_palette) {
+
+	BaseCostumeRenderer::ByleRLEData &compData = *pcompData;
+
+	const byte *src = _srcPtr;
+	byte *dst = compData.destPtr;
+
+	byte len = compData.repLen;
+	uint16 color = compData.repColor;
+
+	// reset every column
+	int lastColumnX = -1;
+	int y = compData.y;
+	uint16 height = _height;
+	int scaleIndexY = compData.scaleYIndex;
+
+	byte maskbit = revBitMask(compData.x & 7);
+	const byte *mask = compData.maskPtr + compData.x / 8;
+
+	byte batch;
+	if (len) {
+		--len;
+		goto StartPos;
+	}
+
+	do {
+		len = *src++;
+		color = len >> compData.shr;
+		len &= compData.mask;
+		if (!len)
+			len = *src++;
+
+		do {
+			batch = height < len ? (byte)height : len;
+			len -= batch;
+			height -= batch;
+
+			assert(compData.x >= compData.boundsRect.left && compData.x < compData.boundsRect.right);
+
+			do {
+				if (_scaleY == 255 || compData.scaleTable[scaleIndexY++ & compData.scaleIndexMask] < _scaleY) {
+					if (color) {
+						const bool masked = (y < compData.boundsRect.top || y >= compData.boundsRect.bottom)
+							|| (*mask & maskbit);
+
+						if (!masked) {
+							uint16 pcolor;
+
+							switch(shadowMode) {
+							case ShadowMode::Mode0:
+								*dst = _palette[color];
+								break;
+
+							case ShadowMode::Classic:
+								if (lastColumnX != compData.x)
+									*dst = _shadowTable[*dst];
+								break;
+
+							case ShadowMode::Mode1:
+								pcolor = _palette[color];
+								if (pcolor == 13 && _shadowTable) {
+									if (lastColumnX != compData.x)
+										*dst = _shadowTable[*dst];
+								} else {
+									*dst = pcolor;
+								}
+								break;
+
+							case ShadowMode::Mode3:
+								pcolor = _palette[color];
+								if (pcolor < 8) {
+									if (lastColumnX != compData.x) {
+										pcolor = (pcolor << 8) + *dst;
+										*dst = _shadowTable[pcolor];
+									}
+								} else {
+									*dst = pcolor;
+								}
+								break;
+							}
+						}
+					}
+					dst += pitch;
+					mask += _numStrips;
+					y++;
+				}
+			} while (--batch);
+
+			if (height == 0) {
+				if (--compData.skipWidth == 0)
+					return;
+				height = _height;
+				y = compData.y;
+
+				scaleIndexY = compData.scaleYIndex;
+				lastColumnX = compData.x;
+
+				if (_scaleX == 255 || compData.scaleTable[compData.scaleXIndex] < _scaleX) {
+					compData.x += compData.scaleXStep;
+					maskbit = revBitMask(compData.x & 7);
+					compData.destPtr += compData.scaleXStep;
+				}
+
+				compData.scaleXIndex = (compData.scaleXIndex + compData.scaleXStep) & compData.scaleIndexMask;
+
+				dst = compData.destPtr;
+				mask = compData.maskPtr + compData.x / 8;
+			}
+		StartPos:;
+		} while (len > 0);
+	} while (true);
+}
+
+void ByleRLEDecode_Classic(
+	BaseCostumeRenderer::ByleRLEData *pcompData,
+	const byte _scaleX,
+	const byte _scaleY,
+	const int _height,
+	const int pitch,
+	const int _numStrips,
+	const ShadowMode shadowMode,
+	const byte *_srcPtr,
+	const byte *_shadowTable,
+	const uint16 *_palette) {
+
+	BaseCostumeRenderer::ByleRLEData &compData = *pcompData;
+
+	const byte *src = _srcPtr;
+	byte *dst = compData.destPtr;
+
+	byte len = compData.repLen;
+	uint16 color = compData.repColor;
+
+	// reset every column
+	int lastColumnX = -1;
+	int y = compData.y;
+	uint16 height = _height;
+	int scaleIndexY = compData.scaleYIndex;
+
+	byte maskbit = revBitMask(compData.x & 7);
+	const byte *mask = compData.maskPtr + compData.x / 8;
+
+	byte batch;
+	if (len) {
+		--len;
+		goto StartPos;
+	}
+
+	do {
+		len = *src++;
+		color = len >> compData.shr;
+		len &= compData.mask;
+		if (!len)
+			len = *src++;
+
+		do {
+			batch = height < len ? (byte)height : len;
+			len -= batch;
+			height -= batch;
+
+			assert(compData.x >= compData.boundsRect.left && compData.x < compData.boundsRect.right);
+
+			do {
+				if (_scaleY == 255 || compData.scaleTable[scaleIndexY++ & compData.scaleIndexMask] < _scaleY) {
+					if (color) {
+						const bool masked = (y < compData.boundsRect.top || y >= compData.boundsRect.bottom)
+							|| (*mask & maskbit);
+
+						if (!masked) {
+							uint16 pcolor;
+
+							switch(shadowMode) {
+							case ShadowMode::Mode0:
+								*dst = _palette[color];
+								break;
+
+							case ShadowMode::Classic:
+								if (lastColumnX != compData.x)
+									*dst = _shadowTable[*dst];
+								break;
+
+							case ShadowMode::Mode1:
+								pcolor = _palette[color];
+								if (pcolor == 13 && _shadowTable) {
+									if (lastColumnX != compData.x)
+										*dst = _shadowTable[*dst];
+								} else {
+									*dst = pcolor;
+								}
+								break;
+
+							case ShadowMode::Mode3:
+								pcolor = _palette[color];
+								if (pcolor < 8) {
+									if (lastColumnX != compData.x) {
+										pcolor = (pcolor << 8) + *dst;
+										*dst = _shadowTable[pcolor];
+									}
+								} else {
+									*dst = pcolor;
+								}
+								break;
+							}
+						}
+					}
+					dst += pitch;
+					mask += _numStrips;
+					y++;
+				}
+			} while (--batch);
+
+			if (height == 0) {
+				if (--compData.skipWidth == 0)
+					return;
+				height = _height;
+				y = compData.y;
+
+				scaleIndexY = compData.scaleYIndex;
+				lastColumnX = compData.x;
+
+				if (_scaleX == 255 || compData.scaleTable[compData.scaleXIndex] < _scaleX) {
+					compData.x += compData.scaleXStep;
+					maskbit = revBitMask(compData.x & 7);
+					compData.destPtr += compData.scaleXStep;
+				}
+
+				compData.scaleXIndex = (compData.scaleXIndex + compData.scaleXStep) & compData.scaleIndexMask;
+
+				dst = compData.destPtr;
+				mask = compData.maskPtr + compData.x / 8;
+			}
+		StartPos:;
+		} while (len > 0);
+	} while (true);
+}
+
+void ByleRLEDecode_Scaled_Mode0(
+	BaseCostumeRenderer::ByleRLEData *pcompData,
+	const byte _scaleX,
+	const byte _scaleY,
+	const int _height,
+	const int pitch,
+	const int _numStrips,
+	const ShadowMode shadowMode,
+	const byte *_srcPtr,
+	const byte *_shadowTable,
+	const uint16 *_palette) {
+
+	BaseCostumeRenderer::ByleRLEData &compData = *pcompData;
+
+	const byte *src = _srcPtr;
+	byte *dst = compData.destPtr;
+
+	byte len = compData.repLen;
+	uint16 color = compData.repColor;
+
+	// reset every column
+	int lastColumnX = -1;
+	int y = compData.y;
+	uint16 height = _height;
+	int scaleIndexY = compData.scaleYIndex;
+
+	byte maskbit = revBitMask(compData.x & 7);
+	const byte *mask = compData.maskPtr + compData.x / 8;
+
+	byte batch;
+	if (len) {
+		--len;
+		goto StartPos;
+	}
+
+	do {
+		len = *src++;
+		color = len >> compData.shr;
+		len &= compData.mask;
+		if (!len)
+			len = *src++;
+
+		do {
+			batch = height < len ? (byte)height : len;
+			len -= batch;
+			height -= batch;
+
+			assert(compData.x >= compData.boundsRect.left && compData.x < compData.boundsRect.right);
+
+			do {
+				if (_scaleY == 255 || compData.scaleTable[scaleIndexY++ & compData.scaleIndexMask] < _scaleY) {
+					if (color) {
+						const bool masked = (y < compData.boundsRect.top || y >= compData.boundsRect.bottom)
+							|| (*mask & maskbit);
+
+						if (!masked) {
+							uint16 pcolor;
+
+							switch(shadowMode) {
+							case ShadowMode::Mode0:
+								*dst = _palette[color];
+								break;
+
+							case ShadowMode::Classic:
+								if (lastColumnX != compData.x)
+									*dst = _shadowTable[*dst];
+								break;
+
+							case ShadowMode::Mode1:
+								pcolor = _palette[color];
+								if (pcolor == 13 && _shadowTable) {
+									if (lastColumnX != compData.x)
+										*dst = _shadowTable[*dst];
+								} else {
+									*dst = pcolor;
+								}
+								break;
+
+							case ShadowMode::Mode3:
+								pcolor = _palette[color];
+								if (pcolor < 8) {
+									if (lastColumnX != compData.x) {
+										pcolor = (pcolor << 8) + *dst;
+										*dst = _shadowTable[pcolor];
+									}
+								} else {
+									*dst = pcolor;
+								}
+								break;
+							}
+						}
+					}
+					dst += pitch;
+					mask += _numStrips;
+					y++;
+				}
+			} while (--batch);
+
+			if (height == 0) {
+				if (--compData.skipWidth == 0)
+					return;
+				height = _height;
+				y = compData.y;
+
+				scaleIndexY = compData.scaleYIndex;
+				lastColumnX = compData.x;
+
+				if (_scaleX == 255 || compData.scaleTable[compData.scaleXIndex] < _scaleX) {
+					compData.x += compData.scaleXStep;
+					maskbit = revBitMask(compData.x & 7);
+					compData.destPtr += compData.scaleXStep;
+				}
+
+				compData.scaleXIndex = (compData.scaleXIndex + compData.scaleXStep) & compData.scaleIndexMask;
+
+				dst = compData.destPtr;
+				mask = compData.maskPtr + compData.x / 8;
+			}
+		StartPos:;
+		} while (len > 0);
+	} while (true);
+}
+
+void ByleRLEDecode_Scaled_Mode0_SMask(
+	BaseCostumeRenderer::ByleRLEData *pcompData,
+	const byte _scaleX,
+	const byte _scaleY,
+	const int _height,
+	const int pitch,
+	const int _numStrips,
+	const ShadowMode shadowMode,
+	const byte *_srcPtr,
+	const byte *_shadowTable,
+	const uint16 *_palette) {
+
+	BaseCostumeRenderer::ByleRLEData &compData = *pcompData;
+
+	const byte *src = _srcPtr;
+	byte *dst = compData.destPtr;
+
+	byte len = compData.repLen;
+	uint16 color = compData.repColor;
+
+	// reset every column
+	int lastColumnX = -1;
+	int y = compData.y;
+	uint16 height = _height;
+	int scaleIndexY = compData.scaleYIndex;
+
+	byte maskbit = revBitMask(compData.x & 7);
+	const byte *mask = compData.maskPtr + compData.x / 8;
+
+	byte batch;
+	if (len) {
+		--len;
+		goto StartPos;
+	}
+
+	do {
+		len = *src++;
+		color = len >> compData.shr;
+		len &= compData.mask;
+		if (!len)
+			len = *src++;
+
+		do {
+			batch = height < len ? (byte)height : len;
+			len -= batch;
+			height -= batch;
+
+			assert(compData.x >= compData.boundsRect.left && compData.x < compData.boundsRect.right);
+
+			do {
+				if (_scaleY == 255 || compData.scaleTable[scaleIndexY++ & compData.scaleIndexMask] < _scaleY) {
+					if (color) {
+						const bool masked = (y < compData.boundsRect.top || y >= compData.boundsRect.bottom)
+							|| (*mask & maskbit);
+
+						if (!masked) {
+							uint16 pcolor;
+
+							switch(shadowMode) {
+							case ShadowMode::Mode0:
+								*dst = _palette[color];
+								break;
+
+							case ShadowMode::Classic:
+								if (lastColumnX != compData.x)
+									*dst = _shadowTable[*dst];
+								break;
+
+							case ShadowMode::Mode1:
+								pcolor = _palette[color];
+								if (pcolor == 13 && _shadowTable) {
+									if (lastColumnX != compData.x)
+										*dst = _shadowTable[*dst];
+								} else {
+									*dst = pcolor;
+								}
+								break;
+
+							case ShadowMode::Mode3:
+								pcolor = _palette[color];
+								if (pcolor < 8) {
+									if (lastColumnX != compData.x) {
+										pcolor = (pcolor << 8) + *dst;
+										*dst = _shadowTable[pcolor];
+									}
+								} else {
+									*dst = pcolor;
+								}
+								break;
+							}
+						}
+					}
+					dst += pitch;
+					mask += _numStrips;
+					y++;
+				}
+			} while (--batch);
+
+			if (height == 0) {
+				if (--compData.skipWidth == 0)
+					return;
+				height = _height;
+				y = compData.y;
+
+				scaleIndexY = compData.scaleYIndex;
+				lastColumnX = compData.x;
+
+				if (_scaleX == 255 || compData.scaleTable[compData.scaleXIndex] < _scaleX) {
+					compData.x += compData.scaleXStep;
+					maskbit = revBitMask(compData.x & 7);
+					compData.destPtr += compData.scaleXStep;
+				}
+
+				compData.scaleXIndex = (compData.scaleXIndex + compData.scaleXStep) & compData.scaleIndexMask;
+
+				dst = compData.destPtr;
+				mask = compData.maskPtr + compData.x / 8;
+			}
+		StartPos:;
+		} while (len > 0);
+	} while (true);
+}
+
+void ByleRLEDecode_Scaled_Mode1(
+	BaseCostumeRenderer::ByleRLEData *pcompData,
+	const byte _scaleX,
+	const byte _scaleY,
+	const int _height,
+	const int pitch,
+	const int _numStrips,
+	const ShadowMode shadowMode,
+	const byte *_srcPtr,
+	const byte *_shadowTable,
+	const uint16 *_palette) {
+
+	BaseCostumeRenderer::ByleRLEData &compData = *pcompData;
+
+	const byte *src = _srcPtr;
+	byte *dst = compData.destPtr;
+
+	byte len = compData.repLen;
+	uint16 color = compData.repColor;
+
+	// reset every column
+	int lastColumnX = -1;
+	int y = compData.y;
+	uint16 height = _height;
+	int scaleIndexY = compData.scaleYIndex;
+
+	byte maskbit = revBitMask(compData.x & 7);
+	const byte *mask = compData.maskPtr + compData.x / 8;
+
+	byte batch;
+	if (len) {
+		--len;
+		goto StartPos;
+	}
+
+	do {
+		len = *src++;
+		color = len >> compData.shr;
+		len &= compData.mask;
+		if (!len)
+			len = *src++;
+
+		do {
+			batch = height < len ? (byte)height : len;
+			len -= batch;
+			height -= batch;
+
+			assert(compData.x >= compData.boundsRect.left && compData.x < compData.boundsRect.right);
+
+			do {
+				if (_scaleY == 255 || compData.scaleTable[scaleIndexY++ & compData.scaleIndexMask] < _scaleY) {
+					if (color) {
+						const bool masked = (y < compData.boundsRect.top || y >= compData.boundsRect.bottom)
+							|| (*mask & maskbit);
+
+						if (!masked) {
+							uint16 pcolor;
+
+							switch(shadowMode) {
+							case ShadowMode::Mode0:
+								*dst = _palette[color];
+								break;
+
+							case ShadowMode::Classic:
+								if (lastColumnX != compData.x)
+									*dst = _shadowTable[*dst];
+								break;
+
+							case ShadowMode::Mode1:
+								pcolor = _palette[color];
+								if (pcolor == 13 && _shadowTable) {
+									if (lastColumnX != compData.x)
+										*dst = _shadowTable[*dst];
+								} else {
+									*dst = pcolor;
+								}
+								break;
+
+							case ShadowMode::Mode3:
+								pcolor = _palette[color];
+								if (pcolor < 8) {
+									if (lastColumnX != compData.x) {
+										pcolor = (pcolor << 8) + *dst;
+										*dst = _shadowTable[pcolor];
+									}
+								} else {
+									*dst = pcolor;
+								}
+								break;
+							}
+						}
+					}
+					dst += pitch;
+					mask += _numStrips;
+					y++;
+				}
+			} while (--batch);
+
+			if (height == 0) {
+				if (--compData.skipWidth == 0)
+					return;
+				height = _height;
+				y = compData.y;
+
+				scaleIndexY = compData.scaleYIndex;
+				lastColumnX = compData.x;
+
+				if (_scaleX == 255 || compData.scaleTable[compData.scaleXIndex] < _scaleX) {
+					compData.x += compData.scaleXStep;
+					maskbit = revBitMask(compData.x & 7);
+					compData.destPtr += compData.scaleXStep;
+				}
+
+				compData.scaleXIndex = (compData.scaleXIndex + compData.scaleXStep) & compData.scaleIndexMask;
+
+				dst = compData.destPtr;
+				mask = compData.maskPtr + compData.x / 8;
+			}
+		StartPos:;
+		} while (len > 0);
+	} while (true);
+}
+
+void ByleRLEDecode_Scaled_Mode1_SMask(
+	BaseCostumeRenderer::ByleRLEData *pcompData,
+	const byte _scaleX,
+	const byte _scaleY,
+	const int _height,
+	const int pitch,
+	const int _numStrips,
+	const ShadowMode shadowMode,
+	const byte *_srcPtr,
+	const byte *_shadowTable,
+	const uint16 *_palette) {
+
+	BaseCostumeRenderer::ByleRLEData &compData = *pcompData;
+
+	const byte *src = _srcPtr;
+	byte *dst = compData.destPtr;
+
+	byte len = compData.repLen;
+	uint16 color = compData.repColor;
+
+	// reset every column
+	int lastColumnX = -1;
+	int y = compData.y;
+	uint16 height = _height;
+	int scaleIndexY = compData.scaleYIndex;
+
+	byte maskbit = revBitMask(compData.x & 7);
+	const byte *mask = compData.maskPtr + compData.x / 8;
+
+	byte batch;
+	if (len) {
+		--len;
+		goto StartPos;
+	}
+
+	do {
+		len = *src++;
+		color = len >> compData.shr;
+		len &= compData.mask;
+		if (!len)
+			len = *src++;
+
+		do {
+			batch = height < len ? (byte)height : len;
+			len -= batch;
+			height -= batch;
+
+			assert(compData.x >= compData.boundsRect.left && compData.x < compData.boundsRect.right);
+
+			do {
+				if (_scaleY == 255 || compData.scaleTable[scaleIndexY++ & compData.scaleIndexMask] < _scaleY) {
+					if (color) {
+						const bool masked = (y < compData.boundsRect.top || y >= compData.boundsRect.bottom)
+							|| (*mask & maskbit);
+
+						if (!masked) {
+							uint16 pcolor;
+
+							switch(shadowMode) {
+							case ShadowMode::Mode0:
+								*dst = _palette[color];
+								break;
+
+							case ShadowMode::Classic:
+								if (lastColumnX != compData.x)
+									*dst = _shadowTable[*dst];
+								break;
+
+							case ShadowMode::Mode1:
+								pcolor = _palette[color];
+								if (pcolor == 13 && _shadowTable) {
+									if (lastColumnX != compData.x)
+										*dst = _shadowTable[*dst];
+								} else {
+									*dst = pcolor;
+								}
+								break;
+
+							case ShadowMode::Mode3:
+								pcolor = _palette[color];
+								if (pcolor < 8) {
+									if (lastColumnX != compData.x) {
+										pcolor = (pcolor << 8) + *dst;
+										*dst = _shadowTable[pcolor];
+									}
+								} else {
+									*dst = pcolor;
+								}
+								break;
+							}
+						}
+					}
+					dst += pitch;
+					mask += _numStrips;
+					y++;
+				}
+			} while (--batch);
+
+			if (height == 0) {
+				if (--compData.skipWidth == 0)
+					return;
+				height = _height;
+				y = compData.y;
+
+				scaleIndexY = compData.scaleYIndex;
+				lastColumnX = compData.x;
+
+				if (_scaleX == 255 || compData.scaleTable[compData.scaleXIndex] < _scaleX) {
+					compData.x += compData.scaleXStep;
+					maskbit = revBitMask(compData.x & 7);
+					compData.destPtr += compData.scaleXStep;
+				}
+
+				compData.scaleXIndex = (compData.scaleXIndex + compData.scaleXStep) & compData.scaleIndexMask;
+
+				dst = compData.destPtr;
+				mask = compData.maskPtr + compData.x / 8;
+			}
+		StartPos:;
+		} while (len > 0);
+	} while (true);
+}
+
+void ByleRLEDecode_Scaled_Mode3(
+	BaseCostumeRenderer::ByleRLEData *pcompData,
+	const byte _scaleX,
+	const byte _scaleY,
+	const int _height,
+	const int pitch,
+	const int _numStrips,
+	const ShadowMode shadowMode,
+	const byte *_srcPtr,
+	const byte *_shadowTable,
+	const uint16 *_palette) {
+
+	BaseCostumeRenderer::ByleRLEData &compData = *pcompData;
+
+	const byte *src = _srcPtr;
+	byte *dst = compData.destPtr;
+
+	byte len = compData.repLen;
+	uint16 color = compData.repColor;
+
+	// reset every column
+	int lastColumnX = -1;
+	int y = compData.y;
+	uint16 height = _height;
+	int scaleIndexY = compData.scaleYIndex;
+
+	byte maskbit = revBitMask(compData.x & 7);
+	const byte *mask = compData.maskPtr + compData.x / 8;
+
+	byte batch;
+	if (len) {
+		--len;
+		goto StartPos;
+	}
+
+	do {
+		len = *src++;
+		color = len >> compData.shr;
+		len &= compData.mask;
+		if (!len)
+			len = *src++;
+
+		do {
+			batch = height < len ? (byte)height : len;
+			len -= batch;
+			height -= batch;
+
+			assert(compData.x >= compData.boundsRect.left && compData.x < compData.boundsRect.right);
+
+			do {
+				if (_scaleY == 255 || compData.scaleTable[scaleIndexY++ & compData.scaleIndexMask] < _scaleY) {
+					if (color) {
+						const bool masked = (y < compData.boundsRect.top || y >= compData.boundsRect.bottom)
+							|| (*mask & maskbit);
+
+						if (!masked) {
+							uint16 pcolor;
+
+							switch(shadowMode) {
+							case ShadowMode::Mode0:
+								*dst = _palette[color];
+								break;
+
+							case ShadowMode::Classic:
+								if (lastColumnX != compData.x)
+									*dst = _shadowTable[*dst];
+								break;
+
+							case ShadowMode::Mode1:
+								pcolor = _palette[color];
+								if (pcolor == 13 && _shadowTable) {
+									if (lastColumnX != compData.x)
+										*dst = _shadowTable[*dst];
+								} else {
+									*dst = pcolor;
+								}
+								break;
+
+							case ShadowMode::Mode3:
+								pcolor = _palette[color];
+								if (pcolor < 8) {
+									if (lastColumnX != compData.x) {
+										pcolor = (pcolor << 8) + *dst;
+										*dst = _shadowTable[pcolor];
+									}
+								} else {
+									*dst = pcolor;
+								}
+								break;
+							}
+						}
+					}
+					dst += pitch;
+					mask += _numStrips;
+					y++;
+				}
+			} while (--batch);
+
+			if (height == 0) {
+				if (--compData.skipWidth == 0)
+					return;
+				height = _height;
+				y = compData.y;
+
+				scaleIndexY = compData.scaleYIndex;
+				lastColumnX = compData.x;
+
+				if (_scaleX == 255 || compData.scaleTable[compData.scaleXIndex] < _scaleX) {
+					compData.x += compData.scaleXStep;
+					maskbit = revBitMask(compData.x & 7);
+					compData.destPtr += compData.scaleXStep;
+				}
+
+				compData.scaleXIndex = (compData.scaleXIndex + compData.scaleXStep) & compData.scaleIndexMask;
+
+				dst = compData.destPtr;
+				mask = compData.maskPtr + compData.x / 8;
+			}
+		StartPos:;
+		} while (len > 0);
+	} while (true);
+}
+
+void ByleRLEDecode_Scaled_Classic_SMask(
+	BaseCostumeRenderer::ByleRLEData *pcompData,
+	const byte _scaleX,
+	const byte _scaleY,
+	const int _height,
+	const int pitch,
+	const int _numStrips,
+	const ShadowMode shadowMode,
+	const byte *_srcPtr,
+	const byte *_shadowTable,
+	const uint16 *_palette) {
+
+	BaseCostumeRenderer::ByleRLEData &compData = *pcompData;
+
 	const byte *src = _srcPtr;
 	byte *dst = compData.destPtr;
 
@@ -279,6 +1399,131 @@ void BaseCostumeRenderer::byleRLEDecodeFast(ByleRLEData &compData) {
 	byte maskbit = revBitMask(compData.x & 7);
 	const byte *mask = compData.maskPtr + compData.x / 8;
 
+	byte batch;
+	if (len) {
+		--len;
+		goto StartPos;
+	}
+
+	do {
+		len = *src++;
+		color = len >> compData.shr;
+		len &= compData.mask;
+		if (!len)
+			len = *src++;
+
+		do {
+			batch = height < len ? (byte)height : len;
+			len -= batch;
+			height -= batch;
+
+			assert(compData.x >= compData.boundsRect.left && compData.x < compData.boundsRect.right);
+
+			do {
+				if (_scaleY == 255 || compData.scaleTable[scaleIndexY++ & compData.scaleIndexMask] < _scaleY) {
+					if (color) {
+						const bool masked = (y < compData.boundsRect.top || y >= compData.boundsRect.bottom)
+							|| (*mask & maskbit);
+
+						if (!masked) {
+							uint16 pcolor;
+
+							switch(shadowMode) {
+							case ShadowMode::Mode0:
+								*dst = _palette[color];
+								break;
+
+							case ShadowMode::Classic:
+								if (lastColumnX != compData.x)
+									*dst = _shadowTable[*dst];
+								break;
+
+							case ShadowMode::Mode1:
+								pcolor = _palette[color];
+								if (pcolor == 13 && _shadowTable) {
+									if (lastColumnX != compData.x)
+										*dst = _shadowTable[*dst];
+								} else {
+									*dst = pcolor;
+								}
+								break;
+
+							case ShadowMode::Mode3:
+								pcolor = _palette[color];
+								if (pcolor < 8) {
+									if (lastColumnX != compData.x) {
+										pcolor = (pcolor << 8) + *dst;
+										*dst = _shadowTable[pcolor];
+									}
+								} else {
+									*dst = pcolor;
+								}
+								break;
+							}
+						}
+					}
+					dst += pitch;
+					mask += _numStrips;
+					y++;
+				}
+			} while (--batch);
+
+			if (height == 0) {
+				if (--compData.skipWidth == 0)
+					return;
+				height = _height;
+				y = compData.y;
+
+				scaleIndexY = compData.scaleYIndex;
+				lastColumnX = compData.x;
+
+				if (_scaleX == 255 || compData.scaleTable[compData.scaleXIndex] < _scaleX) {
+					compData.x += compData.scaleXStep;
+					maskbit = revBitMask(compData.x & 7);
+					compData.destPtr += compData.scaleXStep;
+				}
+
+				compData.scaleXIndex = (compData.scaleXIndex + compData.scaleXStep) & compData.scaleIndexMask;
+
+				dst = compData.destPtr;
+				mask = compData.maskPtr + compData.x / 8;
+			}
+		StartPos:;
+		} while (len > 0);
+	} while (true);
+}
+
+typedef void (*ByleRLEDecodeFunc)(
+	BaseCostumeRenderer::ByleRLEData *pcompData,
+	const byte _scaleX,
+	const byte _scaleY,
+	const int _height,
+	const int pitch,
+	const int _numStrips,
+	const ShadowMode shadowMode,
+	const byte *_srcPtr,
+	const byte *_shadowTable,
+	const uint16 *_palette);
+
+static const ByleRLEDecodeFunc byleRLEDecodeNoScaleTable[4] = {
+	ByleRLEDecode_Mode0,    // 0: Mode0
+	ByleRLEDecode_Mode1,    // 1: Mode1
+	ByleRLEDecode_Mode3,    // 2: Mode3
+	ByleRLEDecode_Classic,  // 3: Classic
+};
+
+static const ByleRLEDecodeFunc byleRLEDecodeScaledTable[8] = {
+	ByleRLEDecode_Scaled_Mode0,         // 0: Mode0,   no scaleIndexMask
+	ByleRLEDecode_Scaled_Mode0_SMask,   // 1: Mode0,   scaleIndexMask
+	ByleRLEDecode_Scaled_Mode1,         // 2: Mode1,   no scaleIndexMask
+	ByleRLEDecode_Scaled_Mode1_SMask,   // 3: Mode1,   scaleIndexMask
+	ByleRLEDecode_Scaled_Mode3,         // 4: Mode3,   no scaleIndexMask
+	nullptr,                            // 5: Mode3,   scaleIndexMask (COMI's Mode3 always uses bigCostumeScaleTable)
+	nullptr,                            // 6: Classic, no scaleIndexMask (_shadowMode & 0x20 always uses smallCostumeScaleTable)
+	ByleRLEDecode_Scaled_Classic_SMask, // 7: Classic, scaleIndexMask
+};
+
+void BaseCostumeRenderer::byleRLEDecodeFast(ByleRLEData &compData) {
 	ShadowMode shadowMode = ShadowMode::Mode0;
 	if (!_akosRendering) {
 		if (_shadowMode & 0x20)
@@ -292,6 +1537,51 @@ void BaseCostumeRenderer::byleRLEDecodeFast(ByleRLEData &compData) {
 			shadowMode = ShadowMode::Mode3;
 	}
 
+	{
+		const int scaled = (_scaleX != 255 || _scaleY != 255);
+		const int useScaleIndexMask = compData.scaleIndexMask != -1;
+		if (!scaled)
+			byleRLEDecodeNoScaleTable[static_cast<int>(shadowMode)](
+				&compData,
+				_scaleX,
+				_scaleY,
+				_height,
+				_out.pitch,
+				_numStrips,
+				shadowMode,
+				_srcPtr,
+				_shadowTable,
+				_palette);
+		else
+			byleRLEDecodeScaledTable[(static_cast<int>(shadowMode) << 1) | useScaleIndexMask](
+				&compData,
+				_scaleX,
+				_scaleY,
+				_height,
+				_out.pitch,
+				_numStrips,
+				shadowMode,
+				_srcPtr,
+				_shadowTable,
+				_palette);
+		return;
+	}
+
+	const byte *src = _srcPtr;
+	byte *dst = compData.destPtr;
+
+	byte len = compData.repLen;
+	uint16 color = compData.repColor;
+
+	// reset every column
+	int lastColumnX = -1;
+	int y = compData.y;
+	uint16 height = _height;
+	int scaleIndexY = compData.scaleYIndex;
+
+	byte maskbit = revBitMask(compData.x & 7);
+	const byte *mask = compData.maskPtr + compData.x / 8;
+
 	byte batch;
 	if (len) {
 		--len;


Commit: 27a71d8195129e7165745aece8f0f2f49593b990
    https://github.com/scummvm/scummvm/commit/27a71d8195129e7165745aece8f0f2f49593b990
Author: Miro Kropacek (miro.kropacek at gmail.com)
Date: 2026-06-14T21:59:20+02:00

Commit Message:
SCUMM: byleRLEDecodeFast: Remove dead code

Changed paths:
    engines/scumm/base-costume-optimised.cpp


diff --git a/engines/scumm/base-costume-optimised.cpp b/engines/scumm/base-costume-optimised.cpp
index 3317bdea9ce..baa515cb3e9 100644
--- a/engines/scumm/base-costume-optimised.cpp
+++ b/engines/scumm/base-costume-optimised.cpp
@@ -265,14 +265,13 @@ enum class ShadowMode : int {
 
 void ByleRLEDecode_Mode0(
 	BaseCostumeRenderer::ByleRLEData *pcompData,
-	const byte _scaleX,
-	const byte _scaleY,
+	const byte _scaleX, /* unused */
+	const byte _scaleY, /* unused */
 	const int _height,
 	const int pitch,
 	const int _numStrips,
-	const ShadowMode shadowMode,
 	const byte *_srcPtr,
-	const byte *_shadowTable,
+	const byte *_shadowTable, /* unused */
 	const uint16 *_palette) {
 
 	BaseCostumeRenderer::ByleRLEData &compData = *pcompData;
@@ -284,10 +283,8 @@ void ByleRLEDecode_Mode0(
 	uint16 color = compData.repColor;
 
 	// reset every column
-	int lastColumnX = -1;
 	int y = compData.y;
 	uint16 height = _height;
-	int scaleIndexY = compData.scaleYIndex;
 
 	byte maskbit = revBitMask(compData.x & 7);
 	const byte *mask = compData.maskPtr + compData.x / 8;
@@ -313,52 +310,17 @@ void ByleRLEDecode_Mode0(
 			assert(compData.x >= compData.boundsRect.left && compData.x < compData.boundsRect.right);
 
 			do {
-				if (_scaleY == 255 || compData.scaleTable[scaleIndexY++ & compData.scaleIndexMask] < _scaleY) {
-					if (color) {
-						const bool masked = (y < compData.boundsRect.top || y >= compData.boundsRect.bottom)
-							|| (*mask & maskbit);
-
-						if (!masked) {
-							uint16 pcolor;
-
-							switch(shadowMode) {
-							case ShadowMode::Mode0:
-								*dst = _palette[color];
-								break;
-
-							case ShadowMode::Classic:
-								if (lastColumnX != compData.x)
-									*dst = _shadowTable[*dst];
-								break;
+				if (color) {
+					const bool masked = (y < compData.boundsRect.top || y >= compData.boundsRect.bottom)
+					|| (*mask & maskbit);
 
-							case ShadowMode::Mode1:
-								pcolor = _palette[color];
-								if (pcolor == 13 && _shadowTable) {
-									if (lastColumnX != compData.x)
-										*dst = _shadowTable[*dst];
-								} else {
-									*dst = pcolor;
-								}
-								break;
-
-							case ShadowMode::Mode3:
-								pcolor = _palette[color];
-								if (pcolor < 8) {
-									if (lastColumnX != compData.x) {
-										pcolor = (pcolor << 8) + *dst;
-										*dst = _shadowTable[pcolor];
-									}
-								} else {
-									*dst = pcolor;
-								}
-								break;
-							}
-						}
+					if (!masked) {
+						*dst = _palette[color];
 					}
-					dst += pitch;
-					mask += _numStrips;
-					y++;
 				}
+				dst += pitch;
+				mask += _numStrips;
+				y++;
 			} while (--batch);
 
 			if (height == 0) {
@@ -367,16 +329,9 @@ void ByleRLEDecode_Mode0(
 				height = _height;
 				y = compData.y;
 
-				scaleIndexY = compData.scaleYIndex;
-				lastColumnX = compData.x;
-
-				if (_scaleX == 255 || compData.scaleTable[compData.scaleXIndex] < _scaleX) {
-					compData.x += compData.scaleXStep;
-					maskbit = revBitMask(compData.x & 7);
-					compData.destPtr += compData.scaleXStep;
-				}
-
-				compData.scaleXIndex = (compData.scaleXIndex + compData.scaleXStep) & compData.scaleIndexMask;
+				compData.x += compData.scaleXStep;
+				maskbit = revBitMask(compData.x & 7);
+				compData.destPtr += compData.scaleXStep;
 
 				dst = compData.destPtr;
 				mask = compData.maskPtr + compData.x / 8;
@@ -388,12 +343,11 @@ void ByleRLEDecode_Mode0(
 
 void ByleRLEDecode_Mode1(
 	BaseCostumeRenderer::ByleRLEData *pcompData,
-	const byte _scaleX,
-	const byte _scaleY,
+	const byte _scaleX, /* unused */
+	const byte _scaleY, /* unused */
 	const int _height,
 	const int pitch,
 	const int _numStrips,
-	const ShadowMode shadowMode,
 	const byte *_srcPtr,
 	const byte *_shadowTable,
 	const uint16 *_palette) {
@@ -410,7 +364,6 @@ void ByleRLEDecode_Mode1(
 	int lastColumnX = -1;
 	int y = compData.y;
 	uint16 height = _height;
-	int scaleIndexY = compData.scaleYIndex;
 
 	byte maskbit = revBitMask(compData.x & 7);
 	const byte *mask = compData.maskPtr + compData.x / 8;
@@ -436,52 +389,25 @@ void ByleRLEDecode_Mode1(
 			assert(compData.x >= compData.boundsRect.left && compData.x < compData.boundsRect.right);
 
 			do {
-				if (_scaleY == 255 || compData.scaleTable[scaleIndexY++ & compData.scaleIndexMask] < _scaleY) {
-					if (color) {
-						const bool masked = (y < compData.boundsRect.top || y >= compData.boundsRect.bottom)
-							|| (*mask & maskbit);
-
-						if (!masked) {
-							uint16 pcolor;
-
-							switch(shadowMode) {
-							case ShadowMode::Mode0:
-								*dst = _palette[color];
-								break;
-
-							case ShadowMode::Classic:
-								if (lastColumnX != compData.x)
-									*dst = _shadowTable[*dst];
-								break;
-
-							case ShadowMode::Mode1:
-								pcolor = _palette[color];
-								if (pcolor == 13 && _shadowTable) {
-									if (lastColumnX != compData.x)
-										*dst = _shadowTable[*dst];
-								} else {
-									*dst = pcolor;
-								}
-								break;
-
-							case ShadowMode::Mode3:
-								pcolor = _palette[color];
-								if (pcolor < 8) {
-									if (lastColumnX != compData.x) {
-										pcolor = (pcolor << 8) + *dst;
-										*dst = _shadowTable[pcolor];
-									}
-								} else {
-									*dst = pcolor;
-								}
-								break;
-							}
+				if (color) {
+					const bool masked = (y < compData.boundsRect.top || y >= compData.boundsRect.bottom)
+					|| (*mask & maskbit);
+
+					if (!masked) {
+						uint16 pcolor;
+
+						pcolor = _palette[color];
+						if (pcolor == 13 && _shadowTable) {
+							if (lastColumnX != compData.x)
+								*dst = _shadowTable[*dst];
+						} else {
+							*dst = pcolor;
 						}
 					}
-					dst += pitch;
-					mask += _numStrips;
-					y++;
 				}
+				dst += pitch;
+				mask += _numStrips;
+				y++;
 			} while (--batch);
 
 			if (height == 0) {
@@ -490,16 +416,11 @@ void ByleRLEDecode_Mode1(
 				height = _height;
 				y = compData.y;
 
-				scaleIndexY = compData.scaleYIndex;
 				lastColumnX = compData.x;
 
-				if (_scaleX == 255 || compData.scaleTable[compData.scaleXIndex] < _scaleX) {
-					compData.x += compData.scaleXStep;
-					maskbit = revBitMask(compData.x & 7);
-					compData.destPtr += compData.scaleXStep;
-				}
-
-				compData.scaleXIndex = (compData.scaleXIndex + compData.scaleXStep) & compData.scaleIndexMask;
+				compData.x += compData.scaleXStep;
+				maskbit = revBitMask(compData.x & 7);
+				compData.destPtr += compData.scaleXStep;
 
 				dst = compData.destPtr;
 				mask = compData.maskPtr + compData.x / 8;
@@ -511,12 +432,11 @@ void ByleRLEDecode_Mode1(
 
 void ByleRLEDecode_Mode3(
 	BaseCostumeRenderer::ByleRLEData *pcompData,
-	const byte _scaleX,
-	const byte _scaleY,
+	const byte _scaleX, /* unused */
+	const byte _scaleY, /* unused */
 	const int _height,
 	const int pitch,
 	const int _numStrips,
-	const ShadowMode shadowMode,
 	const byte *_srcPtr,
 	const byte *_shadowTable,
 	const uint16 *_palette) {
@@ -533,7 +453,6 @@ void ByleRLEDecode_Mode3(
 	int lastColumnX = -1;
 	int y = compData.y;
 	uint16 height = _height;
-	int scaleIndexY = compData.scaleYIndex;
 
 	byte maskbit = revBitMask(compData.x & 7);
 	const byte *mask = compData.maskPtr + compData.x / 8;
@@ -559,52 +478,27 @@ void ByleRLEDecode_Mode3(
 			assert(compData.x >= compData.boundsRect.left && compData.x < compData.boundsRect.right);
 
 			do {
-				if (_scaleY == 255 || compData.scaleTable[scaleIndexY++ & compData.scaleIndexMask] < _scaleY) {
-					if (color) {
-						const bool masked = (y < compData.boundsRect.top || y >= compData.boundsRect.bottom)
-							|| (*mask & maskbit);
-
-						if (!masked) {
-							uint16 pcolor;
-
-							switch(shadowMode) {
-							case ShadowMode::Mode0:
-								*dst = _palette[color];
-								break;
-
-							case ShadowMode::Classic:
-								if (lastColumnX != compData.x)
-									*dst = _shadowTable[*dst];
-								break;
-
-							case ShadowMode::Mode1:
-								pcolor = _palette[color];
-								if (pcolor == 13 && _shadowTable) {
-									if (lastColumnX != compData.x)
-										*dst = _shadowTable[*dst];
-								} else {
-									*dst = pcolor;
-								}
-								break;
-
-							case ShadowMode::Mode3:
-								pcolor = _palette[color];
-								if (pcolor < 8) {
-									if (lastColumnX != compData.x) {
-										pcolor = (pcolor << 8) + *dst;
-										*dst = _shadowTable[pcolor];
-									}
-								} else {
-									*dst = pcolor;
-								}
-								break;
+				if (color) {
+					const bool masked = (y < compData.boundsRect.top || y >= compData.boundsRect.bottom)
+					|| (*mask & maskbit);
+
+					if (!masked) {
+						uint16 pcolor;
+
+						pcolor = _palette[color];
+						if (pcolor < 8) {
+							if (lastColumnX != compData.x) {
+								pcolor = (pcolor << 8) + *dst;
+								*dst = _shadowTable[pcolor];
 							}
+						} else {
+							*dst = pcolor;
 						}
 					}
-					dst += pitch;
-					mask += _numStrips;
-					y++;
 				}
+				dst += pitch;
+				mask += _numStrips;
+				y++;
 			} while (--batch);
 
 			if (height == 0) {
@@ -613,16 +507,11 @@ void ByleRLEDecode_Mode3(
 				height = _height;
 				y = compData.y;
 
-				scaleIndexY = compData.scaleYIndex;
 				lastColumnX = compData.x;
 
-				if (_scaleX == 255 || compData.scaleTable[compData.scaleXIndex] < _scaleX) {
-					compData.x += compData.scaleXStep;
-					maskbit = revBitMask(compData.x & 7);
-					compData.destPtr += compData.scaleXStep;
-				}
-
-				compData.scaleXIndex = (compData.scaleXIndex + compData.scaleXStep) & compData.scaleIndexMask;
+				compData.x += compData.scaleXStep;
+				maskbit = revBitMask(compData.x & 7);
+				compData.destPtr += compData.scaleXStep;
 
 				dst = compData.destPtr;
 				mask = compData.maskPtr + compData.x / 8;
@@ -634,15 +523,14 @@ void ByleRLEDecode_Mode3(
 
 void ByleRLEDecode_Classic(
 	BaseCostumeRenderer::ByleRLEData *pcompData,
-	const byte _scaleX,
-	const byte _scaleY,
+	const byte _scaleX, /* unused */
+	const byte _scaleY, /* unused */
 	const int _height,
 	const int pitch,
 	const int _numStrips,
-	const ShadowMode shadowMode,
 	const byte *_srcPtr,
 	const byte *_shadowTable,
-	const uint16 *_palette) {
+	const uint16 *_palette /* unused */) {
 
 	BaseCostumeRenderer::ByleRLEData &compData = *pcompData;
 
@@ -656,7 +544,6 @@ void ByleRLEDecode_Classic(
 	int lastColumnX = -1;
 	int y = compData.y;
 	uint16 height = _height;
-	int scaleIndexY = compData.scaleYIndex;
 
 	byte maskbit = revBitMask(compData.x & 7);
 	const byte *mask = compData.maskPtr + compData.x / 8;
@@ -682,52 +569,18 @@ void ByleRLEDecode_Classic(
 			assert(compData.x >= compData.boundsRect.left && compData.x < compData.boundsRect.right);
 
 			do {
-				if (_scaleY == 255 || compData.scaleTable[scaleIndexY++ & compData.scaleIndexMask] < _scaleY) {
-					if (color) {
-						const bool masked = (y < compData.boundsRect.top || y >= compData.boundsRect.bottom)
-							|| (*mask & maskbit);
-
-						if (!masked) {
-							uint16 pcolor;
-
-							switch(shadowMode) {
-							case ShadowMode::Mode0:
-								*dst = _palette[color];
-								break;
-
-							case ShadowMode::Classic:
-								if (lastColumnX != compData.x)
-									*dst = _shadowTable[*dst];
-								break;
-
-							case ShadowMode::Mode1:
-								pcolor = _palette[color];
-								if (pcolor == 13 && _shadowTable) {
-									if (lastColumnX != compData.x)
-										*dst = _shadowTable[*dst];
-								} else {
-									*dst = pcolor;
-								}
-								break;
+				if (color) {
+					const bool masked = (y < compData.boundsRect.top || y >= compData.boundsRect.bottom)
+					|| (*mask & maskbit);
 
-							case ShadowMode::Mode3:
-								pcolor = _palette[color];
-								if (pcolor < 8) {
-									if (lastColumnX != compData.x) {
-										pcolor = (pcolor << 8) + *dst;
-										*dst = _shadowTable[pcolor];
-									}
-								} else {
-									*dst = pcolor;
-								}
-								break;
-							}
-						}
+					if (!masked) {
+						if (lastColumnX != compData.x)
+							*dst = _shadowTable[*dst];
 					}
-					dst += pitch;
-					mask += _numStrips;
-					y++;
 				}
+				dst += pitch;
+				mask += _numStrips;
+				y++;
 			} while (--batch);
 
 			if (height == 0) {
@@ -736,16 +589,11 @@ void ByleRLEDecode_Classic(
 				height = _height;
 				y = compData.y;
 
-				scaleIndexY = compData.scaleYIndex;
 				lastColumnX = compData.x;
 
-				if (_scaleX == 255 || compData.scaleTable[compData.scaleXIndex] < _scaleX) {
-					compData.x += compData.scaleXStep;
-					maskbit = revBitMask(compData.x & 7);
-					compData.destPtr += compData.scaleXStep;
-				}
-
-				compData.scaleXIndex = (compData.scaleXIndex + compData.scaleXStep) & compData.scaleIndexMask;
+				compData.x += compData.scaleXStep;
+				maskbit = revBitMask(compData.x & 7);
+				compData.destPtr += compData.scaleXStep;
 
 				dst = compData.destPtr;
 				mask = compData.maskPtr + compData.x / 8;
@@ -762,9 +610,8 @@ void ByleRLEDecode_Scaled_Mode0(
 	const int _height,
 	const int pitch,
 	const int _numStrips,
-	const ShadowMode shadowMode,
 	const byte *_srcPtr,
-	const byte *_shadowTable,
+	const byte *_shadowTable, /* unused */
 	const uint16 *_palette) {
 
 	BaseCostumeRenderer::ByleRLEData &compData = *pcompData;
@@ -776,7 +623,6 @@ void ByleRLEDecode_Scaled_Mode0(
 	uint16 color = compData.repColor;
 
 	// reset every column
-	int lastColumnX = -1;
 	int y = compData.y;
 	uint16 height = _height;
 	int scaleIndexY = compData.scaleYIndex;
@@ -805,46 +651,13 @@ void ByleRLEDecode_Scaled_Mode0(
 			assert(compData.x >= compData.boundsRect.left && compData.x < compData.boundsRect.right);
 
 			do {
-				if (_scaleY == 255 || compData.scaleTable[scaleIndexY++ & compData.scaleIndexMask] < _scaleY) {
+				if (compData.scaleTable[scaleIndexY++] < _scaleY) {
 					if (color) {
 						const bool masked = (y < compData.boundsRect.top || y >= compData.boundsRect.bottom)
-							|| (*mask & maskbit);
+						|| (*mask & maskbit);
 
 						if (!masked) {
-							uint16 pcolor;
-
-							switch(shadowMode) {
-							case ShadowMode::Mode0:
-								*dst = _palette[color];
-								break;
-
-							case ShadowMode::Classic:
-								if (lastColumnX != compData.x)
-									*dst = _shadowTable[*dst];
-								break;
-
-							case ShadowMode::Mode1:
-								pcolor = _palette[color];
-								if (pcolor == 13 && _shadowTable) {
-									if (lastColumnX != compData.x)
-										*dst = _shadowTable[*dst];
-								} else {
-									*dst = pcolor;
-								}
-								break;
-
-							case ShadowMode::Mode3:
-								pcolor = _palette[color];
-								if (pcolor < 8) {
-									if (lastColumnX != compData.x) {
-										pcolor = (pcolor << 8) + *dst;
-										*dst = _shadowTable[pcolor];
-									}
-								} else {
-									*dst = pcolor;
-								}
-								break;
-							}
+							*dst = _palette[color];
 						}
 					}
 					dst += pitch;
@@ -860,15 +673,14 @@ void ByleRLEDecode_Scaled_Mode0(
 				y = compData.y;
 
 				scaleIndexY = compData.scaleYIndex;
-				lastColumnX = compData.x;
 
-				if (_scaleX == 255 || compData.scaleTable[compData.scaleXIndex] < _scaleX) {
+				if (compData.scaleTable[compData.scaleXIndex] < _scaleX) {
 					compData.x += compData.scaleXStep;
 					maskbit = revBitMask(compData.x & 7);
 					compData.destPtr += compData.scaleXStep;
 				}
 
-				compData.scaleXIndex = (compData.scaleXIndex + compData.scaleXStep) & compData.scaleIndexMask;
+				compData.scaleXIndex += compData.scaleXStep;
 
 				dst = compData.destPtr;
 				mask = compData.maskPtr + compData.x / 8;
@@ -885,9 +697,8 @@ void ByleRLEDecode_Scaled_Mode0_SMask(
 	const int _height,
 	const int pitch,
 	const int _numStrips,
-	const ShadowMode shadowMode,
 	const byte *_srcPtr,
-	const byte *_shadowTable,
+	const byte *_shadowTable, /* unused */
 	const uint16 *_palette) {
 
 	BaseCostumeRenderer::ByleRLEData &compData = *pcompData;
@@ -899,7 +710,6 @@ void ByleRLEDecode_Scaled_Mode0_SMask(
 	uint16 color = compData.repColor;
 
 	// reset every column
-	int lastColumnX = -1;
 	int y = compData.y;
 	uint16 height = _height;
 	int scaleIndexY = compData.scaleYIndex;
@@ -928,46 +738,13 @@ void ByleRLEDecode_Scaled_Mode0_SMask(
 			assert(compData.x >= compData.boundsRect.left && compData.x < compData.boundsRect.right);
 
 			do {
-				if (_scaleY == 255 || compData.scaleTable[scaleIndexY++ & compData.scaleIndexMask] < _scaleY) {
+				if (compData.scaleTable[scaleIndexY++ & compData.scaleIndexMask] < _scaleY) {
 					if (color) {
 						const bool masked = (y < compData.boundsRect.top || y >= compData.boundsRect.bottom)
-							|| (*mask & maskbit);
+						|| (*mask & maskbit);
 
 						if (!masked) {
-							uint16 pcolor;
-
-							switch(shadowMode) {
-							case ShadowMode::Mode0:
-								*dst = _palette[color];
-								break;
-
-							case ShadowMode::Classic:
-								if (lastColumnX != compData.x)
-									*dst = _shadowTable[*dst];
-								break;
-
-							case ShadowMode::Mode1:
-								pcolor = _palette[color];
-								if (pcolor == 13 && _shadowTable) {
-									if (lastColumnX != compData.x)
-										*dst = _shadowTable[*dst];
-								} else {
-									*dst = pcolor;
-								}
-								break;
-
-							case ShadowMode::Mode3:
-								pcolor = _palette[color];
-								if (pcolor < 8) {
-									if (lastColumnX != compData.x) {
-										pcolor = (pcolor << 8) + *dst;
-										*dst = _shadowTable[pcolor];
-									}
-								} else {
-									*dst = pcolor;
-								}
-								break;
-							}
+							*dst = _palette[color];
 						}
 					}
 					dst += pitch;
@@ -983,9 +760,8 @@ void ByleRLEDecode_Scaled_Mode0_SMask(
 				y = compData.y;
 
 				scaleIndexY = compData.scaleYIndex;
-				lastColumnX = compData.x;
 
-				if (_scaleX == 255 || compData.scaleTable[compData.scaleXIndex] < _scaleX) {
+				if (compData.scaleTable[compData.scaleXIndex] < _scaleX) {
 					compData.x += compData.scaleXStep;
 					maskbit = revBitMask(compData.x & 7);
 					compData.destPtr += compData.scaleXStep;
@@ -1008,7 +784,6 @@ void ByleRLEDecode_Scaled_Mode1(
 	const int _height,
 	const int pitch,
 	const int _numStrips,
-	const ShadowMode shadowMode,
 	const byte *_srcPtr,
 	const byte *_shadowTable,
 	const uint16 *_palette) {
@@ -1051,45 +826,20 @@ void ByleRLEDecode_Scaled_Mode1(
 			assert(compData.x >= compData.boundsRect.left && compData.x < compData.boundsRect.right);
 
 			do {
-				if (_scaleY == 255 || compData.scaleTable[scaleIndexY++ & compData.scaleIndexMask] < _scaleY) {
+				if (compData.scaleTable[scaleIndexY++] < _scaleY) {
 					if (color) {
 						const bool masked = (y < compData.boundsRect.top || y >= compData.boundsRect.bottom)
-							|| (*mask & maskbit);
+						|| (*mask & maskbit);
 
 						if (!masked) {
 							uint16 pcolor;
 
-							switch(shadowMode) {
-							case ShadowMode::Mode0:
-								*dst = _palette[color];
-								break;
-
-							case ShadowMode::Classic:
+							pcolor = _palette[color];
+							if (pcolor == 13 && _shadowTable) {
 								if (lastColumnX != compData.x)
 									*dst = _shadowTable[*dst];
-								break;
-
-							case ShadowMode::Mode1:
-								pcolor = _palette[color];
-								if (pcolor == 13 && _shadowTable) {
-									if (lastColumnX != compData.x)
-										*dst = _shadowTable[*dst];
-								} else {
-									*dst = pcolor;
-								}
-								break;
-
-							case ShadowMode::Mode3:
-								pcolor = _palette[color];
-								if (pcolor < 8) {
-									if (lastColumnX != compData.x) {
-										pcolor = (pcolor << 8) + *dst;
-										*dst = _shadowTable[pcolor];
-									}
-								} else {
-									*dst = pcolor;
-								}
-								break;
+							} else {
+								*dst = pcolor;
 							}
 						}
 					}
@@ -1108,13 +858,13 @@ void ByleRLEDecode_Scaled_Mode1(
 				scaleIndexY = compData.scaleYIndex;
 				lastColumnX = compData.x;
 
-				if (_scaleX == 255 || compData.scaleTable[compData.scaleXIndex] < _scaleX) {
+				if (compData.scaleTable[compData.scaleXIndex] < _scaleX) {
 					compData.x += compData.scaleXStep;
 					maskbit = revBitMask(compData.x & 7);
 					compData.destPtr += compData.scaleXStep;
 				}
 
-				compData.scaleXIndex = (compData.scaleXIndex + compData.scaleXStep) & compData.scaleIndexMask;
+				compData.scaleXIndex += compData.scaleXStep;
 
 				dst = compData.destPtr;
 				mask = compData.maskPtr + compData.x / 8;
@@ -1131,7 +881,6 @@ void ByleRLEDecode_Scaled_Mode1_SMask(
 	const int _height,
 	const int pitch,
 	const int _numStrips,
-	const ShadowMode shadowMode,
 	const byte *_srcPtr,
 	const byte *_shadowTable,
 	const uint16 *_palette) {
@@ -1174,45 +923,20 @@ void ByleRLEDecode_Scaled_Mode1_SMask(
 			assert(compData.x >= compData.boundsRect.left && compData.x < compData.boundsRect.right);
 
 			do {
-				if (_scaleY == 255 || compData.scaleTable[scaleIndexY++ & compData.scaleIndexMask] < _scaleY) {
+				if (compData.scaleTable[scaleIndexY++ & compData.scaleIndexMask] < _scaleY) {
 					if (color) {
 						const bool masked = (y < compData.boundsRect.top || y >= compData.boundsRect.bottom)
-							|| (*mask & maskbit);
+						|| (*mask & maskbit);
 
 						if (!masked) {
 							uint16 pcolor;
 
-							switch(shadowMode) {
-							case ShadowMode::Mode0:
-								*dst = _palette[color];
-								break;
-
-							case ShadowMode::Classic:
+							pcolor = _palette[color];
+							if (pcolor == 13 && _shadowTable) {
 								if (lastColumnX != compData.x)
 									*dst = _shadowTable[*dst];
-								break;
-
-							case ShadowMode::Mode1:
-								pcolor = _palette[color];
-								if (pcolor == 13 && _shadowTable) {
-									if (lastColumnX != compData.x)
-										*dst = _shadowTable[*dst];
-								} else {
-									*dst = pcolor;
-								}
-								break;
-
-							case ShadowMode::Mode3:
-								pcolor = _palette[color];
-								if (pcolor < 8) {
-									if (lastColumnX != compData.x) {
-										pcolor = (pcolor << 8) + *dst;
-										*dst = _shadowTable[pcolor];
-									}
-								} else {
-									*dst = pcolor;
-								}
-								break;
+							} else {
+								*dst = pcolor;
 							}
 						}
 					}
@@ -1231,7 +955,7 @@ void ByleRLEDecode_Scaled_Mode1_SMask(
 				scaleIndexY = compData.scaleYIndex;
 				lastColumnX = compData.x;
 
-				if (_scaleX == 255 || compData.scaleTable[compData.scaleXIndex] < _scaleX) {
+				if (compData.scaleTable[compData.scaleXIndex] < _scaleX) {
 					compData.x += compData.scaleXStep;
 					maskbit = revBitMask(compData.x & 7);
 					compData.destPtr += compData.scaleXStep;
@@ -1254,7 +978,6 @@ void ByleRLEDecode_Scaled_Mode3(
 	const int _height,
 	const int pitch,
 	const int _numStrips,
-	const ShadowMode shadowMode,
 	const byte *_srcPtr,
 	const byte *_shadowTable,
 	const uint16 *_palette) {
@@ -1297,45 +1020,22 @@ void ByleRLEDecode_Scaled_Mode3(
 			assert(compData.x >= compData.boundsRect.left && compData.x < compData.boundsRect.right);
 
 			do {
-				if (_scaleY == 255 || compData.scaleTable[scaleIndexY++ & compData.scaleIndexMask] < _scaleY) {
+				if (compData.scaleTable[scaleIndexY++] < _scaleY) {
 					if (color) {
 						const bool masked = (y < compData.boundsRect.top || y >= compData.boundsRect.bottom)
-							|| (*mask & maskbit);
+						|| (*mask & maskbit);
 
 						if (!masked) {
 							uint16 pcolor;
 
-							switch(shadowMode) {
-							case ShadowMode::Mode0:
-								*dst = _palette[color];
-								break;
-
-							case ShadowMode::Classic:
-								if (lastColumnX != compData.x)
-									*dst = _shadowTable[*dst];
-								break;
-
-							case ShadowMode::Mode1:
-								pcolor = _palette[color];
-								if (pcolor == 13 && _shadowTable) {
-									if (lastColumnX != compData.x)
-										*dst = _shadowTable[*dst];
-								} else {
-									*dst = pcolor;
-								}
-								break;
-
-							case ShadowMode::Mode3:
-								pcolor = _palette[color];
-								if (pcolor < 8) {
-									if (lastColumnX != compData.x) {
-										pcolor = (pcolor << 8) + *dst;
-										*dst = _shadowTable[pcolor];
-									}
-								} else {
-									*dst = pcolor;
+							pcolor = _palette[color];
+							if (pcolor < 8) {
+								if (lastColumnX != compData.x) {
+									pcolor = (pcolor << 8) + *dst;
+									*dst = _shadowTable[pcolor];
 								}
-								break;
+							} else {
+								*dst = pcolor;
 							}
 						}
 					}
@@ -1354,13 +1054,13 @@ void ByleRLEDecode_Scaled_Mode3(
 				scaleIndexY = compData.scaleYIndex;
 				lastColumnX = compData.x;
 
-				if (_scaleX == 255 || compData.scaleTable[compData.scaleXIndex] < _scaleX) {
+				if (compData.scaleTable[compData.scaleXIndex] < _scaleX) {
 					compData.x += compData.scaleXStep;
 					maskbit = revBitMask(compData.x & 7);
 					compData.destPtr += compData.scaleXStep;
 				}
 
-				compData.scaleXIndex = (compData.scaleXIndex + compData.scaleXStep) & compData.scaleIndexMask;
+				compData.scaleXIndex += compData.scaleXStep;
 
 				dst = compData.destPtr;
 				mask = compData.maskPtr + compData.x / 8;
@@ -1377,10 +1077,9 @@ void ByleRLEDecode_Scaled_Classic_SMask(
 	const int _height,
 	const int pitch,
 	const int _numStrips,
-	const ShadowMode shadowMode,
 	const byte *_srcPtr,
 	const byte *_shadowTable,
-	const uint16 *_palette) {
+	const uint16 *_palette /* unused */) {
 
 	BaseCostumeRenderer::ByleRLEData &compData = *pcompData;
 
@@ -1420,46 +1119,14 @@ void ByleRLEDecode_Scaled_Classic_SMask(
 			assert(compData.x >= compData.boundsRect.left && compData.x < compData.boundsRect.right);
 
 			do {
-				if (_scaleY == 255 || compData.scaleTable[scaleIndexY++ & compData.scaleIndexMask] < _scaleY) {
+				if (compData.scaleTable[scaleIndexY++ & compData.scaleIndexMask] < _scaleY) {
 					if (color) {
 						const bool masked = (y < compData.boundsRect.top || y >= compData.boundsRect.bottom)
-							|| (*mask & maskbit);
+						|| (*mask & maskbit);
 
 						if (!masked) {
-							uint16 pcolor;
-
-							switch(shadowMode) {
-							case ShadowMode::Mode0:
-								*dst = _palette[color];
-								break;
-
-							case ShadowMode::Classic:
-								if (lastColumnX != compData.x)
-									*dst = _shadowTable[*dst];
-								break;
-
-							case ShadowMode::Mode1:
-								pcolor = _palette[color];
-								if (pcolor == 13 && _shadowTable) {
-									if (lastColumnX != compData.x)
-										*dst = _shadowTable[*dst];
-								} else {
-									*dst = pcolor;
-								}
-								break;
-
-							case ShadowMode::Mode3:
-								pcolor = _palette[color];
-								if (pcolor < 8) {
-									if (lastColumnX != compData.x) {
-										pcolor = (pcolor << 8) + *dst;
-										*dst = _shadowTable[pcolor];
-									}
-								} else {
-									*dst = pcolor;
-								}
-								break;
-							}
+							if (lastColumnX != compData.x)
+								*dst = _shadowTable[*dst];
 						}
 					}
 					dst += pitch;
@@ -1477,7 +1144,7 @@ void ByleRLEDecode_Scaled_Classic_SMask(
 				scaleIndexY = compData.scaleYIndex;
 				lastColumnX = compData.x;
 
-				if (_scaleX == 255 || compData.scaleTable[compData.scaleXIndex] < _scaleX) {
+				if (compData.scaleTable[compData.scaleXIndex] < _scaleX) {
 					compData.x += compData.scaleXStep;
 					maskbit = revBitMask(compData.x & 7);
 					compData.destPtr += compData.scaleXStep;
@@ -1500,7 +1167,6 @@ typedef void (*ByleRLEDecodeFunc)(
 	const int _height,
 	const int pitch,
 	const int _numStrips,
-	const ShadowMode shadowMode,
 	const byte *_srcPtr,
 	const byte *_shadowTable,
 	const uint16 *_palette);
@@ -1548,7 +1214,6 @@ void BaseCostumeRenderer::byleRLEDecodeFast(ByleRLEData &compData) {
 				_height,
 				_out.pitch,
 				_numStrips,
-				shadowMode,
 				_srcPtr,
 				_shadowTable,
 				_palette);
@@ -1560,7 +1225,6 @@ void BaseCostumeRenderer::byleRLEDecodeFast(ByleRLEData &compData) {
 				_height,
 				_out.pitch,
 				_numStrips,
-				shadowMode,
 				_srcPtr,
 				_shadowTable,
 				_palette);


Commit: f0011e4729538e131eae6908f1f06e14079d371c
    https://github.com/scummvm/scummvm/commit/f0011e4729538e131eae6908f1f06e14079d371c
Author: Miro Kropacek (miro.kropacek at gmail.com)
Date: 2026-06-14T21:59:20+02:00

Commit Message:
SCUMM: byleRLEDecodeFast: Optimise non-scaled modes

Changed paths:
    engines/scumm/base-costume-optimised.cpp


diff --git a/engines/scumm/base-costume-optimised.cpp b/engines/scumm/base-costume-optimised.cpp
index baa515cb3e9..02c9a878c59 100644
--- a/engines/scumm/base-costume-optimised.cpp
+++ b/engines/scumm/base-costume-optimised.cpp
@@ -309,19 +309,23 @@ void ByleRLEDecode_Mode0(
 
 			assert(compData.x >= compData.boundsRect.left && compData.x < compData.boundsRect.right);
 
-			do {
-				if (color) {
+			if (color) {
+				do {
 					const bool masked = (y < compData.boundsRect.top || y >= compData.boundsRect.bottom)
 					|| (*mask & maskbit);
 
 					if (!masked) {
 						*dst = _palette[color];
 					}
-				}
-				dst += pitch;
-				mask += _numStrips;
-				y++;
-			} while (--batch);
+					dst += pitch;
+					mask += _numStrips;
+					y++;
+				} while (--batch);
+			} else {
+				dst += batch * pitch;
+				mask += batch * _numStrips;
+				y += batch;
+			}
 
 			if (height == 0) {
 				if (--compData.skipWidth == 0)
@@ -388,8 +392,8 @@ void ByleRLEDecode_Mode1(
 
 			assert(compData.x >= compData.boundsRect.left && compData.x < compData.boundsRect.right);
 
-			do {
-				if (color) {
+			if (color) {
+				do {
 					const bool masked = (y < compData.boundsRect.top || y >= compData.boundsRect.bottom)
 					|| (*mask & maskbit);
 
@@ -404,11 +408,15 @@ void ByleRLEDecode_Mode1(
 							*dst = pcolor;
 						}
 					}
-				}
-				dst += pitch;
-				mask += _numStrips;
-				y++;
-			} while (--batch);
+					dst += pitch;
+					mask += _numStrips;
+					y++;
+				} while (--batch);
+			} else {
+				dst += batch * pitch;
+				mask += batch * _numStrips;
+				y += batch;
+			}
 
 			if (height == 0) {
 				if (--compData.skipWidth == 0)
@@ -477,8 +485,8 @@ void ByleRLEDecode_Mode3(
 
 			assert(compData.x >= compData.boundsRect.left && compData.x < compData.boundsRect.right);
 
-			do {
-				if (color) {
+			if (color) {
+				do {
 					const bool masked = (y < compData.boundsRect.top || y >= compData.boundsRect.bottom)
 					|| (*mask & maskbit);
 
@@ -495,11 +503,15 @@ void ByleRLEDecode_Mode3(
 							*dst = pcolor;
 						}
 					}
-				}
-				dst += pitch;
-				mask += _numStrips;
-				y++;
-			} while (--batch);
+					dst += pitch;
+					mask += _numStrips;
+					y++;
+				} while (--batch);
+			} else {
+				dst += batch * pitch;
+				mask += batch * _numStrips;
+				y += batch;
+			}
 
 			if (height == 0) {
 				if (--compData.skipWidth == 0)
@@ -568,8 +580,8 @@ void ByleRLEDecode_Classic(
 
 			assert(compData.x >= compData.boundsRect.left && compData.x < compData.boundsRect.right);
 
-			do {
-				if (color) {
+			if (color) {
+				do {
 					const bool masked = (y < compData.boundsRect.top || y >= compData.boundsRect.bottom)
 					|| (*mask & maskbit);
 
@@ -577,11 +589,15 @@ void ByleRLEDecode_Classic(
 						if (lastColumnX != compData.x)
 							*dst = _shadowTable[*dst];
 					}
-				}
-				dst += pitch;
-				mask += _numStrips;
-				y++;
-			} while (--batch);
+					dst += pitch;
+					mask += _numStrips;
+					y++;
+				} while (--batch);
+			} else {
+				dst += batch * pitch;
+				mask += batch * _numStrips;
+				y += batch;
+			}
 
 			if (height == 0) {
 				if (--compData.skipWidth == 0)


Commit: ad29b12637a8bfa52453c9eac96dee665f1a3a3a
    https://github.com/scummvm/scummvm/commit/ad29b12637a8bfa52453c9eac96dee665f1a3a3a
Author: Miro Kropacek (miro.kropacek at gmail.com)
Date: 2026-06-14T21:59:20+02:00

Commit Message:
SCUMM: byleRLEDecodeFast: Move Y clipping to caller

Changed paths:
    engines/scumm/base-costume-optimised.cpp


diff --git a/engines/scumm/base-costume-optimised.cpp b/engines/scumm/base-costume-optimised.cpp
index 02c9a878c59..bf63751a23b 100644
--- a/engines/scumm/base-costume-optimised.cpp
+++ b/engines/scumm/base-costume-optimised.cpp
@@ -277,19 +277,17 @@ void ByleRLEDecode_Mode0(
 	BaseCostumeRenderer::ByleRLEData &compData = *pcompData;
 
 	const byte *src = _srcPtr;
-	byte *dst = compData.destPtr;
 
-	byte len = compData.repLen;
+	uint16 len = compData.repLen;
 	uint16 color = compData.repColor;
 
 	// reset every column
-	int y = compData.y;
+	byte *dst = compData.destPtr;
 	uint16 height = _height;
-
 	byte maskbit = revBitMask(compData.x & 7);
 	const byte *mask = compData.maskPtr + compData.x / 8;
 
-	byte batch;
+	uint16 batch;
 	if (len) {
 		--len;
 		goto StartPos;
@@ -303,7 +301,7 @@ void ByleRLEDecode_Mode0(
 			len = *src++;
 
 		do {
-			batch = height < len ? (byte)height : len;
+			batch = height < len ? height : len;
 			len -= batch;
 			height -= batch;
 
@@ -311,27 +309,20 @@ void ByleRLEDecode_Mode0(
 
 			if (color) {
 				do {
-					const bool masked = (y < compData.boundsRect.top || y >= compData.boundsRect.bottom)
-					|| (*mask & maskbit);
-
-					if (!masked) {
+					if (!(*mask & maskbit))
 						*dst = _palette[color];
-					}
-					dst += pitch;
+					dst  += pitch;
 					mask += _numStrips;
-					y++;
 				} while (--batch);
 			} else {
-				dst += batch * pitch;
+				dst  += batch * pitch;
 				mask += batch * _numStrips;
-				y += batch;
 			}
 
 			if (height == 0) {
 				if (--compData.skipWidth == 0)
 					return;
 				height = _height;
-				y = compData.y;
 
 				compData.x += compData.scaleXStep;
 				maskbit = revBitMask(compData.x & 7);
@@ -359,20 +350,18 @@ void ByleRLEDecode_Mode1(
 	BaseCostumeRenderer::ByleRLEData &compData = *pcompData;
 
 	const byte *src = _srcPtr;
-	byte *dst = compData.destPtr;
 
-	byte len = compData.repLen;
+	uint16 len = compData.repLen;
 	uint16 color = compData.repColor;
 
 	// reset every column
+	byte *dst = compData.destPtr;
 	int lastColumnX = -1;
-	int y = compData.y;
 	uint16 height = _height;
-
 	byte maskbit = revBitMask(compData.x & 7);
 	const byte *mask = compData.maskPtr + compData.x / 8;
 
-	byte batch;
+	uint16 batch;
 	if (len) {
 		--len;
 		goto StartPos;
@@ -386,7 +375,7 @@ void ByleRLEDecode_Mode1(
 			len = *src++;
 
 		do {
-			batch = height < len ? (byte)height : len;
+			batch = height < len ? height : len;
 			len -= batch;
 			height -= batch;
 
@@ -394,13 +383,8 @@ void ByleRLEDecode_Mode1(
 
 			if (color) {
 				do {
-					const bool masked = (y < compData.boundsRect.top || y >= compData.boundsRect.bottom)
-					|| (*mask & maskbit);
-
-					if (!masked) {
-						uint16 pcolor;
-
-						pcolor = _palette[color];
+					if (!(*mask & maskbit)) {
+						uint16 pcolor = _palette[color];
 						if (pcolor == 13 && _shadowTable) {
 							if (lastColumnX != compData.x)
 								*dst = _shadowTable[*dst];
@@ -408,21 +392,18 @@ void ByleRLEDecode_Mode1(
 							*dst = pcolor;
 						}
 					}
-					dst += pitch;
+					dst  += pitch;
 					mask += _numStrips;
-					y++;
 				} while (--batch);
 			} else {
-				dst += batch * pitch;
+				dst  += batch * pitch;
 				mask += batch * _numStrips;
-				y += batch;
 			}
 
 			if (height == 0) {
 				if (--compData.skipWidth == 0)
 					return;
 				height = _height;
-				y = compData.y;
 
 				lastColumnX = compData.x;
 
@@ -452,20 +433,18 @@ void ByleRLEDecode_Mode3(
 	BaseCostumeRenderer::ByleRLEData &compData = *pcompData;
 
 	const byte *src = _srcPtr;
-	byte *dst = compData.destPtr;
 
-	byte len = compData.repLen;
+	uint16 len = compData.repLen;
 	uint16 color = compData.repColor;
 
 	// reset every column
+	byte *dst = compData.destPtr;
 	int lastColumnX = -1;
-	int y = compData.y;
 	uint16 height = _height;
-
 	byte maskbit = revBitMask(compData.x & 7);
 	const byte *mask = compData.maskPtr + compData.x / 8;
 
-	byte batch;
+	uint16 batch;
 	if (len) {
 		--len;
 		goto StartPos;
@@ -479,7 +458,7 @@ void ByleRLEDecode_Mode3(
 			len = *src++;
 
 		do {
-			batch = height < len ? (byte)height : len;
+			batch = height < len ? height : len;
 			len -= batch;
 			height -= batch;
 
@@ -487,13 +466,8 @@ void ByleRLEDecode_Mode3(
 
 			if (color) {
 				do {
-					const bool masked = (y < compData.boundsRect.top || y >= compData.boundsRect.bottom)
-					|| (*mask & maskbit);
-
-					if (!masked) {
-						uint16 pcolor;
-
-						pcolor = _palette[color];
+					if (!(*mask & maskbit)) {
+						uint16 pcolor = _palette[color];
 						if (pcolor < 8) {
 							if (lastColumnX != compData.x) {
 								pcolor = (pcolor << 8) + *dst;
@@ -503,21 +477,18 @@ void ByleRLEDecode_Mode3(
 							*dst = pcolor;
 						}
 					}
-					dst += pitch;
+					dst  += pitch;
 					mask += _numStrips;
-					y++;
 				} while (--batch);
 			} else {
-				dst += batch * pitch;
+				dst  += batch * pitch;
 				mask += batch * _numStrips;
-				y += batch;
 			}
 
 			if (height == 0) {
 				if (--compData.skipWidth == 0)
 					return;
 				height = _height;
-				y = compData.y;
 
 				lastColumnX = compData.x;
 
@@ -547,20 +518,18 @@ void ByleRLEDecode_Classic(
 	BaseCostumeRenderer::ByleRLEData &compData = *pcompData;
 
 	const byte *src = _srcPtr;
-	byte *dst = compData.destPtr;
 
-	byte len = compData.repLen;
+	uint16 len = compData.repLen;
 	uint16 color = compData.repColor;
 
 	// reset every column
+	byte *dst = compData.destPtr;
 	int lastColumnX = -1;
-	int y = compData.y;
 	uint16 height = _height;
-
 	byte maskbit = revBitMask(compData.x & 7);
 	const byte *mask = compData.maskPtr + compData.x / 8;
 
-	byte batch;
+	uint16 batch;
 	if (len) {
 		--len;
 		goto StartPos;
@@ -574,7 +543,7 @@ void ByleRLEDecode_Classic(
 			len = *src++;
 
 		do {
-			batch = height < len ? (byte)height : len;
+			batch = height < len ? height : len;
 			len -= batch;
 			height -= batch;
 
@@ -582,28 +551,22 @@ void ByleRLEDecode_Classic(
 
 			if (color) {
 				do {
-					const bool masked = (y < compData.boundsRect.top || y >= compData.boundsRect.bottom)
-					|| (*mask & maskbit);
-
-					if (!masked) {
+					if (!(*mask & maskbit)) {
 						if (lastColumnX != compData.x)
 							*dst = _shadowTable[*dst];
 					}
-					dst += pitch;
+					dst  += pitch;
 					mask += _numStrips;
-					y++;
 				} while (--batch);
 			} else {
-				dst += batch * pitch;
+				dst  += batch * pitch;
 				mask += batch * _numStrips;
-				y += batch;
 			}
 
 			if (height == 0) {
 				if (--compData.skipWidth == 0)
 					return;
 				height = _height;
-				y = compData.y;
 
 				lastColumnX = compData.x;
 
@@ -633,16 +596,14 @@ void ByleRLEDecode_Scaled_Mode0(
 	BaseCostumeRenderer::ByleRLEData &compData = *pcompData;
 
 	const byte *src = _srcPtr;
-	byte *dst = compData.destPtr;
 
 	byte len = compData.repLen;
 	uint16 color = compData.repColor;
 
 	// reset every column
-	int y = compData.y;
+	byte *dst = compData.destPtr;
 	uint16 height = _height;
 	int scaleIndexY = compData.scaleYIndex;
-
 	byte maskbit = revBitMask(compData.x & 7);
 	const byte *mask = compData.maskPtr + compData.x / 8;
 
@@ -669,16 +630,11 @@ void ByleRLEDecode_Scaled_Mode0(
 			do {
 				if (compData.scaleTable[scaleIndexY++] < _scaleY) {
 					if (color) {
-						const bool masked = (y < compData.boundsRect.top || y >= compData.boundsRect.bottom)
-						|| (*mask & maskbit);
-
-						if (!masked) {
+						if (!(*mask & maskbit))
 							*dst = _palette[color];
-						}
 					}
 					dst += pitch;
 					mask += _numStrips;
-					y++;
 				}
 			} while (--batch);
 
@@ -686,7 +642,6 @@ void ByleRLEDecode_Scaled_Mode0(
 				if (--compData.skipWidth == 0)
 					return;
 				height = _height;
-				y = compData.y;
 
 				scaleIndexY = compData.scaleYIndex;
 
@@ -720,16 +675,14 @@ void ByleRLEDecode_Scaled_Mode0_SMask(
 	BaseCostumeRenderer::ByleRLEData &compData = *pcompData;
 
 	const byte *src = _srcPtr;
-	byte *dst = compData.destPtr;
 
 	byte len = compData.repLen;
 	uint16 color = compData.repColor;
 
 	// reset every column
-	int y = compData.y;
+	byte *dst = compData.destPtr;
 	uint16 height = _height;
 	int scaleIndexY = compData.scaleYIndex;
-
 	byte maskbit = revBitMask(compData.x & 7);
 	const byte *mask = compData.maskPtr + compData.x / 8;
 
@@ -756,16 +709,11 @@ void ByleRLEDecode_Scaled_Mode0_SMask(
 			do {
 				if (compData.scaleTable[scaleIndexY++ & compData.scaleIndexMask] < _scaleY) {
 					if (color) {
-						const bool masked = (y < compData.boundsRect.top || y >= compData.boundsRect.bottom)
-						|| (*mask & maskbit);
-
-						if (!masked) {
+						if (!(*mask & maskbit))
 							*dst = _palette[color];
-						}
 					}
 					dst += pitch;
 					mask += _numStrips;
-					y++;
 				}
 			} while (--batch);
 
@@ -773,7 +721,6 @@ void ByleRLEDecode_Scaled_Mode0_SMask(
 				if (--compData.skipWidth == 0)
 					return;
 				height = _height;
-				y = compData.y;
 
 				scaleIndexY = compData.scaleYIndex;
 
@@ -807,17 +754,15 @@ void ByleRLEDecode_Scaled_Mode1(
 	BaseCostumeRenderer::ByleRLEData &compData = *pcompData;
 
 	const byte *src = _srcPtr;
-	byte *dst = compData.destPtr;
 
 	byte len = compData.repLen;
 	uint16 color = compData.repColor;
 
 	// reset every column
+	byte *dst = compData.destPtr;
 	int lastColumnX = -1;
-	int y = compData.y;
 	uint16 height = _height;
 	int scaleIndexY = compData.scaleYIndex;
-
 	byte maskbit = revBitMask(compData.x & 7);
 	const byte *mask = compData.maskPtr + compData.x / 8;
 
@@ -844,10 +789,7 @@ void ByleRLEDecode_Scaled_Mode1(
 			do {
 				if (compData.scaleTable[scaleIndexY++] < _scaleY) {
 					if (color) {
-						const bool masked = (y < compData.boundsRect.top || y >= compData.boundsRect.bottom)
-						|| (*mask & maskbit);
-
-						if (!masked) {
+						if (!(*mask & maskbit)) {
 							uint16 pcolor;
 
 							pcolor = _palette[color];
@@ -861,7 +803,6 @@ void ByleRLEDecode_Scaled_Mode1(
 					}
 					dst += pitch;
 					mask += _numStrips;
-					y++;
 				}
 			} while (--batch);
 
@@ -869,7 +810,6 @@ void ByleRLEDecode_Scaled_Mode1(
 				if (--compData.skipWidth == 0)
 					return;
 				height = _height;
-				y = compData.y;
 
 				scaleIndexY = compData.scaleYIndex;
 				lastColumnX = compData.x;
@@ -904,17 +844,15 @@ void ByleRLEDecode_Scaled_Mode1_SMask(
 	BaseCostumeRenderer::ByleRLEData &compData = *pcompData;
 
 	const byte *src = _srcPtr;
-	byte *dst = compData.destPtr;
 
 	byte len = compData.repLen;
 	uint16 color = compData.repColor;
 
 	// reset every column
+	byte *dst = compData.destPtr;
 	int lastColumnX = -1;
-	int y = compData.y;
 	uint16 height = _height;
 	int scaleIndexY = compData.scaleYIndex;
-
 	byte maskbit = revBitMask(compData.x & 7);
 	const byte *mask = compData.maskPtr + compData.x / 8;
 
@@ -941,10 +879,7 @@ void ByleRLEDecode_Scaled_Mode1_SMask(
 			do {
 				if (compData.scaleTable[scaleIndexY++ & compData.scaleIndexMask] < _scaleY) {
 					if (color) {
-						const bool masked = (y < compData.boundsRect.top || y >= compData.boundsRect.bottom)
-						|| (*mask & maskbit);
-
-						if (!masked) {
+						if (!(*mask & maskbit)) {
 							uint16 pcolor;
 
 							pcolor = _palette[color];
@@ -958,7 +893,6 @@ void ByleRLEDecode_Scaled_Mode1_SMask(
 					}
 					dst += pitch;
 					mask += _numStrips;
-					y++;
 				}
 			} while (--batch);
 
@@ -966,7 +900,6 @@ void ByleRLEDecode_Scaled_Mode1_SMask(
 				if (--compData.skipWidth == 0)
 					return;
 				height = _height;
-				y = compData.y;
 
 				scaleIndexY = compData.scaleYIndex;
 				lastColumnX = compData.x;
@@ -1001,17 +934,15 @@ void ByleRLEDecode_Scaled_Mode3(
 	BaseCostumeRenderer::ByleRLEData &compData = *pcompData;
 
 	const byte *src = _srcPtr;
-	byte *dst = compData.destPtr;
 
 	byte len = compData.repLen;
 	uint16 color = compData.repColor;
 
 	// reset every column
+	byte *dst = compData.destPtr;
 	int lastColumnX = -1;
-	int y = compData.y;
 	uint16 height = _height;
 	int scaleIndexY = compData.scaleYIndex;
-
 	byte maskbit = revBitMask(compData.x & 7);
 	const byte *mask = compData.maskPtr + compData.x / 8;
 
@@ -1038,10 +969,7 @@ void ByleRLEDecode_Scaled_Mode3(
 			do {
 				if (compData.scaleTable[scaleIndexY++] < _scaleY) {
 					if (color) {
-						const bool masked = (y < compData.boundsRect.top || y >= compData.boundsRect.bottom)
-						|| (*mask & maskbit);
-
-						if (!masked) {
+						if (!(*mask & maskbit)) {
 							uint16 pcolor;
 
 							pcolor = _palette[color];
@@ -1057,7 +985,6 @@ void ByleRLEDecode_Scaled_Mode3(
 					}
 					dst += pitch;
 					mask += _numStrips;
-					y++;
 				}
 			} while (--batch);
 
@@ -1065,7 +992,6 @@ void ByleRLEDecode_Scaled_Mode3(
 				if (--compData.skipWidth == 0)
 					return;
 				height = _height;
-				y = compData.y;
 
 				scaleIndexY = compData.scaleYIndex;
 				lastColumnX = compData.x;
@@ -1100,17 +1026,15 @@ void ByleRLEDecode_Scaled_Classic_SMask(
 	BaseCostumeRenderer::ByleRLEData &compData = *pcompData;
 
 	const byte *src = _srcPtr;
-	byte *dst = compData.destPtr;
 
 	byte len = compData.repLen;
 	uint16 color = compData.repColor;
 
 	// reset every column
+	byte *dst = compData.destPtr;
 	int lastColumnX = -1;
-	int y = compData.y;
 	uint16 height = _height;
 	int scaleIndexY = compData.scaleYIndex;
-
 	byte maskbit = revBitMask(compData.x & 7);
 	const byte *mask = compData.maskPtr + compData.x / 8;
 
@@ -1137,17 +1061,13 @@ void ByleRLEDecode_Scaled_Classic_SMask(
 			do {
 				if (compData.scaleTable[scaleIndexY++ & compData.scaleIndexMask] < _scaleY) {
 					if (color) {
-						const bool masked = (y < compData.boundsRect.top || y >= compData.boundsRect.bottom)
-						|| (*mask & maskbit);
-
-						if (!masked) {
+						if (!(*mask & maskbit)) {
 							if (lastColumnX != compData.x)
 								*dst = _shadowTable[*dst];
 						}
 					}
 					dst += pitch;
 					mask += _numStrips;
-					y++;
 				}
 			} while (--batch);
 
@@ -1155,7 +1075,6 @@ void ByleRLEDecode_Scaled_Classic_SMask(
 				if (--compData.skipWidth == 0)
 					return;
 				height = _height;
-				y = compData.y;
 
 				scaleIndexY = compData.scaleYIndex;
 				lastColumnX = compData.x;
@@ -1219,10 +1138,9 @@ void BaseCostumeRenderer::byleRLEDecodeFast(ByleRLEData &compData) {
 			shadowMode = ShadowMode::Mode3;
 	}
 
-	{
+	if (compData.y >= compData.boundsRect.top && compData.y + _height <= compData.boundsRect.bottom) {
 		const int scaled = (_scaleX != 255 || _scaleY != 255);
-		const int useScaleIndexMask = compData.scaleIndexMask != -1;
-		if (!scaled)
+		if (!scaled) {
 			byleRLEDecodeNoScaleTable[static_cast<int>(shadowMode)](
 				&compData,
 				_scaleX,
@@ -1233,7 +1151,9 @@ void BaseCostumeRenderer::byleRLEDecodeFast(ByleRLEData &compData) {
 				_srcPtr,
 				_shadowTable,
 				_palette);
-		else
+		} else {
+			const int useScaleIndexMask = compData.scaleIndexMask != -1;
+
 			byleRLEDecodeScaledTable[(static_cast<int>(shadowMode) << 1) | useScaleIndexMask](
 				&compData,
 				_scaleX,
@@ -1244,21 +1164,21 @@ void BaseCostumeRenderer::byleRLEDecodeFast(ByleRLEData &compData) {
 				_srcPtr,
 				_shadowTable,
 				_palette);
+		}
 		return;
 	}
 
 	const byte *src = _srcPtr;
-	byte *dst = compData.destPtr;
 
 	byte len = compData.repLen;
 	uint16 color = compData.repColor;
 
 	// reset every column
+	byte *dst = compData.destPtr;
 	int lastColumnX = -1;
 	int y = compData.y;
 	uint16 height = _height;
 	int scaleIndexY = compData.scaleYIndex;
-
 	byte maskbit = revBitMask(compData.x & 7);
 	const byte *mask = compData.maskPtr + compData.x / 8;
 


Commit: 907150addb3dd917b5341e135f94d69b95fc7f05
    https://github.com/scummvm/scummvm/commit/907150addb3dd917b5341e135f94d69b95fc7f05
Author: Miro Kropacek (miro.kropacek at gmail.com)
Date: 2026-06-14T21:59:20+02:00

Commit Message:
SCUMM: byleRLEDecodeFast: Optimise non-scaled modes even more

Non-scaled modes have always the next x different from previous one.
Should have been removed as part of "SCUMM: byleRLEDecodeFast: Remove
dead code".

Changed paths:
    engines/scumm/base-costume-optimised.cpp


diff --git a/engines/scumm/base-costume-optimised.cpp b/engines/scumm/base-costume-optimised.cpp
index bf63751a23b..db99330e2ea 100644
--- a/engines/scumm/base-costume-optimised.cpp
+++ b/engines/scumm/base-costume-optimised.cpp
@@ -356,7 +356,6 @@ void ByleRLEDecode_Mode1(
 
 	// reset every column
 	byte *dst = compData.destPtr;
-	int lastColumnX = -1;
 	uint16 height = _height;
 	byte maskbit = revBitMask(compData.x & 7);
 	const byte *mask = compData.maskPtr + compData.x / 8;
@@ -386,8 +385,7 @@ void ByleRLEDecode_Mode1(
 					if (!(*mask & maskbit)) {
 						uint16 pcolor = _palette[color];
 						if (pcolor == 13 && _shadowTable) {
-							if (lastColumnX != compData.x)
-								*dst = _shadowTable[*dst];
+							*dst = _shadowTable[*dst];
 						} else {
 							*dst = pcolor;
 						}
@@ -405,8 +403,6 @@ void ByleRLEDecode_Mode1(
 					return;
 				height = _height;
 
-				lastColumnX = compData.x;
-
 				compData.x += compData.scaleXStep;
 				maskbit = revBitMask(compData.x & 7);
 				compData.destPtr += compData.scaleXStep;
@@ -439,7 +435,6 @@ void ByleRLEDecode_Mode3(
 
 	// reset every column
 	byte *dst = compData.destPtr;
-	int lastColumnX = -1;
 	uint16 height = _height;
 	byte maskbit = revBitMask(compData.x & 7);
 	const byte *mask = compData.maskPtr + compData.x / 8;
@@ -469,10 +464,8 @@ void ByleRLEDecode_Mode3(
 					if (!(*mask & maskbit)) {
 						uint16 pcolor = _palette[color];
 						if (pcolor < 8) {
-							if (lastColumnX != compData.x) {
-								pcolor = (pcolor << 8) + *dst;
-								*dst = _shadowTable[pcolor];
-							}
+							pcolor = (pcolor << 8) + *dst;
+							*dst = _shadowTable[pcolor];
 						} else {
 							*dst = pcolor;
 						}
@@ -490,8 +483,6 @@ void ByleRLEDecode_Mode3(
 					return;
 				height = _height;
 
-				lastColumnX = compData.x;
-
 				compData.x += compData.scaleXStep;
 				maskbit = revBitMask(compData.x & 7);
 				compData.destPtr += compData.scaleXStep;
@@ -524,7 +515,6 @@ void ByleRLEDecode_Classic(
 
 	// reset every column
 	byte *dst = compData.destPtr;
-	int lastColumnX = -1;
 	uint16 height = _height;
 	byte maskbit = revBitMask(compData.x & 7);
 	const byte *mask = compData.maskPtr + compData.x / 8;
@@ -551,10 +541,8 @@ void ByleRLEDecode_Classic(
 
 			if (color) {
 				do {
-					if (!(*mask & maskbit)) {
-						if (lastColumnX != compData.x)
-							*dst = _shadowTable[*dst];
-					}
+					if (!(*mask & maskbit))
+						*dst = _shadowTable[*dst];
 					dst  += pitch;
 					mask += _numStrips;
 				} while (--batch);
@@ -568,8 +556,6 @@ void ByleRLEDecode_Classic(
 					return;
 				height = _height;
 
-				lastColumnX = compData.x;
-
 				compData.x += compData.scaleXStep;
 				maskbit = revBitMask(compData.x & 7);
 				compData.destPtr += compData.scaleXStep;


Commit: 783339e94c29a9f48e7df50c7ad02ba11a61d1cf
    https://github.com/scummvm/scummvm/commit/783339e94c29a9f48e7df50c7ad02ba11a61d1cf
Author: Miro Kropacek (miro.kropacek at gmail.com)
Date: 2026-06-14T21:59:20+02:00

Commit Message:
SCUMM: byleRLEDecodeFast: Proper Y clipping in scaled modes

This reduces 'false positives' where clipping functions would be called
instead of the faster ones.

Changed paths:
    engines/scumm/base-costume-optimised.cpp
    engines/scumm/base-costume.h


diff --git a/engines/scumm/base-costume-optimised.cpp b/engines/scumm/base-costume-optimised.cpp
index db99330e2ea..18eb9c4370e 100644
--- a/engines/scumm/base-costume-optimised.cpp
+++ b/engines/scumm/base-costume-optimised.cpp
@@ -169,6 +169,7 @@ byte BaseCostumeRenderer::paintCelByleRLECommon(
 	compData.scaleYIndex = startScaleIndexY;
 	compData.skipWidth = _width;
 	compData.scaleXStep = _drawActorToRight ? 1 : -1;
+	compData.scaledHeight = rect.bottom - rect.top;
 
 	// All the important 'rect' values. scale sequence = 'compData.scaleTable[i] < _scaleX' result)
 	//
@@ -1124,7 +1125,7 @@ void BaseCostumeRenderer::byleRLEDecodeFast(ByleRLEData &compData) {
 			shadowMode = ShadowMode::Mode3;
 	}
 
-	if (compData.y >= compData.boundsRect.top && compData.y + _height <= compData.boundsRect.bottom) {
+	if (compData.y >= compData.boundsRect.top && compData.y + compData.scaledHeight <= compData.boundsRect.bottom) {
 		const int scaled = (_scaleX != 255 || _scaleY != 255);
 		if (!scaled) {
 			byleRLEDecodeNoScaleTable[static_cast<int>(shadowMode)](
diff --git a/engines/scumm/base-costume.h b/engines/scumm/base-costume.h
index 9ffa861c5af..1268a39a677 100644
--- a/engines/scumm/base-costume.h
+++ b/engines/scumm/base-costume.h
@@ -126,6 +126,9 @@ public:
 		Common::Rect boundsRect;
 		int scaleXIndex, scaleYIndex;
 		int scaleIndexMask;
+#ifdef SCUMM_OPTIMISED_CODE
+		int scaledHeight;
+#endif
 	};
 
     BaseCostumeRenderer(ScummEngine *scumm, bool akosRendering = false) {


Commit: 2390a071b95398d3ccd510c1cff18e67e7a593e4
    https://github.com/scummvm/scummvm/commit/2390a071b95398d3ccd510c1cff18e67e7a593e4
Author: Miro Kropacek (miro.kropacek at gmail.com)
Date: 2026-06-14T21:59:20+02:00

Commit Message:
SCUMM: byleRLEDecodeFast: Fast path for Mode1

Mode1 with _shadowTable == NULL is equal to Mode0. Used in early SCUMM
games like Zak, Maniac or Loom. Reduces one if per pixel.

Changed paths:
    engines/scumm/base-costume-optimised.cpp


diff --git a/engines/scumm/base-costume-optimised.cpp b/engines/scumm/base-costume-optimised.cpp
index 18eb9c4370e..1a8d8ae1b90 100644
--- a/engines/scumm/base-costume-optimised.cpp
+++ b/engines/scumm/base-costume-optimised.cpp
@@ -385,7 +385,7 @@ void ByleRLEDecode_Mode1(
 				do {
 					if (!(*mask & maskbit)) {
 						uint16 pcolor = _palette[color];
-						if (pcolor == 13 && _shadowTable) {
+						if (pcolor == 13) {
 							*dst = _shadowTable[*dst];
 						} else {
 							*dst = pcolor;
@@ -780,7 +780,7 @@ void ByleRLEDecode_Scaled_Mode1(
 							uint16 pcolor;
 
 							pcolor = _palette[color];
-							if (pcolor == 13 && _shadowTable) {
+							if (pcolor == 13) {
 								if (lastColumnX != compData.x)
 									*dst = _shadowTable[*dst];
 							} else {
@@ -870,7 +870,7 @@ void ByleRLEDecode_Scaled_Mode1_SMask(
 							uint16 pcolor;
 
 							pcolor = _palette[color];
-							if (pcolor == 13 && _shadowTable) {
+							if (pcolor == 13) {
 								if (lastColumnX != compData.x)
 									*dst = _shadowTable[*dst];
 							} else {
@@ -1116,7 +1116,7 @@ void BaseCostumeRenderer::byleRLEDecodeFast(ByleRLEData &compData) {
 	if (!_akosRendering) {
 		if (_shadowMode & 0x20)
 			shadowMode = ShadowMode::Classic;
-		else
+		else if (_shadowTable)
 			shadowMode = ShadowMode::Mode1;
 	} else {
 		if (_shadowMode == 1)
@@ -1210,7 +1210,7 @@ void BaseCostumeRenderer::byleRLEDecodeFast(ByleRLEData &compData) {
 
 							case ShadowMode::Mode1:
 								pcolor = _palette[color];
-								if (pcolor == 13 && _shadowTable) {
+								if (pcolor == 13) {
 									if (lastColumnX != compData.x)
 										*dst = _shadowTable[*dst];
 								} else {


Commit: 4f5361a2f75926c54191d2f3e4c9ad816128e66b
    https://github.com/scummvm/scummvm/commit/4f5361a2f75926c54191d2f3e4c9ad816128e66b
Author: Miro Kropacek (miro.kropacek at gmail.com)
Date: 2026-06-14T21:59:20+02:00

Commit Message:
SCUMM: byleRLEDecodeFast: m68k assembly functions

Changed paths:
  A engines/scumm/bylerledecodeM68K.h
  A engines/scumm/m68k/bylerledecode.inc
  A engines/scumm/m68k/bylerledecode_classic.S
  A engines/scumm/m68k/bylerledecode_mode0.S
  A engines/scumm/m68k/bylerledecode_mode1.S
  A engines/scumm/m68k/bylerledecode_mode3.S
  A engines/scumm/m68k/bylerledecode_scaled_mode0.S
  A engines/scumm/m68k/bylerledecode_scaled_mode0_smask.S
  A engines/scumm/m68k/bylerledecode_scaled_mode1.S
  A engines/scumm/m68k/bylerledecode_scaled_mode1_smask.S
  A engines/scumm/m68k/bylerledecode_scaled_mode3.S
    configure
    engines/scumm/base-costume-optimised.cpp
    engines/scumm/base-costume.cpp
    engines/scumm/module.mk


diff --git a/configure b/configure
index b454d2dcf41..0739c9d6ae2 100755
--- a/configure
+++ b/configure
@@ -4256,6 +4256,7 @@ case $_backend in
 		;;
 	atari)
 		append_var DEFINES "-DATARI"
+		define_in_config_if_yes yes "USE_M68K_COSTUME_ASM"
 		append_var DEFINES "-DDISABLE_NES_APU"
 		append_var LIBS "-lgem"
 		_ogg=no
diff --git a/engines/scumm/base-costume-optimised.cpp b/engines/scumm/base-costume-optimised.cpp
index 1a8d8ae1b90..b35c4632c06 100644
--- a/engines/scumm/base-costume-optimised.cpp
+++ b/engines/scumm/base-costume-optimised.cpp
@@ -20,12 +20,20 @@
  */
 
 
+#ifdef SCUMM_OPTIMISED_CODE
+
+#define FORCE_TEXT_CONSOLE
+
+#include "common/textconsole.h"
 #include "scumm/base-costume.h"
 #include "scumm/util.h"
 
+#ifdef USE_M68K_COSTUME_ASM
+#include "bylerledecodeM68K.h"
+#endif
+
 namespace Scumm {
 
-#ifdef SCUMM_OPTIMISED_CODE
 byte BaseCostumeRenderer::paintCelByleRLECommon(
 	int xMoveCur,
 	int yMoveCur,
@@ -264,6 +272,7 @@ enum class ShadowMode : int {
 	Classic
 };
 
+#ifndef USE_M68K_COSTUME_ASM
 void ByleRLEDecode_Mode0(
 	BaseCostumeRenderer::ByleRLEData *pcompData,
 	const byte _scaleX, /* unused */
@@ -276,6 +285,7 @@ void ByleRLEDecode_Mode0(
 	const uint16 *_palette) {
 
 	BaseCostumeRenderer::ByleRLEData &compData = *pcompData;
+	//warning("%s: unexpected call, save your game and report", __FUNCTION__);
 
 	const byte *src = _srcPtr;
 
@@ -336,7 +346,9 @@ void ByleRLEDecode_Mode0(
 		} while (len > 0);
 	} while (true);
 }
+#endif
 
+#ifndef USE_M68K_COSTUME_ASM
 void ByleRLEDecode_Mode1(
 	BaseCostumeRenderer::ByleRLEData *pcompData,
 	const byte _scaleX, /* unused */
@@ -349,6 +361,7 @@ void ByleRLEDecode_Mode1(
 	const uint16 *_palette) {
 
 	BaseCostumeRenderer::ByleRLEData &compData = *pcompData;
+	//warning("%s: unexpected call, save your game and report", __FUNCTION__);
 
 	const byte *src = _srcPtr;
 
@@ -415,7 +428,9 @@ void ByleRLEDecode_Mode1(
 		} while (len > 0);
 	} while (true);
 }
+#endif
 
+#ifndef USE_M68K_COSTUME_ASM
 void ByleRLEDecode_Mode3(
 	BaseCostumeRenderer::ByleRLEData *pcompData,
 	const byte _scaleX, /* unused */
@@ -428,6 +443,7 @@ void ByleRLEDecode_Mode3(
 	const uint16 *_palette) {
 
 	BaseCostumeRenderer::ByleRLEData &compData = *pcompData;
+	//warning("%s: unexpected call, save your game and report", __FUNCTION__);
 
 	const byte *src = _srcPtr;
 
@@ -495,7 +511,9 @@ void ByleRLEDecode_Mode3(
 		} while (len > 0);
 	} while (true);
 }
+#endif
 
+#ifndef USE_M68K_COSTUME_ASM
 void ByleRLEDecode_Classic(
 	BaseCostumeRenderer::ByleRLEData *pcompData,
 	const byte _scaleX, /* unused */
@@ -508,6 +526,7 @@ void ByleRLEDecode_Classic(
 	const uint16 *_palette /* unused */) {
 
 	BaseCostumeRenderer::ByleRLEData &compData = *pcompData;
+	//warning("%s: unexpected call, save your game and report", __FUNCTION__);
 
 	const byte *src = _srcPtr;
 
@@ -568,7 +587,9 @@ void ByleRLEDecode_Classic(
 		} while (len > 0);
 	} while (true);
 }
+#endif
 
+#ifndef USE_M68K_COSTUME_ASM
 void ByleRLEDecode_Scaled_Mode0(
 	BaseCostumeRenderer::ByleRLEData *pcompData,
 	const byte _scaleX,
@@ -581,6 +602,7 @@ void ByleRLEDecode_Scaled_Mode0(
 	const uint16 *_palette) {
 
 	BaseCostumeRenderer::ByleRLEData &compData = *pcompData;
+	//warning("%s: unexpected call, save your game and report", __FUNCTION__);
 
 	const byte *src = _srcPtr;
 
@@ -647,7 +669,9 @@ void ByleRLEDecode_Scaled_Mode0(
 		} while (len > 0);
 	} while (true);
 }
+#endif
 
+#ifndef USE_M68K_COSTUME_ASM
 void ByleRLEDecode_Scaled_Mode0_SMask(
 	BaseCostumeRenderer::ByleRLEData *pcompData,
 	const byte _scaleX,
@@ -660,6 +684,7 @@ void ByleRLEDecode_Scaled_Mode0_SMask(
 	const uint16 *_palette) {
 
 	BaseCostumeRenderer::ByleRLEData &compData = *pcompData;
+	//warning("%s: unexpected call, save your game and report", __FUNCTION__);
 
 	const byte *src = _srcPtr;
 
@@ -726,7 +751,9 @@ void ByleRLEDecode_Scaled_Mode0_SMask(
 		} while (len > 0);
 	} while (true);
 }
+#endif
 
+#ifndef USE_M68K_COSTUME_ASM
 void ByleRLEDecode_Scaled_Mode1(
 	BaseCostumeRenderer::ByleRLEData *pcompData,
 	const byte _scaleX,
@@ -739,6 +766,7 @@ void ByleRLEDecode_Scaled_Mode1(
 	const uint16 *_palette) {
 
 	BaseCostumeRenderer::ByleRLEData &compData = *pcompData;
+	//warning("%s: unexpected call, save your game and report", __FUNCTION__);
 
 	const byte *src = _srcPtr;
 
@@ -816,7 +844,9 @@ void ByleRLEDecode_Scaled_Mode1(
 		} while (len > 0);
 	} while (true);
 }
+#endif
 
+#ifndef USE_M68K_COSTUME_ASM
 void ByleRLEDecode_Scaled_Mode1_SMask(
 	BaseCostumeRenderer::ByleRLEData *pcompData,
 	const byte _scaleX,
@@ -829,6 +859,7 @@ void ByleRLEDecode_Scaled_Mode1_SMask(
 	const uint16 *_palette) {
 
 	BaseCostumeRenderer::ByleRLEData &compData = *pcompData;
+	//warning("%s: unexpected call, save your game and report", __FUNCTION__);
 
 	const byte *src = _srcPtr;
 
@@ -906,7 +937,9 @@ void ByleRLEDecode_Scaled_Mode1_SMask(
 		} while (len > 0);
 	} while (true);
 }
+#endif
 
+#ifndef USE_M68K_COSTUME_ASM
 void ByleRLEDecode_Scaled_Mode3(
 	BaseCostumeRenderer::ByleRLEData *pcompData,
 	const byte _scaleX,
@@ -919,6 +952,7 @@ void ByleRLEDecode_Scaled_Mode3(
 	const uint16 *_palette) {
 
 	BaseCostumeRenderer::ByleRLEData &compData = *pcompData;
+	//warning("%s: unexpected call, save your game and report", __FUNCTION__);
 
 	const byte *src = _srcPtr;
 
@@ -998,6 +1032,7 @@ void ByleRLEDecode_Scaled_Mode3(
 		} while (len > 0);
 	} while (true);
 }
+#endif
 
 void ByleRLEDecode_Scaled_Classic_SMask(
 	BaseCostumeRenderer::ByleRLEData *pcompData,
@@ -1011,6 +1046,7 @@ void ByleRLEDecode_Scaled_Classic_SMask(
 	const uint16 *_palette /* unused */) {
 
 	BaseCostumeRenderer::ByleRLEData &compData = *pcompData;
+	warning("%s: unexpected call, save your game and report", __FUNCTION__);
 
 	const byte *src = _srcPtr;
 
@@ -1155,6 +1191,9 @@ void BaseCostumeRenderer::byleRLEDecodeFast(ByleRLEData &compData) {
 		return;
 	}
 
+	warning("%s: unexpected call, save your game and report: %d (%d, %d, %d, %d)", __FUNCTION__, (int)shadowMode,
+			compData.y, compData.boundsRect.top, compData.y + compData.scaledHeight, compData.boundsRect.bottom);
+
 	const byte *src = _srcPtr;
 
 	byte len = compData.repLen;
@@ -1262,6 +1301,7 @@ void BaseCostumeRenderer::byleRLEDecodeFast(ByleRLEData &compData) {
 		} while (len > 0);
 	} while (true);
 }
-#endif
 
 } // End of namespace Scumm
+
+#endif
diff --git a/engines/scumm/base-costume.cpp b/engines/scumm/base-costume.cpp
index cfe12d5a481..13b32bdf578 100644
--- a/engines/scumm/base-costume.cpp
+++ b/engines/scumm/base-costume.cpp
@@ -20,6 +20,9 @@
  */
 
 
+#define FORCE_TEXT_CONSOLE
+
+#include "common/textconsole.h"
 #include "scumm/base-costume.h"
 #include "scumm/util.h"
 
@@ -294,6 +297,7 @@ void BaseCostumeRenderer::byleRLEDecode(ByleRLEData &compData, int16 actorHitX,
 		byleRLEDecodeFast(compData);
 		return;
 	}
+	warning("%s: unoptimised version is being executed", __FUNCTION__);
 #endif
 	const byte *src = _srcPtr;
 	byte *dst = compData.destPtr;
diff --git a/engines/scumm/bylerledecodeM68K.h b/engines/scumm/bylerledecodeM68K.h
new file mode 100644
index 00000000000..a7e5f254140
--- /dev/null
+++ b/engines/scumm/bylerledecodeM68K.h
@@ -0,0 +1,126 @@
+/* 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_BYLE_RLE_DECODE_M68K_H
+#define SCUMM_BYLE_RLE_DECODE_M68K_H
+
+#include "scumm/base-costume.h"
+
+extern "C" void ByleRLEDecode_Mode0(
+	Scumm::BaseCostumeRenderer::ByleRLEData *pcompData,
+	const byte _scaleX, /* unused */
+	const byte _scaleY, /* unused */
+	const int _height,
+	const int pitch,
+	const int _numStrips,
+	const byte *_srcPtr,
+	const byte *_shadowTable, /* unused */
+	const uint16 *_palette);
+
+extern "C" void ByleRLEDecode_Mode1(
+	Scumm::BaseCostumeRenderer::ByleRLEData *pcompData,
+	const byte _scaleX, /* unused */
+	const byte _scaleY, /* unused */
+	const int _height,
+	const int pitch,
+	const int _numStrips,
+	const byte *_srcPtr,
+	const byte *_shadowTable,
+	const uint16 *_palette);
+
+extern "C" void ByleRLEDecode_Mode3(
+	Scumm::BaseCostumeRenderer::ByleRLEData *pcompData,
+	const byte _scaleX, /* unused */
+	const byte _scaleY, /* unused */
+	const int _height,
+	const int pitch,
+	const int _numStrips,
+	const byte *_srcPtr,
+	const byte *_shadowTable,
+	const uint16 *_palette);
+
+extern "C" void ByleRLEDecode_Classic(
+	Scumm::BaseCostumeRenderer::ByleRLEData *pcompData,
+	const byte _scaleX, /* unused */
+	const byte _scaleY, /* unused */
+	const int _height,
+	const int pitch,
+	const int _numStrips,
+	const byte *_srcPtr,
+	const byte *_shadowTable,
+	const uint16 *_palette /* unused */);
+
+extern "C" void ByleRLEDecode_Scaled_Mode0(
+	Scumm::BaseCostumeRenderer::ByleRLEData *pcompData,
+	const byte _scaleX,
+	const byte _scaleY,
+	const int _height,
+	const int pitch,
+	const int _numStrips,
+	const byte *_srcPtr,
+	const byte *_shadowTable, /* unused */
+	const uint16 *_palette);
+
+extern "C" void ByleRLEDecode_Scaled_Mode0_SMask(
+	Scumm::BaseCostumeRenderer::ByleRLEData *pcompData,
+	const byte _scaleX,
+	const byte _scaleY,
+	const int _height,
+	const int pitch,
+	const int _numStrips,
+	const byte *_srcPtr,
+	const byte *_shadowTable, /* unused */
+	const uint16 *_palette);
+
+extern "C" void ByleRLEDecode_Scaled_Mode1(
+	Scumm::BaseCostumeRenderer::ByleRLEData *pcompData,
+	const byte _scaleX,
+	const byte _scaleY,
+	const int _height,
+	const int pitch,
+	const int _numStrips,
+	const byte *_srcPtr,
+	const byte *_shadowTable,
+	const uint16 *_palette);
+
+extern "C" void ByleRLEDecode_Scaled_Mode1_SMask(
+	Scumm::BaseCostumeRenderer::ByleRLEData *pcompData,
+	const byte _scaleX,
+	const byte _scaleY,
+	const int _height,
+	const int pitch,
+	const int _numStrips,
+	const byte *_srcPtr,
+	const byte *_shadowTable,
+	const uint16 *_palette);
+
+extern "C" void ByleRLEDecode_Scaled_Mode3(
+	Scumm::BaseCostumeRenderer::ByleRLEData *pcompData,
+	const byte _scaleX,
+	const byte _scaleY,
+	const int _height,
+	const int pitch,
+	const int _numStrips,
+	const byte *_srcPtr,
+	const byte *_shadowTable,
+	const uint16 *_palette);
+
+#endif
diff --git a/engines/scumm/m68k/bylerledecode.inc b/engines/scumm/m68k/bylerledecode.inc
new file mode 100644
index 00000000000..c1dda9bc148
--- /dev/null
+++ b/engines/scumm/m68k/bylerledecode.inc
@@ -0,0 +1,100 @@
+/* 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/>.
+ *
+ */
+
+| kate: tab-width 8; replace-tabs off; hl Motorola 68k (VASM/Devpac);
+
+#include "../../../backends/platform/atari/symbols.h"
+
+	.struct	0
+compData.x:
+	.space	4	// int x;
+compData.y:
+	.space	4	// int y;
+compData.scaleTable:
+	.space	4	// const byte *scaleTable;
+//compData.height:
+//	.space	4	// int height;
+//compData.width:
+//	.space	4	// int width;
+
+	.space	2
+compData.skipWidth:
+	.space	2	// int skipWidth; (used as uint16)
+
+compData.destPtr:
+	.space	4	// byte *destPtr;
+compData.maskPtr:
+	.space	4	// const byte *maskPtr;
+compData.scaleXStep:
+	.space	4	// int scaleXStep;
+compData.mask:
+	.space	1	// byte mask;
+compData.shr:
+	.space	1	// byte shr;
+compData.repColor:
+	.space	1	// byte repColor;
+compData.repLen:
+	.space	1	// byte repLen;
+compData.boundsRect:
+	.space	8	// Common::Rect boundsRect; (4 x int16)
+compData.scaleXIndex:
+	.space	4	// int scaleXIndex;
+compData.scaleYIndex:
+	.space	4	// int scaleYIndex;
+compData.scaleIndexMask:
+	.space	4	// int scaleIndexMask;
+
+	.set	sizeof_local_vars,0
+	.macro	add_var name,size
+	.set	sizeof_local_vars,sizeof_local_vars+\size
+	.set	\name,-sizeof_local_vars
+	.endm
+
+	.struct 0
+arg_fp:
+	.space	4
+arg_rts:
+	.space	4
+arg_pcompData:
+	.space	4	// BaseCostumeRenderer::ByleRLEData *pcompData
+
+	.space	3
+arg_scaleX:
+	.space	1	// const byte _scaleX
+
+	.space	3
+arg_scaleY:
+	.space	1	// const byte _scaleY
+
+	.space	2
+arg_height:
+	.space	2	// const int _height (used as uint16)
+
+arg_pitch:
+	.space	4	// const int pitch
+arg_numStrips:
+	.space	4	// const int _numStrips
+arg_srcPtr:
+	.space	4	// const byte *_srcPtr
+arg_shadowTable:
+	.space	4	// const byte *_shadowTable
+arg_palette:
+	.space	4	// const uint16 *_palette
diff --git a/engines/scumm/m68k/bylerledecode_classic.S b/engines/scumm/m68k/bylerledecode_classic.S
new file mode 100644
index 00000000000..590062d7d31
--- /dev/null
+++ b/engines/scumm/m68k/bylerledecode_classic.S
@@ -0,0 +1,190 @@
+/* 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/>.
+ *
+ */
+
+| kate: tab-width 8; replace-tabs off; hl Motorola 68k (VASM/Devpac);
+
+#include "bylerledecode.inc"
+
+	.globl	SYM(ByleRLEDecode_Classic)
+
+	add_var	color,1+3	// byte color; (align stack)
+
+
+	.text
+
+| void ByleRLEDecode_Classic(
+|	BaseCostumeRenderer::ByleRLEData *pcompData,
+|	const byte _scaleX, /* unused */
+|	const byte _scaleY, /* unused */
+|	const int _height,
+|	const int pitch,
+|	const int _numStrips,
+|	const byte *_srcPtr,
+|	const byte *_shadowTable,
+|	const uint16 *_palette /* unused */);
+SYM(ByleRLEDecode_Classic):
+// TODO: __FASTCALL__
+	link	a6,#-sizeof_local_vars		| a6: frame pointer
+	movem.l	d2-d7/a2-a5,-(sp)
+
+	// registers taken so far:
+	// - d7(zero/shadow scratch), d6(len), d5(height), d4(maskbit), d3(pitch), d2(_numStrips)
+	// - a0(src), a1(dst), a2(mask), a3(destPtr), a4(_shadowTable), a5(pcompData), a6(frame pointer)
+
+	// BaseCostumeRenderer::ByleRLEData &compData = *pcompData;
+	move.l	(arg_pcompData,a6),a5		| a5: pcompData
+
+	// const byte *src = _srcPtr;
+	move.l	(arg_srcPtr,a6),a0		| a0: src
+
+	// uint16 len = compData.repLen;
+	clr.w	d6
+	move.b	(compData.repLen,a5),d6		| d6.w: len
+	// uint16 color = compData.repColor;
+	move.b	(compData.repColor,a5),(color,a6)
+
+	// byte *dst = compData.destPtr;
+	move.l	(compData.destPtr,a5),a3	| a3: compData.destPtr
+	move.l	a3,a1				| a1: dst
+	// uint16 height = _height;
+	move.w	(arg_height,a6),d5		| d5.w: height
+	// byte maskbit = revBitMask(compData.x & 7);
+	// #define revBitMask(x) (0x80 >> (x))
+	move.l	(compData.x,a5),d0
+	move.b	d0,d4
+	not.b	d4
+	and.b	#0b00000111,d4			| d4.b: bit to test
+	// const byte *mask = compData.maskPtr + compData.x / 8;
+	asr.l	#3,d0
+	move.l	(compData.maskPtr,a5),a2
+	add.l	d0,a2				| a2: mask
+
+	move.l	(arg_pitch,a6),d3		| d3.w: pitch (used as long, too)
+	move.l	(arg_numStrips,a6),d2		| d2.w: _numStrips (used as long, too)
+	move.l	(arg_shadowTable,a6),a4		| a4: _shadowTable
+	clr.l	d7				| d7.l: used for shadow table indexing
+
+	// if (len) {
+	// 	--len;
+	// 	goto StartPos;
+	// }
+	// len   --len action
+	// 0     -1    jump into main_loop
+	// 1      0    jump into main_loop = jump into len_loop (0x)
+	// 2      1    jump into len_loop (1x)
+	// 3      2    jump into len_loop (2x)
+	subq.w	#1,d6
+	bgt.b	len_loop
+main_loop:
+	// do {
+	// len = *src++;
+	// color = len >> compData.shr;
+	// len &= compData.mask;
+	// if (!len)
+	// 	len = *src++;
+	clr.w	d6				| d6.w: clear the high byte
+	move.b	(a0)+,d0
+	move.b	d0,d6
+	move.b	(compData.shr,a5),d1
+	lsr.b	d1,d0				| d0.b: color (only care about zero/nonzero)
+	move.b	d0,(color,a6)
+	and.b	(compData.mask,a5),d6		| d6.w: len
+	bne.b	len_loop
+	move.b	(a0)+,d6
+len_loop:
+	// do {
+	// 	batch = height < len ? height : len;
+	move.w	d5,d1				| d1.w: batch
+	cmp.w	d6,d1
+	blt.b	1f
+	move.w	d6,d1
+1:	// 	len -= batch;
+	// 	height -= batch;
+	sub.w	d1,d6
+	sub.w	d1,d5
+	// 	if (color) {
+	tst.b	(color,a6)
+	beq.b	2f
+
+	subq.w	#1,d1				| dbra
+batch_loop_shadow:
+	// 		do {
+	// 			if (!(*mask & maskbit))
+	btst	d4,(a2)
+	bne.b	1f
+	// 				*dst = _shadowTable[*dst];
+	move.b	(a1),d7
+	move.b	(a4,d7.l),(a1)
+1:	// 			dst  += pitch;
+	// 			mask += _numStrips;
+	add.l	d3,a1
+	add.l	d2,a2
+	// 		} while (--batch);
+	dbra	d1,batch_loop_shadow
+	bra.b	3f
+
+	// 	} else {
+2:	// 		dst  += batch * pitch;
+	move.w	d1,d0
+	mulu.w	d3,d0
+	add.l	d0,a1
+	// 		mask += batch * _numStrips;
+	mulu.w	d2,d1
+	add.l	d1,a2
+	// 	}
+
+3:	// 	if (height == 0) {
+	tst.w	d5
+	bne.b	5f
+	// 		if (--compData.skipWidth == 0)
+	subq.w	#1,(compData.skipWidth,a5)
+	bne.b	4f
+	// 			return;
+	movem.l	(sp)+,d2-d7/a2-a5
+	unlk	a6
+	rts
+
+4:	// 		height = _height;
+	move.w	(arg_height,a6),d5		| d5.w: height
+	move.l	(compData.scaleXStep,a5),d1	| d1.l: preload scaleXStep
+	// 		compData.x += compData.scaleXStep;
+	move.l	(compData.x,a5),d0
+	add.l	d1,d0
+	move.l	d0,(compData.x,a5)		| d0.l: compData.x
+	move.b	d0,d4
+	// 		maskbit = revBitMask(compData.x & 7);
+	not.b	d4
+	and.b	#0b00000111,d4			| d4.b: bit to test
+	// 		destPtr += compData.scaleXStep;
+	add.l	d1,a3
+	// 		dst = destPtr;
+	move.l	a3,a1				| a1: dst
+	// 		mask = compData.maskPtr + compData.x / 8;
+	asr.l	#3,d0
+	move.l	(compData.maskPtr,a5),a2
+	add.l	d0,a2				| a2: mask
+	// 	}
+5:	// StartPos:
+	// } while (len > 0);
+	tst.w	d6
+	bne.b	len_loop
+	// } while (true);
+	bra.w	main_loop
diff --git a/engines/scumm/m68k/bylerledecode_mode0.S b/engines/scumm/m68k/bylerledecode_mode0.S
new file mode 100644
index 00000000000..4dfb7f0f12b
--- /dev/null
+++ b/engines/scumm/m68k/bylerledecode_mode0.S
@@ -0,0 +1,191 @@
+/* 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/>.
+ *
+ */
+
+| kate: tab-width 8; replace-tabs off; hl Motorola 68k (VASM/Devpac);
+
+#include "bylerledecode.inc"
+
+	.globl	SYM(ByleRLEDecode_Mode0)
+
+	add_var	color,1+3	// byte color; (align stack)
+
+
+	.text
+
+| void ByleRLEDecode_Mode0(
+|	BaseCostumeRenderer::ByleRLEData *pcompData,
+|	const byte _scaleX, /* unused */
+|	const byte _scaleY, /* unused */
+|	const int _height,
+|	const int pitch,
+|	const int _numStrips,
+|	const byte *_srcPtr,
+|	const byte *_shadowTable, /* unused */
+|	const uint16 *_palette);
+SYM(ByleRLEDecode_Mode0):
+// TODO: __FASTCALL__
+	link	a6,#-sizeof_local_vars		| a6: frame pointer
+	movem.l	d2-d7/a2-a5,-(sp)
+
+	// registers taken so far:
+	// - d7(pcolor), d6(len), d5(height), d4(maskbit), d3(pitch), d2(_numStrips)
+	// - a0(src), a1(dst), a2(mask), a3(_palette), a4(destPtr), a5(pcompData), a6(frame pointer)
+
+	// BaseCostumeRenderer::ByleRLEData &compData = *pcompData;
+	move.l	(arg_pcompData,a6),a5		| a5: pcompData
+
+	// const byte *src = _srcPtr;
+	move.l	(arg_srcPtr,a6),a0		| a0: src
+
+	// uint16 len = compData.repLen;
+	clr.w	d6
+	move.b	(compData.repLen,a5),d6		| d6.w: len
+
+	// byte *dst = compData.destPtr;
+	move.l	(compData.destPtr,a5),a4	| a4: compData.destPtr
+	move.l	a4,a1				| a1: dst
+	// uint16 height = _height;
+	move.w	(arg_height,a6),d5		| d5.w: height
+	// byte maskbit = revBitMask(compData.x & 7);
+	// #define revBitMask(x) (0x80 >> (x))
+	move.l	(compData.x,a5),d0
+	move.b	d0,d4
+	not.b	d4
+	and.b	#0b00000111,d4			| d4.b: bit to test
+	// const byte *mask = compData.maskPtr + compData.x / 8;
+	asr.l	#3,d0
+	move.l	(compData.maskPtr,a5),a2
+	add.l	d0,a2				| a2: mask
+
+	move.l	(arg_pitch,a6),d3		| d3.w: pitch (used as long, too)
+	move.l	(arg_numStrips,a6),d2		| d2.w: _numStrips (used as long, too)
+	move.l	(arg_palette,a6),a3		| a3: _palette
+
+	// uint16 color = compData.repColor;
+	clr.l	d0				| d0.l: keep the upper 24 bits clean
+	move.b	(compData.repColor,a5),d0
+	move.b	d0,(color,a6)
+	// if (len) {
+	// 	--len;
+	// 	goto StartPos;
+	// }
+	// len   --len action
+	// 0     -1    jump into main_loop
+	// 1      0    jump into main_loop = jump into len_loop (0x)
+	// 2      1    jump into len_loop (1x)
+	// 3      2    jump into len_loop (2x)
+	subq.w	#1,d6
+	bgt.b	pcolor_setup
+main_loop:
+	// do {
+	// len = *src++;
+	// color = len >> compData.shr;
+	// len &= compData.mask;
+	// if (!len)
+	// 	len = *src++;
+	clr.l	d0				| d0.l: keep the upper 24 bits clean
+	move.b	(a0)+,d0
+	move.w	d0,d6				| d6.w: clear the high byte
+	move.b	(compData.shr,a5),d1
+	lsr.b	d1,d0				| d0.b: color (used as long, too)
+	move.b	d0,(color,a6)
+	and.b	(compData.mask,a5),d6		| d6.w: len
+	bne.b	pcolor_setup
+	move.b	(a0)+,d6
+pcolor_setup:
+	move.w	(a3,d0.l*2),d7			| d7.w: _palette[color]
+len_loop:
+	// do {
+	// 	batch = height < len ? height : len;
+	move.w	d5,d1				| d1.w: batch
+	cmp.w	d6,d1
+	blt.b	1f
+	move.w	d6,d1
+1:	// 	len -= batch;
+	// 	height -= batch;
+	sub.w	d1,d6
+	sub.w	d1,d5
+	// 	if (color) {
+	tst.b	(color,a6)
+	beq.b	2f
+
+	subq.w	#1,d1				| dbra
+batch_loop:
+	// 		do {
+	// 			if (!(*mask & maskbit))
+	btst	d4,(a2)
+	bne.b	1f
+	// 				*dst = _palette[color];
+	move.b	d7,(a1)
+1:	// 			dst  += pitch;
+	// 			mask += _numStrips;
+	add.l	d3,a1
+	add.l	d2,a2
+	// 		} while (--batch);
+	dbra	d1,batch_loop
+	bra.b	3f
+	// 	} else {
+2:	// 		dst  += batch * pitch;
+	move.w	d1,d0
+	mulu.w	d3,d0
+	add.l	d0,a1
+	// 		mask += batch * _numStrips;
+	mulu.w	d2,d1
+	add.l	d1,a2
+	// 	}
+
+3:	// 	if (height == 0) {
+	tst.w	d5
+	bne.b	5f
+	// 		if (--compData.skipWidth == 0)
+	subq.w	#1,(compData.skipWidth,a5)
+	bne.b	4f
+	// 			return;
+	movem.l	(sp)+,d2-d7/a2-a5
+	unlk	a6
+	rts
+
+4:	// 		height = _height;
+	move.w	(arg_height,a6),d5		| d5.w: height
+	move.l	(compData.scaleXStep,a5),d1	| d1.l: preload scaleXStep
+	// 		compData.x += compData.scaleXStep;
+	move.l	(compData.x,a5),d0
+	add.l	d1,d0
+	move.l	d0,(compData.x,a5)		| d0.l: compData.x
+	move.l	d0,d4
+	// 		maskbit = revBitMask(compData.x & 7);
+	not.b	d4
+	and.b	#0b00000111,d4			| d4.b: bit to test
+	// 		destPtr += compData.scaleXStep;
+	add.l	d1,a4
+	// 		dst = destPtr;
+	move.l	a4,a1				| a1: dst
+	// 		mask = compData.maskPtr + compData.x / 8;
+	asr.l	#3,d0
+	move.l	(compData.maskPtr,a5),a2
+	add.l	d0,a2				| a2: mask
+	// 	}
+5:	// StartPos:
+	// } while (len > 0);
+	tst.w	d6
+	bne.b	len_loop
+	// } while (true);
+	bra.w	main_loop
diff --git a/engines/scumm/m68k/bylerledecode_mode1.S b/engines/scumm/m68k/bylerledecode_mode1.S
new file mode 100644
index 00000000000..6b11f50c2e1
--- /dev/null
+++ b/engines/scumm/m68k/bylerledecode_mode1.S
@@ -0,0 +1,218 @@
+/* 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/>.
+ *
+ */
+
+| kate: tab-width 8; replace-tabs off; hl Motorola 68k (VASM/Devpac);
+
+#include "bylerledecode.inc"
+
+	.globl	SYM(ByleRLEDecode_Mode1)
+
+	add_var	len,2+2	// uint16 len; (align stack)
+
+
+	.text
+
+| void ByleRLEDecode_Mode1(
+|	BaseCostumeRenderer::ByleRLEData *pcompData,
+|	const byte _scaleX, /* unused */
+|	const byte _scaleY, /* unused */
+|	const int _height,
+|	const int pitch,
+|	const int _numStrips,
+|	const byte *_srcPtr,
+|	const byte *_shadowTable,
+|	const uint16 *_palette);
+SYM(ByleRLEDecode_Mode1):
+// TODO: __FASTCALL__
+	link	a6,#-sizeof_local_vars		| a6: frame pointer
+	movem.l	d2-d7/a2-a5,-(sp)
+
+	// registers taken so far:
+	// - d7(pcolor), d6(color), d5(height), d4(maskbit), d3(pitch), d2(_numStrips)
+	// - a0(src), a1(dst), a2(mask), a3(_palette), a4(_shadowTable), a5(pcompData), a6(frame pointer)
+
+	// BaseCostumeRenderer::ByleRLEData &compData = *pcompData;
+	move.l	(arg_pcompData,a6),a5		| a5: pcompData
+
+	// const byte *src = _srcPtr;
+	move.l	(arg_srcPtr,a6),a0		| a0: src
+
+	// uint16 len = compData.repLen;
+	clr.w	(len,a6)
+	move.b	(compData.repLen,a5),(len+1,a6)
+	// uint16 color = compData.repColor;
+	clr.l	d6
+	move.b	(compData.repColor,a5),d6	| d6.b: color (used as long, too)
+	clr.l	d7				| used as long, too
+
+	// byte *dst = compData.destPtr;
+	move.l	(compData.destPtr,a5),a1	| a1: dst
+	// uint16 height = _height;
+	move.w	(arg_height,a6),d5		| d5.w: height
+	// byte maskbit = revBitMask(compData.x & 7);
+	// #define revBitMask(x) (0x80 >> (x))
+	move.l	(compData.x,a5),d0
+	move.b	d0,d4
+	not.b	d4
+	and.b	#0b00000111,d4			| d4.b: bit to test
+	// const byte *mask = compData.maskPtr + compData.x / 8;
+	asr.l	#3,d0
+	move.l	(compData.maskPtr,a5),a2
+	add.l	d0,a2				| a2: mask
+
+	move.l	(arg_pitch,a6),d3		| d3.w: pitch (used as long, too)
+	move.l	(arg_numStrips,a6),d2		| d2.w: _numStrips (used as long, too)
+	move.l	(arg_palette,a6),a3		| a3: _palette
+	move.l	(arg_shadowTable,a6),a4		| a4: _shadowTable
+
+	// if (len) {
+	// 	--len;
+	// 	goto StartPos;
+	// }
+	// len   --len action
+	// 0     -1    jump into main_loop
+	// 1      0    jump into main_loop = jump into len_loop (0x)
+	// 2      1    jump into len_loop (1x)
+	// 3      2    jump into len_loop (2x)
+	subq.w	#1,(len,a6)
+	bgt.b	pcolor_setup
+main_loop:
+	// do {
+	// len = *src++;
+	// color = len >> compData.shr;
+	// len &= compData.mask;
+	// if (!len)
+	// 	len = *src++;
+	clr.w	d7
+	move.b	(a0)+,d7
+	move.w	d7,d6				| d6.w: clear the highest bit ("shadow_on" bit cleared)
+	move.b	(compData.shr,a5),d0
+	lsr.b	d0,d6				| d6.b: color (used as long, too)
+	and.b	(compData.mask,a5),d7
+	bne.b	1f
+	move.b	(a0)+,d7
+1:	move.w	d7,(len,a6)
+
+pcolor_setup:
+	tst.b	d6
+	beq.b	len_loop
+	move.w	(a3,d6.l*2),d7			| d7.w: pcolor = _palette[color] (used as long, too)
+	cmp.w	#13,d7
+	bne.b	len_loop
+	or.w	#0x8000,d6			| d6.w: "shadow_on" bit set
+len_loop:
+	// do {
+	// 	batch = height < len ? height : len;
+	move.w	(len,a6),d0
+	move.w	d5,d1				| d1.w: batch
+	cmp.w	d0,d1
+	blt.b	1f
+	move.w	d0,d1
+1:	// 	len -= batch;
+	// 	height -= batch;
+	sub.w	d1,(len,a6)
+	sub.w	d1,d5
+	// 	if (color) {
+	tst.b	d6
+	beq.b	2f
+
+	subq.w	#1,d1				| dbra
+	tst.w	d6
+	bmi.b	batch_loop_shadow
+batch_loop:
+	// 		do {
+	// 			if (!(*mask & maskbit))
+	btst	d4,(a2)
+	bne.b	1f
+	// 				*dst = _palette[color];
+	move.b	d7,(a1)
+1:	// 			dst  += pitch;
+	// 			mask += _numStrips;
+	add.l	d3,a1
+	add.l	d2,a2
+	// 		} while (--batch);
+	dbra	d1,batch_loop
+	bra.b	3f
+
+batch_loop_shadow:
+	// 		do {
+	// 			if (!(*mask & maskbit))
+	btst	d4,(a2)
+	bne.b	1f
+	// 				*dst = _shadowTable[*dst];
+	move.b	(a1),d7
+	move.b	(a4,d7.l),(a1)
+1:	// 			dst  += pitch;
+	// 			mask += _numStrips;
+	add.l	d3,a1
+	add.l	d2,a2
+	// 		} while (--batch);
+	dbra	d1,batch_loop_shadow
+	bra.b	3f
+
+	// 	} else {
+2:	// 		dst  += batch * pitch;
+	move.w	d1,d0
+	mulu.w	d3,d0
+	add.l	d0,a1
+	// 		mask += batch * _numStrips;
+	mulu.w	d2,d1
+	add.l	d1,a2
+	// 	}
+
+3:	// 	if (height == 0) {
+	tst.w	d5
+	bne.b	5f
+	// 		if (--compData.skipWidth == 0)
+	subq.w	#1,(compData.skipWidth,a5)
+	bne.b	4f
+	// 			return;
+	movem.l	(sp)+,d2-d7/a2-a5
+	unlk	a6
+	rts
+
+4:	// 		height = _height;
+	move.w	(arg_height,a6),d5		| d5.w: height
+	move.l	(compData.scaleXStep,a5),d1	| d1.l: preload scaleXStep
+	// 		compData.x += compData.scaleXStep;
+	move.l	(compData.x,a5),d0
+	add.l	d1,d0
+	move.l	d0,(compData.x,a5)		| d0.l: compData.x
+	move.l	d0,d4
+	// 		maskbit = revBitMask(compData.x & 7);
+	not.b	d4
+	and.b	#0b00000111,d4			| d4.b: bit to test
+	// 		compData.destPtr += compData.scaleXStep;
+	// 		dst = compData.destPtr;
+	move.l	(compData.destPtr,a5),a1	| a1: dst
+	add.l	d1,a1
+	move.l	a1,(compData.destPtr,a5)
+	// 		mask = compData.maskPtr + compData.x / 8;
+	asr.l	#3,d0
+	move.l	(compData.maskPtr,a5),a2
+	add.l	d0,a2				| a2: mask
+	// 	}
+5:	// StartPos:
+	// } while (len > 0);
+	tst.w	(len,a6)
+	bne.w	len_loop
+	// } while (true);
+	bra.w	main_loop
diff --git a/engines/scumm/m68k/bylerledecode_mode3.S b/engines/scumm/m68k/bylerledecode_mode3.S
new file mode 100644
index 00000000000..fa921e378b1
--- /dev/null
+++ b/engines/scumm/m68k/bylerledecode_mode3.S
@@ -0,0 +1,222 @@
+/* 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/>.
+ *
+ */
+
+| kate: tab-width 8; replace-tabs off; hl Motorola 68k (VASM/Devpac);
+
+#include "bylerledecode.inc"
+
+	.globl	SYM(ByleRLEDecode_Mode3)
+
+	add_var	len,2+2	// uint16 len; (align stack)
+
+
+	.text
+
+| void ByleRLEDecode_Mode3(
+|	BaseCostumeRenderer::ByleRLEData *pcompData,
+|	const byte _scaleX, /* unused */
+|	const byte _scaleY, /* unused */
+|	const int _height,
+|	const int pitch,
+|	const int _numStrips,
+|	const byte *_srcPtr,
+|	const byte *_shadowTable,
+|	const uint16 *_palette);
+SYM(ByleRLEDecode_Mode3):
+// TODO: __FASTCALL__
+	link	a6,#-sizeof_local_vars		| a6: frame pointer
+	movem.l	d2-d7/a2-a5,-(sp)
+
+	// registers taken so far:
+	// - d7(pcolor), d6(color), d5(height), d4(maskbit), d3(pitch), d2(_numStrips)
+	// - a0(src), a1(dst), a2(mask), a3(_palette), a4(_shadowTable), a5(pcompData), a6(frame pointer)
+
+	// BaseCostumeRenderer::ByleRLEData &compData = *pcompData;
+	move.l	(arg_pcompData,a6),a5		| a5: pcompData
+
+	// const byte *src = _srcPtr;
+	move.l	(arg_srcPtr,a6),a0		| a0: src
+
+	// uint16 len = compData.repLen;
+	clr.w	(len,a6)
+	move.b	(compData.repLen,a5),(len+1,a6)
+	// uint16 color = compData.repColor;
+	clr.l	d6
+	move.b	(compData.repColor,a5),d6	| d6.b: color (used as long, too)
+	clr.l	d7				| used as long, too
+
+	// byte *dst = compData.destPtr;
+	move.l	(compData.destPtr,a5),a1	| a1: dst
+	// uint16 height = _height;
+	move.w	(arg_height,a6),d5		| d5.w: height
+	// byte maskbit = revBitMask(compData.x & 7);
+	// #define revBitMask(x) (0x80 >> (x))
+	move.l	(compData.x,a5),d0
+	move.b	d0,d4
+	not.b	d4
+	and.b	#0b00000111,d4			| d4.b: bit to test
+	// const byte *mask = compData.maskPtr + compData.x / 8;
+	asr.l	#3,d0
+	move.l	(compData.maskPtr,a5),a2
+	add.l	d0,a2				| a2: mask
+
+	move.l	(arg_pitch,a6),d3		| d3.w: pitch (used as long, too)
+	move.l	(arg_numStrips,a6),d2		| d2.w: _numStrips (used as long, too)
+	move.l	(arg_palette,a6),a3		| a3: _palette
+	move.l	(arg_shadowTable,a6),a4		| a4: _shadowTable
+
+	// if (len) {
+	// 	--len;
+	// 	goto StartPos;
+	// }
+	// len   --len action
+	// 0     -1    jump into main_loop
+	// 1      0    jump into main_loop = jump into len_loop (0x)
+	// 2      1    jump into len_loop (1x)
+	// 3      2    jump into len_loop (2x)
+	subq.w	#1,(len,a6)
+	bgt.b	pcolor_setup
+main_loop:
+	// do {
+	// len = *src++;
+	// color = len >> compData.shr;
+	// len &= compData.mask;
+	// if (!len)
+	// 	len = *src++;
+	clr.w	d7
+	move.b	(a0)+,d7
+	move.w	d7,d6				| d6.w: clear the highest bit ("shadow_on" bit cleared)
+	move.b	(compData.shr,a5),d0
+	lsr.b	d0,d6				| d6.b: color (used as long, too)
+	and.b	(compData.mask,a5),d7
+	bne.b	1f
+	move.b	(a0)+,d7
+1:	move.w	d7,(len,a6)
+
+pcolor_setup:
+	tst.b	d6
+	beq.b	len_loop
+	move.w	(a3,d6.l*2),d7			| d7.w: pcolor = _palette[color] (used as long, too)
+	cmp.w	#8,d7
+	bhs.b	len_loop
+	lsl.w	#8,d7				| d7.w: if pcolor < 8: pcolor << 8
+	or.w	#0x8000,d6			| d6.w: "shadow_on" bit set
+len_loop:
+	// do {
+	// 	batch = height < len ? height : len;
+	move.w	(len,a6),d0
+	move.w	d5,d1				| d1.w: batch
+	cmp.w	d0,d1
+	blt.b	1f
+	move.w	d0,d1
+1:	// 	len -= batch;
+	// 	height -= batch;
+	sub.w	d1,(len,a6)
+	sub.w	d1,d5
+	// 	if (color) {
+	tst.b	d6
+	beq.b	2f
+
+	subq.w	#1,d1				| dbra
+	tst.w	d6
+	bmi.b	batch_loop_shadow
+batch_loop:
+	// 		do {
+	// 			if (!(*mask & maskbit))
+	btst	d4,(a2)
+	bne.b	1f
+	// 				*dst = _palette[color];
+	move.b	d7,(a1)
+1:	// 			dst  += pitch;
+	// 			mask += _numStrips;
+	add.l	d3,a1
+	add.l	d2,a2
+	// 		} while (--batch);
+	dbra	d1,batch_loop
+	bra.b	3f
+
+batch_loop_shadow:
+	// 		do {
+	// 			if (!(*mask & maskbit)) {
+	btst	d4,(a2)
+	bne.b	1f
+	// 				uint16 pcolor = _palette[color];
+	// 				pcolor = (pcolor << 8) + *dst;
+	// 				*dst = _shadowTable[pcolor];
+	move.b	(a1),d7
+	move.b	(a4,d7.l),(a1)
+	// 			}
+1:	// 			dst  += pitch;
+	// 			mask += _numStrips;
+	add.l	d3,a1
+	add.l	d2,a2
+	// 		} while (--batch);
+	dbra	d1,batch_loop_shadow
+	bra.b	3f
+
+	// 	} else {
+2:	// 		dst  += batch * pitch;
+	move.w	d1,d0
+	mulu.w	d3,d0
+	add.l	d0,a1
+	// 		mask += batch * _numStrips;
+	mulu.w	d2,d1
+	add.l	d1,a2
+	// 	}
+
+3:	// 	if (height == 0) {
+	tst.w	d5
+	bne.b	5f
+	// 		if (--compData.skipWidth == 0)
+	subq.w	#1,(compData.skipWidth,a5)
+	bne.b	4f
+	// 			return;
+	movem.l	(sp)+,d2-d7/a2-a5
+	unlk	a6
+	rts
+
+4:	// 		height = _height;
+	move.w	(arg_height,a6),d5		| d5.w: height
+	move.l	(compData.scaleXStep,a5),d1	| d1.l: preload scaleXStep
+	// 		compData.x += compData.scaleXStep;
+	move.l	(compData.x,a5),d0
+	add.l	d1,d0
+	move.l	d0,(compData.x,a5)		| d0.l: compData.x
+	move.l	d0,d4
+	// 		maskbit = revBitMask(compData.x & 7);
+	not.b	d4
+	and.b	#0b00000111,d4			| d4.b: bit to test
+	// 		compData.destPtr += compData.scaleXStep;
+	// 		dst = compData.destPtr;
+	move.l	(compData.destPtr,a5),a1	| a1: dst
+	add.l	d1,a1
+	move.l	a1,(compData.destPtr,a5)
+	// 		mask = compData.maskPtr + compData.x / 8;
+	asr.l	#3,d0
+	move.l	(compData.maskPtr,a5),a2
+	add.l	d0,a2				| a2: mask
+	// 	}
+5:	// StartPos:
+	// } while (len > 0);
+	tst.w	(len,a6)
+	bne.w	len_loop
+	// } while (true);
+	bra.w	main_loop
diff --git a/engines/scumm/m68k/bylerledecode_scaled_mode0.S b/engines/scumm/m68k/bylerledecode_scaled_mode0.S
new file mode 100644
index 00000000000..e960b0bf68a
--- /dev/null
+++ b/engines/scumm/m68k/bylerledecode_scaled_mode0.S
@@ -0,0 +1,209 @@
+/* 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/>.
+ *
+ */
+
+| kate: tab-width 8; replace-tabs off; hl Motorola 68k (VASM/Devpac);
+
+#include "bylerledecode.inc"
+
+	.globl	SYM(ByleRLEDecode_Scaled_Mode0)
+
+	add_var	color,1+1	// byte color; (align stack)
+	add_var	height,2	// uint16 height;
+	add_var	scaleTableX,4	// &compData.scaleTable[compData.scaleXIndex]
+	add_var	scaleTableY,4	// &compData.scaleTable[compData.scaleYIndex]
+
+	.text
+
+| void ByleRLEDecode_Scaled_Mode0(
+|	BaseCostumeRenderer::ByleRLEData *pcompData,
+|	const byte _scaleX,
+|	const byte _scaleY,
+|	const int _height,
+|	const int pitch,
+|	const int _numStrips,
+|	const byte *_srcPtr,
+|	const byte *_shadowTable, /* unused */
+|	const uint16 *_palette);
+SYM(ByleRLEDecode_Scaled_Mode0):
+// TODO: __FASTCALL__
+	link	a6,#-sizeof_local_vars		| a6: frame pointer
+	movem.l	d2-d7/a2-a5,-(sp)
+
+	// registers taken so far:
+	// - d7(pcolor), d6(len), d5(_scaleY), d4(maskbit), d3(pitch), d2(_numStrips)
+	// - a0(src), a1(dst), a2(mask), a3(scaleTableY), a4(destPtr), a5(pcompData), a6(frame pointer)
+
+	// BaseCostumeRenderer::ByleRLEData &compData = *pcompData;
+	move.l	(arg_pcompData,a6),a5		| a5: pcompData
+
+	// const byte *src = _srcPtr;
+	move.l	(arg_srcPtr,a6),a0		| a0: src
+
+	// uint16 len = compData.repLen;
+	clr.w	d6
+	move.b	(compData.repLen,a5),d6		| d6.w: len
+
+	// byte *dst = compData.destPtr;
+	move.l	(compData.destPtr,a5),a4	| a4: compData.destPtr
+	move.l	a4,a1				| a1: dst
+	// uint16 height = _height;
+	move.w	(arg_height,a6),(height,a6)
+	// int scaleIndexY = compData.scaleYIndex;
+	move.l	(compData.scaleXIndex,a5),d0
+	lea	([compData.scaleTable,a5],d0.l),a3
+	move.l	a3,(scaleTableX,a6)
+	move.l	(compData.scaleYIndex,a5),d0
+	lea	([compData.scaleTable,a5],d0.l),a3	| a3: scaleTableY
+	move.l	a3,(scaleTableY,a6)
+	// byte maskbit = revBitMask(compData.x & 7);
+	// #define revBitMask(x) (0x80 >> (x))
+	move.l	(compData.x,a5),d0
+	move.b	d0,d4
+	not.b	d4
+	and.b	#0b00000111,d4			| d4.b: bit to test
+	// const byte *mask = compData.maskPtr + compData.x / 8;
+	asr.l	#3,d0
+	move.l	(compData.maskPtr,a5),a2
+	add.l	d0,a2				| a2: mask
+
+	move.b	(arg_scaleY,a6),d5		| d5.b: _scaleY
+	move.l	(arg_pitch,a6),d3		| d3.w: pitch (used as long, too)
+	move.l	(arg_numStrips,a6),d2		| d2.w: _numStrips (used as long, too)
+
+	// uint16 color = compData.repColor;
+	clr.l	d0				| d0.l: keep the upper 24 bits clean
+	move.b	(compData.repColor,a5),d0
+	move.b	d0,(color,a6)
+	// if (len) {
+	// 	--len;
+	// 	goto StartPos;
+	// }
+	// len   --len action
+	// 0     -1    jump into main_loop
+	// 1      0    jump into main_loop = jump into len_loop (0x)
+	// 2      1    jump into len_loop (1x)
+	// 3      2    jump into len_loop (2x)
+	subq.w	#1,d6
+	bgt.b	pcolor_setup
+main_loop:
+	// do {
+	// len = *src++;
+	// color = len >> compData.shr;
+	// len &= compData.mask;
+	// if (!len)
+	// 	len = *src++;
+	clr.l	d0				| d0.l: keep the upper 24 bits clean
+	move.b	(a0)+,d0
+	move.w	d0,d6				| d6.w: clear the high byte
+	move.b	(compData.shr,a5),d1
+	lsr.b	d1,d0				| d0.b: color
+	move.b	d0,(color,a6)
+	and.b	(compData.mask,a5),d6		| d6.w: len
+	bne.b	pcolor_setup
+	move.b	(a0)+,d6
+pcolor_setup:
+	move.w	([arg_palette,a6],d0.l*2),d7	| d7.w: _palette[color]
+len_loop:
+	// do {
+	// 	batch = height < len ? height : len;
+	move.w	(height,a6),d1			| d1.w: batch
+	cmp.w	d6,d1
+	blt.b	1f
+	move.w	d6,d1
+1:	// 	len -= batch;
+	// 	height -= batch;
+	sub.w	d1,d6
+	sub.w	d1,(height,a6)
+
+	subq.w	#1,d1				| dbra
+batch_loop:
+	// 		do {
+	// 			if (compData.scaleTable[scaleIndexY++] < _scaleY)
+	cmp.b	(a3)+,d5
+	bls.b	2f
+	// 				if (color) {
+	tst.b	(color,a6)
+	beq.b	1f
+	// 					if (!(*mask & maskbit))
+	btst	d4,(a2)
+	bne.b	1f
+	// 						*dst = _palette[color];
+	move.b	d7,(a1)
+	// 				}
+1:	// 				dst  += pitch;
+	// 				mask += _numStrips;
+	add.l	d3,a1
+	add.l	d2,a2
+	// 			}
+2:	// 		} while (--batch);
+	dbra	d1,batch_loop
+
+	// 	if (height == 0) {
+	tst.w	(height,a6)
+	bne.b	6f
+	// 		if (--compData.skipWidth == 0)
+	subq.w	#1,(compData.skipWidth,a5)
+	bne.b	4f
+	// 			return;
+	movem.l	(sp)+,d2-d7/a2-a5
+	unlk	a6
+	rts
+
+4:	// 		height = _height;
+	move.w	(arg_height,a6),(height,a6)
+	// 		scaleIndexY = compData.scaleYIndex;
+	move.l	(scaleTableY,a6),a3
+
+	move.l	(compData.scaleXStep,a5),d1	| d1.l: preload scaleXStep
+
+	// 		dst = compData.destPtr; (moved from the below)
+	move.l	a4,a1				| a1: dst
+	// 		if (compData.scaleTable[compData.scaleXIndex] < _scaleX) {
+	move.b	(arg_scaleX,a6),d0
+	cmp.b	([scaleTableX,a6]),d0
+	bls.b	5f
+
+	// 			compData.x += compData.scaleXStep;
+	move.l	(compData.x,a5),d4
+	add.l	d1,d4
+	move.l	d4,(compData.x,a5)
+	// 			maskbit = revBitMask(compData.x & 7);
+	not.b	d4
+	and.b	#0b00000111,d4			| d4.b: bit to test
+	// 			compData.destPtr += compData.scaleXStep;
+	add.l	d1,a4
+	// 			dst = destPtr;
+	move.l	a4,a1				| a1: dst
+	// 		}
+5:	// 		compData.scaleXIndex += compData.scaleXStep;
+	add.l	d1,(scaleTableX,a6)
+	// 		mask = compData.maskPtr + compData.x / 8;
+	move.l	(compData.x,a5),d0
+	asr.l	#3,d0
+	move.l	(compData.maskPtr,a5),a2
+	add.l	d0,a2				| a2: mask
+	// 	}
+6:	// StartPos:
+	// } while (len > 0);
+	tst.w	d6
+	bne.b	len_loop
+	// } while (true);
+	bra.w	main_loop
diff --git a/engines/scumm/m68k/bylerledecode_scaled_mode0_smask.S b/engines/scumm/m68k/bylerledecode_scaled_mode0_smask.S
new file mode 100644
index 00000000000..6a5a39838bf
--- /dev/null
+++ b/engines/scumm/m68k/bylerledecode_scaled_mode0_smask.S
@@ -0,0 +1,216 @@
+/* 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/>.
+ *
+ */
+
+| kate: tab-width 8; replace-tabs off; hl Motorola 68k (VASM/Devpac);
+
+#include "bylerledecode.inc"
+
+	.globl	SYM(ByleRLEDecode_Scaled_Mode0_SMask)
+
+	add_var	color,1+1	// byte color; (align stack)
+	add_var	height,2	// uint16 height;
+	add_var	scaleIndexY,1	// byte scaleIndexX;
+	add_var	scaleIndexX,1	// byte scaleIndexY;
+
+	.text
+
+| void ByleRLEDecode_Scaled_Mode0_SMask(
+|	BaseCostumeRenderer::ByleRLEData *pcompData,
+|	const byte _scaleX,
+|	const byte _scaleY,
+|	const int _height,
+|	const int pitch,
+|	const int _numStrips,
+|	const byte *_srcPtr,
+|	const byte *_shadowTable, /* unused */
+|	const uint16 *_palette);
+SYM(ByleRLEDecode_Scaled_Mode0_SMask):
+// TODO: __FASTCALL__
+	link	a6,#-sizeof_local_vars		| a6: frame pointer
+	movem.l	d2-d7/a2-a5,-(sp)
+
+	// registers taken so far:
+	// - d7(pcolor), d6(len), d5(_scaleY), d4(maskbit), d3(pitch), d2(_numStrips)
+	// - a0(src), a1(dst), a2(mask), a3(scaleTable), a4(destPtr), a5(pcompData), a6(frame pointer)
+
+	// BaseCostumeRenderer::ByleRLEData &compData = *pcompData;
+	move.l	(arg_pcompData,a6),a5		| a5: pcompData
+
+	// const byte *src = _srcPtr;
+	move.l	(arg_srcPtr,a6),a0		| a0: src
+
+	// uint16 len = compData.repLen;
+	clr.w	d6
+	move.b	(compData.repLen,a5),d6		| d6.w: len
+
+	// byte *dst = compData.destPtr;
+	move.l	(compData.destPtr,a5),a4	| a4: compData.destPtr
+	move.l	a4,a1				| a1: dst
+	// uint16 height = _height;
+	move.w	(arg_height,a6),(height,a6)
+	// byte scaleIndexX = compData.scaleXIndex;
+	// byte scaleIndexY = compData.scaleYIndex;
+	move.l	(compData.scaleTable,a5),a3	| a3: compData.scaleTable
+	move.b	(compData.scaleXIndex+3,a5),(scaleIndexX,a6)
+	move.b	(compData.scaleYIndex+3,a5),(scaleIndexY,a6)
+	// byte maskbit = revBitMask(compData.x & 7);
+	// #define revBitMask(x) (0x80 >> (x))
+	move.l	(compData.x,a5),d0
+	move.b	d0,d4
+	not.b	d4
+	and.b	#0b00000111,d4			| d4.b: bit to test
+	// const byte *mask = compData.maskPtr + compData.x / 8;
+	asr.l	#3,d0
+	move.l	(compData.maskPtr,a5),a2
+	add.l	d0,a2				| a2: mask
+
+	move.b	(arg_scaleY,a6),d5		| d5.b: _scaleY
+	move.l	(arg_pitch,a6),d3		| d3.w: pitch (used as long, too)
+	move.l	(arg_numStrips,a6),d2		| d2.w: _numStrips (used as long, too)
+
+	// uint16 color = compData.repColor;
+	clr.l	d0				| d0.l: keep the upper 24 bits clean
+	move.b	(compData.repColor,a5),d0
+	move.b	d0,(color,a6)
+	// if (len) {
+	// 	--len;
+	// 	goto StartPos;
+	// }
+	// len   --len action
+	// 0     -1    jump into main_loop
+	// 1      0    jump into main_loop = jump into len_loop (0x)
+	// 2      1    jump into len_loop (1x)
+	// 3      2    jump into len_loop (2x)
+	subq.w	#1,d6
+	bgt.b	pcolor_setup
+main_loop:
+	// do {
+	// len = *src++;
+	// color = len >> compData.shr;
+	// len &= compData.mask;
+	// if (!len)
+	// 	len = *src++;
+	clr.l	d0				| d0.l: keep the upper 24 bits clean
+	move.b	(a0)+,d0
+	move.w	d0,d6				| d6.w: clear the high byte
+	move.b	(compData.shr,a5),d1
+	lsr.b	d1,d0				| d0.b: color
+	move.b	d0,(color,a6)
+	and.b	(compData.mask,a5),d6		| d6.w: len
+	bne.b	pcolor_setup
+	move.b	(a0)+,d6
+pcolor_setup:
+	move.w	([arg_palette,a6],d0.l*2),d7	| d7.w: _palette[color]
+len_loop:
+	// do {
+	// 	batch = height < len ? height : len;
+	move.w	(height,a6),d1			| d1.w: batch
+	cmp.w	d6,d1
+	blt.b	1f
+	move.w	d6,d1
+1:	// 	len -= batch;
+	// 	height -= batch;
+	sub.w	d1,d6
+	sub.w	d1,(height,a6)
+
+	subq.w	#1,d1				| dbra
+
+	// load scaleIndexY into d0 for batch loop (byte index, d0.l clean)
+	clr.l	d0
+	move.b	(scaleIndexY,a6),d0
+
+batch_loop:
+	// 		do {
+	// 			if (compData.scaleTable[scaleIndexY++ & compData.scaleIndexMask] < _scaleY)
+	cmp.b	(a3,d0.l),d5
+	bls.b	2f
+	// 				if (color) {
+	tst.b	(color,a6)
+	beq.b	1f
+	// 					if (!(*mask & maskbit))
+	btst	d4,(a2)
+	bne.b	1f
+	// 						*dst = _palette[color];
+	move.b	d7,(a1)
+	// 				}
+1:	// 				dst  += pitch;
+	// 				mask += _numStrips;
+	add.l	d3,a1
+	add.l	d2,a2
+	// 			}
+2:	addq.b	#1,d0				| scaleIndexY++ (byte wrap)
+	// 		} while (--batch);
+	dbra	d1,batch_loop
+
+	move.b	d0,(scaleIndexY,a6)
+	// 	if (height == 0) {
+	tst.w	(height,a6)
+	bne.b	6f
+	// 		if (--compData.skipWidth == 0)
+	subq.w	#1,(compData.skipWidth,a5)
+	bne.b	4f
+	// 			return;
+	movem.l	(sp)+,d2-d7/a2-a5
+	unlk	a6
+	rts
+
+4:	// 		height = _height;
+	move.w	(arg_height,a6),(height,a6)
+	// 		scaleIndexY = compData.scaleYIndex;
+	move.b	(compData.scaleYIndex+3,a5),(scaleIndexY,a6)
+
+	move.l	(compData.scaleXStep,a5),d1	| d1.l: preload scaleXStep
+
+	// 		dst = compData.destPtr; (moved from the below)
+	move.l	a4,a1				| a1: dst
+	// 		if (compData.scaleTable[compData.scaleXIndex] < _scaleX) {
+	clr.l	d0
+	move.b	(scaleIndexX,a6),d0
+	move.b	(a3,d0.l),d0
+	cmp.b	(arg_scaleX,a6),d0
+	bhs.b	5f
+
+	// 			compData.x += compData.scaleXStep;
+	move.l	(compData.x,a5),d4
+	add.l	d1,d4
+	move.l	d4,(compData.x,a5)
+	// 			maskbit = revBitMask(compData.x & 7);
+	not.b	d4
+	and.b	#0b00000111,d4			| d4.b: bit to test
+	// 			compData.destPtr += compData.scaleXStep;
+	add.l	d1,a4
+	// 			dst = destPtr;
+	move.l	a4,a1				| a1: dst
+	// 		}
+5:	// 		compData.scaleXIndex = (compData.scaleXIndex + compData.scaleXStep) & scaleIndexMask;
+	add.b	d1,(scaleIndexX,a6)		| byte wrap (d1.b = LSB of scaleXStep)
+	// 		mask = compData.maskPtr + compData.x / 8;
+	move.l	(compData.x,a5),d0
+	asr.l	#3,d0
+	move.l	(compData.maskPtr,a5),a2
+	add.l	d0,a2				| a2: mask
+	// 	}
+6:	// StartPos:
+	// } while (len > 0);
+	tst.w	d6
+	bne.w	len_loop
+	// } while (true);
+	bra.w	main_loop
diff --git a/engines/scumm/m68k/bylerledecode_scaled_mode1.S b/engines/scumm/m68k/bylerledecode_scaled_mode1.S
new file mode 100644
index 00000000000..1c78f1bcc90
--- /dev/null
+++ b/engines/scumm/m68k/bylerledecode_scaled_mode1.S
@@ -0,0 +1,249 @@
+/* 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/>.
+ *
+ */
+
+| kate: tab-width 8; replace-tabs off; hl Motorola 68k (VASM/Devpac);
+
+#include "bylerledecode.inc"
+
+	.globl	SYM(ByleRLEDecode_Scaled_Mode1)
+
+	add_var	len,2		// uint16 len;
+	add_var	height,2	// uint16 height;
+	add_var	scaleTableX,4	// &compData.scaleTable[compData.scaleXIndex]
+	add_var	scaleTableY,4	// &compData.scaleTable[compData.scaleYIndex]
+	add_var	shadowMask,1+3	// 0xFF = draw shadows (new column), 0x00 = skip shadows (duplicate column) (align stack)
+
+	.text
+
+| void ByleRLEDecode_Scaled_Mode1(
+|	BaseCostumeRenderer::ByleRLEData *pcompData,
+|	const byte _scaleX,
+|	const byte _scaleY,
+|	const int _height,
+|	const int pitch,
+|	const int _numStrips,
+|	const byte *_srcPtr,
+|	const byte *_shadowTable,
+|	const uint16 *_palette);
+SYM(ByleRLEDecode_Scaled_Mode1):
+// TODO: __FASTCALL__
+	link	a6,#-sizeof_local_vars		| a6: frame pointer
+	movem.l	d2-d7/a2-a5,-(sp)
+
+	// registers taken so far:
+	// - d7(pcolor), d6(color), d5(_scaleY), d4(maskbit), d3(pitch), d2(_numStrips)
+	// - a0(src), a1(dst), a2(mask), a3(scaleTableY), a4(_shadowTable), a5(pcompData), a6(frame pointer)
+
+	// BaseCostumeRenderer::ByleRLEData &compData = *pcompData;
+	move.l	(arg_pcompData,a6),a5		| a5: pcompData
+
+	// const byte *src = _srcPtr;
+	move.l	(arg_srcPtr,a6),a0		| a0: src
+
+	// uint16 len = compData.repLen;
+	clr.w	(len,a6)
+	move.b	(compData.repLen,a5),(len+1,a6)
+	// uint16 color = compData.repColor;
+	clr.l	d6
+	move.b	(compData.repColor,a5),d6	| d6.b: color (used as long, too)
+	clr.l	d7				| used as long, too
+
+	// byte *dst = compData.destPtr;
+	move.l	(compData.destPtr,a5),a1	| a1: dst
+	// int lastColumnX = -1;
+	st.b	(shadowMask,a6)
+	// uint16 height = _height;
+	move.w	(arg_height,a6),(height,a6)
+	// int scaleIndexY = compData.scaleYIndex;
+	move.l	(compData.scaleXIndex,a5),d0
+	lea	([compData.scaleTable,a5],d0.l),a3
+	move.l	a3,(scaleTableX,a6)
+	move.l	(compData.scaleYIndex,a5),d0
+	lea	([compData.scaleTable,a5],d0.l),a3	| a3: scaleTableY
+	move.l	a3,(scaleTableY,a6)
+	// byte maskbit = revBitMask(compData.x & 7);
+	// #define revBitMask(x) (0x80 >> (x))
+	move.l	(compData.x,a5),d0
+	move.b	d0,d4
+	not.b	d4
+	and.b	#0b00000111,d4			| d4.b: bit to test
+	// const byte *mask = compData.maskPtr + compData.x / 8;
+	asr.l	#3,d0
+	move.l	(compData.maskPtr,a5),a2
+	add.l	d0,a2				| a2: mask
+
+	move.b	(arg_scaleY,a6),d5		| d5.b: _scaleY
+	move.l	(arg_pitch,a6),d3		| d3.w: pitch (used as long, too)
+	move.l	(arg_numStrips,a6),d2		| d2.w: _numStrips (used as long, too)
+	move.l	(arg_shadowTable,a6),a4		| a4: _shadowTable
+
+	// if (len) {
+	// 	--len;
+	// 	goto StartPos;
+	// }
+	// len   --len action
+	// 0     -1    jump into main_loop
+	// 1      0    jump into main_loop = jump into len_loop (0x)
+	// 2      1    jump into len_loop (1x)
+	// 3      2    jump into len_loop (2x)
+	subq.w	#1,(len,a6)
+	bgt.b	pcolor_setup
+main_loop:
+	// do {
+	// len = *src++;
+	// color = len >> compData.shr;
+	// len &= compData.mask;
+	// if (!len)
+	// 	len = *src++;
+	clr.w	d7
+	move.b	(a0)+,d7
+	move.w	d7,d6				| d6.w: clear the highest bit ("shadow_on" bit cleared)
+	move.b	(compData.shr,a5),d0
+	lsr.b	d0,d6				| d6.b: color (used as long, too)
+	and.b	(compData.mask,a5),d7
+	bne.b	1f
+	move.b	(a0)+,d7
+1:	move.w	d7,(len,a6)
+
+pcolor_setup:
+	tst.b	d6
+	beq.b	len_loop
+	move.w	([arg_palette,a6],d6.l*2),d7	| d7.w: pcolor = _palette[color] (used as long, too)
+	cmp.w	#13,d7
+	bne.b	len_loop
+	or.w	#0x8000,d6			| d6.w: "shadow_on" bit set
+	and.b	(shadowMask,a6),d6		| 0xFF: no-op; 0x00: clears color, suppresses writes
+len_loop:
+	// do {
+	// 	batch = height < len ? height : len;
+	move.w	(len,a6),d0
+	move.w	(height,a6),d1			| d1.w: batch
+	cmp.w	d0,d1
+	blt.b	1f
+	move.w	d0,d1
+1:	// 	len -= batch;
+	// 	height -= batch;
+	sub.w	d1,(len,a6)
+	sub.w	d1,(height,a6)
+
+	subq.w	#1,d1				| dbra
+	tst.w	d6
+	bmi.b	batch_loop_shadow
+batch_loop:
+	// 		do {
+	// 			if (compData.scaleTable[scaleIndexY++] < _scaleY)
+	cmp.b	(a3)+,d5
+	bls.b	2f
+	// 				if (color) {
+	tst.b	d6
+	beq.b	1f
+	// 					if (!(*mask & maskbit))
+	btst	d4,(a2)
+	bne.b	1f
+	// 						*dst = _palette[color];
+	move.b	d7,(a1)
+	// 				}
+1:	// 				dst  += pitch;
+	// 				mask += _numStrips;
+	add.l	d3,a1
+	add.l	d2,a2
+	// 			}
+2:	// 		} while (--batch);
+	dbra	d1,batch_loop
+	bra.b	3f
+
+batch_loop_shadow:
+	// 		do {
+	// 			if (compData.scaleTable[scaleIndexY++] < _scaleY) {
+	cmp.b	(a3)+,d5
+	bls.b	2f
+	// 				if (color) {
+	tst.b	d6
+	beq.b	1f
+	// 					if (!(*mask & maskbit))
+	btst	d4,(a2)
+	bne.b	1f
+	// 						*dst = _shadowTable[*dst];
+	move.b	(a1),d7
+	move.b	(a4,d7.l),(a1)
+	// 				}
+1:	// 				dst  += pitch;
+	// 				mask += _numStrips;
+	add.l	d3,a1
+	add.l	d2,a2
+	// 			}
+2:	// 		} while (--batch);
+	dbra	d1,batch_loop_shadow
+
+3:	// 	if (height == 0) {
+	tst.w	(height,a6)
+	bne.b	6f
+	// 		if (--compData.skipWidth == 0)
+	subq.w	#1,(compData.skipWidth,a5)
+	bne.b	4f
+	// 			return;
+	movem.l	(sp)+,d2-d7/a2-a5
+	unlk	a6
+	rts
+
+4:	// 		height = _height;
+	move.w	(arg_height,a6),(height,a6)
+	// 		scaleIndexY = compData.scaleYIndex;
+	move.l	(scaleTableY,a6),a3
+	// 		lastColumnX = compData.x;
+	sf.b	(shadowMask,a6)			| assume duplicate column (0x00)
+
+	move.l	(compData.scaleXStep,a5),d1	| d1.l: preload scaleXStep
+
+	// 		dst = compData.destPtr; (moved from the below)
+	move.l	(compData.destPtr,a5),a1	| a1: dst
+	// 		if (compData.scaleTable[compData.scaleXIndex] < _scaleX) {
+	move.b	(arg_scaleX,a6),d0
+	cmp.b	([scaleTableX,a6]),d0
+	bls.b	5f
+
+	st.b	(shadowMask,a6)			| new column (0xFF)
+
+	// 			compData.x += compData.scaleXStep;
+	move.l	(compData.x,a5),d4
+	add.l	d1,d4
+	move.l	d4,(compData.x,a5)
+	// 			maskbit = revBitMask(compData.x & 7);
+	not.b	d4
+	and.b	#0b00000111,d4			| d4.b: bit to test
+	// 			compData.destPtr += compData.scaleXStep;
+	add.l	d1,a1
+	move.l	a1,(compData.destPtr,a5)
+	// 		}
+5:	// 		compData.scaleXIndex += compData.scaleXStep;
+	add.l	d1,(scaleTableX,a6)
+	// 		mask = compData.maskPtr + compData.x / 8;
+	move.l	(compData.x,a5),d0
+	asr.l	#3,d0
+	move.l	(compData.maskPtr,a5),a2
+	add.l	d0,a2				| a2: mask
+	// 	}
+6:	// StartPos:
+	// } while (len > 0);
+	tst.w	(len,a6)
+	bne.w	len_loop
+	// } while (true);
+	bra.w	main_loop
diff --git a/engines/scumm/m68k/bylerledecode_scaled_mode1_smask.S b/engines/scumm/m68k/bylerledecode_scaled_mode1_smask.S
new file mode 100644
index 00000000000..506495963ef
--- /dev/null
+++ b/engines/scumm/m68k/bylerledecode_scaled_mode1_smask.S
@@ -0,0 +1,254 @@
+/* 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/>.
+ *
+ */
+
+| kate: tab-width 8; replace-tabs off; hl Motorola 68k (VASM/Devpac);
+
+#include "bylerledecode.inc"
+
+	.globl	SYM(ByleRLEDecode_Scaled_Mode1_SMask)
+
+	add_var	len,2		// uint16 len;
+	add_var	height,2	// uint16 height;
+	add_var	scaleIndexX,1	// byte scaleIndexX;
+	add_var	scaleIndexY,1	// byte scaleIndexY;
+	add_var	shadowMask,1+1	// 0xFF = draw shadows (new column), 0x00 = skip shadows (duplicate column) (align stack)
+
+	.text
+
+| void ByleRLEDecode_Scaled_Mode1_SMask(
+|	BaseCostumeRenderer::ByleRLEData *pcompData,
+|	const byte _scaleX,
+|	const byte _scaleY,
+|	const int _height,
+|	const int pitch,
+|	const int _numStrips,
+|	const byte *_srcPtr,
+|	const byte *_shadowTable,
+|	const uint16 *_palette);
+SYM(ByleRLEDecode_Scaled_Mode1_SMask):
+// TODO: __FASTCALL__
+	link	a6,#-sizeof_local_vars		| a6: frame pointer
+	movem.l	d2-d7/a2-a5,-(sp)
+
+	// registers taken so far:
+	// - d7(pcolor), d6(color), d5(_scaleY), d4(maskbit), d3(pitch), d2(_numStrips)
+	// - a0(src), a1(dst), a2(mask), a3(scaleTable), a4(_shadowTable), a5(pcompData), a6(frame pointer)
+
+	// BaseCostumeRenderer::ByleRLEData &compData = *pcompData;
+	move.l	(arg_pcompData,a6),a5		| a5: pcompData
+
+	// const byte *src = _srcPtr;
+	move.l	(arg_srcPtr,a6),a0		| a0: src
+
+	// uint16 len = compData.repLen;
+	clr.w	(len,a6)
+	move.b	(compData.repLen,a5),(len+1,a6)
+	// uint16 color = compData.repColor;
+	clr.l	d6
+	move.b	(compData.repColor,a5),d6	| d6.b: color (used as long, too)
+	clr.l	d7				| used as long, too
+
+	// byte *dst = compData.destPtr;
+	move.l	(compData.destPtr,a5),a1	| a1: dst
+	// int lastColumnX = -1;
+	st.b	(shadowMask,a6)
+	// uint16 height = _height;
+	move.w	(arg_height,a6),(height,a6)
+	// byte scaleIndexX = compData.scaleXIndex;
+	// byte scaleIndexY = compData.scaleYIndex;
+	move.l	(compData.scaleTable,a5),a3	| a3: compData.scaleTable
+	move.b	(compData.scaleXIndex+3,a5),(scaleIndexX,a6)
+	move.b	(compData.scaleYIndex+3,a5),(scaleIndexY,a6)
+	// byte maskbit = revBitMask(compData.x & 7);
+	// #define revBitMask(x) (0x80 >> (x))
+	move.l	(compData.x,a5),d0
+	move.b	d0,d4
+	not.b	d4
+	and.b	#0b00000111,d4			| d4.b: bit to test
+	// const byte *mask = compData.maskPtr + compData.x / 8;
+	asr.l	#3,d0
+	move.l	(compData.maskPtr,a5),a2
+	add.l	d0,a2				| a2: mask
+
+	move.b	(arg_scaleY,a6),d5		| d5.b: _scaleY
+	move.l	(arg_pitch,a6),d3		| d3.w: pitch (used as long, too)
+	move.l	(arg_numStrips,a6),d2		| d2.w: _numStrips (used as long, too)
+	move.l	(arg_shadowTable,a6),a4		| a4: _shadowTable
+
+	// if (len) {
+	// 	--len;
+	// 	goto StartPos;
+	// }
+	// len   --len action
+	// 0     -1    jump into main_loop
+	// 1      0    jump into main_loop = jump into len_loop (0x)
+	// 2      1    jump into len_loop (1x)
+	// 3      2    jump into len_loop (2x)
+	subq.w	#1,(len,a6)
+	bgt.b	pcolor_setup
+main_loop:
+	// do {
+	// len = *src++;
+	// color = len >> compData.shr;
+	// len &= compData.mask;
+	// if (!len)
+	// 	len = *src++;
+	clr.w	d7
+	move.b	(a0)+,d7
+	move.w	d7,d6				| d6.w: clear the highest bit ("shadow_on" bit cleared)
+	move.b	(compData.shr,a5),d0
+	lsr.b	d0,d6				| d6.b: color (used as long, too)
+	and.b	(compData.mask,a5),d7
+	bne.b	1f
+	move.b	(a0)+,d7
+1:	move.w	d7,(len,a6)
+
+pcolor_setup:
+	tst.b	d6
+	beq.b	len_loop
+	move.w	([arg_palette,a6],d6.l*2),d7	| d7.w: pcolor = _palette[color] (used as long, too)
+	cmp.w	#13,d7
+	bne.b	len_loop
+	or.w	#0x8000,d6			| d6.w: "shadow_on" bit set
+	and.b	(shadowMask,a6),d6		| 0xFF: no-op; 0x00: clears color, suppresses writes
+len_loop:
+	// do {
+	// 	batch = height < len ? height : len;
+	move.w	(len,a6),d0
+	move.w	(height,a6),d1			| d1.w: batch
+	cmp.w	d0,d1
+	blt.b	1f
+	move.w	d0,d1
+1:	// 	len -= batch;
+	// 	height -= batch;
+	sub.w	d1,(len,a6)
+	sub.w	d1,(height,a6)
+
+	subq.w	#1,d1				| dbra
+	clr.l	d0
+	move.b	(scaleIndexY,a6),d0
+	tst.w	d6
+	bmi.b	batch_loop_shadow
+batch_loop:
+	// 		do {
+	// 			if (compData.scaleTable[scaleIndexY++ & compData.scaleIndexMask] < _scaleY)
+	cmp.b	(a3,d0.l),d5
+	bls.b	2f
+	// 				if (color) {
+	tst.b	d6
+	beq.b	1f
+	// 					if (!(*mask & maskbit))
+	btst	d4,(a2)
+	bne.b	1f
+	// 						*dst = _palette[color];
+	move.b	d7,(a1)
+	// 				}
+1:	// 				dst  += pitch;
+	// 				mask += _numStrips;
+	add.l	d3,a1
+	add.l	d2,a2
+	// 			}
+2:	addq.b	#1,d0				| scaleIndexY++ (byte wrap)
+	// 		} while (--batch);
+	dbra	d1,batch_loop
+	bra.b	3f
+
+batch_loop_shadow:
+	// 		do {
+	// 			if (compData.scaleTable[scaleIndexY++ & compData.scaleIndexMask] < _scaleY) {
+	cmp.b	(a3,d0.l),d5
+	bls.b	2f
+	// 				if (color) {
+	tst.b	d6
+	beq.b	1f
+	// 					if (!(*mask & maskbit))
+	btst	d4,(a2)
+	bne.b	1f
+	// 						*dst = _shadowTable[*dst];
+	move.b	(a1),d7
+	move.b	(a4,d7.l),(a1)
+	// 				}
+1:	// 				dst  += pitch;
+	// 				mask += _numStrips;
+	add.l	d3,a1
+	add.l	d2,a2
+	// 			}
+2:	addq.b	#1,d0				| scaleIndexY++ (byte wrap)
+	// 		} while (--batch);
+	dbra	d1,batch_loop_shadow
+
+3:	move.b	d0,(scaleIndexY,a6)
+	// 	if (height == 0) {
+	tst.w	(height,a6)
+	bne.b	6f
+	// 		if (--compData.skipWidth == 0)
+	subq.w	#1,(compData.skipWidth,a5)
+	bne.b	4f
+	// 			return;
+	movem.l	(sp)+,d2-d7/a2-a5
+	unlk	a6
+	rts
+
+4:	// 		height = _height;
+	move.w	(arg_height,a6),(height,a6)
+	// 		scaleIndexY = compData.scaleYIndex;
+	move.b	(compData.scaleYIndex+3,a5),(scaleIndexY,a6)
+	// 		lastColumnX = compData.x;
+	sf.b	(shadowMask,a6)			| assume duplicate column (0x00)
+
+	move.l	(compData.scaleXStep,a5),d1	| d1.l: preload scaleXStep
+
+	// 		dst = compData.destPtr; (moved from the below)
+	move.l	(compData.destPtr,a5),a1	| a1: dst
+	// 		if (compData.scaleTable[compData.scaleXIndex] < _scaleX) {
+	clr.l	d0
+	move.b	(scaleIndexX,a6),d0
+	move.b	(a3,d0.l),d0
+	cmp.b	(arg_scaleX,a6),d0
+	bhs.b	5f
+
+	st.b	(shadowMask,a6)			| new column (0xFF)
+
+	// 			compData.x += compData.scaleXStep;
+	move.l	(compData.x,a5),d4
+	add.l	d1,d4
+	move.l	d4,(compData.x,a5)
+	// 			maskbit = revBitMask(compData.x & 7);
+	not.b	d4
+	and.b	#0b00000111,d4			| d4.b: bit to test
+	// 			compData.destPtr += compData.scaleXStep;
+	add.l	d1,a1
+	move.l	a1,(compData.destPtr,a5)
+	// 		}
+5:	// 		compData.scaleXIndex = (compData.scaleXIndex + compData.scaleXStep) & compData.scaleIndexMask;
+	add.b	d1,(scaleIndexX,a6)		| byte wrap (d1.b = LSB of scaleXStep)
+	// 		mask = compData.maskPtr + compData.x / 8;
+	move.l	(compData.x,a5),d0
+	asr.l	#3,d0
+	move.l	(compData.maskPtr,a5),a2
+	add.l	d0,a2				| a2: mask
+	// 	}
+6:	// StartPos:
+	// } while (len > 0);
+	tst.w	(len,a6)
+	bne.w	len_loop
+	// } while (true);
+	bra.w	main_loop
diff --git a/engines/scumm/m68k/bylerledecode_scaled_mode3.S b/engines/scumm/m68k/bylerledecode_scaled_mode3.S
new file mode 100644
index 00000000000..e30c99011ac
--- /dev/null
+++ b/engines/scumm/m68k/bylerledecode_scaled_mode3.S
@@ -0,0 +1,253 @@
+/* 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/>.
+ *
+ */
+
+| kate: tab-width 8; replace-tabs off; hl Motorola 68k (VASM/Devpac);
+
+#include "bylerledecode.inc"
+
+	.globl	SYM(ByleRLEDecode_Scaled_Mode3)
+
+	add_var	len,2		// uint16 len;
+	add_var	height,2	// uint16 height;
+	add_var	scaleTableX,4	// &compData.scaleTable[compData.scaleXIndex]
+	add_var	scaleTableY,4	// &compData.scaleTable[compData.scaleYIndex]
+	add_var	shadowMask,1+3	// 0xFF = draw shadows (new column), 0x00 = skip shadows (duplicate column) (align stack)
+
+	.text
+
+| void ByleRLEDecode_Scaled_Mode3(
+|	BaseCostumeRenderer::ByleRLEData *pcompData,
+|	const byte _scaleX,
+|	const byte _scaleY,
+|	const int _height,
+|	const int pitch,
+|	const int _numStrips,
+|	const byte *_srcPtr,
+|	const byte *_shadowTable,
+|	const uint16 *_palette);
+SYM(ByleRLEDecode_Scaled_Mode3):
+// TODO: __FASTCALL__
+	link	a6,#-sizeof_local_vars		| a6: frame pointer
+	movem.l	d2-d7/a2-a5,-(sp)
+
+	// registers taken so far:
+	// - d7(pcolor), d6(color), d5(_scaleY), d4(maskbit), d3(pitch), d2(_numStrips)
+	// - a0(src), a1(dst), a2(mask), a3(scaleTableY), a4(_shadowTable), a5(pcompData), a6(frame pointer)
+
+	// BaseCostumeRenderer::ByleRLEData &compData = *pcompData;
+	move.l	(arg_pcompData,a6),a5		| a5: pcompData
+
+	// const byte *src = _srcPtr;
+	move.l	(arg_srcPtr,a6),a0		| a0: src
+
+	// uint16 len = compData.repLen;
+	clr.w	(len,a6)
+	move.b	(compData.repLen,a5),(len+1,a6)
+	// uint16 color = compData.repColor;
+	clr.l	d6
+	move.b	(compData.repColor,a5),d6	| d6.b: color (used as long, too)
+	clr.l	d7				| used as long, too
+
+	// byte *dst = compData.destPtr;
+	move.l	(compData.destPtr,a5),a1	| a1: dst
+	// int lastColumnX = -1;
+	st.b	(shadowMask,a6)
+	// uint16 height = _height;
+	move.w	(arg_height,a6),(height,a6)
+	// int scaleIndexY = compData.scaleYIndex;
+	move.l	(compData.scaleXIndex,a5),d0
+	lea	([compData.scaleTable,a5],d0.l),a3
+	move.l	a3,(scaleTableX,a6)
+	move.l	(compData.scaleYIndex,a5),d0
+	lea	([compData.scaleTable,a5],d0.l),a3	| a3: scaleTableY
+	move.l	a3,(scaleTableY,a6)
+	// byte maskbit = revBitMask(compData.x & 7);
+	// #define revBitMask(x) (0x80 >> (x))
+	move.l	(compData.x,a5),d0
+	move.b	d0,d4
+	not.b	d4
+	and.b	#0b00000111,d4			| d4.b: bit to test
+	// const byte *mask = compData.maskPtr + compData.x / 8;
+	asr.l	#3,d0
+	move.l	(compData.maskPtr,a5),a2
+	add.l	d0,a2				| a2: mask
+
+	move.b	(arg_scaleY,a6),d5		| d5.b: _scaleY
+	move.l	(arg_pitch,a6),d3		| d3.w: pitch (used as long, too)
+	move.l	(arg_numStrips,a6),d2		| d2.w: _numStrips (used as long, too)
+	move.l	(arg_shadowTable,a6),a4		| a4: _shadowTable
+
+	// if (len) {
+	// 	--len;
+	// 	goto StartPos;
+	// }
+	// len   --len action
+	// 0     -1    jump into main_loop
+	// 1      0    jump into main_loop = jump into len_loop (0x)
+	// 2      1    jump into len_loop (1x)
+	// 3      2    jump into len_loop (2x)
+	subq.w	#1,(len,a6)
+	bgt.b	pcolor_setup
+main_loop:
+	// do {
+	// len = *src++;
+	// color = len >> compData.shr;
+	// len &= compData.mask;
+	// if (!len)
+	// 	len = *src++;
+	clr.w	d7
+	move.b	(a0)+,d7
+	move.w	d7,d6				| d6.w: clear the highest bit ("shadow_on" bit cleared)
+	move.b	(compData.shr,a5),d0
+	lsr.b	d0,d6				| d6.b: color (used as long, too)
+	and.b	(compData.mask,a5),d7
+	bne.b	1f
+	move.b	(a0)+,d7
+1:	move.w	d7,(len,a6)
+
+pcolor_setup:
+	tst.b	d6
+	beq.b	len_loop
+	move.w	([arg_palette,a6],d6.l*2),d7	| d7.w: pcolor = _palette[color] (used as long, too)
+	cmp.w	#8,d7
+	bhs.b	len_loop
+	lsl.w	#8,d7				| d7.w: if pcolor < 8: pcolor << 8
+	or.w	#0x8000,d6			| d6.w: "shadow_on" bit set
+	and.b	(shadowMask,a6),d6		| 0xFF: no-op; 0x00: clears color, suppresses writes
+len_loop:
+	// do {
+	// 	batch = height < len ? height : len;
+	move.w	(len,a6),d0
+	move.w	(height,a6),d1			| d1.w: batch
+	cmp.w	d0,d1
+	blt.b	1f
+	move.w	d0,d1
+1:	// 	len -= batch;
+	// 	height -= batch;
+	sub.w	d1,(len,a6)
+	sub.w	d1,(height,a6)
+
+	subq.w	#1,d1				| dbra
+	tst.w	d6
+	bmi.b	batch_loop_shadow
+batch_loop:
+	// 		do {
+	// 			if (compData.scaleTable[scaleIndexY++] < _scaleY)
+	cmp.b	(a3)+,d5
+	bls.b	2f
+	// 				if (color) {
+	tst.b	d6
+	beq.b	1f
+	// 					if (!(*mask & maskbit))
+	btst	d4,(a2)
+	bne.b	1f
+	// 						*dst = _palette[color];
+	move.b	d7,(a1)
+	// 				}
+1:	// 				dst  += pitch;
+	// 				mask += _numStrips;
+	add.l	d3,a1
+	add.l	d2,a2
+	// 			}
+2:	// 		} while (--batch);
+	dbra	d1,batch_loop
+	bra.b	3f
+
+batch_loop_shadow:
+	// 		do {
+	// 			if (compData.scaleTable[scaleIndexY++] < _scaleY) {
+	cmp.b	(a3)+,d5
+	bls.b	2f
+	// 				if (color) {
+	tst.b	d6
+	beq.b	1f
+	// 					if (!(*mask & maskbit)) {
+	btst	d4,(a2)
+	bne.b	1f
+	// 						uint16 pcolor = _palette[color];
+	// 						pcolor = (pcolor << 8) + *dst;
+	// 						*dst = _shadowTable[pcolor];
+	move.b	(a1),d7
+	move.b	(a4,d7.l),(a1)
+	// 					}
+	// 				}
+1:	// 				dst  += pitch;
+	// 				mask += _numStrips;
+	add.l	d3,a1
+	add.l	d2,a2
+	// 			}
+2:	// 		} while (--batch);
+	dbra	d1,batch_loop_shadow
+
+3:	// 	if (height == 0) {
+	tst.w	(height,a6)
+	bne.b	6f
+	// 		if (--compData.skipWidth == 0)
+	subq.w	#1,(compData.skipWidth,a5)
+	bne.b	4f
+	// 			return;
+	movem.l	(sp)+,d2-d7/a2-a5
+	unlk	a6
+	rts
+
+4:	// 		height = _height;
+	move.w	(arg_height,a6),(height,a6)
+	// 		scaleIndexY = compData.scaleYIndex;
+	move.l	(scaleTableY,a6),a3
+	// 		lastColumnX = compData.x;
+	sf.b	(shadowMask,a6)			| assume duplicate column (0x00)
+
+	move.l	(compData.scaleXStep,a5),d1	| d1.l: preload scaleXStep
+
+	// 		dst = compData.destPtr; (moved from the below)
+	move.l	(compData.destPtr,a5),a1	| a1: dst
+	// 		if (compData.scaleTable[compData.scaleXIndex] < _scaleX) {
+	move.b	(arg_scaleX,a6),d0
+	cmp.b	([scaleTableX,a6]),d0
+	bls.b	5f
+
+	st.b	(shadowMask,a6)			| new column (0xFF)
+
+	// 			compData.x += compData.scaleXStep;
+	move.l	(compData.x,a5),d4
+	add.l	d1,d4
+	move.l	d4,(compData.x,a5)
+	// 			maskbit = revBitMask(compData.x & 7);
+	not.b	d4
+	and.b	#0b00000111,d4			| d4.b: bit to test
+	// 			compData.destPtr += compData.scaleXStep;
+	add.l	d1,a1
+	move.l	a1,(compData.destPtr,a5)
+	// 		}
+5:	// 		compData.scaleXIndex += compData.scaleXStep;
+	add.l	d1,(scaleTableX,a6)
+	// 		mask = compData.maskPtr + compData.x / 8;
+	move.l	(compData.x,a5),d0
+	asr.l	#3,d0
+	move.l	(compData.maskPtr,a5),a2
+	add.l	d0,a2				| a2: mask
+	// 	}
+6:	// StartPos:
+	// } while (len > 0);
+	tst.w	(len,a6)
+	bne.w	len_loop
+	// } while (true);
+	bra.w	main_loop
diff --git a/engines/scumm/module.mk b/engines/scumm/module.mk
index 819efda0e67..0f8996fb794 100644
--- a/engines/scumm/module.mk
+++ b/engines/scumm/module.mk
@@ -106,6 +106,19 @@ MODULE_OBJS += \
 	debugger/resource.o
 endif
 
+ifdef USE_M68K_COSTUME_ASM
+MODULE_OBJS += \
+	m68k/bylerledecode_classic.o \
+	m68k/bylerledecode_mode0.o \
+	m68k/bylerledecode_mode1.o \
+	m68k/bylerledecode_mode3.o \
+	m68k/bylerledecode_scaled_mode0.o \
+	m68k/bylerledecode_scaled_mode0_smask.o \
+	m68k/bylerledecode_scaled_mode1.o \
+	m68k/bylerledecode_scaled_mode1_smask.o \
+	m68k/bylerledecode_scaled_mode3.o
+endif
+
 ifdef ENABLE_SCUMM_7_8
 MODULE_OBJS += \
 	nut_renderer.o \


Commit: 16b6d2a0aead785c32b0a698b75cea54d3c2fdd6
    https://github.com/scummvm/scummvm/commit/16b6d2a0aead785c32b0a698b75cea54d3c2fdd6
Author: Miro Kropacek (miro.kropacek at gmail.com)
Date: 2026-06-14T21:59:20+02:00

Commit Message:
SCUMM: byleRLEDecodeFast: Implement HE>=90 Mode3

Provide ByleRLEDecode_Mode3_HE90 in m68k assembly, too. I haven't been
able to see ByleRLEDecode_Scaled_Mode3_HE90_SMask in use, so keep as a C
function for now.

Changed paths:
    engines/scumm/base-costume-optimised.cpp
    engines/scumm/base-costume.cpp
    engines/scumm/base-costume.h
    engines/scumm/bylerledecodeM68K.h
    engines/scumm/m68k/bylerledecode_mode3.S


diff --git a/engines/scumm/base-costume-optimised.cpp b/engines/scumm/base-costume-optimised.cpp
index b35c4632c06..05dad74542a 100644
--- a/engines/scumm/base-costume-optimised.cpp
+++ b/engines/scumm/base-costume-optimised.cpp
@@ -265,13 +265,6 @@ byte BaseCostumeRenderer::paintCelByleRLECommon(
 	return drawFlag;
 }
 
-enum class ShadowMode : int {
-	Mode0,
-	Mode1,
-	Mode3,
-	Classic
-};
-
 #ifndef USE_M68K_COSTUME_ASM
 void ByleRLEDecode_Mode0(
 	BaseCostumeRenderer::ByleRLEData *pcompData,
@@ -513,6 +506,85 @@ void ByleRLEDecode_Mode3(
 }
 #endif
 
+#ifndef USE_M68K_COSTUME_ASM
+void ByleRLEDecode_Mode3_HE90(
+	BaseCostumeRenderer::ByleRLEData *pcompData,
+	const byte _scaleX, /* unused */
+	const byte _scaleY, /* unused */
+	const int _height,
+	const int pitch,
+	const int _numStrips,
+	const byte *_srcPtr,
+	const byte *_shadowTable,
+	const uint16 *_palette) {
+
+	BaseCostumeRenderer::ByleRLEData &compData = *pcompData;
+	//warning("%s: unexpected call, save your game and report", __FUNCTION__);
+
+	const byte *src = _srcPtr;
+
+	uint16 len = compData.repLen;
+	uint16 color = compData.repColor;
+
+	// reset every column
+	byte *dst = compData.destPtr;
+	uint16 height = _height;
+	byte maskbit = revBitMask(compData.x & 7);
+	const byte *mask = compData.maskPtr + compData.x / 8;
+
+	uint16 batch;
+	if (len) {
+		--len;
+		goto StartPos;
+	}
+
+	do {
+		len = *src++;
+		color = len >> compData.shr;
+		len &= compData.mask;
+		if (!len)
+			len = *src++;
+
+		do {
+			batch = height < len ? height : len;
+			len -= batch;
+			height -= batch;
+
+			assert(compData.x >= compData.boundsRect.left && compData.x < compData.boundsRect.right);
+
+			if (color) {
+				do {
+					if (!(*mask & maskbit)) {
+						uint16 pcolor = _palette[color];
+						pcolor = (pcolor << 8) + *dst;
+						*dst = _shadowTable[pcolor];
+					}
+					dst  += pitch;
+					mask += _numStrips;
+				} while (--batch);
+			} else {
+				dst  += batch * pitch;
+				mask += batch * _numStrips;
+			}
+
+			if (height == 0) {
+				if (--compData.skipWidth == 0)
+					return;
+				height = _height;
+
+				compData.x += compData.scaleXStep;
+				maskbit = revBitMask(compData.x & 7);
+				compData.destPtr += compData.scaleXStep;
+
+				dst = compData.destPtr;
+				mask = compData.maskPtr + compData.x / 8;
+			}
+		StartPos:;
+		} while (len > 0);
+	} while (true);
+}
+#endif
+
 #ifndef USE_M68K_COSTUME_ASM
 void ByleRLEDecode_Classic(
 	BaseCostumeRenderer::ByleRLEData *pcompData,
@@ -1034,6 +1106,95 @@ void ByleRLEDecode_Scaled_Mode3(
 }
 #endif
 
+void ByleRLEDecode_Scaled_Mode3_HE90_SMask(
+	Scumm::BaseCostumeRenderer::ByleRLEData *pcompData,
+	const byte _scaleX,
+	const byte _scaleY,
+	const int _height,
+	const int pitch,
+	const int _numStrips,
+	const byte *_srcPtr,
+	const byte *_shadowTable,
+	const uint16 *_palette) {
+
+	Scumm::BaseCostumeRenderer::ByleRLEData &compData = *pcompData;
+	warning("%s: unexpected call, save your game and report", __FUNCTION__);
+
+	const byte *src = _srcPtr;
+
+	byte len = compData.repLen;
+	uint16 color = compData.repColor;
+
+	// reset every column
+	byte *dst = compData.destPtr;
+	int lastColumnX = -1;
+	uint16 height = _height;
+	int scaleIndexY = compData.scaleYIndex;
+	byte maskbit = revBitMask(compData.x & 7);
+	const byte *mask = compData.maskPtr + compData.x / 8;
+
+	byte batch;
+	if (len) {
+		--len;
+		goto StartPos;
+	}
+
+	do {
+		len = *src++;
+		color = len >> compData.shr;
+		len &= compData.mask;
+		if (!len)
+			len = *src++;
+
+		do {
+			batch = height < len ? (byte)height : len;
+			len -= batch;
+			height -= batch;
+
+			assert(compData.x >= compData.boundsRect.left && compData.x < compData.boundsRect.right);
+
+			do {
+				if (compData.scaleTable[scaleIndexY++ & compData.scaleIndexMask] < _scaleY) {
+					if (color) {
+						if (!(*mask & maskbit)) {
+							uint16 pcolor;
+
+							pcolor = _palette[color];
+							if (lastColumnX != compData.x) {
+								pcolor = (pcolor << 8) + *dst;
+								*dst = _shadowTable[pcolor];
+							}
+						}
+					}
+					dst += pitch;
+					mask += _numStrips;
+				}
+			} while (--batch);
+
+			if (height == 0) {
+				if (--compData.skipWidth == 0)
+					return;
+				height = _height;
+
+				scaleIndexY = compData.scaleYIndex;
+				lastColumnX = compData.x;
+
+				if (compData.scaleTable[compData.scaleXIndex] < _scaleX) {
+					compData.x += compData.scaleXStep;
+					maskbit = revBitMask(compData.x & 7);
+					compData.destPtr += compData.scaleXStep;
+				}
+
+				compData.scaleXIndex = (compData.scaleXIndex + compData.scaleXStep) & compData.scaleIndexMask;
+
+				dst = compData.destPtr;
+				mask = compData.maskPtr + compData.x / 8;
+			}
+		StartPos:;
+		} while (len > 0);
+	} while (true);
+}
+
 void ByleRLEDecode_Scaled_Classic_SMask(
 	BaseCostumeRenderer::ByleRLEData *pcompData,
 	const byte _scaleX,
@@ -1118,6 +1279,14 @@ void ByleRLEDecode_Scaled_Classic_SMask(
 	} while (true);
 }
 
+enum class ShadowMode : int {
+	Mode0,
+	Mode1,
+	Mode3,	// COMI: pcolor < 8
+	Mode3_HE,	// HE >= 90
+	Classic
+};
+
 typedef void (*ByleRLEDecodeFunc)(
 	BaseCostumeRenderer::ByleRLEData *pcompData,
 	const byte _scaleX,
@@ -1129,36 +1298,46 @@ typedef void (*ByleRLEDecodeFunc)(
 	const byte *_shadowTable,
 	const uint16 *_palette);
 
-static const ByleRLEDecodeFunc byleRLEDecodeNoScaleTable[4] = {
-	ByleRLEDecode_Mode0,    // 0: Mode0
-	ByleRLEDecode_Mode1,    // 1: Mode1
-	ByleRLEDecode_Mode3,    // 2: Mode3
-	ByleRLEDecode_Classic,  // 3: Classic
+static const ByleRLEDecodeFunc byleRLEDecodeNoScaleTable[5] = {
+	ByleRLEDecode_Mode0,       // 0: Mode0
+	ByleRLEDecode_Mode1,       // 1: Mode1
+	ByleRLEDecode_Mode3,       // 2: Mode3 COMI
+	ByleRLEDecode_Mode3_HE90,  // 3: Mode3 HE >= 90
+	ByleRLEDecode_Classic,     // 4: Classic
 };
 
-static const ByleRLEDecodeFunc byleRLEDecodeScaledTable[8] = {
-	ByleRLEDecode_Scaled_Mode0,         // 0: Mode0,   no scaleIndexMask
-	ByleRLEDecode_Scaled_Mode0_SMask,   // 1: Mode0,   scaleIndexMask
-	ByleRLEDecode_Scaled_Mode1,         // 2: Mode1,   no scaleIndexMask
-	ByleRLEDecode_Scaled_Mode1_SMask,   // 3: Mode1,   scaleIndexMask
-	ByleRLEDecode_Scaled_Mode3,         // 4: Mode3,   no scaleIndexMask
-	nullptr,                            // 5: Mode3,   scaleIndexMask (COMI's Mode3 always uses bigCostumeScaleTable)
-	nullptr,                            // 6: Classic, no scaleIndexMask (_shadowMode & 0x20 always uses smallCostumeScaleTable)
-	ByleRLEDecode_Scaled_Classic_SMask, // 7: Classic, scaleIndexMask
+static const ByleRLEDecodeFunc byleRLEDecodeScaledTable[10] = {
+	ByleRLEDecode_Scaled_Mode0,            // 0: Mode0,   no scaleIndexMask
+	ByleRLEDecode_Scaled_Mode0_SMask,      // 1: Mode0,   scaleIndexMask
+	ByleRLEDecode_Scaled_Mode1,            // 2: Mode1,   no scaleIndexMask
+	ByleRLEDecode_Scaled_Mode1_SMask,      // 3: Mode1,   scaleIndexMask
+	ByleRLEDecode_Scaled_Mode3,            // 4: Mode3,   no scaleIndexMask
+	nullptr,                               // 5: Mode3,   scaleIndexMask (COMI's Mode3 always uses bigCostumeScaleTable)
+	nullptr,                               // 6: HE>=90,  no scaleIndexMask (HE>=90 always uses smallCostumeScaleTable)
+	ByleRLEDecode_Scaled_Mode3_HE90_SMask, // 7: HE>=90,  scaleIndexMask (unconfirmed)
+	nullptr,                               // 8: Classic, no scaleIndexMask (_shadowMode & 0x20 always uses smallCostumeScaleTable)
+	ByleRLEDecode_Scaled_Classic_SMask,    // 9: Classic, scaleIndexMask (unconfirmed)
 };
 
-void BaseCostumeRenderer::byleRLEDecodeFast(ByleRLEData &compData) {
+void BaseCostumeRenderer::byleRLEDecodeFast(ByleRLEData &compData, const byte *xmap) {
 	ShadowMode shadowMode = ShadowMode::Mode0;
+	const byte *shadowTable = _shadowTable;
 	if (!_akosRendering) {
 		if (_shadowMode & 0x20)
 			shadowMode = ShadowMode::Classic;
 		else if (_shadowTable)
 			shadowMode = ShadowMode::Mode1;
 	} else {
-		if (_shadowMode == 1)
+		if (_shadowMode == 1) {
 			shadowMode = ShadowMode::Mode1;
-		else if (_shadowMode == 3)
-			shadowMode = ShadowMode::Mode3;
+		} else if (_shadowMode == 3) {
+			if (_vm->_game.heversion >= 90) {
+				shadowMode = ShadowMode::Mode3_HE;
+				shadowTable = xmap;
+			} else {
+				shadowMode = ShadowMode::Mode3;
+			}
+		}
 	}
 
 	if (compData.y >= compData.boundsRect.top && compData.y + compData.scaledHeight <= compData.boundsRect.bottom) {
@@ -1172,7 +1351,7 @@ void BaseCostumeRenderer::byleRLEDecodeFast(ByleRLEData &compData) {
 				_out.pitch,
 				_numStrips,
 				_srcPtr,
-				_shadowTable,
+				shadowTable,
 				_palette);
 		} else {
 			const int useScaleIndexMask = compData.scaleIndexMask != -1;
@@ -1185,7 +1364,7 @@ void BaseCostumeRenderer::byleRLEDecodeFast(ByleRLEData &compData) {
 				_out.pitch,
 				_numStrips,
 				_srcPtr,
-				_shadowTable,
+				shadowTable,
 				_palette);
 		}
 		return;
@@ -1268,6 +1447,14 @@ void BaseCostumeRenderer::byleRLEDecodeFast(ByleRLEData &compData) {
 									*dst = pcolor;
 								}
 								break;
+
+							case ShadowMode::Mode3_HE:
+								pcolor = _palette[color];
+								if (lastColumnX != compData.x) {
+									pcolor = (pcolor << 8) + *dst;
+									*dst = xmap[pcolor];
+								}
+								break;
 							}
 						}
 					}
diff --git a/engines/scumm/base-costume.cpp b/engines/scumm/base-costume.cpp
index 13b32bdf578..b9e7cfbd2a5 100644
--- a/engines/scumm/base-costume.cpp
+++ b/engines/scumm/base-costume.cpp
@@ -291,10 +291,10 @@ byte BaseCostumeRenderer::paintCelByleRLECommon(
 void BaseCostumeRenderer::byleRLEDecode(ByleRLEData &compData, int16 actorHitX, int16 actorHitY, bool *actorHitResult, const uint8 *xmap) {
 #ifdef SCUMM_OPTIMISED_CODE
 	if ((_vm->_bytesPerPixel == 1) &&
-		(!_akosRendering || _shadowMode != 3 || (!(_vm->_game.features & GF_16BIT_COLOR) && _vm->_game.heversion < 90)) &&
+		(!(_vm->_game.features & GF_16BIT_COLOR)) &&
 		(actorHitResult == NULL) &&
 		(compData.maskPtr != NULL)) {
-		byleRLEDecodeFast(compData);
+		byleRLEDecodeFast(compData, xmap);
 		return;
 	}
 	warning("%s: unoptimised version is being executed", __FUNCTION__);
diff --git a/engines/scumm/base-costume.h b/engines/scumm/base-costume.h
index 1268a39a677..16642542c65 100644
--- a/engines/scumm/base-costume.h
+++ b/engines/scumm/base-costume.h
@@ -177,7 +177,7 @@ protected:
 
 	void byleRLEDecode(ByleRLEData &compData, int16 actorHitX = 0, int16 actorHitY = 0, bool *actorHitResult = nullptr, const uint8 *xmap = nullptr);
 #ifdef SCUMM_OPTIMISED_CODE
-	void byleRLEDecodeFast(ByleRLEData &compData);
+	void byleRLEDecodeFast(ByleRLEData &compData, const byte *xmap);
 #endif
 	void skipCelLines(ByleRLEData &compData, int num);
 
diff --git a/engines/scumm/bylerledecodeM68K.h b/engines/scumm/bylerledecodeM68K.h
index a7e5f254140..6495759a256 100644
--- a/engines/scumm/bylerledecodeM68K.h
+++ b/engines/scumm/bylerledecodeM68K.h
@@ -57,6 +57,17 @@ extern "C" void ByleRLEDecode_Mode3(
 	const byte *_shadowTable,
 	const uint16 *_palette);
 
+extern "C" void ByleRLEDecode_Mode3_HE90(
+	Scumm::BaseCostumeRenderer::ByleRLEData *pcompData,
+	const byte _scaleX,
+	const byte _scaleY,
+	const int _height,
+	const int pitch,
+	const int _numStrips,
+	const byte *_srcPtr,
+	const byte *_shadowTable,
+	const uint16 *_palette);
+
 extern "C" void ByleRLEDecode_Classic(
 	Scumm::BaseCostumeRenderer::ByleRLEData *pcompData,
 	const byte _scaleX, /* unused */
diff --git a/engines/scumm/m68k/bylerledecode_mode3.S b/engines/scumm/m68k/bylerledecode_mode3.S
index fa921e378b1..9a063ab3e61 100644
--- a/engines/scumm/m68k/bylerledecode_mode3.S
+++ b/engines/scumm/m68k/bylerledecode_mode3.S
@@ -24,8 +24,10 @@
 #include "bylerledecode.inc"
 
 	.globl	SYM(ByleRLEDecode_Mode3)
+	.globl	SYM(ByleRLEDecode_Mode3_HE90)
 
 	add_var	len,2+2	// uint16 len; (align stack)
+	add_var	shadow_threshold,2
 
 
 	.text
@@ -41,9 +43,27 @@
 |	const byte *_shadowTable,
 |	const uint16 *_palette);
 SYM(ByleRLEDecode_Mode3):
+	moveq	#8,d0
+	bra.b	mode3_common
+
+| void ByleRLEDecode_Mode3_HE90(
+|	BaseCostumeRenderer::ByleRLEData *pcompData,
+|	const byte _scaleX, /* unused */
+|	const byte _scaleY, /* unused */
+|	const int _height,
+|	const int pitch,
+|	const int _numStrips,
+|	const byte *_srcPtr,
+|	const byte *_shadowTable,
+|	const uint16 *_palette);
+SYM(ByleRLEDecode_Mode3_HE90):
+	move.w	#256,d0
+
+mode3_common:
 // TODO: __FASTCALL__
 	link	a6,#-sizeof_local_vars		| a6: frame pointer
 	movem.l	d2-d7/a2-a5,-(sp)
+	move.w	d0,(shadow_threshold,a6)
 
 	// registers taken so far:
 	// - d7(pcolor), d6(color), d5(height), d4(maskbit), d3(pitch), d2(_numStrips)
@@ -115,7 +135,7 @@ pcolor_setup:
 	tst.b	d6
 	beq.b	len_loop
 	move.w	(a3,d6.l*2),d7			| d7.w: pcolor = _palette[color] (used as long, too)
-	cmp.w	#8,d7
+	cmp.w	(shadow_threshold,a6),d7
 	bhs.b	len_loop
 	lsl.w	#8,d7				| d7.w: if pcolor < 8: pcolor << 8
 	or.w	#0x8000,d6			| d6.w: "shadow_on" bit set


Commit: ff472311ac8c33c0a450747a1afb9775b88b0f29
    https://github.com/scummvm/scummvm/commit/ff472311ac8c33c0a450747a1afb9775b88b0f29
Author: Miro Kropacek (miro.kropacek at gmail.com)
Date: 2026-06-14T21:59:20+02:00

Commit Message:
SCUMM: byleRLEDecodeFast: Prefer non-exported labels with .L

Hatari profiler should be now less confused.

Changed paths:
    engines/scumm/m68k/bylerledecode_classic.S
    engines/scumm/m68k/bylerledecode_mode0.S
    engines/scumm/m68k/bylerledecode_mode1.S
    engines/scumm/m68k/bylerledecode_mode3.S
    engines/scumm/m68k/bylerledecode_scaled_mode0.S
    engines/scumm/m68k/bylerledecode_scaled_mode0_smask.S
    engines/scumm/m68k/bylerledecode_scaled_mode1.S
    engines/scumm/m68k/bylerledecode_scaled_mode1_smask.S
    engines/scumm/m68k/bylerledecode_scaled_mode3.S


diff --git a/engines/scumm/m68k/bylerledecode_classic.S b/engines/scumm/m68k/bylerledecode_classic.S
index 590062d7d31..01dd94dab49 100644
--- a/engines/scumm/m68k/bylerledecode_classic.S
+++ b/engines/scumm/m68k/bylerledecode_classic.S
@@ -92,8 +92,8 @@ SYM(ByleRLEDecode_Classic):
 	// 2      1    jump into len_loop (1x)
 	// 3      2    jump into len_loop (2x)
 	subq.w	#1,d6
-	bgt.b	len_loop
-main_loop:
+	bgt.b	.Llen_loop
+.Lmain_loop:
 	// do {
 	// len = *src++;
 	// color = len >> compData.shr;
@@ -107,9 +107,9 @@ main_loop:
 	lsr.b	d1,d0				| d0.b: color (only care about zero/nonzero)
 	move.b	d0,(color,a6)
 	and.b	(compData.mask,a5),d6		| d6.w: len
-	bne.b	len_loop
+	bne.b	.Llen_loop
 	move.b	(a0)+,d6
-len_loop:
+.Llen_loop:
 	// do {
 	// 	batch = height < len ? height : len;
 	move.w	d5,d1				| d1.w: batch
@@ -125,7 +125,7 @@ len_loop:
 	beq.b	2f
 
 	subq.w	#1,d1				| dbra
-batch_loop_shadow:
+.Lbatch_loop_shadow:
 	// 		do {
 	// 			if (!(*mask & maskbit))
 	btst	d4,(a2)
@@ -138,7 +138,7 @@ batch_loop_shadow:
 	add.l	d3,a1
 	add.l	d2,a2
 	// 		} while (--batch);
-	dbra	d1,batch_loop_shadow
+	dbra	d1,.Lbatch_loop_shadow
 	bra.b	3f
 
 	// 	} else {
@@ -185,6 +185,6 @@ batch_loop_shadow:
 5:	// StartPos:
 	// } while (len > 0);
 	tst.w	d6
-	bne.b	len_loop
+	bne.b	.Llen_loop
 	// } while (true);
-	bra.w	main_loop
+	bra.w	.Lmain_loop
diff --git a/engines/scumm/m68k/bylerledecode_mode0.S b/engines/scumm/m68k/bylerledecode_mode0.S
index 4dfb7f0f12b..e808437c2a2 100644
--- a/engines/scumm/m68k/bylerledecode_mode0.S
+++ b/engines/scumm/m68k/bylerledecode_mode0.S
@@ -93,8 +93,8 @@ SYM(ByleRLEDecode_Mode0):
 	// 2      1    jump into len_loop (1x)
 	// 3      2    jump into len_loop (2x)
 	subq.w	#1,d6
-	bgt.b	pcolor_setup
-main_loop:
+	bgt.b	.Lpcolor_setup
+.Lmain_loop:
 	// do {
 	// len = *src++;
 	// color = len >> compData.shr;
@@ -108,11 +108,11 @@ main_loop:
 	lsr.b	d1,d0				| d0.b: color (used as long, too)
 	move.b	d0,(color,a6)
 	and.b	(compData.mask,a5),d6		| d6.w: len
-	bne.b	pcolor_setup
+	bne.b	.Lpcolor_setup
 	move.b	(a0)+,d6
-pcolor_setup:
+.Lpcolor_setup:
 	move.w	(a3,d0.l*2),d7			| d7.w: _palette[color]
-len_loop:
+.Llen_loop:
 	// do {
 	// 	batch = height < len ? height : len;
 	move.w	d5,d1				| d1.w: batch
@@ -128,7 +128,7 @@ len_loop:
 	beq.b	2f
 
 	subq.w	#1,d1				| dbra
-batch_loop:
+.Lbatch_loop:
 	// 		do {
 	// 			if (!(*mask & maskbit))
 	btst	d4,(a2)
@@ -140,7 +140,7 @@ batch_loop:
 	add.l	d3,a1
 	add.l	d2,a2
 	// 		} while (--batch);
-	dbra	d1,batch_loop
+	dbra	d1,.Lbatch_loop
 	bra.b	3f
 	// 	} else {
 2:	// 		dst  += batch * pitch;
@@ -186,6 +186,6 @@ batch_loop:
 5:	// StartPos:
 	// } while (len > 0);
 	tst.w	d6
-	bne.b	len_loop
+	bne.b	.Llen_loop
 	// } while (true);
-	bra.w	main_loop
+	bra.w	.Lmain_loop
diff --git a/engines/scumm/m68k/bylerledecode_mode1.S b/engines/scumm/m68k/bylerledecode_mode1.S
index 6b11f50c2e1..a189ad706c6 100644
--- a/engines/scumm/m68k/bylerledecode_mode1.S
+++ b/engines/scumm/m68k/bylerledecode_mode1.S
@@ -93,8 +93,8 @@ SYM(ByleRLEDecode_Mode1):
 	// 2      1    jump into len_loop (1x)
 	// 3      2    jump into len_loop (2x)
 	subq.w	#1,(len,a6)
-	bgt.b	pcolor_setup
-main_loop:
+	bgt.b	.Lpcolor_setup
+.Lmain_loop:
 	// do {
 	// len = *src++;
 	// color = len >> compData.shr;
@@ -111,14 +111,14 @@ main_loop:
 	move.b	(a0)+,d7
 1:	move.w	d7,(len,a6)
 
-pcolor_setup:
+.Lpcolor_setup:
 	tst.b	d6
-	beq.b	len_loop
+	beq.b	.Llen_loop
 	move.w	(a3,d6.l*2),d7			| d7.w: pcolor = _palette[color] (used as long, too)
 	cmp.w	#13,d7
-	bne.b	len_loop
+	bne.b	.Llen_loop
 	or.w	#0x8000,d6			| d6.w: "shadow_on" bit set
-len_loop:
+.Llen_loop:
 	// do {
 	// 	batch = height < len ? height : len;
 	move.w	(len,a6),d0
@@ -136,8 +136,8 @@ len_loop:
 
 	subq.w	#1,d1				| dbra
 	tst.w	d6
-	bmi.b	batch_loop_shadow
-batch_loop:
+	bmi.b	.Lbatch_loop_shadow
+.Lbatch_loop:
 	// 		do {
 	// 			if (!(*mask & maskbit))
 	btst	d4,(a2)
@@ -149,10 +149,10 @@ batch_loop:
 	add.l	d3,a1
 	add.l	d2,a2
 	// 		} while (--batch);
-	dbra	d1,batch_loop
+	dbra	d1,.Lbatch_loop
 	bra.b	3f
 
-batch_loop_shadow:
+.Lbatch_loop_shadow:
 	// 		do {
 	// 			if (!(*mask & maskbit))
 	btst	d4,(a2)
@@ -165,7 +165,7 @@ batch_loop_shadow:
 	add.l	d3,a1
 	add.l	d2,a2
 	// 		} while (--batch);
-	dbra	d1,batch_loop_shadow
+	dbra	d1,.Lbatch_loop_shadow
 	bra.b	3f
 
 	// 	} else {
@@ -213,6 +213,6 @@ batch_loop_shadow:
 5:	// StartPos:
 	// } while (len > 0);
 	tst.w	(len,a6)
-	bne.w	len_loop
+	bne.w	.Llen_loop
 	// } while (true);
-	bra.w	main_loop
+	bra.w	.Lmain_loop
diff --git a/engines/scumm/m68k/bylerledecode_mode3.S b/engines/scumm/m68k/bylerledecode_mode3.S
index 9a063ab3e61..d6cf1794ad1 100644
--- a/engines/scumm/m68k/bylerledecode_mode3.S
+++ b/engines/scumm/m68k/bylerledecode_mode3.S
@@ -44,7 +44,7 @@
 |	const uint16 *_palette);
 SYM(ByleRLEDecode_Mode3):
 	moveq	#8,d0
-	bra.b	mode3_common
+	bra.b	.Lmode3_common
 
 | void ByleRLEDecode_Mode3_HE90(
 |	BaseCostumeRenderer::ByleRLEData *pcompData,
@@ -59,7 +59,7 @@ SYM(ByleRLEDecode_Mode3):
 SYM(ByleRLEDecode_Mode3_HE90):
 	move.w	#256,d0
 
-mode3_common:
+.Lmode3_common:
 // TODO: __FASTCALL__
 	link	a6,#-sizeof_local_vars		| a6: frame pointer
 	movem.l	d2-d7/a2-a5,-(sp)
@@ -113,8 +113,8 @@ mode3_common:
 	// 2      1    jump into len_loop (1x)
 	// 3      2    jump into len_loop (2x)
 	subq.w	#1,(len,a6)
-	bgt.b	pcolor_setup
-main_loop:
+	bgt.b	.Lpcolor_setup
+.Lmain_loop:
 	// do {
 	// len = *src++;
 	// color = len >> compData.shr;
@@ -131,15 +131,15 @@ main_loop:
 	move.b	(a0)+,d7
 1:	move.w	d7,(len,a6)
 
-pcolor_setup:
+.Lpcolor_setup:
 	tst.b	d6
-	beq.b	len_loop
+	beq.b	.Llen_loop
 	move.w	(a3,d6.l*2),d7			| d7.w: pcolor = _palette[color] (used as long, too)
 	cmp.w	(shadow_threshold,a6),d7
-	bhs.b	len_loop
+	bhs.b	.Llen_loop
 	lsl.w	#8,d7				| d7.w: if pcolor < 8: pcolor << 8
 	or.w	#0x8000,d6			| d6.w: "shadow_on" bit set
-len_loop:
+.Llen_loop:
 	// do {
 	// 	batch = height < len ? height : len;
 	move.w	(len,a6),d0
@@ -157,8 +157,8 @@ len_loop:
 
 	subq.w	#1,d1				| dbra
 	tst.w	d6
-	bmi.b	batch_loop_shadow
-batch_loop:
+	bmi.b	.Lbatch_loop_shadow
+.Lbatch_loop:
 	// 		do {
 	// 			if (!(*mask & maskbit))
 	btst	d4,(a2)
@@ -170,10 +170,10 @@ batch_loop:
 	add.l	d3,a1
 	add.l	d2,a2
 	// 		} while (--batch);
-	dbra	d1,batch_loop
+	dbra	d1,.Lbatch_loop
 	bra.b	3f
 
-batch_loop_shadow:
+.Lbatch_loop_shadow:
 	// 		do {
 	// 			if (!(*mask & maskbit)) {
 	btst	d4,(a2)
@@ -189,7 +189,7 @@ batch_loop_shadow:
 	add.l	d3,a1
 	add.l	d2,a2
 	// 		} while (--batch);
-	dbra	d1,batch_loop_shadow
+	dbra	d1,.Lbatch_loop_shadow
 	bra.b	3f
 
 	// 	} else {
@@ -237,6 +237,6 @@ batch_loop_shadow:
 5:	// StartPos:
 	// } while (len > 0);
 	tst.w	(len,a6)
-	bne.w	len_loop
+	bne.w	.Llen_loop
 	// } while (true);
-	bra.w	main_loop
+	bra.w	.Lmain_loop
diff --git a/engines/scumm/m68k/bylerledecode_scaled_mode0.S b/engines/scumm/m68k/bylerledecode_scaled_mode0.S
index e960b0bf68a..ea086e252ea 100644
--- a/engines/scumm/m68k/bylerledecode_scaled_mode0.S
+++ b/engines/scumm/m68k/bylerledecode_scaled_mode0.S
@@ -102,8 +102,8 @@ SYM(ByleRLEDecode_Scaled_Mode0):
 	// 2      1    jump into len_loop (1x)
 	// 3      2    jump into len_loop (2x)
 	subq.w	#1,d6
-	bgt.b	pcolor_setup
-main_loop:
+	bgt.b	.Lpcolor_setup
+.Lmain_loop:
 	// do {
 	// len = *src++;
 	// color = len >> compData.shr;
@@ -117,11 +117,11 @@ main_loop:
 	lsr.b	d1,d0				| d0.b: color
 	move.b	d0,(color,a6)
 	and.b	(compData.mask,a5),d6		| d6.w: len
-	bne.b	pcolor_setup
+	bne.b	.Lpcolor_setup
 	move.b	(a0)+,d6
-pcolor_setup:
+.Lpcolor_setup:
 	move.w	([arg_palette,a6],d0.l*2),d7	| d7.w: _palette[color]
-len_loop:
+.Llen_loop:
 	// do {
 	// 	batch = height < len ? height : len;
 	move.w	(height,a6),d1			| d1.w: batch
@@ -134,7 +134,7 @@ len_loop:
 	sub.w	d1,(height,a6)
 
 	subq.w	#1,d1				| dbra
-batch_loop:
+.Lbatch_loop:
 	// 		do {
 	// 			if (compData.scaleTable[scaleIndexY++] < _scaleY)
 	cmp.b	(a3)+,d5
@@ -154,7 +154,7 @@ batch_loop:
 	add.l	d2,a2
 	// 			}
 2:	// 		} while (--batch);
-	dbra	d1,batch_loop
+	dbra	d1,.Lbatch_loop
 
 	// 	if (height == 0) {
 	tst.w	(height,a6)
@@ -204,6 +204,6 @@ batch_loop:
 6:	// StartPos:
 	// } while (len > 0);
 	tst.w	d6
-	bne.b	len_loop
+	bne.b	.Llen_loop
 	// } while (true);
-	bra.w	main_loop
+	bra.w	.Lmain_loop
diff --git a/engines/scumm/m68k/bylerledecode_scaled_mode0_smask.S b/engines/scumm/m68k/bylerledecode_scaled_mode0_smask.S
index 6a5a39838bf..3357f132e84 100644
--- a/engines/scumm/m68k/bylerledecode_scaled_mode0_smask.S
+++ b/engines/scumm/m68k/bylerledecode_scaled_mode0_smask.S
@@ -100,8 +100,8 @@ SYM(ByleRLEDecode_Scaled_Mode0_SMask):
 	// 2      1    jump into len_loop (1x)
 	// 3      2    jump into len_loop (2x)
 	subq.w	#1,d6
-	bgt.b	pcolor_setup
-main_loop:
+	bgt.b	.Lpcolor_setup
+.Lmain_loop:
 	// do {
 	// len = *src++;
 	// color = len >> compData.shr;
@@ -115,11 +115,11 @@ main_loop:
 	lsr.b	d1,d0				| d0.b: color
 	move.b	d0,(color,a6)
 	and.b	(compData.mask,a5),d6		| d6.w: len
-	bne.b	pcolor_setup
+	bne.b	.Lpcolor_setup
 	move.b	(a0)+,d6
-pcolor_setup:
+.Lpcolor_setup:
 	move.w	([arg_palette,a6],d0.l*2),d7	| d7.w: _palette[color]
-len_loop:
+.Llen_loop:
 	// do {
 	// 	batch = height < len ? height : len;
 	move.w	(height,a6),d1			| d1.w: batch
@@ -137,7 +137,7 @@ len_loop:
 	clr.l	d0
 	move.b	(scaleIndexY,a6),d0
 
-batch_loop:
+.Lbatch_loop:
 	// 		do {
 	// 			if (compData.scaleTable[scaleIndexY++ & compData.scaleIndexMask] < _scaleY)
 	cmp.b	(a3,d0.l),d5
@@ -158,7 +158,7 @@ batch_loop:
 	// 			}
 2:	addq.b	#1,d0				| scaleIndexY++ (byte wrap)
 	// 		} while (--batch);
-	dbra	d1,batch_loop
+	dbra	d1,.Lbatch_loop
 
 	move.b	d0,(scaleIndexY,a6)
 	// 	if (height == 0) {
@@ -211,6 +211,6 @@ batch_loop:
 6:	// StartPos:
 	// } while (len > 0);
 	tst.w	d6
-	bne.w	len_loop
+	bne.w	.Llen_loop
 	// } while (true);
-	bra.w	main_loop
+	bra.w	.Lmain_loop
diff --git a/engines/scumm/m68k/bylerledecode_scaled_mode1.S b/engines/scumm/m68k/bylerledecode_scaled_mode1.S
index 1c78f1bcc90..866c4c47e7d 100644
--- a/engines/scumm/m68k/bylerledecode_scaled_mode1.S
+++ b/engines/scumm/m68k/bylerledecode_scaled_mode1.S
@@ -105,8 +105,8 @@ SYM(ByleRLEDecode_Scaled_Mode1):
 	// 2      1    jump into len_loop (1x)
 	// 3      2    jump into len_loop (2x)
 	subq.w	#1,(len,a6)
-	bgt.b	pcolor_setup
-main_loop:
+	bgt.b	.Lpcolor_setup
+.Lmain_loop:
 	// do {
 	// len = *src++;
 	// color = len >> compData.shr;
@@ -123,15 +123,15 @@ main_loop:
 	move.b	(a0)+,d7
 1:	move.w	d7,(len,a6)
 
-pcolor_setup:
+.Lpcolor_setup:
 	tst.b	d6
-	beq.b	len_loop
+	beq.b	.Llen_loop
 	move.w	([arg_palette,a6],d6.l*2),d7	| d7.w: pcolor = _palette[color] (used as long, too)
 	cmp.w	#13,d7
-	bne.b	len_loop
+	bne.b	.Llen_loop
 	or.w	#0x8000,d6			| d6.w: "shadow_on" bit set
 	and.b	(shadowMask,a6),d6		| 0xFF: no-op; 0x00: clears color, suppresses writes
-len_loop:
+.Llen_loop:
 	// do {
 	// 	batch = height < len ? height : len;
 	move.w	(len,a6),d0
@@ -146,8 +146,8 @@ len_loop:
 
 	subq.w	#1,d1				| dbra
 	tst.w	d6
-	bmi.b	batch_loop_shadow
-batch_loop:
+	bmi.b	.Lbatch_loop_shadow
+.Lbatch_loop:
 	// 		do {
 	// 			if (compData.scaleTable[scaleIndexY++] < _scaleY)
 	cmp.b	(a3)+,d5
@@ -167,10 +167,10 @@ batch_loop:
 	add.l	d2,a2
 	// 			}
 2:	// 		} while (--batch);
-	dbra	d1,batch_loop
+	dbra	d1,.Lbatch_loop
 	bra.b	3f
 
-batch_loop_shadow:
+.Lbatch_loop_shadow:
 	// 		do {
 	// 			if (compData.scaleTable[scaleIndexY++] < _scaleY) {
 	cmp.b	(a3)+,d5
@@ -191,7 +191,7 @@ batch_loop_shadow:
 	add.l	d2,a2
 	// 			}
 2:	// 		} while (--batch);
-	dbra	d1,batch_loop_shadow
+	dbra	d1,.Lbatch_loop_shadow
 
 3:	// 	if (height == 0) {
 	tst.w	(height,a6)
@@ -244,6 +244,6 @@ batch_loop_shadow:
 6:	// StartPos:
 	// } while (len > 0);
 	tst.w	(len,a6)
-	bne.w	len_loop
+	bne.w	.Llen_loop
 	// } while (true);
-	bra.w	main_loop
+	bra.w	.Lmain_loop
diff --git a/engines/scumm/m68k/bylerledecode_scaled_mode1_smask.S b/engines/scumm/m68k/bylerledecode_scaled_mode1_smask.S
index 506495963ef..5db44efbd44 100644
--- a/engines/scumm/m68k/bylerledecode_scaled_mode1_smask.S
+++ b/engines/scumm/m68k/bylerledecode_scaled_mode1_smask.S
@@ -103,8 +103,8 @@ SYM(ByleRLEDecode_Scaled_Mode1_SMask):
 	// 2      1    jump into len_loop (1x)
 	// 3      2    jump into len_loop (2x)
 	subq.w	#1,(len,a6)
-	bgt.b	pcolor_setup
-main_loop:
+	bgt.b	.Lpcolor_setup
+.Lmain_loop:
 	// do {
 	// len = *src++;
 	// color = len >> compData.shr;
@@ -121,15 +121,15 @@ main_loop:
 	move.b	(a0)+,d7
 1:	move.w	d7,(len,a6)
 
-pcolor_setup:
+.Lpcolor_setup:
 	tst.b	d6
-	beq.b	len_loop
+	beq.b	.Llen_loop
 	move.w	([arg_palette,a6],d6.l*2),d7	| d7.w: pcolor = _palette[color] (used as long, too)
 	cmp.w	#13,d7
-	bne.b	len_loop
+	bne.b	.Llen_loop
 	or.w	#0x8000,d6			| d6.w: "shadow_on" bit set
 	and.b	(shadowMask,a6),d6		| 0xFF: no-op; 0x00: clears color, suppresses writes
-len_loop:
+.Llen_loop:
 	// do {
 	// 	batch = height < len ? height : len;
 	move.w	(len,a6),d0
@@ -146,8 +146,8 @@ len_loop:
 	clr.l	d0
 	move.b	(scaleIndexY,a6),d0
 	tst.w	d6
-	bmi.b	batch_loop_shadow
-batch_loop:
+	bmi.b	.Lbatch_loop_shadow
+.Lbatch_loop:
 	// 		do {
 	// 			if (compData.scaleTable[scaleIndexY++ & compData.scaleIndexMask] < _scaleY)
 	cmp.b	(a3,d0.l),d5
@@ -168,10 +168,10 @@ batch_loop:
 	// 			}
 2:	addq.b	#1,d0				| scaleIndexY++ (byte wrap)
 	// 		} while (--batch);
-	dbra	d1,batch_loop
+	dbra	d1,.Lbatch_loop
 	bra.b	3f
 
-batch_loop_shadow:
+.Lbatch_loop_shadow:
 	// 		do {
 	// 			if (compData.scaleTable[scaleIndexY++ & compData.scaleIndexMask] < _scaleY) {
 	cmp.b	(a3,d0.l),d5
@@ -193,7 +193,7 @@ batch_loop_shadow:
 	// 			}
 2:	addq.b	#1,d0				| scaleIndexY++ (byte wrap)
 	// 		} while (--batch);
-	dbra	d1,batch_loop_shadow
+	dbra	d1,.Lbatch_loop_shadow
 
 3:	move.b	d0,(scaleIndexY,a6)
 	// 	if (height == 0) {
@@ -249,6 +249,6 @@ batch_loop_shadow:
 6:	// StartPos:
 	// } while (len > 0);
 	tst.w	(len,a6)
-	bne.w	len_loop
+	bne.w	.Llen_loop
 	// } while (true);
-	bra.w	main_loop
+	bra.w	.Lmain_loop
diff --git a/engines/scumm/m68k/bylerledecode_scaled_mode3.S b/engines/scumm/m68k/bylerledecode_scaled_mode3.S
index e30c99011ac..ad51bff2579 100644
--- a/engines/scumm/m68k/bylerledecode_scaled_mode3.S
+++ b/engines/scumm/m68k/bylerledecode_scaled_mode3.S
@@ -105,8 +105,8 @@ SYM(ByleRLEDecode_Scaled_Mode3):
 	// 2      1    jump into len_loop (1x)
 	// 3      2    jump into len_loop (2x)
 	subq.w	#1,(len,a6)
-	bgt.b	pcolor_setup
-main_loop:
+	bgt.b	.Lpcolor_setup
+.Lmain_loop:
 	// do {
 	// len = *src++;
 	// color = len >> compData.shr;
@@ -123,16 +123,16 @@ main_loop:
 	move.b	(a0)+,d7
 1:	move.w	d7,(len,a6)
 
-pcolor_setup:
+.Lpcolor_setup:
 	tst.b	d6
-	beq.b	len_loop
+	beq.b	.Llen_loop
 	move.w	([arg_palette,a6],d6.l*2),d7	| d7.w: pcolor = _palette[color] (used as long, too)
 	cmp.w	#8,d7
-	bhs.b	len_loop
+	bhs.b	.Llen_loop
 	lsl.w	#8,d7				| d7.w: if pcolor < 8: pcolor << 8
 	or.w	#0x8000,d6			| d6.w: "shadow_on" bit set
 	and.b	(shadowMask,a6),d6		| 0xFF: no-op; 0x00: clears color, suppresses writes
-len_loop:
+.Llen_loop:
 	// do {
 	// 	batch = height < len ? height : len;
 	move.w	(len,a6),d0
@@ -147,8 +147,8 @@ len_loop:
 
 	subq.w	#1,d1				| dbra
 	tst.w	d6
-	bmi.b	batch_loop_shadow
-batch_loop:
+	bmi.b	.Lbatch_loop_shadow
+.Lbatch_loop:
 	// 		do {
 	// 			if (compData.scaleTable[scaleIndexY++] < _scaleY)
 	cmp.b	(a3)+,d5
@@ -168,10 +168,10 @@ batch_loop:
 	add.l	d2,a2
 	// 			}
 2:	// 		} while (--batch);
-	dbra	d1,batch_loop
+	dbra	d1,.Lbatch_loop
 	bra.b	3f
 
-batch_loop_shadow:
+.Lbatch_loop_shadow:
 	// 		do {
 	// 			if (compData.scaleTable[scaleIndexY++] < _scaleY) {
 	cmp.b	(a3)+,d5
@@ -195,7 +195,7 @@ batch_loop_shadow:
 	add.l	d2,a2
 	// 			}
 2:	// 		} while (--batch);
-	dbra	d1,batch_loop_shadow
+	dbra	d1,.Lbatch_loop_shadow
 
 3:	// 	if (height == 0) {
 	tst.w	(height,a6)
@@ -248,6 +248,6 @@ batch_loop_shadow:
 6:	// StartPos:
 	// } while (len > 0);
 	tst.w	(len,a6)
-	bne.w	len_loop
+	bne.w	.Llen_loop
 	// } while (true);
-	bra.w	main_loop
+	bra.w	.Lmain_loop


Commit: de32e66a2a1a6d6453387c8eb4d6b2b719947f6c
    https://github.com/scummvm/scummvm/commit/de32e66a2a1a6d6453387c8eb4d6b2b719947f6c
Author: Miro Kropacek (miro.kropacek at gmail.com)
Date: 2026-06-14T21:59:20+02:00

Commit Message:
SCUMM: byleRLEDecodeFast: Optimise lastColumnX code

Instead of checking for the boolean flag, pretend that the colour has
been set to transparent => skip.

Changed paths:
    engines/scumm/base-costume-optimised.cpp


diff --git a/engines/scumm/base-costume-optimised.cpp b/engines/scumm/base-costume-optimised.cpp
index 05dad74542a..d80593aec1f 100644
--- a/engines/scumm/base-costume-optimised.cpp
+++ b/engines/scumm/base-costume-optimised.cpp
@@ -847,7 +847,7 @@ void ByleRLEDecode_Scaled_Mode1(
 
 	// reset every column
 	byte *dst = compData.destPtr;
-	int lastColumnX = -1;
+	bool isNewColumn = true;
 	uint16 height = _height;
 	int scaleIndexY = compData.scaleYIndex;
 	byte maskbit = revBitMask(compData.x & 7);
@@ -866,6 +866,9 @@ void ByleRLEDecode_Scaled_Mode1(
 		if (!len)
 			len = *src++;
 
+		if (color && !isNewColumn && _palette[color] == 13)
+			color = 0;
+
 		do {
 			batch = height < len ? (byte)height : len;
 			len -= batch;
@@ -881,8 +884,7 @@ void ByleRLEDecode_Scaled_Mode1(
 
 							pcolor = _palette[color];
 							if (pcolor == 13) {
-								if (lastColumnX != compData.x)
-									*dst = _shadowTable[*dst];
+								*dst = _shadowTable[*dst];
 							} else {
 								*dst = pcolor;
 							}
@@ -899,9 +901,10 @@ void ByleRLEDecode_Scaled_Mode1(
 				height = _height;
 
 				scaleIndexY = compData.scaleYIndex;
-				lastColumnX = compData.x;
+				isNewColumn = false;
 
 				if (compData.scaleTable[compData.scaleXIndex] < _scaleX) {
+					isNewColumn = true;
 					compData.x += compData.scaleXStep;
 					maskbit = revBitMask(compData.x & 7);
 					compData.destPtr += compData.scaleXStep;
@@ -940,7 +943,7 @@ void ByleRLEDecode_Scaled_Mode1_SMask(
 
 	// reset every column
 	byte *dst = compData.destPtr;
-	int lastColumnX = -1;
+	bool isNewColumn = true;
 	uint16 height = _height;
 	int scaleIndexY = compData.scaleYIndex;
 	byte maskbit = revBitMask(compData.x & 7);
@@ -959,6 +962,9 @@ void ByleRLEDecode_Scaled_Mode1_SMask(
 		if (!len)
 			len = *src++;
 
+		if (color && !isNewColumn && _palette[color] == 13)
+			color = 0;
+
 		do {
 			batch = height < len ? (byte)height : len;
 			len -= batch;
@@ -974,8 +980,7 @@ void ByleRLEDecode_Scaled_Mode1_SMask(
 
 							pcolor = _palette[color];
 							if (pcolor == 13) {
-								if (lastColumnX != compData.x)
-									*dst = _shadowTable[*dst];
+								*dst = _shadowTable[*dst];
 							} else {
 								*dst = pcolor;
 							}
@@ -992,9 +997,10 @@ void ByleRLEDecode_Scaled_Mode1_SMask(
 				height = _height;
 
 				scaleIndexY = compData.scaleYIndex;
-				lastColumnX = compData.x;
+				isNewColumn = false;
 
 				if (compData.scaleTable[compData.scaleXIndex] < _scaleX) {
+					isNewColumn = true;
 					compData.x += compData.scaleXStep;
 					maskbit = revBitMask(compData.x & 7);
 					compData.destPtr += compData.scaleXStep;
@@ -1033,7 +1039,7 @@ void ByleRLEDecode_Scaled_Mode3(
 
 	// reset every column
 	byte *dst = compData.destPtr;
-	int lastColumnX = -1;
+	bool isNewColumn = true;
 	uint16 height = _height;
 	int scaleIndexY = compData.scaleYIndex;
 	byte maskbit = revBitMask(compData.x & 7);
@@ -1052,6 +1058,9 @@ void ByleRLEDecode_Scaled_Mode3(
 		if (!len)
 			len = *src++;
 
+		if (color && !isNewColumn && _palette[color] < 8)
+			color = 0;
+
 		do {
 			batch = height < len ? (byte)height : len;
 			len -= batch;
@@ -1067,10 +1076,8 @@ void ByleRLEDecode_Scaled_Mode3(
 
 							pcolor = _palette[color];
 							if (pcolor < 8) {
-								if (lastColumnX != compData.x) {
-									pcolor = (pcolor << 8) + *dst;
-									*dst = _shadowTable[pcolor];
-								}
+								pcolor = (pcolor << 8) + *dst;
+								*dst = _shadowTable[pcolor];
 							} else {
 								*dst = pcolor;
 							}
@@ -1087,9 +1094,10 @@ void ByleRLEDecode_Scaled_Mode3(
 				height = _height;
 
 				scaleIndexY = compData.scaleYIndex;
-				lastColumnX = compData.x;
+				isNewColumn = false;
 
 				if (compData.scaleTable[compData.scaleXIndex] < _scaleX) {
+					isNewColumn = true;
 					compData.x += compData.scaleXStep;
 					maskbit = revBitMask(compData.x & 7);
 					compData.destPtr += compData.scaleXStep;
@@ -1127,7 +1135,7 @@ void ByleRLEDecode_Scaled_Mode3_HE90_SMask(
 
 	// reset every column
 	byte *dst = compData.destPtr;
-	int lastColumnX = -1;
+	bool isNewColumn = true;
 	uint16 height = _height;
 	int scaleIndexY = compData.scaleYIndex;
 	byte maskbit = revBitMask(compData.x & 7);
@@ -1146,6 +1154,9 @@ void ByleRLEDecode_Scaled_Mode3_HE90_SMask(
 		if (!len)
 			len = *src++;
 
+		if (color && !isNewColumn)
+			color = 0;
+
 		do {
 			batch = height < len ? (byte)height : len;
 			len -= batch;
@@ -1160,10 +1171,8 @@ void ByleRLEDecode_Scaled_Mode3_HE90_SMask(
 							uint16 pcolor;
 
 							pcolor = _palette[color];
-							if (lastColumnX != compData.x) {
-								pcolor = (pcolor << 8) + *dst;
-								*dst = _shadowTable[pcolor];
-							}
+							pcolor = (pcolor << 8) + *dst;
+							*dst = _shadowTable[pcolor];
 						}
 					}
 					dst += pitch;
@@ -1177,9 +1186,10 @@ void ByleRLEDecode_Scaled_Mode3_HE90_SMask(
 				height = _height;
 
 				scaleIndexY = compData.scaleYIndex;
-				lastColumnX = compData.x;
+				isNewColumn = false;
 
 				if (compData.scaleTable[compData.scaleXIndex] < _scaleX) {
+					isNewColumn = true;
 					compData.x += compData.scaleXStep;
 					maskbit = revBitMask(compData.x & 7);
 					compData.destPtr += compData.scaleXStep;
@@ -1216,7 +1226,7 @@ void ByleRLEDecode_Scaled_Classic_SMask(
 
 	// reset every column
 	byte *dst = compData.destPtr;
-	int lastColumnX = -1;
+	bool isNewColumn = true;
 	uint16 height = _height;
 	int scaleIndexY = compData.scaleYIndex;
 	byte maskbit = revBitMask(compData.x & 7);
@@ -1235,6 +1245,9 @@ void ByleRLEDecode_Scaled_Classic_SMask(
 		if (!len)
 			len = *src++;
 
+		if (color && !isNewColumn)
+			color = 0;
+
 		do {
 			batch = height < len ? (byte)height : len;
 			len -= batch;
@@ -1245,10 +1258,8 @@ void ByleRLEDecode_Scaled_Classic_SMask(
 			do {
 				if (compData.scaleTable[scaleIndexY++ & compData.scaleIndexMask] < _scaleY) {
 					if (color) {
-						if (!(*mask & maskbit)) {
-							if (lastColumnX != compData.x)
-								*dst = _shadowTable[*dst];
-						}
+						if (!(*mask & maskbit))
+							*dst = _shadowTable[*dst];
 					}
 					dst += pitch;
 					mask += _numStrips;
@@ -1261,9 +1272,10 @@ void ByleRLEDecode_Scaled_Classic_SMask(
 				height = _height;
 
 				scaleIndexY = compData.scaleYIndex;
-				lastColumnX = compData.x;
+				isNewColumn = false;
 
 				if (compData.scaleTable[compData.scaleXIndex] < _scaleX) {
+					isNewColumn = true;
 					compData.x += compData.scaleXStep;
 					maskbit = revBitMask(compData.x & 7);
 					compData.destPtr += compData.scaleXStep;
@@ -1380,7 +1392,7 @@ void BaseCostumeRenderer::byleRLEDecodeFast(ByleRLEData &compData, const byte *x
 
 	// reset every column
 	byte *dst = compData.destPtr;
-	int lastColumnX = -1;
+	bool isNewColumn = true;
 	int y = compData.y;
 	uint16 height = _height;
 	int scaleIndexY = compData.scaleYIndex;
@@ -1400,6 +1412,25 @@ void BaseCostumeRenderer::byleRLEDecodeFast(ByleRLEData &compData, const byte *x
 		if (!len)
 			len = *src++;
 
+		if (color && !isNewColumn) {
+			switch(shadowMode) {
+			case ShadowMode::Classic:
+			case ShadowMode::Mode3_HE:
+				color = 0;
+				break;
+			case ShadowMode::Mode1:
+				if (_palette[color] == 13)
+					color = 0;
+				break;
+			case ShadowMode::Mode3:
+				if (_palette[color] < 8)
+					color = 0;
+				break;
+			default:
+				break;
+			}
+		}
+
 		do {
 			batch = height < len ? (byte)height : len;
 			len -= batch;
@@ -1411,7 +1442,7 @@ void BaseCostumeRenderer::byleRLEDecodeFast(ByleRLEData &compData, const byte *x
 				if (_scaleY == 255 || compData.scaleTable[scaleIndexY++ & compData.scaleIndexMask] < _scaleY) {
 					if (color) {
 						const bool masked = (y < compData.boundsRect.top || y >= compData.boundsRect.bottom)
-							|| (*mask & maskbit);
+						|| (*mask & maskbit);
 
 						if (!masked) {
 							uint16 pcolor;
@@ -1422,15 +1453,13 @@ void BaseCostumeRenderer::byleRLEDecodeFast(ByleRLEData &compData, const byte *x
 								break;
 
 							case ShadowMode::Classic:
-								if (lastColumnX != compData.x)
-									*dst = _shadowTable[*dst];
+								*dst = _shadowTable[*dst];
 								break;
 
 							case ShadowMode::Mode1:
 								pcolor = _palette[color];
 								if (pcolor == 13) {
-									if (lastColumnX != compData.x)
-										*dst = _shadowTable[*dst];
+									*dst = _shadowTable[*dst];
 								} else {
 									*dst = pcolor;
 								}
@@ -1439,10 +1468,8 @@ void BaseCostumeRenderer::byleRLEDecodeFast(ByleRLEData &compData, const byte *x
 							case ShadowMode::Mode3:
 								pcolor = _palette[color];
 								if (pcolor < 8) {
-									if (lastColumnX != compData.x) {
-										pcolor = (pcolor << 8) + *dst;
-										*dst = _shadowTable[pcolor];
-									}
+									pcolor = (pcolor << 8) + *dst;
+									*dst = _shadowTable[pcolor];
 								} else {
 									*dst = pcolor;
 								}
@@ -1450,10 +1477,8 @@ void BaseCostumeRenderer::byleRLEDecodeFast(ByleRLEData &compData, const byte *x
 
 							case ShadowMode::Mode3_HE:
 								pcolor = _palette[color];
-								if (lastColumnX != compData.x) {
-									pcolor = (pcolor << 8) + *dst;
-									*dst = xmap[pcolor];
-								}
+								pcolor = (pcolor << 8) + *dst;
+								*dst = xmap[pcolor];
 								break;
 							}
 						}
@@ -1471,9 +1496,10 @@ void BaseCostumeRenderer::byleRLEDecodeFast(ByleRLEData &compData, const byte *x
 				y = compData.y;
 
 				scaleIndexY = compData.scaleYIndex;
-				lastColumnX = compData.x;
+				isNewColumn = false;
 
 				if (_scaleX == 255 || compData.scaleTable[compData.scaleXIndex] < _scaleX) {
+					isNewColumn = true;
 					compData.x += compData.scaleXStep;
 					maskbit = revBitMask(compData.x & 7);
 					compData.destPtr += compData.scaleXStep;


Commit: fded2d42039355045aa7124b2cedb37dd014368a
    https://github.com/scummvm/scummvm/commit/fded2d42039355045aa7124b2cedb37dd014368a
Author: Miro Kropacek (miro.kropacek at gmail.com)
Date: 2026-06-14T21:59:20+02:00

Commit Message:
SCUMM: byleRLEDecodeFast: Introduce specialised Mode0&1 Y clipping functions

Some games contain scenes with static objects whose coordinates are
outside top or bottom border. From my tests, so far only Mode0 and Mode1
seem to be affected.

Changed paths:
    engines/scumm/base-costume-optimised.cpp


diff --git a/engines/scumm/base-costume-optimised.cpp b/engines/scumm/base-costume-optimised.cpp
index d80593aec1f..fbeba7182cd 100644
--- a/engines/scumm/base-costume-optimised.cpp
+++ b/engines/scumm/base-costume-optimised.cpp
@@ -1291,6 +1291,249 @@ void ByleRLEDecode_Scaled_Classic_SMask(
 	} while (true);
 }
 
+// Each column spans _height pixels starting at compData.y:
+//
+//   compData.y  ??????????????????????????????????????????????????????
+//               ???   blankTop     ???  skipped
+//  boundsRect   ??????????????????????????????????????????????????????
+//  .top         ???                ???
+//               ???  activeHeight  ???  mask check + pixel write
+//               ???                ???
+//  boundsRect   ??????????????????????????????????????????????????????
+//  .bottom      ???  blankBottom   ???  skipped
+//               ??????????????????????????????????????????????????????  (compData.y + _height)
+//
+// Optimisation, instead of this:
+//
+// uint16 blankLinesOnBotRem = blankLinesOnBot;
+// ...
+// below = batch < blankLinesOnBotRem ? batch : blankLinesOnBotRem;
+// batch -= below;
+// assert(batch == 0);
+// blankLinesOnBotRem -= below;
+// dst  += below * pitch;
+// mask += below * _numStrips;
+// ...
+// blankLinesOnBotRem = blankLinesOnBot;
+//
+// we use just:
+//
+// dst  += batch * pitch;
+// mask += batch * _numStrips;
+void ByleRLEDecode_Mode0_YClip(
+	BaseCostumeRenderer::ByleRLEData *pcompData,
+	const byte _scaleX, /* unused */
+	const byte _scaleY, /* unused */
+	const int _height,
+	const int pitch,
+	const int _numStrips,
+	const byte *_srcPtr,
+	const byte *_shadowTable, /* unused */
+	const uint16 *_palette) {
+
+	BaseCostumeRenderer::ByleRLEData &compData = *pcompData;
+
+	const byte *src = _srcPtr;
+
+	uint16 len = compData.repLen;
+	uint16 color = compData.repColor;
+
+	const int activeTop = compData.y;
+	const int clipTop   = compData.boundsRect.top;
+	const int clipBot   = compData.boundsRect.bottom;
+
+	const uint16 blankLinesOnTop = (activeTop < clipTop) ? (clipTop - activeTop) : 0;
+	const uint16 blankLinesOnBot = (activeTop + _height > clipBot) ? (activeTop + _height - clipBot) : 0;
+	const uint16 activeLines     = _height - blankLinesOnTop - blankLinesOnBot;
+
+	assert(blankLinesOnTop + activeLines + blankLinesOnBot == _height);
+
+	// reset every column
+	byte *dst = compData.destPtr;
+	uint16 blankLinesOnTopRem = blankLinesOnTop;
+	uint16 activeLinesRem = activeLines;
+	uint16 height = _height;
+	byte maskbit = revBitMask(compData.x & 7);
+	const byte *mask = compData.maskPtr + compData.x / 8;
+
+	uint16 above, active, batch;
+	if (len) {
+		--len;
+		goto StartPos;
+	}
+
+	do {
+		len = *src++;
+		color = len >> compData.shr;
+		len &= compData.mask;
+		if (!len)
+			len = *src++;
+
+		do {
+			batch = height < len ? height : len;
+			len -= batch;
+			height -= batch;
+
+			assert(compData.x >= compData.boundsRect.left && compData.x < compData.boundsRect.right);
+
+			// blank top: skip
+			above = batch < blankLinesOnTopRem ? batch : blankLinesOnTopRem;
+			batch -= above;
+			blankLinesOnTopRem -= above;
+			dst  += above * pitch;
+			mask += above * _numStrips;
+
+			// active lines: draw or skip transparent
+			active = batch < activeLinesRem ? batch : activeLinesRem;
+			batch -= active;
+			activeLinesRem -= active;
+			if (color) {
+				while (active--) {
+					if (!(*mask & maskbit))
+						*dst = _palette[color];
+					dst  += pitch;
+					mask += _numStrips;
+				}
+			} else {
+				dst  += active * pitch;
+				mask += active * _numStrips;
+			}
+
+			// blank bottom: skip
+			dst  += batch * pitch;
+			mask += batch * _numStrips;
+
+			if (height == 0) {
+				if (--compData.skipWidth == 0)
+					return;
+				height = _height;
+
+				blankLinesOnTopRem = blankLinesOnTop;
+				activeLinesRem     = activeLines;
+
+				compData.x += compData.scaleXStep;
+				maskbit = revBitMask(compData.x & 7);
+				compData.destPtr += compData.scaleXStep;
+
+				dst = compData.destPtr;
+				mask = compData.maskPtr + compData.x / 8;
+			}
+		StartPos:;
+		} while (len > 0);
+	} while (true);
+}
+
+void ByleRLEDecode_Mode1_YClip(
+	BaseCostumeRenderer::ByleRLEData *pcompData,
+	const byte _scaleX, /* unused */
+	const byte _scaleY, /* unused */
+	const int _height,
+	const int pitch,
+	const int _numStrips,
+	const byte *_srcPtr,
+	const byte *_shadowTable,
+	const uint16 *_palette) {
+
+	BaseCostumeRenderer::ByleRLEData &compData = *pcompData;
+	//warning("%s: unexpected call, save your game and report", __FUNCTION__);
+
+	const byte *src = _srcPtr;
+
+	uint16 len = compData.repLen;
+	uint16 color = compData.repColor;
+
+	const int activeTop = compData.y;
+	const int clipTop   = compData.boundsRect.top;
+	const int clipBot   = compData.boundsRect.bottom;
+
+	const uint16 blankLinesOnTop = (activeTop < clipTop) ? (clipTop - activeTop) : 0;
+	const uint16 blankLinesOnBot = (activeTop + _height > clipBot) ? (activeTop + _height - clipBot) : 0;
+	const uint16 activeLines     = _height - blankLinesOnTop - blankLinesOnBot;
+
+	assert(blankLinesOnTop + activeLines + blankLinesOnBot == _height);
+
+	// reset every column
+	byte *dst = compData.destPtr;
+	uint16 blankLinesOnTopRem = blankLinesOnTop;
+	uint16 activeLinesRem = activeLines;
+	uint16 height = _height;
+	byte maskbit = revBitMask(compData.x & 7);
+	const byte *mask = compData.maskPtr + compData.x / 8;
+
+	uint16 above, active, batch;
+	if (len) {
+		--len;
+		goto StartPos;
+	}
+
+	do {
+		len = *src++;
+		color = len >> compData.shr;
+		len &= compData.mask;
+		if (!len)
+			len = *src++;
+
+		do {
+			batch = height < len ? height : len;
+			len -= batch;
+			height -= batch;
+
+			assert(compData.x >= compData.boundsRect.left && compData.x < compData.boundsRect.right);
+
+			// blank top: skip
+			above = batch < blankLinesOnTopRem ? batch : blankLinesOnTopRem;
+			batch -= above;
+			blankLinesOnTopRem -= above;
+			dst  += above * pitch;
+			mask += above * _numStrips;
+
+			// active lines: draw or skip transparent
+			active = batch < activeLinesRem ? batch : activeLinesRem;
+			batch -= active;
+			activeLinesRem -= active;
+
+			if (color) {
+				while (active--) {
+					if (!(*mask & maskbit)) {
+						uint16 pcolor = _palette[color];
+						if (pcolor == 13) {
+							*dst = _shadowTable[*dst];
+						} else {
+							*dst = pcolor;
+						}
+					}
+					dst  += pitch;
+					mask += _numStrips;
+				}
+			} else {
+				dst  += active * pitch;
+				mask += active * _numStrips;
+			}
+
+			// blank bottom: skip
+			dst  += batch * pitch;
+			mask += batch * _numStrips;
+
+			if (height == 0) {
+				if (--compData.skipWidth == 0)
+					return;
+				height = _height;
+
+				blankLinesOnTopRem = blankLinesOnTop;
+				activeLinesRem     = activeLines;
+
+				compData.x += compData.scaleXStep;
+				maskbit = revBitMask(compData.x & 7);
+				compData.destPtr += compData.scaleXStep;
+
+				dst = compData.destPtr;
+				mask = compData.maskPtr + compData.x / 8;
+			}
+		StartPos:;
+		} while (len > 0);
+	} while (true);
+}
+
 enum class ShadowMode : int {
 	Mode0,
 	Mode1,
@@ -1352,8 +1595,8 @@ void BaseCostumeRenderer::byleRLEDecodeFast(ByleRLEData &compData, const byte *x
 		}
 	}
 
+	const int scaled = (_scaleX != 255 || _scaleY != 255);
 	if (compData.y >= compData.boundsRect.top && compData.y + compData.scaledHeight <= compData.boundsRect.bottom) {
-		const int scaled = (_scaleX != 255 || _scaleY != 255);
 		if (!scaled) {
 			byleRLEDecodeNoScaleTable[static_cast<int>(shadowMode)](
 				&compData,
@@ -1380,9 +1623,36 @@ void BaseCostumeRenderer::byleRLEDecodeFast(ByleRLEData &compData, const byte *x
 				_palette);
 		}
 		return;
+	} else if (!scaled) {
+		if (shadowMode == ShadowMode::Mode0) {
+			ByleRLEDecode_Mode0_YClip(
+				&compData,
+				_scaleX,
+				_scaleY,
+				_height,
+				_out.pitch,
+				_numStrips,
+				_srcPtr,
+				shadowTable,
+				_palette);
+			return;
+		} else if (shadowMode == ShadowMode::Mode1) {
+			ByleRLEDecode_Mode1_YClip(
+				&compData,
+				_scaleX,
+				_scaleY,
+				_height,
+				_out.pitch,
+				_numStrips,
+				_srcPtr,
+				shadowTable,
+				_palette);
+			return;
+		}
 	}
 
-	warning("%s: unexpected call, save your game and report: %d (%d, %d, %d, %d)", __FUNCTION__, (int)shadowMode,
+	if (!scaled)
+		warning("%s: unexpected call, save your game and report: %d (%d, %d, %d, %d)", __FUNCTION__, (int)shadowMode,
 			compData.y, compData.boundsRect.top, compData.y + compData.scaledHeight, compData.boundsRect.bottom);
 
 	const byte *src = _srcPtr;


Commit: 7a854a8bd799d024977ed348c5bb0bccb16d7d77
    https://github.com/scummvm/scummvm/commit/7a854a8bd799d024977ed348c5bb0bccb16d7d77
Author: Miro Kropacek (miro.kropacek at gmail.com)
Date: 2026-06-14T21:59:20+02:00

Commit Message:
SCUMM: Optimise BaseCostumeRenderer::skipCelLines

There's really no reason to loop over to get the correct value.

Changed paths:
    engines/scumm/base-costume.cpp


diff --git a/engines/scumm/base-costume.cpp b/engines/scumm/base-costume.cpp
index b9e7cfbd2a5..42d5b59eaee 100644
--- a/engines/scumm/base-costume.cpp
+++ b/engines/scumm/base-costume.cpp
@@ -446,10 +446,10 @@ void BaseCostumeRenderer::skipCelLines(ByleRLEData &compData, int num) {
 		if (!compData.repLen)
 			compData.repLen = *_srcPtr++;
 
-		do {
-			if (!--num)
-				return;
-		} while (--compData.repLen);
+		if ((num -= compData.repLen) <= 0) {
+			compData.repLen = 1 - num;
+			return;
+		}
 	} while (true);
 }
 


Commit: da99ff1efdf80d4a564ea3efca10855f325dccfd
    https://github.com/scummvm/scummvm/commit/da99ff1efdf80d4a564ea3efca10855f325dccfd
Author: Miro Kropacek (miro.kropacek at gmail.com)
Date: 2026-06-14T21:59:20+02:00

Commit Message:
SCUMM: Rewrite asmDrawStripToScreen into m68k assembly

Also, remove the odd code guarded by SCUMM_NEED_ALIGNMENT, it provides
neither faster nor safer code.

Changed paths:
  A engines/scumm/gfxM68K.S
    configure
    engines/scumm/gfx.cpp
    engines/scumm/module.mk


diff --git a/configure b/configure
index 0739c9d6ae2..498adf8ad26 100755
--- a/configure
+++ b/configure
@@ -4257,6 +4257,7 @@ case $_backend in
 	atari)
 		append_var DEFINES "-DATARI"
 		define_in_config_if_yes yes "USE_M68K_COSTUME_ASM"
+		define_in_config_if_yes yes "USE_M68K_GFX_ASM"
 		append_var DEFINES "-DDISABLE_NES_APU"
 		append_var LIBS "-lgem"
 		_ogg=no
diff --git a/engines/scumm/gfx.cpp b/engines/scumm/gfx.cpp
index aa4eace00cc..bf85f13c3ab 100644
--- a/engines/scumm/gfx.cpp
+++ b/engines/scumm/gfx.cpp
@@ -33,7 +33,7 @@
 #include "scumm/he/wiz_he.h"
 #include "scumm/util.h"
 
-#ifdef USE_ARM_GFX_ASM
+#if defined(USE_ARM_GFX_ASM)
 
 #ifndef IPHONE
 #define asmDrawStripToScreen _asmDrawStripToScreen
@@ -43,6 +43,12 @@
 extern "C" void asmDrawStripToScreen(int height, int width, void const* text, void const* src, byte* dst,
 	int vsPitch, int vmScreenWidth, int textSurfacePitch);
 extern "C" void asmCopy8Col(byte* dst, int dstPitch, const byte* src, int height, uint8 bitDepth);
+
+#elif defined(USE_M68K_GFX_ASM)
+
+extern "C" void asmDrawStripToScreen(int height, int width, const uint32 *src32, uint32 *dst32, int vsPitch,
+		const uint32 *text32, const int textPitch);
+
 #endif /* USE_ARM_GFX_ASM */
 
 namespace Scumm {
@@ -732,6 +738,9 @@ void ScummEngine::drawStripToScreen(VirtScreen *vs, int x, int width, int top, i
 
 			const uint32 *text32 = (const uint32 *)text;
 			const int textPitch = (_textSurface.pitch - width * m) >> 2;
+#ifdef USE_M68K_GFX_ASM
+			asmDrawStripToScreen(height * m, width * m, src32, dst32, vsPitch, text32, textPitch);
+#else
 			for (int h = height * m; h > 0; --h) {
 				for (int w = width * m; w > 0; w -= 4) {
 					uint32 temp = *text32++;
@@ -754,7 +763,8 @@ void ScummEngine::drawStripToScreen(VirtScreen *vs, int x, int width, int top, i
 				src32 += vsPitch;
 				text32 += textPitch;
 			}
-#endif
+#endif // USE_M68K_GFX_ASM
+#endif // USE_ARM_GFX_ASM
 		}
 		src = _compositeBuf;
 		pitch = width * vs->format.bytesPerPixel;
@@ -1390,18 +1400,8 @@ static void fill(byte *dst, int dstPitch, uint16 color, int w, int h, uint8 bitD
 #else
 
 static void copy8Col(byte *dst, int dstPitch, const byte *src, int height, uint8 bitDepth) {
-
 	do {
-#if defined(SCUMM_NEED_ALIGNMENT)
 		memcpy(dst, src, 8 * bitDepth);
-#else
-		((uint32 *)dst)[0] = ((const uint32 *)src)[0];
-		((uint32 *)dst)[1] = ((const uint32 *)src)[1];
-		if (bitDepth == 2) {
-			((uint32 *)dst)[2] = ((const uint32 *)src)[2];
-			((uint32 *)dst)[3] = ((const uint32 *)src)[3];
-		}
-#endif
 		dst += dstPitch;
 		src += dstPitch;
 	} while (--height);
@@ -1411,23 +1411,10 @@ static void copy8Col(byte *dst, int dstPitch, const byte *src, int height, uint8
 
 static void clear8Col(byte *dst, int dstPitch, int height, uint8 bitDepth) {
 	do {
-#if defined(SCUMM_NEED_ALIGNMENT)
 		if (g_scumm->_game.platform == Common::kPlatformNES)
 			memset(dst, 0x1d, 8 * bitDepth);
 		else
 			memset(dst, 0, 8 * bitDepth);
-#else
-		if (g_scumm->_game.platform == Common::kPlatformNES) {
-			memset(dst, 0x1d, 8 * bitDepth);
-		} else {
-			((uint32*)dst)[0] = 0;
-			((uint32*)dst)[1] = 0;
-			if (bitDepth == 2) {
-				((uint32*)dst)[2] = 0;
-				((uint32*)dst)[3] = 0;
-			}
-		}
-#endif
 		dst += dstPitch;
 	} while (--height);
 }
diff --git a/engines/scumm/gfxM68K.S b/engines/scumm/gfxM68K.S
new file mode 100644
index 00000000000..43f1e68318a
--- /dev/null
+++ b/engines/scumm/gfxM68K.S
@@ -0,0 +1,105 @@
+/* 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/>.
+ *
+ */
+
+| kate: tab-width 8; replace-tabs off; hl Motorola 68k (VASM/Devpac);
+
+#include "../../backends/platform/atari/symbols.h"
+
+	.globl	SYM(asmDrawStripToScreen)
+
+
+	.text
+
+| void asmDrawStripToScreen(
+|     const int height,      4+9*4(sp)
+|     const int width,       4+9*4+4(sp)
+|     const uint32 *src32,   4+9*4+8(sp)
+|     uint32 *dst32,         4+9*4+12(sp)
+|     const int vsPitch,     4+9*4+16(sp)
+|     const uint32 *text32,  4+9*4+20(sp)
+|     const int textPitch    4+9*4+24(sp)
+| );
+SYM(asmDrawStripToScreen):
+// TODO: __FASTCALL__
+	movem.l	d2-d7/a2-a4,-(sp)		| 9 ?? 4 = 36 bytes
+	move.l	(4+9*4+8,sp),a0			| a0: src32
+	move.l	(4+9*4+12,sp),a1		| a1: dst32
+	move.l	(4+9*4+20,sp),a2		| a2: text32
+
+	move.l	(4+9*4+16,sp),d0		| vsPitch
+	lsl.l	#2,d0				|
+	move.l	d0,a3				| a3: vsPitch in bytes
+
+	move.l	(4+9*4+24,sp),d0		| textPitch
+	lsl.l	#2,d0				|
+	move.l	d0,a4				| a4: textPitch in bytes
+
+	move.l	(4+9*4+4,sp),d5			| width
+	lsr.l	#2,d5				|
+	beq.b	3f
+	subq.l	#1,d5				| d5.w: width/4 - 1 for dbra
+
+	// for (int h = height * m; h > 0; --h) {
+	move.l	(4+9*4,sp),d7
+	beq.b	3f
+
+	move.w	d5,-(sp)
+	move.l	#0x7F7F7F7F,d3
+	move.l	#0x80808080,d4
+	move.l	#0xFDFDFDFD,d5
+	subq.l	#1,d7				| d7.w: height - 1 for dbra
+
+1:	//	for (int w = width * m; w > 0; w -= 4) {
+	move.w	(sp),d6
+2:	//		uint32 temp = *text32++;
+	move.l	(a2)+,d0
+	//		uint32 mask = temp ^ CHARSET_MASK_TRANSPARENCY_32;
+	move.l	d0,d1
+	eor.l	d5,d1
+	//		mask = (((mask & 0x7f7f7f7f) + 0x7f7f7f7f) | mask) & 0x80808080;
+	move.l	d1,d2
+	and.l	d3,d2
+	add.l	d3,d2
+	or.l	d1,d2
+	and.l	d4,d2
+	//		mask = ((mask >> 7) + 0x7f7f7f7f) ^ 0x80808080;
+	lsr.l	#7,d2
+	add.l	d3,d2
+	eor.l	d4,d2
+	//		*dst32++ = ((temp ^ *src32++) & mask) ^ temp;
+	move.l	(a0)+,d1
+	eor.l	d0,d1
+	and.l	d2,d1
+	eor.l	d0,d1
+	move.l	d1,(a1)+
+	//	}
+	dbra	d6,2b
+	//	src32 += vsPitch;
+	add.l	a3,a0
+	//	text32 += textPitch;
+	add.l	a4,a2
+	// }
+	dbra	d7,1b
+
+	addq.l	#2,sp				| pop d5.w
+
+3:	movem.l	(sp)+,d2-d7/a2-a4
+	rts
diff --git a/engines/scumm/module.mk b/engines/scumm/module.mk
index 0f8996fb794..2cee05abc31 100644
--- a/engines/scumm/module.mk
+++ b/engines/scumm/module.mk
@@ -189,6 +189,11 @@ MODULE_OBJS += \
 	gfxARM.o
 endif
 
+ifdef USE_M68K_GFX_ASM
+MODULE_OBJS += \
+	gfxM68K.o
+endif
+
 ifdef ENABLE_HE
 MODULE_OBJS += \
 	he/animation_he.o \



From noreply at scummvm.org  Sun Jun 14 20:50:33 2026
From: noreply at scummvm.org (sev-)
Date: Sun, 14 Jun 2026 20:50:33 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 15b3aa750b297df8be0e290c9c225a9679cfc728
Message-ID: <20260614205033.72C467005CC@ron.scummvm.net>

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

Summary:
15b3aa750b GRAPHICS: Replace use of createPixelFormat for non-ColorMask purposes


Commit: 15b3aa750b297df8be0e290c9c225a9679cfc728
    https://github.com/scummvm/scummvm/commit/15b3aa750b297df8be0e290c9c225a9679cfc728
Author: Cameron Cawley (ccawley2011 at gmail.com)
Date: 2026-06-14T22:50:28+02:00

Commit Message:
GRAPHICS: Replace use of createPixelFormat for non-ColorMask purposes

Changed paths:
    backends/graphics/surfacesdl/surfacesdl-graphics.cpp
    engines/gob/inter_v7.cpp
    engines/mtropolis/assets.cpp
    engines/mtropolis/metaengine.cpp
    engines/mtropolis/subtitles.cpp
    engines/testbed/graphics.cpp
    engines/vcruise/menu.cpp
    graphics/thumbnail.cpp


diff --git a/backends/graphics/surfacesdl/surfacesdl-graphics.cpp b/backends/graphics/surfacesdl/surfacesdl-graphics.cpp
index 184fd87b33a..24cd38fc967 100644
--- a/backends/graphics/surfacesdl/surfacesdl-graphics.cpp
+++ b/backends/graphics/surfacesdl/surfacesdl-graphics.cpp
@@ -2163,7 +2163,7 @@ void SurfaceSdlGraphicsManager::setMouseCursor(const void *buf, uint w, uint h,
 		const uint numPixels = w * h;
 		const uint inBPP = format->bytesPerPixel;
 
-		Graphics::PixelFormat formatWithAlpha = Graphics::createPixelFormat<8888>();
+		Graphics::PixelFormat formatWithAlpha = Graphics::PixelFormat::createFormatRGBA32();
 
 		// Use the existing format if it already has alpha
 		if (format->aBits() > 0)
diff --git a/engines/gob/inter_v7.cpp b/engines/gob/inter_v7.cpp
index c2479edf276..84e9812d4f9 100644
--- a/engines/gob/inter_v7.cpp
+++ b/engines/gob/inter_v7.cpp
@@ -622,7 +622,7 @@ void Inter_v7::o7_initScreen() {
 
 		// Comment the next line and uncomment the following ones to use the original pixel format (RGB555) for debugging purposes
 		_vm->setTrueColor(trueColor, false, nullptr);
-		// Graphics::PixelFormat format = Graphics::createPixelFormat<555>();
+		// Graphics::PixelFormat format(2, 5, 5, 5, 0, 10, 5, 0, 0);
 		// _vm->setTrueColor(trueColor, false, &format);
 	}
 
diff --git a/engines/mtropolis/assets.cpp b/engines/mtropolis/assets.cpp
index 5a945933c46..45f82294aa2 100644
--- a/engines/mtropolis/assets.cpp
+++ b/engines/mtropolis/assets.cpp
@@ -1013,6 +1013,10 @@ const Common::SharedPtr<CachedImage> &ImageAsset::loadAndCacheImage(Runtime *run
 		return _imageCache;
 	}
 
+	ImageAsset::ImageFormat imageFormat = getImageFormat();
+	bool bottomUp = (imageFormat == ImageAsset::kImageFormatWindows);
+	bool isBigEndian = (imageFormat == ImageAsset::kImageFormatMac);
+
 	Graphics::PixelFormat pixelFmt;
 	switch (getColorDepth()) {
 	case kColorDepthMode1Bit:
@@ -1033,12 +1037,18 @@ const Common::SharedPtr<CachedImage> &ImageAsset::loadAndCacheImage(Runtime *run
 		break;
 	case kColorDepthMode16Bit:
 		bytesPerRow = (width * 2 + 3) / 4 * 4;
-		pixelFmt = Graphics::createPixelFormat<1555>();
+		pixelFmt = Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0);
 		break;
 	case kColorDepthMode32Bit:
 		bytesPerRow = width * 4;
-		pixelFmt = Graphics::createPixelFormat<8888>();
-		break;
+		if (imageFormat == ImageAsset::kImageFormatMac) {
+			pixelFmt = Graphics::PixelFormat::createFormatARGB32(false);
+			break;
+		} else if (imageFormat == ImageAsset::kImageFormatWindows) {
+			pixelFmt = Graphics::PixelFormat::createFormatBGRA32(false);
+			break;
+		}
+		// fall through
 	default:
 		warning("Image asset has an unrecognizable pixel format");
 		return _imageCache;
@@ -1047,10 +1057,6 @@ const Common::SharedPtr<CachedImage> &ImageAsset::loadAndCacheImage(Runtime *run
 	Common::Array<uint8> rowBuffer;
 	rowBuffer.resize(bytesPerRow);
 
-	ImageAsset::ImageFormat imageFormat = getImageFormat();
-	bool bottomUp = (imageFormat == ImageAsset::kImageFormatWindows);
-	bool isBigEndian = (imageFormat == ImageAsset::kImageFormatMac);
-
 	Common::SharedPtr<Graphics::ManagedSurface> imageSurface;
 	imageSurface.reset(new Graphics::ManagedSurface());
 	imageSurface->create(width, height, pixelFmt);
@@ -1089,44 +1095,18 @@ const Common::SharedPtr<CachedImage> &ImageAsset::loadAndCacheImage(Runtime *run
 				if (isBigEndian) {
 					for (int x = 0; x < width; x++) {
 						uint16 packedPixel = inRowBytes[x * 2 + 1] + (inRowBytes[x * 2 + 0] << 8);
-						int r = ((packedPixel >> 10) & 0x1f);
-						int g = ((packedPixel >> 5) & 0x1f);
-						int b = (packedPixel & 0x1f);
-
-						uint16 repacked = (1 << pixelFmt.aShift) | (r << pixelFmt.rShift) | (g << pixelFmt.gShift) | (b << pixelFmt.bShift);
-						static_cast<uint16 *>(outBase)[x] = repacked;
+						static_cast<uint16 *>(outBase)[x] = packedPixel;
 					}
 				} else {
 					for (int x = 0; x < width; x++) {
 						uint16 packedPixel = inRowBytes[x * 2 + 0] + (inRowBytes[x * 2 + 1] << 8);
-						int r = ((packedPixel >> 10) & 0x1f);
-						int g = ((packedPixel >> 5) & 0x1f);
-						int b = (packedPixel & 0x1f);
-
-						uint16 repacked = (1 << pixelFmt.aShift) | (r << pixelFmt.rShift) | (g << pixelFmt.gShift) | (b << pixelFmt.bShift);
-						static_cast<uint16 *>(outBase)[x] = repacked;
-					}
-				}
-			} break;
-		case kColorDepthMode32Bit: {
-				if (imageFormat == ImageAsset::kImageFormatMac) {
-					for (int x = 0; x < width; x++) {
-						uint8 r = inRowBytes[x * 4 + 1];
-						uint8 g = inRowBytes[x * 4 + 2];
-						uint8 b = inRowBytes[x * 4 + 3];
-						uint32 repacked = (255 << pixelFmt.aShift) | (r << pixelFmt.rShift) | (g << pixelFmt.gShift) | (b << pixelFmt.bShift);
-						static_cast<uint32 *>(outBase)[x] = repacked;
-					}
-				} else if (imageFormat == ImageAsset::kImageFormatWindows) {
-					for (int x = 0; x < width; x++) {
-						uint8 r = inRowBytes[x * 4 + 2];
-						uint8 g = inRowBytes[x * 4 + 1];
-						uint8 b = inRowBytes[x * 4 + 0];
-						uint32 repacked = (255 << pixelFmt.aShift) | (r << pixelFmt.rShift) | (g << pixelFmt.gShift) | (b << pixelFmt.bShift);
-						static_cast<uint32 *>(outBase)[x] = repacked;
+						static_cast<uint16 *>(outBase)[x] = packedPixel;
 					}
 				}
 			} break;
+		case kColorDepthMode32Bit:
+			memcpy(outBase, inRowBytes, width * 4);
+			break;
 		default:
 			break;
 		}
diff --git a/engines/mtropolis/metaengine.cpp b/engines/mtropolis/metaengine.cpp
index 32caad94fde..4a0d5d41ecb 100644
--- a/engines/mtropolis/metaengine.cpp
+++ b/engines/mtropolis/metaengine.cpp
@@ -200,7 +200,7 @@ void MTropolisMetaEngine::getSavegameThumbnail(Graphics::Surface &thumb) {
 		}
 
 		Common::SharedPtr<Graphics::ManagedSurface> outSurface(new Graphics::ManagedSurface());
-		outSurface->create(savegameScreenshot->w, savegameScreenshot->h, Graphics::createPixelFormat<888>());
+		outSurface->create(savegameScreenshot->w, savegameScreenshot->h, Graphics::PixelFormat::createFormatRGBA32());
 
 		for (int y = 0; y < savegameScreenshot->h; y++) {
 			for (int x = 0; x < savegameScreenshot->w; x++) {
@@ -212,7 +212,7 @@ void MTropolisMetaEngine::getSavegameThumbnail(Graphics::Surface &thumb) {
 
 		while (outSurface->w >= thumbnailWidth * 2) {
 			Common::SharedPtr<Graphics::ManagedSurface> temp(new Graphics::ManagedSurface());
-			temp->create(outSurface->w / 2, outSurface->h, Graphics::createPixelFormat<888>());
+			temp->create(outSurface->w / 2, outSurface->h, Graphics::PixelFormat::createFormatRGBA32());
 
 			for (int y = 0; y < temp->h; y++) {
 				for (int x = 0; x < temp->w; x++) {
@@ -234,7 +234,7 @@ void MTropolisMetaEngine::getSavegameThumbnail(Graphics::Surface &thumb) {
 
 		while (outSurface->h >= thumbnailHeight * 2) {
 			Common::SharedPtr<Graphics::ManagedSurface> temp(new Graphics::ManagedSurface());
-			temp->create(outSurface->w, outSurface->h / 2, Graphics::createPixelFormat<888>());
+			temp->create(outSurface->w, outSurface->h / 2, Graphics::PixelFormat::createFormatRGBA32());
 
 			for (int y = 0; y < temp->h; y++) {
 				for (int x = 0; x < temp->w; x++) {
@@ -257,7 +257,7 @@ void MTropolisMetaEngine::getSavegameThumbnail(Graphics::Surface &thumb) {
 		// TODO: Fix this for weird sizes
 		Common::SharedPtr<Graphics::ManagedSurface> changeTo16Temp = outSurface;
 		outSurface.reset(new Graphics::ManagedSurface());
-		outSurface->create(changeTo16Temp->w, changeTo16Temp->h, Graphics::createPixelFormat<565>());
+		outSurface->create(changeTo16Temp->w, changeTo16Temp->h, Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0));
 
 		for (int y = 0; y < outSurface->h; y++) {
 			for (int x = 0; x < outSurface->w; x++) {
diff --git a/engines/mtropolis/subtitles.cpp b/engines/mtropolis/subtitles.cpp
index 6b99f20cae9..d40407e0f44 100644
--- a/engines/mtropolis/subtitles.cpp
+++ b/engines/mtropolis/subtitles.cpp
@@ -569,7 +569,8 @@ void SubtitleRenderer::render() {
 			int surfaceWidth = static_cast<int>(widestLine) + borderWidth * 2 + horizontalPadding * 2;
 			int surfaceHeight = static_cast<int>(itemLines) * _fontHeight + borderWidth * 2 + verticalPadding * 2;
 
-			Graphics::PixelFormat fmt = Graphics::createPixelFormat<8888>();
+			// TODO: Pick a format closer to the screen format
+			Graphics::PixelFormat fmt = Graphics::PixelFormat::createFormatRGBA32();
 			Common::SharedPtr<Graphics::ManagedSurface> surface(new Graphics::ManagedSurface(surfaceWidth, surfaceHeight, fmt));
 
 			surface->fillRect(Common::Rect(0, 0, surfaceWidth, surfaceHeight), fmt.RGBToColor(0, 0, 0));
diff --git a/engines/testbed/graphics.cpp b/engines/testbed/graphics.cpp
index 690502875dc..c4a13e52fa2 100644
--- a/engines/testbed/graphics.cpp
+++ b/engines/testbed/graphics.cpp
@@ -964,7 +964,7 @@ TestExitStatus GFXtests::maskedCursors() {
 			if (haveCursorAlpha) {
 				g_system->delayMillis(500);
 
-				Graphics::PixelFormat rgbaFormat = Graphics::createPixelFormat<8888>();
+				Graphics::PixelFormat rgbaFormat = Graphics::PixelFormat::createFormatRGBA32();
 
 				uint32 rgbaCursorData[16 * 16];
 				for (uint i = 0; i < 16 * 16; i++) {
diff --git a/engines/vcruise/menu.cpp b/engines/vcruise/menu.cpp
index 2126bb519c3..da853ebd374 100644
--- a/engines/vcruise/menu.cpp
+++ b/engines/vcruise/menu.cpp
@@ -761,7 +761,7 @@ void ReahSoundMenuPage::addPageContents() {
 
 		Common::Point sliderSize(40, 60);
 
-		_sliderKeyGraphic.reset(new Graphics::ManagedSurface(sliderSize.x, sliderSize.y, Graphics::createPixelFormat<8888>()));
+		_sliderKeyGraphic.reset(new Graphics::ManagedSurface(sliderSize.x, sliderSize.y, Graphics::PixelFormat::createFormatRGBA32()));
 
 		Graphics::PixelFormat srcFormat = soundGraphics->format;
 		Graphics::PixelFormat dstFormat = _sliderKeyGraphic->format;
diff --git a/graphics/thumbnail.cpp b/graphics/thumbnail.cpp
index 0d173be78e4..93d411f50de 100644
--- a/graphics/thumbnail.cpp
+++ b/graphics/thumbnail.cpp
@@ -101,7 +101,7 @@ HeaderState loadHeader(Common::SeekableReadStream &in, ThumbnailHeader &header,
 		header.format.aShift = in.readByte();
 	} else {
 		// Version 1 used a hardcoded RGB565.
-		header.format = createPixelFormat<565>();
+		header.format = PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0);
 	}
 
 	if (in.err() || in.eos()) {



From noreply at scummvm.org  Sun Jun 14 21:06:26 2026
From: noreply at scummvm.org (sev-)
Date: Sun, 14 Jun 2026 21:06:26 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 05d94b720557df6b1914089120be7f67353a9fa2
Message-ID: <20260614210626.2826B7005CC@ron.scummvm.net>

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:
98c198a1bb WII: Always build with RGB colour support
05d94b7205 WII: Fully implement conversion for unsupported pixel formats


Commit: 98c198a1bb24d7ace3582c7e7f6d0eb8168713f5
    https://github.com/scummvm/scummvm/commit/98c198a1bb24d7ace3582c7e7f6d0eb8168713f5
Author: Cameron Cawley (ccawley2011 at gmail.com)
Date: 2026-06-14T23:06:22+02:00

Commit Message:
WII: Always build with RGB colour support

Changed paths:
    backends/platform/wii/osystem.cpp
    backends/platform/wii/osystem.h
    backends/platform/wii/osystem_gfx.cpp


diff --git a/backends/platform/wii/osystem.cpp b/backends/platform/wii/osystem.cpp
index 2bf4d50911b..3b46e8fb35c 100644
--- a/backends/platform/wii/osystem.cpp
+++ b/backends/platform/wii/osystem.cpp
@@ -70,11 +70,9 @@ OSystem_Wii::OSystem_Wii() :
 	_bilinearFilter(false),
 	_pfRGB565(Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0)),
 	_pfRGB3444(Graphics::PixelFormat(2, 4, 4, 4, 3, 8, 4, 0, 12)),
-#ifdef USE_RGB_COLOR
 	_pfGame(Graphics::PixelFormat::createFormatCLUT8()),
 	_pfGameTexture(Graphics::PixelFormat::createFormatCLUT8()),
 	_pfCursor(Graphics::PixelFormat::createFormatCLUT8()),
-#endif
 
 	_optionsDlgActive(false),
 	_consoleVisible(false),
diff --git a/backends/platform/wii/osystem.h b/backends/platform/wii/osystem.h
index 74feb130c6f..b235cf1e4e7 100644
--- a/backends/platform/wii/osystem.h
+++ b/backends/platform/wii/osystem.h
@@ -86,11 +86,9 @@ private:
 	bool _bilinearFilter;
 	const Graphics::PixelFormat _pfRGB565;
 	const Graphics::PixelFormat _pfRGB3444;
-#ifdef USE_RGB_COLOR
 	Graphics::PixelFormat _pfGame;
 	Graphics::PixelFormat _pfGameTexture;
 	Graphics::PixelFormat _pfCursor;
-#endif
 
 	bool _consoleVisible;
 	bool _optionsDlgActive;
diff --git a/backends/platform/wii/osystem_gfx.cpp b/backends/platform/wii/osystem_gfx.cpp
index d44659ec2d7..8b2b06bc510 100644
--- a/backends/platform/wii/osystem_gfx.cpp
+++ b/backends/platform/wii/osystem_gfx.cpp
@@ -228,7 +228,6 @@ void OSystem_Wii::initSize(uint width, uint height,
 	bool update = false;
 	gfx_tex_format_t tex_format;
 
-#ifdef USE_RGB_COLOR
 	Graphics::PixelFormat newFormat;
 
 	if (format)
@@ -243,21 +242,16 @@ void OSystem_Wii::initSize(uint width, uint height,
 		_pfGame = newFormat;
 		update = true;
 	}
-#endif
 
 	uint newWidth, newHeight;
 
-#ifdef USE_RGB_COLOR
 	if (_pfGame.bytesPerPixel > 1) {
 		newWidth = ROUNDUP(width, 4);
 		newHeight = ROUNDUP(height, 4);
 	} else {
-#endif
 		newWidth = ROUNDUP(width, 8);
 		newHeight = ROUNDUP(height, 4);
-#ifdef USE_RGB_COLOR
 	}
-#endif
 
 	if (_gameWidth != newWidth || _gameHeight != newHeight) {
 		assert((newWidth <= 640) && (newHeight <= 480));
@@ -285,7 +279,6 @@ void OSystem_Wii::initSize(uint width, uint height,
 
 		tex_format = GFX_TF_PALETTE_RGB565;
 
-#ifdef USE_RGB_COLOR
 		if (_pfGame.bytesPerPixel > 1) {
 			tex_format = GFX_TF_RGB565;
 			_pfGameTexture = _pfRGB565;
@@ -301,12 +294,6 @@ void OSystem_Wii::initSize(uint width, uint height,
 										_pfGame.bytesPerPixel);
 		memset(_gamePixels, 0, _gameWidth * _gameHeight *
 				_pfGame.bytesPerPixel);
-#else
-		printf("initSize %u*%u\n", _gameWidth, _gameHeight);
-
-		_gamePixels = (u8 *) memalign(32, _gameWidth * _gameHeight);
-		memset(_gamePixels, 0, _gameWidth * _gameHeight);
-#endif
 
 		if (!gfx_tex_init(&_texGame, tex_format, TLUT_GAME,
 					_gameWidth, _gameHeight)) {
@@ -337,12 +324,10 @@ void OSystem_Wii::updateMousePalette() {
 	if (_texMouse.palette) {
 		if (!_cursorPaletteDisabled) {
 			memcpy(_texMouse.palette, _cursorPalette, 256 * 2);
-#ifdef USE_RGB_COLOR
 		} else if (_pfGame.bytesPerPixel != 1) {
 			// When restoring the palette, there may be cases where game doesn't have any palette
 			// In this case, clear the palette
 			memset(_texMouse.palette, 0, 256 * 2);
-#endif
 		} else {
 			memcpy(_texMouse.palette, _texGame.palette, 256 * 2);
 		}
@@ -356,9 +341,7 @@ void OSystem_Wii::setPalette(const byte *colors, uint start, uint num) {
 	printf("%s(%p, %d, %d) _cursorPaletteDisabled:%d\n", __func__, colors, start, num, _cursorPaletteDisabled);
 #endif
 
-#ifdef USE_RGB_COLOR
 	assert(_pfGame.bytesPerPixel == 1);
-#endif
 
 	const byte *s = colors;
 	u16 *d = _texGame.palette;
@@ -372,9 +355,7 @@ void OSystem_Wii::setPalette(const byte *colors, uint start, uint num) {
 }
 
 void OSystem_Wii::grabPalette(byte *colors, uint start, uint num) const {
-#ifdef USE_RGB_COLOR
 	assert(_pfGame.bytesPerPixel == 1);
-#endif
 
 	u16 *s = _texGame.palette;
 	byte *d = colors;
@@ -424,7 +405,6 @@ void OSystem_Wii::copyRectToScreen(const void *buf, int pitch, int x, int y,
 	assert(w > 0 && x + w <= _gameWidth);
 	assert(h > 0 && y + h <= _gameHeight);
 
-#ifdef USE_RGB_COLOR
 	if (_pfGame.bytesPerPixel > 1) {
 		if (!Graphics::crossBlit(_gamePixels +
 									y * _gameWidth * _pfGame.bytesPerPixel +
@@ -435,7 +415,6 @@ void OSystem_Wii::copyRectToScreen(const void *buf, int pitch, int x, int y,
 			::abort();
 		}
 	} else {
-#endif
 		byte *dst = _gamePixels + y * _gameWidth + x;
 		if (_gameWidth == pitch && pitch == w) {
 			memcpy(dst, buf, h * w);
@@ -447,9 +426,6 @@ void OSystem_Wii::copyRectToScreen(const void *buf, int pitch, int x, int y,
 				dst += _gameWidth;
 			} while (--h);
 		}
-#ifdef USE_RGB_COLOR
-	}
-#endif
 
 	_gameDirty = true;
 }
@@ -555,11 +531,7 @@ void OSystem_Wii::updateScreen() {
 
 Graphics::Surface *OSystem_Wii::lockScreen() {
 	_surface.init(_gameWidth, _gameHeight,
-#ifdef USE_RGB_COLOR
 	              _gameWidth * _pfGame.bytesPerPixel, _gamePixels, _pfGame
-#else
-	              _gameWidth, _gamePixels, Graphics::PixelFormat::createFormatCLUT8()
-#endif
 	             );
 
 	return &_surface;
@@ -696,7 +668,6 @@ void OSystem_Wii::setMouseCursor(const void *buf, uint w, uint h, int hotspotX,
 	uint tw, th;
 	uint32 oldKeycolor = _mouseKeyColor;
 
-#ifdef USE_RGB_COLOR
 	if (!format)
 		_pfCursor = Graphics::PixelFormat::createFormatCLUT8();
 	else
@@ -709,14 +680,11 @@ void OSystem_Wii::setMouseCursor(const void *buf, uint w, uint h, int hotspotX,
 		th = ROUNDUP(h, 4);
 
 	} else {
-#endif
 		_mouseKeyColor = keycolor & 0xff;
 		tw = ROUNDUP(w, 8);
 		th = ROUNDUP(h, 4);
 
-#ifdef USE_RGB_COLOR
 	}
-#endif
 
 	if (!gfx_tex_init(&_texMouse, tex_format, TLUT_MOUSE, tw, th)) {
 		printf("could not init the mouse texture\n");
@@ -738,7 +706,6 @@ void OSystem_Wii::setMouseCursor(const void *buf, uint w, uint h, int hotspotX,
 	else
 		memset(tmp, _mouseKeyColor, tw * th);
 
-#ifdef USE_RGB_COLOR
 	if (bpp > 1) {
 
 		if (!Graphics::crossBlit(tmp, (const byte *)buf,
@@ -763,7 +730,6 @@ void OSystem_Wii::setMouseCursor(const void *buf, uint w, uint h, int hotspotX,
 			d += tw - w;
 		}
 	} else {
-#endif
 		const byte *s = (const byte *)buf;
 		byte *d = (byte *) tmp;
 		for (u16 y = 0; y < h; ++y) {
@@ -773,9 +739,7 @@ void OSystem_Wii::setMouseCursor(const void *buf, uint w, uint h, int hotspotX,
 			d += tw - w;
 		}
 
-#ifdef USE_RGB_COLOR
 	}
-#endif
 
 	gfx_tex_convert(&_texMouse, tmp);
 	free(tmp);


Commit: 05d94b720557df6b1914089120be7f67353a9fa2
    https://github.com/scummvm/scummvm/commit/05d94b720557df6b1914089120be7f67353a9fa2
Author: Cameron Cawley (ccawley2011 at gmail.com)
Date: 2026-06-14T23:06:22+02:00

Commit Message:
WII: Fully implement conversion for unsupported pixel formats

Changed paths:
    backends/platform/wii/osystem.cpp
    backends/platform/wii/osystem.h
    backends/platform/wii/osystem_gfx.cpp


diff --git a/backends/platform/wii/osystem.cpp b/backends/platform/wii/osystem.cpp
index 3b46e8fb35c..4cd6f634b2b 100644
--- a/backends/platform/wii/osystem.cpp
+++ b/backends/platform/wii/osystem.cpp
@@ -50,6 +50,7 @@ OSystem_Wii::OSystem_Wii() :
 	_gameWidth(0),
 	_gameHeight(0),
 	_gamePixels(NULL),
+	_gamePixelsTexture(NULL),
 	_gameDirty(false),
 
 	_overlayVisible(true),
@@ -59,6 +60,7 @@ OSystem_Wii::OSystem_Wii() :
 	_overlayPixels(NULL),
 	_overlayDirty(false),
 
+	_blitFunc(NULL),
 	_lastScreenUpdate(0),
 	_currentWidth(0),
 	_currentHeight(0),
diff --git a/backends/platform/wii/osystem.h b/backends/platform/wii/osystem.h
index b235cf1e4e7..e1688c42cd9 100644
--- a/backends/platform/wii/osystem.h
+++ b/backends/platform/wii/osystem.h
@@ -32,6 +32,7 @@
 #include "common/rect.h"
 #include "common/events.h"
 #include "backends/base-backend.h"
+#include "graphics/blit.h"
 #include "graphics/paletteman.h"
 #include "graphics/surface.h"
 #include "audio/mixer_intern.h"
@@ -62,7 +63,7 @@ private:
 
 	bool _gameRunning;
 	u16 _gameWidth, _gameHeight;
-	u8 *_gamePixels;
+	u8 *_gamePixels, *_gamePixelsTexture;
 	Graphics::Surface _surface;
 	gfx_screen_coords_t _coordsGame;
 	gfx_tex_t _texGame;
@@ -77,6 +78,7 @@ private:
 	bool _overlayDirty;
 	bool _overlayInGUI;
 
+	Graphics::FastBlitFunc _blitFunc;
 	u32 _lastScreenUpdate;
 	u16 _currentWidth, _currentHeight;
 	f32 _currentXScale, _currentYScale;
diff --git a/backends/platform/wii/osystem_gfx.cpp b/backends/platform/wii/osystem_gfx.cpp
index 8b2b06bc510..f10b55aeaf4 100644
--- a/backends/platform/wii/osystem_gfx.cpp
+++ b/backends/platform/wii/osystem_gfx.cpp
@@ -111,6 +111,9 @@ void OSystem_Wii::deinitGfx() {
 	free(_gamePixels);
 	_gamePixels = NULL;
 
+	free(_gamePixelsTexture);
+	_gamePixelsTexture = NULL;
+
 	free(_overlayPixels);
 	_overlayPixels = NULL;
 }
@@ -235,9 +238,6 @@ void OSystem_Wii::initSize(uint width, uint height,
 	else
 		newFormat = Graphics::PixelFormat::createFormatCLUT8();
 
-	if (newFormat.bytesPerPixel > 2)
-		newFormat = Graphics::PixelFormat::createFormatCLUT8();
-
 	if (_pfGame != newFormat) {
 		_pfGame = newFormat;
 		update = true;
@@ -254,6 +254,7 @@ void OSystem_Wii::initSize(uint width, uint height,
 	}
 
 	if (_gameWidth != newWidth || _gameHeight != newHeight) {
+		// TODO: Return an error code in endGFXTransaction()
 		assert((newWidth <= 640) && (newHeight <= 480));
 
 		if (width != newWidth || height != newHeight)
@@ -275,26 +276,40 @@ void OSystem_Wii::initSize(uint width, uint height,
 	}
 
 	if (update) {
-		free(_gamePixels);
+		if (_gamePixels != _gamePixelsTexture)
+			free(_gamePixels);
+		free(_gamePixelsTexture);
 
 		tex_format = GFX_TF_PALETTE_RGB565;
 
 		if (_pfGame.bytesPerPixel > 1) {
+			// TODO: Support other texture formats?
 			tex_format = GFX_TF_RGB565;
 			_pfGameTexture = _pfRGB565;
 		}
 
-		printf("initSize %u*%u*%u (%u%u%u -> %u%u%u match: %d)\n",
-				_gameWidth, _gameHeight, _pfGame.bytesPerPixel * 8,
-				8 - _pfGame.rLoss, 8 - _pfGame.gLoss, 8 - _pfGame.bLoss,
-				8 - _pfGameTexture.rLoss, 8 - _pfGameTexture.gLoss,
-				8 - _pfGameTexture.bLoss, _pfGame == _pfGameTexture);
+		printf("initSize %u*%u*%u (%s -> %s match: %d)\n",
+				_gameWidth, _gameHeight, _pfGame.bytesPerPixel,
+				_pfGame.toString().c_str(),
+				_pfGameTexture.toString().c_str(),
+				_pfGame == _pfGameTexture);
 
 		_gamePixels = (u8 *) memalign(32, _gameWidth * _gameHeight *
 										_pfGame.bytesPerPixel);
 		memset(_gamePixels, 0, _gameWidth * _gameHeight *
 				_pfGame.bytesPerPixel);
 
+		if (_pfGame != _pfGameTexture) {
+			_gamePixelsTexture = (u8 *) memalign(32, _gameWidth * _gameHeight *
+											_pfGameTexture.bytesPerPixel);
+			memset(_gamePixelsTexture, 0, _gameWidth * _gameHeight *
+					_pfGameTexture.bytesPerPixel);
+		} else {
+			_gamePixelsTexture = _gamePixels;
+		}
+
+		_blitFunc = Graphics::getFastBlitFunc(_pfGameTexture, _pfGame);
+
 		if (!gfx_tex_init(&_texGame, tex_format, TLUT_GAME,
 					_gameWidth, _gameHeight)) {
 			printf("could not init the game texture\n");
@@ -405,27 +420,11 @@ void OSystem_Wii::copyRectToScreen(const void *buf, int pitch, int x, int y,
 	assert(w > 0 && x + w <= _gameWidth);
 	assert(h > 0 && y + h <= _gameHeight);
 
-	if (_pfGame.bytesPerPixel > 1) {
-		if (!Graphics::crossBlit(_gamePixels +
-									y * _gameWidth * _pfGame.bytesPerPixel +
-									x * _pfGame.bytesPerPixel,
-									(const byte *)buf, _gameWidth * _pfGame.bytesPerPixel,
-									pitch, w, h, _pfGameTexture, _pfGame)) {
-			printf("crossBlit failed\n");
-			::abort();
-		}
-	} else {
-		byte *dst = _gamePixels + y * _gameWidth + x;
-		if (_gameWidth == pitch && pitch == w) {
-			memcpy(dst, buf, h * w);
-		} else {
-			const byte *src = (const byte *)buf;
-			do {
-				memcpy(dst, src, w);
-				src += pitch;
-				dst += _gameWidth;
-			} while (--h);
-		}
+	Graphics::copyBlit(_gamePixels +
+	                   y * _gameWidth * _pfGame.bytesPerPixel +
+	                   x * _pfGame.bytesPerPixel,
+	                   (const byte *)buf, _gameWidth * _pfGame.bytesPerPixel,
+	                   pitch, w, h, _pfGame.bytesPerPixel);
 
 	_gameDirty = true;
 }
@@ -471,7 +470,22 @@ void OSystem_Wii::updateScreen() {
 
 	if (_gameRunning) {
 		if (_gameDirty) {
-			gfx_tex_convert(&_texGame, _gamePixels);
+			if (_gamePixels != _gamePixelsTexture) {
+				// TODO: Track dirty regions of the screen?
+				if (_blitFunc) {
+					_blitFunc(_gamePixelsTexture, _gamePixels,
+					          _gameWidth * _pfGameTexture.bytesPerPixel,
+					          _gameWidth * _pfGame.bytesPerPixel,
+					          _gameWidth, _gameHeight);
+				} else {
+					Graphics::crossBlit(_gamePixelsTexture, _gamePixels,
+					                    _gameWidth * _pfGameTexture.bytesPerPixel,
+					                    _gameWidth * _pfGame.bytesPerPixel,
+					                    _gameWidth, _gameHeight, _pfGameTexture, _pfGame);
+				}
+
+			}
+			gfx_tex_convert(&_texGame, _gamePixelsTexture);
 			_gameDirty = false;
 		}
 



From noreply at scummvm.org  Sun Jun 14 21:18:46 2026
From: noreply at scummvm.org (sev-)
Date: Sun, 14 Jun 2026 21:18:46 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 e64c0cb51bcc01eec9b70412d2c431f1c3ce7025
Message-ID: <20260614211846.B12077005CC@ron.scummvm.net>

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

Summary:
e64c0cb51b COMMON: Remove implicit comparison between String and U32String


Commit: e64c0cb51bcc01eec9b70412d2c431f1c3ce7025
    https://github.com/scummvm/scummvm/commit/e64c0cb51bcc01eec9b70412d2c431f1c3ce7025
Author: Cameron Cawley (ccawley2011 at gmail.com)
Date: 2026-06-14T23:18:43+02:00

Commit Message:
COMMON: Remove implicit comparison between String and U32String

Changed paths:
    common/dbcs-str.cpp
    common/dbcs-str.h
    common/formats/winexe_pe.cpp
    common/str-base.cpp
    common/str-base.h
    common/ustr.cpp
    common/ustr.h
    engines/bladerunner/bladerunner.cpp
    engines/dgds/sound.cpp
    engines/director/lingo/xlibs/e/ednox.cpp
    engines/glk/scott/load_game.cpp
    engines/glk/zcode/processor_text.cpp
    engines/hadesch/rooms/options.cpp
    engines/ultima/nuvie/keybinding/keys.cpp
    gui/options.cpp
    gui/widgets/grid.cpp
    gui/widgets/groupedlist.cpp
    gui/widgets/list.cpp
    test/common/str.h


diff --git a/common/dbcs-str.cpp b/common/dbcs-str.cpp
index 06367e3f6a2..17104cf0a6f 100644
--- a/common/dbcs-str.cpp
+++ b/common/dbcs-str.cpp
@@ -105,22 +105,6 @@ DBCSString &DBCSString::operator+=(value_type c) {
 	return *this;
 }
 
-bool DBCSString::operator==(const String &x) const {
-	return equalsC(x.c_str());
-}
-
-bool DBCSString::operator==(const char *x) const {
-	return equalsC(x);
-}
-
-bool DBCSString::operator!=(const String &x) const {
-	return !equalsC(x.c_str());
-}
-
-bool DBCSString::operator!=(const char *x) const {
-	return !equalsC(x);
-}
-
 DBCSString operator+(const DBCSString &x, const DBCSString &y) {
 	DBCSString temp(x);
 	temp += y;
diff --git a/common/dbcs-str.h b/common/dbcs-str.h
index d43a60346f1..2e47f24c379 100644
--- a/common/dbcs-str.h
+++ b/common/dbcs-str.h
@@ -105,21 +105,6 @@ public:
 	/** @overload */
 	DBCSString &operator+=(value_type c);
 
-	using BaseString<value_type>::operator==;
-	using BaseString<value_type>::operator!=;
-
-	/** Check whether this string is identical to string @p x. */
-	bool operator==(const String &x) const;
-
-	/** @overload */
-	bool operator==(const char *x) const;
-
-	/** Check whether this string is different than string @p x. */
-	bool operator!=(const String &x) const;
-
-	/** @overload */
-	bool operator!=(const char *x) const;
-
 	/** Convert the string to the standard String represantation. */
 	String convertToString() const;
 
diff --git a/common/formats/winexe_pe.cpp b/common/formats/winexe_pe.cpp
index 5736c69c45d..a0693409fb8 100644
--- a/common/formats/winexe_pe.cpp
+++ b/common/formats/winexe_pe.cpp
@@ -297,7 +297,7 @@ WinResources::VersionInfo *PEResources::parseVersionInfo(SeekableReadStream *res
 
 			info->hash.setVal(key.encode(), value);
 		} else {
-			if (key == "VS_VERSION_INFO") {
+			if (key == U"VS_VERSION_INFO") {
 				if (!info->readVSVersionInfo(res))
 					return info;
 			}
diff --git a/common/str-base.cpp b/common/str-base.cpp
index a41377a0c0f..0cbd6573875 100644
--- a/common/str-base.cpp
+++ b/common/str-base.cpp
@@ -321,21 +321,6 @@ TEMPLATE bool BASESTRING::equals(const value_type *ptr) const {
 	return false;
 }
 
-TEMPLATE bool BASESTRING::equalsC(const char *ptr) const {
-	uint i = 0;
-
-	for (; i < _size && *ptr; i++, ptr++) {
-		if (_str[i] != (T)*ptr)
-			return false;
-	}
-
-	if (i == _size && *ptr == 0) {
-		return true;
-	}
-
-	return false;
-}
-
 TEMPLATE bool BASESTRING::operator==(const BaseString &x) const {
 	return equals(x);
 }
@@ -386,23 +371,6 @@ TEMPLATE int BASESTRING::compareTo(const value_type *ptr) const {
 	return -1;
 }
 
-TEMPLATE int BASESTRING::compareToC(const char *ptr) const {
-	uint32 i = 0;
-	for (; i < _size && *ptr; ++i, ptr++) {
-		uint32 sc = _str[i];
-		uint32 xc = *ptr;
-		if (sc < xc)
-			return -1;
-		else if (sc > xc)
-			return +1;
-	}
-	if (i == _size && *ptr == 0)
-		return 0;
-	if (*ptr == 0)
-		return +1;
-	return -1;
-}
-
 TEMPLATE bool BASESTRING::operator<(const BaseString &x) const {
 	return compareTo(x) < 0;
 }
diff --git a/common/str-base.h b/common/str-base.h
index d48e8dd7aaa..f92b0e3aeef 100644
--- a/common/str-base.h
+++ b/common/str-base.h
@@ -125,10 +125,8 @@ public:
 	 */
 	bool equals(const BaseString &x) const;
 	bool equals(const value_type *x) const;
-	bool equalsC(const char *x) const;
 	int compareTo(const BaseString &x) const;           // strcmp clone
 	int compareTo(const value_type *x) const;             // strcmp clone
-	int compareToC(const char *x) const;             // strcmp clone
 
 	/** Set character c at position p, replacing the previous character there. */
 	void setChar(value_type c, uint32 p);
diff --git a/common/ustr.cpp b/common/ustr.cpp
index 4c6c0614e22..10b68bda44f 100644
--- a/common/ustr.cpp
+++ b/common/ustr.cpp
@@ -95,22 +95,6 @@ U32String &U32String::operator+=(value_type c) {
 	return *this;
 }
 
-bool U32String::operator==(const String &x) const {
-	return equalsC(x.c_str());
-}
-
-bool U32String::operator==(const char *x) const {
-	return equalsC(x);
-}
-
-bool U32String::operator!=(const String &x) const {
-	return !equalsC(x.c_str());
-}
-
-bool U32String::operator!=(const char *x) const {
-	return !equalsC(x);
-}
-
 U32String operator+(const U32String &x, const U32String &y) {
 	U32String temp(x);
 	temp += y;
diff --git a/common/ustr.h b/common/ustr.h
index 2a91a3d8f26..676d72f61ee 100644
--- a/common/ustr.h
+++ b/common/ustr.h
@@ -121,21 +121,6 @@ public:
 	/** @overload */
 	U32String &operator+=(value_type c);
 
-	using BaseString<value_type>::operator==;
-	using BaseString<value_type>::operator!=;
-
-	/** Check whether this string is identical to string @p x. */
-	bool operator==(const String &x) const;
-
-	/** @overload */
-	bool operator==(const char *x) const;
-
-	/** Check whether this string is different than string @p x. */
-	bool operator!=(const String &x) const;
-
-	/** @overload */
-	bool operator!=(const char *x) const;
-
 	/** Convert the string to the given @p page encoding and return the result as a new String. */
 	String encode(CodePage page = kUtf8) const;
 
diff --git a/engines/bladerunner/bladerunner.cpp b/engines/bladerunner/bladerunner.cpp
index d00501bd844..ee7a12eff0d 100644
--- a/engines/bladerunner/bladerunner.cpp
+++ b/engines/bladerunner/bladerunner.cpp
@@ -406,9 +406,9 @@ Common::Error BladeRunnerEngine::run() {
 				getEventManager()->getKeymapper()->getKeymap(BladeRunnerEngine::kGameplayKeymapId)->setEnabled(true);
 				const Common::Keymap::ActionArray karr = getEventManager()->getKeymapper()->getKeymap(BladeRunnerEngine::kGameplayKeymapId)->getActions();
 				for (uint8 i = 0; i < karr.size(); ++i) {
-					if (karr[i]->description == "COMBAT"
-					    || karr[i]->description == "SKIPDLG"
-					    || karr[i]->description == "KIADB") {
+					if (karr[i]->description == U"COMBAT"
+					    || karr[i]->description == U"SKIPDLG"
+					    || karr[i]->description == U"KIADB") {
 						getEventManager()->getKeymapper()->getKeymap(BladeRunnerEngine::kGameplayKeymapId)->unregisterMapping(karr[i]);
 					}
 				}
diff --git a/engines/dgds/sound.cpp b/engines/dgds/sound.cpp
index c044b7d08ba..ba76d012c31 100644
--- a/engines/dgds/sound.cpp
+++ b/engines/dgds/sound.cpp
@@ -430,7 +430,7 @@ void Sound::loadSNGSoundData(const Common::String &filename, Common::Array<Sound
 
 void Sound::patchSoundData(const Common::String& filename, uint16 soundNumber, byte* data, uint32 size) {
 	// TODO Can we check here if the game that's currently playing is Heart of China?
-	if (filename.equalsC("SOUNDS1.SNG") && soundNumber == 59) {
+	if (filename == "SOUNDS1.SNG" && soundNumber == 59) {
 		// Heart of China running water sound effect. This is broken on MT-32.
 		// This is an original game bug.
 		//
diff --git a/engines/director/lingo/xlibs/e/ednox.cpp b/engines/director/lingo/xlibs/e/ednox.cpp
index 5a6d10a3cf8..eb971fa70be 100644
--- a/engines/director/lingo/xlibs/e/ednox.cpp
+++ b/engines/director/lingo/xlibs/e/ednox.cpp
@@ -124,8 +124,8 @@ XOBJSTUBNR(Ednox::m_dispose)
 
 void Ednox::m_getpathx(int nargs) {
 	/* int mMacMode = */ g_lingo->pop().asInt();
-	Common::U32String hStrIn = g_lingo->pop().asString();
-	/* Common::U32String hSection = */ g_lingo->pop().asString();
+	Common::String hStrIn = g_lingo->pop().asString();
+	/* Common::String hSection = */ g_lingo->pop().asString();
 	hStrIn.toLowercase();
 	if (hStrIn == "cdpath"){
 		g_lingo->push(Datum("d:\\"));
@@ -135,8 +135,8 @@ void Ednox::m_getpathx(int nargs) {
 }
 
 void Ednox::m_iscdx(int nargs) {
-	/* Common::U32String hStrIn = */ g_lingo->pop().asString();
-	Common::U32String hDrive = g_lingo->pop().asString();
+	/* Common::String hStrIn = */ g_lingo->pop().asString();
+	Common::String hDrive = g_lingo->pop().asString();
 	// g_lingo->printSTUBWithArglist("Ednox::m_iscdx", nargs);
 	if (hDrive == "d:\\"){
 		g_lingo->push(Datum("0"));
@@ -146,7 +146,7 @@ void Ednox::m_iscdx(int nargs) {
 }
 
 void Ednox::m_setdrivex(int nargs) {
-	// Common::U32String hStrIn = g_lingo->pop().asString();
+	// Common::String hStrIn = g_lingo->pop().asString();
 	g_lingo->printSTUBWithArglist("Ednox::m_setdrivex", nargs);
 	g_lingo->dropStack(nargs);
 	g_lingo->push(Datum("1"));
@@ -156,8 +156,8 @@ XOBJSTUB(Ednox::m_checksoundx, "")
 XOBJSTUB(Ednox::m_clearsoundx, "")
 
 void Ednox::m_deletedocumentfile(int nargs) {
-	// Common::U32String hFile = g_lingo->pop().asString();
-	// Common::U32String hDir = g_lingo->pop().asString();
+	// Common::String hFile = g_lingo->pop().asString();
+	// Common::String hDir = g_lingo->pop().asString();
 	g_lingo->printSTUBWithArglist("Ednox::m_deletedocumentfile", nargs);
 	g_lingo->dropStack(nargs);
 }
@@ -166,7 +166,7 @@ XOBJSTUB(Ednox::m_enabletaskswitch, "")
 XOBJSTUB(Ednox::m_disabletaskswitch, "")
 
 void Ednox::m_drawbkgndx(int nargs) {
-	// Common::U32String hBkgnd = g_lingo->pop().asString();
+	// Common::String hBkgnd = g_lingo->pop().asString();
 	g_lingo->printSTUBWithArglist("Ednox::m_drawbkgndx", nargs);
 }
 
@@ -253,7 +253,7 @@ XOBJSTUB(Ednox::m_playsoundx, "")
 XOBJSTUB(Ednox::m_restorex, "")
 
 void Ednox::m_savex(int nargs) {
-	// Common::U32String hStrIn = g_lingo->pop().asString();
+	// Common::String hStrIn = g_lingo->pop().asString();
 	g_lingo->printSTUBWithArglist("Ednox::m_savex", nargs);
 	g_lingo->dropStack(nargs);
 }
diff --git a/engines/glk/scott/load_game.cpp b/engines/glk/scott/load_game.cpp
index fc9b988633a..3c482195b05 100644
--- a/engines/glk/scott/load_game.cpp
+++ b/engines/glk/scott/load_game.cpp
@@ -96,7 +96,7 @@ void loadGameFile(Common::SeekableReadStream *f) {
 		const GlkDetectionEntry *p = SCOTT_GAMES;
 
 		while (p->_md5) {
-			if (md5.equalsC(p->_md5)) {
+			if (md5 == p->_md5) {
 				if (!scumm_stricmp(p->_extra, "")) {
 					_G(_fallbackGame)._gameID = SCOTTFREE;
 					break;
diff --git a/engines/glk/zcode/processor_text.cpp b/engines/glk/zcode/processor_text.cpp
index 732fe07a2c5..5d2c341fdce 100644
--- a/engines/glk/zcode/processor_text.cpp
+++ b/engines/glk/zcode/processor_text.cpp
@@ -568,14 +568,14 @@ void Processor::handleAbbreviations() {
 	Common::U32String word((const char32_t *)_decoded, wordSize);
 
 	// Check for standard abbreviations
-	if (word == "g")
-		word = "again";
-	else if (word == "o")
-		word = "oops";
-	else if (word == "x")
-		word = "examine";
-	else if (word == "z")
-		word = "wait";
+	if (word == Common::U32String('g'))
+		word = U"again";
+	else if (word == Common::U32String('o'))
+		word = U"oops";
+	else if (word == Common::U32String('x'))
+		word = U"examine";
+	else if (word == Common::U32String('z'))
+		word = U"wait";
 	else
 		return;
 
diff --git a/engines/hadesch/rooms/options.cpp b/engines/hadesch/rooms/options.cpp
index c1678478772..bcf1b150ff7 100644
--- a/engines/hadesch/rooms/options.cpp
+++ b/engines/hadesch/rooms/options.cpp
@@ -515,8 +515,8 @@ private:
 		bool selectedIsShown = false;
 		for (int i = 0; i < 6 && _showPos + i < (int) _userNames.size(); i++) {
 			Common::U32String name = _userNames[_showPos + i];
-			if (name == "")
-				name = "No name";
+			if (name.empty())
+				name = U"No name";
 			room->renderString("largeascii", name,
 					   Common::Point(150, 134 + 36 * i), 4000, 0,
 					   Common::String::format("username%d", i));
diff --git a/engines/ultima/nuvie/keybinding/keys.cpp b/engines/ultima/nuvie/keybinding/keys.cpp
index 33a3906c81d..502eea20652 100644
--- a/engines/ultima/nuvie/keybinding/keys.cpp
+++ b/engines/ultima/nuvie/keybinding/keys.cpp
@@ -501,8 +501,8 @@ Common::Array<Common::U32String> KeyBinder::buildKeyHelp() const {
 			if (inputs.size() > 0) {
 				Common::U32String desc;
 				// The * can't be bolded easily in markdown..
-				if (inputs[0].description == "*")
-					desc = "*";
+				if (inputs[0].description == Common::U32String('*'))
+					desc = Common::U32String('*');
 				else
 					desc = Common::U32String("**") + inputs[0].description + Common::U32String("**");
 				desc += Common::U32String(" - ") + action->description;
diff --git a/gui/options.cpp b/gui/options.cpp
index 7b971f05ab1..0f58e53cca0 100644
--- a/gui/options.cpp
+++ b/gui/options.cpp
@@ -3699,7 +3699,7 @@ void GlobalOptionsDialog::setupCloudTab() {
 	if (_storageLastSyncDesc) _storageLastSyncDesc->setVisible(shownConnectedInfo);
 	if (_storageLastSync) {
 		Common::U32String sync = CloudMan.getStorageLastSync(_selectedStorageIndex);
-		if (sync == "") {
+		if (sync.empty()) {
 			if (_selectedStorageIndex == CloudMan.getStorageIndex() && CloudMan.isSyncing())
 				sync = _("<right now>");
 			else
diff --git a/gui/widgets/grid.cpp b/gui/widgets/grid.cpp
index 41d1ef4b1f6..8c77357705f 100644
--- a/gui/widgets/grid.cpp
+++ b/gui/widgets/grid.cpp
@@ -270,12 +270,12 @@ void GridWidget::loadClosedGroups(const Common::U32String &groupName) {
 	// Recalls what groups were closed from the config
 	if (ConfMan.hasKey("group_" + groupName, ConfMan.kApplicationDomain)) {
 		const Common::String &val = ConfMan.get("group_" + groupName, ConfMan.kApplicationDomain);
-		Common::StringTokenizer hiddenGroups(val);
+		Common::U32StringTokenizer hiddenGroups(val.decode());
 
-		for (Common::String tok = hiddenGroups.nextToken(); tok.size(); tok = hiddenGroups.nextToken()) {
+		for (Common::U32String tok = hiddenGroups.nextToken(); tok.size(); tok = hiddenGroups.nextToken()) {
 			// See if the hidden group is in our group headers still, if so, hide it
 			for (Common::U32StringArray::size_type i = 0; i < _groupHeaders.size(); ++i) {
-				if (_groupHeaders[i] == tok || (tok == "unnamed" && _groupHeaders[i].size() == 0)) {
+				if (_groupHeaders[i] == tok || (tok == U"unnamed" && _groupHeaders[i].size() == 0)) {
 					uint groupID = _groupValueIndex[_groupHeaders[i]];
 					_groupExpanded[groupID] = false;
 					break;
diff --git a/gui/widgets/groupedlist.cpp b/gui/widgets/groupedlist.cpp
index ea166077f2e..91f0a8cdca4 100644
--- a/gui/widgets/groupedlist.cpp
+++ b/gui/widgets/groupedlist.cpp
@@ -165,12 +165,12 @@ void GroupedListWidget::loadClosedGroups(const Common::U32String &groupName) {
 	// Recalls what groups were closed from the config
 	if (ConfMan.hasKey("group_" + groupName, ConfMan.kApplicationDomain)) {
 		const Common::String &val = ConfMan.get("group_" + groupName, ConfMan.kApplicationDomain);
-		Common::StringTokenizer hiddenGroups(val);
+		Common::U32StringTokenizer hiddenGroups(val.decode());
 
-		for (Common::String tok = hiddenGroups.nextToken(); tok.size(); tok = hiddenGroups.nextToken()) {
+		for (Common::U32String tok = hiddenGroups.nextToken(); tok.size(); tok = hiddenGroups.nextToken()) {
 			// See if the hidden group is in our group headers still, if so, hide it
 			for (Common::U32StringArray::size_type i = 0; i < _groupHeaders.size(); ++i) {
-				if (_groupHeaders[i] == tok || (tok == "unnamed" && _groupHeaders[i].size() == 0)) {
+				if (_groupHeaders[i] == tok || (tok == U"unnamed" && _groupHeaders[i].size() == 0)) {
 					uint groupID = _groupValueIndex[_groupHeaders[i]];
 					_groupExpanded[groupID] = false;
 					break;
diff --git a/gui/widgets/list.cpp b/gui/widgets/list.cpp
index 697d0625acf..3e34a5a5a17 100644
--- a/gui/widgets/list.cpp
+++ b/gui/widgets/list.cpp
@@ -1171,10 +1171,10 @@ Common::U32String ListWidget::getThemeColor(ThemeEngine::FontColor color) {
 }
 
 ThemeEngine::FontColor ListWidget::getThemeColor(const Common::U32String &color) {
-	if (color == "normal")
+	if (color == U"normal")
 		return ThemeEngine::kFontColorNormal;
 
-	if (color == "alternate")
+	if (color == U"alternate")
 		return ThemeEngine::kFontColorAlternate;
 
 	warning("ListWidget::getThemeColor(): Malformed color (\"%s\")", color.encode().c_str());
diff --git a/test/common/str.h b/test/common/str.h
index c36adc3cf01..f8f43ea9f1c 100644
--- a/test/common/str.h
+++ b/test/common/str.h
@@ -666,18 +666,18 @@ class StringTestSuite : public CxxTest::TestSuite
 		// Makes a deep copy without changing the length of the original
 		Common::U32String s1 = Common::U32String("TestTestTestTestTestTestTestTestTestTestTest");
 		Common::U32String s2(s1);
-		TS_ASSERT_EQUALS(s1, "TestTestTestTestTestTestTestTestTestTestTest");
-		TS_ASSERT_EQUALS(s2, "TestTestTestTestTestTestTestTestTestTestTest");
+		TS_ASSERT_EQUALS(s1, U"TestTestTestTestTestTestTestTestTestTestTest");
+		TS_ASSERT_EQUALS(s2, U"TestTestTestTestTestTestTestTestTestTestTest");
 		s1.replace(0, 4, Common::U32String("TEST"));
-		TS_ASSERT_EQUALS(s1, "TESTTestTestTestTestTestTestTestTestTestTest");
-		TS_ASSERT_EQUALS(s2, "TestTestTestTestTestTestTestTestTestTestTest");
+		TS_ASSERT_EQUALS(s1, U"TESTTestTestTestTestTestTestTestTestTestTest");
+		TS_ASSERT_EQUALS(s2, U"TestTestTestTestTestTestTestTestTestTestTest");
 
 		// Makes a deep copy when we shorten the string
 		Common::U32String s3 = Common::U32String("TestTestTestTestTestTestTestTestTestTestTest");
 		Common::U32String s4(s3);
 		s3.replace(0, 32, Common::U32String());
-		TS_ASSERT_EQUALS(s3, "TestTestTest");
-		TS_ASSERT_EQUALS(s4, "TestTestTestTestTestTestTestTestTestTestTest");
+		TS_ASSERT_EQUALS(s3, U"TestTestTest");
+		TS_ASSERT_EQUALS(s4, U"TestTestTestTestTestTestTestTestTestTestTest");
 	}
 
 	void test_find() {



From noreply at scummvm.org  Sun Jun 14 22:04:47 2026
From: noreply at scummvm.org (sev-)
Date: Sun, 14 Jun 2026 22:04:47 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 5fe37be2fa24bce51fe9e226073c49aebb41ec29
Message-ID: <20260614220447.EAFB57005CC@ron.scummvm.net>

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

Summary:
5fe37be2fa webOS: add 64-bit compile


Commit: 5fe37be2fa24bce51fe9e226073c49aebb41ec29
    https://github.com/scummvm/scummvm/commit/5fe37be2fa24bce51fe9e226073c49aebb41ec29
Author: Craig Carnell (1188869+cscd98 at users.noreply.github.com)
Date: 2026-06-15T00:04:43+02:00

Commit Message:
webOS: add 64-bit compile

Changed paths:
    backends/platform/libretro/Makefile


diff --git a/backends/platform/libretro/Makefile b/backends/platform/libretro/Makefile
index 2b5b8f7be5b..c0ac403b63a 100644
--- a/backends/platform/libretro/Makefile
+++ b/backends/platform/libretro/Makefile
@@ -88,15 +88,21 @@ else ifeq ($(platform), rpi4_64)
    HAVE_OPENGLES2 := 1
    HAVE_NEON := 1
 
-# webOS (32 bit)
+# webOS (32/64 bit)
 else ifeq ($(platform), webos)
    TARGET = $(TARGET_NAME)_libretro.so
-   DEFINES += -fPIC -D_ARM_ASSEM_ -DUSE_CXX11 -marm -DARM
+   DEFINES += -fPIC -D_ARM_ASSEM_ -DUSE_CXX11 -DARM
    LDFLAGS += -shared -Wl,--version-script=$(ROOT_PATH)/link.T -fPIC -static-libgcc -static-libstdc++
-   CFLAGS += -fPIC -mcpu=cortex-a9 -mtune=cortex-a53 -mfloat-abi=softfp -fomit-frame-pointer -ffast-math
+   CFLAGS += -fPIC -fomit-frame-pointer -ffast-math
    CXXFLAGS = $(CFLAGS) -frtti -std=c++11
    HAVE_OPENGLES2 := 1
    HAVE_NEON := 1
+   ifneq (,$(findstring aarch64,$(CROSS_COMPILE)))
+      CFLAGS += -mcpu=cortex-a53 -mtune=cortex-a76
+   else
+      DEFINES += -marm
+      CFLAGS += -mcpu=cortex-a9 -mtune=cortex-a53 -mfloat-abi=softfp
+   endif
 
 # iOS
 else ifneq (,$(findstring ios,$(platform)))



From noreply at scummvm.org  Sun Jun 14 22:15:10 2026
From: noreply at scummvm.org (sev-)
Date: Sun, 14 Jun 2026 22:15:10 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 7c2ef344d817f37c2ae5331ac46540dd75bb66bc
Message-ID: <20260614221510.D0FAB7005CC@ron.scummvm.net>

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

Summary:
7c2ef344d8 AUDIO: Separate generating sine waves from the PC Speaker code


Commit: 7c2ef344d817f37c2ae5331ac46540dd75bb66bc
    https://github.com/scummvm/scummvm/commit/7c2ef344d817f37c2ae5331ac46540dd75bb66bc
Author: Cameron Cawley (ccawley2011 at gmail.com)
Date: 2026-06-15T00:15:07+02:00

Commit Message:
AUDIO: Separate generating sine waves from the PC Speaker code

Changed paths:
  A audio/sine.h
    audio/softsynth/pcspk.cpp
    audio/softsynth/pcspk.h
    engines/made/scriptfuncs.cpp
    engines/made/scriptfuncs.h
    engines/testbed/sound.cpp


diff --git a/audio/sine.h b/audio/sine.h
new file mode 100644
index 00000000000..2134fc696e6
--- /dev/null
+++ b/audio/sine.h
@@ -0,0 +1,72 @@
+/* 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 AUDIO_SINE_H
+#define AUDIO_SINE_H
+
+#include "audio/audiostream.h"
+#include "common/util.h"
+
+namespace Audio {
+
+class SineStream : public AudioStream {
+public:
+	SineStream(int freq, int rate = 44100) {
+		_rate = rate;
+		_oscLength = rate / freq;
+		_oscSamples = 0;
+		_volume = 20; // The maximum volume is 255
+	}
+
+
+	int readBuffer(int16 *buffer, const int numSamples) override {
+		for (int i = 0; i < numSamples; i++) {
+			buffer[i] = generateSine(_oscSamples, _oscLength) * _volume;
+			if (_oscSamples++ >= _oscLength)
+				_oscSamples = 0;
+		}
+
+		return numSamples;
+	}
+
+	bool isStereo() const override { return false; }
+	bool endOfData() const override { return false; }
+	bool endOfStream() const override { return false; }
+	int getRate() const override { return _rate; }
+
+protected:
+	int _rate;
+	uint32 _oscLength;
+	uint32 _oscSamples;
+	byte _volume;
+
+	int8 generateSine(uint32 x, uint32 oscLength) const {
+		if (oscLength == 0)
+			return 0;
+
+		// TODO: Maybe using a look-up-table would be better?
+		return CLIP<int16>((int16) (128 * sin(2.0 * M_PI * x / oscLength)), -128, 127);
+	}
+};
+
+}
+
+#endif
diff --git a/audio/softsynth/pcspk.cpp b/audio/softsynth/pcspk.cpp
index c356eabf083..fd74b09e65e 100644
--- a/audio/softsynth/pcspk.cpp
+++ b/audio/softsynth/pcspk.cpp
@@ -31,9 +31,7 @@ PCSpeakerStream::Command::Command(PCSpeaker::WaveForm aWaveForm, float aFrequenc
 	waveForm(aWaveForm), frequency(aFrequency), length(aLength) { }
 
 const PCSpeakerStream::generatorFunc PCSpeakerStream::generateWave[] =
-	{&PCSpeakerStream::generateSquare, &PCSpeakerStream::generateSine,
-	 &PCSpeakerStream::generateSaw,    &PCSpeakerStream::generateTriangle,
-	 &PCSpeakerStream::generateSilence};
+	{&PCSpeakerStream::generateSquare, &PCSpeakerStream::generateSilence};
 
 PCSpeakerStream::PCSpeakerStream(int rate) {
 	_rate = rate;
@@ -55,7 +53,7 @@ PCSpeakerStream::~PCSpeakerStream() {
 void PCSpeakerStream::play(PCSpeaker::WaveForm wave, int freq, int32 length) {
 	Common::StackLock lock(_mutex);
 
-	assert((wave >= PCSpeaker::kWaveFormSquare) && (wave <= PCSpeaker::kWaveFormTriangle));
+	assert(wave == PCSpeaker::kWaveFormSquare);
 
 	if (_commandActive || !_commandQueue->empty())
 		// Currently playing back a queued instruction. Stop playback and clear
@@ -158,30 +156,6 @@ int8 PCSpeakerStream::generateSquare(uint32 x, uint32 oscLength) {
 	return (x < (oscLength / 2)) ? 127 : -128;
 }
 
-int8 PCSpeakerStream::generateSine(uint32 x, uint32 oscLength) {
-	if (oscLength == 0)
-		return 0;
-
-	// TODO: Maybe using a look-up-table would be better?
-	return CLIP<int16>((int16) (128 * sin(2.0 * M_PI * x / oscLength)), -128, 127);
-}
-
-int8 PCSpeakerStream::generateSaw(uint32 x, uint32 oscLength) {
-	if (oscLength == 0)
-		return 0;
-
-	return ((x * (65536 / oscLength)) >> 8) - 128;
-}
-
-int8 PCSpeakerStream::generateTriangle(uint32 x, uint32 oscLength) {
-	if (oscLength == 0)
-		return 0;
-
-	int y = ((x * (65536 / (oscLength / 2))) >> 8) - 128;
-
-	return (x <= (oscLength / 2)) ? y : (256 - y);
-}
-
 int8 PCSpeakerStream::generateSilence(uint32 x, uint32 oscLength) {
 	return 0;
 }
diff --git a/audio/softsynth/pcspk.h b/audio/softsynth/pcspk.h
index 9cdd3ae0b5d..88f5ddb0fdb 100644
--- a/audio/softsynth/pcspk.h
+++ b/audio/softsynth/pcspk.h
@@ -35,9 +35,6 @@ class PCSpeaker {
 public:
 	enum WaveForm {
 		kWaveFormSquare = 0,
-		kWaveFormSine,
-		kWaveFormSaw,
-		kWaveFormTriangle,
 		kWaveFormSilence
 	};
 
diff --git a/engines/made/scriptfuncs.cpp b/engines/made/scriptfuncs.cpp
index 5a7bec868e8..238a409b39b 100644
--- a/engines/made/scriptfuncs.cpp
+++ b/engines/made/scriptfuncs.cpp
@@ -26,7 +26,7 @@
 #include "made/database.h"
 #include "made/pmvplayer.h"
 
-#include "audio/softsynth/pcspk.h"
+#include "audio/sine.h"
 
 #include "backends/audiocd/audiocd.h"
 
@@ -39,11 +39,6 @@
 namespace Made {
 
 ScriptFunctions::ScriptFunctions(MadeEngine *vm) : _vm(vm), _soundStarted(false), _gameAudioVolume(Audio::Mixer::kMaxChannelVolume) {
-	// Initialize the two tone generators
-	_pcSpeaker1 = new Audio::PCSpeaker();
-	_pcSpeaker2 = new Audio::PCSpeaker();
-	_pcSpeaker1->init();
-	_pcSpeaker2->init();
 	_soundResource = nullptr;
 	_soundWasPlaying = false;
 }
@@ -51,9 +46,6 @@ ScriptFunctions::ScriptFunctions(MadeEngine *vm) : _vm(vm), _soundStarted(false)
 ScriptFunctions::~ScriptFunctions() {
 	for (uint i = 0; i < _externalFuncs.size(); ++i)
 		delete _externalFuncs[i];
-
-	delete _pcSpeaker1;
-	delete _pcSpeaker2;
 }
 
 typedef Common::Functor2Mem<int16, int16*, int16, ScriptFunctions> ExternalScriptFunc;
@@ -338,10 +330,14 @@ int16 ScriptFunctions::sfPlayNote(int16 argc, int16 *argv) {
 
 	debug(4, "sfPlayNote: Note = %d, Volume(?) = %d", argv[0] - 1, argv[1]);
 
-	_pcSpeaker1->play(Audio::PCSpeaker::kWaveFormSine, freqTable[argv[0] - 1], -1);
+	_vm->_mixer->stopHandle(_sine1);
+
+	Audio::AudioStream *sine = new Audio::SineStream(freqTable[argv[0] - 1], _vm->_mixer->getOutputRate());
+
+	_vm->_mixer->playStream(Audio::Mixer::kSFXSoundType, &_sine1, sine);
 
 	// TODO: Figure out what to do with the second parameter
-	//_pcSpeaker1->setVolume(argv[1]);
+	//_pcSpeaker1->setChannelVolume(_sine1, argv[1]);
 
 	return 0;
 }
@@ -349,7 +345,7 @@ int16 ScriptFunctions::sfPlayNote(int16 argc, int16 *argv) {
 int16 ScriptFunctions::sfStopNote(int16 argc, int16 *argv) {
 	// Used in the same place as sfPlayNote, with the same parameters
 	// We just stop the wave generator here
-	_pcSpeaker1->stop();
+	_vm->_mixer->stopHandle(_sine1);
 	return 0;
 }
 
@@ -375,16 +371,23 @@ int16 ScriptFunctions::sfPlayTele(int16 argc, int16 *argv) {
 
 	debug(4, "sfPlayTele: Button = %d", argv[0]);
 
-	_pcSpeaker1->play(Audio::PCSpeaker::kWaveFormSine, freqTable1[argv[0]], -1);
-	_pcSpeaker2->play(Audio::PCSpeaker::kWaveFormSine, freqTable2[argv[0]], -1);
+	_vm->_mixer->stopHandle(_sine1);
+	_vm->_mixer->stopHandle(_sine2);
+
+	Audio::AudioStream *sine1 = new Audio::SineStream(freqTable1[argv[0]], _vm->_mixer->getOutputRate());
+	Audio::AudioStream *sine2 = new Audio::SineStream(freqTable2[argv[0]], _vm->_mixer->getOutputRate());
+
+	_vm->_mixer->playStream(Audio::Mixer::kSFXSoundType, &_sine1, sine1);
+	_vm->_mixer->playStream(Audio::Mixer::kSFXSoundType, &_sine2, sine2);
+
 	return 0;
 }
 
 int16 ScriptFunctions::sfStopTele(int16 argc, int16 *argv) {
 	// Used in the same place as sfPlayTele, with the same parameters
 	// We just stop both wave generators here
-	_pcSpeaker1->stop();
-	_pcSpeaker2->stop();
+	_vm->_mixer->stopHandle(_sine1);
+	_vm->_mixer->stopHandle(_sine2);
 	return 0;
 }
 
diff --git a/engines/made/scriptfuncs.h b/engines/made/scriptfuncs.h
index db6910e4d9d..b39a703d318 100644
--- a/engines/made/scriptfuncs.h
+++ b/engines/made/scriptfuncs.h
@@ -29,10 +29,6 @@
 #include "common/debug.h"
 #include "common/system.h"
 
-namespace Audio {
-class PCSpeaker;
-}
-
 namespace Made {
 
 class MadeEngine;
@@ -70,7 +66,7 @@ protected:
 	uint8 _gameAudioVolume;
 
 	// PlayNote/StopNote and PlayTele/StopTele wave generators
-	Audio::PCSpeaker *_pcSpeaker1, *_pcSpeaker2;
+	Audio::SoundHandle _sine1, _sine2;
 
 	Common::Array<const ExternalFunc *> _externalFuncs;
 	Common::Array<const char *> _externalFuncNames;
diff --git a/engines/testbed/sound.cpp b/engines/testbed/sound.cpp
index 7daa7e1b9da..4800cef31a8 100644
--- a/engines/testbed/sound.cpp
+++ b/engines/testbed/sound.cpp
@@ -19,7 +19,8 @@
  *
  */
 
-#include "audio/softsynth/pcspk.h"
+#include "audio/audiostream.h"
+#include "audio/sine.h"
 #include "audio/mods/mod_xm_s3m.h"
 #include "audio/mods/universaltracker.h"
 
@@ -55,14 +56,12 @@ SoundSubsystemDialog::SoundSubsystemDialog() : TestbedInteractionDialog(80, 60,
 
 	_mixer = g_system->getMixer();
 
-	// the three streams to be mixed
-	Audio::PCSpeakerStream *s1 = new Audio::PCSpeakerStream();
-	Audio::PCSpeakerStream *s2 = new Audio::PCSpeakerStream();
-	Audio::PCSpeakerStream *s3 = new Audio::PCSpeakerStream();
+	uint rate = _mixer->getOutputRate();
 
-	s1->play(Audio::PCSpeaker::kWaveFormSine, 1000, -1);
-	s2->play(Audio::PCSpeaker::kWaveFormSine, 1200, -1);
-	s3->play(Audio::PCSpeaker::kWaveFormSine, 1400, -1);
+	// the three streams to be mixed
+	Audio::AudioStream *s1 = new Audio::SineStream(1000, rate);
+	Audio::AudioStream *s2 = new Audio::SineStream(1200, rate);
+	Audio::AudioStream *s3 = new Audio::SineStream(1400, rate);
 
 	_mixer->playStream(Audio::Mixer::kPlainSoundType, &_h1, s1);
 	_mixer->pauseHandle(_h1, true);
@@ -126,17 +125,18 @@ TestExitStatus SoundSubsystem::playBeeps() {
 		return kTestSkipped;
 	}
 
-	Audio::PCSpeakerStream *speaker = new Audio::PCSpeakerStream();
 	Audio::Mixer *mixer = g_system->getMixer();
+	uint rate = mixer->getOutputRate();
+	Audio::AudioStream *stream;
 	Audio::SoundHandle handle;
-	mixer->playStream(Audio::Mixer::kPlainSoundType, &handle, speaker);
 
 	// Left Beep
 	Testsuite::writeOnScreen("Left Beep", Common::Point(0, 100));
+	stream = new Audio::SineStream(1000, rate);
+	mixer->playStream(Audio::Mixer::kPlainSoundType, &handle, stream);
 	mixer->setChannelBalance(handle, -127);
-	speaker->play(Audio::PCSpeaker::kWaveFormSine, 1000, -1);
 	g_system->delayMillis(500);
-	mixer->pauseHandle(handle, true);
+	mixer->stopHandle(handle);
 
 	if (Testsuite::handleInteractiveInput("  Were you able to hear the left beep?  ", "Yes", "No", kOptionRight)) {
 		Testsuite::logDetailedPrintf("Error! Left Beep couldn't be detected : Error with Mixer::setChannelBalance()\n");
@@ -145,10 +145,11 @@ TestExitStatus SoundSubsystem::playBeeps() {
 
 	// Right Beep
 	Testsuite::writeOnScreen("Right Beep", Common::Point(0, 100));
+	stream = new Audio::SineStream(1000, rate);
+	mixer->playStream(Audio::Mixer::kPlainSoundType, &handle, stream);
 	mixer->setChannelBalance(handle, 127);
-	mixer->pauseHandle(handle, false);
 	g_system->delayMillis(500);
-	mixer->stopAll();
+	mixer->stopHandle(handle);
 
 	if (Testsuite::handleInteractiveInput("Were you able to hear the right beep?", "Yes", "No", kOptionRight)) {
 		Testsuite::logDetailedPrintf("Error! Right Beep couldn't be detected : Error with Mixer::setChannelBalance()\n");
@@ -160,7 +161,7 @@ TestExitStatus SoundSubsystem::playBeeps() {
 TestExitStatus SoundSubsystem::mixSounds() {
 	Testsuite::clearScreen();
 	TestExitStatus passed = kTestPassed;
-	Common::String info = "Testing Mixer Output by generating multichannel sound output using PC speaker emulator.\n"
+	Common::String info = "Testing Mixer Output by generating multichannel sound output using sine wave forms.\n"
 	"The mixer should be able to play them simultaneously\n";
 
 	if (Testsuite::handleInteractiveInput(info, "OK", "Skip", kOptionRight)) {
@@ -313,16 +314,13 @@ TestExitStatus SoundSubsystem::sampleRates() {
 
 	TestExitStatus passed = kTestPassed;
 	Audio::Mixer *mixer = g_system->getMixer();
+	uint rate = mixer->getOutputRate();
 
-	Audio::PCSpeakerStream *s1 = new Audio::PCSpeakerStream();
+	Audio::AudioStream *s1 = new Audio::SineStream(1000, rate);
 	// Stream at half sampling rate
-	Audio::PCSpeakerStream *s2 = new Audio::PCSpeakerStream(s1->getRate() - 10000);
+	Audio::AudioStream *s2 = new Audio::SineStream(1000, rate - 10000);
 	// Stream at twice sampling rate
-	Audio::PCSpeakerStream *s3 = new Audio::PCSpeakerStream(s1->getRate() + 10000);
-
-	s1->play(Audio::PCSpeaker::kWaveFormSine, 1000, -1);
-	s2->play(Audio::PCSpeaker::kWaveFormSine, 1000, -1);
-	s3->play(Audio::PCSpeaker::kWaveFormSine, 1000, -1);
+	Audio::AudioStream *s3 = new Audio::SineStream(1000, rate + 10000);
 
 	Audio::SoundHandle handle;
 	Common::Point pt(0, 100);



From noreply at scummvm.org  Sun Jun 14 22:15:17 2026
From: noreply at scummvm.org (ScummVM-Translations)
Date: Sun, 14 Jun 2026 22:15:17 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 827acccfc9cd88fc031616937c8113c8207ba712
Message-ID: <20260614221517.2A2AE7005CC@ron.scummvm.net>

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

Summary:
827acccfc9 I18N: Update translations templates


Commit: 827acccfc9cd88fc031616937c8113c8207ba712
    https://github.com/scummvm/scummvm/commit/827acccfc9cd88fc031616937c8113c8207ba712
Author: Weblate (noreply at weblate.org)
Date: 2026-06-14T22:15:12Z

Commit Message:
I18N: Update translations templates

Changed paths:
    po/scummvm.pot


diff --git a/po/scummvm.pot b/po/scummvm.pot
index db3a10793f5..a8a5ad82c88 100644
--- a/po/scummvm.pot
+++ b/po/scummvm.pot
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2026.2.1git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-14 18:41+0000\n"
+"POT-Creation-Date: 2026-06-14 22:15+0000\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL at ADDRESS>\n"
 "Language-Team: LANGUAGE <LL at li.org>\n"
@@ -3858,11 +3858,11 @@ msgstr ""
 msgid "MT-32 emulator"
 msgstr ""
 
-#: audio/softsynth/pcspk.cpp:250
+#: audio/softsynth/pcspk.cpp:224
 msgid "PC Speaker emulator"
 msgstr ""
 
-#: audio/softsynth/pcspk.cpp:269
+#: audio/softsynth/pcspk.cpp:243
 msgid "IBM PCjr emulator"
 msgstr ""
 



From noreply at scummvm.org  Sun Jun 14 22:15:54 2026
From: noreply at scummvm.org (ScummVM-Translations)
Date: Sun, 14 Jun 2026 22:15:54 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 e3fcf0f52b2a27d5c143fcdcd79aea3c162977e5
Message-ID: <20260614221554.695917005CC@ron.scummvm.net>

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

Summary:
e3fcf0f52b I18N: Update translation files


Commit: e3fcf0f52b2a27d5c143fcdcd79aea3c162977e5
    https://github.com/scummvm/scummvm/commit/e3fcf0f52b2a27d5c143fcdcd79aea3c162977e5
Author: Weblate (noreply at weblate.org)
Date: 2026-06-14T22:15:32Z

Commit Message:
I18N: Update translation files

Updated by "Update PO files to match POT (msgmerge)" hook in Weblate.

Translation: ScummVM/scummvm
Translate-URL: https://translations.scummvm.org/projects/scummvm/scummvm/

Changed paths:
    po/ar.po
    po/be-tarask.po
    po/be_BY.po
    po/ca_ES.po
    po/cs_CZ.po
    po/da.po
    po/de_DE.po
    po/el.po
    po/es_ES.po
    po/eu.po
    po/fi_FI.po
    po/fr_FR.po
    po/gl_ES.po
    po/he.po
    po/hi.po
    po/hu_HU.po
    po/it_IT.po
    po/ja.po
    po/ka.po
    po/ko.po
    po/nb_NO.po
    po/nl_NL.po
    po/nn_NO.po
    po/pl_PL.po
    po/pt_BR.po
    po/pt_PT.po
    po/ro.po
    po/ru_RU.po
    po/sv_SE.po
    po/tr.po
    po/uk_UA.po
    po/zh.po
    po/zh_Hans.po
    po/zh_Hant.po


diff --git a/po/ar.po b/po/ar.po
index 9d26847556e..67e31679f61 100644
--- a/po/ar.po
+++ b/po/ar.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-14 18:41+0000\n"
+"POT-Creation-Date: 2026-06-14 22:15+0000\n"
 "PO-Revision-Date: 2026-02-24 22:26+0000\n"
 "Last-Translator: Mohamed Shaaban <mohmedbashamedo at hotmail.com>\n"
 "Language-Team: Arabic <https://translations.scummvm.org/projects/scummvm/"
@@ -4201,11 +4201,11 @@ msgstr "???????? ?????????? ???????????? ???????????? MT-32"
 msgid "MT-32 emulator"
 msgstr "?????????? MT-32"
 
-#: audio/softsynth/pcspk.cpp:250
+#: audio/softsynth/pcspk.cpp:224
 msgid "PC Speaker emulator"
 msgstr "?????????? ???????????? ??????????????????"
 
-#: audio/softsynth/pcspk.cpp:269
+#: audio/softsynth/pcspk.cpp:243
 msgid "IBM PCjr emulator"
 msgstr "?????????? IBM PCjr"
 
diff --git a/po/be-tarask.po b/po/be-tarask.po
index 8d1bbd87697..47c2b64ac86 100644
--- a/po/be-tarask.po
+++ b/po/be-tarask.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-14 18:41+0000\n"
+"POT-Creation-Date: 2026-06-14 22:15+0000\n"
 "PO-Revision-Date: 2023-02-26 15:01+0000\n"
 "Last-Translator: Kastu?? K. <oakum2334 at gmail.com>\n"
 "Language-Team: Belarusian (Tara??kievica) <https://translations.scummvm.org/"
@@ -4162,11 +4162,11 @@ msgstr "?????????????????????????????? ?????????????????? MT-32"
 msgid "MT-32 emulator"
 msgstr "???????????????? MT-32"
 
-#: audio/softsynth/pcspk.cpp:250
+#: audio/softsynth/pcspk.cpp:224
 msgid "PC Speaker emulator"
 msgstr "???????????????? ???????????????? ????"
 
-#: audio/softsynth/pcspk.cpp:269
+#: audio/softsynth/pcspk.cpp:243
 msgid "IBM PCjr emulator"
 msgstr "???????????????? IBM PCjr"
 
diff --git a/po/be_BY.po b/po/be_BY.po
index 3c526d0399c..3981fbed0b9 100644
--- a/po/be_BY.po
+++ b/po/be_BY.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-14 18:41+0000\n"
+"POT-Creation-Date: 2026-06-14 22:15+0000\n"
 "PO-Revision-Date: 2021-03-21 08:24+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Belarusian <https://translations.scummvm.org/projects/scummvm/"
@@ -4164,11 +4164,11 @@ msgstr "?????????????????? ???????????????? MT-32"
 msgid "MT-32 emulator"
 msgstr "???????????????? MT-32"
 
-#: audio/softsynth/pcspk.cpp:250
+#: audio/softsynth/pcspk.cpp:224
 msgid "PC Speaker emulator"
 msgstr "???????????????? ???????????????? ????"
 
-#: audio/softsynth/pcspk.cpp:269
+#: audio/softsynth/pcspk.cpp:243
 msgid "IBM PCjr emulator"
 msgstr "???????????????? IBM PCjr"
 
diff --git a/po/ca_ES.po b/po/ca_ES.po
index 7570b3b7540..d204c12b654 100644
--- a/po/ca_ES.po
+++ b/po/ca_ES.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.6.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-14 18:41+0000\n"
+"POT-Creation-Date: 2026-06-14 22:15+0000\n"
 "PO-Revision-Date: 2026-05-26 04:22+0000\n"
 "Last-Translator: Xaviu <xaviu at xaviu.com>\n"
 "Language-Team: Catalan <https://translations.scummvm.org/projects/scummvm/"
@@ -4085,11 +4085,11 @@ msgstr "Iniciant l'Emulador de MT-32"
 msgid "MT-32 emulator"
 msgstr "Emulador de MT-32"
 
-#: audio/softsynth/pcspk.cpp:250
+#: audio/softsynth/pcspk.cpp:224
 msgid "PC Speaker emulator"
 msgstr "Emulador d'altaveu de PC"
 
-#: audio/softsynth/pcspk.cpp:269
+#: audio/softsynth/pcspk.cpp:243
 msgid "IBM PCjr emulator"
 msgstr "Emulador d'IBM PCjr"
 
diff --git a/po/cs_CZ.po b/po/cs_CZ.po
index 9b0f2147bad..70b72b04b7f 100644
--- a/po/cs_CZ.po
+++ b/po/cs_CZ.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.7.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-14 18:41+0000\n"
+"POT-Creation-Date: 2026-06-14 22:15+0000\n"
 "PO-Revision-Date: 2026-02-09 20:22+0000\n"
 "Last-Translator: AsciiWolf <mail at asciiwolf.com>\n"
 "Language-Team: Czech <https://translations.scummvm.org/projects/scummvm/"
@@ -4151,11 +4151,11 @@ msgstr "Zav??d??m MT-32 Emul??tor"
 msgid "MT-32 emulator"
 msgstr "MT-32 emul??tor"
 
-#: audio/softsynth/pcspk.cpp:250
+#: audio/softsynth/pcspk.cpp:224
 msgid "PC Speaker emulator"
 msgstr "PC Speaker emul??tor"
 
-#: audio/softsynth/pcspk.cpp:269
+#: audio/softsynth/pcspk.cpp:243
 msgid "IBM PCjr emulator"
 msgstr "IBM PCjr emul??tor"
 
diff --git a/po/da.po b/po/da.po
index 42dbb04d6c8..7aa0f4c7827 100644
--- a/po/da.po
+++ b/po/da.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-14 18:41+0000\n"
+"POT-Creation-Date: 2026-06-14 22:15+0000\n"
 "PO-Revision-Date: 2023-09-07 17:04+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Danish <https://translations.scummvm.org/projects/scummvm/"
@@ -4161,11 +4161,11 @@ msgstr "Initialisere MT-32-emulator"
 msgid "MT-32 emulator"
 msgstr "MT-32-emulator"
 
-#: audio/softsynth/pcspk.cpp:250
+#: audio/softsynth/pcspk.cpp:224
 msgid "PC Speaker emulator"
 msgstr "PC h??jttaler-emulator"
 
-#: audio/softsynth/pcspk.cpp:269
+#: audio/softsynth/pcspk.cpp:243
 msgid "IBM PCjr emulator"
 msgstr "IBM PCjr-emulator"
 
diff --git a/po/de_DE.po b/po/de_DE.po
index 2b7e710e576..a0701f68951 100644
--- a/po/de_DE.po
+++ b/po/de_DE.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.10.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-14 18:41+0000\n"
+"POT-Creation-Date: 2026-06-14 22:15+0000\n"
 "PO-Revision-Date: 2026-05-05 16:31+0000\n"
 "Last-Translator: Lothar Serra Mari <lothar.serramari at scummvm.org>\n"
 "Language-Team: German <https://translations.scummvm.org/projects/scummvm/"
@@ -4217,11 +4217,11 @@ msgstr "Initialisiere MT-32-Emulator"
 msgid "MT-32 emulator"
 msgstr "MT-32-Emulator"
 
-#: audio/softsynth/pcspk.cpp:250
+#: audio/softsynth/pcspk.cpp:224
 msgid "PC Speaker emulator"
 msgstr "PC-Lautsprecher-Emulator"
 
-#: audio/softsynth/pcspk.cpp:269
+#: audio/softsynth/pcspk.cpp:243
 msgid "IBM PCjr emulator"
 msgstr "IBM PCjr-Emulator"
 
diff --git a/po/el.po b/po/el.po
index 64bb52ffc0b..c96fb83d6fd 100644
--- a/po/el.po
+++ b/po/el.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.10.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-14 18:41+0000\n"
+"POT-Creation-Date: 2026-06-14 22:15+0000\n"
 "PO-Revision-Date: 2026-04-23 00:22+0000\n"
 "Last-Translator: Antoniou Athanasios <a.antoniou79 at gmail.com>\n"
 "Language-Team: Greek <https://translations.scummvm.org/projects/scummvm/"
@@ -4290,11 +4290,11 @@ msgstr "???????????????????????? ?????????????????? MT-32"
 msgid "MT-32 emulator"
 msgstr "???????????????????? MT-32"
 
-#: audio/softsynth/pcspk.cpp:250
+#: audio/softsynth/pcspk.cpp:224
 msgid "PC Speaker emulator"
 msgstr "???????????????????? PC Speaker"
 
-#: audio/softsynth/pcspk.cpp:269
+#: audio/softsynth/pcspk.cpp:243
 msgid "IBM PCjr emulator"
 msgstr "???????????????????? IBM PCjr"
 
diff --git a/po/es_ES.po b/po/es_ES.po
index 81e4ae0d4ee..dfd79ec4e7a 100644
--- a/po/es_ES.po
+++ b/po/es_ES.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.4.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-14 18:41+0000\n"
+"POT-Creation-Date: 2026-06-14 22:15+0000\n"
 "PO-Revision-Date: 2026-06-14 09:53+0000\n"
 "Last-Translator: IlDucci <pajaroloco_2 at hotmail.com>\n"
 "Language-Team: Spanish <https://translations.scummvm.org/projects/scummvm/"
@@ -4214,11 +4214,11 @@ msgstr "Iniciando el emulador de MT-32"
 msgid "MT-32 emulator"
 msgstr "Emulaci??n de MT-32"
 
-#: audio/softsynth/pcspk.cpp:250
+#: audio/softsynth/pcspk.cpp:224
 msgid "PC Speaker emulator"
 msgstr "Emulaci??n del altavoz de PC"
 
-#: audio/softsynth/pcspk.cpp:269
+#: audio/softsynth/pcspk.cpp:243
 msgid "IBM PCjr emulator"
 msgstr "Emulaci??n de IBM PCjr"
 
diff --git a/po/eu.po b/po/eu.po
index 7c9b9440f54..cd153a56fc1 100644
--- a/po/eu.po
+++ b/po/eu.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.5.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-14 18:41+0000\n"
+"POT-Creation-Date: 2026-06-14 22:15+0000\n"
 "PO-Revision-Date: 2024-02-05 18:42+0000\n"
 "Last-Translator: Josu Igoa <josuigoa at ni.eus>\n"
 "Language-Team: Basque <https://translations.scummvm.org/projects/scummvm/"
@@ -4196,12 +4196,12 @@ msgstr "MT-32 emuladorea hasieratzen"
 msgid "MT-32 emulator"
 msgstr "MT-32 emuladorea"
 
-#: audio/softsynth/pcspk.cpp:250
+#: audio/softsynth/pcspk.cpp:224
 #, fuzzy
 msgid "PC Speaker emulator"
 msgstr "PC bozgoragailuaren emuladorea"
 
-#: audio/softsynth/pcspk.cpp:269
+#: audio/softsynth/pcspk.cpp:243
 #, fuzzy
 msgid "IBM PCjr emulator"
 msgstr "IBM PCjr emuladorea"
diff --git a/po/fi_FI.po b/po/fi_FI.po
index 6bb351f6ab4..35c983f821c 100644
--- a/po/fi_FI.po
+++ b/po/fi_FI.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.6.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-14 18:41+0000\n"
+"POT-Creation-Date: 2026-06-14 22:15+0000\n"
 "PO-Revision-Date: 2026-02-07 15:22+0000\n"
 "Last-Translator: Timo Mikkolainen <tmikkola at gmail.com>\n"
 "Language-Team: Finnish <https://translations.scummvm.org/projects/scummvm/"
@@ -4239,11 +4239,11 @@ msgstr "Alustetaan MT-32 emulaattoria"
 msgid "MT-32 emulator"
 msgstr "MT-32 emulaattori"
 
-#: audio/softsynth/pcspk.cpp:250
+#: audio/softsynth/pcspk.cpp:224
 msgid "PC Speaker emulator"
 msgstr "PC kaiuttimen emulaattori"
 
-#: audio/softsynth/pcspk.cpp:269
+#: audio/softsynth/pcspk.cpp:243
 msgid "IBM PCjr emulator"
 msgstr "IBM PCjr emulaattori"
 
diff --git a/po/fr_FR.po b/po/fr_FR.po
index e2b478f04c6..a7d7505dbd8 100644
--- a/po/fr_FR.po
+++ b/po/fr_FR.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-14 18:41+0000\n"
+"POT-Creation-Date: 2026-06-14 22:15+0000\n"
 "PO-Revision-Date: 2026-06-14 09:41+0000\n"
 "Last-Translator: Purple T <ZEONK at hotmail.com>\n"
 "Language-Team: French <https://translations.scummvm.org/projects/scummvm/"
@@ -4237,11 +4237,11 @@ msgstr "Initialisation de l'??mulateur MT-32"
 msgid "MT-32 emulator"
 msgstr "??mulateur MT-32"
 
-#: audio/softsynth/pcspk.cpp:250
+#: audio/softsynth/pcspk.cpp:224
 msgid "PC Speaker emulator"
 msgstr "??mulateur Haut Parleur PC"
 
-#: audio/softsynth/pcspk.cpp:269
+#: audio/softsynth/pcspk.cpp:243
 msgid "IBM PCjr emulator"
 msgstr "??mulateur IBM PCjr"
 
diff --git a/po/gl_ES.po b/po/gl_ES.po
index 006236a1a7a..1cc033c31a3 100644
--- a/po/gl_ES.po
+++ b/po/gl_ES.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-14 18:41+0000\n"
+"POT-Creation-Date: 2026-06-14 22:15+0000\n"
 "PO-Revision-Date: 2020-09-26 21:13+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Galician <https://translations.scummvm.org/projects/scummvm/"
@@ -4154,12 +4154,12 @@ msgstr "Iniciando emulador de MT-32"
 msgid "MT-32 emulator"
 msgstr "Emulador de MT-32"
 
-#: audio/softsynth/pcspk.cpp:250
+#: audio/softsynth/pcspk.cpp:224
 #, fuzzy
 msgid "PC Speaker emulator"
 msgstr "Emulador de altofalante de PC"
 
-#: audio/softsynth/pcspk.cpp:269
+#: audio/softsynth/pcspk.cpp:243
 #, fuzzy
 msgid "IBM PCjr emulator"
 msgstr "Emulador de IBM PCjr"
diff --git a/po/he.po b/po/he.po
index 3b7433e729f..6bd19dcdd93 100644
--- a/po/he.po
+++ b/po/he.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.1.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-14 18:41+0000\n"
+"POT-Creation-Date: 2026-06-14 22:15+0000\n"
 "PO-Revision-Date: 2025-04-09 20:18+0000\n"
 "Last-Translator: Niv Baehr <bloop93 at gmail.com>\n"
 "Language-Team: Hebrew <https://translations.scummvm.org/projects/scummvm/"
@@ -4088,11 +4088,11 @@ msgstr "?????????? ?????????????? MT-32"
 msgid "MT-32 emulator"
 msgstr "?????????????? MT-32"
 
-#: audio/softsynth/pcspk.cpp:250
+#: audio/softsynth/pcspk.cpp:224
 msgid "PC Speaker emulator"
 msgstr "?????????????? PC Speaker"
 
-#: audio/softsynth/pcspk.cpp:269
+#: audio/softsynth/pcspk.cpp:243
 msgid "IBM PCjr emulator"
 msgstr "?????????????? IBM PCjr"
 
diff --git a/po/hi.po b/po/hi.po
index e74f55d3f4b..d7bb67ed7dd 100644
--- a/po/hi.po
+++ b/po/hi.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-14 18:41+0000\n"
+"POT-Creation-Date: 2026-06-14 22:15+0000\n"
 "PO-Revision-Date: 2026-05-12 22:22+0000\n"
 "Last-Translator: KARTIK SAXENA <kartiksaxena2005 at gmail.com>\n"
 "Language-Team: Hindi <https://translations.scummvm.org/projects/scummvm/"
@@ -4011,11 +4011,11 @@ msgstr ""
 msgid "MT-32 emulator"
 msgstr ""
 
-#: audio/softsynth/pcspk.cpp:250
+#: audio/softsynth/pcspk.cpp:224
 msgid "PC Speaker emulator"
 msgstr ""
 
-#: audio/softsynth/pcspk.cpp:269
+#: audio/softsynth/pcspk.cpp:243
 msgid "IBM PCjr emulator"
 msgstr ""
 
diff --git a/po/hu_HU.po b/po/hu_HU.po
index 8e2b4d8fb24..2b6529b6778 100644
--- a/po/hu_HU.po
+++ b/po/hu_HU.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-14 18:41+0000\n"
+"POT-Creation-Date: 2026-06-14 22:15+0000\n"
 "PO-Revision-Date: 2025-03-07 00:18+0000\n"
 "Last-Translator: Hov??nszki Tam??s <gahex68379 at lofiey.com>\n"
 "Language-Team: Hungarian <https://translations.scummvm.org/projects/scummvm/"
@@ -4255,11 +4255,11 @@ msgstr "MT-32 Emul??tor beind??t??sa"
 msgid "MT-32 emulator"
 msgstr "MT-32 emul??tor"
 
-#: audio/softsynth/pcspk.cpp:250
+#: audio/softsynth/pcspk.cpp:224
 msgid "PC Speaker emulator"
 msgstr "PC Speaker emul??tor"
 
-#: audio/softsynth/pcspk.cpp:269
+#: audio/softsynth/pcspk.cpp:243
 msgid "IBM PCjr emulator"
 msgstr "IBM PCjr emul??tor"
 
diff --git a/po/it_IT.po b/po/it_IT.po
index 54d15a948ab..6333fe9d31a 100644
--- a/po/it_IT.po
+++ b/po/it_IT.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-14 18:41+0000\n"
+"POT-Creation-Date: 2026-06-14 22:15+0000\n"
 "PO-Revision-Date: 2026-06-14 09:41+0000\n"
 "Last-Translator: Walter Agazzi <walter.agazzi at protonmail.com>\n"
 "Language-Team: Italian <https://translations.scummvm.org/projects/scummvm/"
@@ -4204,11 +4204,11 @@ msgstr "Avvio in corso dell'emulatore MT-32"
 msgid "MT-32 emulator"
 msgstr "Emulatore MT-32"
 
-#: audio/softsynth/pcspk.cpp:250
+#: audio/softsynth/pcspk.cpp:224
 msgid "PC Speaker emulator"
 msgstr "Emulatore altoparlante PC"
 
-#: audio/softsynth/pcspk.cpp:269
+#: audio/softsynth/pcspk.cpp:243
 msgid "IBM PCjr emulator"
 msgstr "Emulatore IBM PCjr"
 
diff --git a/po/ja.po b/po/ja.po
index 4baeed986fd..b52f0fcd7d4 100644
--- a/po/ja.po
+++ b/po/ja.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-14 18:41+0000\n"
+"POT-Creation-Date: 2026-06-14 22:15+0000\n"
 "PO-Revision-Date: 2026-06-04 07:22+0000\n"
 "Last-Translator: VAN-Gluon <van.gluonp at gmail.com>\n"
 "Language-Team: Japanese <https://translations.scummvm.org/projects/scummvm/"
@@ -4070,11 +4070,11 @@ msgstr "MT-32??????????????????????????????"
 msgid "MT-32 emulator"
 msgstr "MT-32?????????????????????"
 
-#: audio/softsynth/pcspk.cpp:250
+#: audio/softsynth/pcspk.cpp:224
 msgid "PC Speaker emulator"
 msgstr "PC ????????????????????????????????????"
 
-#: audio/softsynth/pcspk.cpp:269
+#: audio/softsynth/pcspk.cpp:243
 msgid "IBM PCjr emulator"
 msgstr "IBM PCjr ?????????????????????"
 
diff --git a/po/ka.po b/po/ka.po
index a8b2f4f5a64..d236afac818 100644
--- a/po/ka.po
+++ b/po/ka.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-14 18:41+0000\n"
+"POT-Creation-Date: 2026-06-14 22:15+0000\n"
 "PO-Revision-Date: 2026-06-05 10:22+0000\n"
 "Last-Translator: NorwayFun <temuri.doghonadze at gmail.com>\n"
 "Language-Team: Georgian <https://translations.scummvm.org/projects/scummvm/"
@@ -3877,11 +3877,11 @@ msgstr "MT-32-?????? ?????????????????????????????? ???????????????????????????
 msgid "MT-32 emulator"
 msgstr "MT-32-?????? ???????????????????????????"
 
-#: audio/softsynth/pcspk.cpp:250
+#: audio/softsynth/pcspk.cpp:224
 msgid "PC Speaker emulator"
 msgstr "????????????????????????????????? ??????????????????????????? ???????????????????????????"
 
-#: audio/softsynth/pcspk.cpp:269
+#: audio/softsynth/pcspk.cpp:243
 msgid "IBM PCjr emulator"
 msgstr "IBM PCjr-?????? ???????????????????????????"
 
diff --git a/po/ko.po b/po/ko.po
index e1585fba45e..0efb481a84f 100644
--- a/po/ko.po
+++ b/po/ko.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-14 18:41+0000\n"
+"POT-Creation-Date: 2026-06-14 22:15+0000\n"
 "PO-Revision-Date: 2026-05-19 20:22+0000\n"
 "Last-Translator: Hoseok Seo <ddinghoya at gmail.com>\n"
 "Language-Team: Korean <https://translations.scummvm.org/projects/scummvm/"
@@ -4124,11 +4124,11 @@ msgstr "MT-32 ??????????????? ????????? ???"
 msgid "MT-32 emulator"
 msgstr "MT-32 ???????????????"
 
-#: audio/softsynth/pcspk.cpp:250
+#: audio/softsynth/pcspk.cpp:224
 msgid "PC Speaker emulator"
 msgstr "PC ????????? ???????????????"
 
-#: audio/softsynth/pcspk.cpp:269
+#: audio/softsynth/pcspk.cpp:243
 msgid "IBM PCjr emulator"
 msgstr "IBM PCjr ???????????????"
 
diff --git a/po/nb_NO.po b/po/nb_NO.po
index 682a2db8bd1..6444b78701c 100644
--- a/po/nb_NO.po
+++ b/po/nb_NO.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-14 18:41+0000\n"
+"POT-Creation-Date: 2026-06-14 22:15+0000\n"
 "PO-Revision-Date: 2026-02-07 15:22+0000\n"
 "Last-Translator: Einar Johan Tr??an S??m??en <einarjohants at gmail.com>\n"
 "Language-Team: Norwegian Bokm??l <https://translations.scummvm.org/projects/"
@@ -4257,11 +4257,11 @@ msgstr "Initialiserer MT-32-Emulator"
 msgid "MT-32 emulator"
 msgstr "MT-32-emulator"
 
-#: audio/softsynth/pcspk.cpp:250
+#: audio/softsynth/pcspk.cpp:224
 msgid "PC Speaker emulator"
 msgstr "PC-speaker-emulator"
 
-#: audio/softsynth/pcspk.cpp:269
+#: audio/softsynth/pcspk.cpp:243
 msgid "IBM PCjr emulator"
 msgstr "IBM PCjr-emulator"
 
diff --git a/po/nl_NL.po b/po/nl_NL.po
index d62530323f5..e24b902e681 100644
--- a/po/nl_NL.po
+++ b/po/nl_NL.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.9.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-14 18:41+0000\n"
+"POT-Creation-Date: 2026-06-14 22:15+0000\n"
 "PO-Revision-Date: 2026-02-11 14:23+0000\n"
 "Last-Translator: SecularSteve <fairfull.playing at gmail.com>\n"
 "Language-Team: Dutch <https://translations.scummvm.org/projects/scummvm/"
@@ -4183,11 +4183,11 @@ msgstr "MT-32 Emulator initialiseren"
 msgid "MT-32 emulator"
 msgstr "MT-32 emulator"
 
-#: audio/softsynth/pcspk.cpp:250
+#: audio/softsynth/pcspk.cpp:224
 msgid "PC Speaker emulator"
 msgstr "PC Speaker emulator"
 
-#: audio/softsynth/pcspk.cpp:269
+#: audio/softsynth/pcspk.cpp:243
 msgid "IBM PCjr emulator"
 msgstr "IBM PCjr emulator"
 
diff --git a/po/nn_NO.po b/po/nn_NO.po
index b796e548b76..e182a390590 100644
--- a/po/nn_NO.po
+++ b/po/nn_NO.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-14 18:41+0000\n"
+"POT-Creation-Date: 2026-06-14 22:15+0000\n"
 "PO-Revision-Date: 2023-12-14 14:28+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Norwegian Nynorsk <https://translations.scummvm.org/projects/"
@@ -4118,12 +4118,12 @@ msgstr "Initialiserar MT-32 Emulator"
 msgid "MT-32 emulator"
 msgstr "MT-32 Emulator"
 
-#: audio/softsynth/pcspk.cpp:250
+#: audio/softsynth/pcspk.cpp:224
 #, fuzzy
 msgid "PC Speaker emulator"
 msgstr "PC Speaker Emulator"
 
-#: audio/softsynth/pcspk.cpp:269
+#: audio/softsynth/pcspk.cpp:243
 #, fuzzy
 msgid "IBM PCjr emulator"
 msgstr "IBM PCjr Emulator"
diff --git a/po/pl_PL.po b/po/pl_PL.po
index 07a79411249..a0a8f7c906f 100644
--- a/po/pl_PL.po
+++ b/po/pl_PL.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-14 18:41+0000\n"
+"POT-Creation-Date: 2026-06-14 22:15+0000\n"
 "PO-Revision-Date: 2026-04-17 00:22+0000\n"
 "Last-Translator: SkiffPL <skiff at windowslive.com>\n"
 "Language-Team: Polish <https://translations.scummvm.org/projects/scummvm/"
@@ -4175,11 +4175,11 @@ msgstr "Inicjalizacja emulatora MT-32"
 msgid "MT-32 emulator"
 msgstr "Emulator MT-32"
 
-#: audio/softsynth/pcspk.cpp:250
+#: audio/softsynth/pcspk.cpp:224
 msgid "PC Speaker emulator"
 msgstr "Emulator g??o??nika PC"
 
-#: audio/softsynth/pcspk.cpp:269
+#: audio/softsynth/pcspk.cpp:243
 msgid "IBM PCjr emulator"
 msgstr "Emulator IBM PCjr"
 
diff --git a/po/pt_BR.po b/po/pt_BR.po
index aec48ccc5bb..a08185b8ffe 100644
--- a/po/pt_BR.po
+++ b/po/pt_BR.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-14 18:41+0000\n"
+"POT-Creation-Date: 2026-06-14 22:15+0000\n"
 "PO-Revision-Date: 2024-11-16 02:13+0000\n"
 "Last-Translator: Marcel Souza Lemes <marcosoutsider at gmail.com>\n"
 "Language-Team: Portuguese (Brazil) <https://translations.scummvm.org/"
@@ -4272,11 +4272,11 @@ msgstr "Inicializando Emulador de MT-32"
 msgid "MT-32 emulator"
 msgstr "Emulador de MT-32"
 
-#: audio/softsynth/pcspk.cpp:250
+#: audio/softsynth/pcspk.cpp:224
 msgid "PC Speaker emulator"
 msgstr "Emulador de PC Speaker"
 
-#: audio/softsynth/pcspk.cpp:269
+#: audio/softsynth/pcspk.cpp:243
 msgid "IBM PCjr emulator"
 msgstr "Emulador de IBM PCjr"
 
diff --git a/po/pt_PT.po b/po/pt_PT.po
index 67a746c6d3b..66fc16db4e6 100644
--- a/po/pt_PT.po
+++ b/po/pt_PT.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.10.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-14 18:41+0000\n"
+"POT-Creation-Date: 2026-06-14 22:15+0000\n"
 "PO-Revision-Date: 2022-06-05 14:09+0000\n"
 "Last-Translator: Daniel <supsuper at gmail.com>\n"
 "Language-Team: Portuguese (Portugal) <https://translations.scummvm.org/"
@@ -4144,11 +4144,11 @@ msgstr "A inicializar o Emulador MT-32"
 msgid "MT-32 emulator"
 msgstr "Emulador MT-32"
 
-#: audio/softsynth/pcspk.cpp:250
+#: audio/softsynth/pcspk.cpp:224
 msgid "PC Speaker emulator"
 msgstr "Emulador PC Speaker"
 
-#: audio/softsynth/pcspk.cpp:269
+#: audio/softsynth/pcspk.cpp:243
 msgid "IBM PCjr emulator"
 msgstr "Emulador IBM PCjr"
 
diff --git a/po/ro.po b/po/ro.po
index 9e516a8147e..6ebd335643a 100644
--- a/po/ro.po
+++ b/po/ro.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.10.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-14 18:41+0000\n"
+"POT-Creation-Date: 2026-06-14 22:15+0000\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: Automatically generated\n"
 "Language-Team: none\n"
@@ -3857,11 +3857,11 @@ msgstr ""
 msgid "MT-32 emulator"
 msgstr ""
 
-#: audio/softsynth/pcspk.cpp:250
+#: audio/softsynth/pcspk.cpp:224
 msgid "PC Speaker emulator"
 msgstr ""
 
-#: audio/softsynth/pcspk.cpp:269
+#: audio/softsynth/pcspk.cpp:243
 msgid "IBM PCjr emulator"
 msgstr ""
 
diff --git a/po/ru_RU.po b/po/ru_RU.po
index 3533884de1f..c2c17fd5588 100644
--- a/po/ru_RU.po
+++ b/po/ru_RU.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.8.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-14 18:41+0000\n"
+"POT-Creation-Date: 2026-06-14 22:15+0000\n"
 "PO-Revision-Date: 2026-05-06 08:54+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Russian <https://translations.scummvm.org/projects/scummvm/"
@@ -4183,11 +4183,11 @@ msgstr "???????????????????? ???????????????? MT-32"
 msgid "MT-32 emulator"
 msgstr "???????????????? MT-32"
 
-#: audio/softsynth/pcspk.cpp:250
+#: audio/softsynth/pcspk.cpp:224
 msgid "PC Speaker emulator"
 msgstr "???????????????? ???????????????? ????"
 
-#: audio/softsynth/pcspk.cpp:269
+#: audio/softsynth/pcspk.cpp:243
 msgid "IBM PCjr emulator"
 msgstr "???????????????? IBM PCjr"
 
diff --git a/po/sv_SE.po b/po/sv_SE.po
index 2b30da89ce6..fba6b8af51c 100644
--- a/po/sv_SE.po
+++ b/po/sv_SE.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.5.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-14 18:41+0000\n"
+"POT-Creation-Date: 2026-06-14 22:15+0000\n"
 "PO-Revision-Date: 2026-04-19 13:22+0000\n"
 "Last-Translator: Daniel Nylander <po at danielnylander.se>\n"
 "Language-Team: Swedish <https://translations.scummvm.org/projects/scummvm/"
@@ -4175,11 +4175,11 @@ msgstr "Initierar MT-32-emulator"
 msgid "MT-32 emulator"
 msgstr "MT-32-emulator"
 
-#: audio/softsynth/pcspk.cpp:250
+#: audio/softsynth/pcspk.cpp:224
 msgid "PC Speaker emulator"
 msgstr "Emulator f??r PC-h??gtalare"
 
-#: audio/softsynth/pcspk.cpp:269
+#: audio/softsynth/pcspk.cpp:243
 msgid "IBM PCjr emulator"
 msgstr "IBM PCjr-emulator"
 
diff --git a/po/tr.po b/po/tr.po
index 1e61d40cff3..b0b5e44fbb2 100644
--- a/po/tr.po
+++ b/po/tr.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-14 18:41+0000\n"
+"POT-Creation-Date: 2026-06-14 22:15+0000\n"
 "PO-Revision-Date: 2024-10-10 12:33+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Turkish <https://translations.scummvm.org/projects/scummvm/"
@@ -4115,11 +4115,11 @@ msgstr ""
 msgid "MT-32 emulator"
 msgstr "AdLib em??lat??r??:"
 
-#: audio/softsynth/pcspk.cpp:250
+#: audio/softsynth/pcspk.cpp:224
 msgid "PC Speaker emulator"
 msgstr ""
 
-#: audio/softsynth/pcspk.cpp:269
+#: audio/softsynth/pcspk.cpp:243
 msgid "IBM PCjr emulator"
 msgstr ""
 
diff --git a/po/uk_UA.po b/po/uk_UA.po
index aaf50074c37..d93053b011b 100644
--- a/po/uk_UA.po
+++ b/po/uk_UA.po
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.9.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-14 18:41+0000\n"
+"POT-Creation-Date: 2026-06-14 22:15+0000\n"
 "PO-Revision-Date: 2026-05-06 08:54+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Ukrainian <https://translations.scummvm.org/projects/scummvm/"
@@ -4177,11 +4177,11 @@ msgstr "???????????????????? ???????????????? MT-32"
 msgid "MT-32 emulator"
 msgstr "???????????????? MT-32"
 
-#: audio/softsynth/pcspk.cpp:250
+#: audio/softsynth/pcspk.cpp:224
 msgid "PC Speaker emulator"
 msgstr "???????????????? PC ??????????????"
 
-#: audio/softsynth/pcspk.cpp:269
+#: audio/softsynth/pcspk.cpp:243
 msgid "IBM PCjr emulator"
 msgstr "???????????????? IBM PCjr"
 
diff --git a/po/zh.po b/po/zh.po
index 1a48b7f4a83..4bd58d305e6 100644
--- a/po/zh.po
+++ b/po/zh.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-14 18:41+0000\n"
+"POT-Creation-Date: 2026-06-14 22:15+0000\n"
 "PO-Revision-Date: 2024-05-14 12:42+0000\n"
 "Last-Translator: ?????? <clonewith at qq.com>\n"
 "Language-Team: Chinese <https://translations.scummvm.org/projects/scummvm/"
@@ -3922,11 +3922,11 @@ msgstr ""
 msgid "MT-32 emulator"
 msgstr ""
 
-#: audio/softsynth/pcspk.cpp:250
+#: audio/softsynth/pcspk.cpp:224
 msgid "PC Speaker emulator"
 msgstr ""
 
-#: audio/softsynth/pcspk.cpp:269
+#: audio/softsynth/pcspk.cpp:243
 msgid "IBM PCjr emulator"
 msgstr ""
 
diff --git a/po/zh_Hans.po b/po/zh_Hans.po
index 0940a839abf..deee04770ca 100644
--- a/po/zh_Hans.po
+++ b/po/zh_Hans.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.9.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-14 18:41+0000\n"
+"POT-Creation-Date: 2026-06-14 22:15+0000\n"
 "PO-Revision-Date: 2024-12-01 22:18+0000\n"
 "Last-Translator: ?????? <clonewith at qq.com>\n"
 "Language-Team: Chinese (Simplified Han script) <https://"
@@ -4133,11 +4133,11 @@ msgstr "??????????????? MT-32 ?????????"
 msgid "MT-32 emulator"
 msgstr "MT-32 ?????????"
 
-#: audio/softsynth/pcspk.cpp:250
+#: audio/softsynth/pcspk.cpp:224
 msgid "PC Speaker emulator"
 msgstr "PC ??????????????????"
 
-#: audio/softsynth/pcspk.cpp:269
+#: audio/softsynth/pcspk.cpp:243
 msgid "IBM PCjr emulator"
 msgstr "IBM PCjr ?????????"
 
diff --git a/po/zh_Hant.po b/po/zh_Hant.po
index 130dcb7cf4d..33c3b36aa2a 100644
--- a/po/zh_Hant.po
+++ b/po/zh_Hant.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.9.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-14 18:41+0000\n"
+"POT-Creation-Date: 2026-06-14 22:15+0000\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: Automatically generated\n"
 "Language-Team: none\n"
@@ -3858,11 +3858,11 @@ msgstr ""
 msgid "MT-32 emulator"
 msgstr ""
 
-#: audio/softsynth/pcspk.cpp:250
+#: audio/softsynth/pcspk.cpp:224
 msgid "PC Speaker emulator"
 msgstr ""
 
-#: audio/softsynth/pcspk.cpp:269
+#: audio/softsynth/pcspk.cpp:243
 msgid "IBM PCjr emulator"
 msgstr ""
 



From noreply at scummvm.org  Sun Jun 14 22:19:35 2026
From: noreply at scummvm.org (sev-)
Date: Sun, 14 Jun 2026 22:19:35 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 bb6740bad201b81753a7a13066b9ff9c77032bd6
Message-ID: <20260614221935.264FF7005CC@ron.scummvm.net>

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

Summary:
bb6740bad2 MORPHOS: Enet support


Commit: bb6740bad201b81753a7a13066b9ff9c77032bd6
    https://github.com/scummvm/scummvm/commit/bb6740bad201b81753a7a13066b9ff9c77032bd6
Author: BeWorld (36823759+BeWorld2018 at users.noreply.github.com)
Date: 2026-06-15T00:19:31+02:00

Commit Message:
MORPHOS: Enet support

Changed paths:
    backends/networking/enet/source/unix.cpp
    backends/networking/enet/source/unix.h


diff --git a/backends/networking/enet/source/unix.cpp b/backends/networking/enet/source/unix.cpp
index 1487315b634..fe6a178df7a 100644
--- a/backends/networking/enet/source/unix.cpp
+++ b/backends/networking/enet/source/unix.cpp
@@ -17,6 +17,27 @@
 #define ENET_BUILDING_LIB 1
 #include "enet.h"
 
+#if defined(MORPHOS)
+#include <proto/socket.h>
+#include <net/socketbasetags.h>
+
+typedef LONG socklen_t;
+#define HAS_SOCKLEN_T 1
+
+#include <proto/exec.h>
+extern struct Library *SocketBase;
+
+#define ioctl(d, request, argp) IoctlSocket((d), (request), (char *)argp)
+#define close CloseSocket
+
+int inet_aton(const char *cp, struct in_addr *addr)
+{
+    addr->s_addr = inet_addr((const UBYTE*)cp);
+    return (addr->s_addr == INADDR_NONE) ? 0 : 1;
+}
+
+#endif
+
 #ifdef __APPLE__
 #ifdef HAS_POLL
 #undef HAS_POLL
@@ -72,12 +93,30 @@ static enet_uint32 timeBase = 0;
 int
 enet_initialize (void)
 {
+#if defined(MORPHOS)
+	SocketBase = OpenLibrary("bsdsocket.library", 0);
+	if (!SocketBase)
+		return -1;
+
+	if (SocketBaseTags(SBTM_SETVAL(SBTC_ERRNOPTR(sizeof(errno))), (IPTR)&errno, TAG_DONE))
+    {
+		enet_deinitialize();
+		return -1;
+    }
+#endif
     return 0;
 }
 
 void
 enet_deinitialize (void)
 {
+#if defined(MORPHOS)
+    if (SocketBase)
+    {
+		CloseLibrary(SocketBase);
+    	SocketBase = NULL;
+    }
+#endif
 }
 
 enet_uint32
@@ -159,8 +198,12 @@ enet_address_set_host (ENetAddress * address, const char * name)
 #else
     hostEntry = gethostbyname_r (name, & hostData, buffer, sizeof (buffer), & errnum);
 #endif
+#else
+#if defined(MORPHOS)
+    hostEntry = gethostbyname ((const UBYTE*)name);
 #else
     hostEntry = gethostbyname (name);
+#endif
 #endif
 
     if (hostEntry != NULL && hostEntry -> h_addrtype == AF_INET)
@@ -233,8 +276,11 @@ enet_address_get_host (const ENetAddress * address, char * name, size_t nameLeng
 #endif
 #else
     in.s_addr = address -> host;
-
+#if defined(MORPHOS)
+    hostEntry = gethostbyaddr ((const UBYTE*) & in, sizeof (struct in_addr), AF_INET);
+#else
     hostEntry = gethostbyaddr ((char *) & in, sizeof (struct in_addr), AF_INET);
+#endif
 #endif
 
     if (hostEntry != NULL)
@@ -453,7 +499,7 @@ enet_socket_send (ENetSocket socket,
         sin.sin_port = ENET_HOST_TO_NET_16 (address -> port);
         sin.sin_addr.s_addr = address -> host;
 
-#if defined(__amigaos4__)
+#if defined(__amigaos4__) || defined(MORPHOS)
         msgHdr.msg_name = (char *)& sin;
 #else
         msgHdr.msg_name = & sin;
@@ -491,7 +537,7 @@ enet_socket_receive (ENetSocket socket,
 
     if (address != NULL)
     {
-#if defined(__amigaos4__)
+#if defined(__amigaos4__) || defined(MORPHOS)
         msgHdr.msg_name = (char *)& sin;
 #else
         msgHdr.msg_name = & sin;
diff --git a/backends/networking/enet/source/unix.h b/backends/networking/enet/source/unix.h
index b55be33103d..70d14fce50c 100644
--- a/backends/networking/enet/source/unix.h
+++ b/backends/networking/enet/source/unix.h
@@ -13,6 +13,10 @@
 #include <netinet/in.h>
 #include <unistd.h>
 
+#if defined(MORPHOS)
+#undef send
+#endif
+
 #ifdef MSG_MAXIOVLEN
 #define ENET_BUFFER_MAXIMUM MSG_MAXIOVLEN
 #endif



From noreply at scummvm.org  Sun Jun 14 23:20:32 2026
From: noreply at scummvm.org (dreammaster)
Date: Sun, 14 Jun 2026 23:20:32 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 60a96f5c07d03df0613027104d2d1662e4287f11
Message-ID: <20260614232032.90DDC7005CC@ron.scummvm.net>

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:
4c5487c3ee MADS: FOREST: Implemented room 404
13279586b0 MADS: FOREST: Implemented room 405
60a96f5c07 MADS: FOREST: Implemented room 420


Commit: 4c5487c3eedfb76ab933ee811afa59bf39f035e8
    https://github.com/scummvm/scummvm/commit/4c5487c3eedfb76ab933ee811afa59bf39f035e8
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-06-15T09:20:18+10:00

Commit Message:
MADS: FOREST: Implemented room 404

Changed paths:
    engines/mads/madsv2/forest/rooms/room404.cpp


diff --git a/engines/mads/madsv2/forest/rooms/room404.cpp b/engines/mads/madsv2/forest/rooms/room404.cpp
index 39c6517e3cb..23747b6f220 100644
--- a/engines/mads/madsv2/forest/rooms/room404.cpp
+++ b/engines/mads/madsv2/forest/rooms/room404.cpp
@@ -1,4 +1,4 @@
-???/* ScummVM - Graphic Adventure Engine
+/* 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
@@ -20,17 +20,21 @@
  */
 
 #include "mads/madsv2/core/conv.h"
+#include "mads/madsv2/core/digi.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/imath.h"
 #include "mads/madsv2/core/inter.h"
 #include "mads/madsv2/core/kernel.h"
+#include "mads/madsv2/core/object.h"
+#include "mads/madsv2/core/player.h"
 #include "mads/madsv2/core/sound.h"
 #include "mads/madsv2/core/text.h"
 #include "mads/madsv2/forest/mads/inventory.h"
 #include "mads/madsv2/forest/mads/sounds.h"
 #include "mads/madsv2/forest/mads/words.h"
+#include "mads/madsv2/forest/extra.h"
+#include "mads/madsv2/forest/journal.h"
 #include "mads/madsv2/forest/global.h"
-#include "mads/madsv2/forest/rooms/section1.h"
 #include "mads/madsv2/forest/rooms/room404.h"
 
 namespace MADS {
@@ -39,10 +43,26 @@ namespace Forest {
 namespace Rooms {
 
 struct Scratch {
-	int16 sprite[10];       /* Sprite series handles */
-	int16 sequence[10];     /* Sequence handles      */
-	int16 animation[10];     /* Animation handles     */
+	int16 sprite[10];
+	int16 sequence[10];
+	int16 animation[10];
 	AnimationInfo animation_info[10];
+	int16 _8c;
+	int16 _8e;
+	int16 _90;
+	int16 _92;
+	int16 _94;
+	int16 _96;
+	int16 _98;
+	int16 _9a;
+	int16 _9c;
+	int16 _9e;
+	int16 _a0;
+	int16 _a2;
+	int16 _a4;
+	int16 _a6;
+	int16 _a8;
+	int16 _aa;
 };
 
 static Scratch scratch;
@@ -51,25 +71,880 @@ static Scratch scratch;
 #define ss    local->sprite
 #define seq   local->sequence
 #define aa    local->animation
-#define aainfo scratch.animation_info
 
 
+static void room_404_init1() {
+	global[g131] = 0;
+	global[g141] = 0;
+
+	if (global[g084] == 16) {
+		object_set_quality(13, 0, 0L);
+		scratch._9c = kernel_run_animation_disp('r', 6, 0);
+		kernel_position_anim(scratch._9c, 123, 145, 92, 1);
+		scratch._9a = kernel_run_animation_disp('e', 6, 0);
+		kernel_position_anim(scratch._9a, 180, 139, 88, 5);
+	} else {
+		scratch._9c = kernel_run_animation_disp('r', 9, 0);
+		kernel_position_anim(scratch._9c, 91, 153, 98, 2);
+		scratch._9a = kernel_run_animation_disp('e', 9, 0);
+		kernel_position_anim(scratch._9a, 64, 103, 62, 11);
+	}
+
+	if (previous_room != KERNEL_RESTORING_GAME) {
+		player.x = 145;
+		player.y = 110;
+		player.facing = 8;
+	}
+
+	if (previous_room == KERNEL_RESTORING_GAME) {
+		global[g131] = -1;
+		global[g141] = -1;
+		if (global[g084] == 16) {
+			object_set_quality(13, 0, 0L);
+			kernel_reset_animation(scratch._9c, 1);
+			kernel_reset_animation(scratch._9a, 1);
+		} else {
+			kernel_reset_animation(scratch._9a, 2);
+			kernel_reset_animation(scratch._9c, 2);
+		}
+		global[g133] = 0;
+		global[g143] = 0;
+	} else if (previous_room == 199) {
+		global[g131] = -1;
+		global[g141] = -1;
+		kernel_reset_animation(scratch._9a, 2);
+		kernel_reset_animation(scratch._9c, 2);
+		global[g133] = 0;
+		global[g143] = 0;
+		restore_player();
+		player.commands_allowed = -1;
+		player.walker_visible = -1;
+	} else if (previous_room == 401) {
+		aa[0] = kernel_run_animation(kernel_name('y', 1), 100);
+		scratch.animation_info[0]._active = -1;
+		scratch._92 = 74;
+		global[g082] = 0;
+	} else if (previous_room == 405) {
+		aa[0] = kernel_run_animation(kernel_name('y', 2), 100);
+		scratch.animation_info[0]._active = -1;
+		scratch._92 = 97;
+		global[g081] = -1;
+		global[g082] = 0;
+		kernel_flip_hotspot(107, 0);
+	} else {
+		global[g131] = -1;
+		global[g141] = -1;
+		kernel_reset_animation(scratch._9a, 2);
+		kernel_reset_animation(scratch._9c, 2);
+		global[g133] = 0;
+		global[g143] = 0;
+		player.commands_allowed = -1;
+		player.walker_visible = -1;
+	}
+
+	global[g009] = -1;
+	kernel_timing_trigger(1, 106);
+}
+
 static void room_404_init() {
+	for (int16 count = 0; count < 10; count++) {
+		scratch.animation_info[count]._active = 0;
+		scratch.animation_info[count]._frame = 0;
+		scratch.animation_info[count]._val3 = 0;
+		scratch.animation_info[count]._val4 = 0;
+	}
+	scratch._aa = 0;
+
+	ss[0] = kernel_load_series(kernel_name('p', 1), 0);
+	ss[1] = kernel_load_series(kernel_name('p', 3), 0);
+
+	aa[7] = kernel_run_animation(kernel_name('N', 1), 105);
+	scratch.animation_info[7]._active = -1;
+
+	aa[8] = kernel_run_animation(kernel_name('N', 2), 105);
+	scratch.animation_info[8]._active = -1;
+
+	if (scratch.animation_info[7]._active == -1) {
+		kernel.trigger_setup_mode = 1;
+		kernel_timing_trigger(1, 110);
+	}
+
+	object_set_quality(13, 0, -1L);
+
+	scratch._a2 = 0;
+	scratch._a4 = -1;
+
+	if (!global[g081] && previous_room != 405) {
+		seq[0] = kernel_seq_stamp(ss[0], false, -1);
+		kernel_seq_depth(seq[0], 2);
+		kernel_seq_loc(seq[0], 271, 147);
+		kernel_seq_scale(seq[0], 77);
+	} else {
+		kernel_flip_hotspot(107, 0);
+		seq[1] = kernel_seq_stamp(ss[1], false, -1);
+		kernel_seq_depth(seq[1], 1);
+		kernel_seq_loc(seq[1], 229, 161);
+		kernel_seq_scale(seq[1], 77);
+	}
+
+	global[player_score] = 0;
+
+	if (previous_room != KERNEL_RESTORING_GAME) {
+		if (previous_room != 199) {
+			player.walker_visible = 0;
+			player.commands_allowed = 0;
+		}
+
+		for (int16 count = 0; count < 10; count++) {
+			scratch.animation_info[count]._active = 0;
+			scratch.animation_info[count]._frame = 1;
+			scratch.animation_info[count]._val3 = 0;
+			scratch.animation_info[count]._val4 = 0;
+		}
+
+		if (previous_room != 199 && flags[29] != 3) {
+			flags[29]++;
+		}
+	}
+
+	room_404_init1();
+}
+
+static void room_404_anim1() {
+	int16 cur = kernel_anim[scratch.animation[0]].frame;
+	if (cur != scratch.animation_info[0]._frame)
+		scratch.animation_info[0]._frame = cur;
+	if (global[player_hyperwalked] != -1)
+		return;
+	int16 f = scratch._92 - 1;
+	scratch.animation_info[0]._frame = f;
+	kernel_reset_animation(scratch.animation[0], f);
+}
+
+static void room_404_anim2() {
+	int16 cur = kernel_anim[scratch.animation[9]].frame;
+	if (cur != scratch.animation_info[9]._frame) {
+		scratch.animation_info[9]._frame = cur;
+		if (flags[29] == 1) {
+			if (cur == 4) {
+				scratch.animation_info[9]._val3 = 11;
+				digi_play_build(404, 'e', 1, 1);
+				scratch._a6 = 1;
+			} else if (cur == 7) {
+				if (scratch.animation_info[9]._val3 == 11) {
+					scratch.animation_info[9]._frame = 5;
+					kernel_reset_animation(scratch.animation[9], 5);
+				}
+			}
+		} else {
+			if (cur == 18) {
+				if (scratch.animation_info[9]._val3 == 10) {
+					scratch.animation_info[9]._frame = 15;
+					kernel_reset_animation(scratch.animation[9], 15);
+				}
+			} else if (cur < 18) {
+				if (cur == 2) {
+					scratch.animation_info[9]._frame = 13;
+					kernel_reset_animation(scratch.animation[9], 13);
+				} else if (cur == 4) {
+					if (scratch.animation_info[9]._val3 == 13) {
+						scratch.animation_info[9]._frame = 3;
+						kernel_reset_animation(scratch.animation[9], 3);
+					}
+				} else if (cur == 7) {
+					if (scratch.animation_info[9]._val3 == 11) {
+						scratch.animation_info[9]._frame = 5;
+						kernel_reset_animation(scratch.animation[9], 5);
+					}
+				} else if (cur == 14) {
+					scratch.animation_info[9]._val3 = 10;
+					digi_play_build(404, 'b', 5, 1);
+					scratch._a6 = 1;
+				}
+			}
+		}
+	}
+
+	if (kernel.trigger != 7 && kernel.trigger != 28)
+		return;
+
+	if (flags[29] == 1) {
+		scratch.animation_info[9]._val3 = 13;
+		scratch.animation_info[9]._frame = 18;
+		kernel_reset_animation(scratch.animation[9], 18);
+		scratch._a6 = -1;
+		return;
+	}
+
+	if (scratch._a6 == 1) {
+		global[g154] = 0;
+		kernel_timing_trigger(30, 28);
+		scratch.animation_info[9]._val3 = 13;
+		scratch.animation_info[9]._frame = 3;
+		kernel_reset_animation(scratch.animation[9], 3);
+		scratch._a6 = 2;
+	} else if (scratch._a6 == 2) {
+		scratch.animation_info[9]._val3 = 11;
+		scratch.animation_info[9]._frame = 5;
+		kernel_reset_animation(scratch.animation[9], 5);
+		scratch._a6 = 3;
+		digi_play_build(404, 'e', 5, 1);
+	} else if (scratch._a6 == 3) {
+		scratch.animation_info[9]._val3 = 13;
+		scratch.animation_info[9]._frame = 18;
+		kernel_reset_animation(scratch.animation[9], 18);
+		scratch._a6 = -1;
+	}
+}
+
+static void room_404_anim3() {
+	int16 cur = kernel_anim[scratch.animation[2]].frame;
+	if (cur != scratch.animation_info[2]._frame)
+		scratch.animation_info[2]._frame = cur;
+}
+
+static void room_404_anim4() {
+	int16 cur = kernel_anim[scratch.animation[1]].frame;
+	if (cur != scratch.animation_info[1]._frame)
+		scratch.animation_info[1]._frame = cur;
+}
+
+static void room_404_anim5() {
+	int16 cur = kernel_anim[scratch.animation[3]].frame;
+	if (cur != scratch.animation_info[3]._frame) {
+		scratch.animation_info[3]._frame = cur;
+		if (cur == 5) {
+			scratch.animation_info[3]._val3 = 11;
+			digi_play_build(404, 'e', 3, 1);
+			scratch._a6 = 5;
+		} else if (cur == 9) {
+			if (scratch.animation_info[3]._val3 == 11) {
+				scratch.animation_info[3]._frame = 5;
+				kernel_reset_animation(scratch.animation[3], 5);
+			}
+		}
+	}
+
+	if (kernel.trigger != 7 && kernel.trigger != 28)
+		return;
+
+	if (scratch._a6 == 5) {
+		scratch.animation_info[3]._val3 = 14;
+		scratch.animation_info[3]._frame = 9;
+		kernel_reset_animation(scratch.animation[3], 9);
+		scratch._a6 = -1;
+	}
+}
+
+static void room_404_anim6() {
+	int16 cur = kernel_anim[scratch.animation[5]].frame;
+	if (cur != scratch.animation_info[5]._frame) {
+		scratch.animation_info[5]._frame = cur;
+		if (cur == 72) {
+			if (scratch.animation_info[5]._val3 == 11) {
+				scratch.animation_info[5]._frame = 67;
+				kernel_reset_animation(scratch.animation[5], 67);
+			}
+		} else if (cur < 72) {
+			if (cur == 5) {
+				scratch.animation_info[5]._val3 = 10;
+				digi_play_build(404, 'b', 2, 1);
+				scratch._a6 = 5;
+			} else if (cur == 10) {
+				if (scratch.animation_info[5]._val3 == 10) {
+					scratch.animation_info[5]._frame = 5;
+					kernel_reset_animation(scratch.animation[5], 5);
+				}
+			} else if (cur == 67) {
+				scratch.animation_info[5]._val3 = 11;
+				digi_play_build(404, 'e', 2, 1);
+				scratch._a6 = 67;
+			}
+		}
+	}
+
+	if (kernel.trigger != 7 && kernel.trigger != 28)
+		return;
+
+	if (scratch._a6 == 5) {
+		scratch.animation_info[5]._val3 = 13;
+		scratch.animation_info[5]._frame = 11;
+		kernel_reset_animation(scratch.animation[5], 11);
+		scratch._a6 = -1;
+	} else if (scratch._a6 == 67) {
+		scratch.animation_info[5]._val3 = 13;
+		scratch.animation_info[5]._frame = 73;
+		kernel_reset_animation(scratch.animation[5], 73);
+		scratch._a6 = -1;
+	}
+}
+
+static void room_404_anim7() {
+	int16 cur = kernel_anim[scratch.animation[6]].frame;
+	if (cur != scratch.animation_info[6]._frame) {
+		scratch.animation_info[6]._frame = cur;
+		if (cur == 57) {
+			if (scratch.animation_info[6]._val3 == 10) {
+				scratch.animation_info[6]._frame = 52;
+				kernel_reset_animation(scratch.animation[6], 52);
+			}
+		} else if (cur > 57) {
+			// no action
+		} else if (cur < 40) {
+			if (cur == 6) {
+				scratch.animation_info[6]._val3 = 11;
+				digi_play_build(404, 'e', 4, 1);
+				scratch._a6 = 6;
+				kernel_seq_delete(scratch.sequence[0]);
+			} else if (cur == 11) {
+				if (scratch.animation_info[6]._val3 == 11) {
+					scratch.animation_info[6]._frame = 6;
+					kernel_reset_animation(scratch.animation[6], 6);
+				}
+			} else if (cur == 39) {
+				digi_play_build(404, '_', 1, 1);
+				scratch._a6 = 39;
+				scratch._aa++;
+			}
+		} else if (cur > 40) {
+			if (cur == 41) {
+				if (scratch._aa < 6) {
+					if (scratch._aa == 4) {
+						digi_play_build(404, '_', 2, 2);
+						scratch._a6 = 39;
+					}
+					scratch.animation_info[6]._frame = 39;
+					kernel_reset_animation(scratch.animation[6], 39);
+					scratch._aa++;
+				}
+			} else if (cur == 46) {
+				digi_stop(2);
+				digi_play_build(404, '_', 3, 2);
+				scratch._a6 = -1;
+			} else if (cur == 52) {
+				scratch.animation_info[6]._val3 = 10;
+				digi_play_build(404, 'b', 3, 1);
+				scratch._a6 = 52;
+				scratch.sequence[1] = kernel_seq_stamp(scratch.sprite[1], 0, -1);
+				kernel_seq_depth(scratch.sequence[1], 1);
+				kernel_seq_loc(scratch.sequence[1], 229, 161);
+				kernel_seq_scale(scratch.sequence[1], 77);
+			}
+		}
+	}
+
+	if (kernel.trigger == 7 || kernel.trigger == 28) {
+		if (scratch._a6 == 6) {
+			scratch.animation_info[6]._val3 = 13;
+			scratch.animation_info[6]._frame = 11;
+			kernel_reset_animation(scratch.animation[6], 11);
+			scratch._a6 = -1;
+		} else if (scratch._a6 == 52) {
+			scratch.animation_info[6]._val3 = 13;
+			scratch.animation_info[6]._frame = 57;
+			kernel_reset_animation(scratch.animation[6], 57);
+			scratch._a6 = -1;
+		}
+	}
+
+	if (kernel.trigger == 8 && (scratch._a6 == 39 || scratch._a6 == 46))
+		scratch._a6 = -1;
+}
+
+static void room_404_anim8() {
+	int16 cur = kernel_anim[scratch.animation[7]].frame;
+	if (cur != scratch.animation_info[7]._frame) {
+		scratch.animation_info[7]._frame = cur;
+		if (cur == 99) {
+			scratch.animation_info[7]._frame = 1;
+			kernel_reset_animation(scratch.animation[7], 1);
+		} else if (cur < 99) {
+			int threshold = -1;
+			if (cur == 10 || cur == 38 || cur == 55 || cur == 74 || cur == 83)
+				threshold = 20;
+			else if (cur == 19 || cur == 48 || cur == 86)
+				threshold = 60;
+			else if (cur == 1 || cur == 28 || cur == 31 || cur == 94)
+				threshold = 80;
+			if (threshold >= 0 && imath_random(0, 100) > threshold) {
+				scratch.animation_info[7]._frame--;
+				kernel_reset_animation(scratch.animation[7], scratch.animation_info[7]._frame);
+			}
+		}
+	}
+
+	if (kernel.trigger == 8 && scratch._a6 == 1)
+		scratch._a6 = -1;
+}
+
+static void room_404_anim9() {
+	int16 cur = kernel_anim[scratch.animation[8]].frame;
+	if (cur == scratch.animation_info[8]._frame)
+		return;
+	scratch.animation_info[8]._frame = cur;
+
+	if (cur == 49) {
+		if (imath_random(0, 100) > 50) {
+			scratch.animation_info[8]._frame--;
+			kernel_reset_animation(scratch.animation[8], scratch.animation_info[8]._frame);
+		} else {
+			scratch.animation_info[8]._frame = 21;
+			kernel_reset_animation(scratch.animation[8], 21);
+		}
+	} else if (cur < 49) {
+		if (cur == 19 || cur == 30 || cur == 40) {
+			if (imath_random(0, 100) > 20) {
+				scratch.animation_info[8]._frame--;
+				kernel_reset_animation(scratch.animation[8], scratch.animation_info[8]._frame);
+			}
+		} else if (cur == 34 || cur == 46) {
+			if (imath_random(0, 100) > 40) {
+				scratch.animation_info[8]._frame--;
+				kernel_reset_animation(scratch.animation[8], scratch.animation_info[8]._frame);
+			}
+		} else if (cur == 20 || cur == 48) {
+			if (imath_random(0, 100) > 60) {
+				scratch.animation_info[8]._frame -= 2;
+				kernel_reset_animation(scratch.animation[8], scratch.animation_info[8]._frame);
+			}
+		}
+	}
 }
 
 static void room_404_daemon() {
+	switch (kernel.trigger) {
+	case 7:
+		if (global[walker_converse_state] && global[g081]) {
+			global[walker_converse_state] = 0;
+			close_journal(3);
+			player.commands_allowed = -1;
+			if (digi_val1)
+				kernel_timing_trigger(1, 110);
+		} else if (scratch._a6 == 400) {
+			kernel_abort_animation(scratch._9e);
+			scratch._a6 = -1;
+			scratch._a2 = 0;
+			kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
+			player.walker_visible = -1;
+			player.commands_allowed = -1;
+			if (digi_val1)
+				kernel_timing_trigger(1, 110);
+		}
+		break;
+
+	case 24:
+		if (scratch._8c == 3) {
+			aa[1] = kernel_run_animation(kernel_name('E', 1), 102);
+			scratch.animation_info[1]._active = -1;
+			scratch._96 = 1;
+			kernel_reset_animation(scratch._9a, 0);
+			kernel_synch(KERNEL_ANIM, aa[1], KERNEL_ANIM, scratch._9a);
+			global[g133] = 1;
+			kernel_timing_trigger(1, 110);
+		} else if (scratch._8c == 7) {
+			aa[3] = kernel_run_animation(kernel_name('L', 2), 102);
+			scratch.animation_info[3]._active = -1;
+			scratch._96 = 3;
+			kernel_reset_animation(scratch._9a, 0);
+			kernel_synch(KERNEL_ANIM, aa[3], KERNEL_ANIM, scratch._9a);
+			global[g133] = 1;
+			kernel_timing_trigger(1, 110);
+		}
+		break;
+
+	case 25:
+		if (scratch._8e == 2) {
+			aa[2] = kernel_run_animation(kernel_name('R', 1), 101);
+			scratch.animation_info[2]._active = -1;
+			scratch._94 = 2;
+			kernel_reset_animation(scratch._9c, 0);
+			kernel_synch(KERNEL_ANIM, aa[2], KERNEL_ANIM, scratch._9c);
+			global[g143] = 1;
+			kernel_timing_trigger(1, 110);
+		}
+		break;
+
+	case 26:
+		if (scratch._90 == 8) {
+			// TODO: loc_6B27A
+		} else if (scratch._90 < 8) {
+			if (scratch._90 == 1) {
+				// TODO: loc_6B318
+			} else if (scratch._90 == 6) {
+				aa[5] = kernel_run_animation(kernel_name('L', 1), 103);
+				scratch.animation_info[5]._active = -1;
+				scratch._98 = 5;
+				kernel_reset_animation(scratch._9a, 0);
+				kernel_reset_animation(scratch._9c, 0);
+				kernel_synch(KERNEL_ANIM, scratch._9a, KERNEL_ANIM, aa[5]);
+				kernel_synch(KERNEL_ANIM, scratch._9c, KERNEL_ANIM, aa[5]);
+				player.walker_visible = 0;
+				global[g133] = 1;
+				global[g143] = 1;
+				scratch._a4 = 1;
+				kernel_timing_trigger(1, 110);
+			}
+		}
+		break;
+
+	case 27:
+		global[walker_converse_now] = 0;
+		kernel_abort_animation(scratch._a0);
+		global[g131] = -1;
+		kernel_reset_animation(scratch._9a, 1);
+		kernel_synch(KERNEL_ANIM, scratch._9a, KERNEL_NOW, 0);
+		global[g133] = 0;
+		player.commands_allowed = -1;
+		if (digi_val1)
+			kernel_timing_trigger(1, 110);
+		break;
+
+	case 100:
+		kernel_abort_animation(aa[0]);
+		scratch.animation_info[0]._active = 0;
+		if (previous_room == 401) {
+			global[g084] = 15;
+			if (flags[29] == 1 || global[g081]) {
+				aa[9] = kernel_run_animation(kernel_name('t', 1), 107);
+				kernel_synch(KERNEL_ANIM, aa[9], KERNEL_ANIM, aa[0]);
+				scratch.animation_info[9]._active = -1;
+				player.commands_allowed = 0;
+				kernel_timing_trigger(1, 110);
+			} else {
+				global[g131] = -1;
+				global[g141] = -1;
+				kernel_reset_animation(scratch._9a, 1);
+				kernel_synch(KERNEL_ANIM, scratch._9a, KERNEL_NOW, 0);
+				kernel_reset_animation(scratch._9c, 1);
+				kernel_synch(KERNEL_ANIM, scratch._9c, KERNEL_NOW, 0);
+				player.walker_visible = -1;
+				global[g133] = 0;
+				global[g143] = 0;
+				kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
+				player.commands_allowed = -1;
+				if (digi_val1)
+					kernel_timing_trigger(1, 110);
+			}
+		} else if (previous_room == 405) {
+			global[g131] = -1;
+			global[g141] = -1;
+			kernel_reset_animation(scratch._9a, 1);
+			kernel_synch(KERNEL_ANIM, scratch._9a, KERNEL_NOW, 0);
+			kernel_reset_animation(scratch._9c, 1);
+			kernel_synch(KERNEL_ANIM, scratch._9c, KERNEL_NOW, 0);
+			player.walker_visible = -1;
+			global[g133] = 0;
+			global[g143] = 0;
+			kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
+			player.commands_allowed = -1;
+			if (digi_val1)
+				kernel_timing_trigger(1, 110);
+		}
+		break;
+
+	case 101:
+		kernel_abort_animation(aa[scratch._94]);
+		scratch.animation_info[scratch._94]._active = 0;
+		kernel_reset_animation(scratch._9c, 1);
+		kernel_synch(KERNEL_ANIM, scratch._9c, KERNEL_NOW, 0);
+		global[g143] = 0;
+		player.commands_allowed = -1;
+		if (digi_val1)
+			kernel_timing_trigger(1, 110);
+		break;
+
+	case 102:
+		kernel_abort_animation(aa[scratch._96]);
+		scratch.animation_info[scratch._96]._active = 0;
+		kernel_reset_animation(scratch._9a, 1);
+		kernel_synch(KERNEL_ANIM, scratch._9a, KERNEL_NOW, 0);
+		global[g133] = 0;
+		player.commands_allowed = -1;
+		if (digi_val1)
+			kernel_timing_trigger(1, 110);
+		break;
+
+	case 103:
+		if (scratch._a4 == 1) {
+			kernel_abort_animation(aa[5]);
+			scratch.animation_info[5]._active = 0;
+			kernel_abort_animation(scratch._9a);
+			kernel_abort_animation(scratch._9c);
+			global[g084] = 16;
+			object_set_quality(13, 0, 0L);
+			scratch._9c = kernel_run_animation_disp('r', 6, 0);
+			kernel_position_anim(scratch._9c, 123, 145, 92, 1);
+			scratch._9a = kernel_run_animation_disp('e', 6, 0);
+			kernel_position_anim(scratch._9a, 180, 139, 88, 5);
+			kernel_reset_animation(scratch._9c, 1);
+			kernel_synch(KERNEL_ANIM, scratch._9c, KERNEL_NOW, 0);
+			global[g143] = 0;
+			kernel_reset_animation(scratch._9a, 1);
+			kernel_synch(KERNEL_ANIM, scratch._9a, KERNEL_NOW, 0);
+			player.walker_visible = -1;
+			global[g133] = 0;
+			kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
+			global[g082] = 1;
+			if (global[walker_converse_state]) {
+				kernel_timing_trigger(5, 104);
+			} else {
+				player.commands_allowed = -1;
+			}
+			kernel_timing_trigger(1, 110);
+		} else if (scratch._a4 == 2) {
+			kernel_abort_animation(aa[6]);
+			scratch.animation_info[6]._active = 0;
+			aa[6] = kernel_run_animation(kernel_name('L', 4), 103);
+			scratch._a4 = 3;
+			kernel_timing_trigger(1, 110);
+		} else if (scratch._a4 == 3) {
+			kernel_abort_animation(aa[6]);
+			scratch.animation_info[6]._active = 0;
+			kernel_reset_animation(scratch._9c, 1);
+			kernel_synch(KERNEL_ANIM, scratch._9c, KERNEL_NOW, 0);
+			global[g143] = 0;
+			kernel_reset_animation(scratch._9a, 1);
+			kernel_synch(KERNEL_ANIM, scratch._9a, KERNEL_NOW, 0);
+			player.walker_visible = -1;
+			global[g133] = 0;
+			kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
+			global[g081] = -1;
+			kernel_flip_hotspot(107, 0);
+			object_set_quality(13, 0, 0L);
+			inter_move_object(13, 1);
+			display_interface();
+			if (global[walker_converse_state]) {
+				global[walker_converse_state] = 0;
+				close_journal(3);
+			}
+			player.commands_allowed = -1;
+			if (digi_val1)
+				kernel_timing_trigger(1, 110);
+		}
+		break;
+
+	case 104:
+		player.commands_allowed = 0;
+		if (global[walker_converse_state])
+			inter_spin_object(13);
+		global[g150] = -1;
+		scratch._90 = 8;
+		break;
+
+	case 107:
+		kernel_abort_animation(aa[9]);
+		scratch.animation_info[9]._active = 0;
+		if (flags[29] == 1) {
+			global[g141] = -1;
+			kernel_reset_animation(scratch._9c, 1);
+			kernel_synch(KERNEL_ANIM, scratch._9c, KERNEL_NOW, 0);
+			player.walker_visible = -1;
+			global[g143] = 0;
+			kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
+			scratch._a0 = kernel_run_animation_write(0);
+			kernel_reset_animation(scratch._a0, 1);
+			kernel_position_anim(scratch._a0, 64, 103, 62, 11);
+			global[walker_converse_now] = 1;
+			global[g007] = 9;
+			kernel_synch(KERNEL_ANIM, scratch._a0, KERNEL_NOW, 0);
+			kernel_timing_trigger(1, 110);
+		} else {
+			global[g131] = -1;
+			global[g141] = -1;
+			kernel_reset_animation(scratch._9a, 1);
+			kernel_synch(KERNEL_ANIM, scratch._9a, KERNEL_NOW, 0);
+			kernel_reset_animation(scratch._9c, 1);
+			kernel_synch(KERNEL_ANIM, scratch._9c, KERNEL_NOW, 0);
+			player.walker_visible = -1;
+			global[g133] = 0;
+			global[g143] = 0;
+			kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
+			player.commands_allowed = -1;
+			if (digi_val1)
+				kernel_timing_trigger(1, 110);
+		}
+		break;
+
+	case 108:
+		global[g150] = -1;
+		scratch._90 = 6;
+		break;
+
+	case 109:
+		player.commands_allowed = 0;
+		player_walk(202, 133, 3);
+		player_walk_trigger(104);
+		break;
+
+	case 110:
+		digi_play_build(401, '_', 1, 3);
+		digi_val2 = -1;
+		break;
+	}
+
+	if (global[g084] == 16) {
+		global_anim1(6, scratch._9a, global[g131], &global[g132]);
+		global_anim2(6, scratch._9c, global[g141], &global[g142]);
+	} else {
+		global_anim1(9, scratch._9a, global[g131], &global[g132]);
+		global_anim2(9, scratch._9c, global[g141], &global[g142]);
+	}
+
+	if (scratch._a2)
+		kernel_random_frame(scratch._9e, &global[g151], global[g154]);
+
+	if (scratch.animation_info[0]._active)  room_404_anim1();
+	if (scratch.animation_info[9]._active)  room_404_anim2();
+	if (scratch.animation_info[2]._active)  room_404_anim3();
+	if (scratch.animation_info[1]._active)  room_404_anim4();
+	if (scratch.animation_info[3]._active)  room_404_anim5();
+	if (scratch.animation_info[5]._active)  room_404_anim6();
+	if (scratch.animation_info[6]._active)  room_404_anim7();
+
+	if (global[walker_converse_now])
+		global_anim3(scratch._a0, &global[g008]);
+
+	room_404_anim8();
+	room_404_anim9();
 }
 
 static void room_404_pre_parser() {
+	if (player_parse(13, 46, 0)) {
+		global[g084] = 15;
+		player.walk_off_edge_to_room = 401;
+	}
 }
 
 static void room_404_parser() {
+	if (global[walker_converse_state]) {
+		player.commands_allowed = 0;
+		if (global[g081]) {
+			digi_play_build_ii('c', 1, 1);
+			player.command_ready = false;
+			return;
+		}
+		if (global[g084] == 16) {
+			player.commands_allowed = 0;
+			player_walk(202, 133, 3);
+			kernel.trigger_setup_mode = 1;
+			player_walk_trigger(104);
+		} else {
+			player_walk(202, 133, 3);
+			kernel.trigger_setup_mode = 1;
+			player_walk_trigger(108);
+		}
+		player.command_ready = false;
+		return;
+	}
+
+	if (player_parse(13, 50, 0)) {
+		global[g084] = 15;
+		new_room = 405;
+		player.command_ready = false;
+		return;
+	}
+
+	if (global[player_selected_object] >= 0) {
+		if (global[player_selected_object] == 13) {
+			if (!global[g081] && global[g082]) {
+				kernel.trigger_setup_mode = 1;
+				kernel_timing_trigger(2, 109);
+			} else {
+				display_interface();
+			}
+		} else {
+			player.commands_allowed = 0;
+			global[g135] = -1;
+			scratch._8c = 7;
+		}
+		player.command_ready = false;
+		return;
+	}
+
+	if (player_parse(114, 116, 0)) {
+		player.commands_allowed = 0;
+		global[g145] = -1;
+		scratch._8e = 2;
+		player.command_ready = false;
+		return;
+	}
+
+	if (player_parse(126, 99, 0)) {
+		player.commands_allowed = 0;
+		global[g135] = -1;
+		scratch._8c = 3;
+		player.command_ready = false;
+		return;
+	}
+
+	if (player_parse(78, 107, 0)) {
+		if (!global[g082]) {
+			player.commands_allowed = 0;
+			global[g150] = -1;
+			scratch._90 = 6;
+		} else {
+			player.commands_allowed = 0;
+			global[g135] = -1;
+			scratch._8c = 7;
+		}
+		player.command_ready = false;
+		return;
+	}
+
+	if (player_parse(78, 175, 0)) {
+		global[g154] = 2;
+		player.commands_allowed = 0;
+		player.walker_visible = 0;
+		scratch._9e = kernel_run_animation_talk('b', 7, 0);
+		kernel_position_anim(scratch._9e, player.x, player.y, player.scale, player.depth);
+		kernel_synch(KERNEL_ANIM, scratch._9e, KERNEL_PLAYER, 0);
+		digi_play_build(404, 'b', 4, 1);
+		scratch._a6 = 400;
+		scratch._a2 = -1;
+		kernel.trigger_setup_mode = 1;
+		kernel_timing_trigger(1, 110);
+		player.command_ready = false;
+		return;
+	}
+
+	if (player_parse(78, 176, 0)) {
+		global[g154] = 2;
+		player.commands_allowed = 0;
+		player.walker_visible = 0;
+		scratch._9e = kernel_run_animation_talk('b', 9, 0);
+		kernel_position_anim(scratch._9e, player.x, player.y, player.scale, player.depth);
+		kernel_synch(KERNEL_ANIM, scratch._9e, KERNEL_PLAYER, 0);
+		digi_play_build(404, 'b', 4, 1);
+		scratch._a6 = 400;
+		scratch._a2 = -1;
+		kernel.trigger_setup_mode = 1;
+		kernel_timing_trigger(1, 110);
+		player.command_ready = false;
+		return;
+	}
 }
 
 void room_404_synchronize(Common::Serializer &s) {
 	for (int16 &v : scratch.sprite)    s.syncAsSint16LE(v);
 	for (int16 &v : scratch.sequence)  s.syncAsSint16LE(v);
 	for (int16 &v : scratch.animation) s.syncAsSint16LE(v);
+	for (AnimationInfo &ai : scratch.animation_info) ai.synchronize(s);
+	s.syncAsSint16LE(scratch._8c);
+	s.syncAsSint16LE(scratch._8e);
+	s.syncAsSint16LE(scratch._90);
+	s.syncAsSint16LE(scratch._92);
+	s.syncAsSint16LE(scratch._94);
+	s.syncAsSint16LE(scratch._96);
+	s.syncAsSint16LE(scratch._98);
+	s.syncAsSint16LE(scratch._9a);
+	s.syncAsSint16LE(scratch._9c);
+	s.syncAsSint16LE(scratch._9e);
+	s.syncAsSint16LE(scratch._a0);
+	s.syncAsSint16LE(scratch._a2);
+	s.syncAsSint16LE(scratch._a4);
+	s.syncAsSint16LE(scratch._a6);
+	s.syncAsSint16LE(scratch._a8);
+	s.syncAsSint16LE(scratch._aa);
 }
 
 void room_404_preload() {


Commit: 13279586b0f12ca7b1b4861a02af7a6c2bafef84
    https://github.com/scummvm/scummvm/commit/13279586b0f12ca7b1b4861a02af7a6c2bafef84
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-06-15T09:20:19+10:00

Commit Message:
MADS: FOREST: Implemented room 405

Changed paths:
    engines/mads/madsv2/forest/rooms/room405.cpp


diff --git a/engines/mads/madsv2/forest/rooms/room405.cpp b/engines/mads/madsv2/forest/rooms/room405.cpp
index 8a4fb1797b3..35a122ffda8 100644
--- a/engines/mads/madsv2/forest/rooms/room405.cpp
+++ b/engines/mads/madsv2/forest/rooms/room405.cpp
@@ -1,4 +1,4 @@
-???/* ScummVM - Graphic Adventure Engine
+/* 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
@@ -20,17 +20,20 @@
  */
 
 #include "mads/madsv2/core/conv.h"
+#include "mads/madsv2/core/digi.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/imath.h"
 #include "mads/madsv2/core/inter.h"
 #include "mads/madsv2/core/kernel.h"
+#include "mads/madsv2/core/player.h"
 #include "mads/madsv2/core/sound.h"
 #include "mads/madsv2/core/text.h"
 #include "mads/madsv2/forest/mads/inventory.h"
 #include "mads/madsv2/forest/mads/sounds.h"
 #include "mads/madsv2/forest/mads/words.h"
+#include "mads/madsv2/forest/extra.h"
 #include "mads/madsv2/forest/global.h"
-#include "mads/madsv2/forest/rooms/section1.h"
+#include "mads/madsv2/forest/journal.h"
 #include "mads/madsv2/forest/rooms/room405.h"
 
 namespace MADS {
@@ -39,10 +42,22 @@ namespace Forest {
 namespace Rooms {
 
 struct Scratch {
-	int16 sprite[10];       /* Sprite series handles */
-	int16 sequence[10];     /* Sequence handles      */
-	int16 animation[10];     /* Animation handles     */
+	int16 sprite[10];
+	int16 sequence[10];
+	int16 animation[10];
 	AnimationInfo animation_info[10];
+	int16 _8c;
+	int16 _8e;
+	int16 _90;
+	int16 _92;
+	int16 _94;
+	int16 _96;
+	int16 _98;
+	int16 _9a;
+	int16 _9c;
+	int16 _9e;
+	int16 _a0;
+	int16 _a2;
 };
 
 static Scratch scratch;
@@ -54,29 +69,463 @@ static Scratch scratch;
 #define aainfo scratch.animation_info
 
 
+static void room_405_init1() {
+	global[g131] = 0;
+	global[g141] = 0;
+
+	scratch._9c = kernel_run_animation_disp('r', 7, 0);
+	kernel_position_anim(scratch._9c, 308, 152, 69, 1);
+	scratch._9a = kernel_run_animation_disp('e', 7, 0);
+	kernel_position_anim(scratch._9a, 284, 154, 69, 1);
+
+	if (previous_room != KERNEL_RESTORING_GAME) {
+		player.x = 246;
+		player.y = 130;
+		player.facing = 7;
+	}
+
+	if (previous_room == KERNEL_RESTORING_GAME) {
+		global[g131] = -1;
+		global[g141] = -1;
+		kernel_reset_animation(scratch._9a, 2);
+		kernel_reset_animation(scratch._9c, 2);
+		global[g133] = 0;
+		global[g143] = 0;
+	} else if (previous_room == 199) {
+		global[g131] = -1;
+		global[g141] = -1;
+		kernel_reset_animation(scratch._9a, 2);
+		kernel_reset_animation(scratch._9c, 2);
+		global[g133] = 0;
+		global[g143] = 0;
+		restore_player();
+		player.commands_allowed = -1;
+		player.walker_visible = -1;
+	} else if (previous_room == 404) {
+		global[g009] = -1;
+		aa[0] = kernel_run_animation(kernel_name('y', 1), 100);
+		scratch.animation_info[0]._active = -1;
+		scratch._92 = 106;
+	} else if (previous_room == 501) {
+		global[g009] = -1;
+		aa[0] = kernel_run_animation(kernel_name('y', 2), 100);
+		scratch.animation_info[0]._active = -1;
+		scratch._92 = 44;
+	} else {
+		global[g131] = -1;
+		global[g141] = -1;
+		kernel_reset_animation(scratch._9a, 2);
+		kernel_reset_animation(scratch._9c, 2);
+		global[g133] = 0;
+		global[g143] = 0;
+		player.commands_allowed = -1;
+		player.walker_visible = -1;
+	}
+}
+
 static void room_405_init() {
+	global[player_score] = 0;
+	global[g009] = -1;
+
+	aa[7] = kernel_run_animation(kernel_name('N', 1), 105);
+	scratch.animation_info[7]._active = -1;
+	kernel.trigger_setup_mode = 1;
+	kernel_timing_trigger(1, 106);
+
+	scratch._a0 = -1;
+
+	if (previous_room != KERNEL_RESTORING_GAME) {
+		if (previous_room != 199) {
+			player.walker_visible = 0;
+			player.commands_allowed = 0;
+		}
+
+		for (int16 count = 0; count < 10; count++) {
+			scratch.animation_info[count]._active = 0;
+			scratch.animation_info[count]._frame = 1;
+			scratch.animation_info[count]._val3 = 0;
+			scratch.animation_info[count]._val4 = 0;
+		}
+
+		if (previous_room != 199 && flags[30] != 3) {
+			flags[30]++;
+		}
+	}
+
+	room_405_init1();
+}
+
+static void room_405_anim1() {
+	int16 cur = kernel_anim[aa[0]].frame;
+	if (cur != scratch.animation_info[0]._frame)
+		scratch.animation_info[0]._frame = cur;
+	if (global[player_hyperwalked] != -1)
+		return;
+	int16 f = scratch._92 - 1;
+	scratch.animation_info[0]._frame = f;
+	kernel_reset_animation(aa[0], f);
+}
+
+static void room_405_anim2() {
+	int16 cur = kernel_anim[aa[6]].frame;
+	if (cur != scratch.animation_info[6]._frame) {
+		scratch.animation_info[6]._frame = cur;
+		if (cur == 5) {
+			scratch.animation_info[6]._val3 = 12;
+			digi_play_build(404, 'r', 1, 1);
+			scratch._a2 = 5;
+		} else if (cur == 10) {
+			if (scratch.animation_info[6]._val3 == 12) {
+				scratch.animation_info[6]._frame = 5;
+				kernel_reset_animation(aa[6], 5);
+			}
+		}
+	}
+
+	if (kernel.trigger != 7 && kernel.trigger != 28)
+		return;
+
+	if (scratch._a2 == 5) {
+		scratch.animation_info[6]._val3 = 13;
+		scratch.animation_info[6]._frame = 10;
+		kernel_reset_animation(aa[6], 10);
+		scratch._a2 = -1;
+	}
+}
+
+static void room_405_anim3() {
+	int16 cur = kernel_anim[aa[2]].frame;
+	if (cur != scratch.animation_info[2]._frame)
+		scratch.animation_info[2]._frame = cur;
+}
+
+static void room_405_anim4() {
+	int16 cur = kernel_anim[aa[1]].frame;
+	if (cur != scratch.animation_info[1]._frame)
+		scratch.animation_info[1]._frame = cur;
+}
+
+static void room_405_anim5() {
+	int16 cur = kernel_anim[aa[5]].frame;
+	if (cur != scratch.animation_info[5]._frame) {
+		scratch.animation_info[5]._frame = cur;
+		if (cur == 21) {
+			if (scratch.animation_info[5]._val3 == 11) {
+				scratch.animation_info[5]._frame = 16;
+				kernel_reset_animation(aa[5], 16);
+			}
+		} else if (cur < 21) {
+			if (cur == 6) {
+				scratch.animation_info[5]._val3 = 10;
+				digi_play_build(404, 'b', 4, 1);
+				scratch._a2 = 6;
+			} else if (cur == 11) {
+				if (scratch.animation_info[5]._val3 == 10) {
+					scratch.animation_info[5]._frame = 6;
+					kernel_reset_animation(aa[5], 6);
+				}
+			} else if (cur == 16) {
+				scratch.animation_info[5]._val3 = 11;
+				digi_play_build(404, 'e', 5, 1);
+				scratch._a2 = 16;
+			}
+		}
+	}
+
+	if (kernel.trigger != 7 && kernel.trigger != 28)
+		return;
+
+	if (scratch._a2 == 6) {
+		scratch.animation_info[5]._val3 = 13;
+		scratch.animation_info[5]._frame = 11;
+		kernel_reset_animation(aa[5], 11);
+		scratch._a2 = -1;
+	} else if (scratch._a2 == 16) {
+		scratch.animation_info[5]._val3 = 13;
+		scratch.animation_info[5]._frame = 21;
+		kernel_reset_animation(aa[5], 21);
+		scratch._a2 = -1;
+	}
+}
+
+static void room_405_anim6() {
+	int16 cur = kernel_anim[aa[7]].frame;
+	if (cur != scratch.animation_info[7]._frame) {
+		scratch.animation_info[7]._frame = cur;
+		if (cur == 99) {
+			scratch.animation_info[7]._frame = 1;
+			kernel_reset_animation(aa[7], 1);
+		} else if (cur < 99) {
+			int threshold = -1;
+			if (cur == 10 || cur == 38 || cur == 55 || cur == 74 || cur == 83)
+				threshold = 20;
+			else if (cur == 19 || cur == 48 || cur == 86)
+				threshold = 60;
+			else if (cur == 1 || cur == 28 || cur == 31 || cur == 94)
+				threshold = 80;
+			if (threshold >= 0 && imath_random(0, 100) > threshold) {
+				scratch.animation_info[7]._frame--;
+				kernel_reset_animation(aa[7], scratch.animation_info[7]._frame);
+			}
+		}
+	}
+
+	if (kernel.trigger == 8 && scratch._a2 == 1)
+		scratch._a2 = -1;
 }
 
 static void room_405_daemon() {
+	switch (kernel.trigger) {
+	case 7:
+		if (global[walker_converse_state]) {
+			global[walker_converse_state] = 0;
+			close_journal(3);
+			player.commands_allowed = -1;
+			if (digi_val1)
+				kernel_timing_trigger(1, 106);
+		}
+		break;
+
+	case 24:
+		if (scratch._8c == 3) {
+			aa[1] = kernel_run_animation(kernel_name('E', 1), 102);
+			scratch.animation_info[1]._active = -1;
+			scratch._96 = 1;
+			kernel_reset_animation(scratch._9a, 0);
+			kernel_synch(KERNEL_ANIM, aa[1], KERNEL_ANIM, scratch._9a);
+			global[g133] = 1;
+			kernel_timing_trigger(1, 106);
+		}
+		break;
+
+	case 25:
+		if (scratch._8e == 2) {
+			aa[2] = kernel_run_animation(kernel_name('R', 1), 101);
+			scratch.animation_info[2]._active = -1;
+			scratch._94 = 2;
+			kernel_reset_animation(scratch._9c, 0);
+			kernel_synch(KERNEL_ANIM, aa[2], KERNEL_ANIM, scratch._9c);
+			global[g143] = 1;
+			kernel_timing_trigger(1, 106);
+		}
+		break;
+
+	case 26:
+		if (scratch._90 == 1) {
+			kernel_reset_animation(scratch._9a, 0);
+			kernel_reset_animation(scratch._9c, 0);
+			global[g133] = 1;
+			global[g143] = 1;
+		} else if (scratch._90 == 6) {
+			aa[5] = kernel_run_animation(kernel_name('L', 1), 103);
+			scratch.animation_info[5]._active = -1;
+			scratch._98 = 5;
+			kernel_reset_animation(scratch._9a, 0);
+			kernel_reset_animation(scratch._9c, 0);
+			kernel_synch(KERNEL_ANIM, scratch._9a, KERNEL_ANIM, aa[5]);
+			kernel_synch(KERNEL_ANIM, scratch._9c, KERNEL_ANIM, aa[5]);
+			global[g133] = 1;
+			global[g143] = 1;
+			scratch._a0 = 1;
+			kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
+			player.walker_visible = 0;
+			kernel_timing_trigger(1, 106);
+		}
+		break;
+
+	case 27:
+		global[walker_converse_now] = 0;
+		kernel_abort_animation(scratch._9e);
+		global[g131] = -1;
+		kernel_reset_animation(scratch._9a, 1);
+		kernel_synch(KERNEL_ANIM, scratch._9a, KERNEL_NOW, 0);
+		global[g133] = 0;
+		player.commands_allowed = -1;
+		if (digi_val1)
+			kernel_timing_trigger(1, 106);
+		break;
+
+	case 100:
+		kernel_abort_animation(aa[0]);
+		scratch.animation_info[0]._active = 0;
+		if (flags[30] == 1) {
+			aa[6] = kernel_run_animation(kernel_name('t', 1), 104);
+			kernel_synch(KERNEL_ANIM, aa[6], KERNEL_ANIM, aa[0]);
+			scratch.animation_info[6]._active = -1;
+			player.commands_allowed = 0;
+			kernel_timing_trigger(1, 106);
+		} else {
+			global[g131] = -1;
+			global[g141] = -1;
+			kernel_reset_animation(scratch._9a, 1);
+			kernel_synch(KERNEL_ANIM, scratch._9a, KERNEL_NOW, 0);
+			kernel_reset_animation(scratch._9c, 1);
+			kernel_synch(KERNEL_ANIM, scratch._9c, KERNEL_NOW, 0);
+			player.walker_visible = -1;
+			global[g133] = 0;
+			global[g143] = 0;
+			kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
+			player.commands_allowed = -1;
+			if (digi_val1)
+				kernel_timing_trigger(1, 106);
+		}
+		break;
+
+	case 101:
+		kernel_abort_animation(aa[scratch._94]);
+		scratch.animation_info[scratch._94]._active = 0;
+		kernel_reset_animation(scratch._9c, 1);
+		kernel_synch(KERNEL_ANIM, scratch._9c, KERNEL_NOW, 0);
+		global[g143] = 0;
+		player.commands_allowed = -1;
+		if (digi_val1)
+			kernel_timing_trigger(1, 106);
+		break;
+
+	case 102:
+		kernel_abort_animation(aa[scratch._96]);
+		scratch.animation_info[scratch._96]._active = 0;
+		kernel_reset_animation(scratch._9a, 1);
+		kernel_synch(KERNEL_ANIM, scratch._9a, KERNEL_NOW, 0);
+		global[g133] = 0;
+		player.commands_allowed = -1;
+		if (digi_val1)
+			kernel_timing_trigger(1, 106);
+		break;
+
+	case 103:
+		if (scratch._a0 == 1) {
+			kernel_abort_animation(aa[5]);
+			scratch.animation_info[5]._active = 0;
+			kernel_reset_animation(scratch._9c, 1);
+			kernel_synch(KERNEL_ANIM, scratch._9c, KERNEL_NOW, 0);
+			global[g143] = 0;
+			kernel_reset_animation(scratch._9a, 1);
+			kernel_synch(KERNEL_ANIM, scratch._9a, KERNEL_NOW, 0);
+			player.walker_visible = -1;
+			global[g133] = 0;
+			kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
+			global[g082] = 1;
+			player.commands_allowed = -1;
+			if (digi_val1)
+				kernel_timing_trigger(1, 106);
+		}
+		break;
+
+	case 104:
+		kernel_abort_animation(aa[6]);
+		scratch.animation_info[6]._active = 0;
+		global[g141] = -1;
+		kernel_reset_animation(scratch._9c, 1);
+		kernel_synch(KERNEL_ANIM, scratch._9c, KERNEL_NOW, 0);
+		player.walker_visible = -1;
+		global[g143] = 0;
+		kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
+		scratch._9e = kernel_run_animation_write(0);
+		kernel_reset_animation(scratch._9e, 41);
+		kernel_position_anim(scratch._9e, 284, 154, 69, 1);
+		global[walker_converse_now] = 1;
+		global[g007] = 7;
+		kernel_synch(KERNEL_ANIM, scratch._9e, KERNEL_NOW, 0);
+		kernel_timing_trigger(1, 106);
+		break;
+
+	case 106:
+		digi_play_build(401, '_', 1, 3);
+		digi_initial_volume(60);
+		digi_val2 = -1;
+		break;
+
+	}
+
+	global_anim1(7, scratch._9a, global[g131], &global[g132]);
+	global_anim2(7, scratch._9c, global[g141], &global[g142]);
+
+	if (scratch.animation_info[0]._active) room_405_anim1();
+	if (scratch.animation_info[6]._active) room_405_anim2();
+	if (scratch.animation_info[2]._active) room_405_anim3();
+	if (scratch.animation_info[1]._active) room_405_anim4();
+	if (scratch.animation_info[5]._active) room_405_anim5();
+
+	if (global[walker_converse_now])
+		global_anim3(scratch._9e, &global[g008]);
+
+	room_405_anim6();
 }
 
 static void room_405_pre_parser() {
+	if (player_parse(13, 55, 0))
+		player.walk_off_edge_to_room = 501;
 }
 
 static void room_405_parser() {
+	if (global[walker_converse_state]) {
+		player.commands_allowed = 0;
+		digi_play_build_ii('c', 1, 1);
+		player.command_ready = false;
+		return;
+	}
+
+	if (player_parse(13, 49, 0)) {
+		new_room = 404;
+		player.command_ready = false;
+		return;
+	}
+
+	if (global[player_selected_object] >= 0) {
+		player.command_ready = false;
+		return;
+	}
+
+	if (player_parse(114, 116, 0)) {
+		player.commands_allowed = 0;
+		global[g145] = -1;
+		scratch._8e = 2;
+		player.command_ready = false;
+		return;
+	}
+
+	if (player_parse(126, 99, 0)) {
+		player.commands_allowed = 0;
+		global[g135] = -1;
+		scratch._8c = 3;
+		player.command_ready = false;
+		return;
+	}
+
+	if (player_parse(78, 175, 0)) {
+		player.commands_allowed = 0;
+		global[g150] = -1;
+		scratch._90 = 6;
+		player.command_ready = false;
+		return;
+	}
 }
 
 void room_405_synchronize(Common::Serializer &s) {
 	for (int16 &v : scratch.sprite)    s.syncAsSint16LE(v);
 	for (int16 &v : scratch.sequence)  s.syncAsSint16LE(v);
 	for (int16 &v : scratch.animation) s.syncAsSint16LE(v);
+	for (AnimationInfo &ai : scratch.animation_info) ai.synchronize(s);
+	s.syncAsSint16LE(scratch._8c);
+	s.syncAsSint16LE(scratch._8e);
+	s.syncAsSint16LE(scratch._90);
+	s.syncAsSint16LE(scratch._92);
+	s.syncAsSint16LE(scratch._94);
+	s.syncAsSint16LE(scratch._96);
+	s.syncAsSint16LE(scratch._98);
+	s.syncAsSint16LE(scratch._9a);
+	s.syncAsSint16LE(scratch._9c);
+	s.syncAsSint16LE(scratch._9e);
+	s.syncAsSint16LE(scratch._a0);
+	s.syncAsSint16LE(scratch._a2);
 }
 
 void room_405_preload() {
-	room_init_code_pointer = room_405_init;
+	room_init_code_pointer       = room_405_init;
 	room_pre_parser_code_pointer = room_405_pre_parser;
-	room_parser_code_pointer = room_405_parser;
-	room_daemon_code_pointer = room_405_daemon;
+	room_parser_code_pointer     = room_405_parser;
+	room_daemon_code_pointer     = room_405_daemon;
 
 	global_section_walker();
 	global_section_interface();


Commit: 60a96f5c07d03df0613027104d2d1662e4287f11
    https://github.com/scummvm/scummvm/commit/60a96f5c07d03df0613027104d2d1662e4287f11
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-06-15T09:20:19+10:00

Commit Message:
MADS: FOREST: Implemented room 420

Changed paths:
    engines/mads/madsv2/forest/rooms/room420.cpp


diff --git a/engines/mads/madsv2/forest/rooms/room420.cpp b/engines/mads/madsv2/forest/rooms/room420.cpp
index 6fa3c35a826..ed5f1ee07ca 100644
--- a/engines/mads/madsv2/forest/rooms/room420.cpp
+++ b/engines/mads/madsv2/forest/rooms/room420.cpp
@@ -19,16 +19,11 @@
  *
  */
 
-#include "mads/madsv2/core/conv.h"
+#include "mads/madsv2/core/digi.h"
 #include "mads/madsv2/core/game.h"
-#include "mads/madsv2/core/imath.h"
-#include "mads/madsv2/core/inter.h"
 #include "mads/madsv2/core/kernel.h"
-#include "mads/madsv2/core/sound.h"
-#include "mads/madsv2/core/text.h"
-#include "mads/madsv2/forest/mads/inventory.h"
-#include "mads/madsv2/forest/mads/sounds.h"
-#include "mads/madsv2/forest/mads/words.h"
+#include "mads/madsv2/core/midi.h"
+#include "mads/madsv2/core/player.h"
 #include "mads/madsv2/forest/global.h"
 #include "mads/madsv2/forest/rooms/section1.h"
 #include "mads/madsv2/forest/rooms/room420.h"
@@ -39,10 +34,11 @@ namespace Forest {
 namespace Rooms {
 
 struct Scratch {
-	int16 sprite[10];       /* Sprite series handles */
-	int16 sequence[10];     /* Sequence handles      */
-	int16 animation[10];     /* Animation handles     */
+	int16 sprite[10];
+	int16 sequence[10];
+	int16 animation[10];
 	AnimationInfo animation_info[10];
+	int16 _8e;
 };
 
 static Scratch scratch;
@@ -54,28 +50,181 @@ static Scratch scratch;
 #define aainfo scratch.animation_info
 
 
-static void room_420_init() {
+static void room_420_anim1() {
+	int16 cur = kernel_anim[aa[0]].frame;
+	if (cur == scratch.animation_info[0]._frame)
+		return;
+	scratch.animation_info[0]._frame = cur;
+	if (cur != 100)
+		return;
+	kernel_abort_animation(aa[0]);
+	scratch.animation_info[0]._active = 0;
+	aa[1] = kernel_run_animation(kernel_name('T', 2), 0);
+	scratch.animation_info[1]._active = -1;
+	kernel_synch(KERNEL_ANIM, aa[1], KERNEL_ANIM, aa[0]);
 }
 
-static void room_420_daemon() {
+static void room_420_anim2() {
+	int16 cur = kernel_anim[aa[1]].frame;
+	if (cur != scratch.animation_info[1]._frame) {
+		scratch.animation_info[1]._frame = cur;
+		if (cur == 50) {
+			kernel_abort_animation(aa[1]);
+			scratch.animation_info[1]._active = 0;
+			aa[2] = kernel_run_animation(kernel_name('N', 1), 0);
+			scratch.animation_info[2]._active = -1;
+			kernel_synch(KERNEL_ANIM, aa[2], KERNEL_ANIM, aa[1]);
+		} else if (cur < 50) {
+			if (cur == 24) {
+				scratch._8e = 24;
+				scratch.animation_info[1]._val3 = 11;
+				if (global[g066] == 2 || global[g066] == 3)
+					digi_play_build(420, 'e', 1, 1);
+				else
+					digi_play_build(420, 'e', 1, 2);
+			} else if (cur < 24) {
+				if (cur == 5) {
+					scratch._8e = 5;
+					scratch.animation_info[1]._val3 = 9;
+					digi_play_build(420, 'r', 1, 1);
+				} else if (cur == 19) {
+					if (scratch.animation_info[1]._val3 == 9) {
+						scratch.animation_info[1]._frame = 5;
+						kernel_reset_animation(aa[1], 5);
+					}
+				} else if (cur == 23) {
+					if (scratch.animation_info[1]._val3 == 12) {
+						scratch.animation_info[1]._frame = 22;
+						kernel_reset_animation(aa[1], 22);
+					}
+				}
+			} else {
+				if (cur == 29) {
+					if (scratch.animation_info[1]._val3 == 11) {
+						scratch.animation_info[1]._frame = 24;
+						kernel_reset_animation(aa[1], 24);
+					}
+				} else if (cur == 33) {
+					if (scratch.animation_info[1]._val3 == 12) {
+						scratch.animation_info[1]._frame = 32;
+						kernel_reset_animation(aa[1], 32);
+					}
+				} else if (cur == 34) {
+					scratch._8e = 34;
+					scratch.animation_info[1]._val3 = 10;
+					digi_play_build(420, 'b', 1, 1);
+					global_digi_play(3);
+				} else if (cur == 39) {
+					if (scratch.animation_info[1]._val3 == 10) {
+						scratch.animation_info[1]._frame = 34;
+						kernel_reset_animation(aa[1], 34);
+					}
+				}
+			}
+		}
+	}
+
+	if (kernel.trigger != 7 && kernel.trigger != 28)
+		return;
+
+	int16 val = scratch._8e;
+	if (val == 34) {
+		scratch.animation_info[1]._val3 = 13;
+		scratch.animation_info[1]._frame = 39;
+		kernel_reset_animation(aa[1], 39);
+		scratch._8e = -1;
+	} else if ((uint16)val > 34) {
+		return;
+	} else if (val == 5) {
+		scratch.animation_info[1]._val3 = 12;
+		kernel_timing_trigger(30, 28);
+		scratch._8e = 6;
+	} else if (val == 6) {
+		scratch.animation_info[1]._val3 = 13;
+		scratch.animation_info[1]._frame = 19;
+		kernel_reset_animation(aa[1], 19);
+		scratch._8e = -1;
+	} else if (val == 24) {
+		scratch.animation_info[1]._val3 = 12;
+		kernel_timing_trigger(30, 28);
+		scratch._8e = 25;
+	} else if (val == 25) {
+		scratch.animation_info[1]._val3 = 13;
+		scratch.animation_info[1]._frame = 29;
+		kernel_reset_animation(aa[1], 29);
+		scratch._8e = -1;
+	}
+}
+
+static void room_420_anim3() {
+	int16 cur = kernel_anim[aa[2]].frame;
+	if (cur == scratch.animation_info[2]._frame)
+		return;
+	scratch.animation_info[2]._frame = cur;
+	if (cur != 82)
+		return;
+	kernel_abort_animation(aa[2]);
+	scratch.animation_info[2]._active = 0;
+	aa[3] = kernel_run_animation(kernel_name('N', 2), 0);
+	scratch.animation_info[3]._active = -1;
+	kernel_synch(KERNEL_ANIM, aa[3], KERNEL_ANIM, aa[2]);
 }
 
-static void room_420_pre_parser() {
+static void room_420_anim4() {
+	int16 cur = kernel_anim[aa[3]].frame;
+	if (cur == scratch.animation_info[3]._frame)
+		return;
+	scratch.animation_info[3]._frame = cur;
+	if (cur == 10) {
+		scratch.animation_info[3]._active = 0;
+		new_room = 401;
+	} else if ((uint16)cur > 10) {
+		return;
+	} else if (cur == 2) {
+		scratch.animation_info[3]._val4 = 0;
+	} else if (cur == 8) {
+		if (scratch.animation_info[3]._val4 < 20) {
+			scratch.animation_info[3]._frame = 7;
+			kernel_reset_animation(aa[3], 7);
+			scratch.animation_info[3]._val4++;
+		}
+	}
 }
 
-static void room_420_parser() {
+static void room_420_init() {
+    flags[31] = 1;
+    midi_stop();
+    global[player_score] = -1;
+    global[g009] = 0;
+    player.walker_visible = 0;
+    player.commands_allowed = 0;
+
+    for (int16 count = 0; count < 10; count++) {
+        scratch.animation_info[count]._active = 0;
+        scratch.animation_info[count]._frame = -1;
+    }
+
+    aa[0] = kernel_run_animation(kernel_name('T', 1), 0);
+    scratch.animation_info[0]._active = -1;
+}
+
+static void room_420_daemon() {
+    if (scratch.animation_info[0]._active) room_420_anim1();
+    if (scratch.animation_info[1]._active) room_420_anim2();
+    if (scratch.animation_info[2]._active) room_420_anim3();
+    if (scratch.animation_info[3]._active) room_420_anim4();
 }
 
 void room_420_synchronize(Common::Serializer &s) {
 	for (int16 &v : scratch.sprite)    s.syncAsSint16LE(v);
 	for (int16 &v : scratch.sequence)  s.syncAsSint16LE(v);
 	for (int16 &v : scratch.animation) s.syncAsSint16LE(v);
+	for (AnimationInfo &ai : scratch.animation_info) ai.synchronize(s);
+	s.syncAsSint16LE(scratch._8e);
 }
 
 void room_420_preload() {
 	room_init_code_pointer = room_420_init;
-	room_pre_parser_code_pointer = room_420_pre_parser;
-	room_parser_code_pointer = room_420_parser;
 	room_daemon_code_pointer = room_420_daemon;
 
 	if (flags[31] == -4)



From noreply at scummvm.org  Sun Jun 14 23:57:17 2026
From: noreply at scummvm.org (dreammaster)
Date: Sun, 14 Jun 2026 23:57:17 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 4e5071bcfbc460113ee759f8c09aba0a6e22a5fc
Message-ID: <20260614235717.68AFA7005CC@ron.scummvm.net>

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

Summary:
4e5071bcfb MADS: FOREST: Implemented room 501


Commit: 4e5071bcfbc460113ee759f8c09aba0a6e22a5fc
    https://github.com/scummvm/scummvm/commit/4e5071bcfbc460113ee759f8c09aba0a6e22a5fc
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-06-15T09:57:08+10:00

Commit Message:
MADS: FOREST: Implemented room 501

Changed paths:
    engines/mads/madsv2/forest/rooms/room501.cpp


diff --git a/engines/mads/madsv2/forest/rooms/room501.cpp b/engines/mads/madsv2/forest/rooms/room501.cpp
index a0f7801ec0f..8b8556d43f1 100644
--- a/engines/mads/madsv2/forest/rooms/room501.cpp
+++ b/engines/mads/madsv2/forest/rooms/room501.cpp
@@ -24,13 +24,17 @@
 #include "mads/madsv2/core/imath.h"
 #include "mads/madsv2/core/inter.h"
 #include "mads/madsv2/core/kernel.h"
+#include "mads/madsv2/core/digi.h"
+#include "mads/madsv2/core/midi.h"
+#include "mads/madsv2/core/player.h"
 #include "mads/madsv2/core/sound.h"
 #include "mads/madsv2/core/text.h"
 #include "mads/madsv2/forest/mads/inventory.h"
 #include "mads/madsv2/forest/mads/sounds.h"
 #include "mads/madsv2/forest/mads/words.h"
 #include "mads/madsv2/forest/global.h"
-#include "mads/madsv2/forest/rooms/section1.h"
+#include "mads/madsv2/forest/journal.h"
+#include "mads/madsv2/forest/rooms/section5.h"
 #include "mads/madsv2/forest/rooms/room501.h"
 
 namespace MADS {
@@ -39,10 +43,25 @@ namespace Forest {
 namespace Rooms {
 
 struct Scratch {
-	int16 sprite[10];       /* Sprite series handles */
-	int16 sequence[10];     /* Sequence handles      */
-	int16 animation[10];     /* Animation handles     */
+	int16 sprite[10];
+	int16 sequence[10];
+	int16 animation[10];
 	AnimationInfo animation_info[10];
+
+	int16 _8c;
+	int16 _8e;
+	int16 _90;
+	int16 _92;
+	int16 _94;
+	int16 _96;
+	int16 _98;
+	int16 _9a;
+	int16 _9c;
+	int16 _9e;
+	int16 _a0;
+	int16 _a2;
+	int16 _a4;
+	int16 _a6;
 };
 
 static Scratch scratch;
@@ -53,30 +72,565 @@ static Scratch scratch;
 #define aa    local->animation
 #define aainfo scratch.animation_info
 
+static void room_501_init1();
+static void room_501_anim1();
+static void room_501_anim2();
+static void room_501_anim3();
+static void room_501_anim4();
+static void room_501_anim5();
 
 static void room_501_init() {
+	midi_stop();
+	global[g009] = 0;
+	global[player_score] = -1;
+	scratch._a6 = -1;
+
+	if (previous_room != KERNEL_RESTORING_GAME) {
+		if (previous_room != 199) {
+			player.walker_visible = 0;
+			player.commands_allowed = 0;
+		}
+
+		for (int count = 0; count < 10; count++) {
+			aainfo[count]._active = 0;
+			aainfo[count]._frame = -1;
+			aainfo[count]._val3 = 0;
+			aainfo[count]._val4 = 0;
+		}
+
+		if (previous_room != 199) {
+			if (flags[32] != 3)
+				flags[32]++;
+		}
+	}
+
+	room_501_init1();
+}
+
+static void room_501_init1() {
+	global[g131] = 0;
+	global[g141] = 0;
+	scratch._9c = kernel_run_animation_disp('r', 4, 0);
+	kernel_position_anim(scratch._9c, 132, 127, 49, 7);
+	scratch._9a = kernel_run_animation_disp('e', 2, 0);
+	kernel_position_anim(scratch._9a, 112, 127, 49, 7);
+
+	if (previous_room != KERNEL_RESTORING_GAME) {
+		player.x = 79;
+		player.y = 129;
+		player.facing = 3;
+	}
+
+	if (previous_room == KERNEL_RESTORING_GAME) {
+		global[g131] = -1;
+		global[g141] = -1;
+		kernel_reset_animation(scratch._9a, 2);
+		kernel_reset_animation(scratch._9c, 2);
+		global[g133] = 0;
+		global[g143] = 0;
+	} else if (previous_room == 199) {
+		global[g131] = -1;
+		global[g141] = -1;
+		kernel_reset_animation(scratch._9a, 2);
+		kernel_reset_animation(scratch._9c, 2);
+		global[g133] = 0;
+		global[g143] = 0;
+		restore_player();
+		player.commands_allowed = -1;
+		player.walker_visible = -1;
+	} else if (previous_room == 405) {
+		if (flags[32] == 1) {
+			aa[1] = kernel_run_animation(kernel_name('y', 1), 105);
+			aainfo[1]._active = -1;
+			scratch._a2 = 1;
+			scratch._92 = 84;
+		} else {
+			aa[0] = kernel_run_animation(kernel_name('y', 3), 100);
+			aainfo[0]._active = -1;
+			scratch._92 = 104;
+		}
+	} else if (previous_room == 503) {
+		aa[0] = kernel_run_animation(kernel_name('y', 4), 100);
+		aainfo[0]._active = -1;
+		scratch._92 = 98;
+	} else {
+		global[g131] = -1;
+		global[g141] = -1;
+		kernel_reset_animation(scratch._9a, 2);
+		kernel_reset_animation(scratch._9c, 2);
+		global[g133] = 0;
+		global[g143] = 0;
+		player.commands_allowed = -1;
+		player.walker_visible = -1;
+	}
+}
+
+static void room_501_anim1() {
+	if (kernel_anim[aa[0]].frame != aainfo[0]._frame)
+		aainfo[0]._frame = kernel_anim[aa[0]].frame;
+
+	if (global[player_hyperwalked] == -1) {
+		aainfo[0]._frame = scratch._92 - 1;
+		kernel_reset_animation(aa[0], aainfo[0]._frame);
+	}
+}
+
+static void room_501_anim2() {
+	if (kernel_anim[aa[3]].frame != aainfo[3]._frame)
+		aainfo[3]._frame = kernel_anim[aa[3]].frame;
+}
+
+static void room_501_anim3() {
+}
+
+static void room_501_anim4() {
+	if (kernel_anim[aa[4]].frame != aainfo[4]._frame) {
+		aainfo[4]._frame = kernel_anim[aa[4]].frame;
+		int16 frame = aainfo[4]._frame;
+
+		if (scratch._a6 == 1) {
+			if (frame == 42) {
+				if (aainfo[4]._val3 == 23) {
+					aainfo[4]._frame = 40;
+					kernel_reset_animation(aa[4], 40);
+				}
+			} else if (frame < 42) {
+				if (frame == 33) {
+					aainfo[4]._val3 = 21;
+					digi_play_build_ii('e', 3, 1);
+					scratch._a4 = 33;
+				} else if (frame == 38) {
+					if (aainfo[4]._val3 == 21) {
+						aainfo[4]._frame = 33;
+						kernel_reset_animation(aa[4], 33);
+					}
+				}
+			}
+		} else if (scratch._a6 == 2) {
+			if (frame == 49) {
+				if (aainfo[4]._val3 == 23) {
+					aainfo[4]._frame = 46;
+					kernel_reset_animation(aa[4], 46);
+				}
+			} else if (frame < 49) {
+				if (frame == 40) {
+					aainfo[4]._val3 = 21;
+					digi_play_build_ii('e', 3, 1);
+					scratch._a4 = 400;
+				} else if (frame == 45) {
+					if (aainfo[4]._val3 == 21) {
+						aainfo[4]._frame = 40;
+						kernel_reset_animation(aa[4], 40);
+					}
+				}
+			}
+		} else if (scratch._a6 == 3) {
+			if (frame == 40) {
+				if (aainfo[4]._val3 == 23) {
+					aainfo[4]._frame = 38;
+					kernel_reset_animation(aa[4], 38);
+				}
+			} else if (frame < 40) {
+				if (frame == 31) {
+					aainfo[4]._val3 = 21;
+					digi_play_build_ii('e', 5, 1);
+					scratch._a4 = 310;
+				} else if (frame == 36) {
+					if (aainfo[4]._val3 == 21) {
+						aainfo[4]._frame = 31;
+						kernel_reset_animation(aa[4], 31);
+					}
+				}
+			}
+		}
+	}
+
+	if (kernel.trigger != 7 && kernel.trigger != 28)
+		return;
+
+	if (scratch._a4 == 33) {
+		kernel_timing_trigger(30, 28);
+		aainfo[4]._val3 = 23;
+		scratch._a4 = 34;
+	} else if (scratch._a4 == 34) {
+		aainfo[4]._val3 = 21;
+		aainfo[4]._frame = 33;
+		kernel_reset_animation(aa[4], 33);
+		digi_play_build_ii('e', 10, 1);
+		scratch._a4 = 35;
+	} else if (scratch._a4 == 35) {
+		aainfo[4]._val3 = 19;
+		aainfo[4]._frame = 39;
+		kernel_reset_animation(aa[4], 39);
+		scratch._a4 = -1;
+	} else if (scratch._a4 == 310) {
+		kernel_timing_trigger(30, 28);
+		aainfo[4]._val3 = 23;
+		scratch._a4 = 311;
+	} else if (scratch._a4 == 311) {
+		aainfo[4]._val3 = 21;
+		aainfo[4]._frame = 31;
+		kernel_reset_animation(aa[4], 31);
+		digi_play_build_ii('e', 10, 1);
+		scratch._a4 = 312;
+	} else if (scratch._a4 == 312) {
+		aainfo[4]._val3 = 19;
+		aainfo[4]._frame = 37;
+		kernel_reset_animation(aa[4], 37);
+		scratch._a4 = -1;
+	} else if (scratch._a4 == 400) {
+		kernel_timing_trigger(30, 28);
+		aainfo[4]._val3 = 23;
+		scratch._a4 = 401;
+	} else if (scratch._a4 == 401) {
+		aainfo[4]._val3 = 21;
+		aainfo[4]._frame = 40;
+		kernel_reset_animation(aa[4], 40);
+		digi_play_build_ii('e', 10, 1);
+		scratch._a4 = 402;
+	} else if (scratch._a4 == 402) {
+		aainfo[4]._val3 = 19;
+		aainfo[4]._frame = 46;
+		kernel_reset_animation(aa[4], 46);
+		scratch._a4 = -1;
+	}
+}
+
+static void room_501_anim5() {
+	if (kernel_anim[aa[1]].frame != aainfo[1]._frame) {
+		aainfo[1]._frame = kernel_anim[aa[1]].frame;
+
+		if (scratch._a2 == 1) {
+			int16 frame = aainfo[1]._frame;
+			if (frame > 112) {
+				// no action ??? fall through to trigger check
+			} else if (frame == 112) {
+				if (aainfo[1]._val3 == 21) {
+					aainfo[1]._frame = 108;
+					kernel_reset_animation(aa[1], 108);
+				}
+			} else if (frame > 98) {
+				if (frame == 104) {
+					if (aainfo[1]._val3 == 21) {
+						aainfo[1]._frame = 100;
+						kernel_reset_animation(aa[1], 100);
+					}
+				} else if (frame == 106) {
+					if (aainfo[1]._val3 == 23) {
+						aainfo[1]._frame = 105;
+						kernel_reset_animation(aa[1], 105);
+					}
+				}
+			} else if (frame == 98) {
+				if (aainfo[1]._val3 == 23) {
+					aainfo[1]._frame = 97;
+					kernel_reset_animation(aa[1], 97);
+				}
+			} else {
+				if (frame == 90) {
+					aainfo[1]._val3 = 20;
+					digi_play_build(501, 'b', 1, 1);
+					scratch._a4 = 1;
+				} else if (frame == 96) {
+					if (aainfo[1]._val3 == 20) {
+						aainfo[1]._frame = 91;
+						kernel_reset_animation(aa[1], 91);
+					}
+				}
+			}
+		}
+	}
+
+	if (kernel.trigger == 7 || kernel.trigger == 28) {
+		if (scratch._a4 == 1) {
+			global[g154] = 0;
+			aainfo[1]._val3 = 23;
+			aainfo[1]._frame = 97;
+			kernel_timing_trigger(30, 28);
+			kernel_reset_animation(aa[1], aainfo[1]._frame);
+			scratch._a4 = 2;
+		} else if (scratch._a4 == 2) {
+			global[g156] = 2;
+			aainfo[1]._val3 = 21;
+			aainfo[1]._frame = 99;
+			kernel_reset_animation(aa[1], 99);
+			digi_play_build(501, 'e', 1, 1);
+			scratch._a4 = 3;
+		} else if (scratch._a4 == 3) {
+			global_digi_play(14);
+			global[g156] = 0;
+			aainfo[1]._val3 = 23;
+			aainfo[1]._frame = 105;
+			kernel_timing_trigger(30, 28);
+			kernel_reset_animation(aa[1], aainfo[1]._frame);
+			scratch._a4 = 4;
+		} else if (scratch._a4 == 4) {
+			global[g155] = 2;
+			aainfo[1]._val3 = 22;
+			aainfo[1]._frame = 107;
+			kernel_reset_animation(aa[1], 107);
+			digi_play_build(501, 'r', 1, 1);
+			scratch._a4 = 5;
+		} else if (scratch._a4 == 5) {
+			global[g156] = 0;
+			aainfo[1]._val3 = 23;
+			aainfo[1]._frame = 113;
+			kernel_reset_animation(aa[1], 113);
+			scratch._a4 = 6;
+		}
+	}
+
+	if (scratch._a2 == 1 && global[player_hyperwalked] == -1) {
+		int16 target = scratch._92 - 1;
+		if (target > aainfo[1]._frame) {
+			aainfo[1]._frame = target;
+			kernel_reset_animation(aa[1], target);
+		}
+	}
 }
 
 static void room_501_daemon() {
+	switch (kernel.trigger) {
+	case 7:
+		if (global[walker_converse_state] != 0) {
+			global[walker_converse_state] = 0;
+			close_journal(3);
+		}
+		player.commands_allowed = -1;
+		break;
+
+	case 24:
+		if (scratch._8c == 3) {
+			aa[2] = kernel_run_animation(kernel_name('E', 1), 102);
+			aainfo[2]._active = -1;
+			scratch._96 = 2;
+			kernel_reset_animation(scratch._9a, 0);
+			kernel_synch(KERNEL_ANIM, aa[2], KERNEL_ANIM, scratch._9a);
+			global[g133] = 1;
+		} else if (scratch._8c == 5) {
+			if (scratch._a6 == 1) {
+				aa[4] = kernel_run_animation(kernel_name('F', 1), 102);
+				aainfo[4]._active = -1;
+				scratch._96 = 4;
+				kernel_reset_animation(scratch._9a, 0);
+				kernel_synch(KERNEL_ANIM, aa[4], KERNEL_ANIM, scratch._9a);
+				global[g133] = 1;
+			} else if (scratch._a6 == 2) {
+				aa[4] = kernel_run_animation(kernel_name('F', 2), 102);
+				aainfo[4]._active = -1;
+				scratch._96 = 4;
+				kernel_reset_animation(scratch._9a, 0);
+				kernel_synch(KERNEL_ANIM, aa[4], KERNEL_ANIM, scratch._9a);
+				global[g133] = 1;
+			} else if (scratch._a6 == 3) {
+				aa[4] = kernel_run_animation(kernel_name('F', 3), 102);
+				aainfo[4]._active = -1;
+				scratch._96 = 4;
+				kernel_reset_animation(scratch._9a, 0);
+				kernel_synch(KERNEL_ANIM, aa[4], KERNEL_ANIM, scratch._9a);
+				global[g133] = 1;
+			}
+		}
+		break;
+
+	case 25:
+		if (scratch._8e == 2) {
+			aa[3] = kernel_run_animation(kernel_name('R', 1), 101);
+			aainfo[3]._active = -1;
+			scratch._94 = 3;
+			kernel_reset_animation(scratch._9c, 0);
+			kernel_synch(KERNEL_ANIM, aa[3], KERNEL_ANIM, scratch._9c);
+			global[g143] = 1;
+		}
+		break;
+
+	case 26:
+		kernel_reset_animation(scratch._9a, 0);
+		kernel_reset_animation(scratch._9c, 0);
+		global[g133] = 1;
+		global[g143] = 1;
+		scratch._90--;
+		break;
+
+	case 27:
+		kernel_abort_animation(aa[1]);
+		aainfo[1]._active = 0;
+		global[g131] = -1;
+		kernel_reset_animation(scratch._9a, 1);
+		kernel_synch(KERNEL_ANIM, scratch._9a, KERNEL_NOW, 0);
+		global[g133] = 0;
+		player.commands_allowed = -1;
+		break;
+
+	case 100:
+		kernel_abort_animation(aa[0]);
+		aainfo[0]._active = 0;
+		global[g131] = -1;
+		global[g141] = -1;
+		kernel_reset_animation(scratch._9a, 1);
+		kernel_synch(KERNEL_ANIM, scratch._9a, KERNEL_NOW, 0);
+		kernel_reset_animation(scratch._9c, 1);
+		kernel_synch(KERNEL_ANIM, scratch._9c, KERNEL_NOW, 0);
+		player.walker_visible = -1;
+		global[g133] = 0;
+		global[g143] = 0;
+		kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
+		player.commands_allowed = -1;
+		break;
+
+	case 101:
+		kernel_abort_animation(aa[scratch._94]);
+		aainfo[scratch._94]._active = 0;
+		kernel_reset_animation(scratch._9c, 1);
+		kernel_synch(KERNEL_ANIM, scratch._9c, KERNEL_NOW, 0);
+		global[g143] = 0;
+		player.commands_allowed = -1;
+		break;
+
+	case 102:
+		kernel_abort_animation(aa[scratch._96]);
+		aainfo[scratch._96]._active = 0;
+		kernel_reset_animation(scratch._9a, 1);
+		kernel_synch(KERNEL_ANIM, scratch._9a, KERNEL_NOW, 0);
+		global[g133] = 0;
+		global[player_score] = -1;
+		player.commands_allowed = -1;
+		break;
+
+	case 105:
+		if (scratch._a2 == 1) {
+			kernel_abort_animation(aa[1]);
+			aa[1] = kernel_run_animation(kernel_name('y', 2), 105);
+			scratch._a2 = 2;
+		} else if (scratch._a2 == 2) {
+			kernel_abort_animation(aa[1]);
+			// word_7997C = 0 (unused variable, ignored)
+			global[g141] = -1;
+			kernel_reset_animation(scratch._9c, 1);
+			kernel_synch(KERNEL_ANIM, scratch._9c, KERNEL_NOW, 0);
+			player.walker_visible = -1;
+			global[g143] = 0;
+			kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
+			scratch._a0 = kernel_run_animation_write(0);
+			kernel_reset_animation(scratch._a0, 4);
+			kernel_position_anim(scratch._a0, 112, 127, 49, 7);
+			global[walker_converse_now] = 1;
+			global[g007] = 2;
+			kernel_synch(KERNEL_ANIM, scratch._a0, KERNEL_NOW, 0);
+		}
+		break;
+
+	}
+
+	global_anim1(2, scratch._9a, global[g131], &global[g132]);
+	global_anim2(4, scratch._9c, global[g141], &global[g142]);
+
+	if (aainfo[0]._active)
+		room_501_anim1();
+	if (aainfo[3]._active)
+		room_501_anim2();
+	if (aainfo[2]._active)
+		room_501_anim3();
+	if (aainfo[4]._active)
+		room_501_anim4();
+	if (aainfo[1]._active)
+		room_501_anim5();
+
+	if (global[walker_converse_now])
+		global_anim3(scratch._a0, &global[g008]);
 }
 
 static void room_501_pre_parser() {
+	if (player_parse(13, 50, 0))
+		player.walk_off_edge_to_room = 405;
 }
 
 static void room_501_parser() {
+	if (global[walker_converse_state] != 0) {
+		player.commands_allowed = 0;
+		digi_play_build_ii('c', 1, 1);
+		goto handled;
+	}
+
+	if (player_parse(13, 57, 0)) {
+		new_room = 503;
+		goto handled;
+	}
+
+	if (global[player_selected_object] >= 0) {
+		goto handled;
+	}
+
+	if (player_parse(114, 142, 0)) {
+		global[player_score] = 0;
+		digi_stop(3);
+		player.commands_allowed = 0;
+		global[g135] = -1;
+		scratch._8c = 5;
+		scratch._a6 = 1;
+		goto handled;
+	}
+
+	if (player_parse(114, 171, 0)) {
+		global[player_score] = 0;
+		digi_stop(3);
+		player.commands_allowed = 0;
+		global[g135] = -1;
+		scratch._8c = 5;
+		scratch._a6 = 2;
+		goto handled;
+	}
+
+	if (player_parse(114, 131, 0)) {
+		global[player_score] = 0;
+		digi_stop(3);
+		player.commands_allowed = 0;
+		global[g135] = -1;
+		scratch._8c = 5;
+		scratch._a6 = 3;
+		goto handled;
+	}
+
+	if (player_parse(126, 99, 0) || player_parse(78, 119, 0)) {
+		player.commands_allowed = 0;
+		global[g135] = -1;
+		scratch._8c = 3;
+		goto handled;
+	}
+
+	goto done;
+handled:
+	player.command_ready = false;
+done:
+	;
 }
 
 void room_501_synchronize(Common::Serializer &s) {
-	for (int16 &v : scratch.sprite)    s.syncAsSint16LE(v);
-	for (int16 &v : scratch.sequence)  s.syncAsSint16LE(v);
-	for (int16 &v : scratch.animation) s.syncAsSint16LE(v);
+	for (int16 &v : scratch.sprite)         s.syncAsSint16LE(v);
+	for (int16 &v : scratch.sequence)       s.syncAsSint16LE(v);
+	for (int16 &v : scratch.animation)      s.syncAsSint16LE(v);
+	for (AnimationInfo &ai : scratch.animation_info) ai.synchronize(s);
+	s.syncAsSint16LE(scratch._8c);
+	s.syncAsSint16LE(scratch._8e);
+	s.syncAsSint16LE(scratch._90);
+	s.syncAsSint16LE(scratch._92);
+	s.syncAsSint16LE(scratch._94);
+	s.syncAsSint16LE(scratch._96);
+	s.syncAsSint16LE(scratch._98);
+	s.syncAsSint16LE(scratch._9a);
+	s.syncAsSint16LE(scratch._9c);
+	s.syncAsSint16LE(scratch._9e);
+	s.syncAsSint16LE(scratch._a0);
+	s.syncAsSint16LE(scratch._a2);
+	s.syncAsSint16LE(scratch._a4);
+	s.syncAsSint16LE(scratch._a6);
 }
 
 void room_501_preload() {
-	room_init_code_pointer = room_501_init;
+	room_init_code_pointer       = room_501_init;
 	room_pre_parser_code_pointer = room_501_pre_parser;
-	room_parser_code_pointer = room_501_parser;
-	room_daemon_code_pointer = room_501_daemon;
+	room_parser_code_pointer     = room_501_parser;
+	room_daemon_code_pointer     = room_501_daemon;
 
 	global_section_walker();
 	global_section_interface();



From noreply at scummvm.org  Mon Jun 15 00:46:56 2026
From: noreply at scummvm.org (mikrosk)
Date: Mon, 15 Jun 2026 00:46:56 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 8dde5c814ee7a80e64a5952881a8a42b112d845a
Message-ID: <20260615004656.9DDCC7005BC@ron.scummvm.net>

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:
7ffe8d933b BACKENDS: ATARI: Update readme.txt
8dde5c814e NEWS: Add Atari news


Commit: 7ffe8d933bdf887e9945c4b273f54575aea28c4e
    https://github.com/scummvm/scummvm/commit/7ffe8d933bdf887e9945c4b273f54575aea28c4e
Author: Miro Kropacek (miro.kropacek at gmail.com)
Date: 2026-06-15T10:45:44+10:00

Commit Message:
BACKENDS: ATARI: Update readme.txt

Changed paths:
    backends/platform/atari/readme.txt
    backends/platform/atari/readme.txt.in


diff --git a/backends/platform/atari/readme.txt b/backends/platform/atari/readme.txt
index 5a378f81c82..168c067dd2f 100644
--- a/backends/platform/atari/readme.txt
+++ b/backends/platform/atari/readme.txt
@@ -426,22 +426,9 @@ ScummVM has supported native audio cd playback for a long time. It is even
 enabled in the FireBee (SDL) build. However native support in the Atari builds
 has been added just recently. This feature can be used to free the audio mixer
 from loading, decoding and mixing game music with sound effects, leaving more
-CPU time for the engine itself. The following configurations have been tested:
-
-- ExtenDOS 4.10
-	- its cd.bos has a bug preventing ScummVM loading a file from CD on
-	  Falcon030 in TOS4 (030) mode (works fine when executed from HDD)
-	- works perfectly on CT60
-- CD Tools 2.10
-	- based on MetaDOS 2.62, don't run CACHEON.PRG before METADOS.PRG on CT60
-	- otherwise works perfectly in both 030 and 060 mode
-- Spin! 0.34
-	- based on MetaDOS 2.74, disabled cache doesn't help on CT60!
-- BetaDOS 3.12
-	- don't run CACHEON.PRG before BETADOS.PRG on CT60
-	- otherwise works perfectly in both 030 and 060 mode
-
-The MetaDOS API allows mixing various components:
+CPU time for the engine itself.
+
+MetaDOS API allows mixing various components:
 
 - extendos.prg / metados.prg / betados.prg
 - hs-cdrom.bos (CD Tools) / spin_sd.bos (Spin!)
@@ -449,10 +436,13 @@ The MetaDOS API allows mixing various components:
   unidrive.dos (ExtenDOS)
 - cd.bos from ExtenDOS strictly requires unidrive.dos
 
-So to mitigate the loading bug you can (in order of user friendliness):
+There is a bug in ExtenDOS 4.10's cd.bos: under very specific circumstances
+ScummVM seemingly freezes while seeking for a file on CD (black screen). I'm
+working with the author on fixing this problem, in the meantime you can:
 
 - Copy data files from CD to hard disk (makes sense also for speed reasons),
   and ignore the message box with advice about ripping tracks from CD.
+- Reconfigure ExtenDOS to use its own SCSI routines (disabled by default).
 - Edit extendos.cnf and replace cd.bos with e.g. spin_sd.bos.
 - Not use ExtenDOS.
 
@@ -490,6 +480,9 @@ So how do you know which frequency to set as "output_rate" ? This is where
 converters are being used and for what input/values. So you can easily verify
 whether the given game's demands match your setting.
 
+Btw, you can use the same command line parameter for showing what MIDI mode is
+being used (Roland GS, MT-32, General MIDI), sometimes it is not very obvious.
+
 Unfortunately, currently per-game "output_rate" / "output_channels" is not
 possible but this may change in the future.
 
@@ -509,8 +502,8 @@ Changes to upstream
 There are a few features that have been disabled or changed and are not
 possible / plausible to merge into upstream:
 
-- This port contains an experimental / pending optimisations to the SCUMM
-  engine and audio mixing. I'll try to get them merged in the next release.
+- This port contains an experimental / pending optimisations to audio mixing.
+  I'll try to get them merged in the next release.
 
 
 Known issues
diff --git a/backends/platform/atari/readme.txt.in b/backends/platform/atari/readme.txt.in
index 7c898ab9f9d..59787190947 100644
--- a/backends/platform/atari/readme.txt.in
+++ b/backends/platform/atari/readme.txt.in
@@ -426,22 +426,9 @@ ScummVM has supported native audio cd playback for a long time. It is even
 enabled in the FireBee (SDL) build. However native support in the Atari builds
 has been added just recently. This feature can be used to free the audio mixer
 from loading, decoding and mixing game music with sound effects, leaving more
-CPU time for the engine itself. The following configurations have been tested:
-
-- ExtenDOS 4.10
-	- its cd.bos has a bug preventing ScummVM loading a file from CD on
-	  Falcon030 in TOS4 (030) mode (works fine when executed from HDD)
-	- works perfectly on CT60
-- CD Tools 2.10
-	- based on MetaDOS 2.62, don't run CACHEON.PRG before METADOS.PRG on CT60
-	- otherwise works perfectly in both 030 and 060 mode
-- Spin! 0.34
-	- based on MetaDOS 2.74, disabled cache doesn't help on CT60!
-- BetaDOS 3.12
-	- don't run CACHEON.PRG before BETADOS.PRG on CT60
-	- otherwise works perfectly in both 030 and 060 mode
-
-The MetaDOS API allows mixing various components:
+CPU time for the engine itself.
+
+MetaDOS API allows mixing various components:
 
 - extendos.prg / metados.prg / betados.prg
 - hs-cdrom.bos (CD Tools) / spin_sd.bos (Spin!)
@@ -449,10 +436,13 @@ The MetaDOS API allows mixing various components:
   unidrive.dos (ExtenDOS)
 - cd.bos from ExtenDOS strictly requires unidrive.dos
 
-So to mitigate the loading bug you can (in order of user friendliness):
+There is a bug in ExtenDOS 4.10's cd.bos: under very specific circumstances
+ScummVM seemingly freezes while seeking for a file on CD (black screen). I'm
+working with the author on fixing this problem, in the meantime you can:
 
 - Copy data files from CD to hard disk (makes sense also for speed reasons),
   and ignore the message box with advice about ripping tracks from CD.
+- Reconfigure ExtenDOS to use its own SCSI routines (disabled by default).
 - Edit extendos.cnf and replace cd.bos with e.g. spin_sd.bos.
 - Not use ExtenDOS.
 
@@ -490,6 +480,9 @@ So how do you know which frequency to set as "output_rate" ? This is where
 converters are being used and for what input/values. So you can easily verify
 whether the given game's demands match your setting.
 
+Btw, you can use the same command line parameter for showing what MIDI mode is
+being used (Roland GS, MT-32, General MIDI), sometimes it is not very obvious.
+
 Unfortunately, currently per-game "output_rate" / "output_channels" is not
 possible but this may change in the future.
 
@@ -509,8 +502,8 @@ Changes to upstream
 There are a few features that have been disabled or changed and are not
 possible / plausible to merge into upstream:
 
-- This port contains an experimental / pending optimisations to the SCUMM
-  engine and audio mixing. I'll try to get them merged in the next release.
+- This port contains an experimental / pending optimisations to audio mixing.
+  I'll try to get them merged in the next release.
 
 
 Known issues


Commit: 8dde5c814ee7a80e64a5952881a8a42b112d845a
    https://github.com/scummvm/scummvm/commit/8dde5c814ee7a80e64a5952881a8a42b112d845a
Author: Miro Kropacek (miro.kropacek at gmail.com)
Date: 2026-06-15T10:46:08+10:00

Commit Message:
NEWS: Add Atari news

Changed paths:
    NEWS.md


diff --git a/NEWS.md b/NEWS.md
index edd83a15104..24c60671444 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -65,6 +65,11 @@ For a more comprehensive changelog of the latest experimental code, see:
    - Fixed holomap model rendering.
    - Disable autosaves while driving a vehicle.
 
+Atari port:
+   - Added native CDDA support.
+   - Added plugin support (now ScummVM can run with as little as a few megs of RAM).
+   - Added native YM2149 support (currently usable for Elvira 1 and 2)
+
  PS3 port:
    - Added support for running ScummVM engines as separate modules/executables.
      This saves about 90 MB of RAM. It is enabled only for release packages.



From noreply at scummvm.org  Mon Jun 15 06:44:51 2026
From: noreply at scummvm.org (bluegr)
Date: Mon, 15 Jun 2026 06:44:51 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 8b8880c2de31adf7c5451fdad6a7e6826a7e8116
Message-ID: <20260615064451.05F917005BC@ron.scummvm.net>

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:
c6d5853cbc NANCY: Separate cursors set from scripts and from code
a9112fa2cb NANCY: Add a temp hack for records without dependencies in Nancy 10
03a63a7ee3 NANCY: Fixes for the cellphone UI
2621224cd0 NANCY: Fixes for the Nancy10 sortpuzzle
8b8880c2de NANCY: Fix cursor after placing an item in the inventory in Nancy10


Commit: c6d5853cbc7841f7f65455542a4f44984e6c7385
    https://github.com/scummvm/scummvm/commit/c6d5853cbc7841f7f65455542a4f44984e6c7385
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-06-15T09:44:37+03:00

Commit Message:
NANCY: Separate cursors set from scripts and from code

This allows us to skip the Nancy 10 cursor mapping for cases where the
cursor is set directly by game scripts

Changed paths:
    engines/nancy/action/actionmanager.cpp
    engines/nancy/action/actionrecord.h
    engines/nancy/action/datarecords.h
    engines/nancy/action/interactivevideo.cpp
    engines/nancy/action/navigationrecords.h
    engines/nancy/action/puzzle/multibuildpuzzle.cpp
    engines/nancy/action/puzzle/orderingpuzzle.cpp
    engines/nancy/action/puzzle/rippedletterpuzzle.cpp
    engines/nancy/cursor.cpp
    engines/nancy/cursor.h


diff --git a/engines/nancy/action/actionmanager.cpp b/engines/nancy/action/actionmanager.cpp
index 453179b3479..7cd07675549 100644
--- a/engines/nancy/action/actionmanager.cpp
+++ b/engines/nancy/action/actionmanager.cpp
@@ -64,7 +64,7 @@ void ActionManager::handleInput(NancyInput &input) {
 			if (!setHoverCursor) {
 				// Hotspots may overlap, but we want the hover cursor for the first one we encounter
 				// This fixes the stairs in nancy3
-				g_nancy->_cursor->setCursorType(rec->getHoverCursor());
+				g_nancy->_cursor->setCursorType(rec->getHoverCursor(), rec->cursorSetFromScript());
 				setHoverCursor = true;
 			}
 
diff --git a/engines/nancy/action/actionrecord.h b/engines/nancy/action/actionrecord.h
index e30e8f542ec..887fbc02afa 100644
--- a/engines/nancy/action/actionrecord.h
+++ b/engines/nancy/action/actionrecord.h
@@ -114,6 +114,7 @@ public:
 	virtual void onPause(bool pause) {}
 
 	virtual CursorManager::CursorType getHoverCursor() const { return CursorManager::kHotspot; }
+	virtual bool cursorSetFromScript() const { return false; }
 	virtual void handleInput(NancyInput &input) {}
 
 	// Used for debugging
diff --git a/engines/nancy/action/datarecords.h b/engines/nancy/action/datarecords.h
index d200227d2f6..9022aac7a49 100644
--- a/engines/nancy/action/datarecords.h
+++ b/engines/nancy/action/datarecords.h
@@ -38,6 +38,7 @@ public:
 	void execute() override;
 
 	CursorManager::CursorType getHoverCursor() const override { return (CursorManager::CursorType)_cursorType; }
+	bool cursorSetFromScript() const override { return true; }
 
 protected:
 	Common::String getRecordTypeName() const override { return "TableIndexSetValueHS"; }
@@ -121,6 +122,7 @@ public:
 	void execute() override;
 
 	CursorManager::CursorType getHoverCursor() const override;
+	bool cursorSetFromScript() const override { return true; }
 
 	CursorManager::CursorType _hoverCursor = CursorManager::kHotspot;
 	Common::Array<HotspotDescription> _hotspots;
diff --git a/engines/nancy/action/interactivevideo.cpp b/engines/nancy/action/interactivevideo.cpp
index b50851bf8e8..21ea17c58b6 100644
--- a/engines/nancy/action/interactivevideo.cpp
+++ b/engines/nancy/action/interactivevideo.cpp
@@ -130,7 +130,7 @@ void InteractiveVideo::handleInput(NancyInput &input) {
 				if (NancySceneState.getViewport().convertViewportToScreen(hotspot.hotspot).contains(input.mousePos)) {
 					// Mouse is in a hotspot, change cursor and set flag if clicked
 					if (hotspot.cursorID >= 0 && _cursors[hotspot.cursorID] >= 0) {
-						g_nancy->_cursor->setCursorType((CursorManager::CursorType)_cursors[hotspot.cursorID]);
+						g_nancy->_cursor->setCursorType((CursorManager::CursorType)_cursors[hotspot.cursorID], true);
 					}
 
 					if (input.input & NancyInput::kLeftMouseButtonUp) {
@@ -144,7 +144,7 @@ void InteractiveVideo::handleInput(NancyInput &input) {
 			// Mouse is not in a hotspot for the frame, check if we have a default action
 			if (frame.triggerOnNoHotspot) {
 				if (frame.noHSCursorID >= 0 && _cursors[frame.noHSCursorID] >= 0) {
-					g_nancy->_cursor->setCursorType((CursorManager::CursorType)_cursors[frame.noHSCursorID]);
+					g_nancy->_cursor->setCursorType((CursorManager::CursorType)_cursors[frame.noHSCursorID], true);
 				}
 
 				if (input.input & NancyInput::kLeftMouseButtonUp) {
diff --git a/engines/nancy/action/navigationrecords.h b/engines/nancy/action/navigationrecords.h
index 4cf3a750f68..45647d57f88 100644
--- a/engines/nancy/action/navigationrecords.h
+++ b/engines/nancy/action/navigationrecords.h
@@ -52,6 +52,7 @@ public:
 	void execute() override;
 
 	CursorManager::CursorType getHoverCursor() const override { return _hoverCursor; }
+	bool cursorSetFromScript() const override { return true; }
 
 	HotspotDescription _sceneHotspot;
 
@@ -115,6 +116,7 @@ public:
 	void execute() override;
 
 	CursorManager::CursorType getHoverCursor() const override { return _hoverCursor; }
+	bool cursorSetFromScript() const override { return _dynamicCursor; }
 
 	HotspotDescription _hotspotDesc;
 	bool _isTerse = false;
diff --git a/engines/nancy/action/puzzle/multibuildpuzzle.cpp b/engines/nancy/action/puzzle/multibuildpuzzle.cpp
index 5e52f8e20d3..4655799b4da 100644
--- a/engines/nancy/action/puzzle/multibuildpuzzle.cpp
+++ b/engines/nancy/action/puzzle/multibuildpuzzle.cpp
@@ -493,7 +493,7 @@ void MultiBuildPuzzle::handleInput(NancyInput &input) {
 	}
 
 	if (_selectedPiece != -1) {
-		g_nancy->_cursor->setCursorType(dragCursor);
+		g_nancy->_cursor->setCursorType(dragCursor, true);
 
 		if (input.input & NancyInput::kLeftMouseButtonUp) {
 			int sel = _selectedPiece;
@@ -563,7 +563,7 @@ void MultiBuildPuzzle::handleInput(NancyInput &input) {
 	}
 
 	if (topmost != -1) {
-		g_nancy->_cursor->setCursorType(dragCursor);
+		g_nancy->_cursor->setCursorType(dragCursor, true);
 
 		if (input.input & NancyInput::kLeftMouseButtonUp) {
 			Piece &pp = _pieces[topmost];
diff --git a/engines/nancy/action/puzzle/orderingpuzzle.cpp b/engines/nancy/action/puzzle/orderingpuzzle.cpp
index 942031d0714..8270180851b 100644
--- a/engines/nancy/action/puzzle/orderingpuzzle.cpp
+++ b/engines/nancy/action/puzzle/orderingpuzzle.cpp
@@ -511,9 +511,9 @@ void OrderingPuzzle::handleInput(NancyInput &input) {
 		if (NancySceneState.getViewport().convertViewportToScreen(_hotspots[i]).contains(input.mousePos)) {
 			// Set the custom cursor for nancy8+ PianoPuzzle
 			if (NancySceneState.getViewport().convertViewportToScreen(_specialCursor1Dest).contains(input.mousePos)) {
-				g_nancy->_cursor->setCursorType((CursorManager::CursorType)_specialCursor1Id);
+				g_nancy->_cursor->setCursorType((CursorManager::CursorType)_specialCursor1Id, true);
 			} else if (NancySceneState.getViewport().convertViewportToScreen(_specialCursor2Dest).contains(input.mousePos)) {
-				g_nancy->_cursor->setCursorType((CursorManager::CursorType)_specialCursor2Id);
+				g_nancy->_cursor->setCursorType((CursorManager::CursorType)_specialCursor2Id, true);
 			} else {
 				g_nancy->_cursor->setCursorType(CursorManager::kHotspot);
 			}
diff --git a/engines/nancy/action/puzzle/rippedletterpuzzle.cpp b/engines/nancy/action/puzzle/rippedletterpuzzle.cpp
index f6e7afa2ddd..da4957803ed 100644
--- a/engines/nancy/action/puzzle/rippedletterpuzzle.cpp
+++ b/engines/nancy/action/puzzle/rippedletterpuzzle.cpp
@@ -439,7 +439,10 @@ void RippedLetterPuzzle::handleInput(NancyInput &input) {
 	if (_puzzleState->_pickedUpPieceID == -1) {
 		// No piece picked up, check the exit hotspot
 		if (NancySceneState.getViewport().convertViewportToScreen(_exitHotspot).contains(input.mousePos)) {
-			g_nancy->_cursor->setCursorType(_customCursorID != -1 ? (CursorManager::CursorType)_customCursorID : g_nancy->_cursor->_puzzleExitCursor);
+			if (_customCursorID != -1)
+				g_nancy->_cursor->setCursorType((CursorManager::CursorType)_customCursorID, true);
+			else
+				g_nancy->_cursor->setCursorType(g_nancy->_cursor->_puzzleExitCursor);
 
 			if (input.input & NancyInput::kLeftMouseButtonUp) {
 				// Player has clicked, exit
diff --git a/engines/nancy/cursor.cpp b/engines/nancy/cursor.cpp
index f39c61e34c7..eb0df46e0b0 100644
--- a/engines/nancy/cursor.cpp
+++ b/engines/nancy/cursor.cpp
@@ -123,7 +123,7 @@ void CursorManager::init(Common::SeekableReadStream *chunkStream) {
 
 	g_nancy->_resource->loadImage(inventoryCursorsImageName, _invCursorsSurface);
 
-	setCursor(kNormalArrow, -1);
+	setCursor(kNormalArrow, -1, false);
 	showCursor(false);
 
 	_isInitialized = true;
@@ -133,7 +133,7 @@ void CursorManager::init(Common::SeekableReadStream *chunkStream) {
 	delete chunkStream;
 }
 
-uint CursorManager::resolveNancy10CursorID(CursorType type, int16 itemID) {
+uint CursorManager::resolveNancy10CursorID(CursorType type, int16 itemID, bool setFromScript) {
 	// Item-held variants. The Nancy 10+ chunk reserves `numItems ?? 2`
 	// slots after the two 37-entry system arrays (= _numCursorTypes * 2),
 	// each item getting one [idle, hotspot] pair. Held items only
@@ -147,6 +147,9 @@ uint CursorManager::resolveNancy10CursorID(CursorType type, int16 itemID) {
 		return itemsOffset + (uint)itemID * 2 + variant;
 	}
 
+	if (setFromScript)
+		return type;
+
 	// System cursors: translate the legacy CursorType to the matching
 	// kNew* idle slot. Each Nancy 10+ cursor type T occupies a pair
 	// (T*2, T*2+1) in the chunk ??? idle followed by hotspot. We always
@@ -177,7 +180,7 @@ uint CursorManager::resolveNancy10CursorID(CursorType type, int16 itemID) {
 	}
 }
 
-void CursorManager::setCursor(CursorType type, int16 itemID) {
+void CursorManager::setCursor(CursorType type, int16 itemID, bool setFromScript) {
 	if (!_isInitialized)
 		return;
 
@@ -191,7 +194,7 @@ void CursorManager::setCursor(CursorType type, int16 itemID) {
 	_hasItem = false;
 
 	if (gameType >= kGameTypeNancy10) {
-		_curCursorID = resolveNancy10CursorID(type, itemID);
+		_curCursorID = resolveNancy10CursorID(type, itemID, setFromScript);
 		return;
 	}
 
@@ -304,12 +307,12 @@ void CursorManager::setCursor(CursorType type, int16 itemID) {
 	_curCursorID = (uint)(itemID * _numCursorTypes) + itemsOffset + (uint)type;
 }
 
-void CursorManager::setCursorType(CursorType type) {
-	setCursor(type, _curItemID);
+void CursorManager::setCursorType(CursorType type, bool setFromScript) {
+	setCursor(type, _curItemID, setFromScript);
 }
 
 void CursorManager::setCursorItemID(int16 itemID) {
-	setCursor(_curCursorType, itemID);
+	setCursor(_curCursorType, itemID, false);
 }
 
 void CursorManager::warpCursor(const Common::Point &pos) {
diff --git a/engines/nancy/cursor.h b/engines/nancy/cursor.h
index 0dbf7c4a336..f443eb57f4c 100644
--- a/engines/nancy/cursor.h
+++ b/engines/nancy/cursor.h
@@ -95,8 +95,8 @@ public:
 	void init(Common::SeekableReadStream *chunkStream);
 
 	// Change the current cursor ID. Does not change the graphic
-	void setCursor(CursorType type, int16 itemID);
-	void setCursorType(CursorType type);
+	void setCursor(CursorType type, int16 itemID, bool setFromScript);
+	void setCursorType(CursorType type, bool setFromScript = false);
 	void setCursorItemID(int16 itemID);
 	void showCursor(bool shouldShow);
 
@@ -115,7 +115,7 @@ private:
 	void adjustCursorHotspot();
 
 	// Resolve a CursorType + held-item pair to a Nancy 10+ cursor ID.
-	uint resolveNancy10CursorID(CursorType type, int16 itemID);
+	uint resolveNancy10CursorID(CursorType type, int16 itemID, bool setFromScript);
 
 	struct Cursor {
 		Common::Rect bounds;


Commit: a9112fa2cb72a9ac00162716f94e0698d57bedb3
    https://github.com/scummvm/scummvm/commit/a9112fa2cb72a9ac00162716f94e0698d57bedb3
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-06-15T09:44:38+03:00

Commit Message:
NANCY: Add a temp hack for records without dependencies in Nancy 10

Changed paths:
    engines/nancy/action/secondarymovie.cpp


diff --git a/engines/nancy/action/secondarymovie.cpp b/engines/nancy/action/secondarymovie.cpp
index 0aef5ece44b..5417d3d7772 100644
--- a/engines/nancy/action/secondarymovie.cpp
+++ b/engines/nancy/action/secondarymovie.cpp
@@ -157,6 +157,17 @@ void PlaySecondaryMovie::onPause(bool pause) {
 void PlaySecondaryMovie::execute() {
 	switch (_state) {
 	case kBegin:
+		// HACK: In Nancy 10, scene 2987, there are two PlaySecondaryMovie records that play
+		// the same video, but have no dependencies. The first video leads to the losing scene,
+		// while the second one leads to the winning scene. Since none of the two records has a
+		// dependency, the first one will always be executed. It seems like there should be
+		// a check to prevent the first record from being executed, but it wasn't possible to
+		// find it. Don't start the first record for now, so that the second one can be
+		// executed and the player can proceed.
+		// TODO: Find out what the original engine does in this case, and implement it properly.
+		if (g_nancy->getGameType() == kGameTypeNancy10 && _videoSceneChange == kMovieSceneChange && _sceneChange.sceneID == 2989)
+			return;
+
 		init();
 		registerGraphics();
 		g_nancy->_sound->loadSound(_sound);


Commit: 03a63a7ee35a7aa8b9369557dc3475cff56cf1fd
    https://github.com/scummvm/scummvm/commit/03a63a7ee35a7aa8b9369557dc3475cff56cf1fd
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-06-15T09:44:38+03:00

Commit Message:
NANCY: Fixes for the cellphone UI

- Add hovered state for the up/down arrows
- Fix display of e-mail messages

Changed paths:
    engines/nancy/ui/cellphonepopup.cpp
    engines/nancy/ui/cellphonepopup.h


diff --git a/engines/nancy/ui/cellphonepopup.cpp b/engines/nancy/ui/cellphonepopup.cpp
index dcec5b05f61..5444205603f 100644
--- a/engines/nancy/ui/cellphonepopup.cpp
+++ b/engines/nancy/ui/cellphonepopup.cpp
@@ -252,6 +252,8 @@ void CellPhonePopup::open() {
 	_directoryScroll = 0;
 	_directorySelection = 0;
 	_closeButtonHovered = false;
+	_scrollUpHovered = false;
+	_scrollDownHovered = false;
 
 	drawChrome();
 	drawScreenContent();
@@ -1002,20 +1004,31 @@ const UICL::ThreeRectWidget &CellPhonePopup::scrollDownButton() const {
 
 void CellPhonePopup::drawDirectoryArrows() {
 	// Up/down scroll arrows are not in the chrome image; blit on every redraw.
+	// The pressed (lit) sprite is used while the cursor is over the arrow.
 	const UICL::ThreeRectWidget &up = scrollUpButton();
 	const UICL::ThreeRectWidget &down = scrollDownButton();
 
 	const Common::Point chunkOrigin(_screenPosition.left, _screenPosition.top);
 
-	if (!up.srcRectIdle.isEmpty() && !up.destRect.isEmpty()) {
-		_drawSurface.blitFrom(_spritesImage, up.srcRectIdle,
-								Common::Point(up.destRect.left - chunkOrigin.x,
-												up.destRect.top - chunkOrigin.y));
+	if (!up.destRect.isEmpty()) {
+		const Common::Rect &upSrc = (_scrollUpHovered && !up.srcRectPressed.isEmpty())
+			? up.srcRectPressed
+			: up.srcRectIdle;
+		if (!upSrc.isEmpty()) {
+			_drawSurface.blitFrom(_spritesImage, upSrc,
+									Common::Point(up.destRect.left - chunkOrigin.x,
+													up.destRect.top - chunkOrigin.y));
+		}
 	}
-	if (!down.srcRectIdle.isEmpty() && !down.destRect.isEmpty()) {
-		_drawSurface.blitFrom(_spritesImage, down.srcRectIdle,
-								Common::Point(down.destRect.left - chunkOrigin.x,
-												down.destRect.top - chunkOrigin.y));
+	if (!down.destRect.isEmpty()) {
+		const Common::Rect &downSrc = (_scrollDownHovered && !down.srcRectPressed.isEmpty())
+			? down.srcRectPressed
+			: down.srcRectIdle;
+		if (!downSrc.isEmpty()) {
+			_drawSurface.blitFrom(_spritesImage, downSrc,
+									Common::Point(down.destRect.left - chunkOrigin.x,
+													down.destRect.top - chunkOrigin.y));
+		}
 	}
 
 	// Selection indicator (dirArrowSrc sprite) at the dirCursorSrc column,
@@ -1174,7 +1187,12 @@ bool CellPhonePopup::consumeReturnScene(SceneChangeDescription &out) {
 // --------------------------------------------------------------------
 
 int CellPhonePopup::rowPitch() const {
-	// Original (FUN_004d8476): pitch = dirCursorSrc.height + 8.
+	// Email rows are sized by the unread/selected icon so they don't
+	// overlap; directory and search lists use the compact arrow-cursor
+	// pitch.
+	if (_screenState == kEmailList && !_uiclData->emailIconUnread.isEmpty()) {
+		return _uiclData->emailIconUnread.height() + 1;
+	}
 	const Common::Rect &cursor = _uiclData->dirCursorSrc;
 	if (!cursor.isEmpty()) {
 		return cursor.height() + 8;
@@ -1183,7 +1201,11 @@ int CellPhonePopup::rowPitch() const {
 }
 
 int CellPhonePopup::rowTopScreen() const {
-	// First row's Y (screen). Original anchors on dirCursorSrc.top - 5.
+	// Email list anchors on the zoomed-chrome list container; everything
+	// else stacks under the arrow-cursor row.
+	if (_screenState == kEmailList && !_uiclData->emailListContainer.isEmpty()) {
+		return _uiclData->emailListContainer.top;
+	}
 	const Common::Rect &cursor = _uiclData->dirCursorSrc;
 	if (!cursor.isEmpty()) {
 		return cursor.top - 5;
@@ -1196,7 +1218,9 @@ uint CellPhonePopup::maxDirectoryRows() const {
 	if (pitch <= 0) {
 		return 0;
 	}
-	const int yLimit = _uiclData->welcomeScreen.destRect.bottom;
+	const int yLimit = (_screenState == kEmailList && !_uiclData->emailListContainer.isEmpty())
+		? _uiclData->emailListContainer.bottom
+		: _uiclData->welcomeScreen.destRect.bottom;
 	int y = rowTopScreen();
 	uint count = 0;
 	while (y + pitch < yLimit) {
@@ -1473,6 +1497,19 @@ void CellPhonePopup::handleInput(NancyInput &input) {
 
 	const Common::Point chunkMouse = mouseToChunkCoords(input.mousePos);
 
+	// Light the up/down arrows on hover in any state that uses them.
+	const bool arrowsActive = _screenState == kDirectory || isLinkListMode() ||
+								_screenState == kContentView;
+	const bool overUp = arrowsActive &&
+			scrollUpButton().destRect.contains(chunkMouse);
+	const bool overDown = arrowsActive && !overUp &&
+			scrollDownButton().destRect.contains(chunkMouse);
+	if (overUp != _scrollUpHovered || overDown != _scrollDownHovered) {
+		_scrollUpHovered = overUp;
+		_scrollDownHovered = overDown;
+		drawScreenContent();
+	}
+
 	// Help "?" button: opens the help page in the content view. Reachable
 	// from any interactive state (except when already showing it).
 	if (!_uiclData->helpButton.destRect.isEmpty() && !_uiclData->helpTextKey.empty() &&
diff --git a/engines/nancy/ui/cellphonepopup.h b/engines/nancy/ui/cellphonepopup.h
index 9a2759b502b..f60f94a463c 100644
--- a/engines/nancy/ui/cellphonepopup.h
+++ b/engines/nancy/ui/cellphonepopup.h
@@ -193,6 +193,8 @@ private:
 	Graphics::ManagedSurface _spritesImage;
 
 	bool _closeButtonHovered = false;
+	bool _scrollUpHovered = false;
+	bool _scrollDownHovered = false;
 
 	ScreenState _screenState = kWelcome;
 


Commit: 2621224cd0358860ab80aa76eac805c06b03a2e8
    https://github.com/scummvm/scummvm/commit/2621224cd0358860ab80aa76eac805c06b03a2e8
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-06-15T09:44:40+03:00

Commit Message:
NANCY: Fixes for the Nancy10 sortpuzzle

- Fix gem graphics when placing them
- Use the smaller grab cursor when holding gems
- Add a cheat to show the grid in the console when pressing space

Changed paths:
    engines/nancy/action/puzzle/sortpuzzle.cpp
    engines/nancy/cursor.cpp
    engines/nancy/cursor.h


diff --git a/engines/nancy/action/puzzle/sortpuzzle.cpp b/engines/nancy/action/puzzle/sortpuzzle.cpp
index c2cd20dfb0f..3c14abf371b 100644
--- a/engines/nancy/action/puzzle/sortpuzzle.cpp
+++ b/engines/nancy/action/puzzle/sortpuzzle.cpp
@@ -38,7 +38,6 @@ namespace Action {
 // Tiles within a color group share their board sprite (srcRow, srcCol); the
 // hidden `value` field disambiguates them, which is visible in the back of
 // each gem, when picking it up.
-// TODO: Use correct cursor type (hand instead of eyeglass) when picking up gems.
 static void packGrid(const SortPuzzle::Cell grid[][SortPuzzle::kMaxCols], uint16 rows, uint16 cols, Common::Array<int16> &out) {
 	out.clear();
 	out.push_back((int16)rows);
@@ -258,8 +257,10 @@ void SortPuzzle::execute() {
 }
 
 Common::Rect SortPuzzle::cellRect(int row, int col) const {
-	int x = (int)_originX + col * ((int)_spacingX + _cellWidth);
-	int y = (int)_originY + row * ((int)_spacingY + _cellHeight);
+	// Original uses inclusive-coordinate width (right_raw - left_raw) for the
+	// per-col / per-row step, which is cellWidth-1 in our exclusive convention.
+	int x = (int)_originX + col * ((int)_spacingX + _cellWidth  - 1);
+	int y = (int)_originY + row * ((int)_spacingY + _cellHeight - 1);
 	return Common::Rect(x, y, x + _cellWidth, y + _cellHeight);
 }
 
@@ -283,6 +284,20 @@ void SortPuzzle::handleInput(NancyInput &input) {
 	Common::Rect vpScreen = NancySceneState.getViewport().getScreenPosition();
 	Common::Point mouseVP = input.mousePos - Common::Point(vpScreen.left, vpScreen.top);
 
+	// Cheat: show the grid with the value of each gem in the console
+	if (!input.otherKbdInput.empty() && input.otherKbdInput[0].keycode == Common::KEYCODE_SPACE) {
+		debug("-----");
+		for (int r = 0; r < (int)_rows; ++r) {
+			Common::String line;
+			for (int c = 0; c < (int)_cols; ++c) {
+				const Cell &cur = _current[r][c];
+				line += Common::String::format("%d ", cur.value);
+			}
+			debug(line.c_str());
+		}
+		debug("-----");
+	}
+
 	if (_hasHeld && _heldDrawPos != mouseVP) {
 		_heldDrawPos = mouseVP;
 		redraw();
@@ -300,7 +315,7 @@ void SortPuzzle::handleInput(NancyInput &input) {
 		return;
 	}
 
-	g_nancy->_cursor->setCursorType(_hasHeld ? CursorManager::kDragHand
+	g_nancy->_cursor->setCursorType(_hasHeld ? CursorManager::kDropHand
 	                                         : CursorManager::kHotspot);
 	if (!(input.input & NancyInput::kLeftMouseButtonUp))
 		return;
diff --git a/engines/nancy/cursor.cpp b/engines/nancy/cursor.cpp
index eb0df46e0b0..6e9ad58c165 100644
--- a/engines/nancy/cursor.cpp
+++ b/engines/nancy/cursor.cpp
@@ -159,6 +159,7 @@ uint CursorManager::resolveNancy10CursorID(CursorType type, int16 itemID, bool s
 	case kHotspot:              return kNewHotspot;
 	case kHotspotTalk:          return kNewHotspotTalk;
 	case kDragHand:             return kNewDragHand;
+	case kDropHand:             return kNewDropHand;
 	case kNormalArrow:          return kNewNormalArrow;
 	case kHotspotArrow:         return kNewHotspotArrow;
 	case kExit:                 return kNewExit;
diff --git a/engines/nancy/cursor.h b/engines/nancy/cursor.h
index f443eb57f4c..9fdbd8f1ae0 100644
--- a/engines/nancy/cursor.h
+++ b/engines/nancy/cursor.h
@@ -57,6 +57,7 @@ public:
 		kHotspotArrow			= 21,
 		kHotspotTalk			= 22,	// Speech-bubble hover cursor (Nancy 10+)
 		kDragHand				= 23,	// Hand cursor used when dragging an item (Nancy 10+)
+		kDropHand				= 24,	// Drop-hand cursor used while a piece is held over a target (Nancy 10+)
 
 		// Cursors in Nancy10 and newer games. The CURS chunk holds 37 system
 		// cursor types in pairs; type T's idle slot is (T*2) and its hotspot


Commit: 8b8880c2de31adf7c5451fdad6a7e6826a7e8116
    https://github.com/scummvm/scummvm/commit/8b8880c2de31adf7c5451fdad6a7e6826a7e8116
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-06-15T09:44:40+03:00

Commit Message:
NANCY: Fix cursor after placing an item in the inventory in Nancy10

Changed paths:
    engines/nancy/ui/inventorypopup.cpp


diff --git a/engines/nancy/ui/inventorypopup.cpp b/engines/nancy/ui/inventorypopup.cpp
index ddada1f2ef2..3ea138714fd 100644
--- a/engines/nancy/ui/inventorypopup.cpp
+++ b/engines/nancy/ui/inventorypopup.cpp
@@ -314,13 +314,10 @@ void InventoryPopup::handleInput(NancyInput &input) {
 		input.mousePos.x - _screenPosition.left + _uiivData->header.normalDestRect.left,
 		input.mousePos.y - _screenPosition.top  + _uiivData->header.normalDestRect.top);
 
-	// Nancy 10+ only blends the held-item sprite onto kNormal / kHotspot
-	// cursors. Switch to kHotspot when holding so the item stays visible
-	// while hovering popup widgets.
-	const CursorManager::CursorType hoverCursor =
-		NancySceneState.getHeldItem() == -1
-			? CursorManager::kHotspotArrow
-			: CursorManager::kHotspot;
+	// kHotspot is the highlighted magnifier and also the cursor that
+	// blends a held-item sprite on Nancy 10+, so it works in both
+	// "empty hand" (plain magnifier) and "carrying" (magnifier + item) states.
+	const CursorManager::CursorType hoverCursor = CursorManager::kHotspot;
 
 	// Scrollbar interaction takes priority while dragging.
 	const UISliderRecord &slider = _uiivData->header.slider;



From noreply at scummvm.org  Mon Jun 15 08:16:05 2026
From: noreply at scummvm.org (ScummVM-Translations)
Date: Mon, 15 Jun 2026 08:16:05 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm-web master ->
 55adc342c0214a764bc8fd62c5b5f01003c42171
Message-ID: <20260615081605.513527005BC@ron.scummvm.net>

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

Summary:
55adc342c0 L10N: Update translation (Spanish) (News)


Commit: 55adc342c0214a764bc8fd62c5b5f01003c42171
    https://github.com/scummvm/scummvm-web/commit/55adc342c0214a764bc8fd62c5b5f01003c42171
Author: IlDucci (pajaroloco_2 at hotmail.com)
Date: 2026-06-15T08:15:58Z

Commit Message:
L10N: Update translation (Spanish) (News)

Currently translated at 94.3% (830 of 880 strings)

Changed paths:
    data/es/news.json


diff --git a/data/es/news.json b/data/es/news.json
index 832bb44b..ee13652c 100644
--- a/data/es/news.json
+++ b/data/es/news.json
@@ -1754,5 +1754,9 @@
     "20260613": {
         "title": "??El carnaval animado abre sus puertas!",
         "content": "??Pasen y vean, ni??os y ni??as! _Hanna-Barbera's Cartoon Carnival_ ya est?? abierto a pruebas p??blicas en ScummVM.\n\n_Cartoon Carnival_ fue dise??ado y publicado originalmente por Funhouse Design para la Philips CD-i en 1993 y posteriormente convertido a Windows y Macintosh en 1995, y os presenta a un buen plantel de conocidos personajes de Hanna-Barbera en una colecci??n de minijuegos estilo arcade. Scooby-Doo, Pedro Picapiedra, S??per S??nico, Don Gato y sus amigos participar??n en toda clase de pruebas, pasando por laberintos y juegos de encontrar patrones hasta llegar a capturas de globos y rondas de preguntas y respuestas.\n\nM??s que una aventura tradicional, esta es una obra de entretenimiento multimedia de mediados de la d??cada de 1990 completa con animaciones, voces y m??sicas. Tambi??n es uno de esos juegos licenciados que pueden olvidarse f??cilmente, as?? que estamos especialmente contentos de poder hacer que sea jugable en sistemas modernos.\n\nEn estos momentos solo son compatibles las versiones para Windows y Macintosh. La versi??n original para CD-i no es compatible.\n\nPara probar el juego necesitar??is la [??ltima compilaci??n diaria](https://www.scummvm.org/downloads/#daily) y o bien una copia original de la versi??n para Windows o para Macintosh, o bien de su demo (que solo hemos podido localizar en italiano). Para saber cu??les son los archivos de datos necesarios, leed [su p??gina en la wiki](https://wiki.scummvm.org/index.php?title=Cartoon_Carnival).\n\nComo de costumbre, os rogamos que inform??is de cualquier fallo en [nuestro gestor de fallos (en ingl??s)](https://bugs.scummvm.org/) e indiqu??is tambi??n la versi??n que hab??is probado, vuestra plataforma, y que adjunt??is cualquier captura de pantalla o partida guardada que puedan ser ??tiles.\n\n??Pasen y vean, elijan juegos y ay??dennos a que el carnaval vaya como la seda!"
+    },
+    "20260614": {
+        "title": "Alfred Pelrock se une a ScummVM",
+        "content": "Buenas noticias para los fans de las aventuras: ??_Alfred Pelrock_ ya es compatible con ScummVM!\n\nPublicado originalmente en 1996, _Alfred Pelrock_ es una aventura point-and-click de humor con gr??ficos de estilo dibujos animados, acertijos estrafalarios y un sentido del humor irreverente. Tras so??ar con una princesa egipcia capturada en el pasado, Alfred se embarca en una aventura a trav??s del tiempo para rescatarla en el antiguo Egipto, donde encontrar?? situaciones curiosas y personajes exc??ntricos.\n\nAunque el juego est?? repleto de comedia y s??tira, debemos informaros de que tambi??n incluye contenidos sugerentes menores y humos ocasional adulto t??pico de su ??poca. Tambi??n debemos mencionar que el juego sali?? solo en espa??ol y que no existen traducciones oficiales.\n\nComo siempre, si pose??is una copia original del juego, pod??is descargaros la ??ltima [compilaci??n diaria de desarrollo](https://www.scummvm.org/downloads/#daily), a??adir los [archivos de datos necesarios](https://wiki.scummvm.org/index.php?title=Alfred_Pelrock:_En_Busca_de_un_Sue%C3%B1o) del juego y disfrutarlo. Tambi??n aceptamos [informes de fallos (en ingl??s)](https://bugs.scummvm.org/) y comentarios de los jugadores para mejorar la compatibilidad y poder preservar esta curiosa obra de la historia de las aventuras."
     }
 }



From noreply at scummvm.org  Mon Jun 15 09:12:17 2026
From: noreply at scummvm.org (Marisa-Chan)
Date: Mon, 15 Jun 2026 09:12:17 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 1eefcb917cba5f0f20bb3ccef7fc647ea047507e
Message-ID: <20260615091217.9FD957005BC@ron.scummvm.net>

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

Summary:
871d178d9f Init vm field before use it. PVS-Studio V670
a897f219bb Explicit init MoviePlayer and _txtInputBuffer field. PVS-Studio V730
87c82be991 Fix wrong assignment. PVS-Studio V519
fd480f5a49 Do not assign frame field twice. PVS-Studio V519
60e67e4af7 Explicit init fields. PVS-Studio V730
466c427f06 Fix lost exit. PVS-Studio V519
1eefcb917c sscanf string with buffer size. PVS-Studio V576


Commit: 871d178d9fd807b3ace7649d0ca21f768903e254
    https://github.com/scummvm/scummvm/commit/871d178d9fd807b3ace7649d0ca21f768903e254
Author: Marisa-Chan (thunder_8888 at mail.ru)
Date: 2026-06-15T16:11:30+07:00

Commit Message:
Init vm field before use it. PVS-Studio V670

Changed paths:
    engines/gamos/gamos.cpp
    engines/gamos/gamos.h


diff --git a/engines/gamos/gamos.cpp b/engines/gamos/gamos.cpp
index eec83f79876..210ea5e62e5 100644
--- a/engines/gamos/gamos.cpp
+++ b/engines/gamos/gamos.cpp
@@ -44,8 +44,8 @@ namespace Gamos {
 
 GamosEngine::GamosEngine(OSystem *syst, const GamosGameDescription *gameDesc) : Engine(syst),
 	_gameDescription(gameDesc),
-	_messageProc(this),
 	_vm(this, callbackVMCallDispatcher),
+	_messageProc(this),
 	_txtInputVMAccess(_vm),
 	_randomSource("gamos") {
 	for(uint i = 0; i < 256; i++)
diff --git a/engines/gamos/gamos.h b/engines/gamos/gamos.h
index 39a172c4e31..496bd111912 100644
--- a/engines/gamos/gamos.h
+++ b/engines/gamos/gamos.h
@@ -383,6 +383,8 @@ private:
 	Common::String _errMessage;
 
 	GameFile _gameFile;
+	
+	VM _vm;
 
 	byte _cmdByte = 0;
 
@@ -594,8 +596,6 @@ private:
 
 	Common::Array<Common::Rect> _dirtyRects;
 
-	VM _vm;
-
 	bool _needReload = false;
 
 	uint32 _engineVersion = 0x18;


Commit: a897f219bb89d040362e4c0f0708b93359ff40de
    https://github.com/scummvm/scummvm/commit/a897f219bb89d040362e4c0f0708b93359ff40de
Author: Marisa-Chan (thunder_8888 at mail.ru)
Date: 2026-06-15T16:11:30+07:00

Commit Message:
Explicit init MoviePlayer and _txtInputBuffer field. PVS-Studio V730

Changed paths:
    engines/gamos/gamos.cpp
    engines/gamos/movie.h


diff --git a/engines/gamos/gamos.cpp b/engines/gamos/gamos.cpp
index 210ea5e62e5..d70a8f17c72 100644
--- a/engines/gamos/gamos.cpp
+++ b/engines/gamos/gamos.cpp
@@ -50,6 +50,7 @@ GamosEngine::GamosEngine(OSystem *syst, const GamosGameDescription *gameDesc) :
 	_randomSource("gamos") {
 	for(uint i = 0; i < 256; i++)
 		_txtInputObjects[i] = nullptr;
+	memset(_txtInputBuffer, 0, sizeof(_txtInputBuffer));
 }
 
 GamosEngine::~GamosEngine() {
diff --git a/engines/gamos/movie.h b/engines/gamos/movie.h
index 80d26094856..7c397933f38 100644
--- a/engines/gamos/movie.h
+++ b/engines/gamos/movie.h
@@ -32,6 +32,10 @@ class GamosEngine;
 class MoviePlayer {
 public:
 
+	MoviePlayer() {
+		memset(_hdrBytes, 0, sizeof(_hdrBytes));
+	};
+
 	bool playMovie(Common::File *file, uint32 offset, GamosEngine *gamos);
 
 


Commit: 87c82be991763fb1be1e70577fdbdfa46a23518c
    https://github.com/scummvm/scummvm/commit/87c82be991763fb1be1e70577fdbdfa46a23518c
Author: Marisa-Chan (thunder_8888 at mail.ru)
Date: 2026-06-15T16:11:30+07:00

Commit Message:
Fix wrong assignment. PVS-Studio V519

Changed paths:
    engines/gamos/gamos.cpp


diff --git a/engines/gamos/gamos.cpp b/engines/gamos/gamos.cpp
index d70a8f17c72..f570432054b 100644
--- a/engines/gamos/gamos.cpp
+++ b/engines/gamos/gamos.cpp
@@ -1292,7 +1292,7 @@ uint8 GamosEngine::update(Common::Point screenSize, Common::Point mouseMove, Com
 	if (_enableInput == 0) {
 		act1 = ACT_NONE;
 		act2 = ACT_NONE;
-		_pressedKeyCode = ACT_NONE;
+		keyCode = ACT_NONE;
 	}
 
 	_pressedKeyCode = keyCode;


Commit: fd480f5a4922e6cef17d482ea9cf85eada813a8f
    https://github.com/scummvm/scummvm/commit/fd480f5a4922e6cef17d482ea9cf85eada813a8f
Author: Marisa-Chan (thunder_8888 at mail.ru)
Date: 2026-06-15T16:11:31+07:00

Commit Message:
Do not assign frame field twice. PVS-Studio V519

Changed paths:
    engines/gamos/gamos.cpp


diff --git a/engines/gamos/gamos.cpp b/engines/gamos/gamos.cpp
index f570432054b..58e99764b8c 100644
--- a/engines/gamos/gamos.cpp
+++ b/engines/gamos/gamos.cpp
@@ -3467,7 +3467,7 @@ void GamosEngine::addSubtitles(VM::Context *ctx, byte memtype, int32 offset, int
 Object *GamosEngine::addSubtitleImage(uint32 frame, int32 spr, int32 *pX, int32 y) {
 	Object *gfxObj = getFreeObject();
 	gfxObj->flags |= Object::FLAG_GRAPHIC | Object::FLAG_OVERLAY | Object::FLAG_FREECOORDS;
-	gfxObj->frame = 0;
+	//gfxObj->frame = 0;
 	gfxObj->frameMax = 1;
 	gfxObj->priority = _curObject->priority;
 	gfxObj->cell.x = -1;


Commit: 60e67e4af7f9d1ee2a4fe7c9155d001373aa61f0
    https://github.com/scummvm/scummvm/commit/60e67e4af7f9d1ee2a4fe7c9155d001373aa61f0
Author: Marisa-Chan (thunder_8888 at mail.ru)
Date: 2026-06-15T16:11:31+07:00

Commit Message:
Explicit init fields. PVS-Studio V730

Changed paths:
    engines/gamos/gamos.h
    engines/gamos/proc.h
    engines/gamos/vm.h


diff --git a/engines/gamos/gamos.h b/engines/gamos/gamos.h
index 496bd111912..10509890a71 100644
--- a/engines/gamos/gamos.h
+++ b/engines/gamos/gamos.h
@@ -144,13 +144,13 @@ typedef Common::Array<ImagePos> ImageSeq;
 struct Sprite {
 	uint32 index = 0;
 
-	byte field_0;
+	byte field_0 = 0;
 	union {
 		byte flags;
 		byte startChar; // if it's font
 	};
-	byte lastChar;
-	byte frameCount;
+	byte lastChar = 0;
+	byte frameCount = 0;
 
 	Common::Array<ImageSeq *> sequences;
 };
diff --git a/engines/gamos/proc.h b/engines/gamos/proc.h
index 36ff71e6f7f..a3071370d12 100644
--- a/engines/gamos/proc.h
+++ b/engines/gamos/proc.h
@@ -38,7 +38,9 @@ enum ACT2 {
 
 class SystemProc {
 public:
-	SystemProc(KeyCodes *conv) : _codesConverter(conv) {};
+	SystemProc(KeyCodes *conv) : _codesConverter(conv) {
+		memset(_keyCodes, 0, sizeof(_keyCodes));
+	};
 
 	void processMessage(const Common::Event &ev);
 
diff --git a/engines/gamos/vm.h b/engines/gamos/vm.h
index 58afae1ef2c..dcafce1d38b 100644
--- a/engines/gamos/vm.h
+++ b/engines/gamos/vm.h
@@ -152,7 +152,9 @@ public:
 
     class Context {
     public:
-        Context(VM &vm): _vm(vm), _readAccess(vm), _writeAccess(vm) {};
+        Context(VM &vm): _vm(vm), _readAccess(vm), _writeAccess(vm) {
+			memset(_stack, 0, sizeof(_stack));
+		};
 
         Common::String getString(int memtype, uint32 offset, uint32 maxLen = 256);
         Common::String getString(const ValAddr &addr, uint32 maxLen = 256);


Commit: 466c427f06fee5716547975d54bec4c9d9478ba5
    https://github.com/scummvm/scummvm/commit/466c427f06fee5716547975d54bec4c9d9478ba5
Author: Marisa-Chan (thunder_8888 at mail.ru)
Date: 2026-06-15T16:11:31+07:00

Commit Message:
Fix lost exit. PVS-Studio V519

Changed paths:
    engines/gamos/movie.cpp


diff --git a/engines/gamos/movie.cpp b/engines/gamos/movie.cpp
index 0ba6b53bee6..d1413ea9cb1 100644
--- a/engines/gamos/movie.cpp
+++ b/engines/gamos/movie.cpp
@@ -318,8 +318,13 @@ int MoviePlayer::processImageChunk() {
 		if (dtime > _frameTime) {
 			if (_soundBufferSize) {
 				_skippedFrames++;
-				if (_skippedFrames != 8)
+				if (_skippedFrames != 8) {
 					_doUpdateScreen = false;
+					_screen->update();
+					_currentFrame++;
+
+					return 1;
+				}
 			}
 		} else if (dtime < _frameTime) {
 			while (true) {


Commit: 1eefcb917cba5f0f20bb3ccef7fc647ea047507e
    https://github.com/scummvm/scummvm/commit/1eefcb917cba5f0f20bb3ccef7fc647ea047507e
Author: Marisa-Chan (thunder_8888 at mail.ru)
Date: 2026-06-15T16:11:31+07:00

Commit Message:
sscanf string with buffer size. PVS-Studio V576

Changed paths:
    engines/gamos/gamos.cpp


diff --git a/engines/gamos/gamos.cpp b/engines/gamos/gamos.cpp
index 58e99764b8c..e4e9ab8070b 100644
--- a/engines/gamos/gamos.cpp
+++ b/engines/gamos/gamos.cpp
@@ -2971,7 +2971,7 @@ void GamosEngine::vmCallDispatcher(VM::Context *ctx, uint32 funcID) {
 
 		char buffer[256];
 		int a = 0, b = 0, c = 0, d = 0;
-		if (sscanf(str.c_str(), "%s %d %d %d %d", buffer, &a, &b, &c, &d) > 0) {
+		if (sscanf(str.c_str(), "%255s %d %d %d %d", buffer, &a, &b, &c, &d) > 0) {
 			stopMidi();
 			stopSounds();
 



From noreply at scummvm.org  Mon Jun 15 12:02:13 2026
From: noreply at scummvm.org (lotharsm)
Date: Mon, 15 Jun 2026 12:02:13 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 ae89011bb942835c5b39b2a2f40be6021d2714df
Message-ID: <20260615120213.0914C7005BC@ron.scummvm.net>

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:
475daafa03 NEWS: Fix missing full stop in Atari news
ae89011bb9 NEWS: Update German NEWS file


Commit: 475daafa03fe19c4b5e5e15dc1dc5ae8003d4ffa
    https://github.com/scummvm/scummvm/commit/475daafa03fe19c4b5e5e15dc1dc5ae8003d4ffa
Author: Lothar Serra Mari (mail at serra.me)
Date: 2026-06-15T14:02:04+02:00

Commit Message:
NEWS: Fix missing full stop in Atari news

Changed paths:
    NEWS.md


diff --git a/NEWS.md b/NEWS.md
index 24c60671444..c492e8919da 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -68,7 +68,7 @@ For a more comprehensive changelog of the latest experimental code, see:
 Atari port:
    - Added native CDDA support.
    - Added plugin support (now ScummVM can run with as little as a few megs of RAM).
-   - Added native YM2149 support (currently usable for Elvira 1 and 2)
+   - Added native YM2149 support (currently usable for Elvira 1 and 2).
 
  PS3 port:
    - Added support for running ScummVM engines as separate modules/executables.


Commit: ae89011bb942835c5b39b2a2f40be6021d2714df
    https://github.com/scummvm/scummvm/commit/ae89011bb942835c5b39b2a2f40be6021d2714df
Author: Lothar Serra Mari (mail at serra.me)
Date: 2026-06-15T14:02:04+02:00

Commit Message:
NEWS: Update German NEWS file

Changed paths:
    doc/de/NEUES.md


diff --git a/doc/de/NEUES.md b/doc/de/NEUES.md
index bbce441dd6f..484beaa80ae 100644
--- a/doc/de/NEUES.md
+++ b/doc/de/NEUES.md
@@ -6,9 +6,9 @@ Umfangreichere Informationen ??ber die ??nderungen des aktuellen Codes findest D
  Neue Spiele:
    - Unterst??tzung f??r Nancy Drew: The Haunted Carousel hinzugef??gt.
    - Unterst??tzung f??r Nancy Drew: Danger on Deception Island hinzugef??gt.
+   - Unterst??tzung f??r Noctropolis hinzugef??gt.
    - Unterst??tzung f??r Cartoon Carnival hinzugef??gt.
    - Unterst??tzung f??r Alfred Pelrock: En Busca de un Sue??o hinzugef??gt.
-   - Unterst??tzung f??r Noctropolis hinzugef??gt.
 
  Allgemein:
    - Klickbare Links in der Benutzeroberfl??che werden jetzt durch einen ver??nderten Cursor hervorgehoben.
@@ -70,6 +70,11 @@ Umfangreichere Informationen ??ber die ??nderungen des aktuellen Codes findest D
    - Darstellung des Holomap-Modells korrigiert.
    - Automatische Speicherung beim Fahren eines Fahrzeugs deaktiviert.
 
+ Atari-Portierung:
+   - Native Unterst??tzung f??r CDDA hinzugef??gt.
+   - Unterst??tzung f??r Plugins hinzugef??gt. ScummVM kann jetzt schon mit wenigen MB RAM genutzt werden.
+   - Native Unterst??tzung f??r YM2149 hinzugef??gt (aktuell verwendbar in Wlvira 1 und 2).
+
  PS3-Portierung:
    - ScummVM-Engines k??nnen jetzt als separate Module/Programmdateien ausgef??hrt werden.
      Dadurch werden etwa 90 MB RAM eingespart. Die Option wird nur in Release-Paketen aktiviert.



From noreply at scummvm.org  Mon Jun 15 15:27:57 2026
From: noreply at scummvm.org (ScummVM-Translations)
Date: Mon, 15 Jun 2026 15:27:57 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm-web master ->
 7d5d9087bc9e600331827d0047eb4f2167b386ac
Message-ID: <20260615152757.7252E7005BC@ron.scummvm.net>

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

Summary:
7d5d9087bc L10N: Update translation (French) (News)


Commit: 7d5d9087bc9e600331827d0047eb4f2167b386ac
    https://github.com/scummvm/scummvm-web/commit/7d5d9087bc9e600331827d0047eb4f2167b386ac
Author: Purple T (ZEONK at hotmail.com)
Date: 2026-06-15T15:22:50Z

Commit Message:
L10N: Update translation (French) (News)

Currently translated at 100.0% (880 of 880 strings)

Changed paths:
    data/fr/news.json


diff --git a/data/fr/news.json b/data/fr/news.json
index d5ce6d05..aa499de7 100644
--- a/data/fr/news.json
+++ b/data/fr/news.json
@@ -1749,10 +1749,14 @@
     },
     "20260612": {
         "title": "Une nuance de Grey plus sombre??: Noctropolis est pr??t pour les tests???!",
-        "content": "Vous en avez assez de tenir une banale librairie et vous voulez vous plonger dans les pages sombres et baign??es de n??ons d???une bande dessin??e ? Pr??parez votre trench-coat, car *Noctropolis*, le jeu d'aventure sombre de Flashpoint Productions datant de 1994, est d??sormais pr??te pour la phase de test public !\n\nDans cette bande dessin??e interactive qui prend vie, vous incarnez Peter Grey, un libraire solitaire entra??n?? dans un monde dystopique de super-h??ros o?? le soleil ne brille jamais. Le l??gendaire h??ros Darksheer ayant pris sa retraite, c'est ?? vous de reprendre son r??le, de r??soudre les ??nigmes et de sauver la ville d'une bande de m??chants agissant au service d'une force myst??rieuse.\n\nPour nous aider ?? tester le jeu, vous aurez besoin de votre copie des [fichiers de donn??es du jeu](https://wiki.scummvm.org/index.php?title=Noctropolis#Required_Data) et d'une [compilation quotidienne r??cente](https://www.scummvm.org/downloads/#daily) de ScummVM.\n\nLa prise en charge a ??t?? ajout??e ?? la fois pour la version DOS originale (d??mos comprises) et pour la r????dition num??rique de Nightdive Studios, disponible sur [GOG](https://www.gog.com/game/noctropolis) ou [Steam](https://store.steampowered.com/app/377070).\n\nPendant que vous patrouillez dans les rues dangereuses, merci de signaler tout probl??me rencontr?? sur notre [outil de suivi de bugs](https://bugs.scummvm.org/).\n\nLa ville a besoin d'un h??ros, r??pondrez-vous ?? l'appel ? Bonne lutte contre le crime !"
+        "content": "Vous en avez assez de tenir une banale librairie et vous voulez vous plonger dans les pages sombres et baign??es de n??ons d???une bande dessin??e ? Pr??parez votre trench-coat, car *Noctropolis*, le sombre jeu d'aventure de Flashpoint Productions datant de 1994, est d??sormais pr??t pour la phase de test public !\n\nDans cette bande dessin??e interactive qui prend vie, vous incarnez Peter Grey, un libraire solitaire entra??n?? dans un monde dystopique de super-h??ros o?? le soleil ne brille jamais. Le l??gendaire h??ros Darksheer ayant pris sa retraite, c'est ?? vous de reprendre son r??le, de r??soudre les ??nigmes et de sauver la ville d'une bande de m??chants agissant au service d'une force myst??rieuse.\n\nPour nous aider ?? tester le jeu, vous aurez besoin de votre copie des [fichiers de donn??es du jeu](https://wiki.scummvm.org/index.php?title=Noctropolis#Required_Data) et d'une [compilation quotidienne r??cente](https://www.scummvm.org/downloads/#daily) de ScummVM.\n\nLa prise en charge a ??t?? ajout??e ?? la fois pour la version DOS originale (d??mos comprises) et pour la r????dition num??rique de Nightdive Studios, disponible sur [GOG](https://www.gog.com/game/noctropolis) ou [Steam](https://store.steampowered.com/app/377070).\n\nPendant que vous patrouillez dans les rues dangereuses, merci de signaler tout probl??me rencontr?? sur notre [outil de suivi de bugs](https://bugs.scummvm.org/).\n\nLa ville a besoin d'un h??ros, r??pondrez-vous ?? l'appel ? Bonne lutte contre le crime !"
     },
     "20260613": {
         "title": "La F??te foraine des Cartoons ouvre ses portes !",
         "content": "Approchez ! Approchez ! _Hanna-Barbera's Cartoon Carnival_ est d??sormais pr??t pour les tests publics sur ScummVM.\n\nD??velopp?? et commercialis?? ?? l'origine pour le Philips CD-i en 1993 par Funhouse Design, puis port?? sur Windows et Macintosh en 1995, _Cartoon Carnival_ rassemble une galerie de personnages connus de Hanna-Barbera dans une collection de mini-jeux courts de type arcade. Scooby-Doo, Fred Pierrafeu, George Jetson, le Pacha et leurs amis se relaient pour relever diff??rents d??fis, allant des labyrinthes et des jeux de correspondance de motifs ?? la capture de ballons et aux quiz.\n\nPlut??t qu'un jeu d'aventure traditionnel, il s'agit d'un divertissement multim??dia haut en couleur du milieu des ann??es 90, riche en animations, voix et musique. C'est ??galement l'un de ces titres sous licence qui peuvent facilement passer inaper??us, c'est pourquoi nous sommes particuli??rement heureux de contribuer ?? le rendre jouable sur les syst??mes modernes.\n\nPour l'instant, seules les versions Windows et Macintosh sont prises en charge. La version CD-i originale n'est pas prise en charge.\n\nPour tester le jeu, vous aurez besoin de la derni??re [compilation quotidienne](https://www.scummvm.org/downloads/#daily) et soit d'une copie originale de la version Windows ou Macintosh, soit de sa d??mo (que nous n'avons pu trouver qu'en italien). Veuillez consulter la [page wiki](https://wiki.scummvm.org/index.php?title=Cartoon_Carnival) pour conna??tre les fichiers de donn??es requis.\n\nComme d'habitude, merci de signaler tout probl??me sur notre [bug tracker](https://bugs.scummvm.org/), en pr??cisant la version que vous avez test??e, votre plateforme, ainsi que toute capture d'??cran ou fichier de sauvegarde utile.\n\nAlors approchez, choisissez un jeu et aidez-nous ?? nous assurer que cette f??te foraine fonctionne correctement !"
+    },
+    "20260614": {
+        "content": "Bonne nouvelle pour les amateurs de jeux d'aventure : _Alfred Pelrock_ est d??sormais pris en charge par ScummVM !\n\nSorti ?? l'origine en 1996, _Alfred Pelrock_ est un jeu d'aventure ?? point-&-click ?? plein d'humour, avec des graphismes de style bande dessin??e, des ??nigmes d??cal??es et un humour irr??v??rencieux. Apr??s avoir r??v?? d???une princesse ??gyptienne emprisonn??e dans un pass?? lointain, Alfred se lance dans une aventure ?? travers le temps vers l?????gypte antique pour la sauver, rencontrant en chemin des situations bizarres et des personnages excentriques.\n\nBien que le jeu regorge d???humour et de satire, les joueurs doivent savoir qu???il comporte ??galement des th??mes l??g??rement suggestifs et un humour parfois destin?? ?? un public adulte, typique de son ??poque. Il convient ??galement de noter que le jeu est sorti exclusivement en espagnol et ne dispose d???aucune traduction officielle.\n\nComme toujours, si vous poss??dez une copie originale du jeu et que vous parlez espagnol, t??l??chargez la derni??re [compilation de d??veloppement quotidienne](https://www.scummvm.org/downloads/# ), ajoutez les [fichiers de donn??es requis](https://wiki.scummvm.org/index.php?title=Alfred_Pelrock:_En_Busca_de_un_Sue%C3%B1o) et lancez-vous. Les [rapports de bugs](https://bugs.scummvm.org/) et les commentaires des joueurs sont ??galement les bienvenus afin de nous aider ?? am??liorer la compatibilit?? et ?? pr??server ce petit bijou de l'histoire des jeux d'aventure.",
+        "title": "Alfred Pelrock rejoint ScummVM"
     }
 }



From noreply at scummvm.org  Mon Jun 15 17:07:18 2026
From: noreply at scummvm.org (sev-)
Date: Mon, 15 Jun 2026 17:07:18 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 97f38b744f38f27adacc6c34127dbd8969e3ca5a
Message-ID: <20260615170718.D2A5C7005BC@ron.scummvm.net>

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

Summary:
97f38b744f NEWS: Mention kinetic scrolling


Commit: 97f38b744f38f27adacc6c34127dbd8969e3ca5a
    https://github.com/scummvm/scummvm/commit/97f38b744f38f27adacc6c34127dbd8969e3ca5a
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2026-06-15T19:07:04+02:00

Commit Message:
NEWS: Mention kinetic scrolling

Changed paths:
    NEWS.md


diff --git a/NEWS.md b/NEWS.md
index c492e8919da..64a26cb29a0 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -14,6 +14,7 @@ For a more comprehensive changelog of the latest experimental code, see:
    - Highlight clickable links in the GUI with a cursor change.
    - Fixed window size when switching out of fullscreen mode on 3D engines.
    - Implemented NULL OPL driver for lower-base systems.
+   - Implemented kinetic scolling in GUI lists.
 
  ACCESS:
    - Fixed various script issues in Martian Memorandum.



From noreply at scummvm.org  Mon Jun 15 17:35:25 2026
From: noreply at scummvm.org (sev-)
Date: Mon, 15 Jun 2026 17:35:25 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm-sites director-buildbot ->
 101d77f38873fca67fdfdfdebdf60a04e3cd7c23
Message-ID: <20260615173525.D05397005BC@ron.scummvm.net>

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

Summary:
101d77f388 IMAGEDIFF: Add pagination and default to last page


Commit: 101d77f38873fca67fdfdfdebdf60a04e3cd7c23
    https://github.com/scummvm/scummvm-sites/commit/101d77f38873fca67fdfdfdebdf60a04e3cd7c23
Author: ramyak-sharma (ramyaksharma1 at gmail.com)
Date: 2026-06-15T19:35:22+02:00

Commit Message:
IMAGEDIFF: Add pagination and default to last page

Changed paths:
    imagediff/main.py
    imagediff/static/css/style.css
    imagediff/templates/target.html


diff --git a/imagediff/main.py b/imagediff/main.py
index 5ab58f4..7e7f8ab 100644
--- a/imagediff/main.py
+++ b/imagediff/main.py
@@ -108,6 +108,21 @@ def get_frame_number(filename):
         
 
 
+def get_pagination_pages(page, total_pages, window=2):
+    """Return list of page numbers with None for ellipsis gaps."""
+    pages = set([1, total_pages])
+    for i in range(max(1, page - window), min(total_pages, page + window) + 1):
+        pages.add(i)
+    result = []
+    prev = None
+    for p in sorted(pages):
+        if prev is not None and p - prev > 1:
+            result.append(None)
+        result.append(p)
+        prev = p
+    return result
+
+
 def get_sorted_builds(target_path, reverse=True):
     """Get sorted list of builds for a target."""
     builds = [b for b in os.listdir(target_path)
@@ -218,18 +233,17 @@ def target_detail(target):
     if not os.path.exists(target_path) or not os.path.isdir(target_path):
         return "Target not found", 404
 
-    # Only get the build list, other time-consuming operations moved to API
-    builds = get_sorted_builds(target_path,reverse=False)
-
-    # Only return the page framework with build list but without table data
-    page = int(request.args.get('page', 1))
+    builds = get_sorted_builds(target_path, reverse=False)
     page_size = 20
+    total_pages = max(1, (len(builds) + page_size - 1) // page_size)
+    page_param = request.args.get('page')
+    page = int(page_param) if page_param is not None else total_pages
+    page = max(1, min(page, total_pages))
     start = (page - 1) * page_size
     end = start + page_size
     paginated_builds = builds[start:end]
-    total_pages = (len(builds) + page_size - 1) // page_size
+    pagination_pages = get_pagination_pages(page, total_pages)
 
-    # Load cache for this target and page
     cache_data = load_target_cache(target, page)
 
     return render_template('target.html',
@@ -238,6 +252,7 @@ def target_detail(target):
         builds=paginated_builds,
         page=page,
         total_pages=total_pages,
+        pagination_pages=pagination_pages,
         cache_data=cache_data)
 
 
diff --git a/imagediff/static/css/style.css b/imagediff/static/css/style.css
index 410eea8..99119a1 100644
--- a/imagediff/static/css/style.css
+++ b/imagediff/static/css/style.css
@@ -128,4 +128,67 @@ a:hover {
         max-width: 300px;
         max-height: 300px;
     }
+}
+
+.pagination-container {
+    margin-top: 12px;
+}
+
+.pagination {
+    display: flex;
+    align-items: center;
+    gap: 4px;
+    flex-wrap: wrap;
+}
+
+.page-btn {
+    display: inline-block;
+    padding: 4px 8px;
+    border: 1px solid #ccc;
+    border-radius: 4px;
+    color: #337ab7;
+    background: #fff;
+    cursor: pointer;
+    text-decoration: none;
+    font-size: 14px;
+    line-height: 1.4;
+}
+
+.page-btn:hover {
+    background: #e8f0fe;
+    text-decoration: none;
+}
+
+.page-btn.current {
+    background: #337ab7;
+    color: #fff;
+    border-color: #337ab7;
+    font-weight: bold;
+}
+
+.page-btn.disabled {
+    color: #aaa;
+    border-color: #eee;
+    cursor: default;
+    pointer-events: none;
+}
+
+.page-ellipsis {
+    padding: 4px 4px;
+    color: #666;
+}
+
+.page-jump-form {
+    display: inline-flex;
+    align-items: center;
+    gap: 4px;
+    margin-left: 8px;
+}
+
+.page-jump-input {
+    width: 60px;
+    padding: 4px 6px;
+    border: 1px solid #ccc;
+    border-radius: 4px;
+    font-size: 14px;
 }
\ No newline at end of file
diff --git a/imagediff/templates/target.html b/imagediff/templates/target.html
index 841d13e..e9c4438 100644
--- a/imagediff/templates/target.html
+++ b/imagediff/templates/target.html
@@ -104,25 +104,37 @@
         <div class="pagination-container">
     {% if total_pages > 1 %}
         <div class="pagination">
-
             {% if page > 1 %}
-                <a class="page-btn"
-                   href="{{ url_for('target_detail', target=target) }}?page={{ page - 1 }}">
-                    ??? Previous
-                </a>
+                <a class="page-btn" href="{{ url_for('target_detail', target=target) }}?page=1">|&lt;</a>
+                <a class="page-btn" href="{{ url_for('target_detail', target=target) }}?page={{ page - 1 }}">&lt;</a>
+            {% else %}
+                <span class="page-btn disabled">|&lt;</span>
+                <span class="page-btn disabled">&lt;</span>
             {% endif %}
 
-            <span class="page-info">
-                Page {{ page }} of {{ total_pages }}
-            </span>
+            {% for p in pagination_pages %}
+                {% if p is none %}
+                    <span class="page-ellipsis">???</span>
+                {% elif p == page %}
+                    <span class="page-btn current">{{ p }}</span>
+                {% else %}
+                    <a class="page-btn" href="{{ url_for('target_detail', target=target) }}?page={{ p }}">{{ p }}</a>
+                {% endif %}
+            {% endfor %}
 
             {% if page < total_pages %}
-                <a class="page-btn"
-                   href="{{ url_for('target_detail', target=target) }}?page={{ page + 1 }}">
-                    Next ???
-                </a>
+                <a class="page-btn" href="{{ url_for('target_detail', target=target) }}?page={{ page + 1 }}">&gt;</a>
+                <a class="page-btn" href="{{ url_for('target_detail', target=target) }}?page={{ total_pages }}">&gt;|</a>
+            {% else %}
+                <span class="page-btn disabled">&gt;</span>
+                <span class="page-btn disabled">&gt;|</span>
             {% endif %}
 
+            <form method="get" class="page-jump-form">
+                <input type="number" name="page" min="1" max="{{ total_pages }}"
+                       placeholder="{{ page }}" class="page-jump-input">
+                <button type="submit" class="page-btn">Go</button>
+            </form>
         </div>
     {% endif %}
 </div>



From noreply at scummvm.org  Mon Jun 15 19:25:10 2026
From: noreply at scummvm.org (sev-)
Date: Mon, 15 Jun 2026 19:25:10 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 ca9f61a5e8f0c619ce81c0aea3e4f6a3c6eefae6
Message-ID: <20260615192510.2497E7005BC@ron.scummvm.net>

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

Summary:
ca9f61a5e8 BACKENDS: MORPHOS: rework backend


Commit: ca9f61a5e8f0c619ce81c0aea3e4f6a3c6eefae6
    https://github.com/scummvm/scummvm/commit/ca9f61a5e8f0c619ce81c0aea3e4f6a3c6eefae6
Author: BeWorld (36823759+BeWorld2018 at users.noreply.github.com)
Date: 2026-06-15T21:25:05+02:00

Commit Message:
BACKENDS: MORPHOS: rework backend

Some fixes and enhanced to MorphOS / SDL platform:

fix __stack
rework initBackend, do not use PROGDIR anymore and fix default values
logMessage: use OSystem_SDL::logMessage

Changed paths:
    backends/platform/sdl/morphos/morphos-main.cpp
    backends/platform/sdl/morphos/morphos.cpp
    backends/platform/sdl/morphos/morphos.h


diff --git a/backends/platform/sdl/morphos/morphos-main.cpp b/backends/platform/sdl/morphos/morphos-main.cpp
index 3e23ad8fd67..94718e266fb 100644
--- a/backends/platform/sdl/morphos/morphos-main.cpp
+++ b/backends/platform/sdl/morphos/morphos-main.cpp
@@ -28,10 +28,9 @@
 #include "backends/plugins/sdl/sdl-provider.h"
 #include "base/main.h"
 
-int main(int argc, char *argv[]) {
+extern "C" { long __stack = 4 * 1024 * 1024; }
 
-	// Set a stack cookie to avoid crashes from a too low stack.
-	static const char *stack_cookie __attribute__((used)) = "$STACK: 4096000";
+int main(int argc, char *argv[]) {
 
 	// Create our OSystem instance.
 	g_system = new OSystem_MorphOS();
diff --git a/backends/platform/sdl/morphos/morphos.cpp b/backends/platform/sdl/morphos/morphos.cpp
index a26e446b95b..717122be28a 100644
--- a/backends/platform/sdl/morphos/morphos.cpp
+++ b/backends/platform/sdl/morphos/morphos.cpp
@@ -19,10 +19,13 @@
  *
  */
 
-#define FORBIDDEN_SYMBOL_EXCEPTION_printf
 #include "common/scummsys.h"
 
 #ifdef __MORPHOS__
+#include <proto/exec.h>
+#include <proto/dos.h>
+#include <exec/tasks.h>
+#include <dos/dosextens.h>
 
 #include "backends/platform/sdl/morphos/morphos.h"
 #include "backends/fs/morphos/morphos-fs-factory.h"
@@ -30,8 +33,33 @@
 
 static bool cleanupDone = false;
 
+static Common::Path getProcessDirectoryPath(const char *name, bool directory) {
+	char path[1024];
+
+	struct Process *process = (struct Process *)FindTask(NULL);
+
+	if (!process || !process->pr_HomeDir) {
+		Common::String fallback = Common::String::format("PROGDIR:%s", name);
+		if (directory && !fallback.hasSuffix("/"))
+			fallback += "/";
+		return Common::Path(fallback);
+	}
+
+	if (!NameFromLock(process->pr_HomeDir, path, sizeof(path)))
+		return Common::Path(Common::String::format("PROGDIR:%s", name));
+
+	if (!AddPart(path, name, sizeof(path)))
+		return Common::Path(Common::String::format("PROGDIR:%s", name));
+
+	Common::String result(path);
+	if (directory && !result.hasSuffix("/") && !result.hasSuffix(":"))
+		result += "/";
+
+	return Common::Path(result);
+}
+
 static void cleanup() {
-	if (!cleanupDone)
+	if (!cleanupDone && g_system)
 		g_system->destroy();
 }
 
@@ -65,32 +93,66 @@ bool OSystem_MorphOS::hasFeature(Feature f) {
 	return OSystem_SDL::hasFeature(f);
 }
 
+static Common::Path getProcessDirectoryFile(const char *file) {
+	return getProcessDirectoryPath(file, false);
+}
+
+Common::Path OSystem_MorphOS::getDefaultConfigFileName() {
+	return getProcessDirectoryFile("scummvm.ini");
+}
+
+static void assureDrawerExists(const Common::Path &path) {
+	Common::String nativePath = path.toString(Common::Path::kNativeSeparator);
+
+	if (nativePath.size() > 0 && nativePath.lastChar() == '/')
+		nativePath.deleteLastChar();
+
+	BPTR lock = Lock(nativePath.c_str(), ACCESS_READ);
+	if (lock) {
+		UnLock(lock);
+		return;
+	}
+
+	BPTR created = CreateDir(nativePath.c_str());
+	if (created)
+		UnLock(created);
+}
+
 void OSystem_MorphOS::initBackend() {
+	const Common::Path savePath = getProcessDirectoryPath("saves", true);
+	const Common::Path extraPath = getProcessDirectoryPath("extras", true);
+	const Common::Path themePath = getProcessDirectoryPath("themes", true);
+	const Common::Path iconPath = getProcessDirectoryPath("icons", true);
+
+	assureDrawerExists(savePath);
 
-	// First time user defaults
 	ConfMan.registerDefault("audio_buffer_size", "2048");
-	ConfMan.registerDefault("extrapath", Common::Path("PROGDIR:extras/"));
-	ConfMan.registerDefault("savepath", Common::Path("PROGDIR:saves/"));
-	ConfMan.registerDefault("themepath", Common::Path("PROGDIR:themes/"));
-	// First time .ini defaults
-	if (!ConfMan.hasKey("audio_buffer_size")) {
+	ConfMan.registerDefault("savepath", savePath);
+	ConfMan.registerDefault("extrapath", extraPath);
+	ConfMan.registerDefault("themepath", themePath);
+	ConfMan.registerDefault("iconspath", iconPath);
+
+	if (!ConfMan.hasKey("audio_buffer_size"))
 		ConfMan.set("audio_buffer_size", "2048");
-	}
-	if (!ConfMan.hasKey("extrapath")) {
-		ConfMan.setPath("extrapath", "PROGDIR:extras/");
-	}
-	if (!ConfMan.hasKey("savepath")) {
-		ConfMan.setPath("savepath", "PROGDIR:saves/");
-	}
-	if (!ConfMan.hasKey("themepath")) {
-		ConfMan.setPath("themepath", "PROGDIR:themes/");
-	}
+
+	if (!ConfMan.hasKey("savepath"))
+		ConfMan.setPath("savepath", savePath);
+
+	if (!ConfMan.hasKey("extrapath"))
+		ConfMan.setPath("extrapath", extraPath);
+
+	if (!ConfMan.hasKey("themepath"))
+		ConfMan.setPath("themepath", themePath);
+
+	if (!ConfMan.hasKey("iconspath"))
+		ConfMan.setPath("iconspath", iconPath);
+
 	OSystem_SDL::initBackend();
 }
 
 void OSystem_MorphOS::logMessage(LogMessageType::Type type, const char * message) {
 #ifdef DEBUG_BUILD
-	printf("%s\n", message);
+	OSystem_SDL::logMessage(type, message);
 #endif
 }
 #endif
diff --git a/backends/platform/sdl/morphos/morphos.h b/backends/platform/sdl/morphos/morphos.h
index 849d62a3667..da3d0b00f0c 100644
--- a/backends/platform/sdl/morphos/morphos.h
+++ b/backends/platform/sdl/morphos/morphos.h
@@ -36,6 +36,10 @@ public:
 
 	void initBackend() override;
 	void logMessage(LogMessageType::Type type, const char *message) override;
+
+protected:
+	Common::Path getDefaultConfigFileName() override;
+
 };
 
 #endif



From noreply at scummvm.org  Mon Jun 15 20:10:37 2026
From: noreply at scummvm.org (sev-)
Date: Mon, 15 Jun 2026 20:10:37 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm-sites director-buildbot ->
 158e29a9739c1325b1a3724162a45ee6dca46acd
Message-ID: <20260615201037.3E9187005BC@ron.scummvm.net>

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

Summary:
158e29a973 IMAGEDIFF: Replace image comparison with file check


Commit: 158e29a9739c1325b1a3724162a45ee6dca46acd
    https://github.com/scummvm/scummvm-sites/commit/158e29a9739c1325b1a3724162a45ee6dca46acd
Author: ramyak-sharma (ramyaksharma1 at gmail.com)
Date: 2026-06-15T22:10:33+02:00

Commit Message:
IMAGEDIFF: Replace image comparison with file check

Changed paths:
    imagediff/imagediff.py
    imagediff/main.py


diff --git a/imagediff/imagediff.py b/imagediff/imagediff.py
index 40232c3..8931ec2 100644
--- a/imagediff/imagediff.py
+++ b/imagediff/imagediff.py
@@ -67,72 +67,23 @@ def find_baseline_build(screenshots_dir, target, current_build):
 
 def movie_diff(src_build, cmp_build, target, movie, screenshots_dir=None):
     """
-    Compare all frames of a movie between two builds and determine if there are any differences.
+    Determine if a movie differs between two builds by checking file presence.
 
-    Args:
-        src_build (str): The source build name
-        cmp_build (str): The comparison build name
-        target (str): The target name
-        movie (str): The movie name
-        screenshots_dir (str): Override for SCREENSHOTS_DIR from config
-
-    Returns:
-        bool: True if any frame has differences, False otherwise
+    The Director engine only saves a screenshot when it detects a change vs the
+    previous build (using pixel comparison with a threshold). So if a frame file
+    exists in a build, the engine already confirmed it differs. No PIL comparison
+    needed here, file presence IS the diff signal.
     """
     screenshots_dir = screenshots_dir or SCREENSHOTS_DIR
     src_build_path = os.path.join(screenshots_dir, target, src_build)
     cmp_build_path = os.path.join(screenshots_dir, target, cmp_build)
 
-    # Ensure both build paths exist
     if not os.path.exists(src_build_path) or not os.path.exists(cmp_build_path):
         return False
 
-    # Get all frames for this movie in both builds
-    src_frames = [f for f in os.listdir(src_build_path)
-                  if os.path.isfile(os.path.join(src_build_path, f)) and f.startswith(f"{movie}-")]
-
-    cmp_frames = [f for f in os.listdir(cmp_build_path)
-                  if os.path.isfile(os.path.join(cmp_build_path, f)) and f.startswith(f"{movie}-")]
-
-    # If frame counts differ, there's definitely a difference
-    if len(src_frames) != len(cmp_frames):
-        return True
-
-    # Helper function to extract frame number from filename
-    def get_frame_number(filename):
-        parts = filename.split('-')
-        if len(parts) > 1:
-            try:
-                return int(parts[1].split('.')[0])
-            except ValueError:
-                return 0
-        return 0
-
-    src_frame_map = {get_frame_number(f): f for f in src_frames}
-    cmp_frame_map = {get_frame_number(f): f for f in cmp_frames}
-
-    all_frame_numbers = sorted(set(list(src_frame_map.keys()) + list(cmp_frame_map.keys())))
-
-    if set(src_frame_map.keys()) != set(cmp_frame_map.keys()):
-        return True
-
-    for frame_num in all_frame_numbers:
-        src_frame = src_frame_map.get(frame_num)
-        cmp_frame = cmp_frame_map.get(frame_num)
-
-        if src_frame and cmp_frame:
-            src_img_path = os.path.join(src_build_path, src_frame)
-            cmp_img_path = os.path.join(cmp_build_path, cmp_frame)
-
-            try:
-                diff_result = image_diff(src_img_path, cmp_img_path)
+    cmp_frames = {f for f in os.listdir(cmp_build_path) if f.startswith(f"{movie}-")}
 
-                if diff_result.get('has_diff', False):
-                    return True
-            except Exception as e:
-                print(f"Error comparing frames {src_frame} and {cmp_frame}: {e}")
-                return True
-        else:
-            return True
+    # The engine only saves a screenshot file when it detects a change vs the previous build.
+    # So any file in cmp_build means cmp_build differs from src_build.
+    return len(cmp_frames) > 0
 
-    return False
\ No newline at end of file
diff --git a/imagediff/main.py b/imagediff/main.py
index 7e7f8ab..b3b85c7 100644
--- a/imagediff/main.py
+++ b/imagediff/main.py
@@ -328,24 +328,11 @@ def target_data_api(target):
                 movie_reference_builds[movie][current_build] = reference_data
 
     def get_image_diff(current_build, prev_build, movie, frame):
-        cache_key = make_cache_key(target, current_build, prev_build, movie, frame)
-        image_diff_cache = load_frame_cache(target)
-        if cache_key in image_diff_cache:
-            return image_diff_cache[cache_key]
-
         current_frame_path = os.path.join(target_path, current_build, f"{movie}-{frame}.png")
         prev_frame_path = os.path.join(target_path, prev_build, f"{movie}-{frame}.png")
-
-        if os.path.exists(current_frame_path) and os.path.exists(prev_frame_path):
-            try:
-                image_diff_cache[cache_key] = image_diff(current_frame_path, prev_frame_path)
-            except Exception as e:
-                print(f"Error comparing frames {movie}-{frame}: {e}")
-                image_diff_cache[cache_key] = {'has_diff': True}
-        else:
-            image_diff_cache[cache_key] = {'has_diff': True}
-        save_frame_cache(target, image_diff_cache)
-        return image_diff_cache[cache_key]
+        # File presence is the diff signal, engine only saves when it detects a change
+        has_diff = os.path.exists(current_frame_path) and os.path.exists(prev_frame_path)
+        return {'has_diff': has_diff}
 
     # Modified movie difference function to only compare specified frames
     def get_movie_diff_for_frames(current_build, reference_build, target, movie, frames_to_compare):
@@ -468,7 +455,8 @@ def target_data_api(target):
                             'is_partial': True
                         })
                     else:
-                        has_diff = movie_diff(current_build, prev_build, target, movie)
+                        has_diff = get_movie_diff_for_frames(
+                            current_build, prev_build, target, movie, list(common_frames))
 
                         continuous_bars[movie].append({
                             'build': current_build,



From noreply at scummvm.org  Mon Jun 15 20:29:49 2026
From: noreply at scummvm.org (neuromancer)
Date: Mon, 15 Jun 2026 20:29:49 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 797e293027982c24d642a96c8fa818f4e78d4fb2
Message-ID: <20260615202949.42FE77005BC@ron.scummvm.net>

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

Summary:
797e293027 SCUMM: RA2: use correct font for high resolution


Commit: 797e293027982c24d642a96c8fa818f4e78d4fb2
    https://github.com/scummvm/scummvm/commit/797e293027982c24d642a96c8fa818f4e78d4fb2
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-15T22:28:58+02:00

Commit Message:
SCUMM: RA2: use correct font for high resolution

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


diff --git a/engines/scumm/smush/rebel/smush_multi_font.cpp b/engines/scumm/smush/rebel/smush_multi_font.cpp
index 88b5bd30b46..07af9cb1b43 100644
--- a/engines/scumm/smush/rebel/smush_multi_font.cpp
+++ b/engines/scumm/smush/rebel/smush_multi_font.cpp
@@ -51,19 +51,31 @@ NutRenderer *SmushMultiFont::getCurrentFont() const {
 }
 
 Rebel2FontSet SmushMultiFont::getRebel2FontSet() {
-	static const char *ra2Fonts[] = {
+	// High-res mode uses dedicated larger-glyph font assets, not scaled low-res ones (matches getGameFont()).
+	static const char *ra2FontsLo[] = {
 		"SYSTM/TALKFONT.NUT",
 		"SYSTM/SMALFONT.NUT",
 		"SYSTM/TITLFONT.NUT",
 		"SYSTM/POVFONT.NUT"
 	};
+	static const char *ra2FontsHi[] = {
+		"SYSTM/TKHIFONT.NUT",
+		"SYSTM/SMHIFONT.NUT",
+		"SYSTM/TIHIFONT.NUT",
+		"SYSTM/POHIFONT.NUT"
+	};
+	const bool highRes = _vm->_screenWidth >= 640 && _vm->_screenHeight >= 400;
+	const char *const *ra2Fonts = highRes ? ra2FontsHi : ra2FontsLo;
 
 	Rebel2FontSet fontSet;
-	fontSet.numFonts = ARRAYSIZE(ra2Fonts);
+	fontSet.numFonts = ARRAYSIZE(ra2FontsLo);
 	fontSet.defaultFont = CLIP<int>(_defaultFont, 0, fontSet.numFonts - 1);
 	for (int i = 0; i < fontSet.numFonts; i++) {
-		if (!_rebel2Fonts[i])
+		if (!_rebel2Fonts[i]) {
 			_rebel2Fonts[i] = makeRebel2Font(_vm, ra2Fonts[i]);
+			debugC(DEBUG_SMUSH, "SmushMultiFont::getRebel2FontSet: loaded RA2 font[%d]=%s (highRes=%d)",
+				i, ra2Fonts[i], (int)highRes);
+		}
 		fontSet.fonts[i] = _rebel2Fonts[i];
 	}
 	return fontSet;
diff --git a/engines/scumm/smush/rebel/smush_player_ra2.cpp b/engines/scumm/smush/rebel/smush_player_ra2.cpp
index e44fe21ba11..339b2101206 100644
--- a/engines/scumm/smush/rebel/smush_player_ra2.cpp
+++ b/engines/scumm/smush/rebel/smush_player_ra2.cpp
@@ -841,9 +841,14 @@ void SmushPlayerRebel2::handleLoad(int32 subSize, Common::SeekableReadStream &b)
 void SmushPlayerRebel2::ra2HandleTextResource(const char *str, int fontId, int color,
 										int pos_x, int pos_y, int left, int top,
 										int width, int height, TextStyleFlags flg) {
+	// Promote the native 320x200 frame to the 640x400 screen before drawing, else 2x-scaled text lands off-buffer.
+	const bool hiRes = ra2IsHighResMode() && !isRebel2GameplayActive(_insane);
+	if (hiRes)
+		ra2PromoteCurrentFrameToHiRes(0, 0);
+
 	ensureMultiFont();
 	_multiFont->setDefaultFont(fontId);
-	const int scale = (_vm->_screenWidth >= 640 && _vm->_screenHeight >= 400) ? 2 : 1;
+	const int scale = hiRes ? 2 : 1;
 	pos_x *= scale;
 	pos_y *= scale;
 	left *= scale;



From noreply at scummvm.org  Mon Jun 15 21:13:14 2026
From: noreply at scummvm.org (bluegr)
Date: Mon, 15 Jun 2026 21:13:14 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 fc8ea06fce09b3254864177e55a8862df306b343
Message-ID: <20260615211314.A2B297005BC@ron.scummvm.net>

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:
5662f8f019 NANCY: Skip pieces that don't need to be placed in onebuildpuzzle
fc8ea06fce NANCY: Fix overwriting cell phone data when changing scenes in Nancy 10


Commit: 5662f8f0195b9a133e7db1f8753ccd7d85f3f8f1
    https://github.com/scummvm/scummvm/commit/5662f8f0195b9a133e7db1f8753ccd7d85f3f8f1
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-06-16T00:13:03+03:00

Commit Message:
NANCY: Skip pieces that don't need to be placed in onebuildpuzzle

Partially fixes the tuning puzzle in Nancy 10

Changed paths:
    engines/nancy/action/puzzle/onebuildpuzzle.cpp


diff --git a/engines/nancy/action/puzzle/onebuildpuzzle.cpp b/engines/nancy/action/puzzle/onebuildpuzzle.cpp
index c8313c81e5b..d67b9b44dc7 100644
--- a/engines/nancy/action/puzzle/onebuildpuzzle.cpp
+++ b/engines/nancy/action/puzzle/onebuildpuzzle.cpp
@@ -496,8 +496,16 @@ void OneBuildPuzzle::clampRectToViewport(Common::Rect &rect) {
 
 void OneBuildPuzzle::checkAllPlaced() {
 	for (uint i = 0; i < _pieces.size(); ++i) {
-		if (!_pieces[i].placed)
-			return;
+		if (_pieces[i].placed)
+			continue;
+
+		// Nancy 10: pieces with an empty slotRect (top == 0 && bottom == 0)
+		// are filler ??? they don't need to be placed for the puzzle to solve.
+		const Common::Rect &slot = _pieces[i].slotRect;
+		if (slot.top == 0 && slot.bottom == 0)
+			continue;
+
+		return;
 	}
 	_isSolved = true;
 	_solveState = kTriggerCompletion;


Commit: fc8ea06fce09b3254864177e55a8862df306b343
    https://github.com/scummvm/scummvm/commit/fc8ea06fce09b3254864177e55a8862df306b343
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-06-16T00:13:05+03:00

Commit Message:
NANCY: Fix overwriting cell phone data when changing scenes in Nancy 10

Fixes the cell phone when returning from Dry Creek

Changed paths:
    engines/nancy/ui/cellphonepopup.cpp


diff --git a/engines/nancy/ui/cellphonepopup.cpp b/engines/nancy/ui/cellphonepopup.cpp
index 5444205603f..a283748313d 100644
--- a/engines/nancy/ui/cellphonepopup.cpp
+++ b/engines/nancy/ui/cellphonepopup.cpp
@@ -143,9 +143,6 @@ void CellPhonePopup::registerGraphics() {
 }
 
 void CellPhonePopup::setNoSignal(bool noSignal) {
-	if (_noSignal == noSignal) {
-		return;
-	}
 	_noSignal = noSignal;
 	CellPhoneData *cellData = (CellPhoneData *)NancySceneState.getPuzzleData(CellPhoneData::getTag());
 	if (cellData) {
@@ -157,9 +154,6 @@ void CellPhonePopup::setNoSignal(bool noSignal) {
 }
 
 void CellPhonePopup::setBatteryLow(bool low) {
-	if (_batteryLow == low) {
-		return;
-	}
 	_batteryLow = low;
 	CellPhoneData *cellData = (CellPhoneData *)NancySceneState.getPuzzleData(CellPhoneData::getTag());
 	if (cellData) {



From noreply at scummvm.org  Mon Jun 15 22:27:02 2026
From: noreply at scummvm.org (whoozle)
Date: Mon, 15 Jun 2026 22:27:02 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 d5c8d79a1104309a75c8ea84429b4bb960d45176
Message-ID: <20260615222702.125AF7005BC@ron.scummvm.net>

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

Summary:
942242cd17 VIDEO: Add raw 4XM movie playback
6f23527360 PHOENIXVR: Add Dracula script command support
57053995e0 PHOENIXVR: Add Dracula game detection
78e2d343ab PHOENIXVR: Add Dracula script compatibility fixes
db4a85bee6 PHOENIXVR: change the way engine handles undefined variables
31742a6d3e PHOENIXVR: Fix Dracula 2 timer display
e6f3921aaa PHOENIXVR: Improve Dracula runtime script handling
d5c8d79a11 PHOENIXVR: Show VR debug regions as rainbow borders


Commit: 942242cd17b3eabf9d6806f6cbc61f16917bd60b
    https://github.com/scummvm/scummvm/commit/942242cd17b3eabf9d6806f6cbc61f16917bd60b
Author: Scorp (scorp at mrs.mn)
Date: 2026-06-15T23:26:54+01:00

Commit Message:
VIDEO: Add raw 4XM movie playback

Changed paths:
    video/4xm_decoder.cpp
    video/4xm_decoder.h
    video/4xm_utils.cpp
    video/4xm_utils.h


diff --git a/video/4xm_decoder.cpp b/video/4xm_decoder.cpp
index 36d2e9ed977..f72ddb7f4f2 100644
--- a/video/4xm_decoder.cpp
+++ b/video/4xm_decoder.cpp
@@ -22,6 +22,7 @@
 #include "video/4xm_decoder.h"
 #include "audio/audiostream.h"
 #include "audio/decoders/adpcm.h"
+#include "audio/decoders/adpcm_intern.h"
 #include "audio/decoders/raw.h"
 #include "common/bitstream.h"
 #include "common/debug.h"
@@ -55,37 +56,211 @@ static const int8_t mv[256][2] = {
 
 } // namespace
 
+namespace {
+
+enum Raw4XMChunkId {
+	kRaw4XMFile = 0xfb814000,
+	kRaw4XMFrameContainer = 0xfb814100,
+	kRaw4XMFullFrame = 0xfb814210,
+	kRaw4XMDeltaFrame = 0xfb814220,
+	kRaw4XMCompressedAudio = 0xfb814230,
+	kRaw4XMCachedFrame = 0xfb814240,
+	kRaw4XMRawAudio = 0xfb814250
+};
+
+struct Raw4XMCacheEntry {
+	int32 frame = -1;
+	uint32 declaredSize = 0;
+	Common::Array<byte> data;
+};
+
+struct Raw4XMDeltaDecoder {
+	FourXM::RawDeltaReader reader;
+	uint16 *dst = nullptr;
+	const uint16 *src = nullptr;
+	int frameWidth = 0;
+	int frameHeight = 0;
+	const Common::Array<int> &fullOffsets;
+	const Common::Array<int> &expOffsets;
+
+	Raw4XMDeltaDecoder(const byte *data, uint32 size, uint16 *dstPixels, const uint16 *srcPixels,
+					   int width, int height, const Common::Array<int> &fullMotionOffsets, const Common::Array<int> &expMotionOffsets) : reader(data, size), dst(dstPixels), src(srcPixels), frameWidth(width), frameHeight(height),
+																																		 fullOffsets(fullMotionOffsets), expOffsets(expMotionOffsets) {}
+
+	bool copyLeaf(int dstOffset, int blockWidth, int blockHeight, uint32 op) {
+		uint32 motionIndex = 0;
+		uint32 transform = 0;
+		uint32 addFlag = 0;
+		if (reader.mode == 0) {
+			uint16 packed = reader.readPair();
+			motionIndex = packed & 0xfff;
+			transform = (packed >> 12) & 7;
+			addFlag = packed >> 15;
+		} else {
+			motionIndex = reader.readByteIndex();
+			uint32 packed = reader.readNibble();
+			transform = packed & 7;
+			addFlag = (packed & 0xf) >> 3;
+		}
+
+		int add = 0;
+		if (op == 3 && !addFlag)
+			return true;
+
+		if (addFlag) {
+			uint16 packed = reader.readPair();
+			if (op == 3)
+				add = (((int8)(((packed >> 5) & 0x1f) - 0x10) & 0xf) * 0x40 +
+					   ((int8)(((packed >> 10) & 0x1f) - 0x10) & 0xf) * 0x800 +
+					   ((byte)((((byte)packed & 0x1f) - 0x10) * 2) & 0x1f));
+			else
+				add = ((((int8)(((packed >> 10) & 0x1f) - 0x10)) & 0x1f) * 0x20 +
+					   (((int8)(((packed >> 5) & 0x1f) - 0x10)) & 0x1f)) *
+						  0x20 +
+					  (((int8)((packed & 0x1f) - 0x10)) & 0x1f);
+		}
+
+		const Common::Array<int> &motionTable = reader.mode == 0 ? fullOffsets : expOffsets;
+		int srcOffset = dstOffset;
+		if (motionIndex < motionTable.size())
+			srcOffset += motionTable[motionIndex];
+		const int sourceWidth = transform < 4 ? blockWidth : blockHeight;
+		const int sourceHeight = transform < 4 ? blockHeight : blockWidth;
+		if (srcOffset < 0 || srcOffset + frameWidth * (sourceHeight - 1) + sourceWidth - 1 >= frameWidth * frameHeight)
+			return false;
+
+		FourXM::copyRawBlock(dst + dstOffset, src + srcOffset, frameWidth, blockWidth, blockHeight, transform, add);
+		return true;
+	}
+
+	bool decodeDctBlock(int dstOffset) {
+		byte scaleY = reader.readNibble();
+		byte scaleCb = reader.readNibble();
+		byte scaleCr = reader.readNibble();
+		int coeffs[3][64] = {};
+		int blocks[3][64] = {};
+		FourXM::readRawCoefficients(reader, coeffs);
+		FourXM::transformRawCoefficients(coeffs[0], blocks[0], scaleY, false);
+		FourXM::transformRawCoefficients(coeffs[1], blocks[1], scaleCb, true);
+		FourXM::transformRawCoefficients(coeffs[2], blocks[2], scaleCr, true);
+		FourXM::writeRawDctBlock(blocks[0], blocks[1], blocks[2], dst + dstOffset, frameWidth);
+		return true;
+	}
+
+	bool decodeCopyBlock(int dstOffset, int blockWidth, int blockHeight, bool allowDct) {
+		return copyLeaf(dstOffset, blockWidth, blockHeight, 0);
+	}
+
+	bool decodeVerticalSplit(int dstOffset, int blockWidth, int blockHeight, bool allowDct) {
+		if (blockHeight < 2)
+			return false;
+		decodeBlock(dstOffset, blockWidth, blockHeight / 2, false);
+		decodeBlock(dstOffset + frameWidth * (blockHeight / 2), blockWidth, blockHeight / 2, false);
+		return true;
+	}
+
+	bool decodeHorizontalSplit(int dstOffset, int blockWidth, int blockHeight, bool allowDct) {
+		if (blockWidth < 2)
+			return false;
+		decodeBlock(dstOffset, blockWidth / 2, blockHeight, false);
+		decodeBlock(dstOffset + blockWidth / 2, blockWidth / 2, blockHeight, false);
+		return true;
+	}
+
+	bool decodeDctOrCopyBlock(int dstOffset, int blockWidth, int blockHeight, bool allowDct) {
+		uint32 subOp = reader.readControl2();
+		if (subOp == 0 && allowDct)
+			return decodeDctBlock(dstOffset);
+		if (subOp == 2)
+			return copyLeaf(dstOffset, blockWidth, blockHeight, 3);
+		return false;
+	}
+
+	bool decodeBlock(int dstOffset, int blockWidth, int blockHeight, bool allowDct) {
+		typedef bool (Raw4XMDeltaDecoder::*BlockOp)(int, int, int, bool);
+		static const BlockOp kBlockOps[4] = {
+			&Raw4XMDeltaDecoder::decodeCopyBlock,
+			&Raw4XMDeltaDecoder::decodeVerticalSplit,
+			&Raw4XMDeltaDecoder::decodeHorizontalSplit,
+			&Raw4XMDeltaDecoder::decodeDctOrCopyBlock};
+
+		return (this->*kBlockOps[reader.readControl2() & 3])(dstOffset, blockWidth, blockHeight, allowDct);
+	}
+};
+
+static bool applyRaw4XMDelta(const byte *data, uint32 size, uint16 *dst, const uint16 *src,
+							 int width, int height, const Common::Array<int> &fullOffsets, const Common::Array<int> &expOffsets) {
+	Raw4XMDeltaDecoder decoder(data, size, dst, src, width, height, fullOffsets, expOffsets);
+	if (!decoder.reader.valid())
+		return false;
+
+	const int blocksX = (width + 7) / 8;
+	const int blocksY = (height + 7) / 8;
+	int blockOffset = 0;
+
+	for (int y = 0; y < blocksY; ++y) {
+		for (int x = 0; x < blocksX; ++x) {
+			decoder.decodeBlock(blockOffset, 8, 8, true);
+			blockOffset += 8;
+		}
+		blockOffset += width * 7;
+	}
+
+	return true;
+}
+
+static Raw4XMCacheEntry &raw4XMFindCacheEntry(Common::Array<Raw4XMCacheEntry> &cache, int32 frame) {
+	for (uint i = 0; i < cache.size(); ++i) {
+		if (cache[i].frame == frame)
+			return cache[i];
+	}
+
+	for (uint i = 0; i < cache.size(); ++i) {
+		if (cache[i].frame == -1) {
+			cache[i].frame = frame;
+			cache[i].declaredSize = 0;
+			cache[i].data.clear();
+			return cache[i];
+		}
+	}
+
+	cache[0].frame = frame;
+	cache[0].declaredSize = 0;
+	cache[0].data.clear();
+	return cache[0];
+}
+
+Audio::PacketizedAudioStream *makeAudioStream(byte audioType, uint audioChannels, uint sampleRate) {
+	switch (audioType) {
+	case 0: {
+		byte flags = Audio::FLAG_16BITS;
+#ifdef SCUMM_LITTLE_ENDIAN
+		flags |= Audio::FLAG_LITTLE_ENDIAN;
+#endif
+		if (audioChannels > 1)
+			flags |= Audio::FLAG_STEREO;
+		return Audio::makePacketizedRawStream(sampleRate, flags);
+	}
+	case 1:
+		return Audio::makePacketizedADPCMStream(Audio::ADPCMType::kADPCM4XM, sampleRate, audioChannels);
+	default:
+		error("FourXMAudioTrack: unknown audio type: %d", audioType);
+	}
+}
+
+} // namespace
+
 class FourXMDecoder::FourXMAudioTrack : public AudioTrack {
 	uint _audioType;
-	uint _audioChannels;
 	Common::ScopedPtr<Audio::PacketizedAudioStream> _output;
 
 public:
-	FourXMAudioTrack(FourXMDecoder *dec, uint trackIdx, uint audioType, uint audioChannels, uint sampleRate) : AudioTrack(Audio::Mixer::SoundType::kPlainSoundType), _audioType(audioType), _audioChannels(audioChannels) {
-		switch (_audioType) {
-		case 0: {
-			// Raw PCM data
-			byte flags = Audio::FLAG_16BITS;
-#ifdef SCUMM_LITTLE_ENDIAN
-			flags |= Audio::FLAG_LITTLE_ENDIAN;
-#endif
-			if (_audioChannels > 1)
-				flags |= Audio::FLAG_STEREO;
-			_output.reset(Audio::makePacketizedRawStream(sampleRate, flags));
-			break;
-		}
-		case 1:
-			_output.reset(Audio::makePacketizedADPCMStream(Audio::ADPCMType::kADPCM4XM, sampleRate, audioChannels));
-			break;
-		default:
-			error("FourXMAudioTrack: unknown audio type: %d", _audioType);
-		}
-	}
+	FourXMAudioTrack(byte audioType, uint audioChannels, uint sampleRate) : AudioTrack(Audio::Mixer::SoundType::kPlainSoundType), _audioType(audioType),
+																			_output(makeAudioStream(audioType, audioChannels, sampleRate)) {}
 
 	byte getAudioType() const { return _audioType; }
 
-	void decode(byte *buf, uint size) {
-		auto *input = new Common::MemoryReadStream(buf, size, DisposeAfterUse::YES);
+	void decode(Common::SeekableReadStream *input) {
 		_output->queuePacket(input);
 	}
 
@@ -93,6 +268,107 @@ private:
 	Audio::AudioStream *getAudioStream() const override { return _output.get(); }
 };
 
+class FourXMDecoder::FourXMRawAudioTrack : public AudioTrack {
+	uint _audioType;
+	uint _channels;
+	uint _bits;
+	int16 _predictor[2] = {0, 0};
+	int _stepIndex[2] = {0, 0};
+	Common::ScopedPtr<Audio::QueuingAudioStream> _output;
+
+public:
+	FourXMRawAudioTrack(uint audioType, uint channels, uint sampleRate, uint bits) : AudioTrack(Audio::Mixer::SoundType::kPlainSoundType), _audioType(audioType),
+																					 _channels(channels), _bits(bits) {
+		if ((audioType == 0 || audioType == 1) && sampleRate > 0 && (channels == 1 || channels == 2) && bits == 16)
+			_output.reset(Audio::makeQueuingAudioStream(sampleRate, channels == 2));
+	}
+
+	void queueRaw(const byte *payload, uint32 payloadSize) {
+		if (!_output || payloadSize < 4)
+			return;
+
+		byte *data = (byte *)malloc(payloadSize - 4);
+		if (!data)
+			return;
+
+		Common::copy(payload + 4, payload + payloadSize, data);
+		_output->queueBuffer(data, payloadSize - 4, DisposeAfterUse::YES, audioFlags());
+	}
+
+	void queueADPCM(const byte *payload, uint32 payloadSize) {
+		if (!_output || _audioType != 1 || _bits != 16 || payloadSize < 4)
+			return;
+
+		const uint32 decodedBytes = READ_LE_UINT32(payload);
+		if (decodedBytes == 0)
+			return;
+
+		const uint32 samplesPerChannel = decodedBytes / (_channels * 2);
+		if (samplesPerChannel == 0)
+			return;
+
+		byte *out = (byte *)malloc(decodedBytes);
+		if (!out)
+			return;
+		Common::fill(out, out + decodedBytes, 0);
+		int16 *pcm = (int16 *)out;
+
+		const byte *src = payload + 4;
+		const uint32 srcSize = payloadSize - 4;
+		if (_channels == 1) {
+			decodeADPCMChannel(src, srcSize, 0, pcm, samplesPerChannel);
+		} else {
+			Common::Array<int16> left;
+			Common::Array<int16> right;
+			left.resize(samplesPerChannel);
+			right.resize(samplesPerChannel);
+
+			const uint32 compressedChannelSize = ((samplesPerChannel + 7) / 8) * 4;
+			decodeADPCMChannel(src, MIN<uint32>(srcSize, compressedChannelSize), 0, left.data(), samplesPerChannel);
+			if (srcSize > compressedChannelSize)
+				decodeADPCMChannel(src + compressedChannelSize, srcSize - compressedChannelSize, 1, right.data(), samplesPerChannel);
+
+			for (uint32 i = 0; i < samplesPerChannel; ++i) {
+				pcm[i * 2] = left[i];
+				pcm[i * 2 + 1] = right[i];
+			}
+		}
+
+		_output->queueBuffer(out, decodedBytes, DisposeAfterUse::YES, audioFlags());
+	}
+
+private:
+	byte audioFlags() const {
+		byte flags = Audio::FLAG_16BITS | Audio::FLAG_LITTLE_ENDIAN;
+		if (_channels == 2)
+			flags |= Audio::FLAG_STEREO;
+		return flags;
+	}
+
+	int16 decodeNibble(byte nibble, uint channel) {
+		const int step = Audio::Ima_ADPCMStream::_imaTable[_stepIndex[channel]];
+		const int diff = ((step / 2) + (nibble & 7) * step) >> 3;
+		const int sample = CLIP<int>(_predictor[channel] + ((nibble & 8) ? -diff : diff), -32768, 32767);
+		_predictor[channel] = sample;
+		_stepIndex[channel] = CLIP<int>(_stepIndex[channel] + Audio::ADPCMStream::_stepAdjustTable[nibble], 0, 88);
+		return _predictor[channel];
+	}
+
+	void decodeADPCMChannel(const byte *src, uint32 srcSize, uint channel, int16 *dst, uint32 samples) {
+		for (uint32 i = 0; i < samples; ++i) {
+			uint32 byteOffset = (i >> 3) * 4 + ((i & 7) >> 1);
+			if (byteOffset >= srcSize)
+				break;
+
+			byte packed = src[byteOffset];
+			byte nibble = (i & 1) ? (packed >> 4) : (packed & 0xf);
+			dst[i] = decodeNibble(nibble, channel);
+		}
+	}
+
+	Audio::AudioStream *getAudioStream() const override { return _output.get(); }
+};
+
 class FourXMDecoder::FourXMVideoTrack : public FixedRateVideoTrack {
 	FourXMDecoder *_dec;
 	Common::Rational _frameRate;
@@ -125,7 +401,7 @@ public:
 	int getFrameCount() const override;
 	const Graphics::Surface *decodeNextFrame() override;
 
-	void decode(uint32 tag, byte *buf, uint size);
+	void decode(uint32 tag, Common::SeekableReadStream *stream);
 	void decode_ifrm(Common::SeekableReadStream *stream);
 	void decode_pfrm(Common::SeekableReadStream *stream);
 	void decode_cfrm(Common::SeekableReadStream *stream);
@@ -163,6 +439,149 @@ int FourXMDecoder::FourXMVideoTrack::getFrameCount() const {
 
 FourXMDecoder::FourXMVideoTrack::~FourXMVideoTrack() = default;
 
+class FourXMDecoder::FourXMRawVideoTrack : public FixedRateVideoTrack {
+	FourXMDecoder *_dec;
+	Common::Rational _frameRate;
+	uint _w, _h;
+	Common::ScopedPtr<Graphics::ManagedSurface> _surface;
+	Common::Array<uint16> _frameBuffer1;
+	Common::Array<uint16> _frameBuffer2;
+	Common::Array<uint16> *_frame = nullptr;
+	Common::Array<uint16> *_previousFrame = nullptr;
+	Common::Array<int> _fullMotionOffsets;
+	Common::Array<int> _expMotionOffsets;
+	Common::Array<Raw4XMCacheEntry> _cache;
+
+public:
+	FourXMRawVideoTrack(FourXMDecoder *dec, const Common::Rational &frameRate, uint w, uint h) : _dec(dec), _frameRate(frameRate), _w(w), _h(h) {
+		_surface.reset(new Graphics::ManagedSurface());
+		_surface->create(w, h, getPixelFormat());
+		_frameBuffer1.resize(w * h);
+		_frameBuffer2.resize(w * h);
+		_frame = &_frameBuffer1;
+		_previousFrame = &_frameBuffer2;
+		_cache.resize(256);
+		FourXM::buildRawMotionTables(w, _fullMotionOffsets, _expMotionOffsets);
+	}
+
+	uint16 getWidth() const override { return _w; }
+	uint16 getHeight() const override { return _h; }
+
+	Graphics::PixelFormat getPixelFormat() const override {
+		return Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0);
+	}
+
+	int getCurFrame() const override { return _dec->_curFrame; }
+	int getFrameCount() const override { return _dec->_frames.size(); }
+
+	const Graphics::Surface *decodeNextFrame() override {
+		if (_dec->_curFrame >= _dec->_frames.size())
+			return _surface->surfacePtr();
+
+		const FourXMDecoder::Frame &frameInfo = _dec->_frames[_dec->_curFrame];
+		_dec->_stream->seek(frameInfo.offset);
+		const uint32 chunkId = _dec->_stream->readUint32LE();
+		const uint32 chunkSize = _dec->_stream->readUint32LE();
+		if (chunkId != kRaw4XMFrameContainer || chunkSize < 8 || frameInfo.offset + chunkSize > frameInfo.end) {
+			warning("invalid raw 4XM frame at offset %" PRId64, frameInfo.offset);
+			++_dec->_curFrame;
+			return _surface->surfacePtr();
+		}
+
+		Common::Array<byte> payload;
+		payload.resize(chunkSize - 8);
+		_dec->_stream->read(payload.data(), payload.size());
+		const bool changed = decodeContainerPayload(payload.data(), payload.size(), _dec->_curFrame);
+		if (changed) {
+			copyFrameToSurface();
+			SWAP(_frame, _previousFrame);
+		}
+
+		++_dec->_curFrame;
+		return _surface->surfacePtr();
+	}
+
+private:
+	Common::Rational getFrameRate() const override { return _frameRate; }
+
+	void copyFrameToSurface() {
+		Graphics::Surface frame;
+		frame.init(_w, _h, _w * sizeof(uint16), _frame->data(), Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0));
+		_surface->convertFrom(frame, getPixelFormat());
+	}
+
+	bool decodeFullFrame(const byte *payload, uint32 payloadSize) {
+		if (payloadSize < _w * _h * 2)
+			return false;
+
+		Common::MemoryReadStream fullFrameStream(payload, payloadSize);
+		for (uint i = 0; i < _w * _h; ++i)
+			(*_frame)[i] = fullFrameStream.readUint16LE();
+		return true;
+	}
+
+	bool decodeCachedFrame(const byte *payload, uint32 payloadSize, uint32 currentFrame) {
+		if (payloadSize < 8)
+			return false;
+
+		const int32 cacheFrame = (int32)READ_LE_UINT32(payload);
+		const uint32 declaredSize = READ_LE_UINT32(payload + 4);
+		Raw4XMCacheEntry &entry = findCacheEntry(cacheFrame);
+		entry.declaredSize = declaredSize;
+		const uint oldSize = entry.data.size();
+		entry.data.resize(oldSize + payloadSize - 8);
+		Common::copy(payload + 8, payload + payloadSize, entry.data.begin() + oldSize);
+
+		if (cacheFrame != (int32)currentFrame || entry.data.size() < 8 ||
+			READ_LE_UINT32(entry.data.data()) != kRaw4XMFrameContainer)
+			return false;
+
+		const uint32 nestedDeclaredSize = entry.declaredSize >= 8 ? entry.declaredSize : READ_LE_UINT32(entry.data.data() + 4);
+		const uint32 nestedPayloadSize = MIN<uint32>(entry.data.size() - 8, nestedDeclaredSize - 8);
+		const bool changed = decodeContainerPayload(entry.data.data() + 8, nestedPayloadSize, currentFrame);
+
+		entry.frame = -1;
+		entry.declaredSize = 0;
+		entry.data.clear();
+		return changed;
+	}
+
+	bool decodeContainerPayload(const byte *payload, uint32 payloadSize, uint32 currentFrame) {
+		bool changed = false;
+		uint32 pos = 0;
+		while (pos + 8 <= payloadSize) {
+			const uint32 subChunkId = READ_LE_UINT32(payload + pos);
+			const uint32 subChunkSize = READ_LE_UINT32(payload + pos + 4);
+			if (subChunkSize < 8 || pos + subChunkSize > payloadSize)
+				break;
+
+			const byte *subPayload = payload + pos + 8;
+			const uint32 subPayloadSize = subChunkSize - 8;
+			if (subChunkId == kRaw4XMFullFrame) {
+				changed = decodeFullFrame(subPayload, subPayloadSize) || changed;
+			} else if (subChunkId == kRaw4XMDeltaFrame) {
+				changed = applyRaw4XMDelta(subPayload, subPayloadSize, _frame->data(), _previousFrame->data(), _w, _h,
+										   _fullMotionOffsets, _expMotionOffsets) ||
+						  changed;
+			} else if (subChunkId == kRaw4XMCachedFrame) {
+				changed = decodeCachedFrame(subPayload, subPayloadSize, currentFrame) || changed;
+			} else if (subChunkId == kRaw4XMCompressedAudio && _dec->_rawAudio) {
+				_dec->_rawAudio->queueADPCM(subPayload, subPayloadSize);
+			} else if (subChunkId == kRaw4XMRawAudio && _dec->_rawAudio) {
+				_dec->_rawAudio->queueRaw(subPayload, subPayloadSize);
+			}
+
+			pos += subChunkSize;
+		}
+
+		return changed;
+	}
+
+	Raw4XMCacheEntry &findCacheEntry(int32 frame) {
+		return raw4XMFindCacheEntry(_cache, frame);
+	}
+};
+
 namespace {
 static const uint8_t iquant[64] = {
 	16,
@@ -487,17 +906,17 @@ void FourXMDecoder::FourXMVideoTrack::decode_cfrm(Common::SeekableReadStream *st
 	}
 }
 
-void FourXMDecoder::FourXMVideoTrack::decode(uint32 tag, byte *buf, uint size) {
-	Common::MemoryReadStream ms(buf, size, DisposeAfterUse::YES);
+void FourXMDecoder::FourXMVideoTrack::decode(uint32 tag, Common::SeekableReadStream *stream) {
+	Common::ScopedPtr<Common::SeekableReadStream> ms(stream);
 	switch (tag) {
 	case MKTAG('i', 'f', 'r', 'm'):
-		decode_ifrm(&ms);
+		decode_ifrm(ms.get());
 		break;
 	case MKTAG('p', 'f', 'r', 'm'):
-		decode_pfrm(&ms);
+		decode_pfrm(ms.get());
 		break;
 	case MKTAG('c', 'f', 'r', 'm'):
-		decode_cfrm(&ms);
+		decode_cfrm(ms.get());
 		break;
 	default:
 		warning("uknown video frame %s", tagName(tag).c_str());
@@ -520,14 +939,6 @@ void FourXMDecoder::decodeNextFrameImpl() {
 		uint32 size = _stream->readUint32LE();
 		auto pos = _stream->pos();
 
-		auto loadBuf = [this](uint bufSize) {
-			byte *buf = static_cast<byte *>(malloc(bufSize));
-			if (!buf)
-				error("failed to allocate %u bytes", bufSize);
-			if (_stream->read(buf, bufSize) != bufSize)
-				error("loadBuf: short read");
-			return buf;
-		};
 		switch (tag) {
 		case MKTAG('s', 'n', 'd', '_'): {
 			if (!_audio)
@@ -539,7 +950,7 @@ void FourXMDecoder::decodeNextFrameImpl() {
 					auto trackIdx = _stream->readUint32LE();
 					auto packetSize = _stream->readUint32LE();
 					if (trackIdx == 0 && _audio) {
-						_audio->decode(loadBuf(packetSize), packetSize);
+						_audio->decode(_stream->readStream(packetSize));
 					} else {
 						_stream->skip(packetSize);
 					}
@@ -550,7 +961,7 @@ void FourXMDecoder::decodeNextFrameImpl() {
 				auto trackIdx = _stream->readUint32LE();
 				_stream->skip(4);
 				if (trackIdx == 0 && _audio) {
-					_audio->decode(loadBuf(size - 8), size - 8);
+					_audio->decode(_stream->readStream(size - 8));
 				}
 			} break;
 			default:
@@ -562,7 +973,7 @@ void FourXMDecoder::decodeNextFrameImpl() {
 		case MKTAG('c', 'f', 'r', 'm'): {
 			auto trackIdx = _stream->readUint32LE();
 			if (trackIdx == 0)
-				_video->decode(tag, loadBuf(size - 4), size - 4);
+				_video->decode(tag, _stream->readStream(size - 4));
 		} break;
 		default:
 			warning("unknown frame type %s", tagName(tag).c_str());
@@ -626,7 +1037,7 @@ void FourXMDecoder::readList(uint32 listEnd) {
 				debug("audio track idx: %u type: %u channels: %u sample rate: %u bits: %u", trackIdx, audioType, audioChannels, sampleRate, sampleResolution);
 				if (sampleResolution != 16)
 					error("only 16 bit audio is supported");
-				addTrack(_audio = new FourXMAudioTrack(this, trackIdx, audioType, audioChannels, sampleRate));
+				addTrack(_audio = new FourXMAudioTrack(audioType, audioChannels, sampleRate));
 			} break;
 			default:
 				break;
@@ -639,12 +1050,66 @@ void FourXMDecoder::readList(uint32 listEnd) {
 	}
 }
 
+bool FourXMDecoder::loadRawStream() {
+	_stream->seek(0);
+	if (_stream->readUint32LE() != kRaw4XMFile || _stream->readUint32LE() != 0x01000000)
+		return false;
+
+	const uint32 width = _stream->readUint32LE();
+	const uint32 height = _stream->readUint32LE();
+	_stream->skip(8);
+	_stream->skip(4);
+	const float frameRate = _stream->readFloatLE();
+	const uint16 audioCodec = _stream->readUint16LE();
+	const uint16 audioChannels = _stream->readUint16LE();
+	const uint32 sampleRate = _stream->readUint32LE();
+	_stream->skip(6);
+	const uint16 bits = _stream->readUint16LE();
+
+	if (width == 0 || height == 0)
+		return false;
+
+	_frameRate = floatToRational(frameRate > 0.0f ? frameRate : 15.0f);
+	debug("raw 4XM video %ux%u, frame rate: %d/%d", width, height, _frameRate.getNumerator(), _frameRate.getDenominator());
+
+	_frames.clear();
+	_stream->seek(0x88);
+	while (_stream->pos() + 8 <= _stream->size()) {
+		const int64 frameOffset = _stream->pos();
+		const uint32 chunkId = _stream->readUint32LE();
+		const uint32 chunkSize = _stream->readUint32LE();
+		const int64 frameEnd = frameOffset + chunkSize;
+		if (chunkSize < 8 || frameEnd > _stream->size())
+			break;
+
+		if (chunkId == kRaw4XMFrameContainer)
+			_frames.push_back({frameOffset, frameEnd});
+		_stream->seek(frameEnd);
+	}
+
+	if (_frames.empty())
+		return false;
+
+	addTrack(_rawVideo = new FourXMRawVideoTrack(this, _frameRate, width, height));
+	if ((audioCodec == 0 || audioCodec == 1) && sampleRate > 0 &&
+		(audioChannels == 1 || audioChannels == 2) && bits == 16) {
+		_rawAudio = new FourXMRawAudioTrack(audioCodec, audioChannels, sampleRate, bits);
+		addTrack(_rawAudio);
+	}
+
+	return getNumTracks() != 0;
+}
+
 bool FourXMDecoder::loadStream(Common::SeekableReadStream *stream) {
 	_stream.reset(stream);
 	if (!stream->size()) {
 		return false;
 	}
 
+	if (stream->readUint32LE() == kRaw4XMFile)
+		return loadRawStream();
+	stream->seek(0);
+
 	uint32 riffTag = stream->readUint32BE();
 	if (riffTag != MKTAG('R', 'I', 'F', 'F')) {
 		warning("Failed to find RIFF header");
diff --git a/video/4xm_decoder.h b/video/4xm_decoder.h
index ebe6b6077fe..cd926826b98 100644
--- a/video/4xm_decoder.h
+++ b/video/4xm_decoder.h
@@ -45,9 +45,12 @@ private:
 
 	class FourXMVideoTrack;
 	class FourXMAudioTrack;
+	class FourXMRawVideoTrack;
+	class FourXMRawAudioTrack;
 
 	void readList(uint32 size);
 	void decodeNextFrameImpl();
+	bool loadRawStream();
 
 	uint32 _dataRate = 0;
 	Common::Rational _frameRate;
@@ -56,6 +59,8 @@ private:
 	uint _curFrame = 0;
 	FourXMVideoTrack *_video = nullptr;
 	FourXMAudioTrack *_audio = nullptr;
+	FourXMRawVideoTrack *_rawVideo = nullptr;
+	FourXMRawAudioTrack *_rawAudio = nullptr;
 };
 
 } // namespace Video
diff --git a/video/4xm_utils.cpp b/video/4xm_utils.cpp
index a6e75361fb0..7ee78a7f138 100644
--- a/video/4xm_utils.cpp
+++ b/video/4xm_utils.cpp
@@ -22,7 +22,11 @@
 #include "video/4xm_utils.h"
 #include "common/bitstream.h"
 #include "common/debug.h"
+#include "common/endian.h"
 #include "common/memstream.h"
+#include "common/textconsole.h"
+#include "graphics/pixelformat.h"
+#include <math.h>
 
 namespace Video {
 namespace FourXM {
@@ -34,6 +38,192 @@ namespace FourXM {
 
 #define MULTIPLY(var, const) ((int)((var) * (unsigned)(const)) >> 16)
 
+namespace {
+
+struct RawTransform {
+	bool swapAxes;
+	bool flipX;
+	bool flipY;
+};
+
+struct RawCoefficientToken {
+	byte zeroes;
+	int8 values[2];
+	byte valueCount;
+};
+
+static const RawTransform kRawTransforms[8] = {
+	{false, false, false},
+	{false, true, false},
+	{false, false, true},
+	{false, true, true},
+	{true, true, true},
+	{true, false, true},
+	{true, true, false},
+	{true, false, false}};
+
+static const int kRawCoefficientOrder[64] = {
+	0, 1, 8, 9, 2, 3, 10, 11,
+	16, 17, 24, 25, 18, 19, 26, 27,
+	4, 5, 12, 20, 13, 6, 7, 14,
+	21, 28, 29, 22, 15, 23, 30, 31,
+	32, 33, 40, 48, 41, 34, 35, 42,
+	49, 56, 57, 50, 43, 51, 58, 59,
+	36, 37, 44, 52, 45, 38, 39, 46,
+	53, 60, 61, 54, 47, 55, 62, 63};
+
+static const RawCoefficientToken kRawCoefficientTokens[8] = {
+	{64, {0, 0}, 0},
+	{5, {0, 0}, 0},
+	{1, {1, 0}, 1},
+	{1, {-1, 0}, 1},
+	{2, {1, 0}, 1},
+	{1, {0, -1}, 2},
+	{2, {0, 1}, 2},
+	{3, {-1, 0}, 1}};
+
+static const Graphics::PixelFormat kRawPixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0);
+
+int rawSign2(byte value) {
+	return (value & 2) ? (value | ~3) : (value + 1);
+}
+
+int rawSign4(byte value) {
+	return (value & 8) ? (value | ~15) : (value + 1);
+}
+
+void rawTransformPixel(int sx, int sy, int width, int height, uint32 mode, int &dx, int &dy) {
+	const RawTransform &transform = kRawTransforms[mode & 7];
+	dx = transform.swapAxes ? sy : sx;
+	dy = transform.swapAxes ? sx : sy;
+	if (transform.flipX)
+		dx = width - 1 - dx;
+	if (transform.flipY)
+		dy = height - 1 - dy;
+}
+
+void readRawCoefficientToken(RawDeltaReader &reader, int coeff[64], int &index) {
+	auto zero = [&](int count) {
+		for (int i = 0; i < count && index < 64; ++i, ++index)
+			coeff[kRawCoefficientOrder[index]] = 0;
+	};
+	auto write = [&](int value) {
+		if (index < 64)
+			coeff[kRawCoefficientOrder[index++]] = value;
+	};
+	auto writeSigned2 = [&](byte value) {
+		write(rawSign2(value));
+	};
+	auto writeSigned4 = [&]() {
+		write(rawSign4(reader.readNibble()));
+	};
+
+	byte code = reader.readNibble();
+	if (code <= 7) {
+		const RawCoefficientToken &token = kRawCoefficientTokens[code];
+
+		zero(code == 0 ? 64 - index : token.zeroes);
+		for (byte i = 0; i < token.valueCount; ++i)
+			write(token.values[i]);
+		return;
+	}
+
+	if (code == 8) {
+		byte header = reader.readNibble();
+		byte pair = reader.readNibble();
+		zero((header >> 2) + 1);
+		writeSigned2(pair >> 2);
+		writeSigned2(pair & 3);
+
+		int valueCount = header & 3;
+		if (valueCount > 0) {
+			byte extra = reader.readNibble();
+			writeSigned2(extra >> 2);
+			if (valueCount > 1)
+				writeSigned2(extra & 3);
+			if (valueCount > 2)
+				writeSigned2(reader.readNibble() >> 2);
+		}
+	} else if (code == 9) {
+		byte header = reader.readNibble();
+		zero((header >> 2) + 1);
+		for (int i = 0; i <= (header & 3); ++i)
+			writeSigned4();
+	} else if (code == 10) {
+		byte value = reader.readNibble();
+		writeSigned2(value >> 2);
+		writeSigned2(value & 3);
+	} else if (code >= 11 && code <= 13) {
+		for (byte i = 0; i < code - 10; ++i)
+			writeSigned4();
+	} else if (code == 14) {
+		write(0);
+	} else {
+		write(reader.readSignedByte());
+	}
+}
+
+} // namespace
+
+RawDeltaReader::RawDeltaReader(const byte *ptr, uint32 len) : data(ptr), size(len) {
+	if (size < 0x18)
+		return;
+
+	mode = READ_LE_UINT32(data);
+	pairOffset = READ_LE_UINT32(data + 8) + 0x18;
+	byteOffset = READ_LE_UINT32(data + 12) + 0x18;
+	nibbleOffset = READ_LE_UINT32(data + 16) + 0x18;
+	controlWord = READ_LE_UINT32(data + 0x18);
+	if (nibbleOffset + 4 <= size)
+		nibbleWord = READ_LE_UINT32(data + nibbleOffset);
+}
+
+bool RawDeltaReader::valid() const {
+	return size >= 0x18;
+}
+
+uint32 RawDeltaReader::readControl2() {
+	uint32 result = controlWord & 3;
+	if (--wordBitsLeft == 0) {
+		uint32 off = 0x18 + wordIndex * 4;
+		controlWord = off + 4 <= size ? READ_LE_UINT32(data + off) : 0;
+		++wordIndex;
+		wordBitsLeft = 16;
+	} else {
+		controlWord >>= 2;
+	}
+	return result;
+}
+
+uint16 RawDeltaReader::readPair() {
+	uint32 off = pairOffset + pairIndex * 2;
+	++pairIndex;
+	return off + 2 <= size ? READ_LE_UINT16(data + off) : 0;
+}
+
+byte RawDeltaReader::readByteIndex() {
+	uint32 off = byteOffset + byteIndex;
+	++byteIndex;
+	return off < size ? data[off] : 0;
+}
+
+int8 RawDeltaReader::readSignedByte() {
+	return (int8)readByteIndex();
+}
+
+uint32 RawDeltaReader::readNibble() {
+	uint32 result = nibbleWord & 0xf;
+	if (--nibbleBitsLeft == 0) {
+		uint32 off = nibbleOffset + nibbleWordIndex * 4;
+		nibbleWord = off + 4 <= size ? READ_LE_UINT32(data + off) : 0;
+		++nibbleWordIndex;
+		nibbleBitsLeft = 8;
+	} else {
+		nibbleWord >>= 4;
+	}
+	return result;
+}
+
 void idct(int16_t block[64], int shift) {
 	int tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7;
 	int tmp10, tmp11, tmp12, tmp13;
@@ -118,5 +308,129 @@ void idct(int16_t block[64], int shift) {
 	}
 }
 
+void buildRawMotionTables(int width, Common::Array<int> &fullOffsets, Common::Array<int> &expOffsets) {
+	Common::Array<int> sorted;
+	sorted.reserve(0x1000);
+	for (int radius = 0; radius < 0x801; ++radius) {
+		for (int y = -0x20; y < 0x20; ++y) {
+			for (int x = -0x20; x < 0x20; ++x) {
+				if (x * x + y * y == radius)
+					sorted.push_back(y * width + x);
+			}
+		}
+	}
+
+	fullOffsets.resize(sorted.size());
+	for (uint i = 0; i < sorted.size(); ++i)
+		fullOffsets[i] = sorted[i];
+
+	static const float kExpMotionScale = 0.021327873691916466f;
+	expOffsets.resize(0x100);
+	int lastIndex = -1;
+	uint out = 0;
+	for (int i = 0; out < expOffsets.size(); ++i) {
+		int index = static_cast<int>(floorf(expf(i * kExpMotionScale))) - 1;
+		if (index >= (int)fullOffsets.size())
+			index = fullOffsets.size() - 1;
+		if (lastIndex < index) {
+			expOffsets[out++] = fullOffsets[index];
+			lastIndex = index;
+		}
+	}
+}
+
+void copyRawBlock(uint16 *dst, const uint16 *src, int stride, int width, int height, uint32 mode, int add) {
+	const int sourceWidth = (mode >= 4) ? height : width;
+	const int sourceHeight = (mode >= 4) ? width : height;
+
+	for (int sy = 0; sy < sourceHeight; ++sy) {
+		for (int sx = 0; sx < sourceWidth; ++sx) {
+			int dx, dy;
+			rawTransformPixel(sx, sy, width, height, mode, dx, dy);
+			if (dx >= 0 && dx < width && dy >= 0 && dy < height)
+				dst[dy * stride + dx] = src[sy * stride + sx] + add;
+		}
+	}
+}
+
+void readRawCoefficients(RawDeltaReader &reader, int coeffs[3][64]) {
+	for (int channel = 0; channel < 3; ++channel) {
+		int *coeff = coeffs[channel];
+		int index = 1;
+		coeff[0] = reader.readSignedByte();
+
+		while (index <= 63)
+			readRawCoefficientToken(reader, coeff, index);
+	}
+}
+
+void transformRawCoefficients(int coeff[64], int dst[64], int scaleCode, bool chroma) {
+	if (!chroma)
+		coeff[0] <<= 1;
+
+	int temp[64] = {};
+	const int dcScale = 1 << scaleCode;
+	const int stage1Scale = 1 << (scaleCode + 2);
+	const int stage2Scale = 1 << (scaleCode + 4);
+
+	int first[10] = {};
+	int base0 = (coeff[8] + coeff[0]) * dcScale;
+	int base1 = (coeff[0] - coeff[8]) * dcScale;
+	int base2 = (coeff[1] + coeff[9]) * dcScale;
+	int base3 = (coeff[1] - coeff[9]) * dcScale;
+	first[0] = base2 + base0;
+	first[1] = base0 - base2;
+	first[8] = base3 + base1;
+	first[9] = base1 - base3;
+
+	for (int y = 0; y < 4; y += 2) {
+		for (int x = 0; x < 4; x += 2) {
+			int index = (x >> 1) + (y >> 1) * 8;
+			int v0 = first[index] + coeff[index + 0x10] * stage1Scale;
+			int v1 = first[index] - coeff[index + 0x10] * stage1Scale;
+			int v2 = coeff[index + 2] + coeff[index + 0x12];
+			int v3 = coeff[index + 2] * stage1Scale - coeff[index + 0x12] * stage1Scale;
+			temp[y * 8 + x] = v2 * stage1Scale + v0;
+			temp[y * 8 + x + 1] = v0 - v2 * stage1Scale;
+			temp[(y + 1) * 8 + x] = v3 + v1;
+			temp[(y + 1) * 8 + x + 1] = v1 - v3;
+		}
+	}
+
+	for (int y = 0; y < 8; y += 2) {
+		for (int x = 0; x < 8; x += 2) {
+			int index = (x >> 1) + (y >> 1) * 8;
+			int v0 = coeff[index + 0x20] * stage2Scale + temp[index];
+			int v1 = temp[index] - coeff[index + 0x20] * stage2Scale;
+			int v2 = coeff[index + 4] + coeff[index + 0x24];
+			int v3 = coeff[index + 4] * stage2Scale - coeff[index + 0x24] * stage2Scale;
+			dst[y * 8 + x] = (v0 + v2 * stage2Scale) >> 6;
+			dst[y * 8 + x + 1] = (v0 - v2 * stage2Scale) >> 6;
+			dst[(y + 1) * 8 + x] = (v3 + v1) >> 6;
+			dst[(y + 1) * 8 + x + 1] = (v1 - v3) >> 6;
+		}
+	}
+}
+
+void writeRawDctBlock(const int yBlock[64], const int cbBlock[64], const int crBlock[64],
+					  uint16 *dst, int stride) {
+	for (int y = 0; y < 8; ++y) {
+		uint16 *dstPixel = dst + y * stride;
+		for (int x = 0; x < 8; ++x) {
+			int index = y * 8 + x;
+			int luma = yBlock[index] + 0x80;
+			int cr = crBlock[index];
+			int cb = cbBlock[index];
+			int red = (cr + luma) >> 3;
+			int green = (luma - ((cr + cb) >> 1)) >> 3;
+			int blue = (luma + cb * 2) >> 3;
+			red = CLIP(red, 0, 0x1f);
+			green = CLIP(green, 0, 0x1f);
+			blue = CLIP(blue, 0, 0x1f);
+			dstPixel[x] = kRawPixelFormat.RGBToColor(red << 3, green << 3, blue << 3);
+		}
+	}
+}
+
 } // namespace FourXM
 } // namespace Video
diff --git a/video/4xm_utils.h b/video/4xm_utils.h
index bd051e26460..b18839b5abb 100644
--- a/video/4xm_utils.h
+++ b/video/4xm_utils.h
@@ -26,6 +26,32 @@
 namespace Video {
 namespace FourXM {
 
+struct RawDeltaReader {
+	const byte *data = nullptr;
+	uint32 size = 0;
+	uint32 mode = 0;
+	uint32 wordBitsLeft = 16;
+	uint32 wordIndex = 1;
+	uint32 controlWord = 0;
+	uint32 pairIndex = 0;
+	uint32 byteIndex = 0;
+	uint32 nibbleBitsLeft = 8;
+	uint32 nibbleWordIndex = 1;
+	uint32 nibbleWord = 0;
+	uint32 pairOffset = 0;
+	uint32 byteOffset = 0;
+	uint32 nibbleOffset = 0;
+
+	RawDeltaReader(const byte *ptr, uint32 len);
+
+	bool valid() const;
+	uint32 readControl2();
+	uint16 readPair();
+	byte readByteIndex();
+	int8 readSignedByte();
+	uint32 readNibble();
+};
+
 template<typename HuffmanType>
 HuffmanType loadStatistics(const byte *&huff, uint &offset) {
 	Common::Array<uint32> freqs, symbols;
@@ -52,6 +78,12 @@ HuffmanType loadStatistics(const byte *&huff, uint &offset) {
 }
 
 void idct(int16_t block[64], int shift = 6);
+void buildRawMotionTables(int width, Common::Array<int> &fullOffsets, Common::Array<int> &expOffsets);
+void copyRawBlock(uint16 *dst, const uint16 *src, int stride, int width, int height, uint32 mode, int add = 0);
+void readRawCoefficients(RawDeltaReader &reader, int coeffs[3][64]);
+void transformRawCoefficients(int coeff[64], int dst[64], int scaleCode, bool chroma);
+void writeRawDctBlock(const int yBlock[64], const int cbBlock[64], const int crBlock[64],
+					  uint16 *dst, int stride);
 
 inline int readInt(int value, unsigned n) {
 	if (n == 0)


Commit: 6f2352736084d42c07f18cbabd8ee65e6ad3871d
    https://github.com/scummvm/scummvm/commit/6f2352736084d42c07f18cbabd8ee65e6ad3871d
Author: Scorp (scorp at mrs.mn)
Date: 2026-06-15T23:26:54+01:00

Commit Message:
PHOENIXVR: Add Dracula script command support

Changed paths:
    engines/phoenixvr/commands.h
    engines/phoenixvr/phoenixvr.cpp
    engines/phoenixvr/phoenixvr.h
    engines/phoenixvr/script.cpp


diff --git a/engines/phoenixvr/commands.h b/engines/phoenixvr/commands.h
index ca03d986cd7..65ff23ed81a 100644
--- a/engines/phoenixvr/commands.h
+++ b/engines/phoenixvr/commands.h
@@ -31,6 +31,15 @@
 namespace PhoenixVR {
 
 namespace {
+struct MultiCD_Use_Install_Path : public Script::Command {
+	Common::String path;
+
+	MultiCD_Use_Install_Path(const Common::Array<Common::String> &args) : path(args[0]) {}
+	void exec(Script::ExecutionContext &ctx) const override {
+		debug("MultiCD_Use_Install_Path %s", path.c_str());
+	}
+};
+
 struct MultiCD_Set_Transition_Script : public Script::Command {
 	Common::String path;
 
@@ -50,6 +59,16 @@ struct MultiCD_Set_Next_Script : public Script::Command {
 	}
 };
 
+struct MultiCD_If_Next_Script : public Script::Command {
+	Common::String cd;
+	Common::String var;
+
+	MultiCD_If_Next_Script(const Common::Array<Common::String> &args) : cd(args[0]), var(args[1]) {}
+	void exec(Script::ExecutionContext &ctx) const override {
+		debug("MultiCD_If_Next_Script %s %s", cd.c_str(), var.c_str());
+	}
+};
+
 struct LoadSave_Enter_Script : public Script::Command {
 	Common::String reloading, notReloading;
 
@@ -193,6 +212,18 @@ struct Sub : public Script::Command {
 	}
 };
 
+struct CopyVar : public Script::Command {
+	Common::String srcVar;
+	Common::String dstVar;
+
+	CopyVar(const Common::Array<Common::String> &args) : srcVar(args[0]), dstVar(args[1]) {}
+
+	void exec(Script::ExecutionContext &ctx) const override {
+		debug("copyvar %s %s", srcVar.c_str(), dstVar.c_str());
+		g_engine->setVariable(dstVar, g_engine->getVariable(srcVar));
+	}
+};
+
 struct Not : public Script::Command {
 	Common::String var;
 
@@ -272,8 +303,15 @@ struct Cmp : public Script::Command {
 	Common::String op;
 	Common::String arg1;
 
-	Cmp(const Common::Array<Common::String> &args) : var(args[0]), negativeVar(args[1]),
-													 arg0(args[2]), op(args[3]), arg1(args[4]) {}
+	Cmp(const Common::Array<Common::String> &args) : var(args[0]), negativeVar(args[1]), arg0(args[2]), op(args[3]) {
+		if (args.size() == 5) {
+			arg1 = args[4];
+		} else {
+			uint opLength = (op.size() > 1 && op[1] == '=') ? 2 : 1;
+			arg1 = op.substr(opLength);
+			op = op.substr(0, opLength);
+		}
+	}
 
 	void exec(Script::ExecutionContext &ctx) const override {
 		debug("cmp %s %s %s %s %s", var.c_str(), negativeVar.c_str(), arg0.c_str(), op.c_str(), arg1.c_str());
@@ -282,6 +320,8 @@ struct Cmp : public Script::Command {
 		auto value1 = valueOf(arg1);
 		if (op == "==") {
 			r = value0 == value1;
+		} else if (op == "!=") {
+			r = value0 != value1;
 		} else if (op == "<") {
 			r = value0 < value1;
 		} else if (op == "<=") {
@@ -349,6 +389,72 @@ struct SaveCoffre : public Script::Command {
 	}
 };
 
+struct ExeDemo : public Script::Command {
+	Common::String var;
+
+	ExeDemo(const Common::Array<Common::String> &args) : var(args[0]) {}
+	void exec(Script::ExecutionContext &ctx) const override {
+		debug("ExeDemo %s", var.c_str());
+		g_engine->setVariable(var, 1);
+	}
+};
+
+struct AfficheImage : public Script::Command {
+	Common::String image;
+	int x;
+	int y;
+
+	AfficheImage(const Common::Array<Common::String> &args) : image(args[0]), x(atoi(args[1].c_str())), y(atoi(args[2].c_str())) {}
+	void exec(Script::ExecutionContext &ctx) const override {
+		g_engine->showImageOverlay(image, x, y);
+	}
+};
+
+struct StopAffiche : public Script::Command {
+	StopAffiche(const Common::Array<Common::String> &args) {}
+	void exec(Script::ExecutionContext &ctx) const override {
+		g_engine->stopImageOverlay();
+	}
+};
+
+struct UpdateStage : public Script::Command {
+	UpdateStage(const Common::Array<Common::String> &args) {}
+	void exec(Script::ExecutionContext &ctx) const override {
+		g_engine->updateStage();
+	}
+};
+
+struct StartCible : public Script::Command {
+	Common::String name;
+	int periodSeconds;
+	Common::Array<int> bounds;
+
+	StartCible(const Common::Array<Common::String> &args) : name(args[0]), periodSeconds(atoi(args[1].c_str())) {
+		for (uint i = 2; i < args.size(); ++i)
+			bounds.push_back(atoi(args[i].c_str()));
+	}
+	void exec(Script::ExecutionContext &ctx) const override {
+		g_engine->startCible(name, periodSeconds, bounds);
+	}
+};
+
+struct StopCible : public Script::Command {
+	StopCible(const Common::Array<Common::String> &args) {}
+	void exec(Script::ExecutionContext &ctx) const override {
+		g_engine->stopCible();
+	}
+};
+
+struct TestCible : public Script::Command {
+	Common::String xVar;
+	Common::String yVar;
+
+	TestCible(const Common::Array<Common::String> &args) : xVar(args[0]), yVar(args[1]) {}
+	void exec(Script::ExecutionContext &ctx) const override {
+		g_engine->testCible(xVar, yVar);
+	}
+};
+
 struct AfficheCoffre : public Script::Command {
 	AfficheCoffre(const Common::Array<Common::String> &args) {}
 	void exec(Script::ExecutionContext &ctx) const override {
@@ -752,14 +858,17 @@ struct End : public Script::Command {
 	E(AddCoffreObject)               \
 	E(AddObject)                     \
 	E(AfficheCoffre)                 \
+	E(AfficheImage)                  \
 	E(AffichePorteF)                 \
 	E(AfficheSelection)              \
 	E(CarteDestination)              \
 	E(ChangeCurseur)                 \
+	E(CopyVar)                       \
 	E(Cmp)                           \
 	E(Discocier)                     \
 	E(DrawTextSelection)             \
 	E(End)                           \
+	E(ExeDemo)                       \
 	E(GetMonde4)                     \
 	E(SetMonde4)                     \
 	E(IsPresent)                     \
@@ -787,6 +896,8 @@ struct End : public Script::Command {
 	E(LoadVariable)                  \
 	E(MemoryRelease)                 \
 	E(MultiCD_Set_Transition_Script) \
+	E(MultiCD_If_Next_Script)        \
+	E(MultiCD_Use_Install_Path)      \
 	E(MultiCD_Set_Next_Script)       \
 	E(PauseTimer)                    \
 	E(Play_AnimBloc)                 \
@@ -804,11 +915,16 @@ struct End : public Script::Command {
 	E(Set_Global_Pan)                \
 	E(Set_Global_Volume)             \
 	E(Scroll)                        \
+	E(StartCible)                    \
+	E(StopAffiche)                   \
 	E(Stop_AnimBloc)                 \
+	E(StopCible)                     \
+	E(TestCible)                     \
 	E(DoAction)                      \
 	E(StartTimer)                    \
 	E(Sub)                           \
 	E(Until)                         \
+	E(UpdateStage)                   \
 	E(While)                         \
 	E(Waves)                         \
 	/* */
@@ -1100,6 +1216,15 @@ struct Fade : public Script::Command {
 	}
 };
 
+struct Transfade : public Script::Command {
+	int speed;
+
+	Transfade(int a0) : speed(a0) {}
+	void exec(Script::ExecutionContext &ctx) const override {
+		g_engine->transFade(speed);
+	}
+};
+
 } // namespace
 } // namespace PhoenixVR
 
diff --git a/engines/phoenixvr/phoenixvr.cpp b/engines/phoenixvr/phoenixvr.cpp
index cc0623bbf3c..97a3c359279 100644
--- a/engines/phoenixvr/phoenixvr.cpp
+++ b/engines/phoenixvr/phoenixvr.cpp
@@ -80,8 +80,7 @@ static Common::String getAmerzoneLevelLabel(const Common::String &script) {
 		{"03VR_PUEBLO", "Le Pueblo"},
 		{"04VR_FLEUVE", "Le Fleuve"},
 		{"05VR_VILLAGEMARAIS", "Le Village"},
-		{"07VRTEMPLE_VOLCAN", "Le Temple"}
-	};
+		{"07VRTEMPLE_VOLCAN", "Le Temple"}};
 
 	for (const auto &level : levels) {
 		if (script.hasPrefixIgnoreCase(level.prefix))
@@ -93,12 +92,10 @@ static Common::String getAmerzoneLevelLabel(const Common::String &script) {
 
 static const char *mfull[] = {
 	"January", "February", "March", "April", "May", "June",
-	"July", "August", "September", "October", "November", "December"
-};
+	"July", "August", "September", "October", "November", "December"};
 
 static const char *wday[] = {
-	"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"
-};
+	"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};
 
 static Common::String makeSaveText(const Common::String &firstLine, const Common::String &secondLine) {
 	Common::String result = firstLine;
@@ -154,8 +151,8 @@ static void fillSaveSlotRect(Graphics::Surface &dst, const Common::Rect &rect, u
 }
 
 static int drawSaveTextBlock(Graphics::Surface &dst, const Graphics::Font *font, const Common::String &text,
-		int x, int y, int width, uint32 color, Graphics::TextAlign align, int lineHeight, bool splitV, int tileY,
-		bool reserveEmptyFinalLine = false) {
+							 int x, int y, int width, uint32 color, Graphics::TextAlign align, int lineHeight, bool splitV, int tileY,
+							 bool reserveEmptyFinalLine = false) {
 	bool hasText = false;
 	for (uint i = 0; i < text.size(); ++i) {
 		if (text[i] != '\n' && text[i] != '\0') {
@@ -249,8 +246,7 @@ static void projectSaveCard(Graphics::ManagedSurface &faceSurface, const Graphic
 		makeVertex(0.0f, 0.0f, srcW, srcH),
 		makeVertex(static_cast<float>(card.w), 0.0f, 0.0f, srcH),
 		makeVertex(static_cast<float>(card.w), static_cast<float>(card.h), 0.0f, 0.0f),
-		makeVertex(0.0f, static_cast<float>(card.h), srcW, 0.0f)
-	};
+		makeVertex(0.0f, static_cast<float>(card.h), srcW, 0.0f)};
 
 	auto rasterizeTriangle = [&](const Vertex &a, const Vertex &b, const Vertex &c) {
 		const float area = (b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x);
@@ -319,6 +315,9 @@ void PhoenixVREngine::resetState() {
 	_angleX.set(0);
 	_angleY.resetRange();
 	_angleY.set(-kPi2);
+	_imageOverlay.reset();
+	_cibleActive = false;
+	_cibleBounds.clear();
 }
 
 PhoenixVREngine::~PhoenixVREngine() {
@@ -575,6 +574,62 @@ void PhoenixVREngine::fade(int start, int stop, int speed) {
 	}
 }
 
+static uint32 transFadePixel(const Graphics::PixelFormat &format, uint32 left, int leftAmount, uint32 right, int rightAmount) {
+	uint8 lr, lg, lb, rr, rg, rb;
+	format.colorToRGB(left, lr, lg, lb);
+	format.colorToRGB(right, rr, rg, rb);
+	return format.RGBToColor(
+		CLIP(CLIP(static_cast<int>(lr) + leftAmount, 0, 255) + CLIP(static_cast<int>(rr) + rightAmount, 0, 255), 0, 255),
+		CLIP(CLIP(static_cast<int>(lg) + leftAmount, 0, 255) + CLIP(static_cast<int>(rg) + rightAmount, 0, 255), 0, 255),
+		CLIP(CLIP(static_cast<int>(lb) + leftAmount, 0, 255) + CLIP(static_cast<int>(rb) + rightAmount, 0, 255), 0, 255));
+}
+
+void PhoenixVREngine::transFade(int speed) {
+	debug("transfade speed: %d", speed);
+
+	Graphics::ManagedSurface oldFrame(_screen->w, _screen->h, _screen->format);
+	Graphics::ManagedSurface newFrame(_screen->w, _screen->h, _screen->format);
+	Graphics::ManagedSurface workFrame(_screen->w, _screen->h, _screen->format);
+
+	oldFrame.simpleBlitFrom(*_screen);
+	renderVR(0);
+	newFrame.simpleBlitFrom(*_screen);
+
+	bool waiting = true;
+	float dt = 0;
+
+	auto renderTransition = [&](int oldAmount, int newAmount) {
+		for (int y = 0; y < _screen->h; ++y) {
+			for (int x = 0; x < _screen->w; ++x) {
+				workFrame.setPixel(x, y, transFadePixel(_screen->format, oldFrame.getPixel(x, y), oldAmount, newFrame.getPixel(x, y), newAmount));
+			}
+		}
+		_screen->simpleBlitFrom(workFrame);
+	};
+
+	auto runTransition = [&](int pos, int direction) {
+		while (!shouldQuit() && waiting && (direction > 0 ? pos < 0 : pos > -256)) {
+			Common::Event event;
+			while (g_system->getEventManager()->pollEvent(event)) {
+				if (event.type == Common::EVENT_KEYDOWN && event.kbd.ascii == ' ')
+					waiting = false;
+			}
+
+			renderTransition(direction > 0 ? 0 : pos, direction > 0 ? pos : 0);
+			_frameLimiter.delayBeforeSwap();
+			_screen->update();
+			dt = _frameLimiter.startFrame() / 1000.0f;
+
+			pos += direction * static_cast<int>(dt * speed * 1000.0f / 16);
+			if (direction > 0 ? pos < 0 : pos > -256)
+				pos += direction;
+		}
+	};
+
+	runTransition(-255, 1);
+	runTransition(0, -1);
+}
+
 void PhoenixVREngine::until(const Common::String &var, int value) {
 	debug("until %s %d", var.c_str(), value);
 	unsigned frameDuration = 0;
@@ -923,6 +978,57 @@ void PhoenixVREngine::resetLockKey() {
 	_prevWarp = -1; // original game does only this o_O
 }
 
+void PhoenixVREngine::showImageOverlay(const Common::String &image, int x, int y) {
+	debug("AfficheImage %s %d %d", image.c_str(), x, y);
+	_imageOverlay.reset(loadSurface(image));
+	_imageOverlayPos = Common::Point(x, y);
+}
+
+void PhoenixVREngine::stopImageOverlay() {
+	debug("StopAffiche");
+	_imageOverlay.reset();
+	updateStage();
+}
+
+void PhoenixVREngine::updateStage() {
+	renderVR(0);
+	_screen->update();
+}
+
+void PhoenixVREngine::startCible(const Common::String &name, int periodSeconds, const Common::Array<int> &bounds) {
+	debug("StartCible %s %d", name.c_str(), periodSeconds);
+	_cibleActive = true;
+	_cibleStartMillis = g_system->getMillis();
+	_ciblePeriodSeconds = periodSeconds;
+	_cibleBounds = bounds;
+}
+
+void PhoenixVREngine::stopCible() {
+	debug("StopCible");
+	_cibleActive = false;
+}
+
+void PhoenixVREngine::testCible(const Common::String &insideVar, const Common::String &outsideVar) {
+	debug("TestCible %s %s", insideVar.c_str(), outsideVar.c_str());
+	if (!_cibleActive)
+		return;
+
+	bool inside = false;
+	int periodMillis = _ciblePeriodSeconds * 1000;
+	if (periodMillis > 0) {
+		int elapsed = (g_system->getMillis() - _cibleStartMillis) % periodMillis;
+		for (uint i = 0; i + 1 < _cibleBounds.size() && _cibleBounds[i] != 0; i += 2) {
+			if (_cibleBounds[i] * 1000 < elapsed && elapsed < _cibleBounds[i + 1] * 1000) {
+				inside = true;
+				break;
+			}
+		}
+	}
+
+	setVariable(insideVar, inside ? 1 : 0);
+	setVariable(outsideVar, inside ? 0 : 1);
+}
+
 void PhoenixVREngine::lockKey(int idx, const Common::String &warp) {
 	_lockKey[idx] = warp;
 }
@@ -1056,9 +1162,15 @@ void PhoenixVREngine::renderVR(float dt) {
 		int16 y = _textRect.top + (_textRect.height() - _text->h) / 2;
 		_screen->blitFrom(*_text, {x, y});
 	}
+	renderImageOverlay();
 	renderTimer();
 }
 
+void PhoenixVREngine::renderImageOverlay() {
+	if (_imageOverlay)
+		paint(*_imageOverlay, _imageOverlayPos);
+}
+
 void PhoenixVREngine::saveVariables() {
 	debug("SaveVariable() - saving variable state");
 	_variableSnapshot.resize(_variableOrder.size());
diff --git a/engines/phoenixvr/phoenixvr.h b/engines/phoenixvr/phoenixvr.h
index 8261d67ac50..75f43b6e296 100644
--- a/engines/phoenixvr/phoenixvr.h
+++ b/engines/phoenixvr/phoenixvr.h
@@ -154,6 +154,7 @@ public:
 	}
 	void interpolateAngle(float x, float y, float speed, float zoom);
 	void fade(int start, int stop, int speed);
+	void transFade(int speed);
 
 	void setXMax(float max) {
 		_angleY.setRange(-max, max);
@@ -203,6 +204,12 @@ public:
 	bool setNextLevel();
 
 	void setGlobalVolume(int vol);
+	void showImageOverlay(const Common::String &image, int x, int y);
+	void stopImageOverlay();
+	void updateStage();
+	void startCible(const Common::String &name, int periodSeconds, const Common::Array<int> &bounds);
+	void stopCible();
+	void testCible(const Common::String &insideVar, const Common::String &outsideVar);
 
 private:
 	static Common::String removeDrive(const Common::String &path);
@@ -219,6 +226,7 @@ private:
 	void tickTimer(float dt);
 	void loadNextScript();
 	void renderVR(float dt);
+	void renderImageOverlay();
 	void renderTimer();
 	void renderFade(int color);
 	void resetState();
@@ -293,6 +301,12 @@ private:
 
 	Common::ScopedPtr<Graphics::ManagedSurface> _text;
 	Common::Rect _textRect;
+	Common::ScopedPtr<Graphics::Surface> _imageOverlay;
+	Common::Point _imageOverlayPos;
+	bool _cibleActive = false;
+	uint32 _cibleStartMillis = 0;
+	int _ciblePeriodSeconds = 0;
+	Common::Array<int> _cibleBounds;
 
 	Common::Array<Common::String> _levels;
 	uint _currentLevel = 0;
diff --git a/engines/phoenixvr/script.cpp b/engines/phoenixvr/script.cpp
index c94900a60b1..409abc6d684 100644
--- a/engines/phoenixvr/script.cpp
+++ b/engines/phoenixvr/script.cpp
@@ -165,6 +165,8 @@ public:
 			expect(',');
 			auto arg2 = nextInt();
 			return CommandPtr(new Fade(arg0, arg1, arg2));
+		} else if (keyword("transfade")) {
+			return CommandPtr(new Transfade(nextInt()));
 		} else if (maybe("setzoom=")) {
 			return CommandPtr(new SetZoom(toRadian(nextInt())));
 		} else if (maybe("setangle=") || keyword("setangle")) {


Commit: 57053995e0dab2625da144ae62a8cc09687f58fe
    https://github.com/scummvm/scummvm/commit/57053995e0dab2625da144ae62a8cc09687f58fe
Author: Scorp (scorp at mrs.mn)
Date: 2026-06-15T23:26:54+01:00

Commit Message:
PHOENIXVR: Add Dracula game detection

Changed paths:
    engines/phoenixvr/detection_tables.h


diff --git a/engines/phoenixvr/detection_tables.h b/engines/phoenixvr/detection_tables.h
index b733ed3b108..bcfc6d010ba 100644
--- a/engines/phoenixvr/detection_tables.h
+++ b/engines/phoenixvr/detection_tables.h
@@ -28,6 +28,8 @@ const PlainGameDescriptor phoenixvrGames[] = {
 	{"necrono", "Necronomicon: The Dawning of Darkness"},
 	{"lochness", "The Cameron Files: The Secret at Loch Ness"},
 	{"messenger", "The Messenger/Louvre: The Final Curse"},
+	{"dracula1", "Dracula: Resurrection"},
+	{"dracula2", "Dracula 2: The Last Sanctuary"},
 	{"amerzone", "Amerzone: The Explorer's Legacy"},
 	{0, 0}
 };
@@ -205,6 +207,26 @@ const ADGameDescription gameDescriptions[] = {
 		GUIO1(GUIO_NONE)
 	},
 
+	{"dracula1",
+		nullptr,
+		AD_ENTRY2s("script.lst", "78060b78cf403ddb7e22903ba7b269d6", 548,
+				   "Compiler.dat", "0f330d5d674bf01a1bc8512e4c5321cc", 58688),
+		Common::EN_ANY,
+		Common::kPlatformWindows,
+		ADGF_DROPPLATFORM,
+		GUIO1(GUIO_NONE)
+	},
+
+	{"dracula2",
+		nullptr,
+		AD_ENTRY2s("script.pak", "ff52d2000eddc5c438f3c8ef50fea858", 277,
+				   "Compiler.dat", "ff30c7ea065af1e86182b428e1f2cc7b", 42650),
+		Common::EN_ANY,
+		Common::kPlatformWindows,
+		ADGF_DROPPLATFORM,
+		GUIO1(GUIO_NONE)
+	},
+
 	// GOG release
 	{"amerzone",
 		nullptr,


Commit: 78e2d343ab88309be283d0fb27450e48fce062f3
    https://github.com/scummvm/scummvm/commit/78e2d343ab88309be283d0fb27450e48fce062f3
Author: Scorp (scorp at mrs.mn)
Date: 2026-06-15T23:26:54+01:00

Commit Message:
PHOENIXVR: Add Dracula script compatibility fixes

Changed paths:
    engines/phoenixvr/phoenixvr.cpp


diff --git a/engines/phoenixvr/phoenixvr.cpp b/engines/phoenixvr/phoenixvr.cpp
index 97a3c359279..53375942cc7 100644
--- a/engines/phoenixvr/phoenixvr.cpp
+++ b/engines/phoenixvr/phoenixvr.cpp
@@ -368,6 +368,8 @@ Common::SeekableReadStream *PhoenixVREngine::tryOpen(const Common::Path &name, C
 	if (s->open(name)) {
 		auto nameStr = name.toString();
 		debug("opened %s", nameStr.c_str());
+		if (nameStr.hasSuffixIgnoreCase(".pak"))
+			return unpack(*s, origName);
 		return s.release();
 	}
 	auto pakName = name.toString();
@@ -440,6 +442,10 @@ void PhoenixVREngine::loadNextScript() {
 		declareVariable(var);
 	if (gameIdMatches("amerzone"))
 		declareVariable("oeuf_pose"); // crash in chapter 7
+	if (gameIdMatches("dracula1")) {
+		declareVariable("P_Alliance"); // Referenced by 0M1Script.lst, declared by 0M2Script.lst
+		declareVariable("reloaddone"); // Referenced by InsertCD.lst, declared by chapter scripts
+	}
 
 	int numWarps = _script->numWarps();
 	_cursors.clear();
@@ -1821,6 +1827,9 @@ bool PhoenixVREngine::enterScript() {
 
 Common::Error PhoenixVREngine::loadGameStream(Common::SeekableReadStream *slot) {
 	auto state = GameState::load(*slot);
+	while (!state.script.empty() &&
+		   (state.script.lastChar() == '\n' || state.script.lastChar() == '\r'))
+		state.script = state.script.substr(0, state.script.size() - 1);
 
 	_loaded = true;
 	killTimer();


Commit: db4a85bee61d9bae8882ee6f4fb9fa6c9872ecb7
    https://github.com/scummvm/scummvm/commit/db4a85bee61d9bae8882ee6f4fb9fa6c9872ecb7
Author: Scorp (scorp at mrs.mn)
Date: 2026-06-15T23:26:54+01:00

Commit Message:
PHOENIXVR: change the way engine handles undefined variables

Game breaking issue in Dracula 2, ifand/ifor ignores undefined variables. Add default for get and skip sets.

Changed paths:
    engines/phoenixvr/commands.h
    engines/phoenixvr/phoenixvr.cpp
    engines/phoenixvr/phoenixvr.h


diff --git a/engines/phoenixvr/commands.h b/engines/phoenixvr/commands.h
index 65ff23ed81a..ea21d3b67a7 100644
--- a/engines/phoenixvr/commands.h
+++ b/engines/phoenixvr/commands.h
@@ -943,7 +943,7 @@ struct IfAnd : public Script::Conditional {
 	void exec(Script::ExecutionContext &ctx) const override {
 		bool result = true;
 		for (auto &var : vars) {
-			if (var.empty())
+			if (var.empty() || !g_engine->hasVariable(var))
 				continue;
 			auto value = g_engine->getVariable(var);
 			debug("ifand, %s: %d", var.c_str(), value);
@@ -964,7 +964,7 @@ struct IfOr : public Script::Conditional {
 	void exec(Script::ExecutionContext &ctx) const override {
 		bool result = false;
 		for (auto &var : vars) {
-			if (var.empty())
+			if (var.empty() || !g_engine->hasVariable(var))
 				continue;
 			auto value = g_engine->getVariable(var);
 			debug("ifor, %s: %d", var.c_str(), value);
diff --git a/engines/phoenixvr/phoenixvr.cpp b/engines/phoenixvr/phoenixvr.cpp
index 53375942cc7..a0e961055b2 100644
--- a/engines/phoenixvr/phoenixvr.cpp
+++ b/engines/phoenixvr/phoenixvr.cpp
@@ -781,15 +781,23 @@ void PhoenixVREngine::declareVariable(const Common::String &name) {
 		_variables.setVal(name, 0);
 }
 
+bool PhoenixVREngine::hasVariable(const Common::String &name) const {
+	return _variables.contains(name);
+}
+
 void PhoenixVREngine::setVariable(const Common::String &name, int value) {
+	if (!hasVariable(name)) {
+		debug("set %s %d - ignored, variable was not declared", name.c_str(), value);
+		return;
+	}
 	debug("set %s %d", name.c_str(), value);
 	_variables.setVal(name, value);
 }
 
 int PhoenixVREngine::getVariable(const Common::String &name) const {
-	if (gameIdMatches("lochness") && name == "tumuAccpet")
-		return _variables.getVal("tumuAccept");
-	return _variables.getVal(name);
+	if (!hasVariable(name))
+		warning("get %s - variable was not declared", name.c_str());
+	return _variables.getValOrDefault(name, 0);
 }
 
 void PhoenixVREngine::playSound(const Common::String &sound, Audio::Mixer::SoundType type, uint8 volume, int loops, bool spatial, float angle) {
diff --git a/engines/phoenixvr/phoenixvr.h b/engines/phoenixvr/phoenixvr.h
index 75f43b6e296..06ca9559902 100644
--- a/engines/phoenixvr/phoenixvr.h
+++ b/engines/phoenixvr/phoenixvr.h
@@ -131,6 +131,7 @@ public:
 	void playMovie(const Common::String &movie);
 
 	void declareVariable(const Common::String &name);
+	bool hasVariable(const Common::String &name) const;
 	void setVariable(const Common::String &name, int value);
 	int getVariable(const Common::String &name) const;
 


Commit: 31742a6d3e31b8494042c007ee7de7df34882359
    https://github.com/scummvm/scummvm/commit/31742a6d3e31b8494042c007ee7de7df34882359
Author: Scorp (scorp at mrs.mn)
Date: 2026-06-15T23:26:54+01:00

Commit Message:
PHOENIXVR: Fix Dracula 2 timer display

Changed paths:
    engines/phoenixvr/commands.h
    engines/phoenixvr/phoenixvr.cpp
    engines/phoenixvr/phoenixvr.h


diff --git a/engines/phoenixvr/commands.h b/engines/phoenixvr/commands.h
index ea21d3b67a7..4373beefa4e 100644
--- a/engines/phoenixvr/commands.h
+++ b/engines/phoenixvr/commands.h
@@ -151,11 +151,12 @@ struct While : public Script::Command {
 
 struct StartTimer : public Script::Command {
 	float seconds;
+	bool showTimer;
 
-	StartTimer(const Common::Array<Common::String> &args) : seconds(atof(args[0].c_str())) {}
+	StartTimer(const Common::Array<Common::String> &args) : seconds(atof(args[0].c_str())), showTimer(args.size() < 2 || atoi(args[1].c_str()) != 0) {}
 	void exec(Script::ExecutionContext &ctx) const override {
-		debug("starttimer %g", seconds);
-		g_engine->startTimer(seconds);
+		debug("starttimer %g %d", seconds, showTimer);
+		g_engine->startTimer(seconds, showTimer);
 	}
 };
 
diff --git a/engines/phoenixvr/phoenixvr.cpp b/engines/phoenixvr/phoenixvr.cpp
index a0e961055b2..d0272ac6b9c 100644
--- a/engines/phoenixvr/phoenixvr.cpp
+++ b/engines/phoenixvr/phoenixvr.cpp
@@ -1104,10 +1104,11 @@ void PhoenixVREngine::executeTest(int idx) {
 		warning("invalid test id %d", idx);
 }
 
-void PhoenixVREngine::startTimer(float seconds) {
+void PhoenixVREngine::startTimer(float seconds, bool showTimer) {
 	_timer = seconds;
 	_initialTimer = seconds;
 	_timerFlags = 5;
+	_showTimer = showTimer;
 }
 
 void PhoenixVREngine::pauseTimer(bool pause, bool deactivate) {
@@ -1125,6 +1126,7 @@ void PhoenixVREngine::pauseTimer(bool pause, bool deactivate) {
 
 void PhoenixVREngine::killTimer() {
 	_timerFlags = 0;
+	_showTimer = false;
 }
 
 void PhoenixVREngine::tickTimer(float dt) {
@@ -1148,21 +1150,25 @@ void PhoenixVREngine::tickTimer(float dt) {
 }
 
 void PhoenixVREngine::renderTimer() {
-	if (_timerFlags == 0 || !_arn)
+	if (_timerFlags == 0 || !_showTimer || !_arn)
 		return;
 	auto timerBg = _arn->get("cadre.bmp");
 	auto timerFg = _arn->get("cadreB.bmp");
 	if (!timerBg || !timerFg)
 		return;
 
-	// Loch-Ness rectangle for now.
 	// Necronomicon has timer in scripts, but does not contain bitmaps for timers.
 	Common::Rect bgRect{320, 16, 632, 44};
 	Common::Rect fgRect{333, 23, 619, 38};
+	if (gameIdMatches("dracula2")) {
+		bgRect = Common::Rect(165, 15, 474, 48);
+		fgRect = Common::Rect(177, 15, 461, 48);
+	}
 	assert(_initialTimer > 0);
 	auto timeLeft = _timer / _initialTimer;
 	fgRect.right = fgRect.left + fgRect.width() * timeLeft;
-	Common::Rect fgSrcRect{static_cast<short>(timerFg->w * timeLeft), timerFg->h};
+	Common::Rect fgSrcRect(0, 0, timerFg->w, timerFg->h);
+	fgSrcRect.right = fgSrcRect.left + fgSrcRect.width() * timeLeft;
 	if (!fgRect.isValidRect() || !fgSrcRect.isValidRect())
 		return;
 	_screen->blitFrom(*timerBg, bgRect.origin());
diff --git a/engines/phoenixvr/phoenixvr.h b/engines/phoenixvr/phoenixvr.h
index 06ca9559902..d9e82daef41 100644
--- a/engines/phoenixvr/phoenixvr.h
+++ b/engines/phoenixvr/phoenixvr.h
@@ -145,7 +145,7 @@ public:
 
 	void resetLockKey();
 	void lockKey(int idx, const Common::String &warp);
-	void startTimer(float seconds);
+	void startTimer(float seconds, bool showTimer);
 	void pauseTimer(bool pause, bool deactivate);
 	void killTimer();
 	void playAnimation(const Common::String &name, const Common::String &var, int varValue, float speed);
@@ -287,6 +287,7 @@ private:
 	static constexpr byte kPaused = 2;
 	static constexpr byte kActive = 4;
 	byte _timerFlags = 0;
+	bool _showTimer = false;
 	float _timer = 0, _initialTimer = 0;
 
 	Common::String _contextScript;


Commit: e6f3921aaae37e379dc7fd7484d41879daf5cc1f
    https://github.com/scummvm/scummvm/commit/e6f3921aaae37e379dc7fd7484d41879daf5cc1f
Author: Scorp (scorp at mrs.mn)
Date: 2026-06-15T23:26:54+01:00

Commit Message:
PHOENIXVR: Improve Dracula runtime script handling

Adds support for whitespace angle-limit commands, direct region-click dispatch using the last duplicate test record, and ARN-backed image overlays used by Dracula scripts.

Missing cursors now warn instead of aborting, timed waits no longer skip on keypress, animation stops signal their completion variable, and save slot text keeps the intended line spacing.

Changed paths:
    engines/phoenixvr/phoenixvr.cpp
    engines/phoenixvr/script.cpp
    engines/phoenixvr/script.h
    engines/phoenixvr/vr.cpp


diff --git a/engines/phoenixvr/phoenixvr.cpp b/engines/phoenixvr/phoenixvr.cpp
index d0272ac6b9c..308f7795349 100644
--- a/engines/phoenixvr/phoenixvr.cpp
+++ b/engines/phoenixvr/phoenixvr.cpp
@@ -651,8 +651,8 @@ void PhoenixVREngine::until(const Common::String &var, int value) {
 
 		// Delay for a bit. All events loops should have a delay
 		// to prevent the system being unduly loaded
-		_frameLimiter.delayBeforeSwap();
 		drawAudioSubtitles();
+		_frameLimiter.delayBeforeSwap();
 		_screen->update();
 		frameDuration = _frameLimiter.startFrame();
 	}
@@ -669,13 +669,6 @@ void PhoenixVREngine::wait(float seconds) {
 		renderVR(frameDuration / 1000.0f);
 		while (g_system->getEventManager()->pollEvent(event)) {
 			switch (event.type) {
-			case Common::EVENT_KEYDOWN: {
-				if (event.kbd.ascii == ' ') {
-					waiting = false;
-				}
-				break;
-			}
-
 			default:
 				break;
 			}
@@ -683,8 +676,8 @@ void PhoenixVREngine::wait(float seconds) {
 
 		// Delay for a bit. All events loops should have a delay
 		// to prevent the system being unduly loaded
-		_frameLimiter.delayBeforeSwap();
 		drawAudioSubtitles();
+		_frameLimiter.delayBeforeSwap();
 		_screen->update();
 		frameDuration = _frameLimiter.startFrame();
 	}
@@ -994,7 +987,21 @@ void PhoenixVREngine::resetLockKey() {
 
 void PhoenixVREngine::showImageOverlay(const Common::String &image, int x, int y) {
 	debug("AfficheImage %s %d %d", image.c_str(), x, y);
-	_imageOverlay.reset(loadSurface(image));
+	_imageOverlay.reset();
+
+	const Graphics::Surface *surface = _arn ? _arn->get(image) : nullptr;
+	if (!surface && !image.contains('.'))
+		surface = _arn ? _arn->get(image + ".bmp") : nullptr;
+	if (!surface) {
+		warning("can't find image overlay %s", image.c_str());
+		return;
+	}
+
+	uint8 r, g, b;
+	surface->format.colorToRGB(surface->getPixel(surface->w - 1, surface->h - 1), r, g, b);
+	_imageOverlay.reset(surface->convertTo(Graphics::BlendBlit::getSupportedPixelFormat()));
+	if (_imageOverlay)
+		_imageOverlay->applyColorKey(r, g, b);
 	_imageOverlayPos = Common::Point(x, y);
 }
 
@@ -1083,8 +1090,10 @@ Graphics::Surface *PhoenixVREngine::loadCursor(const Common::String &path) {
 	if (it != _cursorCache.end())
 		return it->_value;
 	auto s = loadSurface(path);
-	if (!s)
-		error("can't load cursor from %s", path.c_str());
+	if (!s) {
+		warning("can't load cursor from %s", path.c_str());
+		return nullptr;
+	}
 	_cursorCache[path] = s;
 	return s;
 }
@@ -1626,7 +1635,11 @@ Common::Error PhoenixVREngine::run() {
 
 					if (_vr.isVR() ? region->contains3D(vrPos) : region->contains2D(event.mouse.x, event.mouse.y)) {
 						debug("click region %u", i);
-						executeTest(i);
+						if (auto clickTest = _warp->getLastTest(i)) {
+							Script::ExecutionContext ctx;
+							clickTest->scope.exec(ctx);
+						} else
+							warning("invalid test id %u", i);
 						break;
 					}
 				}
@@ -1648,8 +1661,8 @@ Common::Error PhoenixVREngine::run() {
 
 		// Delay for a bit. All events loops should have a delay
 		// to prevent the system being unduly loaded
-		_frameLimiter.delayBeforeSwap();
 		drawAudioSubtitles();
+		_frameLimiter.delayBeforeSwap();
 		_screen->update();
 		frameDuration = _frameLimiter.startFrame();
 	}
@@ -2035,8 +2048,8 @@ void PhoenixVREngine::drawSlot(int idx, int face, int x, int y) {
 			textY = drawSaveTextBlock(dst, font, state.game, textX, textY, textW, color, textAlign, lineHeight, splitV, tileY);
 			drawSaveTextBlock(dst, font, state.info, textX, textY, textW, color, textAlign, lineHeight, splitV, tileY);
 		} else {
-			textY = drawSaveTextBlock(dst, font, state.game, textX, textY, textW, color, textAlign, lineHeight, splitV, tileY, true);
-			drawSaveTextBlock(dst, font, state.info, textX, textY, textW, color, textAlign, lineHeight, splitV, tileY);
+			drawSaveTextBlock(dst, font, state.game, textX, textY, textW, color, textAlign, lineHeight, splitV, tileY, true);
+			drawSaveTextBlock(dst, font, state.info, textX, textY + lineHeight, textW, color, textAlign, lineHeight, splitV, tileY);
 		}
 	}
 
diff --git a/engines/phoenixvr/script.cpp b/engines/phoenixvr/script.cpp
index 409abc6d684..cd6a432e820 100644
--- a/engines/phoenixvr/script.cpp
+++ b/engines/phoenixvr/script.cpp
@@ -192,9 +192,9 @@ public:
 			// or
 			// x, y, zoom, speed
 			return CommandPtr(a3 != 0 ? new InterpolAngle(a0, a1, a3, toRadian(a2)) : new InterpolAngle(a0, a1, a2, 0));
-		} else if (maybe("anglexmax=")) {
+		} else if (maybe("anglexmax=") || keyword("anglexmax")) {
 			return CommandPtr(new AngleXMax(toAngle(nextInt())));
-		} else if (maybe("angleymax=")) {
+		} else if (maybe("angleymax=") || keyword("angleymax")) {
 			auto y0 = toAngle(nextInt());
 			expect(',');
 			auto y1 = toAngle(nextInt());
@@ -302,6 +302,15 @@ Script::TestPtr Script::Warp::getTest(int idx) const {
 	return it != tests.end() ? *it : Script::TestPtr{};
 }
 
+Script::TestPtr Script::Warp::getLastTest(int idx) const {
+	for (uint i = tests.size(); i > 0; --i) {
+		if (tests[i - 1]->idx == idx)
+			return tests[i - 1];
+	}
+
+	return Script::TestPtr{};
+}
+
 Script::Script(Common::SeekableReadStream &s) {
 	uint lineno = 1;
 	while (!s.eos()) {
diff --git a/engines/phoenixvr/script.h b/engines/phoenixvr/script.h
index 8764adb61c8..dc7e93ef0dd 100644
--- a/engines/phoenixvr/script.h
+++ b/engines/phoenixvr/script.h
@@ -105,6 +105,7 @@ public:
 
 		void parseLine(const Common::String &line, uint lineno);
 		TestPtr getTest(int idx) const;
+		TestPtr getLastTest(int idx) const;
 		TestPtr getDefaultTest() const {
 			return getTest(-1);
 		}
diff --git a/engines/phoenixvr/vr.cpp b/engines/phoenixvr/vr.cpp
index efc1c623ca7..42c4ad7162a 100644
--- a/engines/phoenixvr/vr.cpp
+++ b/engines/phoenixvr/vr.cpp
@@ -392,6 +392,7 @@ void VR::stopAnimation(const Common::String &name) {
 	}
 	auto &animation = *it;
 	animation.active = false;
+	g_engine->setVariable(animation.variable, animation.variableValue);
 }
 
 void VR::Animation::renderNextFrame(Graphics::Surface &pic) {


Commit: d5c8d79a1104309a75c8ea84429b4bb960d45176
    https://github.com/scummvm/scummvm/commit/d5c8d79a1104309a75c8ea84429b4bb960d45176
Author: Scorp (scorp at mrs.mn)
Date: 2026-06-15T23:26:54+01:00

Commit Message:
PHOENIXVR: Show VR debug regions as rainbow borders

Replaces the previous wave displacement debug overlay with a rainbow outline for VR hit regions so interiors remain visible while inspecting hotspots.

Changed paths:
    engines/phoenixvr/vr.cpp


diff --git a/engines/phoenixvr/vr.cpp b/engines/phoenixvr/vr.cpp
index 42c4ad7162a..299e287bbd0 100644
--- a/engines/phoenixvr/vr.cpp
+++ b/engines/phoenixvr/vr.cpp
@@ -72,6 +72,31 @@ uint32 YCbCr2RGB(const Graphics::PixelFormat &format, int16 y, int16 cb, int16 c
 	return format.RGBToColor(r, g, b);
 }
 
+uint32 debugRegionColor(const Graphics::PixelFormat &format, float phase) {
+	phase = fmodf(phase, 6.0f);
+	if (phase < 0.0f)
+		phase += 6.0f;
+
+	const int sector = static_cast<int>(phase);
+	const byte up = static_cast<byte>((phase - sector) * 255.0f);
+	const byte down = 255 - up;
+
+	switch (sector) {
+	case 0:
+		return format.RGBToColor(255, up, 0);
+	case 1:
+		return format.RGBToColor(down, 255, 0);
+	case 2:
+		return format.RGBToColor(0, 255, up);
+	case 3:
+		return format.RGBToColor(0, down, 255);
+	case 4:
+		return format.RGBToColor(up, 0, 255);
+	default:
+		return format.RGBToColor(255, 0, down);
+	}
+}
+
 struct Quantisation {
 	int quantY[64];
 	int quantCbCr[64];
@@ -480,7 +505,6 @@ void VR::renderVR(Graphics::Screen *screen, float ax, float ay, float fov, float
 			dst += y * dstPixelsPitchIncrement;
 		}
 		Vector3d ray = line;
-		int dx = regSet ? static_cast<int>(5 * cosf(hint + 100.0f * dstY / h)) : 0;
 
 		for (int dstX = 0; dstX != w; ++dstX, ray += incrementX, ++dst) {
 			auto cube = toCube(ray.x(), ray.y(), ray.z());
@@ -493,20 +517,21 @@ void VR::renderVR(Graphics::Screen *screen, float ax, float ay, float fov, float
 			srcY += (tileId << 8);
 			auto color = _pic->getPixel(srcX, srcY);
 			if (regSet) {
-				int x = 0;
 				regX += regDX;
 				if (regX >= kTau)
 					regX -= kTau;
+				const float regionY = kTau - regY;
 				for (auto &reg : regSet->getRegions()) {
-					if (reg.contains3D(regX, kTau - regY)) {
-						x += dx;
+					if (reg.contains3D(regX, regionY) &&
+						(!reg.contains3D(regX - regDX, regionY) ||
+						 !reg.contains3D(regX + regDX, regionY) ||
+						 !reg.contains3D(regX, regionY - regDY) ||
+						 !reg.contains3D(regX, regionY + regDY))) {
+						color = debugRegionColor(screen->format, hint + dstX * 0.015f + dstY * 0.025f);
+						break;
 					}
 				}
-				if (dstX + x < 0)
-					x = 0;
-				else if (dstX + x >= screen->w)
-					x = screen->w - 1 - dstX;
-				dst[x] = color;
+				*dst = color;
 			} else
 				*dst = color;
 		}



From noreply at scummvm.org  Mon Jun 15 22:36:42 2026
From: noreply at scummvm.org (bluegr)
Date: Mon, 15 Jun 2026 22:36:42 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 486f030a583c72e398609e5f519c57e0b5a36702
Message-ID: <20260615223642.789F67005BC@ron.scummvm.net>

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

Summary:
486f030a58 NANCY: Disable piece rotation if it's not supported in onebuildpuzzle


Commit: 486f030a583c72e398609e5f519c57e0b5a36702
    https://github.com/scummvm/scummvm/commit/486f030a583c72e398609e5f519c57e0b5a36702
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-06-16T01:36:35+03:00

Commit Message:
NANCY: Disable piece rotation if it's not supported in onebuildpuzzle

Fixes right clicking in the chicken coop puzzle in Nancy 10

Changed paths:
    engines/nancy/action/puzzle/onebuildpuzzle.cpp


diff --git a/engines/nancy/action/puzzle/onebuildpuzzle.cpp b/engines/nancy/action/puzzle/onebuildpuzzle.cpp
index d67b9b44dc7..fd1f080afab 100644
--- a/engines/nancy/action/puzzle/onebuildpuzzle.cpp
+++ b/engines/nancy/action/puzzle/onebuildpuzzle.cpp
@@ -424,6 +424,10 @@ void OneBuildPuzzle::updatePieceRender(int pieceIdx) {
 
 void OneBuildPuzzle::rotatePiece(int pieceIdx) {
 	Piece &p = _pieces[pieceIdx];
+
+	if (!_canRotateAll && !p.isPreRotated)
+		return;
+
 	int oldRot = p.curRotation;
 	int oldW = p.rotateSurfaces[oldRot].w;
 	int oldH = p.rotateSurfaces[oldRot].h;



From noreply at scummvm.org  Mon Jun 15 23:33:34 2026
From: noreply at scummvm.org (sev-)
Date: Mon, 15 Jun 2026 23:33:34 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 7494441d8603f640fcb4d8fce63d5c15f4ab8200
Message-ID: <20260615233334.132507005BC@ron.scummvm.net>

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:
824b057902 DIRECTOR: DT: Improve cast details panel and unify script display
f318fc13ae DIRECTOR: DT: Add browser navigation to scripts window, decouple from execution context
7494441d86 DIRECTOR: DT: Add Ctrl+2/3/4 shortcuts for Control Panel, Cast, Score


Commit: 824b0579026a88879916d1c062973efb54b70cd3
    https://github.com/scummvm/scummvm/commit/824b0579026a88879916d1c062973efb54b70cd3
Author: ramyak-sharma (ramyaksharma1 at gmail.com)
Date: 2026-06-16T01:33:29+02:00

Commit Message:
DIRECTOR: DT: Improve cast details panel and unify script display

Show actual data in the cast details panel instead of placeholder ellipses.
scriptText displays as "(hover)" with a multiline tooltip preview; clicking
opens the script in the handlers window via addToOpenHandlers. modifiedBy and
comments show "N/A" when absent.

Removes the old showScriptCasts/addScriptCastToDisplay/renderCastScript
pipeline entirely. all script display now goes through renderScript. Fixes
a bug where the Lingo/Bytecode toggle in the script viewer was a single
global flag shared across all open handlers.

Changed paths:
    engines/director/debugger/debugtools.cpp
    engines/director/debugger/dt-castdetails.cpp
    engines/director/debugger/dt-internal.h
    engines/director/debugger/dt-score.cpp
    engines/director/debugger/dt-scripts.cpp


diff --git a/engines/director/debugger/debugtools.cpp b/engines/director/debugger/debugtools.cpp
index eac525112e0..e121297d662 100644
--- a/engines/director/debugger/debugtools.cpp
+++ b/engines/director/debugger/debugtools.cpp
@@ -524,11 +524,6 @@ ImVec4 convertColor(uint32 color) {
 	return ImGui::ColorConvertU32ToFloat4(color);
 }
 
-static void addScriptCastToDisplay(CastMemberID &id) {
-	_state->_scriptCasts.remove(id);
-	_state->_scriptCasts.push_back(id);
-}
-
 void addToOpenHandlers(ImGuiScript handler) {
 	_state->_openHandlers.erase(handler.id.member);
 	_state->_openHandlers[handler.id.member] = handler;
@@ -553,8 +548,19 @@ void displayScriptRef(CastMemberID &scriptId) {
 
 		ImGui::SetItemTooltip(scriptId.asString().c_str());
 
-		if (ImGui::IsItemClicked(0))
-			addScriptCastToDisplay(scriptId);
+		if (ImGui::IsItemClicked(0)) {
+			ScriptContext *ctx = getScriptContext(scriptId);
+			if (ctx) {
+				Common::String moviePath = g_director->getCurrentMovie()->getArchive()->getPathName().toString();
+				for (auto &handler : ctx->_functionHandlers) {
+					ImGuiScript script = toImGuiScript(ctx->_scriptType, scriptId, handler._key);
+					script.byteOffsets = ctx->_functionByteOffsets[script.handlerId];
+					script.moviePath = moviePath;
+					script.handlerName = formatHandlerName(ctx->_scriptId, scriptId.member, script.handlerId, ctx->_scriptType, false);
+					addToOpenHandlers(script);
+				}
+			}
+		}
 	} else {
 		ImGui::Selectable("  ");
 	}
@@ -934,7 +940,6 @@ void onImGuiRender() {
 		ImGui::EndMainMenuBar();
 	}
 
-	showScriptCasts();
 	showExecutionContext();
 	showHandlers();
 
diff --git a/engines/director/debugger/dt-castdetails.cpp b/engines/director/debugger/dt-castdetails.cpp
index 1999dddddfa..4546709984a 100644
--- a/engines/director/debugger/dt-castdetails.cpp
+++ b/engines/director/debugger/dt-castdetails.cpp
@@ -44,6 +44,7 @@
 namespace Director {
 namespace DT {
 
+
 template<typename... Args>
 void showProperty(const Common::String &title,
 				  const ImVec4 *color,
@@ -448,9 +449,9 @@ void drawRichTextCMprops(RichTextCastMember *member) {
 				for (int i = 7; i >= 0; i--) {
 					bool bitSet = (member->_cropFlags & (1 << i)) != 0;
 					if (bitSet) {
-						ImGui::TextColored(ImVec4(0.4f, 1.0f, 0.4f, 1.0f), "1");
+						ImGui::TextColored(_state->theme->var_color, "1");
 					} else {
-						ImGui::TextColored(ImVec4(0.5f, 0.5f, 0.5f, 1.0f), "0");
+						ImGui::TextDisabled("0");
 					}
 					if (i > 0) {
 						ImGui::SameLine();
@@ -521,8 +522,8 @@ void drawBaseCMprops(CastMember *member) {
 				if (info) {
 					showProperty("name", "%s", info->name.c_str());
 				} else {
-					ImVec4 grayColor = ImVec4(0.5f, 0.5f, 0.5f, 1.0f);
-					showProperty("name", &grayColor, "No Info");
+					ImVec4 disabledColor = ImGui::GetStyleColorVec4(ImGuiCol_TextDisabled);
+					showProperty("name", &disabledColor, "No Info");
 				}
 
 				showProperty("number", "%d", member->getID());
@@ -532,8 +533,8 @@ void drawBaseCMprops(CastMember *member) {
 				if (info && !info->fileName.empty()) {
 					showProperty("fileName", "%s", info->fileName.c_str());
 				} else {
-					ImVec4 grayColor = ImVec4(0.5f, 0.5f, 0.5f, 1.0f);
-					showProperty("fileName", &grayColor, "...");
+					ImVec4 disabledColor = ImGui::GetStyleColorVec4(ImGuiCol_TextDisabled);
+					showProperty("fileName", &disabledColor, "...");
 				}
 
 				showProperty("type", "#%s", castType2str(member->_type));
@@ -544,47 +545,73 @@ void drawBaseCMprops(CastMember *member) {
 				ImGui::TableSetColumnIndex(0);
 				ImGui::Text("scriptText");
 				ImGui::TableSetColumnIndex(1);
-				if (info && !info->script.empty()) {
+				{
 					CastMemberID scriptCastId(member->getID(), cast->_castLibID);
+					ScriptContext *scriptCtx = getScriptContext(scriptCastId);
+					bool hasScript = scriptCtx != nullptr || (info && !info->script.empty());
 					bool hasCastScript = g_director->getCurrentMovie()->getScriptContext(kCastScript, scriptCastId) != nullptr;
-					ImGui::TextColored(hasCastScript ? ImVec4(0.4f, 0.8f, 0.4f, 1.0f) : ImVec4(0.7f, 0.7f, 1.0f, 1.0f), "...");
-					if (ImGui::IsItemHovered()) {
-						ImGui::SetTooltip(hasCastScript ? "%s\n\n(click to open script)" : "%s", info->script.c_str());
-						if (hasCastScript)
+					if (hasScript) {
+						ImGui::TextColored(hasCastScript ? _state->theme->var_color : _state->theme->script_ref, "(hover)");
+						if (ImGui::IsItemHovered()) {
 							ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
+							if (ImGui::BeginTooltip()) {
+								ImGui::PushTextWrapPos(400.0f);
+								if (info && !info->script.empty()) {
+									Common::String tip = info->script;
+									tip.replace('\r', '\n');
+									if (tip.size() > 500)
+										tip = tip.substr(0, 500) + "\n...";
+									ImGui::TextUnformatted(tip.c_str());
+									ImGui::Separator();
+								}
+								ImGui::TextDisabled("(click to open script)");
+								ImGui::PopTextWrapPos();
+								ImGui::EndTooltip();
+							}
+						}
+						if (ImGui::IsItemClicked(0)) {
+							if (scriptCtx) {
+								Common::String moviePath = g_director->getCurrentMovie()->getArchive()->getPathName().toString();
+								for (auto &handler : scriptCtx->_functionHandlers) {
+									ImGuiScript script = toImGuiScript(scriptCtx->_scriptType, scriptCastId, handler._key);
+									script.byteOffsets = scriptCtx->_functionByteOffsets[script.handlerId];
+									script.moviePath = moviePath;
+									script.handlerName = formatHandlerName(scriptCtx->_scriptId, scriptCastId.member, script.handlerId, scriptCtx->_scriptType, false);
+									addToOpenHandlers(script);
+								}
+							}
+						}
+					} else {
+						ImGui::TextDisabled("...");
 					}
-					if (hasCastScript && ImGui::IsItemClicked(0))
-						displayScriptRef(scriptCastId);
-				} else {
-					ImGui::TextColored(ImVec4(0.5f, 0.5f, 0.5f, 1.0f), "...");
 				}
 
 				if (info && info->creationTime != 0) {
 					showProperty("creationDate", "%u", info->creationTime);
 				} else {
-					ImVec4 grayColor = ImVec4(0.5f, 0.5f, 0.5f, 1.0f);
-					showProperty("creationDate", &grayColor, "...");
+					ImVec4 disabledColor = ImGui::GetStyleColorVec4(ImGuiCol_TextDisabled);
+					showProperty("creationDate", &disabledColor, "N/A");
 				}
 
 				if (info && info->modifiedTime != 0) {
 					showProperty("modifiedDate", "%u", info->modifiedTime);
 				} else {
-					ImVec4 grayColor = ImVec4(0.5f, 0.5f, 0.5f, 1.0f);
-					showProperty("modifiedDate", &grayColor, "...");
+					ImVec4 disabledColor = ImGui::GetStyleColorVec4(ImGuiCol_TextDisabled);
+					showProperty("modifiedDate", &disabledColor, "N/A");
 				}
 
 				if (info && !info->modifiedBy.empty()) {
 					showProperty("modifiedBy", "%s", info->modifiedBy.c_str());
 				} else {
-					ImVec4 grayColor = ImVec4(0.5f, 0.5f, 0.5f, 1.0f);
-					showProperty("modifiedBy", &grayColor, "...");
+					ImVec4 disabledColor = ImGui::GetStyleColorVec4(ImGuiCol_TextDisabled);
+					showProperty("modifiedBy", &disabledColor, "N/A");
 				}
 
 				if (info && !info->comments.empty()) {
 					showProperty("comments", "%s", info->comments.c_str());
 				} else {
-					ImVec4 grayColor = ImVec4(0.5f, 0.5f, 0.5f, 1.0f);
-					showProperty("comments", &grayColor, "...");
+					ImVec4 disabledColor = ImGui::GetStyleColorVec4(ImGuiCol_TextDisabled);
+					showProperty("comments", &disabledColor, "N/A");
 				}
 
 				showProperty("purgePriority", "%d", member->_purgePriority);
@@ -604,8 +631,8 @@ void drawBaseCMprops(CastMember *member) {
 
 				ImGuiImage media = getImageID(member);
 
-				ImVec4 grayColor = ImVec4(0.5f, 0.5f, 0.5f, 1.0f);
-				showProperty("media", &grayColor, "%s", media.id == 0 ? "empty" : "...");
+				ImVec4 disabledColor = ImGui::GetStyleColorVec4(ImGuiCol_TextDisabled);
+				showProperty("media", &disabledColor, "%s", media.id == 0 ? "empty" : "...");
 
 				// Not using showProperty() here because we want to show a
 				// thumbnail of the media instead of text.
@@ -933,10 +960,10 @@ void drawSoundCMprops(SoundCastMember *member) {
 					showProperty("sampleSize", "%d bit", member->_audio->getSampleSize());
 					showProperty("channels", "%d", member->_audio->getChannelCount());
 				} else {
-					ImVec4 gray = ImVec4(0.5f, 0.5f, 0.5f, 1.0f);
-					showProperty("sampleRate", &gray, "N/A");
-					showProperty("sampleSize", &gray, "N/A");
-					showProperty("channels", &gray, "N/A");
+					ImVec4 disabledColor = ImGui::GetStyleColorVec4(ImGuiCol_TextDisabled);
+					showProperty("sampleRate", &disabledColor, "N/A");
+					showProperty("sampleSize", &disabledColor, "N/A");
+					showProperty("channels", &disabledColor, "N/A");
 				}
 				ImGui::EndTable();
 			}
diff --git a/engines/director/debugger/dt-internal.h b/engines/director/debugger/dt-internal.h
index 1372e834365..673791dde7e 100644
--- a/engines/director/debugger/dt-internal.h
+++ b/engines/director/debugger/dt-internal.h
@@ -57,6 +57,7 @@ typedef struct ImGuiScriptCodeLine {
 
 typedef struct ImGuiScript {
 	bool score = false;
+	bool showByteCode = false;
 	CastMemberID id;
 	ScriptType type;
 	Common::String handlerId;
@@ -276,7 +277,6 @@ typedef struct ImGuiState {
 	ImGuiWindows _savedW;
 	bool _wasHidden = false;
 
-	Common::List<CastMemberID> _scriptCasts;
 	Common::HashMap<int, ImGuiScript> _openHandlers;
 	bool _showCompleteScript = true;
 
@@ -385,7 +385,6 @@ void renderScriptAST(ImGuiScript &script, bool showByteCode, bool scrollTo);
 
 // dt-scripts.cpp
 void showFuncList();
-void showScriptCasts();
 void showExecutionContext();
 void showHandlers();
 
diff --git a/engines/director/debugger/dt-score.cpp b/engines/director/debugger/dt-score.cpp
index fcc05364f08..2f66701c6e6 100644
--- a/engines/director/debugger/dt-score.cpp
+++ b/engines/director/debugger/dt-score.cpp
@@ -959,12 +959,6 @@ static void drawMainChannelGrid(ImDrawList *dl, ImVec2 startPos, Score *score) {
 				case kChPalette: // open cast window focused on palette member
 					if (mc.palette.paletteId.member) {
 						_state->_w.cast = true;
-						// select it in the cast so showCast() highlights it
-						for (auto &scriptCast : _state->_scriptCasts) {
-							if (scriptCast == mc.palette.paletteId) {
-								break;
-							}
-						}
 					}
 					break;
 				case kChTransition:
diff --git a/engines/director/debugger/dt-scripts.cpp b/engines/director/debugger/dt-scripts.cpp
index 402f96376cf..c44bf27c76b 100644
--- a/engines/director/debugger/dt-scripts.cpp
+++ b/engines/director/debugger/dt-scripts.cpp
@@ -36,69 +36,6 @@
 namespace Director {
 namespace DT {
 
-static void renderCastScript(Symbol &sym) {
-	if (sym.type != HANDLER)
-		return;
-
-	Director::Lingo *lingo = g_director->getLingo();
-	Common::String handlerName;
-
-	if (sym.ctx && sym.ctx->_id)
-		handlerName = Common::String::format("%d:", sym.ctx->_id);
-
-	handlerName += lingo->formatFunctionName(sym);
-
-	ImGui::Text("%s", handlerName.c_str());
-
-	ImDrawList *dl = ImGui::GetWindowDrawList();
-
-	ImVec4 color;
-
-	uint pc = 0;
-	while (pc < sym.u.defn->size()) {
-		ImVec2 pos = ImGui::GetCursorScreenPos();
-		const ImVec2 mid(pos.x + 7, pos.y + 7);
-		Common::String bpName = Common::String::format("%s-%d", handlerName.c_str(), pc);
-
-		color = _state->theme->bp_color_disabled;
-
-		Director::Breakpoint *bp = getBreakpoint(handlerName, sym.ctx->_id, pc);
-		if (bp)
-			color = _state->theme->bp_color_enabled;
-
-		ImGui::PushID(pc);
-		ImGui::InvisibleButton("Line", ImVec2(16, ImGui::GetFontSize()));
-		if (ImGui::IsItemClicked(0)) {
-			if (bp) {
-				g_lingo->delBreakpoint(bp->id);
-				color = _state->theme->bp_color_disabled;
-			} else {
-				Director::Breakpoint newBp;
-				newBp.type = kBreakpointFunction;
-				newBp.funcName = handlerName;
-				newBp.funcOffset = pc;
-				g_lingo->addBreakpoint(newBp);
-				color = _state->theme->bp_color_enabled;
-			}
-		}
-
-		if (color == _state->theme->bp_color_disabled && ImGui::IsItemHovered()) {
-			color = _state->theme->bp_color_hover;
-		}
-
-		dl->AddCircleFilled(mid, 4.0f, ImColor(color));
-		dl->AddLine(ImVec2(pos.x + 16.0f, pos.y), ImVec2(pos.x + 16.0f, pos.y + 17), ImColor(_state->theme->line_color));
-
-		ImGui::SetItemTooltip("Click to add a breakpoint");
-
-		ImGui::SameLine();
-		ImGui::Text("[%5d] ", pc);
-		ImGui::SameLine();
-		ImGui::Text("%s", lingo->decodeInstruction(sym.u.defn, pc, &pc).c_str());
-		ImGui::PopID();
-	}
-}
-
 static void renderScript(ImGuiScript &script, bool showByteCode, bool scrollTo) {
 	if (script.oldAst) {
 		renderOldScriptAST(script, showByteCode, scrollTo);
@@ -176,55 +113,7 @@ static void renderCallStack(uint pc) {
 	}
 }
 
-static bool showScriptCast(CastMemberID &id) {
-	Common::String wName("Script ");
-	wName += id.asString();
-
-	ImGui::SetNextWindowPos(ImVec2(20, 160), ImGuiCond_FirstUseEver);
-	ImGui::SetNextWindowSize(ImVec2(240, 240), ImGuiCond_FirstUseEver);
-
-	bool closed = true;
-
-	if (ImGui::Begin(wName.c_str(), &closed)) {
-		Cast *cast = g_director->getCurrentMovie()->getCasts()->getValOrDefault(id.castLib, nullptr);
-		ScriptContext *ctx = g_director->getCurrentMovie()->getScriptContext(kScoreScript, id);
-
-		if (ctx) {
-			for (auto &handler : ctx->_functionHandlers)
-				renderCastScript(handler._value);
-		} else if (cast && cast->_lingoArchive->factoryContexts.contains(id.member)) {
-			for (auto &it : *cast->_lingoArchive->factoryContexts.getVal(id.member)) {
-				for (auto &handler : it._value->_functionHandlers)
-					renderCastScript(handler._value);
-			}
-		} else {
-			ImGui::Text("[Nothing]");
-		}
-	}
-	ImGui::End();
-
-	if (!closed)
-		return false;
-
-	return true;
-}
-
-/**
- * Display all open scripts
- */
-void showScriptCasts() {
-	if (_state->_scriptCasts.empty())
-		return;
-
-	for (Common::List<CastMemberID>::iterator scr = _state->_scriptCasts.begin(); scr != _state->_scriptCasts.end();) {
-		if (!showScriptCast(*scr))
-			scr = _state->_scriptCasts.erase(scr);
-		else
-			scr++;
-	}
-}
-
-static bool showHandler(ImGuiScript handler) {
+static bool showHandler(ImGuiScript &handler) {
 	ScriptContext *ctx = getScriptContext(handler.id);
 	Common::String wName;
 	if (ctx) {
@@ -237,27 +126,14 @@ static bool showHandler(ImGuiScript handler) {
 	bool closed = true;
 
 	if (ImGui::Begin(wName.c_str(), &closed)) {
-		ImGuiEx::toggleButton(ICON_MS_PACKAGE_2, &_state->_showCompleteScript, true); // Lingo
-		ImGui::SetItemTooltip("Show Handler");
+		ImGuiEx::toggleButton(ICON_MS_PACKAGE_2, &handler.showByteCode, true); // Lingo
+		ImGui::SetItemTooltip("Show Lingo");
 
 		ImGui::SameLine();
-		ImGuiEx::toggleButton(ICON_MS_STACKS, &_state->_showCompleteScript); // Bytecode
-		ImGui::SetItemTooltip("Show Script Context");
-
-		if (!ctx || ctx->_functionHandlers.size() <= 1 || !_state->_showCompleteScript) {
-			renderScript(handler, false, true);
-		} else {
-			for (auto &functionHandler : ctx->_functionHandlers) {
-				ImGuiScript script = toImGuiScript(ctx->_scriptType, handler.id, functionHandler._key);
-				script.byteOffsets = ctx->_functionByteOffsets[script.handlerId];
+		ImGuiEx::toggleButton(ICON_MS_STACKS, &handler.showByteCode); // Bytecode
+		ImGui::SetItemTooltip("Show Bytecode");
 
-				if (script == handler) {
-					_state->_dbg._goToDefinition = true;
-				}
-				renderScript(script, false, script == handler);
-				ImGui::NewLine();
-			}
-		}
+		renderScript(handler, handler.showByteCode, true);
 	}
 	ImGui::End();
 
@@ -276,7 +152,7 @@ void showHandlers() {
 	}
 
 	Common::Array<uint32> toClose;
-	for (auto handler : _state->_openHandlers) {
+	for (auto &handler : _state->_openHandlers) {
 		if (!showHandler(handler._value))
 			toClose.push_back(handler._value.id.member);
 	}


Commit: f318fc13ae404b1e857d61a239307eec4c75cf76
    https://github.com/scummvm/scummvm/commit/f318fc13ae404b1e857d61a239307eec4c75cf76
Author: ramyak-sharma (ramyaksharma1 at gmail.com)
Date: 2026-06-16T01:33:29+02:00

Commit Message:
DIRECTOR: DT: Add browser navigation to scripts window, decouple from execution context

Replace the per-handler floating windows with a single "Scripts" window
backed by browser-style history (back/forward buttons + dropdown combo).
Extract renderScriptNavBar() and renderScriptContext() as shared helpers
used by both the Scripts window and Execution Context, removing ~80 lines
of duplicated code.

addToOpenHandlers() now pushes into ScriptData _openScripts with forward-
history truncation, replacing HashMap<int, ImGuiScript> _openHandlers which
had a collision bug (two scripts from different cast libs with the same
member number would overwrite each other).

Also fixes: closing the Scripts window and clicking the same script now
reopens it; back/forward in the non-stage execution context window now sets
_goToDefinition; combo entries now use PushID(); getScriptContext() falls
back to lctxContexts so Movie/parent scripts are found by cast member ID.

Changed paths:
    engines/director/debugger/debugtools.cpp
    engines/director/debugger/dt-internal.h
    engines/director/debugger/dt-scripts.cpp


diff --git a/engines/director/debugger/debugtools.cpp b/engines/director/debugger/debugtools.cpp
index e121297d662..3d4b3ef210b 100644
--- a/engines/director/debugger/debugtools.cpp
+++ b/engines/director/debugger/debugtools.cpp
@@ -133,7 +133,7 @@ ImGuiScript toImGuiScript(ScriptType scriptType, CastMemberID id, const Common::
 }
 
 ScriptContext *getScriptContext(CastMemberID id) {
-	const Director::Movie *movie = g_director->getCurrentMovie();;
+	const Director::Movie *movie = g_director->getCurrentMovie();
 	const Cast *cast;
 
 	if (id.castLib == SHARED_CAST_LIB)
@@ -141,12 +141,20 @@ ScriptContext *getScriptContext(CastMemberID id) {
 	else
 		cast = movie->getCasts()->getVal(id.castLib);
 
-	if (!cast) {
+	if (!cast)
 		return nullptr;
-	}
 
 	ScriptContext *ctx = cast->_lingoArchive->findScriptContext(id.member);
-	return ctx;
+	if (ctx)
+		return ctx;
+
+	// Some scripts are only in lctxContexts (keyed by lctx index, not cast member ID).
+	// Do a reverse lookup: find the lctx entry whose cast ID maps to id.member.
+	for (auto &entry : cast->_lingoArchive->lctxContexts) {
+		if (cast->getCastIdByScriptId(entry._key) == id.member)
+			return entry._value;
+	}
+	return nullptr;
 }
 
 ScriptContext *getScriptContext(uint32 nameIndex, CastMemberID id, Common::String handlerName) {
@@ -525,8 +533,17 @@ ImVec4 convertColor(uint32 color) {
 }
 
 void addToOpenHandlers(ImGuiScript handler) {
-	_state->_openHandlers.erase(handler.id.member);
-	_state->_openHandlers[handler.id.member] = handler;
+	ScriptData &data = _state->_openScripts;
+	_state->_w.scripts = true;  // always (re)open the window
+	// Truncate forward history when navigating to a new script
+	if (data._current + 1 < data._scripts.size())
+		data._scripts.resize(data._current + 1);
+	// Don't add a duplicate at the current position
+	if (!data._scripts.empty() && data._scripts.back() == handler)
+		return;
+	data._scripts.push_back(handler);
+	data._current = data._scripts.size() - 1;
+	data._showScript = true;
 }
 
 void setScriptToDisplay(const ImGuiScript &script) {
@@ -941,7 +958,7 @@ void onImGuiRender() {
 	}
 
 	showExecutionContext();
-	showHandlers();
+	showScriptsWindow();
 
 	showControlPanel();
 	showVars();
diff --git a/engines/director/debugger/dt-internal.h b/engines/director/debugger/dt-internal.h
index 673791dde7e..24d21b061d5 100644
--- a/engines/director/debugger/dt-internal.h
+++ b/engines/director/debugger/dt-internal.h
@@ -98,6 +98,7 @@ typedef struct ImGuiWindows {
 	bool archive = false;
 	bool watchedVars = false;
 	bool executionContext = false;
+	bool scripts = false;
 	bool search = false;
 	bool imageViewer = false;
 	bool windows = false;
@@ -277,7 +278,7 @@ typedef struct ImGuiState {
 	ImGuiWindows _savedW;
 	bool _wasHidden = false;
 
-	Common::HashMap<int, ImGuiScript> _openHandlers;
+	ScriptData _openScripts;
 	bool _showCompleteScript = true;
 
 	Common::HashMap<Common::String, bool, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> _variables;
@@ -386,7 +387,7 @@ void renderScriptAST(ImGuiScript &script, bool showByteCode, bool scrollTo);
 // dt-scripts.cpp
 void showFuncList();
 void showExecutionContext();
-void showHandlers();
+void showScriptsWindow();
 
 // dt-save-state.cpp
 void saveCurrentState();
diff --git a/engines/director/debugger/dt-scripts.cpp b/engines/director/debugger/dt-scripts.cpp
index c44bf27c76b..562b24ddaaf 100644
--- a/engines/director/debugger/dt-scripts.cpp
+++ b/engines/director/debugger/dt-scripts.cpp
@@ -113,51 +113,112 @@ static void renderCallStack(uint pc) {
 	}
 }
 
-static bool showHandler(ImGuiScript &handler) {
-	ScriptContext *ctx = getScriptContext(handler.id);
-	Common::String wName;
-	if (ctx) {
-		wName = Common::String(ctx->asString());
-	}
-
-	ImGui::SetNextWindowPos(ImVec2(20, 160), ImGuiCond_FirstUseEver);
-	ImGui::SetNextWindowSize(ImVec2(480, 540), ImGuiCond_FirstUseEver);
+static Common::String getHandlerName(Symbol &sym) {
+	Common::String handlerName;
+	if (sym.ctx && sym.ctx->_id)
+		handlerName = Common::String::format("%d:", sym.ctx->_id);
+	handlerName += g_lingo->formatFunctionName(sym);
+	return handlerName;
+}
 
-	bool closed = true;
+// Renders back/forward navigation, handler dropdown, and Lingo/Bytecode toggle for a ScriptData.
+// Returns true when there is a script to render.
+static bool renderScriptNavBar(ScriptData &data) {
+	if (data._scripts.empty())
+		return false;
 
-	if (ImGui::Begin(wName.c_str(), &closed)) {
-		ImGuiEx::toggleButton(ICON_MS_PACKAGE_2, &handler.showByteCode, true); // Lingo
-		ImGui::SetItemTooltip("Show Lingo");
+	ImGui::BeginDisabled(data._current == 0);
+	if (ImGui::Button(ICON_MS_ARROW_BACK)) {
+		data._current--;
+		_state->_dbg._goToDefinition = true;
+	}
+	ImGui::EndDisabled();
+	ImGui::SetItemTooltip("Backward");
+	ImGui::SameLine();
+
+	ImGui::BeginDisabled(data._current >= data._scripts.size() - 1);
+	if (ImGui::Button(ICON_MS_ARROW_FORWARD)) {
+		data._current++;
+		_state->_dbg._goToDefinition = true;
+	}
+	ImGui::EndDisabled();
+	ImGui::SetItemTooltip("Forward");
+	ImGui::SameLine();
+
+	const char *currentName = data._scripts[data._current].handlerName.c_str();
+	if (ImGui::BeginCombo("##handlers", currentName)) {
+		for (uint i = 0; i < data._scripts.size(); i++) {
+			bool selected = (i == data._current);
+			ImGui::PushID(i);
+			if (ImGui::Selectable(data._scripts[i].handlerName.c_str(), &selected)) {
+				data._current = i;
+				_state->_dbg._goToDefinition = true;
+			}
+			ImGui::PopID();
+		}
+		ImGui::EndCombo();
+	}
 
+	if (!data._scripts[data._current].oldAst) {
+		ImGui::SameLine(0, 20);
+		ImGuiEx::toggleButton(ICON_MS_PACKAGE_2, &data._showByteCode, true);
+		ImGui::SetItemTooltip("Lingo");
 		ImGui::SameLine();
-		ImGuiEx::toggleButton(ICON_MS_STACKS, &handler.showByteCode); // Bytecode
-		ImGui::SetItemTooltip("Show Bytecode");
-
-		renderScript(handler, handler.showByteCode, true);
+		ImGuiEx::toggleButton(ICON_MS_STACKS, &data._showByteCode);
+		ImGui::SetItemTooltip("Bytecode");
 	}
-	ImGui::End();
-
-	if (!closed)
-		return false;
 
 	return true;
 }
 
-/**
- * Display all open handlers
- */
-void showHandlers() {
-	if (_state->_openHandlers.empty()) {
+// Renders all handlers in the context, scrolling to the selected one.
+static void renderScriptContext(ScriptData &data, const Movie *movie) {
+	ImGuiScript &current = data._scripts[data._current];
+	ScriptContext *context = getScriptContext(current.id);
+	bool scrollTo = _state->_dbg._goToDefinition;
+
+	if (!context || context->_functionHandlers.size() == 1) {
+		renderScript(current, data._showByteCode, scrollTo);
 		return;
 	}
 
-	Common::Array<uint32> toClose;
-	for (auto &handler : _state->_openHandlers) {
-		if (!showHandler(handler._value))
-			toClose.push_back(handler._value.id.member);
+	for (auto &functionHandler : context->_functionHandlers) {
+		if (current.handlerId == functionHandler._key) {
+			renderScript(current, data._showByteCode, scrollTo);
+		} else {
+			ImGuiScript script = toImGuiScript(context->_scriptType, current.id, functionHandler._key);
+			script.byteOffsets = context->_functionByteOffsets[script.handlerId];
+			script.moviePath = movie->getArchive()->getPathName().toString();
+			script.handlerName = getHandlerName(functionHandler._value);
+			renderScript(script, data._showByteCode, false);
+		}
+		ImGui::NewLine();
 	}
-	for (uint32 key : toClose)
-		_state->_openHandlers.erase(key);
+}
+
+void showScriptsWindow() {
+	ScriptData &data = _state->_openScripts;
+	if (!_state->_w.scripts || !data._showScript || data._scripts.empty())
+		return;
+
+	ImGui::SetNextWindowPos(ImVec2(20, 160), ImGuiCond_FirstUseEver);
+	ImGui::SetNextWindowSize(ImVec2(480, 540), ImGuiCond_FirstUseEver);
+
+	if (ImGui::Begin("Scripts", &_state->_w.scripts)) {
+		ScriptContext *ctx = getScriptContext(data._scripts[data._current].id);
+		if (ctx)
+			ImGui::Text("%s", ctx->asString().c_str());
+
+		if (renderScriptNavBar(data)) {
+			ImGui::Separator();
+			ImVec2 childSize = ImGui::GetContentRegionAvail();
+			ImGui::BeginChild("##script", childSize);
+			renderScriptContext(data, g_director->getCurrentMovie());
+			ImGui::EndChild();
+		}
+	}
+	ImGui::End();
+	_state->_dbg._goToDefinition = false;
 }
 
 static void updateCurrentScript() {
@@ -190,16 +251,6 @@ static void updateCurrentScript() {
 	setScriptToDisplay(script);
 }
 
-static Common::String getHandlerName(Symbol &sym) {
-	Common::String handlerName;
-
-	if (sym.ctx && sym.ctx->_id)
-		handlerName = Common::String::format("%d:", sym.ctx->_id);
-	handlerName += g_lingo->formatFunctionName(sym);
-
-	return handlerName;
-}
-
 void showFuncList() {
 	if (!_state->_w.funcList)
 		return;
@@ -456,91 +507,23 @@ void showExecutionContext() {
 		ScriptData *scriptData = &_state->_functions._windowScriptData.getOrCreateVal(stage);
 		updateCurrentScript();
 
-		if (scriptData->_showScript && !scriptData->_scripts.empty() && scriptData->_current < scriptData->_scripts.size()) {
-			// disable highlighting while rendering scripts in Execution Context
+		if (scriptData->_showScript && !scriptData->_scripts.empty()) {
 			bool oldSuppress = _state->_dbg._suppressHighlight;
 			_state->_dbg._suppressHighlight = true;
 
-			ImGuiScript &current = scriptData->_scripts[scriptData->_current];
-
-			// Get all the handlers from the script
-			ScriptContext* context = getScriptContext(current.id);
-
-			if (context) {
+			ScriptContext *context = getScriptContext(scriptData->_scripts[scriptData->_current].id);
+			if (context)
 				ImGui::Text("%d:%s type:%s", context->_id, context->getName().c_str(), scriptType2str(context->_scriptType));
-			}
 
-			ImGui::BeginDisabled(scriptData->_scripts.empty() || scriptData->_current == 0);
-			if (ImGui::Button(ICON_MS_ARROW_BACK)) {
-				scriptData->_current--;
-				_state->_dbg._goToDefinition = true;
-			}
-			ImGui::EndDisabled();
-			ImGui::SetItemTooltip("Backward");
-			ImGui::SameLine();
-
-			ImGui::BeginDisabled(scriptData->_current >= scriptData->_scripts.size() - 1);
-			if (ImGui::Button(ICON_MS_ARROW_FORWARD)) {
-				scriptData->_current++;
-				_state->_dbg._goToDefinition = true;
-			}
-			ImGui::EndDisabled();
-			ImGui::SetItemTooltip("Forward");
-			ImGui::SameLine();
-
-			const char *currentScript = nullptr;
-
-			if (scriptData->_current < scriptData->_scripts.size()) {
-				currentScript = scriptData->_scripts[scriptData->_current].handlerName.c_str();
-			}
-
-			if (ImGui::BeginCombo("##handlers", currentScript)) {
-				for (uint i = 0; i < scriptData->_scripts.size(); i++) {
-					auto &script = scriptData->_scripts[i];
-					bool selected = i == scriptData->_current;
-					ImGui::PushID(i);
-					if (ImGui::Selectable(script.handlerName.c_str(), &selected)) {
-						scriptData->_current = i;
-					}
-					ImGui::PopID();
-				}
-				ImGui::EndCombo();
-			}
-
-			if (!scriptData->_scripts[scriptData->_current].oldAst) {
-				ImGui::SameLine(0, 20);
-				ImGuiEx::toggleButton(ICON_MS_PACKAGE_2, &scriptData->_showByteCode, true); // Lingo
-				ImGui::SetItemTooltip("Lingo");
-				ImGui::SameLine();
-
-				ImGuiEx::toggleButton(ICON_MS_STACKS, &scriptData->_showByteCode); // Bytecode
-				ImGui::SetItemTooltip("Bytecode");
-			}
-
-			ImGui::Separator();
-			childSize = ImGui::GetContentRegionAvail();
-			ImGui::BeginChild("##script", childSize);
-
-			if (!context || context->_functionHandlers.size() == 1) {
-				renderScript(current, scriptData->_showByteCode, true);
-			} else {
-				for (auto &functionHandler : context->_functionHandlers) {
-					if (current.handlerId == functionHandler._key) {
-						renderScript(current, scriptData->_showByteCode, true);
-					} else {
-						ImGuiScript script = toImGuiScript(context->_scriptType, current.id, functionHandler._key);
-						script.byteOffsets = context->_functionByteOffsets[script.handlerId];
-						script.moviePath = movie->getArchive()->getPathName().toString();
-						script.handlerName = getHandlerName(functionHandler._value);
-						// Need to pass by reference in case of the current handler
-						renderScript(script, scriptData->_showByteCode, false);
-					}
-					ImGui::NewLine();
-				}
+			if (renderScriptNavBar(*scriptData)) {
+				ImGui::Separator();
+				childSize = ImGui::GetContentRegionAvail();
+				ImGui::BeginChild("##script", childSize);
+				renderScriptContext(*scriptData, movie);
+				ImGui::EndChild();
+				scriptsRendered = true;
 			}
-			scriptsRendered = true;
 
-			ImGui::EndChild();
 			_state->_dbg._suppressHighlight = oldSuppress;
 		}
 
@@ -576,90 +559,23 @@ void showExecutionContext() {
 			scriptData = &_state->_functions._windowScriptData.getOrCreateVal(window);
 			updateCurrentScript();
 
-			if (scriptData->_showScript && !scriptData->_scripts.empty() && scriptData->_current < scriptData->_scripts.size()) {
+			if (scriptData->_showScript && !scriptData->_scripts.empty()) {
 				bool oldSuppress = _state->_dbg._suppressHighlight;
 				_state->_dbg._suppressHighlight = true;
-				ImGuiScript &current = scriptData->_scripts[scriptData->_current];
-
-				// Get all the handlers from the script
-				ScriptContext* context = getScriptContext(current.id);
-
-				if (context) {
-					int castId = context->_id;
-					if (castId == -1) {
-						castId = movie->getCast()->getCastIdByScriptId(context->_parentNumber);
-					}
-					Common::String scriptInfo = Common::String::format("%d:%s type:%s", castId, context->getName().c_str(), scriptType2str(context->_scriptType));
-					ImGui::Text("%s", scriptInfo.c_str());
-				}
-
-				ImGui::BeginDisabled(scriptData->_scripts.empty() || scriptData->_current == 0);
-				if (ImGui::Button(ICON_MS_ARROW_BACK)) {
-					scriptData->_current--;
-				}
-				ImGui::EndDisabled();
-				ImGui::SetItemTooltip("Backward");
-				ImGui::SameLine();
 
-				ImGui::BeginDisabled(scriptData->_current >= scriptData->_scripts.size() - 1);
-				if (ImGui::Button(ICON_MS_ARROW_FORWARD)) {
-					scriptData->_current++;
-				}
-				ImGui::EndDisabled();
-				ImGui::SetItemTooltip("Forward");
-				ImGui::SameLine();
-
-				const char *currentScript = nullptr;
-
-				if (scriptData->_current < scriptData->_scripts.size()) {
-					currentScript = scriptData->_scripts[scriptData->_current].handlerName.c_str();
-				}
-
-				if (ImGui::BeginCombo("##handlers", currentScript)) {
-					for (uint i = 0; i < scriptData->_scripts.size(); i++) {
-						auto &script = scriptData->_scripts[i];
-						bool selected = i == scriptData->_current;
-						if (ImGui::Selectable(script.handlerName.c_str(), &selected)) {
-							scriptData->_current = i;
-						}
-					}
-					ImGui::EndCombo();
+				ScriptContext *context = getScriptContext(scriptData->_scripts[scriptData->_current].id);
+				if (context)
+					ImGui::Text("%d:%s type:%s", context->_id, context->getName().c_str(), scriptType2str(context->_scriptType));
+
+				if (renderScriptNavBar(*scriptData)) {
+					ImGui::Separator();
+					childSize = ImGui::GetContentRegionAvail();
+					ImGui::BeginChild("##script", childSize);
+					renderScriptContext(*scriptData, movie);
+					ImGui::EndChild();
+					scriptsRendered = true;
 				}
 
-				if (!scriptData->_scripts[scriptData->_current].oldAst) {
-					ImGui::SameLine(0, 20);
-					ImGuiEx::toggleButton(ICON_MS_PACKAGE_2, &scriptData->_showByteCode, true); // Lingo
-					ImGui::SetItemTooltip("Lingo");
-					ImGui::SameLine();
-
-					ImGuiEx::toggleButton(ICON_MS_STACKS, &scriptData->_showByteCode); // Bytecode
-					ImGui::SetItemTooltip("Bytecode");
-				}
-
-				ImGui::Separator();
-				childSize = ImGui::GetContentRegionAvail();
-				ImGui::BeginChild("##script", childSize);
-
-				if (!context || context->_functionHandlers.size() == 1) {
-					renderScript(current, scriptData->_showByteCode, true);
-				} else {
-					for (auto &functionHandler : context->_functionHandlers) {
-						if (current.handlerId == functionHandler._key) {
-							renderScript(current, scriptData->_showByteCode, true);
-						} else {
-							ImGuiScript script = toImGuiScript(context->_scriptType, current.id, functionHandler._key);
-							script.byteOffsets = context->_functionByteOffsets[script.handlerId];
-							script.moviePath = movie->getArchive()->getPathName().toString();
-							script.handlerName = getHandlerName(functionHandler._value);
-							// Need to pass by reference in case of the current handler
-							renderScript(script, scriptData->_showByteCode, false);
-						}
-						ImGui::NewLine();
-					}
-				}
-
-				scriptsRendered = true;
-				ImGui::EndChild();
 				_state->_dbg._suppressHighlight = oldSuppress;
 			}
 
@@ -670,6 +586,7 @@ void showExecutionContext() {
 
 		// Mark the scripts not dirty after all the handlers have been rendered
 		_state->_dbg._isScriptDirty = !scriptsRendered;
+		_state->_dbg._goToDefinition = false;
 
 		g_director->setCurrentWindow(currentWindow);
 		g_lingo->switchStateFromWindow();


Commit: 7494441d8603f640fcb4d8fce63d5c15f4ab8200
    https://github.com/scummvm/scummvm/commit/7494441d8603f640fcb4d8fce63d5c15f4ab8200
Author: ramyak-sharma (ramyaksharma1 at gmail.com)
Date: 2026-06-16T01:33:29+02:00

Commit Message:
DIRECTOR: DT: Add Ctrl+2/3/4 shortcuts for Control Panel, Cast, Score

Use ImGui::Shortcut() with RouteGlobal | RouteOverFocused inside
BeginMainMenuBar() to register global shortcuts for toggling the
Control Panel (Ctrl+2), Cast (Ctrl+3), and Score (Ctrl+4) windows.

Since MacOS overrides ctrl, use cmd instead.:wq

Changed paths:
    engines/director/debugger/debugtools.cpp


diff --git a/engines/director/debugger/debugtools.cpp b/engines/director/debugger/debugtools.cpp
index 3d4b3ef210b..f9c32539a1d 100644
--- a/engines/director/debugger/debugtools.cpp
+++ b/engines/director/debugger/debugtools.cpp
@@ -908,6 +908,12 @@ void onImGuiRender() {
 	ImGui::DockSpaceOverViewport(0, ImGui::GetMainViewport(), ImGuiDockNodeFlags_PassthruCentralNode);
 
 	if (ImGui::BeginMainMenuBar()) {
+		if (ImGui::Shortcut(ImGuiMod_Ctrl | ImGuiKey_2, ImGuiInputFlags_RouteGlobal | ImGuiInputFlags_RouteOverFocused))
+			_state->_w.controlPanel = !_state->_w.controlPanel;
+		if (ImGui::Shortcut(ImGuiMod_Ctrl | ImGuiKey_3, ImGuiInputFlags_RouteGlobal | ImGuiInputFlags_RouteOverFocused))
+			_state->_w.cast = !_state->_w.cast;
+		if (ImGui::Shortcut(ImGuiMod_Ctrl | ImGuiKey_4, ImGuiInputFlags_RouteGlobal | ImGuiInputFlags_RouteOverFocused))
+			_state->_w.score = !_state->_w.score;
 		if (ImGui::BeginMenu("View")) {
 			ImGui::SeparatorText("Windows");
 



From noreply at scummvm.org  Mon Jun 15 23:54:42 2026
From: noreply at scummvm.org (sev-)
Date: Mon, 15 Jun 2026 23:54:42 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm-web master ->
 5eee2670ae7a480715a2cf22b86301905d1c8c57
Message-ID: <20260615235442.41ED37005BC@ron.scummvm.net>

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

Summary:
5eee2670ae SCREENSHOTS: Added gamos engine screenshots


Commit: 5eee2670ae7a480715a2cf22b86301905d1c8c57
    https://github.com/scummvm/scummvm-web/commit/5eee2670ae7a480715a2cf22b86301905d1c8c57
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2026-06-16T01:54:24+02:00

Commit Message:
SCREENSHOTS: Added gamos engine screenshots

Changed paths:
  A public_html/data/screenshots/gamos/flop/flop_win_en_1_1.jpg
  A public_html/data/screenshots/gamos/flop/flop_win_en_1_1_full.png
  A public_html/data/screenshots/gamos/flop/flop_win_en_1_2.jpg
  A public_html/data/screenshots/gamos/flop/flop_win_en_1_2_full.png
  A public_html/data/screenshots/gamos/netwalk/netwalk_win_en_1_1.jpg
  A public_html/data/screenshots/gamos/netwalk/netwalk_win_en_1_1_full.png
  A public_html/data/screenshots/gamos/netwalk/netwalk_win_en_1_2.jpg
  A public_html/data/screenshots/gamos/netwalk/netwalk_win_en_1_2_full.png
  A public_html/data/screenshots/gamos/pilots1/pilots1_win_en_1_1.jpg
  A public_html/data/screenshots/gamos/pilots1/pilots1_win_en_1_1_full.png
  A public_html/data/screenshots/gamos/pilots1/pilots1_win_en_1_2.jpg
  A public_html/data/screenshots/gamos/pilots1/pilots1_win_en_1_2_full.png
  A public_html/data/screenshots/gamos/pilots2/pilots2_win_ru_1_1.jpg
  A public_html/data/screenshots/gamos/pilots2/pilots2_win_ru_1_1_full.png
  A public_html/data/screenshots/gamos/pilots2/pilots2_win_ru_1_2.jpg
  A public_html/data/screenshots/gamos/pilots2/pilots2_win_ru_1_2_full.png
  A public_html/data/screenshots/gamos/pilots2/pilots2_win_ru_1_3.jpg
  A public_html/data/screenshots/gamos/pilots2/pilots2_win_ru_1_3_full.png
  A public_html/data/screenshots/gamos/solgamer/solgamer_win_en_1_1.jpg
  A public_html/data/screenshots/gamos/solgamer/solgamer_win_en_1_1_full.png
  A public_html/data/screenshots/gamos/solgamer/solgamer_win_en_1_2.jpg
  A public_html/data/screenshots/gamos/solgamer/solgamer_win_en_1_2_full.png
  A public_html/data/screenshots/gamos/solgamer/solgamer_win_en_1_3.jpg
  A public_html/data/screenshots/gamos/solgamer/solgamer_win_en_1_3_full.png
  A public_html/data/screenshots/gamos/vitamin/vitamin_win_en_1_1.jpg
  A public_html/data/screenshots/gamos/vitamin/vitamin_win_en_1_1_full.png
  A public_html/data/screenshots/gamos/vitamin/vitamin_win_en_1_2.jpg
  A public_html/data/screenshots/gamos/vitamin/vitamin_win_en_1_2_full.png
  A public_html/data/screenshots/gamos/wild/wild_win_en_1_1.jpg
  A public_html/data/screenshots/gamos/wild/wild_win_en_1_1_full.png
  A public_html/data/screenshots/gamos/wild/wild_win_en_1_2.jpg
  A public_html/data/screenshots/gamos/wild/wild_win_en_1_2_full.png


diff --git a/public_html/data/screenshots/gamos/flop/flop_win_en_1_1.jpg b/public_html/data/screenshots/gamos/flop/flop_win_en_1_1.jpg
new file mode 100644
index 00000000..b58d599d
Binary files /dev/null and b/public_html/data/screenshots/gamos/flop/flop_win_en_1_1.jpg differ
diff --git a/public_html/data/screenshots/gamos/flop/flop_win_en_1_1_full.png b/public_html/data/screenshots/gamos/flop/flop_win_en_1_1_full.png
new file mode 100644
index 00000000..c918d432
Binary files /dev/null and b/public_html/data/screenshots/gamos/flop/flop_win_en_1_1_full.png differ
diff --git a/public_html/data/screenshots/gamos/flop/flop_win_en_1_2.jpg b/public_html/data/screenshots/gamos/flop/flop_win_en_1_2.jpg
new file mode 100644
index 00000000..a058bf84
Binary files /dev/null and b/public_html/data/screenshots/gamos/flop/flop_win_en_1_2.jpg differ
diff --git a/public_html/data/screenshots/gamos/flop/flop_win_en_1_2_full.png b/public_html/data/screenshots/gamos/flop/flop_win_en_1_2_full.png
new file mode 100644
index 00000000..ea886cdc
Binary files /dev/null and b/public_html/data/screenshots/gamos/flop/flop_win_en_1_2_full.png differ
diff --git a/public_html/data/screenshots/gamos/netwalk/netwalk_win_en_1_1.jpg b/public_html/data/screenshots/gamos/netwalk/netwalk_win_en_1_1.jpg
new file mode 100644
index 00000000..091fcce8
Binary files /dev/null and b/public_html/data/screenshots/gamos/netwalk/netwalk_win_en_1_1.jpg differ
diff --git a/public_html/data/screenshots/gamos/netwalk/netwalk_win_en_1_1_full.png b/public_html/data/screenshots/gamos/netwalk/netwalk_win_en_1_1_full.png
new file mode 100644
index 00000000..8368c876
Binary files /dev/null and b/public_html/data/screenshots/gamos/netwalk/netwalk_win_en_1_1_full.png differ
diff --git a/public_html/data/screenshots/gamos/netwalk/netwalk_win_en_1_2.jpg b/public_html/data/screenshots/gamos/netwalk/netwalk_win_en_1_2.jpg
new file mode 100644
index 00000000..4dd2324b
Binary files /dev/null and b/public_html/data/screenshots/gamos/netwalk/netwalk_win_en_1_2.jpg differ
diff --git a/public_html/data/screenshots/gamos/netwalk/netwalk_win_en_1_2_full.png b/public_html/data/screenshots/gamos/netwalk/netwalk_win_en_1_2_full.png
new file mode 100644
index 00000000..ba4a54b2
Binary files /dev/null and b/public_html/data/screenshots/gamos/netwalk/netwalk_win_en_1_2_full.png differ
diff --git a/public_html/data/screenshots/gamos/pilots1/pilots1_win_en_1_1.jpg b/public_html/data/screenshots/gamos/pilots1/pilots1_win_en_1_1.jpg
new file mode 100644
index 00000000..edbd199c
Binary files /dev/null and b/public_html/data/screenshots/gamos/pilots1/pilots1_win_en_1_1.jpg differ
diff --git a/public_html/data/screenshots/gamos/pilots1/pilots1_win_en_1_1_full.png b/public_html/data/screenshots/gamos/pilots1/pilots1_win_en_1_1_full.png
new file mode 100644
index 00000000..a34d4d5b
Binary files /dev/null and b/public_html/data/screenshots/gamos/pilots1/pilots1_win_en_1_1_full.png differ
diff --git a/public_html/data/screenshots/gamos/pilots1/pilots1_win_en_1_2.jpg b/public_html/data/screenshots/gamos/pilots1/pilots1_win_en_1_2.jpg
new file mode 100644
index 00000000..87a2eec2
Binary files /dev/null and b/public_html/data/screenshots/gamos/pilots1/pilots1_win_en_1_2.jpg differ
diff --git a/public_html/data/screenshots/gamos/pilots1/pilots1_win_en_1_2_full.png b/public_html/data/screenshots/gamos/pilots1/pilots1_win_en_1_2_full.png
new file mode 100644
index 00000000..f4754389
Binary files /dev/null and b/public_html/data/screenshots/gamos/pilots1/pilots1_win_en_1_2_full.png differ
diff --git a/public_html/data/screenshots/gamos/pilots2/pilots2_win_ru_1_1.jpg b/public_html/data/screenshots/gamos/pilots2/pilots2_win_ru_1_1.jpg
new file mode 100644
index 00000000..6541eb29
Binary files /dev/null and b/public_html/data/screenshots/gamos/pilots2/pilots2_win_ru_1_1.jpg differ
diff --git a/public_html/data/screenshots/gamos/pilots2/pilots2_win_ru_1_1_full.png b/public_html/data/screenshots/gamos/pilots2/pilots2_win_ru_1_1_full.png
new file mode 100644
index 00000000..79289bec
Binary files /dev/null and b/public_html/data/screenshots/gamos/pilots2/pilots2_win_ru_1_1_full.png differ
diff --git a/public_html/data/screenshots/gamos/pilots2/pilots2_win_ru_1_2.jpg b/public_html/data/screenshots/gamos/pilots2/pilots2_win_ru_1_2.jpg
new file mode 100644
index 00000000..0efd620f
Binary files /dev/null and b/public_html/data/screenshots/gamos/pilots2/pilots2_win_ru_1_2.jpg differ
diff --git a/public_html/data/screenshots/gamos/pilots2/pilots2_win_ru_1_2_full.png b/public_html/data/screenshots/gamos/pilots2/pilots2_win_ru_1_2_full.png
new file mode 100644
index 00000000..52911764
Binary files /dev/null and b/public_html/data/screenshots/gamos/pilots2/pilots2_win_ru_1_2_full.png differ
diff --git a/public_html/data/screenshots/gamos/pilots2/pilots2_win_ru_1_3.jpg b/public_html/data/screenshots/gamos/pilots2/pilots2_win_ru_1_3.jpg
new file mode 100644
index 00000000..9e47ab92
Binary files /dev/null and b/public_html/data/screenshots/gamos/pilots2/pilots2_win_ru_1_3.jpg differ
diff --git a/public_html/data/screenshots/gamos/pilots2/pilots2_win_ru_1_3_full.png b/public_html/data/screenshots/gamos/pilots2/pilots2_win_ru_1_3_full.png
new file mode 100644
index 00000000..ecc49fdc
Binary files /dev/null and b/public_html/data/screenshots/gamos/pilots2/pilots2_win_ru_1_3_full.png differ
diff --git a/public_html/data/screenshots/gamos/solgamer/solgamer_win_en_1_1.jpg b/public_html/data/screenshots/gamos/solgamer/solgamer_win_en_1_1.jpg
new file mode 100644
index 00000000..68ac373b
Binary files /dev/null and b/public_html/data/screenshots/gamos/solgamer/solgamer_win_en_1_1.jpg differ
diff --git a/public_html/data/screenshots/gamos/solgamer/solgamer_win_en_1_1_full.png b/public_html/data/screenshots/gamos/solgamer/solgamer_win_en_1_1_full.png
new file mode 100644
index 00000000..c0af3007
Binary files /dev/null and b/public_html/data/screenshots/gamos/solgamer/solgamer_win_en_1_1_full.png differ
diff --git a/public_html/data/screenshots/gamos/solgamer/solgamer_win_en_1_2.jpg b/public_html/data/screenshots/gamos/solgamer/solgamer_win_en_1_2.jpg
new file mode 100644
index 00000000..88fbef30
Binary files /dev/null and b/public_html/data/screenshots/gamos/solgamer/solgamer_win_en_1_2.jpg differ
diff --git a/public_html/data/screenshots/gamos/solgamer/solgamer_win_en_1_2_full.png b/public_html/data/screenshots/gamos/solgamer/solgamer_win_en_1_2_full.png
new file mode 100644
index 00000000..7d895de1
Binary files /dev/null and b/public_html/data/screenshots/gamos/solgamer/solgamer_win_en_1_2_full.png differ
diff --git a/public_html/data/screenshots/gamos/solgamer/solgamer_win_en_1_3.jpg b/public_html/data/screenshots/gamos/solgamer/solgamer_win_en_1_3.jpg
new file mode 100644
index 00000000..eeb8faec
Binary files /dev/null and b/public_html/data/screenshots/gamos/solgamer/solgamer_win_en_1_3.jpg differ
diff --git a/public_html/data/screenshots/gamos/solgamer/solgamer_win_en_1_3_full.png b/public_html/data/screenshots/gamos/solgamer/solgamer_win_en_1_3_full.png
new file mode 100644
index 00000000..b5e42c55
Binary files /dev/null and b/public_html/data/screenshots/gamos/solgamer/solgamer_win_en_1_3_full.png differ
diff --git a/public_html/data/screenshots/gamos/vitamin/vitamin_win_en_1_1.jpg b/public_html/data/screenshots/gamos/vitamin/vitamin_win_en_1_1.jpg
new file mode 100644
index 00000000..e8d17b38
Binary files /dev/null and b/public_html/data/screenshots/gamos/vitamin/vitamin_win_en_1_1.jpg differ
diff --git a/public_html/data/screenshots/gamos/vitamin/vitamin_win_en_1_1_full.png b/public_html/data/screenshots/gamos/vitamin/vitamin_win_en_1_1_full.png
new file mode 100644
index 00000000..a5207516
Binary files /dev/null and b/public_html/data/screenshots/gamos/vitamin/vitamin_win_en_1_1_full.png differ
diff --git a/public_html/data/screenshots/gamos/vitamin/vitamin_win_en_1_2.jpg b/public_html/data/screenshots/gamos/vitamin/vitamin_win_en_1_2.jpg
new file mode 100644
index 00000000..f3e4a8b7
Binary files /dev/null and b/public_html/data/screenshots/gamos/vitamin/vitamin_win_en_1_2.jpg differ
diff --git a/public_html/data/screenshots/gamos/vitamin/vitamin_win_en_1_2_full.png b/public_html/data/screenshots/gamos/vitamin/vitamin_win_en_1_2_full.png
new file mode 100644
index 00000000..cca85b89
Binary files /dev/null and b/public_html/data/screenshots/gamos/vitamin/vitamin_win_en_1_2_full.png differ
diff --git a/public_html/data/screenshots/gamos/wild/wild_win_en_1_1.jpg b/public_html/data/screenshots/gamos/wild/wild_win_en_1_1.jpg
new file mode 100644
index 00000000..89045f82
Binary files /dev/null and b/public_html/data/screenshots/gamos/wild/wild_win_en_1_1.jpg differ
diff --git a/public_html/data/screenshots/gamos/wild/wild_win_en_1_1_full.png b/public_html/data/screenshots/gamos/wild/wild_win_en_1_1_full.png
new file mode 100644
index 00000000..5fabe251
Binary files /dev/null and b/public_html/data/screenshots/gamos/wild/wild_win_en_1_1_full.png differ
diff --git a/public_html/data/screenshots/gamos/wild/wild_win_en_1_2.jpg b/public_html/data/screenshots/gamos/wild/wild_win_en_1_2.jpg
new file mode 100644
index 00000000..5fc5ed49
Binary files /dev/null and b/public_html/data/screenshots/gamos/wild/wild_win_en_1_2.jpg differ
diff --git a/public_html/data/screenshots/gamos/wild/wild_win_en_1_2_full.png b/public_html/data/screenshots/gamos/wild/wild_win_en_1_2_full.png
new file mode 100644
index 00000000..b5ffd192
Binary files /dev/null and b/public_html/data/screenshots/gamos/wild/wild_win_en_1_2_full.png differ



From noreply at scummvm.org  Tue Jun 16 08:31:14 2026
From: noreply at scummvm.org (dreammaster)
Date: Tue, 16 Jun 2026 08:31:14 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 15ea8b571e252f6f2bff288fcd7be24354ee5fa1
Message-ID: <20260616083114.535A47005CC@ron.scummvm.net>

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

Summary:
a6fca5ba75 MADS: FOREST: In progress room 503
7e8bd38783 MADS: FOREST: Finished room 503
0e6b2d62ec MADS: FOREST: Implemented rooms 509 and 510
15ea8b571e MADS: FOREST: Implemented room 520


Commit: a6fca5ba756392fcbd39a375addc4598142cc3d6
    https://github.com/scummvm/scummvm/commit/a6fca5ba756392fcbd39a375addc4598142cc3d6
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-06-16T16:03:03+10:00

Commit Message:
MADS: FOREST: In progress room 503

Changed paths:
    engines/mads/madsv2/forest/rooms/room503.cpp


diff --git a/engines/mads/madsv2/forest/rooms/room503.cpp b/engines/mads/madsv2/forest/rooms/room503.cpp
index 89187a9c0cf..7961800ade9 100644
--- a/engines/mads/madsv2/forest/rooms/room503.cpp
+++ b/engines/mads/madsv2/forest/rooms/room503.cpp
@@ -1,4 +1,4 @@
-???/* ScummVM - Graphic Adventure Engine
+/* 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
@@ -20,17 +20,23 @@
  */
 
 #include "mads/madsv2/core/conv.h"
+#include "mads/madsv2/core/digi.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/imath.h"
 #include "mads/madsv2/core/inter.h"
 #include "mads/madsv2/core/kernel.h"
+#include "mads/madsv2/core/matte.h"
+#include "mads/madsv2/core/midi.h"
+#include "mads/madsv2/core/mouse.h"
+#include "mads/madsv2/core/player.h"
 #include "mads/madsv2/core/sound.h"
 #include "mads/madsv2/core/text.h"
+#include "mads/madsv2/forest/journal.h"
 #include "mads/madsv2/forest/mads/inventory.h"
 #include "mads/madsv2/forest/mads/sounds.h"
 #include "mads/madsv2/forest/mads/words.h"
 #include "mads/madsv2/forest/global.h"
-#include "mads/madsv2/forest/rooms/section1.h"
+#include "mads/madsv2/forest/rooms/section5.h"
 #include "mads/madsv2/forest/rooms/room503.h"
 
 namespace MADS {
@@ -39,10 +45,24 @@ namespace Forest {
 namespace Rooms {
 
 struct Scratch {
-	int16 sprite[10];       /* Sprite series handles */
-	int16 sequence[10];     /* Sequence handles      */
-	int16 animation[10];     /* Animation handles     */
-	AnimationInfo animation_info[10];
+	int16 sprite[10];                  /* Sprite series handles */
+	int16 sequence[10];                /* Sequence handles      */
+	int16 animation[10];               /* Animation handles     */
+	AnimationInfo animation_info[13];  /* 13 entries: _val3/_val4 initialised for [0..11],
+	                                      _active/_frame for [1..12]; [10]._val3 = _90 flag,
+	                                      [11]._val3 = _98 flag */
+	int16 _pad_a4;                     /* 0xA4 unused */
+	int16 _a6;                         /* visit-count / playback-stage select */
+	int16 _pad_a8;                     /* 0xA8 unused */
+	int16 _pad_aa;                     /* 0xAA unused */
+	int16 _pad_ac;                     /* 0xAC unused */
+	int16 _ae;                         /* converse-anim handle (kernel_reset_animation / kernel_synch) */
+	int16 _pad_b0;                     /* 0xB0 unused */
+	int16 _b2;                         /* converse-anim handle (kernel_abort_animation / global_anim3) */
+	int16 _b4;                         /* ambient audio num arg / uninitialised flag */
+	int16 _b6;                         /* ambient audio playing flag */
+	int16 _b8;                         /* ambient loop active flag */
+	int16 _ba;                         /* playback variant (1 / 2 / -1) */
 };
 
 static Scratch scratch;
@@ -53,11 +73,865 @@ static Scratch scratch;
 #define aa    local->animation
 #define aainfo scratch.animation_info
 
+static void room_503_init1();
+static void room_503_init2();
+static void room_503_init3();
+
+static void room_503_anim1();
+static void room_503_anim2();
+static void room_503_anim3();
+static void room_503_anim12();
+static void room_503_anim4();
+static void room_503_anim5();
+static void room_503_anim6();
+static void room_503_anim7();
+static void room_503_anim8();
+static void room_503_anim9();
+static void room_503_anim10();
+static void room_503_anim11();
 
 static void room_503_init() {
+	scratch._b8 = -1;
+	scratch._b4 = -1;
+	scratch._ba = 1;
+	global[g009] = -1;
+
+	if (previous_room != KERNEL_RESTORING_GAME) {
+		player.walker_visible = 0;
+		player.commands_allowed = 0;
+
+		for (int count = 0; count < 12; count++) {
+			aainfo[count]._val3 = 0;
+			aainfo[count]._val4 = -1;
+			aainfo[count + 1]._active = 0;
+			aainfo[count + 1]._frame = 0;
+		}
+
+		if (previous_room != 199) {
+			if (flags[33] != 3)
+				flags[33]++;
+		}
+	}
+
+	switch (flags[33]) {
+	case -3:
+		flags[33] = 1;
+		room_503_init1();
+		break;
+	case 1:
+		room_503_init1();
+		break;
+	case 2:
+		flags[33] = 3;
+		room_503_init3();
+		break;
+	case 5:
+		room_503_init2();
+		break;
+	case 6:
+	case 7:
+		room_503_init3();
+		break;
+	default:
+		room_503_init3();
+		break;
+	}
+}
+
+static void room_503_init1() {
+	global[player_score] = 0;
+	player.walker_visible = 0;
+	player.commands_allowed = 0;
+
+	if (previous_room == 199) {
+		if (global[g100] != 0) {
+			global_digi_play(14);
+			viewing_at_y = 22;
+			scratch._ba = 2;
+			aainfo[0]._active = kernel_run_animation(kernel_name('N', 2), 106);
+			aainfo[10]._val3 = -1;
+			aa[8] = kernel_run_animation(kernel_name('F', 1), 0);
+			aainfo[8]._val3 = -1;
+			scratch._a6 = 104;
+			flags[33] = 4;
+		} else {
+			aa[9] = kernel_run_animation(kernel_name('N', 1), 106);
+			aainfo[9]._val3 = -1;
+			aainfo[0]._active = kernel_run_animation(kernel_name('N', 2), 106);
+			aainfo[10]._val3 = -1;
+
+			if (aainfo[9]._val3 == -1) {
+				kernel.trigger_setup_mode = 1;
+				digi_initial_volume(30);
+				digi_play_build(503, '_', 1, 2);
+				scratch._b8 = 1;
+			}
+
+			aainfo[0]._frame = kernel_run_animation(kernel_name('Z', 1), 0);
+			aainfo[11]._val3 = -1;
+		}
+	} else {
+		aa[9] = kernel_run_animation(kernel_name('N', 1), 106);
+		aainfo[9]._val3 = -1;
+		aainfo[0]._active = kernel_run_animation(kernel_name('N', 2), 106);
+		aainfo[10]._val3 = -1;
+
+		if (aainfo[9]._val3 == -1) {
+			kernel.trigger_setup_mode = 1;
+			digi_initial_volume(30);
+			digi_play_build(503, '_', 1, 2);
+			scratch._b8 = 1;
+		}
+
+		global_digi_play(6);
+		aa[0] = kernel_run_animation(kernel_name('y', 2), 100);
+		aainfo[0]._val3 = -1;
+		scratch._a6 = 52;
+	}
+}
+
+static void room_503_init2() {
+	viewing_at_y = 22;
+	global[player_score] = 0;
+	player.walker_visible = 0;
+	player.commands_allowed = 0;
+	mouse_hide();
+	scratch._ba = -1;
+	aa[6] = kernel_run_animation(kernel_name('F', 2), 0);
+	aainfo[6]._val3 = -1;
+}
+
+static void room_503_init3() {
+	global[player_score] = 0;
+	player.walker_visible = 0;
+	player.commands_allowed = 0;
+
+	if (previous_room == 199) {
+		if (global[g100] != 0) {
+			global_digi_play(14);
+			viewing_at_y = 22;
+			scratch._ba = 2;
+			aainfo[0]._active = kernel_run_animation(kernel_name('N', 2), 106);
+			aainfo[10]._val3 = -1;
+			aa[8] = kernel_run_animation(kernel_name('F', 1), 0);
+			aainfo[8]._val3 = -1;
+			scratch._a6 = 104;
+			flags[33] = 4;
+		} else {
+			aa[9] = kernel_run_animation(kernel_name('N', 1), 106);
+			aainfo[9]._val3 = -1;
+			aainfo[0]._active = kernel_run_animation(kernel_name('N', 2), 106);
+			aainfo[10]._val3 = -1;
+
+			if (aainfo[9]._val3 == -1) {
+				kernel.trigger_setup_mode = 1;
+				digi_initial_volume(30);
+				digi_play_build(503, '_', 1, 2);
+				scratch._b8 = 1;
+			}
+
+			aainfo[0]._frame = kernel_run_animation(kernel_name('Z', 1), 0);
+			aainfo[11]._val3 = -1;
+		}
+	} else {
+		aa[9] = kernel_run_animation(kernel_name('N', 1), 106);
+		aainfo[9]._val3 = -1;
+		aainfo[0]._active = kernel_run_animation(kernel_name('N', 2), 106);
+		aainfo[10]._val3 = -1;
+
+		if (aainfo[9]._val3 == -1) {
+			kernel.trigger_setup_mode = 1;
+			digi_initial_volume(30);
+			digi_play_build(503, '_', 1, 2);
+			scratch._b8 = 1;
+		}
+
+		aa[0] = kernel_run_animation(kernel_name('y', 1), 107);
+		aainfo[0]._val3 = -1;
+		scratch._a6 = 50;
+	}
+}
+
+static void room_503_anim1() {
+	if (kernel_anim[aa[0]].frame != aainfo[0]._val4)
+		aainfo[0]._val4 = kernel_anim[aa[0]].frame;
+
+	if (global[player_hyperwalked] == -1) {
+		aainfo[0]._val4 = scratch._a6 - 1;
+		kernel_reset_animation(aa[0], aainfo[0]._val4);
+	}
+}
+
+static void room_503_anim2() {
+	if (kernel_anim[aainfo[0]._frame].frame == aainfo[11]._val4)
+		return;
+	aainfo[11]._val4 = kernel_anim[aainfo[0]._frame].frame;
+	if (aainfo[11]._val4 == 69)
+		new_room = 501;
+}
+
+static void room_503_anim3() {
+	if (kernel_anim[aa[6]].frame != aainfo[6]._val4) {
+		aainfo[6]._val4 = kernel_anim[aa[6]].frame;
+		int frame = aainfo[6]._val4;
+		if (frame == 50) {
+			kernel_abort_animation(aa[6]);
+			aainfo[6]._val3 = 0;
+			aa[7] = kernel_run_animation(kernel_name('F', 3), 0);
+			aainfo[7]._val3 = -1;
+			kernel_synch(KERNEL_ANIM, aa[7], KERNEL_ANIM, aa[6]);
+			digi_initial_volume(50);
+			digi_play_build(503, '_', 2, 3);
+			digi_val2 = -1;
+			scratch._b6 = 1;
+		} else if (frame < 50) {
+			if (frame == 1) {
+				kernel_seq_delete(seq[0]);
+				kernel_seq_delete(seq[1]);
+				kernel_seq_delete(seq[2]);
+				kernel_seq_delete(seq[3]);
+			} else if (frame == 2) {
+				digi_initial_volume(50);
+				digi_play_build(503, '_', 2, 3);
+				digi_val2 = -1;
+				scratch._b6 = 1;
+			} else if (frame == 10) {
+				digi_play_build(503, 'r', 7, 1);
+				scratch._b4 = 10;
+			}
+		}
+	}
+	if (kernel.trigger == 7 || kernel.trigger == 28) {
+		if (scratch._b4 == 10)
+			scratch._b4 = -1;
+	}
+	if (kernel.trigger == 8) {
+		if (scratch._b6 == 1) {
+			digi_initial_volume(80);
+			scratch._b6++;
+			digi_play_build(503, '_', 2, 3);
+			digi_val2 = -1;
+		} else if (scratch._b6 == 2) {
+			digi_initial_volume(100);
+			scratch._b6 = 2;
+			digi_play_build(503, '_', 2, 3);
+			digi_val2 = -1;
+		}
+	}
+}
+
+static void room_503_anim4() {
+	if (kernel_anim[aa[7]].frame != aainfo[7]._val4) {
+		aainfo[7]._val4 = kernel_anim[aa[7]].frame;
+		int frame = aainfo[7]._val4;
+		if (frame == 59) {
+			new_room = 510;
+		} else if (frame < 59) {
+			if (frame == 1) {
+				digi_initial_volume(30);
+				digi_play_build(503, '_', 3, 3);
+				scratch._b6 = 1;
+				digi_val2 = -1;
+			} else if (frame == 30) {
+				digi_play_build(503, 'e', 5, 1);
+				scratch._b4 = 30;
+			}
+		}
+	}
+	if (kernel.trigger == 7 || kernel.trigger == 28) {
+		if (scratch._b4 == 30)
+			scratch._b4 = -1;
+	}
+	if (kernel.trigger == 8) {
+		if (scratch._b6 == 1) {
+			digi_initial_volume(30);
+			scratch._b6 = 1;
+			digi_play_build(503, '_', 3, 3);
+			digi_val2 = -1;
+		}
+	}
+}
+
+static void room_503_anim5() {
+	if (kernel_anim[aa[8]].frame != aainfo[8]._val4) {
+		aainfo[8]._val4 = kernel_anim[aa[8]].frame;
+		int frame = aainfo[8]._val4;
+		if (frame == 104) {
+			new_room = 509;
+		} else if (frame < 104) {
+			if (frame == 2) {
+				digi_initial_volume(50);
+				digi_play_build(503, '_', 2, 3);
+				digi_val2 = -1;
+				scratch._b6 = 1;
+			} else if (frame == 3) {
+				digi_play_build(503, 'r', 6, 1);
+				scratch._b4 = 3;
+			}
+		}
+	}
+	if (kernel.trigger == 7 || kernel.trigger == 28) {
+		if (scratch._b4 == 3)
+			scratch._b4 = -1;
+	}
+	if (kernel.trigger == 8) {
+		if (scratch._b6 == 1) {
+			digi_initial_volume(30);
+			scratch._b6 = 1;
+			digi_play_build(503, '_', 2, 3);
+			digi_val2 = -1;
+		}
+	}
+}
+static void room_503_anim6() {
+	if (kernel_anim[aa[1]].frame != aainfo[1]._val4) {
+		aainfo[1]._val4 = kernel_anim[aa[1]].frame;
+		switch (aainfo[1]._val4) {
+		case 5:
+			aainfo[2]._active = 11;
+			digi_play_build(503, 'e', 1, 1);
+			scratch._b4 = 5;
+			break;
+		case 10:
+			if (aainfo[2]._active == 11) {
+				aainfo[1]._val4 = 5;
+				kernel_reset_animation(aa[1], 5);
+			}
+			break;
+		case 14:
+			if (aainfo[2]._active == 13) {
+				aainfo[1]._val4 = 13;
+				kernel_reset_animation(aa[1], 13);
+			}
+			break;
+		case 15:
+			aainfo[2]._active = 15;
+			digi_play_build(503, 'g', 1, 1);
+			scratch._b4 = 15;
+			break;
+		case 20:
+			if (aainfo[2]._active == 15) {
+				aainfo[1]._val4 = 15;
+				kernel_reset_animation(aa[1], 15);
+			}
+			break;
+		case 24:
+			if (aainfo[2]._active == 13) {
+				aainfo[1]._val4 = 23;
+				kernel_reset_animation(aa[1], 23);
+			}
+			break;
+		case 25:
+			aainfo[2]._active = 12;
+			digi_play_build(503, 'r', 1, 1);
+			scratch._b4 = 25;
+			break;
+		case 30:
+			if (aainfo[2]._active == 12) {
+				aainfo[1]._val4 = 25;
+				kernel_reset_animation(aa[1], 25);
+			}
+			break;
+		case 34:
+			if (aainfo[2]._active == 13) {
+				aainfo[1]._val4 = 33;
+				kernel_reset_animation(aa[1], 33);
+			}
+			break;
+		case 35:
+			aainfo[2]._active = 15;
+			digi_play_build(503, 'g', 2, 1);
+			scratch._b4 = 35;
+			break;
+		case 40:
+			if (aainfo[2]._active == 15) {
+				aainfo[1]._val4 = 35;
+				kernel_reset_animation(aa[1], 35);
+			}
+			break;
+		case 44:
+			if (aainfo[2]._active == 13) {
+				aainfo[1]._val4 = 43;
+				kernel_reset_animation(aa[1], 43);
+			}
+			break;
+		case 45:
+			aainfo[2]._active = 12;
+			digi_play_build(503, 'r', 2, 1);
+			scratch._b4 = 45;
+			break;
+		case 50:
+			if (aainfo[2]._active == 12) {
+				aainfo[1]._val4 = 45;
+				kernel_reset_animation(aa[1], 45);
+			}
+			break;
+		case 54:
+			if (aainfo[2]._active == 13) {
+				aainfo[1]._val4 = 53;
+				kernel_reset_animation(aa[1], 53);
+			}
+			break;
+		case 55:
+			aainfo[2]._active = 15;
+			digi_play_build(503, 'g', 3, 1);
+			scratch._b4 = 55;
+			break;
+		case 60:
+			if (aainfo[2]._active == 15) {
+				aainfo[1]._val4 = 55;
+				kernel_reset_animation(aa[1], 55);
+			}
+			break;
+		case 64:
+			if (aainfo[2]._active == 13) {
+				aainfo[1]._val4 = 63;
+				kernel_reset_animation(aa[1], 63);
+			}
+			break;
+		case 65:
+			aainfo[2]._active = 12;
+			digi_play_build(503, 'r', 3, 1);
+			scratch._b4 = 65;
+			break;
+		case 70:
+			if (aainfo[2]._active == 12) {
+				aainfo[1]._val4 = 65;
+				kernel_reset_animation(aa[1], 65);
+			}
+			break;
+		case 74:
+			if (aainfo[2]._active == 13) {
+				aainfo[1]._val4 = 73;
+				kernel_reset_animation(aa[1], 73);
+			}
+			break;
+		case 75:
+			aainfo[2]._active = 11;
+			digi_play_build(503, 'e', 2, 1);
+			scratch._b4 = 75;
+			break;
+		case 80:
+			if (aainfo[2]._active == 11) {
+				aainfo[1]._val4 = 75;
+				kernel_reset_animation(aa[1], 75);
+			}
+			break;
+		case 84:
+			if (aainfo[2]._active == 13) {
+				aainfo[1]._val4 = 83;
+				kernel_reset_animation(aa[1], 83);
+			}
+			break;
+		case 85:
+			aainfo[2]._active = 12;
+			digi_play_build(503, 'r', 4, 1);
+			scratch._b4 = 85;
+			break;
+		case 90:
+			if (aainfo[2]._active == 12) {
+				aainfo[1]._val4 = 85;
+				kernel_reset_animation(aa[1], 85);
+			}
+			break;
+		case 94:
+			if (aainfo[2]._active == 13) {
+				aainfo[1]._val4 = 93;
+				kernel_reset_animation(aa[1], 93);
+			}
+			break;
+		case 95:
+			dont_frag_the_palette();
+			kernel_abort_animation(aa[1]);
+			aainfo[1]._val3 = 0;
+			aa[2] = kernel_run_animation("*RM503T21", 0);
+			aainfo[2]._val3 = -1;
+			kernel_synch(KERNEL_ANIM, aa[2], KERNEL_ANIM, aa[1]);
+			digi_initial_volume(20);
+			scratch._b4 = 1;
+			digi_play_build(503, '_', 1, 3);
+			digi_val2 = -1;
+			break;
+		}
+	}
+
+	if (kernel.trigger == 7 || kernel.trigger == 28) {
+		switch (scratch._b4) {
+		case 5:
+			kernel_timing_trigger(30, 28);
+			aainfo[2]._active = 13;
+			scratch._b4 = 6;
+			break;
+		case 6:
+			aainfo[2]._active = 14;
+			aainfo[1]._val4 = 10;
+			kernel_reset_animation(aa[1], 10);
+			scratch._b4 = -1;
+			break;
+		case 15:
+			kernel_timing_trigger(30, 28);
+			aainfo[2]._active = 13;
+			scratch._b4 = 16;
+			break;
+		case 16:
+			aainfo[2]._active = 14;
+			aainfo[1]._val4 = 20;
+			kernel_reset_animation(aa[1], 20);
+			scratch._b4 = -1;
+			break;
+		case 25:
+			kernel_timing_trigger(30, 28);
+			aainfo[2]._active = 13;
+			scratch._b4 = 26;
+			break;
+		case 26:
+			aainfo[2]._active = 14;
+			aainfo[1]._val4 = 30;
+			kernel_reset_animation(aa[1], 30);
+			scratch._b4 = -1;
+			break;
+		case 35:
+			kernel_timing_trigger(30, 28);
+			aainfo[2]._active = 13;
+			scratch._b4 = 36;
+			break;
+		case 36:
+			aainfo[2]._active = 14;
+			aainfo[1]._val4 = 41;
+			kernel_reset_animation(aa[1], 41);
+			scratch._b4 = -1;
+			break;
+		case 45:
+			kernel_timing_trigger(30, 28);
+			aainfo[2]._active = 13;
+			scratch._b4 = 46;
+			break;
+		case 46:
+			aainfo[2]._active = 14;
+			aainfo[1]._val4 = 50;
+			kernel_reset_animation(aa[1], 50);
+			scratch._b4 = -1;
+			break;
+		case 55:
+			kernel_timing_trigger(30, 28);
+			aainfo[2]._active = 13;
+			scratch._b4 = 56;
+			break;
+		case 56:
+			aainfo[2]._active = 14;
+			aainfo[1]._val4 = 60;
+			kernel_reset_animation(aa[1], 60);
+			scratch._b4 = -1;
+			break;
+		case 65:
+			kernel_timing_trigger(30, 28);
+			aainfo[2]._active = 13;
+			scratch._b4 = 66;
+			break;
+		case 66:
+			aainfo[2]._active = 14;
+			aainfo[1]._val4 = 70;
+			kernel_reset_animation(aa[1], 70);
+			scratch._b4 = -1;
+			break;
+		case 75:
+			kernel_timing_trigger(30, 28);
+			aainfo[2]._active = 13;
+			scratch._b4 = 76;
+			break;
+		case 76:
+			aainfo[2]._active = 14;
+			aainfo[1]._val4 = 80;
+			kernel_reset_animation(aa[1], 80);
+			scratch._b4 = -1;
+			break;
+		case 85:
+			kernel_timing_trigger(30, 28);
+			aainfo[2]._active = 13;
+			scratch._b4 = 86;
+			break;
+		case 86:
+			aainfo[2]._active = 14;
+			aainfo[1]._val4 = 91;
+			kernel_reset_animation(aa[1], 91);
+			scratch._b4 = -1;
+			break;
+		}
+	}
+	if (kernel.trigger == 8) {
+		if (scratch._b4 == 1) {
+			digi_initial_volume(20);
+			scratch._b4 = 1;
+			digi_play_build(503, '_', 1, 3);
+			digi_val2 = -1;
+		}
+	}
+}
+static void room_503_anim7() {
+	if (kernel_anim[aa[2]].frame == aainfo[2]._val4)
+		return;
+	aainfo[2]._val4 = kernel_anim[aa[2]].frame;
+	if (aainfo[2]._val4 == 31) {
+		dont_frag_the_palette();
+		kernel_abort_animation(aa[2]);
+		aainfo[2]._val3 = 0;
+		aa[3] = kernel_run_animation("*RM503T22", 0);
+		aainfo[3]._val3 = -1;
+		kernel_synch(KERNEL_ANIM, aa[3], KERNEL_ANIM, aa[2]);
+		digi_initial_volume(20);
+		scratch._b4 = 1;
+		digi_play_build(503, '_', 1, 3);
+		digi_val2 = -1;
+	}
+}
+
+static void room_503_anim8() {
+	if (kernel_anim[aa[3]].frame == aainfo[3]._val4)
+		goto check_trigger;
+	aainfo[3]._val4 = kernel_anim[aa[3]].frame;
+
+	if (aainfo[3]._val4 == 5) {
+		aainfo[4]._active = 15;
+		digi_play_build(503, 'g', 4, 1);
+		scratch._b4 = 5;
+	} else if (aainfo[3]._val4 == 10) {
+		if (aainfo[4]._active == 15) {
+			aainfo[3]._val4 = 5;
+			kernel_reset_animation(aa[3], 5);
+		}
+	} else if (aainfo[3]._val4 == 14) {
+		if (aainfo[4]._active == 13) {
+			aainfo[3]._val4 = 13;
+			kernel_reset_animation(aa[3], 13);
+		}
+	} else if (aainfo[3]._val4 == 42) {
+		dont_frag_the_palette();
+		kernel_abort_animation(aa[3]);
+		aainfo[3]._val3 = 0;
+		aa[4] = kernel_run_animation(kernel_name('T', 3), 0);
+		aainfo[4]._val3 = -1;
+		kernel_synch(KERNEL_ANIM, aa[3], KERNEL_ANIM, aa[4]);
+		digi_initial_volume(20);
+		scratch._b4 = 1;
+		digi_play_build(503, '_', 1, 3);
+		digi_val2 = -1;
+	}
+
+check_trigger:
+	if (kernel.trigger == 7 || kernel.trigger == 28) {
+		if (scratch._b4 == 5) {
+			kernel_timing_trigger(30, 28);
+			aainfo[4]._active = 13;
+			scratch._b4 = 6;
+		} else if (scratch._b4 == 6) {
+			aainfo[4]._active = 14;
+			aainfo[3]._val4 = 10;
+			kernel_reset_animation(aa[3], 10);
+			scratch._b4 = -1;
+		}
+	}
+}
+static void room_503_anim9()  { }
+
+static void room_503_anim10() {
+	if (kernel_anim[aa[5]].frame != aainfo[5]._val4) {
+		aainfo[5]._val4 = kernel_anim[aa[5]].frame;
+		int frame = aainfo[5]._val4;
+		if (frame == 40) {
+			midi_stop();
+			global[g100] = -1;
+			display_journal();
+		} else if (frame < 40) {
+			if (frame == 5) {
+				aainfo[6]._active = 16;
+				digi_play_build(503, 'i', 1, 1);
+				scratch._b4 = 5;
+			} else if (frame == 10) {
+				if (aainfo[6]._active == 16) {
+					aainfo[5]._val4 = 5;
+					kernel_reset_animation(aa[5], 5);
+				}
+			} else if (frame == 14) {
+				if (aainfo[6]._active == 13) {
+					aainfo[5]._val4 = 13;
+					kernel_reset_animation(aa[5], 13);
+				}
+			} else if (frame == 31) {
+				aainfo[6]._active = 16;
+				digi_play_build(503, 'b', 3, 1);
+				scratch._b4 = 31;
+			} else if (frame == 36) {
+				if (aainfo[6]._active == 16) {
+					aainfo[5]._val4 = 31;
+					kernel_reset_animation(aa[5], 31);
+				}
+			} else if (frame == 39) {
+				if (aainfo[6]._active == 13) {
+					aainfo[5]._val4 = 38;
+					kernel_reset_animation(aa[5], 38);
+				}
+			}
+		}
+	}
+	if (kernel.trigger == 7 || kernel.trigger == 28) {
+		if (scratch._b4 == 5) {
+			kernel_timing_trigger(30, 28);
+			aainfo[6]._active = 13;
+			scratch._b4 = 6;
+		} else if (scratch._b4 == 6) {
+			aainfo[6]._active = 14;
+			aainfo[5]._val4 = 9;
+			kernel_reset_animation(aa[5], 9);
+			scratch._b4 = -1;
+		} else if (scratch._b4 == 31) {
+			kernel_timing_trigger(30, 28);
+			aainfo[6]._active = 13;
+			scratch._b4 = 32;
+		} else if (scratch._b4 == 32) {
+			aainfo[6]._active = 14;
+			aainfo[5]._val4 = 36;
+			kernel_reset_animation(aa[5], 36);
+			scratch._b4 = -1;
+		}
+	}
+}
+
+static void room_503_anim11() {
+	if (kernel_anim[aainfo[0]._active].frame == aainfo[10]._val4)
+		return;
+	aainfo[10]._val4 = kernel_anim[aainfo[0]._active].frame;
+
+	int frame = aainfo[10]._val4;
+	if (frame == 49) {
+		aainfo[10]._val4 = 1;
+		kernel_reset_animation(aainfo[0]._active, 1);
+		return;
+	}
+	if (frame > 49)
+		return;
+
+	if (frame == 19 || frame == 30 || frame == 40) {
+		if (imath_random(0, 100) > 20) {
+			aainfo[10]._val4--;
+			kernel_reset_animation(aainfo[0]._active, aainfo[10]._val4);
+		}
+	} else if (frame == 34 || frame == 46) {
+		if (imath_random(0, 100) > 40) {
+			aainfo[10]._val4--;
+			kernel_reset_animation(aainfo[0]._active, aainfo[10]._val4);
+		}
+	} else if (frame == 20) {
+		if (imath_random(0, 100) > 60) {
+			aainfo[10]._val4 -= 2;
+			kernel_reset_animation(aainfo[0]._active, aainfo[10]._val4);
+		}
+	}
+}
+
+static void room_503_anim12() {
+	if (kernel_anim[aa[9]].frame == aainfo[9]._val4)
+		return;
+	aainfo[9]._val4 = kernel_anim[aa[9]].frame;
+	if (aainfo[9]._val4 == 49) {
+		aainfo[9]._val4 = 1;
+		kernel_reset_animation(aa[9], 1);
+	}
 }
 
 static void room_503_daemon() {
+	if (global[g101] != 0 && global[player_hyperwalked] == -1) {
+		game_save_name(0);
+		kernel_save_game(save_game_buf);
+		new_room = 904;
+	}
+
+	switch (kernel.trigger) {
+	case 8:
+		if (scratch._b8 == 1) {
+			digi_initial_volume(30);
+			digi_play_build(503, '_', 1, 3);
+			digi_val2 = -1;
+			scratch._b8 = 1;
+		}
+		break;
+
+	case 27:
+		global[walker_converse_now] = 0;
+		kernel_abort_animation(scratch._b2);
+		kernel_reset_animation(scratch._ae, 1);
+		kernel_synch(KERNEL_ANIM, scratch._ae, KERNEL_NOW, 0);
+		global[g133] = 0;
+		player.commands_allowed = -1;
+		break;
+
+	case 100:
+		kernel_abort_animation(aa[0]);
+		aainfo[0]._val3 = 0;
+		aa[1] = kernel_run_animation(kernel_name('t', 1), 1);
+		kernel_synch(KERNEL_ANIM, aa[1], KERNEL_NOW, 0);
+		aainfo[1]._val3 = -1;
+		player.commands_allowed = 0;
+		digi_initial_volume(20);
+		scratch._b4 = 1;
+		digi_play_build(503, '_', 1, 3);
+		digi_val2 = -1;
+		break;
+
+	case 104:
+		if (flags[33] == 6) {
+			aa[8] = kernel_run_animation(kernel_name('F', 1), 0);
+			aainfo[8]._val3 = -1;
+			scratch._a6 = 104;
+			digi_initial_volume(50);
+			digi_play_build(503, '_', 2, 3);
+			digi_val2 = -1;
+			scratch._b6 = 1;
+		}
+		break;
+
+	case 105:
+		if (flags[33] == 7) {
+			aa[6] = kernel_run_animation(kernel_name('F', 2), 0);
+			aainfo[6]._val3 = -1;
+			digi_initial_volume(50);
+			digi_play_build(503, '_', 2, 3);
+			digi_val2 = -1;
+			scratch._b6 = 1;
+		}
+		break;
+
+	case 107:
+		kernel_abort_animation(aa[0]);
+		aainfo[0]._val3 = 0;
+		midi_stop();
+		global[g100] = -1;
+		display_journal();
+		break;
+
+	default:
+		break;
+	}
+
+	if (aainfo[0]._val3 != 0)  room_503_anim1();
+	if (aainfo[11]._val3 != 0) room_503_anim2();
+	if (aainfo[6]._val3 != 0)  room_503_anim3();
+	if (aainfo[7]._val3 != 0)  room_503_anim4();
+	if (aainfo[8]._val3 != 0)  room_503_anim5();
+	if (aainfo[1]._val3 != 0)  room_503_anim6();
+	if (aainfo[2]._val3 != 0)  room_503_anim7();
+	if (aainfo[3]._val3 != 0)  room_503_anim8();
+	if (aainfo[4]._val3 != 0)  room_503_anim9();
+	if (aainfo[5]._val3 != 0)  room_503_anim10();
+
+	if (scratch._ba == 1) {
+		room_503_anim12();
+		room_503_anim11();
+	} else if (scratch._ba == 2) {
+		room_503_anim11();
+	}
+
+	if (global[walker_converse_now] != 0)
+		global_anim3(scratch._b2, &global[g008]);
 }
 
 static void room_503_pre_parser() {
@@ -70,13 +944,26 @@ void room_503_synchronize(Common::Serializer &s) {
 	for (int16 &v : scratch.sprite)    s.syncAsSint16LE(v);
 	for (int16 &v : scratch.sequence)  s.syncAsSint16LE(v);
 	for (int16 &v : scratch.animation) s.syncAsSint16LE(v);
+	for (AnimationInfo &ai : scratch.animation_info) ai.synchronize(s);
+	s.syncAsSint16LE(scratch._pad_a4);
+	s.syncAsSint16LE(scratch._a6);
+	s.syncAsSint16LE(scratch._pad_a8);
+	s.syncAsSint16LE(scratch._pad_aa);
+	s.syncAsSint16LE(scratch._pad_ac);
+	s.syncAsSint16LE(scratch._ae);
+	s.syncAsSint16LE(scratch._pad_b0);
+	s.syncAsSint16LE(scratch._b2);
+	s.syncAsSint16LE(scratch._b4);
+	s.syncAsSint16LE(scratch._b6);
+	s.syncAsSint16LE(scratch._b8);
+	s.syncAsSint16LE(scratch._ba);
 }
 
 void room_503_preload() {
-	room_init_code_pointer = room_503_init;
+	room_init_code_pointer       = room_503_init;
 	room_pre_parser_code_pointer = room_503_pre_parser;
-	room_parser_code_pointer = room_503_parser;
-	room_daemon_code_pointer = room_503_daemon;
+	room_parser_code_pointer     = room_503_parser;
+	room_daemon_code_pointer     = room_503_daemon;
 
 	global_section_walker();
 	global_section_interface();


Commit: 7e8bd38783978e8d9ac1ffb5ff52566ebb4de2d7
    https://github.com/scummvm/scummvm/commit/7e8bd38783978e8d9ac1ffb5ff52566ebb4de2d7
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-06-16T16:55:36+10:00

Commit Message:
MADS: FOREST: Finished room 503

Changed paths:
    engines/mads/madsv2/forest/rooms/room503.cpp


diff --git a/engines/mads/madsv2/forest/rooms/room503.cpp b/engines/mads/madsv2/forest/rooms/room503.cpp
index 7961800ade9..e13cf1f3c89 100644
--- a/engines/mads/madsv2/forest/rooms/room503.cpp
+++ b/engines/mads/madsv2/forest/rooms/room503.cpp
@@ -731,7 +731,225 @@ check_trigger:
 		}
 	}
 }
-static void room_503_anim9()  { }
+static void room_503_anim9() {
+	if (kernel_anim[aa[4]].frame != aainfo[4]._val4) {
+		aainfo[4]._val4 = kernel_anim[aa[4]].frame;
+		switch (aainfo[4]._val4) {
+		case 5:
+			aainfo[5]._active = 11;
+			digi_play_build(503, 'e', 3, 1);
+			scratch._b4 = 5;
+			break;
+		case 9:
+			if (aainfo[5]._active == 11) {
+				aainfo[4]._val4 = 5;
+				kernel_reset_animation(aa[4], 5);
+			}
+			break;
+		case 18:
+			if (aainfo[5]._active == 13) {
+				aainfo[4]._val4 = 17;
+				kernel_reset_animation(aa[4], 17);
+			}
+			break;
+		case 19:
+			aainfo[5]._active = 15;
+			digi_play_build(503, 'g', 5, 1);
+			scratch._b4 = 19;
+			break;
+		case 22:
+			if (aainfo[5]._active == 15) {
+				aainfo[4]._val4 = 19;
+				kernel_reset_animation(aa[4], 19);
+			}
+			break;
+		case 25:
+			if (aainfo[5]._active == 13) {
+				aainfo[4]._val4 = 24;
+				kernel_reset_animation(aa[4], 24);
+			}
+			break;
+		case 26:
+			aainfo[5]._active = 11;
+			digi_play_build(503, 'e', 4, 1);
+			scratch._b4 = 26;
+			break;
+		case 30:
+			if (aainfo[5]._active == 11) {
+				aainfo[4]._val4 = 26;
+				kernel_reset_animation(aa[4], 26);
+			}
+			break;
+		case 36:
+			if (aainfo[5]._active == 13) {
+				aainfo[4]._val4 = 35;
+				kernel_reset_animation(aa[4], 35);
+			}
+			break;
+		case 37:
+			aainfo[5]._active = 16;
+			digi_play_build(503, 'b', 1, 1);
+			scratch._b4 = 37;
+			break;
+		case 45:
+			if (aainfo[5]._active == 16) {
+				aainfo[4]._val4 = 37;
+				kernel_reset_animation(aa[4], 37);
+			}
+			break;
+		case 50:
+			if (aainfo[5]._active == 13) {
+				aainfo[4]._val4 = 49;
+				kernel_reset_animation(aa[4], 49);
+			}
+			break;
+		case 51:
+			aainfo[5]._active = 12;
+			digi_play_build(503, 'r', 5, 1);
+			scratch._b4 = 51;
+			break;
+		case 55:
+			if (aainfo[5]._active == 12) {
+				aainfo[4]._val4 = 51;
+				kernel_reset_animation(aa[4], 51);
+			}
+			break;
+		case 60:
+			if (aainfo[5]._active == 13) {
+				aainfo[4]._val4 = 59;
+				kernel_reset_animation(aa[4], 59);
+			}
+			break;
+		case 61:
+			aainfo[5]._active = 15;
+			digi_play_build(503, 'g', 6, 1);
+			scratch._b4 = 61;
+			break;
+		case 66:
+			if (aainfo[5]._active == 15) {
+				aainfo[4]._val4 = 61;
+				kernel_reset_animation(aa[4], 61);
+			}
+			break;
+		case 69:
+			if (aainfo[5]._active == 13) {
+				aainfo[4]._val4 = 68;
+				kernel_reset_animation(aa[4], 68);
+			}
+			break;
+		case 70:
+			aainfo[5]._active = 10;
+			digi_play_build(503, 'b', 2, 1);
+			scratch._b4 = 70;
+			break;
+		case 73:
+			if (aainfo[5]._active == 10) {
+				aainfo[4]._val4 = 70;
+				kernel_reset_animation(aa[4], 70);
+			}
+			break;
+		case 74:
+			if (aainfo[5]._active == 13) {
+				aainfo[4]._val4 = 73;
+				kernel_reset_animation(aa[4], 73);
+			} else {
+				dont_frag_the_palette();
+				kernel_abort_animation(aa[4]);
+				aainfo[4]._val3 = 0;
+				aa[5] = kernel_run_animation(kernel_name('T', 4), 0);
+				aainfo[5]._val3 = -1;
+				kernel_synch(KERNEL_ANIM, aa[5], KERNEL_ANIM, aa[4]);
+				digi_initial_volume(20);
+				scratch._b4 = 1;
+				digi_play_build(503, '_', 1, 3);
+				digi_val2 = -1;
+			}
+			break;
+		}
+	}
+
+	if (kernel.trigger == 7 || kernel.trigger == 28) {
+		switch (scratch._b4) {
+		case 5:
+			kernel_timing_trigger(30, 28);
+			aainfo[5]._active = 13;
+			scratch._b4 = 6;
+			break;
+		case 6:
+			aainfo[5]._active = 14;
+			aainfo[4]._val4 = 9;
+			kernel_reset_animation(aa[4], 9);
+			scratch._b4 = -1;
+			break;
+		case 19:
+			kernel_timing_trigger(30, 28);
+			aainfo[5]._active = 13;
+			scratch._b4 = 20;
+			break;
+		case 20:
+			aainfo[5]._active = 14;
+			aainfo[4]._val4 = 22;
+			kernel_reset_animation(aa[4], 22);
+			scratch._b4 = -1;
+			break;
+		case 26:
+			kernel_timing_trigger(30, 28);
+			aainfo[5]._active = 13;
+			scratch._b4 = 27;
+			break;
+		case 27:
+			aainfo[5]._active = 14;
+			aainfo[4]._val4 = 30;
+			kernel_reset_animation(aa[4], 30);
+			scratch._b4 = -1;
+			break;
+		case 37:
+			kernel_timing_trigger(30, 28);
+			aainfo[5]._active = 13;
+			scratch._b4 = 38;
+			break;
+		case 38:
+			aainfo[5]._active = 14;
+			aainfo[4]._val4 = 45;
+			kernel_reset_animation(aa[4], 45);
+			scratch._b4 = -1;
+			break;
+		case 51:
+			kernel_timing_trigger(30, 28);
+			aainfo[5]._active = 13;
+			scratch._b4 = 52;
+			break;
+		case 52:
+			aainfo[5]._active = 14;
+			aainfo[4]._val4 = 55;
+			kernel_reset_animation(aa[4], 55);
+			scratch._b4 = -1;
+			break;
+		case 61:
+			kernel_timing_trigger(30, 28);
+			aainfo[5]._active = 13;
+			scratch._b4 = 62;
+			break;
+		case 62:
+			aainfo[5]._active = 14;
+			aainfo[4]._val4 = 65;
+			kernel_reset_animation(aa[4], 65);
+			scratch._b4 = -1;
+			break;
+		case 70:
+			kernel_timing_trigger(30, 28);
+			aainfo[5]._active = 13;
+			scratch._b4 = 71;
+			break;
+		case 71:
+			aainfo[5]._active = 14;
+			aainfo[4]._val4 = 70;
+			kernel_reset_animation(aa[4], 70);
+			scratch._b4 = -1;
+			break;
+		}
+	}
+}
 
 static void room_503_anim10() {
 	if (kernel_anim[aa[5]].frame != aainfo[5]._val4) {
@@ -934,12 +1152,6 @@ static void room_503_daemon() {
 		global_anim3(scratch._b2, &global[g008]);
 }
 
-static void room_503_pre_parser() {
-}
-
-static void room_503_parser() {
-}
-
 void room_503_synchronize(Common::Serializer &s) {
 	for (int16 &v : scratch.sprite)    s.syncAsSint16LE(v);
 	for (int16 &v : scratch.sequence)  s.syncAsSint16LE(v);
@@ -961,8 +1173,6 @@ void room_503_synchronize(Common::Serializer &s) {
 
 void room_503_preload() {
 	room_init_code_pointer       = room_503_init;
-	room_pre_parser_code_pointer = room_503_pre_parser;
-	room_parser_code_pointer     = room_503_parser;
 	room_daemon_code_pointer     = room_503_daemon;
 
 	global_section_walker();


Commit: 0e6b2d62ecbab3449972166b3c81677d559e0d19
    https://github.com/scummvm/scummvm/commit/0e6b2d62ecbab3449972166b3c81677d559e0d19
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-06-16T18:23:46+10:00

Commit Message:
MADS: FOREST: Implemented rooms 509 and 510

Changed paths:
    engines/mads/madsv2/core/game.h
    engines/mads/madsv2/forest/rooms/room509.cpp
    engines/mads/madsv2/forest/rooms/room510.cpp


diff --git a/engines/mads/madsv2/core/game.h b/engines/mads/madsv2/core/game.h
index c98531586d9..eaf730a697f 100644
--- a/engines/mads/madsv2/core/game.h
+++ b/engines/mads/madsv2/core/game.h
@@ -190,6 +190,7 @@ extern char config_file_name[20];
 extern char save_game_key[8];
 extern char restart_game_key[40];
 extern char save_game_buf[20];
+extern int last_keypressed;
 
 extern void (*game_menu_routine)();   /* Game Menu routines      */
 extern void (*game_menu_init)();
diff --git a/engines/mads/madsv2/forest/rooms/room509.cpp b/engines/mads/madsv2/forest/rooms/room509.cpp
index 4bef95018d3..480dbdc7665 100644
--- a/engines/mads/madsv2/forest/rooms/room509.cpp
+++ b/engines/mads/madsv2/forest/rooms/room509.cpp
@@ -1,4 +1,4 @@
-???/* ScummVM - Graphic Adventure Engine
+/* 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
@@ -19,18 +19,14 @@
  *
  */
 
-#include "mads/madsv2/core/conv.h"
+#include "mads/madsv2/core/digi.h"
 #include "mads/madsv2/core/game.h"
-#include "mads/madsv2/core/imath.h"
-#include "mads/madsv2/core/inter.h"
 #include "mads/madsv2/core/kernel.h"
-#include "mads/madsv2/core/sound.h"
-#include "mads/madsv2/core/text.h"
-#include "mads/madsv2/forest/mads/inventory.h"
-#include "mads/madsv2/forest/mads/sounds.h"
-#include "mads/madsv2/forest/mads/words.h"
+#include "mads/madsv2/core/matte.h"
+#include "mads/madsv2/core/mouse.h"
+#include "mads/madsv2/core/player.h"
+#include "mads/madsv2/core/sprite.h"
 #include "mads/madsv2/forest/global.h"
-#include "mads/madsv2/forest/rooms/section1.h"
 #include "mads/madsv2/forest/rooms/room509.h"
 
 namespace MADS {
@@ -39,10 +35,13 @@ namespace Forest {
 namespace Rooms {
 
 struct Scratch {
-	int16 sprite[10];       /* Sprite series handles */
-	int16 sequence[10];     /* Sequence handles      */
-	int16 animation[10];     /* Animation handles     */
-	AnimationInfo animation_info[10];
+	int16 sprite[10];           /* 0x00 ??? sprite series handles */
+	int16 sequence[10];         /* 0x14 ??? sequence handles      */
+	int16 animation[10];        /* 0x28 ??? animation handles     */
+	AnimationInfo animation_info[10]; /* 0x3C                   */
+	int16 _8c;                  /* 0x8C ??? unused                */
+	int16 _8e;                  /* 0x8E ??? phase/step counter    */
+	int16 _90;                  /* 0x90 ??? volume-tier flag      */
 };
 
 static Scratch scratch;
@@ -53,29 +52,247 @@ static Scratch scratch;
 #define aa    local->animation
 #define aainfo scratch.animation_info
 
+static void room_509_anim1();
+static void room_509_anim2();
+static void room_509_anim3();
+static void room_509_anim4();
+static void room_509_anim5();
 
 static void room_509_init() {
+	global[g101] = -1;
+	mouse_hide();
+	global[player_score] = 0;
+	global[g009] = -1;
+	global_digi_play(15);
+	global[g009] = -1;
+	viewing_at_y = 22;
+	global[player_score] = 0;
+	player.walker_visible = 0;
+	player.commands_allowed = 0;
+
+	for (int count = 0; count < 10; count++) {
+		aainfo[count]._active = 0;
+		aainfo[count]._frame = -1;
+	}
+
+	aa[0] = kernel_run_animation(kernel_name('F', 1), 0);
+	aainfo[0]._active = -1;
 }
 
 static void room_509_daemon() {
+	if (global[player_hyperwalked] == -1) {
+		game_save_name(0);
+		kernel_save_game(save_game_buf);
+		new_room = 904;
+	}
+
+	if (kernel.trigger == 102)
+		new_room = 520;
+
+	if (aainfo[0]._active) room_509_anim1();
+	if (aainfo[1]._active) room_509_anim2();
+	if (aainfo[2]._active) room_509_anim3();
+	if (aainfo[3]._active) room_509_anim4();
+	if (aainfo[4]._active) room_509_anim5();
+}
+
+static void room_509_anim1() {
+	int16 aa_frame = kernel_anim[aa[0]].frame;
+	if (aa_frame != aainfo[0]._frame) {
+		aainfo[0]._frame = aa_frame;
+		if (aa_frame == 65) {
+			dont_frag_the_palette();
+			kernel_abort_animation(aa[0]);
+			aainfo[0]._active = 0;
+			aa[1] = kernel_run_animation(kernel_name('F', 2), 0);
+			aainfo[1]._active = -1;
+			kernel_synch(KERNEL_ANIM, aa[1], KERNEL_ANIM, aa[0]);
+		} else if (aa_frame < 65) {
+			if (aa_frame == 2) {
+				digi_initial_volume(30);
+				scratch._90 = 2;
+				digi_play_build(503, '_', 2, 3);
+				digi_val2 = -1;
+			} else if (aa_frame == 15) {
+				digi_play_build(509, 'b', 1, 1);
+				scratch._8e = 15;
+			}
+		}
+	}
+
+	if (kernel.trigger == 7 || kernel.trigger == 28) {
+		if (scratch._8e == 15) {
+			kernel_timing_trigger(30, 28);
+			scratch._8e = 16;
+		} else if (scratch._8e == 16) {
+			scratch._8e = 17;
+			digi_play_build(509, 'r', 1, 1);
+		} else if (scratch._8e == 17) {
+			scratch._8e = -1;
+		}
+	}
+
+	if (kernel.trigger == 8 && scratch._90 == 2) {
+		digi_initial_volume(60);
+		scratch._90 = 2;
+		digi_play_build(503, '_', 2, 3);
+		digi_val2 = -1;
+	}
+}
+
+static void room_509_anim2() {
+	int16 aa_frame = kernel_anim[aa[1]].frame;
+	if (aa_frame != aainfo[1]._frame) {
+		aainfo[1]._frame = aa_frame;
+		if (aa_frame == 2) {
+			digi_initial_volume(30);
+			scratch._90 = 2;
+			digi_play_build(503, '_', 2, 3);
+			digi_val2 = -1;
+		} else if (aa_frame == 42) {
+			dont_frag_the_palette();
+			kernel_abort_animation(aa[1]);
+			aainfo[1]._active = 0;
+			aa[2] = kernel_run_animation(kernel_name('F', 3), 0);
+			aainfo[2]._active = -1;
+			kernel_synch(KERNEL_ANIM, aa[2], KERNEL_ANIM, aa[1]);
+		}
+	}
+
+	if (kernel.trigger == 8 && scratch._90 == 2) {
+		digi_initial_volume(60);
+		scratch._90 = 2;
+		digi_play_build(503, '_', 2, 3);
+		digi_val2 = -1;
+	}
 }
 
-static void room_509_pre_parser() {
+static void room_509_anim3() {
+	int16 aa_frame = kernel_anim[aa[2]].frame;
+	if (aa_frame != aainfo[2]._frame) {
+		aainfo[2]._frame = aa_frame;
+		if (aa_frame == 2) {
+			digi_initial_volume(30);
+			scratch._90 = 2;
+			digi_play_build(503, '_', 2, 3);
+			digi_val2 = -1;
+		} else if (aa_frame == 20) {
+			dont_frag_the_palette();
+			kernel_abort_animation(aa[2]);
+			aainfo[2]._active = 0;
+			aa[3] = kernel_run_animation(kernel_name('F', 4), 0);
+			aainfo[3]._active = -1;
+			kernel_synch(KERNEL_ANIM, aa[3], KERNEL_ANIM, aa[2]);
+		}
+	}
+
+	if (kernel.trigger == 8 && scratch._90 == 2) {
+		digi_initial_volume(60);
+		scratch._90 = 2;
+		digi_play_build(503, '_', 2, 3);
+		digi_val2 = -1;
+	}
 }
 
-static void room_509_parser() {
+static void room_509_anim4() {
+	int16 aa_frame = kernel_anim[aa[3]].frame;
+	if (aa_frame != aainfo[3]._frame) {
+		aainfo[3]._frame = aa_frame;
+		if (aa_frame == 53) {
+			dont_frag_the_palette();
+			kernel_abort_animation(aa[3]);
+			aainfo[3]._active = 0;
+			aa[4] = kernel_run_animation(kernel_name('F', 5), 0);
+			aainfo[4]._active = -1;
+			kernel_synch(KERNEL_ANIM, aa[4], KERNEL_ANIM, aa[3]);
+		} else if (aa_frame < 53) {
+			if (aa_frame == 2) {
+				digi_initial_volume(30);
+				scratch._90 = 2;
+				digi_play_build(503, '_', 2, 3);
+				digi_val2 = -1;
+			} else if (aa_frame == 27) {
+				digi_play_build(509, 'r', 2, 1);
+				scratch._8e = 27;
+			}
+		}
+	}
+
+	if (kernel.trigger == 7 || kernel.trigger == 28) {
+		if (scratch._8e == 27)
+			scratch._8e = -1;
+	}
+
+	if (kernel.trigger == 8 && scratch._90 == 2) {
+		digi_initial_volume(60);
+		scratch._90 = 2;
+		digi_play_build(503, '_', 2, 3);
+		digi_val2 = -1;
+	}
+}
+
+static void room_509_anim5() {
+	int16 aa_frame = kernel_anim[aa[4]].frame;
+	if (aa_frame != aainfo[4]._frame) {
+		aainfo[4]._frame = aa_frame;
+		if (aa_frame == 80) {
+			kernel_reset_animation(aa[4], 79);
+			aainfo[4]._frame = 79;
+		} else if (aa_frame < 80) {
+			if (aa_frame == 2) {
+				digi_initial_volume(30);
+				scratch._90 = 2;
+				digi_play_build(503, '_', 2, 3);
+				digi_val2 = -1;
+			} else if (aa_frame == 27) {
+				digi_play_build(509, 'e', 1, 1);
+				scratch._8e = 27;
+			} else if (aa_frame == 79) {
+				kernel_timing_trigger(40, 102);
+			}
+		}
+	}
+
+	if (kernel.trigger == 7 || kernel.trigger == 28) {
+		if (scratch._8e == 27) {
+			kernel_timing_trigger(2, 28);
+			scratch._8e = 16;
+		} else if ((uint16)scratch._8e <= 27) {
+			if (scratch._8e == 16) {
+				scratch._8e = 17;
+				digi_play_build(509, 'b', 2, 1);
+			} else if (scratch._8e == 17) {
+				kernel_timing_trigger(2, 28);
+				scratch._8e = 18;
+			} else if (scratch._8e == 18) {
+				digi_play_build(509, 'r', 3, 1);
+				scratch._8e = 19;
+			} else if (scratch._8e == 19) {
+				scratch._8e = -1;
+			}
+		}
+	}
+
+	if (kernel.trigger == 8 && scratch._90 == 2) {
+		digi_initial_volume(60);
+		scratch._90 = 2;
+		digi_play_build(503, '_', 2, 3);
+		digi_val2 = -1;
+	}
 }
 
 void room_509_synchronize(Common::Serializer &s) {
 	for (int16 &v : scratch.sprite)    s.syncAsSint16LE(v);
 	for (int16 &v : scratch.sequence)  s.syncAsSint16LE(v);
 	for (int16 &v : scratch.animation) s.syncAsSint16LE(v);
+	for (AnimationInfo &ai : scratch.animation_info) ai.synchronize(s);
+	s.syncAsSint16LE(scratch._8c);
+	s.syncAsSint16LE(scratch._8e);
+	s.syncAsSint16LE(scratch._90);
 }
 
 void room_509_preload() {
 	room_init_code_pointer = room_509_init;
-	room_pre_parser_code_pointer = room_509_pre_parser;
-	room_parser_code_pointer = room_509_parser;
 	room_daemon_code_pointer = room_509_daemon;
 
 	global[g016] = -1;
diff --git a/engines/mads/madsv2/forest/rooms/room510.cpp b/engines/mads/madsv2/forest/rooms/room510.cpp
index 546e036c21f..2fcaf671ffa 100644
--- a/engines/mads/madsv2/forest/rooms/room510.cpp
+++ b/engines/mads/madsv2/forest/rooms/room510.cpp
@@ -1,4 +1,4 @@
-???/* ScummVM - Graphic Adventure Engine
+/* 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
@@ -19,18 +19,17 @@
  *
  */
 
-#include "mads/madsv2/core/conv.h"
+#include "mads/madsv2/core/digi.h"
+#include "mads/madsv2/core/font.h"
 #include "mads/madsv2/core/game.h"
-#include "mads/madsv2/core/imath.h"
-#include "mads/madsv2/core/inter.h"
 #include "mads/madsv2/core/kernel.h"
-#include "mads/madsv2/core/sound.h"
-#include "mads/madsv2/core/text.h"
-#include "mads/madsv2/forest/mads/inventory.h"
-#include "mads/madsv2/forest/mads/sounds.h"
-#include "mads/madsv2/forest/mads/words.h"
+#include "mads/madsv2/core/matte.h"
+#include "mads/madsv2/core/midi.h"
+#include "mads/madsv2/core/mouse.h"
+#include "mads/madsv2/core/player.h"
+#include "mads/madsv2/core/quote.h"
+#include "mads/madsv2/core/sprite.h"
 #include "mads/madsv2/forest/global.h"
-#include "mads/madsv2/forest/rooms/section1.h"
 #include "mads/madsv2/forest/rooms/room510.h"
 
 namespace MADS {
@@ -39,10 +38,17 @@ namespace Forest {
 namespace Rooms {
 
 struct Scratch {
-	int16 sprite[10];       /* Sprite series handles */
-	int16 sequence[10];     /* Sequence handles      */
-	int16 animation[10];     /* Animation handles     */
-	AnimationInfo animation_info[10];
+	int16 sprite[10];           /* 0x00 ??? sprite series handles */
+	int16 sequence[10];         /* 0x14 ??? sequence handles      */
+	int16 animation[10];        /* 0x28 ??? animation handles     */
+	AnimationInfo animation_info[10]; /* 0x3C                   */
+	int16 _8c;                  /* 0x8C                         */
+	int16 _8e;                  /* 0x8E ??? digi-play phase check */
+	int16 _90;                  /* 0x90 ??? volume/replay flag    */
+	int16 _92;                  /* 0x92 ??? timing accumulator    */
+	int16 _94;                  /* 0x94 ??? sequence table index  */
+	int16 _96;                  /* 0x96 ??? active flag           */
+	int16 _98;                  /* 0x98 ??? room mode (669/670)   */
 };
 
 static Scratch scratch;
@@ -53,28 +59,307 @@ static Scratch scratch;
 #define aa    local->animation
 #define aainfo scratch.animation_info
 
+// Credits-line table
+struct Room510Entry {
+	int16 quote_id;	// quote ID to display, or -1 (schedule anim4), or -2 (end).
+	int16 x;		// x position override (0 = auto-centre on 320-wide screen).
+	int16 y;		// y position override (0 = use 70).
+};
+
+static const Room510Entry room_510_array1[112] = {
+	/* [0] unused */ {  0,  0,   0 },
+	/* [1]  */ {  74,  0,  40 }, /* [2]  */ {  75,  0,  60 },
+	/* [3]  */ {  76,  0,  80 }, /* [4]  */ {  77,  0, 100 },
+	/* [5]  */ {  -1,  0,   0 }, /* [6]  */ {  78,  0,   0 },
+	/* [7]  */ {  -1,  0,   0 }, /* [8]  */ {  79,  0,   0 },
+	/* [9]  */ {  -1,  0,   0 }, /* [10] */ {  80,  0,   0 },
+	/* [11] */ {  -1,  0,   0 }, /* [12] */ {  81,  0,   0 },
+	/* [13] */ {  -1,  0,   0 }, /* [14] */ {  82,  0,  50 },
+	/* [15] */ {  83,  0,  80 }, /* [16] */ {  -1,  0,   0 },
+	/* [17] */ {  84,  0,  50 }, /* [18] */ {  85,  0,  80 },
+	/* [19] */ {  -1,  0,   0 }, /* [20] */ {  86,  0,  50 },
+	/* [21] */ {  87,  0,  80 }, /* [22] */ {  -1,  0,   0 },
+	/* [23] */ {  88,  0,  50 }, /* [24] */ {  89,  0,  80 },
+	/* [25] */ {  -1,  0,   0 }, /* [26] */ {  90,  0,  40 },
+	/* [27] */ {  91,  0,  60 }, /* [28] */ {  92,  0,  80 },
+	/* [29] */ {  93,  0, 100 }, /* [30] */ {  -1,  0,   0 },
+	/* [31] */ {  94,  0,  50 }, /* [32] */ {  95,  0,  80 },
+	/* [33] */ {  -1,  0,   0 }, /* [34] */ {  96,  0,  50 },
+	/* [35] */ {  97,  0,  80 }, /* [36] */ {  -1,  0,   0 },
+	/* [37] */ {  98,  0,  50 }, /* [38] */ {  99,  0,  80 },
+	/* [39] */ { 100,  0, 100 }, /* [40] */ {  -1,  0,   0 },
+	/* [41] */ { 101,  0,  50 }, /* [42] */ { 102,  0,  80 },
+	/* [43] */ {  -1,  0,   0 }, /* [44] */ { 103,  0,  50 },
+	/* [45] */ { 104,  0,  80 }, /* [46] */ {  -1,  0,   0 },
+	/* [47] */ { 105,  0,  50 }, /* [48] */ { 106,  0,  80 },
+	/* [49] */ { 107,  0, 100 }, /* [50] */ {  -1,  0,   0 },
+	/* [51] */ { 108,  0,  50 }, /* [52] */ { 109,  0,  80 },
+	/* [53] */ {  -1,  0,   0 }, /* [54] */ { 110,  0,  50 },
+	/* [55] */ { 111,  0,  80 }, /* [56] */ { 112,  0, 100 },
+	/* [57] */ {  -1,  0,   0 }, /* [58] */ { 113,  0,  50 },
+	/* [59] */ { 114,  0,  80 }, /* [60] */ { 115,  0, 100 },
+	/* [61] */ {  -1,  0,   0 }, /* [62] */ { 116,  0,  50 },
+	/* [63] */ { 117,  0,  80 }, /* [64] */ {  -1,  0,   0 },
+	/* [65] */ { 118,  0,  20 }, /* [66] */ { 119,  0,  40 },
+	/* [67] */ { 120,  0,  60 }, /* [68] */ { 121,  0,  80 },
+	/* [69] */ { 122,  0, 100 }, /* [70] */ { 123,  0, 120 },
+	/* [71] */ {  -1,  0,   0 }, /* [72] */ { 124,  0,  20 },
+	/* [73] */ { 125,  0,  40 }, /* [74] */ { 126,  0,  60 },
+	/* [75] */ { 127,  0,  80 }, /* [76] */ { 128,  0, 100 },
+	/* [77] */ {  -1,  0,   0 }, /* [78] */ { 129,  0,  40 },
+	/* [79] */ { 130,  0,  80 }, /* [80] */ { 131,  0, 120 },
+	/* [81] */ {  -1,  0,   0 }, /* [82] */ { 132,  0,  40 },
+	/* [83] */ { 133,  0,  60 }, /* [84] */ { 134,  0,  80 },
+	/* [85] */ {  -1,  0,   0 }, /* [86] */ { 135,  0,  40 },
+	/* [87] */ { 136,  0,  80 }, /* [88] */ {  -1,  0,   0 },
+	/* [89] */ { 137,  0,  40 }, /* [90] */ { 138,  0,  60 },
+	/* [91] */ { 139,  0,  80 }, /* [92] */ {  -1,  0,   0 },
+	/* [93] */ { 140,  0,  40 }, /* [94] */ { 141,  0,  80 },
+	/* [95] */ {  -1,  0,   0 }, /* [96] */ { 142,  0,  20 },
+	/* [97] */ { 143,  0,  40 }, /* [98] */ { 144,  0,  60 },
+	/* [99] */ { 145,  0,  80 }, /* [100]*/ { 146,  0, 100 },
+	/* [101]*/ { 147,  0, 120 }, /* [102]*/ {  -1,  0,   0 },
+	/* [103]*/ { 148,  0,  40 }, /* [104]*/ { 149,  0,  60 },
+	/* [105]*/ { 150,  0,  80 }, /* [106]*/ { 151,  0, 100 },
+	/* [107]*/ {  -1,  0,   0 }, /* [108]*/ { 152,  0,  50 },
+	/* [109]*/ { 153,  0,  80 }, /* [110]*/ {  -1,  0,   0 },
+	/* [111]*/ {  -2,  0,   0 },
+};
+
+static void room_510_anim4() {
+	for (int i = 0; i < 10; i++)
+		kernel_message_delete(i);
+
+	// The original called an anim5 function here, which did a shift within an unused array.
+}
+
+static void room_510_load_quotes() {
+	scratch._94 = 1;
+	kernel.quotes = quote_load(
+		74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92,
+		93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109,
+		110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124,
+		125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139,
+		140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 0);
+	scratch._92 = 100;
+}
+
+static void room_510_anim1() {
+	const Room510Entry &entry = room_510_array1[scratch._94];
+	char *text = quote_string(kernel.quotes, entry.quote_id);
+	int x;
+	if (entry.x) {
+		x = entry.x;
+	} else {
+		x = 160 - font_string_width(font_conv, text, -1) / 2;
+	}
+	int y = entry.y ? entry.y : 70;
+	kernel_message_add(text, x, y, 92, 999, 0, 0);
+}
+
+static void room_510_anim2() {
+	int16 aa_frame = kernel_anim[aa[0]].frame;
+	if (aa_frame != aainfo[0]._frame) {
+		aainfo[0]._frame = aa_frame;
+		if (aa_frame == 1) {
+			digi_play_build(503, '_', 2, 3);
+			digi_val2 = -1;
+			scratch._90 = 1;
+			kernel_seq_delete(seq[0]);
+			kernel_seq_delete(seq[1]);
+			kernel_seq_delete(seq[2]);
+			kernel_seq_delete(seq[3]);
+			kernel_seq_delete(seq[4]);
+		} else if (aa_frame == 57) {
+			digi_play_build(510, 'e', 1, 1);
+			scratch._8e = 63;
+		} else if (aa_frame == 131) {
+			if (digi_val1)
+				digi_stop(1);
+			scratch._90 = 133;
+			digi_play_build(510, '_', 3, 2);
+		} else if (aa_frame == 140) {
+			kernel_abort_animation(aa[0]);
+			aainfo[0]._active = 0;
+			aa[1] = kernel_run_animation(kernel_name('F', 2), 0);
+			aainfo[1]._active = -1;
+			scratch._8c = 40;
+			kernel_synch(KERNEL_ANIM, aa[1], KERNEL_ANIM, aa[0]);
+		}
+	}
+
+	if (kernel.trigger == 7 || kernel.trigger == 28) {
+		if (scratch._8e == 63) {
+			scratch._8e = 66;
+			kernel_timing_trigger(2, 28);
+		} else if (scratch._8e == 66) {
+			scratch._8e = 90;
+			digi_play_build(510, 'b', 1, 1);
+		} else if (scratch._8e == 90) {
+			scratch._8e = 91;
+			kernel_timing_trigger(2, 28);
+		} else if (scratch._8e == 91) {
+			scratch._8e = 132;
+			digi_play_build(510, 'r', 1, 1);
+		} else if (scratch._8e == 133) {
+			scratch._8e = -1;
+		}
+	}
+
+	if (kernel.trigger == 8) {
+		if (scratch._90 == 1) {
+			scratch._90 = 1;
+			digi_play_build(503, '_', 2, 3);
+			digi_val2 = -1;
+		} else if (scratch._90 == 133) {
+			scratch._90 = -1;
+		}
+	}
+}
+
+static void room_510_anim3() {
+	int16 aa_frame = kernel_anim[aa[1]].frame;
+	if (aa_frame != aainfo[1]._frame) {
+		aainfo[1]._frame = aa_frame;
+		if (aa_frame == 1) {
+			digi_play_build(510, '_', 1, 2);
+			scratch._90 = 1;
+			midi_stop();
+			global_digi_play(4);
+		} else if (aa_frame == 40) {
+			midi_stop();
+			new_room = 107;
+		}
+	}
+
+	if (kernel.trigger == 8 && scratch._90 == 1)
+		scratch._90 = -1;
+}
 
 static void room_510_init() {
+	global[player_score] = 0;
+	global[g009] = 0;
+	scratch._96 = 0;
+	scratch._98 = 670;
+	viewing_at_y = 22;
+	player.walker_visible = 0;
+	player.commands_allowed = 0;
+	mouse_hide();
+
+	for (int count = 0; count < 10; count++) {
+		aainfo[count]._active = 0;
+		aainfo[count]._frame = -1;
+	}
+
+	if (previous_room == 104) {
+		room_510_load_quotes();
+		scratch._98 = 669;
+		aa[2] = kernel_run_animation(kernel_name('c', 1), 106);
+		aainfo[2]._active = -1;
+		aainfo[2]._frame = 0;
+		return;
+	}
+
+	if (global[g102] != 0 || flags[4] == 5) {
+		room_510_load_quotes();
+		kernel_timing_trigger(50, 106);
+		kernel_timing_trigger(51, 108);
+		return;
+	}
+
+	aa[0] = kernel_run_animation(kernel_name('F', 1), 0);
+	aainfo[0]._active = -1;
+	scratch._8c = 140;
 }
 
 static void room_510_daemon() {
-}
+	if (global[player_hyperwalked] == -1) {
+		if (global[g102] == 0) {
+			game_save_name(0);
+			kernel_save_game(save_game_buf);
+		}
+		global[g102] = 0;
+		new_room = 904;
+	}
+
+	switch (kernel.trigger) {
+	case 8:
+		if (scratch._8e == 668) {
+			scratch._8e = -1;
+			global[g009] = 0;
+			global_digi_play(14);
+			kernel_timing_trigger(2100, 104);
+		}
+		break;
+	case 102: {
+		int16 action = room_510_array1[scratch._94].quote_id;
+		if (action == -2) {
+			global[g102] = 0;
+			new_room = 904;
+		} else if (action == -1) {
+			kernel_timing_trigger(scratch._92, 103);
+			scratch._92 = 100;
+		} else {
+			room_510_anim1();
+			kernel_timing_trigger(1, 102);
+			scratch._92 += 20;
+		}
+		scratch._94++;
+		break;
+	}
+	case 103:
+		room_510_anim4();
+		kernel_timing_trigger(20, 102);
+		break;
+	case 104:
+		global[g009] = -1;
+		global_digi_play(11);
+		break;
+	case 106:
+		scratch._96 = -1;
+		last_keypressed = -1;
+		aainfo[2]._active = 0;
+		kernel_timing_trigger(40, 107);
+		kernel_timing_trigger(30, 102);
+		if (scratch._98 == 669)
+			kernel_timing_trigger(1400, 108);
+		break;
+	case 107:
+		mouse_cursor_sprite(cursor, 1);
+		break;
+	case 108:
+		midi_stop();
+		digi_play_build(306, '_', 999, 2);
+		scratch._8e = 668;
+		break;
+	default:
+		break;
+	}
 
-static void room_510_pre_parser() {
+	if (aainfo[0]._active) room_510_anim2();
+	if (aainfo[1]._active) room_510_anim3();
 }
 
 static void room_510_parser() {
+	player.command_ready = 0;
 }
 
 void room_510_synchronize(Common::Serializer &s) {
 	for (int16 &v : scratch.sprite)    s.syncAsSint16LE(v);
 	for (int16 &v : scratch.sequence)  s.syncAsSint16LE(v);
 	for (int16 &v : scratch.animation) s.syncAsSint16LE(v);
+	for (AnimationInfo &ai : scratch.animation_info) ai.synchronize(s);
+	s.syncAsSint16LE(scratch._8c);
+	s.syncAsSint16LE(scratch._8e);
+	s.syncAsSint16LE(scratch._90);
+	s.syncAsSint16LE(scratch._92);
+	s.syncAsSint16LE(scratch._94);
+	s.syncAsSint16LE(scratch._96);
+	s.syncAsSint16LE(scratch._98);
 }
 
 void room_510_preload() {
-	room_init_code_pointer = room_510_init;
-	room_pre_parser_code_pointer = room_510_pre_parser;
+	room_init_code_pointer   = room_510_init;
 	room_parser_code_pointer = room_510_parser;
 	room_daemon_code_pointer = room_510_daemon;
 


Commit: 15ea8b571e252f6f2bff288fcd7be24354ee5fa1
    https://github.com/scummvm/scummvm/commit/15ea8b571e252f6f2bff288fcd7be24354ee5fa1
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-06-16T18:31:02+10:00

Commit Message:
MADS: FOREST: Implemented room 520

Changed paths:
    engines/mads/madsv2/forest/rooms/room520.cpp


diff --git a/engines/mads/madsv2/forest/rooms/room520.cpp b/engines/mads/madsv2/forest/rooms/room520.cpp
index dd529a92c73..4c44a7458df 100644
--- a/engines/mads/madsv2/forest/rooms/room520.cpp
+++ b/engines/mads/madsv2/forest/rooms/room520.cpp
@@ -1,4 +1,4 @@
-???/* ScummVM - Graphic Adventure Engine
+/* 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
@@ -19,18 +19,14 @@
  *
  */
 
-#include "mads/madsv2/core/conv.h"
+#include "mads/madsv2/core/digi.h"
 #include "mads/madsv2/core/game.h"
-#include "mads/madsv2/core/imath.h"
-#include "mads/madsv2/core/inter.h"
 #include "mads/madsv2/core/kernel.h"
-#include "mads/madsv2/core/sound.h"
-#include "mads/madsv2/core/text.h"
-#include "mads/madsv2/forest/mads/inventory.h"
-#include "mads/madsv2/forest/mads/sounds.h"
-#include "mads/madsv2/forest/mads/words.h"
+#include "mads/madsv2/core/matte.h"
+#include "mads/madsv2/core/mouse.h"
+#include "mads/madsv2/core/player.h"
+#include "mads/madsv2/core/sprite.h"
 #include "mads/madsv2/forest/global.h"
-#include "mads/madsv2/forest/rooms/section1.h"
 #include "mads/madsv2/forest/rooms/room520.h"
 
 namespace MADS {
@@ -39,10 +35,12 @@ namespace Forest {
 namespace Rooms {
 
 struct Scratch {
-	int16 sprite[10];       /* Sprite series handles */
-	int16 sequence[10];     /* Sequence handles      */
-	int16 animation[10];     /* Animation handles     */
+	int16 sprite[10];
+	int16 sequence[10];
+	int16 animation[10];
 	AnimationInfo animation_info[10];
+	int16 _8c;
+	int16 _8e;
 };
 
 static Scratch scratch;
@@ -53,30 +51,105 @@ static Scratch scratch;
 #define aa    local->animation
 #define aainfo scratch.animation_info
 
-
 static void room_520_init() {
-}
+	ss[0] = kernel_load_series(kernel_name('n', 1), 0);
+	seq[0] = kernel_seq_stamp(ss[0], false, -1);
+	kernel_seq_depth(seq[0], 7);
+	kernel_seq_loc(seq[0], 252, 75);
+	kernel_seq_scale(seq[0], 100);
 
-static void room_520_daemon() {
+	ss[1] = kernel_load_series(kernel_name('n', 1), 0);
+	seq[1] = kernel_seq_stamp(ss[1], false, -1);
+	kernel_seq_depth(seq[1], 7);
+	kernel_seq_loc(seq[1], 272, 80);
+	kernel_seq_scale(seq[1], 100);
+
+	ss[2] = kernel_load_series(kernel_name('p', 3), 0);
+	seq[2] = kernel_seq_stamp(ss[2], false, -1);
+	kernel_seq_depth(seq[2], 1);
+	kernel_seq_loc(seq[2], 175, 90);
+	kernel_seq_scale(seq[2], 50);
+
+	viewing_at_y = 22;
+	global[player_score] = 0;
+	global[g009] = 0;
+	player.walker_visible = 0;
+	player.commands_allowed = 0;
+	mouse_hide();
+
+	for (int i = 0; i < 10; i++) {
+		aainfo[i]._active = 0;
+		aainfo[i]._frame = -1;
+	}
+
+	kernel_timing_trigger(100, 101);
+	kernel_timing_trigger(5, 100);
 }
 
-static void room_520_pre_parser() {
+static void room_520_anim1() {
+	int16 aa_frame = kernel_anim[aa[0]].frame;
+	if (aa_frame != aainfo[0]._frame) {
+		aainfo[0]._frame = aa_frame;
+		if (aa_frame == 66) {
+			new_room = 503;
+		} else if (aa_frame < 66) {
+			if (aa_frame == 1) {
+				digi_initial_volume(90);
+				digi_play_build(503, '_', 2, 3);
+				digi_val2 = -1;
+				scratch._8e = 1;
+			} else if (aa_frame == 3) {
+				kernel_seq_delete(seq[0]);
+				kernel_seq_delete(seq[1]);
+				kernel_seq_delete(seq[2]);
+			}
+		}
+	}
+
+	if (kernel.trigger == 8 || kernel.trigger == 28) {
+		if (scratch._8e - 1 == 0) {
+			digi_initial_volume(60);
+			scratch._8e = 1;
+			digi_play_build(503, '_', 2, 3);
+			digi_val2 = -1;
+		}
+	}
 }
 
-static void room_520_parser() {
+static void room_520_daemon() {
+	if (global[player_hyperwalked] == -1) {
+		game_save_name(0);
+		kernel_save_game(save_game_buf);
+		new_room = 904;
+	}
+
+	switch (kernel.trigger) {
+	case 100:
+		aa[0] = kernel_run_animation(kernel_name('F', 1), 0);
+		aainfo[0]._active = -1;
+		scratch._8c = 66;
+		break;
+	case 101:
+		global_digi_play(14);
+		break;
+	}
+
+	if (aainfo[0]._active)
+		room_520_anim1();
 }
 
 void room_520_synchronize(Common::Serializer &s) {
 	for (int16 &v : scratch.sprite)    s.syncAsSint16LE(v);
 	for (int16 &v : scratch.sequence)  s.syncAsSint16LE(v);
 	for (int16 &v : scratch.animation) s.syncAsSint16LE(v);
+	for (AnimationInfo &ai : scratch.animation_info) ai.synchronize(s);
+	s.syncAsSint16LE(scratch._8c);
+	s.syncAsSint16LE(scratch._8e);
 }
 
 void room_520_preload() {
-	room_init_code_pointer = room_520_init;
-	room_pre_parser_code_pointer = room_520_pre_parser;
-	room_parser_code_pointer = room_520_parser;
-	room_daemon_code_pointer = room_520_daemon;
+	room_init_code_pointer       = room_520_init;
+	room_daemon_code_pointer     = room_520_daemon;
 
 	global_section_walker();
 	global_section_interface();



From noreply at scummvm.org  Tue Jun 16 19:34:40 2026
From: noreply at scummvm.org (tag2015)
Date: Tue, 16 Jun 2026 19:34:40 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 02891ad9466e149dba6685cf9427592ba97afc82
Message-ID: <20260616193440.D49897005CC@ron.scummvm.net>

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:
f310b3a576 WINTERMUTE: Indicate fantranslations in detection
c55b2fcab4 WINTERMUTE: set sotv1 as stable
02891ad946 WINTERMUTE: Add Golden Calf fantranslations


Commit: f310b3a576d84097269392c225b37a2f5d6bb183
    https://github.com/scummvm/scummvm/commit/f310b3a576d84097269392c225b37a2f5d6bb183
Author: Walter Agazzi (walter.agazzi at protonmail.com)
Date: 2026-06-16T15:31:48+02:00

Commit Message:
WINTERMUTE: Indicate fantranslations in detection

Changed paths:
    engines/wintermute/detection_tables.h


diff --git a/engines/wintermute/detection_tables.h b/engines/wintermute/detection_tables.h
index 0681daf3109..45fcef349a7 100644
--- a/engines/wintermute/detection_tables.h
+++ b/engines/wintermute/detection_tables.h
@@ -1465,7 +1465,7 @@ static const WMEGameDescription gameDescriptions[] = {
 		WME_ENTRY1s("data.dcp", "2e7bae5c1d0fd18f269147b834be2bc8", 1174553), Common::FR_FRA, ADGF_UNSTABLE, WME_1_8_11),
 
 	// The Golden Calf (Spanish fanmade)
-	WME_WINENTRY("goldencalf", "",
+	WME_WINENTRY("goldencalf", "Fanmade",
 		WME_ENTRY2s("data.dcp", "2e7bae5c1d0fd18f269147b834be2bc8", 1174553,
 					"string.tab", "639e54d5d5392e56785c1f6cb7d0d5e8", 122759), Common::ES_ESP, ADGF_UNSTABLE, WME_1_8_11),
 
@@ -1691,12 +1691,12 @@ static const WMEGameDescription gameDescriptions[] = {
 					"german_hd.dcp", "8d85f83a3fc8f1bec4e5ba2158b05b1e", 152499998), Common::DE_DEU, ADGF_UNSTABLE | GF_IGNORE_SD_FILES, WME_LITE),
 
 	// J.U.L.I.A.: Among the Stars (HD Ready Version) (Steam) (Spanish fanmade translation)
-	WME_WINENTRY("juliastars", "HD Ready Version/Steam",
+	WME_WINENTRY("juliastars", "HD Ready Version/Steam/Fanmade",
 		WME_ENTRY2s("data_sd.dcp", "da3508bd60025bac35211fb6fc959d88", 5655554,
 					"data_sd.dcp", "da3508bd60025bac35211fb6fc959d88", 5655554), Common::ES_ESP, ADGF_UNSTABLE | GF_IGNORE_HD_FILES, WME_LITE),
 
 	// J.U.L.I.A.: Among the Stars (Full HD Version) (Steam) (Spanish fanmade translation)
-	WME_WINENTRY("juliastars", "Full HD Version/Steam",
+	WME_WINENTRY("juliastars", "Full HD Version/Steam/Fanmade",
 		WME_ENTRY2s("data_hd.dcp", "da3508bd60025bac35211fb6fc959d88", 5655554,
 					"data_hd.dcp", "da3508bd60025bac35211fb6fc959d88", 5655554), Common::ES_ESP, ADGF_UNSTABLE | GF_IGNORE_SD_FILES, WME_LITE),
 
@@ -1721,12 +1721,12 @@ static const WMEGameDescription gameDescriptions[] = {
 					"german_hd.dcp", "19a771b1a933b71b889026d53734b0c0", 152500044), Common::DE_DEU, ADGF_UNSTABLE | GF_IGNORE_SD_FILES, WME_LITE),
 
 	// J.U.L.I.A.: Among the Stars (HD Ready Version) (GOG) (Spanish fanmade translation)
-	WME_WINENTRY("juliastars", "HD Ready Version/GOG.com",
+	WME_WINENTRY("juliastars", "HD Ready Version/GOG.com/Fanmade",
 		WME_ENTRY2s("data_sd.dcp", "29f4856cc1514bdb86d3b19a39d86d76", 5877935,
 					"data_sd.dcp", "29f4856cc1514bdb86d3b19a39d86d76", 5877935), Common::ES_ESP, ADGF_UNSTABLE | GF_IGNORE_HD_FILES, WME_LITE),
 
 	// J.U.L.I.A.: Among the Stars (Full HD Version) (GOG) (Spanish fanmade translation)
-	WME_WINENTRY("juliastars", "Full HD Version/GOG.com",
+	WME_WINENTRY("juliastars", "Full HD Version/GOG.com/Fanmade",
 		WME_ENTRY2s("data_hd.dcp", "29f4856cc1514bdb86d3b19a39d86d76", 5877935,
 					"data_hd.dcp", "29f4856cc1514bdb86d3b19a39d86d76", 5877935), Common::ES_ESP, ADGF_UNSTABLE | GF_IGNORE_SD_FILES, WME_LITE),
 
@@ -1854,7 +1854,7 @@ static const WMEGameDescription gameDescriptions[] = {
 		WME_ENTRY1s("data.dcp", "2aa5ab924b05c9539a5a118bc263c2f8", 1049803074), Common::RU_RUS, ADGF_UNSTABLE, WME_1_9_1),
 
 	// Monday Starts on Saturday (Spanish fanmade)
-	WME_WINENTRY("msos", "",
+	WME_WINENTRY("msos", "Fanmade",
 		WME_ENTRY2s("data.dcp", "2aa5ab924b05c9539a5a118bc263c2f8", 1049803074,
 					"string.tab", "0f3bd71ea07a32fc74d10bddd0a0c953", 173787), Common::ES_ESP, ADGF_UNSTABLE, WME_1_9_1),
 
@@ -1863,7 +1863,7 @@ static const WMEGameDescription gameDescriptions[] = {
 		WME_ENTRY1s("data.dcp", "ed1deb8e6b2ce0a50ba26e035bdb7006", 1086177292), Common::RU_RUS, ADGF_UNSTABLE | GF_3D, WME_1_9_1),
 
 	// Mukhtar and his Team (Spanish fanmade)
-	WME_WINENTRY("mukhtar", "",
+	WME_WINENTRY("mukhtar", "Fanmade",
 		WME_ENTRY2s("data.dcp", "ed1deb8e6b2ce0a50ba26e035bdb7006", 1086177292,
 					"string.tab", "c2d40757013199ce68831e0b2e903291", 298927), Common::ES_ESP, ADGF_UNSTABLE | GF_3D, WME_1_9_1),
 
@@ -4310,7 +4310,7 @@ static const WMEGameDescription gameDescriptions[] = {
 					"string3.tab", "e9d0e6341e8994be5fa6d70d39ac630b", 813982), Common::RU_RUS, ADGF_NO_FLAGS, WME_1_8_6),
 
 	// 1 1/2 Ritter: Auf der Suche nach der hinreissenden Herzelinde (Spanish fanmade)
-	WME_WINENTRY("ritter", "",
+	WME_WINENTRY("ritter", "Fanmade",
 		WME_ENTRY2s("data.dcp", "5ac416cee605d3a30f4d59687b1cdab2", 364260278,
 					"string3.tab", "a20120c9a45f189321924adb54f3515f", 527627), Common::ES_ESP, ADGF_NO_FLAGS, WME_1_8_6),
 
@@ -4320,7 +4320,7 @@ static const WMEGameDescription gameDescriptions[] = {
 					"string3.tab", "413ee9f47f52567d0ac22e34485cacfa", 513012), Common::EN_ANY, ADGF_NO_FLAGS, WME_1_8_6),
 
 	// 1 1/2 Ritter: Auf der Suche nach der hinreissenden Herzelinde (Czech fanmade)
-	WME_WINENTRY("ritter", "",
+	WME_WINENTRY("ritter", "Fanmade",
 		WME_ENTRY1s("data.dcp", "1138bca234b7e81e11128c4da2c283b0", 364067513), Common::CS_CZE, ADGF_NO_FLAGS, WME_1_8_6),
 
 	// 1 1/2 Ritter: Auf der Suche nach der hinreissenden Herzelinde (German demo)
@@ -5557,7 +5557,7 @@ static const WMEGameDescription gameDescriptions[] = {
 		WME_ENTRY1s("data.dcp", "d0c9ac5a61b37ae9fcedb58fa1c98cd5", 135504102), Common::RU_RUS, ADGF_UNSTABLE | GF_3D, WME_1_7_93),
 
 	// Sunrise: The game (Spanish fanmade)
-	WME_WINENTRY("sunrise", "",
+	WME_WINENTRY("sunrise", "Fanmade",
 		WME_ENTRY2s("data.dcp", "d06744fc60982a30e82d96075698c8bc", 134409552,
 					"string.tab", "596b3be0d09d41be8d0999ca536d810a", 334279), Common::ES_ESP, ADGF_UNSTABLE | GF_3D, WME_1_7_93),
 
@@ -5570,7 +5570,7 @@ static const WMEGameDescription gameDescriptions[] = {
 		WME_ENTRY1s("data.dcp", "bc607fe93d6751de2d0d0b7f4b2c90f8", 798965082), Common::RU_RUS, ADGF_UNSTABLE | GF_3D, WME_1_8_9),
 
 	// Susan Rose: Mysterious Child (Spanish fanmade)
-	WME_WINENTRY("susanrose1", "",
+	WME_WINENTRY("susanrose1", "Fanmade",
 		WME_ENTRY2s("data.dcp", "bc607fe93d6751de2d0d0b7f4b2c90f8", 798965082,
 					"string.tab", "41f5d904edf5f1fd3d80d8f8e6ebd35c", 255118), Common::ES_ESP, ADGF_UNSTABLE | GF_3D, WME_1_8_9),
 
@@ -5579,7 +5579,7 @@ static const WMEGameDescription gameDescriptions[] = {
 		WME_ENTRY1s("data.dcp", "789660631a79ed76d95446d1f1e6e694", 922032035), Common::RU_RUS, ADGF_UNSTABLE | GF_3D, WME_1_8_11),
 
 	// Susan Rose: Delicate Murder (Spanish fanmade)
-	WME_WINENTRY("susanrose2", "",
+	WME_WINENTRY("susanrose2", "Fanmade",
 		WME_ENTRY2s("data.dcp", "789660631a79ed76d95446d1f1e6e694", 922032035,
 					"string.tab", "8d4a6bed924051c864b78e3401da40fe", 181897), Common::ES_ESP, ADGF_UNSTABLE | GF_3D, WME_1_8_11),
 
@@ -5589,7 +5589,7 @@ static const WMEGameDescription gameDescriptions[] = {
 
 	// Tanya Grotter and the Magical Double Bass (Spanish fanmade)
 	// The same translation file is used for both games
-	WME_WINENTRY("tanya1", "",
+	WME_WINENTRY("tanya1", "Fanmade",
 		WME_ENTRY2s("data.dcp", "035bbdaff078cc4053ecf4b518c0d0fd", 1007507786,
 					"string.tab", "877d03e4befb75eb638af8bc1005df8e", 220659), Common::ES_ESP, ADGF_UNSTABLE, WME_1_8_0),
 
@@ -5604,7 +5604,7 @@ static const WMEGameDescription gameDescriptions[] = {
 
 	// Tanya Grotter and the Disappearing Floor (Spanish fanmade)
 	// The same translation file is used for both games
-	WME_WINENTRY("tanya2", "",
+	WME_WINENTRY("tanya2", "Fanmade",
 		WME_ENTRY2s("data.dcp", "9c15f14990f630177e063da885d03e6d", 936959767,
 					"string.tab", "877d03e4befb75eb638af8bc1005df8e", 220659), Common::ES_ESP, ADGF_UNSTABLE, WME_1_8_3),
 
@@ -5722,7 +5722,7 @@ static const WMEGameDescription gameDescriptions[] = {
 		WME_ENTRY1s("data.dcp", "87d296ef3f46570ed18f000d3885db77", 340264526), Common::RU_RUS, ADGF_UNSTABLE, WME_1_7_2),
 
 	// Fairy Tales About Toshechka and Boshechka (Spanish fanmade)
-	WME_WINENTRY("tib", "",
+	WME_WINENTRY("tib", "Fanmade",
 		WME_ENTRY2s("data.dcp", "87d296ef3f46570ed18f000d3885db77", 340264526,
 					"string.tab", "2b6bad7874220f412d146a3086046490", 59697), Common::ES_ESP, ADGF_UNSTABLE, WME_1_7_2),
 


Commit: c55b2fcab46a18da003faa6e9ea62783200f5c3d
    https://github.com/scummvm/scummvm/commit/c55b2fcab46a18da003faa6e9ea62783200f5c3d
Author: Walter Agazzi (walter.agazzi at protonmail.com)
Date: 2026-06-16T15:31:48+02:00

Commit Message:
WINTERMUTE: set sotv1 as stable

Game is completable

Changed paths:
    engines/wintermute/detection_tables.h


diff --git a/engines/wintermute/detection_tables.h b/engines/wintermute/detection_tables.h
index 45fcef349a7..65dda1b6c2b 100644
--- a/engines/wintermute/detection_tables.h
+++ b/engines/wintermute/detection_tables.h
@@ -4460,397 +4460,397 @@ static const WMEGameDescription gameDescriptions[] = {
 	WME_WINENTRY("sotv1", "Legacy Version/Steam",
 		WME_ENTRY3s("data.dcp", "e19b63c6aa19d491a4f533ac9c1609ef", 97061505,
 					"data_sd.dcp", "aee08a5a713c1b45e67471134772f72f", 97833631,
-					"i18n_de_strings.dcp", "9cc009980d018476b177e2a4075f56b4", 43104), Common::DE_DEU, ADGF_UNSTABLE | GF_IGNORE_HD_FILES | GF_3D, WME_1_9_3),
+					"i18n_de_strings.dcp", "9cc009980d018476b177e2a4075f56b4", 43104), Common::DE_DEU, GF_IGNORE_HD_FILES | GF_3D, WME_1_9_3),
 
 	// Shadows on the Vatican - Act I: Greed (Legacy Version) (Steam, Jul 2014) (English)
 	WME_WINENTRY("sotv1", "Legacy Version/Steam",
 		WME_ENTRY3s("data.dcp", "e19b63c6aa19d491a4f533ac9c1609ef", 97061505,
 					"data_sd.dcp", "aee08a5a713c1b45e67471134772f72f", 97833631,
-					"i18n_en_strings.dcp", "40f8ea49a3e8d54a5202aa88c12fba80", 75108), Common::EN_ANY, ADGF_UNSTABLE | GF_IGNORE_HD_FILES | GF_3D, WME_1_9_3),
+					"i18n_en_strings.dcp", "40f8ea49a3e8d54a5202aa88c12fba80", 75108), Common::EN_ANY, GF_IGNORE_HD_FILES | GF_3D, WME_1_9_3),
 
 	// Shadows on the Vatican - Act I: Greed (Legacy Version) (Steam, Jul 2014) (Spanish)
 	WME_WINENTRY("sotv1", "Legacy Version/Steam",
 		WME_ENTRY3s("data.dcp", "e19b63c6aa19d491a4f533ac9c1609ef", 97061505,
 					"data_sd.dcp", "aee08a5a713c1b45e67471134772f72f", 97833631,
-					"i18n_es_strings.dcp", "6a8d5b434dfe758abb2ace1a75a4dab1", 40122), Common::ES_ESP, ADGF_UNSTABLE | GF_IGNORE_HD_FILES | GF_3D, WME_1_9_3),
+					"i18n_es_strings.dcp", "6a8d5b434dfe758abb2ace1a75a4dab1", 40122), Common::ES_ESP, GF_IGNORE_HD_FILES | GF_3D, WME_1_9_3),
 
 	// Shadows on the Vatican - Act I: Greed (Legacy Version) (Steam, Jul 2014) (French)
 	WME_WINENTRY("sotv1", "Legacy Version/Steam",
 		WME_ENTRY3s("data.dcp", "e19b63c6aa19d491a4f533ac9c1609ef", 97061505,
 					"data_sd.dcp", "aee08a5a713c1b45e67471134772f72f", 97833631,
-					"i18n_fr_strings.dcp", "b3fe8c720bb6a5378f6da2f593339c70", 40760), Common::FR_FRA, ADGF_UNSTABLE | GF_IGNORE_HD_FILES | GF_3D, WME_1_9_3),
+					"i18n_fr_strings.dcp", "b3fe8c720bb6a5378f6da2f593339c70", 40760), Common::FR_FRA, GF_IGNORE_HD_FILES | GF_3D, WME_1_9_3),
 
 	// Shadows on the Vatican - Act I: Greed (Legacy Version) (Steam, Jul 2014) (Italian)
 	WME_WINENTRY("sotv1", "Legacy Version/Steam",
 		WME_ENTRY3s("data.dcp", "e19b63c6aa19d491a4f533ac9c1609ef", 97061505,
 					"data_sd.dcp", "aee08a5a713c1b45e67471134772f72f", 97833631,
-					"i18n_it_strings.dcp", "5abaab4d57d7585b3c4b23f34f2d0dd8", 40468), Common::IT_ITA, ADGF_UNSTABLE | GF_IGNORE_HD_FILES | GF_3D, WME_1_9_3),
+					"i18n_it_strings.dcp", "5abaab4d57d7585b3c4b23f34f2d0dd8", 40468), Common::IT_ITA, GF_IGNORE_HD_FILES | GF_3D, WME_1_9_3),
 
 	// Shadows on the Vatican - Act I: Greed (Legacy Version) (Steam, Feb 12th 2015) (German)
 	WME_WINENTRY("sotv1", "Legacy Version/Steam",
 		WME_ENTRY3s("data.dcp", "e382f34f0bd319ad310aba2ecd239dec", 95879376,
 					"data_sd.dcp", "6842ddf537f2cd186df050aa8c05d363", 97835480,
-					"i18n_de_strings.dcp", "780b0d8d4f2b2b32e729090c1018df43", 43136), Common::DE_DEU, ADGF_UNSTABLE | GF_IGNORE_HD_FILES | GF_3D, WME_1_9_3),
+					"i18n_de_strings.dcp", "780b0d8d4f2b2b32e729090c1018df43", 43136), Common::DE_DEU, GF_IGNORE_HD_FILES | GF_3D, WME_1_9_3),
 
 	// Shadows on the Vatican - Act I: Greed (Legacy Version) (Steam, Feb 12th 2015) (English)
 	WME_WINENTRY("sotv1", "Legacy Version/Steam",
 		WME_ENTRY3s("data.dcp", "e382f34f0bd319ad310aba2ecd239dec", 95879376,
 					"data_sd.dcp", "6842ddf537f2cd186df050aa8c05d363", 97835480,
-					"i18n_en_strings.dcp", "636e32cf89f02fca30a6f4caa38dede1", 75126), Common::EN_ANY, ADGF_UNSTABLE | GF_IGNORE_HD_FILES | GF_3D, WME_1_9_3),
+					"i18n_en_strings.dcp", "636e32cf89f02fca30a6f4caa38dede1", 75126), Common::EN_ANY, GF_IGNORE_HD_FILES | GF_3D, WME_1_9_3),
 
 	// Shadows on the Vatican - Act I: Greed (Legacy Version) (Steam, Feb 12th 2015) (Spanish)
 	WME_WINENTRY("sotv1", "Legacy Version/Steam",
 		WME_ENTRY3s("data.dcp", "e382f34f0bd319ad310aba2ecd239dec", 95879376,
 					"data_sd.dcp", "6842ddf537f2cd186df050aa8c05d363", 97835480,
-					"i18n_es_strings.dcp", "c62f94e9cd543ecbdc0f02acc744cd29", 40153), Common::ES_ESP, ADGF_UNSTABLE | GF_IGNORE_HD_FILES | GF_3D, WME_1_9_3),
+					"i18n_es_strings.dcp", "c62f94e9cd543ecbdc0f02acc744cd29", 40153), Common::ES_ESP, GF_IGNORE_HD_FILES | GF_3D, WME_1_9_3),
 
 	// Shadows on the Vatican - Act I: Greed (Legacy Version) (Steam, Feb 12th 2015) (French)
 	WME_WINENTRY("sotv1", "Legacy Version/Steam",
 		WME_ENTRY3s("data.dcp", "e382f34f0bd319ad310aba2ecd239dec", 95879376,
 					"data_sd.dcp", "6842ddf537f2cd186df050aa8c05d363", 97835480,
-					"i18n_fr_strings.dcp", "548e88d67be123bb54f5b265226f051a", 40807), Common::FR_FRA, ADGF_UNSTABLE | GF_IGNORE_HD_FILES | GF_3D, WME_1_9_3),
+					"i18n_fr_strings.dcp", "548e88d67be123bb54f5b265226f051a", 40807), Common::FR_FRA, GF_IGNORE_HD_FILES | GF_3D, WME_1_9_3),
 
 	// Shadows on the Vatican - Act I: Greed (Legacy Version) (Steam, Feb 12th 2015) (Italian)
 	WME_WINENTRY("sotv1", "Legacy Version/Steam",
 		WME_ENTRY3s("data.dcp", "e382f34f0bd319ad310aba2ecd239dec", 95879376,
 					"data_sd.dcp", "6842ddf537f2cd186df050aa8c05d363", 97835480,
-					"i18n_it_strings.dcp", "c3d180dd634705cb16ccd650066e1da8", 40502), Common::IT_ITA, ADGF_UNSTABLE | GF_IGNORE_HD_FILES | GF_3D, WME_1_9_3),
+					"i18n_it_strings.dcp", "c3d180dd634705cb16ccd650066e1da8", 40502), Common::IT_ITA, GF_IGNORE_HD_FILES | GF_3D, WME_1_9_3),
 
 	// Shadows on the Vatican - Act I: Greed (Legacy Version) (Steam, Feb 12th 2015) (Polish)
 	WME_WINENTRY("sotv1", "Legacy Version/Steam",
 		WME_ENTRY3s("data.dcp", "e382f34f0bd319ad310aba2ecd239dec", 95879376,
 					"data_sd.dcp", "6842ddf537f2cd186df050aa8c05d363", 97835480,
-					"i18n_pl_strings.dcp", "8dac09efb73ae4a7a2b897de6f37e906", 40062), Common::PL_POL, ADGF_UNSTABLE | GF_IGNORE_HD_FILES | GF_3D, WME_1_9_3),
+					"i18n_pl_strings.dcp", "8dac09efb73ae4a7a2b897de6f37e906", 40062), Common::PL_POL, GF_IGNORE_HD_FILES | GF_3D, WME_1_9_3),
 
 	// Shadows on the Vatican - Act I: Greed (Legacy Version) (Steam, Feb 12th 2015) (Russian)
 	WME_WINENTRY("sotv1", "Legacy Version/Steam",
 		WME_ENTRY3s("data.dcp", "e382f34f0bd319ad310aba2ecd239dec", 95879376,
 					"data_sd.dcp", "6842ddf537f2cd186df050aa8c05d363", 97835480,
-					"i18n_ru_strings.dcp", "88daa5c022c18535e2da86fa558db792", 45838), Common::RU_RUS, ADGF_UNSTABLE | GF_IGNORE_HD_FILES | GF_3D, WME_1_9_3),
+					"i18n_ru_strings.dcp", "88daa5c022c18535e2da86fa558db792", 45838), Common::RU_RUS, GF_IGNORE_HD_FILES | GF_3D, WME_1_9_3),
 
 	// Shadows on the Vatican - Act I: Greed (Legacy Version) (Steam, Feb 22th 2015) (German)
 	WME_WINENTRY("sotv1", "Legacy Version/Steam",
 		WME_ENTRY3s("data.dcp", "bb6136d76ebadf06c90877283aa1d55a", 95890682,
 					"data_sd.dcp", "6135b62bb28434c1af42de84ef8b96fe", 97808411,
-					"i18n_de_strings.dcp", "d245d247a282cda33b83ed6918b2e6d5", 43136), Common::DE_DEU, ADGF_UNSTABLE | GF_IGNORE_HD_FILES | GF_3D, WME_1_9_3),
+					"i18n_de_strings.dcp", "d245d247a282cda33b83ed6918b2e6d5", 43136), Common::DE_DEU, GF_IGNORE_HD_FILES | GF_3D, WME_1_9_3),
 
 	// Shadows on the Vatican - Act I: Greed (Legacy Version) (Steam, Feb 22th 2015) (English)
 	WME_WINENTRY("sotv1", "Legacy Version/Steam",
 		WME_ENTRY3s("data.dcp", "bb6136d76ebadf06c90877283aa1d55a", 95890682,
 					"data_sd.dcp", "6135b62bb28434c1af42de84ef8b96fe", 97808411,
-					"i18n_en_strings.dcp", "5e9cf5a8403b98f7d92de55efccc0d34", 75119), Common::EN_ANY, ADGF_UNSTABLE | GF_IGNORE_HD_FILES | GF_3D, WME_1_9_3),
+					"i18n_en_strings.dcp", "5e9cf5a8403b98f7d92de55efccc0d34", 75119), Common::EN_ANY, GF_IGNORE_HD_FILES | GF_3D, WME_1_9_3),
 
 	// Shadows on the Vatican - Act I: Greed (Legacy Version) (Steam, Feb 22th 2015) (Spanish)
 	WME_WINENTRY("sotv1", "Legacy Version/Steam",
 		WME_ENTRY3s("data.dcp", "bb6136d76ebadf06c90877283aa1d55a", 95890682,
 					"data_sd.dcp", "6135b62bb28434c1af42de84ef8b96fe", 97808411,
-					"i18n_es_strings.dcp", "edb19a0758243da8929844bac035d384", 40153), Common::ES_ESP, ADGF_UNSTABLE | GF_IGNORE_HD_FILES | GF_3D, WME_1_9_3),
+					"i18n_es_strings.dcp", "edb19a0758243da8929844bac035d384", 40153), Common::ES_ESP, GF_IGNORE_HD_FILES | GF_3D, WME_1_9_3),
 
 	// Shadows on the Vatican - Act I: Greed (Legacy Version) (Steam, Feb 22th 2015) (French)
 	WME_WINENTRY("sotv1", "Legacy Version/Steam",
 		WME_ENTRY3s("data.dcp", "bb6136d76ebadf06c90877283aa1d55a", 95890682,
 					"data_sd.dcp", "6135b62bb28434c1af42de84ef8b96fe", 97808411,
-					"i18n_fr_strings.dcp", "40dda8156fa93717cf962e75d76929b1", 40807), Common::FR_FRA, ADGF_UNSTABLE | GF_IGNORE_HD_FILES | GF_3D, WME_1_9_3),
+					"i18n_fr_strings.dcp", "40dda8156fa93717cf962e75d76929b1", 40807), Common::FR_FRA, GF_IGNORE_HD_FILES | GF_3D, WME_1_9_3),
 
 	// Shadows on the Vatican - Act I: Greed (Legacy Version) (Steam, Feb 22th 2015) (Italian)
 	WME_WINENTRY("sotv1", "Legacy Version/Steam",
 		WME_ENTRY3s("data.dcp", "bb6136d76ebadf06c90877283aa1d55a", 95890682,
 					"data_sd.dcp", "6135b62bb28434c1af42de84ef8b96fe", 97808411,
-					"i18n_it_strings.dcp", "0d1dac14379e55356794fb7cca8865b2", 40502), Common::IT_ITA, ADGF_UNSTABLE | GF_IGNORE_HD_FILES | GF_3D, WME_1_9_3),
+					"i18n_it_strings.dcp", "0d1dac14379e55356794fb7cca8865b2", 40502), Common::IT_ITA, GF_IGNORE_HD_FILES | GF_3D, WME_1_9_3),
 
 	// Shadows on the Vatican - Act I: Greed (Legacy Version) (Steam, Feb 22th 2015) (Polish)
 	WME_WINENTRY("sotv1", "Legacy Version/Steam",
 		WME_ENTRY3s("data.dcp", "bb6136d76ebadf06c90877283aa1d55a", 95890682,
 					"data_sd.dcp", "6135b62bb28434c1af42de84ef8b96fe", 97808411,
-					"i18n_pl_strings.dcp", "cdd0b203c591dfb411cb6fc89ac009c1", 40062), Common::PL_POL, ADGF_UNSTABLE | GF_IGNORE_HD_FILES | GF_3D, WME_1_9_3),
+					"i18n_pl_strings.dcp", "cdd0b203c591dfb411cb6fc89ac009c1", 40062), Common::PL_POL, GF_IGNORE_HD_FILES | GF_3D, WME_1_9_3),
 
 	// Shadows on the Vatican - Act I: Greed (Legacy Version) (Steam, Feb 22th 2015) (Russian)
 	WME_WINENTRY("sotv1", "Legacy Version/Steam",
 		WME_ENTRY3s("data.dcp", "bb6136d76ebadf06c90877283aa1d55a", 95890682,
 					"data_sd.dcp", "6135b62bb28434c1af42de84ef8b96fe", 97808411,
-					"i18n_ru_strings.dcp", "77fc889bb25438bafe897d1566bd7e50", 45546), Common::RU_RUS, ADGF_UNSTABLE | GF_IGNORE_HD_FILES | GF_3D, WME_1_9_3),
+					"i18n_ru_strings.dcp", "77fc889bb25438bafe897d1566bd7e50", 45546), Common::RU_RUS, GF_IGNORE_HD_FILES | GF_3D, WME_1_9_3),
 
 	// Shadows on the Vatican - Act I: Greed (Legacy Version) (Steam, Sep 2016) (German)
 	WME_WINENTRY("sotv1", "Legacy Version/Steam",
 		WME_ENTRY3s("data.dcp", "46bb5822abf6d422d08a68070e05bd86", 95890675,
 					"data_sd.dcp", "8864e2e552bb7816916d8c6630e8f1a5", 97821007,
-					"i18n_de_strings.dcp", "94230807d77dacb420f446c34dd60072", 43136), Common::DE_DEU, ADGF_UNSTABLE | GF_IGNORE_HD_FILES | GF_3D, WME_1_9_3),
+					"i18n_de_strings.dcp", "94230807d77dacb420f446c34dd60072", 43136), Common::DE_DEU, GF_IGNORE_HD_FILES | GF_3D, WME_1_9_3),
 
 	// Shadows on the Vatican - Act I: Greed (Legacy Version) (Steam, Sep 2016) (English)
 	WME_WINENTRY("sotv1", "Legacy Version/Steam",
 		WME_ENTRY3s("data.dcp", "46bb5822abf6d422d08a68070e05bd86", 95890675,
 					"data_sd.dcp", "8864e2e552bb7816916d8c6630e8f1a5", 97821007,
-					"i18n_en_strings.dcp", "253e7f5e2bb4a33c7df52a04624d18c6", 75119), Common::EN_ANY, ADGF_UNSTABLE | GF_IGNORE_HD_FILES | GF_3D, WME_1_9_3),
+					"i18n_en_strings.dcp", "253e7f5e2bb4a33c7df52a04624d18c6", 75119), Common::EN_ANY, GF_IGNORE_HD_FILES | GF_3D, WME_1_9_3),
 
 	// Shadows on the Vatican - Act I: Greed (Legacy Version) (Steam, Sep 2016) (Spanish)
 	WME_WINENTRY("sotv1", "Legacy Version/Steam",
 		WME_ENTRY3s("data.dcp", "46bb5822abf6d422d08a68070e05bd86", 95890675,
 					"data_sd.dcp", "8864e2e552bb7816916d8c6630e8f1a5", 97821007,
-					"i18n_es_strings.dcp", "2227cee67309f564178e48d3eb30fc98", 40153), Common::ES_ESP, ADGF_UNSTABLE | GF_IGNORE_HD_FILES | GF_3D, WME_1_9_3),
+					"i18n_es_strings.dcp", "2227cee67309f564178e48d3eb30fc98", 40153), Common::ES_ESP, GF_IGNORE_HD_FILES | GF_3D, WME_1_9_3),
 
 	// Shadows on the Vatican - Act I: Greed (Legacy Version) (Steam, Sep 2016) (French)
 	WME_WINENTRY("sotv1", "Legacy Version/Steam",
 		WME_ENTRY3s("data.dcp", "46bb5822abf6d422d08a68070e05bd86", 95890675,
 					"data_sd.dcp", "8864e2e552bb7816916d8c6630e8f1a5", 97821007,
-					"i18n_fr_strings.dcp", "f50d251df43e5433b9664d5c2463fb08", 40807), Common::FR_FRA, ADGF_UNSTABLE | GF_IGNORE_HD_FILES | GF_3D, WME_1_9_3),
+					"i18n_fr_strings.dcp", "f50d251df43e5433b9664d5c2463fb08", 40807), Common::FR_FRA, GF_IGNORE_HD_FILES | GF_3D, WME_1_9_3),
 
 	// Shadows on the Vatican - Act I: Greed (Legacy Version) (Steam, Sep 2016) (Italian)
 	WME_WINENTRY("sotv1", "Legacy Version/Steam",
 		WME_ENTRY3s("data.dcp", "46bb5822abf6d422d08a68070e05bd86", 95890675,
 					"data_sd.dcp", "8864e2e552bb7816916d8c6630e8f1a5", 97821007,
-					"i18n_it_strings.dcp", "6ad0d7c0a0c450d6af334cd1b4dbe72e", 40502), Common::IT_ITA, ADGF_UNSTABLE | GF_IGNORE_HD_FILES | GF_3D, WME_1_9_3),
+					"i18n_it_strings.dcp", "6ad0d7c0a0c450d6af334cd1b4dbe72e", 40502), Common::IT_ITA, GF_IGNORE_HD_FILES | GF_3D, WME_1_9_3),
 
 	// Shadows on the Vatican - Act I: Greed (Legacy Version) (Steam, Sep 2016) (Polish)
 	WME_WINENTRY("sotv1", "Legacy Version/Steam",
 		WME_ENTRY3s("data.dcp", "46bb5822abf6d422d08a68070e05bd86", 95890675,
 					"data_sd.dcp", "8864e2e552bb7816916d8c6630e8f1a5", 97821007,
-					"i18n_pl_strings.dcp", "7c07fb021517dd21ba2e2a5739d0168e", 40062), Common::PL_POL, ADGF_UNSTABLE | GF_IGNORE_HD_FILES | GF_3D, WME_1_9_3),
+					"i18n_pl_strings.dcp", "7c07fb021517dd21ba2e2a5739d0168e", 40062), Common::PL_POL, GF_IGNORE_HD_FILES | GF_3D, WME_1_9_3),
 
 	// Shadows on the Vatican - Act I: Greed (Legacy Version) (Steam, Sep 2016) (Russian)
 	WME_WINENTRY("sotv1", "Legacy Version/Steam",
 		WME_ENTRY3s("data.dcp", "46bb5822abf6d422d08a68070e05bd86", 95890675,
 					"data_sd.dcp", "8864e2e552bb7816916d8c6630e8f1a5", 97821007,
-					"i18n_ru_strings.dcp", "5cecc876ec1b364f22780d18a0821349", 45546), Common::RU_RUS, ADGF_UNSTABLE | GF_IGNORE_HD_FILES | GF_3D, WME_1_9_3),
+					"i18n_ru_strings.dcp", "5cecc876ec1b364f22780d18a0821349", 45546), Common::RU_RUS, GF_IGNORE_HD_FILES | GF_3D, WME_1_9_3),
 
 	// Shadows on the Vatican - Act I: Greed (Legacy Version) (Steam, Jun 2018) (German)
 	WME_WINENTRY("sotv1", "Legacy Version/Steam",
 		WME_ENTRY3s("data.dcp", "e0d5b1276cf80c858b7404f1f3381e2b", 95890675,
 					"data_sd.dcp", "551feca25b9e0ac9d467c105efb373e8", 97979104,
-					"i18n_de_strings.dcp", "59d15bb3c32354b5104475df0ff6c50b", 43136), Common::DE_DEU, ADGF_UNSTABLE | GF_IGNORE_HD_FILES | GF_3D, WME_1_9_3),
+					"i18n_de_strings.dcp", "59d15bb3c32354b5104475df0ff6c50b", 43136), Common::DE_DEU, GF_IGNORE_HD_FILES | GF_3D, WME_1_9_3),
 
 	// Shadows on the Vatican - Act I: Greed (Legacy Version) (Steam, Jun 2018) (English)
 	WME_WINENTRY("sotv1", "Legacy Version/Steam",
 		WME_ENTRY3s("data.dcp", "e0d5b1276cf80c858b7404f1f3381e2b", 95890675,
 					"data_sd.dcp", "551feca25b9e0ac9d467c105efb373e8", 97979104,
-					"i18n_en_strings.dcp", "733a53213ab5be27941c34dcef531e0e", 75119), Common::EN_ANY, ADGF_UNSTABLE | GF_IGNORE_HD_FILES | GF_3D, WME_1_9_3),
+					"i18n_en_strings.dcp", "733a53213ab5be27941c34dcef531e0e", 75119), Common::EN_ANY, GF_IGNORE_HD_FILES | GF_3D, WME_1_9_3),
 
 	// Shadows on the Vatican - Act I: Greed (Legacy Version) (Steam, Jun 2018) (Spanish)
 	WME_WINENTRY("sotv1", "Legacy Version/Steam",
 		WME_ENTRY3s("data.dcp", "e0d5b1276cf80c858b7404f1f3381e2b", 95890675,
 					"data_sd.dcp", "551feca25b9e0ac9d467c105efb373e8", 97979104,
-					"i18n_es_strings.dcp", "18876a252741b8bda888805ca860f600", 40153), Common::ES_ESP, ADGF_UNSTABLE | GF_IGNORE_HD_FILES | GF_3D, WME_1_9_3),
+					"i18n_es_strings.dcp", "18876a252741b8bda888805ca860f600", 40153), Common::ES_ESP, GF_IGNORE_HD_FILES | GF_3D, WME_1_9_3),
 
 	// Shadows on the Vatican - Act I: Greed (Legacy Version) (Steam, Jun 2018) (French)
 	WME_WINENTRY("sotv1", "Legacy Version/Steam",
 		WME_ENTRY3s("data.dcp", "e0d5b1276cf80c858b7404f1f3381e2b", 95890675,
 					"data_sd.dcp", "551feca25b9e0ac9d467c105efb373e8", 97979104,
-					"i18n_fr_strings.dcp", "850cce9217d83785e4b56028441b0d71", 40807), Common::FR_FRA, ADGF_UNSTABLE | GF_IGNORE_HD_FILES | GF_3D, WME_1_9_3),
+					"i18n_fr_strings.dcp", "850cce9217d83785e4b56028441b0d71", 40807), Common::FR_FRA, GF_IGNORE_HD_FILES | GF_3D, WME_1_9_3),
 
 	// Shadows on the Vatican - Act I: Greed (Legacy Version) (Steam, Jun 2018) (Italian)
 	WME_WINENTRY("sotv1", "Legacy Version/Steam",
 		WME_ENTRY3s("data.dcp", "e0d5b1276cf80c858b7404f1f3381e2b", 95890675,
 					"data_sd.dcp", "551feca25b9e0ac9d467c105efb373e8", 97979104,
-					"i18n_it_strings.dcp", "de567598fba43589841c354306a80377", 40502), Common::IT_ITA, ADGF_UNSTABLE | GF_IGNORE_HD_FILES | GF_3D, WME_1_9_3),
+					"i18n_it_strings.dcp", "de567598fba43589841c354306a80377", 40502), Common::IT_ITA, GF_IGNORE_HD_FILES | GF_3D, WME_1_9_3),
 
 	// Shadows on the Vatican - Act I: Greed (Legacy Version) (Steam, Jun 2018) (Polish)
 	WME_WINENTRY("sotv1", "Legacy Version/Steam",
 		WME_ENTRY3s("data.dcp", "e0d5b1276cf80c858b7404f1f3381e2b", 95890675,
 					"data_sd.dcp", "551feca25b9e0ac9d467c105efb373e8", 97979104,
-					"i18n_pl_strings.dcp", "daa4b251caec6c6e2c3b96aaf87ae33a", 40062), Common::PL_POL, ADGF_UNSTABLE | GF_IGNORE_HD_FILES | GF_3D, WME_1_9_3),
+					"i18n_pl_strings.dcp", "daa4b251caec6c6e2c3b96aaf87ae33a", 40062), Common::PL_POL, GF_IGNORE_HD_FILES | GF_3D, WME_1_9_3),
 
 	// Shadows on the Vatican - Act I: Greed (Legacy Version) (Steam, Jun 2018) (Russian)
 	WME_WINENTRY("sotv1", "Legacy Version/Steam",
 		WME_ENTRY3s("data.dcp", "e0d5b1276cf80c858b7404f1f3381e2b", 95890675,
 					"data_sd.dcp", "551feca25b9e0ac9d467c105efb373e8", 97979104,
-					"i18n_ru_strings.dcp", "ebf28ca1475b3be9754a323966807683", 45546), Common::RU_RUS, ADGF_UNSTABLE | GF_IGNORE_HD_FILES | GF_3D, WME_1_9_3),
+					"i18n_ru_strings.dcp", "ebf28ca1475b3be9754a323966807683", 45546), Common::RU_RUS, GF_IGNORE_HD_FILES | GF_3D, WME_1_9_3),
 
 	// Shadows on the Vatican - Act I: Greed (Full HD Version) (Steam, Jul 2014) (German)
 	WME_WINENTRY("sotv1", "Full HD Version/Steam",
 		WME_ENTRY3s("data.dcp", "e19b63c6aa19d491a4f533ac9c1609ef", 97061505,
 					"data_hd.dcp", "d8d903cbda2ff7001cc7ce949775897e", 197016744,
-					"i18n_de_strings.dcp", "9cc009980d018476b177e2a4075f56b4", 43104), Common::DE_DEU, ADGF_UNSTABLE | GF_IGNORE_SD_FILES | GF_3D, WME_1_9_3),
+					"i18n_de_strings.dcp", "9cc009980d018476b177e2a4075f56b4", 43104), Common::DE_DEU, GF_IGNORE_SD_FILES | GF_3D, WME_1_9_3),
 
 	// Shadows on the Vatican - Act I: Greed (Full HD Version) (Steam, Jul 2014) (English)
 	WME_WINENTRY("sotv1", "Full HD Version/Steam",
 		WME_ENTRY3s("data.dcp", "e19b63c6aa19d491a4f533ac9c1609ef", 97061505,
 					"data_hd.dcp", "d8d903cbda2ff7001cc7ce949775897e", 197016744,
-					"i18n_en_strings.dcp", "40f8ea49a3e8d54a5202aa88c12fba80", 75108), Common::EN_ANY, ADGF_UNSTABLE | GF_IGNORE_SD_FILES | GF_3D, WME_1_9_3),
+					"i18n_en_strings.dcp", "40f8ea49a3e8d54a5202aa88c12fba80", 75108), Common::EN_ANY, GF_IGNORE_SD_FILES | GF_3D, WME_1_9_3),
 
 	// Shadows on the Vatican - Act I: Greed (Full HD Version) (Steam, Jul 2014) (Spanish)
 	WME_WINENTRY("sotv1", "Full HD Version/Steam",
 		WME_ENTRY3s("data.dcp", "e19b63c6aa19d491a4f533ac9c1609ef", 97061505,
 					"data_hd.dcp", "d8d903cbda2ff7001cc7ce949775897e", 197016744,
-					"i18n_es_strings.dcp", "6a8d5b434dfe758abb2ace1a75a4dab1", 40122), Common::ES_ESP, ADGF_UNSTABLE | GF_IGNORE_SD_FILES | GF_3D, WME_1_9_3),
+					"i18n_es_strings.dcp", "6a8d5b434dfe758abb2ace1a75a4dab1", 40122), Common::ES_ESP, GF_IGNORE_SD_FILES | GF_3D, WME_1_9_3),
 
 	// Shadows on the Vatican - Act I: Greed (Full HD Version) (Steam, Jul 2014) (French)
 	WME_WINENTRY("sotv1", "Full HD Version/Steam",
 		WME_ENTRY3s("data.dcp", "e19b63c6aa19d491a4f533ac9c1609ef", 97061505,
 					"data_hd.dcp", "d8d903cbda2ff7001cc7ce949775897e", 197016744,
-					"i18n_fr_strings.dcp", "b3fe8c720bb6a5378f6da2f593339c70", 40760), Common::FR_FRA, ADGF_UNSTABLE | GF_IGNORE_SD_FILES | GF_3D, WME_1_9_3),
+					"i18n_fr_strings.dcp", "b3fe8c720bb6a5378f6da2f593339c70", 40760), Common::FR_FRA, GF_IGNORE_SD_FILES | GF_3D, WME_1_9_3),
 
 	// Shadows on the Vatican - Act I: Greed (Full HD Version) (Steam, Jul 2014) (Italian)
 	WME_WINENTRY("sotv1", "Full HD Version/Steam",
 		WME_ENTRY3s("data.dcp", "e19b63c6aa19d491a4f533ac9c1609ef", 97061505,
 					"data_hd.dcp", "d8d903cbda2ff7001cc7ce949775897e", 197016744,
-					"i18n_it_strings.dcp", "5abaab4d57d7585b3c4b23f34f2d0dd8", 40468), Common::IT_ITA, ADGF_UNSTABLE | GF_IGNORE_SD_FILES | GF_3D, WME_1_9_3),
+					"i18n_it_strings.dcp", "5abaab4d57d7585b3c4b23f34f2d0dd8", 40468), Common::IT_ITA, GF_IGNORE_SD_FILES | GF_3D, WME_1_9_3),
 
 	// Shadows on the Vatican - Act I: Greed (Full HD Version) (Steam, Feb 12th 2015) (German)
 	WME_WINENTRY("sotv1", "Full HD Version/Steam",
 		WME_ENTRY3s("data.dcp", "e382f34f0bd319ad310aba2ecd239dec", 95879376,
 					"data_hd.dcp", "1a077e884c659f34da61dd205591f83d", 197018612,
-					"i18n_de_strings.dcp", "780b0d8d4f2b2b32e729090c1018df43", 43136), Common::DE_DEU, ADGF_UNSTABLE | GF_IGNORE_SD_FILES | GF_3D, WME_1_9_3),
+					"i18n_de_strings.dcp", "780b0d8d4f2b2b32e729090c1018df43", 43136), Common::DE_DEU, GF_IGNORE_SD_FILES | GF_3D, WME_1_9_3),
 
 	// Shadows on the Vatican - Act I: Greed (Full HD Version) (Steam, Feb 12th 2015) (English)
 	WME_WINENTRY("sotv1", "Full HD Version/Steam",
 		WME_ENTRY3s("data.dcp", "e382f34f0bd319ad310aba2ecd239dec", 95879376,
 					"data_hd.dcp", "1a077e884c659f34da61dd205591f83d", 197018612,
-					"i18n_en_strings.dcp", "636e32cf89f02fca30a6f4caa38dede1", 75126), Common::EN_ANY, ADGF_UNSTABLE | GF_IGNORE_SD_FILES | GF_3D, WME_1_9_3),
+					"i18n_en_strings.dcp", "636e32cf89f02fca30a6f4caa38dede1", 75126), Common::EN_ANY, GF_IGNORE_SD_FILES | GF_3D, WME_1_9_3),
 
 	// Shadows on the Vatican - Act I: Greed (Full HD Version) (Steam, Feb 12th 2015) (Spanish)
 	WME_WINENTRY("sotv1", "Full HD Version/Steam",
 		WME_ENTRY3s("data.dcp", "e382f34f0bd319ad310aba2ecd239dec", 95879376,
 					"data_hd.dcp", "1a077e884c659f34da61dd205591f83d", 197018612,
-					"i18n_es_strings.dcp", "c62f94e9cd543ecbdc0f02acc744cd29", 40153), Common::ES_ESP, ADGF_UNSTABLE | GF_IGNORE_SD_FILES | GF_3D, WME_1_9_3),
+					"i18n_es_strings.dcp", "c62f94e9cd543ecbdc0f02acc744cd29", 40153), Common::ES_ESP, GF_IGNORE_SD_FILES | GF_3D, WME_1_9_3),
 
 	// Shadows on the Vatican - Act I: Greed (Full HD Version) (Steam, Feb 12th 2015) (French)
 	WME_WINENTRY("sotv1", "Full HD Version/Steam",
 		WME_ENTRY3s("data.dcp", "e382f34f0bd319ad310aba2ecd239dec", 95879376,
 					"data_hd.dcp", "1a077e884c659f34da61dd205591f83d", 197018612,
-					"i18n_fr_strings.dcp", "548e88d67be123bb54f5b265226f051a", 40807), Common::FR_FRA, ADGF_UNSTABLE | GF_IGNORE_SD_FILES | GF_3D, WME_1_9_3),
+					"i18n_fr_strings.dcp", "548e88d67be123bb54f5b265226f051a", 40807), Common::FR_FRA, GF_IGNORE_SD_FILES | GF_3D, WME_1_9_3),
 
 	// Shadows on the Vatican - Act I: Greed (Full HD Version) (Steam, Feb 12th 2015) (Italian)
 	WME_WINENTRY("sotv1", "Full HD Version/Steam",
 		WME_ENTRY3s("data.dcp", "e382f34f0bd319ad310aba2ecd239dec", 95879376,
 					"data_hd.dcp", "1a077e884c659f34da61dd205591f83d", 197018612,
-					"i18n_it_strings.dcp", "c3d180dd634705cb16ccd650066e1da8", 40502), Common::IT_ITA, ADGF_UNSTABLE | GF_IGNORE_SD_FILES | GF_3D, WME_1_9_3),
+					"i18n_it_strings.dcp", "c3d180dd634705cb16ccd650066e1da8", 40502), Common::IT_ITA, GF_IGNORE_SD_FILES | GF_3D, WME_1_9_3),
 
 	// Shadows on the Vatican - Act I: Greed (Full HD Version) (Steam, Feb 12th 2015) (Polish)
 	WME_WINENTRY("sotv1", "Full HD Version/Steam",
 		WME_ENTRY3s("data.dcp", "e382f34f0bd319ad310aba2ecd239dec", 95879376,
 					"data_hd.dcp", "1a077e884c659f34da61dd205591f83d", 197018612,
-					"i18n_pl_strings.dcp", "8dac09efb73ae4a7a2b897de6f37e906", 40062), Common::PL_POL, ADGF_UNSTABLE | GF_IGNORE_SD_FILES | GF_3D, WME_1_9_3),
+					"i18n_pl_strings.dcp", "8dac09efb73ae4a7a2b897de6f37e906", 40062), Common::PL_POL, GF_IGNORE_SD_FILES | GF_3D, WME_1_9_3),
 
 	// Shadows on the Vatican - Act I: Greed (Full HD Version) (Steam, Feb 12th 2015) (Russian)
 	WME_WINENTRY("sotv1", "Full HD Version/Steam",
 		WME_ENTRY3s("data.dcp", "e382f34f0bd319ad310aba2ecd239dec", 95879376,
 					"data_hd.dcp", "1a077e884c659f34da61dd205591f83d", 197018612,
-					"i18n_ru_strings.dcp", "88daa5c022c18535e2da86fa558db792", 45838), Common::RU_RUS, ADGF_UNSTABLE | GF_IGNORE_SD_FILES | GF_3D, WME_1_9_3),
+					"i18n_ru_strings.dcp", "88daa5c022c18535e2da86fa558db792", 45838), Common::RU_RUS, GF_IGNORE_SD_FILES | GF_3D, WME_1_9_3),
 
 	// Shadows on the Vatican - Act I: Greed (Full HD Version) (Steam, Feb 22th 2015) (German)
 	WME_WINENTRY("sotv1", "Full HD Version/Steam",
 		WME_ENTRY3s("data.dcp", "bb6136d76ebadf06c90877283aa1d55a", 95890682,
 					"data_hd.dcp", "bfd3bc963c073af866e5405d6d5f1347", 197125364,
-					"i18n_de_strings.dcp", "d245d247a282cda33b83ed6918b2e6d5", 43136), Common::DE_DEU, ADGF_UNSTABLE | GF_IGNORE_SD_FILES | GF_3D, WME_1_9_3),
+					"i18n_de_strings.dcp", "d245d247a282cda33b83ed6918b2e6d5", 43136), Common::DE_DEU, GF_IGNORE_SD_FILES | GF_3D, WME_1_9_3),
 
 	// Shadows on the Vatican - Act I: Greed (Full HD Version) (Steam, Feb 22th 2015) (English)
 	WME_WINENTRY("sotv1", "Full HD Version/Steam",
 		WME_ENTRY3s("data.dcp", "bb6136d76ebadf06c90877283aa1d55a", 95890682,
 					"data_hd.dcp", "bfd3bc963c073af866e5405d6d5f1347", 197125364,
-					"i18n_en_strings.dcp", "5e9cf5a8403b98f7d92de55efccc0d34", 75119), Common::EN_ANY, ADGF_UNSTABLE | GF_IGNORE_SD_FILES | GF_3D, WME_1_9_3),
+					"i18n_en_strings.dcp", "5e9cf5a8403b98f7d92de55efccc0d34", 75119), Common::EN_ANY, GF_IGNORE_SD_FILES | GF_3D, WME_1_9_3),
 
 	// Shadows on the Vatican - Act I: Greed (Full HD Version) (Steam, Feb 22th 2015) (Spanish)
 	WME_WINENTRY("sotv1", "Full HD Version/Steam",
 		WME_ENTRY3s("data.dcp", "bb6136d76ebadf06c90877283aa1d55a", 95890682,
 					"data_hd.dcp", "bfd3bc963c073af866e5405d6d5f1347", 197125364,
-					"i18n_es_strings.dcp", "edb19a0758243da8929844bac035d384", 40153), Common::ES_ESP, ADGF_UNSTABLE | GF_IGNORE_SD_FILES | GF_3D, WME_1_9_3),
+					"i18n_es_strings.dcp", "edb19a0758243da8929844bac035d384", 40153), Common::ES_ESP, GF_IGNORE_SD_FILES | GF_3D, WME_1_9_3),
 
 	// Shadows on the Vatican - Act I: Greed (Full HD Version) (Steam, Feb 22th 2015) (French)
 	WME_WINENTRY("sotv1", "Full HD Version/Steam",
 		WME_ENTRY3s("data.dcp", "bb6136d76ebadf06c90877283aa1d55a", 95890682,
 					"data_hd.dcp", "bfd3bc963c073af866e5405d6d5f1347", 197125364,
-					"i18n_fr_strings.dcp", "40dda8156fa93717cf962e75d76929b1", 40807), Common::FR_FRA, ADGF_UNSTABLE | GF_IGNORE_SD_FILES | GF_3D, WME_1_9_3),
+					"i18n_fr_strings.dcp", "40dda8156fa93717cf962e75d76929b1", 40807), Common::FR_FRA, GF_IGNORE_SD_FILES | GF_3D, WME_1_9_3),
 
 	// Shadows on the Vatican - Act I: Greed (Full HD Version) (Steam, Feb 22th 2015) (Italian)
 	WME_WINENTRY("sotv1", "Full HD Version/Steam",
 		WME_ENTRY3s("data.dcp", "bb6136d76ebadf06c90877283aa1d55a", 95890682,
 					"data_hd.dcp", "bfd3bc963c073af866e5405d6d5f1347", 197125364,
-					"i18n_it_strings.dcp", "0d1dac14379e55356794fb7cca8865b2", 40502), Common::IT_ITA, ADGF_UNSTABLE | GF_IGNORE_SD_FILES | GF_3D, WME_1_9_3),
+					"i18n_it_strings.dcp", "0d1dac14379e55356794fb7cca8865b2", 40502), Common::IT_ITA, GF_IGNORE_SD_FILES | GF_3D, WME_1_9_3),
 
 	// Shadows on the Vatican - Act I: Greed (Full HD Version) (Steam, Feb 22th 2015) (Polish)
 	WME_WINENTRY("sotv1", "Full HD Version/Steam",
 		WME_ENTRY3s("data.dcp", "bb6136d76ebadf06c90877283aa1d55a", 95890682,
 					"data_hd.dcp", "bfd3bc963c073af866e5405d6d5f1347", 197125364,
-					"i18n_pl_strings.dcp", "cdd0b203c591dfb411cb6fc89ac009c1", 40062), Common::PL_POL, ADGF_UNSTABLE | GF_IGNORE_SD_FILES | GF_3D, WME_1_9_3),
+					"i18n_pl_strings.dcp", "cdd0b203c591dfb411cb6fc89ac009c1", 40062), Common::PL_POL, GF_IGNORE_SD_FILES | GF_3D, WME_1_9_3),
 
 	// Shadows on the Vatican - Act I: Greed (Full HD Version) (Steam, Feb 22th 2015) (Russian)
 	WME_WINENTRY("sotv1", "Full HD Version/Steam",
 		WME_ENTRY3s("data.dcp", "bb6136d76ebadf06c90877283aa1d55a", 95890682,
 					"data_hd.dcp", "bfd3bc963c073af866e5405d6d5f1347", 197125364,
-					"i18n_ru_strings.dcp", "77fc889bb25438bafe897d1566bd7e50", 45546), Common::RU_RUS, ADGF_UNSTABLE | GF_IGNORE_SD_FILES | GF_3D, WME_1_9_3),
+					"i18n_ru_strings.dcp", "77fc889bb25438bafe897d1566bd7e50", 45546), Common::RU_RUS, GF_IGNORE_SD_FILES | GF_3D, WME_1_9_3),
 
 	// Shadows on the Vatican - Act I: Greed (Full HD Version) (Steam, Sep 2016) (German)
 	WME_WINENTRY("sotv1", "Full HD Version/Steam",
 		WME_ENTRY3s("data.dcp", "46bb5822abf6d422d08a68070e05bd86", 95890675,
 					"data_hd.dcp", "79a5c4ae560817a149506dce894274d0", 197155130,
-					"i18n_de_strings.dcp", "94230807d77dacb420f446c34dd60072", 43136), Common::DE_DEU, ADGF_UNSTABLE | GF_IGNORE_SD_FILES | GF_3D, WME_1_9_3),
+					"i18n_de_strings.dcp", "94230807d77dacb420f446c34dd60072", 43136), Common::DE_DEU, GF_IGNORE_SD_FILES | GF_3D, WME_1_9_3),
 
 	// Shadows on the Vatican - Act I: Greed (Full HD Version) (Steam, Sep 2016) (English)
 	WME_WINENTRY("sotv1", "Full HD Version/Steam",
 		WME_ENTRY3s("data.dcp", "46bb5822abf6d422d08a68070e05bd86", 95890675,
 					"data_hd.dcp", "79a5c4ae560817a149506dce894274d0", 197155130,
-					"i18n_en_strings.dcp", "253e7f5e2bb4a33c7df52a04624d18c6", 75119), Common::EN_ANY, ADGF_UNSTABLE | GF_IGNORE_SD_FILES | GF_3D, WME_1_9_3),
+					"i18n_en_strings.dcp", "253e7f5e2bb4a33c7df52a04624d18c6", 75119), Common::EN_ANY, GF_IGNORE_SD_FILES | GF_3D, WME_1_9_3),
 
 	// Shadows on the Vatican - Act I: Greed (Full HD Version) (Steam, Sep 2016) (Spanish)
 	WME_WINENTRY("sotv1", "Full HD Version/Steam",
 		WME_ENTRY3s("data.dcp", "46bb5822abf6d422d08a68070e05bd86", 95890675,
 					"data_hd.dcp", "79a5c4ae560817a149506dce894274d0", 197155130,
-					"i18n_es_strings.dcp", "2227cee67309f564178e48d3eb30fc98", 40153), Common::ES_ESP, ADGF_UNSTABLE | GF_IGNORE_SD_FILES | GF_3D, WME_1_9_3),
+					"i18n_es_strings.dcp", "2227cee67309f564178e48d3eb30fc98", 40153), Common::ES_ESP, GF_IGNORE_SD_FILES | GF_3D, WME_1_9_3),
 
 	// Shadows on the Vatican - Act I: Greed (Full HD Version) (Steam, Sep 2016) (French)
 	WME_WINENTRY("sotv1", "Full HD Version/Steam",
 		WME_ENTRY3s("data.dcp", "46bb5822abf6d422d08a68070e05bd86", 95890675,
 					"data_hd.dcp", "79a5c4ae560817a149506dce894274d0", 197155130,
-					"i18n_fr_strings.dcp", "f50d251df43e5433b9664d5c2463fb08", 40807), Common::FR_FRA, ADGF_UNSTABLE | GF_IGNORE_SD_FILES | GF_3D, WME_1_9_3),
+					"i18n_fr_strings.dcp", "f50d251df43e5433b9664d5c2463fb08", 40807), Common::FR_FRA, GF_IGNORE_SD_FILES | GF_3D, WME_1_9_3),
 
 	// Shadows on the Vatican - Act I: Greed (Full HD Version) (Steam, Sep 2016) (Italian)
 	WME_WINENTRY("sotv1", "Full HD Version/Steam",
 		WME_ENTRY3s("data.dcp", "46bb5822abf6d422d08a68070e05bd86", 95890675,
 					"data_hd.dcp", "79a5c4ae560817a149506dce894274d0", 197155130,
-					"i18n_it_strings.dcp", "6ad0d7c0a0c450d6af334cd1b4dbe72e", 40502), Common::IT_ITA, ADGF_UNSTABLE | GF_IGNORE_SD_FILES | GF_3D, WME_1_9_3),
+					"i18n_it_strings.dcp", "6ad0d7c0a0c450d6af334cd1b4dbe72e", 40502), Common::IT_ITA, GF_IGNORE_SD_FILES | GF_3D, WME_1_9_3),
 
 	// Shadows on the Vatican - Act I: Greed (Full HD Version) (Steam, Sep 2016) (Polish)
 	WME_WINENTRY("sotv1", "Full HD Version/Steam",
 		WME_ENTRY3s("data.dcp", "46bb5822abf6d422d08a68070e05bd86", 95890675,
 					"data_hd.dcp", "79a5c4ae560817a149506dce894274d0", 197155130,
-					"i18n_pl_strings.dcp", "7c07fb021517dd21ba2e2a5739d0168e", 40062), Common::PL_POL, ADGF_UNSTABLE | GF_IGNORE_SD_FILES | GF_3D, WME_1_9_3),
+					"i18n_pl_strings.dcp", "7c07fb021517dd21ba2e2a5739d0168e", 40062), Common::PL_POL, GF_IGNORE_SD_FILES | GF_3D, WME_1_9_3),
 
 	// Shadows on the Vatican - Act I: Greed (Full HD Version) (Steam, Sep 2016) (Russian)
 	WME_WINENTRY("sotv1", "Full HD Version/Steam",
 		WME_ENTRY3s("data.dcp", "46bb5822abf6d422d08a68070e05bd86", 95890675,
 					"data_hd.dcp", "79a5c4ae560817a149506dce894274d0", 197155130,
-					"i18n_ru_strings.dcp", "5cecc876ec1b364f22780d18a0821349", 45546), Common::RU_RUS, ADGF_UNSTABLE | GF_IGNORE_SD_FILES | GF_3D, WME_1_9_3),
+					"i18n_ru_strings.dcp", "5cecc876ec1b364f22780d18a0821349", 45546), Common::RU_RUS, GF_IGNORE_SD_FILES | GF_3D, WME_1_9_3),
 
 	// Shadows on the Vatican - Act I: Greed (Full HD Version) (Steam, Jun 2018) (German)
 	WME_WINENTRY("sotv1", "Full HD Version/Steam",
 		WME_ENTRY3s("data.dcp", "e0d5b1276cf80c858b7404f1f3381e2b", 95890675,
 					"data_hd.dcp", "05ce36b4c7b947c503496a1d895671e5", 197431006,
-					"i18n_de_strings.dcp", "59d15bb3c32354b5104475df0ff6c50b", 43136), Common::DE_DEU, ADGF_UNSTABLE | GF_IGNORE_SD_FILES | GF_3D, WME_1_9_3),
+					"i18n_de_strings.dcp", "59d15bb3c32354b5104475df0ff6c50b", 43136), Common::DE_DEU, GF_IGNORE_SD_FILES | GF_3D, WME_1_9_3),
 
 	// Shadows on the Vatican - Act I: Greed (Full HD Version) (Steam, Jun 2018) (English)
 	WME_WINENTRY("sotv1", "Full HD Version/Steam",
 		WME_ENTRY3s("data.dcp", "e0d5b1276cf80c858b7404f1f3381e2b", 95890675,
 					"data_hd.dcp", "05ce36b4c7b947c503496a1d895671e5", 197431006,
-					"i18n_en_strings.dcp", "733a53213ab5be27941c34dcef531e0e", 75119), Common::EN_ANY, ADGF_UNSTABLE | GF_IGNORE_SD_FILES | GF_3D, WME_1_9_3),
+					"i18n_en_strings.dcp", "733a53213ab5be27941c34dcef531e0e", 75119), Common::EN_ANY, GF_IGNORE_SD_FILES | GF_3D, WME_1_9_3),
 
 	// Shadows on the Vatican - Act I: Greed (Full HD Version) (Steam, Jun 2018) (Spanish)
 	WME_WINENTRY("sotv1", "Full HD Version/Steam",
 		WME_ENTRY3s("data.dcp", "e0d5b1276cf80c858b7404f1f3381e2b", 95890675,
 					"data_hd.dcp", "05ce36b4c7b947c503496a1d895671e5", 197431006,
-					"i18n_es_strings.dcp", "18876a252741b8bda888805ca860f600", 40153), Common::ES_ESP, ADGF_UNSTABLE | GF_IGNORE_SD_FILES | GF_3D, WME_1_9_3),
+					"i18n_es_strings.dcp", "18876a252741b8bda888805ca860f600", 40153), Common::ES_ESP, GF_IGNORE_SD_FILES | GF_3D, WME_1_9_3),
 
 	// Shadows on the Vatican - Act I: Greed (Full HD Version) (Steam, Jun 2018) (French)
 	WME_WINENTRY("sotv1", "Full HD Version/Steam",
 		WME_ENTRY3s("data.dcp", "e0d5b1276cf80c858b7404f1f3381e2b", 95890675,
 					"data_hd.dcp", "05ce36b4c7b947c503496a1d895671e5", 197431006,
-					"i18n_fr_strings.dcp", "850cce9217d83785e4b56028441b0d71", 40807), Common::FR_FRA, ADGF_UNSTABLE | GF_IGNORE_SD_FILES | GF_3D, WME_1_9_3),
+					"i18n_fr_strings.dcp", "850cce9217d83785e4b56028441b0d71", 40807), Common::FR_FRA, GF_IGNORE_SD_FILES | GF_3D, WME_1_9_3),
 
 	// Shadows on the Vatican - Act I: Greed (Full HD Version) (Steam, Jun 2018) (Italian)
 	WME_WINENTRY("sotv1", "Full HD Version/Steam",
 		WME_ENTRY3s("data.dcp", "e0d5b1276cf80c858b7404f1f3381e2b", 95890675,
 					"data_hd.dcp", "05ce36b4c7b947c503496a1d895671e5", 197431006,
-					"i18n_it_strings.dcp", "de567598fba43589841c354306a80377", 40502), Common::IT_ITA, ADGF_UNSTABLE | GF_IGNORE_SD_FILES | GF_3D, WME_1_9_3),
+					"i18n_it_strings.dcp", "de567598fba43589841c354306a80377", 40502), Common::IT_ITA, GF_IGNORE_SD_FILES | GF_3D, WME_1_9_3),
 
 	// Shadows on the Vatican - Act I: Greed (Full HD Version) (Steam, Jun 2018) (Polish)
 	WME_WINENTRY("sotv1", "Full HD Version/Steam",
 		WME_ENTRY3s("data.dcp", "e0d5b1276cf80c858b7404f1f3381e2b", 95890675,
 					"data_hd.dcp", "05ce36b4c7b947c503496a1d895671e5", 197431006,
-					"i18n_pl_strings.dcp", "daa4b251caec6c6e2c3b96aaf87ae33a", 40062), Common::PL_POL, ADGF_UNSTABLE | GF_IGNORE_SD_FILES | GF_3D, WME_1_9_3),
+					"i18n_pl_strings.dcp", "daa4b251caec6c6e2c3b96aaf87ae33a", 40062), Common::PL_POL, GF_IGNORE_SD_FILES | GF_3D, WME_1_9_3),
 
 	// Shadows on the Vatican - Act I: Greed (Full HD Version) (Steam, Jun 2018) (Russian)
 	WME_WINENTRY("sotv1", "Full HD Version/Steam",
 		WME_ENTRY3s("data.dcp", "e0d5b1276cf80c858b7404f1f3381e2b", 95890675,
 					"data_hd.dcp", "05ce36b4c7b947c503496a1d895671e5", 197431006,
-					"i18n_ru_strings.dcp", "ebf28ca1475b3be9754a323966807683", 45546), Common::RU_RUS, ADGF_UNSTABLE | GF_IGNORE_SD_FILES | GF_3D, WME_1_9_3),
+					"i18n_ru_strings.dcp", "ebf28ca1475b3be9754a323966807683", 45546), Common::RU_RUS, GF_IGNORE_SD_FILES | GF_3D, WME_1_9_3),
 
 	// Shadows on the Vatican - Act I: Greed (Legacy Version) (NewTech) (Persian)
 	// NOTE: this game distribution contained unpacked game files instead of usual game.dcp package


Commit: 02891ad9466e149dba6685cf9427592ba97afc82
    https://github.com/scummvm/scummvm/commit/02891ad9466e149dba6685cf9427592ba97afc82
Author: Walter Agazzi (walter.agazzi at protonmail.com)
Date: 2026-06-16T21:25:03+02:00

Commit Message:
WINTERMUTE: Add Golden Calf fantranslations

Changed paths:
    engines/wintermute/detection_tables.h


diff --git a/engines/wintermute/detection_tables.h b/engines/wintermute/detection_tables.h
index 65dda1b6c2b..f7a643d4138 100644
--- a/engines/wintermute/detection_tables.h
+++ b/engines/wintermute/detection_tables.h
@@ -1464,6 +1464,21 @@ static const WMEGameDescription gameDescriptions[] = {
 	WME_WINENTRY("goldencalf", "",
 		WME_ENTRY1s("data.dcp", "2e7bae5c1d0fd18f269147b834be2bc8", 1174553), Common::FR_FRA, ADGF_UNSTABLE, WME_1_8_11),
 
+	// The Golden Calf (Russian + English fanmade translation)
+	WME_WINENTRY("goldencalf", "Fanmade",
+		WME_ENTRY2s("data.dcp", "c70536681d839f9b4357938891402af3", 488532641,
+					"string.tab", "be536da5e13f23d8ceab3839a7bf4340", 133687), Common::EN_ANY, ADGF_UNSTABLE, WME_1_8_11),
+
+	// The Golden Calf (German + English fanmade translation)
+	WME_WINENTRY("goldencalf", "Fanmade",
+		WME_ENTRY2s("data.dcp", "c611503c2c10137d1195878080cee2d1", 1174552,
+					"string.tab", "be536da5e13f23d8ceab3839a7bf4340", 133687), Common::EN_ANY, ADGF_UNSTABLE, WME_1_8_11),
+
+	// The Golden Calf (French + English fanmade translation)
+	WME_WINENTRY("goldencalf", "Fanmade",
+		WME_ENTRY2s("data.dcp", "2e7bae5c1d0fd18f269147b834be2bc8", 1174553,
+					"string.tab", "be536da5e13f23d8ceab3839a7bf4340", 133687), Common::EN_ANY, ADGF_UNSTABLE, WME_1_8_11),
+
 	// The Golden Calf (Spanish fanmade)
 	WME_WINENTRY("goldencalf", "Fanmade",
 		WME_ENTRY2s("data.dcp", "2e7bae5c1d0fd18f269147b834be2bc8", 1174553,



From noreply at scummvm.org  Wed Jun 17 01:06:44 2026
From: noreply at scummvm.org (bluegr)
Date: Wed, 17 Jun 2026 01:06:44 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 946eaa3c1e9e3a9d580e7970e214fecb0462ba1a
Message-ID: <20260617010644.916417005BC@ron.scummvm.net>

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

Summary:
e41e7bde85 NANCY: Handle more cases in onebuildpuzzle
e27150898a naNCY: Adjustments to the quiz puzzle
6bf575df25 NANCY: Add a sanity check in setItemDisabledState()
6039a72de3 NANCY: Fix text display in the cell phone screens in Nancy 10
10de11d8c0 NANCY: Fix wall collision in the magnet maze puzzle in Nancy 10
459c286cd8 NANCY: Initial work on the PlayRandomMovie ARs for Nancy 11
946eaa3c1e NANCY: Handle alt surfaces in onebuildpuzzle


Commit: e41e7bde857b96962b075fae5dfab8de1c093ec3
    https://github.com/scummvm/scummvm/commit/e41e7bde857b96962b075fae5dfab8de1c093ec3
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-06-17T04:06:25+03:00

Commit Message:
NANCY: Handle more cases in onebuildpuzzle

- Ignore piece rotation regarding piece placement, like the original
- Skip pre-placed pieces

Fixes the bridle puzzle in Nancy 10

Changed paths:
    engines/nancy/action/puzzle/onebuildpuzzle.cpp


diff --git a/engines/nancy/action/puzzle/onebuildpuzzle.cpp b/engines/nancy/action/puzzle/onebuildpuzzle.cpp
index fd1f080afab..c10539ecda2 100644
--- a/engines/nancy/action/puzzle/onebuildpuzzle.cpp
+++ b/engines/nancy/action/puzzle/onebuildpuzzle.cpp
@@ -317,23 +317,29 @@ void OneBuildPuzzle::handleInput(NancyInput &input) {
 
 			Common::Rect slot = piece.slotRect;
 
-			// Correct placement: bounding-box must fit within slot +- tolerance
-			// (piece is rotation 0, same size as slot)
+			// Bounding-box must fit within slot +- tolerance. The original
+			// engine doesn't check rotation separately; a rotated piece's
+			// dimensions are reflected in gameRect, so a non-fitting rotation
+			// is rejected by the rect inequalities below.
 			bool nearSlot = (piece.gameRect.left >= slot.left - _slotTolerance &&
 							 piece.gameRect.top  >= slot.top  - _slotTolerance &&
 							 piece.gameRect.right  <= slot.right  + _slotTolerance &&
 							 piece.gameRect.bottom <= slot.bottom + _slotTolerance);
 
-			bool correctRotation = (piece.curRotation == 0);
 			bool orderOk = !_orderedPlacement ||
 				(_piecesPlaced < (uint16)_placementOrder.size() &&
 				 _placementOrder[_piecesPlaced] == (int16)(_pickedUpPiece + 1));
 
-			if (nearSlot && correctRotation && orderOk) {
+			if (nearSlot && orderOk) {
 				piece.gameRect = piece.slotRect;
 				piece.placed = true;
 				_correctlyPlaced = true;
 				++_piecesPlaced;
+
+				// Skip pre-placed pieces
+				if (_piecesPlaced < _placementOrder.size() && _placementOrder[_piecesPlaced] - 1 < _pieces.size())
+					if (_pieces[_placementOrder[_piecesPlaced] - 1].isPreRotated)
+						++_piecesPlaced;
 			} else {
 				_correctlyPlaced = false;
 				if (!_freePlacement) {


Commit: e27150898aa8446b93050a24eb3bd43527c1dcde
    https://github.com/scummvm/scummvm/commit/e27150898aa8446b93050a24eb3bd43527c1dcde
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-06-17T04:06:26+03:00

Commit Message:
naNCY: Adjustments to the quiz puzzle

- Bump maximum input boxes to 13 (used in Nancy 10)
- Implement the exit hotspot for this puzzle

Fixes the jail tally marks puzzle in Nancy 10

Changed paths:
    engines/nancy/action/puzzle/quizpuzzle.cpp
    engines/nancy/action/puzzle/quizpuzzle.h


diff --git a/engines/nancy/action/puzzle/quizpuzzle.cpp b/engines/nancy/action/puzzle/quizpuzzle.cpp
index 2a3ffcdc6a9..ffbd03a193c 100644
--- a/engines/nancy/action/puzzle/quizpuzzle.cpp
+++ b/engines/nancy/action/puzzle/quizpuzzle.cpp
@@ -138,7 +138,7 @@ void QuizPuzzle::readDataOld(Common::SeekableReadStream &stream) {
 // 0x32  49  doneSound (readNormal)
 // 0x63  30  done subtitle (skip)
 // 0x81  25  cancelScene
-// 0x9A  16  unknown (skip)
+// 0x9A  16  exitHotspot rect (4??sint32)
 // 0xAA  2   correctSoundChannel
 // 0xAC  2   wrongSoundChannel
 // 0xAE  1   skipEmptyOnEnter flag
@@ -172,7 +172,7 @@ void QuizPuzzle::readDataNew(Common::SeekableReadStream &stream) {
 	_doneText = readSubtitle(stream);
 
 	_cancelScene.readData(stream);
-	stream.skip(16); // unknown
+	readRect(stream, _exitHotspot);
 
 	_correctSoundChannel = stream.readUint16LE();
 	_wrongSoundChannel   = stream.readUint16LE();
@@ -586,6 +586,19 @@ void QuizPuzzle::handleInput(NancyInput &input) {
 
 	char cursorChar = (g_nancy->getGameType() == kGameTypeNancy8) ? '-' : _cursorChar;
 
+	// Nancy 9+: exit hotspot at chunk +0x9A. Click cancels the puzzle.
+	if (g_nancy->getGameType() != kGameTypeNancy8 && !_exitHotspot.isEmpty()) {
+		Common::Rect exitScreen = NancySceneState.getViewport().convertViewportToScreen(_exitHotspot);
+		if (exitScreen.contains(input.mousePos)) {
+			g_nancy->_cursor->setCursorType(g_nancy->_cursor->_puzzleExitCursor);
+			if (input.input & NancyInput::kLeftMouseButtonUp) {
+				_cancelled = true;
+				_state = kActionTrigger;
+			}
+			return;
+		}
+	}
+
 	// Hover over an unsolved text box: show the hotspot cursor and, on click,
 	// move the typing focus to that box.
 	for (uint i = 0; i < _numBoxes; ++i) {
diff --git a/engines/nancy/action/puzzle/quizpuzzle.h b/engines/nancy/action/puzzle/quizpuzzle.h
index 662157d492e..9d5371ebddb 100644
--- a/engines/nancy/action/puzzle/quizpuzzle.h
+++ b/engines/nancy/action/puzzle/quizpuzzle.h
@@ -47,7 +47,7 @@ protected:
 	Common::String getRecordTypeName() const override { return "QuizPuzzle"; }
 
 private:
-	static const int kMaxBoxes = 8;
+	static const int kMaxBoxes = 13;
 
 	// Format-specific read/execute implementations
 	void readDataOld(Common::SeekableReadStream &stream); // Nancy 8
@@ -75,7 +75,7 @@ private:
 	// Per-box answer data: up to 3 valid answers (case-insensitive match), plus
 	// an optional event flag to set when that box is answered correctly.
 	Common::String _answers[kMaxBoxes][3];
-	int16 _answerFlags[kMaxBoxes] = { -1, -1, -1, -1, -1, -1, -1, -1 };
+	int16 _answerFlags[kMaxBoxes] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 };
 
 	SoundDescription _correctSound;  // Nancy 8: global correct sound
 	Common::String _correctText;
@@ -87,6 +87,7 @@ private:
 
 	SceneChangeWithFlag _solveScene;   // scene to go to when all boxes are solved
 	SceneChangeWithFlag _cancelScene;  // scene to go to on cancel
+	Common::Rect _exitHotspot;         // Nancy 9+: viewport-relative cancel hotspot
 
 	// ---- Data (Nancy 9+) ----
 	char _cursorChar = '-';              // cursor character (configurable in Nancy 9)


Commit: 6bf575df250b31c454667a4bcd343ba7da5be5ca
    https://github.com/scummvm/scummvm/commit/6bf575df250b31c454667a4bcd343ba7da5be5ca
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-06-17T04:06:28+03:00

Commit Message:
NANCY: Add a sanity check in setItemDisabledState()

Fixes a crash when the villain chase scene starts in Nancy 10

Changed paths:
    engines/nancy/state/scene.h


diff --git a/engines/nancy/state/scene.h b/engines/nancy/state/scene.h
index e9118c381ea..0a580ab87dc 100644
--- a/engines/nancy/state/scene.h
+++ b/engines/nancy/state/scene.h
@@ -136,7 +136,10 @@ public:
 	void setNoHeldItem();
 	byte hasItem(int16 id) const;
 	byte getItemDisabledState(int16 id) const { return _flags.disabledItems[id]; }
-	void setItemDisabledState(int16 id, byte state) { _flags.disabledItems[id] = state; }
+	void setItemDisabledState(int16 id, byte state) {
+		if (id < _flags.disabledItems.size())
+			_flags.disabledItems[id] = state;
+	}
 
 	void installInventorySoundOverride(byte command, const SoundDescription &sound, const Common::String &caption, uint16 itemID);
 	void playItemCantSound(int16 itemID = -1, bool notHoldingSound = false);


Commit: 6039a72de3a40d89bb7f9b612087bdb9e8f85b35
    https://github.com/scummvm/scummvm/commit/6039a72de3a40d89bb7f9b612087bdb9e8f85b35
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-06-17T04:06:31+03:00

Commit Message:
NANCY: Fix text display in the cell phone screens in Nancy 10

Also, show the selection indicator in the Internet browser screen

Changed paths:
    engines/nancy/ui/cellphonepopup.cpp


diff --git a/engines/nancy/ui/cellphonepopup.cpp b/engines/nancy/ui/cellphonepopup.cpp
index a283748313d..a54f29f84a6 100644
--- a/engines/nancy/ui/cellphonepopup.cpp
+++ b/engines/nancy/ui/cellphonepopup.cpp
@@ -763,8 +763,13 @@ void CellPhonePopup::drawLinkList() {
 			rowText.erase(rowText.find("<n>"), 3);
 		}
 
+		// Original anchors row text on the row's bottom (baseline-up
+		// rendering); mirror that so the glyphs sit at the bottom of
+		// the row instead of glued to the top.
+		const int textY = MAX<int16>(rowRect.top,
+								rowRect.bottom - font->getFontHeight());
 		font->drawString(&_drawSurface, rowText,
-							textX, rowRect.top,
+							textX, textY,
 							rowRect.right - textX, 0);
 	}
 }
@@ -952,8 +957,10 @@ void CellPhonePopup::drawDirectoryList() {
 		++visited;
 
 		const Common::Rect rowRect = directoryRowRect(visibleRow);
+		const int textY = MAX<int16>(rowRect.top,
+								rowRect.bottom - font->getFontHeight());
 		font->drawString(&_drawSurface, c.name,
-							rowRect.left, rowRect.top,
+							rowRect.left, textY,
 							rowRect.width(), 0);
 		++visibleRow;
 	}
@@ -1026,10 +1033,10 @@ void CellPhonePopup::drawDirectoryArrows() {
 	}
 
 	// Selection indicator (dirArrowSrc sprite) at the dirCursorSrc column,
-	// stepped down by the active entry's layout row ??? only meaningful in
-	// directory mode; list and content modes have their own arrow logic
-	// (or none at all) and a stray cursor sprite paints over their LCD.
-	if (_screenState != kDirectory) {
+	// stepped down by the active entry's layout row. Drawn for directory
+	// and the search-topic list; the email list signals the current row
+	// by swapping its per-row icon, so no separate arrow there.
+	if (_screenState != kDirectory && _screenState != kWebList) {
 		return;
 	}
 	const Common::Rect &arrowSrc = _uiclData->dirArrowSrc;


Commit: 10de11d8c06f98265c6082ec66286a6ab085837a
    https://github.com/scummvm/scummvm/commit/10de11d8c06f98265c6082ec66286a6ab085837a
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-06-17T04:06:31+03:00

Commit Message:
NANCY: Fix wall collision in the magnet maze puzzle in Nancy 10

Changed paths:
    engines/nancy/action/puzzle/magnetmazepuzzle.cpp


diff --git a/engines/nancy/action/puzzle/magnetmazepuzzle.cpp b/engines/nancy/action/puzzle/magnetmazepuzzle.cpp
index 710eb2f5263..50c623adf51 100644
--- a/engines/nancy/action/puzzle/magnetmazepuzzle.cpp
+++ b/engines/nancy/action/puzzle/magnetmazepuzzle.cpp
@@ -221,7 +221,14 @@ bool MagnetMazePuzzle::collidesAt(const Common::Rect &r) const {
 	if (r.left < 0 || r.top < 0 || r.right > mazeW || r.bottom > mazeH)
 		return true;
 
-	const uint32 wall = _mazeImage.format.RGBToColor(_wallColorR, _wallColorG, _wallColorB);
+	// The chunk stores each wall channel as a 5-bit value (the original engine
+	// masks with 0x1f and packs directly into a 16-bit display-format word).
+	// Scale to 8-bit before RGBToColor so the resulting packed value matches
+	// the actual maze-image pixels for the current pixel format.
+	const byte r8 = (byte)((_wallColorR & 0x1f) << 3);
+	const byte g8 = (byte)((_wallColorG & 0x1f) << 3);
+	const byte b8 = (byte)((_wallColorB & 0x1f) << 3);
+	const uint32 wall = _mazeImage.format.RGBToColor(r8, g8, b8);
 	for (int y = y0; y < y1; ++y) {
 		for (int x = x0; x < x1; ++x) {
 			if (_mazeImage.getPixel(x, y) == wall)


Commit: 459c286cd8f59c757d1b003cdd0f2d7e0bf6f23e
    https://github.com/scummvm/scummvm/commit/459c286cd8f59c757d1b003cdd0f2d7e0bf6f23e
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-06-17T04:06:33+03:00

Commit Message:
NANCY: Initial work on the PlayRandomMovie ARs for Nancy 11

Used in cases such as the conversation with the parrot. Conversation
videos are still not played in this case, as there's no appropriate
trigger in the conversation scene scripts

Changed paths:
    engines/nancy/action/arfactory.cpp
    engines/nancy/action/secondarymovie.cpp
    engines/nancy/action/secondarymovie.h


diff --git a/engines/nancy/action/arfactory.cpp b/engines/nancy/action/arfactory.cpp
index d89f822b735..c0efd860108 100644
--- a/engines/nancy/action/arfactory.cpp
+++ b/engines/nancy/action/arfactory.cpp
@@ -187,12 +187,10 @@ ActionRecord *ActionManager::createActionRecord(uint16 type, Common::SeekableRea
 	case 32:
 		// Nancy 10+
 		return new UIPopupPrepScene();
-	case 45:	// Nancy11
-		warning("PlayRandomMovie");	// TODO
-		return nullptr;
+	case 45:	// Nancy11 - random-movie variant of PlaySecondaryMovie
+		return new PlaySecondaryMovie(true);
 	case 46:	// Nancy11
-		warning("PlayRandomMovieControl");	// TODO
-		return nullptr;
+		return new PlayRandomMovieControl();
 	case 40:
 		if (g_nancy->getGameType() <= kGameTypeNancy1)
 			return new LightningOn();	// Only used in TVD
diff --git a/engines/nancy/action/secondarymovie.cpp b/engines/nancy/action/secondarymovie.cpp
index 5417d3d7772..8ac8ae5a835 100644
--- a/engines/nancy/action/secondarymovie.cpp
+++ b/engines/nancy/action/secondarymovie.cpp
@@ -28,6 +28,7 @@
 #include "engines/nancy/action/secondarymovie.h"
 #include "engines/nancy/state/scene.h"
 
+#include "common/random.h"
 #include "common/serializer.h"
 
 #include "video/bink_decoder.h"
@@ -45,10 +46,85 @@ PlaySecondaryMovie::~PlaySecondaryMovie() {
 	}
 }
 
+void PlaySecondaryMovie::readRandomSequence(Common::Serializer &ser, RandomSequence &seq) {
+	readFilename(ser, seq.name);
+	ser.syncAsUint16LE(seq.startFrame);
+	ser.syncAsUint16LE(seq.unknown_0x23);
+	ser.syncAsSint32LE(seq.minPauseMs);
+	ser.syncAsSint32LE(seq.maxPauseMs);
+	ser.syncAsUint16LE(seq.unknown_0x2D);
+
+	uint16 nextCount = 0;
+	ser.syncAsUint16LE(nextCount);
+
+	seq.nextSequences.resize(nextCount);
+	for (uint i = 0; i < nextCount; ++i) {
+		readFilename(ser, seq.nextSequences[i].name);
+		ser.syncAsUint16LE(seq.nextSequences[i].unknown);
+	}
+}
+
+void PlaySecondaryMovie::readRandomMovieData(Common::Serializer &ser, Common::SeekableReadStream &stream) {
+	readFilename(ser, _startingSequenceName);
+	ser.syncAsUint16LE(_randomPlayerCursorAllowed);
+
+	uint16 sequenceCount = 0, hotspotCount = 0;
+	ser.syncAsUint16LE(sequenceCount);
+	ser.syncAsUint16LE(hotspotCount);
+
+	_sequences.resize(sequenceCount);
+	for (uint i = 0; i < sequenceCount; ++i) {
+		readRandomSequence(ser, _sequences[i]);
+	}
+
+	_videoDescs.resize(hotspotCount);
+	for (uint i = 0; i < hotspotCount; ++i) {
+		_videoDescs[i].readData(stream);
+	}
+
+	// "RandomMovie" picks any sequence; otherwise look up by name.
+	// Only the first sequence is played; chained playback is TODO.
+	if (!_sequences.empty()) {
+		int startIdx = -1;
+		if (_startingSequenceName == "RandomMovie") {
+			startIdx = g_nancy->_randomSource->getRandomNumber(_sequences.size() - 1);
+		} else {
+			for (uint i = 0; i < _sequences.size(); ++i) {
+				if (_sequences[i].name.toString() == _startingSequenceName) {
+					startIdx = (int)i;
+					break;
+				}
+			}
+			if (startIdx < 0) {
+				warning("PlayRandomMovie: starting sequence \"%s\" is not part of this AR",
+					_startingSequenceName.c_str());
+			}
+		}
+
+		if (startIdx >= 0) {
+			const RandomSequence &src = _sequences[startIdx];
+			_videoName = src.name;
+			_firstFrame = src.startFrame;
+			_videoType = kVideoPlaytypeBink;
+			_videoFormat = kLargeVideoFormat;
+			_videoSceneChange = kMovieNoSceneChange;
+			_playerCursorAllowed = (byte)_randomPlayerCursorAllowed;
+			_playDirection = kPlayMovieForward;
+		}
+	}
+
+	_sound.name = "NO SOUND";
+}
+
 void PlaySecondaryMovie::readData(Common::SeekableReadStream &stream) {
 	Common::Serializer ser(&stream, nullptr);
 	ser.setVersion(g_nancy->getGameType());
 
+	if (_isRandom) {
+		readRandomMovieData(ser, stream);
+		return;
+	}
+
 	readFilename(ser, _videoName);
 	readFilename(ser, _paletteName, kGameTypeVampire, kGameTypeVampire);
 	readFilename(ser, _bitmapOverlayName, kGameTypeVampire, kGameTypeNancy9);
@@ -213,6 +289,10 @@ void PlaySecondaryMovie::execute() {
 			if (activeFrame != -1) {
 				_screenPosition = _videoDescs[activeFrame].destRect;
 				setVisible(true);
+			} else if (_isRandom && _videoDescs.empty()) {
+				// No hotspots: play full-viewport instead of gating on a frame match.
+				_screenPosition = NancySceneState.getViewport().getBounds();
+				setVisible(true);
 			} else {
 				setVisible(false);
 			}
@@ -293,5 +373,25 @@ void PlaySecondaryMovie::execute() {
 	}
 }
 
+// --- PlayRandomMovieControl --------------------------------------------
+
+void PlayRandomMovieControl::readData(Common::SeekableReadStream &stream) {
+	_targetA = stream.readUint32LE();
+	_targetB = stream.readUint32LE();
+	_targetC = stream.readUint32LE();
+	_flagsOrIndex = stream.readUint16LE();
+	_trailing = stream.readByte();
+}
+
+void PlayRandomMovieControl::execute() {
+	// Original binary debug string:
+	//   "STOP AT_PLAY_RANDOM_MOVIE AR %d in Scene %d"
+	// so two of the chunk fields identify the target AR / scene.
+	// Until the exact identification scheme is known, this stub just
+	// warns and completes.
+	warning("PlayRandomMovieControl is not yet fully implemented");
+	finishExecution();
+}
+
 } // End of namespace Action
 } // End of namespace Nancy
diff --git a/engines/nancy/action/secondarymovie.h b/engines/nancy/action/secondarymovie.h
index 555706712b8..b68e47502d0 100644
--- a/engines/nancy/action/secondarymovie.h
+++ b/engines/nancy/action/secondarymovie.h
@@ -42,7 +42,10 @@ class InteractiveVideo;
 // - hiding of player cursor (and thus, disabling input);
 // - setting event flags on a specific frame, as well as at the end of the video;
 // - changing the scene after playback ends
-// Mostly used for cinematics, with some occasional uses for background animations
+// Mostly used for cinematics, with some occasional uses for background animations.
+//
+// Construct with `isRandom = true` for Nancy 11's AT_PLAY_RANDOM_MOVIE (AR 45):
+// the chunk holds a list of sequences and one is picked at readData() time.
 class PlaySecondaryMovie : public RenderActionRecord {
 public:
 	static const byte kMovieSceneChange			= 5;
@@ -59,7 +62,24 @@ public:
 		FlagDescription flagDesc;
 	};
 
-	PlaySecondaryMovie() : RenderActionRecord(8) {}
+	// Name of the next sequence to chain to once the current one finishes.
+	struct NextSequenceRef {
+		Common::Path name;
+		uint16 unknown = 0;
+	};
+
+	// `name` is both the sequence id and the movie filename.
+	struct RandomSequence {
+		Common::Path name;
+		uint16 startFrame = 0;
+		uint16 unknown_0x23 = 0;
+		int32 minPauseMs = 0;
+		int32 maxPauseMs = 0;
+		uint16 unknown_0x2D = 0;
+		Common::Array<NextSequenceRef> nextSequences;
+	};
+
+	PlaySecondaryMovie(bool isRandom = false) : RenderActionRecord(8), _isRandom(isRandom) {}
 	virtual ~PlaySecondaryMovie();
 
 	void init() override;
@@ -92,16 +112,56 @@ public:
 
 	Common::ScopedPtr<Video::VideoDecoder> _decoder;
 
+	// Random-movie state (only populated when _isRandom).
+	bool _isRandom = false;
+	// "RandomMovie" picks any sequence; otherwise it names the starting one.
+	Common::String _startingSequenceName;
+	uint16 _randomPlayerCursorAllowed = kPlayerCursorAllowed;
+	Common::Array<RandomSequence> _sequences;
+
 	bool isViewportRelative() const override { return true; }
 
 protected:
-	Common::String getRecordTypeName() const override { return "PlaySecondaryMovie"; }
+	Common::String getRecordTypeName() const override {
+		return _isRandom ? "PlayRandomMovie" : "PlaySecondaryMovie";
+	}
+
+	// `ser` and `stream` must wrap the same input; `stream` is only
+	// needed for SecondaryVideoDescription::readData.
+	void readRandomMovieData(Common::Serializer &ser, Common::SeekableReadStream &stream);
+	void readRandomSequence(Common::Serializer &ser, RandomSequence &seq);
 
 	Graphics::ManagedSurface _fullFrame;
 	int _curViewportFrame = -1;
 	bool _isFinished = false;
 };
 
+// Companion AR for the random-movie variant of PlaySecondaryMovie. When
+// executed it locates the matching PlaySecondaryMovie(isRandom=true)
+// instance (by sequence/AR name) and stops it. First seen in Nancy 11
+// (AR 46, AT_PLAY_RANDOM_MOVIE_CONTROL).
+//
+// Chunk layout: fixed 15 bytes (3 ?? uint32 + uint16 + uint8). The exact
+// meaning of each field is not yet reverse-engineered; the runtime debug
+// string is "STOP AT_PLAY_RANDOM_MOVIE AR %d in Scene %d", so at least two
+// of these fields identify the target AR.
+class PlayRandomMovieControl : public ActionRecord {
+public:
+	PlayRandomMovieControl() {}
+
+	void readData(Common::SeekableReadStream &stream) override;
+	void execute() override;
+
+protected:
+	Common::String getRecordTypeName() const override { return "PlayRandomMovieControl"; }
+
+	uint32 _targetA = 0;
+	uint32 _targetB = 0;
+	uint32 _targetC = 0;
+	uint16 _flagsOrIndex = 0;
+	byte _trailing = 0;
+};
+
 } // End of namespace Action
 } // End of namespace Nancy
 


Commit: 946eaa3c1e9e3a9d580e7970e214fecb0462ba1a
    https://github.com/scummvm/scummvm/commit/946eaa3c1e9e3a9d580e7970e214fecb0462ba1a
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-06-17T04:06:34+03:00

Commit Message:
NANCY: Handle alt surfaces in onebuildpuzzle

Fixes key drawing in the door puzzle in Nancy 10

Changed paths:
    engines/nancy/action/puzzle/onebuildpuzzle.cpp
    engines/nancy/action/puzzle/onebuildpuzzle.h


diff --git a/engines/nancy/action/puzzle/onebuildpuzzle.cpp b/engines/nancy/action/puzzle/onebuildpuzzle.cpp
index c10539ecda2..222b6b64c86 100644
--- a/engines/nancy/action/puzzle/onebuildpuzzle.cpp
+++ b/engines/nancy/action/puzzle/onebuildpuzzle.cpp
@@ -60,6 +60,16 @@ void OneBuildPuzzle::init() {
 			}
 		}
 
+		p.useAltSurface = false;
+		if (!p.altSrcRect.isEmpty() && !p.isPreRotated) {
+			int aw = p.altSrcRect.width();
+			int ah = p.altSrcRect.height();
+			p.altSurface.create(aw, ah, _image.format);
+			p.altSurface.setTransparentColor(_drawSurface.getTransparentColor());
+			p.altSurface.blitFrom(_image, p.altSrcRect, Common::Point(0, 0));
+			p.useAltSurface = true;
+		}
+
 		// Initial position and rotation
 		if (p.isPreRotated) {
 			// Pre-rotated pieces start at their slot and are already placed
@@ -127,12 +137,15 @@ void OneBuildPuzzle::readData(Common::SeekableReadStream &stream) {
 
 		Piece &p = _pieces[i];
 		if (isNancy10) {
-			// Fall back to the alt rect when the primary one is empty.
+			// Two rects: altSrc = at-home art, srcRect = active art
 			Common::Rect altSrc;
 			readRect(stream, altSrc);
 			readRect(stream, p.srcRect);
-			if (p.srcRect.isEmpty())
+			if (p.srcRect.isEmpty()) {
 				p.srcRect = altSrc;
+			} else if (!altSrc.isEmpty()) {
+				p.altSrcRect = altSrc;
+			}
 		} else {
 			readRect(stream, p.srcRect);
 		}
@@ -350,6 +363,12 @@ void OneBuildPuzzle::handleInput(NancyInput &input) {
 				}
 			}
 
+			// Re-arm at-home art when the piece lands back on homeRect
+			if (!piece.altSurface.empty() && !piece.placed &&
+					piece.gameRect == piece.homeRect) {
+				piece.useAltSurface = true;
+			}
+
 			updatePieceRender(_pickedUpPiece);
 			_isDragging = false;
 			_pickedUpPiece = -1;
@@ -389,10 +408,12 @@ void OneBuildPuzzle::handleInput(NancyInput &input) {
 		if ((leftClick || rightClick) && topmostUnplaced != -1) {
 			_pickedUpPiece = topmostUnplaced;
 
+			Piece &pp = _pieces[_pickedUpPiece];
+			pp.useAltSurface = false;
+
 			if (rightClick)
 				rotatePiece(_pickedUpPiece);
 
-			Piece &pp = _pieces[_pickedUpPiece];
 			_isDragging = true;
 			_pickedUpWidth  = pp.rotateSurfaces[pp.curRotation].w;
 			_pickedUpHeight = pp.rotateSurfaces[pp.curRotation].h;
@@ -418,12 +439,16 @@ void OneBuildPuzzle::handleInput(NancyInput &input) {
 
 void OneBuildPuzzle::updatePieceRender(int pieceIdx) {
 	Piece &p = _pieces[pieceIdx];
-	int rot = p.curRotation;
-	if (!p.hasSurface[rot])
-		rot = 0;
-	if (!p.hasSurface[rot])
-		return;
-	p._drawSurface.create(p.rotateSurfaces[rot], p.rotateSurfaces[rot].getBounds());
+	if (p.useAltSurface && !p.altSurface.empty()) {
+		p._drawSurface.create(p.altSurface, p.altSurface.getBounds());
+	} else {
+		int rot = p.curRotation;
+		if (!p.hasSurface[rot])
+			rot = 0;
+		if (!p.hasSurface[rot])
+			return;
+		p._drawSurface.create(p.rotateSurfaces[rot], p.rotateSurfaces[rot].getBounds());
+	}
 	p.setTransparent(true);
 	p.moveTo(p.gameRect);
 }
diff --git a/engines/nancy/action/puzzle/onebuildpuzzle.h b/engines/nancy/action/puzzle/onebuildpuzzle.h
index 81f2d6f7c49..99acf6dafcc 100644
--- a/engines/nancy/action/puzzle/onebuildpuzzle.h
+++ b/engines/nancy/action/puzzle/onebuildpuzzle.h
@@ -54,21 +54,25 @@ protected:
 		Piece() : RenderObject(0) {}
 
 		// File data
-		Common::Rect srcRect;       // Source rect in source image
-		Common::Rect slotRect;      // Correct placement rect (viewport coords)
-		Common::Rect homeRect;      // Starting position (viewport coords)
-		uint8 defaultRotation = 0;  // Rotation index that fits the slot
-		bool isPreRotated = false;  // Piece starts already in place (slotRect position)
+		Common::Rect srcRect;
+		Common::Rect altSrcRect;    // At-home art, shown until first pickup
+		Common::Rect slotRect;
+		Common::Rect homeRect;
+		uint8 defaultRotation = 0;
+		bool isPreRotated = false;
 
 		// Runtime
 		Common::Rect gameRect;      // Current viewport-space rect
 		int curRotation = 0;
 		bool placed = false;
 
-		// Up to 4 rotation surfaces (rotation 1-3 only exist if canRotateAll or isPreRotated)
+		// Rotations 1-3 only built when canRotateAll or isPreRotated
 		Graphics::ManagedSurface rotateSurfaces[4];
 		bool hasSurface[4] = {};
 
+		Graphics::ManagedSurface altSurface;
+		bool useAltSurface = false;
+
 		void setZ(uint16 z) { _z = z; _needsRedraw = true; }
 
 		bool isViewportRelative() const override { return true; }



From noreply at scummvm.org  Wed Jun 17 01:32:26 2026
From: noreply at scummvm.org (bluegr)
Date: Wed, 17 Jun 2026 01:32:26 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 6a3bf6f80ba92122ed51ee368de10c52d51d5712
Message-ID: <20260617013226.24FD97005BC@ron.scummvm.net>

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:
d5b055df85 NANCY: Fix cursors set by game scripts in Nancy10+
6a3bf6f80b NANCY: Fixes for the notebook popup in Nancy10+


Commit: d5b055df8501e6b233195d2a7525bb4f692df12d
    https://github.com/scummvm/scummvm/commit/d5b055df8501e6b233195d2a7525bb4f692df12d
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-06-17T04:27:30+03:00

Commit Message:
NANCY: Fix cursors set by game scripts in Nancy10+

Changed paths:
    engines/nancy/cursor.cpp


diff --git a/engines/nancy/cursor.cpp b/engines/nancy/cursor.cpp
index 6e9ad58c165..68c3e3c098f 100644
--- a/engines/nancy/cursor.cpp
+++ b/engines/nancy/cursor.cpp
@@ -147,8 +147,13 @@ uint CursorManager::resolveNancy10CursorID(CursorType type, int16 itemID, bool s
 		return itemsOffset + (uint)itemID * 2 + variant;
 	}
 
-	if (setFromScript)
-		return type;
+	if (setFromScript) {
+		// Scripts store a raw cursor type number T, while the chunk lays
+		// each type out as a [idle, hotspot] pair (slots T*2 and T*2+1).
+		// Script cursors are only ever applied while hovering a hotspot,
+		// so we always pick the hotspot variant.
+		return (uint)type * 2 + 1;
+	}
 
 	// System cursors: translate the legacy CursorType to the matching
 	// kNew* idle slot. Each Nancy 10+ cursor type T occupies a pair


Commit: 6a3bf6f80ba92122ed51ee368de10c52d51d5712
    https://github.com/scummvm/scummvm/commit/6a3bf6f80ba92122ed51ee368de10c52d51d5712
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-06-17T04:31:39+03:00

Commit Message:
NANCY: Fixes for the notebook popup in Nancy10+

- Remove hardcoded text for senior detective tasks page
- Fixes for notebook checkboxes

Changed paths:
    engines/nancy/ui/notebookpopup.cpp


diff --git a/engines/nancy/ui/notebookpopup.cpp b/engines/nancy/ui/notebookpopup.cpp
index 73037a51983..9e46c12d915 100644
--- a/engines/nancy/ui/notebookpopup.cpp
+++ b/engines/nancy/ui/notebookpopup.cpp
@@ -442,12 +442,12 @@ void NotebookPopup::buildTextLines() {
 
 	const CVTX *autotext = (const CVTX *)g_nancy->getEngineData("AUTOTEXT");
 
-	// Senior-detective Tasks page: hide the to-do list and show the
-	// AUTOTEXT placeholder body instead.
-	if (surfaceID == kNotebookTabTasks && NancySceneState.getDifficulty() == 2) {
-		// TODO: This is specific for Nancy10, adapt it for others, too
-		if (autotext->texts.contains("SHAT70")) {
-			addTextLine(autotext->texts["SHAT70"]);
+	// Senior-detective Tasks: chunk supplies a CVTX placeholder.
+	if (surfaceID == kNotebookTabTasks && NancySceneState.getDifficulty() != 0 &&
+			_uinbData->useFilenameTextFlag != 0 && autotext) {
+		Common::String key = _uinbData->conditionalTextFilename.toString();
+		if (!key.empty() && autotext->texts.contains(key)) {
+			addTextLine(autotext->texts[key]);
 		}
 		return;
 	}
@@ -460,11 +460,16 @@ void NotebookPopup::buildTextLines() {
 		Common::String stringID = entries[i].stringID;
 		Common::String body = getTextFromCaseInsensitiveKey(autotext->texts, stringID);
 
-		// Tasks are prefixed with a checkbox showing completion state.
-		// mark % 10 == 8 means "complete".
-		if (surfaceID == kNotebookTabTasks) {
-			const uint16 markStatus = entries[i].mark % 10;
-			body = Common::String(markStatus == 8 ? "<2>" : "<1>") + body;
+		// Task rows get a `<N>` prefix that turns into a MARK sprite;
+		// the "complete" sentinel (8) maps to secondaryFontAttr.
+		if (surfaceID == kNotebookTabTasks && entries[i].mark != 0) {
+			uint16 markValue = entries[i].mark;
+			if (markValue == 8) {
+				markValue = _uinbData->secondaryFontAttr;
+			}
+			if (markValue >= 1 && markValue <= 5) {
+				body = Common::String::format("<%u>", markValue) + body;
+			}
 		}
 
 		addTextLine(body);



From noreply at scummvm.org  Wed Jun 17 04:28:44 2026
From: noreply at scummvm.org (mikrosk)
Date: Wed, 17 Jun 2026 04:28:44 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 07b409e46d635ecd180611afb0c886c4cd6b4c8c
Message-ID: <20260617042844.767EF7005BC@ron.scummvm.net>

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:
aa0ca1e937 BACKENDS: ATARI: Last touches before release
07b409e46d NEWS: Add forgotten Atari changes


Commit: aa0ca1e937fa2168b6f280b9f0e6265c51cf0b0d
    https://github.com/scummvm/scummvm/commit/aa0ca1e937fa2168b6f280b9f0e6265c51cf0b0d
Author: Miro Kropacek (miro.kropacek at gmail.com)
Date: 2026-06-17T14:27:27+10:00

Commit Message:
BACKENDS: ATARI: Last touches before release

Changed paths:
    backends/platform/atari/readme.txt
    backends/platform/atari/readme.txt.in


diff --git a/backends/platform/atari/readme.txt b/backends/platform/atari/readme.txt
index 168c067dd2f..6d93f6e5bdc 100644
--- a/backends/platform/atari/readme.txt
+++ b/backends/platform/atari/readme.txt
@@ -499,11 +499,9 @@ distributed with repackaged theme files with compression level zero.
 Changes to upstream
 -------------------
 
-There are a few features that have been disabled or changed and are not
-possible / plausible to merge into upstream:
-
-- This port contains an experimental / pending optimisations to audio mixing.
-  I'll try to get them merged in the next release.
+- This port contains experimental / pending optimisations to audio mixing
+  (https://github.com/scummvm/scummvm/pull/7385). I'll try to get them merged
+  in the next release.
 
 
 Known issues
diff --git a/backends/platform/atari/readme.txt.in b/backends/platform/atari/readme.txt.in
index 59787190947..99a2fd896d6 100644
--- a/backends/platform/atari/readme.txt.in
+++ b/backends/platform/atari/readme.txt.in
@@ -499,11 +499,9 @@ distributed with repackaged theme files with compression level zero.
 Changes to upstream
 -------------------
 
-There are a few features that have been disabled or changed and are not
-possible / plausible to merge into upstream:
-
-- This port contains an experimental / pending optimisations to audio mixing.
-  I'll try to get them merged in the next release.
+- This port contains experimental / pending optimisations to audio mixing
+  (https://github.com/scummvm/scummvm/pull/7385). I'll try to get them merged
+  in the next release.
 
 
 Known issues


Commit: 07b409e46d635ecd180611afb0c886c4cd6b4c8c
    https://github.com/scummvm/scummvm/commit/07b409e46d635ecd180611afb0c886c4cd6b4c8c
Author: Miro Kropacek (miro.kropacek at gmail.com)
Date: 2026-06-17T14:28:37+10:00

Commit Message:
NEWS: Add forgotten Atari changes

Changed paths:
    NEWS.md


diff --git a/NEWS.md b/NEWS.md
index 64a26cb29a0..e93aa095b05 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -70,6 +70,8 @@ Atari port:
    - Added native CDDA support.
    - Added plugin support (now ScummVM can run with as little as a few megs of RAM).
    - Added native YM2149 support (currently usable for Elvira 1 and 2).
+   - Reworked audio mixer (this fixes the barking poodles in SOMI).
+   - Autosave is disabled by default as it leads to audible artefacts during gameplay.
 
  PS3 port:
    - Added support for running ScummVM engines as separate modules/executables.



From noreply at scummvm.org  Wed Jun 17 06:45:49 2026
From: noreply at scummvm.org (bluegr)
Date: Wed, 17 Jun 2026 06:45:49 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 6a5ff0ca40adfa3a64b0c573186ffbad36552ba3
Message-ID: <20260617064549.452B97005BC@ron.scummvm.net>

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

Summary:
6a5ff0ca40 NANCY: Correct handling of the Nancy10+ SecondaryVideo dependency block


Commit: 6a5ff0ca40adfa3a64b0c573186ffbad36552ba3
    https://github.com/scummvm/scummvm/commit/6a5ff0ca40adfa3a64b0c573186ffbad36552ba3
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-06-17T09:45:41+03:00

Commit Message:
NANCY: Correct handling of the Nancy10+ SecondaryVideo dependency block

Fixes the character availability checks in Nancy 10, thus the relevant
hack can be removed now

Changed paths:
    engines/nancy/action/secondaryvideo.cpp


diff --git a/engines/nancy/action/secondaryvideo.cpp b/engines/nancy/action/secondaryvideo.cpp
index ff51376a40d..c1547957c57 100644
--- a/engines/nancy/action/secondaryvideo.cpp
+++ b/engines/nancy/action/secondaryvideo.cpp
@@ -210,11 +210,11 @@ void PlaySecondaryVideo::readData(Common::SeekableReadStream &stream) {
 		_videoDescs[i].readData(stream);
 	}
 
-	// Nancy 10+ tail: 16 trailing bytes after the video descs. Purpose
-	// not yet identified.
-	if (g_nancy->getGameType() >= kGameTypeNancy10) {
-		stream.skip(16);
-	}
+	// Nancy 10+ stores the record's dependency block right after the video
+	// descs (one 16-byte entry per dependency). We deliberately leave it in
+	// the stream so the generic dependency parser in ActionManager picks it
+	// up; characters whose availability is gated by an event flag carry a
+	// kEvent dependency here.
 }
 
 void PlaySecondaryVideo::execute() {
@@ -241,18 +241,6 @@ void PlaySecondaryVideo::execute() {
 				_currentViewportFrame = -1;
 			}
 
-			// HACK: Checks for character availability in Nancy10. These are
-			// currently not handled correctly, so we hardcode them here at the moment.
-			// TODO: Find out why these are not handled correctly and implement a
-			// proper solution for them.
-			if (g_nancy->getGameType() == kGameTypeNancy10) {
-				uint16 sceneId = NancySceneState.getSceneInfo().sceneID;
-				if ((sceneId == 2307 && NancySceneState.getEventFlag(556, g_nancy->_false)) || // EV_ST_Available
-					(sceneId == 2605 && NancySceneState.getEventFlag(588, g_nancy->_false)) || // EV_TB_Available
-					(sceneId == 2915 && NancySceneState.getEventFlag(156, g_nancy->_false))) // EV_DG_Available
-					_currentViewportFrame = -1;
-			}
-
 			if (_currentViewportFrame != -1) {
 				if (!_isInFrame) {
 					_decoder.start();



From noreply at scummvm.org  Wed Jun 17 10:32:27 2026
From: noreply at scummvm.org (sev-)
Date: Wed, 17 Jun 2026 10:32:27 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 493b6b4858f2edd527df053abbf4d25c5da406b3
Message-ID: <20260617103227.B181A7005BC@ron.scummvm.net>

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

Summary:
b8c8008cbb DIRECTOR: XOBJ: Add stubs for StageControl + Widget
bbf1b009b0 DIRECTOR: Properly rectify tile pixel access
32f7fe1f0c DIRECTOR: Fix RIFXArchive::writeToFile corruption
b80b0cb62c DIRECTOR: Only lock modals if the window is visible
5e41663efe DIRECTOR: XOBJ: Add XFCNs from Yellow Brick Road
493b6b4858 MACGUI: Fix keydown event processing if callback is undefined


Commit: b8c8008cbbdd7887cf795ef10a950b5f70758295
    https://github.com/scummvm/scummvm/commit/b8c8008cbbdd7887cf795ef10a950b5f70758295
Author: Scott Percival (code at moral.net.au)
Date: 2026-06-17T12:32:20+02:00

Commit Message:
DIRECTOR: XOBJ: Add stubs for StageControl + Widget

Changed paths:
  A engines/director/lingo/xlibs/s/stagectl.cpp
  A engines/director/lingo/xlibs/s/stagectl.h
  A engines/director/lingo/xlibs/w/widget.cpp
  A engines/director/lingo/xlibs/w/widget.h
  R engines/director/lingo/xlibs/w/widgetxobj.cpp
  R engines/director/lingo/xlibs/w/widgetxobj.h
    engines/director/lingo/lingo-object.cpp
    engines/director/module.mk


diff --git a/engines/director/lingo/lingo-object.cpp b/engines/director/lingo/lingo-object.cpp
index b1002a6d565..17bda976d23 100644
--- a/engines/director/lingo/lingo-object.cpp
+++ b/engines/director/lingo/lingo-object.cpp
@@ -132,6 +132,7 @@
 #include "director/lingo/xlibs/s/stagetc.h"
 #include "director/lingo/xlibs/s/syscolor.h"
 #include "director/lingo/xlibs/s/savenrestorexobj.h"
+#include "director/lingo/xlibs/s/stagectl.h"
 #include "director/lingo/xlibs/t/tengu.h"
 #include "director/lingo/xlibs/t/temnotaxobj.h"
 #include "director/lingo/xlibs/u/unittest.h"
@@ -142,7 +143,7 @@
 #include "director/lingo/xlibs/v/vmpresent.h"
 #include "director/lingo/xlibs/v/volumelist.h"
 #include "director/lingo/xlibs/v/voyagerxsound.h"
-#include "director/lingo/xlibs/w/widgetxobj.h"
+#include "director/lingo/xlibs/w/widget.h"
 #include "director/lingo/xlibs/w/window.h"
 #include "director/lingo/xlibs/w/wininfo.h"
 #include "director/lingo/xlibs/w/winxobj.h"
@@ -369,6 +370,7 @@ static const struct XLibProto {
 	XLIBDEF(SmallUtilXObj,		kXObj,			400),	// D4
 	XLIBDEF(SoundJam,			kXObj,			400),	// D4
 	XLIBDEF(SpaceMgr,			kXObj,			400),	// D4
+	XLIBDEF(StageControlXObj,			kXObj,					300),	// D3
 	XLIBDEF(StageTCXObj,		kXObj,			400),	// D4
 	XLIBDEF(StayToonedBallXtra,			kXtraObj,					500),	// D5
 	XLIBDEF(StayToonedGlopXtra,			kXtraObj,					500),	// D5
@@ -388,8 +390,8 @@ static const struct XLibProto {
 	XLIBDEF(VideodiscXObj,		kXObj,			200),	// D2
 	XLIBDEF(VolumeList,			kXObj,			300),	// D3
 	XLIBDEF(VoyagerXSoundXObj,	kXObj,			400),	// D4
+	XLIBDEF(WidgetXObj,			kXObj,					300),	// D3
 	XLIBDEF(WinInfoXObj,		kXObj,			400),	// D4
-	XLIBDEF(WidgetXObj, 		kXObj,			400),	// D4
 	XLIBDEF(WindowXObj,			kXObj,			200),	// D2
 	XLIBDEF(XCMDGlueXObj,		kXObj,			200),	// D2
 	XLIBDEF(XPlayPACoXFCN,		kXObj,			300),	// D3
diff --git a/engines/director/lingo/xlibs/s/stagectl.cpp b/engines/director/lingo/xlibs/s/stagectl.cpp
new file mode 100644
index 00000000000..5090a16aa1b
--- /dev/null
+++ b/engines/director/lingo/xlibs/s/stagectl.cpp
@@ -0,0 +1,107 @@
+/* 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/system.h"
+
+#include "director/director.h"
+#include "director/lingo/lingo.h"
+#include "director/lingo/lingo-object.h"
+#include "director/lingo/lingo-utils.h"
+#include "director/lingo/xlibs/s/stagectl.h"
+
+/**************************************************
+ *
+ * USED IN:
+ * Yellow Brick Road
+ *
+ **************************************************/
+
+/*
+-- Category Misc.
+Title Stage Control External Factory
+(C)COPYRIGHT IBM CORP. 1993 $Revision
+--StageControl
+I      mNew                --Creates a new instance of the XObject
+X      mDispose            --Disposes of XObject instance
+S      mName               --Returns the XObject name
+II     mNoisy              --Sets noisy flag of the receiver, and returns previous value
+I      mNumberOfInstances  --Returns Number of Module Instances
+X      mBecomeSystemModal  --StageWindow becomes System Modal
+ */
+
+namespace Director {
+
+const char *StageControlXObj::xlibName = "StageControl";
+const XlibFileDesc StageControlXObj::fileNames[] = {
+	{ "STAGECTL",   nullptr },
+	{ nullptr,        nullptr },
+};
+
+static MethodProto xlibMethods[] = {
+	{ "new",				StageControlXObj::m_new,		 0, 0,	300 },
+	{ "dispose",				StageControlXObj::m_dispose,		 0, 0,	300 },
+	{ "name",				StageControlXObj::m_name,		 0, 0,	300 },
+	{ "noisy",				StageControlXObj::m_noisy,		 1, 1,	300 },
+	{ "numberOfInstances",				StageControlXObj::m_numberOfInstances,		 0, 0,	300 },
+	{ "becomeSystemModal",				StageControlXObj::m_becomeSystemModal,		 0, 0,	300 },
+
+	{ nullptr, nullptr, 0, 0, 0 }
+};
+
+static BuiltinProto xlibBuiltins[] = {
+
+	{ nullptr, nullptr, 0, 0, 0, VOIDSYM }
+};
+
+StageControlXObject::StageControlXObject(ObjectType ObjectType) :Object<StageControlXObject>("StageControl") {
+	_objType = ObjectType;
+}
+
+void StageControlXObj::open(ObjectType type, const Common::Path &path) {
+    StageControlXObject::initMethods(xlibMethods);
+    StageControlXObject *xobj = new StageControlXObject(type);
+    if (type == kXtraObj) {
+        g_lingo->_openXtras.push_back(xlibName);
+		g_lingo->_openXtraObjects.push_back(xobj);
+	}
+    g_lingo->exposeXObject(xlibName, xobj);
+    g_lingo->initBuiltIns(xlibBuiltins);
+}
+
+void StageControlXObj::close(ObjectType type) {
+    StageControlXObject::cleanupMethods();
+    g_lingo->_globalvars[xlibName] = Datum();
+
+}
+
+void StageControlXObj::m_new(int nargs) {
+	g_lingo->printSTUBWithArglist("StageControlXObj::m_new", nargs);
+	g_lingo->dropStack(nargs);
+	g_lingo->push(g_lingo->_state->me);
+}
+
+XOBJSTUBNR(StageControlXObj::m_dispose)
+XOBJSTUB(StageControlXObj::m_name, "")
+XOBJSTUB(StageControlXObj::m_noisy, 0)
+XOBJSTUB(StageControlXObj::m_numberOfInstances, 0)
+XOBJSTUBNR(StageControlXObj::m_becomeSystemModal)
+
+}
diff --git a/engines/director/lingo/xlibs/w/widgetxobj.h b/engines/director/lingo/xlibs/s/stagectl.h
similarity index 73%
rename from engines/director/lingo/xlibs/w/widgetxobj.h
rename to engines/director/lingo/xlibs/s/stagectl.h
index 411e4074655..2d914970ac5 100644
--- a/engines/director/lingo/xlibs/w/widgetxobj.h
+++ b/engines/director/lingo/xlibs/s/stagectl.h
@@ -19,19 +19,19 @@
  *
  */
 
-#ifndef DIRECTOR_LINGO_XLIBS_WIDGETXOBJ_H
-#define DIRECTOR_LINGO_XLIBS_WIDGETXOBJ_H
+#ifndef DIRECTOR_LINGO_XLIBS_S_STAGECTL_H
+#define DIRECTOR_LINGO_XLIBS_S_STAGECTL_H
 
 namespace Director {
 
-class WidgetXObject : public Object<WidgetXObject> {
+class StageControlXObject : public Object<StageControlXObject> {
 public:
-	WidgetXObject(ObjectType objType);
+	StageControlXObject(ObjectType objType);
 };
 
-namespace WidgetXObj {
+namespace StageControlXObj {
 
-extern const char *const xlibName;
+extern const char *xlibName;
 extern const XlibFileDesc fileNames[];
 
 void open(ObjectType type, const Common::Path &path);
@@ -39,10 +39,12 @@ void close(ObjectType type);
 
 void m_new(int nargs);
 void m_dispose(int nargs);
-void m_getPro(int nargs);
-void m_askQuit(int nargs);
+void m_name(int nargs);
+void m_noisy(int nargs);
+void m_numberOfInstances(int nargs);
+void m_becomeSystemModal(int nargs);
 
-} // End of namespace WidgetXObj
+} // End of namespace StageControlXObj
 
 } // End of namespace Director
 
diff --git a/engines/director/lingo/xlibs/w/widget.cpp b/engines/director/lingo/xlibs/w/widget.cpp
new file mode 100644
index 00000000000..9120893b146
--- /dev/null
+++ b/engines/director/lingo/xlibs/w/widget.cpp
@@ -0,0 +1,157 @@
+/* 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/system.h"
+
+#include "director/director.h"
+#include "director/lingo/lingo.h"
+#include "director/lingo/lingo-object.h"
+#include "director/lingo/lingo-utils.h"
+#include "director/lingo/xlibs/w/widget.h"
+
+/**************************************************
+ *
+ * USED IN:
+ * [insert game here]
+ *
+ **************************************************/
+
+/*
+-- Widget External Factory. 16Feb93 PTM
+--Widget
+IS     mNew, FileName      --Creates a new instance of the XObject
+X      mDispose            --Disposes of XObject instance
+S      mName               --Returns the XObject name (Widget)
+I      mStatus             --Returns an integer status code
+SI     mError, code        --Returns an error string
+S      mLastError          --Returns last error string
+III    mAdd, arg1, arg2    --Returns arg1+arg2
+I      mAskQuit             --Returns an integer status code
+SSI    mFirst, str, nchars --Return the first nchars of string str
+ISI    mLipSync, str, nchars --Return the first nchars of string str
+I      mLoadData             --Returns an integer status code
+II     mSaveData place       --Returns an integer status code
+I		mHideCursor				--Returns Status
+I		mShowCursor				--Returns Status
+V      mMul, f1, f2        --Returns f1*f2 as floating point
+X      mGlobals            --Sample code to Read & Modify globals
+X      mSymbols            --Sample code to work with Symbols
+X      mSendPerform        --Sample code to show SendPerform call
+X      mFactory            --Sample code to find Factory objects
+ */
+
+namespace Director {
+
+const char *WidgetXObj::xlibName = "Widget";
+const XlibFileDesc WidgetXObj::fileNames[] = {
+	{ "Widget",   nullptr },
+	{ nullptr,        nullptr },
+};
+
+static MethodProto xlibMethods[] = {
+	{ "new",				WidgetXObj::m_new,		 1, 1,	300 },
+	{ "dispose",				WidgetXObj::m_dispose,		 0, 0,	300 },
+	// Yellow Brick Road
+	{ "name",				WidgetXObj::m_name,		 0, 0,	300 },
+	{ "status",				WidgetXObj::m_status,		 0, 0,	300 },
+	{ "error",				WidgetXObj::m_error,		 1, 1,	300 },
+	{ "lastError",				WidgetXObj::m_lastError,		 0, 0,	300 },
+	{ "add",				WidgetXObj::m_add,		 2, 2,	300 },
+	{ "askQuit",				WidgetXObj::m_askQuit,		 0, 0,	300 },
+	{ "first",				WidgetXObj::m_first,		 2, 2,	300 },
+	{ "lipSync",				WidgetXObj::m_lipSync,		 2, 2,	300 },
+	{ "loadData",				WidgetXObj::m_loadData,		 0, 0,	300 },
+	{ "saveData",				WidgetXObj::m_saveData,		 1, 1,	300 },
+	{ "hideCursor",				WidgetXObj::m_hideCursor,		 0, 0,	300 },
+	{ "showCursor",				WidgetXObj::m_showCursor,		 0, 0,	300 },
+	{ "mul",				WidgetXObj::m_mul,		 0, 0,	300 },
+	{ "globals",				WidgetXObj::m_globals,		 0, 0,	300 },
+	{ "symbols",				WidgetXObj::m_symbols,		 0, 0,	300 },
+	{ "sendPerform",				WidgetXObj::m_sendPerform,		 0, 0,	300 },
+	{ "factory",				WidgetXObj::m_factory,		 0, 0,	300 },
+	// Alice
+	{ "GetPro",				WidgetXObj::m_getPro,		0, 0,	300 },	// D4
+
+	{ nullptr, nullptr, 0, 0, 0 }
+};
+
+static BuiltinProto xlibBuiltins[] = {
+
+	{ nullptr, nullptr, 0, 0, 0, VOIDSYM }
+};
+
+WidgetXObject::WidgetXObject(ObjectType ObjectType) :Object<WidgetXObject>("Widget") {
+	_objType = ObjectType;
+}
+
+void WidgetXObj::open(ObjectType type, const Common::Path &path) {
+    WidgetXObject::initMethods(xlibMethods);
+    WidgetXObject *xobj = new WidgetXObject(type);
+    if (type == kXtraObj) {
+        g_lingo->_openXtras.push_back(xlibName);
+		g_lingo->_openXtraObjects.push_back(xobj);
+	}
+    g_lingo->exposeXObject(xlibName, xobj);
+    g_lingo->initBuiltIns(xlibBuiltins);
+}
+
+void WidgetXObj::close(ObjectType type) {
+    WidgetXObject::cleanupMethods();
+    g_lingo->_globalvars[xlibName] = Datum();
+
+}
+
+void WidgetXObj::m_new(int nargs) {
+	g_lingo->printSTUBWithArglist("WidgetXObj::m_new", nargs);
+	g_lingo->dropStack(nargs);
+	g_lingo->push(g_lingo->_state->me);
+}
+
+
+XOBJSTUBNR(WidgetXObj::m_dispose)
+XOBJSTUB(WidgetXObj::m_name, "")
+XOBJSTUB(WidgetXObj::m_status, 0)
+XOBJSTUB(WidgetXObj::m_error, "")
+XOBJSTUB(WidgetXObj::m_lastError, "")
+XOBJSTUB(WidgetXObj::m_add, 0)
+XOBJSTUB(WidgetXObj::m_askQuit, 0)
+XOBJSTUB(WidgetXObj::m_first, "")
+
+// 100 means the animation is done
+XOBJSTUB(WidgetXObj::m_lipSync, 100)
+
+XOBJSTUB(WidgetXObj::m_loadData, 0)
+XOBJSTUB(WidgetXObj::m_saveData, 0)
+XOBJSTUB(WidgetXObj::m_hideCursor, 0)
+XOBJSTUB(WidgetXObj::m_showCursor, 0)
+XOBJSTUB(WidgetXObj::m_mul, 0)
+XOBJSTUBNR(WidgetXObj::m_globals)
+XOBJSTUBNR(WidgetXObj::m_symbols)
+XOBJSTUBNR(WidgetXObj::m_sendPerform)
+XOBJSTUBNR(WidgetXObj::m_factory)
+
+void WidgetXObj::m_getPro(int nargs) {
+	// seems to want a disk drive letter
+	g_lingo->push(Datum("D"));
+}
+
+
+}
diff --git a/engines/director/lingo/xlibs/w/widget.h b/engines/director/lingo/xlibs/w/widget.h
new file mode 100644
index 00000000000..4d7af06e124
--- /dev/null
+++ b/engines/director/lingo/xlibs/w/widget.h
@@ -0,0 +1,66 @@
+/* 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 DIRECTOR_LINGO_XLIBS_W_WIDGET_H
+#define DIRECTOR_LINGO_XLIBS_W_WIDGET_H
+
+namespace Director {
+
+class WidgetXObject : public Object<WidgetXObject> {
+public:
+	WidgetXObject(ObjectType objType);
+};
+
+namespace WidgetXObj {
+
+extern const char *xlibName;
+extern const XlibFileDesc fileNames[];
+
+void open(ObjectType type, const Common::Path &path);
+void close(ObjectType type);
+
+void m_new(int nargs);
+void m_dispose(int nargs);
+void m_name(int nargs);
+void m_status(int nargs);
+void m_error(int nargs);
+void m_lastError(int nargs);
+void m_add(int nargs);
+void m_askQuit(int nargs);
+void m_first(int nargs);
+void m_lipSync(int nargs);
+void m_loadData(int nargs);
+void m_saveData(int nargs);
+void m_hideCursor(int nargs);
+void m_showCursor(int nargs);
+void m_mul(int nargs);
+void m_globals(int nargs);
+void m_symbols(int nargs);
+void m_sendPerform(int nargs);
+void m_factory(int nargs);
+
+void m_getPro(int nargs);
+
+} // End of namespace WidgetXObj
+
+} // End of namespace Director
+
+#endif
diff --git a/engines/director/lingo/xlibs/w/widgetxobj.cpp b/engines/director/lingo/xlibs/w/widgetxobj.cpp
deleted file mode 100644
index d22b60fb761..00000000000
--- a/engines/director/lingo/xlibs/w/widgetxobj.cpp
+++ /dev/null
@@ -1,97 +0,0 @@
-/* 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/>.
- *
- */
-
-/*************************************
- *
- * USED IN:
- * Alice: An Interactive Museum
- *
- *************************************/
-
-/*
- * Widget External Factory
- *
- * Widget
- * I      mNew                --Creates a new instance of the XObject
- * X      mDispose            --Disposes of XObject instance
- * S      mGetPro             --
- * I      mAskQuit            --
- */
-
-#include "director/director.h"
-#include "director/lingo/lingo.h"
-#include "director/lingo/lingo-object.h"
-#include "director/lingo/lingo-utils.h"
-#include "director/lingo/xlibs/w/widgetxobj.h"
-
-namespace Director {
-
-
-const char *const WidgetXObj::xlibName = "Widget";
-const XlibFileDesc WidgetXObj::fileNames[] = {
-	{ "widget",	nullptr },
-	{ nullptr,	nullptr },
-};
-
-static const MethodProto xlibMethods[] = {
-	{ "new",				WidgetXObj::m_new,			0, 0,	400 },	// D4
-	{ "Dispose",			WidgetXObj::m_dispose,		0, 0,	400 },	// D4
-	{ "GetPro",				WidgetXObj::m_getPro,		0, 0,	400 },	// D4
-	{ "AskQuit",			WidgetXObj::m_askQuit,		0, 0,	400 },	// D4
-	{ nullptr, nullptr, 0, 0, 0 }
-};
-
-void WidgetXObj::open(ObjectType type, const Common::Path &path) {
-	if (type == kXObj) {
-		WidgetXObject::initMethods(xlibMethods);
-		WidgetXObject *xobj = new WidgetXObject(kXObj);
-		g_lingo->exposeXObject(xlibName, xobj);
-	}
-}
-
-void WidgetXObj::close(ObjectType type) {
-	if (type == kXObj) {
-		WidgetXObject::cleanupMethods();
-		g_lingo->_globalvars[xlibName] = Datum();
-	}
-}
-
-WidgetXObject::WidgetXObject(ObjectType ObjectType) :Object<WidgetXObject>("Widget") {
-	_objType = ObjectType;
-}
-
-void WidgetXObj::m_new(int nargs) {
-	g_lingo->push(g_lingo->_state->me);
-}
-
-void WidgetXObj::m_dispose(int nargs) {
-	g_lingo->printSTUBWithArglist("WidgetXObj::m_dispose", nargs);
-	g_lingo->dropStack(nargs);
-}
-
-void WidgetXObj::m_getPro(int nargs) {
-	// seems to want a disk drive letter
-	g_lingo->push(Datum("D"));
-}
-
-XOBJSTUB(WidgetXObj::m_askQuit, 0)
-
-} // End of namespace Director
diff --git a/engines/director/module.mk b/engines/director/module.mk
index 0b12b8920b5..de2681f1340 100644
--- a/engines/director/module.mk
+++ b/engines/director/module.mk
@@ -138,6 +138,7 @@ MODULE_OBJS = \
 	lingo/xlibs/m/myfolder.o \
 	lingo/xlibs/g/getuinfo.o \
 	lingo/xlibs/o/openbleedwindowxcmd.o \
+	lingo/xlibs/s/stagectl.o \
  	lingo/xlibs/p/playsoundmoviexobj.o \
  	lingo/xlibs/s/savenrestorexobj.o \
  	lingo/xlibs/t/temnotaxobj.o \
@@ -174,7 +175,7 @@ MODULE_OBJS = \
 	lingo/xlibs/v/vmpresent.o \
 	lingo/xlibs/v/volumelist.o \
 	lingo/xlibs/v/voyagerxsound.o \
-	lingo/xlibs/w/widgetxobj.o \
+	lingo/xlibs/w/widget.o \
 	lingo/xlibs/w/window.o \
 	lingo/xlibs/w/winxobj.o \
 	lingo/xlibs/w/wininfo.o \


Commit: bbf1b009b00c37b7681e69df71844e33073a7a45
    https://github.com/scummvm/scummvm/commit/bbf1b009b00c37b7681e69df71844e33073a7a45
Author: Scott Percival (code at moral.net.au)
Date: 2026-06-17T12:32:20+02:00

Commit Message:
DIRECTOR: Properly rectify tile pixel access

Fixes out of bounds memory access when starting Yellow Brick Road.

Changed paths:
    engines/director/director.h
    engines/director/graphics.cpp


diff --git a/engines/director/director.h b/engines/director/director.h
index 9b506c8bd43..eab89866cd8 100644
--- a/engines/director/director.h
+++ b/engines/director/director.h
@@ -396,7 +396,8 @@ struct DirectorPlotData {
 	DirectorPlotData &operator=(const DirectorPlotData &);
 
 	~DirectorPlotData() {
-		delete ms;
+		if (ms)
+			delete ms;
 	}
 };
 
diff --git a/engines/director/graphics.cpp b/engines/director/graphics.cpp
index 0230c54b70a..a1cf07d0c92 100644
--- a/engines/director/graphics.cpp
+++ b/engines/director/graphics.cpp
@@ -334,8 +334,12 @@ void InkPrimitives<T>::drawPoint(int x, int y, uint32 src, void *data) {
 		}
 
 		if (p->ms->tile) {
-			int x1 = p->ms->tileRect->left + (p->ms->pd->fillOriginX + x) % p->ms->tileRect->width();
-			int y1 = p->ms->tileRect->top  + (p->ms->pd->fillOriginY + y) % p->ms->tileRect->height();
+			int x1 = (p->ms->tileRect->left + p->ms->pd->fillOriginX + x) % p->ms->tileRect->width();
+			if (x1 < 0)
+				x1 += p->ms->tileRect->width();
+			int y1 = (p->ms->tileRect->top  + p->ms->pd->fillOriginY + y) % p->ms->tileRect->height();
+			if (y1 < 0)
+				y1 += p->ms->tileRect->height();
 
 			src = p->ms->tile->_surface.getPixel(x1, y1);
 		} else {


Commit: 32f7fe1f0c738b0b385a77ab4e72826a52e267ec
    https://github.com/scummvm/scummvm/commit/32f7fe1f0c738b0b385a77ab4e72826a52e267ec
Author: Scott Percival (code at moral.net.au)
Date: 2026-06-17T12:32:20+02:00

Commit Message:
DIRECTOR: Fix RIFXArchive::writeToFile corruption

Previously, opening a movie from the save file storage would use a
FSNode for providing data from the archive. In the event of writing the
movie to the same path, one of the first steps would be to create a
Common::OutSaveFile which would break the existing FSNode, and
subsequently every Archive::getResource call would return
nothing/garbage. This was fixed by buffering movie data from the save path
with a Common::MemoryReadStream.

Fixes saving in Opera Fatal's unhinged save game system.

Changed paths:
    engines/director/archive-save.cpp
    engines/director/archive.h
    engines/director/lingo/xlibs/f/fileio.cpp


diff --git a/engines/director/archive-save.cpp b/engines/director/archive-save.cpp
index 4113f072204..1e2fbd43db9 100644
--- a/engines/director/archive-save.cpp
+++ b/engines/director/archive-save.cpp
@@ -189,6 +189,8 @@ bool RIFXArchive::writeToFile(Common::String filename, Movie *movie) {
 	} else {
 		warning("RIFXArchive::writeStream: Error saving the file %s", saveFileName.c_str());
 	}
+	// Add to search index
+	((SavedArchive *)SearchMan.getArchive(kSavedFilesArchive))->_addFile(saveFileName);
 
 	delete saveFile;
 	for (auto it : builtResources) {
@@ -691,17 +693,13 @@ uint32 RIFXArchive::findParentIndex(uint32 tag, uint16 index) {
 	return 0;
 }
 
-SavedArchive::SavedArchive(Common::String target) {
+SavedArchive::SavedArchive(const Common::String &target) {
+	_target = target;
 	Common::StringArray saveFileList = g_engine->getSaveFileManager()->listSavefiles(target + "-*");
 
 	debugC(3, kDebugLoading, "DirectorEngine:: loadSaveFiles: Loading save files");
-	for (auto saveFileName : saveFileList) {
-		// Derive the original file name from the save file name
-		// Save files are named target_name-save_filename
-		Common::String origFileName = saveFileName.substr(target.size() + 1);
-		debugC(3, kDebugLoading, "Found save file: %s -> %s", saveFileName.c_str(), origFileName.c_str());
-
-		_files[origFileName] = saveFileName;
+	for (auto &saveFileName : saveFileList) {
+		_addFile(saveFileName);
 	}
 }
 
@@ -732,7 +730,33 @@ Common::SeekableReadStream *SavedArchive::createReadStreamForMember(const Common
 	if (fDesc == _files.end())
 		return nullptr;
 
-	return g_engine->getSaveFileManager()->openForLoading(fDesc->_value);
+	// Buffer the save file data into memory.
+	// We have to do this because openForLoading will return a stream backed by a
+	// FSNode, and subsequently calling openForSaving on the same file will cause
+	// that stream to become completely empty. RIFXArchive::writeToFile needs to
+	// be able to read from that stream while saving!
+	Common::SeekableReadStream *stream = g_engine->getSaveFileManager()->openForLoading(fDesc->_value);
+	if (!stream)
+		return nullptr;
+
+	byte *data = (byte *)calloc(stream->size(), sizeof(byte));
+	stream->read(data, stream->size());
+	Common::MemoryReadStream *result = new Common::MemoryReadStream(data, stream->size(), DisposeAfterUse::YES);
+	delete stream;
+	return result;
+}
+
+bool SavedArchive::_addFile(const Common::String &fileName) {
+	// Derive the original file name from the save file name
+	// Save files are named target_name-save_filename
+	Common::String origFileName = fileName.substr(_target.size() + 1);
+	if (!_files.contains(origFileName)) {
+		debugC(3, kDebugLoading, "Found save file: %s -> %s", fileName.c_str(), fileName.c_str());
+
+		_files[origFileName] = fileName;
+		return true;
+	}
+	return false;
 }
 
 } // End of namespace Director
diff --git a/engines/director/archive.h b/engines/director/archive.h
index 071c4fa8d44..e7c6c177528 100644
--- a/engines/director/archive.h
+++ b/engines/director/archive.h
@@ -276,15 +276,18 @@ void dumpFile(Common::String filename, uint32 id, uint32 tag, byte *dumpData, ui
 
 class SavedArchive : public Common::Archive {
 public:
-	SavedArchive(Common::String target) ;
+	SavedArchive(const Common::String &target);
 	bool hasFile(const Common::Path &path) const override;
 	int listMembers(Common::ArchiveMemberList &list) const override;
 	const Common::ArchiveMemberPtr getMember(const Common::Path &path) const override;
 	Common::SeekableReadStream *createReadStreamForMember(const Common::Path &path) const override;
 
+	bool _addFile(const Common::String &path);
+
 private:
 	typedef Common::HashMap<Common::String, Common::String, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> FileMap;
 	FileMap _files;
+	Common::String _target;
 };
 
 }
diff --git a/engines/director/lingo/xlibs/f/fileio.cpp b/engines/director/lingo/xlibs/f/fileio.cpp
index aa612552184..76a2cccf891 100644
--- a/engines/director/lingo/xlibs/f/fileio.cpp
+++ b/engines/director/lingo/xlibs/f/fileio.cpp
@@ -128,6 +128,7 @@ delete object me -- deletes the open file
 #include "image/pict.h"
 
 #include "director/director.h"
+#include "director/archive.h"
 #include "director/picture.h"
 #include "director/types.h"
 #include "director/util.h"
@@ -323,6 +324,8 @@ void FileObject::clear() {
 			Common::OutSaveFile *outFile = saves->openForSaving(*_filename, false);
 			outFile->write(_outStream->getData(), _outStream->size());
 			outFile->finalize();
+			// Add to search index
+			((SavedArchive *)SearchMan.getArchive(kSavedFilesArchive))->_addFile(*_filename);
 			delete outFile;
 		}
 		delete _outStream;


Commit: b80b0cb62cea527a325ccd7d1125b649b6170443
    https://github.com/scummvm/scummvm/commit/b80b0cb62cea527a325ccd7d1125b649b6170443
Author: Scott Percival (code at moral.net.au)
Date: 2026-06-17T12:32:20+02:00

Commit Message:
DIRECTOR: Only lock modals if the window is visible

Fixes screen locking up in Opera Fatal's unhinged save game system.

Changed paths:
    engines/director/window.cpp


diff --git a/engines/director/window.cpp b/engines/director/window.cpp
index 63d92adc3e2..a253a7cceb7 100644
--- a/engines/director/window.cpp
+++ b/engines/director/window.cpp
@@ -406,7 +406,8 @@ void Window::setModal(bool modal) {
 		_wm->setLockedWidget(nullptr);
 		_isModal = false;
 	} else if (!_isModal && modal) {
-		_wm->setLockedWidget(this->_window);
+		if (_window->isVisible())
+			_wm->setLockedWidget(this->_window);
 		_isModal = true;
 	}
 }


Commit: 5e41663efed8f807375f5cc4b0b33c1a1e292275
    https://github.com/scummvm/scummvm/commit/5e41663efed8f807375f5cc4b0b33c1a1e292275
Author: Scott Percival (code at moral.net.au)
Date: 2026-06-17T12:32:20+02:00

Commit Message:
DIRECTOR: XOBJ: Add XFCNs from Yellow Brick Road

Changed paths:
  A engines/director/lingo/xlibs/f/flushmousexfcn.cpp
  A engines/director/lingo/xlibs/f/flushmousexfcn.h
  A engines/director/lingo/xlibs/g/getsoundxfcn.cpp
  A engines/director/lingo/xlibs/g/getsoundxfcn.h
    engines/director/lingo/lingo-object.cpp
    engines/director/lingo/xlibs/w/widget.cpp
    engines/director/lingo/xlibs/w/winxobj.cpp
    engines/director/module.mk


diff --git a/engines/director/lingo/lingo-object.cpp b/engines/director/lingo/lingo-object.cpp
index 17bda976d23..4355af923f4 100644
--- a/engines/director/lingo/lingo-object.cpp
+++ b/engines/director/lingo/lingo-object.cpp
@@ -72,12 +72,14 @@
 #include "director/lingo/xlibs/f/flushxobj.h"
 #include "director/lingo/xlibs/f/fplayxobj.h"
 #include "director/lingo/xlibs/f/fsutil.h"
+#include "director/lingo/xlibs/f/flushmousexfcn.h"
 #include "director/lingo/xlibs/g/genutils.h"
 #include "director/lingo/xlibs/g/getscreenrectsxfcn.h"
 #include "director/lingo/xlibs/g/getscreensizexfcn.h"
 #include "director/lingo/xlibs/g/getsoundinlevel.h"
 #include "director/lingo/xlibs/g/gpid.h"
 #include "director/lingo/xlibs/g/getuinfo.h"
+#include "director/lingo/xlibs/g/getsoundxfcn.h"
 #include "director/lingo/xlibs/h/henry.h"
 #include "director/lingo/xlibs/h/hitmap.h"
 #include "director/lingo/xlibs/i/inixobj.h"
@@ -298,6 +300,7 @@ static const struct XLibProto {
 	XLIBDEF(FindSys,			kXObj,			400),	// D4
 	XLIBDEF(FindWin,			kXObj,			400),	// D4
 	XLIBDEF(FinderEventsXCMD,	kXObj,			400),	// D4
+	XLIBDEF(FlushMouseXFCN,			kXObj,					300),	// D3
 	XLIBDEF(FlushXObj,			kXObj,			300),	// D3
 	XLIBDEF(FPlayXObj,			kXObj,			200),	// D2
 	XLIBDEF(GenUtilsXObj,		kXObj,			400),	// D4
@@ -305,6 +308,7 @@ static const struct XLibProto {
 	XLIBDEF(GetScreenRectsXFCN,	kXObj,			300),	// D3
 	XLIBDEF(GetScreenSizeXFCN,	kXObj,			300),	// D3
 	XLIBDEF(GetSoundInLevelXObj,kXObj,			400),	// D4
+	XLIBDEF(GetSoundXFCN,			kXObj,					300),	// D3
 	XLIBDEF(GetUInfoXObj,			kXObj,					400),	// D4
 	XLIBDEF(GpidXObj,			kXObj,			400),	// D4
 	XLIBDEF(HenryXObj,			kXObj,			400),	// D4
@@ -358,7 +362,7 @@ static const struct XLibProto {
 	XLIBDEF(QTVR,				kXObj,			400),	// D4
 	XLIBDEF(QtvrxtraXtra,		kXtraObj,		500),	// D5
 	XLIBDEF(Quicktime,			kXObj,			300),	// D3
-	XLIBDEF(RearWindowXObj,		kXObj,			400),	// D4
+	XLIBDEF(RearWindowXObj,		kXObj,			300),	// D3
 	XLIBDEF(RegisterComponent,	kXObj,			400),	// D4
 	XLIBDEF(RegistryReaderXtra,			kXtraObj,					500),	// D5
 	XLIBDEF(RemixXCMD,			kXObj,			300),	// D3
diff --git a/engines/director/lingo/xlibs/f/flushmousexfcn.cpp b/engines/director/lingo/xlibs/f/flushmousexfcn.cpp
new file mode 100644
index 00000000000..47d4c867a67
--- /dev/null
+++ b/engines/director/lingo/xlibs/f/flushmousexfcn.cpp
@@ -0,0 +1,60 @@
+/* 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/system.h"
+
+#include "director/director.h"
+#include "director/lingo/lingo.h"
+#include "director/lingo/lingo-object.h"
+#include "director/lingo/lingo-utils.h"
+#include "director/lingo/xlibs/f/flushmousexfcn.h"
+
+/**************************************************
+ *
+ * USED IN:
+ * Yellow Brick Road
+ *
+ **************************************************/
+
+namespace Director {
+
+const char *FlushMouseXFCN::xlibName = "FlushMouse";
+const XlibFileDesc FlushMouseXFCN::fileNames[] = {
+	{ "FlushMouse", nullptr },
+	{ nullptr, nullptr }
+};
+
+static BuiltinProto builtins[] = {
+	{ "FlushMouse", FlushMouseXFCN::m_FlushMouse, -1, 0, 300, HBLTIN },
+	{ nullptr, nullptr, 0, 0, 0, VOIDSYM }
+};
+
+void FlushMouseXFCN::open(ObjectType type, const Common::Path &path) {
+	g_lingo->initBuiltIns(builtins);
+}
+
+void FlushMouseXFCN::close(ObjectType type) {
+	g_lingo->cleanupBuiltIns(builtins);
+}
+
+XOBJSTUB(FlushMouseXFCN::m_FlushMouse, 0)
+
+}
diff --git a/engines/director/lingo/xlibs/f/flushmousexfcn.h b/engines/director/lingo/xlibs/f/flushmousexfcn.h
new file mode 100644
index 00000000000..a1bf97fa48d
--- /dev/null
+++ b/engines/director/lingo/xlibs/f/flushmousexfcn.h
@@ -0,0 +1,41 @@
+/* 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 DIRECTOR_LINGO_XLIBS_F_FLUSHMOUSEXFCN_H
+#define DIRECTOR_LINGO_XLIBS_F_FLUSHMOUSEXFCN_H
+
+namespace Director {
+
+namespace FlushMouseXFCN {
+
+extern const char *xlibName;
+extern const XlibFileDesc fileNames[];
+
+void open(ObjectType type, const Common::Path &path);
+void close(ObjectType type);
+
+void m_FlushMouse(int nargs);
+
+} // End of namespace FlushMouseXFCN
+
+} // End of namespace Director
+
+#endif
diff --git a/engines/director/lingo/xlibs/g/getsoundxfcn.cpp b/engines/director/lingo/xlibs/g/getsoundxfcn.cpp
new file mode 100644
index 00000000000..609313e795a
--- /dev/null
+++ b/engines/director/lingo/xlibs/g/getsoundxfcn.cpp
@@ -0,0 +1,60 @@
+/* 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/system.h"
+
+#include "director/director.h"
+#include "director/lingo/lingo.h"
+#include "director/lingo/lingo-object.h"
+#include "director/lingo/lingo-utils.h"
+#include "director/lingo/xlibs/g/getsoundxfcn.h"
+
+/**************************************************
+ *
+ * USED IN:
+ * Yellow Brick Road
+ *
+ **************************************************/
+
+namespace Director {
+
+const char *GetSoundXFCN::xlibName = "GetSound";
+const XlibFileDesc GetSoundXFCN::fileNames[] = {
+	{ "getsound", nullptr },
+	{ nullptr, nullptr }
+};
+
+static BuiltinProto builtins[] = {
+	{ "GetSound", GetSoundXFCN::m_GetSound, -1, 0, 300, HBLTIN },
+	{ nullptr, nullptr, 0, 0, 0, VOIDSYM }
+};
+
+void GetSoundXFCN::open(ObjectType type, const Common::Path &path) {
+	g_lingo->initBuiltIns(builtins);
+}
+
+void GetSoundXFCN::close(ObjectType type) {
+	g_lingo->cleanupBuiltIns(builtins);
+}
+
+XOBJSTUB(GetSoundXFCN::m_GetSound, 0)
+
+}
diff --git a/engines/director/lingo/xlibs/g/getsoundxfcn.h b/engines/director/lingo/xlibs/g/getsoundxfcn.h
new file mode 100644
index 00000000000..d541aa77755
--- /dev/null
+++ b/engines/director/lingo/xlibs/g/getsoundxfcn.h
@@ -0,0 +1,41 @@
+/* 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 DIRECTOR_LINGO_XLIBS_G_GETSOUNDXFCN_H
+#define DIRECTOR_LINGO_XLIBS_G_GETSOUNDXFCN_H
+
+namespace Director {
+
+namespace GetSoundXFCN {
+
+extern const char *xlibName;
+extern const XlibFileDesc fileNames[];
+
+void open(ObjectType type, const Common::Path &path);
+void close(ObjectType type);
+
+void m_GetSound(int nargs);
+
+} // End of namespace GetSoundXFCN
+
+} // End of namespace Director
+
+#endif
diff --git a/engines/director/lingo/xlibs/w/widget.cpp b/engines/director/lingo/xlibs/w/widget.cpp
index 9120893b146..ee5941d5a3a 100644
--- a/engines/director/lingo/xlibs/w/widget.cpp
+++ b/engines/director/lingo/xlibs/w/widget.cpp
@@ -30,7 +30,8 @@
 /**************************************************
  *
  * USED IN:
- * [insert game here]
+ * Alice: An Interactive Museum
+ * Yellow Brick Road
  *
  **************************************************/
 
diff --git a/engines/director/lingo/xlibs/w/winxobj.cpp b/engines/director/lingo/xlibs/w/winxobj.cpp
index 4ea1156b598..3d3ba005ded 100644
--- a/engines/director/lingo/xlibs/w/winxobj.cpp
+++ b/engines/director/lingo/xlibs/w/winxobj.cpp
@@ -23,6 +23,7 @@
  *
  * USED IN:
  * Alice: An Interactive Museum
+ * Yellow Brick Road
  *
  *************************************/
 
@@ -233,16 +234,16 @@ X mDispose -- closes the RearWindow, releases its data, and the XObject itself f
 */
 
 static const MethodProto xlibMethods[] = {
-	{ "new",				RearWindowXObj::m_new,					1,	1,	400 },	// D4
-	{ "getappname",			RearWindowXObj::m_getAppName,			0,	0,	400 },	// D4
-	{ "GetMemoryNeeded",	RearWindowXObj::m_getMemoryNeeded,		0,	0,	400 },	// D4
-	{ "GetScreenBottom",	RearWindowXObj::m_getScreenBottom,		0,	0,	400 },	// D4
-	{ "GetScreenLeft",		RearWindowXObj::m_getScreenLeft,		0,	0,	400 },	// D4
-	{ "GetScreenRight",		RearWindowXObj::m_getScreenRight,		0,	0,	400 },	// D4
-	{ "GetScreenTop",		RearWindowXObj::m_getScreenTop,			0,	0,	400 },	// D4
-	{ "IndexColorToWindow",	RearWindowXObj::m_indexColorToWindow,	1,	1,	400 },	// D4
-	{ "PatToWindow",		RearWindowXObj::m_patToWindow,			1,	1,	400 },	// D4
-	{ "RGBColorToWindow",	RearWindowXObj::m_rgbColorToWindow,		3,	3,	400 },	// D4
+	{ "new",				RearWindowXObj::m_new,					1,	1,	300 },	// D3
+	{ "getappname",			RearWindowXObj::m_getAppName,			0,	0,	300 },	// D3
+	{ "GetMemoryNeeded",	RearWindowXObj::m_getMemoryNeeded,		0,	0,	300 },	// D3
+	{ "GetScreenBottom",	RearWindowXObj::m_getScreenBottom,		0,	0,	300 },	// D3
+	{ "GetScreenLeft",		RearWindowXObj::m_getScreenLeft,		0,	0,	300 },	// D3
+	{ "GetScreenRight",		RearWindowXObj::m_getScreenRight,		0,	0,	300 },	// D3
+	{ "GetScreenTop",		RearWindowXObj::m_getScreenTop,			0,	0,	300 },	// D3
+	{ "IndexColorToWindow",	RearWindowXObj::m_indexColorToWindow,	1,	1,	300 },	// D3
+	{ "PatToWindow",		RearWindowXObj::m_patToWindow,			1,	1,	300 },	// D3
+	{ "RGBColorToWindow",	RearWindowXObj::m_rgbColorToWindow,		3,	3,	300 },	// D3
 	{ nullptr, nullptr, 0, 0, 0 }
 };
 
diff --git a/engines/director/module.mk b/engines/director/module.mk
index de2681f1340..bc153d60146 100644
--- a/engines/director/module.mk
+++ b/engines/director/module.mk
@@ -137,6 +137,8 @@ MODULE_OBJS = \
 	lingo/xlibs/m/mazexobj.o \
 	lingo/xlibs/m/myfolder.o \
 	lingo/xlibs/g/getuinfo.o \
+	lingo/xlibs/g/getsoundxfcn.o \
+	lingo/xlibs/f/flushmousexfcn.o \
 	lingo/xlibs/o/openbleedwindowxcmd.o \
 	lingo/xlibs/s/stagectl.o \
  	lingo/xlibs/p/playsoundmoviexobj.o \


Commit: 493b6b4858f2edd527df053abbf4d25c5da406b3
    https://github.com/scummvm/scummvm/commit/493b6b4858f2edd527df053abbf4d25c5da406b3
Author: Scott Percival (code at moral.net.au)
Date: 2026-06-17T12:32:20+02:00

Commit Message:
MACGUI: Fix keydown event processing if callback is undefined

Changed paths:
    graphics/macgui/macwindow.cpp


diff --git a/graphics/macgui/macwindow.cpp b/graphics/macgui/macwindow.cpp
index 2823c889644..34bc95caf90 100644
--- a/graphics/macgui/macwindow.cpp
+++ b/graphics/macgui/macwindow.cpp
@@ -617,8 +617,13 @@ bool MacWindow::processEvent(Common::Event &event) {
 		if (!_editable && !(_wm->getActiveWidget() && _wm->getActiveWidget()->isEditable()))
 			return result;
 
-		if (_wm->getActiveWidget())
-			return result && _wm->getActiveWidget()->processEvent(event);
+		if (_wm->getActiveWidget()) {
+			if (_callback) {
+				return result && _wm->getActiveWidget()->processEvent(event);
+			} else {
+				return _wm->getActiveWidget()->processEvent(event);
+			}
+		}
 
 		return result;
 



From noreply at scummvm.org  Wed Jun 17 12:02:22 2026
From: noreply at scummvm.org (neuromancer)
Date: Wed, 17 Jun 2026 12:02:22 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 72bea8fb646f8cd2cb874f9f364b093625754015
Message-ID: <20260617120222.5B2A77005BC@ron.scummvm.net>

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:
357a211157 SCUMM: RA2: level 6 (part 1) shield mechanic implemented properly
72bea8fb64 SCUMM: RA2: level 13 (part 2) reactor mechanic implemented properly


Commit: 357a211157eb2fe1a58b2177ea8fa32d8431e584
    https://github.com/scummvm/scummvm/commit/357a211157eb2fe1a58b2177ea8fa32d8431e584
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-17T14:01:07+02:00

Commit Message:
SCUMM: RA2: level 6 (part 1) shield mechanic implemented properly

Changed paths:
    engines/scumm/insane/rebel2/iact.cpp
    engines/scumm/insane/rebel2/rebel.cpp
    engines/scumm/insane/rebel2/rebel.h
    engines/scumm/insane/rebel2/render.cpp
    engines/scumm/insane/rebel2/runlevels.cpp


diff --git a/engines/scumm/insane/rebel2/iact.cpp b/engines/scumm/insane/rebel2/iact.cpp
index eb59a01ad7e..30473c7dbec 100644
--- a/engines/scumm/insane/rebel2/iact.cpp
+++ b/engines/scumm/insane/rebel2/iact.cpp
@@ -78,8 +78,14 @@ void InsaneRebel2::procPreRendering(byte *renderBitmap) {
 	// Reset opcode 6 init flag at the start of each new video.
 	// This ensures the per-wave init (clearBit, link table reset, wave state)
 	// fires exactly once per wave video, not every frame.
+	//
+	// Exception: seamless continuation segments (flag 0x40, e.g. the looping 06PLAY1B attack
+	// run) keep the init flag set, otherwise the wave init's clearBit(0) would resurrect
+	// shield targets the player already destroyed, resetting the shield on every loop.
 	if (_player && _player->_frame == 0) {
-		_rebelOp6Initialized = false;
+		const bool shieldContinuation = _rebelShieldGateActive && (_player->_curVideoFlags & 0x40) != 0;
+		if (!shieldContinuation)
+			_rebelOp6Initialized = false;
 	}
 
 	// For Level 2 handler 8 gameplay, restore the background BEFORE FOBJ decoding.
@@ -421,6 +427,10 @@ void InsaneRebel2::iactRebel2Opcode2(Common::SeekableReadStream &b, int16 par2,
 				if (idx >= 0 && idx < 10) {
 					_rebelValueCounters[idx]++;
 					_rebelLastCounter = _rebelValueCounters[idx];
+					// Track that this target feeds value-counter[idx] so destroying it
+					// decrements the shield gauge. Only while a shield gate is active.
+					if (_rebelShieldGateActive && targetId >= 0 && targetId < 512)
+						_rebelGaugeSlot[targetId] = (int8)idx;
 					debugC(DEBUG_INSANE, "IACT Opcode2: Increment VAL counter[%d] -> %d (target=%d)", value, _rebelValueCounters[idx], targetId);
 				}
 			}
@@ -431,6 +441,8 @@ void InsaneRebel2::iactRebel2Opcode2(Common::SeekableReadStream &b, int16 par2,
 					if (!isBitSet(targetId)) {
 						_rebelMaskCounters[slot]++;
 						_rebelLastCounter = _rebelMaskCounters[slot];
+						if (_rebelShieldGateActive && targetId >= 0 && targetId < 512)
+							_rebelGaugeSlot[targetId] = (int8)(10 + slot);
 						debugC(DEBUG_INSANE, "IACT Opcode2: Increment MASK counter[%d] -> %d (target=%d)", slot, _rebelMaskCounters[slot], targetId);
 					}
 				}
diff --git a/engines/scumm/insane/rebel2/rebel.cpp b/engines/scumm/insane/rebel2/rebel.cpp
index 35c0dd5186b..904c8341e85 100644
--- a/engines/scumm/insane/rebel2/rebel.cpp
+++ b/engines/scumm/insane/rebel2/rebel.cpp
@@ -239,6 +239,11 @@ InsaneRebel2::InsaneRebel2(ScummEngine_v7 *scumm) {
 	}
 	_rebelLastCounter = 0;
 
+	for (int i = 0; i < 512; ++i)
+		_rebelGaugeSlot[i] = -1;
+	_rebelShieldGateActive = false;
+	_rebelShieldDestroyed = false;
+
 	_difficulty = 1; // Default to Medium.
 	_targetLockTimer = 0;  // DAT_00443676 equivalent
 
@@ -1744,6 +1749,23 @@ int32 InsaneRebel2::processMouse() {
 				// Disable self (prevents sprite from rendering via SKIP chunks)
 				setBit(it->id);
 
+				// Shield hit-point gauge: if this target feeds a gauge counter, destroying it
+				// decrements that counter; the shield is destroyed when it reaches 0.
+				if (_rebelShieldGateActive && it->id >= 0 && it->id < 512 && _rebelGaugeSlot[it->id] >= 0) {
+					int slot = _rebelGaugeSlot[it->id];
+					short *counter = (slot < 10) ? &_rebelValueCounters[slot]
+					                             : &_rebelMaskCounters[slot - 10];
+					if (*counter > 0) {
+						(*counter)--;
+						_rebelLastCounter = *counter;
+						if (*counter == 0) {
+							_rebelShieldDestroyed = true;
+							debugC(DEBUG_INSANE, "Shield destroyed (gauge slot %d depleted by target %d)", slot, it->id);
+						}
+					}
+					_rebelGaugeSlot[it->id] = -1; // consume: avoid double-counting
+				}
+
 				// Set enemy type bit in wave state (FUN_004028c5 line 74)
 				// DAT_0047ab98 |= 1 << (enemyType & 0x1f)
 				// This tracks which enemy GROUPS have been killed in this wave
diff --git a/engines/scumm/insane/rebel2/rebel.h b/engines/scumm/insane/rebel2/rebel.h
index ee2924471fa..72365721461 100644
--- a/engines/scumm/insane/rebel2/rebel.h
+++ b/engines/scumm/insane/rebel2/rebel.h
@@ -962,6 +962,13 @@ public:
 	short _rebelMaskCounters[10];  // Index 1..9 used; index 0 unused
 	int _rebelLastCounter;         // Mirrors DAT_0047ab90 (last updated counter)
 
+	// Shield hit-point gauge: opcode-2 sets up a per-target counter; destroying a tracked
+	// target decrements it, and the looping attack run ends when it reaches 0.
+	int8 _rebelGaugeSlot[512];     // per target ID: -1 none; 0..9 value-counter; 10+slot mask-counter
+	bool _rebelShieldGateActive;   // true while a shield-gated looping segment is playing
+	bool _rebelShieldDestroyed;    // set when a tracked gauge counter reaches 0 during the gate
+	void resetShieldGauge();
+
 
 	// ---------------------------------------------------------------------------
 	// Handler 0x26 Turret Shot System
diff --git a/engines/scumm/insane/rebel2/render.cpp b/engines/scumm/insane/rebel2/render.cpp
index 6f3fa8ed5a7..778f0c0baee 100644
--- a/engines/scumm/insane/rebel2/render.cpp
+++ b/engines/scumm/insane/rebel2/render.cpp
@@ -2780,6 +2780,10 @@ void InsaneRebel2::procPostRendering(byte *renderBitmap, int32 codecparam, int32
 	updatePostRenderScroll(width, height);
 	updatePostRenderDeath();
 
+	// End the looping attack-run segment once the shield's hit-point gauge is depleted.
+	if (_rebelShieldGateActive && _rebelShieldDestroyed)
+		_vm->_smushVideoShouldFinish = true;
+
 	// Use video content coordinates, NOT oversized low-res gameplay-buffer coordinates.
 	const int hudScale = isHiRes() ? 2 : getRebel2IndicatorScale(width, height);
 	const int videoWidth = 320 * hudScale;
diff --git a/engines/scumm/insane/rebel2/runlevels.cpp b/engines/scumm/insane/rebel2/runlevels.cpp
index 09a450264a7..aa37df7e2ef 100644
--- a/engines/scumm/insane/rebel2/runlevels.cpp
+++ b/engines/scumm/insane/rebel2/runlevels.cpp
@@ -332,6 +332,18 @@ void InsaneRebel2::resetLevelWaveState() {
 	_rebelWaveState = 0;
 }
 
+// resetShieldGauge -- Clear the shield hit-point gauge before a looping attack-run segment.
+void InsaneRebel2::resetShieldGauge() {
+	for (int i = 0; i < 10; ++i) {
+		_rebelValueCounters[i] = 0;
+		_rebelMaskCounters[i] = 0;
+	}
+	for (int i = 0; i < 512; ++i)
+		_rebelGaugeSlot[i] = -1;
+	_rebelLastCounter = -1;        // non-zero sentinel: gauge not yet depleted
+	_rebelShieldDestroyed = false;
+}
+
 void InsaneRebel2::resetExplosions() {
 	for (uint i = 0; i < ARRAYSIZE(_explosions); ++i) {
 		_explosions[i].active = false;
@@ -933,15 +945,29 @@ int InsaneRebel2::runLevel6() {
 		// DAT_0047ab9c = 0xffffffff ??? init phase state
 		_rebelPhaseState = 0xffffffff;
 
-		// ----- PHASE 1 -----
+		// ----- PHASE 1: shield attack run -----
+		// Play 06PLAY1 (approach), then loop the 06PLAY1B continuation until the player
+		// destroys the shield (its hit gauge reaches 0) or dies.
 		_rebelLevelType = 5;  // DAT_0047a7f8 = 5
 		_currentPhase = 1;
 
-		debugC(DEBUG_INSANE, "Level 6 Phase 1");
-		if (!playLevelSegment("LEV06/06PLAY1.SAN", 0x28))
+		debugC(DEBUG_INSANE, "Level 6 Phase 1 (shield attack run)");
+		resetShieldGauge();
+		_rebelShieldGateActive = true;
+
+		if (!playLevelSegment("LEV06/06PLAY1.SAN", 0x28)) {
+			_rebelShieldGateActive = false;
 			return kLevelQuit;
-		// TODO: Mid-level switch at frame 0x2a8 to 06PLAY1B.SAN (flags 0x468)
-		// + score checkpoint (FUN_00407f55) ??? needs per-frame callback
+		}
+
+		while (_playerShield > 0 && !_rebelShieldDestroyed && !_vm->shouldQuit()) {
+			resetShieldGauge();
+			if (!playLevelSegment("LEV06/06PLAY1B.SAN", 0x468)) {
+				_rebelShieldGateActive = false;
+				return kLevelQuit;
+			}
+		}
+		_rebelShieldGateActive = false;
 
 		if (_playerShield <= 0) {
 			// Died in phase 1


Commit: 72bea8fb646f8cd2cb874f9f364b093625754015
    https://github.com/scummvm/scummvm/commit/72bea8fb646f8cd2cb874f9f364b093625754015
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-17T14:01:07+02:00

Commit Message:
SCUMM: RA2: level 13 (part 2) reactor mechanic implemented properly

Changed paths:
    engines/scumm/insane/rebel2/iact.cpp
    engines/scumm/insane/rebel2/rebel.cpp
    engines/scumm/insane/rebel2/rebel.h
    engines/scumm/insane/rebel2/render.cpp
    engines/scumm/insane/rebel2/runlevels.cpp


diff --git a/engines/scumm/insane/rebel2/iact.cpp b/engines/scumm/insane/rebel2/iact.cpp
index 30473c7dbec..02493b26fbc 100644
--- a/engines/scumm/insane/rebel2/iact.cpp
+++ b/engines/scumm/insane/rebel2/iact.cpp
@@ -429,8 +429,11 @@ void InsaneRebel2::iactRebel2Opcode2(Common::SeekableReadStream &b, int16 par2,
 					_rebelLastCounter = _rebelValueCounters[idx];
 					// Track that this target feeds value-counter[idx] so destroying it
 					// decrements the shield gauge. Only while a shield gate is active.
-					if (_rebelShieldGateActive && targetId >= 0 && targetId < 512)
+					if (_rebelShieldGateActive && targetId >= 0 && targetId < 512) {
 						_rebelGaugeSlot[targetId] = (int8)idx;
+						_rebelGaugeArmed = true;
+						_rebelLastArmedSlot = idx;
+					}
 					debugC(DEBUG_INSANE, "IACT Opcode2: Increment VAL counter[%d] -> %d (target=%d)", value, _rebelValueCounters[idx], targetId);
 				}
 			}
@@ -441,8 +444,11 @@ void InsaneRebel2::iactRebel2Opcode2(Common::SeekableReadStream &b, int16 par2,
 					if (!isBitSet(targetId)) {
 						_rebelMaskCounters[slot]++;
 						_rebelLastCounter = _rebelMaskCounters[slot];
-						if (_rebelShieldGateActive && targetId >= 0 && targetId < 512)
+						if (_rebelShieldGateActive && targetId >= 0 && targetId < 512) {
 							_rebelGaugeSlot[targetId] = (int8)(10 + slot);
+							_rebelGaugeArmed = true;
+							_rebelLastArmedSlot = 10 + slot;
+						}
 						debugC(DEBUG_INSANE, "IACT Opcode2: Increment MASK counter[%d] -> %d (target=%d)", slot, _rebelMaskCounters[slot], targetId);
 					}
 				}
diff --git a/engines/scumm/insane/rebel2/rebel.cpp b/engines/scumm/insane/rebel2/rebel.cpp
index 904c8341e85..6b574ec9675 100644
--- a/engines/scumm/insane/rebel2/rebel.cpp
+++ b/engines/scumm/insane/rebel2/rebel.cpp
@@ -243,6 +243,9 @@ InsaneRebel2::InsaneRebel2(ScummEngine_v7 *scumm) {
 		_rebelGaugeSlot[i] = -1;
 	_rebelShieldGateActive = false;
 	_rebelShieldDestroyed = false;
+	_rebelReactorMode = false;
+	_rebelGaugeArmed = false;
+	_rebelLastArmedSlot = -1;
 
 	_difficulty = 1; // Default to Medium.
 	_targetLockTimer = 0;  // DAT_00443676 equivalent
@@ -1758,7 +1761,9 @@ int32 InsaneRebel2::processMouse() {
 					if (*counter > 0) {
 						(*counter)--;
 						_rebelLastCounter = *counter;
-						if (*counter == 0) {
+						// Level 6 ends on any group emptying; Level 13's reactor is gated in
+						// procPostRendering instead, so early obstacle groups don't complete it.
+						if (*counter == 0 && !_rebelReactorMode) {
 							_rebelShieldDestroyed = true;
 							debugC(DEBUG_INSANE, "Shield destroyed (gauge slot %d depleted by target %d)", slot, it->id);
 						}
diff --git a/engines/scumm/insane/rebel2/rebel.h b/engines/scumm/insane/rebel2/rebel.h
index 72365721461..5fb614c8009 100644
--- a/engines/scumm/insane/rebel2/rebel.h
+++ b/engines/scumm/insane/rebel2/rebel.h
@@ -966,7 +966,10 @@ public:
 	// target decrements it, and the looping attack run ends when it reaches 0.
 	int8 _rebelGaugeSlot[512];     // per target ID: -1 none; 0..9 value-counter; 10+slot mask-counter
 	bool _rebelShieldGateActive;   // true while a shield-gated looping segment is playing
-	bool _rebelShieldDestroyed;    // set when a tracked gauge counter reaches 0 during the gate
+	bool _rebelShieldDestroyed;    // set when the shield/reactor is destroyed during the gate
+	bool _rebelReactorMode;        // Level 13: finale ends when the last armed group is cleared
+	bool _rebelGaugeArmed;         // at least one gauge group has been set up this attempt
+	int _rebelLastArmedSlot;       // counter slot of the most recently armed group (-1 = none)
 	void resetShieldGauge();
 
 
diff --git a/engines/scumm/insane/rebel2/render.cpp b/engines/scumm/insane/rebel2/render.cpp
index 778f0c0baee..b378b56093a 100644
--- a/engines/scumm/insane/rebel2/render.cpp
+++ b/engines/scumm/insane/rebel2/render.cpp
@@ -2780,9 +2780,21 @@ void InsaneRebel2::procPostRendering(byte *renderBitmap, int32 codecparam, int32
 	updatePostRenderScroll(width, height);
 	updatePostRenderDeath();
 
-	// End the looping attack-run segment once the shield's hit-point gauge is depleted.
-	if (_rebelShieldGateActive && _rebelShieldDestroyed)
-		_vm->_smushVideoShouldFinish = true;
+	// 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.
+		if (_rebelReactorMode && _rebelGaugeArmed && _rebelLastArmedSlot >= 0 &&
+		    (_player->_curVideoFlags & 0x40) != 0) {
+			const int slot = _rebelLastArmedSlot;
+			const short remaining = (slot < 10) ? _rebelValueCounters[slot]
+			                                    : _rebelMaskCounters[slot - 10];
+			if (remaining <= 0)
+				_rebelShieldDestroyed = true;
+		}
+		if (_rebelShieldDestroyed)
+			_vm->_smushVideoShouldFinish = true;
+	}
 
 	// Use video content coordinates, NOT oversized low-res gameplay-buffer coordinates.
 	const int hudScale = isHiRes() ? 2 : getRebel2IndicatorScale(width, height);
diff --git a/engines/scumm/insane/rebel2/runlevels.cpp b/engines/scumm/insane/rebel2/runlevels.cpp
index aa37df7e2ef..5f1e95858b4 100644
--- a/engines/scumm/insane/rebel2/runlevels.cpp
+++ b/engines/scumm/insane/rebel2/runlevels.cpp
@@ -342,6 +342,8 @@ void InsaneRebel2::resetShieldGauge() {
 		_rebelGaugeSlot[i] = -1;
 	_rebelLastCounter = -1;        // non-zero sentinel: gauge not yet depleted
 	_rebelShieldDestroyed = false;
+	_rebelGaugeArmed = false;
+	_rebelLastArmedSlot = -1;
 }
 
 void InsaneRebel2::resetExplosions() {
@@ -1937,25 +1939,31 @@ int InsaneRebel2::runLevel13() {
 
 		clearBit(0);
 
-		// Phase A: Main escape flight (13PLAY_A.SAN)
-		// Original: FUN_0041f4d0("13PLAY_A.SAN", 0x28, -1, -1, 0)
-		// First inner loop runs until frame reaches maxFrame-10
-		// Then Phase B (13PLAY_B.SAN, flags 0x468) plays at that exact frame
-		// The 0x468 flags indicate seamless mid-video transition.
-		if (!playLevelSegment("LEV13/13PLAY_A.SAN", 0x28))
+		// Phase A: full approach flight; the reactor gauge is tracked but A plays to the end.
+		resetShieldGauge();
+		_rebelShieldGateActive = true;
+		_rebelReactorMode = true;
+		if (!playLevelSegment("LEV13/13PLAY_A.SAN", 0x28)) {
+			_rebelShieldGateActive = false;
+			_rebelReactorMode = false;
 			return kLevelQuit;
+		}
 
-		// If alive after Phase A, play Phase B (reactor destruction loop)
-		// Original: at frame == maxFrame-10, play 13PLAY_B.SAN (0x468)
-		// Then loop while (DAT_0047ab90 != 0 || DAT_0047ab7c != 0)
-		// Play B as a sequential video. The IACT callbacks manage the reactor
-		// target state through opcode interactions.
-		if (_playerShield > 0) {
-			if (!playLevelSegment("LEV13/13PLAY_B.SAN", 0x468))
+		// Reactor finale: loop 13PLAY_B until the reactor is destroyed or the player dies.
+		// The gauge persists from A (no reset) so hits carry over; finaleGuard caps the loop.
+		int finaleGuard = 0;
+		while (_playerShield > 0 && !_rebelShieldDestroyed && !_vm->shouldQuit() && finaleGuard < 60) {
+			++finaleGuard;
+			if (!playLevelSegment("LEV13/13PLAY_B.SAN", 0x468)) {
+				_rebelShieldGateActive = false;
+				_rebelReactorMode = false;
 				return kLevelQuit;
+			}
 		}
+		_rebelShieldGateActive = false;
+		_rebelReactorMode = false;
 
-		if (_playerShield > 0) {
+		if (_playerShield > 0 && _rebelShieldDestroyed) {
 			int accuracy = calculateAccuracy(_rebelKillCounter, _rebelHitCounter);
 			debugC(DEBUG_INSANE, "Level 13 completed! accuracy=%d%%", accuracy);
 			playLevelEnd(13);



From noreply at scummvm.org  Wed Jun 17 12:40:28 2026
From: noreply at scummvm.org (lotharsm)
Date: Wed, 17 Jun 2026 12:40:28 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 4ff450c9dd27980f4feff153bc8d7cbcf1703549
Message-ID: <20260617124028.6A0677005BC@ron.scummvm.net>

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

Summary:
4ff450c9dd NEWS: Update German NEWS file


Commit: 4ff450c9dd27980f4feff153bc8d7cbcf1703549
    https://github.com/scummvm/scummvm/commit/4ff450c9dd27980f4feff153bc8d7cbcf1703549
Author: Lothar Serra Mari (mail at serra.me)
Date: 2026-06-17T14:40:14+02:00

Commit Message:
NEWS: Update German NEWS file

Changed paths:
    doc/de/NEUES.md


diff --git a/doc/de/NEUES.md b/doc/de/NEUES.md
index 484beaa80ae..16eb0a3ba1e 100644
--- a/doc/de/NEUES.md
+++ b/doc/de/NEUES.md
@@ -74,6 +74,9 @@ Umfangreichere Informationen ??ber die ??nderungen des aktuellen Codes findest D
    - Native Unterst??tzung f??r CDDA hinzugef??gt.
    - Unterst??tzung f??r Plugins hinzugef??gt. ScummVM kann jetzt schon mit wenigen MB RAM genutzt werden.
    - Native Unterst??tzung f??r YM2149 hinzugef??gt (aktuell verwendbar in Wlvira 1 und 2).
+   - Audio-Mixer ??berarbeitet, was auch Audio-Probleme in Secret of Monkey Island behebt.
+   - Autosave ist jetzt standardm????ig deaktiviert, da die Funktion zu h??rbaren Audio-Problemen
+     w??hrend des Spiels f??hrt.
 
  PS3-Portierung:
    - ScummVM-Engines k??nnen jetzt als separate Module/Programmdateien ausgef??hrt werden.



From noreply at scummvm.org  Wed Jun 17 19:46:09 2026
From: noreply at scummvm.org (neuromancer)
Date: Wed, 17 Jun 2026 19:46:09 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 451d0c195de21201604c63f2ca71c208edc2a296
Message-ID: <20260617194609.0C4457005BC@ron.scummvm.net>

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:
45bacff1fa SCUMM: RA2: start level 2 and level 11 in cover state
451d0c195d SCUMM: RA2: make sure translucid destroyable shields are properly handled


Commit: 45bacff1faa381a812d8a68eb0659a30acf0c5fd
    https://github.com/scummvm/scummvm/commit/45bacff1faa381a812d8a68eb0659a30acf0c5fd
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-17T17:03:28+02:00

Commit Message:
SCUMM: RA2: start level 2 and level 11 in cover state

Changed paths:
    engines/scumm/insane/rebel2/runlevels.cpp


diff --git a/engines/scumm/insane/rebel2/runlevels.cpp b/engines/scumm/insane/rebel2/runlevels.cpp
index 5f1e95858b4..2cb6b634a09 100644
--- a/engines/scumm/insane/rebel2/runlevels.cpp
+++ b/engines/scumm/insane/rebel2/runlevels.cpp
@@ -312,6 +312,11 @@ void InsaneRebel2::resetLevelPhaseState(bool clearEnemies) {
 
 	if (clearEnemies)
 		_enemies.clear();
+
+	if (_selectedChapter == 1 || _selectedChapter == 10) {
+		_rebelAutopilot = 1;
+		_rebelDamageLevel = 5;
+	}
 }
 
 void InsaneRebel2::clearEmbeddedHudFrames() {


Commit: 451d0c195de21201604c63f2ca71c208edc2a296
    https://github.com/scummvm/scummvm/commit/451d0c195de21201604c63f2ca71c208edc2a296
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-17T21:41:25+02:00

Commit Message:
SCUMM: RA2: make sure translucid destroyable shields are properly handled

Changed paths:
    engines/scumm/insane/rebel2/iact.cpp
    engines/scumm/insane/rebel2/rebel.cpp
    engines/scumm/insane/rebel2/rebel.h
    engines/scumm/insane/rebel2/runlevels.cpp
    engines/scumm/smush/rebel/codec_ra2.cpp
    engines/scumm/smush/rebel/codec_ra2.h
    engines/scumm/smush/rebel/smush_player_ra2.cpp
    engines/scumm/smush/rebel/smush_player_ra2.h


diff --git a/engines/scumm/insane/rebel2/iact.cpp b/engines/scumm/insane/rebel2/iact.cpp
index 02493b26fbc..c4d52b41477 100644
--- a/engines/scumm/insane/rebel2/iact.cpp
+++ b/engines/scumm/insane/rebel2/iact.cpp
@@ -2699,6 +2699,18 @@ void InsaneRebel2::enemyUpdate(byte *renderBitmap, Common::SeekableReadStream &b
 	int16 w = b.readSint16LE();        // Offset +14 (0x0E) - Width
 	int16 h = b.readSint16LE();        // Offset +16 (0x10) - Height
 
+	// par3==2 (turret handler) marks a translucent surface / force field (FUN_40A2E0 local_8==2):
+	// never a direct hit target ??? it is destroyed by clearing the gauge group (par4) it depends
+	// on. Show it while that group still has elements, hide it once the group is cleared.
+	if (par3 == 2 && _rebelHandler == 0x26) {
+		const int surfaceIdx = (par4 >= 100 && par4 < 110) ? (par4 - 100) : -1;
+		if (surfaceIdx >= 0 && _rebelGaugeCleared[surfaceIdx])
+			setBit(enemyId);
+		else
+			clearBit(enemyId);
+		return; // surfaces are not added to the hittable enemy list
+	}
+
 	// If disabled, stop processing this object
 	if (disabled) {
 		// debugC(DEBUG_INSANE, "Skipping Opcode 4 for disabled enemy ID=%d", enemyId);
diff --git a/engines/scumm/insane/rebel2/rebel.cpp b/engines/scumm/insane/rebel2/rebel.cpp
index 6b574ec9675..02f9442f926 100644
--- a/engines/scumm/insane/rebel2/rebel.cpp
+++ b/engines/scumm/insane/rebel2/rebel.cpp
@@ -246,6 +246,8 @@ InsaneRebel2::InsaneRebel2(ScummEngine_v7 *scumm) {
 	_rebelReactorMode = false;
 	_rebelGaugeArmed = false;
 	_rebelLastArmedSlot = -1;
+	for (int i = 0; i < 10; ++i)
+		_rebelGaugeCleared[i] = false;
 
 	_difficulty = 1; // Default to Medium.
 	_targetLockTimer = 0;  // DAT_00443676 equivalent
@@ -1761,11 +1763,15 @@ int32 InsaneRebel2::processMouse() {
 					if (*counter > 0) {
 						(*counter)--;
 						_rebelLastCounter = *counter;
-						// Level 6 ends on any group emptying; Level 13's reactor is gated in
-						// procPostRendering instead, so early obstacle groups don't complete it.
-						if (*counter == 0 && !_rebelReactorMode) {
-							_rebelShieldDestroyed = true;
-							debugC(DEBUG_INSANE, "Shield destroyed (gauge slot %d depleted by target %d)", slot, it->id);
+						if (*counter == 0) {
+							if (slot < 10)
+								_rebelGaugeCleared[slot] = true; // group emptied ??? its par3==2 surface can drop
+							// Level 6 ends on any group emptying; Level 13's reactor is gated in
+							// procPostRendering instead, so early obstacle groups don't complete it.
+							if (!_rebelReactorMode) {
+								_rebelShieldDestroyed = true;
+								debugC(DEBUG_INSANE, "Shield destroyed (gauge slot %d depleted by target %d)", slot, it->id);
+							}
 						}
 					}
 					_rebelGaugeSlot[it->id] = -1; // consume: avoid double-counting
diff --git a/engines/scumm/insane/rebel2/rebel.h b/engines/scumm/insane/rebel2/rebel.h
index 5fb614c8009..008bb55b591 100644
--- a/engines/scumm/insane/rebel2/rebel.h
+++ b/engines/scumm/insane/rebel2/rebel.h
@@ -970,6 +970,7 @@ public:
 	bool _rebelReactorMode;        // Level 13: finale ends when the last armed group is cleared
 	bool _rebelGaugeArmed;         // at least one gauge group has been set up this attempt
 	int _rebelLastArmedSlot;       // counter slot of the most recently armed group (-1 = none)
+	bool _rebelGaugeCleared[10];   // value-counter slot fully destroyed (drives par3==2 surfaces)
 	void resetShieldGauge();
 
 
diff --git a/engines/scumm/insane/rebel2/runlevels.cpp b/engines/scumm/insane/rebel2/runlevels.cpp
index 2cb6b634a09..15ed6c0e9e3 100644
--- a/engines/scumm/insane/rebel2/runlevels.cpp
+++ b/engines/scumm/insane/rebel2/runlevels.cpp
@@ -349,6 +349,8 @@ void InsaneRebel2::resetShieldGauge() {
 	_rebelShieldDestroyed = false;
 	_rebelGaugeArmed = false;
 	_rebelLastArmedSlot = -1;
+	for (int i = 0; i < 10; ++i)
+		_rebelGaugeCleared[i] = false;
 }
 
 void InsaneRebel2::resetExplosions() {
diff --git a/engines/scumm/smush/rebel/codec_ra2.cpp b/engines/scumm/smush/rebel/codec_ra2.cpp
index e528d51f8a9..cb6100a7e6d 100644
--- a/engines/scumm/smush/rebel/codec_ra2.cpp
+++ b/engines/scumm/smush/rebel/codec_ra2.cpp
@@ -225,6 +225,44 @@ void smushDecodeSkipRLE(byte *dst, const byte *src, int left, int top, int width
 	}
 }
 
+// Codec 23: a skip/run RLE that tints the background in place (no pixel data). Per line,
+// lineDataSize(2) then [skip(1), run(1)] pairs; each run pixel becomes remap[background]
+// (translucency) or background+addColor.
+void smushDecodeRA2SkipRemap(byte *dst, const byte *src, int left, int top, int width, int height, int pitch, int dataSize, const byte *remap, byte addColor) {
+	dst += top * pitch + left;
+	const byte *srcEnd = src + dataSize;
+
+	for (int row = 0; row < height; row++) {
+		if (src + 2 > srcEnd)
+			break;
+		int lineDataSize = READ_LE_UINT16(src);
+		src += 2;
+		const byte *lineStart = src;
+		const byte *lineEnd = (lineStart + lineDataSize <= srcEnd) ? lineStart + lineDataSize : srcEnd;
+		byte *lineDst = dst;
+		int x = 0;
+
+		while (x < width && src < lineEnd) {
+			x += *src++;                 // skip (preserve background)
+			if (x >= width || src >= lineEnd)
+				break;
+			int run = *src++;
+			if (run > width - x)
+				run = width - x;
+			if (remap) {
+				for (int i = 0; i < run; i++, x++)
+					lineDst[x] = remap[lineDst[x]];
+			} else {
+				for (int i = 0; i < run; i++, x++)
+					lineDst[x] = (byte)(lineDst[x] + addColor);
+			}
+		}
+
+		src = lineStart + lineDataSize;
+		dst += pitch;
+	}
+}
+
 bool smushPrepareRA2BlurData(const byte *src, int dataSize, byte *palette, byte *lookup, const byte *&maskData, int &maskSize) {
 	maskData = nullptr;
 	maskSize = 0;
diff --git a/engines/scumm/smush/rebel/codec_ra2.h b/engines/scumm/smush/rebel/codec_ra2.h
index b89797c1a0a..42911065cb0 100644
--- a/engines/scumm/smush/rebel/codec_ra2.h
+++ b/engines/scumm/smush/rebel/codec_ra2.h
@@ -29,6 +29,7 @@ namespace Scumm {
 void smushDecodeRLEOpaque(byte *dst, const byte *src, int left, int top, int width, int height, int pitch, int dataSize);
 void smushDecodeLineUpdate(byte *dst, const byte *src, int left, int top, int width, int height, int pitch, int dataSize);
 void smushDecodeSkipRLE(byte *dst, const byte *src, int left, int top, int width, int height, int pitch, int dataSize);
+void smushDecodeRA2SkipRemap(byte *dst, const byte *src, int left, int top, int width, int height, int pitch, int dataSize, const byte *remap, byte addColor);
 void smushDecodeRA2Blur(byte *dst, const byte *src, int left, int top, int dstWidth, int dstHeight, int pitch, int dataSize, byte *palette, byte *lookup);
 const byte *smushSkipRLELines(const byte *src, int &dataSize, int lines);
 
diff --git a/engines/scumm/smush/rebel/smush_player_ra2.cpp b/engines/scumm/smush/rebel/smush_player_ra2.cpp
index 339b2101206..f0455eea3dc 100644
--- a/engines/scumm/smush/rebel/smush_player_ra2.cpp
+++ b/engines/scumm/smush/rebel/smush_player_ra2.cpp
@@ -189,6 +189,8 @@ void SmushPlayerRebel2::initGamePlayerFields() {
 	_ra2PendingAnimHeaderPalette = false;
 	memset(_ra2Codec45Palette, 0, sizeof(_ra2Codec45Palette));
 	memset(_ra2Codec45Lookup, 0, sizeof(_ra2Codec45Lookup));
+	memset(_ra2SkipRemapTable, 0, sizeof(_ra2SkipRemapTable));
+	_ra2SkipRemapValid = false;
 	_scrollX = 0;
 	_scrollY = 0;
 }
@@ -1440,11 +1442,53 @@ bool SmushPlayerRebel2::handleGameCodecDecode(int codec, const uint8 *src, int l
 	if (isRebel2FullFrameDeltaCodec(codec))
 		return ra2DecodePlacedDeltaCodec(codec, src, left, top, width, height, pitch, dataSize);
 
+	// Codec 23 translucent surfaces: parm2 0x100 prefixes a fresh 256-byte remap table, >=0x100
+	// reuses the last one. (Font glyphs call smushDecodeSkipRLE directly and are unaffected.)
+	if (codec == SMUSH_CODEC_SKIP_RLE && parm2 >= 0x100) {
+		if (parm2 == 0x100 && dataSize >= 256) {
+			memcpy(_ra2SkipRemapTable, src, 256);
+			_ra2SkipRemapValid = true;
+			src += 256;
+			dataSize -= 256;
+		}
+		const byte *remap = _ra2SkipRemapValid ? _ra2SkipRemapTable : nullptr;
+		smushDecodeRA2SkipRemap(_dst, src, left, top, width, height, pitch, dataSize, remap, (byte)parm2);
+		return true;
+	}
+
 	// Handle RA2-specific codecs (21, 23, 44, 45); return false for standard
 	// codecs (RLE) so the base class decodes them.
 	return ra2DecodeCodec(codec, src, left, top, width, height, pitch, dataSize);
 }
 
+// Mirrors SmushPlayer::handleFrameObject but forwards the FOBJ parm2 word (base discards it).
+void SmushPlayerRebel2::handleFrameObject(int32 subSize, Common::SeekableReadStream &b) {
+	assert(subSize >= 14);
+	if (_skipNext) {
+		_skipNext = false;
+		return;
+	}
+
+	int codec = b.readUint16LE();
+	int left = (int)b.readSint16LE();
+	int top = (int)b.readSint16LE();
+	int width = b.readUint16LE();
+	int height = b.readUint16LE();
+	b.readUint16LE();                     // objectId
+	uint16 parm2 = b.readUint16LE();
+
+	const int32 chunkSize = subSize - 14;
+	handleGameFrameObjectPre(codec, left, top, width, height, chunkSize);
+
+	byte *chunkBuffer = (byte *)malloc(chunkSize);
+	assert(chunkBuffer);
+	b.read(chunkBuffer, chunkSize);
+
+	handleGameFrameObjectPost(codec, chunkBuffer, chunkSize, left, top, width, height);
+	decodeFrameObject(codec, chunkBuffer, left, top, width, height, chunkSize, 0, parm2);
+	free(chunkBuffer);
+}
+
 bool SmushPlayerRebel2::handleGameStoreFrame() {
 	// RA2 handles STOR via ra2StoreFobjData in handleGameFrameObjectPost
 	return true;
diff --git a/engines/scumm/smush/rebel/smush_player_ra2.h b/engines/scumm/smush/rebel/smush_player_ra2.h
index c3c87e8ea75..b1628a1a302 100644
--- a/engines/scumm/smush/rebel/smush_player_ra2.h
+++ b/engines/scumm/smush/rebel/smush_player_ra2.h
@@ -38,6 +38,8 @@ protected:
 	void initGameVideoState() override;
 	void releaseGameVideoState() override;
 	bool shouldPreserveFrameBuffer() const override { return true; }
+	// Override to keep the FOBJ parm2 field (codec 23 marker) the base reader discards.
+	void handleFrameObject(int32 subSize, Common::SeekableReadStream &b) override;
 	bool handleGameFetch(int32 subSize, Common::SeekableReadStream &b) override;
 	bool handleGameTextResource(uint32 subType, int32 subSize, Common::SeekableReadStream &b) override;
 	bool handleGameTextRendering(const char *str, int fontId, int color, int pos_x, int pos_y, int left, int top, int width, int height, TextStyleFlags flg) override;
@@ -110,6 +112,8 @@ private:
 	bool _ra2PendingAnimHeaderPalette;
 	byte _ra2Codec45Palette[0x300];
 	byte _ra2Codec45Lookup[0x8000];
+	byte _ra2SkipRemapTable[256]; // codec 23 translucency remap table
+	bool _ra2SkipRemapValid;
 };
 
 } // End of namespace Scumm



From noreply at scummvm.org  Wed Jun 17 21:13:27 2026
From: noreply at scummvm.org (sev-)
Date: Wed, 17 Jun 2026 21:13:27 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 560416990ca530be89abce8d6874cd98b9671019
Message-ID: <20260617211327.5ED9D7005BC@ron.scummvm.net>

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:
9e32e26e89 GAMOS: Mark games for testing
503ad72e89 GAMOS: Enable engine by default
560416990c NEWS: Mention the Gamos engine games support


Commit: 9e32e26e8901b973f307482effb34a50df911510
    https://github.com/scummvm/scummvm/commit/9e32e26e8901b973f307482effb34a50df911510
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2026-06-17T23:12:22+02:00

Commit Message:
GAMOS: Mark games for testing

Changed paths:
    engines/gamos/detection_tables.h


diff --git a/engines/gamos/detection_tables.h b/engines/gamos/detection_tables.h
index d8a5860f276..71a6bf5da24 100644
--- a/engines/gamos/detection_tables.h
+++ b/engines/gamos/detection_tables.h
@@ -42,7 +42,7 @@ const GamosGameDescription gameDescriptions[] = {
 			AD_ENTRY1s("solgamer.exe", "t:4a06f82f7638e865e27d68d2e071e827", 24658521),
 			Common::RU_RUS,
 			Common::kPlatformWindows,
-			ADGF_UNSTABLE,
+			ADGF_TESTING,
 			GUIO1(GUIO_NONE)
 		},
 		0x80000018
@@ -55,7 +55,7 @@ const GamosGameDescription gameDescriptions[] = {
 			AD_ENTRY1s("solgamee.exe", "t:cad7d29da5aa843853fb8e7609a46300", 24624511),
 			Common::EN_ANY,
 			Common::kPlatformWindows,
-			ADGF_UNSTABLE,
+			ADGF_TESTING,
 			GUIO1(GUIO_NONE)
 		},
 		0x80000018
@@ -68,7 +68,7 @@ const GamosGameDescription gameDescriptions[] = {
 			AD_ENTRY1s("solgamee.exe", "t:a32bbae8c381d8cdc4cbac9e18acb0a8", 24676118),
 			Common::EN_ANY,
 			Common::kPlatformWindows,
-			ADGF_UNSTABLE,
+			ADGF_TESTING,
 			GUIO1(GUIO_NONE)
 		},
 		0x80000018
@@ -81,7 +81,7 @@ const GamosGameDescription gameDescriptions[] = {
 			AD_ENTRY1s("pilots.exe", "t:82ae05090898af66447bac4f06e910f3", 48357155),
 			Common::RU_RUS,
 			Common::kPlatformWindows,
-			ADGF_UNSTABLE,
+			ADGF_TESTING,
 			GUIO1(GUIO_NONE)
 		},
 		0x80000016
@@ -94,7 +94,7 @@ const GamosGameDescription gameDescriptions[] = {
 			AD_ENTRY1s("pilde_r.exe", "t:a1bbaa7e59f69cb2be3223c3336982ac", 10770038),
 			Common::RU_RUS,
 			Common::kPlatformWindows,
-			ADGF_UNSTABLE | ADGF_DEMO,
+			ADGF_TESTING | ADGF_DEMO,
 			GUIO1(GUIO_NONE)
 		},
 		0x80000016
@@ -107,7 +107,7 @@ const GamosGameDescription gameDescriptions[] = {
 			AD_ENTRY1s("pildemo.exe", "t:a1bbaa7e59f69cb2be3223c3336982ac", 10770038),
 			Common::RU_RUS,
 			Common::kPlatformWindows,
-			ADGF_UNSTABLE | ADGF_DEMO,
+			ADGF_TESTING | ADGF_DEMO,
 			GUIO1(GUIO_NONE)
 		},
 		0x80000016
@@ -120,7 +120,7 @@ const GamosGameDescription gameDescriptions[] = {
 			AD_ENTRY1s("pilotsrd.exe", "t:a1bbaa7e59f69cb2be3223c3336982ac", 10770038),
 			Common::RU_RUS,
 			Common::kPlatformWindows,
-			ADGF_UNSTABLE | ADGF_DEMO,
+			ADGF_TESTING | ADGF_DEMO,
 			GUIO1(GUIO_NONE)
 		},
 		0x80000016
@@ -133,7 +133,7 @@ const GamosGameDescription gameDescriptions[] = {
 			AD_ENTRY1s("pilde_e.exe", "t:a784fc52f6923817d8ec8b9101e3f3ea", 10904737),
 			Common::EN_ANY,
 			Common::kPlatformWindows,
-			ADGF_UNSTABLE | ADGF_DEMO,
+			ADGF_TESTING | ADGF_DEMO,
 			GUIO1(GUIO_NONE)
 		},
 		0x80000016
@@ -146,7 +146,7 @@ const GamosGameDescription gameDescriptions[] = {
 			AD_ENTRY1s("pilotsed.exe", "t:190cb2dcf936d0c3a891ba333dbc2f33", 10946501),
 			Common::EN_ANY,
 			Common::kPlatformWindows,
-			ADGF_UNSTABLE | ADGF_DEMO,
+			ADGF_TESTING | ADGF_DEMO,
 			GUIO1(GUIO_NONE)
 		},
 		0x80000018
@@ -159,7 +159,7 @@ const GamosGameDescription gameDescriptions[] = {
 			AD_ENTRY1s("pilots2.exe", "t:c434bd8787febde52ccb5ef0e0731c7b", 582283983),
 			Common::RU_RUS,
 			Common::kPlatformWindows,
-			ADGF_UNSTABLE,
+			ADGF_TESTING,
 			GUIO1(GUIO_NONE)
 		},
 		0x80000018
@@ -172,7 +172,7 @@ const GamosGameDescription gameDescriptions[] = {
 			AD_ENTRY1s("wildus.exe", "t:320c8dfd26ae5935b71a37227b7fe67f", 11475754),
 			Common::EN_ANY,
 			Common::kPlatformWindows,
-			ADGF_UNSTABLE,
+			ADGF_TESTING,
 			GUIO1(GUIO_NONE)
 		},
 		0x80000018
@@ -185,7 +185,7 @@ const GamosGameDescription gameDescriptions[] = {
 			AD_ENTRY1s("wildus.exe", "t:a28e6f87a9c5cad5661108678522e27c", 11471282),
 			Common::EN_ANY,
 			Common::kPlatformWindows,
-			ADGF_UNSTABLE,
+			ADGF_TESTING,
 			GUIO1(GUIO_NONE)
 		},
 		0x80000016
@@ -198,7 +198,7 @@ const GamosGameDescription gameDescriptions[] = {
 			AD_ENTRY1s("wildru.exe", "t:35bcdf2b9a0b022fe546de08ab685bd9", 11510210),
 			Common::RU_RUS,
 			Common::kPlatformWindows,
-			ADGF_UNSTABLE,
+			ADGF_TESTING,
 			GUIO1(GUIO_NONE)
 		},
 		0x80000016
@@ -211,7 +211,7 @@ const GamosGameDescription gameDescriptions[] = {
 			AD_ENTRY1s("wildrudm.exe", "t:335fa22e185426843190460bc57d78a7", 11517820),
 			Common::RU_RUS,
 			Common::kPlatformWindows,
-			ADGF_UNSTABLE | ADGF_DEMO,
+			ADGF_TESTING | ADGF_DEMO,
 			GUIO1(GUIO_NONE)
 		},
 		0x80000016
@@ -224,7 +224,7 @@ const GamosGameDescription gameDescriptions[] = {
 			AD_ENTRY1s("wildusdm.exe", "t:bddc7069986caedcfaa38ce35d42520b", 11516932),
 			Common::EN_ANY,
 			Common::kPlatformWindows,
-			ADGF_UNSTABLE | ADGF_DEMO,
+			ADGF_TESTING | ADGF_DEMO,
 			GUIO1(GUIO_NONE)
 		},
 		0x80000016
@@ -237,7 +237,7 @@ const GamosGameDescription gameDescriptions[] = {
 			AD_ENTRY1s("ffloprd.exe", "t:a09793d184d211f819299a5286a75f56", 4637680),
 			Common::RU_RUS,
 			Common::kPlatformWindows,
-			ADGF_UNSTABLE | ADGF_DEMO,
+			ADGF_TESTING | ADGF_DEMO,
 			GUIO1(GUIO_NONE)
 		},
 		0x80000016
@@ -250,7 +250,7 @@ const GamosGameDescription gameDescriptions[] = {
 			AD_ENTRY1s("ffloped.exe", "t:2f90db8d9083b57c775375cf880dc777", 4609380),
 			Common::EN_ANY,
 			Common::kPlatformWindows,
-			ADGF_UNSTABLE | ADGF_DEMO,
+			ADGF_TESTING | ADGF_DEMO,
 			GUIO1(GUIO_NONE)
 		},
 		0x80000016
@@ -263,7 +263,7 @@ const GamosGameDescription gameDescriptions[] = {
 			AD_ENTRY1s("fflope.exe", "t:7e098e0b565d97eb3d55eb3b6d45e413", 4633340),
 			Common::EN_ANY,
 			Common::kPlatformWindows,
-			ADGF_UNSTABLE,
+			ADGF_TESTING,
 			GUIO1(GUIO_NONE)
 		},
 		0x80000018
@@ -276,7 +276,7 @@ const GamosGameDescription gameDescriptions[] = {
 			AD_ENTRY1s("ffloped.exe", "t:1be4097f6564e9240c58079addfd4600", 4613290),
 			Common::EN_ANY,
 			Common::kPlatformWindows,
-			ADGF_UNSTABLE | ADGF_DEMO,
+			ADGF_TESTING | ADGF_DEMO,
 			GUIO1(GUIO_NONE)
 		},
 		0x80000018
@@ -289,7 +289,7 @@ const GamosGameDescription gameDescriptions[] = {
 			AD_ENTRY1s("fflope.exe", "t:8ffba2d788278457bb2f9661b7bdf4ec", 4629006),
 			Common::EN_ANY,
 			Common::kPlatformWindows,
-			ADGF_UNSTABLE,
+			ADGF_TESTING,
 			GUIO1(GUIO_NONE)
 		},
 		0x80000016
@@ -302,7 +302,7 @@ const GamosGameDescription gameDescriptions[] = {
 			AD_ENTRY1s("fflopr.exe", "t:7e452d6f5fbfe32dc1ee3c53fba27f88", 4629153),
 			Common::RU_RUS,
 			Common::kPlatformWindows,
-			ADGF_UNSTABLE,
+			ADGF_TESTING,
 			GUIO1(GUIO_NONE)
 		},
 		0x80000016
@@ -314,7 +314,7 @@ const GamosGameDescription gameDescriptions[] = {
 			AD_ENTRY1s("fflop.exe", "t:fd32bc85b8686504f3e63c8e00444c68", 6662497),
 			Common::RU_RUS,
 			Common::kPlatformWindows,
-			ADGF_UNSTABLE,
+			ADGF_TESTING,
 			GUIO1(GUIO_NONE)
 		},
 		0x8000000b
@@ -326,7 +326,7 @@ const GamosGameDescription gameDescriptions[] = {
 			AD_ENTRY1s("fflop.exe", "t:3f7b90481959b408c9f7dface714cb75", 6691028),
 			Common::EN_ANY,
 			Common::kPlatformWindows,
-			ADGF_UNSTABLE,
+			ADGF_TESTING,
 			GUIO1(GUIO_NONE)
 		},
 		0x8000000b
@@ -339,7 +339,7 @@ const GamosGameDescription gameDescriptions[] = {
 			AD_ENTRY1s("fflop.exe", "t:a1f917e41e24cbb9e2bda3cf2da5e8fb", 6860990),
 			Common::EN_ANY,
 			Common::kPlatformWindows,
-			ADGF_UNSTABLE,
+			ADGF_TESTING,
 			GUIO1(GUIO_NONE)
 		},
 		0x80000012
@@ -352,7 +352,7 @@ const GamosGameDescription gameDescriptions[] = {
 			AD_ENTRY1s("it.exe", "t:76c0cce851177ed28e0d78ab989c6b63", 4125894),
 			Common::RU_RUS,
 			Common::kPlatformWindows,
-			ADGF_UNSTABLE,
+			ADGF_TESTING,
 			GUIO1(GUIO_NONE)
 		},
 		0x80000016
@@ -364,7 +364,7 @@ const GamosGameDescription gameDescriptions[] = {
 			AD_ENTRY1s("it.exe", "t:455ad057b10542674c69f428311b6daf", 4744297),
 			Common::RU_RUS,
 			Common::kPlatformWindows,
-			ADGF_UNSTABLE,
+			ADGF_TESTING,
 			GUIO1(GUIO_NONE)
 		},
 		0x8000000b
@@ -376,7 +376,7 @@ const GamosGameDescription gameDescriptions[] = {
 			AD_ENTRY1s("netwalk.exe", "t:f7e100541adf71f1cec35ecc914c8ca3", 5607140),
 			Common::EN_ANY,
 			Common::kPlatformWindows,
-			ADGF_UNSTABLE,
+			ADGF_TESTING,
 			GUIO1(GUIO_NONE)
 		},
 		0x8000000b
@@ -389,7 +389,7 @@ const GamosGameDescription gameDescriptions[] = {
 			AD_ENTRY1s("netwalk.exe", "t:3512b1127b93c2ffe074e292f19a0ffb", 4981212),
 			Common::EN_ANY,
 			Common::kPlatformWindows,
-			ADGF_UNSTABLE,
+			ADGF_TESTING,
 			GUIO1(GUIO_NONE)
 		},
 		0x80000016
@@ -402,7 +402,7 @@ const GamosGameDescription gameDescriptions[] = {
 			AD_ENTRY1s("netwalk.exe", "t:f1b1713c122a4d60a773046f6aa1e1d8", 4972704),
 			Common::EN_ANY,
 			Common::kPlatformWindows,
-			ADGF_UNSTABLE,
+			ADGF_TESTING,
 			GUIO1(GUIO_NONE)
 		},
 		0x80000016
@@ -415,7 +415,7 @@ const GamosGameDescription gameDescriptions[] = {
 			AD_ENTRY1s("netwalk.exe", "t:ee6ec7c4d1f451d30038665e2edcc278", 5624675),
 			Common::EN_ANY,
 			Common::kPlatformWindows,
-			ADGF_UNSTABLE,
+			ADGF_TESTING,
 			GUIO1(GUIO_NONE)
 		},
 		0x80000012
@@ -428,7 +428,7 @@ const GamosGameDescription gameDescriptions[] = {
 			AD_ENTRY1s("netwalk.exe", "t:140a884942bbc396ae0640645d8118aa", 4979981),
 			Common::EN_ANY,
 			Common::kPlatformWindows,
-			ADGF_UNSTABLE,
+			ADGF_TESTING,
 			GUIO1(GUIO_NONE)
 		},
 		0x80000018
@@ -441,7 +441,7 @@ const GamosGameDescription gameDescriptions[] = {
 			AD_ENTRY1s("netwalk.exe", "t:4d78e86ef47ceb6c578081ba367ee8ea", 4984836),
 			Common::EN_ANY,
 			Common::kPlatformWindows,
-			ADGF_UNSTABLE | ADGF_DEMO,
+			ADGF_TESTING | ADGF_DEMO,
 			GUIO1(GUIO_NONE)
 		},
 		0x80000018
@@ -454,7 +454,7 @@ const GamosGameDescription gameDescriptions[] = {
 			AD_ENTRY1s("vitam_e.exe", "t:04cae8b641a7d79a1842c7ac1899b534", 429943),
 			Common::EN_ANY,
 			Common::kPlatformWindows,
-			ADGF_UNSTABLE,
+			ADGF_TESTING,
 			GUIO1(GUIO_NONE)
 		},
 		0x80000016
@@ -467,7 +467,7 @@ const GamosGameDescription gameDescriptions[] = {
 			AD_ENTRY1s("vitam_e.exe", "t:3506cd8e1a32ec3e841a2043d9dabc5b", 1122512),
 			Common::EN_ANY,
 			Common::kPlatformWindows,
-			ADGF_UNSTABLE,
+			ADGF_TESTING,
 			GUIO1(GUIO_NONE)
 		},
 		0x80000018
@@ -480,7 +480,7 @@ const GamosGameDescription gameDescriptions[] = {
 			AD_ENTRY1s("vitam_r.exe", "t:427c0269c7327c7ca4f434e22c5cfce9", 459546),
 			Common::RU_RUS,
 			Common::kPlatformWindows,
-			ADGF_UNSTABLE,
+			ADGF_TESTING,
 			GUIO1(GUIO_NONE)
 		},
 		0x80000016


Commit: 503ad72e89d9f01dc433b3d03996da492c26b80a
    https://github.com/scummvm/scummvm/commit/503ad72e89d9f01dc433b3d03996da492c26b80a
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2026-06-17T23:12:44+02:00

Commit Message:
GAMOS: Enable engine by default

Changed paths:
    engines/gamos/configure.engine


diff --git a/engines/gamos/configure.engine b/engines/gamos/configure.engine
index 9e1e92694c2..4f2c6820ddf 100644
--- a/engines/gamos/configure.engine
+++ b/engines/gamos/configure.engine
@@ -1,3 +1,3 @@
 # This file is included from the main "configure" script
 # add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps] [components]
-add_engine gamos "Gamos" no "" "" "highres" "midi"
+add_engine gamos "Gamos" yes "" "" "highres" "midi"


Commit: 560416990ca530be89abce8d6874cd98b9671019
    https://github.com/scummvm/scummvm/commit/560416990ca530be89abce8d6874cd98b9671019
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2026-06-17T23:12:54+02:00

Commit Message:
NEWS: Mention the Gamos engine games support

Changed paths:
    NEWS.md


diff --git a/NEWS.md b/NEWS.md
index e93aa095b05..a685ee40c9c 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -9,6 +9,9 @@ For a more comprehensive changelog of the latest experimental code, see:
    - Added support for Noctropolis.
    - Added support for Cartoon Carnival.
    - Added support for Alfred Pelrock: En Busca de un Sue??o.
+   - Added support for Pilot Brothers: On the Track of Striped Elephant,
+     Pilot Brothers: The Case of Serial Maniac, and five minor
+     games built on the Gamos engine.
 
  General:
    - Highlight clickable links in the GUI with a cursor change.



From noreply at scummvm.org  Wed Jun 17 21:16:25 2026
From: noreply at scummvm.org (sev-)
Date: Wed, 17 Jun 2026 21:16:25 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm-web master ->
 7efab396d9fa305159f904b90ab18fcb36faf4f6
Message-ID: <20260617211625.E30037005BC@ron.scummvm.net>

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

Summary:
7efab396d9 NEWS: Gamos engine support


Commit: 7efab396d9fa305159f904b90ab18fcb36faf4f6
    https://github.com/scummvm/scummvm-web/commit/7efab396d9fa305159f904b90ab18fcb36faf4f6
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2026-06-17T23:16:00+02:00

Commit Message:
NEWS: Gamos engine support

Changed paths:
  A data/en/news/20260617.markdown


diff --git a/data/en/news/20260617.markdown b/data/en/news/20260617.markdown
new file mode 100644
index 00000000..57c0426f
--- /dev/null
+++ b/data/en/news/20260617.markdown
@@ -0,0 +1,18 @@
+---
+title: "ScummVM on track for striped patience"
+author: zidane
+date: 1781730558
+---
+ScummVM has officially wrapped up on Gamos???s Windows proprietary game engine. The engine originally powered _Pilot Brothers 1 & 2_, launching the iconic adventure series before it later transitioned to qdEngine and NGI tech. The engine was also widely used to build a wave of office time-killer games.
+
+This engine adds support for 7 games:
+
+_Patience_
+_Flip-Flop_
+_NetWalk_
+_Pilot Brothers: On the Track of Striped Elephant_
+_Pilot Brothers: The Case of Serial Maniac_
+_Vitamin_
+_WildSnake_
+
+So, if you would like to remember the past and spend some time with two eccentric detectives, Chief and Colleague, or kill some time with Solitaire-style games, you need the latest [daily development build](https://www.scummvm.org/downloads/#daily) and the game???s required data files. As always, if you encounter any irregularities or oddities, please submit those to our [bug tracker](https://bugs.scummvm.org/).



From noreply at scummvm.org  Wed Jun 17 21:21:14 2026
From: noreply at scummvm.org (lotharsm)
Date: Wed, 17 Jun 2026 21:21:14 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm-web master ->
 0e86d0cbfa2d166664ed6c783e1e90c7e9158acd
Message-ID: <20260617212114.5E68E7005BC@ron.scummvm.net>

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

Summary:
dcba0616bf L10N: Update base news translation file
0e86d0cbfa DATA: Update data files


Commit: dcba0616bfafd57d3ed21b6286c705876c349633
    https://github.com/scummvm/scummvm-web/commit/dcba0616bfafd57d3ed21b6286c705876c349633
Author: sev- (sev at scummvm.org)
Date: 2026-06-17T21:21:09Z

Commit Message:
L10N: Update base news translation file

Changed paths:
    data/en/news.json


diff --git a/data/en/news.json b/data/en/news.json
index db5a30a8..58b9b677 100644
--- a/data/en/news.json
+++ b/data/en/news.json
@@ -1758,5 +1758,9 @@
     "20260614": {
         "title": "Alfred Pelrock joins ScummVM",
         "content": "Good news for adventure game fans: _Alfred Pelrock_ is now supported in ScummVM!\n\nOriginally released in 1996, _Alfred Pelrock_ is a humorous point-and-click adventure featuring cartoon-style graphics, quirky puzzles, and an irreverent sense of humor. After dreaming of an Egyptian princess imprisoned in the distant past, Alfred embarks on a time-travelling adventure to Ancient Egypt to rescue her, encountering bizarre situations and eccentric characters along the way.\n\nWhile the game is packed with comedy and satire, players should be aware that it also includes mildly suggestive themes and occasional adult-oriented humor typical of its era. It is also worth noting that the game was released exclusively in Spanish and has no official translations.\n\nAs always, if you own an original copy of the game and speak Spanish, grab the latest [daily development build](https://www.scummvm.org/downloads/#daily), add the game???s [required data files](https://wiki.scummvm.org/index.php?title=Alfred_Pelrock:_En_Busca_de_un_Sue%C3%B1o) and enjoy. We also welcome [bug reports](https://bugs.scummvm.org/). and feedback from players to help improve compatibility and preserve this quirky piece of adventure gaming history."
+    },
+    "20260617": {
+        "title": "ScummVM on track for striped patience",
+        "content": "ScummVM has officially wrapped up on Gamos???s Windows proprietary game engine. The engine originally powered _Pilot Brothers 1 & 2_, launching the iconic adventure series before it later transitioned to qdEngine and NGI tech. The engine was also widely used to build a wave of office time-killer games.\n\nThis engine adds support for 7 games:\n\n_Patience_\n_Flip-Flop_\n_NetWalk_\n_Pilot Brothers: On the Track of Striped Elephant_\n_Pilot Brothers: The Case of Serial Maniac_\n_Vitamin_\n_WildSnake_\n\nSo, if you would like to remember the past and spend some time with two eccentric detectives, Chief and Colleague, or kill some time with Solitaire-style games, you need the latest [daily development build](https://www.scummvm.org/downloads/#daily) and the game???s required data files. As always, if you encounter any irregularities or oddities, please submit those to our [bug tracker](https://bugs.scummvm.org/)."
     }
 }


Commit: 0e86d0cbfa2d166664ed6c783e1e90c7e9158acd
    https://github.com/scummvm/scummvm-web/commit/0e86d0cbfa2d166664ed6c783e1e90c7e9158acd
Author: sev- (sev at scummvm.org)
Date: 2026-06-17T21:21:09Z

Commit Message:
DATA: Update data files

Changed paths:
    data/en/compatibility.yaml
    data/en/screenshots.yaml


diff --git a/data/en/compatibility.yaml b/data/en/compatibility.yaml
index 71575298..26e0a38f 100644
--- a/data/en/compatibility.yaml
+++ b/data/en/compatibility.yaml
@@ -5802,3 +5802,52 @@
     since_version: DEV
     stable_platforms: dos
     unstable_platforms: ''
+-
+    id: 'gamos:solgamer'
+    support: good
+    notes: ''
+    since_version: DEV
+    stable_platforms: win
+    unstable_platforms: ''
+-
+    id: 'gamos:pilots1'
+    support: good
+    notes: '- Only Windows version is supported'
+    since_version: DEV
+    stable_platforms: win
+    unstable_platforms: ''
+-
+    id: 'gamos:pilots2'
+    support: good
+    notes: '- Only Windows version is supported'
+    since_version: DEV
+    stable_platforms: win
+    unstable_platforms: ''
+-
+    id: 'gamos:wild'
+    support: good
+    notes: ''
+    since_version: DEV
+    stable_platforms: win
+    unstable_platforms: ''
+-
+    id: 'gamos:flop'
+    support: good
+    notes: ''
+    since_version: DEV
+    stable_platforms: win
+    unstable_platforms: ''
+-
+    id: 'gamos:netwalk'
+    support: good
+    notes: ''
+    since_version: DEV
+    stable_platforms: win
+    unstable_platforms: ''
+-
+    id: 'gamos:vitamin'
+    support: good
+    notes: ''
+    since_version: DEV
+    stable_platforms: win
+    unstable_platforms: ''
diff --git a/data/en/screenshots.yaml b/data/en/screenshots.yaml
index 9d68f1de..828baa62 100644
--- a/data/en/screenshots.yaml
+++ b/data/en/screenshots.yaml
@@ -1067,6 +1067,48 @@
     platform_id: zx
     language: en
     variant_id: '1'
+-
+    id: 'gamos:solgamer'
+    variant: ''
+    platform_id: win
+    language: en
+    variant_id: '1'
+-
+    id: 'gamos:pilots1'
+    variant: ''
+    platform_id: win
+    language: en
+    variant_id: '1'
+-
+    id: 'gamos:pilots2'
+    variant: ''
+    platform_id: win
+    language: ru
+    variant_id: '1'
+-
+    id: 'gamos:wild'
+    variant: ''
+    platform_id: win
+    language: en
+    variant_id: '1'
+-
+    id: 'gamos:flop'
+    variant: ''
+    platform_id: win
+    language: en
+    variant_id: '1'
+-
+    id: 'gamos:netwalk'
+    variant: ''
+    platform_id: win
+    language: en
+    variant_id: '1'
+-
+    id: 'gamos:vitamin'
+    variant: ''
+    platform_id: win
+    language: en
+    variant_id: '1'
 -
     id: 'glk:corruption'
     variant: ''



From noreply at scummvm.org  Wed Jun 17 21:23:53 2026
From: noreply at scummvm.org (sev-)
Date: Wed, 17 Jun 2026 21:23:53 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm-web master ->
 f4eb00222e422d56f843a0be4885256f55602d17
Message-ID: <20260617212353.D7D467005BC@ron.scummvm.net>

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

Summary:
f4eb00222e NEWS: Fix formatting


Commit: f4eb00222e422d56f843a0be4885256f55602d17
    https://github.com/scummvm/scummvm-web/commit/f4eb00222e422d56f843a0be4885256f55602d17
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2026-06-17T23:23:47+02:00

Commit Message:
NEWS: Fix formatting

Changed paths:
    data/en/news/20260617.markdown


diff --git a/data/en/news/20260617.markdown b/data/en/news/20260617.markdown
index 57c0426f..cc09685b 100644
--- a/data/en/news/20260617.markdown
+++ b/data/en/news/20260617.markdown
@@ -7,12 +7,12 @@ ScummVM has officially wrapped up on Gamos???s Windows proprietary game engine.
 
 This engine adds support for 7 games:
 
-_Patience_
-_Flip-Flop_
-_NetWalk_
-_Pilot Brothers: On the Track of Striped Elephant_
-_Pilot Brothers: The Case of Serial Maniac_
-_Vitamin_
-_WildSnake_
+* _Patience_
+* _Flip-Flop_
+* _NetWalk_
+* _Pilot Brothers: On the Track of Striped Elephant_
+* _Pilot Brothers: The Case of Serial Maniac_
+* _Vitamin_
+* _WildSnake_
 
 So, if you would like to remember the past and spend some time with two eccentric detectives, Chief and Colleague, or kill some time with Solitaire-style games, you need the latest [daily development build](https://www.scummvm.org/downloads/#daily) and the game???s required data files. As always, if you encounter any irregularities or oddities, please submit those to our [bug tracker](https://bugs.scummvm.org/).



From noreply at scummvm.org  Wed Jun 17 21:24:42 2026
From: noreply at scummvm.org (lotharsm)
Date: Wed, 17 Jun 2026 21:24:42 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm-web master ->
 ce98b16514418d66e546c00cffe8fb6919d6965c
Message-ID: <20260617212442.3F0CF7005BC@ron.scummvm.net>

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

Summary:
ce98b16514 L10N: Update base news translation file


Commit: ce98b16514418d66e546c00cffe8fb6919d6965c
    https://github.com/scummvm/scummvm-web/commit/ce98b16514418d66e546c00cffe8fb6919d6965c
Author: sev- (sev at scummvm.org)
Date: 2026-06-17T21:24:38Z

Commit Message:
L10N: Update base news translation file

Changed paths:
    data/en/news.json


diff --git a/data/en/news.json b/data/en/news.json
index 58b9b677..52e8f093 100644
--- a/data/en/news.json
+++ b/data/en/news.json
@@ -1761,6 +1761,6 @@
     },
     "20260617": {
         "title": "ScummVM on track for striped patience",
-        "content": "ScummVM has officially wrapped up on Gamos???s Windows proprietary game engine. The engine originally powered _Pilot Brothers 1 & 2_, launching the iconic adventure series before it later transitioned to qdEngine and NGI tech. The engine was also widely used to build a wave of office time-killer games.\n\nThis engine adds support for 7 games:\n\n_Patience_\n_Flip-Flop_\n_NetWalk_\n_Pilot Brothers: On the Track of Striped Elephant_\n_Pilot Brothers: The Case of Serial Maniac_\n_Vitamin_\n_WildSnake_\n\nSo, if you would like to remember the past and spend some time with two eccentric detectives, Chief and Colleague, or kill some time with Solitaire-style games, you need the latest [daily development build](https://www.scummvm.org/downloads/#daily) and the game???s required data files. As always, if you encounter any irregularities or oddities, please submit those to our [bug tracker](https://bugs.scummvm.org/)."
+        "content": "ScummVM has officially wrapped up on Gamos???s Windows proprietary game engine. The engine originally powered _Pilot Brothers 1 & 2_, launching the iconic adventure series before it later transitioned to qdEngine and NGI tech. The engine was also widely used to build a wave of office time-killer games.\n\nThis engine adds support for 7 games:\n\n* _Patience_\n* _Flip-Flop_\n* _NetWalk_\n* _Pilot Brothers: On the Track of Striped Elephant_\n* _Pilot Brothers: The Case of Serial Maniac_\n* _Vitamin_\n* _WildSnake_\n\nSo, if you would like to remember the past and spend some time with two eccentric detectives, Chief and Colleague, or kill some time with Solitaire-style games, you need the latest [daily development build](https://www.scummvm.org/downloads/#daily) and the game???s required data files. As always, if you encounter any irregularities or oddities, please submit those to our [bug tracker](https://bugs.scummvm.org/)."
     }
 }



From noreply at scummvm.org  Wed Jun 17 21:28:03 2026
From: noreply at scummvm.org (lotharsm)
Date: Wed, 17 Jun 2026 21:28:03 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm-web master ->
 745eda1c4677a761e8ca41885515ae2f8c76a2c0
Message-ID: <20260617212803.36FE47005BC@ron.scummvm.net>

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

Summary:
745eda1c46 DATA: Update data files


Commit: 745eda1c4677a761e8ca41885515ae2f8c76a2c0
    https://github.com/scummvm/scummvm-web/commit/745eda1c4677a761e8ca41885515ae2f8c76a2c0
Author: sev- (sev at scummvm.org)
Date: 2026-06-17T21:27:59Z

Commit Message:
DATA: Update data files

Changed paths:
    data/en/engines.yaml


diff --git a/data/en/engines.yaml b/data/en/engines.yaml
index 17c42d3c..4ce147ae 100644
--- a/data/en/engines.yaml
+++ b/data/en/engines.yaml
@@ -183,7 +183,7 @@
     id: gamos
     name: Gamos
     alt_name: ''
-    enabled: false
+    enabled: true
 -
     id: glk
     name: Glk



From noreply at scummvm.org  Wed Jun 17 22:10:22 2026
From: noreply at scummvm.org (ScummVM-Translations)
Date: Wed, 17 Jun 2026 22:10:22 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 8280c13b4ef6b42be01aa186c9164ce1a8bca452
Message-ID: <20260617221022.7A16A7005BC@ron.scummvm.net>

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

Summary:
8280c13b4e I18N: Update translation (Portuguese (Brazil))


Commit: 8280c13b4ef6b42be01aa186c9164ce1a8bca452
    https://github.com/scummvm/scummvm/commit/8280c13b4ef6b42be01aa186c9164ce1a8bca452
Author: Marcel Souza Lemes (marcosoutsider at gmail.com)
Date: 2026-06-17T22:10:14Z

Commit Message:
I18N: Update translation (Portuguese (Brazil))

Currently translated at 78.4% (2526 of 3221 strings)

Changed paths:
    po/pt_BR.po


diff --git a/po/pt_BR.po b/po/pt_BR.po
index a08185b8ffe..cc4ca984941 100644
--- a/po/pt_BR.po
+++ b/po/pt_BR.po
@@ -8,7 +8,7 @@ msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
 "POT-Creation-Date: 2026-06-14 22:15+0000\n"
-"PO-Revision-Date: 2024-11-16 02:13+0000\n"
+"PO-Revision-Date: 2026-06-17 22:10+0000\n"
 "Last-Translator: Marcel Souza Lemes <marcosoutsider at gmail.com>\n"
 "Language-Team: Portuguese (Brazil) <https://translations.scummvm.org/"
 "projects/scummvm/scummvm/pt_BR/>\n"
@@ -17,7 +17,7 @@ msgstr ""
 "Content-Type: text/plain; charset=utf-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=2; plural=n > 1;\n"
-"X-Generator: Weblate 5.7.2\n"
+"X-Generator: Weblate 5.15.1\n"
 "X-Poedit-Language: Portuguese\n"
 "X-Poedit-Country: BRAZIL\n"
 "X-Poedit-SourceCharset: iso-8859-1\n"
@@ -886,7 +886,7 @@ msgstr "Executar Cliente de E-mail"
 
 #: gui/integrity-dialog.cpp:152
 msgid "This game includes add-ons, pick the part you want to be checked:"
-msgstr ""
+msgstr "Este jogo inclui complementos, escolha a parte que deseja verificar:"
 
 #: gui/integrity-dialog.cpp:258
 msgid "Calculation complete"
@@ -1716,7 +1716,7 @@ msgstr "Op??. de Jo~g~o..."
 
 #: gui/launcher.cpp:1265
 msgid "Add-on"
-msgstr ""
+msgstr "Complemento"
 
 #. I18N: List grouping when no engine is specified
 #: gui/launcher.cpp:1335 gui/launcher.cpp:1580
@@ -2885,7 +2885,7 @@ msgstr "*  Abc"
 
 #: gui/printing-dialog.cpp:54
 msgid "Print"
-msgstr ""
+msgstr "Imprimir"
 
 #: gui/printing-dialog.cpp:56
 #, fuzzy
@@ -3933,6 +3933,10 @@ msgid ""
 "testing mode.\n"
 "If you encounter any bugs or oddities, please report them to our bugtracker."
 msgstr ""
+"AVISO: O jogo que voc?? est?? prestes a iniciar tem suporte recente e est?? em "
+"modo de testes.\n"
+"Se encontrar quaisquer erros ou anomalias, por favor reporte-os ao nosso "
+"rastreador de erros."
 
 #: engines/engine.cpp:861
 #, c-format
@@ -3941,6 +3945,10 @@ msgid ""
 "run independently. Please copy the add-on contents into a subdirectory of "
 "the base game, and start the base game itself."
 msgstr ""
+"O jogo \"%s\" que voc?? est?? tentando adicionar ?? um complemento de \"%s\" "
+"que n??o pode ser executado de forma independente. Por favor, copie o "
+"conte??do do complemento para um subdiret??rio do jogo base e inicie o pr??prio "
+"jogo base."
 
 #: engines/engine.cpp:878
 msgid "This game is not supported."
@@ -3968,11 +3976,14 @@ msgid ""
 "The directory '%s' looks like an add-on for the game '%s', but ScummVM could "
 "not find any matching add-on in it."
 msgstr ""
+"O diret??rio '%s' parece ser um complemento do jogo '%s', mas o ScummVM n??o "
+"encontrou nenhum complemento correspondente nele."
 
 #: engines/engine.cpp:1250
 #, c-format
 msgid "Directory '%s' matches several add-ons, please pick one."
 msgstr ""
+"O diret??rio '%s' corresponde a v??rios complementos, por favor escolha um."
 
 #: engines/game.cpp:199
 #, c-format
@@ -4488,7 +4499,7 @@ msgstr "Habilitar captura do mouse"
 
 #: backends/graphics/sdl/sdl-graphics.cpp:503
 msgid "Toggle resizable window"
-msgstr ""
+msgstr "Alternar janela redimension??vel"
 
 #: backends/graphics/sdl/sdl-graphics.cpp:508 engines/stark/metaengine.cpp:240
 #: engines/wintermute/keymapper_tables.h:646
@@ -5530,7 +5541,7 @@ msgstr "Mostrar controle na tela"
 
 #: backends/platform/android/options.cpp:138
 msgid "Ignore safe areas in-game"
-msgstr ""
+msgstr "Ignorar ??reas seguras no jogo"
 
 #: backends/platform/android/options.cpp:139
 #: backends/platform/ios7/ios7_options.mm:112
@@ -5613,19 +5624,19 @@ msgstr "Nos jogos"
 
 #: backends/platform/android/options.cpp:195
 msgid "Export backup"
-msgstr ""
+msgstr "Exportar backup"
 
 #: backends/platform/android/options.cpp:195
 msgid "Export a backup of the configuration and save files"
-msgstr ""
+msgstr "Exportar um backup das configura????es e dos arquivos de salvamento"
 
 #: backends/platform/android/options.cpp:196
 msgid "Import backup"
-msgstr ""
+msgstr "Importar backup"
 
 #: backends/platform/android/options.cpp:196
 msgid "Import a previously exported backup file"
-msgstr ""
+msgstr "Importar um arquivo de backup exportado anteriormente"
 
 #. I18N: This button opens a list of all folders added for Android Storage Attached Framework
 #: backends/platform/android/options.cpp:199
@@ -5640,11 +5651,11 @@ msgstr "Selecionar orienta????o:"
 
 #: backends/platform/android/options.cpp:280
 msgid "The backup has been saved successfully."
-msgstr ""
+msgstr "O backup foi salvo com sucesso."
 
 #: backends/platform/android/options.cpp:282
 msgid "The backup has been saved successfully to the Downloads folder."
-msgstr ""
+msgstr "O backup foi salvo com sucesso na pasta Downloads."
 
 #: backends/platform/android/options.cpp:284
 #: backends/platform/android/options.cpp:317
@@ -5664,6 +5675,8 @@ msgid ""
 "Restoring a backup will erase the current configuration and overwrite "
 "existing saves. Do you want to proceed?"
 msgstr ""
+"Restaurar um backup apagar?? a configura????o atual e substituir?? os "
+"salvamentos existentes. Deseja continuar?"
 
 #: backends/platform/android/options.cpp:298
 #, fuzzy
@@ -5673,7 +5686,7 @@ msgstr "Selecione um Tema"
 
 #: backends/platform/android/options.cpp:315
 msgid "The backup has been restored successfully."
-msgstr ""
+msgstr "O backup foi restaurado com sucesso."
 
 #: backends/platform/android/options.cpp:319
 #, fuzzy
@@ -6740,7 +6753,7 @@ msgstr "Usar"
 
 #: engines/access/metaengine.cpp:313
 msgid "Goto"
-msgstr ""
+msgstr "Ir para"
 
 #: engines/access/metaengine.cpp:318 engines/access/metaengine.cpp:349
 #: engines/bbvs/metaengine.cpp:130 engines/drascula/metaengine.cpp:250
@@ -6786,7 +6799,7 @@ msgstr "Invent??rio"
 
 #: engines/access/metaengine.cpp:344 engines/ultima/ultima4/metaengine.cpp:51
 msgid "Climb"
-msgstr ""
+msgstr "Escalar"
 
 #: engines/access/metaengine.cpp:354 engines/bbvs/metaengine.cpp:142
 #: engines/freescape/games/castle/castle.cpp:511
@@ -7034,7 +7047,7 @@ msgstr ""
 
 #: engines/agi/metaengine.cpp:207
 msgid "Use PCjr's sound chip in 16-bit shift mode"
-msgstr ""
+msgstr "Usar o chip de som do PCjr no modo de deslocamento de 16 bits"
 
 #: engines/agi/metaengine.cpp:208
 msgid ""
@@ -7043,6 +7056,10 @@ msgid ""
 "especially in fanmade games, but original Sierra music designed strictly for "
 "PCjr may sound wrong."
 msgstr ""
+"No modo de som PCjr, emula um chip de som SN76496 n??o padr??o, semelhante aos "
+"chips encontrados nos consoles SEGA Master System. Permite certos efeitos "
+"musicais, especialmente em jogos feitos por f??s, mas a m??sica original da "
+"Sierra projetada estritamente para PCjr pode soar incorreta."
 
 #: engines/agi/metaengine.cpp:447 engines/asylum/asylum.cpp:655
 #: engines/buried/saveload.cpp:53 engines/mohawk/riven.cpp:759
@@ -7113,7 +7130,7 @@ msgstr "Utilizar telas de salvar/carregar originais"
 
 #: engines/agos/dialogs.cpp:94
 msgid "Toggle between the system cursor and the default cursor."
-msgstr ""
+msgstr "Alternar entre o cursor do sistema e o cursor padr??o."
 
 #: engines/agos/dialogs.cpp:107 engines/sci/detection_options.h:261
 msgid "MIDI mode:"
@@ -7489,7 +7506,7 @@ msgstr ""
 
 #: engines/alcachofa/graphics-opengl.cpp:108
 msgid "Old OpenGL detected, some graphical errors will occur."
-msgstr ""
+msgstr "OpenGL antigo detectado, alguns erros gr??ficos ocorrer??o."
 
 #: engines/alcachofa/metaengine.cpp:40
 #, fuzzy
@@ -7505,11 +7522,11 @@ msgstr "Ligar/desligar efeito sonoro"
 
 #: engines/alcachofa/metaengine.cpp:51
 msgid "32 Bits"
-msgstr ""
+msgstr "32 bits"
 
 #: engines/alcachofa/metaengine.cpp:52
 msgid "Whether to render the game in 16-bit color"
-msgstr ""
+msgstr "Define se o jogo deve ser renderizado em cores de 16 bits"
 
 #: engines/alcachofa/metaengine.cpp:62
 #, fuzzy
@@ -7519,7 +7536,7 @@ msgstr "Gr??ficos com filtro"
 
 #: engines/alcachofa/metaengine.cpp:63
 msgid "Whether textures should be linearly filtered"
-msgstr ""
+msgstr "Define se as texturas devem ser filtradas linearmente"
 
 #: engines/alcachofa/metaengine.cpp:98 engines/cine/metaengine.cpp:347
 #: engines/freescape/games/castle/castle.cpp:528
@@ -7541,13 +7558,15 @@ msgstr "Ativar/desativar legendas"
 
 #: engines/alg/metaengine.cpp:34
 msgid "Use lower quality single speed CD-ROM video"
-msgstr ""
+msgstr "Usar v??deo de CD-ROM de velocidade simples e qualidade inferior"
 
 #: engines/alg/metaengine.cpp:35
 msgid ""
 "These videos are of lower quality, the default version uses double speed CD-"
 "ROM videos which are of better quality"
 msgstr ""
+"Estes v??deos s??o de qualidade inferior; a vers??o padr??o usa v??deos de CD-ROM "
+"de velocidade dupla, que s??o de melhor qualidade"
 
 #: engines/asylum/metaengine.cpp:115
 msgid "Show version"
@@ -7638,7 +7657,7 @@ msgstr "Carregar mesmo assim"
 
 #: engines/awe/metaengine.cpp:35
 msgid "Enable title and copy protection screens (if present)"
-msgstr ""
+msgstr "Ativar telas de t??tulo e de prote????o contra c??pia (se presentes)"
 
 #: engines/awe/metaengine.cpp:36
 #, fuzzy
@@ -7663,7 +7682,7 @@ msgstr "Pular"
 
 #: engines/awe/metaengine.cpp:63
 msgid "Enter level code"
-msgstr ""
+msgstr "Inserir c??digo de fase"
 
 #: engines/awe/metaengine.cpp:88 engines/dgds/metaengine.cpp:87
 #: engines/got/metaengine.cpp:105
@@ -7712,7 +7731,7 @@ msgstr "Tecla anti-chefe"
 
 #: engines/bagel/metaengine.cpp:73
 msgid "End"
-msgstr ""
+msgstr "Fim"
 
 #: engines/bagel/metaengine.cpp:180
 msgid "General Keys"
@@ -8113,6 +8132,8 @@ msgid ""
 "This option allows the game to play at a slightly extended resolution. This "
 "is what the Mac and CDi versions are supposed to play like."
 msgstr ""
+"Esta op????o permite que o jogo seja executado em uma resolu????o ligeiramente "
+"estendida. ?? assim que as vers??es Mac e CDi deveriam ser exibidas."
 
 #: engines/buried/buried.cpp:598
 msgid "Are you sure you want to quit?"
@@ -8212,27 +8233,27 @@ msgstr "Mover para frente"
 #. I18N: AI is Artificial Intelligence
 #: engines/buried/metaengine.cpp:230
 msgid "Replay last AI comment"
-msgstr ""
+msgstr "Repetir o ??ltimo coment??rio da IA"
 
 #. I18N: The game has an inventory with a list of biochips. This action is used to switch the equipped biochip to the Artificial Intelligence biochip.
 #: engines/buried/metaengine.cpp:237
 msgid "Biochip AI"
-msgstr ""
+msgstr "Biochip IA"
 
 #. I18N: The game has an inventory with a list of biochips. This action is used to switch the equipped biochip to the blank biochip.
 #: engines/buried/metaengine.cpp:243
 msgid "Biochip blank"
-msgstr ""
+msgstr "Biochip em branco"
 
 #. I18N: The game has an inventory with a list of biochips. This action is used to switch the equipped biochip to the cloak biochip.
 #: engines/buried/metaengine.cpp:249
 msgid "Biochip cloak"
-msgstr ""
+msgstr "Biochip de camuflagem"
 
 #. I18N: The game has an inventory with a list of biochips. This action is used to switch the equipped biochip to the evidence biochip.
 #: engines/buried/metaengine.cpp:255
 msgid "Biochip evidence"
-msgstr ""
+msgstr "Biochip de evid??ncias"
 
 #. I18N: The game has an inventory with a list of biochips. This action is used to switch the equipped biochip to the files biochip.
 #: engines/buried/metaengine.cpp:261
@@ -8244,17 +8265,17 @@ msgstr "Exibir arquivos ocultos"
 #. I18N: The game has an inventory with a list of biochips. This action is used to switch the equipped biochip to the interface biochip.
 #: engines/buried/metaengine.cpp:267
 msgid "Biochip interface"
-msgstr ""
+msgstr "Biochip de interface"
 
 #. I18N: The game has an inventory with a list of biochips. This action is used to switch the equipped biochip to the jump biochip.
 #: engines/buried/metaengine.cpp:273
 msgid "Biochip jump"
-msgstr ""
+msgstr "Biochip de salto"
 
 #. I18N: The game has an inventory with a list of biochips. This action is used to switch the equipped biochip to the translate biochip.
 #: engines/buried/metaengine.cpp:279
 msgid "Biochip translate"
-msgstr ""
+msgstr "Biochip de tradu????o"
 
 #. I18N: Shows a summary of collected points in the game.
 #: engines/buried/metaengine.cpp:285
@@ -8647,11 +8668,11 @@ msgstr "Pr??xima a????o"
 
 #: engines/colony/savegame.cpp:291
 msgid "Saving is only available in first-person view."
-msgstr ""
+msgstr "Salvar s?? est?? dispon??vel na vis??o em primeira pessoa."
 
 #: engines/colony/savegame.cpp:300
 msgid "Loading is only available in first-person view."
-msgstr ""
+msgstr "Carregar s?? est?? dispon??vel na vis??o em primeira pessoa."
 
 #: engines/crab/input/input.cpp:119
 #, fuzzy
@@ -8789,6 +8810,8 @@ msgid ""
 "Use the music from the floppy version. The floppy version's music files must "
 "be copied to the SOUND directory."
 msgstr ""
+"Usar a m??sica da vers??o em disquete. Os arquivos de m??sica da vers??o em "
+"disquete devem ser copiados para o diret??rio SOUND."
 
 #: engines/darkseed/dialogs.cpp:121
 #, fuzzy
@@ -8802,18 +8825,22 @@ msgid ""
 "additional floppy SFX, or floppy SFX only. Floppy SFX are only available if "
 "floppy music is used."
 msgstr ""
+"Determina se o jogo deve usar apenas os efeitos sonoros da vers??o em CD, os "
+"efeitos sonoros do CD com efeitos adicionais da vers??o em disquete, ou "
+"apenas os efeitos sonoros da vers??o em disquete. Os efeitos sonoros do "
+"disquete s?? est??o dispon??veis se a m??sica do disquete for usada."
 
 #: engines/darkseed/dialogs.cpp:127
 msgid "CD version SFX only"
-msgstr ""
+msgstr "Apenas efeitos sonoros da vers??o em CD"
 
 #: engines/darkseed/dialogs.cpp:131
 msgid "CD + extra floppy SFX"
-msgstr ""
+msgstr "CD + efeitos sonoros extras do disquete"
 
 #: engines/darkseed/dialogs.cpp:135
 msgid "Floppy version SFX only"
-msgstr ""
+msgstr "Apenas efeitos sonoros da vers??o em disquete"
 
 #. I18N: Perform action on object where cursor points
 #: engines/darkseed/metaengine.cpp:42 engines/dragons/metaengine.cpp:135
@@ -8883,11 +8910,11 @@ msgstr "Corre????o de propor????o habilitada"
 
 #: engines/director/metaengine.cpp:71
 msgid "Brighten the graphics to simulate a Macintosh monitor"
-msgstr ""
+msgstr "Clarear os gr??ficos para simular um monitor Macintosh"
 
 #: engines/director/metaengine.cpp:81
 msgid "Force true color (32bpp) mode"
-msgstr ""
+msgstr "For??ar o modo de cores reais (32bpp)"
 
 #: engines/director/metaengine.cpp:82
 #, fuzzy
@@ -8914,7 +8941,7 @@ msgstr "Alternar exibi????o do Invent??rio/Pontos de QI"
 #. I18N: (Game name: Dungeon Master) The game has multi-choice dialogs. If there is only one choice, then this action can be used to select it.
 #: engines/dm/metaengine.cpp:144
 msgid "Select dialog choice (only works if there is a single choice)"
-msgstr ""
+msgstr "Selecionar a op????o de di??logo (s?? funciona se houver uma ??nica op????o)"
 
 #. I18N: (Game name: Dungeon Master) The player has a team of 4 characters called "champions" which they choose themselves. This action toggles the inventory screen of champion 1.
 #: engines/dm/metaengine.cpp:151
@@ -8958,7 +8985,7 @@ msgstr "Mover para tr??s"
 #. I18N: (Game name: Dungeon Master) The player has a team of 4 characters called "champions" which they choose themselves. The champions can be put to sleep. This action wakes up the sleeping champion.
 #: engines/dm/metaengine.cpp:247
 msgid "Wake up champion"
-msgstr ""
+msgstr "Acordar o campe??o"
 
 #: engines/draci/metaengine.cpp:68 engines/prince/metaengine.cpp:64
 #, fuzzy
@@ -9341,7 +9368,7 @@ msgstr "Soltar"
 
 #: engines/efh/metaengine.cpp:437
 msgid "Equip"
-msgstr ""
+msgstr "Equipar"
 
 #: engines/efh/metaengine.cpp:442 engines/queen/metaengine.cpp:274
 #: engines/saga/metaengine.cpp:384 engines/scumm/help.cpp:124
@@ -9361,11 +9388,11 @@ msgstr "Menu de Informa????o"
 
 #: engines/efh/metaengine.cpp:452
 msgid "Passive skills"
-msgstr ""
+msgstr "Habilidades passivas"
 
 #: engines/efh/metaengine.cpp:457
 msgid "Trade"
-msgstr ""
+msgstr "Negociar"
 
 #: engines/efh/metaengine.cpp:467
 #, fuzzy
@@ -9381,7 +9408,7 @@ msgstr "Sair do jogo"
 
 #: engines/efh/metaengine.cpp:493
 msgid "Leave"
-msgstr ""
+msgstr "Sair"
 
 #: engines/efh/metaengine.cpp:500 engines/efh/metaengine.cpp:536
 #, fuzzy
@@ -9391,7 +9418,7 @@ msgstr "Status:"
 
 #: engines/efh/metaengine.cpp:518
 msgid "Defend"
-msgstr ""
+msgstr "Defender"
 
 #. I18N: Run is a movement type
 #. I18N: Action in In Cold Blood
@@ -9509,7 +9536,7 @@ msgstr "Reproduzir nota 1: A"
 
 #: engines/efh/metaengine.cpp:657
 msgid "Play sound type 9"
-msgstr ""
+msgstr "Reproduzir som tipo 9"
 
 #: engines/efh/metaengine.cpp:662
 #, fuzzy
@@ -9593,11 +9620,11 @@ msgstr "Habilita viagem utilizando lan??amento de pedra desde o in??cio"
 
 #: engines/freescape/metaengine.cpp:140
 msgid "Smoother movement"
-msgstr ""
+msgstr "Movimento mais suave"
 
 #: engines/freescape/metaengine.cpp:141
 msgid "Use smoother movements instead of discrete steps"
-msgstr ""
+msgstr "Usar movimentos mais suaves em vez de passos discretos"
 
 #. I18N: Use modern FPS-style controls: WASD for movement, Shift to run
 #: engines/freescape/metaengine.cpp:152
@@ -9608,26 +9635,27 @@ msgstr "Controles de Toque"
 
 #: engines/freescape/metaengine.cpp:153
 msgid "Use WASD keys for movement and Shift to run"
-msgstr ""
+msgstr "Usar as teclas WASD para se mover e Shift para correr"
 
 #. I18N: Enable background music using AdLib/OPL2 FM synthesis
 #: engines/freescape/metaengine.cpp:164
 msgid "Backported music from C64 releases (AdLib)"
-msgstr ""
+msgstr "M??sica adaptada das vers??es do C64 (AdLib)"
 
 #: engines/freescape/metaengine.cpp:165
 msgid ""
 "Enable background music ported from the C64 version using AdLib FM synthesis"
 msgstr ""
+"Ativar a m??sica de fundo adaptada da vers??o do C64 usando s??ntese FM AdLib"
 
 #. I18N: Enable background music using AY chip emulation
 #: engines/freescape/metaengine.cpp:176
 msgid "Backported music from C64 releases"
-msgstr ""
+msgstr "M??sica adaptada das vers??es do C64"
 
 #: engines/freescape/metaengine.cpp:177
 msgid "Enable background music ported from the C64 version"
-msgstr ""
+msgstr "Ativar a m??sica de fundo adaptada da vers??o do C64"
 
 #. I18N: Combat command in Might & Magic 1
 #: engines/freescape/movement.cpp:69 engines/mm/mm1/metaengine.cpp:84
@@ -9692,7 +9720,7 @@ msgstr "Rastejar"
 
 #: engines/freescape/games/castle/castle.cpp:534
 msgid "Run (hold)"
-msgstr ""
+msgstr "Correr (segurar)"
 
 #: engines/freescape/games/dark/dark.cpp:262
 #: engines/freescape/games/driller/driller.cpp:182
@@ -9787,7 +9815,7 @@ msgstr "Mudar Tamanho do Passo"
 
 #: engines/freescape/games/eclipse/eclipse.cpp:376
 msgid "Sprint (hold)"
-msgstr ""
+msgstr "Disparada (segurar)"
 
 #: engines/freescape/games/eclipse/eclipse.cpp:384
 #, fuzzy
@@ -9876,11 +9904,11 @@ msgstr "Janela de imagem alternada\n"
 
 #: engines/glk/dialogs.cpp:141
 msgid "white"
-msgstr ""
+msgstr "branco"
 
 #: engines/glk/dialogs.cpp:142
 msgid "green"
-msgstr ""
+msgstr "verde"
 
 #: engines/glk/dialogs.cpp:143
 #, fuzzy
@@ -9890,47 +9918,47 @@ msgstr "Prosseguir"
 
 #: engines/glk/dialogs.cpp:144
 msgid "blue"
-msgstr ""
+msgstr "azul"
 
 #: engines/glk/dialogs.cpp:145
 msgid "black"
-msgstr ""
+msgstr "preto"
 
 #: engines/glk/dialogs.cpp:146
 msgid "grey"
-msgstr ""
+msgstr "cinza"
 
 #: engines/glk/dialogs.cpp:195
 msgid "Monospace Regular"
-msgstr ""
+msgstr "Monoespa??ada Regular"
 
 #: engines/glk/dialogs.cpp:196
 msgid "Monospace Bold"
-msgstr ""
+msgstr "Monoespa??ada Negrito"
 
 #: engines/glk/dialogs.cpp:197
 msgid "Monospace Italic"
-msgstr ""
+msgstr "Monoespa??ada It??lico"
 
 #: engines/glk/dialogs.cpp:198
 msgid "Monospace Bold Italic"
-msgstr ""
+msgstr "Monoespa??ada Negrito It??lico"
 
 #: engines/glk/dialogs.cpp:199
 msgid "Proportional Regular"
-msgstr ""
+msgstr "Proporcional Regular"
 
 #: engines/glk/dialogs.cpp:200
 msgid "Proportional Bold"
-msgstr ""
+msgstr "Proporcional Negrito"
 
 #: engines/glk/dialogs.cpp:201
 msgid "Proportional Italic"
-msgstr ""
+msgstr "Proporcional It??lico"
 
 #: engines/glk/dialogs.cpp:202
 msgid "Proportional Bold Italic"
-msgstr ""
+msgstr "Proporcional Negrito It??lico"
 
 #: engines/glk/dialogs.cpp:210
 #, fuzzy
@@ -9940,23 +9968,24 @@ msgstr "SoundFont:"
 
 #: engines/glk/dialogs.cpp:212 engines/glk/dialogs.cpp:222
 msgid "Text:"
-msgstr ""
+msgstr "Texto:"
 
 #: engines/glk/dialogs.cpp:212
 msgid "Font for the text"
-msgstr ""
+msgstr "Fonte para o texto"
 
 #: engines/glk/dialogs.cpp:216 engines/glk/dialogs.cpp:227
 msgid "Grid:"
-msgstr ""
+msgstr "Grade:"
 
 #: engines/glk/dialogs.cpp:216
 msgid "Font for drawn graphics like maps, diagrams, puzzles, etc"
 msgstr ""
+"Fonte para gr??ficos desenhados como mapas, diagramas, quebra-cabe??as, etc."
 
 #: engines/glk/dialogs.cpp:220
 msgid "Color"
-msgstr ""
+msgstr "Cor"
 
 #: engines/glk/dialogs.cpp:222
 #, fuzzy
@@ -9969,11 +9998,12 @@ msgstr "Ir para pr??ximo objeto"
 #: engines/glk/dialogs.cpp:254 engines/glk/dialogs.cpp:260
 #: engines/glk/dialogs.cpp:269
 msgid "<custom>"
-msgstr ""
+msgstr "<personalizado>"
 
 #: engines/glk/dialogs.cpp:227
 msgid "Color for drawn graphics such as maps, diagrams, puzzles, etc"
 msgstr ""
+"Cor para gr??ficos desenhados como mapas, diagramas, quebra-cabe??as, etc."
 
 #: engines/glk/dialogs.cpp:232
 #, fuzzy
@@ -10034,7 +10064,7 @@ msgstr "Sub-escaneamento vertical:"
 
 #: engines/glk/dialogs.cpp:250
 msgid "Caret:"
-msgstr ""
+msgstr "Cursor:"
 
 #: engines/glk/dialogs.cpp:250
 #, fuzzy
@@ -10044,20 +10074,20 @@ msgstr "Cursor normal"
 
 #: engines/glk/dialogs.cpp:256
 msgid "Link:"
-msgstr ""
+msgstr "Link:"
 
 #: engines/glk/dialogs.cpp:256
 msgid "Color for URLs if they appear in-game"
-msgstr ""
+msgstr "Cor para URLs caso apare??am no jogo"
 
 #. I18N: "More..." is a prompt in text windows, which appears when the text exceeds the window size
 #: engines/glk/dialogs.cpp:264
 msgid "More:"
-msgstr ""
+msgstr "Mais:"
 
 #: engines/glk/dialogs.cpp:265
 msgid "Color for the \"More...\" markers in the text"
-msgstr ""
+msgstr "Cor para os marcadores \"Mais...\" no texto"
 
 #: engines/glk/dialogs.cpp:272
 #, fuzzy
@@ -10067,11 +10097,11 @@ msgstr "Modo gr??fico:"
 
 #: engines/glk/dialogs.cpp:274
 msgid "Link style:"
-msgstr ""
+msgstr "Estilo do link:"
 
 #: engines/glk/dialogs.cpp:274
 msgid "Style for URLs if they appear in-game"
-msgstr ""
+msgstr "Estilo para URLs caso apare??am no jogo"
 
 #: engines/glk/dialogs.cpp:279
 #, fuzzy
@@ -10096,6 +10126,8 @@ msgid ""
 "Custom marker in place of the \"More...\" marker for eg. \"continue\". Leave "
 "it blank to use the game's default"
 msgstr ""
+"Marcador personalizado no lugar do marcador \"Mais...\", por exemplo "
+"\"continuar\". Deixe em branco para usar o padr??o do jogo"
 
 #: engines/glk/dialogs.cpp:288
 #, fuzzy
@@ -10105,7 +10137,7 @@ msgstr "Ocultar dicas"
 
 #: engines/glk/dialogs.cpp:288
 msgid "Let the game suggest text styling options"
-msgstr ""
+msgstr "Permitir que o jogo sugira op????es de estilo de texto"
 
 #: engines/glk/dialogs.cpp:289
 #, fuzzy
@@ -10115,7 +10147,7 @@ msgstr "Clique esquerdo"
 
 #: engines/glk/dialogs.cpp:289
 msgid "Safely apply clicks while input is pending"
-msgstr ""
+msgstr "Aplicar cliques com seguran??a enquanto h?? entrada pendente"
 
 #. I18N: This is setting for number of text columns
 #: engines/glk/dialogs.cpp:292
@@ -10126,24 +10158,24 @@ msgstr "Controles de Toque"
 
 #: engines/glk/dialogs.cpp:292
 msgid "Number of columns"
-msgstr ""
+msgstr "N??mero de colunas"
 
 #. I18N: This is setting for number of text rows
 #: engines/glk/dialogs.cpp:294
 msgid "Rows count:"
-msgstr ""
+msgstr "N??mero de linhas:"
 
 #: engines/glk/dialogs.cpp:294
 msgid "Number of rows"
-msgstr ""
+msgstr "N??mero de linhas"
 
 #: engines/glk/dialogs.cpp:296
 msgid "Lock columns:"
-msgstr ""
+msgstr "Travar colunas:"
 
 #: engines/glk/dialogs.cpp:296
 msgid "Check it to manually change the column count"
-msgstr ""
+msgstr "Marque para alterar manualmente o n??mero de colunas"
 
 #: engines/glk/dialogs.cpp:297
 #, fuzzy
@@ -10153,12 +10185,12 @@ msgstr "Defender embaixo"
 
 #: engines/glk/dialogs.cpp:297
 msgid "Check it to manually change the row count"
-msgstr ""
+msgstr "Marque para alterar manualmente o n??mero de linhas"
 
 #. I18N: This is a setting for enabling text justification
 #: engines/glk/dialogs.cpp:301
 msgid "Justify:"
-msgstr ""
+msgstr "Justificar:"
 
 #: engines/glk/dialogs.cpp:302
 #, fuzzy
@@ -10182,7 +10214,7 @@ msgstr "Gr??ficos coloridos"
 #: engines/glk/dialogs.cpp:307
 msgctxt "quotes"
 msgid "Off"
-msgstr ""
+msgstr "Desativado"
 
 #. I18N: This is a setting for using normal typographic quotes, which are like in most books, with the opening quote higher than the closing one
 #: engines/glk/dialogs.cpp:309
@@ -10196,40 +10228,40 @@ msgstr "Normal"
 #: engines/glk/dialogs.cpp:311
 msgctxt "quotes"
 msgid "Rabid"
-msgstr ""
+msgstr "Raivosas"
 
 #. I18N: This is a setting for forcing all input to be in uppercase
 #: engines/glk/dialogs.cpp:314
 msgid "Caps:"
-msgstr ""
+msgstr "Mai??sculas:"
 
 #: engines/glk/dialogs.cpp:314
 msgid "Force uppercase input"
-msgstr ""
+msgstr "For??ar entrada em mai??sculas"
 
 #. I18N: This is a setting for type of text dash symbols
 #: engines/glk/dialogs.cpp:317
 msgid "Dashes:"
-msgstr ""
+msgstr "Travess??es:"
 
 #. I18N: This is a setting for type of text dash symbols
 #: engines/glk/dialogs.cpp:319
 msgid "Types of dashes"
-msgstr ""
+msgstr "Tipos de travess??es"
 
 #: engines/glk/dialogs.cpp:320 engines/glk/dialogs.cpp:332
 msgid "Off"
-msgstr ""
+msgstr "Desativado"
 
 #. I18N: This is a setting for using normal typographic dashes, which are like in most books, with the em dash being the longest and the en dash being half of it
 #: engines/glk/dialogs.cpp:322
 msgid "Em dashes"
-msgstr ""
+msgstr "Travess??es longos (em)"
 
 #. I18N: This is a setting for using "en+em" dashes, which are like normal typographic dashes but with the en dash being the same length as the em dash, like in some comic books
 #: engines/glk/dialogs.cpp:324
 msgid "En+Em dashes"
-msgstr ""
+msgstr "Travess??es en+em"
 
 #: engines/glk/dialogs.cpp:326
 #, fuzzy
@@ -10247,17 +10279,17 @@ msgstr "Espa??o"
 #. I18N: This is a setting for type of spaces in the text
 #: engines/glk/dialogs.cpp:331
 msgid "Types of spaces"
-msgstr ""
+msgstr "Tipos de espa??os"
 
 #. I18N: This is a setting for compressing double spaces into single ones in text
 #: engines/glk/dialogs.cpp:334
 msgid "Compress double spaces"
-msgstr ""
+msgstr "Comprimir espa??os duplos"
 
 #. I18N: This is a setting for expanding single spaces into double ones in text
 #: engines/glk/dialogs.cpp:336
 msgid "Expand single spaces"
-msgstr ""
+msgstr "Expandir espa??os simples"
 
 #: engines/glk/dialogs.cpp:338
 #, fuzzy
@@ -10267,7 +10299,7 @@ msgstr "Gr??ficos"
 
 #: engines/glk/dialogs.cpp:338
 msgid "Turn graphics on/off"
-msgstr ""
+msgstr "Ativar/desativar gr??ficos"
 
 #: engines/glk/dialogs.cpp:340
 #, fuzzy
@@ -10277,11 +10309,11 @@ msgstr "Janela"
 
 #: engines/glk/dialogs.cpp:341
 msgid "Horizontal margin for window"
-msgstr ""
+msgstr "Margem horizontal da janela"
 
 #: engines/glk/dialogs.cpp:342
 msgid "Vertical margin for window"
-msgstr ""
+msgstr "Margem vertical da janela"
 
 #: engines/glk/dialogs.cpp:344
 #, fuzzy
@@ -10291,11 +10323,11 @@ msgstr "Janela"
 
 #: engines/glk/dialogs.cpp:345
 msgid "Horizontal padding for window"
-msgstr ""
+msgstr "Preenchimento horizontal da janela"
 
 #: engines/glk/dialogs.cpp:346
 msgid "Vertical padding for window"
-msgstr ""
+msgstr "Preenchimento vertical da janela"
 
 #: engines/glk/dialogs.cpp:348
 #, fuzzy
@@ -10305,11 +10337,11 @@ msgstr "Pr??xima a????o"
 
 #: engines/glk/dialogs.cpp:349
 msgid "Horizontal margin for text"
-msgstr ""
+msgstr "Margem horizontal do texto"
 
 #: engines/glk/dialogs.cpp:350
 msgid "Vertical margin for text"
-msgstr ""
+msgstr "Margem vertical do texto"
 
 #. I18N: This is a setting for leading, which is the vertical distance between text rows
 #: engines/glk/dialogs.cpp:353
@@ -10320,49 +10352,53 @@ msgstr "Carregando jogo..."
 #: engines/glk/dialogs.cpp:354
 msgid "Vertical distance between text rows. Valid inputs - 12, 16, 22 etc."
 msgstr ""
+"Dist??ncia vertical entre as linhas de texto. Valores v??lidos - 12, 16, 22, "
+"etc."
 
 #. I18N: This is a setting for baseline, which is the invisible horizontal line on which text sits
 #: engines/glk/dialogs.cpp:356
 msgid "Baseline:"
-msgstr ""
+msgstr "Linha de base:"
 
 #: engines/glk/dialogs.cpp:357
 msgid ""
 "Invisible horizontal line on which text sits. Valid inputs - 8, 16 etc. as "
 "per your font size"
 msgstr ""
+"Linha horizontal invis??vel sobre a qual o texto se apoia. Valores v??lidos - "
+"8, 16, etc., de acordo com o tamanho da sua fonte"
 
 #. I18N: Font size scaling for the monospace text font
 #: engines/glk/dialogs.cpp:360
 msgid "Monosize:"
-msgstr ""
+msgstr "Tamanho mono:"
 
 #: engines/glk/dialogs.cpp:361
 msgid "Font size scaling for the monospace text font"
-msgstr ""
+msgstr "Escala do tamanho da fonte para o texto monoespa??ado"
 
 #. I18N: Font size scaling for the proportional text font
 #: engines/glk/dialogs.cpp:363
 msgid "Propsize:"
-msgstr ""
+msgstr "Tamanho prop:"
 
 #: engines/glk/dialogs.cpp:364
 msgid "Font size scaling for the proportional text font"
-msgstr ""
+msgstr "Escala do tamanho da fonte para o texto proporcional"
 
 #. I18N: This is a section for settings related to the "More..." prompt in text windows
 #: engines/glk/dialogs.cpp:367
 msgid "More"
-msgstr ""
+msgstr "Mais"
 
 #. I18N: This is a setting for alignment of the "More..." prompt in text windows
 #: engines/glk/dialogs.cpp:369
 msgid "Align:"
-msgstr ""
+msgstr "Alinhamento:"
 
 #: engines/glk/dialogs.cpp:370
 msgid "Alignment of the \"More...\" marker on the window"
-msgstr ""
+msgstr "Alinhamento do marcador \"Mais...\" na janela"
 
 #: engines/glk/dialogs.cpp:375
 #, fuzzy
@@ -10372,7 +10408,7 @@ msgstr "SoundFont:"
 
 #: engines/glk/dialogs.cpp:375
 msgid "Font for the \"More...\" marker"
-msgstr ""
+msgstr "Fonte para o marcador \"Mais...\""
 
 #: engines/glk/glulx/glulx.cpp:81
 msgid "This is too short to be a valid Glulx file."
@@ -10432,11 +10468,11 @@ msgstr "Disparar"
 
 #: engines/got/metaengine.cpp:75
 msgid "Magic"
-msgstr ""
+msgstr "Magia"
 
 #: engines/got/metaengine.cpp:79
 msgid "Thor dies"
-msgstr ""
+msgstr "Thor morre"
 
 #: engines/got/got.cpp:196 engines/got/got.cpp:206
 #, fuzzy
@@ -10804,7 +10840,7 @@ msgstr "Interface f??cil do mouse"
 
 #: engines/hugo/metaengine.cpp:46
 msgid "Use mouse and toolbar from Windows version"
-msgstr ""
+msgstr "Usar o mouse e a barra de ferramentas da vers??o Windows"
 
 #: engines/hugo/metaengine.cpp:148
 msgid "User help"
@@ -10933,7 +10969,7 @@ msgstr "Mapeamentos de teclas de ??ndice"
 
 #: engines/hypno/metaengine.cpp:134 engines/hypno/metaengine.cpp:144
 msgid "Primary shoot"
-msgstr ""
+msgstr "Disparo prim??rio"
 
 #: engines/hypno/metaengine.cpp:139
 #, fuzzy
@@ -10967,7 +11003,7 @@ msgstr "Para Cima ?? Direita"
 
 #: engines/hypno/metaengine.cpp:170
 msgid "Aim up"
-msgstr ""
+msgstr "Mirar para cima"
 
 #: engines/hypno/metaengine.cpp:175 engines/scumm/metaengine.cpp:1202
 #: engines/scumm/metaengine.cpp:1279
@@ -11035,22 +11071,22 @@ msgstr "Pular fala"
 #: engines/hypno/metaengine.cpp:238 engines/hypno/metaengine.cpp:334
 #: engines/hypno/metaengine.cpp:388
 msgid "Kill player"
-msgstr ""
+msgstr "Matar o jogador"
 
 #. I18N: (Game name: Soldier Boyz) the game has 3 difficulty levels: Chump, Punk and Badass. Chump is the easy mode.
 #: engines/hypno/metaengine.cpp:262
 msgid "Chump"
-msgstr ""
+msgstr "Chump"
 
 #. I18N: (Game name: Soldier Boyz) the game has 3 difficulty levels: Chump, Punk and Badass. Punk is the medium mode.
 #: engines/hypno/metaengine.cpp:269
 msgid "Punk"
-msgstr ""
+msgstr "Punk"
 
 #. I18N: (Game name: Soldier Boyz) the game has 3 difficulty levels: Chump, Punk and Badass. Badass is the hard mode.
 #: engines/hypno/metaengine.cpp:276
 msgid "Badass"
-msgstr ""
+msgstr "Badass"
 
 #. I18N: (Game name: Soldier Boyz) This makes the player restart from the last checkpoint.
 #: engines/hypno/metaengine.cpp:289
@@ -11068,7 +11104,7 @@ msgstr "Reiniciar anima????o"
 
 #: engines/hypno/metaengine.cpp:302
 msgid "Begin new mission"
-msgstr ""
+msgstr "Iniciar nova miss??o"
 
 #: engines/hypno/metaengine.cpp:374
 #, fuzzy
@@ -11270,24 +11306,31 @@ msgid ""
 "- Correct projectile weapon damage (per AD&D 2nd Edition rules)\n"
 "- Elves get +1 to hit with swords and bows (per official game manual)"
 msgstr ""
+"- Eliminar erros de arredondamento no ganho de HP\n"
+"- Corrigir o dano de armas de proj??til (conforme as regras da 2?? Edi????o de "
+"AD&D)\n"
+"- Elfos recebem +1 de acerto com espadas e arcos (conforme o manual oficial "
+"do jogo)"
 
 #: engines/kyra/metaengine.cpp:175
 msgid "Fix Ileria and Beohram NPCs"
-msgstr ""
+msgstr "Corrigir os NPCs Ileria e Beohram"
 
 #: engines/kyra/metaengine.cpp:176
 msgid "Make Ileria a female and Beohram a paladin"
-msgstr ""
+msgstr "Tornar Ileria uma mulher e Beohram um paladino"
 
 #: engines/kyra/metaengine.cpp:187
 msgid "Enhanced thrown weapon reload"
-msgstr ""
+msgstr "Recarga aprimorada de armas de arremesso"
 
 #: engines/kyra/metaengine.cpp:188
 msgid ""
 "Thrown weapons (daggers, darts, spears...) get replaced from belt and "
 "backpack inventory slots, with thrown weapons only"
 msgstr ""
+"Armas de arremesso (adagas, dardos, lan??as...) s??o repostas a partir dos "
+"espa??os de invent??rio do cinto e da mochila, apenas com armas de arremesso"
 
 #: engines/kyra/metaengine.cpp:275
 msgid "Lands of Lore support is not compiled in"
@@ -11558,11 +11601,11 @@ msgstr "Habilita o som do jogo"
 
 #: engines/lab/metaengine.cpp:185
 msgid "Lower the sound volume"
-msgstr ""
+msgstr "Diminuir o volume do som"
 
 #: engines/lab/metaengine.cpp:216
 msgid "Interact with object with chosen action"
-msgstr ""
+msgstr "Interagir com o objeto usando a a????o escolhida"
 
 #: engines/lab/metaengine.cpp:221
 #, fuzzy
@@ -11584,11 +11627,11 @@ msgstr "Abrir Holomapa"
 
 #: engines/lab/metaengine.cpp:239
 msgid "Close door or object"
-msgstr ""
+msgstr "Fechar porta ou objeto"
 
 #: engines/lab/metaengine.cpp:245
 msgid "Look at object close-up"
-msgstr ""
+msgstr "Examinar o objeto de perto"
 
 #: engines/lab/metaengine.cpp:251
 #, fuzzy
@@ -11604,15 +11647,15 @@ msgstr "Ir para pr??ximo objeto"
 
 #: engines/lab/metaengine.cpp:292 engines/lab/metaengine.cpp:352
 msgid "Start dropping virtual bread crumbs"
-msgstr ""
+msgstr "Come??ar a deixar migalhas de p??o virtuais"
 
 #: engines/lab/metaengine.cpp:298 engines/lab/metaengine.cpp:359
 msgid "Follow virtual bread crumbs"
-msgstr ""
+msgstr "Seguir as migalhas de p??o virtuais"
 
 #: engines/lab/metaengine.cpp:304 engines/lab/metaengine.cpp:366
 msgid "Run while following virtual bread crumbs"
-msgstr ""
+msgstr "Correr enquanto segue as migalhas de p??o virtuais"
 
 #: engines/lab/metaengine.cpp:311
 #, fuzzy
@@ -11646,7 +11689,7 @@ msgstr "Invent??rio de itens animado"
 
 #: engines/lab/metaengine.cpp:373
 msgid "Exit map display"
-msgstr ""
+msgstr "Sair da exibi????o do mapa"
 
 #: engines/lab/metaengine.cpp:379
 #, fuzzy
@@ -11771,6 +11814,9 @@ msgid ""
 "original .exe file. Otherwise, it will use lower resolution cursors from the "
 "data files."
 msgstr ""
+"Se selecionado, o jogo usar?? os cursores de mouse do Windows inclu??dos no "
+"arquivo .exe original. Caso contr??rio, usar?? cursores de resolu????o inferior "
+"dos arquivos de dados."
 
 #: engines/made/metaengine.cpp:158
 msgid "Cursor up"
@@ -12184,12 +12230,14 @@ msgstr "Melhor"
 
 #: engines/mohawk/dialogs.cpp:456
 msgid "Fix audio pops/clicks"
-msgstr ""
+msgstr "Corrigir estalos/cliques no ??udio"
 
 #: engines/mohawk/dialogs.cpp:457
 msgid ""
 "Reduces audible pops at the end of some sound effects (Non Myst/Riven only)."
 msgstr ""
+"Reduz os estalos aud??veis no final de alguns efeitos sonoros (apenas para "
+"jogos que n??o sejam Myst/Riven)."
 
 #: engines/mohawk/metaengine.cpp:291
 msgid "Myst ME support not compiled in"
@@ -12636,7 +12684,7 @@ msgstr "Pular cena"
 #. I18N: (Game name: The Neverhood) The game has multiple cutscenes, and this action skips the entire scene.
 #: engines/neverhood/metaengine.cpp:221
 msgid "Skip entire scene (works only in some scenes)"
-msgstr ""
+msgstr "Pular a cena inteira (funciona apenas em algumas cenas)"
 
 #. I18N: (Game name: The Neverhood) This action confirms the selected save or entered new save name in the save file management menus.
 #: engines/neverhood/metaengine.cpp:228
@@ -12807,6 +12855,8 @@ msgid ""
 "Use ScummVM's alternate animation timing instead of the original game's "
 "slower walking and talking speed"
 msgstr ""
+"Usar a temporiza????o de anima????o alternativa do ScummVM em vez da velocidade "
+"mais lenta de caminhada e fala do jogo original"
 
 #: engines/pelrock/metaengine.cpp:57
 #, fuzzy
@@ -12819,6 +12869,8 @@ msgid ""
 "Play the game's intro sequence. Plays automatically the first time the game "
 "is launched."
 msgstr ""
+"Reproduzir a sequ??ncia de introdu????o do jogo. ?? reproduzida automaticamente "
+"na primeira vez que o jogo ?? iniciado."
 
 #: engines/pelrock/metaengine.cpp:68
 #, fuzzy
@@ -12829,6 +12881,8 @@ msgstr "Desabilitar sensores"
 #: engines/pelrock/metaengine.cpp:69
 msgid "Disable original screensaver slider mini-game every 60 seconds."
 msgstr ""
+"Desativar o minijogo deslizante de prote????o de tela original que aparece a "
+"cada 60 segundos."
 
 #: engines/petka/metaengine.cpp:117
 #, fuzzy
@@ -12918,7 +12972,7 @@ msgstr "Pular di??logo"
 #. I18N: (Game Name: Pink Panther) The player normally clicks on a target (character/object) to move toward it and interact (talk,use etc.) with it. This action skips the walking animation and also prevents the interaction, instead instantly placing the player next to the target.
 #: engines/pink/metaengine.cpp:151
 msgid "Skip walk and cancel interaction"
-msgstr ""
+msgstr "Pular a caminhada e cancelar a intera????o"
 
 #. I18N: (Game Name: Pink Panther) This action fully skips the current running sequence (cutscene, dialog, interaction, etc).
 #: engines/pink/metaengine.cpp:158
@@ -12981,15 +13035,16 @@ msgstr "Destacar pr??ximo di??logo"
 
 #: engines/private/metaengine.cpp:51
 msgid "Highlight clickable areas in decision scenes."
-msgstr ""
+msgstr "Destacar ??reas clic??veis nas cenas de decis??o."
 
 #: engines/private/metaengine.cpp:61
 msgid "Increased contrast for small print"
-msgstr ""
+msgstr "Contraste aumentado para textos pequenos"
 
 #: engines/private/metaengine.cpp:62
 msgid "Enhance small printed paper close-ups for improved readability."
 msgstr ""
+"Aprimorar os close-ups de pap??is com texto pequeno para melhor legibilidade."
 
 #: engines/private/metaengine.cpp:147 engines/trecision/metaengine.cpp:137
 msgid "Skip video"
@@ -13003,7 +13058,7 @@ msgstr "Habilita modo de depura????o"
 
 #: engines/qdengine/dialogs.cpp:32
 msgid "Enable this if backend does not support 32bpp and/or to debug graphics"
-msgstr ""
+msgstr "Ative isto se o backend n??o suportar 32bpp e/ou para depurar gr??ficos"
 
 #: engines/queen/metaengine.cpp:41
 msgid "Alternative intro"
@@ -13023,7 +13078,7 @@ msgstr "Utiliza uma fonte personalizada mais f??cil de ler"
 
 #: engines/queen/metaengine.cpp:186
 msgid "Move / Skip / Use default action with inventory item"
-msgstr ""
+msgstr "Mover / Pular / Usar a????o padr??o com item do invent??rio"
 
 #: engines/queen/metaengine.cpp:198 engines/twp/metaengine.cpp:175
 #, fuzzy
@@ -13394,6 +13449,8 @@ msgid ""
 "Detect and attempt to repair overflows in DPCM8 audio, which cause "
 "noticeable pops and crackles."
 msgstr ""
+"Detectar e tentar reparar estouros no ??udio DPCM8, que causam estalos e "
+"crepita????es percept??veis."
 
 #: engines/sci/detection_options.h:271
 msgid "Roland D-110 / D-10 / D-20"
@@ -13737,11 +13794,11 @@ msgstr ""
 
 #: engines/scumm/dialogs.cpp:1213 engines/scumm/metaengine.cpp:793
 msgid "Brighten the graphics to simulate a Macintosh monitor."
-msgstr ""
+msgstr "Clarear os gr??ficos para simular um monitor Macintosh."
 
 #: engines/scumm/dialogs.cpp:1219
 msgid "Simulate Sega colors"
-msgstr ""
+msgstr "Simular as cores do Sega"
 
 #: engines/scumm/dialogs.cpp:1220
 msgid ""
@@ -13749,15 +13806,20 @@ msgid ""
 "actual Sega hardware. These are significantly darker, though it's unclear if "
 "that was intended or not."
 msgstr ""
+"Em vez de usar as cores definidas no jogo, simula as cores usadas no "
+"hardware real do Sega. Elas s??o significativamente mais escuras, embora n??o "
+"esteja claro se isso era intencional ou n??o."
 
 #: engines/scumm/dialogs.cpp:1226
 msgid "Show wait cursor when paused"
-msgstr ""
+msgstr "Mostrar o cursor de espera quando pausado"
 
 #: engines/scumm/dialogs.cpp:1227
 msgid ""
 "When paused, show the animated wait cursor from the original Sega CD version."
 msgstr ""
+"Quando pausado, mostra o cursor de espera animado da vers??o original do Sega "
+"CD."
 
 #: engines/scumm/dialogs.cpp:1364
 msgid "Overture Timing:"
@@ -14402,6 +14464,9 @@ msgid ""
 "This fan-made translation is not supported, because it is known to contain\n"
 "corrupted resources that might make it crash or seriously misbehave."
 msgstr ""
+"Esta tradu????o feita por f??s n??o ?? suportada, pois sabe-se que cont??m\n"
+"recursos corrompidos que podem faz??-la travar ou apresentar mau "
+"funcionamento grave."
 
 #: engines/scumm/metaengine.cpp:404
 msgid ""
@@ -14545,6 +14610,8 @@ msgid ""
 "Disable the scripted part of the demo (dangerous!). This makes it "
 "interactive, but as this was never intended, expect frequent crashes!"
 msgstr ""
+"Desativar a parte programada da demonstra????o (perigoso!). Isso a torna "
+"interativa, mas como isso nunca foi planejado, espere travamentos frequentes!"
 
 #: engines/scumm/metaengine.cpp:848
 #, fuzzy
@@ -14554,7 +14621,7 @@ msgstr "Utilizar ??udio de CD"
 
 #: engines/scumm/metaengine.cpp:849
 msgid "Use the remastered speech and sound effects."
-msgstr ""
+msgstr "Usar a fala e os efeitos sonoros remasterizados."
 
 #: engines/scumm/metaengine.cpp:861
 #, fuzzy
@@ -14577,7 +14644,7 @@ msgstr "Alta resolu????o ativada"
 
 #: engines/scumm/metaengine.cpp:883
 msgid "Run the game in 640x400 high resolution mode instead of 320x200"
-msgstr ""
+msgstr "Executar o jogo no modo de alta resolu????o 640x400 em vez de 320x200"
 
 #: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:919
 #, fuzzy
@@ -14606,6 +14673,8 @@ msgstr "Modo r??pido"
 #: engines/scumm/metaengine.cpp:910
 msgid "Enable original Yoda mode shortcuts, including movie mode and auto play"
 msgstr ""
+"Ativar os atalhos originais do modo Yoda, incluindo o modo filme e a "
+"reprodu????o autom??tica"
 
 #: engines/scumm/metaengine.cpp:1085
 #, fuzzy
@@ -14642,23 +14711,23 @@ msgstr "Trapa??a para vencer a briga de motos"
 
 #: engines/scumm/metaengine.cpp:1166
 msgid "Rebel Assault controls"
-msgstr ""
+msgstr "Controles de Rebel Assault"
 
 #: engines/scumm/metaengine.cpp:1168 engines/scumm/metaengine.cpp:1245
 msgid "Aim up / menu up"
-msgstr ""
+msgstr "Mirar para cima / menu para cima"
 
 #: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1250
 msgid "Aim down / menu down"
-msgstr ""
+msgstr "Mirar para baixo / menu para baixo"
 
 #: engines/scumm/metaengine.cpp:1178 engines/scumm/metaengine.cpp:1255
 msgid "Aim left / menu left"
-msgstr ""
+msgstr "Mirar para a esquerda / menu para a esquerda"
 
 #: engines/scumm/metaengine.cpp:1183 engines/scumm/metaengine.cpp:1260
 msgid "Aim right / menu right"
-msgstr ""
+msgstr "Mirar para a direita / menu para a direita"
 
 #: engines/scumm/metaengine.cpp:1216 engines/scumm/metaengine.cpp:1293
 #, fuzzy
@@ -14680,7 +14749,7 @@ msgstr "Omitir menu principal"
 
 #: engines/scumm/metaengine.cpp:1243
 msgid "Rebel Assault II controls"
-msgstr ""
+msgstr "Controles de Rebel Assault II"
 
 #: engines/scumm/metaengine.cpp:1298
 #, fuzzy
@@ -14770,11 +14839,11 @@ msgstr ""
 
 #: engines/scumm/scumm.cpp:1379
 msgid "The Mac GUI"
-msgstr ""
+msgstr "A interface gr??fica do Mac"
 
 #: engines/scumm/scumm.cpp:1379
 msgid "The music and the Mac GUI"
-msgstr ""
+msgstr "A m??sica e a interface gr??fica do Mac"
 
 #: engines/scumm/scumm.cpp:1408
 #, c-format
@@ -14796,6 +14865,10 @@ msgid ""
 "dialog definitions. Look for a 'The Dig f' folder on your CD. Any one from "
 "its sub-folders should be what you need."
 msgstr ""
+"'%s' parece ser o execut??vel errado de The Dig. Pode ser o inicializador "
+"encontrado na raiz do CD, que n??o cont??m nenhuma das defini????es de menu e "
+"di??logo necess??rias. Procure por uma pasta 'The Dig f' no seu CD. Qualquer "
+"um de suas subpastas deve ser o que voc?? precisa."
 
 #: engines/scumm/scumm.cpp:1584
 msgid ""
@@ -14804,6 +14877,11 @@ msgid ""
 "Since the One-Stop Fun Shop series makes extensive use of TTF fonts,\n"
 "some of the graphics on screen will be missing."
 msgstr ""
+"Parece que a sua vers??o do ScummVM n??o foi compilada com suporte a fontes "
+"TrueType.\n"
+"\n"
+"Como a s??rie One-Stop Fun Shop faz uso extensivo de fontes TTF,\n"
+"alguns dos gr??ficos na tela estar??o faltando."
 
 #: engines/scumm/scumm.cpp:1918
 msgid ""
@@ -15118,7 +15196,7 @@ msgstr "Conjunto de pe??as"
 #. I18N: (Game name: The Lost Files of Sherlock Holmes: The Case of the Rose Tattoo) The game has a journal, this action is used to go back 10 pages in the journal
 #: engines/sherlock/metaengine.cpp:501 engines/sherlock/metaengine.cpp:910
 msgid "Go back 10 pages"
-msgstr ""
+msgstr "Voltar 10 p??ginas"
 
 #. I18N: (Game: The Lost Files of Sherlock Holmes: The Case of the Serrated Scalpel) The game has a journal, this action is used to go forward 10 pages in the journal
 #. I18N: (Game name: The Lost Files of Sherlock Holmes: The Case of the Rose Tattoo) The game has a journal, this action is used to go forward 10 pages in the journal
@@ -15192,7 +15270,7 @@ msgstr "Habilitar trapa??as"
 #. I18N: (Game: The Lost Files of Sherlock Holmes: The Case of the Serrated Scalpel) The game has a fade animation when switching scenes, this action is used to change the fade mode to "Fade by pixel"
 #: engines/sherlock/metaengine.cpp:726
 msgid "Fade by pixel"
-msgstr ""
+msgstr "Desvanecer por pixel"
 
 #. I18N: (Game: The Lost Files of Sherlock Holmes: The Case of the Serrated Scalpel) The game has a fade animation when switching scenes, this action is used to change the fade mode to "Fade directly"
 #: engines/sherlock/metaengine.cpp:732
@@ -15287,11 +15365,11 @@ msgstr "Ajustar ?? janela"
 
 #: engines/sherlock/metaengine.cpp:882 engines/sherlock/metaengine.cpp:949
 msgid "Change selected option to the left"
-msgstr ""
+msgstr "Mudar a op????o selecionada para a esquerda"
 
 #: engines/sherlock/metaengine.cpp:888 engines/sherlock/metaengine.cpp:955
 msgid "Change selected option to the right"
-msgstr ""
+msgstr "Mudar a op????o selecionada para a direita"
 
 #: engines/sherlock/metaengine.cpp:972
 #, fuzzy
@@ -15363,6 +15441,7 @@ msgstr "Fechar caixa de di??logo"
 #: engines/sherlock/metaengine.cpp:1177
 msgid "Exit inventory / Close verb menu / Cancel item use action"
 msgstr ""
+"Sair do invent??rio / Fechar menu de verbos / Cancelar a????o de uso de item"
 
 #: engines/sherlock/metaengine.cpp:1239
 #, fuzzy
@@ -15457,12 +15536,12 @@ msgstr "Salvamento autom??tico sem t??tulo"
 #: engines/sludge/keymapper_tables.h:200 engines/sludge/keymapper_tables.h:435
 #: engines/sludge/keymapper_tables.h:552
 msgid "DEBUG: Show floor"
-msgstr ""
+msgstr "DEPURA????O: Mostrar o ch??o"
 
 #: engines/sludge/keymapper_tables.h:205 engines/sludge/keymapper_tables.h:440
 #: engines/sludge/keymapper_tables.h:547
 msgid "DEBUG: Show boxes"
-msgstr ""
+msgstr "DEPURA????O: Mostrar as caixas"
 
 #: engines/sludge/keymapper_tables.h:237
 #, fuzzy
@@ -15485,11 +15564,11 @@ msgstr "Pular cena"
 
 #: engines/sludge/keymapper_tables.h:328
 msgid "Smell"
-msgstr ""
+msgstr "Cheirar"
 
 #: engines/sludge/keymapper_tables.h:338
 msgid "Consume"
-msgstr ""
+msgstr "Consumir"
 
 #: engines/sludge/keymapper_tables.h:361
 #, fuzzy
@@ -15776,15 +15855,15 @@ msgstr "Abrir gerenciador de Arquivos"
 
 #: engines/supernova/metaengine.cpp:385
 msgid "Phone"
-msgstr ""
+msgstr "Telefone"
 
 #: engines/supernova/metaengine.cpp:391
 msgid "ProText"
-msgstr ""
+msgstr "ProText"
 
 #: engines/supernova/metaengine.cpp:397
 msgid "Calculata"
-msgstr ""
+msgstr "Calculata"
 
 #: engines/sword1/animation.cpp:566 engines/sword2/animation.cpp:459
 msgid ""
@@ -15922,7 +16001,7 @@ msgstr ""
 
 #: engines/sword25/metaengine.cpp:130
 msgid "Reveal all interactive hotspots (hold the key)"
-msgstr ""
+msgstr "Revelar todos os pontos interativos (segure a tecla)"
 
 #: engines/teenagent/metaengine.cpp:201
 #, fuzzy
@@ -16001,7 +16080,7 @@ msgstr "Ir para o fim do menu de salvar/carregar"
 
 #: engines/tinsel/tinsel.cpp:1061
 msgid "Discworld Noir needs ScummVM with TinyGL enabled"
-msgstr ""
+msgstr "Discworld Noir requer o ScummVM com o TinyGL ativado"
 
 #: engines/titanic/metaengine.cpp:173
 #, fuzzy
@@ -16077,7 +16156,7 @@ msgstr "Sala:"
 
 #: engines/titanic/metaengine.cpp:259
 msgid "Real life"
-msgstr ""
+msgstr "Vida real"
 
 #: engines/titanic/metaengine.cpp:278
 #, fuzzy
@@ -16099,19 +16178,19 @@ msgstr "Transi????es:"
 
 #: engines/titanic/metaengine.cpp:307
 msgid "Toggle between Star Map and photo of your home"
-msgstr ""
+msgstr "Alternar entre o Mapa Estelar e a foto da sua casa"
 
 #: engines/titanic/metaengine.cpp:350
 msgid "Stop moving"
-msgstr ""
+msgstr "Parar de se mover"
 
 #: engines/titanic/metaengine.cpp:356
 msgid "Lock coordinate"
-msgstr ""
+msgstr "Travar coordenada"
 
 #: engines/titanic/metaengine.cpp:362
 msgid "Unlock coordinate"
-msgstr ""
+msgstr "Destravar coordenada"
 
 #: engines/titanic/metaengine.cpp:368
 #, fuzzy
@@ -16121,7 +16200,7 @@ msgstr "Sair da conversa"
 
 #: engines/titanic/metaengine.cpp:373
 msgid "View boundaries"
-msgstr ""
+msgstr "Ver os limites"
 
 #: engines/toltecs/metaengine.cpp:216
 #, fuzzy
@@ -16131,7 +16210,7 @@ msgstr "Mover para esquerda"
 
 #: engines/toltecs/metaengine.cpp:222
 msgid "Move / Perform default action"
-msgstr ""
+msgstr "Mover / Executar a????o padr??o"
 
 #: engines/toltecs/metaengine.cpp:258
 #, fuzzy
@@ -16204,7 +16283,7 @@ msgstr "Transi????es de cena quadriculadas"
 
 #: engines/tot/metaengine.cpp:48
 msgid "Disable original transition effects between scenes"
-msgstr ""
+msgstr "Desativar os efeitos de transi????o originais entre cenas"
 
 #: engines/tot/metaengine.cpp:58
 #, fuzzy
@@ -16724,6 +16803,8 @@ msgid ""
 "This game requires OpenGL Framebuffer Objects, which are not supported on "
 "your system"
 msgstr ""
+"Este jogo requer Objetos de Framebuffer do OpenGL, que n??o s??o suportados "
+"pelo seu sistema"
 
 #: engines/ultima/metaengine.cpp:67
 msgid "Enable frame skipping"
@@ -16802,35 +16883,35 @@ msgstr "N??o foi poss??vel encontrar o arquivo de dados ultima.dat correto"
 
 #: engines/ultima/ultima0/metaengine.cpp:49
 msgid "Swing"
-msgstr ""
+msgstr "Balan??ar"
 
 #: engines/ultima/ultima0/metaengine.cpp:50
 msgid "Throw"
-msgstr ""
+msgstr "Arremessar"
 
 #: engines/ultima/ultima0/metaengine.cpp:51
 msgid "Food"
-msgstr ""
+msgstr "Comida"
 
 #: engines/ultima/ultima0/metaengine.cpp:52
 msgid "Rapier"
-msgstr ""
+msgstr "Rapieira"
 
 #: engines/ultima/ultima0/metaengine.cpp:53
 msgid "Axe"
-msgstr ""
+msgstr "Machado"
 
 #: engines/ultima/ultima0/metaengine.cpp:54
 msgid "Shield"
-msgstr ""
+msgstr "Escudo"
 
 #: engines/ultima/ultima0/metaengine.cpp:55
 msgid "Bow & arrow"
-msgstr ""
+msgstr "Arco e flecha"
 
 #: engines/ultima/ultima0/metaengine.cpp:56
 msgid "Magic amulet"
-msgstr ""
+msgstr "Amuleto m??gico"
 
 #: engines/ultima/ultima0/metaengine.cpp:57
 #, fuzzy
@@ -16930,7 +17011,7 @@ msgstr "Teclas Gerais"
 #: engines/ultima/ultima0/metaengine.cpp:101
 #: engines/ultima/ultima0/metaengine.cpp:124
 msgid "Dungeon keys"
-msgstr ""
+msgstr "Chaves da masmorra"
 
 #: engines/ultima/ultima4/metaengine.cpp:43
 #: engines/ultima/ultima4/metaengine.cpp:133
@@ -16950,7 +17031,7 @@ msgstr "Teclado"
 #: engines/ultima/ultima4/metaengine.cpp:52
 #: engines/ultima/ultima8/metaengine.cpp:107
 msgid "Descend"
-msgstr ""
+msgstr "Descer"
 
 #: engines/ultima/ultima4/metaengine.cpp:56
 #, fuzzy
@@ -16960,16 +17041,16 @@ msgstr "Trapa??a de luta"
 
 #: engines/ultima/ultima4/metaengine.cpp:57
 msgid "Hole up & camp"
-msgstr ""
+msgstr "Abrigar-se e acampar"
 
 #. I18N: Jimmy is used to open locked doors
 #: engines/ultima/ultima4/metaengine.cpp:59
 msgid "Jimmy"
-msgstr ""
+msgstr "For??ar fechadura"
 
 #: engines/ultima/ultima4/metaengine.cpp:60
 msgid "Ignite"
-msgstr ""
+msgstr "Acender"
 
 #: engines/ultima/ultima4/metaengine.cpp:61
 #, fuzzy
@@ -16979,7 +17060,7 @@ msgstr "Olhar para"
 
 #: engines/ultima/ultima4/metaengine.cpp:62
 msgid "Mix reagents"
-msgstr ""
+msgstr "Misturar reagentes"
 
 #: engines/ultima/ultima4/metaengine.cpp:63
 #, fuzzy
@@ -17003,7 +17084,7 @@ msgstr "Senha:"
 #. I18N: Peer is used to look through keyholes or other small openings
 #: engines/ultima/ultima4/metaengine.cpp:68
 msgid "Peer"
-msgstr ""
+msgstr "Espiar"
 
 #: engines/ultima/ultima4/metaengine.cpp:69
 #, fuzzy
@@ -17025,11 +17106,11 @@ msgstr "Status:"
 
 #: engines/ultima/ultima4/metaengine.cpp:75
 msgid "Wear armor"
-msgstr ""
+msgstr "Vestir armadura"
 
 #: engines/ultima/ultima4/metaengine.cpp:76
 msgid "Yell"
-msgstr ""
+msgstr "Gritar"
 
 #: engines/ultima/ultima4/metaengine.cpp:82
 #, fuzzy
@@ -17051,7 +17132,7 @@ msgstr "Somente Fala"
 
 #: engines/ultima/ultima4/metaengine.cpp:85
 msgid "Combat speed up"
-msgstr ""
+msgstr "Acelerar combate"
 
 #: engines/ultima/ultima4/metaengine.cpp:86
 #, fuzzy
@@ -17061,11 +17142,11 @@ msgstr "Girar para baixo"
 
 #: engines/ultima/ultima4/metaengine.cpp:87
 msgid "Combat speed normal"
-msgstr ""
+msgstr "Velocidade de combate normal"
 
 #: engines/ultima/ultima4/metaengine.cpp:93
 msgid "Party - None"
-msgstr ""
+msgstr "Grupo - Nenhum"
 
 #: engines/ultima/ultima4/metaengine.cpp:94
 #, fuzzy
@@ -17117,7 +17198,7 @@ msgstr "Transferir Personagem"
 
 #: engines/ultima/ultima4/metaengine.cpp:107
 msgid "Destroy all creatures"
-msgstr ""
+msgstr "Destruir todas as criaturas"
 
 #: engines/ultima/ultima4/metaengine.cpp:108
 #, fuzzy
@@ -17134,7 +17215,7 @@ msgstr "Pr??ximo objeto"
 
 #: engines/ultima/ultima4/metaengine.cpp:110
 msgid "Full equipment"
-msgstr ""
+msgstr "Equipamento completo"
 
 #: engines/ultima/ultima4/metaengine.cpp:111
 #, fuzzy
@@ -17144,15 +17225,15 @@ msgstr "Alternar Modo de Combate"
 
 #: engines/ultima/ultima4/metaengine.cpp:113
 msgid "Help - Teleport to Lord British"
-msgstr ""
+msgstr "Ajuda - Teleportar para Lord British"
 
 #: engines/ultima/ultima4/metaengine.cpp:114
 msgid "Give items"
-msgstr ""
+msgstr "Dar itens"
 
 #: engines/ultima/ultima4/metaengine.cpp:115
 msgid "List karma"
-msgstr ""
+msgstr "Listar karma"
 
 #: engines/ultima/ultima4/metaengine.cpp:116
 #, fuzzy
@@ -17162,11 +17243,11 @@ msgstr "Local seguinte"
 
 #: engines/ultima/ultima4/metaengine.cpp:117
 msgid "Give mixtures"
-msgstr ""
+msgstr "Dar misturas"
 
 #: engines/ultima/ultima4/metaengine.cpp:118
 msgid "Combat encounters"
-msgstr ""
+msgstr "Encontros de combate"
 
 #: engines/ultima/ultima4/metaengine.cpp:119
 #, fuzzy
@@ -17176,15 +17257,15 @@ msgstr "Alternar Modo de Arrastar"
 
 #: engines/ultima/ultima4/metaengine.cpp:120
 msgid "Full party"
-msgstr ""
+msgstr "Grupo completo"
 
 #: engines/ultima/ultima4/metaengine.cpp:121
 msgid "Give reagents"
-msgstr ""
+msgstr "Dar reagentes"
 
 #: engines/ultima/ultima4/metaengine.cpp:122
 msgid "Full stats"
-msgstr ""
+msgstr "Atributos completos"
 
 #: engines/ultima/ultima4/metaengine.cpp:123
 #, fuzzy
@@ -17206,7 +17287,7 @@ msgstr "N??vel de Depura????o:"
 
 #: engines/ultima/ultima4/metaengine.cpp:126
 msgid "Grant virtue"
-msgstr ""
+msgstr "Conceder virtude"
 
 #: engines/ultima/ultima4/metaengine.cpp:127
 #, fuzzy
@@ -17254,23 +17335,23 @@ msgstr "Andar at??"
 
 #: engines/ultima/nuvie/metaengine.cpp:63
 msgid "Walk north-east"
-msgstr ""
+msgstr "Caminhar para nordeste"
 
 #: engines/ultima/nuvie/metaengine.cpp:64
 msgid "Walk south-east"
-msgstr ""
+msgstr "Caminhar para sudeste"
 
 #: engines/ultima/nuvie/metaengine.cpp:65
 msgid "Walk north-west"
-msgstr ""
+msgstr "Caminhar para noroeste"
 
 #: engines/ultima/nuvie/metaengine.cpp:66
 msgid "Walk south-west"
-msgstr ""
+msgstr "Caminhar para sudoeste"
 
 #: engines/ultima/nuvie/metaengine.cpp:75
 msgid "Multi-use"
-msgstr ""
+msgstr "Uso m??ltiplo"
 
 #. I18N: command bar is the bar of commands at the bottom of the screen
 #: engines/ultima/nuvie/metaengine.cpp:77
@@ -17313,7 +17394,7 @@ msgstr "Tecla anti-chefe"
 
 #: engines/ultima/nuvie/metaengine.cpp:85
 msgid "End key"
-msgstr ""
+msgstr "Tecla End"
 
 #: engines/ultima/nuvie/metaengine.cpp:86
 #, fuzzy
@@ -17347,7 +17428,7 @@ msgstr "Salvamento autom??tico sem t??tulo"
 
 #: engines/ultima/nuvie/metaengine.cpp:95
 msgid "Show game menu; Cancel action if in middle of action"
-msgstr ""
+msgstr "Mostrar o menu do jogo; cancelar a a????o se estiver no meio de uma a????o"
 
 #: engines/ultima/nuvie/metaengine.cpp:97
 #, fuzzy
@@ -17382,7 +17463,7 @@ msgstr "Alternar som"
 #. I18N: command bar is the bar of commands at the bottom of the screen
 #: engines/ultima/nuvie/metaengine.cpp:104
 msgid "Toggle original style command bar"
-msgstr ""
+msgstr "Alternar a barra de comandos no estilo original"
 
 #: engines/ultima/nuvie/metaengine.cpp:105
 #, fuzzy
@@ -17435,51 +17516,51 @@ msgstr "Fechar caixa de di??logo"
 
 #: engines/ultima/nuvie/metaengine.cpp:115
 msgid "Enter ALT code (hold)"
-msgstr ""
+msgstr "Inserir c??digo ALT (segurar)"
 
 #: engines/ultima/nuvie/metaengine.cpp:116
 msgid "ALT code 0"
-msgstr ""
+msgstr "C??digo ALT 0"
 
 #: engines/ultima/nuvie/metaengine.cpp:117
 msgid "ALT code 1"
-msgstr ""
+msgstr "C??digo ALT 1"
 
 #: engines/ultima/nuvie/metaengine.cpp:118
 msgid "ALT code 2"
-msgstr ""
+msgstr "C??digo ALT 2"
 
 #: engines/ultima/nuvie/metaengine.cpp:119
 msgid "ALT code 3"
-msgstr ""
+msgstr "C??digo ALT 3"
 
 #: engines/ultima/nuvie/metaengine.cpp:120
 msgid "ALT code 4"
-msgstr ""
+msgstr "C??digo ALT 4"
 
 #: engines/ultima/nuvie/metaengine.cpp:121
 msgid "ALT code 5"
-msgstr ""
+msgstr "C??digo ALT 5"
 
 #: engines/ultima/nuvie/metaengine.cpp:122
 msgid "ALT code 6"
-msgstr ""
+msgstr "C??digo ALT 6"
 
 #: engines/ultima/nuvie/metaengine.cpp:123
 msgid "ALT code 7"
-msgstr ""
+msgstr "C??digo ALT 7"
 
 #: engines/ultima/nuvie/metaengine.cpp:124
 msgid "ALT code 8"
-msgstr ""
+msgstr "C??digo ALT 8"
 
 #: engines/ultima/nuvie/metaengine.cpp:125
 msgid "ALT code 9"
-msgstr ""
+msgstr "C??digo ALT 9"
 
 #: engines/ultima/nuvie/metaengine.cpp:129
 msgid "Open the asset viewer"
-msgstr ""
+msgstr "Abrir o visualizador de recursos"
 
 #. I18N: Eggs are in-game hatchable objects that are normally invisible
 #: engines/ultima/nuvie/metaengine.cpp:131
@@ -17533,7 +17614,7 @@ msgstr "Alternar modo r??pido"
 
 #: engines/ultima/nuvie/metaengine.cpp:140
 msgid "Heal party"
-msgstr ""
+msgstr "Curar o grupo"
 
 #: engines/ultima/nuvie/metaengine.cpp:141
 #, fuzzy
@@ -17549,7 +17630,7 @@ msgstr "Habilitar trapa??as"
 
 #: engines/ultima/nuvie/metaengine.cpp:146
 msgid "Solo mode as Avatar"
-msgstr ""
+msgstr "Modo solo como o Avatar"
 
 #: engines/ultima/nuvie/metaengine.cpp:147
 #, fuzzy
@@ -17583,51 +17664,51 @@ msgstr "Ver Integrante 6"
 
 #: engines/ultima/nuvie/metaengine.cpp:152
 msgid "Solo mode as party member 7"
-msgstr ""
+msgstr "Modo solo como o membro do grupo 7"
 
 #: engines/ultima/nuvie/metaengine.cpp:153
 msgid "Solo mode as party member 8"
-msgstr ""
+msgstr "Modo solo como o membro do grupo 8"
 
 #: engines/ultima/nuvie/metaengine.cpp:154
 msgid "Solo mode as party member 9"
-msgstr ""
+msgstr "Modo solo como o membro do grupo 9"
 
 #: engines/ultima/nuvie/metaengine.cpp:155
 msgid "Show stats for Avatar"
-msgstr ""
+msgstr "Mostrar atributos do Avatar"
 
 #: engines/ultima/nuvie/metaengine.cpp:156
 msgid "Show stats for party member 2"
-msgstr ""
+msgstr "Mostrar atributos do membro do grupo 2"
 
 #: engines/ultima/nuvie/metaengine.cpp:157
 msgid "Show stats for party member 3"
-msgstr ""
+msgstr "Mostrar atributos do membro do grupo 3"
 
 #: engines/ultima/nuvie/metaengine.cpp:158
 msgid "Show stats for party member 4"
-msgstr ""
+msgstr "Mostrar atributos do membro do grupo 4"
 
 #: engines/ultima/nuvie/metaengine.cpp:159
 msgid "Show stats for party member 5"
-msgstr ""
+msgstr "Mostrar atributos do membro do grupo 5"
 
 #: engines/ultima/nuvie/metaengine.cpp:160
 msgid "Show stats for party member 6"
-msgstr ""
+msgstr "Mostrar atributos do membro do grupo 6"
 
 #: engines/ultima/nuvie/metaengine.cpp:161
 msgid "Show stats for party member 7"
-msgstr ""
+msgstr "Mostrar atributos do membro do grupo 7"
 
 #: engines/ultima/nuvie/metaengine.cpp:162
 msgid "Show stats for party member 8"
-msgstr ""
+msgstr "Mostrar atributos do membro do grupo 8"
 
 #: engines/ultima/nuvie/metaengine.cpp:163
 msgid "Show stats for party member 9"
-msgstr ""
+msgstr "Mostrar atributos do membro do grupo 9"
 
 #: engines/ultima/nuvie/metaengine.cpp:164
 #, fuzzy
@@ -17637,80 +17718,80 @@ msgstr "Exibir invent??rio"
 
 #: engines/ultima/nuvie/metaengine.cpp:165
 msgid "Show inventory for party member 2"
-msgstr ""
+msgstr "Mostrar invent??rio do membro do grupo 2"
 
 #: engines/ultima/nuvie/metaengine.cpp:166
 msgid "Show inventory for party member 3"
-msgstr ""
+msgstr "Mostrar invent??rio do membro do grupo 3"
 
 #: engines/ultima/nuvie/metaengine.cpp:167
 msgid "Show inventory for party member 4"
-msgstr ""
+msgstr "Mostrar invent??rio do membro do grupo 4"
 
 #: engines/ultima/nuvie/metaengine.cpp:168
 msgid "Show inventory for party member 5"
-msgstr ""
+msgstr "Mostrar invent??rio do membro do grupo 5"
 
 #: engines/ultima/nuvie/metaengine.cpp:169
 msgid "Show inventory for party member 6"
-msgstr ""
+msgstr "Mostrar invent??rio do membro do grupo 6"
 
 #: engines/ultima/nuvie/metaengine.cpp:170
 msgid "Show inventory for party member 7"
-msgstr ""
+msgstr "Mostrar invent??rio do membro do grupo 7"
 
 #: engines/ultima/nuvie/metaengine.cpp:171
 msgid "Show inventory for party member 8"
-msgstr ""
+msgstr "Mostrar invent??rio do membro do grupo 8"
 
 #: engines/ultima/nuvie/metaengine.cpp:172
 msgid "Show inventory for party member 9"
-msgstr ""
+msgstr "Mostrar invent??rio do membro do grupo 9"
 
 #. I18N: gump is Graphical User Menu Pop-up
 #: engines/ultima/nuvie/metaengine.cpp:174
 msgid "Show doll gump for Avatar"
-msgstr ""
+msgstr "Mostrar o boneco (gump) do Avatar"
 
 #. I18N: gump is Graphical User Menu Pop-up
 #: engines/ultima/nuvie/metaengine.cpp:176
 msgid "Show doll gump for party member 2"
-msgstr ""
+msgstr "Mostrar o boneco (gump) do membro do grupo 2"
 
 #. I18N: gump is Graphical User Menu Pop-up
 #: engines/ultima/nuvie/metaengine.cpp:178
 msgid "Show doll gump for party member 3"
-msgstr ""
+msgstr "Mostrar o boneco (gump) do membro do grupo 3"
 
 #. I18N: gump is Graphical User Menu Pop-up
 #: engines/ultima/nuvie/metaengine.cpp:180
 msgid "Show doll gump for party member 4"
-msgstr ""
+msgstr "Mostrar o boneco (gump) do membro do grupo 4"
 
 #. I18N: gump is Graphical User Menu Pop-up
 #: engines/ultima/nuvie/metaengine.cpp:182
 msgid "Show doll gump for party member 5"
-msgstr ""
+msgstr "Mostrar o boneco (gump) do membro do grupo 5"
 
 #. I18N: gump is Graphical User Menu Pop-up
 #: engines/ultima/nuvie/metaengine.cpp:184
 msgid "Show doll gump for party member 6"
-msgstr ""
+msgstr "Mostrar o boneco (gump) do membro do grupo 6"
 
 #. I18N: gump is Graphical User Menu Pop-up
 #: engines/ultima/nuvie/metaengine.cpp:186
 msgid "Show doll gump for party member 7"
-msgstr ""
+msgstr "Mostrar o boneco (gump) do membro do grupo 7"
 
 #. I18N: gump is Graphical User Menu Pop-up
 #: engines/ultima/nuvie/metaengine.cpp:188
 msgid "Show doll gump for party member 8"
-msgstr ""
+msgstr "Mostrar o boneco (gump) do membro do grupo 8"
 
 #. I18N: gump is Graphical User Menu Pop-up
 #: engines/ultima/nuvie/metaengine.cpp:190
 msgid "Show doll gump for party member 9"
-msgstr ""
+msgstr "Mostrar o boneco (gump) do membro do grupo 9"
 
 #: engines/ultima/nuvie/metaengine.cpp:196
 #: engines/ultima/nuvie/metaengine.cpp:201
@@ -17737,7 +17818,7 @@ msgstr "Transferir Personagem"
 
 #: engines/ultima/ultima8/metaengine.cpp:46
 msgid "Draw weapon / Combat"
-msgstr ""
+msgstr "Sacar arma / Combate"
 
 #: engines/ultima/ultima8/metaengine.cpp:51
 #, fuzzy
@@ -17753,7 +17834,7 @@ msgstr "Destacar pr??ximo di??logo"
 
 #: engines/ultima/ultima8/metaengine.cpp:63
 msgid "Use bedroll"
-msgstr ""
+msgstr "Usar saco de dormir"
 
 #: engines/ultima/ultima8/metaengine.cpp:64
 #, fuzzy
@@ -17775,7 +17856,7 @@ msgstr "Abrir mapa"
 
 #: engines/ultima/ultima8/metaengine.cpp:67
 msgid "Use recall"
-msgstr ""
+msgstr "Usar Recall"
 
 #: engines/ultima/ultima8/metaengine.cpp:68
 #, fuzzy
@@ -17786,15 +17867,15 @@ msgstr "Abrir invent??rio"
 #. I18N: gump is Graphical User Menu Pop-up
 #: engines/ultima/ultima8/metaengine.cpp:70
 msgid "Close all displays"
-msgstr ""
+msgstr "Fechar todas as exibi????es"
 
 #: engines/ultima/ultima8/metaengine.cpp:71
 msgid "Jump (fake both-button-click)"
-msgstr ""
+msgstr "Saltar (simular clique de ambos os bot??es)"
 
 #: engines/ultima/ultima8/metaengine.cpp:72
 msgid "Careful step"
-msgstr ""
+msgstr "Passo cuidadoso"
 
 #: engines/ultima/ultima8/metaengine.cpp:78
 #, fuzzy
@@ -17816,7 +17897,7 @@ msgstr "Invent??rio"
 
 #: engines/ultima/ultima8/metaengine.cpp:81
 msgid "Use medikit"
-msgstr ""
+msgstr "Usar kit m??dico"
 
 #: engines/ultima/ultima8/metaengine.cpp:82
 #, fuzzy
@@ -17826,7 +17907,7 @@ msgstr "Invent??rio"
 
 #: engines/ultima/ultima8/metaengine.cpp:83
 msgid "Detonate bomb"
-msgstr ""
+msgstr "Detonar bomba"
 
 #: engines/ultima/ultima8/metaengine.cpp:85
 #, fuzzy
@@ -17848,7 +17929,7 @@ msgstr "Pr??xima a????o"
 
 #: engines/ultima/ultima8/metaengine.cpp:88
 msgid "Grab items"
-msgstr ""
+msgstr "Pegar itens"
 
 #: engines/ultima/ultima8/metaengine.cpp:90
 #, fuzzy
@@ -17858,7 +17939,7 @@ msgstr "C??mera acompanha Jogador"
 
 #: engines/ultima/ultima8/metaengine.cpp:91
 msgid "Jump / Roll / Crouch"
-msgstr ""
+msgstr "Saltar / Rolar / Agachar"
 
 #: engines/ultima/ultima8/metaengine.cpp:92
 #, fuzzy
@@ -17892,7 +17973,7 @@ msgstr "Alternar Rel??gio"
 
 #: engines/ultima/ultima8/metaengine.cpp:100
 msgid "Side step / Advance / Retreat"
-msgstr ""
+msgstr "Passo lateral / Avan??ar / Recuar"
 
 #: engines/ultima/ultima8/metaengine.cpp:105
 #, fuzzy
@@ -17902,7 +17983,7 @@ msgstr "Alternar Rel??gio"
 
 #: engines/ultima/ultima8/metaengine.cpp:106
 msgid "Ascend"
-msgstr ""
+msgstr "Subir"
 
 #: engines/ultima/ultima8/metaengine.cpp:114
 #, fuzzy
@@ -17918,11 +17999,11 @@ msgstr "Exibir cita????es"
 
 #: engines/ultima/ultima8/metaengine.cpp:116
 msgid "Decrement map sort order"
-msgstr ""
+msgstr "Diminuir a ordem de classifica????o do mapa"
 
 #: engines/ultima/ultima8/metaengine.cpp:117
 msgid "Increment map sort order"
-msgstr ""
+msgstr "Aumentar a ordem de classifica????o do mapa"
 
 #: engines/ultima/ultima8/metaengine.cpp:118
 #, fuzzy
@@ -17932,11 +18013,11 @@ msgstr "Alternar estilo do painel"
 
 #: engines/ultima/ultima8/metaengine.cpp:119
 msgid "Advance frame"
-msgstr ""
+msgstr "Avan??ar quadro"
 
 #: engines/ultima/ultima8/metaengine.cpp:120
 msgid "Toggle Avatar in stasis"
-msgstr ""
+msgstr "Alternar o Avatar em estase"
 
 #: engines/ultima/ultima8/metaengine.cpp:121
 #, fuzzy
@@ -18129,11 +18210,11 @@ msgstr "Pular cena"
 
 #: engines/voyeur/metaengine.cpp:211
 msgid "View evidence"
-msgstr ""
+msgstr "Ver evid??ncias"
 
 #: engines/voyeur/metaengine.cpp:217
 msgid "Exit / Put away evidence"
-msgstr ""
+msgstr "Sair / Guardar evid??ncias"
 
 #: engines/voyeur/metaengine.cpp:223
 #, fuzzy
@@ -18175,15 +18256,15 @@ msgstr "Utilizar fala em Ingl??s"
 
 #: engines/wintermute/metaengine.cpp:95
 msgid "Use Italian dubbing instead of the default English one"
-msgstr ""
+msgstr "Usar a dublagem italiana em vez da inglesa padr??o"
 
 #: engines/wintermute/metaengine.cpp:106
 msgid "Run SD version (1280x800)"
-msgstr ""
+msgstr "Executar a vers??o SD (1280x800)"
 
 #: engines/wintermute/metaengine.cpp:107
 msgid "Run the legacy SD version, which is better for small screens"
-msgstr ""
+msgstr "Executar a vers??o SD legada, que ?? melhor para telas pequenas"
 
 #: engines/wintermute/metaengine.cpp:148
 msgid "FoxTail support is not compiled in"
@@ -18721,6 +18802,8 @@ msgid ""
 "Rearrange placement of menus & subtitles so as to make better use of modern "
 "wide aspect ratio displays"
 msgstr ""
+"Reorganizar a disposi????o de menus e legendas para aproveitar melhor as telas "
+"modernas de propor????o ampla"
 
 #: engines/zvision/metaengine.cpp:114
 #, fuzzy
@@ -18790,6 +18873,11 @@ msgid ""
 "solved.  These puzzles may alternatively be solved using subtitles, if "
 "supported. Continue launching game?"
 msgstr ""
+"A reprodu????o de MIDI n??o est?? dispon??vel, ou est?? configurada "
+"incorretamente. Zork Nemesis cont??m v??rios quebra-cabe??as musicais que "
+"exigem ??udio MIDI para serem resolvidos.  Esses quebra-cabe??as podem, "
+"alternativamente, ser resolvidos usando legendas, se houver suporte. "
+"Continuar iniciando o jogo?"
 
 #, fuzzy
 #~| msgid "Move Back"



From noreply at scummvm.org  Wed Jun 17 23:04:50 2026
From: noreply at scummvm.org (ScummVM-Translations)
Date: Wed, 17 Jun 2026 23:04:50 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 febedfe4e42e2c3053f0df752b8ef1ca9b001a0b
Message-ID: <20260617230450.A62847005BC@ron.scummvm.net>

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:
901643311e I18N: Update translation (Russian)
febedfe4e4 I18N: Update translation (Ukrainian)


Commit: 901643311e17854430b1ebb5bdc0fb8ed4b6899c
    https://github.com/scummvm/scummvm/commit/901643311e17854430b1ebb5bdc0fb8ed4b6899c
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2026-06-17T23:04:41Z

Commit Message:
I18N: Update translation (Russian)

Currently translated at 100.0% (3221 of 3221 strings)

Changed paths:
    po/ru_RU.po


diff --git a/po/ru_RU.po b/po/ru_RU.po
index c2c17fd5588..65d5f8b0211 100644
--- a/po/ru_RU.po
+++ b/po/ru_RU.po
@@ -7,7 +7,7 @@ msgstr ""
 "Project-Id-Version: ScummVM 1.8.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
 "POT-Creation-Date: 2026-06-14 22:15+0000\n"
-"PO-Revision-Date: 2026-05-06 08:54+0000\n"
+"PO-Revision-Date: 2026-06-17 23:04+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Russian <https://translations.scummvm.org/projects/scummvm/"
 "scummvm/ru/>\n"
@@ -2351,28 +2351,20 @@ msgid "Disable fixed font scaling"
 msgstr "?????????????????? ?????????????????????????????? ?????????????????? ??????????????"
 
 #: gui/options.cpp:2667
-#, fuzzy
-#| msgid ""
-#| "Do not upscale fixed size fonts in the GUI. This reduces artefacts on low "
-#| "resolution screens"
 msgid ""
 "Do not upscale fixed size fonts in the GUI. This reduces artefacts on low "
 "resolution screens."
 msgstr ""
 "???? ?????????????????????? ?????????????? ?????????????? ???????????????????????????? ?????????????? ?? GUI. ?????? ?????????????????? "
-"???????????????????? ???????????????????? ???? ?????????????? ?? ???????????? ??????????????????????"
+"???????????????????? ???????????????????? ???? ?????????????? ?? ???????????? ??????????????????????."
 
 #: gui/options.cpp:2673
-#, fuzzy
-#| msgid "Enable smooth scrolling when walking"
 msgid "Enable kinetic scrolling in lists"
-msgstr "???????????????? ?????????????? ?????????????????? ???? ?????????? ????????????"
+msgstr "???????????????? ?????????????? ?????????????????? ?? ??????????????"
 
 #: gui/options.cpp:2674
-#, fuzzy
-#| msgid "Enable smooth scrolling when walking"
 msgid "Enable smooth, momentum-based scrolling in list widgets."
-msgstr "???????????????? ?????????????? ?????????????????? ???? ?????????? ????????????"
+msgstr "???????????????? ?????????????? ?????????????????? ?? ???????????????????????????? ?????????????? ?? ???????????????? ??????????????."
 
 #: gui/options.cpp:2680
 msgid "GUI language:"
@@ -3841,6 +3833,10 @@ msgid ""
 "testing mode.\n"
 "If you encounter any bugs or oddities, please report them to our bugtracker."
 msgstr ""
+"????????????????: ????????, ?????????????? ???? ?????????????????????? ??????????????????, ???????????? ?????????????? ???????????????? "
+"?????????? ?????????????????? ?? ?????????????????? ?? ???????????? ????????????????????????.\n"
+"???????? ???? ???????????????????? ??????????-???????? ???????????? ?????? ????????????????????, ????????????????????, ???????????????? ?? "
+"?????? ?? ???????? ?????????????? ???????????????????????? ????????????."
 
 #: engines/engine.cpp:861
 #, c-format
@@ -9085,10 +9081,8 @@ msgstr "????????-????????"
 
 #. I18N: Toggles the red/blue stereoscopic 3D effect (anaglyph glasses).
 #: engines/freescape/movement.cpp:119
-#, fuzzy
-#| msgid "Toggle resizable window"
 msgid "Toggle red/blue 3D"
-msgstr "?????????????????????? ?????????????????? ???????????????? ????????"
+msgstr "?????????????????????? ??????????????/?????????? 3D"
 
 #: engines/freescape/games/castle/castle.cpp:463
 msgid "Select prince"
@@ -10244,60 +10238,44 @@ msgid "Secondary shoot"
 msgstr "?????????????????? ??????????????"
 
 #: engines/hypno/metaengine.cpp:149
-#, fuzzy
-#| msgid "Map hide toolbar action"
 msgid "Rapid fire / secondary action"
-msgstr "?????????????????? ???????????????? \"???????????????? ???????????? ????????????????????????\""
+msgstr "???????????????????????????????? / ?????????????????? ????????????????"
 
 #: engines/hypno/metaengine.cpp:155
-#, fuzzy
-#| msgid "Slide left"
 msgid "Aim left"
-msgstr "?????????????????? ??????????"
+msgstr "???????????????? ??????????"
 
 #: engines/hypno/metaengine.cpp:160
-#, fuzzy
-#| msgid "Scroll down"
 msgid "Aim down"
-msgstr "???????????????????? ????????"
+msgstr "???????????????? ????????"
 
 #: engines/hypno/metaengine.cpp:165
-#, fuzzy
-#| msgid "Up right"
 msgid "Aim right"
-msgstr "??????????-????????????"
+msgstr "???????????????? ????????????"
 
 #: engines/hypno/metaengine.cpp:170
 msgid "Aim up"
-msgstr ""
+msgstr "???????????????? ??????????"
 
 #: engines/hypno/metaengine.cpp:175 engines/scumm/metaengine.cpp:1202
 #: engines/scumm/metaengine.cpp:1279
-#, fuzzy
-#| msgid "Slide left"
 msgid "Stick left"
-msgstr "?????????????????? ??????????"
+msgstr "???????? ??????????"
 
 #: engines/hypno/metaengine.cpp:182 engines/scumm/metaengine.cpp:1195
 #: engines/scumm/metaengine.cpp:1272
-#, fuzzy
-#| msgid "Scroll down"
 msgid "Stick down"
-msgstr "???????????????????? ????????"
+msgstr "???????? ????????"
 
 #: engines/hypno/metaengine.cpp:189 engines/scumm/metaengine.cpp:1209
 #: engines/scumm/metaengine.cpp:1286
-#, fuzzy
-#| msgid "Slide right"
 msgid "Stick right"
-msgstr "?????????????????? ????????????"
+msgstr "???????? ????????????"
 
 #: engines/hypno/metaengine.cpp:196 engines/scumm/metaengine.cpp:1188
 #: engines/scumm/metaengine.cpp:1265
-#, fuzzy
-#| msgid "Pick up"
 msgid "Stick up"
-msgstr "??????????????"
+msgstr "???????? ??????????"
 
 #: engines/hypno/metaengine.cpp:203 engines/lab/metaengine.cpp:209
 #: engines/ngi/metaengine.cpp:168 engines/sherlock/metaengine.cpp:872
@@ -10374,28 +10352,20 @@ msgid "Show credits"
 msgstr "???????????????? ??????????????"
 
 #: engines/hypno/metaengine.cpp:417
-#, fuzzy
-#| msgid "Move left"
 msgid "Menu left"
-msgstr "???????? ??????????"
+msgstr "???????? ??????????"
 
 #: engines/hypno/metaengine.cpp:425
-#, fuzzy
-#| msgid "Move down"
 msgid "Menu down"
-msgstr "???????? ????????"
+msgstr "???????? ????????"
 
 #: engines/hypno/metaengine.cpp:433
-#, fuzzy
-#| msgid "Move right"
 msgid "Menu right"
-msgstr "???????? ????????????"
+msgstr "???????? ????????????"
 
 #: engines/hypno/metaengine.cpp:441
-#, fuzzy
-#| msgid "Menu"
 msgid "Menu up"
-msgstr "????????"
+msgstr "???????? ??????????"
 
 #. I18N: Action in In Cold Blood
 #: engines/icb/icb.cpp:107
@@ -12093,11 +12063,13 @@ msgstr "???????????????? ??????????????, ???? ?????????????? ?????????? ????????
 
 #: engines/private/metaengine.cpp:61
 msgid "Increased contrast for small print"
-msgstr ""
+msgstr "???????????????????? ?????????????????????????? ?????? ?????????????? ????????????"
 
 #: engines/private/metaengine.cpp:62
 msgid "Enhance small printed paper close-ups for improved readability."
 msgstr ""
+"???????????????? ???????????????? ?????????????? ???????????? ?????????????????? ???????????????? ?????????????? ?????? ?????????????????? "
+"????????????????????."
 
 #: engines/private/metaengine.cpp:147 engines/trecision/metaengine.cpp:137
 msgid "Skip video"
@@ -13646,43 +13618,34 @@ msgid "Enable ambience sounds."
 msgstr "???????????????? ?????????? ????????."
 
 #: engines/scumm/metaengine.cpp:882
-#, fuzzy
-#| msgctxt "Options menu"
-#| msgid "High resolution on"
 msgid "High resolution mode"
-msgstr "?????????????? ???????????????????? ????????????????"
+msgstr "?????????? ???????????????? ????????????????????"
 
 #: engines/scumm/metaengine.cpp:883
 msgid "Run the game in 640x400 high resolution mode instead of 320x200"
-msgstr ""
+msgstr "?????????????????? ???????? ?? ???????????? ???????????????? ???????????????????? 640x400 ???????????? 320x200"
 
 #: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:919
-#, fuzzy
-#| msgid "All levels will be available to play."
 msgid "All levels will be available without requiring passwords"
-msgstr "?????? ???????????? ?????????? ???????????????? ?????? ????????."
+msgstr "?????? ???????????? ?????????? ???????????????? ?????? ??????????????"
 
 #: engines/scumm/metaengine.cpp:900 engines/scumm/metaengine.cpp:927
-#, fuzzy
-#| msgid "No date saved"
 msgid "No damage"
-msgstr "???????? ???? ????????????????"
+msgstr "?????? ??????????????????????"
 
 #: engines/scumm/metaengine.cpp:901 engines/scumm/metaengine.cpp:928
-#, fuzzy
-#| msgid "Disable save menu"
 msgid "Disable player damage"
-msgstr "?????????????????? ???????? ????????????????????"
+msgstr "?????????????????? ????????, ?????????????????? ????????????"
 
 #: engines/scumm/metaengine.cpp:909
-#, fuzzy
-#| msgid "Fast mode"
 msgid "Yoda mode"
-msgstr "?????????????? ??????????"
+msgstr "?????????? ????????"
 
 #: engines/scumm/metaengine.cpp:910
 msgid "Enable original Yoda mode shortcuts, including movie mode and auto play"
 msgstr ""
+"???????????????? ???????????????????????? ???????????????????? ?????????????? ???????????? ????????, ?????????????? ?????????? "
+"?????????????????? ?????????????? ?? ???????????????????????????? ??????????????????????????????"
 
 #: engines/scumm/metaengine.cpp:1085
 msgid "Down left"
@@ -13711,63 +13674,51 @@ msgstr "?????? ?????? ???????????????? ???????? ???? ????????????"
 
 #: engines/scumm/metaengine.cpp:1166
 msgid "Rebel Assault controls"
-msgstr ""
+msgstr "???????????????????? Rebel Assault"
 
 #: engines/scumm/metaengine.cpp:1168 engines/scumm/metaengine.cpp:1245
 msgid "Aim up / menu up"
-msgstr ""
+msgstr "???????????????? ?????????? / ???????? ??????????"
 
 #: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1250
 msgid "Aim down / menu down"
-msgstr ""
+msgstr "???????????????? ???????? / ???????? ????????"
 
 #: engines/scumm/metaengine.cpp:1178 engines/scumm/metaengine.cpp:1255
 msgid "Aim left / menu left"
-msgstr ""
+msgstr "???????????????? ?????????? / ???????? ??????????"
 
 #: engines/scumm/metaengine.cpp:1183 engines/scumm/metaengine.cpp:1260
 msgid "Aim right / menu right"
-msgstr ""
+msgstr "???????????????? ???????????? / ???????? ????????????"
 
 #: engines/scumm/metaengine.cpp:1216 engines/scumm/metaengine.cpp:1293
-#, fuzzy
-#| msgid "Move / Select"
 msgid "Fire / select"
-msgstr "???????? / ??????????????"
+msgstr "???????????????? / ??????????????"
 
 #: engines/scumm/metaengine.cpp:1222
-#, fuzzy
-#| msgid "Skip main menu"
 msgid "Walk / menu back"
-msgstr "???????????????????? ?????????????? ????????"
+msgstr "???????? / ???????? ??????????"
 
 #: engines/scumm/metaengine.cpp:1227
-#, fuzzy
-#| msgid "Skip main menu"
 msgid "Skip / menu back"
-msgstr "???????????????????? ?????????????? ????????"
+msgstr "???????????????????? / ???????? ??????????"
 
 #: engines/scumm/metaengine.cpp:1243
 msgid "Rebel Assault II controls"
-msgstr ""
+msgstr "???????????????????? Rebel Assault II"
 
 #: engines/scumm/metaengine.cpp:1298
-#, fuzzy
-#| msgid "Move back"
 msgid "Cover / back"
-msgstr "???????? ??????????"
+msgstr "???????????????? / ??????????"
 
 #: engines/scumm/metaengine.cpp:1304
-#, fuzzy
-#| msgid "Skip walk"
 msgid "Skip / back"
-msgstr "???????????????????? ????????????"
+msgstr "???????????????????? / ??????????"
 
 #: engines/scumm/metaengine.cpp:1311
-#, fuzzy
-#| msgid "Skip main menu"
 msgid "Skip / menu"
-msgstr "???????????????????? ?????????????? ????????"
+msgstr "???????????????????? / ????????"
 
 #: engines/scumm/saveload.cpp:1990
 #, c-format


Commit: febedfe4e42e2c3053f0df752b8ef1ca9b001a0b
    https://github.com/scummvm/scummvm/commit/febedfe4e42e2c3053f0df752b8ef1ca9b001a0b
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2026-06-17T23:04:43Z

Commit Message:
I18N: Update translation (Ukrainian)

Currently translated at 100.0% (3221 of 3221 strings)

Changed paths:
    po/uk_UA.po


diff --git a/po/uk_UA.po b/po/uk_UA.po
index d93053b011b..0ff1d058119 100644
--- a/po/uk_UA.po
+++ b/po/uk_UA.po
@@ -9,7 +9,7 @@ msgstr ""
 "Project-Id-Version: ScummVM 1.9.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
 "POT-Creation-Date: 2026-06-14 22:15+0000\n"
-"PO-Revision-Date: 2026-05-06 08:54+0000\n"
+"PO-Revision-Date: 2026-06-17 23:04+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Ukrainian <https://translations.scummvm.org/projects/scummvm/"
 "scummvm/uk/>\n"
@@ -2352,28 +2352,20 @@ msgid "Disable fixed font scaling"
 msgstr "?????????????????? ?????????????????????????? ???????????? ??????????????"
 
 #: gui/options.cpp:2667
-#, fuzzy
-#| msgid ""
-#| "Do not upscale fixed size fonts in the GUI. This reduces artefacts on low "
-#| "resolution screens"
 msgid ""
 "Do not upscale fixed size fonts in the GUI. This reduces artefacts on low "
 "resolution screens."
 msgstr ""
-"???? ???????????????????????? ???????????? ?????????????????????? ???????????? ?? GUI. ???? ?????????????? ?????????????????? "
-"?????????????????? ???? ?????????????? ?? ?????????????? ??????????????????????????"
+"???? ???????????????????????? ???????????? ???????????????????? ???????????? ?? GUI. ???? ?????????????? ?????????????????? "
+"?????????????????? ???? ?????????????? ?? ?????????????? ??????????????????????????."
 
 #: gui/options.cpp:2673
-#, fuzzy
-#| msgid "Enable smooth scrolling when walking"
 msgid "Enable kinetic scrolling in lists"
-msgstr "?????????????????? ???????????? ?????????????????? ?????? ?????? ??????????????"
+msgstr "?????????????????? ???????????? ?????????????????? ?? ??????????????"
 
 #: gui/options.cpp:2674
-#, fuzzy
-#| msgid "Enable smooth scrolling when walking"
 msgid "Enable smooth, momentum-based scrolling in list widgets."
-msgstr "?????????????????? ???????????? ?????????????????? ?????? ?????? ??????????????"
+msgstr "?????????????????? ????????????, ?????????????? ?????????????????? ?? ???????????????? ??????????????."
 
 #: gui/options.cpp:2680
 msgid "GUI language:"
@@ -3837,6 +3829,10 @@ msgid ""
 "testing mode.\n"
 "If you encounter any bugs or oddities, please report them to our bugtracker."
 msgstr ""
+"????????????????????????: ??????, ?????? ???? ???????????????????? ??????????????????, ?????????????????? ?????????????????????????? ???? "
+"?????????????????? ?? ?????????????????? ????????????.\n"
+"???????? ???? ???????????????????? ?? ????????-?????????? ?????????????????? ?????? ?????????????? ??????????????????, ???????? ??????????, "
+"?????????????????? ?????? ?????? ?? ?????? ??????????????????."
 
 #: engines/engine.cpp:861
 #, c-format
@@ -8980,7 +8976,7 @@ msgstr "?????????????????????? ???????? Y ????????"
 
 #: engines/freescape/metaengine.cpp:108
 msgid "Use alternative camera controls"
-msgstr "?????????????????????????????? ?????????????????????????? ???????????????????? ??????????????"
+msgstr "?????????????????????????????? ?????????????????????????? ?????????????????? ??????????????"
 
 #: engines/freescape/metaengine.cpp:118
 msgid "Authentic graphics"
@@ -9064,10 +9060,8 @@ msgstr "???????? ????????"
 
 #. I18N: Toggles the red/blue stereoscopic 3D effect (anaglyph glasses).
 #: engines/freescape/movement.cpp:119
-#, fuzzy
-#| msgid "Toggle resizable window"
 msgid "Toggle red/blue 3D"
-msgstr "???????????????????? ?????????? ???????????????? ??????????"
+msgstr "???????????????????? ????????????????/?????????? 3D"
 
 #: engines/freescape/games/castle/castle.cpp:463
 msgid "Select prince"
@@ -9968,7 +9962,7 @@ msgstr "?????????? ?????? Speedrun"
 
 #: engines/groovie/metaengine.cpp:114
 msgid "Affects the controls for fast forwarding the game"
-msgstr "???????????? ???????????????????? ?????? ???????????????? ?????????????????????? ???? ??????"
+msgstr "???????????? ?????????????????? ?????? ???????????????? ?????????????????????? ???? ??????"
 
 #: engines/groovie/metaengine.cpp:150
 msgid "GroovieV2 support is not compiled in"
@@ -10223,60 +10217,44 @@ msgid "Secondary shoot"
 msgstr "?????????????????????? ??????????????"
 
 #: engines/hypno/metaengine.cpp:149
-#, fuzzy
-#| msgid "Map hide toolbar action"
 msgid "Rapid fire / secondary action"
-msgstr "???????????????????????????? ?????? '?????????????? ???????????? ??????????.'"
+msgstr "???????????? ???????????????????? / ???????????????? ??????"
 
 #: engines/hypno/metaengine.cpp:155
-#, fuzzy
-#| msgid "Slide left"
 msgid "Aim left"
-msgstr "???????? ????????????"
+msgstr "???????????????? ??????????????"
 
 #: engines/hypno/metaengine.cpp:160
-#, fuzzy
-#| msgid "Scroll down"
 msgid "Aim down"
-msgstr "?????????????????????? ????????????"
+msgstr "???????????????? ????????????"
 
 #: engines/hypno/metaengine.cpp:165
-#, fuzzy
-#| msgid "Up right"
 msgid "Aim right"
-msgstr "????????????-??????????????"
+msgstr "???????????????? ????????????????"
 
 #: engines/hypno/metaengine.cpp:170
 msgid "Aim up"
-msgstr ""
+msgstr "???????????????? ????????????"
 
 #: engines/hypno/metaengine.cpp:175 engines/scumm/metaengine.cpp:1202
 #: engines/scumm/metaengine.cpp:1279
-#, fuzzy
-#| msgid "Slide left"
 msgid "Stick left"
-msgstr "???????? ????????????"
+msgstr "???????? ????????????"
 
 #: engines/hypno/metaengine.cpp:182 engines/scumm/metaengine.cpp:1195
 #: engines/scumm/metaengine.cpp:1272
-#, fuzzy
-#| msgid "Scroll down"
 msgid "Stick down"
-msgstr "?????????????????????? ????????????"
+msgstr "???????? ????????????"
 
 #: engines/hypno/metaengine.cpp:189 engines/scumm/metaengine.cpp:1209
 #: engines/scumm/metaengine.cpp:1286
-#, fuzzy
-#| msgid "Slide right"
 msgid "Stick right"
-msgstr "???????? ??????????????"
+msgstr "???????? ??????????????"
 
 #: engines/hypno/metaengine.cpp:196 engines/scumm/metaengine.cpp:1188
 #: engines/scumm/metaengine.cpp:1265
-#, fuzzy
-#| msgid "Pick up"
 msgid "Stick up"
-msgstr "??????????????????"
+msgstr "???????? ????????????"
 
 #: engines/hypno/metaengine.cpp:203 engines/lab/metaengine.cpp:209
 #: engines/ngi/metaengine.cpp:168 engines/sherlock/metaengine.cpp:872
@@ -10353,28 +10331,20 @@ msgid "Show credits"
 msgstr "???????????????? ??????????????"
 
 #: engines/hypno/metaengine.cpp:417
-#, fuzzy
-#| msgid "Move left"
 msgid "Menu left"
-msgstr "???????????????? ????????????"
+msgstr "???????? ????????????"
 
 #: engines/hypno/metaengine.cpp:425
-#, fuzzy
-#| msgid "Move down"
 msgid "Menu down"
-msgstr "???????????????? ????????"
+msgstr "???????? ????????????"
 
 #: engines/hypno/metaengine.cpp:433
-#, fuzzy
-#| msgid "Move right"
 msgid "Menu right"
-msgstr "???????????????? ??????????????"
+msgstr "???????? ??????????????"
 
 #: engines/hypno/metaengine.cpp:441
-#, fuzzy
-#| msgid "Menu"
 msgid "Menu up"
-msgstr "????????"
+msgstr "???????? ????????????"
 
 #. I18N: Action in In Cold Blood
 #: engines/icb/icb.cpp:107
@@ -12063,11 +12033,11 @@ msgstr "???????????????????? ?????????????????????? ?????????????? ?? ??????????
 
 #: engines/private/metaengine.cpp:61
 msgid "Increased contrast for small print"
-msgstr ""
+msgstr "?????????????????? ?????????????????????????? ?????? ???????????????? ????????????"
 
 #: engines/private/metaengine.cpp:62
 msgid "Enhance small printed paper close-ups for improved readability."
-msgstr ""
+msgstr "?????????????????? ?????????????????????????? ?????????????? ???????????????????????? ???????????????????? ?????????????? ????????????."
 
 #: engines/private/metaengine.cpp:147 engines/trecision/metaengine.cpp:137
 msgid "Skip video"
@@ -13605,43 +13575,35 @@ msgid "Enable ambience sounds."
 msgstr "?????????????????? ???????????? ??????????."
 
 #: engines/scumm/metaengine.cpp:882
-#, fuzzy
-#| msgctxt "Options menu"
-#| msgid "High resolution on"
 msgid "High resolution mode"
-msgstr "???????????? ???????????????????????? ??????????????????"
+msgstr "?????????? ???????????????? ????????????????????????"
 
 #: engines/scumm/metaengine.cpp:883
 msgid "Run the game in 640x400 high resolution mode instead of 320x200"
 msgstr ""
+"?????????????????? ?????? ?? ???????????? ?????????????? ???????????????????? ?????????????????? 640x400 ?????????????? 320x200"
 
 #: engines/scumm/metaengine.cpp:892 engines/scumm/metaengine.cpp:919
-#, fuzzy
-#| msgid "All levels will be available to play."
 msgid "All levels will be available without requiring passwords"
-msgstr "?????? ?????????? ???????????? ???????????????? ?????? ??????."
+msgstr "?????? ?????????? ???????????? ???????????????? ?????? ???????????? ??????????????"
 
 #: engines/scumm/metaengine.cpp:900 engines/scumm/metaengine.cpp:927
-#, fuzzy
-#| msgid "No date saved"
 msgid "No damage"
-msgstr "???????? ???? ????????????????"
+msgstr "?????? ????????????????????"
 
 #: engines/scumm/metaengine.cpp:901 engines/scumm/metaengine.cpp:928
-#, fuzzy
-#| msgid "Disable save menu"
 msgid "Disable player damage"
-msgstr "???????????????????? ???????? ????????????????????"
+msgstr "???????????????? ?????????????????????? ????????????"
 
 #: engines/scumm/metaengine.cpp:909
-#, fuzzy
-#| msgid "Fast mode"
 msgid "Yoda mode"
-msgstr "???????????? ??????????"
+msgstr "?????????? ????????"
 
 #: engines/scumm/metaengine.cpp:910
 msgid "Enable original Yoda mode shortcuts, including movie mode and auto play"
 msgstr ""
+"?????????????????? ?????????????????????? ???????????????????? ???????????? ?? ???????????? ????????, ?????????????? ?????????? ???????????? "
+"???? ?????????????????????? ??????????????????????"
 
 #: engines/scumm/metaengine.cpp:1085
 msgid "Down left"
@@ -13670,63 +13632,51 @@ msgstr "?????? ???????????????????? ?????? ???? ??????????????"
 
 #: engines/scumm/metaengine.cpp:1166
 msgid "Rebel Assault controls"
-msgstr ""
+msgstr "?????????????????? Rebel Assault"
 
 #: engines/scumm/metaengine.cpp:1168 engines/scumm/metaengine.cpp:1245
 msgid "Aim up / menu up"
-msgstr ""
+msgstr "?????????????????????? ?????????? / ???????? ??????????"
 
 #: engines/scumm/metaengine.cpp:1173 engines/scumm/metaengine.cpp:1250
 msgid "Aim down / menu down"
-msgstr ""
+msgstr "?????????????????????? ???????????? / ???????? ????????????"
 
 #: engines/scumm/metaengine.cpp:1178 engines/scumm/metaengine.cpp:1255
 msgid "Aim left / menu left"
-msgstr ""
+msgstr "?????????????????????? ???????????? / ???????? ????????????"
 
 #: engines/scumm/metaengine.cpp:1183 engines/scumm/metaengine.cpp:1260
 msgid "Aim right / menu right"
-msgstr ""
+msgstr "?????????????????????? ?????????????? / ???????? ??????????????"
 
 #: engines/scumm/metaengine.cpp:1216 engines/scumm/metaengine.cpp:1293
-#, fuzzy
-#| msgid "Move / Select"
 msgid "Fire / select"
-msgstr "???????????????? / ??????????????"
+msgstr "???????????????? / ??????????????"
 
 #: engines/scumm/metaengine.cpp:1222
-#, fuzzy
-#| msgid "Skip main menu"
 msgid "Walk / menu back"
-msgstr "???????????????????? ?????????????? ????????"
+msgstr "???????????? / ?????????????????????? ?? ????????"
 
 #: engines/scumm/metaengine.cpp:1227
-#, fuzzy
-#| msgid "Skip main menu"
 msgid "Skip / menu back"
-msgstr "???????????????????? ?????????????? ????????"
+msgstr "???????????????????? / ???????? ??????????"
 
 #: engines/scumm/metaengine.cpp:1243
 msgid "Rebel Assault II controls"
-msgstr ""
+msgstr "?????????????????? Rebel Assault II"
 
 #: engines/scumm/metaengine.cpp:1298
-#, fuzzy
-#| msgid "Move back"
 msgid "Cover / back"
-msgstr "???????????????? ??????????"
+msgstr "???????????????? / ??????????"
 
 #: engines/scumm/metaengine.cpp:1304
-#, fuzzy
-#| msgid "Skip walk"
 msgid "Skip / back"
-msgstr "???????????????????? ??????????????"
+msgstr "???????????????????? / ??????????"
 
 #: engines/scumm/metaengine.cpp:1311
-#, fuzzy
-#| msgid "Skip main menu"
 msgid "Skip / menu"
-msgstr "???????????????????? ?????????????? ????????"
+msgstr "???????????????????? / ????????"
 
 #: engines/scumm/saveload.cpp:1990
 #, c-format



From noreply at scummvm.org  Wed Jun 17 23:07:19 2026
From: noreply at scummvm.org (sev-)
Date: Wed, 17 Jun 2026 23:07:19 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 238a7dc64dd3fb069687baddbac0dd22a95f3361
Message-ID: <20260617230719.743007005BC@ron.scummvm.net>

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

Summary:
238a7dc64d COLONY: Added missing POTFILES


Commit: 238a7dc64dd3fb069687baddbac0dd22a95f3361
    https://github.com/scummvm/scummvm/commit/238a7dc64dd3fb069687baddbac0dd22a95f3361
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2026-06-18T01:06:45+02:00

Commit Message:
COLONY: Added missing POTFILES

Changed paths:
    engines/colony/POTFILES


diff --git a/engines/colony/POTFILES b/engines/colony/POTFILES
index f3a17c30b5e..43009791e53 100644
--- a/engines/colony/POTFILES
+++ b/engines/colony/POTFILES
@@ -1,2 +1,3 @@
+engines/colony/intro.cpp
 engines/colony/metaengine.cpp
 engines/colony/savegame.cpp



From noreply at scummvm.org  Wed Jun 17 23:07:25 2026
From: noreply at scummvm.org (ScummVM-Translations)
Date: Wed, 17 Jun 2026 23:07:25 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 1de3cc76b2fbffaf4ec8e9dad7ecbd674aa69bff
Message-ID: <20260617230725.644227005BC@ron.scummvm.net>

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

Summary:
1de3cc76b2 I18N: Update translations templates


Commit: 1de3cc76b2fbffaf4ec8e9dad7ecbd674aa69bff
    https://github.com/scummvm/scummvm/commit/1de3cc76b2fbffaf4ec8e9dad7ecbd674aa69bff
Author: Weblate (noreply at weblate.org)
Date: 2026-06-17T23:07:21Z

Commit Message:
I18N: Update translations templates

Changed paths:
    po/scummvm.pot


diff --git a/po/scummvm.pot b/po/scummvm.pot
index a8a5ad82c88..f188b29b92a 100644
--- a/po/scummvm.pot
+++ b/po/scummvm.pot
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2026.2.1git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-14 22:15+0000\n"
+"POT-Creation-Date: 2026-06-17 23:07+0000\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL at ADDRESS>\n"
 "Language-Team: LANGUAGE <LL at li.org>\n"
@@ -3164,7 +3164,8 @@ msgstr ""
 #: backends/events/default/default-events.cpp:217
 #: backends/events/default/default-events.cpp:358
 #: engines/adl/metaengine.cpp:352 engines/buried/metaengine.cpp:154
-#: engines/chamber/metaengine.cpp:84 engines/drascula/metaengine.cpp:304
+#: engines/chamber/metaengine.cpp:84 engines/colony/intro.cpp:156
+#: engines/colony/intro.cpp:1243 engines/drascula/metaengine.cpp:304
 #: engines/hdb/metaengine.cpp:275 engines/hypno/metaengine.cpp:308
 #: engines/lab/metaengine.cpp:172 engines/mohawk/myst_stacks/menu.cpp:317
 #: engines/mohawk/riven_stacks/aspit.cpp:454
@@ -7367,6 +7368,18 @@ msgid ""
 "beginning using new savegames."
 msgstr ""
 
+#: engines/colony/intro.cpp:154 engines/colony/intro.cpp:1244
+msgid "New Game"
+msgstr ""
+
+#: engines/colony/intro.cpp:155 engines/colony/intro.cpp:1242
+msgid "Load Game"
+msgstr ""
+
+#: engines/colony/intro.cpp:1219 engines/colony/intro.cpp:1244
+msgid "You have been terminated."
+msgstr ""
+
 #: engines/colony/metaengine.cpp:45 engines/myst3/metaengine.cpp:40
 msgid "Widescreen mod"
 msgstr ""



From noreply at scummvm.org  Wed Jun 17 23:07:57 2026
From: noreply at scummvm.org (ScummVM-Translations)
Date: Wed, 17 Jun 2026 23:07:57 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 92168d315a0847f52ef10b1184950b2bad720795
Message-ID: <20260617230757.E7DA97005BC@ron.scummvm.net>

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

Summary:
92168d315a I18N: Update translation files


Commit: 92168d315a0847f52ef10b1184950b2bad720795
    https://github.com/scummvm/scummvm/commit/92168d315a0847f52ef10b1184950b2bad720795
Author: Weblate (noreply at weblate.org)
Date: 2026-06-17T23:07:38Z

Commit Message:
I18N: Update translation files

Updated by "Update PO files to match POT (msgmerge)" hook in Weblate.

Translation: ScummVM/scummvm
Translate-URL: https://translations.scummvm.org/projects/scummvm/scummvm/

Changed paths:
    po/ar.po
    po/be-tarask.po
    po/be_BY.po
    po/ca_ES.po
    po/cs_CZ.po
    po/da.po
    po/de_DE.po
    po/el.po
    po/es_ES.po
    po/eu.po
    po/fi_FI.po
    po/fr_FR.po
    po/gl_ES.po
    po/he.po
    po/hi.po
    po/hu_HU.po
    po/it_IT.po
    po/ja.po
    po/ka.po
    po/ko.po
    po/nb_NO.po
    po/nl_NL.po
    po/nn_NO.po
    po/pl_PL.po
    po/pt_BR.po
    po/pt_PT.po
    po/ro.po
    po/ru_RU.po
    po/sv_SE.po
    po/tr.po
    po/uk_UA.po
    po/zh.po
    po/zh_Hans.po
    po/zh_Hant.po


diff --git a/po/ar.po b/po/ar.po
index 67e31679f61..6333bc3653b 100644
--- a/po/ar.po
+++ b/po/ar.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-14 22:15+0000\n"
+"POT-Creation-Date: 2026-06-17 23:07+0000\n"
 "PO-Revision-Date: 2026-02-24 22:26+0000\n"
 "Last-Translator: Mohamed Shaaban <mohmedbashamedo at hotmail.com>\n"
 "Language-Team: Arabic <https://translations.scummvm.org/projects/scummvm/"
@@ -3399,7 +3399,8 @@ msgstr ""
 #: backends/events/default/default-events.cpp:217
 #: backends/events/default/default-events.cpp:358
 #: engines/adl/metaengine.cpp:352 engines/buried/metaengine.cpp:154
-#: engines/chamber/metaengine.cpp:84 engines/drascula/metaengine.cpp:304
+#: engines/chamber/metaengine.cpp:84 engines/colony/intro.cpp:156
+#: engines/colony/intro.cpp:1243 engines/drascula/metaengine.cpp:304
 #: engines/hdb/metaengine.cpp:275 engines/hypno/metaengine.cpp:308
 #: engines/lab/metaengine.cpp:172 engines/mohawk/myst_stacks/menu.cpp:317
 #: engines/mohawk/riven_stacks/aspit.cpp:454
@@ -8127,6 +8128,20 @@ msgstr ""
 "??????????: ???????????? ???????? ?????????? ???????? ???????? ???????????????? ?????????????? ???????????? ????????????. ?????? ?????????? "
 "??????????????. ???????? ?????????????? ???? ?????? ?????????? ???????????? ???? ?????? ?????????????? ?????????? ?????? ??????????."
 
+#: engines/colony/intro.cpp:154 engines/colony/intro.cpp:1244
+#, fuzzy
+#| msgid "New game"
+msgid "New Game"
+msgstr "???????? ??????????"
+
+#: engines/colony/intro.cpp:155 engines/colony/intro.cpp:1242
+msgid "Load Game"
+msgstr "?????????? ????????"
+
+#: engines/colony/intro.cpp:1219 engines/colony/intro.cpp:1244
+msgid "You have been terminated."
+msgstr ""
+
 #: engines/colony/metaengine.cpp:45 engines/myst3/metaengine.cpp:40
 msgid "Widescreen mod"
 msgstr "?????????? ???????????? ??????????????"
@@ -18867,9 +18882,6 @@ msgstr ""
 #~ msgid "Save Game"
 #~ msgstr "?????? ????????????"
 
-#~ msgid "Load Game"
-#~ msgstr "?????????? ????????"
-
 #~ msgid "Quit Game"
 #~ msgstr "???? ???????????? ????????????"
 
diff --git a/po/be-tarask.po b/po/be-tarask.po
index 47c2b64ac86..4244a4191fd 100644
--- a/po/be-tarask.po
+++ b/po/be-tarask.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-14 22:15+0000\n"
+"POT-Creation-Date: 2026-06-17 23:07+0000\n"
 "PO-Revision-Date: 2023-02-26 15:01+0000\n"
 "Last-Translator: Kastu?? K. <oakum2334 at gmail.com>\n"
 "Language-Team: Belarusian (Tara??kievica) <https://translations.scummvm.org/"
@@ -3366,7 +3366,8 @@ msgstr ""
 #: backends/events/default/default-events.cpp:217
 #: backends/events/default/default-events.cpp:358
 #: engines/adl/metaengine.cpp:352 engines/buried/metaengine.cpp:154
-#: engines/chamber/metaengine.cpp:84 engines/drascula/metaengine.cpp:304
+#: engines/chamber/metaengine.cpp:84 engines/colony/intro.cpp:156
+#: engines/colony/intro.cpp:1243 engines/drascula/metaengine.cpp:304
 #: engines/hdb/metaengine.cpp:275 engines/hypno/metaengine.cpp:308
 #: engines/lab/metaengine.cpp:172 engines/mohawk/myst_stacks/menu.cpp:317
 #: engines/mohawk/riven_stacks/aspit.cpp:454
@@ -8074,6 +8075,22 @@ msgstr ""
 "????????????. ???????????? ?????????? ????????????????. ???????? ??????????, ???????????????????? ???????????????? ?? Operation "
 "Stealth ?????????????????? ?? ???????????? ????????????????????????."
 
+#: engines/colony/intro.cpp:154 engines/colony/intro.cpp:1244
+#, fuzzy
+#| msgid "New game"
+msgid "New Game"
+msgstr "?????????? ????????????"
+
+#: engines/colony/intro.cpp:155 engines/colony/intro.cpp:1242
+msgid "Load Game"
+msgstr "???????????????????? ????????????"
+
+#: engines/colony/intro.cpp:1219 engines/colony/intro.cpp:1244
+#, fuzzy
+#| msgid "You have stored "
+msgid "You have been terminated."
+msgstr "???? ???????????? "
+
 #: engines/colony/metaengine.cpp:45 engines/myst3/metaengine.cpp:40
 msgid "Widescreen mod"
 msgstr "?????? ???? ???????????? ??????????"
@@ -18775,9 +18792,6 @@ msgstr ""
 #~ msgid "Save Game"
 #~ msgstr "???????????????? ????????????"
 
-#~ msgid "Load Game"
-#~ msgstr "???????????????????? ????????????"
-
 #~ msgid "Quit Game"
 #~ msgstr "?????????????? ?? ????????????"
 
@@ -19417,9 +19431,6 @@ msgstr ""
 #~ msgid "The game is now over.\n"
 #~ msgstr "???????????? ??????????????????.\n"
 
-#~ msgid "You have stored "
-#~ msgstr "???? ???????????? "
-
 #~ msgid "I've stored "
 #~ msgstr "?? ?????????? "
 
diff --git a/po/be_BY.po b/po/be_BY.po
index 3981fbed0b9..23b4c3f6b1a 100644
--- a/po/be_BY.po
+++ b/po/be_BY.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-14 22:15+0000\n"
+"POT-Creation-Date: 2026-06-17 23:07+0000\n"
 "PO-Revision-Date: 2021-03-21 08:24+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Belarusian <https://translations.scummvm.org/projects/scummvm/"
@@ -3384,7 +3384,8 @@ msgstr ""
 #: backends/events/default/default-events.cpp:217
 #: backends/events/default/default-events.cpp:358
 #: engines/adl/metaengine.cpp:352 engines/buried/metaengine.cpp:154
-#: engines/chamber/metaengine.cpp:84 engines/drascula/metaengine.cpp:304
+#: engines/chamber/metaengine.cpp:84 engines/colony/intro.cpp:156
+#: engines/colony/intro.cpp:1243 engines/drascula/metaengine.cpp:304
 #: engines/hdb/metaengine.cpp:275 engines/hypno/metaengine.cpp:308
 #: engines/lab/metaengine.cpp:172 engines/mohawk/myst_stacks/menu.cpp:317
 #: engines/mohawk/riven_stacks/aspit.cpp:454
@@ -8038,6 +8039,21 @@ msgstr ""
 "????????????. ???????????? ???? ?????????? ?????????? ??????????????????. ???????? ??????????, ?????????????? ???????????? "
 "Operation Stealth ?????????????????? ?? ???????????? ???????????????????? ????????????????."
 
+#: engines/colony/intro.cpp:154 engines/colony/intro.cpp:1244
+#, fuzzy
+#| msgid "New game"
+msgid "New Game"
+msgstr "?????????? ????????????"
+
+#: engines/colony/intro.cpp:155 engines/colony/intro.cpp:1242
+msgid "Load Game"
+msgstr "?????????????????? ????????????"
+
+#: engines/colony/intro.cpp:1219 engines/colony/intro.cpp:1244
+#, fuzzy
+msgid "You have been terminated."
+msgstr "????????????????"
+
 #: engines/colony/metaengine.cpp:45 engines/myst3/metaengine.cpp:40
 #, fuzzy
 msgid "Widescreen mod"
@@ -18479,9 +18495,6 @@ msgstr ""
 #~ msgid "Save Game"
 #~ msgstr "???????????????? ????????????"
 
-#~ msgid "Load Game"
-#~ msgstr "?????????????????? ????????????"
-
 #~ msgid "Quit Game"
 #~ msgstr "???????????? ?? ????????????"
 
@@ -18989,10 +19002,6 @@ msgstr ""
 #~ msgid "The game is now over.\n"
 #~ msgstr "???????????? ???? ???????? ??????????????????"
 
-#, fuzzy
-#~ msgid "You have stored "
-#~ msgstr "????????????????"
-
 #, fuzzy
 #~ msgid "I've stored "
 #~ msgstr "????????????????"
diff --git a/po/ca_ES.po b/po/ca_ES.po
index d204c12b654..25eee049032 100644
--- a/po/ca_ES.po
+++ b/po/ca_ES.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.6.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-14 22:15+0000\n"
+"POT-Creation-Date: 2026-06-17 23:07+0000\n"
 "PO-Revision-Date: 2026-05-26 04:22+0000\n"
 "Last-Translator: Xaviu <xaviu at xaviu.com>\n"
 "Language-Team: Catalan <https://translations.scummvm.org/projects/scummvm/"
@@ -3304,7 +3304,8 @@ msgstr "Motor de jocs d'aventura gr??fica"
 #: backends/events/default/default-events.cpp:217
 #: backends/events/default/default-events.cpp:358
 #: engines/adl/metaengine.cpp:352 engines/buried/metaengine.cpp:154
-#: engines/chamber/metaengine.cpp:84 engines/drascula/metaengine.cpp:304
+#: engines/chamber/metaengine.cpp:84 engines/colony/intro.cpp:156
+#: engines/colony/intro.cpp:1243 engines/drascula/metaengine.cpp:304
 #: engines/hdb/metaengine.cpp:275 engines/hypno/metaengine.cpp:308
 #: engines/lab/metaengine.cpp:172 engines/mohawk/myst_stacks/menu.cpp:317
 #: engines/mohawk/riven_stacks/aspit.cpp:454
@@ -7741,6 +7742,22 @@ msgstr ""
 "temporal incorrecte. Les coses es trencaran. Si us plau, considereu comen??ar "
 "l'Operation Stealth des del comen??ament utilitzant noves partides desades."
 
+#: engines/colony/intro.cpp:154 engines/colony/intro.cpp:1244
+#, fuzzy
+#| msgid "New game"
+msgid "New Game"
+msgstr "Nova partida"
+
+#: engines/colony/intro.cpp:155 engines/colony/intro.cpp:1242
+msgid "Load Game"
+msgstr "Carrega la partida"
+
+#: engines/colony/intro.cpp:1219 engines/colony/intro.cpp:1244
+#, fuzzy
+#| msgid "You have stored "
+msgid "You have been terminated."
+msgstr "Heu emmagatzemat "
+
 #: engines/colony/metaengine.cpp:45 engines/myst3/metaengine.cpp:40
 msgid "Widescreen mod"
 msgstr "Mod de pantalla panor??mica"
@@ -16939,9 +16956,6 @@ msgstr ""
 #~ msgid "Save Game"
 #~ msgstr "Desa la partida"
 
-#~ msgid "Load Game"
-#~ msgstr "Carrega la partida"
-
 #~ msgid "Quit Game"
 #~ msgstr "Surt del joc"
 
@@ -17394,9 +17408,6 @@ msgstr ""
 #~ msgid "The game is now over.\n"
 #~ msgstr "El joc s'ha acabat.\n"
 
-#~ msgid "You have stored "
-#~ msgstr "Heu emmagatzemat "
-
 #~ msgid "I've stored "
 #~ msgstr "He emmagatzemat "
 
diff --git a/po/cs_CZ.po b/po/cs_CZ.po
index 70b72b04b7f..5179056ce17 100644
--- a/po/cs_CZ.po
+++ b/po/cs_CZ.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.7.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-14 22:15+0000\n"
+"POT-Creation-Date: 2026-06-17 23:07+0000\n"
 "PO-Revision-Date: 2026-02-09 20:22+0000\n"
 "Last-Translator: AsciiWolf <mail at asciiwolf.com>\n"
 "Language-Team: Czech <https://translations.scummvm.org/projects/scummvm/"
@@ -3365,7 +3365,8 @@ msgstr "Grafick?? engine pro hry ????nru adventur"
 #: backends/events/default/default-events.cpp:217
 #: backends/events/default/default-events.cpp:358
 #: engines/adl/metaengine.cpp:352 engines/buried/metaengine.cpp:154
-#: engines/chamber/metaengine.cpp:84 engines/drascula/metaengine.cpp:304
+#: engines/chamber/metaengine.cpp:84 engines/colony/intro.cpp:156
+#: engines/colony/intro.cpp:1243 engines/drascula/metaengine.cpp:304
 #: engines/hdb/metaengine.cpp:275 engines/hypno/metaengine.cpp:308
 #: engines/lab/metaengine.cpp:172 engines/mohawk/myst_stacks/menu.cpp:317
 #: engines/mohawk/riven_stacks/aspit.cpp:454
@@ -7988,6 +7989,20 @@ msgid ""
 "beginning using new savegames."
 msgstr ""
 
+#: engines/colony/intro.cpp:154 engines/colony/intro.cpp:1244
+#, fuzzy
+msgid "New Game"
+msgstr "Nov?? ulo??en?? pozice"
+
+#: engines/colony/intro.cpp:155 engines/colony/intro.cpp:1242
+msgid "Load Game"
+msgstr "Nahr??t hru"
+
+#: engines/colony/intro.cpp:1219 engines/colony/intro.cpp:1244
+#, fuzzy
+msgid "You have been terminated."
+msgstr "Obnovit"
+
 #: engines/colony/metaengine.cpp:45 engines/myst3/metaengine.cpp:40
 #, fuzzy
 msgid "Widescreen mod"
@@ -18163,9 +18178,6 @@ msgstr ""
 #~ msgid "Save Game"
 #~ msgstr "Ulo??it hru"
 
-#~ msgid "Load Game"
-#~ msgstr "Nahr??t hru"
-
 #, fuzzy
 #~ msgid "Quit Game"
 #~ msgstr " (Hra)"
@@ -18651,10 +18663,6 @@ msgstr ""
 #~ msgid "The game is now over.\n"
 #~ msgstr "Hra NEBYLA na??tena"
 
-#, fuzzy
-#~ msgid "You have stored "
-#~ msgstr "Obnovit"
-
 #, fuzzy
 #~ msgid "I've stored "
 #~ msgstr "Obnovit"
diff --git a/po/da.po b/po/da.po
index 7aa0f4c7827..c4361aeee3b 100644
--- a/po/da.po
+++ b/po/da.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-14 22:15+0000\n"
+"POT-Creation-Date: 2026-06-17 23:07+0000\n"
 "PO-Revision-Date: 2023-09-07 17:04+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Danish <https://translations.scummvm.org/projects/scummvm/"
@@ -3382,7 +3382,8 @@ msgstr ""
 #: backends/events/default/default-events.cpp:217
 #: backends/events/default/default-events.cpp:358
 #: engines/adl/metaengine.cpp:352 engines/buried/metaengine.cpp:154
-#: engines/chamber/metaengine.cpp:84 engines/drascula/metaengine.cpp:304
+#: engines/chamber/metaengine.cpp:84 engines/colony/intro.cpp:156
+#: engines/colony/intro.cpp:1243 engines/drascula/metaengine.cpp:304
 #: engines/hdb/metaengine.cpp:275 engines/hypno/metaengine.cpp:308
 #: engines/lab/metaengine.cpp:172 engines/mohawk/myst_stacks/menu.cpp:317
 #: engines/mohawk/riven_stacks/aspit.cpp:454
@@ -8015,6 +8016,23 @@ msgid ""
 "beginning using new savegames."
 msgstr ""
 
+#: engines/colony/intro.cpp:154 engines/colony/intro.cpp:1244
+#, fuzzy
+#| msgid "New game"
+msgid "New Game"
+msgstr "Nyt spil"
+
+#: engines/colony/intro.cpp:155 engines/colony/intro.cpp:1242
+#, fuzzy
+msgid "Load Game"
+msgstr "Indl??s spil"
+
+#: engines/colony/intro.cpp:1219 engines/colony/intro.cpp:1244
+#, fuzzy
+#| msgid "You have stored "
+msgid "You have been terminated."
+msgstr "Du har gemt "
+
 #: engines/colony/metaengine.cpp:45 engines/myst3/metaengine.cpp:40
 #, fuzzy
 msgid "Widescreen mod"
@@ -18299,10 +18317,6 @@ msgstr ""
 #~ msgid "Save Game"
 #~ msgstr "Gemt spil:"
 
-#, fuzzy
-#~ msgid "Load Game"
-#~ msgstr "Indl??s spil"
-
 #, fuzzy
 #~ msgid "Quit Game"
 #~ msgstr " (Spil)"
@@ -18835,9 +18849,6 @@ msgstr ""
 #~ msgid "The game is now over.\n"
 #~ msgstr "Spillet er slut.\n"
 
-#~ msgid "You have stored "
-#~ msgstr "Du har gemt "
-
 #~ msgid "I've stored "
 #~ msgstr "Jeg har gemt "
 
diff --git a/po/de_DE.po b/po/de_DE.po
index a0701f68951..a865230efec 100644
--- a/po/de_DE.po
+++ b/po/de_DE.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.10.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-14 22:15+0000\n"
+"POT-Creation-Date: 2026-06-17 23:07+0000\n"
 "PO-Revision-Date: 2026-05-05 16:31+0000\n"
 "Last-Translator: Lothar Serra Mari <lothar.serramari at scummvm.org>\n"
 "Language-Team: German <https://translations.scummvm.org/projects/scummvm/"
@@ -3436,7 +3436,8 @@ msgstr "Spiele-Engine f??r Grafik-Adventures"
 #: backends/events/default/default-events.cpp:217
 #: backends/events/default/default-events.cpp:358
 #: engines/adl/metaengine.cpp:352 engines/buried/metaengine.cpp:154
-#: engines/chamber/metaengine.cpp:84 engines/drascula/metaengine.cpp:304
+#: engines/chamber/metaengine.cpp:84 engines/colony/intro.cpp:156
+#: engines/colony/intro.cpp:1243 engines/drascula/metaengine.cpp:304
 #: engines/hdb/metaengine.cpp:275 engines/hypno/metaengine.cpp:308
 #: engines/lab/metaengine.cpp:172 engines/mohawk/myst_stacks/menu.cpp:317
 #: engines/mohawk/riven_stacks/aspit.cpp:454
@@ -8226,6 +8227,22 @@ msgstr ""
 "Format. Einige Dinge werden kaputt sein. Bitte denke dar??ber nach, Operation "
 "Stealth neu zu starten und neue Spielst??nde anzulegen."
 
+#: engines/colony/intro.cpp:154 engines/colony/intro.cpp:1244
+#, fuzzy
+#| msgid "New game"
+msgid "New Game"
+msgstr "Neues Spiel"
+
+#: engines/colony/intro.cpp:155 engines/colony/intro.cpp:1242
+msgid "Load Game"
+msgstr "Spiel laden"
+
+#: engines/colony/intro.cpp:1219 engines/colony/intro.cpp:1244
+#, fuzzy
+#| msgid "You have stored "
+msgid "You have been terminated."
+msgstr "Du hast aufgenommen "
+
 #: engines/colony/metaengine.cpp:45 engines/myst3/metaengine.cpp:40
 msgid "Widescreen mod"
 msgstr "Breitbild-Modus"
@@ -17880,9 +17897,6 @@ msgstr ""
 #~ msgid "Save Game"
 #~ msgstr "Spiel speichern"
 
-#~ msgid "Load Game"
-#~ msgstr "Spiel laden"
-
 #~ msgid "Quit Game"
 #~ msgstr "Spiel beenden"
 
@@ -18649,9 +18663,6 @@ msgstr ""
 #~ msgid "The game is now over.\n"
 #~ msgstr "Das Spiel ist nun vorbei.\n"
 
-#~ msgid "You have stored "
-#~ msgstr "Du hast aufgenommen "
-
 #~ msgid "I've stored "
 #~ msgstr "Ich habe aufgenommen "
 
diff --git a/po/el.po b/po/el.po
index c96fb83d6fd..a14a55dcb82 100644
--- a/po/el.po
+++ b/po/el.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.10.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-14 22:15+0000\n"
+"POT-Creation-Date: 2026-06-17 23:07+0000\n"
 "PO-Revision-Date: 2026-04-23 00:22+0000\n"
 "Last-Translator: Antoniou Athanasios <a.antoniou79 at gmail.com>\n"
 "Language-Team: Greek <https://translations.scummvm.org/projects/scummvm/"
@@ -3491,7 +3491,8 @@ msgstr "???????????? ???????????????????? ??????????????????????"
 #: backends/events/default/default-events.cpp:217
 #: backends/events/default/default-events.cpp:358
 #: engines/adl/metaengine.cpp:352 engines/buried/metaengine.cpp:154
-#: engines/chamber/metaengine.cpp:84 engines/drascula/metaengine.cpp:304
+#: engines/chamber/metaengine.cpp:84 engines/colony/intro.cpp:156
+#: engines/colony/intro.cpp:1243 engines/drascula/metaengine.cpp:304
 #: engines/hdb/metaengine.cpp:275 engines/hypno/metaengine.cpp:308
 #: engines/lab/metaengine.cpp:172 engines/mohawk/myst_stacks/menu.cpp:317
 #: engines/mohawk/riven_stacks/aspit.cpp:454
@@ -8355,6 +8356,22 @@ msgstr ""
 "????????????????, ???????????????????? ???? ???????????????????? ???? Operation Stealth ?????? ?????? ???????? ?????? ???? "
 "???????????????????????? ???? ???????? ???? ???????????????? ??????."
 
+#: engines/colony/intro.cpp:154 engines/colony/intro.cpp:1244
+#, fuzzy
+#| msgid "New game"
+msgid "New Game"
+msgstr "?????? ????????????????"
+
+#: engines/colony/intro.cpp:155 engines/colony/intro.cpp:1242
+msgid "Load Game"
+msgstr "?????????????? ????????????????????"
+
+#: engines/colony/intro.cpp:1219 engines/colony/intro.cpp:1244
+#, fuzzy
+#| msgid "You have stored "
+msgid "You have been terminated."
+msgstr "?????????? ???????????????????????? "
+
 #: engines/colony/metaengine.cpp:45 engines/myst3/metaengine.cpp:40
 msgid "Widescreen mod"
 msgstr "???????????????????? ?????????????? ????????????"
@@ -18167,9 +18184,6 @@ msgstr ""
 #~ msgid "Save Game"
 #~ msgstr "???????????????????? ????????????????????"
 
-#~ msgid "Load Game"
-#~ msgstr "?????????????? ????????????????????"
-
 #~ msgid "Quit Game"
 #~ msgstr "???????????? ?????? ???? ????????????????"
 
@@ -19176,9 +19190,6 @@ msgstr ""
 #~ msgid "The game is now over.\n"
 #~ msgstr "???? ???????????????? ???????? ?????????????????? ????????.\n"
 
-#~ msgid "You have stored "
-#~ msgstr "?????????? ???????????????????????? "
-
 #~ msgid "I've stored "
 #~ msgstr "?????? ???????????????????????? "
 
diff --git a/po/es_ES.po b/po/es_ES.po
index dfd79ec4e7a..31de6979050 100644
--- a/po/es_ES.po
+++ b/po/es_ES.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.4.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-14 22:15+0000\n"
+"POT-Creation-Date: 2026-06-17 23:07+0000\n"
 "PO-Revision-Date: 2026-06-14 09:53+0000\n"
 "Last-Translator: IlDucci <pajaroloco_2 at hotmail.com>\n"
 "Language-Team: Spanish <https://translations.scummvm.org/projects/scummvm/"
@@ -3431,7 +3431,8 @@ msgstr "Motor de aventuras gr??ficas"
 #: backends/events/default/default-events.cpp:217
 #: backends/events/default/default-events.cpp:358
 #: engines/adl/metaengine.cpp:352 engines/buried/metaengine.cpp:154
-#: engines/chamber/metaengine.cpp:84 engines/drascula/metaengine.cpp:304
+#: engines/chamber/metaengine.cpp:84 engines/colony/intro.cpp:156
+#: engines/colony/intro.cpp:1243 engines/drascula/metaengine.cpp:304
 #: engines/hdb/metaengine.cpp:275 engines/hypno/metaengine.cpp:308
 #: engines/lab/metaengine.cpp:172 engines/mohawk/myst_stacks/menu.cpp:317
 #: engines/mohawk/riven_stacks/aspit.cpp:454
@@ -8233,6 +8234,22 @@ msgstr ""
 "defectuoso. El juego funcionar?? mal. Por favor, plant??ate empezar Operation "
 "Stealth desde el principio, con partidas guardadas nuevas."
 
+#: engines/colony/intro.cpp:154 engines/colony/intro.cpp:1244
+#, fuzzy
+#| msgid "New game"
+msgid "New Game"
+msgstr "Nueva partida"
+
+#: engines/colony/intro.cpp:155 engines/colony/intro.cpp:1242
+msgid "Load Game"
+msgstr "Cargar partida"
+
+#: engines/colony/intro.cpp:1219 engines/colony/intro.cpp:1244
+#, fuzzy
+#| msgid "You have stored "
+msgid "You have been terminated."
+msgstr "Tienes "
+
 #: engines/colony/metaengine.cpp:45 engines/myst3/metaengine.cpp:40
 msgid "Widescreen mod"
 msgstr "Mod de pantalla panor??mica"
@@ -17746,9 +17763,6 @@ msgstr ""
 #~ msgid "Save Game"
 #~ msgstr "Guardar partida"
 
-#~ msgid "Load Game"
-#~ msgstr "Cargar partida"
-
 #~ msgid "Quit Game"
 #~ msgstr "Salir del juego"
 
@@ -18704,9 +18718,6 @@ msgstr ""
 #~ msgid "The game is now over.\n"
 #~ msgstr "El juego ha terminado.\n"
 
-#~ msgid "You have stored "
-#~ msgstr "Tienes "
-
 #~ msgid "I've stored "
 #~ msgstr "Tengo "
 
diff --git a/po/eu.po b/po/eu.po
index cd153a56fc1..d76e13d9476 100644
--- a/po/eu.po
+++ b/po/eu.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.5.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-14 22:15+0000\n"
+"POT-Creation-Date: 2026-06-17 23:07+0000\n"
 "PO-Revision-Date: 2024-02-05 18:42+0000\n"
 "Last-Translator: Josu Igoa <josuigoa at ni.eus>\n"
 "Language-Team: Basque <https://translations.scummvm.org/projects/scummvm/"
@@ -3420,7 +3420,8 @@ msgstr "Abendura grafikoen motorea"
 #: backends/events/default/default-events.cpp:217
 #: backends/events/default/default-events.cpp:358
 #: engines/adl/metaengine.cpp:352 engines/buried/metaengine.cpp:154
-#: engines/chamber/metaengine.cpp:84 engines/drascula/metaengine.cpp:304
+#: engines/chamber/metaengine.cpp:84 engines/colony/intro.cpp:156
+#: engines/colony/intro.cpp:1243 engines/drascula/metaengine.cpp:304
 #: engines/hdb/metaengine.cpp:275 engines/hypno/metaengine.cpp:308
 #: engines/lab/metaengine.cpp:172 engines/mohawk/myst_stacks/menu.cpp:317
 #: engines/mohawk/riven_stacks/aspit.cpp:454
@@ -8065,6 +8066,21 @@ msgid ""
 "beginning using new savegames."
 msgstr ""
 
+#: engines/colony/intro.cpp:154 engines/colony/intro.cpp:1244
+#, fuzzy
+msgid "New Game"
+msgstr "Gorde"
+
+#: engines/colony/intro.cpp:155 engines/colony/intro.cpp:1242
+#, fuzzy
+msgid "Load Game"
+msgstr "Jokoa kargatu:"
+
+#: engines/colony/intro.cpp:1219 engines/colony/intro.cpp:1244
+#, fuzzy
+msgid "You have been terminated."
+msgstr "Kargatu"
+
 #: engines/colony/metaengine.cpp:45 engines/myst3/metaengine.cpp:40
 #, fuzzy
 msgid "Widescreen mod"
@@ -18266,10 +18282,6 @@ msgstr ""
 #~ msgid "Save Game"
 #~ msgstr "Gorde jokoa:"
 
-#, fuzzy
-#~ msgid "Load Game"
-#~ msgstr "Jokoa kargatu:"
-
 #, fuzzy
 #~ msgid "Quit Game"
 #~ msgstr " (Jokoa)"
@@ -18705,10 +18717,6 @@ msgstr ""
 #~ msgid "The game is now over.\n"
 #~ msgstr "Jokoa EZ da kargatu"
 
-#, fuzzy
-#~ msgid "You have stored "
-#~ msgstr "Kargatu"
-
 #, fuzzy
 #~ msgid "I've stored "
 #~ msgstr "Kargatu"
diff --git a/po/fi_FI.po b/po/fi_FI.po
index 35c983f821c..e1101a8192a 100644
--- a/po/fi_FI.po
+++ b/po/fi_FI.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.6.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-14 22:15+0000\n"
+"POT-Creation-Date: 2026-06-17 23:07+0000\n"
 "PO-Revision-Date: 2026-02-07 15:22+0000\n"
 "Last-Translator: Timo Mikkolainen <tmikkola at gmail.com>\n"
 "Language-Team: Finnish <https://translations.scummvm.org/projects/scummvm/"
@@ -3463,7 +3463,8 @@ msgstr "Pelimoottori graafisille seikkailupeleille"
 #: backends/events/default/default-events.cpp:217
 #: backends/events/default/default-events.cpp:358
 #: engines/adl/metaengine.cpp:352 engines/buried/metaengine.cpp:154
-#: engines/chamber/metaengine.cpp:84 engines/drascula/metaengine.cpp:304
+#: engines/chamber/metaengine.cpp:84 engines/colony/intro.cpp:156
+#: engines/colony/intro.cpp:1243 engines/drascula/metaengine.cpp:304
 #: engines/hdb/metaengine.cpp:275 engines/hypno/metaengine.cpp:308
 #: engines/lab/metaengine.cpp:172 engines/mohawk/myst_stacks/menu.cpp:317
 #: engines/mohawk/riven_stacks/aspit.cpp:454
@@ -8339,6 +8340,22 @@ msgstr ""
 "tiedostomuotoa. T??m?? ei tule toimimaan. Harkitse aloittavasi Operation "
 "Stealth alusta k??ytt??en uusia pelitallennuksia."
 
+#: engines/colony/intro.cpp:154 engines/colony/intro.cpp:1244
+#, fuzzy
+#| msgid "New game"
+msgid "New Game"
+msgstr "Uusi peli"
+
+#: engines/colony/intro.cpp:155 engines/colony/intro.cpp:1242
+msgid "Load Game"
+msgstr "Lataa pelitallennus"
+
+#: engines/colony/intro.cpp:1219 engines/colony/intro.cpp:1244
+#, fuzzy
+#| msgid "You have stored "
+msgid "You have been terminated."
+msgstr "Olet ker??nnyt "
+
 #: engines/colony/metaengine.cpp:45 engines/myst3/metaengine.cpp:40
 msgid "Widescreen mod"
 msgstr "Laajakuvamodi"
@@ -18187,9 +18204,6 @@ msgstr ""
 #~ msgid "Save Game"
 #~ msgstr "Tallenna peli"
 
-#~ msgid "Load Game"
-#~ msgstr "Lataa pelitallennus"
-
 #~ msgid "Quit Game"
 #~ msgstr "Lopeta peli"
 
@@ -19155,9 +19169,6 @@ msgstr ""
 #~ msgid "The game is now over.\n"
 #~ msgstr "Peli on nyt loppu.\n"
 
-#~ msgid "You have stored "
-#~ msgstr "Olet ker??nnyt "
-
 #~ msgid "I've stored "
 #~ msgstr "Olen ker??nnyt "
 
diff --git a/po/fr_FR.po b/po/fr_FR.po
index a7d7505dbd8..22101fe4159 100644
--- a/po/fr_FR.po
+++ b/po/fr_FR.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-14 22:15+0000\n"
+"POT-Creation-Date: 2026-06-17 23:07+0000\n"
 "PO-Revision-Date: 2026-06-14 09:41+0000\n"
 "Last-Translator: Purple T <ZEONK at hotmail.com>\n"
 "Language-Team: French <https://translations.scummvm.org/projects/scummvm/"
@@ -3447,7 +3447,8 @@ msgstr "Moteur de jeux d'aventure graphiques"
 #: backends/events/default/default-events.cpp:217
 #: backends/events/default/default-events.cpp:358
 #: engines/adl/metaengine.cpp:352 engines/buried/metaengine.cpp:154
-#: engines/chamber/metaengine.cpp:84 engines/drascula/metaengine.cpp:304
+#: engines/chamber/metaengine.cpp:84 engines/colony/intro.cpp:156
+#: engines/colony/intro.cpp:1243 engines/drascula/metaengine.cpp:304
 #: engines/hdb/metaengine.cpp:275 engines/hypno/metaengine.cpp:308
 #: engines/lab/metaengine.cpp:172 engines/mohawk/myst_stacks/menu.cpp:317
 #: engines/mohawk/riven_stacks/aspit.cpp:454
@@ -8236,6 +8237,22 @@ msgstr ""
 "red??marrer Operation Stealth depuis le d??but en utilisant de nouvelles "
 "sauvegardes."
 
+#: engines/colony/intro.cpp:154 engines/colony/intro.cpp:1244
+#, fuzzy
+#| msgid "New game"
+msgid "New Game"
+msgstr "Nouvelle partie"
+
+#: engines/colony/intro.cpp:155 engines/colony/intro.cpp:1242
+msgid "Load Game"
+msgstr "Charger"
+
+#: engines/colony/intro.cpp:1219 engines/colony/intro.cpp:1244
+#, fuzzy
+#| msgid "You have stored "
+msgid "You have been terminated."
+msgstr "Vous avez conserv?? "
+
 #: engines/colony/metaengine.cpp:45 engines/myst3/metaengine.cpp:40
 msgid "Widescreen mod"
 msgstr "Mode Ecran large"
@@ -17793,9 +17810,6 @@ msgstr ""
 #~ msgid "Save Game"
 #~ msgstr "Sauvegarder"
 
-#~ msgid "Load Game"
-#~ msgstr "Charger"
-
 #~ msgid "Quit Game"
 #~ msgstr "Quitter le jeu"
 
@@ -18755,9 +18769,6 @@ msgstr ""
 #~ msgid "The game is now over.\n"
 #~ msgstr "Le jeu est maintenant termin??.\n"
 
-#~ msgid "You have stored "
-#~ msgstr "Vous avez conserv?? "
-
 #~ msgid "I've stored "
 #~ msgstr "J'ai conserv?? "
 
diff --git a/po/gl_ES.po b/po/gl_ES.po
index 1cc033c31a3..9f9ef1f882e 100644
--- a/po/gl_ES.po
+++ b/po/gl_ES.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-14 22:15+0000\n"
+"POT-Creation-Date: 2026-06-17 23:07+0000\n"
 "PO-Revision-Date: 2020-09-26 21:13+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Galician <https://translations.scummvm.org/projects/scummvm/"
@@ -3372,7 +3372,8 @@ msgstr ""
 #: backends/events/default/default-events.cpp:217
 #: backends/events/default/default-events.cpp:358
 #: engines/adl/metaengine.cpp:352 engines/buried/metaengine.cpp:154
-#: engines/chamber/metaengine.cpp:84 engines/drascula/metaengine.cpp:304
+#: engines/chamber/metaengine.cpp:84 engines/colony/intro.cpp:156
+#: engines/colony/intro.cpp:1243 engines/drascula/metaengine.cpp:304
 #: engines/hdb/metaengine.cpp:275 engines/hypno/metaengine.cpp:308
 #: engines/lab/metaengine.cpp:172 engines/mohawk/myst_stacks/menu.cpp:317
 #: engines/mohawk/riven_stacks/aspit.cpp:454
@@ -7996,6 +7997,21 @@ msgid ""
 "beginning using new savegames."
 msgstr ""
 
+#: engines/colony/intro.cpp:154 engines/colony/intro.cpp:1244
+#, fuzzy
+msgid "New Game"
+msgstr "Novo ficheiro"
+
+#: engines/colony/intro.cpp:155 engines/colony/intro.cpp:1242
+#, fuzzy
+msgid "Load Game"
+msgstr "Cargar partida:"
+
+#: engines/colony/intro.cpp:1219 engines/colony/intro.cpp:1244
+#, fuzzy
+msgid "You have been terminated."
+msgstr "Restaurar"
+
 #: engines/colony/metaengine.cpp:45 engines/myst3/metaengine.cpp:40
 #, fuzzy
 msgid "Widescreen mod"
@@ -18167,10 +18183,6 @@ msgstr ""
 #~ msgid "Save Game"
 #~ msgstr "Gardar partida:"
 
-#, fuzzy
-#~ msgid "Load Game"
-#~ msgstr "Cargar partida:"
-
 #, fuzzy
 #~ msgid "Quit Game"
 #~ msgstr " (Xogo)"
@@ -18658,10 +18670,6 @@ msgstr ""
 #~ msgid "The game is now over.\n"
 #~ msgstr "Non se puido cargar a partida"
 
-#, fuzzy
-#~ msgid "You have stored "
-#~ msgstr "Restaurar"
-
 #, fuzzy
 #~ msgid "I've stored "
 #~ msgstr "Restaurar"
diff --git a/po/he.po b/po/he.po
index 6bd19dcdd93..8557e26c398 100644
--- a/po/he.po
+++ b/po/he.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.1.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-14 22:15+0000\n"
+"POT-Creation-Date: 2026-06-17 23:07+0000\n"
 "PO-Revision-Date: 2025-04-09 20:18+0000\n"
 "Last-Translator: Niv Baehr <bloop93 at gmail.com>\n"
 "Language-Team: Hebrew <https://translations.scummvm.org/projects/scummvm/"
@@ -3311,7 +3311,8 @@ msgstr "???????? ???????? ???????????? ????????"
 #: backends/events/default/default-events.cpp:217
 #: backends/events/default/default-events.cpp:358
 #: engines/adl/metaengine.cpp:352 engines/buried/metaengine.cpp:154
-#: engines/chamber/metaengine.cpp:84 engines/drascula/metaengine.cpp:304
+#: engines/chamber/metaengine.cpp:84 engines/colony/intro.cpp:156
+#: engines/colony/intro.cpp:1243 engines/drascula/metaengine.cpp:304
 #: engines/hdb/metaengine.cpp:275 engines/hypno/metaengine.cpp:308
 #: engines/lab/metaengine.cpp:172 engines/mohawk/myst_stacks/menu.cpp:317
 #: engines/mohawk/riven_stacks/aspit.cpp:454
@@ -7989,6 +7990,22 @@ msgstr ""
 "??????????: ?????????? ?????????? ?????????? ?????????? ???????????? ???????? ????????. ?????????? ??????????. ???? ?????????? "
 "???????????? ???? Operation Stealth ?????????????? ???????????? ?????????????? ??????????."
 
+#: engines/colony/intro.cpp:154 engines/colony/intro.cpp:1244
+#, fuzzy
+#| msgid "New game"
+msgid "New Game"
+msgstr "???????? ??????"
+
+#: engines/colony/intro.cpp:155 engines/colony/intro.cpp:1242
+msgid "Load Game"
+msgstr "?????????? ????????"
+
+#: engines/colony/intro.cpp:1219 engines/colony/intro.cpp:1244
+#, fuzzy
+#| msgid "You have stored "
+msgid "You have been terminated."
+msgstr "???????? "
+
 #: engines/colony/metaengine.cpp:45 engines/myst3/metaengine.cpp:40
 msgid "Widescreen mod"
 msgstr "???????? ?????? ??????"
@@ -18648,9 +18665,6 @@ msgstr ""
 #~ msgid "Save Game"
 #~ msgstr "?????????? ????????"
 
-#~ msgid "Load Game"
-#~ msgstr "?????????? ????????"
-
 #~ msgid "Quit Game"
 #~ msgstr "?????????? ????????????"
 
@@ -19312,9 +19326,6 @@ msgstr ""
 #~ msgid "The game is now over.\n"
 #~ msgstr "?????????? ????????.\n"
 
-#~ msgid "You have stored "
-#~ msgstr "???????? "
-
 #~ msgid "I've stored "
 #~ msgstr "?????????? "
 
diff --git a/po/hi.po b/po/hi.po
index d7bb67ed7dd..dc148ff816c 100644
--- a/po/hi.po
+++ b/po/hi.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-14 22:15+0000\n"
+"POT-Creation-Date: 2026-06-17 23:07+0000\n"
 "PO-Revision-Date: 2026-05-12 22:22+0000\n"
 "Last-Translator: KARTIK SAXENA <kartiksaxena2005 at gmail.com>\n"
 "Language-Team: Hindi <https://translations.scummvm.org/projects/scummvm/"
@@ -3281,7 +3281,8 @@ msgstr ""
 #: backends/events/default/default-events.cpp:217
 #: backends/events/default/default-events.cpp:358
 #: engines/adl/metaengine.cpp:352 engines/buried/metaengine.cpp:154
-#: engines/chamber/metaengine.cpp:84 engines/drascula/metaengine.cpp:304
+#: engines/chamber/metaengine.cpp:84 engines/colony/intro.cpp:156
+#: engines/colony/intro.cpp:1243 engines/drascula/metaengine.cpp:304
 #: engines/hdb/metaengine.cpp:275 engines/hypno/metaengine.cpp:308
 #: engines/lab/metaengine.cpp:172 engines/mohawk/myst_stacks/menu.cpp:317
 #: engines/mohawk/riven_stacks/aspit.cpp:454
@@ -7682,6 +7683,20 @@ msgid ""
 "beginning using new savegames."
 msgstr ""
 
+#: engines/colony/intro.cpp:154 engines/colony/intro.cpp:1244
+#, fuzzy
+#| msgid "Save Game"
+msgid "New Game"
+msgstr "????????? ????????? ????????????"
+
+#: engines/colony/intro.cpp:155 engines/colony/intro.cpp:1242
+msgid "Load Game"
+msgstr "????????? ????????? ????????????"
+
+#: engines/colony/intro.cpp:1219 engines/colony/intro.cpp:1244
+msgid "You have been terminated."
+msgstr ""
+
 #: engines/colony/metaengine.cpp:45 engines/myst3/metaengine.cpp:40
 msgid "Widescreen mod"
 msgstr ""
@@ -17475,12 +17490,6 @@ msgstr ""
 #~ msgid "Scroll Down"
 #~ msgstr "???????????? ????????????????????? ????????????"
 
-#~ msgid "Save Game"
-#~ msgstr "????????? ????????? ????????????"
-
-#~ msgid "Load Game"
-#~ msgstr "????????? ????????? ????????????"
-
 #~ msgid "Quit Game"
 #~ msgstr "????????? quit ????????????"
 
diff --git a/po/hu_HU.po b/po/hu_HU.po
index 2b6529b6778..b066a03f335 100644
--- a/po/hu_HU.po
+++ b/po/hu_HU.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-14 22:15+0000\n"
+"POT-Creation-Date: 2026-06-17 23:07+0000\n"
 "PO-Revision-Date: 2025-03-07 00:18+0000\n"
 "Last-Translator: Hov??nszki Tam??s <gahex68379 at lofiey.com>\n"
 "Language-Team: Hungarian <https://translations.scummvm.org/projects/scummvm/"
@@ -3467,7 +3467,8 @@ msgstr "Grafikus kalandj??t??k motor"
 #: backends/events/default/default-events.cpp:217
 #: backends/events/default/default-events.cpp:358
 #: engines/adl/metaengine.cpp:352 engines/buried/metaengine.cpp:154
-#: engines/chamber/metaengine.cpp:84 engines/drascula/metaengine.cpp:304
+#: engines/chamber/metaengine.cpp:84 engines/colony/intro.cpp:156
+#: engines/colony/intro.cpp:1243 engines/drascula/metaengine.cpp:304
 #: engines/hdb/metaengine.cpp:275 engines/hypno/metaengine.cpp:308
 #: engines/lab/metaengine.cpp:172 engines/mohawk/myst_stacks/menu.cpp:317
 #: engines/mohawk/riven_stacks/aspit.cpp:454
@@ -8512,6 +8513,22 @@ msgstr ""
 "emiatt hib??k mer??lhetnek fel. Aj??nlott az Operation Stealth egy ??j ment??sb??l "
 "val?? visszat??lt??se."
 
+#: engines/colony/intro.cpp:154 engines/colony/intro.cpp:1244
+#, fuzzy
+#| msgid "New game"
+msgid "New Game"
+msgstr "??j j??t??k"
+
+#: engines/colony/intro.cpp:155 engines/colony/intro.cpp:1242
+msgid "Load Game"
+msgstr "J??t??k bet??lt??s"
+
+#: engines/colony/intro.cpp:1219 engines/colony/intro.cpp:1244
+#, fuzzy
+#| msgid "You have stored "
+msgid "You have been terminated."
+msgstr "Rakt??rozt??l "
+
 #: engines/colony/metaengine.cpp:45 engines/myst3/metaengine.cpp:40
 msgid "Widescreen mod"
 msgstr "Sz??lesv??szn?? m??d"
@@ -19068,9 +19085,6 @@ msgstr ""
 #~ msgid "Save Game"
 #~ msgstr "J??t??k ment??s"
 
-#~ msgid "Load Game"
-#~ msgstr "J??t??k bet??lt??s"
-
 #~ msgid "Quit Game"
 #~ msgstr "Kil??p??s a j??t??kb??l"
 
@@ -19854,9 +19868,6 @@ msgstr ""
 #~ msgid "The game is now over.\n"
 #~ msgstr "A j??t??knak most v??ge.\n"
 
-#~ msgid "You have stored "
-#~ msgstr "Rakt??rozt??l "
-
 #~ msgid "I've stored "
 #~ msgstr "Rakt??roztam "
 
diff --git a/po/it_IT.po b/po/it_IT.po
index 6333fe9d31a..e1a12177b42 100644
--- a/po/it_IT.po
+++ b/po/it_IT.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-14 22:15+0000\n"
+"POT-Creation-Date: 2026-06-17 23:07+0000\n"
 "PO-Revision-Date: 2026-06-14 09:41+0000\n"
 "Last-Translator: Walter Agazzi <walter.agazzi at protonmail.com>\n"
 "Language-Team: Italian <https://translations.scummvm.org/projects/scummvm/"
@@ -3420,7 +3420,8 @@ msgstr "Motore per avventure grafiche"
 #: backends/events/default/default-events.cpp:217
 #: backends/events/default/default-events.cpp:358
 #: engines/adl/metaengine.cpp:352 engines/buried/metaengine.cpp:154
-#: engines/chamber/metaengine.cpp:84 engines/drascula/metaengine.cpp:304
+#: engines/chamber/metaengine.cpp:84 engines/colony/intro.cpp:156
+#: engines/colony/intro.cpp:1243 engines/drascula/metaengine.cpp:304
 #: engines/hdb/metaengine.cpp:275 engines/hypno/metaengine.cpp:308
 #: engines/lab/metaengine.cpp:172 engines/mohawk/myst_stacks/menu.cpp:317
 #: engines/mohawk/riven_stacks/aspit.cpp:454
@@ -8211,6 +8212,22 @@ msgstr ""
 "Riscontrerai dei problemi. Se possibile, ricomincia Operation Stealth "
 "dall'inizio e crea dei nuovi salvataggi."
 
+#: engines/colony/intro.cpp:154 engines/colony/intro.cpp:1244
+#, fuzzy
+#| msgid "New game"
+msgid "New Game"
+msgstr "Nuova partita"
+
+#: engines/colony/intro.cpp:155 engines/colony/intro.cpp:1242
+msgid "Load Game"
+msgstr "Carica Partita"
+
+#: engines/colony/intro.cpp:1219 engines/colony/intro.cpp:1244
+#, fuzzy
+#| msgid "You have stored "
+msgid "You have been terminated."
+msgstr "Hai messo nell'inventario "
+
 #: engines/colony/metaengine.cpp:45 engines/myst3/metaengine.cpp:40
 msgid "Widescreen mod"
 msgstr "Modalit?? widescreen"
@@ -17723,9 +17740,6 @@ msgstr ""
 #~ msgid "Save Game"
 #~ msgstr "Salva Partita"
 
-#~ msgid "Load Game"
-#~ msgstr "Carica Partita"
-
 #~ msgid "Quit Game"
 #~ msgstr "Esci dal gioco"
 
@@ -18541,9 +18555,6 @@ msgstr ""
 #~ msgid "The game is now over.\n"
 #~ msgstr "La partita ?? finita.\n"
 
-#~ msgid "You have stored "
-#~ msgstr "Hai messo nell'inventario "
-
 #~ msgid "I've stored "
 #~ msgstr "Ho messo nell'inventario "
 
diff --git a/po/ja.po b/po/ja.po
index b52f0fcd7d4..4cef53d393f 100644
--- a/po/ja.po
+++ b/po/ja.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-14 22:15+0000\n"
+"POT-Creation-Date: 2026-06-17 23:07+0000\n"
 "PO-Revision-Date: 2026-06-04 07:22+0000\n"
 "Last-Translator: VAN-Gluon <van.gluonp at gmail.com>\n"
 "Language-Team: Japanese <https://translations.scummvm.org/projects/scummvm/"
@@ -3316,7 +3316,8 @@ msgstr "???????????????????????????????????????????????????"
 #: backends/events/default/default-events.cpp:217
 #: backends/events/default/default-events.cpp:358
 #: engines/adl/metaengine.cpp:352 engines/buried/metaengine.cpp:154
-#: engines/chamber/metaengine.cpp:84 engines/drascula/metaengine.cpp:304
+#: engines/chamber/metaengine.cpp:84 engines/colony/intro.cpp:156
+#: engines/colony/intro.cpp:1243 engines/drascula/metaengine.cpp:304
 #: engines/hdb/metaengine.cpp:275 engines/hypno/metaengine.cpp:308
 #: engines/lab/metaengine.cpp:172 engines/mohawk/myst_stacks/menu.cpp:317
 #: engines/mohawk/riven_stacks/aspit.cpp:454
@@ -8115,6 +8116,22 @@ msgstr ""
 "?????????????????????????????????????????????????????????????????????Operation Stealth?????????????????????"
 "???????????????????????????"
 
+#: engines/colony/intro.cpp:154 engines/colony/intro.cpp:1244
+#, fuzzy
+#| msgid "New game"
+msgid "New Game"
+msgstr "??????????????????"
+
+#: engines/colony/intro.cpp:155 engines/colony/intro.cpp:1242
+msgid "Load Game"
+msgstr "?????????????????????"
+
+#: engines/colony/intro.cpp:1219 engines/colony/intro.cpp:1244
+#, fuzzy
+#| msgid "You have stored "
+msgid "You have been terminated."
+msgstr "?????????????????????????????? "
+
 #: engines/colony/metaengine.cpp:45 engines/myst3/metaengine.cpp:40
 msgid "Widescreen mod"
 msgstr "??????????????????????????????MOD"
@@ -18704,9 +18721,6 @@ msgstr ""
 #~ msgid "Save Game"
 #~ msgstr "?????????????????????"
 
-#~ msgid "Load Game"
-#~ msgstr "?????????????????????"
-
 #~ msgid "Quit Game"
 #~ msgstr "????????????????????????"
 
@@ -19559,9 +19573,6 @@ msgstr ""
 #~ msgid "The game is now over.\n"
 #~ msgstr "?????????????????????????????????\n"
 
-#~ msgid "You have stored "
-#~ msgstr "?????????????????????????????? "
-
 #~ msgid "I've stored "
 #~ msgstr "???????????????????????? "
 
diff --git a/po/ka.po b/po/ka.po
index d236afac818..f01844e91bf 100644
--- a/po/ka.po
+++ b/po/ka.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-14 22:15+0000\n"
+"POT-Creation-Date: 2026-06-17 23:07+0000\n"
 "PO-Revision-Date: 2026-06-05 10:22+0000\n"
 "Last-Translator: NorwayFun <temuri.doghonadze at gmail.com>\n"
 "Language-Team: Georgian <https://translations.scummvm.org/projects/scummvm/"
@@ -3179,7 +3179,8 @@ msgstr "????????????????????????????????????????????? ????????????????????? ???
 #: backends/events/default/default-events.cpp:217
 #: backends/events/default/default-events.cpp:358
 #: engines/adl/metaengine.cpp:352 engines/buried/metaengine.cpp:154
-#: engines/chamber/metaengine.cpp:84 engines/drascula/metaengine.cpp:304
+#: engines/chamber/metaengine.cpp:84 engines/colony/intro.cpp:156
+#: engines/colony/intro.cpp:1243 engines/drascula/metaengine.cpp:304
 #: engines/hdb/metaengine.cpp:275 engines/hypno/metaengine.cpp:308
 #: engines/lab/metaengine.cpp:172 engines/mohawk/myst_stacks/menu.cpp:317
 #: engines/mohawk/riven_stacks/aspit.cpp:454
@@ -7403,6 +7404,20 @@ msgid ""
 "beginning using new savegames."
 msgstr ""
 
+#: engines/colony/intro.cpp:154 engines/colony/intro.cpp:1244
+#, fuzzy
+#| msgid "New game"
+msgid "New Game"
+msgstr "??????????????? ??????????????????"
+
+#: engines/colony/intro.cpp:155 engines/colony/intro.cpp:1242
+msgid "Load Game"
+msgstr "????????????????????? ???????????????????????????"
+
+#: engines/colony/intro.cpp:1219 engines/colony/intro.cpp:1244
+msgid "You have been terminated."
+msgstr ""
+
 #: engines/colony/metaengine.cpp:45 engines/myst3/metaengine.cpp:40
 msgid "Widescreen mod"
 msgstr "?????????????????????????????????????????? ????????????"
@@ -16271,9 +16286,6 @@ msgstr ""
 #~ msgid "Save Game"
 #~ msgstr "????????????????????? ?????????????????????"
 
-#~ msgid "Load Game"
-#~ msgstr "????????????????????? ???????????????????????????"
-
 #~ msgid "Quit Game"
 #~ msgstr "??????????????????????????? ??????????????????"
 
diff --git a/po/ko.po b/po/ko.po
index 0efb481a84f..c124993489a 100644
--- a/po/ko.po
+++ b/po/ko.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-14 22:15+0000\n"
+"POT-Creation-Date: 2026-06-17 23:07+0000\n"
 "PO-Revision-Date: 2026-05-19 20:22+0000\n"
 "Last-Translator: Hoseok Seo <ddinghoya at gmail.com>\n"
 "Language-Team: Korean <https://translations.scummvm.org/projects/scummvm/"
@@ -3362,7 +3362,8 @@ msgstr "????????? ?????? ?????? ??????"
 #: backends/events/default/default-events.cpp:217
 #: backends/events/default/default-events.cpp:358
 #: engines/adl/metaengine.cpp:352 engines/buried/metaengine.cpp:154
-#: engines/chamber/metaengine.cpp:84 engines/drascula/metaengine.cpp:304
+#: engines/chamber/metaengine.cpp:84 engines/colony/intro.cpp:156
+#: engines/colony/intro.cpp:1243 engines/drascula/metaengine.cpp:304
 #: engines/hdb/metaengine.cpp:275 engines/hypno/metaengine.cpp:308
 #: engines/lab/metaengine.cpp:172 engines/mohawk/myst_stacks/menu.cpp:317
 #: engines/mohawk/riven_stacks/aspit.cpp:454
@@ -8038,6 +8039,22 @@ msgstr ""
 "?????? ????????? ?????? ????????? ????????????. ??? ?????? ????????? ???????????? ???????????? Operation "
 "Stealth??? ???????????? ?????? ????????? ?????????."
 
+#: engines/colony/intro.cpp:154 engines/colony/intro.cpp:1244
+#, fuzzy
+#| msgid "New game"
+msgid "New Game"
+msgstr "??? ??????"
+
+#: engines/colony/intro.cpp:155 engines/colony/intro.cpp:1242
+msgid "Load Game"
+msgstr "?????? ????????????"
+
+#: engines/colony/intro.cpp:1219 engines/colony/intro.cpp:1244
+#, fuzzy
+#| msgid "You have stored "
+msgid "You have been terminated."
+msgstr "????????? ?????? ??????????????? "
+
 #: engines/colony/metaengine.cpp:45 engines/myst3/metaengine.cpp:40
 msgid "Widescreen mod"
 msgstr "????????? ????????? ??????"
@@ -17378,9 +17395,6 @@ msgstr ""
 #~ msgid "Save Game"
 #~ msgstr "?????? ??????"
 
-#~ msgid "Load Game"
-#~ msgstr "?????? ????????????"
-
 #~ msgid "Quit Game"
 #~ msgstr "?????? ??????"
 
@@ -17996,9 +18010,6 @@ msgstr ""
 #~ msgid "The game is now over.\n"
 #~ msgstr "?????? ?????? ???????????????.\n"
 
-#~ msgid "You have stored "
-#~ msgstr "????????? ?????? ??????????????? "
-
 #~ msgid "I've stored "
 #~ msgstr "?????? ????????????????????? "
 
diff --git a/po/nb_NO.po b/po/nb_NO.po
index 6444b78701c..cf199050f6e 100644
--- a/po/nb_NO.po
+++ b/po/nb_NO.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-14 22:15+0000\n"
+"POT-Creation-Date: 2026-06-17 23:07+0000\n"
 "PO-Revision-Date: 2026-02-07 15:22+0000\n"
 "Last-Translator: Einar Johan Tr??an S??m??en <einarjohants at gmail.com>\n"
 "Language-Team: Norwegian Bokm??l <https://translations.scummvm.org/projects/"
@@ -3471,7 +3471,8 @@ msgstr "Spillmotor for grafikkbaserte eventyr"
 #: backends/events/default/default-events.cpp:217
 #: backends/events/default/default-events.cpp:358
 #: engines/adl/metaengine.cpp:352 engines/buried/metaengine.cpp:154
-#: engines/chamber/metaengine.cpp:84 engines/drascula/metaengine.cpp:304
+#: engines/chamber/metaengine.cpp:84 engines/colony/intro.cpp:156
+#: engines/colony/intro.cpp:1243 engines/drascula/metaengine.cpp:304
 #: engines/hdb/metaengine.cpp:275 engines/hypno/metaengine.cpp:308
 #: engines/lab/metaengine.cpp:172 engines/mohawk/myst_stacks/menu.cpp:317
 #: engines/mohawk/riven_stacks/aspit.cpp:454
@@ -8584,6 +8585,22 @@ msgstr ""
 "format. Enkelte ting vil ikke v??re optimalt. Start heller Operation Stealth "
 "p?? nytt og opprett et nytt lagret spill."
 
+#: engines/colony/intro.cpp:154 engines/colony/intro.cpp:1244
+#, fuzzy
+#| msgid "New game"
+msgid "New Game"
+msgstr "Nytt spill"
+
+#: engines/colony/intro.cpp:155 engines/colony/intro.cpp:1242
+msgid "Load Game"
+msgstr "Laste inn spill"
+
+#: engines/colony/intro.cpp:1219 engines/colony/intro.cpp:1244
+#, fuzzy
+#| msgid "You have stored "
+msgid "You have been terminated."
+msgstr "Du har oppbevart "
+
 #: engines/colony/metaengine.cpp:45 engines/myst3/metaengine.cpp:40
 msgid "Widescreen mod"
 msgstr "Widescreen-modifikasjon"
@@ -19251,9 +19268,6 @@ msgstr ""
 #~ msgid "Save Game"
 #~ msgstr "Lagre spill"
 
-#~ msgid "Load Game"
-#~ msgstr "Laste inn spill"
-
 #~ msgid "Quit Game"
 #~ msgstr "Avslutt spill"
 
@@ -19970,9 +19984,6 @@ msgstr ""
 #~ msgid "The game is now over.\n"
 #~ msgstr "Spillet er n?? over.\n"
 
-#~ msgid "You have stored "
-#~ msgstr "Du har oppbevart "
-
 #~ msgid "I've stored "
 #~ msgstr "Jeg har oppbevart "
 
diff --git a/po/nl_NL.po b/po/nl_NL.po
index e24b902e681..19a23c4d561 100644
--- a/po/nl_NL.po
+++ b/po/nl_NL.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.9.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-14 22:15+0000\n"
+"POT-Creation-Date: 2026-06-17 23:07+0000\n"
 "PO-Revision-Date: 2026-02-11 14:23+0000\n"
 "Last-Translator: SecularSteve <fairfull.playing at gmail.com>\n"
 "Language-Team: Dutch <https://translations.scummvm.org/projects/scummvm/"
@@ -3403,7 +3403,8 @@ msgstr "Grafische avonturen spel engine"
 #: backends/events/default/default-events.cpp:217
 #: backends/events/default/default-events.cpp:358
 #: engines/adl/metaengine.cpp:352 engines/buried/metaengine.cpp:154
-#: engines/chamber/metaengine.cpp:84 engines/drascula/metaengine.cpp:304
+#: engines/chamber/metaengine.cpp:84 engines/colony/intro.cpp:156
+#: engines/colony/intro.cpp:1243 engines/drascula/metaengine.cpp:304
 #: engines/hdb/metaengine.cpp:275 engines/hypno/metaengine.cpp:308
 #: engines/lab/metaengine.cpp:172 engines/mohawk/myst_stacks/menu.cpp:317
 #: engines/mohawk/riven_stacks/aspit.cpp:454
@@ -8167,6 +8168,22 @@ msgstr ""
 "onvolledig formaat. Er zullen zaken fout gaan. Overweeg a.u.b. om Operation "
 "Stealth opnieuw te beginnen met nieuwe opgeslagen spellen."
 
+#: engines/colony/intro.cpp:154 engines/colony/intro.cpp:1244
+#, fuzzy
+#| msgid "New game"
+msgid "New Game"
+msgstr "Nieuw spel"
+
+#: engines/colony/intro.cpp:155 engines/colony/intro.cpp:1242
+msgid "Load Game"
+msgstr "Laad Spel"
+
+#: engines/colony/intro.cpp:1219 engines/colony/intro.cpp:1244
+#, fuzzy
+#| msgid "You have stored "
+msgid "You have been terminated."
+msgstr "U heeft opgeslagen "
+
 #: engines/colony/metaengine.cpp:45 engines/myst3/metaengine.cpp:40
 msgid "Widescreen mod"
 msgstr "Breedbeeld aanpassing"
@@ -17880,9 +17897,6 @@ msgstr ""
 #~ msgid "Save Game"
 #~ msgstr "Spel Opslaan"
 
-#~ msgid "Load Game"
-#~ msgstr "Laad Spel"
-
 #~ msgid "Quit Game"
 #~ msgstr "Spel Verlaten"
 
@@ -18573,9 +18587,6 @@ msgstr ""
 #~ msgid "The game is now over.\n"
 #~ msgstr "Het spel is nu over.\n"
 
-#~ msgid "You have stored "
-#~ msgstr "U heeft opgeslagen "
-
 #~ msgid "I've stored "
 #~ msgstr "Ik heb opgeslagen "
 
diff --git a/po/nn_NO.po b/po/nn_NO.po
index e182a390590..dfb52357bcd 100644
--- a/po/nn_NO.po
+++ b/po/nn_NO.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-14 22:15+0000\n"
+"POT-Creation-Date: 2026-06-17 23:07+0000\n"
 "PO-Revision-Date: 2023-12-14 14:28+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Norwegian Nynorsk <https://translations.scummvm.org/projects/"
@@ -3342,7 +3342,8 @@ msgstr ""
 #: backends/events/default/default-events.cpp:217
 #: backends/events/default/default-events.cpp:358
 #: engines/adl/metaengine.cpp:352 engines/buried/metaengine.cpp:154
-#: engines/chamber/metaengine.cpp:84 engines/drascula/metaengine.cpp:304
+#: engines/chamber/metaengine.cpp:84 engines/colony/intro.cpp:156
+#: engines/colony/intro.cpp:1243 engines/drascula/metaengine.cpp:304
 #: engines/hdb/metaengine.cpp:275 engines/hypno/metaengine.cpp:308
 #: engines/lab/metaengine.cpp:172 engines/mohawk/myst_stacks/menu.cpp:317
 #: engines/mohawk/riven_stacks/aspit.cpp:454
@@ -7944,6 +7945,21 @@ msgid ""
 "beginning using new savegames."
 msgstr ""
 
+#: engines/colony/intro.cpp:154 engines/colony/intro.cpp:1244
+#, fuzzy
+msgid "New Game"
+msgstr "Ny Lagring"
+
+#: engines/colony/intro.cpp:155 engines/colony/intro.cpp:1242
+#, fuzzy
+msgid "Load Game"
+msgstr "??pne spel:"
+
+#: engines/colony/intro.cpp:1219 engines/colony/intro.cpp:1244
+#, fuzzy
+msgid "You have been terminated."
+msgstr "Gjenopprett"
+
 #: engines/colony/metaengine.cpp:45 engines/myst3/metaengine.cpp:40
 #, fuzzy
 msgid "Widescreen mod"
@@ -18093,10 +18109,6 @@ msgstr ""
 #~ msgid "Save Game"
 #~ msgstr "Lagra spel:"
 
-#, fuzzy
-#~ msgid "Load Game"
-#~ msgstr "??pne spel:"
-
 #, fuzzy
 #~ msgid "Quit Game"
 #~ msgstr " (Spel)"
@@ -18522,10 +18534,6 @@ msgstr ""
 #~ msgid "The game is now over.\n"
 #~ msgstr "Spelet vart IKKJE lasta"
 
-#, fuzzy
-#~ msgid "You have stored "
-#~ msgstr "Gjenopprett"
-
 #, fuzzy
 #~ msgid "I've stored "
 #~ msgstr "Gjenopprett"
diff --git a/po/pl_PL.po b/po/pl_PL.po
index a0a8f7c906f..bf096475100 100644
--- a/po/pl_PL.po
+++ b/po/pl_PL.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-14 22:15+0000\n"
+"POT-Creation-Date: 2026-06-17 23:07+0000\n"
 "PO-Revision-Date: 2026-04-17 00:22+0000\n"
 "Last-Translator: SkiffPL <skiff at windowslive.com>\n"
 "Language-Team: Polish <https://translations.scummvm.org/projects/scummvm/"
@@ -3406,7 +3406,8 @@ msgstr "Silnik graficzny gier przygodowych"
 #: backends/events/default/default-events.cpp:217
 #: backends/events/default/default-events.cpp:358
 #: engines/adl/metaengine.cpp:352 engines/buried/metaengine.cpp:154
-#: engines/chamber/metaengine.cpp:84 engines/drascula/metaengine.cpp:304
+#: engines/chamber/metaengine.cpp:84 engines/colony/intro.cpp:156
+#: engines/colony/intro.cpp:1243 engines/drascula/metaengine.cpp:304
 #: engines/hdb/metaengine.cpp:275 engines/hypno/metaengine.cpp:308
 #: engines/lab/metaengine.cpp:172 engines/mohawk/myst_stacks/menu.cpp:317
 #: engines/mohawk/riven_stacks/aspit.cpp:454
@@ -8162,6 +8163,21 @@ msgstr ""
 "Rzeczy zostan?? zepsute. Prosz?? rozwa??y?? rozpocz??cie Operacji Stealth od "
 "pocz??tku, korzystaj??c z nowych zapis??w stanu gry."
 
+#: engines/colony/intro.cpp:154 engines/colony/intro.cpp:1244
+#, fuzzy
+#| msgid "New game"
+msgid "New Game"
+msgstr "Nowa gra"
+
+#: engines/colony/intro.cpp:155 engines/colony/intro.cpp:1242
+msgid "Load Game"
+msgstr "Wczytaj gr??"
+
+#: engines/colony/intro.cpp:1219 engines/colony/intro.cpp:1244
+#, fuzzy
+msgid "You have been terminated."
+msgstr "Wzn??w"
+
 #: engines/colony/metaengine.cpp:45 engines/myst3/metaengine.cpp:40
 msgid "Widescreen mod"
 msgstr "Tryb szerokoekranowy"
@@ -18113,9 +18129,6 @@ msgstr ""
 #~ msgid "Save Game"
 #~ msgstr "Zapisz gr??"
 
-#~ msgid "Load Game"
-#~ msgstr "Wczytaj gr??"
-
 #~ msgid "Quit Game"
 #~ msgstr "Zamknij gr??"
 
@@ -18681,10 +18694,6 @@ msgstr ""
 #~ msgid "The game is now over.\n"
 #~ msgstr "Gra si?? sko??czy??a.\n"
 
-#, fuzzy
-#~ msgid "You have stored "
-#~ msgstr "Wzn??w"
-
 #, fuzzy
 #~ msgid "I've stored "
 #~ msgstr "Wzn??w"
diff --git a/po/pt_BR.po b/po/pt_BR.po
index cc4ca984941..8d16be32b68 100644
--- a/po/pt_BR.po
+++ b/po/pt_BR.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-14 22:15+0000\n"
+"POT-Creation-Date: 2026-06-17 23:07+0000\n"
 "PO-Revision-Date: 2026-06-17 22:10+0000\n"
 "Last-Translator: Marcel Souza Lemes <marcosoutsider at gmail.com>\n"
 "Language-Team: Portuguese (Brazil) <https://translations.scummvm.org/"
@@ -3485,7 +3485,8 @@ msgstr "Engine de jogos de aventura"
 #: backends/events/default/default-events.cpp:217
 #: backends/events/default/default-events.cpp:358
 #: engines/adl/metaengine.cpp:352 engines/buried/metaengine.cpp:154
-#: engines/chamber/metaengine.cpp:84 engines/drascula/metaengine.cpp:304
+#: engines/chamber/metaengine.cpp:84 engines/colony/intro.cpp:156
+#: engines/colony/intro.cpp:1243 engines/drascula/metaengine.cpp:304
 #: engines/hdb/metaengine.cpp:275 engines/hypno/metaengine.cpp:308
 #: engines/lab/metaengine.cpp:172 engines/mohawk/myst_stacks/menu.cpp:317
 #: engines/mohawk/riven_stacks/aspit.cpp:454
@@ -8572,6 +8573,22 @@ msgstr ""
 "favor, considere iniciar Operation Stealth do come??o, usando um novo jogo "
 "salvo."
 
+#: engines/colony/intro.cpp:154 engines/colony/intro.cpp:1244
+#, fuzzy
+#| msgid "New game"
+msgid "New Game"
+msgstr "Novo jogo"
+
+#: engines/colony/intro.cpp:155 engines/colony/intro.cpp:1242
+msgid "Load Game"
+msgstr "Carregar Jogo"
+
+#: engines/colony/intro.cpp:1219 engines/colony/intro.cpp:1244
+#, fuzzy
+#| msgid "You have stored "
+msgid "You have been terminated."
+msgstr "Voc?? guardou "
+
 #: engines/colony/metaengine.cpp:45 engines/myst3/metaengine.cpp:40
 msgid "Widescreen mod"
 msgstr "Mod de Widescreen"
@@ -19253,9 +19270,6 @@ msgstr ""
 #~ msgid "Save Game"
 #~ msgstr "Salvar Jogo"
 
-#~ msgid "Load Game"
-#~ msgstr "Carregar Jogo"
-
 #~ msgid "Quit Game"
 #~ msgstr "Sair do Jogo"
 
@@ -20255,9 +20269,6 @@ msgstr ""
 #~ msgid "The game is now over.\n"
 #~ msgstr "O jogo acabou.\n"
 
-#~ msgid "You have stored "
-#~ msgstr "Voc?? guardou "
-
 #~ msgid "I've stored "
 #~ msgstr "Eu guardei "
 
diff --git a/po/pt_PT.po b/po/pt_PT.po
index 66fc16db4e6..a0ba10f268d 100644
--- a/po/pt_PT.po
+++ b/po/pt_PT.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.10.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-14 22:15+0000\n"
+"POT-Creation-Date: 2026-06-17 23:07+0000\n"
 "PO-Revision-Date: 2022-06-05 14:09+0000\n"
 "Last-Translator: Daniel <supsuper at gmail.com>\n"
 "Language-Team: Portuguese (Portugal) <https://translations.scummvm.org/"
@@ -3373,7 +3373,8 @@ msgstr ""
 #: backends/events/default/default-events.cpp:217
 #: backends/events/default/default-events.cpp:358
 #: engines/adl/metaengine.cpp:352 engines/buried/metaengine.cpp:154
-#: engines/chamber/metaengine.cpp:84 engines/drascula/metaengine.cpp:304
+#: engines/chamber/metaengine.cpp:84 engines/colony/intro.cpp:156
+#: engines/colony/intro.cpp:1243 engines/drascula/metaengine.cpp:304
 #: engines/hdb/metaengine.cpp:275 engines/hypno/metaengine.cpp:308
 #: engines/lab/metaengine.cpp:172 engines/mohawk/myst_stacks/menu.cpp:317
 #: engines/mohawk/riven_stacks/aspit.cpp:454
@@ -7967,6 +7968,22 @@ msgid ""
 "beginning using new savegames."
 msgstr ""
 
+#: engines/colony/intro.cpp:154 engines/colony/intro.cpp:1244
+#, fuzzy
+#| msgid "New game"
+msgid "New Game"
+msgstr "Novo jogo"
+
+#: engines/colony/intro.cpp:155 engines/colony/intro.cpp:1242
+msgid "Load Game"
+msgstr "Carregar Jogo"
+
+#: engines/colony/intro.cpp:1219 engines/colony/intro.cpp:1244
+#, fuzzy
+#| msgid "You have stored "
+msgid "You have been terminated."
+msgstr "Guardaste "
+
 #: engines/colony/metaengine.cpp:45 engines/myst3/metaengine.cpp:40
 #, fuzzy
 msgid "Widescreen mod"
@@ -18277,9 +18294,6 @@ msgstr ""
 #~ msgid "Save Game"
 #~ msgstr "Guardar Jogo"
 
-#~ msgid "Load Game"
-#~ msgstr "Carregar Jogo"
-
 #~ msgid "Quit Game"
 #~ msgstr "Sair do Jogo"
 
@@ -18735,9 +18749,6 @@ msgstr ""
 #~ msgid "The game is now over.\n"
 #~ msgstr "O jogo acabou.\n"
 
-#~ msgid "You have stored "
-#~ msgstr "Guardaste "
-
 #~ msgid "I've stored "
 #~ msgstr "Guardei "
 
diff --git a/po/ro.po b/po/ro.po
index 6ebd335643a..a60c21c01ef 100644
--- a/po/ro.po
+++ b/po/ro.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.10.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-14 22:15+0000\n"
+"POT-Creation-Date: 2026-06-17 23:07+0000\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: Automatically generated\n"
 "Language-Team: none\n"
@@ -3163,7 +3163,8 @@ msgstr ""
 #: backends/events/default/default-events.cpp:217
 #: backends/events/default/default-events.cpp:358
 #: engines/adl/metaengine.cpp:352 engines/buried/metaengine.cpp:154
-#: engines/chamber/metaengine.cpp:84 engines/drascula/metaengine.cpp:304
+#: engines/chamber/metaengine.cpp:84 engines/colony/intro.cpp:156
+#: engines/colony/intro.cpp:1243 engines/drascula/metaengine.cpp:304
 #: engines/hdb/metaengine.cpp:275 engines/hypno/metaengine.cpp:308
 #: engines/lab/metaengine.cpp:172 engines/mohawk/myst_stacks/menu.cpp:317
 #: engines/mohawk/riven_stacks/aspit.cpp:454
@@ -7366,6 +7367,18 @@ msgid ""
 "beginning using new savegames."
 msgstr ""
 
+#: engines/colony/intro.cpp:154 engines/colony/intro.cpp:1244
+msgid "New Game"
+msgstr ""
+
+#: engines/colony/intro.cpp:155 engines/colony/intro.cpp:1242
+msgid "Load Game"
+msgstr ""
+
+#: engines/colony/intro.cpp:1219 engines/colony/intro.cpp:1244
+msgid "You have been terminated."
+msgstr ""
+
 #: engines/colony/metaengine.cpp:45 engines/myst3/metaengine.cpp:40
 msgid "Widescreen mod"
 msgstr ""
diff --git a/po/ru_RU.po b/po/ru_RU.po
index 65d5f8b0211..5a4aff34575 100644
--- a/po/ru_RU.po
+++ b/po/ru_RU.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.8.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-14 22:15+0000\n"
+"POT-Creation-Date: 2026-06-17 23:07+0000\n"
 "PO-Revision-Date: 2026-06-17 23:04+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Russian <https://translations.scummvm.org/projects/scummvm/"
@@ -2364,7 +2364,8 @@ msgstr "???????????????? ?????????????? ?????????????????? ?? ??????????????"
 
 #: gui/options.cpp:2674
 msgid "Enable smooth, momentum-based scrolling in list widgets."
-msgstr "???????????????? ?????????????? ?????????????????? ?? ???????????????????????????? ?????????????? ?? ???????????????? ??????????????."
+msgstr ""
+"???????????????? ?????????????? ?????????????????? ?? ???????????????????????????? ?????????????? ?? ???????????????? ??????????????."
 
 #: gui/options.cpp:2680
 msgid "GUI language:"
@@ -3402,7 +3403,8 @@ msgstr "???????????? ?????????????????????? ??????????????????"
 #: backends/events/default/default-events.cpp:217
 #: backends/events/default/default-events.cpp:358
 #: engines/adl/metaengine.cpp:352 engines/buried/metaengine.cpp:154
-#: engines/chamber/metaengine.cpp:84 engines/drascula/metaengine.cpp:304
+#: engines/chamber/metaengine.cpp:84 engines/colony/intro.cpp:156
+#: engines/colony/intro.cpp:1243 engines/drascula/metaengine.cpp:304
 #: engines/hdb/metaengine.cpp:275 engines/hypno/metaengine.cpp:308
 #: engines/lab/metaengine.cpp:172 engines/mohawk/myst_stacks/menu.cpp:317
 #: engines/mohawk/riven_stacks/aspit.cpp:454
@@ -8157,6 +8159,22 @@ msgstr ""
 "???????? ?????????? ????????????????. ???????????????? ???????????????? ???????????? ???????????? ?? Operation Stealth ?? "
 "???????????? ????????????, ?? ???????????? ????????????????????????."
 
+#: engines/colony/intro.cpp:154 engines/colony/intro.cpp:1244
+#, fuzzy
+#| msgid "New game"
+msgid "New Game"
+msgstr "?????????? ????????????????????"
+
+#: engines/colony/intro.cpp:155 engines/colony/intro.cpp:1242
+msgid "Load Game"
+msgstr "?????????????????? ????????"
+
+#: engines/colony/intro.cpp:1219 engines/colony/intro.cpp:1244
+#, fuzzy
+#| msgid "You have stored "
+msgid "You have been terminated."
+msgstr "???? ?????????????? "
+
 #: engines/colony/metaengine.cpp:45 engines/myst3/metaengine.cpp:40
 msgid "Widescreen mod"
 msgstr "???????????????????????????? ??????"
@@ -17617,9 +17635,6 @@ msgstr ""
 #~ msgid "Save Game"
 #~ msgstr "?????????????????? ????????"
 
-#~ msgid "Load Game"
-#~ msgstr "?????????????????? ????????"
-
 #~ msgid "Quit Game"
 #~ msgstr "?????????? ???? ????????"
 
@@ -18311,9 +18326,6 @@ msgstr ""
 #~ msgid "The game is now over.\n"
 #~ msgstr "???????? ????????????????.\n"
 
-#~ msgid "You have stored "
-#~ msgstr "???? ?????????????? "
-
 #~ msgid "I've stored "
 #~ msgstr "?? ???????????? "
 
diff --git a/po/sv_SE.po b/po/sv_SE.po
index fba6b8af51c..79506ccf12f 100644
--- a/po/sv_SE.po
+++ b/po/sv_SE.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.5.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-14 22:15+0000\n"
+"POT-Creation-Date: 2026-06-17 23:07+0000\n"
 "PO-Revision-Date: 2026-04-19 13:22+0000\n"
 "Last-Translator: Daniel Nylander <po at danielnylander.se>\n"
 "Language-Team: Swedish <https://translations.scummvm.org/projects/scummvm/"
@@ -3400,7 +3400,8 @@ msgstr "Grafisk ??ventyrsspelmotor"
 #: backends/events/default/default-events.cpp:217
 #: backends/events/default/default-events.cpp:358
 #: engines/adl/metaengine.cpp:352 engines/buried/metaengine.cpp:154
-#: engines/chamber/metaengine.cpp:84 engines/drascula/metaengine.cpp:304
+#: engines/chamber/metaengine.cpp:84 engines/colony/intro.cpp:156
+#: engines/colony/intro.cpp:1243 engines/drascula/metaengine.cpp:304
 #: engines/hdb/metaengine.cpp:275 engines/hypno/metaengine.cpp:308
 #: engines/lab/metaengine.cpp:172 engines/mohawk/myst_stacks/menu.cpp:317
 #: engines/mohawk/riven_stacks/aspit.cpp:454
@@ -8135,6 +8136,21 @@ msgstr ""
 "format. Saker och ting kommer att vara trasiga. ??verv??g att starta Operation "
 "Stealth fr??n b??rjan med nya sparade spel."
 
+#: engines/colony/intro.cpp:154 engines/colony/intro.cpp:1244
+#, fuzzy
+#| msgid "New game"
+msgid "New Game"
+msgstr "Nytt spel"
+
+#: engines/colony/intro.cpp:155 engines/colony/intro.cpp:1242
+msgid "Load Game"
+msgstr "L??s in spel"
+
+#: engines/colony/intro.cpp:1219 engines/colony/intro.cpp:1244
+#, fuzzy
+msgid "You have been terminated."
+msgstr "Ladda"
+
 #: engines/colony/metaengine.cpp:45 engines/myst3/metaengine.cpp:40
 msgid "Widescreen mod"
 msgstr "Bredbildsmodifiering"
@@ -17664,9 +17680,6 @@ msgstr ""
 #~ msgid "Save Game"
 #~ msgstr "Spara spel"
 
-#~ msgid "Load Game"
-#~ msgstr "L??s in spel"
-
 #~ msgid "Quit Game"
 #~ msgstr "Avsluta spel"
 
@@ -18208,10 +18221,6 @@ msgstr ""
 #~ msgid "The game is now over.\n"
 #~ msgstr "Spelet ??r nu slut.\n"
 
-#, fuzzy
-#~ msgid "You have stored "
-#~ msgstr "Ladda"
-
 #, fuzzy
 #~ msgid "I've stored "
 #~ msgstr "Ladda"
diff --git a/po/tr.po b/po/tr.po
index b0b5e44fbb2..2bed0099aa2 100644
--- a/po/tr.po
+++ b/po/tr.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-14 22:15+0000\n"
+"POT-Creation-Date: 2026-06-17 23:07+0000\n"
 "PO-Revision-Date: 2024-10-10 12:33+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Turkish <https://translations.scummvm.org/projects/scummvm/"
@@ -3360,7 +3360,8 @@ msgstr ""
 #: backends/events/default/default-events.cpp:217
 #: backends/events/default/default-events.cpp:358
 #: engines/adl/metaengine.cpp:352 engines/buried/metaengine.cpp:154
-#: engines/chamber/metaengine.cpp:84 engines/drascula/metaengine.cpp:304
+#: engines/chamber/metaengine.cpp:84 engines/colony/intro.cpp:156
+#: engines/colony/intro.cpp:1243 engines/drascula/metaengine.cpp:304
 #: engines/hdb/metaengine.cpp:275 engines/hypno/metaengine.cpp:308
 #: engines/lab/metaengine.cpp:172 engines/mohawk/myst_stacks/menu.cpp:317
 #: engines/mohawk/riven_stacks/aspit.cpp:454
@@ -7811,6 +7812,21 @@ msgid ""
 "beginning using new savegames."
 msgstr ""
 
+#: engines/colony/intro.cpp:154 engines/colony/intro.cpp:1244
+#, fuzzy
+#| msgid "New Save"
+msgid "New Game"
+msgstr "Yeni Kay??t"
+
+#: engines/colony/intro.cpp:155 engines/colony/intro.cpp:1242
+#, fuzzy
+msgid "Load Game"
+msgstr "Oyun y??kle:"
+
+#: engines/colony/intro.cpp:1219 engines/colony/intro.cpp:1244
+msgid "You have been terminated."
+msgstr ""
+
 #: engines/colony/metaengine.cpp:45 engines/myst3/metaengine.cpp:40
 #, fuzzy
 msgid "Widescreen mod"
@@ -17392,10 +17408,6 @@ msgstr ""
 #~ msgid "Save Game"
 #~ msgstr "~O~yunu kald??r"
 
-#, fuzzy
-#~ msgid "Load Game"
-#~ msgstr "Oyun y??kle:"
-
 #, fuzzy
 #~ msgid "Quit Game"
 #~ msgstr "Oyun"
diff --git a/po/uk_UA.po b/po/uk_UA.po
index 0ff1d058119..d90a96ce8c7 100644
--- a/po/uk_UA.po
+++ b/po/uk_UA.po
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.9.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-14 22:15+0000\n"
+"POT-Creation-Date: 2026-06-17 23:07+0000\n"
 "PO-Revision-Date: 2026-06-17 23:04+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Ukrainian <https://translations.scummvm.org/projects/scummvm/"
@@ -3400,7 +3400,8 @@ msgstr "???????????? ?????????????????? ??????????????"
 #: backends/events/default/default-events.cpp:217
 #: backends/events/default/default-events.cpp:358
 #: engines/adl/metaengine.cpp:352 engines/buried/metaengine.cpp:154
-#: engines/chamber/metaengine.cpp:84 engines/drascula/metaengine.cpp:304
+#: engines/chamber/metaengine.cpp:84 engines/colony/intro.cpp:156
+#: engines/colony/intro.cpp:1243 engines/drascula/metaengine.cpp:304
 #: engines/hdb/metaengine.cpp:275 engines/hypno/metaengine.cpp:308
 #: engines/lab/metaengine.cpp:172 engines/mohawk/myst_stacks/menu.cpp:317
 #: engines/mohawk/riven_stacks/aspit.cpp:454
@@ -8138,6 +8139,22 @@ msgstr ""
 "?????? ???? ???????? ?????????????????? ??????????. ????????-??????????, ?????????????????? ?????????????????? ?????? Operation "
 "Stealth ?? ???????????? ??????????????, ???????????????????????????? ???????? ?????????????????? ????????."
 
+#: engines/colony/intro.cpp:154 engines/colony/intro.cpp:1244
+#, fuzzy
+#| msgid "New game"
+msgid "New Game"
+msgstr "???????? ??????"
+
+#: engines/colony/intro.cpp:155 engines/colony/intro.cpp:1242
+msgid "Load Game"
+msgstr "?????????????????????? ?????????????????? ??????"
+
+#: engines/colony/intro.cpp:1219 engines/colony/intro.cpp:1244
+#, fuzzy
+#| msgid "You have stored "
+msgid "You have been terminated."
+msgstr "???? ?????????????? "
+
 #: engines/colony/metaengine.cpp:45 engines/myst3/metaengine.cpp:40
 msgid "Widescreen mod"
 msgstr "?????? ?????? ???????????????? ????????????"
@@ -12037,7 +12054,8 @@ msgstr "?????????????????? ?????????????????????????? ?????? ???????????????? ??
 
 #: engines/private/metaengine.cpp:62
 msgid "Enhance small printed paper close-ups for improved readability."
-msgstr "?????????????????? ?????????????????????????? ?????????????? ???????????????????????? ???????????????????? ?????????????? ????????????."
+msgstr ""
+"?????????????????? ?????????????????????????? ?????????????? ???????????????????????? ???????????????????? ?????????????? ????????????."
 
 #: engines/private/metaengine.cpp:147 engines/trecision/metaengine.cpp:137
 msgid "Skip video"
@@ -17536,9 +17554,6 @@ msgstr ""
 #~ msgid "Save Game"
 #~ msgstr "???????????????? ??????"
 
-#~ msgid "Load Game"
-#~ msgstr "?????????????????????? ?????????????????? ??????"
-
 #~ msgid "Quit Game"
 #~ msgstr "?????????? ?? ????????"
 
@@ -18230,9 +18245,6 @@ msgstr ""
 #~ msgid "The game is now over.\n"
 #~ msgstr "???? ?????? ??????????????????????.\n"
 
-#~ msgid "You have stored "
-#~ msgstr "???? ?????????????? "
-
 #~ msgid "I've stored "
 #~ msgstr "?? ???????????????? "
 
diff --git a/po/zh.po b/po/zh.po
index 4bd58d305e6..2dcdf1b1685 100644
--- a/po/zh.po
+++ b/po/zh.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-14 22:15+0000\n"
+"POT-Creation-Date: 2026-06-17 23:07+0000\n"
 "PO-Revision-Date: 2024-05-14 12:42+0000\n"
 "Last-Translator: ?????? <clonewith at qq.com>\n"
 "Language-Team: Chinese <https://translations.scummvm.org/projects/scummvm/"
@@ -3208,7 +3208,8 @@ msgstr ""
 #: backends/events/default/default-events.cpp:217
 #: backends/events/default/default-events.cpp:358
 #: engines/adl/metaengine.cpp:352 engines/buried/metaengine.cpp:154
-#: engines/chamber/metaengine.cpp:84 engines/drascula/metaengine.cpp:304
+#: engines/chamber/metaengine.cpp:84 engines/colony/intro.cpp:156
+#: engines/colony/intro.cpp:1243 engines/drascula/metaengine.cpp:304
 #: engines/hdb/metaengine.cpp:275 engines/hypno/metaengine.cpp:308
 #: engines/lab/metaengine.cpp:172 engines/mohawk/myst_stacks/menu.cpp:317
 #: engines/mohawk/riven_stacks/aspit.cpp:454
@@ -7580,6 +7581,22 @@ msgid ""
 "beginning using new savegames."
 msgstr ""
 
+#: engines/colony/intro.cpp:154 engines/colony/intro.cpp:1244
+#, fuzzy
+#| msgid "Save Game"
+msgid "New Game"
+msgstr "????????????"
+
+#: engines/colony/intro.cpp:155 engines/colony/intro.cpp:1242
+#, fuzzy
+#| msgid "Load game:"
+msgid "Load Game"
+msgstr "???????????????"
+
+#: engines/colony/intro.cpp:1219 engines/colony/intro.cpp:1244
+msgid "You have been terminated."
+msgstr ""
+
 #: engines/colony/metaengine.cpp:45 engines/myst3/metaengine.cpp:40
 msgid "Widescreen mod"
 msgstr ""
@@ -17084,9 +17101,6 @@ msgstr ""
 #~ msgid "N"
 #~ msgstr "???"
 
-#~ msgid "Load game:"
-#~ msgstr "???????????????"
-
 #, fuzzy
 #~| msgid "Backend"
 #~ msgid "Move Forward"
@@ -17297,11 +17311,6 @@ msgstr ""
 #~ msgid "Move Backwards"
 #~ msgstr "??????"
 
-#, fuzzy
-#~| msgid "Save Game"
-#~ msgid "Pause Game"
-#~ msgstr "????????????"
-
 #, fuzzy
 #~| msgid "Show hidden files"
 #~ msgid "Options Dialog"
diff --git a/po/zh_Hans.po b/po/zh_Hans.po
index deee04770ca..a9521474bf3 100644
--- a/po/zh_Hans.po
+++ b/po/zh_Hans.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.9.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-14 22:15+0000\n"
+"POT-Creation-Date: 2026-06-17 23:07+0000\n"
 "PO-Revision-Date: 2024-12-01 22:18+0000\n"
 "Last-Translator: ?????? <clonewith at qq.com>\n"
 "Language-Team: Chinese (Simplified Han script) <https://"
@@ -3379,7 +3379,8 @@ msgstr "????????????????????????"
 #: backends/events/default/default-events.cpp:217
 #: backends/events/default/default-events.cpp:358
 #: engines/adl/metaengine.cpp:352 engines/buried/metaengine.cpp:154
-#: engines/chamber/metaengine.cpp:84 engines/drascula/metaengine.cpp:304
+#: engines/chamber/metaengine.cpp:84 engines/colony/intro.cpp:156
+#: engines/colony/intro.cpp:1243 engines/drascula/metaengine.cpp:304
 #: engines/hdb/metaengine.cpp:275 engines/hypno/metaengine.cpp:308
 #: engines/lab/metaengine.cpp:172 engines/mohawk/myst_stacks/menu.cpp:317
 #: engines/mohawk/riven_stacks/aspit.cpp:454
@@ -8290,6 +8291,20 @@ msgstr ""
 "??????????????????????????????????????????????????????????????????????????????????????????????????????????????????"
 "???????????????????????????????????????"
 
+#: engines/colony/intro.cpp:154 engines/colony/intro.cpp:1244
+#, fuzzy
+#| msgid "New game"
+msgid "New Game"
+msgstr "?????????"
+
+#: engines/colony/intro.cpp:155 engines/colony/intro.cpp:1242
+msgid "Load Game"
+msgstr "????????????"
+
+#: engines/colony/intro.cpp:1219 engines/colony/intro.cpp:1244
+msgid "You have been terminated."
+msgstr ""
+
 #: engines/colony/metaengine.cpp:45 engines/myst3/metaengine.cpp:40
 msgid "Widescreen mod"
 msgstr "????????????"
@@ -18650,9 +18665,6 @@ msgstr ""
 #~ msgid "Save Game"
 #~ msgstr "????????????"
 
-#~ msgid "Load Game"
-#~ msgstr "????????????"
-
 #~ msgid "Quit Game"
 #~ msgstr "????????????"
 
diff --git a/po/zh_Hant.po b/po/zh_Hant.po
index 33c3b36aa2a..c1e11f3aef0 100644
--- a/po/zh_Hant.po
+++ b/po/zh_Hant.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.9.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-14 22:15+0000\n"
+"POT-Creation-Date: 2026-06-17 23:07+0000\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: Automatically generated\n"
 "Language-Team: none\n"
@@ -3164,7 +3164,8 @@ msgstr ""
 #: backends/events/default/default-events.cpp:217
 #: backends/events/default/default-events.cpp:358
 #: engines/adl/metaengine.cpp:352 engines/buried/metaengine.cpp:154
-#: engines/chamber/metaengine.cpp:84 engines/drascula/metaengine.cpp:304
+#: engines/chamber/metaengine.cpp:84 engines/colony/intro.cpp:156
+#: engines/colony/intro.cpp:1243 engines/drascula/metaengine.cpp:304
 #: engines/hdb/metaengine.cpp:275 engines/hypno/metaengine.cpp:308
 #: engines/lab/metaengine.cpp:172 engines/mohawk/myst_stacks/menu.cpp:317
 #: engines/mohawk/riven_stacks/aspit.cpp:454
@@ -7367,6 +7368,18 @@ msgid ""
 "beginning using new savegames."
 msgstr ""
 
+#: engines/colony/intro.cpp:154 engines/colony/intro.cpp:1244
+msgid "New Game"
+msgstr ""
+
+#: engines/colony/intro.cpp:155 engines/colony/intro.cpp:1242
+msgid "Load Game"
+msgstr ""
+
+#: engines/colony/intro.cpp:1219 engines/colony/intro.cpp:1244
+msgid "You have been terminated."
+msgstr ""
+
 #: engines/colony/metaengine.cpp:45 engines/myst3/metaengine.cpp:40
 msgid "Widescreen mod"
 msgstr ""



From noreply at scummvm.org  Wed Jun 17 23:08:42 2026
From: noreply at scummvm.org (sev-)
Date: Wed, 17 Jun 2026 23:08:42 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 172ee997cd337a8629180bc8e86641395b1e6342
Message-ID: <20260617230842.857D07005BC@ron.scummvm.net>

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

Summary:
172ee997cd DISTS: Regeenerate mm.dat


Commit: 172ee997cd337a8629180bc8e86641395b1e6342
    https://github.com/scummvm/scummvm/commit/172ee997cd337a8629180bc8e86641395b1e6342
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2026-06-18T01:08:34+02:00

Commit Message:
DISTS: Regeenerate mm.dat

Changed paths:
    dists/engine-data/mm.dat


diff --git a/dists/engine-data/mm.dat b/dists/engine-data/mm.dat
index a85da34b942..e9060e43b7c 100644
Binary files a/dists/engine-data/mm.dat and b/dists/engine-data/mm.dat differ



From noreply at scummvm.org  Wed Jun 17 23:50:13 2026
From: noreply at scummvm.org (bluegr)
Date: Wed, 17 Jun 2026 23:50:13 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 099f0adf560b78a3e5bda24ad995a855a16bc377
Message-ID: <20260617235013.95A737005BC@ron.scummvm.net>

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:
8e4d59608a NANCY: Limit the mouse cursor movement inside popups in Nancy10+
099f0adf56 NANCY: Don't clear generic flags when saving


Commit: 8e4d59608a51db3745b4eb482640acece20b3319
    https://github.com/scummvm/scummvm/commit/8e4d59608a51db3745b4eb482640acece20b3319
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-06-18T02:50:00+03:00

Commit Message:
NANCY: Limit the mouse cursor movement inside popups in Nancy10+

Changed paths:
    engines/nancy/state/scene.cpp
    engines/nancy/state/scene.h


diff --git a/engines/nancy/state/scene.cpp b/engines/nancy/state/scene.cpp
index f3f856c11ce..5e0a440b034 100644
--- a/engines/nancy/state/scene.cpp
+++ b/engines/nancy/state/scene.cpp
@@ -1133,6 +1133,17 @@ void Scene::run() {
 	}
 }
 
+Common::Rect Scene::activePopupConfinement() const {
+	// Pick the first visible Nancy 10+ popup; if more than one is open
+	// (shouldn't normally happen) the priority order matches the input
+	// order ??? conversation, inventory, notebook, cellphone.
+	if (_conversationPopup.isVisible()) return _conversationPopup.getScreenPosition();
+	if (_inventoryPopup.isVisible())    return _inventoryPopup.getScreenPosition();
+	if (_notebookPopup.isVisible())     return _notebookPopup.getScreenPosition();
+	if (_cellPhonePopup.isVisible())    return _cellPhonePopup.getScreenPosition();
+	return Common::Rect();
+}
+
 void Scene::handleInput() {
 	NancyInput input = g_nancy->_input->getInput();
 
@@ -1170,6 +1181,16 @@ void Scene::handleInput() {
 	// the popup that overlapped the textbox area could accidentally pick
 	// a conversation response.
 	if (g_nancy->getGameType() >= kGameTypeNancy10) {
+		// Confine the cursor to whichever popup is open so the player
+		// can't drag it into the underlying scene UI.
+		const Common::Rect confine = activePopupConfinement();
+		if (!confine.isEmpty() && !confine.contains(input.mousePos)) {
+			input.mousePos.x = CLIP<int16>(input.mousePos.x,
+											confine.left, confine.right - 1);
+			input.mousePos.y = CLIP<int16>(input.mousePos.y,
+											confine.top, confine.bottom - 1);
+			g_nancy->_cursor->warpCursor(input.mousePos);
+		}
 		_conversationPopup.handleInput(input);
 		_inventoryPopup.handleInput(input);
 		_notebookPopup.handleInput(input);
diff --git a/engines/nancy/state/scene.h b/engines/nancy/state/scene.h
index 0a580ab87dc..973ec2c28ff 100644
--- a/engines/nancy/state/scene.h
+++ b/engines/nancy/state/scene.h
@@ -238,6 +238,9 @@ private:
 	void run();
 	void handleInput();
 
+	// Rect of the open Nancy 10+ taskbar popup, or empty if none.
+	Common::Rect activePopupConfinement() const;
+
 	void initStaticData();
 
 	void clearSceneData();


Commit: 099f0adf560b78a3e5bda24ad995a855a16bc377
    https://github.com/scummvm/scummvm/commit/099f0adf560b78a3e5bda24ad995a855a16bc377
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-06-18T02:50:02+03:00

Commit Message:
NANCY: Don't clear generic flags when saving

This change was introduced in commit a97ee89. However, it has the
unwanted side effect of clearing generic flags when saving, e.g. when
the game auto-saves for a "second chance" save.

As an example, this led to the phone not being answered in Nancy 9
(bug #16799), because the game was trying to create a second chance
saved game while the phone rang. The phone normally opens when the
ringing sound starts, which sets flag 20. However, when the game tried
to auto-save, all the generic scene flags, including flag 20, were
incorrectly reset.

We fix this unwanted behavior by only clearing generic flags when
loading a save - saving a game shouldn't reset scene flags.

Fix #16799

Changed paths:
    engines/nancy/state/scene.cpp


diff --git a/engines/nancy/state/scene.cpp b/engines/nancy/state/scene.cpp
index 5e0a440b034..2c268d40fdd 100644
--- a/engines/nancy/state/scene.cpp
+++ b/engines/nancy/state/scene.cpp
@@ -750,9 +750,11 @@ void Scene::synchronize(Common::Serializer &ser) {
 
 	ser.syncArray(_flags.eventFlags.data(), g_nancy->getStaticData().numEventFlags, Common::Serializer::Byte);
 
-	// Clear generic flags
-	for (uint16 id : g_nancy->getStaticData().genericEventFlags) {
-		_flags.eventFlags[id] = g_nancy->_false;
+	if (!ser.isSaving()) {
+		// Clear generic flags
+		for (uint16 id : g_nancy->getStaticData().genericEventFlags) {
+			_flags.eventFlags[id] = g_nancy->_false;
+		}
 	}
 
 	// Skip empty sceneCount array



From noreply at scummvm.org  Thu Jun 18 01:16:36 2026
From: noreply at scummvm.org (bluegr)
Date: Thu, 18 Jun 2026 01:16:36 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 2eb7929d5ee56b292749854bf12ff683f5f3eb7f
Message-ID: <20260618011636.AA73C7005CC@ron.scummvm.net>

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

Summary:
6822c3496c NANCY: Fix signed/unsigned warnings
59041cbaf0 NANCY: Remove unused variable
0d0fdeab6d NANCY: Fix cursor mapping in Nancy10+
2eb7929d5e NANCY: Fix top bar display in cellphone sub-screens in Nancy10+


Commit: 6822c3496cbafc29180ed3c92a627bdb06291768
    https://github.com/scummvm/scummvm/commit/6822c3496cbafc29180ed3c92a627bdb06291768
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-06-18T03:52:07+03:00

Commit Message:
NANCY: Fix signed/unsigned warnings

Introduced with commit 6bf575df250b31c454667a4bcd343ba7da5be5ca

Changed paths:
    engines/nancy/state/scene.h


diff --git a/engines/nancy/state/scene.h b/engines/nancy/state/scene.h
index 973ec2c28ff..8a685f55be8 100644
--- a/engines/nancy/state/scene.h
+++ b/engines/nancy/state/scene.h
@@ -137,7 +137,7 @@ public:
 	byte hasItem(int16 id) const;
 	byte getItemDisabledState(int16 id) const { return _flags.disabledItems[id]; }
 	void setItemDisabledState(int16 id, byte state) {
-		if (id < _flags.disabledItems.size())
+		if ((uint16)id < _flags.disabledItems.size())
 			_flags.disabledItems[id] = state;
 	}
 


Commit: 59041cbaf0e060b6be107d4ace22a58b37bf0fe2
    https://github.com/scummvm/scummvm/commit/59041cbaf0e060b6be107d4ace22a58b37bf0fe2
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-06-18T03:52:53+03:00

Commit Message:
NANCY: Remove unused variable

Changed paths:
    engines/nancy/action/puzzle/quizpuzzle.cpp


diff --git a/engines/nancy/action/puzzle/quizpuzzle.cpp b/engines/nancy/action/puzzle/quizpuzzle.cpp
index ffbd03a193c..6ab64370cc9 100644
--- a/engines/nancy/action/puzzle/quizpuzzle.cpp
+++ b/engines/nancy/action/puzzle/quizpuzzle.cpp
@@ -113,8 +113,6 @@ void QuizPuzzle::readDataOld(Common::SeekableReadStream &stream) {
 		_answerFlags[i] = stream.readSint16LE();
 	}
 
-	char textBuf[30];
-
 	_correctSound.readNormal(stream);
 	_correctText = readSubtitle(stream);
 


Commit: 0d0fdeab6d6b280928177baf77605c29e779e92c
    https://github.com/scummvm/scummvm/commit/0d0fdeab6d6b280928177baf77605c29e779e92c
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-06-18T03:58:38+03:00

Commit Message:
NANCY: Fix cursor mapping in Nancy10+

Changed paths:
    engines/nancy/cursor.h


diff --git a/engines/nancy/cursor.h b/engines/nancy/cursor.h
index 9fdbd8f1ae0..f5529ea98c1 100644
--- a/engines/nancy/cursor.h
+++ b/engines/nancy/cursor.h
@@ -75,14 +75,14 @@ public:
 		kNewNormalArrow			= 8,	// Type 4  ??? Taskbar arrow
 		kNewHotspotArrow		= 9,	// Type 4  hotspot
 		kNewExit 				= 10,	// Type 5  ??? Exit / back movement
-		kNewRotateCW 			= 12,	// Type 6  ??? Puzzle rotation
-		kNewRotateCCW 			= 14,	// Type 7  ??? Puzzle rotation
-		kNewMoveLeft 			= 16,	// Type 8  ??? Movement / 360 turn
-		kNewMoveRight 			= 18,	// Type 9  ??? Movement / 360 turn
-		kNewMoveForward			= 20,	// Type 10 ??? Movement
-		kNewMoveBackward		= 22,	// Type 11 ??? Movement / exit puzzles
-		kNewMoveUp				= 24,	// Type 12 ??? Movement
-		kNewMoveDown			= 26,	// Type 13 ??? Movement
+		kNewMoveLeft 			= 12,	// Type 6  ??? Movement / 360 turn
+		kNewMoveRight 			= 14,	// Type 7  ??? Movement / 360 turn
+		kNewMoveForward			= 16,	// Type 8  ??? Movement
+		kNewMoveBackward		= 18,	// Type 9  ??? Movement / exit puzzles
+		kNewMoveUp				= 20,	// Type 10 ??? Movement
+		kNewMoveDown			= 22,	// Type 11 ??? Movement
+		kNewRotateCW 			= 24,	// Type 12 ??? Puzzle rotation
+		kNewRotateCCW 			= 26,	// Type 13 ??? Puzzle rotation
 		kNewRotateRight			= 28,	// Type 14 ??? 360 scenes
 		kNewRotateLeft			= 30,	// Type 15 ??? 360 scenes
 		kNewInvertedRotateRight = 32,	// Type 16 ??? Inverted 360 rotation


Commit: 2eb7929d5ee56b292749854bf12ff683f5f3eb7f
    https://github.com/scummvm/scummvm/commit/2eb7929d5ee56b292749854bf12ff683f5f3eb7f
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-06-18T04:01:08+03:00

Commit Message:
NANCY: Fix top bar display in cellphone sub-screens in Nancy10+

Changed paths:
    engines/nancy/ui/cellphonepopup.cpp
    engines/nancy/ui/cellphonepopup.h


diff --git a/engines/nancy/ui/cellphonepopup.cpp b/engines/nancy/ui/cellphonepopup.cpp
index a54f29f84a6..98545399992 100644
--- a/engines/nancy/ui/cellphonepopup.cpp
+++ b/engines/nancy/ui/cellphonepopup.cpp
@@ -410,14 +410,16 @@ void CellPhonePopup::drawChrome() {
 			: _uiclData->header.normalSrcRect;
 	_drawSurface.blitFrom(_overlayImage, chromeSrc, Common::Point(0, 0));
 	drawCloseButton(_closeButtonHovered ? 1 : 0);
-	drawHelpButton(0);
+	if (!isSubScreenState()) {
+		drawHelpButton(0);
+	}
 	_needsRedraw = true;
 }
 
 void CellPhonePopup::drawScreenContent() {
 	drawChrome();
 
-	if (_screenState != kConnected) {
+	if (_screenState != kConnected && !isSubScreenState()) {
 		drawStatusIcons();
 	}
 
@@ -1511,9 +1513,10 @@ void CellPhonePopup::handleInput(NancyInput &input) {
 		drawScreenContent();
 	}
 
-	// Help "?" button: opens the help page in the content view. Reachable
-	// from any interactive state (except when already showing it).
-	if (!_uiclData->helpButton.destRect.isEmpty() && !_uiclData->helpTextKey.empty() &&
+	// Help "?" button: opens the help page in the content view. Hidden
+	// (and unclickable) on sub-screens that already show their own heading.
+	if (!isSubScreenState() &&
+			!_uiclData->helpButton.destRect.isEmpty() && !_uiclData->helpTextKey.empty() &&
 			!(_screenState == kContentView && _contentKey == _uiclData->helpTextKey) &&
 			_uiclData->helpButton.destRect.contains(chunkMouse)) {
 		g_nancy->_cursor->setCursorType(CursorManager::kHotspotArrow);
diff --git a/engines/nancy/ui/cellphonepopup.h b/engines/nancy/ui/cellphonepopup.h
index f60f94a463c..46a8517fc96 100644
--- a/engines/nancy/ui/cellphonepopup.h
+++ b/engines/nancy/ui/cellphonepopup.h
@@ -138,6 +138,14 @@ private:
 				_screenState == kContentView;
 	}
 
+	// True for screens that hide the status icons and "?" button so the
+	// top bar shows only the section heading and the up arrow.
+	bool isSubScreenState() const {
+		return _screenState == kDirectory ||
+				_screenState == kOnlineHub ||
+				isZoomedChromeState();
+	}
+
 	void resetDialPad();
 	void enterScreenState(ScreenState newState);
 	void appendDigit(byte slotIndex);



From noreply at scummvm.org  Thu Jun 18 09:25:57 2026
From: noreply at scummvm.org (mduggan)
Date: Thu, 18 Jun 2026 09:25:57 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 2e00256424edea691a3831c07ed822c296649428
Message-ID: <20260618092557.5FE457005CC@ron.scummvm.net>

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

Summary:
2e00256424 ACCESS: Support multiple languages in Noct rerelease


Commit: 2e00256424edea691a3831c07ed822c296649428
    https://github.com/scummvm/scummvm/commit/2e00256424edea691a3831c07ed822c296649428
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2026-06-18T18:57:23+10:00

Commit Message:
ACCESS: Support multiple languages in Noct rerelease

Changed paths:
    engines/access/access.cpp
    engines/access/access.h
    engines/access/detection.cpp
    engines/access/detection_tables.h
    engines/access/metaengine.cpp
    engines/access/noctropolis/noctropolis_comicviewer.cpp
    engines/access/noctropolis/noctropolis_game.cpp
    engines/access/noctropolis/noctropolis_game.h
    engines/access/noctropolis/noctropolis_inventory.cpp
    engines/access/noctropolis/noctropolis_resources.cpp
    engines/access/noctropolis/noctropolis_resources.h
    engines/access/scripts.cpp


diff --git a/engines/access/access.cpp b/engines/access/access.cpp
index 9fb4964b70a..04c92b2ddcd 100644
--- a/engines/access/access.cpp
+++ b/engines/access/access.cpp
@@ -122,6 +122,8 @@ AccessEngine::AccessEngine(OSystem *syst, const AccessGameDescription *gameDesc)
 	_stilScaleOff = 0;
 
 	ARRAYCLEAR(_countTbl);
+
+	_lang = Common::UNK_LANG;
 }
 
 AccessEngine::~AccessEngine() {
@@ -212,8 +214,7 @@ void AccessEngine::initialize() {
 
 const SpriteResource *AccessEngine::getIcons() {
 	if (!_icons) {
-		const char *fname = (getGameID() == kGameNoctropolis) ? "ICONS.AP" : "ICONS.LZ";
-		Resource *iconData = _files->loadRawFile(fname);
+		Resource *iconData = _files->loadRawFile(getIconPath());
 		_icons = new SpriteResource(this, iconData);
 		delete iconData;
 	}
@@ -221,6 +222,11 @@ const SpriteResource *AccessEngine::getIcons() {
 }
 
 Common::Error AccessEngine::run() {
+	if (ConfMan.hasKey("language"))
+		_lang = Common::parseLanguage(ConfMan.get("language"));
+	else
+		_lang = getLanguage();
+
 	_res = Resources::init(this);
 	Common::U32String errorMessage;
 	if (!_res->load(errorMessage)) {
diff --git a/engines/access/access.h b/engines/access/access.h
index 0e2698252fc..6044f115658 100644
--- a/engines/access/access.h
+++ b/engines/access/access.h
@@ -147,6 +147,11 @@ private:
 	 */
 	SpriteResource *_icons;
 
+	/**
+	 * Lang to use for resources.
+	 */
+	Common::Language _lang;
+
 	/**
 	 * Handles basic initialization
 	 */
@@ -201,6 +206,11 @@ protected:
 	 */
 	virtual void setupGame() = 0;
 
+	/**
+	 * Get the path to the icons file
+	 */
+	virtual Common::Path getIconPath() const { return Common::Path("ICONS.LZ"); }
+
 public:
 	AnimationManager *_animation;
 	BubbleBox *_bubbleBox;
diff --git a/engines/access/detection.cpp b/engines/access/detection.cpp
index 9de6d795476..b73c1e2c5d8 100644
--- a/engines/access/detection.cpp
+++ b/engines/access/detection.cpp
@@ -65,6 +65,30 @@ public:
 	const DebugChannelDef *getDebugChannels() const override {
 		return debugFlagList;
 	}
+
+	DetectedGame toDetectedGame(const ADDetectedGame &adGame, ADDetectedGameExtraInfo *extraInfo) const override {
+		static const Common::Language NOCTROPOLIS_RERELEASE_LANGS[] = {
+			Common::EN_ANY,
+			Common::FR_FRA,
+			Common::DE_DEU,
+			Common::ES_ESP,
+		};
+		DetectedGame game = AdvancedMetaEngineDetection::toDetectedGame(adGame);
+
+		const Access::AccessGameDescription *desc = reinterpret_cast<const Access::AccessGameDescription *>(adGame.desc);
+		assert(desc);
+
+		if (desc && (desc->features & Access::FEATURE_NOCT_MULTI_LANG)) {
+			// The AdvancedDetector model only allows specifying a single supported
+			// game language. The Noctropolis re-release supports multiple languages.
+			for (const auto lang: NOCTROPOLIS_RERELEASE_LANGS) {
+				game.appendGUIOptions(Common::getGameGUIOptionsDescriptionLanguage(lang));
+			}
+		}
+
+		return game;
+	}
+
 };
 
 
diff --git a/engines/access/detection_tables.h b/engines/access/detection_tables.h
index cf9398ab7a1..b9513ac7d0f 100644
--- a/engines/access/detection_tables.h
+++ b/engines/access/detection_tables.h
@@ -21,6 +21,8 @@
 
 namespace Access {
 
+static const uint FEATURE_NOCT_MULTI_LANG = 1;
+
 static const AccessGameDescription gameDescriptions[] = {
 	{
 		// Amazon Guardians of Eden - Floppy English
@@ -163,7 +165,7 @@ static const AccessGameDescription gameDescriptions[] = {
 	},
 
 	{
-		// Noctropolis - sum of this file is the same in all known verisons
+		// Noctropolis - original release
 		{
 			"noctropolis",
 			nullptr,
@@ -179,13 +181,13 @@ static const AccessGameDescription gameDescriptions[] = {
 	},
 
 	{
-		// Noctropolis - GOG/Steam version
+		// Noctropolis - original release, German
 		{
 			"noctropolis",
-			"Rerelease",
+			nullptr,
 			AD_ENTRY2s("dark/scene01.ap", "3a154bf58e10cd7ace14cab1bf5adf4a", 147954,
-					   "dark/scene13.ap", "0d400713ed30e692d63af4d28ba42c5e", 327980),
-			Common::EN_ANY,
+					   "dark/scene13.ap", "261c65ea702c6088ab3ed6e39e217d64", 328430),
+			Common::DE_DEU,
 			Common::kPlatformWindows,
 			ADGF_TESTING,
 			GUIO1(GUIO_NONE)
@@ -194,6 +196,22 @@ static const AccessGameDescription gameDescriptions[] = {
 		0
 	},
 
+	{
+		// Noctropolis - GOG/Steam version (multi-language support)
+		{
+			"noctropolis",
+			"Rerelease",
+			AD_ENTRY2s("dark/scene01.ap", "3a154bf58e10cd7ace14cab1bf5adf4a", 147954,
+					   "dark/scene13.ap", "0d400713ed30e692d63af4d28ba42c5e", 327980),
+			Common::UNK_LANG,
+			Common::kPlatformWindows,
+			ADGF_TESTING,
+			GUIO1(GUIO_GAMEOPTIONS1)
+		},
+		kGameNoctropolis,
+		FEATURE_NOCT_MULTI_LANG
+	},
+
 	{ AD_TABLE_END_MARKER, 0, 0 }
 };
 
diff --git a/engines/access/metaengine.cpp b/engines/access/metaengine.cpp
index 4da9f8dedb9..309663aa331 100644
--- a/engines/access/metaengine.cpp
+++ b/engines/access/metaengine.cpp
@@ -41,6 +41,7 @@
 #include "common/translation.h"
 
 #define MAX_SAVES 99
+#define GAMEOPTION_OGG_MUSIC GUIO_GAMEOPTIONS1
 
 namespace Access {
 
@@ -65,7 +66,7 @@ bool AccessEngine::isDemo() const {
 }
 
 Common::Language AccessEngine::getLanguage() const {
-	return _gameDescription->desc.language;
+	return _lang;
 }
 
 Common::Platform AccessEngine::getPlatform() const {
@@ -74,6 +75,22 @@ Common::Platform AccessEngine::getPlatform() const {
 
 } // End of namespace Access
 
+static const ADExtraGuiOptionsMap optionsList[] = {
+	{
+		GAMEOPTION_OGG_MUSIC,
+		{
+			_s("Use 'high definition' OGG music"),
+			_s("Use the OGG audio from the re-release instead of original MIDI tracks"),
+			"ogg_music",
+			true,
+			0,
+			0
+		}
+	},
+	AD_EXTRA_GUI_OPTIONS_TERMINATOR
+};
+
+
 class AccessMetaEngine : public AdvancedMetaEngine<Access::AccessGameDescription> {
 public:
 	const char *getName() const override {
@@ -88,6 +105,11 @@ public:
 	SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const override;
 
 	Common::KeymapArray initKeymaps(const char *target) const override;
+
+	const ADExtraGuiOptionsMap *getAdvancedExtraGuiOptions() const override {
+		return optionsList;
+	}
+
 };
 
 bool AccessMetaEngine::hasFeature(MetaEngineFeature f) const {
@@ -201,7 +223,6 @@ SaveStateDescriptor AccessMetaEngine::querySaveMetaInfos(const char *target, int
 	return SaveStateDescriptor();
 }
 
-
 Common::KeymapArray AccessMetaEngine::initKeymaps(const char *target) const {
 	using namespace Common;
 	using namespace Access;
diff --git a/engines/access/noctropolis/noctropolis_comicviewer.cpp b/engines/access/noctropolis/noctropolis_comicviewer.cpp
index 30bc311df63..757c5631760 100644
--- a/engines/access/noctropolis/noctropolis_comicviewer.cpp
+++ b/engines/access/noctropolis/noctropolis_comicviewer.cpp
@@ -20,6 +20,7 @@
  */
 
 #include "access/noctropolis/noctropolis_comicviewer.h"
+#include "access/noctropolis/noctropolis_resources.h"
 
 namespace Access {
 
@@ -72,7 +73,7 @@ void ComicViewer::run(const ComicResource *comic) {
 PageResult ComicViewer::runPage(const ComicPage *page) {
 	PageResult result = kPageResultNone;
 
-	Common::Path pagePath(page->filename);
+	Common::Path pagePath = ((NoctropolisResources *)_vm->_res)->translatePath(page->filename);
 
 	if (!_vm->_files->existFile(pagePath)) {
 		// Happens in Demo 2 - no comic files
@@ -82,7 +83,8 @@ PageResult ComicViewer::runPage(const ComicPage *page) {
 
 	_vm->_files->loadScreen(pagePath);
 
-	Resource *bubbleData = _vm->_files->loadRawFile("COMDATA/comic.ap");
+	Common::Path bubbleDataPath = ((NoctropolisResources *)_vm->_res)->translatePath("DARK/COMDATA/comic.ap");
+	Resource *bubbleData = _vm->_files->loadRawFile(bubbleDataPath);
 	_bubbleSprites = new SpriteResource(_vm, bubbleData);
 	delete bubbleData;
 
diff --git a/engines/access/noctropolis/noctropolis_game.cpp b/engines/access/noctropolis/noctropolis_game.cpp
index 8e040bad117..eecb61287b8 100644
--- a/engines/access/noctropolis/noctropolis_game.cpp
+++ b/engines/access/noctropolis/noctropolis_game.cpp
@@ -72,6 +72,11 @@ void NoctropolisEngine::initObjects() {
 	}
 }
 
+Common::Path NoctropolisEngine::getIconPath() const {
+	return ((NoctropolisResources *)_res)->translatePath("DARK/ICONS.AP");
+}
+
+
 void NoctropolisEngine::setupGame() {
 	_timers.clear();
 	for (int i = 0; i < 32; ++i) {
diff --git a/engines/access/noctropolis/noctropolis_game.h b/engines/access/noctropolis/noctropolis_game.h
index badb490bc83..8ddf2729e70 100644
--- a/engines/access/noctropolis/noctropolis_game.h
+++ b/engines/access/noctropolis/noctropolis_game.h
@@ -106,6 +106,11 @@ protected:
 	*/
 	Common::Error synchronize(Common::Serializer &s) override;
 
+	/**
+	 * Get the path to the icons file
+	 */
+	Common::Path getIconPath() const override;
+
 private:
 	void doIntro();
 	void doFlashLogo();
diff --git a/engines/access/noctropolis/noctropolis_inventory.cpp b/engines/access/noctropolis/noctropolis_inventory.cpp
index 98b780cafc6..39a7d155614 100644
--- a/engines/access/noctropolis/noctropolis_inventory.cpp
+++ b/engines/access/noctropolis/noctropolis_inventory.cpp
@@ -20,6 +20,7 @@
  */
 
 #include "access/noctropolis/noctropolis_inventory.h"
+#include "access/noctropolis/noctropolis_resources.h"
 #include "access/access.h"
 #include "access/resources.h"
 #include "access/asurface.h"
@@ -60,8 +61,8 @@ int NoctropolisInventory::displayInv() {
 			g_system->warpMouse(warpMouseX, warpMouseY);
 	}
 
-	// TODO: Maybe move/load these globally?
-	Resource *iconData = _vm->_files->loadRawFile("INV.AP");
+	Common::Path path = ((NoctropolisResources *)_vm->_res)->translatePath("DARK/INV.AP");
+	Resource *iconData = _vm->_files->loadRawFile(path);
 	SpriteResource *inventorySprites = new SpriteResource(_vm, iconData);
 	delete iconData;
 
diff --git a/engines/access/noctropolis/noctropolis_resources.cpp b/engines/access/noctropolis/noctropolis_resources.cpp
index 43739415fd3..c3a1c708af0 100644
--- a/engines/access/noctropolis/noctropolis_resources.cpp
+++ b/engines/access/noctropolis/noctropolis_resources.cpp
@@ -1866,19 +1866,15 @@ static int _langOffset(Common::Language lang) {
 
 void NoctropolisResources::load(Common::SeekableReadStream &s) {
 	// Note: *don't* call the base class here. Noctropolis doesn't have data in access.dat.
-
-	// TODO: For non-EN variants we want to use something other than DARK/ as the path.
 	for (int i = 0; i < ARRAYSIZE(NOCT_FILES_1); i++) {
-		Common::Path filename = Common::Path(NOCT_FILES_1[i]).getLastComponent();
-		FILENAMES.push_back(filename);
+		FILENAMES.push_back(translatePath(NOCT_FILES_1[i]));
 	}
 	// TODO: These last few files are maybe only ever used from hard-coded points,
 	// so maybe we can just hard-code the names in those places and avoid this hack?
 	while (FILENAMES.size() < 256 - ARRAYSIZE(NOCT_FILES_2))
 		FILENAMES.push_back(Common::Path());
 	for (int i = 0; i < ARRAYSIZE(NOCT_FILES_2); i++) {
-		Common::Path filename = Common::Path(NOCT_FILES_2[i]).getLastComponent();
-		FILENAMES.push_back(filename);
+		FILENAMES.push_back(translatePath(NOCT_FILES_2[i]));
 	}
 
 	_fontChaleteu = new NoctropolisFont(0x66, 11, 1, 0xe2, CHALETEU_OFFSETS, CHALETEU_DATA);
@@ -2022,6 +2018,26 @@ void NoctropolisResources::load(Common::SeekableReadStream &s) {
 	}
 }
 
+Common::Path NoctropolisResources::translatePath(const char *rawPath) const {
+	Common::String path_prefix;
+	switch (_vm->getLanguage()) {
+	case Common::FR_FRA: path_prefix = "FR/"; break;
+	case Common::DE_DEU: path_prefix = "DE/"; break;
+	case Common::ES_ESP: path_prefix = "ES/"; break;
+	default: break;
+	}
+
+	Common::String path = path_prefix + rawPath;
+	if (_vm->isDemo())
+		path.replace(0, 4, "DEMO");  // replace DARK with DEMO.
+
+	if (!path_prefix.empty() && !SearchMan.hasFile(Common::Path(path)))
+		path = path.substr(path_prefix.size());
+
+	return Common::Path(path);
+}
+
+
 int NoctropolisResources::menuAt(int16 x, int16 y) const {
 	for (int i = 0; i < (int)_menus.size(); i++) {
 		if (Polygon::pointInside(_menus[i], x, y))
diff --git a/engines/access/noctropolis/noctropolis_resources.h b/engines/access/noctropolis/noctropolis_resources.h
index d496ab309a9..d3c2e3c6e72 100644
--- a/engines/access/noctropolis/noctropolis_resources.h
+++ b/engines/access/noctropolis/noctropolis_resources.h
@@ -74,6 +74,8 @@ public:
 	const ComicResource *getLastComicResource() const;
 	const ComicResource *getSpecialComicResource() const;
 
+	Common::Path translatePath(const char *rawPath) const;
+
 	int menuAt(int16 x, int16 y) const;
 
 private:
diff --git a/engines/access/scripts.cpp b/engines/access/scripts.cpp
index 1d9ed34f795..ceb4f757ef3 100644
--- a/engines/access/scripts.cpp
+++ b/engines/access/scripts.cpp
@@ -1532,7 +1532,8 @@ void Scripts::cmdDispAbout_v3() {
 	int selectedItem = -2;
 	bool needRedraw = true;
 
-	Resource *spriteData = _vm->_files->loadRawFile("ASK.AP");
+	Common::Path path = ((Noctropolis::NoctropolisResources *)_vm->_res)->translatePath("DARK/ASK.AP");
+	Resource *spriteData = _vm->_files->loadRawFile(path);
 	SpriteResource *askSprites = new SpriteResource(_vm, spriteData);
 	delete spriteData;
 



From noreply at scummvm.org  Thu Jun 18 09:26:02 2026
From: noreply at scummvm.org (ScummVM-Translations)
Date: Thu, 18 Jun 2026 09:26:02 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 56af3b3f29b417734d655280c764e21f8bb7ab2d
Message-ID: <20260618092602.3FF0A7005CF@ron.scummvm.net>

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

Summary:
56af3b3f29 I18N: Update translations templates


Commit: 56af3b3f29b417734d655280c764e21f8bb7ab2d
    https://github.com/scummvm/scummvm/commit/56af3b3f29b417734d655280c764e21f8bb7ab2d
Author: Weblate (noreply at weblate.org)
Date: 2026-06-18T09:25:58Z

Commit Message:
I18N: Update translations templates

Changed paths:
    po/scummvm.pot


diff --git a/po/scummvm.pot b/po/scummvm.pot
index f188b29b92a..fad71845a85 100644
--- a/po/scummvm.pot
+++ b/po/scummvm.pot
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2026.2.1git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-17 23:07+0000\n"
+"POT-Creation-Date: 2026-06-18 09:25+0000\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL at ADDRESS>\n"
 "Language-Team: LANGUAGE <LL at li.org>\n"
@@ -80,7 +80,7 @@ msgstr ""
 #. I18N: Opens the help screen.
 #: gui/browser.cpp:64 gui/launcher.cpp:277 gui/launcher.cpp:959
 #: backends/platform/sdl/macosx/appmenu_osx.mm:319
-#: engines/access/metaengine.cpp:360 engines/bladerunner/metaengine.cpp:321
+#: engines/access/metaengine.cpp:381 engines/bladerunner/metaengine.cpp:321
 #: engines/ngi/metaengine.cpp:162 engines/supernova/metaengine.cpp:238
 #: engines/tucker/metaengine.cpp:215 engines/zvision/metaengine.cpp:246
 msgid "Help"
@@ -2499,7 +2499,7 @@ msgid ""
 msgstr ""
 
 #: gui/options.cpp:2975 engines/engine.cpp:647
-#: engines/access/metaengine.cpp:298 engines/drascula/metaengine.cpp:255
+#: engines/access/metaengine.cpp:319 engines/drascula/metaengine.cpp:255
 #: engines/queen/metaengine.cpp:269 engines/sherlock/metaengine.cpp:410
 #: engines/ultima/nuvie/metaengine.cpp:71
 msgid "Move"
@@ -3687,7 +3687,7 @@ msgstr ""
 
 #. I18N: Skipping cutscene plaback
 #. I18N: Skipping cutscene playback
-#: engines/metaengine.cpp:112 engines/access/metaengine.cpp:225
+#: engines/metaengine.cpp:112 engines/access/metaengine.cpp:246
 #: engines/composer/metaengine.cpp:126 engines/drascula/metaengine.cpp:224
 #: engines/efh/metaengine.cpp:379 engines/freescape/movement.cpp:93
 #: engines/grim/grim.cpp:533 engines/grim/grim.cpp:633
@@ -5636,7 +5636,15 @@ msgstr ""
 msgid "Check for Updates..."
 msgstr ""
 
-#: engines/access/metaengine.cpp:215 engines/alcachofa/metaengine.cpp:94
+#: engines/access/metaengine.cpp:82
+msgid "Use 'high definition' OGG music"
+msgstr ""
+
+#: engines/access/metaengine.cpp:83
+msgid "Use the OGG audio from the re-release instead of original MIDI tracks"
+msgstr ""
+
+#: engines/access/metaengine.cpp:236 engines/alcachofa/metaengine.cpp:94
 #: engines/bbvs/metaengine.cpp:92 engines/buried/metaengine.cpp:133
 #: engines/chamber/metaengine.cpp:59 engines/cine/metaengine.cpp:270
 #: engines/cruise/metaengine.cpp:168 engines/dm/metaengine.cpp:124
@@ -5660,16 +5668,16 @@ msgstr ""
 msgid "Default keymappings"
 msgstr ""
 
-#: engines/access/metaengine.cpp:219
+#: engines/access/metaengine.cpp:240
 msgid "Move / Interact / Skip"
 msgstr ""
 
-#: engines/access/metaengine.cpp:231 engines/sherlock/metaengine.cpp:483
+#: engines/access/metaengine.cpp:252 engines/sherlock/metaengine.cpp:483
 msgid "Skip movie"
 msgstr ""
 
 #. I18N: Move Actor to upwards direction
-#: engines/access/metaengine.cpp:237 engines/asylum/metaengine.cpp:171
+#: engines/access/metaengine.cpp:258 engines/asylum/metaengine.cpp:171
 #: engines/asylum/metaengine.cpp:198 engines/cine/metaengine.cpp:377
 #: engines/efh/metaengine.cpp:286 engines/hdb/metaengine.cpp:213
 #: engines/hypno/metaengine.cpp:363 engines/hypno/metaengine.cpp:411
@@ -5679,7 +5687,7 @@ msgid "Move up"
 msgstr ""
 
 #. I18N: Move Actor to downwards direction
-#: engines/access/metaengine.cpp:244 engines/asylum/metaengine.cpp:177
+#: engines/access/metaengine.cpp:265 engines/asylum/metaengine.cpp:177
 #: engines/asylum/metaengine.cpp:205 engines/cine/metaengine.cpp:384
 #: engines/efh/metaengine.cpp:293 engines/hdb/metaengine.cpp:219
 #: engines/hypno/metaengine.cpp:351 engines/hypno/metaengine.cpp:399
@@ -5690,7 +5698,7 @@ msgstr ""
 
 #. I18N: Move Actor to left direction
 #. I18N: Move actor left during fight
-#: engines/access/metaengine.cpp:251 engines/asylum/metaengine.cpp:183
+#: engines/access/metaengine.cpp:272 engines/asylum/metaengine.cpp:183
 #: engines/asylum/metaengine.cpp:212 engines/cine/metaengine.cpp:391
 #: engines/efh/metaengine.cpp:300 engines/hdb/metaengine.cpp:225
 #: engines/hypno/metaengine.cpp:345 engines/hypno/metaengine.cpp:393
@@ -5703,7 +5711,7 @@ msgstr ""
 
 #. I18N: Move Actor to right direction
 #. I18N: Move actor right during fight
-#: engines/access/metaengine.cpp:258 engines/asylum/metaengine.cpp:189
+#: engines/access/metaengine.cpp:279 engines/asylum/metaengine.cpp:189
 #: engines/asylum/metaengine.cpp:219 engines/cine/metaengine.cpp:398
 #: engines/efh/metaengine.cpp:307 engines/hdb/metaengine.cpp:231
 #: engines/hypno/metaengine.cpp:357 engines/hypno/metaengine.cpp:405
@@ -5715,31 +5723,31 @@ msgid "Move right"
 msgstr ""
 
 #. I18N: Move Actor to top-left direction
-#: engines/access/metaengine.cpp:265 engines/cine/metaengine.cpp:405
+#: engines/access/metaengine.cpp:286 engines/cine/metaengine.cpp:405
 #: engines/efh/metaengine.cpp:314
 msgid "Move up-left"
 msgstr ""
 
 #. I18N: Move Actor to top-right direction
-#: engines/access/metaengine.cpp:270 engines/cine/metaengine.cpp:411
+#: engines/access/metaengine.cpp:291 engines/cine/metaengine.cpp:411
 #: engines/efh/metaengine.cpp:320
 msgid "Move up-right"
 msgstr ""
 
 #. I18N: Move Actor to bottom-left direction
-#: engines/access/metaengine.cpp:275 engines/cine/metaengine.cpp:417
+#: engines/access/metaengine.cpp:296 engines/cine/metaengine.cpp:417
 #: engines/efh/metaengine.cpp:326
 msgid "Move down-left"
 msgstr ""
 
 #. I18N: Move Actor to bottom-right direction
-#: engines/access/metaengine.cpp:280 engines/cine/metaengine.cpp:423
+#: engines/access/metaengine.cpp:301 engines/cine/metaengine.cpp:423
 #: engines/efh/metaengine.cpp:332
 msgid "Move down-right"
 msgstr ""
 
 #. I18N: (Game name: The Lost Files of Sherlock Holmes: The Case of the Rose Tattoo) This action is used to look at an object or npc in the game
-#: engines/access/metaengine.cpp:285 engines/bbvs/metaengine.cpp:124
+#: engines/access/metaengine.cpp:306 engines/bbvs/metaengine.cpp:124
 #: engines/dgds/metaengine.cpp:83 engines/drascula/metaengine.cpp:230
 #: engines/petka/metaengine.cpp:130 engines/scumm/help.cpp:167
 #: engines/scumm/help.cpp:231 engines/sherlock/metaengine.cpp:405
@@ -5750,7 +5758,7 @@ msgstr ""
 msgid "Look"
 msgstr ""
 
-#: engines/access/metaengine.cpp:293 engines/drascula/metaengine.cpp:240
+#: engines/access/metaengine.cpp:314 engines/drascula/metaengine.cpp:240
 #: engines/queen/metaengine.cpp:259 engines/saga/metaengine.cpp:369
 #: engines/scumm/help.cpp:125 engines/scumm/help.cpp:140
 #: engines/scumm/help.cpp:165 engines/scumm/help.cpp:193
@@ -5760,13 +5768,13 @@ msgstr ""
 msgid "Open"
 msgstr ""
 
-#: engines/access/metaengine.cpp:303 engines/scumm/help.cpp:128
+#: engines/access/metaengine.cpp:324 engines/scumm/help.cpp:128
 #: engines/ultima/nuvie/metaengine.cpp:70
 msgid "Get"
 msgstr ""
 
 #. I18N: Combat command in Might & Magic 1
-#: engines/access/metaengine.cpp:308 engines/access/metaengine.cpp:329
+#: engines/access/metaengine.cpp:329 engines/access/metaengine.cpp:350
 #: engines/bbvs/metaengine.cpp:136 engines/cine/metaengine.cpp:342
 #: engines/efh/metaengine.cpp:462 engines/hdb/metaengine.cpp:237
 #: engines/lab/metaengine.cpp:323 engines/mm/mm1/metaengine.cpp:86
@@ -5786,11 +5794,11 @@ msgstr ""
 msgid "Use"
 msgstr ""
 
-#: engines/access/metaengine.cpp:313
+#: engines/access/metaengine.cpp:334
 msgid "Goto"
 msgstr ""
 
-#: engines/access/metaengine.cpp:318 engines/access/metaengine.cpp:349
+#: engines/access/metaengine.cpp:339 engines/access/metaengine.cpp:370
 #: engines/bbvs/metaengine.cpp:130 engines/drascula/metaengine.cpp:250
 #: engines/efh/metaengine.cpp:506 engines/petka/metaengine.cpp:158
 #: engines/scumm/help.cpp:174 engines/scumm/help.cpp:226
@@ -5801,18 +5809,18 @@ msgstr ""
 msgid "Talk"
 msgstr ""
 
-#: engines/access/metaengine.cpp:323 engines/scumm/help.cpp:175
+#: engines/access/metaengine.cpp:344 engines/scumm/help.cpp:175
 msgid "Travel"
 msgstr ""
 
-#: engines/access/metaengine.cpp:334 engines/cine/metaengine.cpp:332
+#: engines/access/metaengine.cpp:355 engines/cine/metaengine.cpp:332
 #: engines/petka/metaengine.cpp:144 engines/saga/metaengine.cpp:394
 #: engines/supernova/metaengine.cpp:331
 msgid "Take"
 msgstr ""
 
 #. I18N: Action in In Cold Blood
-#: engines/access/metaengine.cpp:339 engines/alcachofa/metaengine.cpp:116
+#: engines/access/metaengine.cpp:360 engines/alcachofa/metaengine.cpp:116
 #: engines/cine/metaengine.cpp:337 engines/crab/input/input.cpp:251
 #: engines/cruise/metaengine.cpp:192 engines/draci/metaengine.cpp:237
 #: engines/dragons/metaengine.cpp:147 engines/drascula/metaengine.cpp:218
@@ -5832,18 +5840,18 @@ msgstr ""
 msgid "Inventory"
 msgstr ""
 
-#: engines/access/metaengine.cpp:344 engines/ultima/ultima4/metaengine.cpp:51
+#: engines/access/metaengine.cpp:365 engines/ultima/ultima4/metaengine.cpp:51
 msgid "Climb"
 msgstr ""
 
-#: engines/access/metaengine.cpp:354 engines/bbvs/metaengine.cpp:142
+#: engines/access/metaengine.cpp:375 engines/bbvs/metaengine.cpp:142
 #: engines/freescape/games/castle/castle.cpp:511
 #: engines/petka/metaengine.cpp:137 engines/scumm/help.cpp:225
 #: engines/tsage/metaengine.cpp:201
 msgid "Walk"
 msgstr ""
 
-#: engines/access/metaengine.cpp:365
+#: engines/access/metaengine.cpp:386
 msgid "Open save/load menu"
 msgstr ""
 



From noreply at scummvm.org  Thu Jun 18 09:26:35 2026
From: noreply at scummvm.org (ScummVM-Translations)
Date: Thu, 18 Jun 2026 09:26:35 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 3f4a1f467db0870f8c6e68fe8818c386987a8ebc
Message-ID: <20260618092635.A3F147005CC@ron.scummvm.net>

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

Summary:
3f4a1f467d I18N: Update translation files


Commit: 3f4a1f467db0870f8c6e68fe8818c386987a8ebc
    https://github.com/scummvm/scummvm/commit/3f4a1f467db0870f8c6e68fe8818c386987a8ebc
Author: Weblate (noreply at weblate.org)
Date: 2026-06-18T09:26:15Z

Commit Message:
I18N: Update translation files

Updated by "Update PO files to match POT (msgmerge)" hook in Weblate.

Translation: ScummVM/scummvm
Translate-URL: https://translations.scummvm.org/projects/scummvm/scummvm/

Changed paths:
    po/ar.po
    po/be-tarask.po
    po/be_BY.po
    po/ca_ES.po
    po/cs_CZ.po
    po/da.po
    po/de_DE.po
    po/el.po
    po/es_ES.po
    po/eu.po
    po/fi_FI.po
    po/fr_FR.po
    po/gl_ES.po
    po/he.po
    po/hi.po
    po/hu_HU.po
    po/it_IT.po
    po/ja.po
    po/ka.po
    po/ko.po
    po/nb_NO.po
    po/nl_NL.po
    po/nn_NO.po
    po/pl_PL.po
    po/pt_BR.po
    po/pt_PT.po
    po/ro.po
    po/ru_RU.po
    po/sv_SE.po
    po/tr.po
    po/uk_UA.po
    po/zh.po
    po/zh_Hans.po
    po/zh_Hant.po


diff --git a/po/ar.po b/po/ar.po
index 6333bc3653b..25447269051 100644
--- a/po/ar.po
+++ b/po/ar.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-17 23:07+0000\n"
+"POT-Creation-Date: 2026-06-18 09:25+0000\n"
 "PO-Revision-Date: 2026-02-24 22:26+0000\n"
 "Last-Translator: Mohamed Shaaban <mohmedbashamedo at hotmail.com>\n"
 "Language-Team: Arabic <https://translations.scummvm.org/projects/scummvm/"
@@ -91,7 +91,7 @@ msgstr "??????????"
 #. I18N: Opens the help screen.
 #: gui/browser.cpp:64 gui/launcher.cpp:277 gui/launcher.cpp:959
 #: backends/platform/sdl/macosx/appmenu_osx.mm:319
-#: engines/access/metaengine.cpp:360 engines/bladerunner/metaengine.cpp:321
+#: engines/access/metaengine.cpp:381 engines/bladerunner/metaengine.cpp:321
 #: engines/ngi/metaengine.cpp:162 engines/supernova/metaengine.cpp:238
 #: engines/tucker/metaengine.cpp:215 engines/zvision/metaengine.cpp:246
 msgid "Help"
@@ -2636,7 +2636,7 @@ msgstr ""
 "?????????????? ..."
 
 #: gui/options.cpp:2975 engines/engine.cpp:647
-#: engines/access/metaengine.cpp:298 engines/drascula/metaengine.cpp:255
+#: engines/access/metaengine.cpp:319 engines/drascula/metaengine.cpp:255
 #: engines/queen/metaengine.cpp:269 engines/sherlock/metaengine.cpp:410
 #: engines/ultima/nuvie/metaengine.cpp:71
 msgid "Move"
@@ -4022,7 +4022,7 @@ msgstr "?????????? ????????????"
 
 #. I18N: Skipping cutscene plaback
 #. I18N: Skipping cutscene playback
-#: engines/metaengine.cpp:112 engines/access/metaengine.cpp:225
+#: engines/metaengine.cpp:112 engines/access/metaengine.cpp:246
 #: engines/composer/metaengine.cpp:126 engines/drascula/metaengine.cpp:224
 #: engines/efh/metaengine.cpp:379 engines/freescape/movement.cpp:93
 #: engines/grim/grim.cpp:533 engines/grim/grim.cpp:633
@@ -6140,7 +6140,15 @@ msgstr "???????? ??????????????"
 msgid "Check for Updates..."
 msgstr "???????? ???? ???????? ??????????????..."
 
-#: engines/access/metaengine.cpp:215 engines/alcachofa/metaengine.cpp:94
+#: engines/access/metaengine.cpp:82
+msgid "Use 'high definition' OGG music"
+msgstr ""
+
+#: engines/access/metaengine.cpp:83
+msgid "Use the OGG audio from the re-release instead of original MIDI tracks"
+msgstr ""
+
+#: engines/access/metaengine.cpp:236 engines/alcachofa/metaengine.cpp:94
 #: engines/bbvs/metaengine.cpp:92 engines/buried/metaengine.cpp:133
 #: engines/chamber/metaengine.cpp:59 engines/cine/metaengine.cpp:270
 #: engines/cruise/metaengine.cpp:168 engines/dm/metaengine.cpp:124
@@ -6166,20 +6174,20 @@ msgstr "???????? ???? ???????? ??????????????..."
 msgid "Default keymappings"
 msgstr "?????????? ???????????? ???????????? ????????????????????"
 
-#: engines/access/metaengine.cpp:219
+#: engines/access/metaengine.cpp:240
 #, fuzzy
 #| msgid "Highlight next dialogue"
 msgid "Move / Interact / Skip"
 msgstr "???? ???????????? ???????????? ????????????"
 
-#: engines/access/metaengine.cpp:231 engines/sherlock/metaengine.cpp:483
+#: engines/access/metaengine.cpp:252 engines/sherlock/metaengine.cpp:483
 #, fuzzy
 #| msgid "Skip line"
 msgid "Skip movie"
 msgstr "???????? ????????"
 
 #. I18N: Move Actor to upwards direction
-#: engines/access/metaengine.cpp:237 engines/asylum/metaengine.cpp:171
+#: engines/access/metaengine.cpp:258 engines/asylum/metaengine.cpp:171
 #: engines/asylum/metaengine.cpp:198 engines/cine/metaengine.cpp:377
 #: engines/efh/metaengine.cpp:286 engines/hdb/metaengine.cpp:213
 #: engines/hypno/metaengine.cpp:363 engines/hypno/metaengine.cpp:411
@@ -6189,7 +6197,7 @@ msgid "Move up"
 msgstr "????????"
 
 #. I18N: Move Actor to downwards direction
-#: engines/access/metaengine.cpp:244 engines/asylum/metaengine.cpp:177
+#: engines/access/metaengine.cpp:265 engines/asylum/metaengine.cpp:177
 #: engines/asylum/metaengine.cpp:205 engines/cine/metaengine.cpp:384
 #: engines/efh/metaengine.cpp:293 engines/hdb/metaengine.cpp:219
 #: engines/hypno/metaengine.cpp:351 engines/hypno/metaengine.cpp:399
@@ -6200,7 +6208,7 @@ msgstr "???????? ??????????"
 
 #. I18N: Move Actor to left direction
 #. I18N: Move actor left during fight
-#: engines/access/metaengine.cpp:251 engines/asylum/metaengine.cpp:183
+#: engines/access/metaengine.cpp:272 engines/asylum/metaengine.cpp:183
 #: engines/asylum/metaengine.cpp:212 engines/cine/metaengine.cpp:391
 #: engines/efh/metaengine.cpp:300 engines/hdb/metaengine.cpp:225
 #: engines/hypno/metaengine.cpp:345 engines/hypno/metaengine.cpp:393
@@ -6213,7 +6221,7 @@ msgstr "???????? ??????????"
 
 #. I18N: Move Actor to right direction
 #. I18N: Move actor right during fight
-#: engines/access/metaengine.cpp:258 engines/asylum/metaengine.cpp:189
+#: engines/access/metaengine.cpp:279 engines/asylum/metaengine.cpp:189
 #: engines/asylum/metaengine.cpp:219 engines/cine/metaengine.cpp:398
 #: engines/efh/metaengine.cpp:307 engines/hdb/metaengine.cpp:231
 #: engines/hypno/metaengine.cpp:357 engines/hypno/metaengine.cpp:405
@@ -6225,7 +6233,7 @@ msgid "Move right"
 msgstr "???????? ??????????"
 
 #. I18N: Move Actor to top-left direction
-#: engines/access/metaengine.cpp:265 engines/cine/metaengine.cpp:405
+#: engines/access/metaengine.cpp:286 engines/cine/metaengine.cpp:405
 #: engines/efh/metaengine.cpp:314
 #, fuzzy
 #| msgid "Move left"
@@ -6233,7 +6241,7 @@ msgid "Move up-left"
 msgstr "???????? ??????????"
 
 #. I18N: Move Actor to top-right direction
-#: engines/access/metaengine.cpp:270 engines/cine/metaengine.cpp:411
+#: engines/access/metaengine.cpp:291 engines/cine/metaengine.cpp:411
 #: engines/efh/metaengine.cpp:320
 #, fuzzy
 #| msgid "Move right"
@@ -6241,7 +6249,7 @@ msgid "Move up-right"
 msgstr "???????? ??????????"
 
 #. I18N: Move Actor to bottom-left direction
-#: engines/access/metaengine.cpp:275 engines/cine/metaengine.cpp:417
+#: engines/access/metaengine.cpp:296 engines/cine/metaengine.cpp:417
 #: engines/efh/metaengine.cpp:326
 #, fuzzy
 #| msgid "Move down"
@@ -6249,7 +6257,7 @@ msgid "Move down-left"
 msgstr "???????? ??????????"
 
 #. I18N: Move Actor to bottom-right direction
-#: engines/access/metaengine.cpp:280 engines/cine/metaengine.cpp:423
+#: engines/access/metaengine.cpp:301 engines/cine/metaengine.cpp:423
 #: engines/efh/metaengine.cpp:332
 #, fuzzy
 #| msgid "Move right"
@@ -6257,7 +6265,7 @@ msgid "Move down-right"
 msgstr "???????? ??????????"
 
 #. I18N: (Game name: The Lost Files of Sherlock Holmes: The Case of the Rose Tattoo) This action is used to look at an object or npc in the game
-#: engines/access/metaengine.cpp:285 engines/bbvs/metaengine.cpp:124
+#: engines/access/metaengine.cpp:306 engines/bbvs/metaengine.cpp:124
 #: engines/dgds/metaengine.cpp:83 engines/drascula/metaengine.cpp:230
 #: engines/petka/metaengine.cpp:130 engines/scumm/help.cpp:167
 #: engines/scumm/help.cpp:231 engines/sherlock/metaengine.cpp:405
@@ -6268,7 +6276,7 @@ msgstr "???????? ??????????"
 msgid "Look"
 msgstr "????????"
 
-#: engines/access/metaengine.cpp:293 engines/drascula/metaengine.cpp:240
+#: engines/access/metaengine.cpp:314 engines/drascula/metaengine.cpp:240
 #: engines/queen/metaengine.cpp:259 engines/saga/metaengine.cpp:369
 #: engines/scumm/help.cpp:125 engines/scumm/help.cpp:140
 #: engines/scumm/help.cpp:165 engines/scumm/help.cpp:193
@@ -6278,13 +6286,13 @@ msgstr "????????"
 msgid "Open"
 msgstr "????????"
 
-#: engines/access/metaengine.cpp:303 engines/scumm/help.cpp:128
+#: engines/access/metaengine.cpp:324 engines/scumm/help.cpp:128
 #: engines/ultima/nuvie/metaengine.cpp:70
 msgid "Get"
 msgstr "????????"
 
 #. I18N: Combat command in Might & Magic 1
-#: engines/access/metaengine.cpp:308 engines/access/metaengine.cpp:329
+#: engines/access/metaengine.cpp:329 engines/access/metaengine.cpp:350
 #: engines/bbvs/metaengine.cpp:136 engines/cine/metaengine.cpp:342
 #: engines/efh/metaengine.cpp:462 engines/hdb/metaengine.cpp:237
 #: engines/lab/metaengine.cpp:323 engines/mm/mm1/metaengine.cpp:86
@@ -6304,11 +6312,11 @@ msgstr "????????"
 msgid "Use"
 msgstr "????????????"
 
-#: engines/access/metaengine.cpp:313
+#: engines/access/metaengine.cpp:334
 msgid "Goto"
 msgstr ""
 
-#: engines/access/metaengine.cpp:318 engines/access/metaengine.cpp:349
+#: engines/access/metaengine.cpp:339 engines/access/metaengine.cpp:370
 #: engines/bbvs/metaengine.cpp:130 engines/drascula/metaengine.cpp:250
 #: engines/efh/metaengine.cpp:506 engines/petka/metaengine.cpp:158
 #: engines/scumm/help.cpp:174 engines/scumm/help.cpp:226
@@ -6319,11 +6327,11 @@ msgstr ""
 msgid "Talk"
 msgstr "??????????"
 
-#: engines/access/metaengine.cpp:323 engines/scumm/help.cpp:175
+#: engines/access/metaengine.cpp:344 engines/scumm/help.cpp:175
 msgid "Travel"
 msgstr "??????????"
 
-#: engines/access/metaengine.cpp:334 engines/cine/metaengine.cpp:332
+#: engines/access/metaengine.cpp:355 engines/cine/metaengine.cpp:332
 #: engines/petka/metaengine.cpp:144 engines/saga/metaengine.cpp:394
 #: engines/supernova/metaengine.cpp:331
 #, fuzzy
@@ -6332,7 +6340,7 @@ msgid "Take"
 msgstr "????????"
 
 #. I18N: Action in In Cold Blood
-#: engines/access/metaengine.cpp:339 engines/alcachofa/metaengine.cpp:116
+#: engines/access/metaengine.cpp:360 engines/alcachofa/metaengine.cpp:116
 #: engines/cine/metaengine.cpp:337 engines/crab/input/input.cpp:251
 #: engines/cruise/metaengine.cpp:192 engines/draci/metaengine.cpp:237
 #: engines/dragons/metaengine.cpp:147 engines/drascula/metaengine.cpp:218
@@ -6352,18 +6360,18 @@ msgstr "????????"
 msgid "Inventory"
 msgstr "?????????? ??????????"
 
-#: engines/access/metaengine.cpp:344 engines/ultima/ultima4/metaengine.cpp:51
+#: engines/access/metaengine.cpp:365 engines/ultima/ultima4/metaengine.cpp:51
 msgid "Climb"
 msgstr ""
 
-#: engines/access/metaengine.cpp:354 engines/bbvs/metaengine.cpp:142
+#: engines/access/metaengine.cpp:375 engines/bbvs/metaengine.cpp:142
 #: engines/freescape/games/castle/castle.cpp:511
 #: engines/petka/metaengine.cpp:137 engines/scumm/help.cpp:225
 #: engines/tsage/metaengine.cpp:201
 msgid "Walk"
 msgstr "????????"
 
-#: engines/access/metaengine.cpp:365
+#: engines/access/metaengine.cpp:386
 #, fuzzy
 #| msgid "Go to save/load menu"
 msgid "Open save/load menu"
diff --git a/po/be-tarask.po b/po/be-tarask.po
index 4244a4191fd..8fe4cca0063 100644
--- a/po/be-tarask.po
+++ b/po/be-tarask.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-17 23:07+0000\n"
+"POT-Creation-Date: 2026-06-18 09:25+0000\n"
 "PO-Revision-Date: 2023-02-26 15:01+0000\n"
 "Last-Translator: Kastu?? K. <oakum2334 at gmail.com>\n"
 "Language-Team: Belarusian (Tara??kievica) <https://translations.scummvm.org/"
@@ -91,7 +91,7 @@ msgstr "??????????????"
 #. I18N: Opens the help screen.
 #: gui/browser.cpp:64 gui/launcher.cpp:277 gui/launcher.cpp:959
 #: backends/platform/sdl/macosx/appmenu_osx.mm:319
-#: engines/access/metaengine.cpp:360 engines/bladerunner/metaengine.cpp:321
+#: engines/access/metaengine.cpp:381 engines/bladerunner/metaengine.cpp:321
 #: engines/ngi/metaengine.cpp:162 engines/supernova/metaengine.cpp:238
 #: engines/tucker/metaengine.cpp:215 engines/zvision/metaengine.cpp:246
 msgid "Help"
@@ -2663,7 +2663,7 @@ msgstr ""
 "???? ?????????????"
 
 #: gui/options.cpp:2975 engines/engine.cpp:647
-#: engines/access/metaengine.cpp:298 engines/drascula/metaengine.cpp:255
+#: engines/access/metaengine.cpp:319 engines/drascula/metaengine.cpp:255
 #: engines/queen/metaengine.cpp:269 engines/sherlock/metaengine.cpp:410
 #: engines/ultima/nuvie/metaengine.cpp:71
 msgid "Move"
@@ -3978,7 +3978,7 @@ msgstr "???????? ????????????"
 
 #. I18N: Skipping cutscene plaback
 #. I18N: Skipping cutscene playback
-#: engines/metaengine.cpp:112 engines/access/metaengine.cpp:225
+#: engines/metaengine.cpp:112 engines/access/metaengine.cpp:246
 #: engines/composer/metaengine.cpp:126 engines/drascula/metaengine.cpp:224
 #: engines/efh/metaengine.cpp:379 engines/freescape/movement.cpp:93
 #: engines/grim/grim.cpp:533 engines/grim/grim.cpp:633
@@ -6073,7 +6073,15 @@ msgstr "ScummVM"
 msgid "Check for Updates..."
 msgstr "?????????????????? ????????????..."
 
-#: engines/access/metaengine.cpp:215 engines/alcachofa/metaengine.cpp:94
+#: engines/access/metaengine.cpp:82
+msgid "Use 'high definition' OGG music"
+msgstr ""
+
+#: engines/access/metaengine.cpp:83
+msgid "Use the OGG audio from the re-release instead of original MIDI tracks"
+msgstr ""
+
+#: engines/access/metaengine.cpp:236 engines/alcachofa/metaengine.cpp:94
 #: engines/bbvs/metaengine.cpp:92 engines/buried/metaengine.cpp:133
 #: engines/chamber/metaengine.cpp:59 engines/cine/metaengine.cpp:270
 #: engines/cruise/metaengine.cpp:168 engines/dm/metaengine.cpp:124
@@ -6099,20 +6107,20 @@ msgstr "?????????????????? ????????????..."
 msgid "Default keymappings"
 msgstr "?????????????????????? ?????????????? ????????????"
 
-#: engines/access/metaengine.cpp:219
+#: engines/access/metaengine.cpp:240
 #, fuzzy
 #| msgid "Highlight next dialogue"
 msgid "Move / Interact / Skip"
 msgstr "???????????????????? ???????????????? ????????????"
 
-#: engines/access/metaengine.cpp:231 engines/sherlock/metaengine.cpp:483
+#: engines/access/metaengine.cpp:252 engines/sherlock/metaengine.cpp:483
 #, fuzzy
 #| msgid "Skip line"
 msgid "Skip movie"
 msgstr "?????????????????????? ??????????"
 
 #. I18N: Move Actor to upwards direction
-#: engines/access/metaengine.cpp:237 engines/asylum/metaengine.cpp:171
+#: engines/access/metaengine.cpp:258 engines/asylum/metaengine.cpp:171
 #: engines/asylum/metaengine.cpp:198 engines/cine/metaengine.cpp:377
 #: engines/efh/metaengine.cpp:286 engines/hdb/metaengine.cpp:213
 #: engines/hypno/metaengine.cpp:363 engines/hypno/metaengine.cpp:411
@@ -6122,7 +6130,7 @@ msgid "Move up"
 msgstr "?????????????? ??????????"
 
 #. I18N: Move Actor to downwards direction
-#: engines/access/metaengine.cpp:244 engines/asylum/metaengine.cpp:177
+#: engines/access/metaengine.cpp:265 engines/asylum/metaengine.cpp:177
 #: engines/asylum/metaengine.cpp:205 engines/cine/metaengine.cpp:384
 #: engines/efh/metaengine.cpp:293 engines/hdb/metaengine.cpp:219
 #: engines/hypno/metaengine.cpp:351 engines/hypno/metaengine.cpp:399
@@ -6133,7 +6141,7 @@ msgstr "?????????????? ????????"
 
 #. I18N: Move Actor to left direction
 #. I18N: Move actor left during fight
-#: engines/access/metaengine.cpp:251 engines/asylum/metaengine.cpp:183
+#: engines/access/metaengine.cpp:272 engines/asylum/metaengine.cpp:183
 #: engines/asylum/metaengine.cpp:212 engines/cine/metaengine.cpp:391
 #: engines/efh/metaengine.cpp:300 engines/hdb/metaengine.cpp:225
 #: engines/hypno/metaengine.cpp:345 engines/hypno/metaengine.cpp:393
@@ -6146,7 +6154,7 @@ msgstr "?????????????? ????????????"
 
 #. I18N: Move Actor to right direction
 #. I18N: Move actor right during fight
-#: engines/access/metaengine.cpp:258 engines/asylum/metaengine.cpp:189
+#: engines/access/metaengine.cpp:279 engines/asylum/metaengine.cpp:189
 #: engines/asylum/metaengine.cpp:219 engines/cine/metaengine.cpp:398
 #: engines/efh/metaengine.cpp:307 engines/hdb/metaengine.cpp:231
 #: engines/hypno/metaengine.cpp:357 engines/hypno/metaengine.cpp:405
@@ -6158,7 +6166,7 @@ msgid "Move right"
 msgstr "?????????????? ??????????????"
 
 #. I18N: Move Actor to top-left direction
-#: engines/access/metaengine.cpp:265 engines/cine/metaengine.cpp:405
+#: engines/access/metaengine.cpp:286 engines/cine/metaengine.cpp:405
 #: engines/efh/metaengine.cpp:314
 #, fuzzy
 #| msgid "Move left"
@@ -6166,7 +6174,7 @@ msgid "Move up-left"
 msgstr "?????????????? ????????????"
 
 #. I18N: Move Actor to top-right direction
-#: engines/access/metaengine.cpp:270 engines/cine/metaengine.cpp:411
+#: engines/access/metaengine.cpp:291 engines/cine/metaengine.cpp:411
 #: engines/efh/metaengine.cpp:320
 #, fuzzy
 #| msgid "Move right"
@@ -6174,7 +6182,7 @@ msgid "Move up-right"
 msgstr "?????????????? ??????????????"
 
 #. I18N: Move Actor to bottom-left direction
-#: engines/access/metaengine.cpp:275 engines/cine/metaengine.cpp:417
+#: engines/access/metaengine.cpp:296 engines/cine/metaengine.cpp:417
 #: engines/efh/metaengine.cpp:326
 #, fuzzy
 #| msgid "Move down"
@@ -6182,7 +6190,7 @@ msgid "Move down-left"
 msgstr "?????????????? ????????"
 
 #. I18N: Move Actor to bottom-right direction
-#: engines/access/metaengine.cpp:280 engines/cine/metaengine.cpp:423
+#: engines/access/metaengine.cpp:301 engines/cine/metaengine.cpp:423
 #: engines/efh/metaengine.cpp:332
 #, fuzzy
 #| msgid "Move right"
@@ -6190,7 +6198,7 @@ msgid "Move down-right"
 msgstr "?????????????? ??????????????"
 
 #. I18N: (Game name: The Lost Files of Sherlock Holmes: The Case of the Rose Tattoo) This action is used to look at an object or npc in the game
-#: engines/access/metaengine.cpp:285 engines/bbvs/metaengine.cpp:124
+#: engines/access/metaengine.cpp:306 engines/bbvs/metaengine.cpp:124
 #: engines/dgds/metaengine.cpp:83 engines/drascula/metaengine.cpp:230
 #: engines/petka/metaengine.cpp:130 engines/scumm/help.cpp:167
 #: engines/scumm/help.cpp:231 engines/sherlock/metaengine.cpp:405
@@ -6201,7 +6209,7 @@ msgstr "?????????????? ??????????????"
 msgid "Look"
 msgstr "????????????????"
 
-#: engines/access/metaengine.cpp:293 engines/drascula/metaengine.cpp:240
+#: engines/access/metaengine.cpp:314 engines/drascula/metaengine.cpp:240
 #: engines/queen/metaengine.cpp:259 engines/saga/metaengine.cpp:369
 #: engines/scumm/help.cpp:125 engines/scumm/help.cpp:140
 #: engines/scumm/help.cpp:165 engines/scumm/help.cpp:193
@@ -6211,13 +6219,13 @@ msgstr "????????????????"
 msgid "Open"
 msgstr "??????????????"
 
-#: engines/access/metaengine.cpp:303 engines/scumm/help.cpp:128
+#: engines/access/metaengine.cpp:324 engines/scumm/help.cpp:128
 #: engines/ultima/nuvie/metaengine.cpp:70
 msgid "Get"
 msgstr "??????????"
 
 #. I18N: Combat command in Might & Magic 1
-#: engines/access/metaengine.cpp:308 engines/access/metaengine.cpp:329
+#: engines/access/metaengine.cpp:329 engines/access/metaengine.cpp:350
 #: engines/bbvs/metaengine.cpp:136 engines/cine/metaengine.cpp:342
 #: engines/efh/metaengine.cpp:462 engines/hdb/metaengine.cpp:237
 #: engines/lab/metaengine.cpp:323 engines/mm/mm1/metaengine.cpp:86
@@ -6237,11 +6245,11 @@ msgstr "??????????"
 msgid "Use"
 msgstr "????????????????????"
 
-#: engines/access/metaengine.cpp:313
+#: engines/access/metaengine.cpp:334
 msgid "Goto"
 msgstr ""
 
-#: engines/access/metaengine.cpp:318 engines/access/metaengine.cpp:349
+#: engines/access/metaengine.cpp:339 engines/access/metaengine.cpp:370
 #: engines/bbvs/metaengine.cpp:130 engines/drascula/metaengine.cpp:250
 #: engines/efh/metaengine.cpp:506 engines/petka/metaengine.cpp:158
 #: engines/scumm/help.cpp:174 engines/scumm/help.cpp:226
@@ -6252,11 +6260,11 @@ msgstr ""
 msgid "Talk"
 msgstr "????????????????"
 
-#: engines/access/metaengine.cpp:323 engines/scumm/help.cpp:175
+#: engines/access/metaengine.cpp:344 engines/scumm/help.cpp:175
 msgid "Travel"
 msgstr "????????????????????"
 
-#: engines/access/metaengine.cpp:334 engines/cine/metaengine.cpp:332
+#: engines/access/metaengine.cpp:355 engines/cine/metaengine.cpp:332
 #: engines/petka/metaengine.cpp:144 engines/saga/metaengine.cpp:394
 #: engines/supernova/metaengine.cpp:331
 #, fuzzy
@@ -6265,7 +6273,7 @@ msgid "Take"
 msgstr "????????????"
 
 #. I18N: Action in In Cold Blood
-#: engines/access/metaengine.cpp:339 engines/alcachofa/metaengine.cpp:116
+#: engines/access/metaengine.cpp:360 engines/alcachofa/metaengine.cpp:116
 #: engines/cine/metaengine.cpp:337 engines/crab/input/input.cpp:251
 #: engines/cruise/metaengine.cpp:192 engines/draci/metaengine.cpp:237
 #: engines/dragons/metaengine.cpp:147 engines/drascula/metaengine.cpp:218
@@ -6285,18 +6293,18 @@ msgstr "????????????"
 msgid "Inventory"
 msgstr "????????????????"
 
-#: engines/access/metaengine.cpp:344 engines/ultima/ultima4/metaengine.cpp:51
+#: engines/access/metaengine.cpp:365 engines/ultima/ultima4/metaengine.cpp:51
 msgid "Climb"
 msgstr ""
 
-#: engines/access/metaengine.cpp:354 engines/bbvs/metaengine.cpp:142
+#: engines/access/metaengine.cpp:375 engines/bbvs/metaengine.cpp:142
 #: engines/freescape/games/castle/castle.cpp:511
 #: engines/petka/metaengine.cpp:137 engines/scumm/help.cpp:225
 #: engines/tsage/metaengine.cpp:201
 msgid "Walk"
 msgstr "??????????"
 
-#: engines/access/metaengine.cpp:365
+#: engines/access/metaengine.cpp:386
 #, fuzzy
 #| msgid "Go to save/load menu"
 msgid "Open save/load menu"
diff --git a/po/be_BY.po b/po/be_BY.po
index 23b4c3f6b1a..99e4f4416e3 100644
--- a/po/be_BY.po
+++ b/po/be_BY.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-17 23:07+0000\n"
+"POT-Creation-Date: 2026-06-18 09:25+0000\n"
 "PO-Revision-Date: 2021-03-21 08:24+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Belarusian <https://translations.scummvm.org/projects/scummvm/"
@@ -89,7 +89,7 @@ msgstr "??????????????"
 #. I18N: Opens the help screen.
 #: gui/browser.cpp:64 gui/launcher.cpp:277 gui/launcher.cpp:959
 #: backends/platform/sdl/macosx/appmenu_osx.mm:319
-#: engines/access/metaengine.cpp:360 engines/bladerunner/metaengine.cpp:321
+#: engines/access/metaengine.cpp:381 engines/bladerunner/metaengine.cpp:321
 #: engines/ngi/metaengine.cpp:162 engines/supernova/metaengine.cpp:238
 #: engines/tucker/metaengine.cpp:215 engines/zvision/metaengine.cpp:246
 msgid "Help"
@@ -2673,7 +2673,7 @@ msgid ""
 msgstr "???????????????? ????????..."
 
 #: gui/options.cpp:2975 engines/engine.cpp:647
-#: engines/access/metaengine.cpp:298 engines/drascula/metaengine.cpp:255
+#: engines/access/metaengine.cpp:319 engines/drascula/metaengine.cpp:255
 #: engines/queen/metaengine.cpp:269 engines/sherlock/metaengine.cpp:410
 #: engines/ultima/nuvie/metaengine.cpp:71
 #, fuzzy
@@ -3981,7 +3981,7 @@ msgstr "???????? ????????????"
 
 #. I18N: Skipping cutscene plaback
 #. I18N: Skipping cutscene playback
-#: engines/metaengine.cpp:112 engines/access/metaengine.cpp:225
+#: engines/metaengine.cpp:112 engines/access/metaengine.cpp:246
 #: engines/composer/metaengine.cpp:126 engines/drascula/metaengine.cpp:224
 #: engines/efh/metaengine.cpp:379 engines/freescape/movement.cpp:93
 #: engines/grim/grim.cpp:533 engines/grim/grim.cpp:633
@@ -6082,7 +6082,15 @@ msgstr "???????????????? ????????"
 msgid "Check for Updates..."
 msgstr "???????????????? ????????????????????..."
 
-#: engines/access/metaengine.cpp:215 engines/alcachofa/metaengine.cpp:94
+#: engines/access/metaengine.cpp:82
+msgid "Use 'high definition' OGG music"
+msgstr ""
+
+#: engines/access/metaengine.cpp:83
+msgid "Use the OGG audio from the re-release instead of original MIDI tracks"
+msgstr ""
+
+#: engines/access/metaengine.cpp:236 engines/alcachofa/metaengine.cpp:94
 #: engines/bbvs/metaengine.cpp:92 engines/buried/metaengine.cpp:133
 #: engines/chamber/metaengine.cpp:59 engines/cine/metaengine.cpp:270
 #: engines/cruise/metaengine.cpp:168 engines/dm/metaengine.cpp:124
@@ -6108,20 +6116,20 @@ msgstr "???????????????? ????????????????????..."
 msgid "Default keymappings"
 msgstr "?????????????????? ???????????? ???? ??????????????????"
 
-#: engines/access/metaengine.cpp:219
+#: engines/access/metaengine.cpp:240
 #, fuzzy
 #| msgid "Highlight next dialogue"
 msgid "Move / Interact / Skip"
 msgstr "?????????????????????? ???????????????? ????????????"
 
-#: engines/access/metaengine.cpp:231 engines/sherlock/metaengine.cpp:483
+#: engines/access/metaengine.cpp:252 engines/sherlock/metaengine.cpp:483
 #, fuzzy
 #| msgid "Skip line"
 msgid "Skip movie"
 msgstr "???????????????????? ??????????"
 
 #. I18N: Move Actor to upwards direction
-#: engines/access/metaengine.cpp:237 engines/asylum/metaengine.cpp:171
+#: engines/access/metaengine.cpp:258 engines/asylum/metaengine.cpp:171
 #: engines/asylum/metaengine.cpp:198 engines/cine/metaengine.cpp:377
 #: engines/efh/metaengine.cpp:286 engines/hdb/metaengine.cpp:213
 #: engines/hypno/metaengine.cpp:363 engines/hypno/metaengine.cpp:411
@@ -6131,7 +6139,7 @@ msgid "Move up"
 msgstr "?????????????? ??????????"
 
 #. I18N: Move Actor to downwards direction
-#: engines/access/metaengine.cpp:244 engines/asylum/metaengine.cpp:177
+#: engines/access/metaengine.cpp:265 engines/asylum/metaengine.cpp:177
 #: engines/asylum/metaengine.cpp:205 engines/cine/metaengine.cpp:384
 #: engines/efh/metaengine.cpp:293 engines/hdb/metaengine.cpp:219
 #: engines/hypno/metaengine.cpp:351 engines/hypno/metaengine.cpp:399
@@ -6142,7 +6150,7 @@ msgstr "?????????????? ????????"
 
 #. I18N: Move Actor to left direction
 #. I18N: Move actor left during fight
-#: engines/access/metaengine.cpp:251 engines/asylum/metaengine.cpp:183
+#: engines/access/metaengine.cpp:272 engines/asylum/metaengine.cpp:183
 #: engines/asylum/metaengine.cpp:212 engines/cine/metaengine.cpp:391
 #: engines/efh/metaengine.cpp:300 engines/hdb/metaengine.cpp:225
 #: engines/hypno/metaengine.cpp:345 engines/hypno/metaengine.cpp:393
@@ -6155,7 +6163,7 @@ msgstr "?????????????? ????????????"
 
 #. I18N: Move Actor to right direction
 #. I18N: Move actor right during fight
-#: engines/access/metaengine.cpp:258 engines/asylum/metaengine.cpp:189
+#: engines/access/metaengine.cpp:279 engines/asylum/metaengine.cpp:189
 #: engines/asylum/metaengine.cpp:219 engines/cine/metaengine.cpp:398
 #: engines/efh/metaengine.cpp:307 engines/hdb/metaengine.cpp:231
 #: engines/hypno/metaengine.cpp:357 engines/hypno/metaengine.cpp:405
@@ -6167,7 +6175,7 @@ msgid "Move right"
 msgstr "?????????????? ??????????????"
 
 #. I18N: Move Actor to top-left direction
-#: engines/access/metaengine.cpp:265 engines/cine/metaengine.cpp:405
+#: engines/access/metaengine.cpp:286 engines/cine/metaengine.cpp:405
 #: engines/efh/metaengine.cpp:314
 #, fuzzy
 #| msgid "Move left"
@@ -6175,7 +6183,7 @@ msgid "Move up-left"
 msgstr "?????????????? ????????????"
 
 #. I18N: Move Actor to top-right direction
-#: engines/access/metaengine.cpp:270 engines/cine/metaengine.cpp:411
+#: engines/access/metaengine.cpp:291 engines/cine/metaengine.cpp:411
 #: engines/efh/metaengine.cpp:320
 #, fuzzy
 #| msgid "Move right"
@@ -6183,7 +6191,7 @@ msgid "Move up-right"
 msgstr "?????????????? ??????????????"
 
 #. I18N: Move Actor to bottom-left direction
-#: engines/access/metaengine.cpp:275 engines/cine/metaengine.cpp:417
+#: engines/access/metaengine.cpp:296 engines/cine/metaengine.cpp:417
 #: engines/efh/metaengine.cpp:326
 #, fuzzy
 #| msgid "Move down"
@@ -6191,7 +6199,7 @@ msgid "Move down-left"
 msgstr "?????????????? ????????"
 
 #. I18N: Move Actor to bottom-right direction
-#: engines/access/metaengine.cpp:280 engines/cine/metaengine.cpp:423
+#: engines/access/metaengine.cpp:301 engines/cine/metaengine.cpp:423
 #: engines/efh/metaengine.cpp:332
 #, fuzzy
 #| msgid "Move right"
@@ -6199,7 +6207,7 @@ msgid "Move down-right"
 msgstr "?????????????? ??????????????"
 
 #. I18N: (Game name: The Lost Files of Sherlock Holmes: The Case of the Rose Tattoo) This action is used to look at an object or npc in the game
-#: engines/access/metaengine.cpp:285 engines/bbvs/metaengine.cpp:124
+#: engines/access/metaengine.cpp:306 engines/bbvs/metaengine.cpp:124
 #: engines/dgds/metaengine.cpp:83 engines/drascula/metaengine.cpp:230
 #: engines/petka/metaengine.cpp:130 engines/scumm/help.cpp:167
 #: engines/scumm/help.cpp:231 engines/sherlock/metaengine.cpp:405
@@ -6210,7 +6218,7 @@ msgstr "?????????????? ??????????????"
 msgid "Look"
 msgstr "????????????????"
 
-#: engines/access/metaengine.cpp:293 engines/drascula/metaengine.cpp:240
+#: engines/access/metaengine.cpp:314 engines/drascula/metaengine.cpp:240
 #: engines/queen/metaengine.cpp:259 engines/saga/metaengine.cpp:369
 #: engines/scumm/help.cpp:125 engines/scumm/help.cpp:140
 #: engines/scumm/help.cpp:165 engines/scumm/help.cpp:193
@@ -6220,13 +6228,13 @@ msgstr "????????????????"
 msgid "Open"
 msgstr "????????????????"
 
-#: engines/access/metaengine.cpp:303 engines/scumm/help.cpp:128
+#: engines/access/metaengine.cpp:324 engines/scumm/help.cpp:128
 #: engines/ultima/nuvie/metaengine.cpp:70
 msgid "Get"
 msgstr "??????????"
 
 #. I18N: Combat command in Might & Magic 1
-#: engines/access/metaengine.cpp:308 engines/access/metaengine.cpp:329
+#: engines/access/metaengine.cpp:329 engines/access/metaengine.cpp:350
 #: engines/bbvs/metaengine.cpp:136 engines/cine/metaengine.cpp:342
 #: engines/efh/metaengine.cpp:462 engines/hdb/metaengine.cpp:237
 #: engines/lab/metaengine.cpp:323 engines/mm/mm1/metaengine.cpp:86
@@ -6246,11 +6254,11 @@ msgstr "??????????"
 msgid "Use"
 msgstr "??????????????????????"
 
-#: engines/access/metaengine.cpp:313
+#: engines/access/metaengine.cpp:334
 msgid "Goto"
 msgstr ""
 
-#: engines/access/metaengine.cpp:318 engines/access/metaengine.cpp:349
+#: engines/access/metaengine.cpp:339 engines/access/metaengine.cpp:370
 #: engines/bbvs/metaengine.cpp:130 engines/drascula/metaengine.cpp:250
 #: engines/efh/metaengine.cpp:506 engines/petka/metaengine.cpp:158
 #: engines/scumm/help.cpp:174 engines/scumm/help.cpp:226
@@ -6261,11 +6269,11 @@ msgstr ""
 msgid "Talk"
 msgstr "????????????????"
 
-#: engines/access/metaengine.cpp:323 engines/scumm/help.cpp:175
+#: engines/access/metaengine.cpp:344 engines/scumm/help.cpp:175
 msgid "Travel"
 msgstr "??????????????????????????"
 
-#: engines/access/metaengine.cpp:334 engines/cine/metaengine.cpp:332
+#: engines/access/metaengine.cpp:355 engines/cine/metaengine.cpp:332
 #: engines/petka/metaengine.cpp:144 engines/saga/metaengine.cpp:394
 #: engines/supernova/metaengine.cpp:331
 #, fuzzy
@@ -6274,7 +6282,7 @@ msgid "Take"
 msgstr "??????????????"
 
 #. I18N: Action in In Cold Blood
-#: engines/access/metaengine.cpp:339 engines/alcachofa/metaengine.cpp:116
+#: engines/access/metaengine.cpp:360 engines/alcachofa/metaengine.cpp:116
 #: engines/cine/metaengine.cpp:337 engines/crab/input/input.cpp:251
 #: engines/cruise/metaengine.cpp:192 engines/draci/metaengine.cpp:237
 #: engines/dragons/metaengine.cpp:147 engines/drascula/metaengine.cpp:218
@@ -6294,18 +6302,18 @@ msgstr "??????????????"
 msgid "Inventory"
 msgstr "????????????????"
 
-#: engines/access/metaengine.cpp:344 engines/ultima/ultima4/metaengine.cpp:51
+#: engines/access/metaengine.cpp:365 engines/ultima/ultima4/metaengine.cpp:51
 msgid "Climb"
 msgstr ""
 
-#: engines/access/metaengine.cpp:354 engines/bbvs/metaengine.cpp:142
+#: engines/access/metaengine.cpp:375 engines/bbvs/metaengine.cpp:142
 #: engines/freescape/games/castle/castle.cpp:511
 #: engines/petka/metaengine.cpp:137 engines/scumm/help.cpp:225
 #: engines/tsage/metaengine.cpp:201
 msgid "Walk"
 msgstr "????????"
 
-#: engines/access/metaengine.cpp:365
+#: engines/access/metaengine.cpp:386
 #, fuzzy
 msgid "Open save/load menu"
 msgstr "?????????????? ???????? ?? ??????????????????????"
diff --git a/po/ca_ES.po b/po/ca_ES.po
index 25eee049032..2c27f0ecc59 100644
--- a/po/ca_ES.po
+++ b/po/ca_ES.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.6.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-17 23:07+0000\n"
+"POT-Creation-Date: 2026-06-18 09:25+0000\n"
 "PO-Revision-Date: 2026-05-26 04:22+0000\n"
 "Last-Translator: Xaviu <xaviu at xaviu.com>\n"
 "Language-Team: Catalan <https://translations.scummvm.org/projects/scummvm/"
@@ -83,7 +83,7 @@ msgstr "Tanca"
 #. I18N: Opens the help screen.
 #: gui/browser.cpp:64 gui/launcher.cpp:277 gui/launcher.cpp:959
 #: backends/platform/sdl/macosx/appmenu_osx.mm:319
-#: engines/access/metaengine.cpp:360 engines/bladerunner/metaengine.cpp:321
+#: engines/access/metaengine.cpp:381 engines/bladerunner/metaengine.cpp:321
 #: engines/ngi/metaengine.cpp:162 engines/supernova/metaengine.cpp:238
 #: engines/tucker/metaengine.cpp:215 engines/zvision/metaengine.cpp:246
 msgid "Help"
@@ -2620,7 +2620,7 @@ msgstr ""
 "I m??s..."
 
 #: gui/options.cpp:2975 engines/engine.cpp:647
-#: engines/access/metaengine.cpp:298 engines/drascula/metaengine.cpp:255
+#: engines/access/metaengine.cpp:319 engines/drascula/metaengine.cpp:255
 #: engines/queen/metaengine.cpp:269 engines/sherlock/metaengine.cpp:410
 #: engines/ultima/nuvie/metaengine.cpp:71
 msgid "Move"
@@ -3902,7 +3902,7 @@ msgstr "Men?? del joc"
 
 #. I18N: Skipping cutscene plaback
 #. I18N: Skipping cutscene playback
-#: engines/metaengine.cpp:112 engines/access/metaengine.cpp:225
+#: engines/metaengine.cpp:112 engines/access/metaengine.cpp:246
 #: engines/composer/metaengine.cpp:126 engines/drascula/metaengine.cpp:224
 #: engines/efh/metaengine.cpp:379 engines/freescape/movement.cpp:93
 #: engines/grim/grim.cpp:533 engines/grim/grim.cpp:633
@@ -5888,7 +5888,15 @@ msgstr "Llan??ador"
 msgid "Check for Updates..."
 msgstr "Comprova si hi ha actualitzacions..."
 
-#: engines/access/metaengine.cpp:215 engines/alcachofa/metaengine.cpp:94
+#: engines/access/metaengine.cpp:82
+msgid "Use 'high definition' OGG music"
+msgstr ""
+
+#: engines/access/metaengine.cpp:83
+msgid "Use the OGG audio from the re-release instead of original MIDI tracks"
+msgstr ""
+
+#: engines/access/metaengine.cpp:236 engines/alcachofa/metaengine.cpp:94
 #: engines/bbvs/metaengine.cpp:92 engines/buried/metaengine.cpp:133
 #: engines/chamber/metaengine.cpp:59 engines/cine/metaengine.cpp:270
 #: engines/cruise/metaengine.cpp:168 engines/dm/metaengine.cpp:124
@@ -5912,16 +5920,16 @@ msgstr "Comprova si hi ha actualitzacions..."
 msgid "Default keymappings"
 msgstr "Assignaci?? de tecles per defecte"
 
-#: engines/access/metaengine.cpp:219
+#: engines/access/metaengine.cpp:240
 msgid "Move / Interact / Skip"
 msgstr "Mou / Interactua / Omet"
 
-#: engines/access/metaengine.cpp:231 engines/sherlock/metaengine.cpp:483
+#: engines/access/metaengine.cpp:252 engines/sherlock/metaengine.cpp:483
 msgid "Skip movie"
 msgstr "Omet la pel??l??cula"
 
 #. I18N: Move Actor to upwards direction
-#: engines/access/metaengine.cpp:237 engines/asylum/metaengine.cpp:171
+#: engines/access/metaengine.cpp:258 engines/asylum/metaengine.cpp:171
 #: engines/asylum/metaengine.cpp:198 engines/cine/metaengine.cpp:377
 #: engines/efh/metaengine.cpp:286 engines/hdb/metaengine.cpp:213
 #: engines/hypno/metaengine.cpp:363 engines/hypno/metaengine.cpp:411
@@ -5931,7 +5939,7 @@ msgid "Move up"
 msgstr "Mou amunt"
 
 #. I18N: Move Actor to downwards direction
-#: engines/access/metaengine.cpp:244 engines/asylum/metaengine.cpp:177
+#: engines/access/metaengine.cpp:265 engines/asylum/metaengine.cpp:177
 #: engines/asylum/metaengine.cpp:205 engines/cine/metaengine.cpp:384
 #: engines/efh/metaengine.cpp:293 engines/hdb/metaengine.cpp:219
 #: engines/hypno/metaengine.cpp:351 engines/hypno/metaengine.cpp:399
@@ -5942,7 +5950,7 @@ msgstr "Mou avall"
 
 #. I18N: Move Actor to left direction
 #. I18N: Move actor left during fight
-#: engines/access/metaengine.cpp:251 engines/asylum/metaengine.cpp:183
+#: engines/access/metaengine.cpp:272 engines/asylum/metaengine.cpp:183
 #: engines/asylum/metaengine.cpp:212 engines/cine/metaengine.cpp:391
 #: engines/efh/metaengine.cpp:300 engines/hdb/metaengine.cpp:225
 #: engines/hypno/metaengine.cpp:345 engines/hypno/metaengine.cpp:393
@@ -5955,7 +5963,7 @@ msgstr "Mou a l'esquerra"
 
 #. I18N: Move Actor to right direction
 #. I18N: Move actor right during fight
-#: engines/access/metaengine.cpp:258 engines/asylum/metaengine.cpp:189
+#: engines/access/metaengine.cpp:279 engines/asylum/metaengine.cpp:189
 #: engines/asylum/metaengine.cpp:219 engines/cine/metaengine.cpp:398
 #: engines/efh/metaengine.cpp:307 engines/hdb/metaengine.cpp:231
 #: engines/hypno/metaengine.cpp:357 engines/hypno/metaengine.cpp:405
@@ -5967,31 +5975,31 @@ msgid "Move right"
 msgstr "Mou a la dreta"
 
 #. I18N: Move Actor to top-left direction
-#: engines/access/metaengine.cpp:265 engines/cine/metaengine.cpp:405
+#: engines/access/metaengine.cpp:286 engines/cine/metaengine.cpp:405
 #: engines/efh/metaengine.cpp:314
 msgid "Move up-left"
 msgstr "Mou cap amunt a l'esquerra"
 
 #. I18N: Move Actor to top-right direction
-#: engines/access/metaengine.cpp:270 engines/cine/metaengine.cpp:411
+#: engines/access/metaengine.cpp:291 engines/cine/metaengine.cpp:411
 #: engines/efh/metaengine.cpp:320
 msgid "Move up-right"
 msgstr "Mou cap amunt a la dreta"
 
 #. I18N: Move Actor to bottom-left direction
-#: engines/access/metaengine.cpp:275 engines/cine/metaengine.cpp:417
+#: engines/access/metaengine.cpp:296 engines/cine/metaengine.cpp:417
 #: engines/efh/metaengine.cpp:326
 msgid "Move down-left"
 msgstr "Mou cap avall a l'esquerra"
 
 #. I18N: Move Actor to bottom-right direction
-#: engines/access/metaengine.cpp:280 engines/cine/metaengine.cpp:423
+#: engines/access/metaengine.cpp:301 engines/cine/metaengine.cpp:423
 #: engines/efh/metaengine.cpp:332
 msgid "Move down-right"
 msgstr "Mou cap avall a la dreta"
 
 #. I18N: (Game name: The Lost Files of Sherlock Holmes: The Case of the Rose Tattoo) This action is used to look at an object or npc in the game
-#: engines/access/metaengine.cpp:285 engines/bbvs/metaengine.cpp:124
+#: engines/access/metaengine.cpp:306 engines/bbvs/metaengine.cpp:124
 #: engines/dgds/metaengine.cpp:83 engines/drascula/metaengine.cpp:230
 #: engines/petka/metaengine.cpp:130 engines/scumm/help.cpp:167
 #: engines/scumm/help.cpp:231 engines/sherlock/metaengine.cpp:405
@@ -6002,7 +6010,7 @@ msgstr "Mou cap avall a la dreta"
 msgid "Look"
 msgstr "Mira"
 
-#: engines/access/metaengine.cpp:293 engines/drascula/metaengine.cpp:240
+#: engines/access/metaengine.cpp:314 engines/drascula/metaengine.cpp:240
 #: engines/queen/metaengine.cpp:259 engines/saga/metaengine.cpp:369
 #: engines/scumm/help.cpp:125 engines/scumm/help.cpp:140
 #: engines/scumm/help.cpp:165 engines/scumm/help.cpp:193
@@ -6012,13 +6020,13 @@ msgstr "Mira"
 msgid "Open"
 msgstr "Obre"
 
-#: engines/access/metaengine.cpp:303 engines/scumm/help.cpp:128
+#: engines/access/metaengine.cpp:324 engines/scumm/help.cpp:128
 #: engines/ultima/nuvie/metaengine.cpp:70
 msgid "Get"
 msgstr "Obt??"
 
 #. I18N: Combat command in Might & Magic 1
-#: engines/access/metaengine.cpp:308 engines/access/metaengine.cpp:329
+#: engines/access/metaengine.cpp:329 engines/access/metaengine.cpp:350
 #: engines/bbvs/metaengine.cpp:136 engines/cine/metaengine.cpp:342
 #: engines/efh/metaengine.cpp:462 engines/hdb/metaengine.cpp:237
 #: engines/lab/metaengine.cpp:323 engines/mm/mm1/metaengine.cpp:86
@@ -6038,11 +6046,11 @@ msgstr "Obt??"
 msgid "Use"
 msgstr "Utilitza"
 
-#: engines/access/metaengine.cpp:313
+#: engines/access/metaengine.cpp:334
 msgid "Goto"
 msgstr "Ves"
 
-#: engines/access/metaengine.cpp:318 engines/access/metaengine.cpp:349
+#: engines/access/metaengine.cpp:339 engines/access/metaengine.cpp:370
 #: engines/bbvs/metaengine.cpp:130 engines/drascula/metaengine.cpp:250
 #: engines/efh/metaengine.cpp:506 engines/petka/metaengine.cpp:158
 #: engines/scumm/help.cpp:174 engines/scumm/help.cpp:226
@@ -6053,18 +6061,18 @@ msgstr "Ves"
 msgid "Talk"
 msgstr "Parla"
 
-#: engines/access/metaengine.cpp:323 engines/scumm/help.cpp:175
+#: engines/access/metaengine.cpp:344 engines/scumm/help.cpp:175
 msgid "Travel"
 msgstr "Viatja"
 
-#: engines/access/metaengine.cpp:334 engines/cine/metaengine.cpp:332
+#: engines/access/metaengine.cpp:355 engines/cine/metaengine.cpp:332
 #: engines/petka/metaengine.cpp:144 engines/saga/metaengine.cpp:394
 #: engines/supernova/metaengine.cpp:331
 msgid "Take"
 msgstr "Agafa"
 
 #. I18N: Action in In Cold Blood
-#: engines/access/metaengine.cpp:339 engines/alcachofa/metaengine.cpp:116
+#: engines/access/metaengine.cpp:360 engines/alcachofa/metaengine.cpp:116
 #: engines/cine/metaengine.cpp:337 engines/crab/input/input.cpp:251
 #: engines/cruise/metaengine.cpp:192 engines/draci/metaengine.cpp:237
 #: engines/dragons/metaengine.cpp:147 engines/drascula/metaengine.cpp:218
@@ -6084,18 +6092,18 @@ msgstr "Agafa"
 msgid "Inventory"
 msgstr "Inventari"
 
-#: engines/access/metaengine.cpp:344 engines/ultima/ultima4/metaengine.cpp:51
+#: engines/access/metaengine.cpp:365 engines/ultima/ultima4/metaengine.cpp:51
 msgid "Climb"
 msgstr "Escala"
 
-#: engines/access/metaengine.cpp:354 engines/bbvs/metaengine.cpp:142
+#: engines/access/metaengine.cpp:375 engines/bbvs/metaengine.cpp:142
 #: engines/freescape/games/castle/castle.cpp:511
 #: engines/petka/metaengine.cpp:137 engines/scumm/help.cpp:225
 #: engines/tsage/metaengine.cpp:201
 msgid "Walk"
 msgstr "Camina"
 
-#: engines/access/metaengine.cpp:365
+#: engines/access/metaengine.cpp:386
 msgid "Open save/load menu"
 msgstr "Obre el men?? de desar/carregar"
 
diff --git a/po/cs_CZ.po b/po/cs_CZ.po
index 5179056ce17..f634dcb8f07 100644
--- a/po/cs_CZ.po
+++ b/po/cs_CZ.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.7.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-17 23:07+0000\n"
+"POT-Creation-Date: 2026-06-18 09:25+0000\n"
 "PO-Revision-Date: 2026-02-09 20:22+0000\n"
 "Last-Translator: AsciiWolf <mail at asciiwolf.com>\n"
 "Language-Team: Czech <https://translations.scummvm.org/projects/scummvm/"
@@ -92,7 +92,7 @@ msgstr "Zav????t"
 #. I18N: Opens the help screen.
 #: gui/browser.cpp:64 gui/launcher.cpp:277 gui/launcher.cpp:959
 #: backends/platform/sdl/macosx/appmenu_osx.mm:319
-#: engines/access/metaengine.cpp:360 engines/bladerunner/metaengine.cpp:321
+#: engines/access/metaengine.cpp:381 engines/bladerunner/metaengine.cpp:321
 #: engines/ngi/metaengine.cpp:162 engines/supernova/metaengine.cpp:238
 #: engines/tucker/metaengine.cpp:215 engines/zvision/metaengine.cpp:246
 msgid "Help"
@@ -2666,7 +2666,7 @@ msgid ""
 msgstr "P??idat Hru..."
 
 #: gui/options.cpp:2975 engines/engine.cpp:647
-#: engines/access/metaengine.cpp:298 engines/drascula/metaengine.cpp:255
+#: engines/access/metaengine.cpp:319 engines/drascula/metaengine.cpp:255
 #: engines/queen/metaengine.cpp:269 engines/sherlock/metaengine.cpp:410
 #: engines/ultima/nuvie/metaengine.cpp:71
 #, fuzzy
@@ -3969,7 +3969,7 @@ msgstr "Menu hry"
 
 #. I18N: Skipping cutscene plaback
 #. I18N: Skipping cutscene playback
-#: engines/metaengine.cpp:112 engines/access/metaengine.cpp:225
+#: engines/metaengine.cpp:112 engines/access/metaengine.cpp:246
 #: engines/composer/metaengine.cpp:126 engines/drascula/metaengine.cpp:224
 #: engines/efh/metaengine.cpp:379 engines/freescape/movement.cpp:93
 #: engines/grim/grim.cpp:533 engines/grim/grim.cpp:633
@@ -6050,7 +6050,15 @@ msgstr "Spou??t????"
 msgid "Check for Updates..."
 msgstr "Zkontrolovat Aktualizace..."
 
-#: engines/access/metaengine.cpp:215 engines/alcachofa/metaengine.cpp:94
+#: engines/access/metaengine.cpp:82
+msgid "Use 'high definition' OGG music"
+msgstr ""
+
+#: engines/access/metaengine.cpp:83
+msgid "Use the OGG audio from the re-release instead of original MIDI tracks"
+msgstr ""
+
+#: engines/access/metaengine.cpp:236 engines/alcachofa/metaengine.cpp:94
 #: engines/bbvs/metaengine.cpp:92 engines/buried/metaengine.cpp:133
 #: engines/chamber/metaengine.cpp:59 engines/cine/metaengine.cpp:270
 #: engines/cruise/metaengine.cpp:168 engines/dm/metaengine.cpp:124
@@ -6076,20 +6084,20 @@ msgstr "Zkontrolovat Aktualizace..."
 msgid "Default keymappings"
 msgstr "V??choz?? rozlo??en?? kl??ves"
 
-#: engines/access/metaengine.cpp:219
+#: engines/access/metaengine.cpp:240
 #, fuzzy
 #| msgid "Highlight next dialogue"
 msgid "Move / Interact / Skip"
 msgstr "Zv??raznit n??sleduj??c?? dialog"
 
-#: engines/access/metaengine.cpp:231 engines/sherlock/metaengine.cpp:483
+#: engines/access/metaengine.cpp:252 engines/sherlock/metaengine.cpp:483
 #, fuzzy
 #| msgid "Skip line"
 msgid "Skip movie"
 msgstr "P??esko??it ????dek"
 
 #. I18N: Move Actor to upwards direction
-#: engines/access/metaengine.cpp:237 engines/asylum/metaengine.cpp:171
+#: engines/access/metaengine.cpp:258 engines/asylum/metaengine.cpp:171
 #: engines/asylum/metaengine.cpp:198 engines/cine/metaengine.cpp:377
 #: engines/efh/metaengine.cpp:286 engines/hdb/metaengine.cpp:213
 #: engines/hypno/metaengine.cpp:363 engines/hypno/metaengine.cpp:411
@@ -6100,7 +6108,7 @@ msgid "Move up"
 msgstr "Vzad"
 
 #. I18N: Move Actor to downwards direction
-#: engines/access/metaengine.cpp:244 engines/asylum/metaengine.cpp:177
+#: engines/access/metaengine.cpp:265 engines/asylum/metaengine.cpp:177
 #: engines/asylum/metaengine.cpp:205 engines/cine/metaengine.cpp:384
 #: engines/efh/metaengine.cpp:293 engines/hdb/metaengine.cpp:219
 #: engines/hypno/metaengine.cpp:351 engines/hypno/metaengine.cpp:399
@@ -6112,7 +6120,7 @@ msgstr "Zru??it stahov??n??"
 
 #. I18N: Move Actor to left direction
 #. I18N: Move actor left during fight
-#: engines/access/metaengine.cpp:251 engines/asylum/metaengine.cpp:183
+#: engines/access/metaengine.cpp:272 engines/asylum/metaengine.cpp:183
 #: engines/asylum/metaengine.cpp:212 engines/cine/metaengine.cpp:391
 #: engines/efh/metaengine.cpp:300 engines/hdb/metaengine.cpp:225
 #: engines/hypno/metaengine.cpp:345 engines/hypno/metaengine.cpp:393
@@ -6126,7 +6134,7 @@ msgstr "P??esunout se doleva"
 
 #. I18N: Move Actor to right direction
 #. I18N: Move actor right during fight
-#: engines/access/metaengine.cpp:258 engines/asylum/metaengine.cpp:189
+#: engines/access/metaengine.cpp:279 engines/asylum/metaengine.cpp:189
 #: engines/asylum/metaengine.cpp:219 engines/cine/metaengine.cpp:398
 #: engines/efh/metaengine.cpp:307 engines/hdb/metaengine.cpp:231
 #: engines/hypno/metaengine.cpp:357 engines/hypno/metaengine.cpp:405
@@ -6139,35 +6147,35 @@ msgid "Move right"
 msgstr "P??esunout se doprava"
 
 #. I18N: Move Actor to top-left direction
-#: engines/access/metaengine.cpp:265 engines/cine/metaengine.cpp:405
+#: engines/access/metaengine.cpp:286 engines/cine/metaengine.cpp:405
 #: engines/efh/metaengine.cpp:314
 #, fuzzy
 msgid "Move up-left"
 msgstr "P??esunout se doleva"
 
 #. I18N: Move Actor to top-right direction
-#: engines/access/metaengine.cpp:270 engines/cine/metaengine.cpp:411
+#: engines/access/metaengine.cpp:291 engines/cine/metaengine.cpp:411
 #: engines/efh/metaengine.cpp:320
 #, fuzzy
 msgid "Move up-right"
 msgstr "P??esunout se doprava"
 
 #. I18N: Move Actor to bottom-left direction
-#: engines/access/metaengine.cpp:275 engines/cine/metaengine.cpp:417
+#: engines/access/metaengine.cpp:296 engines/cine/metaengine.cpp:417
 #: engines/efh/metaengine.cpp:326
 #, fuzzy
 msgid "Move down-left"
 msgstr "Zru??it stahov??n??"
 
 #. I18N: Move Actor to bottom-right direction
-#: engines/access/metaengine.cpp:280 engines/cine/metaengine.cpp:423
+#: engines/access/metaengine.cpp:301 engines/cine/metaengine.cpp:423
 #: engines/efh/metaengine.cpp:332
 #, fuzzy
 msgid "Move down-right"
 msgstr "P??esunout se doprava"
 
 #. I18N: (Game name: The Lost Files of Sherlock Holmes: The Case of the Rose Tattoo) This action is used to look at an object or npc in the game
-#: engines/access/metaengine.cpp:285 engines/bbvs/metaengine.cpp:124
+#: engines/access/metaengine.cpp:306 engines/bbvs/metaengine.cpp:124
 #: engines/dgds/metaengine.cpp:83 engines/drascula/metaengine.cpp:230
 #: engines/petka/metaengine.cpp:130 engines/scumm/help.cpp:167
 #: engines/scumm/help.cpp:231 engines/sherlock/metaengine.cpp:405
@@ -6178,7 +6186,7 @@ msgstr "P??esunout se doprava"
 msgid "Look"
 msgstr "D??vat se"
 
-#: engines/access/metaengine.cpp:293 engines/drascula/metaengine.cpp:240
+#: engines/access/metaengine.cpp:314 engines/drascula/metaengine.cpp:240
 #: engines/queen/metaengine.cpp:259 engines/saga/metaengine.cpp:369
 #: engines/scumm/help.cpp:125 engines/scumm/help.cpp:140
 #: engines/scumm/help.cpp:165 engines/scumm/help.cpp:193
@@ -6188,13 +6196,13 @@ msgstr "D??vat se"
 msgid "Open"
 msgstr "Otev????t"
 
-#: engines/access/metaengine.cpp:303 engines/scumm/help.cpp:128
+#: engines/access/metaengine.cpp:324 engines/scumm/help.cpp:128
 #: engines/ultima/nuvie/metaengine.cpp:70
 msgid "Get"
 msgstr "Vz??t"
 
 #. I18N: Combat command in Might & Magic 1
-#: engines/access/metaengine.cpp:308 engines/access/metaengine.cpp:329
+#: engines/access/metaengine.cpp:329 engines/access/metaengine.cpp:350
 #: engines/bbvs/metaengine.cpp:136 engines/cine/metaengine.cpp:342
 #: engines/efh/metaengine.cpp:462 engines/hdb/metaengine.cpp:237
 #: engines/lab/metaengine.cpp:323 engines/mm/mm1/metaengine.cpp:86
@@ -6214,11 +6222,11 @@ msgstr "Vz??t"
 msgid "Use"
 msgstr "Pou????t"
 
-#: engines/access/metaengine.cpp:313
+#: engines/access/metaengine.cpp:334
 msgid "Goto"
 msgstr ""
 
-#: engines/access/metaengine.cpp:318 engines/access/metaengine.cpp:349
+#: engines/access/metaengine.cpp:339 engines/access/metaengine.cpp:370
 #: engines/bbvs/metaengine.cpp:130 engines/drascula/metaengine.cpp:250
 #: engines/efh/metaengine.cpp:506 engines/petka/metaengine.cpp:158
 #: engines/scumm/help.cpp:174 engines/scumm/help.cpp:226
@@ -6229,11 +6237,11 @@ msgstr ""
 msgid "Talk"
 msgstr "Mluvit"
 
-#: engines/access/metaengine.cpp:323 engines/scumm/help.cpp:175
+#: engines/access/metaengine.cpp:344 engines/scumm/help.cpp:175
 msgid "Travel"
 msgstr "Cestovat"
 
-#: engines/access/metaengine.cpp:334 engines/cine/metaengine.cpp:332
+#: engines/access/metaengine.cpp:355 engines/cine/metaengine.cpp:332
 #: engines/petka/metaengine.cpp:144 engines/saga/metaengine.cpp:394
 #: engines/supernova/metaengine.cpp:331
 #, fuzzy
@@ -6242,7 +6250,7 @@ msgid "Take"
 msgstr "Svl??ct"
 
 #. I18N: Action in In Cold Blood
-#: engines/access/metaengine.cpp:339 engines/alcachofa/metaengine.cpp:116
+#: engines/access/metaengine.cpp:360 engines/alcachofa/metaengine.cpp:116
 #: engines/cine/metaengine.cpp:337 engines/crab/input/input.cpp:251
 #: engines/cruise/metaengine.cpp:192 engines/draci/metaengine.cpp:237
 #: engines/dragons/metaengine.cpp:147 engines/drascula/metaengine.cpp:218
@@ -6262,18 +6270,18 @@ msgstr "Svl??ct"
 msgid "Inventory"
 msgstr "Invent????"
 
-#: engines/access/metaengine.cpp:344 engines/ultima/ultima4/metaengine.cpp:51
+#: engines/access/metaengine.cpp:365 engines/ultima/ultima4/metaengine.cpp:51
 msgid "Climb"
 msgstr ""
 
-#: engines/access/metaengine.cpp:354 engines/bbvs/metaengine.cpp:142
+#: engines/access/metaengine.cpp:375 engines/bbvs/metaengine.cpp:142
 #: engines/freescape/games/castle/castle.cpp:511
 #: engines/petka/metaengine.cpp:137 engines/scumm/help.cpp:225
 #: engines/tsage/metaengine.cpp:201
 msgid "Walk"
 msgstr "J??t"
 
-#: engines/access/metaengine.cpp:365
+#: engines/access/metaengine.cpp:386
 #, fuzzy
 msgid "Open save/load menu"
 msgstr "Vlo??te hern?? disk pro ulo??en??/na??ten??"
diff --git a/po/da.po b/po/da.po
index c4361aeee3b..c8c69093966 100644
--- a/po/da.po
+++ b/po/da.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-17 23:07+0000\n"
+"POT-Creation-Date: 2026-06-18 09:25+0000\n"
 "PO-Revision-Date: 2023-09-07 17:04+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Danish <https://translations.scummvm.org/projects/scummvm/"
@@ -90,7 +90,7 @@ msgstr "Luk"
 #. I18N: Opens the help screen.
 #: gui/browser.cpp:64 gui/launcher.cpp:277 gui/launcher.cpp:959
 #: backends/platform/sdl/macosx/appmenu_osx.mm:319
-#: engines/access/metaengine.cpp:360 engines/bladerunner/metaengine.cpp:321
+#: engines/access/metaengine.cpp:381 engines/bladerunner/metaengine.cpp:321
 #: engines/ngi/metaengine.cpp:162 engines/supernova/metaengine.cpp:238
 #: engines/tucker/metaengine.cpp:215 engines/zvision/metaengine.cpp:246
 msgid "Help"
@@ -2676,7 +2676,7 @@ msgstr ""
 "Og mere..."
 
 #: gui/options.cpp:2975 engines/engine.cpp:647
-#: engines/access/metaengine.cpp:298 engines/drascula/metaengine.cpp:255
+#: engines/access/metaengine.cpp:319 engines/drascula/metaengine.cpp:255
 #: engines/queen/metaengine.cpp:269 engines/sherlock/metaengine.cpp:410
 #: engines/ultima/nuvie/metaengine.cpp:71
 msgid "Move"
@@ -3980,7 +3980,7 @@ msgstr "Spilmenu"
 
 #. I18N: Skipping cutscene plaback
 #. I18N: Skipping cutscene playback
-#: engines/metaengine.cpp:112 engines/access/metaengine.cpp:225
+#: engines/metaengine.cpp:112 engines/access/metaengine.cpp:246
 #: engines/composer/metaengine.cpp:126 engines/drascula/metaengine.cpp:224
 #: engines/efh/metaengine.cpp:379 engines/freescape/movement.cpp:93
 #: engines/grim/grim.cpp:533 engines/grim/grim.cpp:633
@@ -6069,7 +6069,15 @@ msgstr "Oversigt"
 msgid "Check for Updates..."
 msgstr "S??g efter opdateringer ..."
 
-#: engines/access/metaengine.cpp:215 engines/alcachofa/metaengine.cpp:94
+#: engines/access/metaengine.cpp:82
+msgid "Use 'high definition' OGG music"
+msgstr ""
+
+#: engines/access/metaengine.cpp:83
+msgid "Use the OGG audio from the re-release instead of original MIDI tracks"
+msgstr ""
+
+#: engines/access/metaengine.cpp:236 engines/alcachofa/metaengine.cpp:94
 #: engines/bbvs/metaengine.cpp:92 engines/buried/metaengine.cpp:133
 #: engines/chamber/metaengine.cpp:59 engines/cine/metaengine.cpp:270
 #: engines/cruise/metaengine.cpp:168 engines/dm/metaengine.cpp:124
@@ -6094,20 +6102,20 @@ msgstr "S??g efter opdateringer ..."
 msgid "Default keymappings"
 msgstr "~I~ndstillinger ..."
 
-#: engines/access/metaengine.cpp:219
+#: engines/access/metaengine.cpp:240
 #, fuzzy
 #| msgid "Highlight next dialogue"
 msgid "Move / Interact / Skip"
 msgstr "Fremh??v n??ste dialog"
 
-#: engines/access/metaengine.cpp:231 engines/sherlock/metaengine.cpp:483
+#: engines/access/metaengine.cpp:252 engines/sherlock/metaengine.cpp:483
 #, fuzzy
 #| msgid "Skip line"
 msgid "Skip movie"
 msgstr "Spring linje over"
 
 #. I18N: Move Actor to upwards direction
-#: engines/access/metaengine.cpp:237 engines/asylum/metaengine.cpp:171
+#: engines/access/metaengine.cpp:258 engines/asylum/metaengine.cpp:171
 #: engines/asylum/metaengine.cpp:198 engines/cine/metaengine.cpp:377
 #: engines/efh/metaengine.cpp:286 engines/hdb/metaengine.cpp:213
 #: engines/hypno/metaengine.cpp:363 engines/hypno/metaengine.cpp:411
@@ -6118,7 +6126,7 @@ msgid "Move up"
 msgstr "Flyt bagud"
 
 #. I18N: Move Actor to downwards direction
-#: engines/access/metaengine.cpp:244 engines/asylum/metaengine.cpp:177
+#: engines/access/metaengine.cpp:265 engines/asylum/metaengine.cpp:177
 #: engines/asylum/metaengine.cpp:205 engines/cine/metaengine.cpp:384
 #: engines/efh/metaengine.cpp:293 engines/hdb/metaengine.cpp:219
 #: engines/hypno/metaengine.cpp:351 engines/hypno/metaengine.cpp:399
@@ -6130,7 +6138,7 @@ msgstr "Annuller download"
 
 #. I18N: Move Actor to left direction
 #. I18N: Move actor left during fight
-#: engines/access/metaengine.cpp:251 engines/asylum/metaengine.cpp:183
+#: engines/access/metaengine.cpp:272 engines/asylum/metaengine.cpp:183
 #: engines/asylum/metaengine.cpp:212 engines/cine/metaengine.cpp:391
 #: engines/efh/metaengine.cpp:300 engines/hdb/metaengine.cpp:225
 #: engines/hypno/metaengine.cpp:345 engines/hypno/metaengine.cpp:393
@@ -6144,7 +6152,7 @@ msgstr "Flyt til venstre"
 
 #. I18N: Move Actor to right direction
 #. I18N: Move actor right during fight
-#: engines/access/metaengine.cpp:258 engines/asylum/metaengine.cpp:189
+#: engines/access/metaengine.cpp:279 engines/asylum/metaengine.cpp:189
 #: engines/asylum/metaengine.cpp:219 engines/cine/metaengine.cpp:398
 #: engines/efh/metaengine.cpp:307 engines/hdb/metaengine.cpp:231
 #: engines/hypno/metaengine.cpp:357 engines/hypno/metaengine.cpp:405
@@ -6157,35 +6165,35 @@ msgid "Move right"
 msgstr "Flyt til h??jre"
 
 #. I18N: Move Actor to top-left direction
-#: engines/access/metaengine.cpp:265 engines/cine/metaengine.cpp:405
+#: engines/access/metaengine.cpp:286 engines/cine/metaengine.cpp:405
 #: engines/efh/metaengine.cpp:314
 #, fuzzy
 msgid "Move up-left"
 msgstr "Flyt til venstre"
 
 #. I18N: Move Actor to top-right direction
-#: engines/access/metaengine.cpp:270 engines/cine/metaengine.cpp:411
+#: engines/access/metaengine.cpp:291 engines/cine/metaengine.cpp:411
 #: engines/efh/metaengine.cpp:320
 #, fuzzy
 msgid "Move up-right"
 msgstr "Flyt til h??jre"
 
 #. I18N: Move Actor to bottom-left direction
-#: engines/access/metaengine.cpp:275 engines/cine/metaengine.cpp:417
+#: engines/access/metaengine.cpp:296 engines/cine/metaengine.cpp:417
 #: engines/efh/metaengine.cpp:326
 #, fuzzy
 msgid "Move down-left"
 msgstr "Annuller download"
 
 #. I18N: Move Actor to bottom-right direction
-#: engines/access/metaengine.cpp:280 engines/cine/metaengine.cpp:423
+#: engines/access/metaengine.cpp:301 engines/cine/metaengine.cpp:423
 #: engines/efh/metaengine.cpp:332
 #, fuzzy
 msgid "Move down-right"
 msgstr "Flyt til h??jre"
 
 #. I18N: (Game name: The Lost Files of Sherlock Holmes: The Case of the Rose Tattoo) This action is used to look at an object or npc in the game
-#: engines/access/metaengine.cpp:285 engines/bbvs/metaengine.cpp:124
+#: engines/access/metaengine.cpp:306 engines/bbvs/metaengine.cpp:124
 #: engines/dgds/metaengine.cpp:83 engines/drascula/metaengine.cpp:230
 #: engines/petka/metaengine.cpp:130 engines/scumm/help.cpp:167
 #: engines/scumm/help.cpp:231 engines/sherlock/metaengine.cpp:405
@@ -6196,7 +6204,7 @@ msgstr "Flyt til h??jre"
 msgid "Look"
 msgstr "Se"
 
-#: engines/access/metaengine.cpp:293 engines/drascula/metaengine.cpp:240
+#: engines/access/metaengine.cpp:314 engines/drascula/metaengine.cpp:240
 #: engines/queen/metaengine.cpp:259 engines/saga/metaengine.cpp:369
 #: engines/scumm/help.cpp:125 engines/scumm/help.cpp:140
 #: engines/scumm/help.cpp:165 engines/scumm/help.cpp:193
@@ -6206,13 +6214,13 @@ msgstr "Se"
 msgid "Open"
 msgstr "??bn"
 
-#: engines/access/metaengine.cpp:303 engines/scumm/help.cpp:128
+#: engines/access/metaengine.cpp:324 engines/scumm/help.cpp:128
 #: engines/ultima/nuvie/metaengine.cpp:70
 msgid "Get"
 msgstr "Tag"
 
 #. I18N: Combat command in Might & Magic 1
-#: engines/access/metaengine.cpp:308 engines/access/metaengine.cpp:329
+#: engines/access/metaengine.cpp:329 engines/access/metaengine.cpp:350
 #: engines/bbvs/metaengine.cpp:136 engines/cine/metaengine.cpp:342
 #: engines/efh/metaengine.cpp:462 engines/hdb/metaengine.cpp:237
 #: engines/lab/metaengine.cpp:323 engines/mm/mm1/metaengine.cpp:86
@@ -6232,11 +6240,11 @@ msgstr "Tag"
 msgid "Use"
 msgstr "Brug"
 
-#: engines/access/metaengine.cpp:313
+#: engines/access/metaengine.cpp:334
 msgid "Goto"
 msgstr ""
 
-#: engines/access/metaengine.cpp:318 engines/access/metaengine.cpp:349
+#: engines/access/metaengine.cpp:339 engines/access/metaengine.cpp:370
 #: engines/bbvs/metaengine.cpp:130 engines/drascula/metaengine.cpp:250
 #: engines/efh/metaengine.cpp:506 engines/petka/metaengine.cpp:158
 #: engines/scumm/help.cpp:174 engines/scumm/help.cpp:226
@@ -6247,11 +6255,11 @@ msgstr ""
 msgid "Talk"
 msgstr "Tal"
 
-#: engines/access/metaengine.cpp:323 engines/scumm/help.cpp:175
+#: engines/access/metaengine.cpp:344 engines/scumm/help.cpp:175
 msgid "Travel"
 msgstr "Rejs"
 
-#: engines/access/metaengine.cpp:334 engines/cine/metaengine.cpp:332
+#: engines/access/metaengine.cpp:355 engines/cine/metaengine.cpp:332
 #: engines/petka/metaengine.cpp:144 engines/saga/metaengine.cpp:394
 #: engines/supernova/metaengine.cpp:331
 #, fuzzy
@@ -6260,7 +6268,7 @@ msgid "Take"
 msgstr "Tag af"
 
 #. I18N: Action in In Cold Blood
-#: engines/access/metaengine.cpp:339 engines/alcachofa/metaengine.cpp:116
+#: engines/access/metaengine.cpp:360 engines/alcachofa/metaengine.cpp:116
 #: engines/cine/metaengine.cpp:337 engines/crab/input/input.cpp:251
 #: engines/cruise/metaengine.cpp:192 engines/draci/metaengine.cpp:237
 #: engines/dragons/metaengine.cpp:147 engines/drascula/metaengine.cpp:218
@@ -6280,18 +6288,18 @@ msgstr "Tag af"
 msgid "Inventory"
 msgstr "Inventar"
 
-#: engines/access/metaengine.cpp:344 engines/ultima/ultima4/metaengine.cpp:51
+#: engines/access/metaengine.cpp:365 engines/ultima/ultima4/metaengine.cpp:51
 msgid "Climb"
 msgstr ""
 
-#: engines/access/metaengine.cpp:354 engines/bbvs/metaengine.cpp:142
+#: engines/access/metaengine.cpp:375 engines/bbvs/metaengine.cpp:142
 #: engines/freescape/games/castle/castle.cpp:511
 #: engines/petka/metaengine.cpp:137 engines/scumm/help.cpp:225
 #: engines/tsage/metaengine.cpp:201
 msgid "Walk"
 msgstr "G??"
 
-#: engines/access/metaengine.cpp:365
+#: engines/access/metaengine.cpp:386
 #, fuzzy
 msgid "Open save/load menu"
 msgstr "Inds??t gem/indl??s spil disk"
diff --git a/po/de_DE.po b/po/de_DE.po
index a865230efec..28a025b49de 100644
--- a/po/de_DE.po
+++ b/po/de_DE.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.10.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-17 23:07+0000\n"
+"POT-Creation-Date: 2026-06-18 09:25+0000\n"
 "PO-Revision-Date: 2026-05-05 16:31+0000\n"
 "Last-Translator: Lothar Serra Mari <lothar.serramari at scummvm.org>\n"
 "Language-Team: German <https://translations.scummvm.org/projects/scummvm/"
@@ -83,7 +83,7 @@ msgstr "Schlie??en"
 #. I18N: Opens the help screen.
 #: gui/browser.cpp:64 gui/launcher.cpp:277 gui/launcher.cpp:959
 #: backends/platform/sdl/macosx/appmenu_osx.mm:319
-#: engines/access/metaengine.cpp:360 engines/bladerunner/metaengine.cpp:321
+#: engines/access/metaengine.cpp:381 engines/bladerunner/metaengine.cpp:321
 #: engines/ngi/metaengine.cpp:162 engines/supernova/metaengine.cpp:238
 #: engines/tucker/metaengine.cpp:215 engines/zvision/metaengine.cpp:246
 msgid "Help"
@@ -2713,7 +2713,7 @@ msgstr ""
 "Und weitere..."
 
 #: gui/options.cpp:2975 engines/engine.cpp:647
-#: engines/access/metaengine.cpp:298 engines/drascula/metaengine.cpp:255
+#: engines/access/metaengine.cpp:319 engines/drascula/metaengine.cpp:255
 #: engines/queen/metaengine.cpp:269 engines/sherlock/metaengine.cpp:410
 #: engines/ultima/nuvie/metaengine.cpp:71
 msgid "Move"
@@ -4035,7 +4035,7 @@ msgstr "Spielmen??"
 
 #. I18N: Skipping cutscene plaback
 #. I18N: Skipping cutscene playback
-#: engines/metaengine.cpp:112 engines/access/metaengine.cpp:225
+#: engines/metaengine.cpp:112 engines/access/metaengine.cpp:246
 #: engines/composer/metaengine.cpp:126 engines/drascula/metaengine.cpp:224
 #: engines/efh/metaengine.cpp:379 engines/freescape/movement.cpp:93
 #: engines/grim/grim.cpp:533 engines/grim/grim.cpp:633
@@ -6353,7 +6353,15 @@ msgstr "Spieleliste"
 msgid "Check for Updates..."
 msgstr "Suche nach Aktualisierungen..."
 
-#: engines/access/metaengine.cpp:215 engines/alcachofa/metaengine.cpp:94
+#: engines/access/metaengine.cpp:82
+msgid "Use 'high definition' OGG music"
+msgstr ""
+
+#: engines/access/metaengine.cpp:83
+msgid "Use the OGG audio from the re-release instead of original MIDI tracks"
+msgstr ""
+
+#: engines/access/metaengine.cpp:236 engines/alcachofa/metaengine.cpp:94
 #: engines/bbvs/metaengine.cpp:92 engines/buried/metaengine.cpp:133
 #: engines/chamber/metaengine.cpp:59 engines/cine/metaengine.cpp:270
 #: engines/cruise/metaengine.cpp:168 engines/dm/metaengine.cpp:124
@@ -6377,16 +6385,16 @@ msgstr "Suche nach Aktualisierungen..."
 msgid "Default keymappings"
 msgstr "Standard-Tastenbelegungen"
 
-#: engines/access/metaengine.cpp:219
+#: engines/access/metaengine.cpp:240
 msgid "Move / Interact / Skip"
 msgstr "Bewegen / Interagieren / ??berspringen"
 
-#: engines/access/metaengine.cpp:231 engines/sherlock/metaengine.cpp:483
+#: engines/access/metaengine.cpp:252 engines/sherlock/metaengine.cpp:483
 msgid "Skip movie"
 msgstr "Film ??berspringen"
 
 #. I18N: Move Actor to upwards direction
-#: engines/access/metaengine.cpp:237 engines/asylum/metaengine.cpp:171
+#: engines/access/metaengine.cpp:258 engines/asylum/metaengine.cpp:171
 #: engines/asylum/metaengine.cpp:198 engines/cine/metaengine.cpp:377
 #: engines/efh/metaengine.cpp:286 engines/hdb/metaengine.cpp:213
 #: engines/hypno/metaengine.cpp:363 engines/hypno/metaengine.cpp:411
@@ -6396,7 +6404,7 @@ msgid "Move up"
 msgstr "Nach oben bewegen"
 
 #. I18N: Move Actor to downwards direction
-#: engines/access/metaengine.cpp:244 engines/asylum/metaengine.cpp:177
+#: engines/access/metaengine.cpp:265 engines/asylum/metaengine.cpp:177
 #: engines/asylum/metaengine.cpp:205 engines/cine/metaengine.cpp:384
 #: engines/efh/metaengine.cpp:293 engines/hdb/metaengine.cpp:219
 #: engines/hypno/metaengine.cpp:351 engines/hypno/metaengine.cpp:399
@@ -6407,7 +6415,7 @@ msgstr "Nach unten bewegen"
 
 #. I18N: Move Actor to left direction
 #. I18N: Move actor left during fight
-#: engines/access/metaengine.cpp:251 engines/asylum/metaengine.cpp:183
+#: engines/access/metaengine.cpp:272 engines/asylum/metaengine.cpp:183
 #: engines/asylum/metaengine.cpp:212 engines/cine/metaengine.cpp:391
 #: engines/efh/metaengine.cpp:300 engines/hdb/metaengine.cpp:225
 #: engines/hypno/metaengine.cpp:345 engines/hypno/metaengine.cpp:393
@@ -6420,7 +6428,7 @@ msgstr "Nach links bewegen"
 
 #. I18N: Move Actor to right direction
 #. I18N: Move actor right during fight
-#: engines/access/metaengine.cpp:258 engines/asylum/metaengine.cpp:189
+#: engines/access/metaengine.cpp:279 engines/asylum/metaengine.cpp:189
 #: engines/asylum/metaengine.cpp:219 engines/cine/metaengine.cpp:398
 #: engines/efh/metaengine.cpp:307 engines/hdb/metaengine.cpp:231
 #: engines/hypno/metaengine.cpp:357 engines/hypno/metaengine.cpp:405
@@ -6432,31 +6440,31 @@ msgid "Move right"
 msgstr "Nach rechts bewegen"
 
 #. I18N: Move Actor to top-left direction
-#: engines/access/metaengine.cpp:265 engines/cine/metaengine.cpp:405
+#: engines/access/metaengine.cpp:286 engines/cine/metaengine.cpp:405
 #: engines/efh/metaengine.cpp:314
 msgid "Move up-left"
 msgstr "Nach oben links bewegen"
 
 #. I18N: Move Actor to top-right direction
-#: engines/access/metaengine.cpp:270 engines/cine/metaengine.cpp:411
+#: engines/access/metaengine.cpp:291 engines/cine/metaengine.cpp:411
 #: engines/efh/metaengine.cpp:320
 msgid "Move up-right"
 msgstr "Nach oben rechts bewegen"
 
 #. I18N: Move Actor to bottom-left direction
-#: engines/access/metaengine.cpp:275 engines/cine/metaengine.cpp:417
+#: engines/access/metaengine.cpp:296 engines/cine/metaengine.cpp:417
 #: engines/efh/metaengine.cpp:326
 msgid "Move down-left"
 msgstr "Nach unten links bewegen"
 
 #. I18N: Move Actor to bottom-right direction
-#: engines/access/metaengine.cpp:280 engines/cine/metaengine.cpp:423
+#: engines/access/metaengine.cpp:301 engines/cine/metaengine.cpp:423
 #: engines/efh/metaengine.cpp:332
 msgid "Move down-right"
 msgstr "Nach unten rechts bewegen"
 
 #. I18N: (Game name: The Lost Files of Sherlock Holmes: The Case of the Rose Tattoo) This action is used to look at an object or npc in the game
-#: engines/access/metaengine.cpp:285 engines/bbvs/metaengine.cpp:124
+#: engines/access/metaengine.cpp:306 engines/bbvs/metaengine.cpp:124
 #: engines/dgds/metaengine.cpp:83 engines/drascula/metaengine.cpp:230
 #: engines/petka/metaengine.cpp:130 engines/scumm/help.cpp:167
 #: engines/scumm/help.cpp:231 engines/sherlock/metaengine.cpp:405
@@ -6467,7 +6475,7 @@ msgstr "Nach unten rechts bewegen"
 msgid "Look"
 msgstr "Schau"
 
-#: engines/access/metaengine.cpp:293 engines/drascula/metaengine.cpp:240
+#: engines/access/metaengine.cpp:314 engines/drascula/metaengine.cpp:240
 #: engines/queen/metaengine.cpp:259 engines/saga/metaengine.cpp:369
 #: engines/scumm/help.cpp:125 engines/scumm/help.cpp:140
 #: engines/scumm/help.cpp:165 engines/scumm/help.cpp:193
@@ -6477,13 +6485,13 @@ msgstr "Schau"
 msgid "Open"
 msgstr "??ffne"
 
-#: engines/access/metaengine.cpp:303 engines/scumm/help.cpp:128
+#: engines/access/metaengine.cpp:324 engines/scumm/help.cpp:128
 #: engines/ultima/nuvie/metaengine.cpp:70
 msgid "Get"
 msgstr "Nimm"
 
 #. I18N: Combat command in Might & Magic 1
-#: engines/access/metaengine.cpp:308 engines/access/metaengine.cpp:329
+#: engines/access/metaengine.cpp:329 engines/access/metaengine.cpp:350
 #: engines/bbvs/metaengine.cpp:136 engines/cine/metaengine.cpp:342
 #: engines/efh/metaengine.cpp:462 engines/hdb/metaengine.cpp:237
 #: engines/lab/metaengine.cpp:323 engines/mm/mm1/metaengine.cpp:86
@@ -6503,11 +6511,11 @@ msgstr "Nimm"
 msgid "Use"
 msgstr "Benutze"
 
-#: engines/access/metaengine.cpp:313
+#: engines/access/metaengine.cpp:334
 msgid "Goto"
 msgstr "Gehe zu"
 
-#: engines/access/metaengine.cpp:318 engines/access/metaengine.cpp:349
+#: engines/access/metaengine.cpp:339 engines/access/metaengine.cpp:370
 #: engines/bbvs/metaengine.cpp:130 engines/drascula/metaengine.cpp:250
 #: engines/efh/metaengine.cpp:506 engines/petka/metaengine.cpp:158
 #: engines/scumm/help.cpp:174 engines/scumm/help.cpp:226
@@ -6518,18 +6526,18 @@ msgstr "Gehe zu"
 msgid "Talk"
 msgstr "Rede"
 
-#: engines/access/metaengine.cpp:323 engines/scumm/help.cpp:175
+#: engines/access/metaengine.cpp:344 engines/scumm/help.cpp:175
 msgid "Travel"
 msgstr "Reise"
 
-#: engines/access/metaengine.cpp:334 engines/cine/metaengine.cpp:332
+#: engines/access/metaengine.cpp:355 engines/cine/metaengine.cpp:332
 #: engines/petka/metaengine.cpp:144 engines/saga/metaengine.cpp:394
 #: engines/supernova/metaengine.cpp:331
 msgid "Take"
 msgstr "Nehmen"
 
 #. I18N: Action in In Cold Blood
-#: engines/access/metaengine.cpp:339 engines/alcachofa/metaengine.cpp:116
+#: engines/access/metaengine.cpp:360 engines/alcachofa/metaengine.cpp:116
 #: engines/cine/metaengine.cpp:337 engines/crab/input/input.cpp:251
 #: engines/cruise/metaengine.cpp:192 engines/draci/metaengine.cpp:237
 #: engines/dragons/metaengine.cpp:147 engines/drascula/metaengine.cpp:218
@@ -6549,18 +6557,18 @@ msgstr "Nehmen"
 msgid "Inventory"
 msgstr "Inventar"
 
-#: engines/access/metaengine.cpp:344 engines/ultima/ultima4/metaengine.cpp:51
+#: engines/access/metaengine.cpp:365 engines/ultima/ultima4/metaengine.cpp:51
 msgid "Climb"
 msgstr "Klettern"
 
-#: engines/access/metaengine.cpp:354 engines/bbvs/metaengine.cpp:142
+#: engines/access/metaengine.cpp:375 engines/bbvs/metaengine.cpp:142
 #: engines/freescape/games/castle/castle.cpp:511
 #: engines/petka/metaengine.cpp:137 engines/scumm/help.cpp:225
 #: engines/tsage/metaengine.cpp:201
 msgid "Walk"
 msgstr "Gehe"
 
-#: engines/access/metaengine.cpp:365
+#: engines/access/metaengine.cpp:386
 msgid "Open save/load menu"
 msgstr "Men?? \"Speichern/Laden??? ??ffnen"
 
diff --git a/po/el.po b/po/el.po
index a14a55dcb82..6822cfae29d 100644
--- a/po/el.po
+++ b/po/el.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.10.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-17 23:07+0000\n"
+"POT-Creation-Date: 2026-06-18 09:25+0000\n"
 "PO-Revision-Date: 2026-04-23 00:22+0000\n"
 "Last-Translator: Antoniou Athanasios <a.antoniou79 at gmail.com>\n"
 "Language-Team: Greek <https://translations.scummvm.org/projects/scummvm/"
@@ -82,7 +82,7 @@ msgstr "????????????????"
 #. I18N: Opens the help screen.
 #: gui/browser.cpp:64 gui/launcher.cpp:277 gui/launcher.cpp:959
 #: backends/platform/sdl/macosx/appmenu_osx.mm:319
-#: engines/access/metaengine.cpp:360 engines/bladerunner/metaengine.cpp:321
+#: engines/access/metaengine.cpp:381 engines/bladerunner/metaengine.cpp:321
 #: engines/ngi/metaengine.cpp:162 engines/supernova/metaengine.cpp:238
 #: engines/tucker/metaengine.cpp:215 engines/zvision/metaengine.cpp:246
 msgid "Help"
@@ -2755,7 +2755,7 @@ msgstr ""
 "?????? ????????..."
 
 #: gui/options.cpp:2975 engines/engine.cpp:647
-#: engines/access/metaengine.cpp:298 engines/drascula/metaengine.cpp:255
+#: engines/access/metaengine.cpp:319 engines/drascula/metaengine.cpp:255
 #: engines/queen/metaengine.cpp:269 engines/sherlock/metaengine.cpp:410
 #: engines/ultima/nuvie/metaengine.cpp:71
 msgid "Move"
@@ -4106,7 +4106,7 @@ msgstr "?????????? ????????????????????"
 
 #. I18N: Skipping cutscene plaback
 #. I18N: Skipping cutscene playback
-#: engines/metaengine.cpp:112 engines/access/metaengine.cpp:225
+#: engines/metaengine.cpp:112 engines/access/metaengine.cpp:246
 #: engines/composer/metaengine.cpp:126 engines/drascula/metaengine.cpp:224
 #: engines/efh/metaengine.cpp:379 engines/freescape/movement.cpp:93
 #: engines/grim/grim.cpp:533 engines/grim/grim.cpp:633
@@ -6477,7 +6477,15 @@ msgstr "??????????????????"
 msgid "Check for Updates..."
 msgstr "?????????????? ??????????????????????..."
 
-#: engines/access/metaengine.cpp:215 engines/alcachofa/metaengine.cpp:94
+#: engines/access/metaengine.cpp:82
+msgid "Use 'high definition' OGG music"
+msgstr ""
+
+#: engines/access/metaengine.cpp:83
+msgid "Use the OGG audio from the re-release instead of original MIDI tracks"
+msgstr ""
+
+#: engines/access/metaengine.cpp:236 engines/alcachofa/metaengine.cpp:94
 #: engines/bbvs/metaengine.cpp:92 engines/buried/metaengine.cpp:133
 #: engines/chamber/metaengine.cpp:59 engines/cine/metaengine.cpp:270
 #: engines/cruise/metaengine.cpp:168 engines/dm/metaengine.cpp:124
@@ -6501,16 +6509,16 @@ msgstr "?????????????? ??????????????????????..."
 msgid "Default keymappings"
 msgstr "?????????????????????????????? ????????????????????????????"
 
-#: engines/access/metaengine.cpp:219
+#: engines/access/metaengine.cpp:240
 msgid "Move / Interact / Skip"
 msgstr "???????????????????? / ?????????????????????????? / ??????????????????"
 
-#: engines/access/metaengine.cpp:231 engines/sherlock/metaengine.cpp:483
+#: engines/access/metaengine.cpp:252 engines/sherlock/metaengine.cpp:483
 msgid "Skip movie"
 msgstr "?????????????????? ??????????????"
 
 #. I18N: Move Actor to upwards direction
-#: engines/access/metaengine.cpp:237 engines/asylum/metaengine.cpp:171
+#: engines/access/metaengine.cpp:258 engines/asylum/metaengine.cpp:171
 #: engines/asylum/metaengine.cpp:198 engines/cine/metaengine.cpp:377
 #: engines/efh/metaengine.cpp:286 engines/hdb/metaengine.cpp:213
 #: engines/hypno/metaengine.cpp:363 engines/hypno/metaengine.cpp:411
@@ -6520,7 +6528,7 @@ msgid "Move up"
 msgstr "???????????? ????????"
 
 #. I18N: Move Actor to downwards direction
-#: engines/access/metaengine.cpp:244 engines/asylum/metaengine.cpp:177
+#: engines/access/metaengine.cpp:265 engines/asylum/metaengine.cpp:177
 #: engines/asylum/metaengine.cpp:205 engines/cine/metaengine.cpp:384
 #: engines/efh/metaengine.cpp:293 engines/hdb/metaengine.cpp:219
 #: engines/hypno/metaengine.cpp:351 engines/hypno/metaengine.cpp:399
@@ -6531,7 +6539,7 @@ msgstr "???????????? ????????"
 
 #. I18N: Move Actor to left direction
 #. I18N: Move actor left during fight
-#: engines/access/metaengine.cpp:251 engines/asylum/metaengine.cpp:183
+#: engines/access/metaengine.cpp:272 engines/asylum/metaengine.cpp:183
 #: engines/asylum/metaengine.cpp:212 engines/cine/metaengine.cpp:391
 #: engines/efh/metaengine.cpp:300 engines/hdb/metaengine.cpp:225
 #: engines/hypno/metaengine.cpp:345 engines/hypno/metaengine.cpp:393
@@ -6544,7 +6552,7 @@ msgstr "???????????? ????????????????"
 
 #. I18N: Move Actor to right direction
 #. I18N: Move actor right during fight
-#: engines/access/metaengine.cpp:258 engines/asylum/metaengine.cpp:189
+#: engines/access/metaengine.cpp:279 engines/asylum/metaengine.cpp:189
 #: engines/asylum/metaengine.cpp:219 engines/cine/metaengine.cpp:398
 #: engines/efh/metaengine.cpp:307 engines/hdb/metaengine.cpp:231
 #: engines/hypno/metaengine.cpp:357 engines/hypno/metaengine.cpp:405
@@ -6556,31 +6564,31 @@ msgid "Move right"
 msgstr "???????????? ??????????"
 
 #. I18N: Move Actor to top-left direction
-#: engines/access/metaengine.cpp:265 engines/cine/metaengine.cpp:405
+#: engines/access/metaengine.cpp:286 engines/cine/metaengine.cpp:405
 #: engines/efh/metaengine.cpp:314
 msgid "Move up-left"
 msgstr "???????????? ????????-????????????????"
 
 #. I18N: Move Actor to top-right direction
-#: engines/access/metaengine.cpp:270 engines/cine/metaengine.cpp:411
+#: engines/access/metaengine.cpp:291 engines/cine/metaengine.cpp:411
 #: engines/efh/metaengine.cpp:320
 msgid "Move up-right"
 msgstr "???????????? ????????-??????????"
 
 #. I18N: Move Actor to bottom-left direction
-#: engines/access/metaengine.cpp:275 engines/cine/metaengine.cpp:417
+#: engines/access/metaengine.cpp:296 engines/cine/metaengine.cpp:417
 #: engines/efh/metaengine.cpp:326
 msgid "Move down-left"
 msgstr "???????????? ????????-????????????????"
 
 #. I18N: Move Actor to bottom-right direction
-#: engines/access/metaengine.cpp:280 engines/cine/metaengine.cpp:423
+#: engines/access/metaengine.cpp:301 engines/cine/metaengine.cpp:423
 #: engines/efh/metaengine.cpp:332
 msgid "Move down-right"
 msgstr "???????????? ????????-??????????"
 
 #. I18N: (Game name: The Lost Files of Sherlock Holmes: The Case of the Rose Tattoo) This action is used to look at an object or npc in the game
-#: engines/access/metaengine.cpp:285 engines/bbvs/metaengine.cpp:124
+#: engines/access/metaengine.cpp:306 engines/bbvs/metaengine.cpp:124
 #: engines/dgds/metaengine.cpp:83 engines/drascula/metaengine.cpp:230
 #: engines/petka/metaengine.cpp:130 engines/scumm/help.cpp:167
 #: engines/scumm/help.cpp:231 engines/sherlock/metaengine.cpp:405
@@ -6591,7 +6599,7 @@ msgstr "???????????? ????????-??????????"
 msgid "Look"
 msgstr "??????????????"
 
-#: engines/access/metaengine.cpp:293 engines/drascula/metaengine.cpp:240
+#: engines/access/metaengine.cpp:314 engines/drascula/metaengine.cpp:240
 #: engines/queen/metaengine.cpp:259 engines/saga/metaengine.cpp:369
 #: engines/scumm/help.cpp:125 engines/scumm/help.cpp:140
 #: engines/scumm/help.cpp:165 engines/scumm/help.cpp:193
@@ -6601,13 +6609,13 @@ msgstr "??????????????"
 msgid "Open"
 msgstr "????????????"
 
-#: engines/access/metaengine.cpp:303 engines/scumm/help.cpp:128
+#: engines/access/metaengine.cpp:324 engines/scumm/help.cpp:128
 #: engines/ultima/nuvie/metaengine.cpp:70
 msgid "Get"
 msgstr "????????"
 
 #. I18N: Combat command in Might & Magic 1
-#: engines/access/metaengine.cpp:308 engines/access/metaengine.cpp:329
+#: engines/access/metaengine.cpp:329 engines/access/metaengine.cpp:350
 #: engines/bbvs/metaengine.cpp:136 engines/cine/metaengine.cpp:342
 #: engines/efh/metaengine.cpp:462 engines/hdb/metaengine.cpp:237
 #: engines/lab/metaengine.cpp:323 engines/mm/mm1/metaengine.cpp:86
@@ -6627,11 +6635,11 @@ msgstr "????????"
 msgid "Use"
 msgstr "??????????"
 
-#: engines/access/metaengine.cpp:313
+#: engines/access/metaengine.cpp:334
 msgid "Goto"
 msgstr "???????? ????????"
 
-#: engines/access/metaengine.cpp:318 engines/access/metaengine.cpp:349
+#: engines/access/metaengine.cpp:339 engines/access/metaengine.cpp:370
 #: engines/bbvs/metaengine.cpp:130 engines/drascula/metaengine.cpp:250
 #: engines/efh/metaengine.cpp:506 engines/petka/metaengine.cpp:158
 #: engines/scumm/help.cpp:174 engines/scumm/help.cpp:226
@@ -6642,18 +6650,18 @@ msgstr "???????? ????????"
 msgid "Talk"
 msgstr "????????"
 
-#: engines/access/metaengine.cpp:323 engines/scumm/help.cpp:175
+#: engines/access/metaengine.cpp:344 engines/scumm/help.cpp:175
 msgid "Travel"
 msgstr "????????????????"
 
-#: engines/access/metaengine.cpp:334 engines/cine/metaengine.cpp:332
+#: engines/access/metaengine.cpp:355 engines/cine/metaengine.cpp:332
 #: engines/petka/metaengine.cpp:144 engines/saga/metaengine.cpp:394
 #: engines/supernova/metaengine.cpp:331
 msgid "Take"
 msgstr "????????"
 
 #. I18N: Action in In Cold Blood
-#: engines/access/metaengine.cpp:339 engines/alcachofa/metaengine.cpp:116
+#: engines/access/metaengine.cpp:360 engines/alcachofa/metaengine.cpp:116
 #: engines/cine/metaengine.cpp:337 engines/crab/input/input.cpp:251
 #: engines/cruise/metaengine.cpp:192 engines/draci/metaengine.cpp:237
 #: engines/dragons/metaengine.cpp:147 engines/drascula/metaengine.cpp:218
@@ -6673,18 +6681,18 @@ msgstr "????????"
 msgid "Inventory"
 msgstr "??????????????????"
 
-#: engines/access/metaengine.cpp:344 engines/ultima/ultima4/metaengine.cpp:51
+#: engines/access/metaengine.cpp:365 engines/ultima/ultima4/metaengine.cpp:51
 msgid "Climb"
 msgstr "????????????????????"
 
-#: engines/access/metaengine.cpp:354 engines/bbvs/metaengine.cpp:142
+#: engines/access/metaengine.cpp:375 engines/bbvs/metaengine.cpp:142
 #: engines/freescape/games/castle/castle.cpp:511
 #: engines/petka/metaengine.cpp:137 engines/scumm/help.cpp:225
 #: engines/tsage/metaengine.cpp:201
 msgid "Walk"
 msgstr "??????????????????"
 
-#: engines/access/metaengine.cpp:365
+#: engines/access/metaengine.cpp:386
 msgid "Open save/load menu"
 msgstr "?????????????? ?????????? ??????????????????????/????????????????"
 
diff --git a/po/es_ES.po b/po/es_ES.po
index 31de6979050..66e31fe033c 100644
--- a/po/es_ES.po
+++ b/po/es_ES.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.4.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-17 23:07+0000\n"
+"POT-Creation-Date: 2026-06-18 09:25+0000\n"
 "PO-Revision-Date: 2026-06-14 09:53+0000\n"
 "Last-Translator: IlDucci <pajaroloco_2 at hotmail.com>\n"
 "Language-Team: Spanish <https://translations.scummvm.org/projects/scummvm/"
@@ -83,7 +83,7 @@ msgstr "Cerrar"
 #. I18N: Opens the help screen.
 #: gui/browser.cpp:64 gui/launcher.cpp:277 gui/launcher.cpp:959
 #: backends/platform/sdl/macosx/appmenu_osx.mm:319
-#: engines/access/metaengine.cpp:360 engines/bladerunner/metaengine.cpp:321
+#: engines/access/metaengine.cpp:381 engines/bladerunner/metaengine.cpp:321
 #: engines/ngi/metaengine.cpp:162 engines/supernova/metaengine.cpp:238
 #: engines/tucker/metaengine.cpp:215 engines/zvision/metaengine.cpp:246
 msgid "Help"
@@ -2708,7 +2708,7 @@ msgstr ""
 "Y m??s..."
 
 #: gui/options.cpp:2975 engines/engine.cpp:647
-#: engines/access/metaengine.cpp:298 engines/drascula/metaengine.cpp:255
+#: engines/access/metaengine.cpp:319 engines/drascula/metaengine.cpp:255
 #: engines/queen/metaengine.cpp:269 engines/sherlock/metaengine.cpp:410
 #: engines/ultima/nuvie/metaengine.cpp:71
 msgid "Move"
@@ -4030,7 +4030,7 @@ msgstr "Men?? del juego"
 
 #. I18N: Skipping cutscene plaback
 #. I18N: Skipping cutscene playback
-#: engines/metaengine.cpp:112 engines/access/metaengine.cpp:225
+#: engines/metaengine.cpp:112 engines/access/metaengine.cpp:246
 #: engines/composer/metaengine.cpp:126 engines/drascula/metaengine.cpp:224
 #: engines/efh/metaengine.cpp:379 engines/freescape/movement.cpp:93
 #: engines/grim/grim.cpp:533 engines/grim/grim.cpp:633
@@ -6367,7 +6367,15 @@ msgstr "Iniciador"
 msgid "Check for Updates..."
 msgstr "Buscar actualizaciones..."
 
-#: engines/access/metaengine.cpp:215 engines/alcachofa/metaengine.cpp:94
+#: engines/access/metaengine.cpp:82
+msgid "Use 'high definition' OGG music"
+msgstr ""
+
+#: engines/access/metaengine.cpp:83
+msgid "Use the OGG audio from the re-release instead of original MIDI tracks"
+msgstr ""
+
+#: engines/access/metaengine.cpp:236 engines/alcachofa/metaengine.cpp:94
 #: engines/bbvs/metaengine.cpp:92 engines/buried/metaengine.cpp:133
 #: engines/chamber/metaengine.cpp:59 engines/cine/metaengine.cpp:270
 #: engines/cruise/metaengine.cpp:168 engines/dm/metaengine.cpp:124
@@ -6391,16 +6399,16 @@ msgstr "Buscar actualizaciones..."
 msgid "Default keymappings"
 msgstr "Asignaciones predeterminadas"
 
-#: engines/access/metaengine.cpp:219
+#: engines/access/metaengine.cpp:240
 msgid "Move / Interact / Skip"
 msgstr "Mover/Interactuar/Omitir"
 
-#: engines/access/metaengine.cpp:231 engines/sherlock/metaengine.cpp:483
+#: engines/access/metaengine.cpp:252 engines/sherlock/metaengine.cpp:483
 msgid "Skip movie"
 msgstr "Omitir v??deo"
 
 #. I18N: Move Actor to upwards direction
-#: engines/access/metaengine.cpp:237 engines/asylum/metaengine.cpp:171
+#: engines/access/metaengine.cpp:258 engines/asylum/metaengine.cpp:171
 #: engines/asylum/metaengine.cpp:198 engines/cine/metaengine.cpp:377
 #: engines/efh/metaengine.cpp:286 engines/hdb/metaengine.cpp:213
 #: engines/hypno/metaengine.cpp:363 engines/hypno/metaengine.cpp:411
@@ -6410,7 +6418,7 @@ msgid "Move up"
 msgstr "Mover hacia arriba"
 
 #. I18N: Move Actor to downwards direction
-#: engines/access/metaengine.cpp:244 engines/asylum/metaengine.cpp:177
+#: engines/access/metaengine.cpp:265 engines/asylum/metaengine.cpp:177
 #: engines/asylum/metaengine.cpp:205 engines/cine/metaengine.cpp:384
 #: engines/efh/metaengine.cpp:293 engines/hdb/metaengine.cpp:219
 #: engines/hypno/metaengine.cpp:351 engines/hypno/metaengine.cpp:399
@@ -6421,7 +6429,7 @@ msgstr "Mover hacia abajo"
 
 #. I18N: Move Actor to left direction
 #. I18N: Move actor left during fight
-#: engines/access/metaengine.cpp:251 engines/asylum/metaengine.cpp:183
+#: engines/access/metaengine.cpp:272 engines/asylum/metaengine.cpp:183
 #: engines/asylum/metaengine.cpp:212 engines/cine/metaengine.cpp:391
 #: engines/efh/metaengine.cpp:300 engines/hdb/metaengine.cpp:225
 #: engines/hypno/metaengine.cpp:345 engines/hypno/metaengine.cpp:393
@@ -6434,7 +6442,7 @@ msgstr "Mover hacia la izquierda"
 
 #. I18N: Move Actor to right direction
 #. I18N: Move actor right during fight
-#: engines/access/metaengine.cpp:258 engines/asylum/metaengine.cpp:189
+#: engines/access/metaengine.cpp:279 engines/asylum/metaengine.cpp:189
 #: engines/asylum/metaengine.cpp:219 engines/cine/metaengine.cpp:398
 #: engines/efh/metaengine.cpp:307 engines/hdb/metaengine.cpp:231
 #: engines/hypno/metaengine.cpp:357 engines/hypno/metaengine.cpp:405
@@ -6446,31 +6454,31 @@ msgid "Move right"
 msgstr "Mover hacia la derecha"
 
 #. I18N: Move Actor to top-left direction
-#: engines/access/metaengine.cpp:265 engines/cine/metaengine.cpp:405
+#: engines/access/metaengine.cpp:286 engines/cine/metaengine.cpp:405
 #: engines/efh/metaengine.cpp:314
 msgid "Move up-left"
 msgstr "Mover hacia arriba a la izquierda"
 
 #. I18N: Move Actor to top-right direction
-#: engines/access/metaengine.cpp:270 engines/cine/metaengine.cpp:411
+#: engines/access/metaengine.cpp:291 engines/cine/metaengine.cpp:411
 #: engines/efh/metaengine.cpp:320
 msgid "Move up-right"
 msgstr "Mover hacia arriba a la derecha"
 
 #. I18N: Move Actor to bottom-left direction
-#: engines/access/metaengine.cpp:275 engines/cine/metaengine.cpp:417
+#: engines/access/metaengine.cpp:296 engines/cine/metaengine.cpp:417
 #: engines/efh/metaengine.cpp:326
 msgid "Move down-left"
 msgstr "Mover hacia abajo a la izquierda"
 
 #. I18N: Move Actor to bottom-right direction
-#: engines/access/metaengine.cpp:280 engines/cine/metaengine.cpp:423
+#: engines/access/metaengine.cpp:301 engines/cine/metaengine.cpp:423
 #: engines/efh/metaengine.cpp:332
 msgid "Move down-right"
 msgstr "Mover hacia abajo a la derecha"
 
 #. I18N: (Game name: The Lost Files of Sherlock Holmes: The Case of the Rose Tattoo) This action is used to look at an object or npc in the game
-#: engines/access/metaengine.cpp:285 engines/bbvs/metaengine.cpp:124
+#: engines/access/metaengine.cpp:306 engines/bbvs/metaengine.cpp:124
 #: engines/dgds/metaengine.cpp:83 engines/drascula/metaengine.cpp:230
 #: engines/petka/metaengine.cpp:130 engines/scumm/help.cpp:167
 #: engines/scumm/help.cpp:231 engines/sherlock/metaengine.cpp:405
@@ -6481,7 +6489,7 @@ msgstr "Mover hacia abajo a la derecha"
 msgid "Look"
 msgstr "Mirar"
 
-#: engines/access/metaengine.cpp:293 engines/drascula/metaengine.cpp:240
+#: engines/access/metaengine.cpp:314 engines/drascula/metaengine.cpp:240
 #: engines/queen/metaengine.cpp:259 engines/saga/metaengine.cpp:369
 #: engines/scumm/help.cpp:125 engines/scumm/help.cpp:140
 #: engines/scumm/help.cpp:165 engines/scumm/help.cpp:193
@@ -6491,13 +6499,13 @@ msgstr "Mirar"
 msgid "Open"
 msgstr "Abrir"
 
-#: engines/access/metaengine.cpp:303 engines/scumm/help.cpp:128
+#: engines/access/metaengine.cpp:324 engines/scumm/help.cpp:128
 #: engines/ultima/nuvie/metaengine.cpp:70
 msgid "Get"
 msgstr "Coger"
 
 #. I18N: Combat command in Might & Magic 1
-#: engines/access/metaengine.cpp:308 engines/access/metaengine.cpp:329
+#: engines/access/metaengine.cpp:329 engines/access/metaengine.cpp:350
 #: engines/bbvs/metaengine.cpp:136 engines/cine/metaengine.cpp:342
 #: engines/efh/metaengine.cpp:462 engines/hdb/metaengine.cpp:237
 #: engines/lab/metaengine.cpp:323 engines/mm/mm1/metaengine.cpp:86
@@ -6517,11 +6525,11 @@ msgstr "Coger"
 msgid "Use"
 msgstr "Usar"
 
-#: engines/access/metaengine.cpp:313
+#: engines/access/metaengine.cpp:334
 msgid "Goto"
 msgstr "Ir a"
 
-#: engines/access/metaengine.cpp:318 engines/access/metaengine.cpp:349
+#: engines/access/metaengine.cpp:339 engines/access/metaengine.cpp:370
 #: engines/bbvs/metaengine.cpp:130 engines/drascula/metaengine.cpp:250
 #: engines/efh/metaengine.cpp:506 engines/petka/metaengine.cpp:158
 #: engines/scumm/help.cpp:174 engines/scumm/help.cpp:226
@@ -6532,18 +6540,18 @@ msgstr "Ir a"
 msgid "Talk"
 msgstr "Hablar"
 
-#: engines/access/metaengine.cpp:323 engines/scumm/help.cpp:175
+#: engines/access/metaengine.cpp:344 engines/scumm/help.cpp:175
 msgid "Travel"
 msgstr "Viajar"
 
-#: engines/access/metaengine.cpp:334 engines/cine/metaengine.cpp:332
+#: engines/access/metaengine.cpp:355 engines/cine/metaengine.cpp:332
 #: engines/petka/metaengine.cpp:144 engines/saga/metaengine.cpp:394
 #: engines/supernova/metaengine.cpp:331
 msgid "Take"
 msgstr "Tomar"
 
 #. I18N: Action in In Cold Blood
-#: engines/access/metaengine.cpp:339 engines/alcachofa/metaengine.cpp:116
+#: engines/access/metaengine.cpp:360 engines/alcachofa/metaengine.cpp:116
 #: engines/cine/metaengine.cpp:337 engines/crab/input/input.cpp:251
 #: engines/cruise/metaengine.cpp:192 engines/draci/metaengine.cpp:237
 #: engines/dragons/metaengine.cpp:147 engines/drascula/metaengine.cpp:218
@@ -6563,18 +6571,18 @@ msgstr "Tomar"
 msgid "Inventory"
 msgstr "Inventario"
 
-#: engines/access/metaengine.cpp:344 engines/ultima/ultima4/metaengine.cpp:51
+#: engines/access/metaengine.cpp:365 engines/ultima/ultima4/metaengine.cpp:51
 msgid "Climb"
 msgstr "Escalar"
 
-#: engines/access/metaengine.cpp:354 engines/bbvs/metaengine.cpp:142
+#: engines/access/metaengine.cpp:375 engines/bbvs/metaengine.cpp:142
 #: engines/freescape/games/castle/castle.cpp:511
 #: engines/petka/metaengine.cpp:137 engines/scumm/help.cpp:225
 #: engines/tsage/metaengine.cpp:201
 msgid "Walk"
 msgstr "Caminar"
 
-#: engines/access/metaengine.cpp:365
+#: engines/access/metaengine.cpp:386
 msgid "Open save/load menu"
 msgstr "Abrir men?? de guardado/carga"
 
diff --git a/po/eu.po b/po/eu.po
index d76e13d9476..01a056caa7b 100644
--- a/po/eu.po
+++ b/po/eu.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.5.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-17 23:07+0000\n"
+"POT-Creation-Date: 2026-06-18 09:25+0000\n"
 "PO-Revision-Date: 2024-02-05 18:42+0000\n"
 "Last-Translator: Josu Igoa <josuigoa at ni.eus>\n"
 "Language-Team: Basque <https://translations.scummvm.org/projects/scummvm/"
@@ -84,7 +84,7 @@ msgstr "Itxi"
 #. I18N: Opens the help screen.
 #: gui/browser.cpp:64 gui/launcher.cpp:277 gui/launcher.cpp:959
 #: backends/platform/sdl/macosx/appmenu_osx.mm:319
-#: engines/access/metaengine.cpp:360 engines/bladerunner/metaengine.cpp:321
+#: engines/access/metaengine.cpp:381 engines/bladerunner/metaengine.cpp:321
 #: engines/ngi/metaengine.cpp:162 engines/supernova/metaengine.cpp:238
 #: engines/tucker/metaengine.cpp:215 engines/zvision/metaengine.cpp:246
 msgid "Help"
@@ -2691,7 +2691,7 @@ msgstr ""
 "gehitu..."
 
 #: gui/options.cpp:2975 engines/engine.cpp:647
-#: engines/access/metaengine.cpp:298 engines/drascula/metaengine.cpp:255
+#: engines/access/metaengine.cpp:319 engines/drascula/metaengine.cpp:255
 #: engines/queen/metaengine.cpp:269 engines/sherlock/metaengine.cpp:410
 #: engines/ultima/nuvie/metaengine.cpp:71
 msgid "Move"
@@ -4010,7 +4010,7 @@ msgstr "Jokoaren menua"
 
 #. I18N: Skipping cutscene plaback
 #. I18N: Skipping cutscene playback
-#: engines/metaengine.cpp:112 engines/access/metaengine.cpp:225
+#: engines/metaengine.cpp:112 engines/access/metaengine.cpp:246
 #: engines/composer/metaengine.cpp:126 engines/drascula/metaengine.cpp:224
 #: engines/efh/metaengine.cpp:379 engines/freescape/movement.cpp:93
 #: engines/grim/grim.cpp:533 engines/grim/grim.cpp:633
@@ -6128,7 +6128,15 @@ msgstr "Abiarazlea"
 msgid "Check for Updates..."
 msgstr "Eguneraketak bilatzen..."
 
-#: engines/access/metaengine.cpp:215 engines/alcachofa/metaengine.cpp:94
+#: engines/access/metaengine.cpp:82
+msgid "Use 'high definition' OGG music"
+msgstr ""
+
+#: engines/access/metaengine.cpp:83
+msgid "Use the OGG audio from the re-release instead of original MIDI tracks"
+msgstr ""
+
+#: engines/access/metaengine.cpp:236 engines/alcachofa/metaengine.cpp:94
 #: engines/bbvs/metaengine.cpp:92 engines/buried/metaengine.cpp:133
 #: engines/chamber/metaengine.cpp:59 engines/cine/metaengine.cpp:270
 #: engines/cruise/metaengine.cpp:168 engines/dm/metaengine.cpp:124
@@ -6153,20 +6161,20 @@ msgstr "Eguneraketak bilatzen..."
 msgid "Default keymappings"
 msgstr "~A~ukerak..."
 
-#: engines/access/metaengine.cpp:219
+#: engines/access/metaengine.cpp:240
 #, fuzzy
 #| msgid "Highlight next dialogue"
 msgid "Move / Interact / Skip"
 msgstr "Hurrengo elkarrizketa aukeratu"
 
-#: engines/access/metaengine.cpp:231 engines/sherlock/metaengine.cpp:483
+#: engines/access/metaengine.cpp:252 engines/sherlock/metaengine.cpp:483
 #, fuzzy
 #| msgid "Skip line"
 msgid "Skip movie"
 msgstr "Lerroa saltatu"
 
 #. I18N: Move Actor to upwards direction
-#: engines/access/metaengine.cpp:237 engines/asylum/metaengine.cpp:171
+#: engines/access/metaengine.cpp:258 engines/asylum/metaengine.cpp:171
 #: engines/asylum/metaengine.cpp:198 engines/cine/metaengine.cpp:377
 #: engines/efh/metaengine.cpp:286 engines/hdb/metaengine.cpp:213
 #: engines/hypno/metaengine.cpp:363 engines/hypno/metaengine.cpp:411
@@ -6177,7 +6185,7 @@ msgid "Move up"
 msgstr "Atzera mugitu"
 
 #. I18N: Move Actor to downwards direction
-#: engines/access/metaengine.cpp:244 engines/asylum/metaengine.cpp:177
+#: engines/access/metaengine.cpp:265 engines/asylum/metaengine.cpp:177
 #: engines/asylum/metaengine.cpp:205 engines/cine/metaengine.cpp:384
 #: engines/efh/metaengine.cpp:293 engines/hdb/metaengine.cpp:219
 #: engines/hypno/metaengine.cpp:351 engines/hypno/metaengine.cpp:399
@@ -6189,7 +6197,7 @@ msgstr "Eskuinera irristatu"
 
 #. I18N: Move Actor to left direction
 #. I18N: Move actor left during fight
-#: engines/access/metaengine.cpp:251 engines/asylum/metaengine.cpp:183
+#: engines/access/metaengine.cpp:272 engines/asylum/metaengine.cpp:183
 #: engines/asylum/metaengine.cpp:212 engines/cine/metaengine.cpp:391
 #: engines/efh/metaengine.cpp:300 engines/hdb/metaengine.cpp:225
 #: engines/hypno/metaengine.cpp:345 engines/hypno/metaengine.cpp:393
@@ -6203,7 +6211,7 @@ msgstr "Ezkerrera irristatu"
 
 #. I18N: Move Actor to right direction
 #. I18N: Move actor right during fight
-#: engines/access/metaengine.cpp:258 engines/asylum/metaengine.cpp:189
+#: engines/access/metaengine.cpp:279 engines/asylum/metaengine.cpp:189
 #: engines/asylum/metaengine.cpp:219 engines/cine/metaengine.cpp:398
 #: engines/efh/metaengine.cpp:307 engines/hdb/metaengine.cpp:231
 #: engines/hypno/metaengine.cpp:357 engines/hypno/metaengine.cpp:405
@@ -6216,35 +6224,35 @@ msgid "Move right"
 msgstr "Eskuinera irristatu"
 
 #. I18N: Move Actor to top-left direction
-#: engines/access/metaengine.cpp:265 engines/cine/metaengine.cpp:405
+#: engines/access/metaengine.cpp:286 engines/cine/metaengine.cpp:405
 #: engines/efh/metaengine.cpp:314
 #, fuzzy
 msgid "Move up-left"
 msgstr "Ezkerrera irristatu"
 
 #. I18N: Move Actor to top-right direction
-#: engines/access/metaengine.cpp:270 engines/cine/metaengine.cpp:411
+#: engines/access/metaengine.cpp:291 engines/cine/metaengine.cpp:411
 #: engines/efh/metaengine.cpp:320
 #, fuzzy
 msgid "Move up-right"
 msgstr "Eskuinera irristatu"
 
 #. I18N: Move Actor to bottom-left direction
-#: engines/access/metaengine.cpp:275 engines/cine/metaengine.cpp:417
+#: engines/access/metaengine.cpp:296 engines/cine/metaengine.cpp:417
 #: engines/efh/metaengine.cpp:326
 #, fuzzy
 msgid "Move down-left"
 msgstr "Eskuinera irristatu"
 
 #. I18N: Move Actor to bottom-right direction
-#: engines/access/metaengine.cpp:280 engines/cine/metaengine.cpp:423
+#: engines/access/metaengine.cpp:301 engines/cine/metaengine.cpp:423
 #: engines/efh/metaengine.cpp:332
 #, fuzzy
 msgid "Move down-right"
 msgstr "Eskuinera irristatu"
 
 #. I18N: (Game name: The Lost Files of Sherlock Holmes: The Case of the Rose Tattoo) This action is used to look at an object or npc in the game
-#: engines/access/metaengine.cpp:285 engines/bbvs/metaengine.cpp:124
+#: engines/access/metaengine.cpp:306 engines/bbvs/metaengine.cpp:124
 #: engines/dgds/metaengine.cpp:83 engines/drascula/metaengine.cpp:230
 #: engines/petka/metaengine.cpp:130 engines/scumm/help.cpp:167
 #: engines/scumm/help.cpp:231 engines/sherlock/metaengine.cpp:405
@@ -6255,7 +6263,7 @@ msgstr "Eskuinera irristatu"
 msgid "Look"
 msgstr "Begiratu"
 
-#: engines/access/metaengine.cpp:293 engines/drascula/metaengine.cpp:240
+#: engines/access/metaengine.cpp:314 engines/drascula/metaengine.cpp:240
 #: engines/queen/metaengine.cpp:259 engines/saga/metaengine.cpp:369
 #: engines/scumm/help.cpp:125 engines/scumm/help.cpp:140
 #: engines/scumm/help.cpp:165 engines/scumm/help.cpp:193
@@ -6265,13 +6273,13 @@ msgstr "Begiratu"
 msgid "Open"
 msgstr "Ireki"
 
-#: engines/access/metaengine.cpp:303 engines/scumm/help.cpp:128
+#: engines/access/metaengine.cpp:324 engines/scumm/help.cpp:128
 #: engines/ultima/nuvie/metaengine.cpp:70
 msgid "Get"
 msgstr "Jaso"
 
 #. I18N: Combat command in Might & Magic 1
-#: engines/access/metaengine.cpp:308 engines/access/metaengine.cpp:329
+#: engines/access/metaengine.cpp:329 engines/access/metaengine.cpp:350
 #: engines/bbvs/metaengine.cpp:136 engines/cine/metaengine.cpp:342
 #: engines/efh/metaengine.cpp:462 engines/hdb/metaengine.cpp:237
 #: engines/lab/metaengine.cpp:323 engines/mm/mm1/metaengine.cpp:86
@@ -6291,11 +6299,11 @@ msgstr "Jaso"
 msgid "Use"
 msgstr "Erabili"
 
-#: engines/access/metaengine.cpp:313
+#: engines/access/metaengine.cpp:334
 msgid "Goto"
 msgstr ""
 
-#: engines/access/metaengine.cpp:318 engines/access/metaengine.cpp:349
+#: engines/access/metaengine.cpp:339 engines/access/metaengine.cpp:370
 #: engines/bbvs/metaengine.cpp:130 engines/drascula/metaengine.cpp:250
 #: engines/efh/metaengine.cpp:506 engines/petka/metaengine.cpp:158
 #: engines/scumm/help.cpp:174 engines/scumm/help.cpp:226
@@ -6306,11 +6314,11 @@ msgstr ""
 msgid "Talk"
 msgstr "Hitz egin"
 
-#: engines/access/metaengine.cpp:323 engines/scumm/help.cpp:175
+#: engines/access/metaengine.cpp:344 engines/scumm/help.cpp:175
 msgid "Travel"
 msgstr "Bidaiatu"
 
-#: engines/access/metaengine.cpp:334 engines/cine/metaengine.cpp:332
+#: engines/access/metaengine.cpp:355 engines/cine/metaengine.cpp:332
 #: engines/petka/metaengine.cpp:144 engines/saga/metaengine.cpp:394
 #: engines/supernova/metaengine.cpp:331
 #, fuzzy
@@ -6319,7 +6327,7 @@ msgid "Take"
 msgstr "Kendu"
 
 #. I18N: Action in In Cold Blood
-#: engines/access/metaengine.cpp:339 engines/alcachofa/metaengine.cpp:116
+#: engines/access/metaengine.cpp:360 engines/alcachofa/metaengine.cpp:116
 #: engines/cine/metaengine.cpp:337 engines/crab/input/input.cpp:251
 #: engines/cruise/metaengine.cpp:192 engines/draci/metaengine.cpp:237
 #: engines/dragons/metaengine.cpp:147 engines/drascula/metaengine.cpp:218
@@ -6339,18 +6347,18 @@ msgstr "Kendu"
 msgid "Inventory"
 msgstr "Inbentarioa"
 
-#: engines/access/metaengine.cpp:344 engines/ultima/ultima4/metaengine.cpp:51
+#: engines/access/metaengine.cpp:365 engines/ultima/ultima4/metaengine.cpp:51
 msgid "Climb"
 msgstr ""
 
-#: engines/access/metaengine.cpp:354 engines/bbvs/metaengine.cpp:142
+#: engines/access/metaengine.cpp:375 engines/bbvs/metaengine.cpp:142
 #: engines/freescape/games/castle/castle.cpp:511
 #: engines/petka/metaengine.cpp:137 engines/scumm/help.cpp:225
 #: engines/tsage/metaengine.cpp:201
 msgid "Walk"
 msgstr "Ibili"
 
-#: engines/access/metaengine.cpp:365
+#: engines/access/metaengine.cpp:386
 #, fuzzy
 msgid "Open save/load menu"
 msgstr "Sartu partida gordeak dituen diskoa"
diff --git a/po/fi_FI.po b/po/fi_FI.po
index e1101a8192a..65dc89438dc 100644
--- a/po/fi_FI.po
+++ b/po/fi_FI.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.6.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-17 23:07+0000\n"
+"POT-Creation-Date: 2026-06-18 09:25+0000\n"
 "PO-Revision-Date: 2026-02-07 15:22+0000\n"
 "Last-Translator: Timo Mikkolainen <tmikkola at gmail.com>\n"
 "Language-Team: Finnish <https://translations.scummvm.org/projects/scummvm/"
@@ -84,7 +84,7 @@ msgstr "Sulje"
 #. I18N: Opens the help screen.
 #: gui/browser.cpp:64 gui/launcher.cpp:277 gui/launcher.cpp:959
 #: backends/platform/sdl/macosx/appmenu_osx.mm:319
-#: engines/access/metaengine.cpp:360 engines/bladerunner/metaengine.cpp:321
+#: engines/access/metaengine.cpp:381 engines/bladerunner/metaengine.cpp:321
 #: engines/ngi/metaengine.cpp:162 engines/supernova/metaengine.cpp:238
 #: engines/tucker/metaengine.cpp:215 engines/zvision/metaengine.cpp:246
 msgid "Help"
@@ -2743,7 +2743,7 @@ msgstr ""
 "Ja lis????..."
 
 #: gui/options.cpp:2975 engines/engine.cpp:647
-#: engines/access/metaengine.cpp:298 engines/drascula/metaengine.cpp:255
+#: engines/access/metaengine.cpp:319 engines/drascula/metaengine.cpp:255
 #: engines/queen/metaengine.cpp:269 engines/sherlock/metaengine.cpp:410
 #: engines/ultima/nuvie/metaengine.cpp:71
 msgid "Move"
@@ -4058,7 +4058,7 @@ msgstr "Pelivalikko"
 
 #. I18N: Skipping cutscene plaback
 #. I18N: Skipping cutscene playback
-#: engines/metaengine.cpp:112 engines/access/metaengine.cpp:225
+#: engines/metaengine.cpp:112 engines/access/metaengine.cpp:246
 #: engines/composer/metaengine.cpp:126 engines/drascula/metaengine.cpp:224
 #: engines/efh/metaengine.cpp:379 engines/freescape/movement.cpp:93
 #: engines/grim/grim.cpp:533 engines/grim/grim.cpp:633
@@ -6472,7 +6472,15 @@ msgstr "Pelivalitsin"
 msgid "Check for Updates..."
 msgstr "Tarkista p??ivitykset..."
 
-#: engines/access/metaengine.cpp:215 engines/alcachofa/metaengine.cpp:94
+#: engines/access/metaengine.cpp:82
+msgid "Use 'high definition' OGG music"
+msgstr ""
+
+#: engines/access/metaengine.cpp:83
+msgid "Use the OGG audio from the re-release instead of original MIDI tracks"
+msgstr ""
+
+#: engines/access/metaengine.cpp:236 engines/alcachofa/metaengine.cpp:94
 #: engines/bbvs/metaengine.cpp:92 engines/buried/metaengine.cpp:133
 #: engines/chamber/metaengine.cpp:59 engines/cine/metaengine.cpp:270
 #: engines/cruise/metaengine.cpp:168 engines/dm/metaengine.cpp:124
@@ -6496,16 +6504,16 @@ msgstr "Tarkista p??ivitykset..."
 msgid "Default keymappings"
 msgstr "N??pp??inten oletuskartoitukset"
 
-#: engines/access/metaengine.cpp:219
+#: engines/access/metaengine.cpp:240
 msgid "Move / Interact / Skip"
 msgstr "Liiku / K??yt?? / Ohita"
 
-#: engines/access/metaengine.cpp:231 engines/sherlock/metaengine.cpp:483
+#: engines/access/metaengine.cpp:252 engines/sherlock/metaengine.cpp:483
 msgid "Skip movie"
 msgstr "Ohita video"
 
 #. I18N: Move Actor to upwards direction
-#: engines/access/metaengine.cpp:237 engines/asylum/metaengine.cpp:171
+#: engines/access/metaengine.cpp:258 engines/asylum/metaengine.cpp:171
 #: engines/asylum/metaengine.cpp:198 engines/cine/metaengine.cpp:377
 #: engines/efh/metaengine.cpp:286 engines/hdb/metaengine.cpp:213
 #: engines/hypno/metaengine.cpp:363 engines/hypno/metaengine.cpp:411
@@ -6515,7 +6523,7 @@ msgid "Move up"
 msgstr "Liiku yl??sp??in"
 
 #. I18N: Move Actor to downwards direction
-#: engines/access/metaengine.cpp:244 engines/asylum/metaengine.cpp:177
+#: engines/access/metaengine.cpp:265 engines/asylum/metaengine.cpp:177
 #: engines/asylum/metaengine.cpp:205 engines/cine/metaengine.cpp:384
 #: engines/efh/metaengine.cpp:293 engines/hdb/metaengine.cpp:219
 #: engines/hypno/metaengine.cpp:351 engines/hypno/metaengine.cpp:399
@@ -6526,7 +6534,7 @@ msgstr "Liiku alasp??in"
 
 #. I18N: Move Actor to left direction
 #. I18N: Move actor left during fight
-#: engines/access/metaengine.cpp:251 engines/asylum/metaengine.cpp:183
+#: engines/access/metaengine.cpp:272 engines/asylum/metaengine.cpp:183
 #: engines/asylum/metaengine.cpp:212 engines/cine/metaengine.cpp:391
 #: engines/efh/metaengine.cpp:300 engines/hdb/metaengine.cpp:225
 #: engines/hypno/metaengine.cpp:345 engines/hypno/metaengine.cpp:393
@@ -6539,7 +6547,7 @@ msgstr "Liiku vasemmalle"
 
 #. I18N: Move Actor to right direction
 #. I18N: Move actor right during fight
-#: engines/access/metaengine.cpp:258 engines/asylum/metaengine.cpp:189
+#: engines/access/metaengine.cpp:279 engines/asylum/metaengine.cpp:189
 #: engines/asylum/metaengine.cpp:219 engines/cine/metaengine.cpp:398
 #: engines/efh/metaengine.cpp:307 engines/hdb/metaengine.cpp:231
 #: engines/hypno/metaengine.cpp:357 engines/hypno/metaengine.cpp:405
@@ -6551,31 +6559,31 @@ msgid "Move right"
 msgstr "Liiku oikealle"
 
 #. I18N: Move Actor to top-left direction
-#: engines/access/metaengine.cpp:265 engines/cine/metaengine.cpp:405
+#: engines/access/metaengine.cpp:286 engines/cine/metaengine.cpp:405
 #: engines/efh/metaengine.cpp:314
 msgid "Move up-left"
 msgstr "Liiku vasemmalle yl??s"
 
 #. I18N: Move Actor to top-right direction
-#: engines/access/metaengine.cpp:270 engines/cine/metaengine.cpp:411
+#: engines/access/metaengine.cpp:291 engines/cine/metaengine.cpp:411
 #: engines/efh/metaengine.cpp:320
 msgid "Move up-right"
 msgstr "Liiku oikealle yl??s"
 
 #. I18N: Move Actor to bottom-left direction
-#: engines/access/metaengine.cpp:275 engines/cine/metaengine.cpp:417
+#: engines/access/metaengine.cpp:296 engines/cine/metaengine.cpp:417
 #: engines/efh/metaengine.cpp:326
 msgid "Move down-left"
 msgstr "Liiku alas vasemmalle"
 
 #. I18N: Move Actor to bottom-right direction
-#: engines/access/metaengine.cpp:280 engines/cine/metaengine.cpp:423
+#: engines/access/metaengine.cpp:301 engines/cine/metaengine.cpp:423
 #: engines/efh/metaengine.cpp:332
 msgid "Move down-right"
 msgstr "Liiku alas oikealle"
 
 #. I18N: (Game name: The Lost Files of Sherlock Holmes: The Case of the Rose Tattoo) This action is used to look at an object or npc in the game
-#: engines/access/metaengine.cpp:285 engines/bbvs/metaengine.cpp:124
+#: engines/access/metaengine.cpp:306 engines/bbvs/metaengine.cpp:124
 #: engines/dgds/metaengine.cpp:83 engines/drascula/metaengine.cpp:230
 #: engines/petka/metaengine.cpp:130 engines/scumm/help.cpp:167
 #: engines/scumm/help.cpp:231 engines/sherlock/metaengine.cpp:405
@@ -6586,7 +6594,7 @@ msgstr "Liiku alas oikealle"
 msgid "Look"
 msgstr "Katso"
 
-#: engines/access/metaengine.cpp:293 engines/drascula/metaengine.cpp:240
+#: engines/access/metaengine.cpp:314 engines/drascula/metaengine.cpp:240
 #: engines/queen/metaengine.cpp:259 engines/saga/metaengine.cpp:369
 #: engines/scumm/help.cpp:125 engines/scumm/help.cpp:140
 #: engines/scumm/help.cpp:165 engines/scumm/help.cpp:193
@@ -6596,13 +6604,13 @@ msgstr "Katso"
 msgid "Open"
 msgstr "Avaa"
 
-#: engines/access/metaengine.cpp:303 engines/scumm/help.cpp:128
+#: engines/access/metaengine.cpp:324 engines/scumm/help.cpp:128
 #: engines/ultima/nuvie/metaengine.cpp:70
 msgid "Get"
 msgstr "Ota"
 
 #. I18N: Combat command in Might & Magic 1
-#: engines/access/metaengine.cpp:308 engines/access/metaengine.cpp:329
+#: engines/access/metaengine.cpp:329 engines/access/metaengine.cpp:350
 #: engines/bbvs/metaengine.cpp:136 engines/cine/metaengine.cpp:342
 #: engines/efh/metaengine.cpp:462 engines/hdb/metaengine.cpp:237
 #: engines/lab/metaengine.cpp:323 engines/mm/mm1/metaengine.cpp:86
@@ -6622,11 +6630,11 @@ msgstr "Ota"
 msgid "Use"
 msgstr "K??yt??"
 
-#: engines/access/metaengine.cpp:313
+#: engines/access/metaengine.cpp:334
 msgid "Goto"
 msgstr "Mene"
 
-#: engines/access/metaengine.cpp:318 engines/access/metaengine.cpp:349
+#: engines/access/metaengine.cpp:339 engines/access/metaengine.cpp:370
 #: engines/bbvs/metaengine.cpp:130 engines/drascula/metaengine.cpp:250
 #: engines/efh/metaengine.cpp:506 engines/petka/metaengine.cpp:158
 #: engines/scumm/help.cpp:174 engines/scumm/help.cpp:226
@@ -6637,18 +6645,18 @@ msgstr "Mene"
 msgid "Talk"
 msgstr "Puhu"
 
-#: engines/access/metaengine.cpp:323 engines/scumm/help.cpp:175
+#: engines/access/metaengine.cpp:344 engines/scumm/help.cpp:175
 msgid "Travel"
 msgstr "Matkusta"
 
-#: engines/access/metaengine.cpp:334 engines/cine/metaengine.cpp:332
+#: engines/access/metaengine.cpp:355 engines/cine/metaengine.cpp:332
 #: engines/petka/metaengine.cpp:144 engines/saga/metaengine.cpp:394
 #: engines/supernova/metaengine.cpp:331
 msgid "Take"
 msgstr "Ota"
 
 #. I18N: Action in In Cold Blood
-#: engines/access/metaengine.cpp:339 engines/alcachofa/metaengine.cpp:116
+#: engines/access/metaengine.cpp:360 engines/alcachofa/metaengine.cpp:116
 #: engines/cine/metaengine.cpp:337 engines/crab/input/input.cpp:251
 #: engines/cruise/metaengine.cpp:192 engines/draci/metaengine.cpp:237
 #: engines/dragons/metaengine.cpp:147 engines/drascula/metaengine.cpp:218
@@ -6668,18 +6676,18 @@ msgstr "Ota"
 msgid "Inventory"
 msgstr "Tavarat"
 
-#: engines/access/metaengine.cpp:344 engines/ultima/ultima4/metaengine.cpp:51
+#: engines/access/metaengine.cpp:365 engines/ultima/ultima4/metaengine.cpp:51
 msgid "Climb"
 msgstr "Kiipe??"
 
-#: engines/access/metaengine.cpp:354 engines/bbvs/metaengine.cpp:142
+#: engines/access/metaengine.cpp:375 engines/bbvs/metaengine.cpp:142
 #: engines/freescape/games/castle/castle.cpp:511
 #: engines/petka/metaengine.cpp:137 engines/scumm/help.cpp:225
 #: engines/tsage/metaengine.cpp:201
 msgid "Walk"
 msgstr "K??vele"
 
-#: engines/access/metaengine.cpp:365
+#: engines/access/metaengine.cpp:386
 msgid "Open save/load menu"
 msgstr "Avaa tallennus / lataus valikko"
 
diff --git a/po/fr_FR.po b/po/fr_FR.po
index 22101fe4159..85e039c6230 100644
--- a/po/fr_FR.po
+++ b/po/fr_FR.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-17 23:07+0000\n"
+"POT-Creation-Date: 2026-06-18 09:25+0000\n"
 "PO-Revision-Date: 2026-06-14 09:41+0000\n"
 "Last-Translator: Purple T <ZEONK at hotmail.com>\n"
 "Language-Team: French <https://translations.scummvm.org/projects/scummvm/"
@@ -83,7 +83,7 @@ msgstr "Fermer"
 #. I18N: Opens the help screen.
 #: gui/browser.cpp:64 gui/launcher.cpp:277 gui/launcher.cpp:959
 #: backends/platform/sdl/macosx/appmenu_osx.mm:319
-#: engines/access/metaengine.cpp:360 engines/bladerunner/metaengine.cpp:321
+#: engines/access/metaengine.cpp:381 engines/bladerunner/metaengine.cpp:321
 #: engines/ngi/metaengine.cpp:162 engines/supernova/metaengine.cpp:238
 #: engines/tucker/metaengine.cpp:215 engines/zvision/metaengine.cpp:246
 msgid "Help"
@@ -2718,7 +2718,7 @@ msgstr ""
 "Voir plus..."
 
 #: gui/options.cpp:2975 engines/engine.cpp:647
-#: engines/access/metaengine.cpp:298 engines/drascula/metaengine.cpp:255
+#: engines/access/metaengine.cpp:319 engines/drascula/metaengine.cpp:255
 #: engines/queen/metaengine.cpp:269 engines/sherlock/metaengine.cpp:410
 #: engines/ultima/nuvie/metaengine.cpp:71
 msgid "Move"
@@ -4054,7 +4054,7 @@ msgstr "Menu du jeu"
 
 #. I18N: Skipping cutscene plaback
 #. I18N: Skipping cutscene playback
-#: engines/metaengine.cpp:112 engines/access/metaengine.cpp:225
+#: engines/metaengine.cpp:112 engines/access/metaengine.cpp:246
 #: engines/composer/metaengine.cpp:126 engines/drascula/metaengine.cpp:224
 #: engines/efh/metaengine.cpp:379 engines/freescape/movement.cpp:93
 #: engines/grim/grim.cpp:533 engines/grim/grim.cpp:633
@@ -6383,7 +6383,15 @@ msgstr "Lanceur"
 msgid "Check for Updates..."
 msgstr "Recherche des mises ?? jour..."
 
-#: engines/access/metaengine.cpp:215 engines/alcachofa/metaengine.cpp:94
+#: engines/access/metaengine.cpp:82
+msgid "Use 'high definition' OGG music"
+msgstr ""
+
+#: engines/access/metaengine.cpp:83
+msgid "Use the OGG audio from the re-release instead of original MIDI tracks"
+msgstr ""
+
+#: engines/access/metaengine.cpp:236 engines/alcachofa/metaengine.cpp:94
 #: engines/bbvs/metaengine.cpp:92 engines/buried/metaengine.cpp:133
 #: engines/chamber/metaengine.cpp:59 engines/cine/metaengine.cpp:270
 #: engines/cruise/metaengine.cpp:168 engines/dm/metaengine.cpp:124
@@ -6407,16 +6415,16 @@ msgstr "Recherche des mises ?? jour..."
 msgid "Default keymappings"
 msgstr "Touches par d??faut"
 
-#: engines/access/metaengine.cpp:219
+#: engines/access/metaengine.cpp:240
 msgid "Move / Interact / Skip"
 msgstr "Se d??placer / Interagir / Passer"
 
-#: engines/access/metaengine.cpp:231 engines/sherlock/metaengine.cpp:483
+#: engines/access/metaengine.cpp:252 engines/sherlock/metaengine.cpp:483
 msgid "Skip movie"
 msgstr "Passer le film"
 
 #. I18N: Move Actor to upwards direction
-#: engines/access/metaengine.cpp:237 engines/asylum/metaengine.cpp:171
+#: engines/access/metaengine.cpp:258 engines/asylum/metaengine.cpp:171
 #: engines/asylum/metaengine.cpp:198 engines/cine/metaengine.cpp:377
 #: engines/efh/metaengine.cpp:286 engines/hdb/metaengine.cpp:213
 #: engines/hypno/metaengine.cpp:363 engines/hypno/metaengine.cpp:411
@@ -6426,7 +6434,7 @@ msgid "Move up"
 msgstr "Se d??placer vers le haut"
 
 #. I18N: Move Actor to downwards direction
-#: engines/access/metaengine.cpp:244 engines/asylum/metaengine.cpp:177
+#: engines/access/metaengine.cpp:265 engines/asylum/metaengine.cpp:177
 #: engines/asylum/metaengine.cpp:205 engines/cine/metaengine.cpp:384
 #: engines/efh/metaengine.cpp:293 engines/hdb/metaengine.cpp:219
 #: engines/hypno/metaengine.cpp:351 engines/hypno/metaengine.cpp:399
@@ -6437,7 +6445,7 @@ msgstr "Se d??placer vers le bas"
 
 #. I18N: Move Actor to left direction
 #. I18N: Move actor left during fight
-#: engines/access/metaengine.cpp:251 engines/asylum/metaengine.cpp:183
+#: engines/access/metaengine.cpp:272 engines/asylum/metaengine.cpp:183
 #: engines/asylum/metaengine.cpp:212 engines/cine/metaengine.cpp:391
 #: engines/efh/metaengine.cpp:300 engines/hdb/metaengine.cpp:225
 #: engines/hypno/metaengine.cpp:345 engines/hypno/metaengine.cpp:393
@@ -6450,7 +6458,7 @@ msgstr "Se d??placer vers la gauche"
 
 #. I18N: Move Actor to right direction
 #. I18N: Move actor right during fight
-#: engines/access/metaengine.cpp:258 engines/asylum/metaengine.cpp:189
+#: engines/access/metaengine.cpp:279 engines/asylum/metaengine.cpp:189
 #: engines/asylum/metaengine.cpp:219 engines/cine/metaengine.cpp:398
 #: engines/efh/metaengine.cpp:307 engines/hdb/metaengine.cpp:231
 #: engines/hypno/metaengine.cpp:357 engines/hypno/metaengine.cpp:405
@@ -6462,31 +6470,31 @@ msgid "Move right"
 msgstr "Se d??placer vers la droite"
 
 #. I18N: Move Actor to top-left direction
-#: engines/access/metaengine.cpp:265 engines/cine/metaengine.cpp:405
+#: engines/access/metaengine.cpp:286 engines/cine/metaengine.cpp:405
 #: engines/efh/metaengine.cpp:314
 msgid "Move up-left"
 msgstr "Se d??placer vers le haut-gauche"
 
 #. I18N: Move Actor to top-right direction
-#: engines/access/metaengine.cpp:270 engines/cine/metaengine.cpp:411
+#: engines/access/metaengine.cpp:291 engines/cine/metaengine.cpp:411
 #: engines/efh/metaengine.cpp:320
 msgid "Move up-right"
 msgstr "Se d??placer vers le haut-droite"
 
 #. I18N: Move Actor to bottom-left direction
-#: engines/access/metaengine.cpp:275 engines/cine/metaengine.cpp:417
+#: engines/access/metaengine.cpp:296 engines/cine/metaengine.cpp:417
 #: engines/efh/metaengine.cpp:326
 msgid "Move down-left"
 msgstr "Se d??placer vers le bas-gauche"
 
 #. I18N: Move Actor to bottom-right direction
-#: engines/access/metaengine.cpp:280 engines/cine/metaengine.cpp:423
+#: engines/access/metaengine.cpp:301 engines/cine/metaengine.cpp:423
 #: engines/efh/metaengine.cpp:332
 msgid "Move down-right"
 msgstr "Se d??placer vers le bas-droite"
 
 #. I18N: (Game name: The Lost Files of Sherlock Holmes: The Case of the Rose Tattoo) This action is used to look at an object or npc in the game
-#: engines/access/metaengine.cpp:285 engines/bbvs/metaengine.cpp:124
+#: engines/access/metaengine.cpp:306 engines/bbvs/metaengine.cpp:124
 #: engines/dgds/metaengine.cpp:83 engines/drascula/metaengine.cpp:230
 #: engines/petka/metaengine.cpp:130 engines/scumm/help.cpp:167
 #: engines/scumm/help.cpp:231 engines/sherlock/metaengine.cpp:405
@@ -6497,7 +6505,7 @@ msgstr "Se d??placer vers le bas-droite"
 msgid "Look"
 msgstr "Examiner"
 
-#: engines/access/metaengine.cpp:293 engines/drascula/metaengine.cpp:240
+#: engines/access/metaengine.cpp:314 engines/drascula/metaengine.cpp:240
 #: engines/queen/metaengine.cpp:259 engines/saga/metaengine.cpp:369
 #: engines/scumm/help.cpp:125 engines/scumm/help.cpp:140
 #: engines/scumm/help.cpp:165 engines/scumm/help.cpp:193
@@ -6507,13 +6515,13 @@ msgstr "Examiner"
 msgid "Open"
 msgstr "Ouvrir"
 
-#: engines/access/metaengine.cpp:303 engines/scumm/help.cpp:128
+#: engines/access/metaengine.cpp:324 engines/scumm/help.cpp:128
 #: engines/ultima/nuvie/metaengine.cpp:70
 msgid "Get"
 msgstr "Prendre"
 
 #. I18N: Combat command in Might & Magic 1
-#: engines/access/metaengine.cpp:308 engines/access/metaengine.cpp:329
+#: engines/access/metaengine.cpp:329 engines/access/metaengine.cpp:350
 #: engines/bbvs/metaengine.cpp:136 engines/cine/metaengine.cpp:342
 #: engines/efh/metaengine.cpp:462 engines/hdb/metaengine.cpp:237
 #: engines/lab/metaengine.cpp:323 engines/mm/mm1/metaengine.cpp:86
@@ -6533,11 +6541,11 @@ msgstr "Prendre"
 msgid "Use"
 msgstr "Utiliser"
 
-#: engines/access/metaengine.cpp:313
+#: engines/access/metaengine.cpp:334
 msgid "Goto"
 msgstr "Aller vers"
 
-#: engines/access/metaengine.cpp:318 engines/access/metaengine.cpp:349
+#: engines/access/metaengine.cpp:339 engines/access/metaengine.cpp:370
 #: engines/bbvs/metaengine.cpp:130 engines/drascula/metaengine.cpp:250
 #: engines/efh/metaengine.cpp:506 engines/petka/metaengine.cpp:158
 #: engines/scumm/help.cpp:174 engines/scumm/help.cpp:226
@@ -6548,18 +6556,18 @@ msgstr "Aller vers"
 msgid "Talk"
 msgstr "Parler"
 
-#: engines/access/metaengine.cpp:323 engines/scumm/help.cpp:175
+#: engines/access/metaengine.cpp:344 engines/scumm/help.cpp:175
 msgid "Travel"
 msgstr "Voyager"
 
-#: engines/access/metaengine.cpp:334 engines/cine/metaengine.cpp:332
+#: engines/access/metaengine.cpp:355 engines/cine/metaengine.cpp:332
 #: engines/petka/metaengine.cpp:144 engines/saga/metaengine.cpp:394
 #: engines/supernova/metaengine.cpp:331
 msgid "Take"
 msgstr "Prendre"
 
 #. I18N: Action in In Cold Blood
-#: engines/access/metaengine.cpp:339 engines/alcachofa/metaengine.cpp:116
+#: engines/access/metaengine.cpp:360 engines/alcachofa/metaengine.cpp:116
 #: engines/cine/metaengine.cpp:337 engines/crab/input/input.cpp:251
 #: engines/cruise/metaengine.cpp:192 engines/draci/metaengine.cpp:237
 #: engines/dragons/metaengine.cpp:147 engines/drascula/metaengine.cpp:218
@@ -6579,18 +6587,18 @@ msgstr "Prendre"
 msgid "Inventory"
 msgstr "Inventaire"
 
-#: engines/access/metaengine.cpp:344 engines/ultima/ultima4/metaengine.cpp:51
+#: engines/access/metaengine.cpp:365 engines/ultima/ultima4/metaengine.cpp:51
 msgid "Climb"
 msgstr "Grimper"
 
-#: engines/access/metaengine.cpp:354 engines/bbvs/metaengine.cpp:142
+#: engines/access/metaengine.cpp:375 engines/bbvs/metaengine.cpp:142
 #: engines/freescape/games/castle/castle.cpp:511
 #: engines/petka/metaengine.cpp:137 engines/scumm/help.cpp:225
 #: engines/tsage/metaengine.cpp:201
 msgid "Walk"
 msgstr "Marcher"
 
-#: engines/access/metaengine.cpp:365
+#: engines/access/metaengine.cpp:386
 msgid "Open save/load menu"
 msgstr "Ouvrir le menu sauvegarder/charger"
 
diff --git a/po/gl_ES.po b/po/gl_ES.po
index 9f9ef1f882e..c5b9f10eefc 100644
--- a/po/gl_ES.po
+++ b/po/gl_ES.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-17 23:07+0000\n"
+"POT-Creation-Date: 2026-06-18 09:25+0000\n"
 "PO-Revision-Date: 2020-09-26 21:13+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Galician <https://translations.scummvm.org/projects/scummvm/"
@@ -89,7 +89,7 @@ msgstr "Pechar"
 #. I18N: Opens the help screen.
 #: gui/browser.cpp:64 gui/launcher.cpp:277 gui/launcher.cpp:959
 #: backends/platform/sdl/macosx/appmenu_osx.mm:319
-#: engines/access/metaengine.cpp:360 engines/bladerunner/metaengine.cpp:321
+#: engines/access/metaengine.cpp:381 engines/bladerunner/metaengine.cpp:321
 #: engines/ngi/metaengine.cpp:162 engines/supernova/metaengine.cpp:238
 #: engines/tucker/metaengine.cpp:215 engines/zvision/metaengine.cpp:246
 msgid "Help"
@@ -2663,7 +2663,7 @@ msgid ""
 msgstr "Eng~a~dir xogo..."
 
 #: gui/options.cpp:2975 engines/engine.cpp:647
-#: engines/access/metaengine.cpp:298 engines/drascula/metaengine.cpp:255
+#: engines/access/metaengine.cpp:319 engines/drascula/metaengine.cpp:255
 #: engines/queen/metaengine.cpp:269 engines/sherlock/metaengine.cpp:410
 #: engines/ultima/nuvie/metaengine.cpp:71
 #, fuzzy
@@ -3967,7 +3967,7 @@ msgstr "Men?? do xogo"
 
 #. I18N: Skipping cutscene plaback
 #. I18N: Skipping cutscene playback
-#: engines/metaengine.cpp:112 engines/access/metaengine.cpp:225
+#: engines/metaengine.cpp:112 engines/access/metaengine.cpp:246
 #: engines/composer/metaengine.cpp:126 engines/drascula/metaengine.cpp:224
 #: engines/efh/metaengine.cpp:379 engines/freescape/movement.cpp:93
 #: engines/grim/grim.cpp:533 engines/grim/grim.cpp:633
@@ -6058,7 +6058,15 @@ msgstr "Iniciador"
 msgid "Check for Updates..."
 msgstr "Buscar actualizaci??ns..."
 
-#: engines/access/metaengine.cpp:215 engines/alcachofa/metaengine.cpp:94
+#: engines/access/metaengine.cpp:82
+msgid "Use 'high definition' OGG music"
+msgstr ""
+
+#: engines/access/metaengine.cpp:83
+msgid "Use the OGG audio from the re-release instead of original MIDI tracks"
+msgstr ""
+
+#: engines/access/metaengine.cpp:236 engines/alcachofa/metaengine.cpp:94
 #: engines/bbvs/metaengine.cpp:92 engines/buried/metaengine.cpp:133
 #: engines/chamber/metaengine.cpp:59 engines/cine/metaengine.cpp:270
 #: engines/cruise/metaengine.cpp:168 engines/dm/metaengine.cpp:124
@@ -6083,20 +6091,20 @@ msgstr "Buscar actualizaci??ns..."
 msgid "Default keymappings"
 msgstr "~O~pci??ns..."
 
-#: engines/access/metaengine.cpp:219
+#: engines/access/metaengine.cpp:240
 #, fuzzy
 #| msgid "Highlight next dialogue"
 msgid "Move / Interact / Skip"
 msgstr "Destacar di??logo seguinte"
 
-#: engines/access/metaengine.cpp:231 engines/sherlock/metaengine.cpp:483
+#: engines/access/metaengine.cpp:252 engines/sherlock/metaengine.cpp:483
 #, fuzzy
 #| msgid "Skip line"
 msgid "Skip movie"
 msgstr "Omitir li??a"
 
 #. I18N: Move Actor to upwards direction
-#: engines/access/metaengine.cpp:237 engines/asylum/metaengine.cpp:171
+#: engines/access/metaengine.cpp:258 engines/asylum/metaengine.cpp:171
 #: engines/asylum/metaengine.cpp:198 engines/cine/metaengine.cpp:377
 #: engines/efh/metaengine.cpp:286 engines/hdb/metaengine.cpp:213
 #: engines/hypno/metaengine.cpp:363 engines/hypno/metaengine.cpp:411
@@ -6107,7 +6115,7 @@ msgid "Move up"
 msgstr "Mover cara atr??s"
 
 #. I18N: Move Actor to downwards direction
-#: engines/access/metaengine.cpp:244 engines/asylum/metaengine.cpp:177
+#: engines/access/metaengine.cpp:265 engines/asylum/metaengine.cpp:177
 #: engines/asylum/metaengine.cpp:205 engines/cine/metaengine.cpp:384
 #: engines/efh/metaengine.cpp:293 engines/hdb/metaengine.cpp:219
 #: engines/hypno/metaengine.cpp:351 engines/hypno/metaengine.cpp:399
@@ -6119,7 +6127,7 @@ msgstr "Cancelar descarga"
 
 #. I18N: Move Actor to left direction
 #. I18N: Move actor left during fight
-#: engines/access/metaengine.cpp:251 engines/asylum/metaengine.cpp:183
+#: engines/access/metaengine.cpp:272 engines/asylum/metaengine.cpp:183
 #: engines/asylum/metaengine.cpp:212 engines/cine/metaengine.cpp:391
 #: engines/efh/metaengine.cpp:300 engines/hdb/metaengine.cpp:225
 #: engines/hypno/metaengine.cpp:345 engines/hypno/metaengine.cpp:393
@@ -6133,7 +6141,7 @@ msgstr "Mover ?? esquerda"
 
 #. I18N: Move Actor to right direction
 #. I18N: Move actor right during fight
-#: engines/access/metaengine.cpp:258 engines/asylum/metaengine.cpp:189
+#: engines/access/metaengine.cpp:279 engines/asylum/metaengine.cpp:189
 #: engines/asylum/metaengine.cpp:219 engines/cine/metaengine.cpp:398
 #: engines/efh/metaengine.cpp:307 engines/hdb/metaengine.cpp:231
 #: engines/hypno/metaengine.cpp:357 engines/hypno/metaengine.cpp:405
@@ -6146,35 +6154,35 @@ msgid "Move right"
 msgstr "Mover ?? dereita"
 
 #. I18N: Move Actor to top-left direction
-#: engines/access/metaengine.cpp:265 engines/cine/metaengine.cpp:405
+#: engines/access/metaengine.cpp:286 engines/cine/metaengine.cpp:405
 #: engines/efh/metaengine.cpp:314
 #, fuzzy
 msgid "Move up-left"
 msgstr "Mover ?? esquerda"
 
 #. I18N: Move Actor to top-right direction
-#: engines/access/metaengine.cpp:270 engines/cine/metaengine.cpp:411
+#: engines/access/metaengine.cpp:291 engines/cine/metaengine.cpp:411
 #: engines/efh/metaengine.cpp:320
 #, fuzzy
 msgid "Move up-right"
 msgstr "Mover ?? dereita"
 
 #. I18N: Move Actor to bottom-left direction
-#: engines/access/metaengine.cpp:275 engines/cine/metaengine.cpp:417
+#: engines/access/metaengine.cpp:296 engines/cine/metaengine.cpp:417
 #: engines/efh/metaengine.cpp:326
 #, fuzzy
 msgid "Move down-left"
 msgstr "Cancelar descarga"
 
 #. I18N: Move Actor to bottom-right direction
-#: engines/access/metaengine.cpp:280 engines/cine/metaengine.cpp:423
+#: engines/access/metaengine.cpp:301 engines/cine/metaengine.cpp:423
 #: engines/efh/metaengine.cpp:332
 #, fuzzy
 msgid "Move down-right"
 msgstr "Mover ?? dereita"
 
 #. I18N: (Game name: The Lost Files of Sherlock Holmes: The Case of the Rose Tattoo) This action is used to look at an object or npc in the game
-#: engines/access/metaengine.cpp:285 engines/bbvs/metaengine.cpp:124
+#: engines/access/metaengine.cpp:306 engines/bbvs/metaengine.cpp:124
 #: engines/dgds/metaengine.cpp:83 engines/drascula/metaengine.cpp:230
 #: engines/petka/metaengine.cpp:130 engines/scumm/help.cpp:167
 #: engines/scumm/help.cpp:231 engines/sherlock/metaengine.cpp:405
@@ -6185,7 +6193,7 @@ msgstr "Mover ?? dereita"
 msgid "Look"
 msgstr "Mirar"
 
-#: engines/access/metaengine.cpp:293 engines/drascula/metaengine.cpp:240
+#: engines/access/metaengine.cpp:314 engines/drascula/metaengine.cpp:240
 #: engines/queen/metaengine.cpp:259 engines/saga/metaengine.cpp:369
 #: engines/scumm/help.cpp:125 engines/scumm/help.cpp:140
 #: engines/scumm/help.cpp:165 engines/scumm/help.cpp:193
@@ -6195,13 +6203,13 @@ msgstr "Mirar"
 msgid "Open"
 msgstr "Abrir"
 
-#: engines/access/metaengine.cpp:303 engines/scumm/help.cpp:128
+#: engines/access/metaengine.cpp:324 engines/scumm/help.cpp:128
 #: engines/ultima/nuvie/metaengine.cpp:70
 msgid "Get"
 msgstr "Coller"
 
 #. I18N: Combat command in Might & Magic 1
-#: engines/access/metaengine.cpp:308 engines/access/metaengine.cpp:329
+#: engines/access/metaengine.cpp:329 engines/access/metaengine.cpp:350
 #: engines/bbvs/metaengine.cpp:136 engines/cine/metaengine.cpp:342
 #: engines/efh/metaengine.cpp:462 engines/hdb/metaengine.cpp:237
 #: engines/lab/metaengine.cpp:323 engines/mm/mm1/metaengine.cpp:86
@@ -6221,11 +6229,11 @@ msgstr "Coller"
 msgid "Use"
 msgstr "Usar"
 
-#: engines/access/metaengine.cpp:313
+#: engines/access/metaengine.cpp:334
 msgid "Goto"
 msgstr ""
 
-#: engines/access/metaengine.cpp:318 engines/access/metaengine.cpp:349
+#: engines/access/metaengine.cpp:339 engines/access/metaengine.cpp:370
 #: engines/bbvs/metaengine.cpp:130 engines/drascula/metaengine.cpp:250
 #: engines/efh/metaengine.cpp:506 engines/petka/metaengine.cpp:158
 #: engines/scumm/help.cpp:174 engines/scumm/help.cpp:226
@@ -6236,11 +6244,11 @@ msgstr ""
 msgid "Talk"
 msgstr "Falar"
 
-#: engines/access/metaengine.cpp:323 engines/scumm/help.cpp:175
+#: engines/access/metaengine.cpp:344 engines/scumm/help.cpp:175
 msgid "Travel"
 msgstr "Viaxar"
 
-#: engines/access/metaengine.cpp:334 engines/cine/metaengine.cpp:332
+#: engines/access/metaengine.cpp:355 engines/cine/metaengine.cpp:332
 #: engines/petka/metaengine.cpp:144 engines/saga/metaengine.cpp:394
 #: engines/supernova/metaengine.cpp:331
 #, fuzzy
@@ -6249,7 +6257,7 @@ msgid "Take"
 msgstr "Quitar"
 
 #. I18N: Action in In Cold Blood
-#: engines/access/metaengine.cpp:339 engines/alcachofa/metaengine.cpp:116
+#: engines/access/metaengine.cpp:360 engines/alcachofa/metaengine.cpp:116
 #: engines/cine/metaengine.cpp:337 engines/crab/input/input.cpp:251
 #: engines/cruise/metaengine.cpp:192 engines/draci/metaengine.cpp:237
 #: engines/dragons/metaengine.cpp:147 engines/drascula/metaengine.cpp:218
@@ -6269,18 +6277,18 @@ msgstr "Quitar"
 msgid "Inventory"
 msgstr "Inventario"
 
-#: engines/access/metaengine.cpp:344 engines/ultima/ultima4/metaengine.cpp:51
+#: engines/access/metaengine.cpp:365 engines/ultima/ultima4/metaengine.cpp:51
 msgid "Climb"
 msgstr ""
 
-#: engines/access/metaengine.cpp:354 engines/bbvs/metaengine.cpp:142
+#: engines/access/metaengine.cpp:375 engines/bbvs/metaengine.cpp:142
 #: engines/freescape/games/castle/castle.cpp:511
 #: engines/petka/metaengine.cpp:137 engines/scumm/help.cpp:225
 #: engines/tsage/metaengine.cpp:201
 msgid "Walk"
 msgstr "Ir a"
 
-#: engines/access/metaengine.cpp:365
+#: engines/access/metaengine.cpp:386
 #, fuzzy
 msgid "Open save/load menu"
 msgstr "Inserir disco de gardado/carga"
diff --git a/po/he.po b/po/he.po
index 8557e26c398..1eef55c8aca 100644
--- a/po/he.po
+++ b/po/he.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.1.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-17 23:07+0000\n"
+"POT-Creation-Date: 2026-06-18 09:25+0000\n"
 "PO-Revision-Date: 2025-04-09 20:18+0000\n"
 "Last-Translator: Niv Baehr <bloop93 at gmail.com>\n"
 "Language-Team: Hebrew <https://translations.scummvm.org/projects/scummvm/"
@@ -85,7 +85,7 @@ msgstr "??????????"
 #. I18N: Opens the help screen.
 #: gui/browser.cpp:64 gui/launcher.cpp:277 gui/launcher.cpp:959
 #: backends/platform/sdl/macosx/appmenu_osx.mm:319
-#: engines/access/metaengine.cpp:360 engines/bladerunner/metaengine.cpp:321
+#: engines/access/metaengine.cpp:381 engines/bladerunner/metaengine.cpp:321
 #: engines/ngi/metaengine.cpp:162 engines/supernova/metaengine.cpp:238
 #: engines/tucker/metaengine.cpp:215 engines/zvision/metaengine.cpp:246
 msgid "Help"
@@ -2552,7 +2552,7 @@ msgstr ""
 "????????..."
 
 #: gui/options.cpp:2975 engines/engine.cpp:647
-#: engines/access/metaengine.cpp:298 engines/drascula/metaengine.cpp:255
+#: engines/access/metaengine.cpp:319 engines/drascula/metaengine.cpp:255
 #: engines/queen/metaengine.cpp:269 engines/sherlock/metaengine.cpp:410
 #: engines/ultima/nuvie/metaengine.cpp:71
 msgid "Move"
@@ -3915,7 +3915,7 @@ msgstr "?????????? ????????"
 
 #. I18N: Skipping cutscene plaback
 #. I18N: Skipping cutscene playback
-#: engines/metaengine.cpp:112 engines/access/metaengine.cpp:225
+#: engines/metaengine.cpp:112 engines/access/metaengine.cpp:246
 #: engines/composer/metaengine.cpp:126 engines/drascula/metaengine.cpp:224
 #: engines/efh/metaengine.cpp:379 engines/freescape/movement.cpp:93
 #: engines/grim/grim.cpp:533 engines/grim/grim.cpp:633
@@ -6016,7 +6016,15 @@ msgstr "?????? ???????? ??????????????"
 msgid "Check for Updates..."
 msgstr "?????????? ??????????????..."
 
-#: engines/access/metaengine.cpp:215 engines/alcachofa/metaengine.cpp:94
+#: engines/access/metaengine.cpp:82
+msgid "Use 'high definition' OGG music"
+msgstr ""
+
+#: engines/access/metaengine.cpp:83
+msgid "Use the OGG audio from the re-release instead of original MIDI tracks"
+msgstr ""
+
+#: engines/access/metaengine.cpp:236 engines/alcachofa/metaengine.cpp:94
 #: engines/bbvs/metaengine.cpp:92 engines/buried/metaengine.cpp:133
 #: engines/chamber/metaengine.cpp:59 engines/cine/metaengine.cpp:270
 #: engines/cruise/metaengine.cpp:168 engines/dm/metaengine.cpp:124
@@ -6042,20 +6050,20 @@ msgstr "?????????? ??????????????..."
 msgid "Default keymappings"
 msgstr "?????????? ??????????"
 
-#: engines/access/metaengine.cpp:219
+#: engines/access/metaengine.cpp:240
 #, fuzzy
 #| msgid "Highlight next dialogue"
 msgid "Move / Interact / Skip"
 msgstr "???????? ???? ?????????????? ??????"
 
-#: engines/access/metaengine.cpp:231 engines/sherlock/metaengine.cpp:483
+#: engines/access/metaengine.cpp:252 engines/sherlock/metaengine.cpp:483
 #, fuzzy
 #| msgid "Skip line"
 msgid "Skip movie"
 msgstr "?????????? ???? ????????"
 
 #. I18N: Move Actor to upwards direction
-#: engines/access/metaengine.cpp:237 engines/asylum/metaengine.cpp:171
+#: engines/access/metaengine.cpp:258 engines/asylum/metaengine.cpp:171
 #: engines/asylum/metaengine.cpp:198 engines/cine/metaengine.cpp:377
 #: engines/efh/metaengine.cpp:286 engines/hdb/metaengine.cpp:213
 #: engines/hypno/metaengine.cpp:363 engines/hypno/metaengine.cpp:411
@@ -6065,7 +6073,7 @@ msgid "Move up"
 msgstr "?????????? ????????"
 
 #. I18N: Move Actor to downwards direction
-#: engines/access/metaengine.cpp:244 engines/asylum/metaengine.cpp:177
+#: engines/access/metaengine.cpp:265 engines/asylum/metaengine.cpp:177
 #: engines/asylum/metaengine.cpp:205 engines/cine/metaengine.cpp:384
 #: engines/efh/metaengine.cpp:293 engines/hdb/metaengine.cpp:219
 #: engines/hypno/metaengine.cpp:351 engines/hypno/metaengine.cpp:399
@@ -6076,7 +6084,7 @@ msgstr "?????????? ??????"
 
 #. I18N: Move Actor to left direction
 #. I18N: Move actor left during fight
-#: engines/access/metaengine.cpp:251 engines/asylum/metaengine.cpp:183
+#: engines/access/metaengine.cpp:272 engines/asylum/metaengine.cpp:183
 #: engines/asylum/metaengine.cpp:212 engines/cine/metaengine.cpp:391
 #: engines/efh/metaengine.cpp:300 engines/hdb/metaengine.cpp:225
 #: engines/hypno/metaengine.cpp:345 engines/hypno/metaengine.cpp:393
@@ -6089,7 +6097,7 @@ msgstr "?????????? ??????????"
 
 #. I18N: Move Actor to right direction
 #. I18N: Move actor right during fight
-#: engines/access/metaengine.cpp:258 engines/asylum/metaengine.cpp:189
+#: engines/access/metaengine.cpp:279 engines/asylum/metaengine.cpp:189
 #: engines/asylum/metaengine.cpp:219 engines/cine/metaengine.cpp:398
 #: engines/efh/metaengine.cpp:307 engines/hdb/metaengine.cpp:231
 #: engines/hypno/metaengine.cpp:357 engines/hypno/metaengine.cpp:405
@@ -6101,7 +6109,7 @@ msgid "Move right"
 msgstr "?????????? ??????????"
 
 #. I18N: Move Actor to top-left direction
-#: engines/access/metaengine.cpp:265 engines/cine/metaengine.cpp:405
+#: engines/access/metaengine.cpp:286 engines/cine/metaengine.cpp:405
 #: engines/efh/metaengine.cpp:314
 #, fuzzy
 #| msgid "Move left"
@@ -6109,7 +6117,7 @@ msgid "Move up-left"
 msgstr "?????????? ??????????"
 
 #. I18N: Move Actor to top-right direction
-#: engines/access/metaengine.cpp:270 engines/cine/metaengine.cpp:411
+#: engines/access/metaengine.cpp:291 engines/cine/metaengine.cpp:411
 #: engines/efh/metaengine.cpp:320
 #, fuzzy
 #| msgid "Move right"
@@ -6117,7 +6125,7 @@ msgid "Move up-right"
 msgstr "?????????? ??????????"
 
 #. I18N: Move Actor to bottom-left direction
-#: engines/access/metaengine.cpp:275 engines/cine/metaengine.cpp:417
+#: engines/access/metaengine.cpp:296 engines/cine/metaengine.cpp:417
 #: engines/efh/metaengine.cpp:326
 #, fuzzy
 #| msgid "Move down"
@@ -6125,7 +6133,7 @@ msgid "Move down-left"
 msgstr "?????????? ??????"
 
 #. I18N: Move Actor to bottom-right direction
-#: engines/access/metaengine.cpp:280 engines/cine/metaengine.cpp:423
+#: engines/access/metaengine.cpp:301 engines/cine/metaengine.cpp:423
 #: engines/efh/metaengine.cpp:332
 #, fuzzy
 #| msgid "Move right"
@@ -6133,7 +6141,7 @@ msgid "Move down-right"
 msgstr "?????????? ??????????"
 
 #. I18N: (Game name: The Lost Files of Sherlock Holmes: The Case of the Rose Tattoo) This action is used to look at an object or npc in the game
-#: engines/access/metaengine.cpp:285 engines/bbvs/metaengine.cpp:124
+#: engines/access/metaengine.cpp:306 engines/bbvs/metaengine.cpp:124
 #: engines/dgds/metaengine.cpp:83 engines/drascula/metaengine.cpp:230
 #: engines/petka/metaengine.cpp:130 engines/scumm/help.cpp:167
 #: engines/scumm/help.cpp:231 engines/sherlock/metaengine.cpp:405
@@ -6144,7 +6152,7 @@ msgstr "?????????? ??????????"
 msgid "Look"
 msgstr "????????"
 
-#: engines/access/metaengine.cpp:293 engines/drascula/metaengine.cpp:240
+#: engines/access/metaengine.cpp:314 engines/drascula/metaengine.cpp:240
 #: engines/queen/metaengine.cpp:259 engines/saga/metaengine.cpp:369
 #: engines/scumm/help.cpp:125 engines/scumm/help.cpp:140
 #: engines/scumm/help.cpp:165 engines/scumm/help.cpp:193
@@ -6154,13 +6162,13 @@ msgstr "????????"
 msgid "Open"
 msgstr "????????"
 
-#: engines/access/metaengine.cpp:303 engines/scumm/help.cpp:128
+#: engines/access/metaengine.cpp:324 engines/scumm/help.cpp:128
 #: engines/ultima/nuvie/metaengine.cpp:70
 msgid "Get"
 msgstr "????????"
 
 #. I18N: Combat command in Might & Magic 1
-#: engines/access/metaengine.cpp:308 engines/access/metaengine.cpp:329
+#: engines/access/metaengine.cpp:329 engines/access/metaengine.cpp:350
 #: engines/bbvs/metaengine.cpp:136 engines/cine/metaengine.cpp:342
 #: engines/efh/metaengine.cpp:462 engines/hdb/metaengine.cpp:237
 #: engines/lab/metaengine.cpp:323 engines/mm/mm1/metaengine.cpp:86
@@ -6180,11 +6188,11 @@ msgstr "????????"
 msgid "Use"
 msgstr "????????????"
 
-#: engines/access/metaengine.cpp:313
+#: engines/access/metaengine.cpp:334
 msgid "Goto"
 msgstr ""
 
-#: engines/access/metaengine.cpp:318 engines/access/metaengine.cpp:349
+#: engines/access/metaengine.cpp:339 engines/access/metaengine.cpp:370
 #: engines/bbvs/metaengine.cpp:130 engines/drascula/metaengine.cpp:250
 #: engines/efh/metaengine.cpp:506 engines/petka/metaengine.cpp:158
 #: engines/scumm/help.cpp:174 engines/scumm/help.cpp:226
@@ -6195,11 +6203,11 @@ msgstr ""
 msgid "Talk"
 msgstr "????????????"
 
-#: engines/access/metaengine.cpp:323 engines/scumm/help.cpp:175
+#: engines/access/metaengine.cpp:344 engines/scumm/help.cpp:175
 msgid "Travel"
 msgstr "????????????"
 
-#: engines/access/metaengine.cpp:334 engines/cine/metaengine.cpp:332
+#: engines/access/metaengine.cpp:355 engines/cine/metaengine.cpp:332
 #: engines/petka/metaengine.cpp:144 engines/saga/metaengine.cpp:394
 #: engines/supernova/metaengine.cpp:331
 #, fuzzy
@@ -6208,7 +6216,7 @@ msgid "Take"
 msgstr "????????????"
 
 #. I18N: Action in In Cold Blood
-#: engines/access/metaengine.cpp:339 engines/alcachofa/metaengine.cpp:116
+#: engines/access/metaengine.cpp:360 engines/alcachofa/metaengine.cpp:116
 #: engines/cine/metaengine.cpp:337 engines/crab/input/input.cpp:251
 #: engines/cruise/metaengine.cpp:192 engines/draci/metaengine.cpp:237
 #: engines/dragons/metaengine.cpp:147 engines/drascula/metaengine.cpp:218
@@ -6228,18 +6236,18 @@ msgstr "????????????"
 msgid "Inventory"
 msgstr "????????"
 
-#: engines/access/metaengine.cpp:344 engines/ultima/ultima4/metaengine.cpp:51
+#: engines/access/metaengine.cpp:365 engines/ultima/ultima4/metaengine.cpp:51
 msgid "Climb"
 msgstr ""
 
-#: engines/access/metaengine.cpp:354 engines/bbvs/metaengine.cpp:142
+#: engines/access/metaengine.cpp:375 engines/bbvs/metaengine.cpp:142
 #: engines/freescape/games/castle/castle.cpp:511
 #: engines/petka/metaengine.cpp:137 engines/scumm/help.cpp:225
 #: engines/tsage/metaengine.cpp:201
 msgid "Walk"
 msgstr "????????"
 
-#: engines/access/metaengine.cpp:365
+#: engines/access/metaengine.cpp:386
 #, fuzzy
 #| msgid "Go to save/load menu"
 msgid "Open save/load menu"
diff --git a/po/hi.po b/po/hi.po
index dc148ff816c..2cb3d5ce4a0 100644
--- a/po/hi.po
+++ b/po/hi.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-17 23:07+0000\n"
+"POT-Creation-Date: 2026-06-18 09:25+0000\n"
 "PO-Revision-Date: 2026-05-12 22:22+0000\n"
 "Last-Translator: KARTIK SAXENA <kartiksaxena2005 at gmail.com>\n"
 "Language-Team: Hindi <https://translations.scummvm.org/projects/scummvm/"
@@ -83,7 +83,7 @@ msgstr "????????? ????????????"
 #. I18N: Opens the help screen.
 #: gui/browser.cpp:64 gui/launcher.cpp:277 gui/launcher.cpp:959
 #: backends/platform/sdl/macosx/appmenu_osx.mm:319
-#: engines/access/metaengine.cpp:360 engines/bladerunner/metaengine.cpp:321
+#: engines/access/metaengine.cpp:381 engines/bladerunner/metaengine.cpp:321
 #: engines/ngi/metaengine.cpp:162 engines/supernova/metaengine.cpp:238
 #: engines/tucker/metaengine.cpp:215 engines/zvision/metaengine.cpp:246
 msgid "Help"
@@ -2600,7 +2600,7 @@ msgstr ""
 "????????? ????????? ??????????????? (A)..."
 
 #: gui/options.cpp:2975 engines/engine.cpp:647
-#: engines/access/metaengine.cpp:298 engines/drascula/metaengine.cpp:255
+#: engines/access/metaengine.cpp:319 engines/drascula/metaengine.cpp:255
 #: engines/queen/metaengine.cpp:269 engines/sherlock/metaengine.cpp:410
 #: engines/ultima/nuvie/metaengine.cpp:71
 msgid "Move"
@@ -3840,7 +3840,7 @@ msgstr "????????? ????????????"
 
 #. I18N: Skipping cutscene plaback
 #. I18N: Skipping cutscene playback
-#: engines/metaengine.cpp:112 engines/access/metaengine.cpp:225
+#: engines/metaengine.cpp:112 engines/access/metaengine.cpp:246
 #: engines/composer/metaengine.cpp:126 engines/drascula/metaengine.cpp:224
 #: engines/efh/metaengine.cpp:379 engines/freescape/movement.cpp:93
 #: engines/grim/grim.cpp:533 engines/grim/grim.cpp:633
@@ -5839,7 +5839,15 @@ msgstr ""
 msgid "Check for Updates..."
 msgstr ""
 
-#: engines/access/metaengine.cpp:215 engines/alcachofa/metaengine.cpp:94
+#: engines/access/metaengine.cpp:82
+msgid "Use 'high definition' OGG music"
+msgstr ""
+
+#: engines/access/metaengine.cpp:83
+msgid "Use the OGG audio from the re-release instead of original MIDI tracks"
+msgstr ""
+
+#: engines/access/metaengine.cpp:236 engines/alcachofa/metaengine.cpp:94
 #: engines/bbvs/metaengine.cpp:92 engines/buried/metaengine.cpp:133
 #: engines/chamber/metaengine.cpp:59 engines/cine/metaengine.cpp:270
 #: engines/cruise/metaengine.cpp:168 engines/dm/metaengine.cpp:124
@@ -5865,20 +5873,20 @@ msgstr ""
 msgid "Default keymappings"
 msgstr "????????? ??????????????????(O)..."
 
-#: engines/access/metaengine.cpp:219
+#: engines/access/metaengine.cpp:240
 #, fuzzy
 #| msgid "Go to end of line"
 msgid "Move / Interact / Skip"
 msgstr "?????????????????? ?????? ????????? ????????? ????????????"
 
-#: engines/access/metaengine.cpp:231 engines/sherlock/metaengine.cpp:483
+#: engines/access/metaengine.cpp:252 engines/sherlock/metaengine.cpp:483
 #, fuzzy
 #| msgid "Skip autosave"
 msgid "Skip movie"
 msgstr "?????????????????? ??????????????? ????????????"
 
 #. I18N: Move Actor to upwards direction
-#: engines/access/metaengine.cpp:237 engines/asylum/metaengine.cpp:171
+#: engines/access/metaengine.cpp:258 engines/asylum/metaengine.cpp:171
 #: engines/asylum/metaengine.cpp:198 engines/cine/metaengine.cpp:377
 #: engines/efh/metaengine.cpp:286 engines/hdb/metaengine.cpp:213
 #: engines/hypno/metaengine.cpp:363 engines/hypno/metaengine.cpp:411
@@ -5888,7 +5896,7 @@ msgid "Move up"
 msgstr "????????? ?????? ????????????"
 
 #. I18N: Move Actor to downwards direction
-#: engines/access/metaengine.cpp:244 engines/asylum/metaengine.cpp:177
+#: engines/access/metaengine.cpp:265 engines/asylum/metaengine.cpp:177
 #: engines/asylum/metaengine.cpp:205 engines/cine/metaengine.cpp:384
 #: engines/efh/metaengine.cpp:293 engines/hdb/metaengine.cpp:219
 #: engines/hypno/metaengine.cpp:351 engines/hypno/metaengine.cpp:399
@@ -5899,7 +5907,7 @@ msgstr "???????????? ?????? ????????????"
 
 #. I18N: Move Actor to left direction
 #. I18N: Move actor left during fight
-#: engines/access/metaengine.cpp:251 engines/asylum/metaengine.cpp:183
+#: engines/access/metaengine.cpp:272 engines/asylum/metaengine.cpp:183
 #: engines/asylum/metaengine.cpp:212 engines/cine/metaengine.cpp:391
 #: engines/efh/metaengine.cpp:300 engines/hdb/metaengine.cpp:225
 #: engines/hypno/metaengine.cpp:345 engines/hypno/metaengine.cpp:393
@@ -5912,7 +5920,7 @@ msgstr ""
 
 #. I18N: Move Actor to right direction
 #. I18N: Move actor right during fight
-#: engines/access/metaengine.cpp:258 engines/asylum/metaengine.cpp:189
+#: engines/access/metaengine.cpp:279 engines/asylum/metaengine.cpp:189
 #: engines/asylum/metaengine.cpp:219 engines/cine/metaengine.cpp:398
 #: engines/efh/metaengine.cpp:307 engines/hdb/metaengine.cpp:231
 #: engines/hypno/metaengine.cpp:357 engines/hypno/metaengine.cpp:405
@@ -5925,7 +5933,7 @@ msgid "Move right"
 msgstr "??????????????? ??????????????????"
 
 #. I18N: Move Actor to top-left direction
-#: engines/access/metaengine.cpp:265 engines/cine/metaengine.cpp:405
+#: engines/access/metaengine.cpp:286 engines/cine/metaengine.cpp:405
 #: engines/efh/metaengine.cpp:314
 #, fuzzy
 #| msgid "Move Left"
@@ -5933,14 +5941,14 @@ msgid "Move up-left"
 msgstr "???????????? ????????????"
 
 #. I18N: Move Actor to top-right direction
-#: engines/access/metaengine.cpp:270 engines/cine/metaengine.cpp:411
+#: engines/access/metaengine.cpp:291 engines/cine/metaengine.cpp:411
 #: engines/efh/metaengine.cpp:320
 #, fuzzy
 msgid "Move up-right"
 msgstr "??????????????? ??????????????????"
 
 #. I18N: Move Actor to bottom-left direction
-#: engines/access/metaengine.cpp:275 engines/cine/metaengine.cpp:417
+#: engines/access/metaengine.cpp:296 engines/cine/metaengine.cpp:417
 #: engines/efh/metaengine.cpp:326
 #, fuzzy
 #| msgid "Move down"
@@ -5948,14 +5956,14 @@ msgid "Move down-left"
 msgstr "???????????? ?????? ????????????"
 
 #. I18N: Move Actor to bottom-right direction
-#: engines/access/metaengine.cpp:280 engines/cine/metaengine.cpp:423
+#: engines/access/metaengine.cpp:301 engines/cine/metaengine.cpp:423
 #: engines/efh/metaengine.cpp:332
 #, fuzzy
 msgid "Move down-right"
 msgstr "??????????????? ??????????????????"
 
 #. I18N: (Game name: The Lost Files of Sherlock Holmes: The Case of the Rose Tattoo) This action is used to look at an object or npc in the game
-#: engines/access/metaengine.cpp:285 engines/bbvs/metaengine.cpp:124
+#: engines/access/metaengine.cpp:306 engines/bbvs/metaengine.cpp:124
 #: engines/dgds/metaengine.cpp:83 engines/drascula/metaengine.cpp:230
 #: engines/petka/metaengine.cpp:130 engines/scumm/help.cpp:167
 #: engines/scumm/help.cpp:231 engines/sherlock/metaengine.cpp:405
@@ -5966,7 +5974,7 @@ msgstr "??????????????? ??????????????????"
 msgid "Look"
 msgstr "????????????"
 
-#: engines/access/metaengine.cpp:293 engines/drascula/metaengine.cpp:240
+#: engines/access/metaengine.cpp:314 engines/drascula/metaengine.cpp:240
 #: engines/queen/metaengine.cpp:259 engines/saga/metaengine.cpp:369
 #: engines/scumm/help.cpp:125 engines/scumm/help.cpp:140
 #: engines/scumm/help.cpp:165 engines/scumm/help.cpp:193
@@ -5976,13 +5984,13 @@ msgstr "????????????"
 msgid "Open"
 msgstr ""
 
-#: engines/access/metaengine.cpp:303 engines/scumm/help.cpp:128
+#: engines/access/metaengine.cpp:324 engines/scumm/help.cpp:128
 #: engines/ultima/nuvie/metaengine.cpp:70
 msgid "Get"
 msgstr ""
 
 #. I18N: Combat command in Might & Magic 1
-#: engines/access/metaengine.cpp:308 engines/access/metaengine.cpp:329
+#: engines/access/metaengine.cpp:329 engines/access/metaengine.cpp:350
 #: engines/bbvs/metaengine.cpp:136 engines/cine/metaengine.cpp:342
 #: engines/efh/metaengine.cpp:462 engines/hdb/metaengine.cpp:237
 #: engines/lab/metaengine.cpp:323 engines/mm/mm1/metaengine.cpp:86
@@ -6002,11 +6010,11 @@ msgstr ""
 msgid "Use"
 msgstr ""
 
-#: engines/access/metaengine.cpp:313
+#: engines/access/metaengine.cpp:334
 msgid "Goto"
 msgstr ""
 
-#: engines/access/metaengine.cpp:318 engines/access/metaengine.cpp:349
+#: engines/access/metaengine.cpp:339 engines/access/metaengine.cpp:370
 #: engines/bbvs/metaengine.cpp:130 engines/drascula/metaengine.cpp:250
 #: engines/efh/metaengine.cpp:506 engines/petka/metaengine.cpp:158
 #: engines/scumm/help.cpp:174 engines/scumm/help.cpp:226
@@ -6017,18 +6025,18 @@ msgstr ""
 msgid "Talk"
 msgstr ""
 
-#: engines/access/metaengine.cpp:323 engines/scumm/help.cpp:175
+#: engines/access/metaengine.cpp:344 engines/scumm/help.cpp:175
 msgid "Travel"
 msgstr ""
 
-#: engines/access/metaengine.cpp:334 engines/cine/metaengine.cpp:332
+#: engines/access/metaengine.cpp:355 engines/cine/metaengine.cpp:332
 #: engines/petka/metaengine.cpp:144 engines/saga/metaengine.cpp:394
 #: engines/supernova/metaengine.cpp:331
 msgid "Take"
 msgstr ""
 
 #. I18N: Action in In Cold Blood
-#: engines/access/metaengine.cpp:339 engines/alcachofa/metaengine.cpp:116
+#: engines/access/metaengine.cpp:360 engines/alcachofa/metaengine.cpp:116
 #: engines/cine/metaengine.cpp:337 engines/crab/input/input.cpp:251
 #: engines/cruise/metaengine.cpp:192 engines/draci/metaengine.cpp:237
 #: engines/dragons/metaengine.cpp:147 engines/drascula/metaengine.cpp:218
@@ -6048,18 +6056,18 @@ msgstr ""
 msgid "Inventory"
 msgstr ""
 
-#: engines/access/metaengine.cpp:344 engines/ultima/ultima4/metaengine.cpp:51
+#: engines/access/metaengine.cpp:365 engines/ultima/ultima4/metaengine.cpp:51
 msgid "Climb"
 msgstr ""
 
-#: engines/access/metaengine.cpp:354 engines/bbvs/metaengine.cpp:142
+#: engines/access/metaengine.cpp:375 engines/bbvs/metaengine.cpp:142
 #: engines/freescape/games/castle/castle.cpp:511
 #: engines/petka/metaengine.cpp:137 engines/scumm/help.cpp:225
 #: engines/tsage/metaengine.cpp:201
 msgid "Walk"
 msgstr ""
 
-#: engines/access/metaengine.cpp:365
+#: engines/access/metaengine.cpp:386
 #, fuzzy
 #| msgid "Go to end of line"
 msgid "Open save/load menu"
diff --git a/po/hu_HU.po b/po/hu_HU.po
index b066a03f335..53774f546eb 100644
--- a/po/hu_HU.po
+++ b/po/hu_HU.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-17 23:07+0000\n"
+"POT-Creation-Date: 2026-06-18 09:25+0000\n"
 "PO-Revision-Date: 2025-03-07 00:18+0000\n"
 "Last-Translator: Hov??nszki Tam??s <gahex68379 at lofiey.com>\n"
 "Language-Team: Hungarian <https://translations.scummvm.org/projects/scummvm/"
@@ -84,7 +84,7 @@ msgstr "Bez??r??s"
 #. I18N: Opens the help screen.
 #: gui/browser.cpp:64 gui/launcher.cpp:277 gui/launcher.cpp:959
 #: backends/platform/sdl/macosx/appmenu_osx.mm:319
-#: engines/access/metaengine.cpp:360 engines/bladerunner/metaengine.cpp:321
+#: engines/access/metaengine.cpp:381 engines/bladerunner/metaengine.cpp:321
 #: engines/ngi/metaengine.cpp:162 engines/supernova/metaengine.cpp:238
 #: engines/tucker/metaengine.cpp:215 engines/zvision/metaengine.cpp:246
 msgid "Help"
@@ -2741,7 +2741,7 @@ msgstr ""
 "Tov??bbiak..."
 
 #: gui/options.cpp:2975 engines/engine.cpp:647
-#: engines/access/metaengine.cpp:298 engines/drascula/metaengine.cpp:255
+#: engines/access/metaengine.cpp:319 engines/drascula/metaengine.cpp:255
 #: engines/queen/metaengine.cpp:269 engines/sherlock/metaengine.cpp:410
 #: engines/ultima/nuvie/metaengine.cpp:71
 msgid "Move"
@@ -4073,7 +4073,7 @@ msgstr "J??t??k men??"
 
 #. I18N: Skipping cutscene plaback
 #. I18N: Skipping cutscene playback
-#: engines/metaengine.cpp:112 engines/access/metaengine.cpp:225
+#: engines/metaengine.cpp:112 engines/access/metaengine.cpp:246
 #: engines/composer/metaengine.cpp:126 engines/drascula/metaengine.cpp:224
 #: engines/efh/metaengine.cpp:379 engines/freescape/movement.cpp:93
 #: engines/grim/grim.cpp:533 engines/grim/grim.cpp:633
@@ -6560,7 +6560,15 @@ msgstr "Ind??t??pult"
 msgid "Check for Updates..."
 msgstr "Friss??t??sek keres??se..."
 
-#: engines/access/metaengine.cpp:215 engines/alcachofa/metaengine.cpp:94
+#: engines/access/metaengine.cpp:82
+msgid "Use 'high definition' OGG music"
+msgstr ""
+
+#: engines/access/metaengine.cpp:83
+msgid "Use the OGG audio from the re-release instead of original MIDI tracks"
+msgstr ""
+
+#: engines/access/metaengine.cpp:236 engines/alcachofa/metaengine.cpp:94
 #: engines/bbvs/metaengine.cpp:92 engines/buried/metaengine.cpp:133
 #: engines/chamber/metaengine.cpp:59 engines/cine/metaengine.cpp:270
 #: engines/cruise/metaengine.cpp:168 engines/dm/metaengine.cpp:124
@@ -6584,20 +6592,20 @@ msgstr "Friss??t??sek keres??se..."
 msgid "Default keymappings"
 msgstr "Alap??rtelmezett kioszt??sok"
 
-#: engines/access/metaengine.cpp:219
+#: engines/access/metaengine.cpp:240
 #, fuzzy
 #| msgid "Go to next dialogue"
 msgid "Move / Interact / Skip"
 msgstr "Ugr??s a k??vetkez?? dial??gusra"
 
-#: engines/access/metaengine.cpp:231 engines/sherlock/metaengine.cpp:483
+#: engines/access/metaengine.cpp:252 engines/sherlock/metaengine.cpp:483
 #, fuzzy
 #| msgid "Skip video"
 msgid "Skip movie"
 msgstr "Vide?? ??tugr??sa"
 
 #. I18N: Move Actor to upwards direction
-#: engines/access/metaengine.cpp:237 engines/asylum/metaengine.cpp:171
+#: engines/access/metaengine.cpp:258 engines/asylum/metaengine.cpp:171
 #: engines/asylum/metaengine.cpp:198 engines/cine/metaengine.cpp:377
 #: engines/efh/metaengine.cpp:286 engines/hdb/metaengine.cpp:213
 #: engines/hypno/metaengine.cpp:363 engines/hypno/metaengine.cpp:411
@@ -6607,7 +6615,7 @@ msgid "Move up"
 msgstr "Mozg??s fel"
 
 #. I18N: Move Actor to downwards direction
-#: engines/access/metaengine.cpp:244 engines/asylum/metaengine.cpp:177
+#: engines/access/metaengine.cpp:265 engines/asylum/metaengine.cpp:177
 #: engines/asylum/metaengine.cpp:205 engines/cine/metaengine.cpp:384
 #: engines/efh/metaengine.cpp:293 engines/hdb/metaengine.cpp:219
 #: engines/hypno/metaengine.cpp:351 engines/hypno/metaengine.cpp:399
@@ -6618,7 +6626,7 @@ msgstr "Mozg??s le"
 
 #. I18N: Move Actor to left direction
 #. I18N: Move actor left during fight
-#: engines/access/metaengine.cpp:251 engines/asylum/metaengine.cpp:183
+#: engines/access/metaengine.cpp:272 engines/asylum/metaengine.cpp:183
 #: engines/asylum/metaengine.cpp:212 engines/cine/metaengine.cpp:391
 #: engines/efh/metaengine.cpp:300 engines/hdb/metaengine.cpp:225
 #: engines/hypno/metaengine.cpp:345 engines/hypno/metaengine.cpp:393
@@ -6631,7 +6639,7 @@ msgstr "Mozg??s balra"
 
 #. I18N: Move Actor to right direction
 #. I18N: Move actor right during fight
-#: engines/access/metaengine.cpp:258 engines/asylum/metaengine.cpp:189
+#: engines/access/metaengine.cpp:279 engines/asylum/metaengine.cpp:189
 #: engines/asylum/metaengine.cpp:219 engines/cine/metaengine.cpp:398
 #: engines/efh/metaengine.cpp:307 engines/hdb/metaengine.cpp:231
 #: engines/hypno/metaengine.cpp:357 engines/hypno/metaengine.cpp:405
@@ -6643,31 +6651,31 @@ msgid "Move right"
 msgstr "Mozg??s jobbra"
 
 #. I18N: Move Actor to top-left direction
-#: engines/access/metaengine.cpp:265 engines/cine/metaengine.cpp:405
+#: engines/access/metaengine.cpp:286 engines/cine/metaengine.cpp:405
 #: engines/efh/metaengine.cpp:314
 msgid "Move up-left"
 msgstr "Mozg??s bal-felfel??"
 
 #. I18N: Move Actor to top-right direction
-#: engines/access/metaengine.cpp:270 engines/cine/metaengine.cpp:411
+#: engines/access/metaengine.cpp:291 engines/cine/metaengine.cpp:411
 #: engines/efh/metaengine.cpp:320
 msgid "Move up-right"
 msgstr "Mozg??s jobb-felfel??"
 
 #. I18N: Move Actor to bottom-left direction
-#: engines/access/metaengine.cpp:275 engines/cine/metaengine.cpp:417
+#: engines/access/metaengine.cpp:296 engines/cine/metaengine.cpp:417
 #: engines/efh/metaengine.cpp:326
 msgid "Move down-left"
 msgstr "Mozg??s bal-lefele"
 
 #. I18N: Move Actor to bottom-right direction
-#: engines/access/metaengine.cpp:280 engines/cine/metaengine.cpp:423
+#: engines/access/metaengine.cpp:301 engines/cine/metaengine.cpp:423
 #: engines/efh/metaengine.cpp:332
 msgid "Move down-right"
 msgstr "Mozg??s jobb-lefele"
 
 #. I18N: (Game name: The Lost Files of Sherlock Holmes: The Case of the Rose Tattoo) This action is used to look at an object or npc in the game
-#: engines/access/metaengine.cpp:285 engines/bbvs/metaengine.cpp:124
+#: engines/access/metaengine.cpp:306 engines/bbvs/metaengine.cpp:124
 #: engines/dgds/metaengine.cpp:83 engines/drascula/metaengine.cpp:230
 #: engines/petka/metaengine.cpp:130 engines/scumm/help.cpp:167
 #: engines/scumm/help.cpp:231 engines/sherlock/metaengine.cpp:405
@@ -6678,7 +6686,7 @@ msgstr "Mozg??s jobb-lefele"
 msgid "Look"
 msgstr "Megn??z"
 
-#: engines/access/metaengine.cpp:293 engines/drascula/metaengine.cpp:240
+#: engines/access/metaengine.cpp:314 engines/drascula/metaengine.cpp:240
 #: engines/queen/metaengine.cpp:259 engines/saga/metaengine.cpp:369
 #: engines/scumm/help.cpp:125 engines/scumm/help.cpp:140
 #: engines/scumm/help.cpp:165 engines/scumm/help.cpp:193
@@ -6688,13 +6696,13 @@ msgstr "Megn??z"
 msgid "Open"
 msgstr "Kinyit"
 
-#: engines/access/metaengine.cpp:303 engines/scumm/help.cpp:128
+#: engines/access/metaengine.cpp:324 engines/scumm/help.cpp:128
 #: engines/ultima/nuvie/metaengine.cpp:70
 msgid "Get"
 msgstr "Vesz"
 
 #. I18N: Combat command in Might & Magic 1
-#: engines/access/metaengine.cpp:308 engines/access/metaengine.cpp:329
+#: engines/access/metaengine.cpp:329 engines/access/metaengine.cpp:350
 #: engines/bbvs/metaengine.cpp:136 engines/cine/metaengine.cpp:342
 #: engines/efh/metaengine.cpp:462 engines/hdb/metaengine.cpp:237
 #: engines/lab/metaengine.cpp:323 engines/mm/mm1/metaengine.cpp:86
@@ -6714,11 +6722,11 @@ msgstr "Vesz"
 msgid "Use"
 msgstr "Haszn??l"
 
-#: engines/access/metaengine.cpp:313
+#: engines/access/metaengine.cpp:334
 msgid "Goto"
 msgstr ""
 
-#: engines/access/metaengine.cpp:318 engines/access/metaengine.cpp:349
+#: engines/access/metaengine.cpp:339 engines/access/metaengine.cpp:370
 #: engines/bbvs/metaengine.cpp:130 engines/drascula/metaengine.cpp:250
 #: engines/efh/metaengine.cpp:506 engines/petka/metaengine.cpp:158
 #: engines/scumm/help.cpp:174 engines/scumm/help.cpp:226
@@ -6729,18 +6737,18 @@ msgstr ""
 msgid "Talk"
 msgstr "Besz??l"
 
-#: engines/access/metaengine.cpp:323 engines/scumm/help.cpp:175
+#: engines/access/metaengine.cpp:344 engines/scumm/help.cpp:175
 msgid "Travel"
 msgstr "Utaz??s"
 
-#: engines/access/metaengine.cpp:334 engines/cine/metaengine.cpp:332
+#: engines/access/metaengine.cpp:355 engines/cine/metaengine.cpp:332
 #: engines/petka/metaengine.cpp:144 engines/saga/metaengine.cpp:394
 #: engines/supernova/metaengine.cpp:331
 msgid "Take"
 msgstr "Tesz"
 
 #. I18N: Action in In Cold Blood
-#: engines/access/metaengine.cpp:339 engines/alcachofa/metaengine.cpp:116
+#: engines/access/metaengine.cpp:360 engines/alcachofa/metaengine.cpp:116
 #: engines/cine/metaengine.cpp:337 engines/crab/input/input.cpp:251
 #: engines/cruise/metaengine.cpp:192 engines/draci/metaengine.cpp:237
 #: engines/dragons/metaengine.cpp:147 engines/drascula/metaengine.cpp:218
@@ -6760,18 +6768,18 @@ msgstr "Tesz"
 msgid "Inventory"
 msgstr "Felszerel??s"
 
-#: engines/access/metaengine.cpp:344 engines/ultima/ultima4/metaengine.cpp:51
+#: engines/access/metaengine.cpp:365 engines/ultima/ultima4/metaengine.cpp:51
 msgid "Climb"
 msgstr ""
 
-#: engines/access/metaengine.cpp:354 engines/bbvs/metaengine.cpp:142
+#: engines/access/metaengine.cpp:375 engines/bbvs/metaengine.cpp:142
 #: engines/freescape/games/castle/castle.cpp:511
 #: engines/petka/metaengine.cpp:137 engines/scumm/help.cpp:225
 #: engines/tsage/metaengine.cpp:201
 msgid "Walk"
 msgstr "Megy"
 
-#: engines/access/metaengine.cpp:365
+#: engines/access/metaengine.cpp:386
 #, fuzzy
 #| msgid "Page Up in save/load menu"
 msgid "Open save/load menu"
diff --git a/po/it_IT.po b/po/it_IT.po
index e1a12177b42..6284ccfa54d 100644
--- a/po/it_IT.po
+++ b/po/it_IT.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-17 23:07+0000\n"
+"POT-Creation-Date: 2026-06-18 09:25+0000\n"
 "PO-Revision-Date: 2026-06-14 09:41+0000\n"
 "Last-Translator: Walter Agazzi <walter.agazzi at protonmail.com>\n"
 "Language-Team: Italian <https://translations.scummvm.org/projects/scummvm/"
@@ -83,7 +83,7 @@ msgstr "Chiudi"
 #. I18N: Opens the help screen.
 #: gui/browser.cpp:64 gui/launcher.cpp:277 gui/launcher.cpp:959
 #: backends/platform/sdl/macosx/appmenu_osx.mm:319
-#: engines/access/metaengine.cpp:360 engines/bladerunner/metaengine.cpp:321
+#: engines/access/metaengine.cpp:381 engines/bladerunner/metaengine.cpp:321
 #: engines/ngi/metaengine.cpp:162 engines/supernova/metaengine.cpp:238
 #: engines/tucker/metaengine.cpp:215 engines/zvision/metaengine.cpp:246
 msgid "Help"
@@ -2699,7 +2699,7 @@ msgstr ""
 "E altri ancora..."
 
 #: gui/options.cpp:2975 engines/engine.cpp:647
-#: engines/access/metaengine.cpp:298 engines/drascula/metaengine.cpp:255
+#: engines/access/metaengine.cpp:319 engines/drascula/metaengine.cpp:255
 #: engines/queen/metaengine.cpp:269 engines/sherlock/metaengine.cpp:410
 #: engines/ultima/nuvie/metaengine.cpp:71
 msgid "Move"
@@ -4021,7 +4021,7 @@ msgstr "Men?? di gioco"
 
 #. I18N: Skipping cutscene plaback
 #. I18N: Skipping cutscene playback
-#: engines/metaengine.cpp:112 engines/access/metaengine.cpp:225
+#: engines/metaengine.cpp:112 engines/access/metaengine.cpp:246
 #: engines/composer/metaengine.cpp:126 engines/drascula/metaengine.cpp:224
 #: engines/efh/metaengine.cpp:379 engines/freescape/movement.cpp:93
 #: engines/grim/grim.cpp:533 engines/grim/grim.cpp:633
@@ -6349,7 +6349,15 @@ msgstr "Elenco giochi"
 msgid "Check for Updates..."
 msgstr "Cerca aggiornamenti..."
 
-#: engines/access/metaengine.cpp:215 engines/alcachofa/metaengine.cpp:94
+#: engines/access/metaengine.cpp:82
+msgid "Use 'high definition' OGG music"
+msgstr ""
+
+#: engines/access/metaengine.cpp:83
+msgid "Use the OGG audio from the re-release instead of original MIDI tracks"
+msgstr ""
+
+#: engines/access/metaengine.cpp:236 engines/alcachofa/metaengine.cpp:94
 #: engines/bbvs/metaengine.cpp:92 engines/buried/metaengine.cpp:133
 #: engines/chamber/metaengine.cpp:59 engines/cine/metaengine.cpp:270
 #: engines/cruise/metaengine.cpp:168 engines/dm/metaengine.cpp:124
@@ -6373,16 +6381,16 @@ msgstr "Cerca aggiornamenti..."
 msgid "Default keymappings"
 msgstr "Mappe tasti predefinite"
 
-#: engines/access/metaengine.cpp:219
+#: engines/access/metaengine.cpp:240
 msgid "Move / Interact / Skip"
 msgstr "Movimento / Interazione / Salta"
 
-#: engines/access/metaengine.cpp:231 engines/sherlock/metaengine.cpp:483
+#: engines/access/metaengine.cpp:252 engines/sherlock/metaengine.cpp:483
 msgid "Skip movie"
 msgstr "Salta filmato"
 
 #. I18N: Move Actor to upwards direction
-#: engines/access/metaengine.cpp:237 engines/asylum/metaengine.cpp:171
+#: engines/access/metaengine.cpp:258 engines/asylum/metaengine.cpp:171
 #: engines/asylum/metaengine.cpp:198 engines/cine/metaengine.cpp:377
 #: engines/efh/metaengine.cpp:286 engines/hdb/metaengine.cpp:213
 #: engines/hypno/metaengine.cpp:363 engines/hypno/metaengine.cpp:411
@@ -6392,7 +6400,7 @@ msgid "Move up"
 msgstr "Movimento verso l'alto"
 
 #. I18N: Move Actor to downwards direction
-#: engines/access/metaengine.cpp:244 engines/asylum/metaengine.cpp:177
+#: engines/access/metaengine.cpp:265 engines/asylum/metaengine.cpp:177
 #: engines/asylum/metaengine.cpp:205 engines/cine/metaengine.cpp:384
 #: engines/efh/metaengine.cpp:293 engines/hdb/metaengine.cpp:219
 #: engines/hypno/metaengine.cpp:351 engines/hypno/metaengine.cpp:399
@@ -6403,7 +6411,7 @@ msgstr "Movimento verso il basso"
 
 #. I18N: Move Actor to left direction
 #. I18N: Move actor left during fight
-#: engines/access/metaengine.cpp:251 engines/asylum/metaengine.cpp:183
+#: engines/access/metaengine.cpp:272 engines/asylum/metaengine.cpp:183
 #: engines/asylum/metaengine.cpp:212 engines/cine/metaengine.cpp:391
 #: engines/efh/metaengine.cpp:300 engines/hdb/metaengine.cpp:225
 #: engines/hypno/metaengine.cpp:345 engines/hypno/metaengine.cpp:393
@@ -6416,7 +6424,7 @@ msgstr "Movimento verso sinistra"
 
 #. I18N: Move Actor to right direction
 #. I18N: Move actor right during fight
-#: engines/access/metaengine.cpp:258 engines/asylum/metaengine.cpp:189
+#: engines/access/metaengine.cpp:279 engines/asylum/metaengine.cpp:189
 #: engines/asylum/metaengine.cpp:219 engines/cine/metaengine.cpp:398
 #: engines/efh/metaengine.cpp:307 engines/hdb/metaengine.cpp:231
 #: engines/hypno/metaengine.cpp:357 engines/hypno/metaengine.cpp:405
@@ -6428,31 +6436,31 @@ msgid "Move right"
 msgstr "Movimento verso destra"
 
 #. I18N: Move Actor to top-left direction
-#: engines/access/metaengine.cpp:265 engines/cine/metaengine.cpp:405
+#: engines/access/metaengine.cpp:286 engines/cine/metaengine.cpp:405
 #: engines/efh/metaengine.cpp:314
 msgid "Move up-left"
 msgstr "Movimento verso l'alto e a sinistra"
 
 #. I18N: Move Actor to top-right direction
-#: engines/access/metaengine.cpp:270 engines/cine/metaengine.cpp:411
+#: engines/access/metaengine.cpp:291 engines/cine/metaengine.cpp:411
 #: engines/efh/metaengine.cpp:320
 msgid "Move up-right"
 msgstr "Movimento verso l'alto e a destra"
 
 #. I18N: Move Actor to bottom-left direction
-#: engines/access/metaengine.cpp:275 engines/cine/metaengine.cpp:417
+#: engines/access/metaengine.cpp:296 engines/cine/metaengine.cpp:417
 #: engines/efh/metaengine.cpp:326
 msgid "Move down-left"
 msgstr "Movimento verso il basso e a sinistra"
 
 #. I18N: Move Actor to bottom-right direction
-#: engines/access/metaengine.cpp:280 engines/cine/metaengine.cpp:423
+#: engines/access/metaengine.cpp:301 engines/cine/metaengine.cpp:423
 #: engines/efh/metaengine.cpp:332
 msgid "Move down-right"
 msgstr "Movimento verso il basso e a destra"
 
 #. I18N: (Game name: The Lost Files of Sherlock Holmes: The Case of the Rose Tattoo) This action is used to look at an object or npc in the game
-#: engines/access/metaengine.cpp:285 engines/bbvs/metaengine.cpp:124
+#: engines/access/metaengine.cpp:306 engines/bbvs/metaengine.cpp:124
 #: engines/dgds/metaengine.cpp:83 engines/drascula/metaengine.cpp:230
 #: engines/petka/metaengine.cpp:130 engines/scumm/help.cpp:167
 #: engines/scumm/help.cpp:231 engines/sherlock/metaengine.cpp:405
@@ -6463,7 +6471,7 @@ msgstr "Movimento verso il basso e a destra"
 msgid "Look"
 msgstr "Guarda"
 
-#: engines/access/metaengine.cpp:293 engines/drascula/metaengine.cpp:240
+#: engines/access/metaengine.cpp:314 engines/drascula/metaengine.cpp:240
 #: engines/queen/metaengine.cpp:259 engines/saga/metaengine.cpp:369
 #: engines/scumm/help.cpp:125 engines/scumm/help.cpp:140
 #: engines/scumm/help.cpp:165 engines/scumm/help.cpp:193
@@ -6473,13 +6481,13 @@ msgstr "Guarda"
 msgid "Open"
 msgstr "Apri"
 
-#: engines/access/metaengine.cpp:303 engines/scumm/help.cpp:128
+#: engines/access/metaengine.cpp:324 engines/scumm/help.cpp:128
 #: engines/ultima/nuvie/metaengine.cpp:70
 msgid "Get"
 msgstr "Prendi"
 
 #. I18N: Combat command in Might & Magic 1
-#: engines/access/metaengine.cpp:308 engines/access/metaengine.cpp:329
+#: engines/access/metaengine.cpp:329 engines/access/metaengine.cpp:350
 #: engines/bbvs/metaengine.cpp:136 engines/cine/metaengine.cpp:342
 #: engines/efh/metaengine.cpp:462 engines/hdb/metaengine.cpp:237
 #: engines/lab/metaengine.cpp:323 engines/mm/mm1/metaengine.cpp:86
@@ -6499,11 +6507,11 @@ msgstr "Prendi"
 msgid "Use"
 msgstr "Usa"
 
-#: engines/access/metaengine.cpp:313
+#: engines/access/metaengine.cpp:334
 msgid "Goto"
 msgstr "Vai verso"
 
-#: engines/access/metaengine.cpp:318 engines/access/metaengine.cpp:349
+#: engines/access/metaengine.cpp:339 engines/access/metaengine.cpp:370
 #: engines/bbvs/metaengine.cpp:130 engines/drascula/metaengine.cpp:250
 #: engines/efh/metaengine.cpp:506 engines/petka/metaengine.cpp:158
 #: engines/scumm/help.cpp:174 engines/scumm/help.cpp:226
@@ -6514,18 +6522,18 @@ msgstr "Vai verso"
 msgid "Talk"
 msgstr "Parla"
 
-#: engines/access/metaengine.cpp:323 engines/scumm/help.cpp:175
+#: engines/access/metaengine.cpp:344 engines/scumm/help.cpp:175
 msgid "Travel"
 msgstr "Viaggio"
 
-#: engines/access/metaengine.cpp:334 engines/cine/metaengine.cpp:332
+#: engines/access/metaengine.cpp:355 engines/cine/metaengine.cpp:332
 #: engines/petka/metaengine.cpp:144 engines/saga/metaengine.cpp:394
 #: engines/supernova/metaengine.cpp:331
 msgid "Take"
 msgstr "Prendi"
 
 #. I18N: Action in In Cold Blood
-#: engines/access/metaengine.cpp:339 engines/alcachofa/metaengine.cpp:116
+#: engines/access/metaengine.cpp:360 engines/alcachofa/metaengine.cpp:116
 #: engines/cine/metaengine.cpp:337 engines/crab/input/input.cpp:251
 #: engines/cruise/metaengine.cpp:192 engines/draci/metaengine.cpp:237
 #: engines/dragons/metaengine.cpp:147 engines/drascula/metaengine.cpp:218
@@ -6545,18 +6553,18 @@ msgstr "Prendi"
 msgid "Inventory"
 msgstr "Inventario"
 
-#: engines/access/metaengine.cpp:344 engines/ultima/ultima4/metaengine.cpp:51
+#: engines/access/metaengine.cpp:365 engines/ultima/ultima4/metaengine.cpp:51
 msgid "Climb"
 msgstr "Scala"
 
-#: engines/access/metaengine.cpp:354 engines/bbvs/metaengine.cpp:142
+#: engines/access/metaengine.cpp:375 engines/bbvs/metaengine.cpp:142
 #: engines/freescape/games/castle/castle.cpp:511
 #: engines/petka/metaengine.cpp:137 engines/scumm/help.cpp:225
 #: engines/tsage/metaengine.cpp:201
 msgid "Walk"
 msgstr "Cammina"
 
-#: engines/access/metaengine.cpp:365
+#: engines/access/metaengine.cpp:386
 msgid "Open save/load menu"
 msgstr "Apri men?? di salvataggio/caricamento"
 
diff --git a/po/ja.po b/po/ja.po
index 4cef53d393f..0604334ff3d 100644
--- a/po/ja.po
+++ b/po/ja.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-17 23:07+0000\n"
+"POT-Creation-Date: 2026-06-18 09:25+0000\n"
 "PO-Revision-Date: 2026-06-04 07:22+0000\n"
 "Last-Translator: VAN-Gluon <van.gluonp at gmail.com>\n"
 "Language-Team: Japanese <https://translations.scummvm.org/projects/scummvm/"
@@ -83,7 +83,7 @@ msgstr "?????????"
 #. I18N: Opens the help screen.
 #: gui/browser.cpp:64 gui/launcher.cpp:277 gui/launcher.cpp:959
 #: backends/platform/sdl/macosx/appmenu_osx.mm:319
-#: engines/access/metaengine.cpp:360 engines/bladerunner/metaengine.cpp:321
+#: engines/access/metaengine.cpp:381 engines/bladerunner/metaengine.cpp:321
 #: engines/ngi/metaengine.cpp:162 engines/supernova/metaengine.cpp:238
 #: engines/tucker/metaengine.cpp:215 engines/zvision/metaengine.cpp:246
 msgid "Help"
@@ -2607,7 +2607,7 @@ msgstr ""
 "???????????????..."
 
 #: gui/options.cpp:2975 engines/engine.cpp:647
-#: engines/access/metaengine.cpp:298 engines/drascula/metaengine.cpp:255
+#: engines/access/metaengine.cpp:319 engines/drascula/metaengine.cpp:255
 #: engines/queen/metaengine.cpp:269 engines/sherlock/metaengine.cpp:410
 #: engines/ultima/nuvie/metaengine.cpp:71
 msgid "Move"
@@ -3887,7 +3887,7 @@ msgstr "?????????????????????"
 
 #. I18N: Skipping cutscene plaback
 #. I18N: Skipping cutscene playback
-#: engines/metaengine.cpp:112 engines/access/metaengine.cpp:225
+#: engines/metaengine.cpp:112 engines/access/metaengine.cpp:246
 #: engines/composer/metaengine.cpp:126 engines/drascula/metaengine.cpp:224
 #: engines/efh/metaengine.cpp:379 engines/freescape/movement.cpp:93
 #: engines/grim/grim.cpp:533 engines/grim/grim.cpp:633
@@ -6211,7 +6211,15 @@ msgstr "???????????????"
 msgid "Check for Updates..."
 msgstr "??????????????????????????????..."
 
-#: engines/access/metaengine.cpp:215 engines/alcachofa/metaengine.cpp:94
+#: engines/access/metaengine.cpp:82
+msgid "Use 'high definition' OGG music"
+msgstr ""
+
+#: engines/access/metaengine.cpp:83
+msgid "Use the OGG audio from the re-release instead of original MIDI tracks"
+msgstr ""
+
+#: engines/access/metaengine.cpp:236 engines/alcachofa/metaengine.cpp:94
 #: engines/bbvs/metaengine.cpp:92 engines/buried/metaengine.cpp:133
 #: engines/chamber/metaengine.cpp:59 engines/cine/metaengine.cpp:270
 #: engines/cruise/metaengine.cpp:168 engines/dm/metaengine.cpp:124
@@ -6235,20 +6243,20 @@ msgstr "??????????????????????????????..."
 msgid "Default keymappings"
 msgstr "?????????????????????????????????"
 
-#: engines/access/metaengine.cpp:219
+#: engines/access/metaengine.cpp:240
 #, fuzzy
 #| msgid "Highlight next dialogue"
 msgid "Move / Interact / Skip"
 msgstr "???????????????????????????????????????"
 
-#: engines/access/metaengine.cpp:231 engines/sherlock/metaengine.cpp:483
+#: engines/access/metaengine.cpp:252 engines/sherlock/metaengine.cpp:483
 #, fuzzy
 #| msgid "Skip line"
 msgid "Skip movie"
 msgstr "????????????????????????"
 
 #. I18N: Move Actor to upwards direction
-#: engines/access/metaengine.cpp:237 engines/asylum/metaengine.cpp:171
+#: engines/access/metaengine.cpp:258 engines/asylum/metaengine.cpp:171
 #: engines/asylum/metaengine.cpp:198 engines/cine/metaengine.cpp:377
 #: engines/efh/metaengine.cpp:286 engines/hdb/metaengine.cpp:213
 #: engines/hypno/metaengine.cpp:363 engines/hypno/metaengine.cpp:411
@@ -6258,7 +6266,7 @@ msgid "Move up"
 msgstr "???"
 
 #. I18N: Move Actor to downwards direction
-#: engines/access/metaengine.cpp:244 engines/asylum/metaengine.cpp:177
+#: engines/access/metaengine.cpp:265 engines/asylum/metaengine.cpp:177
 #: engines/asylum/metaengine.cpp:205 engines/cine/metaengine.cpp:384
 #: engines/efh/metaengine.cpp:293 engines/hdb/metaengine.cpp:219
 #: engines/hypno/metaengine.cpp:351 engines/hypno/metaengine.cpp:399
@@ -6269,7 +6277,7 @@ msgstr "???"
 
 #. I18N: Move Actor to left direction
 #. I18N: Move actor left during fight
-#: engines/access/metaengine.cpp:251 engines/asylum/metaengine.cpp:183
+#: engines/access/metaengine.cpp:272 engines/asylum/metaengine.cpp:183
 #: engines/asylum/metaengine.cpp:212 engines/cine/metaengine.cpp:391
 #: engines/efh/metaengine.cpp:300 engines/hdb/metaengine.cpp:225
 #: engines/hypno/metaengine.cpp:345 engines/hypno/metaengine.cpp:393
@@ -6282,7 +6290,7 @@ msgstr "????????????"
 
 #. I18N: Move Actor to right direction
 #. I18N: Move actor right during fight
-#: engines/access/metaengine.cpp:258 engines/asylum/metaengine.cpp:189
+#: engines/access/metaengine.cpp:279 engines/asylum/metaengine.cpp:189
 #: engines/asylum/metaengine.cpp:219 engines/cine/metaengine.cpp:398
 #: engines/efh/metaengine.cpp:307 engines/hdb/metaengine.cpp:231
 #: engines/hypno/metaengine.cpp:357 engines/hypno/metaengine.cpp:405
@@ -6294,31 +6302,31 @@ msgid "Move right"
 msgstr "????????????"
 
 #. I18N: Move Actor to top-left direction
-#: engines/access/metaengine.cpp:265 engines/cine/metaengine.cpp:405
+#: engines/access/metaengine.cpp:286 engines/cine/metaengine.cpp:405
 #: engines/efh/metaengine.cpp:314
 msgid "Move up-left"
 msgstr "???????????????"
 
 #. I18N: Move Actor to top-right direction
-#: engines/access/metaengine.cpp:270 engines/cine/metaengine.cpp:411
+#: engines/access/metaengine.cpp:291 engines/cine/metaengine.cpp:411
 #: engines/efh/metaengine.cpp:320
 msgid "Move up-right"
 msgstr "???????????????"
 
 #. I18N: Move Actor to bottom-left direction
-#: engines/access/metaengine.cpp:275 engines/cine/metaengine.cpp:417
+#: engines/access/metaengine.cpp:296 engines/cine/metaengine.cpp:417
 #: engines/efh/metaengine.cpp:326
 msgid "Move down-left"
 msgstr "???????????????"
 
 #. I18N: Move Actor to bottom-right direction
-#: engines/access/metaengine.cpp:280 engines/cine/metaengine.cpp:423
+#: engines/access/metaengine.cpp:301 engines/cine/metaengine.cpp:423
 #: engines/efh/metaengine.cpp:332
 msgid "Move down-right"
 msgstr "???????????????"
 
 #. I18N: (Game name: The Lost Files of Sherlock Holmes: The Case of the Rose Tattoo) This action is used to look at an object or npc in the game
-#: engines/access/metaengine.cpp:285 engines/bbvs/metaengine.cpp:124
+#: engines/access/metaengine.cpp:306 engines/bbvs/metaengine.cpp:124
 #: engines/dgds/metaengine.cpp:83 engines/drascula/metaengine.cpp:230
 #: engines/petka/metaengine.cpp:130 engines/scumm/help.cpp:167
 #: engines/scumm/help.cpp:231 engines/sherlock/metaengine.cpp:405
@@ -6329,7 +6337,7 @@ msgstr "???????????????"
 msgid "Look"
 msgstr "??????"
 
-#: engines/access/metaengine.cpp:293 engines/drascula/metaengine.cpp:240
+#: engines/access/metaengine.cpp:314 engines/drascula/metaengine.cpp:240
 #: engines/queen/metaengine.cpp:259 engines/saga/metaengine.cpp:369
 #: engines/scumm/help.cpp:125 engines/scumm/help.cpp:140
 #: engines/scumm/help.cpp:165 engines/scumm/help.cpp:193
@@ -6339,13 +6347,13 @@ msgstr "??????"
 msgid "Open"
 msgstr "??????"
 
-#: engines/access/metaengine.cpp:303 engines/scumm/help.cpp:128
+#: engines/access/metaengine.cpp:324 engines/scumm/help.cpp:128
 #: engines/ultima/nuvie/metaengine.cpp:70
 msgid "Get"
 msgstr "??????"
 
 #. I18N: Combat command in Might & Magic 1
-#: engines/access/metaengine.cpp:308 engines/access/metaengine.cpp:329
+#: engines/access/metaengine.cpp:329 engines/access/metaengine.cpp:350
 #: engines/bbvs/metaengine.cpp:136 engines/cine/metaengine.cpp:342
 #: engines/efh/metaengine.cpp:462 engines/hdb/metaengine.cpp:237
 #: engines/lab/metaengine.cpp:323 engines/mm/mm1/metaengine.cpp:86
@@ -6365,11 +6373,11 @@ msgstr "??????"
 msgid "Use"
 msgstr "??????"
 
-#: engines/access/metaengine.cpp:313
+#: engines/access/metaengine.cpp:334
 msgid "Goto"
 msgstr "??????"
 
-#: engines/access/metaengine.cpp:318 engines/access/metaengine.cpp:349
+#: engines/access/metaengine.cpp:339 engines/access/metaengine.cpp:370
 #: engines/bbvs/metaengine.cpp:130 engines/drascula/metaengine.cpp:250
 #: engines/efh/metaengine.cpp:506 engines/petka/metaengine.cpp:158
 #: engines/scumm/help.cpp:174 engines/scumm/help.cpp:226
@@ -6380,18 +6388,18 @@ msgstr "??????"
 msgid "Talk"
 msgstr "??????"
 
-#: engines/access/metaengine.cpp:323 engines/scumm/help.cpp:175
+#: engines/access/metaengine.cpp:344 engines/scumm/help.cpp:175
 msgid "Travel"
 msgstr "?????????"
 
-#: engines/access/metaengine.cpp:334 engines/cine/metaengine.cpp:332
+#: engines/access/metaengine.cpp:355 engines/cine/metaengine.cpp:332
 #: engines/petka/metaengine.cpp:144 engines/saga/metaengine.cpp:394
 #: engines/supernova/metaengine.cpp:331
 msgid "Take"
 msgstr "??????"
 
 #. I18N: Action in In Cold Blood
-#: engines/access/metaengine.cpp:339 engines/alcachofa/metaengine.cpp:116
+#: engines/access/metaengine.cpp:360 engines/alcachofa/metaengine.cpp:116
 #: engines/cine/metaengine.cpp:337 engines/crab/input/input.cpp:251
 #: engines/cruise/metaengine.cpp:192 engines/draci/metaengine.cpp:237
 #: engines/dragons/metaengine.cpp:147 engines/drascula/metaengine.cpp:218
@@ -6411,18 +6419,18 @@ msgstr "??????"
 msgid "Inventory"
 msgstr "?????????????????????"
 
-#: engines/access/metaengine.cpp:344 engines/ultima/ultima4/metaengine.cpp:51
+#: engines/access/metaengine.cpp:365 engines/ultima/ultima4/metaengine.cpp:51
 msgid "Climb"
 msgstr "??????"
 
-#: engines/access/metaengine.cpp:354 engines/bbvs/metaengine.cpp:142
+#: engines/access/metaengine.cpp:375 engines/bbvs/metaengine.cpp:142
 #: engines/freescape/games/castle/castle.cpp:511
 #: engines/petka/metaengine.cpp:137 engines/scumm/help.cpp:225
 #: engines/tsage/metaengine.cpp:201
 msgid "Walk"
 msgstr "??????"
 
-#: engines/access/metaengine.cpp:365
+#: engines/access/metaengine.cpp:386
 #, fuzzy
 #| msgid "Go to save/load menu"
 msgid "Open save/load menu"
diff --git a/po/ka.po b/po/ka.po
index f01844e91bf..36361d352a8 100644
--- a/po/ka.po
+++ b/po/ka.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-17 23:07+0000\n"
+"POT-Creation-Date: 2026-06-18 09:25+0000\n"
 "PO-Revision-Date: 2026-06-05 10:22+0000\n"
 "Last-Translator: NorwayFun <temuri.doghonadze at gmail.com>\n"
 "Language-Team: Georgian <https://translations.scummvm.org/projects/scummvm/"
@@ -83,7 +83,7 @@ msgstr "?????????????????????"
 #. I18N: Opens the help screen.
 #: gui/browser.cpp:64 gui/launcher.cpp:277 gui/launcher.cpp:959
 #: backends/platform/sdl/macosx/appmenu_osx.mm:319
-#: engines/access/metaengine.cpp:360 engines/bladerunner/metaengine.cpp:321
+#: engines/access/metaengine.cpp:381 engines/bladerunner/metaengine.cpp:321
 #: engines/ngi/metaengine.cpp:162 engines/supernova/metaengine.cpp:238
 #: engines/tucker/metaengine.cpp:215 engines/zvision/metaengine.cpp:246
 msgid "Help"
@@ -2512,7 +2512,7 @@ msgstr ""
 "?????? ???????????????..."
 
 #: gui/options.cpp:2975 engines/engine.cpp:647
-#: engines/access/metaengine.cpp:298 engines/drascula/metaengine.cpp:255
+#: engines/access/metaengine.cpp:319 engines/drascula/metaengine.cpp:255
 #: engines/queen/metaengine.cpp:269 engines/sherlock/metaengine.cpp:410
 #: engines/ultima/nuvie/metaengine.cpp:71
 msgid "Move"
@@ -3706,7 +3706,7 @@ msgstr "????????????????????? ???????????????"
 
 #. I18N: Skipping cutscene plaback
 #. I18N: Skipping cutscene playback
-#: engines/metaengine.cpp:112 engines/access/metaengine.cpp:225
+#: engines/metaengine.cpp:112 engines/access/metaengine.cpp:246
 #: engines/composer/metaengine.cpp:126 engines/drascula/metaengine.cpp:224
 #: engines/efh/metaengine.cpp:379 engines/freescape/movement.cpp:93
 #: engines/grim/grim.cpp:533 engines/grim/grim.cpp:633
@@ -5666,7 +5666,15 @@ msgstr "????????????????????????"
 msgid "Check for Updates..."
 msgstr "???????????????????????????????????? ???????????????????????????..."
 
-#: engines/access/metaengine.cpp:215 engines/alcachofa/metaengine.cpp:94
+#: engines/access/metaengine.cpp:82
+msgid "Use 'high definition' OGG music"
+msgstr ""
+
+#: engines/access/metaengine.cpp:83
+msgid "Use the OGG audio from the re-release instead of original MIDI tracks"
+msgstr ""
+
+#: engines/access/metaengine.cpp:236 engines/alcachofa/metaengine.cpp:94
 #: engines/bbvs/metaengine.cpp:92 engines/buried/metaengine.cpp:133
 #: engines/chamber/metaengine.cpp:59 engines/cine/metaengine.cpp:270
 #: engines/cruise/metaengine.cpp:168 engines/dm/metaengine.cpp:124
@@ -5690,16 +5698,16 @@ msgstr "???????????????????????????????????? ???????????????????????????..."
 msgid "Default keymappings"
 msgstr "???????????????????????????????????? ????????????????????????????????? ????????????????????????????????????"
 
-#: engines/access/metaengine.cpp:219
+#: engines/access/metaengine.cpp:240
 msgid "Move / Interact / Skip"
 msgstr "???????????????????????????????????? / ?????????????????????????????????????????? / ??????????????????????????????"
 
-#: engines/access/metaengine.cpp:231 engines/sherlock/metaengine.cpp:483
+#: engines/access/metaengine.cpp:252 engines/sherlock/metaengine.cpp:483
 msgid "Skip movie"
 msgstr "?????????????????? ??????????????????????????????"
 
 #. I18N: Move Actor to upwards direction
-#: engines/access/metaengine.cpp:237 engines/asylum/metaengine.cpp:171
+#: engines/access/metaengine.cpp:258 engines/asylum/metaengine.cpp:171
 #: engines/asylum/metaengine.cpp:198 engines/cine/metaengine.cpp:377
 #: engines/efh/metaengine.cpp:286 engines/hdb/metaengine.cpp:213
 #: engines/hypno/metaengine.cpp:363 engines/hypno/metaengine.cpp:411
@@ -5709,7 +5717,7 @@ msgid "Move up"
 msgstr "???????????????"
 
 #. I18N: Move Actor to downwards direction
-#: engines/access/metaengine.cpp:244 engines/asylum/metaengine.cpp:177
+#: engines/access/metaengine.cpp:265 engines/asylum/metaengine.cpp:177
 #: engines/asylum/metaengine.cpp:205 engines/cine/metaengine.cpp:384
 #: engines/efh/metaengine.cpp:293 engines/hdb/metaengine.cpp:219
 #: engines/hypno/metaengine.cpp:351 engines/hypno/metaengine.cpp:399
@@ -5720,7 +5728,7 @@ msgstr "?????????????????? ????????????????????????"
 
 #. I18N: Move Actor to left direction
 #. I18N: Move actor left during fight
-#: engines/access/metaengine.cpp:251 engines/asylum/metaengine.cpp:183
+#: engines/access/metaengine.cpp:272 engines/asylum/metaengine.cpp:183
 #: engines/asylum/metaengine.cpp:212 engines/cine/metaengine.cpp:391
 #: engines/efh/metaengine.cpp:300 engines/hdb/metaengine.cpp:225
 #: engines/hypno/metaengine.cpp:345 engines/hypno/metaengine.cpp:393
@@ -5733,7 +5741,7 @@ msgstr "???????????????????????? ??????????????????"
 
 #. I18N: Move Actor to right direction
 #. I18N: Move actor right during fight
-#: engines/access/metaengine.cpp:258 engines/asylum/metaengine.cpp:189
+#: engines/access/metaengine.cpp:279 engines/asylum/metaengine.cpp:189
 #: engines/asylum/metaengine.cpp:219 engines/cine/metaengine.cpp:398
 #: engines/efh/metaengine.cpp:307 engines/hdb/metaengine.cpp:231
 #: engines/hypno/metaengine.cpp:357 engines/hypno/metaengine.cpp:405
@@ -5745,31 +5753,31 @@ msgid "Move right"
 msgstr "???????????????????????? ??????????????????"
 
 #. I18N: Move Actor to top-left direction
-#: engines/access/metaengine.cpp:265 engines/cine/metaengine.cpp:405
+#: engines/access/metaengine.cpp:286 engines/cine/metaengine.cpp:405
 #: engines/efh/metaengine.cpp:314
 msgid "Move up-left"
 msgstr "????????????????????????-??????????????? ??????????????????"
 
 #. I18N: Move Actor to top-right direction
-#: engines/access/metaengine.cpp:270 engines/cine/metaengine.cpp:411
+#: engines/access/metaengine.cpp:291 engines/cine/metaengine.cpp:411
 #: engines/efh/metaengine.cpp:320
 msgid "Move up-right"
 msgstr "????????????????????????-??????????????? ??????????????????"
 
 #. I18N: Move Actor to bottom-left direction
-#: engines/access/metaengine.cpp:275 engines/cine/metaengine.cpp:417
+#: engines/access/metaengine.cpp:296 engines/cine/metaengine.cpp:417
 #: engines/efh/metaengine.cpp:326
 msgid "Move down-left"
 msgstr "??????????????????-???????????????????????? ????????????????????????"
 
 #. I18N: Move Actor to bottom-right direction
-#: engines/access/metaengine.cpp:280 engines/cine/metaengine.cpp:423
+#: engines/access/metaengine.cpp:301 engines/cine/metaengine.cpp:423
 #: engines/efh/metaengine.cpp:332
 msgid "Move down-right"
 msgstr "????????????????????????-??????????????? ??????????????????"
 
 #. I18N: (Game name: The Lost Files of Sherlock Holmes: The Case of the Rose Tattoo) This action is used to look at an object or npc in the game
-#: engines/access/metaengine.cpp:285 engines/bbvs/metaengine.cpp:124
+#: engines/access/metaengine.cpp:306 engines/bbvs/metaengine.cpp:124
 #: engines/dgds/metaengine.cpp:83 engines/drascula/metaengine.cpp:230
 #: engines/petka/metaengine.cpp:130 engines/scumm/help.cpp:167
 #: engines/scumm/help.cpp:231 engines/sherlock/metaengine.cpp:405
@@ -5780,7 +5788,7 @@ msgstr "????????????????????????-??????????????? ??????????????????"
 msgid "Look"
 msgstr "???????????????????????????"
 
-#: engines/access/metaengine.cpp:293 engines/drascula/metaengine.cpp:240
+#: engines/access/metaengine.cpp:314 engines/drascula/metaengine.cpp:240
 #: engines/queen/metaengine.cpp:259 engines/saga/metaengine.cpp:369
 #: engines/scumm/help.cpp:125 engines/scumm/help.cpp:140
 #: engines/scumm/help.cpp:165 engines/scumm/help.cpp:193
@@ -5790,13 +5798,13 @@ msgstr "???????????????????????????"
 msgid "Open"
 msgstr "??????????????????"
 
-#: engines/access/metaengine.cpp:303 engines/scumm/help.cpp:128
+#: engines/access/metaengine.cpp:324 engines/scumm/help.cpp:128
 #: engines/ultima/nuvie/metaengine.cpp:70
 msgid "Get"
 msgstr "??????????????????"
 
 #. I18N: Combat command in Might & Magic 1
-#: engines/access/metaengine.cpp:308 engines/access/metaengine.cpp:329
+#: engines/access/metaengine.cpp:329 engines/access/metaengine.cpp:350
 #: engines/bbvs/metaengine.cpp:136 engines/cine/metaengine.cpp:342
 #: engines/efh/metaengine.cpp:462 engines/hdb/metaengine.cpp:237
 #: engines/lab/metaengine.cpp:323 engines/mm/mm1/metaengine.cpp:86
@@ -5816,11 +5824,11 @@ msgstr "??????????????????"
 msgid "Use"
 msgstr "??????????????????????????????"
 
-#: engines/access/metaengine.cpp:313
+#: engines/access/metaengine.cpp:334
 msgid "Goto"
 msgstr "????????????????????????"
 
-#: engines/access/metaengine.cpp:318 engines/access/metaengine.cpp:349
+#: engines/access/metaengine.cpp:339 engines/access/metaengine.cpp:370
 #: engines/bbvs/metaengine.cpp:130 engines/drascula/metaengine.cpp:250
 #: engines/efh/metaengine.cpp:506 engines/petka/metaengine.cpp:158
 #: engines/scumm/help.cpp:174 engines/scumm/help.cpp:226
@@ -5831,18 +5839,18 @@ msgstr "????????????????????????"
 msgid "Talk"
 msgstr "?????????????????????"
 
-#: engines/access/metaengine.cpp:323 engines/scumm/help.cpp:175
+#: engines/access/metaengine.cpp:344 engines/scumm/help.cpp:175
 msgid "Travel"
 msgstr "??????????????????????????????"
 
-#: engines/access/metaengine.cpp:334 engines/cine/metaengine.cpp:332
+#: engines/access/metaengine.cpp:355 engines/cine/metaengine.cpp:332
 #: engines/petka/metaengine.cpp:144 engines/saga/metaengine.cpp:394
 #: engines/supernova/metaengine.cpp:331
 msgid "Take"
 msgstr "????????????"
 
 #. I18N: Action in In Cold Blood
-#: engines/access/metaengine.cpp:339 engines/alcachofa/metaengine.cpp:116
+#: engines/access/metaengine.cpp:360 engines/alcachofa/metaengine.cpp:116
 #: engines/cine/metaengine.cpp:337 engines/crab/input/input.cpp:251
 #: engines/cruise/metaengine.cpp:192 engines/draci/metaengine.cpp:237
 #: engines/dragons/metaengine.cpp:147 engines/drascula/metaengine.cpp:218
@@ -5862,18 +5870,18 @@ msgstr "????????????"
 msgid "Inventory"
 msgstr "???????????????????????????"
 
-#: engines/access/metaengine.cpp:344 engines/ultima/ultima4/metaengine.cpp:51
+#: engines/access/metaengine.cpp:365 engines/ultima/ultima4/metaengine.cpp:51
 msgid "Climb"
 msgstr "?????????????????????"
 
-#: engines/access/metaengine.cpp:354 engines/bbvs/metaengine.cpp:142
+#: engines/access/metaengine.cpp:375 engines/bbvs/metaengine.cpp:142
 #: engines/freescape/games/castle/castle.cpp:511
 #: engines/petka/metaengine.cpp:137 engines/scumm/help.cpp:225
 #: engines/tsage/metaengine.cpp:201
 msgid "Walk"
 msgstr "??????????????????????????????"
 
-#: engines/access/metaengine.cpp:365
+#: engines/access/metaengine.cpp:386
 msgid "Open save/load menu"
 msgstr "????????????????????????/?????????????????????????????? ?????????????????? ??????????????????"
 
diff --git a/po/ko.po b/po/ko.po
index c124993489a..28572d9b454 100644
--- a/po/ko.po
+++ b/po/ko.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-17 23:07+0000\n"
+"POT-Creation-Date: 2026-06-18 09:25+0000\n"
 "PO-Revision-Date: 2026-05-19 20:22+0000\n"
 "Last-Translator: Hoseok Seo <ddinghoya at gmail.com>\n"
 "Language-Team: Korean <https://translations.scummvm.org/projects/scummvm/"
@@ -83,7 +83,7 @@ msgstr "??????"
 #. I18N: Opens the help screen.
 #: gui/browser.cpp:64 gui/launcher.cpp:277 gui/launcher.cpp:959
 #: backends/platform/sdl/macosx/appmenu_osx.mm:319
-#: engines/access/metaengine.cpp:360 engines/bladerunner/metaengine.cpp:321
+#: engines/access/metaengine.cpp:381 engines/bladerunner/metaengine.cpp:321
 #: engines/ngi/metaengine.cpp:162 engines/supernova/metaengine.cpp:238
 #: engines/tucker/metaengine.cpp:215 engines/zvision/metaengine.cpp:246
 msgid "Help"
@@ -2655,7 +2655,7 @@ msgstr ""
 "????????? ??? ?????? ??????..."
 
 #: gui/options.cpp:2975 engines/engine.cpp:647
-#: engines/access/metaengine.cpp:298 engines/drascula/metaengine.cpp:255
+#: engines/access/metaengine.cpp:319 engines/drascula/metaengine.cpp:255
 #: engines/queen/metaengine.cpp:269 engines/sherlock/metaengine.cpp:410
 #: engines/ultima/nuvie/metaengine.cpp:71
 msgid "Move"
@@ -3941,7 +3941,7 @@ msgstr "?????? ??????"
 
 #. I18N: Skipping cutscene plaback
 #. I18N: Skipping cutscene playback
-#: engines/metaengine.cpp:112 engines/access/metaengine.cpp:225
+#: engines/metaengine.cpp:112 engines/access/metaengine.cpp:246
 #: engines/composer/metaengine.cpp:126 engines/drascula/metaengine.cpp:224
 #: engines/efh/metaengine.cpp:379 engines/freescape/movement.cpp:93
 #: engines/grim/grim.cpp:533 engines/grim/grim.cpp:633
@@ -6222,7 +6222,15 @@ msgstr "??????"
 msgid "Check for Updates..."
 msgstr "???????????? ?????? ???..."
 
-#: engines/access/metaengine.cpp:215 engines/alcachofa/metaengine.cpp:94
+#: engines/access/metaengine.cpp:82
+msgid "Use 'high definition' OGG music"
+msgstr ""
+
+#: engines/access/metaengine.cpp:83
+msgid "Use the OGG audio from the re-release instead of original MIDI tracks"
+msgstr ""
+
+#: engines/access/metaengine.cpp:236 engines/alcachofa/metaengine.cpp:94
 #: engines/bbvs/metaengine.cpp:92 engines/buried/metaengine.cpp:133
 #: engines/chamber/metaengine.cpp:59 engines/cine/metaengine.cpp:270
 #: engines/cruise/metaengine.cpp:168 engines/dm/metaengine.cpp:124
@@ -6246,16 +6254,16 @@ msgstr "???????????? ?????? ???..."
 msgid "Default keymappings"
 msgstr "?????? ?????????"
 
-#: engines/access/metaengine.cpp:219
+#: engines/access/metaengine.cpp:240
 msgid "Move / Interact / Skip"
 msgstr "?????? / ?????? / ????????????"
 
-#: engines/access/metaengine.cpp:231 engines/sherlock/metaengine.cpp:483
+#: engines/access/metaengine.cpp:252 engines/sherlock/metaengine.cpp:483
 msgid "Skip movie"
 msgstr "????????? ????????????"
 
 #. I18N: Move Actor to upwards direction
-#: engines/access/metaengine.cpp:237 engines/asylum/metaengine.cpp:171
+#: engines/access/metaengine.cpp:258 engines/asylum/metaengine.cpp:171
 #: engines/asylum/metaengine.cpp:198 engines/cine/metaengine.cpp:377
 #: engines/efh/metaengine.cpp:286 engines/hdb/metaengine.cpp:213
 #: engines/hypno/metaengine.cpp:363 engines/hypno/metaengine.cpp:411
@@ -6265,7 +6273,7 @@ msgid "Move up"
 msgstr "?????? ??????"
 
 #. I18N: Move Actor to downwards direction
-#: engines/access/metaengine.cpp:244 engines/asylum/metaengine.cpp:177
+#: engines/access/metaengine.cpp:265 engines/asylum/metaengine.cpp:177
 #: engines/asylum/metaengine.cpp:205 engines/cine/metaengine.cpp:384
 #: engines/efh/metaengine.cpp:293 engines/hdb/metaengine.cpp:219
 #: engines/hypno/metaengine.cpp:351 engines/hypno/metaengine.cpp:399
@@ -6276,7 +6284,7 @@ msgstr "????????? ??????"
 
 #. I18N: Move Actor to left direction
 #. I18N: Move actor left during fight
-#: engines/access/metaengine.cpp:251 engines/asylum/metaengine.cpp:183
+#: engines/access/metaengine.cpp:272 engines/asylum/metaengine.cpp:183
 #: engines/asylum/metaengine.cpp:212 engines/cine/metaengine.cpp:391
 #: engines/efh/metaengine.cpp:300 engines/hdb/metaengine.cpp:225
 #: engines/hypno/metaengine.cpp:345 engines/hypno/metaengine.cpp:393
@@ -6289,7 +6297,7 @@ msgstr "???????????? ??????"
 
 #. I18N: Move Actor to right direction
 #. I18N: Move actor right during fight
-#: engines/access/metaengine.cpp:258 engines/asylum/metaengine.cpp:189
+#: engines/access/metaengine.cpp:279 engines/asylum/metaengine.cpp:189
 #: engines/asylum/metaengine.cpp:219 engines/cine/metaengine.cpp:398
 #: engines/efh/metaengine.cpp:307 engines/hdb/metaengine.cpp:231
 #: engines/hypno/metaengine.cpp:357 engines/hypno/metaengine.cpp:405
@@ -6301,31 +6309,31 @@ msgid "Move right"
 msgstr "??????????????? ??????"
 
 #. I18N: Move Actor to top-left direction
-#: engines/access/metaengine.cpp:265 engines/cine/metaengine.cpp:405
+#: engines/access/metaengine.cpp:286 engines/cine/metaengine.cpp:405
 #: engines/efh/metaengine.cpp:314
 msgid "Move up-left"
 msgstr "?????? ?????? ??????"
 
 #. I18N: Move Actor to top-right direction
-#: engines/access/metaengine.cpp:270 engines/cine/metaengine.cpp:411
+#: engines/access/metaengine.cpp:291 engines/cine/metaengine.cpp:411
 #: engines/efh/metaengine.cpp:320
 msgid "Move up-right"
 msgstr "????????? ?????? ??????"
 
 #. I18N: Move Actor to bottom-left direction
-#: engines/access/metaengine.cpp:275 engines/cine/metaengine.cpp:417
+#: engines/access/metaengine.cpp:296 engines/cine/metaengine.cpp:417
 #: engines/efh/metaengine.cpp:326
 msgid "Move down-left"
 msgstr "?????? ????????? ??????"
 
 #. I18N: Move Actor to bottom-right direction
-#: engines/access/metaengine.cpp:280 engines/cine/metaengine.cpp:423
+#: engines/access/metaengine.cpp:301 engines/cine/metaengine.cpp:423
 #: engines/efh/metaengine.cpp:332
 msgid "Move down-right"
 msgstr "????????? ????????? ??????"
 
 #. I18N: (Game name: The Lost Files of Sherlock Holmes: The Case of the Rose Tattoo) This action is used to look at an object or npc in the game
-#: engines/access/metaengine.cpp:285 engines/bbvs/metaengine.cpp:124
+#: engines/access/metaengine.cpp:306 engines/bbvs/metaengine.cpp:124
 #: engines/dgds/metaengine.cpp:83 engines/drascula/metaengine.cpp:230
 #: engines/petka/metaengine.cpp:130 engines/scumm/help.cpp:167
 #: engines/scumm/help.cpp:231 engines/sherlock/metaengine.cpp:405
@@ -6336,7 +6344,7 @@ msgstr "????????? ????????? ??????"
 msgid "Look"
 msgstr "??????"
 
-#: engines/access/metaengine.cpp:293 engines/drascula/metaengine.cpp:240
+#: engines/access/metaengine.cpp:314 engines/drascula/metaengine.cpp:240
 #: engines/queen/metaengine.cpp:259 engines/saga/metaengine.cpp:369
 #: engines/scumm/help.cpp:125 engines/scumm/help.cpp:140
 #: engines/scumm/help.cpp:165 engines/scumm/help.cpp:193
@@ -6346,13 +6354,13 @@ msgstr "??????"
 msgid "Open"
 msgstr "??????"
 
-#: engines/access/metaengine.cpp:303 engines/scumm/help.cpp:128
+#: engines/access/metaengine.cpp:324 engines/scumm/help.cpp:128
 #: engines/ultima/nuvie/metaengine.cpp:70
 msgid "Get"
 msgstr "??????"
 
 #. I18N: Combat command in Might & Magic 1
-#: engines/access/metaengine.cpp:308 engines/access/metaengine.cpp:329
+#: engines/access/metaengine.cpp:329 engines/access/metaengine.cpp:350
 #: engines/bbvs/metaengine.cpp:136 engines/cine/metaengine.cpp:342
 #: engines/efh/metaengine.cpp:462 engines/hdb/metaengine.cpp:237
 #: engines/lab/metaengine.cpp:323 engines/mm/mm1/metaengine.cpp:86
@@ -6372,11 +6380,11 @@ msgstr "??????"
 msgid "Use"
 msgstr "????????????"
 
-#: engines/access/metaengine.cpp:313
+#: engines/access/metaengine.cpp:334
 msgid "Goto"
 msgstr "??????"
 
-#: engines/access/metaengine.cpp:318 engines/access/metaengine.cpp:349
+#: engines/access/metaengine.cpp:339 engines/access/metaengine.cpp:370
 #: engines/bbvs/metaengine.cpp:130 engines/drascula/metaengine.cpp:250
 #: engines/efh/metaengine.cpp:506 engines/petka/metaengine.cpp:158
 #: engines/scumm/help.cpp:174 engines/scumm/help.cpp:226
@@ -6387,18 +6395,18 @@ msgstr "??????"
 msgid "Talk"
 msgstr "????????????"
 
-#: engines/access/metaengine.cpp:323 engines/scumm/help.cpp:175
+#: engines/access/metaengine.cpp:344 engines/scumm/help.cpp:175
 msgid "Travel"
 msgstr "????????????"
 
-#: engines/access/metaengine.cpp:334 engines/cine/metaengine.cpp:332
+#: engines/access/metaengine.cpp:355 engines/cine/metaengine.cpp:332
 #: engines/petka/metaengine.cpp:144 engines/saga/metaengine.cpp:394
 #: engines/supernova/metaengine.cpp:331
 msgid "Take"
 msgstr "??????"
 
 #. I18N: Action in In Cold Blood
-#: engines/access/metaengine.cpp:339 engines/alcachofa/metaengine.cpp:116
+#: engines/access/metaengine.cpp:360 engines/alcachofa/metaengine.cpp:116
 #: engines/cine/metaengine.cpp:337 engines/crab/input/input.cpp:251
 #: engines/cruise/metaengine.cpp:192 engines/draci/metaengine.cpp:237
 #: engines/dragons/metaengine.cpp:147 engines/drascula/metaengine.cpp:218
@@ -6418,18 +6426,18 @@ msgstr "??????"
 msgid "Inventory"
 msgstr "????????????"
 
-#: engines/access/metaengine.cpp:344 engines/ultima/ultima4/metaengine.cpp:51
+#: engines/access/metaengine.cpp:365 engines/ultima/ultima4/metaengine.cpp:51
 msgid "Climb"
 msgstr "??????"
 
-#: engines/access/metaengine.cpp:354 engines/bbvs/metaengine.cpp:142
+#: engines/access/metaengine.cpp:375 engines/bbvs/metaengine.cpp:142
 #: engines/freescape/games/castle/castle.cpp:511
 #: engines/petka/metaengine.cpp:137 engines/scumm/help.cpp:225
 #: engines/tsage/metaengine.cpp:201
 msgid "Walk"
 msgstr "??????"
 
-#: engines/access/metaengine.cpp:365
+#: engines/access/metaengine.cpp:386
 msgid "Open save/load menu"
 msgstr "??????/?????? ?????? ??????"
 
diff --git a/po/nb_NO.po b/po/nb_NO.po
index cf199050f6e..7ec8b4d851a 100644
--- a/po/nb_NO.po
+++ b/po/nb_NO.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-17 23:07+0000\n"
+"POT-Creation-Date: 2026-06-18 09:25+0000\n"
 "PO-Revision-Date: 2026-02-07 15:22+0000\n"
 "Last-Translator: Einar Johan Tr??an S??m??en <einarjohants at gmail.com>\n"
 "Language-Team: Norwegian Bokm??l <https://translations.scummvm.org/projects/"
@@ -85,7 +85,7 @@ msgstr "Lukk"
 #. I18N: Opens the help screen.
 #: gui/browser.cpp:64 gui/launcher.cpp:277 gui/launcher.cpp:959
 #: backends/platform/sdl/macosx/appmenu_osx.mm:319
-#: engines/access/metaengine.cpp:360 engines/bladerunner/metaengine.cpp:321
+#: engines/access/metaengine.cpp:381 engines/bladerunner/metaengine.cpp:321
 #: engines/ngi/metaengine.cpp:162 engines/supernova/metaengine.cpp:238
 #: engines/tucker/metaengine.cpp:215 engines/zvision/metaengine.cpp:246
 msgid "Help"
@@ -2743,7 +2743,7 @@ msgstr ""
 "og flere ..."
 
 #: gui/options.cpp:2975 engines/engine.cpp:647
-#: engines/access/metaengine.cpp:298 engines/drascula/metaengine.cpp:255
+#: engines/access/metaengine.cpp:319 engines/drascula/metaengine.cpp:255
 #: engines/queen/metaengine.cpp:269 engines/sherlock/metaengine.cpp:410
 #: engines/ultima/nuvie/metaengine.cpp:71
 msgid "Move"
@@ -4075,7 +4075,7 @@ msgstr "Spillmeny"
 
 #. I18N: Skipping cutscene plaback
 #. I18N: Skipping cutscene playback
-#: engines/metaengine.cpp:112 engines/access/metaengine.cpp:225
+#: engines/metaengine.cpp:112 engines/access/metaengine.cpp:246
 #: engines/composer/metaengine.cpp:126 engines/drascula/metaengine.cpp:224
 #: engines/efh/metaengine.cpp:379 engines/freescape/movement.cpp:93
 #: engines/grim/grim.cpp:533 engines/grim/grim.cpp:633
@@ -6621,7 +6621,15 @@ msgstr "Oppstarter"
 msgid "Check for Updates..."
 msgstr "Sjekk for oppdateringer..."
 
-#: engines/access/metaengine.cpp:215 engines/alcachofa/metaengine.cpp:94
+#: engines/access/metaengine.cpp:82
+msgid "Use 'high definition' OGG music"
+msgstr ""
+
+#: engines/access/metaengine.cpp:83
+msgid "Use the OGG audio from the re-release instead of original MIDI tracks"
+msgstr ""
+
+#: engines/access/metaengine.cpp:236 engines/alcachofa/metaengine.cpp:94
 #: engines/bbvs/metaengine.cpp:92 engines/buried/metaengine.cpp:133
 #: engines/chamber/metaengine.cpp:59 engines/cine/metaengine.cpp:270
 #: engines/cruise/metaengine.cpp:168 engines/dm/metaengine.cpp:124
@@ -6645,20 +6653,20 @@ msgstr "Sjekk for oppdateringer..."
 msgid "Default keymappings"
 msgstr "Standard tasteoppsett"
 
-#: engines/access/metaengine.cpp:219
+#: engines/access/metaengine.cpp:240
 #, fuzzy
 #| msgid "Highlight next dialogue"
 msgid "Move / Interact / Skip"
 msgstr "Merk neste dialog"
 
-#: engines/access/metaengine.cpp:231 engines/sherlock/metaengine.cpp:483
+#: engines/access/metaengine.cpp:252 engines/sherlock/metaengine.cpp:483
 #, fuzzy
 #| msgid "Skip line"
 msgid "Skip movie"
 msgstr "Hopp over linje"
 
 #. I18N: Move Actor to upwards direction
-#: engines/access/metaengine.cpp:237 engines/asylum/metaengine.cpp:171
+#: engines/access/metaengine.cpp:258 engines/asylum/metaengine.cpp:171
 #: engines/asylum/metaengine.cpp:198 engines/cine/metaengine.cpp:377
 #: engines/efh/metaengine.cpp:286 engines/hdb/metaengine.cpp:213
 #: engines/hypno/metaengine.cpp:363 engines/hypno/metaengine.cpp:411
@@ -6668,7 +6676,7 @@ msgid "Move up"
 msgstr "G?? oppover"
 
 #. I18N: Move Actor to downwards direction
-#: engines/access/metaengine.cpp:244 engines/asylum/metaengine.cpp:177
+#: engines/access/metaengine.cpp:265 engines/asylum/metaengine.cpp:177
 #: engines/asylum/metaengine.cpp:205 engines/cine/metaengine.cpp:384
 #: engines/efh/metaengine.cpp:293 engines/hdb/metaengine.cpp:219
 #: engines/hypno/metaengine.cpp:351 engines/hypno/metaengine.cpp:399
@@ -6679,7 +6687,7 @@ msgstr "G?? nedover"
 
 #. I18N: Move Actor to left direction
 #. I18N: Move actor left during fight
-#: engines/access/metaengine.cpp:251 engines/asylum/metaengine.cpp:183
+#: engines/access/metaengine.cpp:272 engines/asylum/metaengine.cpp:183
 #: engines/asylum/metaengine.cpp:212 engines/cine/metaengine.cpp:391
 #: engines/efh/metaengine.cpp:300 engines/hdb/metaengine.cpp:225
 #: engines/hypno/metaengine.cpp:345 engines/hypno/metaengine.cpp:393
@@ -6692,7 +6700,7 @@ msgstr "G?? til venstre"
 
 #. I18N: Move Actor to right direction
 #. I18N: Move actor right during fight
-#: engines/access/metaengine.cpp:258 engines/asylum/metaengine.cpp:189
+#: engines/access/metaengine.cpp:279 engines/asylum/metaengine.cpp:189
 #: engines/asylum/metaengine.cpp:219 engines/cine/metaengine.cpp:398
 #: engines/efh/metaengine.cpp:307 engines/hdb/metaengine.cpp:231
 #: engines/hypno/metaengine.cpp:357 engines/hypno/metaengine.cpp:405
@@ -6704,31 +6712,31 @@ msgid "Move right"
 msgstr "G?? til h??yre"
 
 #. I18N: Move Actor to top-left direction
-#: engines/access/metaengine.cpp:265 engines/cine/metaengine.cpp:405
+#: engines/access/metaengine.cpp:286 engines/cine/metaengine.cpp:405
 #: engines/efh/metaengine.cpp:314
 msgid "Move up-left"
 msgstr "G?? opp-venstre"
 
 #. I18N: Move Actor to top-right direction
-#: engines/access/metaengine.cpp:270 engines/cine/metaengine.cpp:411
+#: engines/access/metaengine.cpp:291 engines/cine/metaengine.cpp:411
 #: engines/efh/metaengine.cpp:320
 msgid "Move up-right"
 msgstr "G?? opp-h??yre"
 
 #. I18N: Move Actor to bottom-left direction
-#: engines/access/metaengine.cpp:275 engines/cine/metaengine.cpp:417
+#: engines/access/metaengine.cpp:296 engines/cine/metaengine.cpp:417
 #: engines/efh/metaengine.cpp:326
 msgid "Move down-left"
 msgstr "G?? ned-venstre"
 
 #. I18N: Move Actor to bottom-right direction
-#: engines/access/metaengine.cpp:280 engines/cine/metaengine.cpp:423
+#: engines/access/metaengine.cpp:301 engines/cine/metaengine.cpp:423
 #: engines/efh/metaengine.cpp:332
 msgid "Move down-right"
 msgstr "G?? ned-h??yre"
 
 #. I18N: (Game name: The Lost Files of Sherlock Holmes: The Case of the Rose Tattoo) This action is used to look at an object or npc in the game
-#: engines/access/metaengine.cpp:285 engines/bbvs/metaengine.cpp:124
+#: engines/access/metaengine.cpp:306 engines/bbvs/metaengine.cpp:124
 #: engines/dgds/metaengine.cpp:83 engines/drascula/metaengine.cpp:230
 #: engines/petka/metaengine.cpp:130 engines/scumm/help.cpp:167
 #: engines/scumm/help.cpp:231 engines/sherlock/metaengine.cpp:405
@@ -6739,7 +6747,7 @@ msgstr "G?? ned-h??yre"
 msgid "Look"
 msgstr "Kikk"
 
-#: engines/access/metaengine.cpp:293 engines/drascula/metaengine.cpp:240
+#: engines/access/metaengine.cpp:314 engines/drascula/metaengine.cpp:240
 #: engines/queen/metaengine.cpp:259 engines/saga/metaengine.cpp:369
 #: engines/scumm/help.cpp:125 engines/scumm/help.cpp:140
 #: engines/scumm/help.cpp:165 engines/scumm/help.cpp:193
@@ -6749,13 +6757,13 @@ msgstr "Kikk"
 msgid "Open"
 msgstr "??pne"
 
-#: engines/access/metaengine.cpp:303 engines/scumm/help.cpp:128
+#: engines/access/metaengine.cpp:324 engines/scumm/help.cpp:128
 #: engines/ultima/nuvie/metaengine.cpp:70
 msgid "Get"
 msgstr "F??"
 
 #. I18N: Combat command in Might & Magic 1
-#: engines/access/metaengine.cpp:308 engines/access/metaengine.cpp:329
+#: engines/access/metaengine.cpp:329 engines/access/metaengine.cpp:350
 #: engines/bbvs/metaengine.cpp:136 engines/cine/metaengine.cpp:342
 #: engines/efh/metaengine.cpp:462 engines/hdb/metaengine.cpp:237
 #: engines/lab/metaengine.cpp:323 engines/mm/mm1/metaengine.cpp:86
@@ -6775,11 +6783,11 @@ msgstr "F??"
 msgid "Use"
 msgstr "Bruk"
 
-#: engines/access/metaengine.cpp:313
+#: engines/access/metaengine.cpp:334
 msgid "Goto"
 msgstr "G?? til"
 
-#: engines/access/metaengine.cpp:318 engines/access/metaengine.cpp:349
+#: engines/access/metaengine.cpp:339 engines/access/metaengine.cpp:370
 #: engines/bbvs/metaengine.cpp:130 engines/drascula/metaengine.cpp:250
 #: engines/efh/metaengine.cpp:506 engines/petka/metaengine.cpp:158
 #: engines/scumm/help.cpp:174 engines/scumm/help.cpp:226
@@ -6790,18 +6798,18 @@ msgstr "G?? til"
 msgid "Talk"
 msgstr "Snakk"
 
-#: engines/access/metaengine.cpp:323 engines/scumm/help.cpp:175
+#: engines/access/metaengine.cpp:344 engines/scumm/help.cpp:175
 msgid "Travel"
 msgstr "Reis"
 
-#: engines/access/metaengine.cpp:334 engines/cine/metaengine.cpp:332
+#: engines/access/metaengine.cpp:355 engines/cine/metaengine.cpp:332
 #: engines/petka/metaengine.cpp:144 engines/saga/metaengine.cpp:394
 #: engines/supernova/metaengine.cpp:331
 msgid "Take"
 msgstr "Ta"
 
 #. I18N: Action in In Cold Blood
-#: engines/access/metaengine.cpp:339 engines/alcachofa/metaengine.cpp:116
+#: engines/access/metaengine.cpp:360 engines/alcachofa/metaengine.cpp:116
 #: engines/cine/metaengine.cpp:337 engines/crab/input/input.cpp:251
 #: engines/cruise/metaengine.cpp:192 engines/draci/metaengine.cpp:237
 #: engines/dragons/metaengine.cpp:147 engines/drascula/metaengine.cpp:218
@@ -6821,18 +6829,18 @@ msgstr "Ta"
 msgid "Inventory"
 msgstr "Utstyrsliste"
 
-#: engines/access/metaengine.cpp:344 engines/ultima/ultima4/metaengine.cpp:51
+#: engines/access/metaengine.cpp:365 engines/ultima/ultima4/metaengine.cpp:51
 msgid "Climb"
 msgstr "Klatre"
 
-#: engines/access/metaengine.cpp:354 engines/bbvs/metaengine.cpp:142
+#: engines/access/metaengine.cpp:375 engines/bbvs/metaengine.cpp:142
 #: engines/freescape/games/castle/castle.cpp:511
 #: engines/petka/metaengine.cpp:137 engines/scumm/help.cpp:225
 #: engines/tsage/metaengine.cpp:201
 msgid "Walk"
 msgstr "G??"
 
-#: engines/access/metaengine.cpp:365
+#: engines/access/metaengine.cpp:386
 #, fuzzy
 #| msgid "Go to save/load menu"
 msgid "Open save/load menu"
diff --git a/po/nl_NL.po b/po/nl_NL.po
index 19a23c4d561..8712958b935 100644
--- a/po/nl_NL.po
+++ b/po/nl_NL.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.9.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-17 23:07+0000\n"
+"POT-Creation-Date: 2026-06-18 09:25+0000\n"
 "PO-Revision-Date: 2026-02-11 14:23+0000\n"
 "Last-Translator: SecularSteve <fairfull.playing at gmail.com>\n"
 "Language-Team: Dutch <https://translations.scummvm.org/projects/scummvm/"
@@ -85,7 +85,7 @@ msgstr "Sluiten"
 #. I18N: Opens the help screen.
 #: gui/browser.cpp:64 gui/launcher.cpp:277 gui/launcher.cpp:959
 #: backends/platform/sdl/macosx/appmenu_osx.mm:319
-#: engines/access/metaengine.cpp:360 engines/bladerunner/metaengine.cpp:321
+#: engines/access/metaengine.cpp:381 engines/bladerunner/metaengine.cpp:321
 #: engines/ngi/metaengine.cpp:162 engines/supernova/metaengine.cpp:238
 #: engines/tucker/metaengine.cpp:215 engines/zvision/metaengine.cpp:246
 msgid "Help"
@@ -2679,7 +2679,7 @@ msgstr ""
 "En meer..."
 
 #: gui/options.cpp:2975 engines/engine.cpp:647
-#: engines/access/metaengine.cpp:298 engines/drascula/metaengine.cpp:255
+#: engines/access/metaengine.cpp:319 engines/drascula/metaengine.cpp:255
 #: engines/queen/metaengine.cpp:269 engines/sherlock/metaengine.cpp:410
 #: engines/ultima/nuvie/metaengine.cpp:71
 msgid "Move"
@@ -4002,7 +4002,7 @@ msgstr "Spelmenu"
 
 #. I18N: Skipping cutscene plaback
 #. I18N: Skipping cutscene playback
-#: engines/metaengine.cpp:112 engines/access/metaengine.cpp:225
+#: engines/metaengine.cpp:112 engines/access/metaengine.cpp:246
 #: engines/composer/metaengine.cpp:126 engines/drascula/metaengine.cpp:224
 #: engines/efh/metaengine.cpp:379 engines/freescape/movement.cpp:93
 #: engines/grim/grim.cpp:533 engines/grim/grim.cpp:633
@@ -6296,7 +6296,15 @@ msgstr "Startmenu"
 msgid "Check for Updates..."
 msgstr "Controleren op updates..."
 
-#: engines/access/metaengine.cpp:215 engines/alcachofa/metaengine.cpp:94
+#: engines/access/metaengine.cpp:82
+msgid "Use 'high definition' OGG music"
+msgstr ""
+
+#: engines/access/metaengine.cpp:83
+msgid "Use the OGG audio from the re-release instead of original MIDI tracks"
+msgstr ""
+
+#: engines/access/metaengine.cpp:236 engines/alcachofa/metaengine.cpp:94
 #: engines/bbvs/metaengine.cpp:92 engines/buried/metaengine.cpp:133
 #: engines/chamber/metaengine.cpp:59 engines/cine/metaengine.cpp:270
 #: engines/cruise/metaengine.cpp:168 engines/dm/metaengine.cpp:124
@@ -6320,16 +6328,16 @@ msgstr "Controleren op updates..."
 msgid "Default keymappings"
 msgstr "Standaardtoetsenindelingen"
 
-#: engines/access/metaengine.cpp:219
+#: engines/access/metaengine.cpp:240
 msgid "Move / Interact / Skip"
 msgstr "Verplaatsen / Interactie / Overslaan"
 
-#: engines/access/metaengine.cpp:231 engines/sherlock/metaengine.cpp:483
+#: engines/access/metaengine.cpp:252 engines/sherlock/metaengine.cpp:483
 msgid "Skip movie"
 msgstr "Film overslaan"
 
 #. I18N: Move Actor to upwards direction
-#: engines/access/metaengine.cpp:237 engines/asylum/metaengine.cpp:171
+#: engines/access/metaengine.cpp:258 engines/asylum/metaengine.cpp:171
 #: engines/asylum/metaengine.cpp:198 engines/cine/metaengine.cpp:377
 #: engines/efh/metaengine.cpp:286 engines/hdb/metaengine.cpp:213
 #: engines/hypno/metaengine.cpp:363 engines/hypno/metaengine.cpp:411
@@ -6339,7 +6347,7 @@ msgid "Move up"
 msgstr "Beweeg omhoog"
 
 #. I18N: Move Actor to downwards direction
-#: engines/access/metaengine.cpp:244 engines/asylum/metaengine.cpp:177
+#: engines/access/metaengine.cpp:265 engines/asylum/metaengine.cpp:177
 #: engines/asylum/metaengine.cpp:205 engines/cine/metaengine.cpp:384
 #: engines/efh/metaengine.cpp:293 engines/hdb/metaengine.cpp:219
 #: engines/hypno/metaengine.cpp:351 engines/hypno/metaengine.cpp:399
@@ -6350,7 +6358,7 @@ msgstr "Beweeg omlaag"
 
 #. I18N: Move Actor to left direction
 #. I18N: Move actor left during fight
-#: engines/access/metaengine.cpp:251 engines/asylum/metaengine.cpp:183
+#: engines/access/metaengine.cpp:272 engines/asylum/metaengine.cpp:183
 #: engines/asylum/metaengine.cpp:212 engines/cine/metaengine.cpp:391
 #: engines/efh/metaengine.cpp:300 engines/hdb/metaengine.cpp:225
 #: engines/hypno/metaengine.cpp:345 engines/hypno/metaengine.cpp:393
@@ -6363,7 +6371,7 @@ msgstr "Beweeg naar links"
 
 #. I18N: Move Actor to right direction
 #. I18N: Move actor right during fight
-#: engines/access/metaengine.cpp:258 engines/asylum/metaengine.cpp:189
+#: engines/access/metaengine.cpp:279 engines/asylum/metaengine.cpp:189
 #: engines/asylum/metaengine.cpp:219 engines/cine/metaengine.cpp:398
 #: engines/efh/metaengine.cpp:307 engines/hdb/metaengine.cpp:231
 #: engines/hypno/metaengine.cpp:357 engines/hypno/metaengine.cpp:405
@@ -6375,31 +6383,31 @@ msgid "Move right"
 msgstr "Beweeg naar rechts"
 
 #. I18N: Move Actor to top-left direction
-#: engines/access/metaengine.cpp:265 engines/cine/metaengine.cpp:405
+#: engines/access/metaengine.cpp:286 engines/cine/metaengine.cpp:405
 #: engines/efh/metaengine.cpp:314
 msgid "Move up-left"
 msgstr "Naar linksboven verplaatsen"
 
 #. I18N: Move Actor to top-right direction
-#: engines/access/metaengine.cpp:270 engines/cine/metaengine.cpp:411
+#: engines/access/metaengine.cpp:291 engines/cine/metaengine.cpp:411
 #: engines/efh/metaengine.cpp:320
 msgid "Move up-right"
 msgstr "Naar rechtsboven verplaatsen"
 
 #. I18N: Move Actor to bottom-left direction
-#: engines/access/metaengine.cpp:275 engines/cine/metaengine.cpp:417
+#: engines/access/metaengine.cpp:296 engines/cine/metaengine.cpp:417
 #: engines/efh/metaengine.cpp:326
 msgid "Move down-left"
 msgstr "Naar linksonder verplaatsen"
 
 #. I18N: Move Actor to bottom-right direction
-#: engines/access/metaengine.cpp:280 engines/cine/metaengine.cpp:423
+#: engines/access/metaengine.cpp:301 engines/cine/metaengine.cpp:423
 #: engines/efh/metaengine.cpp:332
 msgid "Move down-right"
 msgstr "Naar rechtsonder gaan"
 
 #. I18N: (Game name: The Lost Files of Sherlock Holmes: The Case of the Rose Tattoo) This action is used to look at an object or npc in the game
-#: engines/access/metaengine.cpp:285 engines/bbvs/metaengine.cpp:124
+#: engines/access/metaengine.cpp:306 engines/bbvs/metaengine.cpp:124
 #: engines/dgds/metaengine.cpp:83 engines/drascula/metaengine.cpp:230
 #: engines/petka/metaengine.cpp:130 engines/scumm/help.cpp:167
 #: engines/scumm/help.cpp:231 engines/sherlock/metaengine.cpp:405
@@ -6410,7 +6418,7 @@ msgstr "Naar rechtsonder gaan"
 msgid "Look"
 msgstr "Kijk"
 
-#: engines/access/metaengine.cpp:293 engines/drascula/metaengine.cpp:240
+#: engines/access/metaengine.cpp:314 engines/drascula/metaengine.cpp:240
 #: engines/queen/metaengine.cpp:259 engines/saga/metaengine.cpp:369
 #: engines/scumm/help.cpp:125 engines/scumm/help.cpp:140
 #: engines/scumm/help.cpp:165 engines/scumm/help.cpp:193
@@ -6420,13 +6428,13 @@ msgstr "Kijk"
 msgid "Open"
 msgstr "Open"
 
-#: engines/access/metaengine.cpp:303 engines/scumm/help.cpp:128
+#: engines/access/metaengine.cpp:324 engines/scumm/help.cpp:128
 #: engines/ultima/nuvie/metaengine.cpp:70
 msgid "Get"
 msgstr "Pak"
 
 #. I18N: Combat command in Might & Magic 1
-#: engines/access/metaengine.cpp:308 engines/access/metaengine.cpp:329
+#: engines/access/metaengine.cpp:329 engines/access/metaengine.cpp:350
 #: engines/bbvs/metaengine.cpp:136 engines/cine/metaengine.cpp:342
 #: engines/efh/metaengine.cpp:462 engines/hdb/metaengine.cpp:237
 #: engines/lab/metaengine.cpp:323 engines/mm/mm1/metaengine.cpp:86
@@ -6446,11 +6454,11 @@ msgstr "Pak"
 msgid "Use"
 msgstr "Gebruik"
 
-#: engines/access/metaengine.cpp:313
+#: engines/access/metaengine.cpp:334
 msgid "Goto"
 msgstr "Ga naar"
 
-#: engines/access/metaengine.cpp:318 engines/access/metaengine.cpp:349
+#: engines/access/metaengine.cpp:339 engines/access/metaengine.cpp:370
 #: engines/bbvs/metaengine.cpp:130 engines/drascula/metaengine.cpp:250
 #: engines/efh/metaengine.cpp:506 engines/petka/metaengine.cpp:158
 #: engines/scumm/help.cpp:174 engines/scumm/help.cpp:226
@@ -6461,18 +6469,18 @@ msgstr "Ga naar"
 msgid "Talk"
 msgstr "Praat"
 
-#: engines/access/metaengine.cpp:323 engines/scumm/help.cpp:175
+#: engines/access/metaengine.cpp:344 engines/scumm/help.cpp:175
 msgid "Travel"
 msgstr "Reis"
 
-#: engines/access/metaengine.cpp:334 engines/cine/metaengine.cpp:332
+#: engines/access/metaengine.cpp:355 engines/cine/metaengine.cpp:332
 #: engines/petka/metaengine.cpp:144 engines/saga/metaengine.cpp:394
 #: engines/supernova/metaengine.cpp:331
 msgid "Take"
 msgstr "Pak"
 
 #. I18N: Action in In Cold Blood
-#: engines/access/metaengine.cpp:339 engines/alcachofa/metaengine.cpp:116
+#: engines/access/metaengine.cpp:360 engines/alcachofa/metaengine.cpp:116
 #: engines/cine/metaengine.cpp:337 engines/crab/input/input.cpp:251
 #: engines/cruise/metaengine.cpp:192 engines/draci/metaengine.cpp:237
 #: engines/dragons/metaengine.cpp:147 engines/drascula/metaengine.cpp:218
@@ -6492,18 +6500,18 @@ msgstr "Pak"
 msgid "Inventory"
 msgstr "Inventaris"
 
-#: engines/access/metaengine.cpp:344 engines/ultima/ultima4/metaengine.cpp:51
+#: engines/access/metaengine.cpp:365 engines/ultima/ultima4/metaengine.cpp:51
 msgid "Climb"
 msgstr "Klimmen"
 
-#: engines/access/metaengine.cpp:354 engines/bbvs/metaengine.cpp:142
+#: engines/access/metaengine.cpp:375 engines/bbvs/metaengine.cpp:142
 #: engines/freescape/games/castle/castle.cpp:511
 #: engines/petka/metaengine.cpp:137 engines/scumm/help.cpp:225
 #: engines/tsage/metaengine.cpp:201
 msgid "Walk"
 msgstr "Loop"
 
-#: engines/access/metaengine.cpp:365
+#: engines/access/metaengine.cpp:386
 msgid "Open save/load menu"
 msgstr "Menu openen/opslaan laden"
 
diff --git a/po/nn_NO.po b/po/nn_NO.po
index dfb52357bcd..cd0db72468a 100644
--- a/po/nn_NO.po
+++ b/po/nn_NO.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-17 23:07+0000\n"
+"POT-Creation-Date: 2026-06-18 09:25+0000\n"
 "PO-Revision-Date: 2023-12-14 14:28+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Norwegian Nynorsk <https://translations.scummvm.org/projects/"
@@ -88,7 +88,7 @@ msgstr "Steng"
 #. I18N: Opens the help screen.
 #: gui/browser.cpp:64 gui/launcher.cpp:277 gui/launcher.cpp:959
 #: backends/platform/sdl/macosx/appmenu_osx.mm:319
-#: engines/access/metaengine.cpp:360 engines/bladerunner/metaengine.cpp:321
+#: engines/access/metaengine.cpp:381 engines/bladerunner/metaengine.cpp:321
 #: engines/ngi/metaengine.cpp:162 engines/supernova/metaengine.cpp:238
 #: engines/tucker/metaengine.cpp:215 engines/zvision/metaengine.cpp:246
 msgid "Help"
@@ -2634,7 +2634,7 @@ msgid ""
 msgstr "Legg til spill..."
 
 #: gui/options.cpp:2975 engines/engine.cpp:647
-#: engines/access/metaengine.cpp:298 engines/drascula/metaengine.cpp:255
+#: engines/access/metaengine.cpp:319 engines/drascula/metaengine.cpp:255
 #: engines/queen/metaengine.cpp:269 engines/sherlock/metaengine.cpp:410
 #: engines/ultima/nuvie/metaengine.cpp:71
 #, fuzzy
@@ -3931,7 +3931,7 @@ msgstr "Spelmeny"
 
 #. I18N: Skipping cutscene plaback
 #. I18N: Skipping cutscene playback
-#: engines/metaengine.cpp:112 engines/access/metaengine.cpp:225
+#: engines/metaengine.cpp:112 engines/access/metaengine.cpp:246
 #: engines/composer/metaengine.cpp:126 engines/drascula/metaengine.cpp:224
 #: engines/efh/metaengine.cpp:379 engines/freescape/movement.cpp:93
 #: engines/grim/grim.cpp:533 engines/grim/grim.cpp:633
@@ -6020,7 +6020,15 @@ msgstr "Oppstartar"
 msgid "Check for Updates..."
 msgstr "SJ?? etter oppdateringar..."
 
-#: engines/access/metaengine.cpp:215 engines/alcachofa/metaengine.cpp:94
+#: engines/access/metaengine.cpp:82
+msgid "Use 'high definition' OGG music"
+msgstr ""
+
+#: engines/access/metaengine.cpp:83
+msgid "Use the OGG audio from the re-release instead of original MIDI tracks"
+msgstr ""
+
+#: engines/access/metaengine.cpp:236 engines/alcachofa/metaengine.cpp:94
 #: engines/bbvs/metaengine.cpp:92 engines/buried/metaengine.cpp:133
 #: engines/chamber/metaengine.cpp:59 engines/cine/metaengine.cpp:270
 #: engines/cruise/metaengine.cpp:168 engines/dm/metaengine.cpp:124
@@ -6046,20 +6054,20 @@ msgstr "SJ?? etter oppdateringar..."
 msgid "Default keymappings"
 msgstr "Spelval"
 
-#: engines/access/metaengine.cpp:219
+#: engines/access/metaengine.cpp:240
 #, fuzzy
 #| msgid "Highlight next dialogue"
 msgid "Move / Interact / Skip"
 msgstr "Merk neste dialog"
 
-#: engines/access/metaengine.cpp:231 engines/sherlock/metaengine.cpp:483
+#: engines/access/metaengine.cpp:252 engines/sherlock/metaengine.cpp:483
 #, fuzzy
 #| msgid "Skip line"
 msgid "Skip movie"
 msgstr "Hopp over linje"
 
 #. I18N: Move Actor to upwards direction
-#: engines/access/metaengine.cpp:237 engines/asylum/metaengine.cpp:171
+#: engines/access/metaengine.cpp:258 engines/asylum/metaengine.cpp:171
 #: engines/asylum/metaengine.cpp:198 engines/cine/metaengine.cpp:377
 #: engines/efh/metaengine.cpp:286 engines/hdb/metaengine.cpp:213
 #: engines/hypno/metaengine.cpp:363 engines/hypno/metaengine.cpp:411
@@ -6070,7 +6078,7 @@ msgid "Move up"
 msgstr "Beveg Bakover"
 
 #. I18N: Move Actor to downwards direction
-#: engines/access/metaengine.cpp:244 engines/asylum/metaengine.cpp:177
+#: engines/access/metaengine.cpp:265 engines/asylum/metaengine.cpp:177
 #: engines/asylum/metaengine.cpp:205 engines/cine/metaengine.cpp:384
 #: engines/efh/metaengine.cpp:293 engines/hdb/metaengine.cpp:219
 #: engines/hypno/metaengine.cpp:351 engines/hypno/metaengine.cpp:399
@@ -6082,7 +6090,7 @@ msgstr "Skli til H??yre"
 
 #. I18N: Move Actor to left direction
 #. I18N: Move actor left during fight
-#: engines/access/metaengine.cpp:251 engines/asylum/metaengine.cpp:183
+#: engines/access/metaengine.cpp:272 engines/asylum/metaengine.cpp:183
 #: engines/asylum/metaengine.cpp:212 engines/cine/metaengine.cpp:391
 #: engines/efh/metaengine.cpp:300 engines/hdb/metaengine.cpp:225
 #: engines/hypno/metaengine.cpp:345 engines/hypno/metaengine.cpp:393
@@ -6096,7 +6104,7 @@ msgstr "Skli til Venstre"
 
 #. I18N: Move Actor to right direction
 #. I18N: Move actor right during fight
-#: engines/access/metaengine.cpp:258 engines/asylum/metaengine.cpp:189
+#: engines/access/metaengine.cpp:279 engines/asylum/metaengine.cpp:189
 #: engines/asylum/metaengine.cpp:219 engines/cine/metaengine.cpp:398
 #: engines/efh/metaengine.cpp:307 engines/hdb/metaengine.cpp:231
 #: engines/hypno/metaengine.cpp:357 engines/hypno/metaengine.cpp:405
@@ -6109,35 +6117,35 @@ msgid "Move right"
 msgstr "Skli til H??yre"
 
 #. I18N: Move Actor to top-left direction
-#: engines/access/metaengine.cpp:265 engines/cine/metaengine.cpp:405
+#: engines/access/metaengine.cpp:286 engines/cine/metaengine.cpp:405
 #: engines/efh/metaengine.cpp:314
 #, fuzzy
 msgid "Move up-left"
 msgstr "Skli til Venstre"
 
 #. I18N: Move Actor to top-right direction
-#: engines/access/metaengine.cpp:270 engines/cine/metaengine.cpp:411
+#: engines/access/metaengine.cpp:291 engines/cine/metaengine.cpp:411
 #: engines/efh/metaengine.cpp:320
 #, fuzzy
 msgid "Move up-right"
 msgstr "Skli til H??yre"
 
 #. I18N: Move Actor to bottom-left direction
-#: engines/access/metaengine.cpp:275 engines/cine/metaengine.cpp:417
+#: engines/access/metaengine.cpp:296 engines/cine/metaengine.cpp:417
 #: engines/efh/metaengine.cpp:326
 #, fuzzy
 msgid "Move down-left"
 msgstr "Skli til H??yre"
 
 #. I18N: Move Actor to bottom-right direction
-#: engines/access/metaengine.cpp:280 engines/cine/metaengine.cpp:423
+#: engines/access/metaengine.cpp:301 engines/cine/metaengine.cpp:423
 #: engines/efh/metaengine.cpp:332
 #, fuzzy
 msgid "Move down-right"
 msgstr "Skli til H??yre"
 
 #. I18N: (Game name: The Lost Files of Sherlock Holmes: The Case of the Rose Tattoo) This action is used to look at an object or npc in the game
-#: engines/access/metaengine.cpp:285 engines/bbvs/metaengine.cpp:124
+#: engines/access/metaengine.cpp:306 engines/bbvs/metaengine.cpp:124
 #: engines/dgds/metaengine.cpp:83 engines/drascula/metaengine.cpp:230
 #: engines/petka/metaengine.cpp:130 engines/scumm/help.cpp:167
 #: engines/scumm/help.cpp:231 engines/sherlock/metaengine.cpp:405
@@ -6148,7 +6156,7 @@ msgstr "Skli til H??yre"
 msgid "Look"
 msgstr "Kikk"
 
-#: engines/access/metaengine.cpp:293 engines/drascula/metaengine.cpp:240
+#: engines/access/metaengine.cpp:314 engines/drascula/metaengine.cpp:240
 #: engines/queen/metaengine.cpp:259 engines/saga/metaengine.cpp:369
 #: engines/scumm/help.cpp:125 engines/scumm/help.cpp:140
 #: engines/scumm/help.cpp:165 engines/scumm/help.cpp:193
@@ -6158,13 +6166,13 @@ msgstr "Kikk"
 msgid "Open"
 msgstr "??pne"
 
-#: engines/access/metaengine.cpp:303 engines/scumm/help.cpp:128
+#: engines/access/metaengine.cpp:324 engines/scumm/help.cpp:128
 #: engines/ultima/nuvie/metaengine.cpp:70
 msgid "Get"
 msgstr "F??"
 
 #. I18N: Combat command in Might & Magic 1
-#: engines/access/metaengine.cpp:308 engines/access/metaengine.cpp:329
+#: engines/access/metaengine.cpp:329 engines/access/metaengine.cpp:350
 #: engines/bbvs/metaengine.cpp:136 engines/cine/metaengine.cpp:342
 #: engines/efh/metaengine.cpp:462 engines/hdb/metaengine.cpp:237
 #: engines/lab/metaengine.cpp:323 engines/mm/mm1/metaengine.cpp:86
@@ -6184,11 +6192,11 @@ msgstr "F??"
 msgid "Use"
 msgstr "Nytt"
 
-#: engines/access/metaengine.cpp:313
+#: engines/access/metaengine.cpp:334
 msgid "Goto"
 msgstr ""
 
-#: engines/access/metaengine.cpp:318 engines/access/metaengine.cpp:349
+#: engines/access/metaengine.cpp:339 engines/access/metaengine.cpp:370
 #: engines/bbvs/metaengine.cpp:130 engines/drascula/metaengine.cpp:250
 #: engines/efh/metaengine.cpp:506 engines/petka/metaengine.cpp:158
 #: engines/scumm/help.cpp:174 engines/scumm/help.cpp:226
@@ -6199,11 +6207,11 @@ msgstr ""
 msgid "Talk"
 msgstr "Snakk"
 
-#: engines/access/metaengine.cpp:323 engines/scumm/help.cpp:175
+#: engines/access/metaengine.cpp:344 engines/scumm/help.cpp:175
 msgid "Travel"
 msgstr "Reis"
 
-#: engines/access/metaengine.cpp:334 engines/cine/metaengine.cpp:332
+#: engines/access/metaengine.cpp:355 engines/cine/metaengine.cpp:332
 #: engines/petka/metaengine.cpp:144 engines/saga/metaengine.cpp:394
 #: engines/supernova/metaengine.cpp:331
 #, fuzzy
@@ -6212,7 +6220,7 @@ msgid "Take"
 msgstr "Ta av (klede)"
 
 #. I18N: Action in In Cold Blood
-#: engines/access/metaengine.cpp:339 engines/alcachofa/metaengine.cpp:116
+#: engines/access/metaengine.cpp:360 engines/alcachofa/metaengine.cpp:116
 #: engines/cine/metaengine.cpp:337 engines/crab/input/input.cpp:251
 #: engines/cruise/metaengine.cpp:192 engines/draci/metaengine.cpp:237
 #: engines/dragons/metaengine.cpp:147 engines/drascula/metaengine.cpp:218
@@ -6232,18 +6240,18 @@ msgstr "Ta av (klede)"
 msgid "Inventory"
 msgstr "Inventar"
 
-#: engines/access/metaengine.cpp:344 engines/ultima/ultima4/metaengine.cpp:51
+#: engines/access/metaengine.cpp:365 engines/ultima/ultima4/metaengine.cpp:51
 msgid "Climb"
 msgstr ""
 
-#: engines/access/metaengine.cpp:354 engines/bbvs/metaengine.cpp:142
+#: engines/access/metaengine.cpp:375 engines/bbvs/metaengine.cpp:142
 #: engines/freescape/games/castle/castle.cpp:511
 #: engines/petka/metaengine.cpp:137 engines/scumm/help.cpp:225
 #: engines/tsage/metaengine.cpp:201
 msgid "Walk"
 msgstr "G??"
 
-#: engines/access/metaengine.cpp:365
+#: engines/access/metaengine.cpp:386
 #, fuzzy
 msgid "Open save/load menu"
 msgstr "Sett in lagre/laste speldisk"
diff --git a/po/pl_PL.po b/po/pl_PL.po
index bf096475100..65162fcc2b5 100644
--- a/po/pl_PL.po
+++ b/po/pl_PL.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-17 23:07+0000\n"
+"POT-Creation-Date: 2026-06-18 09:25+0000\n"
 "PO-Revision-Date: 2026-04-17 00:22+0000\n"
 "Last-Translator: SkiffPL <skiff at windowslive.com>\n"
 "Language-Team: Polish <https://translations.scummvm.org/projects/scummvm/"
@@ -88,7 +88,7 @@ msgstr "Zamknij"
 #. I18N: Opens the help screen.
 #: gui/browser.cpp:64 gui/launcher.cpp:277 gui/launcher.cpp:959
 #: backends/platform/sdl/macosx/appmenu_osx.mm:319
-#: engines/access/metaengine.cpp:360 engines/bladerunner/metaengine.cpp:321
+#: engines/access/metaengine.cpp:381 engines/bladerunner/metaengine.cpp:321
 #: engines/ngi/metaengine.cpp:162 engines/supernova/metaengine.cpp:238
 #: engines/tucker/metaengine.cpp:215 engines/zvision/metaengine.cpp:246
 msgid "Help"
@@ -2688,7 +2688,7 @@ msgstr ""
 "i inne..."
 
 #: gui/options.cpp:2975 engines/engine.cpp:647
-#: engines/access/metaengine.cpp:298 engines/drascula/metaengine.cpp:255
+#: engines/access/metaengine.cpp:319 engines/drascula/metaengine.cpp:255
 #: engines/queen/metaengine.cpp:269 engines/sherlock/metaengine.cpp:410
 #: engines/ultima/nuvie/metaengine.cpp:71
 msgid "Move"
@@ -3993,7 +3993,7 @@ msgstr "Menu gry"
 
 #. I18N: Skipping cutscene plaback
 #. I18N: Skipping cutscene playback
-#: engines/metaengine.cpp:112 engines/access/metaengine.cpp:225
+#: engines/metaengine.cpp:112 engines/access/metaengine.cpp:246
 #: engines/composer/metaengine.cpp:126 engines/drascula/metaengine.cpp:224
 #: engines/efh/metaengine.cpp:379 engines/freescape/movement.cpp:93
 #: engines/grim/grim.cpp:533 engines/grim/grim.cpp:633
@@ -6308,7 +6308,15 @@ msgstr "Program uruchamiaj??cy"
 msgid "Check for Updates..."
 msgstr "Szukaj aktualizacji..."
 
-#: engines/access/metaengine.cpp:215 engines/alcachofa/metaengine.cpp:94
+#: engines/access/metaengine.cpp:82
+msgid "Use 'high definition' OGG music"
+msgstr ""
+
+#: engines/access/metaengine.cpp:83
+msgid "Use the OGG audio from the re-release instead of original MIDI tracks"
+msgstr ""
+
+#: engines/access/metaengine.cpp:236 engines/alcachofa/metaengine.cpp:94
 #: engines/bbvs/metaengine.cpp:92 engines/buried/metaengine.cpp:133
 #: engines/chamber/metaengine.cpp:59 engines/cine/metaengine.cpp:270
 #: engines/cruise/metaengine.cpp:168 engines/dm/metaengine.cpp:124
@@ -6332,16 +6340,16 @@ msgstr "Szukaj aktualizacji..."
 msgid "Default keymappings"
 msgstr "Domy??lne mapowanie klawiszy"
 
-#: engines/access/metaengine.cpp:219
+#: engines/access/metaengine.cpp:240
 msgid "Move / Interact / Skip"
 msgstr "Chodzenie / Interakcja / Pomijanie"
 
-#: engines/access/metaengine.cpp:231 engines/sherlock/metaengine.cpp:483
+#: engines/access/metaengine.cpp:252 engines/sherlock/metaengine.cpp:483
 msgid "Skip movie"
 msgstr "Pomi?? film"
 
 #. I18N: Move Actor to upwards direction
-#: engines/access/metaengine.cpp:237 engines/asylum/metaengine.cpp:171
+#: engines/access/metaengine.cpp:258 engines/asylum/metaengine.cpp:171
 #: engines/asylum/metaengine.cpp:198 engines/cine/metaengine.cpp:377
 #: engines/efh/metaengine.cpp:286 engines/hdb/metaengine.cpp:213
 #: engines/hypno/metaengine.cpp:363 engines/hypno/metaengine.cpp:411
@@ -6351,7 +6359,7 @@ msgid "Move up"
 msgstr "Ruch w g??r??"
 
 #. I18N: Move Actor to downwards direction
-#: engines/access/metaengine.cpp:244 engines/asylum/metaengine.cpp:177
+#: engines/access/metaengine.cpp:265 engines/asylum/metaengine.cpp:177
 #: engines/asylum/metaengine.cpp:205 engines/cine/metaengine.cpp:384
 #: engines/efh/metaengine.cpp:293 engines/hdb/metaengine.cpp:219
 #: engines/hypno/metaengine.cpp:351 engines/hypno/metaengine.cpp:399
@@ -6362,7 +6370,7 @@ msgstr "Ruch w d????"
 
 #. I18N: Move Actor to left direction
 #. I18N: Move actor left during fight
-#: engines/access/metaengine.cpp:251 engines/asylum/metaengine.cpp:183
+#: engines/access/metaengine.cpp:272 engines/asylum/metaengine.cpp:183
 #: engines/asylum/metaengine.cpp:212 engines/cine/metaengine.cpp:391
 #: engines/efh/metaengine.cpp:300 engines/hdb/metaengine.cpp:225
 #: engines/hypno/metaengine.cpp:345 engines/hypno/metaengine.cpp:393
@@ -6375,7 +6383,7 @@ msgstr "Ruch w lewo"
 
 #. I18N: Move Actor to right direction
 #. I18N: Move actor right during fight
-#: engines/access/metaengine.cpp:258 engines/asylum/metaengine.cpp:189
+#: engines/access/metaengine.cpp:279 engines/asylum/metaengine.cpp:189
 #: engines/asylum/metaengine.cpp:219 engines/cine/metaengine.cpp:398
 #: engines/efh/metaengine.cpp:307 engines/hdb/metaengine.cpp:231
 #: engines/hypno/metaengine.cpp:357 engines/hypno/metaengine.cpp:405
@@ -6387,31 +6395,31 @@ msgid "Move right"
 msgstr "Ruch w prawo"
 
 #. I18N: Move Actor to top-left direction
-#: engines/access/metaengine.cpp:265 engines/cine/metaengine.cpp:405
+#: engines/access/metaengine.cpp:286 engines/cine/metaengine.cpp:405
 #: engines/efh/metaengine.cpp:314
 msgid "Move up-left"
 msgstr "Ruch w g??r?? i w lewo"
 
 #. I18N: Move Actor to top-right direction
-#: engines/access/metaengine.cpp:270 engines/cine/metaengine.cpp:411
+#: engines/access/metaengine.cpp:291 engines/cine/metaengine.cpp:411
 #: engines/efh/metaengine.cpp:320
 msgid "Move up-right"
 msgstr "Ruch w g??r?? i w prawo"
 
 #. I18N: Move Actor to bottom-left direction
-#: engines/access/metaengine.cpp:275 engines/cine/metaengine.cpp:417
+#: engines/access/metaengine.cpp:296 engines/cine/metaengine.cpp:417
 #: engines/efh/metaengine.cpp:326
 msgid "Move down-left"
 msgstr "Ruch w d???? i w lewo"
 
 #. I18N: Move Actor to bottom-right direction
-#: engines/access/metaengine.cpp:280 engines/cine/metaengine.cpp:423
+#: engines/access/metaengine.cpp:301 engines/cine/metaengine.cpp:423
 #: engines/efh/metaengine.cpp:332
 msgid "Move down-right"
 msgstr "Ruch w d???? i w prawo"
 
 #. I18N: (Game name: The Lost Files of Sherlock Holmes: The Case of the Rose Tattoo) This action is used to look at an object or npc in the game
-#: engines/access/metaengine.cpp:285 engines/bbvs/metaengine.cpp:124
+#: engines/access/metaengine.cpp:306 engines/bbvs/metaengine.cpp:124
 #: engines/dgds/metaengine.cpp:83 engines/drascula/metaengine.cpp:230
 #: engines/petka/metaengine.cpp:130 engines/scumm/help.cpp:167
 #: engines/scumm/help.cpp:231 engines/sherlock/metaengine.cpp:405
@@ -6422,7 +6430,7 @@ msgstr "Ruch w d???? i w prawo"
 msgid "Look"
 msgstr "Sp??jrz"
 
-#: engines/access/metaengine.cpp:293 engines/drascula/metaengine.cpp:240
+#: engines/access/metaengine.cpp:314 engines/drascula/metaengine.cpp:240
 #: engines/queen/metaengine.cpp:259 engines/saga/metaengine.cpp:369
 #: engines/scumm/help.cpp:125 engines/scumm/help.cpp:140
 #: engines/scumm/help.cpp:165 engines/scumm/help.cpp:193
@@ -6432,13 +6440,13 @@ msgstr "Sp??jrz"
 msgid "Open"
 msgstr "Otw??rz"
 
-#: engines/access/metaengine.cpp:303 engines/scumm/help.cpp:128
+#: engines/access/metaengine.cpp:324 engines/scumm/help.cpp:128
 #: engines/ultima/nuvie/metaengine.cpp:70
 msgid "Get"
 msgstr "We??"
 
 #. I18N: Combat command in Might & Magic 1
-#: engines/access/metaengine.cpp:308 engines/access/metaengine.cpp:329
+#: engines/access/metaengine.cpp:329 engines/access/metaengine.cpp:350
 #: engines/bbvs/metaengine.cpp:136 engines/cine/metaengine.cpp:342
 #: engines/efh/metaengine.cpp:462 engines/hdb/metaengine.cpp:237
 #: engines/lab/metaengine.cpp:323 engines/mm/mm1/metaengine.cpp:86
@@ -6458,11 +6466,11 @@ msgstr "We??"
 msgid "Use"
 msgstr "U??yj"
 
-#: engines/access/metaengine.cpp:313
+#: engines/access/metaengine.cpp:334
 msgid "Goto"
 msgstr "Id?? do"
 
-#: engines/access/metaengine.cpp:318 engines/access/metaengine.cpp:349
+#: engines/access/metaengine.cpp:339 engines/access/metaengine.cpp:370
 #: engines/bbvs/metaengine.cpp:130 engines/drascula/metaengine.cpp:250
 #: engines/efh/metaengine.cpp:506 engines/petka/metaengine.cpp:158
 #: engines/scumm/help.cpp:174 engines/scumm/help.cpp:226
@@ -6473,18 +6481,18 @@ msgstr "Id?? do"
 msgid "Talk"
 msgstr "Rozmawiaj"
 
-#: engines/access/metaengine.cpp:323 engines/scumm/help.cpp:175
+#: engines/access/metaengine.cpp:344 engines/scumm/help.cpp:175
 msgid "Travel"
 msgstr "Podr????uj"
 
-#: engines/access/metaengine.cpp:334 engines/cine/metaengine.cpp:332
+#: engines/access/metaengine.cpp:355 engines/cine/metaengine.cpp:332
 #: engines/petka/metaengine.cpp:144 engines/saga/metaengine.cpp:394
 #: engines/supernova/metaengine.cpp:331
 msgid "Take"
 msgstr "We??"
 
 #. I18N: Action in In Cold Blood
-#: engines/access/metaengine.cpp:339 engines/alcachofa/metaengine.cpp:116
+#: engines/access/metaengine.cpp:360 engines/alcachofa/metaengine.cpp:116
 #: engines/cine/metaengine.cpp:337 engines/crab/input/input.cpp:251
 #: engines/cruise/metaengine.cpp:192 engines/draci/metaengine.cpp:237
 #: engines/dragons/metaengine.cpp:147 engines/drascula/metaengine.cpp:218
@@ -6504,18 +6512,18 @@ msgstr "We??"
 msgid "Inventory"
 msgstr "Ekwipunek"
 
-#: engines/access/metaengine.cpp:344 engines/ultima/ultima4/metaengine.cpp:51
+#: engines/access/metaengine.cpp:365 engines/ultima/ultima4/metaengine.cpp:51
 msgid "Climb"
 msgstr "Wspinanie"
 
-#: engines/access/metaengine.cpp:354 engines/bbvs/metaengine.cpp:142
+#: engines/access/metaengine.cpp:375 engines/bbvs/metaengine.cpp:142
 #: engines/freescape/games/castle/castle.cpp:511
 #: engines/petka/metaengine.cpp:137 engines/scumm/help.cpp:225
 #: engines/tsage/metaengine.cpp:201
 msgid "Walk"
 msgstr "Id??"
 
-#: engines/access/metaengine.cpp:365
+#: engines/access/metaengine.cpp:386
 msgid "Open save/load menu"
 msgstr "Otw??rz menu zapisu/wczytywania"
 
diff --git a/po/pt_BR.po b/po/pt_BR.po
index 8d16be32b68..9b24e9e9010 100644
--- a/po/pt_BR.po
+++ b/po/pt_BR.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-17 23:07+0000\n"
+"POT-Creation-Date: 2026-06-18 09:25+0000\n"
 "PO-Revision-Date: 2026-06-17 22:10+0000\n"
 "Last-Translator: Marcel Souza Lemes <marcosoutsider at gmail.com>\n"
 "Language-Team: Portuguese (Brazil) <https://translations.scummvm.org/"
@@ -87,7 +87,7 @@ msgstr "Fechar"
 #. I18N: Opens the help screen.
 #: gui/browser.cpp:64 gui/launcher.cpp:277 gui/launcher.cpp:959
 #: backends/platform/sdl/macosx/appmenu_osx.mm:319
-#: engines/access/metaengine.cpp:360 engines/bladerunner/metaengine.cpp:321
+#: engines/access/metaengine.cpp:381 engines/bladerunner/metaengine.cpp:321
 #: engines/ngi/metaengine.cpp:162 engines/supernova/metaengine.cpp:238
 #: engines/tucker/metaengine.cpp:215 engines/zvision/metaengine.cpp:246
 msgid "Help"
@@ -2756,7 +2756,7 @@ msgstr ""
 "E mais..."
 
 #: gui/options.cpp:2975 engines/engine.cpp:647
-#: engines/access/metaengine.cpp:298 engines/drascula/metaengine.cpp:255
+#: engines/access/metaengine.cpp:319 engines/drascula/metaengine.cpp:255
 #: engines/queen/metaengine.cpp:269 engines/sherlock/metaengine.cpp:410
 #: engines/ultima/nuvie/metaengine.cpp:71
 msgid "Move"
@@ -4100,7 +4100,7 @@ msgstr "Menu do jogo"
 
 #. I18N: Skipping cutscene plaback
 #. I18N: Skipping cutscene playback
-#: engines/metaengine.cpp:112 engines/access/metaengine.cpp:225
+#: engines/metaengine.cpp:112 engines/access/metaengine.cpp:246
 #: engines/composer/metaengine.cpp:126 engines/drascula/metaengine.cpp:224
 #: engines/efh/metaengine.cpp:379 engines/freescape/movement.cpp:93
 #: engines/grim/grim.cpp:533 engines/grim/grim.cpp:633
@@ -6598,7 +6598,15 @@ msgstr "Inicializador"
 msgid "Check for Updates..."
 msgstr "Procurar por Atualiza????es..."
 
-#: engines/access/metaengine.cpp:215 engines/alcachofa/metaengine.cpp:94
+#: engines/access/metaengine.cpp:82
+msgid "Use 'high definition' OGG music"
+msgstr ""
+
+#: engines/access/metaengine.cpp:83
+msgid "Use the OGG audio from the re-release instead of original MIDI tracks"
+msgstr ""
+
+#: engines/access/metaengine.cpp:236 engines/alcachofa/metaengine.cpp:94
 #: engines/bbvs/metaengine.cpp:92 engines/buried/metaengine.cpp:133
 #: engines/chamber/metaengine.cpp:59 engines/cine/metaengine.cpp:270
 #: engines/cruise/metaengine.cpp:168 engines/dm/metaengine.cpp:124
@@ -6622,20 +6630,20 @@ msgstr "Procurar por Atualiza????es..."
 msgid "Default keymappings"
 msgstr "Mapeamento padr??o de teclas"
 
-#: engines/access/metaengine.cpp:219
+#: engines/access/metaengine.cpp:240
 #, fuzzy
 #| msgid "Go to next dialogue"
 msgid "Move / Interact / Skip"
 msgstr "Ir para pr??ximo di??logo"
 
-#: engines/access/metaengine.cpp:231 engines/sherlock/metaengine.cpp:483
+#: engines/access/metaengine.cpp:252 engines/sherlock/metaengine.cpp:483
 #, fuzzy
 #| msgid "Skip video"
 msgid "Skip movie"
 msgstr "Pular v??deo"
 
 #. I18N: Move Actor to upwards direction
-#: engines/access/metaengine.cpp:237 engines/asylum/metaengine.cpp:171
+#: engines/access/metaengine.cpp:258 engines/asylum/metaengine.cpp:171
 #: engines/asylum/metaengine.cpp:198 engines/cine/metaengine.cpp:377
 #: engines/efh/metaengine.cpp:286 engines/hdb/metaengine.cpp:213
 #: engines/hypno/metaengine.cpp:363 engines/hypno/metaengine.cpp:411
@@ -6645,7 +6653,7 @@ msgid "Move up"
 msgstr "Mover para cima"
 
 #. I18N: Move Actor to downwards direction
-#: engines/access/metaengine.cpp:244 engines/asylum/metaengine.cpp:177
+#: engines/access/metaengine.cpp:265 engines/asylum/metaengine.cpp:177
 #: engines/asylum/metaengine.cpp:205 engines/cine/metaengine.cpp:384
 #: engines/efh/metaengine.cpp:293 engines/hdb/metaengine.cpp:219
 #: engines/hypno/metaengine.cpp:351 engines/hypno/metaengine.cpp:399
@@ -6656,7 +6664,7 @@ msgstr "Mover para baixo"
 
 #. I18N: Move Actor to left direction
 #. I18N: Move actor left during fight
-#: engines/access/metaengine.cpp:251 engines/asylum/metaengine.cpp:183
+#: engines/access/metaengine.cpp:272 engines/asylum/metaengine.cpp:183
 #: engines/asylum/metaengine.cpp:212 engines/cine/metaengine.cpp:391
 #: engines/efh/metaengine.cpp:300 engines/hdb/metaengine.cpp:225
 #: engines/hypno/metaengine.cpp:345 engines/hypno/metaengine.cpp:393
@@ -6669,7 +6677,7 @@ msgstr "Mover para esquerda"
 
 #. I18N: Move Actor to right direction
 #. I18N: Move actor right during fight
-#: engines/access/metaengine.cpp:258 engines/asylum/metaengine.cpp:189
+#: engines/access/metaengine.cpp:279 engines/asylum/metaengine.cpp:189
 #: engines/asylum/metaengine.cpp:219 engines/cine/metaengine.cpp:398
 #: engines/efh/metaengine.cpp:307 engines/hdb/metaengine.cpp:231
 #: engines/hypno/metaengine.cpp:357 engines/hypno/metaengine.cpp:405
@@ -6681,31 +6689,31 @@ msgid "Move right"
 msgstr "Mover para direita"
 
 #. I18N: Move Actor to top-left direction
-#: engines/access/metaengine.cpp:265 engines/cine/metaengine.cpp:405
+#: engines/access/metaengine.cpp:286 engines/cine/metaengine.cpp:405
 #: engines/efh/metaengine.cpp:314
 msgid "Move up-left"
 msgstr "Mover para cima e esquerda"
 
 #. I18N: Move Actor to top-right direction
-#: engines/access/metaengine.cpp:270 engines/cine/metaengine.cpp:411
+#: engines/access/metaengine.cpp:291 engines/cine/metaengine.cpp:411
 #: engines/efh/metaengine.cpp:320
 msgid "Move up-right"
 msgstr "Mover para cima e direita"
 
 #. I18N: Move Actor to bottom-left direction
-#: engines/access/metaengine.cpp:275 engines/cine/metaengine.cpp:417
+#: engines/access/metaengine.cpp:296 engines/cine/metaengine.cpp:417
 #: engines/efh/metaengine.cpp:326
 msgid "Move down-left"
 msgstr "Mover para baixo e esquerda"
 
 #. I18N: Move Actor to bottom-right direction
-#: engines/access/metaengine.cpp:280 engines/cine/metaengine.cpp:423
+#: engines/access/metaengine.cpp:301 engines/cine/metaengine.cpp:423
 #: engines/efh/metaengine.cpp:332
 msgid "Move down-right"
 msgstr "Mover para baixo e direita"
 
 #. I18N: (Game name: The Lost Files of Sherlock Holmes: The Case of the Rose Tattoo) This action is used to look at an object or npc in the game
-#: engines/access/metaengine.cpp:285 engines/bbvs/metaengine.cpp:124
+#: engines/access/metaengine.cpp:306 engines/bbvs/metaengine.cpp:124
 #: engines/dgds/metaengine.cpp:83 engines/drascula/metaengine.cpp:230
 #: engines/petka/metaengine.cpp:130 engines/scumm/help.cpp:167
 #: engines/scumm/help.cpp:231 engines/sherlock/metaengine.cpp:405
@@ -6716,7 +6724,7 @@ msgstr "Mover para baixo e direita"
 msgid "Look"
 msgstr "Olhar"
 
-#: engines/access/metaengine.cpp:293 engines/drascula/metaengine.cpp:240
+#: engines/access/metaengine.cpp:314 engines/drascula/metaengine.cpp:240
 #: engines/queen/metaengine.cpp:259 engines/saga/metaengine.cpp:369
 #: engines/scumm/help.cpp:125 engines/scumm/help.cpp:140
 #: engines/scumm/help.cpp:165 engines/scumm/help.cpp:193
@@ -6726,13 +6734,13 @@ msgstr "Olhar"
 msgid "Open"
 msgstr "Abrir"
 
-#: engines/access/metaengine.cpp:303 engines/scumm/help.cpp:128
+#: engines/access/metaengine.cpp:324 engines/scumm/help.cpp:128
 #: engines/ultima/nuvie/metaengine.cpp:70
 msgid "Get"
 msgstr "Pegar"
 
 #. I18N: Combat command in Might & Magic 1
-#: engines/access/metaengine.cpp:308 engines/access/metaengine.cpp:329
+#: engines/access/metaengine.cpp:329 engines/access/metaengine.cpp:350
 #: engines/bbvs/metaengine.cpp:136 engines/cine/metaengine.cpp:342
 #: engines/efh/metaengine.cpp:462 engines/hdb/metaengine.cpp:237
 #: engines/lab/metaengine.cpp:323 engines/mm/mm1/metaengine.cpp:86
@@ -6752,11 +6760,11 @@ msgstr "Pegar"
 msgid "Use"
 msgstr "Usar"
 
-#: engines/access/metaengine.cpp:313
+#: engines/access/metaengine.cpp:334
 msgid "Goto"
 msgstr "Ir para"
 
-#: engines/access/metaengine.cpp:318 engines/access/metaengine.cpp:349
+#: engines/access/metaengine.cpp:339 engines/access/metaengine.cpp:370
 #: engines/bbvs/metaengine.cpp:130 engines/drascula/metaengine.cpp:250
 #: engines/efh/metaengine.cpp:506 engines/petka/metaengine.cpp:158
 #: engines/scumm/help.cpp:174 engines/scumm/help.cpp:226
@@ -6767,18 +6775,18 @@ msgstr "Ir para"
 msgid "Talk"
 msgstr "Falar"
 
-#: engines/access/metaengine.cpp:323 engines/scumm/help.cpp:175
+#: engines/access/metaengine.cpp:344 engines/scumm/help.cpp:175
 msgid "Travel"
 msgstr "Viajar"
 
-#: engines/access/metaengine.cpp:334 engines/cine/metaengine.cpp:332
+#: engines/access/metaengine.cpp:355 engines/cine/metaengine.cpp:332
 #: engines/petka/metaengine.cpp:144 engines/saga/metaengine.cpp:394
 #: engines/supernova/metaengine.cpp:331
 msgid "Take"
 msgstr "Pegar"
 
 #. I18N: Action in In Cold Blood
-#: engines/access/metaengine.cpp:339 engines/alcachofa/metaengine.cpp:116
+#: engines/access/metaengine.cpp:360 engines/alcachofa/metaengine.cpp:116
 #: engines/cine/metaengine.cpp:337 engines/crab/input/input.cpp:251
 #: engines/cruise/metaengine.cpp:192 engines/draci/metaengine.cpp:237
 #: engines/dragons/metaengine.cpp:147 engines/drascula/metaengine.cpp:218
@@ -6798,18 +6806,18 @@ msgstr "Pegar"
 msgid "Inventory"
 msgstr "Invent??rio"
 
-#: engines/access/metaengine.cpp:344 engines/ultima/ultima4/metaengine.cpp:51
+#: engines/access/metaengine.cpp:365 engines/ultima/ultima4/metaengine.cpp:51
 msgid "Climb"
 msgstr "Escalar"
 
-#: engines/access/metaengine.cpp:354 engines/bbvs/metaengine.cpp:142
+#: engines/access/metaengine.cpp:375 engines/bbvs/metaengine.cpp:142
 #: engines/freescape/games/castle/castle.cpp:511
 #: engines/petka/metaengine.cpp:137 engines/scumm/help.cpp:225
 #: engines/tsage/metaengine.cpp:201
 msgid "Walk"
 msgstr "Andar"
 
-#: engines/access/metaengine.cpp:365
+#: engines/access/metaengine.cpp:386
 #, fuzzy
 #| msgid "Page Up in save/load menu"
 msgid "Open save/load menu"
diff --git a/po/pt_PT.po b/po/pt_PT.po
index a0ba10f268d..5926e4315b6 100644
--- a/po/pt_PT.po
+++ b/po/pt_PT.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.10.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-17 23:07+0000\n"
+"POT-Creation-Date: 2026-06-18 09:25+0000\n"
 "PO-Revision-Date: 2022-06-05 14:09+0000\n"
 "Last-Translator: Daniel <supsuper at gmail.com>\n"
 "Language-Team: Portuguese (Portugal) <https://translations.scummvm.org/"
@@ -90,7 +90,7 @@ msgstr "Fechar"
 #. I18N: Opens the help screen.
 #: gui/browser.cpp:64 gui/launcher.cpp:277 gui/launcher.cpp:959
 #: backends/platform/sdl/macosx/appmenu_osx.mm:319
-#: engines/access/metaengine.cpp:360 engines/bladerunner/metaengine.cpp:321
+#: engines/access/metaengine.cpp:381 engines/bladerunner/metaengine.cpp:321
 #: engines/ngi/metaengine.cpp:162 engines/supernova/metaengine.cpp:238
 #: engines/tucker/metaengine.cpp:215 engines/zvision/metaengine.cpp:246
 msgid "Help"
@@ -2671,7 +2671,7 @@ msgstr ""
 "E mais..."
 
 #: gui/options.cpp:2975 engines/engine.cpp:647
-#: engines/access/metaengine.cpp:298 engines/drascula/metaengine.cpp:255
+#: engines/access/metaengine.cpp:319 engines/drascula/metaengine.cpp:255
 #: engines/queen/metaengine.cpp:269 engines/sherlock/metaengine.cpp:410
 #: engines/ultima/nuvie/metaengine.cpp:71
 msgid "Move"
@@ -3963,7 +3963,7 @@ msgstr "Menu do jogo"
 
 #. I18N: Skipping cutscene plaback
 #. I18N: Skipping cutscene playback
-#: engines/metaengine.cpp:112 engines/access/metaengine.cpp:225
+#: engines/metaengine.cpp:112 engines/access/metaengine.cpp:246
 #: engines/composer/metaengine.cpp:126 engines/drascula/metaengine.cpp:224
 #: engines/efh/metaengine.cpp:379 engines/freescape/movement.cpp:93
 #: engines/grim/grim.cpp:533 engines/grim/grim.cpp:633
@@ -6043,7 +6043,15 @@ msgstr "Menu principal"
 msgid "Check for Updates..."
 msgstr "Procurar por Atualiza????es..."
 
-#: engines/access/metaengine.cpp:215 engines/alcachofa/metaengine.cpp:94
+#: engines/access/metaengine.cpp:82
+msgid "Use 'high definition' OGG music"
+msgstr ""
+
+#: engines/access/metaengine.cpp:83
+msgid "Use the OGG audio from the re-release instead of original MIDI tracks"
+msgstr ""
+
+#: engines/access/metaengine.cpp:236 engines/alcachofa/metaengine.cpp:94
 #: engines/bbvs/metaengine.cpp:92 engines/buried/metaengine.cpp:133
 #: engines/chamber/metaengine.cpp:59 engines/cine/metaengine.cpp:270
 #: engines/cruise/metaengine.cpp:168 engines/dm/metaengine.cpp:124
@@ -6069,19 +6077,19 @@ msgstr "Procurar por Atualiza????es..."
 msgid "Default keymappings"
 msgstr "Mapeamento padr??o"
 
-#: engines/access/metaengine.cpp:219
+#: engines/access/metaengine.cpp:240
 #, fuzzy
 msgid "Move / Interact / Skip"
 msgstr "Menu de Op????es da DS"
 
-#: engines/access/metaengine.cpp:231 engines/sherlock/metaengine.cpp:483
+#: engines/access/metaengine.cpp:252 engines/sherlock/metaengine.cpp:483
 #, fuzzy
 #| msgid "Skip line"
 msgid "Skip movie"
 msgstr "Ignorar linha"
 
 #. I18N: Move Actor to upwards direction
-#: engines/access/metaengine.cpp:237 engines/asylum/metaengine.cpp:171
+#: engines/access/metaengine.cpp:258 engines/asylum/metaengine.cpp:171
 #: engines/asylum/metaengine.cpp:198 engines/cine/metaengine.cpp:377
 #: engines/efh/metaengine.cpp:286 engines/hdb/metaengine.cpp:213
 #: engines/hypno/metaengine.cpp:363 engines/hypno/metaengine.cpp:411
@@ -6091,7 +6099,7 @@ msgid "Move up"
 msgstr "Mover para cima"
 
 #. I18N: Move Actor to downwards direction
-#: engines/access/metaengine.cpp:244 engines/asylum/metaengine.cpp:177
+#: engines/access/metaengine.cpp:265 engines/asylum/metaengine.cpp:177
 #: engines/asylum/metaengine.cpp:205 engines/cine/metaengine.cpp:384
 #: engines/efh/metaengine.cpp:293 engines/hdb/metaengine.cpp:219
 #: engines/hypno/metaengine.cpp:351 engines/hypno/metaengine.cpp:399
@@ -6102,7 +6110,7 @@ msgstr "Mover para baixo"
 
 #. I18N: Move Actor to left direction
 #. I18N: Move actor left during fight
-#: engines/access/metaengine.cpp:251 engines/asylum/metaengine.cpp:183
+#: engines/access/metaengine.cpp:272 engines/asylum/metaengine.cpp:183
 #: engines/asylum/metaengine.cpp:212 engines/cine/metaengine.cpp:391
 #: engines/efh/metaengine.cpp:300 engines/hdb/metaengine.cpp:225
 #: engines/hypno/metaengine.cpp:345 engines/hypno/metaengine.cpp:393
@@ -6115,7 +6123,7 @@ msgstr "Mover para a esquerda"
 
 #. I18N: Move Actor to right direction
 #. I18N: Move actor right during fight
-#: engines/access/metaengine.cpp:258 engines/asylum/metaengine.cpp:189
+#: engines/access/metaengine.cpp:279 engines/asylum/metaengine.cpp:189
 #: engines/asylum/metaengine.cpp:219 engines/cine/metaengine.cpp:398
 #: engines/efh/metaengine.cpp:307 engines/hdb/metaengine.cpp:231
 #: engines/hypno/metaengine.cpp:357 engines/hypno/metaengine.cpp:405
@@ -6127,7 +6135,7 @@ msgid "Move right"
 msgstr "Mover para a direita"
 
 #. I18N: Move Actor to top-left direction
-#: engines/access/metaengine.cpp:265 engines/cine/metaengine.cpp:405
+#: engines/access/metaengine.cpp:286 engines/cine/metaengine.cpp:405
 #: engines/efh/metaengine.cpp:314
 #, fuzzy
 #| msgid "Move left"
@@ -6135,7 +6143,7 @@ msgid "Move up-left"
 msgstr "Mover para a esquerda"
 
 #. I18N: Move Actor to top-right direction
-#: engines/access/metaengine.cpp:270 engines/cine/metaengine.cpp:411
+#: engines/access/metaengine.cpp:291 engines/cine/metaengine.cpp:411
 #: engines/efh/metaengine.cpp:320
 #, fuzzy
 #| msgid "Move right"
@@ -6143,7 +6151,7 @@ msgid "Move up-right"
 msgstr "Mover para a direita"
 
 #. I18N: Move Actor to bottom-left direction
-#: engines/access/metaengine.cpp:275 engines/cine/metaengine.cpp:417
+#: engines/access/metaengine.cpp:296 engines/cine/metaengine.cpp:417
 #: engines/efh/metaengine.cpp:326
 #, fuzzy
 #| msgid "Move down"
@@ -6151,7 +6159,7 @@ msgid "Move down-left"
 msgstr "Mover para baixo"
 
 #. I18N: Move Actor to bottom-right direction
-#: engines/access/metaengine.cpp:280 engines/cine/metaengine.cpp:423
+#: engines/access/metaengine.cpp:301 engines/cine/metaengine.cpp:423
 #: engines/efh/metaengine.cpp:332
 #, fuzzy
 #| msgid "Move right"
@@ -6159,7 +6167,7 @@ msgid "Move down-right"
 msgstr "Mover para a direita"
 
 #. I18N: (Game name: The Lost Files of Sherlock Holmes: The Case of the Rose Tattoo) This action is used to look at an object or npc in the game
-#: engines/access/metaengine.cpp:285 engines/bbvs/metaengine.cpp:124
+#: engines/access/metaengine.cpp:306 engines/bbvs/metaengine.cpp:124
 #: engines/dgds/metaengine.cpp:83 engines/drascula/metaengine.cpp:230
 #: engines/petka/metaengine.cpp:130 engines/scumm/help.cpp:167
 #: engines/scumm/help.cpp:231 engines/sherlock/metaengine.cpp:405
@@ -6171,7 +6179,7 @@ msgstr "Mover para a direita"
 msgid "Look"
 msgstr "Olhar para Cima"
 
-#: engines/access/metaengine.cpp:293 engines/drascula/metaengine.cpp:240
+#: engines/access/metaengine.cpp:314 engines/drascula/metaengine.cpp:240
 #: engines/queen/metaengine.cpp:259 engines/saga/metaengine.cpp:369
 #: engines/scumm/help.cpp:125 engines/scumm/help.cpp:140
 #: engines/scumm/help.cpp:165 engines/scumm/help.cpp:193
@@ -6182,13 +6190,13 @@ msgstr "Olhar para Cima"
 msgid "Open"
 msgstr "OpenGL"
 
-#: engines/access/metaengine.cpp:303 engines/scumm/help.cpp:128
+#: engines/access/metaengine.cpp:324 engines/scumm/help.cpp:128
 #: engines/ultima/nuvie/metaengine.cpp:70
 msgid "Get"
 msgstr ""
 
 #. I18N: Combat command in Might & Magic 1
-#: engines/access/metaengine.cpp:308 engines/access/metaengine.cpp:329
+#: engines/access/metaengine.cpp:329 engines/access/metaengine.cpp:350
 #: engines/bbvs/metaengine.cpp:136 engines/cine/metaengine.cpp:342
 #: engines/efh/metaengine.cpp:462 engines/hdb/metaengine.cpp:237
 #: engines/lab/metaengine.cpp:323 engines/mm/mm1/metaengine.cpp:86
@@ -6208,11 +6216,11 @@ msgstr ""
 msgid "Use"
 msgstr ""
 
-#: engines/access/metaengine.cpp:313
+#: engines/access/metaengine.cpp:334
 msgid "Goto"
 msgstr ""
 
-#: engines/access/metaengine.cpp:318 engines/access/metaengine.cpp:349
+#: engines/access/metaengine.cpp:339 engines/access/metaengine.cpp:370
 #: engines/bbvs/metaengine.cpp:130 engines/drascula/metaengine.cpp:250
 #: engines/efh/metaengine.cpp:506 engines/petka/metaengine.cpp:158
 #: engines/scumm/help.cpp:174 engines/scumm/help.cpp:226
@@ -6223,18 +6231,18 @@ msgstr ""
 msgid "Talk"
 msgstr ""
 
-#: engines/access/metaengine.cpp:323 engines/scumm/help.cpp:175
+#: engines/access/metaengine.cpp:344 engines/scumm/help.cpp:175
 msgid "Travel"
 msgstr ""
 
-#: engines/access/metaengine.cpp:334 engines/cine/metaengine.cpp:332
+#: engines/access/metaengine.cpp:355 engines/cine/metaengine.cpp:332
 #: engines/petka/metaengine.cpp:144 engines/saga/metaengine.cpp:394
 #: engines/supernova/metaengine.cpp:331
 msgid "Take"
 msgstr ""
 
 #. I18N: Action in In Cold Blood
-#: engines/access/metaengine.cpp:339 engines/alcachofa/metaengine.cpp:116
+#: engines/access/metaengine.cpp:360 engines/alcachofa/metaengine.cpp:116
 #: engines/cine/metaengine.cpp:337 engines/crab/input/input.cpp:251
 #: engines/cruise/metaengine.cpp:192 engines/draci/metaengine.cpp:237
 #: engines/dragons/metaengine.cpp:147 engines/drascula/metaengine.cpp:218
@@ -6255,18 +6263,18 @@ msgstr ""
 msgid "Inventory"
 msgstr "Mostrar invent??rio"
 
-#: engines/access/metaengine.cpp:344 engines/ultima/ultima4/metaengine.cpp:51
+#: engines/access/metaengine.cpp:365 engines/ultima/ultima4/metaengine.cpp:51
 msgid "Climb"
 msgstr ""
 
-#: engines/access/metaengine.cpp:354 engines/bbvs/metaengine.cpp:142
+#: engines/access/metaengine.cpp:375 engines/bbvs/metaengine.cpp:142
 #: engines/freescape/games/castle/castle.cpp:511
 #: engines/petka/metaengine.cpp:137 engines/scumm/help.cpp:225
 #: engines/tsage/metaengine.cpp:201
 msgid "Walk"
 msgstr ""
 
-#: engines/access/metaengine.cpp:365
+#: engines/access/metaengine.cpp:386
 #, fuzzy
 msgid "Open save/load menu"
 msgstr "Iniciar sincroniza????o de jogos guardados"
diff --git a/po/ro.po b/po/ro.po
index a60c21c01ef..7277594d762 100644
--- a/po/ro.po
+++ b/po/ro.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.10.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-17 23:07+0000\n"
+"POT-Creation-Date: 2026-06-18 09:25+0000\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: Automatically generated\n"
 "Language-Team: none\n"
@@ -79,7 +79,7 @@ msgstr ""
 #. I18N: Opens the help screen.
 #: gui/browser.cpp:64 gui/launcher.cpp:277 gui/launcher.cpp:959
 #: backends/platform/sdl/macosx/appmenu_osx.mm:319
-#: engines/access/metaengine.cpp:360 engines/bladerunner/metaengine.cpp:321
+#: engines/access/metaengine.cpp:381 engines/bladerunner/metaengine.cpp:321
 #: engines/ngi/metaengine.cpp:162 engines/supernova/metaengine.cpp:238
 #: engines/tucker/metaengine.cpp:215 engines/zvision/metaengine.cpp:246
 msgid "Help"
@@ -2498,7 +2498,7 @@ msgid ""
 msgstr ""
 
 #: gui/options.cpp:2975 engines/engine.cpp:647
-#: engines/access/metaengine.cpp:298 engines/drascula/metaengine.cpp:255
+#: engines/access/metaengine.cpp:319 engines/drascula/metaengine.cpp:255
 #: engines/queen/metaengine.cpp:269 engines/sherlock/metaengine.cpp:410
 #: engines/ultima/nuvie/metaengine.cpp:71
 msgid "Move"
@@ -3686,7 +3686,7 @@ msgstr ""
 
 #. I18N: Skipping cutscene plaback
 #. I18N: Skipping cutscene playback
-#: engines/metaengine.cpp:112 engines/access/metaengine.cpp:225
+#: engines/metaengine.cpp:112 engines/access/metaengine.cpp:246
 #: engines/composer/metaengine.cpp:126 engines/drascula/metaengine.cpp:224
 #: engines/efh/metaengine.cpp:379 engines/freescape/movement.cpp:93
 #: engines/grim/grim.cpp:533 engines/grim/grim.cpp:633
@@ -5635,7 +5635,15 @@ msgstr ""
 msgid "Check for Updates..."
 msgstr ""
 
-#: engines/access/metaengine.cpp:215 engines/alcachofa/metaengine.cpp:94
+#: engines/access/metaengine.cpp:82
+msgid "Use 'high definition' OGG music"
+msgstr ""
+
+#: engines/access/metaengine.cpp:83
+msgid "Use the OGG audio from the re-release instead of original MIDI tracks"
+msgstr ""
+
+#: engines/access/metaengine.cpp:236 engines/alcachofa/metaengine.cpp:94
 #: engines/bbvs/metaengine.cpp:92 engines/buried/metaengine.cpp:133
 #: engines/chamber/metaengine.cpp:59 engines/cine/metaengine.cpp:270
 #: engines/cruise/metaengine.cpp:168 engines/dm/metaengine.cpp:124
@@ -5659,16 +5667,16 @@ msgstr ""
 msgid "Default keymappings"
 msgstr ""
 
-#: engines/access/metaengine.cpp:219
+#: engines/access/metaengine.cpp:240
 msgid "Move / Interact / Skip"
 msgstr ""
 
-#: engines/access/metaengine.cpp:231 engines/sherlock/metaengine.cpp:483
+#: engines/access/metaengine.cpp:252 engines/sherlock/metaengine.cpp:483
 msgid "Skip movie"
 msgstr ""
 
 #. I18N: Move Actor to upwards direction
-#: engines/access/metaengine.cpp:237 engines/asylum/metaengine.cpp:171
+#: engines/access/metaengine.cpp:258 engines/asylum/metaengine.cpp:171
 #: engines/asylum/metaengine.cpp:198 engines/cine/metaengine.cpp:377
 #: engines/efh/metaengine.cpp:286 engines/hdb/metaengine.cpp:213
 #: engines/hypno/metaengine.cpp:363 engines/hypno/metaengine.cpp:411
@@ -5678,7 +5686,7 @@ msgid "Move up"
 msgstr ""
 
 #. I18N: Move Actor to downwards direction
-#: engines/access/metaengine.cpp:244 engines/asylum/metaengine.cpp:177
+#: engines/access/metaengine.cpp:265 engines/asylum/metaengine.cpp:177
 #: engines/asylum/metaengine.cpp:205 engines/cine/metaengine.cpp:384
 #: engines/efh/metaengine.cpp:293 engines/hdb/metaengine.cpp:219
 #: engines/hypno/metaengine.cpp:351 engines/hypno/metaengine.cpp:399
@@ -5689,7 +5697,7 @@ msgstr ""
 
 #. I18N: Move Actor to left direction
 #. I18N: Move actor left during fight
-#: engines/access/metaengine.cpp:251 engines/asylum/metaengine.cpp:183
+#: engines/access/metaengine.cpp:272 engines/asylum/metaengine.cpp:183
 #: engines/asylum/metaengine.cpp:212 engines/cine/metaengine.cpp:391
 #: engines/efh/metaengine.cpp:300 engines/hdb/metaengine.cpp:225
 #: engines/hypno/metaengine.cpp:345 engines/hypno/metaengine.cpp:393
@@ -5702,7 +5710,7 @@ msgstr ""
 
 #. I18N: Move Actor to right direction
 #. I18N: Move actor right during fight
-#: engines/access/metaengine.cpp:258 engines/asylum/metaengine.cpp:189
+#: engines/access/metaengine.cpp:279 engines/asylum/metaengine.cpp:189
 #: engines/asylum/metaengine.cpp:219 engines/cine/metaengine.cpp:398
 #: engines/efh/metaengine.cpp:307 engines/hdb/metaengine.cpp:231
 #: engines/hypno/metaengine.cpp:357 engines/hypno/metaengine.cpp:405
@@ -5714,31 +5722,31 @@ msgid "Move right"
 msgstr ""
 
 #. I18N: Move Actor to top-left direction
-#: engines/access/metaengine.cpp:265 engines/cine/metaengine.cpp:405
+#: engines/access/metaengine.cpp:286 engines/cine/metaengine.cpp:405
 #: engines/efh/metaengine.cpp:314
 msgid "Move up-left"
 msgstr ""
 
 #. I18N: Move Actor to top-right direction
-#: engines/access/metaengine.cpp:270 engines/cine/metaengine.cpp:411
+#: engines/access/metaengine.cpp:291 engines/cine/metaengine.cpp:411
 #: engines/efh/metaengine.cpp:320
 msgid "Move up-right"
 msgstr ""
 
 #. I18N: Move Actor to bottom-left direction
-#: engines/access/metaengine.cpp:275 engines/cine/metaengine.cpp:417
+#: engines/access/metaengine.cpp:296 engines/cine/metaengine.cpp:417
 #: engines/efh/metaengine.cpp:326
 msgid "Move down-left"
 msgstr ""
 
 #. I18N: Move Actor to bottom-right direction
-#: engines/access/metaengine.cpp:280 engines/cine/metaengine.cpp:423
+#: engines/access/metaengine.cpp:301 engines/cine/metaengine.cpp:423
 #: engines/efh/metaengine.cpp:332
 msgid "Move down-right"
 msgstr ""
 
 #. I18N: (Game name: The Lost Files of Sherlock Holmes: The Case of the Rose Tattoo) This action is used to look at an object or npc in the game
-#: engines/access/metaengine.cpp:285 engines/bbvs/metaengine.cpp:124
+#: engines/access/metaengine.cpp:306 engines/bbvs/metaengine.cpp:124
 #: engines/dgds/metaengine.cpp:83 engines/drascula/metaengine.cpp:230
 #: engines/petka/metaengine.cpp:130 engines/scumm/help.cpp:167
 #: engines/scumm/help.cpp:231 engines/sherlock/metaengine.cpp:405
@@ -5749,7 +5757,7 @@ msgstr ""
 msgid "Look"
 msgstr ""
 
-#: engines/access/metaengine.cpp:293 engines/drascula/metaengine.cpp:240
+#: engines/access/metaengine.cpp:314 engines/drascula/metaengine.cpp:240
 #: engines/queen/metaengine.cpp:259 engines/saga/metaengine.cpp:369
 #: engines/scumm/help.cpp:125 engines/scumm/help.cpp:140
 #: engines/scumm/help.cpp:165 engines/scumm/help.cpp:193
@@ -5759,13 +5767,13 @@ msgstr ""
 msgid "Open"
 msgstr ""
 
-#: engines/access/metaengine.cpp:303 engines/scumm/help.cpp:128
+#: engines/access/metaengine.cpp:324 engines/scumm/help.cpp:128
 #: engines/ultima/nuvie/metaengine.cpp:70
 msgid "Get"
 msgstr ""
 
 #. I18N: Combat command in Might & Magic 1
-#: engines/access/metaengine.cpp:308 engines/access/metaengine.cpp:329
+#: engines/access/metaengine.cpp:329 engines/access/metaengine.cpp:350
 #: engines/bbvs/metaengine.cpp:136 engines/cine/metaengine.cpp:342
 #: engines/efh/metaengine.cpp:462 engines/hdb/metaengine.cpp:237
 #: engines/lab/metaengine.cpp:323 engines/mm/mm1/metaengine.cpp:86
@@ -5785,11 +5793,11 @@ msgstr ""
 msgid "Use"
 msgstr ""
 
-#: engines/access/metaengine.cpp:313
+#: engines/access/metaengine.cpp:334
 msgid "Goto"
 msgstr ""
 
-#: engines/access/metaengine.cpp:318 engines/access/metaengine.cpp:349
+#: engines/access/metaengine.cpp:339 engines/access/metaengine.cpp:370
 #: engines/bbvs/metaengine.cpp:130 engines/drascula/metaengine.cpp:250
 #: engines/efh/metaengine.cpp:506 engines/petka/metaengine.cpp:158
 #: engines/scumm/help.cpp:174 engines/scumm/help.cpp:226
@@ -5800,18 +5808,18 @@ msgstr ""
 msgid "Talk"
 msgstr ""
 
-#: engines/access/metaengine.cpp:323 engines/scumm/help.cpp:175
+#: engines/access/metaengine.cpp:344 engines/scumm/help.cpp:175
 msgid "Travel"
 msgstr ""
 
-#: engines/access/metaengine.cpp:334 engines/cine/metaengine.cpp:332
+#: engines/access/metaengine.cpp:355 engines/cine/metaengine.cpp:332
 #: engines/petka/metaengine.cpp:144 engines/saga/metaengine.cpp:394
 #: engines/supernova/metaengine.cpp:331
 msgid "Take"
 msgstr ""
 
 #. I18N: Action in In Cold Blood
-#: engines/access/metaengine.cpp:339 engines/alcachofa/metaengine.cpp:116
+#: engines/access/metaengine.cpp:360 engines/alcachofa/metaengine.cpp:116
 #: engines/cine/metaengine.cpp:337 engines/crab/input/input.cpp:251
 #: engines/cruise/metaengine.cpp:192 engines/draci/metaengine.cpp:237
 #: engines/dragons/metaengine.cpp:147 engines/drascula/metaengine.cpp:218
@@ -5831,18 +5839,18 @@ msgstr ""
 msgid "Inventory"
 msgstr ""
 
-#: engines/access/metaengine.cpp:344 engines/ultima/ultima4/metaengine.cpp:51
+#: engines/access/metaengine.cpp:365 engines/ultima/ultima4/metaengine.cpp:51
 msgid "Climb"
 msgstr ""
 
-#: engines/access/metaengine.cpp:354 engines/bbvs/metaengine.cpp:142
+#: engines/access/metaengine.cpp:375 engines/bbvs/metaengine.cpp:142
 #: engines/freescape/games/castle/castle.cpp:511
 #: engines/petka/metaengine.cpp:137 engines/scumm/help.cpp:225
 #: engines/tsage/metaengine.cpp:201
 msgid "Walk"
 msgstr ""
 
-#: engines/access/metaengine.cpp:365
+#: engines/access/metaengine.cpp:386
 msgid "Open save/load menu"
 msgstr ""
 
diff --git a/po/ru_RU.po b/po/ru_RU.po
index 5a4aff34575..4846db60c88 100644
--- a/po/ru_RU.po
+++ b/po/ru_RU.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.8.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-17 23:07+0000\n"
+"POT-Creation-Date: 2026-06-18 09:25+0000\n"
 "PO-Revision-Date: 2026-06-17 23:04+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Russian <https://translations.scummvm.org/projects/scummvm/"
@@ -83,7 +83,7 @@ msgstr "??????????????"
 #. I18N: Opens the help screen.
 #: gui/browser.cpp:64 gui/launcher.cpp:277 gui/launcher.cpp:959
 #: backends/platform/sdl/macosx/appmenu_osx.mm:319
-#: engines/access/metaengine.cpp:360 engines/bladerunner/metaengine.cpp:321
+#: engines/access/metaengine.cpp:381 engines/bladerunner/metaengine.cpp:321
 #: engines/ngi/metaengine.cpp:162 engines/supernova/metaengine.cpp:238
 #: engines/tucker/metaengine.cpp:215 engines/zvision/metaengine.cpp:246
 msgid "Help"
@@ -2687,7 +2687,7 @@ msgstr ""
 "??????????..."
 
 #: gui/options.cpp:2975 engines/engine.cpp:647
-#: engines/access/metaengine.cpp:298 engines/drascula/metaengine.cpp:255
+#: engines/access/metaengine.cpp:319 engines/drascula/metaengine.cpp:255
 #: engines/queen/metaengine.cpp:269 engines/sherlock/metaengine.cpp:410
 #: engines/ultima/nuvie/metaengine.cpp:71
 msgid "Move"
@@ -3999,7 +3999,7 @@ msgstr "???????? ????????"
 
 #. I18N: Skipping cutscene plaback
 #. I18N: Skipping cutscene playback
-#: engines/metaengine.cpp:112 engines/access/metaengine.cpp:225
+#: engines/metaengine.cpp:112 engines/access/metaengine.cpp:246
 #: engines/composer/metaengine.cpp:126 engines/drascula/metaengine.cpp:224
 #: engines/efh/metaengine.cpp:379 engines/freescape/movement.cpp:93
 #: engines/grim/grim.cpp:533 engines/grim/grim.cpp:633
@@ -6315,7 +6315,15 @@ msgstr "?????????????? ????????"
 msgid "Check for Updates..."
 msgstr "?????????????????? ????????????????????..."
 
-#: engines/access/metaengine.cpp:215 engines/alcachofa/metaengine.cpp:94
+#: engines/access/metaengine.cpp:82
+msgid "Use 'high definition' OGG music"
+msgstr ""
+
+#: engines/access/metaengine.cpp:83
+msgid "Use the OGG audio from the re-release instead of original MIDI tracks"
+msgstr ""
+
+#: engines/access/metaengine.cpp:236 engines/alcachofa/metaengine.cpp:94
 #: engines/bbvs/metaengine.cpp:92 engines/buried/metaengine.cpp:133
 #: engines/chamber/metaengine.cpp:59 engines/cine/metaengine.cpp:270
 #: engines/cruise/metaengine.cpp:168 engines/dm/metaengine.cpp:124
@@ -6339,16 +6347,16 @@ msgstr "?????????????????? ????????????????????..."
 msgid "Default keymappings"
 msgstr "?????????????????? ???????????? ???? ??????????????????"
 
-#: engines/access/metaengine.cpp:219
+#: engines/access/metaengine.cpp:240
 msgid "Move / Interact / Skip"
 msgstr "?????????????????????? / ?????????????????????????????????? / ????????????????????"
 
-#: engines/access/metaengine.cpp:231 engines/sherlock/metaengine.cpp:483
+#: engines/access/metaengine.cpp:252 engines/sherlock/metaengine.cpp:483
 msgid "Skip movie"
 msgstr "???????????????????? ??????????"
 
 #. I18N: Move Actor to upwards direction
-#: engines/access/metaengine.cpp:237 engines/asylum/metaengine.cpp:171
+#: engines/access/metaengine.cpp:258 engines/asylum/metaengine.cpp:171
 #: engines/asylum/metaengine.cpp:198 engines/cine/metaengine.cpp:377
 #: engines/efh/metaengine.cpp:286 engines/hdb/metaengine.cpp:213
 #: engines/hypno/metaengine.cpp:363 engines/hypno/metaengine.cpp:411
@@ -6358,7 +6366,7 @@ msgid "Move up"
 msgstr "???????? ??????????"
 
 #. I18N: Move Actor to downwards direction
-#: engines/access/metaengine.cpp:244 engines/asylum/metaengine.cpp:177
+#: engines/access/metaengine.cpp:265 engines/asylum/metaengine.cpp:177
 #: engines/asylum/metaengine.cpp:205 engines/cine/metaengine.cpp:384
 #: engines/efh/metaengine.cpp:293 engines/hdb/metaengine.cpp:219
 #: engines/hypno/metaengine.cpp:351 engines/hypno/metaengine.cpp:399
@@ -6369,7 +6377,7 @@ msgstr "???????? ????????"
 
 #. I18N: Move Actor to left direction
 #. I18N: Move actor left during fight
-#: engines/access/metaengine.cpp:251 engines/asylum/metaengine.cpp:183
+#: engines/access/metaengine.cpp:272 engines/asylum/metaengine.cpp:183
 #: engines/asylum/metaengine.cpp:212 engines/cine/metaengine.cpp:391
 #: engines/efh/metaengine.cpp:300 engines/hdb/metaengine.cpp:225
 #: engines/hypno/metaengine.cpp:345 engines/hypno/metaengine.cpp:393
@@ -6382,7 +6390,7 @@ msgstr "???????? ??????????"
 
 #. I18N: Move Actor to right direction
 #. I18N: Move actor right during fight
-#: engines/access/metaengine.cpp:258 engines/asylum/metaengine.cpp:189
+#: engines/access/metaengine.cpp:279 engines/asylum/metaengine.cpp:189
 #: engines/asylum/metaengine.cpp:219 engines/cine/metaengine.cpp:398
 #: engines/efh/metaengine.cpp:307 engines/hdb/metaengine.cpp:231
 #: engines/hypno/metaengine.cpp:357 engines/hypno/metaengine.cpp:405
@@ -6394,31 +6402,31 @@ msgid "Move right"
 msgstr "???????? ????????????"
 
 #. I18N: Move Actor to top-left direction
-#: engines/access/metaengine.cpp:265 engines/cine/metaengine.cpp:405
+#: engines/access/metaengine.cpp:286 engines/cine/metaengine.cpp:405
 #: engines/efh/metaengine.cpp:314
 msgid "Move up-left"
 msgstr "?????????????????????????? ??????????-??????????"
 
 #. I18N: Move Actor to top-right direction
-#: engines/access/metaengine.cpp:270 engines/cine/metaengine.cpp:411
+#: engines/access/metaengine.cpp:291 engines/cine/metaengine.cpp:411
 #: engines/efh/metaengine.cpp:320
 msgid "Move up-right"
 msgstr "?????????????????????????? ??????????-????????????"
 
 #. I18N: Move Actor to bottom-left direction
-#: engines/access/metaengine.cpp:275 engines/cine/metaengine.cpp:417
+#: engines/access/metaengine.cpp:296 engines/cine/metaengine.cpp:417
 #: engines/efh/metaengine.cpp:326
 msgid "Move down-left"
 msgstr "?????????????????????????? ????????-??????????"
 
 #. I18N: Move Actor to bottom-right direction
-#: engines/access/metaengine.cpp:280 engines/cine/metaengine.cpp:423
+#: engines/access/metaengine.cpp:301 engines/cine/metaengine.cpp:423
 #: engines/efh/metaengine.cpp:332
 msgid "Move down-right"
 msgstr "?????????????????????????? ????????-????????????"
 
 #. I18N: (Game name: The Lost Files of Sherlock Holmes: The Case of the Rose Tattoo) This action is used to look at an object or npc in the game
-#: engines/access/metaengine.cpp:285 engines/bbvs/metaengine.cpp:124
+#: engines/access/metaengine.cpp:306 engines/bbvs/metaengine.cpp:124
 #: engines/dgds/metaengine.cpp:83 engines/drascula/metaengine.cpp:230
 #: engines/petka/metaengine.cpp:130 engines/scumm/help.cpp:167
 #: engines/scumm/help.cpp:231 engines/sherlock/metaengine.cpp:405
@@ -6429,7 +6437,7 @@ msgstr "?????????????????????????? ????????-????????????"
 msgid "Look"
 msgstr "????????????????"
 
-#: engines/access/metaengine.cpp:293 engines/drascula/metaengine.cpp:240
+#: engines/access/metaengine.cpp:314 engines/drascula/metaengine.cpp:240
 #: engines/queen/metaengine.cpp:259 engines/saga/metaengine.cpp:369
 #: engines/scumm/help.cpp:125 engines/scumm/help.cpp:140
 #: engines/scumm/help.cpp:165 engines/scumm/help.cpp:193
@@ -6439,13 +6447,13 @@ msgstr "????????????????"
 msgid "Open"
 msgstr "??????????????"
 
-#: engines/access/metaengine.cpp:303 engines/scumm/help.cpp:128
+#: engines/access/metaengine.cpp:324 engines/scumm/help.cpp:128
 #: engines/ultima/nuvie/metaengine.cpp:70
 msgid "Get"
 msgstr "??????????"
 
 #. I18N: Combat command in Might & Magic 1
-#: engines/access/metaengine.cpp:308 engines/access/metaengine.cpp:329
+#: engines/access/metaengine.cpp:329 engines/access/metaengine.cpp:350
 #: engines/bbvs/metaengine.cpp:136 engines/cine/metaengine.cpp:342
 #: engines/efh/metaengine.cpp:462 engines/hdb/metaengine.cpp:237
 #: engines/lab/metaengine.cpp:323 engines/mm/mm1/metaengine.cpp:86
@@ -6465,11 +6473,11 @@ msgstr "??????????"
 msgid "Use"
 msgstr "????????????????????????"
 
-#: engines/access/metaengine.cpp:313
+#: engines/access/metaengine.cpp:334
 msgid "Goto"
 msgstr "??????????????"
 
-#: engines/access/metaengine.cpp:318 engines/access/metaengine.cpp:349
+#: engines/access/metaengine.cpp:339 engines/access/metaengine.cpp:370
 #: engines/bbvs/metaengine.cpp:130 engines/drascula/metaengine.cpp:250
 #: engines/efh/metaengine.cpp:506 engines/petka/metaengine.cpp:158
 #: engines/scumm/help.cpp:174 engines/scumm/help.cpp:226
@@ -6480,18 +6488,18 @@ msgstr "??????????????"
 msgid "Talk"
 msgstr "????????????????"
 
-#: engines/access/metaengine.cpp:323 engines/scumm/help.cpp:175
+#: engines/access/metaengine.cpp:344 engines/scumm/help.cpp:175
 msgid "Travel"
 msgstr "????????????????????????????"
 
-#: engines/access/metaengine.cpp:334 engines/cine/metaengine.cpp:332
+#: engines/access/metaengine.cpp:355 engines/cine/metaengine.cpp:332
 #: engines/petka/metaengine.cpp:144 engines/saga/metaengine.cpp:394
 #: engines/supernova/metaengine.cpp:331
 msgid "Take"
 msgstr "??????????????"
 
 #. I18N: Action in In Cold Blood
-#: engines/access/metaengine.cpp:339 engines/alcachofa/metaengine.cpp:116
+#: engines/access/metaengine.cpp:360 engines/alcachofa/metaengine.cpp:116
 #: engines/cine/metaengine.cpp:337 engines/crab/input/input.cpp:251
 #: engines/cruise/metaengine.cpp:192 engines/draci/metaengine.cpp:237
 #: engines/dragons/metaengine.cpp:147 engines/drascula/metaengine.cpp:218
@@ -6511,18 +6519,18 @@ msgstr "??????????????"
 msgid "Inventory"
 msgstr "??????????????????"
 
-#: engines/access/metaengine.cpp:344 engines/ultima/ultima4/metaengine.cpp:51
+#: engines/access/metaengine.cpp:365 engines/ultima/ultima4/metaengine.cpp:51
 msgid "Climb"
 msgstr "??????????????????????"
 
-#: engines/access/metaengine.cpp:354 engines/bbvs/metaengine.cpp:142
+#: engines/access/metaengine.cpp:375 engines/bbvs/metaengine.cpp:142
 #: engines/freescape/games/castle/castle.cpp:511
 #: engines/petka/metaengine.cpp:137 engines/scumm/help.cpp:225
 #: engines/tsage/metaengine.cpp:201
 msgid "Walk"
 msgstr "????????"
 
-#: engines/access/metaengine.cpp:365
+#: engines/access/metaengine.cpp:386
 msgid "Open save/load menu"
 msgstr "?????????????? ???????? ????????????????????/????????????????"
 
diff --git a/po/sv_SE.po b/po/sv_SE.po
index 79506ccf12f..78cf32139ed 100644
--- a/po/sv_SE.po
+++ b/po/sv_SE.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.5.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-17 23:07+0000\n"
+"POT-Creation-Date: 2026-06-18 09:25+0000\n"
 "PO-Revision-Date: 2026-04-19 13:22+0000\n"
 "Last-Translator: Daniel Nylander <po at danielnylander.se>\n"
 "Language-Team: Swedish <https://translations.scummvm.org/projects/scummvm/"
@@ -84,7 +84,7 @@ msgstr "St??ng"
 #. I18N: Opens the help screen.
 #: gui/browser.cpp:64 gui/launcher.cpp:277 gui/launcher.cpp:959
 #: backends/platform/sdl/macosx/appmenu_osx.mm:319
-#: engines/access/metaengine.cpp:360 engines/bladerunner/metaengine.cpp:321
+#: engines/access/metaengine.cpp:381 engines/bladerunner/metaengine.cpp:321
 #: engines/ngi/metaengine.cpp:162 engines/supernova/metaengine.cpp:238
 #: engines/tucker/metaengine.cpp:215 engines/zvision/metaengine.cpp:246
 msgid "Help"
@@ -2685,7 +2685,7 @@ msgstr ""
 "Och mera..."
 
 #: gui/options.cpp:2975 engines/engine.cpp:647
-#: engines/access/metaengine.cpp:298 engines/drascula/metaengine.cpp:255
+#: engines/access/metaengine.cpp:319 engines/drascula/metaengine.cpp:255
 #: engines/queen/metaengine.cpp:269 engines/sherlock/metaengine.cpp:410
 #: engines/ultima/nuvie/metaengine.cpp:71
 msgid "Move"
@@ -3992,7 +3992,7 @@ msgstr "Spelmeny"
 
 #. I18N: Skipping cutscene plaback
 #. I18N: Skipping cutscene playback
-#: engines/metaengine.cpp:112 engines/access/metaengine.cpp:225
+#: engines/metaengine.cpp:112 engines/access/metaengine.cpp:246
 #: engines/composer/metaengine.cpp:126 engines/drascula/metaengine.cpp:224
 #: engines/efh/metaengine.cpp:379 engines/freescape/movement.cpp:93
 #: engines/grim/grim.cpp:533 engines/grim/grim.cpp:633
@@ -6295,7 +6295,15 @@ msgstr "Spelstartaren"
 msgid "Check for Updates..."
 msgstr "Leta efter uppdateringar..."
 
-#: engines/access/metaengine.cpp:215 engines/alcachofa/metaengine.cpp:94
+#: engines/access/metaengine.cpp:82
+msgid "Use 'high definition' OGG music"
+msgstr ""
+
+#: engines/access/metaengine.cpp:83
+msgid "Use the OGG audio from the re-release instead of original MIDI tracks"
+msgstr ""
+
+#: engines/access/metaengine.cpp:236 engines/alcachofa/metaengine.cpp:94
 #: engines/bbvs/metaengine.cpp:92 engines/buried/metaengine.cpp:133
 #: engines/chamber/metaengine.cpp:59 engines/cine/metaengine.cpp:270
 #: engines/cruise/metaengine.cpp:168 engines/dm/metaengine.cpp:124
@@ -6319,16 +6327,16 @@ msgstr "Leta efter uppdateringar..."
 msgid "Default keymappings"
 msgstr "Standardkortkommandon"
 
-#: engines/access/metaengine.cpp:219
+#: engines/access/metaengine.cpp:240
 msgid "Move / Interact / Skip"
 msgstr "Flytta / Interagera / Hoppa ??ver"
 
-#: engines/access/metaengine.cpp:231 engines/sherlock/metaengine.cpp:483
+#: engines/access/metaengine.cpp:252 engines/sherlock/metaengine.cpp:483
 msgid "Skip movie"
 msgstr "Hoppa ??ver film"
 
 #. I18N: Move Actor to upwards direction
-#: engines/access/metaengine.cpp:237 engines/asylum/metaengine.cpp:171
+#: engines/access/metaengine.cpp:258 engines/asylum/metaengine.cpp:171
 #: engines/asylum/metaengine.cpp:198 engines/cine/metaengine.cpp:377
 #: engines/efh/metaengine.cpp:286 engines/hdb/metaengine.cpp:213
 #: engines/hypno/metaengine.cpp:363 engines/hypno/metaengine.cpp:411
@@ -6338,7 +6346,7 @@ msgid "Move up"
 msgstr "Flytta upp??t"
 
 #. I18N: Move Actor to downwards direction
-#: engines/access/metaengine.cpp:244 engines/asylum/metaengine.cpp:177
+#: engines/access/metaengine.cpp:265 engines/asylum/metaengine.cpp:177
 #: engines/asylum/metaengine.cpp:205 engines/cine/metaengine.cpp:384
 #: engines/efh/metaengine.cpp:293 engines/hdb/metaengine.cpp:219
 #: engines/hypno/metaengine.cpp:351 engines/hypno/metaengine.cpp:399
@@ -6349,7 +6357,7 @@ msgstr "Flytta ner??t"
 
 #. I18N: Move Actor to left direction
 #. I18N: Move actor left during fight
-#: engines/access/metaengine.cpp:251 engines/asylum/metaengine.cpp:183
+#: engines/access/metaengine.cpp:272 engines/asylum/metaengine.cpp:183
 #: engines/asylum/metaengine.cpp:212 engines/cine/metaengine.cpp:391
 #: engines/efh/metaengine.cpp:300 engines/hdb/metaengine.cpp:225
 #: engines/hypno/metaengine.cpp:345 engines/hypno/metaengine.cpp:393
@@ -6362,7 +6370,7 @@ msgstr "Flytta ??t v??nster"
 
 #. I18N: Move Actor to right direction
 #. I18N: Move actor right during fight
-#: engines/access/metaengine.cpp:258 engines/asylum/metaengine.cpp:189
+#: engines/access/metaengine.cpp:279 engines/asylum/metaengine.cpp:189
 #: engines/asylum/metaengine.cpp:219 engines/cine/metaengine.cpp:398
 #: engines/efh/metaengine.cpp:307 engines/hdb/metaengine.cpp:231
 #: engines/hypno/metaengine.cpp:357 engines/hypno/metaengine.cpp:405
@@ -6374,31 +6382,31 @@ msgid "Move right"
 msgstr "Flytta ??t h??ger"
 
 #. I18N: Move Actor to top-left direction
-#: engines/access/metaengine.cpp:265 engines/cine/metaengine.cpp:405
+#: engines/access/metaengine.cpp:286 engines/cine/metaengine.cpp:405
 #: engines/efh/metaengine.cpp:314
 msgid "Move up-left"
 msgstr "Flytta upp till v??nster"
 
 #. I18N: Move Actor to top-right direction
-#: engines/access/metaengine.cpp:270 engines/cine/metaengine.cpp:411
+#: engines/access/metaengine.cpp:291 engines/cine/metaengine.cpp:411
 #: engines/efh/metaengine.cpp:320
 msgid "Move up-right"
 msgstr "Flytta upp till h??ger"
 
 #. I18N: Move Actor to bottom-left direction
-#: engines/access/metaengine.cpp:275 engines/cine/metaengine.cpp:417
+#: engines/access/metaengine.cpp:296 engines/cine/metaengine.cpp:417
 #: engines/efh/metaengine.cpp:326
 msgid "Move down-left"
 msgstr "Flytta ner till v??nster"
 
 #. I18N: Move Actor to bottom-right direction
-#: engines/access/metaengine.cpp:280 engines/cine/metaengine.cpp:423
+#: engines/access/metaengine.cpp:301 engines/cine/metaengine.cpp:423
 #: engines/efh/metaengine.cpp:332
 msgid "Move down-right"
 msgstr "Flytta ner till h??ger"
 
 #. I18N: (Game name: The Lost Files of Sherlock Holmes: The Case of the Rose Tattoo) This action is used to look at an object or npc in the game
-#: engines/access/metaengine.cpp:285 engines/bbvs/metaengine.cpp:124
+#: engines/access/metaengine.cpp:306 engines/bbvs/metaengine.cpp:124
 #: engines/dgds/metaengine.cpp:83 engines/drascula/metaengine.cpp:230
 #: engines/petka/metaengine.cpp:130 engines/scumm/help.cpp:167
 #: engines/scumm/help.cpp:231 engines/sherlock/metaengine.cpp:405
@@ -6409,7 +6417,7 @@ msgstr "Flytta ner till h??ger"
 msgid "Look"
 msgstr "Titta"
 
-#: engines/access/metaengine.cpp:293 engines/drascula/metaengine.cpp:240
+#: engines/access/metaengine.cpp:314 engines/drascula/metaengine.cpp:240
 #: engines/queen/metaengine.cpp:259 engines/saga/metaengine.cpp:369
 #: engines/scumm/help.cpp:125 engines/scumm/help.cpp:140
 #: engines/scumm/help.cpp:165 engines/scumm/help.cpp:193
@@ -6419,13 +6427,13 @@ msgstr "Titta"
 msgid "Open"
 msgstr "??ppna"
 
-#: engines/access/metaengine.cpp:303 engines/scumm/help.cpp:128
+#: engines/access/metaengine.cpp:324 engines/scumm/help.cpp:128
 #: engines/ultima/nuvie/metaengine.cpp:70
 msgid "Get"
 msgstr "Ta emot"
 
 #. I18N: Combat command in Might & Magic 1
-#: engines/access/metaengine.cpp:308 engines/access/metaengine.cpp:329
+#: engines/access/metaengine.cpp:329 engines/access/metaengine.cpp:350
 #: engines/bbvs/metaengine.cpp:136 engines/cine/metaengine.cpp:342
 #: engines/efh/metaengine.cpp:462 engines/hdb/metaengine.cpp:237
 #: engines/lab/metaengine.cpp:323 engines/mm/mm1/metaengine.cpp:86
@@ -6445,11 +6453,11 @@ msgstr "Ta emot"
 msgid "Use"
 msgstr "Anv??nd"
 
-#: engines/access/metaengine.cpp:313
+#: engines/access/metaengine.cpp:334
 msgid "Goto"
 msgstr "G?? till"
 
-#: engines/access/metaengine.cpp:318 engines/access/metaengine.cpp:349
+#: engines/access/metaengine.cpp:339 engines/access/metaengine.cpp:370
 #: engines/bbvs/metaengine.cpp:130 engines/drascula/metaengine.cpp:250
 #: engines/efh/metaengine.cpp:506 engines/petka/metaengine.cpp:158
 #: engines/scumm/help.cpp:174 engines/scumm/help.cpp:226
@@ -6460,18 +6468,18 @@ msgstr "G?? till"
 msgid "Talk"
 msgstr "Tala"
 
-#: engines/access/metaengine.cpp:323 engines/scumm/help.cpp:175
+#: engines/access/metaengine.cpp:344 engines/scumm/help.cpp:175
 msgid "Travel"
 msgstr "Res"
 
-#: engines/access/metaengine.cpp:334 engines/cine/metaengine.cpp:332
+#: engines/access/metaengine.cpp:355 engines/cine/metaengine.cpp:332
 #: engines/petka/metaengine.cpp:144 engines/saga/metaengine.cpp:394
 #: engines/supernova/metaengine.cpp:331
 msgid "Take"
 msgstr "Ta"
 
 #. I18N: Action in In Cold Blood
-#: engines/access/metaengine.cpp:339 engines/alcachofa/metaengine.cpp:116
+#: engines/access/metaengine.cpp:360 engines/alcachofa/metaengine.cpp:116
 #: engines/cine/metaengine.cpp:337 engines/crab/input/input.cpp:251
 #: engines/cruise/metaengine.cpp:192 engines/draci/metaengine.cpp:237
 #: engines/dragons/metaengine.cpp:147 engines/drascula/metaengine.cpp:218
@@ -6491,18 +6499,18 @@ msgstr "Ta"
 msgid "Inventory"
 msgstr "Inventarie"
 
-#: engines/access/metaengine.cpp:344 engines/ultima/ultima4/metaengine.cpp:51
+#: engines/access/metaengine.cpp:365 engines/ultima/ultima4/metaengine.cpp:51
 msgid "Climb"
 msgstr "Kl??ttra"
 
-#: engines/access/metaengine.cpp:354 engines/bbvs/metaengine.cpp:142
+#: engines/access/metaengine.cpp:375 engines/bbvs/metaengine.cpp:142
 #: engines/freescape/games/castle/castle.cpp:511
 #: engines/petka/metaengine.cpp:137 engines/scumm/help.cpp:225
 #: engines/tsage/metaengine.cpp:201
 msgid "Walk"
 msgstr "G??"
 
-#: engines/access/metaengine.cpp:365
+#: engines/access/metaengine.cpp:386
 msgid "Open save/load menu"
 msgstr "??ppna menyn f??r spara/l??s in"
 
diff --git a/po/tr.po b/po/tr.po
index 2bed0099aa2..c677b35f502 100644
--- a/po/tr.po
+++ b/po/tr.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-17 23:07+0000\n"
+"POT-Creation-Date: 2026-06-18 09:25+0000\n"
 "PO-Revision-Date: 2024-10-10 12:33+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Turkish <https://translations.scummvm.org/projects/scummvm/"
@@ -86,7 +86,7 @@ msgstr "Kapat"
 #. I18N: Opens the help screen.
 #: gui/browser.cpp:64 gui/launcher.cpp:277 gui/launcher.cpp:959
 #: backends/platform/sdl/macosx/appmenu_osx.mm:319
-#: engines/access/metaengine.cpp:360 engines/bladerunner/metaengine.cpp:321
+#: engines/access/metaengine.cpp:381 engines/bladerunner/metaengine.cpp:321
 #: engines/ngi/metaengine.cpp:162 engines/supernova/metaengine.cpp:238
 #: engines/tucker/metaengine.cpp:215 engines/zvision/metaengine.cpp:246
 msgid "Help"
@@ -2652,7 +2652,7 @@ msgid ""
 msgstr "~O~yun Ekle..."
 
 #: gui/options.cpp:2975 engines/engine.cpp:647
-#: engines/access/metaengine.cpp:298 engines/drascula/metaengine.cpp:255
+#: engines/access/metaengine.cpp:319 engines/drascula/metaengine.cpp:255
 #: engines/queen/metaengine.cpp:269 engines/sherlock/metaengine.cpp:410
 #: engines/ultima/nuvie/metaengine.cpp:71
 #, fuzzy
@@ -3935,7 +3935,7 @@ msgstr "Oyun"
 
 #. I18N: Skipping cutscene plaback
 #. I18N: Skipping cutscene playback
-#: engines/metaengine.cpp:112 engines/access/metaengine.cpp:225
+#: engines/metaengine.cpp:112 engines/access/metaengine.cpp:246
 #: engines/composer/metaengine.cpp:126 engines/drascula/metaengine.cpp:224
 #: engines/efh/metaengine.cpp:379 engines/freescape/movement.cpp:93
 #: engines/grim/grim.cpp:533 engines/grim/grim.cpp:633
@@ -5965,7 +5965,15 @@ msgstr ""
 msgid "Check for Updates..."
 msgstr ""
 
-#: engines/access/metaengine.cpp:215 engines/alcachofa/metaengine.cpp:94
+#: engines/access/metaengine.cpp:82
+msgid "Use 'high definition' OGG music"
+msgstr ""
+
+#: engines/access/metaengine.cpp:83
+msgid "Use the OGG audio from the re-release instead of original MIDI tracks"
+msgstr ""
+
+#: engines/access/metaengine.cpp:236 engines/alcachofa/metaengine.cpp:94
 #: engines/bbvs/metaengine.cpp:92 engines/buried/metaengine.cpp:133
 #: engines/chamber/metaengine.cpp:59 engines/cine/metaengine.cpp:270
 #: engines/cruise/metaengine.cpp:168 engines/dm/metaengine.cpp:124
@@ -5990,19 +5998,19 @@ msgstr ""
 msgid "Default keymappings"
 msgstr "A~y~arlar..."
 
-#: engines/access/metaengine.cpp:219
+#: engines/access/metaengine.cpp:240
 #, fuzzy
 #| msgid "Show hidden files"
 msgid "Move / Interact / Skip"
 msgstr "Gizli dosyalar?? g??ster"
 
-#: engines/access/metaengine.cpp:231 engines/sherlock/metaengine.cpp:483
+#: engines/access/metaengine.cpp:252 engines/sherlock/metaengine.cpp:483
 #, fuzzy
 msgid "Skip movie"
 msgstr "??ptal"
 
 #. I18N: Move Actor to upwards direction
-#: engines/access/metaengine.cpp:237 engines/asylum/metaengine.cpp:171
+#: engines/access/metaengine.cpp:258 engines/asylum/metaengine.cpp:171
 #: engines/asylum/metaengine.cpp:198 engines/cine/metaengine.cpp:377
 #: engines/efh/metaengine.cpp:286 engines/hdb/metaengine.cpp:213
 #: engines/hypno/metaengine.cpp:363 engines/hypno/metaengine.cpp:411
@@ -6012,7 +6020,7 @@ msgid "Move up"
 msgstr ""
 
 #. I18N: Move Actor to downwards direction
-#: engines/access/metaengine.cpp:244 engines/asylum/metaengine.cpp:177
+#: engines/access/metaengine.cpp:265 engines/asylum/metaengine.cpp:177
 #: engines/asylum/metaengine.cpp:205 engines/cine/metaengine.cpp:384
 #: engines/efh/metaengine.cpp:293 engines/hdb/metaengine.cpp:219
 #: engines/hypno/metaengine.cpp:351 engines/hypno/metaengine.cpp:399
@@ -6023,7 +6031,7 @@ msgstr ""
 
 #. I18N: Move Actor to left direction
 #. I18N: Move actor left during fight
-#: engines/access/metaengine.cpp:251 engines/asylum/metaengine.cpp:183
+#: engines/access/metaengine.cpp:272 engines/asylum/metaengine.cpp:183
 #: engines/asylum/metaengine.cpp:212 engines/cine/metaengine.cpp:391
 #: engines/efh/metaengine.cpp:300 engines/hdb/metaengine.cpp:225
 #: engines/hypno/metaengine.cpp:345 engines/hypno/metaengine.cpp:393
@@ -6036,7 +6044,7 @@ msgstr ""
 
 #. I18N: Move Actor to right direction
 #. I18N: Move actor right during fight
-#: engines/access/metaengine.cpp:258 engines/asylum/metaengine.cpp:189
+#: engines/access/metaengine.cpp:279 engines/asylum/metaengine.cpp:189
 #: engines/asylum/metaengine.cpp:219 engines/cine/metaengine.cpp:398
 #: engines/efh/metaengine.cpp:307 engines/hdb/metaengine.cpp:231
 #: engines/hypno/metaengine.cpp:357 engines/hypno/metaengine.cpp:405
@@ -6048,35 +6056,35 @@ msgid "Move right"
 msgstr ""
 
 #. I18N: Move Actor to top-left direction
-#: engines/access/metaengine.cpp:265 engines/cine/metaengine.cpp:405
+#: engines/access/metaengine.cpp:286 engines/cine/metaengine.cpp:405
 #: engines/efh/metaengine.cpp:314
 #, fuzzy
 msgid "Move up-left"
 msgstr "Sol"
 
 #. I18N: Move Actor to top-right direction
-#: engines/access/metaengine.cpp:270 engines/cine/metaengine.cpp:411
+#: engines/access/metaengine.cpp:291 engines/cine/metaengine.cpp:411
 #: engines/efh/metaengine.cpp:320
 #, fuzzy
 msgid "Move up-right"
 msgstr "Sa??"
 
 #. I18N: Move Actor to bottom-left direction
-#: engines/access/metaengine.cpp:275 engines/cine/metaengine.cpp:417
+#: engines/access/metaengine.cpp:296 engines/cine/metaengine.cpp:417
 #: engines/efh/metaengine.cpp:326
 #, fuzzy
 msgid "Move down-left"
 msgstr "Sol"
 
 #. I18N: Move Actor to bottom-right direction
-#: engines/access/metaengine.cpp:280 engines/cine/metaengine.cpp:423
+#: engines/access/metaengine.cpp:301 engines/cine/metaengine.cpp:423
 #: engines/efh/metaengine.cpp:332
 #, fuzzy
 msgid "Move down-right"
 msgstr "Sa??"
 
 #. I18N: (Game name: The Lost Files of Sherlock Holmes: The Case of the Rose Tattoo) This action is used to look at an object or npc in the game
-#: engines/access/metaengine.cpp:285 engines/bbvs/metaengine.cpp:124
+#: engines/access/metaengine.cpp:306 engines/bbvs/metaengine.cpp:124
 #: engines/dgds/metaengine.cpp:83 engines/drascula/metaengine.cpp:230
 #: engines/petka/metaengine.cpp:130 engines/scumm/help.cpp:167
 #: engines/scumm/help.cpp:231 engines/sherlock/metaengine.cpp:405
@@ -6088,7 +6096,7 @@ msgstr "Sa??"
 msgid "Look"
 msgstr "A??a????"
 
-#: engines/access/metaengine.cpp:293 engines/drascula/metaengine.cpp:240
+#: engines/access/metaengine.cpp:314 engines/drascula/metaengine.cpp:240
 #: engines/queen/metaengine.cpp:259 engines/saga/metaengine.cpp:369
 #: engines/scumm/help.cpp:125 engines/scumm/help.cpp:140
 #: engines/scumm/help.cpp:165 engines/scumm/help.cpp:193
@@ -6099,13 +6107,13 @@ msgstr "A??a????"
 msgid "Open"
 msgstr "URL'yi a??"
 
-#: engines/access/metaengine.cpp:303 engines/scumm/help.cpp:128
+#: engines/access/metaengine.cpp:324 engines/scumm/help.cpp:128
 #: engines/ultima/nuvie/metaengine.cpp:70
 msgid "Get"
 msgstr ""
 
 #. I18N: Combat command in Might & Magic 1
-#: engines/access/metaengine.cpp:308 engines/access/metaengine.cpp:329
+#: engines/access/metaengine.cpp:329 engines/access/metaengine.cpp:350
 #: engines/bbvs/metaengine.cpp:136 engines/cine/metaengine.cpp:342
 #: engines/efh/metaengine.cpp:462 engines/hdb/metaengine.cpp:237
 #: engines/lab/metaengine.cpp:323 engines/mm/mm1/metaengine.cpp:86
@@ -6125,11 +6133,11 @@ msgstr ""
 msgid "Use"
 msgstr ""
 
-#: engines/access/metaengine.cpp:313
+#: engines/access/metaengine.cpp:334
 msgid "Goto"
 msgstr ""
 
-#: engines/access/metaengine.cpp:318 engines/access/metaengine.cpp:349
+#: engines/access/metaengine.cpp:339 engines/access/metaengine.cpp:370
 #: engines/bbvs/metaengine.cpp:130 engines/drascula/metaengine.cpp:250
 #: engines/efh/metaengine.cpp:506 engines/petka/metaengine.cpp:158
 #: engines/scumm/help.cpp:174 engines/scumm/help.cpp:226
@@ -6140,18 +6148,18 @@ msgstr ""
 msgid "Talk"
 msgstr ""
 
-#: engines/access/metaengine.cpp:323 engines/scumm/help.cpp:175
+#: engines/access/metaengine.cpp:344 engines/scumm/help.cpp:175
 msgid "Travel"
 msgstr ""
 
-#: engines/access/metaengine.cpp:334 engines/cine/metaengine.cpp:332
+#: engines/access/metaengine.cpp:355 engines/cine/metaengine.cpp:332
 #: engines/petka/metaengine.cpp:144 engines/saga/metaengine.cpp:394
 #: engines/supernova/metaengine.cpp:331
 msgid "Take"
 msgstr ""
 
 #. I18N: Action in In Cold Blood
-#: engines/access/metaengine.cpp:339 engines/alcachofa/metaengine.cpp:116
+#: engines/access/metaengine.cpp:360 engines/alcachofa/metaengine.cpp:116
 #: engines/cine/metaengine.cpp:337 engines/crab/input/input.cpp:251
 #: engines/cruise/metaengine.cpp:192 engines/draci/metaengine.cpp:237
 #: engines/dragons/metaengine.cpp:147 engines/drascula/metaengine.cpp:218
@@ -6171,18 +6179,18 @@ msgstr ""
 msgid "Inventory"
 msgstr ""
 
-#: engines/access/metaengine.cpp:344 engines/ultima/ultima4/metaengine.cpp:51
+#: engines/access/metaengine.cpp:365 engines/ultima/ultima4/metaengine.cpp:51
 msgid "Climb"
 msgstr ""
 
-#: engines/access/metaengine.cpp:354 engines/bbvs/metaengine.cpp:142
+#: engines/access/metaengine.cpp:375 engines/bbvs/metaengine.cpp:142
 #: engines/freescape/games/castle/castle.cpp:511
 #: engines/petka/metaengine.cpp:137 engines/scumm/help.cpp:225
 #: engines/tsage/metaengine.cpp:201
 msgid "Walk"
 msgstr ""
 
-#: engines/access/metaengine.cpp:365
+#: engines/access/metaengine.cpp:386
 #, fuzzy
 msgid "Open save/load menu"
 msgstr "Oyun"
diff --git a/po/uk_UA.po b/po/uk_UA.po
index d90a96ce8c7..5625eeb9590 100644
--- a/po/uk_UA.po
+++ b/po/uk_UA.po
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.9.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-17 23:07+0000\n"
+"POT-Creation-Date: 2026-06-18 09:25+0000\n"
 "PO-Revision-Date: 2026-06-17 23:04+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Ukrainian <https://translations.scummvm.org/projects/scummvm/"
@@ -85,7 +85,7 @@ msgstr "??????????????"
 #. I18N: Opens the help screen.
 #: gui/browser.cpp:64 gui/launcher.cpp:277 gui/launcher.cpp:959
 #: backends/platform/sdl/macosx/appmenu_osx.mm:319
-#: engines/access/metaengine.cpp:360 engines/bladerunner/metaengine.cpp:321
+#: engines/access/metaengine.cpp:381 engines/bladerunner/metaengine.cpp:321
 #: engines/ngi/metaengine.cpp:162 engines/supernova/metaengine.cpp:238
 #: engines/tucker/metaengine.cpp:215 engines/zvision/metaengine.cpp:246
 msgid "Help"
@@ -2688,7 +2688,7 @@ msgstr ""
 "????????..."
 
 #: gui/options.cpp:2975 engines/engine.cpp:647
-#: engines/access/metaengine.cpp:298 engines/drascula/metaengine.cpp:255
+#: engines/access/metaengine.cpp:319 engines/drascula/metaengine.cpp:255
 #: engines/queen/metaengine.cpp:269 engines/sherlock/metaengine.cpp:410
 #: engines/ultima/nuvie/metaengine.cpp:71
 msgid "Move"
@@ -3992,7 +3992,7 @@ msgstr "???????? ??????"
 
 #. I18N: Skipping cutscene plaback
 #. I18N: Skipping cutscene playback
-#: engines/metaengine.cpp:112 engines/access/metaengine.cpp:225
+#: engines/metaengine.cpp:112 engines/access/metaengine.cpp:246
 #: engines/composer/metaengine.cpp:126 engines/drascula/metaengine.cpp:224
 #: engines/efh/metaengine.cpp:379 engines/freescape/movement.cpp:93
 #: engines/grim/grim.cpp:533 engines/grim/grim.cpp:633
@@ -6300,7 +6300,15 @@ msgstr "?????????????? ????????"
 msgid "Check for Updates..."
 msgstr "???????????????????? ??????????????????..."
 
-#: engines/access/metaengine.cpp:215 engines/alcachofa/metaengine.cpp:94
+#: engines/access/metaengine.cpp:82
+msgid "Use 'high definition' OGG music"
+msgstr ""
+
+#: engines/access/metaengine.cpp:83
+msgid "Use the OGG audio from the re-release instead of original MIDI tracks"
+msgstr ""
+
+#: engines/access/metaengine.cpp:236 engines/alcachofa/metaengine.cpp:94
 #: engines/bbvs/metaengine.cpp:92 engines/buried/metaengine.cpp:133
 #: engines/chamber/metaengine.cpp:59 engines/cine/metaengine.cpp:270
 #: engines/cruise/metaengine.cpp:168 engines/dm/metaengine.cpp:124
@@ -6324,16 +6332,16 @@ msgstr "???????????????????? ??????????????????..."
 msgid "Default keymappings"
 msgstr "???????? ???????????? ???? ??????????????????????"
 
-#: engines/access/metaengine.cpp:219
+#: engines/access/metaengine.cpp:240
 msgid "Move / Interact / Skip"
 msgstr "?????????????????????? / ?????????????????? / ??????????????"
 
-#: engines/access/metaengine.cpp:231 engines/sherlock/metaengine.cpp:483
+#: engines/access/metaengine.cpp:252 engines/sherlock/metaengine.cpp:483
 msgid "Skip movie"
 msgstr "???????????????????? ??????????"
 
 #. I18N: Move Actor to upwards direction
-#: engines/access/metaengine.cpp:237 engines/asylum/metaengine.cpp:171
+#: engines/access/metaengine.cpp:258 engines/asylum/metaengine.cpp:171
 #: engines/asylum/metaengine.cpp:198 engines/cine/metaengine.cpp:377
 #: engines/efh/metaengine.cpp:286 engines/hdb/metaengine.cpp:213
 #: engines/hypno/metaengine.cpp:363 engines/hypno/metaengine.cpp:411
@@ -6343,7 +6351,7 @@ msgid "Move up"
 msgstr "???????????????? ??????????"
 
 #. I18N: Move Actor to downwards direction
-#: engines/access/metaengine.cpp:244 engines/asylum/metaengine.cpp:177
+#: engines/access/metaengine.cpp:265 engines/asylum/metaengine.cpp:177
 #: engines/asylum/metaengine.cpp:205 engines/cine/metaengine.cpp:384
 #: engines/efh/metaengine.cpp:293 engines/hdb/metaengine.cpp:219
 #: engines/hypno/metaengine.cpp:351 engines/hypno/metaengine.cpp:399
@@ -6354,7 +6362,7 @@ msgstr "???????????????? ????????"
 
 #. I18N: Move Actor to left direction
 #. I18N: Move actor left during fight
-#: engines/access/metaengine.cpp:251 engines/asylum/metaengine.cpp:183
+#: engines/access/metaengine.cpp:272 engines/asylum/metaengine.cpp:183
 #: engines/asylum/metaengine.cpp:212 engines/cine/metaengine.cpp:391
 #: engines/efh/metaengine.cpp:300 engines/hdb/metaengine.cpp:225
 #: engines/hypno/metaengine.cpp:345 engines/hypno/metaengine.cpp:393
@@ -6367,7 +6375,7 @@ msgstr "???????????????? ????????????"
 
 #. I18N: Move Actor to right direction
 #. I18N: Move actor right during fight
-#: engines/access/metaengine.cpp:258 engines/asylum/metaengine.cpp:189
+#: engines/access/metaengine.cpp:279 engines/asylum/metaengine.cpp:189
 #: engines/asylum/metaengine.cpp:219 engines/cine/metaengine.cpp:398
 #: engines/efh/metaengine.cpp:307 engines/hdb/metaengine.cpp:231
 #: engines/hypno/metaengine.cpp:357 engines/hypno/metaengine.cpp:405
@@ -6379,31 +6387,31 @@ msgid "Move right"
 msgstr "???????????????? ??????????????"
 
 #. I18N: Move Actor to top-left direction
-#: engines/access/metaengine.cpp:265 engines/cine/metaengine.cpp:405
+#: engines/access/metaengine.cpp:286 engines/cine/metaengine.cpp:405
 #: engines/efh/metaengine.cpp:314
 msgid "Move up-left"
 msgstr "???????????????? ????????????-????????????"
 
 #. I18N: Move Actor to top-right direction
-#: engines/access/metaengine.cpp:270 engines/cine/metaengine.cpp:411
+#: engines/access/metaengine.cpp:291 engines/cine/metaengine.cpp:411
 #: engines/efh/metaengine.cpp:320
 msgid "Move up-right"
 msgstr "???????????????? ????????????-??????????????"
 
 #. I18N: Move Actor to bottom-left direction
-#: engines/access/metaengine.cpp:275 engines/cine/metaengine.cpp:417
+#: engines/access/metaengine.cpp:296 engines/cine/metaengine.cpp:417
 #: engines/efh/metaengine.cpp:326
 msgid "Move down-left"
 msgstr "???????????????? ????????????-????????????"
 
 #. I18N: Move Actor to bottom-right direction
-#: engines/access/metaengine.cpp:280 engines/cine/metaengine.cpp:423
+#: engines/access/metaengine.cpp:301 engines/cine/metaengine.cpp:423
 #: engines/efh/metaengine.cpp:332
 msgid "Move down-right"
 msgstr "???????????????? ????????????-??????????????"
 
 #. I18N: (Game name: The Lost Files of Sherlock Holmes: The Case of the Rose Tattoo) This action is used to look at an object or npc in the game
-#: engines/access/metaengine.cpp:285 engines/bbvs/metaengine.cpp:124
+#: engines/access/metaengine.cpp:306 engines/bbvs/metaengine.cpp:124
 #: engines/dgds/metaengine.cpp:83 engines/drascula/metaengine.cpp:230
 #: engines/petka/metaengine.cpp:130 engines/scumm/help.cpp:167
 #: engines/scumm/help.cpp:231 engines/sherlock/metaengine.cpp:405
@@ -6414,7 +6422,7 @@ msgstr "???????????????? ????????????-??????????????"
 msgid "Look"
 msgstr "??????????????"
 
-#: engines/access/metaengine.cpp:293 engines/drascula/metaengine.cpp:240
+#: engines/access/metaengine.cpp:314 engines/drascula/metaengine.cpp:240
 #: engines/queen/metaengine.cpp:259 engines/saga/metaengine.cpp:369
 #: engines/scumm/help.cpp:125 engines/scumm/help.cpp:140
 #: engines/scumm/help.cpp:165 engines/scumm/help.cpp:193
@@ -6424,13 +6432,13 @@ msgstr "??????????????"
 msgid "Open"
 msgstr "????????????????"
 
-#: engines/access/metaengine.cpp:303 engines/scumm/help.cpp:128
+#: engines/access/metaengine.cpp:324 engines/scumm/help.cpp:128
 #: engines/ultima/nuvie/metaengine.cpp:70
 msgid "Get"
 msgstr "????????????????"
 
 #. I18N: Combat command in Might & Magic 1
-#: engines/access/metaengine.cpp:308 engines/access/metaengine.cpp:329
+#: engines/access/metaengine.cpp:329 engines/access/metaengine.cpp:350
 #: engines/bbvs/metaengine.cpp:136 engines/cine/metaengine.cpp:342
 #: engines/efh/metaengine.cpp:462 engines/hdb/metaengine.cpp:237
 #: engines/lab/metaengine.cpp:323 engines/mm/mm1/metaengine.cpp:86
@@ -6450,11 +6458,11 @@ msgstr "????????????????"
 msgid "Use"
 msgstr "??????????????????????"
 
-#: engines/access/metaengine.cpp:313
+#: engines/access/metaengine.cpp:334
 msgid "Goto"
 msgstr "??????????????"
 
-#: engines/access/metaengine.cpp:318 engines/access/metaengine.cpp:349
+#: engines/access/metaengine.cpp:339 engines/access/metaengine.cpp:370
 #: engines/bbvs/metaengine.cpp:130 engines/drascula/metaengine.cpp:250
 #: engines/efh/metaengine.cpp:506 engines/petka/metaengine.cpp:158
 #: engines/scumm/help.cpp:174 engines/scumm/help.cpp:226
@@ -6465,18 +6473,18 @@ msgstr "??????????????"
 msgid "Talk"
 msgstr "????????????????"
 
-#: engines/access/metaengine.cpp:323 engines/scumm/help.cpp:175
+#: engines/access/metaengine.cpp:344 engines/scumm/help.cpp:175
 msgid "Travel"
 msgstr "??????????????"
 
-#: engines/access/metaengine.cpp:334 engines/cine/metaengine.cpp:332
+#: engines/access/metaengine.cpp:355 engines/cine/metaengine.cpp:332
 #: engines/petka/metaengine.cpp:144 engines/saga/metaengine.cpp:394
 #: engines/supernova/metaengine.cpp:331
 msgid "Take"
 msgstr "??????????"
 
 #. I18N: Action in In Cold Blood
-#: engines/access/metaengine.cpp:339 engines/alcachofa/metaengine.cpp:116
+#: engines/access/metaengine.cpp:360 engines/alcachofa/metaengine.cpp:116
 #: engines/cine/metaengine.cpp:337 engines/crab/input/input.cpp:251
 #: engines/cruise/metaengine.cpp:192 engines/draci/metaengine.cpp:237
 #: engines/dragons/metaengine.cpp:147 engines/drascula/metaengine.cpp:218
@@ -6496,18 +6504,18 @@ msgstr "??????????"
 msgid "Inventory"
 msgstr "????????????????"
 
-#: engines/access/metaengine.cpp:344 engines/ultima/ultima4/metaengine.cpp:51
+#: engines/access/metaengine.cpp:365 engines/ultima/ultima4/metaengine.cpp:51
 msgid "Climb"
 msgstr "??????????????????"
 
-#: engines/access/metaengine.cpp:354 engines/bbvs/metaengine.cpp:142
+#: engines/access/metaengine.cpp:375 engines/bbvs/metaengine.cpp:142
 #: engines/freescape/games/castle/castle.cpp:511
 #: engines/petka/metaengine.cpp:137 engines/scumm/help.cpp:225
 #: engines/tsage/metaengine.cpp:201
 msgid "Walk"
 msgstr "??????"
 
-#: engines/access/metaengine.cpp:365
+#: engines/access/metaengine.cpp:386
 msgid "Open save/load menu"
 msgstr "???????????????? ???????? ????????????????????/????????????????????????"
 
diff --git a/po/zh.po b/po/zh.po
index 2dcdf1b1685..12a893d6dd6 100644
--- a/po/zh.po
+++ b/po/zh.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-17 23:07+0000\n"
+"POT-Creation-Date: 2026-06-18 09:25+0000\n"
 "PO-Revision-Date: 2024-05-14 12:42+0000\n"
 "Last-Translator: ?????? <clonewith at qq.com>\n"
 "Language-Team: Chinese <https://translations.scummvm.org/projects/scummvm/"
@@ -84,7 +84,7 @@ msgstr "??????"
 #. I18N: Opens the help screen.
 #: gui/browser.cpp:64 gui/launcher.cpp:277 gui/launcher.cpp:959
 #: backends/platform/sdl/macosx/appmenu_osx.mm:319
-#: engines/access/metaengine.cpp:360 engines/bladerunner/metaengine.cpp:321
+#: engines/access/metaengine.cpp:381 engines/bladerunner/metaengine.cpp:321
 #: engines/ngi/metaengine.cpp:162 engines/supernova/metaengine.cpp:238
 #: engines/tucker/metaengine.cpp:215 engines/zvision/metaengine.cpp:246
 msgid "Help"
@@ -2529,7 +2529,7 @@ msgstr ""
 "????????????(A)..."
 
 #: gui/options.cpp:2975 engines/engine.cpp:647
-#: engines/access/metaengine.cpp:298 engines/drascula/metaengine.cpp:255
+#: engines/access/metaengine.cpp:319 engines/drascula/metaengine.cpp:255
 #: engines/queen/metaengine.cpp:269 engines/sherlock/metaengine.cpp:410
 #: engines/ultima/nuvie/metaengine.cpp:71
 msgid "Move"
@@ -3751,7 +3751,7 @@ msgstr ""
 
 #. I18N: Skipping cutscene plaback
 #. I18N: Skipping cutscene playback
-#: engines/metaengine.cpp:112 engines/access/metaengine.cpp:225
+#: engines/metaengine.cpp:112 engines/access/metaengine.cpp:246
 #: engines/composer/metaengine.cpp:126 engines/drascula/metaengine.cpp:224
 #: engines/efh/metaengine.cpp:379 engines/freescape/movement.cpp:93
 #: engines/grim/grim.cpp:533 engines/grim/grim.cpp:633
@@ -5744,7 +5744,15 @@ msgstr ""
 msgid "Check for Updates..."
 msgstr ""
 
-#: engines/access/metaengine.cpp:215 engines/alcachofa/metaengine.cpp:94
+#: engines/access/metaengine.cpp:82
+msgid "Use 'high definition' OGG music"
+msgstr ""
+
+#: engines/access/metaengine.cpp:83
+msgid "Use the OGG audio from the re-release instead of original MIDI tracks"
+msgstr ""
+
+#: engines/access/metaengine.cpp:236 engines/alcachofa/metaengine.cpp:94
 #: engines/bbvs/metaengine.cpp:92 engines/buried/metaengine.cpp:133
 #: engines/chamber/metaengine.cpp:59 engines/cine/metaengine.cpp:270
 #: engines/cruise/metaengine.cpp:168 engines/dm/metaengine.cpp:124
@@ -5770,20 +5778,20 @@ msgstr ""
 msgid "Default keymappings"
 msgstr "????????????"
 
-#: engines/access/metaengine.cpp:219
+#: engines/access/metaengine.cpp:240
 #, fuzzy
 #| msgid "Show hidden files"
 msgid "Move / Interact / Skip"
 msgstr "?????????????????????"
 
-#: engines/access/metaengine.cpp:231 engines/sherlock/metaengine.cpp:483
+#: engines/access/metaengine.cpp:252 engines/sherlock/metaengine.cpp:483
 #, fuzzy
 #| msgid "Skip autosave"
 msgid "Skip movie"
 msgstr "??????????????????"
 
 #. I18N: Move Actor to upwards direction
-#: engines/access/metaengine.cpp:237 engines/asylum/metaengine.cpp:171
+#: engines/access/metaengine.cpp:258 engines/asylum/metaengine.cpp:171
 #: engines/asylum/metaengine.cpp:198 engines/cine/metaengine.cpp:377
 #: engines/efh/metaengine.cpp:286 engines/hdb/metaengine.cpp:213
 #: engines/hypno/metaengine.cpp:363 engines/hypno/metaengine.cpp:411
@@ -5793,7 +5801,7 @@ msgid "Move up"
 msgstr ""
 
 #. I18N: Move Actor to downwards direction
-#: engines/access/metaengine.cpp:244 engines/asylum/metaengine.cpp:177
+#: engines/access/metaengine.cpp:265 engines/asylum/metaengine.cpp:177
 #: engines/asylum/metaengine.cpp:205 engines/cine/metaengine.cpp:384
 #: engines/efh/metaengine.cpp:293 engines/hdb/metaengine.cpp:219
 #: engines/hypno/metaengine.cpp:351 engines/hypno/metaengine.cpp:399
@@ -5804,7 +5812,7 @@ msgstr ""
 
 #. I18N: Move Actor to left direction
 #. I18N: Move actor left during fight
-#: engines/access/metaengine.cpp:251 engines/asylum/metaengine.cpp:183
+#: engines/access/metaengine.cpp:272 engines/asylum/metaengine.cpp:183
 #: engines/asylum/metaengine.cpp:212 engines/cine/metaengine.cpp:391
 #: engines/efh/metaengine.cpp:300 engines/hdb/metaengine.cpp:225
 #: engines/hypno/metaengine.cpp:345 engines/hypno/metaengine.cpp:393
@@ -5817,7 +5825,7 @@ msgstr ""
 
 #. I18N: Move Actor to right direction
 #. I18N: Move actor right during fight
-#: engines/access/metaengine.cpp:258 engines/asylum/metaengine.cpp:189
+#: engines/access/metaengine.cpp:279 engines/asylum/metaengine.cpp:189
 #: engines/asylum/metaengine.cpp:219 engines/cine/metaengine.cpp:398
 #: engines/efh/metaengine.cpp:307 engines/hdb/metaengine.cpp:231
 #: engines/hypno/metaengine.cpp:357 engines/hypno/metaengine.cpp:405
@@ -5829,31 +5837,31 @@ msgid "Move right"
 msgstr ""
 
 #. I18N: Move Actor to top-left direction
-#: engines/access/metaengine.cpp:265 engines/cine/metaengine.cpp:405
+#: engines/access/metaengine.cpp:286 engines/cine/metaengine.cpp:405
 #: engines/efh/metaengine.cpp:314
 msgid "Move up-left"
 msgstr ""
 
 #. I18N: Move Actor to top-right direction
-#: engines/access/metaengine.cpp:270 engines/cine/metaengine.cpp:411
+#: engines/access/metaengine.cpp:291 engines/cine/metaengine.cpp:411
 #: engines/efh/metaengine.cpp:320
 msgid "Move up-right"
 msgstr ""
 
 #. I18N: Move Actor to bottom-left direction
-#: engines/access/metaengine.cpp:275 engines/cine/metaengine.cpp:417
+#: engines/access/metaengine.cpp:296 engines/cine/metaengine.cpp:417
 #: engines/efh/metaengine.cpp:326
 msgid "Move down-left"
 msgstr ""
 
 #. I18N: Move Actor to bottom-right direction
-#: engines/access/metaengine.cpp:280 engines/cine/metaengine.cpp:423
+#: engines/access/metaengine.cpp:301 engines/cine/metaengine.cpp:423
 #: engines/efh/metaengine.cpp:332
 msgid "Move down-right"
 msgstr ""
 
 #. I18N: (Game name: The Lost Files of Sherlock Holmes: The Case of the Rose Tattoo) This action is used to look at an object or npc in the game
-#: engines/access/metaengine.cpp:285 engines/bbvs/metaengine.cpp:124
+#: engines/access/metaengine.cpp:306 engines/bbvs/metaengine.cpp:124
 #: engines/dgds/metaengine.cpp:83 engines/drascula/metaengine.cpp:230
 #: engines/petka/metaengine.cpp:130 engines/scumm/help.cpp:167
 #: engines/scumm/help.cpp:231 engines/sherlock/metaengine.cpp:405
@@ -5864,7 +5872,7 @@ msgstr ""
 msgid "Look"
 msgstr ""
 
-#: engines/access/metaengine.cpp:293 engines/drascula/metaengine.cpp:240
+#: engines/access/metaengine.cpp:314 engines/drascula/metaengine.cpp:240
 #: engines/queen/metaengine.cpp:259 engines/saga/metaengine.cpp:369
 #: engines/scumm/help.cpp:125 engines/scumm/help.cpp:140
 #: engines/scumm/help.cpp:165 engines/scumm/help.cpp:193
@@ -5874,13 +5882,13 @@ msgstr ""
 msgid "Open"
 msgstr ""
 
-#: engines/access/metaengine.cpp:303 engines/scumm/help.cpp:128
+#: engines/access/metaengine.cpp:324 engines/scumm/help.cpp:128
 #: engines/ultima/nuvie/metaengine.cpp:70
 msgid "Get"
 msgstr ""
 
 #. I18N: Combat command in Might & Magic 1
-#: engines/access/metaengine.cpp:308 engines/access/metaengine.cpp:329
+#: engines/access/metaengine.cpp:329 engines/access/metaengine.cpp:350
 #: engines/bbvs/metaengine.cpp:136 engines/cine/metaengine.cpp:342
 #: engines/efh/metaengine.cpp:462 engines/hdb/metaengine.cpp:237
 #: engines/lab/metaengine.cpp:323 engines/mm/mm1/metaengine.cpp:86
@@ -5900,11 +5908,11 @@ msgstr ""
 msgid "Use"
 msgstr ""
 
-#: engines/access/metaengine.cpp:313
+#: engines/access/metaengine.cpp:334
 msgid "Goto"
 msgstr ""
 
-#: engines/access/metaengine.cpp:318 engines/access/metaengine.cpp:349
+#: engines/access/metaengine.cpp:339 engines/access/metaengine.cpp:370
 #: engines/bbvs/metaengine.cpp:130 engines/drascula/metaengine.cpp:250
 #: engines/efh/metaengine.cpp:506 engines/petka/metaengine.cpp:158
 #: engines/scumm/help.cpp:174 engines/scumm/help.cpp:226
@@ -5915,18 +5923,18 @@ msgstr ""
 msgid "Talk"
 msgstr ""
 
-#: engines/access/metaengine.cpp:323 engines/scumm/help.cpp:175
+#: engines/access/metaengine.cpp:344 engines/scumm/help.cpp:175
 msgid "Travel"
 msgstr ""
 
-#: engines/access/metaengine.cpp:334 engines/cine/metaengine.cpp:332
+#: engines/access/metaengine.cpp:355 engines/cine/metaengine.cpp:332
 #: engines/petka/metaengine.cpp:144 engines/saga/metaengine.cpp:394
 #: engines/supernova/metaengine.cpp:331
 msgid "Take"
 msgstr ""
 
 #. I18N: Action in In Cold Blood
-#: engines/access/metaengine.cpp:339 engines/alcachofa/metaengine.cpp:116
+#: engines/access/metaengine.cpp:360 engines/alcachofa/metaengine.cpp:116
 #: engines/cine/metaengine.cpp:337 engines/crab/input/input.cpp:251
 #: engines/cruise/metaengine.cpp:192 engines/draci/metaengine.cpp:237
 #: engines/dragons/metaengine.cpp:147 engines/drascula/metaengine.cpp:218
@@ -5946,18 +5954,18 @@ msgstr ""
 msgid "Inventory"
 msgstr ""
 
-#: engines/access/metaengine.cpp:344 engines/ultima/ultima4/metaengine.cpp:51
+#: engines/access/metaengine.cpp:365 engines/ultima/ultima4/metaengine.cpp:51
 msgid "Climb"
 msgstr ""
 
-#: engines/access/metaengine.cpp:354 engines/bbvs/metaengine.cpp:142
+#: engines/access/metaengine.cpp:375 engines/bbvs/metaengine.cpp:142
 #: engines/freescape/games/castle/castle.cpp:511
 #: engines/petka/metaengine.cpp:137 engines/scumm/help.cpp:225
 #: engines/tsage/metaengine.cpp:201
 msgid "Walk"
 msgstr ""
 
-#: engines/access/metaengine.cpp:365
+#: engines/access/metaengine.cpp:386
 #, fuzzy
 #| msgid "Search in game list"
 msgid "Open save/load menu"
diff --git a/po/zh_Hans.po b/po/zh_Hans.po
index a9521474bf3..64b6cb5ce39 100644
--- a/po/zh_Hans.po
+++ b/po/zh_Hans.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.9.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-17 23:07+0000\n"
+"POT-Creation-Date: 2026-06-18 09:25+0000\n"
 "PO-Revision-Date: 2024-12-01 22:18+0000\n"
 "Last-Translator: ?????? <clonewith at qq.com>\n"
 "Language-Team: Chinese (Simplified Han script) <https://"
@@ -84,7 +84,7 @@ msgstr "??????"
 #. I18N: Opens the help screen.
 #: gui/browser.cpp:64 gui/launcher.cpp:277 gui/launcher.cpp:959
 #: backends/platform/sdl/macosx/appmenu_osx.mm:319
-#: engines/access/metaengine.cpp:360 engines/bladerunner/metaengine.cpp:321
+#: engines/access/metaengine.cpp:381 engines/bladerunner/metaengine.cpp:321
 #: engines/ngi/metaengine.cpp:162 engines/supernova/metaengine.cpp:238
 #: engines/tucker/metaengine.cpp:215 engines/zvision/metaengine.cpp:246
 msgid "Help"
@@ -2666,7 +2666,7 @@ msgstr ""
 "?????????..."
 
 #: gui/options.cpp:2975 engines/engine.cpp:647
-#: engines/access/metaengine.cpp:298 engines/drascula/metaengine.cpp:255
+#: engines/access/metaengine.cpp:319 engines/drascula/metaengine.cpp:255
 #: engines/queen/metaengine.cpp:269 engines/sherlock/metaengine.cpp:410
 #: engines/ultima/nuvie/metaengine.cpp:71
 msgid "Move"
@@ -3960,7 +3960,7 @@ msgstr "????????????"
 
 #. I18N: Skipping cutscene plaback
 #. I18N: Skipping cutscene playback
-#: engines/metaengine.cpp:112 engines/access/metaengine.cpp:225
+#: engines/metaengine.cpp:112 engines/access/metaengine.cpp:246
 #: engines/composer/metaengine.cpp:126 engines/drascula/metaengine.cpp:224
 #: engines/efh/metaengine.cpp:379 engines/freescape/movement.cpp:93
 #: engines/grim/grim.cpp:533 engines/grim/grim.cpp:633
@@ -6388,7 +6388,15 @@ msgstr "?????????"
 msgid "Check for Updates..."
 msgstr "??????????????????..."
 
-#: engines/access/metaengine.cpp:215 engines/alcachofa/metaengine.cpp:94
+#: engines/access/metaengine.cpp:82
+msgid "Use 'high definition' OGG music"
+msgstr ""
+
+#: engines/access/metaengine.cpp:83
+msgid "Use the OGG audio from the re-release instead of original MIDI tracks"
+msgstr ""
+
+#: engines/access/metaengine.cpp:236 engines/alcachofa/metaengine.cpp:94
 #: engines/bbvs/metaengine.cpp:92 engines/buried/metaengine.cpp:133
 #: engines/chamber/metaengine.cpp:59 engines/cine/metaengine.cpp:270
 #: engines/cruise/metaengine.cpp:168 engines/dm/metaengine.cpp:124
@@ -6412,20 +6420,20 @@ msgstr "??????????????????..."
 msgid "Default keymappings"
 msgstr "??????????????????"
 
-#: engines/access/metaengine.cpp:219
+#: engines/access/metaengine.cpp:240
 #, fuzzy
 #| msgid "Go to next dialogue"
 msgid "Move / Interact / Skip"
 msgstr "??????????????????"
 
-#: engines/access/metaengine.cpp:231 engines/sherlock/metaengine.cpp:483
+#: engines/access/metaengine.cpp:252 engines/sherlock/metaengine.cpp:483
 #, fuzzy
 #| msgid "Skip video"
 msgid "Skip movie"
 msgstr "????????????"
 
 #. I18N: Move Actor to upwards direction
-#: engines/access/metaengine.cpp:237 engines/asylum/metaengine.cpp:171
+#: engines/access/metaengine.cpp:258 engines/asylum/metaengine.cpp:171
 #: engines/asylum/metaengine.cpp:198 engines/cine/metaengine.cpp:377
 #: engines/efh/metaengine.cpp:286 engines/hdb/metaengine.cpp:213
 #: engines/hypno/metaengine.cpp:363 engines/hypno/metaengine.cpp:411
@@ -6435,7 +6443,7 @@ msgid "Move up"
 msgstr "????????????"
 
 #. I18N: Move Actor to downwards direction
-#: engines/access/metaengine.cpp:244 engines/asylum/metaengine.cpp:177
+#: engines/access/metaengine.cpp:265 engines/asylum/metaengine.cpp:177
 #: engines/asylum/metaengine.cpp:205 engines/cine/metaengine.cpp:384
 #: engines/efh/metaengine.cpp:293 engines/hdb/metaengine.cpp:219
 #: engines/hypno/metaengine.cpp:351 engines/hypno/metaengine.cpp:399
@@ -6446,7 +6454,7 @@ msgstr "????????????"
 
 #. I18N: Move Actor to left direction
 #. I18N: Move actor left during fight
-#: engines/access/metaengine.cpp:251 engines/asylum/metaengine.cpp:183
+#: engines/access/metaengine.cpp:272 engines/asylum/metaengine.cpp:183
 #: engines/asylum/metaengine.cpp:212 engines/cine/metaengine.cpp:391
 #: engines/efh/metaengine.cpp:300 engines/hdb/metaengine.cpp:225
 #: engines/hypno/metaengine.cpp:345 engines/hypno/metaengine.cpp:393
@@ -6459,7 +6467,7 @@ msgstr "????????????"
 
 #. I18N: Move Actor to right direction
 #. I18N: Move actor right during fight
-#: engines/access/metaengine.cpp:258 engines/asylum/metaengine.cpp:189
+#: engines/access/metaengine.cpp:279 engines/asylum/metaengine.cpp:189
 #: engines/asylum/metaengine.cpp:219 engines/cine/metaengine.cpp:398
 #: engines/efh/metaengine.cpp:307 engines/hdb/metaengine.cpp:231
 #: engines/hypno/metaengine.cpp:357 engines/hypno/metaengine.cpp:405
@@ -6471,31 +6479,31 @@ msgid "Move right"
 msgstr "????????????"
 
 #. I18N: Move Actor to top-left direction
-#: engines/access/metaengine.cpp:265 engines/cine/metaengine.cpp:405
+#: engines/access/metaengine.cpp:286 engines/cine/metaengine.cpp:405
 #: engines/efh/metaengine.cpp:314
 msgid "Move up-left"
 msgstr "??????????????????"
 
 #. I18N: Move Actor to top-right direction
-#: engines/access/metaengine.cpp:270 engines/cine/metaengine.cpp:411
+#: engines/access/metaengine.cpp:291 engines/cine/metaengine.cpp:411
 #: engines/efh/metaengine.cpp:320
 msgid "Move up-right"
 msgstr "??????????????????"
 
 #. I18N: Move Actor to bottom-left direction
-#: engines/access/metaengine.cpp:275 engines/cine/metaengine.cpp:417
+#: engines/access/metaengine.cpp:296 engines/cine/metaengine.cpp:417
 #: engines/efh/metaengine.cpp:326
 msgid "Move down-left"
 msgstr "??????????????????"
 
 #. I18N: Move Actor to bottom-right direction
-#: engines/access/metaengine.cpp:280 engines/cine/metaengine.cpp:423
+#: engines/access/metaengine.cpp:301 engines/cine/metaengine.cpp:423
 #: engines/efh/metaengine.cpp:332
 msgid "Move down-right"
 msgstr "??????????????????"
 
 #. I18N: (Game name: The Lost Files of Sherlock Holmes: The Case of the Rose Tattoo) This action is used to look at an object or npc in the game
-#: engines/access/metaengine.cpp:285 engines/bbvs/metaengine.cpp:124
+#: engines/access/metaengine.cpp:306 engines/bbvs/metaengine.cpp:124
 #: engines/dgds/metaengine.cpp:83 engines/drascula/metaengine.cpp:230
 #: engines/petka/metaengine.cpp:130 engines/scumm/help.cpp:167
 #: engines/scumm/help.cpp:231 engines/sherlock/metaengine.cpp:405
@@ -6506,7 +6514,7 @@ msgstr "??????????????????"
 msgid "Look"
 msgstr "??????"
 
-#: engines/access/metaengine.cpp:293 engines/drascula/metaengine.cpp:240
+#: engines/access/metaengine.cpp:314 engines/drascula/metaengine.cpp:240
 #: engines/queen/metaengine.cpp:259 engines/saga/metaengine.cpp:369
 #: engines/scumm/help.cpp:125 engines/scumm/help.cpp:140
 #: engines/scumm/help.cpp:165 engines/scumm/help.cpp:193
@@ -6516,13 +6524,13 @@ msgstr "??????"
 msgid "Open"
 msgstr "??????"
 
-#: engines/access/metaengine.cpp:303 engines/scumm/help.cpp:128
+#: engines/access/metaengine.cpp:324 engines/scumm/help.cpp:128
 #: engines/ultima/nuvie/metaengine.cpp:70
 msgid "Get"
 msgstr "??????"
 
 #. I18N: Combat command in Might & Magic 1
-#: engines/access/metaengine.cpp:308 engines/access/metaengine.cpp:329
+#: engines/access/metaengine.cpp:329 engines/access/metaengine.cpp:350
 #: engines/bbvs/metaengine.cpp:136 engines/cine/metaengine.cpp:342
 #: engines/efh/metaengine.cpp:462 engines/hdb/metaengine.cpp:237
 #: engines/lab/metaengine.cpp:323 engines/mm/mm1/metaengine.cpp:86
@@ -6542,11 +6550,11 @@ msgstr "??????"
 msgid "Use"
 msgstr "??????"
 
-#: engines/access/metaengine.cpp:313
+#: engines/access/metaengine.cpp:334
 msgid "Goto"
 msgstr ""
 
-#: engines/access/metaengine.cpp:318 engines/access/metaengine.cpp:349
+#: engines/access/metaengine.cpp:339 engines/access/metaengine.cpp:370
 #: engines/bbvs/metaengine.cpp:130 engines/drascula/metaengine.cpp:250
 #: engines/efh/metaengine.cpp:506 engines/petka/metaengine.cpp:158
 #: engines/scumm/help.cpp:174 engines/scumm/help.cpp:226
@@ -6557,18 +6565,18 @@ msgstr ""
 msgid "Talk"
 msgstr "??????"
 
-#: engines/access/metaengine.cpp:323 engines/scumm/help.cpp:175
+#: engines/access/metaengine.cpp:344 engines/scumm/help.cpp:175
 msgid "Travel"
 msgstr "??????"
 
-#: engines/access/metaengine.cpp:334 engines/cine/metaengine.cpp:332
+#: engines/access/metaengine.cpp:355 engines/cine/metaengine.cpp:332
 #: engines/petka/metaengine.cpp:144 engines/saga/metaengine.cpp:394
 #: engines/supernova/metaengine.cpp:331
 msgid "Take"
 msgstr "??????"
 
 #. I18N: Action in In Cold Blood
-#: engines/access/metaengine.cpp:339 engines/alcachofa/metaengine.cpp:116
+#: engines/access/metaengine.cpp:360 engines/alcachofa/metaengine.cpp:116
 #: engines/cine/metaengine.cpp:337 engines/crab/input/input.cpp:251
 #: engines/cruise/metaengine.cpp:192 engines/draci/metaengine.cpp:237
 #: engines/dragons/metaengine.cpp:147 engines/drascula/metaengine.cpp:218
@@ -6588,18 +6596,18 @@ msgstr "??????"
 msgid "Inventory"
 msgstr "?????????"
 
-#: engines/access/metaengine.cpp:344 engines/ultima/ultima4/metaengine.cpp:51
+#: engines/access/metaengine.cpp:365 engines/ultima/ultima4/metaengine.cpp:51
 msgid "Climb"
 msgstr ""
 
-#: engines/access/metaengine.cpp:354 engines/bbvs/metaengine.cpp:142
+#: engines/access/metaengine.cpp:375 engines/bbvs/metaengine.cpp:142
 #: engines/freescape/games/castle/castle.cpp:511
 #: engines/petka/metaengine.cpp:137 engines/scumm/help.cpp:225
 #: engines/tsage/metaengine.cpp:201
 msgid "Walk"
 msgstr "??????"
 
-#: engines/access/metaengine.cpp:365
+#: engines/access/metaengine.cpp:386
 #, fuzzy
 #| msgid "Page Up in save/load menu"
 msgid "Open save/load menu"
diff --git a/po/zh_Hant.po b/po/zh_Hant.po
index c1e11f3aef0..fdc92ada17f 100644
--- a/po/zh_Hant.po
+++ b/po/zh_Hant.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.9.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-17 23:07+0000\n"
+"POT-Creation-Date: 2026-06-18 09:25+0000\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: Automatically generated\n"
 "Language-Team: none\n"
@@ -80,7 +80,7 @@ msgstr ""
 #. I18N: Opens the help screen.
 #: gui/browser.cpp:64 gui/launcher.cpp:277 gui/launcher.cpp:959
 #: backends/platform/sdl/macosx/appmenu_osx.mm:319
-#: engines/access/metaengine.cpp:360 engines/bladerunner/metaengine.cpp:321
+#: engines/access/metaengine.cpp:381 engines/bladerunner/metaengine.cpp:321
 #: engines/ngi/metaengine.cpp:162 engines/supernova/metaengine.cpp:238
 #: engines/tucker/metaengine.cpp:215 engines/zvision/metaengine.cpp:246
 msgid "Help"
@@ -2499,7 +2499,7 @@ msgid ""
 msgstr ""
 
 #: gui/options.cpp:2975 engines/engine.cpp:647
-#: engines/access/metaengine.cpp:298 engines/drascula/metaengine.cpp:255
+#: engines/access/metaengine.cpp:319 engines/drascula/metaengine.cpp:255
 #: engines/queen/metaengine.cpp:269 engines/sherlock/metaengine.cpp:410
 #: engines/ultima/nuvie/metaengine.cpp:71
 msgid "Move"
@@ -3687,7 +3687,7 @@ msgstr ""
 
 #. I18N: Skipping cutscene plaback
 #. I18N: Skipping cutscene playback
-#: engines/metaengine.cpp:112 engines/access/metaengine.cpp:225
+#: engines/metaengine.cpp:112 engines/access/metaengine.cpp:246
 #: engines/composer/metaengine.cpp:126 engines/drascula/metaengine.cpp:224
 #: engines/efh/metaengine.cpp:379 engines/freescape/movement.cpp:93
 #: engines/grim/grim.cpp:533 engines/grim/grim.cpp:633
@@ -5636,7 +5636,15 @@ msgstr ""
 msgid "Check for Updates..."
 msgstr ""
 
-#: engines/access/metaengine.cpp:215 engines/alcachofa/metaengine.cpp:94
+#: engines/access/metaengine.cpp:82
+msgid "Use 'high definition' OGG music"
+msgstr ""
+
+#: engines/access/metaengine.cpp:83
+msgid "Use the OGG audio from the re-release instead of original MIDI tracks"
+msgstr ""
+
+#: engines/access/metaengine.cpp:236 engines/alcachofa/metaengine.cpp:94
 #: engines/bbvs/metaengine.cpp:92 engines/buried/metaengine.cpp:133
 #: engines/chamber/metaengine.cpp:59 engines/cine/metaengine.cpp:270
 #: engines/cruise/metaengine.cpp:168 engines/dm/metaengine.cpp:124
@@ -5660,16 +5668,16 @@ msgstr ""
 msgid "Default keymappings"
 msgstr ""
 
-#: engines/access/metaengine.cpp:219
+#: engines/access/metaengine.cpp:240
 msgid "Move / Interact / Skip"
 msgstr ""
 
-#: engines/access/metaengine.cpp:231 engines/sherlock/metaengine.cpp:483
+#: engines/access/metaengine.cpp:252 engines/sherlock/metaengine.cpp:483
 msgid "Skip movie"
 msgstr ""
 
 #. I18N: Move Actor to upwards direction
-#: engines/access/metaengine.cpp:237 engines/asylum/metaengine.cpp:171
+#: engines/access/metaengine.cpp:258 engines/asylum/metaengine.cpp:171
 #: engines/asylum/metaengine.cpp:198 engines/cine/metaengine.cpp:377
 #: engines/efh/metaengine.cpp:286 engines/hdb/metaengine.cpp:213
 #: engines/hypno/metaengine.cpp:363 engines/hypno/metaengine.cpp:411
@@ -5679,7 +5687,7 @@ msgid "Move up"
 msgstr ""
 
 #. I18N: Move Actor to downwards direction
-#: engines/access/metaengine.cpp:244 engines/asylum/metaengine.cpp:177
+#: engines/access/metaengine.cpp:265 engines/asylum/metaengine.cpp:177
 #: engines/asylum/metaengine.cpp:205 engines/cine/metaengine.cpp:384
 #: engines/efh/metaengine.cpp:293 engines/hdb/metaengine.cpp:219
 #: engines/hypno/metaengine.cpp:351 engines/hypno/metaengine.cpp:399
@@ -5690,7 +5698,7 @@ msgstr ""
 
 #. I18N: Move Actor to left direction
 #. I18N: Move actor left during fight
-#: engines/access/metaengine.cpp:251 engines/asylum/metaengine.cpp:183
+#: engines/access/metaengine.cpp:272 engines/asylum/metaengine.cpp:183
 #: engines/asylum/metaengine.cpp:212 engines/cine/metaengine.cpp:391
 #: engines/efh/metaengine.cpp:300 engines/hdb/metaengine.cpp:225
 #: engines/hypno/metaengine.cpp:345 engines/hypno/metaengine.cpp:393
@@ -5703,7 +5711,7 @@ msgstr ""
 
 #. I18N: Move Actor to right direction
 #. I18N: Move actor right during fight
-#: engines/access/metaengine.cpp:258 engines/asylum/metaengine.cpp:189
+#: engines/access/metaengine.cpp:279 engines/asylum/metaengine.cpp:189
 #: engines/asylum/metaengine.cpp:219 engines/cine/metaengine.cpp:398
 #: engines/efh/metaengine.cpp:307 engines/hdb/metaengine.cpp:231
 #: engines/hypno/metaengine.cpp:357 engines/hypno/metaengine.cpp:405
@@ -5715,31 +5723,31 @@ msgid "Move right"
 msgstr ""
 
 #. I18N: Move Actor to top-left direction
-#: engines/access/metaengine.cpp:265 engines/cine/metaengine.cpp:405
+#: engines/access/metaengine.cpp:286 engines/cine/metaengine.cpp:405
 #: engines/efh/metaengine.cpp:314
 msgid "Move up-left"
 msgstr ""
 
 #. I18N: Move Actor to top-right direction
-#: engines/access/metaengine.cpp:270 engines/cine/metaengine.cpp:411
+#: engines/access/metaengine.cpp:291 engines/cine/metaengine.cpp:411
 #: engines/efh/metaengine.cpp:320
 msgid "Move up-right"
 msgstr ""
 
 #. I18N: Move Actor to bottom-left direction
-#: engines/access/metaengine.cpp:275 engines/cine/metaengine.cpp:417
+#: engines/access/metaengine.cpp:296 engines/cine/metaengine.cpp:417
 #: engines/efh/metaengine.cpp:326
 msgid "Move down-left"
 msgstr ""
 
 #. I18N: Move Actor to bottom-right direction
-#: engines/access/metaengine.cpp:280 engines/cine/metaengine.cpp:423
+#: engines/access/metaengine.cpp:301 engines/cine/metaengine.cpp:423
 #: engines/efh/metaengine.cpp:332
 msgid "Move down-right"
 msgstr ""
 
 #. I18N: (Game name: The Lost Files of Sherlock Holmes: The Case of the Rose Tattoo) This action is used to look at an object or npc in the game
-#: engines/access/metaengine.cpp:285 engines/bbvs/metaengine.cpp:124
+#: engines/access/metaengine.cpp:306 engines/bbvs/metaengine.cpp:124
 #: engines/dgds/metaengine.cpp:83 engines/drascula/metaengine.cpp:230
 #: engines/petka/metaengine.cpp:130 engines/scumm/help.cpp:167
 #: engines/scumm/help.cpp:231 engines/sherlock/metaengine.cpp:405
@@ -5750,7 +5758,7 @@ msgstr ""
 msgid "Look"
 msgstr ""
 
-#: engines/access/metaengine.cpp:293 engines/drascula/metaengine.cpp:240
+#: engines/access/metaengine.cpp:314 engines/drascula/metaengine.cpp:240
 #: engines/queen/metaengine.cpp:259 engines/saga/metaengine.cpp:369
 #: engines/scumm/help.cpp:125 engines/scumm/help.cpp:140
 #: engines/scumm/help.cpp:165 engines/scumm/help.cpp:193
@@ -5760,13 +5768,13 @@ msgstr ""
 msgid "Open"
 msgstr ""
 
-#: engines/access/metaengine.cpp:303 engines/scumm/help.cpp:128
+#: engines/access/metaengine.cpp:324 engines/scumm/help.cpp:128
 #: engines/ultima/nuvie/metaengine.cpp:70
 msgid "Get"
 msgstr ""
 
 #. I18N: Combat command in Might & Magic 1
-#: engines/access/metaengine.cpp:308 engines/access/metaengine.cpp:329
+#: engines/access/metaengine.cpp:329 engines/access/metaengine.cpp:350
 #: engines/bbvs/metaengine.cpp:136 engines/cine/metaengine.cpp:342
 #: engines/efh/metaengine.cpp:462 engines/hdb/metaengine.cpp:237
 #: engines/lab/metaengine.cpp:323 engines/mm/mm1/metaengine.cpp:86
@@ -5786,11 +5794,11 @@ msgstr ""
 msgid "Use"
 msgstr ""
 
-#: engines/access/metaengine.cpp:313
+#: engines/access/metaengine.cpp:334
 msgid "Goto"
 msgstr ""
 
-#: engines/access/metaengine.cpp:318 engines/access/metaengine.cpp:349
+#: engines/access/metaengine.cpp:339 engines/access/metaengine.cpp:370
 #: engines/bbvs/metaengine.cpp:130 engines/drascula/metaengine.cpp:250
 #: engines/efh/metaengine.cpp:506 engines/petka/metaengine.cpp:158
 #: engines/scumm/help.cpp:174 engines/scumm/help.cpp:226
@@ -5801,18 +5809,18 @@ msgstr ""
 msgid "Talk"
 msgstr ""
 
-#: engines/access/metaengine.cpp:323 engines/scumm/help.cpp:175
+#: engines/access/metaengine.cpp:344 engines/scumm/help.cpp:175
 msgid "Travel"
 msgstr ""
 
-#: engines/access/metaengine.cpp:334 engines/cine/metaengine.cpp:332
+#: engines/access/metaengine.cpp:355 engines/cine/metaengine.cpp:332
 #: engines/petka/metaengine.cpp:144 engines/saga/metaengine.cpp:394
 #: engines/supernova/metaengine.cpp:331
 msgid "Take"
 msgstr ""
 
 #. I18N: Action in In Cold Blood
-#: engines/access/metaengine.cpp:339 engines/alcachofa/metaengine.cpp:116
+#: engines/access/metaengine.cpp:360 engines/alcachofa/metaengine.cpp:116
 #: engines/cine/metaengine.cpp:337 engines/crab/input/input.cpp:251
 #: engines/cruise/metaengine.cpp:192 engines/draci/metaengine.cpp:237
 #: engines/dragons/metaengine.cpp:147 engines/drascula/metaengine.cpp:218
@@ -5832,18 +5840,18 @@ msgstr ""
 msgid "Inventory"
 msgstr ""
 
-#: engines/access/metaengine.cpp:344 engines/ultima/ultima4/metaengine.cpp:51
+#: engines/access/metaengine.cpp:365 engines/ultima/ultima4/metaengine.cpp:51
 msgid "Climb"
 msgstr ""
 
-#: engines/access/metaengine.cpp:354 engines/bbvs/metaengine.cpp:142
+#: engines/access/metaengine.cpp:375 engines/bbvs/metaengine.cpp:142
 #: engines/freescape/games/castle/castle.cpp:511
 #: engines/petka/metaengine.cpp:137 engines/scumm/help.cpp:225
 #: engines/tsage/metaengine.cpp:201
 msgid "Walk"
 msgstr ""
 
-#: engines/access/metaengine.cpp:365
+#: engines/access/metaengine.cpp:386
 msgid "Open save/load menu"
 msgstr ""
 



From noreply at scummvm.org  Thu Jun 18 09:40:33 2026
From: noreply at scummvm.org (dwatteau)
Date: Thu, 18 Jun 2026 09:40:33 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 8775c6b489e4b15108642162eb45270a55aa89c0
Message-ID: <20260618094033.5B26F7005CC@ron.scummvm.net>

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:
aa0a4949e0 ACCESS: Silence -Wunused-but-set-variable warnings
6fb3002fe7 MM: MM1: Silence -Wunused-but-set-variable warnings
8775c6b489 TWINE: Silence -Wunused-private-field warning


Commit: aa0a4949e00425dc09e90cc57cc93db4e6aa6be8
    https://github.com/scummvm/scummvm/commit/aa0a4949e00425dc09e90cc57cc93db4e6aa6be8
Author: Donovan Watteau (contrib at dwatteau.fr)
Date: 2026-06-18T11:33:04+02:00

Commit Message:
ACCESS: Silence -Wunused-but-set-variable warnings

Changed paths:
    engines/access/noctropolis/noctropolis_font.cpp
    engines/access/scripts.cpp


diff --git a/engines/access/noctropolis/noctropolis_font.cpp b/engines/access/noctropolis/noctropolis_font.cpp
index 13a3f805b75..4b9f43e84d6 100644
--- a/engines/access/noctropolis/noctropolis_font.cpp
+++ b/engines/access/noctropolis/noctropolis_font.cpp
@@ -119,6 +119,8 @@ void NoctropolisFont::load(const uint16 *offsets, const byte *data) {
 	pal[3] = pal[4] = pal[5] = 0xff;
 	dumpfile.open(Common::Path(Common::String::format("/tmp/font-%d.png", dumpno++)));
 	Image::writePNG(dumpfile, dump, pal);
+#else
+	(void)totalw;
 #endif
 }
 
diff --git a/engines/access/scripts.cpp b/engines/access/scripts.cpp
index ceb4f757ef3..9853559cf8f 100644
--- a/engines/access/scripts.cpp
+++ b/engines/access/scripts.cpp
@@ -898,13 +898,15 @@ void Scripts::cmdSetVideo_v3() {
 	if (cellIndex > 0x3f)
 		error("Invalid room video number %d", cellIndex);
 
-	bool flag;
 	const int roomNum = _vm->_player->_roomNumber;
+#if 0
+	bool flag;
 
 	if ((roomNum == 0x1e && cellIndex == 0) || roomNum == 0x21 || (roomNum == 0x36 && cellIndex < 32))
 		flag = true;
 	else
 		flag = false;
+#endif
 
 	if (roomNum == 0x1b || roomNum == 0x1e)
 		pt.x = rawx + -5;


Commit: 6fb3002fe74e6562ec0e312cb26cf453e5ce9989
    https://github.com/scummvm/scummvm/commit/6fb3002fe74e6562ec0e312cb26cf453e5ce9989
Author: Donovan Watteau (contrib at dwatteau.fr)
Date: 2026-06-18T11:34:49+02:00

Commit Message:
MM: MM1: Silence -Wunused-but-set-variable warnings

Changed paths:
    engines/mm/mm1/metaengine.cpp
    engines/mm/mm1/views_enh/create_characters.cpp


diff --git a/engines/mm/mm1/metaengine.cpp b/engines/mm/mm1/metaengine.cpp
index e8eb06a9982..b16b0399a9b 100644
--- a/engines/mm/mm1/metaengine.cpp
+++ b/engines/mm/mm1/metaengine.cpp
@@ -123,10 +123,12 @@ static const KeybindingRecord NORMAL_KEYS[] = {
 	{ KEYBIND_NONE, nullptr, nullptr, nullptr, nullptr }
 };
 
+#if 0
 static const KeybindingRecord CHEAT_KEYS[] = {
 	{ KEYBIND_CHEAT_GOTO, "CHEAT-GOTO", _s("Goto location"), "A+g", nullptr },
 	{ KEYBIND_NONE, nullptr, nullptr, nullptr, nullptr }
 };
+#endif
 
 static const char *const kMenuKeymapId = "mm1_menu";
 static const char *const kMinimalKeymapId = "mm1_minimal";
@@ -154,7 +156,9 @@ static const KeysRecord ALL_RECORDS[] = {
 	{ kNormalKeymapId,  _s("Might and Magic 1 - Main"), NORMAL_KEYS },
 	{ kPartyKeymapId,   _s("Might and Magic 1 - Party"), PARTY_KEYS },
 	{ kCombatKeymapId,  _s("Might and Magic 1 - Combat"), COMBAT_KEYS },
-//	{ kCheatsKeymapId,  _s("Might and Magic 1 - Cheats"), CHEAT_KEYS },
+#if 0
+	{ kCheatsKeymapId,  _s("Might and Magic 1 - Cheats"), CHEAT_KEYS },
+#endif
 	{ nullptr, nullptr, nullptr }
 };
 
diff --git a/engines/mm/mm1/views_enh/create_characters.cpp b/engines/mm/mm1/views_enh/create_characters.cpp
index 27109894929..0620b73a30a 100644
--- a/engines/mm/mm1/views_enh/create_characters.cpp
+++ b/engines/mm/mm1/views_enh/create_characters.cpp
@@ -74,7 +74,6 @@ static void loadSuggestedNames() {
 		return;
 	}
 
-	int invalidLines = 0;
 	while (!f.eos()) {
 		Common::String line = f.readLine();
 		line.trim();
@@ -84,10 +83,8 @@ static void loadSuggestedNames() {
 		size_t p1 = line.findFirstOf('\t');
 		size_t p2 = p1 == Common::String::npos ? Common::String::npos :
 			line.findFirstOf('\t', p1 + 1);
-		if (p1 == Common::String::npos || p2 == Common::String::npos) {
-			++invalidLines;
+		if (p1 == Common::String::npos || p2 == Common::String::npos)
 			continue;
-		}
 
 		int classId = atoi(Common::String(line.c_str(), line.c_str() + p1).c_str());
 		int sexId = atoi(Common::String(line.c_str() + p1 + 1, line.c_str() + p2).c_str());


Commit: 8775c6b489e4b15108642162eb45270a55aa89c0
    https://github.com/scummvm/scummvm/commit/8775c6b489e4b15108642162eb45270a55aa89c0
Author: Donovan Watteau (contrib at dwatteau.fr)
Date: 2026-06-18T11:39:40+02:00

Commit Message:
TWINE: Silence -Wunused-private-field warning

Changed paths:
    engines/twine/holomap_v2.cpp


diff --git a/engines/twine/holomap_v2.cpp b/engines/twine/holomap_v2.cpp
index 6c9dc9b6835..80887ea3637 100644
--- a/engines/twine/holomap_v2.cpp
+++ b/engines/twine/holomap_v2.cpp
@@ -342,6 +342,7 @@ void HolomapV2::holoMap() {
 	_flagPal = true;
 	_flagHoloEnd = false;
 	_numObjectif = -1;
+	_oldObjectif = -1;
 
 	_engine->_input->enableKeyMap(holomapKeyMapId);
 



From noreply at scummvm.org  Thu Jun 18 09:40:38 2026
From: noreply at scummvm.org (ScummVM-Translations)
Date: Thu, 18 Jun 2026 09:40:38 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 e6c719e692fd4323612958c19f0d8665fa4a86c3
Message-ID: <20260618094038.8A1827005CD@ron.scummvm.net>

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

Summary:
e6c719e692 I18N: Update translations templates


Commit: e6c719e692fd4323612958c19f0d8665fa4a86c3
    https://github.com/scummvm/scummvm/commit/e6c719e692fd4323612958c19f0d8665fa4a86c3
Author: Weblate (noreply at weblate.org)
Date: 2026-06-18T09:40:34Z

Commit Message:
I18N: Update translations templates

Changed paths:
    po/scummvm.pot


diff --git a/po/scummvm.pot b/po/scummvm.pot
index fad71845a85..42843193ffa 100644
--- a/po/scummvm.pot
+++ b/po/scummvm.pot
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2026.2.1git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-18 09:25+0000\n"
+"POT-Creation-Date: 2026-06-18 09:40+0000\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL at ADDRESS>\n"
 "Language-Team: LANGUAGE <LL at li.org>\n"
@@ -10262,30 +10262,34 @@ msgstr ""
 msgid "Unlock"
 msgstr ""
 
-#: engines/mm/mm1/metaengine.cpp:127 engines/ultima/ultima4/metaengine.cpp:112
+#: engines/mm/mm1/metaengine.cpp:128 engines/ultima/ultima4/metaengine.cpp:112
 msgid "Goto location"
 msgstr ""
 
-#: engines/mm/mm1/metaengine.cpp:152
+#: engines/mm/mm1/metaengine.cpp:154
 msgid "Might and Magic 1 - Menus"
 msgstr ""
 
-#: engines/mm/mm1/metaengine.cpp:153
+#: engines/mm/mm1/metaengine.cpp:155
 msgid "Might and Magic 1 - Minimal Keys"
 msgstr ""
 
-#: engines/mm/mm1/metaengine.cpp:154
+#: engines/mm/mm1/metaengine.cpp:156
 msgid "Might and Magic 1 - Main"
 msgstr ""
 
-#: engines/mm/mm1/metaengine.cpp:155
+#: engines/mm/mm1/metaengine.cpp:157
 msgid "Might and Magic 1 - Party"
 msgstr ""
 
-#: engines/mm/mm1/metaengine.cpp:156
+#: engines/mm/mm1/metaengine.cpp:158
 msgid "Might and Magic 1 - Combat"
 msgstr ""
 
+#: engines/mm/mm1/metaengine.cpp:160
+msgid "Might and Magic 1 - Cheats"
+msgstr ""
+
 #: engines/mm/mm1/mm1.cpp:57
 msgid ""
 "You cannot run the game directly from the Graphics Overhaul Mod. Instead, it "



From noreply at scummvm.org  Thu Jun 18 09:41:12 2026
From: noreply at scummvm.org (ScummVM-Translations)
Date: Thu, 18 Jun 2026 09:41:12 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 fc4a787cdf94cdf9c7847a534ab1ea0180718c2c
Message-ID: <20260618094112.548057005CC@ron.scummvm.net>

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

Summary:
fc4a787cdf I18N: Update translation files


Commit: fc4a787cdf94cdf9c7847a534ab1ea0180718c2c
    https://github.com/scummvm/scummvm/commit/fc4a787cdf94cdf9c7847a534ab1ea0180718c2c
Author: Weblate (noreply at weblate.org)
Date: 2026-06-18T09:40:52Z

Commit Message:
I18N: Update translation files

Updated by "Update PO files to match POT (msgmerge)" hook in Weblate.

Translation: ScummVM/scummvm
Translate-URL: https://translations.scummvm.org/projects/scummvm/scummvm/

Changed paths:
    po/ar.po
    po/be-tarask.po
    po/be_BY.po
    po/ca_ES.po
    po/cs_CZ.po
    po/da.po
    po/de_DE.po
    po/el.po
    po/es_ES.po
    po/eu.po
    po/fi_FI.po
    po/fr_FR.po
    po/gl_ES.po
    po/he.po
    po/hi.po
    po/hu_HU.po
    po/it_IT.po
    po/ja.po
    po/ka.po
    po/ko.po
    po/nb_NO.po
    po/nl_NL.po
    po/nn_NO.po
    po/pl_PL.po
    po/pt_BR.po
    po/pt_PT.po
    po/ro.po
    po/ru_RU.po
    po/sv_SE.po
    po/tr.po
    po/uk_UA.po
    po/zh.po
    po/zh_Hans.po
    po/zh_Hant.po


diff --git a/po/ar.po b/po/ar.po
index 25447269051..d794f4dbc2d 100644
--- a/po/ar.po
+++ b/po/ar.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-18 09:25+0000\n"
+"POT-Creation-Date: 2026-06-18 09:40+0000\n"
 "PO-Revision-Date: 2026-02-24 22:26+0000\n"
 "Last-Translator: Mohamed Shaaban <mohmedbashamedo at hotmail.com>\n"
 "Language-Team: Arabic <https://translations.scummvm.org/projects/scummvm/"
@@ -11712,34 +11712,38 @@ msgstr "??????"
 msgid "Unlock"
 msgstr "?????? ??????????"
 
-#: engines/mm/mm1/metaengine.cpp:127 engines/ultima/ultima4/metaengine.cpp:112
+#: engines/mm/mm1/metaengine.cpp:128 engines/ultima/ultima4/metaengine.cpp:112
 msgid "Goto location"
 msgstr "?????????? ?????? ????????????"
 
-#: engines/mm/mm1/metaengine.cpp:152
+#: engines/mm/mm1/metaengine.cpp:154
 msgid "Might and Magic 1 - Menus"
 msgstr "?????????? ???????????? 1 - ??????????????"
 
-#: engines/mm/mm1/metaengine.cpp:153
+#: engines/mm/mm1/metaengine.cpp:155
 #, fuzzy
 #| msgid "Might and Magic 1 - Menus"
 msgid "Might and Magic 1 - Minimal Keys"
 msgstr "?????????? ???????????? 1 - ??????????????"
 
-#: engines/mm/mm1/metaengine.cpp:154
+#: engines/mm/mm1/metaengine.cpp:156
 #, fuzzy
 #| msgid "Might and Magic 1 - Menus"
 msgid "Might and Magic 1 - Main"
 msgstr "?????????? ???????????? 1 - ??????????????"
 
-#: engines/mm/mm1/metaengine.cpp:155
+#: engines/mm/mm1/metaengine.cpp:157
 msgid "Might and Magic 1 - Party"
 msgstr "?????????? ???????????? 1 - ????????????"
 
-#: engines/mm/mm1/metaengine.cpp:156
+#: engines/mm/mm1/metaengine.cpp:158
 msgid "Might and Magic 1 - Combat"
 msgstr "?????????? ???????????? 1 - ????????????"
 
+#: engines/mm/mm1/metaengine.cpp:160
+msgid "Might and Magic 1 - Cheats"
+msgstr "?????????? ???????????? 1 - ????"
+
 #: engines/mm/mm1/mm1.cpp:57
 msgid ""
 "You cannot run the game directly from the Graphics Overhaul Mod. Instead, it "
@@ -18524,9 +18528,6 @@ msgstr ""
 #~ msgid "N"
 #~ msgstr "????"
 
-#~ msgid "Might and Magic 1 - Cheats"
-#~ msgstr "?????????? ???????????? 1 - ????"
-
 #~ msgid "3DO Plumbers requires RGB support."
 #~ msgstr "?????????? 3DO Plumbers ?????? RGB."
 
diff --git a/po/be-tarask.po b/po/be-tarask.po
index 8fe4cca0063..b612a198d20 100644
--- a/po/be-tarask.po
+++ b/po/be-tarask.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-18 09:25+0000\n"
+"POT-Creation-Date: 2026-06-18 09:40+0000\n"
 "PO-Revision-Date: 2023-02-26 15:01+0000\n"
 "Last-Translator: Kastu?? K. <oakum2334 at gmail.com>\n"
 "Language-Team: Belarusian (Tara??kievica) <https://translations.scummvm.org/"
@@ -11664,32 +11664,36 @@ msgstr ""
 msgid "Unlock"
 msgstr "????????????????"
 
-#: engines/mm/mm1/metaengine.cpp:127 engines/ultima/ultima4/metaengine.cpp:112
+#: engines/mm/mm1/metaengine.cpp:128 engines/ultima/ultima4/metaengine.cpp:112
 #, fuzzy
 #| msgid "Next location"
 msgid "Goto location"
 msgstr "?????????????????? ??????????????"
 
-#: engines/mm/mm1/metaengine.cpp:152
+#: engines/mm/mm1/metaengine.cpp:154
 msgid "Might and Magic 1 - Menus"
 msgstr ""
 
-#: engines/mm/mm1/metaengine.cpp:153
+#: engines/mm/mm1/metaengine.cpp:155
 msgid "Might and Magic 1 - Minimal Keys"
 msgstr ""
 
-#: engines/mm/mm1/metaengine.cpp:154
+#: engines/mm/mm1/metaengine.cpp:156
 msgid "Might and Magic 1 - Main"
 msgstr ""
 
-#: engines/mm/mm1/metaengine.cpp:155
+#: engines/mm/mm1/metaengine.cpp:157
 msgid "Might and Magic 1 - Party"
 msgstr ""
 
-#: engines/mm/mm1/metaengine.cpp:156
+#: engines/mm/mm1/metaengine.cpp:158
 msgid "Might and Magic 1 - Combat"
 msgstr ""
 
+#: engines/mm/mm1/metaengine.cpp:160
+msgid "Might and Magic 1 - Cheats"
+msgstr ""
+
 #: engines/mm/mm1/mm1.cpp:57
 msgid ""
 "You cannot run the game directly from the Graphics Overhaul Mod. Instead, it "
diff --git a/po/be_BY.po b/po/be_BY.po
index 99e4f4416e3..cce7b71d98e 100644
--- a/po/be_BY.po
+++ b/po/be_BY.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-18 09:25+0000\n"
+"POT-Creation-Date: 2026-06-18 09:40+0000\n"
 "PO-Revision-Date: 2021-03-21 08:24+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Belarusian <https://translations.scummvm.org/projects/scummvm/"
@@ -11538,31 +11538,35 @@ msgstr ""
 msgid "Unlock"
 msgstr "????????????????"
 
-#: engines/mm/mm1/metaengine.cpp:127 engines/ultima/ultima4/metaengine.cpp:112
+#: engines/mm/mm1/metaengine.cpp:128 engines/ultima/ultima4/metaengine.cpp:112
 #, fuzzy
 msgid "Goto location"
 msgstr "?????????????????? ??????????????"
 
-#: engines/mm/mm1/metaengine.cpp:152
+#: engines/mm/mm1/metaengine.cpp:154
 msgid "Might and Magic 1 - Menus"
 msgstr ""
 
-#: engines/mm/mm1/metaengine.cpp:153
+#: engines/mm/mm1/metaengine.cpp:155
 msgid "Might and Magic 1 - Minimal Keys"
 msgstr ""
 
-#: engines/mm/mm1/metaengine.cpp:154
+#: engines/mm/mm1/metaengine.cpp:156
 msgid "Might and Magic 1 - Main"
 msgstr ""
 
-#: engines/mm/mm1/metaengine.cpp:155
+#: engines/mm/mm1/metaengine.cpp:157
 msgid "Might and Magic 1 - Party"
 msgstr ""
 
-#: engines/mm/mm1/metaengine.cpp:156
+#: engines/mm/mm1/metaengine.cpp:158
 msgid "Might and Magic 1 - Combat"
 msgstr ""
 
+#: engines/mm/mm1/metaengine.cpp:160
+msgid "Might and Magic 1 - Cheats"
+msgstr ""
+
 #: engines/mm/mm1/mm1.cpp:57
 msgid ""
 "You cannot run the game directly from the Graphics Overhaul Mod. Instead, it "
diff --git a/po/ca_ES.po b/po/ca_ES.po
index 2c27f0ecc59..bfae1608a35 100644
--- a/po/ca_ES.po
+++ b/po/ca_ES.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.6.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-18 09:25+0000\n"
+"POT-Creation-Date: 2026-06-18 09:40+0000\n"
 "PO-Revision-Date: 2026-05-26 04:22+0000\n"
 "Last-Translator: Xaviu <xaviu at xaviu.com>\n"
 "Language-Team: Catalan <https://translations.scummvm.org/projects/scummvm/"
@@ -10817,30 +10817,34 @@ msgstr "Colpejar"
 msgid "Unlock"
 msgstr "Desbloqueja"
 
-#: engines/mm/mm1/metaengine.cpp:127 engines/ultima/ultima4/metaengine.cpp:112
+#: engines/mm/mm1/metaengine.cpp:128 engines/ultima/ultima4/metaengine.cpp:112
 msgid "Goto location"
 msgstr "Ves a la ubicaci??"
 
-#: engines/mm/mm1/metaengine.cpp:152
+#: engines/mm/mm1/metaengine.cpp:154
 msgid "Might and Magic 1 - Menus"
 msgstr "Might and Magic 1 - Men??s"
 
-#: engines/mm/mm1/metaengine.cpp:153
+#: engines/mm/mm1/metaengine.cpp:155
 msgid "Might and Magic 1 - Minimal Keys"
 msgstr "Might and Magic 1 - Tecles m??nimes"
 
-#: engines/mm/mm1/metaengine.cpp:154
+#: engines/mm/mm1/metaengine.cpp:156
 msgid "Might and Magic 1 - Main"
 msgstr "Might and Magic 1 - Principal"
 
-#: engines/mm/mm1/metaengine.cpp:155
+#: engines/mm/mm1/metaengine.cpp:157
 msgid "Might and Magic 1 - Party"
 msgstr "Might and Magic 1 - Grup"
 
-#: engines/mm/mm1/metaengine.cpp:156
+#: engines/mm/mm1/metaengine.cpp:158
 msgid "Might and Magic 1 - Combat"
 msgstr "Might and Magic 1 - Combat"
 
+#: engines/mm/mm1/metaengine.cpp:160
+msgid "Might and Magic 1 - Cheats"
+msgstr "Might and Magic 1 - Trucs"
+
 #: engines/mm/mm1/mm1.cpp:57
 msgid ""
 "You cannot run the game directly from the Graphics Overhaul Mod. Instead, it "
@@ -16696,9 +16700,6 @@ msgstr ""
 #~ msgid "N"
 #~ msgstr "No"
 
-#~ msgid "Might and Magic 1 - Cheats"
-#~ msgstr "Might and Magic 1 - Trucs"
-
 #~ msgid "Load game:"
 #~ msgstr "Carrega partida:"
 
diff --git a/po/cs_CZ.po b/po/cs_CZ.po
index f634dcb8f07..32a7ebc8744 100644
--- a/po/cs_CZ.po
+++ b/po/cs_CZ.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.7.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-18 09:25+0000\n"
+"POT-Creation-Date: 2026-06-18 09:40+0000\n"
 "PO-Revision-Date: 2026-02-09 20:22+0000\n"
 "Last-Translator: AsciiWolf <mail at asciiwolf.com>\n"
 "Language-Team: Czech <https://translations.scummvm.org/projects/scummvm/"
@@ -11379,31 +11379,35 @@ msgstr ""
 msgid "Unlock"
 msgstr "Odemknout"
 
-#: engines/mm/mm1/metaengine.cpp:127 engines/ultima/ultima4/metaengine.cpp:112
+#: engines/mm/mm1/metaengine.cpp:128 engines/ultima/ultima4/metaengine.cpp:112
 #, fuzzy
 msgid "Goto location"
 msgstr "~P~??edchoz??"
 
-#: engines/mm/mm1/metaengine.cpp:152
+#: engines/mm/mm1/metaengine.cpp:154
 msgid "Might and Magic 1 - Menus"
 msgstr ""
 
-#: engines/mm/mm1/metaengine.cpp:153
+#: engines/mm/mm1/metaengine.cpp:155
 msgid "Might and Magic 1 - Minimal Keys"
 msgstr ""
 
-#: engines/mm/mm1/metaengine.cpp:154
+#: engines/mm/mm1/metaengine.cpp:156
 msgid "Might and Magic 1 - Main"
 msgstr ""
 
-#: engines/mm/mm1/metaengine.cpp:155
+#: engines/mm/mm1/metaengine.cpp:157
 msgid "Might and Magic 1 - Party"
 msgstr ""
 
-#: engines/mm/mm1/metaengine.cpp:156
+#: engines/mm/mm1/metaengine.cpp:158
 msgid "Might and Magic 1 - Combat"
 msgstr ""
 
+#: engines/mm/mm1/metaengine.cpp:160
+msgid "Might and Magic 1 - Cheats"
+msgstr ""
+
 #: engines/mm/mm1/mm1.cpp:57
 msgid ""
 "You cannot run the game directly from the Graphics Overhaul Mod. Instead, it "
diff --git a/po/da.po b/po/da.po
index c8c69093966..b2698d3fa1f 100644
--- a/po/da.po
+++ b/po/da.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-18 09:25+0000\n"
+"POT-Creation-Date: 2026-06-18 09:40+0000\n"
 "PO-Revision-Date: 2023-09-07 17:04+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Danish <https://translations.scummvm.org/projects/scummvm/"
@@ -11442,31 +11442,35 @@ msgstr ""
 msgid "Unlock"
 msgstr "L??s op"
 
-#: engines/mm/mm1/metaengine.cpp:127 engines/ultima/ultima4/metaengine.cpp:112
+#: engines/mm/mm1/metaengine.cpp:128 engines/ultima/ultima4/metaengine.cpp:112
 #, fuzzy
 msgid "Goto location"
 msgstr "Fo~r~rige"
 
-#: engines/mm/mm1/metaengine.cpp:152
+#: engines/mm/mm1/metaengine.cpp:154
 msgid "Might and Magic 1 - Menus"
 msgstr ""
 
-#: engines/mm/mm1/metaengine.cpp:153
+#: engines/mm/mm1/metaengine.cpp:155
 msgid "Might and Magic 1 - Minimal Keys"
 msgstr ""
 
-#: engines/mm/mm1/metaengine.cpp:154
+#: engines/mm/mm1/metaengine.cpp:156
 msgid "Might and Magic 1 - Main"
 msgstr ""
 
-#: engines/mm/mm1/metaengine.cpp:155
+#: engines/mm/mm1/metaengine.cpp:157
 msgid "Might and Magic 1 - Party"
 msgstr ""
 
-#: engines/mm/mm1/metaengine.cpp:156
+#: engines/mm/mm1/metaengine.cpp:158
 msgid "Might and Magic 1 - Combat"
 msgstr ""
 
+#: engines/mm/mm1/metaengine.cpp:160
+msgid "Might and Magic 1 - Cheats"
+msgstr ""
+
 #: engines/mm/mm1/mm1.cpp:57
 msgid ""
 "You cannot run the game directly from the Graphics Overhaul Mod. Instead, it "
diff --git a/po/de_DE.po b/po/de_DE.po
index 28a025b49de..4af3543874a 100644
--- a/po/de_DE.po
+++ b/po/de_DE.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.10.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-18 09:25+0000\n"
+"POT-Creation-Date: 2026-06-18 09:40+0000\n"
 "PO-Revision-Date: 2026-05-05 16:31+0000\n"
 "Last-Translator: Lothar Serra Mari <lothar.serramari at scummvm.org>\n"
 "Language-Team: German <https://translations.scummvm.org/projects/scummvm/"
@@ -11340,30 +11340,34 @@ msgstr "Schlagen"
 msgid "Unlock"
 msgstr "Schlie?? auf"
 
-#: engines/mm/mm1/metaengine.cpp:127 engines/ultima/ultima4/metaengine.cpp:112
+#: engines/mm/mm1/metaengine.cpp:128 engines/ultima/ultima4/metaengine.cpp:112
 msgid "Goto location"
 msgstr "Zu Ort gehen"
 
-#: engines/mm/mm1/metaengine.cpp:152
+#: engines/mm/mm1/metaengine.cpp:154
 msgid "Might and Magic 1 - Menus"
 msgstr "Might and Magic 1 - Men??s"
 
-#: engines/mm/mm1/metaengine.cpp:153
+#: engines/mm/mm1/metaengine.cpp:155
 msgid "Might and Magic 1 - Minimal Keys"
 msgstr "Might and Magic 1 - Minimale Tasten"
 
-#: engines/mm/mm1/metaengine.cpp:154
+#: engines/mm/mm1/metaengine.cpp:156
 msgid "Might and Magic 1 - Main"
 msgstr "Might and Magic 1 - Hauptmen??"
 
-#: engines/mm/mm1/metaengine.cpp:155
+#: engines/mm/mm1/metaengine.cpp:157
 msgid "Might and Magic 1 - Party"
 msgstr "Might and Magic 1 - Team"
 
-#: engines/mm/mm1/metaengine.cpp:156
+#: engines/mm/mm1/metaengine.cpp:158
 msgid "Might and Magic 1 - Combat"
 msgstr "Might and Magic 1 - Kampf"
 
+#: engines/mm/mm1/metaengine.cpp:160
+msgid "Might and Magic 1 - Cheats"
+msgstr "Might and Magic 1 - Cheats"
+
 #: engines/mm/mm1/mm1.cpp:57
 msgid ""
 "You cannot run the game directly from the Graphics Overhaul Mod. Instead, it "
@@ -17560,9 +17564,6 @@ msgstr ""
 #~ msgid "N"
 #~ msgstr "Nein"
 
-#~ msgid "Might and Magic 1 - Cheats"
-#~ msgstr "Might and Magic 1 - Cheats"
-
 #~ msgid "3DO Plumbers requires RGB support."
 #~ msgstr "Die 3DO-Version von Plumbers ben??tigt RGB-Unterst??tzung."
 
diff --git a/po/el.po b/po/el.po
index 6822cfae29d..ccdc4c8decc 100644
--- a/po/el.po
+++ b/po/el.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.10.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-18 09:25+0000\n"
+"POT-Creation-Date: 2026-06-18 09:40+0000\n"
 "PO-Revision-Date: 2026-04-23 00:22+0000\n"
 "Last-Translator: Antoniou Athanasios <a.antoniou79 at gmail.com>\n"
 "Language-Team: Greek <https://translations.scummvm.org/projects/scummvm/"
@@ -11470,32 +11470,36 @@ msgstr "??????????"
 msgid "Unlock"
 msgstr "????????????????????"
 
-#: engines/mm/mm1/metaengine.cpp:127 engines/ultima/ultima4/metaengine.cpp:112
+#: engines/mm/mm1/metaengine.cpp:128 engines/ultima/ultima4/metaengine.cpp:112
 msgid "Goto location"
 msgstr "???????????????? ???????? ??????????????"
 
-#: engines/mm/mm1/metaengine.cpp:152
+#: engines/mm/mm1/metaengine.cpp:154
 msgid "Might and Magic 1 - Menus"
 msgstr "Might and Magic 1 - ??????????"
 
-#: engines/mm/mm1/metaengine.cpp:153
+#: engines/mm/mm1/metaengine.cpp:155
 msgid "Might and Magic 1 - Minimal Keys"
 msgstr "Might and Magic 1 - ???????????????? ??????????????"
 
-#: engines/mm/mm1/metaengine.cpp:154
+#: engines/mm/mm1/metaengine.cpp:156
 #, fuzzy
 #| msgid "Might and Magic 1 - Menus"
 msgid "Might and Magic 1 - Main"
 msgstr "Might and Magic 1 - ??????????"
 
-#: engines/mm/mm1/metaengine.cpp:155
+#: engines/mm/mm1/metaengine.cpp:157
 msgid "Might and Magic 1 - Party"
 msgstr "Might and Magic 1 - ??????????"
 
-#: engines/mm/mm1/metaengine.cpp:156
+#: engines/mm/mm1/metaengine.cpp:158
 msgid "Might and Magic 1 - Combat"
 msgstr "Might and Magic 1 - ????????"
 
+#: engines/mm/mm1/metaengine.cpp:160
+msgid "Might and Magic 1 - Cheats"
+msgstr "Might and Magic 1 - ?????????? (Cheats)"
+
 #: engines/mm/mm1/mm1.cpp:57
 msgid ""
 "You cannot run the game directly from the Graphics Overhaul Mod. Instead, it "
@@ -17848,9 +17852,6 @@ msgstr ""
 #~ msgid "N"
 #~ msgstr "??????"
 
-#~ msgid "Might and Magic 1 - Cheats"
-#~ msgstr "Might and Magic 1 - ?????????? (Cheats)"
-
 #~ msgid "3DO Plumbers requires RGB support."
 #~ msgstr "?? ???????????? ?????? Plumbers ?????? 3DO ?????????????? ???????????????????? RGB."
 
diff --git a/po/es_ES.po b/po/es_ES.po
index 66e31fe033c..a7d5ba8720b 100644
--- a/po/es_ES.po
+++ b/po/es_ES.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.4.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-18 09:25+0000\n"
+"POT-Creation-Date: 2026-06-18 09:40+0000\n"
 "PO-Revision-Date: 2026-06-14 09:53+0000\n"
 "Last-Translator: IlDucci <pajaroloco_2 at hotmail.com>\n"
 "Language-Team: Spanish <https://translations.scummvm.org/projects/scummvm/"
@@ -11312,30 +11312,34 @@ msgstr "Golpear"
 msgid "Unlock"
 msgstr "Abrir con llave"
 
-#: engines/mm/mm1/metaengine.cpp:127 engines/ultima/ultima4/metaengine.cpp:112
+#: engines/mm/mm1/metaengine.cpp:128 engines/ultima/ultima4/metaengine.cpp:112
 msgid "Goto location"
 msgstr "Ir a lugar"
 
-#: engines/mm/mm1/metaengine.cpp:152
+#: engines/mm/mm1/metaengine.cpp:154
 msgid "Might and Magic 1 - Menus"
 msgstr "Might and Magic 1 - Men??s"
 
-#: engines/mm/mm1/metaengine.cpp:153
+#: engines/mm/mm1/metaengine.cpp:155
 msgid "Might and Magic 1 - Minimal Keys"
 msgstr "Might and Magic 1 - Teclas b??sicas"
 
-#: engines/mm/mm1/metaengine.cpp:154
+#: engines/mm/mm1/metaengine.cpp:156
 msgid "Might and Magic 1 - Main"
 msgstr "Might and Magic 1 - Principales"
 
-#: engines/mm/mm1/metaengine.cpp:155
+#: engines/mm/mm1/metaengine.cpp:157
 msgid "Might and Magic 1 - Party"
 msgstr "Might and Magic 1 - Grupo"
 
-#: engines/mm/mm1/metaengine.cpp:156
+#: engines/mm/mm1/metaengine.cpp:158
 msgid "Might and Magic 1 - Combat"
 msgstr "Might and Magic 1 - Combate"
 
+#: engines/mm/mm1/metaengine.cpp:160
+msgid "Might and Magic 1 - Cheats"
+msgstr "Might and Magic 1 - Trucos"
+
 #: engines/mm/mm1/mm1.cpp:57
 msgid ""
 "You cannot run the game directly from the Graphics Overhaul Mod. Instead, it "
@@ -17461,9 +17465,6 @@ msgstr ""
 #~ msgid "N"
 #~ msgstr "N"
 
-#~ msgid "Might and Magic 1 - Cheats"
-#~ msgstr "Might and Magic 1 - Trucos"
-
 #~ msgid "3DO Plumbers requires RGB support."
 #~ msgstr "Plumbers para 3DO require soporte para RGB."
 
diff --git a/po/eu.po b/po/eu.po
index 01a056caa7b..f0460e40dc4 100644
--- a/po/eu.po
+++ b/po/eu.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.5.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-18 09:25+0000\n"
+"POT-Creation-Date: 2026-06-18 09:40+0000\n"
 "PO-Revision-Date: 2024-02-05 18:42+0000\n"
 "Last-Translator: Josu Igoa <josuigoa at ni.eus>\n"
 "Language-Team: Basque <https://translations.scummvm.org/projects/scummvm/"
@@ -11464,31 +11464,35 @@ msgstr ""
 msgid "Unlock"
 msgstr "Ireki"
 
-#: engines/mm/mm1/metaengine.cpp:127 engines/ultima/ultima4/metaengine.cpp:112
+#: engines/mm/mm1/metaengine.cpp:128 engines/ultima/ultima4/metaengine.cpp:112
 #, fuzzy
 msgid "Goto location"
 msgstr "~A~urrekoa"
 
-#: engines/mm/mm1/metaengine.cpp:152
+#: engines/mm/mm1/metaengine.cpp:154
 msgid "Might and Magic 1 - Menus"
 msgstr ""
 
-#: engines/mm/mm1/metaengine.cpp:153
+#: engines/mm/mm1/metaengine.cpp:155
 msgid "Might and Magic 1 - Minimal Keys"
 msgstr ""
 
-#: engines/mm/mm1/metaengine.cpp:154
+#: engines/mm/mm1/metaengine.cpp:156
 msgid "Might and Magic 1 - Main"
 msgstr ""
 
-#: engines/mm/mm1/metaengine.cpp:155
+#: engines/mm/mm1/metaengine.cpp:157
 msgid "Might and Magic 1 - Party"
 msgstr ""
 
-#: engines/mm/mm1/metaengine.cpp:156
+#: engines/mm/mm1/metaengine.cpp:158
 msgid "Might and Magic 1 - Combat"
 msgstr ""
 
+#: engines/mm/mm1/metaengine.cpp:160
+msgid "Might and Magic 1 - Cheats"
+msgstr ""
+
 #: engines/mm/mm1/mm1.cpp:57
 msgid ""
 "You cannot run the game directly from the Graphics Overhaul Mod. Instead, it "
diff --git a/po/fi_FI.po b/po/fi_FI.po
index 65dc89438dc..7924b8320b7 100644
--- a/po/fi_FI.po
+++ b/po/fi_FI.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.6.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-18 09:25+0000\n"
+"POT-Creation-Date: 2026-06-18 09:40+0000\n"
 "PO-Revision-Date: 2026-02-07 15:22+0000\n"
 "Last-Translator: Timo Mikkolainen <tmikkola at gmail.com>\n"
 "Language-Team: Finnish <https://translations.scummvm.org/projects/scummvm/"
@@ -11545,32 +11545,36 @@ msgstr "Runno"
 msgid "Unlock"
 msgstr "Avaa lukko"
 
-#: engines/mm/mm1/metaengine.cpp:127 engines/ultima/ultima4/metaengine.cpp:112
+#: engines/mm/mm1/metaengine.cpp:128 engines/ultima/ultima4/metaengine.cpp:112
 msgid "Goto location"
 msgstr "Mene sijaintiin"
 
-#: engines/mm/mm1/metaengine.cpp:152
+#: engines/mm/mm1/metaengine.cpp:154
 msgid "Might and Magic 1 - Menus"
 msgstr "Might and Magic 1 - Valikot"
 
-#: engines/mm/mm1/metaengine.cpp:153
+#: engines/mm/mm1/metaengine.cpp:155
 msgid "Might and Magic 1 - Minimal Keys"
 msgstr "Might and Magic 1 - Minimaaliset N??pp??imet"
 
-#: engines/mm/mm1/metaengine.cpp:154
+#: engines/mm/mm1/metaengine.cpp:156
 #, fuzzy
 #| msgid "Might and Magic 1 - Menus"
 msgid "Might and Magic 1 - Main"
 msgstr "Might and Magic 1 - Valikot"
 
-#: engines/mm/mm1/metaengine.cpp:155
+#: engines/mm/mm1/metaengine.cpp:157
 msgid "Might and Magic 1 - Party"
 msgstr "Might and Magic 1 - Ryhm??"
 
-#: engines/mm/mm1/metaengine.cpp:156
+#: engines/mm/mm1/metaengine.cpp:158
 msgid "Might and Magic 1 - Combat"
 msgstr "Might and Magic 1 - Taistelu"
 
+#: engines/mm/mm1/metaengine.cpp:160
+msgid "Might and Magic 1 - Cheats"
+msgstr "Might and Magic 1 - Huijaukset"
+
 #: engines/mm/mm1/mm1.cpp:57
 msgid ""
 "You cannot run the game directly from the Graphics Overhaul Mod. Instead, it "
@@ -17876,9 +17880,6 @@ msgstr ""
 #~ msgid "N"
 #~ msgstr "Ei"
 
-#~ msgid "Might and Magic 1 - Cheats"
-#~ msgstr "Might and Magic 1 - Huijaukset"
-
 #~ msgid "3DO Plumbers requires RGB support."
 #~ msgstr "3DO Plumbers vaatii RGB-tuen."
 
diff --git a/po/fr_FR.po b/po/fr_FR.po
index 85e039c6230..f8d3f159d72 100644
--- a/po/fr_FR.po
+++ b/po/fr_FR.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-18 09:25+0000\n"
+"POT-Creation-Date: 2026-06-18 09:40+0000\n"
 "PO-Revision-Date: 2026-06-14 09:41+0000\n"
 "Last-Translator: Purple T <ZEONK at hotmail.com>\n"
 "Language-Team: French <https://translations.scummvm.org/projects/scummvm/"
@@ -11323,30 +11323,34 @@ msgstr "Enfoncer"
 msgid "Unlock"
 msgstr "D??verrouiller"
 
-#: engines/mm/mm1/metaengine.cpp:127 engines/ultima/ultima4/metaengine.cpp:112
+#: engines/mm/mm1/metaengine.cpp:128 engines/ultima/ultima4/metaengine.cpp:112
 msgid "Goto location"
 msgstr "Aller vers lieu"
 
-#: engines/mm/mm1/metaengine.cpp:152
+#: engines/mm/mm1/metaengine.cpp:154
 msgid "Might and Magic 1 - Menus"
 msgstr "Might and Magic 1 - Menus"
 
-#: engines/mm/mm1/metaengine.cpp:153
+#: engines/mm/mm1/metaengine.cpp:155
 msgid "Might and Magic 1 - Minimal Keys"
 msgstr "Might and Magic 1 - Touches r??duites"
 
-#: engines/mm/mm1/metaengine.cpp:154
+#: engines/mm/mm1/metaengine.cpp:156
 msgid "Might and Magic 1 - Main"
 msgstr "Might and Magic 1 - Principal"
 
-#: engines/mm/mm1/metaengine.cpp:155
+#: engines/mm/mm1/metaengine.cpp:157
 msgid "Might and Magic 1 - Party"
 msgstr "Might and Magic 1 - Groupe"
 
-#: engines/mm/mm1/metaengine.cpp:156
+#: engines/mm/mm1/metaengine.cpp:158
 msgid "Might and Magic 1 - Combat"
 msgstr "Might and Magic 1 - Combat"
 
+#: engines/mm/mm1/metaengine.cpp:160
+msgid "Might and Magic 1 - Cheats"
+msgstr "Might and Magic 1 - Triches"
+
 #: engines/mm/mm1/mm1.cpp:57
 msgid ""
 "You cannot run the game directly from the Graphics Overhaul Mod. Instead, it "
@@ -17513,9 +17517,6 @@ msgstr ""
 #~ msgid "N"
 #~ msgstr "N"
 
-#~ msgid "Might and Magic 1 - Cheats"
-#~ msgstr "Might and Magic 1 - Triches"
-
 #~ msgid "3DO Plumbers requires RGB support."
 #~ msgstr "Plumbers 3DO n??cessite le support du RGB."
 
diff --git a/po/gl_ES.po b/po/gl_ES.po
index c5b9f10eefc..9e26572bc33 100644
--- a/po/gl_ES.po
+++ b/po/gl_ES.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-18 09:25+0000\n"
+"POT-Creation-Date: 2026-06-18 09:40+0000\n"
 "PO-Revision-Date: 2020-09-26 21:13+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Galician <https://translations.scummvm.org/projects/scummvm/"
@@ -11384,31 +11384,35 @@ msgstr ""
 msgid "Unlock"
 msgstr "Despechar"
 
-#: engines/mm/mm1/metaengine.cpp:127 engines/ultima/ultima4/metaengine.cpp:112
+#: engines/mm/mm1/metaengine.cpp:128 engines/ultima/ultima4/metaengine.cpp:112
 #, fuzzy
 msgid "Goto location"
 msgstr "~A~nterior"
 
-#: engines/mm/mm1/metaengine.cpp:152
+#: engines/mm/mm1/metaengine.cpp:154
 msgid "Might and Magic 1 - Menus"
 msgstr ""
 
-#: engines/mm/mm1/metaengine.cpp:153
+#: engines/mm/mm1/metaengine.cpp:155
 msgid "Might and Magic 1 - Minimal Keys"
 msgstr ""
 
-#: engines/mm/mm1/metaengine.cpp:154
+#: engines/mm/mm1/metaengine.cpp:156
 msgid "Might and Magic 1 - Main"
 msgstr ""
 
-#: engines/mm/mm1/metaengine.cpp:155
+#: engines/mm/mm1/metaengine.cpp:157
 msgid "Might and Magic 1 - Party"
 msgstr ""
 
-#: engines/mm/mm1/metaengine.cpp:156
+#: engines/mm/mm1/metaengine.cpp:158
 msgid "Might and Magic 1 - Combat"
 msgstr ""
 
+#: engines/mm/mm1/metaengine.cpp:160
+msgid "Might and Magic 1 - Cheats"
+msgstr ""
+
 #: engines/mm/mm1/mm1.cpp:57
 msgid ""
 "You cannot run the game directly from the Graphics Overhaul Mod. Instead, it "
diff --git a/po/he.po b/po/he.po
index 1eef55c8aca..f630b29b8e5 100644
--- a/po/he.po
+++ b/po/he.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.1.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-18 09:25+0000\n"
+"POT-Creation-Date: 2026-06-18 09:40+0000\n"
 "PO-Revision-Date: 2025-04-09 20:18+0000\n"
 "Last-Translator: Niv Baehr <bloop93 at gmail.com>\n"
 "Language-Team: Hebrew <https://translations.scummvm.org/projects/scummvm/"
@@ -11555,32 +11555,36 @@ msgstr ""
 msgid "Unlock"
 msgstr "???????? ??????????"
 
-#: engines/mm/mm1/metaengine.cpp:127 engines/ultima/ultima4/metaengine.cpp:112
+#: engines/mm/mm1/metaengine.cpp:128 engines/ultima/ultima4/metaengine.cpp:112
 #, fuzzy
 #| msgid "Next location"
 msgid "Goto location"
 msgstr "???????????? ??????"
 
-#: engines/mm/mm1/metaengine.cpp:152
+#: engines/mm/mm1/metaengine.cpp:154
 msgid "Might and Magic 1 - Menus"
 msgstr ""
 
-#: engines/mm/mm1/metaengine.cpp:153
+#: engines/mm/mm1/metaengine.cpp:155
 msgid "Might and Magic 1 - Minimal Keys"
 msgstr ""
 
-#: engines/mm/mm1/metaengine.cpp:154
+#: engines/mm/mm1/metaengine.cpp:156
 msgid "Might and Magic 1 - Main"
 msgstr ""
 
-#: engines/mm/mm1/metaengine.cpp:155
+#: engines/mm/mm1/metaengine.cpp:157
 msgid "Might and Magic 1 - Party"
 msgstr ""
 
-#: engines/mm/mm1/metaengine.cpp:156
+#: engines/mm/mm1/metaengine.cpp:158
 msgid "Might and Magic 1 - Combat"
 msgstr ""
 
+#: engines/mm/mm1/metaengine.cpp:160
+msgid "Might and Magic 1 - Cheats"
+msgstr ""
+
 #: engines/mm/mm1/mm1.cpp:57
 msgid ""
 "You cannot run the game directly from the Graphics Overhaul Mod. Instead, it "
diff --git a/po/hi.po b/po/hi.po
index 2cb3d5ce4a0..3fb63eb4731 100644
--- a/po/hi.po
+++ b/po/hi.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-18 09:25+0000\n"
+"POT-Creation-Date: 2026-06-18 09:40+0000\n"
 "PO-Revision-Date: 2026-05-12 22:22+0000\n"
 "Last-Translator: KARTIK SAXENA <kartiksaxena2005 at gmail.com>\n"
 "Language-Team: Hindi <https://translations.scummvm.org/projects/scummvm/"
@@ -10963,30 +10963,34 @@ msgstr ""
 msgid "Unlock"
 msgstr ""
 
-#: engines/mm/mm1/metaengine.cpp:127 engines/ultima/ultima4/metaengine.cpp:112
+#: engines/mm/mm1/metaengine.cpp:128 engines/ultima/ultima4/metaengine.cpp:112
 msgid "Goto location"
 msgstr "?????????????????? ?????? ????????????"
 
-#: engines/mm/mm1/metaengine.cpp:152
+#: engines/mm/mm1/metaengine.cpp:154
 msgid "Might and Magic 1 - Menus"
 msgstr ""
 
-#: engines/mm/mm1/metaengine.cpp:153
+#: engines/mm/mm1/metaengine.cpp:155
 msgid "Might and Magic 1 - Minimal Keys"
 msgstr ""
 
-#: engines/mm/mm1/metaengine.cpp:154
+#: engines/mm/mm1/metaengine.cpp:156
 msgid "Might and Magic 1 - Main"
 msgstr ""
 
-#: engines/mm/mm1/metaengine.cpp:155
+#: engines/mm/mm1/metaengine.cpp:157
 msgid "Might and Magic 1 - Party"
 msgstr ""
 
-#: engines/mm/mm1/metaengine.cpp:156
+#: engines/mm/mm1/metaengine.cpp:158
 msgid "Might and Magic 1 - Combat"
 msgstr ""
 
+#: engines/mm/mm1/metaengine.cpp:160
+msgid "Might and Magic 1 - Cheats"
+msgstr ""
+
 #: engines/mm/mm1/mm1.cpp:57
 msgid ""
 "You cannot run the game directly from the Graphics Overhaul Mod. Instead, it "
diff --git a/po/hu_HU.po b/po/hu_HU.po
index 53774f546eb..7d430bcfbad 100644
--- a/po/hu_HU.po
+++ b/po/hu_HU.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-18 09:25+0000\n"
+"POT-Creation-Date: 2026-06-18 09:40+0000\n"
 "PO-Revision-Date: 2025-03-07 00:18+0000\n"
 "Last-Translator: Hov??nszki Tam??s <gahex68379 at lofiey.com>\n"
 "Language-Team: Hungarian <https://translations.scummvm.org/projects/scummvm/"
@@ -11979,32 +11979,36 @@ msgstr "??t??s"
 msgid "Unlock"
 msgstr "Felold"
 
-#: engines/mm/mm1/metaengine.cpp:127 engines/ultima/ultima4/metaengine.cpp:112
+#: engines/mm/mm1/metaengine.cpp:128 engines/ultima/ultima4/metaengine.cpp:112
 msgid "Goto location"
 msgstr "Ugr??s helysz??nre"
 
-#: engines/mm/mm1/metaengine.cpp:152
+#: engines/mm/mm1/metaengine.cpp:154
 msgid "Might and Magic 1 - Menus"
 msgstr "Might and Magic 1 - Men??k"
 
-#: engines/mm/mm1/metaengine.cpp:153
+#: engines/mm/mm1/metaengine.cpp:155
 msgid "Might and Magic 1 - Minimal Keys"
 msgstr "Might and Magic 1 - Minim??lis gombok"
 
-#: engines/mm/mm1/metaengine.cpp:154
+#: engines/mm/mm1/metaengine.cpp:156
 #, fuzzy
 #| msgid "Might and Magic 1 - Menus"
 msgid "Might and Magic 1 - Main"
 msgstr "Might and Magic 1 - Men??k"
 
-#: engines/mm/mm1/metaengine.cpp:155
+#: engines/mm/mm1/metaengine.cpp:157
 msgid "Might and Magic 1 - Party"
 msgstr "Might and Magic 1 - Csapat"
 
-#: engines/mm/mm1/metaengine.cpp:156
+#: engines/mm/mm1/metaengine.cpp:158
 msgid "Might and Magic 1 - Combat"
 msgstr "Might and Magic 1 - Harc"
 
+#: engines/mm/mm1/metaengine.cpp:160
+msgid "Might and Magic 1 - Cheats"
+msgstr "Might and Magic 1 - Csal??sok"
+
 #: engines/mm/mm1/mm1.cpp:57
 msgid ""
 "You cannot run the game directly from the Graphics Overhaul Mod. Instead, it "
@@ -18737,9 +18741,6 @@ msgstr ""
 #~ msgid "N"
 #~ msgstr "Nem"
 
-#~ msgid "Might and Magic 1 - Cheats"
-#~ msgstr "Might and Magic 1 - Csal??sok"
-
 #~ msgid "3DO Plumbers requires RGB support."
 #~ msgstr "A 3DO Plumbers RGB t??mogat??st ig??nyel."
 
diff --git a/po/it_IT.po b/po/it_IT.po
index 6284ccfa54d..c0e2201f6bf 100644
--- a/po/it_IT.po
+++ b/po/it_IT.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-18 09:25+0000\n"
+"POT-Creation-Date: 2026-06-18 09:40+0000\n"
 "PO-Revision-Date: 2026-06-14 09:41+0000\n"
 "Last-Translator: Walter Agazzi <walter.agazzi at protonmail.com>\n"
 "Language-Team: Italian <https://translations.scummvm.org/projects/scummvm/"
@@ -11277,30 +11277,34 @@ msgstr "Colpisci"
 msgid "Unlock"
 msgstr "Apri serratura (disserrare)"
 
-#: engines/mm/mm1/metaengine.cpp:127 engines/ultima/ultima4/metaengine.cpp:112
+#: engines/mm/mm1/metaengine.cpp:128 engines/ultima/ultima4/metaengine.cpp:112
 msgid "Goto location"
 msgstr "Vai a Locazione"
 
-#: engines/mm/mm1/metaengine.cpp:152
+#: engines/mm/mm1/metaengine.cpp:154
 msgid "Might and Magic 1 - Menus"
 msgstr "Might and Magic 1 - Men??"
 
-#: engines/mm/mm1/metaengine.cpp:153
+#: engines/mm/mm1/metaengine.cpp:155
 msgid "Might and Magic 1 - Minimal Keys"
 msgstr "Might and Magic 1 - Tasti Base"
 
-#: engines/mm/mm1/metaengine.cpp:154
+#: engines/mm/mm1/metaengine.cpp:156
 msgid "Might and Magic 1 - Main"
 msgstr "Might and Magic 1 - Principale"
 
-#: engines/mm/mm1/metaengine.cpp:155
+#: engines/mm/mm1/metaengine.cpp:157
 msgid "Might and Magic 1 - Party"
 msgstr "Might and Magic 1 - Gruppo"
 
-#: engines/mm/mm1/metaengine.cpp:156
+#: engines/mm/mm1/metaengine.cpp:158
 msgid "Might and Magic 1 - Combat"
 msgstr "Might and Magic 1 - Combattimento"
 
+#: engines/mm/mm1/metaengine.cpp:160
+msgid "Might and Magic 1 - Cheats"
+msgstr "Might and Magic 1 - Cheats"
+
 #: engines/mm/mm1/mm1.cpp:57
 msgid ""
 "You cannot run the game directly from the Graphics Overhaul Mod. Instead, it "
@@ -17444,9 +17448,6 @@ msgstr ""
 #~ msgid "N"
 #~ msgstr "No"
 
-#~ msgid "Might and Magic 1 - Cheats"
-#~ msgstr "Might and Magic 1 - Cheats"
-
 #~ msgid "3DO Plumbers requires RGB support."
 #~ msgstr "La versione 3DO di Plumbers richiede il supporto RGB."
 
diff --git a/po/ja.po b/po/ja.po
index 0604334ff3d..0aa2d96df4e 100644
--- a/po/ja.po
+++ b/po/ja.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-18 09:25+0000\n"
+"POT-Creation-Date: 2026-06-18 09:40+0000\n"
 "PO-Revision-Date: 2026-06-04 07:22+0000\n"
 "Last-Translator: VAN-Gluon <van.gluonp at gmail.com>\n"
 "Language-Team: Japanese <https://translations.scummvm.org/projects/scummvm/"
@@ -11631,32 +11631,36 @@ msgstr "????????????"
 msgid "Unlock"
 msgstr "??????"
 
-#: engines/mm/mm1/metaengine.cpp:127 engines/ultima/ultima4/metaengine.cpp:112
+#: engines/mm/mm1/metaengine.cpp:128 engines/ultima/ultima4/metaengine.cpp:112
 msgid "Goto location"
 msgstr "???????????????"
 
-#: engines/mm/mm1/metaengine.cpp:152
+#: engines/mm/mm1/metaengine.cpp:154
 msgid "Might and Magic 1 - Menus"
 msgstr "Might and Magic 1 - ????????????"
 
-#: engines/mm/mm1/metaengine.cpp:153
+#: engines/mm/mm1/metaengine.cpp:155
 msgid "Might and Magic 1 - Minimal Keys"
 msgstr "Might and Magic 1 - ??????????????????"
 
-#: engines/mm/mm1/metaengine.cpp:154
+#: engines/mm/mm1/metaengine.cpp:156
 #, fuzzy
 #| msgid "Might and Magic 1 - Menus"
 msgid "Might and Magic 1 - Main"
 msgstr "Might and Magic 1 - ????????????"
 
-#: engines/mm/mm1/metaengine.cpp:155
+#: engines/mm/mm1/metaengine.cpp:157
 msgid "Might and Magic 1 - Party"
 msgstr "Might and Magic 1 - ???????????????"
 
-#: engines/mm/mm1/metaengine.cpp:156
+#: engines/mm/mm1/metaengine.cpp:158
 msgid "Might and Magic 1 - Combat"
 msgstr "Might and Magic 1 - ??????"
 
+#: engines/mm/mm1/metaengine.cpp:160
+msgid "Might and Magic 1 - Cheats"
+msgstr "Might and Magic 1 - ?????????"
+
 #: engines/mm/mm1/mm1.cpp:57
 msgid ""
 "You cannot run the game directly from the Graphics Overhaul Mod. Instead, it "
@@ -18362,9 +18366,6 @@ msgstr ""
 #~ msgid "N"
 #~ msgstr "?????????"
 
-#~ msgid "Might and Magic 1 - Cheats"
-#~ msgstr "Might and Magic 1 - ?????????"
-
 #~ msgid "3DO Plumbers requires RGB support."
 #~ msgstr "3DO??????Plumbers??????RGB??????????????????"
 
diff --git a/po/ka.po b/po/ka.po
index 36361d352a8..fb1b17b8171 100644
--- a/po/ka.po
+++ b/po/ka.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-18 09:25+0000\n"
+"POT-Creation-Date: 2026-06-18 09:40+0000\n"
 "PO-Revision-Date: 2026-06-05 10:22+0000\n"
 "Last-Translator: NorwayFun <temuri.doghonadze at gmail.com>\n"
 "Language-Team: Georgian <https://translations.scummvm.org/projects/scummvm/"
@@ -10314,30 +10314,34 @@ msgstr "Bash"
 msgid "Unlock"
 msgstr "???????????????????????????"
 
-#: engines/mm/mm1/metaengine.cpp:127 engines/ultima/ultima4/metaengine.cpp:112
+#: engines/mm/mm1/metaengine.cpp:128 engines/ultima/ultima4/metaengine.cpp:112
 msgid "Goto location"
 msgstr "???????????????????????????????????? ????????????????????????"
 
-#: engines/mm/mm1/metaengine.cpp:152
+#: engines/mm/mm1/metaengine.cpp:154
 msgid "Might and Magic 1 - Menus"
 msgstr "Might and Magic 1 - ????????????????????????"
 
-#: engines/mm/mm1/metaengine.cpp:153
+#: engines/mm/mm1/metaengine.cpp:155
 msgid "Might and Magic 1 - Minimal Keys"
 msgstr "Might and Magic 1 - ?????????????????????????????? ????????????????????????"
 
-#: engines/mm/mm1/metaengine.cpp:154
+#: engines/mm/mm1/metaengine.cpp:156
 msgid "Might and Magic 1 - Main"
 msgstr "Might and Magic 1 - ?????????????????????"
 
-#: engines/mm/mm1/metaengine.cpp:155
+#: engines/mm/mm1/metaengine.cpp:157
 msgid "Might and Magic 1 - Party"
 msgstr "Might and Magic 1 - ???????????????"
 
-#: engines/mm/mm1/metaengine.cpp:156
+#: engines/mm/mm1/metaengine.cpp:158
 msgid "Might and Magic 1 - Combat"
 msgstr "Might and Magic 1 - ??????????????????"
 
+#: engines/mm/mm1/metaengine.cpp:160
+msgid "Might and Magic 1 - Cheats"
+msgstr "Might and Magic 1 - ???????????????????????????"
+
 #: engines/mm/mm1/mm1.cpp:57
 msgid ""
 "You cannot run the game directly from the Graphics Overhaul Mod. Instead, it "
@@ -16049,9 +16053,6 @@ msgstr ""
 #~ msgid "N"
 #~ msgstr "?????????"
 
-#~ msgid "Might and Magic 1 - Cheats"
-#~ msgstr "Might and Magic 1 - ???????????????????????????"
-
 #~ msgid "Load game:"
 #~ msgstr "????????????????????? ???????????????????????????:"
 
diff --git a/po/ko.po b/po/ko.po
index 28572d9b454..ce9d3bc57be 100644
--- a/po/ko.po
+++ b/po/ko.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-18 09:25+0000\n"
+"POT-Creation-Date: 2026-06-18 09:40+0000\n"
 "PO-Revision-Date: 2026-05-19 20:22+0000\n"
 "Last-Translator: Hoseok Seo <ddinghoya at gmail.com>\n"
 "Language-Team: Korean <https://translations.scummvm.org/projects/scummvm/"
@@ -11075,30 +11075,34 @@ msgstr "??????"
 msgid "Unlock"
 msgstr "?????? ??????"
 
-#: engines/mm/mm1/metaengine.cpp:127 engines/ultima/ultima4/metaengine.cpp:112
+#: engines/mm/mm1/metaengine.cpp:128 engines/ultima/ultima4/metaengine.cpp:112
 msgid "Goto location"
 msgstr "????????? ??????"
 
-#: engines/mm/mm1/metaengine.cpp:152
+#: engines/mm/mm1/metaengine.cpp:154
 msgid "Might and Magic 1 - Menus"
 msgstr "????????? ??? ?????? 1 - ??????"
 
-#: engines/mm/mm1/metaengine.cpp:153
+#: engines/mm/mm1/metaengine.cpp:155
 msgid "Might and Magic 1 - Minimal Keys"
 msgstr "????????? ??? ?????? 1 - ???????????? ???"
 
-#: engines/mm/mm1/metaengine.cpp:154
+#: engines/mm/mm1/metaengine.cpp:156
 msgid "Might and Magic 1 - Main"
 msgstr "????????? ??? ?????? 1 - ??????"
 
-#: engines/mm/mm1/metaengine.cpp:155
+#: engines/mm/mm1/metaengine.cpp:157
 msgid "Might and Magic 1 - Party"
 msgstr "????????? ??? ?????? 1 - ??????"
 
-#: engines/mm/mm1/metaengine.cpp:156
+#: engines/mm/mm1/metaengine.cpp:158
 msgid "Might and Magic 1 - Combat"
 msgstr "????????? ??? ?????? 1 - ??????"
 
+#: engines/mm/mm1/metaengine.cpp:160
+msgid "Might and Magic 1 - Cheats"
+msgstr "????????? ??? ?????? - ??????"
+
 #: engines/mm/mm1/mm1.cpp:57
 msgid ""
 "You cannot run the game directly from the Graphics Overhaul Mod. Instead, it "
@@ -17104,9 +17108,6 @@ msgstr ""
 #~ msgid "N"
 #~ msgstr "?????????"
 
-#~ msgid "Might and Magic 1 - Cheats"
-#~ msgstr "????????? ??? ?????? - ??????"
-
 #~ msgid "3DO Plumbers requires RGB support."
 #~ msgstr "3DO Plumbers?????? RGB ????????? ???????????????."
 
diff --git a/po/nb_NO.po b/po/nb_NO.po
index 7ec8b4d851a..03b6fa36129 100644
--- a/po/nb_NO.po
+++ b/po/nb_NO.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-18 09:25+0000\n"
+"POT-Creation-Date: 2026-06-18 09:40+0000\n"
 "PO-Revision-Date: 2026-02-07 15:22+0000\n"
 "Last-Translator: Einar Johan Tr??an S??m??en <einarjohants at gmail.com>\n"
 "Language-Team: Norwegian Bokm??l <https://translations.scummvm.org/projects/"
@@ -12057,32 +12057,36 @@ msgstr "Sl??"
 msgid "Unlock"
 msgstr "L??s opp"
 
-#: engines/mm/mm1/metaengine.cpp:127 engines/ultima/ultima4/metaengine.cpp:112
+#: engines/mm/mm1/metaengine.cpp:128 engines/ultima/ultima4/metaengine.cpp:112
 msgid "Goto location"
 msgstr "G?? til plassering"
 
-#: engines/mm/mm1/metaengine.cpp:152
+#: engines/mm/mm1/metaengine.cpp:154
 msgid "Might and Magic 1 - Menus"
 msgstr "Might and Magic 1 - Menyer"
 
-#: engines/mm/mm1/metaengine.cpp:153
+#: engines/mm/mm1/metaengine.cpp:155
 msgid "Might and Magic 1 - Minimal Keys"
 msgstr "Might and Magic 1 - Minimale taster"
 
-#: engines/mm/mm1/metaengine.cpp:154
+#: engines/mm/mm1/metaengine.cpp:156
 #, fuzzy
 #| msgid "Might and Magic 1 - Menus"
 msgid "Might and Magic 1 - Main"
 msgstr "Might and Magic 1 - Menyer"
 
-#: engines/mm/mm1/metaengine.cpp:155
+#: engines/mm/mm1/metaengine.cpp:157
 msgid "Might and Magic 1 - Party"
 msgstr "Might and Magic 1 - Gruppe"
 
-#: engines/mm/mm1/metaengine.cpp:156
+#: engines/mm/mm1/metaengine.cpp:158
 msgid "Might and Magic 1 - Combat"
 msgstr "Might and Magic 1 - Kamp"
 
+#: engines/mm/mm1/metaengine.cpp:160
+msgid "Might and Magic 1 - Cheats"
+msgstr "Might and Magic 1 - Juksefunksjoner"
+
 #: engines/mm/mm1/mm1.cpp:57
 msgid ""
 "You cannot run the game directly from the Graphics Overhaul Mod. Instead, it "
@@ -18913,9 +18917,6 @@ msgstr ""
 #~ msgid "N"
 #~ msgstr "Nei"
 
-#~ msgid "Might and Magic 1 - Cheats"
-#~ msgstr "Might and Magic 1 - Juksefunksjoner"
-
 #~ msgid "3DO Plumbers requires RGB support."
 #~ msgstr "3DO Plumbers-krever RGB-st??tte."
 
diff --git a/po/nl_NL.po b/po/nl_NL.po
index 8712958b935..b02114ff9a3 100644
--- a/po/nl_NL.po
+++ b/po/nl_NL.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.9.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-18 09:25+0000\n"
+"POT-Creation-Date: 2026-06-18 09:40+0000\n"
 "PO-Revision-Date: 2026-02-11 14:23+0000\n"
 "Last-Translator: SecularSteve <fairfull.playing at gmail.com>\n"
 "Language-Team: Dutch <https://translations.scummvm.org/projects/scummvm/"
@@ -11356,32 +11356,36 @@ msgstr "Beuk"
 msgid "Unlock"
 msgstr "Maak open"
 
-#: engines/mm/mm1/metaengine.cpp:127 engines/ultima/ultima4/metaengine.cpp:112
+#: engines/mm/mm1/metaengine.cpp:128 engines/ultima/ultima4/metaengine.cpp:112
 msgid "Goto location"
 msgstr "Ga naar lokatie"
 
-#: engines/mm/mm1/metaengine.cpp:152
+#: engines/mm/mm1/metaengine.cpp:154
 msgid "Might and Magic 1 - Menus"
 msgstr "Might and Magic 1 - Menus"
 
-#: engines/mm/mm1/metaengine.cpp:153
+#: engines/mm/mm1/metaengine.cpp:155
 msgid "Might and Magic 1 - Minimal Keys"
 msgstr "Might and Magic 1 - Minimale Toetsen"
 
-#: engines/mm/mm1/metaengine.cpp:154
+#: engines/mm/mm1/metaengine.cpp:156
 #, fuzzy
 #| msgid "Might and Magic 1 - Menus"
 msgid "Might and Magic 1 - Main"
 msgstr "Might and Magic 1 - Menus"
 
-#: engines/mm/mm1/metaengine.cpp:155
+#: engines/mm/mm1/metaengine.cpp:157
 msgid "Might and Magic 1 - Party"
 msgstr "Might and Magic 1 - Groep"
 
-#: engines/mm/mm1/metaengine.cpp:156
+#: engines/mm/mm1/metaengine.cpp:158
 msgid "Might and Magic 1 - Combat"
 msgstr "Might and Magic 1 - Vechten"
 
+#: engines/mm/mm1/metaengine.cpp:160
+msgid "Might and Magic 1 - Cheats"
+msgstr "Might and Magic 1 - Cheats"
+
 #: engines/mm/mm1/mm1.cpp:57
 msgid ""
 "You cannot run the game directly from the Graphics Overhaul Mod. Instead, it "
@@ -17556,9 +17560,6 @@ msgstr ""
 #~ msgid "N"
 #~ msgstr "Nee"
 
-#~ msgid "Might and Magic 1 - Cheats"
-#~ msgstr "Might and Magic 1 - Cheats"
-
 #~ msgid "3DO Plumbers requires RGB support."
 #~ msgstr "3DO Plumbers vereist RGB-ondersteuning."
 
diff --git a/po/nn_NO.po b/po/nn_NO.po
index cd0db72468a..662fd489a07 100644
--- a/po/nn_NO.po
+++ b/po/nn_NO.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-18 09:25+0000\n"
+"POT-Creation-Date: 2026-06-18 09:40+0000\n"
 "PO-Revision-Date: 2023-12-14 14:28+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Norwegian Nynorsk <https://translations.scummvm.org/projects/"
@@ -11324,31 +11324,35 @@ msgstr ""
 msgid "Unlock"
 msgstr "L??s opp"
 
-#: engines/mm/mm1/metaengine.cpp:127 engines/ultima/ultima4/metaengine.cpp:112
+#: engines/mm/mm1/metaengine.cpp:128 engines/ultima/ultima4/metaengine.cpp:112
 #, fuzzy
 msgid "Goto location"
 msgstr "~F~orrige"
 
-#: engines/mm/mm1/metaengine.cpp:152
+#: engines/mm/mm1/metaengine.cpp:154
 msgid "Might and Magic 1 - Menus"
 msgstr ""
 
-#: engines/mm/mm1/metaengine.cpp:153
+#: engines/mm/mm1/metaengine.cpp:155
 msgid "Might and Magic 1 - Minimal Keys"
 msgstr ""
 
-#: engines/mm/mm1/metaengine.cpp:154
+#: engines/mm/mm1/metaengine.cpp:156
 msgid "Might and Magic 1 - Main"
 msgstr ""
 
-#: engines/mm/mm1/metaengine.cpp:155
+#: engines/mm/mm1/metaengine.cpp:157
 msgid "Might and Magic 1 - Party"
 msgstr ""
 
-#: engines/mm/mm1/metaengine.cpp:156
+#: engines/mm/mm1/metaengine.cpp:158
 msgid "Might and Magic 1 - Combat"
 msgstr ""
 
+#: engines/mm/mm1/metaengine.cpp:160
+msgid "Might and Magic 1 - Cheats"
+msgstr ""
+
 #: engines/mm/mm1/mm1.cpp:57
 msgid ""
 "You cannot run the game directly from the Graphics Overhaul Mod. Instead, it "
diff --git a/po/pl_PL.po b/po/pl_PL.po
index 65162fcc2b5..51e7148c8a7 100644
--- a/po/pl_PL.po
+++ b/po/pl_PL.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-18 09:25+0000\n"
+"POT-Creation-Date: 2026-06-18 09:40+0000\n"
 "PO-Revision-Date: 2026-04-17 00:22+0000\n"
 "Last-Translator: SkiffPL <skiff at windowslive.com>\n"
 "Language-Team: Polish <https://translations.scummvm.org/projects/scummvm/"
@@ -11507,32 +11507,36 @@ msgstr "Uderz"
 msgid "Unlock"
 msgstr "Otw??rz"
 
-#: engines/mm/mm1/metaengine.cpp:127 engines/ultima/ultima4/metaengine.cpp:112
+#: engines/mm/mm1/metaengine.cpp:128 engines/ultima/ultima4/metaengine.cpp:112
 msgid "Goto location"
 msgstr "Przejd?? do lokalizacji"
 
-#: engines/mm/mm1/metaengine.cpp:152
+#: engines/mm/mm1/metaengine.cpp:154
 msgid "Might and Magic 1 - Menus"
 msgstr "Might and Magic 1 - Menu"
 
-#: engines/mm/mm1/metaengine.cpp:153
+#: engines/mm/mm1/metaengine.cpp:155
 msgid "Might and Magic 1 - Minimal Keys"
 msgstr "Might and Magic 1 - Klucze minimalne"
 
-#: engines/mm/mm1/metaengine.cpp:154
+#: engines/mm/mm1/metaengine.cpp:156
 #, fuzzy
 #| msgid "Might and Magic 1 - Menus"
 msgid "Might and Magic 1 - Main"
 msgstr "Might and Magic 1 - Menu"
 
-#: engines/mm/mm1/metaengine.cpp:155
+#: engines/mm/mm1/metaengine.cpp:157
 msgid "Might and Magic 1 - Party"
 msgstr "Might and Magic 1 - Ekipa"
 
-#: engines/mm/mm1/metaengine.cpp:156
+#: engines/mm/mm1/metaengine.cpp:158
 msgid "Might and Magic 1 - Combat"
 msgstr "Might and Magic 1 - Walka"
 
+#: engines/mm/mm1/metaengine.cpp:160
+msgid "Might and Magic 1 - Cheats"
+msgstr "Might and Magic 1 - Cheaty"
+
 #: engines/mm/mm1/mm1.cpp:57
 msgid ""
 "You cannot run the game directly from the Graphics Overhaul Mod. Instead, it "
@@ -17810,9 +17814,6 @@ msgstr ""
 #~ msgid "N"
 #~ msgstr "Nie"
 
-#~ msgid "Might and Magic 1 - Cheats"
-#~ msgstr "Might and Magic 1 - Cheaty"
-
 #~ msgid "3DO Plumbers requires RGB support."
 #~ msgstr "Gra Plumbers na 3DO wymaga obs??ugi RGB."
 
diff --git a/po/pt_BR.po b/po/pt_BR.po
index 9b24e9e9010..66508a144a9 100644
--- a/po/pt_BR.po
+++ b/po/pt_BR.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-18 09:25+0000\n"
+"POT-Creation-Date: 2026-06-18 09:40+0000\n"
 "PO-Revision-Date: 2026-06-17 22:10+0000\n"
 "Last-Translator: Marcel Souza Lemes <marcosoutsider at gmail.com>\n"
 "Language-Team: Portuguese (Brazil) <https://translations.scummvm.org/"
@@ -12070,32 +12070,36 @@ msgstr "Golpear"
 msgid "Unlock"
 msgstr "Destravar"
 
-#: engines/mm/mm1/metaengine.cpp:127 engines/ultima/ultima4/metaengine.cpp:112
+#: engines/mm/mm1/metaengine.cpp:128 engines/ultima/ultima4/metaengine.cpp:112
 msgid "Goto location"
 msgstr "Ir para local"
 
-#: engines/mm/mm1/metaengine.cpp:152
+#: engines/mm/mm1/metaengine.cpp:154
 msgid "Might and Magic 1 - Menus"
 msgstr "Might and Magic 1 - Menus"
 
-#: engines/mm/mm1/metaengine.cpp:153
+#: engines/mm/mm1/metaengine.cpp:155
 msgid "Might and Magic 1 - Minimal Keys"
 msgstr "Might and Magic 1 - Teclas B??sicas"
 
-#: engines/mm/mm1/metaengine.cpp:154
+#: engines/mm/mm1/metaengine.cpp:156
 #, fuzzy
 #| msgid "Might and Magic 1 - Menus"
 msgid "Might and Magic 1 - Main"
 msgstr "Might and Magic 1 - Menus"
 
-#: engines/mm/mm1/metaengine.cpp:155
+#: engines/mm/mm1/metaengine.cpp:157
 msgid "Might and Magic 1 - Party"
 msgstr "Might and Magic 1 - Equipe"
 
-#: engines/mm/mm1/metaengine.cpp:156
+#: engines/mm/mm1/metaengine.cpp:158
 msgid "Might and Magic 1 - Combat"
 msgstr "Might and Magic 1 - Combate"
 
+#: engines/mm/mm1/metaengine.cpp:160
+msgid "Might and Magic 1 - Cheats"
+msgstr "Might and Magic 1 - Trapa??as"
+
 #: engines/mm/mm1/mm1.cpp:57
 msgid ""
 "You cannot run the game directly from the Graphics Overhaul Mod. Instead, it "
@@ -18919,9 +18923,6 @@ msgstr ""
 #~ msgid "N"
 #~ msgstr "N??o"
 
-#~ msgid "Might and Magic 1 - Cheats"
-#~ msgstr "Might and Magic 1 - Trapa??as"
-
 #~ msgid "3DO Plumbers requires RGB support."
 #~ msgstr "Plumbers para 3DO requer suporte RGB."
 
diff --git a/po/pt_PT.po b/po/pt_PT.po
index 5926e4315b6..49e54b6bc94 100644
--- a/po/pt_PT.po
+++ b/po/pt_PT.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.10.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-18 09:25+0000\n"
+"POT-Creation-Date: 2026-06-18 09:40+0000\n"
 "PO-Revision-Date: 2022-06-05 14:09+0000\n"
 "Last-Translator: Daniel <supsuper at gmail.com>\n"
 "Language-Team: Portuguese (Portugal) <https://translations.scummvm.org/"
@@ -11412,31 +11412,35 @@ msgstr ""
 msgid "Unlock"
 msgstr ""
 
-#: engines/mm/mm1/metaengine.cpp:127 engines/ultima/ultima4/metaengine.cpp:112
+#: engines/mm/mm1/metaengine.cpp:128 engines/ultima/ultima4/metaengine.cpp:112
 #, fuzzy
 msgid "Goto location"
 msgstr "A????o seguinte"
 
-#: engines/mm/mm1/metaengine.cpp:152
+#: engines/mm/mm1/metaengine.cpp:154
 msgid "Might and Magic 1 - Menus"
 msgstr ""
 
-#: engines/mm/mm1/metaengine.cpp:153
+#: engines/mm/mm1/metaengine.cpp:155
 msgid "Might and Magic 1 - Minimal Keys"
 msgstr ""
 
-#: engines/mm/mm1/metaengine.cpp:154
+#: engines/mm/mm1/metaengine.cpp:156
 msgid "Might and Magic 1 - Main"
 msgstr ""
 
-#: engines/mm/mm1/metaengine.cpp:155
+#: engines/mm/mm1/metaengine.cpp:157
 msgid "Might and Magic 1 - Party"
 msgstr ""
 
-#: engines/mm/mm1/metaengine.cpp:156
+#: engines/mm/mm1/metaengine.cpp:158
 msgid "Might and Magic 1 - Combat"
 msgstr ""
 
+#: engines/mm/mm1/metaengine.cpp:160
+msgid "Might and Magic 1 - Cheats"
+msgstr ""
+
 #: engines/mm/mm1/mm1.cpp:57
 msgid ""
 "You cannot run the game directly from the Graphics Overhaul Mod. Instead, it "
diff --git a/po/ro.po b/po/ro.po
index 7277594d762..77b50440a1d 100644
--- a/po/ro.po
+++ b/po/ro.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.10.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-18 09:25+0000\n"
+"POT-Creation-Date: 2026-06-18 09:40+0000\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: Automatically generated\n"
 "Language-Team: none\n"
@@ -10261,30 +10261,34 @@ msgstr ""
 msgid "Unlock"
 msgstr ""
 
-#: engines/mm/mm1/metaengine.cpp:127 engines/ultima/ultima4/metaengine.cpp:112
+#: engines/mm/mm1/metaengine.cpp:128 engines/ultima/ultima4/metaengine.cpp:112
 msgid "Goto location"
 msgstr ""
 
-#: engines/mm/mm1/metaengine.cpp:152
+#: engines/mm/mm1/metaengine.cpp:154
 msgid "Might and Magic 1 - Menus"
 msgstr ""
 
-#: engines/mm/mm1/metaengine.cpp:153
+#: engines/mm/mm1/metaengine.cpp:155
 msgid "Might and Magic 1 - Minimal Keys"
 msgstr ""
 
-#: engines/mm/mm1/metaengine.cpp:154
+#: engines/mm/mm1/metaengine.cpp:156
 msgid "Might and Magic 1 - Main"
 msgstr ""
 
-#: engines/mm/mm1/metaengine.cpp:155
+#: engines/mm/mm1/metaengine.cpp:157
 msgid "Might and Magic 1 - Party"
 msgstr ""
 
-#: engines/mm/mm1/metaengine.cpp:156
+#: engines/mm/mm1/metaengine.cpp:158
 msgid "Might and Magic 1 - Combat"
 msgstr ""
 
+#: engines/mm/mm1/metaengine.cpp:160
+msgid "Might and Magic 1 - Cheats"
+msgstr ""
+
 #: engines/mm/mm1/mm1.cpp:57
 msgid ""
 "You cannot run the game directly from the Graphics Overhaul Mod. Instead, it "
diff --git a/po/ru_RU.po b/po/ru_RU.po
index 4846db60c88..7e9e56e0e81 100644
--- a/po/ru_RU.po
+++ b/po/ru_RU.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.8.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-18 09:25+0000\n"
+"POT-Creation-Date: 2026-06-18 09:40+0000\n"
 "PO-Revision-Date: 2026-06-17 23:04+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Russian <https://translations.scummvm.org/projects/scummvm/"
@@ -11202,30 +11202,34 @@ msgstr "????????"
 msgid "Unlock"
 msgstr "??????????????"
 
-#: engines/mm/mm1/metaengine.cpp:127 engines/ultima/ultima4/metaengine.cpp:112
+#: engines/mm/mm1/metaengine.cpp:128 engines/ultima/ultima4/metaengine.cpp:112
 msgid "Goto location"
 msgstr "?????????????? ?? ??????????????"
 
-#: engines/mm/mm1/metaengine.cpp:152
+#: engines/mm/mm1/metaengine.cpp:154
 msgid "Might and Magic 1 - Menus"
 msgstr "Might and Magic 1 - ????????"
 
-#: engines/mm/mm1/metaengine.cpp:153
+#: engines/mm/mm1/metaengine.cpp:155
 msgid "Might and Magic 1 - Minimal Keys"
 msgstr "Might and Magic 1 - ?????????????????????? ??????????????"
 
-#: engines/mm/mm1/metaengine.cpp:154
+#: engines/mm/mm1/metaengine.cpp:156
 msgid "Might and Magic 1 - Main"
 msgstr "Might and Magic 1 - ??????????????"
 
-#: engines/mm/mm1/metaengine.cpp:155
+#: engines/mm/mm1/metaengine.cpp:157
 msgid "Might and Magic 1 - Party"
 msgstr "Might and Magic 1 - ??????????"
 
-#: engines/mm/mm1/metaengine.cpp:156
+#: engines/mm/mm1/metaengine.cpp:158
 msgid "Might and Magic 1 - Combat"
 msgstr "Might and Magic 1 - ??????"
 
+#: engines/mm/mm1/metaengine.cpp:160
+msgid "Might and Magic 1 - Cheats"
+msgstr "Might and Magic 1 - ????????"
+
 #: engines/mm/mm1/mm1.cpp:57
 msgid ""
 "You cannot run the game directly from the Graphics Overhaul Mod. Instead, it "
@@ -17298,9 +17302,6 @@ msgstr ""
 #~ msgid "N"
 #~ msgstr "??????"
 
-#~ msgid "Might and Magic 1 - Cheats"
-#~ msgstr "Might and Magic 1 - ????????"
-
 #~ msgid "3DO Plumbers requires RGB support."
 #~ msgstr "???????? Plumbers ?????? 3DO ?????????????? ?????????????????? RGB."
 
diff --git a/po/sv_SE.po b/po/sv_SE.po
index 78cf32139ed..a082cb95280 100644
--- a/po/sv_SE.po
+++ b/po/sv_SE.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.5.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-18 09:25+0000\n"
+"POT-Creation-Date: 2026-06-18 09:40+0000\n"
 "PO-Revision-Date: 2026-04-19 13:22+0000\n"
 "Last-Translator: Daniel Nylander <po at danielnylander.se>\n"
 "Language-Team: Swedish <https://translations.scummvm.org/projects/scummvm/"
@@ -11274,32 +11274,36 @@ msgstr "Dr??m till"
 msgid "Unlock"
 msgstr "L??s upp"
 
-#: engines/mm/mm1/metaengine.cpp:127 engines/ultima/ultima4/metaengine.cpp:112
+#: engines/mm/mm1/metaengine.cpp:128 engines/ultima/ultima4/metaengine.cpp:112
 msgid "Goto location"
 msgstr "G?? till plats"
 
-#: engines/mm/mm1/metaengine.cpp:152
+#: engines/mm/mm1/metaengine.cpp:154
 msgid "Might and Magic 1 - Menus"
 msgstr "Might and Magic 1 - Menyer"
 
-#: engines/mm/mm1/metaengine.cpp:153
+#: engines/mm/mm1/metaengine.cpp:155
 msgid "Might and Magic 1 - Minimal Keys"
 msgstr "Might and Magic 1 - Minimala tangenter"
 
-#: engines/mm/mm1/metaengine.cpp:154
+#: engines/mm/mm1/metaengine.cpp:156
 #, fuzzy
 #| msgid "Might and Magic 1 - Menus"
 msgid "Might and Magic 1 - Main"
 msgstr "Might and Magic 1 - Menyer"
 
-#: engines/mm/mm1/metaengine.cpp:155
+#: engines/mm/mm1/metaengine.cpp:157
 msgid "Might and Magic 1 - Party"
 msgstr "Might and Magic 1 - Grupp"
 
-#: engines/mm/mm1/metaengine.cpp:156
+#: engines/mm/mm1/metaengine.cpp:158
 msgid "Might and Magic 1 - Combat"
 msgstr "Might and Magic 1 - Kamp"
 
+#: engines/mm/mm1/metaengine.cpp:160
+msgid "Might and Magic 1 - Cheats"
+msgstr "Might and Magic 1 - Fusk"
+
 #: engines/mm/mm1/mm1.cpp:57
 msgid ""
 "You cannot run the game directly from the Graphics Overhaul Mod. Instead, it "
@@ -17380,9 +17384,6 @@ msgstr ""
 #~ msgid "N"
 #~ msgstr "Nej"
 
-#~ msgid "Might and Magic 1 - Cheats"
-#~ msgstr "Might and Magic 1 - Fusk"
-
 #~ msgid "3DO Plumbers requires RGB support."
 #~ msgstr "3DO Plumbers kr??ver RGB-st??d."
 
diff --git a/po/tr.po b/po/tr.po
index c677b35f502..c6f605427e9 100644
--- a/po/tr.po
+++ b/po/tr.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-18 09:25+0000\n"
+"POT-Creation-Date: 2026-06-18 09:40+0000\n"
 "PO-Revision-Date: 2024-10-10 12:33+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Turkish <https://translations.scummvm.org/projects/scummvm/"
@@ -11003,30 +11003,34 @@ msgstr ""
 msgid "Unlock"
 msgstr ""
 
-#: engines/mm/mm1/metaengine.cpp:127 engines/ultima/ultima4/metaengine.cpp:112
+#: engines/mm/mm1/metaengine.cpp:128 engines/ultima/ultima4/metaengine.cpp:112
 msgid "Goto location"
 msgstr ""
 
-#: engines/mm/mm1/metaengine.cpp:152
+#: engines/mm/mm1/metaengine.cpp:154
 msgid "Might and Magic 1 - Menus"
 msgstr ""
 
-#: engines/mm/mm1/metaengine.cpp:153
+#: engines/mm/mm1/metaengine.cpp:155
 msgid "Might and Magic 1 - Minimal Keys"
 msgstr ""
 
-#: engines/mm/mm1/metaengine.cpp:154
+#: engines/mm/mm1/metaengine.cpp:156
 msgid "Might and Magic 1 - Main"
 msgstr ""
 
-#: engines/mm/mm1/metaengine.cpp:155
+#: engines/mm/mm1/metaengine.cpp:157
 msgid "Might and Magic 1 - Party"
 msgstr ""
 
-#: engines/mm/mm1/metaengine.cpp:156
+#: engines/mm/mm1/metaengine.cpp:158
 msgid "Might and Magic 1 - Combat"
 msgstr ""
 
+#: engines/mm/mm1/metaengine.cpp:160
+msgid "Might and Magic 1 - Cheats"
+msgstr ""
+
 #: engines/mm/mm1/mm1.cpp:57
 msgid ""
 "You cannot run the game directly from the Graphics Overhaul Mod. Instead, it "
diff --git a/po/uk_UA.po b/po/uk_UA.po
index 5625eeb9590..d9ac98fa495 100644
--- a/po/uk_UA.po
+++ b/po/uk_UA.po
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.9.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-18 09:25+0000\n"
+"POT-Creation-Date: 2026-06-18 09:40+0000\n"
 "PO-Revision-Date: 2026-06-17 23:04+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Ukrainian <https://translations.scummvm.org/projects/scummvm/"
@@ -11177,30 +11177,34 @@ msgstr "??????????????????"
 msgid "Unlock"
 msgstr "????????????????"
 
-#: engines/mm/mm1/metaengine.cpp:127 engines/ultima/ultima4/metaengine.cpp:112
+#: engines/mm/mm1/metaengine.cpp:128 engines/ultima/ultima4/metaengine.cpp:112
 msgid "Goto location"
 msgstr "?????????????? ?? ??????????????"
 
-#: engines/mm/mm1/metaengine.cpp:152
+#: engines/mm/mm1/metaengine.cpp:154
 msgid "Might and Magic 1 - Menus"
 msgstr "Might and Magic 1 - ????????"
 
-#: engines/mm/mm1/metaengine.cpp:153
+#: engines/mm/mm1/metaengine.cpp:155
 msgid "Might and Magic 1 - Minimal Keys"
 msgstr "Might and Magic 1 - ???????????????????? ??????????????"
 
-#: engines/mm/mm1/metaengine.cpp:154
+#: engines/mm/mm1/metaengine.cpp:156
 msgid "Might and Magic 1 - Main"
 msgstr "Might and Magic 1 - ??????????????"
 
-#: engines/mm/mm1/metaengine.cpp:155
+#: engines/mm/mm1/metaengine.cpp:157
 msgid "Might and Magic 1 - Party"
 msgstr "Might and Magic 1 - ??????????"
 
-#: engines/mm/mm1/metaengine.cpp:156
+#: engines/mm/mm1/metaengine.cpp:158
 msgid "Might and Magic 1 - Combat"
 msgstr "Might and Magic 1 - ??????????"
 
+#: engines/mm/mm1/metaengine.cpp:160
+msgid "Might and Magic 1 - Cheats"
+msgstr "Might and Magic 1 - ????????"
+
 #: engines/mm/mm1/mm1.cpp:57
 msgid ""
 "You cannot run the game directly from the Graphics Overhaul Mod. Instead, it "
@@ -17246,9 +17250,6 @@ msgstr ""
 #~ msgid "N"
 #~ msgstr "????"
 
-#~ msgid "Might and Magic 1 - Cheats"
-#~ msgstr "Might and Magic 1 - ????????"
-
 #~ msgid "3DO Plumbers requires RGB support."
 #~ msgstr "?????? Plumbers ?????? 3DO ???????????????? ?????????????????? RGB."
 
diff --git a/po/zh.po b/po/zh.po
index 12a893d6dd6..30c8be25653 100644
--- a/po/zh.po
+++ b/po/zh.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-18 09:25+0000\n"
+"POT-Creation-Date: 2026-06-18 09:40+0000\n"
 "PO-Revision-Date: 2024-05-14 12:42+0000\n"
 "Last-Translator: ?????? <clonewith at qq.com>\n"
 "Language-Team: Chinese <https://translations.scummvm.org/projects/scummvm/"
@@ -10843,30 +10843,34 @@ msgstr ""
 msgid "Unlock"
 msgstr ""
 
-#: engines/mm/mm1/metaengine.cpp:127 engines/ultima/ultima4/metaengine.cpp:112
+#: engines/mm/mm1/metaengine.cpp:128 engines/ultima/ultima4/metaengine.cpp:112
 msgid "Goto location"
 msgstr ""
 
-#: engines/mm/mm1/metaengine.cpp:152
+#: engines/mm/mm1/metaengine.cpp:154
 msgid "Might and Magic 1 - Menus"
 msgstr ""
 
-#: engines/mm/mm1/metaengine.cpp:153
+#: engines/mm/mm1/metaengine.cpp:155
 msgid "Might and Magic 1 - Minimal Keys"
 msgstr ""
 
-#: engines/mm/mm1/metaengine.cpp:154
+#: engines/mm/mm1/metaengine.cpp:156
 msgid "Might and Magic 1 - Main"
 msgstr ""
 
-#: engines/mm/mm1/metaengine.cpp:155
+#: engines/mm/mm1/metaengine.cpp:157
 msgid "Might and Magic 1 - Party"
 msgstr ""
 
-#: engines/mm/mm1/metaengine.cpp:156
+#: engines/mm/mm1/metaengine.cpp:158
 msgid "Might and Magic 1 - Combat"
 msgstr ""
 
+#: engines/mm/mm1/metaengine.cpp:160
+msgid "Might and Magic 1 - Cheats"
+msgstr ""
+
 #: engines/mm/mm1/mm1.cpp:57
 msgid ""
 "You cannot run the game directly from the Graphics Overhaul Mod. Instead, it "
diff --git a/po/zh_Hans.po b/po/zh_Hans.po
index 64b6cb5ce39..9cc18a2b0ee 100644
--- a/po/zh_Hans.po
+++ b/po/zh_Hans.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.9.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-18 09:25+0000\n"
+"POT-Creation-Date: 2026-06-18 09:40+0000\n"
 "PO-Revision-Date: 2024-12-01 22:18+0000\n"
 "Last-Translator: ?????? <clonewith at qq.com>\n"
 "Language-Team: Chinese (Simplified Han script) <https://"
@@ -11712,32 +11712,36 @@ msgstr "??????"
 msgid "Unlock"
 msgstr "??????"
 
-#: engines/mm/mm1/metaengine.cpp:127 engines/ultima/ultima4/metaengine.cpp:112
+#: engines/mm/mm1/metaengine.cpp:128 engines/ultima/ultima4/metaengine.cpp:112
 msgid "Goto location"
 msgstr "????????????"
 
-#: engines/mm/mm1/metaengine.cpp:152
+#: engines/mm/mm1/metaengine.cpp:154
 msgid "Might and Magic 1 - Menus"
 msgstr "Might and Magic 1 - ??????"
 
-#: engines/mm/mm1/metaengine.cpp:153
+#: engines/mm/mm1/metaengine.cpp:155
 msgid "Might and Magic 1 - Minimal Keys"
 msgstr "Might and Magic 1 - ????????????"
 
-#: engines/mm/mm1/metaengine.cpp:154
+#: engines/mm/mm1/metaengine.cpp:156
 #, fuzzy
 #| msgid "Might and Magic 1 - Menus"
 msgid "Might and Magic 1 - Main"
 msgstr "Might and Magic 1 - ??????"
 
-#: engines/mm/mm1/metaengine.cpp:155
+#: engines/mm/mm1/metaengine.cpp:157
 msgid "Might and Magic 1 - Party"
 msgstr "Might and Magic 1 - ??????"
 
-#: engines/mm/mm1/metaengine.cpp:156
+#: engines/mm/mm1/metaengine.cpp:158
 msgid "Might and Magic 1 - Combat"
 msgstr "Might and Magic 1 - ??????"
 
+#: engines/mm/mm1/metaengine.cpp:160
+msgid "Might and Magic 1 - Cheats"
+msgstr "Might and Magic 1 - ??????"
+
 #: engines/mm/mm1/mm1.cpp:57
 msgid ""
 "You cannot run the game directly from the Graphics Overhaul Mod. Instead, it "
@@ -18326,9 +18330,6 @@ msgstr ""
 #~ msgid "N"
 #~ msgstr "???"
 
-#~ msgid "Might and Magic 1 - Cheats"
-#~ msgstr "Might and Magic 1 - ??????"
-
 #~ msgid "3DO Plumbers requires RGB support."
 #~ msgstr "3DO Plumbers ?????? RGB ?????????"
 
diff --git a/po/zh_Hant.po b/po/zh_Hant.po
index fdc92ada17f..6ec5d8797db 100644
--- a/po/zh_Hant.po
+++ b/po/zh_Hant.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.9.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-18 09:25+0000\n"
+"POT-Creation-Date: 2026-06-18 09:40+0000\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: Automatically generated\n"
 "Language-Team: none\n"
@@ -10262,30 +10262,34 @@ msgstr ""
 msgid "Unlock"
 msgstr ""
 
-#: engines/mm/mm1/metaengine.cpp:127 engines/ultima/ultima4/metaengine.cpp:112
+#: engines/mm/mm1/metaengine.cpp:128 engines/ultima/ultima4/metaengine.cpp:112
 msgid "Goto location"
 msgstr ""
 
-#: engines/mm/mm1/metaengine.cpp:152
+#: engines/mm/mm1/metaengine.cpp:154
 msgid "Might and Magic 1 - Menus"
 msgstr ""
 
-#: engines/mm/mm1/metaengine.cpp:153
+#: engines/mm/mm1/metaengine.cpp:155
 msgid "Might and Magic 1 - Minimal Keys"
 msgstr ""
 
-#: engines/mm/mm1/metaengine.cpp:154
+#: engines/mm/mm1/metaengine.cpp:156
 msgid "Might and Magic 1 - Main"
 msgstr ""
 
-#: engines/mm/mm1/metaengine.cpp:155
+#: engines/mm/mm1/metaengine.cpp:157
 msgid "Might and Magic 1 - Party"
 msgstr ""
 
-#: engines/mm/mm1/metaengine.cpp:156
+#: engines/mm/mm1/metaengine.cpp:158
 msgid "Might and Magic 1 - Combat"
 msgstr ""
 
+#: engines/mm/mm1/metaengine.cpp:160
+msgid "Might and Magic 1 - Cheats"
+msgstr ""
+
 #: engines/mm/mm1/mm1.cpp:57
 msgid ""
 "You cannot run the game directly from the Graphics Overhaul Mod. Instead, it "



From noreply at scummvm.org  Thu Jun 18 11:21:05 2026
From: noreply at scummvm.org (dreammaster)
Date: Thu, 18 Jun 2026 11:21:05 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 94950c1f7c9db7697ea36668b687b2648d9110e1
Message-ID: <20260618112105.D558C7005CC@ron.scummvm.net>

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

Summary:
94950c1f7c MADS: DRAGONSPHERE: Fix accidental double play of speech lines


Commit: 94950c1f7c9db7697ea36668b687b2648d9110e1
    https://github.com/scummvm/scummvm/commit/94950c1f7c9db7697ea36668b687b2648d9110e1
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-06-18T21:20:53+10:00

Commit Message:
MADS: DRAGONSPHERE: Fix accidental double play of speech lines

Changed paths:
    engines/mads/madsv2/core/conv.cpp


diff --git a/engines/mads/madsv2/core/conv.cpp b/engines/mads/madsv2/core/conv.cpp
index e418a5bc6fb..66faa7aebb8 100644
--- a/engines/mads/madsv2/core/conv.cpp
+++ b/engines/mads/madsv2/core/conv.cpp
@@ -1349,44 +1349,33 @@ static void conv_generate_message(Conv *convIn, ConvData *convData,
 	if (msgListSize == 0)
 		goto done;
 
-	// TODO: Hook this up to ScummVM subtitles - original disabled reply boxes if in voice only mode
-	//if (speech_system_active && speech_on && speechCount > 0 && !show_subtitles) showBox = false;
-
 	if (showBox) {
-		if (msgListSize != 0) {
-			personSpeaking = conv_control.person_speaking;
-			if (!popup_create(popup_estimate_pieces(conv_control.width[personSpeaking]),
-				conv_control.x[personSpeaking], conv_control.y[personSpeaking])) {
-				if (conv_control.speaker_series[personSpeaking] >= 0) {
-					popup_add_icon(series_list[conv_control.speaker_series[personSpeaking]],
-						conv_control.speaker_frame[personSpeaking], 0);
-				}
-
-				for (int msgIndex = 0; msgIndex < msgListSize; ++msgIndex) {
-					messageId = msgList[msgIndex];
-					lineStart = convIn->messages[messageId].lineStart;
-					lineCount = convIn->messages[messageId].lineCount;
-
-					for (int lineCtr = 0; lineCtr < lineCount; ++lineCtr) {
-						Common::strcpy_s(tempString, conv_string(convIn, lineStart + lineCtr));
-						string_trim(tempString);
-						popup_write_string(tempString);
-					}
-				}
-
-				popup_next_line();
-				if (!popup_draw(-1, -1)) {
-					conv_control.popup_is_up = -1;
-					conv_control.popup_clock = kernel.clock + conv_control.popup_duration;
-
-					if (speech_system_active && speech_on) {
-						if (voiceListSize != 0) {
-							speech_play(convIn->speech_file, *voiceList);
-						}
-					}
-				}
+		// Create the popup
+		personSpeaking = conv_control.person_speaking;
+		if (popup_create(popup_estimate_pieces(conv_control.width[personSpeaking]),
+			conv_control.x[personSpeaking], conv_control.y[personSpeaking]))
+			goto done;
+
+		if (conv_control.speaker_series[personSpeaking] >= 0) {
+			popup_add_icon(series_list[conv_control.speaker_series[personSpeaking]],
+				conv_control.speaker_frame[personSpeaking], 0);
+		}
+
+		for (int msgIndex = 0; msgIndex < msgListSize; ++msgIndex) {
+			messageId = msgList[msgIndex];
+			lineStart = convIn->messages[messageId].lineStart;
+			lineCount = convIn->messages[messageId].lineCount;
+
+			for (int lineCtr = 0; lineCtr < lineCount; ++lineCtr) {
+				Common::strcpy_s(tempString, conv_string(convIn, lineStart + lineCtr));
+				string_trim(tempString);
+				popup_write_string(tempString);
 			}
 		}
+
+		popup_next_line();
+		if (popup_draw(-1, -1))
+			goto done;
 	}
 
 	// Record that a conversation popup is live.



From noreply at scummvm.org  Thu Jun 18 12:28:03 2026
From: noreply at scummvm.org (lotharsm)
Date: Thu, 18 Jun 2026 12:28:03 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 ae201eca39580c9b4ea6b1aab0b968ab4f59f677
Message-ID: <20260618122804.021F17005CC@ron.scummvm.net>

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

Summary:
ae201eca39 NEWS: Update German NEWS file


Commit: ae201eca39580c9b4ea6b1aab0b968ab4f59f677
    https://github.com/scummvm/scummvm/commit/ae201eca39580c9b4ea6b1aab0b968ab4f59f677
Author: Lothar Serra Mari (mail at serra.me)
Date: 2026-06-18T14:27:50+02:00

Commit Message:
NEWS: Update German NEWS file

Changed paths:
    doc/de/NEUES.md


diff --git a/doc/de/NEUES.md b/doc/de/NEUES.md
index 16eb0a3ba1e..54676164a73 100644
--- a/doc/de/NEUES.md
+++ b/doc/de/NEUES.md
@@ -9,6 +9,9 @@ Umfangreichere Informationen ??ber die ??nderungen des aktuellen Codes findest D
    - Unterst??tzung f??r Noctropolis hinzugef??gt.
    - Unterst??tzung f??r Cartoon Carnival hinzugef??gt.
    - Unterst??tzung f??r Alfred Pelrock: En Busca de un Sue??o hinzugef??gt.
+   - Unterst??tzung f??r Pilot Brothers: On the Track of Striped Elephant,
+     Pilot Brothers: The Case of Serial Maniac, und 5 kleinere Spiele,
+     die die Gamos-Engine nutzen, hinzugef??gt.
 
  Allgemein:
    - Klickbare Links in der Benutzeroberfl??che werden jetzt durch einen ver??nderten Cursor hervorgehoben.



From noreply at scummvm.org  Thu Jun 18 13:24:37 2026
From: noreply at scummvm.org (ScummVM-Translations)
Date: Thu, 18 Jun 2026 13:24:37 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 6232683e3c924478a4edaf803bf466b4956ecbf3
Message-ID: <20260618132437.CE2EE7005CC@ron.scummvm.net>

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

Summary:
6232683e3c I18N: Update translation (Spanish)


Commit: 6232683e3c924478a4edaf803bf466b4956ecbf3
    https://github.com/scummvm/scummvm/commit/6232683e3c924478a4edaf803bf466b4956ecbf3
Author: IlDucci (pajaroloco_2 at hotmail.com)
Date: 2026-06-18T13:24:31Z

Commit Message:
I18N: Update translation (Spanish)

Currently translated at 100.0% (3227 of 3227 strings)

Changed paths:
    po/es_ES.po


diff --git a/po/es_ES.po b/po/es_ES.po
index a7d5ba8720b..4d0bc896e15 100644
--- a/po/es_ES.po
+++ b/po/es_ES.po
@@ -8,7 +8,7 @@ msgstr ""
 "Project-Id-Version: ScummVM 1.4.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
 "POT-Creation-Date: 2026-06-18 09:40+0000\n"
-"PO-Revision-Date: 2026-06-14 09:53+0000\n"
+"PO-Revision-Date: 2026-06-18 13:24+0000\n"
 "Last-Translator: IlDucci <pajaroloco_2 at hotmail.com>\n"
 "Language-Team: Spanish <https://translations.scummvm.org/projects/scummvm/"
 "scummvm/es/>\n"
@@ -6369,11 +6369,13 @@ msgstr "Buscar actualizaciones..."
 
 #: engines/access/metaengine.cpp:82
 msgid "Use 'high definition' OGG music"
-msgstr ""
+msgstr "Utilizar m??sica en formato OGG y en ??alta definici??n??"
 
 #: engines/access/metaengine.cpp:83
 msgid "Use the OGG audio from the re-release instead of original MIDI tracks"
 msgstr ""
+"Utiliza los audios en formato .OGG del relanzamiento y no las pistas MIDI "
+"originales"
 
 #: engines/access/metaengine.cpp:236 engines/alcachofa/metaengine.cpp:94
 #: engines/bbvs/metaengine.cpp:92 engines/buried/metaengine.cpp:133
@@ -8243,8 +8245,6 @@ msgstr ""
 "Stealth desde el principio, con partidas guardadas nuevas."
 
 #: engines/colony/intro.cpp:154 engines/colony/intro.cpp:1244
-#, fuzzy
-#| msgid "New game"
 msgid "New Game"
 msgstr "Nueva partida"
 
@@ -8253,10 +8253,8 @@ msgid "Load Game"
 msgstr "Cargar partida"
 
 #: engines/colony/intro.cpp:1219 engines/colony/intro.cpp:1244
-#, fuzzy
-#| msgid "You have stored "
 msgid "You have been terminated."
-msgstr "Tienes "
+msgstr "Has fallecido."
 
 #: engines/colony/metaengine.cpp:45 engines/myst3/metaengine.cpp:40
 msgid "Widescreen mod"



From noreply at scummvm.org  Thu Jun 18 13:30:26 2026
From: noreply at scummvm.org (ScummVM-Translations)
Date: Thu, 18 Jun 2026 13:30:26 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm-web master ->
 096805eb7214a7f8b56144a1ef6206825e0245a4
Message-ID: <20260618133026.3BD1E7005CC@ron.scummvm.net>

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

Summary:
096805eb72 L10N: Update translation (Spanish) (News)


Commit: 096805eb7214a7f8b56144a1ef6206825e0245a4
    https://github.com/scummvm/scummvm-web/commit/096805eb7214a7f8b56144a1ef6206825e0245a4
Author: IlDucci (pajaroloco_2 at hotmail.com)
Date: 2026-06-18T13:30:20Z

Commit Message:
L10N: Update translation (Spanish) (News)

Currently translated at 94.3% (832 of 882 strings)

Changed paths:
    data/es/news.json


diff --git a/data/es/news.json b/data/es/news.json
index ee13652c..46d32195 100644
--- a/data/es/news.json
+++ b/data/es/news.json
@@ -1758,5 +1758,9 @@
     "20260614": {
         "title": "Alfred Pelrock se une a ScummVM",
         "content": "Buenas noticias para los fans de las aventuras: ??_Alfred Pelrock_ ya es compatible con ScummVM!\n\nPublicado originalmente en 1996, _Alfred Pelrock_ es una aventura point-and-click de humor con gr??ficos de estilo dibujos animados, acertijos estrafalarios y un sentido del humor irreverente. Tras so??ar con una princesa egipcia capturada en el pasado, Alfred se embarca en una aventura a trav??s del tiempo para rescatarla en el antiguo Egipto, donde encontrar?? situaciones curiosas y personajes exc??ntricos.\n\nAunque el juego est?? repleto de comedia y s??tira, debemos informaros de que tambi??n incluye contenidos sugerentes menores y humos ocasional adulto t??pico de su ??poca. Tambi??n debemos mencionar que el juego sali?? solo en espa??ol y que no existen traducciones oficiales.\n\nComo siempre, si pose??is una copia original del juego, pod??is descargaros la ??ltima [compilaci??n diaria de desarrollo](https://www.scummvm.org/downloads/#daily), a??adir los [archivos de datos necesarios](https://wiki.scummvm.org/index.php?title=Alfred_Pelrock:_En_Busca_de_un_Sue%C3%B1o) del juego y disfrutarlo. Tambi??n aceptamos [informes de fallos (en ingl??s)](https://bugs.scummvm.org/) y comentarios de los jugadores para mejorar la compatibilidad y poder preservar esta curiosa obra de la historia de las aventuras."
+    },
+    "20260617": {
+        "title": "ScummVM se prepara para la paciencia a rayas",
+        "content": "ScummVM ha terminado oficialmente de desarrollar el motor de juegos propietario Gamos para Windows. Originalmente estaba pensado para _Pilot Brothers 1 y 2_ y dando el pistoletazo de salida a la ic??nica saga de aventuras antes de que Gamos cambiase a los motores qdEngine y NGI. Gamos tambi??n se utiliz?? para desarrollar bastantes juegos para oficinas.\n\nEste motor da soporte a 7 juegos:\n\n* _Patience_\n* _Flip-Flop_\n* _NetWalk_\n* _Pilot Brothers: On the Track of Striped Elephant_\n* _Pilot Brothers: The Case of Serial Maniac_\n* _Vitamin_\n* _WildSnake_\n\nAs?? que si quer??is recordar el pasado con los exc??ntricos detectives Chief y Colleague o echar el rato con juegos tipo solitario, necesitar??is la ??ltima [compilaci??n diaria de desarrollo](https://www.scummvm.org/downloads/#daily) y los archivos de datos necesarios para cada juego. Como siempre, si encontr??is cualquier cosa rara, os rogamos que lo coment??is en [nuestro gestor de fallos (en ingl??s)](https://bugs.scummvm.org/)."
     }
 }



From noreply at scummvm.org  Thu Jun 18 16:55:53 2026
From: noreply at scummvm.org (sev-)
Date: Thu, 18 Jun 2026 16:55:53 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 2dbcc8111d074f0110391140f5228692346a543c
Message-ID: <20260618165553.165627005CC@ron.scummvm.net>

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

Summary:
2dbcc8111d GAMOS: Added GOG/Steam versions to detection and mark as unsupported


Commit: 2dbcc8111d074f0110391140f5228692346a543c
    https://github.com/scummvm/scummvm/commit/2dbcc8111d074f0110391140f5228692346a543c
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2026-06-18T18:55:40+02:00

Commit Message:
GAMOS: Added GOG/Steam versions to detection and mark as unsupported

The original engine sources were lost, so the new re-releases are
using completely different engine

Changed paths:
    engines/gamos/detection.cpp
    engines/gamos/detection_tables.h


diff --git a/engines/gamos/detection.cpp b/engines/gamos/detection.cpp
index 82a852d9911..2a24aa65db8 100644
--- a/engines/gamos/detection.cpp
+++ b/engines/gamos/detection.cpp
@@ -33,6 +33,7 @@ class GamosMetaEngineDetection : public AdvancedMetaEngineDetection<Gamos::Gamos
 public:
 	GamosMetaEngineDetection(): AdvancedMetaEngineDetection(
 		    Gamos::gameDescriptions, Gamos::gamosGames) {
+			_flags = kADFlagMatchFullPaths;
 	}
 	~GamosMetaEngineDetection() override {}
 
diff --git a/engines/gamos/detection_tables.h b/engines/gamos/detection_tables.h
index 71a6bf5da24..f9c39dd6e6e 100644
--- a/engines/gamos/detection_tables.h
+++ b/engines/gamos/detection_tables.h
@@ -138,6 +138,19 @@ const GamosGameDescription gameDescriptions[] = {
 		},
 		0x80000016
 	},
+	{
+		{ /* GOG/Steam version, not supported, different engine. v gog-3, v 2.0.0.3 */
+			"pilots1",
+			"The game is using unsupported engine",
+			AD_ENTRY2s("bin/Pilot Brothers.exe", "487170c62bbc2a2f03eb672e9520fddb", 474624,
+					   "content/sprites/s000.tex", "cd7bb45147ad91d4e4682c1fd0db9e4d", 397869),
+			Common::EN_ANY,
+			Common::kPlatformWindows,
+			ADGF_UNSUPPORTED,
+			GUIO1(GUIO_NONE)
+		},
+		0x80000016
+	},
 	{
 		{ /* Copyright (C) 1997 by Gamos JSC, Moscow, Russia...  Run-time engine [1.50] by Vadim Sytnikov
 			17.12.1997 */
@@ -164,6 +177,19 @@ const GamosGameDescription gameDescriptions[] = {
 		},
 		0x80000018
 	},
+	{
+		{ /* GOG/Steam version, not supported, different engine v2.0.0,2 */
+			"pilots2",
+			"The game is using unsupported engine",
+			AD_ENTRY2s("bin/Pilot Brothers.exe", "4d7b3f9a03c4517dadf536ddc378f195", 428544,
+					   "content/sprites/s000.tex", "33714cdb4c53c234c23d8a9417a74dbd", 397637),
+			Common::EN_ANY,
+			Common::kPlatformWindows,
+			ADGF_UNSUPPORTED,
+			GUIO1(GUIO_NONE)
+		},
+		0x80000016
+	},
 	{
 		{ /* Copyright (C) 1998 by Gamos JSC, Moscow, Russia...  Run-time engine [1.51] by Vadim Sytnikov
 			03.12.1998 */



From noreply at scummvm.org  Thu Jun 18 21:00:57 2026
From: noreply at scummvm.org (sev-)
Date: Thu, 18 Jun 2026 21:00:57 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 bc118d9d7ec19e6a3cc237c26b2cb4c4385ef2df
Message-ID: <20260618210057.161117005CC@ron.scummvm.net>

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:
66e4f17dce DIRECTOR: Sync cast member save code with loading for D6+
4c09d65ca3 DIRECTOR: Cover D6+ in cast member getCastDataSize() for save round-trip
bc118d9d7e DIRECTOR: Only resolve the frame script-channel behavior if it actually has	a handler for this event.


Commit: 66e4f17dcead467d37d6a3c2b2038914c851e1d0
    https://github.com/scummvm/scummvm/commit/66e4f17dcead467d37d6a3c2b2038914c851e1d0
Author: Lariaa (30549703+Lariaa at users.noreply.github.com)
Date: 2026-06-18T23:00:51+02:00

Commit Message:
DIRECTOR: Sync cast member save code with loading for D6+
Tested with TKKG2

Changed paths:
    engines/director/archive-save.cpp
    engines/director/castmember/castmember.cpp
    engines/director/castmember/script.cpp
    engines/director/score.cpp


diff --git a/engines/director/archive-save.cpp b/engines/director/archive-save.cpp
index 1e2fbd43db9..185482cfd4e 100644
--- a/engines/director/archive-save.cpp
+++ b/engines/director/archive-save.cpp
@@ -64,7 +64,8 @@ bool RIFXArchive::writeToFile(Common::String filename, Movie *movie) {
 	_size = getArchiveSize(builtResources);
 	saveFile->writeUint32LE(_metaTag); // The _metaTag is "RIFX" or "XFIR"
 
-	saveFile->writeUint32LE(getResourceSize(_metaTag, 0) - 8); // The size of the RIFX archive, except header and size
+	// We need to recompute the chunk size here, to accommodate any resournce changes
+	saveFile->writeUint32LE(getArchiveSize(builtResources) + 4);
 	saveFile->writeUint32LE(_rifxType);	// e.g. "MV93", "MV95"
 
 	switch (_rifxType) {
@@ -155,8 +156,8 @@ bool RIFXArchive::writeToFile(Common::String filename, Movie *movie) {
 			break;
 
 		case MKTAG('S', 'C', 'V', 'W'):
-			{
 
+			{
 				uint32 parentIndex = findParentIndex(it->tag, it->index);
 				Resource parent = castResMap[parentIndex];
 
@@ -278,13 +279,15 @@ bool RIFXArchive::writeAfterBurnerMap(Common::SeekableWriteStream *writeStream)
 bool RIFXArchive::writeKeyTable(Common::SeekableWriteStream *writeStream, uint32 offset) {
 	writeStream->seek(offset);
 
+	uint32 numEntries = (getKeyTableResourceSize() - 12) / 12;
+
 	writeStream->writeUint32LE(MKTAG('K', 'E', 'Y', '*'));
-	writeStream->writeUint32LE(getResourceSize(MKTAG('K', 'E', 'Y', '*'), getResourceIDList(MKTAG('K', 'E', 'Y', '*'))[0]));
+	writeStream->writeUint32LE(getKeyTableResourceSize());
 
 	writeStream->writeUint16LE(_keyTableEntrySize);
 	writeStream->writeUint16LE(_keyTableEntrySize2);
-	writeStream->writeUint32LE(_keyTableEntryCount);
-	writeStream->writeUint32LE(_keyTableUsedCount);
+	writeStream->writeUint32LE(numEntries);
+	writeStream->writeUint32LE(numEntries);
 
 	debugC(3, kDebugSaving, "RIFXArchive::writeKeyTable: writing key table:");
 
@@ -602,7 +605,7 @@ Common::Array<Resource *> RIFXArchive::rebuildResources(Movie *movie) {
 	// Now that all sizes have been updated, we can safely calculate the overall archive size
 	for (auto &it : builtResources) {
 		if (it->tag == MKTAG('R', 'I', 'F', 'X') || it->tag == MKTAG('X', 'F', 'I', 'R')) {
-			it->size = getArchiveSize(builtResources) + 8;
+			it->size = getArchiveSize(builtResources) + 4;
 		}
 	}
 
diff --git a/engines/director/castmember/castmember.cpp b/engines/director/castmember/castmember.cpp
index e1da80eab3e..abfd8e59f17 100644
--- a/engines/director/castmember/castmember.cpp
+++ b/engines/director/castmember/castmember.cpp
@@ -389,7 +389,7 @@ uint32 CastMember::writeCAStResource(Common::SeekableWriteStream *writeStream) {
 		if (castInfoToWrite) {
 			_cast->writeCastInfo(writeStream, _castId);
 		}
-	} else if (_cast->_version >= kFileVer500 && _cast->_version < kFileVer600) {
+	} else if (_cast->_version >= kFileVer500 && _cast->_version < kFileVer1200) {
 		writeStream->writeUint32BE((uint32)_type);
 		writeStream->writeUint32BE(castInfoToWrite);
 		writeStream->writeUint32BE(castDataToWrite);
@@ -445,8 +445,7 @@ uint32 CastMember::getCastResourceSize() {
 		if (_flags1 != 0xFF) {
 			headerSize += 1;
 		}
-	} else if (_cast->_version >= kFileVer500 && _cast->_version < kFileVer600) {
-		// Header size for director version 5
+	} else if (_cast->_version >= kFileVer500 && _cast->_version < kFileVer1200) {
 		headerSize = 12;		// See Cast::loadCastData() for director version 5
 	}
 
diff --git a/engines/director/castmember/script.cpp b/engines/director/castmember/script.cpp
index 0432b3573cc..4d78f052e7e 100644
--- a/engines/director/castmember/script.cpp
+++ b/engines/director/castmember/script.cpp
@@ -146,17 +146,16 @@ uint32 ScriptCastMember::getCastDataSize() {
 	if (_cast->_version >= kFileVer400 && _cast->_version < kFileVer500) {
 		// 2 bytes for type and unk1 + 2 byte for castType and flags ma(see Cast::loadCastData() for Director 4 only
 		return 2 + 2;
-	} else if (_cast->_version >= kFileVer500 && _cast->_version < kFileVer600) {
-		// type and unk1: 2 bytes
+	} else if (_cast->_version >= kFileVer500 && _cast->_version < kFileVer1200) {
 		return 2;
 	} else {
-		warning("ScriptCastMember::writeCastData(): invalid or unhandled Script version: %d", _cast->_version);
+		warning("ScriptCastMember::getCastDataSize(): invalid or unhandled Script version: %d", _cast->_version);
 		return 0;
 	}
 }
 
 void ScriptCastMember::writeCastData(Common::SeekableWriteStream *writeStream) {
-	if (_cast->_version >= kFileVer400 && _cast->_version < kFileVer600) {
+	if (_cast->_version >= kFileVer400 && _cast->_version < kFileVer1200) {
 		writeStream->writeByte(0);		// unknown
 
 		switch (_scriptType) {
diff --git a/engines/director/score.cpp b/engines/director/score.cpp
index 29dc5cd5d14..80c77dcf717 100644
--- a/engines/director/score.cpp
+++ b/engines/director/score.cpp
@@ -2462,8 +2462,14 @@ void Score::writeVWSCResource(Common::SeekableWriteStream *writeStream, uint32 o
 	} else if (_version >= kFileVer500 && _version < kFileVer600) {
 		channelSize = kSprChannelSizeD5;
 		mainChannelSize = kMainChannelSizeD5;
+	} else if (_version >= kFileVer600 && _version < kFileVer700) {
+		channelSize = kSprChannelSizeD6;
+		mainChannelSize = kMainChannelSizeD6;
+	} else if (_version >= kFileVer700 && _version < kFileVer1100) {
+		channelSize = kSprChannelSizeD7;
+		mainChannelSize = kMainChannelSizeD7;
 	} else {
-		warning("FilmLoopCastMember::writeSCVWResource: Writing Director Version 6+ not supported yet");
+		warning("Score::writeVWSCResource: Writing this Director version is not supported yet");
 		return;
 	}
 
@@ -2523,6 +2529,10 @@ void Score::writeFrame(Common::SeekableWriteStream *writeStream, Frame frame, ui
 			writeSpriteDataD4(writeStream, sprite);
 		} else if (_version >= kFileVer500 && _version < kFileVer600) {
 			writeSpriteDataD5(writeStream, sprite);
+		} else if (_version >= kFileVer600 && _version < kFileVer700) {
+			writeSpriteDataD6(writeStream, sprite);
+		} else if (_version >= kFileVer700 && _version < kFileVer1100) {
+			writeSpriteDataD7(writeStream, sprite);
 		}
 	}
 }
@@ -2533,11 +2543,18 @@ uint32 Score::getVWSCResourceSize() {
 	if (_version >= kFileVer400 && _version < kFileVer500) {
 		channelSize = kSprChannelSizeD4;
 		mainChannelSize = kMainChannelSizeD4;
-	} else if (_version >= kFileVer500) {
+	} else if (_version >= kFileVer500 && _version < kFileVer600) {
 		channelSize = kSprChannelSizeD5;
 		mainChannelSize = kMainChannelSizeD5;
+	} else if (_version >= kFileVer600 && _version < kFileVer700) {
+		// Must match writeVWSCResource()/Frame::readChannel() (D6: main channel 144).
+		channelSize = kSprChannelSizeD6;
+		mainChannelSize = kMainChannelSizeD6;
+	} else if (_version >= kFileVer700 && _version < kFileVer1100) {
+		channelSize = kSprChannelSizeD7;
+		mainChannelSize = kMainChannelSizeD7;
 	} else {
-		warning("FilmLoopCastMember::getSCVWResourceSize: Director version unsupported");
+		warning("Score::getVWSCResourceSize: Director version unsupported");
 	}
 
 	uint32 framesSize = 0;


Commit: 4c09d65ca3fe00eee604c9bdd6ec5682c24f47ff
    https://github.com/scummvm/scummvm/commit/4c09d65ca3fe00eee604c9bdd6ec5682c24f47ff
Author: Lariaa (30549703+Lariaa at users.noreply.github.com)
Date: 2026-06-18T23:00:51+02:00

Commit Message:
DIRECTOR: Cover D6+ in cast member getCastDataSize() for save round-trip

Changed paths:
    engines/director/castmember/digitalvideo.cpp
    engines/director/castmember/filmloop.cpp
    engines/director/castmember/palette.cpp
    engines/director/castmember/shape.cpp
    engines/director/castmember/sound.cpp
    engines/director/castmember/transition.cpp


diff --git a/engines/director/castmember/digitalvideo.cpp b/engines/director/castmember/digitalvideo.cpp
index facd9065913..46ef7911ebe 100644
--- a/engines/director/castmember/digitalvideo.cpp
+++ b/engines/director/castmember/digitalvideo.cpp
@@ -740,7 +740,7 @@ uint32 DigitalVideoCastMember::getCastDataSize() {
 	if (_cast->_version >= kFileVer400 && _cast->_version < kFileVer500) {
 		// It has been observed that the DigitalVideoCastMember has _flags set to 0x00
 		return (_flags1 == 0xFF) ? 13 : 14;
-	} else if (_cast->_version >= kFileVer500 && _cast->_version < kFileVer600) {
+	} else if (_cast->_version >= kFileVer500 && _cast->_version < kFileVer1100) {
 		return 8 + 4;
 	}
 
diff --git a/engines/director/castmember/filmloop.cpp b/engines/director/castmember/filmloop.cpp
index ca01dfd5440..0cff9c8ec7d 100644
--- a/engines/director/castmember/filmloop.cpp
+++ b/engines/director/castmember/filmloop.cpp
@@ -267,7 +267,7 @@ uint32 FilmLoopCastMember::getCastDataSize() {
 	if (_cast->_version >= kFileVer400 && _cast->_version < kFileVer500) {
 		// It has been observed that the FilmCastMember has _flags as 0x00
 		return 8 + 4 + 2 + 2;
-	} else if (_cast->_version >= kFileVer500 && _cast->_version < kFileVer600) {
+	} else if (_cast->_version >= kFileVer500 && _cast->_version < kFileVer700) {
 		return 8 + 4 + 2;
 	}
 
diff --git a/engines/director/castmember/palette.cpp b/engines/director/castmember/palette.cpp
index 6994cff3edb..7534a8cb4ef 100644
--- a/engines/director/castmember/palette.cpp
+++ b/engines/director/castmember/palette.cpp
@@ -138,8 +138,8 @@ void PaletteCastMember::unload() {
 // PaletteCastMember has no data in the 'CASt' resource or is ignored
 // This is the data in 'CASt' resource
 uint32 PaletteCastMember::getCastDataSize() {
-	if (_cast->_version >= kFileVer500 && _cast->_version < kFileVer600) {
-		// It has been observed as well that the Data size in PaletteCastMember's CASt resource is 0 for d5
+	if (_cast->_version >= kFileVer500 && _cast->_version < kFileVer1100) {
+		// D5 onward palette lives in the CLUT resources
 		return 0;
 	} else if (_cast->_version >= kFileVer400 && _cast->_version < kFileVer500) {
 		// (castType (see Cast::loadCastData() for Director 4 only) 1 byte
diff --git a/engines/director/castmember/shape.cpp b/engines/director/castmember/shape.cpp
index c57f355b334..5b443c14dc4 100644
--- a/engines/director/castmember/shape.cpp
+++ b/engines/director/castmember/shape.cpp
@@ -222,10 +222,10 @@ uint32 ShapeCastMember::getCastDataSize() {
 	// For Director 4 : 1 byte extra for casttype (See Cast::loadCastData())
 	if (_cast->_version >= kFileVer400 && _cast->_version < kFileVer500) {
 		return 17 + 1;
-	} else if (_cast->_version >= kFileVer500 && _cast->_version < kFileVer600) {
+	} else if (_cast->_version >= kFileVer500 && _cast->_version < kFileVer1100) {
 		return 17;
 	} else {
-		warning("ScriptCastMember::writeCastData(): invalid or unhandled Script version: %d", _cast->_version);
+		warning("ShapeCastMember::getCastDataSize(): invalid or unhandled cast version: %d", _cast->_version);
 		return 0;
 	}
 }
diff --git a/engines/director/castmember/sound.cpp b/engines/director/castmember/sound.cpp
index 16bb0f1b475..1c8db8cf9b7 100644
--- a/engines/director/castmember/sound.cpp
+++ b/engines/director/castmember/sound.cpp
@@ -303,7 +303,8 @@ void SoundCastMember::setField(int field, const Datum &d) {
 // Similar to PaletteCastMember, SoundCastMember has no data in the 'CASt' resource or is ignored
 // This is the data in 'CASt' resource
 uint32 SoundCastMember::getCastDataSize() {
-	if (_cast->_version >= kFileVer500 && _cast->_version < kFileVer600) {
+	if (_cast->_version >= kFileVer500 && _cast->_version < kFileVer700) {
+		// D5+  sound lives in snd/sndH children
 		return 0;
 	} else if (_cast->_version >= kFileVer400 && _cast->_version < kFileVer500) {
 		// (castType (see Cast::loadCastData() for Director 4 only) 1 byte
diff --git a/engines/director/castmember/transition.cpp b/engines/director/castmember/transition.cpp
index 5fddf31dfd9..67801162134 100644
--- a/engines/director/castmember/transition.cpp
+++ b/engines/director/castmember/transition.cpp
@@ -126,12 +126,12 @@ Common::String TransitionCastMember::formatInfo() {
 }
 
 uint32 TransitionCastMember::getCastDataSize() {
-	if (_cast->_version >= kFileVer500 && _cast->_version < kFileVer600) {
+	if (_cast->_version >= kFileVer500 && _cast->_version < kFileVer1100) {
 		// Ignored 1 byte
 		// _chunkSize 1 byte
 		// _transType 1 byte
 		// _flags 1 byte
-		// _durationMiilis 2 bytes
+		// _durationMillis 2 bytes
 		return 6;
 	} else {
 		warning("TransitionCastMember()::getCastDataSize(): CastMember version invalid or not handled");
@@ -140,12 +140,12 @@ uint32 TransitionCastMember::getCastDataSize() {
 }
 
 void TransitionCastMember::writeCastData(Common::SeekableWriteStream *writeStream) {
-	if (_cast->_version >= kFileVer400 && _cast->_version < kFileVer600) {
+	if (_cast->_version >= kFileVer400 && _cast->_version < kFileVer1100) {
 		writeStream->writeByte(0);
 		writeStream->writeByte(_chunkSize);
 		writeStream->writeByte((uint8)_transType);
 		writeStream->writeByte(_flags);
-		writeStream->writeUint16LE(_durationMillis);
+		writeStream->writeUint16BE(_durationMillis);
 	} else {
 		warning("TransitionCastMember()::writeCastData(): CastMember version invalid or not handled");
 	}


Commit: bc118d9d7ec19e6a3cc237c26b2cb4c4385ef2df
    https://github.com/scummvm/scummvm/commit/bc118d9d7ec19e6a3cc237c26b2cb4c4385ef2df
Author: Lariaa (30549703+Lariaa at users.noreply.github.com)
Date: 2026-06-18T23:00:51+02:00

Commit Message:
DIRECTOR: Only resolve the frame script-channel behavior if it actually has	a handler for this event.
Otherwise a handler-less frame behavior would be run as a no-op for keyDown/mouseDown yet still consume the pass-through swallowing the subsequent movie-level handler.
Fixes language selection in TKKG3+

Changed paths:
    engines/director/lingo/lingo-events.cpp


diff --git a/engines/director/lingo/lingo-events.cpp b/engines/director/lingo/lingo-events.cpp
index d44bc5b28fe..2304a3c8a52 100644
--- a/engines/director/lingo/lingo-events.cpp
+++ b/engines/director/lingo/lingo-events.cpp
@@ -350,7 +350,8 @@ void Movie::resolveScriptEvent(LingoEvent &event) {
 				return;
 
 			if (_vm->getVersion() >= 600) {
-				if (_score->_scriptChannelScriptInstance.type == OBJECT) {
+				if (_score->_scriptChannelScriptInstance.type == OBJECT &&
+						_score->_scriptChannelScriptInstance.u.obj->getMethod(_lingo->_eventHandlerTypes[event.event]).type != VOIDSYM) {
 					event.scriptType = kScoreScript;
 					event.scriptId = CastMemberID(); // No ID for the script channel script
 					event.scriptInstance = _score->_scriptChannelScriptInstance.u.obj;



From noreply at scummvm.org  Thu Jun 18 21:05:06 2026
From: noreply at scummvm.org (sev-)
Date: Thu, 18 Jun 2026 21:05:06 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 9bb31cb1daa3d45b04827bb57ae00cd8f8f73834
Message-ID: <20260618210506.267C47005CC@ron.scummvm.net>

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

Summary:
9bb31cb1da DIRECTOR: Correctly process empty MCsL/VWFI


Commit: 9bb31cb1daa3d45b04827bb57ae00cd8f8f73834
    https://github.com/scummvm/scummvm/commit/9bb31cb1daa3d45b04827bb57ae00cd8f8f73834
Author: Lariaa (30549703+Lariaa at users.noreply.github.com)
Date: 2026-06-18T23:04:45+02:00

Commit Message:
DIRECTOR: Correctly process empty MCsL/VWFI

Changed paths:
    engines/director/movie.cpp


diff --git a/engines/director/movie.cpp b/engines/director/movie.cpp
index 5ec6b45a37a..e644c5bffc7 100644
--- a/engines/director/movie.cpp
+++ b/engines/director/movie.cpp
@@ -135,6 +135,12 @@ void Movie::setArchive(Common::SharedPtr<Archive> archive) {
 		// D4 or lower, only 1 cast
 		_cast->setArchive(archive);
 	}
+
+	// The cast-lib mapping (MCsL) may be empty or may not list the default
+	// internal library
+	if (!_cast->getArchive())
+		_cast->setArchive(archive);
+
 	// Frame Labels
 	if ((r = archive->getMovieResourceIfPresent(MKTAG('V', 'W', 'L', 'B')))) {
 		_score->loadLabels(*r);



From noreply at scummvm.org  Thu Jun 18 21:06:13 2026
From: noreply at scummvm.org (bluegr)
Date: Thu, 18 Jun 2026 21:06:13 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 11ab98f89aabd732bd376b6b0e2ccf95a8c061a7
Message-ID: <20260618210613.907E27005CC@ron.scummvm.net>

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:
960486d2ba NANCY: Implement animations when placing pieces in multibuildpuzzle
11ab98f89a NANCY: Fix label when using the ring at the slot machine in Nancy 10


Commit: 960486d2bac39060b53ca1657d59a4c8c0ac5914
    https://github.com/scummvm/scummvm/commit/960486d2bac39060b53ca1657d59a4c8c0ac5914
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-06-19T00:05:59+03:00

Commit Message:
NANCY: Implement animations when placing pieces in multibuildpuzzle

Functionality introduced in Nancy 10. Now, the mixing animation is
shown when placing ingredients in the bowl in the cake mixing puzzle

Changed paths:
    engines/nancy/action/puzzle/multibuildpuzzle.cpp
    engines/nancy/action/puzzle/multibuildpuzzle.h


diff --git a/engines/nancy/action/puzzle/multibuildpuzzle.cpp b/engines/nancy/action/puzzle/multibuildpuzzle.cpp
index 4655799b4da..a50e4b2ef64 100644
--- a/engines/nancy/action/puzzle/multibuildpuzzle.cpp
+++ b/engines/nancy/action/puzzle/multibuildpuzzle.cpp
@@ -46,6 +46,21 @@ void MultiBuildPuzzle::init() {
 		_closeupImage.setTransparentColor(_drawSurface.getTransparentColor());
 	}
 
+	if (_hasAnimImage) {
+		_animRender._drawSurface.create(_animRect.width(), _animRect.height(),
+				g_nancy->_graphics->getInputPixelFormat());
+		_animRender.setTransparent(true);
+		_animRender._drawSurface.clear(g_nancy->_graphics->getTransColor());
+		_animRender.moveTo(_animRect);
+		_animRender.setZOrder((uint16)(_z + 80 + 10));
+		_animRender.setVisible(true);
+
+		g_nancy->_resource->loadImage(_animImageName, _animImage);
+		_animImage.setTransparentColor(g_nancy->_graphics->getTransColor());
+
+		_animSurfaceReady = true;
+	}
+
 	for (uint i = 0; i < _pieces.size(); ++i) {
 		Piece &p = _pieces[i];
 
@@ -85,6 +100,9 @@ void MultiBuildPuzzle::registerGraphics() {
 	if (!_isInitialized)
 		return;
 
+	if (_animSurfaceReady)
+		_animRender.registerGraphics();
+
 	for (uint i = 0; i < _pieces.size(); ++i)
 		_pieces[i].registerGraphics();
 }
@@ -119,8 +137,7 @@ void MultiBuildPuzzle::readData(Common::SeekableReadStream &stream) {
 	_checkOverlapOnDrop = stream.readByte() != 0;
 
 	if (isNancy10) {
-		// Completion-animation block: image name + sprite-sheet rect + 4 layout
-		// shorts (cols, framesPerStep, spacing, totalRows). TODO: not wired up.
+		// Anim block: name + rect + 4 layout shorts (cols/framesPerStep/spacing/totalRows)
 		Common::String animName;
 		readFilename(stream, animName);
 		_animImageName = Common::Path(animName);
@@ -225,6 +242,56 @@ void MultiBuildPuzzle::execute() {
 				_state = kActionTrigger;
 			}
 			break;
+
+		case kAnimStep: {
+			if (!_hasAnimImage) {
+				if (g_system->getMillis() >= _timerEnd)
+					_solveState = kIdle;
+				break;
+			}
+
+			_animActive = true;
+
+			if (_dropSound.name != "NO SOUND" &&
+					!g_nancy->_sound->isSoundPlaying(_dropSound)) {
+				g_nancy->_sound->playSound(_dropSound);
+			}
+
+			if (_animLayout[1] > 0 &&
+					_animFrameCounter / _animLayout[1] >= _animLayout[0]) {
+				_animFrameCounter = 0;
+				_animRowCounter++;
+			}
+
+			if (_animRowCounter < _animLayout[3] && _animLayout[1] > 0) {
+				// pdW/pdH match the raw inclusive `right - left` (our exclusive width - 1)
+				int pdW = _animRect.width()  - 1;
+				int pdH = _animRect.height() - 1;
+				int spacing = _animLayout[2];
+				int col = _animFrameCounter % _animLayout[1];
+				int row = _animFrameCounter / _animLayout[1];
+				int srcL = (pdW + spacing) * col + 1;
+				int srcT = (pdH + spacing) * row + 1;
+				_animSrcRect = Common::Rect(srcL, srcT, srcL + pdW, srcT + pdH);
+				_animFrameCounter++;
+				renderAnimFrame();
+				_animFrameWaitEnd = g_system->getMillis() + 100;
+				_solveState = kAnimWaitFrame;
+			} else {
+				_animRowCounter = 0;
+				_animFrameCounter = 0;
+				_animEnded = true;
+				_animActive = false;
+				clearAnimFrame();
+				_solveState = kIdle;
+			}
+			break;
+		}
+
+		case kAnimWaitFrame:
+			if (g_system->getMillis() >= _animFrameWaitEnd)
+				_solveState = kAnimStep;
+			break;
 		}
 		break;
 
@@ -484,7 +551,14 @@ void MultiBuildPuzzle::handleInput(NancyInput &input) {
 			if (_autoSolveOnDrop || _pieces.size() > 79)
 				checkIfSolved();
 
-			if (_solveState == kIdle) {
+			if (validDrop && _hasAnimImage && _solveState != kAnimStep &&
+					_solveState != kAnimWaitFrame) {
+				_animFrameCounter = 0;
+				_animRowCounter = 0;
+				_animActive = true;
+				_animEnded = false;
+				_solveState = kAnimStep;
+			} else if (_solveState == kIdle) {
 				_solveState = kWaitTimer;
 				_timerEnd = g_system->getMillis() + 300;
 			}
@@ -636,13 +710,6 @@ bool MultiBuildPuzzle::updateSolveFlags() {
 	if (total < _requiredPieces)
 		return false;
 
-	if (_cancelScene._flag.label != kFlagNoLabel)
-		NancySceneState.setEventFlag(_cancelScene._flag);
-
-	// Preemptively clear the solve flag; the exact-match check below re-sets it.
-	if (_solveScene._flag.label != kFlagNoLabel)
-		NancySceneState.setEventFlag(_solveScene._flag.label, _solveScene._flag.flag ? 0 : 1);
-
 	for (uint i = 0; i < _numPieces; ++i) {
 		if (_pieces[i].placeCount > 0 && _pieces[i].mustNotPlace > 0)
 			return false;
@@ -650,8 +717,8 @@ bool MultiBuildPuzzle::updateSolveFlags() {
 			return false;
 	}
 
-	if (_solveScene._flag.label != kFlagNoLabel)
-		NancySceneState.setEventFlag(_solveScene._flag);
+	// Cancel flag is owned by kActionTrigger so it doesn't retrigger per drop
+	NancySceneState.setEventFlag(_solveScene._flag);
 
 	return true;
 }
@@ -710,6 +777,22 @@ void MultiBuildPuzzle::checkIfSolved() {
 	_solveState = kWaitSolveSound;
 }
 
+void MultiBuildPuzzle::renderAnimFrame() {
+	if (!_animSurfaceReady || _animImage.empty())
+		return;
+
+	_animRender._drawSurface.clear(g_nancy->_graphics->getTransColor());
+	_animRender._drawSurface.blitFrom(_animImage, _animSrcRect, Common::Point(0, 0));
+	_animRender.setNeedsRedraw(true);
+}
+
+void MultiBuildPuzzle::clearAnimFrame() {
+	if (!_animSurfaceReady)
+		return;
+	_animRender._drawSurface.clear(g_nancy->_graphics->getTransColor());
+	_animRender.setNeedsRedraw(true);
+}
+
 void MultiBuildPuzzle::updatePieceRender(int pieceIdx) {
 	Piece &p = _pieces[pieceIdx];
 	bool isSelected = (!_isDragging && pieceIdx == _selectedPiece);
diff --git a/engines/nancy/action/puzzle/multibuildpuzzle.h b/engines/nancy/action/puzzle/multibuildpuzzle.h
index c47b1382355..66f8a9d6317 100644
--- a/engines/nancy/action/puzzle/multibuildpuzzle.h
+++ b/engines/nancy/action/puzzle/multibuildpuzzle.h
@@ -138,11 +138,31 @@ protected:
 		kIdle           = 0,
 		kWaitTimer      = 1,
 		kWaitSolveSound = 4,
-		kPlaySolveSound = 5
+		kPlaySolveSound = 5,
+		kAnimStep       = 6, // Blit current frame, advance counters
+		kAnimWaitFrame  = 7  // ~100 ms hold between frames
 	};
 	SolveState _solveState = kIdle;
 	uint32 _timerEnd = 0;
 
+	// Nancy 10 mixing animation
+	struct AnimRender : RenderObject {
+		AnimRender() : RenderObject(0) {}
+		bool isViewportRelative() const override { return true; }
+	};
+
+	Graphics::ManagedSurface _animImage;
+	AnimRender _animRender;
+	uint16 _animFrameCounter = 0;
+	uint16 _animRowCounter = 0;
+	uint32 _animFrameWaitEnd = 0;
+	Common::Rect _animSrcRect;
+	bool _animActive = false;
+	bool _animEnded = false;
+	bool _animSurfaceReady = false;
+	void renderAnimFrame();
+	void clearAnimFrame();
+
 	int16 _pickedUpWidth = 0;
 	int16 _pickedUpHeight = 0;
 


Commit: 11ab98f89aabd732bd376b6b0e2ccf95a8c061a7
    https://github.com/scummvm/scummvm/commit/11ab98f89aabd732bd376b6b0e2ccf95a8c061a7
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-06-19T00:06:01+03:00

Commit Message:
NANCY: Fix label when using the ring at the slot machine in Nancy 10

Also, restore an old comment as to how and why we deviate from the
original in Overlay::execute()

Changed paths:
    engines/nancy/action/overlay.cpp


diff --git a/engines/nancy/action/overlay.cpp b/engines/nancy/action/overlay.cpp
index dcf30656ceb..55a6da14b21 100644
--- a/engines/nancy/action/overlay.cpp
+++ b/engines/nancy/action/overlay.cpp
@@ -360,7 +360,14 @@ void Overlay::execute() {
 		break;
 	}
 	case kActionTrigger:
-		setVisible(false);
+		if (g_nancy->getGameType() <= kGameTypeNancy9) {
+			// This isn't done by the original engine, but it's here
+			// to fix Nancy1's safe lock light not turning off. Removing
+			// it for Nancy 10, to fix the animated label showing correctly,
+			// when using the ring at the slot machine.
+			setVisible(false);
+		}
+
 		g_nancy->_sound->stopSound(_sound);
 
 		_flagsOnTrigger.execute();



From noreply at scummvm.org  Thu Jun 18 22:24:23 2026
From: noreply at scummvm.org (lotharsm)
Date: Thu, 18 Jun 2026 22:24:23 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm-web master ->
 708839c70da0ac1f1d7b64534687ee1ca147738b
Message-ID: <20260618222423.68BCB7005CC@ron.scummvm.net>

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

Summary:
708839c70d DATA: Update data files


Commit: 708839c70da0ac1f1d7b64534687ee1ca147738b
    https://github.com/scummvm/scummvm-web/commit/708839c70da0ac1f1d7b64534687ee1ca147738b
Author: sev- (sev at scummvm.org)
Date: 2026-06-18T22:24:18Z

Commit Message:
DATA: Update data files

Changed paths:
    data/en/compatibility.yaml
    data/en/game_demos.yaml


diff --git a/data/en/compatibility.yaml b/data/en/compatibility.yaml
index 26e0a38f..1c76aed5 100644
--- a/data/en/compatibility.yaml
+++ b/data/en/compatibility.yaml
@@ -5812,14 +5812,14 @@
 -
     id: 'gamos:pilots1'
     support: good
-    notes: '- Only Windows version is supported'
+    notes: '- Only Windows version is supported - GOG/Steam versions are not supported'
     since_version: DEV
     stable_platforms: win
     unstable_platforms: ''
 -
     id: 'gamos:pilots2'
     support: good
-    notes: '- Only Windows version is supported'
+    notes: '- Only Windows version is supported - GOG/Steam versions are not supported'
     since_version: DEV
     stable_platforms: win
     unstable_platforms: ''
diff --git a/data/en/game_demos.yaml b/data/en/game_demos.yaml
index 49047c01..9049eb7a 100644
--- a/data/en/game_demos.yaml
+++ b/data/en/game_demos.yaml
@@ -1025,6 +1025,87 @@
     language2: fr
     language3: de
     url: /frs/demos/freescape/castle-amiga-demo.zip
+-
+    id: 'gamos:flop'
+    platform: win
+    '': ''
+    category: v1.43
+    language: en
+    language2: ''
+    language3: ''
+    url: /frs/demos/gamos/flop-win-demo-en-16.zip
+-
+    id: 'gamos:flop'
+    platform: win
+    '': ''
+    category: v1.51
+    language: en
+    language2: ''
+    language3: ''
+    url: /frs/demos/gamos/flop-win-demo-en-18.zip
+-
+    id: 'gamos:flop'
+    platform: win
+    '': ''
+    category: ''
+    language: ru
+    language2: ''
+    language3: ''
+    url: /frs/demos/gamos/flop-win-demo-ru.zip
+-
+    id: 'gamos:netwalk'
+    platform: win
+    '': ''
+    category: ''
+    language: en
+    language2: ''
+    language3: ''
+    url: /frs/demos/gamos/netwalk-win-demo-en.zip
+-
+    id: 'gamos:pilots1'
+    platform: win
+    '': ''
+    category: v1.47
+    language: en
+    language2: ''
+    language3: ''
+    url: /frs/demos/gamos/pilots1-win-demo-en-16.zip
+-
+    id: 'gamos:pilots1'
+    platform: win
+    '': ''
+    category: v1.50
+    language: en
+    language2: ''
+    language3: ''
+    url: /frs/demos/gamos/pilots1-win-demo-en-18.zip
+-
+    id: 'gamos:pilots1'
+    platform: win
+    '': ''
+    category: ''
+    language: ru
+    language2: ''
+    language3: ''
+    url: /frs/demos/gamos/pilots1-win-demo-ru.zip
+-
+    id: 'gamos:wild'
+    platform: win
+    '': ''
+    category: ''
+    language: en
+    language2: ''
+    language3: ''
+    url: /frs/demos/gamos/wild-win-demo-en.zip
+-
+    id: 'gamos:wild'
+    platform: win
+    '': ''
+    category: ''
+    language: ru
+    language2: ''
+    language3: ''
+    url: /frs/demos/gamos/wild-win-demo-ru.zip
 -
     id: 'gob:adi2'
     platform: win



From noreply at scummvm.org  Thu Jun 18 22:27:26 2026
From: noreply at scummvm.org (lotharsm)
Date: Thu, 18 Jun 2026 22:27:26 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm-web master ->
 5d42440d6ce68e70b664049cbda20d31fdbee6c3
Message-ID: <20260618222726.AAF857005CC@ron.scummvm.net>

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

Summary:
5d42440d6c DATA: Update data files


Commit: 5d42440d6ce68e70b664049cbda20d31fdbee6c3
    https://github.com/scummvm/scummvm-web/commit/5d42440d6ce68e70b664049cbda20d31fdbee6c3
Author: sev- (sev at scummvm.org)
Date: 2026-06-18T22:27:23Z

Commit Message:
DATA: Update data files

Changed paths:
    data/en/game_demos.yaml


diff --git a/data/en/game_demos.yaml b/data/en/game_demos.yaml
index 9049eb7a..02af6f83 100644
--- a/data/en/game_demos.yaml
+++ b/data/en/game_demos.yaml
@@ -1029,7 +1029,7 @@
     id: 'gamos:flop'
     platform: win
     '': ''
-    category: v1.43
+    category: 'English v1.43'
     language: en
     language2: ''
     language3: ''
@@ -1038,7 +1038,7 @@
     id: 'gamos:flop'
     platform: win
     '': ''
-    category: v1.51
+    category: 'English v1.51'
     language: en
     language2: ''
     language3: ''
@@ -1047,7 +1047,7 @@
     id: 'gamos:flop'
     platform: win
     '': ''
-    category: ''
+    category: Russian
     language: ru
     language2: ''
     language3: ''
@@ -1056,7 +1056,7 @@
     id: 'gamos:netwalk'
     platform: win
     '': ''
-    category: ''
+    category: English
     language: en
     language2: ''
     language3: ''
@@ -1065,7 +1065,7 @@
     id: 'gamos:pilots1'
     platform: win
     '': ''
-    category: v1.47
+    category: 'English v1.47'
     language: en
     language2: ''
     language3: ''
@@ -1074,7 +1074,7 @@
     id: 'gamos:pilots1'
     platform: win
     '': ''
-    category: v1.50
+    category: 'English v1.50'
     language: en
     language2: ''
     language3: ''
@@ -1083,7 +1083,7 @@
     id: 'gamos:pilots1'
     platform: win
     '': ''
-    category: ''
+    category: Russian
     language: ru
     language2: ''
     language3: ''
@@ -1092,7 +1092,7 @@
     id: 'gamos:wild'
     platform: win
     '': ''
-    category: ''
+    category: English
     language: en
     language2: ''
     language3: ''
@@ -1101,7 +1101,7 @@
     id: 'gamos:wild'
     platform: win
     '': ''
-    category: ''
+    category: Russian
     language: ru
     language2: ''
     language3: ''



From noreply at scummvm.org  Thu Jun 18 23:15:44 2026
From: noreply at scummvm.org (bluegr)
Date: Thu, 18 Jun 2026 23:15:44 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 682c3628a839caedea64f0bf027ecc46195b5fd9
Message-ID: <20260618231544.D4FF17005CC@ron.scummvm.net>

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

Summary:
682c3628a8 CONFIGURE: Disable SEQ MIDI on FreeBSD and DragonFlyBSD


Commit: 682c3628a839caedea64f0bf027ecc46195b5fd9
    https://github.com/scummvm/scummvm/commit/682c3628a839caedea64f0bf027ecc46195b5fd9
Author: tunnelsociety (tunnelsociety at mm.st)
Date: 2026-06-19T02:15:40+03:00

Commit Message:
CONFIGURE: Disable SEQ MIDI on FreeBSD and DragonFlyBSD

Neither can build as they do not have `SNDCTL_SYNTH_ID`. (FreeBSD's port
unconditionally disables SEQ MIDI.)
FreeBSD dropped its sequencer in 2025, https://reviews.freebsd.org/D50605

Changed paths:
    configure


diff --git a/configure b/configure
index 498adf8ad26..cdbe5b166dd 100755
--- a/configure
+++ b/configure
@@ -3432,9 +3432,10 @@ EOF
 			_zlib=no
 		fi
 		;;
-	freebsd* | dragonfly*)
+	freebsd* | dragonfly* | openbsd*)
 		append_var LDFLAGS "-L/usr/local/lib"
 		append_var CXXFLAGS "-I/usr/local/include"
+		_seq_midi=no
 		;;
 	gamecube)
 		_optimization_level=-Os
@@ -3522,11 +3523,6 @@ EOF
 		_nuked_opl=no
 		_sid_audio=no
 		;;
-	openbsd*)
-		append_var LDFLAGS "-L/usr/local/lib"
-		append_var CXXFLAGS "-I/usr/local/include"
-		_seq_midi=no
-		;;
 	ps3)
 		# Force use of SDL and freetype from the ps3 toolchain
 		_sdlpath="$PS3DEV/portlibs/ppu:$PS3DEV/portlibs/ppu/bin"



From noreply at scummvm.org  Fri Jun 19 05:50:43 2026
From: noreply at scummvm.org (dreammaster)
Date: Fri, 19 Jun 2026 05:50:43 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 7bdb352b9e151ac8ca97ac63c5a2646416372833
Message-ID: <20260619055043.8389F7005BC@ron.scummvm.net>

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

Summary:
7bdb352b9e MADS: DRAGONSPHERE: Fix main menu animation


Commit: 7bdb352b9e151ac8ca97ac63c5a2646416372833
    https://github.com/scummvm/scummvm/commit/7bdb352b9e151ac8ca97ac63c5a2646416372833
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-06-19T15:50:37+10:00

Commit Message:
MADS: DRAGONSPHERE: Fix main menu animation

Changed paths:
    engines/mads/madsv2/dragonsphere/main_menu.cpp


diff --git a/engines/mads/madsv2/dragonsphere/main_menu.cpp b/engines/mads/madsv2/dragonsphere/main_menu.cpp
index 7ece6326494..32c93df6fc5 100644
--- a/engines/mads/madsv2/dragonsphere/main_menu.cpp
+++ b/engines/mads/madsv2/dragonsphere/main_menu.cpp
@@ -312,7 +312,7 @@ void menu_control() {
 	start_series();
 	start_hotspots();
 
-	anim = kernel_run_animation("*RM902A.AA", 0);
+	anim = kernel_run_animation("*RM920A.AA", 0);
 
 	mouse_init_cycle();
 



From noreply at scummvm.org  Fri Jun 19 10:12:44 2026
From: noreply at scummvm.org (ScummVM git)
Date: Fri, 19 Jun 2026 10:12:44 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm-web
 dependabot/composer/guzzlehttp/guzzle-7.12.1 ->
 299e74c00d1ede28cfc3b99fe089dc31f9b71e63
Message-ID: <20260619101244.BC4F07005CD@ron.scummvm.net>

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

Summary:
299e74c00d Bump guzzlehttp/guzzle from 7.11.1 to 7.12.1


Commit: 299e74c00d1ede28cfc3b99fe089dc31f9b71e63
    https://github.com/scummvm/scummvm-web/commit/299e74c00d1ede28cfc3b99fe089dc31f9b71e63
Author: dependabot[bot] (49699333+dependabot[bot]@users.noreply.github.com)
Date: 2026-06-19T10:12:40Z

Commit Message:
Bump guzzlehttp/guzzle from 7.11.1 to 7.12.1

Bumps [guzzlehttp/guzzle](https://github.com/guzzle/guzzle) from 7.11.1 to 7.12.1.
- [Release notes](https://github.com/guzzle/guzzle/releases)
- [Changelog](https://github.com/guzzle/guzzle/blob/7.12/CHANGELOG.md)
- [Commits](https://github.com/guzzle/guzzle/compare/7.11.1...7.12.1)

---
updated-dependencies:
- dependency-name: guzzlehttp/guzzle
  dependency-version: 7.12.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support at github.com>

Changed paths:
    composer.lock


diff --git a/composer.lock b/composer.lock
index 5aaa8496..f6f8bd30 100644
--- a/composer.lock
+++ b/composer.lock
@@ -183,22 +183,22 @@
         },
         {
             "name": "guzzlehttp/guzzle",
-            "version": "7.11.1",
+            "version": "7.12.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/guzzle/guzzle.git",
-                "reference": "5af96f374e0ab4ebd747b8310888c99d3adb0a8c"
+                "reference": "d34627490fbc03bf5c5d7cfed81f2faa19519425"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/guzzle/guzzle/zipball/5af96f374e0ab4ebd747b8310888c99d3adb0a8c",
-                "reference": "5af96f374e0ab4ebd747b8310888c99d3adb0a8c",
+                "url": "https://api.github.com/repos/guzzle/guzzle/zipball/d34627490fbc03bf5c5d7cfed81f2faa19519425",
+                "reference": "d34627490fbc03bf5c5d7cfed81f2faa19519425",
                 "shasum": ""
             },
             "require": {
                 "ext-json": "*",
                 "guzzlehttp/promises": "^2.5",
-                "guzzlehttp/psr7": "^2.11",
+                "guzzlehttp/psr7": "^2.12.1",
                 "php": "^7.2.5 || ^8.0",
                 "psr/http-client": "^1.0",
                 "symfony/deprecation-contracts": "^2.5 || ^3.0",
@@ -211,7 +211,7 @@
                 "bamarni/composer-bin-plugin": "^1.8.2",
                 "ext-curl": "*",
                 "guzzle/client-integration-tests": "3.0.2",
-                "guzzlehttp/test-server": "^0.5",
+                "guzzlehttp/test-server": "^0.5.1",
                 "php-http/message-factory": "^1.1",
                 "phpunit/phpunit": "^8.5.52 || ^9.6.34",
                 "psr/log": "^1.1 || ^2.0 || ^3.0"
@@ -291,7 +291,7 @@
             ],
             "support": {
                 "issues": "https://github.com/guzzle/guzzle/issues",
-                "source": "https://github.com/guzzle/guzzle/tree/7.11.1"
+                "source": "https://github.com/guzzle/guzzle/tree/7.12.1"
             },
             "funding": [
                 {
@@ -307,7 +307,7 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2026-06-07T22:54:06+00:00"
+            "time": "2026-06-18T14:12:49+00:00"
         },
         {
             "name": "guzzlehttp/promises",
@@ -395,16 +395,16 @@
         },
         {
             "name": "guzzlehttp/psr7",
-            "version": "2.11.0",
+            "version": "2.12.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/guzzle/psr7.git",
-                "reference": "bbb5e61349fa5cb822b3e87842b951088b76b81f"
+                "reference": "172ef2f4e9824c1e058b7f30be8ae25a02c0f2b7"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/guzzle/psr7/zipball/bbb5e61349fa5cb822b3e87842b951088b76b81f",
-                "reference": "bbb5e61349fa5cb822b3e87842b951088b76b81f",
+                "url": "https://api.github.com/repos/guzzle/psr7/zipball/172ef2f4e9824c1e058b7f30be8ae25a02c0f2b7",
+                "reference": "172ef2f4e9824c1e058b7f30be8ae25a02c0f2b7",
                 "shasum": ""
             },
             "require": {
@@ -494,7 +494,7 @@
             ],
             "support": {
                 "issues": "https://github.com/guzzle/psr7/issues",
-                "source": "https://github.com/guzzle/psr7/tree/2.11.0"
+                "source": "https://github.com/guzzle/psr7/tree/2.12.1"
             },
             "funding": [
                 {
@@ -510,7 +510,7 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2026-06-02T12:30:48+00:00"
+            "time": "2026-06-18T09:49:37+00:00"
         },
         {
             "name": "league/csv",



From noreply at scummvm.org  Fri Jun 19 10:13:40 2026
From: noreply at scummvm.org (ScummVM git)
Date: Fri, 19 Jun 2026 10:13:40 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm-web
 dependabot/npm_and_yarn/dumper-companion/eslint-7b65bbab32 ->
 ef6a463f32fb1029e3ccf01c50dbcc72de34b21d
Message-ID: <20260619101340.16B337005CD@ron.scummvm.net>

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

Summary:
ef6a463f32 Bump the eslint group across 1 directory with 3 updates


Commit: ef6a463f32fb1029e3ccf01c50dbcc72de34b21d
    https://github.com/scummvm/scummvm-web/commit/ef6a463f32fb1029e3ccf01c50dbcc72de34b21d
Author: dependabot[bot] (49699333+dependabot[bot]@users.noreply.github.com)
Date: 2026-06-19T10:13:36Z

Commit Message:
Bump the eslint group across 1 directory with 3 updates

Bumps the eslint group with 3 updates in the /dumper-companion directory: [@eslint/js](https://github.com/eslint/eslint/tree/HEAD/packages/js), [eslint](https://github.com/eslint/eslint) and [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint).


Updates `@eslint/js` from 9.39.4 to 10.0.1
- [Release notes](https://github.com/eslint/eslint/releases)
- [Commits](https://github.com/eslint/eslint/commits/v10.0.1/packages/js)

Updates `eslint` from 9.39.4 to 10.5.0
- [Release notes](https://github.com/eslint/eslint/releases)
- [Commits](https://github.com/eslint/eslint/compare/v9.39.4...v10.5.0)

Updates `typescript-eslint` from 8.60.1 to 8.61.1
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/typescript-eslint/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.61.1/packages/typescript-eslint)

---
updated-dependencies:
- dependency-name: "@eslint/js"
  dependency-version: 10.0.1
  dependency-type: direct:development
  update-type: version-update:semver-major
  dependency-group: eslint
- dependency-name: eslint
  dependency-version: 10.5.0
  dependency-type: direct:development
  update-type: version-update:semver-major
  dependency-group: eslint
- dependency-name: typescript-eslint
  dependency-version: 8.61.1
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: eslint
...

Signed-off-by: dependabot[bot] <support at github.com>

Changed paths:
    dumper-companion/package-lock.json
    dumper-companion/package.json


diff --git a/dumper-companion/package-lock.json b/dumper-companion/package-lock.json
index e7324deb..7f6d192e 100644
--- a/dumper-companion/package-lock.json
+++ b/dumper-companion/package-lock.json
@@ -17,12 +17,12 @@
         "webpack-cli": "^7.0.2"
       },
       "devDependencies": {
-        "@eslint/js": "^9.39.4",
+        "@eslint/js": "^10.0.1",
         "@stylistic/eslint-plugin": "^5.10.0",
-        "eslint": "^9.39.4",
+        "eslint": "^10.5.0",
         "eslint-config-preact": "^2.0.0",
         "globals": "^17.5.0",
-        "typescript-eslint": "^8.58.2"
+        "typescript-eslint": "^8.61.1"
       }
     },
     "node_modules/@babel/code-frame": {
@@ -81,35 +81,6 @@
         "url": "https://opencollective.com/babel"
       }
     },
-    "node_modules/@babel/eslint-parser": {
-      "version": "7.29.7",
-      "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.29.7.tgz",
-      "integrity": "sha512-zxt+UJTOMKvUt3yOg+D58MLuz334pHp93qifMFcjIIO+9hN6t+ufw2gi7vDPMpxvfnHRR+3VVXvIjineCcgyXw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@nicolo-ribaudo/eslint-scope-5-internals": "5.1.1-v1",
-        "eslint-visitor-keys": "^2.1.0",
-        "semver": "^6.3.1"
-      },
-      "engines": {
-        "node": "^10.13.0 || ^12.13.0 || >=14.0.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.11.0",
-        "eslint": "^7.5.0 || ^8.0.0 || ^9.0.0"
-      }
-    },
-    "node_modules/@babel/eslint-parser/node_modules/eslint-visitor-keys": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz",
-      "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==",
-      "dev": true,
-      "license": "Apache-2.0",
-      "engines": {
-        "node": ">=10"
-      }
-    },
     "node_modules/@babel/generator": {
       "version": "7.29.7",
       "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.7.tgz",
@@ -385,118 +356,89 @@
       }
     },
     "node_modules/@eslint/config-array": {
-      "version": "0.21.2",
-      "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.2.tgz",
-      "integrity": "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==",
+      "version": "0.23.5",
+      "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.23.5.tgz",
+      "integrity": "sha512-Y3kKLvC1dvTOT+oGlqNQ1XLqK6D1HU2YXPc52NmAlJZbMMWDzGYXMiPRJ8TYD39muD/OTjlZmNJ4ib7dvSrMBA==",
       "dev": true,
       "license": "Apache-2.0",
       "dependencies": {
-        "@eslint/object-schema": "^2.1.7",
+        "@eslint/object-schema": "^3.0.5",
         "debug": "^4.3.1",
-        "minimatch": "^3.1.5"
+        "minimatch": "^10.2.4"
       },
       "engines": {
-        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+        "node": "^20.19.0 || ^22.13.0 || >=24"
       }
     },
     "node_modules/@eslint/config-helpers": {
-      "version": "0.4.2",
-      "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz",
-      "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==",
+      "version": "0.6.0",
+      "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.6.0.tgz",
+      "integrity": "sha512-ii6Bw9jJ2zi2cWA2Z+9/QZ/+3DX6kwaV5Q986D/CdP3Lap3w/pgQZ373FV7byY/i7L4IRH/G43I5dz1ClsCbpA==",
       "dev": true,
       "license": "Apache-2.0",
       "dependencies": {
-        "@eslint/core": "^0.17.0"
+        "@eslint/core": "^1.2.1"
       },
       "engines": {
-        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+        "node": "^20.19.0 || ^22.13.0 || >=24"
       }
     },
     "node_modules/@eslint/core": {
-      "version": "0.17.0",
-      "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz",
-      "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==",
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/@eslint/core/-/core-1.2.1.tgz",
+      "integrity": "sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ==",
       "dev": true,
       "license": "Apache-2.0",
       "dependencies": {
         "@types/json-schema": "^7.0.15"
       },
       "engines": {
-        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
-      }
-    },
-    "node_modules/@eslint/eslintrc": {
-      "version": "3.3.5",
-      "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.5.tgz",
-      "integrity": "sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "ajv": "^6.14.0",
-        "debug": "^4.3.2",
-        "espree": "^10.0.1",
-        "globals": "^14.0.0",
-        "ignore": "^5.2.0",
-        "import-fresh": "^3.2.1",
-        "js-yaml": "^4.1.1",
-        "minimatch": "^3.1.5",
-        "strip-json-comments": "^3.1.1"
-      },
-      "engines": {
-        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
-      },
-      "funding": {
-        "url": "https://opencollective.com/eslint"
-      }
-    },
-    "node_modules/@eslint/eslintrc/node_modules/globals": {
-      "version": "14.0.0",
-      "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz",
-      "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=18"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
+        "node": "^20.19.0 || ^22.13.0 || >=24"
       }
     },
     "node_modules/@eslint/js": {
-      "version": "9.39.4",
-      "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.4.tgz",
-      "integrity": "sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==",
+      "version": "10.0.1",
+      "resolved": "https://registry.npmjs.org/@eslint/js/-/js-10.0.1.tgz",
+      "integrity": "sha512-zeR9k5pd4gxjZ0abRoIaxdc7I3nDktoXZk2qOv9gCNWx3mVwEn32VRhyLaRsDiJjTs0xq/T8mfPtyuXu7GWBcA==",
       "dev": true,
       "license": "MIT",
       "engines": {
-        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+        "node": "^20.19.0 || ^22.13.0 || >=24"
       },
       "funding": {
         "url": "https://eslint.org/donate"
+      },
+      "peerDependencies": {
+        "eslint": "^10.0.0"
+      },
+      "peerDependenciesMeta": {
+        "eslint": {
+          "optional": true
+        }
       }
     },
     "node_modules/@eslint/object-schema": {
-      "version": "2.1.7",
-      "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz",
-      "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==",
+      "version": "3.0.5",
+      "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-3.0.5.tgz",
+      "integrity": "sha512-vqTaUEgxzm+YDSdElad6PiRoX4t8VGDjCtt05zn4nU810UIx/uNEV7/lZJ6KwFThKZOzOxzXy48da+No7HZaMw==",
       "dev": true,
       "license": "Apache-2.0",
       "engines": {
-        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+        "node": "^20.19.0 || ^22.13.0 || >=24"
       }
     },
     "node_modules/@eslint/plugin-kit": {
-      "version": "0.4.1",
-      "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz",
-      "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==",
+      "version": "0.7.2",
+      "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.7.2.tgz",
+      "integrity": "sha512-+CNAzxglkrpNf/kKywqQfk74QjtceuOE7Qm+AF8miRvPF/wmmK5+OJOgVh3AVTT3RP2mH3+FOaxlE5v72owk0A==",
       "dev": true,
       "license": "Apache-2.0",
       "dependencies": {
-        "@eslint/core": "^0.17.0",
+        "@eslint/core": "^1.2.1",
         "levn": "^0.4.1"
       },
       "engines": {
-        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+        "node": "^20.19.0 || ^22.13.0 || >=24"
       }
     },
     "node_modules/@humanfs/core": {
@@ -683,6 +625,13 @@
         "eslint": "^9.0.0 || ^10.0.0"
       }
     },
+    "node_modules/@types/esrecurse": {
+      "version": "4.3.1",
+      "resolved": "https://registry.npmjs.org/@types/esrecurse/-/esrecurse-4.3.1.tgz",
+      "integrity": "sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==",
+      "dev": true,
+      "license": "MIT"
+    },
     "node_modules/@types/estree": {
       "version": "1.0.9",
       "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz",
@@ -711,17 +660,17 @@
       "license": "MIT"
     },
     "node_modules/@typescript-eslint/eslint-plugin": {
-      "version": "8.60.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.60.1.tgz",
-      "integrity": "sha512-JQ4S5GB0tfjO8BuJ4fcX+HodkzJjYBV+7OJ+wLygaX7OGQ7FudyHL4NSCA6ob+w3Yn+5MkKIozOwQhXeM7opVg==",
+      "version": "8.61.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.61.1.tgz",
+      "integrity": "sha512-ZPlVl3PB3et/59Ne0fv/sci6ZXz4T4Hp4nTJ56i/Y0gR89ARb+KphojTq6j+56E5PIezmOIOOWyY+aWQFd+IkQ==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
         "@eslint-community/regexpp": "^4.12.2",
-        "@typescript-eslint/scope-manager": "8.60.1",
-        "@typescript-eslint/type-utils": "8.60.1",
-        "@typescript-eslint/utils": "8.60.1",
-        "@typescript-eslint/visitor-keys": "8.60.1",
+        "@typescript-eslint/scope-manager": "8.61.1",
+        "@typescript-eslint/type-utils": "8.61.1",
+        "@typescript-eslint/utils": "8.61.1",
+        "@typescript-eslint/visitor-keys": "8.61.1",
         "ignore": "^7.0.5",
         "natural-compare": "^1.4.0",
         "ts-api-utils": "^2.5.0"
@@ -734,7 +683,7 @@
         "url": "https://opencollective.com/typescript-eslint"
       },
       "peerDependencies": {
-        "@typescript-eslint/parser": "^8.60.1",
+        "@typescript-eslint/parser": "^8.61.1",
         "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
         "typescript": ">=4.8.4 <6.1.0"
       }
@@ -750,16 +699,16 @@
       }
     },
     "node_modules/@typescript-eslint/parser": {
-      "version": "8.60.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.60.1.tgz",
-      "integrity": "sha512-A0M6ua6H252bVjPvvtSgl2QA4+ET9S5Mtkb2GDyTxIhH/C4qDItT7RQNO5PhMC6NXGYXOR9dIalcDDgBKT7oFA==",
+      "version": "8.61.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.61.1.tgz",
+      "integrity": "sha512-PJ5vePq5/ognBbrIcoC5+SHO5dfpeLPzP9FpLkzWrguoYQEeeSjlJpVwOpo1JRSTEi7dRcwNy4h4dzV70PqHcg==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@typescript-eslint/scope-manager": "8.60.1",
-        "@typescript-eslint/types": "8.60.1",
-        "@typescript-eslint/typescript-estree": "8.60.1",
-        "@typescript-eslint/visitor-keys": "8.60.1",
+        "@typescript-eslint/scope-manager": "8.61.1",
+        "@typescript-eslint/types": "8.61.1",
+        "@typescript-eslint/typescript-estree": "8.61.1",
+        "@typescript-eslint/visitor-keys": "8.61.1",
         "debug": "^4.4.3"
       },
       "engines": {
@@ -775,14 +724,14 @@
       }
     },
     "node_modules/@typescript-eslint/project-service": {
-      "version": "8.60.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.60.1.tgz",
-      "integrity": "sha512-eXkTH2bxmXlqD1RnOPmLZ9ZM9D3VwSx04JOwBnP9RQ+yUA5a2Mu7SfW8uaV2Aon53NJzZlZYuX7tn91Izf+xaw==",
+      "version": "8.61.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.61.1.tgz",
+      "integrity": "sha512-PrC4JYGmR241lYnfhmKGTXkFqv8+ymbTFgSAY0fVXpY82/QkMw5TZPl+vGzuDDU2QYJk9fIDOBTntF+yDv9LEA==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@typescript-eslint/tsconfig-utils": "^8.60.1",
-        "@typescript-eslint/types": "^8.60.1",
+        "@typescript-eslint/tsconfig-utils": "^8.61.1",
+        "@typescript-eslint/types": "^8.61.1",
         "debug": "^4.4.3"
       },
       "engines": {
@@ -797,14 +746,14 @@
       }
     },
     "node_modules/@typescript-eslint/scope-manager": {
-      "version": "8.60.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.60.1.tgz",
-      "integrity": "sha512-gvI5OQoptnxQnchOirukCuQ55svJSTuD/4k5+pC267xyBtYry748R9/c3tYUzb/iE6RZfllRz2lVulLCHkTm4w==",
+      "version": "8.61.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.61.1.tgz",
+      "integrity": "sha512-L2bdIeoQS8FlKAvONAr20w6OcLXeB+qiDKbAooS9A0Ben+iSIkBef0FxqwKWYqt5sa0i4KJtxVyVmhMylKzF5w==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@typescript-eslint/types": "8.60.1",
-        "@typescript-eslint/visitor-keys": "8.60.1"
+        "@typescript-eslint/types": "8.61.1",
+        "@typescript-eslint/visitor-keys": "8.61.1"
       },
       "engines": {
         "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -815,9 +764,9 @@
       }
     },
     "node_modules/@typescript-eslint/tsconfig-utils": {
-      "version": "8.60.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.60.1.tgz",
-      "integrity": "sha512-nh8w4qAteiKuZu3pSSzG/yGKpw0OlkrKnzFmbVRenKaD4qc+7i1GrmZaLVkr8rk4uipiPGMOW4YsM6WmKZ5CvA==",
+      "version": "8.61.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.61.1.tgz",
+      "integrity": "sha512-UN/H4di+OO7EWx2ovME+8t31YO+KVnK0RRKEHR3kOt21/Ay8BOq3M1OMvWs5vNiqcFCYGYoxK3MXPZzmMUE+yg==",
       "dev": true,
       "license": "MIT",
       "engines": {
@@ -832,15 +781,15 @@
       }
     },
     "node_modules/@typescript-eslint/type-utils": {
-      "version": "8.60.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.60.1.tgz",
-      "integrity": "sha512-sdwTrpjosW7ANQYJ39ZBF1ZyEMEGVB2UsikrserVM/30a/F1dTLnu9bGxEdosugyu5caigjLrR2qiD11asjI1A==",
+      "version": "8.61.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.61.1.tgz",
+      "integrity": "sha512-GYRicKmVK0C4fsKgaACaknOUAq9Oa2kwsjnpFhFcS/5p4Ht5IP9OVLbgIgcK4SRk92nVHFluurg1lumD9dBcLw==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@typescript-eslint/types": "8.60.1",
-        "@typescript-eslint/typescript-estree": "8.60.1",
-        "@typescript-eslint/utils": "8.60.1",
+        "@typescript-eslint/types": "8.61.1",
+        "@typescript-eslint/typescript-estree": "8.61.1",
+        "@typescript-eslint/utils": "8.61.1",
         "debug": "^4.4.3",
         "ts-api-utils": "^2.5.0"
       },
@@ -857,9 +806,9 @@
       }
     },
     "node_modules/@typescript-eslint/types": {
-      "version": "8.60.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.60.1.tgz",
-      "integrity": "sha512-4h0tY8ppCkdCzcrl2YM5M3my0xsE1Tf8om3owEu5oPWmXwkKRmk0j0LGDzYBGUcAlesEbxBhazqu/K4cu3Ug7w==",
+      "version": "8.61.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.61.1.tgz",
+      "integrity": "sha512-G+CRlPqLv7Bz1IZVs03x5K59F1veqL0EJUROAdGhKsEq8qOiRiZbI+HUojPq5l0fEGOKModD9br6lObhB8zkoA==",
       "dev": true,
       "license": "MIT",
       "engines": {
@@ -871,16 +820,16 @@
       }
     },
     "node_modules/@typescript-eslint/typescript-estree": {
-      "version": "8.60.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.60.1.tgz",
-      "integrity": "sha512-alpRkfG8hlVE5kdJW2GkfgDgXxold3e8e4l6EnmhRmRLbekgAPCCGDVD++sABy9FcgPFroq+uFcCSM1vR57Cew==",
+      "version": "8.61.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.61.1.tgz",
+      "integrity": "sha512-u+oQD3BqYWPc8YV9Zab4vaJElJuwOLPRc10Jm1o/qS+6Qwen14HCWwx0Seo4LnSn2wxea2Ik8DxPt2/FHmuhrg==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@typescript-eslint/project-service": "8.60.1",
-        "@typescript-eslint/tsconfig-utils": "8.60.1",
-        "@typescript-eslint/types": "8.60.1",
-        "@typescript-eslint/visitor-keys": "8.60.1",
+        "@typescript-eslint/project-service": "8.61.1",
+        "@typescript-eslint/tsconfig-utils": "8.61.1",
+        "@typescript-eslint/types": "8.61.1",
+        "@typescript-eslint/visitor-keys": "8.61.1",
         "debug": "^4.4.3",
         "minimatch": "^10.2.2",
         "semver": "^7.7.3",
@@ -898,49 +847,10 @@
         "typescript": ">=4.8.4 <6.1.0"
       }
     },
-    "node_modules/@typescript-eslint/typescript-estree/node_modules/balanced-match": {
-      "version": "4.0.4",
-      "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
-      "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": "18 || 20 || >=22"
-      }
-    },
-    "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": {
-      "version": "5.0.6",
-      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz",
-      "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "balanced-match": "^4.0.2"
-      },
-      "engines": {
-        "node": "18 || 20 || >=22"
-      }
-    },
-    "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": {
-      "version": "10.2.5",
-      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz",
-      "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==",
-      "dev": true,
-      "license": "BlueOak-1.0.0",
-      "dependencies": {
-        "brace-expansion": "^5.0.5"
-      },
-      "engines": {
-        "node": "18 || 20 || >=22"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/isaacs"
-      }
-    },
     "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": {
-      "version": "7.8.2",
-      "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.2.tgz",
-      "integrity": "sha512-c8jsqUZm3omBOI66G90z1Dyw5z622G8oLG+omfsHBJf3CWQTlOcwOjvOG6wtiNfW6anKm/eA39LMwMtMez2TiQ==",
+      "version": "7.8.4",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.4.tgz",
+      "integrity": "sha512-rUCObTnP32Q08R2uuIrt7r9PlEonuTmtuXYcW6s5kjdlj3xbnwe+21yXptAUYcMAABLkYYTtnmzb3w3EDZfueA==",
       "dev": true,
       "license": "ISC",
       "bin": {
@@ -951,16 +861,16 @@
       }
     },
     "node_modules/@typescript-eslint/utils": {
-      "version": "8.60.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.60.1.tgz",
-      "integrity": "sha512-h2MPBLoNtjc3qZWfY3Tl51yPorQ2McHn8pJfcMNTcIvrrZrr90Ykffit0yjrPFWQcRcUxzH20+6OcVdW4yHtUg==",
+      "version": "8.61.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.61.1.tgz",
+      "integrity": "sha512-1+P/3Dj6jvtybE1q0HQ6yBt/gq+oKJyLdEv4HdnqasaEXRSYCAsD59mXEVQnM/ULNdQxbX77tdG4jPRjIS6knA==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
         "@eslint-community/eslint-utils": "^4.9.1",
-        "@typescript-eslint/scope-manager": "8.60.1",
-        "@typescript-eslint/types": "8.60.1",
-        "@typescript-eslint/typescript-estree": "8.60.1"
+        "@typescript-eslint/scope-manager": "8.61.1",
+        "@typescript-eslint/types": "8.61.1",
+        "@typescript-eslint/typescript-estree": "8.61.1"
       },
       "engines": {
         "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -975,13 +885,13 @@
       }
     },
     "node_modules/@typescript-eslint/visitor-keys": {
-      "version": "8.60.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.60.1.tgz",
-      "integrity": "sha512-EbGRQg4FhrmwLodl+t3JNAnXHWVr9Vp+Zl1QBZVPY4ByfkzIT8cX3K6QWODHtkIZqqJVEWvhHSx3v5PDHsaQag==",
+      "version": "8.61.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.61.1.tgz",
+      "integrity": "sha512-6fJ9MHWtK14C1DSkiMlHUSOmrVebL7150xZJBlJiL62jjhIA4JmOq6flwBgDxIdBKKdoiZRel+dfPD5MLfny3w==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@typescript-eslint/types": "8.60.1",
+        "@typescript-eslint/types": "8.61.1",
         "eslint-visitor-keys": "^5.0.0"
       },
       "engines": {
@@ -1279,13 +1189,6 @@
         "url": "https://github.com/chalk/ansi-styles?sponsor=1"
       }
     },
-    "node_modules/argparse": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
-      "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
-      "dev": true,
-      "license": "Python-2.0"
-    },
     "node_modules/array-buffer-byte-length": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz",
@@ -1468,11 +1371,14 @@
       }
     },
     "node_modules/balanced-match": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
-      "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+      "version": "4.0.4",
+      "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
+      "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==",
       "dev": true,
-      "license": "MIT"
+      "license": "MIT",
+      "engines": {
+        "node": "18 || 20 || >=22"
+      }
     },
     "node_modules/baseline-browser-mapping": {
       "version": "2.10.34",
@@ -1487,14 +1393,16 @@
       }
     },
     "node_modules/brace-expansion": {
-      "version": "1.1.15",
-      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz",
-      "integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==",
+      "version": "5.0.6",
+      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz",
+      "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "balanced-match": "^1.0.0",
-        "concat-map": "0.0.1"
+        "balanced-match": "^4.0.2"
+      },
+      "engines": {
+        "node": "18 || 20 || >=22"
       }
     },
     "node_modules/braces": {
@@ -1598,16 +1506,6 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
-    "node_modules/callsites": {
-      "version": "3.1.0",
-      "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
-      "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=6"
-      }
-    },
     "node_modules/caniuse-lite": {
       "version": "1.0.30001797",
       "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001797.tgz",
@@ -1962,6 +1860,25 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
+    "node_modules/es-abstract-get": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/es-abstract-get/-/es-abstract-get-1.0.0.tgz",
+      "integrity": "sha512-6PMWXpdhshVvFp+FoWYs1EvG1Nj0tvk0dZM+XcK0xMEM1czRVcP6ohqPWHy6qPagSpC8j4+p89WXlT+xXJs/fg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "es-errors": "^1.3.0",
+        "es-object-atoms": "^1.1.2",
+        "is-callable": "^1.2.7",
+        "object-inspect": "^1.13.4"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
     "node_modules/es-define-property": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
@@ -1982,9 +1899,9 @@
       }
     },
     "node_modules/es-iterator-helpers": {
-      "version": "1.3.2",
-      "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.3.2.tgz",
-      "integrity": "sha512-HVLACW1TppGYjJ8H6/jqH/pqOtKRw6wMlrB23xfExmFWxFquAIWCmwoLsOyN96K4a5KbmOf5At9ZUO3GZbetAw==",
+      "version": "1.3.3",
+      "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.3.3.tgz",
+      "integrity": "sha512-0PuBxFi+4uPanB97iDxCLWuHeYud2FALrw5HFZGtAF38UpJDbDC8frwp2cnDyae692CQ0dou60UwWfhgsa4U/g==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
@@ -2058,15 +1975,17 @@
       }
     },
     "node_modules/es-to-primitive": {
-      "version": "1.3.0",
-      "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz",
-      "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==",
+      "version": "1.3.1",
+      "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.1.tgz",
+      "integrity": "sha512-CxN9N56HYfd2m/acc/NOFrZQsN9kU4eh+2kk6A707Kz1krH8tKmfrs5RnftB8WNX80T0NS7vSQsDOlg23diR2g==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
+        "es-abstract-get": "^1.0.0",
+        "es-errors": "^1.3.0",
         "is-callable": "^1.2.7",
-        "is-date-object": "^1.0.5",
-        "is-symbol": "^1.0.4"
+        "is-date-object": "^1.1.0",
+        "is-symbol": "^1.1.1"
       },
       "engines": {
         "node": ">= 0.4"
@@ -2098,33 +2017,33 @@
       }
     },
     "node_modules/eslint": {
-      "version": "9.39.4",
-      "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.4.tgz",
-      "integrity": "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==",
+      "version": "10.5.0",
+      "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.5.0.tgz",
+      "integrity": "sha512-1y+7C+vi12bUK1IpZeaV3gsH9fHLBmPvYmPx42pvT/E9yG0IC8g3PUZZgp0+JLJl7ZDK0flc2gc+Aw9dpCvIsQ==",
       "dev": true,
       "license": "MIT",
+      "workspaces": [
+        "packages/*"
+      ],
       "dependencies": {
         "@eslint-community/eslint-utils": "^4.8.0",
-        "@eslint-community/regexpp": "^4.12.1",
-        "@eslint/config-array": "^0.21.2",
-        "@eslint/config-helpers": "^0.4.2",
-        "@eslint/core": "^0.17.0",
-        "@eslint/eslintrc": "^3.3.5",
-        "@eslint/js": "9.39.4",
-        "@eslint/plugin-kit": "^0.4.1",
+        "@eslint-community/regexpp": "^4.12.2",
+        "@eslint/config-array": "^0.23.5",
+        "@eslint/config-helpers": "^0.6.0",
+        "@eslint/core": "^1.2.1",
+        "@eslint/plugin-kit": "^0.7.2",
         "@humanfs/node": "^0.16.6",
         "@humanwhocodes/module-importer": "^1.0.1",
         "@humanwhocodes/retry": "^0.4.2",
         "@types/estree": "^1.0.6",
         "ajv": "^6.14.0",
-        "chalk": "^4.0.0",
         "cross-spawn": "^7.0.6",
         "debug": "^4.3.2",
         "escape-string-regexp": "^4.0.0",
-        "eslint-scope": "^8.4.0",
-        "eslint-visitor-keys": "^4.2.1",
-        "espree": "^10.4.0",
-        "esquery": "^1.5.0",
+        "eslint-scope": "^9.1.2",
+        "eslint-visitor-keys": "^5.0.1",
+        "espree": "^11.2.0",
+        "esquery": "^1.7.0",
         "esutils": "^2.0.2",
         "fast-deep-equal": "^3.1.3",
         "file-entry-cache": "^8.0.0",
@@ -2134,8 +2053,7 @@
         "imurmurhash": "^0.1.4",
         "is-glob": "^4.0.0",
         "json-stable-stringify-without-jsonify": "^1.0.1",
-        "lodash.merge": "^4.6.2",
-        "minimatch": "^3.1.5",
+        "minimatch": "^10.2.4",
         "natural-compare": "^1.4.0",
         "optionator": "^0.9.3"
       },
@@ -2143,7 +2061,7 @@
         "eslint": "bin/eslint.js"
       },
       "engines": {
-        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+        "node": "^20.19.0 || ^22.13.0 || >=24"
       },
       "funding": {
         "url": "https://eslint.org/donate"
@@ -2178,68 +2096,57 @@
         "eslint": "^8.57.1 || ^9.0.0"
       }
     },
-    "node_modules/eslint-config-preact/node_modules/globals": {
-      "version": "16.5.0",
-      "resolved": "https://registry.npmjs.org/globals/-/globals-16.5.0.tgz",
-      "integrity": "sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=18"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/eslint-plugin-compat": {
-      "version": "6.2.1",
-      "resolved": "https://registry.npmjs.org/eslint-plugin-compat/-/eslint-plugin-compat-6.2.1.tgz",
-      "integrity": "sha512-gLKqUH+lQcCL+HzsROUjBDvakc5Zaga51Y4ZAkPCXc41pzKBfyluqTr2j8zOx8QQQb7zyglu1LVoL5aSNWf2SQ==",
+    "node_modules/eslint-config-preact/node_modules/@babel/eslint-parser": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.29.7.tgz",
+      "integrity": "sha512-zxt+UJTOMKvUt3yOg+D58MLuz334pHp93qifMFcjIIO+9hN6t+ufw2gi7vDPMpxvfnHRR+3VVXvIjineCcgyXw==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@mdn/browser-compat-data": "^6.1.1",
-        "ast-metadata-inferer": "^0.8.1",
-        "browserslist": "^4.25.2",
-        "find-up": "^5.0.0",
-        "globals": "^15.7.0",
-        "lodash.memoize": "^4.1.2",
-        "semver": "^7.6.2"
+        "@nicolo-ribaudo/eslint-scope-5-internals": "5.1.1-v1",
+        "eslint-visitor-keys": "^2.1.0",
+        "semver": "^6.3.1"
       },
       "engines": {
-        "node": ">=18.x"
+        "node": "^10.13.0 || ^12.13.0 || >=14.0.0"
       },
       "peerDependencies": {
-        "eslint": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0"
+        "@babel/core": "^7.11.0",
+        "eslint": "^7.5.0 || ^8.0.0 || ^9.0.0"
       }
     },
-    "node_modules/eslint-plugin-compat/node_modules/globals": {
-      "version": "15.15.0",
-      "resolved": "https://registry.npmjs.org/globals/-/globals-15.15.0.tgz",
-      "integrity": "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==",
+    "node_modules/eslint-config-preact/node_modules/@eslint/js": {
+      "version": "9.39.4",
+      "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.4.tgz",
+      "integrity": "sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==",
       "dev": true,
       "license": "MIT",
       "engines": {
-        "node": ">=18"
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
       },
       "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
+        "url": "https://eslint.org/donate"
       }
     },
-    "node_modules/eslint-plugin-compat/node_modules/semver": {
-      "version": "7.8.2",
-      "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.2.tgz",
-      "integrity": "sha512-c8jsqUZm3omBOI66G90z1Dyw5z622G8oLG+omfsHBJf3CWQTlOcwOjvOG6wtiNfW6anKm/eA39LMwMtMez2TiQ==",
+    "node_modules/eslint-config-preact/node_modules/balanced-match": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+      "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
       "dev": true,
-      "license": "ISC",
-      "bin": {
-        "semver": "bin/semver.js"
-      },
-      "engines": {
-        "node": ">=10"
+      "license": "MIT"
+    },
+    "node_modules/eslint-config-preact/node_modules/brace-expansion": {
+      "version": "1.1.15",
+      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz",
+      "integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "balanced-match": "^1.0.0",
+        "concat-map": "0.0.1"
       }
     },
-    "node_modules/eslint-plugin-react": {
+    "node_modules/eslint-config-preact/node_modules/eslint-plugin-react": {
       "version": "7.37.5",
       "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.5.tgz",
       "integrity": "sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==",
@@ -2272,7 +2179,7 @@
         "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7"
       }
     },
-    "node_modules/eslint-plugin-react-hooks": {
+    "node_modules/eslint-config-preact/node_modules/eslint-plugin-react-hooks": {
       "version": "5.2.0",
       "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.2.0.tgz",
       "integrity": "sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==",
@@ -2285,18 +2192,104 @@
         "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0"
       }
     },
+    "node_modules/eslint-config-preact/node_modules/eslint-visitor-keys": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz",
+      "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/eslint-config-preact/node_modules/globals": {
+      "version": "16.5.0",
+      "resolved": "https://registry.npmjs.org/globals/-/globals-16.5.0.tgz",
+      "integrity": "sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/eslint-config-preact/node_modules/minimatch": {
+      "version": "3.1.5",
+      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
+      "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "brace-expansion": "^1.1.7"
+      },
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/eslint-plugin-compat": {
+      "version": "6.2.1",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-compat/-/eslint-plugin-compat-6.2.1.tgz",
+      "integrity": "sha512-gLKqUH+lQcCL+HzsROUjBDvakc5Zaga51Y4ZAkPCXc41pzKBfyluqTr2j8zOx8QQQb7zyglu1LVoL5aSNWf2SQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@mdn/browser-compat-data": "^6.1.1",
+        "ast-metadata-inferer": "^0.8.1",
+        "browserslist": "^4.25.2",
+        "find-up": "^5.0.0",
+        "globals": "^15.7.0",
+        "lodash.memoize": "^4.1.2",
+        "semver": "^7.6.2"
+      },
+      "engines": {
+        "node": ">=18.x"
+      },
+      "peerDependencies": {
+        "eslint": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0"
+      }
+    },
+    "node_modules/eslint-plugin-compat/node_modules/globals": {
+      "version": "15.15.0",
+      "resolved": "https://registry.npmjs.org/globals/-/globals-15.15.0.tgz",
+      "integrity": "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/eslint-plugin-compat/node_modules/semver": {
+      "version": "7.8.2",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.2.tgz",
+      "integrity": "sha512-c8jsqUZm3omBOI66G90z1Dyw5z622G8oLG+omfsHBJf3CWQTlOcwOjvOG6wtiNfW6anKm/eA39LMwMtMez2TiQ==",
+      "dev": true,
+      "license": "ISC",
+      "bin": {
+        "semver": "bin/semver.js"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
     "node_modules/eslint-scope": {
-      "version": "8.4.0",
-      "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz",
-      "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==",
+      "version": "9.1.2",
+      "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-9.1.2.tgz",
+      "integrity": "sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==",
       "dev": true,
       "license": "BSD-2-Clause",
       "dependencies": {
+        "@types/esrecurse": "^4.3.1",
+        "@types/estree": "^1.0.8",
         "esrecurse": "^4.3.0",
         "estraverse": "^5.2.0"
       },
       "engines": {
-        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+        "node": "^20.19.0 || ^22.13.0 || >=24"
       },
       "funding": {
         "url": "https://opencollective.com/eslint"
@@ -2315,6 +2308,37 @@
         "url": "https://opencollective.com/eslint"
       }
     },
+    "node_modules/eslint/node_modules/eslint-visitor-keys": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz",
+      "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "engines": {
+        "node": "^20.19.0 || ^22.13.0 || >=24"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint"
+      }
+    },
+    "node_modules/eslint/node_modules/espree": {
+      "version": "11.2.0",
+      "resolved": "https://registry.npmjs.org/espree/-/espree-11.2.0.tgz",
+      "integrity": "sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "dependencies": {
+        "acorn": "^8.16.0",
+        "acorn-jsx": "^5.3.2",
+        "eslint-visitor-keys": "^5.0.1"
+      },
+      "engines": {
+        "node": "^20.19.0 || ^22.13.0 || >=24"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint"
+      }
+    },
     "node_modules/espree": {
       "version": "10.4.0",
       "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz",
@@ -2538,18 +2562,21 @@
       }
     },
     "node_modules/function.prototype.name": {
-      "version": "1.1.8",
-      "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz",
-      "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==",
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.2.0.tgz",
+      "integrity": "sha512-jObKIik1P2QjPHP5nz5BaOtUlfgS0fWo8IUByNXkM+o+02sJOi94em77GwJKQSJ3gfPHdgzLNrHc1uokV4P/ew==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "call-bind": "^1.0.8",
-        "call-bound": "^1.0.3",
-        "define-properties": "^1.2.1",
+        "call-bind": "^1.0.9",
+        "call-bound": "^1.0.4",
+        "es-define-property": "^1.0.1",
+        "es-errors": "^1.3.0",
         "functions-have-names": "^1.2.3",
-        "hasown": "^2.0.2",
-        "is-callable": "^1.2.7"
+        "has-property-descriptors": "^1.0.2",
+        "hasown": "^2.0.4",
+        "is-callable": "^1.2.7",
+        "is-document.all": "^1.0.0"
       },
       "engines": {
         "node": ">= 0.4"
@@ -2815,23 +2842,6 @@
         "node": ">= 4"
       }
     },
-    "node_modules/import-fresh": {
-      "version": "3.3.1",
-      "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
-      "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "parent-module": "^1.0.0",
-        "resolve-from": "^4.0.0"
-      },
-      "engines": {
-        "node": ">=6"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
     "node_modules/import-local": {
       "version": "3.2.0",
       "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz",
@@ -3019,6 +3029,22 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
+    "node_modules/is-document.all": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/is-document.all/-/is-document.all-1.0.0.tgz",
+      "integrity": "sha512-+XSoyS05OdBbhFuELhgTCpFNHkpBOJqtsZfUFFpe5QTw+9Sjbh8zitxhQkYAo6wV7e1Vb8cAPvpCk9jGam/82g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "call-bound": "^1.0.4"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
     "node_modules/is-extglob": {
       "version": "2.1.1",
       "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
@@ -3363,29 +3389,6 @@
       "dev": true,
       "license": "MIT"
     },
-    "node_modules/js-yaml": {
-      "version": "4.2.0",
-      "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.2.0.tgz",
-      "integrity": "sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==",
-      "dev": true,
-      "funding": [
-        {
-          "type": "github",
-          "url": "https://github.com/sponsors/puzrin"
-        },
-        {
-          "type": "github",
-          "url": "https://github.com/sponsors/nodeca"
-        }
-      ],
-      "license": "MIT",
-      "dependencies": {
-        "argparse": "^2.0.1"
-      },
-      "bin": {
-        "js-yaml": "bin/js-yaml.js"
-      }
-    },
     "node_modules/jsesc": {
       "version": "3.1.0",
       "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",
@@ -3518,13 +3521,6 @@
       "dev": true,
       "license": "MIT"
     },
-    "node_modules/lodash.merge": {
-      "version": "4.6.2",
-      "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
-      "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
-      "dev": true,
-      "license": "MIT"
-    },
     "node_modules/loose-envify": {
       "version": "1.4.0",
       "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
@@ -3599,16 +3595,19 @@
       }
     },
     "node_modules/minimatch": {
-      "version": "3.1.5",
-      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
-      "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
+      "version": "10.2.5",
+      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz",
+      "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==",
       "dev": true,
-      "license": "ISC",
+      "license": "BlueOak-1.0.0",
       "dependencies": {
-        "brace-expansion": "^1.1.7"
+        "brace-expansion": "^5.0.5"
       },
       "engines": {
-        "node": "*"
+        "node": "18 || 20 || >=22"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
       }
     },
     "node_modules/ms": {
@@ -3844,19 +3843,6 @@
         "node": ">=6"
       }
     },
-    "node_modules/parent-module": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
-      "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "callsites": "^3.0.0"
-      },
-      "engines": {
-        "node": ">=6"
-      }
-    },
     "node_modules/path-exists": {
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
@@ -4153,16 +4139,6 @@
         "node": ">=8"
       }
     },
-    "node_modules/resolve-from": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
-      "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=4"
-      }
-    },
     "node_modules/safe-array-concat": {
       "version": "1.1.4",
       "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.4.tgz",
@@ -4364,15 +4340,15 @@
       }
     },
     "node_modules/side-channel": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
-      "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==",
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz",
+      "integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
         "es-errors": "^1.3.0",
-        "object-inspect": "^1.13.3",
-        "side-channel-list": "^1.0.0",
+        "object-inspect": "^1.13.4",
+        "side-channel-list": "^1.0.1",
         "side-channel-map": "^1.0.1",
         "side-channel-weakmap": "^1.0.2"
       },
@@ -4580,19 +4556,6 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
-    "node_modules/strip-json-comments": {
-      "version": "3.1.1",
-      "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
-      "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=8"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
     "node_modules/supports-color": {
       "version": "7.2.0",
       "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
@@ -4894,16 +4857,16 @@
       }
     },
     "node_modules/typescript-eslint": {
-      "version": "8.60.1",
-      "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.60.1.tgz",
-      "integrity": "sha512-6m5hkkRAp8lKvhVpcprAIn5KkehQEh+47oHH2VGnExEh7dhNxXlg6GPAOIu6TxbVQxhebrJDvjl3020ooiWCMA==",
+      "version": "8.61.1",
+      "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.61.1.tgz",
+      "integrity": "sha512-V7PayAfJokV3pEHgN7/v03D1SpujhRfQtYLbLIiBfDDncdg4PAiRBfoS4cnCANK4jmAPncczi59QO3afiXUlNw==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@typescript-eslint/eslint-plugin": "8.60.1",
-        "@typescript-eslint/parser": "8.60.1",
-        "@typescript-eslint/typescript-estree": "8.60.1",
-        "@typescript-eslint/utils": "8.60.1"
+        "@typescript-eslint/eslint-plugin": "8.61.1",
+        "@typescript-eslint/parser": "8.61.1",
+        "@typescript-eslint/typescript-estree": "8.61.1",
+        "@typescript-eslint/utils": "8.61.1"
       },
       "engines": {
         "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
diff --git a/dumper-companion/package.json b/dumper-companion/package.json
index 90e97964..c95257e7 100644
--- a/dumper-companion/package.json
+++ b/dumper-companion/package.json
@@ -16,11 +16,11 @@
     "webpack-cli": "^7.0.2"
   },
   "devDependencies": {
-    "@eslint/js": "^9.39.4",
+    "@eslint/js": "^10.0.1",
     "@stylistic/eslint-plugin": "^5.10.0",
-    "eslint": "^9.39.4",
+    "eslint": "^10.5.0",
     "eslint-config-preact": "^2.0.0",
     "globals": "^17.5.0",
-    "typescript-eslint": "^8.58.2"
+    "typescript-eslint": "^8.61.1"
   }
 }



From noreply at scummvm.org  Fri Jun 19 10:13:48 2026
From: noreply at scummvm.org (ScummVM git)
Date: Fri, 19 Jun 2026 10:13:48 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm-web
 dependabot/npm_and_yarn/dumper-companion/patch-releases-cb54dbbe77 ->
 a18f0aea40e3c9593ba9c733dbb688476ff3e7de
Message-ID: <20260619101348.924BB7005CE@ron.scummvm.net>

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

Summary:
a18f0aea40 Bump ts-loader


Commit: a18f0aea40e3c9593ba9c733dbb688476ff3e7de
    https://github.com/scummvm/scummvm-web/commit/a18f0aea40e3c9593ba9c733dbb688476ff3e7de
Author: dependabot[bot] (49699333+dependabot[bot]@users.noreply.github.com)
Date: 2026-06-19T10:13:45Z

Commit Message:
Bump ts-loader

Bumps the patch-releases group with 1 update in the /dumper-companion directory: [ts-loader](https://github.com/TypeStrong/ts-loader).


Updates `ts-loader` from 9.6.0 to 9.6.1
- [Release notes](https://github.com/TypeStrong/ts-loader/releases)
- [Changelog](https://github.com/TypeStrong/ts-loader/blob/main/CHANGELOG.md)
- [Commits](https://github.com/TypeStrong/ts-loader/compare/v9.6.0...v9.6.1)

---
updated-dependencies:
- dependency-name: ts-loader
  dependency-version: 9.6.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: patch-releases
...

Signed-off-by: dependabot[bot] <support at github.com>

Changed paths:
    dumper-companion/package-lock.json
    dumper-companion/package.json


diff --git a/dumper-companion/package-lock.json b/dumper-companion/package-lock.json
index e7324deb..835d51c1 100644
--- a/dumper-companion/package-lock.json
+++ b/dumper-companion/package-lock.json
@@ -12,7 +12,7 @@
         "@zip.js/zip.js": "^2.8.26",
         "preact": "^10.29.1",
         "punycode": "^2.3.1",
-        "ts-loader": "^9.5.7",
+        "ts-loader": "^9.6.1",
         "webpack": "^5.107.1",
         "webpack-cli": "^7.0.2"
       },
@@ -4751,9 +4751,9 @@
       }
     },
     "node_modules/ts-loader": {
-      "version": "9.6.0",
-      "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.6.0.tgz",
-      "integrity": "sha512-dsJO0S+T7grTDWTc4a0nTygXGjKncVUpx8Y+af8EvI/D5WgTJby5UEk5eoMCB9EcLQmnvitqh99MqtjtHgAwFQ==",
+      "version": "9.6.1",
+      "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.6.1.tgz",
+      "integrity": "sha512-8FMHnmxtpncUAu0ZjkqpXnOTlwc9eY95esH8WVN94guTPPdkg2ofVdiVM5j8L2lmjiGerXd56zXb/D2JyVQPLg==",
       "license": "MIT",
       "dependencies": {
         "chalk": "^4.1.0",
diff --git a/dumper-companion/package.json b/dumper-companion/package.json
index 90e97964..bd52c0dd 100644
--- a/dumper-companion/package.json
+++ b/dumper-companion/package.json
@@ -11,7 +11,7 @@
     "preact": "^10.29.1",
     "punycode": "^2.3.1",
     "@types/punycode": "^2.1.4",
-    "ts-loader": "^9.5.7",
+    "ts-loader": "^9.6.1",
     "webpack": "^5.107.1",
     "webpack-cli": "^7.0.2"
   },



From noreply at scummvm.org  Fri Jun 19 11:35:55 2026
From: noreply at scummvm.org (dreammaster)
Date: Fri, 19 Jun 2026 11:35:55 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 a37ce1065b023c540dfab393ae9f88dde6c6ccd0
Message-ID: <20260619113555.6D2077005CD@ron.scummvm.net>

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:
050141580c MADS: FOREST: Implementing global game functions
a37ce1065b MADS: FOREST: Completely remove unused ForestSoundManager


Commit: 050141580caea2dad102da0e98a7dc2c94fa3312
    https://github.com/scummvm/scummvm/commit/050141580caea2dad102da0e98a7dc2c94fa3312
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-06-19T21:12:14+10:00

Commit Message:
MADS: FOREST: Implementing global game functions

Changed paths:
    engines/mads/madsv2/core/digi.cpp
    engines/mads/madsv2/core/digi.h
    engines/mads/madsv2/forest/forest.cpp
    engines/mads/madsv2/forest/forest.h
    engines/mads/madsv2/forest/global.cpp
    engines/mads/madsv2/forest/global.h


diff --git a/engines/mads/madsv2/core/digi.cpp b/engines/mads/madsv2/core/digi.cpp
index 5d08cae44b4..75a9d8c2208 100644
--- a/engines/mads/madsv2/core/digi.cpp
+++ b/engines/mads/madsv2/core/digi.cpp
@@ -28,6 +28,7 @@ namespace MADSV2 {
 int digi_val1;
 int digi_val2;
 int digi_timing_index;
+bool digi_flag1, digi_flag2;
 
 void digi_install() {
 	digi_val1 = 0;
diff --git a/engines/mads/madsv2/core/digi.h b/engines/mads/madsv2/core/digi.h
index db4eee253ae..ad0bb273ba8 100644
--- a/engines/mads/madsv2/core/digi.h
+++ b/engines/mads/madsv2/core/digi.h
@@ -30,6 +30,7 @@ namespace MADSV2 {
 extern int digi_val1;
 extern int digi_val2;
 extern int digi_timing_index;
+extern bool digi_flag1, digi_flag2;
 
 extern void digi_install();
 extern void digi_play(const char *name, int slot);
diff --git a/engines/mads/madsv2/forest/forest.cpp b/engines/mads/madsv2/forest/forest.cpp
index 922dc568c3b..c029be800a7 100644
--- a/engines/mads/madsv2/forest/forest.cpp
+++ b/engines/mads/madsv2/forest/forest.cpp
@@ -114,18 +114,21 @@ void ForestEngine::global_daemon_code() {
 }
 
 void ForestEngine::global_pre_parser_code() {
+	if (player_said_1(look) || player_said_1(throw)) {
+		player.need_to_walk = false;
+	}
 }
 
 void ForestEngine::global_parser_code() {
-}
-
-void ForestEngine::global_object_examine() {
+	// No implementation
 }
 
 void ForestEngine::global_error_code() {
+	Forest::global_error_code();
 }
 
 void ForestEngine::global_room_init() {
+	Forest::global_room_init();
 }
 
 void ForestEngine::global_sound_driver() {
diff --git a/engines/mads/madsv2/forest/forest.h b/engines/mads/madsv2/forest/forest.h
index e7f9d2f6662..6765d195a40 100644
--- a/engines/mads/madsv2/forest/forest.h
+++ b/engines/mads/madsv2/forest/forest.h
@@ -29,9 +29,6 @@ namespace MADSV2 {
 namespace Forest {
 
 class ForestEngine : public MADSV2Engine {
-private:
-	static void global_object_examine();
-
 public:
 	ForestEngine(OSystem *syst, const MADSGameDescription *gameDesc) :
 		MADSV2Engine(syst, gameDesc) {}
diff --git a/engines/mads/madsv2/forest/global.cpp b/engines/mads/madsv2/forest/global.cpp
index 476693e6ac2..5b6de4b6fe6 100644
--- a/engines/mads/madsv2/forest/global.cpp
+++ b/engines/mads/madsv2/forest/global.cpp
@@ -21,6 +21,9 @@
 
 #include "common/textconsole.h"
 #include "mads/madsv2/core/digi.h"
+#include "mads/madsv2/core/object.h"
+#include "mads/madsv2/core/player.h"
+#include "mads/madsv2/core/text.h"
 #include "mads/madsv2/core/error.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/imath.h"
@@ -893,6 +896,95 @@ static void global_anim2_5(int arg_0, int arg_2, int16 *arg_4) {
 	}
 }
 
+void global_room_init() {
+	global[g133] = 1;
+	global[g143] = 1;
+	global[g132] = -1;
+	global[g137] = -1;
+	global[g142] = -1;
+	global[g147] = -1;
+	global[g151] = -1;
+	global[g152] = -1;
+	global[g153] = -1;
+	room_203_flag = true;
+	global[g131] = 0;
+	global[g130] = 0;
+	global[g135] = 0;
+	global[g136] = 0;
+	global[g134] = 0;
+	global[g141] = 0;
+	global[g140] = 0;
+	global[g145] = 0;
+	global[g146] = 0;
+	global[g144] = 0;
+	global[g148] = 0;
+	global[perform_displacements] = 0;
+	digi_flag1 = false;
+	digi_flag2 = false;
+	digi_val2 = 0;
+}
+
+void global_error_code() {
+	int16 randVal = imath_random(1, 1000);
+	int16 text_id = 0;
+
+	if (player_parse(4, 0)) {
+		if (player_has(object_named(player_main_noun)) && player.main_object_source != 4) {
+			text_id = 25;
+		} else {
+			if (randVal <= 333)      text_id = 1;
+			else if (randVal <= 666) text_id = 2;
+			else                     text_id = 3;
+		}
+	} else if (player_parse(5, 0)) {
+		text_id = (randVal < 750) ? 4 : 5;
+	} else if (player_parse(10, 0)) {
+		text_id = (randVal < 750) ? 6 : 7;
+	} else if (player_parse(6, 0)) {
+		if (randVal <= 500)      text_id = 8;
+		else if (randVal <= 750) text_id = 9;
+		else                     text_id = 10;
+	} else if (player_parse(11, 0)) {
+		if (randVal <= 500)      text_id = 11;
+		else if (randVal <= 750) text_id = 12;
+		else                     text_id = 13;
+	} else if (player_parse(7, 0)) {
+		if (player_has(object_named(player_main_noun))) {
+			text_id = 26;
+		} else if (player.main_object_source == 4 && player.second_object_source == 4) {
+			text_id = 28;
+		} else {
+			text_id = (randVal < 500) ? 14 : 15;
+		}
+	} else if (player_parse(8, 0)) {
+		text_id = (randVal <= 500) ? 16 : 17;
+	} else if (player_parse(9, 0)) {
+		if (player_has(object_named(player_main_noun))) {
+			text_id = 27;
+		} else if (player.main_object_source == 4 && player.second_object_source == 4) {
+			text_id = 28;
+		} else {
+			text_id = 18;
+		}
+	} else if (player_parse(12, 0)) {
+		if (player_has(object_named(player_main_noun))) {
+			text_id = 19;
+		} else {
+			text_id = 28;
+		}
+	} else if (player_parse(3, 0)) {
+		object_named(player_main_noun);
+		if (randVal <= 333)      text_id = 20;
+		else if (randVal <= 666) text_id = 21;
+		else                     text_id = 22;
+	} else if (!player_parse(13, 0) && !player_parse(160, 0)) {
+		text_id = (randVal < 500) ? 23 : 24;
+	}
+
+	if (text_id)
+		text_show(text_id);
+}
+
 void global_digi_play(int num) {
 	// TODO
 	warning("TODO: global_digi_play");
diff --git a/engines/mads/madsv2/forest/global.h b/engines/mads/madsv2/forest/global.h
index f5db0c4fecb..0c47ac2b5b2 100644
--- a/engines/mads/madsv2/forest/global.h
+++ b/engines/mads/madsv2/forest/global.h
@@ -263,6 +263,8 @@ extern void sync_room(Common::Serializer &s);
 extern void global_section_walker();
 extern void global_section_interface();
 
+extern void global_room_init();
+extern void global_error_code();
 extern void global_anim1(int arg_0, int arg_2, int arg_4, int16 *arg_6);
 extern void global_anim2(int arg_0, int arg_2, int arg_4, int16 *arg_6);
 extern void global_anim3(int handle, int16 *frame);


Commit: a37ce1065b023c540dfab393ae9f88dde6c6ccd0
    https://github.com/scummvm/scummvm/commit/a37ce1065b023c540dfab393ae9f88dde6c6ccd0
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-06-19T21:35:37+10:00

Commit Message:
MADS: FOREST: Completely remove unused ForestSoundManager

Changed paths:
  R engines/mads/madsv2/forest/sound_forest.cpp
  R engines/mads/madsv2/forest/sound_forest.h
    engines/mads/madsv2/forest/forest.cpp
    engines/mads/module.mk


diff --git a/engines/mads/madsv2/forest/forest.cpp b/engines/mads/madsv2/forest/forest.cpp
index c029be800a7..751831779f0 100644
--- a/engines/mads/madsv2/forest/forest.cpp
+++ b/engines/mads/madsv2/forest/forest.cpp
@@ -38,7 +38,6 @@
 #include "mads/madsv2/forest/forest.h"
 #include "mads/madsv2/forest/global.h"
 #include "mads/madsv2/forest/main.h"
-#include "mads/madsv2/forest/sound_forest.h"
 #include "mads/madsv2/forest/rooms/section1.h"
 #include "mads/madsv2/forest/rooms/section2.h"
 #include "mads/madsv2/forest/rooms/section3.h"
@@ -69,10 +68,6 @@ Common::Error ForestEngine::run() {
 			SearchMan.add("mpslabs", arch);
 	}
 
-	// Set up sound manager
-	_soundManager = new ForestSoundManager(_mixer, _soundFlag);
-	_soundManager->validate();
-
 	// Run the game
 	Forest::forest_main();
 
diff --git a/engines/mads/madsv2/forest/sound_forest.cpp b/engines/mads/madsv2/forest/sound_forest.cpp
deleted file mode 100644
index abd67d0176f..00000000000
--- a/engines/mads/madsv2/forest/sound_forest.cpp
+++ /dev/null
@@ -1,66 +0,0 @@
-/* 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/endian.h"
-#include "common/file.h"
-#include "common/md5.h"
-#include "common/textconsole.h"
-#include "mads/madsv2/forest/sound_forest.h"
-
-namespace MADS {
-namespace MADSV2 {
-namespace Forest {
-
-void ForestSoundManager::validate() {
-	Common::File f;
-	static const char *const MD5[] = {
-		"cac84f53ccf18ca56f4c03352037790f",
-		"2dcdbe18ca5225384cdb97ceb7f5642a",
-		"c6001b0dfe32cb9399ab60742b631c2e",
-		"1596b657c6171e13714eaf114bf94641",
-		"ecbb8bdf1e2e36fcacedce79761e625b",
-		nullptr,
-		nullptr,
-		nullptr,
-		"379fcc9af2142f15a3e7166eee6dd49d"
-	};
-
-	for (int i = 1; i <= 9; ++i) {
-		if (i == 7 || i == 8)
-			continue;
-		Common::Path filename(Common::String::format("asound.dr%d", i));
-		if (!f.open(filename))
-			error("Could not process - %s", filename.toString().c_str());
-		Common::String md5str = Common::computeStreamMD5AsString(f, 8192);
-		f.close();
-
-		if (md5str != MD5[i - 1])
-			error("Invalid sound file - %s", filename.toString().c_str());
-	}
-}
-
-void ForestSoundManager::loadDriver(int sectionNumber) {
-	// TODO
-}
-
-} // namespace Forest
-} // namespace MADSV2
-} // namespace MADS
diff --git a/engines/mads/madsv2/forest/sound_forest.h b/engines/mads/madsv2/forest/sound_forest.h
deleted file mode 100644
index ba84cce9623..00000000000
--- a/engines/mads/madsv2/forest/sound_forest.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/* 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 MADS_FOREST_SOUND_H
-#define MADS_FOREST_SOUND_H
-
-#include "mads/madsv2/forest/asound.h"
-
-namespace MADS {
-namespace MADSV2 {
-namespace Forest {
-
-class ForestSoundManager : public SoundManager {
-protected:
-	void loadDriver(int sectionNum) override;
-
-public:
-	ForestSoundManager(Audio::Mixer *mixer, bool &soundFlag) : SoundManager(mixer, soundFlag) {
-	}
-	~ForestSoundManager() override {
-	}
-
-	void validate() override;
-};
-
-} // namespace Forest
-} // namespace MADSV2
-} // namespace MADS
-
-#endif
diff --git a/engines/mads/module.mk b/engines/mads/module.mk
index 42123ec7955..707e17543b1 100644
--- a/engines/mads/module.mk
+++ b/engines/mads/module.mk
@@ -270,7 +270,6 @@ MODULE_OBJS += \
 	madsv2/forest/main.o \
 	madsv2/forest/main_menu.o \
 	madsv2/forest/menus.o \
-	madsv2/forest/sound_forest.o \
 	madsv2/forest/mads/mads.o \
 	madsv2/forest/rooms/room101.o \
 	madsv2/forest/rooms/room103.o \



From noreply at scummvm.org  Fri Jun 19 21:36:59 2026
From: noreply at scummvm.org (sev-)
Date: Fri, 19 Jun 2026 21:36:59 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 58560d17fbcbf229f81f01aaf8dcc2ea873002aa
Message-ID: <20260619213659.B51137005CD@ron.scummvm.net>

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:
379e0dcde6 DIRECTOR: Keep track of context when working with factories
828a15ad8a CREDITS: Moved leads to residualvm section
58560d17fb CREDITS: Regenerate


Commit: 379e0dcde6887185af651dbb5e9636dd3655e039
    https://github.com/scummvm/scummvm/commit/379e0dcde6887185af651dbb5e9636dd3655e039
Author: Zachary Berry (zberry92 at gmail.com)
Date: 2026-06-19T23:29:48+02:00

Commit Message:
DIRECTOR: Keep track of context when working with factories

A factory could be created in one movie, and then called from
another MIAW (via tell). This should execute in the context
of the original movie.

This keeps track of the context window and switched Lingo
context when calling those methods.

Fixes startup in jewels

Changed paths:
    engines/director/cast.cpp
    engines/director/cast.h
    engines/director/lingo/lingo-code.cpp
    engines/director/lingo/lingo-object.cpp
    engines/director/lingo/lingo-object.h
    engines/director/lingo/lingo.cpp
    engines/director/lingo/lingo.h


diff --git a/engines/director/cast.cpp b/engines/director/cast.cpp
index 7f403b7cd5e..b9d8ba07068 100644
--- a/engines/director/cast.cpp
+++ b/engines/director/cast.cpp
@@ -1806,6 +1806,7 @@ void Cast::loadLingoContext(Common::SeekableReadStreamEndian &stream) {
 				if (_lingoArchive->getScriptContext(script->_scriptType, script->_id)) {
 					error("Cast::loadLingoContext: Script already defined for type %s, id %d", scriptType2str(script->_scriptType), script->_id);
 				}
+				script->_cast = this;
 				_lingoArchive->scriptContexts[script->_scriptType][script->_id] = script;
 				_lingoArchive->patchScriptHandler(script->_scriptType, CastMemberID(script->_id, _castLibID));
 			} else {
diff --git a/engines/director/cast.h b/engines/director/cast.h
index d6be2246252..43f08651f59 100644
--- a/engines/director/cast.h
+++ b/engines/director/cast.h
@@ -92,6 +92,7 @@ public:
 	void loadArchive();
 	void setArchive(Common::SharedPtr<Archive> archive);
 	Common::SharedPtr<Archive> getArchive() const { return _castArchive; };
+	Movie *getMovie() const { return _movie; }
 	Common::String getMacName() const { return _macName; }
 	Common::String getCastName() const { return _castName; }
 	void setCastName(const Common::String &name) { _castName = name; }
diff --git a/engines/director/lingo/lingo-code.cpp b/engines/director/lingo/lingo-code.cpp
index 6d1a422f89c..d73993e97a5 100644
--- a/engines/director/lingo/lingo-code.cpp
+++ b/engines/director/lingo/lingo-code.cpp
@@ -47,6 +47,7 @@
 
 #include "director/director.h"
 #include "director/debugger.h"
+#include "director/cast.h"
 #include "director/movie.h"
 #include "director/score.h"
 #include "director/sprite.h"
@@ -258,6 +259,18 @@ void Lingo::pushContext(const Symbol funcSym, bool allowRetVal, Datum defaultRet
 		_state->context->incRefCount();
 	}
 
+	// Run the handler in the window that owns its script (MIAW scoping); mirrors c_tell().
+	fp->retWindow = nullptr;
+	if (funcSym.ctx && funcSym.ctx->_cast && funcSym.ctx->_cast->getMovie()) {
+		Window *targetWindow = funcSym.ctx->_cast->getMovie()->getWindow();
+		Window *currentWindow = _vm->getCurrentWindow();
+		if (targetWindow && targetWindow != currentWindow) {
+			fp->retWindow = currentWindow;
+			currentWindow->moveLingoState(targetWindow);
+			_vm->setCurrentWindow(targetWindow);
+		}
+	}
+
 	DatumHash *localvars = new DatumHash;
 	if (funcSym.anonymous && _state->localVars) {
 		// Execute anonymous functions within the current var frame.
@@ -364,8 +377,16 @@ void Lingo::popContext(bool aborting) {
 		printCallStack(_state->pc);
 	}
 
+	// Undo the pushContext window switch.
+	Window *retWindow = fp->retWindow;
+
 	delete fp;
 
+	if (retWindow && _vm->getCurrentWindow() != retWindow) {
+		_vm->getCurrentWindow()->moveLingoState(retWindow);
+		_vm->setCurrentWindow(retWindow);
+	}
+
 	g_debugger->popContextHook();
 }
 
diff --git a/engines/director/lingo/lingo-object.cpp b/engines/director/lingo/lingo-object.cpp
index 4355af923f4..fd191216170 100644
--- a/engines/director/lingo/lingo-object.cpp
+++ b/engines/director/lingo/lingo-object.cpp
@@ -577,6 +577,7 @@ ScriptContext::ScriptContext(const ScriptContext &sc) : Object<ScriptContext>(sc
 
 	_id = sc._id;
 	_castLibHint = sc._castLibHint;
+	_cast = sc._cast;
 }
 
 ScriptContext::~ScriptContext() {
diff --git a/engines/director/lingo/lingo-object.h b/engines/director/lingo/lingo-object.h
index ebd29576ad4..b56717251e4 100644
--- a/engines/director/lingo/lingo-object.h
+++ b/engines/director/lingo/lingo-object.h
@@ -217,6 +217,7 @@ public:
 	int _scriptId;
 	uint16 _parentNumber;
 	uint16 _castLibHint;
+	Cast *_cast = nullptr;	// owning cast (MIAW scoping)
 	Common::Array<Common::String> _functionNames; // used by cb_localcall
 	Common::HashMap<Common::String, Common::Array<uint32>> _functionByteOffsets;
 	SymbolHash _functionHandlers;
diff --git a/engines/director/lingo/lingo.cpp b/engines/director/lingo/lingo.cpp
index d11bbe941ad..95ab781a34e 100644
--- a/engines/director/lingo/lingo.cpp
+++ b/engines/director/lingo/lingo.cpp
@@ -401,6 +401,7 @@ void LingoArchive::addCode(const Common::U32String &code, ScriptType type, uint1
 
 	ScriptContext *sc = g_lingo->_compiler->compileLingo(code, this, type, CastMemberID(id, cast->_castLibID), contextName, false, preprocFlags);
 	if (sc) {
+		sc->_cast = cast;
 		scriptContexts[type][id] = sc;
 		sc->incRefCount();
 	}
diff --git a/engines/director/lingo/lingo.h b/engines/director/lingo/lingo.h
index e140baedd48..2f30e6f1902 100644
--- a/engines/director/lingo/lingo.h
+++ b/engines/director/lingo/lingo.h
@@ -46,6 +46,7 @@ class Cast;
 class ScriptContext;
 class DirectorEngine;
 class Frame;
+class Window;
 class LingoCompiler;
 struct Breakpoint;
 
@@ -262,6 +263,7 @@ struct CFrame {	/* proc/func call stack frame */
 	Datum			defaultRetVal;		/* default return value */
 	int				paramCount;			/* original number of arguments submitted */
 	Common::Array<Datum> paramList;		/* original argument list */
+	Window			*retWindow;			/* window to restore on return */
 };
 
 struct LingoEvent {


Commit: 828a15ad8a6109a3cfcdc4347d0fedeb24192ea2
    https://github.com/scummvm/scummvm/commit/828a15ad8a6109a3cfcdc4347d0fedeb24192ea2
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2026-06-19T23:36:26+02:00

Commit Message:
CREDITS: Moved leads to residualvm section

Changed paths:
    devtools/credits.pl


diff --git a/devtools/credits.pl b/devtools/credits.pl
index d94341e812f..08fcd2ac5be 100755
--- a/devtools/credits.pl
+++ b/devtools/credits.pl
@@ -508,10 +508,8 @@ begin_credits("Credits");
 	begin_section("ScummVM Team", "scummvm_team");
 		begin_section("Project Leaders", "project_leader");
 			begin_persons();
-				add_person("Pawe&#322; Ko&#322;odziejski", "aquadran", "");
-				add_person("Eugene Sandulenko", "sev", "");
-				add_person("Einar Johan T. S&oslash;m&aring;en", "somaen", "");
-				add_person("Lothar Serra Mari", "felsqualle", "");
+				add_person("Eugene Sandulenko", "sev", "Project Leader");
+				add_person("Lothar Serra Mari", "felsqualle", "Project Co-Leader and Admin");
 			end_persons();
 		end_section();
 
@@ -1117,6 +1115,11 @@ begin_credits("Credits");
 		end_section();
 
 		begin_section("ResidualVM Contributors", "residualvm_contrib");
+			begin_section("Project Leaders");
+				add_person("Pawe&#322; Ko&#322;odziejski", "aquadran", "");
+				add_person("Einar Johan T. S&oslash;m&aring;en", "somaen", "");
+			end_section();
+
 			begin_section("Grim");
 				add_person("Thomas Allen", "olldray", "Various engine code fixes and improvements");
 				add_person("Torbj&ouml;rn Andersson", "eriktorbjorn", "Various code fixes");


Commit: 58560d17fbcbf229f81f01aaf8dcc2ea873002aa
    https://github.com/scummvm/scummvm/commit/58560d17fbcbf229f81f01aaf8dcc2ea873002aa
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2026-06-19T23:36:41+02:00

Commit Message:
CREDITS: Regenerate

Changed paths:
    AUTHORS
    doc/docportal/help/credits.rst
    gui/credits.h


diff --git a/AUTHORS b/AUTHORS
index 7f6f7e8cd3e..09cd67fd4d1 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -2,10 +2,8 @@ ScummVM Team
 ************
   Project Leaders
   ---------------
-     Pawel Kolodziejski
-     Eugene Sandulenko
-     Einar Johan T. Somaaen
-     Lothar Serra Mari
+     Eugene Sandulenko              - Project Leader
+     Lothar Serra Mari              - Project Co-Leader and Admin
 
   PR Office
   ---------
@@ -1217,6 +1215,10 @@ Other contributions
 
   ResidualVM Contributors
   -----------------------
+    Project Leaders:
+       Pawel Kolodziejski
+       Einar Johan T. Somaaen
+
     Grim:
        Thomas Allen                   - Various engine code fixes and
                                         improvements
diff --git a/doc/docportal/help/credits.rst b/doc/docportal/help/credits.rst
index 83cba9afb50..f59a4b9f8d7 100644
--- a/doc/docportal/help/credits.rst
+++ b/doc/docportal/help/credits.rst
@@ -14,14 +14,10 @@ Project Leaders
 .. list-table::
    :widths: 35 65
 
-   * - Pawe?? Ko??odziejski
-     -
    * - Eugene Sandulenko
-     -
-   * - Einar Johan T. S??m??en
-     -
+     - Project Leader
    * - Lothar Serra Mari
-     -
+     - Project Co-Leader and Admin
 
 PR Office
 *********
@@ -2939,6 +2935,17 @@ Special thanks to Bob Heitman and Corey Cole for their support of FreeSCI.
 ResidualVM Contributors
 ***********************
 
+Project Leaders
+^^^^^^^^^^^^^^^
+
+.. list-table::
+   :widths: 35 65
+
+   * - Pawe?? Ko??odziejski
+     -
+   * - Einar Johan T. S??m??en
+     -
+
 Grim
 ^^^^
 
diff --git a/gui/credits.h b/gui/credits.h
index 046f8917163..2953f97d528 100644
--- a/gui/credits.h
+++ b/gui/credits.h
@@ -3,10 +3,10 @@ static const char *const credits[] = {
 "C1""ScummVM Team",
 "",
 "C1""Project Leaders",
-"C0""Pawe\305\202 Ko\305\202odziejski",
 "C0""Eugene Sandulenko",
-"C0""Einar Johan T. S\303\270m\303\245en",
+"C2""Project Leader",
 "C0""Lothar Serra Mari",
+"C2""Project Co-Leader and Admin",
 "",
 "C1""PR Office",
 "C0""Arnaud Boutonn\303\251",
@@ -1450,6 +1450,10 @@ static const char *const credits[] = {
 "C0""",
 "",
 "C1""ResidualVM Contributors",
+"C1""Project Leaders",
+"C0""Pawe\305\202 Ko\305\202odziejski",
+"C0""Einar Johan T. S\303\270m\303\245en",
+"",
 "C1""Grim",
 "C0""Thomas Allen",
 "C2""Various engine code fixes and improvements",



From noreply at scummvm.org  Fri Jun 19 21:37:18 2026
From: noreply at scummvm.org (sev-)
Date: Fri, 19 Jun 2026 21:37:18 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm-web master ->
 250976f01f3803c3d6a4c3f7143c5f66e70f9e54
Message-ID: <20260619213718.379F77005CD@ron.scummvm.net>

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

Summary:
250976f01f WEB: Sync credits


Commit: 250976f01f3803c3d6a4c3f7143c5f66e70f9e54
    https://github.com/scummvm/scummvm-web/commit/250976f01f3803c3d6a4c3f7143c5f66e70f9e54
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2026-06-19T23:37:12+02:00

Commit Message:
WEB: Sync credits

Changed paths:
    data/en/credits.yaml


diff --git a/data/en/credits.yaml b/data/en/credits.yaml
index 483284e2..adff47ed 100644
--- a/data/en/credits.yaml
+++ b/data/en/credits.yaml
@@ -10,22 +10,14 @@
     -
       name: ""
       person:
-      -
-        name: "Pawe&#322; Ko&#322;odziejski"
-        alias: "aquadran"
-        description: ""
       -
         name: "Eugene Sandulenko"
         alias: "sev"
-        description: ""
-      -
-        name: "Einar Johan T. S&oslash;m&aring;en"
-        alias: "somaen"
-        description: ""
+        description: "Project Leader"
       -
         name: "Lothar Serra Mari"
         alias: "felsqualle"
-        description: ""
+        description: "Project Co-Leader and Admin"
   -
     title: "PR Office"
     anchor: "pr"
@@ -3546,6 +3538,17 @@
     title: "ResidualVM Contributors"
     anchor: "residualvm_contrib"
     group:
+    -
+      name: "Project Leaders"
+      person:
+      -
+        name: "Pawe&#322; Ko&#322;odziejski"
+        alias: "aquadran"
+        description: ""
+      -
+        name: "Einar Johan T. S&oslash;m&aring;en"
+        alias: "somaen"
+        description: ""
     -
       name: "Grim"
       person:



From noreply at scummvm.org  Fri Jun 19 22:12:02 2026
From: noreply at scummvm.org (sev-)
Date: Fri, 19 Jun 2026 22:12:02 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 cb04714a079740f2b1dc223340a01501e4512cf3
Message-ID: <20260619221202.6031F7005CD@ron.scummvm.net>

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

Summary:
5991cbeeae DIRECTOR: XOBJ: Add lipsync playback to WidgetXObj
d5540f2919 DIRECTOR: LINGO: Allow setting the timeoutLapsed in all versions
d4e2e3d8a2 DIRECTOR: XOBJ: Add loadData/saveData to WidgetXObj
cb04714a07 DIRECTOR: LINGO: Add inline kLPPTrimGarbage handling to compiler


Commit: 5991cbeeae1cca9645964b068d2f4b7435e98e2d
    https://github.com/scummvm/scummvm/commit/5991cbeeae1cca9645964b068d2f4b7435e98e2d
Author: Scott Percival (code at moral.net.au)
Date: 2026-06-20T00:11:56+02:00

Commit Message:
DIRECTOR: XOBJ: Add lipsync playback to WidgetXObj

Fixes character portraits in Yellow Brick Road.

Changed paths:
    engines/director/lingo/xlibs/w/widget.cpp
    engines/director/lingo/xlibs/w/widget.h


diff --git a/engines/director/lingo/xlibs/w/widget.cpp b/engines/director/lingo/xlibs/w/widget.cpp
index ee5941d5a3a..c32cb0954c3 100644
--- a/engines/director/lingo/xlibs/w/widget.cpp
+++ b/engines/director/lingo/xlibs/w/widget.cpp
@@ -19,6 +19,7 @@
  *
  */
 
+#include "common/macresman.h"
 #include "common/system.h"
 
 #include "director/director.h"
@@ -121,7 +122,27 @@ void WidgetXObj::close(ObjectType type) {
 }
 
 void WidgetXObj::m_new(int nargs) {
-	g_lingo->printSTUBWithArglist("WidgetXObj::m_new", nargs);
+
+	WidgetXObject *me = static_cast<WidgetXObject *>(g_lingo->_state->me.u.obj);
+	if (nargs == 1) {
+		Common::String rawPath = g_lingo->pop().asString();
+		nargs--;
+		Common::SeekableReadStream *stream = Common::MacResManager::openFileOrDataFork(findPath(rawPath));
+		if (!stream) {
+			warning("WidgetXObj::m_new: unable to resolve path %s", rawPath.c_str());
+		} else {
+			debugC(5, kDebugXObj, "WidgetXObj::m_new: loading lip sync data from \"%s\"", rawPath.c_str());
+			while (stream->pos() < stream->size()) {
+				Common::String name = stream->readString(0, 16);
+				uint16 size = stream->readUint16LE();
+				me->_lipSyncData[name] = Common::Array<byte>();
+				me->_lipSyncData[name].resize(size);
+				stream->read(me->_lipSyncData[name].data(), size);
+				debugC(5, kDebugXObj, "WidgetXObj::m_new: added \"%s\" (%d)", name.c_str(), size);
+			}
+			delete stream;
+		}
+	}
 	g_lingo->dropStack(nargs);
 	g_lingo->push(g_lingo->_state->me);
 }
@@ -136,8 +157,22 @@ XOBJSTUB(WidgetXObj::m_add, 0)
 XOBJSTUB(WidgetXObj::m_askQuit, 0)
 XOBJSTUB(WidgetXObj::m_first, "")
 
-// 100 means the animation is done
-XOBJSTUB(WidgetXObj::m_lipSync, 100)
+void WidgetXObj::m_lipSync(int nargs) {
+	WidgetXObject *me = static_cast<WidgetXObject *>(g_lingo->_state->me.u.obj);
+	ARGNUMCHECK(2);
+	int pos = MAX(0, g_lingo->pop().asInt());
+	// 100 means the animation is done
+	int result = 100;
+	Common::String name = g_lingo->pop().asString();
+	if (me->_lipSyncData.contains(name)) {
+		if (pos < (int)me->_lipSyncData[name].size()) {
+			result = (int)me->_lipSyncData[name][pos];
+		}
+	}
+
+	debugC(5, kDebugXObj, "WidgetXObj::m_lipSync: \"%s\" frame %d, result: %d", name.c_str(), pos, result);
+	g_lingo->push(result);
+}
 
 XOBJSTUB(WidgetXObj::m_loadData, 0)
 XOBJSTUB(WidgetXObj::m_saveData, 0)
diff --git a/engines/director/lingo/xlibs/w/widget.h b/engines/director/lingo/xlibs/w/widget.h
index 4d7af06e124..955d1bc8c0d 100644
--- a/engines/director/lingo/xlibs/w/widget.h
+++ b/engines/director/lingo/xlibs/w/widget.h
@@ -27,6 +27,8 @@ namespace Director {
 class WidgetXObject : public Object<WidgetXObject> {
 public:
 	WidgetXObject(ObjectType objType);
+
+	Common::HashMap<Common::String, Common::Array<byte>> _lipSyncData;
 };
 
 namespace WidgetXObj {


Commit: d5540f29191a57c76302f0d45930966b57cde946
    https://github.com/scummvm/scummvm/commit/d5540f29191a57c76302f0d45930966b57cde946
Author: Scott Percival (code at moral.net.au)
Date: 2026-06-20T00:11:56+02:00

Commit Message:
DIRECTOR: LINGO: Allow setting the timeoutLapsed in all versions

Fixes enemies speeding up when using an item + long pauses after an
attack in Yellow Brick Road.

Changed paths:
    engines/director/lingo/lingo-the.cpp


diff --git a/engines/director/lingo/lingo-the.cpp b/engines/director/lingo/lingo-the.cpp
index 3afd0676ebc..13d3fd36f26 100644
--- a/engines/director/lingo/lingo-the.cpp
+++ b/engines/director/lingo/lingo-the.cpp
@@ -1477,9 +1477,11 @@ void Lingo::setTheEntity(int entity, Datum &id, int field, Datum &d) {
 		movie->_timeOutKeyDown = d.asInt();
 		break;
 	case kTheTimeoutLapsed:
-		// timeOutLapsed can be set in D4, but can't in D3. see D3.1 interactivity manual p312 and D4 dictionary p296.
-		if (g_director->getVersion() >= 400 && (d.type == INT || d.type == FLOAT)) {
-			g_director->_tickBaseline = (int)g_director->getMacTicks() - d.asInt();
+		// The D3.1 interactivity interactivity manual p312 says timeoutLapsed can't be set,
+		// but it is lying. D2 and D3 Mac will let you set it, and games like ybr1 use it.
+		// See D4 dictionary p296.
+		if ((d.type == INT || d.type == FLOAT)) {
+			movie->_lastTimeOut = (int)g_director->getMacTicks() - d.asInt();
 		}
 		if (d.type != INT) {
 			warning("Lingo::setTheEntity() : Wrong DatumType %d for setting of Lingo Property timeOutLapsed", d.type);


Commit: d4e2e3d8a26c77dd0337c1d327bb6d073d13a455
    https://github.com/scummvm/scummvm/commit/d4e2e3d8a26c77dd0337c1d327bb6d073d13a455
Author: Scott Percival (code at moral.net.au)
Date: 2026-06-20T00:11:56+02:00

Commit Message:
DIRECTOR: XOBJ: Add loadData/saveData to WidgetXObj

Fixes save points in Yellow Brick Road.

Changed paths:
    engines/director/lingo/xlibs/w/widget.cpp


diff --git a/engines/director/lingo/xlibs/w/widget.cpp b/engines/director/lingo/xlibs/w/widget.cpp
index c32cb0954c3..ea57e1c9dd3 100644
--- a/engines/director/lingo/xlibs/w/widget.cpp
+++ b/engines/director/lingo/xlibs/w/widget.cpp
@@ -20,6 +20,7 @@
  */
 
 #include "common/macresman.h"
+#include "common/savefile.h"
 #include "common/system.h"
 
 #include "director/director.h"
@@ -174,8 +175,105 @@ void WidgetXObj::m_lipSync(int nargs) {
 	g_lingo->push(result);
 }
 
-XOBJSTUB(WidgetXObj::m_loadData, 0)
-XOBJSTUB(WidgetXObj::m_saveData, 0)
+void WidgetXObj::m_loadData(int nargs) {
+	uint32 result = 0;
+	if (Common::String("ybr1").equals(g_director->getGameId())) {
+		// I have no idea why they decided to write this in C instead of use Lingo
+		ARGNUMCHECK(0);
+		Common::SaveFileManager *saves = g_system->getSavefileManager();
+		Common::SeekableReadStream *stream = saves->openForLoading(savePrefix() + Common::String("YBR.INI"));
+		if (stream) {
+			Common::String line1 = stream->readLine();
+			Common::String line2 = stream->readLine();
+			if (line1.equals("[YBRDAT]") && line2.hasPrefix("Dat=")) {
+				Datum value(line2.substr(4, Common::String::npos));
+				result = value.asInt();
+
+				Datum batIsu("BatIsu");
+				batIsu.type = GLOBALREF;
+				g_lingo->varAssign(batIsu, result & 0x8000 ? 1 : 0);
+
+				Datum kKey("K_Key");
+				kKey.type = GLOBALREF;
+				g_lingo->varAssign(kKey, result & 0x4000 ? 1 : 0);
+
+				Datum bedSui("BedSui");
+				bedSui.type = GLOBALREF;
+				g_lingo->varAssign(bedSui, result & 0x2000 ? 1 : 0);
+
+				Datum warp("Warp");
+				warp.type = GLOBALREF;
+				g_lingo->varAssign(warp, result & 0x1000 ? 1 : 0);
+
+				Datum bat4("Bat4");
+				bat4.type = GLOBALREF;
+				g_lingo->varAssign(bat4, result & 0x800 ? 1 : 0);
+
+				result &= 0x7ff;
+			}
+		}
+
+	}
+	g_lingo->push((int)result);
+}
+
+void WidgetXObj::m_saveData(int nargs) {
+	if (Common::String("ybr1").equals(g_director->getGameId())) {
+		// I have no idea why they decided to write this in C instead of use Lingo
+		ARGNUMCHECK(1);
+		Datum place = g_lingo->pop();
+		TYPECHECK(place, INT);
+		uint32 flags = place.asInt();
+
+		Datum batIsu("BatIsu");
+		batIsu.type = GLOBALREF;
+		batIsu = g_lingo->varFetch(batIsu);
+		if (batIsu.asInt() > 0) {
+			flags |= 0x8000;
+		}
+
+		Datum kKey("K_Key");
+		kKey.type = GLOBALREF;
+		kKey = g_lingo->varFetch(kKey);
+		if (kKey.asInt() > 0) {
+			flags |= 0x4000;
+		}
+
+		Datum bedSui("BedSui");
+		bedSui.type = GLOBALREF;
+		bedSui = g_lingo->varFetch(bedSui);
+		if (bedSui.asInt() > 0) {
+			flags |= 0x2000;
+		}
+
+		Datum warp("Warp");
+		warp.type = GLOBALREF;
+		warp = g_lingo->varFetch(warp);
+		if (warp.asInt() > 0) {
+			flags |= 0x1000;
+		}
+
+		Datum bat4("Bat4");
+		bat4.type = GLOBALREF;
+		bat4 = g_lingo->varFetch(bat4);
+		if (bat4.asInt() > 0) {
+			flags |= 0x800;
+		}
+
+		Common::SaveFileManager *saves = g_system->getSavefileManager();
+		Common::SeekableWriteStream *stream = saves->openForSaving(savePrefix() + Common::String("YBR.INI"), false);
+		if (stream) {
+			Common::String buffer = Common::String::format("[YBRDAT]\r\nDat=%d", flags);
+			stream->writeString(buffer);
+			stream->finalize();
+			delete stream;
+		} else {
+			warning("WidgetXObj::m_saveData: unable to open YBR.INI for writing");
+		}
+	}
+	g_lingo->push(0);
+}
+
 XOBJSTUB(WidgetXObj::m_hideCursor, 0)
 XOBJSTUB(WidgetXObj::m_showCursor, 0)
 XOBJSTUB(WidgetXObj::m_mul, 0)


Commit: cb04714a079740f2b1dc223340a01501e4512cf3
    https://github.com/scummvm/scummvm/commit/cb04714a079740f2b1dc223340a01501e4512cf3
Author: Scott Percival (code at moral.net.au)
Date: 2026-06-20T00:11:56+02:00

Commit Message:
DIRECTOR: LINGO: Add inline kLPPTrimGarbage handling to compiler

D2 and D3 have a pretty glaring design flaw. If a syntax error is
encountered on a line of script, the compiler will keep everything
up until the first symbol it doesn't understand, then continue to
the next line.

A reasonable person might think "that can't be right, wouldn't every
D2 and D3 game be a mess of typos???". Yep. Also there's no bytecode
version, so a non-compiling script is a big problem.

A previous attempt to fix this problem was to use the text preprocessor
to strip the end of the script, using the offset returned by the parser
error-handling function, then retry the whole thing. This sort of worked
for b_value, which takes a one-liner expression, but didn't work for
movie or score scripts with more than one line.

This new attempt uses Bison's special "error" matching token; if the
regular parser attempt fails with a syntax error, the partial match will
be sent to the same handler but with "error" sandwiched just before the
new line token. If the trim garbage flag is switched on, Bison will
keep the AST and continue to the next line, otherwise it will error out
as usual.

90+% of the text script patch overrides can probably be removed.

Fixes (amongst many things) using a weapon in Yellow Brick Road.

Changed paths:
    engines/director/lingo/lingo-builtins.cpp
    engines/director/lingo/lingo-codegen.cpp
    engines/director/lingo/lingo-codegen.h
    engines/director/lingo/lingo-gr.cpp
    engines/director/lingo/lingo-gr.h
    engines/director/lingo/lingo-gr.y


diff --git a/engines/director/lingo/lingo-builtins.cpp b/engines/director/lingo/lingo-builtins.cpp
index 6b97ddeba8d..ef0815fbd16 100644
--- a/engines/director/lingo/lingo-builtins.cpp
+++ b/engines/director/lingo/lingo-builtins.cpp
@@ -1976,7 +1976,10 @@ void LB::b_do(int nargs) {
 	if (code.empty())
 		return;
 
-	ScriptContext *sc = g_lingo->_compiler->compileAnonymous(code);
+	uint32 flags = 0;
+	flags |= g_director->getVersion() < 400 ? kLPPTrimGarbage : 0;
+
+	ScriptContext *sc = g_lingo->_compiler->compileAnonymous(code, flags);
 	if (!sc) {
 		warning("b_do(): compilation failed, ignoring");
 
diff --git a/engines/director/lingo/lingo-codegen.cpp b/engines/director/lingo/lingo-codegen.cpp
index 601defdc412..11006c29b8f 100644
--- a/engines/director/lingo/lingo-codegen.cpp
+++ b/engines/director/lingo/lingo-codegen.cpp
@@ -52,6 +52,7 @@
 #include "director/lingo/lingo-codegen.h"
 #include "director/lingo/lingo-object.h"
 #include "director/lingo/lingo-the.h"
+#include "director/types.h"
 
 namespace Director {
 
@@ -119,6 +120,7 @@ LingoCompiler::LingoCompiler() {
 	_refMode = false;
 
 	_hadError = false;
+	_trimGarbage = false;
 }
 
 ScriptContext *LingoCompiler::compileAnonymous(const Common::U32String &code, uint32 preprocFlags) {
@@ -138,6 +140,7 @@ ScriptContext *LingoCompiler::compileLingo(const Common::U32String &code, LingoA
 	_methodVars = new VarTypeHash;
 	_linenumber = _colnumber = 1;
 	_hadError = false;
+	_trimGarbage = (bool)(preprocFlags & kLPPTrimGarbage);
 
 	// Preprocess the code for ease of the parser
 	Common::U32String codePrep = codePreprocessor(code, archive, type, id, preprocFlags);
@@ -152,7 +155,7 @@ ScriptContext *LingoCompiler::compileLingo(const Common::U32String &code, LingoA
 	parse(utf8Code);
 	// If it doesn't work, and we have kLPPTrimGarbage enabled,
 	// have another try with the input trimmed to the last valid character.
-	if (!_assemblyAST && (preprocFlags & kLPPTrimGarbage)) {
+	if (!_assemblyAST && _trimGarbage) {
 		delete _assemblyContext;
 		delete _currentAssembly;
 		delete _methodVars;
diff --git a/engines/director/lingo/lingo-codegen.h b/engines/director/lingo/lingo-codegen.h
index 88bd91ba042..8cb407d96a4 100644
--- a/engines/director/lingo/lingo-codegen.h
+++ b/engines/director/lingo/lingo-codegen.h
@@ -58,6 +58,7 @@ public:
 	Common::SharedPtr<Node> _assemblyAST;
 	int32 _assemblyId;
 	ScriptData *_currentAssembly;
+	bool _trimGarbage;
 	bool _indef;
 	uint _linenumber;
 	uint _colnumber;
diff --git a/engines/director/lingo/lingo-gr.cpp b/engines/director/lingo/lingo-gr.cpp
index dc0d6653a89..ad6ce54c92c 100644
--- a/engines/director/lingo/lingo-gr.cpp
+++ b/engines/director/lingo/lingo-gr.cpp
@@ -96,6 +96,20 @@ extern int yylex();
 
 using namespace Director;
 
+// if we have the trim garbage flag on, and we encounter
+// an unwelcome surprise in an expression, use everything up until
+// the first failing token.
+#define TRIM_GARBAGE(src, target) \
+	if (g_lingo->_compiler->_trimGarbage) { \
+		warning("Trimming garbage and continuing.");	\
+		src = target;	\
+		yyerrok; \
+	} else { \
+		src = nullptr; \
+		YYABORT; \
+	}	\
+
+
 static void yyerror(const char *s) {
 	LingoCompiler *compiler = g_lingo->_compiler;
 	compiler->_hadError = true;
@@ -132,7 +146,7 @@ static void checkEnd(Common::String *token, Common::String *expect, bool require
 }
 
 
-#line 136 "engines/director/lingo/lingo-gr.cpp"
+#line 150 "engines/director/lingo/lingo-gr.cpp"
 
 # ifndef YY_CAST
 #  ifdef __cplusplus
@@ -658,18 +672,18 @@ union yyalloc
 #endif /* !YYCOPY_NEEDED */
 
 /* YYFINAL -- State number of the termination state.  */
-#define YYFINAL  195
+#define YYFINAL  197
 /* YYLAST -- Last index in YYTABLE.  */
-#define YYLAST   4723
+#define YYLAST   4834
 
 /* YYNTOKENS -- Number of terminals.  */
 #define YYNTOKENS  108
 /* YYNNTS -- Number of nonterminals.  */
 #define YYNNTS  65
 /* YYNRULES -- Number of rules.  */
-#define YYNRULES  337
+#define YYNRULES  369
 /* YYNSTATES -- Number of states.  */
-#define YYNSTATES  635
+#define YYNSTATES  721
 
 /* YYMAXUTOK -- Last valid token kind.  */
 #define YYMAXUTOK   348
@@ -727,40 +741,43 @@ static const yytype_int8 yytranslate[] =
 /* YYRLINE[YYN] -- Source line where rule number YYN was defined.  */
 static const yytype_int16 yyrline[] =
 {
-       0,   211,   211,   213,   219,   226,   227,   228,   229,   230,
-     231,   260,   264,   266,   268,   269,   272,   278,   285,   286,
-     287,   292,   296,   300,   301,   302,   309,   310,   311,   312,
-     313,   314,   315,   316,   317,   318,   319,   320,   321,   322,
-     323,   324,   325,   326,   327,   328,   329,   330,   331,   332,
-     333,   334,   335,   336,   337,   338,   339,   340,   341,   342,
-     343,   344,   345,   346,   347,   348,   349,   350,   351,   352,
-     353,   354,   355,   356,   357,   360,   361,   362,   363,   364,
-     365,   366,   367,   368,   369,   370,   371,   372,   373,   374,
-     375,   376,   377,   378,   379,   382,   383,   384,   387,   391,
-     401,   402,   405,   406,   407,   408,   409,   410,   413,   414,
-     415,   418,   419,   420,   421,   422,   423,   424,   425,   430,
-     431,   432,   433,   434,   435,   436,   437,   440,   443,   448,
-     452,   458,   463,   466,   471,   475,   481,   488,   488,   494,
-     499,   505,   511,   517,   525,   526,   527,   528,   531,   531,
-     533,   534,   535,   538,   542,   546,   552,   556,   560,   564,
-     571,   573,   575,   577,   579,   583,   587,   591,   593,   594,
-     598,   604,   611,   612,   615,   616,   620,   626,   633,   634,
-     640,   641,   642,   643,   644,   645,   646,   647,   653,   658,
-     659,   660,   661,   662,   663,   666,   668,   669,   672,   673,
-     676,   677,   678,   679,   680,   682,   684,   686,   688,   690,
-     692,   694,   696,   699,   700,   701,   702,   705,   706,   709,
-     714,   717,   722,   728,   729,   730,   731,   732,   735,   736,
-     737,   738,   739,   742,   744,   745,   746,   747,   748,   749,
-     750,   751,   752,   753,   757,   758,   759,   760,   761,   762,
-     763,   764,   767,   767,   769,   770,   773,   774,   775,   776,
-     777,   780,   781,   782,   788,   792,   795,   800,   801,   802,
-     803,   804,   807,   808,   811,   812,   816,   817,   818,   819,
-     820,   821,   822,   823,   824,   825,   826,   827,   828,   829,
-     830,   831,   832,   833,   834,   841,   842,   843,   844,   845,
-     846,   847,   848,   849,   850,   851,   852,   853,   854,   855,
-     856,   857,   858,   859,   862,   863,   864,   865,   866,   867,
-     868,   869,   870,   871,   872,   873,   874,   875,   876,   877,
-     878,   879,   882,   883,   886,   887,   890,   894
+       0,   225,   225,   227,   233,   240,   241,   242,   243,   244,
+     245,   274,   275,   280,   281,   284,   285,   288,   289,   292,
+     298,   305,   306,   307,   312,   316,   320,   322,   326,   327,
+     328,   335,   336,   337,   338,   339,   340,   341,   342,   343,
+     344,   345,   346,   347,   348,   349,   350,   351,   352,   353,
+     354,   355,   356,   357,   358,   359,   360,   361,   362,   363,
+     364,   365,   366,   367,   368,   369,   370,   371,   372,   373,
+     374,   375,   376,   377,   378,   379,   380,   381,   382,   383,
+     386,   387,   388,   389,   390,   391,   392,   393,   394,   395,
+     396,   397,   398,   399,   400,   401,   402,   403,   404,   405,
+     408,   409,   410,   413,   417,   427,   428,   431,   432,   433,
+     434,   435,   436,   439,   440,   441,   444,   445,   446,   447,
+     448,   449,   450,   451,   452,   453,   454,   455,   456,   457,
+     458,   463,   464,   465,   466,   467,   468,   469,   470,   471,
+     472,   473,   474,   475,   476,   477,   480,   483,   488,   492,
+     498,   503,   506,   511,   515,   521,   528,   528,   534,   539,
+     545,   551,   557,   565,   566,   567,   568,   569,   570,   571,
+     572,   575,   575,   577,   578,   579,   580,   581,   582,   585,
+     589,   591,   595,   601,   605,   609,   613,   620,   622,   624,
+     626,   628,   630,   632,   634,   636,   640,   644,   646,   650,
+     652,   653,   657,   663,   670,   671,   674,   675,   679,   685,
+     692,   693,   699,   700,   701,   702,   703,   704,   705,   706,
+     712,   717,   718,   719,   720,   721,   722,   725,   727,   728,
+     731,   732,   735,   736,   737,   738,   739,   741,   743,   745,
+     747,   749,   751,   753,   755,   758,   759,   760,   761,   764,
+     765,   768,   773,   776,   781,   787,   788,   789,   790,   791,
+     794,   795,   796,   797,   798,   801,   803,   804,   805,   806,
+     807,   808,   809,   810,   811,   812,   816,   817,   818,   819,
+     820,   821,   822,   823,   826,   826,   828,   829,   832,   833,
+     834,   835,   836,   839,   840,   841,   847,   851,   854,   859,
+     860,   861,   862,   863,   866,   867,   870,   871,   875,   876,
+     877,   878,   879,   880,   881,   882,   883,   884,   885,   886,
+     887,   888,   889,   890,   891,   892,   893,   900,   901,   902,
+     903,   904,   905,   906,   907,   908,   909,   910,   911,   912,
+     913,   914,   915,   916,   917,   918,   921,   922,   923,   924,
+     925,   926,   927,   928,   929,   930,   931,   932,   933,   934,
+     935,   936,   937,   938,   941,   942,   945,   946,   949,   953
 };
 #endif
 
@@ -811,7 +828,7 @@ yysymbol_name (yysymbol_kind_t yysymbol)
 }
 #endif
 
-#define YYPACT_NINF (-534)
+#define YYPACT_NINF (-562)
 
 #define yypact_value_is_default(Yyn) \
   ((Yyn) == YYPACT_NINF)
@@ -825,70 +842,79 @@ yysymbol_name (yysymbol_kind_t yysymbol)
    STATE-NUM.  */
 static const yytype_int16 yypact[] =
 {
-    3033,  -534,  3586,  -534,  -534,  -534,  -534,  -534,  -534,   108,
-    -534,   -23,  -534,  3586,  2119,   108,  2221,  -534,  -534,  3586,
-    1507,  -534,     6,  -534,  -534,  2323,    32,  3671,  -534,   -59,
-    -534,  -534,  3586,  2323,  2119,  3586,  -534,  -534,  -534,  -534,
-    -534,  -534,  -534,  -534,  -534,  -534,  -534,  -534,  -534,  -534,
-    -534,  -534,  -534,  -534,  -534,  -534,  -534,  -534,  -534,  -534,
-    -534,  -534,  -534,  2221,  3586,  3586,   -56,  3925,  -534,    61,
-    3033,  -534,  -534,  -534,  -534,  2323,  -534,  -534,  -534,  -534,
-    -534,  -534,  -534,  -534,  -534,  -534,  -534,  -534,  -534,  -534,
-    -534,  -534,  -534,  -534,  -534,  -534,  -534,  -534,  -534,  -534,
-    -534,  -534,  -534,  -534,  -534,  -534,  -534,  -534,  -534,  -534,
-    -534,     9,  -534,  -534,  2425,  2425,  2425,  2425,  2221,  2221,
-    2221,  2221,     7,   -17,     5,  -534,  -534,    24,    47,  -534,
-    -534,  -534,  -534,  2425,  2425,  2425,  2425,  2425,  2425,  2221,
-    2221,  2527,  2221,  2221,  2221,  2221,  3756,  2221,  2527,  2527,
-    1609,   791,   -12,    39,    62,  -534,  -534,  -534,  -534,  -534,
-    -534,  -534,  -534,  -534,  -534,   895,  -534,    66,  2221,  4157,
-    3586,  -534,  4357,    73,    79,   997,  3586,  2221,  3586,  -534,
-    -534,    11,  -534,  -534,    80,    81,  1099,    82,    83,    88,
-    4122,    92,  3586,  -534,  -534,  -534,  -534,    97,  1201,  -534,
-    3586,  1711,  -534,  -534,  -534,  -534,  -534,   609,   682,  3974,
-    3996,   132,  -534,  -534,  -534,  3586,  -534,  -534,  1303,  4554,
-    -534,   -20,    38,    45,    56,    72,   132,    65,    71,  4541,
-    -534,  -534,  -534,  2935,  4179,    50,    90,    93,    94,     2,
-     -82,   -46,  -534,  4554,    67,   100,  1813,  -534,  -534,   176,
-    2221,  2221,  2221,  2221,  2221,  2221,  2221,  2221,  2221,  2221,
-    2221,  2221,  2221,  2221,  2221,  2221,  2221,  2221,  -534,  -534,
-    4273,  -534,  -534,  4213,  3125,   105,  -534,  -534,  -534,  3841,
-    3841,  3841,    29,  4370,   177,  -534,  -534,  2221,     4,  -534,
-    2221,  -534,  -534,  -534,  3925,  3217,  -534,   106,  -534,  -534,
-    -534,  4192,  2527,  2221,  2527,  2221,  2527,  2221,  2527,  2221,
-    -534,  -534,  -534,  -534,    43,  -534,   186,  4610,  -534,  -534,
-    -534,  -534,  -534,  -534,  -534,  -534,  -534,  -534,  1405,  2629,
-    2527,  2527,  4286,  1915,  -534,  2221,  2221,  2221,  2221,  -534,
-    2221,  2731,  -534,  -534,  2221,  -534,  2935,   115,  2221,   -11,
-     -11,   -11,   -11,   363,   363,  -534,     1,   -11,   -11,   -11,
-     -11,     1,   -19,   -19,  -534,  -534,   115,  2221,  2221,  2221,
-    2221,  2221,  2221,  2221,  2221,  2221,  2221,  2221,  2221,  2221,
-    2221,  2221,  2221,  2221,  2221,  -534,  3493,   201,  3217,     7,
-    -534,   125,  -534,   126,   134,  2221,  2221,  3217,  2833,  4383,
-    3586,  3586,  -534,  -534,  -534,     4,  -534,  4440,  -534,  -534,
-    -534,   133,  3217,  -534,  3217,  2017,  -534,  4009,  -534,  4031,
-    -534,  4066,  -534,  4088,  -534,  -534,  2527,  2221,    43,    43,
-      43,    43,  2527,  -534,  2527,    43,  -534,  -534,  2527,  2527,
-    -534,  -534,  -534,  -534,  -534,  -534,  2221,   135,  -534,   115,
-    4554,  4554,  4554,  4554,  4554,  -534,  4554,  4554,  4299,  2221,
-     137,  4554,  -534,   -11,   -11,   -11,   -11,   363,   363,  -534,
-       1,   -11,   -11,   -11,   -11,     1,   -19,   -19,  -534,  -534,
-     115,  -534,  -534,   -13,  3493,  -534,  3309,  -534,  -534,  -534,
-    -534,  4453,   539,   145,  2221,  2221,  2221,  2221,  -534,  -534,
-    -534,    19,  3586,  -534,  -534,   136,  -534,   229,  -534,   115,
-    2527,  2527,  2527,  2527,  -534,  4554,  2527,  2527,  2527,  2527,
-    -534,   211,   170,  -534,  -534,   115,  -534,   160,  2221,   161,
-    -534,  -534,  3401,   165,  -534,  -534,  3493,  -534,  3217,   230,
-    2221,   167,  -534,   525,  -534,  4101,   525,   525,  -534,   169,
-    -534,  3586,   168,  -534,  -534,  -534,  -534,  -534,  -534,  -534,
-    -534,   198,  2527,  -534,   171,  -534,   115,  -534,  3493,  -534,
-    -534,   183,   185,  2221,  4466,  -534,  2221,  2221,  2221,  2221,
-    2221,  2221,  2221,  2221,  2221,  2221,  2221,  2221,  2221,  2221,
-    2221,  2221,   209,  3217,    51,  -534,  2527,  -534,   179,   183,
-    -534,   191,  4523,  3217,   124,   124,   124,  4623,  4623,  -534,
-      31,   124,   124,   124,   124,    31,    -5,    -5,  -534,  -534,
-    2221,  -534,  -534,  -534,  -534,  -534,  -534,  3217,   195,   525,
-     196,   192,   199,  -534,  -534
+    3284,  -562,  3745,  -562,  -562,  -562,  -562,  -562,  -562,   242,
+    -562,    10,  -562,  3745,  2279,   242,  2381,  -562,  -562,  3745,
+     951,  -562,    17,  -562,  -562,  2483,    64,  3830,  -562,   -36,
+    -562,  -562,  3745,  2483,  2279,  3745,  -562,  -562,  -562,  -562,
+    -562,  -562,  -562,  -562,  -562,  -562,  -562,  -562,  -562,  -562,
+    -562,  -562,  -562,  -562,  -562,  -562,  -562,  -562,  -562,  -562,
+    -562,  -562,  -562,  2381,  3745,  3745,   -30,  4084,  -562,    78,
+    3284,  -562,  -562,  -562,  -562,  2483,  -562,  -562,  -562,  -562,
+    -562,  -562,  -562,  -562,  -562,  -562,  -562,  -562,  -562,  -562,
+    -562,  -562,  -562,  -562,  -562,  -562,  -562,  -562,  -562,  -562,
+    -562,  -562,  -562,  -562,  -562,  -562,  -562,  -562,  -562,  -562,
+    -562,   -10,  -562,  -562,  2585,  2585,  2585,  2585,  2381,  2381,
+    2381,  2381,     8,    15,   -12,    16,  -562,  -562,    20,    -4,
+    -562,  -562,  -562,  -562,  2585,  2585,  2585,  2585,  2585,  2585,
+    2381,  2381,  2687,  2381,  2381,  2381,  2381,  3915,  2381,  2687,
+    2687,  1769,  1053,    28,    23,    24,  -562,  -562,  -562,  -562,
+    -562,  -562,  -562,  -562,  -562,  -562,  1157,  -562,    26,  2381,
+    4387,  3745,    12,  -562,   629,    29,    36,  1259,  3745,  2381,
+    3745,  -562,  -562,    13,  -562,  -562,    38,    39,  1361,    40,
+      41,    44,   525,    47,  3745,  -562,  -562,  -562,  -562,    48,
+    1463,  -562,  3745,  1871,  -562,  -562,  -562,  -562,  -562,  4163,
+    4197,  4219,  4253,   109,    18,  -562,  -562,    46,  -562,    71,
+    -562,  3745,  -562,  -562,  1565,  4652,  -562,     3,    30,    31,
+      83,    86,   109,   130,   171,  4596,  -562,  -562,  -562,  3095,
+    4400,    22,   103,   105,   111,    99,   110,   132,  -562,  4652,
+     115,   114,  1973,   136,  -562,   142,  -562,   202,  2381,  2381,
+    2381,  2381,  2381,  2381,  2381,  2381,  2381,  2381,  2381,  2381,
+    2381,  2381,  2381,  2381,  2381,  2381,  -562,  -562,  4494,  -562,
+     143,  -562,  4481,  3191,    51,  -562,   144,  -562,   146,  -562,
+     147,  -562,  4000,  4000,  4000,    60,   665,   223,  -562,  -562,
+    2381,   154,    11,   157,  -562,  2381,   158,  -562,   159,  -562,
+     160,  -562,   161,  4084,  3376,   162,  -562,    52,   167,  -562,
+    -562,  -562,  4421,  2687,  2381,  2687,  2381,  2687,  2381,  2687,
+    2381,  -562,  -562,  -562,  -562,    37,  -562,  -562,  -562,  -562,
+     243,  4665,  -562,  -562,  -562,  -562,  -562,  -562,  -562,  -562,
+    -562,  -562,  1667,  2789,  2687,  2687,  4507,  2075,  -562,  2381,
+    2381,  2381,  2381,  -562,  2381,  2891,  -562,  -562,  2381,  -562,
+    3095,   185,  -562,  -562,  2381,   178,   178,   178,   178,  4127,
+    4127,  -562,    92,   178,   178,   178,   178,    92,   -24,   -24,
+    -562,  -562,   185,  2381,  2381,  2381,  2381,  2381,  2381,  2381,
+    2381,  2381,  2381,  2381,  2381,  2381,  2381,  2381,  2381,  2381,
+    2381,  -562,  -562,   172,  3652,   270,   189,  3376,  -562,  -562,
+    -562,     8,  -562,    54,  -562,    55,    57,  2381,  2381,   198,
+    3376,  2993,   700,    11,  3745,  3745,  -562,  -562,  -562,    11,
+    -562,  -562,  4578,  -562,  -562,  -562,  3376,  -562,  -562,  -562,
+     211,  3376,  -562,  -562,   206,  3376,  -562,  2177,  -562,  4275,
+    -562,  4309,  -562,  4331,  -562,  4365,  -562,  -562,  2687,  2381,
+      37,    37,    37,    37,  2687,  -562,  2687,    37,  -562,  -562,
+    2687,  2687,  -562,  -562,  -562,  -562,  -562,  -562,  2381,   213,
+    -562,   185,  4652,  4652,  4652,  4652,  4652,  -562,  4652,  4652,
+    4565,  2381,   214,  4652,  -562,   178,   178,   178,   178,  4127,
+    4127,  -562,    92,   178,   178,   178,   178,    92,   -24,   -24,
+    -562,  -562,   185,  3652,  -562,  -562,    43,  3652,  -562,  3468,
+    3376,  -562,   215,  -562,   218,  -562,   238,  -562,   766,  4141,
+    3376,   224,  2381,  2381,  2381,  2381,  -562,  -562,   239,  -562,
+    -562,    62,  3745,  -562,  -562,   249,   241,  -562,  3376,   334,
+    -562,   185,  2687,  2687,  2687,  2687,  -562,  4652,  2687,  2687,
+    2687,  2687,  -562,   313,   272,  -562,  -562,   185,  -562,   244,
+    2381,   245,  -562,  -562,   256,  3560,   246,  -562,  -562,  3652,
+    -562,  -562,  -562,  -562,  -562,   250,  3376,   315,  2381,   259,
+     252,  -562,  4678,  -562,   504,  4678,  4678,  -562,  -562,    58,
+     253,  -562,   346,  3745,   254,  -562,  -562,  -562,  -562,  -562,
+    -562,  -562,  -562,   284,  2687,  -562,   255,  -562,   185,  -562,
+    -562,  3652,  -562,  -562,   256,  3376,   269,  2381,   801,   260,
+    -562,  2381,  2381,  2381,  2381,  2381,  2381,  2381,  2381,  2381,
+    2381,  2381,  2381,  2381,  2381,  2381,  2381,   289,   264,  3376,
+    -562,  3745,    84,  -562,  2687,  -562,   262,   256,  -562,   276,
+     268,   834,   271,  3376,  -562,   212,   212,   212,  4734,  4734,
+    -562,   126,   212,   212,   212,   212,   126,    35,    35,  -562,
+    -562,  2381,  3376,  -562,    98,  -562,  -562,  -562,  -562,   273,
+    -562,   274,  3376,  3376,   279,  4678,  -562,  -562,  -562,  3376,
+     280,   282,   275,   290,   281,   283,  -562,   285,  -562,  -562,
+    -562
 };
 
 /* YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM.
@@ -896,94 +922,103 @@ static const yytype_int16 yypact[] =
    means the default is an error.  */
 static const yytype_int16 yydefact[] =
 {
-       0,    26,    23,    32,    39,    61,    70,    51,    33,    37,
-      38,     0,    40,    95,   127,    41,     0,    42,    44,     0,
-       0,    55,    56,    58,    59,   127,    60,     0,    68,     0,
-      71,    69,     0,   127,   127,    95,    30,    31,    34,    35,
-      45,    46,    48,    49,    73,    74,    27,    28,    29,    50,
-      63,    36,    47,    52,    53,    54,    57,    66,    67,    64,
-      65,    43,    72,     0,    95,     0,     0,    62,     5,     0,
-       2,     3,     6,     7,     8,   127,     9,   100,   102,   108,
-     109,   110,   103,   104,   105,   106,   107,    77,    37,    76,
-      78,    80,    81,    41,    82,    84,    91,    56,    90,    60,
-      92,    94,    79,    87,    88,    83,    93,    89,    86,    85,
-      62,     0,    75,    24,     0,     0,     0,     0,     0,     0,
-       0,     0,     0,     0,     0,   121,    98,     0,    96,   180,
-     181,   183,   182,    32,    39,    61,    70,    51,    33,    40,
-      55,     0,    34,    45,    48,    73,    66,    65,     0,     0,
-       0,   334,   195,     0,     0,   274,   190,   191,   192,   193,
-     226,   227,   194,   275,   276,   137,   277,     0,     0,     0,
-      95,   122,     0,     0,     0,   137,     0,     0,    66,   195,
-     198,     0,   199,   167,     0,     0,   137,     0,     0,     0,
-       0,     0,    95,   101,   126,     1,     4,     0,   137,    10,
-       0,     0,   201,   219,   200,   202,   203,     0,     0,     0,
-       0,     0,   124,   120,   150,    97,   217,   218,   139,   140,
-     184,    27,    28,    29,    50,    63,    47,    57,   223,     0,
-     272,   273,   132,   190,     0,   180,   181,   183,   182,     0,
-     195,     0,   264,   336,     0,   335,     0,   113,   114,    58,
+       0,    31,    28,    37,    44,    66,    75,    56,    38,    42,
+      43,     0,    45,   100,   146,    46,     0,    47,    49,     0,
+       0,    60,    61,    63,    64,   146,    65,     0,    73,     0,
+      76,    74,     0,   146,   146,   100,    35,    36,    39,    40,
+      50,    51,    53,    54,    78,    79,    32,    33,    34,    55,
+      68,    41,    52,    57,    58,    59,    62,    71,    72,    69,
+      70,    48,    77,     0,   100,     0,     0,    67,     5,     0,
+       2,     3,     6,     7,     8,   146,     9,   105,   107,   113,
+     114,   115,   108,   109,   110,   111,   112,    82,    42,    81,
+      83,    85,    86,    46,    87,    89,    96,    61,    95,    65,
+      97,    99,    84,    92,    93,    88,    98,    94,    91,    90,
+      67,     0,    80,    29,     0,     0,     0,     0,     0,     0,
+       0,     0,     0,     0,     0,     0,   135,   103,     0,   101,
+     212,   213,   215,   214,    37,    44,    66,    75,    56,    38,
+      45,    60,     0,    39,    50,    53,    78,    71,    70,     0,
+       0,     0,   366,   227,     0,     0,   306,   222,   223,   224,
+     225,   258,   259,   226,   307,   308,   156,   309,     0,     0,
+       0,   100,     0,   137,     0,     0,     0,   156,     0,     0,
+      71,   227,   230,     0,   231,   199,     0,     0,   156,     0,
+       0,     0,     0,     0,   100,   106,   145,     1,     4,     0,
+     156,    10,     0,     0,   233,   251,   232,   234,   235,     0,
+       0,     0,     0,     0,     0,   141,   136,     0,   133,     0,
+     173,   102,   249,   250,   158,   159,   216,    32,    33,    34,
+      55,    68,    52,    62,   255,     0,   304,   305,   151,   222,
+       0,   212,   213,   215,   214,     0,   227,     0,   296,   368,
+       0,   367,     0,     0,   120,     0,   122,    63,     0,     0,
        0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
-       0,     0,     0,     0,     0,     0,     0,   138,   128,   295,
-     137,   296,   125,     0,     0,     0,   123,   119,   112,    44,
-      30,    31,     0,     0,   254,   148,   149,     0,    14,   117,
-      71,   115,   116,   152,     0,   168,   151,     0,   111,    25,
-     220,     0,     0,     0,     0,     0,     0,     0,     0,     0,
-     213,   215,   216,   214,     0,    99,    58,   143,   234,   235,
-     236,   237,   238,   239,   240,   241,   242,   243,     0,     0,
-       0,     0,   137,     0,   189,     0,     0,     0,     0,   262,
-       0,     0,   263,   261,     0,   185,   190,   137,     0,   287,
-     288,   285,   286,   289,   290,   282,   292,   293,   294,   284,
-     283,   291,   278,   279,   280,   281,   137,     0,     0,     0,
+       0,     0,     0,     0,     0,   157,   147,   327,   156,   328,
+       0,   143,     0,     0,     0,   138,     0,   139,     0,   131,
+       0,   118,    49,    35,    36,     0,     0,   286,   171,   172,
+       0,     0,    17,     0,   128,    76,     0,   124,     0,   126,
+       0,   177,     0,     0,   200,     0,   175,     0,     0,   116,
+      30,   252,     0,     0,     0,     0,     0,     0,     0,     0,
+       0,   245,   247,   248,   246,     0,   142,   134,   174,   104,
+      63,   162,   266,   267,   268,   269,   270,   271,   272,   273,
+     274,   275,     0,     0,     0,     0,   156,     0,   221,     0,
+       0,     0,     0,   294,     0,     0,   295,   293,     0,   217,
+     222,   156,   121,   123,     0,   319,   320,   317,   318,   321,
+     322,   314,   324,   325,   326,   316,   315,   323,   310,   311,
+     312,   313,   156,     0,     0,     0,     0,     0,     0,     0,
        0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
-       0,     0,     0,     0,   138,   130,   174,   153,   168,    66,
-     196,     0,   197,     0,     0,     0,     0,   168,     0,     0,
-      23,     0,    18,    19,    12,    15,    16,     0,   165,   172,
-     173,     0,   169,   170,   168,     0,   204,     0,   208,     0,
-     210,     0,   206,     0,   252,   253,     0,     0,    35,    46,
-      49,    74,    52,   249,    53,    54,   250,   251,    64,    65,
-     225,   229,   228,   224,   332,   333,   138,     0,   133,   137,
-     270,   271,   269,   267,   268,   265,   266,   337,   137,   138,
-       0,   142,   129,   306,   307,   304,   305,   308,   309,   301,
-     311,   312,   313,   303,   302,   310,   297,   298,   299,   300,
-     137,   178,   179,   159,   175,   176,     0,    11,   144,   145,
-     146,     0,     0,     0,    52,    53,    64,    65,   255,   256,
-     147,     0,    95,    17,   118,     0,   171,    22,   221,   137,
-       0,     0,     0,     0,   212,   141,     0,     0,     0,     0,
-     233,     0,     0,   231,   232,   137,   135,     0,   138,     0,
-     186,   131,     0,     0,   154,   177,   174,   155,   168,     0,
-       0,     0,   314,   257,   315,     0,   259,   260,    20,     0,
-     166,    23,     0,   205,   209,   211,   207,   244,   246,   247,
-     245,     0,     0,   248,     0,   134,   137,   187,   174,   157,
-     160,   159,     0,     0,     0,   161,     0,     0,     0,     0,
-       0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
-       0,     0,     0,   168,     0,   222,     0,   136,     0,   159,
-     156,     0,     0,   168,   324,   325,   323,   326,   327,   320,
-     329,   330,   331,   322,   321,   328,   316,   317,   318,   319,
-       0,    13,    21,   230,   188,   158,   164,   168,     0,   258,
-       0,     0,     0,   162,   163
+     157,   149,   144,     0,   206,   179,     0,   200,   140,   132,
+     119,    71,   228,     0,   229,     0,     0,     0,     0,     0,
+     200,     0,     0,    17,    28,     0,    21,    22,    13,    18,
+      19,   129,     0,   125,   127,   178,   200,   196,   204,   205,
+       0,   201,   202,   176,     0,   200,   117,     0,   236,     0,
+     240,     0,   242,     0,   238,     0,   284,   285,     0,     0,
+      40,    51,    54,    79,    57,   281,    58,    59,   282,   283,
+      69,    70,   257,   261,   260,   256,   364,   365,   157,     0,
+     152,   156,   302,   303,   301,   299,   300,   297,   298,   369,
+     156,   157,     0,   161,   148,   338,   339,   336,   337,   340,
+     341,   333,   343,   344,   345,   335,   334,   342,   329,   330,
+     331,   332,   156,   206,   210,   211,   186,   207,   208,     0,
+     200,    11,     0,   163,     0,   165,     0,   167,     0,     0,
+     200,     0,    57,    58,    69,    70,   287,   288,     0,   169,
+      14,     0,   100,    20,   130,     0,     0,   203,   200,    26,
+     253,   156,     0,     0,     0,     0,   244,   160,     0,     0,
+       0,     0,   265,     0,     0,   263,   264,   156,   154,     0,
+     157,     0,   218,   150,   186,     0,     0,   180,   209,   206,
+     182,    12,   164,   166,   168,     0,   200,     0,     0,     0,
+       0,   346,   289,   347,     0,   291,   292,   170,    23,     0,
+       0,   197,    27,    28,     0,   237,   241,   243,   239,   276,
+     278,   279,   277,     0,     0,   280,     0,   153,   156,   219,
+     181,   206,   184,   187,   186,   200,     0,     0,     0,     0,
+     188,     0,     0,     0,     0,     0,     0,     0,     0,     0,
+       0,     0,     0,     0,     0,     0,     0,     0,     0,   200,
+     198,    28,     0,   254,     0,   155,     0,   186,   183,     0,
+       0,     0,     0,   200,   189,   356,   357,   355,   358,   359,
+     352,   361,   362,   363,   354,   353,   360,   348,   349,   350,
+     351,     0,   200,    15,     0,    24,   262,   220,   185,     0,
+     194,     0,   200,   200,     0,   290,    16,    25,   195,   200,
+       0,     0,     0,     0,     0,     0,   190,     0,   192,   191,
+     193
 };
 
 /* YYPGOTO[NTERM-NUM].  */
 static const yytype_int16 yypgoto[] =
 {
-    -534,  -534,  -534,   226,  -534,  -534,  -534,  -534,  -534,  -103,
-    -534,  -396,     0,    -1,   -28,  -534,     3,  -380,   -65,  -534,
-     -10,  -240,   270,  -534,  -534,  -534,  -534,  -534,  -533,  -534,
-    -534,  -534,  -210,  -534,  -107,  -519,  -534,  -178,  -136,   -18,
-    -134,  -534,    37,  -534,  -534,    96,  -534,   -16,  -208,  -534,
-    -534,  -270,  -534,  -534,  -534,  -534,   -22,  -534,   174,    -6,
-    -198,   -69,  -143,  -534,  -230
+    -562,  -562,  -562,   318,  -562,  -562,  -562,   -50,  -562,   -54,
+    -562,  -426,     0,    -1,   -28,  -562,     2,  -409,   -64,  -562,
+      61,  -272,   356,  -562,  -562,  -562,  -562,  -562,  -561,  -562,
+    -562,  -562,  -408,  -562,   -59,  -508,  -562,  -133,  -131,   -23,
+     -53,  -562,     4,  -562,  -562,    59,  -562,    42,  -178,  -562,
+    -562,  -191,  -562,  -562,  -562,  -562,    50,  -562,   263,    63,
+    -210,    -2,  -134,  -562,  -242
 };
 
 /* YYDEFGOTO[NTERM-NUM].  */
 static const yytype_int16 yydefgoto[] =
 {
-       0,    69,    70,    71,    72,    73,   403,   404,   405,   406,
-      74,   111,   112,   152,   127,   128,   410,    77,    78,    79,
-     153,   268,   154,    80,   287,    81,    82,    83,   534,    84,
-      85,    86,   411,   412,   413,   483,   484,   485,   155,   156,
-     391,   181,   157,   314,   158,   202,   159,   440,   441,   160,
-     161,   426,   182,   498,   162,   241,   242,   163,   164,   243,
-     270,   543,   166,   244,   245
+       0,    69,    70,    71,    72,    73,   437,   438,   439,   440,
+      74,   111,   112,   153,   128,   129,   449,    77,    78,    79,
+     154,   276,   155,    80,   300,    81,    82,    83,   587,    84,
+      85,    86,   450,   451,   452,   526,   527,   528,   156,   157,
+     423,   183,   158,   335,   159,   204,   160,   482,   483,   161,
+     162,   468,   184,   546,   163,   247,   248,   164,   165,   249,
+     278,   602,   167,   250,   251
 };
 
 /* YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM.  If
@@ -991,348 +1026,327 @@ static const yytype_int16 yydefgoto[] =
    number is the opposite.  If YYTABLE_NINF, syntax error.  */
 static const yytype_int16 yytable[] =
 {
-      75,   113,   194,    76,   501,   532,   482,   189,   165,   180,
-     169,   124,   126,   400,   172,   174,   347,   571,   170,   175,
-     317,   246,   271,   185,   187,   340,   179,   186,   165,   269,
-     385,   184,   271,   256,   126,   332,   191,   366,   600,   269,
-     173,   256,   183,   271,   257,   193,   123,   581,   285,   599,
-     269,   318,   167,   256,   395,   271,   341,   190,   286,   319,
-     342,   195,   269,   126,   192,   197,   625,    75,   424,   198,
-      75,   176,   177,    76,   425,   271,   396,   533,   125,   211,
-     265,   266,   269,   581,   212,   262,   263,   264,   265,   266,
-     271,   246,   447,   401,   590,   591,   328,   269,   263,   264,
-     265,   266,   329,   449,   482,   402,   213,   460,   339,   320,
-     199,   200,   207,   208,   209,   210,   322,   321,   114,   115,
-     548,   200,   116,   117,   323,   214,   462,   324,   588,   589,
-     590,   591,   233,   218,   219,   325,   207,   208,   209,   210,
-     247,   229,   275,   326,   234,   228,   393,   394,   458,   215,
-     240,   327,   622,   200,   480,   594,   482,   335,   516,   517,
-     518,   519,   273,   248,   297,   522,   118,   272,   119,   126,
-     120,   283,   121,   343,   277,   282,   581,   284,   487,   582,
-     278,   288,   289,   291,   292,   509,   122,   493,   482,   293,
-     310,   126,   311,   296,   312,   301,   313,   336,   298,   299,
-     337,   338,   344,   271,   507,   348,   388,   414,   398,   527,
-     269,   204,   205,   206,   315,   427,   525,   459,   529,   486,
-     587,   588,   589,   590,   591,   505,   488,   489,   346,   408,
-     204,   216,   217,   205,   206,   490,   541,   550,   551,   526,
-     531,   530,   561,   562,   349,   350,   351,   352,   353,   354,
-     355,   356,   357,   358,   359,   360,   361,   362,   363,   364,
-     365,   390,   390,   390,   565,   567,   570,   573,   575,   552,
-     593,   596,   595,   533,    75,   597,   601,   387,   179,   179,
-     179,   399,   620,   624,   407,   564,   631,   632,   203,   203,
-     203,   203,   626,   633,    75,    75,   196,   417,   566,   419,
-     634,   421,   503,   423,   188,   506,   535,   203,   203,   203,
-     203,   203,   203,   443,   563,   220,   392,   392,   392,   455,
-       0,     0,   230,   231,     0,     0,   598,     0,   572,   450,
-     451,   452,   453,     0,   454,   456,     0,     0,   457,     0,
-     240,     0,   461,     0,     0,     0,     0,     0,     0,     0,
-       0,   544,   544,   544,   544,     0,     0,     0,     0,     0,
-       0,   463,   464,   465,   466,   467,   468,   469,   470,   471,
-     472,   473,   474,   475,   476,   477,   478,   479,     0,     0,
-       0,     0,     0,   621,     0,     0,    75,     0,    75,   491,
-     492,     0,     0,   628,     0,     0,     0,    75,     0,   113,
-     502,     0,     0,     0,     0,     0,     0,     0,   250,   251,
-     252,   253,    75,     0,    75,   256,     0,   630,   257,   258,
-     259,   515,     0,     0,     0,     0,   545,   546,   547,     0,
-       0,     0,     0,   544,   544,   544,   544,   544,   544,   544,
-     544,   544,   544,   544,   544,   544,   544,   544,   544,     0,
-       0,     0,     0,   457,     0,     0,     0,   260,   261,   262,
-     263,   264,   265,   266,     0,     0,     0,     0,     0,     0,
-       0,     0,     0,     0,   549,     0,   416,   544,   418,     0,
-     420,     0,   422,     0,    75,     0,    75,     0,     0,   537,
+      75,   113,    76,   196,   182,   525,   411,   191,   551,   531,
+     371,   124,   127,   123,   341,   584,   214,   217,   171,   168,
+     434,   219,   541,   630,   253,   255,   181,   280,   264,   356,
+     288,   186,   279,   392,   127,   277,   193,   290,   555,   301,
+     303,   306,   308,   279,   125,   310,   277,   559,   315,   318,
+     298,   175,   416,   454,   279,   532,   534,   277,   536,   658,
+     299,   585,   466,   127,   194,   185,   279,    75,   467,   277,
+      75,   195,    76,   668,   342,   273,   274,   166,   197,   170,
+     213,   634,   343,   174,   489,   427,   176,   646,   177,   216,
+     279,   201,   202,   277,   187,   189,   188,   166,   221,   502,
+     435,   344,   346,   178,   179,   279,   698,   428,   277,   345,
+     347,   126,   436,   285,   525,   491,   215,   218,   525,   336,
+     504,   220,   591,   667,   254,   256,   192,   281,   239,   359,
+     289,   252,   599,   586,   655,   656,   199,   291,   200,   302,
+     304,   307,   309,   284,   264,   311,   234,   337,   316,   319,
+     612,   246,   417,   455,   348,   533,   535,   350,   537,   659,
+     500,   352,   349,   608,   202,   351,   317,   331,   522,   332,
+     127,   333,   338,   334,   206,   207,   208,   295,   646,   297,
+     525,   209,   210,   211,   212,   695,   202,   662,   636,   271,
+     272,   273,   274,   127,   206,   222,   223,   207,   208,   707,
+     202,   320,   353,   224,   225,   363,   209,   210,   211,   212,
+     360,   235,   361,   252,   240,   561,   368,   364,   362,   579,
+     339,   367,   525,   653,   654,   655,   656,   669,   581,   370,
+     264,   374,   282,   265,   365,   694,   279,   372,   366,   277,
+     425,   426,   296,   373,   412,   418,   577,   419,   420,   447,
+     583,   693,   114,   115,   431,   433,   116,   117,   441,   443,
+     444,   445,   446,   453,   646,   704,   322,   647,   456,   422,
+     422,   422,   469,   523,   270,   271,   272,   273,   274,   568,
+     569,   570,   571,    75,   706,   415,   574,   501,   529,   614,
+     530,   181,   181,   181,   710,   711,   424,   424,   424,   540,
+     118,   713,   119,   556,   120,   626,   121,   558,   652,   653,
+     654,   655,   656,    75,    75,   600,   592,   578,   582,   593,
+     122,   375,   376,   377,   378,   379,   380,   381,   382,   383,
+     384,   385,   386,   387,   388,   389,   390,   391,   628,   594,
+     607,   610,   611,   613,   623,   624,   586,   633,   627,   629,
+     639,   635,   637,   640,   660,   661,   666,   664,   663,   665,
+     670,   674,   691,   432,   246,   692,   697,   699,   442,   700,
+     712,   714,   703,   715,   708,   709,   716,   205,   205,   205,
+     205,   717,   718,   550,   719,   553,   720,   459,   198,   461,
+     190,   463,   557,   465,   588,   485,   625,   205,   205,   205,
+     205,   205,   205,     0,     0,   226,     0,     0,   603,   603,
+     603,   603,   236,   237,    75,   497,     0,    75,     0,     0,
+       0,     0,   492,   493,   494,   495,     0,   496,   498,     0,
+      75,   499,     0,   113,   552,     0,     0,   503,     0,     0,
+       0,     0,     0,     0,     0,     0,    75,     0,     0,     0,
+       0,    75,     0,     0,     0,    75,   505,   506,   507,   508,
+     509,   510,   511,   512,   513,   514,   515,   516,   517,   518,
+     519,   520,   521,     0,     0,     0,     0,     0,     0,     0,
+       0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
+     538,   539,     0,     0,     0,     0,     0,     0,     0,     0,
+       0,     0,     0,     0,     0,     0,     0,   603,   603,   603,
+     603,   603,   603,   603,   603,   603,   603,   603,   603,   603,
+     603,   603,   603,    75,   609,     0,   312,    75,     0,    75,
+      75,   590,   567,     0,     0,   657,     0,     0,     0,     0,
+      75,   604,   605,   606,     0,     0,     0,     0,     0,   641,
+     642,   127,   643,   644,   645,     0,   646,   603,    75,   647,
+     648,   649,   313,     0,   499,     0,     0,     0,     0,     0,
+     258,   259,   260,   261,   262,   263,     0,   264,     0,     0,
+     265,   266,   267,     0,     0,    75,   458,   632,   460,    75,
+     462,     0,   464,     0,     0,     0,    75,     0,   650,   651,
+     652,   653,   654,   655,   656,     0,     0,     0,     0,     0,
+       0,     0,   113,     0,     0,   484,   484,   486,   487,   268,
+     269,   270,   271,   272,   273,   274,   314,     0,     0,     0,
+     286,    75,     0,     0,     0,    75,     0,     0,     0,   675,
+     676,   677,   678,   679,   680,   681,   682,   683,   684,   685,
+     686,   687,   688,   689,   690,     0,     0,     0,     0,    75,
+     113,   638,     0,     0,     0,     0,   429,     0,     0,     0,
+       0,     0,     0,    75,   258,   259,   260,   261,   262,   263,
+       0,   264,     0,     0,   265,   266,   267,     0,     0,   705,
+       0,     0,    75,     0,   547,     0,     0,     0,     0,     0,
+     671,   548,    75,    75,     0,     0,     0,     0,     0,    75,
+     258,   259,   260,   261,   262,   263,     0,   264,     0,     0,
+     265,   266,   267,   268,   269,   270,   271,   272,   273,   274,
+     287,   566,     0,     0,     0,     0,     0,   572,     0,   573,
+       0,     0,     0,   575,   576,   258,   259,   260,   261,   262,
+     263,     0,   264,     0,     0,   265,   266,   267,     0,   268,
+     269,   270,   271,   272,   273,   274,   430,   595,     0,     0,
        0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
-       0,   126,   442,   442,   444,   445,     0,   604,   605,   606,
-     607,   608,   609,   610,   611,   612,   613,   614,   615,   616,
-     617,   618,   619,     0,     0,     0,     0,     0,     0,     0,
-       0,     0,    75,     0,   574,   569,    75,     0,    75,     0,
        0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
-     113,   629,     0,     0,     0,     0,   539,     0,     0,     0,
-       0,     0,     0,     0,     0,     0,     0,   602,    75,     0,
-     576,   577,   499,   578,   579,   580,   540,   581,     0,     0,
-     582,   583,   584,     0,   250,   251,   252,   253,   254,   255,
-       0,   256,     0,    75,   257,   258,   259,     0,     0,     0,
-     514,     0,     0,    75,     0,     0,   520,     0,   521,     0,
-       0,     0,   523,   524,     0,     0,     0,     0,     0,   585,
-     586,   587,   588,   589,   590,   591,     0,    75,     0,     0,
-       0,     0,     0,   260,   261,   262,   263,   264,   265,   266,
-     302,     0,     0,     0,     0,     0,   303,     0,     0,     0,
-       0,     0,     0,     0,   250,   251,   252,   253,   254,   255,
-       0,   256,     0,     0,   257,   258,   259,     0,   542,   542,
-     542,   542,     0,     0,     0,     0,     0,     0,     0,     0,
-       0,     0,     0,     0,   553,   554,   555,   556,     0,     0,
-     557,   558,   559,   560,     0,     0,     0,     0,     0,     0,
-       0,     0,     0,   260,   261,   262,   263,   264,   265,   266,
-       0,     0,     0,   304,     0,     0,     0,     0,     0,   305,
-       0,     0,     0,     0,     0,     0,     0,   250,   251,   252,
-     253,   254,   255,     0,   256,     0,   520,   257,   258,   259,
+       0,     0,     0,     0,   268,   269,   270,   271,   272,   273,
+     274,   549,   672,     0,     0,   601,   601,   601,   601,     0,
+       0,   258,   259,   260,   261,   262,   263,     0,   264,     0,
+       0,   265,   266,   267,     0,   615,   616,   617,   618,     0,
+       0,   619,   620,   621,   622,   701,     0,     0,     0,     0,
+       0,     0,     0,     0,     0,     0,   258,   259,   260,   261,
+     262,   263,     0,   264,     0,     0,   265,   266,   267,     0,
+     268,   269,   270,   271,   272,   273,   274,   596,     0,     0,
+       0,     0,     0,     0,     0,     0,     0,     0,     0,   258,
+     259,   260,   261,   262,   263,     0,   264,   572,     0,   265,
+     266,   267,     0,     0,     0,   268,   269,   270,   271,   272,
+     273,   274,   673,     0,   601,   601,   601,   601,   601,   601,
+     601,   601,   601,   601,   601,   601,   601,   601,   601,   601,
+       0,     0,     0,     0,     0,     0,     0,   696,   268,   269,
+     270,   271,   272,   273,   274,   702,     0,     0,     0,     0,
        0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
-     542,   542,   542,   542,   542,   542,   542,   542,   542,   542,
-     542,   542,   542,   542,   542,   542,     0,     0,     0,     0,
-     623,     0,     0,     0,     0,     0,   260,   261,   262,   263,
-     264,   265,   266,     0,     0,     0,     0,     0,     0,     0,
-       0,     0,     0,     0,   542,   235,   236,     1,   237,   238,
-      87,   133,   134,   135,   136,   137,   138,    88,    10,    89,
+       0,     0,   172,     0,   601,   130,   131,     1,   132,   133,
+      87,   134,   135,   136,   137,   138,   139,    88,    10,    89,
       90,    12,    91,    92,    93,    94,    17,    18,    95,    96,
       21,    97,    23,    24,    98,    99,   100,   101,    28,     0,
       30,    31,   102,   103,   104,   105,     0,     0,     0,     0,
-       0,     0,   141,     0,    36,    37,     0,     0,     0,   142,
-      39,   143,    41,   144,    43,   145,    45,    46,    47,    48,
-      49,    50,    51,    52,    53,     0,    54,    55,    56,   146,
-      58,     0,     0,    59,   147,    61,    62,   106,   107,   108,
-     109,     0,     0,     0,   110,     0,     0,     0,   148,   149,
-       0,     0,     0,     0,   168,     0,   151,     0,   239,   129,
-     130,     1,   131,   132,    87,   133,   134,   135,   136,   137,
-     138,    88,    10,    89,    90,    12,    91,    92,    93,    94,
-      17,    18,    95,    96,    21,    97,   249,    24,    98,    99,
-     100,   101,    28,     0,    30,    31,   102,   103,   104,   105,
-     250,   251,   252,   253,   254,   255,   141,   256,    36,    37,
-     257,   258,   259,   142,    39,   143,    41,   144,    43,   145,
-      45,    46,    47,    48,    49,    50,    51,    52,    53,     0,
-      54,    55,    56,   146,    58,     0,     0,    59,   147,    61,
-      62,   106,   107,   108,   109,     0,     0,     0,   110,   260,
-     261,   262,   263,   264,   265,   266,     0,   267,   168,     0,
-     151,   129,   130,     1,   131,   132,    87,   133,   134,   135,
-     136,   137,   138,    88,    10,    89,    90,    12,    91,    92,
-      93,    94,    17,   279,    95,    96,    21,    97,    23,    24,
+       0,     0,   142,     0,    36,    37,     0,     0,     0,   143,
+      39,   144,    41,   145,    43,   146,    45,    46,    47,    48,
+      49,    50,    51,    52,    53,     0,    54,    55,    56,   147,
+      58,     0,     0,    59,   148,    61,    62,   106,   107,   108,
+     109,     0,     0,     0,   110,     0,     0,     0,   149,   150,
+       0,     0,   173,     0,   169,     0,   152,   241,   242,     1,
+     243,   244,    87,   134,   135,   136,   137,   138,   139,    88,
+      10,    89,    90,    12,    91,    92,    93,    94,    17,    18,
+      95,    96,    21,    97,    23,    24,    98,    99,   100,   101,
+      28,     0,    30,    31,   102,   103,   104,   105,     0,     0,
+       0,     0,     0,     0,   142,     0,    36,    37,     0,     0,
+       0,   143,    39,   144,    41,   145,    43,   146,    45,    46,
+      47,    48,    49,    50,    51,    52,    53,     0,    54,    55,
+      56,   147,    58,     0,     0,    59,   148,    61,    62,   106,
+     107,   108,   109,     0,     0,     0,   110,     0,     0,     0,
+     149,   150,     0,     0,     0,     0,   169,     0,   152,     0,
+     245,   130,   131,     1,   132,   133,    87,   134,   135,   136,
+     137,   138,   139,    88,    10,    89,    90,    12,    91,    92,
+      93,    94,    17,    18,    95,    96,    21,    97,   257,    24,
       98,    99,   100,   101,    28,     0,    30,    31,   102,   103,
-     104,   105,   250,   251,   252,   253,   254,   255,   141,   256,
-     280,   281,   257,   258,   259,   142,    39,   143,    41,   144,
-      43,   145,    45,    46,    47,    48,    49,    50,    51,    52,
-      53,     0,    54,    55,    56,   146,    58,     0,     0,    59,
-     147,    61,    62,   106,   107,   108,   109,     0,     0,     0,
-     110,   260,   261,   262,   263,   264,   265,   266,     0,   267,
-     168,     0,   151,   129,   130,     1,   131,   132,    87,   133,
-     134,   135,   136,   137,   138,    88,    10,    89,    90,    12,
-      91,    92,    93,    94,    17,    18,    95,    96,    21,    97,
-      23,    24,    98,    99,   100,   101,    28,     0,   290,    31,
-     102,   103,   104,   105,   250,   251,   252,   253,   254,   255,
-     141,   256,    36,    37,   257,   258,   259,   142,    39,   143,
-      41,   144,    43,   145,    45,    46,    47,    48,    49,    50,
-      51,    52,    53,     0,    54,    55,    56,   146,    58,     0,
-       0,    59,   147,    61,    62,   106,   107,   108,   109,     0,
-       0,     0,   110,   260,   261,   262,   263,   264,   265,   266,
-       0,   267,   168,     0,   151,   129,   130,     1,   131,   132,
-      87,   133,   134,   135,   136,   137,   138,    88,    10,    89,
+     104,   105,   258,   259,   260,   261,   262,   263,   142,   264,
+      36,    37,   265,   266,   267,   143,    39,   144,    41,   145,
+      43,   146,    45,    46,    47,    48,    49,    50,    51,    52,
+      53,     0,    54,    55,    56,   147,    58,     0,     0,    59,
+     148,    61,    62,   106,   107,   108,   109,     0,     0,     0,
+     110,   268,   269,   270,   271,   272,   273,   274,     0,   275,
+     169,     0,   152,   130,   131,     1,   132,   133,    87,   134,
+     135,   136,   137,   138,   139,    88,    10,    89,    90,    12,
+      91,    92,    93,    94,    17,   292,    95,    96,    21,    97,
+      23,    24,    98,    99,   100,   101,    28,     0,    30,    31,
+     102,   103,   104,   105,   258,   259,   260,   261,   262,   263,
+     142,   264,   293,   294,   265,   266,   267,   143,    39,   144,
+      41,   145,    43,   146,    45,    46,    47,    48,    49,    50,
+      51,    52,    53,     0,    54,    55,    56,   147,    58,     0,
+       0,    59,   148,    61,    62,   106,   107,   108,   109,     0,
+       0,     0,   110,   268,   269,   270,   271,   272,   273,   274,
+       0,   275,   169,     0,   152,   130,   131,     1,   132,   133,
+      87,   134,   135,   136,   137,   138,   139,    88,    10,    89,
       90,    12,    91,    92,    93,    94,    17,    18,    95,    96,
       21,    97,    23,    24,    98,    99,   100,   101,    28,     0,
-      30,    31,   102,   103,   104,   105,   250,   251,   252,   253,
-     254,   255,   141,   256,    36,    37,   257,   258,   259,   142,
-      39,   143,    41,   144,    43,   145,    45,    46,    47,    48,
-      49,    50,    51,    52,    53,     0,    54,    55,    56,   146,
-      58,     0,     0,    59,   147,    61,    62,   106,   107,   108,
-     109,     0,     0,     0,   110,   260,   261,   262,   263,   264,
-     265,   266,     0,   267,   168,     0,   151,   129,   130,     1,
-     131,   132,    87,   133,   134,   135,   136,   137,   138,    88,
+     305,    31,   102,   103,   104,   105,   258,   259,   260,   261,
+     262,   263,   142,   264,    36,    37,   265,   266,   267,   143,
+      39,   144,    41,   145,    43,   146,    45,    46,    47,    48,
+      49,    50,    51,    52,    53,     0,    54,    55,    56,   147,
+      58,     0,     0,    59,   148,    61,    62,   106,   107,   108,
+     109,     0,     0,     0,   110,   268,   269,   270,   271,   272,
+     273,   274,     0,   275,   169,     0,   152,   130,   131,     1,
+     132,   133,    87,   134,   135,   136,   137,   138,   139,    88,
       10,    89,    90,    12,    91,    92,    93,    94,    17,    18,
-      95,    96,    21,    97,   316,    24,    98,    99,   100,   101,
-      28,     0,    30,    31,   102,   103,   104,   105,   250,   251,
-     252,   253,   254,   255,   141,   256,    36,    37,   257,   258,
-     259,   142,    39,   143,    41,   144,    43,   145,    45,    46,
+      95,    96,    21,    97,    23,    24,    98,    99,   100,   101,
+      28,     0,    30,    31,   102,   103,   104,   105,   258,   259,
+     260,   261,   262,   263,   142,   264,    36,    37,   265,   266,
+     267,   143,    39,   144,    41,   145,    43,   146,    45,    46,
       47,    48,    49,    50,    51,    52,    53,     0,    54,    55,
-      56,   146,    58,     0,     0,    59,   147,    61,    62,   106,
-     107,   108,   109,     0,     0,     0,   110,   260,   261,   262,
-     263,   264,   265,   266,     0,     0,   168,     0,   151,   129,
-     130,     1,   131,   132,    87,   133,   134,   135,   136,   137,
-     138,    88,    10,    89,    90,    12,    91,    92,    93,    94,
-      17,    18,    95,    96,    21,    97,    23,    24,    98,    99,
+      56,   147,    58,     0,     0,    59,   148,    61,    62,   106,
+     107,   108,   109,     0,     0,     0,   110,   268,   269,   270,
+     271,   272,   273,   274,     0,   275,   169,     0,   152,   130,
+     131,     1,   132,   133,    87,   134,   135,   136,   137,   138,
+     139,    88,    10,    89,    90,    12,    91,    92,    93,    94,
+      17,    18,    95,    96,    21,    97,   340,    24,    98,    99,
      100,   101,    28,     0,    30,    31,   102,   103,   104,   105,
-       0,     0,     0,     0,     0,     0,   141,     0,    36,    37,
-       0,     0,     0,   142,   428,   143,   429,   144,   430,   145,
-     431,    46,    47,    48,    49,    50,    51,    52,   432,   433,
-     434,   435,    56,   146,    58,   436,   437,   438,   439,    61,
-      62,   106,   107,   108,   109,     0,     0,     0,   110,     0,
-       0,     0,   148,   149,     0,     0,     0,     0,   168,     0,
-     151,   129,   130,     1,   131,   132,    87,   133,   134,   135,
-     136,   137,   138,    88,    10,    89,    90,    12,    91,    92,
+     258,   259,   260,   261,   262,   263,   142,   264,    36,    37,
+     265,   266,   267,   143,    39,   144,    41,   145,    43,   146,
+      45,    46,    47,    48,    49,    50,    51,    52,    53,     0,
+      54,    55,    56,   147,    58,     0,     0,    59,   148,    61,
+      62,   106,   107,   108,   109,     0,     0,     0,   110,   268,
+     269,   270,   271,   272,   273,   274,     0,     0,   169,     0,
+     152,   130,   131,     1,   132,   133,    87,   134,   135,   136,
+     137,   138,   139,    88,    10,    89,    90,    12,    91,    92,
       93,    94,    17,    18,    95,    96,    21,    97,    23,    24,
       98,    99,   100,   101,    28,     0,    30,    31,   102,   103,
-     104,   105,     0,     0,     0,     0,     0,     0,   141,     0,
-      36,    37,     0,     0,     0,   142,    39,   143,    41,   144,
-      43,   145,    45,    46,    47,    48,    49,    50,    51,    52,
-      53,     0,    54,    55,    56,   146,    58,     0,     0,    59,
-     147,    61,    62,   106,   107,   108,   109,     0,     0,     0,
-     110,     0,     0,     0,   148,   149,     0,     0,   171,     0,
-     168,     0,   151,   129,   130,     1,   131,   132,    87,   133,
-     134,   135,   136,   137,   138,    88,    10,    89,    90,    12,
+     104,   105,     0,     0,     0,     0,     0,     0,   142,     0,
+      36,    37,     0,     0,     0,   143,   470,   144,   471,   145,
+     472,   146,   473,    46,    47,    48,    49,    50,    51,    52,
+     474,   475,   476,   477,    56,   147,    58,   478,   479,   480,
+     481,    61,    62,   106,   107,   108,   109,     0,     0,     0,
+     110,     0,     0,     0,   149,   150,     0,     0,     0,     0,
+     169,     0,   152,   130,   131,     1,   132,   133,    87,   134,
+     135,   136,   137,   138,   139,    88,    10,    89,    90,    12,
       91,    92,    93,    94,    17,    18,    95,    96,    21,    97,
       23,    24,    98,    99,   100,   101,    28,     0,    30,    31,
      102,   103,   104,   105,     0,     0,     0,     0,     0,     0,
-     141,     0,    36,    37,     0,     0,     0,   142,    39,   143,
-      41,   144,    43,   145,    45,    46,    47,    48,    49,    50,
-      51,    52,    53,     0,    54,    55,    56,   146,    58,     0,
-       0,    59,   147,    61,    62,   106,   107,   108,   109,     0,
-       0,     0,   110,     0,     0,     0,   148,   149,     0,     0,
-       0,     0,   168,   232,   151,   129,   130,     1,   131,   132,
-      87,   133,   134,   135,   136,   137,   138,    88,    10,    89,
+     142,     0,    36,    37,     0,     0,     0,   143,    39,   144,
+      41,   145,    43,   146,    45,    46,    47,    48,    49,    50,
+      51,    52,    53,     0,    54,    55,    56,   147,    58,     0,
+       0,    59,   148,    61,    62,   106,   107,   108,   109,     0,
+       0,     0,   110,     0,     0,     0,   149,   150,     0,     0,
+       0,     0,   169,   238,   152,   130,   131,     1,   132,   133,
+      87,   134,   135,   136,   137,   138,   139,    88,    10,    89,
       90,    12,    91,    92,    93,    94,    17,    18,    95,    96,
       21,    97,    23,    24,    98,    99,   100,   101,    28,     0,
       30,    31,   102,   103,   104,   105,     0,     0,     0,     0,
-       0,     0,   141,     0,    36,    37,     0,     0,     0,   142,
-      39,   143,    41,   144,    43,   145,    45,    46,    47,    48,
-      49,    50,    51,    52,    53,     0,    54,    55,    56,   146,
-      58,     0,     0,    59,   147,    61,    62,   106,   107,   108,
-     109,     0,     0,     0,   110,     0,     0,     0,   148,   149,
-       0,     0,     0,     0,   168,   300,   151,   129,   130,     1,
-     131,   132,    87,   133,   134,   135,   136,   137,   138,    88,
+       0,     0,   142,     0,    36,    37,     0,     0,     0,   143,
+      39,   144,    41,   145,    43,   146,    45,    46,    47,    48,
+      49,    50,    51,    52,    53,     0,    54,    55,    56,   147,
+      58,     0,     0,    59,   148,    61,    62,   106,   107,   108,
+     109,     0,     0,     0,   110,     0,     0,     0,   149,   150,
+       0,     0,     0,     0,   169,   321,   152,   130,   131,     1,
+     132,   133,    87,   134,   135,   136,   137,   138,   139,    88,
       10,    89,    90,    12,    91,    92,    93,    94,    17,    18,
       95,    96,    21,    97,    23,    24,    98,    99,   100,   101,
       28,     0,    30,    31,   102,   103,   104,   105,     0,     0,
-       0,     0,     0,     0,   141,     0,    36,    37,     0,     0,
-       0,   142,    39,   143,    41,   144,    43,   145,    45,    46,
+       0,     0,     0,     0,   142,     0,    36,    37,     0,     0,
+       0,   143,    39,   144,    41,   145,    43,   146,    45,    46,
       47,    48,    49,    50,    51,    52,    53,     0,    54,    55,
-      56,   146,    58,     0,     0,    59,   147,    61,    62,   106,
+      56,   147,    58,     0,     0,    59,   148,    61,    62,   106,
      107,   108,   109,     0,     0,     0,   110,     0,     0,     0,
-     148,   149,     0,     0,     0,     0,   168,   345,   151,   129,
-     130,     1,   131,   132,    87,   133,   134,   135,   136,   137,
-     138,    88,    10,    89,    90,    12,    91,    92,    93,    94,
+     149,   150,     0,     0,     0,     0,   169,   369,   152,   130,
+     131,     1,   132,   133,    87,   134,   135,   136,   137,   138,
+     139,    88,    10,    89,    90,    12,    91,    92,    93,    94,
       17,    18,    95,    96,    21,    97,    23,    24,    98,    99,
      100,   101,    28,     0,    30,    31,   102,   103,   104,   105,
-       0,     0,     0,     0,     0,     0,   141,     0,    36,    37,
-       0,     0,     0,   142,    39,   143,    41,   144,    43,   145,
+       0,     0,     0,     0,     0,     0,   142,     0,    36,    37,
+       0,     0,     0,   143,    39,   144,    41,   145,    43,   146,
       45,    46,    47,    48,    49,    50,    51,    52,    53,     0,
-      54,    55,    56,   146,    58,     0,     0,    59,   147,    61,
+      54,    55,    56,   147,    58,     0,     0,    59,   148,    61,
       62,   106,   107,   108,   109,     0,     0,     0,   110,     0,
-       0,     0,   148,   149,     0,     0,     0,     0,   168,   448,
-     151,   129,   130,     1,   131,   132,    87,   133,   134,   135,
-     136,   137,   138,    88,    10,    89,    90,    12,    91,    92,
+       0,     0,   149,   150,     0,     0,     0,     0,   169,   490,
+     152,   130,   131,     1,   132,   133,    87,   134,   135,   136,
+     137,   138,   139,    88,    10,    89,    90,    12,    91,    92,
       93,    94,    17,    18,    95,    96,    21,    97,    23,    24,
       98,    99,   100,   101,    28,     0,    30,    31,   102,   103,
-     104,   105,     0,     0,     0,     0,     0,     0,   141,     0,
-      36,    37,     0,     0,     0,   142,    39,   143,    41,   144,
-      43,   145,    45,    46,    47,    48,    49,    50,    51,    52,
-      53,     0,    54,    55,    56,   146,    58,     0,     0,    59,
-     147,    61,    62,   106,   107,   108,   109,     0,     0,     0,
-     110,     0,     0,     0,   148,   149,     0,     0,     0,     0,
-     168,   508,   151,   129,   130,     1,   131,   132,    87,   133,
-     134,   135,   136,   137,   138,    88,    10,    89,    90,   139,
-      91,    92,    93,    94,    17,    18,    95,    96,   140,    97,
+     104,   105,     0,     0,     0,     0,     0,     0,   142,     0,
+      36,    37,     0,     0,     0,   143,    39,   144,    41,   145,
+      43,   146,    45,    46,    47,    48,    49,    50,    51,    52,
+      53,     0,    54,    55,    56,   147,    58,     0,     0,    59,
+     148,    61,    62,   106,   107,   108,   109,     0,     0,     0,
+     110,     0,     0,     0,   149,   150,     0,     0,     0,     0,
+     169,   560,   152,   130,   131,     1,   132,   133,    87,   134,
+     135,   136,   137,   138,   139,    88,    10,    89,    90,   140,
+      91,    92,    93,    94,    17,    18,    95,    96,   141,    97,
       23,    24,    98,    99,   100,   101,    28,     0,    30,    31,
      102,   103,   104,   105,     0,     0,     0,     0,     0,     0,
-     141,     0,    36,    37,     0,     0,     0,   142,    39,   143,
-      41,   144,    43,   145,    45,    46,    47,    48,    49,    50,
-      51,    52,    53,     0,    54,    55,    56,   146,    58,     0,
-       0,    59,   147,    61,    62,   106,   107,   108,   109,     0,
-       0,     0,   110,     0,     0,     0,   148,   149,     0,     0,
-       0,     0,   150,     0,   151,   129,   130,     1,   131,   132,
-      87,   133,   134,   135,   136,   137,   138,    88,    10,    89,
+     142,     0,    36,    37,     0,     0,     0,   143,    39,   144,
+      41,   145,    43,   146,    45,    46,    47,    48,    49,    50,
+      51,    52,    53,     0,    54,    55,    56,   147,    58,     0,
+       0,    59,   148,    61,    62,   106,   107,   108,   109,     0,
+       0,     0,   110,     0,     0,     0,   149,   150,     0,     0,
+       0,     0,   151,     0,   152,   130,   131,     1,   132,   133,
+      87,   134,   135,   136,   137,   138,   139,    88,    10,    89,
       90,    12,    91,    92,    93,    94,    17,    18,    95,    96,
       21,    97,    23,    24,    98,    99,   100,   101,    28,     0,
       30,    31,   102,   103,   104,   105,     0,     0,     0,     0,
-       0,     0,   141,     0,    36,    37,     0,     0,     0,   142,
-      39,   143,    41,   144,    43,   145,    45,    46,    47,    48,
-      49,    50,    51,    52,    53,     0,    54,    55,    56,   146,
-      58,     0,     0,    59,   147,    61,    62,   106,   107,   108,
-     109,     0,     0,     0,   110,     0,     0,     0,   148,   149,
-       0,     0,     0,     0,   168,     0,   151,   129,   130,     1,
-     131,   132,    87,   133,   134,   135,   136,   137,   138,    88,
+       0,     0,   142,     0,    36,    37,     0,     0,     0,   143,
+      39,   144,    41,   145,    43,   146,    45,    46,    47,    48,
+      49,    50,    51,    52,    53,     0,    54,    55,    56,   147,
+      58,     0,     0,    59,   148,    61,    62,   106,   107,   108,
+     109,     0,     0,     0,   110,     0,     0,     0,   149,   150,
+       0,     0,     0,     0,   169,     0,   152,   130,   131,     1,
+     132,   133,    87,   134,   135,   136,   137,   138,   139,    88,
       10,    89,    90,    12,    91,    92,    93,    94,    17,    18,
       95,    96,    21,    97,    23,    24,    98,    99,   100,   101,
       28,     0,    30,    31,   102,   103,   104,   105,     0,     0,
-       0,     0,     0,     0,   141,     0,    36,    37,     0,     0,
-       0,   142,    39,   143,    41,   144,    43,   145,    45,    46,
+       0,     0,     0,     0,   142,     0,    36,    37,     0,     0,
+       0,   143,    39,   144,    41,   145,    43,   146,    45,    46,
       47,    48,    49,    50,    51,    52,    53,     0,    54,    55,
-      56,   146,    58,     0,     0,    59,   147,    61,    62,   106,
+      56,   147,    58,     0,     0,    59,   148,    61,    62,   106,
      107,   108,   109,     0,     0,     0,   110,     0,     0,     0,
-     148,   149,     0,     0,     0,     0,   150,     0,   151,   129,
-     130,     1,   131,   132,    87,   133,   134,   135,   136,   137,
-     138,    88,    10,    89,    90,    12,    91,    92,    93,    94,
+     149,   150,     0,     0,     0,     0,   151,     0,   152,   130,
+     131,     1,   132,   133,    87,   134,   135,   136,   137,   138,
+     139,    88,    10,    89,    90,    12,    91,    92,    93,    94,
       17,    18,    95,    96,    21,    97,    23,    24,    98,    99,
      100,   101,    28,     0,    30,    31,   102,   103,   104,   105,
-       0,     0,     0,     0,     0,     0,   141,     0,    36,    37,
-       0,     0,     0,   142,    39,   143,    41,   144,    43,   145,
+       0,     0,     0,     0,     0,     0,   142,     0,    36,    37,
+       0,     0,     0,   143,    39,   144,    41,   145,    43,   146,
       45,    46,    47,    48,    49,    50,    51,    52,    53,     0,
-      54,    55,    56,   146,    58,     0,     0,    59,    60,    61,
+      54,    55,    56,   147,    58,     0,     0,    59,    60,    61,
       62,   106,   107,   108,   109,     0,     0,     0,   110,     0,
-       0,     0,   148,   149,     0,     0,     0,     0,   201,     0,
-     151,   129,   130,     1,   131,   132,    87,   133,   134,   135,
-     136,   137,   138,    88,    10,    89,    90,    12,    91,    92,
+       0,     0,   149,   150,     0,     0,     0,     0,   203,     0,
+     152,   130,   131,     1,   132,   133,    87,   134,   135,   136,
+     137,   138,   139,    88,    10,    89,    90,    12,    91,    92,
       93,    94,    17,    18,    95,    96,    21,    97,    23,    24,
       98,    99,   100,   101,    28,     0,    30,    31,   102,   103,
-     104,   105,     0,     0,     0,     0,     0,     0,   141,     0,
-      36,    37,     0,     0,     0,   142,    39,   143,    41,   144,
-      43,   145,    45,    46,    47,    48,    49,    50,    51,    52,
-      53,     0,    54,    55,    56,   146,    58,     0,     0,    59,
+     104,   105,     0,     0,     0,     0,     0,     0,   142,     0,
+      36,    37,     0,     0,     0,   143,    39,   144,    41,   145,
+      43,   146,    45,    46,    47,    48,    49,    50,    51,    52,
+      53,     0,    54,    55,    56,   147,    58,     0,     0,    59,
       60,    61,    62,   106,   107,   108,   109,     0,     0,     0,
-     110,     0,     0,     0,   148,   149,     0,     0,     0,     0,
-     168,     0,   151,   129,   130,     1,   131,   132,    87,   133,
-     134,   135,   136,   137,   138,    88,    10,    89,    90,    12,
+     110,     0,     0,     0,   149,   150,     0,     0,     0,     0,
+     169,     0,   152,   130,   131,     1,   132,   133,    87,   134,
+     135,   136,   137,   138,   139,    88,    10,    89,    90,    12,
       91,    92,    93,    94,    17,    18,    95,    96,    21,    97,
       23,    24,    98,    99,   100,   101,    28,     0,    30,    31,
      102,   103,   104,   105,     0,     0,     0,     0,     0,     0,
-     141,     0,    36,    37,     0,     0,     0,   142,    39,   143,
-      41,   144,    43,   145,    45,    46,    47,    48,    49,    50,
-      51,    52,   432,     0,   434,    55,    56,   146,    58,     0,
-       0,   438,   439,    61,    62,   106,   107,   108,   109,     0,
-       0,     0,   110,     0,     0,     0,   148,   149,     0,     0,
-       0,     0,   168,     0,   151,   235,   236,     1,   237,   238,
-      87,   133,   134,   135,   136,   137,   138,    88,    10,    89,
+     142,     0,    36,    37,     0,     0,     0,   143,    39,   144,
+      41,   145,    43,   146,    45,    46,    47,    48,    49,    50,
+      51,    52,   474,     0,   476,    55,    56,   147,    58,     0,
+       0,   480,   481,    61,    62,   106,   107,   108,   109,     0,
+       0,     0,   110,     0,     0,     0,   149,   150,     0,     0,
+       0,     0,   169,     0,   152,   241,   242,     1,   243,   244,
+      87,   134,   135,   136,   137,   138,   139,    88,    10,    89,
       90,    12,    91,    92,    93,    94,    17,    18,    95,    96,
       21,    97,    23,    24,    98,    99,   100,   101,    28,     0,
       30,    31,   102,   103,   104,   105,     0,     0,     0,     0,
-       0,     0,   141,     0,    36,    37,     0,     0,     0,   142,
-      39,   143,    41,   144,    43,   145,    45,    46,    47,    48,
-      49,    50,    51,    52,    53,     0,    54,    55,    56,   146,
-      58,     0,     0,    59,   147,    61,    62,   106,   107,   108,
-     109,     0,     0,     0,   110,     0,     0,     0,   148,   149,
-       0,     0,     0,     0,   168,     0,   151,   129,   130,     1,
-     131,   132,    87,   133,   134,   135,   136,   137,   138,    88,
+       0,     0,   142,     0,    36,    37,     0,     0,     0,   143,
+      39,   144,    41,   145,    43,   146,    45,    46,    47,    48,
+      49,    50,    51,    52,    53,     0,    54,    55,    56,   147,
+      58,     0,     0,    59,   148,    61,    62,   106,   107,   108,
+     109,     0,     0,     0,   110,     0,     0,     0,   149,   150,
+       0,     0,     0,     0,   169,     0,   152,   130,   131,     1,
+     132,   133,    87,   134,   135,   136,   137,   138,   139,    88,
       10,    89,    90,    12,    91,    92,    93,    94,    17,    18,
       95,    96,    21,    97,    23,    24,    98,    99,   100,   101,
       28,     0,    30,    31,   102,   103,   104,   105,     0,     0,
-       0,     0,     0,     0,   141,     0,    36,    37,     0,     0,
-       0,   142,    39,   143,    41,   144,    43,   145,    45,    46,
-      47,    48,    49,    50,    51,    52,   494,     0,   495,    55,
-      56,   146,    58,     0,     0,   496,   497,    61,    62,   106,
+       0,     0,     0,     0,   142,     0,    36,    37,     0,     0,
+       0,   143,    39,   144,    41,   145,    43,   146,    45,    46,
+      47,    48,    49,    50,    51,    52,   542,     0,   543,    55,
+      56,   147,    58,     0,     0,   544,   545,    61,    62,   106,
      107,   108,   109,     0,     0,     0,   110,     0,     0,     0,
-     148,   149,     0,     0,     0,     0,   168,     0,   151,   129,
-     130,     1,   131,   132,    87,   133,   134,   135,   136,   137,
-     138,    88,    10,    89,    90,    12,    91,    92,    93,    94,
+     149,   150,     0,     0,     0,     0,   169,     0,   152,   130,
+     131,     1,   132,   133,    87,   134,   135,   136,   137,   138,
+     139,    88,    10,    89,    90,    12,    91,    92,    93,    94,
       17,    18,    95,    96,    21,    97,    23,    24,    98,    99,
      100,   101,    28,     0,    30,    31,   102,   103,   104,   105,
-       0,     0,     0,     0,     0,     0,   141,     0,    36,    37,
-       0,     0,     0,   142,    39,   143,    41,   144,    43,   145,
+       0,     0,     0,     0,     0,     0,   142,     0,    36,    37,
+       0,     0,     0,   143,    39,   144,    41,   145,    43,   146,
       45,    46,    47,    48,    49,    50,    51,    52,    53,     0,
-      54,    55,    56,   146,    58,     0,     0,    59,   147,    61,
+      54,    55,    56,   147,    58,     0,     0,    59,   148,    61,
       62,   106,   107,   108,   109,     0,     0,     0,   110,     0,
-       0,     0,     0,     0,     0,     0,     0,     0,   168,     1,
-     151,     0,     2,     3,     4,     5,     6,     7,     8,     9,
-      10,     0,    11,    12,    13,    14,    15,    16,    17,    18,
-      19,    20,    21,    22,    23,    24,    25,    26,    27,     0,
-      28,    29,    30,    31,    32,    33,    34,    35,     0,     0,
-       0,     0,     0,     0,     0,     0,    36,    37,     0,     0,
-       0,    38,    39,    40,    41,    42,    43,    44,    45,    46,
-      47,    48,    49,    50,    51,    52,    53,     0,    54,    55,
-      56,    57,    58,     0,     0,    59,    60,    61,    62,    63,
-      64,    65,     0,    66,     0,     0,    67,     0,     0,     0,
-       0,     1,     0,     0,    68,     3,     4,     5,     6,     7,
-       8,     9,    10,     0,    11,    12,    13,    14,    15,    16,
-      17,    18,     0,    20,    21,    22,    23,    24,    25,    26,
-      27,     0,    28,    29,    30,    31,     0,    33,    34,    35,
-       0,     0,     0,     0,     0,     0,     0,     0,    36,    37,
-       0,     0,     0,    38,    39,    40,    41,    42,    43,    44,
-      45,    46,    47,    48,    49,    50,    51,    52,    53,     0,
-      54,    55,    56,    57,    58,     0,     0,    59,    60,    61,
-      62,    63,    64,     0,     0,    66,     0,     0,    67,     0,
-       0,     0,     0,     1,     0,     0,   386,     3,     4,     5,
-       6,     7,     8,     9,    10,     0,    11,    12,    13,    14,
-      15,    16,    17,    18,     0,    20,    21,    22,    23,    24,
-      25,    26,    27,     0,    28,    29,    30,    31,     0,    33,
-      34,    35,     0,     0,     0,     0,     0,     0,     0,     0,
-      36,    37,     0,     0,     0,    38,    39,    40,    41,    42,
-      43,    44,    45,    46,    47,    48,    49,    50,    51,    52,
-      53,     0,    54,    55,    56,    57,    58,     0,     0,    59,
-      60,    61,    62,    63,    64,     0,     0,    66,     0,     0,
-      67,     0,     0,     0,     0,     1,     0,     0,   409,     3,
-       4,     5,     6,     7,     8,     9,    10,     0,    11,    12,
-      13,    14,    15,    16,    17,    18,     0,    20,    21,    22,
-      23,    24,    25,    26,    27,     0,    28,    29,    30,    31,
-       0,    33,    34,    35,     0,     0,     0,     0,     0,     0,
-       0,     0,    36,    37,     0,     0,     0,    38,    39,    40,
-      41,    42,    43,    44,    45,    46,    47,    48,    49,    50,
-      51,    52,    53,     0,    54,    55,    56,    57,    58,     0,
-       0,    59,    60,    61,    62,    63,    64,     0,     0,    66,
-       0,     0,    67,     0,     0,     0,     0,     1,     0,     0,
-     536,     3,     4,     5,     6,     7,     8,     9,    10,     0,
+       0,     0,   413,     0,     0,     0,     0,     1,   169,     0,
+     152,     3,     4,     5,     6,     7,     8,     9,    10,     0,
       11,    12,    13,    14,    15,    16,    17,    18,     0,    20,
       21,    22,    23,    24,    25,    26,    27,     0,    28,    29,
       30,    31,     0,    33,    34,    35,     0,     0,     0,     0,
@@ -1340,214 +1354,262 @@ static const yytype_int16 yytable[] =
       39,    40,    41,    42,    43,    44,    45,    46,    47,    48,
       49,    50,    51,    52,    53,     0,    54,    55,    56,    57,
       58,     0,     0,    59,    60,    61,    62,    63,    64,     0,
-       0,    66,     0,     0,    67,     0,     0,     0,     0,     1,
-       0,     0,   568,     3,     4,     5,     6,     7,     8,     9,
-      10,     0,    11,    12,    13,    14,    15,    16,    17,    18,
-       0,    20,    21,    22,    23,    24,    25,    26,    27,     0,
-      28,    29,    30,    31,     0,    33,    34,    35,     0,     0,
-       0,     0,     0,     0,     0,     0,    36,    37,     0,     0,
-       0,    38,    39,    40,    41,    42,    43,    44,    45,    46,
-      47,    48,    49,    50,    51,    52,    53,     0,    54,    55,
-      56,    57,    58,     0,     0,    59,    60,    61,    62,    63,
-      64,     0,     0,     0,     0,     0,    67,     0,     0,     0,
-       0,     0,     1,     0,   481,    87,     3,     4,     5,     6,
-       7,     8,    88,    10,    89,    90,    12,    91,    92,    93,
-      94,    17,    18,    95,    96,    21,    97,    23,    24,    98,
-      99,   100,   101,    28,     0,    30,    31,   102,   103,   104,
-     105,     0,     0,     0,     0,     0,     0,     0,     0,    36,
+       0,    66,     0,     0,    67,     0,     0,     0,     0,     0,
+       1,     0,   414,     2,     3,     4,     5,     6,     7,     8,
+       9,    10,     0,    11,    12,    13,    14,    15,    16,    17,
+      18,    19,    20,    21,    22,    23,    24,    25,    26,    27,
+       0,    28,    29,    30,    31,    32,    33,    34,    35,     0,
+       0,     0,     0,     0,     0,     0,     0,    36,    37,     0,
+       0,     0,    38,    39,    40,    41,    42,    43,    44,    45,
+      46,    47,    48,    49,    50,    51,    52,    53,     0,    54,
+      55,    56,    57,    58,     0,     0,    59,    60,    61,    62,
+      63,    64,    65,     0,    66,     0,     0,    67,     0,     0,
+       0,     0,     1,     0,     0,    68,     3,     4,     5,     6,
+       7,     8,     9,    10,     0,    11,    12,    13,    14,    15,
+      16,    17,    18,     0,    20,    21,    22,    23,    24,    25,
+      26,    27,     0,    28,    29,    30,    31,     0,    33,    34,
+      35,     0,     0,     0,     0,     0,     0,     0,     0,    36,
       37,     0,     0,     0,    38,    39,    40,    41,    42,    43,
       44,    45,    46,    47,    48,    49,    50,    51,    52,    53,
        0,    54,    55,    56,    57,    58,     0,     0,    59,    60,
-      61,    62,   106,   107,   108,   109,     0,     1,     0,   110,
-      87,     3,     4,     5,     6,     7,     8,    88,    10,    89,
-      90,    12,    91,    92,    93,    94,    17,    18,    95,    96,
-      21,    97,    23,    24,    98,    99,   100,   101,    28,     0,
-      30,    31,   102,   103,   104,   105,     0,     0,     0,     0,
-       0,     0,     0,     0,    36,    37,     0,     0,     0,    38,
-      39,    40,    41,    42,    43,    44,    45,    46,    47,    48,
-      49,    50,    51,    52,    53,     0,    54,    55,    56,   178,
-      58,     0,     0,    59,    60,    61,    62,   106,   107,   108,
-     109,     0,     1,     0,   110,    87,     3,     4,     5,     6,
-       7,     8,    88,    10,    89,    90,    12,    91,    92,    93,
-      94,    17,    18,    95,    96,    21,    97,    23,    24,    98,
-      99,   100,   101,    28,     0,    30,    31,   102,   103,   104,
-     105,     0,     0,     0,     0,     0,     0,     0,     0,    36,
-      37,     0,     0,     0,    38,    39,    40,    41,    42,    43,
-      44,    45,   221,   222,   223,   224,   225,    51,   226,    53,
-       0,    54,    55,   227,    57,    58,     0,     0,    59,    60,
-      61,    62,   106,   107,   108,   109,     0,     1,     0,   110,
-      87,   133,   134,     5,     6,   137,   138,    88,    10,    89,
-      90,    12,    91,    92,    93,    94,    17,    18,    95,    96,
-      21,    97,    23,    24,    98,    99,   100,   101,    28,     0,
-      30,    31,   102,   103,   104,   105,     0,     0,     0,     0,
-       0,     0,     0,     0,    36,    37,     0,     0,     0,   142,
-      39,   143,    41,   144,    43,   145,    45,    46,    47,    48,
-      49,    50,    51,    52,    53,     0,    54,    55,    56,   389,
-      58,     0,     0,    59,    60,    61,    62,   106,   107,   108,
-     109,     1,     0,     0,   110,     3,     4,     5,     6,     7,
-       8,     9,    10,     0,    11,    12,    13,    14,    15,     0,
-      17,    18,     0,    20,    21,    22,    23,    24,    25,    99,
-      27,     0,    28,     0,    30,    31,     0,    33,    34,    35,
+      61,    62,    63,    64,     0,     0,    66,     0,     0,    67,
+       0,     0,     0,     0,     1,     0,     0,   448,     3,     4,
+       5,     6,     7,     8,     9,    10,     0,    11,    12,    13,
+      14,    15,    16,    17,    18,     0,    20,    21,    22,    23,
+      24,    25,    26,    27,     0,    28,    29,    30,    31,     0,
+      33,    34,    35,     0,     0,     0,     0,     0,     0,     0,
+       0,    36,    37,     0,     0,     0,    38,    39,    40,    41,
+      42,    43,    44,    45,    46,    47,    48,    49,    50,    51,
+      52,    53,     0,    54,    55,    56,    57,    58,     0,     0,
+      59,    60,    61,    62,    63,    64,     0,     0,    66,     0,
+       0,    67,     0,     0,     0,     0,     1,     0,     0,   589,
+       3,     4,     5,     6,     7,     8,     9,    10,     0,    11,
+      12,    13,    14,    15,    16,    17,    18,     0,    20,    21,
+      22,    23,    24,    25,    26,    27,     0,    28,    29,    30,
+      31,     0,    33,    34,    35,     0,     0,     0,     0,     0,
+       0,     0,     0,    36,    37,     0,     0,     0,    38,    39,
+      40,    41,    42,    43,    44,    45,    46,    47,    48,    49,
+      50,    51,    52,    53,     0,    54,    55,    56,    57,    58,
+       0,     0,    59,    60,    61,    62,    63,    64,     0,     0,
+      66,     0,     0,    67,     0,     0,     0,     0,     1,     0,
+       0,   631,     3,     4,     5,     6,     7,     8,     9,    10,
+       0,    11,    12,    13,    14,    15,    16,    17,    18,     0,
+      20,    21,    22,    23,    24,    25,    26,    27,     0,    28,
+      29,    30,    31,     0,    33,    34,    35,     0,     0,     0,
+       0,     0,     0,     0,     0,    36,    37,     0,     0,     0,
+      38,    39,    40,    41,    42,    43,    44,    45,    46,    47,
+      48,    49,    50,    51,    52,    53,     0,    54,    55,    56,
+      57,    58,     0,     0,    59,    60,    61,    62,    63,    64,
+       0,     0,     0,     0,     0,    67,     0,     0,     0,     0,
+       0,     1,     0,   524,    87,     3,     4,     5,     6,     7,
+       8,    88,    10,    89,    90,    12,    91,    92,    93,    94,
+      17,    18,    95,    96,    21,    97,    23,    24,    98,    99,
+     100,   101,    28,     0,    30,    31,   102,   103,   104,   105,
        0,     0,     0,     0,     0,     0,     0,     0,    36,    37,
        0,     0,     0,    38,    39,    40,    41,    42,    43,    44,
       45,    46,    47,    48,    49,    50,    51,    52,    53,     0,
-      54,    55,    56,    57,    58,   306,     0,    59,    60,    61,
-      62,   307,    64,     0,     0,     0,     0,     0,    67,   250,
-     251,   252,   253,   254,   255,     0,   256,   308,     0,   257,
-     258,   259,     0,   309,     0,     0,     0,     0,     0,     0,
-     510,   250,   251,   252,   253,   254,   255,     0,   256,     0,
-       0,   257,   258,   259,   250,   251,   252,   253,   254,   255,
-       0,   256,   511,     0,   257,   258,   259,     0,   260,   261,
-     262,   263,   264,   265,   266,     0,   250,   251,   252,   253,
-     254,   255,     0,   256,     0,     0,   257,   258,   259,     0,
-     260,   261,   262,   263,   264,   265,   266,   512,     0,     0,
-       0,     0,     0,   260,   261,   262,   263,   264,   265,   266,
-       0,   250,   251,   252,   253,   254,   255,     0,   256,   513,
-       0,   257,   258,   259,     0,   260,   261,   262,   263,   264,
-     265,   266,   592,   250,   251,   252,   253,   254,   255,     0,
-     256,     0,     0,   257,   258,   259,   576,   577,     0,   578,
-     579,   580,     0,   581,     0,     0,   582,   583,   584,   294,
-     260,   261,   262,   263,   264,   265,   266,   250,   251,   252,
-     253,   254,   255,     0,   256,     0,     0,   257,   258,   259,
-       0,     0,   260,   261,   262,   263,   264,   265,   266,     0,
-       0,     0,     0,   274,     0,   585,   586,   587,   588,   589,
-     590,   591,   250,   251,   252,   253,   254,   255,     0,   256,
-       0,     0,   257,   258,   259,     0,   260,   261,   262,   263,
-     264,   265,   266,   295,   250,   251,   252,   253,   254,   255,
-       0,   256,     0,     0,   257,   258,   259,   250,   251,   252,
-     253,   254,   255,     0,   256,     0,     0,   257,   258,   259,
-       0,   260,   261,   262,   263,   264,   265,   266,   250,   251,
-     252,   253,   254,   255,     0,   256,     0,     0,   257,   258,
-     259,     0,     0,   260,   261,   262,   263,   264,   265,   266,
-       0,   333,     0,   334,     0,     0,   260,   261,   262,   263,
-     264,   265,   266,     0,   415,     0,   334,     0,     0,     0,
-       0,     0,     0,     0,     0,     0,     0,   260,   261,   262,
-     263,   264,   265,   266,     0,     0,     0,   334,   367,   368,
-     369,   370,   371,   372,     0,   373,     0,     0,   374,   375,
-     376,   367,   368,   369,   370,   371,   372,     0,   373,     0,
-       0,   374,   375,   376,   367,   368,   369,   370,   371,   372,
-       0,   373,     0,     0,   374,   375,   376,     0,     0,     0,
-       0,     0,     0,     0,     0,     0,     0,   377,   378,   379,
-     380,   381,   382,   383,     0,   384,     0,     0,     0,     0,
-     377,   378,   379,   380,   381,   382,   383,     0,   446,     0,
-       0,     0,     0,   377,   378,   379,   380,   381,   382,   383,
-       0,   528,   250,   251,   252,   253,   254,   255,     0,   256,
-       0,     0,   257,   258,   259,   250,   251,   252,   253,   254,
-     255,     0,   256,     0,     0,   257,   258,   259,   250,   251,
-     252,   253,   254,   255,     0,   256,     0,     0,   257,   258,
-     259,     0,     0,     0,     0,     0,     0,     0,     0,     0,
-       0,   260,   261,   262,   263,   264,   265,   266,   276,     0,
-       0,     0,     0,     0,   260,   261,   262,   263,   264,   265,
-     266,   397,     0,     0,     0,     0,     0,   260,   261,   262,
-     263,   264,   265,   266,   500,   250,   251,   252,   253,   254,
-     255,     0,   256,     0,     0,   257,   258,   259,   250,   251,
-     252,   253,   254,   255,     0,   256,     0,     0,   257,   258,
-     259,   250,   251,   252,   253,   254,   255,     0,   256,     0,
-       0,   257,   258,   259,     0,     0,     0,     0,     0,     0,
-       0,     0,     0,     0,   260,   261,   262,   263,   264,   265,
-     266,   504,     0,     0,     0,     0,     0,   260,   261,   262,
-     263,   264,   265,   266,   538,     0,     0,     0,     0,     0,
-     260,   261,   262,   263,   264,   265,   266,   603,   250,   251,
-     252,   253,   254,   255,     0,   256,     0,     0,   257,   258,
-     259,     0,     0,     0,     0,     0,   250,   251,   252,   253,
-     254,   255,     0,   256,     0,     0,   257,   258,   259,   250,
-     251,   252,   253,   254,   255,     0,   256,     0,     0,   257,
-     258,   259,     0,     0,     0,     0,     0,   260,   261,   262,
-     263,   264,   265,   266,   627,   330,   331,     0,     0,     0,
-       0,     0,     0,     0,     0,   260,   261,   262,   263,   264,
-     265,   266,     0,     0,     0,     0,     0,     0,   260,   261,
-     262,   263,   264,   265,   266,   367,   368,   369,   370,   371,
-     372,     0,   373,     0,     0,   374,   375,   376,   576,   577,
-       0,   578,     0,     0,     0,   581,     0,     0,   582,   583,
-     584,     0,     0,     0,     0,     0,     0,     0,     0,     0,
+      54,    55,    56,    57,    58,     0,     0,    59,    60,    61,
+      62,   106,   107,   108,   109,     0,     1,     0,   110,    87,
+       3,     4,     5,     6,     7,     8,    88,    10,    89,    90,
+      12,    91,    92,    93,    94,    17,    18,    95,    96,    21,
+      97,    23,    24,    98,    99,   100,   101,    28,     0,    30,
+      31,   102,   103,   104,   105,     0,     0,     0,     0,     0,
+       0,     0,     0,    36,    37,     0,     0,     0,    38,    39,
+      40,    41,    42,    43,    44,    45,    46,    47,    48,    49,
+      50,    51,    52,    53,     0,    54,    55,    56,   180,    58,
+       0,     0,    59,    60,    61,    62,   106,   107,   108,   109,
+       0,     1,     0,   110,    87,     3,     4,     5,     6,     7,
+       8,    88,    10,    89,    90,    12,    91,    92,    93,    94,
+      17,    18,    95,    96,    21,    97,    23,    24,    98,    99,
+     100,   101,    28,     0,    30,    31,   102,   103,   104,   105,
+       0,     0,     0,     0,     0,     0,     0,     0,    36,    37,
+       0,     0,     0,    38,    39,    40,    41,    42,    43,    44,
+      45,   227,   228,   229,   230,   231,    51,   232,    53,     0,
+      54,    55,   233,    57,    58,     0,     0,    59,    60,    61,
+      62,   106,   107,   108,   109,     0,     1,     0,   110,    87,
+     134,   135,     5,     6,   138,   139,    88,    10,    89,    90,
+      12,    91,    92,    93,    94,    17,    18,    95,    96,    21,
+      97,    23,    24,    98,    99,   100,   101,    28,     0,    30,
+      31,   102,   103,   104,   105,     0,     0,     0,     0,     0,
+       0,     0,     0,    36,    37,     0,     0,     0,   143,    39,
+     144,    41,   145,    43,   146,    45,    46,    47,    48,    49,
+      50,    51,    52,    53,     0,    54,    55,    56,   421,    58,
+       0,     0,    59,    60,    61,    62,   106,   107,   108,   109,
+       1,     0,     0,   110,     3,     4,     5,     6,     7,     8,
+       9,    10,     0,    11,    12,    13,    14,    15,     0,    17,
+      18,     0,    20,    21,    22,    23,    24,    25,    99,    27,
+       0,    28,     0,    30,    31,     0,    33,    34,    35,     0,
+       0,     0,     0,     0,     0,     0,     0,    36,    37,     0,
+       0,     0,    38,    39,    40,    41,    42,    43,    44,    45,
+      46,    47,    48,    49,    50,    51,    52,    53,   597,    54,
+      55,    56,    57,    58,     0,     0,    59,    60,    61,    62,
+       0,    64,   258,   259,   260,   261,     0,    67,   598,   264,
+       0,     0,   265,   266,   267,     0,   258,   259,   260,   261,
+     262,   263,     0,   264,   323,     0,   265,   266,   267,     0,
+     324,     0,     0,     0,     0,     0,     0,     0,   258,   259,
+     260,   261,   262,   263,     0,   264,     0,     0,   265,   266,
+     267,   268,   269,   270,   271,   272,   273,   274,   325,     0,
+       0,     0,     0,     0,   326,   268,   269,   270,   271,   272,
+     273,   274,   258,   259,   260,   261,   262,   263,     0,   264,
+     327,     0,   265,   266,   267,     0,   328,   268,   269,   270,
+     271,   272,   273,   274,   258,   259,   260,   261,   262,   263,
+       0,   264,     0,     0,   265,   266,   267,     0,     0,     0,
+       0,     0,     0,     0,   329,     0,     0,     0,     0,     0,
+     330,   268,   269,   270,   271,   272,   273,   274,   258,   259,
+     260,   261,   262,   263,     0,   264,   562,     0,   265,   266,
+     267,     0,     0,   268,   269,   270,   271,   272,   273,   274,
+     258,   259,   260,   261,   262,   263,     0,   264,     0,     0,
+     265,   266,   267,     0,     0,     0,     0,     0,     0,     0,
+     563,     0,     0,     0,     0,     0,     0,   268,   269,   270,
+     271,   272,   273,   274,   258,   259,   260,   261,   262,   263,
+       0,   264,   564,     0,   265,   266,   267,     0,     0,   268,
+     269,   270,   271,   272,   273,   274,   258,   259,   260,   261,
+     262,   263,     0,   264,     0,     0,   265,   266,   267,     0,
+       0,     0,     0,     0,     0,     0,   565,     0,     0,     0,
+       0,     0,     0,   268,   269,   270,   271,   272,   273,   274,
+     258,   259,   260,   261,   262,   263,     0,   264,     0,     0,
+     265,   266,   267,   283,     0,   268,   269,   270,   271,   272,
+     273,   274,   258,   259,   260,   261,   262,   263,     0,   264,
+       0,     0,   265,   266,   267,   258,   259,   260,   261,   262,
+     263,     0,   264,     0,     0,   265,   266,   267,     0,   268,
+     269,   270,   271,   272,   273,   274,   258,   259,   260,   261,
+     262,   263,     0,   264,     0,     0,   265,   266,   267,     0,
+       0,   268,   269,   270,   271,   272,   273,   274,     0,     0,
+       0,     0,     0,     0,   268,   269,   270,   271,   272,   273,
+     274,     0,   357,     0,   358,     0,     0,     0,     0,     0,
+       0,     0,     0,     0,     0,   268,   269,   270,   271,   272,
+     273,   274,     0,   457,     0,   358,   258,   259,   260,   261,
+     262,   263,     0,   264,     0,     0,   265,   266,   267,   393,
+     394,   395,   396,   397,   398,     0,   399,     0,     0,   400,
+     401,   402,   393,   394,   395,   396,   397,   398,     0,   399,
+       0,     0,   400,   401,   402,     0,     0,     0,     0,     0,
+       0,     0,     0,     0,     0,   268,   269,   270,   271,   272,
+     273,   274,     0,     0,     0,   358,     0,     0,   403,   404,
+     405,   406,   407,   408,   409,     0,   410,     0,     0,     0,
+       0,   403,   404,   405,   406,   407,   408,   409,     0,   488,
+     393,   394,   395,   396,   397,   398,     0,   399,     0,     0,
+     400,   401,   402,   258,   259,   260,   261,   262,   263,     0,
+     264,     0,     0,   265,   266,   267,     0,     0,     0,     0,
+       0,   258,   259,   260,   261,   262,   263,     0,   264,     0,
+       0,   265,   266,   267,     0,     0,     0,     0,     0,   403,
+     404,   405,   406,   407,   408,   409,     0,   580,     0,     0,
+       0,     0,   268,   269,   270,   271,   272,   273,   274,   554,
+     354,   355,     0,     0,     0,     0,     0,     0,     0,     0,
+     268,   269,   270,   271,   272,   273,   274,   258,   259,   260,
+     261,   262,   263,     0,   264,     0,     0,   265,   266,   267,
+     393,   394,   395,   396,   397,   398,     0,   399,     0,     0,
+     400,   401,   402,   641,   642,     0,   643,   644,   645,     0,
+     646,     0,     0,   647,   648,   649,     0,     0,     0,     0,
+       0,     0,     0,     0,     0,     0,   268,   269,   270,   271,
+     272,   273,   274,     0,     0,     0,     0,     0,     0,   403,
+     404,   405,   406,   407,   408,   409,     0,     0,     0,     0,
+       0,     0,   650,   651,   652,   653,   654,   655,   656,   641,
+     642,     0,   643,     0,     0,     0,   646,     0,     0,   647,
+     648,   649,     0,     0,     0,     0,     0,     0,     0,     0,
+       0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
        0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
-       0,     0,     0,     0,   377,   378,   379,   380,   381,   382,
-     383,     0,     0,     0,     0,     0,     0,   585,   586,   587,
-     588,   589,   590,   591
+       0,     0,     0,     0,     0,     0,     0,     0,   650,   651,
+     652,   653,   654,   655,   656
 };
 
 static const yytype_int16 yycheck[] =
 {
-       0,     2,    67,     0,   400,    18,   386,    35,    14,    27,
-      16,    34,    13,     9,    20,    25,   246,   536,    19,    25,
-     218,   103,   165,    33,    34,   107,    27,    33,    34,   165,
-     270,    32,   175,    52,    35,   233,    64,   267,   571,   175,
-      34,    52,   101,   186,    55,   101,     9,    52,    37,   568,
-     186,    71,    15,    52,    25,   198,   102,    63,    47,    79,
-     106,     0,   198,    64,    65,    75,   599,    67,    25,    75,
-      70,    39,    40,    70,    31,   218,    47,    90,   101,    72,
-      99,   100,   218,    52,   101,    96,    97,    98,    99,   100,
-     233,   103,   332,    89,    99,   100,    31,   233,    97,    98,
-      99,   100,    31,   333,   484,   101,   101,   347,   106,    71,
-     101,   102,   118,   119,   120,   121,    71,    79,    10,    11,
-     101,   102,    14,    15,    79,   101,   366,    71,    97,    98,
-      99,   100,   150,   139,   140,    79,   142,   143,   144,   145,
-     101,   147,   170,    71,   150,   146,   280,   281,   346,   102,
-     151,    79,   101,   102,   384,   551,   536,   107,   428,   429,
-     430,   431,   168,   101,   192,   435,    58,   101,    60,   170,
-      62,   177,    64,   106,   101,   176,    52,   178,   388,    55,
-     101,   101,   101,   101,   101,   415,    78,   397,   568,   101,
-      58,   192,    60,   101,    62,   201,    64,   107,   101,   200,
-     107,   107,   102,   346,   414,    29,   101,   101,    31,   449,
-     346,   115,   116,   117,   215,    29,   446,   102,   458,    18,
-      96,    97,    98,    99,   100,    92,   101,   101,   246,   294,
-     134,   135,   136,   137,   138,   101,    91,   101,     9,   104,
-     480,   104,    31,    73,   250,   251,   252,   253,   254,   255,
-     256,   257,   258,   259,   260,   261,   262,   263,   264,   265,
-     266,   279,   280,   281,   104,   104,   101,    37,   101,   509,
-     101,    73,   104,    90,   274,   104,    91,   274,   279,   280,
-     281,   287,    73,   104,   290,   525,    91,    91,   114,   115,
-     116,   117,   101,   101,   294,   295,    70,   303,   528,   305,
-     101,   307,   405,   309,    34,   412,   484,   133,   134,   135,
-     136,   137,   138,   329,   522,   141,   279,   280,   281,   341,
-      -1,    -1,   148,   149,    -1,    -1,   566,    -1,   538,   335,
-     336,   337,   338,    -1,   340,   341,    -1,    -1,   344,    -1,
-     341,    -1,   348,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
-      -1,   494,   495,   496,   497,    -1,    -1,    -1,    -1,    -1,
-      -1,   367,   368,   369,   370,   371,   372,   373,   374,   375,
-     376,   377,   378,   379,   380,   381,   382,   383,    -1,    -1,
-      -1,    -1,    -1,   593,    -1,    -1,   386,    -1,   388,   395,
-     396,    -1,    -1,   603,    -1,    -1,    -1,   397,    -1,   400,
-     401,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    45,    46,
-      47,    48,   412,    -1,   414,    52,    -1,   627,    55,    56,
-      57,   427,    -1,    -1,    -1,    -1,   495,   496,   497,    -1,
-      -1,    -1,    -1,   576,   577,   578,   579,   580,   581,   582,
-     583,   584,   585,   586,   587,   588,   589,   590,   591,    -1,
-      -1,    -1,    -1,   459,    -1,    -1,    -1,    94,    95,    96,
-      97,    98,    99,   100,    -1,    -1,    -1,    -1,    -1,    -1,
-      -1,    -1,    -1,    -1,   502,    -1,   302,   620,   304,    -1,
-     306,    -1,   308,    -1,   484,    -1,   486,    -1,    -1,   486,
+       0,     2,     0,    67,    27,   414,   278,    35,   434,   417,
+     252,     1,    13,     9,   224,   523,     1,     1,    19,    15,
+       9,     1,   430,   584,     1,     1,    27,     1,    52,   239,
+       1,    32,   166,   275,    35,   166,    64,     1,   446,     1,
+       1,     1,     1,   177,    34,     1,   177,   455,     1,     1,
+      37,    34,     1,     1,   188,     1,     1,   188,     1,     1,
+      47,    18,    25,    64,    65,   101,   200,    67,    31,   200,
+      70,   101,    70,   634,    71,    99,   100,    14,     0,    16,
+      72,   589,    79,    20,   356,    25,    25,    52,    25,   101,
+     224,   101,   102,   224,    33,    34,    33,    34,   102,   371,
+      89,    71,    71,    39,    40,   239,   667,    47,   239,    79,
+      79,   101,   101,   101,   523,   357,   101,   101,   527,   101,
+     392,   101,   530,   631,   101,   101,    63,   101,   151,   107,
+     101,   103,   540,    90,    99,   100,    75,   101,    75,   101,
+     101,   101,   101,   171,    52,   101,   147,   101,   101,   101,
+     558,   152,   101,   101,    71,   101,   101,    71,   101,   101,
+     370,    31,    79,   101,   102,    79,   194,    58,   410,    60,
+     171,    62,   101,    64,   115,   116,   117,   178,    52,   180,
+     589,   118,   119,   120,   121,   101,   102,   613,   596,    97,
+      98,    99,   100,   194,   135,   136,   137,   138,   139,   101,
+     102,   202,    31,   140,   141,   106,   143,   144,   145,   146,
+     107,   148,   107,   103,   151,   457,   102,   107,   107,   491,
+     221,   106,   631,    97,    98,    99,   100,   635,   500,   252,
+      52,    29,   169,    55,   102,   661,   370,   101,   106,   370,
+     293,   294,   179,   101,   101,   101,   488,   101,   101,   313,
+     522,   659,    10,    11,    31,   101,    14,    15,   101,   101,
+     101,   101,   101,   101,    52,   673,   203,    55,   101,   292,
+     293,   294,    29,   101,    96,    97,    98,    99,   100,   470,
+     471,   472,   473,   283,   692,   283,   477,   102,    18,   561,
+     101,   292,   293,   294,   702,   703,   292,   293,   294,   101,
+      58,   709,    60,    92,    62,   577,    64,   101,    96,    97,
+      98,    99,   100,   313,   314,    91,   101,   104,   104,   101,
+      78,   258,   259,   260,   261,   262,   263,   264,   265,   266,
+     267,   268,   269,   270,   271,   272,   273,   274,   580,   101,
+     101,    92,   101,     9,    31,    73,    90,   101,   104,   104,
+      91,   101,    37,   101,   101,     9,   628,    73,   104,   104,
+      91,   101,    73,   300,   365,   101,   104,    91,   305,   101,
+      91,    91,   101,    91,   101,   101,   101,   114,   115,   116,
+     117,    91,   101,   433,   101,   439,   101,   324,    70,   326,
+      34,   328,   451,   330,   527,   353,   574,   134,   135,   136,
+     137,   138,   139,    -1,    -1,   142,    -1,    -1,   542,   543,
+     544,   545,   149,   150,   414,   365,    -1,   417,    -1,    -1,
+      -1,    -1,   359,   360,   361,   362,    -1,   364,   365,    -1,
+     430,   368,    -1,   434,   435,    -1,    -1,   374,    -1,    -1,
+      -1,    -1,    -1,    -1,    -1,    -1,   446,    -1,    -1,    -1,
+      -1,   451,    -1,    -1,    -1,   455,   393,   394,   395,   396,
+     397,   398,   399,   400,   401,   402,   403,   404,   405,   406,
+     407,   408,   409,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
       -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
-      -1,   502,   328,   329,   330,   331,    -1,   576,   577,   578,
-     579,   580,   581,   582,   583,   584,   585,   586,   587,   588,
-     589,   590,   591,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
-      -1,    -1,   532,    -1,   540,   532,   536,    -1,   538,    -1,
+     427,   428,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
+      -1,    -1,    -1,    -1,    -1,    -1,    -1,   641,   642,   643,
+     644,   645,   646,   647,   648,   649,   650,   651,   652,   653,
+     654,   655,   656,   523,   552,    -1,     1,   527,    -1,   529,
+     530,   529,   469,    -1,    -1,    31,    -1,    -1,    -1,    -1,
+     540,   543,   544,   545,    -1,    -1,    -1,    -1,    -1,    45,
+      46,   552,    48,    49,    50,    -1,    52,   691,   558,    55,
+      56,    57,    37,    -1,   501,    -1,    -1,    -1,    -1,    -1,
+      45,    46,    47,    48,    49,    50,    -1,    52,    -1,    -1,
+      55,    56,    57,    -1,    -1,   585,   323,   585,   325,   589,
+     327,    -1,   329,    -1,    -1,    -1,   596,    -1,    94,    95,
+      96,    97,    98,    99,   100,    -1,    -1,    -1,    -1,    -1,
+      -1,    -1,   613,    -1,    -1,   352,   353,   354,   355,    94,
+      95,    96,    97,    98,    99,   100,   101,    -1,    -1,    -1,
+       1,   631,    -1,    -1,    -1,   635,    -1,    -1,    -1,   641,
+     642,   643,   644,   645,   646,   647,   648,   649,   650,   651,
+     652,   653,   654,   655,   656,    -1,    -1,    -1,    -1,   659,
+     661,   598,    -1,    -1,    -1,    -1,     1,    -1,    -1,    -1,
+      -1,    -1,    -1,   673,    45,    46,    47,    48,    49,    50,
+      -1,    52,    -1,    -1,    55,    56,    57,    -1,    -1,   691,
+      -1,    -1,   692,    -1,   431,    -1,    -1,    -1,    -1,    -1,
+     637,     1,   702,   703,    -1,    -1,    -1,    -1,    -1,   709,
+      45,    46,    47,    48,    49,    50,    -1,    52,    -1,    -1,
+      55,    56,    57,    94,    95,    96,    97,    98,    99,   100,
+     101,   468,    -1,    -1,    -1,    -1,    -1,   474,    -1,   476,
+      -1,    -1,    -1,   480,   481,    45,    46,    47,    48,    49,
+      50,    -1,    52,    -1,    -1,    55,    56,    57,    -1,    94,
+      95,    96,    97,    98,    99,   100,   101,     1,    -1,    -1,
       -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
-     551,   620,    -1,    -1,    -1,    -1,    17,    -1,    -1,    -1,
-      -1,    -1,    -1,    -1,    -1,    -1,    -1,   573,   568,    -1,
-      45,    46,   398,    48,    49,    50,    37,    52,    -1,    -1,
-      55,    56,    57,    -1,    45,    46,    47,    48,    49,    50,
-      -1,    52,    -1,   593,    55,    56,    57,    -1,    -1,    -1,
-     426,    -1,    -1,   603,    -1,    -1,   432,    -1,   434,    -1,
-      -1,    -1,   438,   439,    -1,    -1,    -1,    -1,    -1,    94,
-      95,    96,    97,    98,    99,   100,    -1,   627,    -1,    -1,
-      -1,    -1,    -1,    94,    95,    96,    97,    98,    99,   100,
-      31,    -1,    -1,    -1,    -1,    -1,    37,    -1,    -1,    -1,
-      -1,    -1,    -1,    -1,    45,    46,    47,    48,    49,    50,
-      -1,    52,    -1,    -1,    55,    56,    57,    -1,   494,   495,
-     496,   497,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
-      -1,    -1,    -1,    -1,   510,   511,   512,   513,    -1,    -1,
-     516,   517,   518,   519,    -1,    -1,    -1,    -1,    -1,    -1,
-      -1,    -1,    -1,    94,    95,    96,    97,    98,    99,   100,
-      -1,    -1,    -1,    31,    -1,    -1,    -1,    -1,    -1,    37,
-      -1,    -1,    -1,    -1,    -1,    -1,    -1,    45,    46,    47,
-      48,    49,    50,    -1,    52,    -1,   562,    55,    56,    57,
       -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
-     576,   577,   578,   579,   580,   581,   582,   583,   584,   585,
-     586,   587,   588,   589,   590,   591,    -1,    -1,    -1,    -1,
-     596,    -1,    -1,    -1,    -1,    -1,    94,    95,    96,    97,
-      98,    99,   100,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
-      -1,    -1,    -1,    -1,   620,     4,     5,     6,     7,     8,
+      -1,    -1,    -1,    -1,    94,    95,    96,    97,    98,    99,
+     100,   101,     1,    -1,    -1,   542,   543,   544,   545,    -1,
+      -1,    45,    46,    47,    48,    49,    50,    -1,    52,    -1,
+      -1,    55,    56,    57,    -1,   562,   563,   564,   565,    -1,
+      -1,   568,   569,   570,   571,     1,    -1,    -1,    -1,    -1,
+      -1,    -1,    -1,    -1,    -1,    -1,    45,    46,    47,    48,
+      49,    50,    -1,    52,    -1,    -1,    55,    56,    57,    -1,
+      94,    95,    96,    97,    98,    99,   100,   101,    -1,    -1,
+      -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    45,
+      46,    47,    48,    49,    50,    -1,    52,   624,    -1,    55,
+      56,    57,    -1,    -1,    -1,    94,    95,    96,    97,    98,
+      99,   100,   101,    -1,   641,   642,   643,   644,   645,   646,
+     647,   648,   649,   650,   651,   652,   653,   654,   655,   656,
+      -1,    -1,    -1,    -1,    -1,    -1,    -1,   664,    94,    95,
+      96,    97,    98,    99,   100,   101,    -1,    -1,    -1,    -1,
+      -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
+      -1,    -1,     1,    -1,   691,     4,     5,     6,     7,     8,
        9,    10,    11,    12,    13,    14,    15,    16,    17,    18,
       19,    20,    21,    22,    23,    24,    25,    26,    27,    28,
       29,    30,    31,    32,    33,    34,    35,    36,    37,    -1,
@@ -1557,18 +1619,18 @@ static const yytype_int16 yycheck[] =
       69,    70,    71,    72,    73,    -1,    75,    76,    77,    78,
       79,    -1,    -1,    82,    83,    84,    85,    86,    87,    88,
       89,    -1,    -1,    -1,    93,    -1,    -1,    -1,    97,    98,
-      -1,    -1,    -1,    -1,   103,    -1,   105,    -1,   107,     4,
-       5,     6,     7,     8,     9,    10,    11,    12,    13,    14,
-      15,    16,    17,    18,    19,    20,    21,    22,    23,    24,
-      25,    26,    27,    28,    29,    30,    31,    32,    33,    34,
-      35,    36,    37,    -1,    39,    40,    41,    42,    43,    44,
-      45,    46,    47,    48,    49,    50,    51,    52,    53,    54,
-      55,    56,    57,    58,    59,    60,    61,    62,    63,    64,
-      65,    66,    67,    68,    69,    70,    71,    72,    73,    -1,
-      75,    76,    77,    78,    79,    -1,    -1,    82,    83,    84,
-      85,    86,    87,    88,    89,    -1,    -1,    -1,    93,    94,
-      95,    96,    97,    98,    99,   100,    -1,   102,   103,    -1,
-     105,     4,     5,     6,     7,     8,     9,    10,    11,    12,
+      -1,    -1,   101,    -1,   103,    -1,   105,     4,     5,     6,
+       7,     8,     9,    10,    11,    12,    13,    14,    15,    16,
+      17,    18,    19,    20,    21,    22,    23,    24,    25,    26,
+      27,    28,    29,    30,    31,    32,    33,    34,    35,    36,
+      37,    -1,    39,    40,    41,    42,    43,    44,    -1,    -1,
+      -1,    -1,    -1,    -1,    51,    -1,    53,    54,    -1,    -1,
+      -1,    58,    59,    60,    61,    62,    63,    64,    65,    66,
+      67,    68,    69,    70,    71,    72,    73,    -1,    75,    76,
+      77,    78,    79,    -1,    -1,    82,    83,    84,    85,    86,
+      87,    88,    89,    -1,    -1,    -1,    93,    -1,    -1,    -1,
+      97,    98,    -1,    -1,    -1,    -1,   103,    -1,   105,    -1,
+     107,     4,     5,     6,     7,     8,     9,    10,    11,    12,
       13,    14,    15,    16,    17,    18,    19,    20,    21,    22,
       23,    24,    25,    26,    27,    28,    29,    30,    31,    32,
       33,    34,    35,    36,    37,    -1,    39,    40,    41,    42,
@@ -1608,17 +1670,17 @@ static const yytype_int16 yycheck[] =
       67,    68,    69,    70,    71,    72,    73,    -1,    75,    76,
       77,    78,    79,    -1,    -1,    82,    83,    84,    85,    86,
       87,    88,    89,    -1,    -1,    -1,    93,    94,    95,    96,
-      97,    98,    99,   100,    -1,    -1,   103,    -1,   105,     4,
+      97,    98,    99,   100,    -1,   102,   103,    -1,   105,     4,
        5,     6,     7,     8,     9,    10,    11,    12,    13,    14,
       15,    16,    17,    18,    19,    20,    21,    22,    23,    24,
       25,    26,    27,    28,    29,    30,    31,    32,    33,    34,
       35,    36,    37,    -1,    39,    40,    41,    42,    43,    44,
-      -1,    -1,    -1,    -1,    -1,    -1,    51,    -1,    53,    54,
-      -1,    -1,    -1,    58,    59,    60,    61,    62,    63,    64,
-      65,    66,    67,    68,    69,    70,    71,    72,    73,    74,
-      75,    76,    77,    78,    79,    80,    81,    82,    83,    84,
-      85,    86,    87,    88,    89,    -1,    -1,    -1,    93,    -1,
-      -1,    -1,    97,    98,    -1,    -1,    -1,    -1,   103,    -1,
+      45,    46,    47,    48,    49,    50,    51,    52,    53,    54,
+      55,    56,    57,    58,    59,    60,    61,    62,    63,    64,
+      65,    66,    67,    68,    69,    70,    71,    72,    73,    -1,
+      75,    76,    77,    78,    79,    -1,    -1,    82,    83,    84,
+      85,    86,    87,    88,    89,    -1,    -1,    -1,    93,    94,
+      95,    96,    97,    98,    99,   100,    -1,    -1,   103,    -1,
      105,     4,     5,     6,     7,     8,     9,    10,    11,    12,
       13,    14,    15,    16,    17,    18,    19,    20,    21,    22,
       23,    24,    25,    26,    27,    28,    29,    30,    31,    32,
@@ -1626,9 +1688,9 @@ static const yytype_int16 yycheck[] =
       43,    44,    -1,    -1,    -1,    -1,    -1,    -1,    51,    -1,
       53,    54,    -1,    -1,    -1,    58,    59,    60,    61,    62,
       63,    64,    65,    66,    67,    68,    69,    70,    71,    72,
-      73,    -1,    75,    76,    77,    78,    79,    -1,    -1,    82,
+      73,    74,    75,    76,    77,    78,    79,    80,    81,    82,
       83,    84,    85,    86,    87,    88,    89,    -1,    -1,    -1,
-      93,    -1,    -1,    -1,    97,    98,    -1,    -1,   101,    -1,
+      93,    -1,    -1,    -1,    97,    98,    -1,    -1,    -1,    -1,
      103,    -1,   105,     4,     5,     6,     7,     8,     9,    10,
       11,    12,    13,    14,    15,    16,    17,    18,    19,    20,
       21,    22,    23,    24,    25,    26,    27,    28,    29,    30,
@@ -1771,45 +1833,8 @@ static const yytype_int16 yycheck[] =
       65,    66,    67,    68,    69,    70,    71,    72,    73,    -1,
       75,    76,    77,    78,    79,    -1,    -1,    82,    83,    84,
       85,    86,    87,    88,    89,    -1,    -1,    -1,    93,    -1,
-      -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,   103,     6,
-     105,    -1,     9,    10,    11,    12,    13,    14,    15,    16,
-      17,    -1,    19,    20,    21,    22,    23,    24,    25,    26,
-      27,    28,    29,    30,    31,    32,    33,    34,    35,    -1,
-      37,    38,    39,    40,    41,    42,    43,    44,    -1,    -1,
-      -1,    -1,    -1,    -1,    -1,    -1,    53,    54,    -1,    -1,
-      -1,    58,    59,    60,    61,    62,    63,    64,    65,    66,
-      67,    68,    69,    70,    71,    72,    73,    -1,    75,    76,
-      77,    78,    79,    -1,    -1,    82,    83,    84,    85,    86,
-      87,    88,    -1,    90,    -1,    -1,    93,    -1,    -1,    -1,
-      -1,     6,    -1,    -1,   101,    10,    11,    12,    13,    14,
-      15,    16,    17,    -1,    19,    20,    21,    22,    23,    24,
-      25,    26,    -1,    28,    29,    30,    31,    32,    33,    34,
-      35,    -1,    37,    38,    39,    40,    -1,    42,    43,    44,
-      -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    53,    54,
-      -1,    -1,    -1,    58,    59,    60,    61,    62,    63,    64,
-      65,    66,    67,    68,    69,    70,    71,    72,    73,    -1,
-      75,    76,    77,    78,    79,    -1,    -1,    82,    83,    84,
-      85,    86,    87,    -1,    -1,    90,    -1,    -1,    93,    -1,
-      -1,    -1,    -1,     6,    -1,    -1,   101,    10,    11,    12,
-      13,    14,    15,    16,    17,    -1,    19,    20,    21,    22,
-      23,    24,    25,    26,    -1,    28,    29,    30,    31,    32,
-      33,    34,    35,    -1,    37,    38,    39,    40,    -1,    42,
-      43,    44,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
-      53,    54,    -1,    -1,    -1,    58,    59,    60,    61,    62,
-      63,    64,    65,    66,    67,    68,    69,    70,    71,    72,
-      73,    -1,    75,    76,    77,    78,    79,    -1,    -1,    82,
-      83,    84,    85,    86,    87,    -1,    -1,    90,    -1,    -1,
-      93,    -1,    -1,    -1,    -1,     6,    -1,    -1,   101,    10,
-      11,    12,    13,    14,    15,    16,    17,    -1,    19,    20,
-      21,    22,    23,    24,    25,    26,    -1,    28,    29,    30,
-      31,    32,    33,    34,    35,    -1,    37,    38,    39,    40,
-      -1,    42,    43,    44,    -1,    -1,    -1,    -1,    -1,    -1,
-      -1,    -1,    53,    54,    -1,    -1,    -1,    58,    59,    60,
-      61,    62,    63,    64,    65,    66,    67,    68,    69,    70,
-      71,    72,    73,    -1,    75,    76,    77,    78,    79,    -1,
-      -1,    82,    83,    84,    85,    86,    87,    -1,    -1,    90,
-      -1,    -1,    93,    -1,    -1,    -1,    -1,     6,    -1,    -1,
-     101,    10,    11,    12,    13,    14,    15,    16,    17,    -1,
+      -1,    -1,     1,    -1,    -1,    -1,    -1,     6,   103,    -1,
+     105,    10,    11,    12,    13,    14,    15,    16,    17,    -1,
       19,    20,    21,    22,    23,    24,    25,    26,    -1,    28,
       29,    30,    31,    32,    33,    34,    35,    -1,    37,    38,
       39,    40,    -1,    42,    43,    44,    -1,    -1,    -1,    -1,
@@ -1817,130 +1842,162 @@ static const yytype_int16 yycheck[] =
       59,    60,    61,    62,    63,    64,    65,    66,    67,    68,
       69,    70,    71,    72,    73,    -1,    75,    76,    77,    78,
       79,    -1,    -1,    82,    83,    84,    85,    86,    87,    -1,
-      -1,    90,    -1,    -1,    93,    -1,    -1,    -1,    -1,     6,
-      -1,    -1,   101,    10,    11,    12,    13,    14,    15,    16,
-      17,    -1,    19,    20,    21,    22,    23,    24,    25,    26,
-      -1,    28,    29,    30,    31,    32,    33,    34,    35,    -1,
-      37,    38,    39,    40,    -1,    42,    43,    44,    -1,    -1,
-      -1,    -1,    -1,    -1,    -1,    -1,    53,    54,    -1,    -1,
-      -1,    58,    59,    60,    61,    62,    63,    64,    65,    66,
-      67,    68,    69,    70,    71,    72,    73,    -1,    75,    76,
-      77,    78,    79,    -1,    -1,    82,    83,    84,    85,    86,
-      87,    -1,    -1,    -1,    -1,    -1,    93,    -1,    -1,    -1,
-      -1,    -1,     6,    -1,   101,     9,    10,    11,    12,    13,
-      14,    15,    16,    17,    18,    19,    20,    21,    22,    23,
-      24,    25,    26,    27,    28,    29,    30,    31,    32,    33,
-      34,    35,    36,    37,    -1,    39,    40,    41,    42,    43,
-      44,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    53,
-      54,    -1,    -1,    -1,    58,    59,    60,    61,    62,    63,
-      64,    65,    66,    67,    68,    69,    70,    71,    72,    73,
-      -1,    75,    76,    77,    78,    79,    -1,    -1,    82,    83,
-      84,    85,    86,    87,    88,    89,    -1,     6,    -1,    93,
-       9,    10,    11,    12,    13,    14,    15,    16,    17,    18,
-      19,    20,    21,    22,    23,    24,    25,    26,    27,    28,
-      29,    30,    31,    32,    33,    34,    35,    36,    37,    -1,
-      39,    40,    41,    42,    43,    44,    -1,    -1,    -1,    -1,
-      -1,    -1,    -1,    -1,    53,    54,    -1,    -1,    -1,    58,
-      59,    60,    61,    62,    63,    64,    65,    66,    67,    68,
-      69,    70,    71,    72,    73,    -1,    75,    76,    77,    78,
-      79,    -1,    -1,    82,    83,    84,    85,    86,    87,    88,
-      89,    -1,     6,    -1,    93,     9,    10,    11,    12,    13,
-      14,    15,    16,    17,    18,    19,    20,    21,    22,    23,
-      24,    25,    26,    27,    28,    29,    30,    31,    32,    33,
-      34,    35,    36,    37,    -1,    39,    40,    41,    42,    43,
+      -1,    90,    -1,    -1,    93,    -1,    -1,    -1,    -1,    -1,
+       6,    -1,   101,     9,    10,    11,    12,    13,    14,    15,
+      16,    17,    -1,    19,    20,    21,    22,    23,    24,    25,
+      26,    27,    28,    29,    30,    31,    32,    33,    34,    35,
+      -1,    37,    38,    39,    40,    41,    42,    43,    44,    -1,
+      -1,    -1,    -1,    -1,    -1,    -1,    -1,    53,    54,    -1,
+      -1,    -1,    58,    59,    60,    61,    62,    63,    64,    65,
+      66,    67,    68,    69,    70,    71,    72,    73,    -1,    75,
+      76,    77,    78,    79,    -1,    -1,    82,    83,    84,    85,
+      86,    87,    88,    -1,    90,    -1,    -1,    93,    -1,    -1,
+      -1,    -1,     6,    -1,    -1,   101,    10,    11,    12,    13,
+      14,    15,    16,    17,    -1,    19,    20,    21,    22,    23,
+      24,    25,    26,    -1,    28,    29,    30,    31,    32,    33,
+      34,    35,    -1,    37,    38,    39,    40,    -1,    42,    43,
       44,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    53,
       54,    -1,    -1,    -1,    58,    59,    60,    61,    62,    63,
       64,    65,    66,    67,    68,    69,    70,    71,    72,    73,
       -1,    75,    76,    77,    78,    79,    -1,    -1,    82,    83,
-      84,    85,    86,    87,    88,    89,    -1,     6,    -1,    93,
-       9,    10,    11,    12,    13,    14,    15,    16,    17,    18,
-      19,    20,    21,    22,    23,    24,    25,    26,    27,    28,
-      29,    30,    31,    32,    33,    34,    35,    36,    37,    -1,
-      39,    40,    41,    42,    43,    44,    -1,    -1,    -1,    -1,
-      -1,    -1,    -1,    -1,    53,    54,    -1,    -1,    -1,    58,
-      59,    60,    61,    62,    63,    64,    65,    66,    67,    68,
-      69,    70,    71,    72,    73,    -1,    75,    76,    77,    78,
-      79,    -1,    -1,    82,    83,    84,    85,    86,    87,    88,
-      89,     6,    -1,    -1,    93,    10,    11,    12,    13,    14,
-      15,    16,    17,    -1,    19,    20,    21,    22,    23,    -1,
-      25,    26,    -1,    28,    29,    30,    31,    32,    33,    34,
-      35,    -1,    37,    -1,    39,    40,    -1,    42,    43,    44,
+      84,    85,    86,    87,    -1,    -1,    90,    -1,    -1,    93,
+      -1,    -1,    -1,    -1,     6,    -1,    -1,   101,    10,    11,
+      12,    13,    14,    15,    16,    17,    -1,    19,    20,    21,
+      22,    23,    24,    25,    26,    -1,    28,    29,    30,    31,
+      32,    33,    34,    35,    -1,    37,    38,    39,    40,    -1,
+      42,    43,    44,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
+      -1,    53,    54,    -1,    -1,    -1,    58,    59,    60,    61,
+      62,    63,    64,    65,    66,    67,    68,    69,    70,    71,
+      72,    73,    -1,    75,    76,    77,    78,    79,    -1,    -1,
+      82,    83,    84,    85,    86,    87,    -1,    -1,    90,    -1,
+      -1,    93,    -1,    -1,    -1,    -1,     6,    -1,    -1,   101,
+      10,    11,    12,    13,    14,    15,    16,    17,    -1,    19,
+      20,    21,    22,    23,    24,    25,    26,    -1,    28,    29,
+      30,    31,    32,    33,    34,    35,    -1,    37,    38,    39,
+      40,    -1,    42,    43,    44,    -1,    -1,    -1,    -1,    -1,
+      -1,    -1,    -1,    53,    54,    -1,    -1,    -1,    58,    59,
+      60,    61,    62,    63,    64,    65,    66,    67,    68,    69,
+      70,    71,    72,    73,    -1,    75,    76,    77,    78,    79,
+      -1,    -1,    82,    83,    84,    85,    86,    87,    -1,    -1,
+      90,    -1,    -1,    93,    -1,    -1,    -1,    -1,     6,    -1,
+      -1,   101,    10,    11,    12,    13,    14,    15,    16,    17,
+      -1,    19,    20,    21,    22,    23,    24,    25,    26,    -1,
+      28,    29,    30,    31,    32,    33,    34,    35,    -1,    37,
+      38,    39,    40,    -1,    42,    43,    44,    -1,    -1,    -1,
+      -1,    -1,    -1,    -1,    -1,    53,    54,    -1,    -1,    -1,
+      58,    59,    60,    61,    62,    63,    64,    65,    66,    67,
+      68,    69,    70,    71,    72,    73,    -1,    75,    76,    77,
+      78,    79,    -1,    -1,    82,    83,    84,    85,    86,    87,
+      -1,    -1,    -1,    -1,    -1,    93,    -1,    -1,    -1,    -1,
+      -1,     6,    -1,   101,     9,    10,    11,    12,    13,    14,
+      15,    16,    17,    18,    19,    20,    21,    22,    23,    24,
+      25,    26,    27,    28,    29,    30,    31,    32,    33,    34,
+      35,    36,    37,    -1,    39,    40,    41,    42,    43,    44,
       -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    53,    54,
       -1,    -1,    -1,    58,    59,    60,    61,    62,    63,    64,
       65,    66,    67,    68,    69,    70,    71,    72,    73,    -1,
-      75,    76,    77,    78,    79,    31,    -1,    82,    83,    84,
-      85,    37,    87,    -1,    -1,    -1,    -1,    -1,    93,    45,
-      46,    47,    48,    49,    50,    -1,    52,    31,    -1,    55,
-      56,    57,    -1,    37,    -1,    -1,    -1,    -1,    -1,    -1,
-      31,    45,    46,    47,    48,    49,    50,    -1,    52,    -1,
-      -1,    55,    56,    57,    45,    46,    47,    48,    49,    50,
-      -1,    52,    31,    -1,    55,    56,    57,    -1,    94,    95,
-      96,    97,    98,    99,   100,    -1,    45,    46,    47,    48,
-      49,    50,    -1,    52,    -1,    -1,    55,    56,    57,    -1,
-      94,    95,    96,    97,    98,    99,   100,    31,    -1,    -1,
-      -1,    -1,    -1,    94,    95,    96,    97,    98,    99,   100,
-      -1,    45,    46,    47,    48,    49,    50,    -1,    52,    31,
-      -1,    55,    56,    57,    -1,    94,    95,    96,    97,    98,
-      99,   100,    31,    45,    46,    47,    48,    49,    50,    -1,
-      52,    -1,    -1,    55,    56,    57,    45,    46,    -1,    48,
-      49,    50,    -1,    52,    -1,    -1,    55,    56,    57,    37,
-      94,    95,    96,    97,    98,    99,   100,    45,    46,    47,
-      48,    49,    50,    -1,    52,    -1,    -1,    55,    56,    57,
-      -1,    -1,    94,    95,    96,    97,    98,    99,   100,    -1,
-      -1,    -1,    -1,    36,    -1,    94,    95,    96,    97,    98,
-      99,   100,    45,    46,    47,    48,    49,    50,    -1,    52,
-      -1,    -1,    55,    56,    57,    -1,    94,    95,    96,    97,
-      98,    99,   100,   101,    45,    46,    47,    48,    49,    50,
-      -1,    52,    -1,    -1,    55,    56,    57,    45,    46,    47,
-      48,    49,    50,    -1,    52,    -1,    -1,    55,    56,    57,
-      -1,    94,    95,    96,    97,    98,    99,   100,    45,    46,
-      47,    48,    49,    50,    -1,    52,    -1,    -1,    55,    56,
-      57,    -1,    -1,    94,    95,    96,    97,    98,    99,   100,
-      -1,   102,    -1,   104,    -1,    -1,    94,    95,    96,    97,
-      98,    99,   100,    -1,   102,    -1,   104,    -1,    -1,    -1,
-      -1,    -1,    -1,    -1,    -1,    -1,    -1,    94,    95,    96,
-      97,    98,    99,   100,    -1,    -1,    -1,   104,    45,    46,
+      75,    76,    77,    78,    79,    -1,    -1,    82,    83,    84,
+      85,    86,    87,    88,    89,    -1,     6,    -1,    93,     9,
+      10,    11,    12,    13,    14,    15,    16,    17,    18,    19,
+      20,    21,    22,    23,    24,    25,    26,    27,    28,    29,
+      30,    31,    32,    33,    34,    35,    36,    37,    -1,    39,
+      40,    41,    42,    43,    44,    -1,    -1,    -1,    -1,    -1,
+      -1,    -1,    -1,    53,    54,    -1,    -1,    -1,    58,    59,
+      60,    61,    62,    63,    64,    65,    66,    67,    68,    69,
+      70,    71,    72,    73,    -1,    75,    76,    77,    78,    79,
+      -1,    -1,    82,    83,    84,    85,    86,    87,    88,    89,
+      -1,     6,    -1,    93,     9,    10,    11,    12,    13,    14,
+      15,    16,    17,    18,    19,    20,    21,    22,    23,    24,
+      25,    26,    27,    28,    29,    30,    31,    32,    33,    34,
+      35,    36,    37,    -1,    39,    40,    41,    42,    43,    44,
+      -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    53,    54,
+      -1,    -1,    -1,    58,    59,    60,    61,    62,    63,    64,
+      65,    66,    67,    68,    69,    70,    71,    72,    73,    -1,
+      75,    76,    77,    78,    79,    -1,    -1,    82,    83,    84,
+      85,    86,    87,    88,    89,    -1,     6,    -1,    93,     9,
+      10,    11,    12,    13,    14,    15,    16,    17,    18,    19,
+      20,    21,    22,    23,    24,    25,    26,    27,    28,    29,
+      30,    31,    32,    33,    34,    35,    36,    37,    -1,    39,
+      40,    41,    42,    43,    44,    -1,    -1,    -1,    -1,    -1,
+      -1,    -1,    -1,    53,    54,    -1,    -1,    -1,    58,    59,
+      60,    61,    62,    63,    64,    65,    66,    67,    68,    69,
+      70,    71,    72,    73,    -1,    75,    76,    77,    78,    79,
+      -1,    -1,    82,    83,    84,    85,    86,    87,    88,    89,
+       6,    -1,    -1,    93,    10,    11,    12,    13,    14,    15,
+      16,    17,    -1,    19,    20,    21,    22,    23,    -1,    25,
+      26,    -1,    28,    29,    30,    31,    32,    33,    34,    35,
+      -1,    37,    -1,    39,    40,    -1,    42,    43,    44,    -1,
+      -1,    -1,    -1,    -1,    -1,    -1,    -1,    53,    54,    -1,
+      -1,    -1,    58,    59,    60,    61,    62,    63,    64,    65,
+      66,    67,    68,    69,    70,    71,    72,    73,    17,    75,
+      76,    77,    78,    79,    -1,    -1,    82,    83,    84,    85,
+      -1,    87,    45,    46,    47,    48,    -1,    93,    37,    52,
+      -1,    -1,    55,    56,    57,    -1,    45,    46,    47,    48,
+      49,    50,    -1,    52,    31,    -1,    55,    56,    57,    -1,
+      37,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    45,    46,
       47,    48,    49,    50,    -1,    52,    -1,    -1,    55,    56,
-      57,    45,    46,    47,    48,    49,    50,    -1,    52,    -1,
-      -1,    55,    56,    57,    45,    46,    47,    48,    49,    50,
+      57,    94,    95,    96,    97,    98,    99,   100,    31,    -1,
+      -1,    -1,    -1,    -1,    37,    94,    95,    96,    97,    98,
+      99,   100,    45,    46,    47,    48,    49,    50,    -1,    52,
+      31,    -1,    55,    56,    57,    -1,    37,    94,    95,    96,
+      97,    98,    99,   100,    45,    46,    47,    48,    49,    50,
       -1,    52,    -1,    -1,    55,    56,    57,    -1,    -1,    -1,
-      -1,    -1,    -1,    -1,    -1,    -1,    -1,    94,    95,    96,
-      97,    98,    99,   100,    -1,   102,    -1,    -1,    -1,    -1,
-      94,    95,    96,    97,    98,    99,   100,    -1,   102,    -1,
+      -1,    -1,    -1,    -1,    31,    -1,    -1,    -1,    -1,    -1,
+      37,    94,    95,    96,    97,    98,    99,   100,    45,    46,
+      47,    48,    49,    50,    -1,    52,    31,    -1,    55,    56,
+      57,    -1,    -1,    94,    95,    96,    97,    98,    99,   100,
+      45,    46,    47,    48,    49,    50,    -1,    52,    -1,    -1,
+      55,    56,    57,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
+      31,    -1,    -1,    -1,    -1,    -1,    -1,    94,    95,    96,
+      97,    98,    99,   100,    45,    46,    47,    48,    49,    50,
+      -1,    52,    31,    -1,    55,    56,    57,    -1,    -1,    94,
+      95,    96,    97,    98,    99,   100,    45,    46,    47,    48,
+      49,    50,    -1,    52,    -1,    -1,    55,    56,    57,    -1,
+      -1,    -1,    -1,    -1,    -1,    -1,    31,    -1,    -1,    -1,
       -1,    -1,    -1,    94,    95,    96,    97,    98,    99,   100,
-      -1,   102,    45,    46,    47,    48,    49,    50,    -1,    52,
+      45,    46,    47,    48,    49,    50,    -1,    52,    -1,    -1,
+      55,    56,    57,    36,    -1,    94,    95,    96,    97,    98,
+      99,   100,    45,    46,    47,    48,    49,    50,    -1,    52,
       -1,    -1,    55,    56,    57,    45,    46,    47,    48,    49,
-      50,    -1,    52,    -1,    -1,    55,    56,    57,    45,    46,
-      47,    48,    49,    50,    -1,    52,    -1,    -1,    55,    56,
-      57,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
-      -1,    94,    95,    96,    97,    98,    99,   100,   101,    -1,
-      -1,    -1,    -1,    -1,    94,    95,    96,    97,    98,    99,
-     100,   101,    -1,    -1,    -1,    -1,    -1,    94,    95,    96,
-      97,    98,    99,   100,   101,    45,    46,    47,    48,    49,
-      50,    -1,    52,    -1,    -1,    55,    56,    57,    45,    46,
-      47,    48,    49,    50,    -1,    52,    -1,    -1,    55,    56,
-      57,    45,    46,    47,    48,    49,    50,    -1,    52,    -1,
-      -1,    55,    56,    57,    -1,    -1,    -1,    -1,    -1,    -1,
+      50,    -1,    52,    -1,    -1,    55,    56,    57,    -1,    94,
+      95,    96,    97,    98,    99,   100,    45,    46,    47,    48,
+      49,    50,    -1,    52,    -1,    -1,    55,    56,    57,    -1,
+      -1,    94,    95,    96,    97,    98,    99,   100,    -1,    -1,
       -1,    -1,    -1,    -1,    94,    95,    96,    97,    98,    99,
-     100,   101,    -1,    -1,    -1,    -1,    -1,    94,    95,    96,
-      97,    98,    99,   100,   101,    -1,    -1,    -1,    -1,    -1,
-      94,    95,    96,    97,    98,    99,   100,   101,    45,    46,
-      47,    48,    49,    50,    -1,    52,    -1,    -1,    55,    56,
-      57,    -1,    -1,    -1,    -1,    -1,    45,    46,    47,    48,
+     100,    -1,   102,    -1,   104,    -1,    -1,    -1,    -1,    -1,
+      -1,    -1,    -1,    -1,    -1,    94,    95,    96,    97,    98,
+      99,   100,    -1,   102,    -1,   104,    45,    46,    47,    48,
       49,    50,    -1,    52,    -1,    -1,    55,    56,    57,    45,
       46,    47,    48,    49,    50,    -1,    52,    -1,    -1,    55,
-      56,    57,    -1,    -1,    -1,    -1,    -1,    94,    95,    96,
-      97,    98,    99,   100,   101,    84,    85,    -1,    -1,    -1,
+      56,    57,    45,    46,    47,    48,    49,    50,    -1,    52,
+      -1,    -1,    55,    56,    57,    -1,    -1,    -1,    -1,    -1,
       -1,    -1,    -1,    -1,    -1,    94,    95,    96,    97,    98,
-      99,   100,    -1,    -1,    -1,    -1,    -1,    -1,    94,    95,
-      96,    97,    98,    99,   100,    45,    46,    47,    48,    49,
-      50,    -1,    52,    -1,    -1,    55,    56,    57,    45,    46,
-      -1,    48,    -1,    -1,    -1,    52,    -1,    -1,    55,    56,
-      57,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
+      99,   100,    -1,    -1,    -1,   104,    -1,    -1,    94,    95,
+      96,    97,    98,    99,   100,    -1,   102,    -1,    -1,    -1,
+      -1,    94,    95,    96,    97,    98,    99,   100,    -1,   102,
+      45,    46,    47,    48,    49,    50,    -1,    52,    -1,    -1,
+      55,    56,    57,    45,    46,    47,    48,    49,    50,    -1,
+      52,    -1,    -1,    55,    56,    57,    -1,    -1,    -1,    -1,
+      -1,    45,    46,    47,    48,    49,    50,    -1,    52,    -1,
+      -1,    55,    56,    57,    -1,    -1,    -1,    -1,    -1,    94,
+      95,    96,    97,    98,    99,   100,    -1,   102,    -1,    -1,
+      -1,    -1,    94,    95,    96,    97,    98,    99,   100,   101,
+      84,    85,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
+      94,    95,    96,    97,    98,    99,   100,    45,    46,    47,
+      48,    49,    50,    -1,    52,    -1,    -1,    55,    56,    57,
+      45,    46,    47,    48,    49,    50,    -1,    52,    -1,    -1,
+      55,    56,    57,    45,    46,    -1,    48,    49,    50,    -1,
+      52,    -1,    -1,    55,    56,    57,    -1,    -1,    -1,    -1,
+      -1,    -1,    -1,    -1,    -1,    -1,    94,    95,    96,    97,
+      98,    99,   100,    -1,    -1,    -1,    -1,    -1,    -1,    94,
+      95,    96,    97,    98,    99,   100,    -1,    -1,    -1,    -1,
+      -1,    -1,    94,    95,    96,    97,    98,    99,   100,    45,
+      46,    -1,    48,    -1,    -1,    -1,    52,    -1,    -1,    55,
+      56,    57,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
       -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
-      -1,    -1,    -1,    -1,    94,    95,    96,    97,    98,    99,
-     100,    -1,    -1,    -1,    -1,    -1,    -1,    94,    95,    96,
-      97,    98,    99,   100
+      -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
+      -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    94,    95,
+      96,    97,    98,    99,   100
 };
 
 /* YYSTOS[STATE-NUM] -- The symbol kind of the accessing symbol of
@@ -1959,136 +2016,151 @@ static const yytype_uint8 yystos[] =
       19,    21,    22,    23,    24,    27,    28,    30,    33,    34,
       35,    36,    41,    42,    43,    44,    86,    87,    88,    89,
       93,   119,   120,   121,    10,    11,    14,    15,    58,    60,
-      62,    64,    78,   150,    34,   101,   121,   122,   123,     4,
-       5,     7,     8,    10,    11,    12,    13,    14,    15,    20,
-      29,    51,    58,    60,    62,    64,    78,    83,    97,    98,
-     103,   105,   121,   128,   130,   146,   147,   150,   152,   154,
-     157,   158,   162,   165,   166,   167,   170,   150,   103,   167,
-     121,   101,   167,    34,   128,   167,    39,    40,    78,   121,
-     147,   149,   160,   101,   121,   128,   167,   128,   130,   122,
-     167,   122,   121,   101,   126,     0,   111,   128,   167,   101,
-     102,   103,   153,   166,   153,   153,   153,   167,   167,   167,
-     167,    72,   101,   101,   101,   102,   153,   153,   167,   167,
-     166,    66,    67,    68,    69,    70,    72,    77,   121,   167,
-     166,   166,   104,   147,   167,     4,     5,     7,     8,   107,
-     121,   163,   164,   167,   171,   172,   103,   101,   101,    31,
-      45,    46,    47,    48,    49,    50,    52,    55,    56,    57,
-      94,    95,    96,    97,    98,    99,   100,   102,   129,   146,
-     168,   170,   101,   167,    36,   122,   101,   101,   101,    26,
-      53,    54,   121,   167,   121,    37,    47,   132,   101,   101,
-      39,   101,   101,   101,    37,   101,   101,   122,   101,   121,
-     104,   167,    31,    37,    31,    37,    31,    37,    31,    37,
-      58,    60,    62,    64,   151,   121,    31,   168,    71,    79,
-      71,    79,    71,    79,    71,    79,    71,    79,    31,    31,
-      84,    85,   168,   102,   104,   107,   107,   107,   107,   106,
-     107,   102,   106,   106,   102,   104,   147,   172,    29,   167,
+      62,    64,    78,   150,     1,    34,   101,   121,   122,   123,
+       4,     5,     7,     8,    10,    11,    12,    13,    14,    15,
+      20,    29,    51,    58,    60,    62,    64,    78,    83,    97,
+      98,   103,   105,   121,   128,   130,   146,   147,   150,   152,
+     154,   157,   158,   162,   165,   166,   167,   170,   150,   103,
+     167,   121,     1,   101,   167,    34,   128,   167,    39,    40,
+      78,   121,   147,   149,   160,   101,   121,   128,   167,   128,
+     130,   122,   167,   122,   121,   101,   126,     0,   111,   128,
+     167,   101,   102,   103,   153,   166,   153,   153,   153,   167,
+     167,   167,   167,    72,     1,   101,   101,     1,   101,     1,
+     101,   102,   153,   153,   167,   167,   166,    66,    67,    68,
+      69,    70,    72,    77,   121,   167,   166,   166,   104,   147,
+     167,     4,     5,     7,     8,   107,   121,   163,   164,   167,
+     171,   172,   103,     1,   101,     1,   101,    31,    45,    46,
+      47,    48,    49,    50,    52,    55,    56,    57,    94,    95,
+      96,    97,    98,    99,   100,   102,   129,   146,   168,   170,
+       1,   101,   167,    36,   122,   101,     1,   101,     1,   101,
+       1,   101,    26,    53,    54,   121,   167,   121,    37,    47,
+     132,     1,   101,     1,   101,    39,     1,   101,     1,   101,
+       1,   101,     1,    37,   101,     1,   101,   122,     1,   101,
+     121,   104,   167,    31,    37,    31,    37,    31,    37,    31,
+      37,    58,    60,    62,    64,   151,   101,   101,   101,   121,
+      31,   168,    71,    79,    71,    79,    71,    79,    71,    79,
+      71,    79,    31,    31,    84,    85,   168,   102,   104,   107,
+     107,   107,   107,   106,   107,   102,   106,   106,   102,   104,
+     147,   172,   101,   101,    29,   167,   167,   167,   167,   167,
      167,   167,   167,   167,   167,   167,   167,   167,   167,   167,
-     167,   167,   167,   167,   167,   167,   172,    45,    46,    47,
-      48,    49,    50,    52,    55,    56,    57,    94,    95,    96,
-      97,    98,    99,   100,   102,   129,   101,   124,   101,    78,
-     147,   148,   150,   148,   148,    25,    47,   101,    31,   167,
-       9,    89,   101,   114,   115,   116,   117,   167,   126,   101,
-     124,   140,   141,   142,   101,   102,   166,   167,   166,   167,
-     166,   167,   166,   167,    25,    31,   159,    29,    59,    61,
-      63,    65,    73,    74,    75,    76,    80,    81,    82,    83,
-     155,   156,   166,   155,   166,   166,   102,   129,   104,   172,
-     167,   167,   167,   167,   167,   164,   167,   167,   168,   102,
-     129,   167,   129,   167,   167,   167,   167,   167,   167,   167,
+     167,   167,   172,    45,    46,    47,    48,    49,    50,    52,
+      55,    56,    57,    94,    95,    96,    97,    98,    99,   100,
+     102,   129,   101,     1,   101,   124,     1,   101,   101,   101,
+     101,    78,   147,   148,   150,   148,   148,    25,    47,     1,
+     101,    31,   167,   101,     9,    89,   101,   114,   115,   116,
+     117,   101,   167,   101,   101,   101,   101,   126,   101,   124,
+     140,   141,   142,   101,     1,   101,   101,   102,   166,   167,
+     166,   167,   166,   167,   166,   167,    25,    31,   159,    29,
+      59,    61,    63,    65,    73,    74,    75,    76,    80,    81,
+      82,    83,   155,   156,   166,   155,   166,   166,   102,   129,
+     104,   172,   167,   167,   167,   167,   167,   164,   167,   167,
+     168,   102,   129,   167,   129,   167,   167,   167,   167,   167,
      167,   167,   167,   167,   167,   167,   167,   167,   167,   167,
-     172,   101,   125,   143,   144,   145,    18,   140,   101,   101,
-     101,   167,   167,   140,    73,    75,    82,    83,   161,   166,
-     101,   119,   121,   117,   101,    92,   142,   140,   104,   172,
-      31,    31,    31,    31,   166,   167,   159,   159,   159,   159,
-     166,   166,   159,   166,   166,   172,   104,   129,   102,   129,
-     104,   129,    18,    90,   136,   145,   101,   124,   101,    17,
-      37,    91,   166,   169,   170,   169,   169,   169,   101,   122,
-     101,     9,   129,   166,   166,   166,   166,   166,   166,   166,
-     166,    31,    73,   156,   129,   104,   172,   104,   101,   124,
-     101,   143,   140,    37,   167,   101,    45,    46,    48,    49,
-      50,    52,    55,    56,    57,    94,    95,    96,    97,    98,
-      99,   100,    31,   101,   119,   104,    73,   104,   129,   143,
-     136,    91,   167,   101,   169,   169,   169,   169,   169,   169,
+     167,   167,   172,   101,   101,   125,   143,   144,   145,    18,
+     101,   140,     1,   101,     1,   101,     1,   101,   167,   167,
+     101,   140,    73,    75,    82,    83,   161,   166,     1,   101,
+     115,   119,   121,   117,   101,   140,    92,   142,   101,   140,
+     104,   172,    31,    31,    31,    31,   166,   167,   159,   159,
+     159,   159,   166,   166,   159,   166,   166,   172,   104,   129,
+     102,   129,   104,   129,   143,    18,    90,   136,   145,   101,
+     124,   140,   101,   101,   101,     1,   101,    17,    37,   140,
+      91,   166,   169,   170,   169,   169,   169,   101,   101,   122,
+      92,   101,   140,     9,   129,   166,   166,   166,   166,   166,
+     166,   166,   166,    31,    73,   156,   129,   104,   172,   104,
+     136,   101,   124,   101,   143,   101,   140,    37,   167,    91,
+     101,    45,    46,    48,    49,    50,    52,    55,    56,    57,
+      94,    95,    96,    97,    98,    99,   100,    31,     1,   101,
+     101,     9,   119,   104,    73,   104,   129,   143,   136,   140,
+      91,   167,     1,   101,   101,   169,   169,   169,   169,   169,
      169,   169,   169,   169,   169,   169,   169,   169,   169,   169,
-      73,   140,   101,   166,   104,   136,   101,   101,   140,   169,
-     140,    91,    91,   101,   101
+     169,    73,   101,   140,   119,   101,   166,   104,   136,    91,
+     101,     1,   101,   101,   140,   169,   140,   101,   101,   101,
+     140,   140,    91,   140,    91,    91,   101,    91,   101,   101,
+     101
 };
 
 /* YYR1[RULE-NUM] -- Symbol kind of the left-hand side of rule RULE-NUM.  */
 static const yytype_uint8 yyr1[] =
 {
        0,   108,   109,   110,   110,   111,   111,   111,   111,   111,
-     111,   112,   113,   114,   115,   115,   116,   116,   117,   117,
-     117,   118,   118,   119,   119,   119,   120,   120,   120,   120,
+     111,   112,   112,   113,   113,   114,   114,   115,   115,   116,
+     116,   117,   117,   117,   118,   118,   118,   118,   119,   119,
+     119,   120,   120,   120,   120,   120,   120,   120,   120,   120,
      120,   120,   120,   120,   120,   120,   120,   120,   120,   120,
      120,   120,   120,   120,   120,   120,   120,   120,   120,   120,
      120,   120,   120,   120,   120,   120,   120,   120,   120,   120,
      120,   120,   120,   120,   120,   120,   120,   120,   120,   120,
-     120,   120,   120,   120,   120,   121,   121,   121,   121,   121,
      121,   121,   121,   121,   121,   121,   121,   121,   121,   121,
-     121,   121,   121,   121,   121,   122,   122,   122,   123,   123,
-     124,   124,   125,   125,   125,   125,   125,   125,   126,   126,
-     126,   127,   127,   127,   127,   127,   127,   127,   127,   127,
-     127,   127,   127,   127,   127,   127,   127,   128,   128,   128,
-     128,   128,   128,   128,   128,   128,   128,   129,   129,   130,
-     130,   130,   130,   130,   131,   131,   131,   131,   132,   132,
-     133,   133,   133,   134,   134,   135,   135,   135,   135,   136,
-     136,   137,   137,   137,   137,   138,   138,   139,   140,   140,
-     141,   141,   142,   142,   143,   143,   144,   144,   145,   145,
-     146,   146,   146,   146,   146,   146,   146,   146,   146,   146,
-     146,   146,   146,   146,   146,   147,   148,   148,   149,   149,
-     150,   150,   150,   150,   150,   150,   150,   150,   150,   150,
-     150,   150,   150,   151,   151,   151,   151,   152,   152,   153,
-     153,   153,   153,   154,   154,   154,   154,   154,   155,   155,
-     155,   155,   155,   156,   157,   157,   157,   157,   157,   157,
-     157,   157,   157,   157,   158,   158,   158,   158,   158,   158,
-     158,   158,   159,   159,   160,   160,   161,   161,   161,   161,
-     161,   162,   162,   162,   163,   163,   163,   164,   164,   164,
-     164,   164,   165,   165,   166,   166,   167,   167,   167,   167,
+     121,   121,   121,   121,   121,   121,   121,   121,   121,   121,
+     122,   122,   122,   123,   123,   124,   124,   125,   125,   125,
+     125,   125,   125,   126,   126,   126,   127,   127,   127,   127,
+     127,   127,   127,   127,   127,   127,   127,   127,   127,   127,
+     127,   127,   127,   127,   127,   127,   127,   127,   127,   127,
+     127,   127,   127,   127,   127,   127,   128,   128,   128,   128,
+     128,   128,   128,   128,   128,   128,   129,   129,   130,   130,
+     130,   130,   130,   131,   131,   131,   131,   131,   131,   131,
+     131,   132,   132,   133,   133,   133,   133,   133,   133,   134,
+     134,   134,   135,   135,   135,   135,   136,   136,   137,   137,
+     137,   137,   137,   137,   137,   137,   138,   138,   138,   139,
+     140,   140,   141,   141,   142,   142,   143,   143,   144,   144,
+     145,   145,   146,   146,   146,   146,   146,   146,   146,   146,
+     146,   146,   146,   146,   146,   146,   146,   147,   148,   148,
+     149,   149,   150,   150,   150,   150,   150,   150,   150,   150,
+     150,   150,   150,   150,   150,   151,   151,   151,   151,   152,
+     152,   153,   153,   153,   153,   154,   154,   154,   154,   154,
+     155,   155,   155,   155,   155,   156,   157,   157,   157,   157,
+     157,   157,   157,   157,   157,   157,   158,   158,   158,   158,
+     158,   158,   158,   158,   159,   159,   160,   160,   161,   161,
+     161,   161,   161,   162,   162,   162,   163,   163,   163,   164,
+     164,   164,   164,   164,   165,   165,   166,   166,   167,   167,
      167,   167,   167,   167,   167,   167,   167,   167,   167,   167,
-     167,   167,   167,   167,   167,   168,   168,   168,   168,   168,
+     167,   167,   167,   167,   167,   167,   167,   168,   168,   168,
      168,   168,   168,   168,   168,   168,   168,   168,   168,   168,
-     168,   168,   168,   168,   169,   169,   169,   169,   169,   169,
+     168,   168,   168,   168,   168,   168,   169,   169,   169,   169,
      169,   169,   169,   169,   169,   169,   169,   169,   169,   169,
-     169,   169,   170,   170,   171,   171,   172,   172
+     169,   169,   169,   169,   170,   170,   171,   171,   172,   172
 };
 
 /* YYR2[RULE-NUM] -- Number of symbols on the right-hand side of rule RULE-NUM.  */
 static const yytype_int8 yyr2[] =
 {
        0,     2,     1,     1,     2,     1,     1,     1,     1,     1,
-       3,     5,     4,     5,     0,     1,     1,     2,     1,     1,
-       3,     8,     5,     0,     1,     3,     1,     1,     1,     1,
-       1,     1,     1,     1,     1,     1,     1,     1,     1,     1,
+       3,     5,     6,     4,     5,     5,     6,     0,     1,     1,
+       2,     1,     1,     3,     8,     9,     5,     6,     0,     1,
+       3,     1,     1,     1,     1,     1,     1,     1,     1,     1,
        1,     1,     1,     1,     1,     1,     1,     1,     1,     1,
        1,     1,     1,     1,     1,     1,     1,     1,     1,     1,
        1,     1,     1,     1,     1,     1,     1,     1,     1,     1,
        1,     1,     1,     1,     1,     1,     1,     1,     1,     1,
        1,     1,     1,     1,     1,     1,     1,     1,     1,     1,
-       1,     1,     1,     1,     1,     0,     1,     2,     1,     3,
-       1,     2,     1,     1,     1,     1,     1,     1,     1,     1,
-       1,     3,     3,     3,     3,     3,     3,     3,     5,     3,
-       3,     2,     2,     3,     3,     3,     2,     0,     2,     4,
-       3,     5,     2,     4,     6,     5,     7,     0,     1,     2,
-       2,     5,     4,     3,     5,     5,     5,     5,     1,     1,
-       3,     3,     3,     4,     6,     6,     8,     7,     9,     0,
-       2,     7,    11,    12,     9,     4,     6,     2,     0,     1,
-       1,     2,     1,     1,     0,     1,     1,     2,     1,     1,
-       1,     1,     1,     1,     2,     3,     5,     6,     8,     3,
        1,     1,     1,     1,     1,     1,     1,     1,     1,     1,
-       2,     2,     2,     2,     4,     6,     4,     6,     4,     6,
-       4,     6,     5,     1,     1,     1,     1,     2,     2,     1,
-       2,     4,     6,     2,     4,     4,     1,     1,     1,     1,
-       5,     2,     2,     2,     3,     3,     3,     3,     3,     3,
-       3,     3,     3,     3,     6,     6,     6,     6,     6,     4,
-       4,     4,     1,     1,     2,     4,     1,     2,     5,     2,
-       2,     3,     3,     3,     1,     3,     3,     3,     3,     3,
-       3,     3,     2,     2,     1,     1,     1,     1,     3,     3,
+       0,     1,     2,     1,     3,     1,     2,     1,     1,     1,
+       1,     1,     1,     1,     1,     1,     3,     4,     3,     4,
+       3,     4,     3,     4,     3,     4,     3,     4,     3,     4,
+       5,     3,     4,     3,     4,     2,     3,     2,     3,     3,
+       4,     3,     4,     3,     4,     2,     0,     2,     4,     3,
+       5,     2,     4,     6,     5,     7,     0,     1,     2,     2,
+       5,     4,     3,     5,     6,     5,     6,     5,     6,     5,
+       6,     1,     1,     3,     4,     3,     4,     3,     4,     4,
+       6,     7,     6,     8,     7,     9,     0,     2,     7,     8,
+      11,    12,    12,    13,     9,    10,     4,     6,     7,     2,
+       0,     1,     1,     2,     1,     1,     0,     1,     1,     2,
+       1,     1,     1,     1,     1,     1,     2,     3,     5,     6,
+       8,     3,     1,     1,     1,     1,     1,     1,     1,     1,
+       1,     1,     2,     2,     2,     2,     4,     6,     4,     6,
+       4,     6,     4,     6,     5,     1,     1,     1,     1,     2,
+       2,     1,     2,     4,     6,     2,     4,     4,     1,     1,
+       1,     1,     5,     2,     2,     2,     3,     3,     3,     3,
+       3,     3,     3,     3,     3,     3,     6,     6,     6,     6,
+       6,     4,     4,     4,     1,     1,     2,     4,     1,     2,
+       5,     2,     2,     3,     3,     3,     1,     3,     3,     3,
+       3,     3,     3,     3,     2,     2,     1,     1,     1,     1,
        3,     3,     3,     3,     3,     3,     3,     3,     3,     3,
-       3,     3,     3,     3,     3,     1,     1,     3,     3,     3,
+       3,     3,     3,     3,     3,     3,     3,     1,     1,     3,
        3,     3,     3,     3,     3,     3,     3,     3,     3,     3,
-       3,     3,     3,     3,     1,     1,     3,     3,     3,     3,
+       3,     3,     3,     3,     3,     3,     1,     1,     3,     3,
        3,     3,     3,     3,     3,     3,     3,     3,     3,     3,
-       3,     3,     4,     4,     0,     1,     1,     3
+       3,     3,     3,     3,     4,     4,     0,     1,     1,     3
 };
 
 
@@ -2354,285 +2426,285 @@ yydestruct (const char *yymsg,
   switch (yykind)
     {
     case YYSYMBOL_tVARID: /* tVARID  */
-#line 204 "engines/director/lingo/lingo-gr.y"
+#line 218 "engines/director/lingo/lingo-gr.y"
             { delete ((*yyvaluep).s); }
-#line 2360 "engines/director/lingo/lingo-gr.cpp"
+#line 2432 "engines/director/lingo/lingo-gr.cpp"
         break;
 
     case YYSYMBOL_tSTRING: /* tSTRING  */
-#line 204 "engines/director/lingo/lingo-gr.y"
+#line 218 "engines/director/lingo/lingo-gr.y"
             { delete ((*yyvaluep).s); }
-#line 2366 "engines/director/lingo/lingo-gr.cpp"
+#line 2438 "engines/director/lingo/lingo-gr.cpp"
         break;
 
     case YYSYMBOL_tSYMBOL: /* tSYMBOL  */
-#line 204 "engines/director/lingo/lingo-gr.y"
+#line 218 "engines/director/lingo/lingo-gr.y"
             { delete ((*yyvaluep).s); }
-#line 2372 "engines/director/lingo/lingo-gr.cpp"
+#line 2444 "engines/director/lingo/lingo-gr.cpp"
         break;
 
     case YYSYMBOL_tENDCLAUSE: /* tENDCLAUSE  */
-#line 204 "engines/director/lingo/lingo-gr.y"
+#line 218 "engines/director/lingo/lingo-gr.y"
             { delete ((*yyvaluep).s); }
-#line 2378 "engines/director/lingo/lingo-gr.cpp"
+#line 2450 "engines/director/lingo/lingo-gr.cpp"
         break;
 
     case YYSYMBOL_script: /* script  */
-#line 205 "engines/director/lingo/lingo-gr.y"
+#line 219 "engines/director/lingo/lingo-gr.y"
             { delete ((*yyvaluep).node); }
-#line 2384 "engines/director/lingo/lingo-gr.cpp"
+#line 2456 "engines/director/lingo/lingo-gr.cpp"
         break;
 
     case YYSYMBOL_scriptpart: /* scriptpart  */
-#line 205 "engines/director/lingo/lingo-gr.y"
+#line 219 "engines/director/lingo/lingo-gr.y"
             { delete ((*yyvaluep).node); }
-#line 2390 "engines/director/lingo/lingo-gr.cpp"
+#line 2462 "engines/director/lingo/lingo-gr.cpp"
         break;
 
     case YYSYMBOL_macro: /* macro  */
-#line 205 "engines/director/lingo/lingo-gr.y"
+#line 219 "engines/director/lingo/lingo-gr.y"
             { delete ((*yyvaluep).node); }
-#line 2396 "engines/director/lingo/lingo-gr.cpp"
+#line 2468 "engines/director/lingo/lingo-gr.cpp"
         break;
 
     case YYSYMBOL_factory: /* factory  */
-#line 205 "engines/director/lingo/lingo-gr.y"
+#line 219 "engines/director/lingo/lingo-gr.y"
             { delete ((*yyvaluep).node); }
-#line 2402 "engines/director/lingo/lingo-gr.cpp"
+#line 2474 "engines/director/lingo/lingo-gr.cpp"
         break;
 
     case YYSYMBOL_method: /* method  */
-#line 205 "engines/director/lingo/lingo-gr.y"
+#line 219 "engines/director/lingo/lingo-gr.y"
             { delete ((*yyvaluep).node); }
-#line 2408 "engines/director/lingo/lingo-gr.cpp"
+#line 2480 "engines/director/lingo/lingo-gr.cpp"
         break;
 
     case YYSYMBOL_methodlistline: /* methodlistline  */
-#line 205 "engines/director/lingo/lingo-gr.y"
+#line 219 "engines/director/lingo/lingo-gr.y"
             { delete ((*yyvaluep).node); }
-#line 2414 "engines/director/lingo/lingo-gr.cpp"
+#line 2486 "engines/director/lingo/lingo-gr.cpp"
         break;
 
     case YYSYMBOL_handler: /* handler  */
-#line 205 "engines/director/lingo/lingo-gr.y"
+#line 219 "engines/director/lingo/lingo-gr.y"
             { delete ((*yyvaluep).node); }
-#line 2420 "engines/director/lingo/lingo-gr.cpp"
+#line 2492 "engines/director/lingo/lingo-gr.cpp"
         break;
 
     case YYSYMBOL_CMDID: /* CMDID  */
-#line 204 "engines/director/lingo/lingo-gr.y"
+#line 218 "engines/director/lingo/lingo-gr.y"
             { delete ((*yyvaluep).s); }
-#line 2426 "engines/director/lingo/lingo-gr.cpp"
+#line 2498 "engines/director/lingo/lingo-gr.cpp"
         break;
 
     case YYSYMBOL_ID: /* ID  */
-#line 204 "engines/director/lingo/lingo-gr.y"
+#line 218 "engines/director/lingo/lingo-gr.y"
             { delete ((*yyvaluep).s); }
-#line 2432 "engines/director/lingo/lingo-gr.cpp"
+#line 2504 "engines/director/lingo/lingo-gr.cpp"
         break;
 
     case YYSYMBOL_stmt: /* stmt  */
-#line 205 "engines/director/lingo/lingo-gr.y"
+#line 219 "engines/director/lingo/lingo-gr.y"
             { delete ((*yyvaluep).node); }
-#line 2438 "engines/director/lingo/lingo-gr.cpp"
+#line 2510 "engines/director/lingo/lingo-gr.cpp"
         break;
 
     case YYSYMBOL_stmt_insideif: /* stmt_insideif  */
-#line 205 "engines/director/lingo/lingo-gr.y"
+#line 219 "engines/director/lingo/lingo-gr.y"
             { delete ((*yyvaluep).node); }
-#line 2444 "engines/director/lingo/lingo-gr.cpp"
+#line 2516 "engines/director/lingo/lingo-gr.cpp"
         break;
 
     case YYSYMBOL_stmtoneliner: /* stmtoneliner  */
-#line 205 "engines/director/lingo/lingo-gr.y"
+#line 219 "engines/director/lingo/lingo-gr.y"
             { delete ((*yyvaluep).node); }
-#line 2450 "engines/director/lingo/lingo-gr.cpp"
+#line 2522 "engines/director/lingo/lingo-gr.cpp"
         break;
 
     case YYSYMBOL_proc: /* proc  */
-#line 205 "engines/director/lingo/lingo-gr.y"
+#line 219 "engines/director/lingo/lingo-gr.y"
             { delete ((*yyvaluep).node); }
-#line 2456 "engines/director/lingo/lingo-gr.cpp"
+#line 2528 "engines/director/lingo/lingo-gr.cpp"
         break;
 
     case YYSYMBOL_asgn: /* asgn  */
-#line 205 "engines/director/lingo/lingo-gr.y"
+#line 219 "engines/director/lingo/lingo-gr.y"
             { delete ((*yyvaluep).node); }
-#line 2462 "engines/director/lingo/lingo-gr.cpp"
+#line 2534 "engines/director/lingo/lingo-gr.cpp"
         break;
 
     case YYSYMBOL_definevars: /* definevars  */
-#line 205 "engines/director/lingo/lingo-gr.y"
+#line 219 "engines/director/lingo/lingo-gr.y"
             { delete ((*yyvaluep).node); }
-#line 2468 "engines/director/lingo/lingo-gr.cpp"
+#line 2540 "engines/director/lingo/lingo-gr.cpp"
         break;
 
     case YYSYMBOL_ifstmt: /* ifstmt  */
-#line 205 "engines/director/lingo/lingo-gr.y"
+#line 219 "engines/director/lingo/lingo-gr.y"
             { delete ((*yyvaluep).node); }
-#line 2474 "engines/director/lingo/lingo-gr.cpp"
+#line 2546 "engines/director/lingo/lingo-gr.cpp"
         break;
 
     case YYSYMBOL_ifelsestmt: /* ifelsestmt  */
-#line 205 "engines/director/lingo/lingo-gr.y"
+#line 219 "engines/director/lingo/lingo-gr.y"
             { delete ((*yyvaluep).node); }
-#line 2480 "engines/director/lingo/lingo-gr.cpp"
+#line 2552 "engines/director/lingo/lingo-gr.cpp"
         break;
 
     case YYSYMBOL_loop: /* loop  */
-#line 205 "engines/director/lingo/lingo-gr.y"
+#line 219 "engines/director/lingo/lingo-gr.y"
             { delete ((*yyvaluep).node); }
-#line 2486 "engines/director/lingo/lingo-gr.cpp"
+#line 2558 "engines/director/lingo/lingo-gr.cpp"
         break;
 
     case YYSYMBOL_tell: /* tell  */
-#line 205 "engines/director/lingo/lingo-gr.y"
+#line 219 "engines/director/lingo/lingo-gr.y"
             { delete ((*yyvaluep).node); }
-#line 2492 "engines/director/lingo/lingo-gr.cpp"
+#line 2564 "engines/director/lingo/lingo-gr.cpp"
         break;
 
     case YYSYMBOL_when: /* when  */
-#line 205 "engines/director/lingo/lingo-gr.y"
+#line 219 "engines/director/lingo/lingo-gr.y"
             { delete ((*yyvaluep).node); }
-#line 2498 "engines/director/lingo/lingo-gr.cpp"
+#line 2570 "engines/director/lingo/lingo-gr.cpp"
         break;
 
     case YYSYMBOL_stmtlistline: /* stmtlistline  */
-#line 205 "engines/director/lingo/lingo-gr.y"
+#line 219 "engines/director/lingo/lingo-gr.y"
             { delete ((*yyvaluep).node); }
-#line 2504 "engines/director/lingo/lingo-gr.cpp"
+#line 2576 "engines/director/lingo/lingo-gr.cpp"
         break;
 
     case YYSYMBOL_stmtlistline_insideif: /* stmtlistline_insideif  */
-#line 205 "engines/director/lingo/lingo-gr.y"
+#line 219 "engines/director/lingo/lingo-gr.y"
             { delete ((*yyvaluep).node); }
-#line 2510 "engines/director/lingo/lingo-gr.cpp"
+#line 2582 "engines/director/lingo/lingo-gr.cpp"
         break;
 
     case YYSYMBOL_simpleexpr_nounarymath: /* simpleexpr_nounarymath  */
-#line 205 "engines/director/lingo/lingo-gr.y"
+#line 219 "engines/director/lingo/lingo-gr.y"
             { delete ((*yyvaluep).node); }
-#line 2516 "engines/director/lingo/lingo-gr.cpp"
+#line 2588 "engines/director/lingo/lingo-gr.cpp"
         break;
 
     case YYSYMBOL_var: /* var  */
-#line 205 "engines/director/lingo/lingo-gr.y"
+#line 219 "engines/director/lingo/lingo-gr.y"
             { delete ((*yyvaluep).node); }
-#line 2522 "engines/director/lingo/lingo-gr.cpp"
+#line 2594 "engines/director/lingo/lingo-gr.cpp"
         break;
 
     case YYSYMBOL_varorchunk: /* varorchunk  */
-#line 205 "engines/director/lingo/lingo-gr.y"
+#line 219 "engines/director/lingo/lingo-gr.y"
             { delete ((*yyvaluep).node); }
-#line 2528 "engines/director/lingo/lingo-gr.cpp"
+#line 2600 "engines/director/lingo/lingo-gr.cpp"
         break;
 
     case YYSYMBOL_varorthe: /* varorthe  */
-#line 205 "engines/director/lingo/lingo-gr.y"
+#line 219 "engines/director/lingo/lingo-gr.y"
             { delete ((*yyvaluep).node); }
-#line 2534 "engines/director/lingo/lingo-gr.cpp"
+#line 2606 "engines/director/lingo/lingo-gr.cpp"
         break;
 
     case YYSYMBOL_chunk: /* chunk  */
-#line 205 "engines/director/lingo/lingo-gr.y"
+#line 219 "engines/director/lingo/lingo-gr.y"
             { delete ((*yyvaluep).node); }
-#line 2540 "engines/director/lingo/lingo-gr.cpp"
+#line 2612 "engines/director/lingo/lingo-gr.cpp"
         break;
 
     case YYSYMBOL_object: /* object  */
-#line 205 "engines/director/lingo/lingo-gr.y"
+#line 219 "engines/director/lingo/lingo-gr.y"
             { delete ((*yyvaluep).node); }
-#line 2546 "engines/director/lingo/lingo-gr.cpp"
+#line 2618 "engines/director/lingo/lingo-gr.cpp"
         break;
 
     case YYSYMBOL_the: /* the  */
-#line 205 "engines/director/lingo/lingo-gr.y"
+#line 219 "engines/director/lingo/lingo-gr.y"
             { delete ((*yyvaluep).node); }
-#line 2552 "engines/director/lingo/lingo-gr.cpp"
+#line 2624 "engines/director/lingo/lingo-gr.cpp"
         break;
 
     case YYSYMBOL_theobj: /* theobj  */
-#line 205 "engines/director/lingo/lingo-gr.y"
+#line 219 "engines/director/lingo/lingo-gr.y"
             { delete ((*yyvaluep).node); }
-#line 2558 "engines/director/lingo/lingo-gr.cpp"
+#line 2630 "engines/director/lingo/lingo-gr.cpp"
         break;
 
     case YYSYMBOL_menu: /* menu  */
-#line 205 "engines/director/lingo/lingo-gr.y"
+#line 219 "engines/director/lingo/lingo-gr.y"
             { delete ((*yyvaluep).node); }
-#line 2564 "engines/director/lingo/lingo-gr.cpp"
+#line 2636 "engines/director/lingo/lingo-gr.cpp"
         break;
 
     case YYSYMBOL_thedatetime: /* thedatetime  */
-#line 205 "engines/director/lingo/lingo-gr.y"
+#line 219 "engines/director/lingo/lingo-gr.y"
             { delete ((*yyvaluep).node); }
-#line 2570 "engines/director/lingo/lingo-gr.cpp"
+#line 2642 "engines/director/lingo/lingo-gr.cpp"
         break;
 
     case YYSYMBOL_thenumberof: /* thenumberof  */
-#line 205 "engines/director/lingo/lingo-gr.y"
+#line 219 "engines/director/lingo/lingo-gr.y"
             { delete ((*yyvaluep).node); }
-#line 2576 "engines/director/lingo/lingo-gr.cpp"
+#line 2648 "engines/director/lingo/lingo-gr.cpp"
         break;
 
     case YYSYMBOL_writablethe: /* writablethe  */
-#line 205 "engines/director/lingo/lingo-gr.y"
+#line 219 "engines/director/lingo/lingo-gr.y"
             { delete ((*yyvaluep).node); }
-#line 2582 "engines/director/lingo/lingo-gr.cpp"
+#line 2654 "engines/director/lingo/lingo-gr.cpp"
         break;
 
     case YYSYMBOL_writabletheobj: /* writabletheobj  */
-#line 205 "engines/director/lingo/lingo-gr.y"
+#line 219 "engines/director/lingo/lingo-gr.y"
             { delete ((*yyvaluep).node); }
-#line 2588 "engines/director/lingo/lingo-gr.cpp"
+#line 2660 "engines/director/lingo/lingo-gr.cpp"
         break;
 
     case YYSYMBOL_list: /* list  */
-#line 205 "engines/director/lingo/lingo-gr.y"
+#line 219 "engines/director/lingo/lingo-gr.y"
             { delete ((*yyvaluep).node); }
-#line 2594 "engines/director/lingo/lingo-gr.cpp"
+#line 2666 "engines/director/lingo/lingo-gr.cpp"
         break;
 
     case YYSYMBOL_proppair: /* proppair  */
-#line 205 "engines/director/lingo/lingo-gr.y"
+#line 219 "engines/director/lingo/lingo-gr.y"
             { delete ((*yyvaluep).node); }
-#line 2600 "engines/director/lingo/lingo-gr.cpp"
+#line 2672 "engines/director/lingo/lingo-gr.cpp"
         break;
 
     case YYSYMBOL_unarymath: /* unarymath  */
-#line 205 "engines/director/lingo/lingo-gr.y"
+#line 219 "engines/director/lingo/lingo-gr.y"
             { delete ((*yyvaluep).node); }
-#line 2606 "engines/director/lingo/lingo-gr.cpp"
+#line 2678 "engines/director/lingo/lingo-gr.cpp"
         break;
 
     case YYSYMBOL_simpleexpr: /* simpleexpr  */
-#line 205 "engines/director/lingo/lingo-gr.y"
+#line 219 "engines/director/lingo/lingo-gr.y"
             { delete ((*yyvaluep).node); }
-#line 2612 "engines/director/lingo/lingo-gr.cpp"
+#line 2684 "engines/director/lingo/lingo-gr.cpp"
         break;
 
     case YYSYMBOL_expr: /* expr  */
-#line 205 "engines/director/lingo/lingo-gr.y"
+#line 219 "engines/director/lingo/lingo-gr.y"
             { delete ((*yyvaluep).node); }
-#line 2618 "engines/director/lingo/lingo-gr.cpp"
+#line 2690 "engines/director/lingo/lingo-gr.cpp"
         break;
 
     case YYSYMBOL_expr_nounarymath: /* expr_nounarymath  */
-#line 205 "engines/director/lingo/lingo-gr.y"
+#line 219 "engines/director/lingo/lingo-gr.y"
             { delete ((*yyvaluep).node); }
-#line 2624 "engines/director/lingo/lingo-gr.cpp"
+#line 2696 "engines/director/lingo/lingo-gr.cpp"
         break;
 
     case YYSYMBOL_expr_noeq: /* expr_noeq  */
-#line 205 "engines/director/lingo/lingo-gr.y"
+#line 219 "engines/director/lingo/lingo-gr.y"
             { delete ((*yyvaluep).node); }
-#line 2630 "engines/director/lingo/lingo-gr.cpp"
+#line 2702 "engines/director/lingo/lingo-gr.cpp"
         break;
 
     case YYSYMBOL_sprite: /* sprite  */
-#line 205 "engines/director/lingo/lingo-gr.y"
+#line 219 "engines/director/lingo/lingo-gr.y"
             { delete ((*yyvaluep).node); }
-#line 2636 "engines/director/lingo/lingo-gr.cpp"
+#line 2708 "engines/director/lingo/lingo-gr.cpp"
         break;
 
       default:
@@ -2900,906 +2972,1073 @@ yyreduce:
   switch (yyn)
     {
   case 2: /* script: scriptpartlist  */
-#line 211 "engines/director/lingo/lingo-gr.y"
+#line 225 "engines/director/lingo/lingo-gr.y"
                                                         { g_lingo->_compiler->_assemblyAST = Common::SharedPtr<Node>(new ScriptNode((yyvsp[0].nodelist))); (yyval.node) = nullptr; }
-#line 2906 "engines/director/lingo/lingo-gr.cpp"
+#line 2978 "engines/director/lingo/lingo-gr.cpp"
     break;
 
   case 3: /* scriptpartlist: scriptpart  */
-#line 213 "engines/director/lingo/lingo-gr.y"
+#line 227 "engines/director/lingo/lingo-gr.y"
                                                                 {
 		NodeList *list = new NodeList;
 		if ((yyvsp[0].node)) {
 			list->push_back((yyvsp[0].node));
 		}
 		(yyval.nodelist) = list; }
-#line 2917 "engines/director/lingo/lingo-gr.cpp"
+#line 2989 "engines/director/lingo/lingo-gr.cpp"
     break;
 
   case 4: /* scriptpartlist: scriptpartlist scriptpart  */
-#line 219 "engines/director/lingo/lingo-gr.y"
+#line 233 "engines/director/lingo/lingo-gr.y"
                                                         {
 		if ((yyvsp[0].node)) {
 			(yyvsp[-1].nodelist)->push_back((yyvsp[0].node));
 		}
 		(yyval.nodelist) = (yyvsp[-1].nodelist); }
-#line 2927 "engines/director/lingo/lingo-gr.cpp"
+#line 2999 "engines/director/lingo/lingo-gr.cpp"
     break;
 
   case 5: /* scriptpart: '\n'  */
-#line 226 "engines/director/lingo/lingo-gr.y"
+#line 240 "engines/director/lingo/lingo-gr.y"
                                                                 { (yyval.node) = nullptr; }
-#line 2933 "engines/director/lingo/lingo-gr.cpp"
+#line 3005 "engines/director/lingo/lingo-gr.cpp"
     break;
 
   case 10: /* scriptpart: tENDCLAUSE endargdef '\n'  */
-#line 231 "engines/director/lingo/lingo-gr.y"
+#line 245 "engines/director/lingo/lingo-gr.y"
                                                         { (yyval.node) = nullptr; delete (yyvsp[-2].s); }
-#line 2939 "engines/director/lingo/lingo-gr.cpp"
+#line 3011 "engines/director/lingo/lingo-gr.cpp"
     break;
 
   case 11: /* macro: tMACRO ID idlist '\n' stmtlist  */
-#line 260 "engines/director/lingo/lingo-gr.y"
+#line 274 "engines/director/lingo/lingo-gr.y"
                                         { (yyval.node) = new HandlerNode((yyvsp[-3].s), (yyvsp[-2].idlist), (yyvsp[0].nodelist)); }
-#line 2945 "engines/director/lingo/lingo-gr.cpp"
+#line 3017 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 12: /* factory: tFACTORY ID '\n' methodlist  */
-#line 264 "engines/director/lingo/lingo-gr.y"
+  case 12: /* macro: tMACRO ID idlist error '\n' stmtlist  */
+#line 275 "engines/director/lingo/lingo-gr.y"
+                                                { TRIM_GARBAGE((yyval.node), new HandlerNode((yyvsp[-4].s), (yyvsp[-3].idlist), (yyvsp[0].nodelist))); }
+#line 3023 "engines/director/lingo/lingo-gr.cpp"
+    break;
+
+  case 13: /* factory: tFACTORY ID '\n' methodlist  */
+#line 280 "engines/director/lingo/lingo-gr.y"
                                         { (yyval.node) = new FactoryNode((yyvsp[-2].s), (yyvsp[0].nodelist)); }
-#line 2951 "engines/director/lingo/lingo-gr.cpp"
+#line 3029 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 13: /* method: tMETHOD ID idlist '\n' stmtlist  */
-#line 266 "engines/director/lingo/lingo-gr.y"
+  case 14: /* factory: tFACTORY ID error '\n' methodlist  */
+#line 281 "engines/director/lingo/lingo-gr.y"
+                                                { TRIM_GARBAGE((yyval.node), new FactoryNode((yyvsp[-3].s), (yyvsp[0].nodelist))); }
+#line 3035 "engines/director/lingo/lingo-gr.cpp"
+    break;
+
+  case 15: /* method: tMETHOD ID idlist '\n' stmtlist  */
+#line 284 "engines/director/lingo/lingo-gr.y"
                                         { (yyval.node) = new HandlerNode((yyvsp[-3].s), (yyvsp[-2].idlist), (yyvsp[0].nodelist)); }
-#line 2957 "engines/director/lingo/lingo-gr.cpp"
+#line 3041 "engines/director/lingo/lingo-gr.cpp"
+    break;
+
+  case 16: /* method: tMETHOD ID idlist error '\n' stmtlist  */
+#line 285 "engines/director/lingo/lingo-gr.y"
+                                                        { TRIM_GARBAGE((yyval.node), new HandlerNode((yyvsp[-4].s), (yyvsp[-3].idlist), (yyvsp[0].nodelist))); }
+#line 3047 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 14: /* methodlist: %empty  */
-#line 268 "engines/director/lingo/lingo-gr.y"
+  case 17: /* methodlist: %empty  */
+#line 288 "engines/director/lingo/lingo-gr.y"
                                                 { (yyval.nodelist) = new NodeList; }
-#line 2963 "engines/director/lingo/lingo-gr.cpp"
+#line 3053 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 16: /* nonemptymethodlist: methodlistline  */
-#line 272 "engines/director/lingo/lingo-gr.y"
+  case 19: /* nonemptymethodlist: methodlistline  */
+#line 292 "engines/director/lingo/lingo-gr.y"
                                                                 {
 		NodeList *list = new NodeList;
 		if ((yyvsp[0].node)) {
 			list->push_back((yyvsp[0].node));
 		}
 		(yyval.nodelist) = list; }
-#line 2974 "engines/director/lingo/lingo-gr.cpp"
+#line 3064 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 17: /* nonemptymethodlist: nonemptymethodlist methodlistline  */
-#line 278 "engines/director/lingo/lingo-gr.y"
+  case 20: /* nonemptymethodlist: nonemptymethodlist methodlistline  */
+#line 298 "engines/director/lingo/lingo-gr.y"
                                                         {
 		if ((yyvsp[0].node)) {
 			(yyvsp[-1].nodelist)->push_back((yyvsp[0].node));
 		}
 		(yyval.nodelist) = (yyvsp[-1].nodelist); }
-#line 2984 "engines/director/lingo/lingo-gr.cpp"
+#line 3074 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 18: /* methodlistline: '\n'  */
-#line 285 "engines/director/lingo/lingo-gr.y"
+  case 21: /* methodlistline: '\n'  */
+#line 305 "engines/director/lingo/lingo-gr.y"
                                                 { (yyval.node) = nullptr; }
-#line 2990 "engines/director/lingo/lingo-gr.cpp"
+#line 3080 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 20: /* methodlistline: tENDCLAUSE endargdef '\n'  */
-#line 287 "engines/director/lingo/lingo-gr.y"
+  case 23: /* methodlistline: tENDCLAUSE endargdef '\n'  */
+#line 307 "engines/director/lingo/lingo-gr.y"
                                                         { (yyval.node) = nullptr; delete (yyvsp[-2].s); }
-#line 2996 "engines/director/lingo/lingo-gr.cpp"
+#line 3086 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 21: /* handler: tON ID idlist '\n' stmtlist tENDCLAUSE endargdef '\n'  */
-#line 292 "engines/director/lingo/lingo-gr.y"
+  case 24: /* handler: tON ID idlist '\n' stmtlist tENDCLAUSE endargdef '\n'  */
+#line 312 "engines/director/lingo/lingo-gr.y"
                                                                {	// D3
 		(yyval.node) = new HandlerNode((yyvsp[-6].s), (yyvsp[-5].idlist), (yyvsp[-3].nodelist));
 		checkEnd((yyvsp[-2].s), (yyvsp[-6].s), false);
 		delete (yyvsp[-2].s); }
-#line 3005 "engines/director/lingo/lingo-gr.cpp"
+#line 3095 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 22: /* handler: tON ID idlist '\n' stmtlist  */
-#line 296 "engines/director/lingo/lingo-gr.y"
+  case 25: /* handler: tON ID idlist error '\n' stmtlist tENDCLAUSE endargdef '\n'  */
+#line 316 "engines/director/lingo/lingo-gr.y"
+                                                                      {	// D3
+		TRIM_GARBAGE((yyval.node), new HandlerNode((yyvsp[-7].s), (yyvsp[-6].idlist), (yyvsp[-3].nodelist)));
+		checkEnd((yyvsp[-2].s), (yyvsp[-7].s), false);
+		delete (yyvsp[-2].s); }
+#line 3104 "engines/director/lingo/lingo-gr.cpp"
+    break;
+
+  case 26: /* handler: tON ID idlist '\n' stmtlist  */
+#line 320 "engines/director/lingo/lingo-gr.y"
                                       {	// D4. No 'end' clause
 		(yyval.node) = new HandlerNode((yyvsp[-3].s), (yyvsp[-2].idlist), (yyvsp[0].nodelist)); }
-#line 3012 "engines/director/lingo/lingo-gr.cpp"
+#line 3111 "engines/director/lingo/lingo-gr.cpp"
+    break;
+
+  case 27: /* handler: tON ID idlist error '\n' stmtlist  */
+#line 322 "engines/director/lingo/lingo-gr.y"
+                                            {	// D4. No 'end' clause
+		TRIM_GARBAGE((yyval.node), new HandlerNode((yyvsp[-4].s), (yyvsp[-3].idlist), (yyvsp[0].nodelist))); }
+#line 3118 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 24: /* endargdef: ID  */
-#line 301 "engines/director/lingo/lingo-gr.y"
+  case 29: /* endargdef: ID  */
+#line 327 "engines/director/lingo/lingo-gr.y"
                                                         { delete (yyvsp[0].s); }
-#line 3018 "engines/director/lingo/lingo-gr.cpp"
+#line 3124 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 25: /* endargdef: endargdef ',' ID  */
-#line 302 "engines/director/lingo/lingo-gr.y"
+  case 30: /* endargdef: endargdef ',' ID  */
+#line 328 "engines/director/lingo/lingo-gr.y"
                                                 { delete (yyvsp[0].s); }
-#line 3024 "engines/director/lingo/lingo-gr.cpp"
+#line 3130 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 27: /* CMDID: tABBREVIATED  */
-#line 310 "engines/director/lingo/lingo-gr.y"
+  case 32: /* CMDID: tABBREVIATED  */
+#line 336 "engines/director/lingo/lingo-gr.y"
                         { (yyval.s) = new Common::String("abbreviated"); }
-#line 3030 "engines/director/lingo/lingo-gr.cpp"
+#line 3136 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 28: /* CMDID: tABBREV  */
-#line 311 "engines/director/lingo/lingo-gr.y"
+  case 33: /* CMDID: tABBREV  */
+#line 337 "engines/director/lingo/lingo-gr.y"
                                 { (yyval.s) = new Common::String("abbrev"); }
-#line 3036 "engines/director/lingo/lingo-gr.cpp"
+#line 3142 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 29: /* CMDID: tABBR  */
-#line 312 "engines/director/lingo/lingo-gr.y"
+  case 34: /* CMDID: tABBR  */
+#line 338 "engines/director/lingo/lingo-gr.y"
                                 { (yyval.s) = new Common::String("abbr"); }
-#line 3042 "engines/director/lingo/lingo-gr.cpp"
+#line 3148 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 30: /* CMDID: tAFTER  */
-#line 313 "engines/director/lingo/lingo-gr.y"
+  case 35: /* CMDID: tAFTER  */
+#line 339 "engines/director/lingo/lingo-gr.y"
                                 { (yyval.s) = new Common::String("after"); }
-#line 3048 "engines/director/lingo/lingo-gr.cpp"
+#line 3154 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 31: /* CMDID: tBEFORE  */
-#line 314 "engines/director/lingo/lingo-gr.y"
+  case 36: /* CMDID: tBEFORE  */
+#line 340 "engines/director/lingo/lingo-gr.y"
                                 { (yyval.s) = new Common::String("before"); }
-#line 3054 "engines/director/lingo/lingo-gr.cpp"
+#line 3160 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 32: /* CMDID: tCAST  */
-#line 315 "engines/director/lingo/lingo-gr.y"
+  case 37: /* CMDID: tCAST  */
+#line 341 "engines/director/lingo/lingo-gr.y"
                                 { (yyval.s) = new Common::String("cast"); }
-#line 3060 "engines/director/lingo/lingo-gr.cpp"
+#line 3166 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 33: /* CMDID: tCASTLIB  */
-#line 316 "engines/director/lingo/lingo-gr.y"
+  case 38: /* CMDID: tCASTLIB  */
+#line 342 "engines/director/lingo/lingo-gr.y"
                                 { (yyval.s) = new Common::String("castLib"); }
-#line 3066 "engines/director/lingo/lingo-gr.cpp"
+#line 3172 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 34: /* CMDID: tCHAR  */
-#line 317 "engines/director/lingo/lingo-gr.y"
+  case 39: /* CMDID: tCHAR  */
+#line 343 "engines/director/lingo/lingo-gr.y"
                                 { (yyval.s) = new Common::String("char"); }
-#line 3072 "engines/director/lingo/lingo-gr.cpp"
+#line 3178 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 35: /* CMDID: tCHARS  */
-#line 318 "engines/director/lingo/lingo-gr.y"
+  case 40: /* CMDID: tCHARS  */
+#line 344 "engines/director/lingo/lingo-gr.y"
                                 { (yyval.s) = new Common::String("chars"); }
-#line 3078 "engines/director/lingo/lingo-gr.cpp"
+#line 3184 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 36: /* CMDID: tDATE  */
-#line 319 "engines/director/lingo/lingo-gr.y"
+  case 41: /* CMDID: tDATE  */
+#line 345 "engines/director/lingo/lingo-gr.y"
                                 { (yyval.s) = new Common::String("date"); }
-#line 3084 "engines/director/lingo/lingo-gr.cpp"
+#line 3190 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 37: /* CMDID: tDELETE  */
-#line 320 "engines/director/lingo/lingo-gr.y"
+  case 42: /* CMDID: tDELETE  */
+#line 346 "engines/director/lingo/lingo-gr.y"
                                 { (yyval.s) = new Common::String("delete"); }
-#line 3090 "engines/director/lingo/lingo-gr.cpp"
+#line 3196 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 38: /* CMDID: tDOWN  */
-#line 321 "engines/director/lingo/lingo-gr.y"
+  case 43: /* CMDID: tDOWN  */
+#line 347 "engines/director/lingo/lingo-gr.y"
                                 { (yyval.s) = new Common::String("down"); }
-#line 3096 "engines/director/lingo/lingo-gr.cpp"
+#line 3202 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 39: /* CMDID: tFIELD  */
-#line 322 "engines/director/lingo/lingo-gr.y"
+  case 44: /* CMDID: tFIELD  */
+#line 348 "engines/director/lingo/lingo-gr.y"
                                 { (yyval.s) = new Common::String("field"); }
-#line 3102 "engines/director/lingo/lingo-gr.cpp"
+#line 3208 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 40: /* CMDID: tFRAME  */
-#line 323 "engines/director/lingo/lingo-gr.y"
+  case 45: /* CMDID: tFRAME  */
+#line 349 "engines/director/lingo/lingo-gr.y"
                                 { (yyval.s) = new Common::String("frame"); }
-#line 3108 "engines/director/lingo/lingo-gr.cpp"
+#line 3214 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 41: /* CMDID: tHILITE  */
-#line 324 "engines/director/lingo/lingo-gr.y"
+  case 46: /* CMDID: tHILITE  */
+#line 350 "engines/director/lingo/lingo-gr.y"
                                 { (yyval.s) = new Common::String("hilite"); }
-#line 3114 "engines/director/lingo/lingo-gr.cpp"
+#line 3220 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 42: /* CMDID: tIN  */
-#line 325 "engines/director/lingo/lingo-gr.y"
+  case 47: /* CMDID: tIN  */
+#line 351 "engines/director/lingo/lingo-gr.y"
                                 { (yyval.s) = new Common::String("in"); }
-#line 3120 "engines/director/lingo/lingo-gr.cpp"
+#line 3226 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 43: /* CMDID: tINTERSECTS  */
-#line 326 "engines/director/lingo/lingo-gr.y"
+  case 48: /* CMDID: tINTERSECTS  */
+#line 352 "engines/director/lingo/lingo-gr.y"
                         { (yyval.s) = new Common::String("intersects"); }
-#line 3126 "engines/director/lingo/lingo-gr.cpp"
+#line 3232 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 44: /* CMDID: tINTO  */
-#line 327 "engines/director/lingo/lingo-gr.y"
+  case 49: /* CMDID: tINTO  */
+#line 353 "engines/director/lingo/lingo-gr.y"
                                 { (yyval.s) = new Common::String("into"); }
-#line 3132 "engines/director/lingo/lingo-gr.cpp"
+#line 3238 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 45: /* CMDID: tITEM  */
-#line 328 "engines/director/lingo/lingo-gr.y"
+  case 50: /* CMDID: tITEM  */
+#line 354 "engines/director/lingo/lingo-gr.y"
                                 { (yyval.s) = new Common::String("item"); }
-#line 3138 "engines/director/lingo/lingo-gr.cpp"
+#line 3244 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 46: /* CMDID: tITEMS  */
-#line 329 "engines/director/lingo/lingo-gr.y"
+  case 51: /* CMDID: tITEMS  */
+#line 355 "engines/director/lingo/lingo-gr.y"
                                 { (yyval.s) = new Common::String("items"); }
-#line 3144 "engines/director/lingo/lingo-gr.cpp"
+#line 3250 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 47: /* CMDID: tLAST  */
-#line 330 "engines/director/lingo/lingo-gr.y"
+  case 52: /* CMDID: tLAST  */
+#line 356 "engines/director/lingo/lingo-gr.y"
                                 { (yyval.s) = new Common::String("last"); }
-#line 3150 "engines/director/lingo/lingo-gr.cpp"
+#line 3256 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 48: /* CMDID: tLINE  */
-#line 331 "engines/director/lingo/lingo-gr.y"
+  case 53: /* CMDID: tLINE  */
+#line 357 "engines/director/lingo/lingo-gr.y"
                                 { (yyval.s) = new Common::String("line"); }
-#line 3156 "engines/director/lingo/lingo-gr.cpp"
+#line 3262 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 49: /* CMDID: tLINES  */
-#line 332 "engines/director/lingo/lingo-gr.y"
+  case 54: /* CMDID: tLINES  */
+#line 358 "engines/director/lingo/lingo-gr.y"
                                 { (yyval.s) = new Common::String("lines"); }
-#line 3162 "engines/director/lingo/lingo-gr.cpp"
+#line 3268 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 50: /* CMDID: tLONG  */
-#line 333 "engines/director/lingo/lingo-gr.y"
+  case 55: /* CMDID: tLONG  */
+#line 359 "engines/director/lingo/lingo-gr.y"
                                 { (yyval.s) = new Common::String("long"); }
-#line 3168 "engines/director/lingo/lingo-gr.cpp"
+#line 3274 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 51: /* CMDID: tMEMBER  */
-#line 334 "engines/director/lingo/lingo-gr.y"
+  case 56: /* CMDID: tMEMBER  */
+#line 360 "engines/director/lingo/lingo-gr.y"
                                 { (yyval.s) = new Common::String("member"); }
-#line 3174 "engines/director/lingo/lingo-gr.cpp"
+#line 3280 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 52: /* CMDID: tMENU  */
-#line 335 "engines/director/lingo/lingo-gr.y"
+  case 57: /* CMDID: tMENU  */
+#line 361 "engines/director/lingo/lingo-gr.y"
                                 { (yyval.s) = new Common::String("menu"); }
-#line 3180 "engines/director/lingo/lingo-gr.cpp"
+#line 3286 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 53: /* CMDID: tMENUITEM  */
-#line 336 "engines/director/lingo/lingo-gr.y"
+  case 58: /* CMDID: tMENUITEM  */
+#line 362 "engines/director/lingo/lingo-gr.y"
                                 { (yyval.s) = new Common::String("menuItem"); }
-#line 3186 "engines/director/lingo/lingo-gr.cpp"
+#line 3292 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 54: /* CMDID: tMENUITEMS  */
-#line 337 "engines/director/lingo/lingo-gr.y"
+  case 59: /* CMDID: tMENUITEMS  */
+#line 363 "engines/director/lingo/lingo-gr.y"
                         { (yyval.s) = new Common::String("menuItems"); }
-#line 3192 "engines/director/lingo/lingo-gr.cpp"
+#line 3298 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 55: /* CMDID: tMOVIE  */
-#line 338 "engines/director/lingo/lingo-gr.y"
+  case 60: /* CMDID: tMOVIE  */
+#line 364 "engines/director/lingo/lingo-gr.y"
                                 { (yyval.s) = new Common::String("movie"); }
-#line 3198 "engines/director/lingo/lingo-gr.cpp"
+#line 3304 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 56: /* CMDID: tNEXT  */
-#line 339 "engines/director/lingo/lingo-gr.y"
+  case 61: /* CMDID: tNEXT  */
+#line 365 "engines/director/lingo/lingo-gr.y"
                                 { (yyval.s) = new Common::String("next"); }
-#line 3204 "engines/director/lingo/lingo-gr.cpp"
+#line 3310 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 57: /* CMDID: tNUMBER  */
-#line 340 "engines/director/lingo/lingo-gr.y"
+  case 62: /* CMDID: tNUMBER  */
+#line 366 "engines/director/lingo/lingo-gr.y"
                                 { (yyval.s) = new Common::String("number"); }
-#line 3210 "engines/director/lingo/lingo-gr.cpp"
+#line 3316 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 58: /* CMDID: tOF  */
-#line 341 "engines/director/lingo/lingo-gr.y"
+  case 63: /* CMDID: tOF  */
+#line 367 "engines/director/lingo/lingo-gr.y"
                                 { (yyval.s) = new Common::String("of"); }
-#line 3216 "engines/director/lingo/lingo-gr.cpp"
+#line 3322 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 59: /* CMDID: tPREVIOUS  */
-#line 342 "engines/director/lingo/lingo-gr.y"
+  case 64: /* CMDID: tPREVIOUS  */
+#line 368 "engines/director/lingo/lingo-gr.y"
                                 { (yyval.s) = new Common::String("previous"); }
-#line 3222 "engines/director/lingo/lingo-gr.cpp"
+#line 3328 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 60: /* CMDID: tREPEAT  */
-#line 343 "engines/director/lingo/lingo-gr.y"
+  case 65: /* CMDID: tREPEAT  */
+#line 369 "engines/director/lingo/lingo-gr.y"
                                 { (yyval.s) = new Common::String("repeat"); }
-#line 3228 "engines/director/lingo/lingo-gr.cpp"
+#line 3334 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 61: /* CMDID: tSCRIPT  */
-#line 344 "engines/director/lingo/lingo-gr.y"
+  case 66: /* CMDID: tSCRIPT  */
+#line 370 "engines/director/lingo/lingo-gr.y"
                                 { (yyval.s) = new Common::String("script"); }
-#line 3234 "engines/director/lingo/lingo-gr.cpp"
+#line 3340 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 62: /* CMDID: tASSERTERROR  */
-#line 345 "engines/director/lingo/lingo-gr.y"
+  case 67: /* CMDID: tASSERTERROR  */
+#line 371 "engines/director/lingo/lingo-gr.y"
                         { (yyval.s) = new Common::String("scummvmAssertError"); }
-#line 3240 "engines/director/lingo/lingo-gr.cpp"
+#line 3346 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 63: /* CMDID: tSHORT  */
-#line 346 "engines/director/lingo/lingo-gr.y"
+  case 68: /* CMDID: tSHORT  */
+#line 372 "engines/director/lingo/lingo-gr.y"
                                 { (yyval.s) = new Common::String("short"); }
-#line 3246 "engines/director/lingo/lingo-gr.cpp"
+#line 3352 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 64: /* CMDID: tSOUND  */
-#line 347 "engines/director/lingo/lingo-gr.y"
+  case 69: /* CMDID: tSOUND  */
+#line 373 "engines/director/lingo/lingo-gr.y"
                                 { (yyval.s) = new Common::String("sound"); }
-#line 3252 "engines/director/lingo/lingo-gr.cpp"
+#line 3358 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 65: /* CMDID: tSPRITE  */
-#line 348 "engines/director/lingo/lingo-gr.y"
+  case 70: /* CMDID: tSPRITE  */
+#line 374 "engines/director/lingo/lingo-gr.y"
                                 { (yyval.s) = new Common::String("sprite"); }
-#line 3258 "engines/director/lingo/lingo-gr.cpp"
+#line 3364 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 66: /* CMDID: tTHE  */
-#line 349 "engines/director/lingo/lingo-gr.y"
+  case 71: /* CMDID: tTHE  */
+#line 375 "engines/director/lingo/lingo-gr.y"
                                 { (yyval.s) = new Common::String("the"); }
-#line 3264 "engines/director/lingo/lingo-gr.cpp"
+#line 3370 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 67: /* CMDID: tTIME  */
-#line 350 "engines/director/lingo/lingo-gr.y"
+  case 72: /* CMDID: tTIME  */
+#line 376 "engines/director/lingo/lingo-gr.y"
                                 { (yyval.s) = new Common::String("time"); }
-#line 3270 "engines/director/lingo/lingo-gr.cpp"
+#line 3376 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 68: /* CMDID: tTO  */
-#line 351 "engines/director/lingo/lingo-gr.y"
+  case 73: /* CMDID: tTO  */
+#line 377 "engines/director/lingo/lingo-gr.y"
                                 { (yyval.s) = new Common::String("to"); }
-#line 3276 "engines/director/lingo/lingo-gr.cpp"
+#line 3382 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 69: /* CMDID: tWHILE  */
-#line 352 "engines/director/lingo/lingo-gr.y"
+  case 74: /* CMDID: tWHILE  */
+#line 378 "engines/director/lingo/lingo-gr.y"
                                 { (yyval.s) = new Common::String("while"); }
-#line 3282 "engines/director/lingo/lingo-gr.cpp"
+#line 3388 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 70: /* CMDID: tWINDOW  */
-#line 353 "engines/director/lingo/lingo-gr.y"
+  case 75: /* CMDID: tWINDOW  */
+#line 379 "engines/director/lingo/lingo-gr.y"
                                 { (yyval.s) = new Common::String("window"); }
-#line 3288 "engines/director/lingo/lingo-gr.cpp"
+#line 3394 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 71: /* CMDID: tWITH  */
-#line 354 "engines/director/lingo/lingo-gr.y"
+  case 76: /* CMDID: tWITH  */
+#line 380 "engines/director/lingo/lingo-gr.y"
                                 { (yyval.s) = new Common::String("with"); }
-#line 3294 "engines/director/lingo/lingo-gr.cpp"
+#line 3400 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 72: /* CMDID: tWITHIN  */
-#line 355 "engines/director/lingo/lingo-gr.y"
+  case 77: /* CMDID: tWITHIN  */
+#line 381 "engines/director/lingo/lingo-gr.y"
                                 { (yyval.s) = new Common::String("within"); }
-#line 3300 "engines/director/lingo/lingo-gr.cpp"
+#line 3406 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 73: /* CMDID: tWORD  */
-#line 356 "engines/director/lingo/lingo-gr.y"
+  case 78: /* CMDID: tWORD  */
+#line 382 "engines/director/lingo/lingo-gr.y"
                                 { (yyval.s) = new Common::String("word"); }
-#line 3306 "engines/director/lingo/lingo-gr.cpp"
+#line 3412 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 74: /* CMDID: tWORDS  */
-#line 357 "engines/director/lingo/lingo-gr.y"
+  case 79: /* CMDID: tWORDS  */
+#line 383 "engines/director/lingo/lingo-gr.y"
                                 { (yyval.s) = new Common::String("words"); }
-#line 3312 "engines/director/lingo/lingo-gr.cpp"
+#line 3418 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 76: /* ID: tELSE  */
-#line 361 "engines/director/lingo/lingo-gr.y"
+  case 81: /* ID: tELSE  */
+#line 387 "engines/director/lingo/lingo-gr.y"
                                 { (yyval.s) = new Common::String("else"); }
-#line 3318 "engines/director/lingo/lingo-gr.cpp"
+#line 3424 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 77: /* ID: tENDCLAUSE  */
-#line 362 "engines/director/lingo/lingo-gr.y"
+  case 82: /* ID: tENDCLAUSE  */
+#line 388 "engines/director/lingo/lingo-gr.y"
                         { (yyval.s) = new Common::String("end"); delete (yyvsp[0].s); }
-#line 3324 "engines/director/lingo/lingo-gr.cpp"
+#line 3430 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 78: /* ID: tEXIT  */
-#line 363 "engines/director/lingo/lingo-gr.y"
+  case 83: /* ID: tEXIT  */
+#line 389 "engines/director/lingo/lingo-gr.y"
                                 { (yyval.s) = new Common::String("exit"); }
-#line 3330 "engines/director/lingo/lingo-gr.cpp"
+#line 3436 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 79: /* ID: tFACTORY  */
-#line 364 "engines/director/lingo/lingo-gr.y"
+  case 84: /* ID: tFACTORY  */
+#line 390 "engines/director/lingo/lingo-gr.y"
                                 { (yyval.s) = new Common::String("factory"); }
-#line 3336 "engines/director/lingo/lingo-gr.cpp"
+#line 3442 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 80: /* ID: tGLOBAL  */
-#line 365 "engines/director/lingo/lingo-gr.y"
+  case 85: /* ID: tGLOBAL  */
+#line 391 "engines/director/lingo/lingo-gr.y"
                                 { (yyval.s) = new Common::String("global"); }
-#line 3342 "engines/director/lingo/lingo-gr.cpp"
+#line 3448 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 81: /* ID: tGO  */
-#line 366 "engines/director/lingo/lingo-gr.y"
+  case 86: /* ID: tGO  */
+#line 392 "engines/director/lingo/lingo-gr.y"
                                 { (yyval.s) = new Common::String("go"); }
-#line 3348 "engines/director/lingo/lingo-gr.cpp"
+#line 3454 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 82: /* ID: tIF  */
-#line 367 "engines/director/lingo/lingo-gr.y"
+  case 87: /* ID: tIF  */
+#line 393 "engines/director/lingo/lingo-gr.y"
                                 { (yyval.s) = new Common::String("if"); }
-#line 3354 "engines/director/lingo/lingo-gr.cpp"
+#line 3460 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 83: /* ID: tINSTANCE  */
-#line 368 "engines/director/lingo/lingo-gr.y"
+  case 88: /* ID: tINSTANCE  */
+#line 394 "engines/director/lingo/lingo-gr.y"
                                 { (yyval.s) = new Common::String("instance"); }
-#line 3360 "engines/director/lingo/lingo-gr.cpp"
+#line 3466 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 84: /* ID: tMACRO  */
-#line 369 "engines/director/lingo/lingo-gr.y"
+  case 89: /* ID: tMACRO  */
+#line 395 "engines/director/lingo/lingo-gr.y"
                                 { (yyval.s) = new Common::String("macro"); }
-#line 3366 "engines/director/lingo/lingo-gr.cpp"
+#line 3472 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 85: /* ID: tMETHOD  */
-#line 370 "engines/director/lingo/lingo-gr.y"
+  case 90: /* ID: tMETHOD  */
+#line 396 "engines/director/lingo/lingo-gr.y"
                                 { (yyval.s) = new Common::String("method"); }
-#line 3372 "engines/director/lingo/lingo-gr.cpp"
+#line 3478 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 86: /* ID: tON  */
-#line 371 "engines/director/lingo/lingo-gr.y"
+  case 91: /* ID: tON  */
+#line 397 "engines/director/lingo/lingo-gr.y"
                                 { (yyval.s) = new Common::String("on"); }
-#line 3378 "engines/director/lingo/lingo-gr.cpp"
+#line 3484 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 87: /* ID: tOPEN  */
-#line 372 "engines/director/lingo/lingo-gr.y"
+  case 92: /* ID: tOPEN  */
+#line 398 "engines/director/lingo/lingo-gr.y"
                                 { (yyval.s) = new Common::String("open"); }
-#line 3384 "engines/director/lingo/lingo-gr.cpp"
+#line 3490 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 88: /* ID: tPLAY  */
-#line 373 "engines/director/lingo/lingo-gr.y"
+  case 93: /* ID: tPLAY  */
+#line 399 "engines/director/lingo/lingo-gr.y"
                                 { (yyval.s) = new Common::String("play"); }
-#line 3390 "engines/director/lingo/lingo-gr.cpp"
+#line 3496 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 89: /* ID: tPROPERTY  */
-#line 374 "engines/director/lingo/lingo-gr.y"
+  case 94: /* ID: tPROPERTY  */
+#line 400 "engines/director/lingo/lingo-gr.y"
                                 { (yyval.s) = new Common::String("property"); }
-#line 3396 "engines/director/lingo/lingo-gr.cpp"
+#line 3502 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 90: /* ID: tPUT  */
-#line 375 "engines/director/lingo/lingo-gr.y"
+  case 95: /* ID: tPUT  */
+#line 401 "engines/director/lingo/lingo-gr.y"
                                 { (yyval.s) = new Common::String("put"); }
-#line 3402 "engines/director/lingo/lingo-gr.cpp"
+#line 3508 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 91: /* ID: tRETURN  */
-#line 376 "engines/director/lingo/lingo-gr.y"
+  case 96: /* ID: tRETURN  */
+#line 402 "engines/director/lingo/lingo-gr.y"
                                 { (yyval.s) = new Common::String("return"); }
-#line 3408 "engines/director/lingo/lingo-gr.cpp"
+#line 3514 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 92: /* ID: tSET  */
-#line 377 "engines/director/lingo/lingo-gr.y"
+  case 97: /* ID: tSET  */
+#line 403 "engines/director/lingo/lingo-gr.y"
                                 { (yyval.s) = new Common::String("set"); }
-#line 3414 "engines/director/lingo/lingo-gr.cpp"
+#line 3520 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 93: /* ID: tTELL  */
-#line 378 "engines/director/lingo/lingo-gr.y"
+  case 98: /* ID: tTELL  */
+#line 404 "engines/director/lingo/lingo-gr.y"
                                 { (yyval.s) = new Common::String("tell"); }
-#line 3420 "engines/director/lingo/lingo-gr.cpp"
+#line 3526 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 94: /* ID: tTHEN  */
-#line 379 "engines/director/lingo/lingo-gr.y"
+  case 99: /* ID: tTHEN  */
+#line 405 "engines/director/lingo/lingo-gr.y"
                                 { (yyval.s) = new Common::String("then"); }
-#line 3426 "engines/director/lingo/lingo-gr.cpp"
+#line 3532 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 95: /* idlist: %empty  */
-#line 382 "engines/director/lingo/lingo-gr.y"
+  case 100: /* idlist: %empty  */
+#line 408 "engines/director/lingo/lingo-gr.y"
                                                         { (yyval.idlist) = new IDList; }
-#line 3432 "engines/director/lingo/lingo-gr.cpp"
+#line 3538 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 98: /* nonemptyidlist: ID  */
-#line 387 "engines/director/lingo/lingo-gr.y"
+  case 103: /* nonemptyidlist: ID  */
+#line 413 "engines/director/lingo/lingo-gr.y"
                                                                 {
 		Common::Array<Common::String *> *list = new IDList;
 		list->push_back((yyvsp[0].s));
 		(yyval.idlist) = list; }
-#line 3441 "engines/director/lingo/lingo-gr.cpp"
+#line 3547 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 99: /* nonemptyidlist: nonemptyidlist ',' ID  */
-#line 391 "engines/director/lingo/lingo-gr.y"
+  case 104: /* nonemptyidlist: nonemptyidlist ',' ID  */
+#line 417 "engines/director/lingo/lingo-gr.y"
                                                         {
 		(yyvsp[-2].idlist)->push_back((yyvsp[0].s));
 		(yyval.idlist) = (yyvsp[-2].idlist); }
-#line 3449 "engines/director/lingo/lingo-gr.cpp"
+#line 3555 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 101: /* stmt: tENDIF '\n'  */
-#line 402 "engines/director/lingo/lingo-gr.y"
+  case 106: /* stmt: tENDIF '\n'  */
+#line 428 "engines/director/lingo/lingo-gr.y"
                                                                 { (yyval.node) = nullptr; }
-#line 3455 "engines/director/lingo/lingo-gr.cpp"
+#line 3561 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 111: /* proc: CMDID cmdargs '\n'  */
-#line 418 "engines/director/lingo/lingo-gr.y"
+  case 116: /* proc: CMDID cmdargs '\n'  */
+#line 444 "engines/director/lingo/lingo-gr.y"
                                                         { (yyval.node) = new CmdNode((yyvsp[-2].s), (yyvsp[-1].nodelist), g_lingo->_compiler->_linenumber - 1); }
-#line 3461 "engines/director/lingo/lingo-gr.cpp"
+#line 3567 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 112: /* proc: tPUT cmdargs '\n'  */
-#line 419 "engines/director/lingo/lingo-gr.y"
+  case 117: /* proc: CMDID cmdargs error '\n'  */
+#line 445 "engines/director/lingo/lingo-gr.y"
+                                                        { TRIM_GARBAGE((yyval.node), new CmdNode((yyvsp[-3].s), (yyvsp[-2].nodelist), g_lingo->_compiler->_linenumber - 1)); }
+#line 3573 "engines/director/lingo/lingo-gr.cpp"
+    break;
+
+  case 118: /* proc: tPUT cmdargs '\n'  */
+#line 446 "engines/director/lingo/lingo-gr.y"
                                                                 { (yyval.node) = new CmdNode(new Common::String("put"), (yyvsp[-1].nodelist), g_lingo->_compiler->_linenumber - 1); }
-#line 3467 "engines/director/lingo/lingo-gr.cpp"
+#line 3579 "engines/director/lingo/lingo-gr.cpp"
+    break;
+
+  case 119: /* proc: tPUT cmdargs error '\n'  */
+#line 447 "engines/director/lingo/lingo-gr.y"
+                                                                        { TRIM_GARBAGE((yyval.node), new CmdNode(new Common::String("put"), (yyvsp[-2].nodelist), g_lingo->_compiler->_linenumber - 1)); }
+#line 3585 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 113: /* proc: tGO cmdargs '\n'  */
-#line 420 "engines/director/lingo/lingo-gr.y"
+  case 120: /* proc: tGO cmdargs '\n'  */
+#line 448 "engines/director/lingo/lingo-gr.y"
                                                                 { (yyval.node) = new CmdNode(new Common::String("go"), (yyvsp[-1].nodelist), g_lingo->_compiler->_linenumber - 1); }
-#line 3473 "engines/director/lingo/lingo-gr.cpp"
+#line 3591 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 114: /* proc: tGO frameargs '\n'  */
-#line 421 "engines/director/lingo/lingo-gr.y"
+  case 121: /* proc: tGO cmdargs error '\n'  */
+#line 449 "engines/director/lingo/lingo-gr.y"
+                                                                        { TRIM_GARBAGE((yyval.node), new CmdNode(new Common::String("go"), (yyvsp[-2].nodelist), g_lingo->_compiler->_linenumber - 1)); }
+#line 3597 "engines/director/lingo/lingo-gr.cpp"
+    break;
+
+  case 122: /* proc: tGO frameargs '\n'  */
+#line 450 "engines/director/lingo/lingo-gr.y"
                                                         { (yyval.node) = new CmdNode(new Common::String("go"), (yyvsp[-1].nodelist), g_lingo->_compiler->_linenumber - 1); }
-#line 3479 "engines/director/lingo/lingo-gr.cpp"
+#line 3603 "engines/director/lingo/lingo-gr.cpp"
+    break;
+
+  case 123: /* proc: tGO frameargs error '\n'  */
+#line 451 "engines/director/lingo/lingo-gr.y"
+                                                                { TRIM_GARBAGE((yyval.node), new CmdNode(new Common::String("go"), (yyvsp[-2].nodelist), g_lingo->_compiler->_linenumber - 1)); }
+#line 3609 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 115: /* proc: tPLAY cmdargs '\n'  */
-#line 422 "engines/director/lingo/lingo-gr.y"
+  case 124: /* proc: tPLAY cmdargs '\n'  */
+#line 452 "engines/director/lingo/lingo-gr.y"
                                                         { (yyval.node) = new CmdNode(new Common::String("play"), (yyvsp[-1].nodelist), g_lingo->_compiler->_linenumber - 1); }
-#line 3485 "engines/director/lingo/lingo-gr.cpp"
+#line 3615 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 116: /* proc: tPLAY frameargs '\n'  */
-#line 423 "engines/director/lingo/lingo-gr.y"
+  case 125: /* proc: tPLAY cmdargs error '\n'  */
+#line 453 "engines/director/lingo/lingo-gr.y"
+                                                                { TRIM_GARBAGE((yyval.node), new CmdNode(new Common::String("play"), (yyvsp[-2].nodelist), g_lingo->_compiler->_linenumber - 1)); }
+#line 3621 "engines/director/lingo/lingo-gr.cpp"
+    break;
+
+  case 126: /* proc: tPLAY frameargs '\n'  */
+#line 454 "engines/director/lingo/lingo-gr.y"
                                                         { (yyval.node) = new CmdNode(new Common::String("play"), (yyvsp[-1].nodelist), g_lingo->_compiler->_linenumber - 1); }
-#line 3491 "engines/director/lingo/lingo-gr.cpp"
+#line 3627 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 117: /* proc: tOPEN cmdargs '\n'  */
-#line 424 "engines/director/lingo/lingo-gr.y"
+  case 127: /* proc: tPLAY frameargs error '\n'  */
+#line 455 "engines/director/lingo/lingo-gr.y"
+                                                                { TRIM_GARBAGE((yyval.node), new CmdNode(new Common::String("play"), (yyvsp[-2].nodelist), g_lingo->_compiler->_linenumber - 1)); }
+#line 3633 "engines/director/lingo/lingo-gr.cpp"
+    break;
+
+  case 128: /* proc: tOPEN cmdargs '\n'  */
+#line 456 "engines/director/lingo/lingo-gr.y"
                                                         { (yyval.node) = new CmdNode(new Common::String("open"), (yyvsp[-1].nodelist), g_lingo->_compiler->_linenumber - 1); }
-#line 3497 "engines/director/lingo/lingo-gr.cpp"
+#line 3639 "engines/director/lingo/lingo-gr.cpp"
+    break;
+
+  case 129: /* proc: tOPEN cmdargs error '\n'  */
+#line 457 "engines/director/lingo/lingo-gr.y"
+                                                                { TRIM_GARBAGE((yyval.node), new CmdNode(new Common::String("open"), (yyvsp[-2].nodelist), g_lingo->_compiler->_linenumber - 1)); }
+#line 3645 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 118: /* proc: tOPEN expr tWITH expr '\n'  */
-#line 425 "engines/director/lingo/lingo-gr.y"
+  case 130: /* proc: tOPEN expr tWITH expr '\n'  */
+#line 458 "engines/director/lingo/lingo-gr.y"
                                                  {
 		NodeList *args = new NodeList;
 		args->push_back((yyvsp[-3].node));
 		args->push_back((yyvsp[-1].node));
 		(yyval.node) = new CmdNode(new Common::String("open"), args, g_lingo->_compiler->_linenumber - 1); }
-#line 3507 "engines/director/lingo/lingo-gr.cpp"
+#line 3655 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 119: /* proc: tNEXT tREPEAT '\n'  */
-#line 430 "engines/director/lingo/lingo-gr.y"
+  case 131: /* proc: tNEXT tREPEAT '\n'  */
+#line 463 "engines/director/lingo/lingo-gr.y"
                                                         { (yyval.node) = new NextRepeatNode(); }
-#line 3513 "engines/director/lingo/lingo-gr.cpp"
+#line 3661 "engines/director/lingo/lingo-gr.cpp"
+    break;
+
+  case 132: /* proc: tNEXT tREPEAT error '\n'  */
+#line 464 "engines/director/lingo/lingo-gr.y"
+                                                                { TRIM_GARBAGE((yyval.node), new NextRepeatNode()); }
+#line 3667 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 120: /* proc: tEXIT tREPEAT '\n'  */
-#line 431 "engines/director/lingo/lingo-gr.y"
+  case 133: /* proc: tEXIT tREPEAT '\n'  */
+#line 465 "engines/director/lingo/lingo-gr.y"
                                                         { (yyval.node) = new ExitRepeatNode(); }
-#line 3519 "engines/director/lingo/lingo-gr.cpp"
+#line 3673 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 121: /* proc: tEXIT '\n'  */
-#line 432 "engines/director/lingo/lingo-gr.y"
+  case 134: /* proc: tEXIT tREPEAT error '\n'  */
+#line 466 "engines/director/lingo/lingo-gr.y"
+                                                                { TRIM_GARBAGE((yyval.node), new ExitRepeatNode()); }
+#line 3679 "engines/director/lingo/lingo-gr.cpp"
+    break;
+
+  case 135: /* proc: tEXIT '\n'  */
+#line 467 "engines/director/lingo/lingo-gr.y"
                                                                 { (yyval.node) = new ExitNode(); }
-#line 3525 "engines/director/lingo/lingo-gr.cpp"
+#line 3685 "engines/director/lingo/lingo-gr.cpp"
+    break;
+
+  case 136: /* proc: tEXIT error '\n'  */
+#line 468 "engines/director/lingo/lingo-gr.y"
+                                                                        { TRIM_GARBAGE((yyval.node), new ExitNode()); }
+#line 3691 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 122: /* proc: tRETURN '\n'  */
-#line 433 "engines/director/lingo/lingo-gr.y"
+  case 137: /* proc: tRETURN '\n'  */
+#line 469 "engines/director/lingo/lingo-gr.y"
                                                                 { (yyval.node) = new ReturnNode(nullptr); }
-#line 3531 "engines/director/lingo/lingo-gr.cpp"
+#line 3697 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 123: /* proc: tRETURN expr '\n'  */
-#line 434 "engines/director/lingo/lingo-gr.y"
+  case 138: /* proc: tRETURN error '\n'  */
+#line 470 "engines/director/lingo/lingo-gr.y"
+                                                                        { TRIM_GARBAGE((yyval.node), new ReturnNode(nullptr)); }
+#line 3703 "engines/director/lingo/lingo-gr.cpp"
+    break;
+
+  case 139: /* proc: tRETURN expr '\n'  */
+#line 471 "engines/director/lingo/lingo-gr.y"
                                                                 { (yyval.node) = new ReturnNode((yyvsp[-1].node)); }
-#line 3537 "engines/director/lingo/lingo-gr.cpp"
+#line 3709 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 124: /* proc: tDELETE chunk '\n'  */
-#line 435 "engines/director/lingo/lingo-gr.y"
+  case 140: /* proc: tRETURN expr error '\n'  */
+#line 472 "engines/director/lingo/lingo-gr.y"
+                                                                        { TRIM_GARBAGE((yyval.node), new ReturnNode((yyvsp[-2].node))); }
+#line 3715 "engines/director/lingo/lingo-gr.cpp"
+    break;
+
+  case 141: /* proc: tDELETE chunk '\n'  */
+#line 473 "engines/director/lingo/lingo-gr.y"
                                                         { (yyval.node) = new DeleteNode((yyvsp[-1].node)); }
-#line 3543 "engines/director/lingo/lingo-gr.cpp"
+#line 3721 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 125: /* proc: tHILITE chunk '\n'  */
-#line 436 "engines/director/lingo/lingo-gr.y"
+  case 142: /* proc: tDELETE chunk error '\n'  */
+#line 474 "engines/director/lingo/lingo-gr.y"
+                                                                { TRIM_GARBAGE((yyval.node), new DeleteNode((yyvsp[-2].node))); }
+#line 3727 "engines/director/lingo/lingo-gr.cpp"
+    break;
+
+  case 143: /* proc: tHILITE chunk '\n'  */
+#line 475 "engines/director/lingo/lingo-gr.y"
                                                         { (yyval.node) = new HiliteNode((yyvsp[-1].node)); }
-#line 3549 "engines/director/lingo/lingo-gr.cpp"
+#line 3733 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 126: /* proc: tASSERTERROR stmtoneliner  */
-#line 437 "engines/director/lingo/lingo-gr.y"
+  case 144: /* proc: tHILITE chunk error '\n'  */
+#line 476 "engines/director/lingo/lingo-gr.y"
+                                                                { TRIM_GARBAGE((yyval.node), new HiliteNode((yyvsp[-2].node))); }
+#line 3739 "engines/director/lingo/lingo-gr.cpp"
+    break;
+
+  case 145: /* proc: tASSERTERROR stmtoneliner  */
+#line 477 "engines/director/lingo/lingo-gr.y"
                                                         { (yyval.node) = new AssertErrorNode((yyvsp[0].node)); }
-#line 3555 "engines/director/lingo/lingo-gr.cpp"
+#line 3745 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 127: /* cmdargs: %empty  */
-#line 440 "engines/director/lingo/lingo-gr.y"
+  case 146: /* cmdargs: %empty  */
+#line 480 "engines/director/lingo/lingo-gr.y"
                                                                                         {
 		// This matches `cmd`
 		(yyval.nodelist) = new NodeList; }
-#line 3563 "engines/director/lingo/lingo-gr.cpp"
+#line 3753 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 128: /* cmdargs: expr trailingcomma  */
-#line 443 "engines/director/lingo/lingo-gr.y"
+  case 147: /* cmdargs: expr trailingcomma  */
+#line 483 "engines/director/lingo/lingo-gr.y"
                                                                                         {
 		// This matches `cmd arg` and `cmd(arg)`
 		NodeList *args = new NodeList;
 		args->push_back((yyvsp[-1].node));
 		(yyval.nodelist) = args; }
-#line 3573 "engines/director/lingo/lingo-gr.cpp"
+#line 3763 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 129: /* cmdargs: expr ',' nonemptyexprlist trailingcomma  */
-#line 448 "engines/director/lingo/lingo-gr.y"
+  case 148: /* cmdargs: expr ',' nonemptyexprlist trailingcomma  */
+#line 488 "engines/director/lingo/lingo-gr.y"
                                                                 {
 		// This matches `cmd arg, ...)
 		(yyvsp[-1].nodelist)->insert_at(0, (yyvsp[-3].node));
 		(yyval.nodelist) = (yyvsp[-1].nodelist); }
-#line 3582 "engines/director/lingo/lingo-gr.cpp"
+#line 3772 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 130: /* cmdargs: expr expr_nounarymath trailingcomma  */
-#line 452 "engines/director/lingo/lingo-gr.y"
+  case 149: /* cmdargs: expr expr_nounarymath trailingcomma  */
+#line 492 "engines/director/lingo/lingo-gr.y"
                                                                         {
 		// This matches `cmd arg arg`
 		NodeList *args = new NodeList;
 		args->push_back((yyvsp[-2].node));
 		args->push_back((yyvsp[-1].node));
 		(yyval.nodelist) = args; }
-#line 3593 "engines/director/lingo/lingo-gr.cpp"
+#line 3783 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 131: /* cmdargs: expr expr_nounarymath ',' nonemptyexprlist trailingcomma  */
-#line 458 "engines/director/lingo/lingo-gr.y"
+  case 150: /* cmdargs: expr expr_nounarymath ',' nonemptyexprlist trailingcomma  */
+#line 498 "engines/director/lingo/lingo-gr.y"
                                                                                 {
 		// This matches `cmd arg arg, ...`
 		(yyvsp[-1].nodelist)->insert_at(0, (yyvsp[-3].node));
 		(yyvsp[-1].nodelist)->insert_at(0, (yyvsp[-4].node));
 		(yyval.nodelist) = (yyvsp[-1].nodelist); }
-#line 3603 "engines/director/lingo/lingo-gr.cpp"
+#line 3793 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 132: /* cmdargs: '(' ')'  */
-#line 463 "engines/director/lingo/lingo-gr.y"
+  case 151: /* cmdargs: '(' ')'  */
+#line 503 "engines/director/lingo/lingo-gr.y"
                                                                         {
 		// This matches `cmd()`
 		(yyval.nodelist) = new NodeList; }
-#line 3611 "engines/director/lingo/lingo-gr.cpp"
+#line 3801 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 133: /* cmdargs: '(' expr ',' ')'  */
-#line 466 "engines/director/lingo/lingo-gr.y"
+  case 152: /* cmdargs: '(' expr ',' ')'  */
+#line 506 "engines/director/lingo/lingo-gr.y"
                            {
 		// This matches `cmd(arg,)`
 		NodeList *args = new NodeList;
 		args->push_back((yyvsp[-2].node));
 		(yyval.nodelist) = args; }
-#line 3621 "engines/director/lingo/lingo-gr.cpp"
+#line 3811 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 134: /* cmdargs: '(' expr ',' nonemptyexprlist trailingcomma ')'  */
-#line 471 "engines/director/lingo/lingo-gr.y"
+  case 153: /* cmdargs: '(' expr ',' nonemptyexprlist trailingcomma ')'  */
+#line 511 "engines/director/lingo/lingo-gr.y"
                                                                 {
 		// This matches `cmd(arg, ...)`
 		(yyvsp[-2].nodelist)->insert_at(0, (yyvsp[-4].node));
 		(yyval.nodelist) = (yyvsp[-2].nodelist); }
-#line 3630 "engines/director/lingo/lingo-gr.cpp"
+#line 3820 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 135: /* cmdargs: '(' var expr_nounarymath trailingcomma ')'  */
-#line 475 "engines/director/lingo/lingo-gr.y"
+  case 154: /* cmdargs: '(' var expr_nounarymath trailingcomma ')'  */
+#line 515 "engines/director/lingo/lingo-gr.y"
                                                                 {
 		// This matches `obj(method arg)`
 		NodeList *args = new NodeList;
 		args->push_back((yyvsp[-3].node));
 		args->push_back((yyvsp[-2].node));
 		(yyval.nodelist) = args; }
-#line 3641 "engines/director/lingo/lingo-gr.cpp"
+#line 3831 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 136: /* cmdargs: '(' var expr_nounarymath ',' nonemptyexprlist trailingcomma ')'  */
-#line 481 "engines/director/lingo/lingo-gr.y"
+  case 155: /* cmdargs: '(' var expr_nounarymath ',' nonemptyexprlist trailingcomma ')'  */
+#line 521 "engines/director/lingo/lingo-gr.y"
                                                                                         {
 		// This matches `obj(method arg, ...)`
 		(yyvsp[-2].nodelist)->insert_at(0, (yyvsp[-4].node));
 		(yyvsp[-2].nodelist)->insert_at(0, (yyvsp[-5].node));
 		(yyval.nodelist) = (yyvsp[-2].nodelist); }
-#line 3651 "engines/director/lingo/lingo-gr.cpp"
+#line 3841 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 139: /* frameargs: tFRAME expr  */
-#line 494 "engines/director/lingo/lingo-gr.y"
+  case 158: /* frameargs: tFRAME expr  */
+#line 534 "engines/director/lingo/lingo-gr.y"
                                                                         {
 		// This matches `play frame arg`
 		NodeList *args = new NodeList;
 		args->push_back(new FrameNode((yyvsp[0].node)));
 		(yyval.nodelist) = args; }
-#line 3661 "engines/director/lingo/lingo-gr.cpp"
+#line 3851 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 140: /* frameargs: tMOVIE expr  */
-#line 499 "engines/director/lingo/lingo-gr.y"
+  case 159: /* frameargs: tMOVIE expr  */
+#line 539 "engines/director/lingo/lingo-gr.y"
                                                                                 {
 		// This matches `play movie arg`
 		NodeList *args = new NodeList;
 		args->push_back(new IntNode(1));
 		args->push_back(new MovieNode((yyvsp[0].node)));
 		(yyval.nodelist) = args; }
-#line 3672 "engines/director/lingo/lingo-gr.cpp"
+#line 3862 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 141: /* frameargs: tFRAME expr tOF tMOVIE expr  */
-#line 505 "engines/director/lingo/lingo-gr.y"
+  case 160: /* frameargs: tFRAME expr tOF tMOVIE expr  */
+#line 545 "engines/director/lingo/lingo-gr.y"
                                                                 {
 		// This matches `play frame arg of movie arg`
 		NodeList *args = new NodeList;
 		args->push_back(new FrameNode((yyvsp[-3].node)));
 		args->push_back(new MovieNode((yyvsp[0].node)));
 		(yyval.nodelist) = args; }
-#line 3683 "engines/director/lingo/lingo-gr.cpp"
+#line 3873 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 142: /* frameargs: expr tOF tMOVIE expr  */
-#line 511 "engines/director/lingo/lingo-gr.y"
+  case 161: /* frameargs: expr tOF tMOVIE expr  */
+#line 551 "engines/director/lingo/lingo-gr.y"
                                                                 {
 		// This matches `play arg of movie arg` (weird but valid)
 		NodeList *args = new NodeList;
 		args->push_back((yyvsp[-3].node));
 		args->push_back(new MovieNode((yyvsp[0].node)));
 		(yyval.nodelist) = args; }
-#line 3694 "engines/director/lingo/lingo-gr.cpp"
+#line 3884 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 143: /* frameargs: tFRAME expr expr_nounarymath  */
-#line 517 "engines/director/lingo/lingo-gr.y"
+  case 162: /* frameargs: tFRAME expr expr_nounarymath  */
+#line 557 "engines/director/lingo/lingo-gr.y"
                                                         {
 		// This matches `play frame arg arg` (also weird but valid)
 		NodeList *args = new NodeList;
 		args->push_back(new FrameNode((yyvsp[-1].node)));
 		args->push_back((yyvsp[0].node));
 		(yyval.nodelist) = args; }
-#line 3705 "engines/director/lingo/lingo-gr.cpp"
+#line 3895 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 144: /* asgn: tPUT expr tINTO varorchunk '\n'  */
-#line 525 "engines/director/lingo/lingo-gr.y"
+  case 163: /* asgn: tPUT expr tINTO varorchunk '\n'  */
+#line 565 "engines/director/lingo/lingo-gr.y"
                                         { (yyval.node) = new PutIntoNode((yyvsp[-3].node), (yyvsp[-1].node)); }
-#line 3711 "engines/director/lingo/lingo-gr.cpp"
+#line 3901 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 145: /* asgn: tPUT expr tAFTER varorchunk '\n'  */
-#line 526 "engines/director/lingo/lingo-gr.y"
+  case 164: /* asgn: tPUT expr tINTO varorchunk error '\n'  */
+#line 566 "engines/director/lingo/lingo-gr.y"
+                                                { TRIM_GARBAGE((yyval.node), new PutIntoNode((yyvsp[-4].node), (yyvsp[-2].node))); }
+#line 3907 "engines/director/lingo/lingo-gr.cpp"
+    break;
+
+  case 165: /* asgn: tPUT expr tAFTER varorchunk '\n'  */
+#line 567 "engines/director/lingo/lingo-gr.y"
                                                 { (yyval.node) = new PutAfterNode((yyvsp[-3].node), (yyvsp[-1].node)); }
-#line 3717 "engines/director/lingo/lingo-gr.cpp"
+#line 3913 "engines/director/lingo/lingo-gr.cpp"
+    break;
+
+  case 166: /* asgn: tPUT expr tAFTER varorchunk error '\n'  */
+#line 568 "engines/director/lingo/lingo-gr.y"
+                                                        { TRIM_GARBAGE((yyval.node), new PutAfterNode((yyvsp[-4].node), (yyvsp[-2].node))); }
+#line 3919 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 146: /* asgn: tPUT expr tBEFORE varorchunk '\n'  */
-#line 527 "engines/director/lingo/lingo-gr.y"
+  case 167: /* asgn: tPUT expr tBEFORE varorchunk '\n'  */
+#line 569 "engines/director/lingo/lingo-gr.y"
                                                 { (yyval.node) = new PutBeforeNode((yyvsp[-3].node), (yyvsp[-1].node)); }
-#line 3723 "engines/director/lingo/lingo-gr.cpp"
+#line 3925 "engines/director/lingo/lingo-gr.cpp"
+    break;
+
+  case 168: /* asgn: tPUT expr tBEFORE varorchunk error '\n'  */
+#line 570 "engines/director/lingo/lingo-gr.y"
+                                                        { TRIM_GARBAGE((yyval.node), new PutBeforeNode((yyvsp[-4].node), (yyvsp[-2].node))); }
+#line 3931 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 147: /* asgn: tSET varorthe to expr '\n'  */
-#line 528 "engines/director/lingo/lingo-gr.y"
+  case 169: /* asgn: tSET varorthe to expr '\n'  */
+#line 571 "engines/director/lingo/lingo-gr.y"
                                                 { (yyval.node) = new SetNode((yyvsp[-3].node), (yyvsp[-1].node)); }
-#line 3729 "engines/director/lingo/lingo-gr.cpp"
+#line 3937 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 150: /* definevars: tGLOBAL idlist '\n'  */
-#line 533 "engines/director/lingo/lingo-gr.y"
+  case 170: /* asgn: tSET varorthe to expr error '\n'  */
+#line 572 "engines/director/lingo/lingo-gr.y"
+                                                { TRIM_GARBAGE((yyval.node), new SetNode((yyvsp[-4].node), (yyvsp[-2].node))); }
+#line 3943 "engines/director/lingo/lingo-gr.cpp"
+    break;
+
+  case 173: /* definevars: tGLOBAL idlist '\n'  */
+#line 577 "engines/director/lingo/lingo-gr.y"
                                                 { (yyval.node) = new GlobalNode((yyvsp[-1].idlist)); }
-#line 3735 "engines/director/lingo/lingo-gr.cpp"
+#line 3949 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 151: /* definevars: tPROPERTY idlist '\n'  */
-#line 534 "engines/director/lingo/lingo-gr.y"
+  case 174: /* definevars: tGLOBAL idlist error '\n'  */
+#line 578 "engines/director/lingo/lingo-gr.y"
+                                                        { TRIM_GARBAGE((yyval.node), new GlobalNode((yyvsp[-2].idlist))); }
+#line 3955 "engines/director/lingo/lingo-gr.cpp"
+    break;
+
+  case 175: /* definevars: tPROPERTY idlist '\n'  */
+#line 579 "engines/director/lingo/lingo-gr.y"
                                                         { (yyval.node) = new PropertyNode((yyvsp[-1].idlist)); }
-#line 3741 "engines/director/lingo/lingo-gr.cpp"
+#line 3961 "engines/director/lingo/lingo-gr.cpp"
+    break;
+
+  case 176: /* definevars: tPROPERTY idlist error '\n'  */
+#line 580 "engines/director/lingo/lingo-gr.y"
+                                                                { TRIM_GARBAGE((yyval.node), new PropertyNode((yyvsp[-2].idlist))); }
+#line 3967 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 152: /* definevars: tINSTANCE idlist '\n'  */
-#line 535 "engines/director/lingo/lingo-gr.y"
+  case 177: /* definevars: tINSTANCE idlist '\n'  */
+#line 581 "engines/director/lingo/lingo-gr.y"
                                                         { (yyval.node) = new InstanceNode((yyvsp[-1].idlist)); }
-#line 3747 "engines/director/lingo/lingo-gr.cpp"
+#line 3973 "engines/director/lingo/lingo-gr.cpp"
+    break;
+
+  case 178: /* definevars: tINSTANCE idlist error '\n'  */
+#line 582 "engines/director/lingo/lingo-gr.y"
+                                                                { TRIM_GARBAGE((yyval.node), new InstanceNode((yyvsp[-2].idlist))); }
+#line 3979 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 153: /* ifstmt: tIF expr tTHEN stmt  */
-#line 538 "engines/director/lingo/lingo-gr.y"
+  case 179: /* ifstmt: tIF expr tTHEN stmt  */
+#line 585 "engines/director/lingo/lingo-gr.y"
                             {
 		NodeList *stmtlist = new NodeList;
 		stmtlist->push_back((yyvsp[0].node));
 		(yyval.node) = new IfStmtNode((yyvsp[-2].node), stmtlist); }
-#line 3756 "engines/director/lingo/lingo-gr.cpp"
+#line 3988 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 154: /* ifstmt: tIF expr tTHEN '\n' stmtlist_insideif endif  */
-#line 542 "engines/director/lingo/lingo-gr.y"
+  case 180: /* ifstmt: tIF expr tTHEN '\n' stmtlist_insideif endif  */
+#line 589 "engines/director/lingo/lingo-gr.y"
                                                       {
 		(yyval.node) = new IfStmtNode((yyvsp[-4].node), (yyvsp[-1].nodelist)); }
-#line 3763 "engines/director/lingo/lingo-gr.cpp"
+#line 3995 "engines/director/lingo/lingo-gr.cpp"
+    break;
+
+  case 181: /* ifstmt: tIF expr tTHEN error '\n' stmtlist_insideif endif  */
+#line 591 "engines/director/lingo/lingo-gr.y"
+                                                            {
+		TRIM_GARBAGE((yyval.node), new IfStmtNode((yyvsp[-5].node), (yyvsp[-1].nodelist))); }
+#line 4002 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 155: /* ifelsestmt: tIF expr tTHEN stmt tELSE stmt  */
-#line 546 "engines/director/lingo/lingo-gr.y"
+  case 182: /* ifelsestmt: tIF expr tTHEN stmt tELSE stmt  */
+#line 595 "engines/director/lingo/lingo-gr.y"
                                                          {
 		NodeList *stmtlist1 = new NodeList;
 		stmtlist1->push_back((yyvsp[-2].node));
 		NodeList *stmtlist2 = new NodeList;
 		stmtlist2->push_back((yyvsp[0].node));
 		(yyval.node) = new IfElseStmtNode((yyvsp[-4].node), stmtlist1, stmtlist2); }
-#line 3774 "engines/director/lingo/lingo-gr.cpp"
+#line 4013 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 156: /* ifelsestmt: tIF expr tTHEN stmt tELSE '\n' stmtlist_insideif endif  */
-#line 552 "engines/director/lingo/lingo-gr.y"
+  case 183: /* ifelsestmt: tIF expr tTHEN stmt tELSE '\n' stmtlist_insideif endif  */
+#line 601 "engines/director/lingo/lingo-gr.y"
                                                                                    {
 		NodeList *stmtlist1 = new NodeList;
 		stmtlist1->push_back((yyvsp[-4].node));
 		(yyval.node) = new IfElseStmtNode((yyvsp[-6].node), stmtlist1, (yyvsp[-1].nodelist)); }
-#line 3783 "engines/director/lingo/lingo-gr.cpp"
+#line 4022 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 157: /* ifelsestmt: tIF expr tTHEN '\n' stmtlist_insideif tELSE stmt  */
-#line 556 "engines/director/lingo/lingo-gr.y"
+  case 184: /* ifelsestmt: tIF expr tTHEN '\n' stmtlist_insideif tELSE stmt  */
+#line 605 "engines/director/lingo/lingo-gr.y"
                                                                              {
 		NodeList *stmtlist2 = new NodeList;
 		stmtlist2->push_back((yyvsp[0].node));
 		(yyval.node) = new IfElseStmtNode((yyvsp[-5].node), (yyvsp[-2].nodelist), stmtlist2); }
-#line 3792 "engines/director/lingo/lingo-gr.cpp"
+#line 4031 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 158: /* ifelsestmt: tIF expr tTHEN '\n' stmtlist_insideif tELSE '\n' stmtlist_insideif endif  */
-#line 560 "engines/director/lingo/lingo-gr.y"
+  case 185: /* ifelsestmt: tIF expr tTHEN '\n' stmtlist_insideif tELSE '\n' stmtlist_insideif endif  */
+#line 609 "engines/director/lingo/lingo-gr.y"
                                                                                                          {
 		(yyval.node) = new IfElseStmtNode((yyvsp[-7].node), (yyvsp[-4].nodelist), (yyvsp[-1].nodelist)); }
-#line 3799 "engines/director/lingo/lingo-gr.cpp"
+#line 4038 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 159: /* endif: %empty  */
-#line 564 "engines/director/lingo/lingo-gr.y"
+  case 186: /* endif: %empty  */
+#line 613 "engines/director/lingo/lingo-gr.y"
                         {
 		LingoCompiler *compiler = g_lingo->_compiler;
 		warning("LingoCompiler::parse: no end if at line %d col %d in %s id: %d",
@@ -3807,967 +4046,1002 @@ yyreduce:
 			compiler->_assemblyContext->_id);
 
 		}
-#line 3811 "engines/director/lingo/lingo-gr.cpp"
+#line 4050 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 161: /* loop: tREPEAT tWHILE expr '\n' stmtlist tENDREPEAT '\n'  */
-#line 573 "engines/director/lingo/lingo-gr.y"
+  case 188: /* loop: tREPEAT tWHILE expr '\n' stmtlist tENDREPEAT '\n'  */
+#line 622 "engines/director/lingo/lingo-gr.y"
                                                         {
 		(yyval.node) = new RepeatWhileNode((yyvsp[-4].node), (yyvsp[-2].nodelist)); }
-#line 3818 "engines/director/lingo/lingo-gr.cpp"
+#line 4057 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 162: /* loop: tREPEAT tWITH ID tEQ expr tTO expr '\n' stmtlist tENDREPEAT '\n'  */
-#line 575 "engines/director/lingo/lingo-gr.y"
+  case 189: /* loop: tREPEAT tWHILE expr error '\n' stmtlist tENDREPEAT '\n'  */
+#line 624 "engines/director/lingo/lingo-gr.y"
+                                                                  {
+		TRIM_GARBAGE((yyval.node), new RepeatWhileNode((yyvsp[-5].node), (yyvsp[-2].nodelist))); }
+#line 4064 "engines/director/lingo/lingo-gr.cpp"
+    break;
+
+  case 190: /* loop: tREPEAT tWITH ID tEQ expr tTO expr '\n' stmtlist tENDREPEAT '\n'  */
+#line 626 "engines/director/lingo/lingo-gr.y"
                                                                                        {
 		(yyval.node) = new RepeatWithToNode((yyvsp[-8].s), (yyvsp[-6].node), false, (yyvsp[-4].node), (yyvsp[-2].nodelist)); }
-#line 3825 "engines/director/lingo/lingo-gr.cpp"
+#line 4071 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 163: /* loop: tREPEAT tWITH ID tEQ expr tDOWN tTO expr '\n' stmtlist tENDREPEAT '\n'  */
-#line 577 "engines/director/lingo/lingo-gr.y"
+  case 191: /* loop: tREPEAT tWITH ID tEQ expr tTO expr error '\n' stmtlist tENDREPEAT '\n'  */
+#line 628 "engines/director/lingo/lingo-gr.y"
+                                                                                             {
+		TRIM_GARBAGE((yyval.node), new RepeatWithToNode((yyvsp[-9].s), (yyvsp[-7].node), false, (yyvsp[-5].node), (yyvsp[-2].nodelist))); }
+#line 4078 "engines/director/lingo/lingo-gr.cpp"
+    break;
+
+  case 192: /* loop: tREPEAT tWITH ID tEQ expr tDOWN tTO expr '\n' stmtlist tENDREPEAT '\n'  */
+#line 630 "engines/director/lingo/lingo-gr.y"
                                                                                              {
 		(yyval.node) = new RepeatWithToNode((yyvsp[-9].s), (yyvsp[-7].node), true, (yyvsp[-4].node), (yyvsp[-2].nodelist)); }
-#line 3832 "engines/director/lingo/lingo-gr.cpp"
+#line 4085 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 164: /* loop: tREPEAT tWITH ID tIN expr '\n' stmtlist tENDREPEAT '\n'  */
-#line 579 "engines/director/lingo/lingo-gr.y"
+  case 193: /* loop: tREPEAT tWITH ID tEQ expr tDOWN tTO expr error '\n' stmtlist tENDREPEAT '\n'  */
+#line 632 "engines/director/lingo/lingo-gr.y"
+                                                                                                   {
+		TRIM_GARBAGE((yyval.node), new RepeatWithToNode((yyvsp[-10].s), (yyvsp[-8].node), true, (yyvsp[-5].node), (yyvsp[-2].nodelist))); }
+#line 4092 "engines/director/lingo/lingo-gr.cpp"
+    break;
+
+  case 194: /* loop: tREPEAT tWITH ID tIN expr '\n' stmtlist tENDREPEAT '\n'  */
+#line 634 "engines/director/lingo/lingo-gr.y"
                                                                   {
 		(yyval.node) = new RepeatWithInNode((yyvsp[-6].s), (yyvsp[-4].node), (yyvsp[-2].nodelist)); }
-#line 3839 "engines/director/lingo/lingo-gr.cpp"
+#line 4099 "engines/director/lingo/lingo-gr.cpp"
+    break;
+
+  case 195: /* loop: tREPEAT tWITH ID tIN expr error '\n' stmtlist tENDREPEAT '\n'  */
+#line 636 "engines/director/lingo/lingo-gr.y"
+                                                                        {
+		TRIM_GARBAGE((yyval.node), new RepeatWithInNode((yyvsp[-7].s), (yyvsp[-5].node), (yyvsp[-2].nodelist))); }
+#line 4106 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 165: /* tell: tTELL expr tTO stmtoneliner  */
-#line 583 "engines/director/lingo/lingo-gr.y"
+  case 196: /* tell: tTELL expr tTO stmtoneliner  */
+#line 640 "engines/director/lingo/lingo-gr.y"
                                                                 {
 		NodeList *stmtlist = new NodeList;
 		stmtlist->push_back((yyvsp[0].node));
 		(yyval.node) = new TellNode((yyvsp[-2].node), stmtlist); }
-#line 3848 "engines/director/lingo/lingo-gr.cpp"
+#line 4115 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 166: /* tell: tTELL expr '\n' stmtlist tENDTELL '\n'  */
-#line 587 "engines/director/lingo/lingo-gr.y"
+  case 197: /* tell: tTELL expr '\n' stmtlist tENDTELL '\n'  */
+#line 644 "engines/director/lingo/lingo-gr.y"
                                                         {
 		(yyval.node) = new TellNode((yyvsp[-4].node), (yyvsp[-2].nodelist)); }
-#line 3855 "engines/director/lingo/lingo-gr.cpp"
+#line 4122 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 167: /* when: tWHEN '\n'  */
-#line 591 "engines/director/lingo/lingo-gr.y"
+  case 198: /* tell: tTELL expr error '\n' stmtlist tENDTELL '\n'  */
+#line 646 "engines/director/lingo/lingo-gr.y"
+                                                        {
+		TRIM_GARBAGE((yyval.node), new TellNode((yyvsp[-5].node), (yyvsp[-2].nodelist))); }
+#line 4129 "engines/director/lingo/lingo-gr.cpp"
+    break;
+
+  case 199: /* when: tWHEN '\n'  */
+#line 650 "engines/director/lingo/lingo-gr.y"
                                                         { (yyval.node) = new WhenNode((yyvsp[-1].w).eventName, (yyvsp[-1].w).stmt); }
-#line 3861 "engines/director/lingo/lingo-gr.cpp"
+#line 4135 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 168: /* stmtlist: %empty  */
-#line 593 "engines/director/lingo/lingo-gr.y"
+  case 200: /* stmtlist: %empty  */
+#line 652 "engines/director/lingo/lingo-gr.y"
                                                 { (yyval.nodelist) = new NodeList; }
-#line 3867 "engines/director/lingo/lingo-gr.cpp"
+#line 4141 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 170: /* nonemptystmtlist: stmtlistline  */
-#line 598 "engines/director/lingo/lingo-gr.y"
+  case 202: /* nonemptystmtlist: stmtlistline  */
+#line 657 "engines/director/lingo/lingo-gr.y"
                                                                 {
 		NodeList *list = new NodeList;
 		if ((yyvsp[0].node)) {
 			list->push_back((yyvsp[0].node));
 		}
 		(yyval.nodelist) = list; }
-#line 3878 "engines/director/lingo/lingo-gr.cpp"
+#line 4152 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 171: /* nonemptystmtlist: nonemptystmtlist stmtlistline  */
-#line 604 "engines/director/lingo/lingo-gr.y"
+  case 203: /* nonemptystmtlist: nonemptystmtlist stmtlistline  */
+#line 663 "engines/director/lingo/lingo-gr.y"
                                                         {
 		if ((yyvsp[0].node)) {
 			(yyvsp[-1].nodelist)->push_back((yyvsp[0].node));
 		}
 		(yyval.nodelist) = (yyvsp[-1].nodelist); }
-#line 3888 "engines/director/lingo/lingo-gr.cpp"
+#line 4162 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 172: /* stmtlistline: '\n'  */
-#line 611 "engines/director/lingo/lingo-gr.y"
+  case 204: /* stmtlistline: '\n'  */
+#line 670 "engines/director/lingo/lingo-gr.y"
                                                         { (yyval.node) = nullptr; }
-#line 3894 "engines/director/lingo/lingo-gr.cpp"
+#line 4168 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 174: /* stmtlist_insideif: %empty  */
-#line 615 "engines/director/lingo/lingo-gr.y"
+  case 206: /* stmtlist_insideif: %empty  */
+#line 674 "engines/director/lingo/lingo-gr.y"
                                         { (yyval.nodelist) = new NodeList; }
-#line 3900 "engines/director/lingo/lingo-gr.cpp"
+#line 4174 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 176: /* nonemptystmtlist_insideif: stmtlistline_insideif  */
-#line 620 "engines/director/lingo/lingo-gr.y"
+  case 208: /* nonemptystmtlist_insideif: stmtlistline_insideif  */
+#line 679 "engines/director/lingo/lingo-gr.y"
                                                 {
 		NodeList *list = new NodeList;
 		if ((yyvsp[0].node)) {
 			list->push_back((yyvsp[0].node));
 		}
 		(yyval.nodelist) = list; }
-#line 3911 "engines/director/lingo/lingo-gr.cpp"
+#line 4185 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 177: /* nonemptystmtlist_insideif: nonemptystmtlist_insideif stmtlistline_insideif  */
-#line 626 "engines/director/lingo/lingo-gr.y"
+  case 209: /* nonemptystmtlist_insideif: nonemptystmtlist_insideif stmtlistline_insideif  */
+#line 685 "engines/director/lingo/lingo-gr.y"
                                                                         {
 		if ((yyvsp[0].node)) {
 			(yyvsp[-1].nodelist)->push_back((yyvsp[0].node));
 		}
 		(yyval.nodelist) = (yyvsp[-1].nodelist); }
-#line 3921 "engines/director/lingo/lingo-gr.cpp"
+#line 4195 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 178: /* stmtlistline_insideif: '\n'  */
-#line 633 "engines/director/lingo/lingo-gr.y"
+  case 210: /* stmtlistline_insideif: '\n'  */
+#line 692 "engines/director/lingo/lingo-gr.y"
                                                 { (yyval.node) = nullptr; }
-#line 3927 "engines/director/lingo/lingo-gr.cpp"
+#line 4201 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 180: /* simpleexpr_nounarymath: tINT  */
-#line 640 "engines/director/lingo/lingo-gr.y"
+  case 212: /* simpleexpr_nounarymath: tINT  */
+#line 699 "engines/director/lingo/lingo-gr.y"
                                                                 { (yyval.node) = new IntNode((yyvsp[0].i)); }
-#line 3933 "engines/director/lingo/lingo-gr.cpp"
+#line 4207 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 181: /* simpleexpr_nounarymath: tFLOAT  */
-#line 641 "engines/director/lingo/lingo-gr.y"
+  case 213: /* simpleexpr_nounarymath: tFLOAT  */
+#line 700 "engines/director/lingo/lingo-gr.y"
                                                                 { (yyval.node) = new FloatNode((yyvsp[0].f)); }
-#line 3939 "engines/director/lingo/lingo-gr.cpp"
+#line 4213 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 182: /* simpleexpr_nounarymath: tSYMBOL  */
-#line 642 "engines/director/lingo/lingo-gr.y"
+  case 214: /* simpleexpr_nounarymath: tSYMBOL  */
+#line 701 "engines/director/lingo/lingo-gr.y"
                                                                 { (yyval.node) = new SymbolNode((yyvsp[0].s)); }
-#line 3945 "engines/director/lingo/lingo-gr.cpp"
+#line 4219 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 183: /* simpleexpr_nounarymath: tSTRING  */
-#line 643 "engines/director/lingo/lingo-gr.y"
+  case 215: /* simpleexpr_nounarymath: tSTRING  */
+#line 702 "engines/director/lingo/lingo-gr.y"
                                                                 { (yyval.node) = new StringNode((yyvsp[0].s)); }
-#line 3951 "engines/director/lingo/lingo-gr.cpp"
+#line 4225 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 184: /* simpleexpr_nounarymath: tNOT simpleexpr  */
-#line 644 "engines/director/lingo/lingo-gr.y"
+  case 216: /* simpleexpr_nounarymath: tNOT simpleexpr  */
+#line 703 "engines/director/lingo/lingo-gr.y"
                                                 { (yyval.node) = new UnaryOpNode(LC::c_not, (yyvsp[0].node)); }
-#line 3957 "engines/director/lingo/lingo-gr.cpp"
+#line 4231 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 185: /* simpleexpr_nounarymath: ID '(' ')'  */
-#line 645 "engines/director/lingo/lingo-gr.y"
+  case 217: /* simpleexpr_nounarymath: ID '(' ')'  */
+#line 704 "engines/director/lingo/lingo-gr.y"
                                                         { (yyval.node) = new FuncNode((yyvsp[-2].s), new NodeList); }
-#line 3963 "engines/director/lingo/lingo-gr.cpp"
+#line 4237 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 186: /* simpleexpr_nounarymath: ID '(' nonemptyexprlist trailingcomma ')'  */
-#line 646 "engines/director/lingo/lingo-gr.y"
+  case 218: /* simpleexpr_nounarymath: ID '(' nonemptyexprlist trailingcomma ')'  */
+#line 705 "engines/director/lingo/lingo-gr.y"
                                                                 { (yyval.node) = new FuncNode((yyvsp[-4].s), (yyvsp[-2].nodelist)); }
-#line 3969 "engines/director/lingo/lingo-gr.cpp"
+#line 4243 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 187: /* simpleexpr_nounarymath: ID '(' var expr_nounarymath trailingcomma ')'  */
-#line 647 "engines/director/lingo/lingo-gr.y"
+  case 219: /* simpleexpr_nounarymath: ID '(' var expr_nounarymath trailingcomma ')'  */
+#line 706 "engines/director/lingo/lingo-gr.y"
                                                                 {
 		// This matches `obj(method arg)`
 		NodeList *args = new NodeList;
 		args->push_back((yyvsp[-3].node));
 		args->push_back((yyvsp[-2].node));
 		(yyval.node) = new FuncNode((yyvsp[-5].s), args); }
-#line 3980 "engines/director/lingo/lingo-gr.cpp"
+#line 4254 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 188: /* simpleexpr_nounarymath: ID '(' var expr_nounarymath ',' nonemptyexprlist trailingcomma ')'  */
-#line 653 "engines/director/lingo/lingo-gr.y"
+  case 220: /* simpleexpr_nounarymath: ID '(' var expr_nounarymath ',' nonemptyexprlist trailingcomma ')'  */
+#line 712 "engines/director/lingo/lingo-gr.y"
                                                                                                 {
 		// This matches `obj(method arg, ...)`
 		(yyvsp[-2].nodelist)->insert_at(0, (yyvsp[-4].node));
 		(yyvsp[-2].nodelist)->insert_at(0, (yyvsp[-5].node));
 		(yyval.node) = new FuncNode((yyvsp[-7].s), (yyvsp[-2].nodelist)); }
-#line 3990 "engines/director/lingo/lingo-gr.cpp"
+#line 4264 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 189: /* simpleexpr_nounarymath: '(' expr ')'  */
-#line 658 "engines/director/lingo/lingo-gr.y"
+  case 221: /* simpleexpr_nounarymath: '(' expr ')'  */
+#line 717 "engines/director/lingo/lingo-gr.y"
                                                         { (yyval.node) = (yyvsp[-1].node); }
-#line 3996 "engines/director/lingo/lingo-gr.cpp"
+#line 4270 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 195: /* var: ID  */
-#line 666 "engines/director/lingo/lingo-gr.y"
+  case 227: /* var: ID  */
+#line 725 "engines/director/lingo/lingo-gr.y"
                                                         { (yyval.node) = new VarNode((yyvsp[0].s)); }
-#line 4002 "engines/director/lingo/lingo-gr.cpp"
+#line 4276 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 200: /* chunk: tFIELD refargs  */
-#line 676 "engines/director/lingo/lingo-gr.y"
+  case 232: /* chunk: tFIELD refargs  */
+#line 735 "engines/director/lingo/lingo-gr.y"
                                 { (yyval.node) = new FuncNode(new Common::String("field"), (yyvsp[0].nodelist)); }
-#line 4008 "engines/director/lingo/lingo-gr.cpp"
+#line 4282 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 201: /* chunk: tCAST refargs  */
-#line 677 "engines/director/lingo/lingo-gr.y"
+  case 233: /* chunk: tCAST refargs  */
+#line 736 "engines/director/lingo/lingo-gr.y"
                                         { (yyval.node) = new FuncNode(new Common::String("cast"), (yyvsp[0].nodelist)); }
-#line 4014 "engines/director/lingo/lingo-gr.cpp"
+#line 4288 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 202: /* chunk: tMEMBER refargs  */
-#line 678 "engines/director/lingo/lingo-gr.y"
+  case 234: /* chunk: tMEMBER refargs  */
+#line 737 "engines/director/lingo/lingo-gr.y"
                                         { (yyval.node) = new FuncNode(new Common::String("member"), (yyvsp[0].nodelist)); }
-#line 4020 "engines/director/lingo/lingo-gr.cpp"
+#line 4294 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 203: /* chunk: tCASTLIB refargs  */
-#line 679 "engines/director/lingo/lingo-gr.y"
+  case 235: /* chunk: tCASTLIB refargs  */
+#line 738 "engines/director/lingo/lingo-gr.y"
                                         { (yyval.node) = new FuncNode(new Common::String("castLib"), (yyvsp[0].nodelist)); }
-#line 4026 "engines/director/lingo/lingo-gr.cpp"
+#line 4300 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 204: /* chunk: tCHAR expr tOF simpleexpr  */
-#line 680 "engines/director/lingo/lingo-gr.y"
+  case 236: /* chunk: tCHAR expr tOF simpleexpr  */
+#line 739 "engines/director/lingo/lingo-gr.y"
                                                 {
 		(yyval.node) = new ChunkExprNode(kChunkChar, (yyvsp[-2].node), nullptr, (yyvsp[0].node)); }
-#line 4033 "engines/director/lingo/lingo-gr.cpp"
+#line 4307 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 205: /* chunk: tCHAR expr tTO expr tOF simpleexpr  */
-#line 682 "engines/director/lingo/lingo-gr.y"
+  case 237: /* chunk: tCHAR expr tTO expr tOF simpleexpr  */
+#line 741 "engines/director/lingo/lingo-gr.y"
                                                                 {
 		(yyval.node) = new ChunkExprNode(kChunkChar, (yyvsp[-4].node), (yyvsp[-2].node), (yyvsp[0].node)); }
-#line 4040 "engines/director/lingo/lingo-gr.cpp"
+#line 4314 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 206: /* chunk: tWORD expr tOF simpleexpr  */
-#line 684 "engines/director/lingo/lingo-gr.y"
+  case 238: /* chunk: tWORD expr tOF simpleexpr  */
+#line 743 "engines/director/lingo/lingo-gr.y"
                                                 {
 		(yyval.node) = new ChunkExprNode(kChunkWord, (yyvsp[-2].node), nullptr, (yyvsp[0].node)); }
-#line 4047 "engines/director/lingo/lingo-gr.cpp"
+#line 4321 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 207: /* chunk: tWORD expr tTO expr tOF simpleexpr  */
-#line 686 "engines/director/lingo/lingo-gr.y"
+  case 239: /* chunk: tWORD expr tTO expr tOF simpleexpr  */
+#line 745 "engines/director/lingo/lingo-gr.y"
                                                                 {
 		(yyval.node) = new ChunkExprNode(kChunkWord, (yyvsp[-4].node), (yyvsp[-2].node), (yyvsp[0].node)); }
-#line 4054 "engines/director/lingo/lingo-gr.cpp"
+#line 4328 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 208: /* chunk: tITEM expr tOF simpleexpr  */
-#line 688 "engines/director/lingo/lingo-gr.y"
+  case 240: /* chunk: tITEM expr tOF simpleexpr  */
+#line 747 "engines/director/lingo/lingo-gr.y"
                                                 {
 		(yyval.node) = new ChunkExprNode(kChunkItem, (yyvsp[-2].node), nullptr, (yyvsp[0].node)); }
-#line 4061 "engines/director/lingo/lingo-gr.cpp"
+#line 4335 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 209: /* chunk: tITEM expr tTO expr tOF simpleexpr  */
-#line 690 "engines/director/lingo/lingo-gr.y"
+  case 241: /* chunk: tITEM expr tTO expr tOF simpleexpr  */
+#line 749 "engines/director/lingo/lingo-gr.y"
                                                                 {
 		(yyval.node) = new ChunkExprNode(kChunkItem, (yyvsp[-4].node), (yyvsp[-2].node), (yyvsp[0].node)); }
-#line 4068 "engines/director/lingo/lingo-gr.cpp"
+#line 4342 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 210: /* chunk: tLINE expr tOF simpleexpr  */
-#line 692 "engines/director/lingo/lingo-gr.y"
+  case 242: /* chunk: tLINE expr tOF simpleexpr  */
+#line 751 "engines/director/lingo/lingo-gr.y"
                                                 {
 		(yyval.node) = new ChunkExprNode(kChunkLine, (yyvsp[-2].node), nullptr, (yyvsp[0].node)); }
-#line 4075 "engines/director/lingo/lingo-gr.cpp"
+#line 4349 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 211: /* chunk: tLINE expr tTO expr tOF simpleexpr  */
-#line 694 "engines/director/lingo/lingo-gr.y"
+  case 243: /* chunk: tLINE expr tTO expr tOF simpleexpr  */
+#line 753 "engines/director/lingo/lingo-gr.y"
                                                                 {
 		(yyval.node) = new ChunkExprNode(kChunkLine, (yyvsp[-4].node), (yyvsp[-2].node), (yyvsp[0].node)); }
-#line 4082 "engines/director/lingo/lingo-gr.cpp"
+#line 4356 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 212: /* chunk: tTHE tLAST chunktype inof simpleexpr  */
-#line 696 "engines/director/lingo/lingo-gr.y"
+  case 244: /* chunk: tTHE tLAST chunktype inof simpleexpr  */
+#line 755 "engines/director/lingo/lingo-gr.y"
                                                 { (yyval.node) = new TheLastNode((yyvsp[-2].chunktype), (yyvsp[0].node)); }
-#line 4088 "engines/director/lingo/lingo-gr.cpp"
+#line 4362 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 213: /* chunktype: tCHAR  */
-#line 699 "engines/director/lingo/lingo-gr.y"
+  case 245: /* chunktype: tCHAR  */
+#line 758 "engines/director/lingo/lingo-gr.y"
                                                 { (yyval.chunktype) = kChunkChar; }
-#line 4094 "engines/director/lingo/lingo-gr.cpp"
+#line 4368 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 214: /* chunktype: tWORD  */
-#line 700 "engines/director/lingo/lingo-gr.y"
+  case 246: /* chunktype: tWORD  */
+#line 759 "engines/director/lingo/lingo-gr.y"
                                                         { (yyval.chunktype) = kChunkWord; }
-#line 4100 "engines/director/lingo/lingo-gr.cpp"
+#line 4374 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 215: /* chunktype: tITEM  */
-#line 701 "engines/director/lingo/lingo-gr.y"
+  case 247: /* chunktype: tITEM  */
+#line 760 "engines/director/lingo/lingo-gr.y"
                                                         { (yyval.chunktype) = kChunkItem; }
-#line 4106 "engines/director/lingo/lingo-gr.cpp"
+#line 4380 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 216: /* chunktype: tLINE  */
-#line 702 "engines/director/lingo/lingo-gr.y"
+  case 248: /* chunktype: tLINE  */
+#line 761 "engines/director/lingo/lingo-gr.y"
                                                         { (yyval.chunktype) = kChunkLine; }
-#line 4112 "engines/director/lingo/lingo-gr.cpp"
+#line 4386 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 217: /* object: tSCRIPT refargs  */
-#line 705 "engines/director/lingo/lingo-gr.y"
+  case 249: /* object: tSCRIPT refargs  */
+#line 764 "engines/director/lingo/lingo-gr.y"
                                 { (yyval.node) = new FuncNode(new Common::String("script"), (yyvsp[0].nodelist)); }
-#line 4118 "engines/director/lingo/lingo-gr.cpp"
+#line 4392 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 218: /* object: tWINDOW refargs  */
-#line 706 "engines/director/lingo/lingo-gr.y"
+  case 250: /* object: tWINDOW refargs  */
+#line 765 "engines/director/lingo/lingo-gr.y"
                                         { (yyval.node) = new FuncNode(new Common::String("window"), (yyvsp[0].nodelist)); }
-#line 4124 "engines/director/lingo/lingo-gr.cpp"
+#line 4398 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 219: /* refargs: simpleexpr  */
-#line 709 "engines/director/lingo/lingo-gr.y"
+  case 251: /* refargs: simpleexpr  */
+#line 768 "engines/director/lingo/lingo-gr.y"
                                                                                 {
 		// This matches `ref arg` and `ref(arg)`
 		NodeList *args = new NodeList;
 		args->push_back((yyvsp[0].node));
 		(yyval.nodelist) = args; }
-#line 4134 "engines/director/lingo/lingo-gr.cpp"
+#line 4408 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 220: /* refargs: '(' ')'  */
-#line 714 "engines/director/lingo/lingo-gr.y"
+  case 252: /* refargs: '(' ')'  */
+#line 773 "engines/director/lingo/lingo-gr.y"
                                                                                         {
 		// This matches `ref()`
 		(yyval.nodelist) = new NodeList; }
-#line 4142 "engines/director/lingo/lingo-gr.cpp"
+#line 4416 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 221: /* refargs: '(' expr ',' ')'  */
-#line 717 "engines/director/lingo/lingo-gr.y"
+  case 253: /* refargs: '(' expr ',' ')'  */
+#line 776 "engines/director/lingo/lingo-gr.y"
                            {
 		// This matches `ref(arg,)`
 		NodeList *args = new NodeList;
 		args->push_back((yyvsp[-2].node));
 		(yyval.nodelist) = args; }
-#line 4152 "engines/director/lingo/lingo-gr.cpp"
+#line 4426 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 222: /* refargs: '(' expr ',' nonemptyexprlist trailingcomma ')'  */
-#line 722 "engines/director/lingo/lingo-gr.y"
+  case 254: /* refargs: '(' expr ',' nonemptyexprlist trailingcomma ')'  */
+#line 781 "engines/director/lingo/lingo-gr.y"
                                                                 {
 		// This matches `ref(arg, ...)`
 		(yyvsp[-2].nodelist)->insert_at(0, (yyvsp[-4].node));
 		(yyval.nodelist) = (yyvsp[-2].nodelist); }
-#line 4161 "engines/director/lingo/lingo-gr.cpp"
+#line 4435 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 223: /* the: tTHE ID  */
-#line 728 "engines/director/lingo/lingo-gr.y"
+  case 255: /* the: tTHE ID  */
+#line 787 "engines/director/lingo/lingo-gr.y"
                                                                 { (yyval.node) = new TheNode((yyvsp[0].s)); }
-#line 4167 "engines/director/lingo/lingo-gr.cpp"
+#line 4441 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 224: /* the: tTHE ID tOF theobj  */
-#line 729 "engines/director/lingo/lingo-gr.y"
+  case 256: /* the: tTHE ID tOF theobj  */
+#line 788 "engines/director/lingo/lingo-gr.y"
                                                         { (yyval.node) = new TheOfNode((yyvsp[-2].s), (yyvsp[0].node)); }
-#line 4173 "engines/director/lingo/lingo-gr.cpp"
+#line 4447 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 225: /* the: tTHE tNUMBER tOF theobj  */
-#line 730 "engines/director/lingo/lingo-gr.y"
+  case 257: /* the: tTHE tNUMBER tOF theobj  */
+#line 789 "engines/director/lingo/lingo-gr.y"
                                                         { (yyval.node) = new TheOfNode(new Common::String("number"), (yyvsp[0].node)); }
-#line 4179 "engines/director/lingo/lingo-gr.cpp"
+#line 4453 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 230: /* theobj: tMENUITEM simpleexpr tOF tMENU simpleexpr  */
-#line 737 "engines/director/lingo/lingo-gr.y"
+  case 262: /* theobj: tMENUITEM simpleexpr tOF tMENU simpleexpr  */
+#line 796 "engines/director/lingo/lingo-gr.y"
                                                                 { (yyval.node) = new MenuItemNode((yyvsp[-3].node), (yyvsp[0].node)); }
-#line 4185 "engines/director/lingo/lingo-gr.cpp"
+#line 4459 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 231: /* theobj: tSOUND simpleexpr  */
-#line 738 "engines/director/lingo/lingo-gr.y"
+  case 263: /* theobj: tSOUND simpleexpr  */
+#line 797 "engines/director/lingo/lingo-gr.y"
                                                         { (yyval.node) = new SoundNode((yyvsp[0].node)); }
-#line 4191 "engines/director/lingo/lingo-gr.cpp"
+#line 4465 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 232: /* theobj: tSPRITE simpleexpr  */
-#line 739 "engines/director/lingo/lingo-gr.y"
+  case 264: /* theobj: tSPRITE simpleexpr  */
+#line 798 "engines/director/lingo/lingo-gr.y"
                                                         { (yyval.node) = new SpriteNode((yyvsp[0].node)); }
-#line 4197 "engines/director/lingo/lingo-gr.cpp"
+#line 4471 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 233: /* menu: tMENU simpleexpr  */
-#line 742 "engines/director/lingo/lingo-gr.y"
+  case 265: /* menu: tMENU simpleexpr  */
+#line 801 "engines/director/lingo/lingo-gr.y"
                                                         { (yyval.node) = new MenuNode((yyvsp[0].node)); }
-#line 4203 "engines/director/lingo/lingo-gr.cpp"
+#line 4477 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 234: /* thedatetime: tTHE tABBREVIATED tDATE  */
-#line 744 "engines/director/lingo/lingo-gr.y"
+  case 266: /* thedatetime: tTHE tABBREVIATED tDATE  */
+#line 803 "engines/director/lingo/lingo-gr.y"
                                         { (yyval.node) = new TheDateTimeNode(kTheAbbr, kTheDate); }
-#line 4209 "engines/director/lingo/lingo-gr.cpp"
+#line 4483 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 235: /* thedatetime: tTHE tABBREVIATED tTIME  */
-#line 745 "engines/director/lingo/lingo-gr.y"
+  case 267: /* thedatetime: tTHE tABBREVIATED tTIME  */
+#line 804 "engines/director/lingo/lingo-gr.y"
                                                         { (yyval.node) = new TheDateTimeNode(kTheAbbr, kTheTime); }
-#line 4215 "engines/director/lingo/lingo-gr.cpp"
+#line 4489 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 236: /* thedatetime: tTHE tABBREV tDATE  */
-#line 746 "engines/director/lingo/lingo-gr.y"
+  case 268: /* thedatetime: tTHE tABBREV tDATE  */
+#line 805 "engines/director/lingo/lingo-gr.y"
                                                         { (yyval.node) = new TheDateTimeNode(kTheAbbr, kTheDate); }
-#line 4221 "engines/director/lingo/lingo-gr.cpp"
+#line 4495 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 237: /* thedatetime: tTHE tABBREV tTIME  */
-#line 747 "engines/director/lingo/lingo-gr.y"
+  case 269: /* thedatetime: tTHE tABBREV tTIME  */
+#line 806 "engines/director/lingo/lingo-gr.y"
                                                         { (yyval.node) = new TheDateTimeNode(kTheAbbr, kTheTime); }
-#line 4227 "engines/director/lingo/lingo-gr.cpp"
+#line 4501 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 238: /* thedatetime: tTHE tABBR tDATE  */
-#line 748 "engines/director/lingo/lingo-gr.y"
+  case 270: /* thedatetime: tTHE tABBR tDATE  */
+#line 807 "engines/director/lingo/lingo-gr.y"
                                                                 { (yyval.node) = new TheDateTimeNode(kTheAbbr, kTheDate); }
-#line 4233 "engines/director/lingo/lingo-gr.cpp"
+#line 4507 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 239: /* thedatetime: tTHE tABBR tTIME  */
-#line 749 "engines/director/lingo/lingo-gr.y"
+  case 271: /* thedatetime: tTHE tABBR tTIME  */
+#line 808 "engines/director/lingo/lingo-gr.y"
                                                                 { (yyval.node) = new TheDateTimeNode(kTheAbbr, kTheTime); }
-#line 4239 "engines/director/lingo/lingo-gr.cpp"
+#line 4513 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 240: /* thedatetime: tTHE tLONG tDATE  */
-#line 750 "engines/director/lingo/lingo-gr.y"
+  case 272: /* thedatetime: tTHE tLONG tDATE  */
+#line 809 "engines/director/lingo/lingo-gr.y"
                                                                 { (yyval.node) = new TheDateTimeNode(kTheLong, kTheDate); }
-#line 4245 "engines/director/lingo/lingo-gr.cpp"
+#line 4519 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 241: /* thedatetime: tTHE tLONG tTIME  */
-#line 751 "engines/director/lingo/lingo-gr.y"
+  case 273: /* thedatetime: tTHE tLONG tTIME  */
+#line 810 "engines/director/lingo/lingo-gr.y"
                                                                 { (yyval.node) = new TheDateTimeNode(kTheLong, kTheTime); }
-#line 4251 "engines/director/lingo/lingo-gr.cpp"
+#line 4525 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 242: /* thedatetime: tTHE tSHORT tDATE  */
-#line 752 "engines/director/lingo/lingo-gr.y"
+  case 274: /* thedatetime: tTHE tSHORT tDATE  */
+#line 811 "engines/director/lingo/lingo-gr.y"
                                                                 { (yyval.node) = new TheDateTimeNode(kTheShort, kTheDate); }
-#line 4257 "engines/director/lingo/lingo-gr.cpp"
+#line 4531 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 243: /* thedatetime: tTHE tSHORT tTIME  */
-#line 753 "engines/director/lingo/lingo-gr.y"
+  case 275: /* thedatetime: tTHE tSHORT tTIME  */
+#line 812 "engines/director/lingo/lingo-gr.y"
                                                                 { (yyval.node) = new TheDateTimeNode(kTheShort, kTheTime); }
-#line 4263 "engines/director/lingo/lingo-gr.cpp"
+#line 4537 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 244: /* thenumberof: tTHE tNUMBER tOF tCHARS inof simpleexpr  */
-#line 757 "engines/director/lingo/lingo-gr.y"
+  case 276: /* thenumberof: tTHE tNUMBER tOF tCHARS inof simpleexpr  */
+#line 816 "engines/director/lingo/lingo-gr.y"
                                                         { (yyval.node) = new TheNumberOfNode(kNumberOfChars, (yyvsp[0].node)); }
-#line 4269 "engines/director/lingo/lingo-gr.cpp"
+#line 4543 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 245: /* thenumberof: tTHE tNUMBER tOF tWORDS inof simpleexpr  */
-#line 758 "engines/director/lingo/lingo-gr.y"
+  case 277: /* thenumberof: tTHE tNUMBER tOF tWORDS inof simpleexpr  */
+#line 817 "engines/director/lingo/lingo-gr.y"
                                                         { (yyval.node) = new TheNumberOfNode(kNumberOfWords, (yyvsp[0].node)); }
-#line 4275 "engines/director/lingo/lingo-gr.cpp"
+#line 4549 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 246: /* thenumberof: tTHE tNUMBER tOF tITEMS inof simpleexpr  */
-#line 759 "engines/director/lingo/lingo-gr.y"
+  case 278: /* thenumberof: tTHE tNUMBER tOF tITEMS inof simpleexpr  */
+#line 818 "engines/director/lingo/lingo-gr.y"
                                                         { (yyval.node) = new TheNumberOfNode(kNumberOfItems, (yyvsp[0].node)); }
-#line 4281 "engines/director/lingo/lingo-gr.cpp"
+#line 4555 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 247: /* thenumberof: tTHE tNUMBER tOF tLINES inof simpleexpr  */
-#line 760 "engines/director/lingo/lingo-gr.y"
+  case 279: /* thenumberof: tTHE tNUMBER tOF tLINES inof simpleexpr  */
+#line 819 "engines/director/lingo/lingo-gr.y"
                                                         { (yyval.node) = new TheNumberOfNode(kNumberOfLines, (yyvsp[0].node)); }
-#line 4287 "engines/director/lingo/lingo-gr.cpp"
+#line 4561 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 248: /* thenumberof: tTHE tNUMBER tOF tMENUITEMS inof menu  */
-#line 761 "engines/director/lingo/lingo-gr.y"
+  case 280: /* thenumberof: tTHE tNUMBER tOF tMENUITEMS inof menu  */
+#line 820 "engines/director/lingo/lingo-gr.y"
                                                         { (yyval.node) = new TheNumberOfNode(kNumberOfMenuItems, (yyvsp[0].node)); }
-#line 4293 "engines/director/lingo/lingo-gr.cpp"
+#line 4567 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 249: /* thenumberof: tTHE tNUMBER tOF tMENUS  */
-#line 762 "engines/director/lingo/lingo-gr.y"
+  case 281: /* thenumberof: tTHE tNUMBER tOF tMENUS  */
+#line 821 "engines/director/lingo/lingo-gr.y"
                                                                         { (yyval.node) = new TheNumberOfNode(kNumberOfMenus, nullptr); }
-#line 4299 "engines/director/lingo/lingo-gr.cpp"
+#line 4573 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 250: /* thenumberof: tTHE tNUMBER tOF tXTRAS  */
-#line 763 "engines/director/lingo/lingo-gr.y"
+  case 282: /* thenumberof: tTHE tNUMBER tOF tXTRAS  */
+#line 822 "engines/director/lingo/lingo-gr.y"
                                                                         { (yyval.node) = new TheNumberOfNode(kNumberOfXtras, nullptr); }
-#line 4305 "engines/director/lingo/lingo-gr.cpp"
+#line 4579 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 251: /* thenumberof: tTHE tNUMBER tOF tCASTLIBS  */
-#line 764 "engines/director/lingo/lingo-gr.y"
+  case 283: /* thenumberof: tTHE tNUMBER tOF tCASTLIBS  */
+#line 823 "engines/director/lingo/lingo-gr.y"
                                                                 { (yyval.node) = new TheNumberOfNode(kNumberOfCastlibs, nullptr); }
-#line 4311 "engines/director/lingo/lingo-gr.cpp"
+#line 4585 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 254: /* writablethe: tTHE ID  */
-#line 769 "engines/director/lingo/lingo-gr.y"
+  case 286: /* writablethe: tTHE ID  */
+#line 828 "engines/director/lingo/lingo-gr.y"
                                                         { (yyval.node) = new TheNode((yyvsp[0].s)); }
-#line 4317 "engines/director/lingo/lingo-gr.cpp"
+#line 4591 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 255: /* writablethe: tTHE ID tOF writabletheobj  */
-#line 770 "engines/director/lingo/lingo-gr.y"
+  case 287: /* writablethe: tTHE ID tOF writabletheobj  */
+#line 829 "engines/director/lingo/lingo-gr.y"
                                                 { (yyval.node) = new TheOfNode((yyvsp[-2].s), (yyvsp[0].node)); }
-#line 4323 "engines/director/lingo/lingo-gr.cpp"
+#line 4597 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 257: /* writabletheobj: tMENU expr_noeq  */
-#line 774 "engines/director/lingo/lingo-gr.y"
+  case 289: /* writabletheobj: tMENU expr_noeq  */
+#line 833 "engines/director/lingo/lingo-gr.y"
                                                         { (yyval.node) = new MenuNode((yyvsp[0].node)); }
-#line 4329 "engines/director/lingo/lingo-gr.cpp"
+#line 4603 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 258: /* writabletheobj: tMENUITEM expr_noeq tOF tMENU expr_noeq  */
-#line 775 "engines/director/lingo/lingo-gr.y"
+  case 290: /* writabletheobj: tMENUITEM expr_noeq tOF tMENU expr_noeq  */
+#line 834 "engines/director/lingo/lingo-gr.y"
                                                                 { (yyval.node) = new MenuItemNode((yyvsp[-3].node), (yyvsp[0].node)); }
-#line 4335 "engines/director/lingo/lingo-gr.cpp"
+#line 4609 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 259: /* writabletheobj: tSOUND expr_noeq  */
-#line 776 "engines/director/lingo/lingo-gr.y"
+  case 291: /* writabletheobj: tSOUND expr_noeq  */
+#line 835 "engines/director/lingo/lingo-gr.y"
                                                         { (yyval.node) = new SoundNode((yyvsp[0].node)); }
-#line 4341 "engines/director/lingo/lingo-gr.cpp"
+#line 4615 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 260: /* writabletheobj: tSPRITE expr_noeq  */
-#line 777 "engines/director/lingo/lingo-gr.y"
+  case 292: /* writabletheobj: tSPRITE expr_noeq  */
+#line 836 "engines/director/lingo/lingo-gr.y"
                                                         { (yyval.node) = new SpriteNode((yyvsp[0].node)); }
-#line 4347 "engines/director/lingo/lingo-gr.cpp"
+#line 4621 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 261: /* list: '[' exprlist ']'  */
-#line 780 "engines/director/lingo/lingo-gr.y"
+  case 293: /* list: '[' exprlist ']'  */
+#line 839 "engines/director/lingo/lingo-gr.y"
                                         { (yyval.node) = new ListNode((yyvsp[-1].nodelist)); }
-#line 4353 "engines/director/lingo/lingo-gr.cpp"
+#line 4627 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 262: /* list: '[' ':' ']'  */
-#line 781 "engines/director/lingo/lingo-gr.y"
+  case 294: /* list: '[' ':' ']'  */
+#line 840 "engines/director/lingo/lingo-gr.y"
                                                 { (yyval.node) = new PropListNode(new NodeList); }
-#line 4359 "engines/director/lingo/lingo-gr.cpp"
+#line 4633 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 263: /* list: '[' proplist ']'  */
-#line 782 "engines/director/lingo/lingo-gr.y"
+  case 295: /* list: '[' proplist ']'  */
+#line 841 "engines/director/lingo/lingo-gr.y"
                                                 { (yyval.node) = new PropListNode((yyvsp[-1].nodelist)); }
-#line 4365 "engines/director/lingo/lingo-gr.cpp"
+#line 4639 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 264: /* proplist: proppair  */
-#line 788 "engines/director/lingo/lingo-gr.y"
+  case 296: /* proplist: proppair  */
+#line 847 "engines/director/lingo/lingo-gr.y"
                                                         {
 		NodeList *list = new NodeList;
 		list->push_back((yyvsp[0].node));
 		(yyval.nodelist) = list; }
-#line 4374 "engines/director/lingo/lingo-gr.cpp"
+#line 4648 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 265: /* proplist: proplist ',' proppair  */
-#line 792 "engines/director/lingo/lingo-gr.y"
+  case 297: /* proplist: proplist ',' proppair  */
+#line 851 "engines/director/lingo/lingo-gr.y"
                                                 {
 		(yyvsp[-2].nodelist)->push_back((yyvsp[0].node));
 		(yyval.nodelist) = (yyvsp[-2].nodelist); }
-#line 4382 "engines/director/lingo/lingo-gr.cpp"
+#line 4656 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 266: /* proplist: proplist ',' expr  */
-#line 795 "engines/director/lingo/lingo-gr.y"
+  case 298: /* proplist: proplist ',' expr  */
+#line 854 "engines/director/lingo/lingo-gr.y"
                                         {
 		(yyvsp[-2].nodelist)->push_back((yyvsp[0].node));
 		(yyval.nodelist) = (yyvsp[-2].nodelist); }
-#line 4390 "engines/director/lingo/lingo-gr.cpp"
+#line 4664 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 267: /* proppair: tSYMBOL ':' expr  */
-#line 800 "engines/director/lingo/lingo-gr.y"
+  case 299: /* proppair: tSYMBOL ':' expr  */
+#line 859 "engines/director/lingo/lingo-gr.y"
                                         { (yyval.node) = new PropPairNode(new SymbolNode((yyvsp[-2].s)), (yyvsp[0].node)); }
-#line 4396 "engines/director/lingo/lingo-gr.cpp"
+#line 4670 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 268: /* proppair: ID ':' expr  */
-#line 801 "engines/director/lingo/lingo-gr.y"
+  case 300: /* proppair: ID ':' expr  */
+#line 860 "engines/director/lingo/lingo-gr.y"
                                                 { (yyval.node) = new PropPairNode(new SymbolNode((yyvsp[-2].s)), (yyvsp[0].node)); }
-#line 4402 "engines/director/lingo/lingo-gr.cpp"
+#line 4676 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 269: /* proppair: tSTRING ':' expr  */
-#line 802 "engines/director/lingo/lingo-gr.y"
+  case 301: /* proppair: tSTRING ':' expr  */
+#line 861 "engines/director/lingo/lingo-gr.y"
                                                 { (yyval.node) = new PropPairNode(new StringNode((yyvsp[-2].s)), (yyvsp[0].node)); }
-#line 4408 "engines/director/lingo/lingo-gr.cpp"
+#line 4682 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 270: /* proppair: tINT ':' expr  */
-#line 803 "engines/director/lingo/lingo-gr.y"
+  case 302: /* proppair: tINT ':' expr  */
+#line 862 "engines/director/lingo/lingo-gr.y"
                                     { (yyval.node) = new PropPairNode(new IntNode((yyvsp[-2].i)), (yyvsp[0].node)); }
-#line 4414 "engines/director/lingo/lingo-gr.cpp"
+#line 4688 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 271: /* proppair: tFLOAT ':' expr  */
-#line 804 "engines/director/lingo/lingo-gr.y"
+  case 303: /* proppair: tFLOAT ':' expr  */
+#line 863 "engines/director/lingo/lingo-gr.y"
                                     { (yyval.node) = new PropPairNode(new FloatNode((yyvsp[-2].f)), (yyvsp[0].node)); }
-#line 4420 "engines/director/lingo/lingo-gr.cpp"
+#line 4694 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 272: /* unarymath: '+' simpleexpr  */
-#line 807 "engines/director/lingo/lingo-gr.y"
+  case 304: /* unarymath: '+' simpleexpr  */
+#line 866 "engines/director/lingo/lingo-gr.y"
                                                 { (yyval.node) = (yyvsp[0].node); }
-#line 4426 "engines/director/lingo/lingo-gr.cpp"
+#line 4700 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 273: /* unarymath: '-' simpleexpr  */
-#line 808 "engines/director/lingo/lingo-gr.y"
+  case 305: /* unarymath: '-' simpleexpr  */
+#line 867 "engines/director/lingo/lingo-gr.y"
                                                                 { (yyval.node) = new UnaryOpNode(LC::c_negate, (yyvsp[0].node)); }
-#line 4432 "engines/director/lingo/lingo-gr.cpp"
+#line 4706 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 278: /* expr: expr '+' expr  */
-#line 818 "engines/director/lingo/lingo-gr.y"
+  case 310: /* expr: expr '+' expr  */
+#line 877 "engines/director/lingo/lingo-gr.y"
                                         { (yyval.node) = new BinaryOpNode(LC::c_add, (yyvsp[-2].node), (yyvsp[0].node)); }
-#line 4438 "engines/director/lingo/lingo-gr.cpp"
+#line 4712 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 279: /* expr: expr '-' expr  */
-#line 819 "engines/director/lingo/lingo-gr.y"
+  case 311: /* expr: expr '-' expr  */
+#line 878 "engines/director/lingo/lingo-gr.y"
                                         { (yyval.node) = new BinaryOpNode(LC::c_sub, (yyvsp[-2].node), (yyvsp[0].node)); }
-#line 4444 "engines/director/lingo/lingo-gr.cpp"
+#line 4718 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 280: /* expr: expr '*' expr  */
-#line 820 "engines/director/lingo/lingo-gr.y"
+  case 312: /* expr: expr '*' expr  */
+#line 879 "engines/director/lingo/lingo-gr.y"
                                         { (yyval.node) = new BinaryOpNode(LC::c_mul, (yyvsp[-2].node), (yyvsp[0].node)); }
-#line 4450 "engines/director/lingo/lingo-gr.cpp"
+#line 4724 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 281: /* expr: expr '/' expr  */
-#line 821 "engines/director/lingo/lingo-gr.y"
+  case 313: /* expr: expr '/' expr  */
+#line 880 "engines/director/lingo/lingo-gr.y"
                                         { (yyval.node) = new BinaryOpNode(LC::c_div, (yyvsp[-2].node), (yyvsp[0].node)); }
-#line 4456 "engines/director/lingo/lingo-gr.cpp"
+#line 4730 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 282: /* expr: expr tMOD expr  */
-#line 822 "engines/director/lingo/lingo-gr.y"
+  case 314: /* expr: expr tMOD expr  */
+#line 881 "engines/director/lingo/lingo-gr.y"
                                         { (yyval.node) = new BinaryOpNode(LC::c_mod, (yyvsp[-2].node), (yyvsp[0].node)); }
-#line 4462 "engines/director/lingo/lingo-gr.cpp"
+#line 4736 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 283: /* expr: expr '>' expr  */
-#line 823 "engines/director/lingo/lingo-gr.y"
+  case 315: /* expr: expr '>' expr  */
+#line 882 "engines/director/lingo/lingo-gr.y"
                                         { (yyval.node) = new BinaryOpNode(LC::c_gt, (yyvsp[-2].node), (yyvsp[0].node)); }
-#line 4468 "engines/director/lingo/lingo-gr.cpp"
+#line 4742 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 284: /* expr: expr '<' expr  */
-#line 824 "engines/director/lingo/lingo-gr.y"
+  case 316: /* expr: expr '<' expr  */
+#line 883 "engines/director/lingo/lingo-gr.y"
                                         { (yyval.node) = new BinaryOpNode(LC::c_lt, (yyvsp[-2].node), (yyvsp[0].node)); }
-#line 4474 "engines/director/lingo/lingo-gr.cpp"
+#line 4748 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 285: /* expr: expr tEQ expr  */
-#line 825 "engines/director/lingo/lingo-gr.y"
+  case 317: /* expr: expr tEQ expr  */
+#line 884 "engines/director/lingo/lingo-gr.y"
                                         { (yyval.node) = new BinaryOpNode(LC::c_eq, (yyvsp[-2].node), (yyvsp[0].node)); }
-#line 4480 "engines/director/lingo/lingo-gr.cpp"
+#line 4754 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 286: /* expr: expr tNEQ expr  */
-#line 826 "engines/director/lingo/lingo-gr.y"
+  case 318: /* expr: expr tNEQ expr  */
+#line 885 "engines/director/lingo/lingo-gr.y"
                                         { (yyval.node) = new BinaryOpNode(LC::c_neq, (yyvsp[-2].node), (yyvsp[0].node)); }
-#line 4486 "engines/director/lingo/lingo-gr.cpp"
+#line 4760 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 287: /* expr: expr tGE expr  */
-#line 827 "engines/director/lingo/lingo-gr.y"
+  case 319: /* expr: expr tGE expr  */
+#line 886 "engines/director/lingo/lingo-gr.y"
                                         { (yyval.node) = new BinaryOpNode(LC::c_ge, (yyvsp[-2].node), (yyvsp[0].node)); }
-#line 4492 "engines/director/lingo/lingo-gr.cpp"
+#line 4766 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 288: /* expr: expr tLE expr  */
-#line 828 "engines/director/lingo/lingo-gr.y"
+  case 320: /* expr: expr tLE expr  */
+#line 887 "engines/director/lingo/lingo-gr.y"
                                         { (yyval.node) = new BinaryOpNode(LC::c_le, (yyvsp[-2].node), (yyvsp[0].node)); }
-#line 4498 "engines/director/lingo/lingo-gr.cpp"
+#line 4772 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 289: /* expr: expr tAND expr  */
-#line 829 "engines/director/lingo/lingo-gr.y"
+  case 321: /* expr: expr tAND expr  */
+#line 888 "engines/director/lingo/lingo-gr.y"
                                         { (yyval.node) = new BinaryOpNode(LC::c_and, (yyvsp[-2].node), (yyvsp[0].node)); }
-#line 4504 "engines/director/lingo/lingo-gr.cpp"
+#line 4778 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 290: /* expr: expr tOR expr  */
-#line 830 "engines/director/lingo/lingo-gr.y"
+  case 322: /* expr: expr tOR expr  */
+#line 889 "engines/director/lingo/lingo-gr.y"
                                         { (yyval.node) = new BinaryOpNode(LC::c_or, (yyvsp[-2].node), (yyvsp[0].node)); }
-#line 4510 "engines/director/lingo/lingo-gr.cpp"
+#line 4784 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 291: /* expr: expr '&' expr  */
-#line 831 "engines/director/lingo/lingo-gr.y"
+  case 323: /* expr: expr '&' expr  */
+#line 890 "engines/director/lingo/lingo-gr.y"
                                         { (yyval.node) = new BinaryOpNode(LC::c_ampersand, (yyvsp[-2].node), (yyvsp[0].node)); }
-#line 4516 "engines/director/lingo/lingo-gr.cpp"
+#line 4790 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 292: /* expr: expr tCONCAT expr  */
-#line 832 "engines/director/lingo/lingo-gr.y"
+  case 324: /* expr: expr tCONCAT expr  */
+#line 891 "engines/director/lingo/lingo-gr.y"
                                         { (yyval.node) = new BinaryOpNode(LC::c_concat, (yyvsp[-2].node), (yyvsp[0].node)); }
-#line 4522 "engines/director/lingo/lingo-gr.cpp"
+#line 4796 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 293: /* expr: expr tCONTAINS expr  */
-#line 833 "engines/director/lingo/lingo-gr.y"
+  case 325: /* expr: expr tCONTAINS expr  */
+#line 892 "engines/director/lingo/lingo-gr.y"
                                         { (yyval.node) = new BinaryOpNode(LC::c_contains, (yyvsp[-2].node), (yyvsp[0].node)); }
-#line 4528 "engines/director/lingo/lingo-gr.cpp"
+#line 4802 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 294: /* expr: expr tSTARTS expr  */
-#line 834 "engines/director/lingo/lingo-gr.y"
+  case 326: /* expr: expr tSTARTS expr  */
+#line 893 "engines/director/lingo/lingo-gr.y"
                                         { (yyval.node) = new BinaryOpNode(LC::c_starts, (yyvsp[-2].node), (yyvsp[0].node)); }
-#line 4534 "engines/director/lingo/lingo-gr.cpp"
+#line 4808 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 297: /* expr_nounarymath: expr_nounarymath '+' expr  */
-#line 843 "engines/director/lingo/lingo-gr.y"
+  case 329: /* expr_nounarymath: expr_nounarymath '+' expr  */
+#line 902 "engines/director/lingo/lingo-gr.y"
                                                         { (yyval.node) = new BinaryOpNode(LC::c_add, (yyvsp[-2].node), (yyvsp[0].node)); }
-#line 4540 "engines/director/lingo/lingo-gr.cpp"
+#line 4814 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 298: /* expr_nounarymath: expr_nounarymath '-' expr  */
-#line 844 "engines/director/lingo/lingo-gr.y"
+  case 330: /* expr_nounarymath: expr_nounarymath '-' expr  */
+#line 903 "engines/director/lingo/lingo-gr.y"
                                                         { (yyval.node) = new BinaryOpNode(LC::c_sub, (yyvsp[-2].node), (yyvsp[0].node)); }
-#line 4546 "engines/director/lingo/lingo-gr.cpp"
+#line 4820 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 299: /* expr_nounarymath: expr_nounarymath '*' expr  */
-#line 845 "engines/director/lingo/lingo-gr.y"
+  case 331: /* expr_nounarymath: expr_nounarymath '*' expr  */
+#line 904 "engines/director/lingo/lingo-gr.y"
                                                         { (yyval.node) = new BinaryOpNode(LC::c_mul, (yyvsp[-2].node), (yyvsp[0].node)); }
-#line 4552 "engines/director/lingo/lingo-gr.cpp"
+#line 4826 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 300: /* expr_nounarymath: expr_nounarymath '/' expr  */
-#line 846 "engines/director/lingo/lingo-gr.y"
+  case 332: /* expr_nounarymath: expr_nounarymath '/' expr  */
+#line 905 "engines/director/lingo/lingo-gr.y"
                                                         { (yyval.node) = new BinaryOpNode(LC::c_div, (yyvsp[-2].node), (yyvsp[0].node)); }
-#line 4558 "engines/director/lingo/lingo-gr.cpp"
+#line 4832 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 301: /* expr_nounarymath: expr_nounarymath tMOD expr  */
-#line 847 "engines/director/lingo/lingo-gr.y"
+  case 333: /* expr_nounarymath: expr_nounarymath tMOD expr  */
+#line 906 "engines/director/lingo/lingo-gr.y"
                                                         { (yyval.node) = new BinaryOpNode(LC::c_mod, (yyvsp[-2].node), (yyvsp[0].node)); }
-#line 4564 "engines/director/lingo/lingo-gr.cpp"
+#line 4838 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 302: /* expr_nounarymath: expr_nounarymath '>' expr  */
-#line 848 "engines/director/lingo/lingo-gr.y"
+  case 334: /* expr_nounarymath: expr_nounarymath '>' expr  */
+#line 907 "engines/director/lingo/lingo-gr.y"
                                                         { (yyval.node) = new BinaryOpNode(LC::c_gt, (yyvsp[-2].node), (yyvsp[0].node)); }
-#line 4570 "engines/director/lingo/lingo-gr.cpp"
+#line 4844 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 303: /* expr_nounarymath: expr_nounarymath '<' expr  */
-#line 849 "engines/director/lingo/lingo-gr.y"
+  case 335: /* expr_nounarymath: expr_nounarymath '<' expr  */
+#line 908 "engines/director/lingo/lingo-gr.y"
                                                         { (yyval.node) = new BinaryOpNode(LC::c_lt, (yyvsp[-2].node), (yyvsp[0].node)); }
-#line 4576 "engines/director/lingo/lingo-gr.cpp"
+#line 4850 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 304: /* expr_nounarymath: expr_nounarymath tEQ expr  */
-#line 850 "engines/director/lingo/lingo-gr.y"
+  case 336: /* expr_nounarymath: expr_nounarymath tEQ expr  */
+#line 909 "engines/director/lingo/lingo-gr.y"
                                                         { (yyval.node) = new BinaryOpNode(LC::c_eq, (yyvsp[-2].node), (yyvsp[0].node)); }
-#line 4582 "engines/director/lingo/lingo-gr.cpp"
+#line 4856 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 305: /* expr_nounarymath: expr_nounarymath tNEQ expr  */
-#line 851 "engines/director/lingo/lingo-gr.y"
+  case 337: /* expr_nounarymath: expr_nounarymath tNEQ expr  */
+#line 910 "engines/director/lingo/lingo-gr.y"
                                                         { (yyval.node) = new BinaryOpNode(LC::c_neq, (yyvsp[-2].node), (yyvsp[0].node)); }
-#line 4588 "engines/director/lingo/lingo-gr.cpp"
+#line 4862 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 306: /* expr_nounarymath: expr_nounarymath tGE expr  */
-#line 852 "engines/director/lingo/lingo-gr.y"
+  case 338: /* expr_nounarymath: expr_nounarymath tGE expr  */
+#line 911 "engines/director/lingo/lingo-gr.y"
                                                         { (yyval.node) = new BinaryOpNode(LC::c_ge, (yyvsp[-2].node), (yyvsp[0].node)); }
-#line 4594 "engines/director/lingo/lingo-gr.cpp"
+#line 4868 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 307: /* expr_nounarymath: expr_nounarymath tLE expr  */
-#line 853 "engines/director/lingo/lingo-gr.y"
+  case 339: /* expr_nounarymath: expr_nounarymath tLE expr  */
+#line 912 "engines/director/lingo/lingo-gr.y"
                                                         { (yyval.node) = new BinaryOpNode(LC::c_le, (yyvsp[-2].node), (yyvsp[0].node)); }
-#line 4600 "engines/director/lingo/lingo-gr.cpp"
+#line 4874 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 308: /* expr_nounarymath: expr_nounarymath tAND expr  */
-#line 854 "engines/director/lingo/lingo-gr.y"
+  case 340: /* expr_nounarymath: expr_nounarymath tAND expr  */
+#line 913 "engines/director/lingo/lingo-gr.y"
                                                         { (yyval.node) = new BinaryOpNode(LC::c_and, (yyvsp[-2].node), (yyvsp[0].node)); }
-#line 4606 "engines/director/lingo/lingo-gr.cpp"
+#line 4880 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 309: /* expr_nounarymath: expr_nounarymath tOR expr  */
-#line 855 "engines/director/lingo/lingo-gr.y"
+  case 341: /* expr_nounarymath: expr_nounarymath tOR expr  */
+#line 914 "engines/director/lingo/lingo-gr.y"
                                                         { (yyval.node) = new BinaryOpNode(LC::c_or, (yyvsp[-2].node), (yyvsp[0].node)); }
-#line 4612 "engines/director/lingo/lingo-gr.cpp"
+#line 4886 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 310: /* expr_nounarymath: expr_nounarymath '&' expr  */
-#line 856 "engines/director/lingo/lingo-gr.y"
+  case 342: /* expr_nounarymath: expr_nounarymath '&' expr  */
+#line 915 "engines/director/lingo/lingo-gr.y"
                                                         { (yyval.node) = new BinaryOpNode(LC::c_ampersand, (yyvsp[-2].node), (yyvsp[0].node)); }
-#line 4618 "engines/director/lingo/lingo-gr.cpp"
+#line 4892 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 311: /* expr_nounarymath: expr_nounarymath tCONCAT expr  */
-#line 857 "engines/director/lingo/lingo-gr.y"
+  case 343: /* expr_nounarymath: expr_nounarymath tCONCAT expr  */
+#line 916 "engines/director/lingo/lingo-gr.y"
                                                 { (yyval.node) = new BinaryOpNode(LC::c_concat, (yyvsp[-2].node), (yyvsp[0].node)); }
-#line 4624 "engines/director/lingo/lingo-gr.cpp"
+#line 4898 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 312: /* expr_nounarymath: expr_nounarymath tCONTAINS expr  */
-#line 858 "engines/director/lingo/lingo-gr.y"
+  case 344: /* expr_nounarymath: expr_nounarymath tCONTAINS expr  */
+#line 917 "engines/director/lingo/lingo-gr.y"
                                                 { (yyval.node) = new BinaryOpNode(LC::c_contains, (yyvsp[-2].node), (yyvsp[0].node)); }
-#line 4630 "engines/director/lingo/lingo-gr.cpp"
+#line 4904 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 313: /* expr_nounarymath: expr_nounarymath tSTARTS expr  */
-#line 859 "engines/director/lingo/lingo-gr.y"
+  case 345: /* expr_nounarymath: expr_nounarymath tSTARTS expr  */
+#line 918 "engines/director/lingo/lingo-gr.y"
                                                 { (yyval.node) = new BinaryOpNode(LC::c_starts, (yyvsp[-2].node), (yyvsp[0].node)); }
-#line 4636 "engines/director/lingo/lingo-gr.cpp"
+#line 4910 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 316: /* expr_noeq: expr_noeq '+' expr_noeq  */
-#line 864 "engines/director/lingo/lingo-gr.y"
+  case 348: /* expr_noeq: expr_noeq '+' expr_noeq  */
+#line 923 "engines/director/lingo/lingo-gr.y"
                                                         { (yyval.node) = new BinaryOpNode(LC::c_add, (yyvsp[-2].node), (yyvsp[0].node)); }
-#line 4642 "engines/director/lingo/lingo-gr.cpp"
+#line 4916 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 317: /* expr_noeq: expr_noeq '-' expr_noeq  */
-#line 865 "engines/director/lingo/lingo-gr.y"
+  case 349: /* expr_noeq: expr_noeq '-' expr_noeq  */
+#line 924 "engines/director/lingo/lingo-gr.y"
                                                         { (yyval.node) = new BinaryOpNode(LC::c_sub, (yyvsp[-2].node), (yyvsp[0].node)); }
-#line 4648 "engines/director/lingo/lingo-gr.cpp"
+#line 4922 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 318: /* expr_noeq: expr_noeq '*' expr_noeq  */
-#line 866 "engines/director/lingo/lingo-gr.y"
+  case 350: /* expr_noeq: expr_noeq '*' expr_noeq  */
+#line 925 "engines/director/lingo/lingo-gr.y"
                                                         { (yyval.node) = new BinaryOpNode(LC::c_mul, (yyvsp[-2].node), (yyvsp[0].node)); }
-#line 4654 "engines/director/lingo/lingo-gr.cpp"
+#line 4928 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 319: /* expr_noeq: expr_noeq '/' expr_noeq  */
-#line 867 "engines/director/lingo/lingo-gr.y"
+  case 351: /* expr_noeq: expr_noeq '/' expr_noeq  */
+#line 926 "engines/director/lingo/lingo-gr.y"
                                                         { (yyval.node) = new BinaryOpNode(LC::c_div, (yyvsp[-2].node), (yyvsp[0].node)); }
-#line 4660 "engines/director/lingo/lingo-gr.cpp"
+#line 4934 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 320: /* expr_noeq: expr_noeq tMOD expr_noeq  */
-#line 868 "engines/director/lingo/lingo-gr.y"
+  case 352: /* expr_noeq: expr_noeq tMOD expr_noeq  */
+#line 927 "engines/director/lingo/lingo-gr.y"
                                                         { (yyval.node) = new BinaryOpNode(LC::c_mod, (yyvsp[-2].node), (yyvsp[0].node)); }
-#line 4666 "engines/director/lingo/lingo-gr.cpp"
+#line 4940 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 321: /* expr_noeq: expr_noeq '>' expr_noeq  */
-#line 869 "engines/director/lingo/lingo-gr.y"
+  case 353: /* expr_noeq: expr_noeq '>' expr_noeq  */
+#line 928 "engines/director/lingo/lingo-gr.y"
                                                         { (yyval.node) = new BinaryOpNode(LC::c_gt, (yyvsp[-2].node), (yyvsp[0].node)); }
-#line 4672 "engines/director/lingo/lingo-gr.cpp"
+#line 4946 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 322: /* expr_noeq: expr_noeq '<' expr_noeq  */
-#line 870 "engines/director/lingo/lingo-gr.y"
+  case 354: /* expr_noeq: expr_noeq '<' expr_noeq  */
+#line 929 "engines/director/lingo/lingo-gr.y"
                                                         { (yyval.node) = new BinaryOpNode(LC::c_lt, (yyvsp[-2].node), (yyvsp[0].node)); }
-#line 4678 "engines/director/lingo/lingo-gr.cpp"
+#line 4952 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 323: /* expr_noeq: expr_noeq tNEQ expr_noeq  */
-#line 871 "engines/director/lingo/lingo-gr.y"
+  case 355: /* expr_noeq: expr_noeq tNEQ expr_noeq  */
+#line 930 "engines/director/lingo/lingo-gr.y"
                                                         { (yyval.node) = new BinaryOpNode(LC::c_neq, (yyvsp[-2].node), (yyvsp[0].node)); }
-#line 4684 "engines/director/lingo/lingo-gr.cpp"
+#line 4958 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 324: /* expr_noeq: expr_noeq tGE expr_noeq  */
-#line 872 "engines/director/lingo/lingo-gr.y"
+  case 356: /* expr_noeq: expr_noeq tGE expr_noeq  */
+#line 931 "engines/director/lingo/lingo-gr.y"
                                                         { (yyval.node) = new BinaryOpNode(LC::c_ge, (yyvsp[-2].node), (yyvsp[0].node)); }
-#line 4690 "engines/director/lingo/lingo-gr.cpp"
+#line 4964 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 325: /* expr_noeq: expr_noeq tLE expr_noeq  */
-#line 873 "engines/director/lingo/lingo-gr.y"
+  case 357: /* expr_noeq: expr_noeq tLE expr_noeq  */
+#line 932 "engines/director/lingo/lingo-gr.y"
                                                         { (yyval.node) = new BinaryOpNode(LC::c_le, (yyvsp[-2].node), (yyvsp[0].node)); }
-#line 4696 "engines/director/lingo/lingo-gr.cpp"
+#line 4970 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 326: /* expr_noeq: expr_noeq tAND expr_noeq  */
-#line 874 "engines/director/lingo/lingo-gr.y"
+  case 358: /* expr_noeq: expr_noeq tAND expr_noeq  */
+#line 933 "engines/director/lingo/lingo-gr.y"
                                                         { (yyval.node) = new BinaryOpNode(LC::c_and, (yyvsp[-2].node), (yyvsp[0].node)); }
-#line 4702 "engines/director/lingo/lingo-gr.cpp"
+#line 4976 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 327: /* expr_noeq: expr_noeq tOR expr_noeq  */
-#line 875 "engines/director/lingo/lingo-gr.y"
+  case 359: /* expr_noeq: expr_noeq tOR expr_noeq  */
+#line 934 "engines/director/lingo/lingo-gr.y"
                                                         { (yyval.node) = new BinaryOpNode(LC::c_or, (yyvsp[-2].node), (yyvsp[0].node)); }
-#line 4708 "engines/director/lingo/lingo-gr.cpp"
+#line 4982 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 328: /* expr_noeq: expr_noeq '&' expr_noeq  */
-#line 876 "engines/director/lingo/lingo-gr.y"
+  case 360: /* expr_noeq: expr_noeq '&' expr_noeq  */
+#line 935 "engines/director/lingo/lingo-gr.y"
                                                         { (yyval.node) = new BinaryOpNode(LC::c_ampersand, (yyvsp[-2].node), (yyvsp[0].node)); }
-#line 4714 "engines/director/lingo/lingo-gr.cpp"
+#line 4988 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 329: /* expr_noeq: expr_noeq tCONCAT expr_noeq  */
-#line 877 "engines/director/lingo/lingo-gr.y"
+  case 361: /* expr_noeq: expr_noeq tCONCAT expr_noeq  */
+#line 936 "engines/director/lingo/lingo-gr.y"
                                                         { (yyval.node) = new BinaryOpNode(LC::c_concat, (yyvsp[-2].node), (yyvsp[0].node)); }
-#line 4720 "engines/director/lingo/lingo-gr.cpp"
+#line 4994 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 330: /* expr_noeq: expr_noeq tCONTAINS expr_noeq  */
-#line 878 "engines/director/lingo/lingo-gr.y"
+  case 362: /* expr_noeq: expr_noeq tCONTAINS expr_noeq  */
+#line 937 "engines/director/lingo/lingo-gr.y"
                                                 { (yyval.node) = new BinaryOpNode(LC::c_contains, (yyvsp[-2].node), (yyvsp[0].node)); }
-#line 4726 "engines/director/lingo/lingo-gr.cpp"
+#line 5000 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 331: /* expr_noeq: expr_noeq tSTARTS expr_noeq  */
-#line 879 "engines/director/lingo/lingo-gr.y"
+  case 363: /* expr_noeq: expr_noeq tSTARTS expr_noeq  */
+#line 938 "engines/director/lingo/lingo-gr.y"
                                                         { (yyval.node) = new BinaryOpNode(LC::c_starts, (yyvsp[-2].node), (yyvsp[0].node)); }
-#line 4732 "engines/director/lingo/lingo-gr.cpp"
+#line 5006 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 332: /* sprite: tSPRITE expr tINTERSECTS simpleexpr  */
-#line 882 "engines/director/lingo/lingo-gr.y"
+  case 364: /* sprite: tSPRITE expr tINTERSECTS simpleexpr  */
+#line 941 "engines/director/lingo/lingo-gr.y"
                                                 { (yyval.node) = new IntersectsNode((yyvsp[-2].node), (yyvsp[0].node)); }
-#line 4738 "engines/director/lingo/lingo-gr.cpp"
+#line 5012 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 333: /* sprite: tSPRITE expr tWITHIN simpleexpr  */
-#line 883 "engines/director/lingo/lingo-gr.y"
+  case 365: /* sprite: tSPRITE expr tWITHIN simpleexpr  */
+#line 942 "engines/director/lingo/lingo-gr.y"
                                                         { (yyval.node) = new WithinNode((yyvsp[-2].node), (yyvsp[0].node)); }
-#line 4744 "engines/director/lingo/lingo-gr.cpp"
+#line 5018 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 334: /* exprlist: %empty  */
-#line 886 "engines/director/lingo/lingo-gr.y"
+  case 366: /* exprlist: %empty  */
+#line 945 "engines/director/lingo/lingo-gr.y"
                                                                 { (yyval.nodelist) = new NodeList; }
-#line 4750 "engines/director/lingo/lingo-gr.cpp"
+#line 5024 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 336: /* nonemptyexprlist: expr  */
-#line 890 "engines/director/lingo/lingo-gr.y"
+  case 368: /* nonemptyexprlist: expr  */
+#line 949 "engines/director/lingo/lingo-gr.y"
                                                         {
 		NodeList *list = new NodeList;
 		list->push_back((yyvsp[0].node));
 		(yyval.nodelist) = list; }
-#line 4759 "engines/director/lingo/lingo-gr.cpp"
+#line 5033 "engines/director/lingo/lingo-gr.cpp"
     break;
 
-  case 337: /* nonemptyexprlist: nonemptyexprlist ',' expr  */
-#line 894 "engines/director/lingo/lingo-gr.y"
+  case 369: /* nonemptyexprlist: nonemptyexprlist ',' expr  */
+#line 953 "engines/director/lingo/lingo-gr.y"
                                                 {
 		(yyvsp[-2].nodelist)->push_back((yyvsp[0].node));
 		(yyval.nodelist) = (yyvsp[-2].nodelist); }
-#line 4767 "engines/director/lingo/lingo-gr.cpp"
+#line 5041 "engines/director/lingo/lingo-gr.cpp"
     break;
 
 
-#line 4771 "engines/director/lingo/lingo-gr.cpp"
+#line 5045 "engines/director/lingo/lingo-gr.cpp"
 
       default: break;
     }
@@ -4965,7 +5239,7 @@ yyreturnlab:
   return yyresult;
 }
 
-#line 899 "engines/director/lingo/lingo-gr.y"
+#line 958 "engines/director/lingo/lingo-gr.y"
 
 
 int yyreport_syntax_error(const yypcontext_t *ctx) {
diff --git a/engines/director/lingo/lingo-gr.h b/engines/director/lingo/lingo-gr.h
index c6ba1237d49..35d6f939a36 100644
--- a/engines/director/lingo/lingo-gr.h
+++ b/engines/director/lingo/lingo-gr.h
@@ -153,7 +153,7 @@ extern int yydebug;
 #if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
 union YYSTYPE
 {
-#line 118 "engines/director/lingo/lingo-gr.y"
+#line 132 "engines/director/lingo/lingo-gr.y"
 
 	Common::String *s;
 	int i;
diff --git a/engines/director/lingo/lingo-gr.y b/engines/director/lingo/lingo-gr.y
index fc23083a5df..3c09c551fd0 100644
--- a/engines/director/lingo/lingo-gr.y
+++ b/engines/director/lingo/lingo-gr.y
@@ -78,6 +78,20 @@ extern int yylex();
 
 using namespace Director;
 
+// if we have the trim garbage flag on, and we encounter
+// an unwelcome surprise in an expression, use everything up until
+// the first failing token.
+#define TRIM_GARBAGE(src, target) \
+	if (g_lingo->_compiler->_trimGarbage) { \
+		warning("Trimming garbage and continuing.");	\
+		src = target;	\
+		yyerrok; \
+	} else { \
+		src = nullptr; \
+		YYABORT; \
+	}	\
+
+
 static void yyerror(const char *s) {
 	LingoCompiler *compiler = g_lingo->_compiler;
 	compiler->_hadError = true;
@@ -257,13 +271,19 @@ scriptpart:	'\n'						{ $$ = nullptr; }
 // See also:
 //   on keyword
 
-macro: tMACRO ID idlist '\n' stmtlist	{ $$ = new HandlerNode($ID, $idlist, $stmtlist); } ;
+macro: tMACRO ID idlist '\n' stmtlist	{ $$ = new HandlerNode($ID, $idlist, $stmtlist); }
+	| tMACRO ID idlist error '\n' stmtlist	{ TRIM_GARBAGE($$, new HandlerNode($ID, $idlist, $stmtlist)); }
+	;
 
 // FACTORY
 
-factory: tFACTORY ID '\n' methodlist	{ $$ = new FactoryNode($ID, $methodlist); } ;
+factory: tFACTORY ID '\n' methodlist	{ $$ = new FactoryNode($ID, $methodlist); }
+	| tFACTORY ID error '\n' methodlist	{ TRIM_GARBAGE($$, new FactoryNode($ID, $methodlist)); }
+	;
 
-method: tMETHOD ID idlist '\n' stmtlist	{ $$ = new HandlerNode($ID, $idlist, $stmtlist); } ;
+method: tMETHOD ID idlist '\n' stmtlist	{ $$ = new HandlerNode($ID, $idlist, $stmtlist); }
+	|  tMETHOD ID idlist error '\n' stmtlist	{ TRIM_GARBAGE($$, new HandlerNode($ID, $idlist, $stmtlist)); }
+	;
 
 methodlist: /* empty */				{ $$ = new NodeList; }
 	| nonemptymethodlist
@@ -293,8 +313,14 @@ handler: tON ID idlist '\n' stmtlist tENDCLAUSE endargdef '\n' {	// D3
 		$$ = new HandlerNode($ID, $idlist, $stmtlist);
 		checkEnd($tENDCLAUSE, $ID, false);
 		delete $tENDCLAUSE; }
+	| tON ID idlist error '\n' stmtlist tENDCLAUSE endargdef '\n' {	// D3
+		TRIM_GARBAGE($$, new HandlerNode($ID, $idlist, $stmtlist));
+		checkEnd($tENDCLAUSE, $ID, false);
+		delete $tENDCLAUSE; }
 	| tON ID idlist '\n' stmtlist {	// D4. No 'end' clause
 		$$ = new HandlerNode($ID, $idlist, $stmtlist); }
+	| tON ID idlist error '\n' stmtlist {	// D4. No 'end' clause
+		TRIM_GARBAGE($$, new HandlerNode($ID, $idlist, $stmtlist)); }
 	;
 
 endargdef:	/* nothing */
@@ -416,24 +442,38 @@ stmtoneliner: proc
 	;
 
 proc: CMDID cmdargs '\n'				{ $$ = new CmdNode($CMDID, $cmdargs, g_lingo->_compiler->_linenumber - 1); }
+	| CMDID cmdargs error '\n'			{ TRIM_GARBAGE($$, new CmdNode($CMDID, $cmdargs, g_lingo->_compiler->_linenumber - 1)); }
 	| tPUT cmdargs '\n'					{ $$ = new CmdNode(new Common::String("put"), $cmdargs, g_lingo->_compiler->_linenumber - 1); }
+	| tPUT cmdargs error '\n'					{ TRIM_GARBAGE($$, new CmdNode(new Common::String("put"), $cmdargs, g_lingo->_compiler->_linenumber - 1)); }
 	| tGO cmdargs '\n'					{ $$ = new CmdNode(new Common::String("go"), $cmdargs, g_lingo->_compiler->_linenumber - 1); }
+	| tGO cmdargs error '\n'					{ TRIM_GARBAGE($$, new CmdNode(new Common::String("go"), $cmdargs, g_lingo->_compiler->_linenumber - 1)); }
 	| tGO frameargs '\n'				{ $$ = new CmdNode(new Common::String("go"), $frameargs, g_lingo->_compiler->_linenumber - 1); }
+	| tGO frameargs error '\n'				{ TRIM_GARBAGE($$, new CmdNode(new Common::String("go"), $frameargs, g_lingo->_compiler->_linenumber - 1)); }
 	| tPLAY cmdargs '\n'				{ $$ = new CmdNode(new Common::String("play"), $cmdargs, g_lingo->_compiler->_linenumber - 1); }
+	| tPLAY cmdargs error '\n'				{ TRIM_GARBAGE($$, new CmdNode(new Common::String("play"), $cmdargs, g_lingo->_compiler->_linenumber - 1)); }
 	| tPLAY frameargs '\n'				{ $$ = new CmdNode(new Common::String("play"), $frameargs, g_lingo->_compiler->_linenumber - 1); }
+	| tPLAY frameargs error '\n'				{ TRIM_GARBAGE($$, new CmdNode(new Common::String("play"), $frameargs, g_lingo->_compiler->_linenumber - 1)); }
 	| tOPEN cmdargs '\n'				{ $$ = new CmdNode(new Common::String("open"), $cmdargs, g_lingo->_compiler->_linenumber - 1); }
+	| tOPEN cmdargs error '\n'				{ TRIM_GARBAGE($$, new CmdNode(new Common::String("open"), $cmdargs, g_lingo->_compiler->_linenumber - 1)); }
 	| tOPEN expr[arg1] tWITH expr[arg2] '\n' {
 		NodeList *args = new NodeList;
 		args->push_back($arg1);
 		args->push_back($arg2);
 		$$ = new CmdNode(new Common::String("open"), args, g_lingo->_compiler->_linenumber - 1); }
 	| tNEXT tREPEAT '\n'				{ $$ = new NextRepeatNode(); }
+	| tNEXT tREPEAT error '\n'				{ TRIM_GARBAGE($$, new NextRepeatNode()); }
 	| tEXIT tREPEAT '\n'				{ $$ = new ExitRepeatNode(); }
+	| tEXIT tREPEAT error '\n'				{ TRIM_GARBAGE($$, new ExitRepeatNode()); }
 	| tEXIT '\n'						{ $$ = new ExitNode(); }
+	| tEXIT error '\n'						{ TRIM_GARBAGE($$, new ExitNode()); }
 	| tRETURN '\n'						{ $$ = new ReturnNode(nullptr); }
+	| tRETURN error '\n'						{ TRIM_GARBAGE($$, new ReturnNode(nullptr)); }
 	| tRETURN expr '\n'					{ $$ = new ReturnNode($expr); }
+	| tRETURN expr error '\n'					{ TRIM_GARBAGE($$, new ReturnNode($expr)); }
 	| tDELETE chunk '\n'				{ $$ = new DeleteNode($chunk); }
+	| tDELETE chunk error '\n'				{ TRIM_GARBAGE($$, new DeleteNode($chunk)); }
 	| tHILITE chunk '\n'				{ $$ = new HiliteNode($chunk); }
+	| tHILITE chunk error '\n'				{ TRIM_GARBAGE($$, new HiliteNode($chunk)); }
 	| tASSERTERROR stmtoneliner			{ $$ = new AssertErrorNode($stmtoneliner); }
 	;
 
@@ -523,16 +563,23 @@ frameargs:
 	;
 
 asgn: tPUT expr tINTO varorchunk '\n'	{ $$ = new PutIntoNode($expr, $varorchunk); }
+	| tPUT expr tINTO varorchunk error '\n'	{ TRIM_GARBAGE($$, new PutIntoNode($expr, $varorchunk)); }
 	| tPUT expr tAFTER varorchunk '\n'	{ $$ = new PutAfterNode($expr, $varorchunk); }
+	| tPUT expr tAFTER varorchunk error '\n'	{ TRIM_GARBAGE($$, new PutAfterNode($expr, $varorchunk)); }
 	| tPUT expr tBEFORE varorchunk '\n'	{ $$ = new PutBeforeNode($expr, $varorchunk); }
+	| tPUT expr tBEFORE varorchunk error '\n'	{ TRIM_GARBAGE($$, new PutBeforeNode($expr, $varorchunk)); }
 	| tSET varorthe to expr '\n'		{ $$ = new SetNode($varorthe, $expr); }
+	| tSET varorthe to expr error '\n'	{ TRIM_GARBAGE($$, new SetNode($varorthe, $expr)); }
 	;
 
 to: tTO | tEQ ;
 
 definevars: tGLOBAL idlist '\n'			{ $$ = new GlobalNode($idlist); }
+	| tGLOBAL idlist error '\n'			{ TRIM_GARBAGE($$, new GlobalNode($idlist)); }
 	| tPROPERTY idlist '\n'				{ $$ = new PropertyNode($idlist); }
+	| tPROPERTY idlist error '\n'				{ TRIM_GARBAGE($$, new PropertyNode($idlist)); }
 	| tINSTANCE idlist '\n'				{ $$ = new InstanceNode($idlist); }
+	| tINSTANCE idlist error '\n'				{ TRIM_GARBAGE($$, new InstanceNode($idlist)); }
 	;
 
 ifstmt: tIF expr tTHEN stmt {
@@ -541,6 +588,8 @@ ifstmt: tIF expr tTHEN stmt {
 		$$ = new IfStmtNode($expr, stmtlist); }
 	| tIF expr tTHEN '\n' stmtlist_insideif endif {
 		$$ = new IfStmtNode($expr, $stmtlist_insideif); }
+	| tIF expr tTHEN error '\n' stmtlist_insideif endif {
+		TRIM_GARBAGE($$, new IfStmtNode($expr, $stmtlist_insideif)); }
 	;
 
 ifelsestmt: tIF expr tTHEN stmt[stmt1] tELSE stmt[stmt2] {
@@ -572,12 +621,20 @@ endif: /* empty */	{
 
 loop: tREPEAT tWHILE expr '\n' stmtlist tENDREPEAT '\n' {
 		$$ = new RepeatWhileNode($expr, $stmtlist); }
+	| tREPEAT tWHILE expr error '\n' stmtlist tENDREPEAT '\n' {
+		TRIM_GARBAGE($$, new RepeatWhileNode($expr, $stmtlist)); }
 	| tREPEAT tWITH ID tEQ expr[start] tTO expr[end] '\n' stmtlist tENDREPEAT '\n' {
 		$$ = new RepeatWithToNode($ID, $start, false, $end, $stmtlist); }
+	| tREPEAT tWITH ID tEQ expr[start] tTO expr[end] error '\n' stmtlist tENDREPEAT '\n' {
+		TRIM_GARBAGE($$, new RepeatWithToNode($ID, $start, false, $end, $stmtlist)); }
 	| tREPEAT tWITH ID tEQ expr[start] tDOWN tTO expr[end] '\n' stmtlist tENDREPEAT '\n' {
 		$$ = new RepeatWithToNode($ID, $start, true, $end, $stmtlist); }
+	| tREPEAT tWITH ID tEQ expr[start] tDOWN tTO expr[end] error '\n' stmtlist tENDREPEAT '\n' {
+		TRIM_GARBAGE($$, new RepeatWithToNode($ID, $start, true, $end, $stmtlist)); }
 	| tREPEAT tWITH ID tIN expr '\n' stmtlist tENDREPEAT '\n' {
 		$$ = new RepeatWithInNode($ID, $expr, $stmtlist); }
+	| tREPEAT tWITH ID tIN expr error '\n' stmtlist tENDREPEAT '\n' {
+		TRIM_GARBAGE($$, new RepeatWithInNode($ID, $expr, $stmtlist)); }
 	;
 
 tell: tTELL expr tTO stmtoneliner				{
@@ -586,6 +643,8 @@ tell: tTELL expr tTO stmtoneliner				{
 		$$ = new TellNode($expr, stmtlist); }
 	| tTELL expr '\n' stmtlist tENDTELL '\n'	{
 		$$ = new TellNode($expr, $stmtlist); }
+	| tTELL expr error '\n' stmtlist tENDTELL '\n'	{
+		TRIM_GARBAGE($$, new TellNode($expr, $stmtlist)); }
 	;
 
 when: tWHEN '\n'					{ $$ = new WhenNode($tWHEN.eventName, $tWHEN.stmt); } ;



From noreply at scummvm.org  Fri Jun 19 23:38:25 2026
From: noreply at scummvm.org (mduggan)
Date: Fri, 19 Jun 2026 23:38:25 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 180b1ae39284c16d2be12f260ec247195e3506b0
Message-ID: <20260619233825.63C6A7005CD@ron.scummvm.net>

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

Summary:
180b1ae392 ACCESS: Fix possible unfilled pixel in Noctropolis dialogs


Commit: 180b1ae39284c16d2be12f260ec247195e3506b0
    https://github.com/scummvm/scummvm/commit/180b1ae39284c16d2be12f260ec247195e3506b0
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2026-06-20T09:38:10+10:00

Commit Message:
ACCESS: Fix possible unfilled pixel in Noctropolis dialogs

The fill rect calculation was off by one, leaving an empty row or column on
some dialogs.

Changed paths:
    engines/access/bubble_box.cpp


diff --git a/engines/access/bubble_box.cpp b/engines/access/bubble_box.cpp
index 7e851dfb4cf..f608616dbdb 100644
--- a/engines/access/bubble_box.cpp
+++ b/engines/access/bubble_box.cpp
@@ -376,10 +376,10 @@ void BubbleBox::doBox_v3(int item, int box) {
 
 	_vm->_screen->saveBlock(_bounds);
 
-	screen.fillRect(Common::Rect(boxX + 5, boxY + 5, boxX + 5 + boxWidth * 16 + 15, boxY + 5 + boxHeight * 16 + 8), 246);
+	screen.fillRect(Common::Rect(boxX + 5, boxY + 5, boxX + 5 + boxWidth * 16 + 16, boxY + 5 + boxHeight * 16 + 9), 246);
 
 	if (_type & kTextBoxNoctCaption) {
-		screen.fillRect(Common::Rect(boxX + 5, boxY + 5, boxX + 5 + boxWidth * 16 + 15, boxY + 5 + fontHeight), 236);
+		screen.fillRect(Common::Rect(boxX + 5, boxY + 5, boxX + 5 + boxWidth * 16 + 16, boxY + 6 + fontHeight), 236);
 		screen.hLine(boxX + 5, boxY + 5 + fontHeight, boxX + 5 + boxWidth * 16 + 15, 237);
 	}
 



From noreply at scummvm.org  Sat Jun 20 00:00:39 2026
From: noreply at scummvm.org (mduggan)
Date: Sat, 20 Jun 2026 00:00:39 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 62b8abf954700126f568b0d8877870a04d9efffa
Message-ID: <20260620000039.AA9A97005CC@ron.scummvm.net>

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

Summary:
62b8abf954 ACCESS: Reset ask list position more in Noctropolis


Commit: 62b8abf954700126f568b0d8877870a04d9efffa
    https://github.com/scummvm/scummvm/commit/62b8abf954700126f568b0d8877870a04d9efffa
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2026-06-20T09:59:34+10:00

Commit Message:
ACCESS: Reset ask list position more in Noctropolis

To be consistent with the original, ask list position should reset if there is
a new ask item.

Changed paths:
    engines/access/noctropolis/noctropolis_game.cpp
    engines/access/scripts.cpp


diff --git a/engines/access/noctropolis/noctropolis_game.cpp b/engines/access/noctropolis/noctropolis_game.cpp
index eecb61287b8..51357662c6a 100644
--- a/engines/access/noctropolis/noctropolis_game.cpp
+++ b/engines/access/noctropolis/noctropolis_game.cpp
@@ -121,6 +121,8 @@ void NoctropolisEngine::initVariables() {
 		entry._value = ITEM_NOT_FOUND;
 	}
 
+	_startAboutBox = _startAboutItem = 0;
+
 	// This is Noct_InitTravel in the original
 	_travScrollRow = 15;
 	_travScrollCol = 0;
diff --git a/engines/access/scripts.cpp b/engines/access/scripts.cpp
index 9853559cf8f..debbd6b6775 100644
--- a/engines/access/scripts.cpp
+++ b/engines/access/scripts.cpp
@@ -795,10 +795,11 @@ void Scripts::cmdSetAbout() {
 		error("Invalid index %d in cmdSetAbout", idx);
 
 	debugC(1, kDebugScripts, "cmdSetAbout(idx=%d, val=%d)", idx, val);
+	bool isChange = _vm->_ask[idx] != val;
 	_vm->_ask[idx] = val;
 
-	// Reset location of about items, except in Noctropolis
-	if (_vm->getGameID() != kGameNoctropolis)
+	// Reset top of list, only if it's a change in Noctropolis.
+	if (_vm->getGameID() != kGameNoctropolis || isChange)
 		_vm->_startAboutBox = _vm->_startAboutItem = 0;
 }
 



From noreply at scummvm.org  Sat Jun 20 01:40:47 2026
From: noreply at scummvm.org (moralrecordings)
Date: Sat, 20 Jun 2026 01:40:47 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 3d408ec3516f7c29314d8ae8fb7916f31c9cd9aa
Message-ID: <20260620014047.5EA2C7005CC@ron.scummvm.net>

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

Summary:
3d408ec351 Revert "DIRECTOR: Keep track of context when working with factories"


Commit: 3d408ec3516f7c29314d8ae8fb7916f31c9cd9aa
    https://github.com/scummvm/scummvm/commit/3d408ec3516f7c29314d8ae8fb7916f31c9cd9aa
Author: Scott Percival (code at moral.net.au)
Date: 2026-06-20T09:28:41+08:00

Commit Message:
Revert "DIRECTOR: Keep track of context when working with factories"

Keeping a Movie pointer in the Cast doesn't match the lifetime of
Movie, and causes a use-after-free crash following a movie switch.

This reverts commit 379e0dcde6887185af651dbb5e9636dd3655e039.

Changed paths:
    engines/director/cast.cpp
    engines/director/cast.h
    engines/director/lingo/lingo-code.cpp
    engines/director/lingo/lingo-object.cpp
    engines/director/lingo/lingo-object.h
    engines/director/lingo/lingo.cpp
    engines/director/lingo/lingo.h


diff --git a/engines/director/cast.cpp b/engines/director/cast.cpp
index b9d8ba07068..7f403b7cd5e 100644
--- a/engines/director/cast.cpp
+++ b/engines/director/cast.cpp
@@ -1806,7 +1806,6 @@ void Cast::loadLingoContext(Common::SeekableReadStreamEndian &stream) {
 				if (_lingoArchive->getScriptContext(script->_scriptType, script->_id)) {
 					error("Cast::loadLingoContext: Script already defined for type %s, id %d", scriptType2str(script->_scriptType), script->_id);
 				}
-				script->_cast = this;
 				_lingoArchive->scriptContexts[script->_scriptType][script->_id] = script;
 				_lingoArchive->patchScriptHandler(script->_scriptType, CastMemberID(script->_id, _castLibID));
 			} else {
diff --git a/engines/director/cast.h b/engines/director/cast.h
index 43f08651f59..d6be2246252 100644
--- a/engines/director/cast.h
+++ b/engines/director/cast.h
@@ -92,7 +92,6 @@ public:
 	void loadArchive();
 	void setArchive(Common::SharedPtr<Archive> archive);
 	Common::SharedPtr<Archive> getArchive() const { return _castArchive; };
-	Movie *getMovie() const { return _movie; }
 	Common::String getMacName() const { return _macName; }
 	Common::String getCastName() const { return _castName; }
 	void setCastName(const Common::String &name) { _castName = name; }
diff --git a/engines/director/lingo/lingo-code.cpp b/engines/director/lingo/lingo-code.cpp
index d73993e97a5..6d1a422f89c 100644
--- a/engines/director/lingo/lingo-code.cpp
+++ b/engines/director/lingo/lingo-code.cpp
@@ -47,7 +47,6 @@
 
 #include "director/director.h"
 #include "director/debugger.h"
-#include "director/cast.h"
 #include "director/movie.h"
 #include "director/score.h"
 #include "director/sprite.h"
@@ -259,18 +258,6 @@ void Lingo::pushContext(const Symbol funcSym, bool allowRetVal, Datum defaultRet
 		_state->context->incRefCount();
 	}
 
-	// Run the handler in the window that owns its script (MIAW scoping); mirrors c_tell().
-	fp->retWindow = nullptr;
-	if (funcSym.ctx && funcSym.ctx->_cast && funcSym.ctx->_cast->getMovie()) {
-		Window *targetWindow = funcSym.ctx->_cast->getMovie()->getWindow();
-		Window *currentWindow = _vm->getCurrentWindow();
-		if (targetWindow && targetWindow != currentWindow) {
-			fp->retWindow = currentWindow;
-			currentWindow->moveLingoState(targetWindow);
-			_vm->setCurrentWindow(targetWindow);
-		}
-	}
-
 	DatumHash *localvars = new DatumHash;
 	if (funcSym.anonymous && _state->localVars) {
 		// Execute anonymous functions within the current var frame.
@@ -377,16 +364,8 @@ void Lingo::popContext(bool aborting) {
 		printCallStack(_state->pc);
 	}
 
-	// Undo the pushContext window switch.
-	Window *retWindow = fp->retWindow;
-
 	delete fp;
 
-	if (retWindow && _vm->getCurrentWindow() != retWindow) {
-		_vm->getCurrentWindow()->moveLingoState(retWindow);
-		_vm->setCurrentWindow(retWindow);
-	}
-
 	g_debugger->popContextHook();
 }
 
diff --git a/engines/director/lingo/lingo-object.cpp b/engines/director/lingo/lingo-object.cpp
index fd191216170..4355af923f4 100644
--- a/engines/director/lingo/lingo-object.cpp
+++ b/engines/director/lingo/lingo-object.cpp
@@ -577,7 +577,6 @@ ScriptContext::ScriptContext(const ScriptContext &sc) : Object<ScriptContext>(sc
 
 	_id = sc._id;
 	_castLibHint = sc._castLibHint;
-	_cast = sc._cast;
 }
 
 ScriptContext::~ScriptContext() {
diff --git a/engines/director/lingo/lingo-object.h b/engines/director/lingo/lingo-object.h
index b56717251e4..ebd29576ad4 100644
--- a/engines/director/lingo/lingo-object.h
+++ b/engines/director/lingo/lingo-object.h
@@ -217,7 +217,6 @@ public:
 	int _scriptId;
 	uint16 _parentNumber;
 	uint16 _castLibHint;
-	Cast *_cast = nullptr;	// owning cast (MIAW scoping)
 	Common::Array<Common::String> _functionNames; // used by cb_localcall
 	Common::HashMap<Common::String, Common::Array<uint32>> _functionByteOffsets;
 	SymbolHash _functionHandlers;
diff --git a/engines/director/lingo/lingo.cpp b/engines/director/lingo/lingo.cpp
index 95ab781a34e..d11bbe941ad 100644
--- a/engines/director/lingo/lingo.cpp
+++ b/engines/director/lingo/lingo.cpp
@@ -401,7 +401,6 @@ void LingoArchive::addCode(const Common::U32String &code, ScriptType type, uint1
 
 	ScriptContext *sc = g_lingo->_compiler->compileLingo(code, this, type, CastMemberID(id, cast->_castLibID), contextName, false, preprocFlags);
 	if (sc) {
-		sc->_cast = cast;
 		scriptContexts[type][id] = sc;
 		sc->incRefCount();
 	}
diff --git a/engines/director/lingo/lingo.h b/engines/director/lingo/lingo.h
index 2f30e6f1902..e140baedd48 100644
--- a/engines/director/lingo/lingo.h
+++ b/engines/director/lingo/lingo.h
@@ -46,7 +46,6 @@ class Cast;
 class ScriptContext;
 class DirectorEngine;
 class Frame;
-class Window;
 class LingoCompiler;
 struct Breakpoint;
 
@@ -263,7 +262,6 @@ struct CFrame {	/* proc/func call stack frame */
 	Datum			defaultRetVal;		/* default return value */
 	int				paramCount;			/* original number of arguments submitted */
 	Common::Array<Datum> paramList;		/* original argument list */
-	Window			*retWindow;			/* window to restore on return */
 };
 
 struct LingoEvent {



From noreply at scummvm.org  Sat Jun 20 02:16:28 2026
From: noreply at scummvm.org (tag2015)
Date: Sat, 20 Jun 2026 02:16:28 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 da6a0efe1a3f2522e445719d3111566440959405
Message-ID: <20260620021628.4F3487005CC@ron.scummvm.net>

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

Summary:
da6a0efe1a AGS: Update Wadjet game entries


Commit: da6a0efe1a3f2522e445719d3111566440959405
    https://github.com/scummvm/scummvm/commit/da6a0efe1a3f2522e445719d3111566440959405
Author: Walter Agazzi (walter.agazzi at protonmail.com)
Date: 2026-06-20T04:15:33+02:00

Commit Message:
AGS: Update Wadjet game entries

blackwell4 / primordia / strangeland /unavowed

Changed paths:
    engines/ags/detection_tables.h


diff --git a/engines/ags/detection_tables.h b/engines/ags/detection_tables.h
index e96ec24354d..f761712bac4 100644
--- a/engines/ags/detection_tables.h
+++ b/engines/ags/detection_tables.h
@@ -4778,6 +4778,7 @@ const AGSGameDescription GAME_DESCRIPTIONS[] = {
 	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("blackwell4", "deception.exe", "9cca5df9331a98e910cce4d5b4ea24f0", 219797616, 0),  //v3.4b
 	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("blackwell4", "deception.exe", "9cca5df9331a98e910cce4d5b4ea24f0", 219800273, 0),  //v3.5
 	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("blackwell4", "deception.exe", "9cca5df9331a98e910cce4d5b4ea24f0", 219803342, 0),  //v3.7
+	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("blackwell4", "deception.exe", "4fd8fe625df33c613b09d6569d3a8d27", 219774654, 0),  //v3.8
 	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("blackwell4", "deception.ags", "3c999468155295d3e0809c04b2e53cff", 216579565, 0),  //Linux v3.0
 	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("blackwell4", "deception.ags", "1f174c44bfa6725ceb6b80aa9cc69d7e", 216583148, 0),  //Linux v3.1a
 	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("blackwell4", "deception.ags", "ff5e37e618eb9ed86c4a40de806e9a4a", 216583146, 0),  //Linux v3.2
@@ -4785,6 +4786,7 @@ const AGSGameDescription GAME_DESCRIPTIONS[] = {
 	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("blackwell4", "deception.ags", "122ce61f1697dfcca1e4d56fe4ab6a3d", 216583772, 0),  //Linux v3.3c
 	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("blackwell4", "deception.ags", "18bf596634e933ddfe775be2d1c93cd5", 216586429, 0),  //Linux v3.5
 	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("blackwell4", "deception.ags", "b500996529c1fe4899a1ba304785bb18", 216589498, 0),  //Linux v3.7
+	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("blackwell4", "deception.ags", "dd9fb2d7310bf7036141e6f1f32d1cef", 216595114, 0),  //Linux v3.8
 	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("blackwell4", "agsgame.dat", "1c0bf79d9720d0a81219e8778fb7e231", 304040703, 0), // Linux
 	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("blackwell4", "ac2game.dat", "05a3a5ff709a5764d2b4a5dc301107d6", 302140480, 0), // Mac
 	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("blackwell4", "ac2game.dat", "3c999468155295d3e0809c04b2e53cff", 216579565, 0), // Mac v3.0
@@ -4794,6 +4796,7 @@ const AGSGameDescription GAME_DESCRIPTIONS[] = {
 	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("blackwell4", "ac2game.dat", "122ce61f1697dfcca1e4d56fe4ab6a3d", 216583772, 0), // Mac v3.5d
 	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("blackwell4", "ac2game.dat", "18bf596634e933ddfe775be2d1c93cd5", 216586429, 0), // Mac v3.6a
 	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("blackwell4", "ac2game.dat", "b500996529c1fe4899a1ba304785bb18", 216589498, 0), // Mac v3.7
+	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("blackwell4", "ac2game.dat", "dd9fb2d7310bf7036141e6f1f32d1cef", 216595114, 0), // Mac v3.8
 	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("blackwell4", "deception.exe", "b3b192cf20a2f7666ddea3410dbd87cc", 304076138, GAMEFLAG_PLUGINS_AGSTEAM_WADJETEYE),
 	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("blackwell4", "deception.exe", "499c3261a1f73c5b78125beeca1c2d08", 305215631, GAMEFLAG_PLUGINS_AGSTEAM_WADJETEYE),
 	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("blackwell4", "deception.exe", "8f7335b030b38b610637b3033859f828", 305293936, GAMEFLAG_PLUGINS_AGSTEAM_WADJETEYE),
@@ -4805,6 +4808,7 @@ const AGSGameDescription GAME_DESCRIPTIONS[] = {
 	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("blackwell4", "deception.exe", "9cca5df9331a98e910cce4d5b4ea24f0", 219797389, GAMEFLAG_PLUGINS_AGSTEAM_WADJETEYE),
 	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("blackwell4", "deception.exe", "9cca5df9331a98e910cce4d5b4ea24f0", 219800046, GAMEFLAG_PLUGINS_AGSTEAM_WADJETEYE),
 	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("blackwell4", "deception.exe", "9cca5df9331a98e910cce4d5b4ea24f0", 219803115, GAMEFLAG_PLUGINS_AGSTEAM_WADJETEYE),
+	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("blackwell4", "deception.exe", "4fd8fe625df33c613b09d6569d3a8d27", 219774427, GAMEFLAG_PLUGINS_AGSTEAM_WADJETEYE),
 	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("blackwell4", "ac2game.dat", "8492a5cf8a91044a8505bc62bc1e9dfc", 216579330, GAMEFLAG_PLUGINS_AGSTEAM_WADJETEYE),  // Mac
 	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("blackwell4", "ac2game.dat", "1f174c44bfa6725ceb6b80aa9cc69d7e", 216583148, GAMEFLAG_PLUGINS_AGSTEAM_WADJETEYE),  // Mac
 	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("blackwell4", "ac2game.dat", "fa0be2343abc9833940630b40dbc0042", 216582919, GAMEFLAG_PLUGINS_AGSTEAM_WADJETEYE),  // Mac
@@ -4812,6 +4816,7 @@ const AGSGameDescription GAME_DESCRIPTIONS[] = {
 	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("blackwell4", "ac2game.dat", "061394fb335c11ee1994c03bd620b7a3", 216583545, GAMEFLAG_PLUGINS_AGSTEAM_WADJETEYE),  // Mac
 	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("blackwell4", "ac2game.dat", "ca48a7dfffe27396b91909891e4900e8", 216586202, GAMEFLAG_PLUGINS_AGSTEAM_WADJETEYE),  // Mac
 	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("blackwell4", "ac2game.dat", "10a515d03bf1a9ff40dcca9abb15bf1a", 216589271, GAMEFLAG_PLUGINS_AGSTEAM_WADJETEYE),  // Mac
+	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("blackwell4", "ac2game.dat", "1ca89f6a4bbc8766f02f5d9e82dfa9ea", 216594887, GAMEFLAG_PLUGINS_AGSTEAM_WADJETEYE),  // Mac
 	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("blackwell4", "deception.ags", "8492a5cf8a91044a8505bc62bc1e9dfc", 216579330, GAMEFLAG_PLUGINS_AGSTEAM_WADJETEYE), // Linux
 	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("blackwell4", "deception.ags", "e08b56307a597c1384e45b7e6e25d14c", 216582921, GAMEFLAG_PLUGINS_AGSTEAM_WADJETEYE), // Linux
 	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("blackwell4", "deception.ags", "fa0be2343abc9833940630b40dbc0042", 216582919, GAMEFLAG_PLUGINS_AGSTEAM_WADJETEYE), // Linux
@@ -4819,7 +4824,9 @@ const AGSGameDescription GAME_DESCRIPTIONS[] = {
 	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("blackwell4", "deception.ags", "061394fb335c11ee1994c03bd620b7a3", 216583545, GAMEFLAG_PLUGINS_AGSTEAM_WADJETEYE), // Linux
 	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("blackwell4", "deception.ags", "ca48a7dfffe27396b91909891e4900e8", 216586202, GAMEFLAG_PLUGINS_AGSTEAM_WADJETEYE), // Linux
 	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("blackwell4", "deception.ags", "10a515d03bf1a9ff40dcca9abb15bf1a", 216589271, GAMEFLAG_PLUGINS_AGSTEAM_WADJETEYE), // Linux
+	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("blackwell4", "deception.ags", "1ca89f6a4bbc8766f02f5d9e82dfa9ea", 216594887, GAMEFLAG_PLUGINS_AGSTEAM_WADJETEYE), // Linux
 	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("blackwell4", "deception.ags", "1023007c2a9d8148566f56c74013c650", 216635035, GAMEFLAG_PLUGINS_AGSTEAM_WADJETEYE), // Steamdeck
+	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("blackwell4", "deception.ags", "d6c30d736edb75d94c7e63eeac1573be", 216641780, GAMEFLAG_PLUGINS_AGSTEAM_WADJETEYE), // Steamdeck
 	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("blackwell5", "epiphany.exe", "c1cddd6fcdbcd030beda9f10d4e4270a", 281849897, 0),
 	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("blackwell5", "epiphany.exe", "c3f7a995bbea7ce4ba7a2a97995c677e", 283092455, 0),  //v2.2
 	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("blackwell5", "epiphany.exe", "c3f7a995bbea7ce4ba7a2a97995c677e", 283092983, 0),  //v2.3
@@ -5227,6 +5234,7 @@ const AGSGameDescription GAME_DESCRIPTIONS[] = {
 	GAME_ENTRY_PLUGIN_GOG_NOAUTOSAVE("primordia", "primordia.ags", "fe4f46407ac736cf0e6badf96cef4ee5", 866352226, 0), // v5.2
 	GAME_ENTRY_PLUGIN_GOG_NOAUTOSAVE("primordia", "primordia.ags", "fe4f46407ac736cf0e6badf96cef4ee5", 866352218, 0), // v5.2a
 	GAME_ENTRY_PLUGIN_GOG_NOAUTOSAVE("primordia", "primordia.ags", "27d64680de79f67622d51282bb806f95", 866354351, 0), // v5.3
+	GAME_ENTRY_PLUGIN_GOG_NOAUTOSAVE("primordia", "primordia.ags", "27d64680de79f67622d51282bb806f95", 866376616, 0), // v5.4
 	GAME_ENTRY_PLUGIN_GOG_NOAUTOSAVE("primordia", "agsgame.dat", "22313e59c3233001488c26f18c80cc08", 973495830, 0), // Linux
 	GAME_ENTRY_PLUGIN_GOG_NOAUTOSAVE("primordia", "ac2game.dat", "7755b4df8706de91f31ddeee98bd20e9", 976474850, 0), // Mac
 	GAME_ENTRY_PLUGIN_GOG_NOAUTOSAVE("primordia", "ac2game.dat", "7755b4df8706de91f31ddeee98bd20e9", 976475347, 0), // Mac v2.7a
@@ -5239,6 +5247,7 @@ const AGSGameDescription GAME_DESCRIPTIONS[] = {
 	GAME_ENTRY_PLUGIN_GOG_NOAUTOSAVE("primordia", "ac2game.dat", "fe4f46407ac736cf0e6badf96cef4ee5", 866349239, 0), // Mac v4.4
 	GAME_ENTRY_PLUGIN_GOG_NOAUTOSAVE("primordia", "ac2game.dat", "fe4f46407ac736cf0e6badf96cef4ee5", 866352218, 0), // Mac v5.2a
 	GAME_ENTRY_PLUGIN_GOG_NOAUTOSAVE("primordia", "ac2game.dat", "27d64680de79f67622d51282bb806f95", 866354351, 0), // Mac v5.3
+	GAME_ENTRY_PLUGIN_GOG_NOAUTOSAVE("primordia", "ac2game.dat", "27d64680de79f67622d51282bb806f95", 866376616, 0), // Mac v5.4
 	GAME_ENTRY_PLUGIN_STEAM_NOAUTOSAVE("primordia", "primordia.exe", "f2edc9c3161f1f538df9b4c59fc89e24", 978377182, 0),
 	GAME_ENTRY_PLUGIN_STEAM_NOAUTOSAVE("primordia", "primordia.exe", "8f717a5a14ceda815292ce4065348afb", 979602678, 0),  // with Ita translation
 	GAME_ENTRY_PLUGIN_STEAM_NOAUTOSAVE("primordia", "primordia.exe", "8f717a5a14ceda815292ce4065348afb", 979603766, 0),
@@ -5256,7 +5265,9 @@ const AGSGameDescription GAME_DESCRIPTIONS[] = {
 	GAME_ENTRY_PLUGIN_STEAM_NOAUTOSAVE("primordia", "primordia.ags", "fe4f46407ac736cf0e6badf96cef4ee5", 866351140, 0),
 	GAME_ENTRY_PLUGIN_STEAM_NOAUTOSAVE("primordia", "primordia.ags", "fe4f46407ac736cf0e6badf96cef4ee5", 866351966, 0),  // v5.2a
 	GAME_ENTRY_PLUGIN_STEAM_NOAUTOSAVE("primordia", "primordia.ags", "27d64680de79f67622d51282bb806f95", 866354168, 0),  // v5.3
+	GAME_ENTRY_PLUGIN_STEAM_NOAUTOSAVE("primordia", "primordia.ags", "27d64680de79f67622d51282bb806f95", 866376364, 0),  // v5.4
 	GAME_ENTRY_PLUGIN_STEAM_NOAUTOSAVE("primordia", "primordia.ags", "27d64680de79f67622d51282bb806f95", 866330430, 0),  // Steamdeck
+	GAME_ENTRY_PLUGIN_STEAM_NOAUTOSAVE("primordia", "primordia.ags", "27d64680de79f67622d51282bb806f95", 866353583, 0),  // Steamdeck
 	GAME_ENTRY_PLUGIN_STEAM_NOAUTOSAVE("primordia", "ac2game.dat", "7755b4df8706de91f31ddeee98bd20e9", 976475938, 0),  // Mac
 	GAME_ENTRY_PLUGIN_STEAM_NOAUTOSAVE("primordia", "ac2game.dat", "fe4f46407ac736cf0e6badf96cef4ee5", 866341384, 0),  // Mac v3.6 updated
 	GAME_ENTRY_PLUGIN_STEAM_NOAUTOSAVE("primordia", "ac2game.dat", "fe4f46407ac736cf0e6badf96cef4ee5", 866341392, 0),  // Mac v3.6 updated2
@@ -5268,6 +5279,7 @@ const AGSGameDescription GAME_DESCRIPTIONS[] = {
 	GAME_ENTRY_PLUGIN_STEAM_NOAUTOSAVE("primordia", "ac2game.dat", "fe4f46407ac736cf0e6badf96cef4ee5", 866348987, 0),  // Mac v4.0b
 	GAME_ENTRY_PLUGIN_STEAM_NOAUTOSAVE("primordia", "ac2game.dat", "fe4f46407ac736cf0e6badf96cef4ee5", 866351966, 0),  // Mac v5.2a
 	GAME_ENTRY_PLUGIN_STEAM_NOAUTOSAVE("primordia", "ac2game.dat", "27d64680de79f67622d51282bb806f95", 866354168, 0),  // Mac v5.3
+	GAME_ENTRY_PLUGIN_STEAM_NOAUTOSAVE("primordia", "ac2game.dat", "27d64680de79f67622d51282bb806f95", 866376364, 0),  // Mac v5.4
 	GAME_ENTRY("primordia", "primordia.exe", "22313e59c3233001488c26f18c80cc08", 973154021), // DVD version
 	GAME_ENTRY("primordia", "primordia.exe", "8f717a5a14ceda815292ce4065348afb", 978722743),
 	GAME_ENTRY_EN_STEAM("primordialfate", "PrimordialFate.exe", "1263af4b7ae146be14aa8ada3c8c7d9c", 131697770),
@@ -5442,29 +5454,36 @@ const AGSGameDescription GAME_DESCRIPTIONS[] = {
 	DETECTION_ENTRY_GUIO("strangeland", "Strangeland.exe", "a20e9de4567f25b692c724d9783436ac", 3012885588, Common::UNK_LANG, "GOG.com", GUIO3(GUIO_NOLANG, GUIO_NOLAUNCHLOAD, GAMEOPTION_NO_SAVELOAD), ADGF_NO_FLAGS, 0), // Win v3.2c
 	DETECTION_ENTRY_GUIO("strangeland", "Strangeland.exe", "a20e9de4567f25b692c724d9783436ac", 3012887472, Common::UNK_LANG, "GOG.com", GUIO3(GUIO_NOLANG, GUIO_NOLAUNCHLOAD, GAMEOPTION_NO_SAVELOAD), ADGF_NO_FLAGS, 0), // Win v3.3
 	DETECTION_ENTRY_GUIO("strangeland", "Strangeland.exe", "a20e9de4567f25b692c724d9783436ac", 3013047904, Common::UNK_LANG, "GOG.com", GUIO3(GUIO_NOLANG, GUIO_NOLAUNCHLOAD, GAMEOPTION_NO_SAVELOAD), ADGF_NO_FLAGS, 0), // Win v4.1
+	DETECTION_ENTRY_GUIO("strangeland", "Strangeland.exe", "a20e9de4567f25b692c724d9783436ac", 3013047862, Common::UNK_LANG, "GOG.com", GUIO3(GUIO_NOLANG, GUIO_NOLAUNCHLOAD, GAMEOPTION_NO_SAVELOAD), ADGF_NO_FLAGS, 0), // Win v4.2
 	DETECTION_ENTRY_GUIO("strangeland", "ac2game.dat", "511e08ad31c4020a9ca01b6f7ce1365f", 3009634736, Common::UNK_LANG, "GOG.com", GUIO3(GUIO_NOLANG, GUIO_NOLAUNCHLOAD, GAMEOPTION_NO_SAVELOAD), ADGF_NO_FLAGS, 0), // Mac
 	DETECTION_ENTRY_GUIO("strangeland", "ac2game.dat", "7e5abc5202098bd00ddef999854eb9ab", 3183368017, Common::UNK_LANG, "GOG.com", GUIO3(GUIO_NOLANG, GUIO_NOLAUNCHLOAD, GAMEOPTION_NO_SAVELOAD), ADGF_NO_FLAGS, 0), // Mac
 	DETECTION_ENTRY_GUIO("strangeland", "ac2game.dat", "e4b9f0aa26c1115143ad483c99bc9383", 3183880236, Common::UNK_LANG, "GOG.com", GUIO3(GUIO_NOLANG, GUIO_NOLAUNCHLOAD, GAMEOPTION_NO_SAVELOAD), ADGF_NO_FLAGS, 0), // Mac v2.5a
 	DETECTION_ENTRY_GUIO("strangeland", "ac2game.dat", "86e8b93b8231d6c571669f1621561a21", 3183886917, Common::UNK_LANG, "GOG.com", GUIO3(GUIO_NOLANG, GUIO_NOLAUNCHLOAD, GAMEOPTION_NO_SAVELOAD), ADGF_NO_FLAGS, 0), // Mac v2.7
 	DETECTION_ENTRY_GUIO("strangeland", "ac2game.dat", "c0d0009485795a4ff0cf9dbe5ad82a2f", 3183902196, Common::UNK_LANG, "GOG.com", GUIO3(GUIO_NOLANG, GUIO_NOLAUNCHLOAD, GAMEOPTION_NO_SAVELOAD), ADGF_NO_FLAGS, 0), // Mac v3?
 	DETECTION_ENTRY_GUIO("strangeland", "ac2game.dat", "0d676355c58cde3a7ce7a61abe3180e2", 3009812556, Common::UNK_LANG, "GOG.com", GUIO3(GUIO_NOLANG, GUIO_NOLAUNCHLOAD, GAMEOPTION_NO_SAVELOAD), ADGF_NO_FLAGS, 0), // Mac v4.1
+	DETECTION_ENTRY_GUIO("strangeland", "ac2game.dat", "6c6fc4f1c0f82ee99a50409db53e22b4", 3009812514, Common::UNK_LANG, "GOG.com", GUIO3(GUIO_NOLANG, GUIO_NOLAUNCHLOAD, GAMEOPTION_NO_SAVELOAD), ADGF_NO_FLAGS, 0), // Mac v4.2
 	DETECTION_ENTRY_GUIO("strangeland", "Strangeland.ags", "0d676355c58cde3a7ce7a61abe3180e2", 3009812556, Common::UNK_LANG, "GOG.com", GUIO3(GUIO_NOLANG, GUIO_NOLAUNCHLOAD, GAMEOPTION_NO_SAVELOAD), ADGF_NO_FLAGS, 0), // Linux v4.1
+	DETECTION_ENTRY_GUIO("strangeland", "Strangeland.ags", "6c6fc4f1c0f82ee99a50409db53e22b4", 3009812514, Common::UNK_LANG, "GOG.com", GUIO3(GUIO_NOLANG, GUIO_NOLAUNCHLOAD, GAMEOPTION_NO_SAVELOAD), ADGF_NO_FLAGS, 0), // Linux v4.2
 	DETECTION_ENTRY_GUIO("strangeland", "Strangeland.exe", "c5978d50a9b7ee1c8a50a731847d8504", 3186428869, Common::UNK_LANG, "Steam", GUIO3(GUIO_NOLANG, GUIO_NOLAUNCHLOAD, GAMEOPTION_NO_SAVELOAD), ADGF_NO_FLAGS, 0), // Win 2.3
 	DETECTION_ENTRY_GUIO("strangeland", "Strangeland.exe", "c5978d50a9b7ee1c8a50a731847d8504", 3187004654, Common::UNK_LANG, "Steam", GUIO3(GUIO_NOLANG, GUIO_NOLAUNCHLOAD, GAMEOPTION_NO_SAVELOAD), ADGF_NO_FLAGS, 0), // Win-Linux v3.0
 	DETECTION_ENTRY_GUIO("strangeland", "Strangeland.exe", "a20e9de4567f25b692c724d9783436ac", 3012885224, Common::UNK_LANG, "Steam", GUIO3(GUIO_NOLANG, GUIO_NOLAUNCHLOAD, GAMEOPTION_NO_SAVELOAD), ADGF_NO_FLAGS, 0), // Win v3.2?
 	DETECTION_ENTRY_GUIO("strangeland", "Strangeland.exe", "a20e9de4567f25b692c724d9783436ac", 3012885314, Common::UNK_LANG, "Steam", GUIO3(GUIO_NOLANG, GUIO_NOLAUNCHLOAD, GAMEOPTION_NO_SAVELOAD), ADGF_NO_FLAGS, 0), // Win v3.2c
 	DETECTION_ENTRY_GUIO("strangeland", "Strangeland.exe", "a20e9de4567f25b692c724d9783436ac", 3012887258, Common::UNK_LANG, "Steam", GUIO3(GUIO_NOLANG, GUIO_NOLAUNCHLOAD, GAMEOPTION_NO_SAVELOAD), ADGF_NO_FLAGS, 0), // Win v3.3
 	DETECTION_ENTRY_GUIO("strangeland", "Strangeland.exe", "a20e9de4567f25b692c724d9783436ac", 3013047630, Common::UNK_LANG, "Steam", GUIO3(GUIO_NOLANG, GUIO_NOLAUNCHLOAD, GAMEOPTION_NO_SAVELOAD), ADGF_NO_FLAGS, 0), // Win v4.1
+	DETECTION_ENTRY_GUIO("strangeland", "Strangeland.exe", "a20e9de4567f25b692c724d9783436ac", 3013047588, Common::UNK_LANG, "Steam", GUIO3(GUIO_NOLANG, GUIO_NOLAUNCHLOAD, GAMEOPTION_NO_SAVELOAD), ADGF_NO_FLAGS, 0), // Win v4.2
 	DETECTION_ENTRY_GUIO("strangeland", "Strangeland.ags", "186db7411f5dd4b8996be8a47d3a15ea", 3009649966, Common::UNK_LANG, "Steam", GUIO3(GUIO_NOLANG, GUIO_NOLAUNCHLOAD, GAMEOPTION_NO_SAVELOAD), ADGF_NO_FLAGS, 0), // Linux v3.2c
 	DETECTION_ENTRY_GUIO("strangeland", "Strangeland.ags", "d305575f31c9dbf0ddfe156a4e313261", 3009651910, Common::UNK_LANG, "Steam", GUIO3(GUIO_NOLANG, GUIO_NOLAUNCHLOAD, GAMEOPTION_NO_SAVELOAD), ADGF_NO_FLAGS, 0), // Linux v3.3
 	DETECTION_ENTRY_GUIO("strangeland", "Strangeland.ags", "a275c941ce0afff0ef69fb7376c04e17", 3009812282, Common::UNK_LANG, "Steam", GUIO3(GUIO_NOLANG, GUIO_NOLAUNCHLOAD, GAMEOPTION_NO_SAVELOAD), ADGF_NO_FLAGS, 0), // Linux v4.1
+	DETECTION_ENTRY_GUIO("strangeland", "Strangeland.ags", "eaad004cb2c3a2e6d5538e8f04624d2b", 3009812240, Common::UNK_LANG, "Steam", GUIO3(GUIO_NOLANG, GUIO_NOLAUNCHLOAD, GAMEOPTION_NO_SAVELOAD), ADGF_NO_FLAGS, 0), // Linux v4.2
 	DETECTION_ENTRY_GUIO("strangeland", "Strangeland.ags", "be199765b7e2d7e01a96d215f26e4aa8", 3010815535, Common::UNK_LANG, "Steam", GUIO3(GUIO_NOLANG, GUIO_NOLAUNCHLOAD, GAMEOPTION_NO_SAVELOAD), ADGF_NO_FLAGS, 0), // Steamdeck
 	DETECTION_ENTRY_GUIO("strangeland", "Strangeland.ags", "82d4462a5d040f0a1beb383ac3fc2271", 3010975907, Common::UNK_LANG, "Steam", GUIO3(GUIO_NOLANG, GUIO_NOLAUNCHLOAD, GAMEOPTION_NO_SAVELOAD), ADGF_NO_FLAGS, 0), // Steamdeck
+	DETECTION_ENTRY_GUIO("strangeland", "Strangeland.ags", "f820453c664755b02a255d7f4e5867b3", 3010975973, Common::UNK_LANG, "Steam", GUIO3(GUIO_NOLANG, GUIO_NOLAUNCHLOAD, GAMEOPTION_NO_SAVELOAD), ADGF_NO_FLAGS, 0), // Steamdeck
 	DETECTION_ENTRY_GUIO("strangeland", "ac2game.dat", "e037ab3414dec971b90715c7dbb56f4c", 3183901914, Common::UNK_LANG, "Steam", GUIO3(GUIO_NOLANG, GUIO_NOLAUNCHLOAD, GAMEOPTION_NO_SAVELOAD), ADGF_NO_FLAGS, 0), // Mac
 	DETECTION_ENTRY_GUIO("strangeland", "ac2game.dat", "f82b54c827a6c77c9f3922f8d3c52565", 3183901922, Common::UNK_LANG, "Steam", GUIO3(GUIO_NOLANG, GUIO_NOLAUNCHLOAD, GAMEOPTION_NO_SAVELOAD), ADGF_NO_FLAGS, 0), // Mac
 	DETECTION_ENTRY_GUIO("strangeland", "ac2game.dat", "186db7411f5dd4b8996be8a47d3a15ea", 3009649966, Common::UNK_LANG, "Steam", GUIO3(GUIO_NOLANG, GUIO_NOLAUNCHLOAD, GAMEOPTION_NO_SAVELOAD), ADGF_NO_FLAGS, 0), // Mac v3.6
 	DETECTION_ENTRY_GUIO("strangeland", "ac2game.dat", "d305575f31c9dbf0ddfe156a4e313261", 3009651910, Common::UNK_LANG, "Steam", GUIO3(GUIO_NOLANG, GUIO_NOLAUNCHLOAD, GAMEOPTION_NO_SAVELOAD), ADGF_NO_FLAGS, 0), // Mac v3.6a
 	DETECTION_ENTRY_GUIO("strangeland", "ac2game.dat", "a275c941ce0afff0ef69fb7376c04e17", 3009812282, Common::UNK_LANG, "Steam", GUIO3(GUIO_NOLANG, GUIO_NOLAUNCHLOAD, GAMEOPTION_NO_SAVELOAD), ADGF_NO_FLAGS, 0), // Mac v4.1
+	DETECTION_ENTRY_GUIO("strangeland", "ac2game.dat", "eaad004cb2c3a2e6d5538e8f04624d2b", 3009812240, Common::UNK_LANG, "Steam", GUIO3(GUIO_NOLANG, GUIO_NOLAUNCHLOAD, GAMEOPTION_NO_SAVELOAD), ADGF_NO_FLAGS, 0), // Mac v4.2
 	GAME_ENTRY("sulifallenharmony", "Suli Fallen Harmony.exe", "8a58836830dce896e4366f57791ab6cc", 35900870),  // v1.4 itch.io En-Fr
 	GAME_ENTRY_STEAM("sumatra", "sumatra fate of yandi.exe", "57c868b1a81c0335ab60970292cd79d8", 170088886),  // En-Fr-De-Es
 	GAME_ENTRY_STEAM("sumatra", "sumatra fate of yandi.exe", "b4407ac542f316456f549fc8a60dccae", 170634789),
@@ -5572,6 +5591,7 @@ const AGSGameDescription GAME_DESCRIPTIONS[] = {
 	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("unavowed", "ac2game.dat", "f311000c476689a6e77d25f002f412db", 1617820344, 0), // Mac v1.2.2
 	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("unavowed", "ac2game.dat", "f311000c476689a6e77d25f002f412db", 1617832279, 0), // Mac v2.0.0
 	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("unavowed", "ac2game.dat", "f311000c476689a6e77d25f002f412db", 1617832846, 0), // Mac v2.0.0 hotfix
+	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("unavowed", "ac2game.dat", "f311000c476689a6e77d25f002f412db", 1617875957, 0), // Mac v2.0.0 hotfix2
 	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("unavowed", "Unavowed.exe", "b1ff7d96667707daf4266975cea2bf90", 1755451248, 0), // Windows/Linux
 	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("unavowed", "Unavowed.exe", "52c411caa3bfb65788ed8768ceaa0c30", 1756036604, 0), // Windows/Linux v0.9.0 (GOG v1.2)
 	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("unavowed", "Unavowed.exe", "52c411caa3bfb65788ed8768ceaa0c30", 1756040045, 0), // Windows?/Linux v1.0.0
@@ -5584,7 +5604,9 @@ const AGSGameDescription GAME_DESCRIPTIONS[] = {
 	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("unavowed", "unavowed.ags", "f311000c476689a6e77d25f002f412db", 1617821115, 0), // Windows/Linux v1.2.3
 	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("unavowed", "unavowed.ags", "f311000c476689a6e77d25f002f412db", 1617832279, 0), // Windows/Linux v2.0.0
 	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("unavowed", "unavowed.ags", "f311000c476689a6e77d25f002f412db", 1617832846, 0), // Windows/Linux v2.0.0 hotfix
+	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("unavowed", "unavowed.ags", "f311000c476689a6e77d25f002f412db", 1617875957, 0), // Windows/Linux v2.0.0 hotfix2
 	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("unavowed", "unavowed.ags", "f311000c476689a6e77d25f002f412db", 1617853455, 0), // Steamdeck
+	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("unavowed", "unavowed.ags", "f311000c476689a6e77d25f002f412db", 1617897835, 0), // Steamdeck
 	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("unavowed", "Unavowed.exe", "b1ff7d96667707daf4266975cea2bf90", 1755451248, 0), // Windows/Linux
 	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("unavowed", "Unavowed.exe", "52c411caa3bfb65788ed8768ceaa0c30", 1756036604, 0), // Windows/Linux v0.9.0 (GOG v1.2)
 	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("unavowed", "Unavowed.exe", "52c411caa3bfb65788ed8768ceaa0c30", 1756037170, 0), // Windows/Linux v0.9.0 (GOG v1.3)
@@ -5599,11 +5621,13 @@ const AGSGameDescription GAME_DESCRIPTIONS[] = {
 	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("unavowed", "unavowed.ags", "f311000c476689a6e77d25f002f412db", 1617821432, 0), // Windows/Linux v1.2.3 (GOG v2.3)
 	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("unavowed", "unavowed.ags", "f311000c476689a6e77d25f002f412db", 1617832596, 0), // Windows/Linux v2.0.0 (GOG v2.5)
 	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("unavowed", "unavowed.ags", "f311000c476689a6e77d25f002f412db", 1617833163, 0), // Windows/Linux v2.0.0 hotfix (GOG v2.7)
+	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("unavowed", "unavowed.ags", "f311000c476689a6e77d25f002f412db", 1617876274, 0), // Windows/Linux v2.0.0 hotfix2 (GOG v2.8)
 	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("unavowed", "ac2game.dat", "f311000c476689a6e77d25f002f412db", 1617819592, 0), // Mac v1.1.1 (GOG v2.0.2)
 	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("unavowed", "ac2game.dat", "f311000c476689a6e77d25f002f412db", 1617820192, 0), // Mac v1.2.1 (GOG v2.1-v2.3)
 	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("unavowed", "ac2game.dat", "f311000c476689a6e77d25f002f412db", 1617820661, 0), // Mac v1.2.2 (GOG v2.4)
 	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("unavowed", "ac2game.dat", "f311000c476689a6e77d25f002f412db", 1617832596, 0), // Mac v2.0.0 (GOG v2.6)
 	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("unavowed", "ac2game.dat", "f311000c476689a6e77d25f002f412db", 1617833163, 0), // Mac v2.0.0 hotfix (GOG v2.7)
+	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("unavowed", "ac2game.dat", "f311000c476689a6e77d25f002f412db", 1617876274, 0), // Mac v2.0.0 hotfix2 (GOG v2.8)
 	GAME_ENTRY_EN_STEAM("unlikelyprometheus", "The Unlikely Prometheus.exe", "a370a1db43a1d376d09e43469abba1d1", 129124983),
 	GAME_ENTRY_EN_STEAM("untilihaveyou", "until i have you.exe", "cda1d7e36993dd55ba5513c1c43e5b2b", 1072879555),
 	GAME_ENTRY_EN_STEAM("untilihaveyou", "until i have you.exe", "cda1d7e36993dd55ba5513c1c43e5b2b", 1074262526),



From noreply at scummvm.org  Sat Jun 20 06:20:03 2026
From: noreply at scummvm.org (lotharsm)
Date: Sat, 20 Jun 2026 06:20:03 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 4496634ba5059468c11c9ff5d8470653613e2674
Message-ID: <20260620062003.0BEE07005CC@ron.scummvm.net>

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

Summary:
4496634ba5 NEWS: Update German NEWS file


Commit: 4496634ba5059468c11c9ff5d8470653613e2674
    https://github.com/scummvm/scummvm/commit/4496634ba5059468c11c9ff5d8470653613e2674
Author: Lothar Serra Mari (mail at serra.me)
Date: 2026-06-20T08:19:50+02:00

Commit Message:
NEWS: Update German NEWS file

Changed paths:
    doc/de/NEUES.md


diff --git a/doc/de/NEUES.md b/doc/de/NEUES.md
index 54676164a73..7a38d285fda 100644
--- a/doc/de/NEUES.md
+++ b/doc/de/NEUES.md
@@ -17,6 +17,7 @@ Umfangreichere Informationen ??ber die ??nderungen des aktuellen Codes findest D
    - Klickbare Links in der Benutzeroberfl??che werden jetzt durch einen ver??nderten Cursor hervorgehoben.
    - Die Fenstergr????e wird jetzt korrekt wiederhergestellt, wenn in 3D-Engines von Vollbild zur??ck zum Fenster-Modus gewechselt wird.
    - NULL OPL-Treiber f??r schw??chere Systeme implementiert.
+   - Fl??ssiges Scrollen in Listen innerhalb der Benutzeroberfl??che implementiert.
 
  ACCESS:
    - Mehrere Skript-Fehler in Martian Memorandum behoben.



From noreply at scummvm.org  Sat Jun 20 08:42:12 2026
From: noreply at scummvm.org (dreammaster)
Date: Sat, 20 Jun 2026 08:42:12 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 ef625ccaf347f428592b83a27c1f89fc917600fb
Message-ID: <20260620084212.286C07005CC@ron.scummvm.net>

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:
666c8bb3fe MADS: FOREST: Digi global is actually config_file field
ef625ccaf3 MADS: FOREST: Room loading fixes


Commit: 666c8bb3fe066a7f63273348de13207a3df8e096
    https://github.com/scummvm/scummvm/commit/666c8bb3fe066a7f63273348de13207a3df8e096
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-06-20T18:41:47+10:00

Commit Message:
MADS: FOREST: Digi global is actually config_file field

Changed paths:
    engines/mads/madsv2/core/config.h
    engines/mads/madsv2/core/digi.cpp
    engines/mads/madsv2/core/digi.h
    engines/mads/madsv2/forest/main.cpp
    engines/mads/madsv2/forest/rooms/room101.cpp
    engines/mads/madsv2/forest/rooms/room103.cpp
    engines/mads/madsv2/forest/rooms/room104.cpp
    engines/mads/madsv2/forest/rooms/room106.cpp
    engines/mads/madsv2/forest/rooms/room210.cpp
    engines/mads/madsv2/forest/rooms/room221.cpp
    engines/mads/madsv2/forest/rooms/room302.cpp
    engines/mads/madsv2/forest/rooms/room303.cpp
    engines/mads/madsv2/forest/rooms/room304.cpp
    engines/mads/madsv2/forest/rooms/room305.cpp
    engines/mads/madsv2/forest/rooms/room306.cpp
    engines/mads/madsv2/forest/rooms/room307.cpp
    engines/mads/madsv2/forest/rooms/room308.cpp
    engines/mads/madsv2/forest/rooms/room321.cpp
    engines/mads/madsv2/forest/rooms/room322.cpp
    engines/mads/madsv2/forest/rooms/room401.cpp
    engines/mads/madsv2/forest/rooms/room402.cpp
    engines/mads/madsv2/forest/rooms/room403.cpp
    engines/mads/madsv2/forest/rooms/room404.cpp
    engines/mads/madsv2/forest/rooms/room405.cpp
    engines/mads/madsv2/forest/rooms/room501.cpp
    engines/mads/madsv2/forest/rooms/room503.cpp
    engines/mads/madsv2/forest/rooms/room510.cpp
    engines/mads/madsv2/forest/rooms/room901.cpp
    engines/mads/madsv2/forest/rooms/room903.cpp
    engines/mads/madsv2/forest/rooms/room904.cpp


diff --git a/engines/mads/madsv2/core/config.h b/engines/mads/madsv2/core/config.h
index 5dada0d05fc..0492814a5c2 100644
--- a/engines/mads/madsv2/core/config.h
+++ b/engines/mads/madsv2/core/config.h
@@ -96,10 +96,12 @@ struct ConfigFile {
 	int show_speech_boxes;        /* Show text during speech       */
 
 	int sound_card_irq;
-	bool original_save_load;
 	int misc3;
 	int misc4;
 	int misc5;
+	int forest1;
+
+	bool original_save_load;
 };
 
 extern ConfigFile config_file;
diff --git a/engines/mads/madsv2/core/digi.cpp b/engines/mads/madsv2/core/digi.cpp
index 75a9d8c2208..c598d28ae94 100644
--- a/engines/mads/madsv2/core/digi.cpp
+++ b/engines/mads/madsv2/core/digi.cpp
@@ -20,18 +20,18 @@
  */
 
 #include "mads/madsv2/core/digi.h"
+#include "mads/madsv2/core/config.h"
 #include "mads/madsv2/engine.h"
 
 namespace MADS {
 namespace MADSV2 {
 
-int digi_val1;
 int digi_val2;
 int digi_timing_index;
 bool digi_flag1, digi_flag2;
 
 void digi_install() {
-	digi_val1 = 0;
+	config_file.forest1 = 0;
 	digi_val2 = 0;
 	digi_timing_index = 0;
 }
diff --git a/engines/mads/madsv2/core/digi.h b/engines/mads/madsv2/core/digi.h
index ad0bb273ba8..f212c2f334c 100644
--- a/engines/mads/madsv2/core/digi.h
+++ b/engines/mads/madsv2/core/digi.h
@@ -27,7 +27,7 @@
 namespace MADS {
 namespace MADSV2 {
 
-extern int digi_val1;
+//extern int config_file.forest1;
 extern int digi_val2;
 extern int digi_timing_index;
 extern bool digi_flag1, digi_flag2;
diff --git a/engines/mads/madsv2/forest/main.cpp b/engines/mads/madsv2/forest/main.cpp
index eac24e274b3..96601a09f6f 100644
--- a/engines/mads/madsv2/forest/main.cpp
+++ b/engines/mads/madsv2/forest/main.cpp
@@ -52,6 +52,7 @@ constexpr byte LINE_COLOR = 2;
 char *quotes;
 
 static void main_menu_main() {
+#ifdef TODO
 	auto &screen = *g_engine->getScreen();
 	Palette palette;
 
@@ -113,6 +114,9 @@ static void main_menu_main() {
 	}
 
 	mcga_reset();
+#else
+	selected_item = 0;
+#endif
 }
 
 static void main_cold_data_init() {
diff --git a/engines/mads/madsv2/forest/rooms/room101.cpp b/engines/mads/madsv2/forest/rooms/room101.cpp
index b8d0b5abb75..45a43b03e8a 100644
--- a/engines/mads/madsv2/forest/rooms/room101.cpp
+++ b/engines/mads/madsv2/forest/rooms/room101.cpp
@@ -19,7 +19,6 @@
  *
  */
 
-#include "mads/madsv2/core/conv.h"
 #include "mads/madsv2/core/digi.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/imath.h"
diff --git a/engines/mads/madsv2/forest/rooms/room103.cpp b/engines/mads/madsv2/forest/rooms/room103.cpp
index 999ad6a4f85..89426005151 100644
--- a/engines/mads/madsv2/forest/rooms/room103.cpp
+++ b/engines/mads/madsv2/forest/rooms/room103.cpp
@@ -19,7 +19,6 @@
  *
  */
 
-#include "mads/madsv2/core/conv.h"
 #include "mads/madsv2/core/digi.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/imath.h"
diff --git a/engines/mads/madsv2/forest/rooms/room104.cpp b/engines/mads/madsv2/forest/rooms/room104.cpp
index 4fec105fb1f..7191b997870 100644
--- a/engines/mads/madsv2/forest/rooms/room104.cpp
+++ b/engines/mads/madsv2/forest/rooms/room104.cpp
@@ -19,7 +19,6 @@
  *
  */
 
-#include "mads/madsv2/core/conv.h"
 #include "mads/madsv2/core/digi.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/imath.h"
diff --git a/engines/mads/madsv2/forest/rooms/room106.cpp b/engines/mads/madsv2/forest/rooms/room106.cpp
index 9f6a92ade9e..e54b918ce02 100644
--- a/engines/mads/madsv2/forest/rooms/room106.cpp
+++ b/engines/mads/madsv2/forest/rooms/room106.cpp
@@ -19,7 +19,7 @@
  *
  */
 
-#include "mads/madsv2/core/conv.h"
+#include "mads/madsv2/core/config.h"
 #include "mads/madsv2/core/digi.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/imath.h"
@@ -255,7 +255,7 @@ static void room_106_anim1() {
 
 	if (kernel.trigger != 7 && kernel.trigger != 28)
 		return;
-	if (digi_val1 != 0)
+	if (config_file.forest1 != 0)
 		kernel_timing_trigger(1, 109);
 	if (scratch._ae == 114) { scratch._ae = -1; return; }
 	if (scratch._ae > 114)  return;
diff --git a/engines/mads/madsv2/forest/rooms/room210.cpp b/engines/mads/madsv2/forest/rooms/room210.cpp
index f1ec570a629..abea8109c55 100644
--- a/engines/mads/madsv2/forest/rooms/room210.cpp
+++ b/engines/mads/madsv2/forest/rooms/room210.cpp
@@ -19,6 +19,7 @@
  *
  */
 
+#include "mads/madsv2/core/config.h"
 #include "mads/madsv2/core/digi.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/imath.h"
@@ -385,13 +386,13 @@ static void room_210_daemon() {
 		case 3:
 			aainfo[8]._frame = 12;
 			kernel_reset_animation(aa[8], 12);
-			if (digi_val1 != 0) kernel_timing_trigger(1, 109);
+			if (config_file.forest1 != 0) kernel_timing_trigger(1, 109);
 			break;
 		case 4:
 			global[walker_converse_state] = 0;
 			close_journal(3);
 			player.commands_allowed = -1;
-			if (digi_val1 != 0) kernel_timing_trigger(1, 109);
+			if (config_file.forest1 != 0) kernel_timing_trigger(1, 109);
 			break;
 		}
 		scratch._a6 = 0;
diff --git a/engines/mads/madsv2/forest/rooms/room221.cpp b/engines/mads/madsv2/forest/rooms/room221.cpp
index 062d5ff6244..503d72a44d7 100644
--- a/engines/mads/madsv2/forest/rooms/room221.cpp
+++ b/engines/mads/madsv2/forest/rooms/room221.cpp
@@ -19,7 +19,6 @@
  *
  */
 
-#include "mads/madsv2/core/conv.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/imath.h"
 #include "mads/madsv2/core/inter.h"
diff --git a/engines/mads/madsv2/forest/rooms/room302.cpp b/engines/mads/madsv2/forest/rooms/room302.cpp
index 2e86c40f7cc..ee3db098fd5 100644
--- a/engines/mads/madsv2/forest/rooms/room302.cpp
+++ b/engines/mads/madsv2/forest/rooms/room302.cpp
@@ -19,7 +19,6 @@
  *
  */
 
-#include "mads/madsv2/core/conv.h"
 #include "mads/madsv2/core/digi.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/imath.h"
diff --git a/engines/mads/madsv2/forest/rooms/room303.cpp b/engines/mads/madsv2/forest/rooms/room303.cpp
index ba06a7eeb61..c6f8028e090 100644
--- a/engines/mads/madsv2/forest/rooms/room303.cpp
+++ b/engines/mads/madsv2/forest/rooms/room303.cpp
@@ -19,7 +19,6 @@
  *
  */
 
-#include "mads/madsv2/core/conv.h"
 #include "mads/madsv2/core/digi.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/imath.h"
diff --git a/engines/mads/madsv2/forest/rooms/room304.cpp b/engines/mads/madsv2/forest/rooms/room304.cpp
index e024a8e17ba..70235ac4f5e 100644
--- a/engines/mads/madsv2/forest/rooms/room304.cpp
+++ b/engines/mads/madsv2/forest/rooms/room304.cpp
@@ -19,7 +19,7 @@
  *
  */
 
-#include "mads/madsv2/core/conv.h"
+#include "mads/madsv2/core/config.h"
 #include "mads/madsv2/core/digi.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/imath.h"
@@ -133,7 +133,7 @@ static void room_304_init() {
 
 static void room_304_finish() {
 	player.commands_allowed = -1;
-	if (digi_val1)
+	if (config_file.forest1)
 		kernel_timing_trigger(1, 107);
 }
 
diff --git a/engines/mads/madsv2/forest/rooms/room305.cpp b/engines/mads/madsv2/forest/rooms/room305.cpp
index 32a89cc1c49..468ae49e6e6 100644
--- a/engines/mads/madsv2/forest/rooms/room305.cpp
+++ b/engines/mads/madsv2/forest/rooms/room305.cpp
@@ -19,6 +19,7 @@
  *
  */
 
+#include "mads/madsv2/core/config.h"
 #include "mads/madsv2/core/digi.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/imath.h"
@@ -747,7 +748,7 @@ static void room_305_anim10() {
 			kernel_abort_animation(aa[9]);
 			aainfo[9]._active = 0;
 			player.commands_allowed = -1;
-			if (digi_val1 != 0)
+			if (config_file.forest1 != 0)
 				kernel_timing_trigger(1, 109);
 			global[g131] = -1;
 			kernel_reset_animation(scratch._9a, 1);
@@ -780,7 +781,7 @@ static void room_305_daemon() {
 			global[walker_converse_state] = 0;
 			close_journal(3);
 			player.commands_allowed = -1;
-			if (digi_val1 != 0)
+			if (config_file.forest1 != 0)
 				kernel_timing_trigger(1, 109);
 		} else {
 			if (scratch._a4 == 300) {
@@ -876,7 +877,7 @@ static void room_305_daemon() {
 		kernel_synch(KERNEL_ANIM, scratch._9a, KERNEL_NOW, 0);
 		global[g133] = 0;
 		player.commands_allowed = -1;
-		if (digi_val1 != 0)
+		if (config_file.forest1 != 0)
 			kernel_timing_trigger(1, 109);
 		break;
 
@@ -899,7 +900,7 @@ static void room_305_daemon() {
 			global[g143] = 0;
 			kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
 			player.commands_allowed = -1;
-			if (digi_val1 != 0)
+			if (config_file.forest1 != 0)
 				kernel_timing_trigger(1, 109);
 		}
 		break;
@@ -912,7 +913,7 @@ static void room_305_daemon() {
 		kernel_synch(KERNEL_ANIM, scratch._9c, KERNEL_NOW, 0);
 		global[g143] = 0;
 		player.commands_allowed = -1;
-		if (digi_val1 != 0)
+		if (config_file.forest1 != 0)
 			kernel_timing_trigger(1, 109);
 		break;
 	}
@@ -938,7 +939,7 @@ static void room_305_daemon() {
 			global[g069] = -1;
 			global[player_score] = -1;
 			player.commands_allowed = -1;
-			if (digi_val1 != 0)
+			if (config_file.forest1 != 0)
 				kernel_timing_trigger(1, 109);
 		}
 		break;
@@ -988,7 +989,7 @@ static void room_305_daemon() {
 		global[g143] = 0;
 		kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
 		player.commands_allowed = -1;
-		if (digi_val1 != 0)
+		if (config_file.forest1 != 0)
 			kernel_timing_trigger(1, 109);
 		break;
 
@@ -1000,7 +1001,7 @@ static void room_305_daemon() {
 		global[g133] = 0;
 		global[player_score] = -1;
 		player.commands_allowed = -1;
-		if (digi_val1 != 0)
+		if (config_file.forest1 != 0)
 			kernel_timing_trigger(1, 109);
 		break;
 
diff --git a/engines/mads/madsv2/forest/rooms/room306.cpp b/engines/mads/madsv2/forest/rooms/room306.cpp
index 8d44e900d4b..72858656bac 100644
--- a/engines/mads/madsv2/forest/rooms/room306.cpp
+++ b/engines/mads/madsv2/forest/rooms/room306.cpp
@@ -19,7 +19,7 @@
  *
  */
 
-#include "mads/madsv2/core/conv.h"
+#include "mads/madsv2/core/config.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/imath.h"
 #include "mads/madsv2/core/inter.h"
@@ -687,7 +687,7 @@ static void room_306_daemon() {
 			global[walker_converse_state] = 0;
 			close_journal(3);
 			player.commands_allowed = -1;
-			if (digi_val1 != 0) {
+			if (config_file.forest1 != 0) {
 				if (global[g066] == 3) {
 					kernel_timing_trigger(1, 118);
 				} else {
@@ -710,7 +710,7 @@ static void room_306_daemon() {
 				kernel_synch(KERNEL_ANIM, scratch.x9a, KERNEL_NOW, 0);
 				global[g133] = 0;
 				player.commands_allowed = -1;
-				if (digi_val1 != 0) {
+				if (config_file.forest1 != 0) {
 					if (global[g066] == 3) {
 						kernel_timing_trigger(1, 118);
 					} else {
@@ -734,7 +734,7 @@ static void room_306_daemon() {
 				scratch.xa2 = 0;
 				scratch.x9e = -1;
 				player.commands_allowed = -1;
-				if (digi_val1 != 0) {
+				if (config_file.forest1 != 0) {
 					if (global[g066] == 3) {
 						kernel_timing_trigger(1, 118);
 					} else {
@@ -792,7 +792,7 @@ static void room_306_daemon() {
 	case 100:
 		kernel_abort_animation(aa[0]);
 		aainfo[0]._active = 0;
-		if (digi_val1 != 0) {
+		if (config_file.forest1 != 0) {
 			if (global[g066] == 3) {
 				kernel_timing_trigger(1, 118);
 			} else {
@@ -820,7 +820,7 @@ static void room_306_daemon() {
 		kernel_synch(KERNEL_ANIM, scratch.x9c, KERNEL_NOW, 0);
 		global[g143] = 0;
 		player.commands_allowed = -1;
-		if (digi_val1 != 0) {
+		if (config_file.forest1 != 0) {
 			if (global[g066] == 3) {
 				kernel_timing_trigger(1, 118);
 			} else {
@@ -975,7 +975,7 @@ static void room_306_parser() {
 		digi_play_build(306, 'b', 1, 1);
 		scratch.x9e = 2500;
 		scratch.xa2 = -1;
-		if (digi_val1 == 0) {
+		if (config_file.forest1 == 0) {
 			kernel.trigger_setup_mode = 1;
 			kernel_timing_trigger(1, 117);
 		}
diff --git a/engines/mads/madsv2/forest/rooms/room307.cpp b/engines/mads/madsv2/forest/rooms/room307.cpp
index fbac29ef51b..122ff122079 100644
--- a/engines/mads/madsv2/forest/rooms/room307.cpp
+++ b/engines/mads/madsv2/forest/rooms/room307.cpp
@@ -19,7 +19,7 @@
  *
  */
 
-#include "mads/madsv2/core/conv.h"
+#include "mads/madsv2/core/config.h"
 #include "mads/madsv2/core/digi.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/imath.h"
@@ -321,7 +321,7 @@ static void room_307_anim5() {
 		scratch._aa++;
 		global[g156] = 2;
 		digi_play_build(307, 'E', 2, 1);
-		if (digi_val1 == 0)
+		if (config_file.forest1 == 0)
 			digi_play_build(307, '_', 1, 2);
 		break;
 	case 20:
diff --git a/engines/mads/madsv2/forest/rooms/room308.cpp b/engines/mads/madsv2/forest/rooms/room308.cpp
index b25277516da..ec247be676f 100644
--- a/engines/mads/madsv2/forest/rooms/room308.cpp
+++ b/engines/mads/madsv2/forest/rooms/room308.cpp
@@ -19,6 +19,7 @@
  *
  */
 
+#include "mads/madsv2/core/config.h"
 #include "mads/madsv2/core/digi.h"
 #include "mads/madsv2/forest/extra.h"
 #include "mads/madsv2/forest/journal.h"
@@ -178,7 +179,7 @@ static void room_308_anim4() {
 
 	bool in_e72 = (cur == 1 || cur == 5) ||
 	              (cur >= 10 && cur <= 58 && cur % 4 == 2);
-	if (in_e72 && (digi_val1 == 0 || (cur != 10 && cur != 14))) {
+	if (in_e72 && (config_file.forest1 == 0 || (cur != 10 && cur != 14))) {
 		digi_initial_volume(60);
 		digi_play_build(308, '_', 1, 2);
 	}
diff --git a/engines/mads/madsv2/forest/rooms/room321.cpp b/engines/mads/madsv2/forest/rooms/room321.cpp
index 2ce5ff1a7b7..bbf9024206f 100644
--- a/engines/mads/madsv2/forest/rooms/room321.cpp
+++ b/engines/mads/madsv2/forest/rooms/room321.cpp
@@ -19,7 +19,6 @@
  *
  */
 
-#include "mads/madsv2/core/conv.h"
 #include "mads/madsv2/core/digi.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/imath.h"
diff --git a/engines/mads/madsv2/forest/rooms/room322.cpp b/engines/mads/madsv2/forest/rooms/room322.cpp
index 863509384a3..6fbecfcff86 100644
--- a/engines/mads/madsv2/forest/rooms/room322.cpp
+++ b/engines/mads/madsv2/forest/rooms/room322.cpp
@@ -19,7 +19,6 @@
  *
  */
 
-#include "mads/madsv2/core/conv.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/imath.h"
 #include "mads/madsv2/core/inter.h"
diff --git a/engines/mads/madsv2/forest/rooms/room401.cpp b/engines/mads/madsv2/forest/rooms/room401.cpp
index 433b39a8ff7..f2c9c3c9766 100644
--- a/engines/mads/madsv2/forest/rooms/room401.cpp
+++ b/engines/mads/madsv2/forest/rooms/room401.cpp
@@ -19,7 +19,7 @@
  *
  */
 
-#include "mads/madsv2/core/conv.h"
+#include "mads/madsv2/core/config.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/imath.h"
 #include "mads/madsv2/core/inter.h"
@@ -107,7 +107,7 @@ static void room_401_anim_state(int16 state) {
 		kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
 		player.commands_allowed = -1;
 		player.walker_visible = -1;
-		if (digi_val1) {
+		if (config_file.forest1) {
 			digi_stop(1);
 			kernel_timing_trigger(1, 106);
 		}
@@ -230,7 +230,7 @@ static void room_401_anim2() {
 					} else if (frame == 32) {
 						result = 29;
 					} else if (frame == 35) {
-						if (digi_val1)
+						if (config_file.forest1)
 							kernel_timing_trigger(1, 106);
 					}
 				}
@@ -472,7 +472,7 @@ static void room_401_daemon() {
 		global[walker_converse_state] = 0;
 		close_journal(3);
 		player.commands_allowed = -1;
-		if (digi_val1)
+		if (config_file.forest1)
 			kernel_timing_trigger(1, 106);
 		break;
 
diff --git a/engines/mads/madsv2/forest/rooms/room402.cpp b/engines/mads/madsv2/forest/rooms/room402.cpp
index f8fec0ca4fa..cbee1bfb2c0 100644
--- a/engines/mads/madsv2/forest/rooms/room402.cpp
+++ b/engines/mads/madsv2/forest/rooms/room402.cpp
@@ -19,7 +19,6 @@
  *
  */
 
-#include "mads/madsv2/core/conv.h"
 #include "mads/madsv2/core/digi.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/imath.h"
diff --git a/engines/mads/madsv2/forest/rooms/room403.cpp b/engines/mads/madsv2/forest/rooms/room403.cpp
index c0699dca9c4..d777d24502b 100644
--- a/engines/mads/madsv2/forest/rooms/room403.cpp
+++ b/engines/mads/madsv2/forest/rooms/room403.cpp
@@ -19,7 +19,7 @@
  *
  */
 
-#include "mads/madsv2/core/conv.h"
+#include "mads/madsv2/core/config.h"
 #include "mads/madsv2/core/digi.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/imath.h"
@@ -463,7 +463,7 @@ static void room_403_daemon() {
 			global[walker_converse_state] = 0;
 			close_journal(3);
 			player.commands_allowed = -1;
-			if (digi_val1 != 0)
+			if (config_file.forest1 != 0)
 				kernel_timing_trigger(1, 105);
 		} else if (scratch._a4 == 300) {
 			kernel_abort_animation(scratch._9e);
@@ -567,7 +567,7 @@ static void room_403_daemon() {
 		kernel_synch(KERNEL_ANIM, scratch._9a, KERNEL_NOW, 0);
 		global[g133] = 0;
 		player.commands_allowed = -1;
-		if (digi_val1 != 0)
+		if (config_file.forest1 != 0)
 			kernel_timing_trigger(1, 105);
 		break;
 
@@ -585,7 +585,7 @@ static void room_403_daemon() {
 		global[g143] = 0;
 		kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
 		player.commands_allowed = -1;
-		if (digi_val1 != 0)
+		if (config_file.forest1 != 0)
 			kernel_timing_trigger(1, 105);
 		break;
 
@@ -596,7 +596,7 @@ static void room_403_daemon() {
 		kernel_synch(KERNEL_ANIM, scratch._9c, KERNEL_NOW, 0);
 		global[g143] = 0;
 		player.commands_allowed = -1;
-		if (digi_val1 != 0)
+		if (config_file.forest1 != 0)
 			kernel_timing_trigger(1, 105);
 		break;
 
@@ -607,7 +607,7 @@ static void room_403_daemon() {
 		kernel_synch(KERNEL_ANIM, scratch._9a, KERNEL_NOW, 0);
 		global[g133] = 0;
 		player.commands_allowed = -1;
-		if (digi_val1 != 0)
+		if (config_file.forest1 != 0)
 			kernel_timing_trigger(1, 105);
 		break;
 
@@ -625,7 +625,7 @@ static void room_403_daemon() {
 			inter_move_object(13, 2);
 			display_interface();
 			player.commands_allowed = -1;
-			if (digi_val1 != 0)
+			if (config_file.forest1 != 0)
 				kernel_timing_trigger(1, 105);
 		} else if (scratch._a2 == 2) {
 			kernel_abort_animation(aa[5]);
@@ -640,7 +640,7 @@ static void room_403_daemon() {
 			kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
 			kernel_flip_hotspot(95, 0);
 			player.commands_allowed = -1;
-			if (digi_val1 != 0)
+			if (config_file.forest1 != 0)
 				kernel_timing_trigger(1, 105);
 		}
 		break;
diff --git a/engines/mads/madsv2/forest/rooms/room404.cpp b/engines/mads/madsv2/forest/rooms/room404.cpp
index 23747b6f220..b17a9c55636 100644
--- a/engines/mads/madsv2/forest/rooms/room404.cpp
+++ b/engines/mads/madsv2/forest/rooms/room404.cpp
@@ -19,7 +19,7 @@
  *
  */
 
-#include "mads/madsv2/core/conv.h"
+#include "mads/madsv2/core/config.h"
 #include "mads/madsv2/core/digi.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/imath.h"
@@ -518,7 +518,7 @@ static void room_404_daemon() {
 			global[walker_converse_state] = 0;
 			close_journal(3);
 			player.commands_allowed = -1;
-			if (digi_val1)
+			if (config_file.forest1)
 				kernel_timing_trigger(1, 110);
 		} else if (scratch._a6 == 400) {
 			kernel_abort_animation(scratch._9e);
@@ -527,7 +527,7 @@ static void room_404_daemon() {
 			kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
 			player.walker_visible = -1;
 			player.commands_allowed = -1;
-			if (digi_val1)
+			if (config_file.forest1)
 				kernel_timing_trigger(1, 110);
 		}
 		break;
@@ -595,7 +595,7 @@ static void room_404_daemon() {
 		kernel_synch(KERNEL_ANIM, scratch._9a, KERNEL_NOW, 0);
 		global[g133] = 0;
 		player.commands_allowed = -1;
-		if (digi_val1)
+		if (config_file.forest1)
 			kernel_timing_trigger(1, 110);
 		break;
 
@@ -622,7 +622,7 @@ static void room_404_daemon() {
 				global[g143] = 0;
 				kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
 				player.commands_allowed = -1;
-				if (digi_val1)
+				if (config_file.forest1)
 					kernel_timing_trigger(1, 110);
 			}
 		} else if (previous_room == 405) {
@@ -637,7 +637,7 @@ static void room_404_daemon() {
 			global[g143] = 0;
 			kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
 			player.commands_allowed = -1;
-			if (digi_val1)
+			if (config_file.forest1)
 				kernel_timing_trigger(1, 110);
 		}
 		break;
@@ -649,7 +649,7 @@ static void room_404_daemon() {
 		kernel_synch(KERNEL_ANIM, scratch._9c, KERNEL_NOW, 0);
 		global[g143] = 0;
 		player.commands_allowed = -1;
-		if (digi_val1)
+		if (config_file.forest1)
 			kernel_timing_trigger(1, 110);
 		break;
 
@@ -660,7 +660,7 @@ static void room_404_daemon() {
 		kernel_synch(KERNEL_ANIM, scratch._9a, KERNEL_NOW, 0);
 		global[g133] = 0;
 		player.commands_allowed = -1;
-		if (digi_val1)
+		if (config_file.forest1)
 			kernel_timing_trigger(1, 110);
 		break;
 
@@ -718,7 +718,7 @@ static void room_404_daemon() {
 				close_journal(3);
 			}
 			player.commands_allowed = -1;
-			if (digi_val1)
+			if (config_file.forest1)
 				kernel_timing_trigger(1, 110);
 		}
 		break;
@@ -760,7 +760,7 @@ static void room_404_daemon() {
 			global[g143] = 0;
 			kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
 			player.commands_allowed = -1;
-			if (digi_val1)
+			if (config_file.forest1)
 				kernel_timing_trigger(1, 110);
 		}
 		break;
diff --git a/engines/mads/madsv2/forest/rooms/room405.cpp b/engines/mads/madsv2/forest/rooms/room405.cpp
index 35a122ffda8..31e712d124f 100644
--- a/engines/mads/madsv2/forest/rooms/room405.cpp
+++ b/engines/mads/madsv2/forest/rooms/room405.cpp
@@ -19,7 +19,7 @@
  *
  */
 
-#include "mads/madsv2/core/conv.h"
+#include "mads/madsv2/core/config.h"
 #include "mads/madsv2/core/digi.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/imath.h"
@@ -281,7 +281,7 @@ static void room_405_daemon() {
 			global[walker_converse_state] = 0;
 			close_journal(3);
 			player.commands_allowed = -1;
-			if (digi_val1)
+			if (config_file.forest1)
 				kernel_timing_trigger(1, 106);
 		}
 		break;
@@ -341,7 +341,7 @@ static void room_405_daemon() {
 		kernel_synch(KERNEL_ANIM, scratch._9a, KERNEL_NOW, 0);
 		global[g133] = 0;
 		player.commands_allowed = -1;
-		if (digi_val1)
+		if (config_file.forest1)
 			kernel_timing_trigger(1, 106);
 		break;
 
@@ -366,7 +366,7 @@ static void room_405_daemon() {
 			global[g143] = 0;
 			kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
 			player.commands_allowed = -1;
-			if (digi_val1)
+			if (config_file.forest1)
 				kernel_timing_trigger(1, 106);
 		}
 		break;
@@ -378,7 +378,7 @@ static void room_405_daemon() {
 		kernel_synch(KERNEL_ANIM, scratch._9c, KERNEL_NOW, 0);
 		global[g143] = 0;
 		player.commands_allowed = -1;
-		if (digi_val1)
+		if (config_file.forest1)
 			kernel_timing_trigger(1, 106);
 		break;
 
@@ -389,7 +389,7 @@ static void room_405_daemon() {
 		kernel_synch(KERNEL_ANIM, scratch._9a, KERNEL_NOW, 0);
 		global[g133] = 0;
 		player.commands_allowed = -1;
-		if (digi_val1)
+		if (config_file.forest1)
 			kernel_timing_trigger(1, 106);
 		break;
 
@@ -407,7 +407,7 @@ static void room_405_daemon() {
 			kernel_synch(KERNEL_PLAYER, 0, KERNEL_NOW, 0);
 			global[g082] = 1;
 			player.commands_allowed = -1;
-			if (digi_val1)
+			if (config_file.forest1)
 				kernel_timing_trigger(1, 106);
 		}
 		break;
diff --git a/engines/mads/madsv2/forest/rooms/room501.cpp b/engines/mads/madsv2/forest/rooms/room501.cpp
index 8b8556d43f1..e8ed6ca0d89 100644
--- a/engines/mads/madsv2/forest/rooms/room501.cpp
+++ b/engines/mads/madsv2/forest/rooms/room501.cpp
@@ -19,7 +19,6 @@
  *
  */
 
-#include "mads/madsv2/core/conv.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/imath.h"
 #include "mads/madsv2/core/inter.h"
diff --git a/engines/mads/madsv2/forest/rooms/room503.cpp b/engines/mads/madsv2/forest/rooms/room503.cpp
index e13cf1f3c89..26ed0aa44e7 100644
--- a/engines/mads/madsv2/forest/rooms/room503.cpp
+++ b/engines/mads/madsv2/forest/rooms/room503.cpp
@@ -19,7 +19,6 @@
  *
  */
 
-#include "mads/madsv2/core/conv.h"
 #include "mads/madsv2/core/digi.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/imath.h"
diff --git a/engines/mads/madsv2/forest/rooms/room510.cpp b/engines/mads/madsv2/forest/rooms/room510.cpp
index 2fcaf671ffa..5627f2ef5cc 100644
--- a/engines/mads/madsv2/forest/rooms/room510.cpp
+++ b/engines/mads/madsv2/forest/rooms/room510.cpp
@@ -19,6 +19,7 @@
  *
  */
 
+#include "mads/madsv2/core/config.h"
 #include "mads/madsv2/core/digi.h"
 #include "mads/madsv2/core/font.h"
 #include "mads/madsv2/core/game.h"
@@ -174,7 +175,7 @@ static void room_510_anim2() {
 			digi_play_build(510, 'e', 1, 1);
 			scratch._8e = 63;
 		} else if (aa_frame == 131) {
-			if (digi_val1)
+			if (config_file.forest1)
 				digi_stop(1);
 			scratch._90 = 133;
 			digi_play_build(510, '_', 3, 2);
diff --git a/engines/mads/madsv2/forest/rooms/room901.cpp b/engines/mads/madsv2/forest/rooms/room901.cpp
index 857d70d737a..9dc094624f7 100644
--- a/engines/mads/madsv2/forest/rooms/room901.cpp
+++ b/engines/mads/madsv2/forest/rooms/room901.cpp
@@ -20,7 +20,6 @@
  */
 
 #include "common/config-manager.h"
-#include "mads/madsv2/core/conv.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/kernel.h"
 #include "mads/madsv2/core/matte.h"
diff --git a/engines/mads/madsv2/forest/rooms/room903.cpp b/engines/mads/madsv2/forest/rooms/room903.cpp
index affb572a887..6d17b2af68b 100644
--- a/engines/mads/madsv2/forest/rooms/room903.cpp
+++ b/engines/mads/madsv2/forest/rooms/room903.cpp
@@ -19,7 +19,6 @@
  *
  */
 
-#include "mads/madsv2/core/conv.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/imath.h"
 #include "mads/madsv2/core/inter.h"
diff --git a/engines/mads/madsv2/forest/rooms/room904.cpp b/engines/mads/madsv2/forest/rooms/room904.cpp
index f926ac2abe5..5414c294812 100644
--- a/engines/mads/madsv2/forest/rooms/room904.cpp
+++ b/engines/mads/madsv2/forest/rooms/room904.cpp
@@ -19,7 +19,6 @@
  *
  */
 
-#include "mads/madsv2/core/conv.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/imath.h"
 #include "mads/madsv2/core/inter.h"


Commit: ef625ccaf347f428592b83a27c1f89fc917600fb
    https://github.com/scummvm/scummvm/commit/ef625ccaf347f428592b83a27c1f89fc917600fb
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-06-20T18:41:47+10:00

Commit Message:
MADS: FOREST: Room loading fixes

Changed paths:
    engines/mads/madsv2/core/kernel.cpp
    engines/mads/madsv2/core/sound.cpp


diff --git a/engines/mads/madsv2/core/kernel.cpp b/engines/mads/madsv2/core/kernel.cpp
index 401776fde6e..94adbca3805 100644
--- a/engines/mads/madsv2/core/kernel.cpp
+++ b/engines/mads/madsv2/core/kernel.cpp
@@ -688,7 +688,7 @@ int kernel_room_startup(int newRoom, int initial_variant, const char *interface,
 
 	pal_activate_shadow(&kernel_shadow_inter);
 
-	if (g_engine->getGameID() == GType_Dragonsphere) {
+	if (g_engine->getGameID() == GType_Dragonsphere || g_engine->getGameID() == GType_Forest) {
 		inter_anim = nullptr;
 
 	} else {
@@ -2555,12 +2555,14 @@ void kernel_unload_sound_driver() {
 }
 
 int kernel_load_sound_driver(const char *name, char sound_card_, int sound_board_address_, int sound_board_type_, int sound_board_irq_) {
-	// Get the section number from the end of the driver filename, and use it to initialize
-	// the sound system; we provide our own implementation of the drivers
-	int sectionNum = *(name + strlen(name) - 1) - '0';
-	assert(sectionNum >= 1 && sectionNum <= 9);
+	if (g_engine->_soundManager != nullptr) {
+		// Get the section number from the end of the driver filename, and use it to initialize
+		// the sound system; we provide our own implementation of the drivers
+		int sectionNum = *(name + strlen(name) - 1) - '0';
+		assert(sectionNum >= 1 && sectionNum <= 9);
 
-	g_engine->_soundManager->init(sectionNum);
+		g_engine->_soundManager->init(sectionNum);
+	}
 
 	return 0;
 }
diff --git a/engines/mads/madsv2/core/sound.cpp b/engines/mads/madsv2/core/sound.cpp
index 2eb92631ba4..8d016049df0 100644
--- a/engines/mads/madsv2/core/sound.cpp
+++ b/engines/mads/madsv2/core/sound.cpp
@@ -31,15 +31,17 @@ int sound_play(int soundNum) {
 }
 
 int sound_queue(int soundNum, int /*distance*/) {
-	return g_engine->_soundManager->command(soundNum);
+	return (g_engine->_soundManager) ? g_engine->_soundManager->command(soundNum) : 0;
 }
 
 void sound_queue_hold() {
-	g_engine->_soundManager->pauseNewCommands();
+	if (g_engine->_soundManager)
+		g_engine->_soundManager->pauseNewCommands();
 }
 
 void sound_queue_flush() {
-	g_engine->_soundManager->startQueuedCommands();
+	if (g_engine->_soundManager)
+		g_engine->_soundManager->startQueuedCommands();
 }
 
 } // namespace MADSV2



From noreply at scummvm.org  Sat Jun 20 09:56:28 2026
From: noreply at scummvm.org (bluegr)
Date: Sat, 20 Jun 2026 09:56:28 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 1d800f4799a1b024468bd5cae8d403f741c7cfa4
Message-ID: <20260620095628.B1A2D7005CC@ron.scummvm.net>

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

Summary:
1d800f4799 NANCY: Implement PlayRandomMovieControl for Nancy 11


Commit: 1d800f4799a1b024468bd5cae8d403f741c7cfa4
    https://github.com/scummvm/scummvm/commit/1d800f4799a1b024468bd5cae8d403f741c7cfa4
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-06-20T12:56:19+03:00

Commit Message:
NANCY: Implement PlayRandomMovieControl for Nancy 11

Now, conversations with characters exit correctly

Changed paths:
    engines/nancy/action/secondarymovie.cpp
    engines/nancy/action/secondarymovie.h
    engines/nancy/commontypes.cpp
    engines/nancy/commontypes.h


diff --git a/engines/nancy/action/secondarymovie.cpp b/engines/nancy/action/secondarymovie.cpp
index 8ac8ae5a835..9e450a4e1e8 100644
--- a/engines/nancy/action/secondarymovie.cpp
+++ b/engines/nancy/action/secondarymovie.cpp
@@ -376,20 +376,17 @@ void PlaySecondaryMovie::execute() {
 // --- PlayRandomMovieControl --------------------------------------------
 
 void PlayRandomMovieControl::readData(Common::SeekableReadStream &stream) {
-	_targetA = stream.readUint32LE();
-	_targetB = stream.readUint32LE();
-	_targetC = stream.readUint32LE();
-	_flagsOrIndex = stream.readUint16LE();
-	_trailing = stream.readByte();
+	_mode = stream.readByte();
+	_sceneChange.readData(stream, true, true);
 }
 
 void PlayRandomMovieControl::execute() {
-	// Original binary debug string:
-	//   "STOP AT_PLAY_RANDOM_MOVIE AR %d in Scene %d"
-	// so two of the chunk fields identify the target AR / scene.
-	// Until the exact identification scheme is known, this stub just
-	// warns and completes.
-	warning("PlayRandomMovieControl is not yet fully implemented");
+	//PlaySecondaryMovie *target = NancySceneState.getActiveMovie();
+	//if (target && target->_isRandom) {
+	//	target->stopRandom();
+	//}
+
+	_sceneChange.execute();
 	finishExecution();
 }
 
diff --git a/engines/nancy/action/secondarymovie.h b/engines/nancy/action/secondarymovie.h
index b68e47502d0..0c8f35ad3e7 100644
--- a/engines/nancy/action/secondarymovie.h
+++ b/engines/nancy/action/secondarymovie.h
@@ -137,14 +137,8 @@ protected:
 };
 
 // Companion AR for the random-movie variant of PlaySecondaryMovie. When
-// executed it locates the matching PlaySecondaryMovie(isRandom=true)
-// instance (by sequence/AR name) and stops it. First seen in Nancy 11
-// (AR 46, AT_PLAY_RANDOM_MOVIE_CONTROL).
-//
-// Chunk layout: fixed 15 bytes (3 ?? uint32 + uint16 + uint8). The exact
-// meaning of each field is not yet reverse-engineered; the runtime debug
-// string is "STOP AT_PLAY_RANDOM_MOVIE AR %d in Scene %d", so at least two
-// of these fields identify the target AR.
+// executed it stops the currently-active random PlaySecondaryMovie and
+// optionally performs a scene change / event-flag set.
 class PlayRandomMovieControl : public ActionRecord {
 public:
 	PlayRandomMovieControl() {}
@@ -152,14 +146,17 @@ public:
 	void readData(Common::SeekableReadStream &stream) override;
 	void execute() override;
 
+	enum RandomMovieControlMode : byte {
+		kStopNow = 0,
+		kStopAfterSequence = 1,
+		kResume = 2
+	};
+
 protected:
 	Common::String getRecordTypeName() const override { return "PlayRandomMovieControl"; }
 
-	uint32 _targetA = 0;
-	uint32 _targetB = 0;
-	uint32 _targetC = 0;
-	uint16 _flagsOrIndex = 0;
-	byte _trailing = 0;
+	byte _mode = kStopNow;
+	SceneChangeWithFlag _sceneChange;
 };
 
 } // End of namespace Action
diff --git a/engines/nancy/commontypes.cpp b/engines/nancy/commontypes.cpp
index 15c8887f2f7..d99d96e074a 100644
--- a/engines/nancy/commontypes.cpp
+++ b/engines/nancy/commontypes.cpp
@@ -50,7 +50,7 @@ void SceneChangeDescription::readData(Common::SeekableReadStream &stream, bool l
 	}
 }
 
-void SceneChangeWithFlag::readData(Common::SeekableReadStream &stream, bool reverseFormat) {
+void SceneChangeWithFlag::readData(Common::SeekableReadStream &stream, bool reverseFormat, bool terse) {
 	_sceneChange.sceneID = stream.readUint16LE();
 	_sceneChange.frameID = stream.readUint16LE();
 	_sceneChange.verticalOffset = stream.readUint16LE();
@@ -61,23 +61,29 @@ void SceneChangeWithFlag::readData(Common::SeekableReadStream &stream, bool reve
 		_flag.label = stream.readSint16LE();
 		_flag.flag = stream.readByte();
 
+		if (terse) {
+			stream.skip(1);	// unknown
+			stream.skip(2);	// shouldStopRendering
+		}
+
 		if (g_nancy->getGameType() >= kGameTypeNancy3) {
-			int32 x = stream.readSint32LE();
-			int32 y = stream.readSint32LE();
-			int32 z = stream.readSint32LE();
+			int32 x = terse ? 0 : stream.readSint32LE();
+			int32 y = terse ? 0 : stream.readSint32LE();
+			int32 z = terse ? 1 : stream.readSint32LE();
 			_sceneChange.listenerFrontVector.set(x, y, z);
 			_sceneChange.frontVectorFrameID = _sceneChange.frameID;
 		}
 	} else {
 		if (g_nancy->getGameType() >= kGameTypeNancy3) {
-			int32 x = stream.readSint32LE();
-			int32 y = stream.readSint32LE();
-			int32 z = stream.readSint32LE();
+			int32 x = terse ? 0 : stream.readSint32LE();
+			int32 y = terse ? 0 : stream.readSint32LE();
+			int32 z = terse ? 1 : stream.readSint32LE();
 			_sceneChange.listenerFrontVector.set(x, y, z);
 			_sceneChange.frontVectorFrameID = _sceneChange.frameID;
 		}
 
 		stream.skip(2); // shouldStopRendering
+
 		_flag.label = stream.readSint16LE();
 		_flag.flag = stream.readByte();
 	}
diff --git a/engines/nancy/commontypes.h b/engines/nancy/commontypes.h
index 8d1cf27be6f..9a55aaad7c7 100644
--- a/engines/nancy/commontypes.h
+++ b/engines/nancy/commontypes.h
@@ -173,7 +173,7 @@ struct SceneChangeWithFlag {
 	SceneChangeDescription _sceneChange;
 	FlagDescription _flag;
 
-	void readData(Common::SeekableReadStream &stream, bool reverseFormat = false);
+	void readData(Common::SeekableReadStream &stream, bool reverseFormat = false, bool terse = false);
 	void execute();
 };
 



From noreply at scummvm.org  Sat Jun 20 11:19:10 2026
From: noreply at scummvm.org (dreammaster)
Date: Sat, 20 Jun 2026 11:19:10 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 54963c69e218c6c7d0a249cd13c6e02b5d0926b2
Message-ID: <20260620111910.5B11A7005CC@ron.scummvm.net>

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:
8f82acfe81 MADS: FOREST: Properly start in room 901 title/credits view
e74e6744ac MADS: FOREST: Implement main menu room
54963c69e2 MADS: FOREST: Remove placeholder main menu executable code


Commit: 8f82acfe8187cae011c963f4f341326aa501dda1
    https://github.com/scummvm/scummvm/commit/8f82acfe8187cae011c963f4f341326aa501dda1
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-06-20T21:18:59+10:00

Commit Message:
MADS: FOREST: Properly start in room 901 title/credits view

Changed paths:
    engines/mads/madsv2/core/matte.cpp
    engines/mads/madsv2/forest/main.cpp
    engines/mads/madsv2/forest/rooms/room901.cpp


diff --git a/engines/mads/madsv2/core/matte.cpp b/engines/mads/madsv2/core/matte.cpp
index 800c50f545a..06dc11664e6 100644
--- a/engines/mads/madsv2/core/matte.cpp
+++ b/engines/mads/madsv2/core/matte.cpp
@@ -610,8 +610,6 @@ static void matte_special_effect(int special_effect, int full_screen) {
 		work_screen = &scr_work;
 	}
 
-	mouse_hide();
-
 	if (!timer_low_semaphore) {
 		if ((special_effect == MATTE_FX_FADE_FROM_BLACK) ||
 			(special_effect == MATTE_FX_FADE_THRU_BLACK)) {
@@ -708,8 +706,6 @@ static void matte_special_effect(int special_effect, int full_screen) {
 			work_screen->x, work_screen->y);
 		break;
 	}
-
-	mouse_show();
 }
 
 void matte_frame(int special_effect, int full_screen) {
diff --git a/engines/mads/madsv2/forest/main.cpp b/engines/mads/madsv2/forest/main.cpp
index 96601a09f6f..75727c6a7d5 100644
--- a/engines/mads/madsv2/forest/main.cpp
+++ b/engines/mads/madsv2/forest/main.cpp
@@ -127,17 +127,17 @@ static void main_cold_data_init() {
 	game_menu_exit = global_menu_system_shutdown;
 	game_emergency_save = global_emergency_save;
 
-	Common::strcpy_s(config_file_name, "config.dra");
-	Common::strcpy_s(save_game_key, "drag");
-	Common::strcpy_s(restart_game_key, "dragon");
+	Common::strcpy_s(config_file_name, "config.for");
+	Common::strcpy_s(save_game_key, "oauf");
+	Common::strcpy_s(restart_game_key, "oauf");
 
-	Common::strcpy_s(player.series_name, "RAL");
+	Common::strcpy_s(player.series_name, "B");
 	player.walker_must_reload = true;
 	player.walker_loads_first = false;
 	player.walker_visible = true;
 	player.scaling_velocity = true;
 
-	Common::strcpy_s(kernel_cheating_password, "WIDECHEW");
+	Common::strcpy_s(kernel_cheating_password, "LLAMA");
 	kernel_cheating_allowed = strlen(kernel_cheating_password);
 
 	kernel.cheating = gDebugLevel == 9 ? kernel_cheating_allowed : 0;
@@ -152,8 +152,8 @@ static void game_main(int argc, const char **argv) {
 
 	mads_mode = env_verify();
 
-	new_section = 1;
-	new_room = 101;
+	new_section = 9;
+	new_room = 901;
 	player.x = 160;
 	player.y = 78;
 
diff --git a/engines/mads/madsv2/forest/rooms/room901.cpp b/engines/mads/madsv2/forest/rooms/room901.cpp
index 9dc094624f7..ef465ebc4ca 100644
--- a/engines/mads/madsv2/forest/rooms/room901.cpp
+++ b/engines/mads/madsv2/forest/rooms/room901.cpp
@@ -67,10 +67,11 @@ static void room_901_pre_parser() {
 }
 
 static void room_901_parser() {
+	// No implementation
 }
 
 void room_901_synchronize(Common::Serializer &s) {
-	// Room has no scratch area
+	// No implementation
 }
 
 void room_901_preload() {


Commit: e74e6744ac5dc79fdc5eedf28df37e1f06dbc6d7
    https://github.com/scummvm/scummvm/commit/e74e6744ac5dc79fdc5eedf28df37e1f06dbc6d7
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-06-20T21:18:59+10:00

Commit Message:
MADS: FOREST: Implement main menu room

Changed paths:
    engines/mads/madsv2/forest/rooms/room901.cpp
    engines/mads/madsv2/forest/rooms/room904.cpp


diff --git a/engines/mads/madsv2/forest/rooms/room901.cpp b/engines/mads/madsv2/forest/rooms/room901.cpp
index ef465ebc4ca..e61409d1287 100644
--- a/engines/mads/madsv2/forest/rooms/room901.cpp
+++ b/engines/mads/madsv2/forest/rooms/room901.cpp
@@ -47,9 +47,6 @@ static void room_901_init() {
 	if (ConfMan.getBool("seen_intro")) {
 		new_room = 904;
 	} else {
-		ConfMan.setBool("seen_intro", true);
-		ConfMan.flushToDisk();
-
 		kernel_timing_trigger(300, START_INTRO);
 	}
 }
diff --git a/engines/mads/madsv2/forest/rooms/room904.cpp b/engines/mads/madsv2/forest/rooms/room904.cpp
index 5414c294812..77ae7046368 100644
--- a/engines/mads/madsv2/forest/rooms/room904.cpp
+++ b/engines/mads/madsv2/forest/rooms/room904.cpp
@@ -1,4 +1,4 @@
-???/* ScummVM - Graphic Adventure Engine
+/* 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
@@ -19,59 +19,323 @@
  *
  */
 
+#include "common/config-manager.h"
 #include "mads/madsv2/core/game.h"
-#include "mads/madsv2/core/imath.h"
-#include "mads/madsv2/core/inter.h"
+#include "mads/madsv2/core/global.h"
 #include "mads/madsv2/core/kernel.h"
 #include "mads/madsv2/core/matte.h"
+#include "mads/madsv2/core/midi.h"
 #include "mads/madsv2/core/mouse.h"
-#include "mads/madsv2/core/sound.h"
-#include "mads/madsv2/core/text.h"
-#include "mads/madsv2/forest/mads/inventory.h"
-#include "mads/madsv2/forest/mads/sounds.h"
-#include "mads/madsv2/forest/mads/words.h"
+#include "mads/madsv2/core/object.h"
+#include "mads/madsv2/core/player.h"
 #include "mads/madsv2/forest/global.h"
 #include "mads/madsv2/forest/rooms/section9.h"
 #include "mads/madsv2/forest/rooms/room904.h"
+#include "mads/madsv2/engine.h"
 
 namespace MADS {
 namespace MADSV2 {
 namespace Forest {
 namespace Rooms {
 
+struct Scratch {
+	int16 sprite[10];        /* ss[] ??? series handles for maint0-5 and main0-3 */
+	int16 sequence[10];      /* seq[] ??? sequence handles                        */
+	int16 animation[7];      /* aa[]  ??? pressed-state sequence handles          */
+	AnimationInfo animation_info[2];
+};
+
+static Scratch scratch;
+
+#define local (&scratch)
+#define ss    local->sprite
+#define seq   local->sequence
+#define aa    local->animation
+
 /* Triggers */
-#define TRIGGER0  100
-#define TRIGGER1  101
+#define TRIGGER1        101
+#define TRIGGER_ACTION  150
+
+static void room_904_setup_objects() {
+	object[0].location  = 101;
+	object[1].location  = 302;
+	object[2].location  = 221;
+	object[3].location  = 503;
+	object[4].location  = 307;
+	object[5].location  = 106;
+	object[6].location  = 303;
+	object[7].location  = 201;
+	object[8].location  = 101;
+	object[9].location  = 204;
+	object[10].location = 301;
+	object[11].location = 101;
+	object[12].location = 201;
+	object[13].location = 403;
+	object[14].location = 307;
+	object[15].location = 301;
 
+	for (int i = 0; i < 16; ++i)
+		object_set_quality(i, 0, 0);
+
+	player.num_rooms_been_in = 0;
+	global_init_code();
+}
+
+static void room_904_seen_intro() {
+	ConfMan.setBool("seen_intro", true);
+	ConfMan.flushToDisk();
+}
 
 static void room_904_init() {
-	mouse_hide();
-	global[g009] = false;
-	global[player_score] = false;
+	last_keypressed = -1;
+	global_digi_play(3);
+	scratch.animation_info[1]._active = 0;
+	scratch.animation_info[1]._frame = 0;
+	global[g009] = -1;
 	viewing_at_y = 22;
-	kernel_timing_trigger(20, TRIGGER1);
-	kernel_run_animation("*rm903i", TRIGGER0);
-	player.walker_visible = false;
+	scratch.animation_info[1]._val3 = 0;
+	global[player_score] = 0;
+
+	ss[6] = kernel_load_series("*main0", 0);
+	ss[8] = kernel_load_series("*main1", 0);
+	ss[9] = kernel_load_series("*main2", 0);
+	ss[7] = kernel_load_series("*main3", 0);
+	seq[1] = kernel_load_series("*main4", 0);
+
+	kernel_seq_depth(kernel_seq_stamp(ss[6], false, -1), 10);
+	kernel_seq_depth(kernel_seq_stamp(ss[7], false, -1), 10);
+	kernel_seq_depth(kernel_seq_stamp(ss[8], false, -1), 10);
+	kernel_seq_depth(kernel_seq_stamp(ss[9], false, -1), 10);
+	kernel_seq_depth(kernel_seq_stamp(seq[1], false, -1), 10);
+
+	ss[0] = kernel_load_series("*maint0", 0);
+	ss[2] = kernel_load_series("*maint1", 0);
+	ss[3] = kernel_load_series("*maint2", 0);
+	ss[1] = kernel_load_series("*maint3", 0);
+	ss[5] = kernel_load_series("*maint4", 0);
+	ss[4] = kernel_load_series("*maint5", 0);
+
+	kernel_timing_trigger(10, TRIGGER1);
+	player.walker_visible = 0;
 }
 
 static void room_904_daemon() {
-	// TODO: room_904_daemon
-}
+	int16 selection = 0;
 
-static void room_904_pre_parser() {
-}
+	if (kernel.trigger == TRIGGER1)
+		mouse_cursor_sprite(cursor, 1);
+
+	if (mouse_hidden)
+		mouse_show();
+
+	/* Hit-test the 6 menu button regions */
+	if (mouse_x >= 42 && mouse_x <= 71 && mouse_y >= 30 && mouse_y <= 52)
+		selection = 1;
+	if (mouse_x >= 41 && mouse_x <= 71 && mouse_y >= 146 && mouse_y <= 170)
+		selection = 2;
+	if (mouse_x >= 248 && mouse_x <= 278 && mouse_y >= 30 && mouse_y <= 52)
+		selection = 3;
+	if (mouse_x >= 125 && mouse_x <= 191 && mouse_y >= 146 && mouse_y <= 171)
+		selection = 4;
+	if (mouse_x >= 248 && mouse_x <= 277 && mouse_y >= 146 && mouse_y <= 170)
+		selection = 5;
+	if (mouse_x >= 72 && mouse_x <= 247 && mouse_y >= 55 && mouse_y <= 144)
+		selection = 6;
+
+	/* Hover: stamp highlight when first entering a button area */
+	if (scratch.animation_info[1]._active == 0) {
+		switch (selection) {
+		case 1:
+			seq[5] = kernel_seq_stamp(ss[0], false, 1);
+			kernel_seq_depth(seq[5], 1);
+			scratch.animation_info[1]._active = 1;
+			break;
+		case 2:
+			seq[7] = kernel_seq_stamp(ss[2], false, 1);
+			kernel_seq_depth(seq[7], 1);
+			scratch.animation_info[1]._active = 2;
+			break;
+		case 3:
+			seq[8] = kernel_seq_stamp(ss[3], false, 1);
+			kernel_seq_depth(seq[8], 1);
+			scratch.animation_info[1]._active = 3;
+			break;
+		case 4:
+			seq[6] = kernel_seq_stamp(ss[1], false, 1);
+			kernel_seq_depth(seq[6], 1);
+			scratch.animation_info[1]._active = 4;
+			break;
+		case 5:
+			aa[0] = kernel_seq_stamp(ss[5], false, 1);
+			kernel_seq_depth(aa[0], 1);
+			scratch.animation_info[1]._active = 5;
+			break;
+		case 6:
+			seq[9] = kernel_seq_stamp(ss[4], false, 1);
+			kernel_seq_depth(seq[9], 1);
+			scratch.animation_info[1]._active = 6;
+			break;
+		}
+	}
+
+	/* Hover: delete old highlight when leaving a button area or switching buttons */
+	if (selection == 0 || scratch.animation_info[1]._active != selection) {
+		switch (scratch.animation_info[1]._active) {
+		case 1: kernel_seq_delete(seq[5]); break;
+		case 2: kernel_seq_delete(seq[7]); break;
+		case 3: kernel_seq_delete(seq[8]); break;
+		case 4: kernel_seq_delete(seq[6]); break;
+		case 5: kernel_seq_delete(aa[0]);  break;
+		case 6: kernel_seq_delete(seq[9]); break;
+		}
+		scratch.animation_info[1]._active = 0;
+	}
+
+	/* Click: stamp pressed state when button first clicked */
+	if (mouse_stroke_going &&
+	    scratch.animation_info[1]._val3 == 0 &&
+	    scratch.animation_info[1]._frame == 0) {
+		switch (selection) {
+		case 1:
+			aa[1] = kernel_seq_stamp(ss[6], false, 2);
+			kernel_seq_depth(aa[1], 1);
+			scratch.animation_info[1]._frame = 1;
+			break;
+		case 2:
+			aa[3] = kernel_seq_stamp(ss[8], false, 2);
+			kernel_seq_depth(aa[3], 1);
+			scratch.animation_info[1]._frame = 2;
+			break;
+		case 3:
+			aa[4] = kernel_seq_stamp(ss[9], false, 2);
+			kernel_seq_depth(aa[4], 1);
+			scratch.animation_info[1]._frame = 3;
+			break;
+		case 4:
+			aa[2] = kernel_seq_stamp(ss[7], false, 2);
+			kernel_seq_depth(aa[2], 1);
+			scratch.animation_info[1]._frame = 4;
+			break;
+		case 5:
+			aa[6] = kernel_seq_stamp(seq[1], false, 2);
+			kernel_seq_depth(aa[6], 1);
+			scratch.animation_info[1]._frame = 5;
+			break;
+		case 6:
+			scratch.animation_info[1]._frame = 6;
+			break;
+		}
+	}
+
+	/* Cancel: delete pressed stamp when mouse moves off a button while held */
+	if (scratch.animation_info[1]._frame != 0 &&
+	    scratch.animation_info[1]._val3 == 0 &&
+	    selection == 0) {
+		switch (scratch.animation_info[1]._frame) {
+		case 1: kernel_seq_delete(aa[1]); break;
+		case 2: kernel_seq_delete(aa[3]); break;
+		case 3: kernel_seq_delete(aa[4]); break;
+		case 4: kernel_seq_delete(aa[2]); break;
+		case 5: kernel_seq_delete(aa[6]); break;
+		default: break;
+		}
+		scratch.animation_info[1]._frame = 0;
+	}
+
+	/* Release: delete pressed stamp on mouse release and schedule the action */
+	if (!mouse_stroke_going && scratch.animation_info[1]._val3 == 0) {
+		switch (scratch.animation_info[1]._frame) {
+		case 1: kernel_seq_delete(aa[1]); break;
+		case 2: kernel_seq_delete(aa[3]); break;
+		case 3: kernel_seq_delete(aa[4]); break;
+		case 4: kernel_seq_delete(aa[2]); break;
+		case 5: kernel_seq_delete(aa[6]); break;
+		default: break;
+		}
+		if (scratch.animation_info[1]._frame != 0) {
+			kernel_timing_trigger(1, TRIGGER_ACTION);
+			scratch.animation_info[1]._val3 = -1;
+		}
+	}
+
+	/* Action: execute the chosen menu option when the delay trigger fires */
+	if (kernel.trigger == TRIGGER_ACTION) {
+		switch (scratch.animation_info[1]._frame) {
+		case 1:
+			midi_stop();
+			global[player_score] = 0;
+			global[g016] = -1;
+			global[g102] = -1;
+			new_room = 510;
+			break;
+
+		case 2:
+			midi_stop();
+			if (g_engine->loadGameState(0).getCode() != Common::kNoError)
+				new_room = 101;
+			break;
+
+		case 3:
+			midi_stop();
+			kernel.activate_menu = 3;
+			break;
+
+		case 4:
+			room_904_setup_objects();
+			global[player_score] = 0;
+			midi_stop();
+			flags[0] = -4;
+			global[g016] = -1;
+			new_room = 101;
+			room_904_seen_intro();
+			break;
+
+		case 5:
+			game.going = 0;
+			break;
+
+		case 6:
+			new_room = 101;
+			midi_stop();
+			room_904_setup_objects();
+			global[g016] = 0;
+			flags[0] = flags[1] = flags[2] = flags[3] = 0;
+			break;
+
+		default:
+			break;
+		}
+
+		scratch.animation_info[1]._val3 = 0;
+		scratch.animation_info[1]._frame = 0;
+	}
+
+	/* Escape key: trigger quit */
+	if (last_keypressed == 27) {
+		kernel_timing_trigger(1, TRIGGER_ACTION);
+		scratch.animation_info[1]._val3 = -1;
+		scratch.animation_info[1]._frame = 5;
+		last_keypressed = -1;
+	}
 
-static void room_904_parser() {
+	/* Enter key: trigger play/continue */
+	if (last_keypressed == 13) {
+		kernel_timing_trigger(1, TRIGGER_ACTION);
+		scratch.animation_info[1]._val3 = -1;
+		scratch.animation_info[1]._frame = 6;
+		last_keypressed = -1;
+	}
 }
 
 void room_904_synchronize(Common::Serializer &s) {
-	// Room has no scratch area
+	for (int16 &v : scratch.sprite)    s.syncAsSint16LE(v);
+	for (int16 &v : scratch.sequence)  s.syncAsSint16LE(v);
+	for (int16 &v : scratch.animation) s.syncAsSint16LE(v);
+	for (auto &ai : scratch.animation_info) ai.synchronize(s);
 }
 
 void room_904_preload() {
 	room_init_code_pointer = room_904_init;
-	room_pre_parser_code_pointer = room_904_pre_parser;
-	room_parser_code_pointer = room_904_parser;
 	room_daemon_code_pointer = room_904_daemon;
 
 	global[g016] = true;


Commit: 54963c69e218c6c7d0a249cd13c6e02b5d0926b2
    https://github.com/scummvm/scummvm/commit/54963c69e218c6c7d0a249cd13c6e02b5d0926b2
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-06-20T21:18:59+10:00

Commit Message:
MADS: FOREST: Remove placeholder main menu executable code

Unlike the earlier games, Forest doesn't have a separate
executable for the main menu, nor does it have animview/textivew
resources for the intro or credits display. All the startup code
is implemented directly as custom rooms in section 9.

Changed paths:
  R engines/mads/madsv2/forest/main_menu.cpp
  R engines/mads/madsv2/forest/main_menu.h
    engines/mads/madsv2/forest/main.cpp
    engines/mads/module.mk


diff --git a/engines/mads/madsv2/forest/main.cpp b/engines/mads/madsv2/forest/main.cpp
index 75727c6a7d5..c93e50de25d 100644
--- a/engines/mads/madsv2/forest/main.cpp
+++ b/engines/mads/madsv2/forest/main.cpp
@@ -38,7 +38,6 @@
 #include "mads/madsv2/core/player.h"
 #include "mads/madsv2/core/quote.h"
 #include "mads/madsv2/core/speech.h"
-#include "mads/madsv2/forest/main_menu.h"
 #include "mads/madsv2/forest/menus.h"
 #include "mads/madsv2/engine.h"
 
@@ -51,74 +50,6 @@ constexpr byte LINE_COLOR = 2;
 
 char *quotes;
 
-static void main_menu_main() {
-#ifdef TODO
-	auto &screen = *g_engine->getScreen();
-	Palette palette;
-
-	if (!kernel_game_startup(19, KERNEL_STARTUP_CURSOR | KERNEL_STARTUP_INTERRUPT | KERNEL_STARTUP_FONT,
-		nullptr, nullptr)) {
-		matte_init(0xFFFF);
-		bool valid = !kernel_room_startup(920, 0, nullptr, true, true);
-
-		// Setup scr_work to use the full surface of scr_main (i.e. 320x200).
-		// Because of this, viewing_at_y is actually == 0
-		scr_work.y = picture_map.viewport_y;
-		viewing_at_y = (200 - scr_work.y) >> 1;
-
-		mouse_cursor_sprite(cursor, 7);
-		mouse_show();
-		mouse_force(280, 126);
-		mouse_hide();
-
-		mouse_cursor_sprite(cursor, 1);
-		kernel_seq_init();
-		kernel_message_init();
-		kernel_animation_init();
-		kernel_init_dynamic();
-
-		picture_view_x = 0;
-		picture_view_y = 0;
-		new_background = true;
-
-		if (valid) {
-			memset(&palette, 0, sizeof(palette));
-			mcga_setpal(&palette);
-			mouse_cursor_sprite(cursor, 1);
-
-			if (SHOW_LINES && viewing_at_y != 0) {
-				screen.hLine(0, viewing_at_y - 2, 319, LINE_COLOR);
-				screen.hLine(0, scr_work.y + viewing_at_y + 1, 319, LINE_COLOR);
-			}
-
-			kernel_load_sound_driver("*#SOUND.DR9", 'N', 544, 0, 49);
-
-			menu_control();
-
-			if (selected_item >= 0) {
-				// Zero out the first 3 entries of both magic color arrays
-				for (int i = 0; i < 3; i++) {
-					magic_color_values[i] = 0;
-					magic_color_flags[i] = 0;
-				}
-
-				mcga_getpal(&palette);
-
-				magic_fade_to_grey(palette, 0, 0x10, 1, 1, 0, 0, 0);
-			}
-		}
-
-		free(quotes);
-		kernel_unload_sound_driver();
-		kernel_game_shutdown();
-	}
-
-	mcga_reset();
-#else
-	selected_item = 0;
-#endif
-}
-
 static void main_cold_data_init() {
 	debugger_reset = game_debugger_reset;
 	debugger_update = game_debugger;
@@ -222,59 +153,7 @@ void forest_main() {
 	if (!env_verify())
 		env_search_mode = ENV_SEARCH_CONCAT_FILES;
 
-	if (ConfMan.getBool("start_game") || ConfMan.hasKey("save_slot"))
-		selected_item = 0;
-	else if (ConfMan.getBool("start_intro"))
-		selected_item = 3;
-	else
-		selected_item = -1;
-
-	while (!g_engine->shouldQuit()) {
-		g_engine->getScreen()->clear();
-
-		switch (selected_item) {
-		case -1:
-			main_menu_main();
-			break;
-
-		case 0:
-			game_main(2, CMD_LINE);
-			return;
-
-		case 1: {
-			// Resume savegame
-			// Get a list of saves and choose the last one
-			auto saves = g_engine->listSaves();
-			if (!saves.empty())
-				savegame_slot = saves.back().getSaveSlot();
-
-			// Start the game, which will also load the selected savegame
-			game_main(2, CMD_LINE);
-			return;
-		}
-
-		case 2:
-			// Restore savegame
-			game_restore_flag = 2;
-			game_main(2, CMD_LINE);
-			return;
-
-		case 3:
-			AnimView::animview_main("@forest");
-			selected_item = -1;
-			break;
-
-		case 4:
-			// Exit
-			return;
-
-		default:
-			// Credits
-			TextView::textview_main("credits");
-			selected_item = -1;
-			break;
-		}
-	}
+	game_main(2, CMD_LINE);
 }
 
 } // namespace Foreste
diff --git a/engines/mads/madsv2/forest/main_menu.cpp b/engines/mads/madsv2/forest/main_menu.cpp
deleted file mode 100644
index 7dff7b0108e..00000000000
--- a/engines/mads/madsv2/forest/main_menu.cpp
+++ /dev/null
@@ -1,433 +0,0 @@
-/* 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 "mads/madsv2/engine.h"
-#include "mads/madsv2/core/general.h"
-#include "mads/madsv2/core/config.h"
-#include "mads/madsv2/core/font.h"
-#include "mads/madsv2/core/speech.h"
-#include "mads/madsv2/core/matte.h"
-#include "mads/madsv2/core/sprite.h"
-#include "mads/madsv2/core/error.h"
-#include "mads/madsv2/core/room.h"
-#include "mads/madsv2/core/fileio.h"
-#include "mads/madsv2/core/env.h"
-#include "mads/madsv2/core/buffer.h"
-#include "mads/madsv2/core/mem.h"
-#include "mads/madsv2/core/pal.h"
-#include "mads/madsv2/core/timer.h"
-#include "mads/madsv2/core/mcga.h"
-#include "mads/madsv2/core/cycle.h"
-#include "mads/madsv2/core/mouse.h"
-#include "mads/madsv2/core/screen.h"
-#include "mads/madsv2/core/video.h"
-#include "mads/madsv2/core/lib.h"
-#include "mads/madsv2/core/keys.h"
-#include "mads/madsv2/core/sound.h"
-#include "mads/madsv2/core/btype.h"
-#include "mads/madsv2/core/hspot.h"
-#include "mads/madsv2/core/magic.h"
-#include "mads/madsv2/core/ems.h"
-#include "mads/madsv2/core/xms.h"
-#include "mads/madsv2/core/imath.h"
-#include "mads/madsv2/core/kernel.h"
-#include "mads/madsv2/core/game.h"
-#include "mads/madsv2/core/pack.h"
-#include "mads/madsv2/core/quote.h"
-#include "mads/madsv2/forest/mads/sounds.h"
-#include "mads/madsv2/forest/main_menu.h"
-
-namespace MADS {
-namespace MADSV2 {
-namespace Forest {
-
-#define CONFIG_FILE_NAME "CONFIG.PHA"
-
-#define EYE_QUOTES      16
-#define EYE_HOTSPOT     32
-#define EYE_MESSAGES    2
-
-#undef MESSAGE_COLOR
-#define MESSAGE_COLOR   1284
-
-
-ConfigFile config_file;
-
-int mads_mode = false;
-
-FontPtr font = NULL;
-int font_auto_spacing = -1;
-
-bool new_background   = false;
-int room_number       = 0;
-
-int win_status        = 0;
-
-bool force_opening    = false;
-bool dont_return      = false;
-bool white_bars       = true;
-byte white_border     = 2;
-
-char command_line[COMMAND_LINE_MAX][80];
-char *command_list[COMMAND_LINE_MAX+1];
-int  command_line_size = 0;
-
-int  use_mouse_cursor_fix = false;
-
-bool going;
-int  menu_mode;
-int  must_perform_matte;
-
-long frame_clock;
-long now_clock;
-long menu_clock;
-
-int menu_state;
-int menu_appear_state;
-
-int highest_y = -1;
-int next_x    = -1;
-
-char sound_file[80]    = "*#SOUND.PH9";
-char sound_letter      = 'N';
-
-int  sound_board_address = 0x220;              /* Default sound board address */
-int  sound_board_type    = sound_board_roland; /* Default sound board type    */
-
-int  swapping = false;                   /* Flag when swapping in new background */
-int  swap_line;                          /* Currently active swap line           */
-long swap_clock;                         /* Time for next swap */
-
-int  current_item;
-int  current_eye   = false;
-int  eye_latch     = false;
-int  selected_item = -1;
-
-#define LEFT_EYE   0
-#define RIGHT_EYE  1
-
-extern char *quotes;
-int  eye_message[2];
-int  eye_pokes   = 0;
-int  recent_eye  = 0;
-int  rebel_base  = 0;
-int  poke_count_message = -1;
-int  poke_count         = 0;
-int  poke_counting      = false;
-
-char poke_count_buf[2][20];
-
-char bonus_buf[80] = "";
-
-Palette special_pal;                     /* Palette for fadeout */
-
-
-MenuItem menu_item[NUM_MENU_ITEMS+1];    /* Menu item array */
-
-static void start_series() {
-	int error_flag = true;
-	int count;
-	int handle;
-	char temp_buf[80];
-
-	for (count = 0; count < NUM_MENU_ITEMS; count++) {
-		handle = -1;
-		Common::strcpy_s(temp_buf, "*MAIN0.SS");
-		temp_buf[5] = (char)('0' + count);
-
-		handle = matte_load_series(temp_buf, 0, 0);
-		if (handle < 0) goto done;
-
-		menu_item[count].handle = handle;
-		menu_item[count].active = count != 6;	// Quotes button was never implemented
-		menu_item[count].status = 0;
-	}
-
-	error_flag = false;
-
-done:
-	if (error_flag) {
-		Common::strcpy_s(error_string, temp_buf);
-		error_report(ERROR_SERIES_LOAD_FAILED, WARNING, MODULE_UNKNOWN, count, handle);
-	}
-}
-
-static void stop_series() {
-	int count;
-
-	for (count = NUM_MENU_ITEMS - 1; count >= 0; count--) {
-		matte_deallocate_series(menu_item[count].handle, true);
-	}
-}
-
-static void start_hotspots() {
-	int count;
-	int x1, x2, y1, y2;
-	int xs, ys;
-	SeriesPtr series;
-
-	numspots = 0;
-
-	for (count = 0; count < NUM_MENU_ITEMS; count++) {
-		series = series_list[menu_item[count].handle];
-		xs = series->index[0].xs;
-		ys = series->index[0].ys;
-		x1 = series->index[0].x - (xs >> 1);
-		y1 = series->index[0].y - (ys - 1);
-
-		x2 = x1 + xs - 1;
-		y2 = y1 + ys - 1;
-		hspot_add(x1, y1, x2, y2, 1, count, mcga_mode);
-	}
-}
-
-static void process_menu() {
-	int myspot;
-
-	myspot = hspot_which(mouse_x, mouse_y - viewing_at_y, mcga_mode);
-
-	current_eye = false;
-
-	if ((myspot > 0) && mouse_any_stroke && (menu_mode == MENU_ACCEPTING_COMMANDS)) {
-		current_item = spot[myspot].num;
-		if (current_item >= EYE_HOTSPOT) {
-			current_item = -1;
-			current_eye = true;
-		}
-	} else {
-		current_item = -1;
-	}
-
-	if (mouse_stop_stroke && (current_item >= 0) && (menu_mode == MENU_ACCEPTING_COMMANDS)) {
-		selected_item = current_item;
-		menu_mode = MENU_DISAPPEARING;
-		menu_state = 1;
-	}
-}
-
-static void process_sprites() {
-	int count;
-	int sprite;
-	int series;
-	int look, match;
-	Image image;
-
-	for (count = 0; count < (int)image_marker; count++) {
-		if ((image_list[count].segment_id < KERNEL_SEGMENT_ANIMATION) ||
-			(image_list[count].segment_id > KERNEL_SEGMENT_ANIMATION_HIGH)) {
-			if (image_list[count].flags >= IMAGE_STATIC) {
-				image_list[count].flags = IMAGE_ERASE;
-			}
-		}
-	}
-
-	if (menu_mode == MENU_APPEARING) goto done;
-
-	for (count = 0; count < NUM_MENU_ITEMS; count++) {
-		if (menu_item[count].active) {
-			image.flags = IMAGE_UPDATE;
-			image.segment_id = (byte)(count + 1);
-
-			series = count;
-			if (menu_mode == MENU_ACCEPTING_COMMANDS) {
-				if (count != current_item) {
-					sprite = 1;
-				} else {
-					sprite = 2;
-				}
-			} else {
-				sprite = 1;
-			}
-
-			image.series_id = (byte)menu_item[series].handle;
-			image.sprite_id = sprite;
-
-			image.x = series_list[series]->index[sprite - 1].x;
-			image.y = series_list[series]->index[sprite - 1].y;
-
-			image.depth = 0;
-			image.scale = 100;
-
-			match = !(sprite <= MENU_HIGH_SPRITE);
-
-			for (look = 0; !match && (look < (int)image_marker); look++) {
-				if (image_list[look].segment_id == image.segment_id) {
-					if (memcmp(&image_list[look].series_id,
-						&image.series_id, 9) == 0) {
-						image_list[look].flags = IMAGE_STATIC;
-						match = true;
-					}
-				}
-			}
-
-			if (!match) {
-				image_list[image_marker] = image;
-				image_marker++;
-			}
-		}
-	}
-
-done:
-	;
-}
-
-void menu_control() {
-	int fx;
-	int mykey;
-	int last_frame = -1;
-	int now_frame;
-	int reset_frame;
-	int anim;
-	int initial_reset = false;
-
-	menu_mode = MENU_APPEARING;
-	menu_state = MENU_HIGH_SPRITE;
-	menu_appear_state = 0;
-	menu_clock = 999999999;
-
-	going = true;
-	must_perform_matte = false;
-
-	frame_clock = 0;
-
-	start_series();
-	start_hotspots();
-
-	anim = kernel_run_animation("*RM902A.AA", 0);
-
-	mouse_init_cycle();
-
-	while (going && !g_engine->shouldQuit()) {
-		if (keys_any()) {
-			mykey = keys_get();
-			switch (toupper(mykey)) {
-			case esc_key:
-				selected_item = 4;
-				going = false;
-				break;
-
-			case alt_x_key:
-			case alt_q_key:
-			case ctrl_x_key:
-			case ctrl_q_key:
-				going = false;
-				break;
-
-			case 'S':
-				menu_mode = MENU_APPEARING;
-				menu_state = MENU_HIGH_SPRITE;
-				menu_appear_state = 0;
-				break;
-
-			default:
-				if (menu_mode == MENU_APPEARING) {
-					menu_mode = MENU_ACCEPTING_COMMANDS;
-					kernel_reset_animation(anim, 105);
-					kernel_anim[anim].next_clock = kernel.clock;
-				}
-				break;
-			}
-		}
-
-		mouse_begin_cycle(false);
-
-		if (mouse_stroke_going) {
-			if (menu_mode == MENU_APPEARING) {
-				menu_mode = MENU_ACCEPTING_COMMANDS;
-				kernel_reset_animation(anim, 105);
-				kernel_anim[anim].next_clock = kernel.clock;
-				mouse_init_cycle();
-			}
-		}
-
-		if (menu_mode != MENU_APPEARING) {
-			mouse_show();
-		}
-
-		now_clock = timer_read();
-		kernel.clock = now_clock;
-
-		if (now_clock >= menu_clock) {
-			if (menu_mode == MENU_DISAPPEARING)
-				going = false;
-
-			menu_clock = now_clock + MENU_FRAME_RATE;
-		}
-
-		process_menu();
-		kernel_message_update_all();
-		kernel_process_all_animations();
-
-		reset_frame = -1;
-		now_frame = kernel_anim[anim].frame;
-		if (now_frame != last_frame) {
-			last_frame = now_frame;
-
-			if (now_frame < 105) {
-				if (menu_mode != MENU_APPEARING) {
-					if (!initial_reset) {
-						reset_frame = 105;
-						initial_reset = true;
-					}
-				}
-			}
-
-			if (now_frame >= 109) {
-				if (menu_mode == MENU_APPEARING)
-					menu_mode = MENU_ACCEPTING_COMMANDS;
-
-				reset_frame = 105;
-			}
-
-			if (reset_frame >= 0) {
-				kernel_reset_animation(anim, reset_frame);
-				last_frame = reset_frame;
-			}
-		}
-
-		if (now_clock >= frame_clock) {
-			process_sprites();
-
-			fx = new_background ? 1 : 0;
-			matte_frame(fx, false);
-
-			if (fx) {
-				now_clock = timer_read();
-				menu_clock = now_clock + MENU_FRAME_RATE;
-			}
-
-			must_perform_matte = false;
-			new_background = false;
-
-			frame_clock = now_clock + FRAME_RATE;
-		}
-
-		mouse_end_cycle(false, going);
-	}
-
-	kernel_abort_animation(anim);
-
-	stop_series();
-}
-
-} // namespace Forest
-} // namespace MADSV2
-} // namespace MADS
diff --git a/engines/mads/madsv2/forest/main_menu.h b/engines/mads/madsv2/forest/main_menu.h
deleted file mode 100644
index ae655e0f4c3..00000000000
--- a/engines/mads/madsv2/forest/main_menu.h
+++ /dev/null
@@ -1,59 +0,0 @@
-/* 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 MADS_FOREST_MAIN_MENU_H
-#define MADS_FOREST_MAIN_MENU_H
-
-#include "common/str.h"
-
-namespace MADS {
-namespace MADSV2 {
-namespace Forest {
-
-#define COMMAND_LINE_MAX        10
-
-#define FRAME_RATE              1
-#define MENU_FRAME_RATE         3
-
-#define NUM_MENU_ITEMS          6
-
-#define MENU_APPEARING          0
-#define MENU_ACCEPTING_COMMANDS 1
-#define MENU_DISAPPEARING       2
-
-#define MENU_HIGH_SPRITE        15
-
-typedef struct {
-	int handle;           /* Sprite series handle */
-	int active;           /* Menu item is active  */
-	int status;           /* Current status       */
-} MenuItem;
-
-extern bool new_background;
-extern int selected_item;
-
-extern void menu_control();
-
-} // namespace Forest
-} // namespace MADSV2
-} // namespace MADS
-
-#endif
diff --git a/engines/mads/module.mk b/engines/mads/module.mk
index 707e17543b1..b5e8aee7e80 100644
--- a/engines/mads/module.mk
+++ b/engines/mads/module.mk
@@ -268,7 +268,6 @@ MODULE_OBJS += \
 	madsv2/forest/global.o \
 	madsv2/forest/journal.o \
 	madsv2/forest/main.o \
-	madsv2/forest/main_menu.o \
 	madsv2/forest/menus.o \
 	madsv2/forest/mads/mads.o \
 	madsv2/forest/rooms/room101.o \



From noreply at scummvm.org  Sat Jun 20 12:11:28 2026
From: noreply at scummvm.org (dreammaster)
Date: Sat, 20 Jun 2026 12:11:28 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 a119914f0ff59ab42af015cbd85dc84312f41655
Message-ID: <20260620121128.B09B77005CC@ron.scummvm.net>

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:
f07c5fd734 MADS: FOREST: Cleanup for main menu code
a119914f0f MADS: FOREST: Reset interface mode when loading new rooms


Commit: f07c5fd734a28dd95ffb5991174f9a9063b68976
    https://github.com/scummvm/scummvm/commit/f07c5fd734a28dd95ffb5991174f9a9063b68976
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-06-20T21:59:07+10:00

Commit Message:
MADS: FOREST: Cleanup for main menu code

Changed paths:
    engines/mads/madsv2/forest/rooms/room904.cpp


diff --git a/engines/mads/madsv2/forest/rooms/room904.cpp b/engines/mads/madsv2/forest/rooms/room904.cpp
index 77ae7046368..5a08adbba09 100644
--- a/engines/mads/madsv2/forest/rooms/room904.cpp
+++ b/engines/mads/madsv2/forest/rooms/room904.cpp
@@ -39,18 +39,18 @@ namespace Forest {
 namespace Rooms {
 
 struct Scratch {
-	int16 sprite[10];        /* ss[] ??? series handles for maint0-5 and main0-3 */
-	int16 sequence[10];      /* seq[] ??? sequence handles                        */
-	int16 animation[7];      /* aa[]  ??? pressed-state sequence handles          */
+	int16 sprite[10];
+	int16 sequence[10];
+	int16 animation[7];
 	AnimationInfo animation_info[2];
 };
 
-static Scratch scratch;
+static Scratch local;
 
-#define local (&scratch)
-#define ss    local->sprite
-#define seq   local->sequence
-#define aa    local->animation
+#define ss    local.sprite
+#define seq   local.sequence
+#define aa    local.animation
+#define aainfo local.animation_info
 
 /* Triggers */
 #define TRIGGER1        101
@@ -89,11 +89,11 @@ static void room_904_seen_intro() {
 static void room_904_init() {
 	last_keypressed = -1;
 	global_digi_play(3);
-	scratch.animation_info[1]._active = 0;
-	scratch.animation_info[1]._frame = 0;
+	aainfo[1]._active = 0;
+	aainfo[1]._frame = 0;
 	global[g009] = -1;
 	viewing_at_y = 22;
-	scratch.animation_info[1]._val3 = 0;
+	aainfo[1]._val3 = 0;
 	global[player_score] = 0;
 
 	ss[6] = kernel_load_series("*main0", 0);
@@ -128,7 +128,7 @@ static void room_904_daemon() {
 	if (mouse_hidden)
 		mouse_show();
 
-	/* Hit-test the 6 menu button regions */
+	// Hit-test the 6 menu button regions
 	if (mouse_x >= 42 && mouse_x <= 71 && mouse_y >= 30 && mouse_y <= 52)
 		selection = 1;
 	if (mouse_x >= 41 && mouse_x <= 71 && mouse_y >= 146 && mouse_y <= 170)
@@ -142,45 +142,45 @@ static void room_904_daemon() {
 	if (mouse_x >= 72 && mouse_x <= 247 && mouse_y >= 55 && mouse_y <= 144)
 		selection = 6;
 
-	/* Hover: stamp highlight when first entering a button area */
-	if (scratch.animation_info[1]._active == 0) {
+	// Hover: stamp highlight when first entering a button area
+	if (aainfo[1]._active == 0) {
 		switch (selection) {
 		case 1:
 			seq[5] = kernel_seq_stamp(ss[0], false, 1);
 			kernel_seq_depth(seq[5], 1);
-			scratch.animation_info[1]._active = 1;
+			aainfo[1]._active = 1;
 			break;
 		case 2:
 			seq[7] = kernel_seq_stamp(ss[2], false, 1);
 			kernel_seq_depth(seq[7], 1);
-			scratch.animation_info[1]._active = 2;
+			aainfo[1]._active = 2;
 			break;
 		case 3:
 			seq[8] = kernel_seq_stamp(ss[3], false, 1);
 			kernel_seq_depth(seq[8], 1);
-			scratch.animation_info[1]._active = 3;
+			aainfo[1]._active = 3;
 			break;
 		case 4:
 			seq[6] = kernel_seq_stamp(ss[1], false, 1);
 			kernel_seq_depth(seq[6], 1);
-			scratch.animation_info[1]._active = 4;
+			aainfo[1]._active = 4;
 			break;
 		case 5:
 			aa[0] = kernel_seq_stamp(ss[5], false, 1);
 			kernel_seq_depth(aa[0], 1);
-			scratch.animation_info[1]._active = 5;
+			aainfo[1]._active = 5;
 			break;
 		case 6:
 			seq[9] = kernel_seq_stamp(ss[4], false, 1);
 			kernel_seq_depth(seq[9], 1);
-			scratch.animation_info[1]._active = 6;
+			aainfo[1]._active = 6;
 			break;
 		}
 	}
 
-	/* Hover: delete old highlight when leaving a button area or switching buttons */
-	if (selection == 0 || scratch.animation_info[1]._active != selection) {
-		switch (scratch.animation_info[1]._active) {
+	// Hover: delete old highlight when leaving a button area or switching buttons
+	if (selection == 0 || aainfo[1]._active != selection) {
+		switch (aainfo[1]._active) {
 		case 1: kernel_seq_delete(seq[5]); break;
 		case 2: kernel_seq_delete(seq[7]); break;
 		case 3: kernel_seq_delete(seq[8]); break;
@@ -188,50 +188,50 @@ static void room_904_daemon() {
 		case 5: kernel_seq_delete(aa[0]);  break;
 		case 6: kernel_seq_delete(seq[9]); break;
 		}
-		scratch.animation_info[1]._active = 0;
+		aainfo[1]._active = 0;
 	}
 
-	/* Click: stamp pressed state when button first clicked */
+	// Click: stamp pressed state when button first clicked
 	if (mouse_stroke_going &&
-	    scratch.animation_info[1]._val3 == 0 &&
-	    scratch.animation_info[1]._frame == 0) {
+	    aainfo[1]._val3 == 0 &&
+	    aainfo[1]._frame == 0) {
 		switch (selection) {
 		case 1:
 			aa[1] = kernel_seq_stamp(ss[6], false, 2);
 			kernel_seq_depth(aa[1], 1);
-			scratch.animation_info[1]._frame = 1;
+			aainfo[1]._frame = 1;
 			break;
 		case 2:
 			aa[3] = kernel_seq_stamp(ss[8], false, 2);
 			kernel_seq_depth(aa[3], 1);
-			scratch.animation_info[1]._frame = 2;
+			aainfo[1]._frame = 2;
 			break;
 		case 3:
 			aa[4] = kernel_seq_stamp(ss[9], false, 2);
 			kernel_seq_depth(aa[4], 1);
-			scratch.animation_info[1]._frame = 3;
+			aainfo[1]._frame = 3;
 			break;
 		case 4:
 			aa[2] = kernel_seq_stamp(ss[7], false, 2);
 			kernel_seq_depth(aa[2], 1);
-			scratch.animation_info[1]._frame = 4;
+			aainfo[1]._frame = 4;
 			break;
 		case 5:
 			aa[6] = kernel_seq_stamp(seq[1], false, 2);
 			kernel_seq_depth(aa[6], 1);
-			scratch.animation_info[1]._frame = 5;
+			aainfo[1]._frame = 5;
 			break;
 		case 6:
-			scratch.animation_info[1]._frame = 6;
+			aainfo[1]._frame = 6;
 			break;
 		}
 	}
 
-	/* Cancel: delete pressed stamp when mouse moves off a button while held */
-	if (scratch.animation_info[1]._frame != 0 &&
-	    scratch.animation_info[1]._val3 == 0 &&
+	// Cancel: delete pressed stamp when mouse moves off a button while held
+	if (aainfo[1]._frame != 0 &&
+	    aainfo[1]._val3 == 0 &&
 	    selection == 0) {
-		switch (scratch.animation_info[1]._frame) {
+		switch (aainfo[1]._frame) {
 		case 1: kernel_seq_delete(aa[1]); break;
 		case 2: kernel_seq_delete(aa[3]); break;
 		case 3: kernel_seq_delete(aa[4]); break;
@@ -239,12 +239,12 @@ static void room_904_daemon() {
 		case 5: kernel_seq_delete(aa[6]); break;
 		default: break;
 		}
-		scratch.animation_info[1]._frame = 0;
+		aainfo[1]._frame = 0;
 	}
 
-	/* Release: delete pressed stamp on mouse release and schedule the action */
-	if (!mouse_stroke_going && scratch.animation_info[1]._val3 == 0) {
-		switch (scratch.animation_info[1]._frame) {
+	// Release: delete pressed stamp on mouse release and schedule the action
+	if (!mouse_stroke_going && aainfo[1]._val3 == 0) {
+		switch (aainfo[1]._frame) {
 		case 1: kernel_seq_delete(aa[1]); break;
 		case 2: kernel_seq_delete(aa[3]); break;
 		case 3: kernel_seq_delete(aa[4]); break;
@@ -252,16 +252,17 @@ static void room_904_daemon() {
 		case 5: kernel_seq_delete(aa[6]); break;
 		default: break;
 		}
-		if (scratch.animation_info[1]._frame != 0) {
+		if (aainfo[1]._frame != 0) {
 			kernel_timing_trigger(1, TRIGGER_ACTION);
-			scratch.animation_info[1]._val3 = -1;
+			aainfo[1]._val3 = -1;
 		}
 	}
 
-	/* Action: execute the chosen menu option when the delay trigger fires */
+	// Action: execute the chosen menu option when the delay trigger fires
 	if (kernel.trigger == TRIGGER_ACTION) {
-		switch (scratch.animation_info[1]._frame) {
+		switch (aainfo[1]._frame) {
 		case 1:
+			// Credits
 			midi_stop();
 			global[player_score] = 0;
 			global[g016] = -1;
@@ -269,18 +270,27 @@ static void room_904_daemon() {
 			new_room = 510;
 			break;
 
-		case 2:
-			midi_stop();
-			if (g_engine->loadGameState(0).getCode() != Common::kNoError)
-				new_room = 101;
+		case 2: {
+			// Resume Game
+			auto saves = g_engine->listSaves();
+			if (!saves.empty()) {
+				int savegameSlot = saves.back().getSaveSlot();
+
+				midi_stop();
+				if (g_engine->loadGameState(savegameSlot).getCode() != Common::kNoError)
+					new_room = 101;
+			}
 			break;
+		}
 
 		case 3:
+			// Restore game
 			midi_stop();
-			kernel.activate_menu = 3;
+			kernel.activate_menu = GAME_RESTORE_MENU;
 			break;
 
 		case 4:
+			// Play Intro
 			room_904_setup_objects();
 			global[player_score] = 0;
 			midi_stop();
@@ -291,10 +301,12 @@ static void room_904_daemon() {
 			break;
 
 		case 5:
+			// Exit
 			game.going = 0;
 			break;
 
 		case 6:
+			// Start Game
 			new_room = 101;
 			midi_stop();
 			room_904_setup_objects();
@@ -306,32 +318,32 @@ static void room_904_daemon() {
 			break;
 		}
 
-		scratch.animation_info[1]._val3 = 0;
-		scratch.animation_info[1]._frame = 0;
+		aainfo[1]._val3 = 0;
+		aainfo[1]._frame = 0;
 	}
 
-	/* Escape key: trigger quit */
+	// Escape key: trigger quit
 	if (last_keypressed == 27) {
 		kernel_timing_trigger(1, TRIGGER_ACTION);
-		scratch.animation_info[1]._val3 = -1;
-		scratch.animation_info[1]._frame = 5;
+		aainfo[1]._val3 = -1;
+		aainfo[1]._frame = 5;
 		last_keypressed = -1;
 	}
 
-	/* Enter key: trigger play/continue */
+	// Enter key: trigger play/continue
 	if (last_keypressed == 13) {
 		kernel_timing_trigger(1, TRIGGER_ACTION);
-		scratch.animation_info[1]._val3 = -1;
-		scratch.animation_info[1]._frame = 6;
+		aainfo[1]._val3 = -1;
+		aainfo[1]._frame = 6;
 		last_keypressed = -1;
 	}
 }
 
 void room_904_synchronize(Common::Serializer &s) {
-	for (int16 &v : scratch.sprite)    s.syncAsSint16LE(v);
-	for (int16 &v : scratch.sequence)  s.syncAsSint16LE(v);
-	for (int16 &v : scratch.animation) s.syncAsSint16LE(v);
-	for (auto &ai : scratch.animation_info) ai.synchronize(s);
+	for (int16 &v : ss)     s.syncAsSint16LE(v);
+	for (int16 &v : seq)    s.syncAsSint16LE(v);
+	for (int16 &v : aa)     s.syncAsSint16LE(v);
+	for (auto &ai : aainfo) ai.synchronize(s);
 }
 
 void room_904_preload() {
@@ -339,7 +351,7 @@ void room_904_preload() {
 	room_daemon_code_pointer = room_904_daemon;
 
 	global[g016] = true;
-	kernel.activate_menu = false;
+	kernel.activate_menu = GAME_NO_MENU;
 	section_9_walker();
 	section_9_interface();
 }


Commit: a119914f0ff59ab42af015cbd85dc84312f41655
    https://github.com/scummvm/scummvm/commit/a119914f0ff59ab42af015cbd85dc84312f41655
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-06-20T22:11:17+10:00

Commit Message:
MADS: FOREST: Reset interface mode when loading new rooms

Changed paths:
    engines/mads/madsv2/core/game.cpp


diff --git a/engines/mads/madsv2/core/game.cpp b/engines/mads/madsv2/core/game.cpp
index d3cb1fc3718..649ca02bf4e 100644
--- a/engines/mads/madsv2/core/game.cpp
+++ b/engines/mads/madsv2/core/game.cpp
@@ -1487,6 +1487,9 @@ void game_control() {
 
 			global_room_init();
 
+			if (g_engine->getGameID() == GType_Forest)
+				kernel_set_interface_mode(INTER_LIMITED_SENTENCES);
+
 			game_exec_function(room_init_code_pointer);
 #if 0
 			// paul - oh no! magic numbers!



From noreply at scummvm.org  Sat Jun 20 13:05:25 2026
From: noreply at scummvm.org (tag2015)
Date: Sat, 20 Jun 2026 13:05:25 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 4dc49d92569e1920a4a82e7c58632f74bd94fb6f
Message-ID: <20260620130525.434BE7005CC@ron.scummvm.net>

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

Summary:
4dc49d9256 AGS: Add reported games


Commit: 4dc49d92569e1920a4a82e7c58632f74bd94fb6f
    https://github.com/scummvm/scummvm/commit/4dc49d92569e1920a4a82e7c58632f74bd94fb6f
Author: Walter Agazzi (walter.agazzi at protonmail.com)
Date: 2026-06-20T15:04:13+02:00

Commit Message:
AGS: Add reported games

Fix #16857, #16864, #16866, #16867

Changed paths:
    engines/ags/detection_tables.h


diff --git a/engines/ags/detection_tables.h b/engines/ags/detection_tables.h
index f761712bac4..bbfad375463 100644
--- a/engines/ags/detection_tables.h
+++ b/engines/ags/detection_tables.h
@@ -1221,6 +1221,7 @@ const PlainGameDescriptor GAME_NAMES[] = {
 	{ "discoflow", "DISCO FLOW" },
 	{ "disgust", "Disgust" },
 	{ "dislocation", "Dislocation" },
+	{ "disparu", "Disparu: A Dream Cat Mystery" },
 	{ "disquiet", "Disquiet" },
 	{ "distancenoobject", "Distance no object" },
 	{ "djdeceased", "D. Jones: Deceased" },
@@ -4828,6 +4829,7 @@ const AGSGameDescription GAME_DESCRIPTIONS[] = {
 	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("blackwell4", "deception.ags", "1023007c2a9d8148566f56c74013c650", 216635035, GAMEFLAG_PLUGINS_AGSTEAM_WADJETEYE), // Steamdeck
 	GAME_ENTRY_PLUGIN_STEAM_EN_NOAUTOSAVE("blackwell4", "deception.ags", "d6c30d736edb75d94c7e63eeac1573be", 216641780, GAMEFLAG_PLUGINS_AGSTEAM_WADJETEYE), // Steamdeck
 	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("blackwell5", "epiphany.exe", "c1cddd6fcdbcd030beda9f10d4e4270a", 281849897, 0),
+	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("blackwell5", "epiphany.exe", "c1cddd6fcdbcd030beda9f10d4e4270a", 281846103, 0),  //v2.0.0.2
 	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("blackwell5", "epiphany.exe", "c3f7a995bbea7ce4ba7a2a97995c677e", 283092455, 0),  //v2.2
 	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("blackwell5", "epiphany.exe", "c3f7a995bbea7ce4ba7a2a97995c677e", 283092983, 0),  //v2.3
 	GAME_ENTRY_PLUGIN_GOG_EN_NOAUTOSAVE("blackwell5", "epiphany.exe", "ee9f05e549e1890a66fa29f174514d83", 247075915, 0),  //v3.0
@@ -4936,6 +4938,7 @@ const AGSGameDescription GAME_DESCRIPTIONS[] = {
 	GAME_ENTRY_PLUGIN_STEAM_NOAUTOSAVE("englishhaunting", "An English Haunting.exe", "de2b4205a56fce1b199a692a8b65e161", 987491517, 0),
 	GAME_ENTRY_PLUGIN_STEAM_NOAUTOSAVE("englishhaunting", "An English Haunting.exe", "de2b4205a56fce1b199a692a8b65e161", 987492292, 0),
 	DETECTION_ENTRY_GUIO("excavationhb", "TEOHB.exe", "f176b46bc89e227f745dae9878171676", 566323169, Common::EN_ANY, "Steam", GUIO3(GUIO_NOLANG, GUIO_NOLAUNCHLOAD, GAMEOPTION_NO_AUTOSAVE), ADGF_NO_FLAGS, 0), // Win
+	DETECTION_ENTRY_GUIO("excavationhb", "TEOHB.exe", "58e50b15d39bd15dc6eaf3dfff3936ab", 431438957, Common::EN_ANY, "Steam", GUIO3(GUIO_NOLANG, GUIO_NOLAUNCHLOAD, GAMEOPTION_NO_AUTOSAVE), ADGF_NO_FLAGS, 0), // Win
 	DETECTION_ENTRY_GUIO("excavationhb", "ac2game.dat", "e5553f7c45d26d5fbc8b376a859bb87c", 563281442, Common::EN_ANY, "Steam", GUIO3(GUIO_NOLANG, GUIO_NOLAUNCHLOAD, GAMEOPTION_NO_AUTOSAVE), ADGF_NO_FLAGS, GAMEFLAG_PLUGINS_AGSSPRITEFONT_CLIFFTOP), // Mac
 	DETECTION_ENTRY_GUIO("excavationhb", "ac2game.dat", "e5553f7c45d26d5fbc8b376a859bb87c", 563283917, Common::EN_ANY, "Steam", GUIO3(GUIO_NOLANG, GUIO_NOLAUNCHLOAD, GAMEOPTION_NO_AUTOSAVE), ADGF_NO_FLAGS, GAMEFLAG_PLUGINS_AGSSPRITEFONT_CLIFFTOP), // Mac
 	DETECTION_ENTRY_GUIO("excavationhb", "TEOHB.exe", "f176b46bc89e227f745dae9878171676", 566320586, Common::EN_ANY, "GOG.com", GUIO3(GUIO_NOLANG, GUIO_NOLAUNCHLOAD, GAMEOPTION_NO_AUTOSAVE), ADGF_NO_FLAGS, GAMEFLAG_PLUGINS_AGSSPRITEFONT_CLIFFTOP),
@@ -7267,6 +7270,7 @@ const AGSGameDescription GAME_DESCRIPTIONS[] = {
 	GAME_ENTRY_EN("dislocation", "dislocation.exe", "aecd482222ff54206e43a029b5f0b170", 168869859),
 	GAME_ENTRY_EN_PLATFORM("dislocation", "dislocation.exe", "b5c41e08919834b549ca350a28f18f87", 108929984, "OROW"),
 	GAME_ENTRY_EN_PLATFORM("dislocation", "dislocation.ags", "235b356963f58570445e10d1306a690c", 106668464, "OROW"),
+	GAME_ENTRY_LANG("disparu", "disparu.exe", "503641148592679ccfcc1d6053dd4b29", 10493231, Common::DE_DEU),
 	GAME_ENTRY_EN("disquiet", "Disquiet.exe", "f90f5f612ed5879addfdd8634d093333", 9624101),
 	GAME_ENTRY_EN("distancenoobject", "Distance no object.exe", "5cca0f8af961d2bc1c6e475437d3b23b", 98011325),  // Windows
 	GAME_ENTRY_EN("distancenoobject", "Distance no object.ags", "60b1978b591c4ffcb63e81e69d425343", 94977705),  // Linux
@@ -8284,6 +8288,7 @@ const AGSGameDescription GAME_DESCRIPTIONS[] = {
 	GAME_ENTRY_EN("lutherinhood", "afroTest4.exe", "2ca80bd50763378b72cd1e1cf25afac3", 26440699),
 	GAME_ENTRY_EN("lydia", "Kill The Lights.exe", "9b54ea3557373188d8388ec4d1ae5eed", 20416736),
 	GAME_ENTRY_EN("lydianellreno", "LydiaW.exe", "615e73fc1874e92d60a1996c2330ea36", 33971307),
+	GAME_ENTRY_EN("lydianellreno", "LydiaW.exe", "615e73fc1874e92d60a1996c2330ea36", 33971358),
 	GAME_ENTRY_EN("lygophilous", "Lygophilous.exe", "0564de07d3fd5c16e6947a647061913c", 38762137),
 	GAME_ENTRY_EN("machinesdreams", "Machines Have Lucid Dreams v0.91.exe", "784c7fae35c6501eaa3673ea29391a7b", 653366138),
 	GAME_ENTRY_EN_PLATFORM("madjack", "MadJackBrakeShake.ags", "25e4e7303bce8c521dcfc8056afeaa6e", 26312912, "MAGS"),



From noreply at scummvm.org  Sat Jun 20 14:38:28 2026
From: noreply at scummvm.org (sev-)
Date: Sat, 20 Jun 2026 14:38:28 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 ce50525f0660828dbb7244f483cf5cd2e1bd3ecf
Message-ID: <20260620143828.92BC77005CC@ron.scummvm.net>

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

Summary:
ce50525f06 NEWS: Specify codename and release date for 2026.3.0


Commit: ce50525f0660828dbb7244f483cf5cd2e1bd3ecf
    https://github.com/scummvm/scummvm/commit/ce50525f0660828dbb7244f483cf5cd2e1bd3ecf
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2026-06-20T16:38:15+02:00

Commit Message:
NEWS: Specify codename and release date for 2026.3.0

Changed paths:
    NEWS.md
    doc/de/NEUES.md


diff --git a/NEWS.md b/NEWS.md
index a685ee40c9c..b9e6f3d1cd8 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -1,7 +1,7 @@
 For a more comprehensive changelog of the latest experimental code, see:
         https://github.com/scummvm/scummvm/commits/
 
-#### 2026.3.0 (2026-06-XX)
+#### 2026.3.0 "Carousels & Killer Whales" (2026-06-20)
 
  New games:
    - Added support for Nancy Drew: The Haunted Carousel.
diff --git a/doc/de/NEUES.md b/doc/de/NEUES.md
index 7a38d285fda..9d8215d14ae 100644
--- a/doc/de/NEUES.md
+++ b/doc/de/NEUES.md
@@ -1,7 +1,7 @@
 Umfangreichere Informationen ??ber die ??nderungen des aktuellen Codes findest Du unter:
         https://github.com/scummvm/scummvm/commits/
 
-#### 2026.3.0 (XX.06.2026)
+#### 2026.3.0 "Carousels & Killer Whales" (20.06.2026)
 
  Neue Spiele:
    - Unterst??tzung f??r Nancy Drew: The Haunted Carousel hinzugef??gt.



From noreply at scummvm.org  Sat Jun 20 14:39:48 2026
From: noreply at scummvm.org (tag2015)
Date: Sat, 20 Jun 2026 14:39:48 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 46a05746c2b0907f3b37d5d74ec123aaf06e64d9
Message-ID: <20260620143948.246CF7005CC@ron.scummvm.net>

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

Summary:
46a05746c2 AGS: Add a couple new games


Commit: 46a05746c2b0907f3b37d5d74ec123aaf06e64d9
    https://github.com/scummvm/scummvm/commit/46a05746c2b0907f3b37d5d74ec123aaf06e64d9
Author: Walter Agazzi (walter.agazzi at protonmail.com)
Date: 2026-06-20T16:39:16+02:00

Commit Message:
AGS: Add a couple new games

Changed paths:
    engines/ags/detection_tables.h


diff --git a/engines/ags/detection_tables.h b/engines/ags/detection_tables.h
index bbfad375463..26a871fb04a 100644
--- a/engines/ags/detection_tables.h
+++ b/engines/ags/detection_tables.h
@@ -543,7 +543,6 @@ const PlainGameDescriptor GAME_NAMES[] = {
 	{ "afriendindeed", "A Friend Indeed ..." },
 	{ "afrojones", "Afro Jones" },
 	{ "afterashadow", "After a Shadow" },
-	{ "aftergold", "After the Golden Age" },
 	{ "agentbee", "Agent Bee" },
 	{ "agenthudson", "Agent Hudson" },
 	{ "agenttrinityepisode0theultimatum", "Agent Trinity - Episode 0 - The Ultimatum" },
@@ -2846,6 +2845,7 @@ const PlainGameDescriptor GAME_NAMES[] = {
 	{ "ripperjack", "Ripper Jack" },
 	{ "rnbquest", "R'n'B Quest" },
 	{ "roadbrollywood", "Road to Brollywood" },
+	{ "roadnowhere", "Road to Nowhere" },
 	{ "roadofdestiny", "Road of Destiny" },
 	{ "roadracer", "Road Racer" },
 	{ "roastmothergoose", "Roast Mother Goose" },
@@ -3665,6 +3665,7 @@ const PlainGameDescriptor GAME_NAMES[] = {
 	{ "wasted", "Wasted" },
 	{ "waterquest", "Water Quest" },
 	{ "watchafight", "Watch A Fight During Your Break" },
+	{ "watchtowerags", "The Watchtower" },
 	{ "wduprodigal", "World of Darkness Unbound - Prodigal" },
 	{ "wearevectors", "We Are Vectors" },
 	{ "weathered", "Weathered" },
@@ -3795,6 +3796,7 @@ const PlainGameDescriptor GAME_NAMES[] = {
 
 
 	// AGS 3.6.2/3 games. not currently supported
+	{ "aftergold", "After the Golden Age" },
 	{ "agent11", "Agent 11 - The Missing Knight" },
 	{ "badcoral", "Bad to the Coral" },
 	{ "beachbelly", "Beach Belly Bloodbath" },
@@ -3823,6 +3825,7 @@ const PlainGameDescriptor GAME_NAMES[] = {
 	{ "schooled", "School'd" },
 	{ "sequencebreak", "Sequence Break" },
 	{ "sinkholesy", "Sinkhole Sy" },
+	{ "smokeymike", "Smokey Mike" },
 	{ "snarl", "Snarl" },
 	{ "startrekxeno", "Star Trek - Xenomorph Alert" },
 	{ "thedissident", "The Dissident" },
@@ -4443,6 +4446,7 @@ const AGSGameDescription GAME_DESCRIPTIONS[] = {
 	UNSUPPORTED_GAME_ENTRY_EN("sequencebreak", "Sequence Break.ags", "1a80f829b0739c6db5ac4141eb880b82", 28319904),  // PoC Win/Linux v2
 	UNSUPPORTED_GAME_ENTRY_EN("shardsofgod", "Shards of God.ags", "dafcc09ca5a4f4d4ba721f905dc9f366", 79014256),  // v1.3
 	UNSUPPORTED_GAME_ENTRY_EN("sinkholesy", "Sinkhole Sy.ags", "e450236f4a67a6191ce669d815975bdc", 2228303),
+	UNSUPPORTED_DEMO_ENTRY_EN("smokeymike", "Smokey.ags", "aa1d62e8ee2b0f8927a0e2a00b3aaf64", 112633325),
 	UNSUPPORTED_GAME_ENTRY_EN("snarl", "Snarl.exe", "7b206bbaec47b9e30d7c41212e3f9a31", 75839830),  // v1.0.2
 	UNSUPPORTED_GAME_ENTRY_EN("snarl", "Snarl.exe", "7b206bbaec47b9e30d7c41212e3f9a31", 75841814),  // TRAC #16236
 	UNSUPPORTED_GAME_ENTRY_EN("snarl", "Snarl.exe", "7b206bbaec47b9e30d7c41212e3f9a31", 75845392),  // v1.0.3
@@ -6290,6 +6294,7 @@ const AGSGameDescription GAME_DESCRIPTIONS[] = {
 	DEMO_ENTRY_EN("vorezkor", "The VOREZKOR Hack.exe", "f3c6c85fd477033f539b95515fdf8520", 184231232),  // v0.83
 	DEMO_ENTRY_EN("wallyweasel", "wallydemo.exe", "95b7dd55f6e15c8a2118856ed9fe8ff9", 12579444),
 	DEMO_ENTRY_EN("waitingfortheloop", "WaitingForTheLoopDemo.exe", "0241777c2537fc5d077c05cde10bfa9f", 48581712),
+	DEMO_ENTRY_EN("watchtowerags", "TheWatchtowerDemo.ags", "1532182f1932253072f2ee1d19a96e42", 18703355),
 	DEMO_ENTRY_EN("welcometodarklake", "mystery.exe", "18b284c22010850f79bc5c20054a70c4", 5438912),
 	DEMO_ENTRY_EN("whattimeisit", "Wtii-demo.exe", "13a143130096b8d6baae1a7ccca52ac4", 4044338),  // 2002-09-09
 	DEMO_ENTRY_EN("whiskeyweather", "sword-denz-wjam2013.exe", "19467230b9a39aea9f711e83ccf4c984", 14336391),
@@ -7060,6 +7065,7 @@ const AGSGameDescription GAME_DESCRIPTIONS[] = {
 	GAME_ENTRY_EN_PLATFORM("colonelcarver", "Colonel Carver.exe", "7ddb9e776648faed5a51170d087074e9", 7002643, "MAGS"),
 	GAME_ENTRY_EN("colonelcarver", "Colonel Carver.exe", "7ddb9e776648faed5a51170d087074e9", 9297683),
 	GAME_ENTRY_LANG_PLATFORM("colonization", "bowsette-meiers.exe", "63f8a60cc094996bd070e68cb3c4722c", 43615607, Common::ES_ESP, "Prototype"),
+	GAME_ENTRY_LANG_PLATFORM("colonization", "Bowsette Meiers.exe", "63f8a60cc094996bd070e68cb3c4722c", 100947181, Common::ES_ESP, "Prototype Enhanced"),
 	GAME_ENTRY_EN("colourclash", "colourclash.exe", "0241777c2537fc5d077c05cde10bfa9f", 2803703),
 	GAME_ENTRY_EN("colourwise", "colourwise.exe", "473f0e2fd72b747cef62d91090ab74c7", 24461117),
 	GAME_ENTRY_EN("colourwiseleveleditor", "colourwise - Level Editor.exe", "3e09048ec0763a90ffa4eaeeb8f96df5", 3365519),
@@ -9253,6 +9259,7 @@ const AGSGameDescription GAME_DESCRIPTIONS[] = {
 	GAME_ENTRY_EN("ripperjack", "ripper_jack_v0.1.exe", "c3b72132686c4a102b3365d380c8ae3a", 2580361),
 	GAME_ENTRY_LANG("rnbquest", "R`n`B Quest.exe", "465f972675db2da6040518221af5b0ba", 119895894, Common::RU_RUS),
 	GAME_ENTRY_EN("roadbrollywood", "brollywood.exe", "615e73fc1874e92d60a1996c2330ea36", 3118294),
+	GAME_ENTRY_EN("roadnowhere", "Road to Nowhere.ags", "7582c4dd270a67f635be13b55c1809d6", 213910321),
 	GAME_ENTRY_EN("roadofdestiny", "ROD.exe", "618d7dce9631229b4579340b964c6810", 30127308),
 	GAME_ENTRY_EN("roadracer", "TR_Bryvis.exe", "cebb3ac5c3d2df939e7f0ec8f2975b64", 25080647),
 	GAME_ENTRY_EN("roastmothergoose", "RMG.exe", "00328f4f1e7729144483107b96b11df8", 46474982),



From noreply at scummvm.org  Sat Jun 20 14:56:33 2026
From: noreply at scummvm.org (ScummVM-Translations)
Date: Sat, 20 Jun 2026 14:56:33 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 3eecc91346d40d6ec1ec75f762c8a2d557f40b34
Message-ID: <20260620145633.23EAE7005CC@ron.scummvm.net>

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:
31988f96cf I18N: Update translation (Italian)
2b2c6de276 I18N: Update translation (Russian)
3eecc91346 I18N: Update translation (Ukrainian)


Commit: 31988f96cfb07b3b28958b2d5a4312a8c93fc81d
    https://github.com/scummvm/scummvm/commit/31988f96cfb07b3b28958b2d5a4312a8c93fc81d
Author: Walter Agazzi (walter.agazzi at protonmail.com)
Date: 2026-06-20T14:56:23Z

Commit Message:
I18N: Update translation (Italian)

Currently translated at 100.0% (3227 of 3227 strings)

Changed paths:
    po/it_IT.po


diff --git a/po/it_IT.po b/po/it_IT.po
index c0e2201f6bf..2d55ae4c70f 100644
--- a/po/it_IT.po
+++ b/po/it_IT.po
@@ -8,7 +8,7 @@ msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
 "POT-Creation-Date: 2026-06-18 09:40+0000\n"
-"PO-Revision-Date: 2026-06-14 09:41+0000\n"
+"PO-Revision-Date: 2026-06-20 14:56+0000\n"
 "Last-Translator: Walter Agazzi <walter.agazzi at protonmail.com>\n"
 "Language-Team: Italian <https://translations.scummvm.org/projects/scummvm/"
 "scummvm/it/>\n"
@@ -6351,11 +6351,13 @@ msgstr "Cerca aggiornamenti..."
 
 #: engines/access/metaengine.cpp:82
 msgid "Use 'high definition' OGG music"
-msgstr ""
+msgstr "Usa musica OGG ad \"alta qualit??\""
 
 #: engines/access/metaengine.cpp:83
 msgid "Use the OGG audio from the re-release instead of original MIDI tracks"
 msgstr ""
+"Usa l'audio della riedizione, in formato OGG, anzich?? le tracce MIDI "
+"originali"
 
 #: engines/access/metaengine.cpp:236 engines/alcachofa/metaengine.cpp:94
 #: engines/bbvs/metaengine.cpp:92 engines/buried/metaengine.cpp:133
@@ -8221,20 +8223,16 @@ msgstr ""
 "dall'inizio e crea dei nuovi salvataggi."
 
 #: engines/colony/intro.cpp:154 engines/colony/intro.cpp:1244
-#, fuzzy
-#| msgid "New game"
 msgid "New Game"
-msgstr "Nuova partita"
+msgstr "Nuova Partita"
 
 #: engines/colony/intro.cpp:155 engines/colony/intro.cpp:1242
 msgid "Load Game"
 msgstr "Carica Partita"
 
 #: engines/colony/intro.cpp:1219 engines/colony/intro.cpp:1244
-#, fuzzy
-#| msgid "You have stored "
 msgid "You have been terminated."
-msgstr "Hai messo nell'inventario "
+msgstr "Sei morto."
 
 #: engines/colony/metaengine.cpp:45 engines/myst3/metaengine.cpp:40
 msgid "Widescreen mod"


Commit: 2b2c6de276bf809a7e76aebd8b5c0128eeef7fe0
    https://github.com/scummvm/scummvm/commit/2b2c6de276bf809a7e76aebd8b5c0128eeef7fe0
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2026-06-20T14:56:24Z

Commit Message:
I18N: Update translation (Russian)

Currently translated at 99.9% (3226 of 3227 strings)

Changed paths:
    po/ru_RU.po


diff --git a/po/ru_RU.po b/po/ru_RU.po
index 7e9e56e0e81..59ddf7e9aec 100644
--- a/po/ru_RU.po
+++ b/po/ru_RU.po
@@ -7,7 +7,7 @@ msgstr ""
 "Project-Id-Version: ScummVM 1.8.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
 "POT-Creation-Date: 2026-06-18 09:40+0000\n"
-"PO-Revision-Date: 2026-06-17 23:04+0000\n"
+"PO-Revision-Date: 2026-06-20 14:56+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Russian <https://translations.scummvm.org/projects/scummvm/"
 "scummvm/ru/>\n"
@@ -6317,11 +6317,13 @@ msgstr "?????????????????? ????????????????????..."
 
 #: engines/access/metaengine.cpp:82
 msgid "Use 'high definition' OGG music"
-msgstr ""
+msgstr "???????????????????????? ???????????? OGG ?????????????????? ??????????????????????"
 
 #: engines/access/metaengine.cpp:83
 msgid "Use the OGG audio from the re-release instead of original MIDI tracks"
 msgstr ""
+"???????????? ???????????????????????? MIDI-???????????? ???????????????????????? ???????????????????? ?? ?????????????? OGG ???? "
+"??????????????????????"
 
 #: engines/access/metaengine.cpp:236 engines/alcachofa/metaengine.cpp:94
 #: engines/bbvs/metaengine.cpp:92 engines/buried/metaengine.cpp:133
@@ -8168,10 +8170,8 @@ msgstr ""
 "???????????? ????????????, ?? ???????????? ????????????????????????."
 
 #: engines/colony/intro.cpp:154 engines/colony/intro.cpp:1244
-#, fuzzy
-#| msgid "New game"
 msgid "New Game"
-msgstr "?????????? ????????????????????"
+msgstr "?????????? ????????"
 
 #: engines/colony/intro.cpp:155 engines/colony/intro.cpp:1242
 msgid "Load Game"


Commit: 3eecc91346d40d6ec1ec75f762c8a2d557f40b34
    https://github.com/scummvm/scummvm/commit/3eecc91346d40d6ec1ec75f762c8a2d557f40b34
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2026-06-20T14:56:25Z

Commit Message:
I18N: Update translation (Ukrainian)

Currently translated at 100.0% (3227 of 3227 strings)

Changed paths:
    po/uk_UA.po


diff --git a/po/uk_UA.po b/po/uk_UA.po
index d9ac98fa495..b20c9a7d26d 100644
--- a/po/uk_UA.po
+++ b/po/uk_UA.po
@@ -9,7 +9,7 @@ msgstr ""
 "Project-Id-Version: ScummVM 1.9.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
 "POT-Creation-Date: 2026-06-18 09:40+0000\n"
-"PO-Revision-Date: 2026-06-17 23:04+0000\n"
+"PO-Revision-Date: 2026-06-20 14:56+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Ukrainian <https://translations.scummvm.org/projects/scummvm/"
 "scummvm/uk/>\n"
@@ -6302,11 +6302,12 @@ msgstr "???????????????????? ??????????????????..."
 
 #: engines/access/metaengine.cpp:82
 msgid "Use 'high definition' OGG music"
-msgstr ""
+msgstr "?????????????????????????????? ???????????? OGG ???????????????? ??????????????????"
 
 #: engines/access/metaengine.cpp:83
 msgid "Use the OGG audio from the re-release instead of original MIDI tracks"
 msgstr ""
+"?????????????????????????????? ???????????????????? OGG ?? ?????????????????????? ?????????????? ???????????????????????? MIDI-????????????"
 
 #: engines/access/metaengine.cpp:236 engines/alcachofa/metaengine.cpp:94
 #: engines/bbvs/metaengine.cpp:92 engines/buried/metaengine.cpp:133
@@ -8148,20 +8149,16 @@ msgstr ""
 "Stealth ?? ???????????? ??????????????, ???????????????????????????? ???????? ?????????????????? ????????."
 
 #: engines/colony/intro.cpp:154 engines/colony/intro.cpp:1244
-#, fuzzy
-#| msgid "New game"
 msgid "New Game"
-msgstr "???????? ??????"
+msgstr "???????? ??????"
 
 #: engines/colony/intro.cpp:155 engines/colony/intro.cpp:1242
 msgid "Load Game"
 msgstr "?????????????????????? ?????????????????? ??????"
 
 #: engines/colony/intro.cpp:1219 engines/colony/intro.cpp:1244
-#, fuzzy
-#| msgid "You have stored "
 msgid "You have been terminated."
-msgstr "???? ?????????????? "
+msgstr "?????? ???????? ????????????????."
 
 #: engines/colony/metaengine.cpp:45 engines/myst3/metaengine.cpp:40
 msgid "Widescreen mod"



From noreply at scummvm.org  Sat Jun 20 15:03:57 2026
From: noreply at scummvm.org (ScummVM-Translations)
Date: Sat, 20 Jun 2026 15:03:57 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 f4243907bb87658d9c5b366456d8ee2ae6a1c437
Message-ID: <20260620150357.52FAE7005CC@ron.scummvm.net>

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

Summary:
f4243907bb I18N: Update translation (Russian)


Commit: f4243907bb87658d9c5b366456d8ee2ae6a1c437
    https://github.com/scummvm/scummvm/commit/f4243907bb87658d9c5b366456d8ee2ae6a1c437
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2026-06-20T15:03:51Z

Commit Message:
I18N: Update translation (Russian)

Currently translated at 100.0% (3227 of 3227 strings)

Changed paths:
    po/ru_RU.po


diff --git a/po/ru_RU.po b/po/ru_RU.po
index 59ddf7e9aec..ae78013424e 100644
--- a/po/ru_RU.po
+++ b/po/ru_RU.po
@@ -7,7 +7,7 @@ msgstr ""
 "Project-Id-Version: ScummVM 1.8.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
 "POT-Creation-Date: 2026-06-18 09:40+0000\n"
-"PO-Revision-Date: 2026-06-20 14:56+0000\n"
+"PO-Revision-Date: 2026-06-20 15:03+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Russian <https://translations.scummvm.org/projects/scummvm/"
 "scummvm/ru/>\n"
@@ -8178,10 +8178,8 @@ msgid "Load Game"
 msgstr "?????????????????? ????????"
 
 #: engines/colony/intro.cpp:1219 engines/colony/intro.cpp:1244
-#, fuzzy
-#| msgid "You have stored "
 msgid "You have been terminated."
-msgstr "???? ?????????????? "
+msgstr "???? ????????????????????."
 
 #: engines/colony/metaengine.cpp:45 engines/myst3/metaengine.cpp:40
 msgid "Widescreen mod"



From noreply at scummvm.org  Sat Jun 20 15:07:44 2026
From: noreply at scummvm.org (ScummVM-Translations)
Date: Sat, 20 Jun 2026 15:07:44 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 fff0b6b166eedd979637709a6298976ec9ce2579
Message-ID: <20260620150744.B57B07005CC@ron.scummvm.net>

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

Summary:
fff0b6b166 I18N: Update translation (Italian)


Commit: fff0b6b166eedd979637709a6298976ec9ce2579
    https://github.com/scummvm/scummvm/commit/fff0b6b166eedd979637709a6298976ec9ce2579
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2026-06-20T15:07:39Z

Commit Message:
I18N: Update translation (Italian)

Currently translated at 100.0% (3227 of 3227 strings)

Changed paths:
    po/it_IT.po


diff --git a/po/it_IT.po b/po/it_IT.po
index 2d55ae4c70f..9b632cfca3c 100644
--- a/po/it_IT.po
+++ b/po/it_IT.po
@@ -8,8 +8,8 @@ msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
 "POT-Creation-Date: 2026-06-18 09:40+0000\n"
-"PO-Revision-Date: 2026-06-20 14:56+0000\n"
-"Last-Translator: Walter Agazzi <walter.agazzi at protonmail.com>\n"
+"PO-Revision-Date: 2026-06-20 15:07+0000\n"
+"Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Italian <https://translations.scummvm.org/projects/scummvm/"
 "scummvm/it/>\n"
 "Language: it_IT\n"
@@ -2373,7 +2373,7 @@ msgstr "Abilita scorrimento cinetico nelle liste"
 
 #: gui/options.cpp:2674
 msgid "Enable smooth, momentum-based scrolling in list widgets."
-msgstr "Attiva lo scorrimento fluido (con momento) dei widget a lista"
+msgstr "Attiva lo scorrimento fluido (con momento) dei widget a lista."
 
 #: gui/options.cpp:2680
 msgid "GUI language:"
@@ -3786,7 +3786,7 @@ msgid ""
 "Alternatively, you can skip the autosave (will prompt again in 5 minutes)."
 msgstr ""
 "ATTENZIONE: La posizione dedicata al salvataggio automatico ?? occupata da un "
-"salvataggio denominato %S, e un salvataggio automatico ?? stato avviato.\n"
+"salvataggio denominato %s, e un salvataggio automatico ?? stato avviato.\n"
 "Puoi spostare il salvataggio esistente in una posizione libera o eliminarlo "
 "se non pi?? necessario.\n"
 "In alternativa puoi posticipare la decisione (verr?? chiesto nuovamente tra 5 "
@@ -5199,7 +5199,6 @@ msgstr ""
 "schermo\n"
 "\n"
 "### Tocco a due dita\n"
-"\n"
 "Per effettuare un tocco a due dita, mantieni un dito sullo schermo e quindi "
 "tocca con un secondo dito.\n"
 "\n"
@@ -7453,11 +7452,11 @@ msgstr "Escape"
 
 #: engines/bagel/metaengine.cpp:70
 msgid "Page Up"
-msgstr "Pagina prec."
+msgstr "Pagina prec"
 
 #: engines/bagel/metaengine.cpp:71
 msgid "Page Down"
-msgstr "Pagina succ."
+msgstr "Pagina succ"
 
 #: engines/bagel/metaengine.cpp:72
 msgid "Home"
@@ -12173,7 +12172,7 @@ msgstr "Mostra sottotitoli effetti sonori"
 #: engines/private/metaengine.cpp:40
 msgid "Use SFX subtitles (if subtitles are enabled)."
 msgstr ""
-"Mostra sottotitoli per gli effetti sonori (se i sottotitoli sono abilitati),"
+"Mostra sottotitoli per gli effetti sonori (se i sottotitoli sono abilitati)."
 
 #: engines/private/metaengine.cpp:50
 msgid "Highlight decision areas"



From noreply at scummvm.org  Sat Jun 20 15:09:41 2026
From: noreply at scummvm.org (ScummVM-Translations)
Date: Sat, 20 Jun 2026 15:09:41 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 cedcaaec6d2b01bc486a5523e1c0380954fcd9fb
Message-ID: <20260620150941.A418E7005CC@ron.scummvm.net>

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

Summary:
cedcaaec6d I18N: Update translation (Spanish)


Commit: cedcaaec6d2b01bc486a5523e1c0380954fcd9fb
    https://github.com/scummvm/scummvm/commit/cedcaaec6d2b01bc486a5523e1c0380954fcd9fb
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2026-06-20T15:09:35Z

Commit Message:
I18N: Update translation (Spanish)

Currently translated at 100.0% (3227 of 3227 strings)

Changed paths:
    po/es_ES.po


diff --git a/po/es_ES.po b/po/es_ES.po
index 4d0bc896e15..62a5e8d51c3 100644
--- a/po/es_ES.po
+++ b/po/es_ES.po
@@ -8,8 +8,8 @@ msgstr ""
 "Project-Id-Version: ScummVM 1.4.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
 "POT-Creation-Date: 2026-06-18 09:40+0000\n"
-"PO-Revision-Date: 2026-06-18 13:24+0000\n"
-"Last-Translator: IlDucci <pajaroloco_2 at hotmail.com>\n"
+"PO-Revision-Date: 2026-06-20 15:09+0000\n"
+"Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Spanish <https://translations.scummvm.org/projects/scummvm/"
 "scummvm/es/>\n"
 "Language: es_ES\n"
@@ -8246,7 +8246,7 @@ msgstr ""
 
 #: engines/colony/intro.cpp:154 engines/colony/intro.cpp:1244
 msgid "New Game"
-msgstr "Nueva partida"
+msgstr "Nueva Partida"
 
 #: engines/colony/intro.cpp:155 engines/colony/intro.cpp:1242
 msgid "Load Game"



From noreply at scummvm.org  Sat Jun 20 15:33:18 2026
From: noreply at scummvm.org (sev-)
Date: Sat, 20 Jun 2026 15:33:18 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm refs/tags/v2026.3.0 ->
 e19453f556aeb6da7854214d47e5159b1f82aaf5
Message-ID: <20260620153318.078537005CC@ron.scummvm.net>

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:
5f2cb5df45 I18N: Regenerate translations.dat
c2246a9f04 DISTS: Specify the latest release date
fed42f2068 RELEASE: This is 2026.3.0


Commit: 5f2cb5df450c40cafbfe7ea1b55bf88e4189116b
    https://github.com/scummvm/scummvm/commit/5f2cb5df450c40cafbfe7ea1b55bf88e4189116b
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2026-06-20T17:11:37+02:00

Commit Message:
I18N: Regenerate translations.dat

Changed paths:
    gui/themes/translations.dat




Commit: c2246a9f044471b4711d8cb26ad318913a1f8e1d
    https://github.com/scummvm/scummvm/commit/c2246a9f044471b4711d8cb26ad318913a1f8e1d
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2026-06-20T17:31:05+02:00

Commit Message:
DISTS: Specify the latest release date

Changed paths:
    dists/win32/scummvm.gdf.xml




Commit: fed42f2068dcafc6aafa1c28c77e4c88def74b66
    https://github.com/scummvm/scummvm/commit/fed42f2068dcafc6aafa1c28c77e4c88def74b66
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2026-06-20T17:32:49+02:00

Commit Message:
RELEASE: This is 2026.3.0

Changed paths:
    backends/platform/atari/readme.txt
    backends/platform/psp/README.PSP
    base/internal_version.h
    dists/amigaos/md2ag.rexx
    dists/ios7/Info.plist
    dists/irix/scummvm.spec
    dists/macosx/Info.plist
    dists/macosx/dockplugin/Info.plist
    dists/openpandora/PXML.xml
    dists/openpandora/README-OPENPANDORA
    dists/openpandora/README-PND.txt
    dists/openpandora/index.html
    dists/redhat/scummvm-tools.spec
    dists/redhat/scummvm.spec
    dists/riscos/!Boot,feb
    dists/slackware/scummvm.SlackBuild
    dists/tvos/Info.plist
    dists/wii/meta.xml





From noreply at scummvm.org  Sat Jun 20 15:33:25 2026
From: noreply at scummvm.org (sev-)
Date: Sat, 20 Jun 2026 15:33:25 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 fed42f2068dcafc6aafa1c28c77e4c88def74b66
Message-ID: <20260620153325.D3FB67005CC@ron.scummvm.net>

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:
5f2cb5df45 I18N: Regenerate translations.dat
c2246a9f04 DISTS: Specify the latest release date
fed42f2068 RELEASE: This is 2026.3.0


Commit: 5f2cb5df450c40cafbfe7ea1b55bf88e4189116b
    https://github.com/scummvm/scummvm/commit/5f2cb5df450c40cafbfe7ea1b55bf88e4189116b
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2026-06-20T17:11:37+02:00

Commit Message:
I18N: Regenerate translations.dat

Changed paths:
    gui/themes/translations.dat


diff --git a/gui/themes/translations.dat b/gui/themes/translations.dat
index d0b5e5a963d..5de20e33bc7 100644
Binary files a/gui/themes/translations.dat and b/gui/themes/translations.dat differ


Commit: c2246a9f044471b4711d8cb26ad318913a1f8e1d
    https://github.com/scummvm/scummvm/commit/c2246a9f044471b4711d8cb26ad318913a1f8e1d
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2026-06-20T17:31:05+02:00

Commit Message:
DISTS: Specify the latest release date

Changed paths:
    dists/win32/scummvm.gdf.xml


diff --git a/dists/win32/scummvm.gdf.xml b/dists/win32/scummvm.gdf.xml
index a4a85f37cec..c8d85b8eef3 100644
--- a/dists/win32/scummvm.gdf.xml
+++ b/dists/win32/scummvm.gdf.xml
@@ -3,7 +3,7 @@
     <GameDefinition gameID="{F2475C5C-EA7C-41F0-A56D-1ABF7CFEA389}">
         <Name>ScummVM</Name>
         <Description>ScummVM is a program which allows you to run certain classic graphical point-and-click adventure games, provided you already have their data files. The clever part about this: ScummVM just replaces the executables shipped with the games, allowing you to play them on systems for which they were never designed!</Description>
-        <ReleaseDate>2025-05-25</ReleaseDate>
+        <ReleaseDate>2026-06-20</ReleaseDate>
         <Genres>
             <Genre>Adventure</Genre>
         </Genres>


Commit: fed42f2068dcafc6aafa1c28c77e4c88def74b66
    https://github.com/scummvm/scummvm/commit/fed42f2068dcafc6aafa1c28c77e4c88def74b66
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2026-06-20T17:32:49+02:00

Commit Message:
RELEASE: This is 2026.3.0

Changed paths:
    backends/platform/atari/readme.txt
    backends/platform/psp/README.PSP
    base/internal_version.h
    dists/amigaos/md2ag.rexx
    dists/ios7/Info.plist
    dists/irix/scummvm.spec
    dists/macosx/Info.plist
    dists/macosx/dockplugin/Info.plist
    dists/openpandora/PXML.xml
    dists/openpandora/README-OPENPANDORA
    dists/openpandora/README-PND.txt
    dists/openpandora/index.html
    dists/redhat/scummvm-tools.spec
    dists/redhat/scummvm.spec
    dists/riscos/!Boot,feb
    dists/slackware/scummvm.SlackBuild
    dists/tvos/Info.plist
    dists/wii/meta.xml


diff --git a/backends/platform/atari/readme.txt b/backends/platform/atari/readme.txt
index 6d93f6e5bdc..b87406b7c7b 100644
--- a/backends/platform/atari/readme.txt
+++ b/backends/platform/atari/readme.txt
@@ -1,4 +1,4 @@
-ScummVM 2026.2.1git
+ScummVM 2026.3.0
 ================
 
 This is a port of ScummVM (https://www.scummvm.org), a program which allows you
@@ -35,7 +35,7 @@ Differences between the versions
 --------------------------------
 
 After talking to Keith we have decided to provide three flavours of ScummVM.
-Please refer to https://docs.scummvm.org/en/v2026.2.1git/other_platforms/atari.html
+Please refer to https://docs.scummvm.org/en/v2026.3.0/other_platforms/atari.html
 for more details (TBD).
 
 Atari Full package
diff --git a/backends/platform/psp/README.PSP b/backends/platform/psp/README.PSP
index 3a0fb5af8fa..3419e82a744 100644
--- a/backends/platform/psp/README.PSP
+++ b/backends/platform/psp/README.PSP
@@ -1,4 +1,4 @@
-ScummVM-PSP 2026.2.1git README
+ScummVM-PSP 2026.3.0 README
 ==============================================================================
 
 Installation
diff --git a/base/internal_version.h b/base/internal_version.h
index 7b6288fac47..f69be5fc028 100644
--- a/base/internal_version.h
+++ b/base/internal_version.h
@@ -16,8 +16,8 @@
 #define SCUMMVM_REVISION
 #endif
 
-#define SCUMMVM_VERSION "2026.2.1git"
+#define SCUMMVM_VERSION "2026.3.0"
 
 #define SCUMMVM_VER_MAJOR 2026
-#define SCUMMVM_VER_MINOR 2
-#define SCUMMVM_VER_PATCH 1
+#define SCUMMVM_VER_MINOR 3
+#define SCUMMVM_VER_PATCH 0
diff --git a/dists/amigaos/md2ag.rexx b/dists/amigaos/md2ag.rexx
index a586afc0519..98cc2d92d68 100644
--- a/dists/amigaos/md2ag.rexx
+++ b/dists/amigaos/md2ag.rexx
@@ -63,7 +63,7 @@ CALL OPEN write_guide,'README.guide','W'
 Prepare Amiga guide, add intro and fixed text.
 */
 CALL WRITELN write_guide,'@DATABASE ScummVM README.guide'
-CALL WRITELN write_guide,'@$VER: ScummVM Readme 2026.2.1git'
+CALL WRITELN write_guide,'@$VER: ScummVM Readme 2026.3.0'
 CALL WRITELN write_guide,'@(C) by The ScummVM team'
 CALL WRITELN write_guide,'@AUTHOR The ScummVM team'
 CALL WRITELN write_guide,'@WORDWRAP'
diff --git a/dists/ios7/Info.plist b/dists/ios7/Info.plist
index 11d6b39d868..0b7b621e153 100644
--- a/dists/ios7/Info.plist
+++ b/dists/ios7/Info.plist
@@ -19,11 +19,11 @@
 	<key>CFBundlePackageType</key>
 	<string>APPL</string>
 	<key>CFBundleShortVersionString</key>
-	<string>2026.2.1git</string>
+	<string>2026.3.0</string>
 	<key>CFBundleSignature</key>
 	<string>????</string>
 	<key>CFBundleVersion</key>
-	<string>2026.2.1git</string>
+	<string>2026.3.0</string>
 	<key>GCSupportedGameControllers</key>
 	<array>
 		<dict>
diff --git a/dists/irix/scummvm.spec b/dists/irix/scummvm.spec
index 1a4dc958b1f..399dd10b5b0 100644
--- a/dists/irix/scummvm.spec
+++ b/dists/irix/scummvm.spec
@@ -1,5 +1,5 @@
 product scummvm
-    id "ScummVM 2026.2.1git"
+    id "ScummVM 2026.3.0"
     image sw
         id "software"
         version 18
diff --git a/dists/macosx/Info.plist b/dists/macosx/Info.plist
index 0f06558ccc2..67a528be566 100644
--- a/dists/macosx/Info.plist
+++ b/dists/macosx/Info.plist
@@ -25,7 +25,7 @@
 	<key>CFBundleExecutable</key>
 	<string>scummvm</string>
 	<key>CFBundleGetInfoString</key>
-	<string>2026.2.1git, Copyright 2001-2026 The ScummVM Team</string>
+	<string>2026.3.0, Copyright 2001-2026 The ScummVM Team</string>
 	<key>CFBundleIconFile</key>
 	<string>scummvm.icns</string>
 	<key>CFBundleIdentifier</key>
@@ -63,9 +63,9 @@
 	<key>CFBundlePackageType</key>
 	<string>APPL</string>
 	<key>CFBundleShortVersionString</key>
-	<string>2026.2.1git</string>
+	<string>2026.3.0</string>
 	<key>CFBundleVersion</key>
-	<string>2026.2.1git</string>
+	<string>2026.3.0</string>
 	<key>NSHumanReadableCopyright</key>
 	<string>Copyright 2001-2026 The ScummVM Team</string>
 	<key>NSPrincipalClass</key>
diff --git a/dists/macosx/dockplugin/Info.plist b/dists/macosx/dockplugin/Info.plist
index 1c62924096c..845896b8b81 100644
--- a/dists/macosx/dockplugin/Info.plist
+++ b/dists/macosx/dockplugin/Info.plist
@@ -15,9 +15,9 @@
 	<key>CFBundlePackageType</key>
 	<string>BNDL</string>
 	<key>CFBundleShortVersionString</key>
-	<string>2026.2.1git</string>
+	<string>2026.3.0</string>
 	<key>CFBundleVersion</key>
-	<string>2026.2.1git</string>
+	<string>2026.3.0</string>
 	<key>NSHumanReadableCopyright</key>
 	<string>Copyright 2001-2026 The ScummVM Team</string>
 	<key>NSPrincipalClass</key>
diff --git a/dists/openpandora/PXML.xml b/dists/openpandora/PXML.xml
index 25c7d36fdc6..f713309b7ec 100644
--- a/dists/openpandora/PXML.xml
+++ b/dists/openpandora/PXML.xml
@@ -4,11 +4,11 @@
     <package id="scummvm.djwillis.0001">
         <author name="DJWillis" website="https://www.scummvm.org/"/>
         <!-- version type can be alpha, beta or release, set to release in branch -->
-        <version major="2026" minor="2" release="1" build="1" type="release"/>
+        <version major="2026" minor="3" release="0" build="1" type="release"/>
         <!-- Both title and titles are needed -->
-        <title lang="en_US">ScummVM 2026.2.1git</title>
+        <title lang="en_US">ScummVM 2026.3.0</title>
         <titles>
-            <title lang="en_US">ScummVM 2026.2.1git</title>
+            <title lang="en_US">ScummVM 2026.3.0</title>
         </titles>
         <descriptions>
             <description lang="en_US">
@@ -25,7 +25,7 @@
         <exec command="./runscummvm.sh"/>
         <author name="DJWillis" website="https://www.scummvm.org/"/>
         <!-- version type can be alpha, beta or release, set to release in branch -->
-        <version major="2026" minor="2" release="1" build="1" type="release"/>
+        <version major="2026" minor="3" release="0" build="1" type="release"/>
         <!-- Both title and titles are needed -->
         <title lang="en_US">ScummVM</title>
         <titles>
diff --git a/dists/openpandora/README-OPENPANDORA b/dists/openpandora/README-OPENPANDORA
index d73c44acfd4..8846040ee8e 100644
--- a/dists/openpandora/README-OPENPANDORA
+++ b/dists/openpandora/README-OPENPANDORA
@@ -1,4 +1,4 @@
-ScummVM 2026.2.1git - OPENPANDORA SPECIFIC README
+ScummVM 2026.3.0 - OPENPANDORA SPECIFIC README
 ------------------------------------------------------------------------
 Please refer to the:
 
diff --git a/dists/openpandora/README-PND.txt b/dists/openpandora/README-PND.txt
index 28b29743f68..dd10a84a35f 100644
--- a/dists/openpandora/README-PND.txt
+++ b/dists/openpandora/README-PND.txt
@@ -1,4 +1,4 @@
-ScummVM 2026.2.1git - OPENPANDORA README - HOW TO INSTALL
+ScummVM 2026.3.0 - OPENPANDORA README - HOW TO INSTALL
 ------------------------------------------------------------------------
 
 Please refer to the:
diff --git a/dists/openpandora/index.html b/dists/openpandora/index.html
index d2d036822c3..09b93c90d03 100644
--- a/dists/openpandora/index.html
+++ b/dists/openpandora/index.html
@@ -5,7 +5,7 @@
 </h3>
 
 <h4>
-  <p>ScummVM 2026.2.1git: OpenPandora Specific Documentation</p>
+  <p>ScummVM 2026.3.0: OpenPandora Specific Documentation</p>
 </h4>
 
 <A href="docs/README-OPENPANDORA">ScummVM OpenPandora README</a><br/>
@@ -13,7 +13,7 @@
 <A href="https://wiki.scummvm.org/index.php/OpenPandora">ScummVM OpenPandora WiKi</a><br/>
 
 <h4>
-  <p>ScummVM 2026.2.1git: General Documentation</p>
+  <p>ScummVM 2026.3.0: General Documentation</p>
 </h4>
 
 <A href="https://www.scummvm.org/">ScummVM website</a><br/>
diff --git a/dists/redhat/scummvm-tools.spec b/dists/redhat/scummvm-tools.spec
index b61beb30532..edcf62b7507 100644
--- a/dists/redhat/scummvm-tools.spec
+++ b/dists/redhat/scummvm-tools.spec
@@ -7,7 +7,7 @@
 #   Prologue information
 #------------------------------------------------------------------------------
 Name		: scummvm-tools
-Version		: 2026.2.1git
+Version		: 2026.3.0
 Release		: 1
 Summary		: ScummVM-related tools
 Group		: Interpreters
diff --git a/dists/redhat/scummvm.spec b/dists/redhat/scummvm.spec
index d67d37ac658..d1ca0878d12 100644
--- a/dists/redhat/scummvm.spec
+++ b/dists/redhat/scummvm.spec
@@ -7,7 +7,7 @@
 #   Prologue information
 #------------------------------------------------------------------------------
 Name		: scummvm
-Version		: 2026.2.1git
+Version		: 2026.3.0
 Release		: 1
 Summary		: Graphic adventure game interpreter
 Group		: Interpreters
diff --git a/dists/riscos/!Boot,feb b/dists/riscos/!Boot,feb
index a89ce80060d..94795cd60d0 100644
--- a/dists/riscos/!Boot,feb
+++ b/dists/riscos/!Boot,feb
@@ -5,4 +5,4 @@ Set ScummVM$Title "ScummVM"
 Set ScummVM$Description "Play certain classic graphical point-and-click adventure games"
 Set ScummVM$Publisher "ScummVM Developers"
 Set ScummVM$Web "https://www.scummvm.org/"
-Set ScummVM$Version "2026.2.1git"
+Set ScummVM$Version "2026.3.0"
diff --git a/dists/slackware/scummvm.SlackBuild b/dists/slackware/scummvm.SlackBuild
index bc50e7aad8f..9ab3753afce 100755
--- a/dists/slackware/scummvm.SlackBuild
+++ b/dists/slackware/scummvm.SlackBuild
@@ -9,7 +9,7 @@ if [ "$TMP" = "" ]; then
 fi
 PKG=$TMP/package-scummvm
 
-VERSION=2026.2.1git
+VERSION=2026.3.0
 ARCH=i486
 BUILD=1
 
diff --git a/dists/tvos/Info.plist b/dists/tvos/Info.plist
index d82d300544b..6412e3d3286 100644
--- a/dists/tvos/Info.plist
+++ b/dists/tvos/Info.plist
@@ -17,11 +17,11 @@
 	<key>CFBundlePackageType</key>
 	<string>APPL</string>
 	<key>CFBundleShortVersionString</key>
-	<string>2026.2.1git</string>
+	<string>2026.3.0</string>
 	<key>CFBundleSignature</key>
 	<string>????</string>
 	<key>CFBundleVersion</key>
-	<string>2026.2.1git</string>
+	<string>2026.3.0</string>
 	<key>GCSupportedGameControllers</key>
 	<array>
 		<dict>
diff --git a/dists/wii/meta.xml b/dists/wii/meta.xml
index d862dcc2b01..406304c6421 100644
--- a/dists/wii/meta.xml
+++ b/dists/wii/meta.xml
@@ -2,7 +2,7 @@
 <app version="1">
   <name>ScummVM</name>
   <coder>The ScummVM Team</coder>
-  <version>2026.2.1git at REVISION@</version>
+  <version>2026.3.0 at REVISION@</version>
   <release_date>@TIMESTAMP@</release_date>
   <short_description>Point &amp; Click Adventures</short_description>
   <long_description>ScummVM is a program which allows you to run certain classic graphical point-and-click adventure games, provided you already have their data files. The clever part about this: ScummVM just replaces the executables shipped with the games, allowing you to play them on systems for which they were never designed!



From noreply at scummvm.org  Sat Jun 20 15:33:35 2026
From: noreply at scummvm.org (ScummVM-Translations)
Date: Sat, 20 Jun 2026 15:33:35 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 d346f63ed51c9c871a53f08c39f9b5ea946a0a38
Message-ID: <20260620153335.DB97D7005CC@ron.scummvm.net>

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

Summary:
d346f63ed5 I18N: Update translations templates


Commit: d346f63ed51c9c871a53f08c39f9b5ea946a0a38
    https://github.com/scummvm/scummvm/commit/d346f63ed51c9c871a53f08c39f9b5ea946a0a38
Author: Weblate (noreply at weblate.org)
Date: 2026-06-20T15:33:31Z

Commit Message:
I18N: Update translations templates

Changed paths:
    po/scummvm.pot


diff --git a/po/scummvm.pot b/po/scummvm.pot
index 42843193ffa..4da72140a0c 100644
--- a/po/scummvm.pot
+++ b/po/scummvm.pot
@@ -6,9 +6,9 @@
 #, fuzzy
 msgid ""
 msgstr ""
-"Project-Id-Version: ScummVM 2026.2.1git\n"
+"Project-Id-Version: ScummVM 2026.3.0\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-18 09:40+0000\n"
+"POT-Creation-Date: 2026-06-20 15:33+0000\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL at ADDRESS>\n"
 "Language-Team: LANGUAGE <LL at li.org>\n"



From noreply at scummvm.org  Sat Jun 20 15:34:07 2026
From: noreply at scummvm.org (ScummVM-Translations)
Date: Sat, 20 Jun 2026 15:34:07 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 94146b89eef4cadb89cb98ef578e759b627b4330
Message-ID: <20260620153407.9AB857005CC@ron.scummvm.net>

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

Summary:
94146b89ee I18N: Update translation files


Commit: 94146b89eef4cadb89cb98ef578e759b627b4330
    https://github.com/scummvm/scummvm/commit/94146b89eef4cadb89cb98ef578e759b627b4330
Author: Weblate (noreply at weblate.org)
Date: 2026-06-20T15:33:48Z

Commit Message:
I18N: Update translation files

Updated by "Update PO files to match POT (msgmerge)" hook in Weblate.

Translation: ScummVM/scummvm
Translate-URL: https://translations.scummvm.org/projects/scummvm/scummvm/

Changed paths:
    po/ar.po
    po/be-tarask.po
    po/be_BY.po
    po/ca_ES.po
    po/cs_CZ.po
    po/da.po
    po/de_DE.po
    po/el.po
    po/es_ES.po
    po/eu.po
    po/fi_FI.po
    po/fr_FR.po
    po/gl_ES.po
    po/he.po
    po/hi.po
    po/hu_HU.po
    po/it_IT.po
    po/ja.po
    po/ka.po
    po/ko.po
    po/nb_NO.po
    po/nl_NL.po
    po/nn_NO.po
    po/pl_PL.po
    po/pt_BR.po
    po/pt_PT.po
    po/ro.po
    po/ru_RU.po
    po/sv_SE.po
    po/tr.po
    po/uk_UA.po
    po/zh.po
    po/zh_Hans.po
    po/zh_Hant.po


diff --git a/po/ar.po b/po/ar.po
index d794f4dbc2d..553a7c617a8 100644
--- a/po/ar.po
+++ b/po/ar.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-18 09:40+0000\n"
+"POT-Creation-Date: 2026-06-20 15:33+0000\n"
 "PO-Revision-Date: 2026-02-24 22:26+0000\n"
 "Last-Translator: Mohamed Shaaban <mohmedbashamedo at hotmail.com>\n"
 "Language-Team: Arabic <https://translations.scummvm.org/projects/scummvm/"
diff --git a/po/be-tarask.po b/po/be-tarask.po
index b612a198d20..cf8548867d6 100644
--- a/po/be-tarask.po
+++ b/po/be-tarask.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-18 09:40+0000\n"
+"POT-Creation-Date: 2026-06-20 15:33+0000\n"
 "PO-Revision-Date: 2023-02-26 15:01+0000\n"
 "Last-Translator: Kastu?? K. <oakum2334 at gmail.com>\n"
 "Language-Team: Belarusian (Tara??kievica) <https://translations.scummvm.org/"
diff --git a/po/be_BY.po b/po/be_BY.po
index cce7b71d98e..773a240481e 100644
--- a/po/be_BY.po
+++ b/po/be_BY.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-18 09:40+0000\n"
+"POT-Creation-Date: 2026-06-20 15:33+0000\n"
 "PO-Revision-Date: 2021-03-21 08:24+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Belarusian <https://translations.scummvm.org/projects/scummvm/"
diff --git a/po/ca_ES.po b/po/ca_ES.po
index bfae1608a35..008a401c7ec 100644
--- a/po/ca_ES.po
+++ b/po/ca_ES.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.6.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-18 09:40+0000\n"
+"POT-Creation-Date: 2026-06-20 15:33+0000\n"
 "PO-Revision-Date: 2026-05-26 04:22+0000\n"
 "Last-Translator: Xaviu <xaviu at xaviu.com>\n"
 "Language-Team: Catalan <https://translations.scummvm.org/projects/scummvm/"
diff --git a/po/cs_CZ.po b/po/cs_CZ.po
index 32a7ebc8744..70c33edc20d 100644
--- a/po/cs_CZ.po
+++ b/po/cs_CZ.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.7.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-18 09:40+0000\n"
+"POT-Creation-Date: 2026-06-20 15:33+0000\n"
 "PO-Revision-Date: 2026-02-09 20:22+0000\n"
 "Last-Translator: AsciiWolf <mail at asciiwolf.com>\n"
 "Language-Team: Czech <https://translations.scummvm.org/projects/scummvm/"
diff --git a/po/da.po b/po/da.po
index b2698d3fa1f..2470246dc7b 100644
--- a/po/da.po
+++ b/po/da.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-18 09:40+0000\n"
+"POT-Creation-Date: 2026-06-20 15:33+0000\n"
 "PO-Revision-Date: 2023-09-07 17:04+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Danish <https://translations.scummvm.org/projects/scummvm/"
diff --git a/po/de_DE.po b/po/de_DE.po
index 4af3543874a..f8530613a81 100644
--- a/po/de_DE.po
+++ b/po/de_DE.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.10.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-18 09:40+0000\n"
+"POT-Creation-Date: 2026-06-20 15:33+0000\n"
 "PO-Revision-Date: 2026-05-05 16:31+0000\n"
 "Last-Translator: Lothar Serra Mari <lothar.serramari at scummvm.org>\n"
 "Language-Team: German <https://translations.scummvm.org/projects/scummvm/"
diff --git a/po/el.po b/po/el.po
index ccdc4c8decc..b58ca3b1587 100644
--- a/po/el.po
+++ b/po/el.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.10.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-18 09:40+0000\n"
+"POT-Creation-Date: 2026-06-20 15:33+0000\n"
 "PO-Revision-Date: 2026-04-23 00:22+0000\n"
 "Last-Translator: Antoniou Athanasios <a.antoniou79 at gmail.com>\n"
 "Language-Team: Greek <https://translations.scummvm.org/projects/scummvm/"
diff --git a/po/es_ES.po b/po/es_ES.po
index 62a5e8d51c3..9a7c186e39b 100644
--- a/po/es_ES.po
+++ b/po/es_ES.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.4.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-18 09:40+0000\n"
+"POT-Creation-Date: 2026-06-20 15:33+0000\n"
 "PO-Revision-Date: 2026-06-20 15:09+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Spanish <https://translations.scummvm.org/projects/scummvm/"
diff --git a/po/eu.po b/po/eu.po
index f0460e40dc4..6fcdae924be 100644
--- a/po/eu.po
+++ b/po/eu.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.5.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-18 09:40+0000\n"
+"POT-Creation-Date: 2026-06-20 15:33+0000\n"
 "PO-Revision-Date: 2024-02-05 18:42+0000\n"
 "Last-Translator: Josu Igoa <josuigoa at ni.eus>\n"
 "Language-Team: Basque <https://translations.scummvm.org/projects/scummvm/"
diff --git a/po/fi_FI.po b/po/fi_FI.po
index 7924b8320b7..ebfa577ad5a 100644
--- a/po/fi_FI.po
+++ b/po/fi_FI.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.6.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-18 09:40+0000\n"
+"POT-Creation-Date: 2026-06-20 15:33+0000\n"
 "PO-Revision-Date: 2026-02-07 15:22+0000\n"
 "Last-Translator: Timo Mikkolainen <tmikkola at gmail.com>\n"
 "Language-Team: Finnish <https://translations.scummvm.org/projects/scummvm/"
diff --git a/po/fr_FR.po b/po/fr_FR.po
index f8d3f159d72..b2bcdb0cf5e 100644
--- a/po/fr_FR.po
+++ b/po/fr_FR.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-18 09:40+0000\n"
+"POT-Creation-Date: 2026-06-20 15:33+0000\n"
 "PO-Revision-Date: 2026-06-14 09:41+0000\n"
 "Last-Translator: Purple T <ZEONK at hotmail.com>\n"
 "Language-Team: French <https://translations.scummvm.org/projects/scummvm/"
diff --git a/po/gl_ES.po b/po/gl_ES.po
index 9e26572bc33..e0dc1066fd7 100644
--- a/po/gl_ES.po
+++ b/po/gl_ES.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-18 09:40+0000\n"
+"POT-Creation-Date: 2026-06-20 15:33+0000\n"
 "PO-Revision-Date: 2020-09-26 21:13+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Galician <https://translations.scummvm.org/projects/scummvm/"
diff --git a/po/he.po b/po/he.po
index f630b29b8e5..87cc827d16f 100644
--- a/po/he.po
+++ b/po/he.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.1.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-18 09:40+0000\n"
+"POT-Creation-Date: 2026-06-20 15:33+0000\n"
 "PO-Revision-Date: 2025-04-09 20:18+0000\n"
 "Last-Translator: Niv Baehr <bloop93 at gmail.com>\n"
 "Language-Team: Hebrew <https://translations.scummvm.org/projects/scummvm/"
diff --git a/po/hi.po b/po/hi.po
index 3fb63eb4731..a543983fb3f 100644
--- a/po/hi.po
+++ b/po/hi.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-18 09:40+0000\n"
+"POT-Creation-Date: 2026-06-20 15:33+0000\n"
 "PO-Revision-Date: 2026-05-12 22:22+0000\n"
 "Last-Translator: KARTIK SAXENA <kartiksaxena2005 at gmail.com>\n"
 "Language-Team: Hindi <https://translations.scummvm.org/projects/scummvm/"
diff --git a/po/hu_HU.po b/po/hu_HU.po
index 7d430bcfbad..cd9b1018e99 100644
--- a/po/hu_HU.po
+++ b/po/hu_HU.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-18 09:40+0000\n"
+"POT-Creation-Date: 2026-06-20 15:33+0000\n"
 "PO-Revision-Date: 2025-03-07 00:18+0000\n"
 "Last-Translator: Hov??nszki Tam??s <gahex68379 at lofiey.com>\n"
 "Language-Team: Hungarian <https://translations.scummvm.org/projects/scummvm/"
diff --git a/po/it_IT.po b/po/it_IT.po
index 9b632cfca3c..c6144d93f2d 100644
--- a/po/it_IT.po
+++ b/po/it_IT.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-18 09:40+0000\n"
+"POT-Creation-Date: 2026-06-20 15:33+0000\n"
 "PO-Revision-Date: 2026-06-20 15:07+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Italian <https://translations.scummvm.org/projects/scummvm/"
diff --git a/po/ja.po b/po/ja.po
index 0aa2d96df4e..b901f02435e 100644
--- a/po/ja.po
+++ b/po/ja.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-18 09:40+0000\n"
+"POT-Creation-Date: 2026-06-20 15:33+0000\n"
 "PO-Revision-Date: 2026-06-04 07:22+0000\n"
 "Last-Translator: VAN-Gluon <van.gluonp at gmail.com>\n"
 "Language-Team: Japanese <https://translations.scummvm.org/projects/scummvm/"
diff --git a/po/ka.po b/po/ka.po
index fb1b17b8171..1efa4bdcd9b 100644
--- a/po/ka.po
+++ b/po/ka.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-18 09:40+0000\n"
+"POT-Creation-Date: 2026-06-20 15:33+0000\n"
 "PO-Revision-Date: 2026-06-05 10:22+0000\n"
 "Last-Translator: NorwayFun <temuri.doghonadze at gmail.com>\n"
 "Language-Team: Georgian <https://translations.scummvm.org/projects/scummvm/"
diff --git a/po/ko.po b/po/ko.po
index ce9d3bc57be..aaaf69990c5 100644
--- a/po/ko.po
+++ b/po/ko.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-18 09:40+0000\n"
+"POT-Creation-Date: 2026-06-20 15:33+0000\n"
 "PO-Revision-Date: 2026-05-19 20:22+0000\n"
 "Last-Translator: Hoseok Seo <ddinghoya at gmail.com>\n"
 "Language-Team: Korean <https://translations.scummvm.org/projects/scummvm/"
diff --git a/po/nb_NO.po b/po/nb_NO.po
index 03b6fa36129..e7f073d386d 100644
--- a/po/nb_NO.po
+++ b/po/nb_NO.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-18 09:40+0000\n"
+"POT-Creation-Date: 2026-06-20 15:33+0000\n"
 "PO-Revision-Date: 2026-02-07 15:22+0000\n"
 "Last-Translator: Einar Johan Tr??an S??m??en <einarjohants at gmail.com>\n"
 "Language-Team: Norwegian Bokm??l <https://translations.scummvm.org/projects/"
diff --git a/po/nl_NL.po b/po/nl_NL.po
index b02114ff9a3..cff511c9003 100644
--- a/po/nl_NL.po
+++ b/po/nl_NL.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.9.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-18 09:40+0000\n"
+"POT-Creation-Date: 2026-06-20 15:33+0000\n"
 "PO-Revision-Date: 2026-02-11 14:23+0000\n"
 "Last-Translator: SecularSteve <fairfull.playing at gmail.com>\n"
 "Language-Team: Dutch <https://translations.scummvm.org/projects/scummvm/"
diff --git a/po/nn_NO.po b/po/nn_NO.po
index 662fd489a07..55306434652 100644
--- a/po/nn_NO.po
+++ b/po/nn_NO.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-18 09:40+0000\n"
+"POT-Creation-Date: 2026-06-20 15:33+0000\n"
 "PO-Revision-Date: 2023-12-14 14:28+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Norwegian Nynorsk <https://translations.scummvm.org/projects/"
diff --git a/po/pl_PL.po b/po/pl_PL.po
index 51e7148c8a7..3708ea58cab 100644
--- a/po/pl_PL.po
+++ b/po/pl_PL.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-18 09:40+0000\n"
+"POT-Creation-Date: 2026-06-20 15:33+0000\n"
 "PO-Revision-Date: 2026-04-17 00:22+0000\n"
 "Last-Translator: SkiffPL <skiff at windowslive.com>\n"
 "Language-Team: Polish <https://translations.scummvm.org/projects/scummvm/"
diff --git a/po/pt_BR.po b/po/pt_BR.po
index 66508a144a9..5bcae19c6e1 100644
--- a/po/pt_BR.po
+++ b/po/pt_BR.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-18 09:40+0000\n"
+"POT-Creation-Date: 2026-06-20 15:33+0000\n"
 "PO-Revision-Date: 2026-06-17 22:10+0000\n"
 "Last-Translator: Marcel Souza Lemes <marcosoutsider at gmail.com>\n"
 "Language-Team: Portuguese (Brazil) <https://translations.scummvm.org/"
diff --git a/po/pt_PT.po b/po/pt_PT.po
index 49e54b6bc94..5a4813e1871 100644
--- a/po/pt_PT.po
+++ b/po/pt_PT.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.10.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-18 09:40+0000\n"
+"POT-Creation-Date: 2026-06-20 15:33+0000\n"
 "PO-Revision-Date: 2022-06-05 14:09+0000\n"
 "Last-Translator: Daniel <supsuper at gmail.com>\n"
 "Language-Team: Portuguese (Portugal) <https://translations.scummvm.org/"
diff --git a/po/ro.po b/po/ro.po
index 77b50440a1d..47b3e99b097 100644
--- a/po/ro.po
+++ b/po/ro.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.10.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-18 09:40+0000\n"
+"POT-Creation-Date: 2026-06-20 15:33+0000\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: Automatically generated\n"
 "Language-Team: none\n"
diff --git a/po/ru_RU.po b/po/ru_RU.po
index ae78013424e..d6e54e838ca 100644
--- a/po/ru_RU.po
+++ b/po/ru_RU.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.8.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-18 09:40+0000\n"
+"POT-Creation-Date: 2026-06-20 15:33+0000\n"
 "PO-Revision-Date: 2026-06-20 15:03+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Russian <https://translations.scummvm.org/projects/scummvm/"
diff --git a/po/sv_SE.po b/po/sv_SE.po
index a082cb95280..0538a41a5e4 100644
--- a/po/sv_SE.po
+++ b/po/sv_SE.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.5.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-18 09:40+0000\n"
+"POT-Creation-Date: 2026-06-20 15:33+0000\n"
 "PO-Revision-Date: 2026-04-19 13:22+0000\n"
 "Last-Translator: Daniel Nylander <po at danielnylander.se>\n"
 "Language-Team: Swedish <https://translations.scummvm.org/projects/scummvm/"
diff --git a/po/tr.po b/po/tr.po
index c6f605427e9..7ea0e0ef293 100644
--- a/po/tr.po
+++ b/po/tr.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-18 09:40+0000\n"
+"POT-Creation-Date: 2026-06-20 15:33+0000\n"
 "PO-Revision-Date: 2024-10-10 12:33+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Turkish <https://translations.scummvm.org/projects/scummvm/"
diff --git a/po/uk_UA.po b/po/uk_UA.po
index b20c9a7d26d..c6ff7548907 100644
--- a/po/uk_UA.po
+++ b/po/uk_UA.po
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.9.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-18 09:40+0000\n"
+"POT-Creation-Date: 2026-06-20 15:33+0000\n"
 "PO-Revision-Date: 2026-06-20 14:56+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Ukrainian <https://translations.scummvm.org/projects/scummvm/"
diff --git a/po/zh.po b/po/zh.po
index 30c8be25653..b313f1d4f36 100644
--- a/po/zh.po
+++ b/po/zh.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-18 09:40+0000\n"
+"POT-Creation-Date: 2026-06-20 15:33+0000\n"
 "PO-Revision-Date: 2024-05-14 12:42+0000\n"
 "Last-Translator: ?????? <clonewith at qq.com>\n"
 "Language-Team: Chinese <https://translations.scummvm.org/projects/scummvm/"
diff --git a/po/zh_Hans.po b/po/zh_Hans.po
index 9cc18a2b0ee..e475863c38e 100644
--- a/po/zh_Hans.po
+++ b/po/zh_Hans.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.9.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-18 09:40+0000\n"
+"POT-Creation-Date: 2026-06-20 15:33+0000\n"
 "PO-Revision-Date: 2024-12-01 22:18+0000\n"
 "Last-Translator: ?????? <clonewith at qq.com>\n"
 "Language-Team: Chinese (Simplified Han script) <https://"
diff --git a/po/zh_Hant.po b/po/zh_Hant.po
index 6ec5d8797db..a5d123a46f1 100644
--- a/po/zh_Hant.po
+++ b/po/zh_Hant.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.9.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-18 09:40+0000\n"
+"POT-Creation-Date: 2026-06-20 15:33+0000\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: Automatically generated\n"
 "Language-Team: none\n"



From noreply at scummvm.org  Sat Jun 20 15:54:02 2026
From: noreply at scummvm.org (ScummVM-Translations)
Date: Sat, 20 Jun 2026 15:54:02 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 043b72d56d097898ecd9d179a73d816f09966b3a
Message-ID: <20260620155402.D5EBF7005CC@ron.scummvm.net>

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

Summary:
043b72d56d I18N: Update translation (Spanish)


Commit: 043b72d56d097898ecd9d179a73d816f09966b3a
    https://github.com/scummvm/scummvm/commit/043b72d56d097898ecd9d179a73d816f09966b3a
Author: IlDucci (pajaroloco_2 at hotmail.com)
Date: 2026-06-20T15:53:57Z

Commit Message:
I18N: Update translation (Spanish)

Currently translated at 100.0% (3227 of 3227 strings)

Changed paths:
    po/es_ES.po


diff --git a/po/es_ES.po b/po/es_ES.po
index 9a7c186e39b..3627f23b583 100644
--- a/po/es_ES.po
+++ b/po/es_ES.po
@@ -8,8 +8,8 @@ msgstr ""
 "Project-Id-Version: ScummVM 1.4.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
 "POT-Creation-Date: 2026-06-20 15:33+0000\n"
-"PO-Revision-Date: 2026-06-20 15:09+0000\n"
-"Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
+"PO-Revision-Date: 2026-06-20 15:53+0000\n"
+"Last-Translator: IlDucci <pajaroloco_2 at hotmail.com>\n"
 "Language-Team: Spanish <https://translations.scummvm.org/projects/scummvm/"
 "scummvm/es/>\n"
 "Language: es_ES\n"
@@ -8246,7 +8246,7 @@ msgstr ""
 
 #: engines/colony/intro.cpp:154 engines/colony/intro.cpp:1244
 msgid "New Game"
-msgstr "Nueva Partida"
+msgstr "Nueva partida"
 
 #: engines/colony/intro.cpp:155 engines/colony/intro.cpp:1242
 msgid "Load Game"



From noreply at scummvm.org  Sat Jun 20 16:03:39 2026
From: noreply at scummvm.org (ScummVM-Translations)
Date: Sat, 20 Jun 2026 16:03:39 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm-web master ->
 8c1447fb0bef6a566375accdc2e6cd12a2aee67e
Message-ID: <20260620160339.B062F7005CC@ron.scummvm.net>

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

Summary:
8c1447fb0b L10N: Update translation (Italian) (News)


Commit: 8c1447fb0bef6a566375accdc2e6cd12a2aee67e
    https://github.com/scummvm/scummvm-web/commit/8c1447fb0bef6a566375accdc2e6cd12a2aee67e
Author: Walter Agazzi (walter.agazzi at protonmail.com)
Date: 2026-06-20T16:03:34Z

Commit Message:
L10N: Update translation (Italian) (News)

Currently translated at 29.4% (260 of 882 strings)

Changed paths:
    data/it/news.json


diff --git a/data/it/news.json b/data/it/news.json
index 0302ebd1..9b1ab811 100644
--- a/data/it/news.json
+++ b/data/it/news.json
@@ -513,5 +513,12 @@
     "20260613": {
         "title": "Le porte del Cartoon Carnival sono aperte!",
         "content": "Siete tutti invitati! _Hanna-Barbera's Cartoon Carnival_ ?? pronto per la fase di test pubblica di ScummVM.\n\nOriginariamente sviluppato e rilasciato nel 1993 dalla Funhouse Design, esclusivamente per Philips CD-i, ?? stato poi portato su Windows e Macintosh nel 1995. _Cartoon Carnival_ raccoglie un cast di popolari personaggi di Hanna-Barbera in una collezione di brevi mini-giochi in stile arcade. Scooby-Doo, Fred Flintstone, George Jetson, Top Cat e compagnia partecipano a turno a diverse sfide, spaziando tra labirinti, puzzle visuali, giochi con i palloni e quiz testuali.\n\nPi?? che un tradizionale gioco di avventura, si tratta di un perfetto esempio di intrattenimento multimediale anni '90, ricco di animazioni, voci e musica. Inoltre, ?? un titolo su licenza che pu?? facilmente venire dimenticato col passare degli anni, e pertanto siamo particolarmente felici di preservarlo per i sistemi contemporanei.\n\nAttualmente, solo le versioni Windows e Macintosh sono supportate. La versione originale per CD-i non ?? supportata.\n\nPer testare il gioco, avrai bisogno dell'ultimo [daily build di sviluppo](https://www.scummvm.org/downloads/#daily) e la versione originale per Windows o Macintosh, o la versione dimostrativa (per ora, abbiamo reperito solo quella in italiano). Consulta la [pagina wiki](https://wiki.scummvm.org/index.php?title=Cartoon_Carnival) per l'elenco dei file necessari.\n\nCome sempre, cortesemente segnala qualsiasi malfunzionamento al nostro [bug tracker](https://bugs.scummvm.org/), specificando la versione testata, la piattaforma, ed eventualmente allegando screenshot utili o salvataggi.\n\nFatti avanti, scegli un gioco, e aiutaci a far funzionare questa festa al meglio!"
+    },
+    "20260614": {
+        "title": "Alfred Pelrock si unisce a ScummVM",
+        "content": "Buone notizie per i fan dei giochi di avventura: _Alfred Pelrock_ ?? ora supportato da ScummVM.\n\nOriginariamente rilasciato nel 1996, _Alfred Pelrock_ ?? un'avventura punta-e-clicca umoristica caratterizzata da grafica in stile cartoon, curiosi puzzle e umorismo irriverente. Dopo aver sognato una principessa egiziana imprigionata dall'antichit??, Alfred intraprende un viaggio nel tempo fino all'Antico Egitto per salvarla, incontrando bizzarre situazioni e personaggi eccentrici lungo il percorso.\n\nIl gioco ?? ricco di situazioni comiche e satiriche, ma contiene anche temi suggestivi e occasionale humor per adulti, tipico del periodo. Inoltre, il gioco ?? stato rilasciato esclusivamente in spagnolo e non esistono traduzioni ufficiali.\n\nCome sempre, se disponi del gioco originale e conosci lo spagnolo, procurati l'ultima [build di sviluppo](https://www.scummvm.org/downloads/#daily), aggiungi i [file richiesti del gioco](https://wiki.scummvm.org/index.php?title=Alfred_Pelrock:_En_Busca_de_un_Sue%C3%B1o) e divertiti! Attendiamo [segnalazioni di bug](https://bugs.scummvm.org/) e feedback da parte dei giocatori per ottimizzare la compatibilit?? e preservare al meglio questo piccolo ritaglio dell'enorme storia delle avventure grafiche."
+    },
+    "20260617": {
+        "content": "ScummVM ha ufficialmente completato il supporto per il motore proprietario per Windows usato dalla Gamos. Tale motore ?? stato originariamente utilizzato per _Pilot Brothers 1 e 2_, dando il via all'iconica serie di giochi di avventura, prima della transizione ai motori qdEngine e NGI. ?? stato inoltre usato per creare una selezione di giochini passatempo \"da ufficio\".\n\nIl motore fornisce il supporto per sette giochi:\n\n* _Patience_\n* _Flip-Flop_\n* _NetWalk_\n* _Pilot Brothers: On the Track of Striped Elephant_\n* _Pilot Brothers: The Case of Serial Maniac_\n* _Vitamin_\n* _WildSnake_\n\nQuindi, se ti va di ricordare il passato e spendere un po' di tempo insieme ai due detective eccentrici Chef e Colleague, o dilettarti con giochi in stile Solitario, scarica l'ultima [daily build di sviluppo](https://www.scummvm.org/downloads/#daily) e recupera i file dati dei giochi. Come sempre, qualora riscontrassi malfunzionamenti o stranezze, per cortesia segnalale al nostro [bug tracker](https://bugs.scummvm.org/)."
     }
 }



From noreply at scummvm.org  Sat Jun 20 16:13:51 2026
From: noreply at scummvm.org (sev-)
Date: Sat, 20 Jun 2026 16:13:51 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 25030db8b9da3f88f673c2249cb17618da095cc5
Message-ID: <20260620161351.52F357005CC@ron.scummvm.net>

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:
17542f1cfe RELEASE: This is 2026.3.1git
143ca08e36 BUILD: Do not add files to git when making ideprojects
25030db8b9 DEVTOOLS: Mix-in ideproject files in the release tarball


Commit: 17542f1cfe41b71e7f04211366cf8be5ac751674
    https://github.com/scummvm/scummvm/commit/17542f1cfe41b71e7f04211366cf8be5ac751674
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2026-06-20T18:13:34+02:00

Commit Message:
RELEASE: This is 2026.3.1git

Changed paths:
    backends/platform/atari/readme.txt
    backends/platform/psp/README.PSP
    base/internal_version.h
    dists/amigaos/md2ag.rexx
    dists/ios7/Info.plist
    dists/irix/scummvm.spec
    dists/macosx/Info.plist
    dists/macosx/dockplugin/Info.plist
    dists/openpandora/PXML.xml
    dists/openpandora/README-OPENPANDORA
    dists/openpandora/README-PND.txt
    dists/openpandora/index.html
    dists/redhat/scummvm-tools.spec
    dists/redhat/scummvm.spec
    dists/riscos/!Boot,feb
    dists/slackware/scummvm.SlackBuild
    dists/tvos/Info.plist
    dists/wii/meta.xml


diff --git a/backends/platform/atari/readme.txt b/backends/platform/atari/readme.txt
index b87406b7c7b..0c10f7aebb8 100644
--- a/backends/platform/atari/readme.txt
+++ b/backends/platform/atari/readme.txt
@@ -1,4 +1,4 @@
-ScummVM 2026.3.0
+ScummVM 2026.3.1git
 ================
 
 This is a port of ScummVM (https://www.scummvm.org), a program which allows you
@@ -35,7 +35,7 @@ Differences between the versions
 --------------------------------
 
 After talking to Keith we have decided to provide three flavours of ScummVM.
-Please refer to https://docs.scummvm.org/en/v2026.3.0/other_platforms/atari.html
+Please refer to https://docs.scummvm.org/en/v2026.3.1git/other_platforms/atari.html
 for more details (TBD).
 
 Atari Full package
diff --git a/backends/platform/psp/README.PSP b/backends/platform/psp/README.PSP
index 3419e82a744..4e1742bdbe6 100644
--- a/backends/platform/psp/README.PSP
+++ b/backends/platform/psp/README.PSP
@@ -1,4 +1,4 @@
-ScummVM-PSP 2026.3.0 README
+ScummVM-PSP 2026.3.1git README
 ==============================================================================
 
 Installation
diff --git a/base/internal_version.h b/base/internal_version.h
index f69be5fc028..330d87d50be 100644
--- a/base/internal_version.h
+++ b/base/internal_version.h
@@ -16,8 +16,8 @@
 #define SCUMMVM_REVISION
 #endif
 
-#define SCUMMVM_VERSION "2026.3.0"
+#define SCUMMVM_VERSION "2026.3.1git"
 
 #define SCUMMVM_VER_MAJOR 2026
 #define SCUMMVM_VER_MINOR 3
-#define SCUMMVM_VER_PATCH 0
+#define SCUMMVM_VER_PATCH 1
diff --git a/dists/amigaos/md2ag.rexx b/dists/amigaos/md2ag.rexx
index 98cc2d92d68..962103c60b8 100644
--- a/dists/amigaos/md2ag.rexx
+++ b/dists/amigaos/md2ag.rexx
@@ -63,7 +63,7 @@ CALL OPEN write_guide,'README.guide','W'
 Prepare Amiga guide, add intro and fixed text.
 */
 CALL WRITELN write_guide,'@DATABASE ScummVM README.guide'
-CALL WRITELN write_guide,'@$VER: ScummVM Readme 2026.3.0'
+CALL WRITELN write_guide,'@$VER: ScummVM Readme 2026.3.1git'
 CALL WRITELN write_guide,'@(C) by The ScummVM team'
 CALL WRITELN write_guide,'@AUTHOR The ScummVM team'
 CALL WRITELN write_guide,'@WORDWRAP'
diff --git a/dists/ios7/Info.plist b/dists/ios7/Info.plist
index 0b7b621e153..f553490362e 100644
--- a/dists/ios7/Info.plist
+++ b/dists/ios7/Info.plist
@@ -19,11 +19,11 @@
 	<key>CFBundlePackageType</key>
 	<string>APPL</string>
 	<key>CFBundleShortVersionString</key>
-	<string>2026.3.0</string>
+	<string>2026.3.1git</string>
 	<key>CFBundleSignature</key>
 	<string>????</string>
 	<key>CFBundleVersion</key>
-	<string>2026.3.0</string>
+	<string>2026.3.1git</string>
 	<key>GCSupportedGameControllers</key>
 	<array>
 		<dict>
diff --git a/dists/irix/scummvm.spec b/dists/irix/scummvm.spec
index 399dd10b5b0..d9fee15c74a 100644
--- a/dists/irix/scummvm.spec
+++ b/dists/irix/scummvm.spec
@@ -1,5 +1,5 @@
 product scummvm
-    id "ScummVM 2026.3.0"
+    id "ScummVM 2026.3.1git"
     image sw
         id "software"
         version 18
diff --git a/dists/macosx/Info.plist b/dists/macosx/Info.plist
index 67a528be566..52fbabf405e 100644
--- a/dists/macosx/Info.plist
+++ b/dists/macosx/Info.plist
@@ -25,7 +25,7 @@
 	<key>CFBundleExecutable</key>
 	<string>scummvm</string>
 	<key>CFBundleGetInfoString</key>
-	<string>2026.3.0, Copyright 2001-2026 The ScummVM Team</string>
+	<string>2026.3.1git, Copyright 2001-2026 The ScummVM Team</string>
 	<key>CFBundleIconFile</key>
 	<string>scummvm.icns</string>
 	<key>CFBundleIdentifier</key>
@@ -63,9 +63,9 @@
 	<key>CFBundlePackageType</key>
 	<string>APPL</string>
 	<key>CFBundleShortVersionString</key>
-	<string>2026.3.0</string>
+	<string>2026.3.1git</string>
 	<key>CFBundleVersion</key>
-	<string>2026.3.0</string>
+	<string>2026.3.1git</string>
 	<key>NSHumanReadableCopyright</key>
 	<string>Copyright 2001-2026 The ScummVM Team</string>
 	<key>NSPrincipalClass</key>
diff --git a/dists/macosx/dockplugin/Info.plist b/dists/macosx/dockplugin/Info.plist
index 845896b8b81..1095abc86a8 100644
--- a/dists/macosx/dockplugin/Info.plist
+++ b/dists/macosx/dockplugin/Info.plist
@@ -15,9 +15,9 @@
 	<key>CFBundlePackageType</key>
 	<string>BNDL</string>
 	<key>CFBundleShortVersionString</key>
-	<string>2026.3.0</string>
+	<string>2026.3.1git</string>
 	<key>CFBundleVersion</key>
-	<string>2026.3.0</string>
+	<string>2026.3.1git</string>
 	<key>NSHumanReadableCopyright</key>
 	<string>Copyright 2001-2026 The ScummVM Team</string>
 	<key>NSPrincipalClass</key>
diff --git a/dists/openpandora/PXML.xml b/dists/openpandora/PXML.xml
index f713309b7ec..d298279007d 100644
--- a/dists/openpandora/PXML.xml
+++ b/dists/openpandora/PXML.xml
@@ -4,11 +4,11 @@
     <package id="scummvm.djwillis.0001">
         <author name="DJWillis" website="https://www.scummvm.org/"/>
         <!-- version type can be alpha, beta or release, set to release in branch -->
-        <version major="2026" minor="3" release="0" build="1" type="release"/>
+        <version major="2026" minor="3" release="1" build="1" type="release"/>
         <!-- Both title and titles are needed -->
-        <title lang="en_US">ScummVM 2026.3.0</title>
+        <title lang="en_US">ScummVM 2026.3.1git</title>
         <titles>
-            <title lang="en_US">ScummVM 2026.3.0</title>
+            <title lang="en_US">ScummVM 2026.3.1git</title>
         </titles>
         <descriptions>
             <description lang="en_US">
@@ -25,7 +25,7 @@
         <exec command="./runscummvm.sh"/>
         <author name="DJWillis" website="https://www.scummvm.org/"/>
         <!-- version type can be alpha, beta or release, set to release in branch -->
-        <version major="2026" minor="3" release="0" build="1" type="release"/>
+        <version major="2026" minor="3" release="1" build="1" type="release"/>
         <!-- Both title and titles are needed -->
         <title lang="en_US">ScummVM</title>
         <titles>
diff --git a/dists/openpandora/README-OPENPANDORA b/dists/openpandora/README-OPENPANDORA
index 8846040ee8e..4799e2cd728 100644
--- a/dists/openpandora/README-OPENPANDORA
+++ b/dists/openpandora/README-OPENPANDORA
@@ -1,4 +1,4 @@
-ScummVM 2026.3.0 - OPENPANDORA SPECIFIC README
+ScummVM 2026.3.1git - OPENPANDORA SPECIFIC README
 ------------------------------------------------------------------------
 Please refer to the:
 
diff --git a/dists/openpandora/README-PND.txt b/dists/openpandora/README-PND.txt
index dd10a84a35f..27c69ecb85a 100644
--- a/dists/openpandora/README-PND.txt
+++ b/dists/openpandora/README-PND.txt
@@ -1,4 +1,4 @@
-ScummVM 2026.3.0 - OPENPANDORA README - HOW TO INSTALL
+ScummVM 2026.3.1git - OPENPANDORA README - HOW TO INSTALL
 ------------------------------------------------------------------------
 
 Please refer to the:
diff --git a/dists/openpandora/index.html b/dists/openpandora/index.html
index 09b93c90d03..ff31ccec856 100644
--- a/dists/openpandora/index.html
+++ b/dists/openpandora/index.html
@@ -5,7 +5,7 @@
 </h3>
 
 <h4>
-  <p>ScummVM 2026.3.0: OpenPandora Specific Documentation</p>
+  <p>ScummVM 2026.3.1git: OpenPandora Specific Documentation</p>
 </h4>
 
 <A href="docs/README-OPENPANDORA">ScummVM OpenPandora README</a><br/>
@@ -13,7 +13,7 @@
 <A href="https://wiki.scummvm.org/index.php/OpenPandora">ScummVM OpenPandora WiKi</a><br/>
 
 <h4>
-  <p>ScummVM 2026.3.0: General Documentation</p>
+  <p>ScummVM 2026.3.1git: General Documentation</p>
 </h4>
 
 <A href="https://www.scummvm.org/">ScummVM website</a><br/>
diff --git a/dists/redhat/scummvm-tools.spec b/dists/redhat/scummvm-tools.spec
index edcf62b7507..77fecb3af3c 100644
--- a/dists/redhat/scummvm-tools.spec
+++ b/dists/redhat/scummvm-tools.spec
@@ -7,7 +7,7 @@
 #   Prologue information
 #------------------------------------------------------------------------------
 Name		: scummvm-tools
-Version		: 2026.3.0
+Version		: 2026.3.1git
 Release		: 1
 Summary		: ScummVM-related tools
 Group		: Interpreters
diff --git a/dists/redhat/scummvm.spec b/dists/redhat/scummvm.spec
index d1ca0878d12..6c744390e09 100644
--- a/dists/redhat/scummvm.spec
+++ b/dists/redhat/scummvm.spec
@@ -7,7 +7,7 @@
 #   Prologue information
 #------------------------------------------------------------------------------
 Name		: scummvm
-Version		: 2026.3.0
+Version		: 2026.3.1git
 Release		: 1
 Summary		: Graphic adventure game interpreter
 Group		: Interpreters
diff --git a/dists/riscos/!Boot,feb b/dists/riscos/!Boot,feb
index 94795cd60d0..657dafebbc3 100644
--- a/dists/riscos/!Boot,feb
+++ b/dists/riscos/!Boot,feb
@@ -5,4 +5,4 @@ Set ScummVM$Title "ScummVM"
 Set ScummVM$Description "Play certain classic graphical point-and-click adventure games"
 Set ScummVM$Publisher "ScummVM Developers"
 Set ScummVM$Web "https://www.scummvm.org/"
-Set ScummVM$Version "2026.3.0"
+Set ScummVM$Version "2026.3.1git"
diff --git a/dists/slackware/scummvm.SlackBuild b/dists/slackware/scummvm.SlackBuild
index 9ab3753afce..53cbd510cb3 100755
--- a/dists/slackware/scummvm.SlackBuild
+++ b/dists/slackware/scummvm.SlackBuild
@@ -9,7 +9,7 @@ if [ "$TMP" = "" ]; then
 fi
 PKG=$TMP/package-scummvm
 
-VERSION=2026.3.0
+VERSION=2026.3.1git
 ARCH=i486
 BUILD=1
 
diff --git a/dists/tvos/Info.plist b/dists/tvos/Info.plist
index 6412e3d3286..15d2dce2e18 100644
--- a/dists/tvos/Info.plist
+++ b/dists/tvos/Info.plist
@@ -17,11 +17,11 @@
 	<key>CFBundlePackageType</key>
 	<string>APPL</string>
 	<key>CFBundleShortVersionString</key>
-	<string>2026.3.0</string>
+	<string>2026.3.1git</string>
 	<key>CFBundleSignature</key>
 	<string>????</string>
 	<key>CFBundleVersion</key>
-	<string>2026.3.0</string>
+	<string>2026.3.1git</string>
 	<key>GCSupportedGameControllers</key>
 	<array>
 		<dict>
diff --git a/dists/wii/meta.xml b/dists/wii/meta.xml
index 406304c6421..f561f7efdb6 100644
--- a/dists/wii/meta.xml
+++ b/dists/wii/meta.xml
@@ -2,7 +2,7 @@
 <app version="1">
   <name>ScummVM</name>
   <coder>The ScummVM Team</coder>
-  <version>2026.3.0 at REVISION@</version>
+  <version>2026.3.1git at REVISION@</version>
   <release_date>@TIMESTAMP@</release_date>
   <short_description>Point &amp; Click Adventures</short_description>
   <long_description>ScummVM is a program which allows you to run certain classic graphical point-and-click adventure games, provided you already have their data files. The clever part about this: ScummVM just replaces the executables shipped with the games, allowing you to play them on systems for which they were never designed!


Commit: 143ca08e3625e44e8a4ac18377a4228b3d96a2e6
    https://github.com/scummvm/scummvm/commit/143ca08e3625e44e8a4ac18377a4228b3d96a2e6
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2026-06-20T18:13:35+02:00

Commit Message:
BUILD: Do not add files to git when making ideprojects

Changed paths:
    ports.mk


diff --git a/ports.mk b/ports.mk
index 81bda2e9da4..261b52002a4 100644
--- a/ports.mk
+++ b/ports.mk
@@ -715,13 +715,11 @@ ifeq ($(VER_DIRTY), -dirty)
 	$(error You have uncommitted changes)
 endif
 	@echo Creating Code::Blocks project files...
-	@cd $(srcdir)/dists/codeblocks && $(PWD)/devtools/create_project/create_project ../.. --codeblocks >/dev/null && git add -f engines/*.h *.workspace *.cbp
+	@cd $(srcdir)/dists/codeblocks && $(PWD)/devtools/create_project/create_project ../.. --codeblocks >/dev/null
 	@echo Creating MSVC project files...
-	@cd $(srcdir)/dists/msvc && $(PWD)/devtools/create_project/create_project ../.. --msvc-version 18 --msvc >/dev/null && git add -f engines/*.h *.sln *.vcxproj *.vcxproj.filters *.props
+	@cd $(srcdir)/dists/msvc && $(PWD)/devtools/create_project/create_project ../.. --msvc-version 18 --msvc >/dev/null
 	@echo
 	@echo All is done.
-	@echo Now run
-	@echo -e "\tgit commit -m 'DISTS: Generated Code::Blocks and MSVC project files'"
 
 release-checks:
 	devtools/release-checks.sh


Commit: 25030db8b9da3f88f673c2249cb17618da095cc5
    https://github.com/scummvm/scummvm/commit/25030db8b9da3f88f673c2249cb17618da095cc5
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2026-06-20T18:13:35+02:00

Commit Message:
DEVTOOLS: Mix-in ideproject files in the release tarball

We stopped committing them, so to not put autogenerated files
to the repo

Changed paths:
    devtools/dist-scummvm.sh


diff --git a/devtools/dist-scummvm.sh b/devtools/dist-scummvm.sh
index 765a979d6c4..bea3d3a2148 100755
--- a/devtools/dist-scummvm.sh
+++ b/devtools/dist-scummvm.sh
@@ -49,6 +49,7 @@ if [ -z "$tag" ]; then
   tag="v$version"
 fi
 fullname="$module-$version"
+repodir=`pwd`
 
 # Check modules
 case $module in
@@ -63,6 +64,11 @@ scummvm-tools)
     exit 1
 esac
 
+if [ ! -f "$repodir/dists/codeblocks/scummvm.cbp" ]; then
+  echo "Code::Blocks project file scummvm.cbp not found in dists/codeblocks"
+  echo "Please run 'make ideprojects' first to generate the project files."
+  exit 1
+fi
 
 echo "packaging $module release $version, GIT tag $tag"
 
@@ -111,6 +117,10 @@ cd $tmpdir
 echo "Cleaning up .git temporary bundle directories..."
 rm -rf $fullname/.git
 
+echo "Copying ideprojects files..."
+cp -rp $repodir/dists/codeblocks/engines $repodir/dists/codeblocks/*.workspace $repodir/dists/codeblocks/*.cbp $fullname/dists/codeblocks/
+cp -rp $repodir/dists/msvc/engines $repodir/dists/msvc/*.sln $repodir/dists/msvc/*.vcxproj $repodir/dists/msvc/*.vcxproj.filters $repodir/dists/msvc/*.props $fullname/dists/msvc/
+
 ### roll the tarball
 
 echo "Creating tarball $fullname.tar..."



From noreply at scummvm.org  Sat Jun 20 16:13:55 2026
From: noreply at scummvm.org (ScummVM-Translations)
Date: Sat, 20 Jun 2026 16:13:55 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 e271cb0ee39e512bfb320c0aaa7fc4a16fad72df
Message-ID: <20260620161355.813857005CC@ron.scummvm.net>

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

Summary:
e271cb0ee3 I18N: Update translations templates


Commit: e271cb0ee39e512bfb320c0aaa7fc4a16fad72df
    https://github.com/scummvm/scummvm/commit/e271cb0ee39e512bfb320c0aaa7fc4a16fad72df
Author: Weblate (noreply at weblate.org)
Date: 2026-06-20T16:13:51Z

Commit Message:
I18N: Update translations templates

Changed paths:
    po/scummvm.pot


diff --git a/po/scummvm.pot b/po/scummvm.pot
index 4da72140a0c..f96c09ece2f 100644
--- a/po/scummvm.pot
+++ b/po/scummvm.pot
@@ -6,9 +6,9 @@
 #, fuzzy
 msgid ""
 msgstr ""
-"Project-Id-Version: ScummVM 2026.3.0\n"
+"Project-Id-Version: ScummVM 2026.3.1git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-20 15:33+0000\n"
+"POT-Creation-Date: 2026-06-20 16:13+0000\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL at ADDRESS>\n"
 "Language-Team: LANGUAGE <LL at li.org>\n"



From noreply at scummvm.org  Sat Jun 20 16:14:27 2026
From: noreply at scummvm.org (ScummVM-Translations)
Date: Sat, 20 Jun 2026 16:14:27 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 36c714ef28728bffdaaddff4c6824808dddfdff5
Message-ID: <20260620161427.DC2967005CC@ron.scummvm.net>

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

Summary:
36c714ef28 I18N: Update translation files


Commit: 36c714ef28728bffdaaddff4c6824808dddfdff5
    https://github.com/scummvm/scummvm/commit/36c714ef28728bffdaaddff4c6824808dddfdff5
Author: Weblate (noreply at weblate.org)
Date: 2026-06-20T16:14:07Z

Commit Message:
I18N: Update translation files

Updated by "Update PO files to match POT (msgmerge)" hook in Weblate.

Translation: ScummVM/scummvm
Translate-URL: https://translations.scummvm.org/projects/scummvm/scummvm/

Changed paths:
    po/ar.po
    po/be-tarask.po
    po/be_BY.po
    po/ca_ES.po
    po/cs_CZ.po
    po/da.po
    po/de_DE.po
    po/el.po
    po/es_ES.po
    po/eu.po
    po/fi_FI.po
    po/fr_FR.po
    po/gl_ES.po
    po/he.po
    po/hi.po
    po/hu_HU.po
    po/it_IT.po
    po/ja.po
    po/ka.po
    po/ko.po
    po/nb_NO.po
    po/nl_NL.po
    po/nn_NO.po
    po/pl_PL.po
    po/pt_BR.po
    po/pt_PT.po
    po/ro.po
    po/ru_RU.po
    po/sv_SE.po
    po/tr.po
    po/uk_UA.po
    po/zh.po
    po/zh_Hans.po
    po/zh_Hant.po


diff --git a/po/ar.po b/po/ar.po
index 553a7c617a8..b611cfcf5c7 100644
--- a/po/ar.po
+++ b/po/ar.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-20 15:33+0000\n"
+"POT-Creation-Date: 2026-06-20 16:13+0000\n"
 "PO-Revision-Date: 2026-02-24 22:26+0000\n"
 "Last-Translator: Mohamed Shaaban <mohmedbashamedo at hotmail.com>\n"
 "Language-Team: Arabic <https://translations.scummvm.org/projects/scummvm/"
diff --git a/po/be-tarask.po b/po/be-tarask.po
index cf8548867d6..96b4c8373af 100644
--- a/po/be-tarask.po
+++ b/po/be-tarask.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-20 15:33+0000\n"
+"POT-Creation-Date: 2026-06-20 16:13+0000\n"
 "PO-Revision-Date: 2023-02-26 15:01+0000\n"
 "Last-Translator: Kastu?? K. <oakum2334 at gmail.com>\n"
 "Language-Team: Belarusian (Tara??kievica) <https://translations.scummvm.org/"
diff --git a/po/be_BY.po b/po/be_BY.po
index 773a240481e..315cd0cf7b3 100644
--- a/po/be_BY.po
+++ b/po/be_BY.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-20 15:33+0000\n"
+"POT-Creation-Date: 2026-06-20 16:13+0000\n"
 "PO-Revision-Date: 2021-03-21 08:24+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Belarusian <https://translations.scummvm.org/projects/scummvm/"
diff --git a/po/ca_ES.po b/po/ca_ES.po
index 008a401c7ec..f8d43f4e3ea 100644
--- a/po/ca_ES.po
+++ b/po/ca_ES.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.6.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-20 15:33+0000\n"
+"POT-Creation-Date: 2026-06-20 16:13+0000\n"
 "PO-Revision-Date: 2026-05-26 04:22+0000\n"
 "Last-Translator: Xaviu <xaviu at xaviu.com>\n"
 "Language-Team: Catalan <https://translations.scummvm.org/projects/scummvm/"
diff --git a/po/cs_CZ.po b/po/cs_CZ.po
index 70c33edc20d..3e112ad825b 100644
--- a/po/cs_CZ.po
+++ b/po/cs_CZ.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.7.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-20 15:33+0000\n"
+"POT-Creation-Date: 2026-06-20 16:13+0000\n"
 "PO-Revision-Date: 2026-02-09 20:22+0000\n"
 "Last-Translator: AsciiWolf <mail at asciiwolf.com>\n"
 "Language-Team: Czech <https://translations.scummvm.org/projects/scummvm/"
diff --git a/po/da.po b/po/da.po
index 2470246dc7b..a679546e091 100644
--- a/po/da.po
+++ b/po/da.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-20 15:33+0000\n"
+"POT-Creation-Date: 2026-06-20 16:13+0000\n"
 "PO-Revision-Date: 2023-09-07 17:04+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Danish <https://translations.scummvm.org/projects/scummvm/"
diff --git a/po/de_DE.po b/po/de_DE.po
index f8530613a81..2c9295f6733 100644
--- a/po/de_DE.po
+++ b/po/de_DE.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.10.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-20 15:33+0000\n"
+"POT-Creation-Date: 2026-06-20 16:13+0000\n"
 "PO-Revision-Date: 2026-05-05 16:31+0000\n"
 "Last-Translator: Lothar Serra Mari <lothar.serramari at scummvm.org>\n"
 "Language-Team: German <https://translations.scummvm.org/projects/scummvm/"
diff --git a/po/el.po b/po/el.po
index b58ca3b1587..a5bc1ee5557 100644
--- a/po/el.po
+++ b/po/el.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.10.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-20 15:33+0000\n"
+"POT-Creation-Date: 2026-06-20 16:13+0000\n"
 "PO-Revision-Date: 2026-04-23 00:22+0000\n"
 "Last-Translator: Antoniou Athanasios <a.antoniou79 at gmail.com>\n"
 "Language-Team: Greek <https://translations.scummvm.org/projects/scummvm/"
diff --git a/po/es_ES.po b/po/es_ES.po
index 3627f23b583..d89d6ac1f07 100644
--- a/po/es_ES.po
+++ b/po/es_ES.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.4.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-20 15:33+0000\n"
+"POT-Creation-Date: 2026-06-20 16:13+0000\n"
 "PO-Revision-Date: 2026-06-20 15:53+0000\n"
 "Last-Translator: IlDucci <pajaroloco_2 at hotmail.com>\n"
 "Language-Team: Spanish <https://translations.scummvm.org/projects/scummvm/"
diff --git a/po/eu.po b/po/eu.po
index 6fcdae924be..90cb9cbb81a 100644
--- a/po/eu.po
+++ b/po/eu.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.5.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-20 15:33+0000\n"
+"POT-Creation-Date: 2026-06-20 16:13+0000\n"
 "PO-Revision-Date: 2024-02-05 18:42+0000\n"
 "Last-Translator: Josu Igoa <josuigoa at ni.eus>\n"
 "Language-Team: Basque <https://translations.scummvm.org/projects/scummvm/"
diff --git a/po/fi_FI.po b/po/fi_FI.po
index ebfa577ad5a..194e8f12895 100644
--- a/po/fi_FI.po
+++ b/po/fi_FI.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.6.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-20 15:33+0000\n"
+"POT-Creation-Date: 2026-06-20 16:13+0000\n"
 "PO-Revision-Date: 2026-02-07 15:22+0000\n"
 "Last-Translator: Timo Mikkolainen <tmikkola at gmail.com>\n"
 "Language-Team: Finnish <https://translations.scummvm.org/projects/scummvm/"
diff --git a/po/fr_FR.po b/po/fr_FR.po
index b2bcdb0cf5e..019d007e379 100644
--- a/po/fr_FR.po
+++ b/po/fr_FR.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-20 15:33+0000\n"
+"POT-Creation-Date: 2026-06-20 16:13+0000\n"
 "PO-Revision-Date: 2026-06-14 09:41+0000\n"
 "Last-Translator: Purple T <ZEONK at hotmail.com>\n"
 "Language-Team: French <https://translations.scummvm.org/projects/scummvm/"
diff --git a/po/gl_ES.po b/po/gl_ES.po
index e0dc1066fd7..ef489309447 100644
--- a/po/gl_ES.po
+++ b/po/gl_ES.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-20 15:33+0000\n"
+"POT-Creation-Date: 2026-06-20 16:13+0000\n"
 "PO-Revision-Date: 2020-09-26 21:13+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Galician <https://translations.scummvm.org/projects/scummvm/"
diff --git a/po/he.po b/po/he.po
index 87cc827d16f..7c820c60687 100644
--- a/po/he.po
+++ b/po/he.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.1.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-20 15:33+0000\n"
+"POT-Creation-Date: 2026-06-20 16:13+0000\n"
 "PO-Revision-Date: 2025-04-09 20:18+0000\n"
 "Last-Translator: Niv Baehr <bloop93 at gmail.com>\n"
 "Language-Team: Hebrew <https://translations.scummvm.org/projects/scummvm/"
diff --git a/po/hi.po b/po/hi.po
index a543983fb3f..e3a82a715c9 100644
--- a/po/hi.po
+++ b/po/hi.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-20 15:33+0000\n"
+"POT-Creation-Date: 2026-06-20 16:13+0000\n"
 "PO-Revision-Date: 2026-05-12 22:22+0000\n"
 "Last-Translator: KARTIK SAXENA <kartiksaxena2005 at gmail.com>\n"
 "Language-Team: Hindi <https://translations.scummvm.org/projects/scummvm/"
diff --git a/po/hu_HU.po b/po/hu_HU.po
index cd9b1018e99..8f5153c4ac4 100644
--- a/po/hu_HU.po
+++ b/po/hu_HU.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-20 15:33+0000\n"
+"POT-Creation-Date: 2026-06-20 16:13+0000\n"
 "PO-Revision-Date: 2025-03-07 00:18+0000\n"
 "Last-Translator: Hov??nszki Tam??s <gahex68379 at lofiey.com>\n"
 "Language-Team: Hungarian <https://translations.scummvm.org/projects/scummvm/"
diff --git a/po/it_IT.po b/po/it_IT.po
index c6144d93f2d..d2d39a131ff 100644
--- a/po/it_IT.po
+++ b/po/it_IT.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-20 15:33+0000\n"
+"POT-Creation-Date: 2026-06-20 16:13+0000\n"
 "PO-Revision-Date: 2026-06-20 15:07+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Italian <https://translations.scummvm.org/projects/scummvm/"
diff --git a/po/ja.po b/po/ja.po
index b901f02435e..6bb5662d831 100644
--- a/po/ja.po
+++ b/po/ja.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-20 15:33+0000\n"
+"POT-Creation-Date: 2026-06-20 16:13+0000\n"
 "PO-Revision-Date: 2026-06-04 07:22+0000\n"
 "Last-Translator: VAN-Gluon <van.gluonp at gmail.com>\n"
 "Language-Team: Japanese <https://translations.scummvm.org/projects/scummvm/"
diff --git a/po/ka.po b/po/ka.po
index 1efa4bdcd9b..74367fb8177 100644
--- a/po/ka.po
+++ b/po/ka.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-20 15:33+0000\n"
+"POT-Creation-Date: 2026-06-20 16:13+0000\n"
 "PO-Revision-Date: 2026-06-05 10:22+0000\n"
 "Last-Translator: NorwayFun <temuri.doghonadze at gmail.com>\n"
 "Language-Team: Georgian <https://translations.scummvm.org/projects/scummvm/"
diff --git a/po/ko.po b/po/ko.po
index aaaf69990c5..3ece3c58d98 100644
--- a/po/ko.po
+++ b/po/ko.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-20 15:33+0000\n"
+"POT-Creation-Date: 2026-06-20 16:13+0000\n"
 "PO-Revision-Date: 2026-05-19 20:22+0000\n"
 "Last-Translator: Hoseok Seo <ddinghoya at gmail.com>\n"
 "Language-Team: Korean <https://translations.scummvm.org/projects/scummvm/"
diff --git a/po/nb_NO.po b/po/nb_NO.po
index e7f073d386d..a17d9e66422 100644
--- a/po/nb_NO.po
+++ b/po/nb_NO.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-20 15:33+0000\n"
+"POT-Creation-Date: 2026-06-20 16:13+0000\n"
 "PO-Revision-Date: 2026-02-07 15:22+0000\n"
 "Last-Translator: Einar Johan Tr??an S??m??en <einarjohants at gmail.com>\n"
 "Language-Team: Norwegian Bokm??l <https://translations.scummvm.org/projects/"
diff --git a/po/nl_NL.po b/po/nl_NL.po
index cff511c9003..5b20f7de201 100644
--- a/po/nl_NL.po
+++ b/po/nl_NL.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.9.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-20 15:33+0000\n"
+"POT-Creation-Date: 2026-06-20 16:13+0000\n"
 "PO-Revision-Date: 2026-02-11 14:23+0000\n"
 "Last-Translator: SecularSteve <fairfull.playing at gmail.com>\n"
 "Language-Team: Dutch <https://translations.scummvm.org/projects/scummvm/"
diff --git a/po/nn_NO.po b/po/nn_NO.po
index 55306434652..8a3ec8f300a 100644
--- a/po/nn_NO.po
+++ b/po/nn_NO.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-20 15:33+0000\n"
+"POT-Creation-Date: 2026-06-20 16:13+0000\n"
 "PO-Revision-Date: 2023-12-14 14:28+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Norwegian Nynorsk <https://translations.scummvm.org/projects/"
diff --git a/po/pl_PL.po b/po/pl_PL.po
index 3708ea58cab..e46b987e27f 100644
--- a/po/pl_PL.po
+++ b/po/pl_PL.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-20 15:33+0000\n"
+"POT-Creation-Date: 2026-06-20 16:13+0000\n"
 "PO-Revision-Date: 2026-04-17 00:22+0000\n"
 "Last-Translator: SkiffPL <skiff at windowslive.com>\n"
 "Language-Team: Polish <https://translations.scummvm.org/projects/scummvm/"
diff --git a/po/pt_BR.po b/po/pt_BR.po
index 5bcae19c6e1..69c32478d02 100644
--- a/po/pt_BR.po
+++ b/po/pt_BR.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-20 15:33+0000\n"
+"POT-Creation-Date: 2026-06-20 16:13+0000\n"
 "PO-Revision-Date: 2026-06-17 22:10+0000\n"
 "Last-Translator: Marcel Souza Lemes <marcosoutsider at gmail.com>\n"
 "Language-Team: Portuguese (Brazil) <https://translations.scummvm.org/"
diff --git a/po/pt_PT.po b/po/pt_PT.po
index 5a4813e1871..0c488810827 100644
--- a/po/pt_PT.po
+++ b/po/pt_PT.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.10.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-20 15:33+0000\n"
+"POT-Creation-Date: 2026-06-20 16:13+0000\n"
 "PO-Revision-Date: 2022-06-05 14:09+0000\n"
 "Last-Translator: Daniel <supsuper at gmail.com>\n"
 "Language-Team: Portuguese (Portugal) <https://translations.scummvm.org/"
diff --git a/po/ro.po b/po/ro.po
index 47b3e99b097..efc4b74ac47 100644
--- a/po/ro.po
+++ b/po/ro.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.10.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-20 15:33+0000\n"
+"POT-Creation-Date: 2026-06-20 16:13+0000\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: Automatically generated\n"
 "Language-Team: none\n"
diff --git a/po/ru_RU.po b/po/ru_RU.po
index d6e54e838ca..81e4d635647 100644
--- a/po/ru_RU.po
+++ b/po/ru_RU.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.8.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-20 15:33+0000\n"
+"POT-Creation-Date: 2026-06-20 16:13+0000\n"
 "PO-Revision-Date: 2026-06-20 15:03+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Russian <https://translations.scummvm.org/projects/scummvm/"
diff --git a/po/sv_SE.po b/po/sv_SE.po
index 0538a41a5e4..bcfe49c93d6 100644
--- a/po/sv_SE.po
+++ b/po/sv_SE.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.5.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-20 15:33+0000\n"
+"POT-Creation-Date: 2026-06-20 16:13+0000\n"
 "PO-Revision-Date: 2026-04-19 13:22+0000\n"
 "Last-Translator: Daniel Nylander <po at danielnylander.se>\n"
 "Language-Team: Swedish <https://translations.scummvm.org/projects/scummvm/"
diff --git a/po/tr.po b/po/tr.po
index 7ea0e0ef293..27c86ce7c8f 100644
--- a/po/tr.po
+++ b/po/tr.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-20 15:33+0000\n"
+"POT-Creation-Date: 2026-06-20 16:13+0000\n"
 "PO-Revision-Date: 2024-10-10 12:33+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Turkish <https://translations.scummvm.org/projects/scummvm/"
diff --git a/po/uk_UA.po b/po/uk_UA.po
index c6ff7548907..b4479d80a1d 100644
--- a/po/uk_UA.po
+++ b/po/uk_UA.po
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.9.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-20 15:33+0000\n"
+"POT-Creation-Date: 2026-06-20 16:13+0000\n"
 "PO-Revision-Date: 2026-06-20 14:56+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Ukrainian <https://translations.scummvm.org/projects/scummvm/"
diff --git a/po/zh.po b/po/zh.po
index b313f1d4f36..543f71af138 100644
--- a/po/zh.po
+++ b/po/zh.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-20 15:33+0000\n"
+"POT-Creation-Date: 2026-06-20 16:13+0000\n"
 "PO-Revision-Date: 2024-05-14 12:42+0000\n"
 "Last-Translator: ?????? <clonewith at qq.com>\n"
 "Language-Team: Chinese <https://translations.scummvm.org/projects/scummvm/"
diff --git a/po/zh_Hans.po b/po/zh_Hans.po
index e475863c38e..4d7687dfb2b 100644
--- a/po/zh_Hans.po
+++ b/po/zh_Hans.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.9.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-20 15:33+0000\n"
+"POT-Creation-Date: 2026-06-20 16:13+0000\n"
 "PO-Revision-Date: 2024-12-01 22:18+0000\n"
 "Last-Translator: ?????? <clonewith at qq.com>\n"
 "Language-Team: Chinese (Simplified Han script) <https://"
diff --git a/po/zh_Hant.po b/po/zh_Hant.po
index a5d123a46f1..293a689662e 100644
--- a/po/zh_Hant.po
+++ b/po/zh_Hant.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.9.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-20 15:33+0000\n"
+"POT-Creation-Date: 2026-06-20 16:13+0000\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: Automatically generated\n"
 "Language-Team: none\n"



From noreply at scummvm.org  Sat Jun 20 18:47:29 2026
From: noreply at scummvm.org (whoozle)
Date: Sat, 20 Jun 2026 18:47:29 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 bbb683616952a5c28345c14cafe383147c0f71ed
Message-ID: <20260620184729.5E9CE7005CC@ron.scummvm.net>

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

Summary:
bbb6836169 PHOENIXVR: Add Dracula Resurrection variants


Commit: bbb683616952a5c28345c14cafe383147c0f71ed
    https://github.com/scummvm/scummvm/commit/bbb683616952a5c28345c14cafe383147c0f71ed
Author: Scorp (scorp at mrs.mn)
Date: 2026-06-20T19:47:24+01:00

Commit Message:
PHOENIXVR: Add Dracula Resurrection variants

Changed paths:
    engines/phoenixvr/detection_tables.h


diff --git a/engines/phoenixvr/detection_tables.h b/engines/phoenixvr/detection_tables.h
index bcfc6d010ba..8b9ae77a1fe 100644
--- a/engines/phoenixvr/detection_tables.h
+++ b/engines/phoenixvr/detection_tables.h
@@ -210,13 +210,43 @@ const ADGameDescription gameDescriptions[] = {
 	{"dracula1",
 		nullptr,
 		AD_ENTRY2s("script.lst", "78060b78cf403ddb7e22903ba7b269d6", 548,
-				   "Compiler.dat", "0f330d5d674bf01a1bc8512e4c5321cc", 58688),
+				   "Interface.vr", "b51691a530ec7398bed8c0c094885225", 85245),
 		Common::EN_ANY,
 		Common::kPlatformWindows,
 		ADGF_DROPPLATFORM,
 		GUIO1(GUIO_NONE)
 	},
 
+	{"dracula1",
+		nullptr,
+		AD_ENTRY2s("script.pak", "6998262fcce9cb5d3cc8a555f8ee024a", 265,
+				   "Interface.vr", "b51691a530ec7398bed8c0c094885225", 85245),
+		Common::EN_ANY,
+		Common::kPlatformWindows,
+		ADGF_DROPPLATFORM,
+		GUIO1(GUIO_NONE)
+	},
+
+	{"dracula1",
+		nullptr,
+		AD_ENTRY2s("script.pak", "6998262fcce9cb5d3cc8a555f8ee024a", 265,
+				   "Interface.vr", "5d7d770a74d6035957c7deffc4865e12", 106762),
+		Common::FR_FRA,
+		Common::kPlatformWindows,
+		ADGF_DROPPLATFORM,
+		GUIO1(GUIO_NONE)
+	},
+
+	{"dracula1",
+		nullptr,
+		AD_ENTRY2s("script.pak", "6998262fcce9cb5d3cc8a555f8ee024a", 265,
+				   "Interface.vr", "47099471f31f794be8a7e5a3c382bf62", 104304),
+		Common::IT_ITA,
+		Common::kPlatformWindows,
+		ADGF_DROPPLATFORM,
+		GUIO1(GUIO_NONE)
+	},
+
 	{"dracula2",
 		nullptr,
 		AD_ENTRY2s("script.pak", "ff52d2000eddc5c438f3c8ef50fea858", 277,



From noreply at scummvm.org  Sat Jun 20 20:10:39 2026
From: noreply at scummvm.org (sev-)
Date: Sat, 20 Jun 2026 20:10:39 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm-web master ->
 63cdd599f8c9d17a1709295a9593863ef3c6a04f
Message-ID: <20260620201039.64D6E7005CC@ron.scummvm.net>

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

Summary:
28e02e20e1 WEB: Bump the release to 2026.3.0
63cdd599f8 NEWS: Announce 2026.3.0


Commit: 28e02e20e1ff1041239849f9b30c8d7c16ca0df2
    https://github.com/scummvm/scummvm-web/commit/28e02e20e1ff1041239849f9b30c8d7c16ca0df2
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2026-06-20T18:29:29+02:00

Commit Message:
WEB: Bump the release to 2026.3.0

Changed paths:
    include/Constants.php


diff --git a/include/Constants.php b/include/Constants.php
index a28b204f..83b641d3 100644
--- a/include/Constants.php
+++ b/include/Constants.php
@@ -6,7 +6,7 @@ class Constants
     public function __construct()
     {
         /* Current version. */
-        define('RELEASE', '2026.2.0');
+        define('RELEASE', '2026.3.0');
         define('RELEASE_TOOLS', '2.9.0');
 
         /* News items on the front page. */


Commit: 63cdd599f8c9d17a1709295a9593863ef3c6a04f
    https://github.com/scummvm/scummvm-web/commit/63cdd599f8c9d17a1709295a9593863ef3c6a04f
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2026-06-20T18:30:08+02:00

Commit Message:
NEWS: Announce 2026.3.0

Changed paths:
  A data/en/news/20260620.markdown


diff --git a/data/en/news/20260620.markdown b/data/en/news/20260620.markdown
new file mode 100644
index 00000000..272df287
--- /dev/null
+++ b/data/en/news/20260620.markdown
@@ -0,0 +1,36 @@
+---
+title: ScummVM 2026.3.0 "Carousels & Killer Whales" comes to fruition
+author: sev
+date: 1781972533
+---
+
+So far, we are able to keep the release cadence. This quarter, the team is bringing you a set of new and old, all exciting features!
+
+## Newly Supported Games:
+* _Nancy Drew: The Haunted Carousel_
+* _Nancy Drew: Danger on Deception Island_
+* _Noctropolis_
+* _Cartoon Carnival_
+* _Alfred Pelrock: En Busca de un Sue??o_
+* _Pilot Brothers: On the Track of Striped Elephant_
+* _Pilot Brothers: The Case of Serial Maniac_ and 5 more games on the same engine
+
+
+## General enhancements
+
+This was the period of the Google Summer of Code start, so we had an influx of new contributors, who were busy with improving our GUI, adding small and big quality-of-life features, such as improved search, kinetic scrolling, and revamped About dialog.
+
+## Engine bugfixes and improvements
+
+Ten supported engines got their tweaks and improvements. _Martian Memorandum_ got script bug fixes, _Simon 2_ multilanguage support became better, there is a new whole control panel for the GLK engine, allowing to adjust text rendering, _Last Express_ had some corner case bugs squashed, which also happened to the _Rex Nebular_. _Might and Magic 1_ had major rewiring of its keymapper, and the enhanced patch support plays better than ever.
+
+There was active work on the _Nancy_ engine, and besides the two newly added games (number 8 and 9), most of the already supported games in the series received their bug fixes (thank you very much for submitting bug reports).
+
+Two new games landed to the SLUDGE engine, and we made them downloadable from our website. And last but not least, the TwinE engine, used for the _Little Big Adventure_ series, got its own measure of bugs fixed.
+
+On the ports side, the Atari port is an ongoing constant series of big and small optimizations, and the PlayStation 3 port is easier on the memory consumption, allowing heavier games to be played.
+
+For more detailed itemization of the improvements, you may refer to the [release notes](https://downloads.scummvm.org/frs/scummvm/2026.3.0/ReleaseNotes.html).
+
+
+And as per our new release procedure, the port builds will be arriving within the next few weeks.



From noreply at scummvm.org  Sat Jun 20 20:57:10 2026
From: noreply at scummvm.org (lotharsm)
Date: Sat, 20 Jun 2026 20:57:10 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 8565733a81e543e4849c57a7741a89ecf87648ca
Message-ID: <20260620205710.29E0E7005CC@ron.scummvm.net>

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

Summary:
8565733a81 DISTS: Update Sparkle manifest for 2026.3.0


Commit: 8565733a81e543e4849c57a7741a89ecf87648ca
    https://github.com/scummvm/scummvm/commit/8565733a81e543e4849c57a7741a89ecf87648ca
Author: Lothar Serra Mari (mail at serra.me)
Date: 2026-06-20T22:56:38+02:00

Commit Message:
DISTS: Update Sparkle manifest for 2026.3.0

Changed paths:
    dists/macosx/scummvm_appcast.xml


diff --git a/dists/macosx/scummvm_appcast.xml b/dists/macosx/scummvm_appcast.xml
index 60522bcee52..e3ebc15c6e5 100644
--- a/dists/macosx/scummvm_appcast.xml
+++ b/dists/macosx/scummvm_appcast.xml
@@ -6,20 +6,20 @@
 		<description>Most recent changes with links to updates.</description>
 		<language>en</language>
 		<item>
-			<title>Version 2026.2.0</title>
+			<title>Version 2026.3.0</title>
 			<sparkle:releaseNotesLink>
-				https://downloads.scummvm.org/frs/scummvm/2026.2.0/ReleaseNotes.html
+				https://downloads.scummvm.org/frs/scummvm/2026.3.0/ReleaseNotes.html
 			</sparkle:releaseNotesLink>
-			<pubDate>Sat, 28 Mar 2026 18:00:00 +0000</pubDate>
-			<enclosure url="https://downloads.scummvm.org/frs/scummvm/2026.2.0/scummvm-2026.2.0-macosx.dmg"
-						sparkle:version="2026.2.0" length="161130545" type="application/octet-stream"
-						sparkle:edSignature="MvuGgkXkwPys6TmciZ6Wg6Dq9S8aO0CggXpUw104y5eIooV0jjFE9ip82Z/kMploviYZmmK6UGSLYZrfdAejDw=="
-						sparkle:dsaSignature="MC4CFQCBHVR1ZWkeUH4vObmvqDOiL3CBOgIVAL6vI9sZpwfsCgxTN/FNJJjukRFD"/>
+			<pubDate>Sat, 20 Jun 2026 18:00:00 +0000</pubDate>
+			<enclosure url="https://downloads.scummvm.org/frs/scummvm/2026.3.0/scummvm-2026.3.0-macosx.dmg"
+						sparkle:version="2026.3.0" length="163659840" type="application/octet-stream"
+						sparkle:edSignature="GfJ7GMDYIaM/xmYiYskI6pUqY0H1BPSVQhffoOiFrHe9kBimX+7VaR9FNcJw1+wEILB1IIfZVV7YzJN38l8PDg=="
+						sparkle:dsaSignature="MC0CFHyP5EI+gE/RRNJ1dKB6H+7YKgZEAhUAjsVQqFvAPovx2YGkEkztEUM9eJU="/>
 
 			<enclosure sparkle:os="windows"
-						url="https://downloads.scummvm.org/frs/scummvm/2026.2.0/scummvm-2026.2.0-win32.exe"
-						sparkle:version="2026.2.0" length="133990788" type="application/octet-stream"
-						sparkle:dsaSignature="MCwCFAvSxlv6TdM11BSzIonvW8iiDYrZAhQ5bNZTKgQEhWY8gkfxcu4ee1OXKw=="/>
+						url="https://downloads.scummvm.org/frs/scummvm/2026.3.0/scummvm-2026.3.0-win32.exe"
+						sparkle:version="2026.3.0" length="135554503" type="application/octet-stream"
+						sparkle:dsaSignature="MCwCFAH9n2yGgANWfWPDkygkfcif8AXVAhQvujCO6pmFN8BNTAEi5vZoeTLfVQ=="/>
 		</item>
 	</channel>
 </rss>



From noreply at scummvm.org  Sat Jun 20 20:57:59 2026
From: noreply at scummvm.org (lotharsm)
Date: Sat, 20 Jun 2026 20:57:59 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm-web master ->
 5f9bf336efb51eb0a19dcfc8f8c67fcc0cf717c4
Message-ID: <20260620205759.F26397005CC@ron.scummvm.net>

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

Summary:
5f9bf336ef WEB: Sync Sparkle appcast file


Commit: 5f9bf336efb51eb0a19dcfc8f8c67fcc0cf717c4
    https://github.com/scummvm/scummvm-web/commit/5f9bf336efb51eb0a19dcfc8f8c67fcc0cf717c4
Author: Lothar Serra Mari (mail at serra.me)
Date: 2026-06-20T22:57:45+02:00

Commit Message:
WEB: Sync Sparkle appcast file

Changed paths:
    public_html/appcasts/macosx/release.xml


diff --git a/public_html/appcasts/macosx/release.xml b/public_html/appcasts/macosx/release.xml
index 60522bce..e3ebc15c 100644
--- a/public_html/appcasts/macosx/release.xml
+++ b/public_html/appcasts/macosx/release.xml
@@ -6,20 +6,20 @@
 		<description>Most recent changes with links to updates.</description>
 		<language>en</language>
 		<item>
-			<title>Version 2026.2.0</title>
+			<title>Version 2026.3.0</title>
 			<sparkle:releaseNotesLink>
-				https://downloads.scummvm.org/frs/scummvm/2026.2.0/ReleaseNotes.html
+				https://downloads.scummvm.org/frs/scummvm/2026.3.0/ReleaseNotes.html
 			</sparkle:releaseNotesLink>
-			<pubDate>Sat, 28 Mar 2026 18:00:00 +0000</pubDate>
-			<enclosure url="https://downloads.scummvm.org/frs/scummvm/2026.2.0/scummvm-2026.2.0-macosx.dmg"
-						sparkle:version="2026.2.0" length="161130545" type="application/octet-stream"
-						sparkle:edSignature="MvuGgkXkwPys6TmciZ6Wg6Dq9S8aO0CggXpUw104y5eIooV0jjFE9ip82Z/kMploviYZmmK6UGSLYZrfdAejDw=="
-						sparkle:dsaSignature="MC4CFQCBHVR1ZWkeUH4vObmvqDOiL3CBOgIVAL6vI9sZpwfsCgxTN/FNJJjukRFD"/>
+			<pubDate>Sat, 20 Jun 2026 18:00:00 +0000</pubDate>
+			<enclosure url="https://downloads.scummvm.org/frs/scummvm/2026.3.0/scummvm-2026.3.0-macosx.dmg"
+						sparkle:version="2026.3.0" length="163659840" type="application/octet-stream"
+						sparkle:edSignature="GfJ7GMDYIaM/xmYiYskI6pUqY0H1BPSVQhffoOiFrHe9kBimX+7VaR9FNcJw1+wEILB1IIfZVV7YzJN38l8PDg=="
+						sparkle:dsaSignature="MC0CFHyP5EI+gE/RRNJ1dKB6H+7YKgZEAhUAjsVQqFvAPovx2YGkEkztEUM9eJU="/>
 
 			<enclosure sparkle:os="windows"
-						url="https://downloads.scummvm.org/frs/scummvm/2026.2.0/scummvm-2026.2.0-win32.exe"
-						sparkle:version="2026.2.0" length="133990788" type="application/octet-stream"
-						sparkle:dsaSignature="MCwCFAvSxlv6TdM11BSzIonvW8iiDYrZAhQ5bNZTKgQEhWY8gkfxcu4ee1OXKw=="/>
+						url="https://downloads.scummvm.org/frs/scummvm/2026.3.0/scummvm-2026.3.0-win32.exe"
+						sparkle:version="2026.3.0" length="135554503" type="application/octet-stream"
+						sparkle:dsaSignature="MCwCFAH9n2yGgANWfWPDkygkfcif8AXVAhQvujCO6pmFN8BNTAEi5vZoeTLfVQ=="/>
 		</item>
 	</channel>
 </rss>



From noreply at scummvm.org  Sat Jun 20 21:04:32 2026
From: noreply at scummvm.org (lotharsm)
Date: Sat, 20 Jun 2026 21:04:32 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm-web master ->
 e135b7e97e6fd23414859020f1680e91552ba581
Message-ID: <20260620210432.C56E07005CC@ron.scummvm.net>

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

Summary:
7738ec11b8 L10N: Update base news translation file
e135b7e97e DATA: Update data files


Commit: 7738ec11b898c6c73a0f52245bec372855fea391
    https://github.com/scummvm/scummvm-web/commit/7738ec11b898c6c73a0f52245bec372855fea391
Author: lotharsm (mail at serra.me)
Date: 2026-06-20T21:04:27Z

Commit Message:
L10N: Update base news translation file

Changed paths:
    data/en/news.json


diff --git a/data/en/news.json b/data/en/news.json
index 52e8f093..312c9c9e 100644
--- a/data/en/news.json
+++ b/data/en/news.json
@@ -1762,5 +1762,9 @@
     "20260617": {
         "title": "ScummVM on track for striped patience",
         "content": "ScummVM has officially wrapped up on Gamos???s Windows proprietary game engine. The engine originally powered _Pilot Brothers 1 & 2_, launching the iconic adventure series before it later transitioned to qdEngine and NGI tech. The engine was also widely used to build a wave of office time-killer games.\n\nThis engine adds support for 7 games:\n\n* _Patience_\n* _Flip-Flop_\n* _NetWalk_\n* _Pilot Brothers: On the Track of Striped Elephant_\n* _Pilot Brothers: The Case of Serial Maniac_\n* _Vitamin_\n* _WildSnake_\n\nSo, if you would like to remember the past and spend some time with two eccentric detectives, Chief and Colleague, or kill some time with Solitaire-style games, you need the latest [daily development build](https://www.scummvm.org/downloads/#daily) and the game???s required data files. As always, if you encounter any irregularities or oddities, please submit those to our [bug tracker](https://bugs.scummvm.org/)."
+    },
+    "20260620": {
+        "title": "ScummVM 2026.3.0 \"Carousels & Killer Whales\" comes to fruition",
+        "content": "So far, we are able to keep the release cadence. This quarter, the team is bringing you a set of new and old, all exciting features!\n\n## Newly Supported Games:\n* _Nancy Drew: The Haunted Carousel_\n* _Nancy Drew: Danger on Deception Island_\n* _Noctropolis_\n* _Cartoon Carnival_\n* _Alfred Pelrock: En Busca de un Sue??o_\n* _Pilot Brothers: On the Track of Striped Elephant_\n* _Pilot Brothers: The Case of Serial Maniac_ and 5 more games on the same engine\n\n\n## General enhancements\n\nThis was the period of the Google Summer of Code start, so we had an influx of new contributors, who were busy with improving our GUI, adding small and big quality-of-life features, such as improved search, kinetic scrolling, and revamped About dialog.\n\n## Engine bugfixes and improvements\n\nTen supported engines got their tweaks and improvements. _Martian Memorandum_ got script bug fixes, _Simon 2_ multilanguage support became better, there is a new whole control panel for the GLK engine, allowing to adjust text rendering, _Last Express_ had some corner case bugs squashed, which also happened to the _Rex Nebular_. _Might and Magic 1_ had major rewiring of its keymapper, and the enhanced patch support plays better than ever.\n\nThere was active work on the _Nancy_ engine, and besides the two newly added games (number 8 and 9), most of the already supported games in the series received their bug fixes (thank you very much for submitting bug reports).\n\nTwo new games landed to the SLUDGE engine, and we made them downloadable from our website. And last but not least, the TwinE engine, used for the _Little Big Adventure_ series, got its own measure of bugs fixed.\n\nOn the ports side, the Atari port is an ongoing constant series of big and small optimizations, and the PlayStation 3 port is easier on the memory consumption, allowing heavier games to be played.\n\nFor more detailed itemization of the improvements, you may refer to the [release notes](https://downloads.scummvm.org/frs/scummvm/2026.3.0/ReleaseNotes.html).\n\n\nAnd as per our new release procedure, the port builds will be arriving within the next few weeks."
     }
 }


Commit: e135b7e97e6fd23414859020f1680e91552ba581
    https://github.com/scummvm/scummvm-web/commit/e135b7e97e6fd23414859020f1680e91552ba581
Author: lotharsm (mail at serra.me)
Date: 2026-06-20T21:04:28Z

Commit Message:
DATA: Update data files

Changed paths:
    data/en/compatibility.yaml
    data/en/scummvm_downloads.yaml
    data/en/versions.yaml


diff --git a/data/en/compatibility.yaml b/data/en/compatibility.yaml
index 1c76aed5..3aafddb4 100644
--- a/data/en/compatibility.yaml
+++ b/data/en/compatibility.yaml
@@ -5771,83 +5771,83 @@
     id: 'nancy:nancy8'
     support: good
     notes: ''
-    since_version: DEV
+    since_version: 2026.3.0
     stable_platforms: win
     unstable_platforms: ''
 -
     id: 'nancy:nancy9'
     support: good
     notes: ''
-    since_version: DEV
+    since_version: 2026.3.0
     stable_platforms: win
     unstable_platforms: ''
 -
     id: 'access:noctropolis'
     support: good
     notes: '- Only English version is supported'
-    since_version: DEV
+    since_version: 2026.3.0
     stable_platforms: win
     unstable_platforms: ''
 -
     id: 'bolt:carnival'
     support: excellent
     notes: '- Only Windows/Macintosh version is supported'
-    since_version: DEV
+    since_version: 2026.3.0
     stable_platforms: 'win,mac'
     unstable_platforms: ''
 -
     id: 'pelrock:pelrock'
     support: good
     notes: ''
-    since_version: DEV
+    since_version: 2026.3.0
     stable_platforms: dos
     unstable_platforms: ''
 -
     id: 'gamos:solgamer'
     support: good
     notes: ''
-    since_version: DEV
+    since_version: 2026.3.0
     stable_platforms: win
     unstable_platforms: ''
 -
     id: 'gamos:pilots1'
     support: good
     notes: '- Only Windows version is supported - GOG/Steam versions are not supported'
-    since_version: DEV
+    since_version: 2026.3.0
     stable_platforms: win
     unstable_platforms: ''
 -
     id: 'gamos:pilots2'
     support: good
     notes: '- Only Windows version is supported - GOG/Steam versions are not supported'
-    since_version: DEV
+    since_version: 2026.3.0
     stable_platforms: win
     unstable_platforms: ''
 -
     id: 'gamos:wild'
     support: good
     notes: ''
-    since_version: DEV
+    since_version: 2026.3.0
     stable_platforms: win
     unstable_platforms: ''
 -
     id: 'gamos:flop'
     support: good
     notes: ''
-    since_version: DEV
+    since_version: 2026.3.0
     stable_platforms: win
     unstable_platforms: ''
 -
     id: 'gamos:netwalk'
     support: good
     notes: ''
-    since_version: DEV
+    since_version: 2026.3.0
     stable_platforms: win
     unstable_platforms: ''
 -
     id: 'gamos:vitamin'
     support: good
     notes: ''
-    since_version: DEV
+    since_version: 2026.3.0
     stable_platforms: win
     unstable_platforms: ''
diff --git a/data/en/scummvm_downloads.yaml b/data/en/scummvm_downloads.yaml
index f3e2cc4e..08192f4b 100644
--- a/data/en/scummvm_downloads.yaml
+++ b/data/en/scummvm_downloads.yaml
@@ -5,7 +5,7 @@
     subcategory: release
     category_icon: windows
     user_agent: Windows
-    version: 2026.2.0
+    version: 2026.3.0
     url: 'scummvm-{$version}-win32.exe'
     name: 'Windows Installer (32 and 64 bit)'
     notes: ''
@@ -15,7 +15,7 @@
     subcategory: release
     category_icon: windows
     user_agent: ''
-    version: 2026.2.0
+    version: 2026.3.0
     url: 'scummvm-{$version}-win32-x86_64.zip'
     name: 'Windows zipfile (64 bit)'
     notes: ''
@@ -25,7 +25,7 @@
     subcategory: release
     category_icon: windows
     user_agent: ''
-    version: 2026.2.0
+    version: 2026.3.0
     url: 'scummvm-{$version}-win32.zip'
     name: 'Windows zipfile (32 bit)'
     notes: ''
@@ -72,7 +72,7 @@
 -
     enabled: true
     category: scummvm
-    subcategory: release
+    subcategory: old
     category_icon: snap
     user_agent: Ubuntu
     version: 2026.2.0
@@ -82,7 +82,7 @@
 -
     enabled: true
     category: scummvm
-    subcategory: release
+    subcategory: old
     category_icon: flatpak
     user_agent: ''
     version: 2026.2.0
@@ -92,7 +92,7 @@
 -
     enabled: true
     category: scummvm
-    subcategory: release
+    subcategory: old
     category_icon: debian
     user_agent: Debian
     version: 2026.2.0
@@ -102,7 +102,7 @@
 -
     enabled: true
     category: scummvm
-    subcategory: release
+    subcategory: old
     category_icon: debian
     user_agent: ''
     version: 2026.2.0
@@ -112,7 +112,7 @@
 -
     enabled: true
     category: scummvm
-    subcategory: release
+    subcategory: old
     category_icon: debian
     user_agent: ''
     version: 2026.2.0
@@ -122,7 +122,7 @@
 -
     enabled: true
     category: scummvm
-    subcategory: release
+    subcategory: old
     category_icon: debian
     user_agent: ''
     version: 2026.2.0
@@ -132,7 +132,7 @@
 -
     enabled: true
     category: scummvm
-    subcategory: release
+    subcategory: old
     category_icon: debian
     user_agent: ''
     version: 2026.2.0
@@ -192,7 +192,7 @@
 -
     enabled: true
     category: scummvm
-    subcategory: release
+    subcategory: old
     category_icon: ubuntu
     user_agent: ''
     version: 2026.2.0
@@ -202,7 +202,7 @@
 -
     enabled: true
     category: scummvm
-    subcategory: release
+    subcategory: old
     category_icon: ubuntu
     user_agent: ''
     version: 2026.2.0
@@ -212,7 +212,7 @@
 -
     enabled: true
     category: scummvm
-    subcategory: release
+    subcategory: old
     category_icon: ubuntu
     user_agent: ''
     version: 2026.2.0
@@ -222,7 +222,7 @@
 -
     enabled: true
     category: scummvm
-    subcategory: release
+    subcategory: old
     category_icon: ubuntu
     user_agent: ''
     version: 2026.2.0
@@ -355,14 +355,14 @@
     subcategory: release
     category_icon: macos
     user_agent: Mac
-    version: 2026.2.0
+    version: 2026.3.0
     url: 'scummvm-{$version}-macosx.dmg'
     name: 'macOS 10.13+ for Intel and Apple Silicon, with Sparkle Disk Image'
     notes: ''
 -
     enabled: true
     category: scummvm
-    subcategory: release
+    subcategory: old
     category_icon: macos
     user_agent: ''
     version: 2026.2.0
@@ -372,7 +372,7 @@
 -
     enabled: true
     category: scummvm
-    subcategory: release
+    subcategory: old
     category_icon: macostiger
     user_agent: ''
     version: 2026.2.0
@@ -382,7 +382,7 @@
 -
     enabled: true
     category: scummvm
-    subcategory: release
+    subcategory: old
     category_icon: macosppc
     user_agent: ''
     version: 2026.2.0
@@ -412,7 +412,7 @@
 -
     enabled: true
     category: scummvm
-    subcategory: release
+    subcategory: old
     category_icon: ps3
     user_agent: ''
     version: 2026.2.0
@@ -432,7 +432,7 @@
 -
     enabled: true
     category: scummvm
-    subcategory: release
+    subcategory: old
     category_icon: psp
     user_agent: ''
     version: 2026.2.0
@@ -442,7 +442,7 @@
 -
     enabled: true
     category: scummvm
-    subcategory: release
+    subcategory: old
     category_icon: psp2
     user_agent: 'PlayStation Portable'
     version: 2026.2.0
@@ -452,7 +452,7 @@
 -
     enabled: true
     category: scummvm
-    subcategory: release
+    subcategory: old
     category_icon: android
     user_agent: Android
     version: 2026.2.0
@@ -462,7 +462,7 @@
 -
     enabled: true
     category: scummvm
-    subcategory: release
+    subcategory: old
     category_icon: android
     user_agent: ''
     version: 2026.2.0
@@ -472,7 +472,7 @@
 -
     enabled: true
     category: scummvm
-    subcategory: release
+    subcategory: old
     category_icon: android
     user_agent: ''
     version: 2026.2.0
@@ -482,7 +482,7 @@
 -
     enabled: true
     category: scummvm
-    subcategory: release
+    subcategory: old
     category_icon: android
     user_agent: ''
     version: 2026.2.0
@@ -492,7 +492,7 @@
 -
     enabled: true
     category: scummvm
-    subcategory: release
+    subcategory: old
     category_icon: android
     user_agent: ''
     version: 2026.2.0
@@ -502,7 +502,7 @@
 -
     enabled: true
     category: scummvm
-    subcategory: release
+    subcategory: old
     category_icon: android
     user_agent: ''
     version: 2026.2.0
@@ -612,7 +612,7 @@
 -
     enabled: true
     category: scummvm
-    subcategory: release
+    subcategory: old
     category_icon: iphone
     user_agent: iOS
     version: 2026.2.0
@@ -672,7 +672,7 @@
 -
     enabled: true
     category: scummvm
-    subcategory: release
+    subcategory: old
     category_icon: dc
     user_agent: ''
     version: 2026.2.0
@@ -682,7 +682,7 @@
 -
     enabled: true
     category: scummvm
-    subcategory: release
+    subcategory: old
     category_icon: dc
     user_agent: ''
     version: 2026.2.0
@@ -712,7 +712,7 @@
 -
     enabled: true
     category: scummvm
-    subcategory: release
+    subcategory: old
     category_icon: 3ds
     user_agent: ''
     version: 2026.2.0
@@ -722,7 +722,7 @@
 -
     enabled: true
     category: scummvm
-    subcategory: release
+    subcategory: old
     category_icon: 3ds
     user_agent: ''
     version: 2026.2.0
@@ -762,7 +762,7 @@
 -
     enabled: true
     category: scummvm
-    subcategory: release
+    subcategory: old
     category_icon: switch
     user_agent: ''
     version: 2026.2.0
@@ -832,7 +832,7 @@
 -
     enabled: true
     category: scummvm
-    subcategory: release
+    subcategory: old
     category_icon: dingux
     user_agent: ''
     version: 2026.2.0
@@ -842,7 +842,7 @@
 -
     enabled: true
     category: scummvm
-    subcategory: release
+    subcategory: old
     category_icon: dingux
     user_agent: ''
     version: 2026.2.0
@@ -862,7 +862,7 @@
 -
     enabled: true
     category: scummvm
-    subcategory: release
+    subcategory: old
     category_icon: dingux
     user_agent: ''
     version: 2026.2.0
@@ -1082,7 +1082,7 @@
 -
     enabled: true
     category: scummvm
-    subcategory: release
+    subcategory: old
     category_icon: haiku
     user_agent: ''
     version: 2026.2.0
@@ -1092,7 +1092,7 @@
 -
     enabled: true
     category: scummvm
-    subcategory: release
+    subcategory: old
     category_icon: haiku
     user_agent: ''
     version: 2026.2.0
@@ -1112,7 +1112,7 @@
 -
     enabled: true
     category: scummvm
-    subcategory: release
+    subcategory: old
     category_icon: morphos
     user_agent: ''
     version: 2026.2.0
@@ -1122,7 +1122,7 @@
 -
     enabled: true
     category: scummvm
-    subcategory: release
+    subcategory: old
     category_icon: riscos
     user_agent: 'RISC OS'
     version: 2026.2.0
@@ -1132,7 +1132,7 @@
 -
     enabled: true
     category: scummvm
-    subcategory: release
+    subcategory: old
     category_icon: riscos
     user_agent: ''
     version: 2026.2.0
@@ -1142,7 +1142,7 @@
 -
     enabled: true
     category: scummvm
-    subcategory: release
+    subcategory: old
     category_icon: atari-lite
     user_agent: ''
     version: 2026.2.0
@@ -1152,7 +1152,7 @@
 -
     enabled: true
     category: scummvm
-    subcategory: release
+    subcategory: old
     category_icon: atari-full
     user_agent: ''
     version: 2026.2.0
@@ -1162,7 +1162,7 @@
 -
     enabled: true
     category: scummvm
-    subcategory: release
+    subcategory: old
     category_icon: firebee
     user_agent: ''
     version: 2026.2.0
@@ -1255,7 +1255,7 @@
     subcategory: source
     category_icon: cpp
     user_agent: ''
-    version: 2026.2.0
+    version: 2026.3.0
     url: 'scummvm-{$version}.tar.lz'
     name: 'ScummVM - Source.tar.lz'
     notes: ''
@@ -1265,7 +1265,7 @@
     subcategory: source
     category_icon: cpp
     user_agent: ''
-    version: 2026.2.0
+    version: 2026.3.0
     url: 'scummvm-{$version}.tar.xz'
     name: 'ScummVM - Source.tar.xz'
     notes: ''
@@ -1275,7 +1275,7 @@
     subcategory: source
     category_icon: cpp
     user_agent: ''
-    version: 2026.2.0
+    version: 2026.3.0
     url: 'scummvm-{$version}.tar.bz2'
     name: 'ScummVM - Source.tar.bz2'
     notes: ''
@@ -1285,7 +1285,7 @@
     subcategory: source
     category_icon: cpp
     user_agent: ''
-    version: 2026.2.0
+    version: 2026.3.0
     url: 'scummvm-{$version}.tar.gz'
     name: 'ScummVM - Source.tar.gz'
     notes: ''
@@ -1295,7 +1295,7 @@
     subcategory: source
     category_icon: cpp
     user_agent: ''
-    version: 2026.2.0
+    version: 2026.3.0
     url: 'scummvm-{$version}.zip'
     name: 'ScummVM - Source.zip'
     notes: ''
diff --git a/data/en/versions.yaml b/data/en/versions.yaml
index 7c8af4cc..511ba391 100644
--- a/data/en/versions.yaml
+++ b/data/en/versions.yaml
@@ -391,3 +391,10 @@
     minor: '2'
     patch: '0'
     name: Railmonicon
+-
+    id: 2026.3.0
+    release_date: 'June 20, 2026'
+    major: '2026'
+    minor: '3'
+    patch: '0'
+    name: 'Carousels & Killer Whales'



From noreply at scummvm.org  Sat Jun 20 21:14:20 2026
From: noreply at scummvm.org (sev-)
Date: Sat, 20 Jun 2026 21:14:20 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 e2ef63e84123c199ab55de445e406aa626147e10
Message-ID: <20260620211420.9F6A77005CC@ron.scummvm.net>

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

Summary:
e2ef63e841 NANCY: Fix potentially dangerous code


Commit: e2ef63e84123c199ab55de445e406aa626147e10
    https://github.com/scummvm/scummvm/commit/e2ef63e84123c199ab55de445e406aa626147e10
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2026-06-20T23:14:10+02:00

Commit Message:
NANCY: Fix potentially dangerous code

Changed paths:
    engines/nancy/action/puzzle/sortpuzzle.cpp


diff --git a/engines/nancy/action/puzzle/sortpuzzle.cpp b/engines/nancy/action/puzzle/sortpuzzle.cpp
index 3c14abf371b..38e2772408d 100644
--- a/engines/nancy/action/puzzle/sortpuzzle.cpp
+++ b/engines/nancy/action/puzzle/sortpuzzle.cpp
@@ -293,7 +293,7 @@ void SortPuzzle::handleInput(NancyInput &input) {
 				const Cell &cur = _current[r][c];
 				line += Common::String::format("%d ", cur.value);
 			}
-			debug(line.c_str());
+			debug("%s", line.c_str());
 		}
 		debug("-----");
 	}



From noreply at scummvm.org  Sat Jun 20 21:55:03 2026
From: noreply at scummvm.org (sev-)
Date: Sat, 20 Jun 2026 21:55:03 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 840adc75a87364558ca79b8cf8692106091c8c65
Message-ID: <20260620215503.88E1C7005CC@ron.scummvm.net>

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

Summary:
840adc75a8 PHONEIXVR: Mark drascula1/drascula2 as unstable


Commit: 840adc75a87364558ca79b8cf8692106091c8c65
    https://github.com/scummvm/scummvm/commit/840adc75a87364558ca79b8cf8692106091c8c65
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2026-06-20T23:54:32+02:00

Commit Message:
PHONEIXVR: Mark drascula1/drascula2 as unstable

Changed paths:
    engines/phoenixvr/detection_tables.h


diff --git a/engines/phoenixvr/detection_tables.h b/engines/phoenixvr/detection_tables.h
index 8b9ae77a1fe..9fe9b1466dc 100644
--- a/engines/phoenixvr/detection_tables.h
+++ b/engines/phoenixvr/detection_tables.h
@@ -213,7 +213,7 @@ const ADGameDescription gameDescriptions[] = {
 				   "Interface.vr", "b51691a530ec7398bed8c0c094885225", 85245),
 		Common::EN_ANY,
 		Common::kPlatformWindows,
-		ADGF_DROPPLATFORM,
+		ADGF_DROPPLATFORM | ADGF_UNSTABLE,
 		GUIO1(GUIO_NONE)
 	},
 
@@ -223,7 +223,7 @@ const ADGameDescription gameDescriptions[] = {
 				   "Interface.vr", "b51691a530ec7398bed8c0c094885225", 85245),
 		Common::EN_ANY,
 		Common::kPlatformWindows,
-		ADGF_DROPPLATFORM,
+		ADGF_DROPPLATFORM | ADGF_UNSTABLE,
 		GUIO1(GUIO_NONE)
 	},
 
@@ -233,7 +233,7 @@ const ADGameDescription gameDescriptions[] = {
 				   "Interface.vr", "5d7d770a74d6035957c7deffc4865e12", 106762),
 		Common::FR_FRA,
 		Common::kPlatformWindows,
-		ADGF_DROPPLATFORM,
+		ADGF_DROPPLATFORM | ADGF_UNSTABLE,
 		GUIO1(GUIO_NONE)
 	},
 
@@ -243,7 +243,7 @@ const ADGameDescription gameDescriptions[] = {
 				   "Interface.vr", "47099471f31f794be8a7e5a3c382bf62", 104304),
 		Common::IT_ITA,
 		Common::kPlatformWindows,
-		ADGF_DROPPLATFORM,
+		ADGF_DROPPLATFORM | ADGF_UNSTABLE,
 		GUIO1(GUIO_NONE)
 	},
 
@@ -253,7 +253,7 @@ const ADGameDescription gameDescriptions[] = {
 				   "Compiler.dat", "ff30c7ea065af1e86182b428e1f2cc7b", 42650),
 		Common::EN_ANY,
 		Common::kPlatformWindows,
-		ADGF_DROPPLATFORM,
+		ADGF_DROPPLATFORM | ADGF_UNSTABLE,
 		GUIO1(GUIO_NONE)
 	},
 



From noreply at scummvm.org  Sun Jun 21 02:42:02 2026
From: noreply at scummvm.org (dreammaster)
Date: Sun, 21 Jun 2026 02:42:02 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 1ce640b1a11d5187cb4c780a4ee1e3e49a5f6bd3
Message-ID: <20260621024202.2D5987005CD@ron.scummvm.net>

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:
4bd44c641c MADS: FOREST: global_digi_play is actual global_midi_play
76905fd6af MADS: FOREST: Move digi/midi files into Forest namespace
1ce640b1a1 MADS: FOREST: Skeleton MidiPlayer class


Commit: 4bd44c641c8a672f55947e3dd8d4d9bd10b96032
    https://github.com/scummvm/scummvm/commit/4bd44c641c8a672f55947e3dd8d4d9bd10b96032
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-06-21T12:41:49+10:00

Commit Message:
MADS: FOREST: global_digi_play is actual global_midi_play

Changed paths:
    engines/mads/madsv2/forest/global.cpp
    engines/mads/madsv2/forest/global.h
    engines/mads/madsv2/forest/rooms/room101.cpp
    engines/mads/madsv2/forest/rooms/room103.cpp
    engines/mads/madsv2/forest/rooms/room104.cpp
    engines/mads/madsv2/forest/rooms/room106.cpp
    engines/mads/madsv2/forest/rooms/room107.cpp
    engines/mads/madsv2/forest/rooms/room199.cpp
    engines/mads/madsv2/forest/rooms/room201.cpp
    engines/mads/madsv2/forest/rooms/room203.cpp
    engines/mads/madsv2/forest/rooms/room205.cpp
    engines/mads/madsv2/forest/rooms/room220.cpp
    engines/mads/madsv2/forest/rooms/room304.cpp
    engines/mads/madsv2/forest/rooms/room305.cpp
    engines/mads/madsv2/forest/rooms/room308.cpp
    engines/mads/madsv2/forest/rooms/room321.cpp
    engines/mads/madsv2/forest/rooms/room322.cpp
    engines/mads/madsv2/forest/rooms/room402.cpp
    engines/mads/madsv2/forest/rooms/room403.cpp
    engines/mads/madsv2/forest/rooms/room420.cpp
    engines/mads/madsv2/forest/rooms/room501.cpp
    engines/mads/madsv2/forest/rooms/room503.cpp
    engines/mads/madsv2/forest/rooms/room509.cpp
    engines/mads/madsv2/forest/rooms/room510.cpp
    engines/mads/madsv2/forest/rooms/room520.cpp
    engines/mads/madsv2/forest/rooms/room903.cpp
    engines/mads/madsv2/forest/rooms/room904.cpp


diff --git a/engines/mads/madsv2/forest/global.cpp b/engines/mads/madsv2/forest/global.cpp
index 5b6de4b6fe6..3615775203e 100644
--- a/engines/mads/madsv2/forest/global.cpp
+++ b/engines/mads/madsv2/forest/global.cpp
@@ -985,9 +985,16 @@ void global_error_code() {
 		text_show(text_id);
 }
 
-void global_digi_play(int num) {
-	// TODO
-	warning("TODO: global_digi_play");
+void global_midi_play(int num) {
+	static const char *NAMES[14] = {
+		"adven2", "foolarnd", "homeag", "humorus1", "humorus2", "pianogtr", "raindrop",
+		"xad", "xcarey", "xuspens1", "travels1", "birdsong", "adventur", "action1"
+	};
+
+	assert(num >= 1 && num <= 14);
+	Common::String name = Common::String::format("*%s.hmi", NAMES[num - 1]);
+
+	midi_play(name.c_str());
 }
 
 void global_daemon_code() {
diff --git a/engines/mads/madsv2/forest/global.h b/engines/mads/madsv2/forest/global.h
index 0c47ac2b5b2..5c847f1e4df 100644
--- a/engines/mads/madsv2/forest/global.h
+++ b/engines/mads/madsv2/forest/global.h
@@ -268,7 +268,7 @@ extern void global_error_code();
 extern void global_anim1(int arg_0, int arg_2, int arg_4, int16 *arg_6);
 extern void global_anim2(int arg_0, int arg_2, int arg_4, int16 *arg_6);
 extern void global_anim3(int handle, int16 *frame);
-extern void global_digi_play(int num);
+extern void global_midi_play(int num);
 extern void global_daemon_code();
 
 } // namespace Forest
diff --git a/engines/mads/madsv2/forest/rooms/room101.cpp b/engines/mads/madsv2/forest/rooms/room101.cpp
index 45a43b03e8a..8e60e2a8050 100644
--- a/engines/mads/madsv2/forest/rooms/room101.cpp
+++ b/engines/mads/madsv2/forest/rooms/room101.cpp
@@ -116,7 +116,7 @@ static void room_101_init2() {
 	kernel_position_anim(scratch._a4, 194, 145, 75, 4);
 
 	global[g009] = -1;
-	global_digi_play(8);
+	global_midi_play(8);
 
 	if (previous_room != KERNEL_RESTORING_GAME) {
 		player.x = 96;
@@ -557,7 +557,7 @@ static void room_101_anim10() {
 				scratch._b4 = 35;
 			} else if (frame < 35) {
 				if (frame == 5)
-					global_digi_play(11);
+					global_midi_play(11);
 				else if (frame == 30) {
 					digi_play_build(101, '_', 3, 1);
 					scratch._b4 = 30;
diff --git a/engines/mads/madsv2/forest/rooms/room103.cpp b/engines/mads/madsv2/forest/rooms/room103.cpp
index 89426005151..f4f4b3a16ff 100644
--- a/engines/mads/madsv2/forest/rooms/room103.cpp
+++ b/engines/mads/madsv2/forest/rooms/room103.cpp
@@ -333,7 +333,7 @@ static void room_103_anim4() {
 		} else if (frame < 50) {
 			switch (frame) {
 			case 16:
-				global_digi_play(8);
+				global_midi_play(8);
 				aainfo[3]._val3 = 15;
 				digi_play_build(103, 'c', 1, 1);
 				scratch._90 = 16;
diff --git a/engines/mads/madsv2/forest/rooms/room104.cpp b/engines/mads/madsv2/forest/rooms/room104.cpp
index 7191b997870..71c10cb5336 100644
--- a/engines/mads/madsv2/forest/rooms/room104.cpp
+++ b/engines/mads/madsv2/forest/rooms/room104.cpp
@@ -151,7 +151,7 @@ static void room_104_init() {
 static void room_104_init1() {
 	global[player_score] = 0;
 	global[g009] = -1;
-	global_digi_play(8);
+	global_midi_play(8);
 	viewing_at_y = 22;
 	player.walker_visible = 0;
 	player.commands_allowed = 0;
@@ -186,7 +186,7 @@ static void room_104_init2() {
 
 static void room_104_init3() {
 	global[g009] = -1;
-	global_digi_play(8);
+	global_midi_play(8);
 	global[g131] = 0;
 	global[g141] = 0;
 
@@ -593,7 +593,7 @@ static void room_104_anim3() {
 			aainfo[8]._val3 = 8;
 			digi_play_build(104, 'e', 3, 1);
 			scratch._a2 = 84;
-			global_digi_play(14);
+			global_midi_play(14);
 			break;
 		case 89:
 			if (aainfo[8]._val3 == 8) {
diff --git a/engines/mads/madsv2/forest/rooms/room106.cpp b/engines/mads/madsv2/forest/rooms/room106.cpp
index e54b918ce02..398d276c017 100644
--- a/engines/mads/madsv2/forest/rooms/room106.cpp
+++ b/engines/mads/madsv2/forest/rooms/room106.cpp
@@ -363,7 +363,7 @@ static void room_106_anim4() {
 		return;
 	case 105:
 		global[g009] = 0;
-		global_digi_play(4);
+		global_midi_play(4);
 		global[g154] = 0;
 		aainfo[7]._val3 = 13;
 		aainfo[7]._frame = 20;
diff --git a/engines/mads/madsv2/forest/rooms/room107.cpp b/engines/mads/madsv2/forest/rooms/room107.cpp
index ebf9365e9e6..f762365faeb 100644
--- a/engines/mads/madsv2/forest/rooms/room107.cpp
+++ b/engines/mads/madsv2/forest/rooms/room107.cpp
@@ -120,7 +120,7 @@ static void room_107_anim1() {
 		if (scratch._8e == 21) {
 			aainfo[0]._val3 = 2;
 			scratch._8e = -1;
-			global_digi_play(4);
+			global_midi_play(4);
 			aainfo[0]._frame = 26;
 			kernel_reset_animation(aa[0], 26);
 		}
diff --git a/engines/mads/madsv2/forest/rooms/room199.cpp b/engines/mads/madsv2/forest/rooms/room199.cpp
index e84be3e44d7..87cbfd4a322 100644
--- a/engines/mads/madsv2/forest/rooms/room199.cpp
+++ b/engines/mads/madsv2/forest/rooms/room199.cpp
@@ -102,7 +102,7 @@ static void room_199_init1() {
 static void room_199_init() {
 	global[player_score] = 0;
 	global[g009] = -1;
-	global_digi_play(7);
+	global_midi_play(7);
 
 	aainfo[2]._val3 = -1;
 	aainfo[2]._val4 = 0;
diff --git a/engines/mads/madsv2/forest/rooms/room201.cpp b/engines/mads/madsv2/forest/rooms/room201.cpp
index 31ab0cd044c..9684f3d598b 100644
--- a/engines/mads/madsv2/forest/rooms/room201.cpp
+++ b/engines/mads/madsv2/forest/rooms/room201.cpp
@@ -73,7 +73,7 @@ static Scratch scratch;
 static void room_201_init1() {
 	global[player_score] = -1;
 	global[g009] = -1;
-	global_digi_play(11);
+	global_midi_play(11);
 	global[g131] = 0;
 	global[g141] = 0;
 
diff --git a/engines/mads/madsv2/forest/rooms/room203.cpp b/engines/mads/madsv2/forest/rooms/room203.cpp
index 72638c88bd0..5088c5f7fd9 100644
--- a/engines/mads/madsv2/forest/rooms/room203.cpp
+++ b/engines/mads/madsv2/forest/rooms/room203.cpp
@@ -125,7 +125,7 @@ static void room_203_init1() {
 		aainfo[0]._active = -1;
 		scratch._9a = 11;
 		scratch._92 = 74;
-		global_digi_play(11);
+		global_midi_play(11);
 		scratch._a6 = 5;
 		return;
 	}
@@ -143,7 +143,7 @@ static void room_203_init1() {
 	global[g143] = 0;
 
 	if (global[g046] == 0) {
-		global_digi_play(15);
+		global_midi_play(15);
 		scratch._a6 = 2;
 		aa[4] = kernel_run_animation(kernel_name('w', 4), 105);
 		aainfo[4]._active = -1;
@@ -171,7 +171,7 @@ static void room_203_init1() {
 		kernel_flip_hotspot(119, global[g047] == 0 ? 1 : 0);
 		kernel_flip_hotspot(27, global[g047]);
 	} else {
-		global_digi_play(11);
+		global_midi_play(11);
 		scratch._a6 = 5;
 	}
 }
@@ -256,7 +256,7 @@ static void room_203_anim1() {
 			} else if (frame < 50) {
 				if (frame == 22) {
 					if (local->_a6 != 1) {
-						global_digi_play(10);
+						global_midi_play(10);
 						local->_a6 = 1;
 					}
 					aainfo[4]._frame = 3;
@@ -270,7 +270,7 @@ static void room_203_anim1() {
 
 		if (aainfo[0]._frame == 1) {
 			if (global[g046] == 0 && flags[8] > 1 && local->_a6 != 1) {
-				global_digi_play(10);
+				global_midi_play(10);
 				local->_a6 = 1;
 			}
 		}
@@ -917,12 +917,12 @@ static void room_203_anim5() {
 			result = 27;
 		} else if (frame < 29) {
 			if (frame == 1) {
-				global_digi_play(12);
+				global_midi_play(12);
 				local->_a6 = 4;
 			} else if (frame == 19) {
 				digi_play_build(103, '_', 3, 2);
 				if (local->_a6 != 2) {
-					global_digi_play(15);
+					global_midi_play(15);
 					local->_a6 = 2;
 				}
 			} else if (frame == 22) {
@@ -957,7 +957,7 @@ static void room_203_anim5() {
 			local->_a6 = 0;
 		} else if (frame < 25) {
 			if (frame == 1) {
-				global_digi_play(5);
+				global_midi_play(5);
 				local->_a6 = 3;
 			} else if (frame == 18) {
 				if (!local->_ac && local->_98 == 33) {
@@ -1297,7 +1297,7 @@ static void room_203_daemon() {
 				player.commands_allowed = true;
 			} else {
 				if (local->_a6 != 1) {
-					global_digi_play(10);
+					global_midi_play(10);
 					local->_a6 = 1;
 				}
 
diff --git a/engines/mads/madsv2/forest/rooms/room205.cpp b/engines/mads/madsv2/forest/rooms/room205.cpp
index a6f427c53f2..384865a30d3 100644
--- a/engines/mads/madsv2/forest/rooms/room205.cpp
+++ b/engines/mads/madsv2/forest/rooms/room205.cpp
@@ -253,7 +253,7 @@ static void room_205_anim5() {
 
 	if (aainfo[6]._frame == 135) {
 		global[g009] = 0;
-		global_digi_play(9);
+		global_midi_play(9);
 		kernel_timing_trigger(180, 105);
 	} else if (aainfo[6]._frame == 138) {
 		aainfo[6]._frame = 137;
diff --git a/engines/mads/madsv2/forest/rooms/room220.cpp b/engines/mads/madsv2/forest/rooms/room220.cpp
index 4973007164b..465632497d6 100644
--- a/engines/mads/madsv2/forest/rooms/room220.cpp
+++ b/engines/mads/madsv2/forest/rooms/room220.cpp
@@ -63,7 +63,7 @@ static Scratch scratch;
 static void room_220_init() {
 	global[player_score] = -1;
 	global[g009] = -1;
-	global_digi_play(10);
+	global_midi_play(10);
 	viewing_at_y = 22;
 	player.walker_visible = 0;
 	player.commands_allowed = 0;
@@ -90,7 +90,7 @@ static void room_220_anim1() {
 		kernel_abort_animation(aa[0]);
 		aainfo[0]._active = 0;
 		global[g009] = -1;
-		global_digi_play(15);
+		global_midi_play(15);
 		aa[1] = kernel_run_animation("*RM220Y12", 0);
 		aainfo[1]._active = -1;
 		scratch._92 = 55;
diff --git a/engines/mads/madsv2/forest/rooms/room304.cpp b/engines/mads/madsv2/forest/rooms/room304.cpp
index 70235ac4f5e..ec8ee34447f 100644
--- a/engines/mads/madsv2/forest/rooms/room304.cpp
+++ b/engines/mads/madsv2/forest/rooms/room304.cpp
@@ -273,7 +273,7 @@ static void room_304_anim4() {
 			digi_play_build(304, '_', 3, 2);
 			scratch._a0 = 3;
 			global[g009] = 0;
-			global_digi_play(14);
+			global_midi_play(14);
 		} else if (frame == 11) {
 			digi_stop(2);
 		}
diff --git a/engines/mads/madsv2/forest/rooms/room305.cpp b/engines/mads/madsv2/forest/rooms/room305.cpp
index 468ae49e6e6..ff0d670596e 100644
--- a/engines/mads/madsv2/forest/rooms/room305.cpp
+++ b/engines/mads/madsv2/forest/rooms/room305.cpp
@@ -89,7 +89,7 @@ static void room_305_init1() {
 	if (previous_room == 301) {
 		aa[1] = kernel_run_animation(kernel_name('t', 1), 104);
 		aainfo[1]._active = -1;
-		global_digi_play(14);
+		global_midi_play(14);
 		return;
 	}
 
@@ -243,7 +243,7 @@ static void room_305_init() {
 
 	global[g009] = -1;
 	if (global[g066] == 1)
-		global_digi_play(8);
+		global_midi_play(8);
 }
 
 static void room_305_anim1() {
@@ -484,7 +484,7 @@ static void room_305_anim6() {
 		kernel_timing_trigger(45, 105);
 		scratch._a4++;
 		global[g009] = -1;
-		global_digi_play(6);
+		global_midi_play(6);
 		break;
 	case 15:
 		if (global[g065] != 0) {
diff --git a/engines/mads/madsv2/forest/rooms/room308.cpp b/engines/mads/madsv2/forest/rooms/room308.cpp
index ec247be676f..7ef97d10d99 100644
--- a/engines/mads/madsv2/forest/rooms/room308.cpp
+++ b/engines/mads/madsv2/forest/rooms/room308.cpp
@@ -108,7 +108,7 @@ static void room_308_anim1() {
 		break;
 	case 70:
 		global[g009] = 0;
-		global_digi_play(9);
+		global_midi_play(9);
 		break;
 	case 88:
 		digi_initial_volume(60);
@@ -794,7 +794,7 @@ static void room_308_anim20() {
 	int16 result = -1;
 	if (cur == 6) {
 		global[g009] = 0;
-		global_digi_play(5);
+		global_midi_play(5);
 		digi_play_build(308, 't', 5, 1);
 		scratch._94 = 3;
 		scratch._b4 = 3;
@@ -923,7 +923,7 @@ static void room_308_anim24() {
 			result = imath_random(20, 25);
 	} else if (cur == 29) {
 		global[g009] = 0;
-		global_digi_play(5);
+		global_midi_play(5);
 	}
 
 	if (result >= 0) {
@@ -1055,7 +1055,7 @@ static void room_308_init() {
 			aainfo[1]._frame = -1;
 			kernel_synch(KERNEL_ANIM, ss[7], KERNEL_NOW, 0);
 			global[g009] = -1;
-			global_digi_play(15);
+			global_midi_play(15);
 			player.commands_allowed = 0;
 			return;
 		}
@@ -1070,7 +1070,7 @@ static void room_308_init() {
 			aainfo[1]._val4 = -1;
 			kernel_synch(KERNEL_ANIM, ss[8], KERNEL_NOW, 0);
 			global[g009] = -1;
-			global_digi_play(15);
+			global_midi_play(15);
 			return;
 		}
 		if (player_has(15)) {
@@ -1083,7 +1083,7 @@ static void room_308_init() {
 		aainfo[1]._val4 = -1;
 		kernel_synch(KERNEL_ANIM, ss[8], KERNEL_NOW, 0);
 		global[g009] = -1;
-		global_digi_play(15);
+		global_midi_play(15);
 		return;
 	}
 
@@ -1114,7 +1114,7 @@ static void room_308_init() {
 
 	if (global[g073] == 0) {
 		global[g009] = -1;
-		global_digi_play(15);
+		global_midi_play(15);
 		return;
 	}
 
@@ -1125,7 +1125,7 @@ static void room_308_init() {
 	aainfo[8]._frame = 0;
 	scratch._c2 = 100;
 	global[g009] = -1;
-	global_digi_play(15);
+	global_midi_play(15);
 }
 
 static void room_308_daemon() {
@@ -1176,7 +1176,7 @@ static void room_308_daemon() {
 		aainfo[0]._val4 = -1;
 		kernel_synch(KERNEL_ANIM, ss[6], KERNEL_NOW, 0);
 		global[g009] = -1;
-		global_digi_play(15);
+		global_midi_play(15);
 		break;
 
 	case 102:
diff --git a/engines/mads/madsv2/forest/rooms/room321.cpp b/engines/mads/madsv2/forest/rooms/room321.cpp
index bbf9024206f..fc47eafab3e 100644
--- a/engines/mads/madsv2/forest/rooms/room321.cpp
+++ b/engines/mads/madsv2/forest/rooms/room321.cpp
@@ -155,9 +155,9 @@ static void room_321_init() {
 		kernel_seq_loc(seq[4], 230, 105);
 		kernel_seq_scale(seq[4], 83);
 		kernel_flip_hotspot(177, 0);
-		global_digi_play(13);
+		global_midi_play(13);
 	} else {
-		global_digi_play(3);
+		global_midi_play(3);
 	}
 
 	global[g009] = -1;
diff --git a/engines/mads/madsv2/forest/rooms/room322.cpp b/engines/mads/madsv2/forest/rooms/room322.cpp
index 6fbecfcff86..c510d1402e7 100644
--- a/engines/mads/madsv2/forest/rooms/room322.cpp
+++ b/engines/mads/madsv2/forest/rooms/room322.cpp
@@ -339,7 +339,7 @@ static void room_322_anim8() {
 		digi_play_build(322, '_', 1, 2);
 	} else if (cur == 27) {
 		global[g009] = 0;
-		global_digi_play(4);
+		global_midi_play(4);
 	}
 
 	if (result >= 0) {
diff --git a/engines/mads/madsv2/forest/rooms/room402.cpp b/engines/mads/madsv2/forest/rooms/room402.cpp
index cbee1bfb2c0..f1387102cf6 100644
--- a/engines/mads/madsv2/forest/rooms/room402.cpp
+++ b/engines/mads/madsv2/forest/rooms/room402.cpp
@@ -168,7 +168,7 @@ static void room_402_init1() {
 	}
 
 	global[player_score] = 0;
-	global_digi_play(15);
+	global_midi_play(15);
 }
 
 static void room_402_init() {
@@ -356,7 +356,7 @@ static void room_402_anim_state() {
 		kernel_flip_hotspot(187, 0);
 		break;
 	case 17:
-		global_digi_play(5);
+		global_midi_play(5);
 		global[g009] = 0;
 		aa[1] = kernel_run_animation(kernel_name('L', 13), 103);
 		aainfo[1]._active = -1;
@@ -584,7 +584,7 @@ static void room_402_anim2() {
 	case 20: {
 		int16 frame = aainfo[1]._frame;
 		if (frame == 2) {
-			global_digi_play(15);
+			global_midi_play(15);
 			player.commands_allowed = -1;
 			global[g083] = 0;
 			room_402_anim6();
@@ -612,7 +612,7 @@ static void room_402_anim2() {
 	case 2: {
 		int16 frame = aainfo[1]._frame;
 		if (frame == 57) {
-			global_digi_play(10);
+			global_midi_play(10);
 			global[player_score] = 0;
 		} else if (frame < 57) {
 			if (frame == 37) {
diff --git a/engines/mads/madsv2/forest/rooms/room403.cpp b/engines/mads/madsv2/forest/rooms/room403.cpp
index d777d24502b..d35ee2533fa 100644
--- a/engines/mads/madsv2/forest/rooms/room403.cpp
+++ b/engines/mads/madsv2/forest/rooms/room403.cpp
@@ -438,7 +438,7 @@ static void room_403_anim6() {
 			aainfo[5]._frame = 12;
 			kernel_reset_animation(aa[5], 12);
 			scratch._a4 = -1;
-			global_digi_play(14);
+			global_midi_play(14);
 			kernel_seq_delete(seq[1]);
 			global[g080] = -1;
 		} else if (a4 == 89) {
diff --git a/engines/mads/madsv2/forest/rooms/room420.cpp b/engines/mads/madsv2/forest/rooms/room420.cpp
index ed5f1ee07ca..b0a53052ea0 100644
--- a/engines/mads/madsv2/forest/rooms/room420.cpp
+++ b/engines/mads/madsv2/forest/rooms/room420.cpp
@@ -113,7 +113,7 @@ static void room_420_anim2() {
 					scratch._8e = 34;
 					scratch.animation_info[1]._val3 = 10;
 					digi_play_build(420, 'b', 1, 1);
-					global_digi_play(3);
+					global_midi_play(3);
 				} else if (cur == 39) {
 					if (scratch.animation_info[1]._val3 == 10) {
 						scratch.animation_info[1]._frame = 34;
diff --git a/engines/mads/madsv2/forest/rooms/room501.cpp b/engines/mads/madsv2/forest/rooms/room501.cpp
index e8ed6ca0d89..ed46140e7b8 100644
--- a/engines/mads/madsv2/forest/rooms/room501.cpp
+++ b/engines/mads/madsv2/forest/rooms/room501.cpp
@@ -356,7 +356,7 @@ static void room_501_anim5() {
 			digi_play_build(501, 'e', 1, 1);
 			scratch._a4 = 3;
 		} else if (scratch._a4 == 3) {
-			global_digi_play(14);
+			global_midi_play(14);
 			global[g156] = 0;
 			aainfo[1]._val3 = 23;
 			aainfo[1]._frame = 105;
diff --git a/engines/mads/madsv2/forest/rooms/room503.cpp b/engines/mads/madsv2/forest/rooms/room503.cpp
index 26ed0aa44e7..35bd8fb3393 100644
--- a/engines/mads/madsv2/forest/rooms/room503.cpp
+++ b/engines/mads/madsv2/forest/rooms/room503.cpp
@@ -144,7 +144,7 @@ static void room_503_init1() {
 
 	if (previous_room == 199) {
 		if (global[g100] != 0) {
-			global_digi_play(14);
+			global_midi_play(14);
 			viewing_at_y = 22;
 			scratch._ba = 2;
 			aainfo[0]._active = kernel_run_animation(kernel_name('N', 2), 106);
@@ -182,7 +182,7 @@ static void room_503_init1() {
 			scratch._b8 = 1;
 		}
 
-		global_digi_play(6);
+		global_midi_play(6);
 		aa[0] = kernel_run_animation(kernel_name('y', 2), 100);
 		aainfo[0]._val3 = -1;
 		scratch._a6 = 52;
@@ -207,7 +207,7 @@ static void room_503_init3() {
 
 	if (previous_room == 199) {
 		if (global[g100] != 0) {
-			global_digi_play(14);
+			global_midi_play(14);
 			viewing_at_y = 22;
 			scratch._ba = 2;
 			aainfo[0]._active = kernel_run_animation(kernel_name('N', 2), 106);
diff --git a/engines/mads/madsv2/forest/rooms/room509.cpp b/engines/mads/madsv2/forest/rooms/room509.cpp
index 480dbdc7665..10ff60818a0 100644
--- a/engines/mads/madsv2/forest/rooms/room509.cpp
+++ b/engines/mads/madsv2/forest/rooms/room509.cpp
@@ -63,7 +63,7 @@ static void room_509_init() {
 	mouse_hide();
 	global[player_score] = 0;
 	global[g009] = -1;
-	global_digi_play(15);
+	global_midi_play(15);
 	global[g009] = -1;
 	viewing_at_y = 22;
 	global[player_score] = 0;
diff --git a/engines/mads/madsv2/forest/rooms/room510.cpp b/engines/mads/madsv2/forest/rooms/room510.cpp
index 5627f2ef5cc..213430b7c43 100644
--- a/engines/mads/madsv2/forest/rooms/room510.cpp
+++ b/engines/mads/madsv2/forest/rooms/room510.cpp
@@ -226,7 +226,7 @@ static void room_510_anim3() {
 			digi_play_build(510, '_', 1, 2);
 			scratch._90 = 1;
 			midi_stop();
-			global_digi_play(4);
+			global_midi_play(4);
 		} else if (aa_frame == 40) {
 			midi_stop();
 			new_room = 107;
@@ -288,7 +288,7 @@ static void room_510_daemon() {
 		if (scratch._8e == 668) {
 			scratch._8e = -1;
 			global[g009] = 0;
-			global_digi_play(14);
+			global_midi_play(14);
 			kernel_timing_trigger(2100, 104);
 		}
 		break;
@@ -314,7 +314,7 @@ static void room_510_daemon() {
 		break;
 	case 104:
 		global[g009] = -1;
-		global_digi_play(11);
+		global_midi_play(11);
 		break;
 	case 106:
 		scratch._96 = -1;
diff --git a/engines/mads/madsv2/forest/rooms/room520.cpp b/engines/mads/madsv2/forest/rooms/room520.cpp
index 4c44a7458df..13d6596dcc9 100644
--- a/engines/mads/madsv2/forest/rooms/room520.cpp
+++ b/engines/mads/madsv2/forest/rooms/room520.cpp
@@ -130,7 +130,7 @@ static void room_520_daemon() {
 		scratch._8c = 66;
 		break;
 	case 101:
-		global_digi_play(14);
+		global_midi_play(14);
 		break;
 	}
 
diff --git a/engines/mads/madsv2/forest/rooms/room903.cpp b/engines/mads/madsv2/forest/rooms/room903.cpp
index 6d17b2af68b..af19e94920a 100644
--- a/engines/mads/madsv2/forest/rooms/room903.cpp
+++ b/engines/mads/madsv2/forest/rooms/room903.cpp
@@ -53,7 +53,7 @@ static void room_903_init() {
 
 static void room_903_daemon() {
 	if (kernel.trigger == TRIGGER1)
-		global_digi_play(14);
+		global_midi_play(14);
 
 	if (mouse_any_stroke || g_engine->hasPendingKey() || kernel.trigger == TRIGGER0) {
 		g_engine->flushKeys();
diff --git a/engines/mads/madsv2/forest/rooms/room904.cpp b/engines/mads/madsv2/forest/rooms/room904.cpp
index 5a08adbba09..bfc535ce269 100644
--- a/engines/mads/madsv2/forest/rooms/room904.cpp
+++ b/engines/mads/madsv2/forest/rooms/room904.cpp
@@ -88,7 +88,7 @@ static void room_904_seen_intro() {
 
 static void room_904_init() {
 	last_keypressed = -1;
-	global_digi_play(3);
+	global_midi_play(3);
 	aainfo[1]._active = 0;
 	aainfo[1]._frame = 0;
 	global[g009] = -1;


Commit: 76905fd6afab17eaec7c88c6f7aba61cb9df1bce
    https://github.com/scummvm/scummvm/commit/76905fd6afab17eaec7c88c6f7aba61cb9df1bce
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-06-21T12:41:50+10:00

Commit Message:
MADS: FOREST: Move digi/midi files into Forest namespace

It's the only game that uses them, so it's best to be clear.

Changed paths:
  A engines/mads/madsv2/forest/digi.cpp
  A engines/mads/madsv2/forest/digi.h
  A engines/mads/madsv2/forest/midi.cpp
  A engines/mads/madsv2/forest/midi.h
  R engines/mads/madsv2/core/digi.cpp
  R engines/mads/madsv2/core/digi.h
  R engines/mads/madsv2/core/midi.cpp
  R engines/mads/madsv2/core/midi.h
  R engines/mads/madsv2/forest/asound.cpp
  R engines/mads/madsv2/forest/asound.h
    engines/mads/madsv2/forest/global.cpp
    engines/mads/madsv2/forest/rooms/room101.cpp
    engines/mads/madsv2/forest/rooms/room103.cpp
    engines/mads/madsv2/forest/rooms/room104.cpp
    engines/mads/madsv2/forest/rooms/room106.cpp
    engines/mads/madsv2/forest/rooms/room107.cpp
    engines/mads/madsv2/forest/rooms/room199.cpp
    engines/mads/madsv2/forest/rooms/room201.cpp
    engines/mads/madsv2/forest/rooms/room203.cpp
    engines/mads/madsv2/forest/rooms/room204.cpp
    engines/mads/madsv2/forest/rooms/room205.cpp
    engines/mads/madsv2/forest/rooms/room210.cpp
    engines/mads/madsv2/forest/rooms/room211.cpp
    engines/mads/madsv2/forest/rooms/room220.cpp
    engines/mads/madsv2/forest/rooms/room301.cpp
    engines/mads/madsv2/forest/rooms/room302.cpp
    engines/mads/madsv2/forest/rooms/room303.cpp
    engines/mads/madsv2/forest/rooms/room304.cpp
    engines/mads/madsv2/forest/rooms/room305.cpp
    engines/mads/madsv2/forest/rooms/room306.cpp
    engines/mads/madsv2/forest/rooms/room307.cpp
    engines/mads/madsv2/forest/rooms/room308.cpp
    engines/mads/madsv2/forest/rooms/room321.cpp
    engines/mads/madsv2/forest/rooms/room322.cpp
    engines/mads/madsv2/forest/rooms/room401.cpp
    engines/mads/madsv2/forest/rooms/room402.cpp
    engines/mads/madsv2/forest/rooms/room403.cpp
    engines/mads/madsv2/forest/rooms/room404.cpp
    engines/mads/madsv2/forest/rooms/room405.cpp
    engines/mads/madsv2/forest/rooms/room420.cpp
    engines/mads/madsv2/forest/rooms/room501.cpp
    engines/mads/madsv2/forest/rooms/room503.cpp
    engines/mads/madsv2/forest/rooms/room509.cpp
    engines/mads/madsv2/forest/rooms/room510.cpp
    engines/mads/madsv2/forest/rooms/room520.cpp
    engines/mads/madsv2/forest/rooms/room904.cpp
    engines/mads/module.mk


diff --git a/engines/mads/madsv2/forest/asound.cpp b/engines/mads/madsv2/forest/asound.cpp
deleted file mode 100644
index 70022f4fdb8..00000000000
--- a/engines/mads/madsv2/forest/asound.cpp
+++ /dev/null
@@ -1,1805 +0,0 @@
-/* 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 "audio/fmopl.h"
-#include "mads/madsv2/forest/asound.h"
-
-namespace MADS {
-namespace MADSV2 {
-namespace Forest {
-
-
-bool AdlibChannel::_isDisabled;
-
-/*
-  * PATCH_ATTEN_TO_TL  (seg001:0x0092 / offset from _asound_samples base)
-  * patchAttenuation (0-127) -> 6-bit OPL total-level.
-  * The Dragonsphere asm uses PATCH_ATTEN_TO_TL[bx] for the modulator lookup
-  * and unk_12431 - bx (i.e. PATCH_ATTEN_TO_TL[127 - patchAtt]) for the
-  * carrier lookup.
-  */
-static const uint8 PATCH_ATTEN_TO_TL[128] = {
-	63, 54, 49, 45, 42, 40, 38, 36, 34, 33, 32, 31, 30, 29, 28, 27,
-	26, 25, 25, 24, 23, 23, 22, 22, 21, 21, 20, 20, 19, 19, 18, 18,
-	18, 17, 17, 16, 16, 16, 15, 15, 15, 14, 14, 14, 14, 13, 13, 13,
-	12, 12, 12, 12, 11, 11, 11, 11, 11, 10, 10, 10, 10,  9,  9,  9,
-	 9,  9,  8,  8,  8,  8,  8,  7,  7,  7,  7,  7,  7,  6,  6,  6,
-	 6,  6,  6,  5,  5,  5,  5,  5,  5,  5,  4,  4,  4,  4,  4,  4,
-	 4,  3,  3,  3,  3,  3,  3,  3,  3,  2,  2,  2,  2,  2,  2,  2,
-	 2,  2,  1,  1,  1,  1,  1,  1,  1,  1,  1,  0,  0,  0,  0,  0
-};
-
-/*
- * VOL_VEL_TO_ATTEN_STEP  (_volVelToAttenStep)
- * volume or velocity byte (0-127) -> attenuation step (1-32).
- * Groups of 4 inputs map to the same step.
- */
-static const uint8 VOL_VEL_TO_ATTEN_STEP[128] = {
-	 1, 1, 1, 1,  2, 2, 2, 2,  3, 3, 3, 3,  4, 4, 4, 4,
-	 5, 5, 5, 5,  6, 6, 6, 6,  7, 7, 7, 7,  8, 8, 8, 8,
-	 9, 9, 9, 9, 10,10,10,10, 11,11,11,11, 12,12,12,12,
-	13,13,13,13, 14,14,14,14, 15,15,15,15, 16,16,16,16,
-	17,17,17,17, 18,18,18,18, 19,19,19,19, 20,20,20,20,
-	21,21,21,21, 22,22,22,22, 23,23,23,23, 24,24,24,24,
-	25,25,25,25, 26,26,26,26, 27,27,27,27, 28,28,28,28,
-	29,29,29,29, 30,30,30,30, 31,31,31,31, 32,32,32,32
-};
-
-/*
- * SEMITONE_FREQ_TABLE  (_semitoneFreqTable)
- * OPL F-number base for each semitone within an octave.
- */
-static const uint16 SEMITONE_FREQ_TABLE[12] = {
-	0x0200, 0x021E, 0x023F, 0x0261, 0x0285, 0x02AB,
-	0x02D4, 0x02FF, 0x032D, 0x035D, 0x0390, 0x03C7
-};
-
-/*
- * VOICE_SLOTS  (byte_1239B in the binary, also used as the operator-reg
- * index table for command6/7)
- *
- * Layout for each voice: { slot0 (modulator), slot1 (carrier) }
- * The writeVolume loop uses:
- *   pass 0 -> VOICE_SLOTS[ch][0]  (modulator)
- *   pass 1 -> VOICE_SLOTS[ch][1]  (carrier)
- * The alg!=0 single-op path (loc_11692) goes directly to VOICE_SLOTS[ch][1].
- */
-static const uint8 VOICE_SLOTS[ADLIB_CHANNEL_COUNT][2] = {
-	{  0,  3 }, {  1,  4 }, {  2,  5 },
-	{  6,  9 }, {  7, 10 }, {  8, 11 },
-	{ 12, 15 }, { 13, 16 }, { 14, 17 }
-};
-
-/*
- * SLOT_TO_REG_OFFSET  (_slotToRegOffset)
- * operator-slot index (0-17) -> OPL register group offset.
- */
-static const uint8 SLOT_TO_REG_OFFSET[18] = {
-	 0,  1,  2,  3,  4,  5,
-	 8,  9, 10, 11, 12, 13,
-	16, 17, 18, 19, 20, 21
-};
-
-/*
- * byte_1239B  - all 22 operator TL register indices muted/restored by
- * command6 and command7.  They cover every OPL operator slot (0x40-0x55).
- */
-static const uint8 ALL_OP_TL_REGS[22] = {
-	0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
-	0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F,
-	0x50, 0x51, 0x52, 0x53, 0x54, 0x55
-};
-
-/* Null / silence sound-data placeholder stream. */
-static uint8 ADLIB_NULLDATA[] = {
-	0x00, 0x01, 0xF3, 0x00, 0x00, 0xEF, 0x00, 0x00,
-	0xF5, 0x00, 0x00, 0x00, 0xF7, 0x00, 0xF8, 0x1D,
-	0xFF, 0x00, 0xF4, 0x6E, 0x2A, 0x1C, 0xF4, 0x5F,
-	0x2A, 0x1C, 0x2A, 0x1C, 0x2A, 0x1C, 0xFF, 0xFF,
-	0x00, 0x00, 0x00, 0x00
-};
-
-/* OPL version flag (_adlib_v5660 / opl_version_flag).
- * 0x18 = OPL3 (patch-attenuation-aware writeVolume path). */
-constexpr uint16 OPL_VERSION_FLAG = 0x18;
-
-/* Rhythm-mode flags (from data segment). */
-constexpr bool RHYTHM_HI_HAT = true;
-constexpr bool RHYTHM_CYMBAL = true;
-constexpr bool RHYTHM_ENABLE = true;
-
-AdlibSample::AdlibSample(Common::SeekableReadStream &s) {
-	_attackRate = s.readByte();
-	_decayRate = s.readByte();
-	_sustainLevel = s.readByte();
-	_releaseRate = s.readByte();
-	_egTyp = s.readByte();
-	_ksr = s.readByte();
-	_totalLevel = s.readByte();
-	_scalingLevel = s.readByte();
-	_waveformSelect = s.readByte();
-	_freqMultiple = s.readByte();
-	_feedback = s.readByte();
-	_ampMod = s.readByte();
-	_vib = s.readByte();
-	_alg = s.readByte();
-	_freqSweepInit = s.readByte();
-	_reserved = s.readByte();
-	_freqMask = s.readUint16LE();
-	_freqBase = s.readUint16LE();
-	_outerLoopPtr = s.readUint16LE();
-}
-
-void AdlibChannel::reset() {
-	/* Zero the scalar "live" fields only.
-	 *
-	 * From AdlibChannel_reset in the binary: clears _activeCount, _pitchBend,
-	 * _volumeFadeStep, _vibratoDepth, _arpCounterReload (field_13),
-	 * _writeVolumePending (field_11), _transpose, _patchAttenuation,
-	 * _pendingStop, _velocity, _volume, _freqSweepCounter, _noiseFreqMask,
-	 * _freqAccum, _freqStep, _loopStartPtr, _pSrc.
-	 * All other fields are left as-is (they are overwritten by load() or
-	 * by the bytecode stream before being read).
-	 */
-	_activeCount = 0;
-	_pitchBend = 0;
-	_volumeFadeStep = 0;
-	_vibratoDepth = 0;
-	_arpCounterReload = 0;   /* field_13 */
-	_writeVolumePending = 0;   /* field_11 */
-	_transpose = 0;
-	_patchAttenuation = 0;
-	_pendingStop = 0;
-	_velocity = 0;
-	_volume = 0;
-	_freqSweepCounter = 0;
-	_noiseFreqMask = 0;
-	_freqAccum = 0;
-	_freqStep = 0;
-	_loopStartPtr = nullptr;
-	_pSrc = nullptr;
-}
-
-void AdlibChannel::load(byte *soundData) {
-	_isDisabled = true;
-
-	/* Zero all bytes in the struct. */
-	memset(this, 0, sizeof(AdlibChannel));
-
-	/* Initialise all loop/source pointers to the supplied data block. */
-	_loopStartPtr = soundData;
-	_pSrc = soundData;
-	_innerLoopPtr = soundData;
-	_outerLoopPtr = soundData;
-	_soundData = soundData;
-
-	/* Start at maximum attenuation (silent) and mark the channel active. */
-	_patchAttenuation = 0x40;
-	_activeCount = 1;
-
-	_isDisabled = false;
-}
-
-void AdlibChannel::setPtr2(byte *ptr) {
-	/* Hard-redirect both read pointers, then arm a one-step fade. */
-	_loopStartPtr = ptr;
-	_pSrc = ptr;
-	_volumeFadeStep = 0xFF;   /* -1 signed: fade down */
-	_fadePeriodReload = 1;
-	_fadePeriodCounter = 1;
-}
-
-void AdlibChannel::enable() {
-	/* AdlibChannel_enable: mark pending-stop on active channels only. */
-	if (_activeCount == 0)
-		return;
-	_pendingStop = 0xFF;
-	_soundData = ADLIB_NULLDATA;
-}
-
-void AdlibChannel::processChannelFade() {
-	if (_activeCount == 0)
-		return;
-	if (_pendingStop == 0)
-		return;
-
-	/* Channel has fully faded when both velocity and volume are zero. */
-	if (_velocity == 0 && _volume == 0) {
-		_loopStartPtr = ADLIB_NULLDATA;
-		_pSrc = ADLIB_NULLDATA;
-		_pendingStop = 0;
-		return;
-	}
-
-	/* Arm a quick fade-down (period 2, step -1). */
-	_volumeFadeStep = 0xFF;
-	_fadePeriodReload = 2;
-	if (_fadePeriodCounter == 0)
-		_fadePeriodCounter = 1;
-}
-
-ASound::ASound(Audio::Mixer *mixer, OPL::OPL *opl, const Common::Path &filename,
-	int dataOffset, int dataSize)
-	: SoundDriver(mixer, opl, filename, dataOffset, dataSize) {
-	AdlibChannel::_isDisabled = false;
-
-	/* Standard OPL timer-reset sequence. */
-	write(4, 0x60);
-	write(4, 0x80);
-	write(2, 0xFF);
-	write(4, 0x21);
-	write(4, 0x60);
-	write(4, 0x80);
-
-	Common::fill(_adlibPorts, _adlibPorts + 256, 0);
-	command0();
-
-	_opl->start(new Common::Functor0Mem<void, ASound>(this, &ASound::onTimer),
-		CALLBACKS_PER_SECOND);
-}
-
-int ASound::stop() {
-	command0();
-	int result = _pollResult;
-	_pollResult = 0;
-	return result;
-}
-
-int ASound::poll() {
-	update();
-	int result = _pollResult;
-	_pollResult = 0;
-	return result;
-}
-
-void ASound::noise() {
-	Common::StackLock slock(_driverMutex);
-	for (int i = 0; i < ADLIB_CHANNEL_COUNT; ++i)
-		noise_inner(i);
-}
-
-int ASound::command0() {
-	/* Push _isDisabled, temporarily force to 0xFFFF while resetting. */
-	uint16 savedDisabled = _isDisabled;
-	_isDisabled = 0xFFFF;
-
-	/* 1. Reset all 9 channels. */
-	for (int i = 0; i < ADLIB_CHANNEL_COUNT; ++i)
-		_channels[i]->reset();
-
-	/* 2. Mute all operator TL registers (0x40 down to 0x4F, via adlib_write2
-	 *    with value 0x3F, stepping from 0x4F down while reg >= 0x40). */
-	for (int reg = 0x4F; reg >= 0x40; --reg)
-		write((uint8)reg, 0x3F);
-
-	/* 3. Zero registers 0xFF down to 0x60. */
-	for (int reg = 0xFF; reg >= 0x60; --reg)
-		write((uint8)reg, 0x00);
-
-	/* 4. Zero registers 0x3F down to 0x01. */
-	for (int reg = 0x3F; reg >= 0x01; --reg)
-		write((uint8)reg, 0x00);
-
-	/* 5. Waveform Select Enable. */
-	write(0x01, 0x20);
-
-	/* 6. Reset tick callback. */
-	resetCallback();
-
-	_isDisabled = savedDisabled;
-	return 0;
-}
-
-int ASound::command1() {
-	/* Fade all channels: music 0-6 (command3) then SFX 7-8 (command5). */
-	command3();
-	command5();
-	return 0;
-}
-
-int ASound::command2() {
-	/* Hard-redirect music channels 0-6 to null stream with setPtr2. */
-	_channel0.setPtr2(ADLIB_NULLDATA);
-	_channel1.setPtr2(ADLIB_NULLDATA);
-	_channel2.setPtr2(ADLIB_NULLDATA);
-	_channel3.setPtr2(ADLIB_NULLDATA);
-	_channel4.setPtr2(ADLIB_NULLDATA);
-	_channel5.setPtr2(ADLIB_NULLDATA);
-	_channel6.setPtr2(ADLIB_NULLDATA);
-	return 0;
-}
-
-int ASound::command3() {
-	/* Pending-stop music channels 0-6 (natural envelope fade via enable). */
-	_channel0.enable();
-	_channel1.enable();
-	_channel2.enable();
-	_channel3.enable();
-	_channel4.enable();
-	_channel5.enable();
-	_channel6.enable();
-	return 0;
-}
-
-int ASound::command4() {
-	/* Hard-redirect SFX channels 7-8. */
-	_channel7.setPtr2(ADLIB_NULLDATA);
-	_channel8.setPtr2(ADLIB_NULLDATA);
-	return 0;
-}
-
-int ASound::command5() {
-	/* Pending-stop SFX channels 7-8. */
-	_channel7.enable();
-	_channel8.enable();
-	return 0;
-}
-
-int ASound::command6() {
-	/* Guard: already disabled. */
-	if (_isDisabled == 0xFFFF)
-		return 0;
-	_isDisabled = 0xFFFF;
-
-	/* Save each channel's _freqSweepCounter -> _savedSweepCounter, then zero it. */
-	for (int i = 0; i < ADLIB_CHANNEL_COUNT; ++i) {
-		AdlibChannel *ch = _channels[i];
-		ch->_savedSweepCounter = ch->_freqSweepCounter;
-		ch->_freqSweepCounter = 0;
-	}
-
-	/* Mute all 22 operator TL registers. */
-	for (int i = 0; i < 22; ++i)
-		adlib_channelOff(ALL_OP_TL_REGS[i]);
-
-	return 0;
-}
-
-int ASound::command7() {
-	/* Guard: only resume from a paused state. */
-	if (_isDisabled != 0xFFFF)
-		return 0;
-
-	/* Restore all operator volumes from the _adlibPorts shadow
-	 * (asound_playMusicC: just re-writes whatever was last written). */
-	for (int i = 0; i < 22; ++i)
-		adlib_channelOn(ALL_OP_TL_REGS[i]);
-
-	/* Restore each channel's sweep counter and check whether any was non-zero. */
-	uint8 anySweep = 0;
-	for (int i = 0; i < ADLIB_CHANNEL_COUNT; ++i) {
-		AdlibChannel *ch = _channels[i];
-		ch->_freqSweepCounter = ch->_savedSweepCounter;
-		anySweep |= ch->_savedSweepCounter;
-	}
-
-	if (anySweep != 0)
-		signalSoundPlaying();
-
-	_isDisabled = 0;
-	return 0;
-}
-
-int ASound::command8() {
-	/* Returns non-zero if any channel is currently active.
-	 * Clears byte_12393 (music-only flag) first so all 9 channels are checked. */
-	_musicOnlyFlag = 0;
-	uint8 result = 0;
-	for (int i = 0; i < ADLIB_CHANNEL_COUNT; ++i)
-		result |= _channels[i]->_activeCount;
-	return result;
-}
-
-int ASound::command18() {
-	/* Re-entrant background-music launcher (asound_command18 in the binary).
-	 * Fades everything, then dispatches back through the command table using
-	 * _musicIndex (word_12370) as the command ID. */
-	command1();
-	return command(_musicIndex, 0);
-}
-
-void ASound::callFunction(uint16 offset) {
-	error("Unsupported call to sound driver function at offset %.4x", offset);
-}
-
-void ASound::write(uint8 reg, uint8 value) {
-	_adlibPorts[reg] = value;
-	_opl->writeReg(reg, value);
-}
-
-void ASound::onTimer() {
-	Common::StackLock slock(_driverMutex);
-	poll();
-}
-
-uint16 ASound::getRandomNumber() {
-	/* asound_getRandomNumber: ax = ror3(0x9248 + seed). */
-	uint16 ax = (uint16)(0x9248u + _randomSeed);
-	ax = (uint16)((ax >> 3) | (ax << 13));
-	_randomSeed = ax;
-	return ax;
-}
-
-void ASound::adlib_channelOff(uint8 portIndex) {
-	/* sub_1018F: OR the register with 0x3F (force max attenuation),
-	 * then write back both to _adlibPorts and to the OPL chip.
-	 * Note: unlike the Phantom driver, the original value is NOT preserved
-	 * in _adlibPorts - the ORed value is stored back. */
-	uint8 val = _adlibPorts[portIndex] | 0x3F;
-	_adlibPorts[portIndex] = val;
-	_opl->writeReg(portIndex, val);
-}
-
-void ASound::adlib_channelOn(uint8 portIndex) {
-	/* asound_playMusicC: re-writes whatever _adlibPorts already holds.
-	 * command7 simply replays the shadow without recalculating anything. */
-	_opl->writeReg(portIndex, _adlibPorts[portIndex]);
-}
-
-void ASound::signalSoundPlaying() {
-	/* asound_signalSoundPlaying: set resultFlag/pollResult to non-zero. */
-	if (_resultFlag == (int16)0xFFFF)
-		return;
-	_resultFlag = (int16)0xFFFF;
-	_pollResult = (int16)0xFFFF;
-}
-
-void ASound::clearCallback() {
-	_callbackFnPtr = nullptr;
-	_callbackCounter = 0;
-	_callbackPeriod = 0;
-}
-
-void ASound::tickCallback() {
-	if (_callbackPeriod == 0)
-		return;
-	if (--_callbackCounter != 0)
-		return;
-
-	_callbackCounter = _callbackPeriod;
-	if (_callbackFnPtr == nullptr)
-		return;
-
-	auto fn = _callbackFnPtr;
-	_callbackFnPtr = nullptr;
-	(this->*fn)();
-}
-
-void ASound::writeVolume() {
-	AdlibChannel *ch = _activeChannelPtr;
-	uint8 chanNum = _activeChannelNumber;
-
-	/* Compute combined attenuation step from volume and velocity. */
-	uint8  volIdx = ch->_volume;
-	uint8  velIdx = ch->_velocity;
-	int16  volStep = (int16)(uint16)VOL_VEL_TO_ATTEN_STEP[volIdx];
-	int16  velStep = (int16)(uint16)VOL_VEL_TO_ATTEN_STEP[velIdx];
-	int16  var4 = volStep + velStep - 1;   /* var_4: combined step (shared) */
-
-	/* Check _alg of the first sample to determine loop count. */
-	AdlibSample *smpFirst = &_samples[ch->_sampleIndex * 2];
-	int passes = (smpFirst->_alg == 0) ? 2 : 1;
-
-	/* si and di track the two TL values written per pass for caching. */
-	int16 finalSi = 0, finalDi = 0;
-
-	/* var_6 = loop counter (0, then 1 for the two-pass case).
-	 * For alg != 0 we jump straight into the carrier path (var_6 stays at
-	 * effectively 1: VOICE_SLOTS[ch][1]). */
-	for (int var6 = (passes == 1 ? 1 : 0); var6 < 2; ++var6) {
-
-		/* Reload var_2 = var_4 at the start of each pass (loc_11642). */
-		int16 var2 = var4;
-
-		/* Select the operator slot. */
-		uint8 slot = VOICE_SLOTS[chanNum][var6];
-		uint8 regOff = SLOT_TO_REG_OFFSET[slot];
-		uint16 tlReg = (uint16)regOff + 0x40;   /* var_8 */
-
-		/* KSL bits from the port shadow. */
-		int16 kslBits = (int16)((uint16)_adlibPorts[tlReg] & 0xC0);   /* var_A */
-
-		int16 si, di;
-
-		if (OPL_VERSION_FLAG < 0x18) {
-			/* ---- OPL2 simple path (loc_1167C / loc_1167C equivalent) ---- */
-			int16 tl = (int16)0x3F - var2;
-			tl |= kslBits;
-			si = tl;
-			di = tl;
-			/* adlib_write2(8, tlReg, tl) */
-			write((uint8)tlReg, (uint8)tl);
-		} else {
-			/* ---- OPL3 patch-attenuation path (loc_115BE / loc_116D4) ---- */
-			uint8 pa = ch->_patchAttenuation;
-
-			/* Modulator TL (first register, offset 0): */
-			int16 tlMod = (int16)(uint16)PATCH_ATTEN_TO_TL[pa];
-			/* -(tlMod - var2) = var2 - tlMod */
-			int16 atten1 = var2 - tlMod;   /* ax = si initially */
-			si = atten1;
-			if (si < 0)       si = 0;
-			else if (si > 63) si = 63;
-			int16 reg0val = ((int16)0x3F - si) | kslBits;
-			si = reg0val;
-			write((uint8)tlReg, (uint8)reg0val);
-
-			/* Carrier TL (second register, offset 2):
-			 * unk_12431 - bx (where bx = pa) == PATCH_ATTEN_TO_TL[127 - pa]. */
-			int16 tlCar = (int16)(uint16)PATCH_ATTEN_TO_TL[127 - pa];
-			/* di = var_2 - tlCar  (var_4 for alg!=0, var_2=var_4 reload for alg==0) */
-			di = var2 - tlCar;
-			int16 diClamped = di;
-			if (diClamped < 0)        diClamped = 0;
-			else if (diClamped > 0x3F) diClamped = 0x3F;
-			int16 reg2val = ((int16)0x3F - diClamped) | kslBits;
-			di = reg2val;
-			write((uint8)(tlReg + 2), (uint8)reg2val);
-		}
-
-		/* For the alg!=0 single-pass case we only run this once (var6==1).
-		 * For the alg==0 two-pass case, record both sets. */
-		if (var6 == 1) {
-			finalSi = si;
-			finalDi = di;
-		} else {
-			/* var6==0: record si/di for the mod pass; di is overwritten on
-			 * the carrier pass so we use si for the high byte of field_2A. */
-			finalSi = si;
-			finalDi = di;
-		}
-	}
-
-	/* Cache the written TL bytes in field_2A:
-	 *   dl = di & 0x3F  (carrier / second register)
-	 *   dh = si & 0x3F  (modulator / first register)
-	 * Packed as a word at offset 0x2A. */
-	ch->_cachedCarrierTL = (uint8)(finalDi & 0x3F);   /* dl -> field_2A low byte  */
-	/* The high byte (si & 0x3F) is stored in _savedFreqSweep (offset 0x2B)
-	 * by the assembly.  We write it separately to avoid clobbering the real
-	 * savedFreqSweep which lives there - the original asm stores both bytes
-	 * as a word at [bx+2Ah], meaning field_2A=dl and field_2B=dh=si&0x3F.
-	 * In our C++ layout _savedFreqSweep is at 0x2B; we shadow the dh byte
-	 * into it only when writing volume, consistent with the original. */
-	ch->_savedFreqSweep = (uint8)(finalSi & 0x3F);
-}
-
-void ASound::writeFrequency() {
-	AdlibChannel *ch = _activeChannelPtr;
-	uint8 chanNum = _activeChannelNumber;
-	uint16 aReg = (uint16)chanNum + 0xA0;
-	uint16 bReg = (uint16)chanNum + 0xB0;
-
-	/* Note is 1-based; _octaveTranspose shifts by whole octaves. */
-	int note = (int)ch->_note + (int)ch->_octaveTranspose - 1;
-	int octave = note / 12;
-	int semi = note % 12;
-	if (semi < 0) {
-		semi += 12; --octave;
-	}
-
-	/* F-number from table, with optional signed transpose offset. */
-	int16 fnum = (int16)SEMITONE_FREQ_TABLE[semi] + (int16)(int8)ch->_transpose;
-
-	write((uint8)aReg, (uint8)fnum);
-
-	uint8 bVal = _adlibPorts[bReg] & 0x20;          /* preserve key-on bit */
-	bVal |= (uint8)((fnum >> 8) & 0x03);            /* F-num bits 9:8 */
-	bVal |= (uint8)(((uint8)octave & 0x07) << 2);   /* block/octave bits 4:2 */
-	write((uint8)bReg, bVal);
-}
-
-void ASound::writePitchBend() {
-	AdlibChannel *ch = _activeChannelPtr;
-	uint8 chanNum = _activeChannelNumber;
-	uint16 aReg = (uint16)chanNum + 0xA0;
-	uint16 bReg = (uint16)chanNum + 0xB0;
-
-	/* Reconstruct the 10-bit F-number from the port shadow. */
-	uint16 fnum = (uint16)_adlibPorts[aReg]
-		| ((uint16)(_adlibPorts[bReg] & 0x1F) << 8);
-
-	int16 bent = (int16)fnum + (int16)(int8)ch->_pitchBend;
-
-	write((uint8)aReg, (uint8)bent);
-
-	/* Preserve block + key-on, update F-num[9:8]. */
-	uint8 bVal = _adlibPorts[bReg] & 0x20;
-	bVal |= (uint8)((bent >> 8) & 0x03);
-	write((uint8)bReg, bVal);
-}
-
-void ASound::writeArpeggio() {
-	AdlibChannel *ch = _activeChannelPtr;
-	uint8 chanNum = _activeChannelNumber;
-	uint16 aReg = (uint16)chanNum + 0xA0;
-	uint16 bReg = (uint16)chanNum + 0xB0;
-
-	/* dl = _note + _octaveTranspose + _writeVolumePending - 1 */
-	int note = (int)ch->_note + (int)ch->_octaveTranspose
-		+ (int)ch->_writeVolumePending - 1;
-	int octave = note / 12;
-	int semi = note % 12;
-	if (semi < 0) {
-		semi += 12; --octave;
-	}
-
-	uint16 freqEntry = SEMITONE_FREQ_TABLE[semi];
-	uint8  fnHigh = (uint8)((freqEntry >> 8) & 0x03);
-	uint8  block = (uint8)(octave & 0x07);
-	uint8  bVal = (uint8)((block << 2) | fnHigh) | (_adlibPorts[bReg] & 0x20);
-
-	write((uint8)aReg, (uint8)freqEntry);
-	write((uint8)bReg, bVal);
-}
-
-void ASound::updateOctave() {
-	uint8 chanNum = _activeChannelNumber;
-	uint16 bReg = (uint16)chanNum + 0xB0;
-	write((uint8)bReg, _adlibPorts[bReg] & 0xDF);
-}
-
-void ASound::noteOn() {
-	AdlibChannel *ch = _activeChannelPtr;
-	AdlibSample *smp = &_samples[ch->_sampleIndex];
-
-	writeVolume();
-
-	ch->_freqSweepCounter = smp->_freqSweepInit;
-
-	if (ch->_freqSweepCounter != 0) {
-		/* Sweep is pending; signal that sound is active but don't key on yet. */
-		signalSoundPlaying();
-		return;
-	}
-
-	/* No sweep - write frequency and set key-on immediately. */
-	writeFrequency();
-
-	uint8 chanNum = _activeChannelNumber;
-	uint16 bReg = (uint16)chanNum + 0xB0;
-	write((uint8)bReg, _adlibPorts[bReg] | 0x20);
-}
-
-void ASound::writeSampleRegs() {
-	AdlibSample *smp = _samplePtr;
-	uint16 base = _currentOpBase;
-
-	/* 0xBD: rhythm mode flags. */
-	uint8 bdVal = _adlibPorts[0xBD] & 0x3F;
-	if (RHYTHM_HI_HAT) bdVal |= 0x80;
-	if (RHYTHM_CYMBAL) bdVal |= 0x40;
-	write(0xBD, bdVal);
-
-	/* 0x08: note-select / rhythm enable. */
-	write(0x08, RHYTHM_ENABLE ? 0x40 : 0x00);
-
-	/* 0xC0+voice: feedback and algorithm.
-	 * asm: feedback << 1, then alg==1 -> bit0=0, else -> bit0=1. */
-	write((uint8)(_activeChannelReg + 0xC0),
-		(uint8)((smp->_feedback << 1) | (smp->_alg == 1 ? 0 : 1)));
-
-	/* 0x60+base: attack / decay. */
-	write((uint8)(base + 0x60),
-		(uint8)(((smp->_attackRate & 0x0F) << 4) | (smp->_decayRate & 0x0F)));
-
-	/* 0x80+base: sustain level / release rate. */
-	write((uint8)(base + 0x80),
-		(uint8)(((smp->_sustainLevel & 0x0F) << 4) | (smp->_releaseRate & 0x0F)));
-
-	/* 0x20+base: AM/VIB/EGT/KSR/mult flags. */
-	uint8 amVal = smp->_freqMultiple & 0x0F;
-	if (smp->_egTyp == 1) amVal |= 0x20;
-	if (smp->_vib == 1) amVal |= 0x40;
-	if (smp->_ksr == 1) amVal |= 0x10;
-	if (smp->_ampMod == 1) amVal |= 0x80;
-	write((uint8)(base + 0x20), amVal);
-
-	/* 0xE0+base: waveform select. */
-	write((uint8)(base + 0xE0), (uint8)(smp->_waveformSelect & 0x03));
-}
-
-void ASound::loadSample() {
-	AdlibChannel *ch = _activeChannelPtr;
-	uint8 chanNum = _activeChannelNumber;
-
-	/* Phase 1: Silence modulator operator (slot 0). */
-	uint8 mSlot = VOICE_SLOTS[chanNum][0];
-	uint8 mRegOff = SLOT_TO_REG_OFFSET[mSlot];
-	write((uint8)(mRegOff + 0x80), 0xFF);
-	write((uint8)(mRegOff + 0x40), 63);
-
-	/* Phase 2: Silence carrier operator (slot 1). */
-	uint8 cSlot = VOICE_SLOTS[chanNum][1];
-	uint8 cRegOff = SLOT_TO_REG_OFFSET[cSlot];
-	write((uint8)(cRegOff + 0x80), 0xFF);
-	write((uint8)(cRegOff + 0x40), 63);
-
-	/* Phase 3: Write first sample (modulator) registers. */
-	_activeChannelReg = (uint16)chanNum;
-	_currentOpBase = mRegOff;
-	_samplePtr = &_samples[ch->_sampleIndex * 2];
-	writeSampleRegs();
-
-	/* Phase 4: Write second sample (carrier) registers. */
-	_currentOpBase = cRegOff;
-	_samplePtr = &_samples[ch->_sampleIndex * 2 + 1];
-	writeSampleRegs();
-
-	/* Phase 5: Write carrier TL (KSL | 0x3F for full attenuation initially). */
-	write((uint8)(cRegOff + 0x40),
-		(uint8)((_samplePtr->_scalingLevel << 6) | 0x3F));
-
-	/* Phase 6: Use the first sample again for the modulator TL and for
-	 * sweep/frequency initialisation. */
-	AdlibSample *smp3 = &_samples[ch->_sampleIndex * 2];
-	uint16        mTLReg = (uint16)mRegOff + 0x40;
-
-	if (smp3->_alg == 0) {
-		write((uint8)mTLReg, (uint8)((smp3->_scalingLevel << 6) | 0x3F));
-	} else {
-		/* alg != 0: negate (totalLevel - 63) to get the TL value. */
-		uint8 tl = (uint8)(-(int8)(smp3->_totalLevel - 63));
-		write((uint8)mTLReg, (uint8)((smp3->_scalingLevel << 6) | (tl & 0x3F)));
-	}
-
-	/* Copy sweep/frequency parameters from the sample into the channel. */
-	ch->_freqSweepCounter = smp3->_freqSweepInit;
-	ch->_noiseFreqMask = smp3->_freqMask;
-	ch->_freqAccum = smp3->_freqBase;
-	ch->_freqStep = smp3->_outerLoopPtr;
-}
-
-void ASound::update() {
-	if (_isDisabled)
-		return;
-
-	(void)getRandomNumber();
-	++_frameNumber2;
-
-	pollAllChannels();
-	tickCallback();       /* asound_updateCallback */
-	updateAllChannels();
-
-	_anySweepActive = 0;
-
-	for (int i = 0; i < ADLIB_CHANNEL_COUNT; ++i)
-		update1(i);
-
-	/* If no sweep is active, flag the result as "done". */
-	if (!_anySweepActive) {
-		if (_resultFlag != (int16)0xFFFF) {
-			_resultFlag = (int16)0xFFFF;
-			_pollResult = (int16)0xFFFF;
-		}
-	}
-}
-
-void ASound::update1(int channelIndex) {
-	/* asound_update1: called with cx = channelIndex+1 in the original loop.
-	 * Accesses channel via the adlib_channels pointer table. */
-	AdlibChannel *ch = _channels[channelIndex];
-
-	if (ch->_freqSweepCounter == 0)
-		return;
-
-	_anySweepActive = 1;
-	ch->_freqAccum += ch->_freqStep;
-	ch->_freqSweepCounter--;
-
-	if (ch->_freqSweepCounter != 0)
-		return;
-
-	/* Sweep finished: write zero frequency to silence this OPL voice. */
-	uint8 voice = (uint8)channelIndex;
-	write((uint8)(voice + 0xA0), 0x00);
-	write((uint8)(voice + 0xB0), 0x00);
-}
-
-void ASound::setFrequency(uint8 voice, uint16 freq) {
-	/* asound_setFrequency: writes A0+v and B0+v (with key-on). */
-	write((uint8)(voice + 0xA0), (uint8)(freq & 0xFF));
-	uint8 bVal = (_adlibPorts[voice + 0xB0] & 0x20)
-		| (uint8)((freq >> 8) & 0x03)
-		| 0x20;
-	write((uint8)(voice + 0xB0), bVal);
-}
-
-void ASound::noise_inner(int channelIndex) {
-	/* adlib_noise_inner */
-	AdlibChannel *ch = _channels[channelIndex];
-
-	if (ch->_freqSweepCounter == 0)
-		return;
-
-	uint16 rnd = getRandomNumber();
-	uint16 freq = (rnd & ch->_noiseFreqMask) + ch->_freqAccum;
-	setFrequency((uint8)channelIndex, freq);
-}
-
-void ASound::updateAllChannels() {
-	for (int i = 0; i < ADLIB_CHANNEL_COUNT; ++i)
-		_channels[i]->processChannelFade();
-}
-
-void ASound::findFreeChannel(byte *soundData) {
-	_findChannelMode = 0;
-
-	/* Scan channels 0-6 for an empty slot. */
-	for (int ch = 0; ch < 7; ++ch) {
-		if (_channels[ch]->_activeCount == 0) {
-			_channels[ch]->load(soundData);
-			return;
-		}
-	}
-
-	/* _findChannelMode==0: fall through to full search. */
-	findFreeChannelFull(soundData);
-}
-
-void ASound::findFreeChannelFull(byte *soundData) {
-	_findChannelMode = 2;
-
-	/* Scan channels 7-8 for an empty slot. */
-	if (_channel7._activeCount == 0) {
-		_channel7.load(soundData); return;
-	}
-	if (_channel8._activeCount == 0) {
-		_channel8.load(soundData); return;
-	}
-
-	/* Scan ch8 then ch7 for pending-stop (pre-emptible). */
-	if (_channel8._pendingStop == 0xFF) {
-		_channel8.load(soundData); return;
-	}
-	if (_channel7._pendingStop == 0xFF) {
-		_channel7.load(soundData); return;
-	}
-
-	/* If _findChannelMode allows it, scan ch6..ch0 for pending-stop. */
-	if (_findChannelMode == 0) {
-		if (_channel6._pendingStop == 0xFF) {
-			_channel6.load(soundData); return;
-		}
-		if (_channel5._pendingStop == 0xFF) {
-			_channel5.load(soundData); return;
-		}
-		if (_channel4._pendingStop == 0xFF) {
-			_channel4.load(soundData); return;
-		}
-		if (_channel3._pendingStop == 0xFF) {
-			_channel3.load(soundData); return;
-		}
-		if (_channel2._pendingStop == 0xFF) {
-			_channel2.load(soundData); return;
-		}
-		if (_channel1._pendingStop == 0xFF) {
-			_channel1.load(soundData); return;
-		}
-		if (_channel0._pendingStop == 0xFF) {
-			_channel0.load(soundData); return;
-		}
-	}
-}
-
-int ASound::isMusicChannelsActive() {
-	_musicOnlyFlag = 1;
-	uint8 result = 0;
-	for (int i = 0; i <= 6; ++i)
-		result |= _channels[i]->_activeCount;
-	return result;
-}
-
-int ASound::isAnyChannelActive() {
-	_musicOnlyFlag = 0;
-	uint8 result = 0;
-	for (int i = 0; i < ADLIB_CHANNEL_COUNT; ++i)
-		result |= _channels[i]->_activeCount;
-	return result;
-}
-
-void ASound::pollAllChannels() {
-	_activeChannelNumber = 0;
-	for (int ch = 0; ch < ADLIB_CHANNEL_COUNT; ++ch) {
-		_activeChannelPtr = _channels[ch];
-		pollActiveChannel();
-		/* Note: _activeChannelNumber is incremented at the end of
-		 * pollActiveChannel, not here. */
-	}
-}
-
-bool ASound::isSoundActive(byte *ptr) const {
-	for (int ch = 0; ch < ADLIB_CHANNEL_COUNT; ++ch) {
-		if (_channels[ch]->_activeCount && _channels[ch]->_soundData == ptr)
-			return true;
-	}
-	return false;
-}
-
-uint16 ASound::readWord_impl() {
-	/* asound_readWord: advance pSrc past the low byte, read lo then hi. */
-	uint16 lo = *++pSrc;
-	uint16 hi = *++pSrc;
-	return lo | (hi << 8);
-}
-
-void ASound::pollActiveChannel() {
-	AdlibChannel *ch = _activeChannelPtr;
-
-	if (ch->_activeCount == 0) {
-		++_activeChannelNumber;
-		return;
-	}
-
-	/* byte_16A0A: volume-dirty flag.  Cleared here, set by various opcodes
-	 * and by the fade/vibrato sections; causes writeVolume at the end. */
-	bool volDirty = false;
-
-	/* ---- Key-on delay countdown ---- */
-	if (ch->_keyOnDelay != 0) {
-		ch->_keyOnDelay--;
-		ch = _activeChannelPtr;
-		if (ch->_keyOnDelay == 0)
-			updateOctave();
-	}
-
-	/* ---- Duration countdown ---- */
-	ch = _activeChannelPtr;
-	ch->_activeCount--;
-	ch = _activeChannelPtr;
-	if (ch->_activeCount != 0)
-		goto post_keyon;
-
-	/* ================================================================
-	 * Command-dispatch loop.
-	 * Re-entered (via goto dispatch) after each command that does not
-	 * consume a duration tick.  Falls through to the note-event path
-	 * when a note byte is encountered.
-	 * ================================================================ */
-dispatch:
-	{
-		ch = _activeChannelPtr;
-		pSrc = ch->_pSrc;
-
-		uint8 b = *pSrc;
-
-		if (!(b & 0x80))
-			goto note_event;
-
-		/* Decode group (si) and sub-opcode (di). */
-		uint8 si = b & 0x70;
-		uint8 di = b & 0x0F;
-
-		if (si == 0x00) {
-			/* ============================================================
-			 * opcodes1  (group 0, 0x8_)
-			 * Called with: pSrc pointing at the command byte.
-			 * On entry, opcodes1 does: inc pSrc (skip past command byte).
-			 * On exit, sets _pSrc = pSrc + 1 (def_10CDC).
-			 * ============================================================ */
-			pSrc++;   /* skip command byte */
-
-			switch (di) {
-
-			case 0x0: /* set sampleIndex, call loadSample */
-				ch->_sampleIndex = *pSrc;
-				loadSample();
-				volDirty = true;
-				/* def_10CDC: _pSrc = pSrc + 1 */
-				ch->_pSrc = pSrc + 1;
-				goto dispatch;
-
-			case 0x1: /* set velocity */
-			{
-				uint8 vel = *pSrc;
-				if (ch->_pendingStop != 0) {
-					/* If current velocity (signed) > new vel (signed), clamp down. */
-					if ((int16)(int8)ch->_velocity > (int16)(int8)vel)
-						ch->_velocity = vel;
-				} else {
-					ch->_velocity = vel;
-				}
-				volDirty = true;
-				ch->_pSrc = pSrc + 1;
-				goto dispatch;
-			}
-
-			case 0x2: /* set volume */
-			{
-				uint8 vol = *pSrc;
-				if (ch->_pendingStop != 0) {
-					/* Don't raise volume above current when fading out. */
-					uint16 curVol = (uint16)ch->_volume;
-					if (curVol <= (uint16)vol)
-						goto op2_set_vol;
-					/* else fall through and skip the set */
-					volDirty = true;
-					ch->_pSrc = pSrc + 1;
-					goto dispatch;
-				}
-op2_set_vol:
-				ch->_volume = vol;
-				volDirty = true;
-				ch->_pSrc = pSrc + 1;
-				goto dispatch;
-			}
-
-			case 0x3: /* set patchAttenuation */
-				ch->_patchAttenuation = *pSrc;
-				volDirty = true;   /* opcodes1 case 3 jumps to loc_10B56 -> byte_16A0A=1 */
-				ch->_pSrc = pSrc + 1;
-				goto dispatch;
-
-			case 0x4: /* set fade: fadePeriodReload, volumeFadeStep */
-				if (ch->_pendingStop != 0) {
-					/* Skip this opcode entirely when pending-stop is set. */
-					ch->_pSrc = pSrc + 2;
-					goto dispatch;
-				}
-				ch->_fadePeriodReload = *pSrc++;
-				ch->_volumeFadeStep = *pSrc;
-				ch->_fadePeriodCounter = 1;
-				ch->_pSrc = pSrc + 1;
-				goto dispatch;
-
-			case 0x5: /* set vibrato: vibPeriodReload, vibPeriodCounter=reload, vibratoDepth, vibratoMode */
-				ch->_vibPeriodReload = *pSrc++;
-				ch->_vibPeriodCounter = ch->_vibPeriodReload;
-				ch->_vibratoDepth = *pSrc++;
-				ch->_vibratoMode = *pSrc;
-				ch->_pSrc = pSrc + 1;
-				goto dispatch;
-
-			case 0x6: /* set transpose */
-				ch->_transpose = *pSrc;
-				ch->_pSrc = pSrc + 1;
-				goto dispatch;
-
-			case 0x7: /* set octaveTranspose */
-				ch->_octaveTranspose = *pSrc;
-				ch->_pSrc = pSrc + 1;
-				goto dispatch;
-
-			case 0x8: /* set field_11 (_writeVolumePending) */
-				ch->_writeVolumePending = *pSrc;
-				ch->_pSrc = pSrc + 1;
-				goto dispatch;
-
-			case 0x9: /* set arpeggio: field_12->field_C, pitchBend, field_13 */
-				ch->_arpPeriodReload = *pSrc++;
-				ch->_arpPeriodCounter = ch->_arpPeriodReload;   /* field_C = field_12 */
-				ch->_pitchBend = *pSrc++;
-				ch->_arpCounterReload = *pSrc;
-				ch->_pSrc = pSrc + 1;
-				goto dispatch;
-
-			case 0xA: /* skip forward by N+3 bytes (from the command byte) */
-			{
-				uint8 skip = *pSrc;
-				/* pSrc currently points at the byte after the command byte.
-				 * Advance _pSrc by (skip + 3) from the *command* byte position,
-				 * which is pSrc-1.  So _pSrc += skip + 3 from original _pSrc,
-				 * which was command byte.  But def_10CDC does _pSrc = pSrc+1
-				 * giving +2 past command.  Then skip+3 total from command: */
-				 /* asm: si = skip; ax = si + 3; add pSrc, ax; then def_10CDC
-				  * does pSrc+1.  So net advance from the command byte =
-				  * skip + 3 + 1 = skip + 4.  But pSrc already points one past
-				  * command byte, so _pSrc = pSrc + skip + 3. */
-				ch->_pSrc = pSrc + (uint16)skip + 3;
-				goto dispatch;
-			}
-
-			case 0xB: /* set durationOverride, clear noteOffset */
-				ch->_durationOverride = *pSrc;
-				ch->_noteOffset = 0;
-				ch->_pSrc = pSrc + 1;
-				goto dispatch;
-
-			case 0xC: /* set noteOffset, clear durationOverride */
-				ch->_noteOffset = *pSrc;
-				ch->_durationOverride = 0;
-				ch->_pSrc = pSrc + 1;
-				goto dispatch;
-
-			default:
-				/* Unknown sub-opcode: advance past the single operand byte. */
-				ch->_pSrc = pSrc + 1;
-				goto dispatch;
-			}
-		}
-
-		if (si == 0x10) {
-			/* ============================================================
-			 * opcodes2  (group 1, 0x9_)
-			 * Loop control, restart, branch / call.
-			 * On exit: either sets _pSrc directly or falls to def_10EAC
-			 * (which just returns without touching _pSrc - the handlers
-			 * themselves are responsible for updating _pSrc).
-			 * ============================================================ */
-			switch (di) {
-
-			case 0x0: /* inner loop */
-			{
-				ch = _activeChannelPtr;
-				if (ch->_innerLoopCount == 0) {
-					pSrc++;   /* advance to count byte */
-					uint8 cnt = *pSrc;
-					if (cnt == 0) {
-						ch->_pSrc += 2;
-						ch = _activeChannelPtr;
-						ch->_innerLoopPtr = ch->_pSrc;
-						ch->_innerLoopCount = 0;
-						goto dispatch;
-					}
-					ch->_innerLoopCount = (uint16)cnt;
-					/* Jump to innerLoopPtr (loc_10D5A). */
-					ch->_pSrc = ch->_innerLoopPtr;
-					goto dispatch;
-				}
-				/* Count is non-zero: decrement. */
-				ch->_innerLoopCount--;
-				ch = _activeChannelPtr;
-				if (ch->_innerLoopCount == 0) {
-					/* Loop done: advance past the two-byte opcode. */
-					ch->_pSrc += 2;
-					ch = _activeChannelPtr;
-					ch->_innerLoopPtr = ch->_pSrc;
-					goto dispatch;
-				}
-				/* Still looping: jump back. */
-				ch->_pSrc = ch->_innerLoopPtr;
-				goto dispatch;
-			}
-
-			case 0x1: /* outer loop */
-			{
-				ch = _activeChannelPtr;
-				if (ch->_outerLoopCount == 0) {
-					pSrc++;
-					uint8 cnt = *pSrc;
-					if (cnt == 0) {
-						ch->_pSrc += 2;
-						ch = _activeChannelPtr;
-						ch->_outerLoopPtr = ch->_pSrc;
-						ch->_innerLoopPtr = ch->_pSrc;
-						ch->_innerLoopCount = 0;
-						ch->_outerLoopCount = 0;
-						goto dispatch;
-					}
-					ch->_outerLoopCount = (uint16)cnt;
-					/* Jump to outerLoopPtr. */
-					ch->_pSrc = ch->_outerLoopPtr;
-					ch = _activeChannelPtr;
-					ch->_innerLoopPtr = ch->_outerLoopPtr;
-					goto dispatch;
-				}
-				/* Count non-zero: decrement. */
-				ch->_outerLoopCount--;
-				ch = _activeChannelPtr;
-				if (ch->_outerLoopCount == 0) {
-					ch->_pSrc += 2;
-					ch = _activeChannelPtr;
-					ch->_outerLoopPtr = ch->_pSrc;
-					ch->_innerLoopPtr = ch->_pSrc;
-					goto dispatch;
-				}
-				ch->_pSrc = ch->_outerLoopPtr;
-				ch = _activeChannelPtr;
-				ch->_innerLoopPtr = ch->_outerLoopPtr;
-				goto dispatch;
-			}
-
-			case 0x2: /* restart: reset all loop pointers to _soundData */
-			{
-				ch = _activeChannelPtr;
-				uint16 sd = (uint16)(uintptr_t)ch->_soundData;
-				ch->_loopStartPtr = ch->_soundData;
-				ch->_pSrc = ch->_soundData;
-				ch->_innerLoopPtr = ch->_soundData;
-				ch->_outerLoopPtr = ch->_soundData;
-				(void)sd;
-				goto dispatch;
-			}
-
-			case 0x3: /* set sound-data pointer (abs word offset), reset all loops */
-			{
-				byte *ptr = &_soundData[readWord_impl()];
-				ch = _activeChannelPtr;
-				ch->_loopStartPtr = ptr;
-				ch->_pSrc = ptr;
-				ch->_innerLoopPtr = ptr;
-				ch->_outerLoopPtr = ptr;
-				ch->_soundData = ptr;
-				goto dispatch;
-			}
-
-			case 0x4: /* branch unconditional */
-			{
-				byte *dest = &_soundData[readWord_impl()];
-				ch = _activeChannelPtr;
-				ch->_pSrc = dest;
-				goto dispatch;
-			}
-
-			case 0x5: /* branch with return-address save */
-			{
-				byte *dest = &_soundData[readWord_impl()];
-				ch = _activeChannelPtr;
-				ch->_branchTargetPtr = ch->_pSrc + 3;
-				ch->_pSrc = dest;
-				goto dispatch;
-			}
-
-			case 0x6: /* return from branch */
-			{
-				ch = _activeChannelPtr;
-				if (ch->_branchTargetPtr != nullptr) {
-					ch->_pSrc = ch->_branchTargetPtr;
-					ch->_branchTargetPtr = nullptr;
-				} else {
-					ch->_pSrc++;
-				}
-				goto dispatch;
-			}
-
-			default:
-				/* Unknown sub-opcode: no _pSrc advance (def_10EAC). */
-				goto dispatch;
-			}
-		}
-
-		if (si == 0x20) {
-			/* ============================================================
-			 * opcodes3  (group 2, 0xA_)
-			 * Tempo/callback globals, random table operations,
-			 * indirect-table operations, call-by-address, nullsub.
-			 *
-			 * opcodes3 receives di (sub-opcode) but NOT the already-
-			 * incremented pSrc; each case increments pSrc itself as needed.
-			 * ============================================================ */
-			switch (di) {
-
-			case 0x0: /* random pick from packed table -> write to stream */
-			{
-				/* Format: [cmd] [tblSize] [entry0..N-1] [targetSlot]
-				 * pSrc points at the command byte on entry. */
-				pSrc++;
-				uint8  tblSize = *pSrc;
-				pSrc++;   /* now at entry[0] */
-				(void)getRandomNumber();
-				uint16 rnd = _randomSeed & 0x7FFF;
-				int16  idx = (int16)((int16)rnd % (int16)(uint16)tblSize);
-				uint8  chosen = *(pSrc + idx);
-				uint8  target = *(pSrc + tblSize);
-				*(pSrc + (uint16)target + 1) = chosen;
-				ch = _activeChannelPtr;
-				ch->_pSrc += (uint16)tblSize + 3;
-				goto dispatch;
-			}
-
-			case 0x1: /* random range [lo..hi] -> write to stream */
-			{
-				/* Format: [cmd] [lo] [hi] [targetSlot] ... */
-				pSrc++;
-				uint8 lo = *pSrc++;
-				uint8 hi = *pSrc++;
-				uint16 range = (uint16)(hi - lo) + 1;
-				/* pSrc now at targetSlot byte. */
-				(void)getRandomNumber();
-				uint16 rnd = _randomSeed & 0x7FFF;
-				uint8  res = (uint8)((int16)rnd % (int16)range) + lo;
-				uint8  slot = *pSrc;
-				*(pSrc + (uint16)slot + 1) = res;
-				ch = _activeChannelPtr;
-				ch->_pSrc += 4;   /* command+lo+hi+slot = 4 bytes consumed */
-				goto dispatch;
-			}
-
-			case 0x2: /* indirect table read from scriptVar -> write to stream */
-			{
-				/* Format: [cmd] [varIdx] [tableSlot] [table...] */
-				pSrc++;
-				uint8 varIdx = *pSrc++;
-				uint8 tableSlot = *pSrc++;
-				/* pSrc now points at the inline table. */
-				uint8 idxVal = _scriptVars[varIdx];
-				uint8 chosen = *(pSrc + idxVal);
-				uint8 target = *(pSrc + tableSlot);
-				*(pSrc + (uint16)target + 1) = chosen;
-				ch = _activeChannelPtr;
-				ch->_pSrc += (uint16)tableSlot + 4;
-				goto dispatch;
-			}
-
-			case 0x3: /* call function by address (near call in original) */
-			{
-				uint16 fnOffset = readWord_impl();
-				callFunction(fnOffset);
-				ch = _activeChannelPtr;
-				ch->_pSrc += 3;
-				goto dispatch;
-			}
-
-			case 0x4: /* nullsub_1 with byte arg */
-			{
-				/* "call near ptr aAsoundDriverAn+33h" - the target is a no-op. */
-				pSrc++;
-				/* (void)*pSrc; */
-				ch = _activeChannelPtr;
-				ch->_pSrc += 2;
-				goto dispatch;
-			}
-
-			case 0x5: /* advance _pSrc by 4 (from command byte) */
-			{
-				/* loc_10F55 is shared with case 1's epilogue: _pSrc += 4. */
-				ch = _activeChannelPtr;
-				ch->_pSrc += 4;
-				goto dispatch;
-			}
-
-			case 0x6: /* set word_124F2 (_tempoFineStep) */
-			{
-				pSrc++;
-				uint8 val = *pSrc;
-				_tempoFineStep = (uint16)val;
-				ch = _activeChannelPtr;
-				ch->_pSrc += 2;
-				goto dispatch;
-			}
-
-			case 0x7: /* set word_124F0 (_tempoCoarseStep) */
-			{
-				pSrc++;
-				uint8 val = *pSrc;
-				_tempoCoarseStep = (uint16)val;
-				ch = _activeChannelPtr;
-				ch->_pSrc += 2;
-				goto dispatch;
-			}
-
-			case 0x8: /* set word_124EE (_tempoPeriod), enable tick callback */
-			{
-				uint16 period = readWord_impl();
-				_tempoPeriod = period;
-				ch = _activeChannelPtr;
-				ch->_pSrc += 3;
-				_tempoEnabled = 1;
-				_tempoTickCounter = 1;
-				goto dispatch;
-			}
-
-			case 0x9: /* set word_124F4 (_tempoShift) */
-			{
-				pSrc++;
-				uint8 val = *pSrc;
-				_tempoShift = (uint16)val;
-				ch = _activeChannelPtr;
-				ch->_pSrc += 2;
-				goto dispatch;
-			}
-
-			default:
-				goto dispatch;
-			}
-		}
-
-		if (si == 0x30) {
-			/* ============================================================
-			 * opcodes4  (group 3, 0xB_)
-			 * Script-variable load (imm or var), store to stream, inc, dec.
-			 * Each case increments pSrc itself, then sets _pSrc.
-			 *
-			 * Switch on di (0-4); di >= 5 falls through to return.
-			 * ============================================================ */
-			switch (di) {
-
-			case 0x0: /* var[dst] = imm */
-			{
-				pSrc++;
-				uint8 dst = *pSrc++;
-				uint8 imm = *pSrc;
-				_scriptVars[dst] = imm;
-				ch = _activeChannelPtr;
-				ch->_pSrc += 3;
-				goto dispatch;
-			}
-
-			case 0x1: /* var[dst] = var[src] */
-			{
-				pSrc++;
-				uint8 dst = *pSrc++;
-				uint8 src = *pSrc;
-				_scriptVars[dst] = _scriptVars[src];
-				ch = _activeChannelPtr;
-				ch->_pSrc += 3;
-				goto dispatch;
-			}
-
-			case 0x2: /* stream[tableSlot+1] = var[src] */
-			{
-				/* asm: [bx+di+1] = cl, where bx=pSrc, di=tableSlot. */
-				pSrc++;
-				uint8 src = *pSrc++;
-				uint8 tableSlot = *pSrc;
-				*(pSrc + (uint16)tableSlot + 1) = _scriptVars[src];
-				ch = _activeChannelPtr;
-				ch->_pSrc += 3;
-				goto dispatch;
-			}
-
-			case 0x3: /* var[dst]++ */
-			{
-				pSrc++;
-				uint8 dst = *pSrc;
-				_scriptVars[dst]++;
-				ch = _activeChannelPtr;
-				ch->_pSrc += 2;
-				goto dispatch;
-			}
-
-			case 0x4: /* var[dst]-- */
-			{
-				pSrc++;
-				uint8 dst = *pSrc;
-				_scriptVars[dst]--;
-				ch = _activeChannelPtr;
-				ch->_pSrc += 2;
-				goto dispatch;
-			}
-
-			default:
-				goto dispatch;
-			}
-		}
-
-		if (si == 0x40) {
-			/* ============================================================
-			 * opcodes5  (group 4, 0xC_)
-			 * Script-variable ALU: add/sub/mul/div/rem/and/or/xor.
-			 *
-			 * Entry: inc pSrc, read dst (si), inc pSrc, read src (di).
-			 * Then _pSrc += 3 (command + dst + src).
-			 * 16 sub-opcodes (0-15).
-			 *
-			 * For sub-opcodes 0,2,4: src is an immediate (di from the byte).
-			 * For sub-opcodes 1,3,5: src is _scriptVars[di].
-			 * For 6-9: signed or unsigned division/remainder.
-			 * For 10-15: bitwise AND/OR/XOR, imm or var.
-			 * ============================================================ */
-			pSrc++;
-			uint8 dst = *pSrc++;
-			uint8 src = *pSrc;   /* immediate value or var index */
-			ch = _activeChannelPtr;
-			ch->_pSrc += 3;
-
-			switch (di) {
-			case 0x0: _scriptVars[dst] += src;                                       break;
-			case 0x1: _scriptVars[dst] += _scriptVars[src];                          break;
-			case 0x2: _scriptVars[dst] -= src;                                       break;
-			case 0x3: _scriptVars[dst] -= _scriptVars[src];                          break;
-			case 0x4: /* mul ax=di, mul [si] -> al stored */
-				_scriptVars[dst] = (uint8)((uint16)src * (uint16)_scriptVars[dst]);  break;
-			case 0x5:
-				_scriptVars[dst] = (uint8)((uint16)_scriptVars[src] * (uint16)_scriptVars[dst]); break;
-			case 0x6: /* signed idiv by imm -> quotient in al */
-				_scriptVars[dst] = (uint8)((int8)_scriptVars[dst] / (int8)src);      break;
-			case 0x7: /* unsigned div by var -> quotient in al */
-				_scriptVars[dst] = _scriptVars[dst] / _scriptVars[src];              break;
-			case 0x8: /* signed idiv by imm -> remainder in dl (stored to dst) */
-				_scriptVars[dst] = (uint8)((int8)_scriptVars[dst] % (int8)src);      break;
-			case 0x9: /* unsigned div by var -> remainder in ah (stored to dst) */
-				_scriptVars[dst] = _scriptVars[dst] % _scriptVars[src];              break;
-			case 0xA: _scriptVars[dst] &= src;                                       break;
-			case 0xB: _scriptVars[dst] &= _scriptVars[src];                          break;
-			case 0xC: _scriptVars[dst] |= src;                                       break;
-			case 0xD: _scriptVars[dst] |= _scriptVars[src];                          break;
-			case 0xE: _scriptVars[dst] ^= src;                                       break;
-			case 0xF: _scriptVars[dst] ^= _scriptVars[src];                          break;
-			default: break;
-			}
-			goto dispatch;
-		}
-
-		if (si == 0x50) {
-			/* ============================================================
-			 * opcodes6  (group 5, 0xD_)
-			 * Conditional branch: compare var[si] vs immediate (di as imm byte).
-			 *
-			 * Format: [cmd] [varIdx] [imm] [addrLo] [addrHi]
-			 * Entry: inc pSrc, read varIdx (si reg), inc pSrc, read imm (di reg).
-			 * Comparison: var[varIdx] vs imm.
-			 *
-			 * If condition met: call asound_readWord (reads addrLo/addrHi
-			 *   from current pSrc position), set _pSrc = &_soundData[addr].
-			 * If not met: _pSrc += 5 (command+varIdx+imm+addr word = 5 bytes).
-			 *
-			 * var_2 is used as a flag: 0 = branch taken, 1 = not taken.
-			 * (Confusingly, var_2 = 1 means "taken" in opcodes7 but
-			 *  0 means "taken" in opcodes6 - see def_11292 / def_11360 difference.)
-			 *
-			 * opcodes6: var_2=1 -> taken (call readWord, set pSrc),
-			 *            var_2=0 -> not taken (_pSrc += 5).
-			 * ============================================================ */
-			pSrc++;
-			uint8 varIdx = *pSrc++;
-			uint8 imm = *pSrc;
-			uint8 vval = _scriptVars[varIdx];
-			bool  taken = false;
-
-			switch (di) {
-			case 0x0: taken = ((uint16)vval == (uint16)imm);                    break; /* == */
-			case 0x1: taken = ((uint16)vval != (uint16)imm);                    break; /* != */
-			case 0x2: taken = ((int16)(uint16)vval >= (int16)(uint16)imm);      break; /* jge -> taken if >= */
-			case 0x3: taken = ((int16)(uint16)vval <= (int16)(uint16)imm);      break; /* jle -> taken if <= */
-			case 0x4: taken = (_scriptVars[varIdx] != _scriptVars[imm]);        break; /* jnz after cmp [di],[si] */
-			case 0x5: taken = (_scriptVars[varIdx] == _scriptVars[imm]);        break; /* jz  after cmp [di],[si] */
-			case 0x6: taken = (_scriptVars[imm] <= _scriptVars[varIdx]);        break; /* jbe -> [di]<=[si] taken */
-			case 0x7: taken = (_scriptVars[imm] >= _scriptVars[varIdx]);        break; /* jnb -> [di]>=[si] taken */
-			default:  break;
-			}
-
-			ch = _activeChannelPtr;
-			if (taken) {
-				ch->_pSrc = &_soundData[readWord_impl()];
-			} else {
-				ch->_pSrc += 5;
-			}
-			goto dispatch;
-		}
-
-		if (si == 0x60) {
-			/* ============================================================
-			 * opcodes7  (group 6, 0xE_)
-			 * Conditional branch: compare var[si] vs var[di] (both scriptVars).
-			 *
-			 * Format: [cmd] [varIdxA] [varIdxB] [addrLo] [addrHi]
-			 * Entry: inc pSrc, read varIdxA (si), inc pSrc, read varIdxB (di).
-			 * var_2 = 0 initially.
-			 *
-			 * If condition met: var_2 = 1 -> branch taken:
-			 *   _pSrc = pSrc + 5; _branchTargetPtr = pSrc + 5;
-			 *   call readWord -> _pSrc = &_soundData[addr].
-			 * If not met (var_2 = 0): _pSrc += 5 (skip condition + word).
-			 *
-			 * Note: unlike opcodes6, opcodes7 DOES save a branch-target
-			 * (_branchTargetPtr = _pSrc + 5) on the taken path.
-			 * ============================================================ */
-			pSrc++;
-			uint8 idxA = *pSrc++;
-			uint8 idxB = *pSrc;
-			uint8 va = _scriptVars[idxA];
-			uint8 vb = (uint8)idxB;   /* di byte used as an immediate in some cases... */
-
-			/* Actually in opcodes7 the layout is var vs var:
-			 * si = varIdxA, di = varIdxB (both are variable indices). */
-			vb = _scriptVars[idxB];
-
-			bool taken = false;
-			switch (di & 0x07) {
-			case 0x0: taken = ((uint16)va != (uint16)vb);  break; /* jnz after cmp ax,di (case 0: jz -> NOT taken if ==; var_2 stays 0; but then def path: var_2==0 -> skip. Wait - let me re-read.) */
-				/* Re-reading loc_112F0: cmp ax,di; jz loc_11352 (-> var_2=1=taken).
-				 * So case 0: taken = (va == vb). */
-			default: break;
-			}
-
-			/* Actually re-reading carefully:
-			 * case 0 (loc_112F0): cmp ax,di; jz -> loc_11352 (var_2=1, taken)
-			 *                      else -> loc_112FA (ax=0, var_2=0, not taken)
-			 * case 1 (loc_11302): cmp ax,di; jz -> loc_1130A -> (jz loc_112FA, not taken)
-			 *                     else -> loc_11284 (var_2=1, taken)
-			 * -> case 1: taken = (va != vb)
-			 * case 2 (loc_1130E): jge -> loc_112FA (not taken); else loc_11284 (taken)
-			 * -> taken = (va < vb) (signed)
-			 * case 3 (loc_1131A): jle -> not taken; else taken
-			 * -> taken = (va > vb) (signed)
-			 * case 4 (loc_11326): cmp [di],al; jnz -> loc_112FA (not taken); else loc_11284 (taken)
-			 * -> taken = (_scriptVars[idxB] == va) (already same as case 0 with vars swapped)
-			 * case 5 (loc_11332): cmp [di],al; jz->loc_1130A (not taken if ==, taken if !=)
-			 * -> taken = (_scriptVars[idxB] != va)
-			 * case 6 (loc_1133C): jbe -> not taken; else taken -> taken = (_scriptVars[idxB] > va)
-			 * case 7 (loc_11348): jnb -> not taken; else taken -> taken = (_scriptVars[idxB] < va)
-			 */
-			switch (di) {
-			case 0x0: taken = (va == vb);               break;
-			case 0x1: taken = (va != vb);               break;
-			case 0x2: taken = ((int8)va < (int8)vb);   break;
-			case 0x3: taken = ((int8)va > (int8)vb);   break;
-			case 0x4: taken = (vb == va);               break;
-			case 0x5: taken = (vb != va);               break;
-			case 0x6: taken = (vb > va);                break;
-			case 0x7: taken = (vb < va);                break;
-			default:  break;
-			}
-
-			ch = _activeChannelPtr;
-			if (taken) {
-				/* Save return target (pSrc+5 from the command byte). */
-				ch->_branchTargetPtr = ch->_pSrc + 5;
-				ch->_pSrc = &_soundData[readWord_impl()];
-			} else {
-				ch->_pSrc += 5;
-			}
-			goto dispatch;
-		}
-
-		/* Unknown group: skip. */
-		goto dispatch;
-	}
-
-note_event:
-	{
-		/* ---- Note event: [note][duration] ---- */
-		ch = _activeChannelPtr;
-		pSrc = ch->_pSrc;
-		ch->_note = *pSrc;
-		pSrc++;
-		ch->_activeCount = *pSrc;
-		pSrc++;
-		ch->_pSrc += 2;
-
-		ch = _activeChannelPtr;
-		if (ch->_note == 0 || ch->_activeCount == 0) {
-			updateOctave();
-			goto post_keyon;
-		}
-
-		/* ---- Key-on ---- */
-		ch = _activeChannelPtr;
-		if (ch->_durationOverride != 0)
-			ch->_keyOnDelay = ch->_durationOverride;
-		else
-			ch->_keyOnDelay = ch->_activeCount - ch->_noteOffset;
-		noteOn();
-	}
-
-post_keyon:
-	{
-		ch = _activeChannelPtr;
-
-		/* ---- Arpeggio counter (field_13 / _arpCounterReload) ---- */
-		if (ch->_arpCounterReload != 0) {
-			/* Decrement the arpeggio period counter (field_C / _arpPeriodCounter). */
-			ch->_arpPeriodCounter--;
-			ch = _activeChannelPtr;
-			if (ch->_arpPeriodCounter == 0) {
-				/* Reload from field_12 (_arpPeriodReload). */
-				ch->_arpPeriodCounter = ch->_arpPeriodReload;
-				/* Call sub_117E8 (writeArpeggio - writes the arpeggio frequency). */
-				writeArpeggio();
-			}
-			ch = _activeChannelPtr;
-			/* Decrement the repeat counter unless it is the infinite sentinel 0xFF. */
-			if (ch->_arpCounterReload != 0xFF)
-				ch->_arpCounterReload--;
-		}
-
-		/* ---- Write-volume pending (field_11 / _writeVolumePending) ---- */
-		ch = _activeChannelPtr;
-		if (ch->_writeVolumePending != 0) {
-			/* sub_11856 was already called by writeArpeggio above (or this is
-			 * a standalone field_11 set via opcode 8).  Clear the flag. */
-			writeArpeggio();
-			ch = _activeChannelPtr;
-			ch->_writeVolumePending = 0;
-		}
-
-		/* ---- Fade / volume step ---- */
-		ch = _activeChannelPtr;
-		if (ch->_fadePeriodCounter != 0) {
-			ch->_fadePeriodCounter--;
-			ch = _activeChannelPtr;
-			if (ch->_fadePeriodCounter == 0) {
-				ch->_fadePeriodCounter = ch->_fadePeriodReload;
-				ch = _activeChannelPtr;
-				if (ch->_volumeFadeStep != 0) {
-					if (ch->_pendingStop != 0) {
-						/* Pending-stop fade: step velocity and volume down. */
-						if (ch->_velocity > 0)
-							ch->_velocity += ch->_volumeFadeStep;
-						ch = _activeChannelPtr;
-						if (ch->_volume != 0)
-							ch->_volume += ch->_volumeFadeStep;
-					} else {
-						/* Normal fade: step velocity, clamp to [0, 0x7F]. */
-						if ((int8)ch->_volumeFadeStep > 0) {
-							ch->_velocity += ch->_volumeFadeStep;
-							ch = _activeChannelPtr;
-							if ((uint16)ch->_velocity > 0x7F)
-								ch->_velocity = 0x7F;
-						} else {
-							ch->_velocity += ch->_volumeFadeStep;
-							ch = _activeChannelPtr;
-							if ((int8)ch->_velocity < 0)
-								ch->_velocity = 0;
-						}
-					}
-					volDirty = true;   /* byte_16A0A = 1 */
-				}
-			}
-		}
-
-		/* ---- Vibrato ---- */
-		ch = _activeChannelPtr;
-		if (ch->_vibPeriodCounter != 0) {
-			ch->_vibPeriodCounter--;
-			ch = _activeChannelPtr;
-			if (ch->_vibPeriodCounter == 0) {
-				ch->_vibPeriodCounter = ch->_vibPeriodReload;
-				ch = _activeChannelPtr;
-
-				if ((int8)ch->_vibratoDepth > 0) {
-					/* Positive vibrato: ceiling at 0x7F. */
-					uint8 sum = ch->_vibratoDepth + ch->_patchAttenuation;
-					if (sum > 0x7F) {
-						if (ch->_vibratoMode == 0xFF) {
-							/* One-shot: clamp and stop. */
-							ch->_vibratoDepth = 0;
-							ch->_patchAttenuation = 0x7F;
-						} else if (sum == 0x80 && ch->_vibratoDepth != 1) {
-							/* Soft bounce at 0x80: patchAtten = 0x7F - vibratoDepth. */
-							ch->_patchAttenuation = 0x7F - ch->_vibratoDepth;
-						} else {
-							ch->_vibratoDepth = (uint8)(-(int8)ch->_vibratoDepth);
-						}
-					}
-					ch = _activeChannelPtr;
-					ch->_patchAttenuation += ch->_vibratoDepth;
-					volDirty = true;
-
-				} else if ((int8)ch->_vibratoDepth < 0) {
-					/* Negative vibrato: floor at 0. */
-					uint8 sum = ch->_vibratoDepth + ch->_patchAttenuation;
-					/* Note: sum is uint8 wrapping, but the asm uses "or al,al; jge"
-					 * treating the result as signed. */
-					if ((int8)sum < 0) {
-						if (ch->_vibratoMode == 0xFF) {
-							ch->_vibratoDepth = 0;
-							ch->_patchAttenuation = 0;
-						} else if (sum == 0xFF && ch->_vibratoDepth != 0xFF) {
-							/* Soft bounce: patchAtten = neg(vibratoDepth). */
-							ch->_patchAttenuation = (uint8)(-(int8)ch->_vibratoDepth);
-						} else {
-							ch->_vibratoDepth = (uint8)(-(int8)ch->_vibratoDepth);
-						}
-					}
-					ch = _activeChannelPtr;
-					ch->_patchAttenuation += ch->_vibratoDepth;
-					volDirty = true;
-				}
-				/* vibratoDepth == 0: nothing to do. */
-			}
-		}
-
-		if (volDirty)
-			writeVolume();
-	}
-
-	++_activeChannelNumber;
-}
-
-void ASound::playSound(int offset) {
-	findFreeChannelFull(loadData(offset));
-}
-
-} // namespace Forest
-} // namespace MADSV2
-} // namespace MADS
diff --git a/engines/mads/madsv2/forest/asound.h b/engines/mads/madsv2/forest/asound.h
deleted file mode 100644
index 32fefa08461..00000000000
--- a/engines/mads/madsv2/forest/asound.h
+++ /dev/null
@@ -1,649 +0,0 @@
-/* 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 MADS_FOREST_ASOUND_H
-#define MADS_FOREST_ASOUND_H
-
-#include "common/mutex.h"
-#include "common/queue.h"
-#include "common/util.h"
-#include "mads/core/sound_manager.h"
-
-namespace MADS {
-namespace MADSV2 {
-namespace Forest {
-
-#define ADLIB_CHANNEL_COUNT 9
-
-struct AdlibChannel {
-	static bool _isDisabled;
-
-	// ---- byte fields (offsets 0x00 - 0x13) --------------------------------
-
-	uint8  _activeCount = 0; // 0x00  duration tick countdown; 0 = channel idle
-	uint8  _pitchBend = 0; // 0x01  signed pitch-bend offset applied by writePitchBend
-	uint8  _volumeFadeStep = 0; // 0x02  signed per-period volume/velocity delta (0xFF = fade out)
-	uint8  _vibratoDepth = 0; // 0x03  signed LFO depth; negated each period to oscillate
-	uint8  _note = 0; // 0x04  MIDI note number for current event
-	uint8  _sampleIndex = 0; // 0x05  index into _samples[] selecting the OPL patch
-	uint8  _volume = 0; // 0x06  channel volume (0-127)
-	uint8  _noteOffset = 0; // 0x07  subtracted from _activeCount to shorten note duration
-	uint8  _durationOverride = 0; // 0x08  when non-zero, overrides the stream's duration byte
-	uint8  _keyOnDelay = 0; // 0x09  countdown before the OPL key-on bit is cleared (gate time)
-	uint8  _fadePeriodCounter = 0; // 0x0A  counts down to 0 before applying _volumeFadeStep
-	uint8  _fadePeriodReload = 0; // 0x0B  reload value for _fadePeriodCounter
-	uint8  _arpPeriodCounter = 0; // 0x0C  arpeggio tick counter (reloaded from _arpPeriodReload)
-	uint8  _vibPeriodCounter = 0; // 0x0D  LFO tick counter; when it reaches 0 a vibrato step fires
-	uint8  _vibPeriodReload = 0; // 0x0E  reload value for _vibPeriodCounter
-	uint8  _patchAttenuation = 0; // 0x0F  per-note attenuation offset added on top of the patch TL
-	uint8  _velocity = 0; // 0x10  note velocity (0-127); used together with _volume for TL
-	uint8  _writeVolumePending = 0; // 0x11  non-zero -> call sub_11856 (secondary freq write) this tick
-	uint8  _arpPeriodReload = 0; // 0x12  arpeggio period reload value (set by opcode 9)
-	uint8  _arpCounterReload = 0; // 0x13  arpeggio counter reload (decremented; 0xFF = infinite)
-
-	// ---- pointer fields (offsets 0x14 - 0x1F, word-sized in original) ----
-
-	byte *_loopStartPtr = nullptr; // 0x14  start of the current loop body
-	byte *_pSrc = nullptr; // 0x16  current read pointer into the sound-data stream
-	byte *_innerLoopPtr = nullptr; // 0x18  inner-loop restart address
-	byte *_outerLoopPtr = nullptr; // 0x1A  outer-loop restart address
-	byte *_soundData = nullptr; // 0x1C  base address of this channel's sound-data block
-	byte *_branchTargetPtr = nullptr; // 0x1E  target of the most recent branch/jump opcode
-
-	// ---- word counter fields (offsets 0x20 - 0x29) ----------------------
-
-	uint16 _innerLoopCount = 0; // 0x20  remaining inner-loop iterations (0 = infinite until opcode)
-	uint16 _outerLoopCount = 0; // 0x22  remaining outer-loop iterations (0 = infinite until opcode)
-	uint16 _noiseFreqMask = 0; // 0x24  AND mask applied to the random number in noise mode
-	uint16 _freqAccum = 0; // 0x26  frequency sweep accumulator (base frequency + swept offset)
-	uint16 _freqStep = 0; // 0x28  per-tick increment added to _freqAccum during a sweep
-
-	// ---- sweep / pause state (offsets 0x2A - 0x2D) ----------------------
-
-	// 0x2A-0x2B  Last computed OPL total-level bytes written by writeVolume.
-	//            lo byte (0x2A) = carrier TL nibble; hi byte (0x2B) = modulator TL nibble.
-	//            Written at the end of asound_writeVolume so that command7 (resume) can
-	//            restore operator levels without a full recalculation.
-	uint8  _cachedCarrierTL = 0; // 0x2A  cached carrier total-level (6-bit, 0 = loudest)
-	uint8  _savedFreqSweep = 0; // 0x2B  copy of _freqSweepCounter saved by command6 (pause)
-	uint8  _freqSweepCounter = 0; // 0x2C  countdown for frequency-sweep ticks; 0 = sweep done
-	uint8  _savedSweepCounter = 0; // 0x2D  second save slot: _freqSweepCounter is mirrored here
-	//       by command6/7 so command7 can restore the live value
-
-// ---- tuning / control (offsets 0x2E - 0x31) ------------------------
-
-	uint8  _transpose = 0; // 0x2E  semitone offset added when looking up _semitoneFreqTable
-	uint8  _octaveTranspose = 0; // 0x2F  octave shift: added to _note before octave calculation
-	uint8  _pendingStop = 0; // 0x30  0xFF = channel is fading out and will go idle when silent
-	uint8  _vibratoMode = 0; // 0x31  0xFF = one-shot vibrato (clamp & stop); else = continuous
-
-	/**
-	 * Zeroes the "live" fields of one channel without touching the
-	 * loop/sound-data pointers (those are handled separately by load).
-	 */
-	void reset();
-
-	/**
-	 * Clears the entire channel struct, then initialises all loop/source
-	 * pointers to the supplied sound-data address, sets _patchAttenuation to
-	 * 0x40 (maximum, silent), and marks the channel active (_activeCount = 1).
-	 */
-	void load(byte *soundData);
-
-	/**
-	 * Redirects _loopStartPtr and _pSrc to the null/silence stream and arms a
-	 * one-step volume fade (_volumeFadeStep = 0xFF, _fadePeriodReload/Counter = 1)
-	 * so the channel fades out gracefully over the next tick.
-	 */
-	void setPtr2(byte *ptr);
-
-	/**
-	 * Marks the channel pending-stop (_pendingStop = 0xFF) and redirects
-	 * _soundData to the null/silence stream.  Unlike setPtr2 this does not
-	 * touch _volumeFadeStep; the channel fades naturally via processChannelFade.
-	 */
-	void enable();
-
-	/**
-	 * Called every frame for each channel.  When _pendingStop is set, waits
-	 * until both _velocity and _volume are zero before marking the channel idle
-	 * (_activeCount = 0); otherwise applies _volumeFadeStep to velocity/volume
-	 * each _fadePeriodReload ticks and clamps the result to [0, 0x7F].
-	 */
-	void processChannelFade();
-};
-
-// ---------------------------------------------------------------------------
-// AdlibSample  (unchanged from Return of the Phantom, 0x16 bytes)
-// ---------------------------------------------------------------------------
-struct AdlibSample {
-	uint8  _attackRate = 0;
-	uint8  _decayRate = 0;
-	uint8  _sustainLevel = 0;
-	uint8  _releaseRate = 0;
-	uint8  _egTyp = 0;
-	uint8  _ksr = 0;
-	uint8  _totalLevel = 0;
-	uint8  _scalingLevel = 0;
-	uint8  _waveformSelect = 0;
-	uint8  _freqMultiple = 0;
-	uint8  _feedback = 0;
-	uint8  _ampMod = 0;
-	uint8  _vib = 0;
-	uint8  _alg = 0;
-	uint8  _freqSweepInit = 0; // initial _freqSweepCounter value; 0 = immediate key-on
-	uint8  _reserved = 0;
-	uint16 _freqMask = 0; // loaded into channel _noiseFreqMask
-	uint16 _freqBase = 0; // loaded into channel _freqAccum
-	uint16 _outerLoopPtr = 0; // loaded into channel _freqStep
-
-	AdlibSample() {
-	}
-	AdlibSample(Common::SeekableReadStream &s);
-};
-
-// ---------------------------------------------------------------------------
-// ASound  -  Dragonsphere Adlib sound driver base class
-//
-// Command dispatch table layout (from off_11A14 / off_11A26 / off_11A2E /
-// funcs_12251 / off_11A64 in the disassembly):
-//
-//   Table 1  off_11A14  commands  0- 8   (max=8,    base=0,    9 entries)
-//   Table 2  off_11A26  commands 16-19   (max=0x13, base=0x10, 4 entries)
-//             command16  = background-music dispatcher (calls command18)
-//             command17  = play specific piece (loads 7 channels direct)
-//             command18  = re-entrant music launcher (reads word_12370 to pick a sub-command)
-//             command19  = no-op (asound_command98)
-//   Table 3  off_11A2E  commands 24-32   (max=0x20, base=0x18, 9 entries)
-//   Table 4  funcs_12251 commands 32-49  (max=0x31, base=0x20, 18 entries)
-//             Includes asound_command32-48 (music pieces / SFX loaders)
-//             and asound_command98 (no-op) in the last slot.
-//   Table 5  off_11A64  commands 64-101  (max=0x65, base=0x40, 38 entries)
-//             Includes asound_command64-101 (single-shot SFX loaders via
-//             findFreeChannel / findFreeChannelFull) and two no-ops.
-//
-// The driver also exposes:
-//   asound_command90 / 91  - two-voice SFX (findFreeChannelFull x2)
-//   asound_command95       - four-voice music piece (findFreeChannel x4)
-//   sub_11F98              - two-voice SFX (findFreeChannelFull x2)
-//
-// word_12370 tracks the "current music index" used by command18 to select
-// which music-piece loader to call.
-// ---------------------------------------------------------------------------
-class ASound : public SoundDriver {
-protected:
-	/** Member-function pointer type for deferred sound-loader callbacks. */
-	typedef void (ASound::*CallbackFunction)();
-
-private:
-	// ---- callback / tick state ------------------------------------------
-	uint16 _callbackCounter = 0;  // per-tick countdown
-	uint16 _callbackPeriod = 0;  // reload value; 0 = callback disabled
-	// Pointer to the deferred sound-loader called when the
-	// counter fires (stored as uint16 in the original; typed as a member
-	// function pointer in the C++ port).
-	CallbackFunction _callbackFnPtr = nullptr;
-
-	// ---- active-channel context (set by pollAllChannels) ----------------
-	AdlibChannel *_activeChannelPtr = nullptr;
-	uint8          _activeChannelNumber = 0;
-	uint16         _activeChannelReg = 0; // 0xA0+ch register for the active channel
-	uint16         _currentOpBase = 0; // operator register base for the active channel
-
-	// ---- per-frame working state ----------------------------------------
-	AdlibSample *_samplePtr = nullptr; // patch being loaded/written
-	byte *pSrc = nullptr; // current read pointer (mirrors channel _pSrc)
-	int16          _pollResult = 0;
-	int16          _resultFlag = 0;
-	uint16         _randomSeed = 0x4D2;
-
-	// ---- driver-wide flags ----------------------------------------------
-	uint16 _isDisabled = 0; // non-zero while the engine is paused (command6)
-	uint8  _findChannelMode = 0; // 0=full search, 1=ch0-5 only, 2=ch6-8 then pending
-
-	// ---- per-channel sweep shadows (for channel 5 special-casing) -------
-	//      Not present as separate globals in Dragonsphere; the channel
-	//      struct fields _savedFreqSweep / _savedSweepCounter handle this.
-
-	// ---- misc globals ---------------------------------------------------
-	uint8  _anySweepActive = 0; // set to 1 if any channel has _freqSweepCounter > 0
-	int    _frameNumber2 = 0; // secondary frame counter incremented every update
-
-	// ---- script / sequencer registers -----------------------------------
-	uint8  _scriptVars[32] = {}; // byte_16A10: 32 general-purpose script registers
-
-	// ---- music-index tracker (word_12370) --------------------------------
-	// Tracks which music piece was last launched by command18.
-	uint16 _musicIndex = 0;
-
-	// ---- tempo / sequencer state (from opcodes3 group 2 handlers) --------
-	uint8  _musicOnlyFlag = 0;     // byte_12393: 1=music-only check, 0=all channels
-	uint16 _tempoFineStep = 0;     // word_124F2: fine tempo step (opcode A6)
-	uint16 _tempoCoarseStep = 0;   // word_124F0: coarse tempo step (opcode A7)
-	uint16 _tempoPeriod = 0;       // word_124EE: tempo period in ticks (opcode A8)
-	uint8  _tempoEnabled = 0;      // non-zero when tempo tick is active (opcode A8)
-	uint16 _tempoTickCounter = 0;  // countdown for tempo callback
-	uint16 _tempoShift = 0;        // word_124F4: tempo shift (opcode A9)
-
-	// =========================================================================
-	// Private helpers
-	// =========================================================================
-
-	/** Timer callback entry point; calls update(). */
-	void onTimer();
-
-	/** Zeros _callbackFnPtr, _callbackCounter, and _callbackPeriod. */
-	void clearCallback();
-	void resetCallback() {
-		clearCallback();
-	}
-
-	/**
-	 * Forces one OPL operator to maximum attenuation (total-level = 0x3F)
-	 * while preserving its upper KSL bits.  'portIndex' is the register index
-	 * into _adlibPorts (i.e. the 0x40-range operator register).
-	 * Used by command6 to mute all operator pairs during a pause.
-	 */
-	void adlib_channelOff(uint8 portIndex);
-
-	/**
-	 * Computes and writes the total-level registers for the active channel
-	 * (asound_writeVolume).
-	 *
-	 * Two top-level paths depending on the _alg field of the first sample:
-	 *   _alg == 0  (FM): both operators contribute; loop twice writing
-	 *              VOICE_SLOTS[ch][0] then VOICE_SLOTS[ch][1].
-	 *   _alg != 0  (additive): only the carrier (slot 1) carries volume.
-	 *
-	 * Within each pass, two inner paths based on OPL version:
-	 *   < 0x18  (OPL2): TL = clamp(0x3F - var_2, 0, 0x3F).
-	 *   >= 0x18 (OPL3): patch-attenuation-aware mapping using
-	 *              PATCH_ATTEN_TO_TL and VOL_VEL_TO_ATTEN_STEP tables;
-	 *              writes two registers (offset 0 and offset 2).
-	 *
-	 * Caches the two written TL bytes in _cachedCarrierTL / _savedFreqSweep
-	 * so command7 can restore levels without a full recalculation.
-	 */
-	void writeVolume();
-
-	/**
-	 * Derives the OPL F-number and block (octave) from _note, _octaveTranspose,
-	 * and _transpose using _semitoneFreqTable, then writes registers 0xA0+ch
-	 * and 0xB0+ch (with key-on bit set).
-	 */
-	void writeFrequency();
-
-	/**
-	 * Applies _pitchBend as a signed offset to the already-computed frequency
-	 * registers without a full note recalculation.
-	 */
-	void writePitchBend();
-
-	/**
-	 * Arpeggio frequency write (sub_11856).
-	 * Called from pollActiveChannel when _writeVolumePending (field_11) is set.
-	 * Computes a modified frequency from _note + _octaveTranspose + field_11 - 1
-	 * and writes it to the OPL registers, preserving the key-on bit.
-	 */
-	void writeArpeggio();
-
-	/**
-	 * Clears the key-on bit (bit 5) of the 0xB0+ch register, silencing the
-	 * note while preserving pitch.  Called during _keyOnDelay countdown.
-	 */
-	void updateOctave();
-
-	/**
-	 * Arms the frequency sweep counter from the sample definition, then - if
-	 * _freqSweepCounter is zero - immediately triggers the note by writing
-	 * volume and frequency registers and setting the key-on bit.
-	 */
-	void noteOn();
-
-	/**
-	 * Writes all OPL operator registers for the patch pointed to by _samplePtr,
-	 * using _currentOpBase as the operator base.  Covers ADSR, KSL/TL,
-	 * feedback/algorithm, AM/VIB flags, and waveform-select registers.
-	 */
-	void writeSampleRegs();
-
-	/**
-	 * Loads all OPL registers for the patch assigned to the active channel,
-	 * covering both modulator and carrier operators, and sets up the channel's
-	 * sweep/frequency state from the AdlibSample definition.
-	 */
-	void loadSample();
-
-	/**
-	 * Main per-frame update: increments frame counters, polls all channels,
-	 * fires the tick callback, calls updateAllChannels, then runs the per-channel
-	 * frequency-sweep tick for all 9 voices.
-	 */
-	void update();
-
-	/**
-	 * Per-channel frequency-sweep tick for channel 'channelIndex' (0-based).
-	 * When _freqSweepCounter > 0: adds _freqStep to _freqAccum, decrements the
-	 * counter, and - when it reaches zero - zeroes the voice's frequency registers.
-	 * Sets _anySweepActive when the counter is still non-zero.
-	 */
-	void update1(int channelIndex);
-
-	/**
-	 * Writes a frequency value directly to the OPL registers for 'voice'.
-	 * The high byte selects the block/octave; the low byte is the F-number LSB.
-	 */
-	void setFrequency(uint8 voice, uint16 freq);
-
-	/**
-	 * Noise-channel inner tick: picks a random frequency offset masked by
-	 * _noiseFreqMask, adds _freqAccum, and writes the result to the voice
-	 * frequency registers.
-	 */
-	void noise_inner(int channelIndex);
-
-	/**
-	 * Calls processChannelFade for all 9 channels each frame.
-	 */
-	void updateAllChannels();
-
-	/**
-	 * Advances pSrc by one, reads two bytes little-endian, and returns the word.
-	 * Used by the bytecode opcodes that take 16-bit operands.
-	 */
-	uint16 readWord_impl();
-
-	/**
-	 * Deferred-callback tick: decrements _callbackCounter; when it reaches zero,
-	 * reloads it from _callbackPeriod and calls _callbackFnPtr (if non-null),
-	 * then clears _callbackFnPtr so it fires exactly once.
-	 */
-	void tickCallback();
-
-protected:
-	// ---- nine AdlibChannel instances ------------------------------------
-	AdlibChannel _channel0, _channel1, _channel2;
-	AdlibChannel _channel3, _channel4, _channel5;
-	AdlibChannel _channel6, _channel7, _channel8;
-	AdlibChannel *_channels[ADLIB_CHANNEL_COUNT] = {
-		&_channel0, &_channel1, &_channel2,
-		&_channel3, &_channel4, &_channel5,
-		&_channel6, &_channel7, &_channel8
-	};
-
-	uint8  _adlibPorts[0x100] = { 0 };
-	Common::Array<AdlibSample> _samples;
-
-protected:
-	// =========================================================================
-	// Protected helpers  (used by ASound1???ASound9)
-	// =========================================================================
-
-	/**
-	 * Checks whether any of channels 0-6 (or 0-8 when _musicOnlyFlag is clear)
-	 * have a non-zero _activeCount.  Returns non-zero if sound is playing.
-	 * This is 'sub_1061A' in the disassembly.
-	 */
-	int isMusicChannelsActive();
-
-	/**
-	 * Like isMusicChannelsActive but scans all 9 channels unconditionally
-	 * (clears the ch0-6-only flag first).  This is 'sub_1064E'.
-	 */
-	int isAnyChannelActive();
-
-	/**
-	 * Schedule fn as the next deferred-load callback.
-	 * Does NOT touch _callbackCounter or _callbackPeriod ??? those are preserved
-	 * from the previous loader so the callback fires on the right beat.
-	 * Cast the derived-class member-function pointer with reinterpret_cast.
-	 */
-	void scheduleCallback(CallbackFunction fn) { _callbackFnPtr = fn; }
-
-	/**
-	 * Arm the periodic timer and clear any pending callback pointer.
-	 * Call at the head of every immediate-load function (symmetric counter/period).
-	 */
-	void resetCallbackTimer(uint16 period) {
-		_callbackFnPtr = nullptr;
-		_callbackCounter = period;
-		_callbackPeriod = period;
-	}
-
-	/**
-	 * Arm the periodic timer with separate counter and period values.
-	 * Used by command44 which sets counter=0x60 but period=0xE0.
-	 */
-	void resetCallbackTimerEx(uint16 counter, uint16 period) {
-		_callbackFnPtr = nullptr;
-		_callbackCounter = counter;
-		_callbackPeriod = period;
-	}
-
-	/** Set the music-piece index (word_12370) read by command18. */
-	void setMusicIndex(uint16 idx) { _musicIndex = idx; }
-	/** Read the current music-piece index. */
-	uint16 getMusicIndex() const { return _musicIndex; }
-
-	/** Write one script-variable register (byte_16A10[idx]). */
-	void setScriptVar(int idx, uint8 val) { _scriptVars[idx] = val; }
-	/**
-	 * Writes (reg, value) to the OPL chip and updates the _adlibPorts shadow
-	 * array so subsequent reads return the last-written value.
-	 */
-	void write(uint8 reg, uint8 value);
-
-	/** Updates and returns _randomSeed using a simple linear-feedback shift. */
-	uint16 getRandomNumber();
-
-	/**
-	 * Restores one OPL operator by re-writing the value already stored in the
-	 * _adlibPorts shadow array.  command7 uses this to resume playback after
-	 * a command6 pause without recalculating any TL values.
-	 * 'portIndex' is the operator register index into _adlibPorts.
-	 */
-	void adlib_channelOn(uint8 portIndex);
-
-	/** Sets _pollResult and _resultFlag to indicate that sound is playing. */
-	void signalSoundPlaying();
-
-	/**
-	 * Iterates over all 9 channels, sets _activeChannelPtr / _activeChannelNumber,
-	 * and calls pollActiveChannel for each one.
-	 */
-	void pollAllChannels();
-
-	/**
-	 * Per-channel bytecode interpreter, called once per frame per active channel.
-	 *
-	 * Sound data is a sequence of (note, duration) byte pairs plus command bytes
-	 * with the high bit set (0x80-0xFF).  The upper nibble of a command byte
-	 * (after masking out the high bit) selects one of seven opcode groups:
-	 *
-	 *   0x0_  -> opcodes1  (patch/velocity/volume/vibrato/transpose/arpeggio)
-	 *   0x1_  -> opcodes2  (inner/outer loop control, restart, branch/call)
-	 *   0x2_  -> opcodes3  (tempo, script-variable arithmetic, call-by-address)
-	 *   0x3_  -> opcodes4  (script-variable load/store/copy/inc/dec)
-	 *   0x4_  -> opcodes5  (script-variable ALU: add/sub/mul/div with imm or var)
-	 *   0x5_  -> opcodes6  (extended: random-range, indexed table read/write)
-	 *   0x6_  -> opcodes7  (driver-level calls: command dispatch, etc.)
-	 *
-	 * The lower nibble is passed as the sub-opcode to each group handler.
-	 * Note bytes (high bit clear) consume one duration tick per call.
-	 */
-	void pollActiveChannel();
-
-	/** Returns true if the sound data block at 'ptr' is already playing. */
-	bool isSoundActive(byte *ptr) const;
-
-	/**
-	 * Scans channels 0-6 for an empty slot (_activeCount == 0) and calls
-	 * load() on the first one found.  Falls through to findFreeChannelFull
-	 * for channels 6-8 when _findChannelMode != 1.
-	 */
-	void findFreeChannel(byte *soundData);
-
-	/**
-	 * Extends the search to channels 7-8, then checks for pending-stop
-	 * channels (which can be pre-empted), working in reverse priority order
-	 * (ch8, ch7, ch6 ... ch0).
-	 */
-	void findFreeChannelFull(byte *soundData);
-
-	/** Returns a pointer to the sound data at the given offset. */
-	byte *loadData(int offset) {
-		return &_soundData[offset];
-	}
-
-protected:
-	// =========================================================================
-	// Core commands (0-8)  -  identical in purpose to Return of the Phantom
-	// =========================================================================
-
-	/**
-	 * command0: Full hardware reset.
-	 *   1. Reset all 9 channels.
-	 *   2. Mute all operator TL registers (0x40-0x55) to 0x3F.
-	 *   3. Zero remaining operator registers (0x60-0xFF and 0x01-0x3F).
-	 *   4. Write Waveform Select Enable (register 0x01 = 0x20).
-	 *   5. Reset the tick callback.
-	 */
-	int command0();
-
-	/**
-	 * command1: Fade out all channels.
-	 *   Calls command3 (fade music channels 0-6) then command5 (fade SFX 7-8).
-	 */
-	int command1();
-
-	/**
-	 * command2: Fade out music channels 0-6.
-	 *   Calls AdlibChannel::setPtr2 on each, redirecting to the null stream
-	 *   and arming a one-step fade.
-	 */
-	int command2();
-
-	/**
-	 * command3: Fade out music channels 0-6 with pending-stop.
-	 *   Calls AdlibChannel::enable on channels 0-6.
-	 */
-	int command3();
-
-	/**
-	 * command4: Fade out SFX channels 7-8.
-	 *   Calls AdlibChannel::setPtr2 on channels 7 and 8.
-	 */
-	int command4();
-
-	/**
-	 * command5: Stop SFX channels 7-8 with pending-stop flag.
-	 *   Calls AdlibChannel::enable on channels 7 and 8, letting each channel
-	 *   finish its current OPL envelope before going idle.
-	 */
-	int command5();
-
-	/**
-	 * command6: Pause playback.
-	 *   Saves each channel's _freqSweepCounter into _savedSweepCounter, zeroes
-	 *   _freqSweepCounter on all channels, then mutes all 22 operator TL
-	 *   registers (the byte_1239B table covers all operator slots 0x40-0x55).
-	 *   Sets _isDisabled to prevent further updates.
-	 */
-	int command6();
-
-	/**
-	 * command7: Resume playback.
-	 *   Restores operator volumes for all channels from _adlibPorts shadow,
-	 *   copies _savedSweepCounter back to _freqSweepCounter on all channels,
-	 *   signals sound playing if any channel was active, then clears _isDisabled.
-	 */
-	int command7();
-
-	/**
-	 * command8: Returns non-zero if any of the 9 channels has a non-zero
-	 *   _activeCount (i.e. sound is currently playing).
-	 *   Also clears the music-only flag (byte_12393 = 0) so the check covers
-	 *   all 9 channels.
-	 */
-	int command8();
-
-	/**
-	 * Calls a function at a fixed offset within the sound driver.
-	 * @param offset		Offset of the function
-	 */
-	virtual void callFunction(uint16 offset);
-
-	// =========================================================================
-	// Music-index launcher (called via command18)
-	// =========================================================================
-
-	/**
-	 * command18: Re-entrant music launcher.
-	 *   First calls command1 to fade current output, then branches on _musicIndex
-	 *   (word_12370):
-	 *     <= 0x12  -> calls off_11A26 table (commands 16-19)
-	 *     > 0x12  -> calls funcs_12251 table (commands 32-49), index = _musicIndex - 0x20
-	 */
-	int command18();
-
-public:
-	/**
-	 * Constructor.
-	 * @param mixer       Mixer instance
-	 * @param opl         OPL chip instance
-	 * @param filename    Path to the .DR1 (or equivalent) sound-driver file
-	 * @param dataOffset  Offset in the file of the data segment
-	 * @param dataSize    Size of the data segment
-	 */
-	ASound(Audio::Mixer *mixer, OPL::OPL *opl, const Common::Path &filename,
-		int dataOffset, int dataSize);
-
-	~ASound() override {
-	}
-
-	/** Stop all currently playing sounds (wraps command0). */
-	virtual int stop() override;
-
-	/** Main poll method; drives the per-frame update. */
-	int poll() override;
-
-	/**
-	 * Noise channel tick: for each of the 9 channels calls noise_inner,
-	 * which randomises the voice frequency each frame using _noiseFreqMask.
-	 */
-	void noise() override;
-
-	/**
-	 * Starts playback of the sound data at the given byte offset within the
-	 * driver's data segment, using findFreeChannelFull to select a channel.
-	 */
-	void playSound(int offset);
-
-	void setVolume(int volume) override {
-		// TODO: implement if needed
-	}
-};
-
-} // namespace Forest
-} // namespace MADSV2
-} // namespace MADS
-
-#endif
diff --git a/engines/mads/madsv2/core/digi.cpp b/engines/mads/madsv2/forest/digi.cpp
similarity index 85%
rename from engines/mads/madsv2/core/digi.cpp
rename to engines/mads/madsv2/forest/digi.cpp
index c598d28ae94..3ab8e397273 100644
--- a/engines/mads/madsv2/core/digi.cpp
+++ b/engines/mads/madsv2/forest/digi.cpp
@@ -19,12 +19,13 @@
  *
  */
 
-#include "mads/madsv2/core/digi.h"
+#include "mads/madsv2/forest/digi.h"
 #include "mads/madsv2/core/config.h"
 #include "mads/madsv2/engine.h"
 
 namespace MADS {
 namespace MADSV2 {
+namespace Forest {
 
 int digi_val2;
 int digi_timing_index;
@@ -55,11 +56,17 @@ void digi_play_build(int room, char thing, int num, int slot) {
 	digi_play(name.c_str(), slot);
 }
 
-void digi_play(const char *name, int slot) {}
-void digi_stop(int which_one) {}
-void digi_read_another_chunk() {}
-void digi_initial_volume(int vol) {}
-void digi_set_volume(int vol, int slot) {}
+void digi_play(const char *name, int slot) {
+}
+void digi_stop(int which_one) {
+}
+void digi_read_another_chunk() {
+}
+void digi_initial_volume(int vol) {
+}
+void digi_set_volume(int vol, int slot) {
+}
 
+} // namespace Forest
 } // namespace MADSV2
 } // namespace MADS
diff --git a/engines/mads/madsv2/core/digi.h b/engines/mads/madsv2/forest/digi.h
similarity index 94%
rename from engines/mads/madsv2/core/digi.h
rename to engines/mads/madsv2/forest/digi.h
index f212c2f334c..58b64fc11a8 100644
--- a/engines/mads/madsv2/core/digi.h
+++ b/engines/mads/madsv2/forest/digi.h
@@ -19,13 +19,14 @@
  *
  */
 
-#ifndef MADS_DIGI_SOUND_H
-#define MADS_DIGI_SOUND_H
+#ifndef MADS_FOREST_DIGI_H
+#define MADS_FOREST_DIGI_H
 
 #include "common/scummsys.h"
 
 namespace MADS {
 namespace MADSV2 {
+namespace Forest {
 
 //extern int config_file.forest1;
 extern int digi_val2;
@@ -42,6 +43,7 @@ extern void digi_read_another_chunk();
 extern void digi_initial_volume(int vol);
 extern void digi_set_volume(int vol, int slot);
 
+} // namespace Forest
 } // namespace MADSV2
 } // namespace MADS
 
diff --git a/engines/mads/madsv2/forest/global.cpp b/engines/mads/madsv2/forest/global.cpp
index 3615775203e..d5bae9788be 100644
--- a/engines/mads/madsv2/forest/global.cpp
+++ b/engines/mads/madsv2/forest/global.cpp
@@ -20,7 +20,7 @@
  */
 
 #include "common/textconsole.h"
-#include "mads/madsv2/core/digi.h"
+#include "mads/madsv2/forest/digi.h"
 #include "mads/madsv2/core/object.h"
 #include "mads/madsv2/core/player.h"
 #include "mads/madsv2/core/text.h"
@@ -28,7 +28,7 @@
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/imath.h"
 #include "mads/madsv2/core/kernel.h"
-#include "mads/madsv2/core/midi.h"
+#include "mads/madsv2/forest/midi.h"
 #include "mads/madsv2/core/pal.h"
 #include "mads/madsv2/forest/global.h"
 #include "mads/madsv2/forest/extra.h"
diff --git a/engines/mads/madsv2/core/midi.cpp b/engines/mads/madsv2/forest/midi.cpp
similarity index 85%
rename from engines/mads/madsv2/core/midi.cpp
rename to engines/mads/madsv2/forest/midi.cpp
index 34ed0051361..6d5533bf289 100644
--- a/engines/mads/madsv2/core/midi.cpp
+++ b/engines/mads/madsv2/forest/midi.cpp
@@ -20,13 +20,20 @@
  */
 
 #include "common/textconsole.h"
-#include "mads/madsv2/core/midi.h"
+#include "mads/madsv2/forest/midi.h"
+#include "mads/madsv2/core/env.h"
 #include "mads/madsv2/engine.h"
 
 namespace MADS {
 namespace MADSV2 {
+namespace Forest {
 
 void midi_play(const char *name) {
+	assert(env_exist(name));
+
+	// TODO
+	warning("TODO: global_midi_play");
+
 	// TODO
 }
 
@@ -34,5 +41,6 @@ void midi_stop() {
 	// TODO
 }
 
+} // namespace Forest
 } // namespace MADSV2
 } // namespace MADS
diff --git a/engines/mads/madsv2/core/midi.h b/engines/mads/madsv2/forest/midi.h
similarity index 89%
rename from engines/mads/madsv2/core/midi.h
rename to engines/mads/madsv2/forest/midi.h
index 52515cc9adc..508c7ec82ac 100644
--- a/engines/mads/madsv2/core/midi.h
+++ b/engines/mads/madsv2/forest/midi.h
@@ -19,18 +19,21 @@
  *
  */
 
-#ifndef MADS_CORE_MIDI_H
-#define MADS_CORE_MIDI_H
+#ifndef MADS_FOREST_MIDI_H
+#define MADS_FOREST_MIDI_H
 
 #include "common/scummsys.h"
 
 namespace MADS {
 namespace MADSV2 {
+namespace Forest {
 
 extern void midi_play(const char *name);
 extern void midi_stop();
-inline void midi_loop() {}
+inline void midi_loop() {
+}
 
+} // namespace Forest
 } // namespace MADSV2
 } // namespace MADS
 
diff --git a/engines/mads/madsv2/forest/rooms/room101.cpp b/engines/mads/madsv2/forest/rooms/room101.cpp
index 8e60e2a8050..650ac79c08f 100644
--- a/engines/mads/madsv2/forest/rooms/room101.cpp
+++ b/engines/mads/madsv2/forest/rooms/room101.cpp
@@ -19,13 +19,13 @@
  *
  */
 
-#include "mads/madsv2/core/digi.h"
+#include "mads/madsv2/forest/digi.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/imath.h"
 #include "mads/madsv2/core/inter.h"
 #include "mads/madsv2/core/kernel.h"
 #include "mads/madsv2/core/matte.h"
-#include "mads/madsv2/core/midi.h"
+#include "mads/madsv2/forest/midi.h"
 #include "mads/madsv2/core/mouse.h"
 #include "mads/madsv2/core/object.h"
 #include "mads/madsv2/core/player.h"
diff --git a/engines/mads/madsv2/forest/rooms/room103.cpp b/engines/mads/madsv2/forest/rooms/room103.cpp
index f4f4b3a16ff..a3f08454721 100644
--- a/engines/mads/madsv2/forest/rooms/room103.cpp
+++ b/engines/mads/madsv2/forest/rooms/room103.cpp
@@ -19,13 +19,13 @@
  *
  */
 
-#include "mads/madsv2/core/digi.h"
+#include "mads/madsv2/forest/digi.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/imath.h"
 #include "mads/madsv2/core/inter.h"
 #include "mads/madsv2/core/kernel.h"
 #include "mads/madsv2/core/matte.h"
-#include "mads/madsv2/core/midi.h"
+#include "mads/madsv2/forest/midi.h"
 #include "mads/madsv2/core/sound.h"
 #include "mads/madsv2/core/sprite.h"
 #include "mads/madsv2/core/text.h"
diff --git a/engines/mads/madsv2/forest/rooms/room104.cpp b/engines/mads/madsv2/forest/rooms/room104.cpp
index 71c10cb5336..02f3b94bce2 100644
--- a/engines/mads/madsv2/forest/rooms/room104.cpp
+++ b/engines/mads/madsv2/forest/rooms/room104.cpp
@@ -19,13 +19,13 @@
  *
  */
 
-#include "mads/madsv2/core/digi.h"
+#include "mads/madsv2/forest/digi.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/imath.h"
 #include "mads/madsv2/core/inter.h"
 #include "mads/madsv2/core/kernel.h"
 #include "mads/madsv2/core/matte.h"
-#include "mads/madsv2/core/midi.h"
+#include "mads/madsv2/forest/midi.h"
 #include "mads/madsv2/core/mouse.h"
 #include "mads/madsv2/core/player.h"
 #include "mads/madsv2/core/sound.h"
diff --git a/engines/mads/madsv2/forest/rooms/room106.cpp b/engines/mads/madsv2/forest/rooms/room106.cpp
index 398d276c017..d600c93d2eb 100644
--- a/engines/mads/madsv2/forest/rooms/room106.cpp
+++ b/engines/mads/madsv2/forest/rooms/room106.cpp
@@ -20,13 +20,13 @@
  */
 
 #include "mads/madsv2/core/config.h"
-#include "mads/madsv2/core/digi.h"
+#include "mads/madsv2/forest/digi.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/imath.h"
 #include "mads/madsv2/core/inter.h"
 #include "mads/madsv2/core/kernel.h"
 #include "mads/madsv2/core/matte.h"
-#include "mads/madsv2/core/midi.h"
+#include "mads/madsv2/forest/midi.h"
 #include "mads/madsv2/core/object.h"
 #include "mads/madsv2/core/player.h"
 #include "mads/madsv2/core/sound.h"
diff --git a/engines/mads/madsv2/forest/rooms/room107.cpp b/engines/mads/madsv2/forest/rooms/room107.cpp
index f762365faeb..7176b0e1239 100644
--- a/engines/mads/madsv2/forest/rooms/room107.cpp
+++ b/engines/mads/madsv2/forest/rooms/room107.cpp
@@ -19,7 +19,7 @@
  *
  */
 
-#include "mads/madsv2/core/digi.h"
+#include "mads/madsv2/forest/digi.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/kernel.h"
 #include "mads/madsv2/core/matte.h"
diff --git a/engines/mads/madsv2/forest/rooms/room199.cpp b/engines/mads/madsv2/forest/rooms/room199.cpp
index 87cbfd4a322..bb9bb8458c3 100644
--- a/engines/mads/madsv2/forest/rooms/room199.cpp
+++ b/engines/mads/madsv2/forest/rooms/room199.cpp
@@ -19,7 +19,7 @@
  *
  */
 
-#include "mads/madsv2/core/digi.h"
+#include "mads/madsv2/forest/digi.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/global.h"
 #include "mads/madsv2/core/kernel.h"
diff --git a/engines/mads/madsv2/forest/rooms/room201.cpp b/engines/mads/madsv2/forest/rooms/room201.cpp
index 9684f3d598b..a90c96828f9 100644
--- a/engines/mads/madsv2/forest/rooms/room201.cpp
+++ b/engines/mads/madsv2/forest/rooms/room201.cpp
@@ -19,7 +19,7 @@
  *
  */
 
-#include "mads/madsv2/core/digi.h"
+#include "mads/madsv2/forest/digi.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/inter.h"
 #include "mads/madsv2/core/kernel.h"
diff --git a/engines/mads/madsv2/forest/rooms/room203.cpp b/engines/mads/madsv2/forest/rooms/room203.cpp
index 5088c5f7fd9..8324ca890c2 100644
--- a/engines/mads/madsv2/forest/rooms/room203.cpp
+++ b/engines/mads/madsv2/forest/rooms/room203.cpp
@@ -19,12 +19,12 @@
  *
  */
 
-#include "mads/madsv2/core/digi.h"
+#include "mads/madsv2/forest/digi.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/imath.h"
 #include "mads/madsv2/core/inter.h"
 #include "mads/madsv2/core/kernel.h"
-#include "mads/madsv2/core/midi.h"
+#include "mads/madsv2/forest/midi.h"
 #include "mads/madsv2/core/object.h"
 #include "mads/madsv2/core/player.h"
 #include "mads/madsv2/forest/extra.h"
diff --git a/engines/mads/madsv2/forest/rooms/room204.cpp b/engines/mads/madsv2/forest/rooms/room204.cpp
index 11c9f3dc451..70fbaec8dab 100644
--- a/engines/mads/madsv2/forest/rooms/room204.cpp
+++ b/engines/mads/madsv2/forest/rooms/room204.cpp
@@ -19,12 +19,12 @@
  *
  */
 
-#include "mads/madsv2/core/digi.h"
+#include "mads/madsv2/forest/digi.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/global.h"
 #include "mads/madsv2/core/inter.h"
 #include "mads/madsv2/core/kernel.h"
-#include "mads/madsv2/core/midi.h"
+#include "mads/madsv2/forest/midi.h"
 #include "mads/madsv2/core/object.h"
 #include "mads/madsv2/core/player.h"
 #include "mads/madsv2/forest/global.h"
diff --git a/engines/mads/madsv2/forest/rooms/room205.cpp b/engines/mads/madsv2/forest/rooms/room205.cpp
index 384865a30d3..3396f9183d9 100644
--- a/engines/mads/madsv2/forest/rooms/room205.cpp
+++ b/engines/mads/madsv2/forest/rooms/room205.cpp
@@ -19,14 +19,14 @@
  *
  */
 
-#include "mads/madsv2/core/digi.h"
+#include "mads/madsv2/forest/digi.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/global.h"
 #include "mads/madsv2/core/imath.h"
 #include "mads/madsv2/core/inter.h"
 #include "mads/madsv2/core/kernel.h"
 #include "mads/madsv2/core/matte.h"
-#include "mads/madsv2/core/midi.h"
+#include "mads/madsv2/forest/midi.h"
 #include "mads/madsv2/core/object.h"
 #include "mads/madsv2/core/player.h"
 #include "mads/madsv2/forest/global.h"
diff --git a/engines/mads/madsv2/forest/rooms/room210.cpp b/engines/mads/madsv2/forest/rooms/room210.cpp
index abea8109c55..97afb3ccc66 100644
--- a/engines/mads/madsv2/forest/rooms/room210.cpp
+++ b/engines/mads/madsv2/forest/rooms/room210.cpp
@@ -20,11 +20,11 @@
  */
 
 #include "mads/madsv2/core/config.h"
-#include "mads/madsv2/core/digi.h"
+#include "mads/madsv2/forest/digi.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/imath.h"
 #include "mads/madsv2/core/kernel.h"
-#include "mads/madsv2/core/midi.h"
+#include "mads/madsv2/forest/midi.h"
 #include "mads/madsv2/core/player.h"
 #include "mads/madsv2/core/speech.h"
 #include "mads/madsv2/forest/mads/inventory.h"
diff --git a/engines/mads/madsv2/forest/rooms/room211.cpp b/engines/mads/madsv2/forest/rooms/room211.cpp
index a572b5b546d..1477e3d4af2 100644
--- a/engines/mads/madsv2/forest/rooms/room211.cpp
+++ b/engines/mads/madsv2/forest/rooms/room211.cpp
@@ -19,11 +19,11 @@
  *
  */
 
-#include "mads/madsv2/core/digi.h"
+#include "mads/madsv2/forest/digi.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/kernel.h"
 #include "mads/madsv2/core/matte.h"
-#include "mads/madsv2/core/midi.h"
+#include "mads/madsv2/forest/midi.h"
 #include "mads/madsv2/core/player.h"
 #include "mads/madsv2/forest/global.h"
 #include "mads/madsv2/forest/rooms/section1.h"
diff --git a/engines/mads/madsv2/forest/rooms/room220.cpp b/engines/mads/madsv2/forest/rooms/room220.cpp
index 465632497d6..bb1d922ab1e 100644
--- a/engines/mads/madsv2/forest/rooms/room220.cpp
+++ b/engines/mads/madsv2/forest/rooms/room220.cpp
@@ -19,11 +19,11 @@
  *
  */
 
-#include "mads/madsv2/core/digi.h"
+#include "mads/madsv2/forest/digi.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/kernel.h"
 #include "mads/madsv2/core/matte.h"
-#include "mads/madsv2/core/midi.h"
+#include "mads/madsv2/forest/midi.h"
 #include "mads/madsv2/core/player.h"
 #include "mads/madsv2/forest/global.h"
 #include "mads/madsv2/forest/rooms/section1.h"
diff --git a/engines/mads/madsv2/forest/rooms/room301.cpp b/engines/mads/madsv2/forest/rooms/room301.cpp
index 45d959c7884..949784b5af7 100644
--- a/engines/mads/madsv2/forest/rooms/room301.cpp
+++ b/engines/mads/madsv2/forest/rooms/room301.cpp
@@ -20,11 +20,11 @@
  */
 
 #include "mads/madsv2/forest/journal.h"
-#include "mads/madsv2/core/digi.h"
+#include "mads/madsv2/forest/digi.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/inter.h"
 #include "mads/madsv2/core/kernel.h"
-#include "mads/madsv2/core/midi.h"
+#include "mads/madsv2/forest/midi.h"
 #include "mads/madsv2/core/object.h"
 #include "mads/madsv2/core/player.h"
 #include "mads/madsv2/forest/global.h"
diff --git a/engines/mads/madsv2/forest/rooms/room302.cpp b/engines/mads/madsv2/forest/rooms/room302.cpp
index ee3db098fd5..a6a8b061c41 100644
--- a/engines/mads/madsv2/forest/rooms/room302.cpp
+++ b/engines/mads/madsv2/forest/rooms/room302.cpp
@@ -19,12 +19,12 @@
  *
  */
 
-#include "mads/madsv2/core/digi.h"
+#include "mads/madsv2/forest/digi.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/imath.h"
 #include "mads/madsv2/core/inter.h"
 #include "mads/madsv2/core/kernel.h"
-#include "mads/madsv2/core/midi.h"
+#include "mads/madsv2/forest/midi.h"
 #include "mads/madsv2/core/object.h"
 #include "mads/madsv2/core/player.h"
 #include "mads/madsv2/core/sound.h"
diff --git a/engines/mads/madsv2/forest/rooms/room303.cpp b/engines/mads/madsv2/forest/rooms/room303.cpp
index c6f8028e090..5587005d36a 100644
--- a/engines/mads/madsv2/forest/rooms/room303.cpp
+++ b/engines/mads/madsv2/forest/rooms/room303.cpp
@@ -19,7 +19,7 @@
  *
  */
 
-#include "mads/madsv2/core/digi.h"
+#include "mads/madsv2/forest/digi.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/imath.h"
 #include "mads/madsv2/core/inter.h"
diff --git a/engines/mads/madsv2/forest/rooms/room304.cpp b/engines/mads/madsv2/forest/rooms/room304.cpp
index ec8ee34447f..ef4329aaee3 100644
--- a/engines/mads/madsv2/forest/rooms/room304.cpp
+++ b/engines/mads/madsv2/forest/rooms/room304.cpp
@@ -20,12 +20,12 @@
  */
 
 #include "mads/madsv2/core/config.h"
-#include "mads/madsv2/core/digi.h"
+#include "mads/madsv2/forest/digi.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/imath.h"
 #include "mads/madsv2/core/inter.h"
 #include "mads/madsv2/core/kernel.h"
-#include "mads/madsv2/core/midi.h"
+#include "mads/madsv2/forest/midi.h"
 #include "mads/madsv2/core/player.h"
 #include "mads/madsv2/core/sound.h"
 #include "mads/madsv2/core/text.h"
diff --git a/engines/mads/madsv2/forest/rooms/room305.cpp b/engines/mads/madsv2/forest/rooms/room305.cpp
index ff0d670596e..744461f52fa 100644
--- a/engines/mads/madsv2/forest/rooms/room305.cpp
+++ b/engines/mads/madsv2/forest/rooms/room305.cpp
@@ -20,7 +20,7 @@
  */
 
 #include "mads/madsv2/core/config.h"
-#include "mads/madsv2/core/digi.h"
+#include "mads/madsv2/forest/digi.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/imath.h"
 #include "mads/madsv2/core/inter.h"
diff --git a/engines/mads/madsv2/forest/rooms/room306.cpp b/engines/mads/madsv2/forest/rooms/room306.cpp
index 72858656bac..4af54428a68 100644
--- a/engines/mads/madsv2/forest/rooms/room306.cpp
+++ b/engines/mads/madsv2/forest/rooms/room306.cpp
@@ -23,9 +23,9 @@
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/imath.h"
 #include "mads/madsv2/core/inter.h"
-#include "mads/madsv2/core/digi.h"
+#include "mads/madsv2/forest/digi.h"
 #include "mads/madsv2/core/kernel.h"
-#include "mads/madsv2/core/midi.h"
+#include "mads/madsv2/forest/midi.h"
 #include "mads/madsv2/core/player.h"
 #include "mads/madsv2/core/sound.h"
 #include "mads/madsv2/core/sprite.h"
diff --git a/engines/mads/madsv2/forest/rooms/room307.cpp b/engines/mads/madsv2/forest/rooms/room307.cpp
index 122ff122079..9e6999583c6 100644
--- a/engines/mads/madsv2/forest/rooms/room307.cpp
+++ b/engines/mads/madsv2/forest/rooms/room307.cpp
@@ -20,12 +20,12 @@
  */
 
 #include "mads/madsv2/core/config.h"
-#include "mads/madsv2/core/digi.h"
+#include "mads/madsv2/forest/digi.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/imath.h"
 #include "mads/madsv2/core/inter.h"
 #include "mads/madsv2/core/kernel.h"
-#include "mads/madsv2/core/midi.h"
+#include "mads/madsv2/forest/midi.h"
 #include "mads/madsv2/core/object.h"
 #include "mads/madsv2/core/player.h"
 #include "mads/madsv2/core/sound.h"
diff --git a/engines/mads/madsv2/forest/rooms/room308.cpp b/engines/mads/madsv2/forest/rooms/room308.cpp
index 7ef97d10d99..f6e514b7cb9 100644
--- a/engines/mads/madsv2/forest/rooms/room308.cpp
+++ b/engines/mads/madsv2/forest/rooms/room308.cpp
@@ -20,7 +20,7 @@
  */
 
 #include "mads/madsv2/core/config.h"
-#include "mads/madsv2/core/digi.h"
+#include "mads/madsv2/forest/digi.h"
 #include "mads/madsv2/forest/extra.h"
 #include "mads/madsv2/forest/journal.h"
 #include "mads/madsv2/core/game.h"
diff --git a/engines/mads/madsv2/forest/rooms/room321.cpp b/engines/mads/madsv2/forest/rooms/room321.cpp
index fc47eafab3e..b5db451096b 100644
--- a/engines/mads/madsv2/forest/rooms/room321.cpp
+++ b/engines/mads/madsv2/forest/rooms/room321.cpp
@@ -19,7 +19,7 @@
  *
  */
 
-#include "mads/madsv2/core/digi.h"
+#include "mads/madsv2/forest/digi.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/imath.h"
 #include "mads/madsv2/core/inter.h"
diff --git a/engines/mads/madsv2/forest/rooms/room322.cpp b/engines/mads/madsv2/forest/rooms/room322.cpp
index c510d1402e7..f2dacc67a0e 100644
--- a/engines/mads/madsv2/forest/rooms/room322.cpp
+++ b/engines/mads/madsv2/forest/rooms/room322.cpp
@@ -23,8 +23,8 @@
 #include "mads/madsv2/core/imath.h"
 #include "mads/madsv2/core/inter.h"
 #include "mads/madsv2/core/kernel.h"
-#include "mads/madsv2/core/digi.h"
-#include "mads/madsv2/core/midi.h"
+#include "mads/madsv2/forest/digi.h"
+#include "mads/madsv2/forest/midi.h"
 #include "mads/madsv2/core/object.h"
 #include "mads/madsv2/core/player.h"
 #include "mads/madsv2/core/sound.h"
diff --git a/engines/mads/madsv2/forest/rooms/room401.cpp b/engines/mads/madsv2/forest/rooms/room401.cpp
index f2c9c3c9766..b2f8706c53e 100644
--- a/engines/mads/madsv2/forest/rooms/room401.cpp
+++ b/engines/mads/madsv2/forest/rooms/room401.cpp
@@ -24,9 +24,9 @@
 #include "mads/madsv2/core/imath.h"
 #include "mads/madsv2/core/inter.h"
 #include "mads/madsv2/core/kernel.h"
-#include "mads/madsv2/core/digi.h"
+#include "mads/madsv2/forest/digi.h"
 #include "mads/madsv2/core/sprite.h"
-#include "mads/madsv2/core/midi.h"
+#include "mads/madsv2/forest/midi.h"
 #include "mads/madsv2/core/player.h"
 #include "mads/madsv2/core/sound.h"
 #include "mads/madsv2/core/text.h"
diff --git a/engines/mads/madsv2/forest/rooms/room402.cpp b/engines/mads/madsv2/forest/rooms/room402.cpp
index f1387102cf6..c18b0b0453e 100644
--- a/engines/mads/madsv2/forest/rooms/room402.cpp
+++ b/engines/mads/madsv2/forest/rooms/room402.cpp
@@ -19,12 +19,12 @@
  *
  */
 
-#include "mads/madsv2/core/digi.h"
+#include "mads/madsv2/forest/digi.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/imath.h"
 #include "mads/madsv2/core/inter.h"
 #include "mads/madsv2/core/kernel.h"
-#include "mads/madsv2/core/midi.h"
+#include "mads/madsv2/forest/midi.h"
 #include "mads/madsv2/core/player.h"
 #include "mads/madsv2/core/sprite.h"
 #include "mads/madsv2/forest/journal.h"
diff --git a/engines/mads/madsv2/forest/rooms/room403.cpp b/engines/mads/madsv2/forest/rooms/room403.cpp
index d35ee2533fa..e1713a0dd2b 100644
--- a/engines/mads/madsv2/forest/rooms/room403.cpp
+++ b/engines/mads/madsv2/forest/rooms/room403.cpp
@@ -20,12 +20,12 @@
  */
 
 #include "mads/madsv2/core/config.h"
-#include "mads/madsv2/core/digi.h"
+#include "mads/madsv2/forest/digi.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/imath.h"
 #include "mads/madsv2/core/inter.h"
 #include "mads/madsv2/core/kernel.h"
-#include "mads/madsv2/core/midi.h"
+#include "mads/madsv2/forest/midi.h"
 #include "mads/madsv2/core/object.h"
 #include "mads/madsv2/core/player.h"
 #include "mads/madsv2/core/sprite.h"
diff --git a/engines/mads/madsv2/forest/rooms/room404.cpp b/engines/mads/madsv2/forest/rooms/room404.cpp
index b17a9c55636..86bfcca25e2 100644
--- a/engines/mads/madsv2/forest/rooms/room404.cpp
+++ b/engines/mads/madsv2/forest/rooms/room404.cpp
@@ -20,7 +20,7 @@
  */
 
 #include "mads/madsv2/core/config.h"
-#include "mads/madsv2/core/digi.h"
+#include "mads/madsv2/forest/digi.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/imath.h"
 #include "mads/madsv2/core/inter.h"
diff --git a/engines/mads/madsv2/forest/rooms/room405.cpp b/engines/mads/madsv2/forest/rooms/room405.cpp
index 31e712d124f..b17501b8f1f 100644
--- a/engines/mads/madsv2/forest/rooms/room405.cpp
+++ b/engines/mads/madsv2/forest/rooms/room405.cpp
@@ -20,7 +20,7 @@
  */
 
 #include "mads/madsv2/core/config.h"
-#include "mads/madsv2/core/digi.h"
+#include "mads/madsv2/forest/digi.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/imath.h"
 #include "mads/madsv2/core/inter.h"
diff --git a/engines/mads/madsv2/forest/rooms/room420.cpp b/engines/mads/madsv2/forest/rooms/room420.cpp
index b0a53052ea0..b212e995bc4 100644
--- a/engines/mads/madsv2/forest/rooms/room420.cpp
+++ b/engines/mads/madsv2/forest/rooms/room420.cpp
@@ -19,10 +19,10 @@
  *
  */
 
-#include "mads/madsv2/core/digi.h"
+#include "mads/madsv2/forest/digi.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/kernel.h"
-#include "mads/madsv2/core/midi.h"
+#include "mads/madsv2/forest/midi.h"
 #include "mads/madsv2/core/player.h"
 #include "mads/madsv2/forest/global.h"
 #include "mads/madsv2/forest/rooms/section1.h"
diff --git a/engines/mads/madsv2/forest/rooms/room501.cpp b/engines/mads/madsv2/forest/rooms/room501.cpp
index ed46140e7b8..8c930fbe334 100644
--- a/engines/mads/madsv2/forest/rooms/room501.cpp
+++ b/engines/mads/madsv2/forest/rooms/room501.cpp
@@ -23,8 +23,8 @@
 #include "mads/madsv2/core/imath.h"
 #include "mads/madsv2/core/inter.h"
 #include "mads/madsv2/core/kernel.h"
-#include "mads/madsv2/core/digi.h"
-#include "mads/madsv2/core/midi.h"
+#include "mads/madsv2/forest/digi.h"
+#include "mads/madsv2/forest/midi.h"
 #include "mads/madsv2/core/player.h"
 #include "mads/madsv2/core/sound.h"
 #include "mads/madsv2/core/text.h"
diff --git a/engines/mads/madsv2/forest/rooms/room503.cpp b/engines/mads/madsv2/forest/rooms/room503.cpp
index 35bd8fb3393..19672e9ccae 100644
--- a/engines/mads/madsv2/forest/rooms/room503.cpp
+++ b/engines/mads/madsv2/forest/rooms/room503.cpp
@@ -19,13 +19,13 @@
  *
  */
 
-#include "mads/madsv2/core/digi.h"
+#include "mads/madsv2/forest/digi.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/imath.h"
 #include "mads/madsv2/core/inter.h"
 #include "mads/madsv2/core/kernel.h"
 #include "mads/madsv2/core/matte.h"
-#include "mads/madsv2/core/midi.h"
+#include "mads/madsv2/forest/midi.h"
 #include "mads/madsv2/core/mouse.h"
 #include "mads/madsv2/core/player.h"
 #include "mads/madsv2/core/sound.h"
diff --git a/engines/mads/madsv2/forest/rooms/room509.cpp b/engines/mads/madsv2/forest/rooms/room509.cpp
index 10ff60818a0..c9bf05a440d 100644
--- a/engines/mads/madsv2/forest/rooms/room509.cpp
+++ b/engines/mads/madsv2/forest/rooms/room509.cpp
@@ -19,7 +19,7 @@
  *
  */
 
-#include "mads/madsv2/core/digi.h"
+#include "mads/madsv2/forest/digi.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/kernel.h"
 #include "mads/madsv2/core/matte.h"
diff --git a/engines/mads/madsv2/forest/rooms/room510.cpp b/engines/mads/madsv2/forest/rooms/room510.cpp
index 213430b7c43..4626dbd6968 100644
--- a/engines/mads/madsv2/forest/rooms/room510.cpp
+++ b/engines/mads/madsv2/forest/rooms/room510.cpp
@@ -20,12 +20,12 @@
  */
 
 #include "mads/madsv2/core/config.h"
-#include "mads/madsv2/core/digi.h"
+#include "mads/madsv2/forest/digi.h"
 #include "mads/madsv2/core/font.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/kernel.h"
 #include "mads/madsv2/core/matte.h"
-#include "mads/madsv2/core/midi.h"
+#include "mads/madsv2/forest/midi.h"
 #include "mads/madsv2/core/mouse.h"
 #include "mads/madsv2/core/player.h"
 #include "mads/madsv2/core/quote.h"
diff --git a/engines/mads/madsv2/forest/rooms/room520.cpp b/engines/mads/madsv2/forest/rooms/room520.cpp
index 13d6596dcc9..3ff8a8239f0 100644
--- a/engines/mads/madsv2/forest/rooms/room520.cpp
+++ b/engines/mads/madsv2/forest/rooms/room520.cpp
@@ -19,7 +19,7 @@
  *
  */
 
-#include "mads/madsv2/core/digi.h"
+#include "mads/madsv2/forest/digi.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/kernel.h"
 #include "mads/madsv2/core/matte.h"
diff --git a/engines/mads/madsv2/forest/rooms/room904.cpp b/engines/mads/madsv2/forest/rooms/room904.cpp
index bfc535ce269..52fcc246db8 100644
--- a/engines/mads/madsv2/forest/rooms/room904.cpp
+++ b/engines/mads/madsv2/forest/rooms/room904.cpp
@@ -24,7 +24,7 @@
 #include "mads/madsv2/core/global.h"
 #include "mads/madsv2/core/kernel.h"
 #include "mads/madsv2/core/matte.h"
-#include "mads/madsv2/core/midi.h"
+#include "mads/madsv2/forest/midi.h"
 #include "mads/madsv2/core/mouse.h"
 #include "mads/madsv2/core/object.h"
 #include "mads/madsv2/core/player.h"
diff --git a/engines/mads/module.mk b/engines/mads/module.mk
index b5e8aee7e80..cee9da707fd 100644
--- a/engines/mads/module.mk
+++ b/engines/mads/module.mk
@@ -72,7 +72,6 @@ MODULE_OBJS += \
 	madsv2/core/cursor.o \
 	madsv2/core/cycle.o \
 	madsv2/core/dialog.o \
-	madsv2/core/digi.o \
 	madsv2/core/ems.o \
 	madsv2/core/env.o \
 	madsv2/core/error.o \
@@ -96,7 +95,6 @@ MODULE_OBJS += \
 	madsv2/core/matte.o \
 	madsv2/core/mcga.o \
 	madsv2/core/mem.o \
-	madsv2/core/midi.o \
 	madsv2/core/mouse.o \
 	madsv2/core/object.o \
 	madsv2/core/pack.o \
@@ -263,12 +261,13 @@ MODULE_OBJS += \
 	madsv2/dragonsphere/menus.o \
 	madsv2/dragonsphere/sound_dragonsphere.o \
 	madsv2/forest/forest.o \
-	madsv2/forest/asound.o \
+	madsv2/forest/digi.o \
 	madsv2/forest/extra.o \
 	madsv2/forest/global.o \
 	madsv2/forest/journal.o \
 	madsv2/forest/main.o \
 	madsv2/forest/menus.o \
+	madsv2/forest/midi.o \
 	madsv2/forest/mads/mads.o \
 	madsv2/forest/rooms/room101.o \
 	madsv2/forest/rooms/room103.o \


Commit: 1ce640b1a11d5187cb4c780a4ee1e3e49a5f6bd3
    https://github.com/scummvm/scummvm/commit/1ce640b1a11d5187cb4c780a4ee1e3e49a5f6bd3
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-06-21T12:41:50+10:00

Commit Message:
MADS: FOREST: Skeleton MidiPlayer class

Changed paths:
    engines/mads/madsv2/forest/forest.cpp
    engines/mads/madsv2/forest/forest.h
    engines/mads/madsv2/forest/midi.cpp
    engines/mads/madsv2/forest/midi.h


diff --git a/engines/mads/madsv2/forest/forest.cpp b/engines/mads/madsv2/forest/forest.cpp
index 751831779f0..56f0f3eaf3d 100644
--- a/engines/mads/madsv2/forest/forest.cpp
+++ b/engines/mads/madsv2/forest/forest.cpp
@@ -53,6 +53,17 @@ namespace MADS {
 namespace MADSV2 {
 namespace Forest {
 
+ForestEngine *g_engine;
+
+ForestEngine::ForestEngine(OSystem *syst, const MADSGameDescription *gameDesc) :
+		MADSV2Engine(syst, gameDesc) {
+	g_engine = this;
+}
+
+ForestEngine::~ForestEngine() {
+	g_engine = nullptr;
+}
+
 Common::Error ForestEngine::run() {
 	initGraphics(320, 200);
 	_screen = new Graphics::Screen();
diff --git a/engines/mads/madsv2/forest/forest.h b/engines/mads/madsv2/forest/forest.h
index 6765d195a40..d012bd26022 100644
--- a/engines/mads/madsv2/forest/forest.h
+++ b/engines/mads/madsv2/forest/forest.h
@@ -23,6 +23,8 @@
 #define MADS_FOREST_H
 
 #include "mads/madsv2/engine.h"
+#include "mads/madsv2/forest/digi.h"
+#include "mads/madsv2/forest/midi.h"
 
 namespace MADS {
 namespace MADSV2 {
@@ -30,9 +32,11 @@ namespace Forest {
 
 class ForestEngine : public MADSV2Engine {
 public:
-	ForestEngine(OSystem *syst, const MADSGameDescription *gameDesc) :
-		MADSV2Engine(syst, gameDesc) {}
-	~ForestEngine() override {}
+	MidiPlayer _midiPlayer;
+
+public:
+	ForestEngine(OSystem *syst, const MADSGameDescription *gameDesc);
+	~ForestEngine() override;
 
 	Common::Error run() override;
 	void syncRoom(Common::Serializer &s) override;
@@ -48,6 +52,8 @@ public:
 	void global_sound_driver() override;
 };
 
+extern ForestEngine *g_engine;
+
 } // namespace Forest
 } // namespace MADSV2
 } // namespace MADS
diff --git a/engines/mads/madsv2/forest/midi.cpp b/engines/mads/madsv2/forest/midi.cpp
index 6d5533bf289..2691ca8d5c7 100644
--- a/engines/mads/madsv2/forest/midi.cpp
+++ b/engines/mads/madsv2/forest/midi.cpp
@@ -20,25 +20,55 @@
  */
 
 #include "common/textconsole.h"
+#include "audio/midiparser_smf.h"
 #include "mads/madsv2/forest/midi.h"
 #include "mads/madsv2/core/env.h"
-#include "mads/madsv2/engine.h"
+#include "mads/madsv2/forest/forest.h"
 
 namespace MADS {
 namespace MADSV2 {
 namespace Forest {
 
-void midi_play(const char *name) {
-	assert(env_exist(name));
+void MidiPlayer::send(uint32 b) {
+	//_driver->send(b);
+	Audio::MidiPlayer::send(b);
+}
+
+void MidiPlayer::play(const char *name) {
+	// Open up the MIDI file
+	Common::SeekableReadStream *f = env_open(name);
+	if (!f) {
+		warning("MIDI not found - %s", name);
+		return;
+	}
+
+#if 0
+	// Read in the file contents
+	size_t size = f->size();
+	byte *buf = (byte *)malloc(size);
+	f->read(buf, size);
+	delete f;
 
-	// TODO
-	warning("TODO: global_midi_play");
+	// Set up the parser
+	_parser = new MidiParser_SMF();
+	if (!_parser->loadMusic(buf, size))
+		error("midiPlay() couldn't load music resource");
 
-	// TODO
+	_parser->setTrack(0);
+	_parser->setMidiDriver(this);
+	_parser->setTimerRate(_driver->getBaseTempo());
+#else
+	warning("TODO: Support hmi midi playback");
+	delete f;
+#endif
+}
+
+void midi_play(const char *name) {
+	g_engine->_midiPlayer.play(name);
 }
 
 void midi_stop() {
-	// TODO
+	g_engine->_midiPlayer.stop();
 }
 
 } // namespace Forest
diff --git a/engines/mads/madsv2/forest/midi.h b/engines/mads/madsv2/forest/midi.h
index 508c7ec82ac..82a5e5d8b40 100644
--- a/engines/mads/madsv2/forest/midi.h
+++ b/engines/mads/madsv2/forest/midi.h
@@ -22,16 +22,27 @@
 #ifndef MADS_FOREST_MIDI_H
 #define MADS_FOREST_MIDI_H
 
-#include "common/scummsys.h"
+#include "audio/midiplayer.h"
 
 namespace MADS {
 namespace MADSV2 {
 namespace Forest {
 
+class MidiPlayer : public Audio::MidiPlayer {
+private:
+	// MidiDriver_BASE interface implementation
+	void send(uint32 b) override;
+
+public:
+	MidiPlayer() : Audio::MidiPlayer() {}
+	~MidiPlayer() override {}
+
+	void play(const char *name);
+};
+
 extern void midi_play(const char *name);
 extern void midi_stop();
-inline void midi_loop() {
-}
+inline void midi_loop() {}
 
 } // namespace Forest
 } // namespace MADSV2



From noreply at scummvm.org  Sun Jun 21 08:34:00 2026
From: noreply at scummvm.org (dreammaster)
Date: Sun, 21 Jun 2026 08:34:00 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 1a3467ea717baca757c1888b8b8fa30f3d76bc96
Message-ID: <20260621083400.4A6037005CD@ron.scummvm.net>

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:
cd03368eae MADS: FOREST: Implement digi playback
1a3467ea71 MADS: FOREST: Janitorial cleanup of room headers


Commit: cd03368eaea886c3772a40df4a4a17bef3e152ce
    https://github.com/scummvm/scummvm/commit/cd03368eaea886c3772a40df4a4a17bef3e152ce
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-06-21T17:21:49+10:00

Commit Message:
MADS: FOREST: Implement digi playback

Changed paths:
    engines/mads/madsv2/forest/digi.cpp
    engines/mads/madsv2/forest/digi.h
    engines/mads/madsv2/forest/forest.h


diff --git a/engines/mads/madsv2/forest/digi.cpp b/engines/mads/madsv2/forest/digi.cpp
index 3ab8e397273..3c4cef59988 100644
--- a/engines/mads/madsv2/forest/digi.cpp
+++ b/engines/mads/madsv2/forest/digi.cpp
@@ -19,9 +19,10 @@
  *
  */
 
+#include "audio/decoders/adpcm.h"
 #include "mads/madsv2/forest/digi.h"
-#include "mads/madsv2/core/config.h"
-#include "mads/madsv2/engine.h"
+#include "mads/madsv2/forest/forest.h"
+#include "mads/madsv2/core/env.h"
 
 namespace MADS {
 namespace MADSV2 {
@@ -31,8 +32,35 @@ int digi_val2;
 int digi_timing_index;
 bool digi_flag1, digi_flag2;
 
+void DigiPlayer::play(const char *name, int slot) {
+	assert(slot >= 0 && slot < 8);
+	_mixer->stopHandle(_slots[slot]);
+
+	Common::SeekableReadStream *src;
+	src = env_open(Common::String::format("*%s.rac", name).c_str());
+	if (!src)
+		src = env_open(Common::String::format("*%s.raw", name).c_str());
+
+	if (!src) {
+		warning("Could not open digi sound - %s", name);
+		return;
+	}
+
+	// Skip past the HMI header
+	src->seek(0x20);
+
+	Audio::AudioStream *audioStream = Audio::makeADPCMStream(src, DisposeAfterUse::YES,
+		src->size() - 0x20, Audio::kADPCMDVI, 11025, 1);
+	_mixer->playStream(Audio::Mixer::kSFXSoundType, &_slots[slot], audioStream);
+}
+
+void DigiPlayer::stop(int slot) {
+	assert(slot >= 0 && slot < 8);
+	_mixer->stopHandle(_slots[slot]);
+}
+
+
 void digi_install() {
-	config_file.forest1 = 0;
 	digi_val2 = 0;
 	digi_timing_index = 0;
 }
@@ -41,29 +69,28 @@ void digi_uninstall() {
 }
 
 void digi_play_build_ii(char thing, int num, int slot) {
-	Common::String name;
-	name += (thing == '_') ? 's' : 'd';
-	name += "0ii";
-	name += Common::String::format("%.3d", num);
+	Common::String name = Common::String::format("%c0ii%c%03d",
+		(thing == '_') ? 's' : 'd', thing, num);
 	digi_play(name.c_str(), slot);
 }
 
 void digi_play_build(int room, char thing, int num, int slot) {
-	Common::String name;
-	name += (thing == '_') ? 's' : 'd';
-	name += Common::String::format("%d", room);
-	name += Common::String::format("%.3d", num);
+	Common::String name = Common::String::format("%c%d%c%03d",
+		(thing == '_') ? 's' : 'd', room, thing, num);
 	digi_play(name.c_str(), slot);
 }
 
 void digi_play(const char *name, int slot) {
+	g_engine->_digiPlayer.play(name, slot);
 }
+
 void digi_stop(int which_one) {
+	g_engine->_digiPlayer.stop(which_one);
 }
-void digi_read_another_chunk() {
-}
+
 void digi_initial_volume(int vol) {
 }
+
 void digi_set_volume(int vol, int slot) {
 }
 
diff --git a/engines/mads/madsv2/forest/digi.h b/engines/mads/madsv2/forest/digi.h
index 58b64fc11a8..accd62d3520 100644
--- a/engines/mads/madsv2/forest/digi.h
+++ b/engines/mads/madsv2/forest/digi.h
@@ -22,13 +22,25 @@
 #ifndef MADS_FOREST_DIGI_H
 #define MADS_FOREST_DIGI_H
 
-#include "common/scummsys.h"
+#include "audio/audiostream.h"
+#include "audio/mixer.h"
 
 namespace MADS {
 namespace MADSV2 {
 namespace Forest {
 
-//extern int config_file.forest1;
+class DigiPlayer {
+private:
+	Audio::Mixer *_mixer;
+	Audio::SoundHandle _slots[8];
+public:
+	DigiPlayer(Audio::Mixer *mixer) : _mixer(mixer) {
+	}
+
+	void play(const char *name, int slot);
+	void stop(int slot);
+};
+
 extern int digi_val2;
 extern int digi_timing_index;
 extern bool digi_flag1, digi_flag2;
@@ -39,9 +51,9 @@ extern void digi_play_build(int room, char thing, int num, int slot);
 extern void digi_play_build_ii(char thing, int num, int slot);
 extern void digi_stop(int which_one);
 extern void digi_uninstall();
-extern void digi_read_another_chunk();
 extern void digi_initial_volume(int vol);
 extern void digi_set_volume(int vol, int slot);
+inline void digi_read_another_chunk() {}
 
 } // namespace Forest
 } // namespace MADSV2
diff --git a/engines/mads/madsv2/forest/forest.h b/engines/mads/madsv2/forest/forest.h
index d012bd26022..2d32537f87d 100644
--- a/engines/mads/madsv2/forest/forest.h
+++ b/engines/mads/madsv2/forest/forest.h
@@ -32,6 +32,7 @@ namespace Forest {
 
 class ForestEngine : public MADSV2Engine {
 public:
+	DigiPlayer _digiPlayer = DigiPlayer(_mixer);
 	MidiPlayer _midiPlayer;
 
 public:


Commit: 1a3467ea717baca757c1888b8b8fa30f3d76bc96
    https://github.com/scummvm/scummvm/commit/1a3467ea717baca757c1888b8b8fa30f3d76bc96
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-06-21T18:33:23+10:00

Commit Message:
MADS: FOREST: Janitorial cleanup of room headers

Changed paths:
  R engines/mads/madsv2/forest/rooms/room101.h
  R engines/mads/madsv2/forest/rooms/room103.h
  R engines/mads/madsv2/forest/rooms/room104.h
  R engines/mads/madsv2/forest/rooms/room106.h
  R engines/mads/madsv2/forest/rooms/room107.h
  R engines/mads/madsv2/forest/rooms/room199.h
  R engines/mads/madsv2/forest/rooms/room201.h
  R engines/mads/madsv2/forest/rooms/room203.h
  R engines/mads/madsv2/forest/rooms/room204.h
  R engines/mads/madsv2/forest/rooms/room205.h
  R engines/mads/madsv2/forest/rooms/room210.h
  R engines/mads/madsv2/forest/rooms/room211.h
  R engines/mads/madsv2/forest/rooms/room220.h
  R engines/mads/madsv2/forest/rooms/room221.h
  R engines/mads/madsv2/forest/rooms/room301.h
  R engines/mads/madsv2/forest/rooms/room302.h
  R engines/mads/madsv2/forest/rooms/room303.h
  R engines/mads/madsv2/forest/rooms/room304.h
  R engines/mads/madsv2/forest/rooms/room305.h
  R engines/mads/madsv2/forest/rooms/room306.h
  R engines/mads/madsv2/forest/rooms/room307.h
  R engines/mads/madsv2/forest/rooms/room308.h
  R engines/mads/madsv2/forest/rooms/room321.h
  R engines/mads/madsv2/forest/rooms/room322.h
  R engines/mads/madsv2/forest/rooms/room401.h
  R engines/mads/madsv2/forest/rooms/room402.h
  R engines/mads/madsv2/forest/rooms/room403.h
  R engines/mads/madsv2/forest/rooms/room404.h
  R engines/mads/madsv2/forest/rooms/room405.h
  R engines/mads/madsv2/forest/rooms/room420.h
  R engines/mads/madsv2/forest/rooms/room501.h
  R engines/mads/madsv2/forest/rooms/room503.h
  R engines/mads/madsv2/forest/rooms/room509.h
  R engines/mads/madsv2/forest/rooms/room510.h
  R engines/mads/madsv2/forest/rooms/room520.h
  R engines/mads/madsv2/forest/rooms/room901.h
  R engines/mads/madsv2/forest/rooms/room903.h
  R engines/mads/madsv2/forest/rooms/room904.h
    engines/mads/madsv2/forest/rooms/room101.cpp
    engines/mads/madsv2/forest/rooms/room103.cpp
    engines/mads/madsv2/forest/rooms/room104.cpp
    engines/mads/madsv2/forest/rooms/room106.cpp
    engines/mads/madsv2/forest/rooms/room107.cpp
    engines/mads/madsv2/forest/rooms/room199.cpp
    engines/mads/madsv2/forest/rooms/room201.cpp
    engines/mads/madsv2/forest/rooms/room203.cpp
    engines/mads/madsv2/forest/rooms/room204.cpp
    engines/mads/madsv2/forest/rooms/room205.cpp
    engines/mads/madsv2/forest/rooms/room210.cpp
    engines/mads/madsv2/forest/rooms/room211.cpp
    engines/mads/madsv2/forest/rooms/room220.cpp
    engines/mads/madsv2/forest/rooms/room221.cpp
    engines/mads/madsv2/forest/rooms/room301.cpp
    engines/mads/madsv2/forest/rooms/room302.cpp
    engines/mads/madsv2/forest/rooms/room303.cpp
    engines/mads/madsv2/forest/rooms/room304.cpp
    engines/mads/madsv2/forest/rooms/room305.cpp
    engines/mads/madsv2/forest/rooms/room306.cpp
    engines/mads/madsv2/forest/rooms/room307.cpp
    engines/mads/madsv2/forest/rooms/room308.cpp
    engines/mads/madsv2/forest/rooms/room321.cpp
    engines/mads/madsv2/forest/rooms/room322.cpp
    engines/mads/madsv2/forest/rooms/room401.cpp
    engines/mads/madsv2/forest/rooms/room402.cpp
    engines/mads/madsv2/forest/rooms/room403.cpp
    engines/mads/madsv2/forest/rooms/room404.cpp
    engines/mads/madsv2/forest/rooms/room405.cpp
    engines/mads/madsv2/forest/rooms/room420.cpp
    engines/mads/madsv2/forest/rooms/room501.cpp
    engines/mads/madsv2/forest/rooms/room503.cpp
    engines/mads/madsv2/forest/rooms/room509.cpp
    engines/mads/madsv2/forest/rooms/room510.cpp
    engines/mads/madsv2/forest/rooms/room520.cpp
    engines/mads/madsv2/forest/rooms/room901.cpp
    engines/mads/madsv2/forest/rooms/room903.cpp
    engines/mads/madsv2/forest/rooms/room904.cpp


diff --git a/engines/mads/madsv2/forest/rooms/room101.cpp b/engines/mads/madsv2/forest/rooms/room101.cpp
index 650ac79c08f..9deaa11e846 100644
--- a/engines/mads/madsv2/forest/rooms/room101.cpp
+++ b/engines/mads/madsv2/forest/rooms/room101.cpp
@@ -19,25 +19,24 @@
  *
  */
 
+#include "mads/madsv2/forest/mads/inventory.h"
+#include "mads/madsv2/forest/mads/sounds.h"
+#include "mads/madsv2/forest/mads/words.h"
+#include "mads/madsv2/forest/rooms/section1.h"
 #include "mads/madsv2/forest/digi.h"
+#include "mads/madsv2/forest/global.h"
+#include "mads/madsv2/forest/journal.h"
+#include "mads/madsv2/forest/midi.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/imath.h"
 #include "mads/madsv2/core/inter.h"
 #include "mads/madsv2/core/kernel.h"
 #include "mads/madsv2/core/matte.h"
-#include "mads/madsv2/forest/midi.h"
 #include "mads/madsv2/core/mouse.h"
 #include "mads/madsv2/core/object.h"
 #include "mads/madsv2/core/player.h"
 #include "mads/madsv2/core/sprite.h"
 #include "mads/madsv2/core/text.h"
-#include "mads/madsv2/forest/mads/inventory.h"
-#include "mads/madsv2/forest/mads/sounds.h"
-#include "mads/madsv2/forest/mads/words.h"
-#include "mads/madsv2/forest/global.h"
-#include "mads/madsv2/forest/journal.h"
-#include "mads/madsv2/forest/rooms/section1.h"
-#include "mads/madsv2/forest/rooms/room101.h"
 
 namespace MADS {
 namespace MADSV2 {
diff --git a/engines/mads/madsv2/forest/rooms/room101.h b/engines/mads/madsv2/forest/rooms/room101.h
deleted file mode 100644
index ac8e31fd121..00000000000
--- a/engines/mads/madsv2/forest/rooms/room101.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/* 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 MADS_FOREST_ROOMS_101_H
-#define MADS_FOREST_ROOMS_101_H
-
-#include "common/serializer.h"
-#include "mads/madsv2/core/general.h"
-
-namespace MADS {
-namespace MADSV2 {
-namespace Forest {
-namespace Rooms {
-
-extern void room_101_preload();
-extern void room_101_synchronize(Common::Serializer &s);
-
-} // namespace Rooms
-} // namespace Forest
-} // namespace MADSV2
-} // namespace MADS
-
-#endif
diff --git a/engines/mads/madsv2/forest/rooms/room103.cpp b/engines/mads/madsv2/forest/rooms/room103.cpp
index a3f08454721..b1deabd7844 100644
--- a/engines/mads/madsv2/forest/rooms/room103.cpp
+++ b/engines/mads/madsv2/forest/rooms/room103.cpp
@@ -19,22 +19,21 @@
  *
  */
 
+#include "mads/madsv2/forest/rooms/section1.h"
+#include "mads/madsv2/forest/mads/inventory.h"
+#include "mads/madsv2/forest/mads/sounds.h"
+#include "mads/madsv2/forest/mads/words.h"
 #include "mads/madsv2/forest/digi.h"
+#include "mads/madsv2/forest/global.h"
+#include "mads/madsv2/forest/midi.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/imath.h"
 #include "mads/madsv2/core/inter.h"
 #include "mads/madsv2/core/kernel.h"
 #include "mads/madsv2/core/matte.h"
-#include "mads/madsv2/forest/midi.h"
 #include "mads/madsv2/core/sound.h"
 #include "mads/madsv2/core/sprite.h"
 #include "mads/madsv2/core/text.h"
-#include "mads/madsv2/forest/mads/inventory.h"
-#include "mads/madsv2/forest/mads/sounds.h"
-#include "mads/madsv2/forest/mads/words.h"
-#include "mads/madsv2/forest/global.h"
-#include "mads/madsv2/forest/rooms/section1.h"
-#include "mads/madsv2/forest/rooms/room103.h"
 
 namespace MADS {
 namespace MADSV2 {
diff --git a/engines/mads/madsv2/forest/rooms/room103.h b/engines/mads/madsv2/forest/rooms/room103.h
deleted file mode 100644
index 78fb07904e4..00000000000
--- a/engines/mads/madsv2/forest/rooms/room103.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/* 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 MADS_FOREST_ROOMS_103_H
-#define MADS_FOREST_ROOMS_103_H
-
-#include "common/serializer.h"
-#include "mads/madsv2/core/general.h"
-
-namespace MADS {
-namespace MADSV2 {
-namespace Forest {
-namespace Rooms {
-
-extern void room_103_preload();
-extern void room_103_synchronize(Common::Serializer &s);
-
-} // namespace Rooms
-} // namespace Forest
-} // namespace MADSV2
-} // namespace MADS
-
-#endif
diff --git a/engines/mads/madsv2/forest/rooms/room104.cpp b/engines/mads/madsv2/forest/rooms/room104.cpp
index 02f3b94bce2..f59c25b975f 100644
--- a/engines/mads/madsv2/forest/rooms/room104.cpp
+++ b/engines/mads/madsv2/forest/rooms/room104.cpp
@@ -19,24 +19,23 @@
  *
  */
 
+#include "mads/madsv2/forest/mads/inventory.h"
+#include "mads/madsv2/forest/mads/sounds.h"
+#include "mads/madsv2/forest/mads/words.h"
+#include "mads/madsv2/forest/rooms/section1.h"
 #include "mads/madsv2/forest/digi.h"
+#include "mads/madsv2/forest/global.h"
+#include "mads/madsv2/forest/journal.h"
+#include "mads/madsv2/forest/midi.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/imath.h"
 #include "mads/madsv2/core/inter.h"
 #include "mads/madsv2/core/kernel.h"
 #include "mads/madsv2/core/matte.h"
-#include "mads/madsv2/forest/midi.h"
 #include "mads/madsv2/core/mouse.h"
 #include "mads/madsv2/core/player.h"
 #include "mads/madsv2/core/sound.h"
 #include "mads/madsv2/core/text.h"
-#include "mads/madsv2/forest/mads/inventory.h"
-#include "mads/madsv2/forest/mads/sounds.h"
-#include "mads/madsv2/forest/mads/words.h"
-#include "mads/madsv2/forest/global.h"
-#include "mads/madsv2/forest/journal.h"
-#include "mads/madsv2/forest/rooms/section1.h"
-#include "mads/madsv2/forest/rooms/room104.h"
 
 namespace MADS {
 namespace MADSV2 {
diff --git a/engines/mads/madsv2/forest/rooms/room104.h b/engines/mads/madsv2/forest/rooms/room104.h
deleted file mode 100644
index 803eb127a36..00000000000
--- a/engines/mads/madsv2/forest/rooms/room104.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/* 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 MADS_FOREST_ROOMS_104_H
-#define MADS_FOREST_ROOMS_104_H
-
-#include "common/serializer.h"
-#include "mads/madsv2/core/general.h"
-
-namespace MADS {
-namespace MADSV2 {
-namespace Forest {
-namespace Rooms {
-
-extern void room_104_preload();
-extern void room_104_synchronize(Common::Serializer &s);
-
-} // namespace Rooms
-} // namespace Forest
-} // namespace MADSV2
-} // namespace MADS
-
-#endif
diff --git a/engines/mads/madsv2/forest/rooms/room106.cpp b/engines/mads/madsv2/forest/rooms/room106.cpp
index d600c93d2eb..05ae6c117f8 100644
--- a/engines/mads/madsv2/forest/rooms/room106.cpp
+++ b/engines/mads/madsv2/forest/rooms/room106.cpp
@@ -19,25 +19,24 @@
  *
  */
 
-#include "mads/madsv2/core/config.h"
+#include "mads/madsv2/forest/mads/inventory.h"
+#include "mads/madsv2/forest/mads/sounds.h"
+#include "mads/madsv2/forest/mads/words.h"
+#include "mads/madsv2/forest/rooms/section1.h"
 #include "mads/madsv2/forest/digi.h"
+#include "mads/madsv2/forest/global.h"
+#include "mads/madsv2/forest/journal.h"
+#include "mads/madsv2/forest/midi.h"
+#include "mads/madsv2/core/config.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/imath.h"
 #include "mads/madsv2/core/inter.h"
 #include "mads/madsv2/core/kernel.h"
 #include "mads/madsv2/core/matte.h"
-#include "mads/madsv2/forest/midi.h"
 #include "mads/madsv2/core/object.h"
 #include "mads/madsv2/core/player.h"
 #include "mads/madsv2/core/sound.h"
 #include "mads/madsv2/core/text.h"
-#include "mads/madsv2/forest/mads/inventory.h"
-#include "mads/madsv2/forest/mads/sounds.h"
-#include "mads/madsv2/forest/mads/words.h"
-#include "mads/madsv2/forest/global.h"
-#include "mads/madsv2/forest/journal.h"
-#include "mads/madsv2/forest/rooms/section1.h"
-#include "mads/madsv2/forest/rooms/room106.h"
 
 namespace MADS {
 namespace MADSV2 {
diff --git a/engines/mads/madsv2/forest/rooms/room106.h b/engines/mads/madsv2/forest/rooms/room106.h
deleted file mode 100644
index dffcf0c2682..00000000000
--- a/engines/mads/madsv2/forest/rooms/room106.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/* 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 MADS_FOREST_ROOMS_106_H
-#define MADS_FOREST_ROOMS_106_H
-
-#include "common/serializer.h"
-#include "mads/madsv2/core/general.h"
-
-namespace MADS {
-namespace MADSV2 {
-namespace Forest {
-namespace Rooms {
-
-extern void room_106_preload();
-extern void room_106_synchronize(Common::Serializer &s);
-
-} // namespace Rooms
-} // namespace Forest
-} // namespace MADSV2
-} // namespace MADS
-
-#endif
diff --git a/engines/mads/madsv2/forest/rooms/room107.cpp b/engines/mads/madsv2/forest/rooms/room107.cpp
index 7176b0e1239..16b33e474a1 100644
--- a/engines/mads/madsv2/forest/rooms/room107.cpp
+++ b/engines/mads/madsv2/forest/rooms/room107.cpp
@@ -19,15 +19,14 @@
  *
  */
 
+#include "mads/madsv2/forest/rooms/section1.h"
 #include "mads/madsv2/forest/digi.h"
+#include "mads/madsv2/forest/global.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/kernel.h"
 #include "mads/madsv2/core/matte.h"
 #include "mads/madsv2/core/mouse.h"
 #include "mads/madsv2/core/player.h"
-#include "mads/madsv2/forest/global.h"
-#include "mads/madsv2/forest/rooms/section1.h"
-#include "mads/madsv2/forest/rooms/room107.h"
 
 namespace MADS {
 namespace MADSV2 {
diff --git a/engines/mads/madsv2/forest/rooms/room107.h b/engines/mads/madsv2/forest/rooms/room107.h
deleted file mode 100644
index 3a733abbcd3..00000000000
--- a/engines/mads/madsv2/forest/rooms/room107.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/* 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 MADS_FOREST_ROOMS_107_H
-#define MADS_FOREST_ROOMS_107_H
-
-#include "common/serializer.h"
-#include "mads/madsv2/core/general.h"
-
-namespace MADS {
-namespace MADSV2 {
-namespace Forest {
-namespace Rooms {
-
-extern void room_107_preload();
-extern void room_107_synchronize(Common::Serializer &s);
-
-} // namespace Rooms
-} // namespace Forest
-} // namespace MADSV2
-} // namespace MADS
-
-#endif
diff --git a/engines/mads/madsv2/forest/rooms/room199.cpp b/engines/mads/madsv2/forest/rooms/room199.cpp
index bb9bb8458c3..9398644b830 100644
--- a/engines/mads/madsv2/forest/rooms/room199.cpp
+++ b/engines/mads/madsv2/forest/rooms/room199.cpp
@@ -19,16 +19,15 @@
  *
  */
 
+#include "mads/madsv2/forest/rooms/section1.h"
 #include "mads/madsv2/forest/digi.h"
+#include "mads/madsv2/forest/global.h"
+#include "mads/madsv2/forest/journal.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/global.h"
 #include "mads/madsv2/core/kernel.h"
 #include "mads/madsv2/core/matte.h"
 #include "mads/madsv2/core/player.h"
-#include "mads/madsv2/forest/global.h"
-#include "mads/madsv2/forest/journal.h"
-#include "mads/madsv2/forest/rooms/section1.h"
-#include "mads/madsv2/forest/rooms/room199.h"
 
 namespace MADS {
 namespace MADSV2 {
diff --git a/engines/mads/madsv2/forest/rooms/room199.h b/engines/mads/madsv2/forest/rooms/room199.h
deleted file mode 100644
index a777a883676..00000000000
--- a/engines/mads/madsv2/forest/rooms/room199.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/* 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 MADS_FOREST_ROOMS_199_H
-#define MADS_FOREST_ROOMS_199_H
-
-#include "common/serializer.h"
-#include "mads/madsv2/core/general.h"
-
-namespace MADS {
-namespace MADSV2 {
-namespace Forest {
-namespace Rooms {
-
-extern void room_199_preload();
-extern void room_199_synchronize(Common::Serializer &s);
-
-} // namespace Rooms
-} // namespace Forest
-} // namespace MADSV2
-} // namespace MADS
-
-#endif
diff --git a/engines/mads/madsv2/forest/rooms/room201.cpp b/engines/mads/madsv2/forest/rooms/room201.cpp
index a90c96828f9..3d50bbcb4aa 100644
--- a/engines/mads/madsv2/forest/rooms/room201.cpp
+++ b/engines/mads/madsv2/forest/rooms/room201.cpp
@@ -19,16 +19,15 @@
  *
  */
 
+#include "mads/madsv2/forest/rooms/section2.h"
 #include "mads/madsv2/forest/digi.h"
+#include "mads/madsv2/forest/global.h"
+#include "mads/madsv2/forest/journal.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/inter.h"
 #include "mads/madsv2/core/kernel.h"
 #include "mads/madsv2/core/object.h"
 #include "mads/madsv2/core/player.h"
-#include "mads/madsv2/forest/global.h"
-#include "mads/madsv2/forest/journal.h"
-#include "mads/madsv2/forest/rooms/section2.h"
-#include "mads/madsv2/forest/rooms/room201.h"
 
 namespace MADS {
 namespace MADSV2 {
diff --git a/engines/mads/madsv2/forest/rooms/room201.h b/engines/mads/madsv2/forest/rooms/room201.h
deleted file mode 100644
index 3d452fa6f65..00000000000
--- a/engines/mads/madsv2/forest/rooms/room201.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/* 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 MADS_FOREST_ROOMS_201_H
-#define MADS_FOREST_ROOMS_201_H
-
-#include "common/serializer.h"
-#include "mads/madsv2/core/general.h"
-
-namespace MADS {
-namespace MADSV2 {
-namespace Forest {
-namespace Rooms {
-
-extern void room_201_preload();
-extern void room_201_synchronize(Common::Serializer &s);
-
-} // namespace Rooms
-} // namespace Forest
-} // namespace MADSV2
-} // namespace MADS
-
-#endif
diff --git a/engines/mads/madsv2/forest/rooms/room203.cpp b/engines/mads/madsv2/forest/rooms/room203.cpp
index 8324ca890c2..0aacaebcf3f 100644
--- a/engines/mads/madsv2/forest/rooms/room203.cpp
+++ b/engines/mads/madsv2/forest/rooms/room203.cpp
@@ -19,19 +19,18 @@
  *
  */
 
+#include "mads/madsv2/forest/rooms/section2.h"
 #include "mads/madsv2/forest/digi.h"
+#include "mads/madsv2/forest/extra.h"
+#include "mads/madsv2/forest/global.h"
+#include "mads/madsv2/forest/journal.h"
+#include "mads/madsv2/forest/midi.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/imath.h"
 #include "mads/madsv2/core/inter.h"
 #include "mads/madsv2/core/kernel.h"
-#include "mads/madsv2/forest/midi.h"
 #include "mads/madsv2/core/object.h"
 #include "mads/madsv2/core/player.h"
-#include "mads/madsv2/forest/extra.h"
-#include "mads/madsv2/forest/global.h"
-#include "mads/madsv2/forest/journal.h"
-#include "mads/madsv2/forest/rooms/section2.h"
-#include "mads/madsv2/forest/rooms/room203.h"
 
 namespace MADS {
 namespace MADSV2 {
diff --git a/engines/mads/madsv2/forest/rooms/room203.h b/engines/mads/madsv2/forest/rooms/room203.h
deleted file mode 100644
index f41afaf244b..00000000000
--- a/engines/mads/madsv2/forest/rooms/room203.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/* 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 MADS_FOREST_ROOMS_203_H
-#define MADS_FOREST_ROOMS_203_H
-
-#include "common/serializer.h"
-#include "mads/madsv2/core/general.h"
-
-namespace MADS {
-namespace MADSV2 {
-namespace Forest {
-namespace Rooms {
-
-extern void room_203_preload();
-extern void room_203_synchronize(Common::Serializer &s);
-
-} // namespace Rooms
-} // namespace Forest
-} // namespace MADSV2
-} // namespace MADS
-
-#endif
diff --git a/engines/mads/madsv2/forest/rooms/room204.cpp b/engines/mads/madsv2/forest/rooms/room204.cpp
index 70fbaec8dab..e7f6afd4d99 100644
--- a/engines/mads/madsv2/forest/rooms/room204.cpp
+++ b/engines/mads/madsv2/forest/rooms/room204.cpp
@@ -19,18 +19,17 @@
  *
  */
 
+#include "mads/madsv2/forest/rooms/section2.h"
 #include "mads/madsv2/forest/digi.h"
+#include "mads/madsv2/forest/global.h"
+#include "mads/madsv2/forest/journal.h"
+#include "mads/madsv2/forest/midi.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/global.h"
 #include "mads/madsv2/core/inter.h"
 #include "mads/madsv2/core/kernel.h"
-#include "mads/madsv2/forest/midi.h"
 #include "mads/madsv2/core/object.h"
 #include "mads/madsv2/core/player.h"
-#include "mads/madsv2/forest/global.h"
-#include "mads/madsv2/forest/journal.h"
-#include "mads/madsv2/forest/rooms/section2.h"
-#include "mads/madsv2/forest/rooms/room204.h"
 
 namespace MADS {
 namespace MADSV2 {
diff --git a/engines/mads/madsv2/forest/rooms/room204.h b/engines/mads/madsv2/forest/rooms/room204.h
deleted file mode 100644
index 5b60edffc52..00000000000
--- a/engines/mads/madsv2/forest/rooms/room204.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/* 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 MADS_FOREST_ROOMS_204_H
-#define MADS_FOREST_ROOMS_204_H
-
-#include "common/serializer.h"
-#include "mads/madsv2/core/general.h"
-
-namespace MADS {
-namespace MADSV2 {
-namespace Forest {
-namespace Rooms {
-
-extern void room_204_preload();
-extern void room_204_synchronize(Common::Serializer &s);
-
-} // namespace Rooms
-} // namespace Forest
-} // namespace MADSV2
-} // namespace MADS
-
-#endif
diff --git a/engines/mads/madsv2/forest/rooms/room205.cpp b/engines/mads/madsv2/forest/rooms/room205.cpp
index 3396f9183d9..b2e0108edfd 100644
--- a/engines/mads/madsv2/forest/rooms/room205.cpp
+++ b/engines/mads/madsv2/forest/rooms/room205.cpp
@@ -19,20 +19,19 @@
  *
  */
 
+#include "mads/madsv2/forest/rooms/section2.h"
 #include "mads/madsv2/forest/digi.h"
+#include "mads/madsv2/forest/global.h"
+#include "mads/madsv2/forest/journal.h"
+#include "mads/madsv2/forest/midi.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/global.h"
 #include "mads/madsv2/core/imath.h"
 #include "mads/madsv2/core/inter.h"
 #include "mads/madsv2/core/kernel.h"
 #include "mads/madsv2/core/matte.h"
-#include "mads/madsv2/forest/midi.h"
 #include "mads/madsv2/core/object.h"
 #include "mads/madsv2/core/player.h"
-#include "mads/madsv2/forest/global.h"
-#include "mads/madsv2/forest/journal.h"
-#include "mads/madsv2/forest/rooms/section2.h"
-#include "mads/madsv2/forest/rooms/room205.h"
 
 namespace MADS {
 namespace MADSV2 {
diff --git a/engines/mads/madsv2/forest/rooms/room205.h b/engines/mads/madsv2/forest/rooms/room205.h
deleted file mode 100644
index 5c961acdbff..00000000000
--- a/engines/mads/madsv2/forest/rooms/room205.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/* 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 MADS_FOREST_ROOMS_205_H
-#define MADS_FOREST_ROOMS_205_H
-
-#include "common/serializer.h"
-#include "mads/madsv2/core/general.h"
-
-namespace MADS {
-namespace MADSV2 {
-namespace Forest {
-namespace Rooms {
-
-extern void room_205_preload();
-extern void room_205_synchronize(Common::Serializer &s);
-
-} // namespace Rooms
-} // namespace Forest
-} // namespace MADSV2
-} // namespace MADS
-
-#endif
diff --git a/engines/mads/madsv2/forest/rooms/room210.cpp b/engines/mads/madsv2/forest/rooms/room210.cpp
index 97afb3ccc66..d396fe9f100 100644
--- a/engines/mads/madsv2/forest/rooms/room210.cpp
+++ b/engines/mads/madsv2/forest/rooms/room210.cpp
@@ -19,21 +19,20 @@
  *
  */
 
-#include "mads/madsv2/core/config.h"
+#include "mads/madsv2/forest/rooms/section1.h"
+#include "mads/madsv2/forest/mads/inventory.h"
+#include "mads/madsv2/forest/mads/sounds.h"
+#include "mads/madsv2/forest/mads/words.h"
 #include "mads/madsv2/forest/digi.h"
+#include "mads/madsv2/forest/global.h"
+#include "mads/madsv2/forest/journal.h"
+#include "mads/madsv2/forest/midi.h"
+#include "mads/madsv2/core/config.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/imath.h"
 #include "mads/madsv2/core/kernel.h"
-#include "mads/madsv2/forest/midi.h"
 #include "mads/madsv2/core/player.h"
 #include "mads/madsv2/core/speech.h"
-#include "mads/madsv2/forest/mads/inventory.h"
-#include "mads/madsv2/forest/mads/sounds.h"
-#include "mads/madsv2/forest/mads/words.h"
-#include "mads/madsv2/forest/global.h"
-#include "mads/madsv2/forest/journal.h"
-#include "mads/madsv2/forest/rooms/section1.h"
-#include "mads/madsv2/forest/rooms/room210.h"
 
 namespace MADS {
 namespace MADSV2 {
diff --git a/engines/mads/madsv2/forest/rooms/room210.h b/engines/mads/madsv2/forest/rooms/room210.h
deleted file mode 100644
index e33943b7497..00000000000
--- a/engines/mads/madsv2/forest/rooms/room210.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/* 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 MADS_FOREST_ROOMS_210_H
-#define MADS_FOREST_ROOMS_210_H
-
-#include "common/serializer.h"
-#include "mads/madsv2/core/general.h"
-
-namespace MADS {
-namespace MADSV2 {
-namespace Forest {
-namespace Rooms {
-
-extern void room_210_preload();
-extern void room_210_synchronize(Common::Serializer &s);
-
-} // namespace Rooms
-} // namespace Forest
-} // namespace MADSV2
-} // namespace MADS
-
-#endif
diff --git a/engines/mads/madsv2/forest/rooms/room211.cpp b/engines/mads/madsv2/forest/rooms/room211.cpp
index 1477e3d4af2..759ddaf4749 100644
--- a/engines/mads/madsv2/forest/rooms/room211.cpp
+++ b/engines/mads/madsv2/forest/rooms/room211.cpp
@@ -19,15 +19,14 @@
  *
  */
 
+#include "mads/madsv2/forest/rooms/section1.h"
 #include "mads/madsv2/forest/digi.h"
+#include "mads/madsv2/forest/global.h"
+#include "mads/madsv2/forest/midi.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/kernel.h"
 #include "mads/madsv2/core/matte.h"
-#include "mads/madsv2/forest/midi.h"
 #include "mads/madsv2/core/player.h"
-#include "mads/madsv2/forest/global.h"
-#include "mads/madsv2/forest/rooms/section1.h"
-#include "mads/madsv2/forest/rooms/room211.h"
 
 namespace MADS {
 namespace MADSV2 {
diff --git a/engines/mads/madsv2/forest/rooms/room211.h b/engines/mads/madsv2/forest/rooms/room211.h
deleted file mode 100644
index acd4d55057d..00000000000
--- a/engines/mads/madsv2/forest/rooms/room211.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/* 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 MADS_FOREST_ROOMS_211_H
-#define MADS_FOREST_ROOMS_211_H
-
-#include "common/serializer.h"
-#include "mads/madsv2/core/general.h"
-
-namespace MADS {
-namespace MADSV2 {
-namespace Forest {
-namespace Rooms {
-
-extern void room_211_preload();
-extern void room_211_synchronize(Common::Serializer &s);
-
-} // namespace Rooms
-} // namespace Forest
-} // namespace MADSV2
-} // namespace MADS
-
-#endif
diff --git a/engines/mads/madsv2/forest/rooms/room220.cpp b/engines/mads/madsv2/forest/rooms/room220.cpp
index bb1d922ab1e..75de51dac4d 100644
--- a/engines/mads/madsv2/forest/rooms/room220.cpp
+++ b/engines/mads/madsv2/forest/rooms/room220.cpp
@@ -19,15 +19,14 @@
  *
  */
 
+#include "mads/madsv2/forest/rooms/section1.h"
 #include "mads/madsv2/forest/digi.h"
+#include "mads/madsv2/forest/global.h"
+#include "mads/madsv2/forest/midi.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/kernel.h"
 #include "mads/madsv2/core/matte.h"
-#include "mads/madsv2/forest/midi.h"
 #include "mads/madsv2/core/player.h"
-#include "mads/madsv2/forest/global.h"
-#include "mads/madsv2/forest/rooms/section1.h"
-#include "mads/madsv2/forest/rooms/room220.h"
 
 namespace MADS {
 namespace MADSV2 {
diff --git a/engines/mads/madsv2/forest/rooms/room220.h b/engines/mads/madsv2/forest/rooms/room220.h
deleted file mode 100644
index eefc984dede..00000000000
--- a/engines/mads/madsv2/forest/rooms/room220.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/* 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 MADS_FOREST_ROOMS_220_H
-#define MADS_FOREST_ROOMS_220_H
-
-#include "common/serializer.h"
-#include "mads/madsv2/core/general.h"
-
-namespace MADS {
-namespace MADSV2 {
-namespace Forest {
-namespace Rooms {
-
-extern void room_220_preload();
-extern void room_220_synchronize(Common::Serializer &s);
-
-} // namespace Rooms
-} // namespace Forest
-} // namespace MADSV2
-} // namespace MADS
-
-#endif
diff --git a/engines/mads/madsv2/forest/rooms/room221.cpp b/engines/mads/madsv2/forest/rooms/room221.cpp
index 503d72a44d7..5fc64e79ff8 100644
--- a/engines/mads/madsv2/forest/rooms/room221.cpp
+++ b/engines/mads/madsv2/forest/rooms/room221.cpp
@@ -19,18 +19,17 @@
  *
  */
 
+#include "mads/madsv2/forest/rooms/section1.h"
+#include "mads/madsv2/forest/mads/inventory.h"
+#include "mads/madsv2/forest/mads/sounds.h"
+#include "mads/madsv2/forest/mads/words.h"
+#include "mads/madsv2/forest/global.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/imath.h"
 #include "mads/madsv2/core/inter.h"
 #include "mads/madsv2/core/kernel.h"
 #include "mads/madsv2/core/sound.h"
 #include "mads/madsv2/core/text.h"
-#include "mads/madsv2/forest/mads/inventory.h"
-#include "mads/madsv2/forest/mads/sounds.h"
-#include "mads/madsv2/forest/mads/words.h"
-#include "mads/madsv2/forest/global.h"
-#include "mads/madsv2/forest/rooms/section1.h"
-#include "mads/madsv2/forest/rooms/room221.h"
 
 namespace MADS {
 namespace MADSV2 {
diff --git a/engines/mads/madsv2/forest/rooms/room221.h b/engines/mads/madsv2/forest/rooms/room221.h
deleted file mode 100644
index 727083335cf..00000000000
--- a/engines/mads/madsv2/forest/rooms/room221.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/* 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 MADS_FOREST_ROOMS_221_H
-#define MADS_FOREST_ROOMS_221_H
-
-#include "common/serializer.h"
-#include "mads/madsv2/core/general.h"
-
-namespace MADS {
-namespace MADSV2 {
-namespace Forest {
-namespace Rooms {
-
-extern void room_221_preload();
-extern void room_221_synchronize(Common::Serializer &s);
-
-} // namespace Rooms
-} // namespace Forest
-} // namespace MADSV2
-} // namespace MADS
-
-#endif
diff --git a/engines/mads/madsv2/forest/rooms/room301.cpp b/engines/mads/madsv2/forest/rooms/room301.cpp
index 949784b5af7..e95322841a4 100644
--- a/engines/mads/madsv2/forest/rooms/room301.cpp
+++ b/engines/mads/madsv2/forest/rooms/room301.cpp
@@ -19,16 +19,15 @@
  *
  */
 
-#include "mads/madsv2/forest/journal.h"
 #include "mads/madsv2/forest/digi.h"
+#include "mads/madsv2/forest/global.h"
+#include "mads/madsv2/forest/journal.h"
+#include "mads/madsv2/forest/midi.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/inter.h"
 #include "mads/madsv2/core/kernel.h"
-#include "mads/madsv2/forest/midi.h"
 #include "mads/madsv2/core/object.h"
 #include "mads/madsv2/core/player.h"
-#include "mads/madsv2/forest/global.h"
-#include "mads/madsv2/forest/rooms/room301.h"
 
 namespace MADS {
 namespace MADSV2 {
diff --git a/engines/mads/madsv2/forest/rooms/room301.h b/engines/mads/madsv2/forest/rooms/room301.h
deleted file mode 100644
index 3b4d6a2ad63..00000000000
--- a/engines/mads/madsv2/forest/rooms/room301.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/* 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 MADS_FOREST_ROOMS_301_H
-#define MADS_FOREST_ROOMS_301_H
-
-#include "common/serializer.h"
-#include "mads/madsv2/core/general.h"
-
-namespace MADS {
-namespace MADSV2 {
-namespace Forest {
-namespace Rooms {
-
-extern void room_301_preload();
-extern void room_301_synchronize(Common::Serializer &s);
-
-} // namespace Rooms
-} // namespace Forest
-} // namespace MADSV2
-} // namespace MADS
-
-#endif
diff --git a/engines/mads/madsv2/forest/rooms/room302.cpp b/engines/mads/madsv2/forest/rooms/room302.cpp
index a6a8b061c41..0986fe1397f 100644
--- a/engines/mads/madsv2/forest/rooms/room302.cpp
+++ b/engines/mads/madsv2/forest/rooms/room302.cpp
@@ -19,23 +19,22 @@
  *
  */
 
+#include "mads/madsv2/forest/rooms/section1.h"
+#include "mads/madsv2/forest/mads/inventory.h"
+#include "mads/madsv2/forest/mads/sounds.h"
+#include "mads/madsv2/forest/mads/words.h"
 #include "mads/madsv2/forest/digi.h"
+#include "mads/madsv2/forest/global.h"
+#include "mads/madsv2/forest/journal.h"
+#include "mads/madsv2/forest/midi.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/imath.h"
 #include "mads/madsv2/core/inter.h"
 #include "mads/madsv2/core/kernel.h"
-#include "mads/madsv2/forest/midi.h"
 #include "mads/madsv2/core/object.h"
 #include "mads/madsv2/core/player.h"
 #include "mads/madsv2/core/sound.h"
 #include "mads/madsv2/core/text.h"
-#include "mads/madsv2/forest/mads/inventory.h"
-#include "mads/madsv2/forest/mads/sounds.h"
-#include "mads/madsv2/forest/mads/words.h"
-#include "mads/madsv2/forest/global.h"
-#include "mads/madsv2/forest/journal.h"
-#include "mads/madsv2/forest/rooms/section1.h"
-#include "mads/madsv2/forest/rooms/room302.h"
 
 namespace MADS {
 namespace MADSV2 {
diff --git a/engines/mads/madsv2/forest/rooms/room302.h b/engines/mads/madsv2/forest/rooms/room302.h
deleted file mode 100644
index 3954e480767..00000000000
--- a/engines/mads/madsv2/forest/rooms/room302.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/* 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 MADS_FOREST_ROOMS_302_H
-#define MADS_FOREST_ROOMS_302_H
-
-#include "common/serializer.h"
-#include "mads/madsv2/core/general.h"
-
-namespace MADS {
-namespace MADSV2 {
-namespace Forest {
-namespace Rooms {
-
-extern void room_302_preload();
-extern void room_302_synchronize(Common::Serializer &s);
-
-} // namespace Rooms
-} // namespace Forest
-} // namespace MADSV2
-} // namespace MADS
-
-#endif
diff --git a/engines/mads/madsv2/forest/rooms/room303.cpp b/engines/mads/madsv2/forest/rooms/room303.cpp
index 5587005d36a..e1ad2a467b6 100644
--- a/engines/mads/madsv2/forest/rooms/room303.cpp
+++ b/engines/mads/madsv2/forest/rooms/room303.cpp
@@ -19,7 +19,13 @@
  *
  */
 
+#include "mads/madsv2/forest/rooms/section1.h"
+#include "mads/madsv2/forest/mads/inventory.h"
+#include "mads/madsv2/forest/mads/sounds.h"
+#include "mads/madsv2/forest/mads/words.h"
 #include "mads/madsv2/forest/digi.h"
+#include "mads/madsv2/forest/global.h"
+#include "mads/madsv2/forest/journal.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/imath.h"
 #include "mads/madsv2/core/inter.h"
@@ -28,13 +34,6 @@
 #include "mads/madsv2/core/sound.h"
 #include "mads/madsv2/core/sprite.h"
 #include "mads/madsv2/core/text.h"
-#include "mads/madsv2/forest/mads/inventory.h"
-#include "mads/madsv2/forest/mads/sounds.h"
-#include "mads/madsv2/forest/mads/words.h"
-#include "mads/madsv2/forest/global.h"
-#include "mads/madsv2/forest/journal.h"
-#include "mads/madsv2/forest/rooms/section1.h"
-#include "mads/madsv2/forest/rooms/room303.h"
 
 namespace MADS {
 namespace MADSV2 {
diff --git a/engines/mads/madsv2/forest/rooms/room303.h b/engines/mads/madsv2/forest/rooms/room303.h
deleted file mode 100644
index ee4b4ef75fa..00000000000
--- a/engines/mads/madsv2/forest/rooms/room303.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/* 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 MADS_FOREST_ROOMS_303_H
-#define MADS_FOREST_ROOMS_303_H
-
-#include "common/serializer.h"
-#include "mads/madsv2/core/general.h"
-
-namespace MADS {
-namespace MADSV2 {
-namespace Forest {
-namespace Rooms {
-
-extern void room_303_preload();
-extern void room_303_synchronize(Common::Serializer &s);
-
-} // namespace Rooms
-} // namespace Forest
-} // namespace MADSV2
-} // namespace MADS
-
-#endif
diff --git a/engines/mads/madsv2/forest/rooms/room304.cpp b/engines/mads/madsv2/forest/rooms/room304.cpp
index ef4329aaee3..3edd9ecabe0 100644
--- a/engines/mads/madsv2/forest/rooms/room304.cpp
+++ b/engines/mads/madsv2/forest/rooms/room304.cpp
@@ -19,23 +19,22 @@
  *
  */
 
-#include "mads/madsv2/core/config.h"
+#include "mads/madsv2/forest/rooms/section1.h"
+#include "mads/madsv2/forest/mads/inventory.h"
+#include "mads/madsv2/forest/mads/sounds.h"
+#include "mads/madsv2/forest/mads/words.h"
 #include "mads/madsv2/forest/digi.h"
+#include "mads/madsv2/forest/global.h"
+#include "mads/madsv2/forest/journal.h"
+#include "mads/madsv2/forest/midi.h"
+#include "mads/madsv2/core/config.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/imath.h"
 #include "mads/madsv2/core/inter.h"
 #include "mads/madsv2/core/kernel.h"
-#include "mads/madsv2/forest/midi.h"
 #include "mads/madsv2/core/player.h"
 #include "mads/madsv2/core/sound.h"
 #include "mads/madsv2/core/text.h"
-#include "mads/madsv2/forest/mads/inventory.h"
-#include "mads/madsv2/forest/mads/sounds.h"
-#include "mads/madsv2/forest/mads/words.h"
-#include "mads/madsv2/forest/global.h"
-#include "mads/madsv2/forest/journal.h"
-#include "mads/madsv2/forest/rooms/section1.h"
-#include "mads/madsv2/forest/rooms/room304.h"
 
 namespace MADS {
 namespace MADSV2 {
diff --git a/engines/mads/madsv2/forest/rooms/room304.h b/engines/mads/madsv2/forest/rooms/room304.h
deleted file mode 100644
index bbc85359cb4..00000000000
--- a/engines/mads/madsv2/forest/rooms/room304.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/* 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 MADS_FOREST_ROOMS_304_H
-#define MADS_FOREST_ROOMS_304_H
-
-#include "common/serializer.h"
-#include "mads/madsv2/core/general.h"
-
-namespace MADS {
-namespace MADSV2 {
-namespace Forest {
-namespace Rooms {
-
-extern void room_304_preload();
-extern void room_304_synchronize(Common::Serializer &s);
-
-} // namespace Rooms
-} // namespace Forest
-} // namespace MADSV2
-} // namespace MADS
-
-#endif
diff --git a/engines/mads/madsv2/forest/rooms/room305.cpp b/engines/mads/madsv2/forest/rooms/room305.cpp
index 744461f52fa..ba7746f4845 100644
--- a/engines/mads/madsv2/forest/rooms/room305.cpp
+++ b/engines/mads/madsv2/forest/rooms/room305.cpp
@@ -19,19 +19,18 @@
  *
  */
 
-#include "mads/madsv2/core/config.h"
+#include "mads/madsv2/forest/mads/inventory.h"
+#include "mads/madsv2/forest/mads/sounds.h"
+#include "mads/madsv2/forest/mads/words.h"
 #include "mads/madsv2/forest/digi.h"
+#include "mads/madsv2/forest/global.h"
+#include "mads/madsv2/forest/journal.h"
+#include "mads/madsv2/core/config.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/imath.h"
 #include "mads/madsv2/core/inter.h"
 #include "mads/madsv2/core/kernel.h"
 #include "mads/madsv2/core/player.h"
-#include "mads/madsv2/forest/mads/inventory.h"
-#include "mads/madsv2/forest/mads/sounds.h"
-#include "mads/madsv2/forest/mads/words.h"
-#include "mads/madsv2/forest/global.h"
-#include "mads/madsv2/forest/journal.h"
-#include "mads/madsv2/forest/rooms/room305.h"
 
 namespace MADS {
 namespace MADSV2 {
diff --git a/engines/mads/madsv2/forest/rooms/room305.h b/engines/mads/madsv2/forest/rooms/room305.h
deleted file mode 100644
index 1a55b1d83a2..00000000000
--- a/engines/mads/madsv2/forest/rooms/room305.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/* 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 MADS_FOREST_ROOMS_305_H
-#define MADS_FOREST_ROOMS_305_H
-
-#include "common/serializer.h"
-#include "mads/madsv2/core/general.h"
-
-namespace MADS {
-namespace MADSV2 {
-namespace Forest {
-namespace Rooms {
-
-extern void room_305_preload();
-extern void room_305_synchronize(Common::Serializer &s);
-
-} // namespace Rooms
-} // namespace Forest
-} // namespace MADSV2
-} // namespace MADS
-
-#endif
diff --git a/engines/mads/madsv2/forest/rooms/room306.cpp b/engines/mads/madsv2/forest/rooms/room306.cpp
index 4af54428a68..3eb3d4d2ed9 100644
--- a/engines/mads/madsv2/forest/rooms/room306.cpp
+++ b/engines/mads/madsv2/forest/rooms/room306.cpp
@@ -19,23 +19,22 @@
  *
  */
 
+#include "mads/madsv2/forest/mads/inventory.h"
+#include "mads/madsv2/forest/mads/sounds.h"
+#include "mads/madsv2/forest/mads/words.h"
+#include "mads/madsv2/forest/digi.h"
+#include "mads/madsv2/forest/global.h"
+#include "mads/madsv2/forest/journal.h"
+#include "mads/madsv2/forest/midi.h"
 #include "mads/madsv2/core/config.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/imath.h"
 #include "mads/madsv2/core/inter.h"
-#include "mads/madsv2/forest/digi.h"
 #include "mads/madsv2/core/kernel.h"
-#include "mads/madsv2/forest/midi.h"
 #include "mads/madsv2/core/player.h"
 #include "mads/madsv2/core/sound.h"
 #include "mads/madsv2/core/sprite.h"
 #include "mads/madsv2/core/text.h"
-#include "mads/madsv2/forest/mads/inventory.h"
-#include "mads/madsv2/forest/mads/sounds.h"
-#include "mads/madsv2/forest/mads/words.h"
-#include "mads/madsv2/forest/global.h"
-#include "mads/madsv2/forest/journal.h"
-#include "mads/madsv2/forest/rooms/room306.h"
 
 namespace MADS {
 namespace MADSV2 {
diff --git a/engines/mads/madsv2/forest/rooms/room306.h b/engines/mads/madsv2/forest/rooms/room306.h
deleted file mode 100644
index 57f2e70ff9b..00000000000
--- a/engines/mads/madsv2/forest/rooms/room306.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/* 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 MADS_FOREST_ROOMS_306_H
-#define MADS_FOREST_ROOMS_306_H
-
-#include "common/serializer.h"
-#include "mads/madsv2/core/general.h"
-
-namespace MADS {
-namespace MADSV2 {
-namespace Forest {
-namespace Rooms {
-
-extern void room_306_preload();
-extern void room_306_synchronize(Common::Serializer &s);
-
-} // namespace Rooms
-} // namespace Forest
-} // namespace MADSV2
-} // namespace MADS
-
-#endif
diff --git a/engines/mads/madsv2/forest/rooms/room307.cpp b/engines/mads/madsv2/forest/rooms/room307.cpp
index 9e6999583c6..5e612f19fba 100644
--- a/engines/mads/madsv2/forest/rooms/room307.cpp
+++ b/engines/mads/madsv2/forest/rooms/room307.cpp
@@ -19,25 +19,24 @@
  *
  */
 
-#include "mads/madsv2/core/config.h"
+#include "mads/madsv2/forest/rooms/section3.h"
+#include "mads/madsv2/forest/mads/inventory.h"
+#include "mads/madsv2/forest/mads/sounds.h"
+#include "mads/madsv2/forest/mads/words.h"
 #include "mads/madsv2/forest/digi.h"
+#include "mads/madsv2/forest/global.h"
+#include "mads/madsv2/forest/journal.h"
+#include "mads/madsv2/forest/midi.h"
+#include "mads/madsv2/core/config.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/imath.h"
 #include "mads/madsv2/core/inter.h"
 #include "mads/madsv2/core/kernel.h"
-#include "mads/madsv2/forest/midi.h"
 #include "mads/madsv2/core/object.h"
 #include "mads/madsv2/core/player.h"
 #include "mads/madsv2/core/sound.h"
 #include "mads/madsv2/core/sprite.h"
 #include "mads/madsv2/core/text.h"
-#include "mads/madsv2/forest/mads/inventory.h"
-#include "mads/madsv2/forest/mads/sounds.h"
-#include "mads/madsv2/forest/mads/words.h"
-#include "mads/madsv2/forest/global.h"
-#include "mads/madsv2/forest/journal.h"
-#include "mads/madsv2/forest/rooms/section3.h"
-#include "mads/madsv2/forest/rooms/room307.h"
 
 namespace MADS {
 namespace MADSV2 {
diff --git a/engines/mads/madsv2/forest/rooms/room307.h b/engines/mads/madsv2/forest/rooms/room307.h
deleted file mode 100644
index e303654ec9f..00000000000
--- a/engines/mads/madsv2/forest/rooms/room307.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/* 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 MADS_FOREST_ROOMS_307_H
-#define MADS_FOREST_ROOMS_307_H
-
-#include "common/serializer.h"
-#include "mads/madsv2/core/general.h"
-
-namespace MADS {
-namespace MADSV2 {
-namespace Forest {
-namespace Rooms {
-
-extern void room_307_preload();
-extern void room_307_synchronize(Common::Serializer &s);
-
-} // namespace Rooms
-} // namespace Forest
-} // namespace MADSV2
-} // namespace MADS
-
-#endif
diff --git a/engines/mads/madsv2/forest/rooms/room308.cpp b/engines/mads/madsv2/forest/rooms/room308.cpp
index f6e514b7cb9..527579e8d89 100644
--- a/engines/mads/madsv2/forest/rooms/room308.cpp
+++ b/engines/mads/madsv2/forest/rooms/room308.cpp
@@ -19,10 +19,12 @@
  *
  */
 
-#include "mads/madsv2/core/config.h"
+#include "mads/madsv2/forest/rooms/section3.h"
 #include "mads/madsv2/forest/digi.h"
 #include "mads/madsv2/forest/extra.h"
+#include "mads/madsv2/forest/global.h"
 #include "mads/madsv2/forest/journal.h"
+#include "mads/madsv2/core/config.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/imath.h"
 #include "mads/madsv2/core/inter.h"
@@ -30,9 +32,6 @@
 #include "mads/madsv2/core/object.h"
 #include "mads/madsv2/core/player.h"
 #include "mads/madsv2/core/sprite.h"
-#include "mads/madsv2/forest/global.h"
-#include "mads/madsv2/forest/rooms/section3.h"
-#include "mads/madsv2/forest/rooms/room308.h"
 
 namespace MADS {
 namespace MADSV2 {
diff --git a/engines/mads/madsv2/forest/rooms/room308.h b/engines/mads/madsv2/forest/rooms/room308.h
deleted file mode 100644
index 878bb459214..00000000000
--- a/engines/mads/madsv2/forest/rooms/room308.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/* 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 MADS_FOREST_ROOMS_308_H
-#define MADS_FOREST_ROOMS_308_H
-
-#include "common/serializer.h"
-#include "mads/madsv2/core/general.h"
-
-namespace MADS {
-namespace MADSV2 {
-namespace Forest {
-namespace Rooms {
-
-extern void room_308_preload();
-extern void room_308_synchronize(Common::Serializer &s);
-
-} // namespace Rooms
-} // namespace Forest
-} // namespace MADSV2
-} // namespace MADS
-
-#endif
diff --git a/engines/mads/madsv2/forest/rooms/room321.cpp b/engines/mads/madsv2/forest/rooms/room321.cpp
index b5db451096b..7682e0be2f4 100644
--- a/engines/mads/madsv2/forest/rooms/room321.cpp
+++ b/engines/mads/madsv2/forest/rooms/room321.cpp
@@ -19,7 +19,13 @@
  *
  */
 
+#include "mads/madsv2/forest/rooms/section1.h"
+#include "mads/madsv2/forest/mads/inventory.h"
+#include "mads/madsv2/forest/mads/sounds.h"
+#include "mads/madsv2/forest/mads/words.h"
 #include "mads/madsv2/forest/digi.h"
+#include "mads/madsv2/forest/global.h"
+#include "mads/madsv2/forest/journal.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/imath.h"
 #include "mads/madsv2/core/inter.h"
@@ -29,13 +35,6 @@
 #include "mads/madsv2/core/sound.h"
 #include "mads/madsv2/core/sprite.h"
 #include "mads/madsv2/core/text.h"
-#include "mads/madsv2/forest/mads/inventory.h"
-#include "mads/madsv2/forest/mads/sounds.h"
-#include "mads/madsv2/forest/mads/words.h"
-#include "mads/madsv2/forest/global.h"
-#include "mads/madsv2/forest/journal.h"
-#include "mads/madsv2/forest/rooms/section1.h"
-#include "mads/madsv2/forest/rooms/room321.h"
 
 namespace MADS {
 namespace MADSV2 {
diff --git a/engines/mads/madsv2/forest/rooms/room321.h b/engines/mads/madsv2/forest/rooms/room321.h
deleted file mode 100644
index 50d9b82d4ef..00000000000
--- a/engines/mads/madsv2/forest/rooms/room321.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/* 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 MADS_FOREST_ROOMS_321_H
-#define MADS_FOREST_ROOMS_321_H
-
-#include "common/serializer.h"
-#include "mads/madsv2/core/general.h"
-
-namespace MADS {
-namespace MADSV2 {
-namespace Forest {
-namespace Rooms {
-
-extern void room_321_preload();
-extern void room_321_synchronize(Common::Serializer &s);
-
-} // namespace Rooms
-} // namespace Forest
-} // namespace MADSV2
-} // namespace MADS
-
-#endif
diff --git a/engines/mads/madsv2/forest/rooms/room322.cpp b/engines/mads/madsv2/forest/rooms/room322.cpp
index f2dacc67a0e..9caa8bee037 100644
--- a/engines/mads/madsv2/forest/rooms/room322.cpp
+++ b/engines/mads/madsv2/forest/rooms/room322.cpp
@@ -19,24 +19,23 @@
  *
  */
 
+#include "mads/madsv2/forest/rooms/section1.h"
+#include "mads/madsv2/forest/mads/inventory.h"
+#include "mads/madsv2/forest/mads/sounds.h"
+#include "mads/madsv2/forest/mads/words.h"
+#include "mads/madsv2/forest/digi.h"
+#include "mads/madsv2/forest/global.h"
+#include "mads/madsv2/forest/journal.h"
+#include "mads/madsv2/forest/midi.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/imath.h"
 #include "mads/madsv2/core/inter.h"
 #include "mads/madsv2/core/kernel.h"
-#include "mads/madsv2/forest/digi.h"
-#include "mads/madsv2/forest/midi.h"
 #include "mads/madsv2/core/object.h"
 #include "mads/madsv2/core/player.h"
 #include "mads/madsv2/core/sound.h"
 #include "mads/madsv2/core/sprite.h"
 #include "mads/madsv2/core/text.h"
-#include "mads/madsv2/forest/journal.h"
-#include "mads/madsv2/forest/mads/inventory.h"
-#include "mads/madsv2/forest/mads/sounds.h"
-#include "mads/madsv2/forest/mads/words.h"
-#include "mads/madsv2/forest/global.h"
-#include "mads/madsv2/forest/rooms/section1.h"
-#include "mads/madsv2/forest/rooms/room322.h"
 
 namespace MADS {
 namespace MADSV2 {
diff --git a/engines/mads/madsv2/forest/rooms/room322.h b/engines/mads/madsv2/forest/rooms/room322.h
deleted file mode 100644
index 7f17842cbfe..00000000000
--- a/engines/mads/madsv2/forest/rooms/room322.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/* 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 MADS_FOREST_ROOMS_322_H
-#define MADS_FOREST_ROOMS_322_H
-
-#include "common/serializer.h"
-#include "mads/madsv2/core/general.h"
-
-namespace MADS {
-namespace MADSV2 {
-namespace Forest {
-namespace Rooms {
-
-extern void room_322_preload();
-extern void room_322_synchronize(Common::Serializer &s);
-
-} // namespace Rooms
-} // namespace Forest
-} // namespace MADSV2
-} // namespace MADS
-
-#endif
diff --git a/engines/mads/madsv2/forest/rooms/room401.cpp b/engines/mads/madsv2/forest/rooms/room401.cpp
index b2f8706c53e..270051af172 100644
--- a/engines/mads/madsv2/forest/rooms/room401.cpp
+++ b/engines/mads/madsv2/forest/rooms/room401.cpp
@@ -19,24 +19,23 @@
  *
  */
 
+#include "mads/madsv2/forest/rooms/section1.h"
+#include "mads/madsv2/forest/mads/inventory.h"
+#include "mads/madsv2/forest/mads/sounds.h"
+#include "mads/madsv2/forest/mads/words.h"
+#include "mads/madsv2/forest/digi.h"
+#include "mads/madsv2/forest/global.h"
+#include "mads/madsv2/forest/journal.h"
+#include "mads/madsv2/forest/midi.h"
 #include "mads/madsv2/core/config.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/imath.h"
 #include "mads/madsv2/core/inter.h"
 #include "mads/madsv2/core/kernel.h"
-#include "mads/madsv2/forest/digi.h"
 #include "mads/madsv2/core/sprite.h"
-#include "mads/madsv2/forest/midi.h"
 #include "mads/madsv2/core/player.h"
 #include "mads/madsv2/core/sound.h"
 #include "mads/madsv2/core/text.h"
-#include "mads/madsv2/forest/journal.h"
-#include "mads/madsv2/forest/mads/inventory.h"
-#include "mads/madsv2/forest/mads/sounds.h"
-#include "mads/madsv2/forest/mads/words.h"
-#include "mads/madsv2/forest/global.h"
-#include "mads/madsv2/forest/rooms/section1.h"
-#include "mads/madsv2/forest/rooms/room401.h"
 
 namespace MADS {
 namespace MADSV2 {
diff --git a/engines/mads/madsv2/forest/rooms/room401.h b/engines/mads/madsv2/forest/rooms/room401.h
deleted file mode 100644
index 8a278732cb1..00000000000
--- a/engines/mads/madsv2/forest/rooms/room401.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/* 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 MADS_FOREST_ROOMS_401_H
-#define MADS_FOREST_ROOMS_401_H
-
-#include "common/serializer.h"
-#include "mads/madsv2/core/general.h"
-
-namespace MADS {
-namespace MADSV2 {
-namespace Forest {
-namespace Rooms {
-
-extern void room_401_preload();
-extern void room_401_synchronize(Common::Serializer &s);
-
-} // namespace Rooms
-} // namespace Forest
-} // namespace MADSV2
-} // namespace MADS
-
-#endif
diff --git a/engines/mads/madsv2/forest/rooms/room402.cpp b/engines/mads/madsv2/forest/rooms/room402.cpp
index c18b0b0453e..95c48e81fed 100644
--- a/engines/mads/madsv2/forest/rooms/room402.cpp
+++ b/engines/mads/madsv2/forest/rooms/room402.cpp
@@ -19,23 +19,22 @@
  *
  */
 
+#include "mads/madsv2/forest/rooms/section1.h"
+#include "mads/madsv2/forest/mads/inventory.h"
+#include "mads/madsv2/forest/mads/sounds.h"
+#include "mads/madsv2/forest/mads/words.h"
 #include "mads/madsv2/forest/digi.h"
+#include "mads/madsv2/forest/global.h"
+#include "mads/madsv2/forest/journal.h"
+#include "mads/madsv2/forest/midi.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/imath.h"
 #include "mads/madsv2/core/inter.h"
 #include "mads/madsv2/core/kernel.h"
-#include "mads/madsv2/forest/midi.h"
 #include "mads/madsv2/core/player.h"
 #include "mads/madsv2/core/sprite.h"
-#include "mads/madsv2/forest/journal.h"
 #include "mads/madsv2/core/sound.h"
 #include "mads/madsv2/core/text.h"
-#include "mads/madsv2/forest/mads/inventory.h"
-#include "mads/madsv2/forest/mads/sounds.h"
-#include "mads/madsv2/forest/mads/words.h"
-#include "mads/madsv2/forest/global.h"
-#include "mads/madsv2/forest/rooms/section1.h"
-#include "mads/madsv2/forest/rooms/room402.h"
 
 namespace MADS {
 namespace MADSV2 {
diff --git a/engines/mads/madsv2/forest/rooms/room402.h b/engines/mads/madsv2/forest/rooms/room402.h
deleted file mode 100644
index 59b9629dd34..00000000000
--- a/engines/mads/madsv2/forest/rooms/room402.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/* 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 MADS_FOREST_ROOMS_402_H
-#define MADS_FOREST_ROOMS_402_H
-
-#include "common/serializer.h"
-#include "mads/madsv2/core/general.h"
-
-namespace MADS {
-namespace MADSV2 {
-namespace Forest {
-namespace Rooms {
-
-extern void room_402_preload();
-extern void room_402_synchronize(Common::Serializer &s);
-
-} // namespace Rooms
-} // namespace Forest
-} // namespace MADSV2
-} // namespace MADS
-
-#endif
diff --git a/engines/mads/madsv2/forest/rooms/room403.cpp b/engines/mads/madsv2/forest/rooms/room403.cpp
index e1713a0dd2b..14c3d8e7804 100644
--- a/engines/mads/madsv2/forest/rooms/room403.cpp
+++ b/engines/mads/madsv2/forest/rooms/room403.cpp
@@ -19,25 +19,24 @@
  *
  */
 
-#include "mads/madsv2/core/config.h"
+#include "mads/madsv2/forest/mads/inventory.h"
+#include "mads/madsv2/forest/mads/sounds.h"
+#include "mads/madsv2/forest/mads/words.h"
 #include "mads/madsv2/forest/digi.h"
+#include "mads/madsv2/forest/extra.h"
+#include "mads/madsv2/forest/global.h"
+#include "mads/madsv2/forest/journal.h"
+#include "mads/madsv2/forest/midi.h"
+#include "mads/madsv2/core/config.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/imath.h"
 #include "mads/madsv2/core/inter.h"
 #include "mads/madsv2/core/kernel.h"
-#include "mads/madsv2/forest/midi.h"
 #include "mads/madsv2/core/object.h"
 #include "mads/madsv2/core/player.h"
 #include "mads/madsv2/core/sprite.h"
 #include "mads/madsv2/core/sound.h"
 #include "mads/madsv2/core/text.h"
-#include "mads/madsv2/forest/mads/inventory.h"
-#include "mads/madsv2/forest/mads/sounds.h"
-#include "mads/madsv2/forest/mads/words.h"
-#include "mads/madsv2/forest/extra.h"
-#include "mads/madsv2/forest/global.h"
-#include "mads/madsv2/forest/journal.h"
-#include "mads/madsv2/forest/rooms/room403.h"
 
 namespace MADS {
 namespace MADSV2 {
diff --git a/engines/mads/madsv2/forest/rooms/room403.h b/engines/mads/madsv2/forest/rooms/room403.h
deleted file mode 100644
index a12e4c0cdf1..00000000000
--- a/engines/mads/madsv2/forest/rooms/room403.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/* 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 MADS_FOREST_ROOMS_403_H
-#define MADS_FOREST_ROOMS_403_H
-
-#include "common/serializer.h"
-#include "mads/madsv2/core/general.h"
-
-namespace MADS {
-namespace MADSV2 {
-namespace Forest {
-namespace Rooms {
-
-extern void room_403_preload();
-extern void room_403_synchronize(Common::Serializer &s);
-
-} // namespace Rooms
-} // namespace Forest
-} // namespace MADSV2
-} // namespace MADS
-
-#endif
diff --git a/engines/mads/madsv2/forest/rooms/room404.cpp b/engines/mads/madsv2/forest/rooms/room404.cpp
index 86bfcca25e2..54cb1bd1ac9 100644
--- a/engines/mads/madsv2/forest/rooms/room404.cpp
+++ b/engines/mads/madsv2/forest/rooms/room404.cpp
@@ -19,8 +19,14 @@
  *
  */
 
-#include "mads/madsv2/core/config.h"
+#include "mads/madsv2/forest/mads/inventory.h"
+#include "mads/madsv2/forest/mads/sounds.h"
+#include "mads/madsv2/forest/mads/words.h"
 #include "mads/madsv2/forest/digi.h"
+#include "mads/madsv2/forest/extra.h"
+#include "mads/madsv2/forest/global.h"
+#include "mads/madsv2/forest/journal.h"
+#include "mads/madsv2/core/config.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/imath.h"
 #include "mads/madsv2/core/inter.h"
@@ -29,13 +35,6 @@
 #include "mads/madsv2/core/player.h"
 #include "mads/madsv2/core/sound.h"
 #include "mads/madsv2/core/text.h"
-#include "mads/madsv2/forest/mads/inventory.h"
-#include "mads/madsv2/forest/mads/sounds.h"
-#include "mads/madsv2/forest/mads/words.h"
-#include "mads/madsv2/forest/extra.h"
-#include "mads/madsv2/forest/journal.h"
-#include "mads/madsv2/forest/global.h"
-#include "mads/madsv2/forest/rooms/room404.h"
 
 namespace MADS {
 namespace MADSV2 {
diff --git a/engines/mads/madsv2/forest/rooms/room404.h b/engines/mads/madsv2/forest/rooms/room404.h
deleted file mode 100644
index 08b49e94a69..00000000000
--- a/engines/mads/madsv2/forest/rooms/room404.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/* 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 MADS_FOREST_ROOMS_404_H
-#define MADS_FOREST_ROOMS_404_H
-
-#include "common/serializer.h"
-#include "mads/madsv2/core/general.h"
-
-namespace MADS {
-namespace MADSV2 {
-namespace Forest {
-namespace Rooms {
-
-extern void room_404_preload();
-extern void room_404_synchronize(Common::Serializer &s);
-
-} // namespace Rooms
-} // namespace Forest
-} // namespace MADSV2
-} // namespace MADS
-
-#endif
diff --git a/engines/mads/madsv2/forest/rooms/room405.cpp b/engines/mads/madsv2/forest/rooms/room405.cpp
index b17501b8f1f..397d6500c30 100644
--- a/engines/mads/madsv2/forest/rooms/room405.cpp
+++ b/engines/mads/madsv2/forest/rooms/room405.cpp
@@ -19,8 +19,14 @@
  *
  */
 
-#include "mads/madsv2/core/config.h"
+#include "mads/madsv2/forest/mads/inventory.h"
+#include "mads/madsv2/forest/mads/sounds.h"
+#include "mads/madsv2/forest/mads/words.h"
 #include "mads/madsv2/forest/digi.h"
+#include "mads/madsv2/forest/extra.h"
+#include "mads/madsv2/forest/global.h"
+#include "mads/madsv2/forest/journal.h"
+#include "mads/madsv2/core/config.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/imath.h"
 #include "mads/madsv2/core/inter.h"
@@ -28,13 +34,6 @@
 #include "mads/madsv2/core/player.h"
 #include "mads/madsv2/core/sound.h"
 #include "mads/madsv2/core/text.h"
-#include "mads/madsv2/forest/mads/inventory.h"
-#include "mads/madsv2/forest/mads/sounds.h"
-#include "mads/madsv2/forest/mads/words.h"
-#include "mads/madsv2/forest/extra.h"
-#include "mads/madsv2/forest/global.h"
-#include "mads/madsv2/forest/journal.h"
-#include "mads/madsv2/forest/rooms/room405.h"
 
 namespace MADS {
 namespace MADSV2 {
diff --git a/engines/mads/madsv2/forest/rooms/room405.h b/engines/mads/madsv2/forest/rooms/room405.h
deleted file mode 100644
index ec0d4dacced..00000000000
--- a/engines/mads/madsv2/forest/rooms/room405.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/* 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 MADS_FOREST_ROOMS_405_H
-#define MADS_FOREST_ROOMS_405_H
-
-#include "common/serializer.h"
-#include "mads/madsv2/core/general.h"
-
-namespace MADS {
-namespace MADSV2 {
-namespace Forest {
-namespace Rooms {
-
-extern void room_405_preload();
-extern void room_405_synchronize(Common::Serializer &s);
-
-} // namespace Rooms
-} // namespace Forest
-} // namespace MADSV2
-} // namespace MADS
-
-#endif
diff --git a/engines/mads/madsv2/forest/rooms/room420.cpp b/engines/mads/madsv2/forest/rooms/room420.cpp
index b212e995bc4..3ce680f7deb 100644
--- a/engines/mads/madsv2/forest/rooms/room420.cpp
+++ b/engines/mads/madsv2/forest/rooms/room420.cpp
@@ -19,14 +19,13 @@
  *
  */
 
+#include "mads/madsv2/forest/rooms/section1.h"
 #include "mads/madsv2/forest/digi.h"
+#include "mads/madsv2/forest/global.h"
+#include "mads/madsv2/forest/midi.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/kernel.h"
-#include "mads/madsv2/forest/midi.h"
 #include "mads/madsv2/core/player.h"
-#include "mads/madsv2/forest/global.h"
-#include "mads/madsv2/forest/rooms/section1.h"
-#include "mads/madsv2/forest/rooms/room420.h"
 
 namespace MADS {
 namespace MADSV2 {
diff --git a/engines/mads/madsv2/forest/rooms/room420.h b/engines/mads/madsv2/forest/rooms/room420.h
deleted file mode 100644
index db92afc132e..00000000000
--- a/engines/mads/madsv2/forest/rooms/room420.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/* 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 MADS_FOREST_ROOMS_420_H
-#define MADS_FOREST_ROOMS_420_H
-
-#include "common/serializer.h"
-#include "mads/madsv2/core/general.h"
-
-namespace MADS {
-namespace MADSV2 {
-namespace Forest {
-namespace Rooms {
-
-extern void room_420_preload();
-extern void room_420_synchronize(Common::Serializer &s);
-
-} // namespace Rooms
-} // namespace Forest
-} // namespace MADSV2
-} // namespace MADS
-
-#endif
diff --git a/engines/mads/madsv2/forest/rooms/room501.cpp b/engines/mads/madsv2/forest/rooms/room501.cpp
index 8c930fbe334..002a4f30897 100644
--- a/engines/mads/madsv2/forest/rooms/room501.cpp
+++ b/engines/mads/madsv2/forest/rooms/room501.cpp
@@ -19,22 +19,21 @@
  *
  */
 
+#include "mads/madsv2/forest/rooms/section5.h"
+#include "mads/madsv2/forest/mads/inventory.h"
+#include "mads/madsv2/forest/mads/sounds.h"
+#include "mads/madsv2/forest/mads/words.h"
+#include "mads/madsv2/forest/digi.h"
+#include "mads/madsv2/forest/global.h"
+#include "mads/madsv2/forest/journal.h"
+#include "mads/madsv2/forest/midi.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/imath.h"
 #include "mads/madsv2/core/inter.h"
 #include "mads/madsv2/core/kernel.h"
-#include "mads/madsv2/forest/digi.h"
-#include "mads/madsv2/forest/midi.h"
 #include "mads/madsv2/core/player.h"
 #include "mads/madsv2/core/sound.h"
 #include "mads/madsv2/core/text.h"
-#include "mads/madsv2/forest/mads/inventory.h"
-#include "mads/madsv2/forest/mads/sounds.h"
-#include "mads/madsv2/forest/mads/words.h"
-#include "mads/madsv2/forest/global.h"
-#include "mads/madsv2/forest/journal.h"
-#include "mads/madsv2/forest/rooms/section5.h"
-#include "mads/madsv2/forest/rooms/room501.h"
 
 namespace MADS {
 namespace MADSV2 {
diff --git a/engines/mads/madsv2/forest/rooms/room501.h b/engines/mads/madsv2/forest/rooms/room501.h
deleted file mode 100644
index cf4c880bbf2..00000000000
--- a/engines/mads/madsv2/forest/rooms/room501.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/* 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 MADS_FOREST_ROOMS_501_H
-#define MADS_FOREST_ROOMS_501_H
-
-#include "common/serializer.h"
-#include "mads/madsv2/core/general.h"
-
-namespace MADS {
-namespace MADSV2 {
-namespace Forest {
-namespace Rooms {
-
-extern void room_501_preload();
-extern void room_501_synchronize(Common::Serializer &s);
-
-} // namespace Rooms
-} // namespace Forest
-} // namespace MADSV2
-} // namespace MADS
-
-#endif
diff --git a/engines/mads/madsv2/forest/rooms/room503.cpp b/engines/mads/madsv2/forest/rooms/room503.cpp
index 19672e9ccae..faa83c13f2b 100644
--- a/engines/mads/madsv2/forest/rooms/room503.cpp
+++ b/engines/mads/madsv2/forest/rooms/room503.cpp
@@ -19,24 +19,23 @@
  *
  */
 
+#include "mads/madsv2/forest/rooms/section5.h"
+#include "mads/madsv2/forest/mads/inventory.h"
+#include "mads/madsv2/forest/mads/sounds.h"
+#include "mads/madsv2/forest/mads/words.h"
 #include "mads/madsv2/forest/digi.h"
+#include "mads/madsv2/forest/global.h"
+#include "mads/madsv2/forest/journal.h"
+#include "mads/madsv2/forest/midi.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/imath.h"
 #include "mads/madsv2/core/inter.h"
 #include "mads/madsv2/core/kernel.h"
 #include "mads/madsv2/core/matte.h"
-#include "mads/madsv2/forest/midi.h"
 #include "mads/madsv2/core/mouse.h"
 #include "mads/madsv2/core/player.h"
 #include "mads/madsv2/core/sound.h"
 #include "mads/madsv2/core/text.h"
-#include "mads/madsv2/forest/journal.h"
-#include "mads/madsv2/forest/mads/inventory.h"
-#include "mads/madsv2/forest/mads/sounds.h"
-#include "mads/madsv2/forest/mads/words.h"
-#include "mads/madsv2/forest/global.h"
-#include "mads/madsv2/forest/rooms/section5.h"
-#include "mads/madsv2/forest/rooms/room503.h"
 
 namespace MADS {
 namespace MADSV2 {
diff --git a/engines/mads/madsv2/forest/rooms/room503.h b/engines/mads/madsv2/forest/rooms/room503.h
deleted file mode 100644
index e71795c42af..00000000000
--- a/engines/mads/madsv2/forest/rooms/room503.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/* 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 MADS_FOREST_ROOMS_503_H
-#define MADS_FOREST_ROOMS_503_H
-
-#include "common/serializer.h"
-#include "mads/madsv2/core/general.h"
-
-namespace MADS {
-namespace MADSV2 {
-namespace Forest {
-namespace Rooms {
-
-extern void room_503_preload();
-extern void room_503_synchronize(Common::Serializer &s);
-
-} // namespace Rooms
-} // namespace Forest
-} // namespace MADSV2
-} // namespace MADS
-
-#endif
diff --git a/engines/mads/madsv2/forest/rooms/room509.cpp b/engines/mads/madsv2/forest/rooms/room509.cpp
index c9bf05a440d..f68df862eb4 100644
--- a/engines/mads/madsv2/forest/rooms/room509.cpp
+++ b/engines/mads/madsv2/forest/rooms/room509.cpp
@@ -20,14 +20,13 @@
  */
 
 #include "mads/madsv2/forest/digi.h"
+#include "mads/madsv2/forest/global.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/kernel.h"
 #include "mads/madsv2/core/matte.h"
 #include "mads/madsv2/core/mouse.h"
 #include "mads/madsv2/core/player.h"
 #include "mads/madsv2/core/sprite.h"
-#include "mads/madsv2/forest/global.h"
-#include "mads/madsv2/forest/rooms/room509.h"
 
 namespace MADS {
 namespace MADSV2 {
diff --git a/engines/mads/madsv2/forest/rooms/room509.h b/engines/mads/madsv2/forest/rooms/room509.h
deleted file mode 100644
index b8db30bda3b..00000000000
--- a/engines/mads/madsv2/forest/rooms/room509.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/* 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 MADS_FOREST_ROOMS_509_H
-#define MADS_FOREST_ROOMS_509_H
-
-#include "common/serializer.h"
-#include "mads/madsv2/core/general.h"
-
-namespace MADS {
-namespace MADSV2 {
-namespace Forest {
-namespace Rooms {
-
-extern void room_509_preload();
-extern void room_509_synchronize(Common::Serializer &s);
-
-} // namespace Rooms
-} // namespace Forest
-} // namespace MADSV2
-} // namespace MADS
-
-#endif
diff --git a/engines/mads/madsv2/forest/rooms/room510.cpp b/engines/mads/madsv2/forest/rooms/room510.cpp
index 4626dbd6968..5148ec71cd2 100644
--- a/engines/mads/madsv2/forest/rooms/room510.cpp
+++ b/engines/mads/madsv2/forest/rooms/room510.cpp
@@ -19,19 +19,18 @@
  *
  */
 
-#include "mads/madsv2/core/config.h"
 #include "mads/madsv2/forest/digi.h"
+#include "mads/madsv2/forest/global.h"
+#include "mads/madsv2/forest/midi.h"
+#include "mads/madsv2/core/config.h"
 #include "mads/madsv2/core/font.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/kernel.h"
 #include "mads/madsv2/core/matte.h"
-#include "mads/madsv2/forest/midi.h"
 #include "mads/madsv2/core/mouse.h"
 #include "mads/madsv2/core/player.h"
 #include "mads/madsv2/core/quote.h"
 #include "mads/madsv2/core/sprite.h"
-#include "mads/madsv2/forest/global.h"
-#include "mads/madsv2/forest/rooms/room510.h"
 
 namespace MADS {
 namespace MADSV2 {
diff --git a/engines/mads/madsv2/forest/rooms/room510.h b/engines/mads/madsv2/forest/rooms/room510.h
deleted file mode 100644
index eecc96b3ced..00000000000
--- a/engines/mads/madsv2/forest/rooms/room510.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/* 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 MADS_FOREST_ROOMS_510_H
-#define MADS_FOREST_ROOMS_510_H
-
-#include "common/serializer.h"
-#include "mads/madsv2/core/general.h"
-
-namespace MADS {
-namespace MADSV2 {
-namespace Forest {
-namespace Rooms {
-
-extern void room_510_preload();
-extern void room_510_synchronize(Common::Serializer &s);
-
-} // namespace Rooms
-} // namespace Forest
-} // namespace MADSV2
-} // namespace MADS
-
-#endif
diff --git a/engines/mads/madsv2/forest/rooms/room520.cpp b/engines/mads/madsv2/forest/rooms/room520.cpp
index 3ff8a8239f0..d706697cfcc 100644
--- a/engines/mads/madsv2/forest/rooms/room520.cpp
+++ b/engines/mads/madsv2/forest/rooms/room520.cpp
@@ -20,14 +20,13 @@
  */
 
 #include "mads/madsv2/forest/digi.h"
+#include "mads/madsv2/forest/global.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/kernel.h"
 #include "mads/madsv2/core/matte.h"
 #include "mads/madsv2/core/mouse.h"
 #include "mads/madsv2/core/player.h"
 #include "mads/madsv2/core/sprite.h"
-#include "mads/madsv2/forest/global.h"
-#include "mads/madsv2/forest/rooms/room520.h"
 
 namespace MADS {
 namespace MADSV2 {
diff --git a/engines/mads/madsv2/forest/rooms/room520.h b/engines/mads/madsv2/forest/rooms/room520.h
deleted file mode 100644
index 4f77e8f3e7b..00000000000
--- a/engines/mads/madsv2/forest/rooms/room520.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/* 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 MADS_FOREST_ROOMS_520_H
-#define MADS_FOREST_ROOMS_520_H
-
-#include "common/serializer.h"
-#include "mads/madsv2/core/general.h"
-
-namespace MADS {
-namespace MADSV2 {
-namespace Forest {
-namespace Rooms {
-
-extern void room_520_preload();
-extern void room_520_synchronize(Common::Serializer &s);
-
-} // namespace Rooms
-} // namespace Forest
-} // namespace MADSV2
-} // namespace MADS
-
-#endif
diff --git a/engines/mads/madsv2/forest/rooms/room901.cpp b/engines/mads/madsv2/forest/rooms/room901.cpp
index e61409d1287..26e17a86a74 100644
--- a/engines/mads/madsv2/forest/rooms/room901.cpp
+++ b/engines/mads/madsv2/forest/rooms/room901.cpp
@@ -20,14 +20,13 @@
  */
 
 #include "common/config-manager.h"
+#include "mads/madsv2/forest/rooms/section9.h"
+#include "mads/madsv2/forest/forest.h"
+#include "mads/madsv2/forest/global.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/kernel.h"
 #include "mads/madsv2/core/matte.h"
 #include "mads/madsv2/core/mouse.h"
-#include "mads/madsv2/forest/rooms/section9.h"
-#include "mads/madsv2/forest/rooms/room901.h"
-#include "mads/madsv2/forest/global.h"
-#include "mads/madsv2/forest/forest.h"
 
 namespace MADS {
 namespace MADSV2 {
diff --git a/engines/mads/madsv2/forest/rooms/room901.h b/engines/mads/madsv2/forest/rooms/room901.h
deleted file mode 100644
index dc4c9893ad5..00000000000
--- a/engines/mads/madsv2/forest/rooms/room901.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/* 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 MADS_FOREST_ROOMS_901_H
-#define MADS_FOREST_ROOMS_901_H
-
-#include "common/serializer.h"
-#include "mads/madsv2/core/general.h"
-
-namespace MADS {
-namespace MADSV2 {
-namespace Forest {
-namespace Rooms {
-
-extern void room_901_preload();
-extern void room_901_synchronize(Common::Serializer &s);
-
-} // namespace Rooms
-} // namespace Forest
-} // namespace MADSV2
-} // namespace MADS
-
-#endif
diff --git a/engines/mads/madsv2/forest/rooms/room903.cpp b/engines/mads/madsv2/forest/rooms/room903.cpp
index af19e94920a..7e5927d6e5f 100644
--- a/engines/mads/madsv2/forest/rooms/room903.cpp
+++ b/engines/mads/madsv2/forest/rooms/room903.cpp
@@ -19,17 +19,15 @@
  *
  */
 
+#include "mads/madsv2/forest/rooms/section9.h"
+#include "mads/madsv2/forest/forest.h"
+#include "mads/madsv2/forest/global.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/imath.h"
 #include "mads/madsv2/core/inter.h"
 #include "mads/madsv2/core/kernel.h"
 #include "mads/madsv2/core/matte.h"
 #include "mads/madsv2/core/mouse.h"
-#include "mads/madsv2/forest/global.h"
-#include "mads/madsv2/forest/rooms/section9.h"
-#include "mads/madsv2/forest/rooms/room903.h"
-#include "mads/madsv2/forest/global.h"
-#include "mads/madsv2/forest/forest.h"
 
 namespace MADS {
 namespace MADSV2 {
diff --git a/engines/mads/madsv2/forest/rooms/room903.h b/engines/mads/madsv2/forest/rooms/room903.h
deleted file mode 100644
index 6b49d960006..00000000000
--- a/engines/mads/madsv2/forest/rooms/room903.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/* 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 MADS_FOREST_ROOMS_903_H
-#define MADS_FOREST_ROOMS_903_H
-
-#include "common/serializer.h"
-#include "mads/madsv2/core/general.h"
-
-namespace MADS {
-namespace MADSV2 {
-namespace Forest {
-namespace Rooms {
-
-extern void room_903_preload();
-extern void room_903_synchronize(Common::Serializer &s);
-
-} // namespace Rooms
-} // namespace Forest
-} // namespace MADSV2
-} // namespace MADS
-
-#endif
diff --git a/engines/mads/madsv2/forest/rooms/room904.cpp b/engines/mads/madsv2/forest/rooms/room904.cpp
index 52fcc246db8..46d77f28b34 100644
--- a/engines/mads/madsv2/forest/rooms/room904.cpp
+++ b/engines/mads/madsv2/forest/rooms/room904.cpp
@@ -20,17 +20,16 @@
  */
 
 #include "common/config-manager.h"
+#include "mads/madsv2/forest/rooms/section9.h"
+#include "mads/madsv2/forest/global.h"
+#include "mads/madsv2/forest/midi.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/global.h"
 #include "mads/madsv2/core/kernel.h"
 #include "mads/madsv2/core/matte.h"
-#include "mads/madsv2/forest/midi.h"
 #include "mads/madsv2/core/mouse.h"
 #include "mads/madsv2/core/object.h"
 #include "mads/madsv2/core/player.h"
-#include "mads/madsv2/forest/global.h"
-#include "mads/madsv2/forest/rooms/section9.h"
-#include "mads/madsv2/forest/rooms/room904.h"
 #include "mads/madsv2/engine.h"
 
 namespace MADS {
diff --git a/engines/mads/madsv2/forest/rooms/room904.h b/engines/mads/madsv2/forest/rooms/room904.h
deleted file mode 100644
index 3cb43458ab3..00000000000
--- a/engines/mads/madsv2/forest/rooms/room904.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/* 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 MADS_FOREST_ROOMS_904_H
-#define MADS_FOREST_ROOMS_904_H
-
-#include "common/serializer.h"
-#include "mads/madsv2/core/general.h"
-
-namespace MADS {
-namespace MADSV2 {
-namespace Forest {
-namespace Rooms {
-
-extern void room_904_preload();
-extern void room_904_synchronize(Common::Serializer &s);
-
-} // namespace Rooms
-} // namespace Forest
-} // namespace MADSV2
-} // namespace MADS
-
-#endif



From noreply at scummvm.org  Sun Jun 21 08:39:04 2026
From: noreply at scummvm.org (lotharsm)
Date: Sun, 21 Jun 2026 08:39:04 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm-web master ->
 37d50161550e061200681ab8800aea27e079c13f
Message-ID: <20260621083904.540EF7005CD@ron.scummvm.net>

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

Summary:
37d5016155 DATA: Update data files


Commit: 37d50161550e061200681ab8800aea27e079c13f
    https://github.com/scummvm/scummvm-web/commit/37d50161550e061200681ab8800aea27e079c13f
Author: lotharsm (mail at serra.me)
Date: 2026-06-21T08:38:59Z

Commit Message:
DATA: Update data files

Changed paths:
    data/en/scummvm_downloads.yaml


diff --git a/data/en/scummvm_downloads.yaml b/data/en/scummvm_downloads.yaml
index 08192f4b..7c0cd484 100644
--- a/data/en/scummvm_downloads.yaml
+++ b/data/en/scummvm_downloads.yaml
@@ -82,10 +82,10 @@
 -
     enabled: true
     category: scummvm
-    subcategory: old
+    subcategory: release
     category_icon: flatpak
     user_agent: ''
-    version: 2026.2.0
+    version: 2026.3.0
     url: 'https://flathub.org/apps/details/org.scummvm.ScummVM'
     name: 'Flatpak package hosted at the flathub.org repository'
     notes: '(external link)'



From noreply at scummvm.org  Sun Jun 21 09:20:11 2026
From: noreply at scummvm.org (antoniou79)
Date: Sun, 21 Jun 2026 09:20:11 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 cfbe5c2fa14d779a286085ff1feae5856d6b4475
Message-ID: <20260621092011.237927005CD@ron.scummvm.net>

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

Summary:
cfbe5c2fa1 DISTS: ANDROID: New versionCodes for 2026.3.1


Commit: cfbe5c2fa14d779a286085ff1feae5856d6b4475
    https://github.com/scummvm/scummvm/commit/cfbe5c2fa14d779a286085ff1feae5856d6b4475
Author: antoniou79 (a.antoniou79 at gmail.com)
Date: 2026-06-21T12:19:57+03:00

Commit Message:
DISTS: ANDROID: New versionCodes for 2026.3.1

Changed paths:
    dists/android/build.gradle


diff --git a/dists/android/build.gradle b/dists/android/build.gradle
index 39050d5e1a5..308ab0b9024 100644
--- a/dists/android/build.gradle
+++ b/dists/android/build.gradle
@@ -33,8 +33,8 @@ android {
         // R8 configuration files.
         proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), srcProperties['srcdir'] + '/dists/android/proguard-rules.pro'
 
-        versionName "2026.2.1"
-        versionCode 26021000
+        versionName "2026.3.1"
+        versionCode 26031000
         // versioncode must be incremented for each market upload
         // maximum versioncode allowed by Play Store is: 2100000000
         // After 2.9.1.1 release the versioncode numbering scheme changed to reflect the post-2026 naming convention for ScummVM versions.
@@ -50,6 +50,7 @@ android {
         // Historical version codes (for releases):
         // SCHEME C (2026.1.0 and later)
         // -------------------
+        // ScummVM 2026.3.0: 26030000 - 26030004 (unspec/fat, arm-v7a, arm64-v8a, x86, x86_64 respectively)
         // ScummVM 2026.2.0: 26020000 - 26020004 (unspec/fat, arm-v7a, arm64-v8a, x86, x86_64 respectively)
         // ScummVM 2026.1.0: 26010000 - 26010004 (unspec/fat, arm-v7a, arm64-v8a, x86, x86_64 respectively)
         //



From noreply at scummvm.org  Sun Jun 21 09:20:57 2026
From: noreply at scummvm.org (lotharsm)
Date: Sun, 21 Jun 2026 09:20:57 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm-web master ->
 d8fbba24fed88f6d1247763a76b20820cdd2f17b
Message-ID: <20260621092057.489467005CD@ron.scummvm.net>

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

Summary:
d8fbba24fe DATA: Update data files


Commit: d8fbba24fed88f6d1247763a76b20820cdd2f17b
    https://github.com/scummvm/scummvm-web/commit/d8fbba24fed88f6d1247763a76b20820cdd2f17b
Author: lotharsm (mail at serra.me)
Date: 2026-06-21T09:20:53Z

Commit Message:
DATA: Update data files

Changed paths:
    data/en/scummvm_downloads.yaml


diff --git a/data/en/scummvm_downloads.yaml b/data/en/scummvm_downloads.yaml
index 7c0cd484..3da785af 100644
--- a/data/en/scummvm_downloads.yaml
+++ b/data/en/scummvm_downloads.yaml
@@ -462,50 +462,50 @@
 -
     enabled: true
     category: scummvm
-    subcategory: old
+    subcategory: release
     category_icon: android
     user_agent: ''
-    version: 2026.2.0
+    version: 2026.3.0
     url: 'scummvm-{$version}-android-armeabi-v7a.apk'
     name: 'Android ARMv7a package'
     notes: ''
 -
     enabled: true
     category: scummvm
-    subcategory: old
+    subcategory: release
     category_icon: android
     user_agent: ''
-    version: 2026.2.0
+    version: 2026.3.0
     url: 'scummvm-{$version}-android-armeabi-v7a-nn.apk'
     name: 'Android ARMv7a (without ARM Neon) package'
     notes: ''
 -
     enabled: true
     category: scummvm
-    subcategory: old
+    subcategory: release
     category_icon: android
     user_agent: ''
-    version: 2026.2.0
+    version: 2026.3.0
     url: 'scummvm-{$version}-android-arm64-v8a.apk'
     name: 'Android ARM64v8a package'
     notes: ''
 -
     enabled: true
     category: scummvm
-    subcategory: old
+    subcategory: release
     category_icon: android
     user_agent: ''
-    version: 2026.2.0
+    version: 2026.3.0
     url: 'scummvm-{$version}-android-x86.apk'
     name: 'Android x86 32bit package'
     notes: ''
 -
     enabled: true
     category: scummvm
-    subcategory: old
+    subcategory: release
     category_icon: android
     user_agent: ''
-    version: 2026.2.0
+    version: 2026.3.0
     url: 'scummvm-{$version}-android-x86_64.apk'
     name: 'Android x86 64bit package'
     notes: ''



From noreply at scummvm.org  Sun Jun 21 09:22:49 2026
From: noreply at scummvm.org (lephilousophe)
Date: Sun, 21 Jun 2026 09:22:49 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm-web master ->
 e140a3efa7f74ae460680ac76a5f806eeae12d81
Message-ID: <20260621092249.EA2837005CD@ron.scummvm.net>

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

Summary:
e140a3efa7 Bump guzzlehttp/guzzle from 7.11.1 to 7.12.1


Commit: e140a3efa7f74ae460680ac76a5f806eeae12d81
    https://github.com/scummvm/scummvm-web/commit/e140a3efa7f74ae460680ac76a5f806eeae12d81
Author: dependabot[bot] (49699333+dependabot[bot]@users.noreply.github.com)
Date: 2026-06-21T11:22:46+02:00

Commit Message:
Bump guzzlehttp/guzzle from 7.11.1 to 7.12.1

Bumps [guzzlehttp/guzzle](https://github.com/guzzle/guzzle) from 7.11.1 to 7.12.1.
- [Release notes](https://github.com/guzzle/guzzle/releases)
- [Changelog](https://github.com/guzzle/guzzle/blob/7.12/CHANGELOG.md)
- [Commits](https://github.com/guzzle/guzzle/compare/7.11.1...7.12.1)

---
updated-dependencies:
- dependency-name: guzzlehttp/guzzle
  dependency-version: 7.12.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support at github.com>

Changed paths:
    composer.lock


diff --git a/composer.lock b/composer.lock
index 5aaa8496..f6f8bd30 100644
--- a/composer.lock
+++ b/composer.lock
@@ -183,22 +183,22 @@
         },
         {
             "name": "guzzlehttp/guzzle",
-            "version": "7.11.1",
+            "version": "7.12.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/guzzle/guzzle.git",
-                "reference": "5af96f374e0ab4ebd747b8310888c99d3adb0a8c"
+                "reference": "d34627490fbc03bf5c5d7cfed81f2faa19519425"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/guzzle/guzzle/zipball/5af96f374e0ab4ebd747b8310888c99d3adb0a8c",
-                "reference": "5af96f374e0ab4ebd747b8310888c99d3adb0a8c",
+                "url": "https://api.github.com/repos/guzzle/guzzle/zipball/d34627490fbc03bf5c5d7cfed81f2faa19519425",
+                "reference": "d34627490fbc03bf5c5d7cfed81f2faa19519425",
                 "shasum": ""
             },
             "require": {
                 "ext-json": "*",
                 "guzzlehttp/promises": "^2.5",
-                "guzzlehttp/psr7": "^2.11",
+                "guzzlehttp/psr7": "^2.12.1",
                 "php": "^7.2.5 || ^8.0",
                 "psr/http-client": "^1.0",
                 "symfony/deprecation-contracts": "^2.5 || ^3.0",
@@ -211,7 +211,7 @@
                 "bamarni/composer-bin-plugin": "^1.8.2",
                 "ext-curl": "*",
                 "guzzle/client-integration-tests": "3.0.2",
-                "guzzlehttp/test-server": "^0.5",
+                "guzzlehttp/test-server": "^0.5.1",
                 "php-http/message-factory": "^1.1",
                 "phpunit/phpunit": "^8.5.52 || ^9.6.34",
                 "psr/log": "^1.1 || ^2.0 || ^3.0"
@@ -291,7 +291,7 @@
             ],
             "support": {
                 "issues": "https://github.com/guzzle/guzzle/issues",
-                "source": "https://github.com/guzzle/guzzle/tree/7.11.1"
+                "source": "https://github.com/guzzle/guzzle/tree/7.12.1"
             },
             "funding": [
                 {
@@ -307,7 +307,7 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2026-06-07T22:54:06+00:00"
+            "time": "2026-06-18T14:12:49+00:00"
         },
         {
             "name": "guzzlehttp/promises",
@@ -395,16 +395,16 @@
         },
         {
             "name": "guzzlehttp/psr7",
-            "version": "2.11.0",
+            "version": "2.12.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/guzzle/psr7.git",
-                "reference": "bbb5e61349fa5cb822b3e87842b951088b76b81f"
+                "reference": "172ef2f4e9824c1e058b7f30be8ae25a02c0f2b7"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/guzzle/psr7/zipball/bbb5e61349fa5cb822b3e87842b951088b76b81f",
-                "reference": "bbb5e61349fa5cb822b3e87842b951088b76b81f",
+                "url": "https://api.github.com/repos/guzzle/psr7/zipball/172ef2f4e9824c1e058b7f30be8ae25a02c0f2b7",
+                "reference": "172ef2f4e9824c1e058b7f30be8ae25a02c0f2b7",
                 "shasum": ""
             },
             "require": {
@@ -494,7 +494,7 @@
             ],
             "support": {
                 "issues": "https://github.com/guzzle/psr7/issues",
-                "source": "https://github.com/guzzle/psr7/tree/2.11.0"
+                "source": "https://github.com/guzzle/psr7/tree/2.12.1"
             },
             "funding": [
                 {
@@ -510,7 +510,7 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2026-06-02T12:30:48+00:00"
+            "time": "2026-06-18T09:49:37+00:00"
         },
         {
             "name": "league/csv",



From noreply at scummvm.org  Sun Jun 21 09:23:15 2026
From: noreply at scummvm.org (lephilousophe)
Date: Sun, 21 Jun 2026 09:23:15 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm-web master ->
 e454929573f0d568167820e0c1f87d7860b30e2b
Message-ID: <20260621092315.4B94B7005CD@ron.scummvm.net>

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

Summary:
e454929573 Bump ts-loader


Commit: e454929573f0d568167820e0c1f87d7860b30e2b
    https://github.com/scummvm/scummvm-web/commit/e454929573f0d568167820e0c1f87d7860b30e2b
Author: dependabot[bot] (49699333+dependabot[bot]@users.noreply.github.com)
Date: 2026-06-21T11:23:12+02:00

Commit Message:
Bump ts-loader

Bumps the patch-releases group with 1 update in the /dumper-companion directory: [ts-loader](https://github.com/TypeStrong/ts-loader).


Updates `ts-loader` from 9.6.0 to 9.6.1
- [Release notes](https://github.com/TypeStrong/ts-loader/releases)
- [Changelog](https://github.com/TypeStrong/ts-loader/blob/main/CHANGELOG.md)
- [Commits](https://github.com/TypeStrong/ts-loader/compare/v9.6.0...v9.6.1)

---
updated-dependencies:
- dependency-name: ts-loader
  dependency-version: 9.6.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: patch-releases
...

Signed-off-by: dependabot[bot] <support at github.com>

Changed paths:
    dumper-companion/package-lock.json
    dumper-companion/package.json


diff --git a/dumper-companion/package-lock.json b/dumper-companion/package-lock.json
index e7324deb..835d51c1 100644
--- a/dumper-companion/package-lock.json
+++ b/dumper-companion/package-lock.json
@@ -12,7 +12,7 @@
         "@zip.js/zip.js": "^2.8.26",
         "preact": "^10.29.1",
         "punycode": "^2.3.1",
-        "ts-loader": "^9.5.7",
+        "ts-loader": "^9.6.1",
         "webpack": "^5.107.1",
         "webpack-cli": "^7.0.2"
       },
@@ -4751,9 +4751,9 @@
       }
     },
     "node_modules/ts-loader": {
-      "version": "9.6.0",
-      "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.6.0.tgz",
-      "integrity": "sha512-dsJO0S+T7grTDWTc4a0nTygXGjKncVUpx8Y+af8EvI/D5WgTJby5UEk5eoMCB9EcLQmnvitqh99MqtjtHgAwFQ==",
+      "version": "9.6.1",
+      "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.6.1.tgz",
+      "integrity": "sha512-8FMHnmxtpncUAu0ZjkqpXnOTlwc9eY95esH8WVN94guTPPdkg2ofVdiVM5j8L2lmjiGerXd56zXb/D2JyVQPLg==",
       "license": "MIT",
       "dependencies": {
         "chalk": "^4.1.0",
diff --git a/dumper-companion/package.json b/dumper-companion/package.json
index 90e97964..bd52c0dd 100644
--- a/dumper-companion/package.json
+++ b/dumper-companion/package.json
@@ -11,7 +11,7 @@
     "preact": "^10.29.1",
     "punycode": "^2.3.1",
     "@types/punycode": "^2.1.4",
-    "ts-loader": "^9.5.7",
+    "ts-loader": "^9.6.1",
     "webpack": "^5.107.1",
     "webpack-cli": "^7.0.2"
   },



From noreply at scummvm.org  Sun Jun 21 09:24:04 2026
From: noreply at scummvm.org (lephilousophe)
Date: Sun, 21 Jun 2026 09:24:04 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm-web master ->
 05d35d6ce5c7a4f8b7771f8ecfa3e6892cb5665f
Message-ID: <20260621092404.A0DC47005CD@ron.scummvm.net>

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

Summary:
3bff642a7d WEB: Update packages
05d35d6ce5 WEB: Use @composer


Commit: 3bff642a7d2d9de2e69c63361b033bedb12374d8
    https://github.com/scummvm/scummvm-web/commit/3bff642a7d2d9de2e69c63361b033bedb12374d8
Author: Le Philousophe (lephilousophe at users.noreply.github.com)
Date: 2026-06-21T11:23:40+02:00

Commit Message:
WEB: Update packages

Changed paths:
    composer.lock
    dumper-companion/package-lock.json
    dumper-companion/package.json
    package-lock.json
    package.json


diff --git a/composer.lock b/composer.lock
index f6f8bd30..d6de3c81 100644
--- a/composer.lock
+++ b/composer.lock
@@ -2183,16 +2183,16 @@
         },
         {
             "name": "symfony/polyfill-mbstring",
-            "version": "v1.38.1",
+            "version": "v1.38.2",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/polyfill-mbstring.git",
-                "reference": "14c5439eec4ccff081ac14eca2dc57feb2a66d92"
+                "reference": "d3d318bad5e7a1bfbd026009c8bfb8d8f99ae6b6"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/14c5439eec4ccff081ac14eca2dc57feb2a66d92",
-                "reference": "14c5439eec4ccff081ac14eca2dc57feb2a66d92",
+                "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/d3d318bad5e7a1bfbd026009c8bfb8d8f99ae6b6",
+                "reference": "d3d318bad5e7a1bfbd026009c8bfb8d8f99ae6b6",
                 "shasum": ""
             },
             "require": {
@@ -2244,7 +2244,7 @@
                 "shim"
             ],
             "support": {
-                "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.38.1"
+                "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.38.2"
             },
             "funding": [
                 {
@@ -2264,7 +2264,7 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2026-05-26T12:51:13+00:00"
+            "time": "2026-05-27T06:59:30+00:00"
         },
         {
             "name": "symfony/polyfill-php80",
@@ -2352,16 +2352,16 @@
         },
         {
             "name": "symfony/polyfill-php83",
-            "version": "v1.38.1",
+            "version": "v1.38.2",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/polyfill-php83.git",
-                "reference": "8339098cae28673c15cce00d80734af0453054e2"
+                "reference": "796a26abb75ce49f3a84433cd81bf1009d73d5f8"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/8339098cae28673c15cce00d80734af0453054e2",
-                "reference": "8339098cae28673c15cce00d80734af0453054e2",
+                "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/796a26abb75ce49f3a84433cd81bf1009d73d5f8",
+                "reference": "796a26abb75ce49f3a84433cd81bf1009d73d5f8",
                 "shasum": ""
             },
             "require": {
@@ -2408,7 +2408,7 @@
                 "shim"
             ],
             "support": {
-                "source": "https://github.com/symfony/polyfill-php83/tree/v1.38.1"
+                "source": "https://github.com/symfony/polyfill-php83/tree/v1.38.2"
             },
             "funding": [
                 {
@@ -2428,7 +2428,7 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2026-05-26T12:51:13+00:00"
+            "time": "2026-05-27T06:51:48+00:00"
         },
         {
             "name": "symfony/service-contracts",
@@ -3131,5 +3131,5 @@
     "platform-overrides": {
         "php": "8.2.0"
     },
-    "plugin-api-version": "2.9.0"
+    "plugin-api-version": "2.6.0"
 }
diff --git a/dumper-companion/package-lock.json b/dumper-companion/package-lock.json
index 835d51c1..9dad2ca4 100644
--- a/dumper-companion/package-lock.json
+++ b/dumper-companion/package-lock.json
@@ -10,19 +10,19 @@
       "dependencies": {
         "@types/punycode": "^2.1.4",
         "@zip.js/zip.js": "^2.8.26",
-        "preact": "^10.29.1",
+        "preact": "^10.29.2",
         "punycode": "^2.3.1",
         "ts-loader": "^9.6.1",
-        "webpack": "^5.107.1",
-        "webpack-cli": "^7.0.2"
+        "webpack": "^5.107.2",
+        "webpack-cli": "^7.0.3"
       },
       "devDependencies": {
         "@eslint/js": "^9.39.4",
         "@stylistic/eslint-plugin": "^5.10.0",
         "eslint": "^9.39.4",
         "eslint-config-preact": "^2.0.0",
-        "globals": "^17.5.0",
-        "typescript-eslint": "^8.58.2"
+        "globals": "^17.6.0",
+        "typescript-eslint": "^8.61.1"
       }
     },
     "node_modules/@babel/code-frame": {
@@ -696,12 +696,12 @@
       "license": "MIT"
     },
     "node_modules/@types/node": {
-      "version": "25.9.2",
-      "resolved": "https://registry.npmjs.org/@types/node/-/node-25.9.2.tgz",
-      "integrity": "sha512-G05zqtJhcDLb8uslf5EjCxXg9G1KQxiV8OS0R26IC//Eoyitzqe8z37I7cqvnZlrlSfgocQRfSn/AHBZJJFyGw==",
+      "version": "26.0.0",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-26.0.0.tgz",
+      "integrity": "sha512-vf2YFi1iY9lHGwNJMs01biZFbKJkrZR1T6/MlzjhJLPdntOHLhTrDSnSVcdtvjihi4VQNlrFRIxLsDBlQpAipA==",
       "license": "MIT",
       "dependencies": {
-        "undici-types": ">=7.24.0 <7.24.7"
+        "undici-types": "~8.3.0"
       }
     },
     "node_modules/@types/punycode": {
@@ -711,17 +711,17 @@
       "license": "MIT"
     },
     "node_modules/@typescript-eslint/eslint-plugin": {
-      "version": "8.60.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.60.1.tgz",
-      "integrity": "sha512-JQ4S5GB0tfjO8BuJ4fcX+HodkzJjYBV+7OJ+wLygaX7OGQ7FudyHL4NSCA6ob+w3Yn+5MkKIozOwQhXeM7opVg==",
+      "version": "8.61.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.61.1.tgz",
+      "integrity": "sha512-ZPlVl3PB3et/59Ne0fv/sci6ZXz4T4Hp4nTJ56i/Y0gR89ARb+KphojTq6j+56E5PIezmOIOOWyY+aWQFd+IkQ==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
         "@eslint-community/regexpp": "^4.12.2",
-        "@typescript-eslint/scope-manager": "8.60.1",
-        "@typescript-eslint/type-utils": "8.60.1",
-        "@typescript-eslint/utils": "8.60.1",
-        "@typescript-eslint/visitor-keys": "8.60.1",
+        "@typescript-eslint/scope-manager": "8.61.1",
+        "@typescript-eslint/type-utils": "8.61.1",
+        "@typescript-eslint/utils": "8.61.1",
+        "@typescript-eslint/visitor-keys": "8.61.1",
         "ignore": "^7.0.5",
         "natural-compare": "^1.4.0",
         "ts-api-utils": "^2.5.0"
@@ -734,7 +734,7 @@
         "url": "https://opencollective.com/typescript-eslint"
       },
       "peerDependencies": {
-        "@typescript-eslint/parser": "^8.60.1",
+        "@typescript-eslint/parser": "^8.61.1",
         "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
         "typescript": ">=4.8.4 <6.1.0"
       }
@@ -750,16 +750,16 @@
       }
     },
     "node_modules/@typescript-eslint/parser": {
-      "version": "8.60.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.60.1.tgz",
-      "integrity": "sha512-A0M6ua6H252bVjPvvtSgl2QA4+ET9S5Mtkb2GDyTxIhH/C4qDItT7RQNO5PhMC6NXGYXOR9dIalcDDgBKT7oFA==",
+      "version": "8.61.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.61.1.tgz",
+      "integrity": "sha512-PJ5vePq5/ognBbrIcoC5+SHO5dfpeLPzP9FpLkzWrguoYQEeeSjlJpVwOpo1JRSTEi7dRcwNy4h4dzV70PqHcg==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@typescript-eslint/scope-manager": "8.60.1",
-        "@typescript-eslint/types": "8.60.1",
-        "@typescript-eslint/typescript-estree": "8.60.1",
-        "@typescript-eslint/visitor-keys": "8.60.1",
+        "@typescript-eslint/scope-manager": "8.61.1",
+        "@typescript-eslint/types": "8.61.1",
+        "@typescript-eslint/typescript-estree": "8.61.1",
+        "@typescript-eslint/visitor-keys": "8.61.1",
         "debug": "^4.4.3"
       },
       "engines": {
@@ -775,14 +775,14 @@
       }
     },
     "node_modules/@typescript-eslint/project-service": {
-      "version": "8.60.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.60.1.tgz",
-      "integrity": "sha512-eXkTH2bxmXlqD1RnOPmLZ9ZM9D3VwSx04JOwBnP9RQ+yUA5a2Mu7SfW8uaV2Aon53NJzZlZYuX7tn91Izf+xaw==",
+      "version": "8.61.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.61.1.tgz",
+      "integrity": "sha512-PrC4JYGmR241lYnfhmKGTXkFqv8+ymbTFgSAY0fVXpY82/QkMw5TZPl+vGzuDDU2QYJk9fIDOBTntF+yDv9LEA==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@typescript-eslint/tsconfig-utils": "^8.60.1",
-        "@typescript-eslint/types": "^8.60.1",
+        "@typescript-eslint/tsconfig-utils": "^8.61.1",
+        "@typescript-eslint/types": "^8.61.1",
         "debug": "^4.4.3"
       },
       "engines": {
@@ -797,14 +797,14 @@
       }
     },
     "node_modules/@typescript-eslint/scope-manager": {
-      "version": "8.60.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.60.1.tgz",
-      "integrity": "sha512-gvI5OQoptnxQnchOirukCuQ55svJSTuD/4k5+pC267xyBtYry748R9/c3tYUzb/iE6RZfllRz2lVulLCHkTm4w==",
+      "version": "8.61.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.61.1.tgz",
+      "integrity": "sha512-L2bdIeoQS8FlKAvONAr20w6OcLXeB+qiDKbAooS9A0Ben+iSIkBef0FxqwKWYqt5sa0i4KJtxVyVmhMylKzF5w==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@typescript-eslint/types": "8.60.1",
-        "@typescript-eslint/visitor-keys": "8.60.1"
+        "@typescript-eslint/types": "8.61.1",
+        "@typescript-eslint/visitor-keys": "8.61.1"
       },
       "engines": {
         "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -815,9 +815,9 @@
       }
     },
     "node_modules/@typescript-eslint/tsconfig-utils": {
-      "version": "8.60.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.60.1.tgz",
-      "integrity": "sha512-nh8w4qAteiKuZu3pSSzG/yGKpw0OlkrKnzFmbVRenKaD4qc+7i1GrmZaLVkr8rk4uipiPGMOW4YsM6WmKZ5CvA==",
+      "version": "8.61.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.61.1.tgz",
+      "integrity": "sha512-UN/H4di+OO7EWx2ovME+8t31YO+KVnK0RRKEHR3kOt21/Ay8BOq3M1OMvWs5vNiqcFCYGYoxK3MXPZzmMUE+yg==",
       "dev": true,
       "license": "MIT",
       "engines": {
@@ -832,15 +832,15 @@
       }
     },
     "node_modules/@typescript-eslint/type-utils": {
-      "version": "8.60.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.60.1.tgz",
-      "integrity": "sha512-sdwTrpjosW7ANQYJ39ZBF1ZyEMEGVB2UsikrserVM/30a/F1dTLnu9bGxEdosugyu5caigjLrR2qiD11asjI1A==",
+      "version": "8.61.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.61.1.tgz",
+      "integrity": "sha512-GYRicKmVK0C4fsKgaACaknOUAq9Oa2kwsjnpFhFcS/5p4Ht5IP9OVLbgIgcK4SRk92nVHFluurg1lumD9dBcLw==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@typescript-eslint/types": "8.60.1",
-        "@typescript-eslint/typescript-estree": "8.60.1",
-        "@typescript-eslint/utils": "8.60.1",
+        "@typescript-eslint/types": "8.61.1",
+        "@typescript-eslint/typescript-estree": "8.61.1",
+        "@typescript-eslint/utils": "8.61.1",
         "debug": "^4.4.3",
         "ts-api-utils": "^2.5.0"
       },
@@ -857,9 +857,9 @@
       }
     },
     "node_modules/@typescript-eslint/types": {
-      "version": "8.60.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.60.1.tgz",
-      "integrity": "sha512-4h0tY8ppCkdCzcrl2YM5M3my0xsE1Tf8om3owEu5oPWmXwkKRmk0j0LGDzYBGUcAlesEbxBhazqu/K4cu3Ug7w==",
+      "version": "8.61.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.61.1.tgz",
+      "integrity": "sha512-G+CRlPqLv7Bz1IZVs03x5K59F1veqL0EJUROAdGhKsEq8qOiRiZbI+HUojPq5l0fEGOKModD9br6lObhB8zkoA==",
       "dev": true,
       "license": "MIT",
       "engines": {
@@ -871,16 +871,16 @@
       }
     },
     "node_modules/@typescript-eslint/typescript-estree": {
-      "version": "8.60.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.60.1.tgz",
-      "integrity": "sha512-alpRkfG8hlVE5kdJW2GkfgDgXxold3e8e4l6EnmhRmRLbekgAPCCGDVD++sABy9FcgPFroq+uFcCSM1vR57Cew==",
+      "version": "8.61.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.61.1.tgz",
+      "integrity": "sha512-u+oQD3BqYWPc8YV9Zab4vaJElJuwOLPRc10Jm1o/qS+6Qwen14HCWwx0Seo4LnSn2wxea2Ik8DxPt2/FHmuhrg==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@typescript-eslint/project-service": "8.60.1",
-        "@typescript-eslint/tsconfig-utils": "8.60.1",
-        "@typescript-eslint/types": "8.60.1",
-        "@typescript-eslint/visitor-keys": "8.60.1",
+        "@typescript-eslint/project-service": "8.61.1",
+        "@typescript-eslint/tsconfig-utils": "8.61.1",
+        "@typescript-eslint/types": "8.61.1",
+        "@typescript-eslint/visitor-keys": "8.61.1",
         "debug": "^4.4.3",
         "minimatch": "^10.2.2",
         "semver": "^7.7.3",
@@ -938,9 +938,9 @@
       }
     },
     "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": {
-      "version": "7.8.2",
-      "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.2.tgz",
-      "integrity": "sha512-c8jsqUZm3omBOI66G90z1Dyw5z622G8oLG+omfsHBJf3CWQTlOcwOjvOG6wtiNfW6anKm/eA39LMwMtMez2TiQ==",
+      "version": "7.8.5",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz",
+      "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==",
       "dev": true,
       "license": "ISC",
       "bin": {
@@ -951,16 +951,16 @@
       }
     },
     "node_modules/@typescript-eslint/utils": {
-      "version": "8.60.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.60.1.tgz",
-      "integrity": "sha512-h2MPBLoNtjc3qZWfY3Tl51yPorQ2McHn8pJfcMNTcIvrrZrr90Ykffit0yjrPFWQcRcUxzH20+6OcVdW4yHtUg==",
+      "version": "8.61.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.61.1.tgz",
+      "integrity": "sha512-1+P/3Dj6jvtybE1q0HQ6yBt/gq+oKJyLdEv4HdnqasaEXRSYCAsD59mXEVQnM/ULNdQxbX77tdG4jPRjIS6knA==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
         "@eslint-community/eslint-utils": "^4.9.1",
-        "@typescript-eslint/scope-manager": "8.60.1",
-        "@typescript-eslint/types": "8.60.1",
-        "@typescript-eslint/typescript-estree": "8.60.1"
+        "@typescript-eslint/scope-manager": "8.61.1",
+        "@typescript-eslint/types": "8.61.1",
+        "@typescript-eslint/typescript-estree": "8.61.1"
       },
       "engines": {
         "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -975,13 +975,13 @@
       }
     },
     "node_modules/@typescript-eslint/visitor-keys": {
-      "version": "8.60.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.60.1.tgz",
-      "integrity": "sha512-EbGRQg4FhrmwLodl+t3JNAnXHWVr9Vp+Zl1QBZVPY4ByfkzIT8cX3K6QWODHtkIZqqJVEWvhHSx3v5PDHsaQag==",
+      "version": "8.61.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.61.1.tgz",
+      "integrity": "sha512-6fJ9MHWtK14C1DSkiMlHUSOmrVebL7150xZJBlJiL62jjhIA4JmOq6flwBgDxIdBKKdoiZRel+dfPD5MLfny3w==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@typescript-eslint/types": "8.60.1",
+        "@typescript-eslint/types": "8.61.1",
         "eslint-visitor-keys": "^5.0.0"
       },
       "engines": {
@@ -1175,9 +1175,9 @@
       }
     },
     "node_modules/acorn": {
-      "version": "8.16.0",
-      "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz",
-      "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==",
+      "version": "8.17.0",
+      "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.17.0.tgz",
+      "integrity": "sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==",
       "license": "MIT",
       "bin": {
         "acorn": "bin/acorn"
@@ -1475,9 +1475,9 @@
       "license": "MIT"
     },
     "node_modules/baseline-browser-mapping": {
-      "version": "2.10.34",
-      "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.34.tgz",
-      "integrity": "sha512-IMDedajPifLnHNY0X9n8hKxRTQ6/eTHwr5bDo04WnuqxyKw6LYtQywCuuqPZwhl3aBXMvQpJov42GLCwRRdQzw==",
+      "version": "2.10.38",
+      "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.38.tgz",
+      "integrity": "sha512-31/02mVB4yuQU6adKk5SlY6m+mxDwUq5KZkyYgnLrrKl7TEm1+3PyDtDBz2kOv/wxZz41GHsvV1A/u6RmiyBvw==",
       "license": "Apache-2.0",
       "bin": {
         "baseline-browser-mapping": "dist/cli.cjs"
@@ -1609,9 +1609,9 @@
       }
     },
     "node_modules/caniuse-lite": {
-      "version": "1.0.30001797",
-      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001797.tgz",
-      "integrity": "sha512-l8xKG+gwAIExZGl9FrF7KUwuOmk6wbEPC9Xoy/RtnWv1XG0Q4LFlagaLpUv3Kiza3W/wm27zy0yWJEieYKAP6w==",
+      "version": "1.0.30001799",
+      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001799.tgz",
+      "integrity": "sha512-hG1bReV+OUU+MOqK4t/ZWI0tZOyz3rqS9XuhOUz1cIcbwBKjOyJEJuw9ER5JuNyqxNk8u/JUVbGibBOL1yrjFw==",
       "funding": [
         {
           "type": "opencollective",
@@ -1863,15 +1863,15 @@
       }
     },
     "node_modules/electron-to-chromium": {
-      "version": "1.5.368",
-      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.368.tgz",
-      "integrity": "sha512-7RckJJK4uESJF9PxvfMWd3TGqIiieUTG4HxnKaKuIpGbcr+r2ZEB3g2gAhCP3Fqm42vJSzLfgab9eva/C4/XVw==",
+      "version": "1.5.376",
+      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.376.tgz",
+      "integrity": "sha512-cUVA7/RvbFTEuw/i3obUwDTRIXojaxkResf+ibByPFxjc6XK3VNtcQXV0NSbAlJ0FMjcJGgftVVB4Qo184EXvA==",
       "license": "ISC"
     },
     "node_modules/enhanced-resolve": {
-      "version": "5.23.0",
-      "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.23.0.tgz",
-      "integrity": "sha512-yJN/BOOLxcOW2aQgeif9mSnaUB8KtvmMMp56oA1kx1CRfBKbhZm2pJ+NBY+3eOboHxix8lfjWpHE0Ei5U8RbSA==",
+      "version": "5.24.0",
+      "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.24.0.tgz",
+      "integrity": "sha512-SkE2t82KlkkxQRVMVLAGKxLfORGQfrkx5dkj+vlgXRVNEdPc4eZcR+J/Fvj8C+yKSFH5L0q3NFlyufOVQnCcYQ==",
       "license": "MIT",
       "dependencies": {
         "graceful-fs": "^4.2.4",
@@ -1962,6 +1962,25 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
+    "node_modules/es-abstract-get": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/es-abstract-get/-/es-abstract-get-1.0.0.tgz",
+      "integrity": "sha512-6PMWXpdhshVvFp+FoWYs1EvG1Nj0tvk0dZM+XcK0xMEM1czRVcP6ohqPWHy6qPagSpC8j4+p89WXlT+xXJs/fg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "es-errors": "^1.3.0",
+        "es-object-atoms": "^1.1.2",
+        "is-callable": "^1.2.7",
+        "object-inspect": "^1.13.4"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
     "node_modules/es-define-property": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
@@ -1982,9 +2001,9 @@
       }
     },
     "node_modules/es-iterator-helpers": {
-      "version": "1.3.2",
-      "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.3.2.tgz",
-      "integrity": "sha512-HVLACW1TppGYjJ8H6/jqH/pqOtKRw6wMlrB23xfExmFWxFquAIWCmwoLsOyN96K4a5KbmOf5At9ZUO3GZbetAw==",
+      "version": "1.3.3",
+      "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.3.3.tgz",
+      "integrity": "sha512-0PuBxFi+4uPanB97iDxCLWuHeYud2FALrw5HFZGtAF38UpJDbDC8frwp2cnDyae692CQ0dou60UwWfhgsa4U/g==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
@@ -2058,15 +2077,17 @@
       }
     },
     "node_modules/es-to-primitive": {
-      "version": "1.3.0",
-      "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz",
-      "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==",
+      "version": "1.3.1",
+      "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.1.tgz",
+      "integrity": "sha512-CxN9N56HYfd2m/acc/NOFrZQsN9kU4eh+2kk6A707Kz1krH8tKmfrs5RnftB8WNX80T0NS7vSQsDOlg23diR2g==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
+        "es-abstract-get": "^1.0.0",
+        "es-errors": "^1.3.0",
         "is-callable": "^1.2.7",
-        "is-date-object": "^1.0.5",
-        "is-symbol": "^1.0.4"
+        "is-date-object": "^1.1.0",
+        "is-symbol": "^1.1.1"
       },
       "engines": {
         "node": ">= 0.4"
@@ -2227,9 +2248,9 @@
       }
     },
     "node_modules/eslint-plugin-compat/node_modules/semver": {
-      "version": "7.8.2",
-      "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.2.tgz",
-      "integrity": "sha512-c8jsqUZm3omBOI66G90z1Dyw5z622G8oLG+omfsHBJf3CWQTlOcwOjvOG6wtiNfW6anKm/eA39LMwMtMez2TiQ==",
+      "version": "7.8.5",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz",
+      "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==",
       "dev": true,
       "license": "ISC",
       "bin": {
@@ -2538,18 +2559,21 @@
       }
     },
     "node_modules/function.prototype.name": {
-      "version": "1.1.8",
-      "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz",
-      "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==",
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.2.0.tgz",
+      "integrity": "sha512-jObKIik1P2QjPHP5nz5BaOtUlfgS0fWo8IUByNXkM+o+02sJOi94em77GwJKQSJ3gfPHdgzLNrHc1uokV4P/ew==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "call-bind": "^1.0.8",
-        "call-bound": "^1.0.3",
-        "define-properties": "^1.2.1",
+        "call-bind": "^1.0.9",
+        "call-bound": "^1.0.4",
+        "es-define-property": "^1.0.1",
+        "es-errors": "^1.3.0",
         "functions-have-names": "^1.2.3",
-        "hasown": "^2.0.2",
-        "is-callable": "^1.2.7"
+        "has-property-descriptors": "^1.0.2",
+        "hasown": "^2.0.4",
+        "is-callable": "^1.2.7",
+        "is-document.all": "^1.0.0"
       },
       "engines": {
         "node": ">= 0.4"
@@ -3019,6 +3043,22 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
+    "node_modules/is-document.all": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/is-document.all/-/is-document.all-1.0.0.tgz",
+      "integrity": "sha512-+XSoyS05OdBbhFuELhgTCpFNHkpBOJqtsZfUFFpe5QTw+9Sjbh8zitxhQkYAo6wV7e1Vb8cAPvpCk9jGam/82g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "call-bound": "^1.0.4"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
     "node_modules/is-extglob": {
       "version": "2.1.1",
       "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
@@ -3651,9 +3691,9 @@
       }
     },
     "node_modules/node-releases": {
-      "version": "2.0.47",
-      "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.47.tgz",
-      "integrity": "sha512-Uzmd6LXpouKo8EUK68IjH4+E01w/hXyV3R3g/geCJo+rXLNfh1xucB+LOzYEOQPSiUK3h/xZf0cQGcSsmyL2Og==",
+      "version": "2.0.48",
+      "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.48.tgz",
+      "integrity": "sha512-1uz8041X6LoI6ZSdZacM9lVY28vuzDlSKitnpbSNK0RfKoIJkX29NBPVEFXhnuSuEOA9Ww0xnPJ+ILWbGAv8DA==",
       "license": "MIT",
       "engines": {
         "node": ">=18"
@@ -4364,15 +4404,15 @@
       }
     },
     "node_modules/side-channel": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
-      "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==",
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz",
+      "integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
         "es-errors": "^1.3.0",
-        "object-inspect": "^1.13.3",
-        "side-channel-list": "^1.0.0",
+        "object-inspect": "^1.13.4",
+        "side-channel-list": "^1.0.1",
         "side-channel-map": "^1.0.1",
         "side-channel-weakmap": "^1.0.2"
       },
@@ -4777,9 +4817,9 @@
       }
     },
     "node_modules/ts-loader/node_modules/semver": {
-      "version": "7.8.2",
-      "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.2.tgz",
-      "integrity": "sha512-c8jsqUZm3omBOI66G90z1Dyw5z622G8oLG+omfsHBJf3CWQTlOcwOjvOG6wtiNfW6anKm/eA39LMwMtMez2TiQ==",
+      "version": "7.8.5",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz",
+      "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==",
       "license": "ISC",
       "bin": {
         "semver": "bin/semver.js"
@@ -4894,16 +4934,16 @@
       }
     },
     "node_modules/typescript-eslint": {
-      "version": "8.60.1",
-      "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.60.1.tgz",
-      "integrity": "sha512-6m5hkkRAp8lKvhVpcprAIn5KkehQEh+47oHH2VGnExEh7dhNxXlg6GPAOIu6TxbVQxhebrJDvjl3020ooiWCMA==",
+      "version": "8.61.1",
+      "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.61.1.tgz",
+      "integrity": "sha512-V7PayAfJokV3pEHgN7/v03D1SpujhRfQtYLbLIiBfDDncdg4PAiRBfoS4cnCANK4jmAPncczi59QO3afiXUlNw==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@typescript-eslint/eslint-plugin": "8.60.1",
-        "@typescript-eslint/parser": "8.60.1",
-        "@typescript-eslint/typescript-estree": "8.60.1",
-        "@typescript-eslint/utils": "8.60.1"
+        "@typescript-eslint/eslint-plugin": "8.61.1",
+        "@typescript-eslint/parser": "8.61.1",
+        "@typescript-eslint/typescript-estree": "8.61.1",
+        "@typescript-eslint/utils": "8.61.1"
       },
       "engines": {
         "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -4937,9 +4977,9 @@
       }
     },
     "node_modules/undici-types": {
-      "version": "7.24.6",
-      "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.24.6.tgz",
-      "integrity": "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==",
+      "version": "8.3.0",
+      "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-8.3.0.tgz",
+      "integrity": "sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==",
       "license": "MIT"
     },
     "node_modules/update-browserslist-db": {
@@ -4983,12 +5023,11 @@
       }
     },
     "node_modules/watchpack": {
-      "version": "2.5.1",
-      "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.5.1.tgz",
-      "integrity": "sha512-Zn5uXdcFNIA1+1Ei5McRd+iRzfhENPCe7LeABkJtNulSxjma+l7ltNx55BWZkRlwRnpOgHqxnjyaDgJnNXnqzg==",
+      "version": "2.5.2",
+      "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.5.2.tgz",
+      "integrity": "sha512-6i/00NBjP4yGPs+caKSyRfpTF/8Torsu0MOW3mMzIbhgISFder8i7xbqgHlLMwJrdiN8ndBV3UA1/AfzPSr+jg==",
       "license": "MIT",
       "dependencies": {
-        "glob-to-regexp": "^0.4.1",
         "graceful-fs": "^4.1.2"
       },
       "engines": {
diff --git a/dumper-companion/package.json b/dumper-companion/package.json
index bd52c0dd..13fe2bc9 100644
--- a/dumper-companion/package.json
+++ b/dumper-companion/package.json
@@ -8,19 +8,19 @@
   },
   "dependencies": {
     "@zip.js/zip.js": "^2.8.26",
-    "preact": "^10.29.1",
+    "preact": "^10.29.2",
     "punycode": "^2.3.1",
     "@types/punycode": "^2.1.4",
     "ts-loader": "^9.6.1",
-    "webpack": "^5.107.1",
-    "webpack-cli": "^7.0.2"
+    "webpack": "^5.107.2",
+    "webpack-cli": "^7.0.3"
   },
   "devDependencies": {
     "@eslint/js": "^9.39.4",
     "@stylistic/eslint-plugin": "^5.10.0",
     "eslint": "^9.39.4",
     "eslint-config-preact": "^2.0.0",
-    "globals": "^17.5.0",
-    "typescript-eslint": "^8.58.2"
+    "globals": "^17.6.0",
+    "typescript-eslint": "^8.61.1"
   }
 }
diff --git a/package-lock.json b/package-lock.json
index 88003fcd..3457b48e 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -347,9 +347,9 @@
       }
     },
     "node_modules/baseline-browser-mapping": {
-      "version": "2.10.34",
-      "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.34.tgz",
-      "integrity": "sha512-IMDedajPifLnHNY0X9n8hKxRTQ6/eTHwr5bDo04WnuqxyKw6LYtQywCuuqPZwhl3aBXMvQpJov42GLCwRRdQzw==",
+      "version": "2.10.38",
+      "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.38.tgz",
+      "integrity": "sha512-31/02mVB4yuQU6adKk5SlY6m+mxDwUq5KZkyYgnLrrKl7TEm1+3PyDtDBz2kOv/wxZz41GHsvV1A/u6RmiyBvw==",
       "license": "Apache-2.0",
       "bin": {
         "baseline-browser-mapping": "dist/cli.cjs"
@@ -392,9 +392,9 @@
       }
     },
     "node_modules/caniuse-lite": {
-      "version": "1.0.30001797",
-      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001797.tgz",
-      "integrity": "sha512-l8xKG+gwAIExZGl9FrF7KUwuOmk6wbEPC9Xoy/RtnWv1XG0Q4LFlagaLpUv3Kiza3W/wm27zy0yWJEieYKAP6w==",
+      "version": "1.0.30001799",
+      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001799.tgz",
+      "integrity": "sha512-hG1bReV+OUU+MOqK4t/ZWI0tZOyz3rqS9XuhOUz1cIcbwBKjOyJEJuw9ER5JuNyqxNk8u/JUVbGibBOL1yrjFw==",
       "funding": [
         {
           "type": "opencollective",
@@ -437,9 +437,9 @@
       }
     },
     "node_modules/electron-to-chromium": {
-      "version": "1.5.368",
-      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.368.tgz",
-      "integrity": "sha512-7RckJJK4uESJF9PxvfMWd3TGqIiieUTG4HxnKaKuIpGbcr+r2ZEB3g2gAhCP3Fqm42vJSzLfgab9eva/C4/XVw==",
+      "version": "1.5.376",
+      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.376.tgz",
+      "integrity": "sha512-cUVA7/RvbFTEuw/i3obUwDTRIXojaxkResf+ibByPFxjc6XK3VNtcQXV0NSbAlJ0FMjcJGgftVVB4Qo184EXvA==",
       "license": "ISC"
     },
     "node_modules/escalade": {
@@ -494,9 +494,9 @@
       }
     },
     "node_modules/nanoid": {
-      "version": "3.3.12",
-      "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz",
-      "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==",
+      "version": "3.3.14",
+      "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.14.tgz",
+      "integrity": "sha512-U9kYi5bpVMEI31yC8iw4bJJp0avcHXA0W8/wNfLfnvJYzihQo2ZRPYPvpAAd570HAcCBjCTN7vnr+v4StKl1IQ==",
       "funding": [
         {
           "type": "github",
@@ -519,9 +519,9 @@
       "optional": true
     },
     "node_modules/node-releases": {
-      "version": "2.0.47",
-      "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.47.tgz",
-      "integrity": "sha512-Uzmd6LXpouKo8EUK68IjH4+E01w/hXyV3R3g/geCJo+rXLNfh1xucB+LOzYEOQPSiUK3h/xZf0cQGcSsmyL2Og==",
+      "version": "2.0.48",
+      "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.48.tgz",
+      "integrity": "sha512-1uz8041X6LoI6ZSdZacM9lVY28vuzDlSKitnpbSNK0RfKoIJkX29NBPVEFXhnuSuEOA9Ww0xnPJ+ILWbGAv8DA==",
       "license": "MIT",
       "engines": {
         "node": ">=18"
diff --git a/package.json b/package.json
index 47cfbf9b..0f87cbaf 100644
--- a/package.json
+++ b/package.json
@@ -4,7 +4,7 @@
   "version": "1.0.0",
   "dependencies": {
     "autoprefixer": "^10.5.0",
-    "postcss": "^8.5.14",
+    "postcss": "^8.5.15",
     "sass": "^1.101.0"
   },
   "scripts": {


Commit: 05d35d6ce5c7a4f8b7771f8ecfa3e6892cb5665f
    https://github.com/scummvm/scummvm-web/commit/05d35d6ce5c7a4f8b7771f8ecfa3e6892cb5665f
Author: Le Philousophe (lephilousophe at users.noreply.github.com)
Date: 2026-06-21T11:23:40+02:00

Commit Message:
WEB: Use @composer

This allows to abstract where the composer command is located.

Changed paths:
    composer.json


diff --git a/composer.json b/composer.json
index 3b42aa56..f882dc32 100644
--- a/composer.json
+++ b/composer.json
@@ -45,17 +45,17 @@
       "@update-data"
     ],
     "build": [
-      "composer install --no-dev",
+      "@composer install --no-dev",
       "@build-common",
       "npm ci --omit=dev"
     ],
     "build-dev": [
-      "composer install",
+      "@composer install",
       "@build-common",
       "npm install"
     ],
     "ci": [
-      "composer install",
+      "@composer install",
       "@build-common",
       "@lint",
       "npm ci",
@@ -82,7 +82,7 @@
       "propel model:build",
       "propel config:convert",
       "propel sql:insert",
-      "composer dumpautoload"
+      "@composer dumpautoload"
     ],
     "localize": [
       "php include/LocalizationUtils.php"



From noreply at scummvm.org  Sun Jun 21 09:25:52 2026
From: noreply at scummvm.org (ScummVM git)
Date: Sun, 21 Jun 2026 09:25:52 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm-web
 dependabot/npm_and_yarn/dumper-companion/eslint-207526f816 ->
 13bf325d0a9d6fd35a987969213ddd1a0c9a1add
Message-ID: <20260621092552.2DB4D7005CD@ron.scummvm.net>

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

Summary:
13bf325d0a Bump the eslint group across 1 directory with 2 updates


Commit: 13bf325d0a9d6fd35a987969213ddd1a0c9a1add
    https://github.com/scummvm/scummvm-web/commit/13bf325d0a9d6fd35a987969213ddd1a0c9a1add
Author: dependabot[bot] (49699333+dependabot[bot]@users.noreply.github.com)
Date: 2026-06-21T09:25:48Z

Commit Message:
Bump the eslint group across 1 directory with 2 updates

Bumps the eslint group with 2 updates in the /dumper-companion directory: [@eslint/js](https://github.com/eslint/eslint/tree/HEAD/packages/js) and [eslint](https://github.com/eslint/eslint).


Updates `@eslint/js` from 9.39.4 to 10.0.1
- [Release notes](https://github.com/eslint/eslint/releases)
- [Commits](https://github.com/eslint/eslint/commits/v10.0.1/packages/js)

Updates `eslint` from 9.39.4 to 10.5.0
- [Release notes](https://github.com/eslint/eslint/releases)
- [Commits](https://github.com/eslint/eslint/compare/v9.39.4...v10.5.0)

---
updated-dependencies:
- dependency-name: "@eslint/js"
  dependency-version: 10.0.1
  dependency-type: direct:development
  update-type: version-update:semver-major
  dependency-group: eslint
- dependency-name: eslint
  dependency-version: 10.5.0
  dependency-type: direct:development
  update-type: version-update:semver-major
  dependency-group: eslint
...

Signed-off-by: dependabot[bot] <support at github.com>

Changed paths:
    dumper-companion/package-lock.json
    dumper-companion/package.json


diff --git a/dumper-companion/package-lock.json b/dumper-companion/package-lock.json
index 9dad2ca4..c9d0fbe9 100644
--- a/dumper-companion/package-lock.json
+++ b/dumper-companion/package-lock.json
@@ -17,9 +17,9 @@
         "webpack-cli": "^7.0.3"
       },
       "devDependencies": {
-        "@eslint/js": "^9.39.4",
+        "@eslint/js": "^10.0.1",
         "@stylistic/eslint-plugin": "^5.10.0",
-        "eslint": "^9.39.4",
+        "eslint": "^10.5.0",
         "eslint-config-preact": "^2.0.0",
         "globals": "^17.6.0",
         "typescript-eslint": "^8.61.1"
@@ -81,35 +81,6 @@
         "url": "https://opencollective.com/babel"
       }
     },
-    "node_modules/@babel/eslint-parser": {
-      "version": "7.29.7",
-      "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.29.7.tgz",
-      "integrity": "sha512-zxt+UJTOMKvUt3yOg+D58MLuz334pHp93qifMFcjIIO+9hN6t+ufw2gi7vDPMpxvfnHRR+3VVXvIjineCcgyXw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@nicolo-ribaudo/eslint-scope-5-internals": "5.1.1-v1",
-        "eslint-visitor-keys": "^2.1.0",
-        "semver": "^6.3.1"
-      },
-      "engines": {
-        "node": "^10.13.0 || ^12.13.0 || >=14.0.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.11.0",
-        "eslint": "^7.5.0 || ^8.0.0 || ^9.0.0"
-      }
-    },
-    "node_modules/@babel/eslint-parser/node_modules/eslint-visitor-keys": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz",
-      "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==",
-      "dev": true,
-      "license": "Apache-2.0",
-      "engines": {
-        "node": ">=10"
-      }
-    },
     "node_modules/@babel/generator": {
       "version": "7.29.7",
       "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.7.tgz",
@@ -385,118 +356,89 @@
       }
     },
     "node_modules/@eslint/config-array": {
-      "version": "0.21.2",
-      "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.2.tgz",
-      "integrity": "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==",
+      "version": "0.23.5",
+      "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.23.5.tgz",
+      "integrity": "sha512-Y3kKLvC1dvTOT+oGlqNQ1XLqK6D1HU2YXPc52NmAlJZbMMWDzGYXMiPRJ8TYD39muD/OTjlZmNJ4ib7dvSrMBA==",
       "dev": true,
       "license": "Apache-2.0",
       "dependencies": {
-        "@eslint/object-schema": "^2.1.7",
+        "@eslint/object-schema": "^3.0.5",
         "debug": "^4.3.1",
-        "minimatch": "^3.1.5"
+        "minimatch": "^10.2.4"
       },
       "engines": {
-        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+        "node": "^20.19.0 || ^22.13.0 || >=24"
       }
     },
     "node_modules/@eslint/config-helpers": {
-      "version": "0.4.2",
-      "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz",
-      "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==",
+      "version": "0.6.0",
+      "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.6.0.tgz",
+      "integrity": "sha512-ii6Bw9jJ2zi2cWA2Z+9/QZ/+3DX6kwaV5Q986D/CdP3Lap3w/pgQZ373FV7byY/i7L4IRH/G43I5dz1ClsCbpA==",
       "dev": true,
       "license": "Apache-2.0",
       "dependencies": {
-        "@eslint/core": "^0.17.0"
+        "@eslint/core": "^1.2.1"
       },
       "engines": {
-        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+        "node": "^20.19.0 || ^22.13.0 || >=24"
       }
     },
     "node_modules/@eslint/core": {
-      "version": "0.17.0",
-      "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz",
-      "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==",
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/@eslint/core/-/core-1.2.1.tgz",
+      "integrity": "sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ==",
       "dev": true,
       "license": "Apache-2.0",
       "dependencies": {
         "@types/json-schema": "^7.0.15"
       },
       "engines": {
-        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
-      }
-    },
-    "node_modules/@eslint/eslintrc": {
-      "version": "3.3.5",
-      "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.5.tgz",
-      "integrity": "sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "ajv": "^6.14.0",
-        "debug": "^4.3.2",
-        "espree": "^10.0.1",
-        "globals": "^14.0.0",
-        "ignore": "^5.2.0",
-        "import-fresh": "^3.2.1",
-        "js-yaml": "^4.1.1",
-        "minimatch": "^3.1.5",
-        "strip-json-comments": "^3.1.1"
-      },
-      "engines": {
-        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
-      },
-      "funding": {
-        "url": "https://opencollective.com/eslint"
-      }
-    },
-    "node_modules/@eslint/eslintrc/node_modules/globals": {
-      "version": "14.0.0",
-      "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz",
-      "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=18"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
+        "node": "^20.19.0 || ^22.13.0 || >=24"
       }
     },
     "node_modules/@eslint/js": {
-      "version": "9.39.4",
-      "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.4.tgz",
-      "integrity": "sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==",
+      "version": "10.0.1",
+      "resolved": "https://registry.npmjs.org/@eslint/js/-/js-10.0.1.tgz",
+      "integrity": "sha512-zeR9k5pd4gxjZ0abRoIaxdc7I3nDktoXZk2qOv9gCNWx3mVwEn32VRhyLaRsDiJjTs0xq/T8mfPtyuXu7GWBcA==",
       "dev": true,
       "license": "MIT",
       "engines": {
-        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+        "node": "^20.19.0 || ^22.13.0 || >=24"
       },
       "funding": {
         "url": "https://eslint.org/donate"
+      },
+      "peerDependencies": {
+        "eslint": "^10.0.0"
+      },
+      "peerDependenciesMeta": {
+        "eslint": {
+          "optional": true
+        }
       }
     },
     "node_modules/@eslint/object-schema": {
-      "version": "2.1.7",
-      "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz",
-      "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==",
+      "version": "3.0.5",
+      "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-3.0.5.tgz",
+      "integrity": "sha512-vqTaUEgxzm+YDSdElad6PiRoX4t8VGDjCtt05zn4nU810UIx/uNEV7/lZJ6KwFThKZOzOxzXy48da+No7HZaMw==",
       "dev": true,
       "license": "Apache-2.0",
       "engines": {
-        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+        "node": "^20.19.0 || ^22.13.0 || >=24"
       }
     },
     "node_modules/@eslint/plugin-kit": {
-      "version": "0.4.1",
-      "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz",
-      "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==",
+      "version": "0.7.2",
+      "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.7.2.tgz",
+      "integrity": "sha512-+CNAzxglkrpNf/kKywqQfk74QjtceuOE7Qm+AF8miRvPF/wmmK5+OJOgVh3AVTT3RP2mH3+FOaxlE5v72owk0A==",
       "dev": true,
       "license": "Apache-2.0",
       "dependencies": {
-        "@eslint/core": "^0.17.0",
+        "@eslint/core": "^1.2.1",
         "levn": "^0.4.1"
       },
       "engines": {
-        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+        "node": "^20.19.0 || ^22.13.0 || >=24"
       }
     },
     "node_modules/@humanfs/core": {
@@ -683,6 +625,13 @@
         "eslint": "^9.0.0 || ^10.0.0"
       }
     },
+    "node_modules/@types/esrecurse": {
+      "version": "4.3.1",
+      "resolved": "https://registry.npmjs.org/@types/esrecurse/-/esrecurse-4.3.1.tgz",
+      "integrity": "sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==",
+      "dev": true,
+      "license": "MIT"
+    },
     "node_modules/@types/estree": {
       "version": "1.0.9",
       "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz",
@@ -898,45 +847,6 @@
         "typescript": ">=4.8.4 <6.1.0"
       }
     },
-    "node_modules/@typescript-eslint/typescript-estree/node_modules/balanced-match": {
-      "version": "4.0.4",
-      "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
-      "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": "18 || 20 || >=22"
-      }
-    },
-    "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": {
-      "version": "5.0.6",
-      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz",
-      "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "balanced-match": "^4.0.2"
-      },
-      "engines": {
-        "node": "18 || 20 || >=22"
-      }
-    },
-    "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": {
-      "version": "10.2.5",
-      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz",
-      "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==",
-      "dev": true,
-      "license": "BlueOak-1.0.0",
-      "dependencies": {
-        "brace-expansion": "^5.0.5"
-      },
-      "engines": {
-        "node": "18 || 20 || >=22"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/isaacs"
-      }
-    },
     "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": {
       "version": "7.8.5",
       "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz",
@@ -1279,13 +1189,6 @@
         "url": "https://github.com/chalk/ansi-styles?sponsor=1"
       }
     },
-    "node_modules/argparse": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
-      "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
-      "dev": true,
-      "license": "Python-2.0"
-    },
     "node_modules/array-buffer-byte-length": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz",
@@ -1468,11 +1371,14 @@
       }
     },
     "node_modules/balanced-match": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
-      "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+      "version": "4.0.4",
+      "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
+      "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==",
       "dev": true,
-      "license": "MIT"
+      "license": "MIT",
+      "engines": {
+        "node": "18 || 20 || >=22"
+      }
     },
     "node_modules/baseline-browser-mapping": {
       "version": "2.10.38",
@@ -1487,14 +1393,16 @@
       }
     },
     "node_modules/brace-expansion": {
-      "version": "1.1.15",
-      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz",
-      "integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==",
+      "version": "5.0.6",
+      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz",
+      "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "balanced-match": "^1.0.0",
-        "concat-map": "0.0.1"
+        "balanced-match": "^4.0.2"
+      },
+      "engines": {
+        "node": "18 || 20 || >=22"
       }
     },
     "node_modules/braces": {
@@ -1598,16 +1506,6 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
-    "node_modules/callsites": {
-      "version": "3.1.0",
-      "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
-      "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=6"
-      }
-    },
     "node_modules/caniuse-lite": {
       "version": "1.0.30001799",
       "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001799.tgz",
@@ -2119,33 +2017,33 @@
       }
     },
     "node_modules/eslint": {
-      "version": "9.39.4",
-      "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.4.tgz",
-      "integrity": "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==",
+      "version": "10.5.0",
+      "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.5.0.tgz",
+      "integrity": "sha512-1y+7C+vi12bUK1IpZeaV3gsH9fHLBmPvYmPx42pvT/E9yG0IC8g3PUZZgp0+JLJl7ZDK0flc2gc+Aw9dpCvIsQ==",
       "dev": true,
       "license": "MIT",
+      "workspaces": [
+        "packages/*"
+      ],
       "dependencies": {
         "@eslint-community/eslint-utils": "^4.8.0",
-        "@eslint-community/regexpp": "^4.12.1",
-        "@eslint/config-array": "^0.21.2",
-        "@eslint/config-helpers": "^0.4.2",
-        "@eslint/core": "^0.17.0",
-        "@eslint/eslintrc": "^3.3.5",
-        "@eslint/js": "9.39.4",
-        "@eslint/plugin-kit": "^0.4.1",
+        "@eslint-community/regexpp": "^4.12.2",
+        "@eslint/config-array": "^0.23.5",
+        "@eslint/config-helpers": "^0.6.0",
+        "@eslint/core": "^1.2.1",
+        "@eslint/plugin-kit": "^0.7.2",
         "@humanfs/node": "^0.16.6",
         "@humanwhocodes/module-importer": "^1.0.1",
         "@humanwhocodes/retry": "^0.4.2",
         "@types/estree": "^1.0.6",
         "ajv": "^6.14.0",
-        "chalk": "^4.0.0",
         "cross-spawn": "^7.0.6",
         "debug": "^4.3.2",
         "escape-string-regexp": "^4.0.0",
-        "eslint-scope": "^8.4.0",
-        "eslint-visitor-keys": "^4.2.1",
-        "espree": "^10.4.0",
-        "esquery": "^1.5.0",
+        "eslint-scope": "^9.1.2",
+        "eslint-visitor-keys": "^5.0.1",
+        "espree": "^11.2.0",
+        "esquery": "^1.7.0",
         "esutils": "^2.0.2",
         "fast-deep-equal": "^3.1.3",
         "file-entry-cache": "^8.0.0",
@@ -2155,8 +2053,7 @@
         "imurmurhash": "^0.1.4",
         "is-glob": "^4.0.0",
         "json-stable-stringify-without-jsonify": "^1.0.1",
-        "lodash.merge": "^4.6.2",
-        "minimatch": "^3.1.5",
+        "minimatch": "^10.2.4",
         "natural-compare": "^1.4.0",
         "optionator": "^0.9.3"
       },
@@ -2164,7 +2061,7 @@
         "eslint": "bin/eslint.js"
       },
       "engines": {
-        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+        "node": "^20.19.0 || ^22.13.0 || >=24"
       },
       "funding": {
         "url": "https://eslint.org/donate"
@@ -2199,68 +2096,57 @@
         "eslint": "^8.57.1 || ^9.0.0"
       }
     },
-    "node_modules/eslint-config-preact/node_modules/globals": {
-      "version": "16.5.0",
-      "resolved": "https://registry.npmjs.org/globals/-/globals-16.5.0.tgz",
-      "integrity": "sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=18"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/eslint-plugin-compat": {
-      "version": "6.2.1",
-      "resolved": "https://registry.npmjs.org/eslint-plugin-compat/-/eslint-plugin-compat-6.2.1.tgz",
-      "integrity": "sha512-gLKqUH+lQcCL+HzsROUjBDvakc5Zaga51Y4ZAkPCXc41pzKBfyluqTr2j8zOx8QQQb7zyglu1LVoL5aSNWf2SQ==",
+    "node_modules/eslint-config-preact/node_modules/@babel/eslint-parser": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.29.7.tgz",
+      "integrity": "sha512-zxt+UJTOMKvUt3yOg+D58MLuz334pHp93qifMFcjIIO+9hN6t+ufw2gi7vDPMpxvfnHRR+3VVXvIjineCcgyXw==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@mdn/browser-compat-data": "^6.1.1",
-        "ast-metadata-inferer": "^0.8.1",
-        "browserslist": "^4.25.2",
-        "find-up": "^5.0.0",
-        "globals": "^15.7.0",
-        "lodash.memoize": "^4.1.2",
-        "semver": "^7.6.2"
+        "@nicolo-ribaudo/eslint-scope-5-internals": "5.1.1-v1",
+        "eslint-visitor-keys": "^2.1.0",
+        "semver": "^6.3.1"
       },
       "engines": {
-        "node": ">=18.x"
+        "node": "^10.13.0 || ^12.13.0 || >=14.0.0"
       },
       "peerDependencies": {
-        "eslint": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0"
+        "@babel/core": "^7.11.0",
+        "eslint": "^7.5.0 || ^8.0.0 || ^9.0.0"
       }
     },
-    "node_modules/eslint-plugin-compat/node_modules/globals": {
-      "version": "15.15.0",
-      "resolved": "https://registry.npmjs.org/globals/-/globals-15.15.0.tgz",
-      "integrity": "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==",
+    "node_modules/eslint-config-preact/node_modules/@eslint/js": {
+      "version": "9.39.4",
+      "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.4.tgz",
+      "integrity": "sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==",
       "dev": true,
       "license": "MIT",
       "engines": {
-        "node": ">=18"
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
       },
       "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
+        "url": "https://eslint.org/donate"
       }
     },
-    "node_modules/eslint-plugin-compat/node_modules/semver": {
-      "version": "7.8.5",
-      "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz",
-      "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==",
+    "node_modules/eslint-config-preact/node_modules/balanced-match": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+      "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
       "dev": true,
-      "license": "ISC",
-      "bin": {
-        "semver": "bin/semver.js"
-      },
-      "engines": {
-        "node": ">=10"
+      "license": "MIT"
+    },
+    "node_modules/eslint-config-preact/node_modules/brace-expansion": {
+      "version": "1.1.15",
+      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz",
+      "integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "balanced-match": "^1.0.0",
+        "concat-map": "0.0.1"
       }
     },
-    "node_modules/eslint-plugin-react": {
+    "node_modules/eslint-config-preact/node_modules/eslint-plugin-react": {
       "version": "7.37.5",
       "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.5.tgz",
       "integrity": "sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==",
@@ -2293,7 +2179,7 @@
         "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7"
       }
     },
-    "node_modules/eslint-plugin-react-hooks": {
+    "node_modules/eslint-config-preact/node_modules/eslint-plugin-react-hooks": {
       "version": "5.2.0",
       "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.2.0.tgz",
       "integrity": "sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==",
@@ -2306,18 +2192,104 @@
         "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0"
       }
     },
+    "node_modules/eslint-config-preact/node_modules/eslint-visitor-keys": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz",
+      "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/eslint-config-preact/node_modules/globals": {
+      "version": "16.5.0",
+      "resolved": "https://registry.npmjs.org/globals/-/globals-16.5.0.tgz",
+      "integrity": "sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/eslint-config-preact/node_modules/minimatch": {
+      "version": "3.1.5",
+      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
+      "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "brace-expansion": "^1.1.7"
+      },
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/eslint-plugin-compat": {
+      "version": "6.2.1",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-compat/-/eslint-plugin-compat-6.2.1.tgz",
+      "integrity": "sha512-gLKqUH+lQcCL+HzsROUjBDvakc5Zaga51Y4ZAkPCXc41pzKBfyluqTr2j8zOx8QQQb7zyglu1LVoL5aSNWf2SQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@mdn/browser-compat-data": "^6.1.1",
+        "ast-metadata-inferer": "^0.8.1",
+        "browserslist": "^4.25.2",
+        "find-up": "^5.0.0",
+        "globals": "^15.7.0",
+        "lodash.memoize": "^4.1.2",
+        "semver": "^7.6.2"
+      },
+      "engines": {
+        "node": ">=18.x"
+      },
+      "peerDependencies": {
+        "eslint": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0"
+      }
+    },
+    "node_modules/eslint-plugin-compat/node_modules/globals": {
+      "version": "15.15.0",
+      "resolved": "https://registry.npmjs.org/globals/-/globals-15.15.0.tgz",
+      "integrity": "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/eslint-plugin-compat/node_modules/semver": {
+      "version": "7.8.5",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz",
+      "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==",
+      "dev": true,
+      "license": "ISC",
+      "bin": {
+        "semver": "bin/semver.js"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
     "node_modules/eslint-scope": {
-      "version": "8.4.0",
-      "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz",
-      "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==",
+      "version": "9.1.2",
+      "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-9.1.2.tgz",
+      "integrity": "sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==",
       "dev": true,
       "license": "BSD-2-Clause",
       "dependencies": {
+        "@types/esrecurse": "^4.3.1",
+        "@types/estree": "^1.0.8",
         "esrecurse": "^4.3.0",
         "estraverse": "^5.2.0"
       },
       "engines": {
-        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+        "node": "^20.19.0 || ^22.13.0 || >=24"
       },
       "funding": {
         "url": "https://opencollective.com/eslint"
@@ -2336,6 +2308,37 @@
         "url": "https://opencollective.com/eslint"
       }
     },
+    "node_modules/eslint/node_modules/eslint-visitor-keys": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz",
+      "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "engines": {
+        "node": "^20.19.0 || ^22.13.0 || >=24"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint"
+      }
+    },
+    "node_modules/eslint/node_modules/espree": {
+      "version": "11.2.0",
+      "resolved": "https://registry.npmjs.org/espree/-/espree-11.2.0.tgz",
+      "integrity": "sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "dependencies": {
+        "acorn": "^8.16.0",
+        "acorn-jsx": "^5.3.2",
+        "eslint-visitor-keys": "^5.0.1"
+      },
+      "engines": {
+        "node": "^20.19.0 || ^22.13.0 || >=24"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint"
+      }
+    },
     "node_modules/espree": {
       "version": "10.4.0",
       "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz",
@@ -2839,23 +2842,6 @@
         "node": ">= 4"
       }
     },
-    "node_modules/import-fresh": {
-      "version": "3.3.1",
-      "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
-      "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "parent-module": "^1.0.0",
-        "resolve-from": "^4.0.0"
-      },
-      "engines": {
-        "node": ">=6"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
     "node_modules/import-local": {
       "version": "3.2.0",
       "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz",
@@ -3403,29 +3389,6 @@
       "dev": true,
       "license": "MIT"
     },
-    "node_modules/js-yaml": {
-      "version": "4.2.0",
-      "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.2.0.tgz",
-      "integrity": "sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==",
-      "dev": true,
-      "funding": [
-        {
-          "type": "github",
-          "url": "https://github.com/sponsors/puzrin"
-        },
-        {
-          "type": "github",
-          "url": "https://github.com/sponsors/nodeca"
-        }
-      ],
-      "license": "MIT",
-      "dependencies": {
-        "argparse": "^2.0.1"
-      },
-      "bin": {
-        "js-yaml": "bin/js-yaml.js"
-      }
-    },
     "node_modules/jsesc": {
       "version": "3.1.0",
       "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",
@@ -3558,13 +3521,6 @@
       "dev": true,
       "license": "MIT"
     },
-    "node_modules/lodash.merge": {
-      "version": "4.6.2",
-      "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
-      "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
-      "dev": true,
-      "license": "MIT"
-    },
     "node_modules/loose-envify": {
       "version": "1.4.0",
       "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
@@ -3639,16 +3595,19 @@
       }
     },
     "node_modules/minimatch": {
-      "version": "3.1.5",
-      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
-      "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
+      "version": "10.2.5",
+      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz",
+      "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==",
       "dev": true,
-      "license": "ISC",
+      "license": "BlueOak-1.0.0",
       "dependencies": {
-        "brace-expansion": "^1.1.7"
+        "brace-expansion": "^5.0.5"
       },
       "engines": {
-        "node": "*"
+        "node": "18 || 20 || >=22"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
       }
     },
     "node_modules/ms": {
@@ -3884,19 +3843,6 @@
         "node": ">=6"
       }
     },
-    "node_modules/parent-module": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
-      "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "callsites": "^3.0.0"
-      },
-      "engines": {
-        "node": ">=6"
-      }
-    },
     "node_modules/path-exists": {
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
@@ -4193,16 +4139,6 @@
         "node": ">=8"
       }
     },
-    "node_modules/resolve-from": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
-      "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=4"
-      }
-    },
     "node_modules/safe-array-concat": {
       "version": "1.1.4",
       "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.4.tgz",
@@ -4620,19 +4556,6 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
-    "node_modules/strip-json-comments": {
-      "version": "3.1.1",
-      "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
-      "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=8"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
     "node_modules/supports-color": {
       "version": "7.2.0",
       "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
diff --git a/dumper-companion/package.json b/dumper-companion/package.json
index 13fe2bc9..4980e441 100644
--- a/dumper-companion/package.json
+++ b/dumper-companion/package.json
@@ -16,9 +16,9 @@
     "webpack-cli": "^7.0.3"
   },
   "devDependencies": {
-    "@eslint/js": "^9.39.4",
+    "@eslint/js": "^10.0.1",
     "@stylistic/eslint-plugin": "^5.10.0",
-    "eslint": "^9.39.4",
+    "eslint": "^10.5.0",
     "eslint-config-preact": "^2.0.0",
     "globals": "^17.6.0",
     "typescript-eslint": "^8.61.1"



From noreply at scummvm.org  Sun Jun 21 09:34:23 2026
From: noreply at scummvm.org (antoniou79)
Date: Sun, 21 Jun 2026 09:34:23 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 d131a545c46b8a12d3d52507b2b4b7b4e6b133ed
Message-ID: <20260621093423.986B27005CD@ron.scummvm.net>

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

Summary:
d131a545c4 DISTS: ANDROID: Release notes for 2026.3.0


Commit: d131a545c46b8a12d3d52507b2b4b7b4e6b133ed
    https://github.com/scummvm/scummvm/commit/d131a545c46b8a12d3d52507b2b4b7b4e6b133ed
Author: antoniou79 (a.antoniou79 at gmail.com)
Date: 2026-06-21T12:32:55+03:00

Commit Message:
DISTS: ANDROID: Release notes for 2026.3.0

Changed paths:
    dists/android/store/metadata-en-US.txt


diff --git a/dists/android/store/metadata-en-US.txt b/dists/android/store/metadata-en-US.txt
index f5c7a1c2469..86d88333f1f 100644
--- a/dists/android/store/metadata-en-US.txt
+++ b/dists/android/store/metadata-en-US.txt
@@ -14,7 +14,7 @@ ScummVM provides a way to play many classic graphical point-and-click adventure
 
 You can find more information, demos, and some free-to-download adventure games on our website. See also an up-to-date list here: https://wiki.scummvm.org/index.php/Where_to_get_the_games
 
-A quick-start guide is available on our website at https://docs.scummvm.org/en/v2026.2.0/other_platforms/android.html which provides some more information, explaining how to configure Android-specific options and where you can find further help.
+A quick-start guide is available on our website at https://docs.scummvm.org/en/v2026.3.0/other_platforms/android.html which provides some more information, explaining how to configure Android-specific options and where you can find further help.
 
 https://forums.scummvm.org/viewforum.php?f=17 is our web forum where you can discuss the Android version.
 
@@ -22,10 +22,10 @@ ScummVM is open source software available under the term of the GPL v3.0 license
 The source code is available from https://github.com/scummvm/scummvm.
 
 *** Current release:
-2026.2.0
+2026.3.0
 
 *** Release notes (limit is 500 characters):
-- Fixed a crash when restarting the app quickly
-- Fixed a Cloud-related crash
+- Reworked the sound subsystem to use the Oboe audio library
+- Fixed a crash related to Cloud storage on OneDrive
 
-Full release notes: https://scummvm.org/frs/scummvm/2026.2.0/ReleaseNotes.html
+Full release notes: https://scummvm.org/frs/scummvm/2026.3.0/ReleaseNotes.html



From noreply at scummvm.org  Sun Jun 21 11:32:01 2026
From: noreply at scummvm.org (lotharsm)
Date: Sun, 21 Jun 2026 11:32:01 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm-web master ->
 e90154b2072fa18e96b72fbfde5cc0476743f10c
Message-ID: <20260621113201.8417A7005CE@ron.scummvm.net>

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

Summary:
e90154b207 DATA: Update data files


Commit: e90154b2072fa18e96b72fbfde5cc0476743f10c
    https://github.com/scummvm/scummvm-web/commit/e90154b2072fa18e96b72fbfde5cc0476743f10c
Author: sev- (sev at scummvm.org)
Date: 2026-06-21T11:31:56Z

Commit Message:
DATA: Update data files

Changed paths:
    data/en/scummvm_downloads.yaml


diff --git a/data/en/scummvm_downloads.yaml b/data/en/scummvm_downloads.yaml
index 3da785af..190708ba 100644
--- a/data/en/scummvm_downloads.yaml
+++ b/data/en/scummvm_downloads.yaml
@@ -92,50 +92,50 @@
 -
     enabled: true
     category: scummvm
-    subcategory: old
+    subcategory: release
     category_icon: debian
     user_agent: Debian
-    version: 2026.2.0
+    version: 2026.3.0
     url: 'scummvm_{$version}-1_debian13_amd64.deb'
     name: 'Debian 13 (trixie) x86 64bit package'
     notes: ''
 -
     enabled: true
     category: scummvm
-    subcategory: old
+    subcategory: release
     category_icon: debian
     user_agent: ''
-    version: 2026.2.0
+    version: 2026.3.0
     url: 'scummvm_{$version}-1_debian12_amd64.deb'
     name: 'Debian 12 (bookworm) x86 64bit package'
     notes: ''
 -
     enabled: true
     category: scummvm
-    subcategory: old
+    subcategory: release
     category_icon: debian
     user_agent: ''
-    version: 2026.2.0
+    version: 2026.3.0
     url: 'scummvm_{$version}-1_debian12_i386.deb'
     name: 'Debian 12 (bookworm) i386 package'
     notes: ''
 -
     enabled: true
     category: scummvm
-    subcategory: old
+    subcategory: release
     category_icon: debian
     user_agent: ''
-    version: 2026.2.0
+    version: 2026.3.0
     url: 'scummvm_{$version}-1_debian11_amd64.deb'
     name: 'Debian 11 (bullseye) x86 64bit package'
     notes: ''
 -
     enabled: true
     category: scummvm
-    subcategory: old
+    subcategory: release
     category_icon: debian
     user_agent: ''
-    version: 2026.2.0
+    version: 2026.3.0
     url: 'scummvm_{$version}-1_debian11_i386.deb'
     name: 'Debian 11 (bullseye) i386 package'
     notes: ''
@@ -192,30 +192,40 @@
 -
     enabled: true
     category: scummvm
-    subcategory: old
+    subcategory: release
     category_icon: ubuntu
     user_agent: ''
-    version: 2026.2.0
+    version: 2026.3.0
+    url: 'scummvm_{$version}-1_ubuntu26_04_amd64.deb'
+    name: 'Ubuntu 26.04 LTS (resolute) x86 64bit package'
+    notes: ''
+-
+    enabled: true
+    category: scummvm
+    subcategory: release
+    category_icon: ubuntu
+    user_agent: ''
+    version: 2026.3.0
     url: 'scummvm_{$version}-1_ubuntu25_10_amd64.deb'
     name: 'Ubuntu 25.10 (questing) x86 64bit package'
     notes: ''
 -
     enabled: true
     category: scummvm
-    subcategory: old
+    subcategory: release
     category_icon: ubuntu
     user_agent: ''
-    version: 2026.2.0
+    version: 2026.3.0
     url: 'scummvm_{$version}-1_ubuntu24_04_amd64.deb'
     name: 'Ubuntu 24.04 LTS (noble) x86 64bit package'
     notes: ''
 -
     enabled: true
     category: scummvm
-    subcategory: old
+    subcategory: release
     category_icon: ubuntu
     user_agent: ''
-    version: 2026.2.0
+    version: 2026.3.0
     url: 'scummvm_{$version}-1_ubuntu22_04_amd64.deb'
     name: 'Ubuntu 22.04 LTS (jammy) x86 64bit package'
     notes: ''



From noreply at scummvm.org  Sun Jun 21 11:32:06 2026
From: noreply at scummvm.org (ScummVM-Translations)
Date: Sun, 21 Jun 2026 11:32:06 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm-web master ->
 5785127841ffe8ad764e8a1cedc4c3db09782d63
Message-ID: <20260621113206.AB5787005D1@ron.scummvm.net>

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

Summary:
5785127841 L10N: Update translation (Spanish) (News)


Commit: 5785127841ffe8ad764e8a1cedc4c3db09782d63
    https://github.com/scummvm/scummvm-web/commit/5785127841ffe8ad764e8a1cedc4c3db09782d63
Author: IlDucci (pajaroloco_2 at hotmail.com)
Date: 2026-06-21T11:32:02Z

Commit Message:
L10N: Update translation (Spanish) (News)

Currently translated at 94.3% (834 of 884 strings)

Changed paths:
    data/es/news.json


diff --git a/data/es/news.json b/data/es/news.json
index 46d32195..d2bf7d7c 100644
--- a/data/es/news.json
+++ b/data/es/news.json
@@ -1736,7 +1736,7 @@
         "content": "Han pasado tres meses desde la ??ltima versi??n y aqu?? estamos de nuevo, con nuestro nuevo sistema de lanzamientos. Est??bamos preocupados y entusiasmados a partes iguales, pero, sobre todo, esperanzados de que la nueva normalidad de la falta de pruebas prelanzamiento y versiones m??s frecuentes nos permita publicar correcciones cr??ticas m??s r??pidamente.\n\nHablemos de la extensi??n de esta versi??n. Habr??n pasado tres meses, pero la cantidad de caracter??sticas nuevas es _muy_ notable.\n\n## Nuevos juegos compatibles:\n* _Necronomicon: The Dawning of Darkness (Necronomic??n: El Alba de las Tinieblas)_\n* _Crime Patrol_\n* _Crime Patrol 2: Drug Wars_\n* _The Last Bounty Hunter_\n* _Mad Dog McCree_\n* _Mad Dog II: The Lost Gold_\n* _Space Pirates_\n* _Who Shot Johnny Rock?_\n\nAdem??s de estos juegos reci??n compatibilizados que vienen de dos motores nuevos, hemos mejorado la emulaci??n de altavoces de PC, a??adido el soporte a selecciones m??ltiples (pulsando May??s/Ctrl) al iniciador, destacado m??s nuestro sistema interno de ayuda incluyendo botones de ??Ayuda?? en varias ventanas y hecho varias mejoras para facilitar la existencia a nuestro equipo de traductores de la interfaz.\n\nRespecto a las mejoras en motores y juegos concretos, estos son los cambios a destacar: a??adido el soporte para la m??sica de las versiones para Atari SI de _Elvira 1_, _Elvira 2_ y para la m??sica MIDI de _Ripley's Believe It or Not!: The Riddle of Master Lu_. Tras una serie de pruebas intensas, hemos mejorado la estabilidad general de _Hodj'n'Podj_, _Might and Magic: Book One - Secret of the Inner Sanctum_, _Myst III: Exile_, _Pilots Brothers 3D_ y _3D-2_, _The Lost Files of Sherlock Holmes: Case of the Rose Tattoo (Los Archivos Secretos de Sherlock Holmes: El Caso de la Rosa Tatuada)_, _Nathan's Second Chance_, _Broken Sword II: The Smoking Mirror (Broken Sword II: Las Fuerzas del Mal)_, _Discworld 1 (Mundodisco 1)_ y numerosos otros juegos basados en el motor WAGE.\n\nEn cuanto a las correcciones para plataformas concretos, _Broken Sword 1_ ya se juega mejor en sistemas Big Endian tales como PS3, Wii y OSXPPC. La conversi??n para Atari ha recibido modificaciones espec??ficas para el motor SCUMM. iOS ya tiene soporte para la conversi??n de texto a voz y ha mejorado su soporte para dispositivos MIDI externos.\n\nEstos son solo los puntos m??s relevantes de esta versi??n, pod??is encontrar la lista completa en [las notas de lanzamiento](https://downloads.scummvm.org/frs/scummvm/2026.2.0/ReleaseNotes.html).\n\nComo esta es nuestra primera versi??n en este formato nuevo, el n??mero de conversiones disponibles desde el d??a uno es limitado. Si busc??is actualizaciones para vuestras plataformas, os rogamos que teng??is paciencia con nosotros. Encontrar??is la versi??n nueva en [nuestra p??gina de descargas](https://www.scummvm.org/downloads/). Las versiones [de la Store de iOS](https://apps.apple.com/us/app/scummvm/id6446184412) y [para Android](https://play.google.com/store/apps/details?id=org.scummvm.scummvm) deber??an estar disponibles en breve en sus tiendas correspondientes."
     },
     "20260414": {
-        "title": "??Nancy Drem 8 y 9 se unen a la investigaci??n!",
+        "title": "??Nancy Drew 8 y 9 se unen a la investigaci??n!",
         "content": "??Vuelve el misterio! Es hora de volver a poneros en la piel de Nancy y seguir las pistas a donde os lleven...\n\nNos complace anunciar que ScummVM es ahora compatible con dos juegos m??s de la longeva saga de aventuras *Nancy Drew*.\n\nEn [_Nancy Drew: The Haunted Carousel_](https://wiki.scummvm.org/index.php?title=Nancy_Drew:_The_Haunted_Carousel), Nancy recibe una llamada para investigar una serie de sucesos extra??os en el parque de atracciones Captain???s Cove, donde ha desaparecido un caballito de un tiovivo y se empieza a rumorear la existencia de una maldici??n. ??Ser?? un sabotaje, una superstici??n u otra cosa muy distinta?\n\nEn [_Nancy Drew: Danger on Deception Island_](https://wiki.scummvm.org/index.php?title=Nancy_Drew:_Danger_on_Deception_Island), Nancy va al noroeste del Pac??fico a estudiar unos incidentes sospechosos en un centro de investigaci??n marina y unas noticias sobre orcas que se comportan de manera agresiva. Pero las cosas no suelen ser lo que parecen...\n\nComo de costumbre, necesitar??is los archivos de datos originales para poder jugar. Dichos juegos se pueden comprar en [la web de HeR Interactive](https://www.herinteractive.com/shop-games/all-games/) o en [Steam](https://store.steampowered.com/search/?term=nancy+drew). Para poder jugar con ellos en ScummVM necesitar??is [una compilaci??n diaria de desarrollo](https://www.scummvm.org/downloads/#daily).\n\nSi encontr??is cualquier problema, os rogamos que envi??is un informe de fallos a [nuestro gestor de fallos (en ingl??s)](https://bugs.scummvm.org/).\n\n??Sed curiosos y disfrutad de vuestras pesquisas!"
     },
     "20260502": {
@@ -1762,5 +1762,9 @@
     "20260617": {
         "title": "ScummVM se prepara para la paciencia a rayas",
         "content": "ScummVM ha terminado oficialmente de desarrollar el motor de juegos propietario Gamos para Windows. Originalmente estaba pensado para _Pilot Brothers 1 y 2_ y dando el pistoletazo de salida a la ic??nica saga de aventuras antes de que Gamos cambiase a los motores qdEngine y NGI. Gamos tambi??n se utiliz?? para desarrollar bastantes juegos para oficinas.\n\nEste motor da soporte a 7 juegos:\n\n* _Patience_\n* _Flip-Flop_\n* _NetWalk_\n* _Pilot Brothers: On the Track of Striped Elephant_\n* _Pilot Brothers: The Case of Serial Maniac_\n* _Vitamin_\n* _WildSnake_\n\nAs?? que si quer??is recordar el pasado con los exc??ntricos detectives Chief y Colleague o echar el rato con juegos tipo solitario, necesitar??is la ??ltima [compilaci??n diaria de desarrollo](https://www.scummvm.org/downloads/#daily) y los archivos de datos necesarios para cada juego. Como siempre, si encontr??is cualquier cosa rara, os rogamos que lo coment??is en [nuestro gestor de fallos (en ingl??s)](https://bugs.scummvm.org/)."
+    },
+    "20260620": {
+        "title": "ScummVM 2026.3.0 ??Carousels & Killer Whales?? ve la luz",
+        "content": "Por ahora estamos siendo capaces de mantener la cadencia de lanzamientos. ??El equipo os ofrece este trimestre una mezcla de caracter??sticas nuevas y viejas, todas ellas emocionantes a partes iguales!\n\n## Nuevos juegos soportados:\n* _Nancy Drew: The Haunted Carousel_\n* _Nancy Drew: Danger on Deception Island_\n* _Noctropolis_\n* _Cartoon Carnival_\n* _Alfred Pelrock: En Busca de un Sue??o_\n* _Pilot Brothers: On the Track of Striped Elephant_\n* _Pilot Brothers: The Case of Serial Maniac_ y 5 juegos m??s del mismo motor\n\n\n## Mejoras generales\n\nEste es el periodo inicial del Google Summer of Code, as?? que hemos tenido varios colaboradores nuevos que se han dedicado a mejorar nuestra interfaz y a incorporar a??adidos (grandes y peque??os) de calidad de vida, como son mejoras en la b??squeda, desplazamiento cin??tico y una nueva ventana ??Acerca de??.\n\n## Correcciones y mejoras en motores\n\nDiez de los motores compatibles han visto cambios. _Martian Memorandum_ tiene correcciones de fallos con los scripts, el soporte multiidioma de _Simon 2_ ha mejorado, hay un panel de control completamente nuevo para el motor GLK que permite modificar el renderizado de textos, _Last Express_ ha corregido algunos fallos muy infrecuentes, fallos que tambi??n se produc??an en _Rex Nebular_. _Might and Magic 1_ ha tenido una reprogramaci??n importante de su asignador de teclas y el soporte para el parche de mejoras va mejor que nunca.\n\nSe ha estado trabajando en el motor _Nancy_ y, adem??s de los dos juegos nuevos que se han a??adido (el octavo y el noveno), la mayor??a de los juegos que ya eran compatibles del resto de la serie han recibido sus correcciones de fallos (muchas gracias por enviarnos vuestros informes de fallos).\n\nDos juegos nuevos han ca??do en el motor SLUDGE y los hemos puesto a vuestra disposici??n en la web. Por ??ltimo, pero no menos importante, el motor TwinE, usado en la saga _Little Big Adventure_, tambi??n ha tenido su raci??n de fallos corregidos.\n\nCambiando de tema a las conversiones, la conversi??n para Atari est?? recibiendo toda clase de optimizaciones y la conversi??n para PlayStation 3 ha reducido su consumo de memoria, lo que permitir?? que se puedan ejecutar juegos m??s cargantes.\n\nSi quer??is ver un resumen pormenorizado de las mejoras, pod??is encontrarlo en [las notas de lanzamiento (en ingl??s)](https://downloads.scummvm.org/frs/scummvm/2026.3.0/ReleaseNotes.html).\n\n\nY siguiendo nuestro nuevo procedimiento de lanzamientos, las compilaciones de las conversiones llegar??n en las pr??ximas semanas."
     }
 }



From noreply at scummvm.org  Sun Jun 21 11:33:11 2026
From: noreply at scummvm.org (lephilousophe)
Date: Sun, 21 Jun 2026 11:33:11 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 e8498ccbfdf07449c2535e377d8ab722e7a2ed12
Message-ID: <20260621113311.6C2547005CE@ron.scummvm.net>

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

Summary:
e8498ccbfd ANDROID: Annotate JNI methods as to be kept


Commit: e8498ccbfdf07449c2535e377d8ab722e7a2ed12
    https://github.com/scummvm/scummvm/commit/e8498ccbfdf07449c2535e377d8ab722e7a2ed12
Author: Le Philousophe (lephilousophe at users.noreply.github.com)
Date: 2026-06-21T13:30:06+02:00

Commit Message:
ANDROID: Annotate JNI methods as to be kept

This avoids zealous minification on elements found by reflection.
Also cleanup things not used by JNI and reorder.

Changed paths:
    backends/platform/android/jni-android.cpp
    backends/platform/android/org/scummvm/scummvm/SAFFSTree.java
    backends/platform/android/org/scummvm/scummvm/ScummVM.java
    backends/platform/android/org/scummvm/scummvm/ScummVMActivity.java
    backends/platform/android/org/scummvm/scummvm/net/HTTPManager.java
    backends/platform/android/org/scummvm/scummvm/net/HTTPRequest.java
    backends/platform/android/org/scummvm/scummvm/net/SSocket.java


diff --git a/backends/platform/android/jni-android.cpp b/backends/platform/android/jni-android.cpp
index cd004b24436..c4d0060ed9a 100644
--- a/backends/platform/android/jni-android.cpp
+++ b/backends/platform/android/jni-android.cpp
@@ -707,7 +707,6 @@ void JNI::create(JNIEnv *env, jobject self, jobject asset_manager,
         }                                                                   \
     } while (0)
 
-	FIND_METHOD(, setWindowCaption, "(Ljava/lang/String;)V");
 	FIND_METHOD(, getDPI, "([F)V");
 	FIND_METHOD(, displayMessageOnOSD, "(Ljava/lang/String;)V");
 	FIND_METHOD(, openUrl, "(Ljava/lang/String;)V");
@@ -715,6 +714,7 @@ void JNI::create(JNIEnv *env, jobject self, jobject asset_manager,
 	FIND_METHOD(, getTextFromClipboard, "()Ljava/lang/String;");
 	FIND_METHOD(, setTextInClipboard, "(Ljava/lang/String;)Z");
 	FIND_METHOD(, isConnectionLimited, "()Z");
+	FIND_METHOD(, setWindowCaption, "(Ljava/lang/String;)V");
 	FIND_METHOD(, showVirtualKeyboard, "(Z)V");
 	FIND_METHOD(, showOnScreenControls, "(I)V");
 	FIND_METHOD(, setTouchMode, "(I)V");
diff --git a/backends/platform/android/org/scummvm/scummvm/SAFFSTree.java b/backends/platform/android/org/scummvm/scummvm/SAFFSTree.java
index 1eaece8eb58..fd82762e3b0 100644
--- a/backends/platform/android/org/scummvm/scummvm/SAFFSTree.java
+++ b/backends/platform/android/org/scummvm/scummvm/SAFFSTree.java
@@ -33,6 +33,7 @@ import android.provider.DocumentsContract;
 import android.system.OsConstants;
 import android.util.Log;
 
+import androidx.annotation.Keep;
 import androidx.annotation.RequiresApi;
 
 import java.io.FileNotFoundException;
@@ -220,6 +221,7 @@ public class SAFFSTree {
 	/** @noinspection unused
 	 * This version is used by the C++ side
 	 */
+	@Keep
 	public static void addNodeRef(long nodeId) {
 		assert(nodeId != 0);
 		SAFFSNode node = _nodes.get(nodeId);
@@ -232,6 +234,7 @@ public class SAFFSTree {
 	/** @noinspection unused
 	 * This version is used by the C++ side
 	 */
+	@Keep
 	public static void decNodeRef(long nodeId) {
 		assert(nodeId != 0);
 		SAFFSNode node = _nodes.get(nodeId);
@@ -243,6 +246,7 @@ public class SAFFSTree {
 	/** @noinspection unused
 	 * This version is used by the C++ side
 	 */
+	@Keep
 	public static SAFFSNode refToNode(long nodeId) {
 		assert(nodeId != 0);
 		return _nodes.get(nodeId);
@@ -255,9 +259,25 @@ public class SAFFSTree {
 		public static final int DELETABLE = 0x08;
 		public static final int REMOVABLE = 0x10;
 
+		/** @noinspection unused
+		 * Read by the C++ side
+		 */
+		@Keep
 		public SAFFSNode _parent;
+		/** @noinspection unused
+		 * Read by the C++ side
+		 */
+		@Keep
 		public String _path;
+		/** @noinspection unused
+		 * Read by the C++ side
+		 */
+		@Keep
 		public String _documentId;
+		/** @noinspection unused
+		 * Read by the C++ side
+		 */
+		@Keep
 		public int _flags;
 
 		private HashMap<String, SoftReference<SAFFSNode>> _children;
@@ -303,6 +323,10 @@ public class SAFFSTree {
 			return _path.compareTo(o._path);
 		}
 
+		/** @noinspection unused
+		 * Called by the C++ side
+		 */
+		@Keep
 		public synchronized long addRef() {
 			_refCnt += 1;
 			if (_refCnt > 1) {
@@ -330,7 +354,15 @@ public class SAFFSTree {
 	private final Context _context;
 	private final Uri _treeUri;
 
+	/** @noinspection unused
+	 * Read by the C++ side
+	 */
+	@Keep
 	private final SAFFSNode _root;
+	/** @noinspection unused
+	 * Read by the C++ side
+	 */
+	@Keep
 	private final String _treeName;
 
 	public SAFFSTree(Context context, Uri treeUri) {
@@ -348,6 +380,10 @@ public class SAFFSTree {
 		_treeName = treeName;
 	}
 
+	/** @noinspection unused
+	 * Called by the C++ side
+	 */
+	@Keep
 	public String getTreeId() {
 		return Uri.encode(DocumentsContract.getTreeDocumentId(_treeUri));
 	}
@@ -376,6 +412,10 @@ public class SAFFSTree {
 		}
 	}
 
+	/** @noinspection unused
+	 * Called by the C++ side
+	 */
+	@Keep
 	public SAFFSNode pathToNode(String path, boolean createDirIfNotExists) {
 		String[] components = path.split("/");
 
@@ -436,6 +476,7 @@ public class SAFFSTree {
 	/** @noinspection unused
 	 * This version is used by the C++ side
 	 */
+	@Keep
 	public SAFFSNode[] getChildren(long nodeId) {
 		SAFFSNode node = _nodes.get(nodeId);
 		assert(node != null);
@@ -557,6 +598,7 @@ public class SAFFSTree {
 	/** @noinspection unused
 	 * This version is used by the C++ side
 	 */
+	@Keep
 	public SAFFSNode getChild(long nodeId, String name) {
 		SAFFSNode node = _nodes.get(nodeId);
 		assert(node != null);
@@ -571,6 +613,7 @@ public class SAFFSTree {
 	/** @noinspection unused
 	 * This version is used by the C++ side
 	 */
+	@Keep
 	public SAFFSNode createDirectory(long nodeId, String name) {
 		SAFFSNode node = _nodes.get(nodeId);
 		assert(node != null);
@@ -585,6 +628,7 @@ public class SAFFSTree {
 	/** @noinspection unused
 	 * This version is used by the C++ side
 	 */
+	@Keep
 	public SAFFSNode createFile(long nodeId, String name) {
 		SAFFSNode node = _nodes.get(nodeId);
 		assert(node != null);
@@ -599,6 +643,7 @@ public class SAFFSTree {
 	/** @noinspection unused
 	 * This version is used by the C++ side
 	 */
+	@Keep
 	public int createReadStream(long nodeId) {
 		SAFFSNode node = _nodes.get(nodeId);
 		assert(node != null);
@@ -613,6 +658,7 @@ public class SAFFSTree {
 	/** @noinspection unused
 	 * This version is used by the C++ side
 	 */
+	@Keep
 	public int createWriteStream(long nodeId) {
 		SAFFSNode node = _nodes.get(nodeId);
 		assert(node != null);
@@ -666,6 +712,7 @@ public class SAFFSTree {
 	/** @noinspection unused
 	 * This version is used by the C++ side
 	 */
+	@Keep
 	public int removeNode(long nodeId) {
 		SAFFSNode node = _nodes.get(nodeId);
 		assert(node != null);
@@ -676,6 +723,7 @@ public class SAFFSTree {
 	/** @noinspection unused
 	 * This version is used by the C++ side
 	 */
+	@Keep
 	public void removeTree() {
 		final ContentResolver resolver = _context.getContentResolver();
 
diff --git a/backends/platform/android/org/scummvm/scummvm/ScummVM.java b/backends/platform/android/org/scummvm/scummvm/ScummVM.java
index a768d488655..76ee0720519 100644
--- a/backends/platform/android/org/scummvm/scummvm/ScummVM.java
+++ b/backends/platform/android/org/scummvm/scummvm/ScummVM.java
@@ -26,6 +26,7 @@ import android.graphics.PixelFormat;
 import android.util.Log;
 import android.view.SurfaceHolder;
 
+import androidx.annotation.Keep;
 import androidx.annotation.NonNull;
 
 import java.util.LinkedHashMap;
@@ -90,30 +91,53 @@ public abstract class ScummVM implements SurfaceHolder.Callback,
 	final public native void systemInsetsUpdated(int[] gestureInsets, int[] systemInsets, int[] cutoutInsets);
 
 	// Callbacks from C++ peer instance
+	/** @noinspection unused */ @Keep
 	abstract protected void getDPI(float[] values);
+	/** @noinspection unused */ @Keep
 	abstract protected void displayMessageOnOSD(String msg);
+	/** @noinspection unused */ @Keep
 	abstract protected void openUrl(String url);
+	/** @noinspection unused */ @Keep
 	abstract protected boolean hasTextInClipboard();
+	/** @noinspection unused */ @Keep
 	abstract protected String getTextFromClipboard();
+	/** @noinspection unused */ @Keep
 	abstract protected boolean setTextInClipboard(String text);
+	/** @noinspection unused */ @Keep
 	abstract protected boolean isConnectionLimited();
+	/** @noinspection unused */ @Keep
 	abstract protected void setWindowCaption(String caption);
+	/** @noinspection unused */ @Keep
 	abstract protected void showVirtualKeyboard(boolean enable);
+	/** @noinspection unused */ @Keep
 	abstract protected void showOnScreenControls(int enableMask);
+	/** @noinspection unused */ @Keep
 	abstract protected void setTouchMode(int touchMode);
+	/** @noinspection unused */ @Keep
 	abstract protected int getTouchMode();
+	/** @noinspection unused */ @Keep
 	abstract protected void setOrientation(int orientation);
+	/** @noinspection unused */ @Keep
 	abstract protected String getScummVMBasePath();
+	/** @noinspection unused */ @Keep
 	abstract protected String getScummVMConfigPath();
+	/** @noinspection unused */ @Keep
 	abstract protected String getScummVMLogPath();
+	/** @noinspection unused */ @Keep
 	abstract protected void setCurrentGame(String target);
+	/** @noinspection unused */ @Keep
 	abstract protected String[] getSysArchives();
+	/** @noinspection unused */ @Keep
 	abstract protected String[] getAllStorageLocations();
-	abstract protected String[] getAllStorageLocationsNoPermissionRequest();
+	/** @noinspection unused */ @Keep
 	abstract protected SAFFSTree getNewSAFTree(boolean write, String initialURI, String prompt);
+	/** @noinspection unused */ @Keep
 	abstract protected SAFFSTree[] getSAFTrees();
+	/** @noinspection unused */ @Keep
 	abstract protected SAFFSTree findSAFTree(String name);
+	/** @noinspection unused */ @Keep
 	abstract protected int exportBackup(String prompt);
+	/** @noinspection unused */ @Keep
 	abstract protected int importBackup(String prompt, String path);
 
 	public ScummVM(AssetManager asset_manager, SurfaceHolder holder, final MyScummVMDestroyedCallback scummVMDestroyedCallback) {
@@ -245,7 +269,10 @@ public abstract class ScummVM implements SurfaceHolder.Callback,
 												_egl.eglGetError()));
 	}
 
-	// Callback from C++ peer instance
+	/** @noinspection unused
+	 * Callback from C++ peer instance
+	 */
+	@Keep
 	final protected EGLSurface initSurface() throws Exception {
 		_egl_surface = _egl.eglCreateWindowSurface(_egl_display, _egl_config,
 													_surface_holder, null);
@@ -269,7 +296,10 @@ public abstract class ScummVM implements SurfaceHolder.Callback,
 		return _egl_surface;
 	}
 
-	// Callback from C++ peer instance
+	/** @noinspection unused
+	 * Callback from C++ peer instance
+	 */
+	@Keep
 	final protected void deinitSurface() {
 		if (_egl_display != EGL10.EGL_NO_DISPLAY) {
 			_egl.eglMakeCurrent(_egl_display, EGL10.EGL_NO_SURFACE,
@@ -282,7 +312,10 @@ public abstract class ScummVM implements SurfaceHolder.Callback,
 		_egl_surface = EGL10.EGL_NO_SURFACE;
 	}
 
-	// Callback from C++ peer instance
+	/** @noinspection unused
+	 * Callback from C++ peer instance
+	 */
+	@Keep
 	final protected int eglVersion() {
 		String version = _egl.eglQueryString(_egl_display, EGL10.EGL_VERSION);
 		if (version == null) {
diff --git a/backends/platform/android/org/scummvm/scummvm/ScummVMActivity.java b/backends/platform/android/org/scummvm/scummvm/ScummVMActivity.java
index 7288d333443..bfb18e1568a 100644
--- a/backends/platform/android/org/scummvm/scummvm/ScummVMActivity.java
+++ b/backends/platform/android/org/scummvm/scummvm/ScummVMActivity.java
@@ -914,7 +914,6 @@ public class ScummVMActivity extends Activity {
 			return new String[0]; // an array of zero length
 		}
 
-		@Override
 		protected String[] getAllStorageLocationsNoPermissionRequest() {
 			if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M
 				|| checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED
diff --git a/backends/platform/android/org/scummvm/scummvm/net/HTTPManager.java b/backends/platform/android/org/scummvm/scummvm/net/HTTPManager.java
index e056778d409..decc98f60d3 100644
--- a/backends/platform/android/org/scummvm/scummvm/net/HTTPManager.java
+++ b/backends/platform/android/org/scummvm/scummvm/net/HTTPManager.java
@@ -21,6 +21,8 @@
 
 package org.scummvm.scummvm.net;
 
+import androidx.annotation.Keep;
+
 import java.util.concurrent.ArrayBlockingQueue;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
@@ -28,11 +30,16 @@ import java.util.concurrent.Executors;
 public class HTTPManager {
 	protected ExecutorService _executor;
 	protected ArrayBlockingQueue<Runnable> _queue;
+	/** @noinspection unused
+	 * Read from JNI (timer ScummVM thread)
+	 */
+	@Keep
 	protected boolean _empty;
 
 	/** @noinspection unused
 	 * Called from JNI (main ScummVM thread)
 	 */
+	@Keep
 	public HTTPManager() {
 		TLSSocketFactory.init();
 
@@ -45,6 +52,7 @@ public class HTTPManager {
 	/** @noinspection unused
 	 * Called from JNI (main ScummVM thread)
 	 */
+	@Keep
 	public void startRequest(HTTPRequest request) {
 		request._manager = this;
 		_executor.execute(request);
@@ -53,6 +61,7 @@ public class HTTPManager {
 	/** @noinspection unused
 	 * Called from JNI (main ScummVM thread)
 	 */
+	@Keep
 	public void poll() {
 		Runnable r;
 		while((r = _queue.poll()) != null) {
diff --git a/backends/platform/android/org/scummvm/scummvm/net/HTTPRequest.java b/backends/platform/android/org/scummvm/scummvm/net/HTTPRequest.java
index 99e66b7f0c5..f63b9677dc2 100644
--- a/backends/platform/android/org/scummvm/scummvm/net/HTTPRequest.java
+++ b/backends/platform/android/org/scummvm/scummvm/net/HTTPRequest.java
@@ -25,6 +25,8 @@ import android.os.Build;
 import android.os.ParcelFileDescriptor;
 import android.util.Log;
 
+import androidx.annotation.Keep;
+
 import org.scummvm.scummvm.SAFFSTree;
 
 import java.io.ByteArrayInputStream;
@@ -71,6 +73,7 @@ public class HTTPRequest implements Runnable {
 	/** @noinspection unused
 	 * Called from JNI
 	 */
+	@Keep
 	public HTTPRequest(long nativePointer, String url, String[] requestHeaders, byte[] uploadBuffer, boolean uploading, boolean usingPatch, boolean post) {
 		init(nativePointer, url);
 		setupUploadBuffer(uploadBuffer, uploading, usingPatch, post);
@@ -80,6 +83,7 @@ public class HTTPRequest implements Runnable {
 	/** @noinspection unused
 	 * Called from JNI
 	 */
+	@Keep
 	public HTTPRequest(long nativePointer, String url, String[] requestHeaders, String[] formFields, String[] formFiles) {
 		init(nativePointer, url);
 		setupMultipartForm(formFields, formFiles);
@@ -221,12 +225,17 @@ public class HTTPRequest implements Runnable {
 	/** @noinspection unused
 	 * Called from JNI
 	 */
+	@Keep
 	public void cancel() {
 		_cancelled.set(true);
 		// Don't notify the native object if we got cancelled: it may have been reused
 		_nativePointer = 0;
 	}
 
+	/** @noinspection unused
+	 * Called from JNI
+	 */
+	@Keep
 	public String getURL() {
 		return _url.get();
 	}
diff --git a/backends/platform/android/org/scummvm/scummvm/net/SSocket.java b/backends/platform/android/org/scummvm/scummvm/net/SSocket.java
index ddc9ea74d05..78bff683f56 100644
--- a/backends/platform/android/org/scummvm/scummvm/net/SSocket.java
+++ b/backends/platform/android/org/scummvm/scummvm/net/SSocket.java
@@ -23,6 +23,8 @@ package org.scummvm.scummvm.net;
 
 import android.util.Log;
 
+import androidx.annotation.Keep;
+
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
@@ -43,7 +45,9 @@ import java.util.Locale;
 
 import javax.net.ssl.SSLSocketFactory;
 
-/** @noinspection unused*/
+/** @noinspection unused
+ * Used from JNI
+ */
 public class SSocket {
 	final static String LOG_TAG = "ScummVM";
 
@@ -51,6 +55,10 @@ public class SSocket {
 
 	protected int _buffer = -2;
 
+	/** @noinspection unused
+	 * Called from JNI
+	 */
+	@Keep
 	public SSocket(String url_) {
 		final URL url;
 		try {
@@ -199,6 +207,10 @@ public class SSocket {
 		}
 	}
 
+	/** @noinspection unused
+	 * Called from JNI
+	 */
+	@Keep
 	public int ready() {
 		if (_buffer != -2) {
 			// We have at least one byte or an EOF
@@ -219,6 +231,10 @@ public class SSocket {
 		}
 	}
 
+	/** @noinspection unused
+	 * Called from JNI
+	 */
+	@Keep
 	public int send(byte[] data) {
 		try {
 			// Setup unlimited read timeout to allow for SSL exchanges to work
@@ -232,6 +248,10 @@ public class SSocket {
 		}
 	}
 
+	/** @noinspection unused
+	 * Called from JNI
+	 */
+	@Keep
 	public int recv(byte[] data) {
 		if (data.length == 0) {
 			return 0;
@@ -277,6 +297,10 @@ public class SSocket {
 		}
 	}
 
+	/** @noinspection unused
+	 * Called from JNI
+	 */
+	@Keep
 	public void close() {
 		try {
 			_socket.close();



From noreply at scummvm.org  Sun Jun 21 11:58:39 2026
From: noreply at scummvm.org (sev-)
Date: Sun, 21 Jun 2026 11:58:39 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 d5539febcca68dafd0048e68066eb7169834db7e
Message-ID: <20260621115839.233107005CE@ron.scummvm.net>

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

Summary:
d5539febcc DISTS: ANDROID: Update German release notes


Commit: d5539febcca68dafd0048e68066eb7169834db7e
    https://github.com/scummvm/scummvm/commit/d5539febcca68dafd0048e68066eb7169834db7e
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2026-06-21T13:58:05+02:00

Commit Message:
DISTS: ANDROID: Update German release notes

Changed paths:
    dists/android/store/metadata-de-DE.txt


diff --git a/dists/android/store/metadata-de-DE.txt b/dists/android/store/metadata-de-DE.txt
index ea0c21b7f21..544ba39d749 100644
--- a/dists/android/store/metadata-de-DE.txt
+++ b/dists/android/store/metadata-de-DE.txt
@@ -24,7 +24,7 @@ https://forums.scummvm.org/viewforum.php?f=17 ist unser Forum, wo du dich mit an
 2026.2.0
 
 *** Release notes:
-??? Ein Absturz beim schnellen Neustart der App wurde behoben.
-??? Ein Cloud-bezogener Absturz wurde behoben.
+- Das Soundsystem wurde ??berarbeitet und verwendet nun die Obon-Audiobibliothek.
+- Ein Absturz im Zusammenhang mit der Cloud-Speicherung auf OneDrive wurde behoben.
 
-Vollst??ndige Versionshinweise: https://scummvm.org/frs/scummvm/2026.2.0/ReleaseNotes.html
+Vollst??ndige Versionshinweise: https://scummvm.org/frs/scummvm/2026.3.0/ReleaseNotes.html



From noreply at scummvm.org  Sun Jun 21 13:10:14 2026
From: noreply at scummvm.org (sev-)
Date: Sun, 21 Jun 2026 13:10:14 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 3a8bcd751f1bf05575e70e2c64361983e3c8cf3e
Message-ID: <20260621131014.14ACB7005CE@ron.scummvm.net>

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

Summary:
3a8bcd751f DIRECTOR: Seed RNG from system time instead of a fixed value


Commit: 3a8bcd751f1bf05575e70e2c64361983e3c8cf3e
    https://github.com/scummvm/scummvm/commit/3a8bcd751f1bf05575e70e2c64361983e3c8cf3e
Author: Zachary Berry (zberry92 at gmail.com)
Date: 2026-06-21T15:10:09+02:00

Commit Message:
DIRECTOR: Seed RNG from system time instead of a fixed value

Director seeds its RNG by time from the classic MacOS epoch. Use
this instead of the hard coded 1 orginally implemented unless
the seed is set in the GUI.

This fixes RNG in games that do not explicitly call
`set the randomSeed`

Changed paths:
    engines/director/util.cpp
    engines/director/util.h


diff --git a/engines/director/util.cpp b/engines/director/util.cpp
index c65c3ee66c7..6aaa3001db0 100644
--- a/engines/director/util.cpp
+++ b/engines/director/util.cpp
@@ -25,7 +25,9 @@
 #include "common/memstream.h"
 #include "common/punycode.h"
 #include "common/str-array.h"
+#include "common/system.h"
 #include "common/tokenizer.h"
+#include "common/util.h"
 #include "common/xpfloat.h"
 #include "common/compression/deflate.h"
 
@@ -1203,7 +1205,19 @@ Common::Path dumpFactoryName(const char *prefix, const char *name, const char *e
 	return Common::Path(Common::String::format("./dumps/%s-factory-%s.%s", prefix, name, ext), '/');
 }
 
-void RandomState::setSeed(int seed, bool runInit) {
+// Seconds between the classic Mac OS epoch and the Unix epoch.
+static const uint32 kMacEpochOffset = 2082844800U;
+
+uint32 macTimeSeed() {
+	// Director seeds its RNG with the local wall-clock time in seconds since
+	// the classic Mac OS epoch.
+	TimeDate td;
+	g_system->getTimeAndDate(td);
+
+	return (uint32)(Common::DateTime::dateTimeToInt64(td) + kMacEpochOffset);
+}
+
+void RandomState::setSeed(uint32 seed, bool runInit) {
 	if (runInit)
 		init(32);
 
@@ -1245,9 +1259,7 @@ void RandomState::init(int len) {
 		_len = (1 << len) - 1;
 	}
 
-	// The original is _always_ initalized with a seed of 1. It is hardcoded and is by design
-	// The developers were offered to use `set the randomSeed` Lingo
-	setSeed(1, false);
+	setSeed(macTimeSeed(), false);
 	_mask = masks[len - 2];
 }
 
diff --git a/engines/director/util.h b/engines/director/util.h
index 8d5e746c6e9..661346710df 100644
--- a/engines/director/util.h
+++ b/engines/director/util.h
@@ -71,6 +71,8 @@ Common::Path dumpFactoryName(const char *prefix, const char *name, const char *e
 
 bool isButtonSprite(SpriteType spriteType);
 
+uint32 macTimeSeed();
+
 class RandomState {
 public:
 	uint32 _seed;
@@ -81,7 +83,7 @@ public:
 		_seed = _mask = _len = 0;
 	}
 
-	void setSeed(int seed, bool runInit = true);
+	void setSeed(uint32 seed, bool runInit = true);
 	uint32 getSeed() { return _seed; }
 	int32 getRandom(int32 range);
 



From noreply at scummvm.org  Sun Jun 21 13:17:43 2026
From: noreply at scummvm.org (sev-)
Date: Sun, 21 Jun 2026 13:17:43 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 85befa9c13c0cf953e63f79e67b605a00d5e8fa4
Message-ID: <20260621131743.A5CFC7005CE@ron.scummvm.net>

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

Summary:
ccbcb756b0 AUDIO: Fix constness
acab1a9175 AUDIO: Input stream always works with int16
d440280f0e SCI32: Don't use Audio::st_sample_t for AudioStream instances
a6d828135f AUDIO: Add support for 32-bit sample size
b9cca6360e AUDIO: RateConverter now supports unclamped mixing
424681dca2 AUDIO: Make sample clamping optional
995c360cfc AUDIO: Don't clamp muted channels
85befa9c13 AUDIO: Template-optimize convert methods


Commit: ccbcb756b0e98088d456843186175015704f28f7
    https://github.com/scummvm/scummvm/commit/ccbcb756b0e98088d456843186175015704f28f7
Author: Miro Kropacek (miro.kropacek at gmail.com)
Date: 2026-06-21T15:17:36+02:00

Commit Message:
AUDIO: Fix constness

Changed paths:
    audio/mixer.cpp
    audio/mixer.h
    audio/mixer_intern.h


diff --git a/audio/mixer.cpp b/audio/mixer.cpp
index 4791628bbac..51ea508d56f 100644
--- a/audio/mixer.cpp
+++ b/audio/mixer.cpp
@@ -98,7 +98,7 @@ public:
 	 *
 	 * @return volume
 	 */
-	byte getVolume();
+	byte getVolume() const;
 
 	/**
 	 * Sets the channel's balance setting.
@@ -112,7 +112,7 @@ public:
 	 *
 	 * @return balance
 	 */
-	int8 getBalance();
+	int8 getBalance() const;
 
 	/**
 	 * Sets the channel's left fader level.
@@ -126,7 +126,7 @@ public:
 	 *
 	 * @return The channel's left fader level.
 	 */
-	uint8 getFaderL();
+	uint8 getFaderL() const;
 
 	/**
 	 * Sets the channel's right fader level.
@@ -140,7 +140,7 @@ public:
 	 *
 	 * @return The channel's right fader level.
 	 */
-	uint8 getFaderR();
+	uint8 getFaderR() const;
 
 	/**
 	 * Set the channel's sample rate.
@@ -154,7 +154,7 @@ public:
 	 *
 	 * @return The current sample rate of the channel.
 	 */
-	uint32 getRate();
+	uint32 getRate() const;
 
 	/**
 	 * Reset the sample rate of the channel back to its
@@ -171,7 +171,7 @@ public:
 	/**
 	 * Queries how long the channel has been playing.
 	 */
-	Timestamp getElapsedTime();
+	Timestamp getElapsedTime() const;
 
 	/**
 	 * Replaces the channel's stream with a version that loops indefinitely.
@@ -425,7 +425,7 @@ void MixerImpl::setChannelVolume(SoundHandle handle, byte volume) {
 	_channels[index]->setVolume(volume);
 }
 
-byte MixerImpl::getChannelVolume(SoundHandle handle) {
+byte MixerImpl::getChannelVolume(SoundHandle handle) const {
 	const int index = handle._val % NUM_CHANNELS;
 	if (!_channels[index] || _channels[index]->getHandle()._val != handle._val)
 		return 0;
@@ -443,7 +443,7 @@ void MixerImpl::setChannelBalance(SoundHandle handle, int8 balance) {
 	_channels[index]->setBalance(balance);
 }
 
-int8 MixerImpl::getChannelBalance(SoundHandle handle) {
+int8 MixerImpl::getChannelBalance(SoundHandle handle) const {
 	const int index = handle._val % NUM_CHANNELS;
 	if (!_channels[index] || _channels[index]->getHandle()._val != handle._val)
 		return 0;
@@ -461,7 +461,7 @@ void MixerImpl::setChannelFaderL(SoundHandle handle, uint8 faderL) {
 	_channels[index]->setFaderL(faderL);
 }
 
-uint8 MixerImpl::getChannelFaderL(SoundHandle handle) {
+uint8 MixerImpl::getChannelFaderL(SoundHandle handle) const {
 	const int index = handle._val % NUM_CHANNELS;
 	if (!_channels[index] || _channels[index]->getHandle()._val != handle._val)
 		return 0;
@@ -479,7 +479,7 @@ void MixerImpl::setChannelFaderR(SoundHandle handle, uint8 faderR) {
 	_channels[index]->setFaderR(faderR);
 }
 
-uint8 MixerImpl::getChannelFaderR(SoundHandle handle) {
+uint8 MixerImpl::getChannelFaderR(SoundHandle handle) const {
 	const int index = handle._val % NUM_CHANNELS;
 	if (!_channels[index] || _channels[index]->getHandle()._val != handle._val)
 		return 0;
@@ -497,7 +497,7 @@ void MixerImpl::setChannelRate(SoundHandle handle, uint32 rate) {
 	_channels[index]->setRate(rate);
 }
 
-uint32 MixerImpl::getChannelRate(SoundHandle handle) {
+uint32 MixerImpl::getChannelRate(SoundHandle handle) const {
 	const int index = handle._val % NUM_CHANNELS;
 	if (!_channels[index] || _channels[index]->getHandle()._val != handle._val)
 		return 0;
@@ -515,11 +515,11 @@ void MixerImpl::resetChannelRate(SoundHandle handle) {
 	_channels[index]->resetRate();
 }
 
-uint32 MixerImpl::getSoundElapsedTime(SoundHandle handle) {
+uint32 MixerImpl::getSoundElapsedTime(SoundHandle handle) const {
 	return getElapsedTime(handle).msecs();
 }
 
-Timestamp MixerImpl::getElapsedTime(SoundHandle handle) {
+Timestamp MixerImpl::getElapsedTime(SoundHandle handle) const {
 	Common::StackLock lock(_mutex);
 
 	const int index = handle._val % NUM_CHANNELS;
@@ -569,7 +569,7 @@ void MixerImpl::pauseHandle(SoundHandle handle, bool paused) {
 	_channels[index]->pause(paused);
 }
 
-bool MixerImpl::isSoundIDActive(int id) {
+bool MixerImpl::isSoundIDActive(int id) const {
 	Common::StackLock lock(_mutex);
 
 #ifdef ENABLE_EVENTRECORDER
@@ -582,7 +582,7 @@ bool MixerImpl::isSoundIDActive(int id) {
 	return false;
 }
 
-int MixerImpl::getSoundID(SoundHandle handle) {
+int MixerImpl::getSoundID(SoundHandle handle) const {
 	Common::StackLock lock(_mutex);
 	const int index = handle._val % NUM_CHANNELS;
 	if (_channels[index] && _channels[index]->getHandle()._val == handle._val)
@@ -590,7 +590,7 @@ int MixerImpl::getSoundID(SoundHandle handle) {
 	return 0;
 }
 
-bool MixerImpl::isSoundHandleActive(SoundHandle handle) {
+bool MixerImpl::isSoundHandleActive(SoundHandle handle) const {
 	Common::StackLock lock(_mutex);
 
 #ifdef ENABLE_EVENTRECORDER
@@ -601,7 +601,7 @@ bool MixerImpl::isSoundHandleActive(SoundHandle handle) {
 	return _channels[index] && _channels[index]->getHandle()._val == handle._val;
 }
 
-bool MixerImpl::hasActiveChannelOfType(SoundType type) {
+bool MixerImpl::hasActiveChannelOfType(SoundType type) const {
 	Common::StackLock lock(_mutex);
 	for (int i = 0; i != NUM_CHANNELS; i++)
 		if (_channels[i] && _channels[i]->getType() == type)
@@ -660,7 +660,7 @@ void Channel::setVolume(const byte volume) {
 	updateChannelVolumes();
 }
 
-byte Channel::getVolume() {
+byte Channel::getVolume() const {
 	return _volume;
 }
 
@@ -669,7 +669,7 @@ void Channel::setBalance(const int8 balance) {
 	updateChannelVolumes();
 }
 
-int8 Channel::getBalance() {
+int8 Channel::getBalance() const {
 	return _balance;
 }
 
@@ -678,7 +678,7 @@ void Channel::setFaderL(uint8 faderL) {
 	updateChannelVolumes();
 }
 
-uint8 Channel::getFaderL() {
+uint8 Channel::getFaderL() const {
 	return _faderL;
 }
 
@@ -687,7 +687,7 @@ void Channel::setFaderR(uint8 faderR) {
 	updateChannelVolumes();
 }
 
-uint8 Channel::getFaderR() {
+uint8 Channel::getFaderR() const {
 	return _faderR;
 }
 
@@ -696,7 +696,7 @@ void Channel::setRate(uint32 rate) {
 		_converter->setInputRate(rate);
 }
 
-uint32 Channel::getRate() {
+uint32 Channel::getRate() const {
 	if (_converter)
 		return _converter->getInputRate();
 
@@ -756,7 +756,7 @@ void Channel::pause(bool paused) {
 	}
 }
 
-Timestamp Channel::getElapsedTime() {
+Timestamp Channel::getElapsedTime() const {
 	const uint32 rate = _mixer->getOutputRate();
 	uint32 delta = 0;
 
diff --git a/audio/mixer.h b/audio/mixer.h
index 4e34879c23a..2c6c35f07b2 100644
--- a/audio/mixer.h
+++ b/audio/mixer.h
@@ -192,7 +192,7 @@ public:
 	 *
 	 * @return True if the sound is active.
 	 */
-	virtual bool isSoundIDActive(int id) = 0;
+	virtual bool isSoundIDActive(int id) const = 0;
 
 	/**
 	 * Get the sound ID for the given handle.
@@ -201,7 +201,7 @@ public:
 	 *
 	 * @return Sound ID if the sound is active.
 	 */
-	virtual int getSoundID(SoundHandle handle) = 0;
+	virtual int getSoundID(SoundHandle handle) const = 0;
 
 	/**
 	 * Check whether a sound with the given handle is active.
@@ -210,7 +210,7 @@ public:
 	 *
 	 * @return True if the sound is active.
 	 */
-	virtual bool isSoundHandleActive(SoundHandle handle) = 0;
+	virtual bool isSoundHandleActive(SoundHandle handle) const = 0;
 
 
 	/**
@@ -243,7 +243,7 @@ public:
 	 *
 	 * @return The channel volume.
 	 */
-	virtual byte getChannelVolume(SoundHandle handle) = 0;
+	virtual byte getChannelVolume(SoundHandle handle) const = 0;
 
 	/**
 	 * Set the channel balance for the given handle.
@@ -261,7 +261,7 @@ public:
 	 *
 	 * @return The channel balance.
 	 */
-	virtual int8 getChannelBalance(SoundHandle handle) = 0;
+	virtual int8 getChannelBalance(SoundHandle handle) const = 0;
 
 	/**
 	 * Set the channel's left fader level for the given handle.
@@ -278,7 +278,7 @@ public:
 	 *
 	 * @return The channel's left fader level.
 	 */
-	virtual uint8 getChannelFaderL(SoundHandle handle) = 0;
+	virtual uint8 getChannelFaderL(SoundHandle handle) const = 0;
 
 	/**
 	 * Set the channel's right fader level for the given handle.
@@ -295,7 +295,7 @@ public:
 	 *
 	 * @return The channel's right fader level.
 	 */
-	virtual uint8 getChannelFaderR(SoundHandle handle) = 0;
+	virtual uint8 getChannelFaderR(SoundHandle handle) const = 0;
 
 	/**
 	 * Set the sample rate for the given handle.
@@ -312,7 +312,7 @@ public:
 	 *
 	 * @return The current sample rate of the channel.
 	*/
-	virtual uint32 getChannelRate(SoundHandle handle) = 0;
+	virtual uint32 getChannelRate(SoundHandle handle) const = 0;
 
 	/**
 	 * Reset the sample rate of the channel back to its
@@ -325,12 +325,12 @@ public:
 	/**
 	 * Get an approximation of for how long the channel has been playing.
 	 */
-	virtual uint32 getSoundElapsedTime(SoundHandle handle) = 0;
+	virtual uint32 getSoundElapsedTime(SoundHandle handle) const = 0;
 
 	/**
 	 * Get an approximation of for how long the channel has been playing.
 	 */
-	virtual Timestamp getElapsedTime(SoundHandle handle) = 0;
+	virtual Timestamp getElapsedTime(SoundHandle handle) const = 0;
 
 	/**
 	 * Replace the channel's stream with a version that loops indefinitely.
@@ -347,7 +347,7 @@ public:
 	 *
 	 * @return True if any channels of the specified type are active.
 	 */
-	virtual bool hasActiveChannelOfType(SoundType type) = 0;
+	virtual bool hasActiveChannelOfType(SoundType type) const = 0;
 
 	/**
 	 * Set the volume for the given sound type.
diff --git a/audio/mixer_intern.h b/audio/mixer_intern.h
index 409315c3c6f..c46a43825ac 100644
--- a/audio/mixer_intern.h
+++ b/audio/mixer_intern.h
@@ -106,32 +106,32 @@ public:
 	void pauseID(int id, bool paused) override;
 	void pauseHandle(SoundHandle handle, bool paused) override;
 
-	bool isSoundIDActive(int id) override;
-	int getSoundID(SoundHandle handle) override;
+	bool isSoundIDActive(int id) const override;
+	int getSoundID(SoundHandle handle) const override;
 
-	bool isSoundHandleActive(SoundHandle handle) override;
+	bool isSoundHandleActive(SoundHandle handle) const override;
 
 	void muteSoundType(SoundType type, bool mute) override;
 	bool isSoundTypeMuted(SoundType type) const override;
 
 	void setChannelVolume(SoundHandle handle, byte volume) override;
-	byte getChannelVolume(SoundHandle handle) override;
+	byte getChannelVolume(SoundHandle handle) const override;
 	void setChannelBalance(SoundHandle handle, int8 balance) override;
-	int8 getChannelBalance(SoundHandle handle) override;
+	int8 getChannelBalance(SoundHandle handle) const override;
 	void setChannelFaderL(SoundHandle handle, uint8 scaleL) override;
-	uint8 getChannelFaderL(SoundHandle handle) override;
+	uint8 getChannelFaderL(SoundHandle handle) const override;
 	void setChannelFaderR(SoundHandle handle, uint8 scaleR) override;
-	uint8 getChannelFaderR(SoundHandle handle) override;
+	uint8 getChannelFaderR(SoundHandle handle) const override;
 	void setChannelRate(SoundHandle handle, uint32 rate) override;
-	uint32 getChannelRate(SoundHandle handle) override;
+	uint32 getChannelRate(SoundHandle handle) const override;
 	void resetChannelRate(SoundHandle handle) override;
 
-	uint32 getSoundElapsedTime(SoundHandle handle) override;
-	Timestamp getElapsedTime(SoundHandle handle) override;
+	uint32 getSoundElapsedTime(SoundHandle handle) const override;
+	Timestamp getElapsedTime(SoundHandle handle) const override;
 
 	void loopChannel(SoundHandle handle) override;
 
-	bool hasActiveChannelOfType(SoundType type) override;
+	bool hasActiveChannelOfType(SoundType type) const override;
 
 	void setVolumeForSoundType(SoundType type, int volume) override;
 	int getVolumeForSoundType(SoundType type) const override;


Commit: acab1a917526161c6bb2780ea62a9289f7c32be7
    https://github.com/scummvm/scummvm/commit/acab1a917526161c6bb2780ea62a9289f7c32be7
Author: Miro Kropacek (miro.kropacek at gmail.com)
Date: 2026-06-21T15:17:36+02:00

Commit Message:
AUDIO: Input stream always works with int16

So make it explicit in code.

Changed paths:
    audio/rate.cpp


diff --git a/audio/rate.cpp b/audio/rate.cpp
index 4638f827270..9ae4d3eb3cf 100644
--- a/audio/rate.cpp
+++ b/audio/rate.cpp
@@ -60,10 +60,10 @@ private:
 	 * but only until some point (depends largely on cache size, target
 	 * processor and various other factors), at which it will decrease again.
 	 */
-	st_sample_t _buffer[512];
+	int16 _buffer[512];
 
 	/** Current position inside the buffer */
-	const st_sample_t *_bufferPos;
+	const int16 *_bufferPos;
 
 	/** Size of data currently loaded into the buffer */
 	int _bufferSize;
@@ -75,10 +75,10 @@ private:
 	frac_t _outPosFrac;
 
 	/** Last sample(s) in the input stream (left/right channel) */
-	st_sample_t _inLastL, _inLastR;
-	
+	int16 _inLastL, _inLastR;
+
 	/** Current sample(s) in the input stream (left/right channel) */
-	st_sample_t _inCurL, _inCurR;
+	int16 _inCurL, _inCurR;
 
 	int copyConvert(AudioStream &input, st_sample_t *outBuffer, st_size_t numSamples, st_volume_t vol_l, st_volume_t vol_r);
 	int simpleConvert(AudioStream &input, st_sample_t *outBuffer, st_size_t numSamples, st_volume_t vol_l, st_volume_t vol_r);
@@ -141,7 +141,7 @@ int RateConverter_Impl<inStereo, outStereo, reverseStereo>::copyConvert(AudioStr
 		}
 
 		// Mix the data into the output buffer
-		st_sample_t inL, inR;
+		int16 inL, inR;
 		inL = *_bufferPos++;
 		inR = (inStereo ? *_bufferPos++ : inL);
 		_bufferSize -= (inStereo ? 2 : 1);
@@ -201,7 +201,7 @@ int RateConverter_Impl<inStereo, outStereo, reverseStereo>::simpleConvert(AudioS
 			}
 		} while (_outPos >= 0);
 
-		st_sample_t inL, inR;
+		int16 inL, inR;
 		inL = *_bufferPos++;
 		inR = (inStereo ? *_bufferPos++ : inL);
 
@@ -269,10 +269,10 @@ int RateConverter_Impl<inStereo, outStereo, reverseStereo>::interpolateConvert(A
 		// still space in the output buffer.
 		while (_outPosFrac < (frac_t)FRAC_ONE_LOW && outBuffer < outEnd) {
 			// Interpolate
-			st_sample_t inL, inR;
-			inL = (st_sample_t)(_inLastL + (((_inCurL - _inLastL) * _outPosFrac + FRAC_HALF_LOW) >> FRAC_BITS_LOW));
+			int16 inL, inR;
+			inL = (int16)(_inLastL + (((_inCurL - _inLastL) * _outPosFrac + FRAC_HALF_LOW) >> FRAC_BITS_LOW));
 			inR = (inStereo ?
-						(st_sample_t)(_inLastR + (((_inCurR - _inLastR) * _outPosFrac + FRAC_HALF_LOW) >> FRAC_BITS_LOW)) :
+						(int16)(_inLastR + (((_inCurR - _inLastR) * _outPosFrac + FRAC_HALF_LOW) >> FRAC_BITS_LOW)) :
 						inL);
 
 			st_sample_t outL, outR;


Commit: d440280f0ef0c5ae13d5cf6d0654ab7601b138e9
    https://github.com/scummvm/scummvm/commit/d440280f0ef0c5ae13d5cf6d0654ab7601b138e9
Author: Miro Kropacek (miro.kropacek at gmail.com)
Date: 2026-06-21T15:17:36+02:00

Commit Message:
SCI32: Don't use Audio::st_sample_t for AudioStream instances

AudioStream always works with int16 so make it explicit. The only
exception here is Audio32::writeAudioInternal() where the targetBuffer
actually is expected to be of type Audio::st_sample_t but that is just
Audio32 class taking advantage of an existing converter. It could easily
take byte or void pointer as input.

Changed paths:
    engines/sci/sound/audio32.cpp
    engines/sci/sound/audio32.h
    engines/sci/video/robot_decoder.cpp
    engines/sci/video/robot_decoder.h


diff --git a/engines/sci/sound/audio32.cpp b/engines/sci/sound/audio32.cpp
index b81df60a01f..0b21070ec7c 100644
--- a/engines/sci/sound/audio32.cpp
+++ b/engines/sci/sound/audio32.cpp
@@ -232,7 +232,7 @@ Audio32::~Audio32() {
 #pragma mark -
 #pragma mark AudioStream implementation
 
-int Audio32::writeAudioInternal(Audio::AudioStream &sourceStream, Audio::RateConverter &converter, Audio::st_sample_t *targetBuffer, const int numSamples, const Audio::st_volume_t leftVolume, const Audio::st_volume_t rightVolume) {
+int Audio32::writeAudioInternal(Audio::AudioStream &sourceStream, Audio::RateConverter &converter, int16 *targetBuffer, const int numSamples, const Audio::st_volume_t leftVolume, const Audio::st_volume_t rightVolume) {
 	const int samplePairsToRead = numSamples >> 1;
 	const int samplePairsWritten = converter.convert(sourceStream, targetBuffer, samplePairsToRead, leftVolume, rightVolume);
 	return samplePairsWritten << 1;
@@ -289,7 +289,7 @@ bool Audio32::channelShouldMix(const AudioChannel &channel) const {
 // do all audio processing in real time, and we have streams, and we do not need
 // to completely fill the audio buffer, the functionality of all these original
 // functions is combined here and simplified.
-int Audio32::readBuffer(Audio::st_sample_t *const buffer, const int numSamples) {
+int Audio32::readBuffer(int16 *const buffer, const int numSamples) {
 	Common::StackLock lock(_mutex);
 
 	if (_pausedAtTick != 0 || _numActiveChannels == 0) {
@@ -307,7 +307,7 @@ int Audio32::readBuffer(Audio::st_sample_t *const buffer, const int numSamples)
 	// The caller of `readBuffer` is a rate converter, which reuses (without
 	// clearing) an intermediate buffer, so we need to zero the intermediate
 	// buffer to prevent mixing into audio data from the last callback.
-	memset(buffer, 0, numSamples * sizeof(Audio::st_sample_t));
+	memset(buffer, 0, numSamples * sizeof(int16));
 
 	// This emulates the attenuated mixing mode of SSCI engine, which reduces
 	// the volume of the target buffer when each new channel is mixed in.
@@ -413,12 +413,12 @@ int Audio32::readBuffer(Audio::st_sample_t *const buffer, const int numSamples)
 			if (numSamples > (int)_monitoredBuffer.size()) {
 				_monitoredBuffer.resize(numSamples);
 			}
-			memset(_monitoredBuffer.data(), 0, _monitoredBuffer.size() * sizeof(Audio::st_sample_t));
+			memset(_monitoredBuffer.data(), 0, _monitoredBuffer.size() * sizeof(int16));
 			_numMonitoredSamples = writeAudioInternal(*channel.stream, *channel.converter, _monitoredBuffer.data(), numSamples, leftVolume, rightVolume);
 
-			Audio::st_sample_t *sourceBuffer = _monitoredBuffer.data();
-			Audio::st_sample_t *targetBuffer = buffer;
-			const Audio::st_sample_t *const end = _monitoredBuffer.data() + _numMonitoredSamples;
+			int16 *sourceBuffer = _monitoredBuffer.data();
+			int16 *targetBuffer = buffer;
+			const int16 *const end = _monitoredBuffer.data() + _numMonitoredSamples;
 			while (sourceBuffer != end) {
 				Audio::clampedAdd(*targetBuffer++, *sourceBuffer++);
 			}
@@ -1203,11 +1203,11 @@ bool Audio32::hasSignal() const {
 		return false;
 	}
 
-	const Audio::st_sample_t *buffer = _monitoredBuffer.data();
-	const Audio::st_sample_t *const end = _monitoredBuffer.data() + _numMonitoredSamples;
+	const int16 *buffer = _monitoredBuffer.data();
+	const int16 *const end = _monitoredBuffer.data() + _numMonitoredSamples;
 
 	while (buffer != end) {
-		const Audio::st_sample_t sample = *buffer++;
+		const int16 sample = *buffer++;
 		if (sample > 1280 || sample < -1280) {
 			return true;
 		}
diff --git a/engines/sci/sound/audio32.h b/engines/sci/sound/audio32.h
index cfa8691d027..ee095294dea 100644
--- a/engines/sci/sound/audio32.h
+++ b/engines/sci/sound/audio32.h
@@ -196,7 +196,7 @@ private:
 #pragma mark -
 #pragma mark AudioStream implementation
 public:
-	int readBuffer(Audio::st_sample_t *buffer, const int numSamples) override;
+	int readBuffer(int16 *buffer, const int numSamples) override;
 	bool isStereo() const override { return true; }
 	int getRate() const override { return _mixer->getOutputRate(); }
 	bool endOfData() const override { return _numActiveChannels == 0; }
@@ -219,7 +219,7 @@ private:
 	 * Mixes audio from the given source stream into the target buffer using the
 	 * given rate converter.
 	 */
-	int writeAudioInternal(Audio::AudioStream &sourceStream, Audio::RateConverter &converter, Audio::st_sample_t *targetBuffer, const int numSamples, const Audio::st_volume_t leftVolume, const Audio::st_volume_t rightVolume);
+	int writeAudioInternal(Audio::AudioStream &sourceStream, Audio::RateConverter &converter, int16 *targetBuffer, const int numSamples, const Audio::st_volume_t leftVolume, const Audio::st_volume_t rightVolume);
 
 #pragma mark -
 #pragma mark Channel management
@@ -627,7 +627,7 @@ private:
 	 * The data buffer holding decompressed audio data for the channel that will
 	 * be monitored for an audio signal.
 	 */
-	Common::Array<Audio::st_sample_t> _monitoredBuffer;
+	Common::Array<int16> _monitoredBuffer;
 
 	/**
 	 * The number of valid audio samples in the signal monitoring buffer.
diff --git a/engines/sci/video/robot_decoder.cpp b/engines/sci/video/robot_decoder.cpp
index 7db361bd8fa..0da109dc2fa 100644
--- a/engines/sci/video/robot_decoder.cpp
+++ b/engines/sci/video/robot_decoder.cpp
@@ -299,7 +299,7 @@ RobotAudioStream::StreamState RobotAudioStream::getStatus() const {
 	return status;
 }
 
-int RobotAudioStream::readBuffer(Audio::st_sample_t *outBuffer, int numSamples) {
+int RobotAudioStream::readBuffer(int16 *outBuffer, int numSamples) {
 	Common::StackLock lock(_mutex);
 
 	if (_waiting) {
@@ -307,7 +307,7 @@ int RobotAudioStream::readBuffer(Audio::st_sample_t *outBuffer, int numSamples)
 	}
 
 	assert(!((_writeHeadAbs - _readHeadAbs) & 1));
-	const int maxNumSamples = (_writeHeadAbs - _readHeadAbs) / sizeof(Audio::st_sample_t);
+	const int maxNumSamples = (_writeHeadAbs - _readHeadAbs) / sizeof(int16);
 	numSamples = MIN(numSamples, maxNumSamples);
 
 	if (!numSamples) {
@@ -316,22 +316,22 @@ int RobotAudioStream::readBuffer(Audio::st_sample_t *outBuffer, int numSamples)
 
 	interpolateMissingSamples(numSamples);
 
-	Audio::st_sample_t *inBuffer = (Audio::st_sample_t *)(_loopBuffer + _readHead);
+	int16 *inBuffer = (int16 *)(_loopBuffer + _readHead);
 
 	assert(!((_loopBufferSize - _readHead) & 1));
-	const int numSamplesToEnd = (_loopBufferSize - _readHead) / sizeof(Audio::st_sample_t);
+	const int numSamplesToEnd = (_loopBufferSize - _readHead) / sizeof(int16);
 
 	int numSamplesToRead = MIN(numSamples, numSamplesToEnd);
 	Common::copy(inBuffer, inBuffer + numSamplesToRead, outBuffer);
 
 	if (numSamplesToRead < numSamples) {
-		inBuffer = (Audio::st_sample_t *)_loopBuffer;
+		inBuffer = (int16 *)_loopBuffer;
 		outBuffer += numSamplesToRead;
 		numSamplesToRead = numSamples - numSamplesToRead;
 		Common::copy(inBuffer, inBuffer + numSamplesToRead, outBuffer);
 	}
 
-	const int32 numBytes = numSamples * sizeof(Audio::st_sample_t);
+	const int32 numBytes = numSamples * sizeof(int16);
 
 	_readHead += numBytes;
 	if (_readHead > _loopBufferSize) {
diff --git a/engines/sci/video/robot_decoder.h b/engines/sci/video/robot_decoder.h
index 226854c54f4..2df4985546c 100644
--- a/engines/sci/video/robot_decoder.h
+++ b/engines/sci/video/robot_decoder.h
@@ -433,7 +433,7 @@ private:
 #pragma mark -
 #pragma mark RobotAudioStream - AudioStream implementation
 public:
-	int readBuffer(Audio::st_sample_t *outBuffer, int numSamples) override;
+	int readBuffer(int16 *outBuffer, int numSamples) override;
 	bool isStereo() const override { return false; };
 	int getRate() const override { return 22050; };
 	bool endOfData() const override {


Commit: a6d828135f33c467e9823d790a5af4148f2c0549
    https://github.com/scummvm/scummvm/commit/a6d828135f33c467e9823d790a5af4148f2c0549
Author: Miro Kropacek (miro.kropacek at gmail.com)
Date: 2026-06-21T15:17:36+02:00

Commit Message:
AUDIO: Add support for 32-bit sample size

- RateConverter now accepts 32-bit input (although still with max 24-bit
  values)

- make clampedAdd suited for 8-24 bit samples

Changed paths:
    audio/mixer.cpp
    audio/mixer.h
    audio/mixer_intern.h
    audio/rate.cpp
    audio/rate.h
    engines/sci/sound/audio32.cpp


diff --git a/audio/mixer.cpp b/audio/mixer.cpp
index 51ea508d56f..174fd8f515d 100644
--- a/audio/mixer.cpp
+++ b/audio/mixer.cpp
@@ -49,12 +49,13 @@ public:
 	 * Mixes the channel's samples into the given buffer.
 	 *
 	 * @param data buffer where to mix the data
-	 * @param len  number of sample *pairs*. So a value of
-	 *             10 means that the buffer contains twice 10 sample, each
-	 *             16 bits, for a total of 40 bytes.
+	 * @param len  number of sample *pairs*. So a value of 10
+	 *             in stereo and 16-bit samples means that the
+	 *             buffer contains twice 10 sample, each 16 bits,
+	 *             for a total of 40 bytes.
 	 * @return number of sample pairs processed (which can still be silence!)
 	 */
-	int mix(int16 *data, uint len);
+	int mix(byte *data, uint len);
 
 	/**
 	 * Queries whether the channel is still playing or not.
@@ -226,8 +227,9 @@ private:
 #pragma mark --- Mixer ---
 #pragma mark -
 
-MixerImpl::MixerImpl(uint sampleRate, bool stereo, uint outBufSize)
-	: _mutex(), _sampleRate(sampleRate), _stereo(stereo), _outBufSize(outBufSize), _mixerReady(false), _handleSeed(0), _soundTypeSettings() {
+MixerImpl::MixerImpl(uint sampleRate, bool stereo, uint outBufSize, uint outBytesPerSample)
+	: _mutex(), _sampleRate(sampleRate), _stereo(stereo), _outBufSize(outBufSize), _outBytesPerSample(outBytesPerSample)
+	, _mixerReady(false), _handleSeed(0), _soundTypeSettings() {
 
 	assert(sampleRate > 0);
 
@@ -258,6 +260,10 @@ uint MixerImpl::getOutputBufSize() const {
 	return _outBufSize;
 }
 
+uint MixerImpl::getOutputBytesPerSample() const {
+	return _outBytesPerSample;
+}
+
 void MixerImpl::insertChannel(SoundHandle *handle, Channel *chan) {
 	int index = -1;
 	for (int i = 0; i != NUM_CHANNELS; i++) {
@@ -333,22 +339,16 @@ int MixerImpl::mixCallback(byte *samples, uint len) {
 
 	Common::StackLock lock(_mutex);
 
-	int16 *buf = (int16 *)samples;
-
 	// Since the mixer callback has been called, the mixer must be ready...
 	_mixerReady = true;
 
-	//  zero the buf
-	memset(buf, 0, len);
+	// zero the sample buffer
+	memset(samples, 0, len);
 
-	// we store 16-bit samples
-	if (_stereo) {
-		assert(len % 4 == 0);
-		len >>= 2;
-	} else {
-		assert(len % 2 == 0);
-		len >>= 1;
-	}
+	// we store samples of size defined by the backend
+	const uint bytesPerFrame = _outBytesPerSample * (_stereo ? 2 : 1);
+	assert(len % bytesPerFrame == 0);
+	len /= bytesPerFrame;
 
 	// mix all channels
 	int res = 0, tmp;
@@ -358,7 +358,7 @@ int MixerImpl::mixCallback(byte *samples, uint len) {
 				delete _channels[i];
 				_channels[i] = nullptr;
 			} else if (!_channels[i]->isPaused()) {
-				tmp = _channels[i]->mix(buf, len);
+				tmp = _channels[i]->mix(samples, len);
 
 				if (tmp > res)
 					res = tmp;
@@ -793,7 +793,7 @@ void Channel::loop() {
 	}
 }
 
-int Channel::mix(int16 *data, uint len) {
+int Channel::mix(byte *data, uint len) {
 	assert(_stream);
 	assert(_converter);
 
@@ -802,7 +802,7 @@ int Channel::mix(int16 *data, uint len) {
 		_samplesConsumed = _samplesDecoded;
 		_mixerTimeStamp = g_system->getMillis(true);
 		_pauseTime = 0;
-		res = _converter->convert(*_stream, data, len, _volL, _volR);
+		res = _converter->convert(*_stream, data, _mixer->getOutputBytesPerSample(), len, _volL, _volR);
 		_samplesDecoded += res;
 	}
 
diff --git a/audio/mixer.h b/audio/mixer.h
index 2c6c35f07b2..7ec1745683f 100644
--- a/audio/mixer.h
+++ b/audio/mixer.h
@@ -390,6 +390,15 @@ public:
 	 * @return The number of samples processed at each audio callback.
 	 */
 	virtual uint getOutputBufSize() const = 0;
+
+	/**
+	 * Return the output sample size of the system.
+	 *
+	 * The return value is measured in bytes.
+	 *
+	 * @return The output sample size is bytes.
+	 */
+	virtual uint getOutputBytesPerSample() const = 0;
 };
 
 /** @} */
diff --git a/audio/mixer_intern.h b/audio/mixer_intern.h
index c46a43825ac..8201736692a 100644
--- a/audio/mixer_intern.h
+++ b/audio/mixer_intern.h
@@ -66,6 +66,7 @@ private:
 	const uint _sampleRate;
 	const bool _stereo;
 	uint _outBufSize;
+	const uint _outBytesPerSample;
 	bool _mixerReady;
 	uint32 _handleSeed;
 
@@ -82,7 +83,7 @@ private:
 
 public:
 
-	MixerImpl(uint sampleRate, bool stereo = true, uint outBufSize = 0);
+	MixerImpl(uint sampleRate, bool stereo = true, uint outBufSize = 0, uint outBytesPerSample = 2);
 	~MixerImpl();
 
 	bool isReady() const override { Common::StackLock lock(_mutex); return _mixerReady; }
@@ -139,6 +140,7 @@ public:
 	uint getOutputRate() const override;
 	bool getOutputStereo() const override;
 	uint getOutputBufSize() const override;
+	uint getOutputBytesPerSample() const override;
 
 protected:
 	void insertChannel(SoundHandle *handle, Channel *chan);
diff --git a/audio/rate.cpp b/audio/rate.cpp
index 9ae4d3eb3cf..80d876cf5d4 100644
--- a/audio/rate.cpp
+++ b/audio/rate.cpp
@@ -80,10 +80,16 @@ private:
 	/** Current sample(s) in the input stream (left/right channel) */
 	int16 _inCurL, _inCurR;
 
+	template<typename st_sample_t>
 	int copyConvert(AudioStream &input, st_sample_t *outBuffer, st_size_t numSamples, st_volume_t vol_l, st_volume_t vol_r);
+	template<typename st_sample_t>
 	int simpleConvert(AudioStream &input, st_sample_t *outBuffer, st_size_t numSamples, st_volume_t vol_l, st_volume_t vol_r);
+	template<typename st_sample_t>
 	int interpolateConvert(AudioStream &input, st_sample_t *outBuffer, st_size_t numSamples, st_volume_t vol_l, st_volume_t vol_r);
 
+	template<typename st_sample_t>
+	int convertForType(AudioStream &input, byte *outBuffer, st_size_t numSamples, st_volume_t volL, st_volume_t volR);
+
 	// keep a single printConvertType shared across all RateConverter_Impl specializations.
 	// PrintContext must be trivially destructible: it lives in a function-scope static and
 	// is torn down after the OSystem (and its memory pool that backs Common::String) is gone.
@@ -110,7 +116,7 @@ public:
 	RateConverter_Impl(st_rate_t inputRate, st_rate_t outputRate);
 	virtual ~RateConverter_Impl() {}
 
-	int convert(AudioStream &input, st_sample_t *outBuffer, st_size_t numSamples, st_volume_t vol_l, st_volume_t vol_r) override;
+	int convert(AudioStream &input, byte *outBuffer, uint outBytesPerSample, st_size_t numSamples, st_volume_t vol_l, st_volume_t vol_r) override;
 
 	void setInputRate(st_rate_t inputRate) override { _inRate = inputRate; }
 	void setOutputRate(st_rate_t outputRate) override { _outRate = outputRate; }
@@ -122,6 +128,7 @@ public:
 };
 
 template<bool inStereo, bool outStereo, bool reverseStereo>
+template<typename st_sample_t>
 int RateConverter_Impl<inStereo, outStereo, reverseStereo>::copyConvert(AudioStream &input, st_sample_t *outBuffer, st_size_t numSamples, st_volume_t volL, st_volume_t volR) {
 	PRINT_OUTPUT_RATE;
 
@@ -170,6 +177,7 @@ int RateConverter_Impl<inStereo, outStereo, reverseStereo>::copyConvert(AudioStr
 }
 
 template<bool inStereo, bool outStereo, bool reverseStereo>
+template<typename st_sample_t>
 int RateConverter_Impl<inStereo, outStereo, reverseStereo>::simpleConvert(AudioStream &input, st_sample_t *outBuffer, st_size_t numSamples, st_volume_t volL, st_volume_t volR) {
 	PRINT_OUTPUT_RATE;
 
@@ -231,6 +239,7 @@ int RateConverter_Impl<inStereo, outStereo, reverseStereo>::simpleConvert(AudioS
 }
 
 template<bool inStereo, bool outStereo, bool reverseStereo>
+template<typename st_sample_t>
 int RateConverter_Impl<inStereo, outStereo, reverseStereo>::interpolateConvert(AudioStream &input, st_sample_t *outBuffer, st_size_t numSamples, st_volume_t volL, st_volume_t volR) {
 	PRINT_OUTPUT_RATE;
 
@@ -315,20 +324,30 @@ RateConverter_Impl<inStereo, outStereo, reverseStereo>::RateConverter_Impl(st_ra
 	_bufferPos(nullptr) {}
 
 template<bool inStereo, bool outStereo, bool reverseStereo>
-int RateConverter_Impl<inStereo, outStereo, reverseStereo>::convert(AudioStream &input, st_sample_t *outBuffer, st_size_t numSamples, st_volume_t volL, st_volume_t volR) {
+template<typename st_sample_t>
+int RateConverter_Impl<inStereo, outStereo, reverseStereo>::convertForType(AudioStream &input, byte *outBuffer, st_size_t numSamples, st_volume_t volL, st_volume_t volR) {
 	assert(input.isStereo() == inStereo);
 
 	if (_inRate == _outRate) {
-		return copyConvert(input, outBuffer, numSamples, volL, volR);
+		return copyConvert<st_sample_t>(input, (st_sample_t *)outBuffer, numSamples, volL, volR);
 	} else {
 		if ((_inRate % _outRate) == 0 && (_inRate < 65536)) {
-			return simpleConvert(input, outBuffer, numSamples, volL, volR);
+			return simpleConvert<st_sample_t>(input, (st_sample_t *)outBuffer, numSamples, volL, volR);
 		} else {
-			return interpolateConvert(input, outBuffer, numSamples, volL, volR);
+			return interpolateConvert<st_sample_t>(input, (st_sample_t *)outBuffer, numSamples, volL, volR);
 		}
 	}
 }
 
+template<bool inStereo, bool outStereo, bool reverseStereo>
+int RateConverter_Impl<inStereo, outStereo, reverseStereo>::convert(AudioStream &input, byte *outBuffer, uint outBytesPerSample, st_size_t numSamples, st_volume_t volL, st_volume_t volR) {
+	if (outBytesPerSample == sizeof(int32)) {
+		return convertForType<int32>(input, outBuffer, numSamples, volL, volR);
+	} else {
+		return convertForType<int16>(input, outBuffer, numSamples, volL, volR);
+	}
+}
+
 RateConverter *makeRateConverter(st_rate_t inRate, st_rate_t outRate, bool inStereo, bool outStereo, bool reverseStereo) {
 	if (inStereo) {
 		if (outStereo) {
diff --git a/audio/rate.h b/audio/rate.h
index 4fc0213038d..1a61d07c854 100644
--- a/audio/rate.h
+++ b/audio/rate.h
@@ -35,34 +35,34 @@ namespace Audio {
 
 class AudioStream;
 
-typedef int16 st_sample_t;
 typedef uint16 st_volume_t;
 typedef uint32 st_size_t;
 typedef uint32 st_rate_t;
 
-/* Minimum and maximum values a sample can hold. */
-enum {
-	ST_SAMPLE_MAX = 0x7fffL,
-	ST_SAMPLE_MIN = (-ST_SAMPLE_MAX - 1L)
-};
+// This assumes that 'a' and 'b' are 24-bit samples at most
+template <typename T>
+static inline void clampedAdd(T& a, int b) {
+	constexpr unsigned long long highestBit = 1ULL << (sizeof(T) * 8 - 1);
+	constexpr int maxVal = (int)(highestBit - 1);
+	constexpr int minVal = ~maxVal;
 
-static inline void clampedAdd(int16& a, int b) {
 	int val;
 #ifdef OUTPUT_UNSIGNED_AUDIO
-	val = (a ^ 0x8000) + b;
+	constexpr int signMask = (int)highestBit;
+	val = ((int)a ^ signMask) + b;
 #else
-	val = a + b;
+	val = (int)a + b;
 #endif
 
-	if (val > ST_SAMPLE_MAX)
-		val = ST_SAMPLE_MAX;
-	else if (val < ST_SAMPLE_MIN)
-		val = ST_SAMPLE_MIN;
+	if (val > maxVal)
+		val = maxVal;
+	else if (val < minVal)
+		val = minVal;
 
 #ifdef OUTPUT_UNSIGNED_AUDIO
-	a = ((int16)val) ^ 0x8000;
+	a = (T)(val ^ signMask);
 #else
-	a = val;
+	a = (T)val;
 #endif
 }
 
@@ -79,16 +79,17 @@ public:
 
 	/**
 	 * Convert the provided AudioStream to the target sample rate.
-	 * 
-	 * @param input			The AudioStream to read data from.
-	 * @param outBuffer		The buffer that the resampled audio will be written to. Must have size of at least @p numSamples.
-	 * @param numSamples	The desired number of samples to be written into the buffer.
-	 * @param vol_l			Volume for left channel.
-	 * @param vol_r			Volume for right channel.
-	 * 
+	 *
+	 * @param input				The AudioStream to read data from.
+	 * @param outBuffer			The buffer that the resampled audio will be written to. Must have size of at least @p numSamples.
+	 * @param outBytesPerSample	The size of each output sample in bytes (e.g. sizeof(int16) or sizeof(int32)).
+	 * @param numSamples		The desired number of samples to be written into the buffer.
+	 * @param vol_l				Volume for left channel.
+	 * @param vol_r				Volume for right channel.
+	 *
 	 * @return Number of sample pairs written into the buffer.
 	 */
-	virtual int convert(AudioStream &input, st_sample_t *outBuffer, st_size_t numSamples, st_volume_t vol_l, st_volume_t vol_r) = 0;
+	virtual int convert(AudioStream &input, byte *outBuffer, uint outBytesPerSample, st_size_t numSamples, st_volume_t vol_l, st_volume_t vol_r) = 0;
 
 	virtual void setInputRate(st_rate_t inputRate) = 0;
 	virtual void setOutputRate(st_rate_t outputRate) = 0;
@@ -98,7 +99,7 @@ public:
 
 	/**
 	 * Does the internal buffer still have some leftover data?
-	 * 
+	 *
 	 * @return True if we need to drain, false otherwise
 	 */
 	virtual bool needsDraining() const = 0;
diff --git a/engines/sci/sound/audio32.cpp b/engines/sci/sound/audio32.cpp
index 0b21070ec7c..ce160284650 100644
--- a/engines/sci/sound/audio32.cpp
+++ b/engines/sci/sound/audio32.cpp
@@ -234,7 +234,7 @@ Audio32::~Audio32() {
 
 int Audio32::writeAudioInternal(Audio::AudioStream &sourceStream, Audio::RateConverter &converter, int16 *targetBuffer, const int numSamples, const Audio::st_volume_t leftVolume, const Audio::st_volume_t rightVolume) {
 	const int samplePairsToRead = numSamples >> 1;
-	const int samplePairsWritten = converter.convert(sourceStream, targetBuffer, samplePairsToRead, leftVolume, rightVolume);
+	const int samplePairsWritten = converter.convert(sourceStream, (byte *)targetBuffer, sizeof(int16), samplePairsToRead, leftVolume, rightVolume);
 	return samplePairsWritten << 1;
 }
 


Commit: b9cca6360ead999587371787f87276247dc583fb
    https://github.com/scummvm/scummvm/commit/b9cca6360ead999587371787f87276247dc583fb
Author: Miro Kropacek (miro.kropacek at gmail.com)
Date: 2026-06-21T15:17:36+02:00

Commit Message:
AUDIO: RateConverter now supports unclamped mixing

Changed paths:
    audio/mixer.cpp
    audio/rate.cpp
    audio/rate.h
    engines/sci/sound/audio32.cpp


diff --git a/audio/mixer.cpp b/audio/mixer.cpp
index 174fd8f515d..015812d90ce 100644
--- a/audio/mixer.cpp
+++ b/audio/mixer.cpp
@@ -802,7 +802,7 @@ int Channel::mix(byte *data, uint len) {
 		_samplesConsumed = _samplesDecoded;
 		_mixerTimeStamp = g_system->getMillis(true);
 		_pauseTime = 0;
-		res = _converter->convert(*_stream, data, _mixer->getOutputBytesPerSample(), len, _volL, _volR);
+		res = _converter->convert(*_stream, data, _mixer->getOutputBytesPerSample(), len, _volL, _volR, MIX_CLAMPED_ADD);
 		_samplesDecoded += res;
 	}
 
diff --git a/audio/rate.cpp b/audio/rate.cpp
index 80d876cf5d4..b115f63c1e9 100644
--- a/audio/rate.cpp
+++ b/audio/rate.cpp
@@ -80,14 +80,14 @@ private:
 	/** Current sample(s) in the input stream (left/right channel) */
 	int16 _inCurL, _inCurR;
 
-	template<typename st_sample_t>
+	template<typename st_sample_t, MixMode mixMode>
 	int copyConvert(AudioStream &input, st_sample_t *outBuffer, st_size_t numSamples, st_volume_t vol_l, st_volume_t vol_r);
-	template<typename st_sample_t>
+	template<typename st_sample_t, MixMode mixMode>
 	int simpleConvert(AudioStream &input, st_sample_t *outBuffer, st_size_t numSamples, st_volume_t vol_l, st_volume_t vol_r);
-	template<typename st_sample_t>
+	template<typename st_sample_t, MixMode mixMode>
 	int interpolateConvert(AudioStream &input, st_sample_t *outBuffer, st_size_t numSamples, st_volume_t vol_l, st_volume_t vol_r);
 
-	template<typename st_sample_t>
+	template<typename st_sample_t, MixMode mixMode>
 	int convertForType(AudioStream &input, byte *outBuffer, st_size_t numSamples, st_volume_t volL, st_volume_t volR);
 
 	// keep a single printConvertType shared across all RateConverter_Impl specializations.
@@ -116,7 +116,7 @@ public:
 	RateConverter_Impl(st_rate_t inputRate, st_rate_t outputRate);
 	virtual ~RateConverter_Impl() {}
 
-	int convert(AudioStream &input, byte *outBuffer, uint outBytesPerSample, st_size_t numSamples, st_volume_t vol_l, st_volume_t vol_r) override;
+	int convert(AudioStream &input, byte *outBuffer, uint outBytesPerSample, st_size_t numSamples, st_volume_t vol_l, st_volume_t vol_r, MixMode mixMode) override;
 
 	void setInputRate(st_rate_t inputRate) override { _inRate = inputRate; }
 	void setOutputRate(st_rate_t outputRate) override { _outRate = outputRate; }
@@ -128,7 +128,7 @@ public:
 };
 
 template<bool inStereo, bool outStereo, bool reverseStereo>
-template<typename st_sample_t>
+template<typename st_sample_t, MixMode mixMode>
 int RateConverter_Impl<inStereo, outStereo, reverseStereo>::copyConvert(AudioStream &input, st_sample_t *outBuffer, st_size_t numSamples, st_volume_t volL, st_volume_t volR) {
 	PRINT_OUTPUT_RATE;
 
@@ -159,15 +159,15 @@ int RateConverter_Impl<inStereo, outStereo, reverseStereo>::copyConvert(AudioStr
 
 		if (outStereo) {
 			// Output left channel
-			clampedAdd(outBuffer[reverseStereo    ], outL);
+			processSample<mixMode>(outBuffer[reverseStereo    ], outL);
 
 			// Output right channel
-			clampedAdd(outBuffer[reverseStereo ^ 1], outR);
+			processSample<mixMode>(outBuffer[reverseStereo ^ 1], outR);
 
 			outBuffer += 2;
 		} else {
 			// Output mono channel
-			clampedAdd(outBuffer[0], (outL + outR) / 2);
+			processSample<mixMode>(outBuffer[0], (outL + outR) / 2);
 
 			outBuffer += 1;
 		}
@@ -177,7 +177,7 @@ int RateConverter_Impl<inStereo, outStereo, reverseStereo>::copyConvert(AudioStr
 }
 
 template<bool inStereo, bool outStereo, bool reverseStereo>
-template<typename st_sample_t>
+template<typename st_sample_t, MixMode mixMode>
 int RateConverter_Impl<inStereo, outStereo, reverseStereo>::simpleConvert(AudioStream &input, st_sample_t *outBuffer, st_size_t numSamples, st_volume_t volL, st_volume_t volR) {
 	PRINT_OUTPUT_RATE;
 
@@ -222,15 +222,15 @@ int RateConverter_Impl<inStereo, outStereo, reverseStereo>::simpleConvert(AudioS
 
 		if (outStereo) {
 			// output left channel
-			clampedAdd(outBuffer[reverseStereo    ], outL);
+			processSample<mixMode>(outBuffer[reverseStereo    ], outL);
 
 			// output right channel
-			clampedAdd(outBuffer[reverseStereo ^ 1], outR);
+			processSample<mixMode>(outBuffer[reverseStereo ^ 1], outR);
 
 			outBuffer += 2;
 		} else {
 			// output mono channel
-			clampedAdd(outBuffer[0], (outL + outR) / 2);
+			processSample<mixMode>(outBuffer[0], (outL + outR) / 2);
 
 			outBuffer += 1;
 		}
@@ -239,7 +239,7 @@ int RateConverter_Impl<inStereo, outStereo, reverseStereo>::simpleConvert(AudioS
 }
 
 template<bool inStereo, bool outStereo, bool reverseStereo>
-template<typename st_sample_t>
+template<typename st_sample_t, MixMode mixMode>
 int RateConverter_Impl<inStereo, outStereo, reverseStereo>::interpolateConvert(AudioStream &input, st_sample_t *outBuffer, st_size_t numSamples, st_volume_t volL, st_volume_t volR) {
 	PRINT_OUTPUT_RATE;
 
@@ -290,15 +290,15 @@ int RateConverter_Impl<inStereo, outStereo, reverseStereo>::interpolateConvert(A
 
 			if (outStereo) {
 				// Output left channel
-				clampedAdd(outBuffer[reverseStereo    ], outL);
+				processSample<mixMode>(outBuffer[reverseStereo    ], outL);
 
 				// Output right channel
-				clampedAdd(outBuffer[reverseStereo ^ 1], outR);
+				processSample<mixMode>(outBuffer[reverseStereo ^ 1], outR);
 
 				outBuffer += 2;
 			} else {
 				// Output mono channel
-				clampedAdd(outBuffer[0], (outL + outR) / 2);
+				processSample<mixMode>(outBuffer[0], (outL + outR) / 2);
 
 				outBuffer += 1;
 			}
@@ -324,27 +324,33 @@ RateConverter_Impl<inStereo, outStereo, reverseStereo>::RateConverter_Impl(st_ra
 	_bufferPos(nullptr) {}
 
 template<bool inStereo, bool outStereo, bool reverseStereo>
-template<typename st_sample_t>
+template<typename st_sample_t, MixMode mixMode>
 int RateConverter_Impl<inStereo, outStereo, reverseStereo>::convertForType(AudioStream &input, byte *outBuffer, st_size_t numSamples, st_volume_t volL, st_volume_t volR) {
 	assert(input.isStereo() == inStereo);
 
 	if (_inRate == _outRate) {
-		return copyConvert<st_sample_t>(input, (st_sample_t *)outBuffer, numSamples, volL, volR);
+		return copyConvert<st_sample_t, mixMode>(input, (st_sample_t *)outBuffer, numSamples, volL, volR);
 	} else {
 		if ((_inRate % _outRate) == 0 && (_inRate < 65536)) {
-			return simpleConvert<st_sample_t>(input, (st_sample_t *)outBuffer, numSamples, volL, volR);
+			return simpleConvert<st_sample_t, mixMode>(input, (st_sample_t *)outBuffer, numSamples, volL, volR);
 		} else {
-			return interpolateConvert<st_sample_t>(input, (st_sample_t *)outBuffer, numSamples, volL, volR);
+			return interpolateConvert<st_sample_t, mixMode>(input, (st_sample_t *)outBuffer, numSamples, volL, volR);
 		}
 	}
 }
 
 template<bool inStereo, bool outStereo, bool reverseStereo>
-int RateConverter_Impl<inStereo, outStereo, reverseStereo>::convert(AudioStream &input, byte *outBuffer, uint outBytesPerSample, st_size_t numSamples, st_volume_t volL, st_volume_t volR) {
+int RateConverter_Impl<inStereo, outStereo, reverseStereo>::convert(AudioStream &input, byte *outBuffer, uint outBytesPerSample, st_size_t numSamples, st_volume_t volL, st_volume_t volR, MixMode mixMode) {
 	if (outBytesPerSample == sizeof(int32)) {
-		return convertForType<int32>(input, outBuffer, numSamples, volL, volR);
+		if (mixMode == MIX_ADD)
+			return convertForType<int32, MIX_ADD>(input, outBuffer, numSamples, volL, volR);
+		else
+			return convertForType<int32, MIX_CLAMPED_ADD>(input, outBuffer, numSamples, volL, volR);
 	} else {
-		return convertForType<int16>(input, outBuffer, numSamples, volL, volR);
+		if (mixMode == MIX_ADD)
+			return convertForType<int16, MIX_ADD>(input, outBuffer, numSamples, volL, volR);
+		else
+			return convertForType<int16, MIX_CLAMPED_ADD>(input, outBuffer, numSamples, volL, volR);
 	}
 }
 
diff --git a/audio/rate.h b/audio/rate.h
index 1a61d07c854..cf59a8f5f0c 100644
--- a/audio/rate.h
+++ b/audio/rate.h
@@ -39,31 +39,49 @@ typedef uint16 st_volume_t;
 typedef uint32 st_size_t;
 typedef uint32 st_rate_t;
 
+enum MixMode {
+	MIX_ADD,
+	MIX_CLAMPED_ADD
+};
+
 // This assumes that 'a' and 'b' are 24-bit samples at most
-template <typename T>
-static inline void clampedAdd(T& a, int b) {
+template <MixMode Mode, typename T>
+static inline void processSample(T& a, int b) {
 	constexpr unsigned long long highestBit = 1ULL << (sizeof(T) * 8 - 1);
 	constexpr int maxVal = (int)(highestBit - 1);
 	constexpr int minVal = ~maxVal;
 
-	int val;
 #ifdef OUTPUT_UNSIGNED_AUDIO
 	constexpr int signMask = (int)highestBit;
-	val = ((int)a ^ signMask) + b;
+#endif
+
+	if (Mode == MIX_ADD) {
+		a += b;
+	} else if (Mode == MIX_CLAMPED_ADD) {
+		int val;
+#ifdef OUTPUT_UNSIGNED_AUDIO
+		val = ((int)a ^ signMask) + b;
 #else
-	val = (int)a + b;
+		val = (int)a + b;
 #endif
 
-	if (val > maxVal)
-		val = maxVal;
-	else if (val < minVal)
-		val = minVal;
+		if (val > maxVal)
+			val = maxVal;
+		else if (val < minVal)
+			val = minVal;
 
 #ifdef OUTPUT_UNSIGNED_AUDIO
-	a = (T)(val ^ signMask);
+		a = (T)(val ^ signMask);
 #else
-	a = (T)val;
+		a = (T)val;
 #endif
+	}
+}
+
+// Fallback for engine code
+template <typename T>
+static inline void clampedAdd(T& a, int b) {
+	processSample<MIX_CLAMPED_ADD>(a, b);
 }
 
 /**
@@ -86,10 +104,11 @@ public:
 	 * @param numSamples		The desired number of samples to be written into the buffer.
 	 * @param vol_l				Volume for left channel.
 	 * @param vol_r				Volume for right channel.
+	 * @param mixMode			Sample mix mode for outBuffer.
 	 *
 	 * @return Number of sample pairs written into the buffer.
 	 */
-	virtual int convert(AudioStream &input, byte *outBuffer, uint outBytesPerSample, st_size_t numSamples, st_volume_t vol_l, st_volume_t vol_r) = 0;
+	virtual int convert(AudioStream &input, byte *outBuffer, uint outBytesPerSample, st_size_t numSamples, st_volume_t vol_l, st_volume_t vol_r, MixMode mixMode) = 0;
 
 	virtual void setInputRate(st_rate_t inputRate) = 0;
 	virtual void setOutputRate(st_rate_t outputRate) = 0;
diff --git a/engines/sci/sound/audio32.cpp b/engines/sci/sound/audio32.cpp
index ce160284650..995d1dbf777 100644
--- a/engines/sci/sound/audio32.cpp
+++ b/engines/sci/sound/audio32.cpp
@@ -234,7 +234,7 @@ Audio32::~Audio32() {
 
 int Audio32::writeAudioInternal(Audio::AudioStream &sourceStream, Audio::RateConverter &converter, int16 *targetBuffer, const int numSamples, const Audio::st_volume_t leftVolume, const Audio::st_volume_t rightVolume) {
 	const int samplePairsToRead = numSamples >> 1;
-	const int samplePairsWritten = converter.convert(sourceStream, (byte *)targetBuffer, sizeof(int16), samplePairsToRead, leftVolume, rightVolume);
+	const int samplePairsWritten = converter.convert(sourceStream, (byte *)targetBuffer, sizeof(int16), samplePairsToRead, leftVolume, rightVolume, Audio::MIX_CLAMPED_ADD);
 	return samplePairsWritten << 1;
 }
 


Commit: 424681dca2ac20e89bb19bfa2c86818311e248ed
    https://github.com/scummvm/scummvm/commit/424681dca2ac20e89bb19bfa2c86818311e248ed
Author: Miro Kropacek (miro.kropacek at gmail.com)
Date: 2026-06-21T15:17:36+02:00

Commit Message:
AUDIO: Make sample clamping optional

Changed paths:
    audio/mixer.cpp
    audio/mixer.h
    audio/mixer_intern.h


diff --git a/audio/mixer.cpp b/audio/mixer.cpp
index 015812d90ce..0f6ab5298e9 100644
--- a/audio/mixer.cpp
+++ b/audio/mixer.cpp
@@ -227,8 +227,8 @@ private:
 #pragma mark --- Mixer ---
 #pragma mark -
 
-MixerImpl::MixerImpl(uint sampleRate, bool stereo, uint outBufSize, uint outBytesPerSample)
-	: _mutex(), _sampleRate(sampleRate), _stereo(stereo), _outBufSize(outBufSize), _outBytesPerSample(outBytesPerSample)
+MixerImpl::MixerImpl(uint sampleRate, bool stereo, uint outBufSize, uint outBytesPerSample, bool clamp)
+	: _mutex(), _sampleRate(sampleRate), _stereo(stereo), _outBufSize(outBufSize), _outBytesPerSample(outBytesPerSample), _clamp(clamp)
 	, _mixerReady(false), _handleSeed(0), _soundTypeSettings() {
 
 	assert(sampleRate > 0);
@@ -264,6 +264,10 @@ uint MixerImpl::getOutputBytesPerSample() const {
 	return _outBytesPerSample;
 }
 
+bool MixerImpl::getClamping() const {
+	return _clamp;
+}
+
 void MixerImpl::insertChannel(SoundHandle *handle, Channel *chan) {
 	int index = -1;
 	for (int i = 0; i != NUM_CHANNELS; i++) {
@@ -802,7 +806,14 @@ int Channel::mix(byte *data, uint len) {
 		_samplesConsumed = _samplesDecoded;
 		_mixerTimeStamp = g_system->getMillis(true);
 		_pauseTime = 0;
-		res = _converter->convert(*_stream, data, _mixer->getOutputBytesPerSample(), len, _volL, _volR, MIX_CLAMPED_ADD);
+		res = _converter->convert(
+			*_stream,
+			data,
+			_mixer->getOutputBytesPerSample(),
+			len,
+			_volL,
+			_volR,
+			_mixer->getClamping() ? MIX_CLAMPED_ADD : MIX_ADD);
 		_samplesDecoded += res;
 	}
 
diff --git a/audio/mixer.h b/audio/mixer.h
index 7ec1745683f..9c15a99f831 100644
--- a/audio/mixer.h
+++ b/audio/mixer.h
@@ -399,6 +399,13 @@ public:
 	 * @return The output sample size is bytes.
 	 */
 	virtual uint getOutputBytesPerSample() const = 0;
+
+	/**
+	 * Return whether the mixer is expected to clamp outgoing samples.
+	 *
+	 * @return true if output is clamped, false if not.
+	 */
+	virtual bool getClamping() const = 0;
 };
 
 /** @} */
diff --git a/audio/mixer_intern.h b/audio/mixer_intern.h
index 8201736692a..d6ff2de96c0 100644
--- a/audio/mixer_intern.h
+++ b/audio/mixer_intern.h
@@ -67,6 +67,7 @@ private:
 	const bool _stereo;
 	uint _outBufSize;
 	const uint _outBytesPerSample;
+	const bool _clamp;
 	bool _mixerReady;
 	uint32 _handleSeed;
 
@@ -83,7 +84,7 @@ private:
 
 public:
 
-	MixerImpl(uint sampleRate, bool stereo = true, uint outBufSize = 0, uint outBytesPerSample = 2);
+	MixerImpl(uint sampleRate, bool stereo = true, uint outBufSize = 0, uint outBytesPerSample = 2, bool clamp = true);
 	~MixerImpl();
 
 	bool isReady() const override { Common::StackLock lock(_mutex); return _mixerReady; }
@@ -141,6 +142,7 @@ public:
 	bool getOutputStereo() const override;
 	uint getOutputBufSize() const override;
 	uint getOutputBytesPerSample() const override;
+	bool getClamping() const override;
 
 protected:
 	void insertChannel(SoundHandle *handle, Channel *chan);


Commit: 995c360cfc03ce037959ddaaab00c5b5b224f630
    https://github.com/scummvm/scummvm/commit/995c360cfc03ce037959ddaaab00c5b5b224f630
Author: Miro Kropacek (miro.kropacek at gmail.com)
Date: 2026-06-21T15:17:36+02:00

Commit Message:
AUDIO: Don't clamp muted channels

Changed paths:
    audio/mixer.cpp


diff --git a/audio/mixer.cpp b/audio/mixer.cpp
index 0f6ab5298e9..cbb47186ae6 100644
--- a/audio/mixer.cpp
+++ b/audio/mixer.cpp
@@ -143,6 +143,11 @@ public:
 	 */
 	uint8 getFaderR() const;
 
+	/**
+	 * Queries whether the channel is silent, i.e. will produce no audio output.
+	 */
+	bool isSilent() const { return _volL == 0 && _volR == 0; }
+
 	/**
 	 * Set the channel's sample rate.
 	 *
@@ -346,15 +351,13 @@ int MixerImpl::mixCallback(byte *samples, uint len) {
 	// Since the mixer callback has been called, the mixer must be ready...
 	_mixerReady = true;
 
-	// zero the sample buffer
-	memset(samples, 0, len);
-
 	// we store samples of size defined by the backend
 	const uint bytesPerFrame = _outBytesPerSample * (_stereo ? 2 : 1);
 	assert(len % bytesPerFrame == 0);
-	len /= bytesPerFrame;
+	const uint numFrames = len / bytesPerFrame;
 
-	// mix all channels
+	// mix all channels, zeroing the buffer lazily on first non-silent channel
+	bool zeroed = false;
 	int res = 0, tmp;
 	for (int i = 0; i != NUM_CHANNELS; i++)
 		if (_channels[i]) {
@@ -362,14 +365,19 @@ int MixerImpl::mixCallback(byte *samples, uint len) {
 				delete _channels[i];
 				_channels[i] = nullptr;
 			} else if (!_channels[i]->isPaused()) {
-				tmp = _channels[i]->mix(samples, len);
+				if (!_channels[i]->isSilent() && !zeroed) {
+					memset(samples, 0, len);
+					zeroed = true;
+				}
+				tmp = _channels[i]->mix(samples, numFrames);
 
 				if (tmp > res)
 					res = tmp;
 			}
 		}
 
-	return res;
+	// optimisation: let the caller know that there's nothing to clamp
+	return (!_clamp && !zeroed) ? 0 : res;
 }
 
 void MixerImpl::stopAll() {


Commit: 85befa9c13c0cf953e63f79e67b605a00d5e8fa4
    https://github.com/scummvm/scummvm/commit/85befa9c13c0cf953e63f79e67b605a00d5e8fa4
Author: Miro Kropacek (miro.kropacek at gmail.com)
Date: 2026-06-21T15:17:36+02:00

Commit Message:
AUDIO: Template-optimize convert methods

Also, introduce upscaleConvert for e.g. 11025, 22050 -> 44100
conversions.

Changed paths:
    audio/rate.cpp


diff --git a/audio/rate.cpp b/audio/rate.cpp
index b115f63c1e9..7ebf1e88af9 100644
--- a/audio/rate.cpp
+++ b/audio/rate.cpp
@@ -80,12 +80,17 @@ private:
 	/** Current sample(s) in the input stream (left/right channel) */
 	int16 _inCurL, _inCurR;
 
-	template<typename st_sample_t, MixMode mixMode>
-	int copyConvert(AudioStream &input, st_sample_t *outBuffer, st_size_t numSamples, st_volume_t vol_l, st_volume_t vol_r);
-	template<typename st_sample_t, MixMode mixMode>
-	int simpleConvert(AudioStream &input, st_sample_t *outBuffer, st_size_t numSamples, st_volume_t vol_l, st_volume_t vol_r);
-	template<typename st_sample_t, MixMode mixMode>
-	int interpolateConvert(AudioStream &input, st_sample_t *outBuffer, st_size_t numSamples, st_volume_t vol_l, st_volume_t vol_r);
+	template<st_volume_t volL, st_volume_t volR, typename st_sample_t, MixMode mixMode>
+	int commonConvert(AudioStream &input, st_sample_t *outBuffer, st_size_t numSamples, st_volume_t volL_val, st_volume_t volR_val, int outputSamples);
+
+	template<st_volume_t volL = static_cast<st_volume_t>(-1), st_volume_t volR = static_cast<st_volume_t>(-1), typename st_sample_t, MixMode mixMode>
+	int copyConvert(AudioStream &input, st_sample_t *outBuffer, st_size_t numSamples, st_volume_t volL_val, st_volume_t volR_val);
+	template<st_volume_t volL = static_cast<st_volume_t>(-1), st_volume_t volR = static_cast<st_volume_t>(-1), typename st_sample_t, MixMode mixMode>
+	int downsampleConvert(AudioStream &input, st_sample_t *outBuffer, st_size_t numSamples, st_volume_t volL_val, st_volume_t volR_val);
+	template<st_volume_t volL = static_cast<st_volume_t>(-1), st_volume_t volR = static_cast<st_volume_t>(-1), typename st_sample_t, MixMode mixMode>
+	int upsampleConvert(AudioStream &input, st_sample_t *outBuffer, st_size_t numSamples, st_volume_t volL_val, st_volume_t volR_val);
+	template<st_volume_t volL = static_cast<st_volume_t>(-1), st_volume_t volR = static_cast<st_volume_t>(-1), typename st_sample_t, MixMode mixMode>
+	int interpolateConvert(AudioStream &input, st_sample_t *outBuffer, st_size_t numSamples, st_volume_t volL_val, st_volume_t volR_val);
 
 	template<typename st_sample_t, MixMode mixMode>
 	int convertForType(AudioStream &input, byte *outBuffer, st_size_t numSamples, st_volume_t volL, st_volume_t volR);
@@ -128,14 +133,10 @@ public:
 };
 
 template<bool inStereo, bool outStereo, bool reverseStereo>
-template<typename st_sample_t, MixMode mixMode>
-int RateConverter_Impl<inStereo, outStereo, reverseStereo>::copyConvert(AudioStream &input, st_sample_t *outBuffer, st_size_t numSamples, st_volume_t volL, st_volume_t volR) {
-	PRINT_OUTPUT_RATE;
-
-	st_sample_t *outStart, *outEnd;
-
-	outStart = outBuffer;
-	outEnd = outBuffer + numSamples * (outStereo ? 2 : 1);
+template<st_volume_t volL, st_volume_t volR, typename st_sample_t, MixMode mixMode>
+int RateConverter_Impl<inStereo, outStereo, reverseStereo>::commonConvert(AudioStream &input, st_sample_t *outBuffer, st_size_t numSamples, st_volume_t volL_val, st_volume_t volR_val, int outputSamples) {
+	const st_sample_t *outStart = outBuffer;
+	const st_sample_t *outEnd = outBuffer + numSamples * (outStereo ? 2 : 1);
 
 	while (outBuffer < outEnd) {
 		// Check if we have to refill the buffer
@@ -147,51 +148,112 @@ int RateConverter_Impl<inStereo, outStereo, reverseStereo>::copyConvert(AudioStr
 				return (outBuffer - outStart) / (outStereo ? 2 : 1);
 		}
 
-		// Mix the data into the output buffer
-		int16 inL, inR;
-		inL = *_bufferPos++;
-		inR = (inStereo ? *_bufferPos++ : inL);
-		_bufferSize -= (inStereo ? 2 : 1);
-
-		st_sample_t outL, outR;
-		outL = (inL * (int)volL) / Audio::Mixer::kMaxMixerVolume;
-		outR = (inR * (int)volR) / Audio::Mixer::kMaxMixerVolume;
-
-		if (outStereo) {
-			// Output left channel
-			processSample<mixMode>(outBuffer[reverseStereo    ], outL);
-
-			// Output right channel
-			processSample<mixMode>(outBuffer[reverseStereo ^ 1], outR);
-
-			outBuffer += 2;
+		// Process as many samples as we can from the current buffer
+		const int count = MIN(
+			_bufferSize / (inStereo ? 2 : 1),
+			(int)(outEnd - outBuffer) / (outStereo ? 2 : 1) / outputSamples);
+		_bufferSize -= count * (inStereo ? 2 : 1);
+
+		if (volL | volR) {
+			// Mix the data into the output buffer
+			for (int i = 0; i < count; ++i) {
+				int16 inL, inR;
+
+				if (inStereo) {
+					if (volL != 0)
+						inL = *_bufferPos++;
+					else
+						_bufferPos++;
+
+					if (volR != 0)
+						inR = *_bufferPos++;
+					else
+						_bufferPos++;
+				} else {
+					if (volL != 0) {
+						inL = *_bufferPos++;
+						if (volR != 0)
+							inR = inL;
+					} else {
+						inR = *_bufferPos++;
+					}
+				}
+
+				st_sample_t outL, outR;
+
+				if (volL != 0) {
+					if (volL != Audio::Mixer::kMaxMixerVolume)
+						outL = (inL * (int)volL_val) / Audio::Mixer::kMaxMixerVolume;
+					else
+						outL = inL;
+				}
+
+				if (volR != 0) {
+					if (volR != Audio::Mixer::kMaxMixerVolume)
+						outR = (inR * (int)volR_val) / Audio::Mixer::kMaxMixerVolume;
+					else
+						outR = inR;
+				}
+
+				// TODO: could be unrolled
+				for (int j = 0; j < outputSamples; ++j) {
+					if (outStereo) {
+						// Output left channel
+						if (volL != 0)
+							processSample<mixMode>(outBuffer[reverseStereo    ], outL);
+
+						// Output right channel
+						if (volR != 0)
+							processSample<mixMode>(outBuffer[reverseStereo ^ 1], outR);
+					} else {
+						// Output mono channel
+						st_sample_t monoOut;
+						if (volL != 0 && volR != 0)
+							monoOut = (outL + outR) / 2;
+						else if (volL != 0)
+							monoOut = outL / 2;
+						else if (volR != 0)
+							monoOut = outR / 2;
+						processSample<mixMode>(outBuffer[0], monoOut);
+					}
+					outBuffer += (outStereo ? 2 : 1);
+				}
+			}
 		} else {
-			// Output mono channel
-			processSample<mixMode>(outBuffer[0], (outL + outR) / 2);
-
-			outBuffer += 1;
+			_bufferPos += count * (inStereo ? 2 : 1);
+			outBuffer += count * outputSamples * (outStereo ? 2 : 1);
 		}
 	}
-
 	return (outBuffer - outStart) / (outStereo ? 2 : 1);
 }
 
 template<bool inStereo, bool outStereo, bool reverseStereo>
-template<typename st_sample_t, MixMode mixMode>
-int RateConverter_Impl<inStereo, outStereo, reverseStereo>::simpleConvert(AudioStream &input, st_sample_t *outBuffer, st_size_t numSamples, st_volume_t volL, st_volume_t volR) {
+template<st_volume_t volL, st_volume_t volR, typename st_sample_t, MixMode mixMode>
+int RateConverter_Impl<inStereo, outStereo, reverseStereo>::copyConvert(AudioStream &input, st_sample_t *outBuffer, st_size_t numSamples, st_volume_t volL_val, st_volume_t volR_val) {
 	PRINT_OUTPUT_RATE;
 
-	// How much to increment _outPos by
-	frac_t outPos_inc = _inRate / _outRate;
+	return commonConvert<volL, volR, st_sample_t, mixMode>(input, outBuffer, numSamples, volL_val, volR_val, 1);
+}
+
+template<bool inStereo, bool outStereo, bool reverseStereo>
+template<st_volume_t volL, st_volume_t volR, typename st_sample_t, MixMode mixMode>
+int RateConverter_Impl<inStereo, outStereo, reverseStereo>::downsampleConvert(AudioStream &input, st_sample_t *outBuffer, st_size_t numSamples, st_volume_t volL_val, st_volume_t volR_val) {
+	PRINT_OUTPUT_RATE;
 
-	st_sample_t *outStart, *outEnd;
+	// How much to increment _outPos by
+	const frac_t outPos_inc = _inRate / _outRate;
 
-	outStart = outBuffer;
-	outEnd = outBuffer + numSamples * (outStereo ? 2 : 1);
+	const st_sample_t *outStart = outBuffer;
+	const st_sample_t *outEnd = outBuffer + numSamples * (outStereo ? 2 : 1);
 
 	while (outBuffer < outEnd) {
 		// Read enough input samples so that _outPos >= 0
-		do {
+		while (_outPos >= 0) {
+			const int skip = MIN((int)_outPos + 1, _bufferSize / (inStereo ? 2 : 1));
+			_bufferPos += skip * (inStereo ? 2 : 1);
+			_bufferSize -= skip * (inStereo ? 2 : 1);
+			_outPos -= skip;
+
 			// Check if we have to refill the buffer
 			if (_bufferSize == 0) {
 				_bufferPos = _buffer;
@@ -200,55 +262,106 @@ int RateConverter_Impl<inStereo, outStereo, reverseStereo>::simpleConvert(AudioS
 				if (_bufferSize <= 0)
 					return (outBuffer - outStart) / (outStereo ? 2 : 1);
 			}
+		}
 
-			_bufferSize -= (inStereo ? 2 : 1);
-			_outPos--;
-
-			if (_outPos >= 0) {
-				_bufferPos += (inStereo ? 2 : 1);
+		// Process as many samples as we can from the current buffer
+		const int count = MIN(
+			_bufferSize / (inStereo ? 2 : 1) / outPos_inc,
+			(int)(outEnd - outBuffer) / (outStereo ? 2 : 1));
+		_bufferSize -= count * outPos_inc * (inStereo ? 2 : 1);
+		_outPos = outPos_inc - 1;
+
+		// Frame stride remaining after reading one frame
+		const int stride = (outPos_inc - 1) * (inStereo ? 2 : 1);
+
+		if (volL | volR) {
+			for (int i = 0; i < count; ++i) {
+				int16 inL, inR;
+
+				if (inStereo) {
+					if (volL != 0)
+						inL = *_bufferPos++;
+					else
+						_bufferPos++;
+
+					if (volR != 0)
+						inR = *_bufferPos++;
+					else
+						_bufferPos++;
+				} else {
+					if (volL != 0) {
+						inL = *_bufferPos++;
+						if (volR != 0)
+							inR = inL;
+					} else {
+						inR = *_bufferPos++;
+					}
+				}
+
+				_bufferPos += stride;
+
+				st_sample_t outL, outR;
+				if (volL != 0) {
+					if (volL != Audio::Mixer::kMaxMixerVolume)
+						outL = (inL * (int)volL_val) / Audio::Mixer::kMaxMixerVolume;
+					else
+						outL = inL;
+				}
+				if (volR != 0) {
+					if (volR != Audio::Mixer::kMaxMixerVolume)
+						outR = (inR * (int)volR_val) / Audio::Mixer::kMaxMixerVolume;
+					else
+						outR = inR;
+				}
+
+				if (outStereo) {
+					// Output left channel
+					if (volL != 0)
+						processSample<mixMode>(outBuffer[reverseStereo    ], outL);
+
+					// Output right channel
+					if (volR != 0)
+						processSample<mixMode>(outBuffer[reverseStereo ^ 1], outR);
+				} else {
+					// Output mono channel
+					st_sample_t monoOut;
+					if (volL != 0 && volR != 0)
+						monoOut = (outL + outR) / 2;
+					else if (volL != 0)
+						monoOut = outL / 2;
+					else if (volR != 0)
+						monoOut = outR / 2;
+					processSample<mixMode>(outBuffer[0], monoOut);
+				}
+
+				outBuffer += (outStereo ? 2 : 1);
 			}
-		} while (_outPos >= 0);
-
-		int16 inL, inR;
-		inL = *_bufferPos++;
-		inR = (inStereo ? *_bufferPos++ : inL);
-
-		// Increment output position
-		_outPos += outPos_inc;
-
-		st_sample_t outL, outR;
-		outL = (inL * (int)volL) / Audio::Mixer::kMaxMixerVolume;
-		outR = (inR * (int)volR) / Audio::Mixer::kMaxMixerVolume;
-
-		if (outStereo) {
-			// output left channel
-			processSample<mixMode>(outBuffer[reverseStereo    ], outL);
-
-			// output right channel
-			processSample<mixMode>(outBuffer[reverseStereo ^ 1], outR);
-
-			outBuffer += 2;
 		} else {
-			// output mono channel
-			processSample<mixMode>(outBuffer[0], (outL + outR) / 2);
-
-			outBuffer += 1;
+			_bufferPos += count * outPos_inc * (inStereo ? 2 : 1);
+			outBuffer += count * (outStereo ? 2 : 1);
 		}
 	}
 	return (outBuffer - outStart) / (outStereo ? 2 : 1);
 }
 
 template<bool inStereo, bool outStereo, bool reverseStereo>
-template<typename st_sample_t, MixMode mixMode>
-int RateConverter_Impl<inStereo, outStereo, reverseStereo>::interpolateConvert(AudioStream &input, st_sample_t *outBuffer, st_size_t numSamples, st_volume_t volL, st_volume_t volR) {
+template<st_volume_t volL, st_volume_t volR, typename st_sample_t, MixMode mixMode>
+int RateConverter_Impl<inStereo, outStereo, reverseStereo>::upsampleConvert(AudioStream &input, st_sample_t *outBuffer, st_size_t numSamples, st_volume_t volL_val, st_volume_t volR_val) {
+	PRINT_OUTPUT_RATE;
+
+	return commonConvert<volL, volR, st_sample_t, mixMode>(input, outBuffer, numSamples, volL_val, volR_val, _outRate / _inRate);
+}
+
+template<bool inStereo, bool outStereo, bool reverseStereo>
+template<st_volume_t volL, st_volume_t volR, typename st_sample_t, MixMode mixMode>
+int RateConverter_Impl<inStereo, outStereo, reverseStereo>::interpolateConvert(AudioStream &input, st_sample_t *outBuffer, st_size_t numSamples, st_volume_t volL_val, st_volume_t volR_val) {
 	PRINT_OUTPUT_RATE;
 
 	// How much to increment _outPosFrac by
-	frac_t outPos_inc = (_inRate << FRAC_BITS_LOW) / _outRate;
+	const frac_t outPos_inc = (_inRate << FRAC_BITS_LOW) / _outRate;
 
-	st_sample_t *outStart, *outEnd;
-	outStart = outBuffer;
-	outEnd = outBuffer + numSamples * (outStereo ? 2 : 1);
+	const st_sample_t *outStart = outBuffer;
+	const st_sample_t *outEnd = outBuffer + numSamples * (outStereo ? 2 : 1);
 
 	while (outBuffer < outEnd) {
 		// Read enough input samples so that _outPosFrac < 0
@@ -263,12 +376,21 @@ int RateConverter_Impl<inStereo, outStereo, reverseStereo>::interpolateConvert(A
 			}
 
 			_bufferSize -= (inStereo ? 2 : 1);
-			_inLastL = _inCurL;
-			_inCurL = *_bufferPos++;
+
+			if (volL != 0 || (!inStereo && volR != 0)) {
+				_inLastL = _inCurL;
+				_inCurL = *_bufferPos++;
+			} else {
+				_bufferPos++;
+			}
 
 			if (inStereo) {
-				_inLastR = _inCurR;
-				_inCurR = *_bufferPos++;
+				if (volR != 0) {
+					_inLastR = _inCurR;
+					_inCurR = *_bufferPos++;
+				} else {
+					_bufferPos++;
+				}
 			}
 
 			_outPosFrac -= FRAC_ONE_LOW;
@@ -277,32 +399,58 @@ int RateConverter_Impl<inStereo, outStereo, reverseStereo>::interpolateConvert(A
 		// Loop as long as the _outPos trails behind, and as long as there is
 		// still space in the output buffer.
 		while (_outPosFrac < (frac_t)FRAC_ONE_LOW && outBuffer < outEnd) {
-			// Interpolate
-			int16 inL, inR;
-			inL = (int16)(_inLastL + (((_inCurL - _inLastL) * _outPosFrac + FRAC_HALF_LOW) >> FRAC_BITS_LOW));
-			inR = (inStereo ?
-						(int16)(_inLastR + (((_inCurR - _inLastR) * _outPosFrac + FRAC_HALF_LOW) >> FRAC_BITS_LOW)) :
-						inL);
+			if (volL | volR) {
+				// Interpolate
+				int16 inL, inR;
 
-			st_sample_t outL, outR;
-			outL = (inL * (int)volL) / Audio::Mixer::kMaxMixerVolume;
-			outR = (inR * (int)volR) / Audio::Mixer::kMaxMixerVolume;
+				if (volL != 0 || (!inStereo && volR != 0)) {
+					inL = (int16)(_inLastL + (((_inCurL - _inLastL) * _outPosFrac + FRAC_HALF_LOW) >> FRAC_BITS_LOW));
+				}
 
-			if (outStereo) {
-				// Output left channel
-				processSample<mixMode>(outBuffer[reverseStereo    ], outL);
-
-				// Output right channel
-				processSample<mixMode>(outBuffer[reverseStereo ^ 1], outR);
-
-				outBuffer += 2;
-			} else {
-				// Output mono channel
-				processSample<mixMode>(outBuffer[0], (outL + outR) / 2);
-
-				outBuffer += 1;
+				if (volR != 0) {
+					inR = (inStereo ?
+						(int16)(_inLastR + (((_inCurR - _inLastR) * _outPosFrac + FRAC_HALF_LOW) >> FRAC_BITS_LOW)) :
+						inL);
+				}
+
+				st_sample_t outL, outR;
+				if (volL != 0) {
+					if (volL != Audio::Mixer::kMaxMixerVolume)
+						outL = (inL * (int)volL_val) / Audio::Mixer::kMaxMixerVolume;
+					else
+						outL = inL;
+				}
+
+				if (volR != 0) {
+					if (volR != Audio::Mixer::kMaxMixerVolume)
+						outR = (inR * (int)volR_val) / Audio::Mixer::kMaxMixerVolume;
+					else
+						outR = inR;
+				}
+
+				if (outStereo) {
+					// Output left channel
+					if (volL != 0)
+						processSample<mixMode>(outBuffer[reverseStereo    ], outL);
+
+					// Output right channel
+					if (volR != 0)
+						processSample<mixMode>(outBuffer[reverseStereo ^ 1], outR);
+				} else {
+					// Output mono channel
+					st_sample_t monoOut;
+					if (volL != 0 && volR != 0)
+						monoOut = (outL + outR) / 2;
+					else if (volL != 0)
+						monoOut = outL / 2;
+					else
+						monoOut = outR / 2;
+					processSample<mixMode>(outBuffer[0], monoOut);
+				}
 			}
 
+			outBuffer += (outStereo ? 2 : 1);
+
 			// Increment output position
 			_outPosFrac += outPos_inc;
 		}
@@ -328,14 +476,52 @@ template<typename st_sample_t, MixMode mixMode>
 int RateConverter_Impl<inStereo, outStereo, reverseStereo>::convertForType(AudioStream &input, byte *outBuffer, st_size_t numSamples, st_volume_t volL, st_volume_t volR) {
 	assert(input.isStereo() == inStereo);
 
+	constexpr auto kMax = Audio::Mixer::kMaxMixerVolume;
+
 	if (_inRate == _outRate) {
-		return copyConvert<st_sample_t, mixMode>(input, (st_sample_t *)outBuffer, numSamples, volL, volR);
+		if (volL == 0 && volR == 0)
+			return copyConvert<0, 0, st_sample_t, mixMode>(input, (st_sample_t *)outBuffer, numSamples, volL, volR);
+		else if (volL == 0 && volR == kMax)
+			return copyConvert<0, kMax, st_sample_t, mixMode>(input, (st_sample_t *)outBuffer, numSamples, volL, volR);
+		else if (volL == kMax && volR == 0)
+			return copyConvert<kMax, 0, st_sample_t, mixMode>(input, (st_sample_t *)outBuffer, numSamples, volL, volR);
+		else if (volL == kMax && volR == kMax)
+			return copyConvert<kMax, kMax, st_sample_t, mixMode>(input, (st_sample_t *)outBuffer, numSamples, volL, volR);
+		else
+			return copyConvert<static_cast<st_volume_t>(-1), static_cast<st_volume_t>(-1), st_sample_t, mixMode>(input, (st_sample_t *)outBuffer, numSamples, volL, volR);
+	} else if ((_inRate % _outRate) == 0 && (_inRate < 65536)) {
+		if (volL == 0 && volR == 0)
+			return downsampleConvert<0, 0, st_sample_t, mixMode>(input, (st_sample_t *)outBuffer, numSamples, volL, volR);
+		else if (volL == 0 && volR == kMax)
+			return downsampleConvert<0, kMax, st_sample_t, mixMode>(input, (st_sample_t *)outBuffer, numSamples, volL, volR);
+		else if (volL == kMax && volR == 0)
+			return downsampleConvert<kMax, 0, st_sample_t, mixMode>(input, (st_sample_t *)outBuffer, numSamples, volL, volR);
+		else if (volL == kMax && volR == kMax)
+			return downsampleConvert<kMax, kMax, st_sample_t, mixMode>(input, (st_sample_t *)outBuffer, numSamples, volL, volR);
+		else
+			return downsampleConvert<static_cast<st_volume_t>(-1), static_cast<st_volume_t>(-1), st_sample_t, mixMode>(input, (st_sample_t *)outBuffer, numSamples, volL, volR);
+	} else if ((_outRate % _inRate) == 0) {
+		if (volL == 0 && volR == 0)
+			return upsampleConvert<0, 0, st_sample_t, mixMode>(input, (st_sample_t *)outBuffer, numSamples, volL, volR);
+		else if (volL == 0 && volR == kMax)
+			return upsampleConvert<0, kMax, st_sample_t, mixMode>(input, (st_sample_t *)outBuffer, numSamples, volL, volR);
+		else if (volL == kMax && volR == 0)
+			return upsampleConvert<kMax, 0, st_sample_t, mixMode>(input, (st_sample_t *)outBuffer, numSamples, volL, volR);
+		else if (volL == kMax && volR == kMax)
+			return upsampleConvert<kMax, kMax, st_sample_t, mixMode>(input, (st_sample_t *)outBuffer, numSamples, volL, volR);
+		else
+			return upsampleConvert<static_cast<st_volume_t>(-1), static_cast<st_volume_t>(-1), st_sample_t, mixMode>(input, (st_sample_t *)outBuffer, numSamples, volL, volR);
 	} else {
-		if ((_inRate % _outRate) == 0 && (_inRate < 65536)) {
-			return simpleConvert<st_sample_t, mixMode>(input, (st_sample_t *)outBuffer, numSamples, volL, volR);
-		} else {
-			return interpolateConvert<st_sample_t, mixMode>(input, (st_sample_t *)outBuffer, numSamples, volL, volR);
-		}
+		if (volL == 0 && volR == 0)
+			return interpolateConvert<0, 0, st_sample_t, mixMode>(input, (st_sample_t *)outBuffer, numSamples, volL, volR);
+		else if (volL == 0 && volR == kMax)
+			return interpolateConvert<0, kMax, st_sample_t, mixMode>(input, (st_sample_t *)outBuffer, numSamples, volL, volR);
+		else if (volL == kMax && volR == 0)
+			return interpolateConvert<kMax, 0, st_sample_t, mixMode>(input, (st_sample_t *)outBuffer, numSamples, volL, volR);
+		else if (volL == kMax && volR == kMax)
+			return interpolateConvert<kMax, kMax, st_sample_t, mixMode>(input, (st_sample_t *)outBuffer, numSamples, volL, volR);
+		else
+			return interpolateConvert<static_cast<st_volume_t>(-1), static_cast<st_volume_t>(-1), st_sample_t, mixMode>(input, (st_sample_t *)outBuffer, numSamples, volL, volR);
 	}
 }
 



From noreply at scummvm.org  Sun Jun 21 13:20:04 2026
From: noreply at scummvm.org (sev-)
Date: Sun, 21 Jun 2026 13:20:04 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 d541e51e28d01533e6be30f486449836612eae8b
Message-ID: <20260621132004.A95007005CE@ron.scummvm.net>

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

Summary:
62d8322944 AUDIO : Atari TOS hardware / software OPL2 / 3 (YM262 / YM3812)
45e363f353 AUDIO : Atari TOS hardware / software OPL2 / 3 (YM262 / YM3812)
fa13bd283b AUDIO : Atari TOS hardware / software OPL2 / 3 (YM262 / YM3812)
a0a300c1bc AUDIO : Atari TOS hardware / software OPL2 / 3 (YM262 / YM3812)
301678656f AUDIO : Atari TOS hardware / software OPL2 / 3 (YM262 / YM3812)
d541e51e28 AUDIO : Atari TOS hardware / software OPL2 / 3 (YM262 / YM3812)


Commit: 62d83229444c2e0f328b9e34320879a8c9befb54
    https://github.com/scummvm/scummvm/commit/62d83229444c2e0f328b9e34320879a8c9befb54
Author: Pawe?? G??ralski (20125592+n0kturnal at users.noreply.github.com)
Date: 2026-06-21T15:19:56+02:00

Commit Message:
AUDIO : Atari TOS hardware / software OPL2 / 3 (YM262 / YM3812)

Added integration with nFM library (https://framagit.org/nokturnal/nfm), which provides driver layer for various OPL devices. Changelist provides Atari support only for now, it might be extended to other platforms as well with some additional work. It requires build system adjustments either by prebuilding library directly from repository or downloading prebuilt package to a place folder set by '--with-nfm-prefix' and with --enable-nfm switch enabling nFM support in build). Software driver integration wasn't tested, might be revisited after Atari audio backend updates. Disabled explicitly nFM on Firebee, nFM is not supported / tested on Firebee/Coldfire yet. Fixed broken auto nFM tests in configure script, set library detection to auto ('--enable-nfm' is not needed if path '--with-nfm-prefix' is valid and contains library). Use angled braces for library includes. Minor formatting in create_project.cpp as in suggested changes by SEV.

Changed paths:
  A audio/nfmopl.cpp
  A audio/nfmopl.h
    audio/adlib.cpp
    audio/fmopl.cpp
    audio/module.mk
    backends/platform/atari/build-firebee.sh
    base/version.cpp
    configure
    devtools/create_project/create_project.cpp


diff --git a/audio/adlib.cpp b/audio/adlib.cpp
index bb746af8fa7..f38c0ed15d7 100644
--- a/audio/adlib.cpp
+++ b/audio/adlib.cpp
@@ -37,7 +37,7 @@ static int g_tick;
 
 // Only include OPL3 when we actually have an AdLib emulator builtin, which
 // supports OPL3.
-#if !defined(DISABLE_DOSBOX_OPL) || !defined(DISABLE_NUKED_OPL)
+#if !defined(DISABLE_DOSBOX_OPL) || !defined(DISABLE_NUKED_OPL) || defined(USE_NFM)
 #define ENABLE_OPL3
 #endif
 
diff --git a/audio/fmopl.cpp b/audio/fmopl.cpp
index 8eba28c3cea..d56083fc409 100644
--- a/audio/fmopl.cpp
+++ b/audio/fmopl.cpp
@@ -22,8 +22,13 @@
 #include "audio/fmopl.h"
 
 #ifdef USE_RETROWAVE
-#include "audio/rwopl3.h"
+	#include "audio/rwopl3.h"
 #endif
+
+#ifdef USE_NFM
+	#include "audio/nfmopl.h"
+#endif
+
 #include "audio/softsynth/opl/dosbox.h"
 #include "audio/softsynth/opl/mame.h"
 #include "audio/softsynth/opl/nuked.h"
@@ -38,22 +43,34 @@ namespace OPL {
 
 #ifdef USE_ALSA
 namespace ALSA {
-	OPL *create(Config::OplType type);
+OPL *create(Config::OplType type);
 } // End of namespace ALSA
 #endif // USE_ALSA
 
 #ifdef ENABLE_OPL2LPT
 namespace OPL2LPT {
-	OPL *create(Config::OplType type);
+OPL *create(Config::OplType type);
 } // End of namespace OPL2LPT
 #endif // ENABLE_OPL2LPT
 
 #ifdef USE_RETROWAVE
 namespace RetroWaveOPL3 {
-	OPL *create(Config::OplType type);
+OPL *create(Config::OplType type);
 } // End of namespace RetroWaveOPL3
 #endif // ENABLE_RETROWAVE_OPL3
 
+#ifdef USE_NFM
+namespace NfmOPL {
+namespace RealChip {
+OPL *create(Config::OplType type, enum NfmOPL::OplDevice dt);
+} // End of namespace RealChip
+
+namespace EmulatedChip {
+OPL *create(Config::OplType type, enum NfmOPL::OplDevice dt);
+} // End of namespace EmulatedChip
+} // End of namespace NfmOPL
+#endif
+
 // Config implementation
 
 enum OplEmulator {
@@ -66,6 +83,17 @@ enum OplEmulator {
 	kOPL2LPT = 6,
 	kOPL3LPT = 7,
 	kRWOPL3 = 8
+#ifdef USE_NFM
+	,kNfmNokturnFM2 = 9,
+	kNfmNokturnFM3 = 10,
+	kNfmRWOpl3Express = 11,
+	kNfmOPL2LPT = 12,
+	kNfmOPL3LPT = 13,
+	kNfmCeOPL2AudioBoard = 14,
+	kNfmCeOPL3Duo = 15,
+	kNfmNatfeatsNull = 16,
+	kNfmNukedOpl3 = 17
+#endif
 };
 
 OPL::OPL() {
@@ -99,6 +127,17 @@ const Config::EmulatorDescription Config::_drivers[] = {
 #endif
 #ifdef USE_RETROWAVE
 	{"rwopl3", _s("RetroWave OPL3"), kRWOPL3, kFlagOpl2 | kFlagDualOpl2 | kFlagOpl3},
+#endif
+#ifdef USE_NFM
+	{"nfm_nokturnfm2", _s("[nFM] NokturnFM2 (OPL2)"), kNfmNokturnFM2, kFlagOpl2 },
+	{"nfm_nokturnfm3", _s("[nFM] NokturnFM3 (OPL3)"), kNfmNokturnFM3, kFlagOpl2 | kFlagDualOpl2 | kFlagOpl3},
+	{"nfm_rwopl3", _s("[nFM] RetroWave USB OPL3 Express (OPL3)"), kNfmRWOpl3Express, kFlagOpl2 | kFlagDualOpl2 | kFlagOpl3},
+	{"nfm_opl2lpt", _s("[nFM] Serdaco OPL2LPT (OPL2)"), kNfmOPL2LPT, kFlagOpl2 },
+	{"nfm_opl3lpt", _s("[nFM] Serdaco OPL3LPT (OPL3)"), kNfmOPL3LPT, kFlagOpl2 | kFlagDualOpl2 | kFlagOpl3},
+	{"nfm_ce_opl2ab", _s("[nFM] Cheerful Electronics OPL2 Audio Board (OPL2)"), kNfmCeOPL2AudioBoard, kFlagOpl2 },
+	{"nfm_ce_opl3duo", _s("[nFM] Cheerful Electronics OPL3 Duo! (2xOPL3)"), kNfmCeOPL3Duo, kFlagOpl2 | kFlagDualOpl2 | kFlagOpl3},
+	{"nfm_natfeats_null", _s("[nFM] NatFeats / NULL"), kNfmNatfeatsNull, kFlagOpl2 | kFlagDualOpl2 | kFlagOpl3},
+	{"nfm_nuked_opl3", _s("[nFM] Nuked-OPL3 softsynth (OPL3)"), kNfmNukedOpl3, kFlagOpl2 | kFlagDualOpl2 | kFlagOpl3},
 #endif
 	{ nullptr, nullptr, 0, 0 }
 };
@@ -234,7 +273,7 @@ OPL *Config::create(DriverId driver, OplType type) {
 		}
 
 		warning("OPL2LPT only supprts OPL2");
-		return 0;
+		return nullptr;
 	case kOPL3LPT:
 		return OPL2LPT::create(type);
 #endif
@@ -244,6 +283,49 @@ OPL *Config::create(DriverId driver, OplType type) {
 		return RetroWaveOPL3::create(type);
 #endif
 
+#ifdef USE_NFM
+	case kNfmNokturnFM2: {
+		if (type == kOpl2) {
+			return NfmOPL::RealChip::create(type, NfmOPL::dtNokturnFM2);
+		}
+		warning("NokturnFM2 supports only OPL2");
+		return nullptr;
+	};
+
+	case kNfmNokturnFM3:
+		return NfmOPL::RealChip::create(type, NfmOPL::dtNokturnFM3);
+
+	case kNfmRWOpl3Express:
+		return NfmOPL::RealChip::create(type, NfmOPL::dtRWOpl3Express);
+
+	case kNfmOPL2LPT: {
+		if (type == kOpl2) {
+			return NfmOPL::RealChip::create(type, NfmOPL::dtOPL2LPT);
+		}
+		warning("OPL2LPT supports only OPL2");
+		return nullptr;
+	};
+
+	case kNfmOPL3LPT:
+		return NfmOPL::RealChip::create(type, NfmOPL::dtOPL3LPT);
+
+	case kNfmCeOPL2AudioBoard: {
+		if (type == kOpl2) {
+			return NfmOPL::RealChip::create(type, NfmOPL::dtOPL2AudioBoard);
+		}
+
+		warning("OPL2 Audio Board supports only OPL2");
+		return nullptr;
+	};
+
+	case kNfmCeOPL3Duo:
+		return NfmOPL::RealChip::create(type, NfmOPL::dtOPL3Duo);
+	case kNfmNatfeatsNull:
+		return NfmOPL::RealChip::create(type, NfmOPL::dtNatfeatsOpl);
+	case kNfmNukedOpl3:
+		return NfmOPL::EmulatedChip::create(type, NfmOPL::dtNukedOpl3);
+#endif
+
 	case kNull:
 		return new NullOPL();
 
@@ -275,9 +357,9 @@ bool OPL::emulateDualOpl2OnOpl3(int r, int v, Config::OplType oplType) {
 	// Prevent writes to the following registers of the second set:
 	// - 01 - Test register. Setting any bit here will disable output.
 	// - 04 - Connection select. This is used to enable 4 operator instruments,
-	//		  which are not used for dual OPL2.
+	//        which are not used for dual OPL2.
 	// - 05 - New. Only allow writes which set bit 0 to 1, which enables OPL3
-	//		  features.
+	//        features.
 	if (r == 0x101 || r == 0x104 || (r == 0x105 && ((v & 1) == 0)))
 		return false;
 
diff --git a/audio/module.mk b/audio/module.mk
index 9841aa55a69..54633bd7bfa 100644
--- a/audio/module.mk
+++ b/audio/module.mk
@@ -133,6 +133,11 @@ MODULE_OBJS += \
 	rwopl3.o
 endif
 
+ifdef USE_NFM
+MODULE_OBJS += \
+	nfmopl.o
+endif
+	
 ifdef USE_VGMTRANS_AUDIO
 MODULE_OBJS += \
 	soundfont/rawfile.o \
diff --git a/audio/nfmopl.cpp b/audio/nfmopl.cpp
new file mode 100644
index 00000000000..8af695f3bee
--- /dev/null
+++ b/audio/nfmopl.cpp
@@ -0,0 +1,694 @@
+/* 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/>.
+ *
+ */
+
+/*
+    OPL interface using nFM library(https://framagit.org/nokturnal/nfm)
+    for NokturnFM2 / 3, OPL-L carts, CE OPL2 Audio board, CE OPL3 Duo!, Serdaco OPL2LPT / OPL3LPT, RetroWave OPL3 Express and more ...
+    (c) 2023-26 Pawe?? G??ralski
+ */
+
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+#include "common/scummsys.h"
+#include "audio/mixer.h"
+#include "audio/fmopl.h"
+#include "audio/nfmopl.h"
+
+#include "backends/platform/atari/atari-debug.h"
+
+extern "C"
+{
+#include <hwinfo.h>
+#include <sc_gmemalloc.h>
+#include <nfmoplshadowregs.h>
+}
+
+#ifndef RELEASE_BUILD
+	#define NFM_ENABLE_LOGS 1
+#endif
+
+#define NFM_ENABLE_BUFFERED_OUTPUT false
+#define NFM_ENABLE_CUSTOM_ALLOC 1
+
+// TODO set for Linux / Win
+//static const char gsOpl3ExpressPortName[] = "";
+
+#if NFM_ENABLE_CUSTOM_ALLOC
+	// custom allocators
+	#include "backends/platform/atari/dlmalloc.h"
+	#define NFM_MSPACE_SIZE 1*1024
+#endif
+
+namespace OPL {
+namespace NfmOPL {
+
+#if NFM_ENABLE_CUSTOM_ALLOC
+
+extern "C"
+{
+	static mspace s_mNfmSpace = nullptr;
+	static sMemoryCallbacks s_MemCallbacks;
+	static void *s_nfmMemoryBase = nullptr;
+
+	static void *nfmAlloc(size_t amount, const eMemoryFlag flag, void* userData, const char* functionName, char* fileName, uint32_t lineNo) {
+#if NFM_ENABLE_LOGS
+		atari_debug("nfmAlloc()");
+#endif
+		return mspace_malloc(s_mNfmSpace, amount);
+	}
+
+	static void *nfmAlignedAlloc(size_t alignment, size_t amount, const eMemoryFlag flag, void* userData, const int8_t* functionName, int8_t* fileName, uint32_t lineNo) {
+#if NFM_ENABLE_LOGS
+		atari_debug("nfmAlignedAlloc()");
+#endif
+		return mspace_memalign(s_mNfmSpace, alignment, amount);
+	}
+
+	static void *nfmRealloc(void* pOriginal, size_t size, void* userData) {
+#if NFM_ENABLE_LOGS
+		atari_debug("nfmRealloc()");
+#endif
+		return mspace_realloc(s_mNfmSpace, pOriginal, size);
+	}
+
+	static void nfmFree(void* ptr, void* userData) {
+#if NFM_ENABLE_LOGS
+		atari_debug("nfmFree()");
+#endif
+		mspace_free(s_mNfmSpace, ptr);
+	}
+
+	static void nfmOutOfMemoryCb(void* userData) {
+#if NFM_ENABLE_LOGS
+		atari_debug("nfmOutOfMemoryCb() out of memory!");
+#endif
+	}
+
+}
+#endif
+
+#if NFM_ENABLE_LOGS
+static const char *s_DebugConfigMsgStrs[NfmOPL::dtNumDevices] = {
+	"Configuring NokturnFM2 cartridge",
+	"Configuring NokturnFM3 cartridge",
+	"Configuring RetroWave OPL3 Express",
+	"Configuring Serdaco OPL2LPT",
+	"Configuring Serdaco OPL3LPT",
+	"Configuring CE OPL2 Audio Board",
+	"Configuring CE OPL3 Duo!",
+	"Configuring NatFeats / NULL",
+	"Configuring Nuked-OPL3",
+};
+
+static const char *s_DebugOplWriteStrs[NfmOPL::dtNumDevices] = {
+	"NfmOPL NokturnFM2 writeReg",
+	"NfmOPL NokturnFM3 writeReg",
+	"NfmOPL OPL3 Express writeReg",
+	"NfmOPL OPL2LPT writeReg",
+	"NfmOPL OPL3LPT writeReg",
+	"NfmOPL OPL2AudioBoard writeReg",
+	"NfmOPL OPL3Duo writeReg",
+	"NfmOPL NatFeats / NULL writeReg",
+	"NfmOPL Nuked-OPL3 writeReg",
+};
+#endif
+namespace RealChip {
+// hardware opl
+OPL::OPL(Config::OplType type, NfmOPL::OplDevice deviceType) : _type(type), _deviceType(deviceType), _activeReg(0), _initialized(false), _useBuffer(NFM_ENABLE_BUFFERED_OUTPUT), _incapableDevice(false) {
+#if NFM_ENABLE_LOGS
+	atari_debug("NfmOPL::RealChip create");
+	atari_debug("Requesting hardware info");
+#endif
+	Supexec(updateHardwareInfo);
+
+	// defaults
+	_ifaceCfg.deviceType = eFmDriverType::FMD_UNDEFINED;
+	_ifaceCfg.soundchip = CM_UNDEFINED;
+	_ifaceCfg.operationMode = CO_UNDEFINED;
+	_ifaceCfg.setup = CC_UNDEFINED;
+	_ifaceCfg.dualChipEmulationEnabled = false;
+	
+	_oplWrite = nullptr;
+	_oplEnqueWrite = nullptr;
+	_oplFlush = nullptr;
+	_oplReset = nullptr;
+
+	if (_type == Config::kOpl2) {
+		_ifaceCfg.operationMode = CO_OPL2;
+	}
+
+	if (_type == Config::kOpl3 || _type == Config::kDualOpl2) {
+		_ifaceCfg.operationMode = CO_OPL3;
+	}
+
+	_ifaceCfg.setup = CC_SINGLE;
+
+#if NFM_ENABLE_LOGS
+	if (deviceType < dtNumDevices) {
+		atari_debug(s_DebugConfigMsgStrs[deviceType]);
+	}
+#endif
+
+	switch (deviceType) {
+	case dtNokturnFM2: {
+		_params.uParam.outputPort = OPT_ST_CART;
+		_ifaceCfg.deviceType = eFmDriverType::FMD_OPLCART;
+		_ifaceCfg.soundchip = CM_OPL2;
+		_ifaceCfg.setup = CC_SINGLE;
+
+		if (_type == Config::kOpl3 || _type == Config::kDualOpl2) {
+			_incapableDevice = true;
+		}
+	}
+	break;
+	case dtNokturnFM3: {
+		_params.uParam.outputPort = OPT_ST_CART;
+		_ifaceCfg.deviceType = eFmDriverType::FMD_OPLCART;
+		_ifaceCfg.soundchip = CM_OPL3;
+		_ifaceCfg.setup = CC_SINGLE;
+	}
+	break;
+	case dtRWOpl3Express: {
+		_useBuffer = true;                                          // use buffered output, sending data has significant overhead
+		_params.uOpl3ExpressSettings.outputPort = OPT_USB;
+		_ifaceCfg.deviceType = eFmDriverType::FMD_OPL3EXPRESS;
+		_ifaceCfg.soundchip = CM_OPL3;
+		_ifaceCfg.setup = CC_SINGLE;
+		// TODO set serial port on Win / Linux requested by user
+	}
+	break;
+	case dtOPL2LPT: {
+		_params.uParam.outputPort = OPT_LPT;
+
+		_ifaceCfg.deviceType = eFmDriverType::FMD_OPL2LPT;
+		_ifaceCfg.soundchip = CM_OPL2;
+		_ifaceCfg.setup = CC_SINGLE;
+
+		if (_type == Config::kOpl3 || _type == Config::kDualOpl2) {
+			_incapableDevice = true;
+		}
+	}
+	break;
+	case dtOPL3LPT: {
+		// OPL2 mode is forced internally on anything below TT due to lack of signals
+		_params.uParam.outputPort = OPT_LPT;
+
+		_ifaceCfg.deviceType = eFmDriverType::FMD_OPL3LPT;
+		_ifaceCfg.soundchip = CM_OPL3;
+		_ifaceCfg.setup = CC_SINGLE;
+	}
+	break;
+	case dtOPL2AudioBoard: {
+		_params.uParam.outputPort = OPT_LPT_SPI;
+		_params.uCeAudioBoardSettings.isOpl2AudioBoard = true;
+		_ifaceCfg.deviceType = eFmDriverType::FMD_CE_OPL2AUDIO_LPT_SPI;
+		_ifaceCfg.soundchip = CM_OPL2;
+		_ifaceCfg.setup = CC_SINGLE;
+
+		if (_type == Config::kOpl3 || _type == Config::kDualOpl2) {
+			_incapableDevice = true;
+		}
+	}
+	break;
+	case dtOPL3Duo: {
+		_params.uParam.outputPort = OPT_LPT_SPI;
+		_params.uCeAudioBoardSettings.isOpl2AudioBoard = false;
+		_ifaceCfg.deviceType = eFmDriverType::FMD_CE_OPL3DUO_LPT_SPI;
+		_ifaceCfg.soundchip = CM_OPL3;
+		_ifaceCfg.setup = CC_SINGLE;
+	}
+	break;
+	case dtNatfeatsOpl: {
+		_params.uParam.outputPort = OPT_INTERNAL;
+		_ifaceCfg.deviceType = eFmDriverType::FMD_NULL;
+		_ifaceCfg.soundchip = CM_OPL3;
+		_ifaceCfg.setup = CC_SINGLE;
+	}
+	break;
+	default: {
+		warning("NfmOPL::RealChip Unrecognized device type or software synthesizer was requested.");
+	}
+	break;
+	};
+}
+
+OPL::~OPL() {
+	if (_initialized == true) {
+#if NFM_ENABLE_LOGS
+		atari_debug("NfmOPL destroy");
+#endif
+		if (_useBuffer) {
+			// flush
+#if NFM_ENABLE_LOGS
+			atari_debug("OPL flush");
+#endif
+			_oplFlush();
+		}
+
+		_oplWrite = nullptr;
+		_oplEnqueWrite = nullptr;
+		_oplFlush = nullptr;
+		_oplReset = nullptr;
+
+		(void)nfDeinit(&_iface);
+		(void)nfDestroyInterface(&_iface);
+
+		_incapableDevice = false;
+		_useBuffer = false;
+		_initialized = false;
+#if NFM_ENABLE_CUSTOM_ALLOC
+		if (s_mNfmSpace) {
+			destroy_mspace(s_mNfmSpace);
+			Mfree(s_nfmMemoryBase);
+			s_nfmMemoryBase = nullptr;
+		}
+#endif
+	}
+}
+
+bool OPL::init() {
+#if NFM_ENABLE_LOGS
+	atari_debug("NfmOPL::RealChip init");
+#endif
+	if (_incapableDevice) {
+#if NFM_ENABLE_LOGS
+		atari_debug("NfmOPL::RealChip OPL2 device cannot emulate requested dual OPL2 / OPL3!");
+#endif
+		return false;
+	}
+
+#if NFM_ENABLE_CUSTOM_ALLOC
+	// set default callbacks
+	setDefaultUserMemoryCallbacks();
+
+	s_nfmMemoryBase = (void *)Mxalloc((int32_t)NFM_MSPACE_SIZE + 256, (int16_t)3);
+
+	if (s_nfmMemoryBase) {
+		s_mNfmSpace = create_mspace_with_base(s_nfmMemoryBase, NFM_MSPACE_SIZE, 0);
+
+		if (s_mNfmSpace == 0) {
+#if NFM_ENABLE_LOGS
+			atari_debug("NfmOPL::RealChip create_mspace failed!");
+#endif
+			return false;
+		}
+
+		// install custom memory allocator callbacks
+		s_MemCallbacks.alloc = nfmAlloc;
+		s_MemCallbacks.alignedAlloc = nfmAlignedAlloc;
+		s_MemCallbacks.release = nfmFree;
+		s_MemCallbacks.realloc = nfmRealloc;
+		s_MemCallbacks.outOfMemory = nfmOutOfMemoryCb;
+
+		setUserMemoryCallbacks(&s_MemCallbacks);
+	} else {
+#if NFM_ENABLE_LOGS
+		atari_debug("NfmOPL::RealChip Out of system memory!");
+#endif
+		return false;
+	}
+#else
+	setDefaultUserMemoryCallbacks();
+#endif
+	_iface = nfCreateInterface(_ifaceCfg);
+
+	if (_iface.setup != CC_UNDEFINED) {
+		const int32_t retval = nfInit(&_iface, &_params);
+
+		if (retval >= 0) {
+			_oplWrite = _iface.write;
+			_oplEnqueWrite = _iface.enqueWrite;
+			_oplFlush = _iface.flush;
+			_oplReset = _iface.reset;
+
+			initDualOpl2OnOpl3(_type);
+			_initialized = true;
+
+#if NFM_ENABLE_LOGS
+			atari_debug("NfmOPL::RealChip init OK");
+#endif
+			return true;
+		}
+	}
+
+#if NFM_ENABLE_LOGS
+	atari_debug("NfmOPL::RealChip init failed!");
+#endif
+	return false;
+}
+
+void OPL::reset() {
+#if NFM_ENABLE_LOGS
+	atari_debug("NfmOPL::RealChip reset");
+#endif
+	for (int16_t i = 0; i < 256; i ++) {
+		writeReg((int)i, 0);
+	}
+
+	if (_type == Config::kOpl3 || _type == Config::kDualOpl2) {
+		for (int16_t i = 0; i < 256; i++) {
+			writeReg((int)i + 256, 0);
+		}
+	}
+
+	_activeReg = 0;
+
+	initDualOpl2OnOpl3(_type);
+}
+
+void OPL::write(int portAddress, int value) {
+	if (portAddress & 1) {
+		writeReg(_activeReg, value);
+		return;
+	} else {
+		if (_type == Config::kOpl2) {
+			_activeReg = value & 0xff;
+			return;
+		} else {
+			// opl3 / dual opl2
+			_activeReg = (value & 0xff) | ((portAddress << 7) & 0x100);
+			return;
+		}
+
+		warning("NfmOPL::RealChip: unsupported OPL mode %d", _type);
+	}
+}
+
+void OPL::writeReg(int reg, int value) {
+#if NFM_ENABLE_LOGS
+	atari_debug(s_DebugOplWriteStrs[_deviceType]);
+#endif
+
+	if (_type == Config::kOpl3 || _type == Config::kDualOpl2) {
+		reg &= 0x1ff;
+	} else {
+		reg &= 0xff;
+	}
+
+	value &= 0xff;
+
+	if (emulateDualOpl2OnOpl3(reg, value, _type)) {
+		sOplRegisterWrite regWrite;
+
+		if (reg < 0x100) {
+			regWrite = {0, (uint8_t)reg, (uint8_t)value};
+		} else {
+			regWrite = {1, (uint8_t)(reg - 0x100), (uint8_t)value};
+		}
+
+		if (_useBuffer) {
+			_oplEnqueWrite(&regWrite);
+		} else {
+			_oplWrite(&regWrite);
+		}
+	}
+}
+
+void OPL::onTimer() {
+	if (_useBuffer) {
+		if (_initialized) {
+#if NFM_ENABLE_LOGS
+			// flush
+			atari_debug("NfmOPL::RealChip flush");
+#endif
+			_oplFlush();
+		}
+	}
+
+	Audio::RealChip::onTimer();
+}
+
+OPL *create(Config::OplType type, OplDevice device) {
+	return new OPL(type, device);
+}
+} // End of namespace RealChip
+
+namespace EmulatedChip {
+OPL::OPL(Config::OplType type, enum NfmOPL::OplDevice deviceType): _type(type), _rate(0), _deviceType(deviceType), _activeReg(0), _initialized(false), _useBuffer(NFM_ENABLE_BUFFERED_OUTPUT), _incapableDevice(false) {
+#if NFM_ENABLE_LOGS
+	atari_debug("NfmOPL::EmulatedChip create");
+	atari_debug("Requesting hardware info");
+#endif
+	Supexec(updateHardwareInfo);
+
+	// defaults
+	_ifaceCfg.deviceType = eFmDriverType::FMD_UNDEFINED;
+	_ifaceCfg.soundchip = CM_UNDEFINED;
+	_ifaceCfg.operationMode = CO_UNDEFINED;
+	_ifaceCfg.setup = CC_UNDEFINED;
+	_ifaceCfg.dualChipEmulationEnabled = false;
+
+	_oplWrite = nullptr;
+	_oplEnqueWrite = nullptr;
+	_oplFlush = nullptr;
+	_oplReset = nullptr;
+	_generateAudioStream = nullptr;
+
+	if (_type == Config::kOpl2) {
+		_ifaceCfg.operationMode = CO_OPL2;
+	}
+
+	if (_type == Config::kOpl3 || _type == Config::kDualOpl2) {
+		_ifaceCfg.operationMode = CO_OPL3;
+	}
+
+	_ifaceCfg.setup = CC_SINGLE;
+
+#if NFM_ENABLE_LOGS
+	if (deviceType < dtNumDevices) {
+		atari_debug(s_DebugConfigMsgStrs[deviceType]);
+	}
+#endif
+	switch (deviceType) {
+	case dtNukedOpl3: {
+		_rate = g_system->getMixer()->getOutputRate();
+		_params.uSoftSynthSettings.outputPort = OPT_INTERNAL;
+		_params.uSoftSynthSettings.sampleRate = _rate;
+		_params.uSoftSynthSettings.enableDualChipEmulation = _ifaceCfg.dualChipEmulationEnabled;
+
+		_ifaceCfg.deviceType = FMD_NUKEDOPL3;
+		_ifaceCfg.soundchip = CM_OPL3;
+		_ifaceCfg.operationMode = CO_OPL3;
+		_ifaceCfg.setup = CC_SINGLE;
+	}
+	break;
+
+	default: {
+		warning("NfmOPL::EmulatedChip Unrecognized device type or not software synthesizer!");
+		_incapableDevice = true;
+	}
+	break;
+	};
+}
+
+OPL::~OPL() {
+
+	if (_initialized == true) {
+#if NFM_ENABLE_LOGS
+		atari_debug("NfmOPL::EmulatedChip destroy");
+#endif
+		stop();
+
+		if (_useBuffer) {
+#if NFM_ENABLE_LOGS
+			// flush
+			atari_debug("NfmOPL::EmulatedChip OPL flush");
+#endif
+			_oplFlush();
+		}
+
+		_oplWrite = nullptr;
+		_oplEnqueWrite = nullptr;
+		_oplFlush = nullptr;
+		_oplReset = nullptr;
+		_generateAudioStream = nullptr;
+
+		(void)nfDeinit(&_iface);
+		(void)nfDestroyInterface(&_iface);
+
+		_incapableDevice = false;
+		_useBuffer = false;
+		_initialized = false;
+
+#if NFM_ENABLE_CUSTOM_ALLOC
+		if (s_mNfmSpace) {
+			destroy_mspace(s_mNfmSpace);
+			Mfree(s_nfmMemoryBase);
+			s_nfmMemoryBase = nullptr;
+		}
+#endif
+	}
+}
+
+bool OPL::init() {
+#if NFM_ENABLE_LOGS
+	atari_debug("NfmOPL::EmulatedChip init");
+#endif
+	if (_incapableDevice) {
+#if NFM_ENABLE_LOGS
+		atari_debug("NfmOPL::EmulatedChip device isn't soft synth type!");
+#endif
+		return false;
+	}
+
+#if NFM_ENABLE_CUSTOM_ALLOC
+	// set default callbacks
+	setDefaultUserMemoryCallbacks();
+
+	s_nfmMemoryBase = (void *)Mxalloc((int32_t)NFM_MSPACE_SIZE + 256, (int16_t)3);
+
+	if (s_nfmMemoryBase) {
+		s_mNfmSpace = create_mspace_with_base(s_nfmMemoryBase, NFM_MSPACE_SIZE, 0);
+
+		if (s_mNfmSpace == 0) {
+#if NFM_ENABLE_LOGS
+			atari_debug("NfmOPL::EmulatedChip create_mspace failed!");
+#endif
+			return false;
+		}
+
+		// install custom memory allocator callbacks
+		s_MemCallbacks.alloc = nfmAlloc;
+		s_MemCallbacks.alignedAlloc = nfmAlignedAlloc;
+		s_MemCallbacks.release = nfmFree;
+		s_MemCallbacks.realloc = nfmRealloc;
+		s_MemCallbacks.outOfMemory = nfmOutOfMemoryCb;
+
+		setUserMemoryCallbacks(&s_MemCallbacks);
+	} else {
+#if NFM_ENABLE_LOGS
+		atari_debug("NfmOPL::EmulatedChip Out of system memory!");
+#endif
+		return false;
+	}
+#else
+	setDefaultUserMemoryCallbacks();
+#endif
+	_iface = nfCreateInterface(_ifaceCfg);
+
+	if (_iface.setup != CC_UNDEFINED) {
+		const int32_t retval = nfInit(&_iface, &_params);
+
+		if (retval >= 0) {
+			_oplWrite = _iface.write;
+			_oplEnqueWrite = _iface.enqueWrite;
+			_oplFlush = _iface.flush;
+			_oplReset = _iface.reset;
+			_generateAudioStream = _iface.generateAudioStream;
+
+			_activeReg = 0;
+
+			initDualOpl2OnOpl3(_type);
+
+			_initialized = true;
+
+#if NFM_ENABLE_LOGS
+			atari_debug("NfmOPL::RealChip init OK");
+#endif
+			return true;
+		}
+	}
+
+#if NFM_ENABLE_LOGS
+	atari_debug("NfmOPL::RealChip init failed!");
+#endif
+	return false;
+}
+
+void OPL::reset() {
+
+	_oplReset();
+
+	for (int16_t i = 0; i < 256; i ++) {
+		writeReg((int)i, 0);
+	}
+
+	if (_type == Config::kOpl3 || _type == Config::kDualOpl2) {
+		for (int16_t i = 0; i < 256; i++) {
+			writeReg((int)i + 256, 0);
+		}
+	}
+
+	_activeReg = 0;
+
+	initDualOpl2OnOpl3(_type);
+}
+
+void OPL::write(int portAddress, int value) {
+	if (portAddress & 1) {
+		writeReg(_activeReg, value);
+		return;
+	} else {
+		if (_type == Config::kOpl2) {
+			_activeReg = value & 0xff;
+			return;
+		} else {
+			// opl3 / dual opl2
+			_activeReg = (value & 0xff) | ((portAddress << 7) & 0x100);
+			return;
+		}
+
+		warning("NfmOPL::EmulatedChip: unsupported OPL mode %d", _type);
+	}
+}
+
+void OPL::writeReg(int reg, int value) {
+#if NFM_ENABLE_LOGS
+	atari_debug(s_DebugOplWriteStrs[_deviceType]);
+#endif
+
+	if (_type == Config::kOpl3 || _type == Config::kDualOpl2) {
+		reg &= 0x1ff;
+	} else {
+		reg &= 0xff;
+	}
+
+	value &= 0xff;
+
+	if (emulateDualOpl2OnOpl3(reg, value, _type)) {
+		sOplRegisterWrite regWrite;
+
+		if (reg < 0x100) {
+			regWrite = {0, (uint8_t)reg, (uint8_t)value};
+		} else {
+			regWrite = {1, (uint8_t)(reg - 0x100), (uint8_t)value};
+		}
+
+		if (_useBuffer) {
+			_oplEnqueWrite(&regWrite);
+		} else {
+			_oplWrite(&regWrite);
+		}
+	}
+}
+
+void OPL::generateSamples(int16 *buffer, int length) {
+	assert(buffer != nullptr);
+	assert(length!=0);
+	_generateAudioStream(buffer, 0, (uint16_t)length / 2);
+}
+
+OPL *create(Config::OplType type, OplDevice device) {
+	return new OPL(type, device);
+}
+} // End of namespace EmulatedChip
+
+} // End of namespace NfmOPL
+} // End of namespace OPL
diff --git a/audio/nfmopl.h b/audio/nfmopl.h
new file mode 100644
index 00000000000..43f2cbd299e
--- /dev/null
+++ b/audio/nfmopl.h
@@ -0,0 +1,133 @@
+/* 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/>.
+ *
+ */
+
+/*
+    OPL interface using nFM library(https://framagit.org/nokturnal/nfm)
+    for NokturnFM2 / 3, OPL-L carts, CE OPL2 Audio board, CE OPL3 Duo!, Serdaco OPL2LPT / OPL3LPT, RetroWave OPL3 Express and more ...
+    (c) 2023-26 Pawe?? G??ralski
+ */
+
+#ifndef AUDIO_SYNTH_NFM_OPL_H
+#define AUDIO_SYNTH_NFM_OPL_H
+
+#include "audio/fmopl.h"
+
+#ifndef restrict
+	#define restrict __restrict__
+#endif
+
+extern "C"
+{
+#include <nfmcore.h>
+#include <nfmutil.h>
+}
+
+namespace OPL {
+namespace NfmOPL {
+enum OplDevice : int16_t {
+	dtNokturnFM2 = 0,
+	dtNokturnFM3,
+	dtRWOpl3Express,
+	dtOPL2LPT,
+	dtOPL3LPT,
+	dtOPL2AudioBoard,
+	dtOPL3Duo,
+	dtNatfeatsOpl,
+	dtNukedOpl3,
+	dtNumDevices
+};
+
+namespace RealChip {
+class OPL : public ::OPL::OPL, public Audio::RealChip {
+private:
+	Config::OplType _type;
+	OplDevice _deviceType;
+	sOplInterface _iface;
+	funcPtrOplWrite _oplWrite;
+	funcPtrOplWrite _oplEnqueWrite;
+	funcPtrOplFlush _oplFlush;
+	funcPtrOplReset _oplReset;
+
+	sInterfaceInitData _params;
+	sOplInterfaceConfiguration _ifaceCfg;
+
+	int _activeReg;
+	bool _initialized;
+	bool _useBuffer;
+	bool _incapableDevice;
+public:
+	explicit OPL(Config::OplType type, enum NfmOPL::OplDevice deviceType);
+	~OPL();
+
+	bool init() override final;
+	void reset() override final;
+
+	void write(int portAddress, int value) override final;
+	void writeReg(int reg, int value) override final;
+
+protected:
+
+	virtual void onTimer() override final;
+};
+};  // End of namespace RealChip
+
+namespace EmulatedChip {
+class OPL : public ::OPL::OPL, public Audio::EmulatedChip {
+private:
+	Config::OplType _type;
+	OplDevice _deviceType;
+	sOplInterface _iface;
+	funcPtrOplWrite _oplWrite;
+	funcPtrOplWrite _oplEnqueWrite;
+	funcPtrOplFlush _oplFlush;
+	funcPtrOplReset _oplReset;
+	funcGenerateAudioStream _generateAudioStream;
+	sInterfaceInitData _params;
+	sOplInterfaceConfiguration _ifaceCfg;
+
+	int _activeReg;
+	uint _rate;
+	bool _initialized;
+	bool _useBuffer;
+	bool _incapableDevice;
+public:
+	explicit OPL(Config::OplType type, enum NfmOPL::OplDevice deviceType);
+	~OPL();
+
+	bool isStereo() const override {
+		return true;
+	}
+
+	bool init() override final;
+	void reset() override final;
+
+	void write(int portAddress, int value) override final;
+	void writeReg(int reg, int value) override final;
+
+protected:
+	void generateSamples(int16 *buffer, int numSamples) override final;
+};
+}; // End of namespace EmulatedChip
+
+} // End of namespace NfmOPL
+} // End of namespace OPL
+
+#endif
diff --git a/backends/platform/atari/build-firebee.sh b/backends/platform/atari/build-firebee.sh
index e175f5476a5..48aee04c5ac 100755
--- a/backends/platform/atari/build-firebee.sh
+++ b/backends/platform/atari/build-firebee.sh
@@ -40,6 +40,7 @@ then
 	--with-freetype2-prefix="$(${PLATFORM}-gcc -print-sysroot)/usr/bin/${CPU_DIR}" \
 	--with-mikmod-prefix="$(${PLATFORM}-gcc -print-sysroot)/usr/bin/${CPU_DIR}" \
 	--enable-release \
+	--disable-nfm \
 	--enable-verbose-build \
 	${PLUGINS_FLAGS}
 fi
diff --git a/base/version.cpp b/base/version.cpp
index e0cf813636a..1719489d9aa 100644
--- a/base/version.cpp
+++ b/base/version.cpp
@@ -253,4 +253,9 @@ const char gScummVMFeatures[] = ""
 #ifdef USE_RETROWAVE
 	"RetroWave "
 #endif
+
+#ifdef USE_NFM
+	"Nokturnal nFM "
+#endif
+
 	;
diff --git a/configure b/configure
index cdbe5b166dd..91512ba356a 100755
--- a/configure
+++ b/configure
@@ -160,6 +160,7 @@ _flac=auto
 _mad=auto
 _opl2lpt=no
 _retrowave=auto
+_nfm=auto
 _alsa=auto
 _seq_midi=auto
 _sndio=auto
@@ -1172,6 +1173,9 @@ Optional Libraries:
   --with-retrowave-prefix=DIR prefix where libretrowave is installed (optional)
   --enable-retrowave          enable RetroWave OPL3 support
 
+  --with-nfm-prefix=DIR  prefix where nFM is installed (optional)
+  --enable-nfm           enable OPL2/3 support via nFM
+
   --with-sparkle-prefix=DIR prefix where sparkle is installed
                             (macOS/Windows only - optional)
   --disable-sparkle         disable sparkle automatic update support
@@ -1310,6 +1314,8 @@ for ac_option in $@; do
 	--disable-opl2lpt)            _opl2lpt=no            ;;
 	--enable-retrowave)           _retrowave=yes         ;;
 	--disable-retrowave)          _retrowave=no          ;;
+	--enable-nfm)                 _nfm=yes               ;;
+	--disable-nfm)                _nfm=no                ;;
 	--enable-flac)                _flac=yes              ;;
 	--disable-flac)               _flac=no               ;;
 	--enable-mad)                 _mad=yes               ;;
@@ -1496,6 +1502,11 @@ for ac_option in $@; do
 		RETROWAVE_CFLAGS="-I$arg/include"
 		RETROWAVE_LIBS="-L$arg/lib"
 		;;
+	--with-nfm-prefix=*)
+		arg=`echo $ac_option | cut -d '=' -f 2`
+		NFM_CFLAGS="-I$arg/include -I$arg/include/platform/atari"
+		NFM_LIBS="-L$arg/lib/m68020-60"
+		;;
 	--with-flac-prefix=*)
 		arg=`echo $ac_option | cut -d '=' -f 2`
 		FLAC_CFLAGS="-I$arg/include"
@@ -3883,6 +3894,7 @@ if test -n "$_host"; then
 			#	append_var LDFLAGS "-s"
 			#fi
 			append_var DEFINES "-DSCUMM_OPTIMISED_CODE"
+			append_var DEFINES "-DDISABLE_DOSBOX_OPL"
 
 			# auto -> no
 			if test "$_optimizations" = "yes"; then
@@ -5593,6 +5605,58 @@ fi
 define_in_config_if_yes "$_retrowave" 'USE_RETROWAVE'
 echo "$_retrowave"
 
+#
+# Check for nFM OPL2/3 library
+#
+echocheck "nFM OPL2 / OPL3"
+if test "$_nfm" = auto ; then
+	_nfm=no
+	cat > $TMPC << EOF
+extern "C"
+{
+#include <nfmcore.h>
+#include <nfmutil.h>
+}
+
+sOplInterface gOplIf;
+
+int main(void)
+{ 
+	sOplInterfaceConfiguration ifCfg;
+	ifCfg.deviceType = FMD_NULL;
+    ifCfg.soundchip = CM_OPL3;
+    ifCfg.operationMode = CO_OPL3;
+    ifCfg.setup = CC_SINGLE;
+    ifCfg.dualChipEmulationEnabled = false;
+
+    sInterfaceInitData params;
+	params.uParam.outputPort = OPT_INTERNAL;
+    params.uParam.param = 0;
+
+    gOplIf = nfCreateInterface(ifCfg); 
+
+    const int32_t retval = nfInit(&gOplIf, &params);
+
+    if (retval >= 0)
+    {
+	 (void)nfDeinit(&gOplIf);
+     return 0;
+    }	
+
+  return -1;
+}
+EOF
+	cc_check $NFM_CFLAGS -DSC_PLATFORM_ATARI=1 -DSC_ENABLE_MALLOC=0 -DNFM_OPL_SHADOW_REG_WRITE_ENABLE=1 $NFM_LIBS -lnfmcore -lnfmutil -ldrvcommon -ldrvnokturnfm -ldrvopl3Duo -ldrvopl3express -ldrvoplxlpt -ldrvoplnull -lsyscore -lcontainers -lallocators &&\
+	_nfm=yes
+fi
+if test "$_nfm" = yes; then
+	append_var DEFINES "-DSC_PLATFORM_ATARI=1 -DSC_ENABLE_MALLOC=0 -DNFM_OPL_SHADOW_REG_WRITE_ENABLE=1"
+	append_var LIBS "$NFM_LIBS -lnfmcore -lnfmutil -ldrvcommon -ldrvnokturnfm -ldrvopl3Duo -ldrvopl3express -ldrvoplxlpt -ldrvoplnull -lsyscore -lcontainers -lallocators"
+	append_var INCLUDES "$NFM_CFLAGS"
+fi
+define_in_config_if_yes "$_nfm" 'USE_NFM'
+echo "$_nfm"
+
 #
 # Check for FLAC
 #
diff --git a/devtools/create_project/create_project.cpp b/devtools/create_project/create_project.cpp
index 3e6e98768da..af4d776dca9 100644
--- a/devtools/create_project/create_project.cpp
+++ b/devtools/create_project/create_project.cpp
@@ -1203,6 +1203,7 @@ const Feature s_features[] = {
 	{    "sdlnet",     "USE_SDL_NET", true, true,  "SDL_net support" },
 	{   "discord",     "USE_DISCORD", true, false, "Discord support" },
 	{ "retrowave",   "USE_RETROWAVE", true, false, "RetroWave OPL3 support" },
+	{     "nFM",           "USE_NFM", true, false, "Nokturnal nFM OPL2/OPL3 support" },
 	{       "a52",         "USE_A52", true, false, "ATSC A/52 support" },
 	{       "mpc",      "USE_MPCDEC", true, false, "Musepack support" },
 


Commit: 45e363f353bc9e601344333cb575f444fef7550d
    https://github.com/scummvm/scummvm/commit/45e363f353bc9e601344333cb575f444fef7550d
Author: Pawe?? G??ralski (20125592+n0kturnal at users.noreply.github.com)
Date: 2026-06-21T15:19:56+02:00

Commit Message:
AUDIO : Atari TOS hardware / software OPL2 / 3 (YM262 / YM3812)

Fixed formatting.

Changed paths:
    devtools/create_project/create_project.cpp


diff --git a/devtools/create_project/create_project.cpp b/devtools/create_project/create_project.cpp
index af4d776dca9..f2c09800889 100644
--- a/devtools/create_project/create_project.cpp
+++ b/devtools/create_project/create_project.cpp
@@ -1203,7 +1203,7 @@ const Feature s_features[] = {
 	{    "sdlnet",     "USE_SDL_NET", true, true,  "SDL_net support" },
 	{   "discord",     "USE_DISCORD", true, false, "Discord support" },
 	{ "retrowave",   "USE_RETROWAVE", true, false, "RetroWave OPL3 support" },
-	{     "nFM",           "USE_NFM", true, false, "Nokturnal nFM OPL2/OPL3 support" },
+	{       "nFM",         "USE_NFM", true, false, "Nokturnal nFM OPL2/OPL3 support" },
 	{       "a52",         "USE_A52", true, false, "ATSC A/52 support" },
 	{       "mpc",      "USE_MPCDEC", true, false, "Musepack support" },
 


Commit: fa13bd283be5e56f303daa5d87516496bd8ed5eb
    https://github.com/scummvm/scummvm/commit/fa13bd283be5e56f303daa5d87516496bd8ed5eb
Author: Pawe?? G??ralski (20125592+n0kturnal at users.noreply.github.com)
Date: 2026-06-21T15:19:56+02:00

Commit Message:
AUDIO : Atari TOS hardware / software OPL2 / 3 (YM262 / YM3812)

Explicit setting of SC_PLATFORM_ATARI define was removed, macro is set by compiler preprocessor now in nFM library core headers.

Changed paths:
    configure


diff --git a/configure b/configure
index 91512ba356a..3a98319b250 100755
--- a/configure
+++ b/configure
@@ -5646,11 +5646,11 @@ int main(void)
   return -1;
 }
 EOF
-	cc_check $NFM_CFLAGS -DSC_PLATFORM_ATARI=1 -DSC_ENABLE_MALLOC=0 -DNFM_OPL_SHADOW_REG_WRITE_ENABLE=1 $NFM_LIBS -lnfmcore -lnfmutil -ldrvcommon -ldrvnokturnfm -ldrvopl3Duo -ldrvopl3express -ldrvoplxlpt -ldrvoplnull -lsyscore -lcontainers -lallocators &&\
+	cc_check $NFM_CFLAGS -DSC_ENABLE_MALLOC=0 -DNFM_OPL_SHADOW_REG_WRITE_ENABLE=1 $NFM_LIBS -lnfmcore -lnfmutil -ldrvcommon -ldrvnokturnfm -ldrvopl3Duo -ldrvopl3express -ldrvoplxlpt -ldrvoplnull -lsyscore -lcontainers -lallocators &&\
 	_nfm=yes
 fi
 if test "$_nfm" = yes; then
-	append_var DEFINES "-DSC_PLATFORM_ATARI=1 -DSC_ENABLE_MALLOC=0 -DNFM_OPL_SHADOW_REG_WRITE_ENABLE=1"
+	append_var DEFINES "-DSC_ENABLE_MALLOC=0 -DNFM_OPL_SHADOW_REG_WRITE_ENABLE=1"
 	append_var LIBS "$NFM_LIBS -lnfmcore -lnfmutil -ldrvcommon -ldrvnokturnfm -ldrvopl3Duo -ldrvopl3express -ldrvoplxlpt -ldrvoplnull -lsyscore -lcontainers -lallocators"
 	append_var INCLUDES "$NFM_CFLAGS"
 fi


Commit: a0a300c1bc48247dee298c86e8b7aaa63b7d8716
    https://github.com/scummvm/scummvm/commit/a0a300c1bc48247dee298c86e8b7aaa63b7d8716
Author: Pawe?? G??ralski (20125592+n0kturnal at users.noreply.github.com)
Date: 2026-06-21T15:19:56+02:00

Commit Message:
AUDIO : Atari TOS hardware / software OPL2 / 3 (YM262 / YM3812)

Helper script for downloading prebuild nFM library to Atari backend folder

Changed paths:
  A backends/platform/atari/get-nfm.sh


diff --git a/backends/platform/atari/get-nfm.sh b/backends/platform/atari/get-nfm.sh
new file mode 100644
index 00000000000..831a759d8d8
--- /dev/null
+++ b/backends/platform/atari/get-nfm.sh
@@ -0,0 +1,14 @@
+#!/bin/bash -eux
+# -e: Exit immediately if a command exits with a non-zero status.
+# -u: Treat unset variables as an error when substituting.
+# -x: Display expanded script commands
+
+# downloads and unpacks in ./deps folder precompiled nFM library release for m68k Atari TOS / mintelf gcc stdlib
+NFM_VERSION=0.2.1
+
+curl -o ./backends/platform/atari/nfm.zip https://nokturnal.pl/downloads/atari/nokturnfm/nfmplay/builds/releases/0.2.1/ataritos-mintelf-gcc-stdlib-m68k-0.2.1-5c688e1a.zip
+rm -rf ./backends/platform/atari/deps/nfm
+
+unzip ./backends/platform/atari/nfm.zip "nFM_${NFM_VERSION}/*" -d ./backends/platform/atari/deps/
+mv ./backends/platform/atari/deps/nFM_${NFM_VERSION} ./backends/platform/atari/deps/nfm
+rm ./backends/platform/atari/nfm.zip
\ No newline at end of file


Commit: 301678656ff4b6cd66e95811ebef4a834e1198fb
    https://github.com/scummvm/scummvm/commit/301678656ff4b6cd66e95811ebef4a834e1198fb
Author: Pawe?? G??ralski (20125592+n0kturnal at users.noreply.github.com)
Date: 2026-06-21T15:19:56+02:00

Commit Message:
AUDIO : Atari TOS hardware / software OPL2 / 3 (YM262 / YM3812)

Updated helper script for downloading prebuild nFM library to Atari dependencies folder in backend, added ST Bus ISA / VME Sound Blaster to drop down list. Fixed linking issues related to newly added drivers. Fixed debug output - atari_debug() macro was removed.

Changed paths:
    audio/fmopl.cpp
    audio/nfmopl.cpp
    audio/nfmopl.h
    backends/platform/atari/get-nfm.sh
    configure


diff --git a/audio/fmopl.cpp b/audio/fmopl.cpp
index d56083fc409..c760dbc6265 100644
--- a/audio/fmopl.cpp
+++ b/audio/fmopl.cpp
@@ -91,8 +91,9 @@ enum OplEmulator {
 	kNfmOPL3LPT = 13,
 	kNfmCeOPL2AudioBoard = 14,
 	kNfmCeOPL3Duo = 15,
-	kNfmNatfeatsNull = 16,
-	kNfmNukedOpl3 = 17
+	kNfmStBusIsaVmeSb = 16,
+	kNfmNatfeatsNull = 17,
+	kNfmNukedOpl3 = 18
 #endif
 };
 
@@ -136,6 +137,7 @@ const Config::EmulatorDescription Config::_drivers[] = {
 	{"nfm_opl3lpt", _s("[nFM] Serdaco OPL3LPT (OPL3)"), kNfmOPL3LPT, kFlagOpl2 | kFlagDualOpl2 | kFlagOpl3},
 	{"nfm_ce_opl2ab", _s("[nFM] Cheerful Electronics OPL2 Audio Board (OPL2)"), kNfmCeOPL2AudioBoard, kFlagOpl2 },
 	{"nfm_ce_opl3duo", _s("[nFM] Cheerful Electronics OPL3 Duo! (2xOPL3)"), kNfmCeOPL3Duo, kFlagOpl2 | kFlagDualOpl2 | kFlagOpl3},
+	{"nfm_ce_stbus_isa_vme", _s("[nFM] ST Bus ISA / VME SoundBlaster"), kNfmStBusIsaVmeSb, kFlagOpl2 | kFlagDualOpl2 | kFlagOpl3},
 	{"nfm_natfeats_null", _s("[nFM] NatFeats / NULL"), kNfmNatfeatsNull, kFlagOpl2 | kFlagDualOpl2 | kFlagOpl3},
 	{"nfm_nuked_opl3", _s("[nFM] Nuked-OPL3 softsynth (OPL3)"), kNfmNukedOpl3, kFlagOpl2 | kFlagDualOpl2 | kFlagOpl3},
 #endif
@@ -320,6 +322,8 @@ OPL *Config::create(DriverId driver, OplType type) {
 
 	case kNfmCeOPL3Duo:
 		return NfmOPL::RealChip::create(type, NfmOPL::dtOPL3Duo);
+	case kNfmStBusIsaVmeSb:
+		return NfmOPL::RealChip::create(type, NfmOPL::dtStBusIsaVmeSb);
 	case kNfmNatfeatsNull:
 		return NfmOPL::RealChip::create(type, NfmOPL::dtNatfeatsOpl);
 	case kNfmNukedOpl3:
diff --git a/audio/nfmopl.cpp b/audio/nfmopl.cpp
index 8af695f3bee..fb1eec66d7a 100644
--- a/audio/nfmopl.cpp
+++ b/audio/nfmopl.cpp
@@ -27,12 +27,12 @@
 
 #define FORBIDDEN_SYMBOL_ALLOW_ALL
 #include "common/scummsys.h"
+#include "common/debug.h"
+
 #include "audio/mixer.h"
 #include "audio/fmopl.h"
 #include "audio/nfmopl.h"
 
-#include "backends/platform/atari/atari-debug.h"
-
 extern "C"
 {
 #include <hwinfo.h>
@@ -69,35 +69,35 @@ extern "C"
 
 	static void *nfmAlloc(size_t amount, const eMemoryFlag flag, void* userData, const char* functionName, char* fileName, uint32_t lineNo) {
 #if NFM_ENABLE_LOGS
-		atari_debug("nfmAlloc()");
+		debug("nfmAlloc()");
 #endif
 		return mspace_malloc(s_mNfmSpace, amount);
 	}
 
 	static void *nfmAlignedAlloc(size_t alignment, size_t amount, const eMemoryFlag flag, void* userData, const int8_t* functionName, int8_t* fileName, uint32_t lineNo) {
 #if NFM_ENABLE_LOGS
-		atari_debug("nfmAlignedAlloc()");
+		debug("nfmAlignedAlloc()");
 #endif
 		return mspace_memalign(s_mNfmSpace, alignment, amount);
 	}
 
 	static void *nfmRealloc(void* pOriginal, size_t size, void* userData) {
 #if NFM_ENABLE_LOGS
-		atari_debug("nfmRealloc()");
+		debug("nfmRealloc()");
 #endif
 		return mspace_realloc(s_mNfmSpace, pOriginal, size);
 	}
 
 	static void nfmFree(void* ptr, void* userData) {
 #if NFM_ENABLE_LOGS
-		atari_debug("nfmFree()");
+		debug("nfmFree()");
 #endif
 		mspace_free(s_mNfmSpace, ptr);
 	}
 
 	static void nfmOutOfMemoryCb(void* userData) {
 #if NFM_ENABLE_LOGS
-		atari_debug("nfmOutOfMemoryCb() out of memory!");
+		debug("nfmOutOfMemoryCb() out of memory!");
 #endif
 	}
 
@@ -113,6 +113,7 @@ static const char *s_DebugConfigMsgStrs[NfmOPL::dtNumDevices] = {
 	"Configuring Serdaco OPL3LPT",
 	"Configuring CE OPL2 Audio Board",
 	"Configuring CE OPL3 Duo!",
+	"Configuring ST Bus ISA / VME SoundBlaster",
 	"Configuring NatFeats / NULL",
 	"Configuring Nuked-OPL3",
 };
@@ -125,6 +126,7 @@ static const char *s_DebugOplWriteStrs[NfmOPL::dtNumDevices] = {
 	"NfmOPL OPL3LPT writeReg",
 	"NfmOPL OPL2AudioBoard writeReg",
 	"NfmOPL OPL3Duo writeReg",
+	"NfmOPL ST Bus ISA / VME SoundBlaster writeReg",
 	"NfmOPL NatFeats / NULL writeReg",
 	"NfmOPL Nuked-OPL3 writeReg",
 };
@@ -133,8 +135,8 @@ namespace RealChip {
 // hardware opl
 OPL::OPL(Config::OplType type, NfmOPL::OplDevice deviceType) : _type(type), _deviceType(deviceType), _activeReg(0), _initialized(false), _useBuffer(NFM_ENABLE_BUFFERED_OUTPUT), _incapableDevice(false) {
 #if NFM_ENABLE_LOGS
-	atari_debug("NfmOPL::RealChip create");
-	atari_debug("Requesting hardware info");
+	debug("NfmOPL::RealChip create");
+	debug("Requesting hardware info");
 #endif
 	Supexec(updateHardwareInfo);
 
@@ -162,7 +164,7 @@ OPL::OPL(Config::OplType type, NfmOPL::OplDevice deviceType) : _type(type), _dev
 
 #if NFM_ENABLE_LOGS
 	if (deviceType < dtNumDevices) {
-		atari_debug(s_DebugConfigMsgStrs[deviceType]);
+		debug(s_DebugConfigMsgStrs[deviceType]);
 	}
 #endif
 
@@ -235,6 +237,17 @@ OPL::OPL(Config::OplType type, NfmOPL::OplDevice deviceType) : _type(type), _dev
 		_ifaceCfg.setup = CC_SINGLE;
 	}
 	break;
+	case dtStBusIsaVmeSb: {
+		// TODO: handle additional VME / ISA parameters if needed
+		_params.uParam.outputPort = OPT_ISA;
+		_params.uParam.param = 0;
+
+		_ifaceCfg.deviceType = eFmDriverType::FMD_ISA_SB;
+		_ifaceCfg.soundchip = CM_OPL3;
+		_ifaceCfg.operationMode = CO_OPL2,
+		_ifaceCfg.setup = CC_SINGLE;
+	}
+	break;
 	case dtNatfeatsOpl: {
 		_params.uParam.outputPort = OPT_INTERNAL;
 		_ifaceCfg.deviceType = eFmDriverType::FMD_NULL;
@@ -252,12 +265,12 @@ OPL::OPL(Config::OplType type, NfmOPL::OplDevice deviceType) : _type(type), _dev
 OPL::~OPL() {
 	if (_initialized == true) {
 #if NFM_ENABLE_LOGS
-		atari_debug("NfmOPL destroy");
+		debug("NfmOPL destroy");
 #endif
 		if (_useBuffer) {
 			// flush
 #if NFM_ENABLE_LOGS
-			atari_debug("OPL flush");
+			debug("OPL flush");
 #endif
 			_oplFlush();
 		}
@@ -285,11 +298,11 @@ OPL::~OPL() {
 
 bool OPL::init() {
 #if NFM_ENABLE_LOGS
-	atari_debug("NfmOPL::RealChip init");
+	debug("NfmOPL::RealChip init");
 #endif
 	if (_incapableDevice) {
 #if NFM_ENABLE_LOGS
-		atari_debug("NfmOPL::RealChip OPL2 device cannot emulate requested dual OPL2 / OPL3!");
+	debug("NfmOPL::RealChip OPL2 device cannot emulate requested dual OPL2 / OPL3!");
 #endif
 		return false;
 	}
@@ -305,7 +318,7 @@ bool OPL::init() {
 
 		if (s_mNfmSpace == 0) {
 #if NFM_ENABLE_LOGS
-			atari_debug("NfmOPL::RealChip create_mspace failed!");
+			debug("NfmOPL::RealChip create_mspace failed!");
 #endif
 			return false;
 		}
@@ -320,7 +333,7 @@ bool OPL::init() {
 		setUserMemoryCallbacks(&s_MemCallbacks);
 	} else {
 #if NFM_ENABLE_LOGS
-		atari_debug("NfmOPL::RealChip Out of system memory!");
+		debug("NfmOPL::RealChip Out of system memory!");
 #endif
 		return false;
 	}
@@ -342,21 +355,21 @@ bool OPL::init() {
 			_initialized = true;
 
 #if NFM_ENABLE_LOGS
-			atari_debug("NfmOPL::RealChip init OK");
+			debug("NfmOPL::RealChip init OK");
 #endif
 			return true;
 		}
 	}
 
 #if NFM_ENABLE_LOGS
-	atari_debug("NfmOPL::RealChip init failed!");
+	debug("NfmOPL::RealChip init failed!");
 #endif
 	return false;
 }
 
 void OPL::reset() {
 #if NFM_ENABLE_LOGS
-	atari_debug("NfmOPL::RealChip reset");
+	debug("NfmOPL::RealChip reset");
 #endif
 	for (int16_t i = 0; i < 256; i ++) {
 		writeReg((int)i, 0);
@@ -393,7 +406,7 @@ void OPL::write(int portAddress, int value) {
 
 void OPL::writeReg(int reg, int value) {
 #if NFM_ENABLE_LOGS
-	atari_debug(s_DebugOplWriteStrs[_deviceType]);
+	debug(s_DebugOplWriteStrs[_deviceType]);
 #endif
 
 	if (_type == Config::kOpl3 || _type == Config::kDualOpl2) {
@@ -426,7 +439,7 @@ void OPL::onTimer() {
 		if (_initialized) {
 #if NFM_ENABLE_LOGS
 			// flush
-			atari_debug("NfmOPL::RealChip flush");
+			debug("NfmOPL::RealChip flush");
 #endif
 			_oplFlush();
 		}
@@ -443,8 +456,8 @@ OPL *create(Config::OplType type, OplDevice device) {
 namespace EmulatedChip {
 OPL::OPL(Config::OplType type, enum NfmOPL::OplDevice deviceType): _type(type), _rate(0), _deviceType(deviceType), _activeReg(0), _initialized(false), _useBuffer(NFM_ENABLE_BUFFERED_OUTPUT), _incapableDevice(false) {
 #if NFM_ENABLE_LOGS
-	atari_debug("NfmOPL::EmulatedChip create");
-	atari_debug("Requesting hardware info");
+	debug("NfmOPL::EmulatedChip create");
+	debug("Requesting hardware info");
 #endif
 	Supexec(updateHardwareInfo);
 
@@ -473,7 +486,7 @@ OPL::OPL(Config::OplType type, enum NfmOPL::OplDevice deviceType): _type(type),
 
 #if NFM_ENABLE_LOGS
 	if (deviceType < dtNumDevices) {
-		atari_debug(s_DebugConfigMsgStrs[deviceType]);
+		debug(s_DebugConfigMsgStrs[deviceType]);
 	}
 #endif
 	switch (deviceType) {
@@ -502,14 +515,14 @@ OPL::~OPL() {
 
 	if (_initialized == true) {
 #if NFM_ENABLE_LOGS
-		atari_debug("NfmOPL::EmulatedChip destroy");
+		debug("NfmOPL::EmulatedChip destroy");
 #endif
 		stop();
 
 		if (_useBuffer) {
 #if NFM_ENABLE_LOGS
 			// flush
-			atari_debug("NfmOPL::EmulatedChip OPL flush");
+			debug("NfmOPL::EmulatedChip OPL flush");
 #endif
 			_oplFlush();
 		}
@@ -539,11 +552,11 @@ OPL::~OPL() {
 
 bool OPL::init() {
 #if NFM_ENABLE_LOGS
-	atari_debug("NfmOPL::EmulatedChip init");
+	debug("NfmOPL::EmulatedChip init");
 #endif
 	if (_incapableDevice) {
 #if NFM_ENABLE_LOGS
-		atari_debug("NfmOPL::EmulatedChip device isn't soft synth type!");
+		debug("NfmOPL::EmulatedChip device isn't soft synth type!");
 #endif
 		return false;
 	}
@@ -559,7 +572,7 @@ bool OPL::init() {
 
 		if (s_mNfmSpace == 0) {
 #if NFM_ENABLE_LOGS
-			atari_debug("NfmOPL::EmulatedChip create_mspace failed!");
+			debug("NfmOPL::EmulatedChip create_mspace failed!");
 #endif
 			return false;
 		}
@@ -574,7 +587,7 @@ bool OPL::init() {
 		setUserMemoryCallbacks(&s_MemCallbacks);
 	} else {
 #if NFM_ENABLE_LOGS
-		atari_debug("NfmOPL::EmulatedChip Out of system memory!");
+		debug("NfmOPL::EmulatedChip Out of system memory!");
 #endif
 		return false;
 	}
@@ -600,14 +613,14 @@ bool OPL::init() {
 			_initialized = true;
 
 #if NFM_ENABLE_LOGS
-			atari_debug("NfmOPL::RealChip init OK");
+			debug("NfmOPL::RealChip init OK");
 #endif
 			return true;
 		}
 	}
 
 #if NFM_ENABLE_LOGS
-	atari_debug("NfmOPL::RealChip init failed!");
+	debug("NfmOPL::RealChip init failed!");
 #endif
 	return false;
 }
@@ -651,7 +664,7 @@ void OPL::write(int portAddress, int value) {
 
 void OPL::writeReg(int reg, int value) {
 #if NFM_ENABLE_LOGS
-	atari_debug(s_DebugOplWriteStrs[_deviceType]);
+	debug(s_DebugOplWriteStrs[_deviceType]);
 #endif
 
 	if (_type == Config::kOpl3 || _type == Config::kDualOpl2) {
diff --git a/audio/nfmopl.h b/audio/nfmopl.h
index 43f2cbd299e..73c383e3493 100644
--- a/audio/nfmopl.h
+++ b/audio/nfmopl.h
@@ -50,6 +50,7 @@ enum OplDevice : int16_t {
 	dtOPL3LPT,
 	dtOPL2AudioBoard,
 	dtOPL3Duo,
+	dtStBusIsaVmeSb,
 	dtNatfeatsOpl,
 	dtNukedOpl3,
 	dtNumDevices
diff --git a/backends/platform/atari/get-nfm.sh b/backends/platform/atari/get-nfm.sh
index 831a759d8d8..c4880a9bf3f 100644
--- a/backends/platform/atari/get-nfm.sh
+++ b/backends/platform/atari/get-nfm.sh
@@ -4,9 +4,10 @@
 # -x: Display expanded script commands
 
 # downloads and unpacks in ./deps folder precompiled nFM library release for m68k Atari TOS / mintelf gcc stdlib
-NFM_VERSION=0.2.1
+NFM_VERSION=0.2.2
+COMMIT_SHORT_SHA=4d077f4b
 
-curl -o ./backends/platform/atari/nfm.zip https://nokturnal.pl/downloads/atari/nokturnfm/nfmplay/builds/releases/0.2.1/ataritos-mintelf-gcc-stdlib-m68k-0.2.1-5c688e1a.zip
+curl -o ./backends/platform/atari/nfm.zip https://nokturnal.pl/downloads/atari/nokturnfm/nfmplay/builds/releases/${NFM_VERSION}/ataritos-mintelf-gcc-stdlib-m68k-${NFM_VERSION}-${COMMIT_SHORT_SHA}.zip
 rm -rf ./backends/platform/atari/deps/nfm
 
 unzip ./backends/platform/atari/nfm.zip "nFM_${NFM_VERSION}/*" -d ./backends/platform/atari/deps/
diff --git a/configure b/configure
index 3a98319b250..13090903c20 100755
--- a/configure
+++ b/configure
@@ -5646,12 +5646,12 @@ int main(void)
   return -1;
 }
 EOF
-	cc_check $NFM_CFLAGS -DSC_ENABLE_MALLOC=0 -DNFM_OPL_SHADOW_REG_WRITE_ENABLE=1 $NFM_LIBS -lnfmcore -lnfmutil -ldrvcommon -ldrvnokturnfm -ldrvopl3Duo -ldrvopl3express -ldrvoplxlpt -ldrvoplnull -lsyscore -lcontainers -lallocators &&\
+	cc_check $NFM_CFLAGS -DSC_ENABLE_MALLOC=0 -DNFM_OPL_SHADOW_REG_WRITE_ENABLE=1 $NFM_LIBS -lnfmcore -lnfmutil -ldrvcommon -ldrvnokturnfm -ldrvopl3Duo -ldrvopl3express -ldrvoplxlpt -ldrvopllcart -ldrvisasb -ldrvoplnull -lsyscore -lcontainers -lallocators &&\
 	_nfm=yes
 fi
 if test "$_nfm" = yes; then
 	append_var DEFINES "-DSC_ENABLE_MALLOC=0 -DNFM_OPL_SHADOW_REG_WRITE_ENABLE=1"
-	append_var LIBS "$NFM_LIBS -lnfmcore -lnfmutil -ldrvcommon -ldrvnokturnfm -ldrvopl3Duo -ldrvopl3express -ldrvoplxlpt -ldrvoplnull -lsyscore -lcontainers -lallocators"
+	append_var LIBS "$NFM_LIBS -lnfmcore -lnfmutil -ldrvcommon -ldrvnokturnfm -ldrvopl3Duo -ldrvopl3express -ldrvoplxlpt -ldrvopllcart -ldrvisasb -ldrvoplnull -lsyscore -lcontainers -lallocators"
 	append_var INCLUDES "$NFM_CFLAGS"
 fi
 define_in_config_if_yes "$_nfm" 'USE_NFM'


Commit: d541e51e28d01533e6be30f486449836612eae8b
    https://github.com/scummvm/scummvm/commit/d541e51e28d01533e6be30f486449836612eae8b
Author: Pawe?? G??ralski (20125592+n0kturnal at users.noreply.github.com)
Date: 2026-06-21T15:19:56+02:00

Commit Message:
AUDIO : Atari TOS hardware / software OPL2 / 3 (YM262 / YM3812)

Include debug logging header only when needed

Changed paths:
    audio/nfmopl.cpp


diff --git a/audio/nfmopl.cpp b/audio/nfmopl.cpp
index fb1eec66d7a..018c778dafa 100644
--- a/audio/nfmopl.cpp
+++ b/audio/nfmopl.cpp
@@ -27,7 +27,6 @@
 
 #define FORBIDDEN_SYMBOL_ALLOW_ALL
 #include "common/scummsys.h"
-#include "common/debug.h"
 
 #include "audio/mixer.h"
 #include "audio/fmopl.h"
@@ -41,6 +40,7 @@ extern "C"
 }
 
 #ifndef RELEASE_BUILD
+	#include "common/debug.h"
 	#define NFM_ENABLE_LOGS 1
 #endif
 



From noreply at scummvm.org  Sun Jun 21 13:20:09 2026
From: noreply at scummvm.org (ScummVM-Translations)
Date: Sun, 21 Jun 2026 13:20:09 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 3ad8991828dfb2cb03baa6863f3c10e8cf499351
Message-ID: <20260621132009.90DFE7005D1@ron.scummvm.net>

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

Summary:
3ad8991828 I18N: Update translations templates


Commit: 3ad8991828dfb2cb03baa6863f3c10e8cf499351
    https://github.com/scummvm/scummvm/commit/3ad8991828dfb2cb03baa6863f3c10e8cf499351
Author: Weblate (noreply at weblate.org)
Date: 2026-06-21T13:20:04Z

Commit Message:
I18N: Update translations templates

Changed paths:
    po/scummvm.pot


diff --git a/po/scummvm.pot b/po/scummvm.pot
index f96c09ece2f..0cde593ee81 100644
--- a/po/scummvm.pot
+++ b/po/scummvm.pot
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2026.3.1git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-20 16:13+0000\n"
+"POT-Creation-Date: 2026-06-21 13:20+0000\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL at ADDRESS>\n"
 "Language-Team: LANGUAGE <LL at li.org>\n"
@@ -2270,7 +2270,7 @@ msgid "Debug level:"
 msgstr ""
 
 #. I18N: Debug level -1, no messages
-#: gui/options.cpp:2758 gui/options.cpp:2895 audio/fmopl.cpp:83
+#: gui/options.cpp:2758 gui/options.cpp:2895 audio/fmopl.cpp:112
 msgid "None"
 msgstr ""
 
@@ -3747,34 +3747,74 @@ msgstr ""
 msgid "AdLib emulator"
 msgstr ""
 
-#: audio/fmopl.cpp:85
+#: audio/fmopl.cpp:114
 msgid "MAME OPL emulator"
 msgstr ""
 
-#: audio/fmopl.cpp:88
+#: audio/fmopl.cpp:117
 msgid "DOSBox OPL emulator"
 msgstr ""
 
-#: audio/fmopl.cpp:91
+#: audio/fmopl.cpp:120
 msgid "Nuked OPL emulator"
 msgstr ""
 
-#: audio/fmopl.cpp:94
+#: audio/fmopl.cpp:123
 msgid "ALSA Direct FM"
 msgstr ""
 
-#: audio/fmopl.cpp:97
+#: audio/fmopl.cpp:126
 msgid "OPL2LPT"
 msgstr ""
 
-#: audio/fmopl.cpp:98
+#: audio/fmopl.cpp:127
 msgid "OPL3LPT"
 msgstr ""
 
-#: audio/fmopl.cpp:101
+#: audio/fmopl.cpp:130
 msgid "RetroWave OPL3"
 msgstr ""
 
+#: audio/fmopl.cpp:133
+msgid "[nFM] NokturnFM2 (OPL2)"
+msgstr ""
+
+#: audio/fmopl.cpp:134
+msgid "[nFM] NokturnFM3 (OPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:135
+msgid "[nFM] RetroWave USB OPL3 Express (OPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:136
+msgid "[nFM] Serdaco OPL2LPT (OPL2)"
+msgstr ""
+
+#: audio/fmopl.cpp:137
+msgid "[nFM] Serdaco OPL3LPT (OPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:138
+msgid "[nFM] Cheerful Electronics OPL2 Audio Board (OPL2)"
+msgstr ""
+
+#: audio/fmopl.cpp:139
+msgid "[nFM] Cheerful Electronics OPL3 Duo! (2xOPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:140
+msgid "[nFM] ST Bus ISA / VME SoundBlaster"
+msgstr ""
+
+#: audio/fmopl.cpp:141
+msgid "[nFM] NatFeats / NULL"
+msgstr ""
+
+#: audio/fmopl.cpp:142
+msgid "[nFM] Nuked-OPL3 softsynth (OPL3)"
+msgstr ""
+
 #: audio/mididrv.cpp:232
 #, c-format
 msgid ""



From noreply at scummvm.org  Sun Jun 21 13:20:51 2026
From: noreply at scummvm.org (ScummVM-Translations)
Date: Sun, 21 Jun 2026 13:20:51 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 b10705b32b401b76761c956ee4aad2c243fc5747
Message-ID: <20260621132051.DC2E47005CE@ron.scummvm.net>

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

Summary:
b10705b32b I18N: Update translation files


Commit: b10705b32b401b76761c956ee4aad2c243fc5747
    https://github.com/scummvm/scummvm/commit/b10705b32b401b76761c956ee4aad2c243fc5747
Author: Weblate (noreply at weblate.org)
Date: 2026-06-21T13:20:28Z

Commit Message:
I18N: Update translation files

Updated by "Update PO files to match POT (msgmerge)" hook in Weblate.

Translation: ScummVM/scummvm
Translate-URL: https://translations.scummvm.org/projects/scummvm/scummvm/

Changed paths:
    po/ar.po
    po/be-tarask.po
    po/be_BY.po
    po/ca_ES.po
    po/cs_CZ.po
    po/da.po
    po/de_DE.po
    po/el.po
    po/es_ES.po
    po/eu.po
    po/fi_FI.po
    po/fr_FR.po
    po/gl_ES.po
    po/he.po
    po/hi.po
    po/hu_HU.po
    po/it_IT.po
    po/ja.po
    po/ka.po
    po/ko.po
    po/nb_NO.po
    po/nl_NL.po
    po/nn_NO.po
    po/pl_PL.po
    po/pt_BR.po
    po/pt_PT.po
    po/ro.po
    po/ru_RU.po
    po/sv_SE.po
    po/tr.po
    po/uk_UA.po
    po/zh.po
    po/zh_Hans.po
    po/zh_Hant.po


diff --git a/po/ar.po b/po/ar.po
index b611cfcf5c7..d5dc71e2e23 100644
--- a/po/ar.po
+++ b/po/ar.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-20 16:13+0000\n"
+"POT-Creation-Date: 2026-06-21 13:20+0000\n"
 "PO-Revision-Date: 2026-02-24 22:26+0000\n"
 "Last-Translator: Mohamed Shaaban <mohmedbashamedo at hotmail.com>\n"
 "Language-Team: Arabic <https://translations.scummvm.org/projects/scummvm/"
@@ -2382,7 +2382,7 @@ msgid "Debug level:"
 msgstr "?????????? ??????????????:"
 
 #. I18N: Debug level -1, no messages
-#: gui/options.cpp:2758 gui/options.cpp:2895 audio/fmopl.cpp:83
+#: gui/options.cpp:2758 gui/options.cpp:2895 audio/fmopl.cpp:112
 msgid "None"
 msgstr "???? ????"
 
@@ -4082,34 +4082,74 @@ msgstr "???????????? OpenGL ???? ??????????"
 msgid "AdLib emulator"
 msgstr "?????????? AdLib"
 
-#: audio/fmopl.cpp:85
+#: audio/fmopl.cpp:114
 msgid "MAME OPL emulator"
 msgstr "?????????? MAME OPL"
 
-#: audio/fmopl.cpp:88
+#: audio/fmopl.cpp:117
 msgid "DOSBox OPL emulator"
 msgstr "?????????? DOSBox OPL"
 
-#: audio/fmopl.cpp:91
+#: audio/fmopl.cpp:120
 msgid "Nuked OPL emulator"
 msgstr "?????????? Nuked OPL"
 
-#: audio/fmopl.cpp:94
+#: audio/fmopl.cpp:123
 msgid "ALSA Direct FM"
 msgstr "ALSA Direct FM"
 
-#: audio/fmopl.cpp:97
+#: audio/fmopl.cpp:126
 msgid "OPL2LPT"
 msgstr "OPL2LPT"
 
-#: audio/fmopl.cpp:98
+#: audio/fmopl.cpp:127
 msgid "OPL3LPT"
 msgstr "OPL3LPT"
 
-#: audio/fmopl.cpp:101
+#: audio/fmopl.cpp:130
 msgid "RetroWave OPL3"
 msgstr "RetroWave OPL3"
 
+#: audio/fmopl.cpp:133
+msgid "[nFM] NokturnFM2 (OPL2)"
+msgstr ""
+
+#: audio/fmopl.cpp:134
+msgid "[nFM] NokturnFM3 (OPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:135
+msgid "[nFM] RetroWave USB OPL3 Express (OPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:136
+msgid "[nFM] Serdaco OPL2LPT (OPL2)"
+msgstr ""
+
+#: audio/fmopl.cpp:137
+msgid "[nFM] Serdaco OPL3LPT (OPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:138
+msgid "[nFM] Cheerful Electronics OPL2 Audio Board (OPL2)"
+msgstr ""
+
+#: audio/fmopl.cpp:139
+msgid "[nFM] Cheerful Electronics OPL3 Duo! (2xOPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:140
+msgid "[nFM] ST Bus ISA / VME SoundBlaster"
+msgstr ""
+
+#: audio/fmopl.cpp:141
+msgid "[nFM] NatFeats / NULL"
+msgstr ""
+
+#: audio/fmopl.cpp:142
+msgid "[nFM] Nuked-OPL3 softsynth (OPL3)"
+msgstr ""
+
 #: audio/mididrv.cpp:232
 #, c-format
 msgid ""
diff --git a/po/be-tarask.po b/po/be-tarask.po
index 96b4c8373af..d6956cd6c48 100644
--- a/po/be-tarask.po
+++ b/po/be-tarask.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-20 16:13+0000\n"
+"POT-Creation-Date: 2026-06-21 13:20+0000\n"
 "PO-Revision-Date: 2023-02-26 15:01+0000\n"
 "Last-Translator: Kastu?? K. <oakum2334 at gmail.com>\n"
 "Language-Team: Belarusian (Tara??kievica) <https://translations.scummvm.org/"
@@ -2405,7 +2405,7 @@ msgid "Debug level:"
 msgstr "???????? ???????????????????????? ??????????"
 
 #. I18N: Debug level -1, no messages
-#: gui/options.cpp:2758 gui/options.cpp:2895 audio/fmopl.cpp:83
+#: gui/options.cpp:2758 gui/options.cpp:2895 audio/fmopl.cpp:112
 msgid "None"
 msgstr "????????"
 
@@ -4040,34 +4040,74 @@ msgstr "OpenGL ?? ??????????????????"
 msgid "AdLib emulator"
 msgstr "???????????????? AdLib"
 
-#: audio/fmopl.cpp:85
+#: audio/fmopl.cpp:114
 msgid "MAME OPL emulator"
 msgstr "???????????????? MAME OPL"
 
-#: audio/fmopl.cpp:88
+#: audio/fmopl.cpp:117
 msgid "DOSBox OPL emulator"
 msgstr "???????????????? DOSBox OPL"
 
-#: audio/fmopl.cpp:91
+#: audio/fmopl.cpp:120
 msgid "Nuked OPL emulator"
 msgstr "???????????????? Nuked OPL"
 
-#: audio/fmopl.cpp:94
+#: audio/fmopl.cpp:123
 msgid "ALSA Direct FM"
 msgstr "ALSA ???????????? FM"
 
-#: audio/fmopl.cpp:97
+#: audio/fmopl.cpp:126
 msgid "OPL2LPT"
 msgstr "OPL2LPT"
 
-#: audio/fmopl.cpp:98
+#: audio/fmopl.cpp:127
 msgid "OPL3LPT"
 msgstr "OPL3LPT"
 
-#: audio/fmopl.cpp:101
+#: audio/fmopl.cpp:130
 msgid "RetroWave OPL3"
 msgstr "RetroWave OPL3"
 
+#: audio/fmopl.cpp:133
+msgid "[nFM] NokturnFM2 (OPL2)"
+msgstr ""
+
+#: audio/fmopl.cpp:134
+msgid "[nFM] NokturnFM3 (OPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:135
+msgid "[nFM] RetroWave USB OPL3 Express (OPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:136
+msgid "[nFM] Serdaco OPL2LPT (OPL2)"
+msgstr ""
+
+#: audio/fmopl.cpp:137
+msgid "[nFM] Serdaco OPL3LPT (OPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:138
+msgid "[nFM] Cheerful Electronics OPL2 Audio Board (OPL2)"
+msgstr ""
+
+#: audio/fmopl.cpp:139
+msgid "[nFM] Cheerful Electronics OPL3 Duo! (2xOPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:140
+msgid "[nFM] ST Bus ISA / VME SoundBlaster"
+msgstr ""
+
+#: audio/fmopl.cpp:141
+msgid "[nFM] NatFeats / NULL"
+msgstr ""
+
+#: audio/fmopl.cpp:142
+msgid "[nFM] Nuked-OPL3 softsynth (OPL3)"
+msgstr ""
+
 #: audio/mididrv.cpp:232
 #, c-format
 msgid ""
diff --git a/po/be_BY.po b/po/be_BY.po
index 315cd0cf7b3..859ad26525a 100644
--- a/po/be_BY.po
+++ b/po/be_BY.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-20 16:13+0000\n"
+"POT-Creation-Date: 2026-06-21 13:20+0000\n"
 "PO-Revision-Date: 2021-03-21 08:24+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Belarusian <https://translations.scummvm.org/projects/scummvm/"
@@ -2412,7 +2412,7 @@ msgid "Debug level:"
 msgstr "?????????????????? ????????"
 
 #. I18N: Debug level -1, no messages
-#: gui/options.cpp:2758 gui/options.cpp:2895 audio/fmopl.cpp:83
+#: gui/options.cpp:2758 gui/options.cpp:2895 audio/fmopl.cpp:112
 msgid "None"
 msgstr "????????"
 
@@ -4043,34 +4043,74 @@ msgstr ""
 msgid "AdLib emulator"
 msgstr "???????????????? AdLib"
 
-#: audio/fmopl.cpp:85
+#: audio/fmopl.cpp:114
 msgid "MAME OPL emulator"
 msgstr "???????????????? MAME OPL"
 
-#: audio/fmopl.cpp:88
+#: audio/fmopl.cpp:117
 msgid "DOSBox OPL emulator"
 msgstr "???????????????? DOSBox OPL"
 
-#: audio/fmopl.cpp:91
+#: audio/fmopl.cpp:120
 msgid "Nuked OPL emulator"
 msgstr "???????????????? Nuked OPL"
 
-#: audio/fmopl.cpp:94
+#: audio/fmopl.cpp:123
 msgid "ALSA Direct FM"
 msgstr "?????????? FM ALSA"
 
-#: audio/fmopl.cpp:97
+#: audio/fmopl.cpp:126
 msgid "OPL2LPT"
 msgstr "OPL2LPT"
 
-#: audio/fmopl.cpp:98
+#: audio/fmopl.cpp:127
 msgid "OPL3LPT"
 msgstr "OPL3LPT"
 
-#: audio/fmopl.cpp:101
+#: audio/fmopl.cpp:130
 msgid "RetroWave OPL3"
 msgstr ""
 
+#: audio/fmopl.cpp:133
+msgid "[nFM] NokturnFM2 (OPL2)"
+msgstr ""
+
+#: audio/fmopl.cpp:134
+msgid "[nFM] NokturnFM3 (OPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:135
+msgid "[nFM] RetroWave USB OPL3 Express (OPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:136
+msgid "[nFM] Serdaco OPL2LPT (OPL2)"
+msgstr ""
+
+#: audio/fmopl.cpp:137
+msgid "[nFM] Serdaco OPL3LPT (OPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:138
+msgid "[nFM] Cheerful Electronics OPL2 Audio Board (OPL2)"
+msgstr ""
+
+#: audio/fmopl.cpp:139
+msgid "[nFM] Cheerful Electronics OPL3 Duo! (2xOPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:140
+msgid "[nFM] ST Bus ISA / VME SoundBlaster"
+msgstr ""
+
+#: audio/fmopl.cpp:141
+msgid "[nFM] NatFeats / NULL"
+msgstr ""
+
+#: audio/fmopl.cpp:142
+msgid "[nFM] Nuked-OPL3 softsynth (OPL3)"
+msgstr ""
+
 #: audio/mididrv.cpp:232
 #, c-format
 msgid ""
diff --git a/po/ca_ES.po b/po/ca_ES.po
index f8d43f4e3ea..7d60fada160 100644
--- a/po/ca_ES.po
+++ b/po/ca_ES.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.6.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-20 16:13+0000\n"
+"POT-Creation-Date: 2026-06-21 13:20+0000\n"
 "PO-Revision-Date: 2026-05-26 04:22+0000\n"
 "Last-Translator: Xaviu <xaviu at xaviu.com>\n"
 "Language-Team: Catalan <https://translations.scummvm.org/projects/scummvm/"
@@ -2358,7 +2358,7 @@ msgid "Debug level:"
 msgstr "Nivell de depuraci??:"
 
 #. I18N: Debug level -1, no messages
-#: gui/options.cpp:2758 gui/options.cpp:2895 audio/fmopl.cpp:83
+#: gui/options.cpp:2758 gui/options.cpp:2895 audio/fmopl.cpp:112
 msgid "None"
 msgstr "Cap"
 
@@ -3962,34 +3962,74 @@ msgstr "OpenGL amb shaders"
 msgid "AdLib emulator"
 msgstr "Emulador AdLib"
 
-#: audio/fmopl.cpp:85
+#: audio/fmopl.cpp:114
 msgid "MAME OPL emulator"
 msgstr "Emulador OPL de MAME"
 
-#: audio/fmopl.cpp:88
+#: audio/fmopl.cpp:117
 msgid "DOSBox OPL emulator"
 msgstr "Emulador OPL de DOSBox"
 
-#: audio/fmopl.cpp:91
+#: audio/fmopl.cpp:120
 msgid "Nuked OPL emulator"
 msgstr "Emulador OPL de Nuked"
 
-#: audio/fmopl.cpp:94
+#: audio/fmopl.cpp:123
 msgid "ALSA Direct FM"
 msgstr "ALSA Direct FM"
 
-#: audio/fmopl.cpp:97
+#: audio/fmopl.cpp:126
 msgid "OPL2LPT"
 msgstr "OPL2LPT"
 
-#: audio/fmopl.cpp:98
+#: audio/fmopl.cpp:127
 msgid "OPL3LPT"
 msgstr "OPL3LPT"
 
-#: audio/fmopl.cpp:101
+#: audio/fmopl.cpp:130
 msgid "RetroWave OPL3"
 msgstr "RetroWave OPL3"
 
+#: audio/fmopl.cpp:133
+msgid "[nFM] NokturnFM2 (OPL2)"
+msgstr ""
+
+#: audio/fmopl.cpp:134
+msgid "[nFM] NokturnFM3 (OPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:135
+msgid "[nFM] RetroWave USB OPL3 Express (OPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:136
+msgid "[nFM] Serdaco OPL2LPT (OPL2)"
+msgstr ""
+
+#: audio/fmopl.cpp:137
+msgid "[nFM] Serdaco OPL3LPT (OPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:138
+msgid "[nFM] Cheerful Electronics OPL2 Audio Board (OPL2)"
+msgstr ""
+
+#: audio/fmopl.cpp:139
+msgid "[nFM] Cheerful Electronics OPL3 Duo! (2xOPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:140
+msgid "[nFM] ST Bus ISA / VME SoundBlaster"
+msgstr ""
+
+#: audio/fmopl.cpp:141
+msgid "[nFM] NatFeats / NULL"
+msgstr ""
+
+#: audio/fmopl.cpp:142
+msgid "[nFM] Nuked-OPL3 softsynth (OPL3)"
+msgstr ""
+
 #: audio/mididrv.cpp:232
 #, c-format
 msgid ""
diff --git a/po/cs_CZ.po b/po/cs_CZ.po
index 3e112ad825b..d9e24192de1 100644
--- a/po/cs_CZ.po
+++ b/po/cs_CZ.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.7.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-20 16:13+0000\n"
+"POT-Creation-Date: 2026-06-21 13:20+0000\n"
 "PO-Revision-Date: 2026-02-09 20:22+0000\n"
 "Last-Translator: AsciiWolf <mail at asciiwolf.com>\n"
 "Language-Team: Czech <https://translations.scummvm.org/projects/scummvm/"
@@ -2409,7 +2409,7 @@ msgid "Debug level:"
 msgstr "Lad??c?? program"
 
 #. I18N: Debug level -1, no messages
-#: gui/options.cpp:2758 gui/options.cpp:2895 audio/fmopl.cpp:83
+#: gui/options.cpp:2758 gui/options.cpp:2895 audio/fmopl.cpp:112
 msgid "None"
 msgstr "Pr??zdn??"
 
@@ -4031,34 +4031,74 @@ msgstr ""
 msgid "AdLib emulator"
 msgstr "AdLib emul??tor"
 
-#: audio/fmopl.cpp:85
+#: audio/fmopl.cpp:114
 msgid "MAME OPL emulator"
 msgstr "MAME OPL Emul??tor"
 
-#: audio/fmopl.cpp:88
+#: audio/fmopl.cpp:117
 msgid "DOSBox OPL emulator"
 msgstr "DOSBox OPL Emul??tor"
 
-#: audio/fmopl.cpp:91
+#: audio/fmopl.cpp:120
 msgid "Nuked OPL emulator"
 msgstr "Nuked OPL Emul??tor"
 
-#: audio/fmopl.cpp:94
+#: audio/fmopl.cpp:123
 msgid "ALSA Direct FM"
 msgstr "ALSA P????m?? FM"
 
-#: audio/fmopl.cpp:97
+#: audio/fmopl.cpp:126
 msgid "OPL2LPT"
 msgstr "OPL2LPT"
 
-#: audio/fmopl.cpp:98
+#: audio/fmopl.cpp:127
 msgid "OPL3LPT"
 msgstr "OPL3LPT"
 
-#: audio/fmopl.cpp:101
+#: audio/fmopl.cpp:130
 msgid "RetroWave OPL3"
 msgstr ""
 
+#: audio/fmopl.cpp:133
+msgid "[nFM] NokturnFM2 (OPL2)"
+msgstr ""
+
+#: audio/fmopl.cpp:134
+msgid "[nFM] NokturnFM3 (OPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:135
+msgid "[nFM] RetroWave USB OPL3 Express (OPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:136
+msgid "[nFM] Serdaco OPL2LPT (OPL2)"
+msgstr ""
+
+#: audio/fmopl.cpp:137
+msgid "[nFM] Serdaco OPL3LPT (OPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:138
+msgid "[nFM] Cheerful Electronics OPL2 Audio Board (OPL2)"
+msgstr ""
+
+#: audio/fmopl.cpp:139
+msgid "[nFM] Cheerful Electronics OPL3 Duo! (2xOPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:140
+msgid "[nFM] ST Bus ISA / VME SoundBlaster"
+msgstr ""
+
+#: audio/fmopl.cpp:141
+msgid "[nFM] NatFeats / NULL"
+msgstr ""
+
+#: audio/fmopl.cpp:142
+msgid "[nFM] Nuked-OPL3 softsynth (OPL3)"
+msgstr ""
+
 #: audio/mididrv.cpp:232
 #, c-format
 msgid ""
diff --git a/po/da.po b/po/da.po
index a679546e091..b2572004e91 100644
--- a/po/da.po
+++ b/po/da.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-20 16:13+0000\n"
+"POT-Creation-Date: 2026-06-21 13:20+0000\n"
 "PO-Revision-Date: 2023-09-07 17:04+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Danish <https://translations.scummvm.org/projects/scummvm/"
@@ -2412,7 +2412,7 @@ msgid "Debug level:"
 msgstr "Fejls??ger"
 
 #. I18N: Debug level -1, no messages
-#: gui/options.cpp:2758 gui/options.cpp:2895 audio/fmopl.cpp:83
+#: gui/options.cpp:2758 gui/options.cpp:2895 audio/fmopl.cpp:112
 #, fuzzy
 msgid "None"
 msgstr "Ingen"
@@ -4042,34 +4042,74 @@ msgstr ""
 msgid "AdLib emulator"
 msgstr "AdLib-emulator"
 
-#: audio/fmopl.cpp:85
+#: audio/fmopl.cpp:114
 msgid "MAME OPL emulator"
 msgstr "MAME OPL-emulator"
 
-#: audio/fmopl.cpp:88
+#: audio/fmopl.cpp:117
 msgid "DOSBox OPL emulator"
 msgstr "DOSBox OPL-emulator"
 
-#: audio/fmopl.cpp:91
+#: audio/fmopl.cpp:120
 msgid "Nuked OPL emulator"
 msgstr "Nuked OPL-emulator"
 
-#: audio/fmopl.cpp:94
+#: audio/fmopl.cpp:123
 msgid "ALSA Direct FM"
 msgstr "ALSA Direct FM"
 
-#: audio/fmopl.cpp:97
+#: audio/fmopl.cpp:126
 msgid "OPL2LPT"
 msgstr "OPL2LPT"
 
-#: audio/fmopl.cpp:98
+#: audio/fmopl.cpp:127
 msgid "OPL3LPT"
 msgstr "OPL3LPT"
 
-#: audio/fmopl.cpp:101
+#: audio/fmopl.cpp:130
 msgid "RetroWave OPL3"
 msgstr ""
 
+#: audio/fmopl.cpp:133
+msgid "[nFM] NokturnFM2 (OPL2)"
+msgstr ""
+
+#: audio/fmopl.cpp:134
+msgid "[nFM] NokturnFM3 (OPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:135
+msgid "[nFM] RetroWave USB OPL3 Express (OPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:136
+msgid "[nFM] Serdaco OPL2LPT (OPL2)"
+msgstr ""
+
+#: audio/fmopl.cpp:137
+msgid "[nFM] Serdaco OPL3LPT (OPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:138
+msgid "[nFM] Cheerful Electronics OPL2 Audio Board (OPL2)"
+msgstr ""
+
+#: audio/fmopl.cpp:139
+msgid "[nFM] Cheerful Electronics OPL3 Duo! (2xOPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:140
+msgid "[nFM] ST Bus ISA / VME SoundBlaster"
+msgstr ""
+
+#: audio/fmopl.cpp:141
+msgid "[nFM] NatFeats / NULL"
+msgstr ""
+
+#: audio/fmopl.cpp:142
+msgid "[nFM] Nuked-OPL3 softsynth (OPL3)"
+msgstr ""
+
 #: audio/mididrv.cpp:232
 #, c-format
 msgid ""
diff --git a/po/de_DE.po b/po/de_DE.po
index 2c9295f6733..4fe379aaf23 100644
--- a/po/de_DE.po
+++ b/po/de_DE.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.10.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-20 16:13+0000\n"
+"POT-Creation-Date: 2026-06-21 13:20+0000\n"
 "PO-Revision-Date: 2026-05-05 16:31+0000\n"
 "Last-Translator: Lothar Serra Mari <lothar.serramari at scummvm.org>\n"
 "Language-Team: German <https://translations.scummvm.org/projects/scummvm/"
@@ -2463,7 +2463,7 @@ msgid "Debug level:"
 msgstr "Debug-Level:"
 
 #. I18N: Debug level -1, no messages
-#: gui/options.cpp:2758 gui/options.cpp:2895 audio/fmopl.cpp:83
+#: gui/options.cpp:2758 gui/options.cpp:2895 audio/fmopl.cpp:112
 msgid "None"
 msgstr "Nichts"
 
@@ -4095,34 +4095,74 @@ msgstr "OpenGL mit Shader"
 msgid "AdLib emulator"
 msgstr "AdLib-Emulator"
 
-#: audio/fmopl.cpp:85
+#: audio/fmopl.cpp:114
 msgid "MAME OPL emulator"
 msgstr "MAME OPL-Emulator"
 
-#: audio/fmopl.cpp:88
+#: audio/fmopl.cpp:117
 msgid "DOSBox OPL emulator"
 msgstr "DOSBox OPL-Emulator"
 
-#: audio/fmopl.cpp:91
+#: audio/fmopl.cpp:120
 msgid "Nuked OPL emulator"
 msgstr "Nuked OPL-Emulator"
 
-#: audio/fmopl.cpp:94
+#: audio/fmopl.cpp:123
 msgid "ALSA Direct FM"
 msgstr "ALSA Direct FM"
 
-#: audio/fmopl.cpp:97
+#: audio/fmopl.cpp:126
 msgid "OPL2LPT"
 msgstr "OPL2LPT"
 
-#: audio/fmopl.cpp:98
+#: audio/fmopl.cpp:127
 msgid "OPL3LPT"
 msgstr "OPL3LPT"
 
-#: audio/fmopl.cpp:101
+#: audio/fmopl.cpp:130
 msgid "RetroWave OPL3"
 msgstr "RetroWave OPL3"
 
+#: audio/fmopl.cpp:133
+msgid "[nFM] NokturnFM2 (OPL2)"
+msgstr ""
+
+#: audio/fmopl.cpp:134
+msgid "[nFM] NokturnFM3 (OPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:135
+msgid "[nFM] RetroWave USB OPL3 Express (OPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:136
+msgid "[nFM] Serdaco OPL2LPT (OPL2)"
+msgstr ""
+
+#: audio/fmopl.cpp:137
+msgid "[nFM] Serdaco OPL3LPT (OPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:138
+msgid "[nFM] Cheerful Electronics OPL2 Audio Board (OPL2)"
+msgstr ""
+
+#: audio/fmopl.cpp:139
+msgid "[nFM] Cheerful Electronics OPL3 Duo! (2xOPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:140
+msgid "[nFM] ST Bus ISA / VME SoundBlaster"
+msgstr ""
+
+#: audio/fmopl.cpp:141
+msgid "[nFM] NatFeats / NULL"
+msgstr ""
+
+#: audio/fmopl.cpp:142
+msgid "[nFM] Nuked-OPL3 softsynth (OPL3)"
+msgstr ""
+
 #: audio/mididrv.cpp:232
 #, c-format
 msgid ""
diff --git a/po/el.po b/po/el.po
index a5bc1ee5557..9c19b6070f1 100644
--- a/po/el.po
+++ b/po/el.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.10.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-20 16:13+0000\n"
+"POT-Creation-Date: 2026-06-21 13:20+0000\n"
 "PO-Revision-Date: 2026-04-23 00:22+0000\n"
 "Last-Translator: Antoniou Athanasios <a.antoniou79 at gmail.com>\n"
 "Language-Team: Greek <https://translations.scummvm.org/projects/scummvm/"
@@ -2486,7 +2486,7 @@ msgid "Debug level:"
 msgstr "?????????????? ????????????????????????????:"
 
 #. I18N: Debug level -1, no messages
-#: gui/options.cpp:2758 gui/options.cpp:2895 audio/fmopl.cpp:83
+#: gui/options.cpp:2758 gui/options.cpp:2895 audio/fmopl.cpp:112
 msgid "None"
 msgstr "?????? ???????? ??????????????????????????"
 
@@ -4166,34 +4166,74 @@ msgstr "OpenGL ???? ????????????????"
 msgid "AdLib emulator"
 msgstr "???????????????????? Adlib"
 
-#: audio/fmopl.cpp:85
+#: audio/fmopl.cpp:114
 msgid "MAME OPL emulator"
 msgstr "???????????????????? MAME OPL"
 
-#: audio/fmopl.cpp:88
+#: audio/fmopl.cpp:117
 msgid "DOSBox OPL emulator"
 msgstr "???????????????????? DOSBox OPL"
 
-#: audio/fmopl.cpp:91
+#: audio/fmopl.cpp:120
 msgid "Nuked OPL emulator"
 msgstr "???????????????????? Nuked OPL"
 
-#: audio/fmopl.cpp:94
+#: audio/fmopl.cpp:123
 msgid "ALSA Direct FM"
 msgstr "ALSA Direct FM"
 
-#: audio/fmopl.cpp:97
+#: audio/fmopl.cpp:126
 msgid "OPL2LPT"
 msgstr "OPL2LPT"
 
-#: audio/fmopl.cpp:98
+#: audio/fmopl.cpp:127
 msgid "OPL3LPT"
 msgstr "OPL3LPT"
 
-#: audio/fmopl.cpp:101
+#: audio/fmopl.cpp:130
 msgid "RetroWave OPL3"
 msgstr "RetroWave OPL3"
 
+#: audio/fmopl.cpp:133
+msgid "[nFM] NokturnFM2 (OPL2)"
+msgstr ""
+
+#: audio/fmopl.cpp:134
+msgid "[nFM] NokturnFM3 (OPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:135
+msgid "[nFM] RetroWave USB OPL3 Express (OPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:136
+msgid "[nFM] Serdaco OPL2LPT (OPL2)"
+msgstr ""
+
+#: audio/fmopl.cpp:137
+msgid "[nFM] Serdaco OPL3LPT (OPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:138
+msgid "[nFM] Cheerful Electronics OPL2 Audio Board (OPL2)"
+msgstr ""
+
+#: audio/fmopl.cpp:139
+msgid "[nFM] Cheerful Electronics OPL3 Duo! (2xOPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:140
+msgid "[nFM] ST Bus ISA / VME SoundBlaster"
+msgstr ""
+
+#: audio/fmopl.cpp:141
+msgid "[nFM] NatFeats / NULL"
+msgstr ""
+
+#: audio/fmopl.cpp:142
+msgid "[nFM] Nuked-OPL3 softsynth (OPL3)"
+msgstr ""
+
 #: audio/mididrv.cpp:232
 #, c-format
 msgid ""
diff --git a/po/es_ES.po b/po/es_ES.po
index d89d6ac1f07..94b40a96c78 100644
--- a/po/es_ES.po
+++ b/po/es_ES.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.4.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-20 16:13+0000\n"
+"POT-Creation-Date: 2026-06-21 13:20+0000\n"
 "PO-Revision-Date: 2026-06-20 15:53+0000\n"
 "Last-Translator: IlDucci <pajaroloco_2 at hotmail.com>\n"
 "Language-Team: Spanish <https://translations.scummvm.org/projects/scummvm/"
@@ -2445,7 +2445,7 @@ msgid "Debug level:"
 msgstr "Niv. de depuraci??n:"
 
 #. I18N: Debug level -1, no messages
-#: gui/options.cpp:2758 gui/options.cpp:2895 audio/fmopl.cpp:83
+#: gui/options.cpp:2758 gui/options.cpp:2895 audio/fmopl.cpp:112
 msgid "None"
 msgstr "Ninguno"
 
@@ -4090,34 +4090,74 @@ msgstr "OpenGL con shaders"
 msgid "AdLib emulator"
 msgstr "Emulaci??n AdLib"
 
-#: audio/fmopl.cpp:85
+#: audio/fmopl.cpp:114
 msgid "MAME OPL emulator"
 msgstr "Emulador OPL de MAME"
 
-#: audio/fmopl.cpp:88
+#: audio/fmopl.cpp:117
 msgid "DOSBox OPL emulator"
 msgstr "Emulador OPL de DOSBox"
 
-#: audio/fmopl.cpp:91
+#: audio/fmopl.cpp:120
 msgid "Nuked OPL emulator"
 msgstr "Emulador OPL de Nuked"
 
-#: audio/fmopl.cpp:94
+#: audio/fmopl.cpp:123
 msgid "ALSA Direct FM"
 msgstr "ALSA Direct FM"
 
-#: audio/fmopl.cpp:97
+#: audio/fmopl.cpp:126
 msgid "OPL2LPT"
 msgstr "OPL2LPT"
 
-#: audio/fmopl.cpp:98
+#: audio/fmopl.cpp:127
 msgid "OPL3LPT"
 msgstr "OPL3LPT"
 
-#: audio/fmopl.cpp:101
+#: audio/fmopl.cpp:130
 msgid "RetroWave OPL3"
 msgstr "RetroWave OPL3"
 
+#: audio/fmopl.cpp:133
+msgid "[nFM] NokturnFM2 (OPL2)"
+msgstr ""
+
+#: audio/fmopl.cpp:134
+msgid "[nFM] NokturnFM3 (OPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:135
+msgid "[nFM] RetroWave USB OPL3 Express (OPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:136
+msgid "[nFM] Serdaco OPL2LPT (OPL2)"
+msgstr ""
+
+#: audio/fmopl.cpp:137
+msgid "[nFM] Serdaco OPL3LPT (OPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:138
+msgid "[nFM] Cheerful Electronics OPL2 Audio Board (OPL2)"
+msgstr ""
+
+#: audio/fmopl.cpp:139
+msgid "[nFM] Cheerful Electronics OPL3 Duo! (2xOPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:140
+msgid "[nFM] ST Bus ISA / VME SoundBlaster"
+msgstr ""
+
+#: audio/fmopl.cpp:141
+msgid "[nFM] NatFeats / NULL"
+msgstr ""
+
+#: audio/fmopl.cpp:142
+msgid "[nFM] Nuked-OPL3 softsynth (OPL3)"
+msgstr ""
+
 #: audio/mididrv.cpp:232
 #, c-format
 msgid ""
diff --git a/po/eu.po b/po/eu.po
index 90cb9cbb81a..da3b6768a25 100644
--- a/po/eu.po
+++ b/po/eu.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.5.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-20 16:13+0000\n"
+"POT-Creation-Date: 2026-06-21 13:20+0000\n"
 "PO-Revision-Date: 2024-02-05 18:42+0000\n"
 "Last-Translator: Josu Igoa <josuigoa at ni.eus>\n"
 "Language-Team: Basque <https://translations.scummvm.org/projects/scummvm/"
@@ -2445,7 +2445,7 @@ msgid "Debug level:"
 msgstr "Araztaile maila:"
 
 #. I18N: Debug level -1, no messages
-#: gui/options.cpp:2758 gui/options.cpp:2895 audio/fmopl.cpp:83
+#: gui/options.cpp:2758 gui/options.cpp:2895 audio/fmopl.cpp:112
 msgid "None"
 msgstr "Bat ere ez"
 
@@ -4073,34 +4073,74 @@ msgstr ""
 msgid "AdLib emulator"
 msgstr "AdLib emuladorea:"
 
-#: audio/fmopl.cpp:85
+#: audio/fmopl.cpp:114
 msgid "MAME OPL emulator"
 msgstr "MAME OPL emuladorea"
 
-#: audio/fmopl.cpp:88
+#: audio/fmopl.cpp:117
 msgid "DOSBox OPL emulator"
 msgstr "DOSBox OPL emuladorea"
 
-#: audio/fmopl.cpp:91
+#: audio/fmopl.cpp:120
 msgid "Nuked OPL emulator"
 msgstr "Nuked OPL emuladorea"
 
-#: audio/fmopl.cpp:94
+#: audio/fmopl.cpp:123
 msgid "ALSA Direct FM"
 msgstr "ALSA FM zuzena"
 
-#: audio/fmopl.cpp:97
+#: audio/fmopl.cpp:126
 msgid "OPL2LPT"
 msgstr "OPL2LPT"
 
-#: audio/fmopl.cpp:98
+#: audio/fmopl.cpp:127
 msgid "OPL3LPT"
 msgstr ""
 
-#: audio/fmopl.cpp:101
+#: audio/fmopl.cpp:130
 msgid "RetroWave OPL3"
 msgstr ""
 
+#: audio/fmopl.cpp:133
+msgid "[nFM] NokturnFM2 (OPL2)"
+msgstr ""
+
+#: audio/fmopl.cpp:134
+msgid "[nFM] NokturnFM3 (OPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:135
+msgid "[nFM] RetroWave USB OPL3 Express (OPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:136
+msgid "[nFM] Serdaco OPL2LPT (OPL2)"
+msgstr ""
+
+#: audio/fmopl.cpp:137
+msgid "[nFM] Serdaco OPL3LPT (OPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:138
+msgid "[nFM] Cheerful Electronics OPL2 Audio Board (OPL2)"
+msgstr ""
+
+#: audio/fmopl.cpp:139
+msgid "[nFM] Cheerful Electronics OPL3 Duo! (2xOPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:140
+msgid "[nFM] ST Bus ISA / VME SoundBlaster"
+msgstr ""
+
+#: audio/fmopl.cpp:141
+msgid "[nFM] NatFeats / NULL"
+msgstr ""
+
+#: audio/fmopl.cpp:142
+msgid "[nFM] Nuked-OPL3 softsynth (OPL3)"
+msgstr ""
+
 #: audio/mididrv.cpp:232
 #, c-format
 msgid ""
diff --git a/po/fi_FI.po b/po/fi_FI.po
index 194e8f12895..780ea69cbe0 100644
--- a/po/fi_FI.po
+++ b/po/fi_FI.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.6.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-20 16:13+0000\n"
+"POT-Creation-Date: 2026-06-21 13:20+0000\n"
 "PO-Revision-Date: 2026-02-07 15:22+0000\n"
 "Last-Translator: Timo Mikkolainen <tmikkola at gmail.com>\n"
 "Language-Team: Finnish <https://translations.scummvm.org/projects/scummvm/"
@@ -2492,7 +2492,7 @@ msgid "Debug level:"
 msgstr "Debug-taso:"
 
 #. I18N: Debug level -1, no messages
-#: gui/options.cpp:2758 gui/options.cpp:2895 audio/fmopl.cpp:83
+#: gui/options.cpp:2758 gui/options.cpp:2895 audio/fmopl.cpp:112
 msgid "None"
 msgstr "Ei mik????n"
 
@@ -4118,34 +4118,74 @@ msgstr "OpenGL s??vyttimill??"
 msgid "AdLib emulator"
 msgstr "AdLib emulaattori"
 
-#: audio/fmopl.cpp:85
+#: audio/fmopl.cpp:114
 msgid "MAME OPL emulator"
 msgstr "MAME OPL emulaattori"
 
-#: audio/fmopl.cpp:88
+#: audio/fmopl.cpp:117
 msgid "DOSBox OPL emulator"
 msgstr "DOSBox OPL emulaattori"
 
-#: audio/fmopl.cpp:91
+#: audio/fmopl.cpp:120
 msgid "Nuked OPL emulator"
 msgstr "Nuked OPL emulaattori"
 
-#: audio/fmopl.cpp:94
+#: audio/fmopl.cpp:123
 msgid "ALSA Direct FM"
 msgstr "ALSA Direct FM"
 
-#: audio/fmopl.cpp:97
+#: audio/fmopl.cpp:126
 msgid "OPL2LPT"
 msgstr "OPL2LPT"
 
-#: audio/fmopl.cpp:98
+#: audio/fmopl.cpp:127
 msgid "OPL3LPT"
 msgstr "OPL3LPT"
 
-#: audio/fmopl.cpp:101
+#: audio/fmopl.cpp:130
 msgid "RetroWave OPL3"
 msgstr "RetroWave OPL3"
 
+#: audio/fmopl.cpp:133
+msgid "[nFM] NokturnFM2 (OPL2)"
+msgstr ""
+
+#: audio/fmopl.cpp:134
+msgid "[nFM] NokturnFM3 (OPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:135
+msgid "[nFM] RetroWave USB OPL3 Express (OPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:136
+msgid "[nFM] Serdaco OPL2LPT (OPL2)"
+msgstr ""
+
+#: audio/fmopl.cpp:137
+msgid "[nFM] Serdaco OPL3LPT (OPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:138
+msgid "[nFM] Cheerful Electronics OPL2 Audio Board (OPL2)"
+msgstr ""
+
+#: audio/fmopl.cpp:139
+msgid "[nFM] Cheerful Electronics OPL3 Duo! (2xOPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:140
+msgid "[nFM] ST Bus ISA / VME SoundBlaster"
+msgstr ""
+
+#: audio/fmopl.cpp:141
+msgid "[nFM] NatFeats / NULL"
+msgstr ""
+
+#: audio/fmopl.cpp:142
+msgid "[nFM] Nuked-OPL3 softsynth (OPL3)"
+msgstr ""
+
 #: audio/mididrv.cpp:232
 #, c-format
 msgid ""
diff --git a/po/fr_FR.po b/po/fr_FR.po
index 019d007e379..b78c00516e8 100644
--- a/po/fr_FR.po
+++ b/po/fr_FR.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-20 16:13+0000\n"
+"POT-Creation-Date: 2026-06-21 13:20+0000\n"
 "PO-Revision-Date: 2026-06-14 09:41+0000\n"
 "Last-Translator: Purple T <ZEONK at hotmail.com>\n"
 "Language-Team: French <https://translations.scummvm.org/projects/scummvm/"
@@ -2458,7 +2458,7 @@ msgid "Debug level:"
 msgstr "Niveau de D??bogage :"
 
 #. I18N: Debug level -1, no messages
-#: gui/options.cpp:2758 gui/options.cpp:2895 audio/fmopl.cpp:83
+#: gui/options.cpp:2758 gui/options.cpp:2895 audio/fmopl.cpp:112
 msgid "None"
 msgstr "Aucun"
 
@@ -4114,34 +4114,74 @@ msgstr "OpenGL avec shaders"
 msgid "AdLib emulator"
 msgstr "??mulateur AdLib"
 
-#: audio/fmopl.cpp:85
+#: audio/fmopl.cpp:114
 msgid "MAME OPL emulator"
 msgstr "??mulateur MAME OPL"
 
-#: audio/fmopl.cpp:88
+#: audio/fmopl.cpp:117
 msgid "DOSBox OPL emulator"
 msgstr "??mulateur DOSBox OPL"
 
-#: audio/fmopl.cpp:91
+#: audio/fmopl.cpp:120
 msgid "Nuked OPL emulator"
 msgstr "??mulateur Nuked OPL"
 
-#: audio/fmopl.cpp:94
+#: audio/fmopl.cpp:123
 msgid "ALSA Direct FM"
 msgstr "ALSA Direct FM"
 
-#: audio/fmopl.cpp:97
+#: audio/fmopl.cpp:126
 msgid "OPL2LPT"
 msgstr "OPL2LPT"
 
-#: audio/fmopl.cpp:98
+#: audio/fmopl.cpp:127
 msgid "OPL3LPT"
 msgstr "OPL3LPT"
 
-#: audio/fmopl.cpp:101
+#: audio/fmopl.cpp:130
 msgid "RetroWave OPL3"
 msgstr "RetroWave OPL3"
 
+#: audio/fmopl.cpp:133
+msgid "[nFM] NokturnFM2 (OPL2)"
+msgstr ""
+
+#: audio/fmopl.cpp:134
+msgid "[nFM] NokturnFM3 (OPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:135
+msgid "[nFM] RetroWave USB OPL3 Express (OPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:136
+msgid "[nFM] Serdaco OPL2LPT (OPL2)"
+msgstr ""
+
+#: audio/fmopl.cpp:137
+msgid "[nFM] Serdaco OPL3LPT (OPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:138
+msgid "[nFM] Cheerful Electronics OPL2 Audio Board (OPL2)"
+msgstr ""
+
+#: audio/fmopl.cpp:139
+msgid "[nFM] Cheerful Electronics OPL3 Duo! (2xOPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:140
+msgid "[nFM] ST Bus ISA / VME SoundBlaster"
+msgstr ""
+
+#: audio/fmopl.cpp:141
+msgid "[nFM] NatFeats / NULL"
+msgstr ""
+
+#: audio/fmopl.cpp:142
+msgid "[nFM] Nuked-OPL3 softsynth (OPL3)"
+msgstr ""
+
 #: audio/mididrv.cpp:232
 #, c-format
 msgid ""
diff --git a/po/gl_ES.po b/po/gl_ES.po
index ef489309447..811a6a74330 100644
--- a/po/gl_ES.po
+++ b/po/gl_ES.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-20 16:13+0000\n"
+"POT-Creation-Date: 2026-06-21 13:20+0000\n"
 "PO-Revision-Date: 2020-09-26 21:13+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Galician <https://translations.scummvm.org/projects/scummvm/"
@@ -2413,7 +2413,7 @@ msgid "Debug level:"
 msgstr "Depurador"
 
 #. I18N: Debug level -1, no messages
-#: gui/options.cpp:2758 gui/options.cpp:2895 audio/fmopl.cpp:83
+#: gui/options.cpp:2758 gui/options.cpp:2895 audio/fmopl.cpp:112
 #, fuzzy
 msgid "None"
 msgstr "Ning??n"
@@ -4030,34 +4030,74 @@ msgstr ""
 msgid "AdLib emulator"
 msgstr "Emulador de AdLib:"
 
-#: audio/fmopl.cpp:85
+#: audio/fmopl.cpp:114
 msgid "MAME OPL emulator"
 msgstr "Emulador de OPL de MAME"
 
-#: audio/fmopl.cpp:88
+#: audio/fmopl.cpp:117
 msgid "DOSBox OPL emulator"
 msgstr "Emulador de OPL de DOSBox"
 
-#: audio/fmopl.cpp:91
+#: audio/fmopl.cpp:120
 msgid "Nuked OPL emulator"
 msgstr "Emulador de OPL de Nuked"
 
-#: audio/fmopl.cpp:94
+#: audio/fmopl.cpp:123
 msgid "ALSA Direct FM"
 msgstr "ALSA Direct FM"
 
-#: audio/fmopl.cpp:97
+#: audio/fmopl.cpp:126
 msgid "OPL2LPT"
 msgstr "OPL2LPT"
 
-#: audio/fmopl.cpp:98
+#: audio/fmopl.cpp:127
 msgid "OPL3LPT"
 msgstr ""
 
-#: audio/fmopl.cpp:101
+#: audio/fmopl.cpp:130
 msgid "RetroWave OPL3"
 msgstr ""
 
+#: audio/fmopl.cpp:133
+msgid "[nFM] NokturnFM2 (OPL2)"
+msgstr ""
+
+#: audio/fmopl.cpp:134
+msgid "[nFM] NokturnFM3 (OPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:135
+msgid "[nFM] RetroWave USB OPL3 Express (OPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:136
+msgid "[nFM] Serdaco OPL2LPT (OPL2)"
+msgstr ""
+
+#: audio/fmopl.cpp:137
+msgid "[nFM] Serdaco OPL3LPT (OPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:138
+msgid "[nFM] Cheerful Electronics OPL2 Audio Board (OPL2)"
+msgstr ""
+
+#: audio/fmopl.cpp:139
+msgid "[nFM] Cheerful Electronics OPL3 Duo! (2xOPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:140
+msgid "[nFM] ST Bus ISA / VME SoundBlaster"
+msgstr ""
+
+#: audio/fmopl.cpp:141
+msgid "[nFM] NatFeats / NULL"
+msgstr ""
+
+#: audio/fmopl.cpp:142
+msgid "[nFM] Nuked-OPL3 softsynth (OPL3)"
+msgstr ""
+
 #: audio/mididrv.cpp:232
 #, c-format
 msgid ""
diff --git a/po/he.po b/po/he.po
index 7c820c60687..c2cfa529f4a 100644
--- a/po/he.po
+++ b/po/he.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.1.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-20 16:13+0000\n"
+"POT-Creation-Date: 2026-06-21 13:20+0000\n"
 "PO-Revision-Date: 2025-04-09 20:18+0000\n"
 "Last-Translator: Niv Baehr <bloop93 at gmail.com>\n"
 "Language-Team: Hebrew <https://translations.scummvm.org/projects/scummvm/"
@@ -2316,7 +2316,7 @@ msgid "Debug level:"
 msgstr "?????? ?????????? ??????????:"
 
 #. I18N: Debug level -1, no messages
-#: gui/options.cpp:2758 gui/options.cpp:2895 audio/fmopl.cpp:83
+#: gui/options.cpp:2758 gui/options.cpp:2895 audio/fmopl.cpp:112
 msgid "None"
 msgstr "??????"
 
@@ -3975,34 +3975,74 @@ msgstr "OpenGL ???? ????????????"
 msgid "AdLib emulator"
 msgstr "?????????????? AdLib"
 
-#: audio/fmopl.cpp:85
+#: audio/fmopl.cpp:114
 msgid "MAME OPL emulator"
 msgstr "?????????????? MAME OPL"
 
-#: audio/fmopl.cpp:88
+#: audio/fmopl.cpp:117
 msgid "DOSBox OPL emulator"
 msgstr "?????????????? DOSBox OPL"
 
-#: audio/fmopl.cpp:91
+#: audio/fmopl.cpp:120
 msgid "Nuked OPL emulator"
 msgstr "?????????????? OPL Nuked"
 
-#: audio/fmopl.cpp:94
+#: audio/fmopl.cpp:123
 msgid "ALSA Direct FM"
 msgstr "ALSA Direct FM"
 
-#: audio/fmopl.cpp:97
+#: audio/fmopl.cpp:126
 msgid "OPL2LPT"
 msgstr "OPL2LPT"
 
-#: audio/fmopl.cpp:98
+#: audio/fmopl.cpp:127
 msgid "OPL3LPT"
 msgstr "OPL3LPT"
 
-#: audio/fmopl.cpp:101
+#: audio/fmopl.cpp:130
 msgid "RetroWave OPL3"
 msgstr "RetroWave OPL3"
 
+#: audio/fmopl.cpp:133
+msgid "[nFM] NokturnFM2 (OPL2)"
+msgstr ""
+
+#: audio/fmopl.cpp:134
+msgid "[nFM] NokturnFM3 (OPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:135
+msgid "[nFM] RetroWave USB OPL3 Express (OPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:136
+msgid "[nFM] Serdaco OPL2LPT (OPL2)"
+msgstr ""
+
+#: audio/fmopl.cpp:137
+msgid "[nFM] Serdaco OPL3LPT (OPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:138
+msgid "[nFM] Cheerful Electronics OPL2 Audio Board (OPL2)"
+msgstr ""
+
+#: audio/fmopl.cpp:139
+msgid "[nFM] Cheerful Electronics OPL3 Duo! (2xOPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:140
+msgid "[nFM] ST Bus ISA / VME SoundBlaster"
+msgstr ""
+
+#: audio/fmopl.cpp:141
+msgid "[nFM] NatFeats / NULL"
+msgstr ""
+
+#: audio/fmopl.cpp:142
+msgid "[nFM] Nuked-OPL3 softsynth (OPL3)"
+msgstr ""
+
 #: audio/mididrv.cpp:232
 #, c-format
 msgid ""
diff --git a/po/hi.po b/po/hi.po
index e3a82a715c9..9cd9274ed29 100644
--- a/po/hi.po
+++ b/po/hi.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-20 16:13+0000\n"
+"POT-Creation-Date: 2026-06-21 13:20+0000\n"
 "PO-Revision-Date: 2026-05-12 22:22+0000\n"
 "Last-Translator: KARTIK SAXENA <kartiksaxena2005 at gmail.com>\n"
 "Language-Team: Hindi <https://translations.scummvm.org/projects/scummvm/"
@@ -2364,7 +2364,7 @@ msgid "Debug level:"
 msgstr ""
 
 #. I18N: Debug level -1, no messages
-#: gui/options.cpp:2758 gui/options.cpp:2895 audio/fmopl.cpp:83
+#: gui/options.cpp:2758 gui/options.cpp:2895 audio/fmopl.cpp:112
 msgid "None"
 msgstr "????????? ????????????"
 
@@ -3900,34 +3900,74 @@ msgstr ""
 msgid "AdLib emulator"
 msgstr ""
 
-#: audio/fmopl.cpp:85
+#: audio/fmopl.cpp:114
 msgid "MAME OPL emulator"
 msgstr ""
 
-#: audio/fmopl.cpp:88
+#: audio/fmopl.cpp:117
 msgid "DOSBox OPL emulator"
 msgstr ""
 
-#: audio/fmopl.cpp:91
+#: audio/fmopl.cpp:120
 msgid "Nuked OPL emulator"
 msgstr ""
 
-#: audio/fmopl.cpp:94
+#: audio/fmopl.cpp:123
 msgid "ALSA Direct FM"
 msgstr ""
 
-#: audio/fmopl.cpp:97
+#: audio/fmopl.cpp:126
 msgid "OPL2LPT"
 msgstr ""
 
-#: audio/fmopl.cpp:98
+#: audio/fmopl.cpp:127
 msgid "OPL3LPT"
 msgstr ""
 
-#: audio/fmopl.cpp:101
+#: audio/fmopl.cpp:130
 msgid "RetroWave OPL3"
 msgstr ""
 
+#: audio/fmopl.cpp:133
+msgid "[nFM] NokturnFM2 (OPL2)"
+msgstr ""
+
+#: audio/fmopl.cpp:134
+msgid "[nFM] NokturnFM3 (OPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:135
+msgid "[nFM] RetroWave USB OPL3 Express (OPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:136
+msgid "[nFM] Serdaco OPL2LPT (OPL2)"
+msgstr ""
+
+#: audio/fmopl.cpp:137
+msgid "[nFM] Serdaco OPL3LPT (OPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:138
+msgid "[nFM] Cheerful Electronics OPL2 Audio Board (OPL2)"
+msgstr ""
+
+#: audio/fmopl.cpp:139
+msgid "[nFM] Cheerful Electronics OPL3 Duo! (2xOPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:140
+msgid "[nFM] ST Bus ISA / VME SoundBlaster"
+msgstr ""
+
+#: audio/fmopl.cpp:141
+msgid "[nFM] NatFeats / NULL"
+msgstr ""
+
+#: audio/fmopl.cpp:142
+msgid "[nFM] Nuked-OPL3 softsynth (OPL3)"
+msgstr ""
+
 #: audio/mididrv.cpp:232
 #, c-format
 msgid ""
diff --git a/po/hu_HU.po b/po/hu_HU.po
index 8f5153c4ac4..634454a2171 100644
--- a/po/hu_HU.po
+++ b/po/hu_HU.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-20 16:13+0000\n"
+"POT-Creation-Date: 2026-06-21 13:20+0000\n"
 "PO-Revision-Date: 2025-03-07 00:18+0000\n"
 "Last-Translator: Hov??nszki Tam??s <gahex68379 at lofiey.com>\n"
 "Language-Team: Hungarian <https://translations.scummvm.org/projects/scummvm/"
@@ -2493,7 +2493,7 @@ msgid "Debug level:"
 msgstr "Hibakeres??si szint:"
 
 #. I18N: Debug level -1, no messages
-#: gui/options.cpp:2758 gui/options.cpp:2895 audio/fmopl.cpp:83
+#: gui/options.cpp:2758 gui/options.cpp:2895 audio/fmopl.cpp:112
 msgid "None"
 msgstr "Nincs"
 
@@ -4133,34 +4133,74 @@ msgstr "OpenGL shader-ekkel"
 msgid "AdLib emulator"
 msgstr "AdLib emul??tor"
 
-#: audio/fmopl.cpp:85
+#: audio/fmopl.cpp:114
 msgid "MAME OPL emulator"
 msgstr "MAME OPL emul??tor"
 
-#: audio/fmopl.cpp:88
+#: audio/fmopl.cpp:117
 msgid "DOSBox OPL emulator"
 msgstr "DOSBox OPL emul??tor"
 
-#: audio/fmopl.cpp:91
+#: audio/fmopl.cpp:120
 msgid "Nuked OPL emulator"
 msgstr "Nuked OPL emul??tor"
 
-#: audio/fmopl.cpp:94
+#: audio/fmopl.cpp:123
 msgid "ALSA Direct FM"
 msgstr "ALSA Direct FM"
 
-#: audio/fmopl.cpp:97
+#: audio/fmopl.cpp:126
 msgid "OPL2LPT"
 msgstr "OPL2LPT"
 
-#: audio/fmopl.cpp:98
+#: audio/fmopl.cpp:127
 msgid "OPL3LPT"
 msgstr "OPL3LPT"
 
-#: audio/fmopl.cpp:101
+#: audio/fmopl.cpp:130
 msgid "RetroWave OPL3"
 msgstr "RetroWave OPL3"
 
+#: audio/fmopl.cpp:133
+msgid "[nFM] NokturnFM2 (OPL2)"
+msgstr ""
+
+#: audio/fmopl.cpp:134
+msgid "[nFM] NokturnFM3 (OPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:135
+msgid "[nFM] RetroWave USB OPL3 Express (OPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:136
+msgid "[nFM] Serdaco OPL2LPT (OPL2)"
+msgstr ""
+
+#: audio/fmopl.cpp:137
+msgid "[nFM] Serdaco OPL3LPT (OPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:138
+msgid "[nFM] Cheerful Electronics OPL2 Audio Board (OPL2)"
+msgstr ""
+
+#: audio/fmopl.cpp:139
+msgid "[nFM] Cheerful Electronics OPL3 Duo! (2xOPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:140
+msgid "[nFM] ST Bus ISA / VME SoundBlaster"
+msgstr ""
+
+#: audio/fmopl.cpp:141
+msgid "[nFM] NatFeats / NULL"
+msgstr ""
+
+#: audio/fmopl.cpp:142
+msgid "[nFM] Nuked-OPL3 softsynth (OPL3)"
+msgstr ""
+
 #: audio/mididrv.cpp:232
 #, c-format
 msgid ""
diff --git a/po/it_IT.po b/po/it_IT.po
index d2d39a131ff..dedec9035fd 100644
--- a/po/it_IT.po
+++ b/po/it_IT.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-20 16:13+0000\n"
+"POT-Creation-Date: 2026-06-21 13:20+0000\n"
 "PO-Revision-Date: 2026-06-20 15:07+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Italian <https://translations.scummvm.org/projects/scummvm/"
@@ -2440,7 +2440,7 @@ msgid "Debug level:"
 msgstr "Livello Debug:"
 
 #. I18N: Debug level -1, no messages
-#: gui/options.cpp:2758 gui/options.cpp:2895 audio/fmopl.cpp:83
+#: gui/options.cpp:2758 gui/options.cpp:2895 audio/fmopl.cpp:112
 msgid "None"
 msgstr "Nessuno"
 
@@ -4081,34 +4081,74 @@ msgstr "OpenGL con shaders"
 msgid "AdLib emulator"
 msgstr "Emulatore AdLib"
 
-#: audio/fmopl.cpp:85
+#: audio/fmopl.cpp:114
 msgid "MAME OPL emulator"
 msgstr "Emulatore OPL MAME"
 
-#: audio/fmopl.cpp:88
+#: audio/fmopl.cpp:117
 msgid "DOSBox OPL emulator"
 msgstr "Emulatore OPL DOSBox"
 
-#: audio/fmopl.cpp:91
+#: audio/fmopl.cpp:120
 msgid "Nuked OPL emulator"
 msgstr "Emulatore OPL Nuked"
 
-#: audio/fmopl.cpp:94
+#: audio/fmopl.cpp:123
 msgid "ALSA Direct FM"
 msgstr "ALSA Direct FM"
 
-#: audio/fmopl.cpp:97
+#: audio/fmopl.cpp:126
 msgid "OPL2LPT"
 msgstr "OPL2LPT"
 
-#: audio/fmopl.cpp:98
+#: audio/fmopl.cpp:127
 msgid "OPL3LPT"
 msgstr "OPL3LPT"
 
-#: audio/fmopl.cpp:101
+#: audio/fmopl.cpp:130
 msgid "RetroWave OPL3"
 msgstr "RetroWave OPL3"
 
+#: audio/fmopl.cpp:133
+msgid "[nFM] NokturnFM2 (OPL2)"
+msgstr ""
+
+#: audio/fmopl.cpp:134
+msgid "[nFM] NokturnFM3 (OPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:135
+msgid "[nFM] RetroWave USB OPL3 Express (OPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:136
+msgid "[nFM] Serdaco OPL2LPT (OPL2)"
+msgstr ""
+
+#: audio/fmopl.cpp:137
+msgid "[nFM] Serdaco OPL3LPT (OPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:138
+msgid "[nFM] Cheerful Electronics OPL2 Audio Board (OPL2)"
+msgstr ""
+
+#: audio/fmopl.cpp:139
+msgid "[nFM] Cheerful Electronics OPL3 Duo! (2xOPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:140
+msgid "[nFM] ST Bus ISA / VME SoundBlaster"
+msgstr ""
+
+#: audio/fmopl.cpp:141
+msgid "[nFM] NatFeats / NULL"
+msgstr ""
+
+#: audio/fmopl.cpp:142
+msgid "[nFM] Nuked-OPL3 softsynth (OPL3)"
+msgstr ""
+
 #: audio/mididrv.cpp:232
 #, c-format
 msgid ""
diff --git a/po/ja.po b/po/ja.po
index 6bb5662d831..96938863038 100644
--- a/po/ja.po
+++ b/po/ja.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-20 16:13+0000\n"
+"POT-Creation-Date: 2026-06-21 13:20+0000\n"
 "PO-Revision-Date: 2026-06-04 07:22+0000\n"
 "Last-Translator: VAN-Gluon <van.gluonp at gmail.com>\n"
 "Language-Team: Japanese <https://translations.scummvm.org/projects/scummvm/"
@@ -2364,7 +2364,7 @@ msgid "Debug level:"
 msgstr "????????????LV???"
 
 #. I18N: Debug level -1, no messages
-#: gui/options.cpp:2758 gui/options.cpp:2895 audio/fmopl.cpp:83
+#: gui/options.cpp:2758 gui/options.cpp:2895 audio/fmopl.cpp:112
 msgid "None"
 msgstr "??????"
 
@@ -3947,34 +3947,74 @@ msgstr "OpenGL(???????????????)"
 msgid "AdLib emulator"
 msgstr "AdLib????????????????????????"
 
-#: audio/fmopl.cpp:85
+#: audio/fmopl.cpp:114
 msgid "MAME OPL emulator"
 msgstr "MAME OPL ?????????????????????"
 
-#: audio/fmopl.cpp:88
+#: audio/fmopl.cpp:117
 msgid "DOSBox OPL emulator"
 msgstr "DOSBox OPL ?????????????????????"
 
-#: audio/fmopl.cpp:91
+#: audio/fmopl.cpp:120
 msgid "Nuked OPL emulator"
 msgstr "Nuked OPL ?????????????????????"
 
-#: audio/fmopl.cpp:94
+#: audio/fmopl.cpp:123
 msgid "ALSA Direct FM"
 msgstr "ALSA(???????????????FM)"
 
-#: audio/fmopl.cpp:97
+#: audio/fmopl.cpp:126
 msgid "OPL2LPT"
 msgstr "OPL2LPT"
 
-#: audio/fmopl.cpp:98
+#: audio/fmopl.cpp:127
 msgid "OPL3LPT"
 msgstr "OPL3LPT"
 
-#: audio/fmopl.cpp:101
+#: audio/fmopl.cpp:130
 msgid "RetroWave OPL3"
 msgstr "OPL3"
 
+#: audio/fmopl.cpp:133
+msgid "[nFM] NokturnFM2 (OPL2)"
+msgstr ""
+
+#: audio/fmopl.cpp:134
+msgid "[nFM] NokturnFM3 (OPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:135
+msgid "[nFM] RetroWave USB OPL3 Express (OPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:136
+msgid "[nFM] Serdaco OPL2LPT (OPL2)"
+msgstr ""
+
+#: audio/fmopl.cpp:137
+msgid "[nFM] Serdaco OPL3LPT (OPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:138
+msgid "[nFM] Cheerful Electronics OPL2 Audio Board (OPL2)"
+msgstr ""
+
+#: audio/fmopl.cpp:139
+msgid "[nFM] Cheerful Electronics OPL3 Duo! (2xOPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:140
+msgid "[nFM] ST Bus ISA / VME SoundBlaster"
+msgstr ""
+
+#: audio/fmopl.cpp:141
+msgid "[nFM] NatFeats / NULL"
+msgstr ""
+
+#: audio/fmopl.cpp:142
+msgid "[nFM] Nuked-OPL3 softsynth (OPL3)"
+msgstr ""
+
 #: audio/mididrv.cpp:232
 #, c-format
 msgid ""
diff --git a/po/ka.po b/po/ka.po
index 74367fb8177..b4ab8a36203 100644
--- a/po/ka.po
+++ b/po/ka.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-20 16:13+0000\n"
+"POT-Creation-Date: 2026-06-21 13:20+0000\n"
 "PO-Revision-Date: 2026-06-05 10:22+0000\n"
 "Last-Translator: NorwayFun <temuri.doghonadze at gmail.com>\n"
 "Language-Team: Georgian <https://translations.scummvm.org/projects/scummvm/"
@@ -2281,7 +2281,7 @@ msgid "Debug level:"
 msgstr "??????????????????????????? ????????????:"
 
 #. I18N: Debug level -1, no messages
-#: gui/options.cpp:2758 gui/options.cpp:2895 audio/fmopl.cpp:83
+#: gui/options.cpp:2758 gui/options.cpp:2895 audio/fmopl.cpp:112
 msgid "None"
 msgstr "?????????????????????"
 
@@ -3766,34 +3766,74 @@ msgstr "OpenGL ??????????????????????????????"
 msgid "AdLib emulator"
 msgstr "AdLib-?????? ???????????????????????????"
 
-#: audio/fmopl.cpp:85
+#: audio/fmopl.cpp:114
 msgid "MAME OPL emulator"
 msgstr "MAME OPL-?????? ???????????????????????????"
 
-#: audio/fmopl.cpp:88
+#: audio/fmopl.cpp:117
 msgid "DOSBox OPL emulator"
 msgstr "DOSBox OPL-?????? ???????????????????????????"
 
-#: audio/fmopl.cpp:91
+#: audio/fmopl.cpp:120
 msgid "Nuked OPL emulator"
 msgstr "Nuked OPL-?????? ???????????????????????????"
 
-#: audio/fmopl.cpp:94
+#: audio/fmopl.cpp:123
 msgid "ALSA Direct FM"
 msgstr "ALSA Direct FM"
 
-#: audio/fmopl.cpp:97
+#: audio/fmopl.cpp:126
 msgid "OPL2LPT"
 msgstr "OPL2LPT"
 
-#: audio/fmopl.cpp:98
+#: audio/fmopl.cpp:127
 msgid "OPL3LPT"
 msgstr "OPL3LPT"
 
-#: audio/fmopl.cpp:101
+#: audio/fmopl.cpp:130
 msgid "RetroWave OPL3"
 msgstr "RetroWave OPL3"
 
+#: audio/fmopl.cpp:133
+msgid "[nFM] NokturnFM2 (OPL2)"
+msgstr ""
+
+#: audio/fmopl.cpp:134
+msgid "[nFM] NokturnFM3 (OPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:135
+msgid "[nFM] RetroWave USB OPL3 Express (OPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:136
+msgid "[nFM] Serdaco OPL2LPT (OPL2)"
+msgstr ""
+
+#: audio/fmopl.cpp:137
+msgid "[nFM] Serdaco OPL3LPT (OPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:138
+msgid "[nFM] Cheerful Electronics OPL2 Audio Board (OPL2)"
+msgstr ""
+
+#: audio/fmopl.cpp:139
+msgid "[nFM] Cheerful Electronics OPL3 Duo! (2xOPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:140
+msgid "[nFM] ST Bus ISA / VME SoundBlaster"
+msgstr ""
+
+#: audio/fmopl.cpp:141
+msgid "[nFM] NatFeats / NULL"
+msgstr ""
+
+#: audio/fmopl.cpp:142
+msgid "[nFM] Nuked-OPL3 softsynth (OPL3)"
+msgstr ""
+
 #: audio/mididrv.cpp:232
 #, c-format
 msgid ""
diff --git a/po/ko.po b/po/ko.po
index 3ece3c58d98..a0bceef108a 100644
--- a/po/ko.po
+++ b/po/ko.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-20 16:13+0000\n"
+"POT-Creation-Date: 2026-06-21 13:20+0000\n"
 "PO-Revision-Date: 2026-05-19 20:22+0000\n"
 "Last-Translator: Hoseok Seo <ddinghoya at gmail.com>\n"
 "Language-Team: Korean <https://translations.scummvm.org/projects/scummvm/"
@@ -2411,7 +2411,7 @@ msgid "Debug level:"
 msgstr "????????? ??????:"
 
 #. I18N: Debug level -1, no messages
-#: gui/options.cpp:2758 gui/options.cpp:2895 audio/fmopl.cpp:83
+#: gui/options.cpp:2758 gui/options.cpp:2895 audio/fmopl.cpp:112
 msgid "None"
 msgstr "??????"
 
@@ -4001,34 +4001,74 @@ msgstr "???????????? ????????? OpenGL"
 msgid "AdLib emulator"
 msgstr "????????? ???????????????"
 
-#: audio/fmopl.cpp:85
+#: audio/fmopl.cpp:114
 msgid "MAME OPL emulator"
 msgstr "?????? OPL ???????????????"
 
-#: audio/fmopl.cpp:88
+#: audio/fmopl.cpp:117
 msgid "DOSBox OPL emulator"
 msgstr "???????????? OPL ???????????????"
 
-#: audio/fmopl.cpp:91
+#: audio/fmopl.cpp:120
 msgid "Nuked OPL emulator"
 msgstr "????????? OPL ???????????????"
 
-#: audio/fmopl.cpp:94
+#: audio/fmopl.cpp:123
 msgid "ALSA Direct FM"
 msgstr "ALSA ???????????? FM"
 
-#: audio/fmopl.cpp:97
+#: audio/fmopl.cpp:126
 msgid "OPL2LPT"
 msgstr "OPL2LPT"
 
-#: audio/fmopl.cpp:98
+#: audio/fmopl.cpp:127
 msgid "OPL3LPT"
 msgstr "OPL3LPT"
 
-#: audio/fmopl.cpp:101
+#: audio/fmopl.cpp:130
 msgid "RetroWave OPL3"
 msgstr "?????????????????? OPL3"
 
+#: audio/fmopl.cpp:133
+msgid "[nFM] NokturnFM2 (OPL2)"
+msgstr ""
+
+#: audio/fmopl.cpp:134
+msgid "[nFM] NokturnFM3 (OPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:135
+msgid "[nFM] RetroWave USB OPL3 Express (OPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:136
+msgid "[nFM] Serdaco OPL2LPT (OPL2)"
+msgstr ""
+
+#: audio/fmopl.cpp:137
+msgid "[nFM] Serdaco OPL3LPT (OPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:138
+msgid "[nFM] Cheerful Electronics OPL2 Audio Board (OPL2)"
+msgstr ""
+
+#: audio/fmopl.cpp:139
+msgid "[nFM] Cheerful Electronics OPL3 Duo! (2xOPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:140
+msgid "[nFM] ST Bus ISA / VME SoundBlaster"
+msgstr ""
+
+#: audio/fmopl.cpp:141
+msgid "[nFM] NatFeats / NULL"
+msgstr ""
+
+#: audio/fmopl.cpp:142
+msgid "[nFM] Nuked-OPL3 softsynth (OPL3)"
+msgstr ""
+
 #: audio/mididrv.cpp:232
 #, c-format
 msgid ""
diff --git a/po/nb_NO.po b/po/nb_NO.po
index a17d9e66422..cb7d42444b9 100644
--- a/po/nb_NO.po
+++ b/po/nb_NO.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-20 16:13+0000\n"
+"POT-Creation-Date: 2026-06-21 13:20+0000\n"
 "PO-Revision-Date: 2026-02-07 15:22+0000\n"
 "Last-Translator: Einar Johan Tr??an S??m??en <einarjohants at gmail.com>\n"
 "Language-Team: Norwegian Bokm??l <https://translations.scummvm.org/projects/"
@@ -2492,7 +2492,7 @@ msgid "Debug level:"
 msgstr "Debug-niv??:"
 
 #. I18N: Debug level -1, no messages
-#: gui/options.cpp:2758 gui/options.cpp:2895 audio/fmopl.cpp:83
+#: gui/options.cpp:2758 gui/options.cpp:2895 audio/fmopl.cpp:112
 msgid "None"
 msgstr "Ingen"
 
@@ -4135,34 +4135,74 @@ msgstr "OpenGL m/shadere"
 msgid "AdLib emulator"
 msgstr "AdLib-emulator"
 
-#: audio/fmopl.cpp:85
+#: audio/fmopl.cpp:114
 msgid "MAME OPL emulator"
 msgstr "MAME OPL emulator"
 
-#: audio/fmopl.cpp:88
+#: audio/fmopl.cpp:117
 msgid "DOSBox OPL emulator"
 msgstr "DOSBox OPL emulator"
 
-#: audio/fmopl.cpp:91
+#: audio/fmopl.cpp:120
 msgid "Nuked OPL emulator"
 msgstr "Nuked OPL emulator"
 
-#: audio/fmopl.cpp:94
+#: audio/fmopl.cpp:123
 msgid "ALSA Direct FM"
 msgstr "ALSA Direct FM"
 
-#: audio/fmopl.cpp:97
+#: audio/fmopl.cpp:126
 msgid "OPL2LPT"
 msgstr "OPL2LPT"
 
-#: audio/fmopl.cpp:98
+#: audio/fmopl.cpp:127
 msgid "OPL3LPT"
 msgstr "OPL3LPT"
 
-#: audio/fmopl.cpp:101
+#: audio/fmopl.cpp:130
 msgid "RetroWave OPL3"
 msgstr "RetroWave OPL3"
 
+#: audio/fmopl.cpp:133
+msgid "[nFM] NokturnFM2 (OPL2)"
+msgstr ""
+
+#: audio/fmopl.cpp:134
+msgid "[nFM] NokturnFM3 (OPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:135
+msgid "[nFM] RetroWave USB OPL3 Express (OPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:136
+msgid "[nFM] Serdaco OPL2LPT (OPL2)"
+msgstr ""
+
+#: audio/fmopl.cpp:137
+msgid "[nFM] Serdaco OPL3LPT (OPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:138
+msgid "[nFM] Cheerful Electronics OPL2 Audio Board (OPL2)"
+msgstr ""
+
+#: audio/fmopl.cpp:139
+msgid "[nFM] Cheerful Electronics OPL3 Duo! (2xOPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:140
+msgid "[nFM] ST Bus ISA / VME SoundBlaster"
+msgstr ""
+
+#: audio/fmopl.cpp:141
+msgid "[nFM] NatFeats / NULL"
+msgstr ""
+
+#: audio/fmopl.cpp:142
+msgid "[nFM] Nuked-OPL3 softsynth (OPL3)"
+msgstr ""
+
 #: audio/mididrv.cpp:232
 #, c-format
 msgid ""
diff --git a/po/nl_NL.po b/po/nl_NL.po
index 5b20f7de201..43512629b99 100644
--- a/po/nl_NL.po
+++ b/po/nl_NL.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.9.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-20 16:13+0000\n"
+"POT-Creation-Date: 2026-06-21 13:20+0000\n"
 "PO-Revision-Date: 2026-02-11 14:23+0000\n"
 "Last-Translator: SecularSteve <fairfull.playing at gmail.com>\n"
 "Language-Team: Dutch <https://translations.scummvm.org/projects/scummvm/"
@@ -2425,7 +2425,7 @@ msgid "Debug level:"
 msgstr "Debug level:"
 
 #. I18N: Debug level -1, no messages
-#: gui/options.cpp:2758 gui/options.cpp:2895 audio/fmopl.cpp:83
+#: gui/options.cpp:2758 gui/options.cpp:2895 audio/fmopl.cpp:112
 msgid "None"
 msgstr "Geen"
 
@@ -4062,34 +4062,74 @@ msgstr "OpenGL met shaders"
 msgid "AdLib emulator"
 msgstr "AdLib emulator"
 
-#: audio/fmopl.cpp:85
+#: audio/fmopl.cpp:114
 msgid "MAME OPL emulator"
 msgstr "MAME OPL emulator"
 
-#: audio/fmopl.cpp:88
+#: audio/fmopl.cpp:117
 msgid "DOSBox OPL emulator"
 msgstr "DOSBox OPL emulator"
 
-#: audio/fmopl.cpp:91
+#: audio/fmopl.cpp:120
 msgid "Nuked OPL emulator"
 msgstr "Nuked OPL emulator"
 
-#: audio/fmopl.cpp:94
+#: audio/fmopl.cpp:123
 msgid "ALSA Direct FM"
 msgstr "ALSA Direct FM"
 
-#: audio/fmopl.cpp:97
+#: audio/fmopl.cpp:126
 msgid "OPL2LPT"
 msgstr "OPL2LPT"
 
-#: audio/fmopl.cpp:98
+#: audio/fmopl.cpp:127
 msgid "OPL3LPT"
 msgstr "OPL3LPT"
 
-#: audio/fmopl.cpp:101
+#: audio/fmopl.cpp:130
 msgid "RetroWave OPL3"
 msgstr "RetroWave OPL3"
 
+#: audio/fmopl.cpp:133
+msgid "[nFM] NokturnFM2 (OPL2)"
+msgstr ""
+
+#: audio/fmopl.cpp:134
+msgid "[nFM] NokturnFM3 (OPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:135
+msgid "[nFM] RetroWave USB OPL3 Express (OPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:136
+msgid "[nFM] Serdaco OPL2LPT (OPL2)"
+msgstr ""
+
+#: audio/fmopl.cpp:137
+msgid "[nFM] Serdaco OPL3LPT (OPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:138
+msgid "[nFM] Cheerful Electronics OPL2 Audio Board (OPL2)"
+msgstr ""
+
+#: audio/fmopl.cpp:139
+msgid "[nFM] Cheerful Electronics OPL3 Duo! (2xOPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:140
+msgid "[nFM] ST Bus ISA / VME SoundBlaster"
+msgstr ""
+
+#: audio/fmopl.cpp:141
+msgid "[nFM] NatFeats / NULL"
+msgstr ""
+
+#: audio/fmopl.cpp:142
+msgid "[nFM] Nuked-OPL3 softsynth (OPL3)"
+msgstr ""
+
 #: audio/mididrv.cpp:232
 #, c-format
 msgid ""
diff --git a/po/nn_NO.po b/po/nn_NO.po
index 8a3ec8f300a..4ebb3f65612 100644
--- a/po/nn_NO.po
+++ b/po/nn_NO.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-20 16:13+0000\n"
+"POT-Creation-Date: 2026-06-21 13:20+0000\n"
 "PO-Revision-Date: 2023-12-14 14:28+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Norwegian Nynorsk <https://translations.scummvm.org/projects/"
@@ -2387,7 +2387,7 @@ msgid "Debug level:"
 msgstr "Debugger"
 
 #. I18N: Debug level -1, no messages
-#: gui/options.cpp:2758 gui/options.cpp:2895 audio/fmopl.cpp:83
+#: gui/options.cpp:2758 gui/options.cpp:2895 audio/fmopl.cpp:112
 #, fuzzy
 msgid "None"
 msgstr "Ingen"
@@ -3994,34 +3994,74 @@ msgstr ""
 msgid "AdLib emulator"
 msgstr "AdLib emulator:"
 
-#: audio/fmopl.cpp:85
+#: audio/fmopl.cpp:114
 msgid "MAME OPL emulator"
 msgstr "MAME OPL emulator"
 
-#: audio/fmopl.cpp:88
+#: audio/fmopl.cpp:117
 msgid "DOSBox OPL emulator"
 msgstr "DOSBox OPL emulator"
 
-#: audio/fmopl.cpp:91
+#: audio/fmopl.cpp:120
 msgid "Nuked OPL emulator"
 msgstr "Nuked OPL emulator"
 
-#: audio/fmopl.cpp:94
+#: audio/fmopl.cpp:123
 msgid "ALSA Direct FM"
 msgstr "ALSA Direct FM"
 
-#: audio/fmopl.cpp:97
+#: audio/fmopl.cpp:126
 msgid "OPL2LPT"
 msgstr "OPL2LPT"
 
-#: audio/fmopl.cpp:98
+#: audio/fmopl.cpp:127
 msgid "OPL3LPT"
 msgstr ""
 
-#: audio/fmopl.cpp:101
+#: audio/fmopl.cpp:130
 msgid "RetroWave OPL3"
 msgstr ""
 
+#: audio/fmopl.cpp:133
+msgid "[nFM] NokturnFM2 (OPL2)"
+msgstr ""
+
+#: audio/fmopl.cpp:134
+msgid "[nFM] NokturnFM3 (OPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:135
+msgid "[nFM] RetroWave USB OPL3 Express (OPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:136
+msgid "[nFM] Serdaco OPL2LPT (OPL2)"
+msgstr ""
+
+#: audio/fmopl.cpp:137
+msgid "[nFM] Serdaco OPL3LPT (OPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:138
+msgid "[nFM] Cheerful Electronics OPL2 Audio Board (OPL2)"
+msgstr ""
+
+#: audio/fmopl.cpp:139
+msgid "[nFM] Cheerful Electronics OPL3 Duo! (2xOPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:140
+msgid "[nFM] ST Bus ISA / VME SoundBlaster"
+msgstr ""
+
+#: audio/fmopl.cpp:141
+msgid "[nFM] NatFeats / NULL"
+msgstr ""
+
+#: audio/fmopl.cpp:142
+msgid "[nFM] Nuked-OPL3 softsynth (OPL3)"
+msgstr ""
+
 #: audio/mididrv.cpp:232
 #, c-format
 msgid ""
diff --git a/po/pl_PL.po b/po/pl_PL.po
index e46b987e27f..ee52aed3e53 100644
--- a/po/pl_PL.po
+++ b/po/pl_PL.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-20 16:13+0000\n"
+"POT-Creation-Date: 2026-06-21 13:20+0000\n"
 "PO-Revision-Date: 2026-04-17 00:22+0000\n"
 "Last-Translator: SkiffPL <skiff at windowslive.com>\n"
 "Language-Team: Polish <https://translations.scummvm.org/projects/scummvm/"
@@ -2440,7 +2440,7 @@ msgid "Debug level:"
 msgstr "Debugger:"
 
 #. I18N: Debug level -1, no messages
-#: gui/options.cpp:2758 gui/options.cpp:2895 audio/fmopl.cpp:83
+#: gui/options.cpp:2758 gui/options.cpp:2895 audio/fmopl.cpp:112
 msgid "None"
 msgstr "Brak"
 
@@ -4053,34 +4053,74 @@ msgstr "OpenGL ze shaderami"
 msgid "AdLib emulator"
 msgstr "Emulator AdLib"
 
-#: audio/fmopl.cpp:85
+#: audio/fmopl.cpp:114
 msgid "MAME OPL emulator"
 msgstr "Emulator MAME OPL"
 
-#: audio/fmopl.cpp:88
+#: audio/fmopl.cpp:117
 msgid "DOSBox OPL emulator"
 msgstr "Emulator DOSBox OPL"
 
-#: audio/fmopl.cpp:91
+#: audio/fmopl.cpp:120
 msgid "Nuked OPL emulator"
 msgstr "Emulator Nuked OPL"
 
-#: audio/fmopl.cpp:94
+#: audio/fmopl.cpp:123
 msgid "ALSA Direct FM"
 msgstr "Bezpo??rednie FM Alsa"
 
-#: audio/fmopl.cpp:97
+#: audio/fmopl.cpp:126
 msgid "OPL2LPT"
 msgstr "OPL2LPT"
 
-#: audio/fmopl.cpp:98
+#: audio/fmopl.cpp:127
 msgid "OPL3LPT"
 msgstr "OPL3LPT"
 
-#: audio/fmopl.cpp:101
+#: audio/fmopl.cpp:130
 msgid "RetroWave OPL3"
 msgstr "RetroWave OPL3"
 
+#: audio/fmopl.cpp:133
+msgid "[nFM] NokturnFM2 (OPL2)"
+msgstr ""
+
+#: audio/fmopl.cpp:134
+msgid "[nFM] NokturnFM3 (OPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:135
+msgid "[nFM] RetroWave USB OPL3 Express (OPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:136
+msgid "[nFM] Serdaco OPL2LPT (OPL2)"
+msgstr ""
+
+#: audio/fmopl.cpp:137
+msgid "[nFM] Serdaco OPL3LPT (OPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:138
+msgid "[nFM] Cheerful Electronics OPL2 Audio Board (OPL2)"
+msgstr ""
+
+#: audio/fmopl.cpp:139
+msgid "[nFM] Cheerful Electronics OPL3 Duo! (2xOPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:140
+msgid "[nFM] ST Bus ISA / VME SoundBlaster"
+msgstr ""
+
+#: audio/fmopl.cpp:141
+msgid "[nFM] NatFeats / NULL"
+msgstr ""
+
+#: audio/fmopl.cpp:142
+msgid "[nFM] Nuked-OPL3 softsynth (OPL3)"
+msgstr ""
+
 #: audio/mididrv.cpp:232
 #, c-format
 msgid ""
diff --git a/po/pt_BR.po b/po/pt_BR.po
index 69c32478d02..0e0a12f209d 100644
--- a/po/pt_BR.po
+++ b/po/pt_BR.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-20 16:13+0000\n"
+"POT-Creation-Date: 2026-06-21 13:20+0000\n"
 "PO-Revision-Date: 2026-06-17 22:10+0000\n"
 "Last-Translator: Marcel Souza Lemes <marcosoutsider at gmail.com>\n"
 "Language-Team: Portuguese (Brazil) <https://translations.scummvm.org/"
@@ -2502,7 +2502,7 @@ msgid "Debug level:"
 msgstr "N??vel de Depura????o:"
 
 #. I18N: Debug level -1, no messages
-#: gui/options.cpp:2758 gui/options.cpp:2895 audio/fmopl.cpp:83
+#: gui/options.cpp:2758 gui/options.cpp:2895 audio/fmopl.cpp:112
 msgid "None"
 msgstr "Nenhum"
 
@@ -4160,34 +4160,74 @@ msgstr "OpenGL com shaders"
 msgid "AdLib emulator"
 msgstr "Emulador de AdLib"
 
-#: audio/fmopl.cpp:85
+#: audio/fmopl.cpp:114
 msgid "MAME OPL emulator"
 msgstr "Emulador de MAME OPL"
 
-#: audio/fmopl.cpp:88
+#: audio/fmopl.cpp:117
 msgid "DOSBox OPL emulator"
 msgstr "Emulador de DOSBox OPL"
 
-#: audio/fmopl.cpp:91
+#: audio/fmopl.cpp:120
 msgid "Nuked OPL emulator"
 msgstr "Emulador de Nuked OPL"
 
-#: audio/fmopl.cpp:94
+#: audio/fmopl.cpp:123
 msgid "ALSA Direct FM"
 msgstr "ALSA Direct FM"
 
-#: audio/fmopl.cpp:97
+#: audio/fmopl.cpp:126
 msgid "OPL2LPT"
 msgstr "OPL2LPT"
 
-#: audio/fmopl.cpp:98
+#: audio/fmopl.cpp:127
 msgid "OPL3LPT"
 msgstr "OPL3LPT"
 
-#: audio/fmopl.cpp:101
+#: audio/fmopl.cpp:130
 msgid "RetroWave OPL3"
 msgstr "RetroWave OPL3"
 
+#: audio/fmopl.cpp:133
+msgid "[nFM] NokturnFM2 (OPL2)"
+msgstr ""
+
+#: audio/fmopl.cpp:134
+msgid "[nFM] NokturnFM3 (OPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:135
+msgid "[nFM] RetroWave USB OPL3 Express (OPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:136
+msgid "[nFM] Serdaco OPL2LPT (OPL2)"
+msgstr ""
+
+#: audio/fmopl.cpp:137
+msgid "[nFM] Serdaco OPL3LPT (OPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:138
+msgid "[nFM] Cheerful Electronics OPL2 Audio Board (OPL2)"
+msgstr ""
+
+#: audio/fmopl.cpp:139
+msgid "[nFM] Cheerful Electronics OPL3 Duo! (2xOPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:140
+msgid "[nFM] ST Bus ISA / VME SoundBlaster"
+msgstr ""
+
+#: audio/fmopl.cpp:141
+msgid "[nFM] NatFeats / NULL"
+msgstr ""
+
+#: audio/fmopl.cpp:142
+msgid "[nFM] Nuked-OPL3 softsynth (OPL3)"
+msgstr ""
+
 #: audio/mididrv.cpp:232
 #, c-format
 msgid ""
diff --git a/po/pt_PT.po b/po/pt_PT.po
index 0c488810827..af952290a92 100644
--- a/po/pt_PT.po
+++ b/po/pt_PT.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.10.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-20 16:13+0000\n"
+"POT-Creation-Date: 2026-06-21 13:20+0000\n"
 "PO-Revision-Date: 2022-06-05 14:09+0000\n"
 "Last-Translator: Daniel <supsuper at gmail.com>\n"
 "Language-Team: Portuguese (Portugal) <https://translations.scummvm.org/"
@@ -2412,7 +2412,7 @@ msgid "Debug level:"
 msgstr "Sa??da do depurador"
 
 #. I18N: Debug level -1, no messages
-#: gui/options.cpp:2758 gui/options.cpp:2895 audio/fmopl.cpp:83
+#: gui/options.cpp:2758 gui/options.cpp:2895 audio/fmopl.cpp:112
 msgid "None"
 msgstr "Nenhum"
 
@@ -4025,34 +4025,74 @@ msgstr "OpenGL com shaders"
 msgid "AdLib emulator"
 msgstr "Emulador AdLib"
 
-#: audio/fmopl.cpp:85
+#: audio/fmopl.cpp:114
 msgid "MAME OPL emulator"
 msgstr "Emulador MAME OPL"
 
-#: audio/fmopl.cpp:88
+#: audio/fmopl.cpp:117
 msgid "DOSBox OPL emulator"
 msgstr "Emulador DOSBox OPL"
 
-#: audio/fmopl.cpp:91
+#: audio/fmopl.cpp:120
 msgid "Nuked OPL emulator"
 msgstr "Emulador Nuked OPL"
 
-#: audio/fmopl.cpp:94
+#: audio/fmopl.cpp:123
 msgid "ALSA Direct FM"
 msgstr "ALSA Direct FM"
 
-#: audio/fmopl.cpp:97
+#: audio/fmopl.cpp:126
 msgid "OPL2LPT"
 msgstr "OPL2LPT"
 
-#: audio/fmopl.cpp:98
+#: audio/fmopl.cpp:127
 msgid "OPL3LPT"
 msgstr "OPL3LPT"
 
-#: audio/fmopl.cpp:101
+#: audio/fmopl.cpp:130
 msgid "RetroWave OPL3"
 msgstr "RetroWave OPL3"
 
+#: audio/fmopl.cpp:133
+msgid "[nFM] NokturnFM2 (OPL2)"
+msgstr ""
+
+#: audio/fmopl.cpp:134
+msgid "[nFM] NokturnFM3 (OPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:135
+msgid "[nFM] RetroWave USB OPL3 Express (OPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:136
+msgid "[nFM] Serdaco OPL2LPT (OPL2)"
+msgstr ""
+
+#: audio/fmopl.cpp:137
+msgid "[nFM] Serdaco OPL3LPT (OPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:138
+msgid "[nFM] Cheerful Electronics OPL2 Audio Board (OPL2)"
+msgstr ""
+
+#: audio/fmopl.cpp:139
+msgid "[nFM] Cheerful Electronics OPL3 Duo! (2xOPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:140
+msgid "[nFM] ST Bus ISA / VME SoundBlaster"
+msgstr ""
+
+#: audio/fmopl.cpp:141
+msgid "[nFM] NatFeats / NULL"
+msgstr ""
+
+#: audio/fmopl.cpp:142
+msgid "[nFM] Nuked-OPL3 softsynth (OPL3)"
+msgstr ""
+
 #: audio/mididrv.cpp:232
 #, c-format
 msgid ""
diff --git a/po/ro.po b/po/ro.po
index efc4b74ac47..fe6bc71e218 100644
--- a/po/ro.po
+++ b/po/ro.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.10.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-20 16:13+0000\n"
+"POT-Creation-Date: 2026-06-21 13:20+0000\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: Automatically generated\n"
 "Language-Team: none\n"
@@ -2269,7 +2269,7 @@ msgid "Debug level:"
 msgstr ""
 
 #. I18N: Debug level -1, no messages
-#: gui/options.cpp:2758 gui/options.cpp:2895 audio/fmopl.cpp:83
+#: gui/options.cpp:2758 gui/options.cpp:2895 audio/fmopl.cpp:112
 msgid "None"
 msgstr ""
 
@@ -3746,34 +3746,74 @@ msgstr ""
 msgid "AdLib emulator"
 msgstr ""
 
-#: audio/fmopl.cpp:85
+#: audio/fmopl.cpp:114
 msgid "MAME OPL emulator"
 msgstr ""
 
-#: audio/fmopl.cpp:88
+#: audio/fmopl.cpp:117
 msgid "DOSBox OPL emulator"
 msgstr ""
 
-#: audio/fmopl.cpp:91
+#: audio/fmopl.cpp:120
 msgid "Nuked OPL emulator"
 msgstr ""
 
-#: audio/fmopl.cpp:94
+#: audio/fmopl.cpp:123
 msgid "ALSA Direct FM"
 msgstr ""
 
-#: audio/fmopl.cpp:97
+#: audio/fmopl.cpp:126
 msgid "OPL2LPT"
 msgstr ""
 
-#: audio/fmopl.cpp:98
+#: audio/fmopl.cpp:127
 msgid "OPL3LPT"
 msgstr ""
 
-#: audio/fmopl.cpp:101
+#: audio/fmopl.cpp:130
 msgid "RetroWave OPL3"
 msgstr ""
 
+#: audio/fmopl.cpp:133
+msgid "[nFM] NokturnFM2 (OPL2)"
+msgstr ""
+
+#: audio/fmopl.cpp:134
+msgid "[nFM] NokturnFM3 (OPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:135
+msgid "[nFM] RetroWave USB OPL3 Express (OPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:136
+msgid "[nFM] Serdaco OPL2LPT (OPL2)"
+msgstr ""
+
+#: audio/fmopl.cpp:137
+msgid "[nFM] Serdaco OPL3LPT (OPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:138
+msgid "[nFM] Cheerful Electronics OPL2 Audio Board (OPL2)"
+msgstr ""
+
+#: audio/fmopl.cpp:139
+msgid "[nFM] Cheerful Electronics OPL3 Duo! (2xOPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:140
+msgid "[nFM] ST Bus ISA / VME SoundBlaster"
+msgstr ""
+
+#: audio/fmopl.cpp:141
+msgid "[nFM] NatFeats / NULL"
+msgstr ""
+
+#: audio/fmopl.cpp:142
+msgid "[nFM] Nuked-OPL3 softsynth (OPL3)"
+msgstr ""
+
 #: audio/mididrv.cpp:232
 #, c-format
 msgid ""
diff --git a/po/ru_RU.po b/po/ru_RU.po
index 81e4d635647..cefb6741e62 100644
--- a/po/ru_RU.po
+++ b/po/ru_RU.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.8.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-20 16:13+0000\n"
+"POT-Creation-Date: 2026-06-21 13:20+0000\n"
 "PO-Revision-Date: 2026-06-20 15:03+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Russian <https://translations.scummvm.org/projects/scummvm/"
@@ -2432,7 +2432,7 @@ msgid "Debug level:"
 msgstr "?????????????? ??????????????:"
 
 #. I18N: Debug level -1, no messages
-#: gui/options.cpp:2758 gui/options.cpp:2895 audio/fmopl.cpp:83
+#: gui/options.cpp:2758 gui/options.cpp:2895 audio/fmopl.cpp:112
 msgid "None"
 msgstr "??????"
 
@@ -4059,34 +4059,74 @@ msgstr "OpenGL ?? ??????????????????"
 msgid "AdLib emulator"
 msgstr "???????????????? AdLib"
 
-#: audio/fmopl.cpp:85
+#: audio/fmopl.cpp:114
 msgid "MAME OPL emulator"
 msgstr "???????????????? MAME OPL"
 
-#: audio/fmopl.cpp:88
+#: audio/fmopl.cpp:117
 msgid "DOSBox OPL emulator"
 msgstr "???????????????? DOSBox OPL"
 
-#: audio/fmopl.cpp:91
+#: audio/fmopl.cpp:120
 msgid "Nuked OPL emulator"
 msgstr "???????????????? Nuked OPL"
 
-#: audio/fmopl.cpp:94
+#: audio/fmopl.cpp:123
 msgid "ALSA Direct FM"
 msgstr "???????????? FM ALSA"
 
-#: audio/fmopl.cpp:97
+#: audio/fmopl.cpp:126
 msgid "OPL2LPT"
 msgstr "OPL2LPT"
 
-#: audio/fmopl.cpp:98
+#: audio/fmopl.cpp:127
 msgid "OPL3LPT"
 msgstr "OPL3LPT"
 
-#: audio/fmopl.cpp:101
+#: audio/fmopl.cpp:130
 msgid "RetroWave OPL3"
 msgstr "RetroWave OPL3"
 
+#: audio/fmopl.cpp:133
+msgid "[nFM] NokturnFM2 (OPL2)"
+msgstr ""
+
+#: audio/fmopl.cpp:134
+msgid "[nFM] NokturnFM3 (OPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:135
+msgid "[nFM] RetroWave USB OPL3 Express (OPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:136
+msgid "[nFM] Serdaco OPL2LPT (OPL2)"
+msgstr ""
+
+#: audio/fmopl.cpp:137
+msgid "[nFM] Serdaco OPL3LPT (OPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:138
+msgid "[nFM] Cheerful Electronics OPL2 Audio Board (OPL2)"
+msgstr ""
+
+#: audio/fmopl.cpp:139
+msgid "[nFM] Cheerful Electronics OPL3 Duo! (2xOPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:140
+msgid "[nFM] ST Bus ISA / VME SoundBlaster"
+msgstr ""
+
+#: audio/fmopl.cpp:141
+msgid "[nFM] NatFeats / NULL"
+msgstr ""
+
+#: audio/fmopl.cpp:142
+msgid "[nFM] Nuked-OPL3 softsynth (OPL3)"
+msgstr ""
+
 #: audio/mididrv.cpp:232
 #, c-format
 msgid ""
diff --git a/po/sv_SE.po b/po/sv_SE.po
index bcfe49c93d6..4ac5e6686ca 100644
--- a/po/sv_SE.po
+++ b/po/sv_SE.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.5.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-20 16:13+0000\n"
+"POT-Creation-Date: 2026-06-21 13:20+0000\n"
 "PO-Revision-Date: 2026-04-19 13:22+0000\n"
 "Last-Translator: Daniel Nylander <po at danielnylander.se>\n"
 "Language-Team: Swedish <https://translations.scummvm.org/projects/scummvm/"
@@ -2436,7 +2436,7 @@ msgid "Debug level:"
 msgstr "Fels??kningsniv??:"
 
 #. I18N: Debug level -1, no messages
-#: gui/options.cpp:2758 gui/options.cpp:2895 audio/fmopl.cpp:83
+#: gui/options.cpp:2758 gui/options.cpp:2895 audio/fmopl.cpp:112
 msgid "None"
 msgstr "Ingen"
 
@@ -4052,34 +4052,74 @@ msgstr "OpenGL med shaders"
 msgid "AdLib emulator"
 msgstr "AdLib-emulator"
 
-#: audio/fmopl.cpp:85
+#: audio/fmopl.cpp:114
 msgid "MAME OPL emulator"
 msgstr "MAME OPL-emulator"
 
-#: audio/fmopl.cpp:88
+#: audio/fmopl.cpp:117
 msgid "DOSBox OPL emulator"
 msgstr "DOSBox OPL-emulator"
 
-#: audio/fmopl.cpp:91
+#: audio/fmopl.cpp:120
 msgid "Nuked OPL emulator"
 msgstr "Nuked OPL-emulator"
 
-#: audio/fmopl.cpp:94
+#: audio/fmopl.cpp:123
 msgid "ALSA Direct FM"
 msgstr "ALSA Direct FM"
 
-#: audio/fmopl.cpp:97
+#: audio/fmopl.cpp:126
 msgid "OPL2LPT"
 msgstr "OPL2LPT"
 
-#: audio/fmopl.cpp:98
+#: audio/fmopl.cpp:127
 msgid "OPL3LPT"
 msgstr "OPL3LPT"
 
-#: audio/fmopl.cpp:101
+#: audio/fmopl.cpp:130
 msgid "RetroWave OPL3"
 msgstr "RetroWave OPL3"
 
+#: audio/fmopl.cpp:133
+msgid "[nFM] NokturnFM2 (OPL2)"
+msgstr ""
+
+#: audio/fmopl.cpp:134
+msgid "[nFM] NokturnFM3 (OPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:135
+msgid "[nFM] RetroWave USB OPL3 Express (OPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:136
+msgid "[nFM] Serdaco OPL2LPT (OPL2)"
+msgstr ""
+
+#: audio/fmopl.cpp:137
+msgid "[nFM] Serdaco OPL3LPT (OPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:138
+msgid "[nFM] Cheerful Electronics OPL2 Audio Board (OPL2)"
+msgstr ""
+
+#: audio/fmopl.cpp:139
+msgid "[nFM] Cheerful Electronics OPL3 Duo! (2xOPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:140
+msgid "[nFM] ST Bus ISA / VME SoundBlaster"
+msgstr ""
+
+#: audio/fmopl.cpp:141
+msgid "[nFM] NatFeats / NULL"
+msgstr ""
+
+#: audio/fmopl.cpp:142
+msgid "[nFM] Nuked-OPL3 softsynth (OPL3)"
+msgstr ""
+
 #: audio/mididrv.cpp:232
 #, c-format
 msgid ""
diff --git a/po/tr.po b/po/tr.po
index 27c86ce7c8f..d10893cedc1 100644
--- a/po/tr.po
+++ b/po/tr.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-20 16:13+0000\n"
+"POT-Creation-Date: 2026-06-21 13:20+0000\n"
 "PO-Revision-Date: 2024-10-10 12:33+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Turkish <https://translations.scummvm.org/projects/scummvm/"
@@ -2390,7 +2390,7 @@ msgid "Debug level:"
 msgstr ""
 
 #. I18N: Debug level -1, no messages
-#: gui/options.cpp:2758 gui/options.cpp:2895 audio/fmopl.cpp:83
+#: gui/options.cpp:2758 gui/options.cpp:2895 audio/fmopl.cpp:112
 msgid "None"
 msgstr "Hi??biri"
 
@@ -3999,36 +3999,76 @@ msgstr ""
 msgid "AdLib emulator"
 msgstr "AdLib em??lat??r??:"
 
-#: audio/fmopl.cpp:85
+#: audio/fmopl.cpp:114
 #, fuzzy
 msgid "MAME OPL emulator"
 msgstr "AdLib em??lat??r??:"
 
-#: audio/fmopl.cpp:88
+#: audio/fmopl.cpp:117
 msgid "DOSBox OPL emulator"
 msgstr ""
 
-#: audio/fmopl.cpp:91
+#: audio/fmopl.cpp:120
 #, fuzzy
 msgid "Nuked OPL emulator"
 msgstr "AdLib em??lat??r??:"
 
-#: audio/fmopl.cpp:94
+#: audio/fmopl.cpp:123
 msgid "ALSA Direct FM"
 msgstr ""
 
-#: audio/fmopl.cpp:97
+#: audio/fmopl.cpp:126
 msgid "OPL2LPT"
 msgstr ""
 
-#: audio/fmopl.cpp:98
+#: audio/fmopl.cpp:127
 msgid "OPL3LPT"
 msgstr ""
 
-#: audio/fmopl.cpp:101
+#: audio/fmopl.cpp:130
 msgid "RetroWave OPL3"
 msgstr ""
 
+#: audio/fmopl.cpp:133
+msgid "[nFM] NokturnFM2 (OPL2)"
+msgstr ""
+
+#: audio/fmopl.cpp:134
+msgid "[nFM] NokturnFM3 (OPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:135
+msgid "[nFM] RetroWave USB OPL3 Express (OPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:136
+msgid "[nFM] Serdaco OPL2LPT (OPL2)"
+msgstr ""
+
+#: audio/fmopl.cpp:137
+msgid "[nFM] Serdaco OPL3LPT (OPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:138
+msgid "[nFM] Cheerful Electronics OPL2 Audio Board (OPL2)"
+msgstr ""
+
+#: audio/fmopl.cpp:139
+msgid "[nFM] Cheerful Electronics OPL3 Duo! (2xOPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:140
+msgid "[nFM] ST Bus ISA / VME SoundBlaster"
+msgstr ""
+
+#: audio/fmopl.cpp:141
+msgid "[nFM] NatFeats / NULL"
+msgstr ""
+
+#: audio/fmopl.cpp:142
+msgid "[nFM] Nuked-OPL3 softsynth (OPL3)"
+msgstr ""
+
 #: audio/mididrv.cpp:232
 #, c-format
 msgid ""
diff --git a/po/uk_UA.po b/po/uk_UA.po
index b4479d80a1d..7c065ddc21e 100644
--- a/po/uk_UA.po
+++ b/po/uk_UA.po
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.9.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-20 16:13+0000\n"
+"POT-Creation-Date: 2026-06-21 13:20+0000\n"
 "PO-Revision-Date: 2026-06-20 14:56+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Ukrainian <https://translations.scummvm.org/projects/scummvm/"
@@ -2430,7 +2430,7 @@ msgid "Debug level:"
 msgstr "???????????? ????????????????:"
 
 #. I18N: Debug level -1, no messages
-#: gui/options.cpp:2758 gui/options.cpp:2895 audio/fmopl.cpp:83
+#: gui/options.cpp:2758 gui/options.cpp:2895 audio/fmopl.cpp:112
 msgid "None"
 msgstr "???? ??????????????"
 
@@ -4052,34 +4052,74 @@ msgstr "OpenGL ?? ??????????????????"
 msgid "AdLib emulator"
 msgstr "???????????????? AdLib"
 
-#: audio/fmopl.cpp:85
+#: audio/fmopl.cpp:114
 msgid "MAME OPL emulator"
 msgstr "???????????????? MAME OPL"
 
-#: audio/fmopl.cpp:88
+#: audio/fmopl.cpp:117
 msgid "DOSBox OPL emulator"
 msgstr "???????????????? DOSBox OPL"
 
-#: audio/fmopl.cpp:91
+#: audio/fmopl.cpp:120
 msgid "Nuked OPL emulator"
 msgstr "???????????????? Nuked OPL"
 
-#: audio/fmopl.cpp:94
+#: audio/fmopl.cpp:123
 msgid "ALSA Direct FM"
 msgstr "?????????????????????????? ALSA FM"
 
-#: audio/fmopl.cpp:97
+#: audio/fmopl.cpp:126
 msgid "OPL2LPT"
 msgstr "OPL2LPT"
 
-#: audio/fmopl.cpp:98
+#: audio/fmopl.cpp:127
 msgid "OPL3LPT"
 msgstr "OPL3LPT"
 
-#: audio/fmopl.cpp:101
+#: audio/fmopl.cpp:130
 msgid "RetroWave OPL3"
 msgstr "RetroWave OPL3"
 
+#: audio/fmopl.cpp:133
+msgid "[nFM] NokturnFM2 (OPL2)"
+msgstr ""
+
+#: audio/fmopl.cpp:134
+msgid "[nFM] NokturnFM3 (OPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:135
+msgid "[nFM] RetroWave USB OPL3 Express (OPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:136
+msgid "[nFM] Serdaco OPL2LPT (OPL2)"
+msgstr ""
+
+#: audio/fmopl.cpp:137
+msgid "[nFM] Serdaco OPL3LPT (OPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:138
+msgid "[nFM] Cheerful Electronics OPL2 Audio Board (OPL2)"
+msgstr ""
+
+#: audio/fmopl.cpp:139
+msgid "[nFM] Cheerful Electronics OPL3 Duo! (2xOPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:140
+msgid "[nFM] ST Bus ISA / VME SoundBlaster"
+msgstr ""
+
+#: audio/fmopl.cpp:141
+msgid "[nFM] NatFeats / NULL"
+msgstr ""
+
+#: audio/fmopl.cpp:142
+msgid "[nFM] Nuked-OPL3 softsynth (OPL3)"
+msgstr ""
+
 #: audio/mididrv.cpp:232
 #, c-format
 msgid ""
diff --git a/po/zh.po b/po/zh.po
index 543f71af138..ac9cf7bb5c0 100644
--- a/po/zh.po
+++ b/po/zh.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-20 16:13+0000\n"
+"POT-Creation-Date: 2026-06-21 13:20+0000\n"
 "PO-Revision-Date: 2024-05-14 12:42+0000\n"
 "Last-Translator: ?????? <clonewith at qq.com>\n"
 "Language-Team: Chinese <https://translations.scummvm.org/projects/scummvm/"
@@ -2298,7 +2298,7 @@ msgid "Debug level:"
 msgstr ""
 
 #. I18N: Debug level -1, no messages
-#: gui/options.cpp:2758 gui/options.cpp:2895 audio/fmopl.cpp:83
+#: gui/options.cpp:2758 gui/options.cpp:2895 audio/fmopl.cpp:112
 msgid "None"
 msgstr ""
 
@@ -3811,34 +3811,74 @@ msgstr ""
 msgid "AdLib emulator"
 msgstr ""
 
-#: audio/fmopl.cpp:85
+#: audio/fmopl.cpp:114
 msgid "MAME OPL emulator"
 msgstr ""
 
-#: audio/fmopl.cpp:88
+#: audio/fmopl.cpp:117
 msgid "DOSBox OPL emulator"
 msgstr ""
 
-#: audio/fmopl.cpp:91
+#: audio/fmopl.cpp:120
 msgid "Nuked OPL emulator"
 msgstr ""
 
-#: audio/fmopl.cpp:94
+#: audio/fmopl.cpp:123
 msgid "ALSA Direct FM"
 msgstr ""
 
-#: audio/fmopl.cpp:97
+#: audio/fmopl.cpp:126
 msgid "OPL2LPT"
 msgstr ""
 
-#: audio/fmopl.cpp:98
+#: audio/fmopl.cpp:127
 msgid "OPL3LPT"
 msgstr ""
 
-#: audio/fmopl.cpp:101
+#: audio/fmopl.cpp:130
 msgid "RetroWave OPL3"
 msgstr ""
 
+#: audio/fmopl.cpp:133
+msgid "[nFM] NokturnFM2 (OPL2)"
+msgstr ""
+
+#: audio/fmopl.cpp:134
+msgid "[nFM] NokturnFM3 (OPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:135
+msgid "[nFM] RetroWave USB OPL3 Express (OPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:136
+msgid "[nFM] Serdaco OPL2LPT (OPL2)"
+msgstr ""
+
+#: audio/fmopl.cpp:137
+msgid "[nFM] Serdaco OPL3LPT (OPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:138
+msgid "[nFM] Cheerful Electronics OPL2 Audio Board (OPL2)"
+msgstr ""
+
+#: audio/fmopl.cpp:139
+msgid "[nFM] Cheerful Electronics OPL3 Duo! (2xOPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:140
+msgid "[nFM] ST Bus ISA / VME SoundBlaster"
+msgstr ""
+
+#: audio/fmopl.cpp:141
+msgid "[nFM] NatFeats / NULL"
+msgstr ""
+
+#: audio/fmopl.cpp:142
+msgid "[nFM] Nuked-OPL3 softsynth (OPL3)"
+msgstr ""
+
 #: audio/mididrv.cpp:232
 #, c-format
 msgid ""
diff --git a/po/zh_Hans.po b/po/zh_Hans.po
index 4d7687dfb2b..7e0c599cd96 100644
--- a/po/zh_Hans.po
+++ b/po/zh_Hans.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.9.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-20 16:13+0000\n"
+"POT-Creation-Date: 2026-06-21 13:20+0000\n"
 "PO-Revision-Date: 2024-12-01 22:18+0000\n"
 "Last-Translator: ?????? <clonewith at qq.com>\n"
 "Language-Team: Chinese (Simplified Han script) <https://"
@@ -2432,7 +2432,7 @@ msgid "Debug level:"
 msgstr "???????????????"
 
 #. I18N: Debug level -1, no messages
-#: gui/options.cpp:2758 gui/options.cpp:2895 audio/fmopl.cpp:83
+#: gui/options.cpp:2758 gui/options.cpp:2895 audio/fmopl.cpp:112
 msgid "None"
 msgstr "???"
 
@@ -4020,34 +4020,74 @@ msgstr "?????????????????? OpenGL"
 msgid "AdLib emulator"
 msgstr "AdLib ?????????"
 
-#: audio/fmopl.cpp:85
+#: audio/fmopl.cpp:114
 msgid "MAME OPL emulator"
 msgstr "MAME OPL ?????????"
 
-#: audio/fmopl.cpp:88
+#: audio/fmopl.cpp:117
 msgid "DOSBox OPL emulator"
 msgstr "DOSBox OPL ?????????"
 
-#: audio/fmopl.cpp:91
+#: audio/fmopl.cpp:120
 msgid "Nuked OPL emulator"
 msgstr "Nuked OPL ?????????"
 
-#: audio/fmopl.cpp:94
+#: audio/fmopl.cpp:123
 msgid "ALSA Direct FM"
 msgstr "ALSA Direct FM"
 
-#: audio/fmopl.cpp:97
+#: audio/fmopl.cpp:126
 msgid "OPL2LPT"
 msgstr "OPL2LPT"
 
-#: audio/fmopl.cpp:98
+#: audio/fmopl.cpp:127
 msgid "OPL3LPT"
 msgstr "OPL3LPT"
 
-#: audio/fmopl.cpp:101
+#: audio/fmopl.cpp:130
 msgid "RetroWave OPL3"
 msgstr "RetroWave OPL3"
 
+#: audio/fmopl.cpp:133
+msgid "[nFM] NokturnFM2 (OPL2)"
+msgstr ""
+
+#: audio/fmopl.cpp:134
+msgid "[nFM] NokturnFM3 (OPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:135
+msgid "[nFM] RetroWave USB OPL3 Express (OPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:136
+msgid "[nFM] Serdaco OPL2LPT (OPL2)"
+msgstr ""
+
+#: audio/fmopl.cpp:137
+msgid "[nFM] Serdaco OPL3LPT (OPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:138
+msgid "[nFM] Cheerful Electronics OPL2 Audio Board (OPL2)"
+msgstr ""
+
+#: audio/fmopl.cpp:139
+msgid "[nFM] Cheerful Electronics OPL3 Duo! (2xOPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:140
+msgid "[nFM] ST Bus ISA / VME SoundBlaster"
+msgstr ""
+
+#: audio/fmopl.cpp:141
+msgid "[nFM] NatFeats / NULL"
+msgstr ""
+
+#: audio/fmopl.cpp:142
+msgid "[nFM] Nuked-OPL3 softsynth (OPL3)"
+msgstr ""
+
 #: audio/mididrv.cpp:232
 #, c-format
 msgid ""
diff --git a/po/zh_Hant.po b/po/zh_Hant.po
index 293a689662e..ffdb1d3ec5f 100644
--- a/po/zh_Hant.po
+++ b/po/zh_Hant.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.9.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-20 16:13+0000\n"
+"POT-Creation-Date: 2026-06-21 13:20+0000\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: Automatically generated\n"
 "Language-Team: none\n"
@@ -2270,7 +2270,7 @@ msgid "Debug level:"
 msgstr ""
 
 #. I18N: Debug level -1, no messages
-#: gui/options.cpp:2758 gui/options.cpp:2895 audio/fmopl.cpp:83
+#: gui/options.cpp:2758 gui/options.cpp:2895 audio/fmopl.cpp:112
 msgid "None"
 msgstr ""
 
@@ -3747,34 +3747,74 @@ msgstr ""
 msgid "AdLib emulator"
 msgstr ""
 
-#: audio/fmopl.cpp:85
+#: audio/fmopl.cpp:114
 msgid "MAME OPL emulator"
 msgstr ""
 
-#: audio/fmopl.cpp:88
+#: audio/fmopl.cpp:117
 msgid "DOSBox OPL emulator"
 msgstr ""
 
-#: audio/fmopl.cpp:91
+#: audio/fmopl.cpp:120
 msgid "Nuked OPL emulator"
 msgstr ""
 
-#: audio/fmopl.cpp:94
+#: audio/fmopl.cpp:123
 msgid "ALSA Direct FM"
 msgstr ""
 
-#: audio/fmopl.cpp:97
+#: audio/fmopl.cpp:126
 msgid "OPL2LPT"
 msgstr ""
 
-#: audio/fmopl.cpp:98
+#: audio/fmopl.cpp:127
 msgid "OPL3LPT"
 msgstr ""
 
-#: audio/fmopl.cpp:101
+#: audio/fmopl.cpp:130
 msgid "RetroWave OPL3"
 msgstr ""
 
+#: audio/fmopl.cpp:133
+msgid "[nFM] NokturnFM2 (OPL2)"
+msgstr ""
+
+#: audio/fmopl.cpp:134
+msgid "[nFM] NokturnFM3 (OPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:135
+msgid "[nFM] RetroWave USB OPL3 Express (OPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:136
+msgid "[nFM] Serdaco OPL2LPT (OPL2)"
+msgstr ""
+
+#: audio/fmopl.cpp:137
+msgid "[nFM] Serdaco OPL3LPT (OPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:138
+msgid "[nFM] Cheerful Electronics OPL2 Audio Board (OPL2)"
+msgstr ""
+
+#: audio/fmopl.cpp:139
+msgid "[nFM] Cheerful Electronics OPL3 Duo! (2xOPL3)"
+msgstr ""
+
+#: audio/fmopl.cpp:140
+msgid "[nFM] ST Bus ISA / VME SoundBlaster"
+msgstr ""
+
+#: audio/fmopl.cpp:141
+msgid "[nFM] NatFeats / NULL"
+msgstr ""
+
+#: audio/fmopl.cpp:142
+msgid "[nFM] Nuked-OPL3 softsynth (OPL3)"
+msgstr ""
+
 #: audio/mididrv.cpp:232
 #, c-format
 msgid ""



From noreply at scummvm.org  Sun Jun 21 13:24:15 2026
From: noreply at scummvm.org (sev-)
Date: Sun, 21 Jun 2026 13:24:15 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 16cf5edf007726ef5771d5585b14e32e61e6cf07
Message-ID: <20260621132415.0D27E7005CE@ron.scummvm.net>

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

Summary:
16cf5edf00 NEWS: Mention newly merged functionality


Commit: 16cf5edf007726ef5771d5585b14e32e61e6cf07
    https://github.com/scummvm/scummvm/commit/16cf5edf007726ef5771d5585b14e32e61e6cf07
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2026-06-21T15:24:06+02:00

Commit Message:
NEWS: Mention newly merged functionality

Changed paths:
    NEWS.md


diff --git a/NEWS.md b/NEWS.md
index b9e6f3d1cd8..feaca0e7c33 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -1,6 +1,14 @@
 For a more comprehensive changelog of the latest experimental code, see:
         https://github.com/scummvm/scummvm/commits/
 
+#### 2026.4.0 "Carousels & Killer Whales" (2026-10-XX)
+ General:
+   - Optimised mixing and rate converters, for better performance.
+
+ Atari port:
+   - Added integration with nFM library.
+
+
 #### 2026.3.0 "Carousels & Killer Whales" (2026-06-20)
 
  New games:
@@ -69,7 +77,7 @@ For a more comprehensive changelog of the latest experimental code, see:
    - Fixed holomap model rendering.
    - Disable autosaves while driving a vehicle.
 
-Atari port:
+ Atari port:
    - Added native CDDA support.
    - Added plugin support (now ScummVM can run with as little as a few megs of RAM).
    - Added native YM2149 support (currently usable for Elvira 1 and 2).



From noreply at scummvm.org  Sun Jun 21 16:24:44 2026
From: noreply at scummvm.org (neuromancer)
Date: Sun, 21 Jun 2026 16:24:44 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 afe64b518cdd6e9178ec8ae668e24f722cce390f
Message-ID: <20260621162444.4ACFC7005CE@ron.scummvm.net>

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

Summary:
3d77964014 SCUMM: RA2: better gamepad navigation in menus, similar to RA1
1ef1e0e2e7 SCUMM: RA2: simplified gamepad controls, no more return to center
b8caec28c0 SCUMM: RA2: gamepad fixes for handler 7
2bd3cf279f SCUMM: RA2: perspective/damage fixes for handler 7
8c086c04d6 SCUMM: RA2: use B to go back/skip cutscenes
5fc31be42d SCUMM: RA2: clean-up of comments for INSANE code
afe64b518c SCUMM: RA1: clean-up of comments for INSANE code


Commit: 3d77964014a1ce401b7f96eb7aadd5c45cc5187e
    https://github.com/scummvm/scummvm/commit/3d77964014a1ce401b7f96eb7aadd5c45cc5187e
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-21T18:24:30+02:00

Commit Message:
SCUMM: RA2: better gamepad navigation in menus, similar to RA1

Changed paths:
    engines/scumm/insane/rebel2/menu.cpp
    engines/scumm/insane/rebel2/rebel.cpp
    engines/scumm/insane/rebel2/rebel.h


diff --git a/engines/scumm/insane/rebel2/menu.cpp b/engines/scumm/insane/rebel2/menu.cpp
index 0ada7d95441..08ece9305ec 100644
--- a/engines/scumm/insane/rebel2/menu.cpp
+++ b/engines/scumm/insane/rebel2/menu.cpp
@@ -57,6 +57,7 @@ void InsaneRebel2::resetMenu() {
 	_menuInactivityTimer = 0;
 	_menuInactivityTimedOut = false;
 	_menuRepeatDelay = 0;
+	resetMenuGamepadAxis();
 	_menuSelectionConfirmed = false;
 	setVirtualKeyboardVisible(false);
 }
@@ -706,6 +707,7 @@ int InsaneRebel2::runMainMenu() {
 			splayer->play("OPEN/O_CREDIT.SAN", 15);
 			_gameState = kStateMainMenu;
 			_menuInputActive = true;
+			resetMenuGamepadAxis();
 			// Returns 1 in original -> stays at stage 1 (main menu)
 			break;
 
@@ -738,6 +740,7 @@ int InsaneRebel2::runChapterSelect() {
 	_menuInputActive = true;
 	while (!_menuEventQueue.empty())
 		_menuEventQueue.pop();
+	resetMenuGamepadAxis();
 
 	// Initialize chapter selection state
 	// Original (lines 51-54): local_10 = 0xf; while (local_10 > 0 && locked) local_10--;
@@ -1234,6 +1237,7 @@ int InsaneRebel2::runLevelSelect() {
 	_menuInputActive = true;
 	while (!_menuEventQueue.empty())
 		_menuEventQueue.pop();
+	resetMenuGamepadAxis();
 
 	_levelSelection = 0;
 	_levelItemCount = _numPilots + 4;  // N pilots + NEW/COPY/DELETE/MAIN MENU
@@ -1284,6 +1288,7 @@ int InsaneRebel2::runLevelSelect() {
 			_pilotMenuMode = kPilotModeSelect;
 			_levelItemCount = _numPilots + 4;
 			_gameState = kStatePilotSelect;
+			resetMenuGamepadAxis();
 			_menuInputActive = false;
 			return kLevelSelectPlay;
 		}
@@ -1299,6 +1304,7 @@ int InsaneRebel2::runLevelSelect() {
 			_pilotMenuMode = kPilotModeDifficulty;
 			_gameState = kStateDifficultySelect;
 			_difficultySelection = 2;
+			resetMenuGamepadAxis();
 			continue;
 		}
 
@@ -1325,6 +1331,7 @@ int InsaneRebel2::runLevelSelect() {
 			_pilotMenuMode = kPilotModeSelect;
 			_levelItemCount = _numPilots + 4;
 			_gameState = kStatePilotSelect;
+			resetMenuGamepadAxis();
 			continue;
 		}
 
@@ -1355,6 +1362,7 @@ int InsaneRebel2::runLevelSelect() {
 				_pilotNameInput = "";
 				_pilotMenuMode = kPilotModeNameInput;
 				_levelItemCount = _numPilots + 4;
+				resetMenuGamepadAxis();
 				updateMenuVirtualKeyboard();
 				debugC(DEBUG_INSANE, "NEW PILOT - entering name for slot %d", newIdx);
 			}
@@ -1366,6 +1374,7 @@ int InsaneRebel2::runLevelSelect() {
 				_pilotMenuMode = kPilotModeCopySelect;
 				_levelSelection = 0;
 				_levelItemCount = _numPilots;
+				resetMenuGamepadAxis();
 				debugC(DEBUG_INSANE, "COPY PILOT - selecting source");
 			}
 			continue;
@@ -1376,6 +1385,7 @@ int InsaneRebel2::runLevelSelect() {
 				_pilotMenuMode = kPilotModeDeleteSelect;
 				_levelSelection = 0;
 				_levelItemCount = _numPilots;
+				resetMenuGamepadAxis();
 				debugC(DEBUG_INSANE, "DELETE PILOT - selecting target");
 			}
 			continue;
@@ -1422,6 +1432,7 @@ int InsaneRebel2::processLevelSelectInput() {
 					}
 					_pilotMenuMode = kPilotModeSelect;
 					_levelItemCount = _numPilots + 4;
+					resetMenuGamepadAxis();
 					updateMenuVirtualKeyboard();
 					debugC(DEBUG_INSANE, "PilotName: cancelled");
 				} else if (event.kbd.keycode == Common::KEYCODE_BACKSPACE) {
@@ -1447,6 +1458,7 @@ int InsaneRebel2::processLevelSelectInput() {
 				}
 				_pilotMenuMode = kPilotModeSelect;
 				_levelItemCount = _numPilots + 4;
+				resetMenuGamepadAxis();
 				updateMenuVirtualKeyboard();
 			}
 		}
@@ -1497,11 +1509,13 @@ int InsaneRebel2::processLevelSelectInput() {
 			case Common::KEYCODE_ESCAPE:
 				if (isDifficultyMode) {
 					_gameState = kStatePilotSelect;
+					resetMenuGamepadAxis();
 				} else if (isPilotOperationMode) {
 					bool wasCopyMode = (_pilotMenuMode == kPilotModeCopySelect);
 					_pilotMenuMode = kPilotModeSelect;
 					_levelItemCount = _numPilots + 4;
 					_levelSelection = _numPilots + (wasCopyMode ? 1 : 2);
+					resetMenuGamepadAxis();
 				} else {
 					result = _levelItemCount - 1;  // Last item = MAIN MENU
 				}
@@ -1542,10 +1556,12 @@ int InsaneRebel2::processLevelSelectInput() {
 		case Common::EVENT_RETURN_TO_LAUNCHER:
 			if (isDifficultyMode) {
 				_gameState = kStatePilotSelect;
+				resetMenuGamepadAxis();
 			} else if (isPilotOperationMode) {
 				_pilotMenuMode = kPilotModeSelect;
 				_levelItemCount = _numPilots + 4;
 				_levelSelection = _levelItemCount - 1;
+				resetMenuGamepadAxis();
 				result = _levelSelection;
 			} else {
 				result = _levelItemCount - 1;
@@ -1743,6 +1759,7 @@ void InsaneRebel2::showTopPilots() {
 	_menuInputActive = true;
 	while (!_menuEventQueue.empty())
 		_menuEventQueue.pop();
+	resetMenuGamepadAxis();
 
 	// param_1 = -1 from main menu: maxFrames = 120 (0x78)
 	_topPilotsMaxFrames = 120;
@@ -1760,6 +1777,7 @@ void InsaneRebel2::showTopPilots() {
 
 	_gameState = kStateMainMenu;
 	_menuInputActive = true;
+	resetMenuGamepadAxis();
 
 	debugC(DEBUG_INSANE, "Top Pilots screen finished");
 }
@@ -1850,6 +1868,7 @@ void InsaneRebel2::showOptionsMenu() {
 	_menuInputActive = true;
 	while (!_menuEventQueue.empty())
 		_menuEventQueue.pop();
+	resetMenuGamepadAxis();
 
 	_optionsSelection = 0;
 	_optionsItemCount = 8;
@@ -1870,6 +1889,7 @@ void InsaneRebel2::showOptionsMenu() {
 
 	_gameState = kStateMainMenu;
 	_menuInputActive = true;
+	resetMenuGamepadAxis();
 
 	debugC(DEBUG_INSANE, "Options menu finished");
 }
diff --git a/engines/scumm/insane/rebel2/rebel.cpp b/engines/scumm/insane/rebel2/rebel.cpp
index 02f9442f926..5c307511965 100644
--- a/engines/scumm/insane/rebel2/rebel.cpp
+++ b/engines/scumm/insane/rebel2/rebel.cpp
@@ -51,9 +51,11 @@
 
 namespace Scumm {
 
-const int kRA2MenuAxisThreshold = Common::JOYAXIS_MAX / 5;
+const int kRA2MenuAxisThreshold = Common::JOYAXIS_MAX / 2;
 const uint32 kRA2MenuGamepadNavigationDebounceMs = 250;
 const uint32 kRA2MenuGamepadMouseSuppressMs = 250;
+const int kRA2MenuKeyLikeFirstAxis = 8;
+const int kRA2MenuKeyLikeAxisTolerance = Common::JOYAXIS_MAX / 14;
 const int kRA2Handler7MouseSettleJumpThreshold = 40;
 const int kRA2Handler7MouseSettleRelativeThreshold = 40;
 const int kRA2Handler7MouseSettleEdgeMargin = 16;
@@ -65,35 +67,70 @@ bool rebel2UsesRelativeGamepadAim(int selectedLevel) {
 	return selectedLevel == 1 || selectedLevel == 5 || selectedLevel == 14;
 }
 
-bool isRebel2RawMenuAxis(int axis) {
-	return axis == Common::JOYSTICK_AXIS_LEFT_STICK_X ||
-	       axis == Common::JOYSTICK_AXIS_LEFT_STICK_Y ||
-	       axis == Common::JOYSTICK_AXIS_RIGHT_STICK_X ||
-	       axis == Common::JOYSTICK_AXIS_RIGHT_STICK_Y ||
-	       axis == Common::JOYSTICK_AXIS_HAT_X ||
-	       axis == Common::JOYSTICK_AXIS_HAT_Y ||
-	       axis >= 8;
+int16 normalizeRebel2AxisMagnitude(int16 position) {
+	return position == Common::JOYAXIS_MIN ? Common::JOYAXIS_MAX : ABS((int)position);
 }
 
-Common::KeyCode getRebel2RawMenuAxisKey(int axis, int16 position) {
-	const bool horizontalAxis = axis == Common::JOYSTICK_AXIS_LEFT_STICK_X ||
-	                            axis == Common::JOYSTICK_AXIS_RIGHT_STICK_X ||
-	                            axis == Common::JOYSTICK_AXIS_HAT_X ||
-	                            (axis >= 8 && (axis % 2) == 0);
-	const bool verticalAxis = axis == Common::JOYSTICK_AXIS_LEFT_STICK_Y ||
-	                          axis == Common::JOYSTICK_AXIS_RIGHT_STICK_Y ||
-	                          axis == Common::JOYSTICK_AXIS_HAT_Y ||
-	                          (axis >= 8 && (axis % 2) != 0);
-
-	if (!horizontalAxis && !verticalAxis)
-		return Common::KEYCODE_INVALID;
-
-	if (position >= kRA2MenuAxisThreshold)
-		return horizontalAxis ? Common::KEYCODE_RIGHT : Common::KEYCODE_DOWN;
-	if (position <= -kRA2MenuAxisThreshold)
-		return horizontalAxis ? Common::KEYCODE_LEFT : Common::KEYCODE_UP;
-
-	return Common::KEYCODE_INVALID;
+int16 combineRebel2MenuAxis(int16 mappedAxis, int16 rawAxis) {
+	return ABS((int)rawAxis) > ABS((int)mappedAxis) ? rawAxis : mappedAxis;
+}
+
+int getRebel2MenuAxisDirection(int16 axisValue) {
+	if (axisValue >= kRA2MenuAxisThreshold)
+		return 1;
+	if (axisValue <= -kRA2MenuAxisThreshold)
+		return -1;
+	return 0;
+}
+
+bool decodeRebel2MenuKeyLikeAxis(int axis, int16 position, int16 &axisX, int16 &axisY) {
+	if (axis < kRA2MenuKeyLikeFirstAxis)
+		return false;
+
+	axisX = 0;
+	axisY = 0;
+
+	// Some backends expose a key-like POV as a single extra axis whose neutral
+	// value is beyond the normal range and arrives clamped to JOYAXIS_MAX.
+	if (position == 0 || position >= Common::JOYAXIS_MAX - kRA2MenuKeyLikeAxisTolerance)
+		return true;
+
+	static const int16 povValues[] = {
+		-Common::JOYAXIS_MAX,
+		-(Common::JOYAXIS_MAX * 5) / 7,
+		-(Common::JOYAXIS_MAX * 3) / 7,
+		-Common::JOYAXIS_MAX / 7,
+		Common::JOYAXIS_MAX / 7,
+		(Common::JOYAXIS_MAX * 3) / 7,
+		(Common::JOYAXIS_MAX * 5) / 7
+	};
+	static const int16 povDirections[][2] = {
+		{ 0, -Common::JOYAXIS_MAX },
+		{ Common::JOYAXIS_MAX, -Common::JOYAXIS_MAX },
+		{ Common::JOYAXIS_MAX, 0 },
+		{ Common::JOYAXIS_MAX, Common::JOYAXIS_MAX },
+		{ 0, Common::JOYAXIS_MAX },
+		{ -Common::JOYAXIS_MAX, Common::JOYAXIS_MAX },
+		{ -Common::JOYAXIS_MAX, 0 }
+	};
+
+	int bestIndex = -1;
+	int bestDistance = Common::JOYAXIS_MAX;
+	for (uint i = 0; i < ARRAYSIZE(povValues); i++) {
+		const int distance = ABS((int)position - povValues[i]);
+		if (distance < bestDistance) {
+			bestDistance = distance;
+			bestIndex = i;
+		}
+	}
+
+	if (bestIndex >= 0 && bestDistance <= kRA2MenuKeyLikeAxisTolerance) {
+		axisX = povDirections[bestIndex][0];
+		axisY = povDirections[bestIndex][1];
+		return true;
+	}
+
+	return false;
 }
 
 bool isRebel2MenuDirectionKey(Common::KeyCode keycode) {
@@ -574,6 +611,11 @@ InsaneRebel2::InsaneRebel2(ScummEngine_v7 *scumm) {
 	_gameplayMouseSettleUntil = 0;
 	_lastGameplayMenuCloseTime = 0;
 	_lastMenuGamepadNavigationTime = 0;
+	_menuGamepadAxisX = 0;
+	_menuGamepadAxisY = 0;
+	_menuGamepadRawAxis = -1;
+	_menuGamepadRawAxisX = 0;
+	_menuGamepadRawAxisY = 0;
 	_handler8HudGlyph = '#';
 	_handler8HudMessageTimer = 0;
 	_handler8HudMessageIndex = 0;
@@ -680,6 +722,105 @@ void InsaneRebel2::restoreIOSGamepadController() {
 	_iosGamepadControllerState.restore();
 }
 
+void InsaneRebel2::resetMenuGamepadAxis() {
+	_menuGamepadAxisX = 0;
+	_menuGamepadAxisY = 0;
+	_menuGamepadRawAxis = -1;
+	_menuGamepadRawAxisX = 0;
+	_menuGamepadRawAxisY = 0;
+}
+
+void InsaneRebel2::queueMenuGamepadAxisKey(Common::KeyCode keycode) {
+	Common::Event syntheticEvent = Common::Event();
+	syntheticEvent.type = Common::EVENT_KEYDOWN;
+	syntheticEvent.kbd.keycode = keycode;
+	_menuEventQueue.push(syntheticEvent);
+
+	_lastMenuGamepadNavigationTime = _vm->_system->getMillis();
+}
+
+void InsaneRebel2::updateMenuGamepadAxisKey(int16 oldAxisX, int16 oldAxisY) {
+	const int16 axisX = combineRebel2MenuAxis(_menuGamepadAxisX, _menuGamepadRawAxisX);
+	const int16 axisY = combineRebel2MenuAxis(_menuGamepadAxisY, _menuGamepadRawAxisY);
+	const int oldX = getRebel2MenuAxisDirection(oldAxisX);
+	const int oldY = getRebel2MenuAxisDirection(oldAxisY);
+	const int newX = getRebel2MenuAxisDirection(axisX);
+	const int newY = getRebel2MenuAxisDirection(axisY);
+
+	if (newY != oldY && newY != 0)
+		queueMenuGamepadAxisKey(newY > 0 ? Common::KEYCODE_DOWN : Common::KEYCODE_UP);
+	else if (newX != oldX && newX != 0)
+		queueMenuGamepadAxisKey(newX > 0 ? Common::KEYCODE_RIGHT : Common::KEYCODE_LEFT);
+}
+
+bool InsaneRebel2::handleMenuGamepadAxisEvent(const Common::Event &event) {
+	if (event.type != Common::EVENT_CUSTOM_BACKEND_ACTION_AXIS)
+		return false;
+
+	const int16 oldAxisX = combineRebel2MenuAxis(_menuGamepadAxisX, _menuGamepadRawAxisX);
+	const int16 oldAxisY = combineRebel2MenuAxis(_menuGamepadAxisY, _menuGamepadRawAxisY);
+	const int16 axisPosition = normalizeRebel2AxisMagnitude(event.joystick.position);
+	const bool pressed = axisPosition >= kRA2MenuAxisThreshold;
+
+	switch (event.customType) {
+	case kScummBackendActionRebel2AxisUp:
+		if (pressed)
+			_menuGamepadAxisY = -axisPosition;
+		else
+			_menuGamepadAxisY = 0;
+		break;
+
+	case kScummBackendActionRebel2AxisDown:
+		if (pressed)
+			_menuGamepadAxisY = axisPosition;
+		else
+			_menuGamepadAxisY = 0;
+		break;
+
+	case kScummBackendActionRebel2AxisLeft:
+		if (pressed)
+			_menuGamepadAxisX = -axisPosition;
+		else
+			_menuGamepadAxisX = 0;
+		break;
+
+	case kScummBackendActionRebel2AxisRight:
+		if (pressed)
+			_menuGamepadAxisX = axisPosition;
+		else
+			_menuGamepadAxisX = 0;
+		break;
+
+	default:
+		return false;
+	}
+
+	updateMenuGamepadAxisKey(oldAxisX, oldAxisY);
+	return true;
+}
+
+bool InsaneRebel2::handleMenuRawJoystickAxisEvent(const Common::Event &event) {
+	if (event.type != Common::EVENT_JOYAXIS_MOTION)
+		return false;
+
+	int16 axisX = 0;
+	int16 axisY = 0;
+	if (!decodeRebel2MenuKeyLikeAxis(event.joystick.axis, event.joystick.position, axisX, axisY))
+		return false;
+
+	const bool active = axisX != 0 || axisY != 0;
+	if (!active && _menuGamepadRawAxis != event.joystick.axis && _menuGamepadRawAxis != -1)
+		return false;
+
+	const int16 oldAxisX = combineRebel2MenuAxis(_menuGamepadAxisX, _menuGamepadRawAxisX);
+	const int16 oldAxisY = combineRebel2MenuAxis(_menuGamepadAxisY, _menuGamepadRawAxisY);
+	_menuGamepadRawAxis = active ? event.joystick.axis : -1;
+	_menuGamepadRawAxisX = axisX;
+	_menuGamepadRawAxisY = axisY;
+	updateMenuGamepadAxisKey(oldAxisX, oldAxisY);
+	return true;
+}
+
 // 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.
@@ -788,26 +929,8 @@ bool InsaneRebel2::notifyEvent(const Common::Event &event) {
 	}
 
 	if (_menuInputActive && event.type == Common::EVENT_JOYAXIS_MOTION) {
-		const int axis = event.joystick.axis;
-
-		if (!isRebel2RawMenuAxis(axis))
-			return false;
-
-		const Common::KeyCode keycode = getRebel2RawMenuAxisKey(axis, event.joystick.position);
-		if (keycode == Common::KEYCODE_INVALID)
-			return true;
-
-		const uint32 now = _vm->_system->getMillis();
-		if (_lastMenuGamepadNavigationTime != 0 &&
-				now - _lastMenuGamepadNavigationTime < kRA2MenuGamepadNavigationDebounceMs)
+		if (handleMenuRawJoystickAxisEvent(event))
 			return true;
-
-		_lastMenuGamepadNavigationTime = now;
-		Common::Event syntheticEvent = Common::Event();
-		syntheticEvent.type = Common::EVENT_KEYDOWN;
-		syntheticEvent.kbd.keycode = keycode;
-		_menuEventQueue.push(syntheticEvent);
-		return true;
 	}
 
 	// Analog stick ??? reticle velocity (mirrors RA1's analog model). The mapped
@@ -815,53 +938,8 @@ bool InsaneRebel2::notifyEvent(const Common::Event &event) {
 	// We ignore a recentre that would fight the opposite direction so a quick
 	// flick doesn't get cancelled by the trailing zero of the other axis half.
 	if (event.type == Common::EVENT_CUSTOM_BACKEND_ACTION_AXIS) {
-		if (_menuInputActive) {
-			if (event.joystick.position == 0) {
-				switch (event.customType) {
-				case kScummBackendActionRebel2AxisUp:
-				case kScummBackendActionRebel2AxisDown:
-					return true;
-				case kScummBackendActionRebel2AxisLeft:
-				case kScummBackendActionRebel2AxisRight:
-					return true;
-				default:
-					break;
-				}
-				return true;
-			}
-
-			Common::KeyCode keycode = Common::KEYCODE_INVALID;
-			switch (event.customType) {
-			case kScummBackendActionRebel2AxisUp:
-				keycode = Common::KEYCODE_UP;
-				break;
-			case kScummBackendActionRebel2AxisDown:
-				keycode = Common::KEYCODE_DOWN;
-				break;
-			case kScummBackendActionRebel2AxisLeft:
-				keycode = Common::KEYCODE_LEFT;
-				break;
-			case kScummBackendActionRebel2AxisRight:
-				keycode = Common::KEYCODE_RIGHT;
-				break;
-			default:
-				break;
-			}
-
-			if (keycode != Common::KEYCODE_INVALID) {
-				const uint32 now = _vm->_system->getMillis();
-				if (_lastMenuGamepadNavigationTime != 0 &&
-						now - _lastMenuGamepadNavigationTime < kRA2MenuGamepadNavigationDebounceMs)
-					return true;
-
-				_lastMenuGamepadNavigationTime = now;
-				Common::Event syntheticEvent = Common::Event();
-				syntheticEvent.type = Common::EVENT_KEYDOWN;
-				syntheticEvent.kbd.keycode = keycode;
-				_menuEventQueue.push(syntheticEvent);
-				return true;
-			}
-		}
+		if (_menuInputActive && handleMenuGamepadAxisEvent(event))
+			return true;
 
 		const int16 axisPosition = (event.joystick.position == Common::JOYAXIS_MIN)
 			? Common::JOYAXIS_MAX : event.joystick.position;
diff --git a/engines/scumm/insane/rebel2/rebel.h b/engines/scumm/insane/rebel2/rebel.h
index 008bb55b591..7d3e61d038a 100644
--- a/engines/scumm/insane/rebel2/rebel.h
+++ b/engines/scumm/insane/rebel2/rebel.h
@@ -511,6 +511,11 @@ public:
 	int32 processMouse() override;
 	Common::Point getGameplayAimPoint();
 	Common::Point getRebelAutoPlayAimPoint();
+	void resetMenuGamepadAxis();
+	bool handleMenuGamepadAxisEvent(const Common::Event &event);
+	bool handleMenuRawJoystickAxisEvent(const Common::Event &event);
+	void updateMenuGamepadAxisKey(int16 oldAxisX, int16 oldAxisY);
+	void queueMenuGamepadAxisKey(Common::KeyCode keycode);
 	// Per-frame: pan the gameplay reticle incrementally from the held directional controls
 	// (on-screen/physical gamepad dpad, keyboard arrows) instead of snapping it to a screen
 	// edge. Call once per frame; getGameplayAimPoint() stays a pure getter.
@@ -531,6 +536,12 @@ public:
 	uint32 _gameplayMouseSettleUntil;
 	uint32 _lastGameplayMenuCloseTime;
 	uint32 _lastMenuGamepadNavigationTime;
+	// Menu-only axis state. Gameplay aiming continues to use _joystickAxisX/Y.
+	int16 _menuGamepadAxisX;
+	int16 _menuGamepadAxisY;
+	int _menuGamepadRawAxis;
+	int16 _menuGamepadRawAxisX;
+	int16 _menuGamepadRawAxisY;
 	void openGameplayMainMenu(SmushPlayer *splayer);
 	void openMenuMainMenu(SmushPlayer *splayer);
 	bool isBitSet(int n) override;


Commit: 1ef1e0e2e7e1a47cd123b44e22eba76b24aa2757
    https://github.com/scummvm/scummvm/commit/1ef1e0e2e7e1a47cd123b44e22eba76b24aa2757
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-21T18:24:30+02:00

Commit Message:
SCUMM: RA2: simplified gamepad controls, no more return to center

Changed paths:
    engines/scumm/insane/rebel2/rebel.cpp


diff --git a/engines/scumm/insane/rebel2/rebel.cpp b/engines/scumm/insane/rebel2/rebel.cpp
index 5c307511965..981ec7a9993 100644
--- a/engines/scumm/insane/rebel2/rebel.cpp
+++ b/engines/scumm/insane/rebel2/rebel.cpp
@@ -63,10 +63,6 @@ const int kRA2GameplayMouseMaxX = 319;
 const int kRA2GameplayMouseMaxY = 199;
 const uint32 kRA2Handler7MouseSettleExtendMs = 1000;
 
-bool rebel2UsesRelativeGamepadAim(int selectedLevel) {
-	return selectedLevel == 1 || selectedLevel == 5 || selectedLevel == 14;
-}
-
 int16 normalizeRebel2AxisMagnitude(int16 position) {
 	return position == Common::JOYAXIS_MIN ? Common::JOYAXIS_MAX : ABS((int)position);
 }
@@ -905,15 +901,11 @@ bool InsaneRebel2::notifyEvent(const Common::Event &event) {
 	// every shot. We observe before ScummEngine (priority 1 > kEventManPriority), so
 	// dropping the event here prevents the clobber. Firing is driven by the
 	// kScummActionInsaneAttack action, not the pointer, so this does not affect shots.
-	// A genuine mouse/touch motion (nonzero relative delta) normally hands control
-	// back; handler 0x26 keeps ownership until its gamepad reticle returns to center,
-	// except for relative gamepad aiming levels where the reticle deliberately holds.
+	// A genuine mouse/touch motion (nonzero relative delta) hands control back.
 	if (_gamepadAimActive && _gameState == kStateGameplay && !_menuInputActive) {
 		switch (event.type) {
 		case Common::EVENT_MOUSEMOVE:
 			if (event.relMouse.x != 0 || event.relMouse.y != 0) {
-				if (_rebelHandler == 0x26 && !rebel2UsesRelativeGamepadAim(_selectedLevel))
-					return true;
 				_gamepadAimActive = false; // real pointer motion takes over
 				break;
 			}
@@ -2006,65 +1998,7 @@ void InsaneRebel2::updateGameplayAimFromGamepad() {
 	int deltaY = 0;
 	bool activeGamepadAim = false;
 
-	if (_rebelHandler == 0x26 && rebel2UsesRelativeGamepadAim(_selectedLevel)) {
-		// Levels 1, 5, and 14 play best with the older mouse-like gamepad behavior from
-		// ec305dee371/0025c4e1086: pan the reticle directly and leave it where
-		// the player releases the stick. Later handler 0x26 levels keep the
-		// original-style centered mapping for obstacle avoidance.
-		if (dpadX || dpadY) {
-			const int kLevel1DigitalStep = 3;
-			deltaX = dpadX * kLevel1DigitalStep;
-			deltaY = dpadY * kLevel1DigitalStep;
-			activeGamepadAim = true;
-		} else if (velX || velY) {
-			const int kLevel1AnalogMaxStep = 8;
-			deltaX = velX * kLevel1AnalogMaxStep / 127;
-			deltaY = velY * kLevel1AnalogMaxStep / 127;
-			activeGamepadAim = true;
-		}
-	} else if (_rebelHandler == 0x26) {
-		// Original RA2 maps joystick axes into a small centered handler 0x26 reticle box.
-		// Gamepad aiming deliberately exposes the full 320x200 mouse aim range
-		// too, but preserves the original joystick feel: curved response for precise
-		// center aiming, and automatic return to screen center when the stick is released.
-		int axisX = 0;
-		int axisY = 0;
-		if (dpadX || dpadY) {
-			axisX = dpadX * 127;
-			axisY = dpadY * 127;
-		} else {
-			axisX = velX;
-			axisY = velY;
-		}
-
-		if (axisX || axisY || _gamepadAimActive) {
-			const Common::Point aimPos = getGameplayAimPoint();
-			const int centerX = 160;
-			const int centerY = 100;
-			const int absAxisX = ABS(axisX);
-			const int absAxisY = ABS(axisY);
-			const int curvedX = (absAxisX * absAxisX + 126) / 127;
-			const int curvedY = (absAxisY * absAxisY + 126) / 127;
-			const int targetX = axisX < 0 ?
-				centerX - curvedX * centerX / 127 :
-				centerX + curvedX * (319 - centerX) / 127;
-			const int targetY = axisY < 0 ?
-				centerY - curvedY * centerY / 127 :
-				centerY + curvedY * (199 - centerY) / 127;
-			const int maxStep = (axisX || axisY) ? 14 : 10;
-			const int distX = targetX - aimPos.x;
-			const int distY = targetY - aimPos.y;
-
-			if (distX || distY) {
-				deltaX = CLIP<int>(distX, -maxStep, maxStep);
-				deltaY = CLIP<int>(distY, -maxStep, maxStep);
-				activeGamepadAim = true;
-			} else {
-				_gamepadAimActive = (axisX || axisY);
-				return;
-			}
-		}
-	} else if (_rebelHandler == 7) {
+	if (_rebelHandler == 7) {
 		int axisX = 0;
 		int axisY = 0;
 		if (dpadX || dpadY) {
@@ -2097,16 +2031,20 @@ void InsaneRebel2::updateGameplayAimFromGamepad() {
 				return;
 			}
 		}
-	} else if (dpadX || dpadY) {
-		const int kOriginalDigitalStep = 3;
-		deltaX = dpadX * kOriginalDigitalStep;
-		deltaY = dpadY * kOriginalDigitalStep;
-		activeGamepadAim = true;
-	} else if (velX || velY) {
-		const int kAnalogMaxStep = 8;
-		deltaX = velX * kAnalogMaxStep / 127;
-		deltaY = velY * kAnalogMaxStep / 127;
-		activeGamepadAim = true;
+	} else {
+		// Handler 0x26 and the remaining gamepad-driven aim modes use relative
+		// reticle panning: releasing the stick leaves the aim point in place.
+		if (dpadX || dpadY) {
+			const int kOriginalDigitalStep = 3;
+			deltaX = dpadX * kOriginalDigitalStep;
+			deltaY = dpadY * kOriginalDigitalStep;
+			activeGamepadAim = true;
+		} else if (velX || velY) {
+			const int kAnalogMaxStep = 8;
+			deltaX = velX * kAnalogMaxStep / 127;
+			deltaY = velY * kAnalogMaxStep / 127;
+			activeGamepadAim = true;
+		}
 	}
 
 	if (!activeGamepadAim)


Commit: b8caec28c07ece990e69bb1a5b1394ace96541ff
    https://github.com/scummvm/scummvm/commit/b8caec28c07ece990e69bb1a5b1394ace96541ff
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-21T18:24:30+02:00

Commit Message:
SCUMM: RA2: gamepad fixes for handler 7

Changed paths:
    engines/scumm/insane/rebel2/iact.cpp
    engines/scumm/insane/rebel2/rebel.cpp


diff --git a/engines/scumm/insane/rebel2/iact.cpp b/engines/scumm/insane/rebel2/iact.cpp
index c4d52b41477..726f62fe56a 100644
--- a/engines/scumm/insane/rebel2/iact.cpp
+++ b/engines/scumm/insane/rebel2/iact.cpp
@@ -869,21 +869,23 @@ void InsaneRebel2::handleOpcode6Handler7(Common::SeekableReadStream &b, int16 pa
 	int16 scaledInputX = (int16)((inputX * 127) / 160);
 	int16 scaledInputY = _optControlsFlipped ? (int16)-inputY : inputY;  // local_14
 
-	// Direct mouse/touch/gamepad aiming can hold the cursor at an edge
-	// indefinitely. Keep this sensitivity concession local to Handler 7
-	// third-person ship steering. A held analog stick is already bounded by its
-	// physical range, so keep its full logical range for more responsive L10 flight.
-	if (!_gamepadAimActive) {
+	const bool useDirectGamepadFlight = _gamepadAimActive && _selectedLevel == 10;
+
+	// Direct mouse/touch aiming can hold the cursor at an edge indefinitely.
+	// Keep this sensitivity concession for mouse-like Handler 7 steering.
+	// Level 10 is the only Handler 7 stage that needs direct full-range
+	// gamepad axes; other Handler 7 stages use bounded target steering to avoid
+	// harsh perspective shifts from a held stick.
+	if (!useDirectGamepadFlight) {
 		scaledInputX = (int16)((scaledInputX * kRA2Handler7DirectInputNumerator) /
 			kRA2Handler7DirectInputDenominator);
 		scaledInputY = (int16)((scaledInputY * kRA2Handler7DirectInputNumerator) /
 			kRA2Handler7DirectInputDenominator);
 	}
-	// Mouse/touch can hold an absolute cursor at an edge indefinitely, so they use
-	// bounded target steering. Gamepad input for Handler 7 now feeds the original
-	// center-relative flight axes and lets the assembly-derived velocity history,
-	// lift/slide tables, wind, and clamps produce the ship movement.
-	const bool useTargetSteering = !_gamepadAimActive;
+	// Mouse/touch and most Handler 7 gamepad stages use bounded target
+	// steering. Level 10 keeps direct center-relative gamepad axes for its
+	// speeder flight.
+	const bool useTargetSteering = !useDirectGamepadFlight;
 	int16 mouseFlightTargetX = _flyShipScreenX;
 	if (useTargetSteering) {
 		mouseFlightTargetX = (int16)(0xd4 + (scaledInputX * kRA2Handler7MouseTargetRangeX) / 127);
@@ -954,7 +956,7 @@ void InsaneRebel2::handleOpcode6Handler7(Common::SeekableReadStream &b, int16 pa
 	// letting a held off-center cursor keep pushing the ship until it bounces.
 	if (useTargetSteering) {
 		int targetDeltaX = mouseFlightTargetX - _flyShipScreenX;
-		const int targetSteeringDivisor = _gamepadAimActive ? 2 : 4;
+		const int targetSteeringDivisor = 4;
 		positionDeltaX = (int16)CLIP<int>(targetDeltaX / targetSteeringDivisor, -12, 12);
 		if (positionDeltaX == 0 && targetDeltaX != 0)
 			positionDeltaX = (targetDeltaX < 0) ? -1 : 1;
diff --git a/engines/scumm/insane/rebel2/rebel.cpp b/engines/scumm/insane/rebel2/rebel.cpp
index 981ec7a9993..e929510663e 100644
--- a/engines/scumm/insane/rebel2/rebel.cpp
+++ b/engines/scumm/insane/rebel2/rebel.cpp
@@ -2009,22 +2009,50 @@ void InsaneRebel2::updateGameplayAimFromGamepad() {
 			axisY = velY;
 		}
 
+		const bool useDirectHandler7Gamepad = _selectedLevel == 10;
+		if (!useDirectHandler7Gamepad && !axisX && !axisY)
+			return;
+
 		if (axisX || axisY || _gamepadAimActive) {
 			const Common::Point aimPos = getGameplayAimPoint();
 			const int centerX = 160;
 			const int centerY = 100;
-			const int targetX = (axisX < 0) ?
-				centerX + axisX * centerX / 127 :
-				centerX + axisX * (319 - centerX) / 127;
-			const int targetY = (axisY < 0) ?
-				centerY + axisY * centerY / 127 :
-				centerY + axisY * (199 - centerY) / 127;
+			int targetX;
+			int targetY;
+			if (useDirectHandler7Gamepad) {
+				targetX = (axisX < 0) ?
+					centerX + axisX * centerX / 127 :
+					centerX + axisX * (319 - centerX) / 127;
+				targetY = (axisY < 0) ?
+					centerY + axisY * centerY / 127 :
+					centerY + axisY * (199 - centerY) / 127;
+			} else {
+				// The wider Level 10 mapping makes other Handler 7 stages swing the
+				// camera too hard. Keep their gamepad cursor in the older bounded
+				// range and move it there gradually.
+				const int kHandler7HorizontalRange = 120;
+				targetX = axisX ?
+					CLIP<int>(centerX + axisX * kHandler7HorizontalRange / 127, 0, 319) :
+					aimPos.x;
+				targetY = axisY ?
+					((axisY < 0) ?
+						centerY + axisY * centerY / 127 :
+						centerY + axisY * (199 - centerY) / 127) :
+					aimPos.y;
+			}
 			const int distX = targetX - aimPos.x;
 			const int distY = targetY - aimPos.y;
 
 			if (distX || distY) {
-				deltaX = distX;
-				deltaY = distY;
+				if (useDirectHandler7Gamepad) {
+					deltaX = distX;
+					deltaY = distY;
+				} else {
+					const int kHandler7HorizontalMaxStep = 24;
+					const int kHandler7VerticalMaxStep = 32;
+					deltaX = CLIP<int>(distX, -kHandler7HorizontalMaxStep, kHandler7HorizontalMaxStep);
+					deltaY = CLIP<int>(distY, -kHandler7VerticalMaxStep, kHandler7VerticalMaxStep);
+				}
 				activeGamepadAim = true;
 			} else {
 				_gamepadAimActive = true;


Commit: 2bd3cf279f320b9dc382af85b6af52bd256b3cb7
    https://github.com/scummvm/scummvm/commit/2bd3cf279f320b9dc382af85b6af52bd256b3cb7
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-21T18:24:31+02:00

Commit Message:
SCUMM: RA2: perspective/damage fixes for handler 7

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


diff --git a/engines/scumm/insane/rebel2/rebel.cpp b/engines/scumm/insane/rebel2/rebel.cpp
index e929510663e..25ef2db64b2 100644
--- a/engines/scumm/insane/rebel2/rebel.cpp
+++ b/engines/scumm/insane/rebel2/rebel.cpp
@@ -2010,7 +2010,8 @@ void InsaneRebel2::updateGameplayAimFromGamepad() {
 		}
 
 		const bool useDirectHandler7Gamepad = _selectedLevel == 10;
-		if (!useDirectHandler7Gamepad && !axisX && !axisY)
+		const bool useCenteredHandler7Gamepad = (_selectedLevel == 3 && _flyControlMode == 1);
+		if (!useDirectHandler7Gamepad && !useCenteredHandler7Gamepad && !axisX && !axisY)
 			return;
 
 		if (axisX || axisY || _gamepadAimActive) {
@@ -2029,22 +2030,23 @@ void InsaneRebel2::updateGameplayAimFromGamepad() {
 			} else {
 				// The wider Level 10 mapping makes other Handler 7 stages swing the
 				// camera too hard. Keep their gamepad cursor in the older bounded
-				// range and move it there gradually.
+				// range and move it there gradually. Level 3 mode 1 is wall-collision
+				// tunnel flight, so neutral stick axes must feed zero input instead of
+				// leaving stale off-center mouse coordinates to keep pushing the ship.
 				const int kHandler7HorizontalRange = 120;
+				const int kHandler7VerticalRange = 80;
 				targetX = axisX ?
 					CLIP<int>(centerX + axisX * kHandler7HorizontalRange / 127, 0, 319) :
-					aimPos.x;
+					(useCenteredHandler7Gamepad ? centerX : aimPos.x);
 				targetY = axisY ?
-					((axisY < 0) ?
-						centerY + axisY * centerY / 127 :
-						centerY + axisY * (199 - centerY) / 127) :
-					aimPos.y;
+					CLIP<int>(centerY + axisY * kHandler7VerticalRange / 127, 0, 199) :
+					(useCenteredHandler7Gamepad ? centerY : aimPos.y);
 			}
 			const int distX = targetX - aimPos.x;
 			const int distY = targetY - aimPos.y;
 
 			if (distX || distY) {
-				if (useDirectHandler7Gamepad) {
+				if (useDirectHandler7Gamepad || useCenteredHandler7Gamepad) {
 					deltaX = distX;
 					deltaY = distY;
 				} else {
diff --git a/engines/scumm/insane/rebel2/rebel.h b/engines/scumm/insane/rebel2/rebel.h
index 7d3e61d038a..a9f2f80ffb9 100644
--- a/engines/scumm/insane/rebel2/rebel.h
+++ b/engines/scumm/insane/rebel2/rebel.h
@@ -642,6 +642,7 @@ public:
 	void renderHandler8MonitorEffect(byte *renderBitmap, int pitch, int width, int height);
 	void renderHandler8PovOverlay(byte *renderBitmap, int pitch, int width, int height);
 	Common::Point getHandler7ShipDrawPoint();
+	Common::Point getHandler7ProjectedPointFor(int16 x, int16 y);
 	Common::Point getHandler7ProjectedPoint();
 	Common::Point getHandler7ShotTargetPoint();
 	Common::Point getHandler8ShotTargetPoint();
diff --git a/engines/scumm/insane/rebel2/render.cpp b/engines/scumm/insane/rebel2/render.cpp
index b378b56093a..e7538874c1a 100644
--- a/engines/scumm/insane/rebel2/render.cpp
+++ b/engines/scumm/insane/rebel2/render.cpp
@@ -630,18 +630,22 @@ Common::Point InsaneRebel2::getHandler7ShipDrawPoint() {
 	                     projected.y - 0x82 + _flyShipSprite->getCharYOffset(spriteIndex));
 }
 
-Common::Point InsaneRebel2::getHandler7ProjectedPoint() {
+Common::Point InsaneRebel2::getHandler7ProjectedPointFor(int16 x, int16 y) {
 	int viewTilt = (_viewShift * 5) / 128;
 	int xSkew = (viewTilt * 9) / 4;
 	int ySkew = viewTilt * 5;
-	int relX = _flyShipScreenX - (_perspectiveX + 0xd4);
-	int relY = _flyShipScreenY - (_perspectiveY + 0x82);
+	int relX = x - (_perspectiveX + 0xd4);
+	int relY = y - (_perspectiveY + 0x82);
 	int projectedX = (xSkew * relY) / 0x55 + relX + 0xa0 + _viewX;
 	int projectedY = relY + 0x55 - (ySkew * relX) / 0xa0 + _viewY;
 
 	return Common::Point(projectedX, projectedY);
 }
 
+Common::Point InsaneRebel2::getHandler7ProjectedPoint() {
+	return getHandler7ProjectedPointFor(_flyShipScreenX, _flyShipScreenY);
+}
+
 Common::Point InsaneRebel2::getHandler7ShotTargetPoint() {
 	// Handler 7 targets the projected ship/crosshair point computed in
 	// FUN_0040d836; it does not use the generic mouse position for combat shots.
@@ -2370,6 +2374,19 @@ void InsaneRebel2::updatePostRenderScroll(int width, int height) {
 	if (maxScrollY < 0)
 		maxScrollY = 0;
 
+	if (_rebelHandler == 7) {
+		// High-detail Handler 7 follows FUN_0041C5C0: the oversized 424x260
+		// flight buffer is presented through a perspective-derived 320x170
+		// gameplay window at source offset (0x34,0x2d) + DAT_00443712/14.
+		// Keep the final crop in that same space so rendered ship/cues and
+		// raw collision zones describe the same tunnel position.
+		const int handler7MaxScrollY = isHiRes() ? MAX<int>(0, height - 170) : maxScrollY;
+		_viewX = CLIP<int>(0x34 + _perspectiveX, 0, maxScrollX);
+		_viewY = CLIP<int>(0x2d + _perspectiveY, 0, handler7MaxScrollY);
+		_player->setScrollOffset(_viewX, _viewY);
+		return;
+	}
+
 	// Simple linear mapping: Center of screen corresponds to center of buffer.
 	Common::Point aimPos = getGameplayAimPoint();
 	_viewX = (aimPos.x * maxScrollX) / viewportWidth;
@@ -2640,9 +2657,11 @@ void InsaneRebel2::renderGameplayPostFrame(byte *renderBitmap, int pitch, int wi
 	// Original: FUN_00403240 only runs handlers when DAT_0047a814 == 0.
 	processMouse();
 
-	_gameplayPresentationClipBottom = statusBarY - 1;
-	if (isHiRes() && _rebelHandler == 7 && curFrame != 0)
-		_gameplayPresentationClipBottom = MIN(_gameplayPresentationClipBottom, 170 * 2 - 1);
+	// Handler 7's high-detail flight view uses a 320x170 gameplay area after
+	// the first frame; the remaining RA2 gameplay handlers keep the 180px split.
+	const int statusScale = (statusBarY >= 360) ? 2 : 1;
+	const int gameplayStatusBarY = (_rebelHandler == 7 && curFrame != 0) ? 170 * statusScale : statusBarY;
+	_gameplayPresentationClipBottom = gameplayStatusBarY - 1;
 
 	_hiResPresentationViewX = 0;
 	_hiResPresentationViewY = 0;
@@ -2690,7 +2709,7 @@ void InsaneRebel2::renderGameplayPostFrame(byte *renderBitmap, int pitch, int wi
 	// We draw directly to screen at Y=180.
 
 	// STEP 0: Fill status bar background (FUN_004288c0).
-	renderStatusBarBackground(renderBitmap, pitch, width, height, videoWidth, videoHeight, statusBarY);
+	renderStatusBarBackground(renderBitmap, pitch, width, height, videoWidth, videoHeight, gameplayStatusBarY);
 
 	// Ship rendering. Handler 7 is drawn later, after its lasers, matching
 	// FUN_0040d836's order so the ship covers the muzzle end of the beams.
@@ -2731,7 +2750,7 @@ void InsaneRebel2::renderGameplayPostFrame(byte *renderBitmap, int pitch, int wi
 	renderEmbeddedHudOverlays(renderBitmap, pitch, width, height);
 
 	// STEP 2: Draw DISPFONT.NUT status bar sprites (FUN_0041c012).
-	renderStatusBarSprites(renderBitmap, pitch, width, height, statusBarY, curFrame);
+	renderStatusBarSprites(renderBitmap, pitch, width, height, gameplayStatusBarY, curFrame);
 
 	updateGameplayDamageEffects(renderBitmap, pitch, width, height);
 	checkGameplayPostRenderCollisions(renderBitmap, pitch, width, height, curFrame);
@@ -2747,7 +2766,7 @@ void InsaneRebel2::renderGameplayPostFrame(byte *renderBitmap, int pitch, int wi
 	renderHandler8PovOverlay(renderBitmap, pitch, width, height);
 
 	// HUD score/lives rendering (FUN_0041c012).
-	renderScoreHUD(renderBitmap, pitch, width, height, statusBarY);
+	renderScoreHUD(renderBitmap, pitch, width, height, gameplayStatusBarY);
 
 	// Reset FOBJ position offsets (FUN_00424510(0,0) in original FUN_0041DB5E line 271).
 	if (_player) {
@@ -3524,9 +3543,13 @@ void InsaneRebel2::renderHandler7Ship(byte *renderBitmap, int pitch, int width,
 			}
 		} else {
 			int16 bottomDist = _corridorBottomY - _flyShipScreenY;
-			int bottomX = shipCenterX;
-			int bottomY = (_corridorBottomY - 0x82) + _perspectiveY + 100 +
-				(renderHiRes ? nativeViewY : _viewY);
+			Common::Point bottomProjected = getHandler7ProjectedPointFor(_flyShipScreenX, _corridorBottomY);
+			if (renderHiRes) {
+				bottomProjected.x += nativeViewX;
+				bottomProjected.y += nativeViewY;
+			}
+			int bottomX = bottomProjected.x;
+			int bottomY = bottomProjected.y;
 
 			if (bottomDist < 0x19) {
 				_flyEffectAnimCounter++;
@@ -3580,8 +3603,9 @@ void InsaneRebel2::renderHandler7Ship(byte *renderBitmap, int pitch, int width,
 			shipDraw.x, shipDraw.y, _flyTargetSprite, spriteIndex);
 	}
 
-	debugC(DEBUG_INSANE, "Handler7Ship: draw=(%d,%d) sprite=%d/%d shipPos=(%d,%d) persp=(%d,%d) smoothVel=%d vertIn=%d fxCtr=%d fxRep=%d",
+	debugC(DEBUG_INSANE, "Handler7Ship: draw=(%d,%d) sprite=%d/%d shipPos=(%d,%d) view=(%d,%d) persp=(%d,%d) smoothVel=%d vertIn=%d fxCtr=%d fxRep=%d",
 		drawX, drawY, spriteIndex, numSprites, _flyShipScreenX, _flyShipScreenY,
+		renderHiRes ? nativeViewX : _viewX, renderHiRes ? nativeViewY : _viewY,
 		_perspectiveX, _perspectiveY, _smoothedVelocity, _verticalInput, _flyEffectAnimCounter, _flyOverlayRepeatCount);
 }
 
@@ -4269,9 +4293,11 @@ void InsaneRebel2::renderSpaceExplosions(byte *renderBitmap, int pitch, int widt
 		const int nativeViewY = renderHiRes ? _hiResPresentationViewY : _viewY;
 
 		if (spriteIndex >= 0 && spriteIndex < numChars) {
-			// Compute ship screen position (simplified FUN_0041c720 transform)
-			int shipDrawX = (_flyShipScreenX - 0xd4) + _perspectiveX + 160 + nativeViewX;
-			int shipDrawY = (_flyShipScreenY - 0x82) + _perspectiveY + 100 + nativeViewY;
+			Common::Point shipProjected = getHandler7ProjectedPoint();
+			if (renderHiRes) {
+				shipProjected.x += nativeViewX;
+				shipProjected.y += nativeViewY;
+			}
 
 			// Per-direction offset from ship center.
 			// Original uses lookup tables (DAT_004438da etc.) indexed by
@@ -4295,8 +4321,8 @@ void InsaneRebel2::renderSpaceExplosions(byte *renderBitmap, int pitch, int widt
 				break;
 			}
 
-			int drawX = shipDrawX + offsetX;
-			int drawY = shipDrawY + offsetY;
+			int drawX = shipProjected.x + offsetX;
+			int drawY = shipProjected.y + offsetY;
 			if (renderHiRes) {
 				drawX = (drawX - nativeViewX) * 2;
 				drawY = (drawY - nativeViewY) * 2;


Commit: 8c086c04d6ae46ded60a36b0ea04dcce16497373
    https://github.com/scummvm/scummvm/commit/8c086c04d6ae46ded60a36b0ea04dcce16497373
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-21T18:24:31+02:00

Commit Message:
SCUMM: RA2: use B to go back/skip cutscenes

Changed paths:
    engines/scumm/insane/rebel2/rebel.cpp
    engines/scumm/metaengine.cpp


diff --git a/engines/scumm/insane/rebel2/rebel.cpp b/engines/scumm/insane/rebel2/rebel.cpp
index 25ef2db64b2..81a1364d665 100644
--- a/engines/scumm/insane/rebel2/rebel.cpp
+++ b/engines/scumm/insane/rebel2/rebel.cpp
@@ -1054,9 +1054,6 @@ bool InsaneRebel2::notifyEvent(const Common::Event &event) {
 				case kScummActionInsaneAttack:
 					keycode = Common::KEYCODE_RETURN;
 					break;
-				case kScummActionInsaneSwitch:
-					keycode = Common::KEYCODE_ESCAPE;
-					break;
 				case kScummActionInsaneBack:
 					keycode = Common::KEYCODE_ESCAPE;
 					break;
@@ -1071,8 +1068,7 @@ bool InsaneRebel2::notifyEvent(const Common::Event &event) {
 			case kStateTopPilots:
 				if (event.customType == kScummActionInsaneAttack)
 					keycode = Common::KEYCODE_RETURN;
-				else if (event.customType == kScummActionInsaneSwitch ||
-				         event.customType == kScummActionInsaneBack ||
+				else if (event.customType == kScummActionInsaneBack ||
 				         event.customType == kScummActionInsaneSkip)
 					keycode = Common::KEYCODE_ESCAPE;
 				break;
diff --git a/engines/scumm/metaengine.cpp b/engines/scumm/metaengine.cpp
index 06c6bd74354..3ed790a2d3c 100644
--- a/engines/scumm/metaengine.cpp
+++ b/engines/scumm/metaengine.cpp
@@ -1295,7 +1295,7 @@ Common::KeymapArray ScummMetaEngine::initKeymaps(const char *target) const {
 		act->addDefaultInputMapping("JOY_A");
 		rebel2Keymap->addAction(act);
 
-		act = new Action("RA2COVER", _("Cover / back"));
+		act = new Action("RA2COVER", _("Cover"));
 		act->setCustomEngineActionEvent(kScummActionInsaneSwitch);
 		act->addDefaultInputMapping("JOY_X");
 		act->addDefaultInputMapping("JOY_Y");


Commit: 5fc31be42d3657b3b35b81fa88ff25f137e6087f
    https://github.com/scummvm/scummvm/commit/5fc31be42d3657b3b35b81fa88ff25f137e6087f
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-21T18:24:31+02:00

Commit Message:
SCUMM: RA2: clean-up of comments for INSANE code

Changed paths:
    engines/scumm/insane/rebel2/audio.cpp
    engines/scumm/insane/rebel2/iact.cpp
    engines/scumm/insane/rebel2/levels.cpp
    engines/scumm/insane/rebel2/menu.cpp
    engines/scumm/insane/rebel2/rebel.cpp
    engines/scumm/insane/rebel2/rebel.h
    engines/scumm/insane/rebel2/render.cpp
    engines/scumm/insane/rebel2/runlevels.cpp


diff --git a/engines/scumm/insane/rebel2/audio.cpp b/engines/scumm/insane/rebel2/audio.cpp
index bf3b26e9122..6cbc4e3e1f4 100644
--- a/engines/scumm/insane/rebel2/audio.cpp
+++ b/engines/scumm/insane/rebel2/audio.cpp
@@ -34,9 +34,7 @@
 
 namespace Scumm {
 
-// ---------------------------------------------------------------------------
 // Audio Handling
-// ---------------------------------------------------------------------------
 // RA2 doesn't use iMUSE -- audio is handled directly through the mixer.
 
 // initAudio -- Initialize audio system for RA2.
@@ -63,22 +61,16 @@ void InsaneRebel2::queueAudioData(int trackIdx, uint8 *data, int32 size, int vol
 	_audio.queueData(trackIdx, data, size, volume, pan);
 }
 
-//
 // processAudioFrame -- Per-frame audio dispatch (replaces iMUSE path)
-//
 // Iterates SmushPlayer audio tracks, handles FADING->PLAYING transitions,
 // and feeds PCM data through queueAudioData. Called from SmushPlayer when
 // iMUSE is null.
-//
 void InsaneRebel2::processAudioFrame(int16 feedSize) {
 	_audio.processFrame(_player, feedSize);
 }
 
-// ---------------------------------------------------------------------------
 // Sound Effects (SAD files)
-// ---------------------------------------------------------------------------
 // Standalone SAUD files from SYSTM/ loaded at init for one-shot SFX.
-// Original: FUN_0042a3b0 loads into DAT_00456888[0..7].
 
 const char *const kRA2SfxFiles[InsaneRebel2::kRA2NumSfx] = {
 	"SYSTM/BLAST.SAD",    // 0 - Player laser fire
@@ -91,7 +83,6 @@ const char *const kRA2SfxFiles[InsaneRebel2::kRA2NumSfx] = {
 	"SYSTM/TBLAST.SAD"    // 7 - TIE blast
 };
 
-// loadSfx -- Load all SAD files from SYSTM/ directory (FUN_0042a3b0).
 void InsaneRebel2::loadSfx() {
 	for (int i = 0; i < kRA2NumSfx; i++) {
 		ScummFile *file = _vm->instantiateScummFile();
@@ -207,7 +198,6 @@ void InsaneRebel2::playSfx(int slot, int volume, int pan) {
 	debugC(DEBUG_INSANE, "InsaneRebel2::playSfx: slot=%d vol=%d pan=%d size=%d", slot, mixVolume, clampedPan, _sfxSize[slot]);
 }
 
-// loadAuxSfx -- Load sound data into auxiliary buffer (FUN_004118df).
 void InsaneRebel2::loadAuxSfx(int buffer, const byte *data, uint32 size) {
 	if (buffer < 0 || buffer >= kRA2NumAuxSfx || !data || size == 0) {
 		return;
@@ -224,7 +214,6 @@ void InsaneRebel2::loadAuxSfx(int buffer, const byte *data, uint32 size) {
 	debugC(DEBUG_INSANE, "InsaneRebel2::loadAuxSfx: buffer=%d size=%d", buffer, size);
 }
 
-// playAuxSfx -- Play from auxiliary buffer (FUN_00411931).
 // Handles both raw PCM and SAUD-wrapped data.
 void InsaneRebel2::playAuxSfx(int buffer, int volume, int pan) {
 	if (buffer < 0 || buffer >= kRA2NumAuxSfx || !_auxSfxData[buffer] || _auxSfxSize[buffer] == 0) {
@@ -235,7 +224,6 @@ void InsaneRebel2::playAuxSfx(int buffer, int volume, int pan) {
 
 	_vm->_mixer->stopHandle(_auxSfxHandles[buffer]);
 
-	// The auxiliary buffer data goes through FUN_00425fc0 (format dispatch) in the original.
 	// Check if data has SAUD header; if so, extract PCM from SDAT chunk.
 	// Otherwise treat as raw 8-bit unsigned PCM at 11025 Hz.
 	const byte *pcmStart = _auxSfxData[buffer];
diff --git a/engines/scumm/insane/rebel2/iact.cpp b/engines/scumm/insane/rebel2/iact.cpp
index 726f62fe56a..3063a0a8f9a 100644
--- a/engines/scumm/insane/rebel2/iact.cpp
+++ b/engines/scumm/insane/rebel2/iact.cpp
@@ -62,12 +62,9 @@ static bool readLevel2BackgroundChunkHeader(Common::SeekableReadStream &stream,
 	return true;
 }
 
-//
 // procPreRendering -- Pre-frame setup: background restore and corridor overlays.
-//
 // Restores Level 2 background before FOBJ decoding (Handler 8) and handles
 // Handler 25 corridor overlay positioning.
-//
 void InsaneRebel2::procPreRendering(byte *renderBitmap) {
 	Insane::procPreRendering(renderBitmap);
 
@@ -78,7 +75,6 @@ void InsaneRebel2::procPreRendering(byte *renderBitmap) {
 	// Reset opcode 6 init flag at the start of each new video.
 	// This ensures the per-wave init (clearBit, link table reset, wave state)
 	// fires exactly once per wave video, not every frame.
-	//
 	// Exception: seamless continuation segments (flag 0x40, e.g. the looping 06PLAY1B attack
 	// run) keep the init flag set, otherwise the wave init's clearBit(0) would resurrect
 	// shield targets the player already destroyed, resetting the shield on every loop.
@@ -92,9 +88,7 @@ void InsaneRebel2::procPreRendering(byte *renderBitmap) {
 	// The tiny FOBJ sprites (7x10, 9x38 pixels) only draw new sprite positions but don't
 	// clear old ones. By restoring the full background each frame, we ensure old sprite
 	// positions are erased before new ones are drawn.
-	//
 	// This is called at the start of handleFrame(), before any FOBJ chunks are processed.
-	//
 	// IMPORTANT: Only restore when the render buffer pitch matches the background pitch (320).
 	// Levels like Level 12 (Sewers) use oversized buffers (424x260) where FOBJ/FETCH handles
 	// background restoration. Copying the 320-wide background into a 640-wide buffer with
@@ -108,18 +102,6 @@ void InsaneRebel2::procPreRendering(byte *renderBitmap) {
 		}
 	}
 
-	// For Handler 25 (Level 2 speeder bike), draw the corridor overlay BEFORE FOBJ decoding.
-	// The corridor overlay (par3=4 -> _rebelEmbeddedHud[4]) is DAT_00482268, a 350x230 buffer.
-	// From FUN_0041cadb line 216: FUN_00428a10(param_1,0,DAT_0045790c,DAT_0045790e,DAT_00482268)
-	// It's drawn at (DAT_0045790c, DAT_0045790e) which are _rebelViewOffsetX/Y.
-	//
-	// For Mode 1: DAT_0045790c = damageLevel * -5 - 14, range -39 (covered) to -14 (uncovered)
-	//
-	// From FUN_00428a10: When position is negative, we skip source pixels and draw at 0.
-	// Handler 25: Corridor overlay and FOBJ position offsets are set during
-	// IACT opcode 6 processing (iactRebel2Opcode6), matching the original
-	// FUN_41CADB architecture. No corridor drawing needed here.
-
 	// Chapter selection: Set FOBJ offset for O_LEVEL.SAN preview strip.
 	// The 80x800 FOBJ strip is at left=320. With offset X=-90, it renders at
 	// X=230 (inside the preview box). The Y offset scrolls the strip vertically
@@ -207,34 +189,14 @@ void InsaneRebel2::procIACT(byte *renderBitmap, int32 codecparam, int32 setupsan
 		iactRebel2Scene1(renderBitmap, codecparam, setupsan12, setupsan13, b, size, flags, par1, par2, par3, par4);
 }
 
-// iactRebel2Scene1 -- Scene 1 IACT dispatcher (FUN_4028C5 / FUN_4033CF).
 void InsaneRebel2::iactRebel2Scene1(byte *renderBitmap, int32 codecparam, int32 setupsan12,
 				  int32 setupsan13, Common::SeekableReadStream &b, int32 size, int32 flags,
 				  int16 par1, int16 par2, int16 par3, int16 par4) {
-	// par1 is the Opcode (word at offset +0)
-	// par2 is word at offset +2
-	// par3 is word at offset +4
-	// par4 is word at offset +6
-	//
-	// Based on disassembly of FUN_4028C5 and FUN_4033CF:
-	//
 	// For IACT opcode 4 (enemy position update), the structure is:
-	//   Offset +0x06: Type/SubType (par3)
-	//   Offset +0x08: Enemy ID
-	//   Offset +0x0A: X position
-	//   Offset +0x0C: Y position
-	//   Offset +0x0E: Width
-	//   Offset +0x10: Height
-	//
-	// The original game calculates bounding box center:
 	//   centerX = X + (Width / 2)
 	//   centerY = Y + (Height / 2)
 	// Then subtracts scroll offsets:
-	//   screenX = centerX - DAT_0043e006 (scrollX)
-	//   screenY = centerY - DAT_0043e008 (scrollY)
 
-	//   screenX = centerX - DAT_0043e006 (scrollX)
-	//   screenY = centerY - DAT_0043e008 (scrollY)
 
 	if (par1 == 4) {
 		enemyUpdate(renderBitmap, b, par2, par3, par4);
@@ -245,7 +207,6 @@ void InsaneRebel2::iactRebel2Scene1(byte *renderBitmap, int32 codecparam, int32
 		iactRebel2Opcode3(b, par2, par3, par4);
 	}
 	else if (par1 == 5) {
-		// Opcode 5: Collision Zone Registration (FUN_004033cf case 5)
 		// Sub-opcode 0x0D (13) = Primary collision zones (obstacles)
 		// Sub-opcode 0x0E (14) = Secondary collision zones (boundaries)
 		// par2 is the sub-opcode that determines which zone table to use
@@ -253,22 +214,12 @@ void InsaneRebel2::iactRebel2Scene1(byte *renderBitmap, int32 codecparam, int32
 
 		if (par2 == 0x0D || par2 == 0x0E) {
 			// Register the collision zone from the remaining IACT data
-			// par4 (userId from IACT header) is the filter value used by FUN_4092D9
-			// for the < 1000 test (offset +6 in the original stored pointer)
 			registerCollisionZone(b, par2, par4);
 		}
 
 	} else if (par1 == 7) {
-		// Opcode 7: Handler 7 corridor/velocity control (FUN_40C3CC case 5)
 		// IACT header: par1=7, par2=flags, par3=0, par4=sub-opcode
 		// Body contains 2 int16 values (body[0], body[1])
-		//
-		// par4 sub-opcodes (from FUN_40C3CC case 5 switch on param_5[3]):
-		//   0: Set velocity params (DAT_00443b12, DAT_00443b14)
-		//   1: Set left X + top Y corridor boundaries (DAT_00443b0a, DAT_00443b0c)
-		//   2: Set right X + bottom Y corridor boundaries (DAT_00443b0e, DAT_00443b10)
-		//   5: Set flag (DAT_00443b52)
-
 		int16 body0 = 0, body1 = 0;
 		if (b.size() - b.pos() >= 4) {
 			body0 = b.readSint16LE();
@@ -277,8 +228,6 @@ void InsaneRebel2::iactRebel2Scene1(byte *renderBitmap, int32 codecparam, int32
 
 		switch (par4) {
 		case 0:
-			// Velocity/wind data ??? affects ship drift in FUN_40C3CC physics
-			// DAT_00443b12 = horizontal wind, DAT_00443b14 = vertical wind
 			_windParamX = body0;
 			_windParamY = body1;
 			debugC(DEBUG_INSANE, "Opcode 7 par4=0: wind=(%d,%d)", body0, body1);
@@ -287,7 +236,6 @@ void InsaneRebel2::iactRebel2Scene1(byte *renderBitmap, int32 codecparam, int32
 			// Set LEFT X boundary and TOP Y boundary
 			_corridorLeftX = body0;
 			_corridorTopY = body1;
-			// Mode-dependent margin adjustment (FUN_40C3CC lines 341-351)
 			if (_flyControlMode == 2) {
 				_corridorLeftX += 15;
 			} else if (_flyControlMode == 0) {
@@ -300,7 +248,6 @@ void InsaneRebel2::iactRebel2Scene1(byte *renderBitmap, int32 codecparam, int32
 			// Set RIGHT X boundary and BOTTOM Y boundary
 			_corridorRightX = body0;
 			_corridorBottomY = body1;
-			// Mode-dependent margin adjustment (FUN_40C3CC lines 356-365)
 			if (_flyControlMode == 2) {
 				_corridorRightX -= 15;
 			} else if (_flyControlMode == 0) {
@@ -310,7 +257,6 @@ void InsaneRebel2::iactRebel2Scene1(byte *renderBitmap, int32 codecparam, int32
 				body0, body1, _corridorRightX);
 			break;
 		case 5:
-			// DAT_00443b52: repeats FLY002 ship overlay in FUN_40D836.
 			_flyOverlayRepeatCount = body0;
 			debugC(DEBUG_INSANE, "Opcode 7 par4=5: flyOverlayRepeat=%d", _flyOverlayRepeatCount);
 			break;
@@ -320,10 +266,8 @@ void InsaneRebel2::iactRebel2Scene1(byte *renderBitmap, int32 codecparam, int32
 		}
 
 	} else if (par1 == 6) {
-		// Opcode 6: Level setup / mode switch (FUN_41CADB case 4)
 		iactRebel2Opcode6(renderBitmap, b, size, par2, par3, par4);
 	} else if (par1 == 8) {
-		// Opcode 8: HUD resource loading (FUN_41CADB case 6)
 		iactRebel2Opcode8(renderBitmap, b, size, par2, par3, par4);
 	} else if (par1 == 9) {
 		// Opcode 9: Text/subtitle display
@@ -336,9 +280,7 @@ void InsaneRebel2::iactRebel2Scene1(byte *renderBitmap, int32 codecparam, int32
 	}
 }
 
-// iactRebel2Opcode2 -- Link table and state setup (FUN_00407fcb).
 void InsaneRebel2::iactRebel2Opcode2(Common::SeekableReadStream &b, int16 par2, int16 par3, int16 par4) {
-	// Handle IACT opcode 2 subcases based on par3 (type). Mirrors FUN_00407fcb behavior where relevant.
 	// Keep existing linking behavior (par3 == 4) for compatibility.
 
 	// Link case: par3 == 4
@@ -347,10 +289,8 @@ void InsaneRebel2::iactRebel2Opcode2(Common::SeekableReadStream &b, int16 par2,
 		int16 parentId = b.readSint16LE(); // Offset +10
 
 		// Validate BOTH parentId AND childId to avoid triggering "set/clear ALL bits" behavior
-		// when childId <= 0. The original game's setBit(0)/clearBit(0) affects ALL bits,
 		// which would disable/enable all enemies at once - not the intended linking behavior.
 		if (parentId >= 1 && parentId < 512 && childId >= 1 && childId < 512) {
-			// Shift links (original: 4 link slots at DAT_0045797c/817c/897c/917c)
 			_rebelLinks[parentId][2] = _rebelLinks[parentId][1];
 			_rebelLinks[parentId][1] = _rebelLinks[parentId][0];
 			_rebelLinks[parentId][0] = childId;
@@ -358,8 +298,6 @@ void InsaneRebel2::iactRebel2Opcode2(Common::SeekableReadStream &b, int16 par2,
 			// Mirror parent's bit state to child (INVERTED):
 			// - Parent alive (bit clear) ??? setBit(child) ??? child hidden
 			// - Parent dead (bit set) ??? clearBit(child) ??? child shown
-			// From FUN_0041CADB case 0, par3==4:
-			//   bVar3 = FUN_00423970(parentId);
 			//   if (bVar3 == 0) setBit(childId); else clearBit(childId);
 			// This ensures linked children (explosion/death sprites) are hidden
 			// while the parent is alive, and revealed when the parent is destroyed.
@@ -375,21 +313,17 @@ void InsaneRebel2::iactRebel2Opcode2(Common::SeekableReadStream &b, int16 par2,
 		}
 		return;
 	} else if (par3 == 1) { // Probabilistic / counter cases: par3 == 1
-		int16 value = par4; // sVar6
-		int16 targetId = b.readSint16LE(); // Offset +8 (sVar7)
+		int16 value = par4;
+		int16 targetId = b.readSint16LE();
 
 		// Validate targetId >= 1 to avoid triggering "set/clear ALL bits" behavior
-		// The original game's setBit(0)/clearBit(0) affects ALL bits, not intended here
 		if (targetId < 1 || targetId >= 0x200)
 			return;
 
-		// Handler 8/25: FUN_401234 case 0 / FUN_0041CADB case 0 par3==1
-		// From original FUN_0041CADB:
 		//   if (par4 == 100) clearBit(body0);  // Force enable
 		//   else { bitMask = 1 << (par4 & 0x1f); if (waveState & bitMask) setBit(body0); }
 		if ((_rebelHandler == 8 || _rebelHandler == 25) && value != 0) {
 			if (value == 100) {
-				// par4==100: Force enable the target (original: FUN_00423a00)
 				clearBit(targetId);
 				debugC(DEBUG_INSANE, "Opcode2 (H%d): Force ENABLE target=%d (par4=100)", _rebelHandler, targetId);
 			} else {
@@ -460,41 +394,19 @@ void InsaneRebel2::iactRebel2Opcode2(Common::SeekableReadStream &b, int16 par2,
 	}
 }
 
-// iactRebel2Opcode3 -- Damage and hit counter processing (FUN_4092D9 / FUN_40E35E / FUN_401234).
 void InsaneRebel2::iactRebel2Opcode3(Common::SeekableReadStream &b, int16 par2, int16 par3, int16 par4) {
 	// IACT opcode 3 ??? damage and hit counter processing.
-	// Based on FUN_4092D9 (Handler 0x26), FUN_40E35E (Handler 7), FUN_401234 (Handler 8).
-	//
-	// The common dispatcher (FUN_4033CF) stores opcode 3 entries in the projectile impact
-	// list (DAT_0043f9e0). For handlers 0x26/7 these are processed per-frame by the
-	// per-handler collision function (FUN_4092D9/FUN_40E35E). For handlers 8/25 they're
 	// processed immediately during IACT dispatch.
-	//
-	// FUN_403ba9() loop in FUN_4092D9 (lines 209-239):
 	//   par3 == 1/2: Direct hit ??? increment hit counter, apply damage if conditions met
-	//     - body[0] (offset +8): srcId for isBitSet check
-	//     - par4 != 0: damage from DAT_0047e0f4 (direct hit damage table)
 	//     - par3==1: par4 must be 1..9 for damage
 	//     - par3==2: par4 must be > 99, with wave state bit check for par4 >= 101
-	//
-	//   par3 == 5: Probabilistic damage ??? probability check from DAT_0047e0fc
-	//     - body[1] (offset +10): srcId for isBitSet check (different from par3=1/2!)
-	//     - Damage from DAT_0047e0f8 (probabilistic damage table)
-	//
-	// Stream position on entry: at offset +8 (body[0], first word after 8-byte header)
-
-	// Handler 25 has a different opcode 3 structure (FUN_41CADB case 1):
-	//   par3==5: probabilistic damage WITH cover check (DAT_0045790a < 2)
 	//   par3==1: increment hit counter ONLY (NO damage), requires par4 != 4
 	//   par4==100: direct damage (separate check after par3 branches, NO cover check)
-	// Other handlers (0x26/7/8) use FUN_4092D9/FUN_40E35E/FUN_401234 with different logic.
 	if (_rebelHandler == 25) {
-		// Handler 25 opcode 3 ??? FUN_41CADB case 1
 		int16 srcIdBody0 = b.readSint16LE(); // body[0] (offset +8)
 		int16 srcIdBody1 = b.readSint16LE(); // body[1] (offset +10)
 
 		if (par3 == 5) {
-			// Probabilistic damage with cover check (lines 81-92)
 			debugC(DEBUG_INSANE, "Opcode3: H25 par3=5 srcId=%d isBitSet=%d damageLevel=%d",
 				srcIdBody1, isBitSet(srcIdBody1), _rebelDamageLevel);
 
@@ -518,7 +430,6 @@ void InsaneRebel2::iactRebel2Opcode3(Common::SeekableReadStream &b, int16 par2,
 					_rebelDamageLevel, isBitSet(srcIdBody1));
 			}
 		} else if (par3 == 1 && !isBitSet(srcIdBody0) && par4 != 4) {
-			// Hit counter only ??? NO damage (lines 94-98)
 			_rebelHitCounter++;
 			debugC(DEBUG_INSANE, "H25 hit counter++ -> %d (par3=1 par4=%d, no damage)",
 				_rebelHitCounter, par4);
@@ -526,7 +437,6 @@ void InsaneRebel2::iactRebel2Opcode3(Common::SeekableReadStream &b, int16 par2,
 			debugC(DEBUG_INSANE, "Opcode3: H25 par3=%d par4=%d (no action)", par3, par4);
 		}
 
-		// Direct damage: par4==100, separate from par3 branches (lines 99-111)
 		if (par4 == 100 && !isBitSet(srcIdBody0)) {
 			LevelDifficultyParams dparams = getDifficultyParams();
 			int directHitDamage = (dparams.missDamage >= 0) ? dparams.missDamage : 0;
@@ -538,7 +448,6 @@ void InsaneRebel2::iactRebel2Opcode3(Common::SeekableReadStream &b, int16 par2,
 				initDamageFlash();
 		}
 	} else if (par3 == 1 || par3 == 2) {
-		// Non-Handler-25 direct hit path ??? FUN_4092D9 lines 209-227
 		int16 srcId = b.readSint16LE(); // body[0] (offset +8): source enemy ID
 
 		debugC(DEBUG_INSANE, "Opcode3: par3=%d par4=%d srcId=%d isBitSet=%d",
@@ -573,7 +482,6 @@ void InsaneRebel2::iactRebel2Opcode3(Common::SeekableReadStream &b, int16 par2,
 			}
 		}
 	} else if (par3 == 5) {
-		// Non-Handler-25 probabilistic damage ??? FUN_4092D9 lines 228-239
 		b.skip(2); // Skip body[0]
 		int16 srcId = b.readSint16LE(); // body[1] (offset +10)
 
@@ -606,9 +514,7 @@ void InsaneRebel2::iactRebel2Opcode3(Common::SeekableReadStream &b, int16 par2,
 	}
 }
 
-// Helper split out of FUN_004033CF case 6; not a separate original function.
 void InsaneRebel2::updateOpcode6Handler(int16 par2) {
-	// Update handler type if par2 is a known handler value (from FUN_4033CF case 6).
 	if (par2 == 7 || par2 == 8 || par2 == 0x19 || par2 == 0x26) {
 		// Reset Level 2 background flag when transitioning away from Handler 8
 		if (_rebelHandler == 8 && par2 != 8) {
@@ -619,13 +525,9 @@ void InsaneRebel2::updateOpcode6Handler(int16 par2) {
 	}
 }
 
-// Helper split out of FUN_00401234 case 4; not a separate original function.
 void InsaneRebel2::handleOpcode6Handler8(Common::SeekableReadStream &b, int16 par4) {
-	// Handler 8 specific logic (third-person on foot) - FUN_00401234 case 4.
-	// DAT_0043e000 = local_14[3], which maps to the IACT header's par4/userId.
 	_shipLevelMode = par4;
 
-	// local_14[4] is the first body word after the 8-byte IACT header.
 	int16 bodyStatusFlag = 0;
 	if (b.pos() + 2 <= b.size()) {
 		int64 savedPos = b.pos();
@@ -633,7 +535,6 @@ void InsaneRebel2::handleOpcode6Handler8(Common::SeekableReadStream &b, int16 pa
 		b.seek(savedPos);
 	}
 
-	// If local_14[4] == 1, enable status bar and re-render laser texture (FUN_0040bb87)
 	if (bodyStatusFlag == 1) {
 		_rebelStatusBarSprite = 5;
 		if (_smush_iconsNut && _smush_iconsNut->getNumChars() > 5) {
@@ -641,7 +542,6 @@ void InsaneRebel2::handleOpcode6Handler8(Common::SeekableReadStream &b, int16 pa
 		}
 	}
 
-	// Reset state when shipLevelMode != 0 && local_14[4] == 1 (FUN_00401234 lines 97-103)
 	// Guard with _rebelOp6Initialized: runs once per wave video, not per frame.
 	if (_shipLevelMode != 0 && bodyStatusFlag == 1 && !_rebelOp6Initialized) {
 		clearBit(0);
@@ -657,8 +557,6 @@ void InsaneRebel2::handleOpcode6Handler8(Common::SeekableReadStream &b, int16 pa
 
 	// Skip position calculation for special modes 4 and 5
 	if (_shipLevelMode != 4 && _shipLevelMode != 5) {
-		// ----- Movement Range Transition (Covered vs Shooting) -----
-		// Based on FUN_00401234 lines 85-120:
 		// Mode 2 = "Covered" state - contract movement range to 41 (0x29)
 		// Other modes = "Shooting" state - expand movement range to 127 (0x7f)
 		// Transition happens gradually at +/-10 per frame for smooth animation
@@ -682,9 +580,6 @@ void InsaneRebel2::handleOpcode6Handler8(Common::SeekableReadStream &b, int16 pa
 
 		// Calculate target position from mouse input
 		// Mouse X maps to ship horizontal tilt, Mouse Y to vertical tilt
-		// Based on FUN_00401234 lines 151-166:
-		// local_18 = ((DAT_0047a7e0 * 5 + 0x27b) * 0x40) / 0xfe
-		// local_1c = ((DAT_0047a7e2 * 5 + 0x27b) * 0x10) / 0xfe
 
 		// Map the effective aim position (-127 to 127 range) to the ship target.
 		Common::Point aimPos = getGameplayAimPoint();
@@ -692,7 +587,6 @@ void InsaneRebel2::handleOpcode6Handler8(Common::SeekableReadStream &b, int16 pa
 		int16 mouseOffsetY = (int16)((aimPos.y - 100) * 127 / 100);
 
 		// Clamp X offset to movement range limit (covered/shooting state)
-		// Based on FUN_00401234 lines 119-136
 		if (mouseOffsetX > _movementRangeLimit)
 			mouseOffsetX = _movementRangeLimit;
 		if (mouseOffsetX < -_movementRangeLimit)
@@ -703,13 +597,6 @@ void InsaneRebel2::handleOpcode6Handler8(Common::SeekableReadStream &b, int16 pa
 		if (mouseOffsetY < -127)
 			mouseOffsetY = -127;
 
-		// Calculate target positions using the original formula
-		// Original FUN_00401234 lines 151-166:
-		//   local_18 = ((mouseX * 5 + 0x27b) * 0x40) / 0xfe    -> X target
-		//   local_1c = ((mouseY * 5 + 0x27b) * 0x10) / 0xfe    -> Y target
-		//   _DAT_0043e004 = -local_1c   (stored negated for cursor display)
-		// The interpolation (lines 181-193) uses local_1c (positive), NOT _DAT_0043e004.
-		// So the interpolation target must be the positive formula result.
 		_shipTargetX = (int16)(((mouseOffsetX * 5 + 0x27b) * 0x40) / 0xfe);
 		_shipTargetY = (int16)(((mouseOffsetY * 5 + 0x27b) * 0x10) / 0xfe);
 
@@ -731,10 +618,7 @@ void InsaneRebel2::handleOpcode6Handler8(Common::SeekableReadStream &b, int16 pa
 			_shipPosY = (newY < _shipTargetY) ? _shipTargetY : newY;
 		}
 
-		// FUN_00401234 calls FUN_00424510(-DAT_0043e006, -DAT_0043e008)
-		// after updating the handler-8 camera. This shifts subsequent FOBJ
-		// decoding into screen coordinates; FUN_00401CCF then draws HUD and
-		// weapon sprites without a separate final-buffer scroll.
+		// Shift subsequent FOBJ weapon sprites after moving the handler-8 camera.
 		if (_player) {
 			_player->_fobjOffsetX = -_shipPosX;
 			_player->_fobjOffsetY = -_shipPosY;
@@ -785,7 +669,6 @@ void InsaneRebel2::handleOpcode6Handler8(Common::SeekableReadStream &b, int16 pa
 	}
 
 	// Update firing state from mouse button or joystick fire action
-	// Mode 4 (autopilot) disables shooting - FUN_00401CCF line 82-84
 	if (_shipLevelMode == 4) {
 		_shipFiring = false;
 	} else {
@@ -798,12 +681,9 @@ void InsaneRebel2::handleOpcode6Handler8(Common::SeekableReadStream &b, int16 pa
 		_shipDirectionH, _shipDirectionV, _shipDirectionIndex);
 }
 
-// Helper split out of FUN_0040C3CC case 4; not a separate original function.
 void InsaneRebel2::handleOpcode6Handler7(Common::SeekableReadStream &b, int16 par4) {
-	// Handler 7 specific logic (third-person ship) - FUN_0040d836 / FUN_0040c3cc
 	// Used for Level 3 and similar space combat levels.
 
-	// Set control mode: DAT_004437c0 = param_5[3] = par4 in FUN_40C3CC case 4.
 	// This determines collision mode and shooting capability:
 	//   Mode 0: Obstacle avoidance - SECONDARY zones, corridor boundaries
 	//   Mode 1: Tunnel flight - PRIMARY zones, per-edge push-back (hMargin=0x28)
@@ -813,10 +693,6 @@ void InsaneRebel2::handleOpcode6Handler7(Common::SeekableReadStream &b, int16 pa
 	debugC(DEBUG_INSANE, "Opcode 6 (Handler 7): Control mode set to %d (shooting %s)",
 		par4, (par4 == 2) ? "ENABLED" : "DISABLED");
 
-	// Status bar: param_5[4] == 1 in original (first body word, 5th IACT word)
-	// In our parsing, par3 maps to param_5[2] and the body follows par4.
-	// FUN_40C3CC: if (param_5[4] == 1) FUN_0040bb87(DAT_0047a828,5);
-	// par3 is param_5[2], which the original doesn't use here.
 	// The body word for status bar is read separately below.
 	int16 bodyStatusFlag = 0;
 	if (b.size() - b.pos() >= 2) {
@@ -830,31 +706,23 @@ void InsaneRebel2::handleOpcode6Handler7(Common::SeekableReadStream &b, int16 pa
 		debugC(DEBUG_INSANE, "Opcode 6 (Handler 7): Status bar enabled (body flag=%d)", bodyStatusFlag);
 	}
 
-	// Ship position update - FUN_40C3CC case 4, lines 49-327.
 	// Velocity-based physics with momentum/inertia:
 	//   Mouse offset from center -> scaled input [-127,127]
 	//   -> velocity history averaging -> physics delta (clamped +/-12/frame)
 	//   -> position clamping -> corridor collision -> perspective offsets
-	//
-	// Level data table (DAT_0047e0e8 + difficulty*0x242 + levelType*0x22):
 	//   offset 2: Y speed          offset 4: X speed (levelSpeed)
 	//   offset 6: wind multiplier
-	// Our extracted difficulty table starts at DAT_0047e0f0, so for Handler 7
 	// level types these fields map to lift/slide/drift of the preceding row.
 	const int flightParamIndex = CLIP(_rebelLevelType - 1, 0, 16);
 	const LevelDifficultyParams &flightParams =
 		kDifficultyTable[CLIP(_difficulty, 0, 5)][flightParamIndex];
 
-	// Step 1: Raw mouse input as offset from screen center.
-	// DAT_0047a7e0 = mouseX - 160, DAT_0047a7e2 = mouseY - 100.
-	// Handler 7 applies DAT_0047a7fe to its local vertical input after clamping.
 	const Common::Point aimPos = getGameplayAimPoint();
 	const int16 mouseX = aimPos.x;
 	const int16 mouseY = aimPos.y;
-	int16 inputX = (int16)(mouseX - 160);  // DAT_0047a7e0
-	int16 inputY = (int16)(mouseY - 100);  // DAT_0047a7e2
+	int16 inputX = (int16)(mouseX - 160);
+	int16 inputY = (int16)(mouseY - 100);
 
-	// Clamp: mouse mode uses [-160, 160] for X, [-127, 127] for Y (lines 55-70).
 	if (inputX > 160)
 		inputX = 160;
 	if (inputX < -160)
@@ -864,10 +732,8 @@ void InsaneRebel2::handleOpcode6Handler7(Common::SeekableReadStream &b, int16 pa
 	if (inputY < -127)
 		inputY = -127;
 
-	// Step 2: Scale to [-127, 127] (lines 82-84).
-	// Mouse mode: scaledInputX = (DAT_0047a7e0 * 0x7f) / 0xa0.
 	int16 scaledInputX = (int16)((inputX * 127) / 160);
-	int16 scaledInputY = _optControlsFlipped ? (int16)-inputY : inputY;  // local_14
+	int16 scaledInputY = _optControlsFlipped ? (int16)-inputY : inputY;
 
 	const bool useDirectGamepadFlight = _gamepadAimActive && _selectedLevel == 10;
 
@@ -892,7 +758,6 @@ void InsaneRebel2::handleOpcode6Handler7(Common::SeekableReadStream &b, int16 pa
 		mouseFlightTargetX = CLIP<int16>(mouseFlightTargetX, 0x14, 0x194);
 	}
 
-	// Step 3: Velocity history + smoothed average (lines 141-157).
 	for (int i = 24; i > 0; i--) {
 		_velocityHistory[i] = _velocityHistory[i - 1];
 	}
@@ -903,9 +768,8 @@ void InsaneRebel2::handleOpcode6Handler7(Common::SeekableReadStream &b, int16 pa
 	for (int i = 0; i < smoothWindow; i++) {
 		velSum += _velocityHistory[i];
 	}
-	_smoothedVelocity = (int16)(velSum / smoothWindow);  // DAT_0044370c
+	_smoothedVelocity = (int16)(velSum / smoothWindow);
 
-	// Step 4: Wind history (lines 158-173).
 	const int16 windMult = flightParams.driftRate;
 	int windSumX = 0, windSumY = 0;
 	for (int i = 14; i > 0; i--) {
@@ -922,14 +786,12 @@ void InsaneRebel2::handleOpcode6Handler7(Common::SeekableReadStream &b, int16 pa
 	_windHistoryY[0] = _windParamY;
 	int16 windEffectY = (int16)((windMult * (windSumY + _windParamY)) / 15);
 
-	// Step 5: Position delta (lines 174-242).
 	const int16 levelSpeed = flightParams.slideRate;
 	const int16 levelYSpeed = flightParams.liftRate;
 	int16 absSmoothVel = ABS(_smoothedVelocity);
 	int16 positionDeltaX;
 
 	if (_flyControlMode == 1) {
-		// Mode 1: Full cross-axis coupling (lines 174-186).
 		// Banking: vertical input deflects horizontal movement.
 		if (scaledInputX < 1) {
 			positionDeltaX = (int16)((levelSpeed * _smoothedVelocity - absSmoothVel * scaledInputY - windEffectX) >> 9);
@@ -937,7 +799,6 @@ void InsaneRebel2::handleOpcode6Handler7(Common::SeekableReadStream &b, int16 pa
 			positionDeltaX = (int16)((levelSpeed * _smoothedVelocity + absSmoothVel * scaledInputY - windEffectX) >> 9);
 		}
 	} else {
-		// Mode 0/2/3: Reduced cross-axis coupling (lines 218-230).
 		if (scaledInputX < 1) {
 			positionDeltaX = (int16)((levelSpeed * _smoothedVelocity - (absSmoothVel * scaledInputY >> 2) - windEffectX) >> 9);
 		} else {
@@ -945,13 +806,11 @@ void InsaneRebel2::handleOpcode6Handler7(Common::SeekableReadStream &b, int16 pa
 		}
 	}
 
-	// Clamp X delta to +/-12 per frame (lines 187-192 / 231-236).
 	if (positionDeltaX < -11)
 		positionDeltaX = -12;
 	if (positionDeltaX > 11)
 		positionDeltaX = 12;
 
-	// FUN_0040C3CC integrates relative flight axes. The real mouse is an
 	// absolute position, so steer toward a bounded position target instead of
 	// letting a held off-center cursor keep pushing the ship until it bounces.
 	if (useTargetSteering) {
@@ -962,12 +821,10 @@ void InsaneRebel2::handleOpcode6Handler7(Common::SeekableReadStream &b, int16 pa
 			positionDeltaX = (targetDeltaX < 0) ? -1 : 1;
 	}
 
-	// Apply X delta (line 193 / 237).
 	_flyShipScreenX += positionDeltaX;
 
 	// Y delta.
 	if (_flyControlMode == 1) {
-		// Mode 1: clamped to +/-12 with wind (lines 194-216).
 		int yCalc = levelYSpeed * scaledInputY - (windEffectY >> 1);
 		int yDelta = yCalc >> 10;
 		if (yDelta < -12)
@@ -976,17 +833,13 @@ void InsaneRebel2::handleOpcode6Handler7(Common::SeekableReadStream &b, int16 pa
 			yDelta = 12;
 		_flyShipScreenY -= (int16)yDelta;
 	} else {
-		// Mode 0/2/3: unclamped (lines 238-241).
 		_flyShipScreenY -= (int16)((levelYSpeed * scaledInputY) >> 10);
 	}
 
-	// Store vertical input for direction sprite (line 243).
-	_verticalInput = scaledInputY;  // DAT_0044370e
+	_verticalInput = scaledInputY;
 
-	// Ship facing direction (line 244).
 	_facingRight = (0xd4 < _smoothedVelocity + _flyShipScreenX);
 
-	// Step 6: Position clamping (lines 245-256).
 	if (_flyShipScreenX > 0x194)
 		_flyShipScreenX = 0x194;  // 404
 	if (_flyShipScreenY > 0xF0)
@@ -996,13 +849,10 @@ void InsaneRebel2::handleOpcode6Handler7(Common::SeekableReadStream &b, int16 pa
 	if (_flyShipScreenY < 0x14)
 		_flyShipScreenY = 0x14;    // 20
 
-	// Step 7: Corridor collision - mode 0/2 only (lines 257-292).
 	if (_flyControlMode == 0 || _flyControlMode == 2) {
 		LevelDifficultyParams wallParams = getDifficultyParams();
 		int corridorWallDmg = (wallParams.dodgeDamage >= 0) ? wallParams.dodgeDamage : 0;
 
-		// Right boundary (lines 258-270).
-		// Original: position is ALWAYS clamped; damage/bounce only when cooldown < 5.
 		if (_corridorRightX < _flyShipScreenX) {
 			_flyShipScreenX = _corridorRightX;
 			if (_hitCooldown < 5) {
@@ -1018,7 +868,6 @@ void InsaneRebel2::handleOpcode6Handler7(Common::SeekableReadStream &b, int16 pa
 			}
 		}
 
-		// Left boundary (lines 271-283).
 		if (_flyShipScreenX < _corridorLeftX) {
 			_flyShipScreenX = _corridorLeftX;
 			if (_hitCooldown < 5) {
@@ -1034,7 +883,6 @@ void InsaneRebel2::handleOpcode6Handler7(Common::SeekableReadStream &b, int16 pa
 			}
 		}
 
-		// Y boundary clamping - no damage (lines 285-292).
 		if (_corridorBottomY < _flyShipScreenY) {
 			_flyShipScreenY = _corridorBottomY;
 		}
@@ -1043,10 +891,7 @@ void InsaneRebel2::handleOpcode6Handler7(Common::SeekableReadStream &b, int16 pa
 		}
 	}
 
-	// Step 8: Perspective offsets (lines 293-316).
 	// f(x) = (focal * center * |offset|) / ((center - focal) * |offset| + focal * center)
-	// Close view (DAT_0047a7fc < 1): focalX=0x34, focalY=0x2d.
-	// Far view (DAT_0047a7fc >= 1): focalX=0x2b, focalY=0x19.
 	{
 		int absOffX = ABS(_flyShipScreenX - 0xd4);
 		int16 focalX = 0x2b;  // Far view default for Level 3
@@ -1071,14 +916,12 @@ void InsaneRebel2::handleOpcode6Handler7(Common::SeekableReadStream &b, int16 pa
 			_perspectiveY = -_perspectiveY;
 	}
 
-	// View shift = clamped smoothed velocity (FUN_0040d836 lines 68-74).
 	_viewShift = _smoothedVelocity;
 	if (_viewShift > 127)
 		_viewShift = 127;
 	if (_viewShift < -127)
 		_viewShift = -127;
 
-	// Step 9: Direction sprite (FUN_0040d836 lines 88-106).
 	// 5x7 grid: vDir(0-4) * 7 + hDir(0-6) = sprite index (0-34).
 	// vDir from vertical input: (0xa0 - verticalInput) >> 6.
 	int16 vDir = (int16)(((int)(0xa0 - _verticalInput) + ((0xa0 - _verticalInput) < 0 ? 63 : 0)) >> 6);
@@ -1094,7 +937,6 @@ void InsaneRebel2::handleOpcode6Handler7(Common::SeekableReadStream &b, int16 pa
 	if (hDir > 6)
 		hDir = 6;
 
-	// Hysteresis at center (lines 90-97, 98-105).
 	if (hDir == 3 && ABS(_smoothedVelocity) > 10) {
 		hDir = (_smoothedVelocity < 1) ? 4 : 2;
 	}
@@ -1120,14 +962,10 @@ void InsaneRebel2::handleOpcode6Handler7(Common::SeekableReadStream &b, int16 pa
 		useTargetSteering ? 1 : 0, _gamepadAimActive ? 1 : 0);
 }
 
-// Helper split out of FUN_0041CADB case 4; not a separate original function.
 void InsaneRebel2::handleOpcode6Handler25(byte *renderBitmap, Common::SeekableReadStream &b, int16 par2, int16 par3, int16 par4) {
 	// Handler 25 (0x19) specific logic (mixed mode - speeder bike).
-	// Based on FUN_0041cadb case 4 (opcode 6) lines 113-229.
 
-	// Read the reset flag from IACT data at offset 8-9 (local_14[4] in decompiled code).
 	// The stream position should be at offset 8 after par4 was read.
-	// From FUN_0041cadb line 114: if (local_14[4] == 1) { ... reset ... }
 	int16 par5 = 0;
 	if (b.pos() + 2 <= b.size()) {
 		int64 savedPos = b.pos();
@@ -1135,8 +973,6 @@ void InsaneRebel2::handleOpcode6Handler25(byte *renderBitmap, Common::SeekableRe
 		b.seek(savedPos);  // Don't consume the stream
 	}
 
-	// If par5 == 1, enable status bar and reset state (lines 114-121).
-	// Note: This is local_14[4] in the decompiled code, NOT local_14[3] (par4).
 	if (par5 == 1) {
 		_rebelStatusBarSprite = 5;
 		if (_smush_iconsNut && _smush_iconsNut->getNumChars() > 5) {
@@ -1158,20 +994,15 @@ void InsaneRebel2::handleOpcode6Handler25(byte *renderBitmap, Common::SeekableRe
 		}
 	}
 
-	// Set sprite mode (DAT_00457900 = local_14[3]) - controls which GRD sprite to render.
-	// From FUN_0041cadb line 122: DAT_00457900 = local_14[3].
-	// In this IACT parser: local_14[3] = offset 6-7 = par4.
 	// Mode 1: Uncovered, shooting position - sprite on left
 	// Mode 2: Covered, vertical shift
 	// Mode 3: Transition between covered/uncovered - sprite position depends on direction
 	// Mode 4: Alternative uncovered position - sprite on right
-	_grdSpriteMode = par4;  // local_14[3] maps to par4 (offset 6-7)
+	_grdSpriteMode = par4;
 
 	debugC(DEBUG_INSANE, "Handler25 Opcode6: par2=%d par3=%d par4=%d(mode) par5=%d(reset) autopilot=%d damageLevel=%d controlMode=%d",
 		par2, par3, par4, par5, _rebelAutopilot, _rebelDamageLevel, _rebelControlMode);
 
-	// Autopilot logic (lines 123-146).
-	// From original FUN_0041cadb - NO damageLevel check, toggle happens immediately.
 	// The damage level counter provides the smooth visual transition.
 	if (!_rebelAutoPlay) {
 		if (_rebelAutopilot == 0) {
@@ -1204,7 +1035,6 @@ void InsaneRebel2::handleOpcode6Handler25(byte *renderBitmap, Common::SeekableRe
 		}
 	}
 
-	// Update damage level counter (lines 147-154).
 	// This provides the smooth transition animation between covered/uncovered states.
 	int prevDamageLevel = _rebelDamageLevel;
 	if (_rebelAutopilot == 0) {
@@ -1223,7 +1053,6 @@ void InsaneRebel2::handleOpcode6Handler25(byte *renderBitmap, Common::SeekableRe
 			prevDamageLevel, _rebelDamageLevel, _rebelAutopilot);
 	}
 
-	// Flight direction logic for mode 3 (lines 155-177).
 	if (_grdSpriteMode == 3) {
 		if (_rebelDamageLevel == 5) {
 			// At max damage, check for direction change input.
@@ -1242,45 +1071,38 @@ void InsaneRebel2::handleOpcode6Handler25(byte *renderBitmap, Common::SeekableRe
 		_rebelFlightDir = 0;
 	}
 
-	// Calculate sprite and view offset positions based on mode (lines 182-213).
-	// DAT_0045790c = view offset X (for corridor overlay)
-	// DAT_0045790e = view offset Y (for corridor overlay)
-	// DAT_00457910 = sprite position X (relative to center)
-	// DAT_00457912 = sprite position Y (relative to center)
 	if (_grdSpriteMode == 1) {
 		// Mode 1: Uncovered, shooting - sprite shifts left as damage increases.
 		_rebelViewMode1 = 0x0e;
 		_rebelViewMode2 = 0;
-		_rebelViewOffsetX = _rebelDamageLevel * -5 + -14;   // DAT_0045790c
-		_rebelViewOffset2X = _rebelDamageLevel * -22;       // DAT_00457910
-		_rebelViewOffsetY = 0;                              // DAT_0045790e
-		_rebelViewOffset2Y = 0;                             // DAT_00457912
+		_rebelViewOffsetX = _rebelDamageLevel * -5 + -14;
+		_rebelViewOffset2X = _rebelDamageLevel * -22;
+		_rebelViewOffsetY = 0;
+		_rebelViewOffset2Y = 0;
 	} else if (_grdSpriteMode == 4) {
 		// Mode 4: Alternative uncovered - sprite shifts right.
 		_rebelViewMode1 = 0x22;
 		_rebelViewMode2 = 0;
-		_rebelViewOffsetX = _rebelDamageLevel * 10 + -16;   // DAT_0045790c
-		_rebelViewOffset2X = _rebelDamageLevel * 17 + -85;  // DAT_00457910 (0x11 = 17, -0x55 = -85)
+		_rebelViewOffsetX = _rebelDamageLevel * 10 + -16;
+		_rebelViewOffset2X = _rebelDamageLevel * 17 + -85;
 		_rebelViewOffsetY = 0;
 		_rebelViewOffset2Y = 0;
 	} else if (_grdSpriteMode == 2) {
 		// Mode 2: Covered - vertical shift.
 		_rebelViewMode1 = 0;
 		_rebelViewMode2 = 0x0e;
-		_rebelViewOffsetY = _rebelDamageLevel * -5 + -14;   // DAT_0045790e
-		_rebelViewOffset2Y = (5 - _rebelDamageLevel) * 15 + -60;  // DAT_00457912 (0xf = 15, -0x3c = -60)
+		_rebelViewOffsetY = _rebelDamageLevel * -5 + -14;
+		_rebelViewOffset2Y = (5 - _rebelDamageLevel) * 15 + -60;
 		_rebelViewOffsetX = 0;
 		_rebelViewOffset2X = 0;
 	} else if (_grdSpriteMode == 3) {
 		// Mode 3: Transition - direction-dependent horizontal shift.
 		_rebelViewMode1 = 0x0f;
 		_rebelViewMode2 = 0;
-		// (-(DAT_00457902 == 0) & 6) - 3 = if dir==0: 6-3=3, else 0-3=-3
 		int16 dirMultX = (_rebelFlightDir == 0) ? 3 : -3;
-		// (-(DAT_00457902 == 0) & 0x28) - 0x14 = if dir==0: 40-20=20, else 0-20=-20
 		int16 dirMultX2 = (_rebelFlightDir == 0) ? 20 : -20;
-		_rebelViewOffsetX = dirMultX * (5 - _rebelDamageLevel) + -15;  // DAT_0045790c
-		_rebelViewOffset2X = dirMultX2 * (5 - _rebelDamageLevel);      // DAT_00457910
+		_rebelViewOffsetX = dirMultX * (5 - _rebelDamageLevel) + -15;
+		_rebelViewOffset2X = dirMultX2 * (5 - _rebelDamageLevel);
 		_rebelViewOffsetY = 0;
 		_rebelViewOffset2Y = 0;
 	} else {
@@ -1298,25 +1120,19 @@ void InsaneRebel2::handleOpcode6Handler25(byte *renderBitmap, Common::SeekableRe
 		_grdSpriteMode, _rebelDamageLevel, _rebelFlightDir, _rebelAutopilot,
 		_rebelViewOffsetX, _rebelViewOffsetY, _rebelViewOffset2X, _rebelViewOffset2Y);
 
-	// Set FOBJ position offsets (FUN_00424510 in original, line 214).
 	// All subsequent FOBJs in this frame will be shifted by these offsets.
 	if (_player) {
 		_player->_fobjOffsetX = _rebelViewOffsetX;
 		_player->_fobjOffsetY = _rebelViewOffsetY;
 	}
 
-	// Draw corridor overlay opaquely (FUN_00428a10 in original, line 216).
 	// This wipes previous frame content so codec 23 delta skip regions show clean corridor.
 	drawHandler25CorridorOverlay(renderBitmap);
 }
 
-// Helper split out of FUN_00407FCB case 4; not a separate original function.
 void InsaneRebel2::handleOpcode6Turret(Common::SeekableReadStream &b, int16 par4) {
-	// Handler 0x26: FUN_407FCB line 77-79 - set level type from par4, read par5 for init trigger.
-	// param_5[3] = par4 = levelType, param_5[4] = par5 = init flag.
 	_rebelLevelType = par4;
 
-	// Read par5 from IACT body (param_5[4]).
 	int16 par5 = 0;
 	if (b.pos() + 2 <= b.size()) {
 		int64 savedPos = b.pos();
@@ -1325,7 +1141,6 @@ void InsaneRebel2::handleOpcode6Turret(Common::SeekableReadStream &b, int16 par4
 	}
 
 	if (par5 == 1) {
-		// Re-render laser texture for this level (FUN_0040bb87).
 		// levelType 5 uses sprite 53, all others use sprite 5.
 		_rebelStatusBarSprite = (_rebelLevelType == 5) ? 53 : 5;
 		if (_smush_iconsNut && _smush_iconsNut->getNumChars() > _rebelStatusBarSprite) {
@@ -1348,7 +1163,6 @@ void InsaneRebel2::handleOpcode6Turret(Common::SeekableReadStream &b, int16 par4
 	}
 }
 
-// Helper split out of generic opcode 6 initialization; not a separate original function.
 void InsaneRebel2::handleOpcode6GenericInit(int16 par4) {
 	// Other handlers: par4 == 1 triggers init (NOT level type).
 	if (_rebelHandler != 0x26 && par4 == 1) {
@@ -1372,9 +1186,7 @@ void InsaneRebel2::handleOpcode6GenericInit(int16 par4) {
 	}
 }
 
-// Helper split out of generic opcode 6 flight state; not a separate original function.
 void InsaneRebel2::updateOpcode6GenericFlightState() {
-	// Step 3: Autopilot/control mode logic (lines 123-146)
 	// This determines whether the ship flies on autopilot or manual control.
 	if (!_rebelAutoPlay) {
 		// Normal mode: check control mode flags.
@@ -1401,7 +1213,6 @@ void InsaneRebel2::updateOpcode6GenericFlightState() {
 		}
 	}
 
-	// Step 4: Update damage level counter (lines 147-154).
 	if (_rebelAutopilot == 0) {
 		if (_rebelDamageLevel > 0) {
 			_rebelDamageLevel--;
@@ -1412,7 +1223,6 @@ void InsaneRebel2::updateOpcode6GenericFlightState() {
 		}
 	}
 
-	// Handle level type 3 special direction logic (lines 155-181).
 	if (_rebelLevelType == 3) {
 		if (_rebelDamageLevel == 5) {
 			// Check for joystick/key input to change direction.
@@ -1429,7 +1239,6 @@ void InsaneRebel2::updateOpcode6GenericFlightState() {
 		_rebelFlightDir = 0;
 	}
 
-	// Step 5: Calculate view offsets based on level type (lines 182-213).
 	switch (_rebelLevelType) {
 	case 1:
 		// Type 1: Vertical movement.
@@ -1466,8 +1275,8 @@ void InsaneRebel2::updateOpcode6GenericFlightState() {
 		_rebelViewMode1 = 0x0f;
 		_rebelViewMode2 = 0;
 		{
-			int dirFactor = (_rebelFlightDir == 0) ? 3 : -3;  // (-(ushort)(DAT_00457902 == 0) & 6) - 3
-			int dirFactor2 = (_rebelFlightDir == 0) ? 0x14 : -0x14;  // (-(ushort)(DAT_00457902 == 0) & 0x28) - 0x14
+			int dirFactor = (_rebelFlightDir == 0) ? 3 : -3;
+			int dirFactor2 = (_rebelFlightDir == 0) ? 0x14 : -0x14;
 			_rebelViewOffsetX = dirFactor * (5 - _rebelDamageLevel) - 0x0f;
 			_rebelViewOffset2X = dirFactor2 * (5 - _rebelDamageLevel);
 		}
@@ -1490,7 +1299,6 @@ void InsaneRebel2::updateOpcode6GenericFlightState() {
 		_rebelLevelType, _rebelAutopilot, _rebelDamageLevel, _rebelViewOffsetX, _rebelViewOffsetY);
 }
 
-// Helper split out of opcode 6 embedded ANIM scanning; not a separate original function.
 void InsaneRebel2::scanOpcode6EmbeddedAnim(byte *renderBitmap, Common::SeekableReadStream &b, int32 chunkSize, int16 par4) {
 	// Detect and load embedded ANIM (SAN) within the remaining IACT payload.
 	// Note: chunkSize is the remaining IACT payload size after par1-par4 header.
@@ -1530,22 +1338,11 @@ void InsaneRebel2::scanOpcode6EmbeddedAnim(byte *renderBitmap, Common::SeekableR
 	}
 }
 
-//
-// iactRebel2Opcode6 -- Level setup / mode switch (FUN_41CADB case 4)
-//
 // Per-wave initialization: clears bit table, resets link tables, configures
-// handler mode (ship/turret/corridor), and loads collision zones. The original
 // gates the reset with handler-specific IACT fields, not always frame 0.
-//
 void InsaneRebel2::iactRebel2Opcode6(byte *renderBitmap, Common::SeekableReadStream &b, int32 chunkSize, int16 par2, int16 par3, int16 par4) {
 	// Opcode 6: Level setup / mode switch
-	// Based on FUN_41CADB case 4 (switch on *local_14 - 2 == 4, meaning opcode 6)
-	//
-	// For Handler 8 (third-person on foot) - FUN_00401234 case 4:
-	// - par4 sets ship level mode (DAT_0043e000)
 	// - first body word == 1 triggers status bar display and state reset
-	// - Updates ship position based on mouse input
-	//
 	// For Handler 0x26/0x19 (turret/FPS):
 	// - Handler-specific status/reset word
 	// - Different view offset calculations
@@ -1578,22 +1375,15 @@ void InsaneRebel2::iactRebel2Opcode6(byte *renderBitmap, Common::SeekableReadStr
 	scanOpcode6EmbeddedAnim(renderBitmap, b, chunkSize, par4);
 }
 
-//
-// iactRebel2Opcode8 -- HUD/Ship resource loading (FUN_0040c3cc / FUN_00401234 / FUN_00407fcb)
-//
 // Decodes embedded ANIM data from IACT chunks and dispatches to
 // handler-specific loaders for NUT sprites, HUD overlays, and backgrounds.
-//
 // Handler-specific routing:
 //   Handler 7  (FLY):  FLY NUT sprites via par4 (1, 2, 3, 11)
 //   Handler 8  (POV):  POV NUT sprites via par3 (1, 3, 6, 7) or background via par4=5
 //   Handler 0x26 (turret): Turret HUD NUT via par3/par4 (1-4)
 //   Handler 0x19: Speeder bike GRD/HUD resources via par4
-//
-// Helper split out of FUN_0040C3CC case 6; not a separate original function.
 bool InsaneRebel2::loadOpcode8Handler7FlySprites(Common::SeekableReadStream &b, int64 startPos, int64 remaining, int16 par4) {
 	// Handler 7: FLY NUT Loading (Third-Person Ship)
-	// FUN_0040c3cc case 6: par4 determines FLY sprite slot.
 	bool isHandler7FLY = (_rebelHandler == 7 && (par4 == 1 || par4 == 2 || par4 == 3 || par4 == 11));
 	if (isHandler7FLY && remaining >= 14) {
 		if (loadHandler7FlySprites(b, remaining, par4)) {
@@ -1606,11 +1396,7 @@ bool InsaneRebel2::loadOpcode8Handler7FlySprites(Common::SeekableReadStream &b,
 	return false;
 }
 
-// Helper split out of FUN_0040C3CC case 6 shot-table loading; not a separate original function.
 bool InsaneRebel2::loadOpcode8Handler7ShotTable(Common::SeekableReadStream &b, int64 startPos, int64 remaining, int16 par4) {
-	// FUN_0040c3cc case 6:
-	//   par4=12 -> FUN_0040fcfa(text, DAT_004437c2, DAT_00443808)
-	//   par4=13 -> FUN_0040fcfa(text, DAT_0044384e, DAT_00443894)
 	if (_rebelHandler != 7 || (par4 != 12 && par4 != 13))
 		return false;
 
@@ -1623,14 +1409,10 @@ bool InsaneRebel2::loadOpcode8Handler7ShotTable(Common::SeekableReadStream &b, i
 	return false;
 }
 
-// Helper split out of FUN_00405663 edge-table loading; not a separate original function.
 bool InsaneRebel2::loadOpcode8EdgeTable(Common::SeekableReadStream &b, int64 startPos, int64 remaining, int16 par4) {
 	// Edge Blend Table Loading (par4 == 1000)
-	// FUN_405663: After all handler-specific opcode 8 processing, checks if par4==1000.
 	// If so, loads a per-level 256x256 color blend table from the IACT chunk data.
 	// This table controls the edge glow color of laser beams (e.g. red vs green).
-	// Data starts at byte offset 18 in the IACT chunk (in_stack_00000014 + 9 shorts).
-	// The stream is positioned after par1..par4 (8 bytes), so FUN_00405663's +18 is startPos + 10.
 	if (par4 == 1000 && remaining >= 10 + 8 + 32896) {
 		byte *edgeData = (byte *)malloc(8 + 32896);
 		if (edgeData) {
@@ -1647,11 +1429,8 @@ bool InsaneRebel2::loadOpcode8EdgeTable(Common::SeekableReadStream &b, int64 sta
 	return false;
 }
 
-// Helper split out of opcode 8 aux SFX loading; not a separate original function.
 bool InsaneRebel2::loadOpcode8AuxSfx(Common::SeekableReadStream &b, int64 startPos, int64 remaining, int16 par4) {
 	// Auxiliary Sound Buffer Loading (par4 20-47)
-	// FUN_401234 case 6 (handler 8): par4 0x14-0x1b (20-27) -> aux buffer 0
-	// FUN_41CADB case 6 (handler 25): par4 0x15-0x1b (21-27) -> aux buffer 0,
 	//   0x1f-0x25 (31-37) -> aux buffer 1, 0x28 (40) -> aux buffer 3,
 	//   0x29-0x2f (41-47) -> aux buffer 2
 	// Data layout: offset 14 = uint32 data size, offset 18 = PCM data start.
@@ -1691,12 +1470,9 @@ bool InsaneRebel2::loadOpcode8AuxSfx(Common::SeekableReadStream &b, int64 startP
 	return true;
 }
 
-// Helper split out of FUN_0041CADB case 6 shot-origin loading; not a separate original function.
 bool InsaneRebel2::loadOpcode8ShotOriginTable(Common::SeekableReadStream &b, int64 startPos, int64 remaining, int16 par4) {
 	// Handler 25 (0x19): Shot-Origin Lookup Table (par4 == 8)
-	// FUN_0041CADB case 6 pushes 30 short pointers into sscanf with format at 0x482360:
 	//   "%hd %hd  %hd %hd ... %hd %hd" (15 X/Y pairs).
-	// Parsed values are written into DAT_004578a6 / DAT_004578c6 at indices 5..19.
 	if (_rebelHandler == 25 && par4 == 8) {
 		if (loadHandler25ShotOriginTable(b, startPos, remaining)) {
 			b.seek(startPos);
@@ -1707,7 +1483,6 @@ bool InsaneRebel2::loadOpcode8ShotOriginTable(Common::SeekableReadStream &b, int
 	return false;
 }
 
-// Helper split out of opcode 8 embedded ANIM scanning; not a separate original function.
 void InsaneRebel2::loadOpcode8EmbeddedAnim(byte *renderBitmap, Common::SeekableReadStream &b, int64 startPos, int64 remaining, int16 par3, int16 par4) {
 	// Remaining handlers require finding ANIM tag in the stream.
 	debugC(DEBUG_INSANE, "Opcode 8: Scanning for ANIM tag (startPos=%lld remaining=%lld)",
@@ -1766,12 +1541,10 @@ void InsaneRebel2::loadOpcode8EmbeddedAnim(byte *renderBitmap, Common::SeekableR
 	b.seek(startPos);
 }
 
-// Helper split out of opcode 8 embedded ANIM routing; not a separate original function.
 bool InsaneRebel2::handleOpcode8EmbeddedAnim(byte *renderBitmap, byte *animData, int32 animDataSize, int16 par3, int16 par4) {
 	bool handled = false;
 
 	// Handler 0x26: Turret HUD Overlays.
-	// FUN_00407fcb case 8: handler 0x26 uses par4 1-4 for HUD NUT loading.
 	// Some chunks use par3 for the same low/high selector.
 	if (!handled && _rebelHandler == 0x26) {
 		int hudSelector = (par4 >= 1 && par4 <= 4) ? par4 : par3;
@@ -1782,7 +1555,6 @@ bool InsaneRebel2::handleOpcode8EmbeddedAnim(byte *renderBitmap, byte *animData,
 	}
 
 	// Handler 8: POV Ship Sprites or Background.
-	// FUN_00401234 case 6: par4 selects POV NUT type (1,3,6,7) or background (5).
 	// NOTE: par3 is always 0 for Handler 8; par4 contains the actual sprite type.
 	if (!handled && _rebelHandler == 8) {
 		if (par4 == 5) {
@@ -1793,14 +1565,6 @@ bool InsaneRebel2::handleOpcode8EmbeddedAnim(byte *renderBitmap, byte *animData,
 	}
 
 	// Handler 25 (0x19): Level 2 GRD Ship Sprites and Background.
-	// FUN_0041cadb case 6 (opcode 8): Uses PAR4 for switch selection.
-	//   par4=1: GRD001 - Primary ship sprite -> DAT_00482240 / _grd001Sprite
-	//   par4=2: GRD002 - Secondary ship sprite -> DAT_00482238 / _grd002Sprite
-	//   par4=4: 350x230 corridor overlay -> DAT_00482268, draws immediately
-	//   par4=5: 320x200 background -> DAT_0048226c
-	//   par4=6: Overlay -> DAT_00482250, draws immediately
-	//   par4=7: Overlay -> DAT_00482248, draws immediately
-	//   par4=10: GRD005 - Mode 3 overlay -> DAT_00482258 / _grd005Sprite
 	if (!handled && _rebelHandler == 25) {
 		if (par4 == 1 || par4 == 2 || par4 == 10) {
 			handled = loadHandler25GrdSprites(animData, animDataSize, par4);
@@ -1879,8 +1643,6 @@ void InsaneRebel2::iactRebel2Opcode8(byte *renderBitmap, Common::SeekableReadStr
 bool InsaneRebel2::loadHandler25ShotOriginTable(Common::SeekableReadStream &b, int64 startPos, int64 remaining) {
 	// IACT layout at this point:
 	// - stream is positioned after par1..par4 (8 bytes consumed by caller)
-	// - FUN_0041CADB reads from offset +18 relative to IACT start -> startPos + 10
-	// - payload size for this opcode family is at offset +14 -> startPos + 6
 	if (remaining < 12)
 		return false;
 
@@ -1946,7 +1708,6 @@ bool InsaneRebel2::loadHandler25ShotOriginTable(Common::SeekableReadStream &b, i
 		return false;
 	}
 
-	// FUN_0041CADB mapping:
 	// token1->0x4578b0 (X index 5), token2->0x4578d0 (Y index 5), ...
 	// token29->0x4578cc (X index 19), token30->0x4578ec (Y index 19).
 	for (int i = 0; i < 15; ++i) {
@@ -1965,9 +1726,6 @@ bool InsaneRebel2::loadHandler25ShotOriginTable(Common::SeekableReadStream &b, i
 
 // loadHandler7ShotTable -- Parse handler 7 laser muzzle coordinate pairs from IACT payload.
 bool InsaneRebel2::loadHandler7ShotTable(Common::SeekableReadStream &b, int64 startPos, int64 remaining, int16 par4) {
-	// FUN_0040FCFA parses 35 "%hd %hd" pairs from offset +18 in the IACT
-	// chunk into two parallel 35-entry tables. These tables are BSS globals in
-	// the EXE, so their values only exist in the SAN/IACT stream.
 	if (remaining < 12)
 		return false;
 
@@ -2048,25 +1806,12 @@ bool InsaneRebel2::loadHandler7ShotTable(Common::SeekableReadStream &b, int64 st
 	return true;
 }
 
-// ---------------------------------------------------------------------------
 // Opcode 8 Helper Functions
-// ---------------------------------------------------------------------------
-// Extracted from the original monolithic iactRebel2Opcode8 to match
-// the original FUN_* function structure.
 
-// loadHandler7FlySprites -- Handler 7 FLY NUT loading (FUN_0040c3cc case 6).
 bool InsaneRebel2::loadHandler7FlySprites(Common::SeekableReadStream &b, int64 remaining, int16 par4) {
-	// Handler 7 FLY NUT loading - FUN_0040c3cc case 6 (opcode 8)
-	// IACT structure after par1-par4 (we're at offset +8):
 	//   +0-5 (6 bytes): additional header
 	//   +6-9 (4 bytes): NUT data size (little-endian)
 	//   +10+: NUT data
-	//
-	// par4 values (param_5[3] - 1 in assembly):
-	//   1 -> case 0: FLY001 - Ship direction sprites (DAT_0047fee8)
-	//   2 -> case 1: FLY003 - Targeting overlay (DAT_0047fef8)
-	//   3 -> case 2: FLY002 - Laser fire sprites (DAT_0047fef0)
-	//  11 -> case 10: FLY004 - High-res alternative (DAT_0047ff00)
 
 	if (remaining < 14) {
 		return false;
@@ -2136,7 +1881,6 @@ bool InsaneRebel2::loadHandler7FlySprites(Common::SeekableReadStream &b, int64 r
 	debugC(DEBUG_INSANE, "loadHandler7FlySprites: Loaded FLY NUT par4=%d with %d sprites",
 		par4, newNut->getNumChars());
 
-	// Assign to appropriate slot based on par4 (matches FUN_0040c3cc case 6 switch)
 	bool assigned = true;
 	switch (par4) {
 	case 1:  // FLY001 - Ship direction sprites (35 frames)
@@ -2166,14 +1910,8 @@ bool InsaneRebel2::loadHandler7FlySprites(Common::SeekableReadStream &b, int64 r
 	return assigned;
 }
 
-// loadTurretHudOverlay -- Handler 0x26 turret HUD loading (FUN_00407fcb case 8).
 bool InsaneRebel2::loadTurretHudOverlay(byte *animData, int32 size, int16 selector) {
-	// Handler 0x26 turret HUD overlay loading - FUN_00407fcb case 8
 	// Resolution-dependent loading:
-	//   selector == 1: Low-res primary HUD (DAT_0047fe78 / _hudOverlayNut)
-	//   selector == 2: High-res primary HUD (DAT_0047fe78 / _hudOverlayNut)
-	//   selector == 3: Low-res secondary HUD (DAT_0047fe80 / _hudOverlay2Nut)
-	//   selector == 4: High-res secondary HUD (DAT_0047fe80 / _hudOverlay2Nut)
 
 	if (!animData || size <= 0) {
 		return false;
@@ -2215,14 +1953,7 @@ bool InsaneRebel2::loadTurretHudOverlay(byte *animData, int32 size, int16 select
 	return true;
 }
 
-// loadHandler8ShipSprites -- Handler 8 POV NUT loading (FUN_00401234 case 6).
 bool InsaneRebel2::loadHandler8ShipSprites(byte *animData, int32 size, int16 par4) {
-	// Handler 8 ship POV NUT loading - FUN_00401234 case 6 (opcode 8)
-	// par4 values (from IACT data offset +6, NOT par3 which is always 0):
-	//   1: POV001 - Primary ship sprite (DAT_0047e010 / _shipSprite)
-	//   3: POV004 - Secondary ship sprite (DAT_0047e028 / _shipSprite2)
-	//   6: POV002 - Shot impact overlay (DAT_0047e020 / _shipOverlay1)
-	//   7: POV003 - Shot impact overlay (DAT_0047e018 / _shipOverlay2)
 
 	if (!animData || size <= 0) {
 		return false;
@@ -2268,13 +1999,7 @@ bool InsaneRebel2::loadHandler8ShipSprites(byte *animData, int32 size, int16 par
 	return true;
 }
 
-// loadHandler25GrdSprites -- Handler 25 GRD NUT loading (FUN_0041cadb case 6).
 bool InsaneRebel2::loadHandler25GrdSprites(byte *animData, int32 size, int16 par4) {
-	// Handler 25 GRD ship NUT loading - FUN_0041cadb case 6 (opcode 8)
-	// par4 values (from IACT data offset +6):
-	//   1: GRD001 - Primary ship sprite (DAT_00482240 / _grd001Sprite)
-	//   2: GRD002 - Secondary ship sprite (DAT_00482238 / _grd002Sprite)
-	//   10: GRD005 - Mode 3 overlay sprite (DAT_00482258 / _grd005Sprite)
 
 	if (!animData || size <= 0) {
 		return false;
@@ -2319,11 +2044,8 @@ bool InsaneRebel2::loadHandler25GrdSprites(byte *animData, int32 size, int16 par
 	return true;
 }
 
-// loadLevel2Background -- Decode Level 2 background from embedded ANIM (FUN_00401234 case 5).
 bool InsaneRebel2::loadLevel2Background(byte *animData, int32 size, byte *renderBitmap) {
-	// Level 2 background loading from embedded ANIM - FUN_00401234 case 5
 	// par4=5 contains the background image embedded as ANIM with FOBJ codec 3
-	// Creates 320x200 buffer (DAT_0047e030 / _level2Background)
 
 	if (!animData || size < 8) {
 		return false;
@@ -2406,8 +2128,6 @@ bool InsaneRebel2::loadLevel2Background(byte *animData, int32 size, byte *render
 			debugC(DEBUG_INSANE, "loadLevel2Background: Found FOBJ: codec=%d pos=(%d,%d) size=%dx%d",
 				codec, fobjX, fobjY, fobjW, fobjH);
 
-			// Decode codec 3 (RLE) into the original 320x200 background buffer.
-			// FUN_0041CADB/FUN_00401234 draw these resources into a fixed buffer
 			// and clip them there; Level 11 backgrounds extend past the right edge.
 			if (codec == 3 && fobjX >= 0 && fobjY >= 0 && fobjW > 0 && fobjH > 0 &&
 					fobjX < 320 && fobjY < 200 && stream.pos() < subDataEnd) {
@@ -2422,7 +2142,6 @@ bool InsaneRebel2::loadLevel2Background(byte *animData, int32 size, byte *render
 				_level2BackgroundLoaded = true;
 				foundBackground = true;
 
-				// Handler 25 uses this buffer as a lookup mask; FUN_0041CADB does not
 				// copy it to the live screen. Handler 8 still uses it as a restore source.
 				if (renderBitmap && _rebelHandler != 25) {
 					int bufferPitch = (_player && _player->_width > 0) ? _player->_width : 320;
@@ -2450,25 +2169,20 @@ bool InsaneRebel2::loadLevel2Background(byte *animData, int32 size, byte *render
 	return foundBackground;
 }
 
-//
 // iactRebel2Opcode9 -- Text/subtitle display via IACT chunk
-//
 // Handles inline text in IACT chunks. Most RA2 subtitles use TRES chunks
 // (handled by SmushPlayer::handleTextResource); this opcode is less common.
 // Supports multi-line wrapping, centered/shadowed text, and clip regions.
-//
 void InsaneRebel2::iactRebel2Opcode9(byte *renderBitmap, Common::SeekableReadStream &b, int16 par2, int16 par3, int16 par4) {
 	// Opcode 9: Text/Subtitle Display via IACT chunk
 	// Note: Most RA2 subtitles use TRES chunks handled by SmushPlayer::handleTextResource()
 	// This opcode handles inline text in IACT chunks (less common)
-	//
 	// IACT Chunk Layout (par1-par4 already read by handleIACT):
 	// +0x00 (2): opcode = 9 (par1, already read)
 	// +0x02 (2): par2 (already read)
 	// +0x04 (2): par3 (already read)
 	// +0x06 (2): par4 (already read)
 	// +0x08 onwards: Text data structure
-	//
 	// Text Data Structure:
 	// +0x00 (2): X position
 	// +0x02 (2): Y position
@@ -2551,13 +2265,11 @@ void InsaneRebel2::iactRebel2Opcode9(byte *renderBitmap, Common::SeekableReadStr
 
 	// Check difficulty gate (flag bit 3 = 0x08)
 	// If set, only show text if difficulty check passes (we skip this check for simplicity)
-	// FUN_00425D30(0) is called.
 
 	// Get render buffer dimensions
 	int width = (_player && _player->_width > 0) ? _player->_width : 320;
 	int height = (_player && _player->_height > 0) ? _player->_height : 200;
 
-	// Apply coordinate clamping (from FUN_004033cf disassembly)
 	// Low-res: X clamped to [16, 304], Y clamped to [16, 196]
 	if (posX < 16)
 		posX = 16;
@@ -2602,7 +2314,6 @@ void InsaneRebel2::iactRebel2Opcode9(byte *renderBitmap, Common::SeekableReadStr
 	}
 
 	// Use white color (index 255) for subtitle text
-	// The original uses colors from the palette, commonly white or yellow for subtitles
 	int16 textColor = 255;
 
 	// RA2 fonts (like DIHIFONT.NUT) have only 58 characters starting at ASCII 32 (space).
@@ -2687,10 +2398,8 @@ void InsaneRebel2::iactRebel2Opcode9(byte *renderBitmap, Common::SeekableReadStr
 		posX, posY, textFlags, clipX, clipY, clipW, clipH);
 }
 
-// enemyUpdate -- Opcode 4: update enemy position and state (FUN_004028C5 / FUN_0041E7C2).
 void InsaneRebel2::enemyUpdate(byte *renderBitmap, Common::SeekableReadStream &b, int16 par2, int16 par3, int16 par4) {
 	// Opcode 4: Enemy position update
-	// Read 5 shorts from the stream (offset +8 through +16)
 	int16 enemyId = b.readSint16LE();  // Offset +8
 	int16 x = b.readSint16LE();        // Offset +10 (0x0A)
 
@@ -2701,7 +2410,6 @@ void InsaneRebel2::enemyUpdate(byte *renderBitmap, Common::SeekableReadStream &b
 	int16 w = b.readSint16LE();        // Offset +14 (0x0E) - Width
 	int16 h = b.readSint16LE();        // Offset +16 (0x10) - Height
 
-	// par3==2 (turret handler) marks a translucent surface / force field (FUN_40A2E0 local_8==2):
 	// never a direct hit target ??? it is destroyed by clearing the gauge group (par4) it depends
 	// on. Show it while that group still has elements, hide it once the group is cleared.
 	if (par3 == 2 && _rebelHandler == 0x26) {
@@ -2719,7 +2427,6 @@ void InsaneRebel2::enemyUpdate(byte *renderBitmap, Common::SeekableReadStream &b
 		return;
 	}
 
-	// The disassembly shows half-width/half-height are used for centering:
 	//   halfW = w >> 1
 	//   halfH = h >> 1
 	//   centerX = x + halfW
@@ -2727,10 +2434,6 @@ void InsaneRebel2::enemyUpdate(byte *renderBitmap, Common::SeekableReadStream &b
 	// But for drawing the bounding box, we want the top-left corner (x, y) and full dimensions.
 
 	// Update enemy list for hit detection
-	// Enemy type comes from par4 (IACT offset +6), NOT par3 (offset +4).
-	// In the original (FUN_004028C5/FUN_0041E7C2): sVar5/sVar2 = *(short *)(*local + 6)
-	// This maps to par4 (userId field). Used for DAT_0047ab98 wave state bitmask:
-	//   DAT_0047ab98 |= 1 << (type & 0x1f)
 	debugC(DEBUG_INSANE, "Opcode4: handler=%d enemyId=%d par2=%d par3=%d par4/type=%d pos=(%d,%d) size=(%d,%d)",
 		_rebelHandler, enemyId, par2, par3, par4, x, y, w, h);
 
diff --git a/engines/scumm/insane/rebel2/levels.cpp b/engines/scumm/insane/rebel2/levels.cpp
index 41be2808224..63bf52c55b1 100644
--- a/engines/scumm/insane/rebel2/levels.cpp
+++ b/engines/scumm/insane/rebel2/levels.cpp
@@ -45,11 +45,7 @@ static void purgeRebel2GameplayInputEvents(Common::EventManager *eventMan) {
 	eventMan->purgeKeyboardEvents();
 }
 
-// ---------------------------------------------------------------------------
 // Level Loading System
-// ---------------------------------------------------------------------------
-// Emulates the level handler functions from FUN_00417E53 through FUN_0041BBE8.
-// Based on disassembly analysis of the Rebel Assault 2 executable.
 
 Common::String InsaneRebel2::getLevelDir(int levelId) {
 	return Common::String::format("LEV%02d", levelId);
@@ -59,12 +55,8 @@ Common::String InsaneRebel2::getLevelPrefix(int levelId) {
 	return Common::String::format("%02d", levelId);
 }
 
-//
-// runGame -- Main game entry point (FUN_004142BD)
-//
 // Full game loop: intro, main menu, pilot select, chapter select, level
 // progression. Called from ScummEngine::go().
-//
 void InsaneRebel2::runGame() {
 	SmushPlayer *splayer = ((ScummEngine_v7 *)_vm)->_splayer;
 
@@ -110,7 +102,6 @@ void InsaneRebel2::runGame() {
 				int selectedLevel = _selectedChapter + 1;
 				debugC(DEBUG_INSANE, "InsaneRebel2: Starting chapter %d (level %d)", _selectedChapter + 1, selectedLevel);
 
-				// Ending selected directly from chapter select (FUN_0041bbe8, case 0xf)
 				if (selectedLevel == 16) {
 					playEndingSequence();
 				}
@@ -142,17 +133,10 @@ void InsaneRebel2::runGame() {
 	}
 }
 
-//
-// playIntroSequence -- Intro sequence (FUN_004142BD case 0)
-//
-// Original flow:
 //   - If 'f','o','x' keys all held: play CREDITS/O_OPEN_C.SAN (Fox logo easter egg)
 //   - If 'b','o','t' keys all held: play CREDITS/O_OPEN_D.SAN (LucasArts logo)
 //   - Else: play OPEN/O_OPEN_A.SAN (main intro)
-//   - If DAT_0047ab45 || DAT_0047ab47: play OPEN/O_OPEN_B.SAN (additional intro)
-//
 // We skip the easter eggs and play both O_OPEN_A + O_OPEN_B unconditionally.
-//
 void InsaneRebel2::playIntroSequence() {
 	debugC(DEBUG_INSANE, "Playing intro sequence");
 
@@ -162,7 +146,6 @@ void InsaneRebel2::playIntroSequence() {
 	SmushPlayer *splayer = ((ScummEngine_v7 *)_vm)->_splayer;
 
 	// Play main intro (OPEN/O_OPEN_A.SAN)
-	// Original: FUN_0041f4d0("OPEN/O_OPEN_A.SAN", 0x28, 0xffff, 0xffff, 0)
 	debugC(DEBUG_INSANE, "Playing main intro (O_OPEN_A.SAN)");
 	splayer->setCurVideoFlags(0x28);
 	splayer->play("OPEN/O_OPEN_A.SAN", 15);
@@ -171,14 +154,12 @@ void InsaneRebel2::playIntroSequence() {
 		return;
 
 	// Play additional intro (OPEN/O_OPEN_B.SAN)
-	// Original: conditional on DAT_0047ab45 || DAT_0047ab47
 	// We play unconditionally (matches "Continue Intro" menu behavior)
 	debugC(DEBUG_INSANE, "Playing additional intro (O_OPEN_B.SAN)");
 	splayer->setCurVideoFlags(0x28);
 	splayer->play("OPEN/O_OPEN_B.SAN", 15);
 }
 
-// playMissionBriefing -- Mission briefing screen (FUN_00415CF8).
 void InsaneRebel2::playMissionBriefing() {
 
 	debugC(DEBUG_INSANE, "Playing mission briefing");
@@ -190,7 +171,6 @@ void InsaneRebel2::playMissionBriefing() {
 
 // playCinematic -- Play a cinematic/cutscene video.
 // Resets handler to 0 (no HUD) and sets flags to 0x28 (cinematic + buffer preserve).
-// All wrapper functions (FUN_00417168/4171c5/417ab2/417327) add | 8 before calling FUN_0041f4d0.
 void InsaneRebel2::playCinematic(const char *filename) {
 	restoreDamageFlashPalette();
 	resetVideoAudio();
@@ -203,9 +183,7 @@ void InsaneRebel2::playCinematic(const char *filename) {
 	splayer->play(filename, 15);
 }
 
-// playVideoWithText -- Video with progressive text overlay (FUN_004171c5).
 // displayLength = currentFrame + 10 - fadeInFrame, capped at 0xBE (190) chars.
-// Text rendered at (textX, textY) via FUN_004341a0.
 void InsaneRebel2::playVideoWithText(const char *filename, int textID, int textX, int textY,
                                      int fadeInFrame, int fadeOutFrame) {
 
@@ -231,7 +209,6 @@ void InsaneRebel2::playVideoWithText(const char *filename, int textID, int textX
 }
 
 // playLevelBegin -- Level beginning cinematic (LEVXX/XXBEG.SAN).
-// Uses per-level text overlay parameters from GAME.TRS via FUN_004171c5.
 void InsaneRebel2::playLevelBegin(int levelId) {
 
 	struct TextOverlayParams {
@@ -243,25 +220,24 @@ void InsaneRebel2::playLevelBegin(int levelId) {
 	};
 
 	// Table of per-level text overlay parameters
-	// All levels use FUN_004171c5 (verified against decompiled level handlers)
 	// Text IDs are sequential: 0xAA (level 1) through 0xB8 (level 15)
 	const TextOverlayParams levelTextParams[16] = {
 		{ -1,   0,  0,   0,    0},    // Level 0 (unused)
-		{0xAA, 0xA0, 10,   5, 0x4B},  // Level 1:  FUN_00417E53
-		{0xAB, 0xA0, 10,   2, 0x46},  // Level 2:  FUN_00418063
-		{0xAC, 0xA0, 10,   2, 0x46},  // Level 3:  FUN_0041885F
-		{0xAD, 0xA0, 10,   2,  100},  // Level 4:  FUN_00418CC4
-		{0xAE, 0xA0, 10,   5, 0x3C},  // Level 5:  FUN_00418EC6
-		{0xAF, 0xA0, 10,   5, 0x4B},  // Level 6:  FUN_004190D6
-		{0xB0, 0xA0, 10,   5, 0x4B},  // Level 7:  FUN_0041974C
-		{0xB1, 0xA0, 10,   5, 0x4B},  // Level 8:  FUN_00419976
-		{0xB2, 0xA0, 10, 200, 0x10E}, // Level 9:  FUN_00419B86
-		{0xB3, 0xA0, 10,   2, 0x46},  // Level 10: FUN_00419E0A
-		{0xB4, 0xA0, 10,   2, 0x46},  // Level 11: FUN_0041A00C
-		{0xB5, 0xA0, 10,   5, 0x4B},  // Level 12: FUN_0041A3EB
-		{0xB6, 0xA0, 10,   2, 0x46},  // Level 13: FUN_0041A806
-		{0xB7, 0xA0, 10,   2, 0x46},  // Level 14: FUN_0041ABB2
-		{0xB8, 0xA0, 10,   2, 0x46},  // Level 15: FUN_0041AEE8
+		{0xAA, 0xA0, 10,   5, 0x4B},
+		{0xAB, 0xA0, 10,   2, 0x46},
+		{0xAC, 0xA0, 10,   2, 0x46},
+		{0xAD, 0xA0, 10,   2,  100},
+		{0xAE, 0xA0, 10,   5, 0x3C},
+		{0xAF, 0xA0, 10,   5, 0x4B},
+		{0xB0, 0xA0, 10,   5, 0x4B},
+		{0xB1, 0xA0, 10,   5, 0x4B},
+		{0xB2, 0xA0, 10, 200, 0x10E},
+		{0xB3, 0xA0, 10,   2, 0x46},
+		{0xB4, 0xA0, 10,   2, 0x46},
+		{0xB5, 0xA0, 10,   5, 0x4B},
+		{0xB6, 0xA0, 10,   2, 0x46},
+		{0xB7, 0xA0, 10,   2, 0x46},
+		{0xB8, 0xA0, 10,   2, 0x46},
 	};
 
 	Common::String dir = getLevelDir(levelId);
@@ -279,7 +255,6 @@ void InsaneRebel2::playLevelBegin(int levelId) {
 	}
 }
 
-// playLevelEnd -- Level completion video (FUN_00417327).
 void InsaneRebel2::playLevelEnd(int levelId) {
 
 	restoreDamageFlashPalette();
@@ -295,12 +270,10 @@ void InsaneRebel2::playLevelEnd(int levelId) {
 	debugC(DEBUG_INSANE, "Playing level %d end: %s", levelId, filename.c_str());
 
 	SmushPlayer *splayer = ((ScummEngine_v7 *)_vm)->_splayer;
-	// Original: FUN_00417327 adds | 8, so flags = 0x20 | 0x08 = 0x28
 	splayer->setCurVideoFlags(0x28);
 	splayer->play(filename.c_str(), 15);
 }
 
-// playLevelRetry -- Retry prompt video (LEVXX/XXRETRY.SAN, FUN_00417168).
 void InsaneRebel2::playLevelRetry(int levelId) {
 
 	restoreDamageFlashPalette();
@@ -316,12 +289,10 @@ void InsaneRebel2::playLevelRetry(int levelId) {
 	debugC(DEBUG_INSANE, "Playing level %d retry: %s", levelId, filename.c_str());
 
 	SmushPlayer *splayer = ((ScummEngine_v7 *)_vm)->_splayer;
-	// Original: FUN_00417168 adds | 8, so flags = 0x20 | 0x08 = 0x28
 	splayer->setCurVideoFlags(0x28);
 	splayer->play(filename.c_str(), 15);
 }
 
-// playLevelGameOver -- Game over video (FUN_00417ab2).
 void InsaneRebel2::playLevelGameOver(int levelId) {
 
 	restoreDamageFlashPalette();
@@ -337,14 +308,10 @@ void InsaneRebel2::playLevelGameOver(int levelId) {
 	debugC(DEBUG_INSANE, "Playing level %d game over: %s", levelId, filename.c_str());
 
 	SmushPlayer *splayer = ((ScummEngine_v7 *)_vm)->_splayer;
-	// Original: FUN_00417ab2 adds | 8, so flags = 0x20 | 0x08 = 0x28
 	splayer->setCurVideoFlags(0x28);
 	splayer->play(filename.c_str(), 15);
 }
 
-// playEndingSequence -- Finale + credits + epilogue (FUN_0041bbe8).
-//
-// Original flow:
 //   1. Play difficulty-dependent finale video:
 //      - Difficulty 2: FINAL/F_FIN_B.SAN
 //      - Difficulty 3: FINAL/F_FIN_C.SAN
@@ -352,7 +319,6 @@ void InsaneRebel2::playLevelGameOver(int levelId) {
 //   2. Play credits: FINAL/F_CREDIT.SAN
 //   3. Play epilogue: FINAL/F_EPILOG.SAN
 //   4. Return to main menu
-//
 void InsaneRebel2::playEndingSequence() {
 
 	debugC(DEBUG_INSANE, "Playing ending sequence (difficulty=%d)", _difficulty);
@@ -459,8 +425,6 @@ int InsaneRebel2::runLevel(int levelId) {
 	// Set the current level
 	_selectedLevel = levelId;
 
-	// Set the level type for difficulty table lookup (DAT_0047a7f8).
-	// Each original level function sets this before gameplay starts.
 	// Levels 1-6 use types 0-5, but Level 6 also uses type 6 mid-level.
 	// Levels 7-15 use types 7-15 (gap at type 6 which is Level 6 phase 2).
 	// Level 15 also switches to type 16 mid-level at frame 0x21e.
@@ -470,7 +434,6 @@ int InsaneRebel2::runLevel(int levelId) {
 	_rebelLevelType = kLevelTypeMap[levelId];
 
 	// Lock the mouse to the game window during gameplay.
-	// The original hides the cursor (ShowCursor(0)) and relies on Windows confining
 	// the mouse to the game window. Without locking, the cursor can escape the
 	// window making the ship uncontrollable.
 	_gameplaySectionActive = false;
@@ -549,28 +512,18 @@ int InsaneRebel2::runLevel(int levelId) {
 	return result;
 }
 
-// ---------------------------------------------------------------------------
 // Helper Functions
-// ---------------------------------------------------------------------------
 
-// Emulates FUN_004233a0.
 int InsaneRebel2::getRandomVariant(int max) {
 	return _vm->_rnd.getRandomNumber(max - 1);
 }
 
-//
 // selectDeathVideoVariant -- Frame-based death video selection
-//
-// Returns variant suffix ("A", "B", "C", etc.) based on level, phase,
-// and the frame where the player died. Emulates the per-level frame
-// threshold tables in the original level handlers.
-//
 Common::String InsaneRebel2::selectDeathVideoVariant(int levelId, int phase, int frame) {
 
 	switch (levelId) {
 	case 1:
 		// Level 1: Random between A and B
-		// Original: random!=0 ??? A (offset 0), random==0 ??? B (offset 0x14)
 		return (getRandomVariant(2) == 0) ? "B" : "A";
 
 	case 2:
@@ -578,9 +531,7 @@ Common::String InsaneRebel2::selectDeathVideoVariant(int levelId, int phase, int
 		return "";
 
 	case 3:
-		// Level 3: Based on death frame and phase
 		if (phase == 1) {
-			// Phase 1 death video selection (from FUN_0041885F lines 80-96)
 			if (frame < 0x10c)
 				return "B";       // < 268
 			if (frame < 0x1a9)
@@ -593,7 +544,6 @@ Common::String InsaneRebel2::selectDeathVideoVariant(int levelId, int phase, int
 				return "B";
 			return "A";
 		} else {
-			// Phase 2 death video selection (from FUN_0041885F lines 53-67)
 			if (frame < 0x2f1)
 				return "A";       // < 753
 			if (frame < 0x347)
@@ -606,18 +556,14 @@ Common::String InsaneRebel2::selectDeathVideoVariant(int levelId, int phase, int
 		}
 
 	case 4:
-		// Level 4: Single variant "A" (original plays 04DIE_A.SAN)
 		return "A";
 
 	case 5:
 		// Level 5: Random between A and B
-		// Original: random!=0 ??? A (offset 0), random==0 ??? B (offset 0x14)
 		return (getRandomVariant(2) == 0) ? "B" : "A";
 
 	case 6:
-		// Level 6 (FUN_004190D6): Phase-based with detailed frame selection
 		if (phase == 1) {
-			// DAT_0047a7f8 == 5 (phase 1)
 			if (frame < 0x4e)
 				return "D";
 			if (frame < 0xe0)
@@ -632,7 +578,6 @@ Common::String InsaneRebel2::selectDeathVideoVariant(int levelId, int phase, int
 				return "C";
 			return "D";
 		} else {
-			// DAT_0047a7f8 == 6 (phase 2)
 			if (frame < 0xcc)
 				return "E";
 			if (frame < 0xfe)
@@ -665,27 +610,19 @@ Common::String InsaneRebel2::selectDeathVideoVariant(int levelId, int phase, int
 		}
 
 	case 7:
-		// Level 7 (FUN_0041974C): Based on DAT_0047ab8c (fork state)
-		// DAT_0047ab8c != 0 ??? DIE_B; == 0 ??? DIE_A
 		// We use phase as a proxy (phase 2 = reached fork)
 		return (phase >= 2) ? "B" : "A";
 
 	case 8:
-		// Level 8 (FUN_00419976): Random A or B
-		// Original: random!=0 ??? A (offset 0), random==0 ??? B (offset 0x14)
 		return (getRandomVariant(2) == 0) ? "B" : "A";
 
 	case 9:
-		// Level 9 (FUN_00419B86): Based on DAT_0047ab94 (death cause)
-		// 0???A, 1???C, else???B. DAT_0047ab94 is not tracked yet.
 		return "A";
 
 	case 10:
-		// Level 10 (FUN_00419E0A): Single death video (no variant suffix)
 		return "";
 
 	case 11:
-		// Level 11 (FUN_0041A00C): Phase-based death videos
 		// Phase 1 ??? DIE_A, Phase 2 ??? DIE_B, Phase 3 ??? DIE_C
 		if (phase <= 1)
 			return "A";
@@ -694,11 +631,9 @@ Common::String InsaneRebel2::selectDeathVideoVariant(int levelId, int phase, int
 		return "C";
 
 	case 12:
-		// Level 12 (FUN_0041AA14): Single death video (no variants)
 		return "";
 
 	case 13:
-		// Level 13 (FUN_0041B3E1): Frame-based
 		if (frame < 0x1c2)
 			return "A";
 		if (frame < 0x302)
@@ -710,11 +645,9 @@ Common::String InsaneRebel2::selectDeathVideoVariant(int levelId, int phase, int
 		return "D";
 
 	case 14:
-		// Level 14 (FUN_0041B6E8): Single death video (no variant suffix)
 		return "";
 
 	case 15:
-		// Level 15 (FUN_0041B8D7): Frame-based with many thresholds
 		if (frame < 0x21e)
 			return "A";
 		if (frame < 0x2f9)
@@ -758,7 +691,6 @@ void InsaneRebel2::playLevelDeathVariant(int levelId, int phase, int frame) {
 	debugC(DEBUG_INSANE, "Playing death video: %s (phase=%d, frame=%d)", filename.c_str(), phase, frame);
 
 	SmushPlayer *splayer = ((ScummEngine_v7 *)_vm)->_splayer;
-	// Original: FUN_00417168 adds | 8, so flags = 0x20 | 0x08 = 0x28
 	splayer->setCurVideoFlags(0x28);
 	splayer->play(filename.c_str(), 15);
 }
@@ -787,7 +719,6 @@ void InsaneRebel2::playLevelRetryVariant(int levelId, int phase) {
 	debugC(DEBUG_INSANE, "Playing retry video: %s (phase=%d)", filename.c_str(), phase);
 
 	SmushPlayer *splayer = ((ScummEngine_v7 *)_vm)->_splayer;
-	// Original: FUN_00417168 adds | 8, so flags = 0x20 | 0x08 = 0x28
 	splayer->setCurVideoFlags(0x28);
 	splayer->play(filename.c_str(), 15);
 }
diff --git a/engines/scumm/insane/rebel2/menu.cpp b/engines/scumm/insane/rebel2/menu.cpp
index 08ece9305ec..9baeddf61b3 100644
--- a/engines/scumm/insane/rebel2/menu.cpp
+++ b/engines/scumm/insane/rebel2/menu.cpp
@@ -40,10 +40,7 @@
 
 namespace Scumm {
 
-// ---------------------------------------------------------------------------
 // Menu System Implementation
-// ---------------------------------------------------------------------------
-// Emulates original menu system from FUN_004147B2 and FUN_0041FDC8.
 
 static void setRebel2MixerVolume(ScummEngine_v7 *vm, int volumeLevel) {
 	const int mixerVolume = CLIP<int>(volumeLevel * 2, 0, (int)Audio::Mixer::kMaxMixerVolume);
@@ -90,7 +87,6 @@ void InsaneRebel2::updateMenuVirtualKeyboard() {
 	setVirtualKeyboardVisible(isMenuTextInputActive());
 }
 
-// unlockAllChapters -- Debug mode unlock (FUN_00415CF8 lines 60-71, DAT_0047ab34=='d').
 void InsaneRebel2::unlockAllChapters() {
 	debugC(DEBUG_INSANE, "Unlocking all chapters for testing");
 	for (int i = 0; i < 16; i++) {
@@ -99,8 +95,6 @@ void InsaneRebel2::unlockAllChapters() {
 	}
 }
 
-// getRandomMenuVideo -- Select random menu video variant (FUN_0041FDC8).
-// Original plays O_MENU.SAN when no progress flags are set, but that file
 // contains ONLY audio (no FOBJ frames) resulting in a black background.
 // We always use O_MENU_X.SAN (A-O) which have 320x200 background images.
 Common::String InsaneRebel2::getRandomMenuVideo() {
@@ -118,15 +112,10 @@ Common::String InsaneRebel2::getRandomMenuVideo() {
 	return Common::String::format("OPEN/O_MENU_%c.SAN", letter);
 }
 
-//
-// processMenuInput -- Menu input handling (FUN_0041f5ae)
-//
 // Returns -1 (no action) or a 0-based selected menu item.
 // Events captured by notifyEvent() before ScummEngine consumes them.
 // Keyboard: Up=0x148, Down=0x150, Enter=0x0d.
 // Physical ESC is handled by notifyEvent() and opens the global menu.
-// Mouse mode (DAT_0047a806 == 1): Y position maps to selection.
-//
 int InsaneRebel2::processMenuInput() {
 
 	int result = -1;
@@ -146,18 +135,15 @@ int InsaneRebel2::processMenuInput() {
 
 			switch (event.kbd.keycode) {
 			case Common::KEYCODE_UP:
-				// Navigate up (wrap around) - emulates key code 0x148
 				_menuSelection--;
 				if (_menuSelection < 0) {
 					_menuSelection = _menuItemCount - 1;
 				}
-				// Reset repeat delay counter (DAT_00459ce0)
 				_menuRepeatDelay = 3;
 				debugC(DEBUG_INSANE, "Menu: Selection changed to %d (UP)", _menuSelection);
 				break;
 
 			case Common::KEYCODE_DOWN:
-				// Navigate down (wrap around) - emulates key code 0x150
 				_menuSelection++;
 				if (_menuSelection >= _menuItemCount) {
 					_menuSelection = 0;
@@ -168,7 +154,6 @@ int InsaneRebel2::processMenuInput() {
 
 			case Common::KEYCODE_RETURN:
 			case Common::KEYCODE_KP_ENTER:
-				// Confirm selection - emulates key code 0x0d
 				if (_menuSelection >= 0 && _menuSelection < _menuItemCount) {
 					result = _menuSelection;
 					debugC(DEBUG_INSANE, "Menu: Item %d selected (ENTER)", _menuSelection);
@@ -239,33 +224,17 @@ int InsaneRebel2::processMenuInput() {
 	return result;
 }
 
-//
-// drawMenuItems -- Shared menu item renderer (FUN_0041f5ae)
-//
 void InsaneRebel2::drawMenuItems(byte *renderBitmap, int pitch, int width, int height,
                                   const char **items, int numItems, int selection,
                                   bool leftAligned) {
-	//
 	// items[0] = title string, items[1..numItems] = selectable items
-	// numItems = number of selectable items (FUN_0041f5ae param_3)
-	// selection = currently highlighted item (0-based, maps to DAT_00459988)
-	// leftAligned = false: param_4==0 (centered), true: param_4==1 (left-aligned)
-	//
-	// Coordinate formulas from FUN_0041f5ae (low-res, DAT_0047a808 < 2):
-	// Centered (param_4=0):
 	//   Title X:     center - titleWidth/2  (centerX = 160)
-	//   Title Y:     param_3 * -5 + 0x51
 	//   Item X:      center - textWidth/2
 	//   Box X:       center - bracketWidth/2
-	// Left-aligned (param_4=1):
 	//   Title X:     0x28 = 40
-	//   Title Y:     param_3 * -5 + 0x56
 	//   Item X:      0x17 = 23
 	//   Box X:       0x14 = 20
 	// Both modes:
-	//   Item base Y: param_3 * -5 + 0x68
-	//   Item Y:      param_3 * -5 + i * 10 + 0x68
-	//   Box Y:       param_3 * -5 + i * 10 + 0x67  (1px above text)
 
 	const bool highRes = isHiRes();
 	const int centerX = highRes ? 0x140 : width / 2;
@@ -291,22 +260,18 @@ void InsaneRebel2::drawMenuItems(byte *renderBitmap, int pitch, int width, int h
 		drawMenuString(renderBitmap, str, x, y, 1);
 	};
 
-	// -------------------------------------------------------------------
 	// Draw title - items[0]
 	// Centered: X = center - titleWidth/2
 	// Left-aligned: X = 40 (0x28)
-	// -------------------------------------------------------------------
 	{
 		int titleWidth = getStringWidth(items[0]);
 		int titleX = leftAligned ? (highRes ? 0x50 : 0x28) : (centerX - titleWidth / 2);
 		drawString(items[0], titleX, titleY);
 	}
 
-	// -------------------------------------------------------------------
 	// Draw selectable items with selection highlight box
 	// Centered: item X = center - textWidth/2, box X = center - bracketWidth/2
 	// Left-aligned: item X = 23 (0x17), box X = 20 (0x14)
-	// -------------------------------------------------------------------
 	for (int i = 0; i < numItems; i++) {
 		int itemY = itemBaseY + i * itemSpacing;
 		const char *text = items[i + 1];
@@ -315,14 +280,10 @@ void InsaneRebel2::drawMenuItems(byte *renderBitmap, int pitch, int width, int h
 		int textX = leftAligned ? (highRes ? 0x2e : 0x17) : (centerX - textWidth / 2);
 		drawString(text, textX, itemY);
 
-		// Selection highlight box - FUN_004292d0
 		if (i == selection) {
-			// Width: textWidth + ((DAT_0047a808 < 2) - 1 & 6) + 6 = textWidth + 6
 			int bracketWidth = textWidth + (highRes ? 12 : 6);
-			// Height: ((DAT_0047a808 < 2) - 1 & 10) + 10 = 10
 			int bracketHeight = highRes ? 20 : 10;
 
-			// Flash color: (-((DAT_0047a7e4 & 1) == 0) & 8U) - 0x10
 			// bit0==0: 8-16=248(0xF8), bit0==1: 0-16=240(0xF0)
 			byte highlightColor = ((_vm->_system->getMillis() / 133) & 1) ? 248 : 240;
 
@@ -343,7 +304,6 @@ void InsaneRebel2::drawMenuItems(byte *renderBitmap, int pitch, int width, int h
 			if (bottomY >= screenH)
 				bottomY = screenH - 1;
 
-			// FUN_004292d0 - Draw rectangle border (4 lines)
 			for (int x = leftX; x <= rightX && x < screenW; x++) {
 				if (topY >= 0 && topY < screenH)
 					renderBitmap[topY * actualPitch + x] = highlightColor;
@@ -446,9 +406,6 @@ void InsaneRebel2::drawMenuStringRight(byte *renderBitmap, const char *str, int
 
 void InsaneRebel2::drawMenuOverlay(byte *renderBitmap, int pitch, int width, int height) {
 	// Main menu renderer - calls shared drawMenuItems()
-	// Emulates FUN_004147b2 -> FUN_0041f5ae with param_3=7, param_4=0
-	// -------------------------------------------------------------------
-	//
 	// Menu strings loaded from GAME.TRS (keyboard mode indices 10-17):
 	//   TRS index 10: "^f02Game Main Menu"           -> Title (uses TITLFONT)
 	//   TRS index 11: "^f01^c005Start Game"          -> Item 0 (uses SMALFONT, color 5)
@@ -475,13 +432,10 @@ void InsaneRebel2::drawMenuOverlay(byte *renderBitmap, int pitch, int width, int
 		}
 	}
 
-	// FUN_004147b2 line 25: param_3 = (DAT_0047a806 == 0) + 6 = 7 (keyboard mode)
 	drawMenuItems(renderBitmap, pitch, width, height, menuItems, 7, _menuSelection);
 }
 
-// ---------------------------------------------------------------------------
 // Pause Overlay
-// ---------------------------------------------------------------------------
 
 // pauseFillRect -- Helper to fill a rectangle in the frame buffer with bounds checking.
 void pauseFillRect(byte *buf, int bufW, int bufH, int x, int y, int w, int h, byte color) {
@@ -495,14 +449,10 @@ void pauseFillRect(byte *buf, int bufW, int bufH, int x, int y, int w, int h, by
 }
 
 // showPauseOverlay -- Dimmed overlay with metallic frame and "PAUSED" text.
-// Reproduces FUN_405A21 from the original executable.
-//
-// The original dims pixels using a green-channel formula that produces palette
 // indices 16-79, relying on a built-in grayscale ramp at those positions.
 // We instead dim the system palette (25% brightness) which achieves the same
 // visual effect regardless of palette layout. The pixel buffer is only modified
 // for the frame decorations and text ??? the game frame pixels stay unchanged.
-// The original palette is saved in _savedPausePalette and restored on unpause.
 void InsaneRebel2::showPauseOverlay() {
 	SmushPlayer *splayer = ((ScummEngine_v7 *)_vm)->_splayer;
 	if (!splayer)
@@ -519,7 +469,6 @@ void InsaneRebel2::showPauseOverlay() {
 	int screenW = MIN(width, (int)_vm->_screenWidth);
 	int screenH = MIN(height, (int)_vm->_screenHeight);
 
-	// Step 1: Save current palette and create a dimmed version.
 	memcpy(_savedPausePalette, palette, 768);
 	_pauseOverlayActive = true;
 
@@ -544,7 +493,6 @@ void InsaneRebel2::showPauseOverlay() {
 
 	_vm->_system->getPaletteManager()->setPalette(dimPal, 0, 256);
 
-	// Step 2: Draw the metallic frame (FUN_405A21 lines 261-283).
 	// Horizontal border lines: 2px thick at y=23 and y=175
 	pauseFillRect(frameBuffer, width, height, 0, 0x17, 0x140, 2, 0x50);
 	pauseFillRect(frameBuffer, width, height, 0, 0xAF, 0x140, 2, 0x50);
@@ -575,8 +523,6 @@ void InsaneRebel2::showPauseOverlay() {
 		pauseFillRect(frameBuffer, width, height, xBase,     yOff + 1, 0x19, 0x0F, 4);
 	}
 
-	// Step 3: Draw "Game Paused" text using TRS string 0x78 (120).
-	// Original: FUN_00434cb0(-1, buf, NULL, x=10, y=10, align=1, fg=4, bg=0x10, text)
 	// fg=4 is the foreground color (used by ^cNNN in the TRS string itself).
 	const char *pauseText = splayer->getString(0x78);
 	if (!pauseText || !pauseText[0])
@@ -589,17 +535,14 @@ void InsaneRebel2::showPauseOverlay() {
 	}
 	if (multiFont) {
 		Common::Rect clipRect(0, 0, screenW, screenH);
-		// Original uses x=10, y=10 (single-buffer mode) or y=20 (double-buffer mode).
 		// We use single-buffer mode (y=10).
 		multiFont->drawString(pauseText, frameBuffer, clipRect, 10, 10, width, 4, kStyleAlignLeft);
 	}
 
-	// Step 4: Push to screen.
 	_vm->_system->copyRectToScreen(frameBuffer, width, 0, 0, screenW, screenH);
 	_vm->_system->updateScreen();
 }
 
-// runMainMenu -- Main menu loop (FUN_004147B2).
 // Returns kMenuNewGame, kMenuResumeDemo, or kMenuQuit.
 int InsaneRebel2::runMainMenu() {
 
@@ -662,13 +605,8 @@ int InsaneRebel2::runMainMenu() {
 		// A selection was made - process it
 		debugC(DEBUG_INSANE, "Menu video ended with selection=%d", _menuSelection);
 
-		// Process the menu result based on current selection
-		// Menu items matching GAME.TRS indices 11-17 (FUN_004147B2):
 		//   case 0 (TRS 11): Start Game -> pilot selection, returns 2
-		//   case 1 (TRS 12): Options -> FUN_00416787 options screen
-		//   case 2 (TRS 13): Calibrate Joystick -> FUN_00425820
 		//   case 3 (TRS 14): Continue Intro -> return to intro/demo loop
-		//   case 4 (TRS 15): Show Top Pilots -> FUN_00420116(-1)
 		//   case 5 (TRS 16): Show Credits -> play O_CREDIT.SAN, returns 1
 		//   case 6 (TRS 17): Return to Launcher -> quit, returns 0
 		switch (_menuSelection) {
@@ -678,7 +616,7 @@ int InsaneRebel2::runMainMenu() {
 			_menuInputActive = false;
 			return kMenuNewGame;  // Return 2 (kMenuNewGame)
 
-		case 1:  // Options -> show options menu (FUN_00416787)
+		case 1:
 			debugC(DEBUG_INSANE, "Options selected");
 			showOptionsMenu();
 			break;
@@ -694,7 +632,7 @@ int InsaneRebel2::runMainMenu() {
 			_menuInputActive = false;
 			return kMenuResumeDemo;
 
-		case 4:  // Show Top Pilots -> high score display (FUN_00420116(-1))
+		case 4:
 			debugC(DEBUG_INSANE, "Show Top Pilots selected");
 			showTopPilots();
 			break;
@@ -708,7 +646,6 @@ int InsaneRebel2::runMainMenu() {
 			_gameState = kStateMainMenu;
 			_menuInputActive = true;
 			resetMenuGamepadAxis();
-			// Returns 1 in original -> stays at stage 1 (main menu)
 			break;
 
 		case 6:  // Return to Launcher -> quit game
@@ -726,11 +663,8 @@ int InsaneRebel2::runMainMenu() {
 	return kMenuQuit;
 }
 
-// ---------------------------------------------------------------------------
 // Chapter Selection Screen
-// ---------------------------------------------------------------------------
 
-// runChapterSelect -- Chapter selection with preview (FUN_00415CF8).
 // Returns kChapterSelectPlay, kChapterSelectBack, or kChapterSelectQuit.
 int InsaneRebel2::runChapterSelect() {
 
@@ -743,7 +677,6 @@ int InsaneRebel2::runChapterSelect() {
 	resetMenuGamepadAxis();
 
 	// Initialize chapter selection state
-	// Original (lines 51-54): local_10 = 0xf; while (local_10 > 0 && locked) local_10--;
 	// Finds highest unlocked chapter. With debug unlock all = 15 (FINALE).
 	_chapterSelection = 15;
 	while (_chapterSelection > 0 && !_chapterUnlocked[_chapterSelection]) {
@@ -762,7 +695,6 @@ int InsaneRebel2::runChapterSelect() {
 	_previewOffsetX = -90;
 	_previewOffsetY = _chapterSelection * -50 + 75;
 
-	// Set iactBits for chapter unlock state (FUN_00415CF8 lines 79-86)
 	// Bits 16..1 correspond to chapters 0..15: set if unlocked, clear if locked.
 	// These control SKIP chunks in O_LEVEL.SAN for locked/unlocked preview variants.
 	for (int i = 0; i < 16; i++) {
@@ -772,8 +704,6 @@ int InsaneRebel2::runChapterSelect() {
 			clearBit(16 - i);
 	}
 
-	// Chapter selection background - FUN_00415CF8 line 57:
-	// FUN_0041f4d0(s_OPEN_O_LEVEL_SAN, 8, 0xffff, 0xffff, 0)
 	// O_LEVEL.SAN contains chapter preview thumbnails at specific FOBJ positions.
 	// The FOBJ offset system scrolls the correct preview into the preview box area.
 	while (!_vm->shouldQuit()) {
@@ -782,7 +712,6 @@ int InsaneRebel2::runChapterSelect() {
 		debugC(DEBUG_INSANE, "Playing chapter select background: OPEN/O_LEVEL.SAN");
 
 		// Flags: 0x20 (overlay/menu rendering) | 0x08 (preserve buffer, suppress
-		// AHDR speed override). Matches original FUN_0041f4d0 parameter 8.
 		// O_LEVEL.SAN AHDR specifies 15fps; flag 0x08 suppresses this override
 		// so we use our intended 12fps. The preview screen is cleared each frame
 		// by procPreRendering's memset, so buffer preservation is harmless.
@@ -807,7 +736,6 @@ int InsaneRebel2::runChapterSelect() {
 
 		debugC(DEBUG_INSANE, "Chapter selection made: %d", _chapterSelection);
 
-		// Process chapter selection (lines 134-236 of FUN_00415CF8)
 		if (_chapterSelection == 16) {
 			// BACK selected (index 16 = 17th item)
 			debugC(DEBUG_INSANE, "BACK to main menu selected");
@@ -826,13 +754,11 @@ int InsaneRebel2::runChapterSelect() {
 				return kChapterSelectPlay;
 			}
 
-			// Locked chapter ??? validate password (FUN_00415CF8 lines 239-257)
 			if (_activePilot >= 0 && _activePilot < _numPilots &&
 			    _pilots[_activePilot].difficulty < 6 && _chapterSelection > 0) {
 				Common::String expected = getChapterPassword(
 					_chapterSelection, _pilots[_activePilot].difficulty);
 				if (!expected.empty() && _passwordInput.equalsIgnoreCase(expected)) {
-					// Password accepted ??? unlock chapter (FUN_00415CF8 lines 253-257)
 					PilotData &pilot = _pilots[_activePilot];
 					pilot.score[_chapterSelection] = 0;
 					pilot.damage[_chapterSelection] = 0;
@@ -840,7 +766,6 @@ int InsaneRebel2::runChapterSelect() {
 					pilot.rating[_chapterSelection] = 0;
 					savePilots();
 					_chapterUnlocked[_chapterSelection] = true;
-					// Update iactBit for video preview (original jumps to LAB_00415d88)
 					setBit(16 - _chapterSelection);
 					_passwordInput.clear();
 					updateMenuVirtualKeyboard();
@@ -861,7 +786,6 @@ int InsaneRebel2::runChapterSelect() {
 
 int InsaneRebel2::processChapterSelectInput() {
 	// Process input for chapter selection screen
-	// Emulates input handling in FUN_00415CF8 (lines 95-133)
 	// Returns: -1 = no action, 0+ = item selected
 
 	int result = -1;
@@ -880,7 +804,6 @@ int InsaneRebel2::processChapterSelectInput() {
 					_chapterSelection = _chapterItemCount - 1;
 				}
 				_passwordInput.clear();
-				// Update preview offset (FUN_00425170: Y = selected * -50 + 75)
 				_previewOffsetY = _chapterSelection * -50 + 75;
 				updateMenuVirtualKeyboard();
 				debugC(DEBUG_INSANE, "ChapterSelect: Selection changed to %d (UP) offsetY=%d", _chapterSelection, _previewOffsetY);
@@ -893,7 +816,6 @@ int InsaneRebel2::processChapterSelectInput() {
 					_chapterSelection = 0;
 				}
 				_passwordInput.clear();
-				// Update preview offset (FUN_00425170: Y = selected * -50 + 75)
 				_previewOffsetY = _chapterSelection * -50 + 75;
 				updateMenuVirtualKeyboard();
 				debugC(DEBUG_INSANE, "ChapterSelect: Selection changed to %d (DOWN) offsetY=%d", _chapterSelection, _previewOffsetY);
@@ -915,7 +837,6 @@ int InsaneRebel2::processChapterSelectInput() {
 				break;
 
 			case Common::KEYCODE_BACKSPACE:
-				// Backspace for password input (line 107-112 of FUN_00415CF8)
 				if (!_passwordInput.empty()) {
 					_passwordInput.deleteLastChar();
 					debugC(DEBUG_INSANE, "ChapterSelect: Password backspace, now: %s", _passwordInput.c_str());
@@ -923,7 +844,6 @@ int InsaneRebel2::processChapterSelectInput() {
 				break;
 
 			default:
-				// Printable character for password input (lines 114-121 of FUN_00415CF8)
 				if (event.kbd.ascii >= 0x20 && event.kbd.ascii <= 0x7E) {
 					if (_passwordInput.size() < 8) {
 						_passwordInput += (char)event.kbd.ascii;
@@ -959,7 +879,6 @@ int InsaneRebel2::processChapterSelectInput() {
 
 		case Common::EVENT_MOUSEMOVE:
 			{
-				// Mouse hover changes highlight (original FUN_0041f5ae mouse mode).
 				// Item Y = numItems * -5 + i * 10 + 0x68
 				const bool highRes = isHiRes();
 				const int baseY = highRes ? (_chapterItemCount * -5 + 0x5a) * 2 + 0x1c : _chapterItemCount * -5 + 0x68;
@@ -992,12 +911,9 @@ int InsaneRebel2::processChapterSelectInput() {
 	return result;
 }
 
-// Draw preview box border - emulates FUN_004292D0 calls at lines 128-133 of FUN_00415CF8
 void InsaneRebel2::drawPreviewBox(byte *renderBitmap, int pitch, int width, int height) {
-	// Low-res (320x200) coordinates from FUN_00415CF8:
 	// Outer box: X=0xe4 (228), Y=0x49 (73), W=0x54 (84), H=0x36 (54), color=0xF8
 	// Inner box: X=0xe5 (229), Y=0x4a (74), W=0x52 (82), H=0x34 (52), color=4
-	// High-res uses the original DAT_0047a808 >= 2 formulas, doubling the
 	// low-res anchors and dimensions.
 	const int scale = isHiRes() ? 2 : 1;
 
@@ -1065,7 +981,6 @@ void InsaneRebel2::drawPreviewBox(byte *renderBitmap, int pitch, int width, int
 	}
 }
 
-// Rating to medal string (FUN_0042001f): TALKFONT glyphs 3=big, 2=medium, 1=small
 Common::String InsaneRebel2::getRankString(int rating) {
 	if (rating > 50)
 		rating = 50;
@@ -1076,8 +991,6 @@ Common::String InsaneRebel2::getRankString(int rating) {
 	return result;
 }
 
-// Password table lookup - emulates FUN_0041BCE0
-// 90 entries: 15 levels ?? 6 difficulty slots, extracted from RA2WIN95.EXE at 0x481AF0
 // Index formula: difficulty + (level * 3 - 3) * 2, level is 1-based (1-15), difficulty 0-5
 const char *const kPasswordTable[90] = {
 	// Level 1:  diff 0-5
@@ -1168,10 +1081,7 @@ void InsaneRebel2::drawChapterInfoLine(byte *renderBitmap, int pitch, int width,
 }
 
 // Draw chapter selection overlay - called during O_LEVEL.SAN playback
-// FUN_00415CF8 - Chapter selection screen with preview thumbnail
 void InsaneRebel2::drawChapterSelectOverlay(byte *renderBitmap, int pitch, int width, int height) {
-	// Emulates FUN_00415CF8 rendering via shared drawMenuItems(leftAligned=true)
-	//
 	// GAME.TRS chapter selection strings:
 	//   TRS 40     = "^f02Chapters" (title)
 	//   TRS 41-56  = unlocked chapter names (e.g. "^f01^c244Chapter 1 - The Dreighton Triangle")
@@ -1179,18 +1089,14 @@ void InsaneRebel2::drawChapterSelectOverlay(byte *renderBitmap, int pitch, int w
 	//   TRS 60     = "^f02Chapters" (title, locked section duplicate)
 	//   TRS 61-76  = locked chapter names (e.g. "^f01^c244Chapter 1 -")
 	//   TRS 77     = "^f01^c240RETURN TO PILOTS" (locked section duplicate)
-	//
 	// Menu array: items[0]=title, items[1..16]=chapters, items[17]=RETURN TO PILOTS
-	// FUN_0041f5ae(0, &DAT_004577a8, 0x11, 1): param_3=17, param_4=1 (left-aligned)
 
 	SmushPlayer *splayer = ((ScummEngine_v7 *)_vm)->_splayer;
 	if (!splayer)
 		return;
 
-	// Build items array matching original DAT_004577a8 layout
 	const char *items[18];
 
-	// Original (FUN_00415CF8 lines 55-77): starts with ALL locked strings,
 	// then overrides unlocked chapters individually.
 	// items[0] = title = TRS 60 ("^f02Chapters")
 	items[0] = splayer->getString(60);
@@ -1214,8 +1120,6 @@ void InsaneRebel2::drawChapterSelectOverlay(byte *renderBitmap, int pitch, int w
 	// Render menu using shared renderer with left-aligned mode
 	drawMenuItems(renderBitmap, pitch, width, height, items, 17, _chapterSelection, true);
 
-	// Draw preview box border on the right side (FUN_004292d0 calls at lines 128-133)
-	// The preview thumbnail is rendered by O_LEVEL.SAN via FOBJ offset + STOR/FTCH.
 	// SKIP chunks in O_LEVEL.SAN use iactBits to show locked/unlocked preview variants.
 	drawPreviewBox(renderBitmap, pitch, width, height);
 
@@ -1223,12 +1127,8 @@ void InsaneRebel2::drawChapterSelectOverlay(byte *renderBitmap, int pitch, int w
 	drawChapterInfoLine(renderBitmap, pitch, width, height);
 }
 
-// ---------------------------------------------------------------------------
-// Pilot Selection Menu (FUN_00414A41)
-// ---------------------------------------------------------------------------
 // Pilot/save selection before chapter selection.
 
-// runLevelSelect -- Pilot selection menu (FUN_00414A41).
 // Returns kLevelSelectPlay, kLevelSelectBack, or kLevelSelectQuit.
 int InsaneRebel2::runLevelSelect() {
 
@@ -1271,7 +1171,6 @@ int InsaneRebel2::runLevelSelect() {
 		_vm->_smushVideoShouldFinish = false;
 		_menuSelectionConfirmed = false;
 
-		// --- Difficulty submenu completed ---
 		if (_pilotMenuMode == kPilotModeDifficulty) {
 			// Store difficulty in the new pilot and save
 			if (_pilotEditIndex >= 0 && _pilotEditIndex < _numPilots) {
@@ -1293,7 +1192,6 @@ int InsaneRebel2::runLevelSelect() {
 			return kLevelSelectPlay;
 		}
 
-		// --- Name input completed ---
 		if (_pilotMenuMode == kPilotModeNameInput) {
 			// Name was confirmed ??? now show difficulty submenu
 			if (_pilotEditIndex >= 0 && _pilotEditIndex < _numPilots) {
@@ -1308,7 +1206,6 @@ int InsaneRebel2::runLevelSelect() {
 			continue;
 		}
 
-		// --- Pilot operation submenu completed ---
 		if (_pilotMenuMode == kPilotModeCopySelect || _pilotMenuMode == kPilotModeDeleteSelect) {
 			int pilotIndex = _levelSelection;
 			if (pilotIndex >= 0 && pilotIndex < _numPilots) {
@@ -1335,7 +1232,6 @@ int InsaneRebel2::runLevelSelect() {
 			continue;
 		}
 
-		// --- Normal pilot menu selection ---
 		debugC(DEBUG_INSANE, "Pilot selection: %d (numPilots=%d)", _levelSelection, _numPilots);
 
 		if (_levelSelection < _numPilots) {
@@ -1369,7 +1265,6 @@ int InsaneRebel2::runLevelSelect() {
 			continue;
 
 		} else if (_levelSelection == _numPilots + 1) {
-			// COPY PILOT - original opens a second menu to select the source pilot.
 			if (_numPilots > 0 && _numPilots < kMaxPilots) {
 				_pilotMenuMode = kPilotModeCopySelect;
 				_levelSelection = 0;
@@ -1380,7 +1275,6 @@ int InsaneRebel2::runLevelSelect() {
 			continue;
 
 		} else if (_levelSelection == _numPilots + 2) {
-			// DELETE PILOT - original opens a second menu to select the target pilot.
 			if (_numPilots > 0) {
 				_pilotMenuMode = kPilotModeDeleteSelect;
 				_levelSelection = 0;
@@ -1411,7 +1305,6 @@ int InsaneRebel2::processLevelSelectInput() {
 	int result = -1;
 
 	// Name input mode ??? keyboard goes to _pilotNameInput instead of menu nav
-	// Original: FUN_00414A41 lines 87-116
 	if (_pilotMenuMode == kPilotModeNameInput) {
 		while (!_menuEventQueue.empty()) {
 			Common::Event event = _menuEventQueue.pop();
@@ -1577,10 +1470,7 @@ int InsaneRebel2::processLevelSelectInput() {
 }
 
 void InsaneRebel2::drawLevelSelectOverlay(byte *renderBitmap, int pitch, int width, int height) {
-	// -------------------------------------------------------------------
 	// Pilot selection / difficulty submenu renderer
-	// Emulates FUN_00414A41 ??? FUN_0041f5ae
-	// -------------------------------------------------------------------
 
 	SmushPlayer *splayer = ((ScummEngine_v7 *)_vm)->_splayer;
 	if (!splayer) {
@@ -1589,12 +1479,7 @@ void InsaneRebel2::drawLevelSelectOverlay(byte *renderBitmap, int pitch, int wid
 	}
 
 	if (_gameState == kStateDifficultySelect) {
-		// -------------------------------------------------------------------
 		// Difficulty submenu - LAB_00414ff6
-		// FUN_0041f5ae(0, &DAT_00457458, 6, 0)
-		// DAT_00457458 = 7 entries loaded from TRS 110-116 (FUN_00414073 lines 47-50)
-		// param_3 = 6 ??? items[0]=title(TRS 110), items[1..6]=selectable(TRS 111-116)
-		// -------------------------------------------------------------------
 		const char *diffItems[7];
 		for (int i = 0; i < 7; i++) {
 			diffItems[i] = splayer->getString(110 + i);
@@ -1630,9 +1515,6 @@ void InsaneRebel2::drawLevelSelectOverlay(byte *renderBitmap, int pitch, int wid
 		return;
 	}
 
-	// -------------------------------------------------------------------
-	// Pilot menu - FUN_0041f5ae(0, &DAT_00457768, N+4, 0)
-	// -------------------------------------------------------------------
 	// items[0]    = title (TRS 20)
 	// items[1..N] = saved pilots (formatted with ^f01^c005<name>^f00)
 	// items[N+1]  = TRS 21 (ADD NEW PILOT)
@@ -1641,7 +1523,6 @@ void InsaneRebel2::drawLevelSelectOverlay(byte *renderBitmap, int pitch, int wid
 	// items[N+4]  = TRS 24 (RETURN TO MAIN MENU)
 
 	// Build pilot name strings with font/color formatting
-	// Original uses "^f01^c005<name>^f00" for pilot entries
 	Common::String pilotNameStrs[kMaxPilots];
 	for (int i = 0; i < _numPilots; i++) {
 		if (_pilotMenuMode == kPilotModeNameInput && i == _pilotEditIndex) {
@@ -1678,18 +1559,12 @@ void InsaneRebel2::drawLevelSelectOverlay(byte *renderBitmap, int pitch, int wid
 	drawMenuItems(renderBitmap, pitch, width, height, pilotItems, _numPilots + 4, _levelSelection);
 }
 
-// ---------------------------------------------------------------------------
-// Top Pilots Screen (FUN_00420116)
-// ---------------------------------------------------------------------------
 // Ranked pilot scores with animated reveal over a menu background video.
-// 0x4a (74) byte records (max 15, from FUN_00410271):
 //   +0x00 (4): timestamp, +0x04 (40): name, +0x36 (4): score,
 //   +0x3a (4): rating, +0x3e (2): difficulty tier (1-3, TRS=value+0x9b),
 //   +0x40 (2): highest chapter (1-15).
 // Column X positions (low-res): medals=43, name=88, diff=195, ch=245, score=295.
-// Row Y: sVar1 * 10 + 42.
 
-// initDefaultRankings -- Fill ranking table with defaults (FUN_0040FF00).
 // Generates 15 placeholder entries: score=(15-i)*1500, rating=(15-i)*2,
 // difficulty=((15-i)*3+14)/15, chapter=((15-i)*15+14)/15.
 void InsaneRebel2::initDefaultRankings() {
@@ -1707,7 +1582,6 @@ void InsaneRebel2::initDefaultRankings() {
 	}
 }
 
-// insertRanking -- Insert into sorted ranking table (FUN_00410271).
 void InsaneRebel2::insertRanking(const char *name, int32 score, int32 rating,
                                   int16 difficulty, int16 chapter) {
 	if (score == 0)
@@ -1761,7 +1635,6 @@ void InsaneRebel2::showTopPilots() {
 		_menuEventQueue.pop();
 	resetMenuGamepadAxis();
 
-	// param_1 = -1 from main menu: maxFrames = 120 (0x78)
 	_topPilotsMaxFrames = 120;
 	_topPilotsFrameCount = 0;
 
@@ -1854,14 +1727,9 @@ void InsaneRebel2::drawTopPilotsOverlay(byte *renderBitmap, int pitch, int width
 	_topPilotsFrameCount++;
 }
 
-// ---------------------------------------------------------------------------
-// Options Menu (FUN_004167A6)
-// ---------------------------------------------------------------------------
-// Toggle labels and slider items. Settings at DAT_00482e20[0..3].
 // TRS IDs: Title=89, Music=90/91, SFX=92/93, Voices=94/95, Text=96/97,
 // Controls=98/99, Rapid Fire=100/101, Volume=103 "%hd%%", Back=107.
 
-// showOptionsMenu -- Options menu loop (FUN_00416787).
 void InsaneRebel2::showOptionsMenu() {
 	debugC(DEBUG_INSANE, "Showing Options menu (FUN_00416787)");
 
@@ -1915,7 +1783,6 @@ int InsaneRebel2::processOptionsInput() {
 				return -1;
 
 			case Common::KEYCODE_LEFT:
-				// Volume slider: decrease by 4 (original step size)
 				if (_optionsSelection == 6) {
 					_optVolumeLevel = MAX(0, _optVolumeLevel - 4);
 					setRebel2MixerVolume(_vm, _optVolumeLevel);
@@ -2007,7 +1874,6 @@ void InsaneRebel2::drawOptionsOverlay(byte *renderBitmap, int pitch, int width,
 
 	_optTextEnabled = ConfMan.getBool("subtitles");
 
-	// Build items array from TRS strings based on current toggle states
 	// TRS 89: title, 90/91: music, 92/93: sfx, 94/95: voices,
 	// 96/97: text, 98/99: controls, 100/101: rapid fire, 103: volume, 107: back
 	const char *items[10];  // title + up to 9 selectable
diff --git a/engines/scumm/insane/rebel2/rebel.cpp b/engines/scumm/insane/rebel2/rebel.cpp
index 81a1364d665..4bada20bbd6 100644
--- a/engines/scumm/insane/rebel2/rebel.cpp
+++ b/engines/scumm/insane/rebel2/rebel.cpp
@@ -169,8 +169,6 @@ InsaneRebel2::InsaneRebel2(ScummEngine_v7 *scumm) {
 	_smush_iconsNut = new NutRenderer(_vm, highRes ? "SYSTM/CPITIMHI.NUT" : "SYSTM/CPITIMAG.NUT");
 	_smush_icons2Nut = nullptr;  // Not used for Rebel2
 
-	// Initialize laser texture buffer (DAT_0047fee4) from sprite 5 of CPITIMAG/CPITIMHI.NUT
-	// This is done by FUN_0040BAB0/FUN_0040BB87 in the original with sprite index 5
 	// Sprite 5 is 136x13 pixels - a wide, thin texture perfect for laser beams
 	_laserTexture.pixels = nullptr;
 	_laserTexture.width = 0;
@@ -179,11 +177,8 @@ InsaneRebel2::InsaneRebel2(ScummEngine_v7 *scumm) {
 		initLaserTexture(_smush_iconsNut, 5);
 	}
 
-	// Initialize edge blend table with default identity table (FUN_404BCE -> FUN_410510(NULL))
 	// Per-level tables are loaded later via IACT opcode 8 par4=1000
 	initEdgeTable(nullptr);
-	// DAT_0047a7fc: Controls edge highlight rendering and widescreen features.
-	// Set from param_10 of FUN_403BD0 (main game init). Values:
 	//   < 0: Edge highlights disabled (low-detail mode)
 	//   >= 0: Edge highlights enabled, >= 1: high-detail (secondary NUTs, widescreen)
 	// Always use high detail.
@@ -193,9 +188,7 @@ InsaneRebel2::InsaneRebel2(ScummEngine_v7 *scumm) {
 	// Load DIHIFONT.NUT for in-video messages/subtitles (Opcode 9)
 	_rebelMsgFont = makeRebel2Font(_vm, "SYSTM/DIHIFONT.NUT");
 
-	// Load menu system fonts (from info.md - FUN_403BD0 lines 302-348)
 	// In low resolution mode, fonts are loaded as a linked list:
-	//   Font 0 (^f00): TALKFONT.NUT - Default menu font (DAT_00485058)
 	//   Font 1 (^f01): SMALFONT.NUT - Small font for format code switching
 	//   Font 2 (^f02): TITLFONT.NUT - Title font
 	//   Font 3 (^f03): POVFONT.NUT - POV font
@@ -226,7 +219,6 @@ InsaneRebel2::InsaneRebel2(ScummEngine_v7 *scumm) {
 	_hiResPresentationViewY = 0;
 	_gameplayPresentationClipBottom = 179;
 
-	// Damage visual effect counters (FUN_420515/420562/420754/42073B)
 	_damageFlashCounter = 0;
 	_damageHighFlashCounter = 0;
 	_damageShakeCounter = 0;
@@ -234,7 +226,6 @@ InsaneRebel2::InsaneRebel2(ScummEngine_v7 *scumm) {
 	memset(_damageRestorePalette, 0, sizeof(_damageRestorePalette));
 	_damageRestorePaletteValid = false;
 
-	// Text overlay state (FUN_004171c5 chapter title rendering)
 	_textOverlayActive = false;
 	_textOverlayID = 0;
 	_textOverlayX = 0;
@@ -242,7 +233,6 @@ InsaneRebel2::InsaneRebel2(ScummEngine_v7 *scumm) {
 	_textOverlayFadeIn = 0;
 	_textOverlayFadeOut = 0;
 
-	// Original globals mapped: hit counter, cooldown, movie/auto-play flags.
 	_rebelOp6Initialized = false;
 	_rebelHitCounter = 0;
 	_rebelKillCounter = 0;
@@ -265,7 +255,6 @@ InsaneRebel2::InsaneRebel2(ScummEngine_v7 *scumm) {
 	_rebelViewMode1 = 0;
 	_rebelViewMode2 = 0;
 
-	// Initialize mirrored original counters.
 	for (int i = 0; i < 10; ++i) {
 		_rebelValueCounters[i] = 0;
 		_rebelMaskCounters[i] = 0;
@@ -283,7 +272,7 @@ InsaneRebel2::InsaneRebel2(ScummEngine_v7 *scumm) {
 		_rebelGaugeCleared[i] = false;
 
 	_difficulty = 1; // Default to Medium.
-	_targetLockTimer = 0;  // DAT_00443676 equivalent
+	_targetLockTimer = 0;
 
 	_speed = 12;
 	_insaneIsRunning = false;
@@ -381,15 +370,12 @@ InsaneRebel2::InsaneRebel2(ScummEngine_v7 *scumm) {
 		_primaryZones[i].active = false;
 		_secondaryZones[i].active = false;
 	}
-	// Corridor boundaries in game coordinate space (FUN_40C040 lines 21-24)
-	// DAT_00443b0a=0, DAT_00443b0c=0, DAT_00443b0e=0x1a8(424), DAT_00443b10=0x104(260)
 	_corridorLeftX = 0;
 	_corridorTopY = 0;
 	_corridorRightX = 0x1A8;   // 424 ??? full game buffer width
 	_corridorBottomY = 0x104;  // 260 ??? full game buffer height
 	_hitCooldown = 0;
 
-	// Initialize Handler 0x26 Turret shot system (FUN_40AD63)
 	for (i = 0; i < 2; i++) {
 		_turretShots[i].counter = 0;
 		_turretShots[i].targetX = 0;
@@ -400,7 +386,6 @@ InsaneRebel2::InsaneRebel2(ScummEngine_v7 *scumm) {
 	}
 	_turretShotSeqCounter = 0;
 
-	// Initialize Handler 8 Vehicle shot system (FUN_402ED0)
 	for (i = 0; i < 2; i++) {
 		_vehicleShots[i].counter = 0;
 		_vehicleShots[i].targetX = 0;
@@ -414,7 +399,6 @@ InsaneRebel2::InsaneRebel2(ScummEngine_v7 *scumm) {
 	}
 	_vehicleShotImpactIndex = 0;
 
-	// Initialize Handler 7 Space shot system (FUN_40FADF)
 	for (i = 0; i < 2; i++) {
 		_spaceShots[i].counter = 0;
 		_spaceShots[i].targetX = 0;
@@ -454,8 +438,8 @@ InsaneRebel2::InsaneRebel2(ScummEngine_v7 *scumm) {
 	_shipTargetX = 0xa0;
 	_shipTargetY = 0x28;
 	_shipLevelMode = 0;
-	_movementRangeLimit = 127;  // DAT_0047e034 - Start at full range (shooting state)
-	_flyControlMode = 0;   // DAT_004437c0 - Start in flight-only mode (no shooting)
+	_movementRangeLimit = 127;
+	_flyControlMode = 0;
 	_shipFiring = false;
 	_prevMouseButtons = 0; // For edge detection in mouse button handling
 	_shipDirectionH = 2;   // Start centered horizontally (0-4 range)
@@ -467,34 +451,34 @@ InsaneRebel2::InsaneRebel2(ScummEngine_v7 *scumm) {
 	_flyLaserSprite = nullptr;   // FLY002 - effect sprites (danger/overlay cues)
 	_flyTargetSprite = nullptr;  // FLY003 - targeting overlay
 	_flyHiResSprite = nullptr;   // FLY004 - high-res alternative
-	_flyEffectAnimCounter = 0;   // DAT_0047ff1c
-	_flyOverlayRepeatCount = 0;  // DAT_00443b52
-	_flyShipScreenX = 0xd4;      // Start at center (212) - matches DAT_00443708 default
-	_flyShipScreenY = 0x82;      // Start at center (130) - matches DAT_0044370a default
-	_smoothedVelocity = 0;       // DAT_0044370c
-	_verticalInput = 0;          // DAT_0044370e
-	memset(_velocityHistory, 0, sizeof(_velocityHistory));  // DAT_00443716
-	memset(_windHistoryX, 0, sizeof(_windHistoryX));         // DAT_00443b16
-	memset(_windHistoryY, 0, sizeof(_windHistoryY));         // DAT_00443b34
-	_windParamX = 0;             // DAT_00443b12
-	_windParamY = 0;             // DAT_00443b14
-	_perspectiveX = 0;           // DAT_00443712
-	_perspectiveY = 0;           // DAT_00443714
-	_viewShift = 0;              // DAT_00443710
-	_facingRight = false;        // DAT_0047ab8c
+	_flyEffectAnimCounter = 0;
+	_flyOverlayRepeatCount = 0;
+	_flyShipScreenX = 0xd4;
+	_flyShipScreenY = 0x82;
+	_smoothedVelocity = 0;
+	_verticalInput = 0;
+	memset(_velocityHistory, 0, sizeof(_velocityHistory));
+	memset(_windHistoryX, 0, sizeof(_windHistoryX));
+	memset(_windHistoryY, 0, sizeof(_windHistoryY));
+	_windParamX = 0;
+	_windParamY = 0;
+	_perspectiveX = 0;
+	_perspectiveY = 0;
+	_viewShift = 0;
+	_facingRight = false;
 
 	// Initialize Handler 25 GRD ship system
-	_grd001Sprite = nullptr;     // DAT_00482240 - GRD001 primary ship
-	_grd002Sprite = nullptr;     // DAT_00482238 - GRD002 secondary ship
-	_grd005Sprite = nullptr;     // DAT_00482258 - GRD005 mode 3 overlay
-	_grdSpriteMode = 0;          // DAT_00457900 - sprite mode (1,2,3,4)
+	_grd001Sprite = nullptr;
+	_grd002Sprite = nullptr;
+	_grd005Sprite = nullptr;
+	_grdSpriteMode = 0;
 	memset(_grdShotOriginX, 0, sizeof(_grdShotOriginX));
 	memset(_grdShotOriginY, 0, sizeof(_grdShotOriginY));
 	_grdShotOriginTableLoaded = false;
 
 	// Initialize Handler 0x26 turret HUD overlay system
-	_hudOverlayNut = nullptr;    // DAT_0047fe78 - Primary HUD overlay (GRD files, animated)
-	_hudOverlay2Nut = nullptr;   // DAT_0047fe80 - Secondary HUD overlay
+	_hudOverlayNut = nullptr;
+	_hudOverlay2Nut = nullptr;
 
 	// Initialize audio system for RA2 (since we don't use iMUSE).
 	// Individual SMUSH audio blocks carry their own source rate.
@@ -519,7 +503,6 @@ InsaneRebel2::InsaneRebel2(ScummEngine_v7 *scumm) {
 	// Main menu has 7 selectable items (0-6) matching GAME.TRS indices 11-17:
 	//   0: Start Game, 1: Options, 2: Calibrate Joystick, 3: Continue Intro,
 	//   4: Show Top Pilots, 5: Show Credits, 6: Return to Launcher
-	// FUN_0041f5ae uses the selectable item count for Y position calculation.
 	_menuItemCount = 7;
 	_menuInactivityTimer = 0;
 	_menuInactivityTimedOut = false;
@@ -530,7 +513,6 @@ InsaneRebel2::InsaneRebel2(ScummEngine_v7 *scumm) {
 		_levelUnlocked[i] = (i == 0);  // Only level 1 unlocked initially
 	}
 
-	// Initialize chapter selection system (FUN_00415CF8)
 	// 17 items: 16 chapters + BACK option
 	_chapterSelection = 0;        // First chapter selected
 	_chapterItemCount = 17;       // 16 chapters + BACK
@@ -538,7 +520,6 @@ InsaneRebel2::InsaneRebel2(ScummEngine_v7 *scumm) {
 	_passwordInput = "";          // No password input
 
 	// Debug flag to unlock all chapters for testing
-	// Based on original debug mode (DAT_0047ab34 == 'd') from FUN_00415CF8
 	// Set to true to bypass normal unlock progression
 	_debugUnlockAll = ConfMan.getBool("rebel2_unlock_all");
 	_noDamage = ConfMan.getBool("rebel2_no_damage");
@@ -547,11 +528,9 @@ InsaneRebel2::InsaneRebel2(ScummEngine_v7 *scumm) {
 	for (i = 0; i < 16; i++) {
 		// If debug unlock is enabled, unlock all chapters
 		// Otherwise only chapter 1 (index 0) is unlocked initially
-		// Original: pilotData->scores[chapter] < 0xFF means unlocked
 		_chapterUnlocked[i] = _debugUnlockAll || (i == 0);
 	}
 
-	// Initialize preview offset for chapter selection (FUN_00425170)
 	// X offset: -90 (0xffa6), Y offset: selection * -50 + 75
 	_previewOffsetX = -90;
 	_previewOffsetY = 75;  // Chapter 0: 0 * -50 + 75 = 75
@@ -564,22 +543,19 @@ InsaneRebel2::InsaneRebel2(ScummEngine_v7 *scumm) {
 	}
 	loadPilots(); // Load saved pilots from disk
 
-	// Initialize pilot selection system (FUN_00414A41)
 	// Menu structure: [saved pilots] + 4 fixed options (NEW/DUPE/DELETE/MAIN MENU)
 	_levelSelection = 0;          // First item selected
 	_levelItemCount = _numPilots + 4; // N saved pilots + 4 fixed options
 	_selectedLevel = 1;           // Default selected level
-	_difficultySelection = 2;     // Default to 3rd difficulty (matching original init param_3=2)
+	_difficultySelection = 2;
 	_pilotMenuMode = kPilotModeSelect;
 	_pilotNameInput = "";
 	_pilotEditIndex = -1;
 
-	// Initialize top pilots display state and ranking table (FUN_0040FF00)
 	_topPilotsFrameCount = 0;
 	_topPilotsMaxFrames = 120;
 	initDefaultRankings();
 
-	// Initialize options menu state (FUN_004167A6 defaults)
 	_optionsSelection = 0;
 	_optionsItemCount = 8;
 	_optMusicEnabled = !_vm->_mixer->isSoundTypeMuted(Audio::Mixer::kMusicSoundType);
@@ -664,7 +640,6 @@ InsaneRebel2::~InsaneRebel2() {
 	delete _hudOverlayNut;
 	delete _hudOverlay2Nut;
 
-	// Clean up laser texture buffer (DAT_0047fee4)
 	freeLaserTexture();
 
 	// Clean up embedded HUD overlays
@@ -819,7 +794,6 @@ bool InsaneRebel2::handleMenuRawJoystickAxisEvent(const Common::Event &event) {
 
 // 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.
 bool InsaneRebel2::notifyEvent(const Common::Event &event) {
 	SmushPlayer *splayer = ((ScummEngine_v7 *)_vm)->_splayer;
 
@@ -833,7 +807,6 @@ bool InsaneRebel2::notifyEvent(const Common::Event &event) {
 	if (_rebelYodaMode && event.type == Common::EVENT_KEYDOWN && !event.kbdRepeat && event.kbd.hasFlags(Common::KBD_ALT)) {
 		switch (event.kbd.keycode) {
 		case Common::KEYCODE_m:
-			// DAT_0047ab60: Yoda-mode Movie Mode skips playable
 			// sections and keeps the story/cutscene sequence moving.
 			_rebelMovieMode = !_rebelMovieMode;
 			debugC(DEBUG_INSANE, "Movie mode %s", _rebelMovieMode ? "enabled" : "disabled");
@@ -842,7 +815,6 @@ bool InsaneRebel2::notifyEvent(const Common::Event &event) {
 			return true;
 
 		case Common::KEYCODE_p:
-			// DAT_0047ab64: Yoda-mode Auto Play makes gameplay
 			// computer controlled.
 			_rebelAutoPlay = !_rebelAutoPlay;
 			debugC(DEBUG_INSANE, "Auto play %s", _rebelAutoPlay ? "enabled" : "disabled");
@@ -1142,7 +1114,6 @@ bool InsaneRebel2::notifyEvent(const Common::Event &event) {
 	if (event.type == Common::EVENT_KEYDOWN) {
 		// Gamepad buttons mapped to ESC can lose their key-up while the GMM is open,
 		// leaving the key repeat source to synthesize another ESC immediately after
-		// returning to gameplay. Treat repeats as part of the original press.
 		if (event.kbd.keycode == Common::KEYCODE_ESCAPE && event.kbdRepeat) {
 			debugC(DEBUG_INSANE, "Ignoring repeated ESC keydown");
 			return true;
@@ -1156,11 +1127,8 @@ bool InsaneRebel2::notifyEvent(const Common::Event &event) {
 			return true;
 		}
 
-		// When paused during gameplay, ANY key unpauses (FUN_405A21 line 360-365).
-		// ESC additionally opens the global menu (original: quit key exits level).
 		if (splayer && splayer->_paused && _gameState == kStateGameplay) {
 			debugC(DEBUG_INSANE, "Key pressed while paused - unpausing");
-			// Restore the original palette saved by showPauseOverlay
 			if (_pauseOverlayActive) {
 				_vm->_system->getPaletteManager()->setPalette(_savedPausePalette, 0, 256);
 				_pauseOverlayActive = false;
@@ -1194,7 +1162,6 @@ bool InsaneRebel2::notifyEvent(const Common::Event &event) {
 			break;
 
 		case Common::KEYCODE_SPACE:
-			// SPACE pauses during gameplay (FUN_405A21).
 			// Unpausing is handled above (any key while paused).
 			if (splayer && _gameState == kStateGameplay && !splayer->_paused) {
 				debugC(DEBUG_INSANE, "SPACE pressed - pausing");
@@ -1268,8 +1235,6 @@ bool InsaneRebel2::notifyEvent(const Common::Event &event) {
 	return false;
 }
 
-// Per-level difficulty parameters extracted from RA2WIN95.EXE at VA 0x47e0f0
-// Original: 2D table indexed by DAT_0047a7fa (difficulty) * 0x242 + DAT_0047a7f8 (levelType) * 0x22
 // 17 fields per entry (34 bytes), 17 entries per difficulty, 6 difficulties
 // Field names from official Difficulty Editor: {laserDelay, snapDistance, missDamage, dodgeDamage,
 //   shotDamage, specialDamage, shotAccuracy, hitPoints, dodgePoints, timePoints,
@@ -1356,7 +1321,6 @@ const InsaneRebel2::LevelDifficultyParams InsaneRebel2::kDifficultyTable[6][17]
 		{   5,    4,   -1,   -1,   10,   -1,   79,  100,   -1,    8, 2000,   -1,    4,   -1,   -1,   -1,   -1}, // Lv15A
 		{   5,    4,  255,   45,    8,    5,   80,  100,  200,    8, 2000, 1000,    4,   -1,   -1,   -1,   -1}, // Lv15B
 	},
-	// Difficulty 4 (Custom1) ??? identical to difficulty 2 (Standard) in original data
 	{
 		{   7,    0,   35,   -1,    5,   -1,   75,   75,   -1,    6, 1500,  750,    0,    7,    7,    8,   -1}, // Lv1
 		{   4,    1,   -1,   -1,    6,   -1,   40,   75,   -1,    0, 1500,  750,    0,  110,  110,  150,   35}, // Lv2
@@ -1403,9 +1367,7 @@ InsaneRebel2::LevelDifficultyParams InsaneRebel2::getDifficultyParams() const {
 	int diff = CLIP(_difficulty, 0, 5);
 	int lvIdx = 0;
 
-	// DAT_0047a7f8 is the per-segment difficulty table index.
 	// This is NOT the same as handler 0x26's gun "levelType" from opcode 6.
-	//
 	// Index mapping reconstructed from level handlers:
 	//   Lv1->0, Lv2->1, Lv3->2, Lv4->3, Lv5->4,
 	//   Lv6A->5, Lv6B->6, Lv7->7 ... Lv14->14, Lv15A->15, Lv15B->16.
@@ -1439,10 +1401,7 @@ bool InsaneRebel2::applyPlayerDamage(int damage) {
 	return true;
 }
 
-// addScore -- Score system with bonus life awards (FUN_0041bf8d).
 void InsaneRebel2::addScore(int points) {
-	// Calculate bonus life threshold based on difficulty (DAT_0047a7fa)
-	// From FUN_0041bf8d:
 	//   if (difficulty < 4) threshold = (difficulty * 5 + 5) * 1000
 	//   else threshold = 15000
 	int threshold;
@@ -1460,7 +1419,6 @@ void InsaneRebel2::addScore(int points) {
 		if (oldMilestone < newMilestone) {
 			// Award bonus life
 			_playerLives++;
-			// TODO: Play bonus life sound (FUN_0041189e(5, 0, 0x7f, 0, 0))
 			debugC(DEBUG_INSANE, "BONUS LIFE! Score crossed %d threshold. Lives=%d", threshold, _playerLives);
 		}
 	}
@@ -1470,9 +1428,6 @@ void InsaneRebel2::addScore(int points) {
 	debugC(DEBUG_INSANE, "Score +%d = %d", points, _playerScore);
 }
 
-// Render score text to HUD (part of FUN_0041c012)
-// FUN_0041c012 lines 133-137: calls FUN_00434cb0 with format "%07ld"
-// using DAT_00482200 (DISPFONT in low-res mode).
 void InsaneRebel2::renderScoreHUD(byte *renderBitmap, int pitch, int width, int height, int statusBarY) {
 	// In low-res mode, score text shares the same NUT as the status bar sprites.
 	NutRenderer *statusFont = _smush_cockpitNut;
@@ -1482,7 +1437,6 @@ void InsaneRebel2::renderScoreHUD(byte *renderBitmap, int pitch, int width, int
 	char scoreStr[16];
 	Common::sprintf_s(scoreStr, "%07d", _playerScore);
 
-	// Score position from FUN_0041c012 assembly:
 	//   X = 0x101 low-res, 0x202 high-res
 	//   Y = 4 low-res, 8 high-res (within status bar)
 	const int statusScale = isHiRes() ? 2 : 1;
@@ -1490,12 +1444,8 @@ void InsaneRebel2::renderScoreHUD(byte *renderBitmap, int pitch, int width, int
 	int scoreY = statusBarY + 4 * statusScale + _viewY;
 
 	// Render each digit as a NUT sprite (direct pixel blit with color 0 transparency).
-	// This matches the original's FUN_00434cb0 ??? FUN_004341a0 text rendering which
 	// uses the NUT font's embedded palette colors (1=white, 3=gray, 4=black outline).
 	// Render each character applying xoffs/yoffs from NUT frame headers,
-	// matching FUN_0042cba0 lines 13-14:
-	//   param_3 = *(short *)(param_6 + 2) + param_3;  // X += xoffs
-	//   param_4 = *(short *)(param_6 + 4) + param_4;  // Y += yoffs
 	int x = scoreX;
 	for (int i = 0; scoreStr[i] != '\0'; i++) {
 		byte ch = (byte)scoreStr[i];
@@ -1508,16 +1458,12 @@ void InsaneRebel2::renderScoreHUD(byte *renderBitmap, int pitch, int width, int
 	}
 }
 
-// ---------------------------------------------------------------------------
 // Pilot Data System
-// ---------------------------------------------------------------------------
 // Save/load pilot profiles using the save file system.
-// Original: FUN_00411980 (load) / FUN_00411A5D (save).
 
 const uint32 kPilotSaveMagic = MKTAG('R', 'A', '2', 'P');
 const uint16 kPilotSaveVersion = 2;
 
-// loadPilots -- Load all pilot profiles from save files (FUN_00411980).
 bool InsaneRebel2::loadPilots() {
 	_numPilots = 0;
 
@@ -1558,7 +1504,6 @@ bool InsaneRebel2::loadPilots() {
 	return _numPilots > 0;
 }
 
-// savePilots -- Save all pilot profiles to save files (FUN_00411A5D).
 bool InsaneRebel2::savePilots() {
 	bool ok = true;
 
@@ -1601,7 +1546,6 @@ bool InsaneRebel2::savePilots() {
 }
 
 int InsaneRebel2::createNewPilot() {
-	// FUN_00411B9A: Create new pilot slot
 	if (_numPilots >= kMaxPilots)
 		return -1;
 
@@ -1612,7 +1556,6 @@ int InsaneRebel2::createNewPilot() {
 }
 
 void InsaneRebel2::deletePilot(int index) {
-	// FUN_00411D29: Delete pilot and shift remaining down
 	if (index < 0 || index >= _numPilots)
 		return;
 
@@ -1691,10 +1634,8 @@ int32 InsaneRebel2::processMouse() {
 	// Store current state for next frame's edge detection
 	_prevMouseButtons = currentButtons;
 
-	// Update _rebelControlMode (DAT_0047a7e4) for Handler 25 covered/uncovered toggle:
 	// Use "sticky" flags - set on button press, cleared by IACT handler after consumption.
 	// This ensures button presses aren't missed due to timing.
-	//
 	// For Handler 25: use edge detection with sticky flags
 	if (_rebelHandler == 25) {
 		// Only SET flags on button press (edge), don't clear them here
@@ -1730,7 +1671,6 @@ int32 InsaneRebel2::processMouse() {
 
 	bool triggerShot = ((_rebelHandler == 25) ? (leftPressed && !leftWasPressed) : leftPressed) || autoFire;
 	if (_rebelHandler == 8) {
-		// FUN_00401CCF uses the same per-frame fire bit both to spawn shots and
 		// to choose the POV gun sprite. Keep the sprite driven by the event-manager
 		// input that processMouse() uses during SMUSH playback.
 		_shipFiring = triggerShot && canShoot;
@@ -1779,7 +1719,6 @@ int32 InsaneRebel2::processMouse() {
 					it->id, it->type, mousePos.x, mousePos.y,
 					it->rect.left, it->rect.top, it->rect.right, it->rect.bottom);
 
-				// Explosion scale is handler-specific in the original:
 				// - H8/H7/H26 use object half-width
 				// - H25 uses half-width + snapDistance (and type 100 doubles it)
 				int explosionHalfWidth = it->rect.width() / 2;
@@ -1790,15 +1729,8 @@ int32 InsaneRebel2::processMouse() {
 						explosionHalfWidth *= 2;
 				}
 
-				// Spawn visual explosion based on handler, enemy type, and flags.
-				//
-				// Rendering functions (FUN_409FBC, FUN_402696, FUN_40F1C5,
-				// FUN_41F29A) check DAT_0047e108 flags & 1 - when set,
 				// explosion NUT sprites are suppressed. This is checked
 				// during rendering in renderExplosions().
-				//
-				// Handler 8 (FUN_4028C5): Only type 0 spawns explosion.
-				// Handler 25 (FUN_41E7C2): Types > 3 set counter but
 				// rendering suppressed by flags bit 0.
 				// Handlers 0x26, 7: All types get visual explosions.
 				if (_rebelHandler != 8 && _rebelHandler != 25) {
@@ -1843,19 +1775,15 @@ int32 InsaneRebel2::processMouse() {
 					_rebelGaugeSlot[it->id] = -1; // consume: avoid double-counting
 				}
 
-				// Set enemy type bit in wave state (FUN_004028c5 line 74)
-				// DAT_0047ab98 |= 1 << (enemyType & 0x1f)
 				// This tracks which enemy GROUPS have been killed in this wave
 				if (it->type > 0 && it->type < 32) {
 					_rebelWaveState |= (1 << it->type);
 					debugC(DEBUG_INSANE, "Wave state updated: 0x%x (set bit %d)", _rebelWaveState, it->type);
 				}
 
-				// Increment kill counter (DAT_0047ab88)
 				_rebelKillCounter++;
 
 				// Handle dependencies.
-				// Handler 25 (FUN_41E7C2) has two kill paths:
 				// - type <= 3: process dependency tables
 				// - type > 3 (and 100): skip dependency handling
 				// Other handlers always use link-table side effects.
@@ -1880,24 +1808,19 @@ int32 InsaneRebel2::processMouse() {
 				}
 
 				// Play enemy death sound.
-				// Pan based on enemy center X position: (screenX - 160) mapped to [-127,127]
 				{
 					int cameraX = (_rebelHandler == 8) ? _shipPosX : _viewX;
 					int enemyCenterX = (it->rect.left + it->rect.right) / 2 - cameraX;
 					int sfxPan = CLIP((enemyCenterX - 160) * 127 / 160, -127, 127);
 					if (_rebelHandler == 8 && it->type >= 1 && it->type <= 4) {
 						// Handler 8 soldier types 1-4: play from auxiliary buffer 0
-						// Original: FUN_00411931(0, slot+3, 0x7f, pan, 0)
 						playAuxSfx(0, 127, sfxPan);
 					} else {
 						// All other enemies: EXPLODE.SAD
-						// Original: FUN_0041189e(2, slot+3, 0x7f, pan, 0)
 						playSfx(2, 127, sfxPan);
 					}
 				}
 
-				// Award score for destroying enemy (FUN_0041bf8d called from FUN_40A2E0)
-				// Score value comes from DAT_0047e0fe indexed by difficulty*level
 				{
 					LevelDifficultyParams dparams = getDifficultyParams();
 					if (dparams.hitPoints > 0) {
@@ -1963,7 +1886,6 @@ Common::Point InsaneRebel2::getGameplayAimPoint() {
 	x = CLIP<int>(x, 0, 319);
 	y = CLIP<int>(y, 0, 199);
 	if (_optControlsFlipped) {
-		// Original DAT_0047a7fe reverses only the up/down gameplay axis.
 		y = CLIP<int>(200 - y, 0, 199);
 	}
 	return Common::Point(x, y);
@@ -2088,7 +2010,6 @@ void InsaneRebel2::updateGameplayAimFromGamepad() {
 }
 
 bool InsaneRebel2::isBitSet(int n) {
-	// FUN_00423970: When param_1 < 1 (0 or negative), the bounds check fails and returns false.
 	// This means ID 0 or negative IDs are always treated as "enabled" (not skipped).
 	if (n < 1) {
 		return false;
@@ -2099,7 +2020,6 @@ bool InsaneRebel2::isBitSet(int n) {
 }
 
 void InsaneRebel2::setBit(int n) {
-	// FUN_004239b0: When n < 1 (i.e., n == 0 or negative), set ALL bits to 1 (disable all objects)
 	// This is used to disable all enemies/objects at once
 	if (n < 1) {
 		for (int i = 0; i < 0x401; i++)
@@ -2111,8 +2031,6 @@ void InsaneRebel2::setBit(int n) {
 }
 
 void InsaneRebel2::clearBit(int n) {
-	// FUN_00423a00: When n < 1 (i.e., n == 0 or negative), clear ALL bits to 0 (enable all objects)
-	// This is called by FUN_00423880 at the start of video playback to reset the bit table,
 	// ensuring all enemies are visible when a new level/segment starts.
 	if (n < 1) {
 		for (int i = 0; i < 0x401; i++)
@@ -2127,21 +2045,17 @@ void InsaneRebel2::clearBit(int n) {
 // Handler 7: only mode 2. Handler 8: not mode 4/5. Handler 25: not damaged.
 bool InsaneRebel2::isShootingAllowed() {
 	// Handler 7 (Third-Person Ship): Only mode 2 allows shooting
-	// FUN_0040d836 line 141: if (DAT_004437c0 == 2) { /* spawn shots, draw crosshair */ }
 	if (_rebelHandler == 7) {
 		return (_flyControlMode == 2);
 	}
 
 	// Handler 8 (Third-Person On Foot): Modes 4/5 disable shooting
-	// FUN_00401CCF line 82-84: if (DAT_0043e000 == 4) { param_5 = 0; }
 	// Mode 5: Ship not rendered (cutscene)
 	if (_rebelHandler == 8) {
 		return (_shipLevelMode != 4 && _shipLevelMode != 5);
 	}
 
 	// Handler 25 (0x19): Only allow shooting when fully uncovered
-	// FUN_41DB5E lines 170-171: if (((param_5 & 1) != 0) && (DAT_0045790a == 0))
-	// _rebelDamageLevel = DAT_0045790a (cover transition counter, 0 = uncovered, 5 = covered)
 	if (_rebelHandler == 25) {
 		return (_rebelDamageLevel == 0);
 	}
@@ -2150,7 +2064,6 @@ bool InsaneRebel2::isShootingAllowed() {
 	return (_rebelHandler != 0);
 }
 
-// procSKIP -- Conditional FOBJ/PSAD skip via bit table (FUN_00423A50).
 // Same mechanism as Full Throttle, but RA2 uses it for enemy objects:
 // when setBit(enemy_id) is called on destruction, SKIP chunks containing
 // that ID cause the next FOBJ (enemy sprite) to be skipped.
diff --git a/engines/scumm/insane/rebel2/rebel.h b/engines/scumm/insane/rebel2/rebel.h
index a9f2f80ffb9..41b1749aa5b 100644
--- a/engines/scumm/insane/rebel2/rebel.h
+++ b/engines/scumm/insane/rebel2/rebel.h
@@ -52,54 +52,44 @@ public:
 	InsaneRebel2(ScummEngine_v7 *scumm);
 	~InsaneRebel2();
 
-	// EventObserver interface - captures events before ScummEngine consumes them
 	bool notifyEvent(const Common::Event &event) override;
 
-	// Menu input event queue - events are captured by notifyEvent() and processed by processMenuInput()
 	Common::Queue<Common::Event> _menuEventQueue;
-	bool _menuInputActive;  // True when we're capturing menu input events
+	bool _menuInputActive;
 	bool _virtualKeyboardActive;
 
-	// ---------------------------------------------------------------------------
-	// Menu System
-	// ---------------------------------------------------------------------------
-	// Main game states (emulates original state machine from FUN_004142BD)
+	// Menu state.
 	enum GameState {
-		kStateIntro = 0,        // Stage 0: Intro/Credits sequence
-		kStateMainMenu = 1,     // Stage 1: Main menu (FUN_004147B2)
-		kStatePilotSelect = 2,  // Stage 2: Pilot selection (FUN_00414A41)
-		kStateChapterSelect = 3,// Stage 3: Chapter selection (FUN_00415CF8)
-		kStateGameplay = 4,     // Stage 4: Gameplay (FUN_00416787)
-		kStateCredits = 5,      // Credits sequence
-		kStateQuit = 6,         // Exit game
-		kStateTopPilots = 8,    // Top Pilots score display (FUN_00420116)
-		kStateDifficultySelect = 7, // Difficulty submenu within pilot select
-		kStateOptions = 9       // Options menu (FUN_004167A6)
+		kStateIntro = 0,
+		kStateMainMenu = 1,
+		kStatePilotSelect = 2,
+		kStateChapterSelect = 3,
+		kStateGameplay = 4,
+		kStateCredits = 5,
+		kStateQuit = 6,
+		kStateTopPilots = 8,
+		kStateDifficultySelect = 7,
+		kStateOptions = 9
 	};
 
-	// Menu selection results (return values from FUN_004147B2)
 	enum MenuResult {
-		kMenuQuit = -1,       // case 6: Return to Launcher
-		kMenuResumeDemo = 0,  // case 3 / inactivity: Continue Intro
-		kMenuCredits = 1,     // case 5: Show credits
-		kMenuNewGame = 2      // case 0: Start Game
+		kMenuQuit = -1,
+		kMenuResumeDemo = 0,
+		kMenuCredits = 1,
+		kMenuNewGame = 2
 	};
 
-	GameState _gameState;           // Current game state
-	int _menuSelection;             // Current menu item (0-6), mirrors DAT_00459988
-	int _menuItemCount;             // Number of menu items (7 for main menu)
-	int _menuInactivityTimer;       // Timeout counter (300 frames = ~10 sec)
-	bool _menuInactivityTimedOut;   // Main menu should return to the intro/demo loop
-	int _lastMenuVariant;           // Last random menu video shown (DAT_00482400)
-	int _menuRepeatDelay;           // Delay for key repeat (DAT_00459ce0)
-	bool _menuSelectionConfirmed;   // True only when user explicitly confirmed a selection
-	bool _levelUnlocked[16];        // Which levels are available (progress flags)
-
-	// Run the main menu loop - returns when game should start or quit
-	// This is the main entry point called from ScummEngine::go()
-	int runMainMenu();
+	GameState _gameState;
+	int _menuSelection;
+	int _menuItemCount;
+	int _menuInactivityTimer;
+	bool _menuInactivityTimedOut;
+	int _lastMenuVariant;
+	int _menuRepeatDelay;
+	bool _menuSelectionConfirmed;
+	bool _levelUnlocked[16];
 
-	// Process menu input (keyboard/mouse) - returns selected item or -1
+	int runMainMenu();
 	int processMenuInput();
 
 	// Format-code-aware string rendering (^fNN=font, ^cNNN=color)
@@ -112,115 +102,67 @@ public:
 	void drawMenuStringCentered(byte *renderBitmap, const char *str, int cx, int y, int defaultColor = 1);
 	void drawMenuStringRight(byte *renderBitmap, const char *str, int rx, int y, int defaultColor = 1);
 
-	// Shared menu item renderer - emulates FUN_0041F5AE
 	void drawMenuItems(byte *renderBitmap, int pitch, int width, int height,
 	                   const char **items, int numItems, int selection,
 	                   bool leftAligned = false);
-
-	// Draw menu overlay (selection highlight) on current frame
 	void drawMenuOverlay(byte *renderBitmap, int pitch, int width, int height);
-
-	// Get random menu video filename (emulates FUN_0041FDC8)
 	Common::String getRandomMenuVideo();
-
-	// Reset menu state for fresh start
 	void resetMenu();
 	bool isMenuTextInputActive() const;
 	void setVirtualKeyboardVisible(bool visible);
 	void updateMenuVirtualKeyboard();
 
-	// ---------------------------------------------------------------------------
-	// Chapter Selection Screen (FUN_00415CF8)
-	// ---------------------------------------------------------------------------
-	// Actual level/chapter selection screen with preview thumbnail.
-	// Distinct from pilot selection (FUN_00414A41).
-
+	// Chapter selection.
 	enum ChapterSelectResult {
-		kChapterSelectBack = 2,   // Return to main menu (ESC or BACK selected)
-		kChapterSelectPlay = 5,   // Play selected chapter
-		kChapterSelectQuit = 0    // Quit game
+		kChapterSelectBack = 2,
+		kChapterSelectPlay = 5,
+		kChapterSelectQuit = 0
 	};
 
-	int _chapterSelection;        // Current chapter selection (0-15, or 16 for BACK)
-	int _chapterItemCount;        // Number of chapter items (17: 16 chapters + BACK)
-	int _selectedChapter;         // Final selected chapter ID (0-15)
-	Common::String _passwordInput; // Current password input string (max 8 chars)
-	bool _chapterUnlocked[16];    // Which chapters are unlocked
-	bool _debugUnlockAll;         // Debug flag to unlock all chapters for testing
-	bool _noDamage;               // Game option: disable player damage
+	int _chapterSelection;
+	int _chapterItemCount;
+	int _selectedChapter;
+	Common::String _passwordInput;
+	bool _chapterUnlocked[16];
+	bool _debugUnlockAll;
+	bool _noDamage;
 
-	// Unlock all chapters for testing (debug mode)
-	// Call this to enable access to all chapters without passwords
 	void unlockAllChapters();
-
-	// Run chapter selection screen - emulates FUN_00415CF8
 	int runChapterSelect();
-
-	// Draw chapter selection overlay - called during O_LEVEL.SAN playback
 	void drawChapterSelectOverlay(byte *renderBitmap, int pitch, int width, int height);
-
-	// Process chapter select input - returns -1 (no action) or action code
 	int processChapterSelectInput();
-
-	// Draw the preview thumbnail box - emulates FUN_004292D0 calls in FUN_00415CF8
 	void drawPreviewBox(byte *renderBitmap, int pitch, int width, int height);
 
-	// View offset for chapter preview scrolling (DAT_0047abe2/DAT_0047abe4)
-	int16 _previewOffsetX;   // X offset = -90 for chapter select
-	int16 _previewOffsetY;   // Y offset = chapter * -50 + 75
+	int16 _previewOffsetX;
+	int16 _previewOffsetY;
 
-	// Draw score/info display at bottom of chapter select - emulates FUN_00434cb0 calls
 	void drawChapterInfoLine(byte *renderBitmap, int pitch, int width, int height);
-
-	// Rating-to-medal string conversion (FUN_0042001f)
 	Common::String getRankString(int rating);
-
-	// Password table lookup (FUN_0041BCE0)
 	Common::String getChapterPassword(int level, int difficulty);
 
-	// ---------------------------------------------------------------------------
-	// Top Pilots Screen (FUN_00420116)
-	// ---------------------------------------------------------------------------
-	// Ranked pilot scores with animated reveal, played over menu video.
-	// Original: DAT_00443b58 ranking table, 0x4a-byte records, max 15 entries.
-
+	// Top pilots.
 	static const int kMaxRankings = 15;
 
 	struct RankingEntry {
-		char name[40];       // +0x04: Pilot name (or "-----" for defaults)
-		int32 score;         // +0x36: Total score
-		int32 rating;        // +0x3a: Total rating (converted to rank medals)
-		int16 difficulty;    // +0x3e: Difficulty tier (0-5), TRS index = difficulty + 155
-		int16 chapter;       // +0x40: Highest chapter completed (1-15)
+		char name[40];
+		int32 score;
+		int32 rating;
+		int16 difficulty;
+		int16 chapter;
 	};
 
 	RankingEntry _rankings[kMaxRankings];
 	int _numRankings;
 
-	// Initialize ranking table with defaults (FUN_0040FF00)
 	void initDefaultRankings();
-
-	// Insert pilot score into sorted ranking table (FUN_00410271)
 	void insertRanking(const char *name, int32 score, int32 rating, int16 difficulty, int16 chapter);
-
-	// Run top pilots display - called from main menu "Show Top Pilots"
 	void showTopPilots();
-
-	// Draw top pilots overlay on current frame during video playback
 	void drawTopPilotsOverlay(byte *renderBitmap, int pitch, int width, int height);
 
-	int _topPilotsFrameCount;     // Animation frame counter (pilots revealed one per frame)
-	int _topPilotsMaxFrames;      // Total frames to display (120 or 240)
-
-	// ---------------------------------------------------------------------------
-	// Options Menu (FUN_004167A6)
-	// ---------------------------------------------------------------------------
-	// TRS strings: 89 (title), 90-101 (toggle labels), 103 (volume), 107/109 (back)
-	// Original settings array at DAT_00482e20[0..4]:
-	//   [0]=text, [1]=music, [2]=voices, [3]=sound, [4]=hidden abort flag
-	// Additional flags: DAT_0047a7fe (controls normal/flipped), DAT_0047a80a (rapid fire)
-	// Volume: DAT_0047a804 (0-127), brightness/gamma: DAT_0047a802 (127-768)
+	int _topPilotsFrameCount;
+	int _topPilotsMaxFrames;
 
+	// Options menu.
 	void showOptionsMenu();
 	void drawOptionsOverlay(byte *renderBitmap, int pitch, int width, int height);
 	int processOptionsInput();
@@ -234,30 +176,25 @@ public:
 	bool _optTextEnabled;
 	bool _optControlsFlipped;
 	bool _optRapidFire;
-	int _optVolumeLevel;       // 0-127 (DAT_0047a804)
-
-	// ---------------------------------------------------------------------------
-	// Pilot Data System (FUN_00411B9A / FUN_00411980 / FUN_00411A5D)
-	// ---------------------------------------------------------------------------
-	// 10 pilot slots ?? 0x118 (280) bytes at DAT_004568A8.
-	// Stored via SaveFileManager in a custom save file.
+	int _optVolumeLevel;
 
+	// Pilot profiles.
 	static const int kMaxPilots = 10;
 	static const int kMaxPilotNameLen = 15;
 	static const int kNumLevels = 16;
 
 	struct PilotData {
-		char name[kMaxPilotNameLen + 1]; // +0x04: Pilot name (15 chars + null)
-		int32 score[kNumLevels];         // +0x2C: Per-level score (0 = default, 0xFF = unplayed)
-		int32 lives[kNumLevels];         // +0x6C: Per-level lives (4 = default, 0xFF = unplayed)
-		int32 damage[kNumLevels];        // +0xAC: Per-level damage (0xFF = unplayed)
-		int16 rating[kNumLevels];        // +0xEC: Per-level difficulty rating (0-50)
-		int16 difficulty;                // +0x10C: Difficulty setting (0-5)
+		char name[kMaxPilotNameLen + 1];
+		int32 score[kNumLevels];
+		int32 lives[kNumLevels];
+		int32 damage[kNumLevels];
+		int16 rating[kNumLevels];
+		int16 difficulty;
 
 		void init() {
 			memset(name, 0, sizeof(name));
 			memset(rating, 0, sizeof(rating));
-			difficulty = 2; // Default to 3rd option
+			difficulty = 2;
 			score[0] = 0;
 			lives[0] = 4;
 			damage[0] = 0;
@@ -269,141 +206,85 @@ public:
 		}
 	};
 
-	PilotData _pilots[kMaxPilots];       // DAT_004568A8 pilot array
-	int _numPilots;                      // DAT_00480318 number of valid pilots
-	int _activePilot;                    // DAT_0047a7ea selected pilot index
+	PilotData _pilots[kMaxPilots];
+	int _numPilots;
+	int _activePilot;
 
-	// Pilot save/load via SaveFileManager
 	bool loadPilots();
 	bool savePilots();
 
-	// Pilot management (FUN_00411B9A, FUN_00411D29)
-	int createNewPilot();                // Returns index of new pilot, or -1 if full
-	void deletePilot(int index);         // FUN_00411D29: shift remaining pilots down
-	void copyPilot(int srcIndex);        // Copy pilot to new slot
+	int createNewPilot();
+	void deletePilot(int index);
+	void copyPilot(int srcIndex);
 
-	// Update pilot progress after level completion
 	void updatePilotProgress(int levelIndex, int32 score, int32 lives, int32 damage);
 
-	// ---------------------------------------------------------------------------
-	// Pilot Selection Menu (FUN_00414A41)
-	// ---------------------------------------------------------------------------
-	// Pilot/save selection menu (separate from chapter selection).
-
 	enum LevelSelectResult {
-		kLevelSelectBack = 0,     // Return to main menu
-		kLevelSelectPlay = 1,     // Play selected level
-		kLevelSelectQuit = 2      // Quit game
+		kLevelSelectBack = 0,
+		kLevelSelectPlay = 1,
+		kLevelSelectQuit = 2
 	};
 
-	// Pilot name input state (for NEW PILOT name entry)
 	enum PilotMenuMode {
-		kPilotModeSelect = 0,     // Normal pilot list selection
-		kPilotModeNameInput = 1,  // Typing a new pilot name
-		kPilotModeDifficulty = 2, // Difficulty submenu
-		kPilotModeCopySelect = 3, // Selecting source pilot to copy
-		kPilotModeDeleteSelect = 4 // Selecting pilot to delete
+		kPilotModeSelect = 0,
+		kPilotModeNameInput = 1,
+		kPilotModeDifficulty = 2,
+		kPilotModeCopySelect = 3,
+		kPilotModeDeleteSelect = 4
 	};
 	PilotMenuMode _pilotMenuMode;
-	Common::String _pilotNameInput;      // Current name being typed
-	int _pilotEditIndex;                 // Index of pilot being edited/created
+	Common::String _pilotNameInput;
+	int _pilotEditIndex;
 
-	int _levelSelection;          // Current level selection (0-based)
-	int _levelItemCount;          // Number of level items (levels + options)
-	int _selectedLevel;           // Final selected level ID (1-15)
-	int _difficultySelection;     // Current difficulty selection in submenu (0-based)
+	int _levelSelection;
+	int _levelItemCount;
+	int _selectedLevel;
+	int _difficultySelection;
 
-	// Run pilot selection menu - emulates FUN_00414A41
 	int runLevelSelect();
-
-	// Draw pilot selection overlay
 	void drawLevelSelectOverlay(byte *renderBitmap, int pitch, int width, int height);
-
-	// Process pilot select input - returns -1 (no action) or action code
 	int processLevelSelectInput();
 
-	// ---------------------------------------------------------------------------
-	// Level Loading System
-	// ---------------------------------------------------------------------------
-	// Emulates the level handler functions FUN_00417E53 through FUN_0041BBE8.
-	// Each level has: BEG (intro), gameplay SANs, END (completion), DIE variants,
-	// RETRY, and OVER (game over) videos.
-
-	// Level playback result (returned by runLevel)
+	// Level playback.
 	enum LevelResult {
-		kLevelCompleted = 0,      // Level completed successfully
-		kLevelNextLevel = 1,      // Proceed to next level
-		kLevelGameOver = 2,       // No lives remaining
-		kLevelQuit = 3,           // Player quit
-		kLevelReturnToMenu = 4    // Return to main menu
+		kLevelCompleted = 0,
+		kLevelNextLevel = 1,
+		kLevelGameOver = 2,
+		kLevelQuit = 3,
+		kLevelReturnToMenu = 4
 	};
 
-	// Main game entry point ??? full game loop (intro, menu, pilot, chapter, levels)
-	// Emulates the original game flow from FUN_004142BD
 	void runGame();
-
-	// Play the intro sequence (CREDITS/O_OPEN_C, O_OPEN_D, OPEN/O_OPEN_A, O_OPEN_B)
-	// Emulates case 0 in FUN_004142BD
 	void playIntroSequence();
-
-	// Play the mission briefing video (OPEN/O_LEVEL.SAN)
-	// Emulates FUN_00415CF8
 	void playMissionBriefing();
-
-	// Main level runner - plays a complete level by ID (1-15)
-	// Handles all videos: BEG, gameplay, END/DIE/RETRY/OVER
-	// Returns LevelResult
 	int runLevel(int levelId);
-
-	// Play level beginning cinematic (LEVXX/XXBEG.SAN)
 	void playLevelBegin(int levelId);
-
-	// Play level completion video (LEVXX/XXEND.SAN)
 	void playLevelEnd(int levelId);
-
-	// Play retry prompt video (LEVXX/XXRETRY.SAN)
 	void playLevelRetry(int levelId);
-
-	// Play game over video (LEVXX/XXOVER.SAN)
 	void playLevelGameOver(int levelId);
-
-	// Play the full ending sequence: finale + credits + epilogue (FUN_0041bbe8)
 	void playEndingSequence();
-
-	// Play main menu credits video (OPEN/O_CREDIT.SAN)
 	void playCreditsSequence();
-
-	// Get the directory name for a level (e.g., "LEV01" for level 1)
 	Common::String getLevelDir(int levelId);
-
-	// Get the file prefix for a level (e.g., "01" for level 1)
 	Common::String getLevelPrefix(int levelId);
 
-	// Per-level handlers (emulate FUN_00417E53 through FUN_0041BBE8)
-	// These implement the complete level logic including retry handling
-	int runLevel1();   // FUN_00417E53 - Single gameplay phase
-	int runLevel2();   // FUN_00418063 - Multiple parts with P1/P2/P3 subdirs
-	int runLevel3();   // FUN_0041885F - Two phases with per-phase retry
-	int runLevel4();   // Cutscene + single gameplay
-	int runLevel5();   // FUN_00418EC6 - Single gameplay phase
-	int runLevel6();   // FUN_004190D6 - Two phases with mid-level video switch
-	int runLevel7();   // FUN_0041974C - TIE Training: single + fork at frame 1592
-	int runLevel8();   // FUN_00419976 - Flight to Imdaar: single phase space battle
-	int runLevel9();   // FUN_00419B86 - Mine Field: single phase with mid-events
-	int runLevel10();  // FUN_00419E0A - Speeder Bikes: single phase
-	int runLevel11();  // FUN_0041A00C - Inside the Terror: 3 phases + bridge (Handler 8)
-	int runLevel12();  // FUN_0041AA14 - Sewers: 4 phases FPS (Handler 25)
-	int runLevel13();  // FUN_0041B3E1 - Escaping Star Destroyer: two-phase A???B
-	int runLevel14();  // FUN_0041B6E8 - TIE Attack: single phase
-	int runLevel15();  // FUN_0041B8D7 - Imdaar Alpha: single + level ID switch
-
-	// Wave state management (FUN_00417b61)
-	// Waits for current video to finish, accumulates kill state, redistributes
-	// kill credits from the budget.
-	// mask: required enemy bits (0x36 for Phase 1, 0x3e for Phases 2/3)
-	// budget: kill credit budget counter (decremented per credit transfer)
-	// threshold: early-exit frame threshold (0=disabled, 0x14=20 for wave loops)
-	// flags: bit 1 = add random unkilled types, bit 0 = limit credits to 2 (else 8)
+	// Per-level handlers.
+	int runLevel1();
+	int runLevel2();
+	int runLevel3();
+	int runLevel4();
+	int runLevel5();
+	int runLevel6();
+	int runLevel7();
+	int runLevel8();
+	int runLevel9();
+	int runLevel10();
+	int runLevel11();
+	int runLevel12();
+	int runLevel13();
+	int runLevel14();
+	int runLevel15();
+
+	// Wave bookkeeping shared by looping combat sections.
 	struct WaveEndResult {
 		WaveEndResult() : creditedBits(0), died(false), quit(false), completed(false), skipped(false) {}
 
@@ -418,18 +299,13 @@ public:
 
 	WaveEndResult processWaveEnd(int16 mask, int16 *budget, int16 threshold, uint16 flags);
 
-	// Play a raw SAN segment from a scripted level handler.
-	// The original reaches these call sites through different wrappers/direct paths; this
-	// only collapses the shared dispatch step. Callers still choose the original
-	// flags and when to call processWaveEnd(). recordFrame preserves the original
-	// split between gameplay/wave calls and transition/init-only segments.
+	// Shared SAN segment dispatcher for scripted level handlers.
 	bool playLevelSegment(const char *filename, uint16 flags, bool recordFrame = true);
 	void enableIOSGamepadController();
 	void restoreIOSGamepadController();
 
 	int calculateAccuracy(int kills, int misses) const;
 
-	// Play DIE/OVER/RETRY tail. Returns true when the caller should restart its retry loop.
 	bool handleLevelDeath(int levelId, int phase, const char *deathVideo, const char *retryVideo, int &levelResult);
 	void resetLevelAttemptState(int initialPhase);
 	void resetLevelPhaseState(bool clearEnemies);
@@ -438,74 +314,46 @@ public:
 	void resetExplosions();
 	void resetHandler7FlightState();
 
-	// Random number helper (emulates FUN_004233a0)
 	int getRandomVariant(int max);
-
-	// Select death video variant based on level, phase, and frame
-	// Returns suffix like "A", "B", "C" for DIE_X.SAN
 	Common::String selectDeathVideoVariant(int levelId, int phase, int frame);
-
-	// Play cinematic video by filename
 	void playCinematic(const char *filename);
 
-	// Play cinematic with text overlay (emulates FUN_004171c5)
 	// Text is progressively revealed during [fadeInFrame, fadeOutFrame)
 	void playVideoWithText(const char *filename, int textID, int textX, int textY,
 	                       int fadeInFrame, int fadeOutFrame);
 
-	// Text overlay state (active during playVideoWithText cinematics)
-	bool _textOverlayActive;      // True when text overlay should render
-	int _textOverlayID;           // TRS string ID
-	int _textOverlayX;            // X position for text rendering
-	int _textOverlayY;            // Y position for text rendering
-	int _textOverlayFadeIn;       // Frame to start progressive text reveal
-	int _textOverlayFadeOut;      // Frame to stop text rendering
+	bool _textOverlayActive;
+	int _textOverlayID;
+	int _textOverlayX;
+	int _textOverlayY;
+	int _textOverlayFadeIn;
+	int _textOverlayFadeOut;
 
-	// Render chapter title text overlay (emulates FUN_004341a0 in FUN_004171c5)
 	void renderTextOverlay(byte *renderBitmap, int pitch, int width, int height, int curFrame);
-
-	// Play death video with proper variant selection
 	void playLevelDeathVariant(int levelId, int phase, int frame);
-
-	// Play retry video (phase-specific for multi-phase levels)
 	void playLevelRetryVariant(int levelId, int phase);
 
-	// Reset gameplay aim to the original centered mouse/joystick baseline.
 	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)
-	int _deathFrame;          // Frame number where player died (for death video selection)
-	bool _skipSectionRequested; // Debug shortcut (Shift+S): force current gameplay section to end
-
-	// ---------------------------------------------------------------------------
-	// Resources and Fonts
-	// ---------------------------------------------------------------------------
+	int _currentPhase;
+	int _deathFrame;
+	bool _skipSectionRequested;
 
+	// Resources and fonts.
 	NutRenderer *_smush_cockpitNut;
-
-	// Font used for opcode 9 text/subtitle rendering (DIHIFONT / TALKFONT)
 	NutRenderer *_rebelMsgFont;
 
-	// Menu system fonts (from info.md - FUN_403BD0 font loading)
-	// Low resolution mode font list (stored in DAT_00485058 linked list):
-	//   Font 0 (^f00): TALKFONT.NUT - Default menu font
-	//   Font 1 (^f01): SMALFONT.NUT - Small font (for format code switching)
-	//   Font 2 (^f02): TITLFONT.NUT - Title font
-	//   Font 3 (^f03): POVFONT.NUT - POV font
-	NutRenderer *_smush_talkfontNut;   // Font 0 - primary menu font (DAT_00485058)
-	NutRenderer *_smush_smalfontNut;   // Font 1 - small font for ^f01 switching
-	NutRenderer *_smush_titlefontNut;  // Font 2 - title font
-	NutRenderer *_smush_povfontNut;    // Font 3 - POV font for Handler 8 overlay
-
-	// Saved palette for pause overlay restoration (FUN_405A21)
+	NutRenderer *_smush_talkfontNut;
+	NutRenderer *_smush_smalfontNut;
+	NutRenderer *_smush_titlefontNut;
+	NutRenderer *_smush_povfontNut;
+
 	byte _savedPausePalette[768];
 	bool _pauseOverlayActive;
 
-	bool _introCursorPushed; // true when we've pushed an invisible cursor for intro
+	bool _introCursorPushed;
 
 
 	int32 processMouse() override;
@@ -516,27 +364,16 @@ public:
 	bool handleMenuRawJoystickAxisEvent(const Common::Event &event);
 	void updateMenuGamepadAxisKey(int16 oldAxisX, int16 oldAxisY);
 	void queueMenuGamepadAxisKey(Common::KeyCode keycode);
-	// Per-frame: pan the gameplay reticle incrementally from the held directional controls
-	// (on-screen/physical gamepad dpad, keyboard arrows) instead of snapping it to a screen
-	// edge. Call once per frame; getGameplayAimPoint() stays a pure getter.
 	void updateGameplayAimFromGamepad();
 	void warpGameplayMouseNow(int x, int y);
 
-	// Analog stick state, ingested from EVENT_CUSTOM_BACKEND_ACTION_AXIS in notifyEvent()
-	// and read (with a deadzone) by updateGameplayAimFromGamepad(). Signed; 0 = centered.
 	int16 _joystickAxisX;
 	int16 _joystickAxisY;
 
-	// True once the gamepad has driven the gameplay reticle, until a genuine mouse/touch
-	// motion takes over. While set, notifyEvent() drops stray pointer events so they can't
-	// recenter the reticle (the cursor is locked ~center during gameplay, so a gamepad
-	// "click" lands at center). See notifyEvent()/updateGameplayAimFromGamepad().
 	bool _gamepadAimActive;
-	// Suppress delayed lock/warp mousemove artifacts after centering Handler 7 gameplay.
 	uint32 _gameplayMouseSettleUntil;
 	uint32 _lastGameplayMenuCloseTime;
 	uint32 _lastMenuGamepadNavigationTime;
-	// Menu-only axis state. Gameplay aiming continues to use _joystickAxisX/Y.
 	int16 _menuGamepadAxisX;
 	int16 _menuGamepadAxisY;
 	int _menuGamepadRawAxis;
@@ -547,7 +384,6 @@ public:
 	bool isBitSet(int n) override;
 	void setBit(int n) override;
 
-	// Get current handler ID (8, 25, 38 etc.) for SMUSH player to query
 	int getHandler() const { return _rebelHandler; }
 	int getHandler25GrdSpriteMode() const { return _grdSpriteMode; }
 	bool isHiRes() const;
@@ -556,7 +392,6 @@ public:
 				  int32 setupsan13, Common::SeekableReadStream &b, int32 size, int32 flags,
 				  int16 par1, int16 par2, int16 par3, int16 par4);
 
-	// Handle IACT opcode subcases
 	void iactRebel2Opcode2(Common::SeekableReadStream &b, int16 par2, int16 par3, int16 par4);
 	void iactRebel2Opcode3(Common::SeekableReadStream &b, int16 par2, int16 par3, int16 par4);
 	void iactRebel2Opcode6(byte *renderBitmap, Common::SeekableReadStream &b, int32 chunkSize, int16 par2, int16 par3, int16 par4);
@@ -585,12 +420,7 @@ public:
 					  int32 setupsan13, Common::SeekableReadStream &b, int32 size, int32 flags,
 					  int16 par1, int16 par2, int16 par3, int16 par4) override;
 
-	// ---------------------------------------------------------------------------
-	// Rendering Helper Functions
-	// ---------------------------------------------------------------------------
-	// Extracted from procPostRendering for better readability.
-
-	// Fill status bar background area (FUN_004288c0 equivalent)
+	// Rendering helpers.
 	void renderStatusBarBackground(byte *renderBitmap, int pitch, int width, int height,
 								   int videoWidth, int videoHeight, int statusBarY);
 
@@ -605,39 +435,23 @@ public:
 	void updateGameplayDamageRecovery(int32 curFrame);
 	void checkGameplayPostRenderCollisions(byte *renderBitmap, int pitch, int width, int height, int32 curFrame);
 
-	// Draw NUT-based HUD overlays for Handler 0x26 turret modes
 	void renderTurretHudOverlays(byte *renderBitmap, int pitch, int width, int height, int32 curFrame);
-
-	// Draw embedded SAN HUD overlays from IACT chunks
 	void renderEmbeddedHudOverlays(byte *renderBitmap, int pitch, int width, int height);
-
-	// Draw DISPFONT.NUT status bar sprites (FUN_0041c012 equivalent)
 	void renderStatusBarSprites(byte *renderBitmap, int pitch, int width, int height,
 								int statusBarY, int32 curFrame);
 
-	// Draw Handler 7 ship sprite (third-person ship - FLY sprites)
 	void renderHandler7Ship(byte *renderBitmap, int pitch, int width, int height);
 	void renderHandler7FlySprite(byte *renderBitmap, int pitch, int width, int height,
 		bool renderHiRes, int renderScale, int nativeViewX, int nativeViewY,
 		int nativeX, int nativeY, NutRenderer *nut, int spriteIndex);
 
-	// Draw Handler 8 ship sprite (third-person on foot - POV sprites)
 	void renderHandler8Ship(byte *renderBitmap, int pitch, int width, int height);
-	void renderVehicleShotImpacts(byte *renderBitmap, int pitch, int width, int height); // FUN_402DA8 (Handler 8)
+	void renderVehicleShotImpacts(byte *renderBitmap, int pitch, int width, int height);
 
-	// Draw fallback ship using embedded HUD frame
 	void renderFallbackShip(byte *renderBitmap, int pitch, int width, int height);
-
-	// Draw per-enemy target indicators from the cockpit icon sheet.
 	void renderEnemyOverlays(byte *renderBitmap, int pitch, int width, int height, int videoWidth);
-
-	// Draw explosion animations from 5-slot system (dispatcher)
 	void renderExplosions(byte *renderBitmap, int pitch, int width, int height);
-
-	// Draw laser shot beams and impacts
 	void renderLaserShots(byte *renderBitmap, int pitch, int width, int height);
-
-	// Update target lock state and draw crosshair/reticle
 	void renderCrosshair(byte *renderBitmap, int pitch, int width, int height);
 	void renderHandler8MonitorEffect(byte *renderBitmap, int pitch, int width, int height);
 	void renderHandler8PovOverlay(byte *renderBitmap, int pitch, int width, int height);
@@ -647,95 +461,48 @@ public:
 	Common::Point getHandler7ShotTargetPoint();
 	Common::Point getHandler8ShotTargetPoint();
 
-	// Reset enemy active flags and collision zones at frame end
 	void frameEndCleanup();
 
-	// ---------------------------------------------------------------------------
-	// Opcode 8 Helper Functions
-	// ---------------------------------------------------------------------------
-	// Resource loading extracted from iactRebel2Opcode8.
-
-	// Load Handler 7 FLY NUT sprites from IACT data
+	// Opcode 8 resource loading.
 	bool loadHandler7FlySprites(Common::SeekableReadStream &b, int64 remaining, int16 par4);
 	bool loadHandler7ShotTable(Common::SeekableReadStream &b, int64 startPos, int64 remaining, int16 par4);
 
-	// Load turret HUD overlay NUT from ANIM data
 	bool loadTurretHudOverlay(byte *animData, int32 size, int16 selector);
-
-	// Load Handler 8 ship POV NUT sprites from ANIM data (par4 = sprite type: 1,3,6,7)
 	bool loadHandler8ShipSprites(byte *animData, int32 size, int16 par4);
-
-	// Load Handler 25 GRD NUT sprites from ANIM data (par4 = sprite type: 1,2)
 	bool loadHandler25GrdSprites(byte *animData, int32 size, int16 par4);
 
-	// Parse Handler 25 shot-origin table text from opcode 8 (par4 = 8).
-	// FUN_0041CADB stores values into DAT_004578a6 / DAT_004578c6 (indices 5..19).
+	// Handler 25 shot-origin table from opcode 8 text.
 	bool loadHandler25ShotOriginTable(Common::SeekableReadStream &b, int64 startPos, int64 remaining);
 
-	// Load Level 2 background from embedded ANIM
 	bool loadLevel2Background(byte *animData, int32 size, byte *renderBitmap);
 
-	// Override procSKIP to disable Full Throttle's conditional frame skip mechanism
-	// RA2 uses a different system for conditional frames via IACT opcodes
 	void procSKIP(int32 subSize, Common::SeekableReadStream &b) override;
-
-	// Override procPreRendering to restore Level 2 background before FOBJ decoding
-	// This is called at the start of each frame, before FOBJ sprites are decoded
 	void procPreRendering(byte *renderBitmap) override;
 
-	// mask231: when true, color 231 is treated as transparent (legacy sprites). For laser beams set false.
+	// Laser beams use color 231 as an opaque texel.
 	void drawTexturedLine(byte *dst, int pitch, int width, int height, int x0, int y0, int x1, int y1, NutRenderer *nut, int spriteIdx, int v, bool mask231 = true);
 
-	// ---------------------------------------------------------------------------
-	// Laser Texture Buffer (DAT_0047fee4)
-	// ---------------------------------------------------------------------------
-	// Pre-rendered laser texture used by FUN_0040BBF6.
-	// Initialized from CPITIMAG.NUT sprite 0 via initLaserTexture() (FUN_0040BAB0)
+	// Laser rendering.
 	struct LaserTexture {
-		byte *pixels;      // Pixel data (rendered from NUT sprite)
-		int16 width;       // Texture width
-		int16 height;      // Texture height (clamped to max 15)
+		byte *pixels;
+		int16 width;
+		int16 height;
 	};
-	LaserTexture _laserTexture;  // DAT_0047fee4
-
-	// ---------------------------------------------------------------------------
-	// Edge Blend Table (DAT_0046a7d0)
-	// ---------------------------------------------------------------------------
-	// 256x256 lookup table used by drawEdgeHighlightLine() (FUN_410962) to compute
-	// glow colors at beam edges. For each pixel on the beam edge, the table maps
-	// [adjacent_pixel_above][adjacent_pixel_below] -> output color, producing a
-	// color-blended highlight that gives beams their distinctive glow.
-	//
-	// Default table (FUN_410510 with NULL): table[a][b] = min(a,b) (identity/transparent).
-	// Per-level tables loaded via IACT opcode 8 par4=1000 (FUN_405663 -> FUN_410510).
-	// The per-level table tunes the glow to the current palette (red for some levels,
-	// green for others).
-	//
-	// _rebelDetailMode (DAT_0047a7fc): Controls whether edge highlights are drawn.
-	// Set from IACT opcode 6. When >= 0, edge highlights are enabled.
-	byte _edgeTable[256 * 256];       // DAT_0046a7d0 - primary edge blend table
-	int16 _rebelDetailMode;           // DAT_0047a7fc - edge highlight enable flag
-
-	// Initialize edge blend table (FUN_410510)
-	// data == nullptr: fill with default identity table (min(a,b))
-	// data != nullptr: load 256x256 symmetric table from data (skips 8-byte header)
+	LaserTexture _laserTexture;
+
+	// Per-level palette lookup for laser edge glow.
+	byte _edgeTable[256 * 256];
+	int16 _rebelDetailMode;
+
 	void initEdgeTable(const byte *data);
 
-	// Draw edge highlight line using the edge blend table (FUN_410962)
-	// Each pixel is blended from its perpendicular neighbors via _edgeTable lookup.
-	// For horizontal-dominant beams, reads pixels above/below the line.
-	// For vertical-dominant beams, reads pixels left/right of the line.
 	void drawEdgeHighlightLine(byte *dst, int pitch, int width, int height,
 	                           int16 x0, int16 y0, int16 x1, int16 y1,
 	                           int16 clipLeft, int16 clipTop, int16 clipRight, int16 clipBottom);
 
-	// Initialize laser texture from NUT sprite (FUN_0040BAB0)
 	void initLaserTexture(NutRenderer *nut, int spriteIdx);
 	void freeLaserTexture();
 
-	// Draw laser beam using pre-initialized texture (FUN_0040BBF6)
-	// Two-layer rendering: textured scanlines (beam body) + edge highlights (glow).
-	// Edge highlights are only drawn when _rebelDetailMode >= 0.
 	void drawLaserBeam(byte *dst, int pitch, int width, int height,
 	                   int16 gunX, int16 gunY, int16 targetX, int16 targetY,
 	                   int16 animFrame, int16 maxFrames,
@@ -745,15 +512,15 @@ public:
 
 	struct enemy {
 		int id;
-		int type;                 // Enemy type/group from IACT opcode 4 par3 (determines DAT_0047ab98 bit)
+		int type;
 		Common::Rect rect;
 		bool active;
-		bool destroyed;           // Set when enemy is shot - prevents re-activation
-		int explosionFrame;       // Current explosion animation frame (0-32, -1 = done)
-		bool explosionComplete;   // True when explosion animation has finished
-		byte *savedBackground;    // Saved background pixels at moment of destruction
-		int savedBgWidth;         // Width of saved background
-		int savedBgHeight;        // Height of saved background
+		bool destroyed;
+		int explosionFrame;
+		bool explosionComplete;
+		byte *savedBackground;
+		int savedBgWidth;
+		int savedBgHeight;
 	};
 
 	void initEnemyStruct(int id, int32 x, int32 y, int32 w, int32 h, bool active, bool destroyed, int32 explosionFrame, int type = 0);
@@ -761,58 +528,38 @@ public:
 
 	Common::List<enemy> _enemies;
 
-	// Current handler type for Rebel Assault 2 (determines crosshair sprite)
-	// Handler 0: Background only
-	// Handler 7: Third-Person Ship - uses crosshair sprite 0x2F (47)
-	// Handler 8: Third-Person On Foot - uses crosshair sprite 0x2E (46)
-	// Handler 0x19: FPS/Mixed view - uses crosshair sprite 0x2F (47)
-	// Handler 0x26: Turret/Cockpit - crosshair varies by level type
+	// Current gameplay handler.
 	int _rebelHandler;
-
-	// Level type from IACT opcode 6 par3 (corresponds to DAT_004436de)
-	// Determines crosshair variant for turret mode:
-	// - levelType == 5: Use sprites 0x30+ (48+) for crosshair
-	// - levelType != 5: Use sprites 0-3 for crosshair (with animation)
 	int _rebelLevelType;
-
-	// Status bar sprite index (5 or 53) triggered by Opcode 6 par4
-	// 0 = disabled
 	int _rebelStatusBarSprite;
 
-	// Embedded SAN HUD overlays (extracted from IACT chunks)
-	// These are decoded frame buffers from embedded ANIM data
-	// Slots 1-4 correspond to userId in the IACT wrapper
+	// Embedded SAN HUD overlays.
 	struct EmbeddedSanFrame {
-		byte *pixels;      // Decoded frame pixels (8-bit indexed)
-		int width;         // Frame width
-		int height;        // Frame height
-		int renderX;       // X position to render (0 = centered based on slot)
-		int renderY;       // Y position to render
-		bool valid;        // True if this slot has valid data
+		byte *pixels;
+		int width;
+		int height;
+		int renderX;
+		int renderY;
+		bool valid;
 	};
 
-	EmbeddedSanFrame _rebelEmbeddedHud[16];  // HUD overlay slots (userId 0-15)
+	EmbeddedSanFrame _rebelEmbeddedHud[16];
 	byte _rebelEmbeddedCodec45Palette[0x300];
 	byte _rebelEmbeddedCodec45Lookup[0x8000];
 
-	// Load and decode an embedded SAN animation from IACT chunk data
-	// userId: HUD slot (1-4), animData: raw ANIM data, size: data size, renderBitmap: current frame buffer
 	void loadEmbeddedSan(int userId, byte *animData, int32 size, byte *renderBitmap) override;
-
-	// Render a decoded embedded frame to the video buffer
-	// Handles transparency (color 0 and 231) and boundary checks
 	void renderEmbeddedFrame(byte *renderBitmap, const EmbeddedSanFrame &frame, int userId);
 	void drawHandler25CorridorOverlay(byte *renderBitmap);
 
-	int16 _rebelLinks[512][3]; // Dependency links: Slot 0 (Disable on death), Slot 1/2 (Enable on death)
+	int16 _rebelLinks[512][3];
 	void clearBit(int n);
-	bool isShootingAllowed();  // FUN_0040d836/FUN_00401CCF: Check control mode before spawning shots
+	bool isShootingAllowed();
 
 	struct Explosion {
 		int x, y;
 		int width, height;
-		int counter;     // Duration counter (starts at 10)
-		int scale;       // Determines sprite set (small/med/large)
+		int counter;
+		int scale;
 		bool active;
 	};
 
@@ -824,66 +571,37 @@ public:
 	Explosion _explosions[5];
 	void spawnExplosion(int x, int y, int objectHalfWidth);
 
-	// ---------------------------------------------------------------------------
-	// Collision Zone System
-	// ---------------------------------------------------------------------------
-	// For Level 3 "pilot" ship obstacle avoidance (FUN_40E35E, FUN_40C3CC).
-	// Collision zones are quadrilaterals defined by IACT Opcode 5
-	// The player's ship position is tested against these zones each frame
-	//
-	// Zone Data Layout from IACT chunk:
-	//   +0x00: opcode (5)
-	//   +0x02: sub-opcode (0x0D = primary, 0x0E = secondary)
-	//   +0x04: par3 (flags)
-	//   +0x06: zoneType (e.g., 5 for damage zones)
-	//   +0x08: frameStart
-	//   +0x0A: frameEnd
-	//   +0x0C-0x1A: X1,Y1,X2,Y2,X3,Y3,X4,Y4 vertex coordinates
-
+	// Collision zones registered by IACT opcode 5.
 	struct CollisionZone {
-		int16 x1, y1;  // Vertex 1 (body[2], body[3])
-		int16 x2, y2;  // Vertex 2 (body[4], body[5])
-		int16 x3, y3;  // Vertex 3 (body[6], body[7])
-		int16 x4, y4;  // Vertex 4 (body[8], body[9])
-		int16 field1;   // body[0] - control field (frame check: field2 - 1 == field1)
-		int16 field2;   // body[1] - control field
-		int16 filterValue; // par4 from IACT header - used for < 1000 filter
-		int16 subOpcode;   // 0x0D = primary, 0x0E = secondary
+		int16 x1, y1;
+		int16 x2, y2;
+		int16 x3, y3;
+		int16 x4, y4;
+		int16 field1;
+		int16 field2;
+		int16 filterValue;
+		int16 subOpcode;
 		bool active;
 	};
 
-	// Two zone tables matching DAT_0043fb00 (primary) and DAT_0043f9c8 (secondary)
 	static const int kMaxCollisionZones = 5;
-	CollisionZone _primaryZones[kMaxCollisionZones];    // Sub-opcode 0x0D zones
-	CollisionZone _secondaryZones[kMaxCollisionZones];  // Sub-opcode 0x0E zones
+	CollisionZone _primaryZones[kMaxCollisionZones];
+	CollisionZone _secondaryZones[kMaxCollisionZones];
 	int _primaryZoneCount;
 	int _secondaryZoneCount;
 
-	// Corridor boundaries from IACT opcode 7 sub-opcodes 1 and 2
-	int16 _corridorLeftX;    // DAT_00443b0a - Left X boundary
-	int16 _corridorTopY;     // DAT_00443b0c - Top Y boundary
-	int16 _corridorRightX;   // DAT_00443b0e - Right X boundary
-	int16 _corridorBottomY;  // DAT_00443b10 - Bottom Y boundary
+	int16 _corridorLeftX;
+	int16 _corridorTopY;
+	int16 _corridorRightX;
+	int16 _corridorBottomY;
 
-	// Hit cooldown timer (DAT_0044374c) - prevents rapid damage stacking
 	int16 _hitCooldown;
 
-	// Register a collision zone from IACT opcode 5 data
 	void registerCollisionZone(Common::SeekableReadStream &b, int16 subOpcode, int16 par4);
-
-	// Reset collision zone counters (called at end of frame)
 	void resetCollisionZones();
-
-	// Per-frame collision checking against registered zones (FUN_4092D9)
-	// Tests aim/ship position against primary zone quadrilaterals
-	// Applies collision damage from DAT_0047e0f6 when inside obstacle zone
 	void checkCollisionZones(byte *renderBitmap, int pitch, int width, int height, int32 curFrame);
 
-	// Handler 7 collision system (FUN_40E35E)
-	// Mode 0/2: Obstacle collision using secondary zones ??? inside quad = hit
-	// Mode 1/3: Wall/boundary collision using primary zones ??? per-edge push-back
-	// Uses ship position (_flyShipScreenX/_flyShipScreenY) in raw buffer coords
-	// and draws proximity shadow cues for nearby danger zones.
+	// Handler 7 collision and warning cues.
 	void checkHandler7CollisionZones(byte *renderBitmap, int pitch, int width, int height, int32 curFrame);
 	bool isHandler7ShipInsideObstacleZone(const CollisionZone &zone, int margin);
 	void applyHandler7ObstacleHit(const CollisionZone &zone, int zoneIndex);
@@ -899,8 +617,8 @@ public:
 	void checkHandler7BoundaryZones(uint16 &warningMask);
 	void renderHandler7WarningCues(byte *renderBitmap, int pitch, int width, int height, int32 curFrame, uint16 warningMask);
 
-	int16 _playerDamage;  // Legacy damage counter (kept for compatibility/telemetry)
-	int16 _playerShield;  // Shields: 0..255 where 255 = full
+	int16 _playerDamage;
+	int16 _playerShield;
 	int16 _playerLives;
 	int32 _playerScore;
 
@@ -910,125 +628,79 @@ public:
 	int _hiResPresentationViewY;
 	int _gameplayPresentationClipBottom;
 
-	// ---------------------------------------------------------------------------
-	// Damage Visual Effect System
-	// ---------------------------------------------------------------------------
-	// Palette flash + screen shake on taking damage.
-	// Original functions: FUN_420515, FUN_420562, FUN_420754, FUN_42073B
-	//
-	// FUN_42073B (triggerDamageEffect): Called on damage hit. Initiates palette
-	//   flash via FUN_420515 and sets screen shake counter to 10.
-	// FUN_420515 (initDamageFlash): Saves current palette, sets 5-frame flash.
-	// FUN_420562 (updateDamageFlashPalette): Per-frame palette inversion.
-	//   Normal hit: all RGB channels inverted toward white, fades over 5 frames.
-	//   High damage (>=255): red channel pulsing on even frames.
-	// FUN_420754 (updateDamageEffect): Per-frame screen shake (random scanline
-	//   shifts) + calls FUN_420562. Called every frame from the render loop.
-
-	void triggerDamageEffect();          // FUN_0042073B
-	void initDamageFlash();              // FUN_00420515
-	void updateDamageFlashPalette();     // FUN_00420562
-	void updateDamageEffect(byte *renderBitmap, int pitch, int width, int height); // FUN_00420754
-	void resetDamageFlash();             // FUN_00420501
+	// Palette flash and screen shake after damage.
+	void triggerDamageEffect();
+	void initDamageFlash();
+	void updateDamageFlashPalette();
+	void updateDamageEffect(byte *renderBitmap, int pitch, int width, int height);
+	void resetDamageFlash();
 	void restoreDamageFlashPalette();
 
-	int16 _damageFlashCounter;           // DAT_00482404 - palette flash countdown (0..5)
-	int16 _damageHighFlashCounter;       // DAT_00482408 - high-damage red flash (0..16)
-	int16 _damageShakeCounter;           // DAT_0048240c - screen shake countdown (0..10)
-	byte _damageSavedPalette[0x300];     // DAT_00459990 - palette snapshot before flash
-	byte _damageRestorePalette[0x300];   // Boundary restore snapshot
+	int16 _damageFlashCounter;
+	int16 _damageHighFlashCounter;
+	int16 _damageShakeCounter;
+	byte _damageSavedPalette[0x300];
+	byte _damageRestorePalette[0x300];
 	bool _damageRestorePaletteValid;
 
-	// Rebel per-level counters / flags mapped from original globals
-	bool _rebelOp6Initialized; // Guard: opcode 6 init block (clearBit/links/wave) runs once per video
-	int _rebelHitCounter;    // DAT_0047ab80 - hit counter / state tracker
-	int _rebelKillCounter;   // DAT_0047ab88 - enemies destroyed this phase
-	bool _rebelYodaMode;     // DAT_0047ab5a > 2 - unlocks Yoda-mode shortcuts
-	bool _rebelMovieMode;    // DAT_0047ab60 - Alt+M skips playable sections
-	bool _rebelAutoPlay;     // DAT_0047ab64 - Alt+P computer-controlled play
-
-	// Enemy wave/phase state tracking (FUN_004028c5 / FUN_00417b61)
-	// DAT_0047ab98: Per-wave enemy kill state. Bits set when enemy types are destroyed.
-	// DAT_0047ab9c: Per-phase accumulated state. Copied from _rebelWaveState between waves.
-	// Phase completion: (_rebelPhaseState & mask) == mask (all required enemy types killed)
-	int _rebelWaveState;     // DAT_0047ab98 - current wave enemy kill flags
-	int _rebelPhaseState;    // DAT_0047ab9c - accumulated phase enemy kill flags
-
-	// Opcode 6 state variables (from FUN_41CADB case 4)
-	int _rebelAutopilot;     // DAT_00457904 - autopilot flag (0 or 1)
-	int _rebelDamageLevel;   // DAT_0045790a - damage level (0-5)
-	int _rebelFlightDir;     // DAT_00457902 - flight direction (0 or 1)
-	int _rebelControlMode;   // DAT_0047a7e4 - control mode flags
-	int _rebelInputThrottle; // DAT_00482278 - frame counter for input throttling (every 5 frames)
-
-	// View offset variables (calculated from level type)
-	int _rebelViewOffsetX;   // DAT_0045790c
-	int _rebelViewOffsetY;   // DAT_0045790e
-	int _rebelViewOffset2X;  // DAT_00457910
-	int _rebelViewOffset2Y;  // DAT_00457912
-	int _rebelViewMode1;     // DAT_00482270
-	int _rebelViewMode2;     // DAT_00482274
-
-	// Original counters mirrored from DAT_00443618 (values 100..109) and DAT_004436e0 (mask counters 1..9)
-	short _rebelValueCounters[10]; // Index 0 -> value 100, ... Index 9 -> 109
-	short _rebelMaskCounters[10];  // Index 1..9 used; index 0 unused
-	int _rebelLastCounter;         // Mirrors DAT_0047ab90 (last updated counter)
+	// Per-level counters and debug flags.
+	bool _rebelOp6Initialized;
+	int _rebelHitCounter;
+	int _rebelKillCounter;
+	bool _rebelYodaMode;
+	bool _rebelMovieMode;
+	bool _rebelAutoPlay;
+
+	int _rebelWaveState;
+	int _rebelPhaseState;
+
+	int _rebelAutopilot;
+	int _rebelDamageLevel;
+	int _rebelFlightDir;
+	int _rebelControlMode;
+	int _rebelInputThrottle;
+
+	int _rebelViewOffsetX;
+	int _rebelViewOffsetY;
+	int _rebelViewOffset2X;
+	int _rebelViewOffset2Y;
+	int _rebelViewMode1;
+	int _rebelViewMode2;
+
+	short _rebelValueCounters[10];
+	short _rebelMaskCounters[10];
+	int _rebelLastCounter;
 
 	// Shield hit-point gauge: opcode-2 sets up a per-target counter; destroying a tracked
 	// target decrements it, and the looping attack run ends when it reaches 0.
-	int8 _rebelGaugeSlot[512];     // per target ID: -1 none; 0..9 value-counter; 10+slot mask-counter
-	bool _rebelShieldGateActive;   // true while a shield-gated looping segment is playing
-	bool _rebelShieldDestroyed;    // set when the shield/reactor is destroyed during the gate
-	bool _rebelReactorMode;        // Level 13: finale ends when the last armed group is cleared
-	bool _rebelGaugeArmed;         // at least one gauge group has been set up this attempt
-	int _rebelLastArmedSlot;       // counter slot of the most recently armed group (-1 = none)
-	bool _rebelGaugeCleared[10];   // value-counter slot fully destroyed (drives par3==2 surfaces)
+	int8 _rebelGaugeSlot[512];
+	bool _rebelShieldGateActive;
+	bool _rebelShieldDestroyed;
+	bool _rebelReactorMode;
+	bool _rebelGaugeArmed;
+	int _rebelLastArmedSlot;
+	bool _rebelGaugeCleared[10];
 	void resetShieldGauge();
 
-
-	// ---------------------------------------------------------------------------
-	// Handler 0x26 Turret Shot System
-	// ---------------------------------------------------------------------------
-	// Based on FUN_40AD63 disassembly - Turret laser rendering.
-	// DAT_0044367a[2]: Shot duration counter (0=inactive)
-	// DAT_0044367e[2]: Target X position
-	// DAT_00443682[2]: Target Y position
-	// DAT_0044368a[2]: Shot sequence number (for alternating gun pattern)
-	// DAT_004436de: Level type (determines gun positions) - already have as _rebelLevelType
-
+	// Handler-specific shot state.
 	struct TurretShot {
-		int16 counter;     // DAT_0044367a[i] - duration counter, 0=inactive
-		int16 targetX;     // DAT_0044367e[i] - target X position
-		int16 targetY;     // DAT_00443682[i] - target Y position
-		int16 seqNum;      // DAT_0044368a[i] - shot sequence (for alternating)
-		int16 gunX;        // DAT_0045791c[i] - gun barrel X (Handler 25 base coords; render adds view offset)
-		int16 gunY;        // DAT_00457920[i] - gun barrel Y (Handler 25 base coords; render adds view offset)
+		int16 counter;
+		int16 targetX;
+		int16 targetY;
+		int16 seqNum;
+		int16 gunX;
+		int16 gunY;
 	};
 	TurretShot _turretShots[2];
-	int16 _turretShotSeqCounter;  // DAT_0047fe94 - global sequence counter
-
-	// ---------------------------------------------------------------------------
-	// Handler 8 Vehicle Shot System
-	// ---------------------------------------------------------------------------
-	// Based on FUN_402ED0 disassembly - Vehicle laser rendering.
-	// DAT_0043e00a[2]: Shot duration counter
-	// DAT_0043e00e[2]: Target X position
-	// DAT_0043e012[2]: Target Y position
-	// Gun position derived from ship position (_shipPosX, _shipPosY)
+	int16 _turretShotSeqCounter;
 
 	struct VehicleShot {
-		int16 counter;     // DAT_0043e00a[i] - duration counter, 0=inactive
-		int16 targetX;     // DAT_0043e00e[i] - target X position
-		int16 targetY;     // DAT_0043e012[i] - target Y position
+		int16 counter;
+		int16 targetX;
+		int16 targetY;
 	};
 	VehicleShot _vehicleShots[2];
 
-	// Handler 8 shot impact animation state (FUN_402DA8 / FUN_402ED0)
-	// DAT_0043f81a[7]: impact duration counter
-	// DAT_0043f828[7]: impact world X
-	// DAT_0043f836[7]: impact world Y
-	// DAT_0043f844[7]: impact sprite index from DAT_0047e030 background mask
-	// DAT_0043f852: ring-buffer index
 	struct VehicleShotImpact {
 		int16 counter;
 		int16 x;
@@ -1038,372 +710,186 @@ public:
 	VehicleShotImpact _vehicleShotImpacts[7];
 	int16 _vehicleShotImpactIndex;
 
-	// ---------------------------------------------------------------------------
-	// Handler 7 Third-Person Ship Shot System
-	// ---------------------------------------------------------------------------
-	// Based on FUN_40FADF disassembly - Third-Person Ship laser rendering.
-	// DAT_00443750[2]: Shot duration counter
-	// DAT_00443754[2]: Target X position
-	// DAT_00443758[2]: Target Y position
-	// DAT_0044375c[2]: Left gun X position
-	// DAT_00443760[2]: Left gun Y position
-	// DAT_00443764[2]: Right gun X position
-	// DAT_00443768[2]: Right gun Y position
-	// DAT_0044376c[2]: Shot variant
-
 	struct SpaceShot {
-		int16 counter;     // DAT_00443750[i] - duration counter, 0=inactive
-		int16 targetX;     // DAT_00443754[i] - target X position
-		int16 targetY;     // DAT_00443758[i] - target Y position
-		int16 leftGunX;    // DAT_0044375c[i] - left gun X
-		int16 leftGunY;    // DAT_00443760[i] - left gun Y
-		int16 rightGunX;   // DAT_00443764[i] - right gun X
-		int16 rightGunY;   // DAT_00443768[i] - right gun Y
-		int16 variant;     // DAT_0044376c[i] - shot variant
+		int16 counter;
+		int16 targetX;
+		int16 targetY;
+		int16 leftGunX;
+		int16 leftGunY;
+		int16 rightGunX;
+		int16 rightGunY;
+		int16 variant;
 	};
 	SpaceShot _spaceShots[2];
-	int16 _spaceShotDirection;  // DAT_0044374e - ship direction for gun lookup
-	int16 _flyLeftGunX[35];     // DAT_004437c2 - handler 7 left muzzle X table
-	int16 _flyLeftGunY[35];     // DAT_00443808 - handler 7 left muzzle Y table
-	int16 _flyRightGunX[35];    // DAT_0044384e - handler 7 right muzzle X table
-	int16 _flyRightGunY[35];    // DAT_00443894 - handler 7 right muzzle Y table
+	int16 _spaceShotDirection;
+	int16 _flyLeftGunX[35];
+	int16 _flyLeftGunY[35];
+	int16 _flyRightGunX[35];
+	int16 _flyRightGunY[35];
 	bool _flyLeftGunTableLoaded;
 	bool _flyRightGunTableLoaded;
 
-	// Handler-specific shot spawning
-	void spawnTurretShot(int x, int y);    // Handler 0x26
-	void spawnVehicleShot(int x, int y);   // Handler 8
-	void spawnSpaceShot(int x, int y);     // Handler 7
-	void spawnHandler25Shot(int x, int y); // Handler 25 (speeder bike)
-	void spawnShot(int x, int y);          // Dispatcher based on current handler
+	void spawnTurretShot(int x, int y);
+	void spawnVehicleShot(int x, int y);
+	void spawnSpaceShot(int x, int y);
+	void spawnHandler25Shot(int x, int y);
+	void spawnShot(int x, int y);
 
-	// Handler-specific explosion rendering
 	void renderExplosionFrame(byte *renderBitmap, int pitch, int width, int height,
 	                          Explosion &explosion, int screenX, int screenY, ExplosionFrameAdvance advance,
 	                          bool resolutionDependentScale);
-	void renderTurretExplosions(byte *renderBitmap, int pitch, int width, int height);     // FUN_409FBC (Handler 0x26)
-	void renderVehicleExplosions(byte *renderBitmap, int pitch, int width, int height);    // FUN_402696 (Handler 8)
-	void renderSpaceExplosions(byte *renderBitmap, int pitch, int width, int height);      // FUN_40F1C5 (Handler 7)
-	void renderHandler25Explosions(byte *renderBitmap, int pitch, int width, int height);  // FUN_41F29A (Handler 25)
+	void renderTurretExplosions(byte *renderBitmap, int pitch, int width, int height);
+	void renderVehicleExplosions(byte *renderBitmap, int pitch, int width, int height);
+	void renderSpaceExplosions(byte *renderBitmap, int pitch, int width, int height);
+	void renderHandler25Explosions(byte *renderBitmap, int pitch, int width, int height);
 
-	// Handler-specific laser rendering (FUN_40AD63, FUN_402ED0, FUN_40FADF, FUN_0041f004)
 	void renderTurretLaserShots(byte *renderBitmap, int pitch, int width, int height);
 	void renderVehicleLaserShots(byte *renderBitmap, int pitch, int width, int height);
 	void renderSpaceLaserShots(byte *renderBitmap, int pitch, int width, int height);
 	void renderHandler25LaserShots(byte *renderBitmap, int pitch, int width, int height);
 
-	// Get max shot duration from level table (DAT_0047e0f0 indexed by DAT_0047a7fa/DAT_0047a7f8)
 	int16 getShotMaxDuration();
 
-	// ---------------------------------------------------------------------------
-	// Handler 8 Ship System
-	// ---------------------------------------------------------------------------
-	// For third-person on foot missions (Level 2, 11), the player controls Rookie One
-	// that can turn in different directions. The ship sprite comes from
-	// NUT files loaded via IACT opcode 8.
-	//
-	// Based on FUN_00401234 and FUN_00401ccf disassembly:
-	// - DAT_0047e010: Primary ship sprite (POV001, subcase 1)
-	// - DAT_0047e028: Secondary ship sprite (POV004, subcase 3)
-	// - DAT_0047e020: Shot impact overlay (POV002, subcase 6)
-	// - DAT_0047e018: Shot impact overlay (POV003, subcase 7)
-	// - DAT_0043e006: Ship X position (raw, needs conversion for display)
-	// - DAT_0043e008: Ship Y position (raw, needs conversion for display)
-	// - DAT_0043e000: Level mode from opcode 6 par4
-
-	NutRenderer *_shipSprite;        // DAT_0047e010 - Primary ship NUT
-	NutRenderer *_shipSprite2;       // DAT_0047e028 - Secondary ship NUT
-	NutRenderer *_shipOverlay1;      // DAT_0047e020 - Shot impact overlay
-	NutRenderer *_shipOverlay2;      // DAT_0047e018 - Shot impact overlay
-
-	// Level 2 background buffer (DAT_0047e030)
-	// Loaded from IACT opcode 8, par4=5 - contains 320x200 background image
-	// decoded from embedded ANIM in gameplay video frame 0
+	// Handler 8 ship and background state.
+	NutRenderer *_shipSprite;
+	NutRenderer *_shipSprite2;
+	NutRenderer *_shipOverlay1;
+	NutRenderer *_shipOverlay2;
+
 	byte *_level2Background;
 	bool _level2BackgroundLoaded;
 
-	// Ship position tracking (matches DAT_0043e006/008)
-	// These are "raw" positions that get converted for display
-	int16 _shipPosX;                 // DAT_0043e006
-	int16 _shipPosY;                 // DAT_0043e008
-
-	// Ship target positions (where ship is trying to move to)
-	// Set from mouse/joystick input in opcode 6 processing
-	int16 _shipTargetX;              // DAT_0043e002 - Target X
-	int16 _shipTargetY;              // DAT_0043e004 - Target Y
-
-	// Level mode for handler 8 (different from _rebelLevelType)
-	// Set by opcode 6 par4, affects ship rendering behavior
-	// Mode 0/1/3: "Shooting" - full movement range (127)
-	// Mode 2: "Covered" - restricted movement (41) - behind cover
-	// Mode 4: "Autopilot" - no shooting, scripted movement
-	// Mode 5: "Cutscene" - ship not rendered
-	int16 _shipLevelMode;            // DAT_0043e000
-	char _handler8HudGlyph;          // DAT_0047e048
-	int16 _handler8HudMessageTimer;  // DAT_0047e040
-	int16 _handler8HudMessageIndex;  // DAT_0047e044
-
-	// Movement range limiter for Handler 8 (Level 2 covered/shooting states)
-	// Controls horizontal movement range: 127 for shooting, 41 for covered
-	// Gradually transitions by ??10 per frame for smooth animation
-	int16 _movementRangeLimit;       // DAT_0047e034
-
-	// Control mode for Handler 7 (third-person ship) - DAT_004437c0
-	// Set by IACT opcode 6 par3 when handler is 7
-	// Determines shooting capability and collision zone type:
-	//   Mode 0: Flight/avoid mode - no shooting, uses secondary zones (sub-opcode 0x0E)
-	//   Mode 1: Alternate flight mode - no shooting, uses primary zones (sub-opcode 0x0D)
-	//   Mode 2: Combat mode - shooting ENABLED, crosshair shown, uses secondary zones
-	// In Level 3's first sequence, par3=0 (no shooting - pure obstacle avoidance)
-	// In combat sequences, par3=2 (shooting enabled)
-	int16 _flyControlMode;           // DAT_004437c0
-
-	// Ship firing state (from mouse button)
-	bool _shipFiring;
+	int16 _shipPosX;
+	int16 _shipPosY;
+	int16 _shipTargetX;
+	int16 _shipTargetY;
+	int16 _shipLevelMode;
+	char _handler8HudGlyph;
+	int16 _handler8HudMessageTimer;
+	int16 _handler8HudMessageIndex;
+
+	// Handler 8 movement range transitions between cover and shooting states.
+	int16 _movementRangeLimit;
+
+	// Handler 7 control mode selects shooting and collision behavior.
+	int16 _flyControlMode;
 
-	// Previous mouse button state for edge detection (bit 0=left, bit 1=right, bit 2=middle)
+	bool _shipFiring;
 	uint32 _prevMouseButtons;
 
-	// Ship direction index for sprite selection (Handler 7)
-	// Calculated from ship position: horizontal * 7 + vertical
-	// horizontal: 0-4 (left to right), vertical: 0-6 (up to down)
-	// Used to select which embedded HUD userId to render
 	int16 _shipDirectionIndex;
-	int16 _shipDirectionH;           // Horizontal direction (0-4, center=2)
-	int16 _shipDirectionV;           // Vertical direction (0-6, center=3)
-
-	// ---------------------------------------------------------------------------
-	// Handler 7 FLY Ship System
-	// ---------------------------------------------------------------------------
-	// For third-person ship missions (Level 3, etc.), Handler 7 uses a 35-frame
-	// direction-based ship sprite system. The ship visually banks and turns
-	// based on player position using a 5x7 grid of sprites.
-	//
-	// Based on FUN_0040c3cc and FUN_0040d836 disassembly:
-	// - DAT_0047fee8: Ship direction sprites (FLY001, par3=1, 35 frames)
-	// - DAT_0047fef0: Ship effect sprites (FLY002, par3=3)
-	// - DAT_0047fef8: Targeting overlay (FLY003, par3=2)
-	// - DAT_0047ff00: High-res alternative (FLY004, par3=11)
-	// - DAT_00443708: Ship X position, DAT_0044370a: Ship Y position
-	// - DAT_0044370c: Smoothed horizontal velocity, DAT_0044370e: Vertical input
-
-	NutRenderer *_flyShipSprite;     // DAT_0047fee8 - FLY001 (35 direction frames)
-	NutRenderer *_flyLaserSprite;    // DAT_0047fef0 - FLY002 (danger/overlay effects)
-	NutRenderer *_flyTargetSprite;   // DAT_0047fef8 - FLY003
-	NutRenderer *_flyHiResSprite;    // DAT_0047ff00 - FLY004
-	int16 _flyEffectAnimCounter;     // DAT_0047ff1c - animated FLY002 cue counter
-	int16 _flyOverlayRepeatCount;    // DAT_00443b52 - repeats for ship overlay effect
-
-	// Handler 7 ship state (FUN_40C3CC / FUN_0040d836)
-	// Position in game coordinate space [20,404]x[20,240], center=(212,130)
-	int16 _flyShipScreenX;           // DAT_00443708 - Ship X game position
-	int16 _flyShipScreenY;           // DAT_0044370a - Ship Y game position
-
-	// Physics state (velocity-based movement system from FUN_40C3CC case 4)
-	int16 _smoothedVelocity;         // DAT_0044370c - Averaged horizontal velocity (from history)
-	int16 _verticalInput;            // DAT_0044370e - Stored vertical input component
-	int16 _velocityHistory[25];      // DAT_00443716 - Horizontal velocity ring buffer (25 entries)
-	int16 _windHistoryX[15];         // DAT_00443b16 - Wind X history buffer
-	int16 _windHistoryY[15];         // DAT_00443b34 - Wind Y history buffer
-	int16 _windParamX;               // DAT_00443b12 - Wind X (from opcode 7 par4=0)
-	int16 _windParamY;               // DAT_00443b14 - Wind Y (from opcode 7 par4=0)
-
-	// Perspective view offsets (computed from ship position, used for rendering)
-	int16 _perspectiveX;             // DAT_00443712 - Perspective shift X
-	int16 _perspectiveY;             // DAT_00443714 - Perspective shift Y
-	int16 _viewShift;                // DAT_00443710 - Clamped smoothed velocity for view transform
-	bool _facingRight;               // DAT_0047ab8c - Ship facing right of center
-
-	// ---------------------------------------------------------------------------
-	// Handler 25 (0x19) GRD Ship System
-	// ---------------------------------------------------------------------------
-	// For mixed mode missions (Level 2 speeder bike, etc.), Handler 25 uses GRD NUT
-	// sprites loaded via IACT opcode 8. The ship is rendered based on DAT_00457900 mode.
-	//
-	// Based on FUN_0041cadb case 6 and FUN_0041db5e disassembly:
-	// - DAT_00482240: Primary ship sprite (GRD001, par4=1)
-	// - DAT_00482238: Secondary ship sprite (GRD002, par4=2)
-	// - DAT_00482258: Mode 3 overlay sprite (GRD005, par4=10)
-	// - DAT_00457900: Sprite mode (1,2,3,4) controls which sprite to draw
-	// - DAT_00457910: Ship X screen position
-	// - DAT_00457912: Ship Y screen position
-	// - DAT_00457902: Flight direction (affects GRD002 mirroring)
-	// - DAT_0045790a: Damage level (affects rendering conditions)
-
-	NutRenderer *_grd001Sprite;      // DAT_00482240 - GRD001 primary ship NUT
-	NutRenderer *_grd002Sprite;      // DAT_00482238 - GRD002 secondary ship NUT
-	NutRenderer *_grd005Sprite;      // DAT_00482258 - GRD005 mode 3 overlay NUT
-
-	// Handler 25 shot-origin lookup tables from opcode 8/par4=8 text payload.
-	// Indices 5..19 are filled by the "%hd %hd ..." parser in FUN_0041CADB case 6.
-	// Uncovered Level 2 firing uses indices 5..14.
-	int16 _grdShotOriginX[30];       // DAT_004578a6 equivalent
-	int16 _grdShotOriginY[30];       // DAT_004578c6 equivalent
+	int16 _shipDirectionH;
+	int16 _shipDirectionV;
+
+	// Handler 7 FLY ship state.
+	NutRenderer *_flyShipSprite;
+	NutRenderer *_flyLaserSprite;
+	NutRenderer *_flyTargetSprite;
+	NutRenderer *_flyHiResSprite;
+	int16 _flyEffectAnimCounter;
+	int16 _flyOverlayRepeatCount;
+
+	int16 _flyShipScreenX;
+	int16 _flyShipScreenY;
+
+	int16 _smoothedVelocity;
+	int16 _verticalInput;
+	int16 _velocityHistory[25];
+	int16 _windHistoryX[15];
+	int16 _windHistoryY[15];
+	int16 _windParamX;
+	int16 _windParamY;
+
+	int16 _perspectiveX;
+	int16 _perspectiveY;
+	int16 _viewShift;
+	bool _facingRight;
+
+	// Handler 25 GRD ship state.
+	NutRenderer *_grd001Sprite;
+	NutRenderer *_grd002Sprite;
+	NutRenderer *_grd005Sprite;
+
+	int16 _grdShotOriginX[30];
+	int16 _grdShotOriginY[30];
 	bool _grdShotOriginTableLoaded;
 
-	// Handler 25 sprite mode (DAT_00457900) - set by opcode 6 par3
-	// Controls which sprite variant to draw:
-	//   1: Draw _grd001Sprite normally
-	//   2: Draw _grd001Sprite only when damaged (DAT_0045790a != 0)
-	//   3: Draw _grd001Sprite and GRD005 (DAT_00482258) overlay
-	//   4: Draw _grd001Sprite with buffer offset
-	int16 _grdSpriteMode;            // DAT_00457900
-
-	// Render Handler 25 ship sprites
-	// renderHandler25ShipPre: Draw GRD001 BEFORE FOBJ (in procPreRendering)
-	// renderHandler25Ship: Draw GRD002 and other overlays AFTER FOBJ (in procPostRendering)
+	int16 _grdSpriteMode;
+
 	void renderHandler25ShipPre(byte *renderBitmap, int pitch, int width, int height);
 	void renderHandler25Ship(byte *renderBitmap, int pitch, int width, int height);
 
-	// ---------------------------------------------------------------------------
-	// Handler 0x26 Turret HUD Overlays
-	// ---------------------------------------------------------------------------
-	// For turret missions (Level 1, etc.), Handler 0x26 uses NUT-based HUD overlays
-	// loaded via IACT opcode 8. These contain animated cockpit panel elements.
-	//
-	// Based on FUN_00407fcb and FUN_004089ab disassembly:
-	// - DAT_0047fe78: Primary HUD overlay (GRD001/002, par3=1 or 2, 6 animation frames)
-	// - DAT_0047fe80: Secondary HUD overlay (GRD010, par3=3 or 4, static or animated)
-	//
-	// Animation: The HUD overlay cycles through 6 sprite frames for blinking lights
-	// Formula: spriteIndex = (frameCounter / 2) % 6
-	//
-	// Position formula (from FUN_004089ab lines 203-222):
-	// X = 160 + (mouseOffsetX >> 4) - (width / 2) - spriteOffsetX
-	// Y = 182 - (mouseOffsetY >> 4) - height - spriteOffsetY
-
-	NutRenderer *_hudOverlayNut;     // DAT_0047fe78 - Primary HUD overlay (animated)
-	NutRenderer *_hudOverlay2Nut;    // DAT_0047fe80 - Secondary HUD overlay
-
-	/* Difficulty Level (0-5, from pilot menu; maps directly to table rows) */
-	int _difficulty;
+	// Handler 0x26 turret HUD overlays.
+	NutRenderer *_hudOverlayNut;
+	NutRenderer *_hudOverlay2Nut;
 
-	// ---------------------------------------------------------------------------
-	// Per-Level Difficulty Parameters
-	// ---------------------------------------------------------------------------
-	// Extracted from RA2WIN95.EXE at VA 0x47e0f0.
-	// 2D table indexed by difficulty (0-5) ?? level type (0-16)
-	// Original indexing: &DAT_0047e0f0 + chapter * 0x242 + levelType * 0x22
-	// Level type (_rebelLevelType) is set by IACT opcode 6 par3
-	// 17 entries: Lv1-5(0-4), Lv6A/6B(5-6), Lv7-14(7-14), Lv15A/15B(15-16)
-	// -1 = not applicable for this level type
+	int _difficulty;
 
+	// Per-level difficulty parameters.
 	struct LevelDifficultyParams {
-		int16 laserDelay;      // +0x00: Laser fire delay (lower = faster)
-		int16 snapDistance;    // +0x02: Crosshair snap distance to targets
-		int16 missDamage;      // +0x04: Damage from enemy misses / grazing hits
-		int16 dodgeDamage;     // +0x06: Damage from wall/obstacle collisions
-		int16 shotDamage;      // +0x08: Damage from enemy projectile hits
-		int16 specialDamage;   // +0x0A: Damage from special attacks
-		int16 shotAccuracy;    // +0x0C: Enemy shot accuracy (0-100, -1=disabled)
-		int16 hitPoints;       // +0x0E: Points awarded for destroying an enemy
-		int16 dodgePoints;     // +0x10: Points awarded for dodging an obstacle
-		int16 timePoints;      // +0x12: Time bonus points
-		int16 levelPoints;     // +0x14: End-of-level bonus points
-		int16 specialPoints;   // +0x16: Special action bonus points
-		int16 flags;           // +0x18: Behavior flags bitfield
-		int16 rollRate;        // +0x1A: Ship roll rate (flight controls)
-		int16 liftRate;        // +0x1C: Ship lift rate (flight controls)
-		int16 slideRate;       // +0x1E: Ship slide rate (flight controls)
-		int16 driftRate;       // +0x20: Ship drift rate (flight controls)
+		int16 laserDelay;
+		int16 snapDistance;
+		int16 missDamage;
+		int16 dodgeDamage;
+		int16 shotDamage;
+		int16 specialDamage;
+		int16 shotAccuracy;
+		int16 hitPoints;
+		int16 dodgePoints;
+		int16 timePoints;
+		int16 levelPoints;
+		int16 specialPoints;
+		int16 flags;
+		int16 rollRate;
+		int16 liftRate;
+		int16 slideRate;
+		int16 driftRate;
 	};
 
-	// Table: 6 difficulty levels ?? 17 level types.
-	// Menu labels in GAME.TRS are: Beginner, Novice, Standard, Expert, Custom1, Custom2.
-	// Custom1 is identical to Standard; Custom2 matches Custom1 except Lv15B drift fields.
 	static const LevelDifficultyParams kDifficultyTable[6][17];
 
-	// Look up difficulty parameters for current _difficulty and _rebelLevelType
 	LevelDifficultyParams getDifficultyParams() const;
 
-	// Score system (FUN_0041bf8d equivalent)
-	// Adds points to score and awards bonus life when crossing threshold
 	void addScore(int points);
-
-	// Score lookup uses LevelDifficultyParams fields:
-	// hitPoints (DAT_0047e0fe), dodgePoints (DAT_0047e100), timePoints (DAT_0047e102)
-
-	// Render score text to HUD (called from procPostRendering)
 	void renderScoreHUD(byte *renderBitmap, int pitch, int width, int height, int statusBarY);
 
-	// ---------------------------------------------------------------------------
-	// Pause Overlay
-	// ---------------------------------------------------------------------------
-	// Show pause overlay with dimming effect and "PAUSED" text.
-	// Emulates FUN_405A21 pause rendering (lines 242-305)
 	void showPauseOverlay();
 
-	// Target lock timer (DAT_00443676) - set to 7 when crosshair is over enemy
 	int _targetLockTimer;
 
-	// ---------------------------------------------------------------------------
-	// Audio Handling
-	// ---------------------------------------------------------------------------
-	// RA2 doesn't use iMUSE -- audio is handled directly through the mixer.
-
+	// Audio.
 	RebelAudio _audio;
 
-	// Initialize audio system for RA2
 	void initAudio(int sampleRate);
-
-	// Terminate audio system
 	void terminateAudio();
-
-	// Reset streamed SAN audio at independent video boundaries.
 	void resetVideoAudio();
-
-	// Process audio dispatches - called from SmushPlayer when iMUSE is null
-	// This replaces the iMUSE audio path for RA2
 	void processAudioFrame(int16 feedSize);
-
-	// Queue audio data for playback on a specific track
 	void queueAudioData(int trackIdx, uint8 *data, int32 size, int volume, int pan);
 
-	// ---------------------------------------------------------------------------
-	// Sound Effects (SAD files)
-	// ---------------------------------------------------------------------------
-	// 8 standalone SAUD files in SYSTM/ loaded at init for one-shot SFX.
-	// Slot mapping (from FUN_0042a3b0 init):
-	//   0=BLAST.SAD   1=CRASH.SAD   2=EXPLODE.SAD  3=ALERT.SAD
-	//   4=LOCKON.SAD  5=BONUS.SAD   6=HBLAST.SAD   7=TBLAST.SAD
-
+	// One-shot SFX.
 	static const int kRA2NumSfx = 8;
 
-	byte *_sfxData[kRA2NumSfx];         // Loaded PCM data for each SAD slot
-	uint32 _sfxSize[kRA2NumSfx];        // PCM data size per slot
-	Audio::SoundHandle _sfxHandles[kRA2NumSfx]; // Mixer handles for SFX playback
+	byte *_sfxData[kRA2NumSfx];
+	uint32 _sfxSize[kRA2NumSfx];
+	Audio::SoundHandle _sfxHandles[kRA2NumSfx];
 
-	// Load all SAD files from SYSTM/ directory
 	void loadSfx();
-
-	// Free all loaded SFX data
 	void freeSfx();
-
-	// Play a one-shot sound effect
-	// slot: 0-7 (SAD file index), volume: 0-127, pan: -127..+127
 	void playSfx(int slot, int volume, int pan);
 
-	// ---------------------------------------------------------------------------
-	// Auxiliary Sound Buffers
-	// ---------------------------------------------------------------------------
-	// 4 pre-allocated buffers (30000 bytes each) loaded from IACT stream data.
-	// Original: DAT_00480308[0..3], loaded via FUN_004118df, played via FUN_00411931.
-	// Used for embedded sound effects (e.g., soldier death sounds in handler 8 levels).
+	// Embedded sound effects.
 	static const int kRA2NumAuxSfx = 4;
 	static const int kRA2AuxBufSize = 30000;
 
-	byte *_auxSfxData[kRA2NumAuxSfx];     // Pre-allocated buffer pointers
-	uint32 _auxSfxSize[kRA2NumAuxSfx];    // Current data size in each buffer
-	Audio::SoundHandle _auxSfxHandles[kRA2NumAuxSfx]; // Mixer handles
+	byte *_auxSfxData[kRA2NumAuxSfx];
+	uint32 _auxSfxSize[kRA2NumAuxSfx];
+	Audio::SoundHandle _auxSfxHandles[kRA2NumAuxSfx];
 
-	// Load sound data into auxiliary buffer (FUN_004118df equivalent)
 	void loadAuxSfx(int buffer, const byte *data, uint32 size);
-
-	// Play from auxiliary buffer (FUN_00411931 equivalent)
 	void playAuxSfx(int buffer, int volume, int pan);
 
 };
 
-} // End of namespace Insane
+} // namespace Scumm
 
 #endif
diff --git a/engines/scumm/insane/rebel2/render.cpp b/engines/scumm/insane/rebel2/render.cpp
index e7538874c1a..a153af2631b 100644
--- a/engines/scumm/insane/rebel2/render.cpp
+++ b/engines/scumm/insane/rebel2/render.cpp
@@ -42,9 +42,7 @@ extern void smushDecodeRLE(byte *dst, const byte *src, int left, int top, int wi
 extern void smushDecodeUncompressed(byte *dst, const byte *src, int left, int top, int width, int height, int pitch);
 
 int getRebel2IndicatorScale(int width, int height) {
-	// Original only doubles these anchors in true high-res mode (DAT_0047a808 >= 2).
 	// RA2's 424x260 low-res gameplay buffer is still displayed through a 320x200
-	// viewport, so it uses the original 320x200 indicator coordinates.
 	return (width >= 640 || height >= 400) ? 2 : 1;
 }
 
@@ -185,9 +183,6 @@ static bool readEmbeddedSanChunkHeader(Common::SeekableReadStream &stream, int64
 void InsaneRebel2::renderEmbeddedFrame(byte *renderBitmap, const EmbeddedSanFrame &frame, int userId) {
 	// Render the decoded embedded frame to the video buffer
 	// Skip immediate draw for handlers that render HUD during post-processing:
-	// - Handler 7/8: Ship direction sprites selected based on direction
-	// - Handler 0x26: Cockpit HUD positioned based on mouse/crosshair
-	//
 	// Exception: Handler 25 (0x19) background overlays (par4/userId=4, 6, 7) should draw immediately.
 	// These complete the visual scene and are NOT positioned by mouse/crosshair.
 	bool skipImmediateDraw = (_rebelHandler == 7 || _rebelHandler == 8 ||
@@ -195,8 +190,6 @@ void InsaneRebel2::renderEmbeddedFrame(byte *renderBitmap, const EmbeddedSanFram
 
 	// Handler 25 overlays:
 	// - userId 4 (corridor overlay): draw immediately at the current view offset.
-	//   FUN_0041cadb case 6/par4=4 decodes DAT_00482268, then calls
-	//   FUN_00428a10(param_1, 0, DAT_0045790c, DAT_0045790e, DAT_00482268).
 	// - userId 6, 7 (static overlays): Draw immediately (they don't move)
 	if (_rebelHandler == 0x19 && userId == 4) {
 		drawHandler25CorridorOverlay(renderBitmap);
@@ -273,12 +266,9 @@ void InsaneRebel2::drawHandler25CorridorOverlay(byte *renderBitmap) {
 		_rebelViewOffsetX, _rebelViewOffsetY, corridorOverlay.width, corridorOverlay.height);
 }
 
-//
 // loadEmbeddedSan -- Decode an embedded SAN (ANIM/FOBJ) from IACT opcode 8 data.
-//
 // Parses ANIM container, extracts FOBJ codec data, decodes using codec 21/23/45,
 // and stores the result in _rebelEmbeddedHud[userId] for later rendering.
-//
 void InsaneRebel2::loadEmbeddedSan(int userId, byte *animData, int32 size, byte *renderBitmap) {
 	// Validate userId - Level 3 uses slots 0-11, allow up to 15 for safety
 	if (userId < 0 || userId > 15 || !animData || size < 8) {
@@ -388,29 +378,23 @@ void InsaneRebel2::loadEmbeddedSan(int userId, byte *animData, int32 size, byte
 								return;
 							}
 
-							// Decode based on codec - use extracted helper functions (FUN_0042BD60, etc.)
 							if (codec == 1 || codec == 3) {
-								// Codec 1/3: RLE - use existing decoder (FUN_0042C590)
 								smushDecodeRLE(frame.pixels, fobjData, 0, 0, width, height, width);
 								frame.valid = true;
 								debugC(DEBUG_INSANE, "Decoded embedded HUD (codec %d/RLE): %dx%d", codec, width, height);
 							} else if (codec == 20) {
-								// Codec 20: Uncompressed (FUN_0042C400)
 								smushDecodeUncompressed(frame.pixels, fobjData, 0, 0, width, height, width);
 								frame.valid = true;
 								debugC(DEBUG_INSANE, "Decoded embedded HUD (codec 20/raw): %dx%d", width, height);
 							} else if (codec == 21 || codec == 44) {
-								// Codec 21/44: Line update (FUN_0042BD60)
 								smushDecodeLineUpdate(frame.pixels, fobjData, 0, 0, width, height, width, dataSize);
 								frame.valid = true;
 								debugC(DEBUG_INSANE, "Decoded embedded HUD (codec %d/line update): %dx%d", codec, width, height);
 							} else if (codec == 45) {
-								// Codec 45: blur/wipe mask (FUN_0042B460 -> FUN_0042B530 -> FUN_0042DDF0)
 								smushDecodeRA2Blur(frame.pixels, fobjData, 0, 0, width, height, width, dataSize,
 									_rebelEmbeddedCodec45Palette, _rebelEmbeddedCodec45Lookup);
 								frame.valid = true;
 							} else if (codec == 23) {
-								// Codec 23: Skip/copy with embedded RLE (FUN_0042BBF0)
 								smushDecodeSkipRLE(frame.pixels, fobjData, 0, 0, width, height, width, dataSize);
 								frame.valid = true;
 								debugC(DEBUG_INSANE, "Decoded embedded HUD (codec 23/skip-RLE): %dx%d", width, height);
@@ -452,8 +436,6 @@ void InsaneRebel2::loadEmbeddedSan(int userId, byte *animData, int32 size, byte
 
 // Spawn explosion into the shared 5-slot system.
 // spawnExplosion -- Allocate an explosion slot at the given position.
-// Per-handler slot arrays: 0x26=DAT_0044368e[], 8=DAT_0043f854[],
-// 7=DAT_00443770[], 25=DAT_0045792c[]. All share same logic: find first
 // free slot (counter==0), set counter=10, scale=objectHalfWidth, position=center.
 void InsaneRebel2::spawnExplosion(int x, int y, int objectHalfWidth) {
 	for (int i = 0; i < 5; i++) {
@@ -468,11 +450,9 @@ void InsaneRebel2::spawnExplosion(int x, int y, int objectHalfWidth) {
 	}
 }
 
-// getShotMaxDuration -- Shot duration from per-level difficulty table (DAT_0047e0f0).
 // Used both as initial shot counter AND maxFrames for beam rendering.
 int16 InsaneRebel2::getShotMaxDuration() {
 	LevelDifficultyParams params = getDifficultyParams();
-	// laserDelay = DAT_0047e0f0 field: shot duration in frames
 	// Clamp to reasonable range to avoid division by zero or extreme beams
 	int16 duration = params.laserDelay;
 	if (duration <= 0)
@@ -500,29 +480,25 @@ void InsaneRebel2::spawnShot(int x, int y) {
 	}
 }
 
-// spawnTurretShot -- Handler 0x26 turret shot spawn (FUN_4089AB).
 void InsaneRebel2::spawnTurretShot(int x, int y) {
 	for (int i = 0; i < 2; i++) {
 		if (_turretShots[i].counter == 0) {
-			// FUN_0041189e(-(ushort)(DAT_004436de == 5) & 7, i + 1, 0x7f, 0, 0)
 			// levelType 5: BLAST.SAD (slot 0), otherwise: TBLAST.SAD (slot 7)
 			playSfx((_rebelLevelType == 5) ? 0 : 7, 127, 0);
 
 			_turretShots[i].counter = getShotMaxDuration();
 			_turretShots[i].seqNum = _turretShotSeqCounter;
 			_turretShotSeqCounter++;
-			_turretShots[i].targetX = x + _viewX;  // DAT_0044366e in original
-			_turretShots[i].targetY = y + _viewY;  // DAT_00443670 in original
+			_turretShots[i].targetX = x + _viewX;
+			_turretShots[i].targetY = y + _viewY;
 			break;
 		}
 	}
 }
 
-// spawnVehicleShot -- Handler 8 vehicle shot spawn (FUN_401CCF).
 void InsaneRebel2::spawnVehicleShot(int x, int y) {
 	for (int i = 0; i < 2; i++) {
 		if (_vehicleShots[i].counter == 0) {
-			// FUN_0041189e(6, local_c + 1, 0x7f, 0, 0) ??? HBLAST.SAD
 			playSfx(6, 127, 0);
 			_vehicleShots[i].counter = getShotMaxDuration();
 			_vehicleShots[i].targetX = x;
@@ -532,10 +508,7 @@ void InsaneRebel2::spawnVehicleShot(int x, int y) {
 	}
 }
 
-// spawnHandler25Shot -- Handler 25 on-foot shot spawn (FUN_0041db5e).
 // Gun position from GRD002 offset tables:
-//   DAT_0045791c[i] = gunOffsetTable[spriteIdx] + DAT_00457910 - DAT_0045790c
-//   DAT_00457920[i] = gunYTable[spriteIdx] + DAT_00457912 - DAT_0045790e
 void InsaneRebel2::spawnHandler25Shot(int x, int y) {
 	// Handler 25 can only shoot when uncovered (damage == 0)
 	if (_rebelDamageLevel != 0) {
@@ -544,7 +517,6 @@ void InsaneRebel2::spawnHandler25Shot(int x, int y) {
 
 	for (int i = 0; i < 2; i++) {
 		if (_turretShots[i].counter == 0) {
-			// FUN_0041189e(6, local_1c + 1, 0x7f, 0, 0) ??? HBLAST.SAD
 			playSfx(6, 127, 0);
 
 			_turretShots[i].counter = getShotMaxDuration();
@@ -555,10 +527,6 @@ void InsaneRebel2::spawnHandler25Shot(int x, int y) {
 			_turretShots[i].targetX = x + _viewX;
 			_turretShots[i].targetY = y + _viewY;
 
-			// Compute gun position from original lookup tables.
-			// Original (FUN_41DB5E) stores:
-			//   DAT_0045791c[i] = gunXTable[spriteIdx] + DAT_00457910 - DAT_0045790c
-			//   DAT_00457920[i] = gunYTable[spriteIdx] + DAT_00457912 - DAT_0045790e
 			// where gunXTable/gunYTable are loaded by opcode 8, par4=8.
 			if (_grdShotOriginTableLoaded) {
 				// Compute current sprite index (same logic as renderHandler25Ship)
@@ -598,7 +566,6 @@ void InsaneRebel2::spawnHandler25Shot(int x, int y) {
 				int16 gunXTable = _grdShotOriginX[spriteIdx];
 				int16 gunYTable = _grdShotOriginY[spriteIdx];
 
-				// Mirrored X when DAT_00457902 != 0.
 				if (_rebelFlightDir != 0) {
 					gunXTable = 320 - gunXTable;
 				}
@@ -648,7 +615,6 @@ Common::Point InsaneRebel2::getHandler7ProjectedPoint() {
 
 Common::Point InsaneRebel2::getHandler7ShotTargetPoint() {
 	// Handler 7 targets the projected ship/crosshair point computed in
-	// FUN_0040d836; it does not use the generic mouse position for combat shots.
 	Common::Point projected = getHandler7ProjectedPoint();
 
 	return Common::Point(projected.x + _smoothedVelocity / 2,
@@ -657,16 +623,13 @@ Common::Point InsaneRebel2::getHandler7ShotTargetPoint() {
 
 Common::Point InsaneRebel2::getHandler8ShotTargetPoint() {
 	// Handler 8 stores and draws the shot target from the damped ship
-	// position in FUN_00401ccf, not from the current mouse/analog aim point.
 	return Common::Point(((_shipPosX - 0xa0) >> 3) + 0xa0,
 	                     ((_shipPosY - 0x28) >> 2) + 0x69);
 }
 
-// spawnSpaceShot -- Handler 7 space combat shot spawn (FUN_40D836).
 void InsaneRebel2::spawnSpaceShot(int x, int y) {
 	for (int i = 0; i < 2; i++) {
 		if (_spaceShots[i].counter == 0) {
-			// FUN_0041189e(6, local_2c + 1, 0x7f, 0, 0) ??? HBLAST.SAD
 			playSfx(6, 127, 0);
 
 			_spaceShots[i].counter = getShotMaxDuration();
@@ -677,7 +640,6 @@ void InsaneRebel2::spawnSpaceShot(int x, int y) {
 			_spaceShots[i].targetX = target.x;
 			_spaceShots[i].targetY = target.y;
 
-			// FUN_0040d836 uses muzzle tables loaded by FUN_0040fcfa from opcode
 			// 8 par4=12/13. Values are centered FLY coordinates, adjusted by
 			// the projected ship point before drawing the line.
 			if (_flyLeftGunTableLoaded) {
@@ -752,11 +714,9 @@ void InsaneRebel2::drawTexturedLine(byte *dst, int pitch, int width, int height,
 	}
 }
 
-// drawTexturedSegment -- Textured segment between two points (FUN_00429360 port).
 void drawTexturedSegment(byte *dst, int pitch, int width, int height,
                          int param_3, int param_4, int param_5, int param_6, int param_7, const byte *param_8,
                          int clipLeft, int clipTop, int clipRight, int clipBottom) {
-	// Near-direct port of FUN_00429360.
 	// Only color 0 is transparent.
 	int sVar4 = clipLeft;
 	int sVar1 = clipTop;
@@ -1095,24 +1055,19 @@ void drawTexturedSegment(byte *dst, int pitch, int width, int height,
 }
 
 
-// initLaserTexture -- Pre-render a NUT sprite into the laser texture buffer (FUN_0040BAB0).
 void InsaneRebel2::initLaserTexture(NutRenderer *nut, int spriteIdx) {
 	if (!nut || spriteIdx >= nut->getNumChars())
 		return;
 
-	// Get sprite dimensions (FUN_0040BAB0 lines 13-14)
 	int16 texWidth = nut->getCharWidth(spriteIdx);
 	int16 texHeight = nut->getCharHeight(spriteIdx);
 
-	// Clamp height to max 15 pixels (FUN_0040BAB0 lines 15-17)
 	if (texHeight > 15) {
 		texHeight = 15;
 	}
 
-	// Free existing texture if any (FUN_0040BAB0 lines 18-20)
 	freeLaserTexture();
 
-	// Allocate new buffer (FUN_0040BAB0 line 21)
 	_laserTexture.width = texWidth;
 	_laserTexture.height = texHeight;
 	_laserTexture.pixels = (byte *)calloc(texWidth * texHeight, 1);
@@ -1120,7 +1075,6 @@ void InsaneRebel2::initLaserTexture(NutRenderer *nut, int spriteIdx) {
 	if (!_laserTexture.pixels)
 		return;
 
-	// FUN_0040BAB0 draws the sprite through the normal NUT blitter (FUN_004236e0),
 	// so we must honor x/y offsets and transparency, not just memcpy glyph rows.
 	const byte *srcData = nut->getCharData(spriteIdx);
 	const int srcWidth = nut->getCharWidth(spriteIdx);
@@ -1141,7 +1095,6 @@ void InsaneRebel2::initLaserTexture(NutRenderer *nut, int spriteIdx) {
 					continue;
 
 				byte px = srcRow[sx];
-				// FUN_00429360 (beam raster) only treats 0 as transparent.
 				// Keep 231 pixels from the source texture to avoid dropping beam sub-segments.
 				if (px != 0) {
 					dstRow[dx] = px;
@@ -1154,7 +1107,6 @@ void InsaneRebel2::initLaserTexture(NutRenderer *nut, int spriteIdx) {
 	      texWidth, texHeight, spriteIdx, srcXOff, srcYOff, srcWidth, srcHeight);
 }
 
-// freeLaserTexture -- Emulates FUN_0040BBD1.
 void InsaneRebel2::freeLaserTexture() {
 	free(_laserTexture.pixels);
 	_laserTexture.pixels = nullptr;
@@ -1162,16 +1114,11 @@ void InsaneRebel2::freeLaserTexture() {
 	_laserTexture.height = 0;
 }
 
-//
-// initEdgeTable -- Initialize edge blend table (FUN_410510).
-//
 // When data is nullptr, fills with the default table:
 //   _edgeTable[a*256+b] = min(a,b) (symmetric identity blend)
 // When data is non-null, loads the primary table from data+8 (upper triangle, symmetric).
-//
 void InsaneRebel2::initEdgeTable(const byte *data) {
 	if (data == nullptr) {
-		// Default table initialization (FUN_410510 param_1==NULL path, lines 12-36)
 		for (int a = 0; a < 256; a++) {
 			for (int b = a; b < 256; b++) {
 				// Primary table: table[a][b] = a (i.e. min(a,b) since b >= a)
@@ -1179,12 +1126,10 @@ void InsaneRebel2::initEdgeTable(const byte *data) {
 				_edgeTable[b + a * 256] = (byte)a;
 			}
 		}
-		// Special entries (FUN_410510 lines 33-36)
-		_edgeTable[0x42 * 256 + 0xf1] = 0x42;   // DAT_00447ff1
-		_edgeTable[0x42 + 0xf0 * 256] = 0x42;   // DAT_004480f0 (symmetric)
-		_edgeTable[0x41 * 256 + 0xb0] = 0x41;   // DAT_00447fb0
+		_edgeTable[0x42 * 256 + 0xf1] = 0x42;
+		_edgeTable[0x42 + 0xf0 * 256] = 0x42;
+		_edgeTable[0x41 * 256 + 0xb0] = 0x41;
 	} else {
-		// Load table from IACT data (FUN_410510 non-NULL path, lines 39-47)
 		// Data format: 8-byte header + upper triangle of 256x256 symmetric table
 		const byte *src = data + 8;
 		for (int a = 0; a < 256; a++) {
@@ -1197,22 +1142,14 @@ void InsaneRebel2::initEdgeTable(const byte *data) {
 	}
 }
 
-//
-// drawEdgeHighlightLine -- Edge highlight line using the blend table (FUN_410962).
 // For each pixel along the line, reads the two adjacent pixels perpendicular to
 // the line direction and uses _edgeTable[above*256+below] as the output color.
 // This creates a glow/blend effect at beam edges.
-//
 // For horizontal-dominant lines (dx > dy), reads pixels above and below.
 // For vertical-dominant lines (dy > dx), reads pixels left and right.
-//
-// param_1 = dst buffer info (base pointer at [0], pitch at [1], width at word [1], height at byte offset 6)
-// param_2 = clip rect (or NULL for full buffer)
-// param_3..param_6 = x0, y0, x1, y1 line endpoints
 void InsaneRebel2::drawEdgeHighlightLine(byte *dst, int pitch, int width, int height,
                                           int16 x0, int16 y0, int16 x1, int16 y1,
                                           int16 clipLeftIn, int16 clipTopIn, int16 clipRightIn, int16 clipBottomIn) {
-	// Clip region (FUN_410962 lines 19-30). Clip is provided by caller (gameplay viewport).
 	int16 clipLeft = CLIP<int16>(clipLeftIn, 1, width - 2);
 	int16 clipTop = CLIP<int16>(clipTopIn, 1, height - 2);
 	int16 clipRight = CLIP<int16>(clipRightIn, 1, width - 2);
@@ -1220,7 +1157,6 @@ void InsaneRebel2::drawEdgeHighlightLine(byte *dst, int pitch, int width, int he
 	if (clipLeft > clipRight || clipTop > clipBottom)
 		return;
 
-	// Clip X endpoints (FUN_410962 lines 35-69)
 	if (x0 == x1) {
 		if (x0 < clipLeft || x0 > clipRight)
 			return;
@@ -1245,7 +1181,6 @@ void InsaneRebel2::drawEdgeHighlightLine(byte *dst, int pitch, int width, int he
 		}
 	}
 
-	// Clip Y endpoints (FUN_410962 lines 71-106)
 	if (y0 == y1) {
 		if (y0 < clipTop || y0 > clipBottom)
 			return;
@@ -1270,12 +1205,10 @@ void InsaneRebel2::drawEdgeHighlightLine(byte *dst, int pitch, int width, int he
 		}
 	}
 
-	// Calculate starting pixel address and deltas (FUN_410962 lines 107-110)
 	byte *pixel = dst + y0 * pitch + x0;
 	int16 dx = x1 - x0;
 	int16 dy = y1 - y0;
 
-	// Bresenham line with perpendicular neighbor lookup (FUN_410962 lines 111-270)
 	// The key insight: for each pixel, the blend reads neighbors PERPENDICULAR to the line.
 	// - Horizontal lines: blend pixel_above * 256 + pixel_below
 	// - Vertical lines: blend pixel_left * 256 + pixel_right (reversed: left=[-1], right=[+1])
@@ -1315,7 +1248,6 @@ void InsaneRebel2::drawEdgeHighlightLine(byte *dst, int pitch, int width, int he
 			}
 		}
 	} else if (dx < 0 || dy < 0) {
-		// Mixed negative direction cases (FUN_410962 lines 149-240)
 		if (dy < 0) {
 			if (dx < 0) {
 				// Both negative: going up-left
@@ -1413,7 +1345,6 @@ void InsaneRebel2::drawEdgeHighlightLine(byte *dst, int pitch, int width, int he
 			}
 		}
 	} else {
-		// Both positive: going right-down (FUN_410962 lines 242-270)
 		if (dy < dx) {
 			// X-major: read above/below
 			int err = dx >> 1;
@@ -1446,22 +1377,10 @@ void InsaneRebel2::drawEdgeHighlightLine(byte *dst, int pitch, int width, int he
 	}
 }
 
-//
-// drawLaserBeam -- Laser beam using pre-initialized texture (FUN_0040BBF6).
-//
 // Two-layer rendering:
 //   Layer 1: Textured scanlines (beam body) via drawTexturedSegment()
 //   Layer 2: Edge highlights (glow) via drawEdgeHighlightLine(), gated by _rebelDetailMode >= 0
-//
-// Parameters (matching FUN_0040bbf6):
 //   dst, pitch, width, height: destination buffer info
-//   gunX, gunY (param_3, param_4): gun/start position
-//   targetX, targetY (param_5, param_6): target/end position
-//   animFrame (param_7): current animation frame (shot counter)
-//   maxFrames (param_8): max animation frames (shot duration)
-//   widthScale (param_9): width scaling factor for perspective
-//   heightScale (param_10): height/thickness multiplier
-//   thickness (param_11): base line thickness
 void InsaneRebel2::drawLaserBeam(byte *dst, int pitch, int width, int height,
                                   int16 gunX, int16 gunY, int16 targetX, int16 targetY,
                                   int16 animFrame, int16 maxFrames,
@@ -1482,18 +1401,15 @@ void InsaneRebel2::drawLaserBeam(byte *dst, int pitch, int width, int height,
 		targetY = (int16)((targetY - _hiResPresentationViewY) * 2);
 	}
 
-	// FUN_0040BBF6 line 23: sVar7 = (thickness * animFrame * 16) / maxFrames
 	if (maxFrames == 0)
 		maxFrames = 1;
 	int16 sVar7 = (int16)(((int)thickness * (int)animFrame * 16) / (int)maxFrames);
 
-	// FUN_0040BBF6 lines 24-25: Calculate delta with scaling
 	int16 dx = targetX - gunX;
 	int16 dy = targetY - gunY;
 	int16 sVar6 = (int16)(((int)dx * (thickness + 1)) / (int)thickness);
 	int16 sVar1 = (int16)(((int)dy * (thickness + 1)) / (int)thickness);
 
-	// FUN_0040BBF6 lines 26-29: Calculate adjusted start and end points
 	// Start point (closer to gun, adjusted by animation progress)
 	int16 startX = (sVar6 + gunX) - (int16)(((int)sVar6 * 16) / (sVar7 + 16));
 	int16 startY = (sVar1 + gunY) - (int16)(((int)sVar1 * 16) / (sVar7 + 16));
@@ -1501,10 +1417,8 @@ void InsaneRebel2::drawLaserBeam(byte *dst, int pitch, int width, int height,
 	int16 endX = (sVar6 + gunX) - (int16)(((int)sVar6 * 16) / (widthScale + sVar7 + 16));
 	int16 endY = (sVar1 + gunY) - (int16)(((int)sVar1 * 16) / (widthScale + sVar7 + 16));
 
-	// FUN_0040BBF6 line 30: Get texture pixel pointer
 	byte *local_28 = texPixels;
 
-	// Original callers pass a clip rect for the gameplay viewport (excluding status bar).
 	// This preserves texture phase at the viewport edge and avoids visibly "chopped" beams.
 	int clipLeft = renderHiRes ? 0 : CLIP<int>(_viewX, 0, width - 1);
 	int clipTop = renderHiRes ? 0 : CLIP<int>(_viewY, 0, height - 1);
@@ -1521,21 +1435,17 @@ void InsaneRebel2::drawLaserBeam(byte *dst, int pitch, int width, int height,
 		gunX, gunY, targetX, targetY, startX, startY, endX, endY,
 		animFrame, maxFrames, widthScale, heightScale, thickness);
 
-	// FUN_0040BBF6 lines 31-32: Calculate abs differences (FUN_004356e4 = abs)
 	int iVar2 = abs(startY - endY);  // |dy| of beam
 	int iVar3 = abs(startX - endX);  // |dx| of beam
 
-	// FUN_0040BBF6 line 33: Choose rendering path based on beam orientation
 	if (iVar2 < iVar3) {
 		// Mostly horizontal beam - draw vertical scanlines
-		// FUN_0040BBF6 lines 34-37
 		iVar2 = abs(startX - endX);
 		int temp = iVar2 * texH * heightScale;
 		int16 numLines = (int16)((temp >> 3) / texW) + 2;
 		int16 local_24 = -numLines;
 		int16 halfLines = numLines >> 1;
 
-		// FUN_0040BBF6 lines 39-46: Draw parallel textured scanlines (beam body)
 		for (int16 lineIdx = 0; lineIdx < numLines; lineIdx++) {
 			drawTexturedSegment(dst, pitch, width, height,
 			                    startX, (startY - halfLines) + lineIdx,
@@ -1549,7 +1459,6 @@ void InsaneRebel2::drawLaserBeam(byte *dst, int pitch, int width, int height,
 			}
 		}
 
-		// FUN_0040BBF6 lines 47-51: Edge highlights along top and bottom beam edges
 		if (_rebelDetailMode >= 0 && edgeClipLeft <= edgeClipRight && edgeClipTop <= edgeClipBottom) {
 			drawEdgeHighlightLine(dst, pitch, width, height,
 			                      startX, startY - halfLines,
@@ -1562,19 +1471,16 @@ void InsaneRebel2::drawLaserBeam(byte *dst, int pitch, int width, int height,
 		}
 	} else {
 		// Mostly vertical beam - draw horizontal scanlines
-		// FUN_0040BBF6 lines 54-56
 		iVar2 = abs(startY - endY);
 		int16 numLines = (int16)((iVar2 * texH) / texW) + 2;
 		int16 local_24 = -numLines;
 
-		// FUN_0040BBF6 lines 58-60: Clamp to texture height
 		if (texH < numLines) {
 			numLines = texH;
 		}
 
 		int16 halfLines = numLines >> 1;
 
-		// FUN_0040BBF6 lines 61-68: Draw parallel textured scanlines (beam body)
 		for (int16 lineIdx = 0; lineIdx < numLines; lineIdx++) {
 			drawTexturedSegment(dst, pitch, width, height,
 			                    (startX - halfLines) + lineIdx, startY,
@@ -1588,7 +1494,6 @@ void InsaneRebel2::drawLaserBeam(byte *dst, int pitch, int width, int height,
 			}
 		}
 
-		// FUN_0040BBF6 lines 69-73: Edge highlights along left and right beam edges
 		if (_rebelDetailMode >= 0 && edgeClipLeft <= edgeClipRight && edgeClipTop <= edgeClipBottom) {
 			drawEdgeHighlightLine(dst, pitch, width, height,
 			                      startX - halfLines, startY,
@@ -1602,29 +1507,15 @@ void InsaneRebel2::drawLaserBeam(byte *dst, int pitch, int width, int height,
 	}
 
 }
-// ---------------------------------------------------------------------------
 // Collision Zone System
-// ---------------------------------------------------------------------------
 // Level 3 pilot ship obstacle avoidance. Zones are quadrilaterals
-// registered via IACT opcode 5 (FUN_40E35E, FUN_40C3CC).
 
-//
-// registerCollisionZone -- Register a quad zone from IACT opcode 5 (FUN_4092D9 / FUN_4033CF).
-//
 void InsaneRebel2::registerCollisionZone(Common::SeekableReadStream &b, int16 subOpcode, int16 par4) {
-	// IACT Opcode 5 data layout ??? corrected from FUN_4033CF / FUN_4092D9 analysis:
-	//
-	// Original game stores pointer to full IACT data (starting at opcode).
 	// SmushPlayer reads the first 8 bytes as header (code/flags/unknown/userId),
-	// so our stream starts at body[0] (IACT byte offset +8).
-	//
-	// FUN_4092D9 field mapping (byte offsets from stored pointer):
 	//   +0x00: opcode (5) ??? already consumed by SmushPlayer
 	//   +0x02: par2 (sub-opcode) ??? already consumed, passed as parameter
 	//   +0x04: par3 ??? already consumed by SmushPlayer
 	//   +0x06: par4 (userId) ??? filter value for < 1000 test, passed as parameter
-	//   +0x08: body[0] (sVar1) ??? control field 1 (frame check: field2-1 == field1)
-	//   +0x0A: body[1] (sVar2) ??? control field 2
 	//   +0x0C: body[2] ??? vertex 1 X
 	//   +0x0E: body[3] ??? vertex 1 Y
 	//   +0x10: body[4] ??? vertex 2 X
@@ -1660,7 +1551,6 @@ void InsaneRebel2::registerCollisionZone(Common::SeekableReadStream &b, int16 su
 	zone.subOpcode = subOpcode;
 	zone.active = true;
 
-	// Register zone into appropriate table based on sub-opcode
 	if (subOpcode == 0x0D && _primaryZoneCount < kMaxCollisionZones) {
 		_primaryZones[_primaryZoneCount++] = zone;
 		debugC(DEBUG_INSANE, "Registered PRIMARY zone %d: filter=%d fields=[%d,%d] quad=(%d,%d)-(%d,%d)-(%d,%d)-(%d,%d)",
@@ -1677,44 +1567,28 @@ void InsaneRebel2::registerCollisionZone(Common::SeekableReadStream &b, int16 su
 	}
 }
 
-// resetCollisionZones -- Clear zone tables at end of frame (FUN_403240).
 void InsaneRebel2::resetCollisionZones() {
-	// Reset zone counters at end of frame (FUN_403240 equivalent)
 	// This clears the zone tables so they can be rebuilt from the next frame's IACT chunks
 	_primaryZoneCount = 0;
 	_secondaryZoneCount = 0;
 }
 
-//
-// checkCollisionZones -- Per-frame collision test against primary zones (FUN_4092D9).
-//
 void InsaneRebel2::checkCollisionZones(byte *renderBitmap, int pitch, int width, int height, int32 curFrame) {
-	// Per-frame collision checking ??? FUN_4092D9.
 	// Tests aim/ship position against primary collision zone quadrilaterals.
-	//
-	// Original coordinate system:
 	//   Zone vertices are in 424x260 buffer space, centered by subtracting (0xD4=212, 0x82=130).
-	//   Aim position (DAT_00443668/DAT_0044366a) is in centered coords [-52..52, -45..45].
-	//   In FUN_407FCB: DAT_00443668 is a smoothed mouse-derived position.
-	//
 	// For our implementation:
-	//   Map mouse position to centered coords matching the original range.
-	//   Mouse X 0..320 ??? centered X ??? [-52..52] (with smoothing in original)
 	//   Mouse Y 0..200 ??? centered Y ??? [-45..45]
 
 	if (_primaryZoneCount == 0)
 		return;
 
 	// Calculate aim position in centered coordinates.
-	// Handler 0x26 applies the mouse-mode vertical inversion before DAT_0047a7fe
-	// (FUN_407FCB lines 108-123), so this must not use getGameplayAimPoint().
 	const Common::Point aimPos = getGameplayAimPoint();
 	const int rawX = aimPos.x - 160;
 	const int rawY = aimPos.y - 100;
 	int16 aimX = (int16)(rawX * 52 / 160);
 	int16 aimY = (int16)((_optControlsFlipped ? -rawY : rawY) * 45 / 100);
 
-	// Clamp to original ranges (DAT_0047a7fc < 1 path)
 	if (aimX > 0x34)
 		aimX = 0x34;
 	if (aimX < -0x34)
@@ -1736,12 +1610,10 @@ void InsaneRebel2::checkCollisionZones(byte *renderBitmap, int pitch, int width,
 			continue;
 
 		// Filter: only process zones with filterValue < 1000 (par4 from IACT header)
-		// Original: *(short *)(*local_c + 6) < 1000
 		if (zone.filterValue >= 1000)
 			continue;
 
 		// Center zone vertices by subtracting buffer center (0xD4=212, 0x82=130)
-		// Original: sVar4 = x1 - 0xD4, sVar8 = y1 - 0x82, etc.
 		int cx1 = zone.x1 - 0xD4;
 		int cy1 = zone.y1 - 0x82;
 		int cx2 = zone.x2 - 0xD4;
@@ -1752,12 +1624,8 @@ void InsaneRebel2::checkCollisionZones(byte *renderBitmap, int pitch, int width,
 		int cy4 = zone.y4 - 0x82;
 
 		// Frame check: field2 - 1 == field1
-		// Original: sVar2 + -1 == (int)sVar1
 		if (zone.field2 - 1 == zone.field1) {
-			// Point-in-quadrilateral test ??? FUN_4092D9 lines 119-128
 			// Tests if aim position is OUTSIDE the safe corridor (= collision with obstacle).
-			// Original uses 4 edge interpolation tests connected by OR (any failure = collision).
-			//
 			// Edge 1: interpolate Y along top edge (v1???v2) at aim X position
 			//   if aimY < interpolated Y ??? outside top edge ??? collision
 			// Edge 2: interpolate Y along bottom edge (v4???v3) at aim X position
@@ -1792,26 +1660,21 @@ void InsaneRebel2::checkCollisionZones(byte *renderBitmap, int pitch, int width,
 
 			if (collision) {
 				// Collision detected ??? apply damage from collision damage table
-				// Original: DAT_0047a7ec += DAT_0047e0f6[chapter * 0x242 + level * 0x22]
 				int collisionDamage = (dparams.dodgeDamage >= 0) ? dparams.dodgeDamage : 0;
 
 				if (applyPlayerDamage(collisionDamage)) {
 					debugC(DEBUG_INSANE, "COLLISION damage! zone=%d aim=(%d,%d) damage=%d total=%d",
 						i, aimX, aimY, collisionDamage, _playerDamage);
 				}
-				// Visual effect ??? FUN_00420515 (palette flash)
 				if (!_noDamage)
 					initDamageFlash();
-				// TODO: FUN_0041189e sound based on collision direction
 			} else {
 				// Safely passed ??? award score bonus
-				// Original: FUN_0041bf8d(DAT_0047e100[levelIdx])
 				if (dparams.dodgePoints > 0) {
 					addScore(dparams.dodgePoints);
 				}
 			}
 		} else if (warningFrame && zone.field2 - 0x0c < zone.field1) {
-			// FUN_4092D9 near-collision indicators. Easy mode (flags bit 0x08)
 			// draws directional arrows from cockpit icon slots 0x2a..0x2d.
 			// Novice mode (flags bit 0x10) draws a generic indicator from slot 0x36.
 			const int iconChars = _smush_iconsNut->getNumChars();
@@ -1842,22 +1705,15 @@ void InsaneRebel2::checkCollisionZones(byte *renderBitmap, int pitch, int width,
 	}
 }
 
-//
-// checkHandler7CollisionZones -- Handler 7 per-frame collision (FUN_40E35E).
-//
 // Two modes: obstacle collision (secondary zones) and wall/boundary
 // collision (primary zones with per-edge push-back).
-//
 // The helpers in this block are split out of checkHandler7CollisionZones;
-// they are not separate original functions.
-//
 bool InsaneRebel2::isHandler7ShipInsideObstacleZone(const InsaneRebel2::CollisionZone &zone, int margin) {
 	int x1 = zone.x1, y1 = zone.y1;
 	int x2 = zone.x2, y2 = zone.y2;
 	int x3 = zone.x3, y3 = zone.y3;
 	int x4 = zone.x4, y4 = zone.y4;
 
-	// Point-in-quad test (lines 75-89).
 	// Start assuming inside, clear if outside any edge (with margin).
 	bool inside = true;
 
@@ -1900,7 +1756,6 @@ void InsaneRebel2::applyHandler7ObstacleHit(const InsaneRebel2::CollisionZone &z
 	_rebelHitCounter++;
 	if (!_noDamage)
 		initDamageFlash();
-	// Pan based on ship X position relative to screen center.
 	playSfx(1, 127, CLIP((_flyShipScreenX - 212) * 127 / 160, -127, 127));
 	debugC(DEBUG_INSANE, "Handler7 Mode0/2 OBSTACLE HIT zone=%d ship=(%d,%d) damage=%d",
 		zoneIndex, _flyShipScreenX, _flyShipScreenY, collisionDamage);
@@ -1908,7 +1763,6 @@ void InsaneRebel2::applyHandler7ObstacleHit(const InsaneRebel2::CollisionZone &z
 
 void InsaneRebel2::awardHandler7DodgeScore() {
 	// Safely avoided obstacle - award score.
-	// Original: FUN_0041bf8d(DAT_0047e100[levelIdx]).
 	LevelDifficultyParams scoreParams = getDifficultyParams();
 	if (scoreParams.dodgePoints > 0) {
 		addScore(scoreParams.dodgePoints);
@@ -1916,10 +1770,8 @@ void InsaneRebel2::awardHandler7DodgeScore() {
 }
 
 void InsaneRebel2::checkHandler7ObstacleZones(uint16 &warningMask) {
-	// ---- Mode 0/2: Obstacle collision using SECONDARY zones (FUN_403b5b) ----
-	// Original lines 52-132: Point-in-quad test with 15px inward margin.
 	// Inside the quad = collision with obstacle.
-	const int margin = 15;  // local_14 = 0x0f, local_20 = 0x0f
+	const int margin = 15;
 
 	for (int i = 0; i < _secondaryZoneCount; i++) {
 		CollisionZone &zone = _secondaryZones[i];
@@ -1928,25 +1780,21 @@ void InsaneRebel2::checkHandler7ObstacleZones(uint16 &warningMask) {
 
 		bool inside = isHandler7ShipInsideObstacleZone(zone, margin);
 
-		// Frame match: field2 - 1 == field1 (line 90).
 		if (zone.field2 - 1 == zone.field1) {
 			if (inside) {
 				applyHandler7ObstacleHit(zone, i);
-				break;  // Only one collision per frame (original breaks).
+				break;
 			} else {
 				awardHandler7DodgeScore();
 			}
 		}
 
-		// FUN_40E35E line 104: mark near-danger proximity for shadow cue rendering.
-		// Uses the low byte of zone.filterValue (original local_1c) to pick direction bits.
 		if (zone.field2 - 13 < zone.field1) {
 			uint32 bit = 4u << ((byte)zone.filterValue & 0x1f);
 			warningMask = (uint16)(warningMask | (uint16)bit);
 		}
 	}
 
-	// Corridor side proximity (FUN_40E35E lines 127-131).
 	if (_flyShipScreenX < _corridorLeftX + 0x28)
 		warningMask |= 1;
 	if (_corridorRightX - 0x28 < _flyShipScreenX)
@@ -1974,7 +1822,6 @@ void InsaneRebel2::checkHandler7TopBoundary(const InsaneRebel2::CollisionZone &z
 	int x1 = zone.x1, y1 = zone.y1;
 	int x2 = zone.x2, y2 = zone.y2;
 
-	// Top edge: interpolate Y along v1->v2 at shipX (lines 152-166).
 	if (x2 != x1) {
 		int16 edgeY = (int16)((_flyShipScreenX - x1) * (y2 - y1) / (x2 - x1) + y1 + vMargin);
 		if (_flyShipScreenY < edgeY) {
@@ -1999,10 +1846,9 @@ void InsaneRebel2::checkHandler7BottomBoundary(const InsaneRebel2::CollisionZone
 	int x3 = zone.x3, y3 = zone.y3;
 	int x4 = zone.x4, y4 = zone.y4;
 
-	// Bottom edge: interpolate Y along v4->v3 at shipX (lines 167-183).
 	if (x3 != x4) {
 		int16 edgeY = (int16)((_flyShipScreenX - x4) * (y3 - y4) / (x3 - x4) + y4 - vMargin);
-		_corridorBottomY = vMargin + edgeY;  // DAT_00443b10 update
+		_corridorBottomY = vMargin + edgeY;
 		if (edgeY < _flyShipScreenY) {
 			// Ship below bottom wall - push up.
 			const bool damageApplied = applyHandler7WallDamage(wallDamage);
@@ -2025,14 +1871,12 @@ void InsaneRebel2::checkHandler7LeftBoundary(const InsaneRebel2::CollisionZone &
 	int x1 = zone.x1, y1 = zone.y1;
 	int x4 = zone.x4, y4 = zone.y4;
 
-	// Left edge: interpolate X along v1->v4 at shipY (lines 184-199).
 	if (y4 != y1) {
 		int16 edgeX = (int16)((_flyShipScreenY - y1) * (x4 - x1) / (y4 - y1) + x1 + hMargin);
 		if (_flyShipScreenX < edgeX) {
 			// Ship left of left wall - push right.
 			_flyShipScreenX = edgeX;  // Push-back
 
-			// FUN_40E35E resets horizontal history to force immediate rightward correction.
 			resetHandler7HorizontalVelocity(127);
 
 			const bool damageApplied = applyHandler7WallDamage(wallDamage);
@@ -2052,14 +1896,12 @@ void InsaneRebel2::checkHandler7RightBoundary(const InsaneRebel2::CollisionZone
 	int x2 = zone.x2, y2 = zone.y2;
 	int x3 = zone.x3, y3 = zone.y3;
 
-	// Right edge: interpolate X along v2->v3 at shipY (lines 200-215).
 	if (y3 != y2) {
 		int16 edgeX = (int16)((_flyShipScreenY - y2) * (x3 - x2) / (y3 - y2) + x2 - hMargin);
 		if (edgeX < _flyShipScreenX) {
 			// Ship right of right wall - push left.
 			_flyShipScreenX = edgeX;  // Push-back
 
-			// FUN_40E35E resets horizontal history to force immediate leftward correction.
 			resetHandler7HorizontalVelocity(-127);
 
 			const bool damageApplied = applyHandler7WallDamage(wallDamage);
@@ -2076,11 +1918,9 @@ void InsaneRebel2::checkHandler7RightBoundary(const InsaneRebel2::CollisionZone
 }
 
 void InsaneRebel2::checkHandler7BoundaryZones(uint16 &warningMask) {
-	// ---- Mode 1/3: Wall/boundary collision using PRIMARY zones (FUN_403b34) ----
-	// Original lines 133-235: Per-edge interpolation with push-back.
 	// Ship position is clamped to wall boundaries when hitting.
-	int16 hMargin = (_flyControlMode == 1) ? 0x28 : 0x0f;  // local_14
-	const int16 vMargin = 0x0f;  // local_20
+	int16 hMargin = (_flyControlMode == 1) ? 0x28 : 0x0f;
+	const int16 vMargin = 0x0f;
 	LevelDifficultyParams wallParams = getDifficultyParams();
 	int wallDamage = (wallParams.dodgeDamage >= 0) ? wallParams.dodgeDamage : 0;
 
@@ -2097,7 +1937,6 @@ void InsaneRebel2::checkHandler7BoundaryZones(uint16 &warningMask) {
 }
 
 void InsaneRebel2::renderHandler7WarningCues(byte *renderBitmap, int pitch, int width, int height, int32 curFrame, uint16 warningMask) {
-	// FUN_40E35E tail: draw proximity danger shadow cues when enabled by frame/flags.
 	// Note: These are cue sprites (often perceived as "shadows"), not the aiming reticle.
 	LevelDifficultyParams dparams = getDifficultyParams();
 	if ((curFrame & 2) != 0 && (dparams.flags & 8) != 0 && _smush_iconsNut) {
@@ -2123,16 +1962,11 @@ void InsaneRebel2::renderHandler7WarningCues(byte *renderBitmap, int pitch, int
 }
 
 void InsaneRebel2::checkHandler7CollisionZones(byte *renderBitmap, int pitch, int width, int height, int32 curFrame) {
-	// FUN_40E35E - Handler 7 per-frame collision system.
 	// Uses ship position (_flyShipScreenX/_flyShipScreenY) in raw buffer coords.
 	// Two modes depending on _flyControlMode:
 	//   Mode 0/2: Obstacle collision using SECONDARY zones (inside quad = hit)
 	//   Mode 1/3: Wall/boundary collision using PRIMARY zones (per-edge push-back)
 
-	// Note: _hitCooldown is decremented in renderSpaceExplosions (FUN_40F1C5)
-	// to match the original where the decrement happens during rendering.
-	//
-	// local_c in FUN_40E35E: proximity mask for nearby danger-zone shadow cues.
 	// bit 0=left, bit 1=right, bit 2=top, bit 3=bottom
 	uint16 warningMask = 0;
 
@@ -2272,7 +2106,6 @@ static void renderNutSpriteScaledClipped(byte *dst, int pitch, int width, int he
 	}
 }
 
-// renderNutSpriteMirrored -- NUT sprite with optional horizontal flip (FUN_004236e0).
 void InsaneRebel2::renderNutSpriteMirrored(byte *dst, int pitch, int width, int height, int x, int y, NutRenderer *nut, int spriteIdx, bool mirror) {
 	if (!nut || spriteIdx < 0 || spriteIdx >= nut->getNumChars())
 		return;
@@ -2340,9 +2173,6 @@ void InsaneRebel2::updatePostRenderScroll(int width, int height) {
 	}
 
 	if (_rebelHandler == 8) {
-		// Handler 8 follows FUN_00401234/FUN_00401CCF: the camera is applied
-		// through FUN_00424510 before FOBJ decoding, not by scrolling the final
-		// buffer copied by FUN_00424540.
 		_viewX = 0;
 		_viewY = 0;
 		_player->setScrollOffset(0, 0);
@@ -2375,9 +2205,7 @@ void InsaneRebel2::updatePostRenderScroll(int width, int height) {
 		maxScrollY = 0;
 
 	if (_rebelHandler == 7) {
-		// High-detail Handler 7 follows FUN_0041C5C0: the oversized 424x260
 		// flight buffer is presented through a perspective-derived 320x170
-		// gameplay window at source offset (0x34,0x2d) + DAT_00443712/14.
 		// Keep the final crop in that same space so rendered ship/cues and
 		// raw collision zones describe the same tunnel position.
 		const int handler7MaxScrollY = isHiRes() ? MAX<int>(0, height - 170) : maxScrollY;
@@ -2397,8 +2225,6 @@ void InsaneRebel2::updatePostRenderScroll(int width, int height) {
 
 // updatePostRenderDeath -- End gameplay playback when player damage reaches 255.
 void InsaneRebel2::updatePostRenderDeath() {
-	// Original game (FUN_417E53 line 25) exits video playback when
-	// DAT_0047a7ec >= 0xff (damage accumulator reaches 255).
 	// Sync _playerShield from _playerDamage and break out of video on death.
 	if (_rebelHandler != 0) {
 		_playerShield = 255 - _playerDamage;
@@ -2411,10 +2237,6 @@ void InsaneRebel2::updatePostRenderDeath() {
 
 // renderPostRenderMenuCursor -- Draw RA2's software cursor for menu videos.
 void InsaneRebel2::renderPostRenderMenuCursor(byte *renderBitmap, int pitch, int width, int height) {
-	// Original menu stages call FUN_0042a6d0() to show the game's software
-	// cursor object. Slot 0 is initialized by FUN_0042a660() from the cursor
-	// table embedded in RA2WIN95.EXE at VA 0x482f30 (7x10, hotspot 0,0).
-	// FUN_0042a660() passes 1 as the transparent color to FUN_00430380().
 	static const byte kRa2MenuCursor[] = {
 		 0,  0,  1,  1,  1,  1,  1,
 		 0, 15,  0,  1,  1,  1,  1,
@@ -2458,10 +2280,8 @@ bool InsaneRebel2::handlePostRenderMenuModes(byte *renderBitmap, int pitch, int
 	bool pilotSelectMode = (introPlaying && (_gameState == kStatePilotSelect || _gameState == kStateDifficultySelect));
 	bool chapterSelectMode = (introPlaying && _gameState == kStateChapterSelect);
 
-	// Handle pilot selection input and rendering (FUN_00414A41).
 	// This is the pilot/save slot selection screen with centered menu.
 	if (pilotSelectMode) {
-		// Process pilot selection input - emulates FUN_00414A41 input handling.
 		int selection = processLevelSelectInput();
 
 		// Draw pilot selection overlay - centered menu like main menu.
@@ -2479,17 +2299,14 @@ bool InsaneRebel2::handlePostRenderMenuModes(byte *renderBitmap, int pitch, int
 		return true;
 	}
 
-	// Handle chapter selection input and rendering (FUN_00415CF8).
 	// This is the actual level/chapter selection screen with preview and password.
 	if (chapterSelectMode) {
 		// O_LEVEL.SAN provides the background with chapter preview thumbnails.
 		// The FOBJ offset system (set in procPreRendering) scrolls the correct preview
 		// into the preview box area. No black fill needed ??? video frame shows through.
 
-		// Process chapter selection input - emulates FUN_00415CF8 input handling.
 		int selection = processChapterSelectInput();
 
-		// Draw chapter selection overlay - emulates FUN_00415CF8 rendering.
 		drawChapterSelectOverlay(renderBitmap, pitch, width, height);
 		renderPostRenderMenuCursor(renderBitmap, pitch, width, height);
 
@@ -2504,13 +2321,11 @@ bool InsaneRebel2::handlePostRenderMenuModes(byte *renderBitmap, int pitch, int
 		return true;
 	}
 
-	// Handle Top Pilots screen (FUN_00420116).
 	if (introPlaying && _gameState == kStateTopPilots) {
 		drawTopPilotsOverlay(renderBitmap, pitch, width, height);
 		return true;
 	}
 
-	// Handle Options menu (FUN_004167A6).
 	if (introPlaying && _gameState == kStateOptions) {
 		processOptionsInput();
 		drawOptionsOverlay(renderBitmap, pitch, width, height);
@@ -2527,9 +2342,7 @@ bool InsaneRebel2::handlePostRenderMenuModes(byte *renderBitmap, int pitch, int
 		_menuInactivityTimer++;
 
 		// Check for inactivity timeout.
-		// From FUN_004147b2: 300 frames of inactivity returns 0 (exit to intro/attract mode).
 		// At 12fps video rate, 300 frames = ~25 seconds of inactivity.
-		// The original checks: if (local_8 > 299) return 0.
 		if (_menuInactivityTimer > 300) {
 			debugC(DEBUG_INSANE, "Menu inactivity timeout - resuming intro/demo loop");
 			_menuInactivityTimer = 0;
@@ -2559,13 +2372,8 @@ bool InsaneRebel2::handlePostRenderMenuModes(byte *renderBitmap, int pitch, int
 // handlePostRenderIntro -- Hide gameplay HUD for intro/cinematic videos.
 bool InsaneRebel2::handlePostRenderIntro(byte *renderBitmap, int pitch, int width, int height, int32 curFrame) {
 	// During intro/cinematic sequences:
-	// - Hide the mouse cursor (original: ShowCursor(0) at startup in FUN_00420c70)
 	// - Skip all HUD/status bar/crosshair rendering
 	// - Skip mouse input processing (no shooting during intros)
-	//
-	// Original behavior from FUN_00403240:
-	// - if (DAT_0047a814 == 0) { switch(DAT_0047ee84) { ... } }
-	// - DAT_0047ee84 (handler) is only set by IACT opcode 6 during gameplay videos
 	// - Cinematics/intros don't have opcode 6, so handler stays 0
 	// - We use _rebelHandler == 0 as the primary indicator for intro/cinematic mode
 	if (_rebelHandler == 0) {
@@ -2579,7 +2387,6 @@ bool InsaneRebel2::handlePostRenderIntro(byte *renderBitmap, int pitch, int widt
 				  _player->_curVideoFlags, _gameState);
 		}
 
-		// Chapter title text overlay (FUN_004171c5).
 		if (_textOverlayActive)
 			renderTextOverlay(renderBitmap, pitch, width, height, curFrame);
 
@@ -2599,11 +2406,6 @@ bool InsaneRebel2::handlePostRenderIntro(byte *renderBitmap, int pitch, int widt
 
 // updateGameplayDamageEffects -- Apply handler-specific damage visuals.
 void InsaneRebel2::updateGameplayDamageEffects(byte *renderBitmap, int pitch, int width, int height) {
-	// Damage visual effects - handler-specific per original architecture:
-	// Handler 8:    FUN_401CCF line 119 -> FUN_00420754 (palette flash + screen shake)
-	// Handler 0x19: FUN_41DB5E line 192 -> FUN_00420562 (palette flash only, every frame)
-	// Handler 0x26: FUN_4092D9 lines 135/225/237 -> FUN_00420515 trigger + palette flash
-	// Handler 7:    FUN_40E35E -> FUN_00420515 trigger + palette flash
 	if (_rebelHandler == 8) {
 		// Full damage effect: palette flash + screen shake.
 		// Suppressed during autopilot (mode 4) and cutscene (mode 5).
@@ -2618,9 +2420,6 @@ void InsaneRebel2::updateGameplayDamageEffects(byte *renderBitmap, int pitch, in
 
 // updateGameplayDamageRecovery -- Apply RA2's damage auto-reduction.
 void InsaneRebel2::updateGameplayDamageRecovery(int32 curFrame) {
-	// Handler 0x26 (FUN_4089AB), Handler 8 (FUN_401CCF), and Handler 7
-	// (FUN_40D836) decrement DAT_0047a7ec once every 16 frames after
-	// gameplay collision processing. Handler 25's FUN_41DB5E only awards the
 	// timed score tick in the same slot and does not reduce damage.
 	if ((_rebelHandler != 0x26 && _rebelHandler != 8 && _rebelHandler != 7) ||
 			(curFrame & 0xf) != 0 || _playerDamage <= 0) {
@@ -2634,11 +2433,7 @@ void InsaneRebel2::updateGameplayDamageRecovery(int32 curFrame) {
 // checkGameplayPostRenderCollisions -- Run handler-specific collision checks.
 void InsaneRebel2::checkGameplayPostRenderCollisions(byte *renderBitmap, int pitch, int width, int height, int32 curFrame) {
 	// Per-frame collision checking against registered zones.
-	//
-	// Handler 0x26 (turret): FUN_4092D9 - aim position vs primary zones (centered coords)
 	//   Zones with filterValue < 1000 tested via point-in-quad against mouse/aim position.
-	//
-	// Handler 7 (ship): FUN_40E35E - ship position vs zones per control mode:
 	//   Mode 0/2: SECONDARY zones (0x0E) - obstacle collision (inside quad = hit)
 	//   Mode 1/3: PRIMARY zones (0x0D) - wall/boundary per-edge with push-back
 	//   Uses ship position in raw buffer coords, hit cooldown, directional damage.
@@ -2649,12 +2444,10 @@ void InsaneRebel2::checkGameplayPostRenderCollisions(byte *renderBitmap, int pit
 	}
 }
 
-// renderGameplayPostFrame -- Draw the gameplay post-render pipeline in original order.
 void InsaneRebel2::renderGameplayPostFrame(byte *renderBitmap, int pitch, int width, int height,
 										   int videoWidth, int videoHeight, int statusBarY, int32 curFrame) {
 	// From here on, we're in gameplay mode (_rebelHandler != 0).
 	// Process mouse input for shooting.
-	// Original: FUN_00403240 only runs handlers when DAT_0047a814 == 0.
 	processMouse();
 
 	// Handler 7's high-detail flight view uses a 320x170 gameplay area after
@@ -2685,34 +2478,15 @@ void InsaneRebel2::renderGameplayPostFrame(byte *renderBitmap, int pitch, int wi
 	// SMUSH decodes the frame's FOBJ sprites. Handler 25 draws its corridor overlay
 	// from IACT opcode 6 instead. Redrawing either here would overwrite enemies.
 
-	// --- HUD Drawing Order (from FUN_004089ab / FUN_40D836 assembly analysis) ---
-	// Original assembly render order for handler 0x26:
-	//   1. FUN_004288c0: Fill status bar background
-	//   2. FUN_004092d9: Collision/hit processing
-	//   3. FUN_00409fbc: Explosion rendering
-	//   4. FUN_0040ad63: LASER SHOTS (drawn BEFORE cockpit overlays)
-	//   5. FUN_004236e0: Crosshair + cockpit NUT overlays (drawn ON TOP of lasers)
-	//   6. FUN_0041c012: Status bar text/numbers
 	// The cockpit frame covers laser beam edges, giving the appearance
 	// that beams emerge from behind the cockpit.
 
-	// Based on FUN_004089ab:
-	// 1. Line 156: FUN_004288c0 fills status bar background at Y=0xb4 (180)
 	// 2. Lines 171-226: Draw turret overlays, targeting reticle, crosshair
-	// 3. Line 243: FUN_0041c012 draws status bar sprites LAST (on top)
-	//
-	// In FUN_0041c012:
-	// - Sprites are drawn to buffer DAT_00482204 at position (0,0)
-	// - Buffer is composited at Y=0xb4 (180) via FUN_0042f780
-	// - DISPFONT.NUT (DAT_00482200) sprites 1-7 contain the status bar elements
-	//
 	// We draw directly to screen at Y=180.
 
-	// STEP 0: Fill status bar background (FUN_004288c0).
 	renderStatusBarBackground(renderBitmap, pitch, width, height, videoWidth, videoHeight, gameplayStatusBarY);
 
 	// Ship rendering. Handler 7 is drawn later, after its lasers, matching
-	// FUN_0040d836's order so the ship covers the muzzle end of the beams.
 	debugC(DEBUG_INSANE, "Ship Check: handler=%d shipSprite=%p flyShipSprite=%p shipLevelMode=%d numSprites=%d/%d",
 		_rebelHandler, (void*)_shipSprite, (void*)_flyShipSprite, _shipLevelMode,
 		_shipSprite ? _shipSprite->getNumChars() : 0,
@@ -2725,7 +2499,6 @@ void InsaneRebel2::renderGameplayPostFrame(byte *renderBitmap, int pitch, int wi
 	// Enemy target indicators (handler-specific; sprite-based in turret mode).
 	renderEnemyOverlays(renderBitmap, pitch, width, height, videoWidth);
 
-	// Explosion animations (FUN_409FBC) - drawn before lasers in original.
 	renderExplosions(renderBitmap, pitch, width, height);
 
 	// Laser shot beams - drawn BEFORE cockpit/HUD overlays so cockpit covers beam edges.
@@ -2733,57 +2506,42 @@ void InsaneRebel2::renderGameplayPostFrame(byte *renderBitmap, int pitch, int wi
 
 	renderHandler7Ship(renderBitmap, pitch, width, height);
 
-	// Handler 25 GRD sprites drawn AFTER enemies/explosions/lasers per original FUN_0041DB5E:
-	//   Line 193: FUN_0041f29a (enemies)
-	//   Line 194: FUN_0041e7c2 (explosions)
-	//   Line 201: FUN_0041f004 (lasers)
 	//   Lines 202-229: GRD001 (wall, opaque, covers enemies behind wall)
 	//   Lines 230-248: GRD002 (character, transparent, drawn last)
 	renderHandler25ShipPre(renderBitmap, pitch, width, height);
 	renderHandler25Ship(renderBitmap, pitch, width, height);
 
-	// STEP 1A: Draw NUT-based HUD overlays for Handler 0x26 (FUN_004089ab lines 195-226).
 	// These are cockpit frame, crosshair, and reticle - drawn ON TOP of laser beams.
 	renderTurretHudOverlays(renderBitmap, pitch, width, height, curFrame);
 
 	// STEP 1B: Draw embedded SAN HUD overlays (from IACT chunks).
 	renderEmbeddedHudOverlays(renderBitmap, pitch, width, height);
 
-	// STEP 2: Draw DISPFONT.NUT status bar sprites (FUN_0041c012).
 	renderStatusBarSprites(renderBitmap, pitch, width, height, gameplayStatusBarY, curFrame);
 
 	updateGameplayDamageEffects(renderBitmap, pitch, width, height);
 	checkGameplayPostRenderCollisions(renderBitmap, pitch, width, height, curFrame);
 	updateGameplayDamageRecovery(curFrame);
 
-	// Crosshair/reticle (FUN_004089ab, FUN_0040d836).
 	renderCrosshair(renderBitmap, pitch, width, height);
 
-	// Handler 8 monitor scanline effect (FUN_0041C6EC/FUN_0041C6C3).
 	renderHandler8MonitorEffect(renderBitmap, pitch, width, height);
 
-	// Handler 8 POV text overlay (FUN_00401CCF).
 	renderHandler8PovOverlay(renderBitmap, pitch, width, height);
 
-	// HUD score/lives rendering (FUN_0041c012).
 	renderScoreHUD(renderBitmap, pitch, width, height, gameplayStatusBarY);
 
-	// Reset FOBJ position offsets (FUN_00424510(0,0) in original FUN_0041DB5E line 271).
 	if (_player) {
 		_player->_fobjOffsetX = 0;
 		_player->_fobjOffsetY = 0;
 	}
 
-	// Frame end cleanup: reset enemy active flags and collision zones (FUN_403240).
 	frameEndCleanup();
 }
 
-//
 // procPostRendering -- Post-frame rendering: HUD, ships, enemies, effects, status bar.
-//
 // Called after FOBJ decoding. Dispatches to per-handler rendering functions
 // for ship sprites, laser shots, explosions, crosshair, and damage effects.
-//
 void InsaneRebel2::procPostRendering(byte *renderBitmap, int32 codecparam, int32 setupsan12,
 							   int32 setupsan13, int32 curFrame, int32 maxFrame) {
 
@@ -2843,13 +2601,9 @@ void InsaneRebel2::procPostRendering(byte *renderBitmap, int32 codecparam, int32
 	renderGameplayPostFrame(renderBitmap, pitch, width, height, videoWidth, videoHeight, statusBarY, curFrame);
 }
 
-// ---------------------------------------------------------------------------
 // Damage Visual Effect Functions
-// ---------------------------------------------------------------------------
 // Palette flash + screen shake when the player takes damage.
-// FUN_420515, FUN_420562, FUN_420754, FUN_42073B, FUN_420501.
 
-// resetDamageFlash -- Reset palette flash counter (FUN_00420501).
 void InsaneRebel2::resetDamageFlash() {
 	_damageFlashCounter = 0;
 }
@@ -2868,7 +2622,6 @@ void InsaneRebel2::restoreDamageFlashPalette() {
 	_damageRestorePaletteValid = false;
 }
 
-// initDamageFlash -- Save palette and initiate 5-frame flash (FUN_00420515).
 void InsaneRebel2::initDamageFlash() {
 	if (!_damageRestorePaletteValid && _player) {
 		memcpy(_damageRestorePalette, _player->_pal, 0x300);
@@ -2882,22 +2635,15 @@ void InsaneRebel2::initDamageFlash() {
 	_damageFlashCounter = 5;
 }
 
-// triggerDamageEffect -- Trigger palette flash and screen shake (FUN_0042073B).
 void InsaneRebel2::triggerDamageEffect() {
 	initDamageFlash();
 	_damageShakeCounter = 10;
 }
 
-//
-// updateDamageFlashPalette -- Per-frame palette modification (FUN_00420562).
-//
 // Normal hit flash (_damageHighFlashCounter == 0 or odd):
 //   Blend formula: output[i] = 0xFF - ((0xFF - saved[i]) * (0x10 - counter)) >> 4
-//   Counter 5->4(apply)->3(skip)->2(apply)->1(skip)->0(apply=original) = strobe.
-//
 // High-damage red pulse (_playerDamage >= 0xFF, even counter):
 //   R channel only (every 3rd byte) using same formula with _damageHighFlashCounter.
-//
 void InsaneRebel2::updateDamageFlashPalette() {
 	// High-damage mode: persistent red pulsing when damage is maxed out
 	if (_playerDamage < 0xFF) {
@@ -2918,7 +2664,6 @@ void InsaneRebel2::updateDamageFlashPalette() {
 
 	if (_damageHighFlashCounter == 0 || (_damageHighFlashCounter & 1) != 0) {
 		// Normal hit flash path: decrement counter, apply on even values.
-		// Original C: if ((counter != 0) && (counter--, (counter & 1) == 0))
 		if (_damageFlashCounter != 0) {
 			_damageFlashCounter--;
 			if ((_damageFlashCounter & 1) == 0) {
@@ -2946,15 +2691,12 @@ void InsaneRebel2::updateDamageFlashPalette() {
 	}
 }
 
-// updateDamageEffect -- Per-frame screen shake + palette flash (FUN_00420754).
 // Shifts counter*5 random scanlines per frame, diminishing over 10 frames.
-// Only called when not in cutscene modes (DAT_0043e000 != 4 && != 5).
 void InsaneRebel2::updateDamageEffect(byte *renderBitmap, int pitch, int width, int height) {
 	if (_damageShakeCounter != 0) {
 		_damageShakeCounter--;
 		int numLines = _damageShakeCounter * 5;
 
-		// Temporary buffer for scanline rotation (case 1 in original)
 		byte tempLine[640];
 		const bool renderHiRes = isHiRes() && width >= 640 && height >= 400;
 		const int maxY = MIN(height, renderHiRes ? 360 : 180);
@@ -2997,14 +2739,10 @@ void InsaneRebel2::updateDamageEffect(byte *renderBitmap, int pitch, int width,
 	updateDamageFlashPalette();
 }
 
-// ---------------------------------------------------------------------------
 // Rendering Helper Functions
-// ---------------------------------------------------------------------------
 // Extracted from procPostRendering for better readability.
 
-// renderTextOverlay -- Progressive chapter title overlay (FUN_004171c5).
 void InsaneRebel2::renderTextOverlay(byte *renderBitmap, int pitch, int width, int height, int curFrame) {
-	// Emulates FUN_004171c5 text overlay: progressive chapter title during [fadeIn, fadeOut)
 	if (curFrame < _textOverlayFadeIn || curFrame >= _textOverlayFadeOut)
 		return;
 
@@ -3031,7 +2769,6 @@ void InsaneRebel2::renderTextOverlay(byte *renderBitmap, int pitch, int width, i
 
 	Common::Rect clipRect(0, 0, width, height);
 
-	// Split into lines, then render each centered at textX (FUN_004341a0).
 	// Older RA2 text loading joined multi-line strings with spaces, leaving
 	// " ^f" as the separator; current TRES loading preserves real newlines.
 	Common::Array<Common::String> lines;
@@ -3122,11 +2859,8 @@ void InsaneRebel2::renderTextOverlay(byte *renderBitmap, int pitch, int width, i
 	}
 }
 
-// renderStatusBarBackground -- Fill status bar area with color 4 (FUN_004288c0).
 void InsaneRebel2::renderStatusBarBackground(byte *renderBitmap, int pitch, int width, int height,
 											 int videoWidth, int videoHeight, int statusBarY) {
-	// Fill status bar background (FUN_004288c0 equivalent)
-	// Original assembly: FUN_004288c0(local_8, 0, 0, 0xb4, 0x140, 0x14, 4)
 	// This fills width=320, height=20 starting at Y=180 with color index 4
 	const byte statusBarBgColor = 4;
 
@@ -3143,11 +2877,8 @@ void InsaneRebel2::renderStatusBarBackground(byte *renderBitmap, int pitch, int
 	}
 }
 
-// renderTurretHudOverlays -- NUT-based HUD for Handler 0x26 (FUN_004089ab).
 void InsaneRebel2::renderTurretHudOverlays(byte *renderBitmap, int pitch, int width, int height, int32 curFrame) {
 	// Draw NUT-based HUD overlays for Handler 0x26 (turret modes)
-	// From FUN_004089ab disassembly (lines 195-226):
-	// - DAT_0047fe78 (_hudOverlayNut): Primary HUD overlay with 6 animation frames
 	// - Position formula (low-res):
 	//   X = 160 + (mouseOffsetX >> 4) - (width / 2) - spriteOffsetX
 	//   Y = 182 - (mouseOffsetY >> 4) - height - spriteOffsetY
@@ -3179,8 +2910,6 @@ void InsaneRebel2::renderTurretHudOverlays(byte *renderBitmap, int pitch, int wi
 
 	const int hudScale = isHiRes() ? 2 : getRebel2IndicatorScale(width, height);
 
-	// FUN_004089ab computes the moving overlay anchor from sprite 0's dimensions
-	// and offsets, then FUN_004236e0 applies each rendered frame's own offsets.
 	const int baseSpriteW = _hudOverlayNut->getCharWidth(0);
 	const int baseSpriteH = _hudOverlayNut->getCharHeight(0);
 	const int baseSpriteXOff = _hudOverlayNut->getCharXOffset(0);
@@ -3210,7 +2939,6 @@ void InsaneRebel2::renderTurretHudOverlays(byte *renderBitmap, int pitch, int wi
 	debugC(DEBUG_INSANE, "HUD: Drawing NUT overlay frame %d/%d at (%d,%d) mouseOffset=(%d,%d)",
 		  animFrame, numSprites, hudX, hudY, mouseOffsetX, mouseOffsetY);
 
-	// Draw secondary HUD overlay if present (DAT_0047fe80)
 	if (_hudOverlay2Nut && _hudOverlay2Nut->getNumChars() > 0) {
 		int spr2W = _hudOverlay2Nut->getCharWidth(0);
 		int spr2H = _hudOverlay2Nut->getCharHeight(0);
@@ -3232,7 +2960,6 @@ void InsaneRebel2::renderEmbeddedHudOverlays(byte *renderBitmap, int pitch, int
 			continue;
 
 		// Handler 25: skip slot 4 (corridor overlay) in post-rendering.
-		// FUN_0041cadb draws it opaquely from opcode 6 and immediately after
 		// loading par4=4; drawing it here would cover enemies.
 		if (_rebelHandler == 25 && hudSlot == 4) {
 			continue;
@@ -3312,10 +3039,8 @@ void InsaneRebel2::renderEmbeddedHudOverlays(byte *renderBitmap, int pitch, int
 	}
 }
 
-// renderStatusBarSprites -- DISPFONT.NUT status bar rendering (FUN_0041c012).
 void InsaneRebel2::renderStatusBarSprites(byte *renderBitmap, int pitch, int width, int height,
 										  int statusBarY, int32 curFrame) {
-	// FUN_0041c012 equivalent ??? renders DISPFONT.NUT status bar sprites.
 	// DISPFONT.NUT sprite layout:
 	//   Sprite 1:   Status bar background
 	//   Sprites 2-5: Difficulty variants (full status bar with 1-4 stars)
@@ -3328,14 +3053,11 @@ void InsaneRebel2::renderStatusBarSprites(byte *renderBitmap, int pitch, int wid
 	int numSprites = _smush_cockpitNut->getNumChars();
 	const int statusScale = isHiRes() ? 2 : getRebel2IndicatorScale(width, height);
 
-	// --- Sprite 1: Status bar background (always drawn first as base layer) ---
 	if (numSprites > 1) {
 		renderNutSprite(renderBitmap, pitch, width, height,
 			_viewX, statusBarY + _viewY, _smush_cockpitNut, 1);
 	}
 
-	// --- Difficulty sprite (2-5) overlaid on top ---
-	// FUN_0041c012 lines 33-43: sprite index = min(difficulty, 4) + 1
 	int difficulty = _difficulty;
 	if (difficulty > 3)
 		difficulty = 3;
@@ -3345,13 +3067,10 @@ void InsaneRebel2::renderStatusBarSprites(byte *renderBitmap, int pitch, int wid
 			_viewX, statusBarY + _viewY, _smush_cockpitNut, difficultySprite);
 	}
 
-	// --- Damage/shield bar (sprite 6 within damage clip rect) ---
-	// FUN_0041c012 lines 44-76:
 	//   Clip rect (low-res): {X=0x3f, Y=9, W=0x40, H=6} = {63, 9, 64, 6}
 	//   Bar width = shield_value >> 2 (divide by 4, range 0-63)
 	//   If shield > 0xAA (170): alert blink with sprite 7
 	if (numSprites > 6) {
-		// Bar width from assembly: param_1 >> 2 (low-res)
 		int damageBarWidth = (_playerDamage * statusScale) >> 2;
 
 		const byte *src = _smush_cockpitNut->getCharData(6);
@@ -3384,7 +3103,6 @@ void InsaneRebel2::renderStatusBarSprites(byte *renderBitmap, int pitch, int wid
 			}
 		}
 
-		// Damage alert overlay (sprite 7) ??? FUN_0041c012 lines 68-76:
 		// When damage > 0xAA (170) and frame counter bit 3 is clear, draw sprite 7
 		// at full clip rect width (64 pixels) to show flashing alert
 		if (numSprites > 7 && _playerDamage > 170 && ((curFrame & 8) == 0)) {
@@ -3418,8 +3136,6 @@ void InsaneRebel2::renderStatusBarSprites(byte *renderBitmap, int pitch, int wid
 		}
 	}
 
-	// --- Lives bar (sprite 6 within lives clip rect) ---
-	// FUN_0041c012 lines 99-131:
 	//   Clip rect (low-res): {X=0xa8, Y=7, W=0x32, H=9} = {168, 7, 50, 9}
 	//   Bar width = min((lives * 5 - 5) * 2, 50) ??? only drawn when lives > 1
 	if (numSprites > 6 && _playerLives > 1) {
@@ -3476,13 +3192,9 @@ void InsaneRebel2::renderHandler7FlySprite(byte *renderBitmap, int pitch, int wi
 	}
 }
 
-// renderHandler7Ship -- Handler 7 third-person ship rendering (FUN_0040d836).
 void InsaneRebel2::renderHandler7Ship(byte *renderBitmap, int pitch, int width, int height) {
 	// Handler 7 Ship Rendering (Third-Person Ship - FLY sprites)
-	// Based on FUN_0040d836 lines 173-185:
-	//   FUN_004236e0(buf, frameInfo, screenX - 0xd4, screenY - 0x82, 0, sprite, frameIdx, 1, 0)
 	// The ship sprite is drawn at the perspective-transformed position offset from center.
-	// FUN_0041c720 transforms game coords (shipX, shipY) using perspective offsets.
 
 	if (_rebelHandler != 7 || !_flyShipSprite || _shipLevelMode == 5)
 		return;
@@ -3514,7 +3226,6 @@ void InsaneRebel2::renderHandler7Ship(byte *renderBitmap, int pitch, int width,
 	int shipCenterX = projected.x;
 	int shipCenterY = projected.y;
 
-	// FUN_40D836 lines 108-136: FLY002 proximity cues near corridor danger.
 	if (_flyLaserSprite && _flyLaserSprite->getNumChars() > 0) {
 		const int laserChars = _flyLaserSprite->getNumChars();
 		_flyEffectAnimCounter++;
@@ -3583,7 +3294,6 @@ void InsaneRebel2::renderHandler7Ship(byte *renderBitmap, int pitch, int width,
 		renderHiRes, renderScale, nativeViewX, nativeViewY,
 		shipDraw.x, shipDraw.y, _flyShipSprite, spriteIndex);
 
-	// FUN_40D836 lines 176-180: optional FLY002 overlay pass at ship position.
 	if (_flyLaserSprite && _flyOverlayRepeatCount > 0) {
 		int overlayIndex = spriteIndex + 0x14;
 		if (overlayIndex >= 0 && overlayIndex < _flyLaserSprite->getNumChars()) {
@@ -3595,7 +3305,6 @@ void InsaneRebel2::renderHandler7Ship(byte *renderBitmap, int pitch, int width,
 		}
 	}
 
-	// FUN_40D836 lines 181-183: optional FLY003 overlay in high detail mode.
 	if (_flyTargetSprite && _rebelDetailMode >= 0 &&
 		spriteIndex >= 0 && spriteIndex < _flyTargetSprite->getNumChars()) {
 		renderHandler7FlySprite(renderBitmap, pitch, width, height,
@@ -3609,35 +3318,25 @@ void InsaneRebel2::renderHandler7Ship(byte *renderBitmap, int pitch, int width,
 		_perspectiveX, _perspectiveY, _smoothedVelocity, _verticalInput, _flyEffectAnimCounter, _flyOverlayRepeatCount);
 }
 
-// renderHandler8Ship -- Handler 8 third-person on-foot rendering (FUN_00401CCF).
 void InsaneRebel2::renderHandler8Ship(byte *renderBitmap, int pitch, int width, int height) {
 	// Handler 8 Ship Rendering (Third-Person On Foot - POV sprites)
 	// Uses _shipSprite (POV001) with position-based offset
-	//
-	// Original FUN_00401CCF lines 87-94:
-	//   FUN_004236e0(bitmap, param_2,
 	//       (short)(shipPosX - 0xa0) >> 3,    // small X offset
 	//       (short)(shipPosY - 0x28) >> 2,    // small Y offset
-	//       0, DAT_0047e010, param_5 & 1, 1, 0);
-	//
-	// FUN_004236e0 adds the NUT sprite's internal X/Y offsets to the position
 	// parameters. The sprite's built-in offsets encode where it should appear
 	// on screen (e.g., center for Level 2/11, bottom for Level 12 FPS gun).
 
 	if (_rebelHandler != 8 || !_shipSprite || _shipLevelMode == 5)
 		return;
 
-	// Small position offsets from dampened ship movement (FUN_00401ccf lines 88-89)
 	int16 displayOffsetX = (_shipPosX - 0xa0) >> 3;
 	int16 displayOffsetY = (_shipPosY - 0x28) >> 2;
 
 	int numSprites = _shipSprite->getNumChars();
-	// Original uses param_5 & 1 (firing flag) as sprite index ??? NOT direction-based
 	int spriteIndex = _shipFiring ? 1 : 0;
 	if (spriteIndex >= numSprites)
 		spriteIndex = 0;
 
-	// FUN_004236e0 adds sprite internal offsets to the x/y parameters.
 	// The internal offsets position the sprite correctly for each level type
 	// (centered for Level 2/11 third-person, bottom for Level 12 FPS).
 	int16 spriteXOffset = _shipSprite->getCharXOffset(spriteIndex);
@@ -3656,8 +3355,6 @@ void InsaneRebel2::renderHandler8Ship(byte *renderBitmap, int pitch, int width,
 		renderNutSprite(renderBitmap, pitch, width, height, drawX, drawY, _shipSprite, spriteIndex);
 	}
 
-	// Shadow sprite (POV004 / DAT_0047e028): drawn at same position as primary ship.
-	// Original FUN_401CCF lines 91-92 uses param_5 & 1 (firing flag) as sprite index.
 	if (_shipSprite2) {
 		int shadowIndex = _shipFiring ? 1 : 0;
 		if (shadowIndex < _shipSprite2->getNumChars()) {
@@ -3684,7 +3381,6 @@ void InsaneRebel2::renderHandler8Ship(byte *renderBitmap, int pitch, int width,
 		_viewX, _viewY, spriteIndex, numSprites, renderScale);
 }
 
-// renderVehicleShotImpacts -- Handler 8 shot impact sprites (FUN_402DA8).
 void InsaneRebel2::renderVehicleShotImpacts(byte *renderBitmap, int pitch, int width, int height) {
 	if (_rebelHandler != 8)
 		return;
@@ -3703,8 +3399,6 @@ void InsaneRebel2::renderVehicleShotImpacts(byte *renderBitmap, int pitch, int w
 			drawY = (drawY - _hiResPresentationViewY) * 2;
 		}
 
-		// Original draws DAT_0047e020 repeatedly based on remaining life, then
-		// DAT_0047e018 once, both using the sampled background-mask sprite index.
 		if (_shipOverlay1 && impact.spriteIndex >= 0 && impact.spriteIndex < _shipOverlay1->getNumChars()) {
 			int spriteX = drawX + _shipOverlay1->getCharXOffset(impact.spriteIndex) -
 				_shipOverlay1->getCharWidth(impact.spriteIndex) / 2;
@@ -3726,7 +3420,6 @@ void InsaneRebel2::renderVehicleShotImpacts(byte *renderBitmap, int pitch, int w
 }
 
 // Handler 25: Draw GRD001 (wall/cockpit overlay) in procPostRendering.
-// renderHandler25ShipPre -- Handler 25 GRD001 pre-rendering (FUN_0041DB5E lines 202-221).
 // GRD sprites drawn AFTER FOBJ enemies, before GRD002. Mode-based clipping:
 //   Mode 1, damage==0: left half only (pixels 0-159)
 //   Mode 4, damage==0: right half only (pixels 160-319)
@@ -3749,26 +3442,20 @@ void InsaneRebel2::renderHandler25ShipPre(byte *renderBitmap, int pitch, int wid
 	const int nativeBufferViewY = renderHiRes ? nativeViewY : _viewY;
 	int renderHeight = renderHiRes ? MIN(height, 180 * renderScale) : MIN(height, 180 + _viewY);
 
-	// Draw _grd001Sprite based on _grdSpriteMode (DAT_00457900)
-	// Each mode has specific conditions from FUN_0041db5e:
 	bool shouldDraw = false;
 	bool useHalfWidth = false;
 	bool useRightHalf = false;
 
-	// Mode 1 (lines 202-210): Draw with width halving when uncovered
 	if (_grdSpriteMode == 1) {
 		shouldDraw = true;
 		useHalfWidth = (_rebelDamageLevel == 0);  // Half width when uncovered
 	}
-	// Mode 2 (lines 222-224): Only draw when damaged (covered)
 	else if (_grdSpriteMode == 2 && _rebelDamageLevel != 0) {
 		shouldDraw = true;
 	}
-	// Mode 3 (lines 225-228): Always draw full width
 	else if (_grdSpriteMode == 3) {
 		shouldDraw = true;
 	}
-	// Mode 4 (lines 211-221): Draw to right half when uncovered
 	else if (_grdSpriteMode == 4) {
 		shouldDraw = true;
 		useHalfWidth = (_rebelDamageLevel == 0);
@@ -3790,7 +3477,6 @@ void InsaneRebel2::renderHandler25ShipPre(byte *renderBitmap, int pitch, int wid
 		int drawX = renderHiRes ? (nativeDrawX - nativeViewX) * renderScale : nativeDrawX;
 		int drawY = renderHiRes ? (nativeDrawY - nativeViewY) * renderScale : nativeDrawY;
 
-		// Apply half-width clipping from FUN_41DB5E:
 		// - mode1 uncovered: left half
 		// - mode4 uncovered: right half
 		int clipLeft = 0;
@@ -3803,8 +3489,6 @@ void InsaneRebel2::renderHandler25ShipPre(byte *renderBitmap, int pitch, int wid
 		int scaledClipLeft = clipLeft * renderScale;
 		int scaledClipRight = clipRight * renderScale;
 
-		// FUN_41DB5E mode-4 uncovered mutates DAT_00482230/34 (clip region),
-		// not DAT_00457910 (draw X). Keep drawX unchanged and clip only.
 		renderNutSpriteScaledClipped(renderBitmap, pitch, width, renderHeight,
 			scaledClipLeft, 0, scaledClipRight, renderHeight,
 			drawX, drawY, _grd001Sprite, 0, false, renderScale, false);
@@ -3836,12 +3520,9 @@ void InsaneRebel2::renderHandler25ShipPre(byte *renderBitmap, int pitch, int wid
 	}
 }
 
-// renderHandler25Ship -- Handler 25 GRD002 post-rendering (FUN_0041db5e).
 void InsaneRebel2::renderHandler25Ship(byte *renderBitmap, int pitch, int width, int height) {
 	// Handler 25 POST-rendering: Draw GRD002 (character sprite) on top of enemies.
 	// GRD001 (wall/cockpit) is drawn before this via renderHandler25ShipPre().
-	//
-	// From FUN_0041db5e disassembly (lines 230-248):
 	// GRD002 is drawn LAST (after enemies) so the character appears in front.
 
 	if (_rebelHandler != 25)
@@ -3856,27 +3537,20 @@ void InsaneRebel2::renderHandler25Ship(byte *renderBitmap, int pitch, int width,
 	const int nativeBufferViewY = renderHiRes ? nativeViewY : _viewY;
 	int renderHeight = renderHiRes ? MIN(height, 180 * renderScale) : MIN(height, 180 + _viewY);
 
-	// _grd002Sprite (GRD002) is always drawn if it exists (from FUN_41DB5E line 230)
-	// The sprite index is calculated based on damage level and aiming position
-	// From FUN_0041db5e lines 160-168:
 	//   If damage == 0: index = yZone * 5 + xZone + 5 (aiming-based, 5-14)
 	//   If damage != 0:
 	//     If direction == 0: index = 5 - damage (0-5, covered left)
 	//     If direction != 0: index = 25 - damage (20-25, covered right)
 	if (_grd002Sprite && _grd002Sprite->getNumChars() > 0) {
-		// Calculate sprite index based on damage level and direction
 		int spriteIdx;
 		int numSprites = _grd002Sprite->getNumChars();
 
-		// Determine if we should mirror the sprite (from FUN_41DB5E lines 231-235)
 		// Mirror when: direction != 0 AND damage == 0 (fully uncovered, facing right)
 		bool shouldMirror = (_rebelFlightDir != 0) && (_rebelDamageLevel == 0);
 
 		if (_rebelDamageLevel == 0) {
 			// Uncovered state: use aiming-based sprite selection (5-14)
 			// Calculate zones from crosshair position relative to playable area
-			// From FUN_41DB5E lines 155-164
-			//
 			// The playable area bounds are defined by corridor boundaries.
 			// xZone = 0-4 (left to right), yZone = 0-1 (top to bottom)
 			// Default to center if bounds not set
@@ -3914,8 +3588,6 @@ void InsaneRebel2::renderHandler25Ship(byte *renderBitmap, int pitch, int width,
 			if (yZone > 1)
 				yZone = 1;
 
-			// Direction-based sprite flip logic (line 161-162 in decompiled)
-			// if (DAT_00457902 == (uVar7 & 1)) { local_58 = 4 - local_58; }
 			if (_rebelFlightDir == (yZone & 1)) {
 				xZone = 4 - xZone;
 			}
@@ -3923,8 +3595,6 @@ void InsaneRebel2::renderHandler25Ship(byte *renderBitmap, int pitch, int width,
 			spriteIdx = yZone * 5 + xZone + 5;
 		} else {
 			// Transitioning/covered state: use direction-based sprite
-			// From FUN_41DB5E lines 166-168:
-			// sVar8 = ((-(ushort)(DAT_00457902 == 0) & 0xffec) + 0x19) - DAT_0045790a
 			// direction == 0: 5 - damage
 			// direction != 0: 25 - damage
 			if (_rebelFlightDir == 0) {
@@ -3945,19 +3615,11 @@ void InsaneRebel2::renderHandler25Ship(byte *renderBitmap, int pitch, int width,
 		int spriteW = _grd002Sprite->getCharWidth(spriteIdx);
 		int spriteH = _grd002Sprite->getCharHeight(spriteIdx);
 
-		// Position calculation from FUN_41DB5E lines 237-247:
 		// GRD002 explicitly adds sprite internal offsets from NUT header:
-		//
 		// Normal case (direction==0 OR damage!=0):
-		//   local_60 = sprite_internal_x_offset (from NUT header +0x12)
-		//   X = DAT_00457910 + local_60
-		//   Y = sprite_internal_y_offset (from NUT header +0x14) + DAT_00457912
-		//
+
 		// Mirrored case (direction!=0 AND damage==0):
-		//   local_60 = 320 - sprite_width - sprite_internal_x_offset
-		//   X = DAT_00457910 + local_60
-		//   Y = sprite_internal_y_offset + DAT_00457912
-		//
+
 		// Now using actual NUT sprite offsets from NutRenderer!
 		int16 spriteXOffset = _grd002Sprite->getCharXOffset(spriteIdx);
 		int16 spriteYOffset = _grd002Sprite->getCharYOffset(spriteIdx);
@@ -3965,17 +3627,11 @@ void InsaneRebel2::renderHandler25Ship(byte *renderBitmap, int pitch, int width,
 		int nativeDrawX, nativeDrawY;
 
 		if (shouldMirror) {
-			// Mirrored position: X = DAT_00457910 + (320 - sprite_width - sprite_x_offset)
-			// From assembly lines 240-243
 			nativeDrawX = _rebelViewOffset2X + (320 - spriteW - spriteXOffset) + nativeBufferViewX;
 		} else {
-			// Normal position: X = DAT_00457910 + sprite_internal_x_offset
-			// From assembly line 238
 			nativeDrawX = _rebelViewOffset2X + spriteXOffset + nativeBufferViewX;
 		}
 
-		// Y = sprite_internal_y_offset + DAT_00457912
-		// From assembly line 246
 		nativeDrawY = spriteYOffset + _rebelViewOffset2Y + nativeBufferViewY;
 		int drawX = renderHiRes ? (nativeDrawX - nativeViewX) * renderScale : nativeDrawX;
 		int drawY = renderHiRes ? (nativeDrawY - nativeViewY) * renderScale : nativeDrawY;
@@ -4043,13 +3699,10 @@ void InsaneRebel2::renderFallbackShip(byte *renderBitmap, int pitch, int width,
 		drawX, drawY, srcX, srcY, numHorizontal, numVertical, _shipDirectionH, _shipDirectionV);
 }
 
-// renderEnemyOverlays -- Handler 0x26 target indicator sprites (FUN_40A2E0).
 void InsaneRebel2::renderEnemyOverlays(byte *renderBitmap, int pitch, int width, int height, int videoWidth) {
-	// Original per-enemy target indicator behavior comes from FUN_40A2E0 (handler 0x26):
 	// - Draws cockpit icon sprites 6..10 at enemy centers.
 	// - Enabled when level flags bit 2 (0x04) is clear.
 	// - Sprite index depends on object half-width bucket.
-	//
 	// It is not a generic all-handler bracket overlay.
 	if (_rebelHandler != 0x26 || !_smush_iconsNut)
 		return;
@@ -4069,7 +3722,7 @@ void InsaneRebel2::renderEnemyOverlays(byte *renderBitmap, int pitch, int width,
 	const int nativeVideoWidth = renderHiRes ? videoWidth / indicatorScale : 320;
 
 	Common::Rect viewRect(nativeViewX, nativeViewY, nativeViewX + nativeVideoWidth, nativeViewY + 200);
-	const int sizeClamp = dparams.specialDamage; // DAT_0047e0fa in FUN_40A2E0
+	const int sizeClamp = dparams.specialDamage;
 
 	for (Common::List<enemy>::iterator it = _enemies.begin(); it != _enemies.end(); ++it) {
 		if (it->destroyed || !it->active || isBitSet(it->id))
@@ -4086,7 +3739,6 @@ void InsaneRebel2::renderEnemyOverlays(byte *renderBitmap, int pitch, int width,
 		if (halfW <= 0 || halfH <= 0)
 			continue;
 
-		// Match size-bucket selection from FUN_40A2E0:
 		// class 0..4 -> sprite 6..10.
 		int indicatorHalfW = halfW;
 		if (sizeClamp > 0)
@@ -4125,7 +3777,6 @@ void InsaneRebel2::renderEnemyOverlays(byte *renderBitmap, int pitch, int width,
 }
 
 // renderExplosions -- Dispatch to per-handler explosion renderer.
-// DAT_0047e108 flags & 1: suppress explosion sprites (counters still tick).
 void InsaneRebel2::renderExplosions(byte *renderBitmap, int pitch, int width, int height) {
 	// Check flags bit 0: suppress explosion sprite rendering
 	LevelDifficultyParams dparams = getDifficultyParams();
@@ -4162,7 +3813,6 @@ void InsaneRebel2::renderExplosions(byte *renderBitmap, int pitch, int width, in
 }
 
 // renderExplosionFrame -- Shared explosion sprite path.
-// The original handlers reach this through separate functions. In the
 // 320x200 path used here, they share centered NUT drawing; callers keep their
 // coordinate transforms, frame timing, and scale bucket rules explicit.
 void InsaneRebel2::renderExplosionFrame(byte *renderBitmap, int pitch, int width, int height,
@@ -4216,9 +3866,6 @@ void InsaneRebel2::renderExplosionFrame(byte *renderBitmap, int pitch, int width
 		explosion.counter--;
 }
 
-// renderTurretExplosions -- Handler 0x26 turret explosion rendering (FUN_409FBC).
-// Position: FUN_0041c720 3D->2D projection (identity at low-res).
-// Scale thresholds: <11, <21. Secondary NUT: DAT_0047fe80 (if DAT_0047a7fc >= 0).
 void InsaneRebel2::renderTurretExplosions(byte *renderBitmap, int pitch, int width, int height) {
 	if (!_smush_iconsNut)
 		return;
@@ -4227,7 +3874,6 @@ void InsaneRebel2::renderTurretExplosions(byte *renderBitmap, int pitch, int wid
 		if (!_explosions[i].active)
 			continue;
 
-		// Position: world coords passed through FUN_0041c720 (3D???2D projection).
 		// At 320x200 low-res turret view, projection is effectively identity.
 		int screenX = _explosions[i].x;
 		int screenY = _explosions[i].y;
@@ -4236,8 +3882,6 @@ void InsaneRebel2::renderTurretExplosions(byte *renderBitmap, int pitch, int wid
 	}
 }
 
-// renderVehicleExplosions -- Handler 8 on-foot explosion rendering (FUN_402696).
-// Position: world coords minus camera offset (DAT_0043e006/08 = _shipPosX/_shipPosY).
 // Scale thresholds: <11, <21. No secondary NUT.
 void InsaneRebel2::renderVehicleExplosions(byte *renderBitmap, int pitch, int width, int height) {
 	if (!_smush_iconsNut)
@@ -4247,7 +3891,6 @@ void InsaneRebel2::renderVehicleExplosions(byte *renderBitmap, int pitch, int wi
 		if (!_explosions[i].active)
 			continue;
 
-		// FUN_402696 line 22-23: screenX = worldX - DAT_0043e006, screenY = worldY - DAT_0043e008
 		int screenX = _explosions[i].x - _shipPosX;
 		int screenY = _explosions[i].y - _shipPosY;
 		renderExplosionFrame(renderBitmap, pitch, width, height, _explosions[i],
@@ -4255,20 +3898,14 @@ void InsaneRebel2::renderVehicleExplosions(byte *renderBitmap, int pitch, int wi
 	}
 }
 
-// renderSpaceExplosions -- Handler 7 space explosion rendering (FUN_40F1C5).
-// Position: FUN_0041c720 3D->2D projection.
-// Original scale thresholds are resolution-dependent (<11/<21, doubled in high-res).
-// Secondary NUT: DAT_0047ff00 (FLY004, if DAT_0047a7fc >= 0).
 void InsaneRebel2::renderSpaceExplosions(byte *renderBitmap, int pitch, int width, int height) {
 	if (!_smush_iconsNut)
 		return;
 
-	// --- Part 1: Space shot explosions (FUN_40F1C5 lines 19-60) ---
 	for (int i = 0; i < 5; i++) {
 		if (!_explosions[i].active)
 			continue;
 
-		// Position: world coords through FUN_0041c720 (3D???2D projection).
 		// At low-res, this is close to identity for the ship view.
 		int screenX = _explosions[i].x;
 		int screenY = _explosions[i].y;
@@ -4276,10 +3913,6 @@ void InsaneRebel2::renderSpaceExplosions(byte *renderBitmap, int pitch, int widt
 			screenX, screenY, kExplosionAdvanceAfterDraw, true);
 	}
 
-	// --- Part 2: Corridor/zone hit explosion (FUN_40F1C5 lines 61-85) ---
-	// Rendered when _hitCooldown > 0 (DAT_0044374c). Decrement happens HERE
-	// (matching original where FUN_40F1C5 decrements DAT_0044374c during render).
-	// _spaceShotDirection (DAT_0044374e) determines explosion side:
 	//   0 = left side (hit left boundary), 1 = right side (hit right boundary)
 	//   2 = bottom (zone push down), 3 = top (zone push up)
 	// Sprite frames: 0x15 - cooldown = 21 - cooldown (frames 12???21 as cooldown 9???0)
@@ -4300,7 +3933,6 @@ void InsaneRebel2::renderSpaceExplosions(byte *renderBitmap, int pitch, int widt
 			}
 
 			// Per-direction offset from ship center.
-			// Original uses lookup tables (DAT_004438da etc.) indexed by
 			// _shipDirectionIndex (35 entries per direction). We approximate
 			// with fixed offsets since we don't have the table data.
 			int offsetX = 0, offsetY = 0;
@@ -4345,9 +3977,6 @@ void InsaneRebel2::renderSpaceExplosions(byte *renderBitmap, int pitch, int widt
 	}
 }
 
-// renderHandler25Explosions -- Handler 25 FPS explosion rendering (FUN_41F29A).
-// Position: world coords + view offset (DAT_0045790c/0e = _rebelViewOffsetX/Y).
-// Original scale thresholds follow Handler 7 (<11/<21, doubled in high-res). No sound panning.
 void InsaneRebel2::renderHandler25Explosions(byte *renderBitmap, int pitch, int width, int height) {
 	if (!_smush_iconsNut)
 		return;
@@ -4356,7 +3985,6 @@ void InsaneRebel2::renderHandler25Explosions(byte *renderBitmap, int pitch, int
 		if (!_explosions[i].active)
 			continue;
 
-		// FUN_41F29A line 22-23: screenX = worldX + DAT_0045790c, screenY = worldY + DAT_0045790e
 		int screenX = _explosions[i].x + _rebelViewOffsetX;
 		int screenY = _explosions[i].y + _rebelViewOffsetY;
 		renderExplosionFrame(renderBitmap, pitch, width, height, _explosions[i],
@@ -4367,16 +3995,16 @@ void InsaneRebel2::renderHandler25Explosions(byte *renderBitmap, int pitch, int
 // renderLaserShots -- Dispatch to per-handler laser renderer.
 void InsaneRebel2::renderLaserShots(byte *renderBitmap, int pitch, int width, int height) {
 	switch (_rebelHandler) {
-	case 0x26:  // Turret - FUN_40AD63
+	case 0x26:
 		renderTurretLaserShots(renderBitmap, pitch, width, height);
 		break;
-	case 8:     // Vehicle - FUN_402ED0
+	case 8:
 		renderVehicleLaserShots(renderBitmap, pitch, width, height);
 		break;
-	case 7:     // Space combat - FUN_40FADF
+	case 7:
 		renderSpaceLaserShots(renderBitmap, pitch, width, height);
 		break;
-	case 25:    // Speeder bike - FUN_0041f004
+	case 25:
 		renderHandler25LaserShots(renderBitmap, pitch, width, height);
 		break;
 	default:
@@ -4385,7 +4013,6 @@ void InsaneRebel2::renderLaserShots(byte *renderBitmap, int pitch, int width, in
 	}
 }
 
-// renderTurretLaserShots -- Handler 0x26 turret laser rendering (FUN_40AD63).
 void InsaneRebel2::renderTurretLaserShots(byte *renderBitmap, int pitch, int width, int height) {
 	// Uses pre-initialized _laserTexture from sprite 5 of CPITIMAG.NUT
 
@@ -4398,8 +4025,6 @@ void InsaneRebel2::renderTurretLaserShots(byte *renderBitmap, int pitch, int wid
 		if (_turretShots[i].counter <= 0)
 			continue;
 
-		// Calculate sound panning from target X position (FUN_004262f0 call)
-		// sVar1 = ((2 - counter) * (targetX - 160)) / 2, clamped to [-127, 127]
 		int16 pan = ((2 - _turretShots[i].counter) * (_turretShots[i].targetX - nativeViewX - 160)) / 2;
 		pan = CLIP<int16>(pan, -127, 127);
 		// TODO: Apply panning to sound channel i+1
@@ -4408,8 +4033,6 @@ void InsaneRebel2::renderTurretLaserShots(byte *renderBitmap, int pitch, int wid
 		int16 targetY = _turretShots[i].targetY;
 		int16 progress = maxDuration - _turretShots[i].counter;
 
-		// Gun positions based on level type (from FUN_40AD63 switch statement)
-		// Parameters from assembly: widthScale=0xC(12), heightScale=8, thickness=0xC(12)
 		switch (_rebelLevelType) {
 		case 1:
 			// Type 1: 3 guns (triple cannon configuration)
@@ -4459,7 +4082,6 @@ void InsaneRebel2::renderTurretLaserShots(byte *renderBitmap, int pitch, int wid
 			break;
 
 		default:
-			// Default: 2 guns with alternating pattern based on shot sequence
 			// When seqNum & 1 == 0: Left (10, 50), Right (310, 130)
 			// When seqNum & 1 == 1: Left (310, 50), Right (10, 130)
 			// Assembly: widthScale=0x19(25), heightScale=8, thickness=0xC(12)
@@ -4487,8 +4109,6 @@ void InsaneRebel2::renderTurretLaserShots(byte *renderBitmap, int pitch, int wid
 	}
 }
 
-// renderVehicleLaserShots -- Handler 8 vehicle laser rendering (FUN_402ED0).
-// In the original, the laser is a short muzzle flash, NOT a traveling projectile.
 void InsaneRebel2::renderVehicleLaserShots(byte *renderBitmap, int pitch, int width, int height) {
 	// No NUT check needed - uses pre-initialized _laserTexture
 
@@ -4498,17 +4118,12 @@ void InsaneRebel2::renderVehicleLaserShots(byte *renderBitmap, int pitch, int wi
 		if (_vehicleShots[i].counter <= 0)
 			continue;
 
-		// Calculate sound panning from STORED target position (FUN_402ED0 lines 24-51)
 		// pan = ((2 - counter) * (targetX - 160)) / 2, clamped to [-127, 127]
 		int16 pan = ((2 - _vehicleShots[i].counter) * (_vehicleShots[i].targetX - _viewX - 160)) / 2;
 		pan = CLIP<int16>(pan, -127, 127);
 		// TODO: Apply panning
 
-		// Calculate positions from CURRENT ship position (FUN_402ED0 lines 53-122)
-		// The original game draws the laser from gun position toward ship center,
 		// creating a short muzzle flash effect (7 pixels horizontal, 25 pixels vertical).
-		//
-		// Low-res formula (DAT_0047a808 < 2):
 		// shipScreenY = ((shipPosY - 0x28) >> 2) + 0x69 = ((shipPosY - 40) >> 2) + 105
 		// shipScreenX = ((shipPosX - 0xa0) >> 3) + 0xa0 = ((shipPosX - 160) >> 3) + 160
 		// gunY = ((shipPosY - 0x28) >> 2) + 0x82 = shipScreenY + 25
@@ -4521,7 +4136,6 @@ void InsaneRebel2::renderVehicleLaserShots(byte *renderBitmap, int pitch, int wi
 		int16 progress = maxDuration - _vehicleShots[i].counter;
 
 		// Draw beam from gun toward ship center (muzzle flash effect)
-		// From FUN_402ED0: widthScale=0x14(20), heightScale=8, thickness=4
 		// Parameters: gunX, gunY -> shipScreenX, shipScreenY (NOT the stored target!)
 		drawLaserBeam(renderBitmap, pitch, width, height,
 			gunX, gunY,
@@ -4530,9 +4144,7 @@ void InsaneRebel2::renderVehicleLaserShots(byte *renderBitmap, int pitch, int wi
 
 		_vehicleShots[i].counter--;
 
-		// FUN_402ED0 samples DAT_0047e030 after drawing the beam. Non-zero
 		// mask pixels select the POV002/POV003 impact sprite index rendered by
-		// FUN_402DA8 on following frames. Mode 2 suppresses these impacts.
 		if (_shipLevelMode != 2 && _level2BackgroundLoaded && _level2Background) {
 			int impactX = ((_shipPosX - 160) >> 3) + _shipPosX + 160;
 			int impactY = ((_shipPosY - 40) >> 2) + _shipPosY + 105;
@@ -4558,7 +4170,6 @@ void InsaneRebel2::renderVehicleLaserShots(byte *renderBitmap, int pitch, int wi
 	}
 }
 
-// renderSpaceLaserShots -- Handler 7 space laser rendering (FUN_40FADF).
 void InsaneRebel2::renderSpaceLaserShots(byte *renderBitmap, int pitch, int width, int height) {
 	// No NUT check needed - uses pre-initialized _laserTexture
 
@@ -4582,7 +4193,6 @@ void InsaneRebel2::renderSpaceLaserShots(byte *renderBitmap, int pitch, int widt
 		int16 progress = maxDuration - _spaceShots[i].counter;
 
 		// Draw dual beams
-		// From FUN_40FADF: widthScale=0xC(12), heightScale=4, thickness=6
 		// Left gun beam
 		drawLaserBeam(renderBitmap, pitch, width, height,
 			leftGunX, leftGunY, targetX, targetY,
@@ -4597,9 +4207,7 @@ void InsaneRebel2::renderSpaceLaserShots(byte *renderBitmap, int pitch, int widt
 	}
 }
 
-// renderHandler25LaserShots -- Handler 25 speeder bike laser rendering (FUN_0041f004).
 void InsaneRebel2::renderHandler25LaserShots(byte *renderBitmap, int pitch, int width, int height) {
-	// FUN_0041f004 uses turret-style shot slots with view offset adjustment
 	// Only render when player is uncovered (damage == 0)
 	if (_rebelDamageLevel != 0) {
 		return;  // Can't shoot while taking cover
@@ -4611,23 +4219,18 @@ void InsaneRebel2::renderHandler25LaserShots(byte *renderBitmap, int pitch, int
 		if (_turretShots[i].counter <= 0)
 			continue;
 
-		// Calculate sound panning from target X position (FUN_004262f0)
-		// sVar1 = ((2 - counter) * (targetX - 160)) / 2, clamped to [-127, 127]
 		int16 pan = ((2 - _turretShots[i].counter) * (_turretShots[i].targetX - 160)) / 2;
 		pan = CLIP<int16>(pan, -127, 127);
 		// TODO: Apply panning to sound channel i+1
 
-		// FUN_00407FCB adds DAT_0045790c/0e at render time to both gun and target.
 		int16 targetX = _turretShots[i].targetX + _rebelViewOffsetX;
 		int16 targetY = _turretShots[i].targetY + _rebelViewOffsetY;
 
-		// Gun position computed at spawn time in base coords (DAT_0045791c/20).
 		int16 gunX = _turretShots[i].gunX + _rebelViewOffsetX;
 		int16 gunY = _turretShots[i].gunY + _rebelViewOffsetY;
 
 		int16 progress = maxDuration - _turretShots[i].counter;
 
-		// From FUN_0041f004 parameters for FUN_0040bbf6:
 		// widthScale=0xC(12), heightScale=4, thickness=6
 		drawLaserBeam(renderBitmap, pitch, width, height,
 			gunX, gunY, targetX, targetY,
@@ -4652,7 +4255,6 @@ void InsaneRebel2::renderHandler8MonitorEffect(byte *renderBitmap, int pitch, in
 		return;
 
 	if (highRes) {
-		// FUN_0041C6C3/FUN_00413EC2: fill every other gameplay row with color 4.
 		for (int y = 1; y < effectHeight; y += 2) {
 			byte *row = renderBitmap + y * pitch;
 			memset(row, 4, effectWidth);
@@ -4663,9 +4265,6 @@ void InsaneRebel2::renderHandler8MonitorEffect(byte *renderBitmap, int pitch, in
 	if (_rebelDetailMode <= 0)
 		return;
 
-	// FUN_0041C6EC/FUN_00413EFC: remap every other gameplay row through
-	// FUN_00410721()+0x400. Use the primary edge table, matching
-	// DAT_0047a81c == 0.
 	const byte *monitorTable = _edgeTable + 0x400;
 	for (int y = 1; y < effectHeight; y += 2) {
 		byte *row = renderBitmap + y * pitch;
@@ -4674,7 +4273,6 @@ void InsaneRebel2::renderHandler8MonitorEffect(byte *renderBitmap, int pitch, in
 	}
 }
 
-// renderHandler8PovOverlay -- Draw Level 12 POV text overlay (FUN_00401CCF).
 void InsaneRebel2::renderHandler8PovOverlay(byte *renderBitmap, int pitch, int width, int height) {
 	if (_rebelHandler != 8 || !renderBitmap || !_smush_talkfontNut || !_smush_povfontNut)
 		return;
@@ -4690,7 +4288,6 @@ void InsaneRebel2::renderHandler8PovOverlay(byte *renderBitmap, int pitch, int w
 	ScummEngine_v7 *vm = (ScummEngine_v7 *)_vm;
 	const int povScale = isHiRes() ? 2 : getRebel2IndicatorScale(width, height);
 
-	// Original updates DAT_0047e048 with random(5)+0x23 when random(20)==0.
 	if (getHandler8PovOverlayRandom(vm, 20) == 0)
 		_handler8HudGlyph = (char)(getHandler8PovOverlayRandom(vm, 5) + 0x23);
 
@@ -4740,21 +4337,17 @@ void InsaneRebel2::renderHandler8PovOverlay(byte *renderBitmap, int pitch, int w
 
 // renderCrosshair -- Draw crosshair/reticle at the current handler's aim point.
 void InsaneRebel2::renderCrosshair(byte *renderBitmap, int pitch, int width, int height) {
-	// From FUN_0040d836 (Handler 7) line 167-168: crosshair only drawn when DAT_004437c0 == 2
 	// Don't draw crosshair when shooting is disabled (flight-only segments)
 	if (!isShootingAllowed()) {
 		return;
 	}
 
-	// Handler 25 (0x19): From FUN_41DB5E lines 195-197, crosshair only drawn when
-	// DAT_0045790a == 0 (fully uncovered). Hide crosshair during cover transition.
 	if (_rebelHandler == 25 && _rebelDamageLevel != 0) {
 		return;
 	}
 
 	// Update target lock state and draw crosshair/reticle
 
-	// Target lock detection (DAT_00443676 equivalent)
 	Common::Point aimPos;
 	if (_rebelHandler == 7) {
 		aimPos = getHandler7ShotTargetPoint();
@@ -4829,13 +4422,11 @@ void InsaneRebel2::renderCrosshair(byte *renderBitmap, int pitch, int width, int
 		}
 
 		// Handler 25 (0x19): Add view offset to crosshair position
-		// From FUN_41DB5E lines 198-199: X = DAT_00457914 + DAT_0045790c, Y = DAT_00457916 + DAT_0045790e
 		if (_rebelHandler == 25) {
 			crosshairX += _rebelViewOffsetX * reticleScale;
 			crosshairY += _rebelViewOffsetY * reticleScale;
 		}
 
-		// FUN_004236e0 with flags=2 applies the NUT frame offsets, then centers.
 		crosshairX += _smush_iconsNut->getCharXOffset(reticleIndex) - cw / 2;
 		crosshairY += _smush_iconsNut->getCharYOffset(reticleIndex) - ch / 2;
 
@@ -4845,10 +4436,8 @@ void InsaneRebel2::renderCrosshair(byte *renderBitmap, int pitch, int width, int
 	}
 }
 
-// frameEndCleanup -- Reset enemy flags and collision zones at frame end (FUN_403240).
 void InsaneRebel2::frameEndCleanup() {
 	// Reset enemy active flags and collision zones at frame end
-	// The original game rebuilds lists from scratch each frame
 
 	for (Common::List<enemy>::iterator it = _enemies.begin(); it != _enemies.end(); ++it) {
 		if (!it->destroyed) {
diff --git a/engines/scumm/insane/rebel2/runlevels.cpp b/engines/scumm/insane/rebel2/runlevels.cpp
index 15ed6c0e9e3..6f835c396c9 100644
--- a/engines/scumm/insane/rebel2/runlevels.cpp
+++ b/engines/scumm/insane/rebel2/runlevels.cpp
@@ -29,42 +29,29 @@
 
 namespace Scumm {
 
-// ---------------------------------------------------------------------------
-// Level 1 Handler - FUN_00417E53
-// Single gameplay phase (01P01.SAN)
-// ---------------------------------------------------------------------------
-
 int InsaneRebel2::runLevel1() {
-	// Play level beginning cinematic (01BEG.SAN)
 	playLevelBegin(1);
 	if (_vm->shouldQuit())
 		return kLevelQuit;
 
-	// Main gameplay retry loop
 	while (!_vm->shouldQuit()) {
-		// Reset shield for this attempt
 		_playerShield = 255;
 		_playerDamage = 0;
 		_deathFrame = 0;
 		resetExplosions();
 
-		// Reset bit table before gameplay starts - FUN_00423880 calls FUN_00423a00(0)
-		// This ensures all enemies are visible (not skipped by SKIP chunks)
 		clearBit(0);
 
-		// Play gameplay (01P01.SAN with 0x28 flags)
 		if (!playLevelSegment("LEV01/01P01.SAN", 0x28))
 			return kLevelQuit;
 
 		if (_playerShield > 0) {
-			// Level completed!
 			debugC(DEBUG_INSANE, "Level 1 completed!");
 			playLevelEnd(1);
-			_levelUnlocked[1] = true;  // Unlock level 2
+			_levelUnlocked[1] = true;
 			return kLevelNextLevel;
 		}
 
-		// Player died - play death video with random A/B variant
 		debugC(DEBUG_INSANE, "Level 1 death at frame %d, lives=%d", _deathFrame, _playerLives - 1);
 		playLevelDeathVariant(1, 1, _deathFrame);
 
@@ -77,7 +64,6 @@ int InsaneRebel2::runLevel1() {
 			return kLevelGameOver;
 		}
 
-		// Play retry prompt and loop
 		playLevelRetry(1);
 		if (_vm->shouldQuit())
 			return kLevelQuit;
@@ -86,19 +72,7 @@ int InsaneRebel2::runLevel1() {
 	return kLevelQuit;
 }
 
-// ---------------------------------------------------------------------------
-// Wave State Management - FUN_00417b61
-// Waits for video completion, accumulates kill state, redistributes kill credits.
-// Used by randomized multi-wave level handlers as the core wave loop primitive.
-// ---------------------------------------------------------------------------
-
 InsaneRebel2::WaveEndResult InsaneRebel2::processWaveEnd(int16 mask, int16 *budget, int16 threshold, uint16 flags) {
-	// FUN_00417b61: Core wave management function
-	// Called after each wave video plays. Handles:
-	// 1. Waiting for video to finish (with early exit on enemy completion)
-	// 2. Copying wave state to accumulated phase state
-	// 3. Redistributing kill credits from the budget
-
 	WaveEndResult result;
 
 	if (_rebelMovieMode) {
@@ -122,25 +96,15 @@ InsaneRebel2::WaveEndResult InsaneRebel2::processWaveEnd(int16 mask, int16 *budg
 		return result;
 	}
 
-	// Step 1: Wait for video to finish (lines 21-32)
-	// Original loop: while (damage < 0xff && frame < maxFrame-1 && !escPressed)
-	// The SmushPlayer::play() call already blocks until video ends, so this step
-	// is handled implicitly. The early-exit logic (threshold > 0: if frame > 50
-	// AND all required enemy type bits are set, count up and break when > threshold)
-	// requires per-frame callbacks to work precisely. The current implementation
-	// covers the primary effect by playing the full video and accumulating state.
+	// Threshold-based early exit needs per-frame callbacks; current playback
+	// waits for the full wave and accumulates the resulting kill state.
 	// TODO: Implement per-frame early exit callback for threshold-based wave termination.
 
-	// Step 2: Copy wave state to phase state (line 33)
-	// DAT_0047ab9c = DAT_0047ab98
 	_rebelPhaseState = _rebelWaveState;
 	debugC(DEBUG_INSANE, "processWaveEnd: waveState=0x%x -> phaseState=0x%x mask=0x%x budget=%d threshold=%d flags=%d",
 		_rebelWaveState, _rebelPhaseState, mask, budget ? *budget : -1, threshold, flags);
 
-	// Step 3: Kill redistribution - add random unkilled types (lines 34-47)
-	// Only when (flags & 2) != 0. Level 2 always passes flags=0, so inactive for Level 2.
 	if ((flags & 2) != 0) {
-		// Collect unkilled enemy type bits that are within the mask
 		byte unkilled[8];
 		int16 numUnkilled = 0;
 		for (byte b = 0; (2 << (b & 0x1f)) < (int)(mask & 0x0e); b++) {
@@ -150,7 +114,6 @@ InsaneRebel2::WaveEndResult InsaneRebel2::processWaveEnd(int16 mask, int16 *budg
 			}
 		}
 		if (numUnkilled > 0) {
-			// Randomly add one unkilled type to phase state
 			int idx = _vm->_rnd.getRandomNumber(numUnkilled - 1);
 			_rebelPhaseState |= unkilled[idx];
 			if (budget)
@@ -158,8 +121,6 @@ InsaneRebel2::WaveEndResult InsaneRebel2::processWaveEnd(int16 mask, int16 *budg
 		}
 	}
 
-	// Step 4: Kill credit transfer (lines 48-73)
-	// Collect all SET enemy type bits from phase state
 	byte killed[8];
 	int16 numKilled = 0;
 	for (byte b = 0; (2 << (b & 0x1f)) < (int)(mask & 0x0e); b++) {
@@ -169,18 +130,15 @@ InsaneRebel2::WaveEndResult InsaneRebel2::processWaveEnd(int16 mask, int16 *budg
 		}
 	}
 
-	// Max credits: 8 normally, 2 if flag bit 0 set
 	int16 maxCredits = ((flags & 1) == 0) ? 8 : 2;
 
-	// Transfer kills from phase state to result, limited by budget
 	int16 creditCount = 0;
 	while (creditCount < maxCredits && numKilled > 0 && budget && *budget > 0) {
 		int idx = _vm->_rnd.getRandomNumber(numKilled - 1);
-		_rebelPhaseState -= killed[idx];   // Remove from accumulated state
-		result.creditedBits |= killed[idx]; // Credit to return value
+		_rebelPhaseState -= killed[idx];
+		result.creditedBits |= killed[idx];
 		(*budget)--;
 
-		// Remove from array (shift remaining elements)
 		for (int i = idx; i + 1 < numKilled; i++) {
 			killed[i] = killed[i + 1];
 		}
@@ -191,7 +149,6 @@ InsaneRebel2::WaveEndResult InsaneRebel2::processWaveEnd(int16 mask, int16 *budg
 	debugC(DEBUG_INSANE, "processWaveEnd: result=0x%x phaseState=0x%x (after redistribution) budget=%d",
 		result.creditedBits, _rebelPhaseState, budget ? *budget : -1);
 
-	// Step 5: Stop conditions (lines 74-78)
 	result.died = (_playerDamage >= 255);
 	result.completed = ((int16)_rebelPhaseState >= mask);
 	result.quit = _vm->shouldQuit();
@@ -337,7 +294,6 @@ void InsaneRebel2::resetLevelWaveState() {
 	_rebelWaveState = 0;
 }
 
-// resetShieldGauge -- Clear the shield hit-point gauge before a looping attack-run segment.
 void InsaneRebel2::resetShieldGauge() {
 	for (int i = 0; i < 10; ++i) {
 		_rebelValueCounters[i] = 0;
@@ -406,61 +362,45 @@ void InsaneRebel2::resetHandler7FlightState() {
 	}
 }
 
-// ---------------------------------------------------------------------------
-// Level 2 Handler - FUN_00418063
 // Multiple parts with P1/P2/P3 subdirectories
 // Random animation variants for each part
-// ---------------------------------------------------------------------------
 
 int InsaneRebel2::runLevel2() {
-	// FUN_00418063: Level 2 "Corellia Star" - Third-person on-foot shooting
 	// Three phases, each with looping enemy waves until all enemy types killed.
 	// Phase completion: (_rebelPhaseState & mask) == mask
 	// Phase 1 mask: 0x06 (enemy types 1,2)
 	// Phase 2 mask: 0x0e (enemy types 1,2,3)
 	// Phase 3 mask: 0x0e (enemy types 1,2,3)
-	//
-	// Kill credit budget (from level data table DAT_0047e0e8):
 	// Each phase gets a budget = tableBase + random(3). processWaveEnd() uses
 	// this budget to randomly redistribute kill credits, creating non-deterministic
 	// wave progression. Using calibrated defaults until exact table values extracted.
 	const int16 kLevel2BudgetBase[3] = { 3, 3, 3 };  // Phase 1, 2, 3
 
-	int bonusCount = 0;     // local_1c: tracks bonus events (DAT_0047ab9c & 0x10)
-	int totalKills = 0;     // local_c: accumulated kill count across phases
-	int totalMisses = 0;    // Accumulated misses (sVar1 + sVar2 from hit counters)
-	int prevWaveState = 0;  // variantIdx: previous wave's state for Phase 3 randomization
+	int bonusCount = 0;
+	int totalKills = 0;
+	int totalMisses = 0;
+	int prevWaveState = 0;
 
-	// Play cutscene (02CUT.SAN)
 	playCinematic("LEV02/02CUT.SAN");
 	if (_vm->shouldQuit())
 		return kLevelQuit;
 
-	// Play level beginning cinematic (02BEG.SAN)
-	// Original: FUN_004171c5("LEV02/02BEG.SAN", 0x20, 0xab, 0xa0, 10, 2, 0x46)
 	playLevelBegin(2);
 	if (_vm->shouldQuit())
 		return kLevelQuit;
 
-	// FUN_00401000 + FUN_00407d10 + FUN_0040c040: Reset game state (before retry loop)
 	clearBit(0);
 
-	// Main gameplay retry loop (restarts from beginning on death)
 	while (!_vm->shouldQuit()) {
 		resetLevelAttemptState(1);
 		bonusCount = 0;
 		totalKills = 0;
 		totalMisses = 0;
 
-		// ----- PHASE 1: P1/02P01_X.SAN -----
-		// FUN_0041c7d0: Reset per-phase counters
 		resetLevelPhaseState(false);
 
-		// Initialize kill budget from level data table + random(3)
-		// Original: sVar4 = levelData[phase1Offset]; local_14[0] = sVar4 + random(3)
 		int16 budget = kLevel2BudgetBase[0] + _vm->_rnd.getRandomNumber(2);
 
-		// Play A.SAN (background loader) ??? flags 0x28 (preserve buffer, gameplay mode)
 		debugC(DEBUG_INSANE, "Level 2 Phase 1 - playing 02P01_A.SAN (background) budget=%d", budget);
 		if (!playLevelSegment("LEV02/P1/02P01_A.SAN", 0x28))
 			return kLevelQuit;
@@ -469,12 +409,10 @@ int InsaneRebel2::runLevel2() {
 		processWaveEnd(0x36, &budget, 0, 0);
 
 		// Phase 1 wave loop: random B/C/D until all type 1,2 enemies killed
-		// Original: while (uVar3 >= 0 && (DAT_0047ab9c & 6) != 6)
 		while (_playerDamage < 255 && (_rebelPhaseState & 0x06) != 0x06) {
 			if (_vm->shouldQuit())
 				return kLevelQuit;
 
-			// Random variant B, C, or D
 			int variant = _vm->_rnd.getRandomNumber(2);  // 0-2
 			const char *variants[] = {
 				"LEV02/P1/02P01_B.SAN",
@@ -482,7 +420,6 @@ int InsaneRebel2::runLevel2() {
 				"LEV02/P1/02P01_D.SAN"
 			};
 			debugC(DEBUG_INSANE, "Phase 1 wave - playing %s (state=0x%x budget=%d)", variants[variant], _rebelPhaseState, budget);
-			// Wave videos use flags 0x428 (original: FUN_0041f4d0 param_2=0x428)
 			if (!playLevelSegment(variants[variant], 0x428))
 				return kLevelQuit;
 
@@ -491,7 +428,6 @@ int InsaneRebel2::runLevel2() {
 			debugC(DEBUG_INSANE, "Phase 1 wave done - state=0x%x (need 0x06) budget=%d", _rebelPhaseState, budget);
 		}
 
-		// Check for bonus (bit 4 = 0x10)
 		if ((_rebelPhaseState & 0x10) != 0)
 			bonusCount++;
 
@@ -504,9 +440,6 @@ int InsaneRebel2::runLevel2() {
 		if (_vm->shouldQuit())
 			return kLevelQuit;
 
-		// Post segment 1 (02PST1.SAN)
-		// Original: FUN_00417168("02PST1.SAN", 0x20) ??? flags = 0x20 | 0x08 = 0x28
-		// FUN_00417168 adds | 8 to preserve the screen buffer between gameplay and transition
 		// Reset handler to 0 so procPostRendering skips HUD/sprite drawing during cinematic
 		_rebelHandler = 0;
 		_rebelStatusBarSprite = 0;
@@ -516,23 +449,18 @@ int InsaneRebel2::runLevel2() {
 		totalKills += _rebelKillCounter;
 		totalMisses += _rebelHitCounter;
 
-		// ----- PHASE 2: P2/02P02_X.SAN -----
 		_currentPhase = 2;
 		resetLevelPhaseState(true);
 
-		// Initialize Phase 2 budget
 		budget = kLevel2BudgetBase[1] + _vm->_rnd.getRandomNumber(2);
 
 		// Restore handler for gameplay ??? will be confirmed by IACT opcode 6
 		_rebelHandler = 8;
 
-		// Play A.SAN (background loader)
 		debugC(DEBUG_INSANE, "Level 2 Phase 2 - playing 02P02_A.SAN (background) budget=%d", budget);
 		if (!playLevelSegment("LEV02/P2/02P02_A.SAN", 0x28))
 			return kLevelQuit;
 
-		// Phase 2 wave loop: processWaveEnd at TOP of loop (matches assembly structure)
-		// Original: local_10 = FUN_00417b61(0x3e, local_14, 0, 0); then switch(local_10)
 		while (true) {
 			WaveEndResult waveEnd = processWaveEnd(0x3e, &budget, 0, 0);
 			uint16 waveSelect = waveEnd.creditedBits;
@@ -541,13 +469,11 @@ int InsaneRebel2::runLevel2() {
 			if (_vm->shouldQuit())
 				return kLevelQuit;
 
-			// If no specific pattern: randomize high bits (original lines 71-74)
-			// When (local_10 & 0xc) == 0: add random 0x10/0x11/0x12
+
 			if ((waveSelect & 0x0c) == 0) {
 				waveSelect = _vm->_rnd.getRandomNumber(2) + 0x10;
 			}
 
-			// Variant selection matching original switch (FUN_418063 lines 75-96)
 			const char *filename;
 			switch (waveSelect) {
 			case 4: case 6:
@@ -581,8 +507,6 @@ int InsaneRebel2::runLevel2() {
 		if (_vm->shouldQuit())
 			return kLevelQuit;
 
-		// Post segment 2 (02PST2.SAN)
-		// Original: FUN_00417168("02PST2.SAN", 0x20) ??? flags = 0x20 | 0x08 = 0x28
 		// Reset handler to 0 so procPostRendering skips HUD/sprite drawing during cinematic
 		_rebelHandler = 0;
 		_rebelStatusBarSprite = 0;
@@ -592,24 +516,20 @@ int InsaneRebel2::runLevel2() {
 		totalKills += _rebelKillCounter;
 		totalMisses += _rebelHitCounter;
 
-		// ----- PHASE 3: P3/02P03_X.SAN -----
 		_currentPhase = 3;
 		resetLevelPhaseState(true);
 		prevWaveState = 0;
 
-		// Initialize Phase 3 budget
 		budget = kLevel2BudgetBase[2] + _vm->_rnd.getRandomNumber(2);
 
 		// Restore handler for gameplay ??? will be confirmed by IACT opcode 6
 		_rebelHandler = 8;
 
-		// Play A.SAN (background loader)
 		debugC(DEBUG_INSANE, "Level 2 Phase 3 - playing 02P03_A.SAN (background) budget=%d", budget);
 		if (!playLevelSegment("LEV02/P3/02P03_A.SAN", 0x28))
 			return kLevelQuit;
 
 		// Phase 3: processWaveEnd at BOTTOM (like Phase 1), waveSelect carried across iterations
-		// Original: local_10 = FUN_00417b61(0x3e, local_14, 0, 0); while (loop) { ...; local_10 = FUN_00417b61(0x3e, local_14, 0x14, 0); }
 		{
 			WaveEndResult waveEnd = processWaveEnd(0x3e, &budget, 0, 0);
 
@@ -619,14 +539,12 @@ int InsaneRebel2::runLevel2() {
 
 				uint16 waveSelect = waveEnd.creditedBits;
 
-				// Phase 3 randomization (original lines 113-115):
 				// If previous wave state bit 0 was clear AND random(8)==0, set bit 0
 				if (((prevWaveState & 1) == 0) && (_vm->_rnd.getRandomNumber(7) == 0)) {
 					waveSelect |= 1;
 				}
 				prevWaveState = waveSelect;
 
-				// Variant selection matching original switch (FUN_418063 lines 117-144)
 				const char *filename;
 				switch (waveSelect) {
 				case 0:
@@ -672,8 +590,6 @@ int InsaneRebel2::runLevel2() {
 		if (_vm->shouldQuit())
 			return kLevelQuit;
 
-		// Level completed! Calculate accuracy score.
-		// Original: FUN_00417327 with score thresholds and medal ranks
 		// Score presentation remains to be implemented.
 		{
 			totalMisses += _rebelHitCounter;
@@ -690,21 +606,16 @@ int InsaneRebel2::runLevel2() {
 	return kLevelQuit;
 }
 
-// ---------------------------------------------------------------------------
-// Level 3 Handler - FUN_0041885F
 // Two phases with per-phase retry handling
 // Phase 1: 03PLAY1.SAN, Phase 2: 03PLAY2.SAN
-// ---------------------------------------------------------------------------
 
 int InsaneRebel2::runLevel3() {
 	int phase1Score = 0;  // Score preserved across phase 2 retries
 
-	// Play level beginning cinematic (03BEG.SAN)
 	playLevelBegin(3);
 	if (_vm->shouldQuit())
 		return kLevelQuit;
 
-	// ----- PHASE 1 retry loop -----
 	while (!_vm->shouldQuit()) {
 		_playerShield = 255;
 		_playerDamage = 0;
@@ -713,22 +624,18 @@ int InsaneRebel2::runLevel3() {
 
 		clearEmbeddedHudFrames();
 
-		// Reset bit table before gameplay starts - FUN_00423880 calls FUN_00423a00(0)
 		clearBit(0);
 		resetHandler7FlightState();
 
-		// Play phase 1 gameplay (03PLAY1.SAN)
 		debugC(DEBUG_INSANE, "Level 3 Phase 1");
 		if (!playLevelSegment("LEV03/03PLAY1.SAN", 0x28))
 			return kLevelQuit;
 
 		if (_playerShield > 0) {
-			// Phase 1 completed - save score and proceed to phase 2
 			phase1Score = _playerScore;
 			break;
 		}
 
-		// Died in phase 1 - frame-based death video
 		debugC(DEBUG_INSANE, "Level 3 Phase 1 death at frame %d", _deathFrame);
 		playLevelDeathVariant(3, 1, _deathFrame);
 		if (_vm->shouldQuit())
@@ -740,7 +647,6 @@ int InsaneRebel2::runLevel3() {
 			return kLevelGameOver;
 		}
 
-		// Phase 1 retry (03RETRY.SAN)
 		playLevelRetryVariant(3, 1);
 		if (_vm->shouldQuit())
 			return kLevelQuit;
@@ -749,15 +655,12 @@ int InsaneRebel2::runLevel3() {
 	if (_vm->shouldQuit())
 		return kLevelQuit;
 
-	// Post segment 1 (03POST1.SAN)
-	// Original: FUN_00417168 adds | 8, so flags = 0x20 | 0x08 = 0x28
 	// Reset handler to 0 so procPostRendering skips HUD/sprite drawing during cinematic
 	_rebelHandler = 0;
 	_rebelStatusBarSprite = 0;
 	if (!playLevelSegment("LEV03/03POST1.SAN", 0x28, false))
 		return kLevelQuit;
 
-	// ----- PHASE 2 retry loop (preserves phase 1 score) -----
 	_currentPhase = 2;
 
 	while (!_vm->shouldQuit()) {
@@ -768,24 +671,20 @@ int InsaneRebel2::runLevel3() {
 
 		clearEmbeddedHudFrames();
 
-		// Reset bit table before gameplay starts
 		clearBit(0);
 		resetHandler7FlightState();
 
-		// Play phase 2 gameplay (03PLAY2.SAN)
 		debugC(DEBUG_INSANE, "Level 3 Phase 2");
 		if (!playLevelSegment("LEV03/03PLAY2.SAN", 0x28))
 			return kLevelQuit;
 
 		if (_playerShield > 0) {
-			// Level completed!
 			debugC(DEBUG_INSANE, "Level 3 completed!");
 			playLevelEnd(3);
 			_levelUnlocked[3] = true;  // Unlock level 4
 			return kLevelNextLevel;
 		}
 
-		// Died in phase 2 - frame-based death video
 		debugC(DEBUG_INSANE, "Level 3 Phase 2 death at frame %d", _deathFrame);
 		playLevelDeathVariant(3, 2, _deathFrame);
 		if (_vm->shouldQuit())
@@ -798,7 +697,6 @@ int InsaneRebel2::runLevel3() {
 			return kLevelGameOver;
 		}
 
-		// Phase 2 retry (03RETRYB.SAN)
 		playLevelRetryVariant(3, 2);
 		if (_vm->shouldQuit())
 			return kLevelQuit;
@@ -807,46 +705,36 @@ int InsaneRebel2::runLevel3() {
 	return kLevelQuit;
 }
 
-// ---------------------------------------------------------------------------
 // Level 4 Handler
 // Cutscene + single gameplay phase
-// ---------------------------------------------------------------------------
 
 int InsaneRebel2::runLevel4() {
-	// Play cutscene (04CUT.SAN)
-	// Original: FUN_00417168 adds | 8, so flags = 0x20 | 0x08 = 0x28
 	if (!playLevelSegment("LEV04/04CUT.SAN", 0x28, false))
 		return kLevelQuit;
 
-	// Play level beginning cinematic (04BEG.SAN)
 	playLevelBegin(4);
 	if (_vm->shouldQuit())
 		return kLevelQuit;
 
-	// Main gameplay retry loop
 	while (!_vm->shouldQuit()) {
 		_playerShield = 255;
 		_playerDamage = 0;
 		_currentPhase = 1;
 		resetExplosions();
 
-		// Reset bit table before gameplay starts
 		clearBit(0);
 
-		// Play gameplay (04PLAY.SAN)
 		debugC(DEBUG_INSANE, "Level 4 gameplay");
 		if (!playLevelSegment("LEV04/04PLAY.SAN", 0x28))
 			return kLevelQuit;
 
 		if (_playerShield > 0) {
-			// Level completed!
 			debugC(DEBUG_INSANE, "Level 4 completed!");
 			playLevelEnd(4);
 			_levelUnlocked[4] = true;  // Unlock level 5
 			return kLevelNextLevel;
 		}
 
-		// Died - play death video (04DIE.SAN, no variants)
 		debugC(DEBUG_INSANE, "Level 4 death");
 		playLevelDeathVariant(4, 1, _deathFrame);
 		if (_vm->shouldQuit())
@@ -866,43 +754,33 @@ int InsaneRebel2::runLevel4() {
 	return kLevelQuit;
 }
 
-// ---------------------------------------------------------------------------
-// Level 5 Handler - FUN_00418EC6
 // Single gameplay phase (05PLAY.SAN)
 // Random A/B death video like Level 1
-// ---------------------------------------------------------------------------
 
 int InsaneRebel2::runLevel5() {
-	// Play level beginning cinematic (05BEG.SAN)
 	playLevelBegin(5);
 	if (_vm->shouldQuit())
 		return kLevelQuit;
 
-	// Main gameplay retry loop
 	while (!_vm->shouldQuit()) {
 		_playerShield = 255;
 		_playerDamage = 0;
 		_currentPhase = 1;
 		resetExplosions();
 
-		// Reset bit table before gameplay starts
 		clearBit(0);
 
-		// Play gameplay (05PLAY.SAN)
-		// Original: FUN_0041f4d0("05PLAY.SAN", 8, -1, -1, 0)
 		debugC(DEBUG_INSANE, "Level 5 gameplay");
 		if (!playLevelSegment("LEV05/05PLAY.SAN", 0x08))
 			return kLevelQuit;
 
 		if (_playerShield > 0) {
-			// Level completed!
 			debugC(DEBUG_INSANE, "Level 5 completed!");
 			playLevelEnd(5);
 			_levelUnlocked[5] = true;  // Unlock level 6
 			return kLevelNextLevel;
 		}
 
-		// Died - play death video with random A/B variant
 		debugC(DEBUG_INSANE, "Level 5 death at frame %d", _deathFrame);
 		playLevelDeathVariant(5, 1, _deathFrame);
 		if (_vm->shouldQuit())
@@ -922,42 +800,30 @@ int InsaneRebel2::runLevel5() {
 	return kLevelQuit;
 }
 
-// ---------------------------------------------------------------------------
-// Level 6 Handler - FUN_00419317
 // Two phases with per-phase retry (like Level 3)
 // Phase 1: 06PLAY1.SAN, Phase 2: 06PLAY2.SAN
-// ---------------------------------------------------------------------------
 
 int InsaneRebel2::runLevel6() {
-	// FUN_004190d6 ??? Mos Eisley: two-phase on-foot (Handler 8)
 	// Phase 1 (levelId=5): 06PLAY1.SAN, mid-switch to 06PLAY1B.SAN at frame 0x2a8
 	// Phase 2 (levelId=6): 06PLAY2.SAN, play until near-end
-	// Original structure: outer do-while for phase 1 retries, inner while(true) for
 	// phase 2 retries + death handling. Phase 1 death breaks inner ??? RETRY at outer bottom.
 	// Phase 2 death ??? RETRYB ??? re-enters phase 2 within inner loop.
 	int phase1Score = 0;
 
-	// Play level beginning cinematic (06BEG.SAN)
-	// Original: FUN_004171c5(s_LEV06_06BEG_SAN, 0x20, 0xaf, 0xa0, 10, 5, 0x4b)
 	playLevelBegin(6);
 	if (_vm->shouldQuit())
 		return kLevelQuit;
 
-	// FUN_00401000 + FUN_0041c7d0 + FUN_0040c040 ??? handler init done by IACT opcode 6
 
 	// Outer retry loop ??? restarts phase 1 on phase 1 death
 	while (!_vm->shouldQuit()) {
-		// FUN_00407d10 ??? reset shot/hit counters
 		clearBit(0);
 		resetExplosions();
 
-		// DAT_0047ab9c = 0xffffffff ??? init phase state
 		_rebelPhaseState = 0xffffffff;
 
-		// ----- PHASE 1: shield attack run -----
-		// Play 06PLAY1 (approach), then loop the 06PLAY1B continuation until the player
 		// destroys the shield (its hit gauge reaches 0) or dies.
-		_rebelLevelType = 5;  // DAT_0047a7f8 = 5
+		_rebelLevelType = 5;
 		_currentPhase = 1;
 
 		debugC(DEBUG_INSANE, "Level 6 Phase 1 (shield attack run)");
@@ -979,7 +845,6 @@ int InsaneRebel2::runLevel6() {
 		_rebelShieldGateActive = false;
 
 		if (_playerShield <= 0) {
-			// Died in phase 1
 			debugC(DEBUG_INSANE, "Level 6 Phase 1 death at frame %d", _deathFrame);
 			playLevelDeathVariant(6, 1, _deathFrame);
 			if (_vm->shouldQuit())
@@ -991,27 +856,24 @@ int InsaneRebel2::runLevel6() {
 				return kLevelGameOver;
 			}
 
-			// Phase 1 retry (06RETRY.SAN) ??? restart outer loop
 			playLevelRetryVariant(6, 1);
 			if (_vm->shouldQuit())
 				return kLevelQuit;
 			continue;
 		}
 
-		// Phase 1 survived ??? preserve this score across phase 2 retries.
-		phase1Score = _playerScore;  // variantIdx = DAT_0047ab84
+		phase1Score = _playerScore;
 
 		_rebelHandler = 0;
 		_rebelStatusBarSprite = 0;
 		if (!playLevelSegment("LEV06/06POST1.SAN", 0x28, false))
 			return kLevelQuit;
 
-		// ----- PHASE 2 retry loop (inner while(true) in original) -----
 		while (!_vm->shouldQuit()) {
-			_rebelLevelType = 6;  // DAT_0047a7f8 = 6
+			_rebelLevelType = 6;
 			_currentPhase = 2;
 			_playerScore = phase1Score;
-			clearBit(0);  // FUN_00407d10
+			clearBit(0);
 			resetExplosions();
 
 			debugC(DEBUG_INSANE, "Level 6 Phase 2");
@@ -1019,14 +881,12 @@ int InsaneRebel2::runLevel6() {
 				return kLevelQuit;
 
 			if (_playerShield > 0) {
-				// Level completed!
 				debugC(DEBUG_INSANE, "Level 6 completed!");
 				playLevelEnd(6);
 				_levelUnlocked[6] = true;
 				return kLevelNextLevel;
 			}
 
-			// Died in phase 2
 			debugC(DEBUG_INSANE, "Level 6 Phase 2 death at frame %d", _deathFrame);
 			playLevelDeathVariant(6, 2, _deathFrame);
 			if (_vm->shouldQuit())
@@ -1038,7 +898,6 @@ int InsaneRebel2::runLevel6() {
 				return kLevelGameOver;
 			}
 
-			// Phase 2 retry (06RETRYB.SAN) ??? re-enter phase 2
 			playLevelRetryVariant(6, 2);
 			if (_vm->shouldQuit())
 				return kLevelQuit;
@@ -1050,27 +909,20 @@ int InsaneRebel2::runLevel6() {
 	return kLevelQuit;
 }
 
-// ---------------------------------------------------------------------------
-// Level 7 Handler - FUN_0041974C
 // "TIE Training" - Canyon flight with fork at frame 1592
 // Single gameplay phase (07PLAY.SAN), optional second segment (07PLAYB.SAN)
-// Death: DAT_0047ab8c-based (fork reached ??? DIE_B, not reached ??? DIE_A)
-// ---------------------------------------------------------------------------
 
 int InsaneRebel2::runLevel7() {
-	bool reachedFork = false;  // DAT_0047ab8c equivalent ??? tracks if 07PLAYB was played
+	bool reachedFork = false;
 
-	// Play cutscene (07CUT.SAN)
 	playCinematic("LEV07/07CUT.SAN");
 	if (_vm->shouldQuit())
 		return kLevelQuit;
 
-	// Play level beginning cinematic (07BEG.SAN)
 	playLevelBegin(7);
 	if (_vm->shouldQuit())
 		return kLevelQuit;
 
-	// FUN_00401000 + FUN_0041c7d0 + FUN_00407d10
 	clearBit(0);
 
 	while (!_vm->shouldQuit()) {
@@ -1082,9 +934,6 @@ int InsaneRebel2::runLevel7() {
 
 		clearBit(0);
 
-		// Play gameplay (07PLAY.SAN)
-		// Original: FUN_0041f4d0("07PLAY.SAN", 0x28, -1, -1, 0)
-		// At frame 0x638 (1592), if DAT_0047ab8c != 0: play 07PLAYB.SAN (0x468)
 		// TODO: Mid-level fork at frame 1592 requires per-frame callback.
 		// The fork video (07PLAYB) should be triggered by IACT callbacks setting
 		// a state flag during gameplay.
@@ -1098,8 +947,6 @@ int InsaneRebel2::runLevel7() {
 			return kLevelNextLevel;
 		}
 
-		// Death video: DIE_B if fork reached, DIE_A if not
-		// Original: s_LEV07_07DIE_B + ((DAT_0047ab8c != 0) - 1 & 0x14)
 		debugC(DEBUG_INSANE, "Level 7 death at frame %d, fork=%d", _deathFrame, reachedFork);
 		if (reachedFork) {
 			playCinematic("LEV07/07DIE_B.SAN");
@@ -1123,21 +970,13 @@ int InsaneRebel2::runLevel7() {
 	return kLevelQuit;
 }
 
-// ---------------------------------------------------------------------------
-// Level 8 Handler - FUN_00419976
 // "Flight to Imdaar" - Y-Wing space battle (single phase)
-// No cutscene (starts with BEG). flags=0x08 for gameplay.
-// Death: random A or B
-// ---------------------------------------------------------------------------
 
 int InsaneRebel2::runLevel8() {
-	// No cutscene ??? starts directly with BEG
-	// Original: FUN_004171c5("08BEG.SAN", 0x20, 0xb1, 0xa0, 10, 5, 0x4b)
 	playLevelBegin(8);
 	if (_vm->shouldQuit())
 		return kLevelQuit;
 
-	// FUN_00401000 + FUN_0041c7d0 + FUN_0040c040
 	clearBit(0);
 
 	while (!_vm->shouldQuit()) {
@@ -1148,8 +987,6 @@ int InsaneRebel2::runLevel8() {
 
 		clearBit(0);
 
-		// Play gameplay (08PLAY.SAN)
-		// Original: FUN_0041f4d0("08PLAY.SAN", 8, -1, -1, 0) ??? note flags=0x08
 		if (!playLevelSegment("LEV08/08PLAY.SAN", 0x08))
 			return kLevelQuit;
 
@@ -1161,8 +998,6 @@ int InsaneRebel2::runLevel8() {
 			return kLevelNextLevel;
 		}
 
-		// Death: random A or B
-		// Original: random(2) ??? A or B via string pointer arithmetic
 		debugC(DEBUG_INSANE, "Level 8 death at frame %d", _deathFrame);
 		playLevelDeathVariant(8, 1, _deathFrame);
 		if (_vm->shouldQuit())
@@ -1182,22 +1017,13 @@ int InsaneRebel2::runLevel8() {
 	return kLevelQuit;
 }
 
-// ---------------------------------------------------------------------------
-// Level 9 Handler - FUN_00419B86
 // "The Mine Field" - Navigate through force fields (single phase)
-// No cutscene. Initial phaseState = 0xfffffffe (all bits set except bit 0).
-// Mid-events at frames 0x19f (415) and 0x352 (850): FUN_00407f55 (score checkpoint)
-// Death: DAT_0047ab94-based (0???A, 1???C, else???B)
-// ---------------------------------------------------------------------------
 
 int InsaneRebel2::runLevel9() {
-	// No cutscene ??? starts directly with BEG
-	// Original: FUN_004171c5("09BEG.SAN", 0x20, 0xb2, 0xa0, 10, 200, 0x10e)
 	playLevelBegin(9);
 	if (_vm->shouldQuit())
 		return kLevelQuit;
 
-	// FUN_00401000 + FUN_0041c7d0 + FUN_0040c040
 	clearBit(0);
 
 	while (!_vm->shouldQuit()) {
@@ -1208,12 +1034,8 @@ int InsaneRebel2::runLevel9() {
 
 		clearBit(0);
 
-		// Original: DAT_0047ab9c = 0xfffffffe (initial phase state ??? all bits except 0)
 		_rebelPhaseState = 0xfffffffe;
 
-		// Play gameplay (09PLAY.SAN)
-		// Original: FUN_0041f4d0("09PLAY.SAN", 0x28, -1, -1, 0)
-		// Mid-events at frames 415 and 850: FUN_00407f55 (score save)
 		// These are handled implicitly ??? the IACT callbacks manage scoring.
 		if (!playLevelSegment("LEV09/09PLAY.SAN", 0x28))
 			return kLevelQuit;
@@ -1226,8 +1048,6 @@ int InsaneRebel2::runLevel9() {
 			return kLevelNextLevel;
 		}
 
-		// Death: based on DAT_0047ab94 (death cause tracking)
-		// Original: 0???DIE_A, 1???DIE_C, else???DIE_B
 		debugC(DEBUG_INSANE, "Level 9 death at frame %d", _deathFrame);
 		playLevelDeathVariant(9, 1, _deathFrame);
 		if (_vm->shouldQuit())
@@ -1247,26 +1067,18 @@ int InsaneRebel2::runLevel9() {
 	return kLevelQuit;
 }
 
-// ---------------------------------------------------------------------------
-// Level 10 Handler - FUN_00419E0A
 // "Speeder Bikes" - Forest speeder chase (single phase)
 // Has cutscene. Single death video (10DIE.SAN, no variants).
-// Original plays DIE then RETRY in sequence (no separate check).
-// ---------------------------------------------------------------------------
 
 int InsaneRebel2::runLevel10() {
-	// Play cutscene (10CUT.SAN)
 	playCinematic("LEV10/10CUT.SAN");
 	if (_vm->shouldQuit())
 		return kLevelQuit;
 
-	// Play level beginning cinematic (10BEG.SAN)
-	// Original: FUN_004171c5("10BEG.SAN", 0x20, 0xb3, 0xa0, 10, 2, 0x46)
 	playLevelBegin(10);
 	if (_vm->shouldQuit())
 		return kLevelQuit;
 
-	// FUN_00401000 + FUN_0041c7d0 + FUN_00407d10
 	clearBit(0);
 
 	while (!_vm->shouldQuit()) {
@@ -1277,8 +1089,6 @@ int InsaneRebel2::runLevel10() {
 
 		clearBit(0);
 
-		// Play gameplay (10PLAY.SAN)
-		// Original: FUN_0041f4d0("10PLAY.SAN", 0x28, -1, -1, 0)
 		if (!playLevelSegment("LEV10/10PLAY.SAN", 0x28))
 			return kLevelQuit;
 
@@ -1290,8 +1100,6 @@ int InsaneRebel2::runLevel10() {
 			return kLevelNextLevel;
 		}
 
-		// Death + Retry: original plays both in sequence
-		// Original: lives--, if 0 break to game over, else DIE+RETRY
 		debugC(DEBUG_INSANE, "Level 10 death at frame %d", _deathFrame);
 		_playerLives--;
 		if (_playerLives <= 0) {
@@ -1310,52 +1118,41 @@ int InsaneRebel2::runLevel10() {
 	return kLevelQuit;
 }
 
-// ---------------------------------------------------------------------------
-// Level 11 Handler - FUN_0041A00C
 // "Inside the Terror" - Three phases + bridge puzzle (Handler 8, on-foot)
-//
 // Phase 1: P1/11P01_X (A,B,C,D) - behind barrels, mask 0x0e
 // Phase 2: P2/11P02_X (A,B,C,D) - walls on right, mask 0x0e, flags=3
 // Phase 3 first half: P3/11P03_X (A-F) - bridge puzzle, mask 0x7e
 //   Exit when (phaseState & 0x70) == 0x70
 // POST3/POST3B/POST3C bridge cinematics
 // Phase 3 second half: P3/11P03_X (G-L) - after bridge, mask 0x0e
-// ---------------------------------------------------------------------------
 
 int InsaneRebel2::runLevel11() {
 	int totalKills = 0;
 	int totalMisses = 0;
 	int prevPhaseState = 0;
 
-	// Kill credit budget bases per phase (from level data table DAT_0047e0e8)
 	const int16 kLevel11BudgetBase[4] = { 3, 3, 3, 3 };
 
-	// Play cutscene (11CUT.SAN)
 	playCinematic("LEV11/11CUT.SAN");
 	if (_vm->shouldQuit())
 		return kLevelQuit;
 
-	// Play level beginning cinematic (11BEG.SAN)
 	playLevelBegin(11);
 	if (_vm->shouldQuit())
 		return kLevelQuit;
 
-	// FUN_00401000 + FUN_00407d10 + FUN_0040c040: Reset game state
 	clearBit(0);
 
-	// Main gameplay retry loop (restarts from Phase 1 on death)
 	while (!_vm->shouldQuit()) {
 		resetLevelAttemptState(1);
 		totalKills = 0;
 		totalMisses = 0;
 		prevPhaseState = 0;
 
-		// ----- PHASE 1: P1/11P01_X.SAN -----
 		resetLevelPhaseState(false);
 
 		int16 budget = kLevel11BudgetBase[0] + _vm->_rnd.getRandomNumber(2);
 
-		// Play A.SAN (background loader)
 		debugC(DEBUG_INSANE, "Level 11 Phase 1 - playing 11P01_A.SAN budget=%d", budget);
 		if (!playLevelSegment("LEV11/P1/11P01_A.SAN", 0x28))
 			return kLevelQuit;
@@ -1373,7 +1170,6 @@ int InsaneRebel2::runLevel11() {
 
 				// Bonus sound check
 				if ((_rebelPhaseState & 0x10) != 0 && (prevPhaseState & 0x10) == 0) {
-					// FUN_00411931 bonus sound ??? not yet implemented
 				}
 				prevPhaseState = _rebelPhaseState;
 
@@ -1403,7 +1199,6 @@ int InsaneRebel2::runLevel11() {
 		if (_vm->shouldQuit())
 			return kLevelQuit;
 
-		// Post segment 1 (11POST1.SAN)
 		_rebelHandler = 0;
 		_rebelStatusBarSprite = 0;
 		if (!playLevelSegment("LEV11/11POST1.SAN", 0x28, false))
@@ -1412,14 +1207,12 @@ int InsaneRebel2::runLevel11() {
 		totalKills += _rebelKillCounter;
 		totalMisses += _rebelHitCounter;
 
-		// ----- PHASE 2: P2/11P02_X.SAN -----
 		_currentPhase = 2;
 		resetLevelPhaseState(true);
 
 		budget = kLevel11BudgetBase[1] + _vm->_rnd.getRandomNumber(2);
 		_rebelHandler = 8;
 
-		// Play A.SAN (background loader)
 		debugC(DEBUG_INSANE, "Level 11 Phase 2 - playing 11P02_A.SAN budget=%d", budget);
 		if (!playLevelSegment("LEV11/P2/11P02_A.SAN", 0x28))
 			return kLevelQuit;
@@ -1459,7 +1252,6 @@ int InsaneRebel2::runLevel11() {
 		if (_vm->shouldQuit())
 			return kLevelQuit;
 
-		// Post segment 2 (11POST2.SAN)
 		_rebelHandler = 0;
 		_rebelStatusBarSprite = 0;
 		if (!playLevelSegment("LEV11/11POST2.SAN", 0x28, false))
@@ -1468,7 +1260,6 @@ int InsaneRebel2::runLevel11() {
 		totalKills += _rebelKillCounter;
 		totalMisses += _rebelHitCounter;
 
-		// ----- PHASE 3 FIRST HALF: P3/11P03_X (A-F) -----
 		// Bridge puzzle ??? exit when (phaseState & 0x70) == 0x70
 		_currentPhase = 3;
 		resetLevelPhaseState(true);
@@ -1492,7 +1283,6 @@ int InsaneRebel2::runLevel11() {
 
 				// Bonus sound: (phaseState & 0xe) == 0xe and previous wasn't
 				if ((_rebelPhaseState & 0x0e) == 0x0e && (prevPhaseState & 0x0e) != 0x0e) {
-					// FUN_00411931 bonus sound ??? not yet implemented
 				}
 				prevPhaseState = _rebelPhaseState;
 
@@ -1519,7 +1309,6 @@ int InsaneRebel2::runLevel11() {
 				if (!playLevelSegment(filename, 0x428))
 					return kLevelQuit;
 
-				// Threshold only for higher variants (original: (2 < variantIdx) - 1 & 0x14)
 				int16 threshold = (variantIdx > 2) ? 0x14 : 0;
 				waveEnd = processWaveEnd(0x7e, &budget, threshold, 0);
 			}
@@ -1534,14 +1323,11 @@ int InsaneRebel2::runLevel11() {
 		if (_vm->shouldQuit())
 			return kLevelQuit;
 
-		// ----- PHASE 3 BRIDGE CINEMATICS -----
 		{
 			bool allBasicKilled = (_rebelPhaseState & 0x0e) >= 0x0e;
 			if (!allBasicKilled) {
 				// Normal bridge drop cinematic
 				playCinematic("LEV11/11POST3.SAN");
-				// Bonus checks (FUN_0042aa70) are not implemented; play the standard path.
-				// Original checks 0x77 and 0x62 for special POST3C cinematic
 			} else {
 				// All enemy types killed ??? bridge dropped successfully
 				playCinematic("LEV11/11POST3B.SAN");
@@ -1551,12 +1337,9 @@ int InsaneRebel2::runLevel11() {
 		if (_vm->shouldQuit())
 			return kLevelQuit;
 
-		// ----- PHASE 3 SECOND HALF: P3/11P03_X (G-L) -----
-		// Reset shots/explosions (FUN_0041ca6a equivalent)
 		resetExplosions();
 		_enemies.clear();
 
-		// Preserve only bits 1-3 of phase state (original: DAT_0047ab9c &= 0xe)
 		_rebelPhaseState &= 0x0e;
 		_rebelWaveState &= 0x0e;
 
@@ -1564,7 +1347,6 @@ int InsaneRebel2::runLevel11() {
 
 		budget = kLevel11BudgetBase[3] + _vm->_rnd.getRandomNumber(2);
 
-		// Play G.SAN (background loader for second half)
 		debugC(DEBUG_INSANE, "Level 11 Phase 3 second half - playing 11P03_G.SAN budget=%d", budget);
 		if (!playLevelSegment("LEV11/P3/11P03_G.SAN", 0x28))
 			return kLevelQuit;
@@ -1618,7 +1400,6 @@ int InsaneRebel2::runLevel11() {
 		if (_vm->shouldQuit())
 			return kLevelQuit;
 
-		// ----- LEVEL COMPLETED -----
 		{
 			totalMisses += _rebelHitCounter;
 			int accuracy = calculateAccuracy(totalKills, totalMisses);
@@ -1634,52 +1415,40 @@ int InsaneRebel2::runLevel11() {
 	return kLevelQuit;
 }
 
-// ---------------------------------------------------------------------------
-// Level 12 Handler - FUN_0041AA14
 // "Sewers" - Four phases FPS corridor shooting (Handler 25)
-//
 // Each phase: init video (P05/P06/P07/P08) ??? first wave ??? wave loop
 // Phase 1: P1/12P01_X (A,B,C,D) mask=6
 // Phase 2: P2/12P02_X (A,B,C,D,E,F) mask=6
 // Phase 3: P3/12P03_X (A,B,C,D,F) mask=6
 // Phase 4: P4/12P04_X (A,B,C,D,E,F) mask=0xe
 // Closing: 12P09.SAN
-// ---------------------------------------------------------------------------
 
 int InsaneRebel2::runLevel12() {
 	// Kill credit budget bases per phase
 	const int16 kLevel12BudgetBase[4] = { 3, 4, 4, 4 };
 
-	// Play cutscene (12CUT.SAN)
 	playCinematic("LEV12/12CUT.SAN");
 	if (_vm->shouldQuit())
 		return kLevelQuit;
 
-	// Play level beginning cinematic (12BEG.SAN)
 	playLevelBegin(12);
 	if (_vm->shouldQuit())
 		return kLevelQuit;
 
-	// FUN_0041c7d0 + FUN_00407d10 + FUN_0040c040: Reset game state
 	clearBit(0);
 
-	// Main gameplay retry loop (restarts from Phase 1 on death)
 	while (!_vm->shouldQuit()) {
 		resetLevelAttemptState(1);
 
-		// ----- PHASE 1: 12P05 ??? P1/12P01_X -----
-		// FUN_00401000: Reset at top of each retry
 		resetLevelPhaseState(false);
 
 		int16 budget = kLevel12BudgetBase[0] + _vm->_rnd.getRandomNumber(2);
 
-		// Initialization video (12P05.SAN)
 		debugC(DEBUG_INSANE, "Level 12 Phase 1 - init 12P05.SAN budget=%d", budget);
 		if (!playLevelSegment("LEV12/12P05.SAN", 0x28, false))
 			return kLevelQuit;
 		processWaveEnd(1, &budget, 0, 0);
 
-		// First wave (P1/12P01_A.SAN)
 		if (!playLevelSegment("LEV12/P1/12P01_A.SAN", 0x428))
 			return kLevelQuit;
 
@@ -1718,19 +1487,16 @@ int InsaneRebel2::runLevel12() {
 		if (_vm->shouldQuit())
 			return kLevelQuit;
 
-		// ----- PHASE 2: 12P06 ??? P2/12P02_X -----
 		_currentPhase = 2;
 		resetLevelWaveState();
 
 		budget = kLevel12BudgetBase[1] + _vm->_rnd.getRandomNumber(3);
 
-		// Initialization video (12P06.SAN)
 		debugC(DEBUG_INSANE, "Level 12 Phase 2 - init 12P06.SAN budget=%d", budget);
 		if (!playLevelSegment("LEV12/12P06.SAN", 0x428, false))
 			return kLevelQuit;
 		processWaveEnd(1, &budget, 0, 0);
 
-		// First wave (P2/12P02_A.SAN)
 		if (!playLevelSegment("LEV12/P2/12P02_A.SAN", 0x428))
 			return kLevelQuit;
 
@@ -1780,19 +1546,16 @@ int InsaneRebel2::runLevel12() {
 		if (_vm->shouldQuit())
 			return kLevelQuit;
 
-		// ----- PHASE 3: 12P07 ??? P3/12P03_X -----
 		_currentPhase = 3;
 		resetLevelWaveState();
 
 		budget = kLevel12BudgetBase[2] + _vm->_rnd.getRandomNumber(3);
 
-		// Initialization video (12P07.SAN)
 		debugC(DEBUG_INSANE, "Level 12 Phase 3 - init 12P07.SAN budget=%d", budget);
 		if (!playLevelSegment("LEV12/12P07.SAN", 0x428, false))
 			return kLevelQuit;
 		processWaveEnd(1, &budget, 0, 0);
 
-		// First wave (P3/12P03_A.SAN)
 		if (!playLevelSegment("LEV12/P3/12P03_A.SAN", 0x428))
 			return kLevelQuit;
 
@@ -1838,19 +1601,16 @@ int InsaneRebel2::runLevel12() {
 		if (_vm->shouldQuit())
 			return kLevelQuit;
 
-		// ----- PHASE 4: 12P08 ??? P4/12P04_X -----
 		_currentPhase = 4;
 		resetLevelWaveState();
 
 		budget = kLevel12BudgetBase[3] + _vm->_rnd.getRandomNumber(3);
 
-		// Initialization video (12P08.SAN)
 		debugC(DEBUG_INSANE, "Level 12 Phase 4 - init 12P08.SAN budget=%d", budget);
 		if (!playLevelSegment("LEV12/12P08.SAN", 0x428, false))
 			return kLevelQuit;
 		processWaveEnd(1, &budget, 0, 0);
 
-		// First wave (P4/12P04_A.SAN)
 		if (!playLevelSegment("LEV12/P4/12P04_A.SAN", 0x428))
 			return kLevelQuit;
 
@@ -1895,19 +1655,16 @@ int InsaneRebel2::runLevel12() {
 		if (_vm->shouldQuit())
 			return kLevelQuit;
 
-		// ----- CLOSING: 12P09.SAN -----
 		if (!playLevelSegment("LEV12/12P09.SAN", 0x428, false))
 			return kLevelQuit;
 		processWaveEnd(1, &budget, 0, 0);
 
-		// ----- LEVEL COMPLETED -----
 		{
 			int accuracy = calculateAccuracy(_rebelKillCounter, _rebelHitCounter);
 			debugC(DEBUG_INSANE, "Level 12 completed! kills=%d misses=%d accuracy=%d%%",
 				_rebelKillCounter, _rebelHitCounter, accuracy);
 		}
 
-		// Bonus checks: FUN_0042aa70(0x61), FUN_0042aa70(99), FUN_0042aa70(0x74)
 		// If all three bonuses found, play special ending (12END_Z.SAN)
 		// Deferred until bonus tracking is implemented
 
@@ -1919,23 +1676,15 @@ int InsaneRebel2::runLevel12() {
 	return kLevelQuit;
 }
 
-// ---------------------------------------------------------------------------
-// Level 13 Handler - FUN_0041B3E1
 // "Escaping the Star Destroyer" - Two-phase flight/escape
 // Phase A: 13PLAY_A.SAN (main flight), transitions to Phase B at maxFrame-10
 // Phase B: 13PLAY_B.SAN (reactor loop, flags 0x468) ??? plays until
-//   (DAT_0047ab90 == 0 && DAT_0047ab7c == 0) meaning all targets destroyed.
-// Death: frame-based (A/B/C/B/D pattern)
-// ---------------------------------------------------------------------------
 
 int InsaneRebel2::runLevel13() {
-	// No cutscene ??? starts directly with BEG
-	// Original: FUN_004171c5("13BEG.SAN", 0x20, 0xb6, 0xa0, 10, 2, 0x46)
 	playLevelBegin(13);
 	if (_vm->shouldQuit())
 		return kLevelQuit;
 
-	// FUN_00401000 + FUN_0041c7d0 + FUN_0040c040
 	clearBit(0);
 
 	while (!_vm->shouldQuit()) {
@@ -1978,7 +1727,6 @@ int InsaneRebel2::runLevel13() {
 			return kLevelNextLevel;
 		}
 
-		// Death: frame-based variant selection (FUN_0041B3E1 lines 47-61)
 		debugC(DEBUG_INSANE, "Level 13 death at frame %d", _deathFrame);
 		playLevelDeathVariant(13, 1, _deathFrame);
 		if (_vm->shouldQuit())
@@ -1998,20 +1746,13 @@ int InsaneRebel2::runLevel13() {
 	return kLevelQuit;
 }
 
-// ---------------------------------------------------------------------------
-// Level 14 Handler - FUN_0041B6E8
 // "TIE Attack" - Final space battle (single phase)
-// No cutscene. Single death video (14DIE.SAN, no variants).
-// ---------------------------------------------------------------------------
 
 int InsaneRebel2::runLevel14() {
-	// No cutscene ??? starts directly with BEG
-	// Original: FUN_004171c5("14BEG.SAN", 0x20, 0xb7, 0xa0, 10, 2, 0x46)
 	playLevelBegin(14);
 	if (_vm->shouldQuit())
 		return kLevelQuit;
 
-	// FUN_00401000 + FUN_0041c7d0 + FUN_0040c040
 	clearBit(0);
 
 	while (!_vm->shouldQuit()) {
@@ -2022,8 +1763,6 @@ int InsaneRebel2::runLevel14() {
 
 		clearBit(0);
 
-		// Play gameplay (14PLAY.SAN)
-		// Original: FUN_0041f4d0("14PLAY.SAN", 0x28, -1, -1, 0)
 		if (!playLevelSegment("LEV14/14PLAY.SAN", 0x28))
 			return kLevelQuit;
 
@@ -2035,7 +1774,6 @@ int InsaneRebel2::runLevel14() {
 			return kLevelNextLevel;
 		}
 
-		// Death: single video (14DIE.SAN)
 		debugC(DEBUG_INSANE, "Level 14 death at frame %d", _deathFrame);
 		playCinematic("LEV14/14DIE.SAN");
 		if (_vm->shouldQuit())
@@ -2055,31 +1793,20 @@ int InsaneRebel2::runLevel14() {
 	return kLevelQuit;
 }
 
-// ---------------------------------------------------------------------------
-// Level 15 Handler - FUN_0041B8D7
 // "Imdaar Alpha" - Final mission (single long phase with level ID switch)
-// Has cutscene. Mid-level: DAT_0047a7f8 changes from 0xf to 0x10 at frame 0x21e.
 // This represents a transition from the tunnel section to the core section.
-// Death: frame-based (A/B/C/B/C/B/D pattern with 7 thresholds)
-// On completion ??? FUN_0041BBE8 (credits/end game, not a playable level)
-// ---------------------------------------------------------------------------
 
 int InsaneRebel2::runLevel15() {
-	// Play cutscene (15CUT.SAN)
 	playCinematic("LEV15/15CUT.SAN");
 	if (_vm->shouldQuit())
 		return kLevelQuit;
 
-	// Play level beginning cinematic (15BEG.SAN)
-	// Original: FUN_004171c5("15BEG.SAN", 0x20, 0xb8, 0xa0, 10, 2, 0x46)
 	playLevelBegin(15);
 	if (_vm->shouldQuit())
 		return kLevelQuit;
 
-	// FUN_00401000 + FUN_0041c7d0 + FUN_0040c040
 	clearBit(0);
 
-	// Original sets DAT_0047a7f8 = 0xf before the gameplay loop
 	_rebelLevelType = 0xf;
 
 	while (!_vm->shouldQuit()) {
@@ -2090,13 +1817,10 @@ int InsaneRebel2::runLevel15() {
 
 		clearBit(0);
 
-		// Original: DAT_0047a7f8 = 0xf again at start of each retry
 		// At frame 0x21e (542): switches to 0x10 (affects difficulty lookup mid-level)
 		// The frame-based switch is handled by IACT opcode 6 in the video data.
 		_rebelLevelType = 0xf;
 
-		// Play gameplay (15PLAY.SAN)
-		// Original: FUN_0041f4d0("15PLAY.SAN", 0x28, -1, -1, 0)
 		if (!playLevelSegment("LEV15/15PLAY.SAN", 0x28))
 			return kLevelQuit;
 
@@ -2105,11 +1829,9 @@ int InsaneRebel2::runLevel15() {
 			debugC(DEBUG_INSANE, "Level 15 completed! accuracy=%d%%", accuracy);
 			playLevelEnd(15);
 			_levelUnlocked[15] = true;
-			// Level 15 completion leads to credits (FUN_0041BBE8)
 			return kLevelNextLevel;
 		}
 
-		// Death: frame-based variant selection (FUN_0041B8D7 lines 46-65)
 		debugC(DEBUG_INSANE, "Level 15 death at frame %d", _deathFrame);
 		playLevelDeathVariant(15, 1, _deathFrame);
 		if (_vm->shouldQuit())


Commit: afe64b518cdd6e9178ec8ae668e24f722cce390f
    https://github.com/scummvm/scummvm/commit/afe64b518cdd6e9178ec8ae668e24f722cce390f
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-21T18:24:31+02:00

Commit Message:
SCUMM: RA1: clean-up of comments for INSANE code

Changed paths:
    engines/scumm/insane/rebel1/audio.cpp
    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/insane/rebel1/render.cpp
    engines/scumm/insane/rebel1/runlevels.cpp


diff --git a/engines/scumm/insane/rebel1/audio.cpp b/engines/scumm/insane/rebel1/audio.cpp
index 482a764db67..83a52d9f50a 100644
--- a/engines/scumm/insane/rebel1/audio.cpp
+++ b/engines/scumm/insane/rebel1/audio.cpp
@@ -44,9 +44,7 @@ const char *const kRA1SfxFiles[8] = {
 	"SYS/BLAST.SAD"
 };
 
-// ---------------------------------------------------------------------------
 // Audio
-// ---------------------------------------------------------------------------
 
 void InsaneRebel1::initAudio(int sampleRate) {
 	_audio.init(_vm, sampleRate);
diff --git a/engines/scumm/insane/rebel1/iact.cpp b/engines/scumm/insane/rebel1/iact.cpp
index 92c798a81f0..a7473fff597 100644
--- a/engines/scumm/insane/rebel1/iact.cpp
+++ b/engines/scumm/insane/rebel1/iact.cpp
@@ -54,7 +54,7 @@ inline int16 stepRebel1Op0BReticleAxis(int axisValue) {
 	if (axisValue >= 0)
 		return (int16)(axisValue >> 4);
 
-	// The 3DO ARM code integrates the standard control-pad path with ASR #4.
+	// Preserve arithmetic-shift rounding for negative movement.
 	return (int16)-((-axisValue + 15) >> 4);
 }
 
@@ -89,8 +89,8 @@ const int kRA1EnhancedFlightDirectMaxX = 64;
 const int kRA1EnhancedFlightDirectMaxY = 40;
 const int kRA1MouseFlightRollTargetScale = 16;
 const int kRA1ControlPadAxisStep = 0x1E;
-const int16 kOnFootCenterX = 0xA3;  // g_perspectiveX in HandleGameOp19
-const int16 kOnFootCenterY = 0x82;  // g_perspectiveY in HandleGameOp19
+const int16 kOnFootCenterX = 0xA3;
+const int16 kOnFootCenterY = 0x82;
 const int16 kOnFootCursorBaseY = kOnFootCenterY - 0x32;
 const int16 kOnFootGamepadStep = 7;
 const int16 kOnFootCursorMinX = 0;
@@ -101,9 +101,7 @@ const int16 kRA1Level12TargetA = 195;
 const int16 kRA1Level12TargetB = 197;
 const int16 kRA1Level12TargetC = 199;
 
-// Level 15 final approach 0x5D damage/event codes consumed by
-// RunLevel1GameLoop. The latch stores the raw GAME parameter; no translation is
-// performed by HandleGameOp5D_SegmentLinkLatch.
+// Level 15 final approach 0x5D damage/event codes.
 inline bool isLevel15FinalDamageLatch(uint16 code) {
 	switch (code) {
 	case 0x0049:
@@ -197,7 +195,6 @@ inline bool isLevel2DamageLatch(uint16 code) {
 	}
 }
 
-// Level 2 asteroid-contact helper from FUN_00012d70. RunLevel2Flow calls it
 // once per frontend frame and raises damage flag 0x20 when the current asteroid
 // pass intersects the camera position.
 inline bool hasLevel2AsteroidImpact(uint16 frameCounter, int16 perspectiveX, int16 perspectiveY) {
@@ -397,9 +394,7 @@ inline bool isLevel10DamageLatch(uint16 code) {
 }
 
 inline bool isLevel12DamageLatch(uint16 code) {
-	// RunLevel12Flow treats these 0x5D latch values as safe; every other value
-	// raises damage flag 0x40. This helper is intentionally the inverse of the
-	// original branch shape so the update path reads as "is damage".
+	// These 0x5D latch values are safe; every other value raises damage flag 0x40.
 	switch (code) {
 	case 0x0000:
 	case 0x0037:
@@ -465,8 +460,6 @@ inline bool hasLevel6PerspectiveHazard(uint16 frame, int16 perspectiveX, int16 p
 	}
 }
 
-// Named translations of the original Level 8 route collision helpers
-// FUN_12FE1/FUN_130C9/FUN_13195. The names are new to the port.
 inline bool hasLevel8WalkerHazardRoute0(uint16 frame, int16 viewX, int16 viewY) {
 	switch (frame) {
 	case 0x00CD:
@@ -551,9 +544,7 @@ void InsaneRebel1::applyFrameObjectHitState(int16 targetIdx) {
 	if (targetIdx < 0)
 		return;
 
-	// Protected targets (shield generators in Level 4, etc.) can be hit
-	// repeatedly ??? skip event mask toggle. Original: DAT_00007732/7734 check
-	// in HandleGameOp5A.
+	// Protected targets use separate hit counters.
 	if (targetIdx + 1 == _protectedTargetA || targetIdx + 1 == _protectedTargetB)
 		return;
 
@@ -599,17 +590,13 @@ void InsaneRebel1::clearFrameObjectPrimaryBits(int byteIndex, byte mask) {
 	_frameObjectState[byteIndex] &= ~mask;
 }
 
-// Port helpers for RunLevel14Flow. The original checks DAT_7614..7616 and
-// DAT_7605..7606 inline, then increments a local 60-frame completion counter.
 bool InsaneRebel1::areLevel14Phase1TargetsDestroyed() const {
-	// g_gameplayPhaseFlags starts at primary byte 0; DAT_7614 is byte 0x12.
 	return areFrameObjectPrimaryBitsSet(0x12, 0x01) &&
 		areFrameObjectPrimaryBitsSet(0x13, 0xFF) &&
 		areFrameObjectPrimaryBitsSet(0x14, 0xFE);
 }
 
 bool InsaneRebel1::areLevel14Phase2TargetsDestroyed() const {
-	// Phase 2 checks DAT_7605 low nibble and all of DAT_7606.
 	return areFrameObjectPrimaryBitsSet(0x03, 0x0F) &&
 		areFrameObjectPrimaryBitsSet(0x04, 0xFF);
 }
@@ -732,14 +719,12 @@ void InsaneRebel1::checkDynamicLevelBranch(int32 curFrame) {
 		return;
 
 	if (_currentLevel == 6) {
-		// RunLevel7Flow compares the branch table against g_frameCounter. The
-		// playback callback writes that value from the ANM-local frame index,
-		// not from the decoded GAME counter embedded in these non-linear files.
+		// Route branches use the ANM-local frame index, not the decoded GAME counter.
 		if (curFrame < 0)
 			return;
 		const uint32 routeFrame = (uint32)curFrame;
-		// GAME 0x09 publishes its branch-tested position in g_shipPosX.
-		// Keep the drawn ship center and the 0x09 aim cursor split,
+			// GAME 0x09 publishes a separate branch-tested cursor position.
+			// Keep the drawn ship center and the 0x09 aim cursor split,
 		// so compare the effective gameplay cursor here.
 		const int16 branchX = getGameplayCursorX();
 		const int route = CLIP<int>(_levelRouteIndex, 0, 5);
@@ -782,7 +767,6 @@ void InsaneRebel1::checkDynamicLevelBranch(int32 curFrame) {
 	}
 
 	// Level 8 owns its branch choice in updateLevel8WalkerState(), where the
-	// original RunLevel8Flow also draws the timer/arrows and updates the local
 	// choice variable. This function only performs the delayed route cutover.
 }
 
@@ -860,10 +844,7 @@ void InsaneRebel1::updateFlightVariantCursor() {
 	if (_rollAccum > 0)
 		xScale = -xScale;
 
-	// Assembly-verified 0x09 layout:
-	//   ship sprite center = (_74B6 + _74BA, _74B8 + _74BC)
-	//   cursor center      = (_74BE, _74C0)
-	// The flight sprite center already lives in _shipPos.
+	// Keep the flight sprite center and aim cursor separate.
 	const int16 shipBaseX = _shipPosX;
 	const int16 shipBaseY = _shipPosY;
 	const int32 liftTerm = (int32)_liftSmooth - 0x0F;
@@ -876,11 +857,7 @@ void InsaneRebel1::updateFlightVariantCursor() {
 		_rollAccum, _liftSmooth, bucket, _shipDirIndex, _perspectiveX, _perspectiveY);
 }
 
-// preprocessMouseAxes ??? FUN_231BE (0x231BE) centered-axis output law, adapted to
-// the absolute 320x200 mouse space. The old DOS virtual-mouse/recenter
-// control path is intentionally not used. For opcode 0x0B, gamepad input uses
-// the 3DO standard-pad reticle model: axis samples move the reticle, and
-// releasing the pad holds the last reticle position.
+// Opcode 0x0B gamepad input moves the reticle and holds position on release.
 bool InsaneRebel1::isOp0BReticleControlLevel() const {
 	switch (_currentLevel) {
 	case 1:  // Level 2
@@ -1057,16 +1034,11 @@ void InsaneRebel1::preprocessMouseAxes(int16 &inputX, int16 &inputY, bool *usedJ
 		inputY = -inputY;
 }
 
-// updateShipPhysics ??? FUN_1DEB5 (0x1DEB5). Accumulator-based position system.
-// Roll accumulator (_74CA) driven by input, position accumulators (_74C2/_74C6)
-// driven by roll + drift + cross-coupling. Ship position = base + accum >> 8.
+// Roll input drives position accumulators. Ship position = base + (accum >> 8).
 void InsaneRebel1::updateShipPhysics() {
 	_frameCounter++;
 
-	// HandleGameOp07_ShipFlight resets the ship accumulators and camera when
-	// the GAME 0x07 frame counter enters at 0. Level 1 happened to work because
-	// its runlevel code pre-initialized the same state, but later 0x07-driven
-	// stages like L3 rely on the handler to do this reset itself.
+	// Reset ship accumulators and camera when a 0x07 stream starts.
 	if (_gameCounter == 0) {
 		_posAccumX = 0;
 		_posAccumY = 0;
@@ -1088,8 +1060,6 @@ void InsaneRebel1::updateShipPhysics() {
 	if (_damageCooldown > 0)
 		_damageCooldown--;
 
-	// --- Step 1: Gameplay axes from FUN_231BE ---
-	// HandleGameOp07_ShipFlight consumes the preprocessed axes in DAT_756C/756E,
 	// not raw mouse coordinates. Reuse the same centered-axis law here.
 	const uint16 effectiveOpcode = getEffectiveGameOpcode();
 	int16 inputX = 0;
@@ -1106,7 +1076,6 @@ void InsaneRebel1::updateShipPhysics() {
 	else if (_activeInputSource == kInputSourceJoystickDigital)
 		inputSourceName = "joystick-dpad";
 
-	// --- Step 2: Roll accumulator (_74CA) ---
 	// Normal mode: accumulate. For absolute mouse input in flight handlers,
 	// steer toward a bounded roll target so holding the cursor off center does
 	// not continue accelerating the ship until it clamps.
@@ -1119,12 +1088,10 @@ void InsaneRebel1::updateShipPhysics() {
 	}
 	_rollAccum = CLIP<int32>(_rollAccum, -0x47F, 0x47F);
 
-	// --- Step 3: Vertical smoothing (_74CE) ---
 	// Exponential decay toward -inputY
 	_liftSmooth += (-_liftSmooth - (int32)inputY) >> 1;
 	_liftSmooth = CLIP<int32>(_liftSmooth, -0x20, 0x20);
 
-	// --- Step 4: Position accumulator deltas ---
 	// X delta: drift + slide coupling - cross-coupling
 	int32 rng = 100;  // RandScaleByte(200), centered at 100
 	int32 crossTermX;
@@ -1147,42 +1114,30 @@ void InsaneRebel1::updateShipPhysics() {
 
 	int32 deltaY = (absRoll >> 1) + crossTermY;
 
-	// --- Step 5: Update position accumulators ---
 	_posAccumX += deltaX;
 	_posAccumX = CLIP<int32>(_posAccumX, -0x8200, 0x8200);
 	_posAccumY += deltaY;
 	_posAccumY = CLIP<int32>(_posAccumY, -0x3200, 0x4600);
 
-	// --- Step 6: Derive pixel position from accumulators ---
-	// Original: _74BA = _74C2 >> 8, _74BC = _74C6 >> 8
 	// Ship position = base + offset
 	_shipPosX = kRA1CenterX + (int16)(_posAccumX >> 8);
 	_shipPosY = kRA1CenterY + (int16)(_posAccumY >> 8);
 
-	// Clamp to screen bounds
+	// Clamp to screen bounds.
 	_shipPosX = CLIP<int16>(_shipPosX, kRA1MinX, kRA1MaxX);
 	_shipPosY = CLIP<int16>(_shipPosY, kRA1MinY, kRA1MaxY);
 
-	// --- Step 7: Perspective offsets (SetCameraOffset) ---
-	// FUN_1DEB5 computes these linearly from ship offsets:
-	//   viewX = clamp((_74BA + 0x20), 0, 0x40)
-	//   viewY = clamp((_74BC + 0x17), 0, 0x2E)
 	_perspectiveX = CLIP<int16>((int16)(_shipPosX - kRA1CenterX + 0x20), 0, 0x40);
 	_perspectiveY = CLIP<int16>((int16)(_shipPosY - kRA1CenterY + 0x17), 0, 0x2E);
-	// Screen shake: when enabled, add random ??2 jitter (original SetCameraOffset at 0x22514)
 	if (_screenShakeEnabled) {
 		_perspectiveX = CLIP<int16>((int16)(_perspectiveX + (_vm->_rnd.getRandomNumber(4) - 2)), 0, 0x40);
 		_perspectiveY = CLIP<int16>((int16)(_perspectiveY + (_vm->_rnd.getRandomNumber(4) - 2)), 0, 0x2E);
 	}
 
-	// FUN_1DEB5 updates the curve table via FUN_22549 after SetCameraOffset.
-	// The full DOS path blends a few roll-history terms; use the current roll
-	// accumulator so side-looking still bends the gameplay projection. DOS
-	// negates before the arithmetic shift.
+	// Side-looking bends the gameplay projection from the current roll.
 	rebuildProjectionTable((int16)CLIP<int32>((-_rollAccum) >> 7, -0x1A, 0x1A), 0x1A);
 
-	// --- Step 8: Direction sprite index (FUN_1DEB5 LAB_1e23e) ---
-	// Horizontal component from _74CA (rollAccum):
+	// Horizontal component from the roll accumulator:
 	//   |rollAccum| <= 0x80: center (0)
 	//   rollAccum > 0x80:  ((rollAccum - 0x80) >> 8) * 5 + 5   (right: 5,10,15,20)
 	//   rollAccum < -0x80: ((abs(rollAccum) - 0x80) >> 8) * 5 + 25 (left: 25,30,35,40)
@@ -1195,8 +1150,7 @@ void InsaneRebel1::updateShipPhysics() {
 		hComponent = 0;
 	}
 
-	// Vertical component from _74CE (liftSmooth):
-	//   (_74CE + 0x20) * 5 / 0x41  ??? 0..4  (5 rows)
+	// Vertical component from lift smoothing.
 	int vComponent = (_liftSmooth + 0x20) * 5 / 0x41;
 
 	if (_shipBank.numSprites > 0)
@@ -1215,16 +1169,12 @@ void InsaneRebel1::updateShipPhysics() {
 		_shipPosX, _shipPosY, _perspectiveX, _perspectiveY, _shipDirIndex,
 		_currentLevel, _flyControlMode, _activeGameOpcode);
 
-	// --- Step 9: Damage/event bit synthesis + damage processing ---
-	// RA1 FUN_1B297-style latches from GAME opcodes:
-	//   0x5D latch 0xFFFF -> bit 0x40 (obstacle/contact)
 	//   0x5F non-zero + RNG -> bit 0x80 (projectile-like hit)
 	if (_gameLatch5D == 0xFFFF)
 		_damageFlags |= 0x40;
 	if (_gameLatch5F != 0 && _vm->_rnd.getRandomNumber((uint16)(_gameLatch5F - 1)) == 0)
 		_damageFlags |= 0x80;
 
-	// Damage guard/mask from FUN_1DEB5: (_damageFlags & 0x96) != 0
 	// damageFlags & 0x96 = bits 1,2,4,7 = wall collisions (0x16) + projectile hit (0x80)
 	if (!_noDamage && (_damageFlags & 0x96) != 0 && _damageCooldown == 0 &&
 		_health >= 0 && _deathTimer <= 0) {
@@ -1237,7 +1187,6 @@ void InsaneRebel1::updateShipPhysics() {
 
 		if (_health < 0) {
 			_deathTimer = kDeathTimerInit;
-			// g_deathCauseIndicator (0x772E) ??? set based on damage source
 			if (_damageFlags & 0x80)
 				_deathCauseIndicator = 2;  // Projectile hit death
 			else
@@ -1246,12 +1195,11 @@ void InsaneRebel1::updateShipPhysics() {
 
 		_prevDamageFlags = _damageFlags;
 		_damageCooldown = kDamageCooldownInit;
-		// HandleGameOp07_ShipFlight dispatches g_sfxDamageHit here.
+		// Damage hit sound.
 		playSfx(kSfxBoom, 127, 0);
 		_screenFlash = 3;
 	}
 
-	// Latches are per-frame event inputs in the original pipeline.
 	_gameLatch5D = 0;
 	_gameLatch5F = 0;
 
@@ -1267,7 +1215,6 @@ void InsaneRebel1::updateShipPhysics() {
 			_score += _tuning.time;
 	}
 
-	// Screen flash decay ??? screen shake follows flash (EnableScreenShake/DisableScreenShake at 0x224ED)
 	if (_screenFlash > 0) {
 		_screenFlash--;
 		_screenShakeEnabled = (_screenFlash > 0);
@@ -1276,8 +1223,6 @@ void InsaneRebel1::updateShipPhysics() {
 	// Clear per-frame damage flags
 	_damageFlags = 0;
 
-	// --- Path branching detection ---
-	// Original (FUN_1B297): at GAME counter 394 (0x18A), sets nextSceneA=0x67/nextSceneB=0x69.
 	// After this point, drift goes strongly negative (pushing ship left for the hard path).
 	// If ship is right of center, player chose the hard branch ??? switch to L1PLAY1R.
 	// Keep this as a one-shot decision: once threshold is reached, lock path.
@@ -1302,8 +1247,7 @@ void InsaneRebel1::updateShipPhysics() {
 		_corridorLeftX, _corridorTopY, _corridorRightX, _corridorBottomY);
 }
 
-// Port helper: FUN_1E6A7 computes this direction bucket inline before applying
-// the current frame's movement update.
+// Update turret sprite direction from the current frame's movement.
 void InsaneRebel1::updateTurretShipDirection(int16 offsetY) {
 	int dir = 0;
 	if (_flyControlMode == 2) {
@@ -1341,13 +1285,8 @@ void InsaneRebel1::updateTurretShipDirection(int16 offsetY) {
 }
 
 void InsaneRebel1::getCollisionShipCenter(int16 &x, int16 &y) const {
-	// Original 0x0D/0x0E collision compares script zones transformed by
-	// FUN_223FE against the gameplay-window ship center (base center +
-	// g_shipOffset). This is DOS screen space; render overlays add the viewport
-	// offset separately when drawing into the larger source buffer.
-	//
-	// In Level 1 part 2, HandleGameOp0A_TurretVariant reuses _shipPos for the
-	// targeting cursor, so collision must read the movement accumulator instead.
+	// Level 1 part 2 reuses _shipPos for the targeting cursor, so collision
+	// must read the movement accumulator instead.
 	if (_currentLevel == 0 && _flyControlMode == 2) {
 		x = (int16)(kRA1CenterX + (int16)(_posAccumX >> 8));
 		y = (int16)(kRA1CenterY + (int16)(_posAccumY >> 8));
@@ -1357,12 +1296,9 @@ void InsaneRebel1::getCollisionShipCenter(int16 &x, int16 &y) const {
 	}
 }
 
-// updateTurretPhysics ??? FUN_1E6A7 (0x1E6A7), opcode 0x08 path.
-// Stage-2 cockpit mode uses different smoothing/clamps than FUN_1DEB5.
 void InsaneRebel1::updateTurretPhysics() {
 	_frameCounter++;
 
-	// FUN_1E6A7 consumes GAME field1 as frame counter (arg6 in dispatcher call).
 	// The 0x10/0x40 gates come from dispatcher arg4 (callback control bits),
 	// not from GAME payload fields.
 	const int32 counter = _gameCounter;
@@ -1383,7 +1319,6 @@ void InsaneRebel1::updateTurretPhysics() {
 		_damageCooldown = 0;
 	}
 
-	// GAME 0x0A appears before 0x08 in turret/combat ANMs. The original
 	// draws shots/targeting and the ship from this pre-physics center, then
 	// updates the camera offset at the end of 0x08 for the final viewport copy.
 	const int16 preMoveOffsetX = (int16)(_posAccumX >> 8);
@@ -1394,7 +1329,6 @@ void InsaneRebel1::updateTurretPhysics() {
 	_turretFrameShipCenterY = (int16)(kRA1CenterY + preMoveOffsetY);
 	_turretFrameShipCenterValid = true;
 
-	// Damage gate from FUN_1E6A7.
 	if (!_noDamage && _damageFlags != 0 && _damageCooldown == 0 && _health >= 0 && _deathTimer <= 0) {
 		if (_damageFlags == 0x80)
 			_health -= _tuning.shot;
@@ -1411,7 +1345,7 @@ void InsaneRebel1::updateTurretPhysics() {
 
 		_prevDamageFlags = _damageFlags;
 		_damageCooldown = kDamageCooldownInit;
-		// HandleGameOp08_TurretFlight dispatches g_sfxDamageHit here.
+		// Damage hit sound.
 		playSfx(kSfxBoom, 127, 0);
 		_screenFlash = 3;
 	}
@@ -1424,9 +1358,7 @@ void InsaneRebel1::updateTurretPhysics() {
 
 	updateTurretShipDirection(preMoveOffsetY);
 
-	// FUN_1E6A7 movement gate: counter > 8 or flags bit 0x40.
 	if (counter > 8 || (modeFlags & 0x40)) {
-		// FUN_1E6A7 consumes DAT_756C/DAT_756E from the shared input bridge,
 		// not raw mouse coordinates.
 		int16 inputX = 0;
 		int16 inputY = 0;
@@ -1438,7 +1370,6 @@ void InsaneRebel1::updateTurretPhysics() {
 		const int16 rawInputY = inputY;
 
 		if (usedJoystick && _flyControlMode == 2) {
-			// Extra concession for Level 1 part 2. The original 0x08 handler
 			// uses raw axes directly; do not damp Level 13's surface controls.
 			inputX /= 2;
 			inputY /= 2;
@@ -1477,12 +1408,9 @@ void InsaneRebel1::updateTurretPhysics() {
 	_perspectiveX = CLIP<int16>((int16)(offsetX + 0x20), 0, 0x40);
 	_perspectiveY = CLIP<int16>((int16)(offsetY + 0x17), 0, 0x2E);
 
-	// FUN_1E6A7 rebuilds the side-look curve with a shallower table than the
-	// main flight handler, derived directly from roll.
-	// DOS negates before the arithmetic shift.
+	// Keep projection bending tied to turret roll.
 	rebuildProjectionTable((int16)((-_rollAccum) >> 9), 0x0D);
 
-	// Regeneration + survival bonus via FUN_1BB0E call in this path.
 	if ((_frameCounter & 0x1F) == 0) {
 		if (_health >= 0 && _health < kMaxHealth)
 			_health++;
@@ -1500,7 +1428,6 @@ void InsaneRebel1::updateTurretPhysics() {
 	_damageFlags = 0;
 }
 
-// New port helpers for the palette-flash block that is inline in FUN_1CDA7.
 void InsaneRebel1::restoreScreenFlashPalette() {
 	if (!_screenFlashBasePaletteValid)
 		return;
@@ -1533,18 +1460,13 @@ void InsaneRebel1::updateScreenFlashPalette() {
 	_player->setPalette(flashPalette);
 }
 
-// updateGameOp0BPhysics ??? FUN_1CDA7 (0x1CDA7). GAME opcode 0x0B handler.
 // Uses 10-frame input history averaging instead of accumulators.
 // Ship position = averaged input + center offset.
 // Viewport = second history buffer for smooth camera scrolling.
 void InsaneRebel1::updateGameOp0BPhysics() {
-	// Enhanced controls keep the original 0x0B pipeline but average fewer
-	// samples for responsiveness.
+	// Keep smoothing short for responsiveness.
 	const int gameOp0BSmoothWindow = 2;
 
-	// RA1 FUN_1B297-style per-frame latches for 0x0B sections:
-	//   0x5D latch 0xFFFF -> bit 0x40 (scripted obstacle/contact)
-	//   0x5F non-zero + RNG -> bit 0x80 (scripted random hit)
 	const bool level15Phase1 = (_currentLevel == 14 && _levelGameplayPhase == 1);
 	const bool level15FinalPhase = (_currentLevel == 14 && _levelGameplayPhase == 2);
 	const bool level14Phase1 = (_currentLevel == 13 && _levelGameplayPhase == 1);
@@ -1562,7 +1484,6 @@ void InsaneRebel1::updateGameOp0BPhysics() {
 		_damageFlags |= 0x40;
 	if (_gameLatch5F != 0 && !level14Phase2) {
 		bool randomProjectileHit = false;
-		// Original level loops spell these fixed-probability cases separately.
 		// The shared 0x0B path collapses them into one branch.
 		if (level14Phase1)
 			randomProjectileHit = (_vm->_rnd.getRandomNumber(3) == 0);
@@ -1592,19 +1513,15 @@ void InsaneRebel1::updateGameOp0BPhysics() {
 		const uint16 walkerFrame = (uint16)_gameCounter;
 		level8WalkerPlayerHit = hasLevel8WalkerPlayerHit(_levelRouteIndex, walkerFrame,
 			_perspectiveX, _perspectiveY);
-		// RunLevel8Flow sets damage flag 0x20 when the AT-AT route contact
-		// helper hits the player; boss damage uses _walkerHealth separately.
+		// Player collision and boss damage are tracked separately.
 		if (level8WalkerPlayerHit)
 			_damageFlags |= 0x20;
 	}
 
-	// Health regeneration (FUN_1BB0E): +1 every 32 frames when alive
 	if (_health >= 0 && _health < kMaxHealth && (_frameCounter & 0x1F) == 0) {
 		_health++;
 	}
 
-	// Damage application (FUN_1CDA7 lines 20-41)
-	// Original 0x0B mapping: 0x80 -> +0x13, 0x40 -> +0x0F, 0x20 -> +0x11.
 	// No cooldown ??? all three damage types can stack each frame
 	if (!_noDamage && _damageFlags != 0 && _health >= 0 && _deathTimer < 1) {
 		const int16 oldHealth = _health;
@@ -1623,7 +1540,6 @@ void InsaneRebel1::updateGameOp0BPhysics() {
 			else
 				_deathCauseIndicator = 1;
 		}
-		// FUN_1CDA7 dispatches g_sfxDamageHit, initialized from SYS/BOOM.SAD.
 		playSfx(kSfxBoom, 127, 0);
 		if (_currentLevel == 1) {
 			debugC(DEBUG_INSANE, "L2 player hit: frame=%u view=(%d,%d) latch=%u asteroid=%d flags=0x%02x health=%d->%d",
@@ -1640,7 +1556,6 @@ void InsaneRebel1::updateGameOp0BPhysics() {
 		_damageFlags = 0;
 	}
 
-	// Latches are frame-local event inputs in the original pipeline.
 	_gameLatch5D = 0;
 	_gameLatch5F = 0;
 
@@ -1656,8 +1571,6 @@ void InsaneRebel1::updateGameOp0BPhysics() {
 	}
 	updateScreenFlashPalette();
 
-	// --- Cursor and perspective smoothing (FUN_1CDA7) ---
-	// _inputHistory* maps to 0x7580/0x7594, _viewHistory* to 0x75A8/0x75BC.
 	int16 inputX = 0;
 	int16 inputY = 0;
 	bool usedJoystick = false;
@@ -1766,10 +1679,7 @@ void InsaneRebel1::updateGameOp0BPhysics() {
 	_frameCounter++;
 
 	if (_currentLevel == 11) {
-		// RunLevel12Flow checks the three attackers around frame 0x550. The
 		// event-mask bits map to L12PLAY's one-based FOBJ IDs 195, 197, and 199
-		// in the port's frame-object helper. On failure the original keeps
-		// pumping L12PLAY until frame 0x564, plays L12RETRY, then restarts
 		// L12PLAY without resetting health.
 		if (_levelGameplayPhase == 0 && _frameCounter == 0x550) {
 			const bool targetADestroyed = isFrameObjectPrimarySet(kRA1Level12TargetA);
@@ -1797,8 +1707,7 @@ void InsaneRebel1::updateGameOp0BPhysics() {
 			_vm->_smushVideoShouldFinish = true;
 	}
 
-	// Level 4 Phase 2: enable torpedo mode at frontend/movie frame 0x3E and
-	// finish as soon as the torpedo registers a hit. The DOS loop exits on killCount.
+	// Level 4 Phase 2: enable torpedo mode and finish on hit.
 	if (_currentLevel == 3 && _levelGameplayPhase == 2) {
 		if (_currentSmushFrame == 0x3E)
 			_gameplayFlags75ff |= 2;
@@ -1807,7 +1716,6 @@ void InsaneRebel1::updateGameOp0BPhysics() {
 	}
 
 	// Level 4 Phase 1: track shield generator hits per frame.
-	// Original (RunLevel4Flow): g_recentKillObjectIdPlus1 checked every frame.
 	// When enough hits accumulated (>0x30), generator is "destroyed" (clear protectedTarget).
 	// When both destroyed for 60 frames, phase ends.
 	if (_currentLevel == 3 && _levelGameplayPhase == 1) {
@@ -1830,9 +1738,7 @@ void InsaneRebel1::updateGameOp0BPhysics() {
 		}
 	}
 
-	// Level 5 Phase 1: DOS RunLevel5Flow exits L5PLAY only after killCount stays
-	// above 2 for 20 frontend frames. That countdown is carried by the runlevel,
-	// not by opcode 0x07 itself.
+	// Level 5 Phase 1: require sustained kill count before ending the phase.
 	if (_currentLevel == 4 && _levelGameplayPhase == 1 &&
 		_level5SuccessFramesRemaining > 0 && _killCount > 2 && !_vm->_smushVideoShouldFinish) {
 		_level5SuccessFramesRemaining--;
@@ -1840,12 +1746,9 @@ void InsaneRebel1::updateGameOp0BPhysics() {
 			_vm->_smushVideoShouldFinish = true;
 	}
 
-	// Level 15 Phase 2: enable torpedo at frontend/movie frame 0x18A, expose
-	// the protected target IDs used by the original flow, and finish when
-	// g_gameplayPhaseFlags & 2 becomes set.
+	// Final torpedo phase.
 	if (_currentLevel == 14 && _levelGameplayPhase == 2) {
 		if (_currentSmushFrame == 0x18A) {
-			// Original writes the 16-bit flags word: g_hudDisableFlags |= 0x210.
 			// Low byte bit 0x10 suppresses normal hit feedback in the torpedo phase;
 			// high byte bit 0x02 switches targeting/shot rendering to torpedoes.
 			_gameplayFlags75fe |= 0x10;
@@ -1870,28 +1773,14 @@ bool InsaneRebel1::isTorpedoModeActive() const {
 	if ((_gameplayFlags75ff & 0x2) == 0)
 		return false;
 
-	// The original high-byte flag is only intentionally armed by the level 4
-	// torpedo run and the level 15 exhaust-port run. Gate the port's rendering
-	// and shot behavior to those phases so stale route/retry state cannot turn
-	// ordinary laser sections into torpedo mode.
+	// Torpedo rendering is valid only during torpedo-run phases.
 	return (_currentLevel == 3 && _levelGameplayPhase == 2) ||
 		(_currentLevel == 14 && _levelGameplayPhase == 2);
 }
 
 
-// Helper splits for the original on-foot GAME handlers:
-// HandleGameOp19_OnFootSequence (0x19) and HandleGameOp1A_OnFootVariant (0x1A).
-// On-foot handler for Level 9 (Stormtroopers). Character walks left/right, crosshair tracks mouse.
-//
-// Original has TWO separate variable pairs:
-//   DAT_000041a0/41a2 = camera offset (SetCameraOffset, ProjectPointToScreen)
-//   g_perspectiveX/Y  = crosshair center (on-foot targeting)
-// Our _perspectiveX/_perspectiveY maps to the camera offset (DAT_000041a0/41a2).
-// The crosshair center (0xA3, 0x82) is a separate constant for on-foot mode.
-// Port split matching HandleGameOp19_OnFootSequence. The helper name is new to
-// this implementation; the original code dispatches the opcode handler directly.
+// On-foot handler for Level 9.
 void InsaneRebel1::initOnFootSequence() {
-	// --- First-frame initialization (0x19 counter==0) ---
 	if (!_onFootInitialized) {
 		_onFootInitialized = true;
 		_shipDirIndex = 15;       // Center facing
@@ -1978,11 +1867,9 @@ void InsaneRebel1::preprocessOnFootAim(int16 &inputX, int16 &inputY, bool *usedJ
 }
 
 // Port split matching HandleGameOp19_OnFootSequence. The helper name is new to
-// this implementation; the original code dispatches the opcode handler directly.
 void InsaneRebel1::updateOnFootSequence() {
 	initOnFootSequence();
 
-	// --- 0x19: Post-draw character walk animation + damage ---
 	// Track fire button for animation
 	if (!_playerFired)
 		_onFootAnimCounter = 0;
@@ -2003,18 +1890,14 @@ void InsaneRebel1::updateOnFootSequence() {
 		_shipDirIndex = 15;
 		_onFootCharX += 0x3A;
 	} 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
-		// L9PILOT.NUT poses 11..14 aim left and 16..19 aim right.
+		// Use the visual screen-space vector so aim poses face the right way.
 		const int16 centerX = _onFootCharX + kOnFootCenterX;
 		const int16 centerY = _onFootCharY + kOnFootCenterY;
 		const int16 aimDir = CLIP<int16>(
 			(int16)ra1ShotDirection(centerX, centerY, _shipPosX, _shipPosY), -4, 4);
 		_shipDirIndex = aimDir + 15;
 	} else {
-		// Walking based on input direction. The 3DO second held button skips the
-		// early aim-pose branch above and reaches these walk tests immediately.
+		// The secondary held button skips the early aim-pose branch above.
 		int16 inputX = (int16)(_shipPosX - kOnFootCenterX);
 		int16 inputY = (int16)(_shipPosY - kOnFootCursorBaseY);
 		if (!hasFrameGameOpcode(0x1A))
@@ -2025,19 +1908,14 @@ void InsaneRebel1::updateOnFootSequence() {
 			_shipDirIndex = 4;  // Walk left
 	}
 
-	// --- Scripted damage latches ??? damageFlags ---
-	// L9 on-foot trooper shots use ordinary 0x5D event ids, gated by the
-	// 0x5D object bitmask handler, then consumed by FUN_1ED95 through 0x74D4.
-	// The ship loops keep narrower level-specific 0x5D damage rules.
+	// L9 on-foot trooper shots use ordinary 0x5D event ids.
 	if (_gameLatch5D != 0)
 		_damageFlags |= 0x40;
 	if (_gameLatch5F != 0 &&
 		_vm->_rnd.getRandomNumber((uint16)(_gameLatch5F - 1)) == 0)
 		_damageFlags |= 0x80;
 
-	// --- Damage handling (from HandleGameOp19_OnFootSequence) ---
-	// On-foot damage uses the same heavy-damage tuning byte as ship shot/collision
-	// damage in the original, not the miss penalty.
+	// On-foot damage uses the same heavy-damage tuning byte as ship shot/collision.
 	if (!_noDamage && _damageFlags != 0 && _damageCooldown == 0 && _health >= 0 && _deathTimer < 1) {
 		const int16 oldHealth = _health;
 		_health -= _tuning.shot;
@@ -2054,13 +1932,8 @@ void InsaneRebel1::updateOnFootSequence() {
 	}
 }
 
-// Port split matching HandleGameOp1A_OnFootVariant. The helper name is new to
-// this implementation; the original code dispatches the opcode handler directly.
 void InsaneRebel1::updateOnFootAimVariant() {
-	// --- 0x1A: Crosshair positioning (HandleGameOp1A_OnFootVariant) ---
-	// DOS used virtual-mouse axes relative to the character offset. The
-	// mouse and gamepad reticle are screen-space controls so the cursor remains
-	// able to cross the whole playfield while Luke is standing at either side.
+	// Screen-space controls let the cursor cross the playfield from either side.
 	int16 inputX = 0, inputY = 0;
 	preprocessOnFootAim(inputX, inputY);
 	_shipPosX = CLIP<int16>((int16)(inputX + kOnFootCenterX), kOnFootCursorMinX, kOnFootCursorMaxX);
@@ -2103,7 +1976,6 @@ void InsaneRebel1::procSKIP(int32 subSize, Common::SeekableReadStream &b) {
 }
 
 void InsaneRebel1::handleGameOpcode5EReset(uint32 param1) {
-	// RA1 dispatcher inline reset/init path (FUN_1BE1B case 0x5E).
 	// This is not a pure control-mode assignment.
 	if (_frameDispatchFlags & 0x40) {
 		debugC(DEBUG_INSANE, "GAME 0x5E: reset suppressed by dispatch flags=0x%02x",
@@ -2159,7 +2031,6 @@ void InsaneRebel1::handleGameOpcode5EReset(uint32 param1) {
 	if (_walkerRoundReplay && _currentLevel == 7)
 		_killCount = walkerReplayKillCount;
 
-	// Field1 == 0 corresponds to baseline recenter behavior in the original.
 	if ((int32)param1 == 0) {
 		_perspectiveX = 0x20;
 		_perspectiveY = 0x17;
@@ -2174,7 +2045,6 @@ void InsaneRebel1::handleGameOpcode5EReset(uint32 param1) {
 	if ((int32)param1 == 0)
 		syncViewportOffset(true);
 
-	// Original RunLevel8Flow initializes its separate g_level8HitboxBuffer
 	// after the first L8PLAY runtime reset. We fold that mask into the
 	// secondary half of _frameObjectState; route resumes preserve it.
 	if (_currentLevel == 7)
@@ -2215,21 +2085,18 @@ void InsaneRebel1::handleGameOpcode07ShipFlight(int32 subSize, Common::SeekableR
 	_activeGameOpcode = 0x07;
 	_frameGameOpcodeMask |= (1u << 0x07);
 	// Per-frame corridor data: f1=frame counter, f2=max frames, f3=drift bias, f4=unused
-	// f1 is the original's _DAT_7740 (game frame counter)
 	// f3 is the drift/wind parameter combined with tuning table
 	_gameCounter = param1;
 	if (subSize >= 20) {
 		b.readUint32BE(); // f2 (max frames, unused in physics)
 		_driftParam = (int16)(int32)b.readUint32BE();
-		b.readUint32BE(); // f4 (unused in original assembly)
+		b.readUint32BE();
 		debugC(DEBUG_INSANE, "GAME 0x07: counter=%d driftParam=%d", _gameCounter, _driftParam);
 	}
 }
 
 void InsaneRebel1::handleGameOpcode0DCorridor(int32 subSize, Common::SeekableReadStream &b, uint32 param1) {
 	// Corridor boundaries: per-frame flight corridor
-	// Original params: left, top, WIDTH, HEIGHT (not right/bottom!)
-	// FUN_1C54D computes center = (left+width/2, top+height/2), transforms, then checks edges.
 	if (subSize < 20)
 		return;
 
@@ -2240,7 +2107,6 @@ void InsaneRebel1::handleGameOpcode0DCorridor(int32 subSize, Common::SeekableRea
 
 	int16 centerX = corridorLeft + corridorWidth / 2;
 	int16 centerY = corridorTop + corridorHeight / 2;
-	// DOS FUN_1C54D calls FUN_223FE here, which projects the scripted
 	// rectangle center into gameplay-window space before testing it against the
 	// ship center.
 	projectGameplayPoint(centerX, centerY);
@@ -2301,8 +2167,6 @@ void InsaneRebel1::handleGameOpcode0DCorridor(int32 subSize, Common::SeekableRea
 }
 
 void InsaneRebel1::handleGameOpcode0EZone(int32 subSize, Common::SeekableReadStream &b, uint32 param1) {
-	// Secondary collision zone (FUN_1C6E9): AABB test, sets damageFlags bit 4 (0x10)
-	// Original params: left, top, WIDTH, HEIGHT (same as 0x0D)
 	if (subSize < 20)
 		return;
 
@@ -2315,7 +2179,6 @@ void InsaneRebel1::handleGameOpcode0EZone(int32 subSize, Common::SeekableReadStr
 
 	int16 centerX = zoneLeft + zoneWidth / 2;
 	int16 centerY = zoneTop + zoneHeight / 2;
-	// Same gameplay-window FUN_223FE transform as opcode 0x0D/FUN_1C54D.
 	projectGameplayPoint(centerX, centerY);
 
 	zoneLeft = centerX - zoneWidth / 2;
@@ -2343,13 +2206,11 @@ void InsaneRebel1::handleGameOpcode0EZone(int32 subSize, Common::SeekableReadStr
 void InsaneRebel1::handleGameOpcode0BFirstPerson(int32 subSize, Common::SeekableReadStream &b, uint32 param1) {
 	_activeGameOpcode = 0x0B;
 	_frameGameOpcodeMask |= (1u << 0x0B);
-	// GAME 0x0B per-frame handler (FUN_1CDA7).
-	// field1 = frame counter, field2 = max frames
 	_gameCounter = param1;
 	if (subSize >= 20) {
-		uint32 maxFrames = b.readUint32BE(); // field2 (max frames)
-		b.readUint32BE(); // field3
-		b.readUint32BE(); // field4
+		uint32 maxFrames = b.readUint32BE();
+		b.readUint32BE();
+		b.readUint32BE();
 
 		// RA1 scripts drive progression with GAME counters. Finish 0x0B-driven
 		// interactive videos once the script counter reaches the terminal frame.
@@ -2368,8 +2229,6 @@ void InsaneRebel1::handleGameOpcode0BFirstPerson(int32 subSize, Common::Seekable
 }
 
 void InsaneRebel1::handleGameOpcode5ATarget(int32 subSize, Common::SeekableReadStream &b, uint32 param1) {
-	// Target detection ??? HandleGameOp5A (0x1C0EF). AABB from video stream.
-	// Original checks event mask: if target already killed, skip to GOST update.
 	if (subSize < 24)
 		return;
 
@@ -2421,7 +2280,6 @@ void InsaneRebel1::handleGameCounterOpcode(uint32 opcode, int32 subSize, Common:
 	}
 }
 
-// handleGameChunk ??? FUN_1BE1B (0x1BE1B). Central GAME opcode dispatcher.
 // Reads 7x32-bit BE integers from GAME chunk, routes to per-opcode handlers.
 void InsaneRebel1::handleGameChunk(int32 subSize, Common::SeekableReadStream &b,
 		byte *renderBitmap, int width, int height) {
@@ -2432,9 +2290,7 @@ void InsaneRebel1::handleGameChunk(int32 subSize, Common::SeekableReadStream &b,
 	uint32 param1 = b.readUint32BE();
 	_frameHasGameChunk = true;
 
-	// FUN_1BE1B applies two global gates before the opcode switch. Bit 0 of
-	// g_combatModeFlags skips gameplay dispatch entirely; bit 5 of g_hudDisableFlags
-	// suppresses the handlers while still requesting HUD refresh for a few opcodes.
+	// Combat-mode flags can suppress handlers while still requesting HUD refresh.
 	if (_gameplayFlags75ff & 1) {
 		debugC(DEBUG_INSANE, "GAME 0x%02x: skipped by combat mode flags=0x%02x",
 			opcode, _gameplayFlags75ff);
@@ -2496,11 +2352,9 @@ void InsaneRebel1::handleGameChunk(int32 subSize, Common::SeekableReadStream &b,
 	}
 }
 
-// processShot ??? FUN_1CCA0 (0x1CCA0). Spawns shot into explosion slot when fired.
 // Called once per frame during interactive rendering.
 void InsaneRebel1::processShot() {
 	if (_optRapidFire) {
-		// 3DO FUN_0000c3a4 advances this before testing fire state; a fresh press
 		// fires immediately, while held repeats are gated to phase 0.
 		_rapidFirePhase++;
 		if (_rapidFirePhase > 2)
@@ -2516,7 +2370,6 @@ void InsaneRebel1::processShot() {
 	}
 
 	// On-foot mode: only spawn when in aiming stance (dirIndex 11-19) or flags force it.
-	// Original: if (((10 < g_shipDirIndex) && (g_shipDirIndex < 0x14)) || ((DAT_000075fe & 8) != 0))
 	const uint16 effectiveOpcode = getEffectiveGameOpcode();
 	const bool onFootMode = (effectiveOpcode == 0x19 || effectiveOpcode == 0x1A);
 	if (onFootMode) {
@@ -2524,7 +2377,6 @@ void InsaneRebel1::processShot() {
 			return;
 	}
 
-	// Find first available slot (timer < 1 or > 5), matching FUN_1CCA0.
 	int slot = -1;
 	for (int i = 0; i < kMaxShotSlots; i++) {
 		if (_shotSlots[i].timer <= 0 || _shotSlots[i].timer > 5) {
@@ -2536,10 +2388,7 @@ void InsaneRebel1::processShot() {
 		return;
 	}
 
-	// Shot origin depends on game mode:
-	// On-foot: character position (g_shipOffsetX + g_perspectiveX)
-	// Turret: ship center
-	// Flight: cursor position
+	// Shot origin depends on game mode.
 	const bool turretMode = (effectiveOpcode == 0x08 || effectiveOpcode == 0x0A);
 	int16 originX, originY;
 	if (onFootMode) {
@@ -2574,8 +2423,6 @@ void InsaneRebel1::processShot() {
 	}
 }
 
-// checkTargetHit ??? FUN_1C0EF (0x1C0EF). AABB target detection with snap tolerance.
-// The original compares raw FOBJ bounds against the cursor after
 // UnprojectScreenPoint(). Keep that separate from 0x0D/0x0E collision, which projects
 // zones into gameplay-window screen space before comparing against the ship center.
 void InsaneRebel1::checkTargetHit(int16 targetIdx, int16 left, int16 top, int16 right, int16 bottom) {
@@ -2630,13 +2477,10 @@ void InsaneRebel1::checkTargetHit(int16 targetIdx, int16 left, int16 top, int16
 				setGameplayCursor(effectiveOpcode, snappedX, snappedY);
 			}
 
-			// DOS uses g_recentKillObjectIdPlus1 as a frame-wide latch. Once one
-			// target is hit this frame, overlapping FOBJ layers must not consume the
-			// same shot again.
+			// Only one overlapping target may consume the shot each frame.
 			if (_lastHitTarget == 0) {
 				for (int i = 0; i < kMaxShotSlots; i++) {
 					if (_shotSlots[i].timer == 1) {  // Shot in final frame = impact
-						// Hit! Record in GOST slot for explosion animation
 						int gi = _gostSlotIdx;
 						_gostSlots[gi].targetId = targetIdx + 1;
 						_gostSlots[gi].frame = 0;
diff --git a/engines/scumm/insane/rebel1/menu.cpp b/engines/scumm/insane/rebel1/menu.cpp
index cfa683a1f60..9636215b453 100644
--- a/engines/scumm/insane/rebel1/menu.cpp
+++ b/engines/scumm/insane/rebel1/menu.cpp
@@ -60,8 +60,6 @@ const int kRA1GameplayMouseSettleEdgeMargin = 16;
 const int kRA1GameplayMouseMaxX = 319;
 const int kRA1GameplayMouseMaxY = 199;
 const uint32 kRA1GameplayMouseSettleExtendMs = 1000;
-// Original picker traversal uses fixed max indices, not strlen(): passcodes
-// stop at 0x1d and high-score names stop at 0x37.
 const char kRA1TextEntryPickerChars[] = "^`_ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
 const int kRA1PasscodePickerCount = 0x1d + 1;
 const int kRA1NamePickerCount = 0x37 + 1;
@@ -146,7 +144,6 @@ static int getRebel1PasscodeDifficulty(int passwordIndex) {
 }
 
 static int getRebel1PasscodeStartLevel(int passwordIndex) {
-	// Original main-menu control flow groups passcodes by difficulty:
 	// 1-3 resume after chapter 3, 4-6 after chapter 6, 7-9 after chapter 10,
 	// 10-12 after chapter 14, and 13-15 jump to the final ending sequence.
 	switch ((passwordIndex - 1) / 3) {
@@ -165,9 +162,6 @@ 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",
@@ -331,9 +325,9 @@ void InsaneRebel1::beginTextEntry(bool passcodeMode) {
 	_textEntryPasscodeMode = passcodeMode;
 	_textEntryDone = false;
 	_textEntryCanceled = false;
-	_textEntryPickerIndex = 3; // First letter in the original picker string.
+	_textEntryPickerIndex = 3;
 	_textEntryPickerOffsetX = 0;
-	// The DOS buffers include a leading '<' font marker and cap strlen() at 8.
+	// Text buffers include a leading '<' font marker and cap strlen() at 8.
 	_textEntryMaxChars = 8;
 	_textEntryBuffer[0] = '\0';
 
@@ -584,8 +578,6 @@ bool InsaneRebel1::handleControllerMenuAxis(int16 oldAxisX, int16 oldAxisY) {
 
 	// RA1 maps stick axes to backend axis events for analog gameplay.
 	// Menus still need edge-triggered digital navigation from those same axes.
-	// Match the original raw-input convention: positive Y is stick-down in
-	// menus, and the Y-flip option reverses that interpretation.
 	const int oldX = getRebel1MenuAxisDirection(oldAxisX);
 	const int oldY = getRebel1MenuAxisDirection(oldAxisY);
 	const int newX = getRebel1MenuAxisDirection(_joystickAxisX);
@@ -617,7 +609,6 @@ void InsaneRebel1::openGameplayMainMenu() {
 		_player->unpause();
 }
 
-// Extra 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
 // highlight frames drawn by the render*Overlay() functions, so they share the
@@ -915,7 +906,6 @@ bool InsaneRebel1::notifyEvent(const Common::Event &event) {
 		handleMenuCommand(getRebel1MenuCommandFromKey(event.kbd)))
 		return true;
 
-	// Shooting: mouse button during interactive gameplay ??? FUN_1CCA0 (0x1CCA0)
 	if (_interactiveVideoActive && !_menuActive) {
 		if (event.type == Common::EVENT_LBUTTONDOWN) {
 			_vm->_mouse.x = event.mouse.x;
@@ -1018,8 +1008,6 @@ void InsaneRebel1::drawMenuTitleText(byte *dst, int pitch, int width, int height
 }
 
 void InsaneRebel1::renderHighScoresOverlay(byte *dst, int pitch, int width, int height) {
-	// --- TOP PILOTS high score display ---
-	// Original renders over O1SCORE.ANM. Title appears after frame 20,
 	// entries fade in one per frame. We show all immediately.
 	const int titleW = getMenuTalkTextWidth("TOP PILOTS");
 	drawMenuTalkText(dst, pitch, width, height, getRebel1MenuCenteredX(titleW), 10, "TOP PILOTS");
@@ -1028,7 +1016,6 @@ void InsaneRebel1::renderHighScoresOverlay(byte *dst, int pitch, int width, int
 		const int y = 25 + i * 14;
 		// Name (left side)
 		drawFontBankString(dst, pitch, width, height, 40, y, _highScores[i].name);
-		// Score + difficulty glyph (right side) ??? original format "<%ld %c"
 		// Difficulty byte 0/1/2 + 0x7B = '{','|','}' tech font glyphs (easy/normal/hard)
 		char scoreLine[32];
 		Common::sprintf_s(scoreLine, "<%ld %c",
@@ -1039,7 +1026,6 @@ void InsaneRebel1::renderHighScoresOverlay(byte *dst, int pitch, int width, int
 }
 
 void InsaneRebel1::renderOptionsOverlay(byte *dst, int pitch, int width, int height) {
-	// --- Options submenu (matching original RunGameOptionsMenu) ---
 	_optTextEnabled = ConfMan.getBool("subtitles");
 	_optVolume = CLIP<int>(ConfMan.getInt("music_volume") / 2, 0, 127);
 
@@ -1078,7 +1064,6 @@ void InsaneRebel1::renderOptionsOverlay(byte *dst, int pitch, int width, int hei
 }
 
 void InsaneRebel1::renderLevelSelectOverlay(byte *dst, int pitch, int width, int height) {
-	// --- Extra level select submenu, styled like the original frontend menus ---
 	const int titleW = getFontBankStringWidth("LEVEL SELECT");
 	drawMenuTitleText(dst, pitch, width, height, getRebel1MenuCenteredX(titleW), 15, "LEVEL SELECT");
 
@@ -1129,7 +1114,6 @@ void InsaneRebel1::renderLevelSelectOverlay(byte *dst, int pitch, int width, int
 }
 
 void InsaneRebel1::renderMainMenuItems(byte *dst, int pitch, int width, int height) {
-	// --- Main menu ---
 	const char *const kMenuItems[kRA1MainMenuItemCount] = {
 		"START NEW GAME",
 		"GAME OPTIONS",
@@ -1418,7 +1402,6 @@ int InsaneRebel1::runLevelSelectMenu() {
 }
 
 void InsaneRebel1::showHighScores() {
-	// Original plays O1SCORE.ANM with TOP PILOTS overlay, dismissable by any key.
 	_highScoresActive = true;
 	_menuActive = true;
 	_menuFrameCounter = 0;
diff --git a/engines/scumm/insane/rebel1/rebel.cpp b/engines/scumm/insane/rebel1/rebel.cpp
index 6a2ce686fb6..afee7436c2a 100644
--- a/engines/scumm/insane/rebel1/rebel.cpp
+++ b/engines/scumm/insane/rebel1/rebel.cpp
@@ -187,8 +187,6 @@ void InsaneRebel1::loadTuningForLevel(int level) {
 	_tuning.levelPts = kTuningTable[l][d][10];
 	_tuning.bonus    = kTuningTable[l][d][11];
 	_tuning.flags    = kTuningTable[l][d][12];
-	// initLevelFromTuning (0x13E7B) writes the 16-bit tuning flags word across
-	// 0x75FE/0x75FF, so we must preserve both bytes.
 	resetGameplayFlagsFromTuning();
 	_protectedTargetA = 0;
 	_protectedTargetB = 0;
@@ -238,7 +236,6 @@ InsaneRebel1::InsaneRebel1(ScummEngine_v7 *scumm) : Insane(), _vm(scumm) {
 	_turretFrameShipCenterValid = false;
 	_driftParam = 0;
 
-	// Start new games on the least punishing original tuning by default.
 	_difficulty = 0;
 	loadTuningForLevel(0);
 
@@ -359,7 +356,6 @@ InsaneRebel1::InsaneRebel1(ScummEngine_v7 *scumm) : Insane(), _vm(scumm) {
 	_optRapidFire = true;
 	_optVolume = _vm->_mixer->getVolumeForSoundType(Audio::Mixer::kMusicSoundType) * 127 / Audio::Mixer::kMaxChannelVolume;
 
-	// Default high scores ??? from DS:0x1D0/0x298/0x2C0
 	const struct { const char *name; int32 score; byte difficulty; } kDefaultScores[kHighScoreCount] = {
 		{"Vince",   10000, 2}, {"Tamlynn",  9000, 2}, {"Chip",    8000, 2},
 		{"Brett",    7000, 1}, {"Casey",    6000, 1}, {"Justin",  5000, 1},
@@ -434,7 +430,6 @@ InsaneRebel1::InsaneRebel1(ScummEngine_v7 *scumm) : Insane(), _vm(scumm) {
 		warning("InsaneRebel1::InsaneRebel1(): failed to load title font bank (TITLFONT/TALKFONT)");
 	}
 
-	// FUN_1CB22 uses "<<" layer markers that resolve to TECHFONT in the original.
 	// Keep a dedicated TECH font bank for targeting markers/lock indicators.
 	if (loadRA1Nut("SYS/TECHFONT.NUT", _techFontBank)) {
 		debugC(DEBUG_INSANE, "InsaneRebel1::InsaneRebel1(): targeting glyph font loaded from SYS/TECHFONT.NUT (%d chars)", _techFontBank.numSprites);
diff --git a/engines/scumm/insane/rebel1/rebel.h b/engines/scumm/insane/rebel1/rebel.h
index 8a17aae30e8..60c078c3779 100644
--- a/engines/scumm/insane/rebel1/rebel.h
+++ b/engines/scumm/insane/rebel1/rebel.h
@@ -51,27 +51,25 @@ enum RA1MenuCommand {
 	kRA1MenuCommandSelect6
 };
 
-// Simple sprite bank for RA1 NUT files (ANIM v1 with odd-alignment padding).
-// Separate from NutRenderer to avoid modifying shared NUT parsing code.
+// Sprite bank for RA1 NUT files.
 struct RA1Sprite {
 	int16 xoffs;
 	int16 yoffs;
 	uint16 width;
 	uint16 height;
-	byte *data;  // Decoded pixel data (width * height bytes, 0 = transparent)
+	byte *data;
 };
 
 struct RA1SpriteBank {
 	int numSprites;
 	RA1Sprite *sprites;
-	byte *decodedData;  // Single allocation for all decoded pixels
+	byte *decodedData;
 	uint32 decodedSize;
 
 	RA1SpriteBank() : numSprites(0), sprites(nullptr), decodedData(nullptr), decodedSize(0) {}
 	~RA1SpriteBank() { delete[] sprites; free(decodedData); }
 };
 
-// RA1 coordinate constants (scaled from RA2's 424x260 ??? 384x242)
 static const int16 kRA1CenterX = 160;
 static const int16 kRA1CenterY = 100;
 static const int16 kRA1MinX = 20;
@@ -83,10 +81,6 @@ static const int16 kRA1FocalY = 25;
 
 int ra1ShotDirection(int16 x1, int16 y1, int16 x2, int16 y2);
 
-/**
- * Star Wars: Rebel Assault (RA1) game logic.
- * Adapts RA2 Handler 7 (ship flight) physics for RA1's 384x242 resolution.
- */
 class InsaneRebel1 : public Insane, public Common::EventObserver {
 public:
 	InsaneRebel1(ScummEngine_v7 *scumm);
@@ -107,9 +101,7 @@ public:
 	void handleGameChunk(int32 subSize, Common::SeekableReadStream &b,
 		byte *renderBitmap = nullptr, int width = 0, int height = 0);
 	bool isInteractiveVideoActive() const { return _interactiveVideoActive; }
-	// True on touchscreen devices (e.g. Android). RA1 skips DOS-style cursor
-	// warping/locking there; direct touch uses absolute aiming, while on-screen
-	// gamepad/joystick input continues through the joystick path.
+	// Touch devices use absolute aiming instead of cursor locking.
 	bool isTouchscreenActive() const;
 	void setFrameHasGameChunk(bool hasGameChunk) { _frameHasGameChunk = hasGameChunk; }
 	void setCurrentSmushFrame(int32 frame);
@@ -138,28 +130,20 @@ public:
 		int codec, uint8 &ra1Param);
 	void resetFrameObjectState();
 
-	// Game flow (matching original at 0x15597)
 	void runGame();
 	Common::Error saveGameState(int slot, const Common::String &desc, bool isAutosave = false);
 	Common::Error loadGameState(int slot, bool startupLoad = false);
 	bool shouldAbortGameFlow() const { return _vm->shouldQuit() || _loadRequested; }
 
 private:
-	// Intro sequence: O1LOGO ??? O1OPEN (0x155ef-0x158f8)
 	void playIntroSequence();
 	void clearVideoBuffer();
 
-	// Main menu loop on O1OPTION.ANM background (0x15968)
-	// Returns: 1=Start New Game, 2=Game Options, 3=Enter Passcode,
-	// 4=Level Select, 5=Continue Demo, 6=Exit
 	int runMainMenu();
 	int runLevelSelectMenu();
 
-	// Level 1 flow (0x16100): hangar ??? CU1 ??? gameplay ??? CU2 ??? turret ??? end
-	// Returns true if level completed, false if player quit
 	bool runLevel1();
 
-	// Level 2 flow: NEW ??? INTRO ??? PLAY (asteroid dodge) ??? END/DEATH
 	bool runLevel2();
 	bool runLevel3();
 	bool runLevel4();
@@ -175,8 +159,7 @@ private:
 	bool runLevel14();
 	bool runLevel15();
 
-	// Play a passive cinematic (no game callback, skippable)
-	// startFrame > 0: fast-forward (decode without display) to that frame
+	// Passive, skippable cinematic playback.
 	void playCinematic(const char *filename, int32 startFrame = 0);
 	void playLevelTransitionCutscene(int level);
 	void playChapterCompleteCinematic(const char *filename, int16 unlockedChapter,
@@ -206,7 +189,6 @@ private:
 	void resetLevelAttemptState(int16 flyControlMode, int16 gameplayPhase,
 		int16 shipDirIndex = 17, bool resetAxisDeltaX = false);
 
-	// Play interactive gameplay video (with ship physics + HUD)
 	void playInteractiveVideo(const char *filename, int32 startFrame = 0);
 	void resetInteractiveVideoAudio();
 	void preserveInteractiveVideoAudioState();
@@ -279,8 +261,6 @@ private:
 	bool areLevel14Phase1TargetsDestroyed() const;
 	bool areLevel14Phase2TargetsDestroyed() const;
 
-	// Shooting pipeline ??? FUN_1CCA0 (0x1CCA0) shot spawner,
-	// FUN_1C0EF (0x1C0EF) target detection, FUN_1C940 (0x1C940) shot processing
 	bool isTorpedoModeActive() const;
 	void processShot();
 	void checkTargetHit(int16 targetIdx, int16 left, int16 top, int16 right, int16 bottom);
@@ -303,44 +283,35 @@ private:
 
 	ScummEngine_v7 *_vm;
 
+	// Sprite and font banks.
 	RA1SpriteBank _shipBank;
-	RA1SpriteBank _shipBankAlt; // Secondary ship bank (e.g. L1BANK2 mode-2 sprites)
-	RA1SpriteBank _displayBank;   // SYS/DISPLAY.NUT ??? bottom status bar
-	RA1SpriteBank _titleFontBank; // SYS/TITLFONT.NUT ??? default subtitle/title layer
-	RA1SpriteBank _hudFontBank;   // RA1 HUD text glyphs (TECHFONT/TALKFONT via RA1 loader)
-	RA1SpriteBank _techFontBank;  // SYS/TECHFONT.NUT ??? targeting glyph layer ("<<" markers)
-	RA1SpriteBank _bangBank;      // LxBANG.NUT ??? impact/explosion sprites (10 frames)
-	RA1SpriteBank _laserBank;     // LxLASER.NUT ??? laser/shot effect sprites
-	RA1SpriteBank _enemyLaserBank; // LxLASR2.NUT ??? incoming projectile sprites
-	SmushFont *_menuFont;         // Use engine text renderer for correct TALKFONT character mapping
-
-	// RA1 screen dimensions (384x242)
+	RA1SpriteBank _shipBankAlt;
+	RA1SpriteBank _displayBank;
+	RA1SpriteBank _titleFontBank;
+	RA1SpriteBank _hudFontBank;
+	RA1SpriteBank _techFontBank;
+	RA1SpriteBank _bangBank;
+	RA1SpriteBank _laserBank;
+	RA1SpriteBank _enemyLaserBank;
+	SmushFont *_menuFont;
+
 	int _screenWidth;
 	int _screenHeight;
 
-	// Ship screen position = kCenter + (accumulator >> 8)
-	// Original: _DAT_74B6/_74B8 (base=160,100) + _DAT_74BA/_74BC (offset)
+	// Gameplay input and projection state.
 	int16 _shipPosX;
 	int16 _shipPosY;
-	// GAME opcode 0x09 uses a separate aim cursor; other handlers target via _shipPos.
 	int16 _flightAimX;
 	int16 _flightAimY;
-
-	// Direction sprite index (5x7 grid = 35 sprites, vDir*7 + hDir)
 	int16 _shipDirIndex;
 
-	// Corridor boundaries (set by GAME opcode 0x0D, computed as left+width, top+height)
 	int16 _corridorLeftX;
 	int16 _corridorTopY;
 	int16 _corridorRightX;
 	int16 _corridorBottomY;
 
-	// Physics state (accumulator-based, matching FUN_1DEB5)
-	// _74CA: horizontal roll accumulator, driven by input * roll_tuning
 	int32 _rollAccum;
-	// _74CE: vertical smoothing, exponential decay toward -inputY
 	int32 _liftSmooth;
-	// _74C2/_74C6: position accumulators (32-bit), pixel offset = accum >> 8
 	int32 _posAccumX;
 	int32 _posAccumY;
 	int16 _turretFrameShipOffsetX;
@@ -349,31 +320,28 @@ private:
 	int16 _turretFrameShipCenterY;
 	bool _turretFrameShipCenterValid;
 
-	// Per-frame drift bias from GAME 0x07 field3
 	int16 _driftParam;
 
-	// Perspective view offsets (0x74B6/0x74B8: viewport scroll base)
 	int16 _perspectiveX;
 	int16 _perspectiveY;
 	static const int kProjectionTableSize = 80;
 	int16 _projectionCurveExtent;
 	int16 _projectionTable[kProjectionTableSize];
 
-	// Input history buffers for 0x0B handler (FUN_1CDA7) ??? 10-frame averaging
 	static const int kInputHistorySize = 10;
-	int16 _inputHistoryX[kInputHistorySize];  // 0x7580: horizontal input history
-	int16 _inputHistoryY[kInputHistorySize];  // 0x7594: vertical input history
-	int16 _viewHistoryX[kInputHistorySize];   // 0x75A8: viewport horizontal history
-	int16 _viewHistoryY[kInputHistorySize];   // 0x75BC: viewport vertical history
-	int16 _inputAxisDeltaX; // Current 0x0B horizontal input sample, before history averaging
-	int16 _avgInputX;    // smoothed horizontal input (clamped to [-0xA0, 0xA0])
-	int16 _avgInputY;    // smoothed vertical input (clamped to [-0x46, 0x41])
-	int16 _joystickAxisX;   // Rebel-specific left-stick X captured from keymapper axis events
-	int16 _joystickAxisY;   // Rebel-specific left-stick Y captured from keymapper axis events
+	int16 _inputHistoryX[kInputHistorySize];
+	int16 _inputHistoryY[kInputHistorySize];
+	int16 _viewHistoryX[kInputHistorySize];
+	int16 _viewHistoryY[kInputHistorySize];
+	int16 _inputAxisDeltaX;
+	int16 _avgInputX;
+	int16 _avgInputY;
+	int16 _joystickAxisX;
+	int16 _joystickAxisY;
 	uint32 _lastJoystickAxisEventTime;
-	int16 _level2JoystickFilteredX; // Smoothed Level 2 analog X input
-	int16 _level2JoystickFilteredY; // Smoothed Level 2 analog Y input
-	int16 _gamepadAimAxisX; // Relative gamepad 0x0B aim in preprocessed input space
+	int16 _level2JoystickFilteredX;
+	int16 _level2JoystickFilteredY;
+	int16 _gamepadAimAxisX;
 	int16 _gamepadAimAxisY;
 	bool _gamepadAimActive;
 	// Suppress delayed lock/warp mousemove artifacts after centering interactive gameplay.
@@ -397,22 +365,21 @@ private:
 	void updateOnFootSequence();
 	void updateOnFootAimVariant();
 	void finishOnFootFrame();
-	int16 _onFootCharX;      // Character draw X (g_shipOffsetX in original)
-	int16 _onFootCharY;      // Character draw Y (g_shipOffsetY in original)
-	int16 _onFootAnimCounter; // DAT_0000828a: fire animation counter
-	bool _onFootInitialized;  // First-frame init flag for 0x19
+	int16 _onFootCharX;
+	int16 _onFootCharY;
+	int16 _onFootAnimCounter;
+	bool _onFootInitialized;
 
-	// Current level index (0-based: 0=LVL1, 1=LVL2, etc.)
+	// Runtime level state.
 	int _currentLevel;
-	int _resumeLevel;             // 1-based level used when saving/restoring RA1 progress
-	int _activeSaveSlot;          // Last manually saved or loaded slot; -1 means use autosave slot
-	bool _loadRequested;          // Runtime load requested while unwinding the current video flow
+	int _resumeLevel;
+	int _activeSaveSlot;
+	bool _loadRequested;
 
-	// Intro title overlay (RunTwoLineTextSplash from original)
 	bool _introTextActive;
-	int32 _introTextStartFrame;  // revealBaseY: frame at which text begins appearing
-	int32 _introTextEndFrame;    // stopY: frame after which text stops
-	int _introTextLevel;         // index into kLevelTitles
+	int32 _introTextStartFrame;
+	int32 _introTextEndFrame;
+	int _introTextLevel;
 	void beginLevelTitleOverlay(int level);
 	void drawLevelTitleOverlay(byte *dst, int pitch, int width, int height, int32 curFrame, int32 maxFrame);
 
@@ -438,17 +405,13 @@ private:
 		int passwordIndex);
 	void drawChapterSummaryOverlay(byte *dst, int pitch, int width, int height, int32 curFrame, int32 maxFrame);
 
-	// Control mode (from GAME opcode 0x5E)
+	// Interactive GAME chunk state.
 	int16 _flyControlMode;
-	// Mode-2 emitter offsets used by FUN_1D79C when _DAT_75E4 == 2.
 	int16 _turretEmitterLeftX;
 	int16 _turretEmitterLeftY;
 	int16 _turretEmitterRightX;
 	int16 _turretEmitterRightY;
-	// Last per-frame GAME opcode observed in the current playback stream.
-	// Kept for legacy call sites; frame-accurate dispatch uses _frameGameOpcodeMask.
 	uint16 _activeGameOpcode;
-	// GAME opcodes pre-scanned from the current frame before FOBJ dispatch.
 	uint32 _frameGameOpcodeHintMask;
 	uint32 _frameGameOpcodeMask;
 	bool _frameHasGameChunk;
@@ -456,62 +419,58 @@ private:
 	bool _gameOp0BPhysicsUpdatedThisFrame;
 	bool _gameOp0BOverlayRenderedThisFrame;
 
-	// Difficulty (0=easy, 1=normal, 2=hard) ??? matches original DAT_22BC
 	int _difficulty;
 
-	// Per-difficulty tuning (from assault_data_3.bin, indexed: difficulty * 0x28B + level * 0x1F)
-	// Original game loads from C:\rebltune.txt; use a hardcoded table.
 	// 21 sub-levels (1A,1B,2,3,4A,4B,5A,5B,6,7,8,9A,9B,10,11,12,13,14A,14B,15A,15B)
 	struct TuningParams {
-		int16 roll;      // +0x05: horizontal speed/sensitivity
-		int16 lift;      // +0x07: vertical speed/sensitivity
-		int16 slide;     // +0x09: cross-axis coupling
-		int16 drift;     // +0x0B: drift/turbulence multiplier
-		int16 snap;      // +0x0D: hit radius for shooting targets
-		int16 miss;      // +0x0F: light/scripted damage; FUN_1CDA7 uses it for bit 0x40
-		int16 wham;      // +0x11: medium/contact damage; FUN_1CDA7 uses it for bit 0x20
-		int16 shot;      // +0x13: heavy/projectile damage; FUN_1CDA7 uses it for bit 0x80
-		int16 kill;      // +0x15: score per target kill
-		int16 time;      // +0x17: survival bonus (added every 32 frames)
-		int16 levelPts;  // +0x19: chapter completion bonus (RunChapterCompleteSummaryScreen)
-		int16 bonus;     // +0x1B: per-level bonus multiplier for kills/accuracy
-		int16 flags;     // +0x1D: level behavior flags (loaded into DAT_75FE at level init)
+		int16 roll;
+		int16 lift;
+		int16 slide;
+		int16 drift;
+		int16 snap;
+		int16 miss;
+		int16 wham;
+		int16 shot;
+		int16 kill;
+		int16 time;
+		int16 levelPts;
+		int16 bonus;
+		int16 flags;
 	};
 	TuningParams _tuning;
 
 	void loadTuningForLevel(int level);
 	void resetGameplayFlagsFromTuning();
 
-	// Damage system (from Ghidra decompilation of FUN_1DEB5)
-	int16 _health;               // 0x7560: current health (init=98, negative=dead, max=98)
-	int16 _lives;                // 0x7562: remaining extra lives
-	int _score;                  // 0x7564: current score
-	int _prevScore;              // 0x8288: previous score (for extra life bonus at 10k intervals)
-	byte _damageFlags;           // 0x74D4: per-frame collision bitmask (cleared each frame)
-	byte _prevDamageFlags;       // 0x74D6: previous frame's damage flags (for explosion direction)
-	uint16 _gameLatch5D;         // 0x75D2: GAME 0x5D latch (scene/obstacle/event trigger)
-	uint16 _gameLatch5F;         // 0x75D4: GAME 0x5F latch (probabilistic hit trigger)
-	int16 _damageCooldown;       // 0x74D8: invulnerability timer (10 frames after hit)
-	int16 _deathTimer;           // 0x756A: death animation countdown (30 on death)
-	int16 _screenFlash;          // 0x7736: screen flash timer on hit
+	int16 _health;
+	int16 _lives;
+	int _score;
+	int _prevScore;
+	byte _damageFlags;
+	byte _prevDamageFlags;
+	uint16 _gameLatch5D;
+	uint16 _gameLatch5F;
+	int16 _damageCooldown;
+	int16 _deathTimer;
+	int16 _screenFlash;
 	uint32 _frameCounter;        // Gameplay handler frame accumulator
-	int32 _currentSmushFrame;    // Current frontend/movie frame (original DAT_7740)
-	bool _screenShakeEnabled;    // 0x41AC: when true, SetCameraOffset adds ??2 random jitter
+	int32 _currentSmushFrame;
+	bool _screenShakeEnabled;
 	byte _screenFlashBasePalette[0x300];
 	bool _screenFlashBasePaletteValid;
-	byte _deathCauseIndicator;   // 0x772E: non-zero = player died; selects death animation variant
-	byte _hudRenderFlag;         // 0x7600: 0xFF when HUD should render (set by combat mode handlers)
-	byte _hudDirtyFlag;          // 0x7601: 0xFF after HUD redraw (set by renderHUD)
-	int16 _maxChapterUnlocked;   // 0x7730: highest unlocked passcode slot (0=none)
-	bool _unlockAllLevels;       // Option: expose level select without passcodes
-	bool _noDamage;              // Option: suppress player damage
+	byte _deathCauseIndicator;
+	byte _hudRenderFlag;
+	byte _hudDirtyFlag;
+	int16 _maxChapterUnlocked;
+	bool _unlockAllLevels;
+	bool _noDamage;
 
 	static const int16 kMaxHealth = 98;
 	static const int16 kDeathTimerInit = 30;
 	static const int16 kDamageCooldownInit = 10;
 	enum { kNumLevels = 15 };
 
-	// Streamed SMUSH audio
+	// Streamed SMUSH audio.
 	RebelAudio _audio;
 	bool _restoreInteractiveVideoAudioState;
 	int16 _savedInteractiveVideoTrackState[SMUSH_MAX_TRACKS];
@@ -532,33 +491,29 @@ private:
 	uint32 _sfxSize[kNumSfx];
 	Audio::SoundHandle _sfxHandles[kNumSfx];
 
-	// True only while an interactive gameplay SMUSH is running.
 	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).
-	// We check ship position at that counter value to decide left vs right path.
-	static const int32 kPathBranchCounter = 394;  // GAME 0x07 field1 value
-	int32 _gameCounter;          // GAME chunk field1/logical counter
-	bool _pathBranchEnabled;     // True when branching is active for this video
-	bool _rightPathSelected;     // True if player branched into L1PLAY1R
-	int _levelRouteIndex;        // Current mid-level route/segment for branching levels
-	int _pendingRouteIndex;      // Next route requested by original frame-branch logic
-	int32 _pendingRouteStartFrame; // Resume/frame-gate target for branch-driven route switches
-	int32 _pendingRouteCutoverFrame; // Delayed inline route splice frame (branchFrame + 7)
-	int32 _pendingRouteVideoStartFrame; // L7 destination ANM-local frame after gate adjustment
-	int16 _level7WarningFrames;  // RunLevel7Flow local_38: 30-frame branch warning countdown
-	int16 _level7WarningThreshold; // Collapses RunLevel7Flow local_40 into its DAT_2361 threshold
-	int _levelGameplayPhase;     // Level-local interactive phase (e.g. LVL4 PLAY1 vs PLAY2)
-	// RunLevel14Flow queues L14PLY2B at L14PLAY2 maxFrame-0x0F.
+	static const int32 kPathBranchCounter = 394;
+	int32 _gameCounter;
+	bool _pathBranchEnabled;
+	bool _rightPathSelected;
+	int _levelRouteIndex;
+	int _pendingRouteIndex;
+	int32 _pendingRouteStartFrame;
+	int32 _pendingRouteCutoverFrame;
+	int32 _pendingRouteVideoStartFrame;
+	int16 _level7WarningFrames;
+	int16 _level7WarningThreshold;
+	int _levelGameplayPhase;
 	bool _level14Play2BSplicePending;
 	bool _level14Play2BSpliced;
 	int32 _level14Play2BSpliceFrame;
 
-	// Main menu / options state
+	// Main menu and options state.
 	void runOptionsMenu();
 	int runPasscodeEntryDialog();
 	bool runHighScoreNameEntry();
@@ -580,35 +535,32 @@ private:
 	const char *getChapterCompletePassword(int passwordIndex) const;
 	bool _menuActive;
 	bool _menuConfirmed;
-	int _menuSelection; // Visible main-menu row; mapped to return values by getMainMenuResultForSelection().
+	int _menuSelection;
 	int _menuFrameCounter;
 
-	// Options submenu state ??? RunGameOptionsMenu (0x14B42)
 	static const int kOptionsItemCount = 9;
-	bool _optionsActive;     // True when showing options instead of main menu
-	int _optionsSel;         // 0..8 selected option row
-	bool _levelSelectActive; // True when showing level-select submenu
-	int _levelSelectSel;     // 0=Level1 ... N-1=Back
-	int _startLevel;         // 1-based start level for "Start New Game"
-
-	// Per-option state (matching original RunGameOptionsMenu globals)
-	bool _optRookieOneFemale; // DAT_22c3: Rookie One gender
-	bool _optMusicEnabled;    // DAT_22b7: music on/off
-	bool _optSfxEnabled;      // DAT_22b8: sfx+voice on/off
-	bool _optTextEnabled;     // DAT_22b9: dialogue text on/off
-	bool _optControlsYFlip;   // DAT_22be: Y-axis inversion
-	bool _optRapidFire;       // Option: held fire keeps shooting
-	int  _optVolume;          // DAT_22c1: master volume 0..127
-
-	// High scores / TOP PILOTS display ??? data at DS:0x1D0
+	bool _optionsActive;
+	int _optionsSel;
+	bool _levelSelectActive;
+	int _levelSelectSel;
+	int _startLevel;
+
+	bool _optRookieOneFemale;
+	bool _optMusicEnabled;
+	bool _optSfxEnabled;
+	bool _optTextEnabled;
+	bool _optControlsYFlip;
+	bool _optRapidFire;
+	int  _optVolume;
+
 	static const int kHighScoreCount = 10;
 	struct HighScoreEntry {
-		char name[20];   // 0x14 bytes per entry (includes '<' prefix)
+		char name[20];
 		int32 score;
-		byte difficulty;  // 0/1/2 ??? tech font glyph '{','|','}' (easy/normal/hard)
+		byte difficulty;
 	};
 	HighScoreEntry _highScores[kHighScoreCount];
-	bool _highScoresActive;  // True when showing TOP PILOTS overlay
+	bool _highScoresActive;
 	void showHighScores();
 	int getMenuTalkTextWidth(const char *text);
 	void drawMenuTalkText(byte *dst, int pitch, int width, int height, int x, int y, const char *text);
@@ -618,7 +570,7 @@ private:
 	void renderLevelSelectOverlay(byte *dst, int pitch, int width, int height);
 	void renderMainMenuItems(byte *dst, int pitch, int width, int height);
 
-	// Character picker shared by RunPasscodeEntryDialog and RunHighScoreNameEntry.
+	// Character picker shared by passcode and high-score entry.
 	static const int kTextEntryBufferSize = 20;
 	bool _textEntryActive;
 	bool _textEntryPasscodeMode;
@@ -631,56 +583,52 @@ private:
 	int _highScoreEntryIndex;
 	char _textEntryBuffer[kTextEntryBufferSize];
 
-	// Shooting state ??? FUN_1CCA0 (0x1CCA0)
-	bool _playerFired;       // 0x7570: current fire-button state
-	// 3DO ControlB/second held button used by L9 on-foot controls.
+	bool _playerFired;
+	// Secondary 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
-	uint16 _gameplayFlags75ff; // 0x75FF: targeting / shot-style flags
+	int16 _fireCooldown;
+	int16 _rapidFirePhase;
+	uint16 _gameplayFlags75fe;
+	uint16 _gameplayFlags75ff;
 
-	// Explosion shot slots (2 slots) ??? FUN_1CCA0 (0x1CCA0)
 	static const int kMaxShotSlots = 2;
 	struct ShotSlot {
-		int16 timer;     // 0x75E6: countdown (5 or 2, 0=inactive)
-		int16 posX;      // 0x75F2: cursor X at time of shot
-		int16 posY;      // 0x75F6: cursor Y at time of shot
-		int16 centerX;   // 0x75EA: perspective-adjusted X
-		int16 centerY;   // 0x75EE: perspective-adjusted Y
-		int16 variant;   // 0x75FA: emitter table selector (DAT_241F snapshot)
+		int16 timer;
+		int16 posX;
+		int16 posY;
+		int16 centerX;
+		int16 centerY;
+		int16 variant;
 	};
 	ShotSlot _shotSlots[kMaxShotSlots];
-	int16 _shotAlternator;   // 0x241F: alternates between 0/1
-	bool _shotSideToggle;    // 0x2423: 0x0B side-toggle for mode-1 beam emitters
-
-	// Targeting state ??? FUN_1C0EF (0x1C0EF)
-	int16 _targetProximity;  // 0x7558: 0=none, 1=near, 2=on-target
-	int16 _prevTargetProx;   // 0x755A: previous frame's proximity
-	int16 _targetAnimCounter; // 0x755C: lock-marker animation counter
-	int16 _targetCount;      // 0x7552: active targets this frame
-	int16 _prevTargetCount;  // 0x7554: previous frame target count
+	int16 _shotAlternator;
+	bool _shotSideToggle;
+
+	int16 _targetProximity;
+	int16 _prevTargetProx;
+	int16 _targetAnimCounter;
+	int16 _targetCount;
+	int16 _prevTargetCount;
 	static const int kMaxTargetBoxes = 20;
-	int16 _targetBoxX[kMaxTargetBoxes];       // 0x74DA: per-target overlay X
-	int16 _targetBoxY[kMaxTargetBoxes];       // 0x7502: per-target overlay Y
-	int16 _targetBoxVariant[kMaxTargetBoxes]; // 0x752A: size/near bucket ('i' + bucket)
+	int16 _targetBoxX[kMaxTargetBoxes];
+	int16 _targetBoxY[kMaxTargetBoxes];
+	int16 _targetBoxVariant[kMaxTargetBoxes];
 
-	// GOST hit animation slots (10 slots) ??? FUN_1C9CD (0x1C9CD)
 	static const int kMaxGostSlots = 10;
 	struct GostSlot {
-		int16 targetId;  // 0x23C3: target identifier (0=empty)
-		int16 frame;     // 0x23D7: animation frame (0-9, >=10 = done)
-		int16 posX;      // 0x239B: screen X
-		int16 posY;      // 0x23AF: screen Y
+		int16 targetId;
+		int16 frame;
+		int16 posX;
+		int16 posY;
 	};
 	GostSlot _gostSlots[kMaxGostSlots];
-	int16 _gostSlotIdx;      // 0x23EB: next slot to write (circular 0-9)
+	int16 _gostSlotIdx;
 
-	int16 _killCount;        // 0x75D0: targets destroyed this stage
-	int16 _lastHitTarget;    // 0x75D6: recent-kill latch, allows at most one hit per frame
-	bool _frameObjectHitRevealPending; // DispatchSmushFrameChunks local_14 high-id reveal latch
+	int16 _killCount;
+	int16 _lastHitTarget;
+	bool _frameObjectHitRevealPending;
 
-	// Incoming enemy projectile slots used by Level 13 RunLevel13Flow.
+	// Incoming enemy projectile slots.
 	static const int kMaxEnemyShotSlots = 5;
 	struct EnemyShotSlot {
 		int16 timer;
@@ -693,31 +641,25 @@ private:
 	};
 	EnemyShotSlot _enemyShotSlots[kMaxEnemyShotSlots];
 
-	// Protected target IDs ??? 0x7732/0x7734 in original
 	// Targets listed here can be hit repeatedly (no event mask toggle).
 	// Used by Level 4 (shield generators) and Level 15 (torpedo targets).
-	int16 _protectedTargetA; // 0x7732: protected objectId+1 (0=none)
-	int16 _protectedTargetB; // 0x7734: protected objectId+1 (0=none)
+	int16 _protectedTargetA;
+	int16 _protectedTargetB;
 
-	// Per-target hit counters for shield generator tracking (Level 4)
-	int16 _shieldGenHitsA;   // Hits on _protectedTargetA
-	int16 _shieldGenHitsB;   // Hits on _protectedTargetB
-	int16 _level5SuccessFramesRemaining; // DOS RunLevel5Flow: 20-frame hold after the third kill
-	int16 _level14SuccessFrames; // RunLevel14Flow: 60-frame hold after required targets are destroyed
+	// Per-target hit counters for shield generator tracking.
+	int16 _shieldGenHitsA;
+	int16 _shieldGenHitsB;
+	int16 _level5SuccessFramesRemaining;
+	int16 _level14SuccessFrames;
 
-	// Level 15 torpedo success latch. The original derives this from
-	// g_gameplayPhaseFlags bit 1, which is _frameObjectState primary byte 0 bit 0x02.
 	bool _torpedoFired;
 
-	// Level 8 walker-specific state ??? RunLevel8Flow (0x18546)
-	int16 _walkerHealth;     // Walker health percentage (0-100), init=100
-	int16 _walkerTimer;      // Attack window countdown (100???0)
-	int16 _walkerBranchChoice; // Directional choice: 0=none, 1=left, 2=right
-	bool _walkerRoundReplay; // Preserve walker damage across a replayed L8 route
+	int16 _walkerHealth;
+	int16 _walkerTimer;
+	int16 _walkerBranchChoice;
+	bool _walkerRoundReplay;
 
-	// Attack window frame numbers per route (3 routes ?? 3 windows)
-	// Route 0: 2588/1709/262, Route 1: 2323/1444/-2, Route 2: 877/-2/-2
-	// -2 = disabled (no window at that slot for this route)
+	// Attack window frame numbers per route.
 	static const int16 kWalkerAttackWindow1[3];
 	static const int16 kWalkerAttackWindow2[3];
 	static const int16 kWalkerAttackWindow3[3];
@@ -726,6 +668,6 @@ private:
 	byte _frameObjectState[kFrameObjectStateBytes];
 };
 
-} // End of namespace Scumm
+} // namespace Scumm
 
 #endif
diff --git a/engines/scumm/insane/rebel1/render.cpp b/engines/scumm/insane/rebel1/render.cpp
index 8e031771cbf..cad22408e14 100644
--- a/engines/scumm/insane/rebel1/render.cpp
+++ b/engines/scumm/insane/rebel1/render.cpp
@@ -47,10 +47,7 @@ int ra1GameplayWindowOffsetX(const InsaneRebel1 *rebel1) {
 	if (!rebel1 || !rebel1->isInteractiveVideoActive())
 		return 0;
 
-	// Ship/cursor/shot coordinates are in DOS's 320x200 gameplay window.
-	// FUN_224FD crop emulation shifts them into the 384x242 source
-	// buffer so the final source-window crop presents them at the same screen
-	// position DOS used for gameplay and collision.
+	// Draw gameplay overlays into the cropped buffer's coordinate space.
 	switch (rebel1->getEffectiveGameOpcode()) {
 	case 0x07:
 	case 0x08:
@@ -159,7 +156,6 @@ const RA1Sprite *lookupBankGlyph(const RA1SpriteBank &bank, char ch) {
 	return &glyph;
 }
 
-// Glyph markers in FUN_1C940/FUN_1CB22 go through DrawStringEx(..., flags=3),
 // which centers the glyph and ignores the NUT x/y offsets. Use the same anchor
 // rules here instead of the generic left-anchored text path.
 void drawCenteredBankGlyph(const RA1SpriteBank &bank, byte *dst, int pitch, int width, int height,
@@ -202,8 +198,6 @@ int getBankStringWidth(const RA1SpriteBank &bank, const char *text) {
 	return w;
 }
 
-// Approximate FUN_221B7/FUN_20BD3 space-advance behavior from available NUT glyphs.
-// The original reads per-font space width from metadata tables and caps it to 8.
 int getBankSpaceAdvance(const RA1SpriteBank &bank) {
 	const int exclWidth = getBankStringWidth(bank, "!");
 	if (exclWidth <= 0)
@@ -247,7 +241,6 @@ void drawCenteredRebel1String(InsaneRebel1 *rebel1, byte *dst, int pitch, int wi
 }
 
 int getBankSpaceHeight(const RA1SpriteBank &bank) {
-	// In FUN_221B7 line advance is derived from the layer's space-glyph height (+4).
 	// With current NUT decoding we approximate that using the '!' glyph (index 0).
 	if (bank.numSprites > 0) {
 		const RA1Sprite &glyph = bank.sprites[0];
@@ -257,7 +250,6 @@ int getBankSpaceHeight(const RA1SpriteBank &bank) {
 	return 8;
 }
 
-// FUN_1C794: direction bucket in range -4..4 from two points.
 int ra1ShotDirection(int16 x1, int16 y1, int16 x2, int16 y2) {
 	int dx = x2 - x1;
 	int dy = y1 - y2;
@@ -290,7 +282,6 @@ int ra1ShotDirection(int16 x1, int16 y1, int16 x2, int16 y2) {
 	return -4;
 }
 
-// FUN_1CDA7 maps abs(FUN_1C794) to sprite base index: <=1 -> 0, ==2 -> 5, else -> 10.
 int ra1ShotDirectionBucket(int dir) {
 	const int absDir = ABS(dir);
 	if (absDir <= 1)
@@ -300,8 +291,7 @@ int ra1ShotDirectionBucket(int dir) {
 	return 10;
 }
 
-// QuantizeDirectionWithAxisFlags from RunLevel13Flow: returns one of nine
-// direction sprites and publishes DrawFobjGlyph flip flags.
+// Return one of nine direction sprites and publish glyph flip flags.
 int ra1ProjectileDirectionWithFlags(int16 srcX, int16 srcY, int16 dstX, int16 dstY, uint16 &flags) {
 	int dx = srcX - dstX;
 	int dy = dstY - srcY;
@@ -342,7 +332,6 @@ struct RA1ShotEmitterPair {
 	int16 y2;
 };
 
-// DAT_244A and DAT_251A in ASSAULT.EXE data section, used by FUN_1D79C.
 const RA1ShotEmitterPair kRA1ShotEmitters244A[27] = {
 	{ 11, -11, -11, 0 }, { 16, -9, -16, -1 }, { 20, -6, -19, -3 }, { 20, -5, -21, -4 }, { -20, -6, 20, -5 },
 	{ -18, -9, 16, -1 }, { -13, -11, 13, 0 }, { -7, -13, 8, 2 }, { 1, -10, 3, 2 }, { 11, -16, -11, 4 },
@@ -361,7 +350,6 @@ const RA1ShotEmitterPair kRA1ShotEmitters251A[27] = {
 	{ -15, -14, 16, 5 }, { 0, -38, -14, 37 }
 };
 
-// DAT_25EC/DAT_25F0 and DAT_28BC in ASSAULT.EXE. GAME opcode 0x09 uses these
 // emitter offsets instead of the generic edge-beam fallback.
 const RA1ShotEmitterPair kRA1FlightShotEmitters25EC[45] = {
 	{ -38, -14, 37, 6 }, { 37, -14, 37, 7 }, { 42, -11, -40, 11 }, { -37, -6, 38, 14 }, { -37, -5, 38, 15 },
@@ -399,7 +387,6 @@ const RA1ShotEmitterPair kRA1FlightShotEmitters28BC[45] = {
 	{ -18, 3, 5, -13 }, { -17, 3, 7, -14 }, { -18, 3, 8, -15 }, { -19, 3, 9, -11 }, { -16, 5, 11, -11 }
 };
 
-// Small subset of FUN_20D43 draw flags used by RA1 shot sprites.
 void renderSpriteWithFlags(byte *dst, int pitch, int width, int height,
 	int x, int y, const RA1Sprite &spr, uint32 flags) {
 	if (!spr.data || spr.width <= 0 || spr.height <= 0)
@@ -453,7 +440,6 @@ void renderSpriteWithFlags(byte *dst, int pitch, int width, int height,
 	}
 }
 
-// Helper only: the original keeps this shot-sprite math inline in
 // several GAME handlers. It is collapsed here because the direction/lerp/render
 // sequence is identical for one-beam shot sprites.
 void renderAimedShotSprite(byte *dst, int pitch, int width, int height,
@@ -481,8 +467,7 @@ void renderAimedShotPair(byte *dst, int pitch, int width, int height,
 		start2X, start2Y, targetX, targetY, lerp);
 }
 
-// Helper for the 0x0B fallback edge-beam path. The original keeps
-// the bucket lookup inline with the renderer, but both left/right beams share it.
+// Beam emitters share the same bucketed shot sprite lookup.
 void renderBucketedShotSprite(byte *dst, int pitch, int width, int height,
 		const RA1SpriteBank &laserBank, int startX, int startY, int targetX, int targetY,
 		int lerp, int frame, uint32 flags) {
@@ -502,10 +487,7 @@ void renderBucketedShotSprite(byte *dst, int pitch, int width, int height,
 }
 
 void InsaneRebel1::getTurretShipCenter(int16 &x, int16 &y) const {
-	// Port helper: original FUN_1E6A7 keeps g_perspectiveX/Y at screen center
-	// and draws the ship at g_perspective + g_shipOffset. In this port
-	// _perspectiveX/Y stores the clamped camera offset passed to SetCameraOffset(),
-	// so recover the ship center from the movement accumulators instead.
+	// Recover the ship center from the movement accumulators.
 	if (_turretFrameShipCenterValid) {
 		x = _turretFrameShipCenterX;
 		y = _turretFrameShipCenterY;
@@ -516,8 +498,6 @@ void InsaneRebel1::getTurretShipCenter(int16 &x, int16 &y) const {
 	y = (int16)(kRA1CenterY + (int16)(_posAccumY >> 8));
 }
 
-// procPreRendering ??? Sets viewport window offset (FUN_224FD at 0x224FD).
-// RA1 decodes FOBJs at chunk coordinates, then displays a scrolled 320x200
 // window inside the 384x242 framebuffer.
 void InsaneRebel1::procPreRendering(byte *renderBitmap) {
 	_frameGameOpcodeMask = 0;
@@ -534,8 +514,6 @@ void InsaneRebel1::procPreRendering(byte *renderBitmap) {
 			(_activeGameOpcode == 0x07 || _activeGameOpcode == 0x08 ||
 			 _activeGameOpcode == 0x09 || _activeGameOpcode == 0x0A ||
 			 _activeGameOpcode == 0x0B);
-		// Only gameplay handlers that actually execute FUN_224FD own the scrolling
-		// 320x200 window inside the 384x242 buffer. Interactive movies with no
 		// GAME stream (for example LVL4/L4PLAY2.ANM) keep a static camera.
 		if (usePerspectiveViewport) {
 			ra1Player()->_ra1ViewportOffsetX = _perspectiveX;
@@ -563,9 +541,7 @@ void InsaneRebel1::syncViewportOffset(bool usePerspectiveViewport) {
 	ra1Player()->_ra1ViewportOffsetX = _perspectiveX;
 	ra1Player()->_ra1ViewportOffsetY = _perspectiveY;
 
-	// SetCameraOffset() applies random shake to the camera value used by the
-	// visible source-window origin. Store it once so FTCH restore, overlays, and
-	// the final crop share one viewport origin for this frontend frame.
+	// Store shake-adjusted viewport once so overlays and crop share one origin.
 	if (_screenFlash > 0) {
 		ra1Player()->_ra1ViewportOffsetX += (int16)(_vm->_rnd.getRandomNumber(4) - 2);
 		ra1Player()->_ra1ViewportOffsetY += (int16)(_vm->_rnd.getRandomNumber(4) - 2);
@@ -583,7 +559,7 @@ void InsaneRebel1::procPostRendering(byte *renderBitmap, int32 codecparam, int32
 		renderMainMenuOverlay(renderBitmap, pitch, width, height);
 	}
 
-	// Intro title overlay (RunTwoLineTextSplash) ??? drawn during intro cinematics
+	// Intro title overlay.
 	if (_introTextActive && renderBitmap) {
 		int w = _player ? _player->_width : _screenWidth;
 		int h = _player ? _player->_height : _screenHeight;
@@ -634,17 +610,13 @@ void InsaneRebel1::procPostRendering(byte *renderBitmap, int32 codecparam, int32
 	bool drawShipAfterGameplayOverlays = false;
 	bool drawGameOp0BTargetingAfterFetch = false;
 	if (gameOp0BMode) {
-		// GAME 0x0B scrolling cockpit/surface handler ??? FUN_1CDA7.
 		if (!_gameOp0BPhysicsUpdatedThisFrame) {
 			updateGameOp0BPhysics();
 			_gameOp0BPhysicsUpdatedThisFrame = true;
 			syncViewportOffset(true);
 		}
 
-		// DOS 0x0B loops test health after each frontend frame and leave the
-		// interactive movie as soon as it drops below zero. Mirror that here so
-		// GAME 0x0B chapters transition to their retry/death clips like
-		// the other gameplay families do.
+		// 0x0B chapters transition to retry/death clips as soon as health drops.
 		if (_health < 0) {
 			_fireCooldown = _playerFired ? 1 : 0;
 			_vm->_smushVideoShouldFinish = true;
@@ -664,17 +636,13 @@ void InsaneRebel1::procPostRendering(byte *renderBitmap, int32 codecparam, int32
 		if (onFootSequenceMode)
 			initOnFootSequence();
 
-		// Original LVL9 frames dispatch GAME 0x1A before 0x19. That means the
 		// shot overlay uses the current crosshair/pose and is then covered by the
 		// character DrawFobjGlyph from 0x19; the 0x19 pose update is for the next
 		// frame. Keep that ordering explicit here.
 		if (onFootAimMode) {
 			shotOverlayHandled = true;
 			if (_health >= 0) {
-				// HandleGameOp5A_ObjectOrSceneTrigger can snap g_shipPos to the
-				// target center before the shot overlay runs. The player
-				// defers GAME 0x1A until after FOBJ dispatch, so preserve that
-				// snap instead of immediately replacing it with raw input again.
+				// Preserve target snap until deferred 0x1A input runs.
 				const bool preserveTargetSnap = (_targetProximity == 2 && _tuning.snap > 0);
 				const int16 snappedTargetX = _shipPosX;
 				const int16 snappedTargetY = _shipPosY;
@@ -710,7 +678,6 @@ void InsaneRebel1::procPostRendering(byte *renderBitmap, int32 codecparam, int32
 		}
 	} else {
 		// Dispatch movement path by GAME handler family:
-		//   0x08/0x0A -> FUN_1E6A7/FUN_1D79C (turret/cockpit)
 		//   0x07/0x09 -> flight-family handlers
 		if (turretMode) {
 			updateTurretPhysics();
@@ -732,10 +699,8 @@ void InsaneRebel1::procPostRendering(byte *renderBitmap, int32 codecparam, int32
 		drawShipAfterGameplayOverlays = (flightMode || turretMode);
 	}
 
-	// GAME handlers in the original update FUN_224FD during the same frame that
-	// the new control state is computed. Sync the current frame's viewport window
-	// before HUD/screen copy so overlays and final crop observe the same camera.
-	// On-foot mode uses SetCameraOffset(0,0) ??? no viewport crop.
+		// Sync viewport before HUD/screen copy so overlays and crop share the camera.
+		// On-foot mode uses no viewport crop.
 	if (_player) {
 		if (onFootMode || (!gameOp0BMode && !turretMode && !flightMode)) {
 			syncViewportOffset(false);
@@ -744,9 +709,7 @@ void InsaneRebel1::procPostRendering(byte *renderBitmap, int32 codecparam, int32
 		}
 	}
 
-	// Assembly dispatch (FUN_1BE1B) only runs the targeting/shot overlay pipeline
-	// in handlers 0x09/0x0A/0x0B/0x1A. LVL1 stage-2 works because the stream emits
-	// both 0x0A and 0x08 in the same frame, not because 0x08 owns the overlay path.
+	// Targeting overlay is owned by handlers 0x09/0x0A/0x0B/0x1A.
 	const bool hasTargetingPipeline =
 		!shotOverlayHandled &&
 		(hasFrameGameOpcode(0x09) || hasFrameGameOpcode(0x0A) ||
@@ -763,12 +726,11 @@ void InsaneRebel1::procPostRendering(byte *renderBitmap, int32 codecparam, int32
 			(allowImplicitGameplayMode && _activeGameOpcode == 0x0A);
 		renderShotOverlayPipeline(renderBitmap, pitch, width, height, true);
 
-		// FUN_1D79C (GAME 0x0A) owns the cursor center in turret/combat mode.
-		// The preceding overlay/shot pass uses the previous frame's cursor; the
-		// handler then publishes the next cursor position from the current
-		// pre-physics ship offset. The following 0x08 handler updates the camera
-		// afterward, so source-space anchors and the final viewport crop
-		// intentionally observe different moments in the frame.
+			// The preceding overlay/shot pass uses the previous frame's cursor; the
+			// handler then publishes the next cursor position from the current
+			// pre-physics ship offset. The following 0x08 handler updates the camera
+			// afterward, so overlay anchors and the viewport crop intentionally
+			// observe different moments in the frame.
 		if (turretTargetingMode) {
 			const int16 shipOffsetX = _turretFrameShipCenterValid ?
 				_turretFrameShipOffsetX : (int16)(_posAccumX >> 8);
@@ -780,8 +742,7 @@ void InsaneRebel1::procPostRendering(byte *renderBitmap, int32 codecparam, int32
 			updateFlightVariantCursor();
 		}
 	} else if (!shotOverlayHandled) {
-		// Keep lock/target accumulators quiescent when current handler doesn't
-		// execute FUN_1C940/FUN_1CCA0/FUN_1C9CD/FUN_1CB22.
+		// Keep lock/target accumulators quiescent outside targeting handlers.
 		_targetProximity = 0;
 		_prevTargetProx = 0;
 		_targetCount = 0;
@@ -789,8 +750,7 @@ void InsaneRebel1::procPostRendering(byte *renderBitmap, int32 codecparam, int32
 		_lastHitTarget = 0;
 	}
 
-	// RunLevel7Flow tests route branches after PumpFrontendFrame, using the
-	// decoded logical route timeline and updated GAME 0x09 cursor state.
+	// Route branches use the decoded timeline and updated 0x09 cursor state.
 	if (_currentLevel == 6)
 		checkDynamicLevelBranch(curFrame);
 
@@ -820,7 +780,6 @@ void InsaneRebel1::procPostRendering(byte *renderBitmap, int32 codecparam, int32
 		renderLevelHitsOverlay(renderBitmap, pitch, width, height, 0x16, true);
 
 	// Level 8 (Imperial Walkers) ??? walker-specific state update + UI overlay.
-	// In the original, RunLevel8Flow runs the walker logic inline in the per-frame
 	// game loop. We call it from procPostRendering when _currentLevel == 7.
 	if (_currentLevel == 7) {
 		updateLevel8WalkerState();
@@ -835,9 +794,7 @@ void InsaneRebel1::procPostRendering(byte *renderBitmap, int32 codecparam, int32
 }
 
 // Helper that groups the common shot/lock overlay calls used by the
-// original GAME handlers. GAME 0x1A uses the same pipeline without the target
 // box draw; 0x09/0x0A/0x0B include DrawTargetIndicators first. GAME 0x0B
-// defers FUN_1CB22 targeting until post-render so FTCH can restore cockpit
 // pixels over shots/boxes while the lock/fire indicator remains visible.
 void InsaneRebel1::renderShotOverlayPipeline(byte *dst, int pitch, int width, int height,
 		bool drawTargetBoxes, bool drawTargeting) {
@@ -850,7 +807,6 @@ void InsaneRebel1::renderShotOverlayPipeline(byte *dst, int pitch, int width, in
 
 	processShot();
 	renderLaserShots(dst, pitch, width, height);
-	// Timer decrement AFTER rendering (original decrements inside the render loop).
 	// This ensures timer==5 first frame is rendered with gun barrel offset and lerp=1.
 	for (int i = 0; i < kMaxShotSlots; i++) {
 		if (_shotSlots[i].timer > 0)
@@ -869,8 +825,6 @@ void InsaneRebel1::renderGameOp0BOverlayDuringChunk(byte *dst, int pitch, int wi
 	_gameOp0BOverlayRenderedThisFrame = true;
 }
 
-// renderTargetBoxes ??? FUN_1C940 (0x1C940). Per-target green box overlays.
-// Original gates on g_hudDisableFlags (0x75FE) bit 1: skip when set (Hard difficulty).
 void InsaneRebel1::renderTargetBoxes(byte *dst, int pitch, int width, int height) {
 	if (_gameplayFlags75fe & 2) {
 		_prevTargetCount = _targetCount;
@@ -899,8 +853,6 @@ void InsaneRebel1::renderTargetBoxes(byte *dst, int pitch, int width, int height
 	_targetCount = 0;
 }
 
-// renderTargeting ??? FUN_1CB22 (0x1CB22). Targeting/lock-on indicator.
-// The original does not draw a hardcoded pixel cross; it renders glyph markers
 // whose state depends on _targetProximity.
 void InsaneRebel1::renderTargeting(byte *dst, int pitch, int width, int height) {
 	const char kRA1TorpedoIndicator[] = "<<d";
@@ -914,14 +866,11 @@ void InsaneRebel1::renderTargeting(byte *dst, int pitch, int width, int height)
 		stopSfx(kSfxLockOn);
 
 	if (markerBank.numSprites > 0) {
-		// FUN_1CB22 can switch marker sets via DAT_75FF bit 1.
 		// Baseline RA1 targeting uses '^' and animation e..h.
 		const bool altMarkerSet = isTorpedoModeActive();
 
-		// DAT_75FF bit 2 suppresses the fixed lock/readiness overlay.
 		if ((_gameplayFlags75ff & 0x4) == 0) {
 			// Lock indicator at fixed center positions:
-			// FUN_1CB22 draws marker strings at (0xA0,0x78) and (0xA0,0x7E).
 			if (_targetProximity > 0) {
 				drawCenteredBankGlyph(markerBank, dst, pitch, width, height, overlayX + 0xA0, overlayY + 0x78, ']');
 				if (_targetProximity > 1)
@@ -929,9 +878,7 @@ void InsaneRebel1::renderTargeting(byte *dst, int pitch, int width, int height)
 			}
 		}
 
-		// DAT_75FE bit 2 suppresses the cursor glyph entirely.
 		if ((_gameplayFlags75fe & 0x4) == 0) {
-			// Pointer glyph at current aim position. Original uses two variants:
 			// default marker ('^' or 'x') and animated lock marker (e..h or y..|).
 			char marker[2] = { (char)(altMarkerSet ? 'x' : '^'), '\0' };
 			if (_targetProximity > 1) {
@@ -957,9 +904,7 @@ void InsaneRebel1::renderTargeting(byte *dst, int pitch, int width, int height)
 	_lastHitTarget = 0;
 }
 
-// handleLevel14Play2BSplice ??? RunLevel14Flow (0x1ACD1) queues L14PLY2B.ANM
 // from inside the L14PLAY2 loop when the current clip reaches maxFrame - 0x0F.
-// This helper is an implementation extraction; the original keeps the PlayAnmFile
 // call inline and passes the old L14PLAY2 timeline frame to the ANM frame gate.
 void InsaneRebel1::handleLevel14Play2BSplice(int32 curFrame, int32 maxFrame) {
 	if (_currentLevel != 13 || _levelGameplayPhase != 2 || _level14Play2BSpliced ||
@@ -973,26 +918,19 @@ void InsaneRebel1::handleLevel14Play2BSplice(int32 curFrame, int32 maxFrame) {
 	_level14Play2BSplicePending = true;
 	_level14Play2BSpliceFrame = curFrame;
 
-	// DOS queues the continuation from inside the active playback loop, so the
-	// STOR/FTCH video state remains live across the L14PLAY2 -> L14PLY2B jump.
+	// Keep video state live across the L14PLAY2 -> L14PLY2B jump.
 	if (_player)
 		_player->setPreserveGameVideoStateOnRelease(true);
 
-	// Original after PlayAnmFile("LVL14/L14PLY2B.ANM", 0x860, maxFrame-0x0F, 1, -1):
-	//   g_extendedPhaseFlags &= 0xFA;
-	//   DAT_7604 &= 0xBF;
-	// g_extendedPhaseFlags is primary byte 1 because g_gameplayPhaseFlags starts
-	// at byte 0 of the original frame-object state array.
+	// Clear carried target flags before switching clips.
 	clearFrameObjectPrimaryBits(1, 0x05);
 	clearFrameObjectPrimaryBits(2, 0x40);
 	_vm->_smushVideoShouldFinish = true;
 }
 
-// renderGostScorePopup ??? Per-kill score glyph from RenderGostOverlaySlots (0x1C9CD).
 // Maps kill score to tech-font glyph and draws it rising upward from the kill position.
 void InsaneRebel1::renderGostScorePopup(byte *dst, int pitch, int width, int height,
 										int16 centerX, int16 centerY, int16 frame) {
-	// Score-to-glyph mapping from original (0x1CA5D-0x1CACB)
 	char glyphChar = '\0';
 	uint16 scoreValue = (uint16)_tuning.kill;
 	if (scoreValue == 10)       glyphChar = 0x72;  // 'r'
@@ -1005,14 +943,12 @@ void InsaneRebel1::renderGostScorePopup(byte *dst, int pitch, int width, int hei
 	if (glyphChar == '\0')
 		return;
 
-	// Original: DrawStringEx(buf, colorMap, centerX-4, centerY-frame, 1, 100, 3, scoreText)
 	// "<<{glyph}" string selects tech font layer via the << markup
 	char scoreText[4] = { '<', '<', glyphChar, '\0' };
 	drawFontBankString(dst, pitch, width, height,
 					   centerX - 4, centerY - frame, scoreText);
 }
 
-// renderGostSlots ??? FUN_1C9CD (0x1C9CD). Hit explosion animations at target positions.
 // Renders explosion sprites from bangBank + per-kill score popup glyphs.
 void InsaneRebel1::renderGostSlots(byte *dst, int pitch, int width, int height) {
 	if ((_gameplayFlags75fe & 0x10) != 0)
@@ -1038,8 +974,6 @@ void InsaneRebel1::renderGostSlots(byte *dst, int pitch, int width, int height)
 				renderSprite(dst, pitch, width, height, drawX, drawY, spr);
 			}
 
-			// Per-kill score popup glyph ??? RenderGostOverlaySlots (0x1CA35)
-			// Suppressed when DAT_75FF bit 3 is set.
 			if ((_gameplayFlags75ff & 8) == 0) {
 				renderGostScorePopup(dst, pitch, width, height,
 									overlayX + centerX, centerY, _gostSlots[i].frame);
@@ -1050,8 +984,6 @@ void InsaneRebel1::renderGostSlots(byte *dst, int pitch, int width, int height)
 	}
 }
 
-// renderLevelHitsOverlay ??? RunLevel1Flow (0x16421-0x16438) and RunLevel11Flow
-// (0x1A07A-0x1A090) call FormatAndDrawText with "<<HITS %02d" at x=0x119.
 void InsaneRebel1::renderLevelHitsOverlay(byte *dst, int pitch, int width, int height, int y,
 		bool screenSpace) {
 	if (_hudFontBank.numSprites <= 0 && _techFontBank.numSprites <= 0)
@@ -1069,7 +1001,6 @@ void InsaneRebel1::renderLevelHitsOverlay(byte *dst, int pitch, int width, int h
 	drawFontBankString(dst, pitch, width, height, drawX, drawY, hitsStr);
 }
 
-// renderLevel5Part2Overlay ??? RunLevel5Flow (0x176D0-0x1777E) draws the
 // part-2 instruction reveal, then switches to the live target count.
 void InsaneRebel1::renderLevel5Part2Overlay(byte *dst, int pitch, int width, int height, int32 curFrame) {
 	if (_hudFontBank.numSprites <= 0 && _techFontBank.numSprites <= 0)
@@ -1102,7 +1033,6 @@ void InsaneRebel1::resetEnemyShotSlots() {
 	memset(_enemyShotSlots, 0, sizeof(_enemyShotSlots));
 }
 
-// Port helper for Level 7 RunLevel7Flow. The original keeps this warning and
 // single incoming projectile slot inline in the L7PLAY frontend loop.
 void InsaneRebel1::renderLevel7RouteOverlays(byte *dst, int pitch, int width, int height) {
 	if (_currentLevel != 6 || !_interactiveVideoActive || _health < 0)
@@ -1166,7 +1096,6 @@ void InsaneRebel1::renderLevel7RouteOverlays(byte *dst, int pitch, int width, in
 	}
 }
 
-// Port helper for Level 13 RunLevel13Flow. The original stores this state in
 // five local stack arrays around the L13PLAY frontend loop, not in a named function.
 void InsaneRebel1::renderLevel13EnemyShots(byte *dst, int pitch, int width, int height) {
 	if (_currentLevel != 12 || !_interactiveVideoActive || _health < 0)
@@ -1236,7 +1165,6 @@ void InsaneRebel1::renderLevel13EnemyShots(byte *dst, int pitch, int width, int
 	}
 }
 
-// renderLaserShots ??? FUN_1CDA7/FUN_1D79C/HandleGameOp1A shot visual path.
 void InsaneRebel1::renderLaserShots(byte *dst, int pitch, int width, int height) {
 	const char kRA1TorpedoTrailLeft[] = "<<&";
 	const char kRA1TorpedoTrailRight[] = "<<'";
@@ -1245,18 +1173,14 @@ void InsaneRebel1::renderLaserShots(byte *dst, int pitch, int width, int height)
 	if (_laserBank.numSprites <= 0 && !torpedoMode)
 		return;
 
-	// DAT_2407 lookup used by FUN_1CDA7/FUN_1D79C for timer 1..5 interpolation.
 	// Entry 0 unused.
 	const int kShotLerpByTimer[6] = { 0, 8, 7, 6, 4, 0 };
-	// DAT_2413: on-foot lerp table (timer 5 = 1, not 0 like flight mode).
 	const int kOnFootShotLerp[6] = { 0, 8, 7, 6, 4, 1 };
-	// DAT_240e: gun barrel X offset indexed by shipDirIndex (for timer==5 first frame).
 	// Indices 11..19 are the active firing poses from L9PILOT.NUT.
 	const int16 kOnFootGunBarrelX[20] = {
 		0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 		0, -56, -47, -23, -13, 0, 13, 30, 54, 59
 	};
-	// DAT_2420: gun barrel Y offset indexed by shipDirIndex (for timer==5 first frame).
 	const int16 kOnFootGunBarrelY[20] = {
 		0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 		0, -3, -19, -24, -30, -28, -30, -29, -20, -5
@@ -1312,9 +1236,6 @@ void InsaneRebel1::renderLaserShots(byte *dst, int pitch, int width, int height)
 			const int lerp = kShotLerpByTimer[timer];
 
 			if (turretMode) {
-				// FUN_1D79C chooses emitters in two ways:
-				// - DAT_75E4 == 2: use DAT_75DC..DAT_75E2 fixed offsets
-				// - otherwise: table path DAT_244A/DAT_251A keyed by DAT_74D2
 				int start1X = 0;
 				int start1Y = 0;
 				int start2X = 0;
@@ -1414,8 +1335,6 @@ void InsaneRebel1::renderLaserShots(byte *dst, int pitch, int width, int height)
 	}
 }
 
-// Level intro title data (from RunTwoLineTextSplash calls in original binary).
-// titleText is drawn at y=10, subtitleText at y=25 (matching original DrawUiString positions).
 // revealStartFrame/revealEndFrame control the frame range during which text is visible.
 const struct {
 	const char *titleText;      // Top line (chapter number)
@@ -1423,7 +1342,7 @@ const struct {
 	int16 revealStartFrame;     // Frame at which text begins appearing
 	int16 revealEndFrame;       // Frame after which text stops
 } kLevelTitles[] = {
-	{ "Chapter 1",  "Flight Training",        -80, -30 },  // Original computes totalFrames-0x50..totalFrames-0x1e
+	{ "Chapter 1",  "Flight Training",        -80, -30 },
 	{ "Chapter 2",  "Asteroid Field Training",  40, 100 },
 	{ "Chapter 3",  "Planet Kolaador",           0,  50 },
 	{ "Chapter 4",  "Star Destroyer Attack",     5,  50 },
@@ -1471,12 +1390,10 @@ void InsaneRebel1::drawLevelTitleOverlay(byte *dst, int pitch, int width, int he
 	const char *title = kLevelTitles[_introTextLevel].titleText;
 	const char *subtitle = kLevelTitles[_introTextLevel].subtitleText;
 
-	// Center horizontally (matching original DrawUiString x_center=0xA0=160)
 	int titleW = getFontBankStringWidth(title);
 	int subtitleW = getFontBankStringWidth(subtitle);
 	int centerX = width / 2;
 
-	// Original positions: title at y=10, subtitle at y=25 (0x19)
 	drawFontBankString(dst, pitch, width, height, centerX - titleW / 2, 10, title);
 	drawFontBankString(dst, pitch, width, height, centerX - subtitleW / 2, 25, subtitle);
 }
@@ -1505,8 +1422,7 @@ void InsaneRebel1::beginChapterSummaryOverlay(int revealOffsetFromEnd, int stopO
 	}
 }
 
-// Passcodes are kept in clear text. The DOS table is XOR-0xAA
-// encoded in 15 20-byte slots at DS:0x00A4.
+// Passcodes are stored in clear text.
 static const char *const kChapterCompletePasswords[] = {
 	"FALCON", "BIGGS", "ACKBAR", "ANOAT", "KAIBURR",
 	"FORNAX", "YUZZEM", "MYNOCK", "BESPIN", "BRIGIA",
@@ -1520,9 +1436,6 @@ const char *InsaneRebel1::getChapterCompletePassword(int passwordIndex) const {
 	return kChapterCompletePasswords[passwordIndex - 1];
 }
 
-// drawChapterSummaryOverlay ??? RunChapterCompleteSummaryScreen (0x15E42), shared by
-// the RA1 runlevel flows that call it. This helper is an implementation extraction;
-// the original pumps frontend frames from the runlevel after queueing the END ANM.
 void InsaneRebel1::drawChapterSummaryOverlay(byte *dst, int pitch, int width, int height,
 		int32 curFrame, int32 maxFrame) {
 	if (!_chapterSummary.active || !dst || maxFrame <= 0)
@@ -1573,13 +1486,11 @@ void InsaneRebel1::drawChapterSummaryOverlay(byte *dst, int pitch, int width, in
 	}
 }
 
-// drawFontBankString ??? FUN_221B7 (0x221B7), partial parity:
 // supports '<'/'>' layer markup and layer-2 space handling used by RA1 HUD/targeting strings.
 void InsaneRebel1::drawFontBankString(byte *dst, int pitch, int width, int height, int x, int y, const char *text) {
 	if (!text || !dst)
 		return;
 
-	// Original FUN_221B7 layer mapping is table-driven at DAT_2D56 (0x406-byte entries).
 	// Current RA1 integration maps subtitle/HUD-relevant layers as:
 	//   layer 0   -> TITLE font bank
 	//   layer 1   -> TALK/HUD font bank
@@ -1601,7 +1512,6 @@ void InsaneRebel1::drawFontBankString(byte *dst, int pitch, int width, int heigh
 		if (bank.numSprites <= 0)
 			return;
 
-		// FUN_221B7 special-case: when layer==2 and char is space, remap to '!'.
 		if (ch == ' ') {
 			if (techLayer) {
 				drawBankString(bank, dst, pitch, width, height, x, y, "!");
@@ -1618,7 +1528,6 @@ void InsaneRebel1::drawFontBankString(byte *dst, int pitch, int width, int heigh
 	}
 }
 
-// getFontBankStringWidth ??? Width pre-pass from FUN_221B7 (0x221B7), including '<'/'>' markup.
 int InsaneRebel1::getFontBankStringWidth(const char *text) {
 	if (!text)
 		return 0;
@@ -1667,10 +1576,7 @@ int InsaneRebel1::getFontBankLineAdvance(const char *text) {
 	return getBankSpaceHeight(bank) + 4;
 }
 
-// renderShip ??? Ship sprite rendering from FUN_1DEB5 (0x1DEB5) at LAB_1e2b2.
-// Also used by FUN_1E6A7 (0x1E6A7) turret handler via FUN_20BD3.
 void InsaneRebel1::renderShip(byte *dst, int pitch, int width, int height) {
-	// From FUN_1DEB5 LAB_1e2b2: ship drawn when health >= 0 OR deathTimer > 20
 	// Hidden during last 20 frames of death sequence (deathTimer 20???0)
 	if (_health < 0 && _deathTimer <= 20)
 		return;
@@ -1702,8 +1608,6 @@ void InsaneRebel1::renderShip(byte *dst, int pitch, int width, int height) {
 	renderSprite(dst, pitch, width, height, drawX, drawY, spr);
 }
 
-// renderExplosions ??? Explosion sprites from FUN_1DEB5 (0x1DEB5) LAB_1e185 (damage hit)
-// and LAB_1e0e3 (death shake). See also FUN_1CCA0 (0x1CCA0) explosion spawner.
 void InsaneRebel1::renderExplosions(byte *dst, int pitch, int width, int height) {
 	if (_bangBank.numSprites <= 0)
 		return;
@@ -1723,7 +1627,6 @@ void InsaneRebel1::renderExplosions(byte *dst, int pitch, int width, int height)
 		shipScreenY = overlayY + centerY;
 	}
 
-	// --- Death shake explosions (FUN_1DEB5 LAB_1e0e3) ---
 	// When dead and deathTimer > 10: random explosion sprites scatter around ship
 	if (_health < 0 && _deathTimer > 10) {
 		int intensity = _deathTimer - 10;  // 20???1 as timer goes 30???11
@@ -1731,16 +1634,14 @@ void InsaneRebel1::renderExplosions(byte *dst, int pitch, int width, int height)
 			intensity = 20 - intensity;     // Triangle: 0???10???0
 
 		// di = intensity * 4 + 1 (vertical scatter range)
-		// si = -20 + intensity * 4 (horizontal scatter range, DAT_75d8 is 0)
 		int rangeY = intensity * 4 + 1;
 		int rangeX = -20 + intensity * 4;
 		if (rangeX < 1) rangeX = 1;
 
 		for (int i = 0; i < intensity; i++) {
-			// Random sprite from bang bank (FUN_21db0(10))
 			int sprIdx = _vm->_rnd.getRandomNumber(_bangBank.numSprites - 1);
 
-			// Random position around ship (matching assembly random scatter)
+			// Random position around ship.
 			int randX = (int)_vm->_rnd.getRandomNumber(rangeX * 2) - rangeX;
 			int randY = (int)_vm->_rnd.getRandomNumber(rangeY * 2) - rangeY;
 
@@ -1754,7 +1655,6 @@ void InsaneRebel1::renderExplosions(byte *dst, int pitch, int width, int height)
 		return;
 	}
 
-	// --- Damage hit explosion (FUN_1DEB5 LAB_1e185) ---
 	// When alive, in cooldown, and bang bank loaded
 	if (_health >= 0 && _damageCooldown > 0) {
 		// Sprite index = 10 - damageCooldown (frames 0???9 as cooldown 10???1)
@@ -1762,7 +1662,6 @@ void InsaneRebel1::renderExplosions(byte *dst, int pitch, int width, int height)
 		if (sprIdx < 0 || sprIdx >= _bangBank.numSprites)
 			return;
 
-		// Position at ship center (DAT_75d8 is always 0 in RA1)
 		int drawX = shipScreenX;
 		int drawY = shipScreenY;
 
@@ -1772,9 +1671,7 @@ void InsaneRebel1::renderExplosions(byte *dst, int pitch, int width, int height)
 	}
 }
 
-// renderHUD ??? FUN_1BBCB (0x1BBCB). Status bar from DISPLAY.NUT with health/lives/score overlays.
 void InsaneRebel1::renderHUD(byte *dst, int pitch, int width, int height) {
-	// Extra life bonus: every 10,000 points (FUN_1BBCB lines 11-27)
 	if (_score / 10000 > _prevScore / 10000) {
 		_lives++;
 		playSfx(kSfxBonus, 127, 0);
@@ -1786,11 +1683,7 @@ void InsaneRebel1::renderHUD(byte *dst, int pitch, int width, int height) {
 
 	const RA1Sprite &bar = _displayBank.sprites[0];
 
-	// DISPLAY.NUT sprite is 320??19 at xoffs=0, yoffs=176 in the original game.
-	// FUN_224FD (0x224FD) sets the 320x200 window origin inside the 384x242 buffer.
-	// FUN_1BBCB (0x1BBCB) HUD coordinates are screen-space, so when we emulate
-	// perspective via source-window cropping, anchor HUD at window origin to keep
-	// it fixed on-screen.
+	// Anchor HUD at the window origin so perspective cropping does not move it.
 	int hudOriginX = 0;
 	int hudOriginY = 0;
 	if (_interactiveVideoActive && _player) {
@@ -1800,12 +1693,10 @@ void InsaneRebel1::renderHUD(byte *dst, int pitch, int width, int height) {
 
 	const int hudX = hudOriginX + bar.xoffs;
 	const int hudY = hudOriginY + bar.yoffs;
-	// FUN_1BBCB draws DISPLAY.NUT at x=5 with a 4..315 clip rect. The HUD
 	// masks/text below use the unshifted screen-space coordinate origin.
 	const int hudPlateX = hudX + 5;
 	const int hudPlateY = hudY;
 
-	// DOS RA1 draws the HUD plate through DrawFobjGlyph(..., flags=0x181),
 	// which selects the opaque blit path. Keep zero-valued pixels black instead
 	// of treating them as transparent.
 	if (bar.data && bar.width > 0 && bar.height > 0) {
@@ -1841,7 +1732,6 @@ void InsaneRebel1::renderHUD(byte *dst, int pitch, int width, int height) {
 		}
 	}
 
-	// Draw health bar from FUN_1BBCB (0x1BBCB) + FUN_21D66 (0x21D66):
 	// fill rect at (0x92-health, 8), width=health, height=5, color=0.
 	// This is a black "remaining health" fill over the HUD template.
 	{
@@ -1861,7 +1751,6 @@ void InsaneRebel1::renderHUD(byte *dst, int pitch, int width, int height) {
 	}
 
 	// Lives: black out excess pilot icons embedded in DISPLAY.NUT background.
-	// Original FUN_1BBCB: FUN_21D66(buf, lives*10+186, 6, 51-lives*10, 9, 0, 320)
 	// Icons are 5 slots at x=186..236, each ~10px wide. Cover unused slots with black.
 	if (_lives >= 0 && _lives < 5) {
 		int coverX = hudX + _lives * 10 + 186;
@@ -1878,15 +1767,12 @@ void InsaneRebel1::renderHUD(byte *dst, int pitch, int width, int height) {
 		}
 	}
 
-	// Score: FUN_1BBCB (0x1BBCB) -> FUN_21FAF (0x21FAF) with format at 0x6713: "<<%06ld".
-	// Keep the leading "<<" markup so FUN_221B7-equivalent path selects TECH font layer.
 	if (_hudFontBank.numSprites > 0 || _techFontBank.numSprites > 0) {
 		char scoreStr[24];
 		Common::sprintf_s(scoreStr, "<<%06d", MAX<int>(_score, 0));
 		drawFontBankString(dst, pitch, width, height, hudX + 273, hudY + 5, scoreStr);
 	}
 
-	// Low-health indicator from FUN_1BBCB (0x1BBCB):
 	// if (health < miss*2 || health < wham*2 || health < shot*2) and (frame & 8),
 	// draw warning glyph at (0x49, 0x07). Two variants:
 	//   "<<[" when above critical thresholds, "<<\\" when critical.
@@ -1901,7 +1787,6 @@ void InsaneRebel1::renderHUD(byte *dst, int pitch, int width, int height) {
 				(_health > _tuning.miss) &&
 				(_health > _tuning.wham) &&
 				(_health > _tuning.shot);
-			// FUN_1BBCB pushes string pointers 0x671b ("<<[") or 0x671f ("<<\\") into FUN_221B7.
 			const char *warningStr = aboveCritical ? "<<[" : "<<\\";
 			drawFontBankString(dst, pitch, width, height, hudX + 0x49, hudY + 0x07, warningStr);
 			if (!aboveCritical && ((_frameCounter & 0x7) == 0))
@@ -1909,10 +1794,9 @@ void InsaneRebel1::renderHUD(byte *dst, int pitch, int width, int height) {
 		}
 	}
 
-	_hudDirtyFlag = 0xFF;  // Mark HUD as freshly drawn (0x7601)
+	_hudDirtyFlag = 0xFF;
 }
 
-// Attack window frame tables ??? RunLevel8Flow (0x18546), data at 0x236D/0x2373/0x2379.
 // Each route has up to 3 attack windows. -2 means disabled.
 const int16 InsaneRebel1::kWalkerAttackWindow1[3] = { 2588, 2323, 877 };
 const int16 InsaneRebel1::kWalkerAttackWindow2[3] = { 1709, 1444, -2 };
@@ -1921,27 +1805,23 @@ const int16 InsaneRebel1::kWalkerAttackWindow3[3] = { 262, -2, -2 };
 // updateLevel8WalkerState ??? Per-frame walker health + attack window logic.
 // Called from procPostRendering when _currentLevel == 7.
 void InsaneRebel1::updateLevel8WalkerState() {
-	// Walker health computation ??? RunLevel8Flow (0x18634-0x18655)
 	if (_walkerHealth >= 11) {
 		_walkerHealth = (int16)(100 - (_killCount + (_killCount >> 2)));
 	} else if (_walkerHealth > 0 && (_gameCounter & 3) == 0) {
 		_walkerHealth--;
 	}
 
-	// Walker destroyed ??? exit interactive video (original loop: `while (sVar6 != 0)`)
 	if (_walkerHealth <= 0) {
 		_vm->_smushVideoShouldFinish = true;
 		return;
 	}
 
-	// FUN_12FE1/FUN_130C9/FUN_13195 test whether the route-specific walker
 	// contact hazards hit the player. The port synthesizes that damage flag in updateGameOp0BPhysics(),
 	// where the 0x0B damage flags are consumed. This is unrelated to _walkerHealth,
 	// which is the boss health displayed by the Level 8 overlay.
 	int route = CLIP(_levelRouteIndex, 0, 2);
 	uint16 fc = (uint16)_gameCounter;
 
-	// Attack window logic ??? RunLevel8Flow (0x18778-0x18B4A)
 	const int16 *windows[3] = {
 		&kWalkerAttackWindow1[route],
 		&kWalkerAttackWindow2[route],
@@ -1990,8 +1870,6 @@ void InsaneRebel1::updateLevel8WalkerState() {
 		}
 	}
 
-	// At window boundary: decide route branch ??? RunLevel8Flow (0x18A7F-0x18B4A)
-	// Original: left branches unless at window3, right branches unless at window2.
 	for (int w = 0; w < 3; w++) {
 		int16 windowEnd = *windows[w];
 		if (windowEnd < 0) continue;
@@ -2024,11 +1902,8 @@ void InsaneRebel1::updateLevel8WalkerState() {
 	}
 }
 
-// renderLevel8Overlay ??? Walker-specific UI from RunLevel8Flow (0x18660-0x18A7E).
 // Draws walker health %, attack timer, directional arrows, and target reticle.
-// Original RunLevel8Flow projects these fixed cockpit-panel points and then uses
 // 1/4 parallax compensation. We draw into the 384x242 SMUSH buffer, so add the
-// viewport offset to convert the original screen-space result back to buffer
 // coordinates before the final RA1 crop.
 void InsaneRebel1::renderLevel8Overlay(byte *dst, int pitch, int width, int height,
 		int viewportX, int viewportY) {
@@ -2118,8 +1993,6 @@ void InsaneRebel1::renderLevel8Overlay(byte *dst, int pitch, int width, int heig
 	}
 }
 
-// renderSprite ??? Simplified version of FUN_20BD3 (0x20BD3) glyph/sprite renderer.
-// Original dispatches through full codec pipeline; this does flat pixel blit with transparency.
 void InsaneRebel1::renderSprite(byte *dst, int pitch, int width, int height,
 								int x, int y, const RA1Sprite &spr) {
 	if (!spr.data || spr.width <= 0 || spr.height <= 0)
diff --git a/engines/scumm/insane/rebel1/runlevels.cpp b/engines/scumm/insane/rebel1/runlevels.cpp
index f9e5fecdd56..efe0be88e1c 100644
--- a/engines/scumm/insane/rebel1/runlevels.cpp
+++ b/engines/scumm/insane/rebel1/runlevels.cpp
@@ -141,9 +141,6 @@ static int calculateThresholdBonus(int kills, int perfectThreshold, int perKillT
 	return bonus;
 }
 
-// ---------------------------------------------------------------------------
-// Game flow (matching original at 0x15597)
-// ---------------------------------------------------------------------------
 
 // Play a passive cinematic (no game callback, skippable).
 // startFrame > 0: fast-forward (decode without display/audio) to that frame.
@@ -155,9 +152,7 @@ void InsaneRebel1::playCinematic(const char *filename, int32 startFrame) {
 	SmushPlayer *splayer = _vm->_splayer;
 	_player = splayer;
 	restoreScreenFlashPalette();
-	// DOS PlayFrontendAnmAndWait keeps pumping until frontend audio clears.
-	// Rebel queues outlive SmushPlayer::play(), so clear stale
-	// passive-cinematic audio before chaining the next ANM.
+	// Clear passive-cinematic audio before chaining the next ANM.
 	_audio.reset();
 	splayer->resetAudioTracks();
 	applyAudioOptions();
@@ -174,8 +169,7 @@ void InsaneRebel1::playCinematic(const char *filename, int32 startFrame) {
 	_introTextActive = false;
 }
 
-// The original runlevel flows repeat this pattern around RunChapterCompleteSummaryScreen:
-// queue the END ANM, draw the summary while the frontend pumps, then award points/unlock.
+// Queue the END ANM, draw the summary while the frontend pumps, then award points/unlock.
 void InsaneRebel1::playChapterCompleteCinematic(const char *filename, int16 unlockedChapter,
 		int revealOffsetFromEnd, int stopOffsetFromEnd,
 		const char *bonusLabel1, const char *detailText1, int bonusValue1,
@@ -273,9 +267,7 @@ void InsaneRebel1::resetLevelAttemptState(int16 flyControlMode, int16 gameplayPh
 void InsaneRebel1::playLevelTransitionCutscene(int level) {
 	switch (level) {
 	case 4:
-		// Original successor path 0x6d7d, reached after chapter 3 and by the
-		// FALCON/BIGGS/WEDGE passcode group. This is separate from RunLevel4Flow
-		// (0x6ee4), which starts with LVL4/L4INTRO.ANM.
+		// FALCON/BIGGS/WEDGE passcode group.
 		playCinematic("CUT1/C1BLOCK.ANM");
 		if (shouldAbortGameFlow())
 			break;
@@ -288,13 +280,9 @@ void InsaneRebel1::playLevelTransitionCutscene(int level) {
 		playCinematic("CUT1/C1DARTH2.ANM");
 		break;
 	case 7:
-		// Original successor path 0x7d52, reached after chapter 6 and by the
-		// chapter-6 passcode group, before RunLevel7Flow (0x7dcc).
 		playCinematic("CUT2/C2CUT2.ANM");
 		break;
 	case 11:
-		// Original successor path 0x9f3a, reached after chapter 10 and by the
-		// chapter-10 passcode group, before RunLevel11Flow (0x9f9f).
 		playCinematic("CUT3/C3BOOM.ANM");
 		break;
 	default:
@@ -321,38 +309,14 @@ void InsaneRebel1::clearVideoBuffer() {
 	free(clearBuffer);
 }
 
-// Intro sequence (0x155ef-0x158f8):
-//   1. O1LOGO.ANM ??? LucasArts logo
-//   2. O1OPEN.ANM ??? Star Wars opening crawl
 void InsaneRebel1::playIntroSequence() {
-	// LucasArts logo (original: PUSH 0x57cc, CALL FUN_1BA32 with flags 0x0420)
 	playCinematic("OPEN/O1LOGO.ANM");
 	if (shouldAbortGameFlow())
 		return;
 
-	// Star Wars opening crawl (original: PUSH 0x5800, CALL FUN_1BA32)
 	playCinematic("OPEN/O1OPEN.ANM");
 }
 
-// Main menu on O1OPTION.ANM background (0x15968).
-// Original renders text overlay with 5 menu items via FUN_21F7A.
-// For now, we play the menu video as a passive cinematic (non-interactive)
-// and return "Start New Game" immediately.
-
-// Level 1 flow (0x16100-0x167A2, from disassembly):
-//   1. Load NUTs (L1BANK1, L1BANK2, L1EXPLD, L1BANG, L1LASER)
-//   2. L1HANGAR.ANM ??? Full hangar departure cutscene (782 frames, flags 0x0420)
-//   3. L1CU1.ANM ??? Pre-flight cutscene (flags 0x0400)
-//   4. L1PLAY1L.ANM ??? Stage 1 flight, hard/left path (788 frames)
-//      At frame 394, if player steers right ??? L1PLAY1R (hard path, 396 frames)
-//   5. L1CU2.ANM ??? Mid-level cutscene
-//   6. L1PLAY2.ANM ??? Stage 2 turret
-//      If score < 5 (0x75D0): L1RETRY ??? retry Stage 2
-//   7. L1END.ANM ??? Level complete
-//   Death (health<0): L1CRASHA/B ??? lives check:
-//     lives>0: L1NEW ??? jump back to Stage 1 (skip L1HANGAR/L1CU1)
-//     lives==0: L1DEATH ??? return to menu
-
 bool InsaneRebel1::runLevel1() {
 	_currentLevel = 0;
 	loadTuningForLevel(0);
@@ -398,9 +362,7 @@ bool InsaneRebel1::runLevel1() {
 				return false;
 
 			while (!shouldAbortGameFlow()) {
-				// RunLevel1Flow calls L1PLAY2 with gameplay selector 1. This is
-				// the "1B" tuning row: snap/kill values are enabled and the
-				// lock/fire text overlay is suppressed.
+				// Stage 2 uses the 1B tuning row.
 				loadTuningForLevel(1);
 				_flyControlMode = 2;
 				_turretEmitterLeftX = 10;
@@ -455,7 +417,6 @@ bool InsaneRebel1::runLevel1() {
 bool InsaneRebel1::runLevel2() {
 	_currentLevel = 1;
 	loadLevelSprites(2);
-	// DOS RunLevel2Flow launches L2PLAY.ANM with gameplay selector 2.
 	loadTuningForLevel(2);
 
 	beginLevelTitleOverlay(1);
@@ -494,7 +455,6 @@ bool InsaneRebel1::runLevel2() {
 bool InsaneRebel1::runLevel3() {
 	_currentLevel = 2;
 	loadLevelSprites(3);
-	// DOS RunLevel3Flow launches L3PLAY.ANM with gameplay selector 3.
 	loadTuningForLevel(3);
 
 	beginLevelTitleOverlay(2);
@@ -533,7 +493,6 @@ bool InsaneRebel1::runLevel3() {
 bool InsaneRebel1::runLevel4() {
 	_currentLevel = 3;
 	loadLevelSprites(4);
-	// DOS RunLevel4Flow launches L4PLAY1.ANM with selector 4 and L4PLAY2.ANM with selector 5.
 	loadTuningForLevel(4);
 
 	beginLevelTitleOverlay(3);
@@ -551,9 +510,7 @@ bool InsaneRebel1::runLevel4() {
 		_killCount = 0;
 		_levelGameplayPhase = 0;
 
-		// Phase 1: Destroy two shield generators.
-		// Original sets DAT_00007732=0x39, DAT_00007734=0x3A ??? protected target IDs
-		// that can be hit repeatedly without event mask toggle.
+		// Phase 1: destroy two shield generators that can be hit repeatedly.
 		_protectedTargetA = 0x39;
 		_protectedTargetB = 0x3A;
 		_shieldGenHitsA = 0;
@@ -588,8 +545,7 @@ bool InsaneRebel1::runLevel4() {
 			return false;
 
 		if (_health >= 0 && shieldGeneratorsDestroyed) {
-			// Phase 2: torpedo run. The DOS loop enables torpedo mode at
-			// frontend/movie frame 0x3E and exits early as soon as killCount becomes nonzero.
+			// Phase 2: torpedo run.
 			_activeGameOpcode = 0;
 			_gameLatch5D = 0;
 			_gameLatch5F = 0;
@@ -623,7 +579,6 @@ bool InsaneRebel1::runLevel4() {
 bool InsaneRebel1::runLevel5() {
 	_currentLevel = 4;
 	loadLevelSprites(5);
-	// DOS RunLevel5Flow passes segment 6 for L5PLAY and segment 7 for L5PLAY2.
 	loadTuningForLevel(6);
 
 	beginLevelTitleOverlay(4);
@@ -703,8 +658,7 @@ bool InsaneRebel1::runLevel5() {
 bool InsaneRebel1::runLevel6() {
 	_currentLevel = 5;
 	loadLevelSprites(6);
-	// DOS RunLevel6Flow starts L6PLAY with PlayAnmFile(..., 8), so chapter 6
-	// uses tuning slot 8 ("6"), not the chapter-4B slot 5.
+	// Chapter 6 uses tuning slot 8, not the chapter-4B slot 5.
 	loadTuningForLevel(8);
 
 	beginLevelTitleOverlay(5);
@@ -756,7 +710,6 @@ bool InsaneRebel1::runLevel7() {
 	_currentLevel = 6;
 	loadLevelSprites(7);
 	loadRA1Nut("LVL7/L7LASER2.NUT", _enemyLaserBank);
-	// DOS RunLevel7Flow starts L7PLAY1.ANM with initLevelFlag=9.
 	loadTuningForLevel(9);
 
 	beginLevelTitleOverlay(6);
@@ -794,9 +747,7 @@ bool InsaneRebel1::runLevel7() {
 			route = _pendingRouteIndex;
 			routeSourceFrame = _pendingRouteStartFrame;
 			routeVideoStartFrame = _pendingRouteVideoStartFrame;
-			// DOS does not seek the destination route ANM here. The ANM-local
-			// decision frame is used by the playback gate/cutoff; this implementation
-			// starts at the already-advanced gate target after the cutover.
+			// The ANM-local decision frame gates the route cutover.
 			routeStartFrame = 0;
 		}
 
@@ -834,9 +785,7 @@ bool InsaneRebel1::runLevel8() {
 
 	_currentLevel = 7;
 	loadLevelSprites(8);
-	// RunLevel8Flow starts L8PLAY.ANM with initLevelFlag=10, so the walker
-	// chapter uses the original "8" tuning row while still keeping chapter
-	// index 7 for assets and Level 8-specific runtime logic.
+	// The walker uses tuning slot 10.
 	loadTuningForLevel(10);
 
 	beginLevelTitleOverlay(7);
@@ -847,7 +796,6 @@ bool InsaneRebel1::runLevel8() {
 	while (!shouldAbortGameFlow()) {
 		resetLevelAttemptState(3, 0, 17, true);
 
-		// Walker-specific state ??? RunLevel8Flow (0x18546)
 		_walkerHealth = 100;
 		_walkerTimer = 0;
 		_walkerBranchChoice = 0;
@@ -879,20 +827,14 @@ bool InsaneRebel1::runLevel8() {
 				break;
 
 			if (_pendingRouteIndex >= 0 && _pendingRouteIndex != route) {
-				// RunLevel8Flow uses PlayAnmFile(..., g_frameCounter, 1, -1)
-				// when it branches from one walker route to another. That wrapper
-				// keeps the current ANM alive for seven more gameplay frames, then
-				// opens the destination route while preserving the active state.
+				// Branch to the next walker route while preserving active state.
 				routeStartFrame = _pendingRouteStartFrame;
 				route = _pendingRouteIndex;
 				replayingRound = false;
 				continue;
 			}
 
-			// RunLevel8Flow keeps pumping the active route while the walker
-			// shield register is nonzero. Blocking SMUSH play returns
-			// when one route pass ends, so explicitly replay that route and
-			// preserve the accumulated walker damage.
+			// Replay the current route while preserving accumulated walker damage.
 			debugC(DEBUG_INSANE, "L8 replaying route=%d walkerHealth=%d killCount=%d",
 				route, (int)_walkerHealth, (int)_killCount);
 			routeStartFrame = 0;
@@ -925,10 +867,7 @@ bool InsaneRebel1::runLevel8() {
 }
 
 bool InsaneRebel1::runLevel9() {
-	// DOS RunLevel9Flow calls RandScaleByte(2) three times before the intro.
-	// That helper advances a byte seed with seed = seed * 9 + 0x35 and returns
-	// (2 * seed) >> 8. Do not use the session RNG here: it can turn the
-	// original right-side route into the capture/restart branch.
+	// Preserve the game's deterministic three-bit route selection.
 	uint8 originalRouteSeed = 0;
 	auto getOriginalRouteBit = [&originalRouteSeed]() {
 		originalRouteSeed = (uint8)(originalRouteSeed * 9 + 0x35);
@@ -939,7 +878,6 @@ bool InsaneRebel1::runLevel9() {
 	const int randPath3 = getOriginalRouteBit();
 	auto playLevel9PathSelector = [&](const char *filename) {
 		while (!shouldAbortGameFlow()) {
-			// DOS zeros g_shipOffsetX around the 0x1A-only selector clips.
 			// Keep the selector cursor centered instead of inheriting the last
 			// walking offset from the previous stormtrooper segment.
 			_onFootCharX = 0;
@@ -966,7 +904,7 @@ bool InsaneRebel1::runLevel9() {
 
 	_currentLevel = 8;
 	loadLevelSprites(9);
-	// DOS RunLevel9Flow alternates selectors 0x0B and 0x0C across its playable routes.
+	// Playable routes alternate tuning selectors 0x0B and 0x0C.
 	loadTuningForLevel(0x0B);
 
 	beginLevelTitleOverlay(8);
@@ -1117,7 +1055,6 @@ bool InsaneRebel1::runLevel9() {
 bool InsaneRebel1::runLevel10() {
 	_currentLevel = 9;
 	loadLevelSprites(10);
-	// DOS RunLevel10Flow starts L10PLAY.ANM with initLevelFlag=0x0D.
 	loadTuningForLevel(0x0D);
 
 	beginLevelTitleOverlay(9);
@@ -1150,14 +1087,11 @@ bool InsaneRebel1::runLevel10() {
 	return false;
 }
 
-// Level 11 flow (RunLevel11Flow, 0x19F67): Yavin Training
 // Turret-style level. Single interactive phase with kill-count retry.
-// Original: L11INTRO ??? L11PLAY (turret, killCount>4 to pass) ??? L11RETRY ??? retry/L11END
 bool InsaneRebel1::runLevel11() {
 	_currentLevel = 10;
 	loadLevelSprites(11);
-	// DOS RunLevel11Flow starts L11PLAY.ANM with initLevelFlag=0x0E.
-	// Row 10 has zero roll/slide tuning, which prevents horizontal aiming.
+	// This tuning row has zero roll/slide, which prevents horizontal aiming.
 	loadTuningForLevel(0x0E);
 
 	beginLevelTitleOverlay(10);
@@ -1178,7 +1112,6 @@ bool InsaneRebel1::runLevel11() {
 			if (_health < 0)
 				break;
 
-			// Original: killCount > 4 means pass
 			if (_killCount > 4 || _interactiveVideoCheatSkipped)
 				break;
 
@@ -1205,13 +1138,10 @@ bool InsaneRebel1::runLevel11() {
 	return false;
 }
 
-// Level 12 flow (RunLevel12Flow, 0x1A2DD): TIE Attack
 // Single interactive phase with mid-level retry mechanism.
-// Original: L12INTRO ??? L12PLAY ??? (retry at specific frame) ??? L12END
 bool InsaneRebel1::runLevel12() {
 	_currentLevel = 11;
 	loadLevelSprites(12);
-	// DOS RunLevel12Flow starts L12PLAY.ANM with initLevelFlag=0x0F.
 	loadTuningForLevel(0x0F);
 
 	beginLevelTitleOverlay(11);
@@ -1266,14 +1196,10 @@ bool InsaneRebel1::runLevel12() {
 	return false;
 }
 
-// Level 13 flow (RunLevel13Flow, 0x1A6E3): Death Star Surface
-// Flight level with enemy projectile system (original has 5-slot projectile tracking).
-// Original: L13INTRO ??? L13PLAY ??? L13END/L13NEW/L13DEATH
 bool InsaneRebel1::runLevel13() {
 	_currentLevel = 12;
 	loadLevelSprites(13);
 	loadRA1Nut("LVL13/L13LASR2.NUT", _enemyLaserBank);
-	// DOS RunLevel13Flow starts L13PLAY.ANM with initLevelFlag=0x10.
 	loadTuningForLevel(0x10);
 
 	beginLevelTitleOverlay(12);
@@ -1306,14 +1232,11 @@ bool InsaneRebel1::runLevel13() {
 	return false;
 }
 
-// Level 14 flow (RunLevel14Flow, 0x1ACB0): Surface Cannon
 // Two interactive phases: L14PLAY (targeting cannons) + L14PLAY2 (exhaust port approach).
-// Original: L14INTRO ??? L14PLAY ??? L14PLAY2 ??? optional L14PLY2B splice
 //   ??? L14END/L14NEW/L14DEATH
 bool InsaneRebel1::runLevel14() {
 	_currentLevel = 13;
 	loadLevelSprites(14);
-	// DOS RunLevel14Flow uses selector 0x11 for L14PLAY and 0x12 for L14PLAY2.
 	loadTuningForLevel(0x11);
 
 	beginLevelTitleOverlay(13);
@@ -1387,10 +1310,7 @@ bool InsaneRebel1::runLevel14() {
 					_level14Play2BSplicePending = false;
 					level14Phase2Video = "LVL14/L14PLY2B.ANM";
 
-					// DOS queues L14PLY2B from the L14PLAY2 loop with startFrame
-					// equal to L14PLAY2's old timeline frame. L14PLY2B itself is the
-					// continuation clip, so the port starts it from frame 0 but uses
-					// the non-zero frame argument to preserve gameplay/video state.
+					// Preserve gameplay/video state across the continuation clip splice.
 					playInteractiveVideo(level14Phase2Video, spliceFrame);
 					if (shouldAbortGameFlow())
 						return false;
@@ -1426,14 +1346,11 @@ bool InsaneRebel1::runLevel14() {
 	return false;
 }
 
-// Level 15 flow (RunLevel1GameLoop, 0x1B283): Death Star Trench
 // Two interactive phases with mid-level cutscene.
-// Original: L15INTRO ??? L15PLAY1 (trench run) ??? L15INTR2 (torpedo lock cutscene)
 //   ??? L15PLAY2 (final approach + torpedo) ??? L15END1/L15NEW/L15DEATH
 bool InsaneRebel1::runLevel15() {
 	_currentLevel = 14;
 	loadLevelSprites(15);
-	// DOS RunLevel1GameLoop uses selector 0x13 for L15PLAY1 and 0x14 for L15PLAY2.
 	loadTuningForLevel(0x13);
 
 	beginLevelTitleOverlay(14);
@@ -1457,9 +1374,7 @@ bool InsaneRebel1::runLevel15() {
 			if (shouldAbortGameFlow())
 				return false;
 
-			// Phase 2: final approach and torpedo shot. The DOS flow enables
-			// torpedo mode at frontend/movie frame 0x18A and completes only after object-state
-			// bit 0x7602 & 2 is set by the exhaust-port hit.
+			// Phase 2: final approach and torpedo shot.
 			_activeGameOpcode = 0;
 			_gameLatch5D = 0;
 			_gameLatch5F = 0;
@@ -1504,7 +1419,6 @@ bool InsaneRebel1::runLevel15() {
 }
 
 // Main game entry point ??? called from ScummEngine::go().
-// Matches original flow at 0x15597: intro ??? menu ??? level.
 void InsaneRebel1::runGame() {
 	typedef bool (InsaneRebel1::*RunLevelMethod)();
 	const RunLevelMethod kLevelRunners[] = {
@@ -1611,7 +1525,6 @@ void InsaneRebel1::runGame() {
 			runOptionsMenu();
 			break;
 		case 3: {
-			// Enter Passcode ??? original RunPasscodeEntryDialog starts the
 			// game from the decoded chapter when a valid passcode is entered.
 			const int passcodeLevel = runPasscodeEntryDialog();
 			if (passcodeLevel >= 1 && passcodeLevel <= numLevels)
@@ -1630,7 +1543,6 @@ void InsaneRebel1::runGame() {
 		}
 		case 4: {
 			// Level Select: extra start point. Continue through the
-			// original successor flow so post-level cinematics still play.
 			int selectedLevel = runLevelSelectMenu();
 			if (selectedLevel >= 1 && selectedLevel <= numLevels)
 				runLevelsFrom(selectedLevel, true);
@@ -1638,7 +1550,6 @@ void InsaneRebel1::runGame() {
 		}
 		case 5:
 			// CONTINUE DEMO ??? attract mode.
-			// Original shows TOP PILOTS (O1SCORE.ANM) then loops O1OPEN.ANM.
 			showHighScores();
 			if (!shouldAbortGameFlow())
 				playCinematic("OPEN/O1OPEN.ANM");
@@ -1713,7 +1624,6 @@ void InsaneRebel1::setupInteractiveVideoState(int32 startFrame) {
 		resetGamepadReticleAim();
 	}
 	_vm->_smushVideoShouldFinish = false;
-	// Route resumes stay in the same gameplay flow in the original executable.
 	// Preserve the previous video/runtime state, but keep the destination clip
 	// fully interactive from its first visible frame.
 	splayer->setPreserveVideoStateOnNextPlay(preserveVideoState);
@@ -1730,9 +1640,7 @@ void InsaneRebel1::resolveSeek(const char *filename, int32 startFrame, int32 &vi
 	videoOffset = 0;
 
 	if (_currentLevel == 6 && level7RouteSplice) {
-		// DOS opens the route ANM from the beginning, then the armed frame gate
-		// suppresses until the adjusted target. With the delayed cutover,
-		// the destination must advance by the source tail already displayed.
+		// Advance the destination route by the previous clip tail already displayed.
 		videoStartFrame = (_pendingRouteVideoStartFrame > 0) ?
 			_pendingRouteVideoStartFrame : 1;
 		videoOffset = findAnimFrameChunkOffset(_vm, filename, videoStartFrame);
@@ -1764,10 +1672,7 @@ void InsaneRebel1::resolveSeek(const char *filename, int32 startFrame, int32 &vi
 				_levelRouteIndex, (int)startFrame, (int)videoStartFrame, (unsigned)videoOffset);
 		}
 	} else if (_currentLevel == 13 && resumingRoute) {
-		// RunLevel14Flow calls PlayAnmFile("LVL14/L14PLY2B.ANM", 0x860,
-		// oldMaxFrame-0x0F, 1, -1). That frame number belongs to L14PLAY2's
-		// timeline; L14PLY2B is already the continuation clip and starts at its
-		// matching lead-in frame. Preserve the current state, but do not seek.
+		// L14PLY2B is already the continuation clip. Preserve state, but do not seek.
 		debugC(DEBUG_INSANE, "L14 splice: L14PLAY2 timelineFrame=%d -> L14PLY2B frame 0",
 			(int)startFrame);
 	}
@@ -1779,14 +1684,10 @@ void InsaneRebel1::captureInteractiveVideoInput() {
 
 	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.
+	// Center mouse, hide system cursor, and lock mouse to window.
 	if (!preserveInputState) {
 		_gameplayMouseSettleUntil = 0;
-		// On touchscreen devices the DOS recenter-the-cursor aiming model does not apply;
-		// warping/locking the system mouse there injects spurious motion that drifts
-		// direct touch aiming and on-screen controls.
+		// Touch input uses absolute aiming, so avoid synthetic mouse motion there.
 		if (!isTouchscreenActive()) {
 			warpGameplayMouseNow(kRA1CenterX, kRA1CenterY);
 			_gameplayMouseSettleUntil = _vm->_system->getMillis() + kRA1GameplayMouseSettleMs;



From noreply at scummvm.org  Sun Jun 21 16:24:47 2026
From: noreply at scummvm.org (ScummVM-Translations)
Date: Sun, 21 Jun 2026 16:24:47 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 13831ab42de61fcf8986c6df57e4200f0f9afce4
Message-ID: <20260621162447.305EA7005D2@ron.scummvm.net>

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

Summary:
13831ab42d I18N: Update translations templates


Commit: 13831ab42de61fcf8986c6df57e4200f0f9afce4
    https://github.com/scummvm/scummvm/commit/13831ab42de61fcf8986c6df57e4200f0f9afce4
Author: Weblate (noreply at weblate.org)
Date: 2026-06-21T16:24:42Z

Commit Message:
I18N: Update translations templates

Changed paths:
    po/scummvm.pot


diff --git a/po/scummvm.pot b/po/scummvm.pot
index 0cde593ee81..7af4781451b 100644
--- a/po/scummvm.pot
+++ b/po/scummvm.pot
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2026.3.1git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-21 13:20+0000\n"
+"POT-Creation-Date: 2026-06-21 16:24+0000\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL at ADDRESS>\n"
 "Language-Team: LANGUAGE <LL at li.org>\n"
@@ -12610,7 +12610,7 @@ msgid "Rebel Assault II controls"
 msgstr ""
 
 #: engines/scumm/metaengine.cpp:1298
-msgid "Cover / back"
+msgid "Cover"
 msgstr ""
 
 #: engines/scumm/metaengine.cpp:1304



From noreply at scummvm.org  Sun Jun 21 16:25:26 2026
From: noreply at scummvm.org (ScummVM-Translations)
Date: Sun, 21 Jun 2026 16:25:26 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 d3f395a1b1a786c710523971b569eafac432454c
Message-ID: <20260621162526.AC1E57005CE@ron.scummvm.net>

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

Summary:
d3f395a1b1 I18N: Update translation files


Commit: d3f395a1b1a786c710523971b569eafac432454c
    https://github.com/scummvm/scummvm/commit/d3f395a1b1a786c710523971b569eafac432454c
Author: Weblate (noreply at weblate.org)
Date: 2026-06-21T16:25:04Z

Commit Message:
I18N: Update translation files

Updated by "Update PO files to match POT (msgmerge)" hook in Weblate.

Translation: ScummVM/scummvm
Translate-URL: https://translations.scummvm.org/projects/scummvm/scummvm/

Changed paths:
    po/ar.po
    po/be-tarask.po
    po/be_BY.po
    po/ca_ES.po
    po/cs_CZ.po
    po/da.po
    po/de_DE.po
    po/el.po
    po/es_ES.po
    po/eu.po
    po/fi_FI.po
    po/fr_FR.po
    po/gl_ES.po
    po/he.po
    po/hi.po
    po/hu_HU.po
    po/it_IT.po
    po/ja.po
    po/ka.po
    po/ko.po
    po/nb_NO.po
    po/nl_NL.po
    po/nn_NO.po
    po/pl_PL.po
    po/pt_BR.po
    po/pt_PT.po
    po/ro.po
    po/ru_RU.po
    po/sv_SE.po
    po/tr.po
    po/uk_UA.po
    po/zh.po
    po/zh_Hans.po
    po/zh_Hant.po


diff --git a/po/ar.po b/po/ar.po
index d5dc71e2e23..95e31dbda87 100644
--- a/po/ar.po
+++ b/po/ar.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-21 13:20+0000\n"
+"POT-Creation-Date: 2026-06-21 16:24+0000\n"
 "PO-Revision-Date: 2026-02-24 22:26+0000\n"
 "Last-Translator: Mohamed Shaaban <mohmedbashamedo at hotmail.com>\n"
 "Language-Team: Arabic <https://translations.scummvm.org/projects/scummvm/"
@@ -14439,10 +14439,8 @@ msgid "Rebel Assault II controls"
 msgstr ""
 
 #: engines/scumm/metaengine.cpp:1298
-#, fuzzy
-#| msgid "Move Back"
-msgid "Cover / back"
-msgstr "???????? ????????????"
+msgid "Cover"
+msgstr ""
 
 #: engines/scumm/metaengine.cpp:1304
 #, fuzzy
@@ -18553,6 +18551,11 @@ msgid ""
 "supported. Continue launching game?"
 msgstr ""
 
+#, fuzzy
+#~| msgid "Move Back"
+#~ msgid "Cover / back"
+#~ msgstr "???????? ????????????"
+
 #, fuzzy
 #~| msgid "Move Back"
 #~ msgid "Menu back"
diff --git a/po/be-tarask.po b/po/be-tarask.po
index d6956cd6c48..c937111b822 100644
--- a/po/be-tarask.po
+++ b/po/be-tarask.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-21 13:20+0000\n"
+"POT-Creation-Date: 2026-06-21 16:24+0000\n"
 "PO-Revision-Date: 2023-02-26 15:01+0000\n"
 "Last-Translator: Kastu?? K. <oakum2334 at gmail.com>\n"
 "Language-Team: Belarusian (Tara??kievica) <https://translations.scummvm.org/"
@@ -14367,10 +14367,8 @@ msgid "Rebel Assault II controls"
 msgstr ""
 
 #: engines/scumm/metaengine.cpp:1298
-#, fuzzy
-#| msgid "Move Back"
-msgid "Cover / back"
-msgstr "?????? ??????????"
+msgid "Cover"
+msgstr ""
 
 #: engines/scumm/metaengine.cpp:1304
 #, fuzzy
@@ -18469,6 +18467,11 @@ msgid ""
 "supported. Continue launching game?"
 msgstr ""
 
+#, fuzzy
+#~| msgid "Move Back"
+#~ msgid "Cover / back"
+#~ msgstr "?????? ??????????"
+
 #, fuzzy
 #~| msgid "Move Back"
 #~ msgid "Menu back"
diff --git a/po/be_BY.po b/po/be_BY.po
index 859ad26525a..57c6fd94bec 100644
--- a/po/be_BY.po
+++ b/po/be_BY.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-21 13:20+0000\n"
+"POT-Creation-Date: 2026-06-21 16:24+0000\n"
 "PO-Revision-Date: 2021-03-21 08:24+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Belarusian <https://translations.scummvm.org/projects/scummvm/"
@@ -14176,10 +14176,8 @@ msgid "Rebel Assault II controls"
 msgstr ""
 
 #: engines/scumm/metaengine.cpp:1298
-#, fuzzy
-#| msgid "Move Back"
-msgid "Cover / back"
-msgstr "???????? ??????????"
+msgid "Cover"
+msgstr ""
 
 #: engines/scumm/metaengine.cpp:1304
 #, fuzzy
@@ -18179,6 +18177,11 @@ msgid ""
 "supported. Continue launching game?"
 msgstr ""
 
+#, fuzzy
+#~| msgid "Move Back"
+#~ msgid "Cover / back"
+#~ msgstr "???????? ??????????"
+
 #, fuzzy
 #~| msgid "Move Back"
 #~ msgid "Menu back"
diff --git a/po/ca_ES.po b/po/ca_ES.po
index 7d60fada160..a87abe2212d 100644
--- a/po/ca_ES.po
+++ b/po/ca_ES.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.6.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-21 13:20+0000\n"
+"POT-Creation-Date: 2026-06-21 16:24+0000\n"
 "PO-Revision-Date: 2026-05-26 04:22+0000\n"
 "Last-Translator: Xaviu <xaviu at xaviu.com>\n"
 "Language-Team: Catalan <https://translations.scummvm.org/projects/scummvm/"
@@ -13251,10 +13251,8 @@ msgid "Rebel Assault II controls"
 msgstr ""
 
 #: engines/scumm/metaengine.cpp:1298
-#, fuzzy
-#| msgid "Move back"
-msgid "Cover / back"
-msgstr "Mou enrere"
+msgid "Cover"
+msgstr ""
 
 #: engines/scumm/metaengine.cpp:1304
 #, fuzzy
@@ -16725,6 +16723,11 @@ msgid ""
 "supported. Continue launching game?"
 msgstr ""
 
+#, fuzzy
+#~| msgid "Move back"
+#~ msgid "Cover / back"
+#~ msgstr "Mou enrere"
+
 #, fuzzy
 #~| msgid "Move back"
 #~ msgid "Menu back"
diff --git a/po/cs_CZ.po b/po/cs_CZ.po
index d9e24192de1..84f0f4118ea 100644
--- a/po/cs_CZ.po
+++ b/po/cs_CZ.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.7.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-21 13:20+0000\n"
+"POT-Creation-Date: 2026-06-21 16:24+0000\n"
 "PO-Revision-Date: 2026-02-09 20:22+0000\n"
 "Last-Translator: AsciiWolf <mail at asciiwolf.com>\n"
 "Language-Team: Czech <https://translations.scummvm.org/projects/scummvm/"
@@ -13977,10 +13977,8 @@ msgid "Rebel Assault II controls"
 msgstr ""
 
 #: engines/scumm/metaengine.cpp:1298
-#, fuzzy
-#| msgid "Move Back"
-msgid "Cover / back"
-msgstr "Vzad"
+msgid "Cover"
+msgstr ""
 
 #: engines/scumm/metaengine.cpp:1304
 #, fuzzy
@@ -17924,6 +17922,11 @@ msgid ""
 "supported. Continue launching game?"
 msgstr ""
 
+#, fuzzy
+#~| msgid "Move Back"
+#~ msgid "Cover / back"
+#~ msgstr "Vzad"
+
 #, fuzzy
 #~| msgid "Move Back"
 #~ msgid "Menu back"
diff --git a/po/da.po b/po/da.po
index b2572004e91..8bc49270cec 100644
--- a/po/da.po
+++ b/po/da.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-21 13:20+0000\n"
+"POT-Creation-Date: 2026-06-21 16:24+0000\n"
 "PO-Revision-Date: 2023-09-07 17:04+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Danish <https://translations.scummvm.org/projects/scummvm/"
@@ -14071,10 +14071,8 @@ msgid "Rebel Assault II controls"
 msgstr ""
 
 #: engines/scumm/metaengine.cpp:1298
-#, fuzzy
-#| msgid "Move Back"
-msgid "Cover / back"
-msgstr "Flyt bagud"
+msgid "Cover"
+msgstr ""
 
 #: engines/scumm/metaengine.cpp:1304
 #, fuzzy
@@ -18050,6 +18048,11 @@ msgid ""
 "supported. Continue launching game?"
 msgstr ""
 
+#, fuzzy
+#~| msgid "Move Back"
+#~ msgid "Cover / back"
+#~ msgstr "Flyt bagud"
+
 #, fuzzy
 #~| msgid "Move Back"
 #~ msgid "Menu back"
diff --git a/po/de_DE.po b/po/de_DE.po
index 4fe379aaf23..e56a9856194 100644
--- a/po/de_DE.po
+++ b/po/de_DE.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.10.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-21 13:20+0000\n"
+"POT-Creation-Date: 2026-06-21 16:24+0000\n"
 "PO-Revision-Date: 2026-05-05 16:31+0000\n"
 "Last-Translator: Lothar Serra Mari <lothar.serramari at scummvm.org>\n"
 "Language-Team: German <https://translations.scummvm.org/projects/scummvm/"
@@ -13982,10 +13982,8 @@ msgid "Rebel Assault II controls"
 msgstr ""
 
 #: engines/scumm/metaengine.cpp:1298
-#, fuzzy
-#| msgid "Move back"
-msgid "Cover / back"
-msgstr "Zur??ckgehen"
+msgid "Cover"
+msgstr ""
 
 #: engines/scumm/metaengine.cpp:1304
 #, fuzzy
@@ -17589,6 +17587,11 @@ msgstr ""
 "Untertiteln gel??st werden, sofern diese unterst??tzt werden. M??chten Sie das "
 "Spiel trotzdem starten?"
 
+#, fuzzy
+#~| msgid "Move back"
+#~ msgid "Cover / back"
+#~ msgstr "Zur??ckgehen"
+
 #, fuzzy
 #~| msgid "Move back"
 #~ msgid "Menu back"
diff --git a/po/el.po b/po/el.po
index 9c19b6070f1..c4f71707726 100644
--- a/po/el.po
+++ b/po/el.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.10.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-21 13:20+0000\n"
+"POT-Creation-Date: 2026-06-21 16:24+0000\n"
 "PO-Revision-Date: 2026-04-23 00:22+0000\n"
 "Last-Translator: Antoniou Athanasios <a.antoniou79 at gmail.com>\n"
 "Language-Team: Greek <https://translations.scummvm.org/projects/scummvm/"
@@ -14137,10 +14137,8 @@ msgid "Rebel Assault II controls"
 msgstr ""
 
 #: engines/scumm/metaengine.cpp:1298
-#, fuzzy
-#| msgid "Move Back"
-msgid "Cover / back"
-msgstr "?????????????????? ????????"
+msgid "Cover"
+msgstr ""
 
 #: engines/scumm/metaengine.cpp:1304
 #, fuzzy
@@ -17877,6 +17875,11 @@ msgstr ""
 "???? ?????????????????? ???? ?????????? ?????????????????? , ???? ????????????????????????????. ???? ???????????????????? ?????? "
 "???????????????? ?????? ????????????????????;"
 
+#, fuzzy
+#~| msgid "Move Back"
+#~ msgid "Cover / back"
+#~ msgstr "?????????????????? ????????"
+
 #, fuzzy
 #~| msgid "Move Back"
 #~ msgid "Menu back"
diff --git a/po/es_ES.po b/po/es_ES.po
index 94b40a96c78..aa0c4e75aa3 100644
--- a/po/es_ES.po
+++ b/po/es_ES.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.4.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-21 13:20+0000\n"
+"POT-Creation-Date: 2026-06-21 16:24+0000\n"
 "PO-Revision-Date: 2026-06-20 15:53+0000\n"
 "Last-Translator: IlDucci <pajaroloco_2 at hotmail.com>\n"
 "Language-Team: Spanish <https://translations.scummvm.org/projects/scummvm/"
@@ -13912,8 +13912,8 @@ msgid "Rebel Assault II controls"
 msgstr "Controles de Rebel Assault II"
 
 #: engines/scumm/metaengine.cpp:1298
-msgid "Cover / back"
-msgstr "Cubrirse/Volver"
+msgid "Cover"
+msgstr ""
 
 #: engines/scumm/metaengine.cpp:1304
 msgid "Skip / back"
@@ -17494,6 +17494,9 @@ msgstr ""
 "se podr??an resolver estos acertijos con subt??tulos, si est??n disponibles. "
 "??Quieres seguir ejecutando el juego?"
 
+#~ msgid "Cover / back"
+#~ msgstr "Cubrirse/Volver"
+
 #~ msgid "Menu back"
 #~ msgstr "Volver (men??s)"
 
diff --git a/po/eu.po b/po/eu.po
index da3b6768a25..8ebeb1c1773 100644
--- a/po/eu.po
+++ b/po/eu.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.5.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-21 13:20+0000\n"
+"POT-Creation-Date: 2026-06-21 16:24+0000\n"
 "PO-Revision-Date: 2024-02-05 18:42+0000\n"
 "Last-Translator: Josu Igoa <josuigoa at ni.eus>\n"
 "Language-Team: Basque <https://translations.scummvm.org/projects/scummvm/"
@@ -14040,10 +14040,8 @@ msgid "Rebel Assault II controls"
 msgstr ""
 
 #: engines/scumm/metaengine.cpp:1298
-#, fuzzy
-#| msgid "Move Back"
-msgid "Cover / back"
-msgstr "Atzera mugitu"
+msgid "Cover"
+msgstr ""
 
 #: engines/scumm/metaengine.cpp:1304
 #, fuzzy
@@ -18024,6 +18022,11 @@ msgid ""
 "supported. Continue launching game?"
 msgstr ""
 
+#, fuzzy
+#~| msgid "Move Back"
+#~ msgid "Cover / back"
+#~ msgstr "Atzera mugitu"
+
 #, fuzzy
 #~| msgid "Move Back"
 #~ msgid "Menu back"
diff --git a/po/fi_FI.po b/po/fi_FI.po
index 780ea69cbe0..d7dd582e000 100644
--- a/po/fi_FI.po
+++ b/po/fi_FI.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.6.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-21 13:20+0000\n"
+"POT-Creation-Date: 2026-06-21 16:24+0000\n"
 "PO-Revision-Date: 2026-02-07 15:22+0000\n"
 "Last-Translator: Timo Mikkolainen <tmikkola at gmail.com>\n"
 "Language-Team: Finnish <https://translations.scummvm.org/projects/scummvm/"
@@ -14182,10 +14182,8 @@ msgid "Rebel Assault II controls"
 msgstr ""
 
 #: engines/scumm/metaengine.cpp:1298
-#, fuzzy
-#| msgid "Move back"
-msgid "Cover / back"
-msgstr "Liiku taaksep??in"
+msgid "Cover"
+msgstr ""
 
 #: engines/scumm/metaengine.cpp:1304
 #, fuzzy
@@ -17905,6 +17903,11 @@ msgid ""
 "supported. Continue launching game?"
 msgstr ""
 
+#, fuzzy
+#~| msgid "Move back"
+#~ msgid "Cover / back"
+#~ msgstr "Liiku taaksep??in"
+
 #, fuzzy
 #~| msgid "Move back"
 #~ msgid "Menu back"
diff --git a/po/fr_FR.po b/po/fr_FR.po
index b78c00516e8..9ed9d426491 100644
--- a/po/fr_FR.po
+++ b/po/fr_FR.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-21 13:20+0000\n"
+"POT-Creation-Date: 2026-06-21 16:24+0000\n"
 "PO-Revision-Date: 2026-06-14 09:41+0000\n"
 "Last-Translator: Purple T <ZEONK at hotmail.com>\n"
 "Language-Team: French <https://translations.scummvm.org/projects/scummvm/"
@@ -13941,8 +13941,8 @@ msgid "Rebel Assault II controls"
 msgstr "Contr??les de Rebel Assault II"
 
 #: engines/scumm/metaengine.cpp:1298
-msgid "Cover / back"
-msgstr "Couverture / retour"
+msgid "Cover"
+msgstr ""
 
 #: engines/scumm/metaengine.cpp:1304
 msgid "Skip / back"
@@ -17548,6 +17548,9 @@ msgstr ""
 "r??solues.  Ces puzzles peuvent ??galement ??tre r??solus ?? l'aide de sous-"
 "titres, s'ils sont pris en charge. Continuer ?? lancer le jeu ?"
 
+#~ msgid "Cover / back"
+#~ msgstr "Couverture / retour"
+
 #~ msgid "Menu back"
 #~ msgstr "Menu retour"
 
diff --git a/po/gl_ES.po b/po/gl_ES.po
index 811a6a74330..fe0ca1a7d6d 100644
--- a/po/gl_ES.po
+++ b/po/gl_ES.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-21 13:20+0000\n"
+"POT-Creation-Date: 2026-06-21 16:24+0000\n"
 "PO-Revision-Date: 2020-09-26 21:13+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Galician <https://translations.scummvm.org/projects/scummvm/"
@@ -13992,10 +13992,8 @@ msgid "Rebel Assault II controls"
 msgstr ""
 
 #: engines/scumm/metaengine.cpp:1298
-#, fuzzy
-#| msgid "Move Back"
-msgid "Cover / back"
-msgstr "Mover cara atr??s"
+msgid "Cover"
+msgstr ""
 
 #: engines/scumm/metaengine.cpp:1304
 #, fuzzy
@@ -17930,6 +17928,11 @@ msgid ""
 "supported. Continue launching game?"
 msgstr ""
 
+#, fuzzy
+#~| msgid "Move Back"
+#~ msgid "Cover / back"
+#~ msgstr "Mover cara atr??s"
+
 #, fuzzy
 #~| msgid "Move Back"
 #~ msgid "Menu back"
diff --git a/po/he.po b/po/he.po
index c2cfa529f4a..2a940df8c4f 100644
--- a/po/he.po
+++ b/po/he.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.1.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-21 13:20+0000\n"
+"POT-Creation-Date: 2026-06-21 16:24+0000\n"
 "PO-Revision-Date: 2025-04-09 20:18+0000\n"
 "Last-Translator: Niv Baehr <bloop93 at gmail.com>\n"
 "Language-Team: Hebrew <https://translations.scummvm.org/projects/scummvm/"
@@ -14224,10 +14224,8 @@ msgid "Rebel Assault II controls"
 msgstr ""
 
 #: engines/scumm/metaengine.cpp:1298
-#, fuzzy
-#| msgid "Move Back"
-msgid "Cover / back"
-msgstr "??????????"
+msgid "Cover"
+msgstr ""
 
 #: engines/scumm/metaengine.cpp:1304
 #, fuzzy
@@ -18333,6 +18331,11 @@ msgid ""
 "supported. Continue launching game?"
 msgstr ""
 
+#, fuzzy
+#~| msgid "Move Back"
+#~ msgid "Cover / back"
+#~ msgstr "??????????"
+
 #, fuzzy
 #~| msgid "Move Back"
 #~ msgid "Menu back"
diff --git a/po/hi.po b/po/hi.po
index 9cd9274ed29..b914ffc07c2 100644
--- a/po/hi.po
+++ b/po/hi.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-21 13:20+0000\n"
+"POT-Creation-Date: 2026-06-21 16:24+0000\n"
 "PO-Revision-Date: 2026-05-12 22:22+0000\n"
 "Last-Translator: KARTIK SAXENA <kartiksaxena2005 at gmail.com>\n"
 "Language-Team: Hindi <https://translations.scummvm.org/projects/scummvm/"
@@ -13468,10 +13468,8 @@ msgid "Rebel Assault II controls"
 msgstr ""
 
 #: engines/scumm/metaengine.cpp:1298
-#, fuzzy
-#| msgid "Move Forward"
-msgid "Cover / back"
-msgstr "????????? ?????????"
+msgid "Cover"
+msgstr ""
 
 #: engines/scumm/metaengine.cpp:1304
 #, fuzzy
@@ -17316,6 +17314,11 @@ msgid ""
 "supported. Continue launching game?"
 msgstr ""
 
+#, fuzzy
+#~| msgid "Move Forward"
+#~ msgid "Cover / back"
+#~ msgstr "????????? ?????????"
+
 #, fuzzy
 #~| msgid "Move Forward"
 #~ msgid "Menu back"
diff --git a/po/hu_HU.po b/po/hu_HU.po
index 634454a2171..f525b47c9cc 100644
--- a/po/hu_HU.po
+++ b/po/hu_HU.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-21 13:20+0000\n"
+"POT-Creation-Date: 2026-06-21 16:24+0000\n"
 "PO-Revision-Date: 2025-03-07 00:18+0000\n"
 "Last-Translator: Hov??nszki Tam??s <gahex68379 at lofiey.com>\n"
 "Language-Team: Hungarian <https://translations.scummvm.org/projects/scummvm/"
@@ -14689,10 +14689,8 @@ msgid "Rebel Assault II controls"
 msgstr ""
 
 #: engines/scumm/metaengine.cpp:1298
-#, fuzzy
-#| msgid "Move Back"
-msgid "Cover / back"
-msgstr "Mozg??s h??tra"
+msgid "Cover"
+msgstr ""
 
 #: engines/scumm/metaengine.cpp:1304
 #, fuzzy
@@ -18766,6 +18764,11 @@ msgid ""
 "supported. Continue launching game?"
 msgstr ""
 
+#, fuzzy
+#~| msgid "Move Back"
+#~ msgid "Cover / back"
+#~ msgstr "Mozg??s h??tra"
+
 #, fuzzy
 #~| msgid "Move Back"
 #~ msgid "Menu back"
diff --git a/po/it_IT.po b/po/it_IT.po
index dedec9035fd..cd2f63405e7 100644
--- a/po/it_IT.po
+++ b/po/it_IT.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-21 13:20+0000\n"
+"POT-Creation-Date: 2026-06-21 16:24+0000\n"
 "PO-Revision-Date: 2026-06-20 15:07+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Italian <https://translations.scummvm.org/projects/scummvm/"
@@ -13882,8 +13882,8 @@ msgid "Rebel Assault II controls"
 msgstr "Controlli di Rebel Assault II"
 
 #: engines/scumm/metaengine.cpp:1298
-msgid "Cover / back"
-msgstr "Copertura / indietro"
+msgid "Cover"
+msgstr ""
 
 #: engines/scumm/metaengine.cpp:1304
 msgid "Skip / back"
@@ -17472,6 +17472,9 @@ msgstr ""
 "poterli risolvere. In alternativa, i puzzle possono essere risolti usando i "
 "sottotitoli, se supportati. Desideri continuare ad avviare il gioco?"
 
+#~ msgid "Cover / back"
+#~ msgstr "Copertura / indietro"
+
 #~ msgid "Menu back"
 #~ msgstr "Indietro (men??)"
 
diff --git a/po/ja.po b/po/ja.po
index 96938863038..4e571a861a7 100644
--- a/po/ja.po
+++ b/po/ja.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-21 13:20+0000\n"
+"POT-Creation-Date: 2026-06-21 16:24+0000\n"
 "PO-Revision-Date: 2026-06-04 07:22+0000\n"
 "Last-Translator: VAN-Gluon <van.gluonp at gmail.com>\n"
 "Language-Team: Japanese <https://translations.scummvm.org/projects/scummvm/"
@@ -14305,10 +14305,8 @@ msgid "Rebel Assault II controls"
 msgstr ""
 
 #: engines/scumm/metaengine.cpp:1298
-#, fuzzy
-#| msgid "Move Back"
-msgid "Cover / back"
-msgstr "??????"
+msgid "Cover"
+msgstr ""
 
 #: engines/scumm/metaengine.cpp:1304
 #, fuzzy
@@ -18391,6 +18389,11 @@ msgid ""
 "supported. Continue launching game?"
 msgstr ""
 
+#, fuzzy
+#~| msgid "Move Back"
+#~ msgid "Cover / back"
+#~ msgstr "??????"
+
 #, fuzzy
 #~| msgid "Move Back"
 #~ msgid "Menu back"
diff --git a/po/ka.po b/po/ka.po
index b4ab8a36203..5c6f38cdce8 100644
--- a/po/ka.po
+++ b/po/ka.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-21 13:20+0000\n"
+"POT-Creation-Date: 2026-06-21 16:24+0000\n"
 "PO-Revision-Date: 2026-06-05 10:22+0000\n"
 "Last-Translator: NorwayFun <temuri.doghonadze at gmail.com>\n"
 "Language-Team: Georgian <https://translations.scummvm.org/projects/scummvm/"
@@ -12670,10 +12670,8 @@ msgid "Rebel Assault II controls"
 msgstr ""
 
 #: engines/scumm/metaengine.cpp:1298
-#, fuzzy
-#| msgid "Move back"
-msgid "Cover / back"
-msgstr "???????????? ????????????????????????"
+msgid "Cover"
+msgstr ""
 
 #: engines/scumm/metaengine.cpp:1304
 #, fuzzy
@@ -16080,6 +16078,11 @@ msgid ""
 "supported. Continue launching game?"
 msgstr ""
 
+#, fuzzy
+#~| msgid "Move back"
+#~ msgid "Cover / back"
+#~ msgstr "???????????? ????????????????????????"
+
 #~ msgid "Menu back"
 #~ msgstr "??????????????? ????????????"
 
diff --git a/po/ko.po b/po/ko.po
index a0bceef108a..184a11f6575 100644
--- a/po/ko.po
+++ b/po/ko.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-21 13:20+0000\n"
+"POT-Creation-Date: 2026-06-21 16:24+0000\n"
 "PO-Revision-Date: 2026-05-19 20:22+0000\n"
 "Last-Translator: Hoseok Seo <ddinghoya at gmail.com>\n"
 "Language-Team: Korean <https://translations.scummvm.org/projects/scummvm/"
@@ -13608,10 +13608,8 @@ msgid "Rebel Assault II controls"
 msgstr ""
 
 #: engines/scumm/metaengine.cpp:1298
-#, fuzzy
-#| msgid "Move back"
-msgid "Cover / back"
-msgstr "?????? ??????"
+msgid "Cover"
+msgstr ""
 
 #: engines/scumm/metaengine.cpp:1304
 #, fuzzy
@@ -17133,6 +17131,11 @@ msgstr ""
 "MIDI ???????????? ????????? ?????? ?????? ????????? ???????????? ????????????.  ????????? ????????? ??????"
 "?????? ?????? ????????? ???????????? ????????? ??? ????????????. ????????? ?????? ????????????????"
 
+#, fuzzy
+#~| msgid "Move back"
+#~ msgid "Cover / back"
+#~ msgstr "?????? ??????"
+
 #, fuzzy
 #~| msgid "Move back"
 #~ msgid "Menu back"
diff --git a/po/nb_NO.po b/po/nb_NO.po
index cb7d42444b9..42579199e3c 100644
--- a/po/nb_NO.po
+++ b/po/nb_NO.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-21 13:20+0000\n"
+"POT-Creation-Date: 2026-06-21 16:24+0000\n"
 "PO-Revision-Date: 2026-02-07 15:22+0000\n"
 "Last-Translator: Einar Johan Tr??an S??m??en <einarjohants at gmail.com>\n"
 "Language-Team: Norwegian Bokm??l <https://translations.scummvm.org/projects/"
@@ -14780,10 +14780,8 @@ msgid "Rebel Assault II controls"
 msgstr ""
 
 #: engines/scumm/metaengine.cpp:1298
-#, fuzzy
-#| msgid "Move Back"
-msgid "Cover / back"
-msgstr "Beveg Bakover"
+msgid "Cover"
+msgstr ""
 
 #: engines/scumm/metaengine.cpp:1304
 #, fuzzy
@@ -18942,6 +18940,11 @@ msgid ""
 "supported. Continue launching game?"
 msgstr ""
 
+#, fuzzy
+#~| msgid "Move Back"
+#~ msgid "Cover / back"
+#~ msgstr "Beveg Bakover"
+
 #, fuzzy
 #~| msgid "Move Back"
 #~ msgid "Menu back"
diff --git a/po/nl_NL.po b/po/nl_NL.po
index 43512629b99..edca59f2f78 100644
--- a/po/nl_NL.po
+++ b/po/nl_NL.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.9.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-21 13:20+0000\n"
+"POT-Creation-Date: 2026-06-21 16:24+0000\n"
 "PO-Revision-Date: 2026-02-11 14:23+0000\n"
 "Last-Translator: SecularSteve <fairfull.playing at gmail.com>\n"
 "Language-Team: Dutch <https://translations.scummvm.org/projects/scummvm/"
@@ -13973,10 +13973,8 @@ msgid "Rebel Assault II controls"
 msgstr ""
 
 #: engines/scumm/metaengine.cpp:1298
-#, fuzzy
-#| msgid "Move back"
-msgid "Cover / back"
-msgstr "Teruggaan"
+msgid "Cover"
+msgstr ""
 
 #: engines/scumm/metaengine.cpp:1304
 #, fuzzy
@@ -17585,6 +17583,11 @@ msgstr ""
 "behulp van ondertitels, indien ondersteund. Doorgaan met het starten van het "
 "spel?"
 
+#, fuzzy
+#~| msgid "Move back"
+#~ msgid "Cover / back"
+#~ msgstr "Teruggaan"
+
 #, fuzzy
 #~| msgid "Move back"
 #~ msgid "Menu back"
diff --git a/po/nn_NO.po b/po/nn_NO.po
index 4ebb3f65612..201b267f948 100644
--- a/po/nn_NO.po
+++ b/po/nn_NO.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-21 13:20+0000\n"
+"POT-Creation-Date: 2026-06-21 16:24+0000\n"
 "PO-Revision-Date: 2023-12-14 14:28+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Norwegian Nynorsk <https://translations.scummvm.org/projects/"
@@ -13889,10 +13889,8 @@ msgid "Rebel Assault II controls"
 msgstr ""
 
 #: engines/scumm/metaengine.cpp:1298
-#, fuzzy
-#| msgid "Move Back"
-msgid "Cover / back"
-msgstr "Beveg Bakover"
+msgid "Cover"
+msgstr ""
 
 #: engines/scumm/metaengine.cpp:1304
 #, fuzzy
@@ -17850,6 +17848,11 @@ msgid ""
 "supported. Continue launching game?"
 msgstr ""
 
+#, fuzzy
+#~| msgid "Move Back"
+#~ msgid "Cover / back"
+#~ msgstr "Beveg Bakover"
+
 #, fuzzy
 #~| msgid "Move Back"
 #~ msgid "Menu back"
diff --git a/po/pl_PL.po b/po/pl_PL.po
index ee52aed3e53..5d0e8e0796b 100644
--- a/po/pl_PL.po
+++ b/po/pl_PL.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-21 13:20+0000\n"
+"POT-Creation-Date: 2026-06-21 16:24+0000\n"
 "PO-Revision-Date: 2026-04-17 00:22+0000\n"
 "Last-Translator: SkiffPL <skiff at windowslive.com>\n"
 "Language-Team: Polish <https://translations.scummvm.org/projects/scummvm/"
@@ -14151,10 +14151,8 @@ msgid "Rebel Assault II controls"
 msgstr ""
 
 #: engines/scumm/metaengine.cpp:1298
-#, fuzzy
-#| msgid "Move back"
-msgid "Cover / back"
-msgstr "Ruch do ty??u"
+msgid "Cover"
+msgstr ""
 
 #: engines/scumm/metaengine.cpp:1304
 #, fuzzy
@@ -17839,6 +17837,11 @@ msgstr ""
 "rozwi??zania. Te ??amig????wki mo??na r??wnie?? rozwi??za?? za pomoc?? napis??w, je??li "
 "s?? obs??ugiwane. Kontynuowa?? uruchamianie gry?"
 
+#, fuzzy
+#~| msgid "Move back"
+#~ msgid "Cover / back"
+#~ msgstr "Ruch do ty??u"
+
 #, fuzzy
 #~| msgid "Move back"
 #~ msgid "Menu back"
diff --git a/po/pt_BR.po b/po/pt_BR.po
index 0e0a12f209d..33fb2b0602f 100644
--- a/po/pt_BR.po
+++ b/po/pt_BR.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-21 13:20+0000\n"
+"POT-Creation-Date: 2026-06-21 16:24+0000\n"
 "PO-Revision-Date: 2026-06-17 22:10+0000\n"
 "Last-Translator: Marcel Souza Lemes <marcosoutsider at gmail.com>\n"
 "Language-Team: Portuguese (Brazil) <https://translations.scummvm.org/"
@@ -14821,10 +14821,8 @@ msgid "Rebel Assault II controls"
 msgstr "Controles de Rebel Assault II"
 
 #: engines/scumm/metaengine.cpp:1298
-#, fuzzy
-#| msgid "Move Back"
-msgid "Cover / back"
-msgstr "Mover para Tr??s"
+msgid "Cover"
+msgstr ""
 
 #: engines/scumm/metaengine.cpp:1304
 #, fuzzy
@@ -18948,6 +18946,11 @@ msgstr ""
 "alternativamente, ser resolvidos usando legendas, se houver suporte. "
 "Continuar iniciando o jogo?"
 
+#, fuzzy
+#~| msgid "Move Back"
+#~ msgid "Cover / back"
+#~ msgstr "Mover para Tr??s"
+
 #, fuzzy
 #~| msgid "Move Back"
 #~ msgid "Menu back"
diff --git a/po/pt_PT.po b/po/pt_PT.po
index af952290a92..ee616be3e3a 100644
--- a/po/pt_PT.po
+++ b/po/pt_PT.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.10.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-21 13:20+0000\n"
+"POT-Creation-Date: 2026-06-21 16:24+0000\n"
 "PO-Revision-Date: 2022-06-05 14:09+0000\n"
 "Last-Translator: Daniel <supsuper at gmail.com>\n"
 "Language-Team: Portuguese (Portugal) <https://translations.scummvm.org/"
@@ -14018,10 +14018,8 @@ msgid "Rebel Assault II controls"
 msgstr ""
 
 #: engines/scumm/metaengine.cpp:1298
-#, fuzzy
-#| msgid "Move Back"
-msgid "Cover / back"
-msgstr "Retroceder"
+msgid "Cover"
+msgstr ""
 
 #: engines/scumm/metaengine.cpp:1304
 #, fuzzy
@@ -18008,6 +18006,11 @@ msgid ""
 "supported. Continue launching game?"
 msgstr ""
 
+#, fuzzy
+#~| msgid "Move Back"
+#~ msgid "Cover / back"
+#~ msgstr "Retroceder"
+
 #, fuzzy
 #~| msgid "Move Back"
 #~ msgid "Menu back"
diff --git a/po/ro.po b/po/ro.po
index fe6bc71e218..9f2962bb59a 100644
--- a/po/ro.po
+++ b/po/ro.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.10.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-21 13:20+0000\n"
+"POT-Creation-Date: 2026-06-21 16:24+0000\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: Automatically generated\n"
 "Language-Team: none\n"
@@ -12609,7 +12609,7 @@ msgid "Rebel Assault II controls"
 msgstr ""
 
 #: engines/scumm/metaengine.cpp:1298
-msgid "Cover / back"
+msgid "Cover"
 msgstr ""
 
 #: engines/scumm/metaengine.cpp:1304
diff --git a/po/ru_RU.po b/po/ru_RU.po
index cefb6741e62..d197b1f4080 100644
--- a/po/ru_RU.po
+++ b/po/ru_RU.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.8.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-21 13:20+0000\n"
+"POT-Creation-Date: 2026-06-21 16:24+0000\n"
 "PO-Revision-Date: 2026-06-20 15:03+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Russian <https://translations.scummvm.org/projects/scummvm/"
@@ -13777,8 +13777,8 @@ msgid "Rebel Assault II controls"
 msgstr "???????????????????? Rebel Assault II"
 
 #: engines/scumm/metaengine.cpp:1298
-msgid "Cover / back"
-msgstr "???????????????? / ??????????"
+msgid "Cover"
+msgstr ""
 
 #: engines/scumm/metaengine.cpp:1304
 msgid "Skip / back"
@@ -17325,6 +17325,9 @@ msgstr ""
 "MIDI-??????????. ?????? ?????????????????????? ?????????? ?????????? ???????????? ?? ?????????????? ??????????????????, ???????? ?????? "
 "????????????????????????????. ???????????????????? ???????????? ?????????"
 
+#~ msgid "Cover / back"
+#~ msgstr "???????????????? / ??????????"
+
 #, fuzzy
 #~| msgid "Move back"
 #~ msgid "Menu back"
diff --git a/po/sv_SE.po b/po/sv_SE.po
index 4ac5e6686ca..a6aae47abb2 100644
--- a/po/sv_SE.po
+++ b/po/sv_SE.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.5.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-21 13:20+0000\n"
+"POT-Creation-Date: 2026-06-21 16:24+0000\n"
 "PO-Revision-Date: 2026-04-19 13:22+0000\n"
 "Last-Translator: Daniel Nylander <po at danielnylander.se>\n"
 "Language-Team: Swedish <https://translations.scummvm.org/projects/scummvm/"
@@ -13869,10 +13869,8 @@ msgid "Rebel Assault II controls"
 msgstr ""
 
 #: engines/scumm/metaengine.cpp:1298
-#, fuzzy
-#| msgid "Move back"
-msgid "Cover / back"
-msgstr "Stega bak??t"
+msgid "Cover"
+msgstr ""
 
 #: engines/scumm/metaengine.cpp:1304
 #, fuzzy
@@ -17409,6 +17407,11 @@ msgstr ""
 "l??sas. Dessa pussel kan alternativt l??sas med hj??lp av undertexter, om detta "
 "st??ds. Vill du forts??tta spela?"
 
+#, fuzzy
+#~| msgid "Move back"
+#~ msgid "Cover / back"
+#~ msgstr "Stega bak??t"
+
 #, fuzzy
 #~| msgid "Move back"
 #~ msgid "Menu back"
diff --git a/po/tr.po b/po/tr.po
index d10893cedc1..e30f1b12a47 100644
--- a/po/tr.po
+++ b/po/tr.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-21 13:20+0000\n"
+"POT-Creation-Date: 2026-06-21 16:24+0000\n"
 "PO-Revision-Date: 2024-10-10 12:33+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Turkish <https://translations.scummvm.org/projects/scummvm/"
@@ -13504,10 +13504,8 @@ msgid "Rebel Assault II controls"
 msgstr ""
 
 #: engines/scumm/metaengine.cpp:1298
-#, fuzzy
-#| msgid "Backend"
-msgid "Cover / back"
-msgstr "Altyap??"
+msgid "Cover"
+msgstr ""
 
 #: engines/scumm/metaengine.cpp:1304
 #, fuzzy
@@ -17247,6 +17245,11 @@ msgid ""
 "supported. Continue launching game?"
 msgstr ""
 
+#, fuzzy
+#~| msgid "Backend"
+#~ msgid "Cover / back"
+#~ msgstr "Altyap??"
+
 #, fuzzy
 #~| msgid "Backend"
 #~ msgid "Menu back"
diff --git a/po/uk_UA.po b/po/uk_UA.po
index 7c065ddc21e..3c8ac57f46e 100644
--- a/po/uk_UA.po
+++ b/po/uk_UA.po
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.9.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-21 13:20+0000\n"
+"POT-Creation-Date: 2026-06-21 16:24+0000\n"
 "PO-Revision-Date: 2026-06-20 14:56+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Ukrainian <https://translations.scummvm.org/projects/scummvm/"
@@ -13734,8 +13734,8 @@ msgid "Rebel Assault II controls"
 msgstr "?????????????????? Rebel Assault II"
 
 #: engines/scumm/metaengine.cpp:1298
-msgid "Cover / back"
-msgstr "???????????????? / ??????????"
+msgid "Cover"
+msgstr ""
 
 #: engines/scumm/metaengine.cpp:1304
 msgid "Skip / back"
@@ -17272,6 +17272,9 @@ msgstr ""
 "??????????. ???? ?????????????????????? ?????????? ?????????? ????????'?????????? ???? ?????????????????? ??????????????????, ???????? "
 "???????? ??????????????????????????. ???????????????????? ???????????? ???????"
 
+#~ msgid "Cover / back"
+#~ msgstr "???????????????? / ??????????"
+
 #, fuzzy
 #~| msgid "Move back"
 #~ msgid "Menu back"
diff --git a/po/zh.po b/po/zh.po
index ac9cf7bb5c0..17088afed2b 100644
--- a/po/zh.po
+++ b/po/zh.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-21 13:20+0000\n"
+"POT-Creation-Date: 2026-06-21 16:24+0000\n"
 "PO-Revision-Date: 2024-05-14 12:42+0000\n"
 "Last-Translator: ?????? <clonewith at qq.com>\n"
 "Language-Team: Chinese <https://translations.scummvm.org/projects/scummvm/"
@@ -13331,10 +13331,8 @@ msgid "Rebel Assault II controls"
 msgstr ""
 
 #: engines/scumm/metaengine.cpp:1298
-#, fuzzy
-#| msgid "Backend"
-msgid "Cover / back"
-msgstr "??????"
+msgid "Cover"
+msgstr ""
 
 #: engines/scumm/metaengine.cpp:1304
 #, fuzzy
@@ -17138,6 +17136,11 @@ msgid ""
 "supported. Continue launching game?"
 msgstr ""
 
+#, fuzzy
+#~| msgid "Backend"
+#~ msgid "Cover / back"
+#~ msgstr "??????"
+
 #, fuzzy
 #~| msgid "Backend"
 #~ msgid "Menu back"
diff --git a/po/zh_Hans.po b/po/zh_Hans.po
index 7e0c599cd96..59c6d906aa7 100644
--- a/po/zh_Hans.po
+++ b/po/zh_Hans.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.9.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-21 13:20+0000\n"
+"POT-Creation-Date: 2026-06-21 16:24+0000\n"
 "PO-Revision-Date: 2024-12-01 22:18+0000\n"
 "Last-Translator: ?????? <clonewith at qq.com>\n"
 "Language-Team: Chinese (Simplified Han script) <https://"
@@ -14326,10 +14326,8 @@ msgid "Rebel Assault II controls"
 msgstr ""
 
 #: engines/scumm/metaengine.cpp:1298
-#, fuzzy
-#| msgid "Move Back"
-msgid "Cover / back"
-msgstr "????????????"
+msgid "Cover"
+msgstr ""
 
 #: engines/scumm/metaengine.cpp:1304
 #, fuzzy
@@ -18355,6 +18353,11 @@ msgid ""
 "supported. Continue launching game?"
 msgstr ""
 
+#, fuzzy
+#~| msgid "Move Back"
+#~ msgid "Cover / back"
+#~ msgstr "????????????"
+
 #, fuzzy
 #~| msgid "Move Back"
 #~ msgid "Menu back"
diff --git a/po/zh_Hant.po b/po/zh_Hant.po
index ffdb1d3ec5f..e6016f46e84 100644
--- a/po/zh_Hant.po
+++ b/po/zh_Hant.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.9.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-21 13:20+0000\n"
+"POT-Creation-Date: 2026-06-21 16:24+0000\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: Automatically generated\n"
 "Language-Team: none\n"
@@ -12610,7 +12610,7 @@ msgid "Rebel Assault II controls"
 msgstr ""
 
 #: engines/scumm/metaengine.cpp:1298
-msgid "Cover / back"
+msgid "Cover"
 msgstr ""
 
 #: engines/scumm/metaengine.cpp:1304



From noreply at scummvm.org  Sun Jun 21 16:29:45 2026
From: noreply at scummvm.org (lotharsm)
Date: Sun, 21 Jun 2026 16:29:45 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm-web master ->
 192fbfa9d0956146a2cadee68a414df9c2f7df27
Message-ID: <20260621162946.007CB7005CE@ron.scummvm.net>

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

Summary:
192fbfa9d0 DATA: Update data files


Commit: 192fbfa9d0956146a2cadee68a414df9c2f7df27
    https://github.com/scummvm/scummvm-web/commit/192fbfa9d0956146a2cadee68a414df9c2f7df27
Author: lotharsm (mail at serra.me)
Date: 2026-06-21T16:29:40Z

Commit Message:
DATA: Update data files

Changed paths:
    data/en/scummvm_downloads.yaml


diff --git a/data/en/scummvm_downloads.yaml b/data/en/scummvm_downloads.yaml
index 190708ba..45fd0bdf 100644
--- a/data/en/scummvm_downloads.yaml
+++ b/data/en/scummvm_downloads.yaml
@@ -682,20 +682,20 @@
 -
     enabled: true
     category: scummvm
-    subcategory: old
+    subcategory: release
     category_icon: dc
     user_agent: ''
-    version: 2026.2.0
+    version: 2026.3.0
     url: 'scummvm-{$version}-dreamcast-plainfiles.tar.bz2'
     name: 'Dreamcast plain files'
     notes: ''
 -
     enabled: true
     category: scummvm
-    subcategory: old
+    subcategory: release
     category_icon: dc
     user_agent: ''
-    version: 2026.2.0
+    version: 2026.3.0
     url: 'scummvm-{$version}-dreamcast-nero+demos.zip'
     name: 'Dreamcast Nero Image & Demos'
     notes: ''



From noreply at scummvm.org  Sun Jun 21 19:40:52 2026
From: noreply at scummvm.org (sev-)
Date: Sun, 21 Jun 2026 19:40:52 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm-sites director-buildbot ->
 c28b3d0bb156e0d8883ce7d7ced1eb3741873a22
Message-ID: <20260621194052.D89D17005CE@ron.scummvm.net>

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

Summary:
c28b3d0bb1 IMAGEDIFF: add imagediff integration using wsgi-dashboards plugin


Commit: c28b3d0bb156e0d8883ce7d7ced1eb3741873a22
    https://github.com/scummvm/scummvm-sites/commit/c28b3d0bb156e0d8883ce7d7ced1eb3741873a22
Author: ramyak-sharma (ramyaksharma1 at gmail.com)
Date: 2026-06-21T21:40:49+02:00

Commit Message:
IMAGEDIFF: add imagediff integration using wsgi-dashboards plugin

Changed paths:
    imagediff/main.py
    master.py
    pyproject.toml
    uv.lock


diff --git a/imagediff/main.py b/imagediff/main.py
index b3b85c7..c736b32 100644
--- a/imagediff/main.py
+++ b/imagediff/main.py
@@ -1,9 +1,14 @@
 import os
 import json
-from PIL import Image
+import sys
+
 from flask import Flask, render_template, jsonify, url_for, send_from_directory, request
 
-from config import CACHE_DIR,SCREENSHOTS_DIR
+_imagediff_dir = os.path.dirname(__file__)
+if _imagediff_dir not in sys.path:
+    sys.path.insert(0, _imagediff_dir)
+
+from config import CACHE_DIR, SCREENSHOTS_DIR
 from imagediff import image_diff, encode_image, movie_diff
 
 app = Flask(__name__)
@@ -220,12 +225,14 @@ def create_frame_map(frames):
     return {get_frame_number(f): f for f in frames}
 
 @app.route('/')
+ at app.route('/index.html')
 def index():
     targets = [d for d in os.listdir(SCREENSHOTS_DIR)
                if os.path.isdir(os.path.join(SCREENSHOTS_DIR, d))]
 
     return render_template('index.html', targets=targets)
 
+
 @app.route('/target/<target>')
 def target_detail(target):
     target_path = os.path.join(SCREENSHOTS_DIR, target)
diff --git a/master.py b/master.py
index be258fe..17ded75 100644
--- a/master.py
+++ b/master.py
@@ -13,6 +13,7 @@ from director.build_factory import build_factory
 from director.discord import DiscordStatusPush
 from director.lingo_factory import lingo_factory
 from director.targets import generate_builder, test_targets
+from imagediff.main import app as imagediff_app
 
 # This is a sample buildmaster config file. It must be installed as
 # 'master.cfg' in your buildmaster's base directory.
@@ -208,11 +209,20 @@ if WEB_UI:
     # minimalistic config to activate new web UI
     c["www"] = dict(
         port=env("WEB_UI_CONNECTION", "tcp:5000:interface=127.0.0.1"),
-        plugins=dict(grid_view={}),
+        plugins=dict(grid_view={},
+                     wsgi_dashboards=[{
+                         'name': 'imagediff',
+                         'caption': 'ImageDiff',
+                         'app': imagediff_app,
+                         'order': 5,
+                         'icon': 'area-chart',
+                     }]),
         change_hook_dialects={"github": github_hook},
         allowed_origins=["*"],
     )
 
+
+
     c["www"]["auth"] = util.GitHubAuth(
         env("GITHUB_CLIENT_ID"),
         env("GITHUB_CLIENT_SECRET"),
diff --git a/pyproject.toml b/pyproject.toml
index 027b0ee..a1a83ac 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -12,6 +12,7 @@ dependencies = [
   "certifi",
   "Pillow>=10.0.0",
   "flask>=3.0.0",
+  "buildbot-wsgi-dashboards>=4.3.0",
 ]
 
 [dependency-groups]
diff --git a/uv.lock b/uv.lock
index 9bc047d..80a531e 100644
--- a/uv.lock
+++ b/uv.lock
@@ -300,6 +300,7 @@ version = "0.2.0"
 source = { editable = "." }
 dependencies = [
     { name = "buildbot", extra = ["bundle"] },
+    { name = "buildbot-wsgi-dashboards" },
     { name = "certifi" },
     { name = "environs" },
     { name = "flask" },
@@ -320,6 +321,7 @@ dev = [
 [package.metadata]
 requires-dist = [
     { name = "buildbot", extras = ["bundle"], specifier = "<4.4.0" },
+    { name = "buildbot-wsgi-dashboards", specifier = ">=4.3.0" },
     { name = "certifi" },
     { name = "environs" },
     { name = "flask", specifier = ">=3.0.0" },
@@ -370,6 +372,15 @@ wheels = [
     { url = "https://files.pythonhosted.org/packages/f1/12/1176d49759b0caf41751ccb9f9f0aae78719ff078680b72136050f1ac450/buildbot_worker-4.3.0-py2.py3-none-any.whl", hash = "sha256:cbde571bb635a84c3f9e1395d27f760d8cc02956c55765f19869cf1319e2ea19", size = 93518, upload-time = "2025-05-12T21:41:32.228Z" },
 ]
 
+[[package]]
+name = "buildbot-wsgi-dashboards"
+version = "4.3.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/28/9f/b0ee66b7918cb2c128d7529cc2ba6ed681fd9f48607ceab7a6e3ae5a26d7/buildbot_wsgi_dashboards-4.3.0.tar.gz", hash = "sha256:be87cac6921f6c0b3b1d1e3763ba231cb4049fafd621d8d93e066278132ccfbe", size = 434342, upload-time = "2025-05-12T21:41:45.906Z" }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/9b/e8/d106d3810179bd906b2ba229a70217feedce34326dbc2f39f70524de5bc5/buildbot_wsgi_dashboards-4.3.0-py3-none-any.whl", hash = "sha256:250eac05f678e2edb277c6a5535cdcce46fc0140819a98d78a31b200e6f63a0b", size = 434937, upload-time = "2025-05-12T21:41:33.836Z" },
+]
+
 [[package]]
 name = "buildbot-www"
 version = "4.3.0"



From noreply at scummvm.org  Sun Jun 21 21:01:24 2026
From: noreply at scummvm.org (sev-)
Date: Sun, 21 Jun 2026 21:01:24 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 0f863703a67e172aee38a6f3c25c97cfb25fc68f
Message-ID: <20260621210124.A58697005CE@ron.scummvm.net>

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

Summary:
0f863703a6 AUDIO: Fix compilation on some platforms


Commit: 0f863703a67e172aee38a6f3c25c97cfb25fc68f
    https://github.com/scummvm/scummvm/commit/0f863703a67e172aee38a6f3c25c97cfb25fc68f
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2026-06-21T23:01:13+02:00

Commit Message:
AUDIO: Fix compilation on some platforms

Changed paths:
    audio/rate.cpp


diff --git a/audio/rate.cpp b/audio/rate.cpp
index 7ebf1e88af9..eac9fb350c3 100644
--- a/audio/rate.cpp
+++ b/audio/rate.cpp
@@ -265,7 +265,7 @@ int RateConverter_Impl<inStereo, outStereo, reverseStereo>::downsampleConvert(Au
 		}
 
 		// Process as many samples as we can from the current buffer
-		const int count = MIN(
+		const int count = MIN<int>(
 			_bufferSize / (inStereo ? 2 : 1) / outPos_inc,
 			(int)(outEnd - outBuffer) / (outStereo ? 2 : 1));
 		_bufferSize -= count * outPos_inc * (inStereo ? 2 : 1);



From noreply at scummvm.org  Sun Jun 21 22:02:22 2026
From: noreply at scummvm.org (bluegr)
Date: Sun, 21 Jun 2026 22:02:22 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 f6315c643fec56c4635a8d72a23ff816f1a9ae48
Message-ID: <20260621220222.766497005CE@ron.scummvm.net>

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

Summary:
3c84d71946 NANCY: Data chunk handling updates for Nancy 10 and 11
3f35533111 NANCY: Implement the random movie playing feature for Nancy 11+
33273628ce NANCY: Remove leftover debug code in GridMapPuzzle
940a57fd1b NANCY: Implement TypingQuizPuzzle for Nancy11
10e9821f6b NANCY: Implement new MemoryPuzzle functionality for Nancy11+
979db279fc NANCY: Implement new OrderingPuzzle functionality for Nancy11
6267ae7f62 NANCY: Don't capture events in the text box when it's not visible
cf19cffcfa NANCY: Remove an unneeded assignment in MemoryPuzzle
3f823089d8 NANCY: Implement new functionality for BulPuzzle in Nancy11
b41dc6fb3b NANCY: Add new functionality for CollisionPuzzle in Nancy11
f6315c643f NANCY: Implement the FadeSoundToSilence AR for Nancy11


Commit: 3c84d71946ad5da6f3ea4c270eced4add1ef81d7
    https://github.com/scummvm/scummvm/commit/3c84d71946ad5da6f3ea4c270eced4add1ef81d7
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-06-22T01:01:53+03:00

Commit Message:
NANCY: Data chunk handling updates for Nancy 10 and 11

- Remove the adjustments in the TBOX offsets
- Implement handling for the SCTB (Scrollable text box) chunk for
  Nancy 11 and newer
- Skip two new fields added in the UICL chunk. This fixes the
  cellphone for Nancy 11

Changed paths:
    engines/nancy/enginedata.cpp
    engines/nancy/enginedata.h
    engines/nancy/nancy.cpp


diff --git a/engines/nancy/enginedata.cpp b/engines/nancy/enginedata.cpp
index 482e0c69890..3cd219eb518 100644
--- a/engines/nancy/enginedata.cpp
+++ b/engines/nancy/enginedata.cpp
@@ -253,9 +253,10 @@ TBOX::TBOX(Common::SeekableReadStream *chunkStream) : EngineData(chunkStream) {
 	scrollbarDefaultPos.y = chunkStream->readUint16LE();
 	scrollbarMaxScroll = chunkStream->readUint16LE();
 
-	upOffset = chunkStream->readUint16LE() + 1;
+	uint16 legacyOffsetAdjust = g_nancy->getGameType() < kGameTypeNancy10 ? 1 : 0;
+	upOffset = chunkStream->readUint16LE() + legacyOffsetAdjust;
 	downOffset = chunkStream->readUint16LE();
-	leftOffset = chunkStream->readUint16LE() - 1;
+	leftOffset = chunkStream->readUint16LE() - legacyOffsetAdjust;
 	rightOffset = chunkStream->readUint16LE();
 
 	if (g_nancy->getGameType() <= kGameTypeNancy9) {
@@ -872,8 +873,8 @@ MARK::MARK(Common::SeekableReadStream *chunkStream) : EngineData(chunkStream) {
 }
 
 SCTB::SCTB(Common::SeekableReadStream *chunkStream) : EngineData(chunkStream) {
-	readFilename(*chunkStream, imageName);
-	// TODO
+	readUIPopupHeader(*chunkStream, header);
+	readRect(*chunkStream, restoreSrcRect);
 }
 
 SHUI::SHUI(Common::SeekableReadStream *chunkStream) : EngineData(chunkStream) {
@@ -994,6 +995,13 @@ UICL::UICL(Common::SeekableReadStream *chunkStream) : EngineData(chunkStream) {
 	readRect(*chunkStream, connectingSpriteSrc);
 	readRect(*chunkStream, connectingSpriteSrcAlt);
 	readRect(*chunkStream, connectingSpriteDest);
+
+	if (g_nancy->getGameType() >= kGameTypeNancy11) {
+		// TODO: Looks to be a new coordinate - values (548, 50)
+		chunkStream->skip(4);
+		chunkStream->skip(4);
+	}
+
 	readRect(*chunkStream, onlineHeading.srcRect);
 	readRect(*chunkStream, onlineHeading.destRect);
 	readRect(*chunkStream, fullEmptyScreenSrc);
diff --git a/engines/nancy/enginedata.h b/engines/nancy/enginedata.h
index 11d29695d9d..bf430b4a2e4 100644
--- a/engines/nancy/enginedata.h
+++ b/engines/nancy/enginedata.h
@@ -506,13 +506,13 @@ struct SHUI : public EngineData {
 	Common::Array<Common::Rect> _sliderRects;	// Slider rects
 };
 
-// ScrollTextBox - introduced in Nancy 11. Configuration for the scrollable
-// text-box UI used for long textbox content (e.g. journal / scheduled-talk
-// panels) that needs to scroll within a fixed frame.
+// ScrollTextBox - introduced in Nancy 11. Standard UI popup header,
+// followed by the rect to restore when the text box is closed.
 struct SCTB : public EngineData {
 	SCTB(Common::SeekableReadStream *chunkStream);
 
-	Common::Path imageName;
+	UIPopupHeader header;
+	Common::Rect restoreSrcRect;
 };
 
 enum TaskButton {
diff --git a/engines/nancy/nancy.cpp b/engines/nancy/nancy.cpp
index 243c95591d9..ff4da9ff9bb 100644
--- a/engines/nancy/nancy.cpp
+++ b/engines/nancy/nancy.cpp
@@ -487,12 +487,12 @@ void NancyEngine::bootGameEngine() {
 	LOAD_BOOT(TASK)	// Task bar (main UI)
 	LOAD_BOOT(UIIV)	// Inventory UI
 	LOAD_BOOT(UICO)	// Conversation UI
-	LOAD_BOOT(UICL) // Cell phone UI
-	LOAD_BOOT(UIBW) // Web browser UI
-	LOAD_BOOT(UINB) // Notebook UI
+	LOAD_BOOT(UICL)	// Cell phone UI
+	LOAD_BOOT(UIBW)	// Web browser UI
+	LOAD_BOOT(UINB)	// Notebook UI
 
 	// Nancy 11+
-	LOAD_BOOT(SCTB)	// Scheduled talk (?) UI
+	LOAD_BOOT(SCTB)	// Scrollable text box UI
 
 	// Nancy 12+
 	// HINT chunk has been removed
@@ -503,7 +503,7 @@ void NancyEngine::bootGameEngine() {
 	// RCPR and RCLB chunks have been removed
 	// LOAD_BOOT(MMIX)
 
-	// Nancy 14+
+	// Nancy 14
 	// LOAD_BOOT(UICM)
 
 	// Nancy 15+


Commit: 3f355331111a51c4ef7bde975791e8e2bcd5c854
    https://github.com/scummvm/scummvm/commit/3f355331111a51c4ef7bde975791e8e2bcd5c854
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-06-22T01:01:55+03:00

Commit Message:
NANCY: Implement the random movie playing feature for Nancy 11+

Nancy 11 and newer games used movies that played randomly when talking
to certain characters. These movies are loaded when a conversation
starts, and are unloaded when it ends by the newly introduced AR
PlayRandomMovieControl

Changed paths:
    engines/nancy/action/actionmanager.cpp
    engines/nancy/action/actionrecord.h
    engines/nancy/action/conversation.cpp
    engines/nancy/action/secondarymovie.cpp
    engines/nancy/action/secondarymovie.h
    engines/nancy/state/scene.cpp
    engines/nancy/state/scene.h


diff --git a/engines/nancy/action/actionmanager.cpp b/engines/nancy/action/actionmanager.cpp
index 7cd07675549..8802874824f 100644
--- a/engines/nancy/action/actionmanager.cpp
+++ b/engines/nancy/action/actionmanager.cpp
@@ -568,10 +568,14 @@ void ActionManager::processDependency(DependencyRecord &dep, ActionRecord &recor
 }
 
 void ActionManager::clearActionRecords() {
-	for (auto &r : _records) {
-		delete r;
+	for (auto it = _records.begin(); it != _records.end(); ) {
+		if ((*it)->isPersistentAcrossScenes()) {
+			++it;
+			continue;
+		}
+		delete *it;
+		it = _records.erase(it);
 	}
-	_records.clear();
 	_activatedRecordsThisFrame.clear();
 	_previousRecordWasExecuted = false;
 }
diff --git a/engines/nancy/action/actionrecord.h b/engines/nancy/action/actionrecord.h
index 887fbc02afa..5b18d164f0e 100644
--- a/engines/nancy/action/actionrecord.h
+++ b/engines/nancy/action/actionrecord.h
@@ -123,6 +123,9 @@ public:
 	// Used for handling kCursorType dependency
 	virtual bool canHaveHotspot() const { return false; }
 
+	// Records returning true survive Scene::clearSceneData / ActionManager::clearActionRecords.
+	virtual bool isPersistentAcrossScenes() const { return false; }
+
 protected:
 	void finishExecution();
 
diff --git a/engines/nancy/action/conversation.cpp b/engines/nancy/action/conversation.cpp
index 4c4a4a609e8..1fc655a56e8 100644
--- a/engines/nancy/action/conversation.cpp
+++ b/engines/nancy/action/conversation.cpp
@@ -32,6 +32,7 @@
 #include "engines/nancy/resource.h"
 
 #include "engines/nancy/action/conversation.h"
+#include "engines/nancy/action/secondarymovie.h"
 
 #include "engines/nancy/state/scene.h"
 
@@ -383,6 +384,13 @@ void ConversationSound::execute() {
 						g_nancy->_sound->playSound(_responseGenericSound);
 					}
 
+					// Nancy 11+: play a fresh random sequence as the character's response anim.
+					if (PlaySecondaryMovie *active = NancySceneState.getActiveMovie()) {
+						if (active->_isRandom) {
+							active->playRandomSequence();
+						}
+					}
+
 					_state = kActionTrigger;
 				}
 			}
diff --git a/engines/nancy/action/secondarymovie.cpp b/engines/nancy/action/secondarymovie.cpp
index 9e450a4e1e8..15b9e5dfe0d 100644
--- a/engines/nancy/action/secondarymovie.cpp
+++ b/engines/nancy/action/secondarymovie.cpp
@@ -30,12 +30,28 @@
 
 #include "common/random.h"
 #include "common/serializer.h"
+#include "common/system.h"
 
 #include "video/bink_decoder.h"
 
 namespace Nancy {
 namespace Action {
 
+PlaySecondaryMovie::PlaySecondaryMovie(bool isRandom)
+		: RenderActionRecord(8), _isRandom(isRandom) {
+	if (_isRandom) {
+		NancySceneState.notifyRandomMovieARLoaded();
+	}
+}
+
+void PlaySecondaryMovie::resetDecoder() {
+	if (_videoType == kVideoPlaytypeAVF) {
+		_decoder.reset(new AVFDecoder());
+	} else {
+		_decoder.reset(new Video::BinkDecoder());
+	}
+}
+
 PlaySecondaryMovie::~PlaySecondaryMovie() {
 	if (NancySceneState.getActiveMovie() == this) {
 		NancySceneState.setActiveMovie(nullptr);
@@ -49,10 +65,10 @@ PlaySecondaryMovie::~PlaySecondaryMovie() {
 void PlaySecondaryMovie::readRandomSequence(Common::Serializer &ser, RandomSequence &seq) {
 	readFilename(ser, seq.name);
 	ser.syncAsUint16LE(seq.startFrame);
-	ser.syncAsUint16LE(seq.unknown_0x23);
+	ser.syncAsUint16LE(seq.lastFrame);
 	ser.syncAsSint32LE(seq.minPauseMs);
 	ser.syncAsSint32LE(seq.maxPauseMs);
-	ser.syncAsUint16LE(seq.unknown_0x2D);
+	ser.syncAsUint16LE(seq.stayWeight);
 
 	uint16 nextCount = 0;
 	ser.syncAsUint16LE(nextCount);
@@ -60,7 +76,7 @@ void PlaySecondaryMovie::readRandomSequence(Common::Serializer &ser, RandomSeque
 	seq.nextSequences.resize(nextCount);
 	for (uint i = 0; i < nextCount; ++i) {
 		readFilename(ser, seq.nextSequences[i].name);
-		ser.syncAsUint16LE(seq.nextSequences[i].unknown);
+		ser.syncAsUint16LE(seq.nextSequences[i].weight);
 	}
 }
 
@@ -103,8 +119,10 @@ void PlaySecondaryMovie::readRandomMovieData(Common::Serializer &ser, Common::Se
 
 		if (startIdx >= 0) {
 			const RandomSequence &src = _sequences[startIdx];
+			_activeSequenceIndex = startIdx;
 			_videoName = src.name;
 			_firstFrame = src.startFrame;
+			_lastFrame = src.lastFrame;
 			_videoType = kVideoPlaytypeBink;
 			_videoFormat = kLargeVideoFormat;
 			_videoSceneChange = kMovieNoSceneChange;
@@ -116,6 +134,102 @@ void PlaySecondaryMovie::readRandomMovieData(Common::Serializer &ser, Common::Se
 	_sound.name = "NO SOUND";
 }
 
+bool PlaySecondaryMovie::activateRandomSequence(int index) {
+	if (index < 0 || index >= (int)_sequences.size()) {
+		return false;
+	}
+
+	const RandomSequence &src = _sequences[index];
+	_activeSequenceIndex = index;
+	_videoName = src.name;
+	_firstFrame = src.startFrame;
+	_lastFrame = src.lastFrame;
+
+	// Reload the decoder with the new movie. The original engine
+	// auto-detects AVF vs Bink from disk; we honour the existing
+	// _videoType but fall back to the other if needed.
+	resetDecoder();
+
+	Common::Path withExt = _videoName.append(_videoType == kVideoPlaytypeAVF ? ".avf" : ".bik");
+	if (!_decoder->loadFile(withExt)) {
+		_videoType = _videoType == kVideoPlaytypeAVF ? kVideoPlaytypeBink : kVideoPlaytypeAVF;
+		resetDecoder();
+		withExt = _videoName.append(_videoType == kVideoPlaytypeAVF ? ".avf" : ".bik");
+		if (!_decoder->loadFile(withExt)) {
+			warning("PlayRandomMovie: couldn't load %s", _videoName.toString().c_str());
+			return false;
+		}
+	}
+
+	_isFinished = false;
+	_curViewportFrame = -1;	// force visibility re-evaluation next tick
+	return true;
+}
+
+void PlaySecondaryMovie::playRandomSequence() {
+	if (!_isRandom || _sequences.empty()) {
+		return;
+	}
+	int picked = g_nancy->_randomSource->getRandomNumber(_sequences.size() - 1);
+	_randomChainState = kRandomPlaying;
+	_randomStopRequested = false;
+	activateRandomSequence(picked);
+}
+
+int PlaySecondaryMovie::rollNextSequence() {
+	if (_activeSequenceIndex < 0 || _activeSequenceIndex >= (int)_sequences.size()) {
+		return -1;
+	}
+
+	const RandomSequence &seq = _sequences[_activeSequenceIndex];
+
+	uint32 totalWeight = seq.stayWeight;
+	for (const NextSequenceRef &ns : seq.nextSequences) {
+		totalWeight += ns.weight;
+	}
+
+	if (totalWeight == 0) {
+		// No weights at all: stay indefinitely without a pause.
+		_randomChainState = kRandomPaused;
+		_randomPauseEndTime = g_system->getMillis() + 1000;	// re-check in 1s
+		return -1;
+	}
+
+	uint32 roll = g_nancy->_randomSource->getRandomNumber(totalWeight - 1);
+
+	if (roll < seq.stayWeight) {
+		int32 pauseMs = seq.minPauseMs;
+		if (seq.maxPauseMs > seq.minPauseMs) {
+			pauseMs += g_nancy->_randomSource->getRandomNumber(seq.maxPauseMs - seq.minPauseMs - 1);
+		}
+		_randomPauseEndTime = g_system->getMillis() + (uint32)MAX<int32>(0, pauseMs);
+		_randomChainState = kRandomPaused;
+		setVisible(false);
+		if (_decoder) {
+			_decoder->pauseVideo(true);
+		}
+		return -1;
+	}
+
+	uint32 cumulative = seq.stayWeight;
+	for (uint i = 0; i < seq.nextSequences.size(); ++i) {
+		cumulative += seq.nextSequences[i].weight;
+		if (roll < cumulative) {
+			// Look up the named sequence in _sequences[].
+			for (uint j = 0; j < _sequences.size(); ++j) {
+				if (_sequences[j].name == seq.nextSequences[i].name) {
+					return (int)j;
+				}
+			}
+			warning("PlayRandomMovie: next-sequence \"%s\" not part of this AR",
+				seq.nextSequences[i].name.toString().c_str());
+			return -1;
+		}
+	}
+
+	return -1;
+}
+
 void PlaySecondaryMovie::readData(Common::SeekableReadStream &stream) {
 	Common::Serializer ser(&stream, nullptr);
 	ser.setVersion(g_nancy->getGameType());
@@ -187,10 +301,7 @@ void PlaySecondaryMovie::readData(Common::SeekableReadStream &stream) {
 
 void PlaySecondaryMovie::init() {
 	if (!_decoder) {
-		if (_videoType == kVideoPlaytypeAVF)
-			_decoder.reset(new AVFDecoder());
-		else
-			_decoder.reset(new Video::BinkDecoder());
+		resetDecoder();
 	}
 
 	if (!_decoder->isVideoLoaded()) {
@@ -220,10 +331,7 @@ void PlaySecondaryMovie::init() {
 
 void PlaySecondaryMovie::onPause(bool pause) {
 	if (!_decoder) {
-		if (_videoType == kVideoPlaytypeAVF)
-			_decoder.reset(new AVFDecoder());
-		else
-			_decoder.reset(new Video::BinkDecoder());
+		resetDecoder();
 	}
 
 	_decoder->pauseVideo(pause);
@@ -274,6 +382,26 @@ void PlaySecondaryMovie::execute() {
 
 		// fall through
 	case kRun: {
+		// Random-movie chain: while paused, wait for the pause to expire
+		// then re-roll. The roll itself may set up another pause, swap to
+		// the next sequence, or finish the AR if stop was requested.
+		if (_isRandom && _randomChainState == kRandomPaused) {
+			if (_randomStopRequested) {
+				_state = kActionTrigger;
+				break;
+			}
+			if (g_system->getMillis() < _randomPauseEndTime) {
+				break;
+			}
+			_randomChainState = kRandomPlaying;
+			int picked = rollNextSequence();
+			if (picked >= 0) {
+				activateRandomSequence(picked);
+			}
+			// If picked < 0, rollNextSequence() set up another pause.
+			break;
+		}
+
 		int newFrame = NancySceneState.getSceneInfo().frameID;
 
 		if (newFrame != _curViewportFrame) {
@@ -289,8 +417,9 @@ void PlaySecondaryMovie::execute() {
 			if (activeFrame != -1) {
 				_screenPosition = _videoDescs[activeFrame].destRect;
 				setVisible(true);
-			} else if (_isRandom && _videoDescs.empty()) {
-				// No hotspots: play full-viewport instead of gating on a frame match.
+			} else if (_isRandom) {
+				// Random movies aren't gated on hotspot/viewport-frame
+				// matches the way regular PSMs are: play full viewport.
 				_screenPosition = NancySceneState.getViewport().getBounds();
 				setVisible(true);
 			} else {
@@ -340,12 +469,25 @@ void PlaySecondaryMovie::execute() {
 			(_decoder->getCurFrame() == _firstFrame && _playDirection == kPlayMovieReverse) ||
 			_decoder->endOfVideo()) {
 
-			// Stop the video and block it from starting again, but also wait for
-			// sound to end before changing state
 			_decoder->pauseVideo(true);
 			_isFinished = true;
 
-			if (!g_nancy->_sound->isSoundPlaying(_sound)) {
+			if (_isRandom) {
+				// Sequence finished: roll for next. If stop was requested
+				// by a PlayRandomMovieControl, wind the AR down normally.
+				if (_randomStopRequested) {
+					_state = kActionTrigger;
+				} else {
+					int picked = rollNextSequence();
+					if (picked >= 0) {
+						activateRandomSequence(picked);
+					}
+					// Otherwise the chain entered the paused state; no
+					// state-trigger transition.
+				}
+			} else if (!g_nancy->_sound->isSoundPlaying(_sound)) {
+				// Stop the video and block it from starting again, but also wait for
+				// sound to end before changing state
 				g_nancy->_sound->stopSound(_sound);
 				_state = kActionTrigger;
 			}
@@ -381,10 +523,10 @@ void PlayRandomMovieControl::readData(Common::SeekableReadStream &stream) {
 }
 
 void PlayRandomMovieControl::execute() {
-	//PlaySecondaryMovie *target = NancySceneState.getActiveMovie();
-	//if (target && target->_isRandom) {
-	//	target->stopRandom();
-	//}
+	PlaySecondaryMovie *target = NancySceneState.getActiveMovie();
+	if (target && target->_isRandom) {
+		target->stopRandom();
+	}
 
 	_sceneChange.execute();
 	finishExecution();
diff --git a/engines/nancy/action/secondarymovie.h b/engines/nancy/action/secondarymovie.h
index 0c8f35ad3e7..a251bde1060 100644
--- a/engines/nancy/action/secondarymovie.h
+++ b/engines/nancy/action/secondarymovie.h
@@ -62,24 +62,28 @@ public:
 		FlagDescription flagDesc;
 	};
 
-	// Name of the next sequence to chain to once the current one finishes.
+	// Name of the next sequence to chain to once the current one finishes,
+	// plus its selection weight in the weighted random pick.
 	struct NextSequenceRef {
 		Common::Path name;
-		uint16 unknown = 0;
+		uint16 weight = 0;
 	};
 
 	// `name` is both the sequence id and the movie filename.
 	struct RandomSequence {
 		Common::Path name;
 		uint16 startFrame = 0;
-		uint16 unknown_0x23 = 0;
+		uint16 lastFrame = 0;
 		int32 minPauseMs = 0;
 		int32 maxPauseMs = 0;
-		uint16 unknown_0x2D = 0;
+		// Weight assigned to "stay on this sequence" in the weighted random
+		// pick. A roll inside [0, stayWeight) means "don't transition";
+		// instead pause for [minPauseMs, maxPauseMs] and re-roll.
+		uint16 stayWeight = 0;
 		Common::Array<NextSequenceRef> nextSequences;
 	};
 
-	PlaySecondaryMovie(bool isRandom = false) : RenderActionRecord(8), _isRandom(isRandom) {}
+	PlaySecondaryMovie(bool isRandom = false);
 	virtual ~PlaySecondaryMovie();
 
 	void init() override;
@@ -119,8 +123,27 @@ public:
 	uint16 _randomPlayerCursorAllowed = kPlayerCursorAllowed;
 	Common::Array<RandomSequence> _sequences;
 
+	// Chain state. After a sequence's movie finishes the engine rolls a
+	// weighted pick: "stay" -> enter pause for a random duration and
+	// re-roll; valid next-sequence -> swap to that sequence's movie.
+	enum RandomChainState { kRandomPlaying, kRandomPaused };
+	int _activeSequenceIndex = -1;
+	RandomChainState _randomChainState = kRandomPlaying;
+	uint32 _randomPauseEndTime = 0;
+	bool _randomStopRequested = false;
+
+	// Called by PlayRandomMovieControl::execute() to wind down the AR.
+	void stopRandom() { _randomStopRequested = true; }
+
+	// Pick & start a fresh random sequence. No-op when not a random AR.
+	void playRandomSequence();
+
 	bool isViewportRelative() const override { return true; }
 
+	bool isPersistentAcrossScenes() const override {
+		return _isRandom && !_isDone && !_randomStopRequested;
+	}
+
 protected:
 	Common::String getRecordTypeName() const override {
 		return _isRandom ? "PlayRandomMovie" : "PlaySecondaryMovie";
@@ -131,6 +154,18 @@ protected:
 	void readRandomMovieData(Common::Serializer &ser, Common::SeekableReadStream &stream);
 	void readRandomSequence(Common::Serializer &ser, RandomSequence &seq);
 
+	// (Re)create _decoder as an AVFDecoder or BinkDecoder matching _videoType.
+	void resetDecoder();
+
+	// Apply a RandomSequence's playback config to the PSM flat fields
+	// and reload the decoder. Returns true on success.
+	bool activateRandomSequence(int index);
+
+	// Pick the next sequence (or "stay") per the weighted random rules.
+	// Returns -1 if "stay" was picked (and sets up the pause state),
+	// or the chosen sequence index otherwise.
+	int rollNextSequence();
+
 	Graphics::ManagedSurface _fullFrame;
 	int _curViewportFrame = -1;
 	bool _isFinished = false;
diff --git a/engines/nancy/state/scene.cpp b/engines/nancy/state/scene.cpp
index 2c268d40fdd..503c202f03d 100644
--- a/engines/nancy/state/scene.cpp
+++ b/engines/nancy/state/scene.cpp
@@ -36,6 +36,8 @@
 #include "engines/nancy/state/scene.h"
 #include "engines/nancy/state/map.h"
 
+#include "engines/nancy/action/secondarymovie.h"
+
 #include "engines/nancy/ui/button.h"
 #include "engines/nancy/ui/ornaments.h"
 #include "engines/nancy/ui/clock.h"
@@ -1398,6 +1400,16 @@ void Scene::clearSceneData() {
 	}
 
 	clearLogicConditions();
+
+	// Stop a leftover random movie if the outgoing scene didn't include
+	// its own PSM(isRandom) AR (so it doesn't bleed into the next scene).
+	if (_activeMovie && _activeMovie->isPersistentAcrossScenes() && !_hadRandomMovieARThisScene) {
+		_activeMovie->stopRandom();
+	}
+	_hadRandomMovieARThisScene = false;
+
+	bool clearActiveMovie = _activeMovie && !_activeMovie->isPersistentAcrossScenes();
+
 	_actionManager.clearActionRecords();
 
 	if (_lightning) {
@@ -1413,7 +1425,10 @@ void Scene::clearSceneData() {
 	}
 
 	_activeConversation = nullptr;
-	_activeMovie = nullptr;
+
+	if (clearActiveMovie) {
+		_activeMovie = nullptr;
+	}
 }
 
 void Scene::clearPuzzleData() {
diff --git a/engines/nancy/state/scene.h b/engines/nancy/state/scene.h
index 8a685f55be8..049557d6f61 100644
--- a/engines/nancy/state/scene.h
+++ b/engines/nancy/state/scene.h
@@ -193,6 +193,9 @@ public:
 
 	void setActiveMovie(Action::PlaySecondaryMovie *activeMovie);
 	Action::PlaySecondaryMovie *getActiveMovie();
+
+	// Called when a PSM(isRandom) AR is loaded ??? drives stale-chain cleanup.
+	void notifyRandomMovieARLoaded() { _hadRandomMovieARThisScene = true; }
 	void setActiveConversation(Action::ConversationSound *activeConversation);
 	Action::ConversationSound *getActiveConversation();
 
@@ -320,6 +323,10 @@ private:
 	Action::PlaySecondaryMovie *_activeMovie;
 	Action::ConversationSound *_activeConversation;
 
+	// Set by notifyRandomMovieARLoaded; checked in clearSceneData to wind
+	// down a persistent random-movie whose scene chain is over.
+	bool _hadRandomMovieARThisScene = false;
+
 	// Contains a screenshot of the Scene state from the last time it was exited
 	Graphics::ManagedSurface _lastScreenshot;
 


Commit: 33273628ce509e43c8b2dd4d8a566e8f2bea4084
    https://github.com/scummvm/scummvm/commit/33273628ce509e43c8b2dd4d8a566e8f2bea4084
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-06-22T01:01:56+03:00

Commit Message:
NANCY: Remove leftover debug code in GridMapPuzzle

Changed paths:
    engines/nancy/action/puzzle/gridmappuzzle.cpp


diff --git a/engines/nancy/action/puzzle/gridmappuzzle.cpp b/engines/nancy/action/puzzle/gridmappuzzle.cpp
index 21d1ccbc0f4..bb1659be529 100644
--- a/engines/nancy/action/puzzle/gridmappuzzle.cpp
+++ b/engines/nancy/action/puzzle/gridmappuzzle.cpp
@@ -34,13 +34,6 @@ namespace Nancy {
 namespace Action {
 
 void GridMapPuzzle::readData(Common::SeekableReadStream &stream) {
-	uint pos = stream.pos();
-	Common::DumpFile d;
-	d.open("nancy10_gridmappuzzle.dat");
-	d.writeStream(&stream, stream.size());
-	d.close();
-	stream.seek(pos);
-
 	readFilename(stream, _boardImageName);
 	readFilename(stream, _cursorImageName);
 


Commit: 940a57fd1be1a9c335afedf097d3377d86428a5d
    https://github.com/scummvm/scummvm/commit/940a57fd1be1a9c335afedf097d3377d86428a5d
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-06-22T01:01:57+03:00

Commit Message:
NANCY: Implement TypingQuizPuzzle for Nancy11

In this puzzle, lettered balloons appear at fixed positions; the
player pops a balloon by typing its character before it floats away.
The puzzle runs for a fixed time limit; the score is a typing rate
(characters per minute).If the final rate meets the target, the win
scene fires; otherwise a default scene fires (with a different event
flag depending on whether a partial threshold was reached).

Unrelated to the text-entry QuizPuzzle (231).

Changed paths:
  A engines/nancy/action/puzzle/typingquizpuzzle.cpp
  A engines/nancy/action/puzzle/typingquizpuzzle.h
    engines/nancy/action/arfactory.cpp
    engines/nancy/module.mk


diff --git a/engines/nancy/action/arfactory.cpp b/engines/nancy/action/arfactory.cpp
index c0efd860108..506541bbbe7 100644
--- a/engines/nancy/action/arfactory.cpp
+++ b/engines/nancy/action/arfactory.cpp
@@ -74,6 +74,7 @@
 #include "engines/nancy/action/puzzle/towerpuzzle.h"
 #include "engines/nancy/action/puzzle/turningpuzzle.h"
 #include "engines/nancy/action/puzzle/twodialpuzzle.h"
+#include "engines/nancy/action/puzzle/typingquizpuzzle.h"
 #include "engines/nancy/action/puzzle/whalesurvivorpuzzle.h"
 
 #include "engines/nancy/state/scene.h"
@@ -502,11 +503,10 @@ ActionRecord *ActionManager::createActionRecord(uint16 type, Common::SeekableRea
 	case 244:
 		return new GridMapPuzzle();
 	// -- Nancy 11 and up --
-	case 245:	// Nancy11
-		warning("TypingQuizPuzzle");	// TODO
-		return nullptr;
-	case 246:	// Nancy11
-		warning("MatchPuzzle246");	// TODO
+	case 245:
+		return new TypingQuizPuzzle();
+	case 246:
+		warning("CardGamePuzzle");	// TODO
 		return nullptr;
 	default:
 		warning("Unknown action record type %d", type);
diff --git a/engines/nancy/action/puzzle/typingquizpuzzle.cpp b/engines/nancy/action/puzzle/typingquizpuzzle.cpp
new file mode 100644
index 00000000000..24a6c5f2d4c
--- /dev/null
+++ b/engines/nancy/action/puzzle/typingquizpuzzle.cpp
@@ -0,0 +1,464 @@
+/* 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/random.h"
+
+#include "engines/nancy/nancy.h"
+#include "engines/nancy/graphics.h"
+#include "engines/nancy/resource.h"
+#include "engines/nancy/sound.h"
+#include "engines/nancy/input.h"
+#include "engines/nancy/util.h"
+#include "engines/nancy/font.h"
+
+#include "engines/nancy/state/scene.h"
+#include "engines/nancy/action/puzzle/typingquizpuzzle.h"
+
+namespace Nancy {
+namespace Action {
+
+// Persists the previous typing rate so the adaptive target can scale across plays.
+static int s_lastScore = 0;
+
+static char toAsciiUpper(char c) {
+	return (c >= 'a' && c <= 'z') ? (char)(c - ('a' - 'A')) : c;
+}
+
+static bool caseFoldEquals(char a, char b) {
+	return toAsciiUpper(a) == toAsciiUpper(b);
+}
+
+void TypingQuizPuzzle::readData(Common::SeekableReadStream &stream) {
+	readFilename(stream, _imageName);          // 0x000
+
+	_numImageRects = stream.readUint16LE();    // 0x021
+	for (uint i = 0; i < kMaxImageRects; ++i)
+		readRect(stream, _balloonSrcRects[i]); // 0x023
+
+	readRect(stream, _poppedSrcRect);          // 0x163
+
+	_numPositions = stream.readUint16LE();     // 0x173
+	for (uint i = 0; i < kMaxBalloons; ++i)
+		readRect(stream, _positionRects[i]);   // 0x175
+
+	for (uint i = 0; i < kNumDigits; ++i)
+		readRect(stream, _scoreDigitRects[i]); // 0x2b5
+	for (uint i = 0; i < kNumDigits; ++i)
+		readRect(stream, _timerDigitRects[i]); // 0x355
+
+	stream.skip(16);                           // 0x3f5 timer bounding rect (unused)
+
+	_scoreDest.x = stream.readSint32LE();      // 0x405
+	_scoreDest.y = stream.readSint32LE();      // 0x409
+	_timerDest.x = stream.readSint32LE();      // 0x40d
+	_timerDest.y = stream.readSint32LE();      // 0x411
+	stream.skip(8);                            // 0x415 score/timer clip widths (unused)
+
+	readRect(stream, _passedMsgSrcRect);       // 0x41d
+	_passedMsgDuration = stream.readUint32LE();// 0x42d
+
+	_caseSensitive = stream.readByte() != 0;   // 0x431
+	_allowDigits   = stream.readByte() != 0;   // 0x432
+	_keyboardMode  = stream.readByte();        // 0x433
+	stream.read(_allowedChars, kAllowedCharsLen); // 0x434
+
+	_balloonLifeMin = stream.readUint16LE();   // 0x452
+	_balloonLifeMax = stream.readUint16LE();   // 0x454
+	_minBalloons    = stream.readUint16LE();   // 0x456
+	_maxBalloons    = stream.readUint16LE();   // 0x458
+	_timeLimit      = stream.readUint16LE();   // 0x45a
+	stream.skip(2);                            // 0x45c (unused)
+	_scoreThreshold     = stream.readUint16LE(); // 0x45e
+	_targetScore        = stream.readUint16LE(); // 0x460
+	_targetScorePercent = stream.readUint16LE(); // 0x462
+	_minTargetScore     = stream.readUint16LE(); // 0x464
+
+	_popSound.readNormal(stream);              // 0x466
+	_wrongSound.readNormal(stream);            // 0x497
+	_escapeSound.readNormal(stream);           // 0x4c8
+
+	_winScene.readData(stream);                // 0x4f9 (20 bytes)
+	_winScene.continueSceneSound = stream.readUint16LE(); // 0x50d
+	_winFlag = stream.readSint16LE();          // 0x50f
+
+	_winSound.readNormal(stream);              // 0x511
+
+	_defaultScene.readData(stream);            // 0x542 (20 bytes)
+	_defaultScene.continueSceneSound = stream.readUint16LE(); // 0x556
+	_flagThreshold = stream.readSint16LE();    // 0x558
+	_flagFail      = stream.readSint16LE();    // 0x55a
+
+	stream.skip(18);                           // 0x55c (trailing, unused)
+
+	if (_numImageRects > kMaxImageRects)
+		_numImageRects = kMaxImageRects;
+	if (_numPositions > (uint16)kMaxBalloons)
+		_numPositions = kMaxBalloons;
+}
+
+void TypingQuizPuzzle::init() {
+	Common::Rect vpBounds = NancySceneState.getViewport().getBounds();
+	_drawSurface.create(vpBounds.width(), vpBounds.height(),
+	                    g_nancy->_graphics->getInputPixelFormat());
+	_drawSurface.clear(g_nancy->_graphics->getTransColor());
+	setTransparent(true);
+	setVisible(true);
+	moveTo(vpBounds);
+
+	g_nancy->_resource->loadImage(_imageName, _image);
+	_image.setTransparentColor(g_nancy->_graphics->getTransColor());
+
+	if (_popSound.name != "NO SOUND")
+		g_nancy->_sound->loadSound(_popSound);
+	if (_wrongSound.name != "NO SOUND")
+		g_nancy->_sound->loadSound(_wrongSound);
+	if (_escapeSound.name != "NO SOUND")
+		g_nancy->_sound->loadSound(_escapeSound);
+
+	// Adaptive target: scale the threshold to the previous run's typing rate
+	if (_targetScorePercent < 1 || s_lastScore < 1) {
+		_effectiveTarget = _targetScore;
+	} else {
+		_effectiveTarget = (uint16)(((int)s_lastScore * (int)_targetScorePercent) / 100);
+		if (_effectiveTarget <= _minTargetScore)
+			_effectiveTarget = _minTargetScore;
+	}
+
+	_startTime  = g_nancy->getTotalPlayTime();
+	_gameState  = kPlaying;
+	_pops       = 0;
+	_score      = 0;
+	_reachedTarget    = false;
+	_reachedThreshold = false;
+
+	spawnInitialBalloons();
+	redraw();
+}
+
+bool TypingQuizPuzzle::isValidChar(byte c) const {
+	if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'))
+		return true;
+	if (_allowDigits && c >= '0' && c <= '9')
+		return true;
+	if (_allowedChars[0] != 0) {
+		for (uint i = 0; i < kAllowedCharsLen; ++i) {
+			if (c == _allowedChars[i])
+				return true;
+		}
+	}
+	return false;
+}
+
+char TypingQuizPuzzle::pickRandomChar() {
+	Common::RandomSource &rnd = *g_nancy->_randomSource;
+	int maxChar = (_keyboardMode != 0) ? 0x100 : (_allowedChars[0] != 0 ? 0x7f : 0x7b);
+
+	for (;;) {
+		int c;
+		if (_allowDigits)
+			c = (int)rnd.getRandomNumber(maxChar - 0x30 - 1) + 0x30;
+		else
+			c = (int)rnd.getRandomNumber(maxChar - 0x41 - 1) + 0x41;
+
+		if (isValidChar((byte)c))
+			return (char)c;
+	}
+}
+
+int TypingQuizPuzzle::pickFreePosition() {
+	Common::RandomSource &rnd = *g_nancy->_randomSource;
+
+	for (;;) {
+		int idx = (int)rnd.getRandomNumber(_numPositions - 1);
+		bool occupied = false;
+		for (uint i = 0; i < _numPositions; ++i) {
+			if (_balloons[i].state != kBalloonInactive && _balloons[i].posIndex == idx) {
+				occupied = true;
+				break;
+			}
+		}
+		if (!occupied)
+			return idx;
+	}
+}
+
+void TypingQuizPuzzle::spawnBalloon(uint slot) {
+	Common::RandomSource &rnd = *g_nancy->_randomSource;
+	Balloon &b = _balloons[slot];
+
+	b.posIndex = pickFreePosition();
+	b.posRect  = _positionRects[b.posIndex];
+	b.imgRect  = _balloonSrcRects[_numImageRects ? (int)rnd.getRandomNumber(_numImageRects - 1) : 0];
+	b.ch       = pickRandomChar();
+	b.spawnTime  = g_nancy->getTotalPlayTime();
+	b.popEndTime = 0;
+	b.lifetime = (uint16)((int)rnd.getRandomNumber(_balloonLifeMax - _balloonLifeMin) + _balloonLifeMin);
+	b.state    = kBalloonActive;
+}
+
+void TypingQuizPuzzle::spawnInitialBalloons() {
+	for (uint i = 0; i < kMaxBalloons; ++i)
+		_balloons[i].state = kBalloonInactive;
+
+	Common::RandomSource &rnd = *g_nancy->_randomSource;
+	_activeCount = (int)rnd.getRandomNumber(_maxBalloons - _minBalloons) + _minBalloons;
+	if (_activeCount > (int)_numPositions)
+		_activeCount = _numPositions;
+
+	for (int c = 0; c < _activeCount; ++c)
+		spawnBalloon(c);
+}
+
+void TypingQuizPuzzle::respawnBalloons(int maxToAdd) {
+	Common::RandomSource &rnd = *g_nancy->_randomSource;
+
+	int target;
+	if (_activeCount + maxToAdd < _minBalloons) {
+		target = _minBalloons;
+	} else {
+		int add = (int)rnd.getRandomNumber(maxToAdd);
+		while (_activeCount + add < _minBalloons || _activeCount + add > _maxBalloons)
+			add = (int)rnd.getRandomNumber(maxToAdd);
+		target = _activeCount + add;
+	}
+
+	int toSpawn = target - _activeCount;
+	_activeCount = target;
+
+	for (uint slot = 0; slot < _numPositions && toSpawn > 0; ++slot) {
+		if (_balloons[slot].state == kBalloonInactive) {
+			spawnBalloon(slot);
+			--toSpawn;
+		}
+	}
+}
+
+void TypingQuizPuzzle::expireBalloons(uint32 now) {
+	int expired = 0;
+
+	for (uint i = 0; i < _numPositions; ++i) {
+		Balloon &b = _balloons[i];
+
+		// Active balloons float away once their lifetime elapses
+		if (b.state == kBalloonActive && (now - b.spawnTime) / 1000 > b.lifetime) {
+			b.state = kBalloonInactive;
+			--_activeCount;
+			++expired;
+		}
+
+		// Popped balloons clear once their burst animation finishes
+		if (b.state == kBalloonPopped && now >= b.popEndTime) {
+			b.state = kBalloonInactive;
+			--_activeCount;
+			respawnBalloons(2);
+		}
+	}
+
+	if (expired > 0) {
+		if (_escapeSound.name != "NO SOUND")
+			g_nancy->_sound->playSound(_escapeSound);
+		respawnBalloons(2);
+	}
+}
+
+void TypingQuizPuzzle::processTyping(uint32 now) {
+	for (uint k = 0; k < _pendingChars.size(); ++k) {
+		char typed = _pendingChars[k];
+		bool matched = false;
+
+		for (uint i = 0; i < _numPositions; ++i) {
+			Balloon &b = _balloons[i];
+			if (b.state != kBalloonActive)
+				continue;
+
+			bool isMatch = _caseSensitive ? (b.ch == typed) : caseFoldEquals(b.ch, typed);
+			if (isMatch) {
+				b.state = kBalloonPopped;
+				b.popEndTime = now + 1000;
+				++_pops;
+				if (_popSound.name != "NO SOUND")
+					g_nancy->_sound->playSound(_popSound);
+				matched = true;
+				break;
+			}
+		}
+
+		if (!matched && _wrongSound.name != "NO SOUND")
+			g_nancy->_sound->playSound(_wrongSound);
+	}
+
+	_pendingChars.clear();
+}
+
+void TypingQuizPuzzle::updateScore(uint32 now) {
+	uint32 elapsedSec = (now - _startTime) / 1000;
+
+	// Score is a typing rate (characters per minute)
+	if (elapsedSec > 0 && _pops > 0)
+		_score = (int)((float)_pops * 60.0f / (float)elapsedSec);
+
+	if (elapsedSec >= _timeLimit)
+		_gameState = kEvaluate;
+}
+
+void TypingQuizPuzzle::drawNumber(int value, const Common::Rect *digitRects, int x, int y) {
+	if (value < 0)
+		value = 0;
+
+	// Render digits most-significant first using each digit sprite's own width
+	Common::String digits = Common::String::format("%d", value);
+	int drawX = x;
+	for (uint i = 0; i < digits.size(); ++i) {
+		int d = digits[i] - '0';
+		const Common::Rect &src = digitRects[d];
+		_drawSurface.blitFrom(_image, src, Common::Point(drawX, y));
+		drawX += src.width();
+	}
+}
+
+void TypingQuizPuzzle::redraw() {
+	_needsRedraw = true;
+	_drawSurface.clear(g_nancy->_graphics->getTransColor());
+
+	if (_gameState != kPlaying) {
+		// Result message, centred in the viewport
+		int w = _passedMsgSrcRect.width();
+		int h = _passedMsgSrcRect.height();
+		int x = (_drawSurface.w - w) / 2;
+		int y = (_drawSurface.h - h) / 2;
+		_drawSurface.blitFrom(_image, _passedMsgSrcRect, Common::Point(x, y));
+		return;
+	}
+
+	const Font *font = g_nancy->_graphics->getFont(_fontID);
+
+	// Balloons and their characters
+	for (uint i = 0; i < _numPositions; ++i) {
+		const Balloon &b = _balloons[i];
+
+		if (b.state == kBalloonActive) {
+			_drawSurface.blitFrom(_image, b.imgRect, Common::Point(b.posRect.left, b.posRect.top));
+
+			if (font) {
+				Common::String s(b.ch);
+				int cw = font->getStringWidth(s);
+				int ch = font->getFontHeight();
+				int cx = b.posRect.left + (b.posRect.width() - cw) / 2;
+				int cy = b.posRect.top + (b.posRect.height() - ch) / 2;
+				font->drawString(&_drawSurface, s, cx, cy, b.posRect.width(), 0);
+			}
+		} else if (b.state == kBalloonPopped) {
+			_drawSurface.blitFrom(_image, _poppedSrcRect, Common::Point(b.posRect.left, b.posRect.top));
+		}
+	}
+
+	// Score and remaining-time displays
+	drawNumber(_score, _scoreDigitRects, _scoreDest.x, _scoreDest.y);
+
+	uint32 elapsedSec = (g_nancy->getTotalPlayTime() - _startTime) / 1000;
+	int remaining = (int)_timeLimit - (int)elapsedSec;
+	drawNumber(remaining, _timerDigitRects, _timerDest.x, _timerDest.y);
+}
+
+void TypingQuizPuzzle::triggerSceneChange() {
+	if (_reachedTarget) {
+		if (_winScene.sceneID != 9999)
+			NancySceneState.changeScene(_winScene);
+		if (_winFlag != -1)
+			NancySceneState.setEventFlag(_winFlag, g_nancy->_true);
+	} else {
+		NancySceneState.changeScene(_defaultScene);
+		if (_reachedThreshold && _flagThreshold != -1)
+			NancySceneState.setEventFlag(_flagThreshold, g_nancy->_true);
+		else if (_flagFail != -1)
+			NancySceneState.setEventFlag(_flagFail, g_nancy->_true);
+	}
+}
+
+void TypingQuizPuzzle::execute() {
+	switch (_state) {
+	case kBegin:
+		init();
+		registerGraphics();
+		_state = kRun;
+		// fall through
+	case kRun: {
+		uint32 now = g_nancy->getTotalPlayTime();
+
+		switch (_gameState) {
+		case kPlaying:
+			processTyping(now);
+			expireBalloons(now);
+			updateScore(now);
+			redraw();
+			break;
+
+		case kEvaluate:
+			if (_score >= (int)_effectiveTarget) {
+				_reachedTarget = true;
+				if (_winSound.name != "NO SOUND") {
+					g_nancy->_sound->loadSound(_winSound);
+					g_nancy->_sound->playSound(_winSound);
+				}
+			} else {
+				_reachedTarget    = false;
+				_reachedThreshold = _score >= (int)_scoreThreshold;
+			}
+
+			_resultEndTime = now + _passedMsgDuration;
+			_gameState = kResult;
+			redraw();
+			break;
+
+		case kResult:
+			redraw();
+			if (now >= _resultEndTime) {
+				s_lastScore = _score;
+				_state = kActionTrigger;
+			}
+			break;
+		}
+		break;
+	}
+	case kActionTrigger:
+		g_nancy->_sound->stopSound(_popSound);
+		g_nancy->_sound->stopSound(_wrongSound);
+		g_nancy->_sound->stopSound(_escapeSound);
+		g_nancy->_sound->stopSound(_winSound);
+
+		triggerSceneChange();
+		finishExecution();
+		break;
+	}
+}
+
+void TypingQuizPuzzle::handleInput(NancyInput &input) {
+	if (_state != kRun || _gameState != kPlaying)
+		return;
+
+	for (auto &key : input.otherKbdInput) {
+		if (key.ascii != 0 && isValidChar((byte)key.ascii))
+			_pendingChars += (char)key.ascii;
+	}
+}
+
+} // End of namespace Action
+} // End of namespace Nancy
diff --git a/engines/nancy/action/puzzle/typingquizpuzzle.h b/engines/nancy/action/puzzle/typingquizpuzzle.h
new file mode 100644
index 00000000000..e76147fccc8
--- /dev/null
+++ b/engines/nancy/action/puzzle/typingquizpuzzle.h
@@ -0,0 +1,163 @@
+/* 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 NANCY_ACTION_TYPINGQUIZPUZZLE_H
+#define NANCY_ACTION_TYPINGQUIZPUZZLE_H
+
+#include "engines/nancy/action/actionrecord.h"
+#include "engines/nancy/commontypes.h"
+
+#include "graphics/managed_surface.h"
+
+namespace Nancy {
+namespace Action {
+
+// Real-time "typing quiz" arcade puzzle, new in Nancy 11 (Curse of Blackmoor Manor).
+// Lettered balloons appear at fixed positions; the player pops a balloon by typing its
+// character before it floats away. The puzzle runs for a fixed time limit; the score is
+// a typing rate (characters per minute). If the final rate meets the target, the win
+// scene fires; otherwise a default scene fires (with a different event flag depending on
+// whether a partial threshold was reached). Unrelated to the text-entry QuizPuzzle (231).
+class TypingQuizPuzzle : public RenderActionRecord {
+public:
+	TypingQuizPuzzle() : RenderActionRecord(7) {}
+	virtual ~TypingQuizPuzzle() {}
+
+	void init() override;
+
+	void readData(Common::SeekableReadStream &stream) override;
+	void execute() override;
+	void handleInput(NancyInput &input) override;
+
+	bool isViewportRelative() const override { return true; }
+
+protected:
+	Common::String getRecordTypeName() const override { return "TypingQuizPuzzle"; }
+
+private:
+	static const uint kMaxBalloons = 20;   // position/balloon slots
+	static const uint kMaxImageRects = 20; // balloon image variations
+	static const uint kNumDigits = 10;     // digit sprites for the score/timer displays
+	static const uint kAllowedCharsLen = 30;
+
+	enum BalloonState {
+		kBalloonInactive = 0,
+		kBalloonActive   = 1, // floating, can be popped
+		kBalloonPopped   = 2  // popped, showing the burst sprite until _popEndTime
+	};
+
+	struct Balloon {
+		BalloonState state = kBalloonInactive;
+		int posIndex = -1;     // index into _positionRects
+		Common::Rect imgRect;  // source rect in the puzzle image
+		Common::Rect posRect;  // viewport-relative destination
+		char ch = 0;
+		uint32 spawnTime = 0;
+		uint32 popEndTime = 0;
+		uint16 lifetime = 0;   // seconds before the balloon floats away
+	};
+
+	// ---- Helpers ----
+	void spawnInitialBalloons();
+	void respawnBalloons(int maxToAdd);
+	void spawnBalloon(uint slot);
+	int pickFreePosition();      // index into _positionRects not currently occupied
+	char pickRandomChar();
+	bool isValidChar(byte c) const;
+	void expireBalloons(uint32 now);
+	void processTyping(uint32 now);
+	void updateScore(uint32 now);
+	void drawNumber(int value, const Common::Rect *digitRects, int x, int y);
+	void redraw();
+	void triggerSceneChange();
+
+	// ---- File data ----
+	Common::Path _imageName;                       // 0x000 puzzle sprite sheet
+	uint16 _numImageRects = 0;                     // 0x021
+	Common::Rect _balloonSrcRects[kMaxImageRects]; // 0x023
+	Common::Rect _poppedSrcRect;                   // 0x163 burst sprite
+	uint16 _numPositions = 0;                      // 0x173
+	Common::Rect _positionRects[kMaxBalloons];     // 0x175 viewport-relative
+
+	Common::Rect _scoreDigitRects[kNumDigits];     // 0x2b5
+	Common::Rect _timerDigitRects[kNumDigits];     // 0x355
+	Common::Point _scoreDest;                      // 0x405 viewport-relative
+	Common::Point _timerDest;                      // 0x40d viewport-relative
+	Common::Rect _passedMsgSrcRect;                // 0x41d result message sprite
+	uint32 _passedMsgDuration = 0;                 // 0x42d ms
+
+	bool _caseSensitive = false;                   // 0x431 (0 = case-insensitive)
+	bool _allowDigits = false;                     // 0x432
+	byte _keyboardMode = 0;                         // 0x433
+	byte _allowedChars[kAllowedCharsLen] = {};     // 0x434
+
+	uint16 _balloonLifeMin = 0;                    // 0x452 seconds
+	uint16 _balloonLifeMax = 0;                    // 0x454
+	uint16 _minBalloons = 0;                       // 0x456
+	uint16 _maxBalloons = 0;                        // 0x458
+	uint16 _timeLimit = 0;                         // 0x45a seconds
+	uint16 _scoreThreshold = 0;                    // 0x45e partial-credit score
+	uint16 _targetScore = 0;                       // 0x460 unscaled target rate
+	uint16 _targetScorePercent = 0;                // 0x462 adaptive scaling percent
+	uint16 _minTargetScore = 0;                    // 0x464 floor for the scaled target
+
+	SoundDescription _popSound;                    // 0x466 balloon popped
+	SoundDescription _wrongSound;                  // 0x497 wrong key
+	SoundDescription _escapeSound;                 // 0x4c8 balloon floated away
+
+	SceneChangeDescription _winScene;              // 0x4f9 (9999 = none)
+	int16 _winFlag = -1;                           // 0x50f
+
+	SoundDescription _winSound;                    // 0x511 played when the target is met
+
+	SceneChangeDescription _defaultScene;          // 0x542 (did not reach the target)
+	int16 _flagThreshold = -1;                     // 0x558 set if the partial threshold was reached
+	int16 _flagFail = -1;                          // 0x55a set otherwise
+
+	// ---- Runtime state ----
+	Graphics::ManagedSurface _image;
+	uint16 _fontID = 0;	// game default font for the balloon characters
+
+	Balloon _balloons[kMaxBalloons];
+	int _activeCount = 0;
+
+	enum GameState {
+		kPlaying  = 0, // typing against the clock
+		kEvaluate = 1, // time is up: decide win/loss, play win sound
+		kResult   = 2  // show the result message, then change scene
+	};
+	GameState _gameState = kPlaying;
+
+	uint32 _startTime = 0;
+	int _pops = 0;          // number of balloons popped
+	int _score = 0;         // displayed typing rate (characters per minute)
+	uint16 _effectiveTarget = 0;
+	bool _reachedTarget = false;
+	bool _reachedThreshold = false;
+	uint32 _resultEndTime = 0;
+
+	Common::String _pendingChars; // typed characters awaiting a match, filled by handleInput
+};
+
+} // End of namespace Action
+} // End of namespace Nancy
+
+#endif // NANCY_ACTION_TYPINGQUIZPUZZLE_H
diff --git a/engines/nancy/module.mk b/engines/nancy/module.mk
index af38b27b11e..d148fc80388 100644
--- a/engines/nancy/module.mk
+++ b/engines/nancy/module.mk
@@ -57,6 +57,7 @@ MODULE_OBJS = \
   action/puzzle/towerpuzzle.o \
   action/puzzle/turningpuzzle.o \
   action/puzzle/twodialpuzzle.o \
+  action/puzzle/typingquizpuzzle.o \
   action/puzzle/whalesurvivorpuzzle.o \
   ui/fullscreenimage.o \
   ui/animatedbutton.o \


Commit: 10e9821f6b665faec5ae543d1dbcf4c6c333142b
    https://github.com/scummvm/scummvm/commit/10e9821f6b665faec5ae543d1dbcf4c6c333142b
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-06-22T01:01:58+03:00

Commit Message:
NANCY: Implement new MemoryPuzzle functionality for Nancy11+

Changed paths:
    engines/nancy/action/puzzle/memorypuzzle.cpp
    engines/nancy/action/puzzle/memorypuzzle.h


diff --git a/engines/nancy/action/puzzle/memorypuzzle.cpp b/engines/nancy/action/puzzle/memorypuzzle.cpp
index 65ba67ed2bf..41f478231b8 100644
--- a/engines/nancy/action/puzzle/memorypuzzle.cpp
+++ b/engines/nancy/action/puzzle/memorypuzzle.cpp
@@ -34,26 +34,12 @@
 namespace Nancy {
 namespace Action {
 
-// Binary layout (offsets from stream start, all LE):
-//   0x000  33  image filename
-//   0x021 576  36 x face src rect (int32 l/t/r/b x 4) [types 0..35]
-//   0x261  48  3 x tab indicator src rect (one per tab)
-//   0x291 384  24 x card screen rect (viewport-relative, int32 x 4)
-//   0x411  16  tab rect (screen rect where the active-tab graphic is drawn)
-//   0x421 144  3 x 3 x 16 tab hotspot rects [currentTab][targetTab]
-//   0x4b1   4  flipDelay (uint32, milliseconds)
-//   0x4b5   4  numPairs (uint32; clamped [4..36] in init)
-//   0x4b9   4  requiredPairs (uint32; clamped [2..36] in init)
-//   0x4bd   1  cursor flag (byte; skip in ScummVM)
-//   0x4be   1  shuffle flag (0 = pairs stay within tab, nonzero = global)
-//   0x4bf  49  match sound (played when a matching pair is found)
-//   0x4f0  49  card flip sound (played when flipping a card face-up)
-//   0x521  25  win SceneChangeWithFlag
-//   0x53a   1  unknown (skip)
-//   0x53b  49  win sound
-//   0x56c  16  exit hotspot rect
-//   [total: 0x57c = 1404 bytes]
 void MemoryPuzzle::readData(Common::SeekableReadStream &stream) {
+	if (g_nancy->getGameType() >= kGameTypeNancy11) {
+		readDataNancy11(stream);
+		return;
+	}
+
 	// 0x000: image filename (33 bytes)
 	readFilename(stream, _imageName);
 
@@ -104,13 +90,71 @@ void MemoryPuzzle::readData(Common::SeekableReadStream &stream) {
 	readRect(stream, _exitHotspot);
 }
 
+// Nancy 11 reworked the layout: fewer (12) face rects, a configurable grid/page count,
+// per-card-type voice clips in 27 fixed-size (0xb6) blocks, and two outcome scenes.
+void MemoryPuzzle::readDataNancy11(Common::SeekableReadStream &stream) {
+	readFilename(stream, _imageName); // 0x000
+
+	for (int i = 0; i < 12; ++i)            // 0x021 face src rects
+		readRect(stream, _faceSrcRects[i]);
+	for (int i = 0; i < kNumTabs; ++i)      // 0x0e1 page-tab indicator src rects
+		readRect(stream, _tabSrcRects[i]);
+	for (int i = 0; i < kCardsPerTab; ++i)  // 0x111 card position / back src rects
+		readRect(stream, _cardRects[i]);
+
+	readRect(stream, _tabRect);             // 0x291 tab indicator dest
+
+	for (int tab = 0; tab < kNumTabs; ++tab) // 0x2a1 tab hotspots
+		for (int slot = 0; slot < 3; ++slot)
+			readRect(stream, _tabHotspots[tab][slot]);
+
+	_flipDelay = stream.readUint32LE();     // 0x331
+	stream.skip(4);                         // 0x335 (second timing value, unused)
+	int32 requirePercent = stream.readSint32LE(); // 0x339 (-1 = use the fixed count below)
+	int32 requireCount   = stream.readSint32LE(); // 0x33d
+	stream.skip(4);                         // 0x341 (unused)
+
+	_shuffleGlobal = (stream.readByte() != 0); // 0x345
+	stream.skip(1);                         // 0x346 (flag, unused)
+
+	int32 pages      = stream.readSint32LE(); // 0x347
+	int32 gridsWide  = stream.readSint32LE(); // 0x34b
+	int32 gridsTall  = stream.readSint32LE(); // 0x34f
+	int32 srcWide    = stream.readSint32LE(); // 0x353
+	int32 srcTall    = stream.readSint32LE(); // 0x357
+	stream.skip(1);                         // 0x35b (tabs flag)
+
+	_numTabs     = CLIP<int>(pages, 1, kNumTabs);
+	_cardsPerTab = CLIP<int>(gridsWide * gridsTall, 1, kCardsPerTab);
+	_numTypes    = CLIP<int>(srcWide * srcTall, 1, kMaxTypes);
+	_numPairs    = _numTypes;
+	_requiredPairs = (requirePercent < 0) ? (uint32)requireCount
+	                                       : (uint32)(_numPairs * requirePercent / 100);
+
+	// 27 fixed 0xb6-byte voice-clip blocks: [0] is the card-flip sound, [17] starts the
+	// per-card match sounds (used here as a single match sound; per-type audio is a TODO).
+	_cardFlipSound.readNormal(stream);             // block 0 @ 0x35c
+	stream.skip(17 * 0xb6 - 0x31);                 // advance to block 17 @ 0xf72
+	_matchSound.readNormal(stream);                // block 17
+	stream.skip((27 - 17) * 0xb6 - 0x31);          // advance to the scenes @ 0x168e
+	_winSound.name = "NO SOUND";
+
+	// Solve scene (0x168e), then an alternate-outcome scene (0x16a8, unused). The event flags
+	// store a 16-bit value rather than a simple on/off.
+	_winScene._sceneChange.readData(stream);
+	_winScene._sceneChange.continueSceneSound = stream.readUint16LE();
+	_winScene._flag.label = stream.readSint16LE();
+	_winScene._flag.flag = stream.readSint16LE() ? g_nancy->_true : g_nancy->_false;
+	stream.skip(26); // alternate scene
+}
+
 // Shuffles type IDs (0..numPairs-1) into the 72-card array so that every type
 // appears exactly twice. numPairs is clamped to [4, 36]; cards beyond numPairs
 // remain typeId -1 (unassigned, unselectable). requiredPairs is clamped to [2, totalCards/2].
 void MemoryPuzzle::initCards() {
-	_numPairs = CLIP<uint32>(_numPairs, 4, (uint32)kMaxTypes);
+	_numPairs = CLIP<uint32>(_numPairs, 4, (uint32)_numTypes);
 
-	const int totalCards = kNumTabs * kCardsPerTab;
+	const int totalCards = _numTabs * _cardsPerTab;
 	const uint32 maxRequire = (uint32)(totalCards / 2);
 	_requiredPairs = CLIP<uint32>(_requiredPairs, 2, maxRequire);
 
@@ -129,9 +173,9 @@ void MemoryPuzzle::initCards() {
 
 	if (!_shuffleGlobal) {
 		// By-tab: pairs are always within the same tab.
-		for (int tab = 0; tab < kNumTabs; ++tab) {
-			int base = tab * kCardsPerTab;
-			for (int i = 0; i < kCardsPerTab; ++i) {
+		for (int tab = 0; tab < _numTabs; ++tab) {
+			int base = tab * _cardsPerTab;
+			for (int i = 0; i < _cardsPerTab; ++i) {
 				if (_cards[base + i].typeId != -1)
 					continue;
 				if (static_cast<uint32>(nextType) >= _numPairs)
@@ -142,7 +186,7 @@ void MemoryPuzzle::initCards() {
 				// Find a random unassigned slot in the same tab for the pair
 				int partner;
 				do {
-					partner = g_nancy->_randomSource->getRandomNumber(kCardsPerTab - 1);
+					partner = g_nancy->_randomSource->getRandomNumber(_cardsPerTab - 1);
 				} while (_cards[base + partner].typeId != -1);
 				_cards[base + partner].typeId = nextType;
 
@@ -256,7 +300,7 @@ void MemoryPuzzle::handleInput(NancyInput &input) {
 	}
 
 	// Tab switching: _tabHotspots[currentTab][slot] where slot is the target tab
-	for (int slot = 0; slot < kNumTabs; ++slot) {
+	for (int slot = 0; slot < _numTabs; ++slot) {
 		if (_tabHotspots[_currentTab][slot].contains(mouseVP)) {
 			g_nancy->_cursor->setCursorType(CursorManager::kHotspot);
 			if ((input.input & NancyInput::kLeftMouseButtonUp) && slot != _currentTab) {
@@ -281,8 +325,8 @@ void MemoryPuzzle::handleInput(NancyInput &input) {
 	if (_flipTimerActive)
 		return;
 
-	int base = _currentTab * kCardsPerTab;
-	for (int i = 0; i < kCardsPerTab; ++i) {
+	int base = _currentTab * _cardsPerTab;
+	for (int i = 0; i < _cardsPerTab; ++i) {
 		if (!_cardRects[i].contains(mouseVP))
 			continue;
 
@@ -356,21 +400,21 @@ void MemoryPuzzle::redrawCards() {
 
 	// Draw the active tab indicator over the corresponding tab button.
 	// The scene background shows inactive tab visuals; the overlay only marks the active one.
-	if (_currentTab < kNumTabs && !_tabSrcRects[_currentTab].isEmpty())
+	if (_currentTab < _numTabs && !_tabSrcRects[_currentTab].isEmpty())
 		_drawSurface.blitFrom(_image, _tabSrcRects[_currentTab],
 			Common::Point(_tabRect.left, _tabRect.top));
 
-	// Draw face-up and matched cards. Face-down cards are left transparent so
-	// the scene background (which carries the card-back visual) shows through.
-	int base = _currentTab * kCardsPerTab;
-	for (int i = 0; i < kCardsPerTab; ++i) {
+	// Draw face-up and matched cards. Face-down cards are left transparent so the scene
+	// background (which carries the card-back visual) shows through.
+	int base = _currentTab * _cardsPerTab;
+	for (int i = 0; i < _cardsPerTab; ++i) {
 		int idx = base + i;
 		const CardState &card = _cards[idx];
 		const Common::Rect &dest = _cardRects[i];
 
 		if (card.matchState != 0 || card.flipState != 0) {
 			int t = card.typeId;
-			if (t >= 0 && t < kMaxTypes && !_faceSrcRects[t].isEmpty())
+			if (t >= 0 && t < _numTypes && !_faceSrcRects[t].isEmpty())
 				_drawSurface.blitFrom(_image, _faceSrcRects[t],
 					Common::Point(dest.left, dest.top));
 		}
diff --git a/engines/nancy/action/puzzle/memorypuzzle.h b/engines/nancy/action/puzzle/memorypuzzle.h
index 5de2b030556..cd26e170b32 100644
--- a/engines/nancy/action/puzzle/memorypuzzle.h
+++ b/engines/nancy/action/puzzle/memorypuzzle.h
@@ -40,6 +40,7 @@ public:
 	void init() override;
 
 	void readData(Common::SeekableReadStream &stream) override;
+	void readDataNancy11(Common::SeekableReadStream &stream);
 	void execute() override;
 	void handleInput(NancyInput &input) override;
 
@@ -68,6 +69,11 @@ protected:
 	uint32 _requiredPairs = 12;    // pairs needed to win (clamped [2..36])
 	uint32 _flipDelay     = 1500;  // ms before non-matching cards flip back
 
+	// Counts are fixed in the Nancy 9 layout but configurable from Nancy 11 onwards
+	int _numTabs      = kNumTabs;
+	int _cardsPerTab  = kCardsPerTab;
+	int _numTypes     = kMaxTypes;
+
 	bool _shuffleGlobal = false;  // false = pairs stay within same tab; true = can cross tabs
 
 	SoundDescription _cardFlipSound; // played when a card is flipped face-up


Commit: 979db279fc019ebe7d38be626f0efd67de8a38b0
    https://github.com/scummvm/scummvm/commit/979db279fc019ebe7d38be626f0efd67de8a38b0
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-06-22T01:02:03+03:00

Commit Message:
NANCY: Implement new OrderingPuzzle functionality for Nancy11

This puzzle is used as follows:
- Piano variant: scenes 3394, 3395
- Keypad variant: scenes 3196
- Keypad terse variant: scenes 2702, 2705

Changed paths:
    engines/nancy/action/puzzle/orderingpuzzle.cpp
    engines/nancy/action/puzzle/orderingpuzzle.h


diff --git a/engines/nancy/action/puzzle/orderingpuzzle.cpp b/engines/nancy/action/puzzle/orderingpuzzle.cpp
index 8270180851b..eedd6ab2ab1 100644
--- a/engines/nancy/action/puzzle/orderingpuzzle.cpp
+++ b/engines/nancy/action/puzzle/orderingpuzzle.cpp
@@ -174,8 +174,20 @@ void OrderingPuzzle::readData(Common::SeekableReadStream &stream) {
 		readRectArray(ser, _overlaySrcs, numOverlays);
 		readRectArray(ser, _overlayDests, numOverlays);
 	} else if (isPiano && g_nancy->getGameType() >= kGameTypeNancy8) {
-		readFilenameArray(stream, _pianoSoundNames, numElements);
-		stream.skip((maxNumElements - numElements) * 33);
+		if (g_nancy->getGameType() >= kGameTypeNancy11) {
+			// Nancy 11 stores two interleaved sound names per key (e.g. a press and
+			// a release sound), padded to maxNumElements pairs
+			_pianoSoundNames.resize(numElements);
+			_pianoReleaseSoundNames.resize(numElements);
+			for (uint i = 0; i < numElements; ++i) {
+				readFilename(stream, _pianoSoundNames[i]);
+				readFilename(stream, _pianoReleaseSoundNames[i]);
+			}
+			stream.skip((maxNumElements - numElements) * 2 * 33);
+		} else {
+			readFilenameArray(stream, _pianoSoundNames, numElements);
+			stream.skip((maxNumElements - numElements) * 33);
+		}
 	}
 
 	if (ser.getVersion() > kGameTypeVampire) {
@@ -212,19 +224,61 @@ void OrderingPuzzle::readData(Common::SeekableReadStream &stream) {
 
 	if (isKeypad && g_nancy->getGameType() >= kGameTypeNancy7) {
 		if (_puzzleType == kKeypad) {
+			if (g_nancy->getGameType() >= kGameTypeNancy11) {
+				// Nancy 11 multi-stage keypad: a stage count, per-stage display rects, the codes
+				// for stages 1+, a final code matrix and an alternate scene, then the button rects.
+				_numStages = stream.readUint16LE();
+				stream.skip(20 * 16 + 1); // per-stage display rects + blink flag
+
+				_stageSequences.resize(4);
+				_stageCheckOrder.resize(4);
+				for (uint s = 0; s < 4; ++s) {
+					uint16 len = stream.readUint16LE();
+					_stageCheckOrder[s] = (stream.readByte() != 0);
+					len = MIN<uint16>(len, 30);
+					_stageSequences[s].resize(len);
+					for (uint16 k = 0; k < len; ++k)
+						_stageSequences[s][k] = stream.readByte();
+					stream.skip(30 - len);
+				}
+
+				stream.skip(25 + 25); // final code matrix + alternate scene (unused by the sequential model)
+			}
 			readRectArray(ser, _down1Rects, numElements, maxNumElements);
 			readRectArray(ser, _destRects, numElements, maxNumElements);
 		} else if (_puzzleType == kKeypadTerse) {
-			_down1Rects.resize(numElements);
-			_destRects.resize(numElements);
-
 			// Terse elements are the same size & placed on a grid (in the source image AND on screen)
 			uint16 columns = stream.readUint16LE();
-			stream.skip(2); // rows
+
+			// The Nancy 11 multi-stage terse keypad chains scenes: each scene holds one stage and
+			// the grid is preceded by the scene to advance to once this stage's code is entered.
+			// Standalone/final keypads omit it. A grid column count can never exceed maxNumElements,
+			// so a larger leading value is that scene id - it overrides the solve scene's target
+			// (the rest of the solve scene change is reused), then the real column count follows.
+			if (g_nancy->getGameType() >= kGameTypeNancy11 && columns > maxNumElements) {
+				_solveExitScene._sceneChange.sceneID = columns;
+				columns = stream.readUint16LE();
+			}
+
+			uint16 rows = stream.readUint16LE();
 
 			uint16 width = stream.readUint16LE();
 			uint16 height = stream.readUint16LE();
 
+			// Nancy 11 stores the element count as 0 and derives it from the grid dimensions
+			if (g_nancy->getGameType() >= kGameTypeNancy11) {
+				numElements = columns * rows;
+			}
+
+			// Guard against malformed dimensions (columns is a divisor below)
+			if (columns == 0) {
+				columns = 1;
+			}
+			numElements = MIN<uint16>(numElements, maxNumElements);
+
+			_down1Rects.resize(numElements);
+			_destRects.resize(numElements);
+
 			Common::Point srcStartPos, srcDist, destStartPos, destDist;
 
 			srcStartPos.x = stream.readUint16LE();
@@ -387,7 +441,36 @@ void OrderingPuzzle::execute() {
 				}
 			}
 
-			if (_puzzleType == kKeypad && _needButtonToCheckSuccess) {
+			if (_puzzleType == kKeypad && _numStages > 1) {
+				// Nancy 11 multi-stage keypad: every stage's code must be entered in turn.
+				// The check button confirms the current code; a wrong code resets the stage.
+				if (!_checkButtonPressed) {
+					return;
+				}
+
+				if (g_nancy->_sound->isSoundPlaying(_pushDownSound)) {
+					return;
+				}
+
+				_checkButtonPressed = false;
+
+				if (!solved) {
+					clearAllElements();
+					return;
+				}
+
+				if (_currentStage + 1 < (int)_numStages) {
+					++_currentStage;
+					_correctSequence = _stageSequences[_currentStage - 1];
+					_checkOrder = _stageCheckOrder[_currentStage - 1];
+					clearAllElements();
+					return;
+				}
+
+				// Final stage solved
+				NancySceneState.setEventFlag(_solveExitScene._flag);
+				_currentStage = 0;
+			} else if (_puzzleType == kKeypad && _needButtonToCheckSuccess) {
 				// KeypadPuzzle moves to the "success" scene regardless whether the puzzle was solved or not,
 				// provided the check button is pressed.
 				if (_checkButtonPressed) {
diff --git a/engines/nancy/action/puzzle/orderingpuzzle.h b/engines/nancy/action/puzzle/orderingpuzzle.h
index 3f9dbfa7f99..9d15e954b0d 100644
--- a/engines/nancy/action/puzzle/orderingpuzzle.h
+++ b/engines/nancy/action/puzzle/orderingpuzzle.h
@@ -73,12 +73,21 @@ protected:
 	Common::Array<Common::Rect> _hotspots;
 	Common::Array<uint16> _correctSequence;
 
+	// Nancy 11 multi-stage keypad: a series of codes that must be entered in turn. Stage 0 is
+	// _correctSequence above; the rest are stored here. The puzzle stays in one scene across all
+	// stages, so the same record instance tracks the current stage.
+	uint16 _numStages = 0;
+	Common::Array<Common::Array<uint16> > _stageSequences;
+	Common::Array<bool> _stageCheckOrder;
+	int _currentStage = 0;
+
 	uint16 _specialCursor1Id = CursorManager::kHotspot;
 	Common::Rect _specialCursor1Dest;
 	uint16 _specialCursor2Id = CursorManager::kHotspot;
 	Common::Rect _specialCursor2Dest;
 
 	Common::Array<Common::String> _pianoSoundNames; // nancy8 and up
+	Common::Array<Common::String> _pianoReleaseSoundNames; // nancy11 and up (second, interleaved, sound name per key)
 
 	uint16 _state2InvItem = 0;
 	Common::Array<Common::Rect> _overlaySrcs;


Commit: 6267ae7f621bee1411f85c5f6f9790e0f5ac3131
    https://github.com/scummvm/scummvm/commit/6267ae7f621bee1411f85c5f6f9790e0f5ac3131
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-06-22T01:02:04+03:00

Commit Message:
NANCY: Don't capture events in the text box when it's not visible

Avoids having mouse events eaten when they shouldn't be

Changed paths:
    engines/nancy/ui/textbox.cpp


diff --git a/engines/nancy/ui/textbox.cpp b/engines/nancy/ui/textbox.cpp
index a8248564fe3..270f3c0d3f9 100644
--- a/engines/nancy/ui/textbox.cpp
+++ b/engines/nancy/ui/textbox.cpp
@@ -196,6 +196,9 @@ void Textbox::setFullMode(bool open, uint32 timeoutMs) {
 }
 
 void Textbox::handleInput(NancyInput &input) {
+	if (!isVisible())
+		return;
+
 	if (_scrollbar)
 		_scrollbar->handleInput(input);
 


Commit: cf19cffcfabb4adea942118636fa811435684bd2
    https://github.com/scummvm/scummvm/commit/cf19cffcfabb4adea942118636fa811435684bd2
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-06-22T01:02:05+03:00

Commit Message:
NANCY: Remove an unneeded assignment in MemoryPuzzle

Changed paths:
    engines/nancy/action/puzzle/memorypuzzle.cpp


diff --git a/engines/nancy/action/puzzle/memorypuzzle.cpp b/engines/nancy/action/puzzle/memorypuzzle.cpp
index 41f478231b8..d70e39ac382 100644
--- a/engines/nancy/action/puzzle/memorypuzzle.cpp
+++ b/engines/nancy/action/puzzle/memorypuzzle.cpp
@@ -137,7 +137,7 @@ void MemoryPuzzle::readDataNancy11(Common::SeekableReadStream &stream) {
 	stream.skip(17 * 0xb6 - 0x31);                 // advance to block 17 @ 0xf72
 	_matchSound.readNormal(stream);                // block 17
 	stream.skip((27 - 17) * 0xb6 - 0x31);          // advance to the scenes @ 0x168e
-	_winSound.name = "NO SOUND";
+	// Nancy 11 has no win sound; _winSound keeps its default "NO SOUND".
 
 	// Solve scene (0x168e), then an alternate-outcome scene (0x16a8, unused). The event flags
 	// store a 16-bit value rather than a simple on/off.


Commit: 3f823089d8eccfb461dec5c73df92abe507c3ff5
    https://github.com/scummvm/scummvm/commit/3f823089d8eccfb461dec5c73df92abe507c3ff5
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-06-22T01:02:06+03:00

Commit Message:
NANCY: Implement new functionality for BulPuzzle in Nancy11

Changed paths:
    engines/nancy/action/puzzle/bulpuzzle.cpp
    engines/nancy/action/puzzle/bulpuzzle.h


diff --git a/engines/nancy/action/puzzle/bulpuzzle.cpp b/engines/nancy/action/puzzle/bulpuzzle.cpp
index 0ef5047be48..49f0ab17fa2 100644
--- a/engines/nancy/action/puzzle/bulpuzzle.cpp
+++ b/engines/nancy/action/puzzle/bulpuzzle.cpp
@@ -177,6 +177,10 @@ void BulPuzzle::updateGraphics() {
 					// This was the last move, go to next turn
 					g_nancy->_sound->playSound(_moveSound);
 					_currentAction = kNone;
+					// The voiced "move" line plays at the end of a player's turn (its second roll)
+					if (g_nancy->getGameType() >= kGameTypeNancy11 && (_turn == 1 || _turn == 3)) {
+						playVoiceLine(_turn / _numRolls, 3);
+					}
 					_turn = _turn + 1 > 3 ? 0 : _turn + 1;
 					_changeLight = true;
 				}
@@ -195,6 +199,10 @@ void BulPuzzle::updateGraphics() {
 			return;
 		case kPass:
 			_currentAction = kNone;
+			// The voiced "transition" line plays when a player passes their turn
+			if (g_nancy->getGameType() >= kGameTypeNancy11 && (_turn == 1 || _turn == 3)) {
+				playVoiceLine(_turn / _numRolls, 1);
+			}
 			_turn = (_turn + 1 > _numRolls * 2) ? 0 : _turn + 1;
 
 			return;
@@ -213,6 +221,11 @@ void BulPuzzle::readData(Common::SeekableReadStream &stream) {
 	_numCells = stream.readUint16LE();
 	_numPieces = stream.readUint16LE();
 	_numRolls = stream.readUint16LE();
+
+	if (g_nancy->getGameType() >= kGameTypeNancy11) {
+		stream.skip(2); // game mode + a flag byte
+	}
+
 	_playerStart = stream.readUint16LE();
 	_enemyStart = stream.readUint16LE();
 
@@ -256,23 +269,92 @@ void BulPuzzle::readData(Common::SeekableReadStream &stream) {
 	readRect(stream, _enemyLightSrc);
 	readRect(stream, _passButtonDisabledSrc);
 
-	_moveSound.readNormal(stream);
-	_enemyCapturedSound.readNormal(stream);
-	_playerCapturedSound.readNormal(stream);
-	_rollSound.readNormal(stream);
-	_passSound.readNormal(stream);
-	_resetSound.readNormal(stream);
+	if (g_nancy->getGameType() >= kGameTypeNancy11) {
+		// Nancy 11 keeps four fixed sounds, then per-event random voice-clip tables, then the scenes.
+		_moveSound.readNormal(stream);
+		_rollSound.readNormal(stream);
+		_passSound.readNormal(stream);
+		_resetSound.readNormal(stream);
+
+		// Captures are unused here and win/loss is voiced from the tables below, so the
+		// capture/solve/lose SoundDescriptions keep their default "NO SOUND".
+
+		// Two per-player blocks: a 6-byte header (channel + two unused shorts) then seven tables
+		static const int tableSizes[7] = { 1, 1, 4, 4, 4, 4, 4 };
+		for (int pl = 0; pl < 2; ++pl) {
+			_voiceChannel[pl] = stream.readUint16LE();
+			stream.skip(4);
+			for (int t = 0; t < 7; ++t) {
+				for (int e = 0; e < tableSizes[t]; ++e) {
+					Common::String name;
+					readFilename(stream, name);
+					if (!name.empty() && name != "NO SOUND")
+						_voiceLines[pl][t].push_back(name);
+				}
+			}
+		}
 
-	_solveScene.readData(stream);
-	_solveSoundDelay = stream.readUint16LE();
-	_solveSound.readNormal(stream);
+		// Loss scene (two possible flags) then the solve scene (one flag). The event flags
+		// store a value rather than a simple on/off.
+		_exitScene._sceneChange.readData(stream);
+		_exitScene._sceneChange.continueSceneSound = stream.readUint16LE();
+		_exitScene._flag.label = stream.readSint16LE();
+		_exitScene._flag.flag = stream.readByte();
+		_loseFlag2.label = stream.readSint16LE();
+		_loseFlag2.flag = stream.readByte();
+
+		_solveScene._sceneChange.readData(stream);
+		_solveScene._sceneChange.continueSceneSound = stream.readUint16LE();
+		_solveScene._flag.label = stream.readSint16LE();
+		_solveScene._flag.flag = stream.readByte();
+	} else {
+		_moveSound.readNormal(stream);
+		_enemyCapturedSound.readNormal(stream);
+		_playerCapturedSound.readNormal(stream);
+		_rollSound.readNormal(stream);
+		_passSound.readNormal(stream);
+		_resetSound.readNormal(stream);
+
+		_solveScene.readData(stream);
+		_solveSoundDelay = stream.readUint16LE();
+		_solveSound.readNormal(stream);
+
+		_exitScene.readData(stream);
+		_loseSoundDelay = stream.readUint16LE();
+		_loseSound.readNormal(stream);
+	}
 
-	_exitScene.readData(stream);
-	_loseSoundDelay = stream.readUint16LE();
-	_loseSound.readNormal(stream);
 	readRect(stream, _exitHotspot);
 }
 
+Common::String BulPuzzle::pickVoiceLine(int player, int table) {
+	if (player < 0 || player > 1 || table < 0 || table > 6) {
+		return Common::String();
+	}
+
+	const Common::Array<Common::String> &lines = _voiceLines[player][table];
+	if (lines.empty()) {
+		return Common::String();
+	}
+
+	return lines[g_nancy->_randomSource->getRandomNumber(lines.size() - 1)];
+}
+
+void BulPuzzle::playVoiceLine(int player, int table) {
+	Common::String line = pickVoiceLine(player, table);
+	if (line.empty()) {
+		return;
+	}
+
+	g_nancy->_sound->stopSound(_voiceSound);
+	_voiceSound.name = line;
+	_voiceSound.channelID = _voiceChannel[player];
+	_voiceSound.numLoops = 1;
+	_voiceSound.volume = _moveSound.volume;
+	g_nancy->_sound->loadSound(_voiceSound);
+	g_nancy->_sound->playSound(_voiceSound);
+}
+
 void BulPuzzle::execute() {
 	switch (_state) {
 	case kBegin:
@@ -284,26 +366,60 @@ void BulPuzzle::execute() {
 		g_nancy->_sound->loadSound(_passSound);
 		g_nancy->_sound->loadSound(_moveSound);
 
+		if (g_nancy->getGameType() >= kGameTypeNancy11) {
+			_prevSide = (_numRolls > 0 && _turn >= _numRolls) ? 1 : 0;
+			playVoiceLine(1, 0); // opponent's opening line
+		}
+
 		_state = kRun;
 		// fall through
-	case kRun:
+	case kRun: {
+		const bool isNancy11 = g_nancy->getGameType() >= kGameTypeNancy11;
+
+		// Voice the start of a turn when control changes hands
+		if (isNancy11 && _numRolls > 0) {
+			int side = (_turn >= _numRolls) ? 1 : 0;
+			if (side != _prevSide) {
+				_prevSide = side;
+				playVoiceLine(side, 2);
+			}
+		}
+
 		if (_playerPieces == 0) {
 			_state = kActionTrigger;
+			if (isNancy11) {
+				Common::String line = pickVoiceLine(1, 5); // opponent's victory line
+				if (!line.empty()) {
+					_loseSound.name = line;
+					_loseSound.channelID = _voiceChannel[1];
+					_loseSound.numLoops = 1;
+					_loseSound.volume = _moveSound.volume;
+				}
+			}
 			_nextMoveTime = g_nancy->getTotalPlayTime() + _loseSoundDelay * 1000;
 		}
 
 		if (_enemyPieces == 0) {
 			_playerWon = true;
 			_state = kActionTrigger;
+			if (isNancy11) {
+				Common::String line = pickVoiceLine(0, 5); // player's victory line
+				if (!line.empty()) {
+					_solveSound.name = line;
+					_solveSound.channelID = _voiceChannel[0];
+					_solveSound.numLoops = 1;
+					_solveSound.volume = _moveSound.volume;
+				}
+			}
 			_nextMoveTime = g_nancy->getTotalPlayTime() + _solveSoundDelay * 1000;
 		}
 
 		if (_state == kRun) {
 			break;
 		}
-
+	}
 		// fall through
-	case kActionTrigger:
+	case kActionTrigger: {
 		SoundDescription &sound = _playerWon ? _solveSound : _loseSound;
 
 		if (g_nancy->getTotalPlayTime() >= _nextMoveTime) {
@@ -315,6 +431,10 @@ void BulPuzzle::execute() {
 		if (_nextMoveTime == 0 && !g_nancy->_sound->isSoundPlaying(sound)) {
 			if (_playerWon) {
 				_solveScene.execute();
+			} else if (g_nancy->getGameType() >= kGameTypeNancy11) {
+				// The second flag marks giving up via the exit hotspot rather than being beaten
+				NancySceneState.changeScene(_exitScene._sceneChange);
+				NancySceneState.setEventFlag(_gaveUp ? _loseFlag2 : _exitScene._flag);
 			} else {
 				_exitScene.execute();
 			}
@@ -322,6 +442,7 @@ void BulPuzzle::execute() {
 
 		break;
 	}
+	}
 }
 
 void BulPuzzle::handleInput(NancyInput &input) {
@@ -331,6 +452,7 @@ void BulPuzzle::handleInput(NancyInput &input) {
 		if (input.input & NancyInput::kLeftMouseButtonUp) {
 			_state = kActionTrigger;
 			_nextMoveTime = 0;
+			_gaveUp = true;
 		}
 
 		return;
diff --git a/engines/nancy/action/puzzle/bulpuzzle.h b/engines/nancy/action/puzzle/bulpuzzle.h
index c6df9d9f143..22c08e1324d 100644
--- a/engines/nancy/action/puzzle/bulpuzzle.h
+++ b/engines/nancy/action/puzzle/bulpuzzle.h
@@ -48,6 +48,10 @@ protected:
 	void movePiece(bool player);
 	void reset(bool capture);
 
+	// Nancy 11: pick (and optionally play) a random clip from one of the per-player voice tables
+	Common::String pickVoiceLine(int player, int table);
+	void playVoiceLine(int player, int table);
+
 	Common::String getRecordTypeName() const override { return "BulPuzzle"; }
 
 	Common::Path _imageName;
@@ -110,8 +114,17 @@ protected:
 	SceneChangeWithFlag _exitScene; // also when losing
 	uint16 _loseSoundDelay = 0;
 	SoundDescription _loseSound;
+	FlagDescription _loseFlag2; // nancy11: event flag set when the player gives up rather than loses
 	Common::Rect _exitHotspot;
 
+	// Nancy 11 voice clips: two players, seven tables each (entry counts 1,1,4,4,4,4,4).
+	// Table 0 = opening line, 2 = turn line, 5 = end-of-game line.
+	Common::Array<Common::String> _voiceLines[2][7];
+	uint16 _voiceChannel[2] = {};
+	SoundDescription _voiceSound;
+	int _prevSide = -1;
+	bool _gaveUp = false;
+
 	Graphics::ManagedSurface _image;
 
 	int16 _playerPos = 0;


Commit: b41dc6fb3b2daeb1ba9e8fbf90d1e00150e94d04
    https://github.com/scummvm/scummvm/commit/b41dc6fb3b2daeb1ba9e8fbf90d1e00150e94d04
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-06-22T01:02:07+03:00

Commit Message:
NANCY: Add new functionality for CollisionPuzzle in Nancy11

Changed paths:
    engines/nancy/action/puzzle/collisionpuzzle.cpp
    engines/nancy/action/puzzle/collisionpuzzle.h


diff --git a/engines/nancy/action/puzzle/collisionpuzzle.cpp b/engines/nancy/action/puzzle/collisionpuzzle.cpp
index 60e73452dc6..27f78648859 100644
--- a/engines/nancy/action/puzzle/collisionpuzzle.cpp
+++ b/engines/nancy/action/puzzle/collisionpuzzle.cpp
@@ -187,9 +187,15 @@ void CollisionPuzzle::readData(Common::SeekableReadStream &stream) {
 	readFilename(stream, _imageName);
 	uint16 numPieces = 0;
 
-	// Nancy 10+ TileMove stores rows then cols (was width then height for square grids)
+	// Nancy 11 raised the Collision piece limit from 5 to 10 and added a leading flag
+	const bool collisionNancy11 = (_puzzleType == kCollision && g_nancy->getGameType() >= kGameTypeNancy11);
+	if (collisionNancy11) {
+		stream.skip(1); // pieces are randomized when this is 0
+	}
+
+	// Nancy 10+ TileMove (and Nancy 11+ Collision) store rows then cols (was width then height for square grids)
 	uint16 width, height;
-	if (_puzzleType == kTileMove && g_nancy->getGameType() >= kGameTypeNancy10) {
+	if ((_puzzleType == kTileMove && g_nancy->getGameType() >= kGameTypeNancy10) || collisionNancy11) {
 		height = stream.readUint16LE();
 		width = stream.readUint16LE();
 	} else {
@@ -217,16 +223,29 @@ void CollisionPuzzle::readData(Common::SeekableReadStream &stream) {
 	}
 	stream.skip((maxGridSize - height) * maxGridSize * 2);
 
+	// Earlier Collision puzzles store wall/block markers as 6-10, which Nancy 11 reuses for
+	// home IDs. Shift them up to the internal 16-20 range.
+	if (_puzzleType == kCollision && g_nancy->getGameType() < kGameTypeNancy11) {
+		for (uint y = 0; y < height; ++y) {
+			for (uint x = 0; x < width; ++x) {
+				if (_grid[y][x] >= 6 && _grid[y][x] <= 10)
+					_grid[y][x] += 10;
+			}
+		}
+	}
+
 	if (_puzzleType == kCollision) {
+		const uint maxPieces = collisionNancy11 ? 10 : 5;
+
 		_startLocations.resize(numPieces);
 		for (uint i = 0; i < numPieces; ++i) {
 			_startLocations[i].x = stream.readUint16LE();
 			_startLocations[i].y = stream.readUint16LE();
 		}
-		stream.skip((5 - numPieces) * 4);
+		stream.skip((maxPieces - numPieces) * 4);
 
-		readRectArray(stream, _pieceSrcs, numPieces, 5);
-		readRectArray(stream, _homeSrcs, numPieces, 5);
+		readRectArray(stream, _pieceSrcs, numPieces, maxPieces);
+		readRectArray(stream, _homeSrcs, numPieces, maxPieces);
 
 		readRect(stream, _verticalWallSrc);
 		readRect(stream, _horizontalWallSrc);
diff --git a/engines/nancy/action/puzzle/collisionpuzzle.h b/engines/nancy/action/puzzle/collisionpuzzle.h
index ab8c29638fe..de69f379c50 100644
--- a/engines/nancy/action/puzzle/collisionpuzzle.h
+++ b/engines/nancy/action/puzzle/collisionpuzzle.h
@@ -52,8 +52,9 @@ public:
 	bool isViewportRelative() const override { return true; }
 
 protected:
-	// numbers 1-5 are home IDs, 0 is empty cell
-	enum WallType { kWallLeft = 6, kWallUp = 7, kWallDown = 8, kWallRight = 9, kBlock = 10 };
+	// 0 is an empty cell, positive numbers are home IDs. Wall/block markers use Nancy 11's
+	// values (16-20); earlier games store 6-10 and are remapped to these on load.
+	enum WallType { kWallLeft = 16, kWallUp = 17, kWallDown = 18, kWallRight = 19, kBlock = 20 };
 
 	class Piece : public RenderObject {
 	public:


Commit: f6315c643fec56c4635a8d72a23ff816f1a9ae48
    https://github.com/scummvm/scummvm/commit/f6315c643fec56c4635a8d72a23ff816f1a9ae48
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-06-22T01:02:08+03:00

Commit Message:
NANCY: Implement the FadeSoundToSilence AR for Nancy11

Changed paths:
    engines/nancy/action/arfactory.cpp
    engines/nancy/action/soundrecords.cpp
    engines/nancy/action/soundrecords.h


diff --git a/engines/nancy/action/arfactory.cpp b/engines/nancy/action/arfactory.cpp
index 506541bbbe7..d8fd412d6ae 100644
--- a/engines/nancy/action/arfactory.cpp
+++ b/engines/nancy/action/arfactory.cpp
@@ -373,8 +373,7 @@ ActionRecord *ActionManager::createActionRecord(uint16 type, Common::SeekableRea
 	case 140:
 		return new SetVolume();
 	case 147:	// Nancy11
-		warning("FadeSoundToSilence");	// TODO
-		return nullptr;
+		return new FadeSoundToSilence();
 	case 148:
 		// MakeScreenFile - seems to save a cropped image of the screen in a bitmap file?
 		// TODO: Used in Nancy 9, sand castle puzzle
diff --git a/engines/nancy/action/soundrecords.cpp b/engines/nancy/action/soundrecords.cpp
index 43559183a8c..9a84bbbfe5d 100644
--- a/engines/nancy/action/soundrecords.cpp
+++ b/engines/nancy/action/soundrecords.cpp
@@ -21,6 +21,7 @@
 
 #include "common/random.h"
 #include "common/config-manager.h"
+#include "common/system.h"
 
 #include "engines/nancy/nancy.h"
 #include "engines/nancy/sound.h"
@@ -43,6 +44,36 @@ void SetVolume::execute() {
 	_isDone = true;
 }
 
+void FadeSoundToSilence::readData(Common::SeekableReadStream &stream) {
+	channel = stream.readUint16LE();
+	stream.skip(2); // pad / flag
+	fadeTimeMs = stream.readUint32LE();
+}
+
+void FadeSoundToSilence::execute() {
+	switch (_state) {
+	case kBegin:
+		_startVolume = g_nancy->_sound->getVolume(channel);
+		_startTime = g_system->getMillis();
+		_state = kRun;
+		break;
+	case kRun: {
+		const uint32 elapsed = g_system->getMillis() - _startTime;
+		if (fadeTimeMs == 0 || elapsed >= fadeTimeMs) {
+			g_nancy->_sound->setVolume(channel, 0);
+			_state = kActionTrigger;
+			break;
+		}
+		const uint16 v = (uint16)((uint32)_startVolume * (fadeTimeMs - elapsed) / fadeTimeMs);
+		g_nancy->_sound->setVolume(channel, v);
+		break;
+	}
+	case kActionTrigger:
+		finishExecution();
+		break;
+	}
+}
+
 void PlaySound::readData(Common::SeekableReadStream &stream) {
 	_sound.readDIGI(stream);
 
diff --git a/engines/nancy/action/soundrecords.h b/engines/nancy/action/soundrecords.h
index f1bf6703b23..02f5d34cc5a 100644
--- a/engines/nancy/action/soundrecords.h
+++ b/engines/nancy/action/soundrecords.h
@@ -40,6 +40,24 @@ protected:
 	Common::String getRecordTypeName() const override { return "SetVolume"; }
 };
 
+// Nancy 11+ AR 147. Linearly ramps a channel's volume down to 0 over
+// the given time, then stops execution.
+class FadeSoundToSilence : public ActionRecord {
+public:
+	void readData(Common::SeekableReadStream &stream) override;
+	void execute() override;
+
+	uint16 channel = 0;
+	uint32 fadeTimeMs = 0;
+
+protected:
+	Common::String getRecordTypeName() const override { return "FadeSoundToSilence"; }
+
+private:
+	uint32 _startTime = 0;
+	uint16 _startVolume = 0;
+};
+
 // Used for sound effects. From nancy3 up it includes 3D sound data, which lets
 // the sound move in 3D space as the player rotates/changes scenes. Also supports
 // changing the scene and/or setting a flag



From noreply at scummvm.org  Sun Jun 21 22:12:09 2026
From: noreply at scummvm.org (sev-)
Date: Sun, 21 Jun 2026 22:12:09 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 98c9b23e673706040b9c773fe73e5cfe9ee2dcdf
Message-ID: <20260621221209.2B1E57005CE@ron.scummvm.net>

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

Summary:
f48a7fa3dd DM: Fix logical bug checking for Fluxcage explosion type in group movement. PVS-Studio V547
82dac76c43 DM: Remove redundant upper bound check. PVS-Studio V560
6d0918292c DM: Use correct index to access champion in drawPanelFoodWaterPoisoned
818dfc7c95 DM: Use correct index to access door button derived bitmap
0b18f8b82c DM: Correct Bitmask in ProjExpl::projectileDelete().
83b27c3350 DM: Fix heap-buffer-overflow in processEventDoorAnimation
df38a47bd5 DM: Fix visual glitches on flying items
0dac5427cd DM: Correct _actionDefense array indexing to prevent out-of-bounds crash
874ff4a4f4 DM: Skip unused thing slots in Console::Cmd_gimme
39c1088693 DM: Add "nuke" command to debugger console
cd9e41d1d5 DM: Fix duplicate item references and crashes from gimme debug command
558e3b329e DM: Add bounds checking to prevent crashes from out-of-bounds object aspect reads
fa2245a23c DM: Change ActiveGroup::_directions type to uint16 to prevent undefined behavior
5b306e97c7 DM: Fix infinite loop freeze in dropCreatureFixedPossessions
98c9b23e67 DM: Fix crash when throwing projectiles by correcting cache index offsets


Commit: f48a7fa3ddce818f86bb67428d3a11607adafe60
    https://github.com/scummvm/scummvm/commit/f48a7fa3ddce818f86bb67428d3a11607adafe60
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-06-22T00:11:59+02:00

Commit Message:
DM: Fix logical bug checking for Fluxcage explosion type in group movement. PVS-Studio V547

Now matches the reversed source code.

Changed paths:
    engines/dm/group.cpp


diff --git a/engines/dm/group.cpp b/engines/dm/group.cpp
index d767950ddad..d83f96fcdf5 100644
--- a/engines/dm/group.cpp
+++ b/engines/dm/group.cpp
@@ -1126,8 +1126,8 @@ bool GroupMan::isMovementPossible(CreatureInfo *creatureInfo, int16 mapX, int16
 		Thing curThing = dungeon.getSquareFirstThing(mapX, mapY);
 		while (curThing != _vm->_thingEndOfList) {
 			if ((curThing).getType() == kDMThingTypeExplosion) {
-				Teleporter *curTeleporter = (Teleporter *)dungeon.getThingData(curThing);
-				if (((Explosion *)curTeleporter)->setType(kDMExplosionTypeFluxcage)) {
+				Explosion *explosion = (Explosion *)dungeon.getThingData(curThing);
+				if (explosion->getType() == kDMExplosionTypeFluxcage) {
 					_fluxCages[dir] = true;
 					_fluxCageCount++;
 					_groupMovBlockedByWallStairsPitFakeWalFluxCageTeleporter = true;


Commit: 82dac76c43d93f9fc741716443df479486462eb4
    https://github.com/scummvm/scummvm/commit/82dac76c43d93f9fc741716443df479486462eb4
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-06-22T00:11:59+02:00

Commit Message:
DM: Remove redundant upper bound check. PVS-Studio V560

Changed paths:
    engines/dm/group.cpp


diff --git a/engines/dm/group.cpp b/engines/dm/group.cpp
index d83f96fcdf5..4558c4514be 100644
--- a/engines/dm/group.cpp
+++ b/engines/dm/group.cpp
@@ -754,7 +754,7 @@ T0209005_AddEventAndReturn:
 		}
 		if (AL0447_i_Behavior == kDMBehaviorAttack) {
 			AL0446_i_CreatureAspectIndex = eventType - kDMEventTypeUpdateAspectCreature0; /* Value -1 for event 32, meaning aspect will be updated for all creatures in the group */
-			bool isAttacking = (AL0446_i_CreatureAspectIndex >= 0 && AL0446_i_CreatureAspectIndex < 4) ? getFlag(activeGroup->_aspect[AL0446_i_CreatureAspectIndex], kDMAspectMaskActiveGroupIsAttacking) : false;
+			bool isAttacking = (AL0446_i_CreatureAspectIndex >= 0) ? getFlag(activeGroup->_aspect[AL0446_i_CreatureAspectIndex], kDMAspectMaskActiveGroupIsAttacking) : false;
 			nextAspectUpdateTime = getCreatureAspectUpdateTime(activeGroup, AL0446_i_CreatureAspectIndex, isAttacking);
 			goto T0209136;
 		}


Commit: 6d0918292c4b1150ea84aa13dc4c39a199d5d106
    https://github.com/scummvm/scummvm/commit/6d0918292c4b1150ea84aa13dc4c39a199d5d106
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-06-22T00:11:59+02:00

Commit Message:
DM: Use correct index to access champion in drawPanelFoodWaterPoisoned

Fix heap-buffer-overflow in drawPanelFoodWaterPoisoned by converting ordinal to index. It occured when you click on 4th party member.

Changed paths:
    engines/dm/inventory.cpp


diff --git a/engines/dm/inventory.cpp b/engines/dm/inventory.cpp
index 47fec4a496a..fbf49a21b7d 100644
--- a/engines/dm/inventory.cpp
+++ b/engines/dm/inventory.cpp
@@ -207,7 +207,7 @@ void InventoryMan::drawPanelFoodWaterPoisoned() {
 	static Box boxWater(112, 159, 83, 91); // @ G0036_s_Graphic562_Box_Water
 	static Box boxPoisoned(112, 207, 105, 119); // @ G0037_s_Graphic562_Box_Poisoned
 
-	Champion &champ = _vm->_championMan->_champions[_inventoryChampionOrdinal];
+	Champion &champ = _vm->_championMan->_champions[_vm->ordinalToIndex(_inventoryChampionOrdinal)];
 	closeChest();
 	DisplayMan &dispMan = *_vm->_displayMan;
 


Commit: 818dfc7c95b678c0c3371c3757a2314acaaadd14
    https://github.com/scummvm/scummvm/commit/818dfc7c95b678c0c3371c3757a2314acaaadd14
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-06-22T00:11:59+02:00

Commit Message:
DM: Use correct index to access door button derived bitmap

Fixes a crash when drawing a door button. Matches with reversed source code.

Changed paths:
    engines/dm/gfx.cpp


diff --git a/engines/dm/gfx.cpp b/engines/dm/gfx.cpp
index a74c0146eed..426d0db39aa 100644
--- a/engines/dm/gfx.cpp
+++ b/engines/dm/gfx.cpp
@@ -948,7 +948,7 @@ void DisplayMan::drawDoorButton(int16 doorButtonOrdinal, DoorButton doorButton)
 			dungeon._dungeonViewClickableBoxes[kDMViewCellDoorButtonOrWallOrn]._rect.top = coordSetRedEagle[2];
 			dungeon._dungeonViewClickableBoxes[kDMViewCellDoorButtonOrWallOrn]._rect.bottom = coordSetRedEagle[3];
 		} else {
-			doorButtonOrdinal = kDMDerivedBitmapFirstDoorButton + (doorButtonOrdinal * 2) + ((doorButton != kDMDoorButtonD3R) ? 0 : (int16)doorButton - 1);
+			doorButtonOrdinal = kDMDerivedBitmapFirstDoorButton + (doorButtonOrdinal * 2) + ((!doorButton) ? 0 : doorButton - 1);
 			if (!isDerivedBitmapInCache(doorButtonOrdinal)) {
 				uint16 *coordSetBlueGoat = _doorButtonCoordSets[coordSet][kDMDoorButtonD1C];
 				byte *bitmapNative = getNativeBitmapOrGraphic(nativeBitmapIndex);


Commit: 0b18f8b82cec88597e0fc740433cdc1a5deaced7
    https://github.com/scummvm/scummvm/commit/0b18f8b82cec88597e0fc740433cdc1a5deaced7
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-06-22T00:11:59+02:00

Commit Message:
DM: Correct Bitmask in ProjExpl::projectileDelete().

Matches original.

Changed paths:
    engines/dm/group.cpp
    engines/dm/projexpl.cpp


diff --git a/engines/dm/group.cpp b/engines/dm/group.cpp
index 4558c4514be..c6180060ba6 100644
--- a/engines/dm/group.cpp
+++ b/engines/dm/group.cpp
@@ -539,7 +539,7 @@ uint16 GroupMan::getGroupValueUpdatedWithCreatureValue(uint16 groupVal, uint16 c
 	creatureVal &= 0x0003;
 	creatureIndex <<= 1;
 	creatureVal <<= creatureIndex;
-	return creatureVal | (groupVal & ~(3 << creatureVal));
+	return creatureVal | (groupVal & ~(3 << creatureIndex));
 }
 
 int16 GroupMan::getDamageAllCreaturesOutcome(Group *group, int16 mapX, int16 mapY, int16 attack, bool notMoving) {
diff --git a/engines/dm/projexpl.cpp b/engines/dm/projexpl.cpp
index b523d446715..ba60479d8c7 100644
--- a/engines/dm/projexpl.cpp
+++ b/engines/dm/projexpl.cpp
@@ -385,7 +385,7 @@ void ProjExpl::projectileDelete(Thing projectileThing, Thing *groupSlot, int16 m
 			} else
 				_vm->_dungeonMan->linkThingToList(projectileSlotThing, previousThing, kDMMapXNotOnASquare, 0);
 		} else
-			_vm->_moveSens->getMoveResult(Thing((projectileSlotThing).getTypeAndIndex() | getFlag(projectileThing.toUint16(), 0xC)), -2, 0, mapX, mapY);
+			_vm->_moveSens->getMoveResult(Thing((projectileSlotThing).getTypeAndIndex() | getFlag(projectileThing.toUint16(), 0xC000)), -2, 0, mapX, mapY);
 	}
 	projectile->_nextThing = _vm->_thingNone;
 }


Commit: 83b27c3350f8582f4dcb1b4d09eb4366d9798643
    https://github.com/scummvm/scummvm/commit/83b27c3350f8582f4dcb1b4d09eb4366d9798643
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-06-22T00:11:59+02:00

Commit Message:
DM: Fix heap-buffer-overflow in processEventDoorAnimation

Stop unconditional querying the creature's attributes before checking if a creature group actually existed on that square.

Changed paths:
    engines/dm/timeline.cpp


diff --git a/engines/dm/timeline.cpp b/engines/dm/timeline.cpp
index 16757915538..7ccc089b813 100644
--- a/engines/dm/timeline.cpp
+++ b/engines/dm/timeline.cpp
@@ -411,8 +411,8 @@ void Timeline::processEventDoorAnimation(TimelineEvent *event) {
 			return;
 		}
 		Thing groupThing = _vm->_groupMan->groupGetThing(mapX, mapY);
-		uint16 creatureAttributes = _vm->_dungeonMan->getCreatureAttributes(groupThing);
-		if ((groupThing != _vm->_thingEndOfList) && !getFlag(creatureAttributes, kDMCreatureMaskNonMaterial)) {
+		uint16 creatureAttributes;
+		if ((groupThing != _vm->_thingEndOfList) && !getFlag((creatureAttributes = _vm->_dungeonMan->getCreatureAttributes(groupThing)), kDMCreatureMaskNonMaterial)) {
 			if (doorState >= (verticalDoorFl ? CreatureInfo::getHeight(creatureAttributes) : 1)) { /* Creature height or 1 */
 				if (_vm->_groupMan->getDamageAllCreaturesOutcome((Group *)_vm->_dungeonMan->getThingData(groupThing), mapX, mapY, 5, true) != kDMKillOutcomeAllCreaturesInGroup)
 					_vm->_groupMan->processEvents29to41(mapX, mapY, kDMEventTypeCreateReactionDangerOnSquare, 0);


Commit: df38a47bd5ab55e06b8ad675d8a1fe496713652f
    https://github.com/scummvm/scummvm/commit/df38a47bd5ab55e06b8ad675d8a1fe496713652f
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-06-22T00:11:59+02:00

Commit Message:
DM: Fix visual glitches on flying items

Use the correct 8-byte aligned destination stride when writing to shrunk bitmaps in blitToBitmapShrinkWithPalChange.

Changed paths:
    engines/dm/gfx.cpp


diff --git a/engines/dm/gfx.cpp b/engines/dm/gfx.cpp
index 426d0db39aa..01e350fffe3 100644
--- a/engines/dm/gfx.cpp
+++ b/engines/dm/gfx.cpp
@@ -2870,11 +2870,12 @@ void DisplayMan::blitToBitmapShrinkWithPalChange(byte *srcBitmap, byte *destBitm
 
 	uint32 scaleX = (kScaleThreshold * srcPixelWidth) / destPixelWidth;
 	uint32 scaleY = (kScaleThreshold * srcHeight) / destHeight;
+	uint32 destStride = getNormalizedByteWidth(destPixelWidth / 2) * 2;
 
 	// Loop through drawing output lines
 	for (uint32 destY = 0, scaleYCtr = 0; destY < (uint32)destHeight; ++destY, scaleYCtr += scaleY) {
 		const byte *srcLine = &srcBitmap[(scaleYCtr / kScaleThreshold) * srcPixelWidth];
-		byte *destLine = &destBitmap[destY * destPixelWidth];
+		byte *destLine = &destBitmap[destY * destStride];
 
 		// Loop through drawing the pixels of the row
 		for (uint32 destX = 0, xCtr = 0, scaleXCtr = 0; destX < (uint32)destPixelWidth; ++destX, ++xCtr, scaleXCtr += scaleX)


Commit: 0dac5427cd988375d3ed8cda92e158e1c00d86ec
    https://github.com/scummvm/scummvm/commit/0dac5427cd988375d3ed8cda92e158e1c00d86ec
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-06-22T00:11:59+02:00

Commit Message:
DM: Correct _actionDefense array indexing to prevent out-of-bounds crash

Changed paths:
    engines/dm/timeline.cpp


diff --git a/engines/dm/timeline.cpp b/engines/dm/timeline.cpp
index 7ccc089b813..4aae9c1317b 100644
--- a/engines/dm/timeline.cpp
+++ b/engines/dm/timeline.cpp
@@ -835,7 +835,7 @@ void Timeline::processEventEnableChampionAction(uint16 champIndex) {
 	curChampion->_enableActionEventIndex = -1;
 	clearFlag(curChampion->_attributes, kDMAttributeDisableAction);
 	if (curChampion->_actionIndex != kDMActionNone) {
-		curChampion->_actionDefense -= _actionDefense[curChampion->_actionDefense];
+		curChampion->_actionDefense -= _actionDefense[curChampion->_actionIndex];
 	}
 	if (curChampion->_currHealth) {
 		if ((curChampion->_actionIndex == kDMActionShoot) && (curChampion->_slots[kDMSlotReadyHand] == _vm->_thingNone)) {


Commit: 874ff4a4f415ba608e267a92d05bc465e9ceea78
    https://github.com/scummvm/scummvm/commit/874ff4a4f415ba608e267a92d05bc465e9ceea78
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-06-22T00:11:59+02:00

Commit Message:
DM: Skip unused thing slots in Console::Cmd_gimme

Prevent out of bounds array lookup in DungeonMan::getWeaponInfo and ObjectMan::getObjectType.

Changed paths:
    engines/dm/console.cpp


diff --git a/engines/dm/console.cpp b/engines/dm/console.cpp
index 7372da0bb18..97ff814eede 100644
--- a/engines/dm/console.cpp
+++ b/engines/dm/console.cpp
@@ -263,6 +263,9 @@ bool Console::Cmd_gimme(int argc, const char** argv) {
 		dummyThing.setType(thingType);
 		for (int16 thingIndex = 0; thingIndex < thingCount; ++thingIndex) {
 			dummyThing.setIndex(thingIndex);
+			uint16 *rawType = _vm->_dungeonMan->getThingData(dummyThing);
+			if (rawType[0] == _vm->_thingNone.toUint16())
+				continue;
 			int16 iconIndex = _vm->_objectMan->getIconIndex(dummyThing);
 			if (iconIndex >= 0 && iconIndex < kDMObjectNameCount) {
 				const char *displayName = _vm->_objectMan->_objectNames[iconIndex];


Commit: 39c10886931b77f6769d43606e662b81eba023b8
    https://github.com/scummvm/scummvm/commit/39c10886931b77f6769d43606e662b81eba023b8
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-06-22T00:11:59+02:00

Commit Message:
DM: Add "nuke" command to debugger console

Kill/Delete the creature group in front of you.

Changed paths:
    engines/dm/console.cpp
    engines/dm/console.h


diff --git a/engines/dm/console.cpp b/engines/dm/console.cpp
index 97ff814eede..eeb6d51c604 100644
--- a/engines/dm/console.cpp
+++ b/engines/dm/console.cpp
@@ -30,6 +30,7 @@
 #include "dm/dungeonman.h"
 #include "dm/movesens.h"
 #include "dm/objectman.h"
+#include "dm/group.h"
 
 
 namespace DM {
@@ -67,6 +68,7 @@ Console::Console(DM::DMEngine* vm) : _vm(vm) {
 	registerCmd("map", WRAP_METHOD(Console, Cmd_map));
 	registerCmd("listItems", WRAP_METHOD(Console, Cmd_listItems));
 	registerCmd("gimme", WRAP_METHOD(Console, Cmd_gimme));
+	registerCmd("nuke", WRAP_METHOD(Console, Cmd_nuke));
 }
 
 bool Console::Cmd_godmode(int argc, const char** argv) {
@@ -289,4 +291,20 @@ bool Console::Cmd_gimme(int argc, const char** argv) {
 	return true;
 }
 
+bool Console::Cmd_nuke(int argc, const char** argv) {
+	DungeonMan &dm = *_vm->_dungeonMan;
+	int16 mapX = dm._partyMapX;
+	int16 mapY = dm._partyMapY;
+	dm.mapCoordsAfterRelMovement(dm._partyDir, 1, 0, mapX, mapY);
+
+	Thing groupThing = _vm->_groupMan->groupGetThing(mapX, mapY);
+	if (groupThing != _vm->_thingNone && groupThing != _vm->_thingEndOfList) {
+		_vm->_groupMan->groupDelete(mapX, mapY);
+		debugPrintf("Creatures in front have been nuked!\n");
+	} else {
+		debugPrintf("No creatures found in front of you.\n");
+	}
+	return true;
+}
+
 }
diff --git a/engines/dm/console.h b/engines/dm/console.h
index 0111e774ae0..6b2d21afb4b 100644
--- a/engines/dm/console.h
+++ b/engines/dm/console.h
@@ -43,6 +43,7 @@ private:
 	bool Cmd_map(int argc, const char **argv);
 	bool Cmd_listItems(int argc, const char **argv);
 	bool Cmd_gimme(int argc, const char **argv);
+	bool Cmd_nuke(int argc, const char **argv);
 
 	const char *debugGetDirectionName(int16 dir);
 


Commit: cd9e41d1d5c4533069ca1a2d54beb95afb2cde30
    https://github.com/scummvm/scummvm/commit/cd9e41d1d5c4533069ca1a2d54beb95afb2cde30
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-06-22T00:11:59+02:00

Commit Message:
DM: Fix duplicate item references and crashes from gimme debug command

Changed paths:
    engines/dm/console.cpp


diff --git a/engines/dm/console.cpp b/engines/dm/console.cpp
index eeb6d51c604..6c2082529c8 100644
--- a/engines/dm/console.cpp
+++ b/engines/dm/console.cpp
@@ -279,6 +279,7 @@ bool Console::Cmd_gimme(int argc, const char** argv) {
 						newThingData[thingCount * thingTypeSize + i] = newThingData[thingIndex * thingTypeSize + i];
 					_vm->_dungeonMan->_dungeonFileHeader._thingCounts[thingType]++;
 					_vm->_dungeonMan->_thingData[thingType] = newThingData;
+					dummyThing.setIndex(thingCount);
 					_vm->_championMan->addObjectInSlot((ChampionIndex)0, dummyThing, (ChampionSlot)29);
 					debugPrintf("Item gimmed to the first champion, last slot\n");
 					return true;


Commit: 558e3b329e54c713504b62c6d96835a9f025dbb5
    https://github.com/scummvm/scummvm/commit/558e3b329e54c713504b62c6d96835a9f025dbb5
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-06-22T00:11:59+02:00

Commit Message:
DM: Add bounds checking to prevent crashes from out-of-bounds object aspect reads

Changed paths:
    engines/dm/gfx.cpp


diff --git a/engines/dm/gfx.cpp b/engines/dm/gfx.cpp
index 01e350fffe3..3da5c63fbdf 100644
--- a/engines/dm/gfx.cpp
+++ b/engines/dm/gfx.cpp
@@ -2991,6 +2991,9 @@ void DisplayMan::drawObjectsCreaturesProjectilesExplosions(Thing thingParam, Dir
 	bool drawProjectileAsObject; /* When true, the code section to draw an object is called (with a goto) to draw the projectile, then the code section goes back to projectile processing with another goto */
 	uint16 currentViewCellToDraw = 0;
 	bool projectileFlipVertical = false;
+	uint16 infoIndex;
+	uint16 aspectIndex;
+
 
 	/* This is the full dungeon view */
 	static Box boxExplosionPatternD0C = Box(0, 223, 0, 135); // @ G0105_s_Graphic558_Box_ExplosionPattern_D0C
@@ -3234,7 +3237,13 @@ void DisplayMan::drawObjectsCreaturesProjectilesExplosions(Thing thingParam, Dir
 			}
 
 			if ((viewSquareIndex >= kDMViewSquareD3C) && (viewSquareIndex <= kDMViewSquareD0C) && (thingParam.getCell() == cellYellowBear)) { /* Square where objects are visible and object is located on cell being processed */
-				objectAspect = &(_objectAspects209[dungeon._objectInfos[dungeon.getObjectInfoIndex(thingParam)]._objectAspectIndex]);
+				infoIndex = dungeon.getObjectInfoIndex(thingParam);
+				if (infoIndex >= 180)
+					continue;
+				aspectIndex = dungeon._objectInfos[infoIndex]._objectAspectIndex;
+				if (aspectIndex >= k85_ObjAspectCount)
+					continue;
+				objectAspect = &(_objectAspects209[aspectIndex]);
 				AL_4_nativeBitmapIndex = kDMGraphicIdxFirstObject + objectAspect->_firstNativeBitmapRelativeIndex;
 				useAlcoveObjectImage = (L0135_B_DrawAlcoveObjects && getFlag(objectAspect->_graphicInfo, k0x0010_ObjectAlcoveMask) && (viewLane == kDMViewLaneCenter));
 				if (useAlcoveObjectImage)


Commit: fa2245a23cb184d352927e58f2a5812720dba5d3
    https://github.com/scummvm/scummvm/commit/fa2245a23cb184d352927e58f2a5812720dba5d3
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-06-22T00:11:59+02:00

Commit Message:
DM: Change ActiveGroup::_directions type to uint16 to prevent undefined behavior

Prevent undefined behavior when storing packed creature direction values

Changed paths:
    engines/dm/dungeonman.cpp
    engines/dm/group.cpp
    engines/dm/group.h


diff --git a/engines/dm/dungeonman.cpp b/engines/dm/dungeonman.cpp
index b6d50a144e1..eb649333f29 100644
--- a/engines/dm/dungeonman.cpp
+++ b/engines/dm/dungeonman.cpp
@@ -1553,7 +1553,7 @@ void DungeonMan::setGroupCells(Group *group, uint16 cells, uint16 mapIndex) {
 
 void DungeonMan::setGroupDirections(Group *group, int16 dir, uint16 mapIndex) {
 	if (mapIndex == _partyMapIndex)
-		_vm->_groupMan->_activeGroups[group->getActiveGroupIndex()]._directions = (Direction)dir;
+		_vm->_groupMan->_activeGroups[group->getActiveGroupIndex()]._directions = dir;
 	else
 		group->setDir(_vm->normalizeModulo4(dir));
 }
diff --git a/engines/dm/group.cpp b/engines/dm/group.cpp
index c6180060ba6..ac0032e314c 100644
--- a/engines/dm/group.cpp
+++ b/engines/dm/group.cpp
@@ -1384,7 +1384,7 @@ void GroupMan::setGroupDirection(ActiveGroup *activeGroup, int16 dir, int16 crea
 		G0396_ps_TwoHalfSquareSizedCreaturesGroupLastDirectionSetActiveGroup = activeGroup;
 	}
 
-	activeGroup->_directions = (Direction)groupDirections;
+	activeGroup->_directions = groupDirections;
 }
 
 void GroupMan::addGroupEvent(TimelineEvent *event, uint32 time) {
@@ -1752,7 +1752,7 @@ void GroupMan::addActiveGroup(Thing thing, int16 mapX, int16 mapY) {
 	activeGroup->_lastMoveTime = _vm->_gameTime - 127;
 	uint16 creatureIndex = curGroup->getCount();
 	do {
-		activeGroup->_directions = (Direction)getGroupValueUpdatedWithCreatureValue(activeGroup->_directions, creatureIndex, curGroup->getDir());
+		activeGroup->_directions = getGroupValueUpdatedWithCreatureValue(activeGroup->_directions, creatureIndex, curGroup->getDir());
 		activeGroup->_aspect[creatureIndex] = 0;
 	} while (creatureIndex--);
 	getCreatureAspectUpdateTime(activeGroup, kDMWholeCreatureGroup, false);
@@ -2118,7 +2118,7 @@ void GroupMan::loadActiveGroupPart(Common::InSaveFile *file) {
 	for (uint16 i = 0; i < _maxActiveGroupCount; ++i) {
 		ActiveGroup *group = &_activeGroups[i];
 		group->_groupThingIndex = file->readUint16BE();
-		group->_directions = (Direction)file->readUint16BE();
+		group->_directions = file->readUint16BE();
 		group->_cells = file->readByte();
 		group->_lastMoveTime = file->readByte();
 		group->_delayFleeingFromTarget = file->readByte();
diff --git a/engines/dm/group.h b/engines/dm/group.h
index 3af34b631b2..468debec04e 100644
--- a/engines/dm/group.h
+++ b/engines/dm/group.h
@@ -111,7 +111,7 @@ enum aspectMask {
 class ActiveGroup {
 public:
 	int16 _groupThingIndex;
-	Direction _directions;
+	uint16 _directions;
 	byte _cells;
 	byte _lastMoveTime;
 	byte _delayFleeingFromTarget;


Commit: 5b306e97c763d453e6eb27e30120fc14486d24bb
    https://github.com/scummvm/scummvm/commit/5b306e97c763d453e6eb27e30120fc14486d24bb
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-06-22T00:11:59+02:00

Commit Message:
DM: Fix infinite loop freeze in dropCreatureFixedPossessions

Advance the fixedPossessions pointer when skipping random drops or when no unused items are available. This fixes a game freeze when throwing explosives at creature groups.

Changed paths:
    engines/dm/group.cpp


diff --git a/engines/dm/group.cpp b/engines/dm/group.cpp
index ac0032e314c..9b65c5c8a02 100644
--- a/engines/dm/group.cpp
+++ b/engines/dm/group.cpp
@@ -271,8 +271,10 @@ void GroupMan::dropCreatureFixedPossessions(CreatureType creatureType, int16 map
 	uint16 currFixedPossession = *fixedPossessions++;
 	bool weaponDropped = false;
 	while (currFixedPossession) {
-		if (getFlag(currFixedPossession, kDMMaskRandomDrop) && _vm->getRandomNumber(2))
+		if (getFlag(currFixedPossession, kDMMaskRandomDrop) && _vm->getRandomNumber(2)) {
+			currFixedPossession = *fixedPossessions++;
 			continue;
+		}
 
 		int16 currThingType;
 		if (clearFlag(currFixedPossession, kDMMaskRandomDrop) >= kDMObjectInfoIndexFirstJunk) {
@@ -288,8 +290,10 @@ void GroupMan::dropCreatureFixedPossessions(CreatureType creatureType, int16 map
 		}
 
 		Thing nextUnusedThing = dungeon.getUnusedThing(currThingType);
-		if ((nextUnusedThing) == _vm->_thingNone)
+		if ((nextUnusedThing) == _vm->_thingNone) {
+			currFixedPossession = *fixedPossessions++;
 			continue;
+		}
 
 		Weapon *currWeapon = (Weapon *)dungeon.getThingData(nextUnusedThing);
 		/* The same pointer type is used no matter the actual type k5_WeaponThingType, k6_ArmourThingType or k10_JunkThingType */


Commit: 98c9b23e673706040b9c773fe73e5cfe9ee2dcdf
    https://github.com/scummvm/scummvm/commit/98c9b23e673706040b9c773fe73e5cfe9ee2dcdf
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-06-22T00:11:59+02:00

Commit Message:
DM: Fix crash when throwing projectiles by correcting cache index offsets

Changed paths:
    engines/dm/gfx.cpp


diff --git a/engines/dm/gfx.cpp b/engines/dm/gfx.cpp
index 3da5c63fbdf..5a9b0187e0d 100644
--- a/engines/dm/gfx.cpp
+++ b/engines/dm/gfx.cpp
@@ -752,13 +752,13 @@ void DisplayMan::initializeGraphicData() {
 
 			for (int16 projectileScaleIndex = 0; projectileScaleIndex < 6; projectileScaleIndex++) {
 				int16 bitmapByteCount = getScaledBitmapByteCount(projectileAspect->_byteWidth, projectileAspect->_height, _projectileScales[projectileScaleIndex]);
-				_derivedBitmapByteCount[derivedBitmapIndex] = bitmapByteCount;
+				_derivedBitmapByteCount[derivedBitmapIndex + projectileScaleIndex] = bitmapByteCount;
 
 				if (getFlag(projectileAspect->_graphicInfo, k0x0003_ProjectileAspectTypeMask) != k3_ProjectileAspectHasNone) {
-					_derivedBitmapByteCount[derivedBitmapIndex + 6] = bitmapByteCount;
+					_derivedBitmapByteCount[derivedBitmapIndex + 6 + projectileScaleIndex] = bitmapByteCount;
 
 					if (getFlag(projectileAspect->_graphicInfo, k0x0003_ProjectileAspectTypeMask) != k2_ProjectileAspectHasRotation)
-						_derivedBitmapByteCount[derivedBitmapIndex + 12] = bitmapByteCount;
+						_derivedBitmapByteCount[derivedBitmapIndex + 12 + projectileScaleIndex] = bitmapByteCount;
 				}
 			}
 		}



From noreply at scummvm.org  Sun Jun 21 22:22:24 2026
From: noreply at scummvm.org (ScummVM-Translations)
Date: Sun, 21 Jun 2026 22:22:24 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 aa2bbeaf272d6765f6133e9a409dccf962cc4253
Message-ID: <20260621222224.432C57005CE@ron.scummvm.net>

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

Summary:
aa2bbeaf27 I18N: Update translations templates


Commit: aa2bbeaf272d6765f6133e9a409dccf962cc4253
    https://github.com/scummvm/scummvm/commit/aa2bbeaf272d6765f6133e9a409dccf962cc4253
Author: Weblate (noreply at weblate.org)
Date: 2026-06-21T22:22:20Z

Commit Message:
I18N: Update translations templates

Changed paths:
    po/scummvm.pot


diff --git a/po/scummvm.pot b/po/scummvm.pot
index 7af4781451b..9359e078e10 100644
--- a/po/scummvm.pot
+++ b/po/scummvm.pot
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2026.3.1git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-21 16:24+0000\n"
+"POT-Creation-Date: 2026-06-21 22:22+0000\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL at ADDRESS>\n"
 "Language-Team: LANGUAGE <LL at li.org>\n"
@@ -7911,6 +7911,42 @@ msgstr ""
 msgid "Enable Text to Speech for Objects, Options, and the Bible Quote"
 msgstr ""
 
+#: engines/eem/metaengine.cpp:38
+msgid "Hide the highlight boxes"
+msgstr ""
+
+#: engines/eem/metaengine.cpp:39
+msgid "Hide the boxes that highlight searchable clue locations."
+msgstr ""
+
+#: engines/eem/metaengine.cpp:49
+msgid "Better fit for dialog balloons"
+msgstr ""
+
+#: engines/eem/metaengine.cpp:50
+msgid ""
+"Pick a smaller speech-bubble graphic when the wrapped text doesn't need the "
+"original size."
+msgstr ""
+
+#: engines/eem/metaengine.cpp:61
+msgid "Skip repeated cases"
+msgstr ""
+
+#: engines/eem/metaengine.cpp:62
+msgid "Skip all Book 2 cases and jump to Book 3 once Book 1 is complete."
+msgstr ""
+
+#: engines/eem/metaengine.cpp:72 engines/hypno/metaengine.cpp:81
+msgid "Enable restored content"
+msgstr ""
+
+#: engines/eem/metaengine.cpp:73
+msgid ""
+"Restore floppy release extras in the CD version, including pre-scrapbook "
+"conversations and first-try badges."
+msgstr ""
+
 #: engines/efh/metaengine.cpp:235
 msgid "Select option 1"
 msgstr ""
@@ -9408,10 +9444,6 @@ msgstr ""
 msgid "All levels will be available to play."
 msgstr ""
 
-#: engines/hypno/metaengine.cpp:81
-msgid "Enable restored content"
-msgstr ""
-
 #: engines/hypno/metaengine.cpp:82
 msgid "Add additional content that is not enabled the original implementation."
 msgstr ""



From noreply at scummvm.org  Sun Jun 21 22:22:59 2026
From: noreply at scummvm.org (ScummVM-Translations)
Date: Sun, 21 Jun 2026 22:22:59 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 5e2b4dbc06435b500ba3c8e0d5b27d9f6c6dff28
Message-ID: <20260621222259.9383D7005CE@ron.scummvm.net>

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

Summary:
5e2b4dbc06 I18N: Update translation files


Commit: 5e2b4dbc06435b500ba3c8e0d5b27d9f6c6dff28
    https://github.com/scummvm/scummvm/commit/5e2b4dbc06435b500ba3c8e0d5b27d9f6c6dff28
Author: Weblate (noreply at weblate.org)
Date: 2026-06-21T22:22:40Z

Commit Message:
I18N: Update translation files

Updated by "Update PO files to match POT (msgmerge)" hook in Weblate.

Translation: ScummVM/scummvm
Translate-URL: https://translations.scummvm.org/projects/scummvm/scummvm/

Changed paths:
    po/ar.po
    po/be-tarask.po
    po/be_BY.po
    po/ca_ES.po
    po/cs_CZ.po
    po/da.po
    po/de_DE.po
    po/el.po
    po/es_ES.po
    po/eu.po
    po/fi_FI.po
    po/fr_FR.po
    po/gl_ES.po
    po/he.po
    po/hi.po
    po/hu_HU.po
    po/it_IT.po
    po/ja.po
    po/ka.po
    po/ko.po
    po/nb_NO.po
    po/nl_NL.po
    po/nn_NO.po
    po/pl_PL.po
    po/pt_BR.po
    po/pt_PT.po
    po/ro.po
    po/ru_RU.po
    po/sv_SE.po
    po/tr.po
    po/uk_UA.po
    po/zh.po
    po/zh_Hans.po
    po/zh_Hant.po


diff --git a/po/ar.po b/po/ar.po
index 95e31dbda87..dfba19502f4 100644
--- a/po/ar.po
+++ b/po/ar.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-21 16:24+0000\n"
+"POT-Creation-Date: 2026-06-21 22:22+0000\n"
 "PO-Revision-Date: 2026-02-24 22:26+0000\n"
 "Last-Translator: Mohamed Shaaban <mohmedbashamedo at hotmail.com>\n"
 "Language-Team: Arabic <https://translations.scummvm.org/projects/scummvm/"
@@ -8805,6 +8805,46 @@ msgstr "???????? ???????????????? ???????????????? ???????? ?????????????? ????
 msgid "Enable Text to Speech for Objects, Options, and the Bible Quote"
 msgstr "?????????? ?????????? ???????? ?????? ???????? ???????????????? ?????????????????? ?????????????? ???????????? ????????????"
 
+#: engines/eem/metaengine.cpp:38
+#, fuzzy
+#| msgid "Highlight next dialogue"
+msgid "Hide the highlight boxes"
+msgstr "???? ???????????? ???????????? ????????????"
+
+#: engines/eem/metaengine.cpp:39
+msgid "Hide the boxes that highlight searchable clue locations."
+msgstr ""
+
+#: engines/eem/metaengine.cpp:49
+msgid "Better fit for dialog balloons"
+msgstr ""
+
+#: engines/eem/metaengine.cpp:50
+msgid ""
+"Pick a smaller speech-bubble graphic when the wrapped text doesn't need the "
+"original size."
+msgstr ""
+
+#: engines/eem/metaengine.cpp:61
+#, fuzzy
+#| msgid "Speech"
+msgid "Skip repeated cases"
+msgstr "????????"
+
+#: engines/eem/metaengine.cpp:62
+msgid "Skip all Book 2 cases and jump to Book 3 once Book 1 is complete."
+msgstr ""
+
+#: engines/eem/metaengine.cpp:72 engines/hypno/metaengine.cpp:81
+msgid "Enable restored content"
+msgstr "?????????? ?????????????? ????????????????"
+
+#: engines/eem/metaengine.cpp:73
+msgid ""
+"Restore floppy release extras in the CD version, including pre-scrapbook "
+"conversations and first-try badges."
+msgstr ""
+
 #: engines/efh/metaengine.cpp:235
 #, fuzzy
 #| msgid "Select Group by"
@@ -10622,10 +10662,6 @@ msgstr "???????? ???????? ??????????????????"
 msgid "All levels will be available to play."
 msgstr "?????????? ???????? ?????????????????? ?????????? ????????."
 
-#: engines/hypno/metaengine.cpp:81
-msgid "Enable restored content"
-msgstr "?????????? ?????????????? ????????????????"
-
 #: engines/hypno/metaengine.cpp:82
 msgid "Add additional content that is not enabled the original implementation."
 msgstr "?????? ?????????? ?????????????? ???? ?????? ?????????? ?????????????? ????????????."
diff --git a/po/be-tarask.po b/po/be-tarask.po
index c937111b822..c706b84260f 100644
--- a/po/be-tarask.po
+++ b/po/be-tarask.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-21 16:24+0000\n"
+"POT-Creation-Date: 2026-06-21 22:22+0000\n"
 "PO-Revision-Date: 2023-02-26 15:01+0000\n"
 "Last-Translator: Kastu?? K. <oakum2334 at gmail.com>\n"
 "Language-Team: Belarusian (Tara??kievica) <https://translations.scummvm.org/"
@@ -8761,6 +8761,46 @@ msgid "Enable Text to Speech for Objects, Options, and the Bible Quote"
 msgstr ""
 "???????????????? ?????????????????????????? ???????????? ?? ???????? ?????? ?????????????????, ?????????????? ?? ?????????????????? ????????????"
 
+#: engines/eem/metaengine.cpp:38
+#, fuzzy
+#| msgid "Highlight next dialogue"
+msgid "Hide the highlight boxes"
+msgstr "???????????????????? ???????????????? ????????????"
+
+#: engines/eem/metaengine.cpp:39
+msgid "Hide the boxes that highlight searchable clue locations."
+msgstr ""
+
+#: engines/eem/metaengine.cpp:49
+msgid "Better fit for dialog balloons"
+msgstr ""
+
+#: engines/eem/metaengine.cpp:50
+msgid ""
+"Pick a smaller speech-bubble graphic when the wrapped text doesn't need the "
+"original size."
+msgstr ""
+
+#: engines/eem/metaengine.cpp:61
+#, fuzzy
+#| msgid "Speech"
+msgid "Skip repeated cases"
+msgstr "????????????"
+
+#: engines/eem/metaengine.cpp:62
+msgid "Skip all Book 2 cases and jump to Book 3 once Book 1 is complete."
+msgstr ""
+
+#: engines/eem/metaengine.cpp:72 engines/hypno/metaengine.cpp:81
+msgid "Enable restored content"
+msgstr "???????????????? ?????????????????? ????????????"
+
+#: engines/eem/metaengine.cpp:73
+msgid ""
+"Restore floppy release extras in the CD version, including pre-scrapbook "
+"conversations and first-try badges."
+msgstr ""
+
 #: engines/efh/metaengine.cpp:235
 #, fuzzy
 #| msgid "Select Group by"
@@ -10579,10 +10619,6 @@ msgstr ""
 msgid "All levels will be available to play."
 msgstr ""
 
-#: engines/hypno/metaengine.cpp:81
-msgid "Enable restored content"
-msgstr "???????????????? ?????????????????? ????????????"
-
 #: engines/hypno/metaengine.cpp:82
 msgid "Add additional content that is not enabled the original implementation."
 msgstr "???????????? ????????????, ?????? ???? ???????????????? ?? ??????????????????."
diff --git a/po/be_BY.po b/po/be_BY.po
index 57c6fd94bec..a533ffb6f38 100644
--- a/po/be_BY.po
+++ b/po/be_BY.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-21 16:24+0000\n"
+"POT-Creation-Date: 2026-06-21 22:22+0000\n"
 "PO-Revision-Date: 2021-03-21 08:24+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Belarusian <https://translations.scummvm.org/projects/scummvm/"
@@ -8708,6 +8708,47 @@ msgstr "?????????? ?????????????? ?? ?????????????????????????? ?????????? ????
 msgid "Enable Text to Speech for Objects, Options, and the Bible Quote"
 msgstr ""
 
+#: engines/eem/metaengine.cpp:38
+#, fuzzy
+#| msgid "Highlight next dialogue"
+msgid "Hide the highlight boxes"
+msgstr "?????????????????????? ???????????????? ????????????"
+
+#: engines/eem/metaengine.cpp:39
+msgid "Hide the boxes that highlight searchable clue locations."
+msgstr ""
+
+#: engines/eem/metaengine.cpp:49
+msgid "Better fit for dialog balloons"
+msgstr ""
+
+#: engines/eem/metaengine.cpp:50
+msgid ""
+"Pick a smaller speech-bubble graphic when the wrapped text doesn't need the "
+"original size."
+msgstr ""
+
+#: engines/eem/metaengine.cpp:61
+#, fuzzy
+#| msgid "Speech"
+msgid "Skip repeated cases"
+msgstr "????????????"
+
+#: engines/eem/metaengine.cpp:62
+msgid "Skip all Book 2 cases and jump to Book 3 once Book 1 is complete."
+msgstr ""
+
+#: engines/eem/metaengine.cpp:72 engines/hypno/metaengine.cpp:81
+#, fuzzy
+msgid "Enable restored content"
+msgstr "???????????????? ?????????????? ????????????????"
+
+#: engines/eem/metaengine.cpp:73
+msgid ""
+"Restore floppy release extras in the CD version, including pre-scrapbook "
+"conversations and first-try badges."
+msgstr ""
+
 #: engines/efh/metaengine.cpp:235
 #, fuzzy
 #| msgid "Select"
@@ -10477,11 +10518,6 @@ msgstr ""
 msgid "All levels will be available to play."
 msgstr ""
 
-#: engines/hypno/metaengine.cpp:81
-#, fuzzy
-msgid "Enable restored content"
-msgstr "???????????????? ?????????????? ????????????????"
-
 #: engines/hypno/metaengine.cpp:82
 msgid "Add additional content that is not enabled the original implementation."
 msgstr ""
diff --git a/po/ca_ES.po b/po/ca_ES.po
index a87abe2212d..8da36c0aa6a 100644
--- a/po/ca_ES.po
+++ b/po/ca_ES.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.6.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-21 16:24+0000\n"
+"POT-Creation-Date: 2026-06-21 22:22+0000\n"
 "PO-Revision-Date: 2026-05-26 04:22+0000\n"
 "Last-Translator: Xaviu <xaviu at xaviu.com>\n"
 "Language-Team: Catalan <https://translations.scummvm.org/projects/scummvm/"
@@ -8321,6 +8321,46 @@ msgstr "Mostra els gr??fics utilitzant la paleta brillant del joc"
 msgid "Enable Text to Speech for Objects, Options, and the Bible Quote"
 msgstr "Activa el text a veu per a objectes, opcions i la cita de la B??blia"
 
+#: engines/eem/metaengine.cpp:38
+#, fuzzy
+#| msgid "Highlight items"
+msgid "Hide the highlight boxes"
+msgstr "Ressalta els objectes"
+
+#: engines/eem/metaengine.cpp:39
+msgid "Hide the boxes that highlight searchable clue locations."
+msgstr ""
+
+#: engines/eem/metaengine.cpp:49
+msgid "Better fit for dialog balloons"
+msgstr ""
+
+#: engines/eem/metaengine.cpp:50
+msgid ""
+"Pick a smaller speech-bubble graphic when the wrapped text doesn't need the "
+"original size."
+msgstr ""
+
+#: engines/eem/metaengine.cpp:61
+#, fuzzy
+#| msgid "Skip speech"
+msgid "Skip repeated cases"
+msgstr "Omet les veus"
+
+#: engines/eem/metaengine.cpp:62
+msgid "Skip all Book 2 cases and jump to Book 3 once Book 1 is complete."
+msgstr ""
+
+#: engines/eem/metaengine.cpp:72 engines/hypno/metaengine.cpp:81
+msgid "Enable restored content"
+msgstr "Habilita el contingut restaurat"
+
+#: engines/eem/metaengine.cpp:73
+msgid ""
+"Restore floppy release extras in the CD version, including pre-scrapbook "
+"conversations and first-try badges."
+msgstr ""
+
 #: engines/efh/metaengine.cpp:235
 msgid "Select option 1"
 msgstr "Selecciona l'opci?? 1"
@@ -9871,10 +9911,6 @@ msgstr "Desbloqueja tots els nivells"
 msgid "All levels will be available to play."
 msgstr "Tots els nivells estaran disponibles per jugar."
 
-#: engines/hypno/metaengine.cpp:81
-msgid "Enable restored content"
-msgstr "Habilita el contingut restaurat"
-
 #: engines/hypno/metaengine.cpp:82
 msgid "Add additional content that is not enabled the original implementation."
 msgstr ""
diff --git a/po/cs_CZ.po b/po/cs_CZ.po
index 84f0f4118ea..36e5c829503 100644
--- a/po/cs_CZ.po
+++ b/po/cs_CZ.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.7.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-21 16:24+0000\n"
+"POT-Creation-Date: 2026-06-21 22:22+0000\n"
 "PO-Revision-Date: 2026-02-09 20:22+0000\n"
 "Last-Translator: AsciiWolf <mail at asciiwolf.com>\n"
 "Language-Team: Czech <https://translations.scummvm.org/projects/scummvm/"
@@ -8634,6 +8634,47 @@ msgstr "Zobrazit grafiku pomoc?? jasn?? palety hry"
 msgid "Enable Text to Speech for Objects, Options, and the Bible Quote"
 msgstr ""
 
+#: engines/eem/metaengine.cpp:38
+#, fuzzy
+#| msgid "Highlight next dialogue"
+msgid "Hide the highlight boxes"
+msgstr "Zv??raznit n??sleduj??c?? dialog"
+
+#: engines/eem/metaengine.cpp:39
+msgid "Hide the boxes that highlight searchable clue locations."
+msgstr ""
+
+#: engines/eem/metaengine.cpp:49
+msgid "Better fit for dialog balloons"
+msgstr ""
+
+#: engines/eem/metaengine.cpp:50
+msgid ""
+"Pick a smaller speech-bubble graphic when the wrapped text doesn't need the "
+"original size."
+msgstr ""
+
+#: engines/eem/metaengine.cpp:61
+#, fuzzy
+#| msgid "Speech"
+msgid "Skip repeated cases"
+msgstr "??e??"
+
+#: engines/eem/metaengine.cpp:62
+msgid "Skip all Book 2 cases and jump to Book 3 once Book 1 is complete."
+msgstr ""
+
+#: engines/eem/metaengine.cpp:72 engines/hypno/metaengine.cpp:81
+#, fuzzy
+msgid "Enable restored content"
+msgstr "Povolit cenzuru obsahu"
+
+#: engines/eem/metaengine.cpp:73
+msgid ""
+"Restore floppy release extras in the CD version, including pre-scrapbook "
+"conversations and first-try badges."
+msgstr ""
+
 #: engines/efh/metaengine.cpp:235
 #, fuzzy
 msgid "Select option 1"
@@ -10365,11 +10406,6 @@ msgstr ""
 msgid "All levels will be available to play."
 msgstr ""
 
-#: engines/hypno/metaengine.cpp:81
-#, fuzzy
-msgid "Enable restored content"
-msgstr "Povolit cenzuru obsahu"
-
 #: engines/hypno/metaengine.cpp:82
 msgid "Add additional content that is not enabled the original implementation."
 msgstr ""
diff --git a/po/da.po b/po/da.po
index 8bc49270cec..c5f52f0917f 100644
--- a/po/da.po
+++ b/po/da.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-21 16:24+0000\n"
+"POT-Creation-Date: 2026-06-21 22:22+0000\n"
 "PO-Revision-Date: 2023-09-07 17:04+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Danish <https://translations.scummvm.org/projects/scummvm/"
@@ -8667,6 +8667,47 @@ msgstr "Vis grafik ved hj??lp af spillets lyse palet"
 msgid "Enable Text to Speech for Objects, Options, and the Bible Quote"
 msgstr ""
 
+#: engines/eem/metaengine.cpp:38
+#, fuzzy
+#| msgid "Highlight next dialogue"
+msgid "Hide the highlight boxes"
+msgstr "Fremh??v n??ste dialog"
+
+#: engines/eem/metaengine.cpp:39
+msgid "Hide the boxes that highlight searchable clue locations."
+msgstr ""
+
+#: engines/eem/metaengine.cpp:49
+msgid "Better fit for dialog balloons"
+msgstr ""
+
+#: engines/eem/metaengine.cpp:50
+msgid ""
+"Pick a smaller speech-bubble graphic when the wrapped text doesn't need the "
+"original size."
+msgstr ""
+
+#: engines/eem/metaengine.cpp:61
+#, fuzzy
+#| msgid "Speech"
+msgid "Skip repeated cases"
+msgstr "Tale"
+
+#: engines/eem/metaengine.cpp:62
+msgid "Skip all Book 2 cases and jump to Book 3 once Book 1 is complete."
+msgstr ""
+
+#: engines/eem/metaengine.cpp:72 engines/hypno/metaengine.cpp:81
+#, fuzzy
+msgid "Enable restored content"
+msgstr "Aktiv??r indholdscensurering"
+
+#: engines/eem/metaengine.cpp:73
+msgid ""
+"Restore floppy release extras in the CD version, including pre-scrapbook "
+"conversations and first-try badges."
+msgstr ""
+
 #: engines/efh/metaengine.cpp:235
 #, fuzzy
 msgid "Select option 1"
@@ -10389,11 +10430,6 @@ msgstr ""
 msgid "All levels will be available to play."
 msgstr ""
 
-#: engines/hypno/metaengine.cpp:81
-#, fuzzy
-msgid "Enable restored content"
-msgstr "Aktiv??r indholdscensurering"
-
 #: engines/hypno/metaengine.cpp:82
 msgid "Add additional content that is not enabled the original implementation."
 msgstr ""
diff --git a/po/de_DE.po b/po/de_DE.po
index e56a9856194..b6d20127353 100644
--- a/po/de_DE.po
+++ b/po/de_DE.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.10.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-21 16:24+0000\n"
+"POT-Creation-Date: 2026-06-21 22:22+0000\n"
 "PO-Revision-Date: 2026-05-05 16:31+0000\n"
 "Last-Translator: Lothar Serra Mari <lothar.serramari at scummvm.org>\n"
 "Language-Team: German <https://translations.scummvm.org/projects/scummvm/"
@@ -8807,6 +8807,46 @@ msgid "Enable Text to Speech for Objects, Options, and the Bible Quote"
 msgstr ""
 "Aktiviere Sprachausgabe f??r Gegenst??nde, Optionen und f??r das Bibel-Zitat"
 
+#: engines/eem/metaengine.cpp:38
+#, fuzzy
+#| msgid "Highlight items"
+msgid "Hide the highlight boxes"
+msgstr "Elemente hervorheben"
+
+#: engines/eem/metaengine.cpp:39
+msgid "Hide the boxes that highlight searchable clue locations."
+msgstr ""
+
+#: engines/eem/metaengine.cpp:49
+msgid "Better fit for dialog balloons"
+msgstr ""
+
+#: engines/eem/metaengine.cpp:50
+msgid ""
+"Pick a smaller speech-bubble graphic when the wrapped text doesn't need the "
+"original size."
+msgstr ""
+
+#: engines/eem/metaengine.cpp:61
+#, fuzzy
+#| msgid "Skip speech"
+msgid "Skip repeated cases"
+msgstr "Sprachausgabe ??berspringen"
+
+#: engines/eem/metaengine.cpp:62
+msgid "Skip all Book 2 cases and jump to Book 3 once Book 1 is complete."
+msgstr ""
+
+#: engines/eem/metaengine.cpp:72 engines/hypno/metaengine.cpp:81
+msgid "Enable restored content"
+msgstr "Wiederhergestellte Inhalte aktivieren"
+
+#: engines/eem/metaengine.cpp:73
+msgid ""
+"Restore floppy release extras in the CD version, including pre-scrapbook "
+"conversations and first-try badges."
+msgstr ""
+
 #: engines/efh/metaengine.cpp:235
 msgid "Select option 1"
 msgstr "Option 1 ausw??hlen"
@@ -10386,10 +10426,6 @@ msgstr "Alle Level freischalten"
 msgid "All levels will be available to play."
 msgstr "Alle Level k??nnen direkt gespielt werden."
 
-#: engines/hypno/metaengine.cpp:81
-msgid "Enable restored content"
-msgstr "Wiederhergestellte Inhalte aktivieren"
-
 #: engines/hypno/metaengine.cpp:82
 msgid "Add additional content that is not enabled the original implementation."
 msgstr ""
diff --git a/po/el.po b/po/el.po
index c4f71707726..4a0e7a85146 100644
--- a/po/el.po
+++ b/po/el.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.10.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-21 16:24+0000\n"
+"POT-Creation-Date: 2026-06-21 22:22+0000\n"
 "PO-Revision-Date: 2026-04-23 00:22+0000\n"
 "Last-Translator: Antoniou Athanasios <a.antoniou79 at gmail.com>\n"
 "Language-Team: Greek <https://translations.scummvm.org/projects/scummvm/"
@@ -8941,6 +8941,46 @@ msgid "Enable Text to Speech for Objects, Options, and the Bible Quote"
 msgstr ""
 "?????????????? ???????????????? ?????? ??????????????????????, ????????????????, ?????? ???? ???????????????????? ?????? ????????????"
 
+#: engines/eem/metaengine.cpp:38
+#, fuzzy
+#| msgid "Highlight items"
+msgid "Hide the highlight boxes"
+msgstr "???????????????????? ????????????????????????"
+
+#: engines/eem/metaengine.cpp:39
+msgid "Hide the boxes that highlight searchable clue locations."
+msgstr ""
+
+#: engines/eem/metaengine.cpp:49
+msgid "Better fit for dialog balloons"
+msgstr ""
+
+#: engines/eem/metaengine.cpp:50
+msgid ""
+"Pick a smaller speech-bubble graphic when the wrapped text doesn't need the "
+"original size."
+msgstr ""
+
+#: engines/eem/metaengine.cpp:61
+#, fuzzy
+#| msgid "Skip speech"
+msgid "Skip repeated cases"
+msgstr "?????????????????? ??????????????"
+
+#: engines/eem/metaengine.cpp:62
+msgid "Skip all Book 2 cases and jump to Book 3 once Book 1 is complete."
+msgstr ""
+
+#: engines/eem/metaengine.cpp:72 engines/hypno/metaengine.cpp:81
+msgid "Enable restored content"
+msgstr "???? ???????????????????????????? ??????????????????????"
+
+#: engines/eem/metaengine.cpp:73
+msgid ""
+"Restore floppy release extras in the CD version, including pre-scrapbook "
+"conversations and first-try badges."
+msgstr ""
+
 #: engines/efh/metaengine.cpp:235
 msgid "Select option 1"
 msgstr "?????????????? 1"
@@ -10515,10 +10555,6 @@ msgstr "???????????????????? ???????? ?????? ????????????????"
 msgid "All levels will be available to play."
 msgstr "?????? ???? ?????????????? ???? ???????????? ?????????????????? ?????? ????????????????."
 
-#: engines/hypno/metaengine.cpp:81
-msgid "Enable restored content"
-msgstr "???? ???????????????????????????? ??????????????????????"
-
 #: engines/hypno/metaengine.cpp:82
 msgid "Add additional content that is not enabled the original implementation."
 msgstr ""
diff --git a/po/es_ES.po b/po/es_ES.po
index aa0c4e75aa3..89d671ed71e 100644
--- a/po/es_ES.po
+++ b/po/es_ES.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.4.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-21 16:24+0000\n"
+"POT-Creation-Date: 2026-06-21 22:22+0000\n"
 "PO-Revision-Date: 2026-06-20 15:53+0000\n"
 "Last-Translator: IlDucci <pajaroloco_2 at hotmail.com>\n"
 "Language-Team: Spanish <https://translations.scummvm.org/projects/scummvm/"
@@ -8813,6 +8813,46 @@ msgstr ""
 "Activa la conversi??n de texto a voz para objetos, opciones y el pasaje de la "
 "Biblia"
 
+#: engines/eem/metaengine.cpp:38
+#, fuzzy
+#| msgid "Highlight items"
+msgid "Hide the highlight boxes"
+msgstr "Resaltar objetos"
+
+#: engines/eem/metaengine.cpp:39
+msgid "Hide the boxes that highlight searchable clue locations."
+msgstr ""
+
+#: engines/eem/metaengine.cpp:49
+msgid "Better fit for dialog balloons"
+msgstr ""
+
+#: engines/eem/metaengine.cpp:50
+msgid ""
+"Pick a smaller speech-bubble graphic when the wrapped text doesn't need the "
+"original size."
+msgstr ""
+
+#: engines/eem/metaengine.cpp:61
+#, fuzzy
+#| msgid "Skip speech"
+msgid "Skip repeated cases"
+msgstr "Omitir di??logo"
+
+#: engines/eem/metaengine.cpp:62
+msgid "Skip all Book 2 cases and jump to Book 3 once Book 1 is complete."
+msgstr ""
+
+#: engines/eem/metaengine.cpp:72 engines/hypno/metaengine.cpp:81
+msgid "Enable restored content"
+msgstr "Activar contenidos restaurados"
+
+#: engines/eem/metaengine.cpp:73
+msgid ""
+"Restore floppy release extras in the CD version, including pre-scrapbook "
+"conversations and first-try badges."
+msgstr ""
+
 #: engines/efh/metaengine.cpp:235
 msgid "Select option 1"
 msgstr "Seleccionar opci??n 1"
@@ -10382,10 +10422,6 @@ msgstr "Desbloquear todos los niveles"
 msgid "All levels will be available to play."
 msgstr "Permite el acceso a todos los niveles."
 
-#: engines/hypno/metaengine.cpp:81
-msgid "Enable restored content"
-msgstr "Activar contenidos restaurados"
-
 #: engines/hypno/metaengine.cpp:82
 msgid "Add additional content that is not enabled the original implementation."
 msgstr ""
diff --git a/po/eu.po b/po/eu.po
index 8ebeb1c1773..1d643ae847b 100644
--- a/po/eu.po
+++ b/po/eu.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.5.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-21 16:24+0000\n"
+"POT-Creation-Date: 2026-06-21 22:22+0000\n"
 "PO-Revision-Date: 2024-02-05 18:42+0000\n"
 "Last-Translator: Josu Igoa <josuigoa at ni.eus>\n"
 "Language-Team: Basque <https://translations.scummvm.org/projects/scummvm/"
@@ -8709,6 +8709,47 @@ msgstr "Erakutsi grafikoak jokoaren paleta argia erabilita"
 msgid "Enable Text to Speech for Objects, Options, and the Bible Quote"
 msgstr ""
 
+#: engines/eem/metaengine.cpp:38
+#, fuzzy
+#| msgid "Highlight next dialogue"
+msgid "Hide the highlight boxes"
+msgstr "Hurrengo elkarrizketa aukeratu"
+
+#: engines/eem/metaengine.cpp:39
+msgid "Hide the boxes that highlight searchable clue locations."
+msgstr ""
+
+#: engines/eem/metaengine.cpp:49
+msgid "Better fit for dialog balloons"
+msgstr ""
+
+#: engines/eem/metaengine.cpp:50
+msgid ""
+"Pick a smaller speech-bubble graphic when the wrapped text doesn't need the "
+"original size."
+msgstr ""
+
+#: engines/eem/metaengine.cpp:61
+#, fuzzy
+#| msgid "Speech"
+msgid "Skip repeated cases"
+msgstr "Ahotsa"
+
+#: engines/eem/metaengine.cpp:62
+msgid "Skip all Book 2 cases and jump to Book 3 once Book 1 is complete."
+msgstr ""
+
+#: engines/eem/metaengine.cpp:72 engines/hypno/metaengine.cpp:81
+#, fuzzy
+msgid "Enable restored content"
+msgstr "Gaitu kurtsore flotatzaileak"
+
+#: engines/eem/metaengine.cpp:73
+msgid ""
+"Restore floppy release extras in the CD version, including pre-scrapbook "
+"conversations and first-try badges."
+msgstr ""
+
 #: engines/efh/metaengine.cpp:235
 #, fuzzy
 #| msgid "Select Group by"
@@ -10463,11 +10504,6 @@ msgstr ""
 msgid "All levels will be available to play."
 msgstr ""
 
-#: engines/hypno/metaengine.cpp:81
-#, fuzzy
-msgid "Enable restored content"
-msgstr "Gaitu kurtsore flotatzaileak"
-
 #: engines/hypno/metaengine.cpp:82
 msgid "Add additional content that is not enabled the original implementation."
 msgstr ""
diff --git a/po/fi_FI.po b/po/fi_FI.po
index d7dd582e000..be704bbd69d 100644
--- a/po/fi_FI.po
+++ b/po/fi_FI.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.6.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-21 16:24+0000\n"
+"POT-Creation-Date: 2026-06-21 22:22+0000\n"
 "PO-Revision-Date: 2026-02-07 15:22+0000\n"
 "Last-Translator: Timo Mikkolainen <tmikkola at gmail.com>\n"
 "Language-Team: Finnish <https://translations.scummvm.org/projects/scummvm/"
@@ -8941,6 +8941,46 @@ msgid "Enable Text to Speech for Objects, Options, and the Bible Quote"
 msgstr ""
 "Tekstist?? puheeksi -toiminto esineille, asetuksille ja raamattulainaukselle"
 
+#: engines/eem/metaengine.cpp:38
+#, fuzzy
+#| msgid "Highlight items"
+msgid "Hide the highlight boxes"
+msgstr "Korosta esineit??"
+
+#: engines/eem/metaengine.cpp:39
+msgid "Hide the boxes that highlight searchable clue locations."
+msgstr ""
+
+#: engines/eem/metaengine.cpp:49
+msgid "Better fit for dialog balloons"
+msgstr ""
+
+#: engines/eem/metaengine.cpp:50
+msgid ""
+"Pick a smaller speech-bubble graphic when the wrapped text doesn't need the "
+"original size."
+msgstr ""
+
+#: engines/eem/metaengine.cpp:61
+#, fuzzy
+#| msgid "Skip speech"
+msgid "Skip repeated cases"
+msgstr "Ohita puhe"
+
+#: engines/eem/metaengine.cpp:62
+msgid "Skip all Book 2 cases and jump to Book 3 once Book 1 is complete."
+msgstr ""
+
+#: engines/eem/metaengine.cpp:72 engines/hypno/metaengine.cpp:81
+msgid "Enable restored content"
+msgstr "K??yt?? palautettua sis??lt????"
+
+#: engines/eem/metaengine.cpp:73
+msgid ""
+"Restore floppy release extras in the CD version, including pre-scrapbook "
+"conversations and first-try badges."
+msgstr ""
+
 #: engines/efh/metaengine.cpp:235
 msgid "Select option 1"
 msgstr "Valitse vaihtoehto 1"
@@ -10588,10 +10628,6 @@ msgstr "Avaa kaikki tasot"
 msgid "All levels will be available to play."
 msgstr "Kaikki tasot ovat pelattavissa."
 
-#: engines/hypno/metaengine.cpp:81
-msgid "Enable restored content"
-msgstr "K??yt?? palautettua sis??lt????"
-
 #: engines/hypno/metaengine.cpp:82
 msgid "Add additional content that is not enabled the original implementation."
 msgstr "Lis???? sis??lt???? joka ei ole n??ht??viss?? alkuper??isess?? toteutuksessa."
diff --git a/po/fr_FR.po b/po/fr_FR.po
index 9ed9d426491..0d10f4b63a5 100644
--- a/po/fr_FR.po
+++ b/po/fr_FR.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-21 16:24+0000\n"
+"POT-Creation-Date: 2026-06-21 22:22+0000\n"
 "PO-Revision-Date: 2026-06-14 09:41+0000\n"
 "Last-Translator: Purple T <ZEONK at hotmail.com>\n"
 "Language-Team: French <https://translations.scummvm.org/projects/scummvm/"
@@ -8819,6 +8819,46 @@ msgstr ""
 "Activer la synth??se vocale pour les objets, les options et la citation de la "
 "Bible"
 
+#: engines/eem/metaengine.cpp:38
+#, fuzzy
+#| msgid "Highlight items"
+msgid "Hide the highlight boxes"
+msgstr "Surligner les items"
+
+#: engines/eem/metaengine.cpp:39
+msgid "Hide the boxes that highlight searchable clue locations."
+msgstr ""
+
+#: engines/eem/metaengine.cpp:49
+msgid "Better fit for dialog balloons"
+msgstr ""
+
+#: engines/eem/metaengine.cpp:50
+msgid ""
+"Pick a smaller speech-bubble graphic when the wrapped text doesn't need the "
+"original size."
+msgstr ""
+
+#: engines/eem/metaengine.cpp:61
+#, fuzzy
+#| msgid "Skip speech"
+msgid "Skip repeated cases"
+msgstr "Passer le dialogue"
+
+#: engines/eem/metaengine.cpp:62
+msgid "Skip all Book 2 cases and jump to Book 3 once Book 1 is complete."
+msgstr ""
+
+#: engines/eem/metaengine.cpp:72 engines/hypno/metaengine.cpp:81
+msgid "Enable restored content"
+msgstr "Activer le contenu restaur??"
+
+#: engines/eem/metaengine.cpp:73
+msgid ""
+"Restore floppy release extras in the CD version, including pre-scrapbook "
+"conversations and first-try badges."
+msgstr ""
+
 #: engines/efh/metaengine.cpp:235
 msgid "Select option 1"
 msgstr "S??lectionner option 1"
@@ -10386,10 +10426,6 @@ msgstr "D??bloquer tous les niveaux"
 msgid "All levels will be available to play."
 msgstr "Tous les niveaux seront disponibles."
 
-#: engines/hypno/metaengine.cpp:81
-msgid "Enable restored content"
-msgstr "Activer le contenu restaur??"
-
 #: engines/hypno/metaengine.cpp:82
 msgid "Add additional content that is not enabled the original implementation."
 msgstr ""
diff --git a/po/gl_ES.po b/po/gl_ES.po
index fe0ca1a7d6d..33b91d1e67d 100644
--- a/po/gl_ES.po
+++ b/po/gl_ES.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-21 16:24+0000\n"
+"POT-Creation-Date: 2026-06-21 22:22+0000\n"
 "PO-Revision-Date: 2020-09-26 21:13+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Galician <https://translations.scummvm.org/projects/scummvm/"
@@ -8641,6 +8641,47 @@ msgstr "Mostrar os gr??ficos coa paletta intensa do xogo"
 msgid "Enable Text to Speech for Objects, Options, and the Bible Quote"
 msgstr ""
 
+#: engines/eem/metaengine.cpp:38
+#, fuzzy
+#| msgid "Highlight next dialogue"
+msgid "Hide the highlight boxes"
+msgstr "Destacar di??logo seguinte"
+
+#: engines/eem/metaengine.cpp:39
+msgid "Hide the boxes that highlight searchable clue locations."
+msgstr ""
+
+#: engines/eem/metaengine.cpp:49
+msgid "Better fit for dialog balloons"
+msgstr ""
+
+#: engines/eem/metaengine.cpp:50
+msgid ""
+"Pick a smaller speech-bubble graphic when the wrapped text doesn't need the "
+"original size."
+msgstr ""
+
+#: engines/eem/metaengine.cpp:61
+#, fuzzy
+#| msgid "Speech"
+msgid "Skip repeated cases"
+msgstr "Voz"
+
+#: engines/eem/metaengine.cpp:62
+msgid "Skip all Book 2 cases and jump to Book 3 once Book 1 is complete."
+msgstr ""
+
+#: engines/eem/metaengine.cpp:72 engines/hypno/metaengine.cpp:81
+#, fuzzy
+msgid "Enable restored content"
+msgstr "Activar censura de contidos"
+
+#: engines/eem/metaengine.cpp:73
+msgid ""
+"Restore floppy release extras in the CD version, including pre-scrapbook "
+"conversations and first-try badges."
+msgstr ""
+
 #: engines/efh/metaengine.cpp:235
 #, fuzzy
 msgid "Select option 1"
@@ -10366,11 +10407,6 @@ msgstr ""
 msgid "All levels will be available to play."
 msgstr ""
 
-#: engines/hypno/metaengine.cpp:81
-#, fuzzy
-msgid "Enable restored content"
-msgstr "Activar censura de contidos"
-
 #: engines/hypno/metaengine.cpp:82
 msgid "Add additional content that is not enabled the original implementation."
 msgstr ""
diff --git a/po/he.po b/po/he.po
index 2a940df8c4f..5634a1a4b51 100644
--- a/po/he.po
+++ b/po/he.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.1.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-21 16:24+0000\n"
+"POT-Creation-Date: 2026-06-21 22:22+0000\n"
 "PO-Revision-Date: 2025-04-09 20:18+0000\n"
 "Last-Translator: Niv Baehr <bloop93 at gmail.com>\n"
 "Language-Team: Hebrew <https://translations.scummvm.org/projects/scummvm/"
@@ -8667,6 +8667,46 @@ msgstr "???????? ???????????? ?????????????? ?????? ???????????? ???? ??????????
 msgid "Enable Text to Speech for Objects, Options, and the Bible Quote"
 msgstr "?????????? ???????? ???????? ???????????? ???????? ??????????, ???????????????? ???????????????? ????????\"??"
 
+#: engines/eem/metaengine.cpp:38
+#, fuzzy
+#| msgid "Highlight next dialogue"
+msgid "Hide the highlight boxes"
+msgstr "???????? ???? ?????????????? ??????"
+
+#: engines/eem/metaengine.cpp:39
+msgid "Hide the boxes that highlight searchable clue locations."
+msgstr ""
+
+#: engines/eem/metaengine.cpp:49
+msgid "Better fit for dialog balloons"
+msgstr ""
+
+#: engines/eem/metaengine.cpp:50
+msgid ""
+"Pick a smaller speech-bubble graphic when the wrapped text doesn't need the "
+"original size."
+msgstr ""
+
+#: engines/eem/metaengine.cpp:61
+#, fuzzy
+#| msgid "Speech"
+msgid "Skip repeated cases"
+msgstr "??????????"
+
+#: engines/eem/metaengine.cpp:62
+msgid "Skip all Book 2 cases and jump to Book 3 once Book 1 is complete."
+msgstr ""
+
+#: engines/eem/metaengine.cpp:72 engines/hypno/metaengine.cpp:81
+msgid "Enable restored content"
+msgstr "?????????? ???????? ????????????"
+
+#: engines/eem/metaengine.cpp:73
+msgid ""
+"Restore floppy release extras in the CD version, including pre-scrapbook "
+"conversations and first-try badges."
+msgstr ""
+
 #: engines/efh/metaengine.cpp:235
 #, fuzzy
 #| msgid "Select Group by"
@@ -10478,10 +10518,6 @@ msgstr "?????????? ???? ????????????"
 msgid "All levels will be available to play."
 msgstr "???? ???????????? ???????? ???????????? ??????????."
 
-#: engines/hypno/metaengine.cpp:81
-msgid "Enable restored content"
-msgstr "?????????? ???????? ????????????"
-
 #: engines/hypno/metaengine.cpp:82
 msgid "Add additional content that is not enabled the original implementation."
 msgstr "?????????? ???????? ???????? ?????? ???????????? ???????????? ????????????."
diff --git a/po/hi.po b/po/hi.po
index b914ffc07c2..158b2f9a793 100644
--- a/po/hi.po
+++ b/po/hi.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-21 16:24+0000\n"
+"POT-Creation-Date: 2026-06-21 22:22+0000\n"
 "PO-Revision-Date: 2026-05-12 22:22+0000\n"
 "Last-Translator: KARTIK SAXENA <kartiksaxena2005 at gmail.com>\n"
 "Language-Team: Hindi <https://translations.scummvm.org/projects/scummvm/"
@@ -8300,6 +8300,42 @@ msgstr ""
 msgid "Enable Text to Speech for Objects, Options, and the Bible Quote"
 msgstr ""
 
+#: engines/eem/metaengine.cpp:38
+msgid "Hide the highlight boxes"
+msgstr ""
+
+#: engines/eem/metaengine.cpp:39
+msgid "Hide the boxes that highlight searchable clue locations."
+msgstr ""
+
+#: engines/eem/metaengine.cpp:49
+msgid "Better fit for dialog balloons"
+msgstr ""
+
+#: engines/eem/metaengine.cpp:50
+msgid ""
+"Pick a smaller speech-bubble graphic when the wrapped text doesn't need the "
+"original size."
+msgstr ""
+
+#: engines/eem/metaengine.cpp:61
+msgid "Skip repeated cases"
+msgstr ""
+
+#: engines/eem/metaengine.cpp:62
+msgid "Skip all Book 2 cases and jump to Book 3 once Book 1 is complete."
+msgstr ""
+
+#: engines/eem/metaengine.cpp:72 engines/hypno/metaengine.cpp:81
+msgid "Enable restored content"
+msgstr "Restored content ??????????????? ????????????"
+
+#: engines/eem/metaengine.cpp:73
+msgid ""
+"Restore floppy release extras in the CD version, including pre-scrapbook "
+"conversations and first-try badges."
+msgstr ""
+
 #: engines/efh/metaengine.cpp:235
 #, fuzzy
 #| msgid "Select shader"
@@ -9986,10 +10022,6 @@ msgstr ""
 msgid "All levels will be available to play."
 msgstr ""
 
-#: engines/hypno/metaengine.cpp:81
-msgid "Enable restored content"
-msgstr "Restored content ??????????????? ????????????"
-
 #: engines/hypno/metaengine.cpp:82
 msgid "Add additional content that is not enabled the original implementation."
 msgstr ""
diff --git a/po/hu_HU.po b/po/hu_HU.po
index f525b47c9cc..d38854c02d3 100644
--- a/po/hu_HU.po
+++ b/po/hu_HU.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-21 16:24+0000\n"
+"POT-Creation-Date: 2026-06-21 22:22+0000\n"
 "PO-Revision-Date: 2025-03-07 00:18+0000\n"
 "Last-Translator: Hov??nszki Tam??s <gahex68379 at lofiey.com>\n"
 "Language-Team: Hungarian <https://translations.scummvm.org/projects/scummvm/"
@@ -9158,6 +9158,46 @@ msgid "Enable Text to Speech for Objects, Options, and the Bible Quote"
 msgstr ""
 "Sz??vegfelolvas?? enged??lyez??se az objektumok, Opci??k ??s a Biblia id??zet eset??n"
 
+#: engines/eem/metaengine.cpp:38
+#, fuzzy
+#| msgid "Highlight next dialogue"
+msgid "Hide the highlight boxes"
+msgstr "K??vetkez?? dial??gus kiemel??se"
+
+#: engines/eem/metaengine.cpp:39
+msgid "Hide the boxes that highlight searchable clue locations."
+msgstr ""
+
+#: engines/eem/metaengine.cpp:49
+msgid "Better fit for dialog balloons"
+msgstr ""
+
+#: engines/eem/metaengine.cpp:50
+msgid ""
+"Pick a smaller speech-bubble graphic when the wrapped text doesn't need the "
+"original size."
+msgstr ""
+
+#: engines/eem/metaengine.cpp:61
+#, fuzzy
+#| msgid "Skip speech"
+msgid "Skip repeated cases"
+msgstr "Besz??d ??tugr??s"
+
+#: engines/eem/metaengine.cpp:62
+msgid "Skip all Book 2 cases and jump to Book 3 once Book 1 is complete."
+msgstr ""
+
+#: engines/eem/metaengine.cpp:72 engines/hypno/metaengine.cpp:81
+msgid "Enable restored content"
+msgstr "Vissza??ll??tott tartalom enged??lyez??se"
+
+#: engines/eem/metaengine.cpp:73
+msgid ""
+"Restore floppy release extras in the CD version, including pre-scrapbook "
+"conversations and first-try badges."
+msgstr ""
+
 #: engines/efh/metaengine.cpp:235
 #, fuzzy
 #| msgid "Select Actor 1"
@@ -10938,10 +10978,6 @@ msgstr "Minden p??lya el??rhet??v??"
 msgid "All levels will be available to play."
 msgstr "Minden p??lya el??rhet??v?? v??lik a j??t??kos sz??m??ra."
 
-#: engines/hypno/metaengine.cpp:81
-msgid "Enable restored content"
-msgstr "Vissza??ll??tott tartalom enged??lyez??se"
-
 #: engines/hypno/metaengine.cpp:82
 msgid "Add additional content that is not enabled the original implementation."
 msgstr ""
diff --git a/po/it_IT.po b/po/it_IT.po
index cd2f63405e7..f10ca4c04c9 100644
--- a/po/it_IT.po
+++ b/po/it_IT.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-21 16:24+0000\n"
+"POT-Creation-Date: 2026-06-21 22:22+0000\n"
 "PO-Revision-Date: 2026-06-20 15:07+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Italian <https://translations.scummvm.org/projects/scummvm/"
@@ -8788,6 +8788,46 @@ msgid "Enable Text to Speech for Objects, Options, and the Bible Quote"
 msgstr ""
 "Abilita Sintesi Vocale per gli oggetti, le opzioni e i passaggi della Bibbia"
 
+#: engines/eem/metaengine.cpp:38
+#, fuzzy
+#| msgid "Highlight items"
+msgid "Hide the highlight boxes"
+msgstr "Evidenzia oggetti"
+
+#: engines/eem/metaengine.cpp:39
+msgid "Hide the boxes that highlight searchable clue locations."
+msgstr ""
+
+#: engines/eem/metaengine.cpp:49
+msgid "Better fit for dialog balloons"
+msgstr ""
+
+#: engines/eem/metaengine.cpp:50
+msgid ""
+"Pick a smaller speech-bubble graphic when the wrapped text doesn't need the "
+"original size."
+msgstr ""
+
+#: engines/eem/metaengine.cpp:61
+#, fuzzy
+#| msgid "Skip speech"
+msgid "Skip repeated cases"
+msgstr "Salta dialogo"
+
+#: engines/eem/metaengine.cpp:62
+msgid "Skip all Book 2 cases and jump to Book 3 once Book 1 is complete."
+msgstr ""
+
+#: engines/eem/metaengine.cpp:72 engines/hypno/metaengine.cpp:81
+msgid "Enable restored content"
+msgstr "Ripristina contenuti tagliati"
+
+#: engines/eem/metaengine.cpp:73
+msgid ""
+"Restore floppy release extras in the CD version, including pre-scrapbook "
+"conversations and first-try badges."
+msgstr ""
+
 #: engines/efh/metaengine.cpp:235
 msgid "Select option 1"
 msgstr "Seleziona opzione 1"
@@ -10347,10 +10387,6 @@ msgstr "Sblocca tutti i livelli"
 msgid "All levels will be available to play."
 msgstr "Permette l'accesso a qualsiasi livello del gioco."
 
-#: engines/hypno/metaengine.cpp:81
-msgid "Enable restored content"
-msgstr "Ripristina contenuti tagliati"
-
 #: engines/hypno/metaengine.cpp:82
 msgid "Add additional content that is not enabled the original implementation."
 msgstr "Abilita alcune parti che sono state escluse dal gioco originale."
diff --git a/po/ja.po b/po/ja.po
index 4e571a861a7..e095095ea8c 100644
--- a/po/ja.po
+++ b/po/ja.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-21 16:24+0000\n"
+"POT-Creation-Date: 2026-06-21 22:22+0000\n"
 "PO-Revision-Date: 2026-06-04 07:22+0000\n"
 "Last-Translator: VAN-Gluon <van.gluonp at gmail.com>\n"
 "Language-Team: Japanese <https://translations.scummvm.org/projects/scummvm/"
@@ -8768,6 +8768,46 @@ msgstr "????????????????????????????????????????????????????????????????????????
 msgid "Enable Text to Speech for Objects, Options, and the Bible Quote"
 msgstr "?????????????????????????????????????????????????????????????????????????????????"
 
+#: engines/eem/metaengine.cpp:38
+#, fuzzy
+#| msgid "Highlight next dialogue"
+msgid "Hide the highlight boxes"
+msgstr "???????????????????????????????????????"
+
+#: engines/eem/metaengine.cpp:39
+msgid "Hide the boxes that highlight searchable clue locations."
+msgstr ""
+
+#: engines/eem/metaengine.cpp:49
+msgid "Better fit for dialog balloons"
+msgstr ""
+
+#: engines/eem/metaengine.cpp:50
+msgid ""
+"Pick a smaller speech-bubble graphic when the wrapped text doesn't need the "
+"original size."
+msgstr ""
+
+#: engines/eem/metaengine.cpp:61
+#, fuzzy
+#| msgid "Speech"
+msgid "Skip repeated cases"
+msgstr "??????"
+
+#: engines/eem/metaengine.cpp:62
+msgid "Skip all Book 2 cases and jump to Book 3 once Book 1 is complete."
+msgstr ""
+
+#: engines/eem/metaengine.cpp:72 engines/hypno/metaengine.cpp:81
+msgid "Enable restored content"
+msgstr "????????????????????????????????????????????????"
+
+#: engines/eem/metaengine.cpp:73
+msgid ""
+"Restore floppy release extras in the CD version, including pre-scrapbook "
+"conversations and first-try badges."
+msgstr ""
+
 #: engines/efh/metaengine.cpp:235
 #, fuzzy
 #| msgid "Select Actor 1"
@@ -10553,10 +10593,6 @@ msgstr "??????????????????"
 msgid "All levels will be available to play."
 msgstr "?????????????????????????????????????????????"
 
-#: engines/hypno/metaengine.cpp:81
-msgid "Enable restored content"
-msgstr "????????????????????????????????????????????????"
-
 #: engines/hypno/metaengine.cpp:82
 msgid "Add additional content that is not enabled the original implementation."
 msgstr "???????????????????????????????????????????????????????????????????????????????????????"
diff --git a/po/ka.po b/po/ka.po
index 5c6f38cdce8..6b5da6fca50 100644
--- a/po/ka.po
+++ b/po/ka.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-21 16:24+0000\n"
+"POT-Creation-Date: 2026-06-21 22:22+0000\n"
 "PO-Revision-Date: 2026-06-05 10:22+0000\n"
 "Last-Translator: NorwayFun <temuri.doghonadze at gmail.com>\n"
 "Language-Team: Georgian <https://translations.scummvm.org/projects/scummvm/"
@@ -7949,6 +7949,46 @@ msgstr ""
 msgid "Enable Text to Speech for Objects, Options, and the Bible Quote"
 msgstr ""
 
+#: engines/eem/metaengine.cpp:38
+#, fuzzy
+#| msgid "Highlight items"
+msgid "Hide the highlight boxes"
+msgstr "????????????????????????????????? ???????????????????????????"
+
+#: engines/eem/metaengine.cpp:39
+msgid "Hide the boxes that highlight searchable clue locations."
+msgstr ""
+
+#: engines/eem/metaengine.cpp:49
+msgid "Better fit for dialog balloons"
+msgstr ""
+
+#: engines/eem/metaengine.cpp:50
+msgid ""
+"Pick a smaller speech-bubble graphic when the wrapped text doesn't need the "
+"original size."
+msgstr ""
+
+#: engines/eem/metaengine.cpp:61
+#, fuzzy
+#| msgid "Skip speech"
+msgid "Skip repeated cases"
+msgstr "????????????????????? ??????????????????????????????"
+
+#: engines/eem/metaengine.cpp:62
+msgid "Skip all Book 2 cases and jump to Book 3 once Book 1 is complete."
+msgstr ""
+
+#: engines/eem/metaengine.cpp:72 engines/hypno/metaengine.cpp:81
+msgid "Enable restored content"
+msgstr "??????????????????????????? ????????????????????????????????? ??????????????????"
+
+#: engines/eem/metaengine.cpp:73
+msgid ""
+"Restore floppy release extras in the CD version, including pre-scrapbook "
+"conversations and first-try badges."
+msgstr ""
+
 #: engines/efh/metaengine.cpp:235
 msgid "Select option 1"
 msgstr "1 ?????????????????????????????? ??????????????????"
@@ -9446,10 +9486,6 @@ msgstr "??????????????? ??????????????? ???????????????????????????"
 msgid "All levels will be available to play."
 msgstr "??????????????????????????? ??????????????????????????????????????? ?????????????????? ??????????????? ????????????."
 
-#: engines/hypno/metaengine.cpp:81
-msgid "Enable restored content"
-msgstr "??????????????????????????? ????????????????????????????????? ??????????????????"
-
 #: engines/hypno/metaengine.cpp:82
 msgid "Add additional content that is not enabled the original implementation."
 msgstr ""
diff --git a/po/ko.po b/po/ko.po
index 184a11f6575..957d26d342b 100644
--- a/po/ko.po
+++ b/po/ko.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-21 16:24+0000\n"
+"POT-Creation-Date: 2026-06-21 22:22+0000\n"
 "PO-Revision-Date: 2026-05-19 20:22+0000\n"
 "Last-Translator: Hoseok Seo <ddinghoya at gmail.com>\n"
 "Language-Team: Korean <https://translations.scummvm.org/projects/scummvm/"
@@ -8610,6 +8610,46 @@ msgstr "?????? ???????????? ?????? ???????????? ??????"
 msgid "Enable Text to Speech for Objects, Options, and the Bible Quote"
 msgstr "??????, ??????, ?????? ???????????? ?????? TTS??? ?????????"
 
+#: engines/eem/metaengine.cpp:38
+#, fuzzy
+#| msgid "Highlight items"
+msgid "Hide the highlight boxes"
+msgstr "????????? ?????? ??????"
+
+#: engines/eem/metaengine.cpp:39
+msgid "Hide the boxes that highlight searchable clue locations."
+msgstr ""
+
+#: engines/eem/metaengine.cpp:49
+msgid "Better fit for dialog balloons"
+msgstr ""
+
+#: engines/eem/metaengine.cpp:50
+msgid ""
+"Pick a smaller speech-bubble graphic when the wrapped text doesn't need the "
+"original size."
+msgstr ""
+
+#: engines/eem/metaengine.cpp:61
+#, fuzzy
+#| msgid "Skip speech"
+msgid "Skip repeated cases"
+msgstr "??? ????????????"
+
+#: engines/eem/metaengine.cpp:62
+msgid "Skip all Book 2 cases and jump to Book 3 once Book 1 is complete."
+msgstr ""
+
+#: engines/eem/metaengine.cpp:72 engines/hypno/metaengine.cpp:81
+msgid "Enable restored content"
+msgstr "????????? ????????? ?????????"
+
+#: engines/eem/metaengine.cpp:73
+msgid ""
+"Restore floppy release extras in the CD version, including pre-scrapbook "
+"conversations and first-try badges."
+msgstr ""
+
 #: engines/efh/metaengine.cpp:235
 msgid "Select option 1"
 msgstr "?????? 1 ??????"
@@ -10146,10 +10186,6 @@ msgstr "?????? ?????? ?????? ??????"
 msgid "All levels will be available to play."
 msgstr "?????? ???????????? ???????????? ??? ????????????."
 
-#: engines/hypno/metaengine.cpp:81
-msgid "Enable restored content"
-msgstr "????????? ????????? ?????????"
-
 #: engines/hypno/metaengine.cpp:82
 msgid "Add additional content that is not enabled the original implementation."
 msgstr "?????? ???????????? ??????????????? ?????? ?????? ???????????? ???????????????."
diff --git a/po/nb_NO.po b/po/nb_NO.po
index 42579199e3c..27df739d374 100644
--- a/po/nb_NO.po
+++ b/po/nb_NO.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-21 16:24+0000\n"
+"POT-Creation-Date: 2026-06-21 22:22+0000\n"
 "PO-Revision-Date: 2026-02-07 15:22+0000\n"
 "Last-Translator: Einar Johan Tr??an S??m??en <einarjohants at gmail.com>\n"
 "Language-Team: Norwegian Bokm??l <https://translations.scummvm.org/projects/"
@@ -9242,6 +9242,46 @@ msgstr "Vis grafikk med spillets lyse palett"
 msgid "Enable Text to Speech for Objects, Options, and the Bible Quote"
 msgstr "Aktiver tekst til tale for gjenstander, innstillinger og bibelsitat"
 
+#: engines/eem/metaengine.cpp:38
+#, fuzzy
+#| msgid "Highlight next dialogue"
+msgid "Hide the highlight boxes"
+msgstr "Merk neste dialog"
+
+#: engines/eem/metaengine.cpp:39
+msgid "Hide the boxes that highlight searchable clue locations."
+msgstr ""
+
+#: engines/eem/metaengine.cpp:49
+msgid "Better fit for dialog balloons"
+msgstr ""
+
+#: engines/eem/metaengine.cpp:50
+msgid ""
+"Pick a smaller speech-bubble graphic when the wrapped text doesn't need the "
+"original size."
+msgstr ""
+
+#: engines/eem/metaengine.cpp:61
+#, fuzzy
+#| msgid "Speech"
+msgid "Skip repeated cases"
+msgstr "Tale"
+
+#: engines/eem/metaengine.cpp:62
+msgid "Skip all Book 2 cases and jump to Book 3 once Book 1 is complete."
+msgstr ""
+
+#: engines/eem/metaengine.cpp:72 engines/hypno/metaengine.cpp:81
+msgid "Enable restored content"
+msgstr "Bruk restaurert innhold"
+
+#: engines/eem/metaengine.cpp:73
+msgid ""
+"Restore floppy release extras in the CD version, including pre-scrapbook "
+"conversations and first-try badges."
+msgstr ""
+
 #: engines/efh/metaengine.cpp:235
 #, fuzzy
 #| msgid "Select Group by"
@@ -11020,10 +11060,6 @@ msgstr "L??ser opp alle niv??er"
 msgid "All levels will be available to play."
 msgstr "Alle niv??er ??pnes for spilling."
 
-#: engines/hypno/metaengine.cpp:81
-msgid "Enable restored content"
-msgstr "Bruk restaurert innhold"
-
 #: engines/hypno/metaengine.cpp:82
 msgid "Add additional content that is not enabled the original implementation."
 msgstr ""
diff --git a/po/nl_NL.po b/po/nl_NL.po
index edca59f2f78..7993a2d28cf 100644
--- a/po/nl_NL.po
+++ b/po/nl_NL.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.9.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-21 16:24+0000\n"
+"POT-Creation-Date: 2026-06-21 22:22+0000\n"
 "PO-Revision-Date: 2026-02-11 14:23+0000\n"
 "Last-Translator: SecularSteve <fairfull.playing at gmail.com>\n"
 "Language-Team: Dutch <https://translations.scummvm.org/projects/scummvm/"
@@ -8764,6 +8764,46 @@ msgstr "Gebruik het heldere palet van het spel voor grafische zaken"
 msgid "Enable Text to Speech for Objects, Options, and the Bible Quote"
 msgstr "Zet spreek tekst aan voor Objecten, Opties en Bijbel Citaat"
 
+#: engines/eem/metaengine.cpp:38
+#, fuzzy
+#| msgid "Highlight items"
+msgid "Hide the highlight boxes"
+msgstr "Items markeren"
+
+#: engines/eem/metaengine.cpp:39
+msgid "Hide the boxes that highlight searchable clue locations."
+msgstr ""
+
+#: engines/eem/metaengine.cpp:49
+msgid "Better fit for dialog balloons"
+msgstr ""
+
+#: engines/eem/metaengine.cpp:50
+msgid ""
+"Pick a smaller speech-bubble graphic when the wrapped text doesn't need the "
+"original size."
+msgstr ""
+
+#: engines/eem/metaengine.cpp:61
+#, fuzzy
+#| msgid "Skip speech"
+msgid "Skip repeated cases"
+msgstr "Toespraak overslaan"
+
+#: engines/eem/metaengine.cpp:62
+msgid "Skip all Book 2 cases and jump to Book 3 once Book 1 is complete."
+msgstr ""
+
+#: engines/eem/metaengine.cpp:72 engines/hypno/metaengine.cpp:81
+msgid "Enable restored content"
+msgstr "Gerestaureerde inhoud inschakelen"
+
+#: engines/eem/metaengine.cpp:73
+msgid ""
+"Restore floppy release extras in the CD version, including pre-scrapbook "
+"conversations and first-try badges."
+msgstr ""
+
 #: engines/efh/metaengine.cpp:235
 msgid "Select option 1"
 msgstr "Selecteer optie 1"
@@ -10407,10 +10447,6 @@ msgstr "Geef alle levels vrij"
 msgid "All levels will be available to play."
 msgstr "Alle levels kunnen dan gespeeld worden."
 
-#: engines/hypno/metaengine.cpp:81
-msgid "Enable restored content"
-msgstr "Gerestaureerde inhoud inschakelen"
-
 #: engines/hypno/metaengine.cpp:82
 msgid "Add additional content that is not enabled the original implementation."
 msgstr ""
diff --git a/po/nn_NO.po b/po/nn_NO.po
index 201b267f948..92e3cdb0cca 100644
--- a/po/nn_NO.po
+++ b/po/nn_NO.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-21 16:24+0000\n"
+"POT-Creation-Date: 2026-06-21 22:22+0000\n"
 "PO-Revision-Date: 2023-12-14 14:28+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Norwegian Nynorsk <https://translations.scummvm.org/projects/"
@@ -8582,6 +8582,47 @@ msgstr ""
 msgid "Enable Text to Speech for Objects, Options, and the Bible Quote"
 msgstr ""
 
+#: engines/eem/metaengine.cpp:38
+#, fuzzy
+#| msgid "Highlight next dialogue"
+msgid "Hide the highlight boxes"
+msgstr "Merk neste dialog"
+
+#: engines/eem/metaengine.cpp:39
+msgid "Hide the boxes that highlight searchable clue locations."
+msgstr ""
+
+#: engines/eem/metaengine.cpp:49
+msgid "Better fit for dialog balloons"
+msgstr ""
+
+#: engines/eem/metaengine.cpp:50
+msgid ""
+"Pick a smaller speech-bubble graphic when the wrapped text doesn't need the "
+"original size."
+msgstr ""
+
+#: engines/eem/metaengine.cpp:61
+#, fuzzy
+#| msgid "Speech"
+msgid "Skip repeated cases"
+msgstr "Tale"
+
+#: engines/eem/metaengine.cpp:62
+msgid "Skip all Book 2 cases and jump to Book 3 once Book 1 is complete."
+msgstr ""
+
+#: engines/eem/metaengine.cpp:72 engines/hypno/metaengine.cpp:81
+#, fuzzy
+msgid "Enable restored content"
+msgstr "Sl?? p?? flytande peikarar"
+
+#: engines/eem/metaengine.cpp:73
+msgid ""
+"Restore floppy release extras in the CD version, including pre-scrapbook "
+"conversations and first-try badges."
+msgstr ""
+
 #: engines/efh/metaengine.cpp:235
 #, fuzzy
 #| msgid "Select Group by"
@@ -10335,11 +10376,6 @@ msgstr ""
 msgid "All levels will be available to play."
 msgstr ""
 
-#: engines/hypno/metaengine.cpp:81
-#, fuzzy
-msgid "Enable restored content"
-msgstr "Sl?? p?? flytande peikarar"
-
 #: engines/hypno/metaengine.cpp:82
 msgid "Add additional content that is not enabled the original implementation."
 msgstr ""
diff --git a/po/pl_PL.po b/po/pl_PL.po
index 5d0e8e0796b..4a35b618290 100644
--- a/po/pl_PL.po
+++ b/po/pl_PL.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-21 16:24+0000\n"
+"POT-Creation-Date: 2026-06-21 22:22+0000\n"
 "PO-Revision-Date: 2026-04-17 00:22+0000\n"
 "Last-Translator: SkiffPL <skiff at windowslive.com>\n"
 "Language-Team: Polish <https://translations.scummvm.org/projects/scummvm/"
@@ -8776,6 +8776,46 @@ msgstr "Wy??wietlaj grafik?? za pomoc?? jasnej palety gry"
 msgid "Enable Text to Speech for Objects, Options, and the Bible Quote"
 msgstr "W????cz zamian?? tekstu na mow?? dla obiekt??w, opcji i cytatu biblijnego"
 
+#: engines/eem/metaengine.cpp:38
+#, fuzzy
+#| msgid "Highlight items"
+msgid "Hide the highlight boxes"
+msgstr "Pod??wietl elementy"
+
+#: engines/eem/metaengine.cpp:39
+msgid "Hide the boxes that highlight searchable clue locations."
+msgstr ""
+
+#: engines/eem/metaengine.cpp:49
+msgid "Better fit for dialog balloons"
+msgstr ""
+
+#: engines/eem/metaengine.cpp:50
+msgid ""
+"Pick a smaller speech-bubble graphic when the wrapped text doesn't need the "
+"original size."
+msgstr ""
+
+#: engines/eem/metaengine.cpp:61
+#, fuzzy
+#| msgid "Skip speech"
+msgid "Skip repeated cases"
+msgstr "Pomi?? rozmow??"
+
+#: engines/eem/metaengine.cpp:62
+msgid "Skip all Book 2 cases and jump to Book 3 once Book 1 is complete."
+msgstr ""
+
+#: engines/eem/metaengine.cpp:72 engines/hypno/metaengine.cpp:81
+msgid "Enable restored content"
+msgstr "W????cz przywr??con?? zawarto????"
+
+#: engines/eem/metaengine.cpp:73
+msgid ""
+"Restore floppy release extras in the CD version, including pre-scrapbook "
+"conversations and first-try badges."
+msgstr ""
+
 #: engines/efh/metaengine.cpp:235
 #, fuzzy
 #| msgid "Select actor 1"
@@ -10516,10 +10556,6 @@ msgstr "Odblokuj wszystkie poziomy"
 msgid "All levels will be available to play."
 msgstr "Wszystkie poziomy gry b??d?? dost??pne."
 
-#: engines/hypno/metaengine.cpp:81
-msgid "Enable restored content"
-msgstr "W????cz przywr??con?? zawarto????"
-
 #: engines/hypno/metaengine.cpp:82
 msgid "Add additional content that is not enabled the original implementation."
 msgstr ""
diff --git a/po/pt_BR.po b/po/pt_BR.po
index 33fb2b0602f..ee864a518f8 100644
--- a/po/pt_BR.po
+++ b/po/pt_BR.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-21 16:24+0000\n"
+"POT-Creation-Date: 2026-06-21 22:22+0000\n"
 "PO-Revision-Date: 2026-06-17 22:10+0000\n"
 "Last-Translator: Marcel Souza Lemes <marcosoutsider at gmail.com>\n"
 "Language-Team: Portuguese (Brazil) <https://translations.scummvm.org/"
@@ -9229,6 +9229,46 @@ msgstr "Exibe gr??ficos utilizando a paleta clara do jogo"
 msgid "Enable Text to Speech for Objects, Options, and the Bible Quote"
 msgstr "Habilita Texto para Fala para Objetos, Op????es e Cita????es da B??blia"
 
+#: engines/eem/metaengine.cpp:38
+#, fuzzy
+#| msgid "Highlight next dialogue"
+msgid "Hide the highlight boxes"
+msgstr "Destacar pr??ximo di??logo"
+
+#: engines/eem/metaengine.cpp:39
+msgid "Hide the boxes that highlight searchable clue locations."
+msgstr ""
+
+#: engines/eem/metaengine.cpp:49
+msgid "Better fit for dialog balloons"
+msgstr ""
+
+#: engines/eem/metaengine.cpp:50
+msgid ""
+"Pick a smaller speech-bubble graphic when the wrapped text doesn't need the "
+"original size."
+msgstr ""
+
+#: engines/eem/metaengine.cpp:61
+#, fuzzy
+#| msgid "Skip speech"
+msgid "Skip repeated cases"
+msgstr "Pular fala"
+
+#: engines/eem/metaengine.cpp:62
+msgid "Skip all Book 2 cases and jump to Book 3 once Book 1 is complete."
+msgstr ""
+
+#: engines/eem/metaengine.cpp:72 engines/hypno/metaengine.cpp:81
+msgid "Enable restored content"
+msgstr "Habilitar conte??do restaurado"
+
+#: engines/eem/metaengine.cpp:73
+msgid ""
+"Restore floppy release extras in the CD version, including pre-scrapbook "
+"conversations and first-try badges."
+msgstr ""
+
 #: engines/efh/metaengine.cpp:235
 #, fuzzy
 #| msgid "Select Actor 1"
@@ -11015,10 +11055,6 @@ msgstr "Desbloquear todos os n??veis"
 msgid "All levels will be available to play."
 msgstr "Todos os n??veis estar??o dispon??veis para jogar."
 
-#: engines/hypno/metaengine.cpp:81
-msgid "Enable restored content"
-msgstr "Habilitar conte??do restaurado"
-
 #: engines/hypno/metaengine.cpp:82
 msgid "Add additional content that is not enabled the original implementation."
 msgstr ""
diff --git a/po/pt_PT.po b/po/pt_PT.po
index ee616be3e3a..a6946bf6ef8 100644
--- a/po/pt_PT.po
+++ b/po/pt_PT.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.10.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-21 16:24+0000\n"
+"POT-Creation-Date: 2026-06-21 22:22+0000\n"
 "PO-Revision-Date: 2022-06-05 14:09+0000\n"
 "Last-Translator: Daniel <supsuper at gmail.com>\n"
 "Language-Team: Portuguese (Portugal) <https://translations.scummvm.org/"
@@ -8626,6 +8626,45 @@ msgstr ""
 msgid "Enable Text to Speech for Objects, Options, and the Bible Quote"
 msgstr ""
 
+#: engines/eem/metaengine.cpp:38
+msgid "Hide the highlight boxes"
+msgstr ""
+
+#: engines/eem/metaengine.cpp:39
+msgid "Hide the boxes that highlight searchable clue locations."
+msgstr ""
+
+#: engines/eem/metaengine.cpp:49
+msgid "Better fit for dialog balloons"
+msgstr ""
+
+#: engines/eem/metaengine.cpp:50
+msgid ""
+"Pick a smaller speech-bubble graphic when the wrapped text doesn't need the "
+"original size."
+msgstr ""
+
+#: engines/eem/metaengine.cpp:61
+#, fuzzy
+#| msgid "Speech"
+msgid "Skip repeated cases"
+msgstr "Di??logo"
+
+#: engines/eem/metaengine.cpp:62
+msgid "Skip all Book 2 cases and jump to Book 3 once Book 1 is complete."
+msgstr ""
+
+#: engines/eem/metaengine.cpp:72 engines/hypno/metaengine.cpp:81
+#, fuzzy
+msgid "Enable restored content"
+msgstr "Activar censura de conte??do"
+
+#: engines/eem/metaengine.cpp:73
+msgid ""
+"Restore floppy release extras in the CD version, including pre-scrapbook "
+"conversations and first-try badges."
+msgstr ""
+
 #: engines/efh/metaengine.cpp:235
 #, fuzzy
 #| msgid "Select Group by"
@@ -10385,11 +10424,6 @@ msgstr ""
 msgid "All levels will be available to play."
 msgstr ""
 
-#: engines/hypno/metaengine.cpp:81
-#, fuzzy
-msgid "Enable restored content"
-msgstr "Activar censura de conte??do"
-
 #: engines/hypno/metaengine.cpp:82
 msgid "Add additional content that is not enabled the original implementation."
 msgstr ""
diff --git a/po/ro.po b/po/ro.po
index 9f2962bb59a..09ce29f1ee9 100644
--- a/po/ro.po
+++ b/po/ro.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.10.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-21 16:24+0000\n"
+"POT-Creation-Date: 2026-06-21 22:22+0000\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: Automatically generated\n"
 "Language-Team: none\n"
@@ -7910,6 +7910,42 @@ msgstr ""
 msgid "Enable Text to Speech for Objects, Options, and the Bible Quote"
 msgstr ""
 
+#: engines/eem/metaengine.cpp:38
+msgid "Hide the highlight boxes"
+msgstr ""
+
+#: engines/eem/metaengine.cpp:39
+msgid "Hide the boxes that highlight searchable clue locations."
+msgstr ""
+
+#: engines/eem/metaengine.cpp:49
+msgid "Better fit for dialog balloons"
+msgstr ""
+
+#: engines/eem/metaengine.cpp:50
+msgid ""
+"Pick a smaller speech-bubble graphic when the wrapped text doesn't need the "
+"original size."
+msgstr ""
+
+#: engines/eem/metaengine.cpp:61
+msgid "Skip repeated cases"
+msgstr ""
+
+#: engines/eem/metaengine.cpp:62
+msgid "Skip all Book 2 cases and jump to Book 3 once Book 1 is complete."
+msgstr ""
+
+#: engines/eem/metaengine.cpp:72 engines/hypno/metaengine.cpp:81
+msgid "Enable restored content"
+msgstr ""
+
+#: engines/eem/metaengine.cpp:73
+msgid ""
+"Restore floppy release extras in the CD version, including pre-scrapbook "
+"conversations and first-try badges."
+msgstr ""
+
 #: engines/efh/metaengine.cpp:235
 msgid "Select option 1"
 msgstr ""
@@ -9407,10 +9443,6 @@ msgstr ""
 msgid "All levels will be available to play."
 msgstr ""
 
-#: engines/hypno/metaengine.cpp:81
-msgid "Enable restored content"
-msgstr ""
-
 #: engines/hypno/metaengine.cpp:82
 msgid "Add additional content that is not enabled the original implementation."
 msgstr ""
diff --git a/po/ru_RU.po b/po/ru_RU.po
index d197b1f4080..3e568279bad 100644
--- a/po/ru_RU.po
+++ b/po/ru_RU.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.8.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-21 16:24+0000\n"
+"POT-Creation-Date: 2026-06-21 22:22+0000\n"
 "PO-Revision-Date: 2026-06-20 15:03+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Russian <https://translations.scummvm.org/projects/scummvm/"
@@ -8732,6 +8732,46 @@ msgstr "???????????? ?????????????? ?? ???????????????????????????? ?????????? 
 msgid "Enable Text to Speech for Objects, Options, and the Bible Quote"
 msgstr "???????????????? ?????????????? ???????????? ?????? ????????????????, ?????????? ?? ???????????? ???? ????????????"
 
+#: engines/eem/metaengine.cpp:38
+#, fuzzy
+#| msgid "Highlight items"
+msgid "Hide the highlight boxes"
+msgstr "???????????????????? ????????????????"
+
+#: engines/eem/metaengine.cpp:39
+msgid "Hide the boxes that highlight searchable clue locations."
+msgstr ""
+
+#: engines/eem/metaengine.cpp:49
+msgid "Better fit for dialog balloons"
+msgstr ""
+
+#: engines/eem/metaengine.cpp:50
+msgid ""
+"Pick a smaller speech-bubble graphic when the wrapped text doesn't need the "
+"original size."
+msgstr ""
+
+#: engines/eem/metaengine.cpp:61
+#, fuzzy
+#| msgid "Skip speech"
+msgid "Skip repeated cases"
+msgstr "???????????????????? ????????"
+
+#: engines/eem/metaengine.cpp:62
+msgid "Skip all Book 2 cases and jump to Book 3 once Book 1 is complete."
+msgstr ""
+
+#: engines/eem/metaengine.cpp:72 engines/hypno/metaengine.cpp:81
+msgid "Enable restored content"
+msgstr "???????????????? ?????????????????????????????? ??????????????"
+
+#: engines/eem/metaengine.cpp:73
+msgid ""
+"Restore floppy release extras in the CD version, including pre-scrapbook "
+"conversations and first-try badges."
+msgstr ""
+
 #: engines/efh/metaengine.cpp:235
 msgid "Select option 1"
 msgstr "?????????????? ?????????????? 1"
@@ -10280,10 +10320,6 @@ msgstr "???????????????????????????? ?????? ????????????"
 msgid "All levels will be available to play."
 msgstr "?????? ???????????? ?????????? ???????????????? ?????? ????????."
 
-#: engines/hypno/metaengine.cpp:81
-msgid "Enable restored content"
-msgstr "???????????????? ?????????????????????????????? ??????????????"
-
 #: engines/hypno/metaengine.cpp:82
 msgid "Add additional content that is not enabled the original implementation."
 msgstr "???????????????? ????????????, ?????????????? ?????? ???????????? ?? ???????????????????????? ????????."
diff --git a/po/sv_SE.po b/po/sv_SE.po
index a6aae47abb2..bdddf1426dd 100644
--- a/po/sv_SE.po
+++ b/po/sv_SE.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.5.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-21 16:24+0000\n"
+"POT-Creation-Date: 2026-06-21 22:22+0000\n"
 "PO-Revision-Date: 2026-04-19 13:22+0000\n"
 "Last-Translator: Daniel Nylander <po at danielnylander.se>\n"
 "Language-Team: Swedish <https://translations.scummvm.org/projects/scummvm/"
@@ -8719,6 +8719,46 @@ msgstr "Visa grafik med spelets ljusa palett"
 msgid "Enable Text to Speech for Objects, Options, and the Bible Quote"
 msgstr "Aktivera text till tal f??r objekt, alternativ och bibelcitat"
 
+#: engines/eem/metaengine.cpp:38
+#, fuzzy
+#| msgid "Highlight items"
+msgid "Hide the highlight boxes"
+msgstr "Markera objekt"
+
+#: engines/eem/metaengine.cpp:39
+msgid "Hide the boxes that highlight searchable clue locations."
+msgstr ""
+
+#: engines/eem/metaengine.cpp:49
+msgid "Better fit for dialog balloons"
+msgstr ""
+
+#: engines/eem/metaengine.cpp:50
+msgid ""
+"Pick a smaller speech-bubble graphic when the wrapped text doesn't need the "
+"original size."
+msgstr ""
+
+#: engines/eem/metaengine.cpp:61
+#, fuzzy
+#| msgid "Skip speech"
+msgid "Skip repeated cases"
+msgstr "Hoppa ??ver tal"
+
+#: engines/eem/metaengine.cpp:62
+msgid "Skip all Book 2 cases and jump to Book 3 once Book 1 is complete."
+msgstr ""
+
+#: engines/eem/metaengine.cpp:72 engines/hypno/metaengine.cpp:81
+msgid "Enable restored content"
+msgstr "Aktivera ??terst??llt inneh??ll"
+
+#: engines/eem/metaengine.cpp:73
+msgid ""
+"Restore floppy release extras in the CD version, including pre-scrapbook "
+"conversations and first-try badges."
+msgstr ""
+
 #: engines/efh/metaengine.cpp:235
 msgid "Select option 1"
 msgstr "V??lj alternativ 1"
@@ -10327,10 +10367,6 @@ msgstr "L??s upp alla niv??er"
 msgid "All levels will be available to play."
 msgstr "Alla niv??er kommer att finnas tillg??ngliga att spela."
 
-#: engines/hypno/metaengine.cpp:81
-msgid "Enable restored content"
-msgstr "Aktivera ??terst??llt inneh??ll"
-
 #: engines/hypno/metaengine.cpp:82
 msgid "Add additional content that is not enabled the original implementation."
 msgstr ""
diff --git a/po/tr.po b/po/tr.po
index e30f1b12a47..e2328dc3c79 100644
--- a/po/tr.po
+++ b/po/tr.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-21 16:24+0000\n"
+"POT-Creation-Date: 2026-06-21 22:22+0000\n"
 "PO-Revision-Date: 2024-10-10 12:33+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Turkish <https://translations.scummvm.org/projects/scummvm/"
@@ -8415,6 +8415,45 @@ msgstr ""
 msgid "Enable Text to Speech for Objects, Options, and the Bible Quote"
 msgstr ""
 
+#: engines/eem/metaengine.cpp:38
+msgid "Hide the highlight boxes"
+msgstr ""
+
+#: engines/eem/metaengine.cpp:39
+msgid "Hide the boxes that highlight searchable clue locations."
+msgstr ""
+
+#: engines/eem/metaengine.cpp:49
+msgid "Better fit for dialog balloons"
+msgstr ""
+
+#: engines/eem/metaengine.cpp:50
+msgid ""
+"Pick a smaller speech-bubble graphic when the wrapped text doesn't need the "
+"original size."
+msgstr ""
+
+#: engines/eem/metaengine.cpp:61
+#, fuzzy
+#| msgid "Speech"
+msgid "Skip repeated cases"
+msgstr "Konu??ma:"
+
+#: engines/eem/metaengine.cpp:62
+msgid "Skip all Book 2 cases and jump to Book 3 once Book 1 is complete."
+msgstr ""
+
+#: engines/eem/metaengine.cpp:72 engines/hypno/metaengine.cpp:81
+#, fuzzy
+msgid "Enable restored content"
+msgstr "Depolamay?? aktif edin"
+
+#: engines/eem/metaengine.cpp:73
+msgid ""
+"Restore floppy release extras in the CD version, including pre-scrapbook "
+"conversations and first-try badges."
+msgstr ""
+
 #: engines/efh/metaengine.cpp:235
 #, fuzzy
 msgid "Select option 1"
@@ -10062,11 +10101,6 @@ msgstr ""
 msgid "All levels will be available to play."
 msgstr ""
 
-#: engines/hypno/metaengine.cpp:81
-#, fuzzy
-msgid "Enable restored content"
-msgstr "Depolamay?? aktif edin"
-
 #: engines/hypno/metaengine.cpp:82
 msgid "Add additional content that is not enabled the original implementation."
 msgstr ""
diff --git a/po/uk_UA.po b/po/uk_UA.po
index 3c8ac57f46e..4e9161ca932 100644
--- a/po/uk_UA.po
+++ b/po/uk_UA.po
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.9.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-21 16:24+0000\n"
+"POT-Creation-Date: 2026-06-21 22:22+0000\n"
 "PO-Revision-Date: 2026-06-20 14:56+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Ukrainian <https://translations.scummvm.org/projects/scummvm/"
@@ -8709,6 +8709,46 @@ msgstr "???????????????????????? ?????????????? ?? ?????????????????????????? ??
 msgid "Enable Text to Speech for Objects, Options, and the Bible Quote"
 msgstr "?????????????????? ?????????????????? ???????? ?????? ????'??????????, ?????????????????????? ???? ?????????????????? ????????????"
 
+#: engines/eem/metaengine.cpp:38
+#, fuzzy
+#| msgid "Highlight items"
+msgid "Hide the highlight boxes"
+msgstr "???????????????? ????????????????"
+
+#: engines/eem/metaengine.cpp:39
+msgid "Hide the boxes that highlight searchable clue locations."
+msgstr ""
+
+#: engines/eem/metaengine.cpp:49
+msgid "Better fit for dialog balloons"
+msgstr ""
+
+#: engines/eem/metaengine.cpp:50
+msgid ""
+"Pick a smaller speech-bubble graphic when the wrapped text doesn't need the "
+"original size."
+msgstr ""
+
+#: engines/eem/metaengine.cpp:61
+#, fuzzy
+#| msgid "Skip speech"
+msgid "Skip repeated cases"
+msgstr "???????????????????? ??????????????"
+
+#: engines/eem/metaengine.cpp:62
+msgid "Skip all Book 2 cases and jump to Book 3 once Book 1 is complete."
+msgstr ""
+
+#: engines/eem/metaengine.cpp:72 engines/hypno/metaengine.cpp:81
+msgid "Enable restored content"
+msgstr "?????????????????? ?????????????????????? ??????????????"
+
+#: engines/eem/metaengine.cpp:73
+msgid ""
+"Restore floppy release extras in the CD version, including pre-scrapbook "
+"conversations and first-try badges."
+msgstr ""
+
 #: engines/efh/metaengine.cpp:235
 msgid "Select option 1"
 msgstr "?????????????? ?????????? 1"
@@ -10257,10 +10297,6 @@ msgstr "???????????????????????? ?????? ??????????"
 msgid "All levels will be available to play."
 msgstr "?????? ?????????? ???????????? ???????????????? ?????? ??????."
 
-#: engines/hypno/metaengine.cpp:81
-msgid "Enable restored content"
-msgstr "?????????????????? ?????????????????????? ??????????????"
-
 #: engines/hypno/metaengine.cpp:82
 msgid "Add additional content that is not enabled the original implementation."
 msgstr "???????????????????? ??????????????, ???????? ???????? ???????????????? ?? ???????????????????????? ??????."
diff --git a/po/zh.po b/po/zh.po
index 17088afed2b..aa130636f1f 100644
--- a/po/zh.po
+++ b/po/zh.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-21 16:24+0000\n"
+"POT-Creation-Date: 2026-06-21 22:22+0000\n"
 "PO-Revision-Date: 2024-05-14 12:42+0000\n"
 "Last-Translator: ?????? <clonewith at qq.com>\n"
 "Language-Team: Chinese <https://translations.scummvm.org/projects/scummvm/"
@@ -8199,6 +8199,44 @@ msgstr ""
 msgid "Enable Text to Speech for Objects, Options, and the Bible Quote"
 msgstr ""
 
+#: engines/eem/metaengine.cpp:38
+msgid "Hide the highlight boxes"
+msgstr ""
+
+#: engines/eem/metaengine.cpp:39
+msgid "Hide the boxes that highlight searchable clue locations."
+msgstr ""
+
+#: engines/eem/metaengine.cpp:49
+msgid "Better fit for dialog balloons"
+msgstr ""
+
+#: engines/eem/metaengine.cpp:50
+msgid ""
+"Pick a smaller speech-bubble graphic when the wrapped text doesn't need the "
+"original size."
+msgstr ""
+
+#: engines/eem/metaengine.cpp:61
+#, fuzzy
+#| msgid "Speech"
+msgid "Skip repeated cases"
+msgstr "??????"
+
+#: engines/eem/metaengine.cpp:62
+msgid "Skip all Book 2 cases and jump to Book 3 once Book 1 is complete."
+msgstr ""
+
+#: engines/eem/metaengine.cpp:72 engines/hypno/metaengine.cpp:81
+msgid "Enable restored content"
+msgstr ""
+
+#: engines/eem/metaengine.cpp:73
+msgid ""
+"Restore floppy release extras in the CD version, including pre-scrapbook "
+"conversations and first-try badges."
+msgstr ""
+
 #: engines/efh/metaengine.cpp:235
 #, fuzzy
 #| msgid "Select Group by"
@@ -9877,10 +9915,6 @@ msgstr ""
 msgid "All levels will be available to play."
 msgstr ""
 
-#: engines/hypno/metaengine.cpp:81
-msgid "Enable restored content"
-msgstr ""
-
 #: engines/hypno/metaengine.cpp:82
 msgid "Add additional content that is not enabled the original implementation."
 msgstr ""
diff --git a/po/zh_Hans.po b/po/zh_Hans.po
index 59c6d906aa7..b22cd2a8af1 100644
--- a/po/zh_Hans.po
+++ b/po/zh_Hans.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.9.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-21 16:24+0000\n"
+"POT-Creation-Date: 2026-06-21 22:22+0000\n"
 "PO-Revision-Date: 2024-12-01 22:18+0000\n"
 "Last-Translator: ?????? <clonewith at qq.com>\n"
 "Language-Team: Chinese (Simplified Han script) <https://"
@@ -8929,6 +8929,46 @@ msgstr "???????????????????????????????????????"
 msgid "Enable Text to Speech for Objects, Options, and the Bible Quote"
 msgstr "????????????????????????????????????????????????????????????"
 
+#: engines/eem/metaengine.cpp:38
+#, fuzzy
+#| msgid "Highlight next dialogue"
+msgid "Hide the highlight boxes"
+msgstr "????????????????????????"
+
+#: engines/eem/metaengine.cpp:39
+msgid "Hide the boxes that highlight searchable clue locations."
+msgstr ""
+
+#: engines/eem/metaengine.cpp:49
+msgid "Better fit for dialog balloons"
+msgstr ""
+
+#: engines/eem/metaengine.cpp:50
+msgid ""
+"Pick a smaller speech-bubble graphic when the wrapped text doesn't need the "
+"original size."
+msgstr ""
+
+#: engines/eem/metaengine.cpp:61
+#, fuzzy
+#| msgid "Skip speech"
+msgid "Skip repeated cases"
+msgstr "????????????"
+
+#: engines/eem/metaengine.cpp:62
+msgid "Skip all Book 2 cases and jump to Book 3 once Book 1 is complete."
+msgstr ""
+
+#: engines/eem/metaengine.cpp:72 engines/hypno/metaengine.cpp:81
+msgid "Enable restored content"
+msgstr "??????????????????"
+
+#: engines/eem/metaengine.cpp:73
+msgid ""
+"Restore floppy release extras in the CD version, including pre-scrapbook "
+"conversations and first-try badges."
+msgstr ""
+
 #: engines/efh/metaengine.cpp:235
 #, fuzzy
 #| msgid "Select Actor 1"
@@ -10695,10 +10735,6 @@ msgstr "??????????????????"
 msgid "All levels will be available to play."
 msgstr "??????????????????????????????"
 
-#: engines/hypno/metaengine.cpp:81
-msgid "Enable restored content"
-msgstr "??????????????????"
-
 #: engines/hypno/metaengine.cpp:82
 msgid "Add additional content that is not enabled the original implementation."
 msgstr "??????????????????????????????????????????"
diff --git a/po/zh_Hant.po b/po/zh_Hant.po
index e6016f46e84..cda9a7527c0 100644
--- a/po/zh_Hant.po
+++ b/po/zh_Hant.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.9.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-21 16:24+0000\n"
+"POT-Creation-Date: 2026-06-21 22:22+0000\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: Automatically generated\n"
 "Language-Team: none\n"
@@ -7911,6 +7911,42 @@ msgstr ""
 msgid "Enable Text to Speech for Objects, Options, and the Bible Quote"
 msgstr ""
 
+#: engines/eem/metaengine.cpp:38
+msgid "Hide the highlight boxes"
+msgstr ""
+
+#: engines/eem/metaengine.cpp:39
+msgid "Hide the boxes that highlight searchable clue locations."
+msgstr ""
+
+#: engines/eem/metaengine.cpp:49
+msgid "Better fit for dialog balloons"
+msgstr ""
+
+#: engines/eem/metaengine.cpp:50
+msgid ""
+"Pick a smaller speech-bubble graphic when the wrapped text doesn't need the "
+"original size."
+msgstr ""
+
+#: engines/eem/metaengine.cpp:61
+msgid "Skip repeated cases"
+msgstr ""
+
+#: engines/eem/metaengine.cpp:62
+msgid "Skip all Book 2 cases and jump to Book 3 once Book 1 is complete."
+msgstr ""
+
+#: engines/eem/metaengine.cpp:72 engines/hypno/metaengine.cpp:81
+msgid "Enable restored content"
+msgstr ""
+
+#: engines/eem/metaengine.cpp:73
+msgid ""
+"Restore floppy release extras in the CD version, including pre-scrapbook "
+"conversations and first-try badges."
+msgstr ""
+
 #: engines/efh/metaengine.cpp:235
 msgid "Select option 1"
 msgstr ""
@@ -9408,10 +9444,6 @@ msgstr ""
 msgid "All levels will be available to play."
 msgstr ""
 
-#: engines/hypno/metaengine.cpp:81
-msgid "Enable restored content"
-msgstr ""
-
 #: engines/hypno/metaengine.cpp:82
 msgid "Add additional content that is not enabled the original implementation."
 msgstr ""



From noreply at scummvm.org  Sun Jun 21 22:23:44 2026
From: noreply at scummvm.org (sev-)
Date: Sun, 21 Jun 2026 22:23:44 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 111cc673fc7edb49681f4eddd0b2c1cd26b6f3e8
Message-ID: <20260621222344.DEEDD7005CE@ron.scummvm.net>

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

Summary:
9c9bc8afce EEM: initial code with detection
c271f98d82 EEM: image loading and first intro image
f6a6833108 EEM: basic per case flow, with initial code for font loading
8ccba0b347 EEM: correct font rendering using char to glyph table
f349111de1 EEM: use Font API to reduce code duplication
17d95660b8 EEM: completed kCharToGlyph
57dc88dafe EEM: show mouse when selecting character
84a82d5580 EEM: improved initial menu following the original implementation
17288a6014 EEM: update pointer during main menu
d3e517a575 EEM: correct ballon dialog placement
b60f6c5570 EEM: draw the big map and allow player to move between locations
3fe1b2e115 EEM: improved character animations when arriving to locations
30367c4690 EEM: zoom/overview map initial implementation
a4c4170605 EEM: load the correct sites and make sure they are labeled
83ab937183 EEM: show NPC on sites
a5cefd6871 EEM: implement clickable areas on maps
0fe0c95dfd EEM: improved NPC handling
ca825ec5b1 EEM: added more features to the PDA
b36297d2a5 EEM: polish PDA features: buttons now works correctly
7be086ee1b EEM: polish PDA features: help interface added
d075832f54 EEM: polish PDA features: acusations
2ae70bdb92 EEM: basic parners animations
30a356bc07 EEM: more parners animations, for intro and overview map
0f7bf385b7 EEM: refactored the code following the original symbols
7bb7d3b82e EEM: enforce code standards
a4dd19f31d EEM: removed lambdas
2fac0e6820 EEM: removed static and use only EEM namespace
d44daf096d EEM: implemented midi music
ffc4f5135b EEM: implemented digitalized sound
f7b94e5721 EEM: profile handling in save games
df22a74a07 EEM: improved animations and added missing UI features
9c88ee5bc9 EEM: fixed the map animation
ef46d69023 EEM: fixed missing frames in partner animations
d7268b5a91 EEM: better handling of saves
e87d591b96 EEM: remove unused screen
253e2f0bb2 EEM: improve the setup screen with actually triggers to the code relevant
ae83ff8a9f EEM: help screen in setup screen implemented
754e723072 EEM: setup screen accesible in zoom map screen
92c6607481 EEM: improved navigation on PDA
d87214f047 EEM: basic accusation flow in the PDA
3b1738c38b EEM: basic scrapbook code
b4dc4f6274 EEM: added more missing animations
256eff166c EEM: stop music/sound in certain points of the game
fb9922f887 EEM: added missing text bubbles here and there
27d73fdbfe EEM: dedicated code for accusation window
4b19ad71a3 EEM: missing background when accusation resulted in the correct answer
1e6b78b869 EEM: implement wrong accusation
ddb7949f39 EEM: fixed some issues in the win screen/newspaper
3761f15c6e EEM: fixed missing screen updates
f9a5f920da EEM: correctly load game when a mystery is solved
1131e4825f EEM: implemented screen to load mysteries from different books
c3f24c4280 EEM: unlock different scrapbooks until the end
ab8cd8380c EEM: fixed animation issue
62063295f0 EEM: unseen hotspot effect
8fa2ec9202 EEM: make sure sound effect played are correct (or don't play any)
15962d464e EEM: removed debug keys
7a9ad68ef6 EEM: use blit API
0ccaaeb7c4 EEM: initial support for floppy version
a55adf77b7 EEM: implement conversations for the floppy version
3ba1ac49ac EEM: npc conversations for the floppy version
20eba94c78 EEM: improved hints and audio in the floppy version
4992f3c704 EEM: text pagination for some conversations
fe87003b43 EEM: draw enter indicators for conversations in floppy version
bfdd2c33ec EEM: initial support for spanish release and fixes for the floppy version
9c41e30159 EEM: make sure all the clues are there
c5336a89fd EEM: setup screen for floppy screen
021009a600 EEM: correctly validate clues in floppy version
7aa9357fb6 EEM: simplify savegame handling
2b45b48e89 EEM: fix invalid state transition in the handling of the setup screen
1cf324b52d EEM: correctly save and reload viewed clues
ec75e6ec08 EEM: avoid saving slot 0
2a0ec6f7e2 EEM: bring virtual keyboard when typing name
8a575a0b4f EEM: correctly render background during enter animation
dcb37c0a03 EEM: original mouse pointer and new feature to hide highlight boxes
00c6438de3 EEM: correctly blit npc pictures
057711225d EEM: implemented the quit dialog
6ce915b5a9 EEM: implemented the (im)patience animation
3e9ba587e4 EEM: improved scrapbook code
5042edef3a EEM: highlight scrapbook interactive zones
916c34e2b2 EEM: highlight pda interactive zones
3354f1af87 EEM: handle initial clues for the CD version
7ac0106b65 EEM: improved animations
9653bf3792 EEM: do not close the game after the end of the scrapbook
5c5a2d1ccf EEM: honor the music config
a1b4c596bc EEM: added missing voices when selecting clues
6318196285 EEM: don't allow to select more than 5 clues in accusation mode
a0d9c69340 EEM: fixed regression on music handling during enter animation
33e2ff6fda EEM: implemented badge handling when a case is over
1423b3442b EEM: shiny map markers
9e532d8f96 EEM: allow to come back from zoomed map to the overview
ba0b1afbc3 EEM: mt32 music
fd806599d2 EEM: message sliding animation for some screens
6494a01769 EEM: complete handler for screen profile
a1da71fd4d EEM: missing button code to reload the current profile
d3fcc8c249 EEM: implement missing animations for floppy release
2e25ab2f46 EEM: corrected text size
e84bf0e798 EEM: keep proper track of clues in floppy version
821f9a0ba7 EEM: keep proper track of visited places in floppy version
f00c88734c EEM: blit accuse partner in floppy version
f2d1372a85 EEM: use the correct initial menu screens
2398f57221 EEM: refresh the correct part of the screen during case introductions
d00eeb0022 EEM: optional better fitted balloon dialog code
8ca7479633 EEM: only some keys will advance dialog
794eeec90c EEM: strips leading spaces
3a87e8fc48 EEM: refactory doGallery into several functions
b25891a687 EEM: openMainMenuDialog when using ESC in most of the screens
a6c48ae179 EEM: improved animation precision with correct framerate and full script table
28556038df EEM: use simpleBlitFrom
b47044e739 EEM: more simpleBlitFrom
e40f51e2dc EEM: use transBlitFrom
a2554f53bd EEM: stop conversations when you skip the last phrase
9ca6f602c4 EEM: fixed highlighting bug when the cursor was moved during conversations
b39c08a950 EEM: added palette fadeout for the intro
59304750cf EEM: reviewed and improved comments across the engine
37e6dc1ba6 EEM: enforced code standards, mostly removed one-lined statements
c3c78f3e10 EEM: removed lambdas in favor of explicit functions
b8ebea90ad EEM: correct implementation of OpenColorCycle
ad29adf750 EEM: fixed bug when picking up the phone earlier than expected
b69b12c244 EEM: fixed bug in Nancy animation
24a3e5a40c EEM: fixed global-constructors from kHappyZones
12776d655a EEM: removed the 'create new' workaround
613dac165b EEM: improved animation in the PDA
23baebb9a8 EEM: removed incorrect attribution in the headers
d901f38d66 EEM: reduced code duplication using transBlitFrom
e34c624ee4 EEM: reduced code duplication in the PDA rendering
40c2459237 EEM: some comment fixes and clarifications
0a11cfeede EEM: use named constants here and there
9cd879dd5c EEM: removed useless usage of static
f789071470 CI: temporary enable CI in github
ba065aa1a9 CI: reverted temporary enable CI in github commit
8bcc62ad5d EEM: removed duplicated definitions
79188e6bd2 EEM: renamed _ASM_Decompress to avoid confusion
2b9c008c66 EEM: add end of notes in the PDA rendering
c9a5bbc56c EEM: add an option to skip repeated cases
f2c5be745a EEM: make sure screenshots contain all the elements from sites
24fe380eed EEM: fixed invalid reference to a sound clip in practice mystery
32e5d42d20 EEM: implemented restored content from the floppy data into the CD one
02418babc4 EEM: fixes and missing code for the floppy release
558a00c546 EEM: use skip api instead of (void)
209d590655 EEM: moved cleanMysterySound after sdx/sdb declarations
a789df41d3 EEM: reduced some technical comments
c63d19aea9 EEM: remove dead code related with fallback pointer
79bf202d8c EEM: boy -> jake, girl -> jenny
8f54582d7d EEM: EEM2 proof-of-concept (intro)
ff0025ab96 EEM: Added a more complete intro and character selection for EEM2
3fb2a3ea44 EEM: load case menu is working in EEM2
a477a58ccf EEM: add more code in the partner selection screen
e69c835219 EEM: corrected palette
29bef8261a EEM: play correctly initial animation in London
24d75f3cc7 EEM: training case in London plays renders the first site
ea2453f9bc EEM: improved animation in London
dd0a8761aa EEM: added more animation from London
a5d0ea9233 EEM: mouse cursor change from London
27f48984c6 EEM: jump effect from applyClue in London
17cf0205f9 EEM: correct parner handling in London
b8f606891d EEM: implemented traveling animation in London
279d74a541 EEM: implemented edutaintment intro video of some locations in London
6ddfb2227b EEM: fix big map markers in London
a35a1dc2d5 EEM: implement player creation/loading in London
a252c9afa9 EEM: proper selection of partner in London
ad1a941360 EEM: proper case selection in London
3f0cccf90a EEM: more side effects for clues in London
66393f6f85 EEM: implement game progression in London
7ca4b9f330 EEM: added missing musical cue in London
53af04e55c EEM: added missing conversation opcodes in London
076d0f0633 EEM: improved TRAVIS UI in London
1f0ad1bf77 EEM: improved hints voices/text in London
d7a7f62671 EEM: new doPuzzle implementation for London
a029ae2241 EEM: doPuzzle highlights for London
c53f9e018e EEM: acusation workflow for London
aca47008bd EEM: doApproach fixes for London
b749d688e8 EEM: enter the name screen fixes for London
f490186308 EEM: make sure the screen is updated during animations
e9c44065c0 EEM: added missing NPC in London cases intro
2372aaf018 EEM: implement setup screen in London
44e61cbf7f EEM: fixed regression on clean background for London
a79edd41f2 EEM: missing music cue in London scrapbook
3555a18bdb EEM: reduce the verbosity of the comments accross the engine
215d297ebd EEM: enable fit dialog ballons for London
670bb5b820 EEM: fixed partner animation that was one frame longer in London
3b86f3eb9a EEM: do not render sublocations in London map
c021df15c9 EEM: added missing music in London
1234998c14 EEM: corrected palette for the water in London map
0e72f80441 EEM: autosave every time we talk or get a new clue in London
846a1b5452 EEM: show notes for each suspect in the gallery correctly in London
8d9720ef74 EEM: deduction flow fixes for London
cfed4b9586 EEM: play the correct animation before showing the scrapbook in London
1161071df3 EEM: concurrent partner animations during conversations
ce99471d14 EEM: improved partner animations reducing flickering
24173e3211 EEM: properly mark interactive zones in London
111cc673fc EEM: completed implementation for setup screen in London


Commit: 9c9bc8afce4d8d965e00ebe085f194fd4c5d2b03
    https://github.com/scummvm/scummvm/commit/9c9bc8afce4d8d965e00ebe085f194fd4c5d2b03
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:20:32+02:00

Commit Message:
EEM: initial code with detection

Changed paths:
  A engines/eem/POTFILES
  A engines/eem/configure.engine
  A engines/eem/console.cpp
  A engines/eem/console.h
  A engines/eem/detection.cpp
  A engines/eem/detection.h
  A engines/eem/eem.cpp
  A engines/eem/eem.h
  A engines/eem/metaengine.cpp
  A engines/eem/module.mk


diff --git a/engines/eem/POTFILES b/engines/eem/POTFILES
new file mode 100644
index 00000000000..df6847f5e2b
--- /dev/null
+++ b/engines/eem/POTFILES
@@ -0,0 +1 @@
+engines/eem/metaengine.cpp
diff --git a/engines/eem/configure.engine b/engines/eem/configure.engine
new file mode 100644
index 00000000000..fdf8aa7aeed
--- /dev/null
+++ b/engines/eem/configure.engine
@@ -0,0 +1,3 @@
+# This file is included from the main "configure" script
+# add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps] [components]
+add_engine eem "Eagle Eye Mysteries" no
diff --git a/engines/eem/console.cpp b/engines/eem/console.cpp
new file mode 100644
index 00000000000..80501e6da06
--- /dev/null
+++ b/engines/eem/console.cpp
@@ -0,0 +1,29 @@
+/* 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 "eem/console.h"
+
+namespace EEM {
+
+Console::Console() : GUI::Debugger() {
+}
+
+} // End of namespace EEM
diff --git a/engines/eem/console.h b/engines/eem/console.h
new file mode 100644
index 00000000000..04c06b19959
--- /dev/null
+++ b/engines/eem/console.h
@@ -0,0 +1,37 @@
+/* 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 EEM_CONSOLE_H
+#define EEM_CONSOLE_H
+
+#include "gui/debugger.h"
+
+namespace EEM {
+
+class Console : public GUI::Debugger {
+public:
+	Console();
+	~Console() override {}
+};
+
+} // End of namespace EEM
+
+#endif
diff --git a/engines/eem/detection.cpp b/engines/eem/detection.cpp
new file mode 100644
index 00000000000..0666827a3db
--- /dev/null
+++ b/engines/eem/detection.cpp
@@ -0,0 +1,83 @@
+/* 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 "base/plugins.h"
+#include "engines/advancedDetector.h"
+
+#include "eem/detection.h"
+
+namespace EEM {
+
+static const PlainGameDescriptor eemGames[] = {
+	{ "eem", "Eagle Eye Mysteries" },
+	{ nullptr, nullptr }
+};
+
+const ADGameDescription gameDescriptions[] = {
+	{
+		"eem",
+		"CD",
+		AD_ENTRY2s("EEMCD.EXE", "286d586113863ceb0e12df5b36fd8504", 272608,
+				   "PICS.DBD",  "cc05ec256cd5a04df9019e9aebfb7c32", 994057),
+		Common::EN_ANY,
+		Common::kPlatformDOS,
+		ADGF_NO_FLAGS,
+		GUIO1(GUIO_NONE)
+	},
+
+	AD_TABLE_END_MARKER
+};
+
+static const DebugChannelDef debugFlagList[] = {
+	{ kDebugGeneral, "general", "General debug" },
+	{ kDebugScript,  "script",  "Script execution" },
+	{ kDebugMystery, "mystery", "Mystery loading and state" },
+	{ kDebugSite,    "site",    "Site rendering and hot-spots" },
+	{ kDebugGfx,     "gfx",     "Graphics, palette, animations" },
+	{ kDebugSound,   "sound",   "Sound and music" },
+	DEBUG_CHANNEL_END
+};
+
+} // End of namespace EEM
+
+class EEMMetaEngineDetection : public AdvancedMetaEngineDetection<ADGameDescription> {
+public:
+	EEMMetaEngineDetection() : AdvancedMetaEngineDetection(EEM::gameDescriptions, EEM::eemGames) {
+	}
+
+	const char *getName() const override {
+		return "eem";
+	}
+
+	const char *getEngineName() const override {
+		return "Eagle Eye Mysteries";
+	}
+
+	const char *getOriginalCopyright() const override {
+		return "Eagle Eye Mysteries (C) 1994 EA Kids";
+	}
+
+	const DebugChannelDef *getDebugChannels() const override {
+		return EEM::debugFlagList;
+	}
+};
+
+REGISTER_PLUGIN_STATIC(EEM_DETECTION, PLUGIN_TYPE_ENGINE_DETECTION, EEMMetaEngineDetection);
diff --git a/engines/eem/detection.h b/engines/eem/detection.h
new file mode 100644
index 00000000000..395daab1c77
--- /dev/null
+++ b/engines/eem/detection.h
@@ -0,0 +1,42 @@
+/* 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 EEM_DETECTION_H
+#define EEM_DETECTION_H
+
+#include "engines/advancedDetector.h"
+
+namespace EEM {
+
+enum EEMDebugChannels {
+	kDebugGeneral = 1 << 0,
+	kDebugScript  = 1 << 1,
+	kDebugMystery = 1 << 2,
+	kDebugSite    = 1 << 3,
+	kDebugGfx     = 1 << 4,
+	kDebugSound   = 1 << 5
+};
+
+extern const ADGameDescription gameDescriptions[];
+
+} // End of namespace EEM
+
+#endif
diff --git a/engines/eem/eem.cpp b/engines/eem/eem.cpp
new file mode 100644
index 00000000000..365c73fa389
--- /dev/null
+++ b/engines/eem/eem.cpp
@@ -0,0 +1,108 @@
+/* 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/debug.h"
+#include "common/debug-channels.h"
+#include "common/error.h"
+#include "common/events.h"
+#include "common/system.h"
+
+#include "engines/util.h"
+
+#include "graphics/paletteman.h"
+
+#include "eem/console.h"
+#include "eem/detection.h"
+#include "eem/eem.h"
+
+namespace EEM {
+
+EEMEngine::EEMEngine(OSystem *syst, const ADGameDescription *gameDesc)
+	: Engine(syst), _gameDescription(gameDesc), _console(nullptr),
+	  _rng("eem"), _lastScreen(kScreenInvalid), _nextScreen(kScreenTitle) {
+}
+
+EEMEngine::~EEMEngine() {
+	// _console is owned by the Engine base class.
+}
+
+Common::Error EEMEngine::run() {
+	// Original _main @ 1a35:0f59 enters mode 13h via _SetMode13X (320x200x256).
+	initGraphics(320, 200);
+
+	_console = new Console();
+	setDebugger(_console);
+
+	// _main's startup paints the screen black via _AllBlack @ 172b:0d4b before
+	// the first screen handler runs; we do the same here.
+	byte palette[3 * 256] = { 0 };
+	g_system->getPaletteManager()->setPalette(palette, 0, 256);
+
+	debugC(1, kDebugGeneral, "EEM engine starting; first screen = 0x%02X", _nextScreen);
+
+	screenDriver();
+
+	debugC(1, kDebugGeneral, "EEM engine exiting");
+	return Common::kNoError;
+}
+
+void EEMEngine::screenDriver() {
+	// Mirrors _ScreenDriver @ 1a35:0dc1. The original walks a 14-entry table at
+	// 1a35:0e5e of (id, handler) pairs; we use a switch as we port handlers in.
+	while (_nextScreen != kScreenInvalid && !shouldQuit()) {
+		ScreenId next = static_cast<ScreenId>(_nextScreen);
+		switch (next) {
+		case kScreenTitle:
+			// TODO(M3): port _ShowTitlePage @ 1a35:06b7
+			warning("Screen 0x%02X (title) not implemented yet", next);
+			_lastScreen = _nextScreen;
+			_nextScreen = kScreenInvalid;
+			break;
+		default:
+			warning("Unknown screen id 0x%02X; exiting", next);
+			_nextScreen = kScreenInvalid;
+			break;
+		}
+
+		// Until handlers run their own event loops, pump events here so the
+		// engine remains responsive and the user can quit.
+		if (!pollEvents())
+			break;
+	}
+}
+
+bool EEMEngine::pollEvents() {
+	Common::Event event;
+	while (g_system->getEventManager()->pollEvent(event)) {
+		switch (event.type) {
+		case Common::EVENT_QUIT:
+		case Common::EVENT_RETURN_TO_LAUNCHER:
+			return false;
+		default:
+			break;
+		}
+	}
+	g_system->updateScreen();
+	g_system->delayMillis(10);
+	return true;
+}
+
+} // End of namespace EEM
diff --git a/engines/eem/eem.h b/engines/eem/eem.h
new file mode 100644
index 00000000000..ac8d6b14931
--- /dev/null
+++ b/engines/eem/eem.h
@@ -0,0 +1,81 @@
+/* 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/>.
+ *
+ * Based on the original engine code by EA Kids / Storm Software (1994).
+ *
+ */
+
+#ifndef EEM_EEM_H
+#define EEM_EEM_H
+
+#include "common/platform.h"
+#include "common/random.h"
+#include "common/scummsys.h"
+
+#include "engines/advancedDetector.h"
+#include "engines/engine.h"
+
+namespace EEM {
+
+class Console;
+
+/**
+ * Screen IDs used by the original ScreenDriver dispatch table at 1a35:0e5e.
+ * The table holds 14 (id, handler) entries; the loop iterates until it finds
+ * a matching id and calls its handler. ID 0xFFFF is the exit sentinel.
+ */
+enum ScreenId {
+	kScreenInvalid    = 0xFFFF,
+	kScreenTitle      = 0x0B,  ///< _ShowTitlePage @ 1a35:06b7
+	kScreenNext       = 0x08   ///< follow-up after title (case selection); to be confirmed
+};
+
+class EEMEngine : public Engine {
+public:
+	EEMEngine(OSystem *syst, const ADGameDescription *gameDesc);
+	~EEMEngine() override;
+
+	Common::Error run() override;
+
+	const char *getGameId() const;
+	Common::Platform getPlatform() const;
+
+	const ADGameDescription *_gameDescription;
+
+private:
+	/**
+	 * Central dispatch loop matching the original _ScreenDriver @ 1a35:0dc1.
+	 * Each iteration restores video mode and calls the screen handler that
+	 * matches _nextScreen. Handlers update _lastScreen / _nextScreen and
+	 * return; the loop exits when _nextScreen == kScreenInvalid.
+	 */
+	void screenDriver();
+
+	bool pollEvents();
+
+	Console *_console;
+	Common::RandomSource _rng;
+
+	uint16 _lastScreen;  ///< Mirrors _LastScreen @ 2d5d:3f24
+	uint16 _nextScreen;  ///< Mirrors _NextScreen @ 2d5d:3f26
+};
+
+} // End of namespace EEM
+
+#endif
diff --git a/engines/eem/metaengine.cpp b/engines/eem/metaengine.cpp
new file mode 100644
index 00000000000..0d61cf6a944
--- /dev/null
+++ b/engines/eem/metaengine.cpp
@@ -0,0 +1,59 @@
+/* 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 "base/plugins.h"
+#include "engines/advancedDetector.h"
+
+#include "eem/eem.h"
+
+namespace EEM {
+
+const char *EEMEngine::getGameId() const {
+	return _gameDescription->gameId;
+}
+
+Common::Platform EEMEngine::getPlatform() const {
+	return _gameDescription->platform;
+}
+
+} // End of namespace EEM
+
+class EEMMetaEngine : public AdvancedMetaEngine<ADGameDescription> {
+public:
+	const char *getName() const override {
+		return "eem";
+	}
+
+	Common::Error createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const override {
+		*engine = new EEM::EEMEngine(syst, desc);
+		return Common::kNoError;
+	}
+
+	bool hasFeature(MetaEngineFeature f) const override {
+		return false;
+	}
+};
+
+#if PLUGIN_ENABLED_DYNAMIC(EEM)
+REGISTER_PLUGIN_DYNAMIC(EEM, PLUGIN_TYPE_ENGINE, EEMMetaEngine);
+#else
+REGISTER_PLUGIN_STATIC(EEM, PLUGIN_TYPE_ENGINE, EEMMetaEngine);
+#endif
diff --git a/engines/eem/module.mk b/engines/eem/module.mk
new file mode 100644
index 00000000000..792a53959ed
--- /dev/null
+++ b/engines/eem/module.mk
@@ -0,0 +1,17 @@
+MODULE := engines/eem
+
+MODULE_OBJS = \
+	console.o \
+	eem.o \
+	metaengine.o
+
+# This module can be built as a plugin
+ifeq ($(ENABLE_EEM), DYNAMIC_PLUGIN)
+PLUGIN := 1
+endif
+
+# Include common rules
+include $(srcdir)/rules.mk
+
+# Detection objects
+DETECT_OBJS += $(MODULE)/detection.o


Commit: c271f98d82c3cd62abf9b2f4d16b43c22eeaefb5
    https://github.com/scummvm/scummvm/commit/c271f98d82c3cd62abf9b2f4d16b43c22eeaefb5
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:20:32+02:00

Commit Message:
EEM: image loading and first intro image

Changed paths:
  A engines/eem/resource.cpp
  A engines/eem/resource.h
    engines/eem/console.cpp
    engines/eem/console.h
    engines/eem/eem.cpp
    engines/eem/eem.h
    engines/eem/module.mk


diff --git a/engines/eem/console.cpp b/engines/eem/console.cpp
index 80501e6da06..bddd6e98fac 100644
--- a/engines/eem/console.cpp
+++ b/engines/eem/console.cpp
@@ -20,10 +20,34 @@
  */
 
 #include "eem/console.h"
+#include "eem/eem.h"
+#include "eem/resource.h"
 
 namespace EEM {
 
-Console::Console() : GUI::Debugger() {
+Console::Console(EEMEngine *vm) : GUI::Debugger(), _vm(vm) {
+	registerCmd("pic", WRAP_METHOD(Console, cmdPic));
+}
+
+bool Console::cmdPic(int argc, const char **argv) {
+	if (argc < 2) {
+		debugPrintf("Usage: pic <number>\n");
+		debugPrintf("Loads picture <number> from PICS.DBD and reports its dimensions.\n");
+		debugPrintf("PICS.DBX has %u entries.\n", _vm->getPics().size());
+		return true;
+	}
+
+	const uint num = (uint)atoi(argv[1]);
+	Picture pic;
+	if (!_vm->getPics().getPicture(num, pic)) {
+		debugPrintf("pic %u: load failed\n", num);
+		return true;
+	}
+
+	debugPrintf("pic %u (idx %u): %dx%d, compsize=%u, flags=0x%04x, miscflags=0x%04x, rowoff=%u\n",
+				num, num - 1, pic.surface.w, pic.surface.h,
+				pic.compsize, pic.flags, pic.miscflags, pic.rowoff);
+	return true;
 }
 
 } // End of namespace EEM
diff --git a/engines/eem/console.h b/engines/eem/console.h
index 04c06b19959..e3c89cdd54c 100644
--- a/engines/eem/console.h
+++ b/engines/eem/console.h
@@ -26,10 +26,17 @@
 
 namespace EEM {
 
+class EEMEngine;
+
 class Console : public GUI::Debugger {
 public:
-	Console();
+	explicit Console(EEMEngine *vm);
 	~Console() override {}
+
+private:
+	EEMEngine *_vm;
+
+	bool cmdPic(int argc, const char **argv);
 };
 
 } // End of namespace EEM
diff --git a/engines/eem/eem.cpp b/engines/eem/eem.cpp
index 365c73fa389..a58748773e3 100644
--- a/engines/eem/eem.cpp
+++ b/engines/eem/eem.cpp
@@ -23,7 +23,10 @@
 #include "common/debug-channels.h"
 #include "common/error.h"
 #include "common/events.h"
+#include "common/file.h"
+#include "common/path.h"
 #include "common/system.h"
+#include "common/textconsole.h"
 
 #include "engines/util.h"
 
@@ -33,6 +36,11 @@
 #include "eem/detection.h"
 #include "eem/eem.h"
 
+namespace {
+const uint kPalSize = 768;     ///< 256 colors * 3 bytes
+const uint kNumSitePals = 40;  ///< SITEPALS holds 40 palettes (40 * 768 = 30720)
+} // anonymous namespace
+
 namespace EEM {
 
 EEMEngine::EEMEngine(OSystem *syst, const ADGameDescription *gameDesc)
@@ -48,7 +56,7 @@ Common::Error EEMEngine::run() {
 	// Original _main @ 1a35:0f59 enters mode 13h via _SetMode13X (320x200x256).
 	initGraphics(320, 200);
 
-	_console = new Console();
+	_console = new Console(this);
 	setDebugger(_console);
 
 	// _main's startup paints the screen black via _AllBlack @ 172b:0d4b before
@@ -56,8 +64,60 @@ Common::Error EEMEngine::run() {
 	byte palette[3 * 256] = { 0 };
 	g_system->getPaletteManager()->setPalette(palette, 0, 256);
 
+	// Mirrors _main's `_picsFile = _fopen("PICS.DBD", ...)` plus
+	// _InitGraphicsSystem's PICS.DBX index parse (172b:0145).
+	if (!_picsArchive.open(Common::Path("PICS.DBD"), Common::Path("PICS.DBX"))) {
+		return Common::Error(Common::kReadingFailed, "PICS archive missing");
+	}
+
+	// Mirrors _ReadPalettes @ 172b:0d89 ??? slurp SITEPALS in one read.
+	Common::File palFile;
+	if (!palFile.open(Common::Path("SITEPALS"))) {
+		return Common::Error(Common::kReadingFailed, "SITEPALS missing");
+	}
+	_sitePals.resize(palFile.size());
+	if (palFile.read(_sitePals.data(), _sitePals.size()) != _sitePals.size()) {
+		return Common::Error(Common::kReadingFailed, "SITEPALS short read");
+	}
+	palFile.close();
+	debugC(1, kDebugGfx, "Loaded %u SITEPALS palettes", (uint)(_sitePals.size() / kPalSize));
+
 	debugC(1, kDebugGeneral, "EEM engine starting; first screen = 0x%02X", _nextScreen);
 
+	// Show the first intro image (EA Kids logo) ??? mirrors the opening of
+	// _ShowEAKids @ 2520:05f0: GetPicture(0x54), MemoryCopy to 0xa000:0,
+	// GetPalette(0x25), setmany(_fpal, 0). Skipped: color-cycle loop.
+	{
+		Picture eakids;
+		if (!_picsArchive.getPicture(0x54, eakids)) {
+			return Common::Error(Common::kReadingFailed, "EA Kids logo (picture #0x54) load failed");
+		}
+		debugC(1, kDebugGfx, "EA Kids logo: %dx%d", eakids.surface.w, eakids.surface.h);
+		blitFullScreen(eakids);
+		setSitePalette(0x25);
+		g_system->updateScreen();
+
+		// Hold the image for up to 3 s or until the user clicks/keys/quits.
+		const uint32 startMs = g_system->getMillis();
+		while (g_system->getMillis() - startMs < 3000) {
+			Common::Event event;
+			bool stop = false;
+			while (g_system->getEventManager()->pollEvent(event)) {
+				if (event.type == Common::EVENT_QUIT ||
+					event.type == Common::EVENT_RETURN_TO_LAUNCHER ||
+					event.type == Common::EVENT_LBUTTONDOWN ||
+					event.type == Common::EVENT_KEYDOWN) {
+					stop = true;
+					break;
+				}
+			}
+			if (stop)
+				break;
+			g_system->updateScreen();
+			g_system->delayMillis(10);
+		}
+	}
+
 	screenDriver();
 
 	debugC(1, kDebugGeneral, "EEM engine exiting");
@@ -89,6 +149,28 @@ void EEMEngine::screenDriver() {
 	}
 }
 
+void EEMEngine::setSitePalette(uint num) {
+	if (num >= kNumSitePals || _sitePals.size() < (num + 1) * kPalSize) {
+		warning("setSitePalette: index %u out of range", num);
+		return;
+	}
+	// SITEPALS stores 6-bit VGA-DAC values (0..63); ScummVM expects 8-bit
+	// (0..255), so left-shift by 2 like the original VGA hardware did.
+	const byte *src = _sitePals.data() + num * kPalSize;
+	byte expanded[kPalSize];
+	for (uint i = 0; i < kPalSize; i++) {
+		expanded[i] = (byte)(src[i] << 2);
+	}
+	g_system->getPaletteManager()->setPalette(expanded, 0, 256);
+}
+
+void EEMEngine::blitFullScreen(const Picture &pic) {
+	// _MemoryCopy(0, 0xa000, srcOff, srcSeg) in _ShowEAKids dumps the picture
+	// straight into VGA's 320x200 framebuffer.
+	g_system->copyRectToScreen(pic.surface.getPixels(), pic.surface.pitch,
+							   0, 0, pic.surface.w, pic.surface.h);
+}
+
 bool EEMEngine::pollEvents() {
 	Common::Event event;
 	while (g_system->getEventManager()->pollEvent(event)) {
diff --git a/engines/eem/eem.h b/engines/eem/eem.h
index ac8d6b14931..67a4b0967db 100644
--- a/engines/eem/eem.h
+++ b/engines/eem/eem.h
@@ -31,6 +31,8 @@
 #include "engines/advancedDetector.h"
 #include "engines/engine.h"
 
+#include "eem/resource.h"
+
 namespace EEM {
 
 class Console;
@@ -58,6 +60,18 @@ public:
 
 	const ADGameDescription *_gameDescription;
 
+	DBDArchive &getPics() { return _picsArchive; }
+
+	/**
+	 * Upload palette index @p num (one of 40 stored in SITEPALS) to the
+	 * screen, with the VGA-DAC 6-bit-to-8-bit shift. Mirrors _GetPalette
+	 * @ 172b:0e80 followed by _setmany @ 1000:0930.
+	 */
+	void setSitePalette(uint num);
+
+	/** Blit @p pic to the screen at (0,0), expecting a 320x200 picture. */
+	void blitFullScreen(const Picture &pic);
+
 private:
 	/**
 	 * Central dispatch loop matching the original _ScreenDriver @ 1a35:0dc1.
@@ -72,6 +86,9 @@ private:
 	Console *_console;
 	Common::RandomSource _rng;
 
+	DBDArchive _picsArchive;  ///< PICS.DBD/.DBX (mouse, buttons, markers, balloons sprites)
+	Common::Array<byte> _sitePals; ///< 40 x 768 bytes of 6-bit VGA palettes from SITEPALS
+
 	uint16 _lastScreen;  ///< Mirrors _LastScreen @ 2d5d:3f24
 	uint16 _nextScreen;  ///< Mirrors _NextScreen @ 2d5d:3f26
 };
diff --git a/engines/eem/module.mk b/engines/eem/module.mk
index 792a53959ed..34247658998 100644
--- a/engines/eem/module.mk
+++ b/engines/eem/module.mk
@@ -3,7 +3,8 @@ MODULE := engines/eem
 MODULE_OBJS = \
 	console.o \
 	eem.o \
-	metaengine.o
+	metaengine.o \
+	resource.o
 
 # This module can be built as a plugin
 ifeq ($(ENABLE_EEM), DYNAMIC_PLUGIN)
diff --git a/engines/eem/resource.cpp b/engines/eem/resource.cpp
new file mode 100644
index 00000000000..67c9ab8a78e
--- /dev/null
+++ b/engines/eem/resource.cpp
@@ -0,0 +1,132 @@
+/* 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/compression/dcl.h"
+#include "common/debug.h"
+#include "common/file.h"
+#include "common/textconsole.h"
+
+#include "graphics/pixelformat.h"
+
+#include "eem/detection.h"
+#include "eem/resource.h"
+
+namespace EEM {
+
+DBDArchive::DBDArchive() {
+}
+
+DBDArchive::~DBDArchive() {
+	close();
+}
+
+bool DBDArchive::open(const Common::Path &dbdName, const Common::Path &dbxName) {
+	close();
+
+	if (!_dbd.open(dbdName)) {
+		warning("DBDArchive: cannot open %s", dbdName.toString().c_str());
+		return false;
+	}
+
+	Common::File dbx;
+	if (!dbx.open(dbxName)) {
+		warning("DBDArchive: cannot open %s", dbxName.toString().c_str());
+		_dbd.close();
+		return false;
+	}
+
+	// _InitGraphicsSystem @ 172b:0145 reads 10 bytes per entry until EOF.
+	const int32 dbxSize = dbx.size();
+	_index.reserve(dbxSize / 10);
+	while (dbx.pos() + 10 <= dbxSize) {
+		DBEntry entry;
+		entry.offset     = dbx.readUint32LE();
+		entry.compressed = dbx.readUint16LE();
+		entry.size       = dbx.readUint32LE();
+		_index.push_back(entry);
+	}
+	dbx.close();
+
+	debugC(1, kDebugGfx, "DBDArchive: opened %s (%u entries)",
+		   dbdName.toString().c_str(), _index.size());
+	return true;
+}
+
+void DBDArchive::close() {
+	if (_dbd.isOpen())
+		_dbd.close();
+	_index.clear();
+}
+
+bool DBDArchive::loadEntry(uint num, Picture &out) {
+	if (num >= _index.size()) {
+		warning("DBDArchive::loadEntry: %u out of range (max %u)", num, _index.size());
+		return false;
+	}
+
+	const DBEntry &entry = _index[num];
+	if (!_dbd.seek(entry.offset)) {
+		warning("DBDArchive::loadEntry: seek to 0x%08x failed", entry.offset);
+		return false;
+	}
+
+	// Mirrors _GetFromDB @ 172b:105d:
+	//   _fread(i)            // 2-byte skip word (purpose unclear; always 0x0001)
+	//   _fread(pic, 1, 12)   // 12-byte header
+	(void)_dbd.readUint16LE();
+	out.flags             = _dbd.readUint16LE();
+	const uint16 height   = _dbd.readUint16LE();
+	const uint16 width    = _dbd.readUint16LE();
+	out.rowoff            = _dbd.readUint16LE();
+	out.miscflags         = _dbd.readUint16LE();
+	out.compsize          = _dbd.readUint16LE();
+
+	if (width == 0 || height == 0) {
+		warning("DBDArchive::loadEntry: %u has zero dimensions (%ux%u)",
+				num, width, height);
+		return false;
+	}
+
+	out.surface.create(width, height, Graphics::PixelFormat::createFormatCLUT8());
+
+	if (entry.compressed == 0) {
+		// Raw pixel data ??? read width*height bytes verbatim.
+		const uint32 pixelCount = (uint32)width * (uint32)height;
+		if (_dbd.read(out.surface.getPixels(), pixelCount) != pixelCount) {
+			warning("DBDArchive::loadEntry: short raw read on %u", num);
+			return false;
+		}
+		return true;
+	}
+
+	// Compressed payload: feed the .DBD stream straight into the DCL
+	// decoder, matching the pattern used by Neverhood's BLB archive.
+	const uint32 unpacked = (uint32)width * (uint32)height;
+	if (!Common::decompressDCL(&_dbd, (byte *)out.surface.getPixels(),
+							   out.compsize, unpacked)) {
+		warning("DBDArchive::loadEntry: DCL decompression failed on %u "
+				"(%u packed -> %u pixels)", num, out.compsize, unpacked);
+		return false;
+	}
+	return true;
+}
+
+} // End of namespace EEM
diff --git a/engines/eem/resource.h b/engines/eem/resource.h
new file mode 100644
index 00000000000..a48deb85518
--- /dev/null
+++ b/engines/eem/resource.h
@@ -0,0 +1,106 @@
+/* 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 EEM_RESOURCE_H
+#define EEM_RESOURCE_H
+
+#include "common/array.h"
+#include "common/file.h"
+#include "common/path.h"
+#include "common/scummsys.h"
+
+#include "graphics/managed_surface.h"
+
+namespace EEM {
+
+/**
+ * Index entry for a .DBD/.DBX archive pair (10 bytes on disk).
+ *
+ * Mirrors the original `dbi` struct read by _InitGraphicsSystem @ 172b:0145
+ * in 10-byte chunks until EOF. Each entry locates one resource blob in the
+ * companion .DBD container.
+ */
+struct DBEntry {
+	uint32 offset;     ///< Byte offset of the entry in the .DBD file.
+	uint16 compressed; ///< Non-zero if the payload is PKWARE DCL ("Implode") packed.
+	uint32 size;       ///< Total size of the entry on disk (including 14-byte header).
+};
+
+/**
+ * 8-bit indexed picture decoded from a .DBD entry.
+ *
+ * The original engine's PicData is a 16-byte struct; we keep the descriptive
+ * fields here and let `Graphics::ManagedSurface` own the pixel data so the
+ * rest of the engine can blit/scale/clip with the standard API.
+ */
+struct Picture {
+	uint16 flags     = 0; ///< +0  high byte = sub-mode used by some sprites
+	uint16 rowoff    = 0; ///< +6  row offset (used by some clipped sprites)
+	uint16 miscflags = 0; ///< +8  high byte = transparent-mask flag
+	uint16 compsize  = 0; ///< +10 packed payload size on disk
+	Graphics::ManagedSurface surface;
+};
+
+/**
+ * Reader for a .DBD + .DBX archive pair.
+ *
+ * The original engine has five such pairs: PICS, SITES, ANI, BALLOON, BUTTON.
+ * Each .DBX is parsed once into an in-memory `_index`; reads of individual
+ * entries seek into the .DBD on demand and (when flagged) decompress with
+ * `Common::decompressDCL`.
+ */
+class DBDArchive {
+public:
+	DBDArchive();
+	~DBDArchive();
+
+	/**
+	 * Open both halves of an archive. @p dbdName / @p dbxName are looked up
+	 * via SearchMan, so case is normalized for us. Returns false if either
+	 * file is missing or the index is malformed.
+	 */
+	bool open(const Common::Path &dbdName, const Common::Path &dbxName);
+	void close();
+
+	/** Number of entries in the index. */
+	uint32 size() const { return _index.size(); }
+
+	/**
+	 * Load entry @p num (0-based index), decompressing if needed.
+	 * Returns true on success. Mirrors _GetFromDB @ 172b:105d.
+	 */
+	bool loadEntry(uint num, Picture &out);
+
+	/**
+	 * Convenience wrapper that mirrors the engine's 1-based picture API:
+	 * `_GetPicture(num)` calls `_GetFromDB(..., num - 1)`. Use this when
+	 * porting code that references picture IDs by their original number.
+	 */
+	bool getPicture(uint num, Picture &out) { return loadEntry(num - 1, out); }
+
+private:
+	Common::File _dbd;
+	Common::Array<DBEntry> _index;
+};
+
+} // End of namespace EEM
+
+#endif


Commit: f6a6833108d61f79ddf03d743d9be80cc1d40a88
    https://github.com/scummvm/scummvm/commit/f6a6833108d61f79ddf03d743d9be80cc1d40a88
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:20:32+02:00

Commit Message:
EEM: basic per case flow, with initial code for font loading

Changed paths:
  A engines/eem/animation.cpp
  A engines/eem/animation.h
  A engines/eem/font.cpp
  A engines/eem/font.h
  A engines/eem/mystery.cpp
  A engines/eem/mystery.h
  A engines/eem/site.cpp
  A engines/eem/site.h
  R engines/eem/console.cpp
  R engines/eem/console.h
    engines/eem/eem.cpp
    engines/eem/eem.h
    engines/eem/metaengine.cpp
    engines/eem/module.mk
    engines/eem/resource.cpp
    engines/eem/resource.h


diff --git a/engines/eem/animation.cpp b/engines/eem/animation.cpp
new file mode 100644
index 00000000000..929fc18025e
--- /dev/null
+++ b/engines/eem/animation.cpp
@@ -0,0 +1,162 @@
+/* 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/debug.h"
+#include "common/textconsole.h"
+
+#include "eem/animation.h"
+#include "eem/detection.h"
+
+namespace EEM {
+
+void asmDecompress(const byte *src, uint srcSize, byte *dst, uint dstSize) {
+	const byte *srcEnd = src + srcSize;
+	byte *dstEnd = dst + dstSize;
+
+	while (dst < dstEnd && src < srcEnd) {
+		const byte op = *src++;
+
+		if (op == 0x80) {
+			// Extended opcode: read 16-bit length argument.
+			if (src + 2 > srcEnd)
+				break;
+			const uint16 n = (uint16)src[0] | ((uint16)src[1] << 8);
+			src += 2;
+
+			if ((n & 0x8000) == 0) {
+				// Skip n bytes ??? preserves previous-frame pixels.
+				dst += n;
+			} else if ((n & 0x4000) == 0) {
+				// Long literal copy: (n & 0x7FFF) bytes from src to dst.
+				uint16 cnt = n & 0x7FFF;
+				while (cnt-- && dst < dstEnd && src < srcEnd)
+					*dst++ = *src++;
+			} else {
+				// Long fill: (n & 0x3FFF) bytes with the next single byte.
+				if (src >= srcEnd)
+					break;
+				const byte val = *src++;
+				uint16 cnt = n & 0x3FFF;
+				while (cnt-- && dst < dstEnd)
+					*dst++ = val;
+			}
+		} else if (op == 0) {
+			// Short fill: byte count, then byte value.
+			if (src + 2 > srcEnd)
+				break;
+			const byte cnt = src[0];
+			const byte val = src[1];
+			src += 2;
+			for (byte i = 0; i < cnt && dst < dstEnd; i++)
+				*dst++ = val;
+		} else if ((op & 0x80) == 0) {
+			// Short literal copy: `op` bytes (1..0x7F) from src to dst.
+			byte cnt = op;
+			while (cnt-- && dst < dstEnd && src < srcEnd)
+				*dst++ = *src++;
+		} else {
+			// Short skip: (op & 0x7F) bytes ??? preserves previous-frame pixels.
+			dst += op & 0x7F;
+		}
+	}
+}
+
+bool ANMDecoder::open(const Common::Path &path) {
+	close();
+
+	if (!_file.open(path)) {
+		warning("ANMDecoder: cannot open %s", path.toString().c_str());
+		return false;
+	}
+
+	if (_file.read(_palette, sizeof(_palette)) != sizeof(_palette)) {
+		warning("ANMDecoder: short palette read on %s", path.toString().c_str());
+		close();
+		return false;
+	}
+
+	_frameCount = _file.readUint16LE();
+	if (_frameCount == 0 || _frameCount > 1024) {
+		warning("ANMDecoder: implausible frame count %u in %s",
+				_frameCount, path.toString().c_str());
+		close();
+		return false;
+	}
+
+	(void)_file.readUint16LE();        // header[+0]: ignored
+	_height = _file.readUint16LE();    // header[+2]
+	_width  = _file.readUint16LE();    // header[+4]
+	(void)_file.readUint16LE();        // header[+6]
+	(void)_file.readUint16LE();        // header[+8]
+	(void)_file.readUint16LE();        // header[+10]
+
+	if (_width == 0 || _height == 0) {
+		warning("ANMDecoder: zero dimensions in %s", path.toString().c_str());
+		close();
+		return false;
+	}
+
+	_lengths.resize(_frameCount);
+	for (uint16 i = 0; i < _frameCount; i++)
+		_lengths[i] = _file.readUint16LE();
+
+	_buffer.resize((uint32)_width * _height);
+	memset(_buffer.data(), 0, _buffer.size());
+	_nextFrameIdx = 0;
+
+	debugC(1, kDebugGfx, "ANMDecoder: %s opened, %u frames, %ux%u",
+		   path.toString().c_str(), _frameCount, _width, _height);
+	return true;
+}
+
+void ANMDecoder::close() {
+	if (_file.isOpen())
+		_file.close();
+	_lengths.clear();
+	_buffer.clear();
+	_packed.clear();
+	_frameCount = _width = _height = _nextFrameIdx = 0;
+}
+
+void ANMDecoder::getPalette8(byte *out) const {
+	for (uint i = 0; i < sizeof(_palette); i++)
+		out[i] = (byte)(_palette[i] << 2);
+}
+
+const byte *ANMDecoder::nextFrame() {
+	if (_nextFrameIdx >= _frameCount)
+		return nullptr;
+
+	const uint16 packedSize = _lengths[_nextFrameIdx];
+	if (_packed.size() < packedSize)
+		_packed.resize(packedSize);
+
+	if (_file.read(_packed.data(), packedSize) != packedSize) {
+		warning("ANMDecoder: short read on frame %u", _nextFrameIdx);
+		return nullptr;
+	}
+
+	asmDecompress(_packed.data(), packedSize, _buffer.data(), _buffer.size());
+	_nextFrameIdx++;
+	return _buffer.data();
+}
+
+} // End of namespace EEM
diff --git a/engines/eem/animation.h b/engines/eem/animation.h
new file mode 100644
index 00000000000..9397872e2fd
--- /dev/null
+++ b/engines/eem/animation.h
@@ -0,0 +1,98 @@
+/* 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 EEM_ANIMATION_H
+#define EEM_ANIMATION_H
+
+#include "common/array.h"
+#include "common/file.h"
+#include "common/path.h"
+#include "common/scummsys.h"
+
+namespace EEM {
+
+/**
+ * Decoder for the engine's full-screen difference animations.
+ *
+ * Used for `BOLT.ANM`, `TITLE.ANM`, `ANIM01.A` .. `ANIM20.A`. The format is
+ * documented by Load_Sequence @ 2503:0006 / OpenDifferenceAnimation @ 2520:0337:
+ *
+ *   - 0x300 bytes : 6-bit VGA palette
+ *   - u16        : frame count
+ *   - 12 bytes   : header (height @ +2, width @ +4, rest unused)
+ *   - frames*u16 : packed length per frame
+ *   - per frame  : `lengths[i]` bytes of RLE-packed delta data
+ *
+ * Each packed frame is unpacked by the custom `_ASM_Decompress` RLE
+ * (1000:0953) into the persistent `_buffer`; skip opcodes preserve pixels
+ * from the previous frame, which is how the difference encoding works.
+ */
+class ANMDecoder {
+public:
+	ANMDecoder() = default;
+	~ANMDecoder() { close(); }
+
+	/// Open @p path and parse the file header. Returns false on error.
+	bool open(const Common::Path &path);
+
+	/// Release the open file and the persistent frame buffer.
+	void close();
+
+	/// Number of frames in the animation.
+	uint16 frameCount() const { return _frameCount; }
+	uint16 width()      const { return _width; }
+	uint16 height()     const { return _height; }
+
+	/// 768 bytes of 6-bit VGA palette (already shifted into 8-bit on getPalette8).
+	const byte *palette6() const { return _palette; }
+
+	/// Fill @p out (768 bytes) with the 8-bit-shifted palette. Convenience.
+	void getPalette8(byte *out) const;
+
+	/**
+	 * Decode the next frame in place into the internal buffer. Returns a
+	 * pointer to the @c width()*@c height() byte image, or nullptr at EOF.
+	 */
+	const byte *nextFrame();
+
+private:
+	Common::File _file;
+	Common::Array<uint16> _lengths;
+	Common::Array<byte> _buffer;   ///< Persistent unpacked frame, width*height bytes.
+	Common::Array<byte> _packed;   ///< Per-frame scratch packed buffer.
+
+	byte _palette[768] = {};
+	uint16 _frameCount = 0;
+	uint16 _width = 0;
+	uint16 _height = 0;
+	uint16 _nextFrameIdx = 0;
+};
+
+/**
+ * Decompress a single frame's RLE payload in place. Mirrors _ASM_Decompress
+ * @ 1000:0953 byte-for-byte. @p dst already holds the previous frame; skip
+ * opcodes leave those pixels untouched.
+ */
+void asmDecompress(const byte *src, uint srcSize, byte *dst, uint dstSize);
+
+} // End of namespace EEM
+
+#endif
diff --git a/engines/eem/console.cpp b/engines/eem/console.cpp
deleted file mode 100644
index bddd6e98fac..00000000000
--- a/engines/eem/console.cpp
+++ /dev/null
@@ -1,53 +0,0 @@
-/* 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 "eem/console.h"
-#include "eem/eem.h"
-#include "eem/resource.h"
-
-namespace EEM {
-
-Console::Console(EEMEngine *vm) : GUI::Debugger(), _vm(vm) {
-	registerCmd("pic", WRAP_METHOD(Console, cmdPic));
-}
-
-bool Console::cmdPic(int argc, const char **argv) {
-	if (argc < 2) {
-		debugPrintf("Usage: pic <number>\n");
-		debugPrintf("Loads picture <number> from PICS.DBD and reports its dimensions.\n");
-		debugPrintf("PICS.DBX has %u entries.\n", _vm->getPics().size());
-		return true;
-	}
-
-	const uint num = (uint)atoi(argv[1]);
-	Picture pic;
-	if (!_vm->getPics().getPicture(num, pic)) {
-		debugPrintf("pic %u: load failed\n", num);
-		return true;
-	}
-
-	debugPrintf("pic %u (idx %u): %dx%d, compsize=%u, flags=0x%04x, miscflags=0x%04x, rowoff=%u\n",
-				num, num - 1, pic.surface.w, pic.surface.h,
-				pic.compsize, pic.flags, pic.miscflags, pic.rowoff);
-	return true;
-}
-
-} // End of namespace EEM
diff --git a/engines/eem/console.h b/engines/eem/console.h
deleted file mode 100644
index e3c89cdd54c..00000000000
--- a/engines/eem/console.h
+++ /dev/null
@@ -1,44 +0,0 @@
-/* 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 EEM_CONSOLE_H
-#define EEM_CONSOLE_H
-
-#include "gui/debugger.h"
-
-namespace EEM {
-
-class EEMEngine;
-
-class Console : public GUI::Debugger {
-public:
-	explicit Console(EEMEngine *vm);
-	~Console() override {}
-
-private:
-	EEMEngine *_vm;
-
-	bool cmdPic(int argc, const char **argv);
-};
-
-} // End of namespace EEM
-
-#endif
diff --git a/engines/eem/eem.cpp b/engines/eem/eem.cpp
index a58748773e3..4bd0da9430a 100644
--- a/engines/eem/eem.cpp
+++ b/engines/eem/eem.cpp
@@ -32,159 +32,1821 @@
 
 #include "graphics/paletteman.h"
 
-#include "eem/console.h"
 #include "eem/detection.h"
 #include "eem/eem.h"
+#include "eem/site.h"
+
+#include "common/config-manager.h"
+#include "common/savefile.h"
+#include "common/serializer.h"
+
+#include "graphics/managed_surface.h"
+
+namespace EEM {
 
 namespace {
 const uint kPalSize = 768;     ///< 256 colors * 3 bytes
 const uint kNumSitePals = 40;  ///< SITEPALS holds 40 palettes (40 * 768 = 30720)
-} // anonymous namespace
 
-namespace EEM {
+// Picture / palette IDs from the original code (1-based picture IDs).
+const uint kPicEAKidsLogo      = 0x54;  ///< _ShowEAKids: GetPicture(0x54)
+const uint kPicHighScoreLogo   = 0x20c; ///< _ShowHScoreLogo: GetPicture(0x20c)
+const uint kPicChooseBackground = 0x8c; ///< _DoChoosePartner: GetBackground(0x8c)
+const uint kPalEAKids          = 0x25;
+const uint kPalHighScore       = 0x27;
+
+// Animation IDs (0-based per ANI.DBX). _DoChoosePartner uses GetAnimation(8/9).
+const uint kAniBoy  = 8;
+const uint kAniGirl = 9;
+
+// On-screen positions for the boy and girl partner sprites, from
+// _DoChoosePartner: NewAnimation(0xe2, 0x62, ...) and (0x42, 0x60, ...).
+const int kBoyX  = 0xe2; // 226
+const int kBoyY  = 0x62; // 98
+const int kGirlX = 0x42; // 66
+const int kGirlY = 0x60; // 96
+} // anonymous namespace
 
 EEMEngine::EEMEngine(OSystem *syst, const ADGameDescription *gameDesc)
-	: Engine(syst), _gameDescription(gameDesc), _console(nullptr),
-	  _rng("eem"), _lastScreen(kScreenInvalid), _nextScreen(kScreenTitle) {
+	: Engine(syst), _gameDescription(gameDesc), _rng("eem"),
+	  _playerName("Detective"),
+	  _lastScreen(kScreenInvalid), _nextScreen(kScreenTitle), _partner(0) {
 }
 
 EEMEngine::~EEMEngine() {
-	// _console is owned by the Engine base class.
 }
 
 Common::Error EEMEngine::run() {
-	// Original _main @ 1a35:0f59 enters mode 13h via _SetMode13X (320x200x256).
+	// _SetMode13X @ 1000:0358 enters VGA mode 13h (320x200x256).
 	initGraphics(320, 200);
 
-	_console = new Console(this);
-	setDebugger(_console);
+	if (!openArchives())
+		return Common::Error(Common::kReadingFailed, "EEM archive open failed");
 
-	// _main's startup paints the screen black via _AllBlack @ 172b:0d4b before
-	// the first screen handler runs; we do the same here.
-	byte palette[3 * 256] = { 0 };
-	g_system->getPaletteManager()->setPalette(palette, 0, 256);
+	if (!loadSitePalettes())
+		return Common::Error(Common::kReadingFailed, "SITEPALS load failed");
+
+	// _LoadFont @ 1b66:023c ??? main 8 px bitmap font.
+	if (!_font.load(Common::Path("FONT.FNT")))
+		warning("FONT.FNT failed to load; text will not render");
 
-	// Mirrors _main's `_picsFile = _fopen("PICS.DBD", ...)` plus
-	// _InitGraphicsSystem's PICS.DBX index parse (172b:0145).
+	// _AllBlack @ 172b:0d4b paints the screen black before the first handler.
+	byte black[3 * 256] = { 0 };
+	g_system->getPaletteManager()->setPalette(black, 0, 256);
+
+	debugC(1, kDebugGeneral, "EEM engine starting");
+
+	// If the user chose "Load" before pressing Play, the framework
+	// invokes `loadGameState` which sets up `_mystery` and `_partner`.
+	// Honour that by skipping the intros and going straight to the
+	// loaded mystery's site loop.
+	const int wantedSave = ConfMan.hasKey("save_slot")
+		? ConfMan.getInt("save_slot") : -1;
+	if (wantedSave >= 0) {
+		const Common::Error err = loadGameState(wantedSave);
+		if (err.getCode() == Common::kNoError && _mystery.isLoaded()) {
+			debugC(1, kDebugGeneral, "Resuming from slot %d at mystery %u",
+				   wantedSave, _mystery.number());
+			doInitClues();
+			doSiteLoop();
+			while (!shouldQuit()) {
+				doCaseSelection();
+				if (!_mystery.isLoaded()) break;
+				doInitClues();
+				doSiteLoop();
+			}
+			return Common::kNoError;
+		}
+	}
+
+	// Reproduces _DoOpeningAnims @ 2520:082a (sans audio):
+	//   EA Kids logo (PIC) -> HighScore Productions logo (PIC) ->
+	//   Storm Software logo (BOLT.ANM) -> 20 character-intro animations
+	//   (ANIM01.A .. ANIM20.A) -> TITLE.ANM. Each can be skipped with a
+	//   click or any key.
+	showEAKidsLogo();
+	if (!shouldQuit())
+		showHighScoreLogo();
+	if (!shouldQuit())
+		playAnm(Common::Path("BOLT.ANM"));
+	for (int i = 1; i <= 20 && !shouldQuit(); i++) {
+		Common::String name = Common::String::format("ANIM%02d.A", i);
+		playAnm(Common::Path(name));
+		// Between anims the original plays a voice clip via _SpoolSound;
+		// without audio we still want a beat so each scene reads.
+		if (!shouldQuit() && i != 20)
+			waitForInput(2000);
+	}
+	if (!shouldQuit())
+		playAnm(Common::Path("TITLE.ANM"), 120, /*holdLastFrame=*/true);
+
+	// After the title chain, the original goes Title (B) -> screen 8
+	// (NewPlayer / saved-record selection) -> screen 9 (ChoosePartner) ->
+	// screen A (CaseSelection) -> site loop. We mirror the same order.
+	if (!shouldQuit())
+		doNewPlayer();
+	if (!shouldQuit())
+		doChoosePartner();
+	if (!shouldQuit())
+		doCaseSelection();
+	if (!shouldQuit() && _mystery.isLoaded()) {
+		// Mark the starting site as active and display the case briefing.
+		// `_DoInitClues` @ 1a35:0411 ??? case briefing.
+		doInitClues();
+		doSiteLoop();
+
+		// After a case, loop back to CaseSelection.
+		while (!shouldQuit()) {
+			doCaseSelection();
+			if (!_mystery.isLoaded())
+				break;
+			doInitClues();
+			doSiteLoop();
+		}
+	}
+
+	debugC(1, kDebugGeneral, "EEM engine exiting");
+	return Common::kNoError;
+}
+
+bool EEMEngine::openArchives() {
+	// _InitGraphicsSystem @ 172b:0145 opens these five .DBD/.DBX pairs.
 	if (!_picsArchive.open(Common::Path("PICS.DBD"), Common::Path("PICS.DBX"))) {
-		return Common::Error(Common::kReadingFailed, "PICS archive missing");
+		warning("PICS archive missing");
+		return false;
+	}
+	if (!_aniArchive.open(Common::Path("ANI.DBD"), Common::Path("ANI.DBX"))) {
+		warning("ANI archive missing");
+		return false;
 	}
+	// SITES + BALLOON are optional for the boot path but needed for site
+	// rendering and clue display.
+	if (!_sitesArchive.open(Common::Path("SITES.DBD"), Common::Path("SITES.DBX")))
+		warning("SITES archive missing ??? site backgrounds disabled");
+	if (!_balloonArchive.open(Common::Path("BALLOON.DBD"), Common::Path("BALLOON.DBX")))
+		warning("BALLOON archive missing ??? clue text will lack balloons");
+	return true;
+}
 
-	// Mirrors _ReadPalettes @ 172b:0d89 ??? slurp SITEPALS in one read.
-	Common::File palFile;
-	if (!palFile.open(Common::Path("SITEPALS"))) {
-		return Common::Error(Common::kReadingFailed, "SITEPALS missing");
+bool EEMEngine::loadSitePalettes() {
+	Common::File f;
+	if (!f.open(Common::Path("SITEPALS"))) {
+		warning("SITEPALS missing");
+		return false;
 	}
-	_sitePals.resize(palFile.size());
-	if (palFile.read(_sitePals.data(), _sitePals.size()) != _sitePals.size()) {
-		return Common::Error(Common::kReadingFailed, "SITEPALS short read");
+	_sitePals.resize(f.size());
+	if (f.read(_sitePals.data(), _sitePals.size()) != _sitePals.size()) {
+		warning("SITEPALS short read");
+		return false;
 	}
-	palFile.close();
-	debugC(1, kDebugGfx, "Loaded %u SITEPALS palettes", (uint)(_sitePals.size() / kPalSize));
+	debugC(1, kDebugGfx, "Loaded %u SITEPALS palettes",
+		   (uint)(_sitePals.size() / kPalSize));
+	return true;
+}
 
-	debugC(1, kDebugGeneral, "EEM engine starting; first screen = 0x%02X", _nextScreen);
+void EEMEngine::setSitePalette(uint num) {
+	if (num >= kNumSitePals || _sitePals.size() < (num + 1) * kPalSize) {
+		warning("setSitePalette: index %u out of range", num);
+		return;
+	}
+	// SITEPALS stores 6-bit VGA-DAC values (0..63); ScummVM expects 8-bit
+	// (0..255), so left-shift by 2 like the original VGA hardware did.
+	const byte *src = _sitePals.data() + num * kPalSize;
+	byte expanded[kPalSize];
+	for (uint i = 0; i < kPalSize; i++)
+		expanded[i] = (byte)(src[i] << 2);
+	g_system->getPaletteManager()->setPalette(expanded, 0, 256);
+}
 
-	// Show the first intro image (EA Kids logo) ??? mirrors the opening of
-	// _ShowEAKids @ 2520:05f0: GetPicture(0x54), MemoryCopy to 0xa000:0,
-	// GetPalette(0x25), setmany(_fpal, 0). Skipped: color-cycle loop.
-	{
-		Picture eakids;
-		if (!_picsArchive.getPicture(0x54, eakids)) {
-			return Common::Error(Common::kReadingFailed, "EA Kids logo (picture #0x54) load failed");
-		}
-		debugC(1, kDebugGfx, "EA Kids logo: %dx%d", eakids.surface.w, eakids.surface.h);
-		blitFullScreen(eakids);
-		setSitePalette(0x25);
+bool EEMEngine::setAnmPalette(const Common::Path &anmPath) {
+	Common::File f;
+	if (!f.open(anmPath)) {
+		warning("setAnmPalette: cannot open %s", anmPath.toString().c_str());
+		return false;
+	}
+	byte raw[kPalSize];
+	if (f.read(raw, kPalSize) != kPalSize) {
+		warning("setAnmPalette: short read on %s", anmPath.toString().c_str());
+		return false;
+	}
+	byte expanded[kPalSize];
+	for (uint i = 0; i < kPalSize; i++)
+		expanded[i] = (byte)(raw[i] << 2);
+	g_system->getPaletteManager()->setPalette(expanded, 0, 256);
+	return true;
+}
+
+void EEMEngine::playAnm(const Common::Path &path, uint frameDelayMs, bool holdLastFrame) {
+	ANMDecoder anm;
+	if (!anm.open(path)) {
+		warning("playAnm: %s missing", path.toString().c_str());
+		return;
+	}
+
+	byte palette[768];
+	anm.getPalette8(palette);
+	g_system->getPaletteManager()->setPalette(palette, 0, 256);
+
+	const uint16 w = anm.width();
+	const uint16 h = anm.height();
+
+	while (!shouldQuit()) {
+		const byte *frame = anm.nextFrame();
+		if (!frame)
+			break;
+
+		g_system->copyRectToScreen(frame, w, 0, 0, w, h);
 		g_system->updateScreen();
 
-		// Hold the image for up to 3 s or until the user clicks/keys/quits.
-		const uint32 startMs = g_system->getMillis();
-		while (g_system->getMillis() - startMs < 3000) {
+		// Drain events and let the user skip with click/key. The original
+		// uses _CheckFrameRate / _kbhit; we use a simple fixed delay until
+		// the frame-rate calibration logic from _GetSpeedRating is wired up.
+		const uint32 frameStart = g_system->getMillis();
+		bool aborted = false;
+		while (g_system->getMillis() - frameStart < frameDelayMs && !aborted) {
 			Common::Event event;
-			bool stop = false;
 			while (g_system->getEventManager()->pollEvent(event)) {
 				if (event.type == Common::EVENT_QUIT ||
 					event.type == Common::EVENT_RETURN_TO_LAUNCHER ||
 					event.type == Common::EVENT_LBUTTONDOWN ||
 					event.type == Common::EVENT_KEYDOWN) {
-					stop = true;
+					aborted = true;
+					break;
+				}
+			}
+			g_system->delayMillis(5);
+		}
+		if (aborted)
+			break;
+	}
+
+	if (holdLastFrame && !shouldQuit()) {
+		// Mirror the wait-loop at the end of `_DoOpeningAnims`:
+		//   while (!keyDataAvailable) ;
+		// We accept either a click or a key.
+		while (!shouldQuit()) {
+			Common::Event ev;
+			bool clicked = false;
+			while (g_system->getEventManager()->pollEvent(ev)) {
+				if (ev.type == Common::EVENT_QUIT ||
+					ev.type == Common::EVENT_RETURN_TO_LAUNCHER ||
+					ev.type == Common::EVENT_LBUTTONDOWN ||
+					ev.type == Common::EVENT_KEYDOWN) {
+					clicked = true;
 					break;
 				}
 			}
-			if (stop)
+			if (clicked)
 				break;
 			g_system->updateScreen();
-			g_system->delayMillis(10);
+			g_system->delayMillis(20);
 		}
 	}
+}
 
-	screenDriver();
+void EEMEngine::blitAt(const Picture &pic, int x, int y) {
+	// Clip against the 320x200 frame buffer.
+	const int w = MIN<int>(pic.surface.w, 320 - x);
+	const int h = MIN<int>(pic.surface.h, 200 - y);
+	if (w <= 0 || h <= 0)
+		return;
+	g_system->copyRectToScreen(pic.surface.getPixels(), pic.surface.pitch,
+							   x, y, w, h);
+}
 
-	debugC(1, kDebugGeneral, "EEM engine exiting");
-	return Common::kNoError;
+void EEMEngine::waitForInput(uint32 maxMs) {
+	const uint32 startMs = g_system->getMillis();
+	while (!shouldQuit() && (g_system->getMillis() - startMs < maxMs)) {
+		Common::Event event;
+		while (g_system->getEventManager()->pollEvent(event)) {
+			if (event.type == Common::EVENT_QUIT ||
+				event.type == Common::EVENT_RETURN_TO_LAUNCHER ||
+				event.type == Common::EVENT_LBUTTONDOWN ||
+				event.type == Common::EVENT_KEYDOWN) {
+				return;
+			}
+		}
+		g_system->updateScreen();
+		g_system->delayMillis(10);
+	}
 }
 
-void EEMEngine::screenDriver() {
-	// Mirrors _ScreenDriver @ 1a35:0dc1. The original walks a 14-entry table at
-	// 1a35:0e5e of (id, handler) pairs; we use a switch as we port handlers in.
-	while (_nextScreen != kScreenInvalid && !shouldQuit()) {
-		ScreenId next = static_cast<ScreenId>(_nextScreen);
-		switch (next) {
-		case kScreenTitle:
-			// TODO(M3): port _ShowTitlePage @ 1a35:06b7
-			warning("Screen 0x%02X (title) not implemented yet", next);
-			_lastScreen = _nextScreen;
-			_nextScreen = kScreenInvalid;
+void EEMEngine::showEAKidsLogo() {
+	// Mirrors _ShowEAKids @ 2520:05f0 (without the color-cycle loop):
+	// GetPicture(0x54), MemoryCopy to VGA, GetPalette(0x25), setmany.
+	Picture pic;
+	if (!_picsArchive.getPicture(kPicEAKidsLogo, pic)) {
+		warning("EA Kids logo (%u) load failed", kPicEAKidsLogo);
+		return;
+	}
+	blitAt(pic, 0, 0);
+	setSitePalette(kPalEAKids);
+	g_system->updateScreen();
+	waitForInput(2500);
+}
+
+void EEMEngine::showHighScoreLogo() {
+	// Mirrors _ShowHScoreLogo @ 2520:0799 (without the wait-loop):
+	// GetPicture(0x20c), MemoryCopy to VGA, GetPalette(0x27), FadeIn.
+	Picture pic;
+	if (!_picsArchive.getPicture(kPicHighScoreLogo, pic)) {
+		warning("HighScore logo (%u) load failed", kPicHighScoreLogo);
+		return;
+	}
+	blitAt(pic, 0, 0);
+	setSitePalette(kPalHighScore);
+	g_system->updateScreen();
+	waitForInput(2500);
+}
+
+void EEMEngine::doNewPlayer() {
+	// Mirrors `_NewPlayer` @ 1c33:0dda. The original draws background
+	// 0x104 + character peek pic 0x107, then shows "Please type your
+	// name" and accepts up to 12 characters until Enter. We render a
+	// minimal version: black screen + prompt.
+	if (!_font.isLoaded()) {
+		_playerName = "Detective";
+		return;
+	}
+
+	Common::String name;
+	const int maxChars = 12;
+
+	// Mirror the original: load PIC 0x104 as the name-entry backdrop.
+	// The original also slides in PIC 0x107 (a peeking character).
+	Picture bg;
+	const bool haveBG = _picsArchive.getPicture(0x104, bg);
+
+	auto draw = [&]() {
+		Graphics::ManagedSurface scratch(320, 200,
+			Graphics::PixelFormat::createFormatCLUT8());
+		scratch.clear();
+		if (haveBG) {
+			const int w = MIN<int>(bg.surface.w, 320);
+			const int h = MIN<int>(bg.surface.h, 200);
+			for (int row = 0; row < h; row++)
+				memcpy((byte *)scratch.getBasePtr(0, row),
+					   (const byte *)bg.surface.getBasePtr(0, row), w);
+		}
+		_font.drawString(&scratch, 40, 24,
+			"Welcome to Eagle Eye Mysteries!", 0xF);
+		_font.drawString(&scratch, 40, 40, "Please type your name:", 0xF);
+		_font.drawString(&scratch, 40, 60,
+			"(Backspace to delete, Enter to confirm)", 0xF);
+		Common::String shown = name + "_";
+		_font.drawString(&scratch, 40, 90, shown, 0xF);
+		g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
+								   0, 0, 320, 200);
+		g_system->updateScreen();
+	};
+	draw();
+
+	while (!shouldQuit()) {
+		Common::Event ev;
+		bool dirty = false;
+		while (g_system->getEventManager()->pollEvent(ev)) {
+			if (ev.type == Common::EVENT_QUIT ||
+				ev.type == Common::EVENT_RETURN_TO_LAUNCHER)
+				return;
+			if (ev.type != Common::EVENT_KEYDOWN)
+				continue;
+			const Common::KeyCode k = ev.kbd.keycode;
+			if (k == Common::KEYCODE_RETURN) {
+				if (name.empty())
+					name = "Detective";
+				_playerName = name;
+				return;
+			}
+			if (k == Common::KEYCODE_ESCAPE) {
+				_playerName = "Detective";
+				return;
+			}
+			if (k == Common::KEYCODE_BACKSPACE) {
+				if (!name.empty()) {
+					name.deleteLastChar();
+					dirty = true;
+				}
+				continue;
+			}
+			if (ev.kbd.ascii >= ' ' && ev.kbd.ascii < 127 &&
+				(int)name.size() < maxChars) {
+				name += (char)ev.kbd.ascii;
+				dirty = true;
+			}
+		}
+		if (dirty)
+			draw();
+		g_system->delayMillis(15);
+	}
+}
+
+void EEMEngine::doChoosePartner() {
+	// Mirrors _DoChoosePartner @ 1a35:0756. The original places boy + girl
+	// animations on a backdrop and polls four click rectangles (two per
+	// character) for the player's choice. We approximate by splitting the
+	// screen at x=160: left half = girl (Jenny), right half = boy (Jake).
+	Picture background;
+	if (!_picsArchive.getPicture(kPicChooseBackground, background)) {
+		warning("ChoosePartner background (%u) load failed", kPicChooseBackground);
+		return;
+	}
+
+	Animation boyAnim;
+	if (!_aniArchive.loadAnimation(kAniBoy, boyAnim) || boyAnim.empty()) {
+		warning("Boy animation (%u) load failed", kAniBoy);
+		return;
+	}
+	Animation girlAnim;
+	if (!_aniArchive.loadAnimation(kAniGirl, girlAnim) || girlAnim.empty()) {
+		warning("Girl animation (%u) load failed", kAniGirl);
+		return;
+	}
+
+	setAnmPalette(Common::Path("TITLE.ANM"));
+	blitAt(background, 0, 0);
+	blitAt(girlAnim[0], kGirlX, kGirlY);
+	blitAt(boyAnim[0], kBoyX, kBoyY);
+	g_system->updateScreen();
+
+	debugC(1, kDebugGeneral, "ChoosePartner: %u boy frames at (%d,%d), "
+		   "%u girl frames at (%d,%d)",
+		   (uint)boyAnim.size(), kBoyX, kBoyY,
+		   (uint)girlAnim.size(), kGirlX, kGirlY);
+
+	uint frame = 0;
+	uint32 lastTick = g_system->getMillis();
+	while (!shouldQuit()) {
+		// Advance frame at ~5 Hz so the animations cycle gently.
+		if (g_system->getMillis() - lastTick > 200) {
+			lastTick = g_system->getMillis();
+			frame++;
+			blitAt(background, 0, 0);
+			blitAt(girlAnim[frame % girlAnim.size()], kGirlX, kGirlY);
+			blitAt(boyAnim[frame % boyAnim.size()], kBoyX, kBoyY);
+			g_system->updateScreen();
+		}
+
+		Common::Event ev;
+		bool done = false;
+		while (g_system->getEventManager()->pollEvent(ev)) {
+			if (ev.type == Common::EVENT_QUIT ||
+				ev.type == Common::EVENT_RETURN_TO_LAUNCHER) {
+				done = true;
+				break;
+			}
+			if (ev.type == Common::EVENT_LBUTTONDOWN) {
+				_partner = (ev.mouse.x >= 160) ? 0 : 1;
+				debugC(1, kDebugGeneral, "Partner picked: %s",
+					   _partner == 0 ? "Jake" : "Jennifer");
+				done = true;
+				break;
+			}
+			if (ev.type == Common::EVENT_KEYDOWN) {
+				if (ev.kbd.keycode == Common::KEYCODE_LEFT) {
+					_partner = 1; done = true; break;
+				}
+				if (ev.kbd.keycode == Common::KEYCODE_RIGHT) {
+					_partner = 0; done = true; break;
+				}
+				if (ev.kbd.keycode == Common::KEYCODE_RETURN ||
+					ev.kbd.keycode == Common::KEYCODE_ESCAPE) {
+					done = true; break;
+				}
+			}
+		}
+		if (done)
 			break;
-		default:
-			warning("Unknown screen id 0x%02X; exiting", next);
-			_nextScreen = kScreenInvalid;
+		g_system->updateScreen();
+		g_system->delayMillis(20);
+	}
+}
+
+void EEMEngine::doCaseSelection() {
+	// Mirrors `_CaseSelection` @ 1c33:0a87. The original draws PIC 0x41
+	// (chooser background) and a paginated list of mystery names rendered
+	// from M<n>.BIN headers, then calls `_DoChoose` to read a selection.
+	// We approximate with a numeric prompt (0..9 for first ten mysteries,
+	// Tab to cycle, Enter to load).
+	const uint kMaxMystery = 54;
+	// Default selection = the next unsolved mystery so post-win the
+	// player doesn't have to scroll to find what's left.
+	uint sel = 0;
+	for (uint i = 0; i <= kMaxMystery; i++) {
+		if (i < sizeof(_mysteriesSolved) && !_mysteriesSolved[i]) {
+			sel = i;
 			break;
 		}
+	}
 
-		// Until handlers run their own event loops, pump events here so the
-		// engine remains responsive and the user can quit.
-		if (!pollEvents())
-			break;
+	// Mirrors `_CaseSelection`: load PIC 0x41 as the chooser backdrop.
+	Picture caseBg;
+	const bool haveCaseBg = _picsArchive.getPicture(0x41, caseBg);
+
+	auto draw = [&]() {
+		Graphics::ManagedSurface scratch(320, 200,
+			Graphics::PixelFormat::createFormatCLUT8());
+		scratch.clear();
+		if (haveCaseBg) {
+			const int w = MIN<int>(caseBg.surface.w, 320);
+			const int h = MIN<int>(caseBg.surface.h, 200);
+			for (int row = 0; row < h; row++) {
+				memcpy((byte *)scratch.getBasePtr(0, row),
+					   (const byte *)caseBg.surface.getBasePtr(0, row), w);
+			}
+		}
+		if (_font.isLoaded()) {
+			_font.drawString(&scratch, 8, 4,
+				Common::String::format("EAGLE EYE - %s", _playerName.c_str()), 0xF);
+
+			// Solved count.
+			uint solved = 0, perfectSolved = 0;
+			for (uint i = 0; i < sizeof(_mysteriesSolved); i++) {
+				if (_mysteriesSolved[i] >= 1) solved++;
+				if (_mysteriesSolved[i] == 2) perfectSolved++;
+			}
+			_font.drawString(&scratch, 200, 4,
+				Common::String::format("solved %u (1st try %u)",
+									   solved, perfectSolved), 0xF);
+			if (perfectSolved >= 55) {
+				_font.drawString(&scratch, 8, 168,
+					"** PERFECT MASTER SLEUTH! **", 0xF);
+			} else if (solved >= 55) {
+				_font.drawString(&scratch, 8, 168,
+					"** ALL MYSTERIES SOLVED! **", 0xF);
+			}
+
+			char marker = ' ';
+			if (sel < sizeof(_mysteriesSolved)) {
+				if (_mysteriesSolved[sel] == 2) marker = '*';
+				else if (_mysteriesSolved[sel] == 1) marker = '+';
+			}
+			// Per the original tiers: 0 (tutorial), 1-24 (Junior),
+			// 25-48 (Senior), 49-54 (Master).
+			const char *tier = "Tutorial";
+			if (sel >= 1 && sel <= 24) tier = "Junior Sleuth";
+			else if (sel >= 25 && sel <= 48) tier = "Senior Sleuth";
+			else if (sel >= 49 && sel <= 54) tier = "Master Sleuth";
+			_font.drawString(&scratch, 8, 24,
+				Common::String::format("Mystery %u  %c  [%s]",
+									   sel, marker, tier), 0xF);
+			_font.drawString(&scratch, 8, 40,
+				"  0..9        quick select", 0xF);
+			_font.drawString(&scratch, 8, 52,
+				"  Tab / +     next mystery", 0xF);
+			_font.drawString(&scratch, 8, 64,
+				"  Shift+Tab   prev mystery", 0xF);
+			_font.drawString(&scratch, 8, 76,
+				"  PgUp/PgDn   jump 10", 0xF);
+			_font.drawString(&scratch, 8, 88,
+				"  Home/End    first/last", 0xF);
+			_font.drawString(&scratch, 8, 100,
+				"  Enter       start mystery", 0xF);
+			_font.drawString(&scratch, 8, 112,
+				"  F5          save / load (ScummVM)", 0xF);
+			_font.drawString(&scratch, 8, 124,
+				"  ESC         quit", 0xF);
+			_font.drawString(&scratch, 8, 144,
+				"  *  solved on first try", 0xF);
+			_font.drawString(&scratch, 8, 156,
+				"  +  solved", 0xF);
+		}
+		g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
+								   0, 0, 320, 200);
+		g_system->updateScreen();
+	};
+
+	draw();
+
+	bool confirmed = false;
+	while (!confirmed && !shouldQuit()) {
+		Common::Event ev;
+		while (g_system->getEventManager()->pollEvent(ev)) {
+			if (ev.type == Common::EVENT_QUIT ||
+				ev.type == Common::EVENT_RETURN_TO_LAUNCHER)
+				return;
+			if (ev.type != Common::EVENT_KEYDOWN)
+				continue;
+			const Common::KeyCode k = ev.kbd.keycode;
+			if (k == Common::KEYCODE_ESCAPE) {
+                _mystery.clear();
+				_nextScreen = kScreenInvalid;
+				return;
+			}
+			if (k == Common::KEYCODE_RETURN) {
+				confirmed = true;
+				break;
+			}
+			if (k >= Common::KEYCODE_0 && k <= Common::KEYCODE_9) {
+				sel = (uint)(k - Common::KEYCODE_0);
+				draw();
+				continue;
+			}
+			if (k == Common::KEYCODE_TAB || k == Common::KEYCODE_PLUS ||
+				k == Common::KEYCODE_RIGHT || k == Common::KEYCODE_DOWN) {
+				const bool back = (ev.kbd.flags & Common::KBD_SHIFT) ||
+								  k == Common::KEYCODE_LEFT;
+				if (back)
+					sel = (sel == 0) ? kMaxMystery : sel - 1;
+				else
+					sel = (sel >= kMaxMystery) ? 0 : sel + 1;
+				draw();
+				continue;
+			}
+			if (k == Common::KEYCODE_LEFT || k == Common::KEYCODE_UP ||
+				k == Common::KEYCODE_MINUS) {
+				sel = (sel == 0) ? kMaxMystery : sel - 1;
+				draw();
+				continue;
+			}
+			if (k == Common::KEYCODE_PAGEDOWN) {
+				sel = (sel + 10 > kMaxMystery) ? kMaxMystery : sel + 10;
+				draw();
+				continue;
+			}
+			if (k == Common::KEYCODE_PAGEUP) {
+				sel = (sel < 10) ? 0 : sel - 10;
+				draw();
+				continue;
+			}
+			if (k == Common::KEYCODE_HOME) {
+				sel = 0;
+				draw();
+				continue;
+			}
+			if (k == Common::KEYCODE_END) {
+				sel = kMaxMystery;
+				draw();
+				continue;
+			}
+		}
+		g_system->delayMillis(15);
+	}
+
+	if (!_mystery.load(sel, &_rng)) {
+		warning("doCaseSelection: failed to load mystery %u", sel);
+		_mystery.clear();
+		return;
 	}
+	debugC(1, kDebugMystery, "Mystery %u loaded; %u sites, %u suspects",
+		   sel, _mystery.numSites(), _mystery.numSuspects());
 }
 
-void EEMEngine::setSitePalette(uint num) {
-	if (num >= kNumSitePals || _sitePals.size() < (num + 1) * kPalSize) {
-		warning("setSitePalette: index %u out of range", num);
+void EEMEngine::doInitClues() {
+	// Mirrors `_DoInitClues` @ 1a35:0411. Sets BG 0x52 + palette 0x22,
+	// blits the Goblindroid game and book first frames, then displays
+	// the case briefing ClueBlock at InitBlock + 4. Marks the starting
+	// site (InitBlock word[1]) on `_OnSites`.
+	if (!_mystery.isLoaded())
 		return;
+
+	const byte *ib = _mystery.initBlock();
+	if (!ib)
+		return;
+
+	const uint16 startSite = READ_LE_UINT16(ib + 2);
+	if (startSite < Mystery::kVisitedSiteCap)
+		_mystery._onSites[startSite] = 1;
+
+	setSitePalette(0x22);
+	Picture bg;
+	if (_picsArchive.getPicture(0x52, bg))
+		blitAt(bg, 0, 0);
+
+	const uint gameAni = _partner == 0 ? 0x17 : 0x3b;
+	const uint bookAni = _partner == 0 ? 0x18 : 0x3c;
+	Animation game, book;
+	if (_aniArchive.loadAnimation(gameAni, game) && !game.empty())
+		blitAt(game[0], 0xcd, 0x6c);
+	if (_aniArchive.loadAnimation(bookAni, book) && !book.empty())
+		blitAt(book[0], 0, 99);
+
+	// Case type 1 also places "Nancy" (a third character) at (0x68, 0x8b)
+	// per `_DoInitClues`.
+	const uint16 caseType = READ_LE_UINT16(ib);
+	if (caseType == 1) {
+		Animation nancy;
+		if (_aniArchive.loadAnimation(0x19, nancy) && !nancy.empty())
+			blitAt(nancy[0], 0x68, 0x8b);
 	}
-	// SITEPALS stores 6-bit VGA-DAC values (0..63); ScummVM expects 8-bit
-	// (0..255), so left-shift by 2 like the original VGA hardware did.
-	const byte *src = _sitePals.data() + num * kPalSize;
-	byte expanded[kPalSize];
-	for (uint i = 0; i < kPalSize; i++) {
-		expanded[i] = (byte)(src[i] << 2);
+
+	displayClue(ib + 4);
+}
+
+void EEMEngine::doSiteLoop() {
+	// Mirrors the per-mystery site loop. SiteScreen::run() handles
+	// hotspot clicks plus M (map), N (notebook), G (gallery), A (accuse),
+	// Tab (next site), ESC (exit).
+	SiteScreen screen(this, &_mystery);
+	screen.run();
+}
+
+/// Mirror `_ParseString` @ 1b66:07c3 ??? substitute the control bytes that
+/// the original engine uses as placeholders. Only the two we encounter most
+/// often (player name = 0x80, partner first name = 0x82) are substituted;
+/// other 0x8N opcodes are stripped. The original engine also handles
+/// hyphenation marks and a hint placeholder (0x89) we ignore for now.
+static Common::String parseString(const Common::String &raw,
+								  const Common::String &playerName,
+								  const Common::String &partnerName) {
+	Common::String out;
+	for (uint i = 0; i < raw.size(); i++) {
+		const byte c = (byte)raw[i];
+		if (c == 0x80) {
+			out += playerName;
+		} else if (c == 0x82) {
+			out += partnerName;
+		} else if (c >= 0x80 && c < 0x8A) {
+			// Other control opcodes: eat them silently for now.
+		} else if (c == 0 || c == '\r') {
+			// stop on NUL, ignore CR
+			if (c == 0)
+				break;
+		} else {
+			out += (char)c;
+		}
 	}
-	g_system->getPaletteManager()->setPalette(expanded, 0, 256);
+	return out;
 }
 
-void EEMEngine::blitFullScreen(const Picture &pic) {
-	// _MemoryCopy(0, 0xa000, srcOff, srcSeg) in _ShowEAKids dumps the picture
-	// straight into VGA's 320x200 framebuffer.
-	g_system->copyRectToScreen(pic.surface.getPixels(), pic.surface.pitch,
-							   0, 0, pic.surface.w, pic.surface.h);
+void EEMEngine::applyClueSideEffects(const byte *c) {
+	for (uint j = 0; j < 5; j++) {
+		const uint16 note = READ_LE_UINT16(c + 0x30 + j * 2);
+		if (note != 0xFFFF && note < Mystery::kCluesFoundCap)
+			_mystery._cluesFound[note] = 1;
+
+		const uint16 galIdx = READ_LE_UINT16(c + 0x26 + j * 2);
+		if (galIdx != 0xFFFF && galIdx < Mystery::kGalleryCap) {
+			const uint8 phys = _mystery._newOrder[galIdx];
+			if (phys < Mystery::kGalleryCap)
+				_mystery._inGallery[phys] = 1;
+		}
+
+		const uint16 siteIdx = READ_LE_UINT16(c + 0x1c + j * 2);
+		if (siteIdx != 0xFFFF) {
+			const uint16 siteVal = siteIdx & 0x7FFF;
+			if (siteVal < Mystery::kVisitedSiteCap)
+				_mystery._onSites[siteVal] = 1;
+			if (siteIdx & 0x8000)
+				_mystery._sawCONSITEs = true;
+		}
+	}
 }
 
-bool EEMEngine::pollEvents() {
-	Common::Event event;
-	while (g_system->getEventManager()->pollEvent(event)) {
-		switch (event.type) {
-		case Common::EVENT_QUIT:
-		case Common::EVENT_RETURN_TO_LAUNCHER:
-			return false;
-		default:
-			break;
+void EEMEngine::displayClue(const byte *clueBlock) {
+	if (!clueBlock || !_mystery.isLoaded())
+		return;
+
+	// ClueBlock layout (verified against M0.BIN):
+	//   +0..1: number (entry count)
+	//   +2..3: pic ID for entry 0 (entry N>0 uses prev entry's last 2 bytes)
+	//   +4..:  array of 62-byte entries
+	const uint16 number = READ_LE_UINT16(clueBlock);
+	debugC(1, kDebugScript, "displayClue: %u entries", number);
+	if (number == 0 || number > 32) {
+		// number==0 = no briefing (e.g. mystery 0 case-type 4); >32 is a
+		// guard against bad pointers.
+		return;
+	}
+
+	// Snapshot the current screen as the BG so character pics from
+	// earlier entries don't stack on top of each other.
+	Graphics::ManagedSurface bg(320, 200,
+		Graphics::PixelFormat::createFormatCLUT8());
+	bg.clear();
+	{
+		Graphics::Surface *screen = g_system->lockScreen();
+		if (screen) {
+			for (int row = 0; row < 200; row++) {
+				memcpy((byte *)bg.getBasePtr(0, row),
+					   (const byte *)screen->getBasePtr(0, row), 320);
+			}
+			g_system->unlockScreen();
+		}
+	}
+
+	for (uint i = 0; i < number && !shouldQuit(); i++) {
+		// Restore BG before drawing this entry's portrait + balloon.
+		g_system->copyRectToScreen(bg.getPixels(), bg.pitch, 0, 0, 320, 200);
+		const byte *c = clueBlock + 4 + i * 62;
+		// Per-partner fields:
+		//   +0..1, +2..3: tx, ty (partner 0)
+		//   +4..5, +6..7: tx, ty (partner 1)
+		//   +8..9, +10..11: bubText offset for partner 0/1 (rel. TextBlock)
+		//   +12..13, +14..15: balloon picture ID for partner 0/1
+		//   +16..17, +18..19: bubX, bubY
+		// Per `_DisplayClue` @ 2404:05e6: partner 1 uses its own field
+		// set ONLY when bubText1 is not -1; otherwise it falls back to
+		// the partner 0 fields entirely. Partner 0 always uses field 0.
+		const bool useP1 = (_partner == 1) &&
+			(READ_LE_UINT16(c + 10) != 0xFFFF);
+		const uint partner = useP1 ? 1 : 0;
+		const uint16 textOff = READ_LE_UINT16(c + 8 + partner * 2);
+		const bool hasText = (textOff != 0xFFFF);
+		// Partner 1 bubX/bubY at +0x14/+0x16; partner 0 at +0x10/+0x12.
+		const uint16 bubX = READ_LE_UINT16(c + (useP1 ? 0x14 : 0x10));
+		const uint16 bubY = READ_LE_UINT16(c + (useP1 ? 0x16 : 0x12));
+		const uint16 bubNum = READ_LE_UINT16(c + (useP1 ? 0x0E : 0x0C));
+		const char *raw   = hasText ? _mystery.textAt(textOff) : "";
+
+		// Speaker portrait. Mirrors `_DisplayClue`'s `pic[clues+i*62-2]`:
+		// for entry 0 the pic ID is in the ClueBlock header at +2; for
+		// later entries it sits in the previous entry's last 2 bytes.
+		// Speaker portrait position uses partner 0 fields (+0..+3) when
+		// _partner==0 or when partner 1 falls back; otherwise partner 1
+		// fields (+4..+7). Same logic as the original.
+		const uint16 charX  = READ_LE_UINT16(c + (useP1 ? 4 : 0));
+		const uint16 charY  = READ_LE_UINT16(c + (useP1 ? 6 : 2));
+		const uint16 charPicId = (i == 0)
+			? READ_LE_UINT16(clueBlock + 2)
+			: READ_LE_UINT16(c - 2);
+		if (charPicId != 0 && charPicId != 0xFFFF) {
+			Picture charPic;
+			if (_picsArchive.getPicture(charPicId, charPic) &&
+				charX < 320 && charY < 200) {
+				const int w = MIN<int>(charPic.surface.w, 320 - charX);
+				const int h = MIN<int>(charPic.surface.h, 200 - charY);
+				if (w > 0 && h > 0)
+					g_system->copyRectToScreen(charPic.surface.getPixels(),
+						charPic.surface.pitch, charX, charY, w, h);
+			}
+		}
+
+		// Substitute placeholder control bytes with the entered player
+		// name and the chosen partner's first name (Jake / Jennifer).
+		const Common::String partnerName = (_partner == 0) ? "Jake" : "Jennifer";
+		const Common::String text = parseString(raw ? raw : "",
+												_playerName, partnerName);
+
+		// Speech balloon. Mirrors `_GetBalloon` + `_AddPicBackground` in
+		// `_DisplayClue`. The original looks up per-balloon text-area
+		// metadata in a table at offset 0x875 (within `_DisplayClue`'s
+		// segment); we don't have that table decoded yet, so we use a
+		// fixed inset of 8 px from the balloon's top-left.
+		Picture balloon;
+		const uint16 balloonId = bubNum & 0x7F;
+		const bool haveBalloon = bubNum != 0xFFFF &&
+			_balloonArchive.size() > balloonId &&
+			_balloonArchive.loadEntry(balloonId, balloon);
+
+		if (_font.isLoaded() && !text.empty()) {
+			// Snapshot the current screen, overlay balloon + text, then
+			// copy the changed band back. This preserves the site BG
+			// underneath unchanged regions.
+			Graphics::Surface *screen = g_system->lockScreen();
+			if (!screen) break;
+			Graphics::ManagedSurface scratch(320, 200,
+				Graphics::PixelFormat::createFormatCLUT8());
+			for (int row = 0; row < 200; row++) {
+				memcpy((byte *)scratch.getBasePtr(0, row),
+					   (const byte *)screen->getBasePtr(0, row), 320);
+			}
+			g_system->unlockScreen();
+
+			int textX = bubX;
+			int textY = bubY;
+			int textW = MIN<int>(320 - bubX, 200);
+			int copyY = bubY;
+			int copyH = _font.height() * 4 + 8;
+
+			if (haveBalloon) {
+				const int bw = MIN<int>(balloon.surface.w, 320 - bubX);
+				const int bh = MIN<int>(balloon.surface.h, 200 - bubY);
+				if (bw > 0 && bh > 0) {
+					for (int row = 0; row < bh; row++) {
+						memcpy((byte *)scratch.getBasePtr(bubX, bubY + row),
+							   (const byte *)balloon.surface.getBasePtr(0, row),
+							   bw);
+					}
+				}
+				// Per-balloon metadata table at 29be:0875 in the original
+				// uses (textX=6, textY=4) inset across all entries; we
+				// adopt the same constants instead of approximating with 8.
+				textX = bubX + 6;
+				textY = bubY + 4;
+				textW = bw - 12;
+				copyH = bh;
+			} else {
+				// No balloon ??? clear a band so old pixels don't bleed.
+				const Common::Rect band(0, bubY, 320,
+					MIN<int>(bubY + copyH, 200));
+				scratch.fillRect(band, 0);
+				copyY = bubY;
+			}
+
+			_font.drawWordWrapped(&scratch, textX, textY,
+				MAX<int>(8, textW), text, 0xF);
+
+			g_system->copyRectToScreen(scratch.getBasePtr(0, copyY),
+				scratch.pitch, 0, copyY, 320,
+				MIN<int>(copyH, 200 - copyY));
+			g_system->updateScreen();
+		}
+
+		// Wait for click/key to advance ??? only if we drew something.
+		// ESC skips the entire dialogue rather than just one entry.
+		if (hasText || (charPicId != 0 && charPicId != 0xFFFF)) {
+			bool advance = false;
+			bool skipAll = false;
+			while (!advance && !shouldQuit()) {
+				Common::Event ev;
+				while (g_system->getEventManager()->pollEvent(ev)) {
+					if (ev.type == Common::EVENT_QUIT ||
+						ev.type == Common::EVENT_RETURN_TO_LAUNCHER) {
+						advance = true;
+						break;
+					}
+					if (ev.type == Common::EVENT_KEYDOWN &&
+						ev.kbd.keycode == Common::KEYCODE_ESCAPE) {
+						advance = true;
+						skipAll = true;
+						break;
+					}
+					if (ev.type == Common::EVENT_LBUTTONDOWN ||
+						ev.type == Common::EVENT_KEYDOWN) {
+						advance = true;
+						break;
+					}
+				}
+				g_system->delayMillis(10);
+			}
+			if (skipAll) {
+				// Apply remaining side-effects without rendering. The
+				// original silently runs the state updates even when the
+				// player skips ahead.
+				for (uint k = i; k < number; k++)
+					applyClueSideEffects(clueBlock + 4 + k * 62);
+				return;
+			}
 		}
+
+		applyClueSideEffects(c);
 	}
+}
+
+void EEMEngine::doNotebook() {
+	// Mirrors `_DrawNotes` @ 161e:01d0 + `_HandleNoteButton`. We list every
+	// found clue with its NoteIndex point value and let the player toggle
+	// "selected" with number keys 1..9 (paged in groups of 9). The total
+	// points of selected clues feed `_SolvedCheck` during accuse.
+	if (!_font.isLoaded())
+		return;
+
+	int page = 0;
+	const int kPerPage = 9;
+
+	auto draw = [&]() {
+		Graphics::ManagedSurface scratch(320, 200,
+			Graphics::PixelFormat::createFormatCLUT8());
+		scratch.clear();
+		_font.drawString(&scratch, 8, 4, "NOTEBOOK", 0xF);
+		_font.drawString(&scratch, 200, 4,
+			Common::String::format("pts: %d", _mystery.selectedPoints()), 0xF);
+
+		// Build a list of found-clue indices.
+		Common::Array<uint> found;
+		for (uint i = 0; i < Mystery::kCluesFoundCap; i++)
+			if (_mystery._cluesFound[i])
+				found.push_back(i);
+		const int total = (int)found.size();
+		const int pages = MAX<int>(1, (total + kPerPage - 1) / kPerPage);
+		page = MIN<int>(page, pages - 1);
+
+		_font.drawString(&scratch, 200, 16,
+			Common::String::format("page %d/%d", page + 1, pages), 0xF);
+
+		const byte *ni = _mystery.noteIndex();
+		const uint16 niCount = _mystery.noteIndexCount();
+		const Common::String partnerName = (_partner == 0) ? "Jake" : "Jennifer";
+		int y = 4 + _font.height() * 2 + 4;
+		for (int slot = 0; slot < kPerPage; slot++) {
+			const int idx = page * kPerPage + slot;
+			if (idx >= total)
+				break;
+			const uint clueId = found[idx];
+			Common::String text;
+			int pts = 0;
+			if (ni && clueId < niCount) {
+				const uint16 textOff = READ_LE_UINT16(ni + clueId * 4);
+				const uint16 ptsRaw  = READ_LE_UINT16(ni + clueId * 4 + 2);
+				pts = (int)(int16)ptsRaw;
+				const Common::String raw = _mystery.textAt(textOff);
+				text = parseString(raw, _playerName, partnerName);
+			}
+			if (text.empty())
+				text = Common::String::format("clue %u", clueId);
+
+			const char selMark = _mystery._noteSelected[clueId] ? '*' : ' ';
+			Common::String line = Common::String::format(
+				"%d [%c] (%d pts) %s", slot + 1, selMark, pts, text.c_str());
+			const int used = _font.drawWordWrapped(&scratch, 8, y, 304, line, 0xF);
+			y += used + 2;
+			if (y >= 192) break;
+		}
+		g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
+								   0, 0, 320, 200);
+		g_system->updateScreen();
+	};
+
+	draw();
+	while (!shouldQuit()) {
+		Common::Event ev;
+		bool dirty = false;
+		bool exit  = false;
+		while (g_system->getEventManager()->pollEvent(ev)) {
+			if (ev.type == Common::EVENT_QUIT ||
+				ev.type == Common::EVENT_RETURN_TO_LAUNCHER) { exit = true; break; }
+			if (ev.type == Common::EVENT_KEYDOWN) {
+				if (ev.kbd.keycode == Common::KEYCODE_ESCAPE) { exit = true; break; }
+				if (ev.kbd.keycode >= Common::KEYCODE_1 && ev.kbd.keycode <= Common::KEYCODE_9) {
+					const int slot = (int)(ev.kbd.keycode - Common::KEYCODE_1);
+					Common::Array<uint> found;
+					for (uint i = 0; i < Mystery::kCluesFoundCap; i++)
+						if (_mystery._cluesFound[i])
+							found.push_back(i);
+					const int idx = page * kPerPage + slot;
+					if (idx < (int)found.size()) {
+						const uint clueId = found[idx];
+						_mystery._noteSelected[clueId] ^= 1;
+						dirty = true;
+					}
+				} else if (ev.kbd.keycode == Common::KEYCODE_TAB ||
+						   ev.kbd.keycode == Common::KEYCODE_RIGHT) {
+					page++; dirty = true;
+				} else if (ev.kbd.keycode == Common::KEYCODE_LEFT) {
+					if (page > 0) page--;
+					dirty = true;
+				}
+			}
+		}
+		if (exit) break;
+		if (dirty) draw();
+		g_system->delayMillis(15);
+	}
+}
+
+void EEMEngine::doGallery() {
+	// Mirrors `_DrawGallery` @ 158f:0046. The original loops `_NumSuspects`
+	// gallery entries (0x46 = 70 bytes each in `_GalleryData`); the first
+	// u16 of each entry is the PIC picture ID for that suspect. We render
+	// them in a row across the screen.
+	if (!_mystery.isLoaded())
+		return;
+
+	const byte *gd = _mystery.galleryData();
+	if (!gd) {
+		warning("doGallery: no GalleryData in mystery %u", _mystery.number());
+		return;
+	}
+
+	Graphics::ManagedSurface scratch(320, 200,
+		Graphics::PixelFormat::createFormatCLUT8());
+	scratch.clear();
+
+	// Use PIC 0x3f as the gallery backdrop, matching `_DoAccuseGallery`.
+	Picture galBg;
+	if (_picsArchive.getPicture(0x3f, galBg)) {
+		const int w = MIN<int>(galBg.surface.w, 320);
+		const int h = MIN<int>(galBg.surface.h, 200);
+		for (int row = 0; row < h; row++) {
+			memcpy((byte *)scratch.getBasePtr(0, row),
+				   (const byte *)galBg.surface.getBasePtr(0, row), w);
+		}
+	}
+
+	if (_font.isLoaded())
+		_font.drawString(&scratch, 8, 4, "GALLERY", 0xF);
+
+	const uint8 num = _mystery.numSuspects();
+	int slotX = 8;
+	const int slotY = 24;
+	const int slotStep = 320 / MAX<uint8>(1, num);
+	for (uint i = 0; i < num; i++) {
+		const uint16 picId = READ_LE_UINT16(gd + i * 0x46);
+		if (picId == 0)
+			continue;
+		Picture portrait;
+		if (!_picsArchive.getPicture(picId, portrait))
+			continue;
+		const int placeX = slotX + (slotStep - portrait.surface.w) / 2;
+		const int placeY = slotY;
+		const int w = MIN<int>(portrait.surface.w, 320 - placeX);
+		const int h = MIN<int>(portrait.surface.h, 200 - placeY);
+		if (w > 0 && h > 0) {
+			for (int row = 0; row < h; row++) {
+				memcpy((byte *)scratch.getBasePtr(placeX, placeY + row),
+					   (const byte *)portrait.surface.getBasePtr(0, row), w);
+			}
+		}
+		// Suspect number + discovered marker under the portrait.
+		if (_font.isLoaded()) {
+			const bool discovered = (i < Mystery::kGalleryCap) &&
+									_mystery._inGallery[i];
+			Common::String label = Common::String::format("%u%s",
+				i + 1, discovered ? " *" : "");
+			_font.drawString(&scratch, placeX + 4, placeY + h + 2, label, 0xF);
+		}
+		slotX += slotStep;
+	}
+
+	g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
+							   0, 0, 320, 200);
 	g_system->updateScreen();
-	g_system->delayMillis(10);
-	return true;
+	waitForInput(60000);
+}
+
+void EEMEngine::doBigMap() {
+	// Mirrors `_DoBigMap` @ 20fe:09e7. The original lays out a 0xe9 x 0xab
+	// map window inside frame PIC 0x42 with per-site clickable markers
+	// scrolling under it. We render BIGMAP.PIC at top-left, draw an
+	// overlay listing the sites that the player can travel to (per
+	// `_OnSites`), and accept either number keys or clicks on the
+	// overlay rows to travel.
+	Common::File f;
+	if (!f.open(Common::Path("BIGMAP.PIC"))) {
+		warning("doBigMap: BIGMAP.PIC missing");
+		return;
+	}
+	const uint16 mapH = f.readUint16LE();
+	const uint16 mapW = f.readUint16LE();
+	if (mapW == 0 || mapH == 0)
+		return;
+
+	Common::Array<byte> mapPixels((uint32)mapW * mapH);
+	if (f.read(mapPixels.data(), mapPixels.size()) != mapPixels.size()) {
+		warning("doBigMap: short read on BIGMAP.PIC");
+		return;
+	}
+
+	// Approximate inner map window from the original `_DoBigMap`:
+	//   if (sx < 0x75) sx = 0; else sx -= 0x74;     // 0x74 = 116
+	//   if (mapW < sx + 0xe9) sx = mapW - 0xe9;     // window width  = 233
+	//   if (sy < 0x56) sy = 0; else sy -= 0x55;
+	//   if (mapH < sy + 0xab) sy = mapH - 0xab;     // window height = 171
+	const int kMapWinW = 0xe9; // 233
+	const int kMapWinH = 0xab; // 171
+	const int kMapWinX = 4;
+	const int kMapWinY = 4;
+
+	int scrollX = 0;
+	int scrollY = 0;
+
+	// Auto-scroll to centre the current site, if known.
+	if (_mystery.isLoaded()) {
+		const byte *entry = _mystery.mapEntry(_mystery._siteNumber);
+		if (entry) {
+			const uint16 mx = READ_LE_UINT16(entry + 4);
+			const uint16 my = READ_LE_UINT16(entry + 6);
+			scrollX = MAX<int>(0, MIN<int>(mapW - kMapWinW,
+				(int)mx - kMapWinW / 2));
+			scrollY = MAX<int>(0, MIN<int>(mapH - kMapWinH,
+				(int)my - kMapWinH / 2));
+		}
+	}
+
+	auto draw = [&]() {
+		Graphics::ManagedSurface scratch(320, 200,
+			Graphics::PixelFormat::createFormatCLUT8());
+		scratch.clear();
+
+		// Frame from PIC 0x42 (`_GetPicture(0x42)` in `_DoBigMap`).
+		Picture frame;
+		if (_picsArchive.getPicture(0x42, frame)) {
+			const int w = MIN<int>(frame.surface.w, 320);
+			const int h = MIN<int>(frame.surface.h, 200);
+			for (int row = 0; row < h; row++) {
+				memcpy((byte *)scratch.getBasePtr(0, row),
+					   (const byte *)frame.surface.getBasePtr(0, row), w);
+			}
+		}
+
+		// Map content clipped into the inner window, applying scroll.
+		const int copyW = MIN<int>(mapW - scrollX, kMapWinW);
+		const int copyH = MIN<int>(mapH - scrollY, kMapWinH);
+		for (int row = 0; row < copyH; row++) {
+			memcpy((byte *)scratch.getBasePtr(kMapWinX, kMapWinY + row),
+				   mapPixels.data() + (scrollY + row) * mapW + scrollX,
+				   copyW);
+		}
+
+		// Site markers from MapData. Each per-mystery MapData entry is
+		// 14 bytes; bytes +4..+5 / +6..+7 hold an (x, y) pair on the big
+		// map. We draw a small filled square for each accessible site.
+		if (_mystery.isLoaded()) {
+			for (uint i = 0; i < _mystery.numSites(); i++) {
+				if (!_mystery._onSites[i] && i != _mystery._siteNumber)
+					continue;
+				const byte *entry = _mystery.mapEntry(i);
+				if (!entry)
+					continue;
+				const uint16 mx = READ_LE_UINT16(entry + 4);
+				const uint16 my = READ_LE_UINT16(entry + 6);
+				const int sx = (int)mx - scrollX + kMapWinX;
+				const int sy = (int)my - scrollY + kMapWinY;
+				if (sx < kMapWinX || sx >= kMapWinX + kMapWinW ||
+					sy < kMapWinY || sy >= kMapWinY + kMapWinH)
+					continue;
+				const byte color = (i == _mystery._siteNumber) ? 0x0E : 0x0F;
+				const Common::Rect mark(sx - 2, sy - 2, sx + 3, sy + 3);
+				scratch.fillRect(mark, color);
+				if (_font.isLoaded()) {
+					Common::String num = Common::String::format("%u", i);
+					_font.drawString(&scratch, sx + 4, sy - 4, num, color);
+				}
+			}
+		}
+
+		// Travel-target overlay (right-side panel).
+		if (_font.isLoaded() && _mystery.isLoaded()) {
+			const int panelX = kMapWinX + kMapWinW + 4;
+			int y = 4;
+			_font.drawString(&scratch, panelX, y, "TRAVEL", 0xF);
+			y += _font.height() + 4;
+			for (uint i = 0; i < _mystery.numSites() && y < 192; i++) {
+				if (!_mystery._onSites[i] && i != _mystery._siteNumber)
+					continue;
+				const char marker = (i == _mystery._siteNumber) ? '>' : ' ';
+				Common::String label = Common::String::format(
+					"%c %u", marker, i);
+				_font.drawString(&scratch, panelX, y, label, 0x0F);
+				y += _font.height() + 1;
+			}
+			_font.drawString(&scratch, panelX, 188,
+							 "Esc", 0x0F);
+		}
+
+		g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
+								   0, 0, 320, 200);
+		g_system->updateScreen();
+	};
+	draw();
+
+	while (!shouldQuit()) {
+		Common::Event ev;
+		bool dirty = false;
+		while (g_system->getEventManager()->pollEvent(ev)) {
+			if (ev.type == Common::EVENT_QUIT ||
+				ev.type == Common::EVENT_RETURN_TO_LAUNCHER)
+				return;
+			if (ev.type == Common::EVENT_KEYDOWN) {
+				if (ev.kbd.keycode == Common::KEYCODE_ESCAPE)
+					return;
+				const int kStep = 16;
+				if (ev.kbd.keycode == Common::KEYCODE_LEFT) {
+					scrollX = MAX<int>(0, scrollX - kStep);
+					dirty = true;
+				} else if (ev.kbd.keycode == Common::KEYCODE_RIGHT) {
+					scrollX = MIN<int>(MAX<int>(0, mapW - kMapWinW), scrollX + kStep);
+					dirty = true;
+				} else if (ev.kbd.keycode == Common::KEYCODE_UP) {
+					scrollY = MAX<int>(0, scrollY - kStep);
+					dirty = true;
+				} else if (ev.kbd.keycode == Common::KEYCODE_DOWN) {
+					scrollY = MIN<int>(MAX<int>(0, mapH - kMapWinH), scrollY + kStep);
+					dirty = true;
+				}
+				if (ev.kbd.keycode >= Common::KEYCODE_0 &&
+					ev.kbd.keycode <= Common::KEYCODE_9) {
+					const uint target = (uint)(ev.kbd.keycode - Common::KEYCODE_0);
+					if (_mystery.isLoaded() &&
+						target < _mystery.numSites() &&
+						_mystery._onSites[target]) {
+						_mystery._lastSite = _mystery._siteNumber;
+						_mystery._siteNumber = (uint16)target;
+						return;
+					}
+				}
+			}
+			if (ev.type == Common::EVENT_LBUTTONDOWN) {
+				// Click on a map marker?
+				if (_mystery.isLoaded() &&
+					ev.mouse.x >= kMapWinX &&
+					ev.mouse.x < kMapWinX + kMapWinW &&
+					ev.mouse.y >= kMapWinY &&
+					ev.mouse.y < kMapWinY + kMapWinH) {
+					for (uint i = 0; i < _mystery.numSites(); i++) {
+						if (!_mystery._onSites[i] &&
+							i != _mystery._siteNumber)
+							continue;
+						const byte *entry = _mystery.mapEntry(i);
+						if (!entry) continue;
+						const uint16 mx = READ_LE_UINT16(entry + 4);
+						const uint16 my = READ_LE_UINT16(entry + 6);
+						const int sx = (int)mx - scrollX + kMapWinX;
+						const int sy = (int)my - scrollY + kMapWinY;
+						if (ABS(ev.mouse.x - sx) <= 5 &&
+							ABS(ev.mouse.y - sy) <= 5) {
+							_mystery._lastSite = _mystery._siteNumber;
+							_mystery._siteNumber = (uint16)i;
+							return;
+						}
+					}
+				}
+
+				// Click in the right panel: travel to that row.
+				const int panelX = kMapWinX + kMapWinW + 4;
+				if (_font.isLoaded() && _mystery.isLoaded() &&
+					ev.mouse.x >= panelX) {
+					const int row = (ev.mouse.y - 4 - _font.height() - 4) /
+									(_font.height() + 1);
+					int seen = 0;
+					for (uint i = 0; i < _mystery.numSites(); i++) {
+						if (!_mystery._onSites[i] && i != _mystery._siteNumber)
+							continue;
+						if (seen == row) {
+							_mystery._lastSite = _mystery._siteNumber;
+							_mystery._siteNumber = (uint16)i;
+							return;
+						}
+						seen++;
+					}
+				}
+			}
+		}
+		if (dirty)
+			draw();
+		g_system->delayMillis(10);
+	}
+}
+
+void EEMEngine::doHelp() {
+	// `_KDHelp` reads two hint TextBlock offsets from `_KDTextIndex`:
+	//   word @ +0xe : first-time hint
+	//   word @ +0x10: second-time hint (cycles back to first if missing)
+	// `_SawHelpHint` toggles between them.
+	if (!_mystery.isLoaded() || !_font.isLoaded())
+		return;
+
+	const byte *kd = _mystery.kdTextIndex();
+	if (!kd)
+		return;
+
+	const uint16 hintFirst  = READ_LE_UINT16(kd + 0x0e);
+	const uint16 hintSecond = READ_LE_UINT16(kd + 0x10);
+	uint16 use = _mystery._sawHelpHint && hintSecond != 0xFFFF ? hintSecond : hintFirst;
+	if (use == 0xFFFF) {
+		debugC(1, kDebugScript, "doHelp: no hint configured");
+		return;
+	}
+	if (!_mystery._sawHelpHint && hintFirst != 0xFFFF)
+		_mystery._sawHelpHint = true;
+
+	const Common::String raw = _mystery.textAt(use);
+	const Common::String partnerName = (_partner == 0) ? "Jake" : "Jennifer";
+	const Common::String text = parseString(raw, _playerName, partnerName);
+
+	Graphics::ManagedSurface scratch(320, 200,
+		Graphics::PixelFormat::createFormatCLUT8());
+	scratch.clear();
+	_font.drawString(&scratch, 8, 4, "HELP", 0xF);
+	_font.drawWordWrapped(&scratch, 8, 24, 304, text, 0xF);
+	g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
+							   0, 0, 320, 200);
+	g_system->updateScreen();
+	waitForInput(60000);
+}
+
+bool EEMEngine::areYouSure() {
+	// Mirrors `_AreYouSure` @ 1a35:0a5c. Original loads PIC 0x136 for the
+	// dialog body and PIC 0x1FD/0x1FE for YES/NO. We render a minimal
+	// text dialog that preserves the screen behind it.
+	if (!_font.isLoaded())
+		return true;
+
+	Graphics::Surface *screen = g_system->lockScreen();
+	Graphics::ManagedSurface saved(320, 200,
+		Graphics::PixelFormat::createFormatCLUT8());
+	if (screen) {
+		for (int row = 0; row < 200; row++) {
+			memcpy((byte *)saved.getBasePtr(0, row),
+				   (const byte *)screen->getBasePtr(0, row), 320);
+		}
+		g_system->unlockScreen();
+	}
+
+	const Common::Rect dlg(60, 70, 260, 140);
+	Graphics::ManagedSurface scratch(320, 200,
+		Graphics::PixelFormat::createFormatCLUT8());
+	for (int row = 0; row < 200; row++)
+		memcpy((byte *)scratch.getBasePtr(0, row),
+			   (const byte *)saved.getBasePtr(0, row), 320);
+	scratch.fillRect(dlg, 0);
+	scratch.frameRect(dlg, 0xF);
+	_font.drawString(&scratch, dlg.left + 8, dlg.top + 8,
+		"Are you sure you want to quit?", 0xF);
+	_font.drawString(&scratch, dlg.left + 16, dlg.top + 36, "Y - Yes", 0xF);
+	_font.drawString(&scratch, dlg.left + 100, dlg.top + 36, "N - No", 0xF);
+	g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
+							   0, 0, 320, 200);
+	g_system->updateScreen();
+
+	bool result = false;
+	while (!shouldQuit()) {
+		Common::Event ev;
+		bool decided = false;
+		while (g_system->getEventManager()->pollEvent(ev)) {
+			if (ev.type == Common::EVENT_QUIT ||
+				ev.type == Common::EVENT_RETURN_TO_LAUNCHER) {
+				result = true;
+				decided = true;
+				break;
+			}
+			if (ev.type == Common::EVENT_KEYDOWN) {
+				if (ev.kbd.keycode == Common::KEYCODE_y ||
+					ev.kbd.keycode == Common::KEYCODE_RETURN) {
+					result = true; decided = true; break;
+				}
+				if (ev.kbd.keycode == Common::KEYCODE_n ||
+					ev.kbd.keycode == Common::KEYCODE_ESCAPE) {
+					result = false; decided = true; break;
+				}
+			}
+		}
+		if (decided)
+			break;
+		g_system->delayMillis(15);
+	}
+
+	// Restore the screen so the caller's UI is intact.
+	g_system->copyRectToScreen(saved.getPixels(), saved.pitch, 0, 0, 320, 200);
+	g_system->updateScreen();
+	return result;
+}
+
+void EEMEngine::doAccuse() {
+	if (!_mystery.isLoaded())
+		return;
+
+	// Mirrors `_DoAccuseGallery` @ 1df2:0a31. Render gallery + prompt,
+	// accept either keyboard 1..N or a click on a suspect's portrait.
+	const uint8 num = _mystery.numSuspects();
+	if (num == 0)
+		return;
+
+	const byte *gd = _mystery.galleryData();
+	const int slotStep = 320 / MAX<uint8>(1, num);
+	const int slotY    = 24;
+
+	// Mirrors `_DoAccuseGallery`: load PIC 0x3f as the accuse backdrop.
+	Picture accuseBg;
+	const bool haveAccuseBg = _picsArchive.getPicture(0x3f, accuseBg);
+
+	auto drawGallery = [&]() {
+		Graphics::ManagedSurface scratch(320, 200,
+			Graphics::PixelFormat::createFormatCLUT8());
+		scratch.clear();
+		if (haveAccuseBg) {
+			const int w = MIN<int>(accuseBg.surface.w, 320);
+			const int h = MIN<int>(accuseBg.surface.h, 200);
+			for (int row = 0; row < h; row++) {
+				memcpy((byte *)scratch.getBasePtr(0, row),
+					   (const byte *)accuseBg.surface.getBasePtr(0, row), w);
+			}
+		}
+		if (_font.isLoaded())
+			_font.drawString(&scratch, 8, 4, "ACCUSE", 0xF);
+
+		for (uint i = 0; i < num; i++) {
+			if (!gd) continue;
+			const uint16 picId = READ_LE_UINT16(gd + i * 0x46);
+			if (picId == 0)
+				continue;
+			Picture portrait;
+			if (!_picsArchive.getPicture(picId, portrait))
+				continue;
+			const int placeX = i * slotStep +
+							   (slotStep - portrait.surface.w) / 2;
+			const int placeY = slotY;
+			const int w = MIN<int>(portrait.surface.w, 320 - placeX);
+			const int h = MIN<int>(portrait.surface.h, 200 - placeY);
+			if (w > 0 && h > 0) {
+				for (int row = 0; row < h; row++)
+					memcpy((byte *)scratch.getBasePtr(placeX, placeY + row),
+						   (const byte *)portrait.surface.getBasePtr(0, row), w);
+			}
+			if (_font.isLoaded()) {
+				Common::String label = Common::String::format("%u", i + 1);
+				_font.drawString(&scratch, placeX + 4,
+					placeY + h + 2, label, 0xF);
+			}
+		}
+		if (_font.isLoaded()) {
+			_font.drawString(&scratch, 8, 180,
+				"Click a suspect or press 1..N - ESC to cancel", 0xF);
+		}
+		g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
+								   0, 0, 320, 200);
+		g_system->updateScreen();
+	};
+	drawGallery();
+
+	int picked = -1;
+	while (picked < 0 && !shouldQuit()) {
+		Common::Event ev;
+		while (g_system->getEventManager()->pollEvent(ev)) {
+			if (ev.type == Common::EVENT_QUIT ||
+				ev.type == Common::EVENT_RETURN_TO_LAUNCHER)
+				return;
+			if (ev.type == Common::EVENT_KEYDOWN) {
+				if (ev.kbd.keycode == Common::KEYCODE_ESCAPE)
+					return;
+				const int k = (int)ev.kbd.keycode;
+				if (k >= Common::KEYCODE_1 && k <= Common::KEYCODE_9) {
+					const int idx = k - Common::KEYCODE_1;
+					if (idx < num)
+						picked = idx;
+				}
+			}
+			if (ev.type == Common::EVENT_LBUTTONDOWN) {
+				const int slot = ev.mouse.x / slotStep;
+				if (slot >= 0 && slot < (int)num &&
+					ev.mouse.y >= slotY && ev.mouse.y < slotY + 120)
+					picked = slot;
+			}
+		}
+		g_system->delayMillis(10);
+	}
+	if (picked < 0)
+		return;
+
+	// Real chain evaluation: sum point values of clues the player marked
+	// "selected" in the notebook. Mirrors `_SolvedCheck` @ 1df2:00ec.
+	const int points = _mystery.selectedPoints();
+	const bool guessedRight = _mystery.solvedCheck();
+	debugC(1, kDebugScript, "doAccuse: picked=%d selectedPts=%d -> %s",
+		   picked, points, guessedRight ? "correct" : "wrong");
+
+	// If the player hasn't marked any evidence yet, give them a hint
+	// rather than an instant fail. Mirrors the original "We're not ready
+	// to solve this mystery yet..." string at 29be:10f0.
+	if (points == 0 && _font.isLoaded()) {
+		Graphics::ManagedSurface scratch(320, 200,
+			Graphics::PixelFormat::createFormatCLUT8());
+		scratch.clear();
+		_font.drawWordWrapped(&scratch, 16, 80, 288,
+			"We're not ready to solve this mystery yet. "
+			"Let's keep investigating until we have some "
+			"more solid evidence to make our case! "
+			"(Press N in the site screen to mark clues.)",
+			0xF);
+		g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
+								   0, 0, 320, 200);
+		g_system->updateScreen();
+		waitForInput(15000);
+		return;
+	}
+
+	// Pick the ending based on the chain. For a correct accusation the
+	// original would call `_DisplayClue(_Mystery + AChain[0])`, play
+	// SCRAPBK.ANI and save progress. We load the matching `E<n>.BIN`
+	// ending text and render its pages with prev/next navigation.
+	const int endingNum = guessedRight ? picked : 0;
+	const Common::String fname = Common::String::format("E%d.BIN", endingNum);
+	Common::File f;
+	if (!f.open(Common::Path(fname))) {
+		warning("doAccuse: %s missing", fname.c_str());
+		return;
+	}
+
+	// E<n>.BIN format (verified against `_DisplayEndingPage` @ 1df2:044c):
+	//   u16 numPages
+	//   per page (10 bytes header + NUL-string):
+	//     u16 picNum
+	//     u16 x1, y1, x2, y2  (story rect)
+	//     bytes[] NUL-terminated text
+	const uint32 fileLen = f.size();
+	Common::Array<byte> blob(fileLen);
+	if (f.read(blob.data(), fileLen) != fileLen)
+		return;
+	const byte *e = blob.data();
+	const uint16 pages = READ_LE_UINT16(e);
+
+	const Common::String partnerName = (_partner == 0) ? "Jake" : "Jennifer";
+	uint pageIdx = 0;
+
+	while (!shouldQuit()) {
+		// Walk to pageIdx.
+		uint pos = 2;
+		uint cur = 0;
+		while (cur < pageIdx && pos + 10 < fileLen) {
+			const char *t = (const char *)(e + pos + 10);
+			pos += 10 + strlen(t) + 1;
+			cur++;
+		}
+		if (pos + 10 >= fileLen)
+			break;
+
+		const uint16 picNum = READ_LE_UINT16(e + pos + 0);
+		const uint16 x1     = READ_LE_UINT16(e + pos + 2);
+		const uint16 y1     = READ_LE_UINT16(e + pos + 4);
+		const uint16 x2     = READ_LE_UINT16(e + pos + 6);
+		const uint16 y2     = READ_LE_UINT16(e + pos + 8);
+		const char *raw     = (const char *)(e + pos + 10);
+		const Common::String txt = parseString(raw, _playerName, partnerName);
+
+		Graphics::ManagedSurface scratch(320, 200,
+			Graphics::PixelFormat::createFormatCLUT8());
+		scratch.clear();
+
+		// Page background.
+		if (picNum != 0) {
+			Picture bg;
+			if (_picsArchive.getPicture(picNum, bg)) {
+				const int w = MIN<int>(bg.surface.w, 320);
+				const int h = MIN<int>(bg.surface.h, 200);
+				for (int row = 0; row < h; row++) {
+					memcpy((byte *)scratch.getBasePtr(0, row),
+						   (const byte *)bg.surface.getBasePtr(0, row), w);
+				}
+			}
+		}
+
+		if (_font.isLoaded()) {
+			Common::String banner = "Not enough evidence";
+			if (guessedRight)
+				banner = _mystery._firstTry ? "CORRECT - FIRST TRY!" : "CORRECT!";
+			_font.drawString(&scratch, 8, 4, banner, 0xF);
+			_font.drawString(&scratch, 8, 16,
+				Common::String::format("Evidence: %d/100  Suspect: %d",
+									   points, picked + 1), 0xF);
+			const int wrapW = MAX<int>(16, x2 - x1);
+			const int wrapY = MAX<int>(28, (int)y1);
+			(void)y2;
+			_font.drawWordWrapped(&scratch, x1, wrapY, wrapW, txt, 0xF);
+			_font.drawString(&scratch, 8, 188,
+				Common::String::format("page %u/%u  (Left/Right or click)",
+									   pageIdx + 1, pages), 0xF);
+		}
+
+		g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
+								   0, 0, 320, 200);
+		g_system->updateScreen();
+
+		// Page navigation.
+		bool advance = false;
+		bool back    = false;
+		bool exit    = false;
+		while (!advance && !back && !exit && !shouldQuit()) {
+			Common::Event ev;
+			while (g_system->getEventManager()->pollEvent(ev)) {
+				if (ev.type == Common::EVENT_QUIT ||
+					ev.type == Common::EVENT_RETURN_TO_LAUNCHER) {
+					exit = true; break;
+				}
+				if (ev.type == Common::EVENT_LBUTTONDOWN) {
+					advance = true; break;
+				}
+				if (ev.type == Common::EVENT_KEYDOWN) {
+					if (ev.kbd.keycode == Common::KEYCODE_ESCAPE)
+						exit = true;
+					else if (ev.kbd.keycode == Common::KEYCODE_LEFT)
+						back = true;
+					else
+						advance = true;
+					break;
+				}
+			}
+			g_system->delayMillis(15);
+		}
+		if (exit) break;
+		if (advance) {
+			if (pageIdx + 1 >= pages) break;
+			pageIdx++;
+		} else if (back) {
+			if (pageIdx > 0) pageIdx--;
+		}
+	}
+
+	// Mirror `_DisplayCorrect`'s scrap-book animation + solved tracking +
+	// auto-save (the original calls `_SavePlayerRecord` after a win).
+	if (guessedRight) {
+		const uint mn = _mystery.number();
+		if (mn < sizeof(_mysteriesSolved)) {
+			_mysteriesSolved[mn] = _mystery._firstTry ? 2 : 1;
+		}
+		playAnm(Common::Path("SCRAPBK.ANI"), 120, true);
+
+		// Auto-save into slot 0 (the engine's quicksave slot).
+		const Common::String desc = Common::String::format(
+			"%s ??? solved mystery %u", _playerName.c_str(), mn);
+		Common::Error err = saveGameState(0, desc, true);
+		if (err.getCode() != Common::kNoError)
+			warning("auto-save after solve failed: %s",
+					err.getDesc().c_str());
+	} else {
+		_mystery._firstTry = false;
+	}
+}
+
+// -------------------- save / load --------------------
+
+namespace {
+const uint32 kSaveMagic = MKTAG('E', 'E', 'M', '0');
+const byte   kSaveVer   = 3;  ///< v2: _mysteriesSolved tracker; v3: player name
+} // anonymous namespace
+
+bool EEMEngine::hasFeature(EngineFeature f) const {
+	// We support saving any time but loading only at startup (via the
+	// `--save-slot=N` resume path or a slot picked from the launcher).
+	// Runtime loads would replace `_mystery._data` while pointers into
+	// it are alive on the stack inside `displayClue` etc.
+	return f == kSupportsSavingDuringRuntime ||
+		   f == kSupportsReturnToLauncher;
+}
+
+bool EEMEngine::canLoadGameStateCurrently(Common::U32String *) {
+	return false;  // Loading is startup-only.
+}
+
+bool EEMEngine::canSaveGameStateCurrently(Common::U32String *) {
+	return _mystery.isLoaded();
+}
+
+Common::Error EEMEngine::saveGameState(int slot, const Common::String &desc, bool isAutosave) {
+	Common::OutSaveFile *out = getSaveFileManager()->openForSaving(getSaveStateName(slot));
+	if (!out)
+		return Common::kCreatingFileFailed;
+
+	out->writeUint32BE(kSaveMagic);
+	out->writeByte(kSaveVer);
+
+	// Header: description + ScummVM extended save metadata are appended
+	// automatically when `EngineFeature::kSavesUseExtendedFormat` is set;
+	// our save body just carries the engine state.
+	(void)desc;
+	(void)isAutosave;
+
+	uint16 mysteryNum = (uint16)_mystery.number();
+	out->writeUint16LE(mysteryNum);
+	out->writeByte(_partner);
+	out->write(_mysteriesSolved, sizeof(_mysteriesSolved));
+
+	// v3: persist the player name so save-slot resume restores it.
+	out->writeUint16LE((uint16)_playerName.size());
+	out->writeString(_playerName);
+
+	debugC(1, kDebugGeneral,
+		   "Saved slot %d: mystery=%u partner=%u name=%s autosave=%d",
+		   slot, mysteryNum, _partner, _playerName.c_str(), isAutosave ? 1 : 0);
+
+	Common::Serializer s(nullptr, out);
+	s.setVersion(kSaveVer);
+	_mystery.syncState(s);
+
+	out->finalize();
+	delete out;
+	return Common::kNoError;
+}
+
+Common::Error EEMEngine::loadGameState(int slot) {
+	Common::InSaveFile *in = getSaveFileManager()->openForLoading(getSaveStateName(slot));
+	if (!in)
+		return Common::kReadingFailed;
+
+	if (in->readUint32BE() != kSaveMagic) {
+		delete in;
+		return Common::kUnknownError;
+	}
+	const byte ver = in->readByte();
+	if (ver > kSaveVer) {
+		delete in;
+		return Common::kUnknownError;
+	}
+
+	const uint16 mysteryNum = in->readUint16LE();
+	_partner = in->readByte();
+	if (ver >= 2)
+		in->read(_mysteriesSolved, sizeof(_mysteriesSolved));
+	else
+		memset(_mysteriesSolved, 0, sizeof(_mysteriesSolved));
+
+	if (ver >= 3) {
+		const uint16 nameLen = in->readUint16LE();
+		Common::String name;
+		for (uint16 i = 0; i < nameLen && i < 64; i++)
+			name += (char)in->readByte();
+		_playerName = name.empty() ? Common::String("Detective") : name;
+	}
+
+	if (!_mystery.load(mysteryNum, &_rng)) {
+		_mystery.clear();
+		delete in;
+		return Common::kReadingFailed;
+	}
+
+	Common::Serializer s(in, nullptr);
+	s.setVersion(ver);
+	_mystery.syncState(s);
+
+	delete in;
+	debugC(1, kDebugGeneral,
+		   "Loaded slot %d: mystery=%u partner=%u name=%s",
+		   slot, mysteryNum, _partner, _playerName.c_str());
+	return Common::kNoError;
+}
+
+void EEMEngine::screenDriver() {
+	// Placeholder for the eventual dispatch table (one entry per ScreenId).
+	// run() currently calls handlers directly until the title path lands.
 }
 
 } // End of namespace EEM
diff --git a/engines/eem/eem.h b/engines/eem/eem.h
index 67a4b0967db..b73ef2ac2bc 100644
--- a/engines/eem/eem.h
+++ b/engines/eem/eem.h
@@ -24,6 +24,7 @@
 #ifndef EEM_EEM_H
 #define EEM_EEM_H
 
+#include "common/array.h"
 #include "common/platform.h"
 #include "common/random.h"
 #include "common/scummsys.h"
@@ -31,21 +32,22 @@
 #include "engines/advancedDetector.h"
 #include "engines/engine.h"
 
+#include "eem/animation.h"
+#include "eem/font.h"
+#include "eem/mystery.h"
 #include "eem/resource.h"
 
 namespace EEM {
 
-class Console;
-
 /**
  * Screen IDs used by the original ScreenDriver dispatch table at 1a35:0e5e.
  * The table holds 14 (id, handler) entries; the loop iterates until it finds
  * a matching id and calls its handler. ID 0xFFFF is the exit sentinel.
  */
 enum ScreenId {
-	kScreenInvalid    = 0xFFFF,
-	kScreenTitle      = 0x0B,  ///< _ShowTitlePage @ 1a35:06b7
-	kScreenNext       = 0x08   ///< follow-up after title (case selection); to be confirmed
+	kScreenInvalid       = 0xFFFF,
+	kScreenChoosePartner = 0x09,  ///< _DoChoosePartner @ 1a35:0756 (boy/girl picker)
+	kScreenTitle         = 0x0B   ///< _ShowTitlePage @ 1a35:06b7
 };
 
 class EEMEngine : public Engine {
@@ -58,9 +60,69 @@ public:
 	const char *getGameId() const;
 	Common::Platform getPlatform() const;
 
+	bool hasFeature(EngineFeature f) const override;
+	bool canLoadGameStateCurrently(Common::U32String *msg = nullptr) override;
+	bool canSaveGameStateCurrently(Common::U32String *msg = nullptr) override;
+	Common::Error loadGameState(int slot) override;
+	Common::Error saveGameState(int slot, const Common::String &desc, bool isAutosave) override;
+
 	const ADGameDescription *_gameDescription;
 
-	DBDArchive &getPics() { return _picsArchive; }
+	DBDArchive &getPics()    { return _picsArchive; }
+	DBDArchive &getAni()     { return _aniArchive; }
+	DBDArchive &getSites()   { return _sitesArchive; }
+	DBDArchive &getBalloons(){ return _balloonArchive; }
+	Mystery    &getMystery() { return _mystery; }
+	const EEMFont &getFont() const { return _font; }
+
+	/// Display one ClueBlock. @p clueBlock points at the u16 frame count
+	/// followed by 62-byte ClueEntries. Mirrors _DisplayClue @ 2404:05e6.
+	void displayClue(const byte *clueBlock);
+
+	/// Apply a single ClueEntry's side effects ??? notebook adds, gallery
+	/// updates, site flags. Called both by `displayClue` after a normal
+	/// click-through and when the player ESC-skips a multi-entry clue.
+	void applyClueSideEffects(const byte *entry);
+
+	/// Show clue/notebook screen. Mirrors `_DrawNotes` @ 161e:01d0.
+	void doNotebook();
+
+	/// Show suspect gallery. Mirrors `_DrawGallery` @ 158f:0046.
+	void doGallery();
+
+	/// Show big map; click chooses next site. Mirrors `_DoBigMap` @ 20fe:09e7.
+	void doBigMap();
+
+	/// Run the accuse flow (pick suspect, evaluate chains, show ending).
+	/// Mirrors `_DoAccuseGallery` @ 1df2:0a31 + `_DisplayEnding` @ 1df2:0548.
+	void doAccuse();
+
+	/// Show a host hint from `KDTextIndex`. Mirrors `_KDHelp` @ 1560:010a +
+	/// `_DisplayHint` @ 1560:0009. Cycles between the two hint slots that
+	/// the original engine tracks via `_SawHelpHint`.
+	void doHelp();
+
+	/// "Are you sure?" yes/no dialog. Mirrors `_AreYouSure` @ 1a35:0a5c.
+	/// Returns true if the user picked YES.
+	bool areYouSure();
+
+private:
+	/**
+	 * Central dispatch loop matching the original _ScreenDriver @ 1a35:0dc1.
+	 * Each iteration calls the screen handler that matches _nextScreen.
+	 * Handlers update _lastScreen / _nextScreen and return; the loop exits
+	 * when _nextScreen == kScreenInvalid.
+	 */
+	void screenDriver();
+
+	/**
+	 * Open the five .DBD/.DBX archive pairs the way _InitGraphicsSystem
+	 * @ 172b:0145 does at boot.
+	 */
+	bool openArchives();
+
+	/** Slurp SITEPALS into @c _sitePals. Mirrors _ReadPalettes @ 172b:0d89. */
+	bool loadSitePalettes();
 
 	/**
 	 * Upload palette index @p num (one of 40 stored in SITEPALS) to the
@@ -69,28 +131,73 @@ public:
 	 */
 	void setSitePalette(uint num);
 
-	/** Blit @p pic to the screen at (0,0), expecting a 320x200 picture. */
-	void blitFullScreen(const Picture &pic);
+	/**
+	 * Upload a 6-bit VGA palette read from the head of an .ANM file (the
+	 * first 0x300 bytes per Load_Sequence @ 2503:0006). Used until the
+	 * full title-page animation chain is wired in.
+	 */
+	bool setAnmPalette(const Common::Path &anmPath);
 
+public:
+	/// Public so SiteScreen can switch palettes per site.
+	void setSitePaletteForSite(uint siteNum) { setSitePalette(siteNum + 1); }
 private:
+
+	/** Blit @p pic to @p x, @p y on screen. */
+	void blitAt(const Picture &pic, int x, int y);
+
+	/** Hold the current frame for up to @p maxMs or until the user inputs. */
+public:
+	void waitForInput(uint32 maxMs);
+private:
+
 	/**
-	 * Central dispatch loop matching the original _ScreenDriver @ 1a35:0dc1.
-	 * Each iteration restores video mode and calls the screen handler that
-	 * matches _nextScreen. Handlers update _lastScreen / _nextScreen and
-	 * return; the loop exits when _nextScreen == kScreenInvalid.
+	 * Play a difference-encoded animation file (.ANM / .A) on the full
+	 * 320x200 screen. Mirrors the data flow of `OpenDifferenceAnimation`
+	 * @ 2520:0337 ??? `Load_Sequence` + `Play_Sequence`. Audio cues are
+	 * skipped for now. The default frame delay is 120 ms to match the
+	 * original FRAME_RATE = 0x78 used by `_DoOpeningAnims`.
+	 *
+	 * If @p holdLastFrame is true the call blocks on the final frame
+	 * until the user clicks or hits a key ??? used for the title screen.
 	 */
-	void screenDriver();
+	void playAnm(const Common::Path &path, uint frameDelayMs = 120,
+				 bool holdLastFrame = false);
 
-	bool pollEvents();
+	// Screen handlers ??? port targets in screens/ later.
+	void showEAKidsLogo();
+	void showHighScoreLogo();
+	void doNewPlayer();          ///< Mirrors `_NewPlayer` @ 1c33:0dda
+	void doChoosePartner();
+	void doCaseSelection();
+	void doSiteLoop();
+
+	/// Render the case briefing background + game/book decorations and
+	/// display the briefing ClueBlock. Mirrors `_DoInitClues` @ 1a35:0411
+	/// minus the live ANI sequence playback.
+	void doInitClues();
+
+	Common::String _playerName;  ///< Substituted into 0x80 placeholders
+
+	/// Per-mystery solved state. 0 = unsolved, 1 = solved, 2 = solved
+	/// on first try. Mirrors `_PlayerRecord.SolvedMysteries[55]` in the
+	/// original `_DisplayCorrect` flow.
+	uint8 _mysteriesSolved[55] = {};
 
-	Console *_console;
 	Common::RandomSource _rng;
 
-	DBDArchive _picsArchive;  ///< PICS.DBD/.DBX (mouse, buttons, markers, balloons sprites)
-	Common::Array<byte> _sitePals; ///< 40 x 768 bytes of 6-bit VGA palettes from SITEPALS
+	DBDArchive _picsArchive;     ///< PICS.DBD/.DBX (sprites, buttons, frame backgrounds)
+	DBDArchive _aniArchive;      ///< ANI.DBD/.DBX (multi-frame character animations)
+	DBDArchive _sitesArchive;    ///< SITES.DBD/.DBX (one full-screen scene per site)
+	DBDArchive _balloonArchive;  ///< BALLOON.DBD/.DBX (speech-balloon sprites)
+	Mystery    _mystery;         ///< Currently-loaded case file (M<n>.BIN)
+	EEMFont    _font;            ///< FONT.FNT - main 8 px font
+
+	Common::Array<byte> _sitePals; ///< 40 x 768 bytes of 6-bit VGA palettes
 
 	uint16 _lastScreen;  ///< Mirrors _LastScreen @ 2d5d:3f24
 	uint16 _nextScreen;  ///< Mirrors _NextScreen @ 2d5d:3f26
+	uint8  _partner;     ///< Mirrors _Partner: 0 = boy (Jake), 1 = girl (Jenny)
 };
 
 } // End of namespace EEM
diff --git a/engines/eem/font.cpp b/engines/eem/font.cpp
new file mode 100644
index 00000000000..f8ca1865c8d
--- /dev/null
+++ b/engines/eem/font.cpp
@@ -0,0 +1,139 @@
+/* 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/debug.h"
+#include "common/file.h"
+#include "common/textconsole.h"
+#include "common/tokenizer.h"
+
+#include "graphics/managed_surface.h"
+#include "graphics/surface.h"
+
+#include "eem/detection.h"
+#include "eem/font.h"
+
+namespace EEM {
+
+bool EEMFont::load(const Common::Path &path) {
+	Common::File f;
+	if (!f.open(path)) {
+		warning("EEMFont::load: cannot open %s", path.toString().c_str());
+		return false;
+	}
+
+	const uint16 numChars = f.readUint16LE();
+	if (numChars == 0 || numChars > 256) {
+		warning("EEMFont::load: %s reports %u chars", path.toString().c_str(), numChars);
+		return false;
+	}
+	_glyphs.resize(numChars);
+
+	for (uint i = 0; i < numChars; i++) {
+		FontGlyph &g = _glyphs[i];
+		g.height    = f.readByte();
+		g.widthBits = f.readByte();
+		g.sizeBytes = f.readByte();
+		if (g.sizeBytes > 0) {
+			g.bitmap.resize(g.sizeBytes);
+			if (f.read(g.bitmap.data(), g.sizeBytes) != g.sizeBytes) {
+				warning("EEMFont::load: short bitmap read at glyph %u", i);
+				return false;
+			}
+		}
+		if (g.height > _maxHeight)
+			_maxHeight = g.height;
+	}
+
+	debugC(1, kDebugGfx, "Font %s loaded: %u glyphs, max h=%u",
+		   path.toString().c_str(), numChars, _maxHeight);
+	return true;
+}
+
+int EEMFont::charWidth(byte c) const {
+	if (c >= _glyphs.size())
+		return 0;
+	return _glyphs[c].widthBits;
+}
+
+int EEMFont::stringWidth(const Common::String &s) const {
+	int w = 0;
+	for (uint i = 0; i < s.size(); i++)
+		w += charWidth((byte)s[i]);
+	return w;
+}
+
+int EEMFont::drawChar(Graphics::ManagedSurface *dst, int x, int y, byte c, byte color) const {
+	if (!dst || c >= _glyphs.size())
+		return 0;
+	const FontGlyph &g = _glyphs[c];
+	if (g.bitmap.empty())
+		return g.widthBits;
+
+	const uint bytesPerRow = (g.widthBits + 7) / 8;
+	for (uint row = 0; row < g.height; row++) {
+		const int dstY = y + (int)row;
+		if (dstY < 0 || dstY >= dst->h)
+			continue;
+		const byte *srcRow = g.bitmap.data() + row * bytesPerRow;
+		byte *dstRow = (byte *)dst->getBasePtr(0, dstY);
+		for (uint bit = 0; bit < g.widthBits; bit++) {
+			const int dstX = x + (int)bit;
+			if (dstX < 0 || dstX >= dst->w)
+				continue;
+			const byte mask = (byte)(0x80 >> (bit & 7));
+			if (srcRow[bit / 8] & mask)
+				dstRow[dstX] = color;
+		}
+	}
+	return g.widthBits;
+}
+
+int EEMFont::drawString(Graphics::ManagedSurface *dst, int x, int y,
+						const Common::String &s, byte color) const {
+	int penX = x;
+	for (uint i = 0; i < s.size(); i++)
+		penX += drawChar(dst, penX, y, (byte)s[i], color);
+	return penX - x;
+}
+
+int EEMFont::drawWordWrapped(Graphics::ManagedSurface *dst, int x, int y, int width,
+							 const Common::String &s, byte color) const {
+	Common::StringTokenizer tok(s, " \t");
+	int penY = y;
+	Common::String line;
+
+	while (!tok.empty()) {
+		const Common::String word = tok.nextToken();
+		Common::String trial = line.empty() ? word : line + " " + word;
+		if (stringWidth(trial) <= width) {
+			line = trial;
+		} else {
+			drawString(dst, x, penY, line, color);
+			penY += _maxHeight + 1;
+			line = word;
+		}
+	}
+	if (!line.empty())
+		drawString(dst, x, penY, line, color);
+	return penY + _maxHeight - y;
+}
+
+} // End of namespace EEM
diff --git a/engines/eem/font.h b/engines/eem/font.h
new file mode 100644
index 00000000000..f175d5360fa
--- /dev/null
+++ b/engines/eem/font.h
@@ -0,0 +1,90 @@
+/* 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 EEM_FONT_H
+#define EEM_FONT_H
+
+#include "common/array.h"
+#include "common/path.h"
+#include "common/scummsys.h"
+#include "common/str.h"
+
+namespace Graphics {
+class ManagedSurface;
+}
+
+namespace EEM {
+
+/// One bitmap glyph, 1 bit per pixel, MSB first.
+struct FontGlyph {
+	uint8 height    = 0;
+	uint8 widthBits = 0;
+	uint8 sizeBytes = 0;
+	Common::Array<byte> bitmap;
+};
+
+/**
+ * Loader for the engine's `.FNT` files (FONT.FNT, SYSTEM.FNT, TINY.FNT,
+ * 8PNTTHIN.FNT). Mirrors `_LoadFont` @ 1b66:023c.
+ *
+ * File layout:
+ *   - u16 numChars
+ *   - per char: u8 height, u8 widthBits, u8 sizeBytes, bytes[sizeBytes] bitmap
+ *
+ * Drawing mirrors `_ShowChar` @ 1b66:0346: each set bit becomes `fontColor`
+ * on the destination surface; clear bits are transparent.
+ */
+class EEMFont {
+public:
+	EEMFont() = default;
+
+	bool load(const Common::Path &path);
+
+	uint16 height() const { return _maxHeight; }
+
+	int charWidth(byte c) const;
+
+	/// Total pixel width of @p s when rendered (no shadow).
+	int stringWidth(const Common::String &s) const;
+
+	/// Draw @p c at (@p x, @p y) on @p dst with foreground @p color.
+	/// Returns the advance width.
+	int drawChar(Graphics::ManagedSurface *dst, int x, int y, byte c, byte color) const;
+
+	/// Draw @p s at (@p x, @p y) and return total advance width.
+	int drawString(Graphics::ManagedSurface *dst, int x, int y,
+				   const Common::String &s, byte color) const;
+
+	/// Word-wrap @p s into the rect (x..x+width, y..) and draw line by line.
+	/// Mirrors the data flow of `_DoWordWrap` @ 1b66:04a7.
+	int drawWordWrapped(Graphics::ManagedSurface *dst, int x, int y, int width,
+						const Common::String &s, byte color) const;
+
+	bool isLoaded() const { return !_glyphs.empty(); }
+
+private:
+	Common::Array<FontGlyph> _glyphs;
+	uint16 _maxHeight = 0;
+};
+
+} // End of namespace EEM
+
+#endif
diff --git a/engines/eem/metaengine.cpp b/engines/eem/metaengine.cpp
index 0d61cf6a944..21dee682b00 100644
--- a/engines/eem/metaengine.cpp
+++ b/engines/eem/metaengine.cpp
@@ -24,6 +24,8 @@
 
 #include "eem/eem.h"
 
+#include "common/system.h"
+
 namespace EEM {
 
 const char *EEMEngine::getGameId() const {
@@ -48,7 +50,8 @@ public:
 	}
 
 	bool hasFeature(MetaEngineFeature f) const override {
-		return false;
+		return checkExtendedSaves(f) ||
+			   f == kSupportsLoadingDuringStartup;
 	}
 };
 
diff --git a/engines/eem/module.mk b/engines/eem/module.mk
index 34247658998..047409575c9 100644
--- a/engines/eem/module.mk
+++ b/engines/eem/module.mk
@@ -1,10 +1,13 @@
 MODULE := engines/eem
 
 MODULE_OBJS = \
-	console.o \
+	animation.o \
 	eem.o \
+	font.o \
 	metaengine.o \
-	resource.o
+	mystery.o \
+	resource.o \
+	site.o
 
 # This module can be built as a plugin
 ifeq ($(ENABLE_EEM), DYNAMIC_PLUGIN)
diff --git a/engines/eem/mystery.cpp b/engines/eem/mystery.cpp
new file mode 100644
index 00000000000..e3b4051a14a
--- /dev/null
+++ b/engines/eem/mystery.cpp
@@ -0,0 +1,261 @@
+/* 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/debug.h"
+#include "common/file.h"
+#include "common/random.h"
+#include "common/serializer.h"
+#include "common/textconsole.h"
+
+#include "eem/detection.h"
+#include "eem/mystery.h"
+
+namespace EEM {
+
+uint16 Mystery::readU16(uint offset) const {
+	if (offset + 2 > _data.size())
+		return 0;
+	return READ_LE_UINT16(_data.data() + offset);
+}
+
+void Mystery::clear() {
+	_data.clear();
+	_number = 0;
+	_initOffset = _mapOffset = _siteIndexOffset = _textOffset = 0;
+	_noteOffset = _galleryOffset = _kdTextOffset = 0;
+	_solvedOffset = _hintOffset = 0;
+	_numSites = 0;
+	_numSuspects = _numCONSITEs = _numCOFFSITEs = 0;
+	memset(_aChain, 0, sizeof(_aChain));
+	memset(_bChain, 0, sizeof(_bChain));
+	memset(_cChain, 0, sizeof(_cChain));
+	memset(_cluesFound, 0, sizeof(_cluesFound));
+	memset(_noteSelected, 0, sizeof(_noteSelected));
+	memset(_hotSpotsSeen, 0, sizeof(_hotSpotsSeen));
+	memset(_inGallery, 0, sizeof(_inGallery));
+	memset(_newOrder, 0, sizeof(_newOrder));
+	memset(_visitedSite, 0, sizeof(_visitedSite));
+	memset(_onSites, 0, sizeof(_onSites));
+	_sawCOFFSITEs = _sawCONSITEs = _sawHelpHint = _solvedPuzzle = false;
+	_firstTry = true;
+	_searchLocationNumber = _siteNumber = 0xFFFF;
+	_lastSite = 0x1B;
+}
+
+bool Mystery::load(uint num, Common::RandomSource *rng) {
+	const Common::String fname = Common::String::format("M%u.BIN", num);
+	Common::File f;
+	if (!f.open(Common::Path(fname))) {
+		warning("Mystery::load: cannot open %s", fname.c_str());
+		return false;
+	}
+
+	const int32 size = f.size();
+	if (size <= 64) {
+		warning("Mystery::load: %s too small (%d bytes)", fname.c_str(), size);
+		return false;
+	}
+
+	// Stage to a temporary buffer so a short read leaves the previous
+	// mystery state intact instead of half-clobbering `_data`.
+	Common::Array<byte> staging(size);
+	if (f.read(staging.data(), size) != (uint32)size) {
+		warning("Mystery::load: short read on %s", fname.c_str());
+		return false;
+	}
+	f.close();
+
+	_data = staging;
+	_number = num;
+
+	// Header is 16-bit-word indexed (matches `int *piVar1 = __Mystery; piVar1[N]`).
+	_initOffset      = readU16(0  * 2);
+	_mapOffset       = readU16(2  * 2);
+	_siteIndexOffset = readU16(3  * 2);
+	_textOffset      = readU16(4  * 2);
+	_noteOffset      = readU16(5  * 2);
+	_galleryOffset   = readU16(6  * 2);
+	_kdTextOffset    = readU16(7  * 2);
+	_solvedOffset    = readU16(8  * 2);
+	_hintOffset      = readU16(9  * 2);
+
+	_numSites    = readU16(10 * 2);
+	_numSuspects = (uint8)readU16(13 * 2);
+	_numCONSITEs = (uint8)readU16(14 * 2);
+	_numCOFFSITEs = (uint8)readU16(15 * 2);
+
+	for (uint i = 0; i < kChainLen; i++) {
+		_aChain[i] = readU16((16 + i) * 2);
+		_bChain[i] = readU16((21 + i) * 2);
+		_cChain[i] = readU16((26 + i) * 2);
+	}
+
+	// Per-mystery runtime state ??? `_ReadMystery` zeroes these at load.
+	memset(_cluesFound, 0, sizeof(_cluesFound));
+	memset(_noteSelected, 0, sizeof(_noteSelected));
+	memset(_hotSpotsSeen, 0, sizeof(_hotSpotsSeen));
+	memset(_inGallery, 0, sizeof(_inGallery));
+	// `_NewOrder` in the original randomly cycles the gallery positions
+	// per playthrough. For consistency between clue side-effects (which
+	// write to `_inGallery[_newOrder[galIdx]]`) and gallery rendering
+	// (which iterates logical indices), we keep the identity mapping.
+	// If the original's randomized positioning is required later, both
+	// the side-effect path AND the rendering path need to use it together.
+	(void)rng;
+	for (uint i = 0; i < kGalleryCap; i++)
+		_newOrder[i] = (uint8)i;
+	memset(_visitedSite, 0, sizeof(_visitedSite));
+	memset(_onSites, 0, sizeof(_onSites));
+	_sawCOFFSITEs = _sawCONSITEs = _sawHelpHint = _solvedPuzzle = false;
+	_firstTry = true;
+	_searchLocationNumber = _siteNumber = 0xFFFF;
+	_lastSite = 0x1B; // Sentinel matching _ReadMystery's `_LastSite = 0x1b`.
+
+	debugC(1, kDebugMystery, "Loaded %s (%d B): %u sites, %u suspects, "
+		   "CON=%u COFF=%u, init=0x%04x site=0x%04x text=0x%04x",
+		   fname.c_str(), size, _numSites, _numSuspects,
+		   _numCONSITEs, _numCOFFSITEs,
+		   _initOffset, _siteIndexOffset, _textOffset);
+	return true;
+}
+
+const byte *Mystery::siteIndexEntry(uint siteNum) const {
+	if (!isLoaded() || siteNum >= _numSites)
+		return nullptr;
+	const uint off = _siteIndexOffset + siteNum * 6;
+	if (off + 6 > _data.size())
+		return nullptr;
+	return _data.data() + off;
+}
+
+const byte *Mystery::siteData(uint siteNum) const {
+	const byte *idx = siteIndexEntry(siteNum);
+	if (!idx)
+		return nullptr;
+	const uint16 dataOff = READ_LE_UINT16(idx);
+	if (dataOff >= _data.size())
+		return nullptr;
+	return _data.data() + dataOff;
+}
+
+const byte *Mystery::hotspots(uint siteNum) const {
+	const byte *idx = siteIndexEntry(siteNum);
+	if (!idx)
+		return nullptr;
+	const uint16 hotspotOff = READ_LE_UINT16(idx + 4);
+	if (hotspotOff >= _data.size())
+		return nullptr;
+	return _data.data() + hotspotOff;
+}
+
+uint16 Mystery::hotspotCount(uint siteNum) const {
+	const byte *site = siteData(siteNum);
+	if (!site || (size_t)(site - _data.data()) + 8 > _data.size())
+		return 0;
+	return READ_LE_UINT16(site + 6);
+}
+
+const char *Mystery::textAt(uint16 offset) const {
+	if (!isLoaded())
+		return "";
+	const uint pos = _textOffset + offset;
+	if (pos >= _data.size())
+		return "";
+	return (const char *)(_data.data() + pos);
+}
+
+const byte *Mystery::initBlock() const {
+	if (!isLoaded() || _initOffset >= _data.size())
+		return nullptr;
+	return _data.data() + _initOffset;
+}
+
+const byte *Mystery::galleryData() const {
+	if (!isLoaded() || _galleryOffset >= _data.size())
+		return nullptr;
+	return _data.data() + _galleryOffset;
+}
+
+const byte *Mystery::noteIndex() const {
+	if (!isLoaded() || _noteOffset >= _data.size())
+		return nullptr;
+	return _data.data() + _noteOffset;
+}
+
+uint16 Mystery::noteIndexCount() const {
+	if (!isLoaded())
+		return 0;
+	// NoteIndex runs from _noteOffset to the start of GalleryData.
+	if (_galleryOffset <= _noteOffset)
+		return 0;
+	return (uint16)((_galleryOffset - _noteOffset) / 4);
+}
+
+const byte *Mystery::kdTextIndex() const {
+	if (!isLoaded() || _kdTextOffset >= _data.size())
+		return nullptr;
+	return _data.data() + _kdTextOffset;
+}
+
+const byte *Mystery::mapEntry(uint siteNum) const {
+	if (!isLoaded() || siteNum >= _numSites)
+		return nullptr;
+	const uint off = _mapOffset + siteNum * 14;
+	if (off + 14 > _data.size())
+		return nullptr;
+	return _data.data() + off;
+}
+
+int Mystery::selectedPoints() const {
+	const byte *ni = noteIndex();
+	const uint16 cnt = noteIndexCount();
+	if (!ni || cnt == 0)
+		return 0;
+	int total = 0;
+	for (uint i = 0; i < cnt && i < kCluesFoundCap; i++) {
+		if (!_noteSelected[i])
+			continue;
+		// Each NoteIndex entry is 4 bytes: u16 textOff + u16 points.
+		const uint16 pts = READ_LE_UINT16(ni + i * 4 + 2);
+		total += (int)(int16)pts;
+	}
+	return total;
+}
+
+void Mystery::syncState(Common::Serializer &s) {
+	s.syncBytes(_cluesFound, kCluesFoundCap);
+	s.syncBytes(_noteSelected, kCluesFoundCap);
+	s.syncArray(_hotSpotsSeen, kHotSpotsCap, Common::Serializer::Uint16LE);
+	s.syncArray(_inGallery,    kGalleryCap,  Common::Serializer::Uint16LE);
+	s.syncBytes(_newOrder, kGalleryCap);
+	s.syncArray(_visitedSite, kVisitedSiteCap, Common::Serializer::Uint16LE);
+	s.syncArray(_onSites,     kVisitedSiteCap, Common::Serializer::Uint16LE);
+	s.syncAsByte(_sawCOFFSITEs);
+	s.syncAsByte(_sawCONSITEs);
+	s.syncAsByte(_sawHelpHint);
+	s.syncAsByte(_solvedPuzzle);
+	s.syncAsByte(_firstTry);
+	s.syncAsUint16LE(_searchLocationNumber);
+	s.syncAsUint16LE(_siteNumber);
+	s.syncAsUint16LE(_lastSite);
+}
+
+} // End of namespace EEM
diff --git a/engines/eem/mystery.h b/engines/eem/mystery.h
new file mode 100644
index 00000000000..b0ea78677e6
--- /dev/null
+++ b/engines/eem/mystery.h
@@ -0,0 +1,188 @@
+/* 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 EEM_MYSTERY_H
+#define EEM_MYSTERY_H
+
+#include "common/array.h"
+#include "common/path.h"
+#include "common/scummsys.h"
+#include "common/serializer.h"
+#include "common/str.h"
+
+namespace EEM {
+
+/**
+ * One mystery (case file) loaded from `M<n>.BIN`.
+ *
+ * Mirrors the layout established by `_ReadMystery` @ 2404:008f:
+ *
+ *   word[0]  = InitBlock byte offset
+ *   word[2]  = MapData byte offset
+ *   word[3]  = SiteIndex byte offset
+ *   word[4]  = TextBlock byte offset
+ *   word[5]  = NoteIndex byte offset
+ *   word[6]  = GalleryData byte offset
+ *   word[7]  = KDTextIndex byte offset
+ *   word[8]  = SolvedClues byte offset
+ *   word[9]  = HintBlock byte offset
+ *   word[10] = NumSites + start of MysteryStats
+ *   word[13] = NumSuspects (low byte)
+ *   word[14] = NumCONSITEs
+ *   word[15] = NumCOFFSITEs
+ *   word[16..20] = AChain (5 words)
+ *   word[21..25] = BChain
+ *   word[26..30] = CChain
+ *
+ * Per-mystery state is reset every time a mystery is loaded; chains and
+ * indices are pointers into the in-memory `_data` blob.
+ */
+class Mystery {
+public:
+	static const uint kNumChains       = 3;
+	static const uint kChainLen        = 5;
+	static const uint kCluesFoundCap   = 100;
+	static const uint kHotSpotsCap     = 100;
+	static const uint kGalleryCap      = 5;
+	static const uint kVisitedSiteCap  = 20;
+
+	Mystery() = default;
+	~Mystery() = default;
+
+	/// Load `M<num>.BIN` and reset per-mystery state. Returns false on error.
+	bool load(uint num, class Common::RandomSource *rng = nullptr);
+
+	/// Drop the loaded mystery and zero per-mystery state. Safe to call
+	/// at any time; `isLoaded()` returns false afterward.
+	void clear();
+
+	/// True once `load()` succeeded and offsets are valid.
+	bool isLoaded() const { return !_data.empty(); }
+
+	uint number() const { return _number; }
+	uint16 numSites() const { return _numSites; }
+	uint8  numSuspects() const { return _numSuspects; }
+	uint8  numCONSITEs() const { return _numCONSITEs; }
+	uint8  numCOFFSITEs() const { return _numCOFFSITEs; }
+
+	/// Pointer to the InitBlock (case briefing).
+	/// _InitBlock @ 2d5d:?? = mystery + word[0] in `_ReadMystery`.
+	const byte *initBlock() const;
+
+	/// Pointer to the GalleryData; one 0x46-byte entry per suspect.
+	/// First u16 of each entry is the PIC picture ID for that suspect.
+	const byte *galleryData() const;
+
+	/// Pointer to the NoteIndex array (4 bytes per entry: u16 textOff + u16 pts).
+	const byte *noteIndex() const;
+
+	/// Number of entries in NoteIndex.
+	uint16 noteIndexCount() const;
+
+	/// Pointer to the KDTextIndex; first u16s are TextBlock offsets for
+	/// host hint lines.
+	const byte *kdTextIndex() const;
+
+	/// Pointer to the MapData entry for site @p siteNum (14 bytes per
+	/// entry; first u16 = sitepic, +4..7 = (x, y) on the big map).
+	const byte *mapEntry(uint siteNum) const;
+
+	/// Pointer to the SiteIndex entry for site @p siteNum (6 bytes per site).
+	const byte *siteIndexEntry(uint siteNum) const;
+
+	/// Pointer to the SiteData (sitepic, travel, hotspot count, ...)
+	/// referenced by SiteIndex[@p siteNum].
+	const byte *siteData(uint siteNum) const;
+
+	/// Pointer to the hotspot rectangle array for site @p siteNum.
+	/// Each rect is 14 bytes: x1, y1, x2, y2, then 6 bytes of clue data.
+	const byte *hotspots(uint siteNum) const;
+
+	/// Number of hotspots in site @p siteNum.
+	uint16 hotspotCount(uint siteNum) const;
+
+	/// Pointer to a NUL-terminated string at TextBlock+ at p offset.
+	const char *textAt(uint16 offset) const;
+
+	/// Pointer at byte offset @p offset within the mystery blob, or null
+	/// if out of range. Used to chase ClueBlock pointers stored in
+	/// hotspot data.
+	const byte *blobAt(uint32 offset) const {
+		return offset < _data.size() ? _data.data() + offset : nullptr;
+	}
+
+	/// Synchronize the per-mystery runtime state for save/load. The fixed
+	/// arrays serialize first, then the booleans and counters.
+	void syncState(Common::Serializer &s);
+
+	/// Sum of point values of every selected notebook entry. Mirrors
+	/// `_GetSelectedPoints` @ 1df2:00bd.
+	int selectedPoints() const;
+
+	/// True when `selectedPoints() > 99`. Mirrors `_SolvedCheck`.
+	bool solvedCheck() const { return selectedPoints() > 99; }
+
+	/// Per-mystery runtime state, zeroed at load time.
+	uint8  _cluesFound[kCluesFoundCap]   = {};
+	uint8  _noteSelected[kCluesFoundCap] = {};  ///< Mirror `_NoteSelected`
+	uint16 _hotSpotsSeen[kHotSpotsCap]   = {};
+	uint16 _inGallery[kGalleryCap]       = {};
+	uint8  _newOrder[kGalleryCap]        = {};
+	uint16 _visitedSite[kVisitedSiteCap] = {};
+	uint16 _onSites[kVisitedSiteCap]     = {};
+	bool   _sawCOFFSITEs = false;
+	bool   _sawCONSITEs  = false;
+	bool   _sawHelpHint  = false;
+	bool   _solvedPuzzle = false;
+	bool   _firstTry     = true;
+	uint16 _searchLocationNumber = 0xFFFF;
+	uint16 _siteNumber           = 0xFFFF;
+	uint16 _lastSite             = 0xFFFF;
+
+private:
+	Common::Array<byte> _data;
+	uint   _number = 0;
+
+	uint16 _initOffset      = 0;
+	uint16 _mapOffset       = 0;
+	uint16 _siteIndexOffset = 0;
+	uint16 _textOffset      = 0;
+	uint16 _noteOffset      = 0;
+	uint16 _galleryOffset   = 0;
+	uint16 _kdTextOffset    = 0;
+	uint16 _solvedOffset    = 0;
+	uint16 _hintOffset      = 0;
+
+	uint16 _numSites    = 0;
+	uint8  _numSuspects = 0;
+	uint8  _numCONSITEs = 0;
+	uint8  _numCOFFSITEs = 0;
+
+	uint16 _aChain[kChainLen] = {};
+	uint16 _bChain[kChainLen] = {};
+	uint16 _cChain[kChainLen] = {};
+
+	uint16 readU16(uint offset) const;
+};
+
+} // End of namespace EEM
+
+#endif
diff --git a/engines/eem/resource.cpp b/engines/eem/resource.cpp
index 67c9ab8a78e..15fc9981d46 100644
--- a/engines/eem/resource.cpp
+++ b/engines/eem/resource.cpp
@@ -76,6 +76,43 @@ void DBDArchive::close() {
 	_index.clear();
 }
 
+/**
+ * Read one 12-byte frame header + payload at the current stream position.
+ * Shared between picture and animation loaders since the layout is the same.
+ */
+static bool readFrame(Common::SeekableReadStream &stream, bool compressed, Picture &out) {
+	out.flags             = stream.readUint16LE();
+	const uint16 height   = stream.readUint16LE();
+	const uint16 width    = stream.readUint16LE();
+	out.rowoff            = stream.readUint16LE();
+	out.miscflags         = stream.readUint16LE();
+	out.compsize          = stream.readUint16LE();
+
+	if (width == 0 || height == 0) {
+		warning("readFrame: zero dimensions (%ux%u)", width, height);
+		return false;
+	}
+
+	out.surface.create(width, height, Graphics::PixelFormat::createFormatCLUT8());
+	const uint32 unpacked = (uint32)width * (uint32)height;
+
+	if (!compressed) {
+		if (stream.read(out.surface.getPixels(), unpacked) != unpacked) {
+			warning("readFrame: short raw read (%u bytes)", unpacked);
+			return false;
+		}
+		return true;
+	}
+
+	if (!Common::decompressDCL(&stream, (byte *)out.surface.getPixels(),
+							   out.compsize, unpacked)) {
+		warning("readFrame: DCL decompression failed (%u packed -> %u pixels)",
+				out.compsize, unpacked);
+		return false;
+	}
+	return true;
+}
+
 bool DBDArchive::loadEntry(uint num, Picture &out) {
 	if (num >= _index.size()) {
 		warning("DBDArchive::loadEntry: %u out of range (max %u)", num, _index.size());
@@ -88,44 +125,40 @@ bool DBDArchive::loadEntry(uint num, Picture &out) {
 		return false;
 	}
 
-	// Mirrors _GetFromDB @ 172b:105d:
-	//   _fread(i)            // 2-byte skip word (purpose unclear; always 0x0001)
-	//   _fread(pic, 1, 12)   // 12-byte header
+	// Mirrors _GetFromDB @ 172b:105d. The 2-byte word read first matches
+	// loadAnimation's frame-count read; for picture entries it is always 1.
 	(void)_dbd.readUint16LE();
-	out.flags             = _dbd.readUint16LE();
-	const uint16 height   = _dbd.readUint16LE();
-	const uint16 width    = _dbd.readUint16LE();
-	out.rowoff            = _dbd.readUint16LE();
-	out.miscflags         = _dbd.readUint16LE();
-	out.compsize          = _dbd.readUint16LE();
+	return readFrame(_dbd, entry.compressed != 0, out);
+}
 
-	if (width == 0 || height == 0) {
-		warning("DBDArchive::loadEntry: %u has zero dimensions (%ux%u)",
-				num, width, height);
+bool DBDArchive::loadAnimation(uint num, Animation &out) {
+	if (num >= _index.size()) {
+		warning("DBDArchive::loadAnimation: %u out of range (max %u)", num, _index.size());
 		return false;
 	}
 
-	out.surface.create(width, height, Graphics::PixelFormat::createFormatCLUT8());
-
-	if (entry.compressed == 0) {
-		// Raw pixel data ??? read width*height bytes verbatim.
-		const uint32 pixelCount = (uint32)width * (uint32)height;
-		if (_dbd.read(out.surface.getPixels(), pixelCount) != pixelCount) {
-			warning("DBDArchive::loadEntry: short raw read on %u", num);
-			return false;
-		}
-		return true;
+	const DBEntry &entry = _index[num];
+	if (!_dbd.seek(entry.offset)) {
+		warning("DBDArchive::loadAnimation: seek to 0x%08x failed", entry.offset);
+		return false;
 	}
 
-	// Compressed payload: feed the .DBD stream straight into the DCL
-	// decoder, matching the pattern used by Neverhood's BLB archive.
-	const uint32 unpacked = (uint32)width * (uint32)height;
-	if (!Common::decompressDCL(&_dbd, (byte *)out.surface.getPixels(),
-							   out.compsize, unpacked)) {
-		warning("DBDArchive::loadEntry: DCL decompression failed on %u "
-				"(%u packed -> %u pixels)", num, out.compsize, unpacked);
+	// Mirrors _GetAnimation @ 172b:163a: u16 frame count, then N frames.
+	const uint16 frameCount = _dbd.readUint16LE();
+	if (frameCount == 0 || frameCount > 256) {
+		warning("DBDArchive::loadAnimation: %u has implausible frame count %u",
+				num, frameCount);
 		return false;
 	}
+
+	out.resize(frameCount);
+	for (uint16 i = 0; i < frameCount; i++) {
+		if (!readFrame(_dbd, entry.compressed != 0, out[i])) {
+			warning("DBDArchive::loadAnimation: frame %u/%u failed in entry %u",
+					i, frameCount, num);
+			return false;
+		}
+	}
 	return true;
 }
 
diff --git a/engines/eem/resource.h b/engines/eem/resource.h
index a48deb85518..846bf0f1135 100644
--- a/engines/eem/resource.h
+++ b/engines/eem/resource.h
@@ -59,6 +59,9 @@ struct Picture {
 	Graphics::ManagedSurface surface;
 };
 
+/// Multi-frame animation as stored in ANI.DBD ??? a sequence of Pictures.
+typedef Common::Array<Picture> Animation;
+
 /**
  * Reader for a .DBD + .DBX archive pair.
  *
@@ -96,6 +99,13 @@ public:
 	 */
 	bool getPicture(uint num, Picture &out) { return loadEntry(num - 1, out); }
 
+	/**
+	 * Load a multi-frame animation entry. Mirrors _GetAnimation @ 172b:163a:
+	 * read u16 frameCount, then for each frame read a 12-byte header and
+	 * decompress the payload. Used for ANI.DBD entries.
+	 */
+	bool loadAnimation(uint num, Animation &out);
+
 private:
 	Common::File _dbd;
 	Common::Array<DBEntry> _index;
diff --git a/engines/eem/site.cpp b/engines/eem/site.cpp
new file mode 100644
index 00000000000..b20a92a96ea
--- /dev/null
+++ b/engines/eem/site.cpp
@@ -0,0 +1,341 @@
+/* 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/debug.h"
+#include "common/events.h"
+#include "common/system.h"
+#include "common/textconsole.h"
+
+#include "eem/detection.h"
+#include "eem/eem.h"
+#include "eem/mystery.h"
+#include "eem/site.h"
+
+namespace EEM {
+
+void SiteScreen::enter(uint siteNum) {
+	if (!_mystery || !_mystery->isLoaded()) {
+		warning("SiteScreen::enter: no mystery loaded");
+		return;
+	}
+	if (siteNum >= _mystery->numSites()) {
+		warning("SiteScreen::enter: site %u out of range (max %u)",
+				siteNum, _mystery->numSites());
+		return;
+	}
+
+	_mystery->_siteNumber = siteNum;
+	if (siteNum < Mystery::kVisitedSiteCap)
+		_mystery->_visitedSite[siteNum] = 1;
+	debugC(1, kDebugSite, "Entering site %u (%u hotspots)",
+		   siteNum, _mystery->hotspotCount(siteNum));
+
+	// Palette: original `_BuildBackground` calls `GetPalette(sitenum + 1)`
+	// where sitenum is the global SITES.DBD index (= the per-mystery
+	// `sitepic` field), not the per-mystery site index.
+	const byte *sd = _mystery->siteData(siteNum);
+	const uint16 sitepic = sd ? READ_LE_UINT16(sd) : 0;
+	_vm->setSitePaletteForSite(sitepic);
+
+	renderBackground(siteNum);
+	renderHotspots(siteNum);
+	g_system->updateScreen();
+}
+
+void SiteScreen::run() {
+	if (!_mystery || !_mystery->isLoaded())
+		return;
+
+	uint cur = 0;
+	enter(cur);
+
+	while (!_vm->shouldQuit()) {
+		Common::Event event;
+		bool exitRequested = false;
+		while (g_system->getEventManager()->pollEvent(event)) {
+			switch (event.type) {
+			case Common::EVENT_QUIT:
+			case Common::EVENT_RETURN_TO_LAUNCHER:
+				return;
+
+			case Common::EVENT_LBUTTONDOWN: {
+				const int idx = hotspotAtPoint(cur, event.mouse.x, event.mouse.y);
+				if (idx >= 0) {
+					onHotspotClicked(cur, (uint)idx);
+					// Restore the site BG after the clue overlay.
+					enter(cur);
+				}
+				break;
+			}
+
+			case Common::EVENT_KEYDOWN:
+				switch (event.kbd.keycode) {
+				case Common::KEYCODE_ESCAPE:
+					if (_vm->areYouSure())
+						return;
+					enter(cur);
+					break;
+				case Common::KEYCODE_m:
+					_vm->doBigMap();
+					// Either way the map covered the site ??? re-render.
+					if (_mystery->_siteNumber < _mystery->numSites())
+						cur = _mystery->_siteNumber;
+					enter(cur);
+					break;
+				case Common::KEYCODE_n:
+					_vm->doNotebook();
+					enter(cur);
+					break;
+				case Common::KEYCODE_g:
+					_vm->doGallery();
+					enter(cur);
+					break;
+				case Common::KEYCODE_a:
+					_vm->doAccuse();
+					exitRequested = true;
+					break;
+				case Common::KEYCODE_h:
+					_vm->doHelp();
+					enter(cur);
+					break;
+				case Common::KEYCODE_v:
+					_showHotspots = !_showHotspots;
+					enter(cur);
+					break;
+				case Common::KEYCODE_r:
+					// Restart the mystery from scratch (mirrors `_ReloadMystery`).
+					if (_mystery->load(_mystery->number())) {
+						cur = 0;
+						enter(cur);
+					}
+					break;
+				case Common::KEYCODE_QUESTION:
+				case Common::KEYCODE_F1: {
+					if (_vm->getFont().isLoaded()) {
+						Graphics::ManagedSurface help(320, 200,
+							Graphics::PixelFormat::createFormatCLUT8());
+						help.clear();
+						const EEMFont &fnt = _vm->getFont();
+						int y = 8;
+						const char *lines[] = {
+							"EAGLE EYE MYSTERIES ??? keys",
+							"",
+							"  click   search a hotspot",
+							"  V       toggle hotspot outlines",
+							"  M       map (travel between sites)",
+							"  N       notebook (mark evidence with 1..9)",
+							"  G       gallery (suspect portraits)",
+							"  H       hint from the case host",
+							"  A       accuse a suspect",
+							"  R       restart current mystery",
+							"  Tab     next site (cycle)",
+							"  F5      save / load (ScummVM dialog)",
+							"  ? / F1  this help",
+							"  Esc     quit (with confirm)",
+							"",
+							"Notebook: select evidence with 1..9.",
+							"Selected-points > 99 wins the case."
+						};
+						for (uint i = 0; i < sizeof(lines)/sizeof(lines[0]); i++) {
+							fnt.drawString(&help, 8, y, lines[i], 0xF);
+							y += fnt.height() + 1;
+						}
+						g_system->copyRectToScreen(help.getPixels(),
+							help.pitch, 0, 0, 320, 200);
+						g_system->updateScreen();
+						_vm->waitForInput(60000);
+						enter(cur);
+					}
+					break;
+				}
+				case Common::KEYCODE_TAB:
+					_mystery->_lastSite = cur;
+					cur = (cur + 1) % _mystery->numSites();
+					enter(cur);
+					break;
+				default:
+					break;
+				}
+				break;
+
+			default:
+				break;
+			}
+		}
+		if (exitRequested)
+			return;
+		g_system->updateScreen();
+		g_system->delayMillis(10);
+	}
+}
+
+void SiteScreen::renderBackground(uint siteNum) {
+	// Mirrors `_BuildBackground` @ 172b:13e2 (simplified):
+	//   1. Load PIC 0x3d (the site frame) from PICS.DBD.
+	//   2. Load entry @p siteNum from SITES.DBD (the site scene).
+	//   3. Composite scene into the frame at the position carried in the
+	//      SiteData fields.
+	//   4. Set palette to (siteNum + 1) ??? per-site palettes start at 1.
+	// We render frame + scene at (0,0); the original positions the scene
+	// at (x,y) read from the SiteData but we don't have the offsets fully
+	// decoded yet so a top-left placement will do.
+
+	// Frame.
+	Picture frame;
+	if (_vm->getPics().getPicture(0x3d, frame)) {
+		g_system->copyRectToScreen(frame.surface.getPixels(),
+								   frame.surface.pitch,
+								   0, 0, frame.surface.w, frame.surface.h);
+	}
+
+	// Scene from SITES.DBD: indexed by `sitepic` from SiteData (global
+	// SITES.DBD entry, NOT the per-mystery site index). Falls back to
+	// `_GetPicture(sitepic)` if SITES is unavailable.
+	const byte *site = _mystery->siteData(siteNum);
+	const uint16 sitepic = site ? READ_LE_UINT16(site) : 0;
+	Picture scene;
+	bool haveScene = false;
+	bool fromPics = false;
+	if (sitepic > 0 && _vm->getSites().size() > sitepic - 1)
+		haveScene = _vm->getSites().loadEntry(sitepic - 1, scene);
+	if (!haveScene && sitepic > 0) {
+		haveScene = _vm->getPics().getPicture(sitepic, scene);
+		fromPics = haveScene;
+	}
+	if (haveScene) {
+		const int w = MIN<int>(scene.surface.w, 320);
+		const int h = MIN<int>(scene.surface.h, 200);
+		// Full-screen pictures (sitepic fallback) go at (0, 0); smaller
+		// SITES.DBD scenes are centred horizontally with the top below
+		// the HUD bar so progress info stays visible.
+		const int x = fromPics ? 0 : (320 - w) / 2;
+		const int y = fromPics ? 0 : (h < 180 ? 4 : 0);
+		g_system->copyRectToScreen(scene.surface.getPixels(),
+								   scene.surface.pitch, x, y, w, h);
+	}
+}
+
+void SiteScreen::renderHotspots(uint siteNum) {
+	// HUD overlay: site number, found clues, selected points. Drawn at
+	// the BOTTOM of the screen so the scene's top row stays visible ???
+	// 320x200 mode 13h has a small bottom strip that the original engine
+	// uses for tool buttons; we repurpose it for the HUD.
+	if (_vm->getFont().isLoaded()) {
+		Graphics::Surface *screen = g_system->lockScreen();
+		if (screen) {
+			uint cluesFound = 0;
+			for (uint i = 0; i < Mystery::kCluesFoundCap; i++)
+				if (_mystery->_cluesFound[i])
+					cluesFound++;
+			Common::String hud = Common::String::format(
+				"Site %u  Clues %u  Pts %d   M N G H A R V Tab ?",
+				siteNum, cluesFound, _mystery->selectedPoints());
+			const int hudY = 192;
+			screen->fillRect(Common::Rect(0, hudY, 320, 200), 0);
+			Graphics::ManagedSurface mgr(320, 9,
+				Graphics::PixelFormat::createFormatCLUT8());
+			mgr.clear();
+			_vm->getFont().drawString(&mgr, 4, 0, hud, 0x0F);
+			for (int row = 0; row < 8; row++) {
+				memcpy((byte *)screen->getBasePtr(0, hudY + row),
+					   (const byte *)mgr.getBasePtr(0, row), 320);
+			}
+			g_system->unlockScreen();
+		}
+	}
+
+	// Hotspot outlines (`_DrawSearchButtons`): toggle via V.
+	if (!_showHotspots)
+		return;
+
+	const byte *spots = _mystery->hotspots(siteNum);
+	const uint16 count = _mystery->hotspotCount(siteNum);
+	if (!spots)
+		return;
+
+	Graphics::Surface *screen = g_system->lockScreen();
+	if (!screen)
+		return;
+
+	for (uint i = 0; i < count; i++) {
+		const byte *r = spots + i * 14;
+		const int16 x1 = (int16)READ_LE_UINT16(r + 0);
+		const int16 y1 = (int16)READ_LE_UINT16(r + 2);
+		const int16 x2 = (int16)READ_LE_UINT16(r + 4);
+		const int16 y2 = (int16)READ_LE_UINT16(r + 6);
+		const Common::Rect rect(MAX<int>(0, x1), MAX<int>(0, y1),
+								MIN<int>(screen->w, x2),
+								MIN<int>(screen->h, y2));
+		// Hotspots flagged as "seen" get a different colour so the
+		// player knows they've already searched them.
+		const byte color =
+			(i < Mystery::kHotSpotsCap && _mystery->_hotSpotsSeen[i]) ? 0x07 : 0x0F;
+		screen->frameRect(rect, color);
+	}
+
+	g_system->unlockScreen();
+}
+
+int SiteScreen::hotspotAtPoint(uint siteNum, int x, int y) const {
+	const byte *spots = _mystery->hotspots(siteNum);
+	const uint16 count = _mystery->hotspotCount(siteNum);
+	if (!spots)
+		return -1;
+
+	for (uint i = 0; i < count; i++) {
+		const byte *r = spots + i * 14;
+		const int16 x1 = (int16)READ_LE_UINT16(r + 0);
+		const int16 y1 = (int16)READ_LE_UINT16(r + 2);
+		const int16 x2 = (int16)READ_LE_UINT16(r + 4);
+		const int16 y2 = (int16)READ_LE_UINT16(r + 6);
+		if (x >= x1 && x < x2 && y >= y1 && y < y2)
+			return (int)i;
+	}
+	return -1;
+}
+
+void SiteScreen::onHotspotClicked(uint siteNum, uint hotIdx) {
+	debugC(1, kDebugSite, "Site %u: hotspot %u clicked", siteNum, hotIdx);
+
+	// Mark the hotspot itself as seen.
+	if (hotIdx < Mystery::kHotSpotsCap)
+		_mystery->_hotSpotsSeen[hotIdx] = 1;
+	_mystery->_searchLocationNumber = (uint16)hotIdx;
+
+	// Bytes 8..9 of each 14-byte hotspot rect = byte offset within the
+	// mystery blob pointing at a ClueBlock. Verified against M0.BIN:
+	// site 0 hotspot 0 -> 0x0502 -> "Hi! I think somebody's playing a
+	// trick on us...". `displayClue` runs the entry's side effects
+	// (`_AddNotebook` for ClueEntry +0x30..+0x39, gallery +0x26..+0x2f,
+	// onsite +0x1c..+0x25) so we don't need to touch `_cluesFound` here.
+	const byte *spots = _mystery->hotspots(siteNum);
+	if (spots) {
+		const uint16 clueOff = READ_LE_UINT16(spots + hotIdx * 14 + 8);
+		debugC(2, kDebugSite, "  hotspot %u -> clue offset 0x%04x",
+			   hotIdx, clueOff);
+		const byte *clueBlock = _mystery->blobAt(clueOff);
+		if (clueBlock)
+			_vm->displayClue(clueBlock);
+	}
+	// Caller (`SiteScreen::run`) re-renders the site after this returns.
+}
+
+} // End of namespace EEM
diff --git a/engines/eem/site.h b/engines/eem/site.h
new file mode 100644
index 00000000000..ce450bb1905
--- /dev/null
+++ b/engines/eem/site.h
@@ -0,0 +1,75 @@
+/* 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 EEM_SITE_H
+#define EEM_SITE_H
+
+#include "common/rect.h"
+#include "common/scummsys.h"
+
+namespace EEM {
+
+class EEMEngine;
+class Mystery;
+
+/// One hotspot (search rectangle) within a site, 14 bytes on disk.
+struct Hotspot {
+	int16  x1, y1, x2, y2;     ///< rectangle in screen coordinates
+	uint16 clueOffset;          ///< +8: byte offset of ClueBlock in the mystery file
+	uint16 hotspotIndex;        ///< +10: 1-based hotspot ordinal within the site
+	uint16 extra;               ///< +12: unknown (zero in M0..M54)
+
+	Common::Rect rect() const { return Common::Rect(x1, y1, x2, y2); }
+};
+
+/**
+ * Site / scene controller.
+ *
+ * Walks the mystery's SiteIndex to render one site at a time, polls hotspots
+ * for the player's search clicks, and dispatches clue display. Mirrors the
+ * site loop driven by `_DrawSearchButtons` @ 2404:0a8f and `_SearchButtons`
+ * @ 2404:0bfb.
+ */
+class SiteScreen {
+public:
+	SiteScreen(EEMEngine *vm, Mystery *mystery)
+		: _vm(vm), _mystery(mystery) {}
+
+	/** Enter site @p siteNum. Renders BG + hotspots; runs the input loop. */
+	void enter(uint siteNum);
+
+	/// Run the per-mystery loop: site -> map -> next site, ESC exits.
+	void run();
+
+private:
+	void renderBackground(uint siteNum);
+	void renderHotspots(uint siteNum);
+	int  hotspotAtPoint(uint siteNum, int x, int y) const;
+	void onHotspotClicked(uint siteNum, uint hotIdx);
+
+	EEMEngine *_vm;
+	Mystery *_mystery;
+	bool _showHotspots = true;  ///< Toggle outlines with V key.
+};
+
+} // End of namespace EEM
+
+#endif


Commit: 8ccba0b3474d69485c07cd04d14e572b1c319078
    https://github.com/scummvm/scummvm/commit/8ccba0b3474d69485c07cd04d14e572b1c319078
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:20:33+02:00

Commit Message:
EEM: correct font rendering using char to glyph table

Changed paths:
    engines/eem/eem.cpp
    engines/eem/font.cpp


diff --git a/engines/eem/eem.cpp b/engines/eem/eem.cpp
index 4bd0da9430a..3ac46881d01 100644
--- a/engines/eem/eem.cpp
+++ b/engines/eem/eem.cpp
@@ -753,7 +753,11 @@ static Common::String parseString(const Common::String &raw,
 		const byte c = (byte)raw[i];
 		if (c == 0x80) {
 			out += playerName;
-		} else if (c == 0x82) {
+		} else if (c == 0x81 || c == 0x82) {
+			// Both forms substitute the partner's name. The original
+			// likely has a casual ("Jake/Jenny") and a formal
+			// ("Jake/Jennifer") variant; we render the same partner
+			// name in both spots ??? close enough for natural reading.
 			out += partnerName;
 		} else if (c >= 0x80 && c < 0x8A) {
 			// Other control opcodes: eat them silently for now.
diff --git a/engines/eem/font.cpp b/engines/eem/font.cpp
index f8ca1865c8d..45d4a12a894 100644
--- a/engines/eem/font.cpp
+++ b/engines/eem/font.cpp
@@ -32,6 +32,34 @@
 
 namespace EEM {
 
+// Character -> glyph translation table from CHR2FNT segment (29b6:0000).
+// 128 bytes mapping ASCII 0..127 to a glyph index in FONT.FNT (which only
+// stores 85 glyphs covering ' ', '!'..'9', ':', 'A'..'Z'). Lowercase
+// letters map to uppercase glyphs; chars without a glyph map to 0.
+static const byte kCharToGlyph[128] = {
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // 0x20..0x27 ' '..'\''
+	0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, // 0x28..0x2F '('..'/'
+	0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, // 0x30..0x37 '0'..'7'
+	0x18, 0x19, 0x1A, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x38..0x3F '8','9',':'..
+	0x00, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, 0x40, 0x41, // 0x40..0x47 '@','A'..'G'
+	0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, // 0x48..0x4F 'H'..'O'
+	0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51, // 0x50..0x57 'P'..'W'
+	0x52, 0x53, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x58..0x5F 'X','Y','Z'..
+	0x00, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, 0x40, 0x41, // 0x60..0x67 '`','a'..'g'
+	0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, // 0x68..0x6F 'h'..'o'
+	0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51, // 0x70..0x77 'p'..'w'
+	0x52, 0x53, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00  // 0x78..0x7F 'x','y','z'..
+};
+
+static inline byte mapChar(byte c) {
+	return c < 128 ? kCharToGlyph[c] : 0;
+}
+
+
 bool EEMFont::load(const Common::Path &path) {
 	Common::File f;
 	if (!f.open(path)) {
@@ -68,9 +96,10 @@ bool EEMFont::load(const Common::Path &path) {
 }
 
 int EEMFont::charWidth(byte c) const {
-	if (c >= _glyphs.size())
+	const byte g = mapChar(c);
+	if (g >= _glyphs.size())
 		return 0;
-	return _glyphs[c].widthBits;
+	return _glyphs[g].widthBits;
 }
 
 int EEMFont::stringWidth(const Common::String &s) const {
@@ -81,9 +110,12 @@ int EEMFont::stringWidth(const Common::String &s) const {
 }
 
 int EEMFont::drawChar(Graphics::ManagedSurface *dst, int x, int y, byte c, byte color) const {
-	if (!dst || c >= _glyphs.size())
+	if (!dst)
+		return 0;
+	const byte gi = mapChar(c);
+	if (gi >= _glyphs.size())
 		return 0;
-	const FontGlyph &g = _glyphs[c];
+	const FontGlyph &g = _glyphs[gi];
 	if (g.bitmap.empty())
 		return g.widthBits;
 


Commit: f349111de13c1c9ced40cd28a13ebcf2120f10e6
    https://github.com/scummvm/scummvm/commit/f349111de13c1c9ced40cd28a13ebcf2120f10e6
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:20:33+02:00

Commit Message:
EEM: use Font API to reduce code duplication

Changed paths:
    engines/eem/eem.cpp
    engines/eem/font.cpp
    engines/eem/font.h
    engines/eem/site.cpp


diff --git a/engines/eem/eem.cpp b/engines/eem/eem.cpp
index 3ac46881d01..793f1b6505a 100644
--- a/engines/eem/eem.cpp
+++ b/engines/eem/eem.cpp
@@ -388,13 +388,11 @@ void EEMEngine::doNewPlayer() {
 				memcpy((byte *)scratch.getBasePtr(0, row),
 					   (const byte *)bg.surface.getBasePtr(0, row), w);
 		}
-		_font.drawString(&scratch, 40, 24,
-			"Welcome to Eagle Eye Mysteries!", 0xF);
-		_font.drawString(&scratch, 40, 40, "Please type your name:", 0xF);
-		_font.drawString(&scratch, 40, 60,
-			"(Backspace to delete, Enter to confirm)", 0xF);
+		_font.drawString(&scratch, "Welcome to Eagle Eye Mysteries!", 40, 24, 320, 0xF);
+		_font.drawString(&scratch, "Please type your name:", 40, 40, 320, 0xF);
+		_font.drawString(&scratch, "(Backspace to delete, Enter to confirm)", 40, 60, 320, 0xF);
 		Common::String shown = name + "_";
-		_font.drawString(&scratch, 40, 90, shown, 0xF);
+		_font.drawString(&scratch, shown, 40, 90, 320, 0xF);
 		g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
 								   0, 0, 320, 200);
 		g_system->updateScreen();
@@ -555,8 +553,7 @@ void EEMEngine::doCaseSelection() {
 			}
 		}
 		if (_font.isLoaded()) {
-			_font.drawString(&scratch, 8, 4,
-				Common::String::format("EAGLE EYE - %s", _playerName.c_str()), 0xF);
+			_font.drawString(&scratch, Common::String::format("EAGLE EYE - %s", _playerName.c_str()), 8, 4, 320, 0xF);
 
 			// Solved count.
 			uint solved = 0, perfectSolved = 0;
@@ -564,15 +561,12 @@ void EEMEngine::doCaseSelection() {
 				if (_mysteriesSolved[i] >= 1) solved++;
 				if (_mysteriesSolved[i] == 2) perfectSolved++;
 			}
-			_font.drawString(&scratch, 200, 4,
-				Common::String::format("solved %u (1st try %u)",
-									   solved, perfectSolved), 0xF);
+			_font.drawString(&scratch, Common::String::format("solved %u (1st try %u)",
+									   solved, perfectSolved), 200, 4, 320, 0xF);
 			if (perfectSolved >= 55) {
-				_font.drawString(&scratch, 8, 168,
-					"** PERFECT MASTER SLEUTH! **", 0xF);
+				_font.drawString(&scratch, "** PERFECT MASTER SLEUTH! **", 8, 168, 320, 0xF);
 			} else if (solved >= 55) {
-				_font.drawString(&scratch, 8, 168,
-					"** ALL MYSTERIES SOLVED! **", 0xF);
+				_font.drawString(&scratch, "** ALL MYSTERIES SOLVED! **", 8, 168, 320, 0xF);
 			}
 
 			char marker = ' ';
@@ -586,29 +580,18 @@ void EEMEngine::doCaseSelection() {
 			if (sel >= 1 && sel <= 24) tier = "Junior Sleuth";
 			else if (sel >= 25 && sel <= 48) tier = "Senior Sleuth";
 			else if (sel >= 49 && sel <= 54) tier = "Master Sleuth";
-			_font.drawString(&scratch, 8, 24,
-				Common::String::format("Mystery %u  %c  [%s]",
-									   sel, marker, tier), 0xF);
-			_font.drawString(&scratch, 8, 40,
-				"  0..9        quick select", 0xF);
-			_font.drawString(&scratch, 8, 52,
-				"  Tab / +     next mystery", 0xF);
-			_font.drawString(&scratch, 8, 64,
-				"  Shift+Tab   prev mystery", 0xF);
-			_font.drawString(&scratch, 8, 76,
-				"  PgUp/PgDn   jump 10", 0xF);
-			_font.drawString(&scratch, 8, 88,
-				"  Home/End    first/last", 0xF);
-			_font.drawString(&scratch, 8, 100,
-				"  Enter       start mystery", 0xF);
-			_font.drawString(&scratch, 8, 112,
-				"  F5          save / load (ScummVM)", 0xF);
-			_font.drawString(&scratch, 8, 124,
-				"  ESC         quit", 0xF);
-			_font.drawString(&scratch, 8, 144,
-				"  *  solved on first try", 0xF);
-			_font.drawString(&scratch, 8, 156,
-				"  +  solved", 0xF);
+			_font.drawString(&scratch, Common::String::format("Mystery %u  %c  [%s]",
+									   sel, marker, tier), 8, 24, 320, 0xF);
+			_font.drawString(&scratch, "  0..9        quick select", 8, 40, 320, 0xF);
+			_font.drawString(&scratch, "  Tab / +     next mystery", 8, 52, 320, 0xF);
+			_font.drawString(&scratch, "  Shift+Tab   prev mystery", 8, 64, 320, 0xF);
+			_font.drawString(&scratch, "  PgUp/PgDn   jump 10", 8, 76, 320, 0xF);
+			_font.drawString(&scratch, "  Home/End    first/last", 8, 88, 320, 0xF);
+			_font.drawString(&scratch, "  Enter       start mystery", 8, 100, 320, 0xF);
+			_font.drawString(&scratch, "  F5          save / load (ScummVM)", 8, 112, 320, 0xF);
+			_font.drawString(&scratch, "  ESC         quit", 8, 124, 320, 0xF);
+			_font.drawString(&scratch, "  *  solved on first try", 8, 144, 320, 0xF);
+			_font.drawString(&scratch, "  +  solved", 8, 156, 320, 0xF);
 		}
 		g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
 								   0, 0, 320, 200);
@@ -910,7 +893,7 @@ void EEMEngine::displayClue(const byte *clueBlock) {
 			int textY = bubY;
 			int textW = MIN<int>(320 - bubX, 200);
 			int copyY = bubY;
-			int copyH = _font.height() * 4 + 8;
+			int copyH = _font.getFontHeight() * 4 + 8;
 
 			if (haveBalloon) {
 				const int bw = MIN<int>(balloon.surface.w, 320 - bubX);
@@ -1002,9 +985,8 @@ void EEMEngine::doNotebook() {
 		Graphics::ManagedSurface scratch(320, 200,
 			Graphics::PixelFormat::createFormatCLUT8());
 		scratch.clear();
-		_font.drawString(&scratch, 8, 4, "NOTEBOOK", 0xF);
-		_font.drawString(&scratch, 200, 4,
-			Common::String::format("pts: %d", _mystery.selectedPoints()), 0xF);
+		_font.drawString(&scratch, "NOTEBOOK", 8, 4, 320, 0xF);
+		_font.drawString(&scratch, Common::String::format("pts: %d", _mystery.selectedPoints()), 200, 4, 320, 0xF);
 
 		// Build a list of found-clue indices.
 		Common::Array<uint> found;
@@ -1015,13 +997,12 @@ void EEMEngine::doNotebook() {
 		const int pages = MAX<int>(1, (total + kPerPage - 1) / kPerPage);
 		page = MIN<int>(page, pages - 1);
 
-		_font.drawString(&scratch, 200, 16,
-			Common::String::format("page %d/%d", page + 1, pages), 0xF);
+		_font.drawString(&scratch, Common::String::format("page %d/%d", page + 1, pages), 200, 16, 320, 0xF);
 
 		const byte *ni = _mystery.noteIndex();
 		const uint16 niCount = _mystery.noteIndexCount();
 		const Common::String partnerName = (_partner == 0) ? "Jake" : "Jennifer";
-		int y = 4 + _font.height() * 2 + 4;
+		int y = 4 + _font.getFontHeight() * 2 + 4;
 		for (int slot = 0; slot < kPerPage; slot++) {
 			const int idx = page * kPerPage + slot;
 			if (idx >= total)
@@ -1118,7 +1099,7 @@ void EEMEngine::doGallery() {
 	}
 
 	if (_font.isLoaded())
-		_font.drawString(&scratch, 8, 4, "GALLERY", 0xF);
+		_font.drawString(&scratch, "GALLERY", 8, 4, 320, 0xF);
 
 	const uint8 num = _mystery.numSuspects();
 	int slotX = 8;
@@ -1147,7 +1128,7 @@ void EEMEngine::doGallery() {
 									_mystery._inGallery[i];
 			Common::String label = Common::String::format("%u%s",
 				i + 1, discovered ? " *" : "");
-			_font.drawString(&scratch, placeX + 4, placeY + h + 2, label, 0xF);
+			_font.drawString(&scratch, label, placeX + 4, placeY + h + 2, 320, 0xF);
 		}
 		slotX += slotStep;
 	}
@@ -1254,7 +1235,7 @@ void EEMEngine::doBigMap() {
 				scratch.fillRect(mark, color);
 				if (_font.isLoaded()) {
 					Common::String num = Common::String::format("%u", i);
-					_font.drawString(&scratch, sx + 4, sy - 4, num, color);
+					_font.drawString(&scratch, num, sx + 4, sy - 4, 320, color);
 				}
 			}
 		}
@@ -1263,19 +1244,18 @@ void EEMEngine::doBigMap() {
 		if (_font.isLoaded() && _mystery.isLoaded()) {
 			const int panelX = kMapWinX + kMapWinW + 4;
 			int y = 4;
-			_font.drawString(&scratch, panelX, y, "TRAVEL", 0xF);
-			y += _font.height() + 4;
+			_font.drawString(&scratch, "TRAVEL", panelX, y, 320, 0xF);
+			y += _font.getFontHeight() + 4;
 			for (uint i = 0; i < _mystery.numSites() && y < 192; i++) {
 				if (!_mystery._onSites[i] && i != _mystery._siteNumber)
 					continue;
 				const char marker = (i == _mystery._siteNumber) ? '>' : ' ';
 				Common::String label = Common::String::format(
 					"%c %u", marker, i);
-				_font.drawString(&scratch, panelX, y, label, 0x0F);
-				y += _font.height() + 1;
+				_font.drawString(&scratch, label, panelX, y, 320, 0x0F);
+				y += _font.getFontHeight() + 1;
 			}
-			_font.drawString(&scratch, panelX, 188,
-							 "Esc", 0x0F);
+			_font.drawString(&scratch, "Esc", panelX, 188, 320, 0x0F);
 		}
 
 		g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
@@ -1350,8 +1330,8 @@ void EEMEngine::doBigMap() {
 				const int panelX = kMapWinX + kMapWinW + 4;
 				if (_font.isLoaded() && _mystery.isLoaded() &&
 					ev.mouse.x >= panelX) {
-					const int row = (ev.mouse.y - 4 - _font.height() - 4) /
-									(_font.height() + 1);
+					const int row = (ev.mouse.y - 4 - _font.getFontHeight() - 4) /
+									(_font.getFontHeight() + 1);
 					int seen = 0;
 					for (uint i = 0; i < _mystery.numSites(); i++) {
 						if (!_mystery._onSites[i] && i != _mystery._siteNumber)
@@ -1401,7 +1381,7 @@ void EEMEngine::doHelp() {
 	Graphics::ManagedSurface scratch(320, 200,
 		Graphics::PixelFormat::createFormatCLUT8());
 	scratch.clear();
-	_font.drawString(&scratch, 8, 4, "HELP", 0xF);
+	_font.drawString(&scratch, "HELP", 8, 4, 320, 0xF);
 	_font.drawWordWrapped(&scratch, 8, 24, 304, text, 0xF);
 	g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
 							   0, 0, 320, 200);
@@ -1435,10 +1415,9 @@ bool EEMEngine::areYouSure() {
 			   (const byte *)saved.getBasePtr(0, row), 320);
 	scratch.fillRect(dlg, 0);
 	scratch.frameRect(dlg, 0xF);
-	_font.drawString(&scratch, dlg.left + 8, dlg.top + 8,
-		"Are you sure you want to quit?", 0xF);
-	_font.drawString(&scratch, dlg.left + 16, dlg.top + 36, "Y - Yes", 0xF);
-	_font.drawString(&scratch, dlg.left + 100, dlg.top + 36, "N - No", 0xF);
+	_font.drawString(&scratch, "Are you sure you want to quit?", dlg.left + 8, dlg.top + 8, 320, 0xF);
+	_font.drawString(&scratch, "Y - Yes", dlg.left + 16, dlg.top + 36, 320, 0xF);
+	_font.drawString(&scratch, "N - No", dlg.left + 100, dlg.top + 36, 320, 0xF);
 	g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
 							   0, 0, 320, 200);
 	g_system->updateScreen();
@@ -1507,7 +1486,7 @@ void EEMEngine::doAccuse() {
 			}
 		}
 		if (_font.isLoaded())
-			_font.drawString(&scratch, 8, 4, "ACCUSE", 0xF);
+			_font.drawString(&scratch, "ACCUSE", 8, 4, 320, 0xF);
 
 		for (uint i = 0; i < num; i++) {
 			if (!gd) continue;
@@ -1529,13 +1508,11 @@ void EEMEngine::doAccuse() {
 			}
 			if (_font.isLoaded()) {
 				Common::String label = Common::String::format("%u", i + 1);
-				_font.drawString(&scratch, placeX + 4,
-					placeY + h + 2, label, 0xF);
+				_font.drawString(&scratch, label, placeX + 4, placeY + h + 2, 320, 0xF);
 			}
 		}
 		if (_font.isLoaded()) {
-			_font.drawString(&scratch, 8, 180,
-				"Click a suspect or press 1..N - ESC to cancel", 0xF);
+			_font.drawString(&scratch, "Click a suspect or press 1..N - ESC to cancel", 8, 180, 320, 0xF);
 		}
 		g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
 								   0, 0, 320, 200);
@@ -1668,17 +1645,15 @@ void EEMEngine::doAccuse() {
 			Common::String banner = "Not enough evidence";
 			if (guessedRight)
 				banner = _mystery._firstTry ? "CORRECT - FIRST TRY!" : "CORRECT!";
-			_font.drawString(&scratch, 8, 4, banner, 0xF);
-			_font.drawString(&scratch, 8, 16,
-				Common::String::format("Evidence: %d/100  Suspect: %d",
-									   points, picked + 1), 0xF);
+			_font.drawString(&scratch, banner, 8, 4, 320, 0xF);
+			_font.drawString(&scratch, Common::String::format("Evidence: %d/100  Suspect: %d",
+									   points, picked + 1), 8, 16, 320, 0xF);
 			const int wrapW = MAX<int>(16, x2 - x1);
 			const int wrapY = MAX<int>(28, (int)y1);
 			(void)y2;
 			_font.drawWordWrapped(&scratch, x1, wrapY, wrapW, txt, 0xF);
-			_font.drawString(&scratch, 8, 188,
-				Common::String::format("page %u/%u  (Left/Right or click)",
-									   pageIdx + 1, pages), 0xF);
+			_font.drawString(&scratch, Common::String::format("page %u/%u  (Left/Right or click)",
+									   pageIdx + 1, pages), 8, 188, 320, 0xF);
 		}
 
 		g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
diff --git a/engines/eem/font.cpp b/engines/eem/font.cpp
index 45d4a12a894..d01e22d3a0e 100644
--- a/engines/eem/font.cpp
+++ b/engines/eem/font.cpp
@@ -22,9 +22,7 @@
 #include "common/debug.h"
 #include "common/file.h"
 #include "common/textconsole.h"
-#include "common/tokenizer.h"
 
-#include "graphics/managed_surface.h"
 #include "graphics/surface.h"
 
 #include "eem/detection.h"
@@ -32,9 +30,9 @@
 
 namespace EEM {
 
-// Character -> glyph translation table from CHR2FNT segment (29b6:0000).
-// 128 bytes mapping ASCII 0..127 to a glyph index in FONT.FNT (which only
-// stores 85 glyphs covering ' ', '!'..'9', ':', 'A'..'Z'). Lowercase
+// Character ??? glyph translation table from segment CHR2FNT (29b6:0000).
+// 128 bytes mapping ASCII 0..127 to a glyph index in FONT.FNT (which
+// only stores 85 glyphs covering ' ', '!'..'9', ':', 'A'..'Z'). Lowercase
 // letters map to uppercase glyphs; chars without a glyph map to 0.
 static const byte kCharToGlyph[128] = {
 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -55,11 +53,10 @@ static const byte kCharToGlyph[128] = {
 	0x52, 0x53, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00  // 0x78..0x7F 'x','y','z'..
 };
 
-static inline byte mapChar(byte c) {
+static inline byte mapChar(uint32 c) {
 	return c < 128 ? kCharToGlyph[c] : 0;
 }
 
-
 bool EEMFont::load(const Common::Path &path) {
 	Common::File f;
 	if (!f.open(path)) {
@@ -69,10 +66,12 @@ bool EEMFont::load(const Common::Path &path) {
 
 	const uint16 numChars = f.readUint16LE();
 	if (numChars == 0 || numChars > 256) {
-		warning("EEMFont::load: %s reports %u chars", path.toString().c_str(), numChars);
+		warning("EEMFont::load: %s reports %u chars",
+				path.toString().c_str(), numChars);
 		return false;
 	}
 	_glyphs.resize(numChars);
+	_maxHeight = _maxWidth = 0;
 
 	for (uint i = 0; i < numChars; i++) {
 		FontGlyph &g = _glyphs[i];
@@ -88,36 +87,43 @@ bool EEMFont::load(const Common::Path &path) {
 		}
 		if (g.height > _maxHeight)
 			_maxHeight = g.height;
+		if (g.widthBits > _maxWidth)
+			_maxWidth = g.widthBits;
 	}
 
-	debugC(1, kDebugGfx, "Font %s loaded: %u glyphs, max h=%u",
-		   path.toString().c_str(), numChars, _maxHeight);
+	debugC(1, kDebugGfx, "Font %s loaded: %u glyphs, max %ux%u",
+		   path.toString().c_str(), numChars, _maxWidth, _maxHeight);
 	return true;
 }
 
-int EEMFont::charWidth(byte c) const {
-	const byte g = mapChar(c);
-	if (g >= _glyphs.size())
+int EEMFont::getCharWidth(uint32 chr) const {
+	const byte gi = mapChar(chr);
+	if (gi >= _glyphs.size())
 		return 0;
-	return _glyphs[g].widthBits;
+	return _glyphs[gi].widthBits;
 }
 
-int EEMFont::stringWidth(const Common::String &s) const {
-	int w = 0;
-	for (uint i = 0; i < s.size(); i++)
-		w += charWidth((byte)s[i]);
-	return w;
+int EEMFont::drawWordWrapped(Graphics::ManagedSurface *dst, int x, int y,
+							 int width, const Common::String &s,
+							 uint32 color) const {
+	Common::Array<Common::String> lines;
+	wordWrapText(s, width, lines);
+	const int lineH = getFontHeight() + 1;
+	for (uint i = 0; i < lines.size(); i++)
+		drawString(dst, lines[i], x, y + (int)i * lineH, width, color);
+	return (int)lines.size() * lineH;
 }
 
-int EEMFont::drawChar(Graphics::ManagedSurface *dst, int x, int y, byte c, byte color) const {
+void EEMFont::drawChar(Graphics::Surface *dst, uint32 chr, int x, int y,
+					   uint32 color) const {
 	if (!dst)
-		return 0;
-	const byte gi = mapChar(c);
+		return;
+	const byte gi = mapChar(chr);
 	if (gi >= _glyphs.size())
-		return 0;
+		return;
 	const FontGlyph &g = _glyphs[gi];
 	if (g.bitmap.empty())
-		return g.widthBits;
+		return;
 
 	const uint bytesPerRow = (g.widthBits + 7) / 8;
 	for (uint row = 0; row < g.height; row++) {
@@ -132,40 +138,9 @@ int EEMFont::drawChar(Graphics::ManagedSurface *dst, int x, int y, byte c, byte
 				continue;
 			const byte mask = (byte)(0x80 >> (bit & 7));
 			if (srcRow[bit / 8] & mask)
-				dstRow[dstX] = color;
-		}
-	}
-	return g.widthBits;
-}
-
-int EEMFont::drawString(Graphics::ManagedSurface *dst, int x, int y,
-						const Common::String &s, byte color) const {
-	int penX = x;
-	for (uint i = 0; i < s.size(); i++)
-		penX += drawChar(dst, penX, y, (byte)s[i], color);
-	return penX - x;
-}
-
-int EEMFont::drawWordWrapped(Graphics::ManagedSurface *dst, int x, int y, int width,
-							 const Common::String &s, byte color) const {
-	Common::StringTokenizer tok(s, " \t");
-	int penY = y;
-	Common::String line;
-
-	while (!tok.empty()) {
-		const Common::String word = tok.nextToken();
-		Common::String trial = line.empty() ? word : line + " " + word;
-		if (stringWidth(trial) <= width) {
-			line = trial;
-		} else {
-			drawString(dst, x, penY, line, color);
-			penY += _maxHeight + 1;
-			line = word;
+				dstRow[dstX] = (byte)color;
 		}
 	}
-	if (!line.empty())
-		drawString(dst, x, penY, line, color);
-	return penY + _maxHeight - y;
 }
 
 } // End of namespace EEM
diff --git a/engines/eem/font.h b/engines/eem/font.h
index f175d5360fa..6ccfd6d4946 100644
--- a/engines/eem/font.h
+++ b/engines/eem/font.h
@@ -27,9 +27,7 @@
 #include "common/scummsys.h"
 #include "common/str.h"
 
-namespace Graphics {
-class ManagedSurface;
-}
+#include "graphics/font.h"
 
 namespace EEM {
 
@@ -49,40 +47,37 @@ struct FontGlyph {
  *   - u16 numChars
  *   - per char: u8 height, u8 widthBits, u8 sizeBytes, bytes[sizeBytes] bitmap
  *
- * Drawing mirrors `_ShowChar` @ 1b66:0346: each set bit becomes `fontColor`
- * on the destination surface; clear bits are transparent.
+ * Subclasses `Graphics::Font` so callers can use the standard
+ * `drawString` / `drawStringUnboxed` / `wordWrapText` helpers without
+ * us reimplementing them. Lookups go through a 128-byte char???glyph
+ * translation table extracted from CHR2FNT (segment 29b6:0000) ??? the
+ * font is uppercase-only with lowercase aliased to uppercase glyphs.
  */
-class EEMFont {
+class EEMFont : public Graphics::Font {
 public:
 	EEMFont() = default;
 
 	bool load(const Common::Path &path);
+	bool isLoaded() const { return !_glyphs.empty(); }
 
-	uint16 height() const { return _maxHeight; }
-
-	int charWidth(byte c) const;
-
-	/// Total pixel width of @p s when rendered (no shadow).
-	int stringWidth(const Common::String &s) const;
-
-	/// Draw @p c at (@p x, @p y) on @p dst with foreground @p color.
-	/// Returns the advance width.
-	int drawChar(Graphics::ManagedSurface *dst, int x, int y, byte c, byte color) const;
-
-	/// Draw @p s at (@p x, @p y) and return total advance width.
-	int drawString(Graphics::ManagedSurface *dst, int x, int y,
-				   const Common::String &s, byte color) const;
-
-	/// Word-wrap @p s into the rect (x..x+width, y..) and draw line by line.
-	/// Mirrors the data flow of `_DoWordWrap` @ 1b66:04a7.
-	int drawWordWrapped(Graphics::ManagedSurface *dst, int x, int y, int width,
-						const Common::String &s, byte color) const;
+	// --- Graphics::Font overrides ---
+	int getFontHeight() const override { return _maxHeight; }
+	int getMaxCharWidth() const override { return _maxWidth; }
+	int getCharWidth(uint32 chr) const override;
+	void drawChar(Graphics::Surface *dst, uint32 chr, int x, int y,
+				  uint32 color) const override;
+	using Graphics::Font::drawChar;  // keep ManagedSurface overload
 
-	bool isLoaded() const { return !_glyphs.empty(); }
+	/// Convenience wrap-and-draw helper that uses the inherited
+	/// `wordWrapText` to break @p s into lines and then `drawString`
+	/// to render each. Returns the total height drawn.
+	int drawWordWrapped(Graphics::ManagedSurface *dst, int x, int y,
+						int width, const Common::String &s, uint32 color) const;
 
 private:
 	Common::Array<FontGlyph> _glyphs;
 	uint16 _maxHeight = 0;
+	uint16 _maxWidth  = 0;
 };
 
 } // End of namespace EEM
diff --git a/engines/eem/site.cpp b/engines/eem/site.cpp
index b20a92a96ea..319f31b3f68 100644
--- a/engines/eem/site.cpp
+++ b/engines/eem/site.cpp
@@ -155,8 +155,8 @@ void SiteScreen::run() {
 							"Selected-points > 99 wins the case."
 						};
 						for (uint i = 0; i < sizeof(lines)/sizeof(lines[0]); i++) {
-							fnt.drawString(&help, 8, y, lines[i], 0xF);
-							y += fnt.height() + 1;
+							fnt.drawString(&help, lines[i], 8, y, 320, 0xF);
+							y += fnt.getFontHeight() + 1;
 						}
 						g_system->copyRectToScreen(help.getPixels(),
 							help.pitch, 0, 0, 320, 200);
@@ -253,7 +253,7 @@ void SiteScreen::renderHotspots(uint siteNum) {
 			Graphics::ManagedSurface mgr(320, 9,
 				Graphics::PixelFormat::createFormatCLUT8());
 			mgr.clear();
-			_vm->getFont().drawString(&mgr, 4, 0, hud, 0x0F);
+			_vm->getFont().drawString(&mgr, hud, 4, 0, 320, 0x0F);
 			for (int row = 0; row < 8; row++) {
 				memcpy((byte *)screen->getBasePtr(0, hudY + row),
 					   (const byte *)mgr.getBasePtr(0, row), 320);


Commit: 17d95660b896e97b384b14d0101af3945b8c5f70
    https://github.com/scummvm/scummvm/commit/17d95660b896e97b384b14d0101af3945b8c5f70
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:20:33+02:00

Commit Message:
EEM: completed kCharToGlyph

Changed paths:
    engines/eem/eem.cpp
    engines/eem/font.cpp


diff --git a/engines/eem/eem.cpp b/engines/eem/eem.cpp
index 793f1b6505a..981329193e0 100644
--- a/engines/eem/eem.cpp
+++ b/engines/eem/eem.cpp
@@ -388,11 +388,12 @@ void EEMEngine::doNewPlayer() {
 				memcpy((byte *)scratch.getBasePtr(0, row),
 					   (const byte *)bg.surface.getBasePtr(0, row), w);
 		}
-		_font.drawString(&scratch, "Welcome to Eagle Eye Mysteries!", 40, 24, 320, 0xF);
-		_font.drawString(&scratch, "Please type your name:", 40, 40, 320, 0xF);
-		_font.drawString(&scratch, "(Backspace to delete, Enter to confirm)", 40, 60, 320, 0xF);
+		// Match the original `_NewPlayer`: `_Show_String(rw=0x28, cl=0x50)`
+		// for the prompt, then `_ShowChar(0x50, x, ???)` for typed input.
+		// (rw=row=y, cl=col=x.) Prompt at (y=40, x=80), input at (y=80, x=80).
+		_font.drawString(&scratch, "Please type your name:", 80, 40, 240, 0xF);
 		Common::String shown = name + "_";
-		_font.drawString(&scratch, shown, 40, 90, 320, 0xF);
+		_font.drawString(&scratch, shown, 80, 80, 240, 0xF);
 		g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
 								   0, 0, 320, 200);
 		g_system->updateScreen();
diff --git a/engines/eem/font.cpp b/engines/eem/font.cpp
index d01e22d3a0e..19f40c1b77e 100644
--- a/engines/eem/font.cpp
+++ b/engines/eem/font.cpp
@@ -30,10 +30,18 @@
 
 namespace EEM {
 
-// Character ??? glyph translation table from segment CHR2FNT (29b6:0000).
-// 128 bytes mapping ASCII 0..127 to a glyph index in FONT.FNT (which
-// only stores 85 glyphs covering ' ', '!'..'9', ':', 'A'..'Z'). Lowercase
-// letters map to uppercase glyphs; chars without a glyph map to 0.
+// Character ??? glyph translation table.
+//
+// FONT.FNT layout (verified by dumping glyph bitmaps):
+//   index 0..26  : ' ' .. ':' (punctuation, digits)
+//   index 27..32 : ';' '<' '=' '>' '?' '@'
+//   index 33..58 : UPPERCASE A..Z
+//   index 59..84 : lowercase a..z
+//
+// The CHR2FNT segment table at 29b6:0000 in the original maps both 'A'
+// and 'a' to the lowercase glyph (so the original engine renders all
+// text in lowercase). We route uppercase ASCII letters to the uppercase
+// glyph slots (33..58) for proper mixed-case rendering.
 static const byte kCharToGlyph[128] = {
 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -42,11 +50,11 @@ static const byte kCharToGlyph[128] = {
 	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // 0x20..0x27 ' '..'\''
 	0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, // 0x28..0x2F '('..'/'
 	0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, // 0x30..0x37 '0'..'7'
-	0x18, 0x19, 0x1A, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x38..0x3F '8','9',':'..
-	0x00, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, 0x40, 0x41, // 0x40..0x47 '@','A'..'G'
-	0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, // 0x48..0x4F 'H'..'O'
-	0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51, // 0x50..0x57 'P'..'W'
-	0x52, 0x53, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x58..0x5F 'X','Y','Z'..
+	0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, // 0x38..0x3F '8','9',':',';','<','=','>','?'
+	0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, // 0x40..0x47 '@','A'..'G'
+	0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, // 0x48..0x4F 'H'..'O'
+	0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, // 0x50..0x57 'P'..'W'
+	0x38, 0x39, 0x3A, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x58..0x5F 'X','Y','Z'..
 	0x00, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, 0x40, 0x41, // 0x60..0x67 '`','a'..'g'
 	0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, // 0x68..0x6F 'h'..'o'
 	0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51, // 0x70..0x77 'p'..'w'


Commit: 57dc88dafe89fdec344074a193af45a011293ecb
    https://github.com/scummvm/scummvm/commit/57dc88dafe89fdec344074a193af45a011293ecb
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:20:34+02:00

Commit Message:
EEM: show mouse when selecting character

Changed paths:
    engines/eem/eem.cpp
    engines/eem/eem.h


diff --git a/engines/eem/eem.cpp b/engines/eem/eem.cpp
index 981329193e0..add9de7d1ef 100644
--- a/engines/eem/eem.cpp
+++ b/engines/eem/eem.cpp
@@ -30,6 +30,7 @@
 
 #include "engines/util.h"
 
+#include "graphics/cursorman.h"
 #include "graphics/paletteman.h"
 
 #include "eem/detection.h"
@@ -65,6 +66,35 @@ const int kBoyX  = 0xe2; // 226
 const int kBoyY  = 0x62; // 98
 const int kGirlX = 0x42; // 66
 const int kGirlY = 0x60; // 96
+
+// 11x16 mouse cursor ??? replaces the DOS hardware cursor wired in by
+// _InitMouse @ 152d:018b (INT 33h). The original game sets the cursor
+// visible/hidden via _MouseCursor; we leave it on once the screens
+// that need it (ChoosePartner, CaseSelection, sites) are reached.
+//   0 = transparent, 1 = black outline, 2 = white fill
+const byte kCursorBitmap[11 * 16] = {
+	1,1,0,0,0,0,0,0,0,0,0,
+	1,2,1,0,0,0,0,0,0,0,0,
+	1,2,2,1,0,0,0,0,0,0,0,
+	1,2,2,2,1,0,0,0,0,0,0,
+	1,2,2,2,2,1,0,0,0,0,0,
+	1,2,2,2,2,2,1,0,0,0,0,
+	1,2,2,2,2,2,2,1,0,0,0,
+	1,2,2,2,2,2,2,2,1,0,0,
+	1,2,2,2,2,2,2,2,2,1,0,
+	1,2,2,2,2,2,2,2,2,2,1,
+	1,2,2,2,2,2,1,0,0,0,0,
+	1,2,1,0,1,2,2,1,0,0,0,
+	1,1,0,0,1,2,2,1,0,0,0,
+	0,0,0,0,0,1,2,2,1,0,0,
+	0,0,0,0,0,1,2,2,1,0,0,
+	0,0,0,0,0,0,1,2,2,1,0
+};
+const byte kCursorPalette[] = {
+	0x00, 0x00, 0x00, // 0 ??? transparent (key)
+	0x00, 0x00, 0x00, // 1 ??? outline
+	0xFF, 0xFF, 0xFF  // 2 ??? fill
+};
 } // anonymous namespace
 
 EEMEngine::EEMEngine(OSystem *syst, const ADGameDescription *gameDesc)
@@ -90,6 +120,14 @@ Common::Error EEMEngine::run() {
 	if (!_font.load(Common::Path("FONT.FNT")))
 		warning("FONT.FNT failed to load; text will not render");
 
+	// _InitMouse @ 152d:018b in the original ??? install our 11x16 arrow,
+	// using palette index 0 as the transparency key. The cursor is left
+	// hidden through the opening anims and switched on at NewPlayer /
+	// ChoosePartner where the player actually clicks.
+	CursorMan.replaceCursor(kCursorBitmap, 11, 16, 0, 0, 0);
+	CursorMan.replaceCursorPalette(kCursorPalette, 0, 3);
+	CursorMan.showMouse(false);
+
 	// _AllBlack @ 172b:0d4b paints the screen black before the first handler.
 	byte black[3 * 256] = { 0 };
 	g_system->getPaletteManager()->setPalette(black, 0, 256);
@@ -107,6 +145,7 @@ Common::Error EEMEngine::run() {
 		if (err.getCode() == Common::kNoError && _mystery.isLoaded()) {
 			debugC(1, kDebugGeneral, "Resuming from slot %d at mystery %u",
 				   wantedSave, _mystery.number());
+			CursorMan.showMouse(true);
 			doInitClues();
 			doSiteLoop();
 			while (!shouldQuit()) {
@@ -122,27 +161,34 @@ Common::Error EEMEngine::run() {
 	// Reproduces _DoOpeningAnims @ 2520:082a (sans audio):
 	//   EA Kids logo (PIC) -> HighScore Productions logo (PIC) ->
 	//   Storm Software logo (BOLT.ANM) -> 20 character-intro animations
-	//   (ANIM01.A .. ANIM20.A) -> TITLE.ANM. Each can be skipped with a
-	//   click or any key.
+	//   (ANIM01.A .. ANIM20.A) -> TITLE.ANM. Click / any key skips a
+	//   single clip; ESC skips the rest of the chain (waitForInput /
+	//   playAnm raise `_skipIntro` so each subsequent step bails out).
+	_skipIntro = false;
 	showEAKidsLogo();
-	if (!shouldQuit())
+	if (!shouldQuit() && !_skipIntro)
 		showHighScoreLogo();
-	if (!shouldQuit())
+	if (!shouldQuit() && !_skipIntro)
 		playAnm(Common::Path("BOLT.ANM"));
-	for (int i = 1; i <= 20 && !shouldQuit(); i++) {
+	for (int i = 1; i <= 20 && !shouldQuit() && !_skipIntro; i++) {
 		Common::String name = Common::String::format("ANIM%02d.A", i);
 		playAnm(Common::Path(name));
 		// Between anims the original plays a voice clip via _SpoolSound;
 		// without audio we still want a beat so each scene reads.
-		if (!shouldQuit() && i != 20)
+		if (!shouldQuit() && !_skipIntro && i != 20)
 			waitForInput(2000);
 	}
-	if (!shouldQuit())
+	if (!shouldQuit() && !_skipIntro)
 		playAnm(Common::Path("TITLE.ANM"), 120, /*holdLastFrame=*/true);
+	_skipIntro = false;
 
 	// After the title chain, the original goes Title (B) -> screen 8
 	// (NewPlayer / saved-record selection) -> screen 9 (ChoosePartner) ->
 	// screen A (CaseSelection) -> site loop. We mirror the same order.
+	// Mouse stays hidden through the opening anims; show it now for
+	// the interactive screens (matches `_MouseCursor = 1` at the tail
+	// of `_NewPlayer`).
+	CursorMan.showMouse(true);
 	if (!shouldQuit())
 		doNewPlayer();
 	if (!shouldQuit())
@@ -261,6 +307,9 @@ void EEMEngine::playAnm(const Common::Path &path, uint frameDelayMs, bool holdLa
 		// Drain events and let the user skip with click/key. The original
 		// uses _CheckFrameRate / _kbhit; we use a simple fixed delay until
 		// the frame-rate calibration logic from _GetSpeedRating is wired up.
+		// ESC additionally sets `_skipIntro` so the opening-anim chain in
+		// run() bails out of the whole sequence instead of advancing to
+		// the next clip.
 		const uint32 frameStart = g_system->getMillis();
 		bool aborted = false;
 		while (g_system->getMillis() - frameStart < frameDelayMs && !aborted) {
@@ -268,8 +317,13 @@ void EEMEngine::playAnm(const Common::Path &path, uint frameDelayMs, bool holdLa
 			while (g_system->getEventManager()->pollEvent(event)) {
 				if (event.type == Common::EVENT_QUIT ||
 					event.type == Common::EVENT_RETURN_TO_LAUNCHER ||
-					event.type == Common::EVENT_LBUTTONDOWN ||
-					event.type == Common::EVENT_KEYDOWN) {
+					event.type == Common::EVENT_LBUTTONDOWN) {
+					aborted = true;
+					break;
+				}
+				if (event.type == Common::EVENT_KEYDOWN) {
+					if (event.kbd.keycode == Common::KEYCODE_ESCAPE)
+						_skipIntro = true;
 					aborted = true;
 					break;
 				}
@@ -280,7 +334,7 @@ void EEMEngine::playAnm(const Common::Path &path, uint frameDelayMs, bool holdLa
 			break;
 	}
 
-	if (holdLastFrame && !shouldQuit()) {
+	if (holdLastFrame && !shouldQuit() && !_skipIntro) {
 		// Mirror the wait-loop at the end of `_DoOpeningAnims`:
 		//   while (!keyDataAvailable) ;
 		// We accept either a click or a key.
@@ -290,8 +344,13 @@ void EEMEngine::playAnm(const Common::Path &path, uint frameDelayMs, bool holdLa
 			while (g_system->getEventManager()->pollEvent(ev)) {
 				if (ev.type == Common::EVENT_QUIT ||
 					ev.type == Common::EVENT_RETURN_TO_LAUNCHER ||
-					ev.type == Common::EVENT_LBUTTONDOWN ||
-					ev.type == Common::EVENT_KEYDOWN) {
+					ev.type == Common::EVENT_LBUTTONDOWN) {
+					clicked = true;
+					break;
+				}
+				if (ev.type == Common::EVENT_KEYDOWN) {
+					if (ev.kbd.keycode == Common::KEYCODE_ESCAPE)
+						_skipIntro = true;
 					clicked = true;
 					break;
 				}
@@ -315,14 +374,20 @@ void EEMEngine::blitAt(const Picture &pic, int x, int y) {
 }
 
 void EEMEngine::waitForInput(uint32 maxMs) {
+	// ESC additionally raises `_skipIntro` so the opening-anim chain
+	// can fast-forward past the rest of the sequence.
 	const uint32 startMs = g_system->getMillis();
 	while (!shouldQuit() && (g_system->getMillis() - startMs < maxMs)) {
 		Common::Event event;
 		while (g_system->getEventManager()->pollEvent(event)) {
 			if (event.type == Common::EVENT_QUIT ||
 				event.type == Common::EVENT_RETURN_TO_LAUNCHER ||
-				event.type == Common::EVENT_LBUTTONDOWN ||
-				event.type == Common::EVENT_KEYDOWN) {
+				event.type == Common::EVENT_LBUTTONDOWN) {
+				return;
+			}
+			if (event.type == Common::EVENT_KEYDOWN) {
+				if (event.kbd.keycode == Common::KEYCODE_ESCAPE)
+					_skipIntro = true;
 				return;
 			}
 		}
diff --git a/engines/eem/eem.h b/engines/eem/eem.h
index b73ef2ac2bc..ed75d78e5ec 100644
--- a/engines/eem/eem.h
+++ b/engines/eem/eem.h
@@ -198,6 +198,11 @@ private:
 	uint16 _lastScreen;  ///< Mirrors _LastScreen @ 2d5d:3f24
 	uint16 _nextScreen;  ///< Mirrors _NextScreen @ 2d5d:3f26
 	uint8  _partner;     ///< Mirrors _Partner: 0 = boy (Jake), 1 = girl (Jenny)
+
+	/// Set when ESC is pressed during an intro animation or logo. Tells
+	/// the opening-anim loop in run() to skip the rest of the chain
+	/// instead of asking the user to click through every screen.
+	bool _skipIntro = false;
 };
 
 } // End of namespace EEM


Commit: 84a82d5580235109bfdbab0d47a27159e7c6ed6c
    https://github.com/scummvm/scummvm/commit/84a82d5580235109bfdbab0d47a27159e7c6ed6c
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:20:34+02:00

Commit Message:
EEM: improved initial menu following the original implementation

Changed paths:
    engines/eem/eem.cpp


diff --git a/engines/eem/eem.cpp b/engines/eem/eem.cpp
index add9de7d1ef..c14c56b4f10 100644
--- a/engines/eem/eem.cpp
+++ b/engines/eem/eem.cpp
@@ -587,20 +587,57 @@ void EEMEngine::doChoosePartner() {
 
 void EEMEngine::doCaseSelection() {
 	// Mirrors `_CaseSelection` @ 1c33:0a87. The original draws PIC 0x41
-	// (chooser background) and a paginated list of mystery names rendered
-	// from M<n>.BIN headers, then calls `_DoChoose` to read a selection.
-	// We approximate with a numeric prompt (0..9 for first ten mysteries,
-	// Tab to cycle, Enter to load).
+	// (chooser background) plus a centred "Book %d" / "Challenge Book"
+	// header at (y=12) and then calls `_DoChoose(list)` to render the
+	// menu via `DrawList` @ 1c33:040d at (_TextBox+3, DAT_29be_0d02) =
+	// (61, 35), 12 rows ?? 10 px line height. The menu list itself is
+	// the static array at 29be:0d6a (verified via `push 0x0d6a` at
+	// 1c33:1ab4). Strings are at 29be:0ef4 onwards. Layout:
+	//   list[0]  = "----------------------------------"
+	//   list[1]  = "         Choose A Mystery"
+	//   list[2..10] = alternating menu items + separators
+	// Five selectable items: Choose A Mystery / Practice Mystery /
+	// See ScrapBook 1/2/3.
 	const uint kMaxMystery = 54;
-	// Default selection = the next unsolved mystery so post-win the
-	// player doesn't have to scroll to find what's left.
-	uint sel = 0;
-	for (uint i = 0; i <= kMaxMystery; i++) {
-		if (i < sizeof(_mysteriesSolved) && !_mysteriesSolved[i]) {
-			sel = i;
-			break;
-		}
-	}
+
+	enum MenuPick {
+		kPickChoose = 0,
+		kPickPractice,
+		kPickScrap1,
+		kPickScrap2,
+		kPickScrap3,
+		kNumPicks
+	};
+	const char *kPickLabel[kNumPicks] = {
+		"         Choose A Mystery",
+		"         Practice Mystery",
+		"         See ScrapBook 1",
+		"         See ScrapBook 2",
+		"         See ScrapBook 3"
+	};
+	// ScrapBooks aren't implemented yet ??? grey them so the player can't
+	// stop on them, mirroring the original `_Greys` mask.
+	const bool kPickEnabled[kNumPicks] = { true, true, false, false, false };
+	uint pick = kPickChoose;
+
+	const char *kSeparator = "----------------------------------";
+
+	// Click rectangles from the original `_DoChoose` @ 1c33:0514 ??? each
+	// `_InRect(_MouseX, _MouseY, addr, 0x29be)` reads one 4??u16 rect at
+	// the listed offset in segment 29be ({x1, y1, x2, y2}). We use
+	// `Common::Rect` (left/top/right/bottom) which also gives us
+	// `contains(x, y)` for hit testing.
+	const Common::Rect kOkRect      ( 12,  63,  41,  87); // 29be:0cd8 confirm
+	const Common::Rect kHelpRect    ( 12, 100,  41, 124); // 29be:0ce0 help
+	const Common::Rect kExitRect    ( 12, 137,  41, 161); // 29be:0ce8 cancel
+	const Common::Rect kUpArrowRect (240,  31, 250,  43); // 29be:0cf0 scroll up
+	const Common::Rect kDnArrowRect (240, 148, 250, 159); // 29be:0cf8 scroll dn
+	const Common::Rect kListRect    ( 58,  35, 238, 158); // 29be:0d00 list panel
+
+	// The original `_NewPlayer` set `_MouseCursor = 1` on exit; the
+	// chain of screens after it expects the cursor to stay visible.
+	// Reassert here in case anything between hid it.
+	CursorMan.showMouse(true);
 
 	// Mirrors `_CaseSelection`: load PIC 0x41 as the chooser backdrop.
 	Picture caseBg;
@@ -619,53 +656,215 @@ void EEMEngine::doCaseSelection() {
 			}
 		}
 		if (_font.isLoaded()) {
-			_font.drawString(&scratch, Common::String::format("EAGLE EYE - %s", _playerName.c_str()), 8, 4, 320, 0xF);
+			// `DrawList` @ 1c33:040d coordinates: `_TextBox + 3` for x
+			// and `DAT_29be_0d02` for y. `_TextBox` @ 29be:0d00 holds
+			// {x=58, y=35, x2=238, y2=158}. Matches the blue panel.
+			const int kListX  = 58 + 3;
+			const int kListW  = 238 - kListX;
+			const int kListY0 = 35;
+			const int kLineH  = 10;
+
+			// Top centred "Book %d" / "Challenge Book" title ??? sprintf
+			// format strings at 29be:0deb / 29be:0dfa shown via
+			// `_Show_String(0xc, (0xba - width)/2 + 0x3c, ???)` in the
+			// original. We don't track challenge tier yet so always
+			// show "Book 1".
+			const Common::String book = "Book 1";
+			const int titleW = _font.getStringWidth(book);
+			const int titleX = (0xba - titleW) / 2 + 0x3c;
+			_font.drawString(&scratch, book, titleX, 12, 320, 0xF);
+
+			// Render 11 list rows: separator + menu item pairs.
+			//   row 0  separator
+			//   row 1  Choose A Mystery
+			//   row 2  separator
+			//   row 3  Practice Mystery
+			//   ...
+			//   row 9  See ScrapBook 3
+			//   row 10 separator
+			for (int r = 0; r < 11; r++) {
+				const int y = kListY0 + r * kLineH;
+				if ((r & 1) == 0) {
+					_font.drawString(&scratch, kSeparator, kListX, y, kListW, 0x7);
+					continue;
+				}
+				const uint mp = (uint)(r >> 1);
+				const bool isSel  = (mp == pick);
+				const byte color  = isSel        ? 0xF :
+									kPickEnabled[mp] ? 0x7 : 0x8;
+				_font.drawString(&scratch, kPickLabel[mp], kListX, y, kListW, color);
+			}
+		}
+		g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
+								   0, 0, 320, 200);
+		g_system->updateScreen();
+	};
+
+	auto pickPrev = [&]() {
+		for (int i = 0; i < (int)kNumPicks; i++) {
+			pick = (pick == 0) ? (uint)(kNumPicks - 1) : pick - 1;
+			if (kPickEnabled[pick])
+				break;
+		}
+	};
+	auto pickNext = [&]() {
+		for (int i = 0; i < (int)kNumPicks; i++) {
+			pick = (pick + 1) % kNumPicks;
+			if (kPickEnabled[pick])
+				break;
+		}
+	};
+
+	draw();
 
-			// Solved count.
-			uint solved = 0, perfectSolved = 0;
-			for (uint i = 0; i < sizeof(_mysteriesSolved); i++) {
-				if (_mysteriesSolved[i] >= 1) solved++;
-				if (_mysteriesSolved[i] == 2) perfectSolved++;
+	bool exitChosen = false;
+	while (!shouldQuit()) {
+		Common::Event ev;
+		bool confirmed = false;
+		while (g_system->getEventManager()->pollEvent(ev)) {
+			if (ev.type == Common::EVENT_QUIT ||
+				ev.type == Common::EVENT_RETURN_TO_LAUNCHER)
+				return;
+			if (ev.type == Common::EVENT_LBUTTONDOWN) {
+				// OK / EXIT / HELP buttons (rectangles from `_DoChoose`).
+				if (kOkRect.contains(ev.mouse.x, ev.mouse.y)) {
+					confirmed = true;
+					break;
+				}
+				if (kExitRect.contains(ev.mouse.x, ev.mouse.y)) {
+					exitChosen = true;
+					confirmed = true;
+					break;
+				}
+				if (kHelpRect.contains(ev.mouse.x, ev.mouse.y)) {
+					// HELP placeholder ??? original calls `_DisplayHint`;
+					// our help screen is wired to `H` later in the flow.
+					continue;
+				}
+				// List panel: click on a non-separator row selects the
+				// menu entry under the cursor.
+				if (kListRect.contains(ev.mouse.x, ev.mouse.y)) {
+					const int kLineH = 10;
+					const int row = (ev.mouse.y - kListRect.top) / kLineH;
+					if ((row & 1) == 1) {
+						const uint mp = (uint)(row >> 1);
+						if (mp < kNumPicks && kPickEnabled[mp]) {
+							pick = mp;
+							draw();
+							continue;
+						}
+					}
+				}
 			}
-			_font.drawString(&scratch, Common::String::format("solved %u (1st try %u)",
-									   solved, perfectSolved), 200, 4, 320, 0xF);
-			if (perfectSolved >= 55) {
-				_font.drawString(&scratch, "** PERFECT MASTER SLEUTH! **", 8, 168, 320, 0xF);
-			} else if (solved >= 55) {
-				_font.drawString(&scratch, "** ALL MYSTERIES SOLVED! **", 8, 168, 320, 0xF);
+			if (ev.type != Common::EVENT_KEYDOWN)
+				continue;
+			const Common::KeyCode k = ev.kbd.keycode;
+			if (k == Common::KEYCODE_ESCAPE) {
+				exitChosen = true;
+				confirmed = true;
+				break;
+			}
+			if (k == Common::KEYCODE_RETURN) {
+				confirmed = true;
+				break;
+			}
+			if (k == Common::KEYCODE_UP || k == Common::KEYCODE_LEFT) {
+				pickPrev();
+				draw();
+				continue;
 			}
+			if (k == Common::KEYCODE_DOWN || k == Common::KEYCODE_RIGHT ||
+				k == Common::KEYCODE_TAB) {
+				pickNext();
+				draw();
+				continue;
+			}
+		}
+		if (confirmed) {
+			draw();
+			break;
+		}
+		g_system->delayMillis(15);
+	}
+
+	if (shouldQuit())
+		return;
 
-			char marker = ' ';
-			if (sel < sizeof(_mysteriesSolved)) {
-				if (_mysteriesSolved[sel] == 2) marker = '*';
-				else if (_mysteriesSolved[sel] == 1) marker = '+';
+	if (exitChosen) {
+		_mystery.clear();
+		_nextScreen = kScreenInvalid;
+		return;
+	}
+
+	// "Practice Mystery" is the tutorial ??? mystery 0.
+	if (pick == kPickPractice) {
+		if (!_mystery.load(0, &_rng)) {
+			warning("doCaseSelection: failed to load practice mystery");
+			_mystery.clear();
+		}
+		return;
+	}
+
+	if (pick != kPickChoose) {
+		// ScrapBooks aren't implemented; bail back to the menu loop.
+		_mystery.clear();
+		return;
+	}
+
+	// "Choose A Mystery" sub-screen: pick a specific case from the
+	// 55-mystery roster. The original opens a different list here;
+	// we approximate with the tier-aware numeric chooser we used
+	// before. Default to the first unsolved mystery.
+	uint sel = 0;
+	for (uint i = 0; i <= kMaxMystery; i++) {
+		if (i < sizeof(_mysteriesSolved) && !_mysteriesSolved[i]) {
+			sel = i;
+			break;
+		}
+	}
+
+	auto drawSubmenu = [&]() {
+		Graphics::ManagedSurface scratch(320, 200,
+			Graphics::PixelFormat::createFormatCLUT8());
+		scratch.clear();
+		if (haveCaseBg) {
+			const int w = MIN<int>(caseBg.surface.w, 320);
+			const int h = MIN<int>(caseBg.surface.h, 200);
+			for (int row = 0; row < h; row++)
+				memcpy((byte *)scratch.getBasePtr(0, row),
+					   (const byte *)caseBg.surface.getBasePtr(0, row), w);
+		}
+		if (_font.isLoaded()) {
+			const int kListX  = 61;
+			const int kListW  = 238 - kListX;
+			const int kListY0 = 35;
+			const int kLineH  = 10;
+			const int kVisible = 12;
+			int top = (int)sel - kVisible / 2;
+			if (top < 0) top = 0;
+			if (top + kVisible > (int)kMaxMystery + 1)
+				top = (int)kMaxMystery + 1 - kVisible;
+			for (int r = 0; r < kVisible; r++) {
+				const int idx = top + r;
+				if (idx > (int)kMaxMystery)
+					break;
+				char marker = ' ';
+				if ((uint)idx < sizeof(_mysteriesSolved)) {
+					if (_mysteriesSolved[idx] == 2) marker = '*';
+					else if (_mysteriesSolved[idx] == 1) marker = '+';
+				}
+				const char arrow = ((uint)idx == sel) ? '>' : ' ';
+				_font.drawString(&scratch,
+								 Common::String::format("%c %c Mystery %d", arrow, marker, idx),
+								 kListX, kListY0 + r * kLineH, kListW, 0xF);
 			}
-			// Per the original tiers: 0 (tutorial), 1-24 (Junior),
-			// 25-48 (Senior), 49-54 (Master).
-			const char *tier = "Tutorial";
-			if (sel >= 1 && sel <= 24) tier = "Junior Sleuth";
-			else if (sel >= 25 && sel <= 48) tier = "Senior Sleuth";
-			else if (sel >= 49 && sel <= 54) tier = "Master Sleuth";
-			_font.drawString(&scratch, Common::String::format("Mystery %u  %c  [%s]",
-									   sel, marker, tier), 8, 24, 320, 0xF);
-			_font.drawString(&scratch, "  0..9        quick select", 8, 40, 320, 0xF);
-			_font.drawString(&scratch, "  Tab / +     next mystery", 8, 52, 320, 0xF);
-			_font.drawString(&scratch, "  Shift+Tab   prev mystery", 8, 64, 320, 0xF);
-			_font.drawString(&scratch, "  PgUp/PgDn   jump 10", 8, 76, 320, 0xF);
-			_font.drawString(&scratch, "  Home/End    first/last", 8, 88, 320, 0xF);
-			_font.drawString(&scratch, "  Enter       start mystery", 8, 100, 320, 0xF);
-			_font.drawString(&scratch, "  F5          save / load (ScummVM)", 8, 112, 320, 0xF);
-			_font.drawString(&scratch, "  ESC         quit", 8, 124, 320, 0xF);
-			_font.drawString(&scratch, "  *  solved on first try", 8, 144, 320, 0xF);
-			_font.drawString(&scratch, "  +  solved", 8, 156, 320, 0xF);
 		}
 		g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
 								   0, 0, 320, 200);
 		g_system->updateScreen();
 	};
 
-	draw();
-
+	drawSubmenu();
 	bool confirmed = false;
 	while (!confirmed && !shouldQuit()) {
 		Common::Event ev;
@@ -673,12 +872,48 @@ void EEMEngine::doCaseSelection() {
 			if (ev.type == Common::EVENT_QUIT ||
 				ev.type == Common::EVENT_RETURN_TO_LAUNCHER)
 				return;
+			if (ev.type == Common::EVENT_LBUTTONDOWN) {
+				// Same `_DoChoose` rectangles as the top-level menu.
+				if (kOkRect.contains(ev.mouse.x, ev.mouse.y)) {
+					confirmed = true;
+					break;
+				}
+				if (kExitRect.contains(ev.mouse.x, ev.mouse.y)) {
+					_mystery.clear();
+					return;
+				}
+				if (kUpArrowRect.contains(ev.mouse.x, ev.mouse.y)) {
+					sel = (sel == 0) ? kMaxMystery : sel - 1;
+					drawSubmenu();
+					continue;
+				}
+				if (kDnArrowRect.contains(ev.mouse.x, ev.mouse.y)) {
+					sel = (sel >= kMaxMystery) ? 0 : sel + 1;
+					drawSubmenu();
+					continue;
+				}
+				if (kListRect.contains(ev.mouse.x, ev.mouse.y)) {
+					// Pick the row under the cursor.
+					const int kLineH = 10;
+					const int kVisible = 12;
+					int top = (int)sel - kVisible / 2;
+					if (top < 0) top = 0;
+					if (top + kVisible > (int)kMaxMystery + 1)
+						top = (int)kMaxMystery + 1 - kVisible;
+					const int row = (ev.mouse.y - kListRect.top) / kLineH;
+					const int idx = top + row;
+					if (idx >= 0 && idx <= (int)kMaxMystery) {
+						sel = (uint)idx;
+						drawSubmenu();
+					}
+					continue;
+				}
+			}
 			if (ev.type != Common::EVENT_KEYDOWN)
 				continue;
 			const Common::KeyCode k = ev.kbd.keycode;
 			if (k == Common::KEYCODE_ESCAPE) {
-                _mystery.clear();
-				_nextScreen = kScreenInvalid;
+				_mystery.clear();
 				return;
 			}
 			if (k == Common::KEYCODE_RETURN) {
@@ -687,46 +922,31 @@ void EEMEngine::doCaseSelection() {
 			}
 			if (k >= Common::KEYCODE_0 && k <= Common::KEYCODE_9) {
 				sel = (uint)(k - Common::KEYCODE_0);
-				draw();
+				drawSubmenu();
 				continue;
 			}
-			if (k == Common::KEYCODE_TAB || k == Common::KEYCODE_PLUS ||
-				k == Common::KEYCODE_RIGHT || k == Common::KEYCODE_DOWN) {
-				const bool back = (ev.kbd.flags & Common::KBD_SHIFT) ||
-								  k == Common::KEYCODE_LEFT;
-				if (back)
-					sel = (sel == 0) ? kMaxMystery : sel - 1;
-				else
-					sel = (sel >= kMaxMystery) ? 0 : sel + 1;
-				draw();
+			if (k == Common::KEYCODE_DOWN || k == Common::KEYCODE_TAB) {
+				sel = (sel >= kMaxMystery) ? 0 : sel + 1;
+				drawSubmenu();
 				continue;
 			}
-			if (k == Common::KEYCODE_LEFT || k == Common::KEYCODE_UP ||
-				k == Common::KEYCODE_MINUS) {
+			if (k == Common::KEYCODE_UP) {
 				sel = (sel == 0) ? kMaxMystery : sel - 1;
-				draw();
+				drawSubmenu();
 				continue;
 			}
 			if (k == Common::KEYCODE_PAGEDOWN) {
 				sel = (sel + 10 > kMaxMystery) ? kMaxMystery : sel + 10;
-				draw();
+				drawSubmenu();
 				continue;
 			}
 			if (k == Common::KEYCODE_PAGEUP) {
 				sel = (sel < 10) ? 0 : sel - 10;
-				draw();
-				continue;
-			}
-			if (k == Common::KEYCODE_HOME) {
-				sel = 0;
-				draw();
-				continue;
-			}
-			if (k == Common::KEYCODE_END) {
-				sel = kMaxMystery;
-				draw();
+				drawSubmenu();
 				continue;
 			}
+			if (k == Common::KEYCODE_HOME) { sel = 0; drawSubmenu(); continue; }
+			if (k == Common::KEYCODE_END)  { sel = kMaxMystery; drawSubmenu(); continue; }
 		}
 		g_system->delayMillis(15);
 	}


Commit: 17288a601407fe4b999357d3659adfdbd701af00
    https://github.com/scummvm/scummvm/commit/17288a601407fe4b999357d3659adfdbd701af00
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:20:35+02:00

Commit Message:
EEM: update pointer during main menu

Changed paths:
    engines/eem/eem.cpp


diff --git a/engines/eem/eem.cpp b/engines/eem/eem.cpp
index c14c56b4f10..c6e96ccd8ca 100644
--- a/engines/eem/eem.cpp
+++ b/engines/eem/eem.cpp
@@ -500,6 +500,7 @@ void EEMEngine::doNewPlayer() {
 		}
 		if (dirty)
 			draw();
+		g_system->updateScreen();
 		g_system->delayMillis(15);
 	}
 }
@@ -784,6 +785,7 @@ void EEMEngine::doCaseSelection() {
 			draw();
 			break;
 		}
+		g_system->updateScreen();
 		g_system->delayMillis(15);
 	}
 
@@ -948,6 +950,7 @@ void EEMEngine::doCaseSelection() {
 			if (k == Common::KEYCODE_HOME) { sel = 0; drawSubmenu(); continue; }
 			if (k == Common::KEYCODE_END)  { sel = kMaxMystery; drawSubmenu(); continue; }
 		}
+		g_system->updateScreen();
 		g_system->delayMillis(15);
 	}
 
@@ -1184,11 +1187,21 @@ void EEMEngine::displayClue(const byte *clueBlock) {
 			if (haveBalloon) {
 				const int bw = MIN<int>(balloon.surface.w, 320 - bubX);
 				const int bh = MIN<int>(balloon.surface.h, 200 - bubY);
+				// `_AddPicBackground` passes `pic->miscflags >> 8` as
+				// the transparent colour to `_Rect_Move_Mask`. Without
+				// that mask, the balloon's tail/corner padding bleeds
+				// into the surrounding scene. The on-disk u16 at file
+				// offset 0 maps to our `Picture::flags` field.
+				const byte transp = (byte)(balloon.flags >> 8);
 				if (bw > 0 && bh > 0) {
 					for (int row = 0; row < bh; row++) {
-						memcpy((byte *)scratch.getBasePtr(bubX, bubY + row),
-							   (const byte *)balloon.surface.getBasePtr(0, row),
-							   bw);
+						const byte *src =
+							(const byte *)balloon.surface.getBasePtr(0, row);
+						byte *dst = (byte *)scratch.getBasePtr(bubX, bubY + row);
+						for (int col = 0; col < bw; col++) {
+							if (src[col] != transp)
+								dst[col] = src[col];
+						}
 					}
 				}
 				// Per-balloon metadata table at 29be:0875 in the original
@@ -1240,6 +1253,10 @@ void EEMEngine::displayClue(const byte *clueBlock) {
 						break;
 					}
 				}
+				// Tick the screen so the OSystem cursor follows the
+				// mouse ??? ScummVM redraws the cursor overlay only on
+				// updateScreen.
+				g_system->updateScreen();
 				g_system->delayMillis(10);
 			}
 			if (skipAll) {
@@ -1351,6 +1368,7 @@ void EEMEngine::doNotebook() {
 		}
 		if (exit) break;
 		if (dirty) draw();
+		g_system->updateScreen();
 		g_system->delayMillis(15);
 	}
 }
@@ -1634,6 +1652,7 @@ void EEMEngine::doBigMap() {
 		}
 		if (dirty)
 			draw();
+		g_system->updateScreen();
 		g_system->delayMillis(10);
 	}
 }
@@ -1732,6 +1751,7 @@ bool EEMEngine::areYouSure() {
 		}
 		if (decided)
 			break;
+		g_system->updateScreen();
 		g_system->delayMillis(15);
 	}
 
@@ -1830,6 +1850,7 @@ void EEMEngine::doAccuse() {
 					picked = slot;
 			}
 		}
+		g_system->updateScreen();
 		g_system->delayMillis(10);
 	}
 	if (picked < 0)
@@ -1970,6 +1991,7 @@ void EEMEngine::doAccuse() {
 					break;
 				}
 			}
+			g_system->updateScreen();
 			g_system->delayMillis(15);
 		}
 		if (exit) break;


Commit: d3e517a57579c9b310cfd8b62eb09c3e85d4a8d8
    https://github.com/scummvm/scummvm/commit/d3e517a57579c9b310cfd8b62eb09c3e85d4a8d8
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:20:35+02:00

Commit Message:
EEM: correct ballon dialog placement

Changed paths:
    engines/eem/eem.cpp


diff --git a/engines/eem/eem.cpp b/engines/eem/eem.cpp
index c6e96ccd8ca..b46d1a1842c 100644
--- a/engines/eem/eem.cpp
+++ b/engines/eem/eem.cpp
@@ -1019,26 +1019,56 @@ void EEMEngine::doSiteLoop() {
 /// hyphenation marks and a hint placeholder (0x89) we ignore for now.
 static Common::String parseString(const Common::String &raw,
 								  const Common::String &playerName,
-								  const Common::String &partnerName) {
+								  uint partner) {
+	// Substitution opcodes from `_ParseString` @ 1b66:07c3, jump-table
+	// at 1b66:0cbe. Each handler reads `_Partner` (16-bit at 0x7918)
+	// and indexes the name table at 29be:0c28 ({Jake, Jennifer, he,
+	// she, him, her, his} as far pointers).
+	//   0x80 ??? player's typed name (auto-cap word starts) ??? uses _PlayerRecord
+	//   0x81 ??? _Partner == 0 ? "Jake"     : "Jennifer"  (chosen detective)
+	//   0x82 ??? _Partner == 0 ? "Jennifer" : "Jake"      (the OTHER one)
+	//   0x83 ??? _Partner == 0 ? "he"       : "she"
+	//   0x84 ??? _Partner == 0 ? "him"      : "her"
+	//   0x85 ??? _Partner == 0 ? "his"      : "her"
+	//   0x86..0x88 read a different gender flag at 0x7985 ??? left alone
+	//     until that flag's source is traced.
+	//   0x89 ??? KD hint placeholder (handled by caller).
+	const bool isJake = (partner == 0);
 	Common::String out;
 	for (uint i = 0; i < raw.size(); i++) {
 		const byte c = (byte)raw[i];
-		if (c == 0x80) {
+		switch (c) {
+		case 0x80:
 			out += playerName;
-		} else if (c == 0x81 || c == 0x82) {
-			// Both forms substitute the partner's name. The original
-			// likely has a casual ("Jake/Jenny") and a formal
-			// ("Jake/Jennifer") variant; we render the same partner
-			// name in both spots ??? close enough for natural reading.
-			out += partnerName;
-		} else if (c >= 0x80 && c < 0x8A) {
-			// Other control opcodes: eat them silently for now.
-		} else if (c == 0 || c == '\r') {
-			// stop on NUL, ignore CR
-			if (c == 0)
-				break;
-		} else {
+			break;
+		case 0x81:
+			out += isJake ? "Jake" : "Jennifer";
+			break;
+		case 0x82:
+			out += isJake ? "Jennifer" : "Jake";
+			break;
+		case 0x83:
+			out += isJake ? "he" : "she";
+			break;
+		case 0x84:
+			out += isJake ? "him" : "her";
+			break;
+		case 0x85:
+			out += isJake ? "his" : "her";
+			break;
+		case 0x86:
+		case 0x87:
+		case 0x88:
+		case 0x89:
+			// Eaten silently ??? see comment above.
+			break;
+		case 0:
+			return out;
+		case '\r':
+			break;
+		default:
 			out += (char)c;
+			break;
 		}
 	}
 	return out;
@@ -1147,11 +1177,10 @@ void EEMEngine::displayClue(const byte *clueBlock) {
 			}
 		}
 
-		// Substitute placeholder control bytes with the entered player
-		// name and the chosen partner's first name (Jake / Jennifer).
-		const Common::String partnerName = (_partner == 0) ? "Jake" : "Jennifer";
+		// Substitute control bytes (0x80..0x89) ??? see `parseString` for
+		// the table. 0x81 = chosen detective, 0x82 = the other one.
 		const Common::String text = parseString(raw ? raw : "",
-												_playerName, partnerName);
+												_playerName, _partner);
 
 		// Speech balloon. Mirrors `_GetBalloon` + `_AddPicBackground` in
 		// `_DisplayClue`. The original looks up per-balloon text-area
@@ -1188,25 +1217,33 @@ void EEMEngine::displayClue(const byte *clueBlock) {
 				const int bw = MIN<int>(balloon.surface.w, 320 - bubX);
 				const int bh = MIN<int>(balloon.surface.h, 200 - bubY);
 				// `_AddPicBackground` passes `pic->miscflags >> 8` as
-				// the transparent colour to `_Rect_Move_Mask`. Without
-				// that mask, the balloon's tail/corner padding bleeds
-				// into the surrounding scene. The on-disk u16 at file
-				// offset 0 maps to our `Picture::flags` field.
+				// the transparent colour to `_Rect_Move_Mask`. The
+				// on-disk u16 at file offset 0 maps to `Picture::flags`.
 				const byte transp = (byte)(balloon.flags >> 8);
+				// `_GetBalloon @ 172b:1d7d` mirrors the picture horizontally
+				// when `(bubNum & 0x80)` is set ??? used for right-side
+				// speakers so the tail points the other way.
+				const bool flipBalloon = (bubNum & 0x80) != 0;
 				if (bw > 0 && bh > 0) {
 					for (int row = 0; row < bh; row++) {
 						const byte *src =
 							(const byte *)balloon.surface.getBasePtr(0, row);
 						byte *dst = (byte *)scratch.getBasePtr(bubX, bubY + row);
 						for (int col = 0; col < bw; col++) {
-							if (src[col] != transp)
-								dst[col] = src[col];
+							const int srcCol = flipBalloon
+								? (balloon.surface.w - 1 - col)
+								: col;
+							const byte px = src[srcCol];
+							if (px != transp)
+								dst[col] = px;
 						}
 					}
 				}
-				// Per-balloon metadata table at 29be:0875 in the original
-				// uses (textX=6, textY=4) inset across all entries; we
-				// adopt the same constants instead of approximating with 8.
+				// Per-balloon metadata table at 29be:0875 ??? 10-byte
+				// entries indexed by `(bubNum & 0x7f)`. Layout:
+				//   +0..1 textX inset, +2..3 textY inset, +4..5 textWidth.
+				// All entries use textX=6, textY=4 so we hard-code those
+				// constants; textWidth is read live from the table.
 				textX = bubX + 6;
 				textY = bubY + 4;
 				textW = bw - 12;
@@ -1219,8 +1256,11 @@ void EEMEngine::displayClue(const byte *clueBlock) {
 				copyY = bubY;
 			}
 
+			// `_DisplayClue` @ 2404:07fe passes fontColor=0 (palette
+			// index 0 of the case-briefing palette 0x22) to `_WordWrap`.
+			// Hard-coding 0xF here gave the wrong colour.
 			_font.drawWordWrapped(&scratch, textX, textY,
-				MAX<int>(8, textW), text, 0xF);
+				MAX<int>(8, textW), text, 0);
 
 			g_system->copyRectToScreen(scratch.getBasePtr(0, copyY),
 				scratch.pitch, 0, copyY, 320,
@@ -1304,7 +1344,6 @@ void EEMEngine::doNotebook() {
 
 		const byte *ni = _mystery.noteIndex();
 		const uint16 niCount = _mystery.noteIndexCount();
-		const Common::String partnerName = (_partner == 0) ? "Jake" : "Jennifer";
 		int y = 4 + _font.getFontHeight() * 2 + 4;
 		for (int slot = 0; slot < kPerPage; slot++) {
 			const int idx = page * kPerPage + slot;
@@ -1318,7 +1357,7 @@ void EEMEngine::doNotebook() {
 				const uint16 ptsRaw  = READ_LE_UINT16(ni + clueId * 4 + 2);
 				pts = (int)(int16)ptsRaw;
 				const Common::String raw = _mystery.textAt(textOff);
-				text = parseString(raw, _playerName, partnerName);
+				text = parseString(raw, _playerName, _partner);
 			}
 			if (text.empty())
 				text = Common::String::format("clue %u", clueId);
@@ -1680,8 +1719,7 @@ void EEMEngine::doHelp() {
 		_mystery._sawHelpHint = true;
 
 	const Common::String raw = _mystery.textAt(use);
-	const Common::String partnerName = (_partner == 0) ? "Jake" : "Jennifer";
-	const Common::String text = parseString(raw, _playerName, partnerName);
+	const Common::String text = parseString(raw, _playerName, _partner);
 
 	Graphics::ManagedSurface scratch(320, 200,
 		Graphics::PixelFormat::createFormatCLUT8());
@@ -1908,7 +1946,6 @@ void EEMEngine::doAccuse() {
 	const byte *e = blob.data();
 	const uint16 pages = READ_LE_UINT16(e);
 
-	const Common::String partnerName = (_partner == 0) ? "Jake" : "Jennifer";
 	uint pageIdx = 0;
 
 	while (!shouldQuit()) {
@@ -1929,7 +1966,7 @@ void EEMEngine::doAccuse() {
 		const uint16 x2     = READ_LE_UINT16(e + pos + 6);
 		const uint16 y2     = READ_LE_UINT16(e + pos + 8);
 		const char *raw     = (const char *)(e + pos + 10);
-		const Common::String txt = parseString(raw, _playerName, partnerName);
+		const Common::String txt = parseString(raw, _playerName, _partner);
 
 		Graphics::ManagedSurface scratch(320, 200,
 			Graphics::PixelFormat::createFormatCLUT8());


Commit: b60f6c5570c0ab4ccb94ef18b806617db515d7fa
    https://github.com/scummvm/scummvm/commit/b60f6c5570c0ab4ccb94ef18b806617db515d7fa
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:20:35+02:00

Commit Message:
EEM: draw the big map and allow player to move between locations

Changed paths:
    engines/eem/eem.cpp
    engines/eem/site.cpp


diff --git a/engines/eem/eem.cpp b/engines/eem/eem.cpp
index b46d1a1842c..f906aec7d69 100644
--- a/engines/eem/eem.cpp
+++ b/engines/eem/eem.cpp
@@ -147,12 +147,19 @@ Common::Error EEMEngine::run() {
 				   wantedSave, _mystery.number());
 			CursorMan.showMouse(true);
 			doInitClues();
-			doSiteLoop();
+			// Original screen 0 ??? screen 1: after the briefing the
+			// game opens the map (function at 20fe:120b ??? _DoBigMap)
+			// and only enters a site once the player clicks on one.
+			doBigMap();
+			if (_mystery.isLoaded())
+				doSiteLoop();
 			while (!shouldQuit()) {
 				doCaseSelection();
 				if (!_mystery.isLoaded()) break;
 				doInitClues();
-				doSiteLoop();
+				doBigMap();
+				if (_mystery.isLoaded())
+					doSiteLoop();
 			}
 			return Common::kNoError;
 		}
@@ -199,7 +206,12 @@ Common::Error EEMEngine::run() {
 		// Mark the starting site as active and display the case briefing.
 		// `_DoInitClues` @ 1a35:0411 ??? case briefing.
 		doInitClues();
-		doSiteLoop();
+		// Original screen 0 ??? screen 1: after the briefing the game
+		// opens the map (function at 20fe:120b ??? `_DoBigMap`) and only
+		// enters a site once the player clicks on one.
+		doBigMap();
+		if (_mystery.isLoaded())
+			doSiteLoop();
 
 		// After a case, loop back to CaseSelection.
 		while (!shouldQuit()) {
@@ -207,7 +219,12 @@ Common::Error EEMEngine::run() {
 			if (!_mystery.isLoaded())
 				break;
 			doInitClues();
-			doSiteLoop();
+			// Original screen 0 ??? screen 1: after the briefing the
+			// game opens the map (function at 20fe:120b ??? _DoBigMap)
+			// and only enters a site once the player clicks on one.
+			doBigMap();
+			if (_mystery.isLoaded())
+				doSiteLoop();
 		}
 	}
 
@@ -978,6 +995,13 @@ void EEMEngine::doInitClues() {
 	const uint16 startSite = READ_LE_UINT16(ib + 2);
 	if (startSite < Mystery::kVisitedSiteCap)
 		_mystery._onSites[startSite] = 1;
+	// Mirror the original: at briefing time the player isn't actually
+	// at any site yet ??? they pick from the map next. Set _siteNumber
+	// to the start site so the map opens centred on the only initially
+	// accessible location and the post-map site loop has a sensible
+	// resume point.
+	_mystery._siteNumber = startSite;
+	_mystery._lastSite = startSite;
 
 	setSitePalette(0x22);
 	Picture bg;
@@ -1483,12 +1507,21 @@ void EEMEngine::doGallery() {
 }
 
 void EEMEngine::doBigMap() {
-	// Mirrors `_DoBigMap` @ 20fe:09e7. The original lays out a 0xe9 x 0xab
-	// map window inside frame PIC 0x42 with per-site clickable markers
-	// scrolling under it. We render BIGMAP.PIC at top-left, draw an
-	// overlay listing the sites that the player can travel to (per
-	// `_OnSites`), and accept either number keys or clicks on the
-	// overlay rows to travel.
+	// Mirrors the small/scrollable map view used inside the site loop ???
+	// the original game draws a 0xe9 ?? 0xab viewport at (2, 2) into
+	// `_MapBitMap` (= BIGMAP.PIC) via `DrawMap @ 20fe:1058`; on top of
+	// that, `_StampButtons @ 20fe:0d2f` bakes site icons into the
+	// bitmap at MapData offsets (+4, +6). We render BIGMAP.PIC into a
+	// 233??171 viewport and overlay markers ourselves.
+	//
+	// MapData entry layout (14 bytes), confirmed from
+	// `_DrawBigMapButtons @ 20fe:0877` and `_StampButtons @ 20fe:0d2f`:
+	//   +0..1  BigMap X (overview frame PIC 0x42)
+	//   +2..3  BigMap Y
+	//   +4..5  SmallMap X (stamped into _MapBitMap at this position)
+	//   +6..7  SmallMap Y
+	//   +8..9  crime-flag (DAT_2d5d_5436): non-zero ??? crime-scene marker
+	//   +10..13 unused
 	Common::File f;
 	if (!f.open(Common::Path("BIGMAP.PIC"))) {
 		warning("doBigMap: BIGMAP.PIC missing");
@@ -1505,11 +1538,8 @@ void EEMEngine::doBigMap() {
 		return;
 	}
 
-	// Approximate inner map window from the original `_DoBigMap`:
-	//   if (sx < 0x75) sx = 0; else sx -= 0x74;     // 0x74 = 116
-	//   if (mapW < sx + 0xe9) sx = mapW - 0xe9;     // window width  = 233
-	//   if (sy < 0x56) sy = 0; else sy -= 0x55;
-	//   if (mapH < sy + 0xab) sy = mapH - 0xab;     // window height = 171
+	// Viewport size from `DrawMap`: 0xe9 ?? 0xab at screen (2, 2).
+	// We use (4, 4) so a 1-pixel inset preserves the frame border.
 	const int kMapWinW = 0xe9; // 233
 	const int kMapWinH = 0xab; // 171
 	const int kMapWinX = 4;
@@ -1518,7 +1548,7 @@ void EEMEngine::doBigMap() {
 	int scrollX = 0;
 	int scrollY = 0;
 
-	// Auto-scroll to centre the current site, if known.
+	// Auto-scroll to centre the current site (using SmallMap coords).
 	if (_mystery.isLoaded()) {
 		const byte *entry = _mystery.mapEntry(_mystery._siteNumber);
 		if (entry) {
@@ -1531,6 +1561,14 @@ void EEMEngine::doBigMap() {
 		}
 	}
 
+	CursorMan.showMouse(true);
+
+	// `_DoBigMap @ 20fe:09e7` calls `_GetPalette(0x24)` after loading
+	// the frame. Without this, the map would render under whatever
+	// palette the prior site set, which makes BIGMAP.PIC look like
+	// noise / incomplete colours.
+	setSitePalette(0x24);
+
 	auto draw = [&]() {
 		Graphics::ManagedSurface scratch(320, 200,
 			Graphics::PixelFormat::createFormatCLUT8());
@@ -1556,9 +1594,12 @@ void EEMEngine::doBigMap() {
 				   copyW);
 		}
 
-		// Site markers from MapData. Each per-mystery MapData entry is
-		// 14 bytes; bytes +4..+5 / +6..+7 hold an (x, y) pair on the big
-		// map. We draw a small filled square for each accessible site.
+		// Site markers ??? three states from `_DrawBigMapButtons`:
+		//   _DoneMarker if `SaveSiteComplete[i]` (already searched)
+		//   _CrimeMarker if MapData[+8] != 0 (crime scene)
+		//   _SiteMarker otherwise
+		// We don't have the marker PICs traced yet, so draw small filled
+		// squares with three colours that match the original semantics.
 		if (_mystery.isLoaded()) {
 			for (uint i = 0; i < _mystery.numSites(); i++) {
 				if (!_mystery._onSites[i] && i != _mystery._siteNumber)
@@ -1568,37 +1609,26 @@ void EEMEngine::doBigMap() {
 					continue;
 				const uint16 mx = READ_LE_UINT16(entry + 4);
 				const uint16 my = READ_LE_UINT16(entry + 6);
+				const uint16 crime = READ_LE_UINT16(entry + 8);
 				const int sx = (int)mx - scrollX + kMapWinX;
 				const int sy = (int)my - scrollY + kMapWinY;
 				if (sx < kMapWinX || sx >= kMapWinX + kMapWinW ||
 					sy < kMapWinY || sy >= kMapWinY + kMapWinH)
 					continue;
-				const byte color = (i == _mystery._siteNumber) ? 0x0E : 0x0F;
-				const Common::Rect mark(sx - 2, sy - 2, sx + 3, sy + 3);
-				scratch.fillRect(mark, color);
-				if (_font.isLoaded()) {
-					Common::String num = Common::String::format("%u", i);
-					_font.drawString(&scratch, num, sx + 4, sy - 4, 320, color);
-				}
-			}
-		}
 
-		// Travel-target overlay (right-side panel).
-		if (_font.isLoaded() && _mystery.isLoaded()) {
-			const int panelX = kMapWinX + kMapWinW + 4;
-			int y = 4;
-			_font.drawString(&scratch, "TRAVEL", panelX, y, 320, 0xF);
-			y += _font.getFontHeight() + 4;
-			for (uint i = 0; i < _mystery.numSites() && y < 192; i++) {
-				if (!_mystery._onSites[i] && i != _mystery._siteNumber)
-					continue;
-				const char marker = (i == _mystery._siteNumber) ? '>' : ' ';
-				Common::String label = Common::String::format(
-					"%c %u", marker, i);
-				_font.drawString(&scratch, label, panelX, y, 320, 0x0F);
-				y += _font.getFontHeight() + 1;
+				byte color;
+				if (i < Mystery::kVisitedSiteCap && _mystery._visitedSite[i])
+					color = 0x07;       // searched (DoneMarker analogue)
+				else if (crime != 0)
+					color = 0x0C;       // crime scene (CrimeMarker analogue)
+				else
+					color = 0x0F;       // available (SiteMarker analogue)
+				if (i == _mystery._siteNumber)
+					color = 0x0E;       // current site ??? bright yellow
+
+				const Common::Rect mark(sx - 3, sy - 3, sx + 4, sy + 4);
+				scratch.fillRect(mark, color);
 			}
-			_font.drawString(&scratch, "Esc", panelX, 188, 320, 0x0F);
 		}
 
 		g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
@@ -1669,24 +1699,6 @@ void EEMEngine::doBigMap() {
 					}
 				}
 
-				// Click in the right panel: travel to that row.
-				const int panelX = kMapWinX + kMapWinW + 4;
-				if (_font.isLoaded() && _mystery.isLoaded() &&
-					ev.mouse.x >= panelX) {
-					const int row = (ev.mouse.y - 4 - _font.getFontHeight() - 4) /
-									(_font.getFontHeight() + 1);
-					int seen = 0;
-					for (uint i = 0; i < _mystery.numSites(); i++) {
-						if (!_mystery._onSites[i] && i != _mystery._siteNumber)
-							continue;
-						if (seen == row) {
-							_mystery._lastSite = _mystery._siteNumber;
-							_mystery._siteNumber = (uint16)i;
-							return;
-						}
-						seen++;
-					}
-				}
 			}
 		}
 		if (dirty)
diff --git a/engines/eem/site.cpp b/engines/eem/site.cpp
index 319f31b3f68..8fa94de5ae5 100644
--- a/engines/eem/site.cpp
+++ b/engines/eem/site.cpp
@@ -64,7 +64,14 @@ void SiteScreen::run() {
 	if (!_mystery || !_mystery->isLoaded())
 		return;
 
-	uint cur = 0;
+	// The caller (run() in eem.cpp) is responsible for bringing the
+	// player into a site via the map first, so `_siteNumber` is
+	// already set to the destination they picked. Resuming a save
+	// also restores `_siteNumber`. Start there instead of forcing
+	// site 0 each time.
+	uint cur = _mystery->_siteNumber;
+	if (cur >= _mystery->numSites())
+		cur = 0;
 	enter(cur);
 
 	while (!_vm->shouldQuit()) {
@@ -188,19 +195,26 @@ void SiteScreen::run() {
 }
 
 void SiteScreen::renderBackground(uint siteNum) {
-	// Mirrors `_BuildBackground` @ 172b:13e2 (simplified):
-	//   1. Load PIC 0x3d (the site frame) from PICS.DBD.
-	//   2. Load entry @p siteNum from SITES.DBD (the site scene).
-	//   3. Composite scene into the frame at the position carried in the
-	//      SiteData fields.
-	//   4. Set palette to (siteNum + 1) ??? per-site palettes start at 1.
-	// We render frame + scene at (0,0); the original positions the scene
-	// at (x,y) read from the SiteData but we don't have the offsets fully
-	// decoded yet so a top-left placement will do.
+	// Site loop entry `screen 1` calls into a function at 20fe:120b in
+	// the original; that function opens `_GetPicture(0x43)` (PIC 0x43,
+	// 1-based) as the screen frame and `_GetPalette(0x23)` as the base
+	// palette. The case-briefing's `_BuildBackground @ 172b:13e2`
+	// instead uses entry 0x3d directly and palette `sitenum + 1` ??? but
+	// that path is only used by `_DisplayCorrect` (winner scene), not
+	// by the regular per-site renderer.
+	//
+	// We follow the regular site loop here:
+	//   1. PIC 0x43 frame at (0, 0).
+	//   2. SITES.DBD entry indexed by SiteData[+0] (1-based sitepic) at
+	//      (x, y) from the original `_Rect_Move` composition.
+	//   3. Per-site palette (`sitenum + 1`) ??? set by the caller in
+	//      SiteScreen::enter via `setSitePaletteForSite`.
 
-	// Frame.
 	Picture frame;
-	if (_vm->getPics().getPicture(0x3d, frame)) {
+	bool haveFrame = _vm->getPics().getPicture(0x43, frame);
+	if (!haveFrame)
+		haveFrame = _vm->getPics().getPicture(0x3d, frame);
+	if (haveFrame) {
 		g_system->copyRectToScreen(frame.surface.getPixels(),
 								   frame.surface.pitch,
 								   0, 0, frame.surface.w, frame.surface.h);


Commit: 3fe1b2e115c9d504281bb706e8b6fc2daecf3261
    https://github.com/scummvm/scummvm/commit/3fe1b2e115c9d504281bb706e8b6fc2daecf3261
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:20:36+02:00

Commit Message:
EEM: improved character animations when arriving to locations

Changed paths:
    engines/eem/eem.h
    engines/eem/site.cpp
    engines/eem/site.h


diff --git a/engines/eem/eem.h b/engines/eem/eem.h
index ed75d78e5ec..24bddf6f644 100644
--- a/engines/eem/eem.h
+++ b/engines/eem/eem.h
@@ -74,6 +74,7 @@ public:
 	DBDArchive &getBalloons(){ return _balloonArchive; }
 	Mystery    &getMystery() { return _mystery; }
 	const EEMFont &getFont() const { return _font; }
+	uint8       getPartnerIndex() const { return _partner; }
 
 	/// Display one ClueBlock. @p clueBlock points at the u16 frame count
 	/// followed by 62-byte ClueEntries. Mirrors _DisplayClue @ 2404:05e6.
diff --git a/engines/eem/site.cpp b/engines/eem/site.cpp
index 8fa94de5ae5..2ee29ea63ab 100644
--- a/engines/eem/site.cpp
+++ b/engines/eem/site.cpp
@@ -56,6 +56,23 @@ void SiteScreen::enter(uint siteNum) {
 	_vm->setSitePaletteForSite(sitepic);
 
 	renderBackground(siteNum);
+
+	// `_DoSiteLoop @ 168d:03f4` plays `_EnterSiteAnim` whenever
+	// `_LastSite != _SiteNumber`. We track the last site we *played*
+	// the arrival on so re-entries (after notebook/map/etc.) don't
+	// repeat the animation.
+	if ((int)siteNum != _lastSiteAnim) {
+		enterSiteAnim();
+		_lastSiteAnim = (int)siteNum;
+		// Re-paint the BG; the arrival animation drew on top of it.
+		renderBackground(siteNum);
+	}
+
+	// Persistent partner sprite (`_NewAnimation` at the tail of
+	// `_DoSiteLoop`). Drawn after the BG so the hotspot outlines and
+	// HUD that follow stay on top of it.
+	renderPartner(siteNum);
+
 	renderHotspots(siteNum);
 	g_system->updateScreen();
 }
@@ -194,56 +211,245 @@ void SiteScreen::run() {
 	}
 }
 
-void SiteScreen::renderBackground(uint siteNum) {
-	// Site loop entry `screen 1` calls into a function at 20fe:120b in
-	// the original; that function opens `_GetPicture(0x43)` (PIC 0x43,
-	// 1-based) as the screen frame and `_GetPalette(0x23)` as the base
-	// palette. The case-briefing's `_BuildBackground @ 172b:13e2`
-	// instead uses entry 0x3d directly and palette `sitenum + 1` ??? but
-	// that path is only used by `_DisplayCorrect` (winner scene), not
-	// by the regular per-site renderer.
-	//
-	// We follow the regular site loop here:
-	//   1. PIC 0x43 frame at (0, 0).
-	//   2. SITES.DBD entry indexed by SiteData[+0] (1-based sitepic) at
-	//      (x, y) from the original `_Rect_Move` composition.
-	//   3. Per-site palette (`sitenum + 1`) ??? set by the caller in
-	//      SiteScreen::enter via `setSitePaletteForSite`.
+void SiteScreen::enterSiteAnim() {
+	// Mirrors `_EnterSiteAnim @ 1000:9b21`. Two phases, both partner
+	// dependent:
+	//   Phase 1 ??? skateboard scroll: anim 6 (Jake) / 0xe (Jenny). Sprite
+	//             starts at (320 - sprite_w, 199 - sprite_h) and slides
+	//             left until off-screen.
+	//   Phase 2 ??? KD slide-in: anim 7 (Jake) / 0xf (Jenny). Sprite enters
+	//             from x = -sprite_w at y = 0x8b (Jake) / 0x8e (Jenny)
+	//             and slides until x = 0.
+	// Original cycles frames every `_MoveSkateBoardPixels` worth of
+	// motion (a runtime-calibrated speed value); we use a fixed 4 px
+	// per tick which feels close to the DOS pacing.
+	if (!_vm || !_mystery)
+		return;
+	const uint8 partner = _vm->getPartnerIndex();
+	const uint kSkateAni = (partner == 0) ? 6  : 0xe;
+	const uint kKDAni    = (partner == 0) ? 7  : 0xf;
+	const int  kKDY      = (partner == 0) ? 0x8b : 0x8e;
+
+	// Snapshot the current screen so we can restore between frames.
+	Graphics::Surface *screen = g_system->lockScreen();
+	if (!screen)
+		return;
+	Graphics::ManagedSurface bg(320, 200,
+		Graphics::PixelFormat::createFormatCLUT8());
+	for (int row = 0; row < 200; row++) {
+		memcpy((byte *)bg.getBasePtr(0, row),
+			   (const byte *)screen->getBasePtr(0, row), 320);
+	}
+	g_system->unlockScreen();
+
+	auto blitFrame = [](Graphics::ManagedSurface &dst, const Picture &p,
+						int x, int y, byte transp) {
+		const int w = p.surface.w, h = p.surface.h;
+		for (int row = 0; row < h; row++) {
+			const int dstY = y + row;
+			if (dstY < 0 || dstY >= 200)
+				continue;
+			const byte *src = (const byte *)p.surface.getBasePtr(0, row);
+			byte *out = (byte *)dst.getBasePtr(0, dstY);
+			for (int col = 0; col < w; col++) {
+				const int dstX = x + col;
+				if (dstX < 0 || dstX >= 320)
+					continue;
+				if (src[col] != transp)
+					out[dstX] = src[col];
+			}
+		}
+	};
+
+	// Phase 1 ??? skateboard scroll. `_GetAnimation(6 | 0xe)`.
+	Animation skate;
+	if (_vm->getAni().loadAnimation(kSkateAni, skate) && !skate.empty()) {
+		// `iVar4 = 199 - sprite_h`, `uVar5 = 320 - sprite_w` from the
+		// original; sprite_h/w come from the FIRST frame.
+		const int spriteH = skate[0].surface.h;
+		const int spriteW = skate[0].surface.w;
+		int x = (320 - spriteW) & ~3;            // 4-px aligned (mode-X)
+		const int y = 199 - spriteH;
+		const byte transp = (byte)(skate[0].flags >> 8);
+		uint frameIdx = 0;
+		int distSinceTick = 0;
+		const int kStep = 4;            // _MoveSkateBoardPixels analogue
+		const int kFrameTicks = 0xc;    // original switches frame at 12 px
+
+		while (x + spriteW > 0 && !_vm->shouldQuit()) {
+			Graphics::ManagedSurface scratch(320, 200,
+				Graphics::PixelFormat::createFormatCLUT8());
+			for (int row = 0; row < 200; row++) {
+				memcpy((byte *)scratch.getBasePtr(0, row),
+					   (const byte *)bg.getBasePtr(0, row), 320);
+			}
+			blitFrame(scratch, skate[frameIdx], x, y, transp);
+			g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
+									   0, 0, 320, 200);
+			g_system->updateScreen();
+
+			Common::Event ev;
+			while (g_system->getEventManager()->pollEvent(ev)) {
+				if (ev.type == Common::EVENT_KEYDOWN ||
+					ev.type == Common::EVENT_LBUTTONDOWN) {
+					return; // user-skip ??? bail out of the animation
+				}
+			}
+
+			x -= kStep;
+			distSinceTick += kStep;
+			if (distSinceTick >= kFrameTicks) {
+				frameIdx = (frameIdx + 1) % skate.size();
+				distSinceTick = 0;
+			}
+			g_system->delayMillis(40);
+		}
+	}
+
+	// Phase 2 ??? KD slide-in. From `_EnterSiteAnim` each frame is blitted
+	// at its OWN anchor offsets (the sprite "walks in" because the
+	// frame-by-frame anchors decrease as the animation progresses):
+	//   destX = -frame.miscflags    (on-disk byte 8 = anchor X)
+	//   destY = kKDY - frame.rowoff (on-disk byte 6 = anchor Y)
+	// Each frame waits one `_CheckFrameRate` tick ??? we use 80 ms which
+	// matches the original's ~12 FPS pacing.
+	Animation kd;
+	if (_vm->getAni().loadAnimation(kKDAni, kd) && !kd.empty()) {
+		for (uint frameIdx = 0;
+			 frameIdx < kd.size() && !_vm->shouldQuit();
+			 frameIdx++) {
+			const Picture &fr = kd[frameIdx];
+			const byte transp = (byte)(fr.flags >> 8);
+			const int destX = -(int)fr.miscflags;
+			const int destY = kKDY - (int)fr.rowoff;
 
+			Graphics::ManagedSurface scratch(320, 200,
+				Graphics::PixelFormat::createFormatCLUT8());
+			for (int row = 0; row < 200; row++) {
+				memcpy((byte *)scratch.getBasePtr(0, row),
+					   (const byte *)bg.getBasePtr(0, row), 320);
+			}
+			blitFrame(scratch, fr, destX, destY, transp);
+			g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
+									   0, 0, 320, 200);
+			g_system->updateScreen();
+
+			Common::Event ev;
+			while (g_system->getEventManager()->pollEvent(ev)) {
+				if (ev.type == Common::EVENT_KEYDOWN ||
+					ev.type == Common::EVENT_LBUTTONDOWN) {
+					return;
+				}
+			}
+			g_system->delayMillis(80);
+		}
+	}
+}
+
+void SiteScreen::renderPartner(uint siteNum) {
+	// `_DoSiteLoop @ 168d:03f4` reads `siteData[+8]` as the speaker
+	// table index, then for each (speaker ?? partner) loads
+	//   anim  = WaitAnims[speakerIdx].anim[partner]
+	//   x     = WaitAnims[speakerIdx].x[partner]
+	//   y     = WaitAnims[speakerIdx].y[partner]
+	// from the table at `_WaitAnims @ 29be:021c`. Each entry is
+	// 12 bytes / 6 u16:
+	//   +0..1 anim Jake, +2..3 anim Jenny,
+	//   +4..5 x    Jake, +6..7 x    Jenny,
+	//   +8..9 y    Jake, +10..11 y    Jenny.
+	// Verbatim copy of the bytes Ghidra dumped at 29be:021c so we
+	// don't need to ship the original data segment.
+	static const uint16 kWaitAnims[][6] = {
+		{ 0x00, 0x0a, 0x06, 0x06, 0x50, 0x50 }, // 0
+		{ 0x03, 0x0c, 0x06, 0x06, 0x50, 0x50 }, // 1
+		{ 0x01, 0x0b, 0x06, 0x06, 0x50, 0x50 }, // 2
+		{ 0x04, 0x0d, 0x06, 0x06, 0x50, 0x50 }, // 3
+		{ 0x02, 0x10, 0x06, 0x06, 0x50, 0x50 }, // 4
+		{ 0x05, 0x05, 0x06, 0x06, 0x50, 0x50 }, // 5
+		{ 0x06, 0x06, 0x06, 0x06, 0x50, 0x50 }, // 6
+		{ 0x00, 0x00, 0x23, 0x6f, 0x38, 0x88 }, // 7 ??? special pos
+		{ 0x07, 0xb1, 0x39, 0xc8, 0x88, 0xae }, // 8 ??? likely junk; 0xb1 anim id is suspect
+		{ 0x9d, 0xbe, 0xa3, 0xae, 0xb8, 0xbe }  // 9 ??? likely junk
+	};
+
+	const byte *site = _mystery->siteData(siteNum);
+	if (!site)
+		return;
+	const uint16 speaker = READ_LE_UINT16(site + 8);
+	if (speaker >= ARRAYSIZE(kWaitAnims))
+		return;
+
+	const uint8 partner = _vm->getPartnerIndex();
+	const uint  animId  = kWaitAnims[speaker][0 + partner];
+	const int   x       = (int)(int16)kWaitAnims[speaker][2 + partner];
+	const int   y       = (int)(int16)kWaitAnims[speaker][4 + partner];
+
+	Animation anim;
+	if (!_vm->getAni().loadAnimation(animId, anim) || anim.empty())
+		return;
+
+	// Show the first frame as a static sprite. The original updates it
+	// each `_CheckFrameRate` tick; we don't have a frame pump in the
+	// site loop yet so a static pose is enough for now.
+	const Picture &fr = anim[0];
+	const byte transp = (byte)(fr.flags >> 8);
+	Graphics::Surface *screen = g_system->lockScreen();
+	if (!screen)
+		return;
+	for (int row = 0; row < fr.surface.h; row++) {
+		const int dstY = y + row;
+		if (dstY < 0 || dstY >= 200)
+			continue;
+		const byte *src = (const byte *)fr.surface.getBasePtr(0, row);
+		byte *dst = (byte *)screen->getBasePtr(0, dstY);
+		for (int col = 0; col < fr.surface.w; col++) {
+			const int dstX = x + col;
+			if (dstX < 0 || dstX >= 320)
+				continue;
+			if (src[col] != transp)
+				dst[dstX] = src[col];
+		}
+	}
+	g_system->unlockScreen();
+}
+
+void SiteScreen::renderBackground(uint siteNum) {
+	// Mirrors `_BuildBackground(sitepic, 0x42, 0x14)` as called from
+	// `_DoSiteLoop @ 168d:03f4` and `_DisplayCorrect`. The original:
+	//   1. Loads frame via `_GetFromDB(_PicIndex, 0x3d)` ??? that's
+	//      DBI entry 0x3d (0-based). Note this is NOT the same as
+	//      `_GetPicture(0x3d)` which would index entry 0x3c. Our
+	//      `loadEntry(0x3d)` matches the original directly.
+	//   2. Loads SITES.DBD entry by `sitepic` (0-based, from
+	//      SiteData[+0..+1]).
+	//   3. `_Rect_Move(... x, y, 48000, ...)` composes the scene at
+	//      (x, y) = (0x42, 0x14) = (66, 20) on top of the frame.
+	//   4. `_GetPalette(sitepic + 1)` ??? per-site palette.
 	Picture frame;
-	bool haveFrame = _vm->getPics().getPicture(0x43, frame);
-	if (!haveFrame)
-		haveFrame = _vm->getPics().getPicture(0x3d, frame);
-	if (haveFrame) {
+	if (_vm->getPics().loadEntry(0x3d, frame)) {
 		g_system->copyRectToScreen(frame.surface.getPixels(),
 								   frame.surface.pitch,
 								   0, 0, frame.surface.w, frame.surface.h);
 	}
 
-	// Scene from SITES.DBD: indexed by `sitepic` from SiteData (global
-	// SITES.DBD entry, NOT the per-mystery site index). Falls back to
-	// `_GetPicture(sitepic)` if SITES is unavailable.
 	const byte *site = _mystery->siteData(siteNum);
 	const uint16 sitepic = site ? READ_LE_UINT16(site) : 0;
 	Picture scene;
 	bool haveScene = false;
-	bool fromPics = false;
 	if (sitepic > 0 && _vm->getSites().size() > sitepic - 1)
 		haveScene = _vm->getSites().loadEntry(sitepic - 1, scene);
-	if (!haveScene && sitepic > 0) {
+	if (!haveScene && sitepic > 0)
 		haveScene = _vm->getPics().getPicture(sitepic, scene);
-		fromPics = haveScene;
-	}
 	if (haveScene) {
-		const int w = MIN<int>(scene.surface.w, 320);
-		const int h = MIN<int>(scene.surface.h, 200);
-		// Full-screen pictures (sitepic fallback) go at (0, 0); smaller
-		// SITES.DBD scenes are centred horizontally with the top below
-		// the HUD bar so progress info stays visible.
-		const int x = fromPics ? 0 : (320 - w) / 2;
-		const int y = fromPics ? 0 : (h < 180 ? 4 : 0);
-		g_system->copyRectToScreen(scene.surface.getPixels(),
-								   scene.surface.pitch, x, y, w, h);
+		// Hard-coded composition position from `_BuildBackground`:
+		//   `_Rect_Move(0, 0, h, ..., 0x42, 0x14, 48000, h, w)`.
+		const int x = 0x42;
+		const int y = 0x14;
+		const int w = MIN<int>(scene.surface.w, 320 - x);
+		const int h = MIN<int>(scene.surface.h, 200 - y);
+		if (w > 0 && h > 0)
+			g_system->copyRectToScreen(scene.surface.getPixels(),
+									   scene.surface.pitch, x, y, w, h);
 	}
 }
 
diff --git a/engines/eem/site.h b/engines/eem/site.h
index ce450bb1905..2346f5e5a77 100644
--- a/engines/eem/site.h
+++ b/engines/eem/site.h
@@ -65,9 +65,22 @@ private:
 	int  hotspotAtPoint(uint siteNum, int x, int y) const;
 	void onHotspotClicked(uint siteNum, uint hotIdx);
 
+	/// Play the partner's site-arrival sequence once `_LastSite !=
+	/// _SiteNumber`. Mirrors `_EnterSiteAnim @ 1000:9b21` ??? animation
+	/// 6 (Jake) / 14 (Jenny) skateboards in from the right edge along
+	/// the bottom, then animation 7 / 15 slides KD in from the left.
+	void enterSiteAnim();
+
+	/// Draw the persistent in-site partner sprite (Jake or Jenny
+	/// standing/idling) at the position from `_WaitAnims` @ 29be:021c.
+	/// Mirrors the `_GetAnimation` + `_NewAnimation` block at the tail
+	/// of `_DoSiteLoop @ 168d:03f4`.
+	void renderPartner(uint siteNum);
+
 	EEMEngine *_vm;
 	Mystery *_mystery;
 	bool _showHotspots = true;  ///< Toggle outlines with V key.
+	int _lastSiteAnim = -1;     ///< Last site we played the arrival on.
 };
 
 } // End of namespace EEM


Commit: 30367c4690af026d7a3889ebbf7530ba2bfbe2d7
    https://github.com/scummvm/scummvm/commit/30367c4690af026d7a3889ebbf7530ba2bfbe2d7
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:20:36+02:00

Commit Message:
EEM: zoom/overview map initial implementation

Changed paths:
    engines/eem/eem.cpp


diff --git a/engines/eem/eem.cpp b/engines/eem/eem.cpp
index f906aec7d69..98539e94427 100644
--- a/engines/eem/eem.cpp
+++ b/engines/eem/eem.cpp
@@ -1507,85 +1507,197 @@ void EEMEngine::doGallery() {
 }
 
 void EEMEngine::doBigMap() {
-	// Mirrors the small/scrollable map view used inside the site loop ???
-	// the original game draws a 0xe9 ?? 0xab viewport at (2, 2) into
-	// `_MapBitMap` (= BIGMAP.PIC) via `DrawMap @ 20fe:1058`; on top of
-	// that, `_StampButtons @ 20fe:0d2f` bakes site icons into the
-	// bitmap at MapData offsets (+4, +6). We render BIGMAP.PIC into a
-	// 233??171 viewport and overlay markers ourselves.
+	// Two-stage flow that mirrors the original screen-1 wrapper at
+	// 20fe:120b and `_DoBigMap @ 20fe:09e7`:
 	//
-	// MapData entry layout (14 bytes), confirmed from
-	// `_DrawBigMapButtons @ 20fe:0877` and `_StampButtons @ 20fe:0d2f`:
-	//   +0..1  BigMap X (overview frame PIC 0x42)
-	//   +2..3  BigMap Y
-	//   +4..5  SmallMap X (stamped into _MapBitMap at this position)
-	//   +6..7  SmallMap Y
-	//   +8..9  crime-flag (DAT_2d5d_5436): non-zero ??? crime-scene marker
-	//   +10..13 unused
+	//   STAGE 1 ??? Overview. PIC 0x42 + site icons drawn via the
+	//   `_DrawBigMapButtons` algorithm at BigMap coords MapData[+4/+6].
+	//   The original `_DoBigMap` returns sx/sy = (mouseX*2 - 0x74,
+	//   mouseY*2 - 0x55) when the player clicks inside `BigMapWindow`,
+	//   which is the scroll position into the SmallMap.
+	//
+	//   STAGE 2 ??? Detail zoom. PIC 0x43 frame + a 0xe9 ?? 0xab viewport
+	//   into BIGMAP.PIC at (2, 2), drawn by `DrawMap @ 20fe:1058` with
+	//   the (sx, sy) returned from stage 1. Site icons are stamped at
+	//   SmallMap coords MapData[+8/+0xa] via `_StampButtons`. Click on
+	//   a site icon ??? travel.
+	//
+	// MapData entry layout (14 bytes), verified directly from the
+	// disassembly of `_DrawBigMapButtons @ 20fe:0877` (`PUSH ES:[BX+4]`
+	// for X, `PUSH ES:[BX+6]` for Y, `CMP ES:[BX+0xc], 0` for crime)
+	// and `_StampButtons @ 20fe:0d2f` (`MOV AX, ES:[BX+8]`,
+	// `MOV AX, ES:[BX+0xa]`):
+	//   +0..3   ??? (not yet decoded)
+	//   +4..5   BigMap X
+	//   +6..7   BigMap Y
+	//   +8..9   SmallMap X
+	//   +0xa..b SmallMap Y
+	//   +0xc..d crime-flag
+
+	if (!_mystery.isLoaded())
+		return;
+
+	CursorMan.showMouse(true);
+
+	// `_GetPalette(0x24)` per `_DoBigMap @ 20fe:09e7`.
+	setSitePalette(0x24);
+
+	const Common::Rect kSetupRect(0xc7, 0x12, 0xc7 + 0x32, 0x12 + 0xa); // approx; original from globals
+	(void)kSetupRect; // not yet wired into our overlay
+
+	// ------------------------------------------------------------------
+	// STAGE 1 ??? Overview: PIC 0x42 + clickable site icons.
+	// ------------------------------------------------------------------
+
+	auto drawOverview = [&]() {
+		Graphics::ManagedSurface scratch(320, 200,
+			Graphics::PixelFormat::createFormatCLUT8());
+		scratch.clear();
+
+		Picture frame;
+		if (_picsArchive.getPicture(0x42, frame)) {
+			const int w = MIN<int>(frame.surface.w, 320);
+			const int h = MIN<int>(frame.surface.h, 200);
+			for (int row = 0; row < h; row++)
+				memcpy((byte *)scratch.getBasePtr(0, row),
+					   (const byte *)frame.surface.getBasePtr(0, row), w);
+		}
+
+		// Site icons at BigMap coords (+4, +6). Three colours by state:
+		//   visited ??? DoneMarker analogue, crime flag ??? CrimeMarker,
+		//   else ??? SiteMarker. Current site gets a bright highlight.
+		for (uint i = 0; i < _mystery.numSites(); i++) {
+			if (!_mystery._onSites[i] && i != _mystery._siteNumber)
+				continue;
+			const byte *entry = _mystery.mapEntry(i);
+			if (!entry)
+				continue;
+			const uint16 mx    = READ_LE_UINT16(entry + 0x4);
+			const uint16 my    = READ_LE_UINT16(entry + 0x6);
+			const uint16 crime = READ_LE_UINT16(entry + 0xc);
+
+			byte color;
+			if (i < Mystery::kVisitedSiteCap && _mystery._visitedSite[i])
+				color = 0x07;
+			else if (crime != 0)
+				color = 0x0C;
+			else
+				color = 0x0F;
+			if (i == _mystery._siteNumber)
+				color = 0x0E;
+
+			const Common::Rect mark(mx - 3, my - 3, mx + 4, my + 4);
+			scratch.fillRect(mark, color);
+		}
+
+		g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
+								   0, 0, 320, 200);
+		g_system->updateScreen();
+	};
+
+	auto findSiteAt = [&](int x, int y) -> int {
+		// Hit-test the icons drawn in stage 1. Generous radius matches
+		// the marker square plus a couple of pixels of slop.
+		for (uint i = 0; i < _mystery.numSites(); i++) {
+			if (!_mystery._onSites[i] && i != _mystery._siteNumber)
+				continue;
+			const byte *entry = _mystery.mapEntry(i);
+			if (!entry) continue;
+			const int mx = (int)READ_LE_UINT16(entry + 0x4);
+			const int my = (int)READ_LE_UINT16(entry + 0x6);
+			if (ABS(x - mx) <= 6 && ABS(y - my) <= 6)
+				return (int)i;
+		}
+		return -1;
+	};
+
+	drawOverview();
+
+	bool wantZoom = false;
+	int  zoomX = 0, zoomY = 0;
+	while (!shouldQuit()) {
+		Common::Event ev;
+		while (g_system->getEventManager()->pollEvent(ev)) {
+			if (ev.type == Common::EVENT_QUIT ||
+				ev.type == Common::EVENT_RETURN_TO_LAUNCHER)
+				return;
+			if (ev.type == Common::EVENT_KEYDOWN &&
+				ev.kbd.keycode == Common::KEYCODE_ESCAPE)
+				return;
+			if (ev.type == Common::EVENT_LBUTTONDOWN) {
+				const int hit = findSiteAt(ev.mouse.x, ev.mouse.y);
+				if (hit >= 0) {
+					// Direct click on a site marker ??? travel.
+					_mystery._lastSite = _mystery._siteNumber;
+					_mystery._siteNumber = (uint16)hit;
+					return;
+				}
+				// `_DoBigMap` rule for clicks elsewhere: convert mouse
+				// coords to a SmallMap scroll position
+				//   sx = mouseX * 2 - 0x74
+				//   sy = mouseY * 2 - 0x55
+				// then transition to the detail zoom.
+				int sx = ev.mouse.x * 2;
+				int sy = ev.mouse.y * 2;
+				sx = (sx < 0x75) ? 0 : sx - 0x74;
+				sy = (sy < 0x56) ? 0 : sy - 0x55;
+				zoomX = sx;
+				zoomY = sy;
+				wantZoom = true;
+				break;
+			}
+		}
+		if (wantZoom)
+			break;
+		g_system->updateScreen();
+		g_system->delayMillis(10);
+	}
+
+	if (!wantZoom)
+		return;
+
+	// ------------------------------------------------------------------
+	// STAGE 2 ??? Detail zoom: PIC 0x43 frame + scrollable BIGMAP.PIC
+	// viewport at (2, 2), 0xe9 ?? 0xab. Click on a stamped icon ??? travel.
+	// ------------------------------------------------------------------
+
 	Common::File f;
 	if (!f.open(Common::Path("BIGMAP.PIC"))) {
-		warning("doBigMap: BIGMAP.PIC missing");
+		warning("doBigMap: BIGMAP.PIC missing for detail view");
 		return;
 	}
 	const uint16 mapH = f.readUint16LE();
 	const uint16 mapW = f.readUint16LE();
 	if (mapW == 0 || mapH == 0)
 		return;
-
 	Common::Array<byte> mapPixels((uint32)mapW * mapH);
 	if (f.read(mapPixels.data(), mapPixels.size()) != mapPixels.size()) {
-		warning("doBigMap: short read on BIGMAP.PIC");
+		warning("doBigMap: short read on BIGMAP.PIC for detail view");
 		return;
 	}
 
-	// Viewport size from `DrawMap`: 0xe9 ?? 0xab at screen (2, 2).
-	// We use (4, 4) so a 1-pixel inset preserves the frame border.
 	const int kMapWinW = 0xe9; // 233
 	const int kMapWinH = 0xab; // 171
-	const int kMapWinX = 4;
-	const int kMapWinY = 4;
-
-	int scrollX = 0;
-	int scrollY = 0;
-
-	// Auto-scroll to centre the current site (using SmallMap coords).
-	if (_mystery.isLoaded()) {
-		const byte *entry = _mystery.mapEntry(_mystery._siteNumber);
-		if (entry) {
-			const uint16 mx = READ_LE_UINT16(entry + 4);
-			const uint16 my = READ_LE_UINT16(entry + 6);
-			scrollX = MAX<int>(0, MIN<int>(mapW - kMapWinW,
-				(int)mx - kMapWinW / 2));
-			scrollY = MAX<int>(0, MIN<int>(mapH - kMapWinH,
-				(int)my - kMapWinH / 2));
-		}
-	}
+	const int kMapWinX = 2;
+	const int kMapWinY = 2;
 
-	CursorMan.showMouse(true);
-
-	// `_DoBigMap @ 20fe:09e7` calls `_GetPalette(0x24)` after loading
-	// the frame. Without this, the map would render under whatever
-	// palette the prior site set, which makes BIGMAP.PIC look like
-	// noise / incomplete colours.
-	setSitePalette(0x24);
+	int scrollX = MAX<int>(0, MIN<int>(mapW - kMapWinW, zoomX));
+	int scrollY = MAX<int>(0, MIN<int>(mapH - kMapWinH, zoomY));
 
-	auto draw = [&]() {
+	auto drawDetail = [&]() {
 		Graphics::ManagedSurface scratch(320, 200,
 			Graphics::PixelFormat::createFormatCLUT8());
 		scratch.clear();
 
-		// Frame from PIC 0x42 (`_GetPicture(0x42)` in `_DoBigMap`).
 		Picture frame;
-		if (_picsArchive.getPicture(0x42, frame)) {
+		if (_picsArchive.getPicture(0x43, frame)) {
 			const int w = MIN<int>(frame.surface.w, 320);
 			const int h = MIN<int>(frame.surface.h, 200);
-			for (int row = 0; row < h; row++) {
+			for (int row = 0; row < h; row++)
 				memcpy((byte *)scratch.getBasePtr(0, row),
 					   (const byte *)frame.surface.getBasePtr(0, row), w);
-			}
 		}
 
-		// Map content clipped into the inner window, applying scroll.
 		const int copyW = MIN<int>(mapW - scrollX, kMapWinW);
 		const int copyH = MIN<int>(mapH - scrollY, kMapWinH);
 		for (int row = 0; row < copyH; row++) {
@@ -1594,48 +1706,42 @@ void EEMEngine::doBigMap() {
 				   copyW);
 		}
 
-		// Site markers ??? three states from `_DrawBigMapButtons`:
-		//   _DoneMarker if `SaveSiteComplete[i]` (already searched)
-		//   _CrimeMarker if MapData[+8] != 0 (crime scene)
-		//   _SiteMarker otherwise
-		// We don't have the marker PICs traced yet, so draw small filled
-		// squares with three colours that match the original semantics.
-		if (_mystery.isLoaded()) {
-			for (uint i = 0; i < _mystery.numSites(); i++) {
-				if (!_mystery._onSites[i] && i != _mystery._siteNumber)
-					continue;
-				const byte *entry = _mystery.mapEntry(i);
-				if (!entry)
-					continue;
-				const uint16 mx = READ_LE_UINT16(entry + 4);
-				const uint16 my = READ_LE_UINT16(entry + 6);
-				const uint16 crime = READ_LE_UINT16(entry + 8);
-				const int sx = (int)mx - scrollX + kMapWinX;
-				const int sy = (int)my - scrollY + kMapWinY;
-				if (sx < kMapWinX || sx >= kMapWinX + kMapWinW ||
-					sy < kMapWinY || sy >= kMapWinY + kMapWinH)
-					continue;
+		// Stamped site icons at SmallMap coords (+8, +0xa). Same colour
+		// scheme as the overview so the player can tell them apart.
+		for (uint i = 0; i < _mystery.numSites(); i++) {
+			if (!_mystery._onSites[i] && i != _mystery._siteNumber)
+				continue;
+			const byte *entry = _mystery.mapEntry(i);
+			if (!entry) continue;
+			const uint16 mx    = READ_LE_UINT16(entry + 0x8);
+			const uint16 my    = READ_LE_UINT16(entry + 0xa);
+			const uint16 crime = READ_LE_UINT16(entry + 0xc);
+			const int sx = (int)mx - scrollX + kMapWinX;
+			const int sy = (int)my - scrollY + kMapWinY;
+			if (sx < kMapWinX || sx >= kMapWinX + kMapWinW ||
+				sy < kMapWinY || sy >= kMapWinY + kMapWinH)
+				continue;
 
-				byte color;
-				if (i < Mystery::kVisitedSiteCap && _mystery._visitedSite[i])
-					color = 0x07;       // searched (DoneMarker analogue)
-				else if (crime != 0)
-					color = 0x0C;       // crime scene (CrimeMarker analogue)
-				else
-					color = 0x0F;       // available (SiteMarker analogue)
-				if (i == _mystery._siteNumber)
-					color = 0x0E;       // current site ??? bright yellow
-
-				const Common::Rect mark(sx - 3, sy - 3, sx + 4, sy + 4);
-				scratch.fillRect(mark, color);
-			}
+			byte color;
+			if (i < Mystery::kVisitedSiteCap && _mystery._visitedSite[i])
+				color = 0x07;
+			else if (crime != 0)
+				color = 0x0C;
+			else
+				color = 0x0F;
+			if (i == _mystery._siteNumber)
+				color = 0x0E;
+
+			const Common::Rect mark(sx - 3, sy - 3, sx + 4, sy + 4);
+			scratch.fillRect(mark, color);
 		}
 
 		g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
 								   0, 0, 320, 200);
 		g_system->updateScreen();
 	};
-	draw();
+
+	drawDetail();
 
 	while (!shouldQuit()) {
 		Common::Event ev;
@@ -1646,37 +1752,26 @@ void EEMEngine::doBigMap() {
 				return;
 			if (ev.type == Common::EVENT_KEYDOWN) {
 				if (ev.kbd.keycode == Common::KEYCODE_ESCAPE)
-					return;
+					return;  // exit detail back to caller (site loop / engine)
 				const int kStep = 16;
 				if (ev.kbd.keycode == Common::KEYCODE_LEFT) {
 					scrollX = MAX<int>(0, scrollX - kStep);
 					dirty = true;
 				} else if (ev.kbd.keycode == Common::KEYCODE_RIGHT) {
-					scrollX = MIN<int>(MAX<int>(0, mapW - kMapWinW), scrollX + kStep);
+					scrollX = MIN<int>(MAX<int>(0, mapW - kMapWinW),
+						scrollX + kStep);
 					dirty = true;
 				} else if (ev.kbd.keycode == Common::KEYCODE_UP) {
 					scrollY = MAX<int>(0, scrollY - kStep);
 					dirty = true;
 				} else if (ev.kbd.keycode == Common::KEYCODE_DOWN) {
-					scrollY = MIN<int>(MAX<int>(0, mapH - kMapWinH), scrollY + kStep);
+					scrollY = MIN<int>(MAX<int>(0, mapH - kMapWinH),
+						scrollY + kStep);
 					dirty = true;
 				}
-				if (ev.kbd.keycode >= Common::KEYCODE_0 &&
-					ev.kbd.keycode <= Common::KEYCODE_9) {
-					const uint target = (uint)(ev.kbd.keycode - Common::KEYCODE_0);
-					if (_mystery.isLoaded() &&
-						target < _mystery.numSites() &&
-						_mystery._onSites[target]) {
-						_mystery._lastSite = _mystery._siteNumber;
-						_mystery._siteNumber = (uint16)target;
-						return;
-					}
-				}
 			}
 			if (ev.type == Common::EVENT_LBUTTONDOWN) {
-				// Click on a map marker?
-				if (_mystery.isLoaded() &&
-					ev.mouse.x >= kMapWinX &&
+				if (ev.mouse.x >= kMapWinX &&
 					ev.mouse.x < kMapWinX + kMapWinW &&
 					ev.mouse.y >= kMapWinY &&
 					ev.mouse.y < kMapWinY + kMapWinH) {
@@ -1686,23 +1781,22 @@ void EEMEngine::doBigMap() {
 							continue;
 						const byte *entry = _mystery.mapEntry(i);
 						if (!entry) continue;
-						const uint16 mx = READ_LE_UINT16(entry + 4);
-						const uint16 my = READ_LE_UINT16(entry + 6);
+						const uint16 mx = READ_LE_UINT16(entry + 0x8);
+						const uint16 my = READ_LE_UINT16(entry + 0xa);
 						const int sx = (int)mx - scrollX + kMapWinX;
 						const int sy = (int)my - scrollY + kMapWinY;
-						if (ABS(ev.mouse.x - sx) <= 5 &&
-							ABS(ev.mouse.y - sy) <= 5) {
+						if (ABS(ev.mouse.x - sx) <= 6 &&
+							ABS(ev.mouse.y - sy) <= 6) {
 							_mystery._lastSite = _mystery._siteNumber;
 							_mystery._siteNumber = (uint16)i;
 							return;
 						}
 					}
 				}
-
 			}
 		}
 		if (dirty)
-			draw();
+			drawDetail();
 		g_system->updateScreen();
 		g_system->delayMillis(10);
 	}


Commit: a4c4170605ca1557ef49e108465c07e0e62d2fa8
    https://github.com/scummvm/scummvm/commit/a4c4170605ca1557ef49e108465c07e0e62d2fa8
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:20:36+02:00

Commit Message:
EEM: load the correct sites and make sure they are labeled

Changed paths:
    engines/eem/eem.cpp
    engines/eem/eem.h
    engines/eem/site.cpp


diff --git a/engines/eem/eem.cpp b/engines/eem/eem.cpp
index 98539e94427..e79f9723286 100644
--- a/engines/eem/eem.cpp
+++ b/engines/eem/eem.cpp
@@ -248,6 +248,12 @@ bool EEMEngine::openArchives() {
 		warning("SITES archive missing ??? site backgrounds disabled");
 	if (!_balloonArchive.open(Common::Path("BALLOON.DBD"), Common::Path("BALLOON.DBX")))
 		warning("BALLOON archive missing ??? clue text will lack balloons");
+	// `_GetButton @ 172b:199d` reads from this archive (see strings
+	// 'button.dbd' / 'Button.DBX' at 29be:06bf / 29be:04bb). Each
+	// per-site map marker (used by `_StampButtons @ 20fe:0d2f` and
+	// looked up via MapData[+0]) lives here.
+	if (!_buttonArchive.open(Common::Path("BUTTON.DBD"), Common::Path("BUTTON.DBX")))
+		warning("BUTTON archive missing ??? map markers will be unlabelled");
 	return true;
 }
 
@@ -1595,22 +1601,6 @@ void EEMEngine::doBigMap() {
 		g_system->updateScreen();
 	};
 
-	auto findSiteAt = [&](int x, int y) -> int {
-		// Hit-test the icons drawn in stage 1. Generous radius matches
-		// the marker square plus a couple of pixels of slop.
-		for (uint i = 0; i < _mystery.numSites(); i++) {
-			if (!_mystery._onSites[i] && i != _mystery._siteNumber)
-				continue;
-			const byte *entry = _mystery.mapEntry(i);
-			if (!entry) continue;
-			const int mx = (int)READ_LE_UINT16(entry + 0x4);
-			const int my = (int)READ_LE_UINT16(entry + 0x6);
-			if (ABS(x - mx) <= 6 && ABS(y - my) <= 6)
-				return (int)i;
-		}
-		return -1;
-	};
-
 	drawOverview();
 
 	bool wantZoom = false;
@@ -1625,18 +1615,11 @@ void EEMEngine::doBigMap() {
 				ev.kbd.keycode == Common::KEYCODE_ESCAPE)
 				return;
 			if (ev.type == Common::EVENT_LBUTTONDOWN) {
-				const int hit = findSiteAt(ev.mouse.x, ev.mouse.y);
-				if (hit >= 0) {
-					// Direct click on a site marker ??? travel.
-					_mystery._lastSite = _mystery._siteNumber;
-					_mystery._siteNumber = (uint16)hit;
-					return;
-				}
-				// `_DoBigMap` rule for clicks elsewhere: convert mouse
-				// coords to a SmallMap scroll position
-				//   sx = mouseX * 2 - 0x74
-				//   sy = mouseY * 2 - 0x55
-				// then transition to the detail zoom.
+				// `_DoBigMap @ 20fe:09e7` does NOT travel directly when
+				// the player clicks an overview icon ??? it always returns
+				// `(sx, sy) = (mouseX*2 - 0x74, mouseY*2 - 0x55)` so the
+				// caller can switch to the detail zoom centred there.
+				// Travel happens after a SECOND click in the detail view.
 				int sx = ev.mouse.x * 2;
 				int sy = ev.mouse.y * 2;
 				sx = (sx < 0x75) ? 0 : sx - 0x74;
@@ -1706,34 +1689,42 @@ void EEMEngine::doBigMap() {
 				   copyW);
 		}
 
-		// Stamped site icons at SmallMap coords (+8, +0xa). Same colour
-		// scheme as the overview so the player can tell them apart.
+		// Stamped site buttons. `_StampButtons @ 20fe:0d2f` does:
+		//   button = _GetButton(MapData[+0])      // BUTTON.DBD entry
+		//   destX  = MapData[+8],  destY = MapData[+0xa]
+		// then bakes the button PIC into the map bitmap. Each button
+		// sprite carries the site name baked in. We blit them on top
+		// of the BIGMAP.PIC viewport at the same SmallMap coords.
 		for (uint i = 0; i < _mystery.numSites(); i++) {
 			if (!_mystery._onSites[i] && i != _mystery._siteNumber)
 				continue;
 			const byte *entry = _mystery.mapEntry(i);
 			if (!entry) continue;
-			const uint16 mx    = READ_LE_UINT16(entry + 0x8);
-			const uint16 my    = READ_LE_UINT16(entry + 0xa);
-			const uint16 crime = READ_LE_UINT16(entry + 0xc);
+			const uint16 buttonId = READ_LE_UINT16(entry + 0x0);
+			const uint16 mx       = READ_LE_UINT16(entry + 0x8);
+			const uint16 my       = READ_LE_UINT16(entry + 0xa);
+
+			Picture button;
+			if (!_buttonArchive.loadEntry(buttonId, button))
+				continue;
 			const int sx = (int)mx - scrollX + kMapWinX;
 			const int sy = (int)my - scrollY + kMapWinY;
-			if (sx < kMapWinX || sx >= kMapWinX + kMapWinW ||
-				sy < kMapWinY || sy >= kMapWinY + kMapWinH)
-				continue;
-
-			byte color;
-			if (i < Mystery::kVisitedSiteCap && _mystery._visitedSite[i])
-				color = 0x07;
-			else if (crime != 0)
-				color = 0x0C;
-			else
-				color = 0x0F;
-			if (i == _mystery._siteNumber)
-				color = 0x0E;
-
-			const Common::Rect mark(sx - 3, sy - 3, sx + 4, sy + 4);
-			scratch.fillRect(mark, color);
+			const byte transp = (byte)(button.flags >> 8);
+
+			// Crop blit against the viewport.
+			const int x0 = MAX<int>(sx, kMapWinX);
+			const int y0 = MAX<int>(sy, kMapWinY);
+			const int x1 = MIN<int>(sx + button.surface.w, kMapWinX + kMapWinW);
+			const int y1 = MIN<int>(sy + button.surface.h, kMapWinY + kMapWinH);
+			for (int row = y0; row < y1; row++) {
+				const byte *src = (const byte *)button.surface.getBasePtr(0, row - sy);
+				byte *dst = (byte *)scratch.getBasePtr(0, row);
+				for (int col = x0; col < x1; col++) {
+					const byte px = src[col - sx];
+					if (px != transp)
+						dst[col] = px;
+				}
+			}
 		}
 
 		g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
@@ -1775,18 +1766,28 @@ void EEMEngine::doBigMap() {
 					ev.mouse.x < kMapWinX + kMapWinW &&
 					ev.mouse.y >= kMapWinY &&
 					ev.mouse.y < kMapWinY + kMapWinH) {
+					// Hit-test the per-site button at its actual bbox
+					// (`_StampButtons` records the rect at SmallMap +8/+0xa
+					// with the button PIC's width/height).
 					for (uint i = 0; i < _mystery.numSites(); i++) {
 						if (!_mystery._onSites[i] &&
 							i != _mystery._siteNumber)
 							continue;
 						const byte *entry = _mystery.mapEntry(i);
 						if (!entry) continue;
-						const uint16 mx = READ_LE_UINT16(entry + 0x8);
-						const uint16 my = READ_LE_UINT16(entry + 0xa);
+						const uint16 buttonId = READ_LE_UINT16(entry + 0x0);
+						const uint16 mx       = READ_LE_UINT16(entry + 0x8);
+						const uint16 my       = READ_LE_UINT16(entry + 0xa);
+						Picture button;
+						int bw = 16, bh = 16;
+						if (_buttonArchive.loadEntry(buttonId, button)) {
+							bw = button.surface.w;
+							bh = button.surface.h;
+						}
 						const int sx = (int)mx - scrollX + kMapWinX;
 						const int sy = (int)my - scrollY + kMapWinY;
-						if (ABS(ev.mouse.x - sx) <= 6 &&
-							ABS(ev.mouse.y - sy) <= 6) {
+						if (ev.mouse.x >= sx && ev.mouse.x < sx + bw &&
+							ev.mouse.y >= sy && ev.mouse.y < sy + bh) {
 							_mystery._lastSite = _mystery._siteNumber;
 							_mystery._siteNumber = (uint16)i;
 							return;
diff --git a/engines/eem/eem.h b/engines/eem/eem.h
index 24bddf6f644..ec8e9d27b35 100644
--- a/engines/eem/eem.h
+++ b/engines/eem/eem.h
@@ -72,6 +72,7 @@ public:
 	DBDArchive &getAni()     { return _aniArchive; }
 	DBDArchive &getSites()   { return _sitesArchive; }
 	DBDArchive &getBalloons(){ return _balloonArchive; }
+	DBDArchive &getButtons() { return _buttonArchive; }
 	Mystery    &getMystery() { return _mystery; }
 	const EEMFont &getFont() const { return _font; }
 	uint8       getPartnerIndex() const { return _partner; }
@@ -191,6 +192,7 @@ private:
 	DBDArchive _aniArchive;      ///< ANI.DBD/.DBX (multi-frame character animations)
 	DBDArchive _sitesArchive;    ///< SITES.DBD/.DBX (one full-screen scene per site)
 	DBDArchive _balloonArchive;  ///< BALLOON.DBD/.DBX (speech-balloon sprites)
+	DBDArchive _buttonArchive;   ///< BUTTON.DBD/.DBX (per-site labeled map buttons; `_GetButton`)
 	Mystery    _mystery;         ///< Currently-loaded case file (M<n>.BIN)
 	EEMFont    _font;            ///< FONT.FNT - main 8 px font
 
diff --git a/engines/eem/site.cpp b/engines/eem/site.cpp
index 2ee29ea63ab..0a65bf95b68 100644
--- a/engines/eem/site.cpp
+++ b/engines/eem/site.cpp
@@ -432,14 +432,22 @@ void SiteScreen::renderBackground(uint siteNum) {
 								   0, 0, frame.surface.w, frame.surface.h);
 	}
 
+	// `_BuildBackground @ 172b:13e2` calls
+	//   `_GetFromDB(_siteFile, &_SiteDBIndex, sitenum)`
+	// with the value at SiteData[+0] passed straight through. The
+	// `_GetFromDB` callee uses that as a **0-based** index into
+	// SITES.DBD (verified at 172b:14c8: `MOV BX, [BP+0x6]; IMUL BX, BX, 0xa`
+	// ??? the dbi entry stride is 10 bytes, no -1 adjustment). Our
+	// previous `loadEntry(sitepic - 1)` was off by one, which is why
+	// the tutorial mystery rendered scenes from neighbouring cases.
 	const byte *site = _mystery->siteData(siteNum);
 	const uint16 sitepic = site ? READ_LE_UINT16(site) : 0;
 	Picture scene;
 	bool haveScene = false;
-	if (sitepic > 0 && _vm->getSites().size() > sitepic - 1)
-		haveScene = _vm->getSites().loadEntry(sitepic - 1, scene);
-	if (!haveScene && sitepic > 0)
-		haveScene = _vm->getPics().getPicture(sitepic, scene);
+	if (sitepic < _vm->getSites().size())
+		haveScene = _vm->getSites().loadEntry(sitepic, scene);
+	if (!haveScene)
+		haveScene = _vm->getPics().getPicture(sitepic + 1, scene);
 	if (haveScene) {
 		// Hard-coded composition position from `_BuildBackground`:
 		//   `_Rect_Move(0, 0, h, ..., 0x42, 0x14, 48000, h, w)`.


Commit: 83ab9371832178fbc586a3cfef5918cc31f00f7a
    https://github.com/scummvm/scummvm/commit/83ab9371832178fbc586a3cfef5918cc31f00f7a
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:20:37+02:00

Commit Message:
EEM: show NPC on sites

Changed paths:
    engines/eem/eem.cpp
    engines/eem/site.cpp
    engines/eem/site.h


diff --git a/engines/eem/eem.cpp b/engines/eem/eem.cpp
index e79f9723286..414180c0e75 100644
--- a/engines/eem/eem.cpp
+++ b/engines/eem/eem.cpp
@@ -1569,9 +1569,36 @@ void EEMEngine::doBigMap() {
 					   (const byte *)frame.surface.getBasePtr(0, row), w);
 		}
 
-		// Site icons at BigMap coords (+4, +6). Three colours by state:
-		//   visited ??? DoneMarker analogue, crime flag ??? CrimeMarker,
-		//   else ??? SiteMarker. Current site gets a bright highlight.
+		// Marker PICs from `_main @ 1a35:0f59`. Three globals are filled
+		// once at boot via `_GetPicture` (1-based IDs ??? entries N-1):
+		//   _DoneMarker  = PIC 0x20d  (already-searched site)
+		//   _SiteMarker  = PIC 0xc5   (default available site)
+		//   _CrimeMarker = PIC 0xc6   (crime-scene flag set)
+		// Picked per-site by `_DrawBigMapButtons @ 20fe:0877`:
+		//   1. SaveSiteComplete[i] ??? DoneMarker
+		//   2. else MapData[+0xc] != 0 ??? CrimeMarker
+		//   3. else SiteMarker
+		Picture done, normal, crimeM;
+		const bool haveDone   = _picsArchive.getPicture(0x20d, done);
+		const bool haveNormal = _picsArchive.getPicture(0xc5,  normal);
+		const bool haveCrime  = _picsArchive.getPicture(0xc6,  crimeM);
+
+		auto blitMarker = [&](const Picture &m, int x, int y) {
+			const byte transp = (byte)(m.flags >> 8);
+			for (int row = 0; row < m.surface.h; row++) {
+				const int dstY = y + row;
+				if (dstY < 0 || dstY >= 200) continue;
+				const byte *src = (const byte *)m.surface.getBasePtr(0, row);
+				byte *dst = (byte *)scratch.getBasePtr(0, dstY);
+				for (int col = 0; col < m.surface.w; col++) {
+					const int dstX = x + col;
+					if (dstX < 0 || dstX >= 320) continue;
+					if (src[col] != transp)
+						dst[dstX] = src[col];
+				}
+			}
+		};
+
 		for (uint i = 0; i < _mystery.numSites(); i++) {
 			if (!_mystery._onSites[i] && i != _mystery._siteNumber)
 				continue;
@@ -1581,19 +1608,21 @@ void EEMEngine::doBigMap() {
 			const uint16 mx    = READ_LE_UINT16(entry + 0x4);
 			const uint16 my    = READ_LE_UINT16(entry + 0x6);
 			const uint16 crime = READ_LE_UINT16(entry + 0xc);
-
-			byte color;
-			if (i < Mystery::kVisitedSiteCap && _mystery._visitedSite[i])
-				color = 0x07;
-			else if (crime != 0)
-				color = 0x0C;
-			else
-				color = 0x0F;
-			if (i == _mystery._siteNumber)
-				color = 0x0E;
-
-			const Common::Rect mark(mx - 3, my - 3, mx + 4, my + 4);
-			scratch.fillRect(mark, color);
+			const bool   done_ = (i < Mystery::kVisitedSiteCap)
+								  && _mystery._visitedSite[i];
+
+			const Picture *m = nullptr;
+			if (done_ && haveDone)            m = &done;
+			else if (crime != 0 && haveCrime) m = &crimeM;
+			else if (haveNormal)              m = &normal;
+
+			if (m)
+				blitMarker(*m, (int)mx, (int)my);
+			else {
+				// Fallback if the markers couldn't be loaded.
+				const Common::Rect mark(mx - 3, my - 3, mx + 4, my + 4);
+				scratch.fillRect(mark, 0x0F);
+			}
 		}
 
 		g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
diff --git a/engines/eem/site.cpp b/engines/eem/site.cpp
index 0a65bf95b68..2250b7523ce 100644
--- a/engines/eem/site.cpp
+++ b/engines/eem/site.cpp
@@ -42,9 +42,14 @@ void SiteScreen::enter(uint siteNum) {
 		return;
 	}
 
+	// Capture whether this is the first time the player enters this
+	// site BEFORE we mark it visited ??? `_DoSiteLoop @ 168d:03f4`
+	// uses the same check to decide whether to play the arrival
+	// dialog: `if (_VisitedSite[_SiteNumber] == 0) _DisplayClue(...)`.
+	const bool firstVisit = (siteNum < Mystery::kVisitedSiteCap)
+							 && (_mystery->_visitedSite[siteNum] == 0);
+
 	_mystery->_siteNumber = siteNum;
-	if (siteNum < Mystery::kVisitedSiteCap)
-		_mystery->_visitedSite[siteNum] = 1;
 	debugC(1, kDebugSite, "Entering site %u (%u hotspots)",
 		   siteNum, _mystery->hotspotCount(siteNum));
 
@@ -68,6 +73,12 @@ void SiteScreen::enter(uint siteNum) {
 		renderBackground(siteNum);
 	}
 
+	// Per-site NPCs / decorative animations (`_DoSiteLoop` reads
+	// `siteData[+0xa]` as the drop count and iterates 6-byte entries
+	// from `siteData[+0x48]`). Drawn before the partner sprite so
+	// the partner appears on top of any background NPCs.
+	renderDrops(siteNum);
+
 	// Persistent partner sprite (`_NewAnimation` at the tail of
 	// `_DoSiteLoop`). Drawn after the BG so the hotspot outlines and
 	// HUD that follow stay on top of it.
@@ -75,6 +86,39 @@ void SiteScreen::enter(uint siteNum) {
 
 	renderHotspots(siteNum);
 	g_system->updateScreen();
+
+	// First-visit dialog. `_DoSiteLoop @ 168d:03f4` does:
+	//   if (_VisitedSite[_SiteNumber] == 0) {
+	//       _DisplayClue(_Mystery + SiteIndex[siteNum*6 + 2], 1);
+	//       _VisitedSite[_SiteNumber] = 1;
+	//   }
+	// `SiteIndex[+2..+3]` is the byte offset (within the mystery
+	// buffer) of a ClueBlock that holds the partner's arrival
+	// dialogue. We've kept the +2 field undocumented up to now ???
+	// this confirms it's the entry-clue offset.
+	if (firstVisit) {
+		const byte *idx = _mystery->siteIndexEntry(siteNum);
+		if (idx) {
+			const uint16 clueOff = READ_LE_UINT16(idx + 2);
+			if (clueOff != 0xFFFF) {
+				const byte *clueBlock = _mystery->blobAt(clueOff);
+				if (clueBlock)
+					_vm->displayClue(clueBlock);
+			}
+		}
+		if (siteNum < Mystery::kVisitedSiteCap)
+			_mystery->_visitedSite[siteNum] = 1;
+		// The dialog overlay will have left the screen with portrait /
+		// balloon residues; refresh the site so the player returns to
+		// a clean state.
+		renderBackground(siteNum);
+		renderDrops(siteNum);
+		renderPartner(siteNum);
+		renderHotspots(siteNum);
+		g_system->updateScreen();
+	} else if (siteNum < Mystery::kVisitedSiteCap) {
+		_mystery->_visitedSite[siteNum] = 1;
+	}
 }
 
 void SiteScreen::run() {
@@ -346,6 +390,92 @@ void SiteScreen::enterSiteAnim() {
 	}
 }
 
+void SiteScreen::renderDrops(uint siteNum) {
+	// `_DoSiteLoop @ 168d:03f4` runs TWO per-site loops, both feeding
+	// the visible NPCs / decorations on the site BG:
+	//
+	//   Loop 1 (animations / animated NPCs)
+	//     bound: siteData[+0xa]
+	//     per entry at siteData[+0x48 + i*6]:  {animId, x, y}
+	//     if animId == -1: a `_ColorCycle(x, y)` palette range each tick
+	//     else: `_GetAnimation(animId)` + `_NewAnimation(x, y, ...)` ???
+	//           added inactive (arg5=0), updated by `_UpdateAnimations`.
+	//
+	//   Loop 2 (static drops)
+	//     bound: siteData[+0x4]  (verified at 168d:05c0:
+	//            `MOV ES:[BX+0x4], DI; CMP ES:[BX+0x4], DI`)
+	//     per entry at siteData[+0xc + i*6]:    {picId, x, y}
+	//     each ??? `_AddDrop(picId, x, y)` which loads PIC `picId-1`
+	//     from PICS.DBD and blits it at (x, y) onto offscreen 32000.
+	//
+	// Both contribute to the visible scene. We render Loop 2 statics
+	// directly and Loop 1's first frame as a static sprite (per-tick
+	// animation cycling is still TODO).
+	if (!_mystery)
+		return;
+	const byte *site = _mystery->siteData(siteNum);
+	if (!site)
+		return;
+	const uint16 numStatic = READ_LE_UINT16(site + 0x4);
+	const uint16 numAnims  = READ_LE_UINT16(site + 0xa);
+
+	Graphics::Surface *screen = g_system->lockScreen();
+	if (!screen)
+		return;
+
+	auto blitMasked = [&](const Picture &p, int x, int y) {
+		const byte transp = (byte)(p.flags >> 8);
+		for (int row = 0; row < p.surface.h; row++) {
+			const int dstY = y + row;
+			if (dstY < 0 || dstY >= screen->h) continue;
+			const byte *src = (const byte *)p.surface.getBasePtr(0, row);
+			byte *dst = (byte *)screen->getBasePtr(0, dstY);
+			for (int col = 0; col < p.surface.w; col++) {
+				const int dstX = x + col;
+				if (dstX < 0 || dstX >= screen->w) continue;
+				if (src[col] != transp)
+					dst[dstX] = src[col];
+			}
+		}
+	};
+
+	// Loop 2 ??? `_AddDrop(picId, x, y)`. PIC IDs are 1-based per
+	// `_AddDrop @ 172b:1a77` (it does `_GetFromDB(.., number - 1)`).
+	if (numStatic > 0 && numStatic <= 16) {
+		for (uint i = 0; i < numStatic; i++) {
+			const uint dropOff = 0xc + i * 6;
+			const uint16 picId = READ_LE_UINT16(site + dropOff + 0);
+			const int16  x     = (int16)READ_LE_UINT16(site + dropOff + 2);
+			const int16  y     = (int16)READ_LE_UINT16(site + dropOff + 4);
+			if (picId == 0)
+				continue;
+			Picture pic;
+			if (!_vm->getPics().getPicture(picId, pic))
+				continue;
+			blitMasked(pic, x, y);
+		}
+	}
+
+	// Loop 1 ??? animation drops. Use frame 0 as a static placeholder
+	// for non-`-1` entries.
+	if (numAnims > 0 && numAnims <= 16) {
+		for (uint i = 0; i < numAnims; i++) {
+			const uint dropOff = 0x48 + i * 6;
+			const int16 animId = (int16)READ_LE_UINT16(site + dropOff + 0);
+			if (animId < 0)
+				continue; // -1 ??? ColorCycle entry, handled per tick
+			const int16 x = (int16)READ_LE_UINT16(site + dropOff + 2);
+			const int16 y = (int16)READ_LE_UINT16(site + dropOff + 4);
+			Animation anim;
+			if (!_vm->getAni().loadAnimation((uint)animId, anim) || anim.empty())
+				continue;
+			blitMasked(anim[0], x, y);
+		}
+	}
+
+	g_system->unlockScreen();
+}
+
 void SiteScreen::renderPartner(uint siteNum) {
 	// `_DoSiteLoop @ 168d:03f4` reads `siteData[+8]` as the speaker
 	// table index, then for each (speaker ?? partner) loads
diff --git a/engines/eem/site.h b/engines/eem/site.h
index 2346f5e5a77..125a6cd853a 100644
--- a/engines/eem/site.h
+++ b/engines/eem/site.h
@@ -77,6 +77,15 @@ private:
 	/// of `_DoSiteLoop @ 168d:03f4`.
 	void renderPartner(uint siteNum);
 
+	/// Draw the per-site NPC "drops" (the locals you click on to
+	/// trigger a clue). `_DoSiteLoop` reads `siteData[+0xa]` as the
+	/// drop count, then iterates 6-byte entries at `siteData[+0x48]`:
+	///   {anim_id (-1 = ColorCycle), x, y}.
+	/// Each non-(-1) entry is a `_NewAnimation(x, y, animId)`. We blit
+	/// the first frame as a static sprite; the original cycles via
+	/// `_UpdateAnimations`.
+	void renderDrops(uint siteNum);
+
 	EEMEngine *_vm;
 	Mystery *_mystery;
 	bool _showHotspots = true;  ///< Toggle outlines with V key.


Commit: a5cefd687110f156ae779c6983b1bbf7c6b17010
    https://github.com/scummvm/scummvm/commit/a5cefd687110f156ae779c6983b1bbf7c6b17010
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:20:37+02:00

Commit Message:
EEM: implement clickable areas on maps

Changed paths:
    engines/eem/eem.cpp
    engines/eem/site.cpp


diff --git a/engines/eem/eem.cpp b/engines/eem/eem.cpp
index 414180c0e75..6143f54daf9 100644
--- a/engines/eem/eem.cpp
+++ b/engines/eem/eem.cpp
@@ -1632,6 +1632,11 @@ void EEMEngine::doBigMap() {
 
 	drawOverview();
 
+	// Static rectangles read directly from the binary at the labelled
+	// addresses (29be:0x1596 onwards). Format is {x1, y1, x2, y2}.
+	const Common::Rect kBigMapWindow   (  0,   0, 247, 192); // 29be:1596
+	const Common::Rect kSetupBtnRect   (252,   4, 315,  42); // 29be:15ce
+
 	bool wantZoom = false;
 	int  zoomX = 0, zoomY = 0;
 	while (!shouldQuit()) {
@@ -1644,19 +1649,27 @@ void EEMEngine::doBigMap() {
 				ev.kbd.keycode == Common::KEYCODE_ESCAPE)
 				return;
 			if (ev.type == Common::EVENT_LBUTTONDOWN) {
-				// `_DoBigMap @ 20fe:09e7` does NOT travel directly when
-				// the player clicks an overview icon ??? it always returns
-				// `(sx, sy) = (mouseX*2 - 0x74, mouseY*2 - 0x55)` so the
-				// caller can switch to the detail zoom centred there.
-				// Travel happens after a SECOND click in the detail view.
-				int sx = ev.mouse.x * 2;
-				int sy = ev.mouse.y * 2;
-				sx = (sx < 0x75) ? 0 : sx - 0x74;
-				sy = (sy < 0x56) ? 0 : sy - 0x55;
-				zoomX = sx;
-				zoomY = sy;
-				wantZoom = true;
-				break;
+				// SetupButtonRect ??? `_NextScreen = 6` (the original's
+				// settings screen). We use it as "back to menu":
+				// abandon the current mystery and return to case
+				// selection.
+				if (kSetupBtnRect.contains(ev.mouse.x, ev.mouse.y)) {
+					_mystery.clear();
+					_nextScreen = kScreenInvalid;
+					return;
+				}
+				// Click in the BigMapWindow ??? zoom. Original formula:
+				//   sx = mouseX*2 - 0x74; sy = mouseY*2 - 0x55
+				if (kBigMapWindow.contains(ev.mouse.x, ev.mouse.y)) {
+					int sx = ev.mouse.x * 2;
+					int sy = ev.mouse.y * 2;
+					sx = (sx < 0x75) ? 0 : sx - 0x74;
+					sy = (sy < 0x56) ? 0 : sy - 0x55;
+					zoomX = sx;
+					zoomY = sy;
+					wantZoom = true;
+					break;
+				}
 			}
 		}
 		if (wantZoom)
@@ -1791,10 +1804,67 @@ void EEMEngine::doBigMap() {
 				}
 			}
 			if (ev.type == Common::EVENT_LBUTTONDOWN) {
-				if (ev.mouse.x >= kMapWinX &&
-					ev.mouse.x < kMapWinX + kMapWinW &&
-					ev.mouse.y >= kMapWinY &&
-					ev.mouse.y < kMapWinY + kMapWinH) {
+				// Scroll arrows + slider rects live in `SmallMapButtons`
+				// at 29be:0x159e (six 8-byte rects in order: Y-up, Y-down,
+				// X-left, X-right, right-panel, top-right) plus the
+				// dedicated `XSliderRect @ 29be:15d6` and
+				// `YSliderRect @ 29be:15de`. Format {x1,y1,x2,y2}.
+				const Common::Rect kArrowYUp   (237,   2, 247,  11);
+				const Common::Rect kArrowYDown (237, 163, 247, 172);
+				const Common::Rect kArrowXLeft (  2, 175,  12, 185);
+				const Common::Rect kArrowXRight(224, 175, 234, 185);
+				const Common::Rect kXSlider    ( 15, 175, 221, 185);
+				const Common::Rect kYSlider    (237,  14, 247, 160);
+				const Common::Rect kSetupBtn   (252,   4, 315,  42);
+
+				const int kArrowStep = 16;
+				const int kSliderRange = mapW - kMapWinW;
+				const int kSliderRangeY = mapH - kMapWinH;
+
+				if (kSetupBtn.contains(ev.mouse.x, ev.mouse.y)) {
+					// Setup button on detail too ??? `_NextScreen = 6` in
+					// the original. We treat it the same way: bail back
+					// to case selection.
+					_mystery.clear();
+					_nextScreen = kScreenInvalid;
+					return;
+				}
+				if (kArrowYUp.contains(ev.mouse.x, ev.mouse.y)) {
+					scrollY = MAX<int>(0, scrollY - kArrowStep);
+					dirty = true;
+				} else if (kArrowYDown.contains(ev.mouse.x, ev.mouse.y)) {
+					scrollY = MIN<int>(MAX<int>(0, kSliderRangeY),
+						scrollY + kArrowStep);
+					dirty = true;
+				} else if (kArrowXLeft.contains(ev.mouse.x, ev.mouse.y)) {
+					scrollX = MAX<int>(0, scrollX - kArrowStep);
+					dirty = true;
+				} else if (kArrowXRight.contains(ev.mouse.x, ev.mouse.y)) {
+					scrollX = MIN<int>(MAX<int>(0, kSliderRange),
+						scrollX + kArrowStep);
+					dirty = true;
+				} else if (kXSlider.contains(ev.mouse.x, ev.mouse.y)) {
+					// Click on X slider track ??? jump scrollX so the
+					// click position maps proportionally into the map.
+					if (kSliderRange > 0) {
+						const int t = ev.mouse.x - kXSlider.left;
+						const int tw = kXSlider.width();
+						scrollX = MAX<int>(0, MIN<int>(kSliderRange,
+							t * kSliderRange / MAX<int>(1, tw)));
+						dirty = true;
+					}
+				} else if (kYSlider.contains(ev.mouse.x, ev.mouse.y)) {
+					if (kSliderRangeY > 0) {
+						const int t = ev.mouse.y - kYSlider.top;
+						const int th = kYSlider.height();
+						scrollY = MAX<int>(0, MIN<int>(kSliderRangeY,
+							t * kSliderRangeY / MAX<int>(1, th)));
+						dirty = true;
+					}
+				} else if (ev.mouse.x >= kMapWinX &&
+						   ev.mouse.x < kMapWinX + kMapWinW &&
+						   ev.mouse.y >= kMapWinY &&
+						   ev.mouse.y < kMapWinY + kMapWinH) {
 					// Hit-test the per-site button at its actual bbox
 					// (`_StampButtons` records the rect at SmallMap +8/+0xa
 					// with the button PIC's width/height).
diff --git a/engines/eem/site.cpp b/engines/eem/site.cpp
index 2250b7523ce..49f7340ac2c 100644
--- a/engines/eem/site.cpp
+++ b/engines/eem/site.cpp
@@ -145,6 +145,31 @@ void SiteScreen::run() {
 				return;
 
 			case Common::EVENT_LBUTTONDOWN: {
+				// On-screen UI buttons. `_DoSiteLoop @ 168d:03f4` calls
+				//   _FindButton(&SiteButtons, 2, MouseX, MouseY)
+				// where `SiteButtons` is two 8-byte rectangles at
+				// 29be:0x274 (verified via 168d:0729-0848):
+				//   Button 0: (35, 111) - (56, 136)  ??? notebook
+				//                                       (`_NextScreen = 4`)
+				//   Button 1: (7, 177)  - (57, 200)  ??? map
+				//                                       (`_NextScreen = 1`)
+				// Test the buttons before falling through to hotspots so
+				// a click on the PDA / map icon doesn't accidentally
+				// trigger a hotspot underneath.
+				const Common::Rect kBtnNotebook(35, 111, 56, 136);
+				const Common::Rect kBtnMap     ( 7, 177, 57, 200);
+				if (kBtnNotebook.contains(event.mouse.x, event.mouse.y)) {
+					_vm->doNotebook();
+					enter(cur);
+					break;
+				}
+				if (kBtnMap.contains(event.mouse.x, event.mouse.y)) {
+					_vm->doBigMap();
+					if (_mystery->_siteNumber < _mystery->numSites())
+						cur = _mystery->_siteNumber;
+					enter(cur);
+					break;
+				}
 				const int idx = hotspotAtPoint(cur, event.mouse.x, event.mouse.y);
 				if (idx >= 0) {
 					onHotspotClicked(cur, (uint)idx);


Commit: 0fe0c95dfdbdb01b029c42871173348e6b5ccf9f
    https://github.com/scummvm/scummvm/commit/0fe0c95dfdbdb01b029c42871173348e6b5ccf9f
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:20:37+02:00

Commit Message:
EEM: improved NPC handling

Changed paths:
    engines/eem/site.cpp
    engines/eem/site.h


diff --git a/engines/eem/site.cpp b/engines/eem/site.cpp
index 49f7340ac2c..30f97bc29c0 100644
--- a/engines/eem/site.cpp
+++ b/engines/eem/site.cpp
@@ -73,16 +73,21 @@ void SiteScreen::enter(uint siteNum) {
 		renderBackground(siteNum);
 	}
 
-	// Per-site NPCs / decorative animations (`_DoSiteLoop` reads
-	// `siteData[+0xa]` as the drop count and iterates 6-byte entries
-	// from `siteData[+0x48]`). Drawn before the partner sprite so
-	// the partner appears on top of any background NPCs.
-	renderDrops(siteNum);
-
-	// Persistent partner sprite (`_NewAnimation` at the tail of
-	// `_DoSiteLoop`). Drawn after the BG so the hotspot outlines and
-	// HUD that follow stay on top of it.
-	renderPartner(siteNum);
+	// Static drops (Loop 2 from `_DoSiteLoop`) ??? no animation, baked
+	// into the BG snapshot the run() pump uses to restore.
+	renderStaticDrops(siteNum);
+
+	// Snapshot the static layers so per-tick animation re-blits don't
+	// have to re-load PIC 0x43, the SITES.DBD scene, or each
+	// `_AddDrop` PIC every frame.
+	captureBgSnapshot();
+	_snapshotSite = (int)siteNum;
+
+	// Animated NPCs (Loop 1) and the persistent partner sit on top of
+	// the snapshot. Initial frame goes at tickMs=now.
+	const uint32 now = g_system->getMillis();
+	renderAnimatedDrops(siteNum, now);
+	renderPartner(siteNum, now);
 
 	renderHotspots(siteNum);
 	g_system->updateScreen();
@@ -110,10 +115,14 @@ void SiteScreen::enter(uint siteNum) {
 			_mystery->_visitedSite[siteNum] = 1;
 		// The dialog overlay will have left the screen with portrait /
 		// balloon residues; refresh the site so the player returns to
-		// a clean state.
+		// a clean state. Re-build the snapshot too.
 		renderBackground(siteNum);
-		renderDrops(siteNum);
-		renderPartner(siteNum);
+		renderStaticDrops(siteNum);
+		captureBgSnapshot();
+		_snapshotSite = (int)siteNum;
+		const uint32 nowAfter = g_system->getMillis();
+		renderAnimatedDrops(siteNum, nowAfter);
+		renderPartner(siteNum, nowAfter);
 		renderHotspots(siteNum);
 		g_system->updateScreen();
 	} else if (siteNum < Mystery::kVisitedSiteCap) {
@@ -275,6 +284,21 @@ void SiteScreen::run() {
 		}
 		if (exitRequested)
 			return;
+
+		// Per-tick frame pump (mirrors `_CheckFrameRate` +
+		// `_UpdateAnimations` at the top of `_DoSiteLoop`'s main loop).
+		// Restore the static BG snapshot, redraw animated NPCs +
+		// partner at the current frame, then re-render hotspots on
+		// top. We tick at 100 ms (~10 FPS) which is in the same ball
+		// park as the original.
+		const uint32 now = g_system->getMillis();
+		if (_snapshotSite == (int)cur && now - _lastTickMs >= 100) {
+			restoreBgSnapshot();
+			renderAnimatedDrops(cur, now);
+			renderPartner(cur, now);
+			renderHotspots(cur);
+			_lastTickMs = now;
+		}
 		g_system->updateScreen();
 		g_system->delayMillis(10);
 	}
@@ -415,93 +439,131 @@ void SiteScreen::enterSiteAnim() {
 	}
 }
 
-void SiteScreen::renderDrops(uint siteNum) {
-	// `_DoSiteLoop @ 168d:03f4` runs TWO per-site loops, both feeding
-	// the visible NPCs / decorations on the site BG:
-	//
-	//   Loop 1 (animations / animated NPCs)
-	//     bound: siteData[+0xa]
-	//     per entry at siteData[+0x48 + i*6]:  {animId, x, y}
-	//     if animId == -1: a `_ColorCycle(x, y)` palette range each tick
-	//     else: `_GetAnimation(animId)` + `_NewAnimation(x, y, ...)` ???
-	//           added inactive (arg5=0), updated by `_UpdateAnimations`.
-	//
-	//   Loop 2 (static drops)
-	//     bound: siteData[+0x4]  (verified at 168d:05c0:
-	//            `MOV ES:[BX+0x4], DI; CMP ES:[BX+0x4], DI`)
-	//     per entry at siteData[+0xc + i*6]:    {picId, x, y}
-	//     each ??? `_AddDrop(picId, x, y)` which loads PIC `picId-1`
-	//     from PICS.DBD and blits it at (x, y) onto offscreen 32000.
-	//
-	// Both contribute to the visible scene. We render Loop 2 statics
-	// directly and Loop 1's first frame as a static sprite (per-tick
-	// animation cycling is still TODO).
+// Mask-aware blit from a Picture into a Graphics::Surface.
+static void blitMaskedSurface(Graphics::Surface *screen,
+							  const Picture &p, int x, int y) {
+	if (!screen)
+		return;
+	const byte transp = (byte)(p.flags >> 8);
+	for (int row = 0; row < p.surface.h; row++) {
+		const int dstY = y + row;
+		if (dstY < 0 || dstY >= screen->h) continue;
+		const byte *src = (const byte *)p.surface.getBasePtr(0, row);
+		byte *dst = (byte *)screen->getBasePtr(0, dstY);
+		for (int col = 0; col < p.surface.w; col++) {
+			const int dstX = x + col;
+			if (dstX < 0 || dstX >= screen->w) continue;
+			if (src[col] != transp)
+				dst[dstX] = src[col];
+		}
+	}
+}
+
+void SiteScreen::renderStaticDrops(uint siteNum) {
+	// Loop 2 from `_DoSiteLoop @ 168d:03f4`:
+	//   bound: siteData[+0x4]   (verified at 168d:05c0:
+	//          `MOV ES:[BX+0x4], DI; CMP ES:[BX+0x4], DI`)
+	//   per entry at siteData[+0xc + i*6]: {picId, x, y}
+	//   each ??? `_AddDrop(picId, x, y)` (`_AddDrop @ 172b:1a77`):
+	//   loads PIC `picId-1` from PICS.DBD and blits with miscflags
+	//   high-byte as the transparent colour. These NEVER cycle, so
+	//   they belong to the BG snapshot.
 	if (!_mystery)
 		return;
 	const byte *site = _mystery->siteData(siteNum);
 	if (!site)
 		return;
 	const uint16 numStatic = READ_LE_UINT16(site + 0x4);
-	const uint16 numAnims  = READ_LE_UINT16(site + 0xa);
+	if (numStatic == 0 || numStatic > 16)
+		return;
 
 	Graphics::Surface *screen = g_system->lockScreen();
 	if (!screen)
 		return;
 
-	auto blitMasked = [&](const Picture &p, int x, int y) {
-		const byte transp = (byte)(p.flags >> 8);
-		for (int row = 0; row < p.surface.h; row++) {
-			const int dstY = y + row;
-			if (dstY < 0 || dstY >= screen->h) continue;
-			const byte *src = (const byte *)p.surface.getBasePtr(0, row);
-			byte *dst = (byte *)screen->getBasePtr(0, dstY);
-			for (int col = 0; col < p.surface.w; col++) {
-				const int dstX = x + col;
-				if (dstX < 0 || dstX >= screen->w) continue;
-				if (src[col] != transp)
-					dst[dstX] = src[col];
-			}
-		}
-	};
-
-	// Loop 2 ??? `_AddDrop(picId, x, y)`. PIC IDs are 1-based per
-	// `_AddDrop @ 172b:1a77` (it does `_GetFromDB(.., number - 1)`).
-	if (numStatic > 0 && numStatic <= 16) {
-		for (uint i = 0; i < numStatic; i++) {
-			const uint dropOff = 0xc + i * 6;
-			const uint16 picId = READ_LE_UINT16(site + dropOff + 0);
-			const int16  x     = (int16)READ_LE_UINT16(site + dropOff + 2);
-			const int16  y     = (int16)READ_LE_UINT16(site + dropOff + 4);
-			if (picId == 0)
-				continue;
-			Picture pic;
-			if (!_vm->getPics().getPicture(picId, pic))
-				continue;
-			blitMasked(pic, x, y);
-		}
+	for (uint i = 0; i < numStatic; i++) {
+		const uint dropOff = 0xc + i * 6;
+		const uint16 picId = READ_LE_UINT16(site + dropOff + 0);
+		const int16  x     = (int16)READ_LE_UINT16(site + dropOff + 2);
+		const int16  y     = (int16)READ_LE_UINT16(site + dropOff + 4);
+		if (picId == 0)
+			continue;
+		Picture pic;
+		if (!_vm->getPics().getPicture(picId, pic))
+			continue;
+		blitMaskedSurface(screen, pic, x, y);
 	}
 
-	// Loop 1 ??? animation drops. Use frame 0 as a static placeholder
-	// for non-`-1` entries.
-	if (numAnims > 0 && numAnims <= 16) {
-		for (uint i = 0; i < numAnims; i++) {
-			const uint dropOff = 0x48 + i * 6;
-			const int16 animId = (int16)READ_LE_UINT16(site + dropOff + 0);
-			if (animId < 0)
-				continue; // -1 ??? ColorCycle entry, handled per tick
-			const int16 x = (int16)READ_LE_UINT16(site + dropOff + 2);
-			const int16 y = (int16)READ_LE_UINT16(site + dropOff + 4);
-			Animation anim;
-			if (!_vm->getAni().loadAnimation((uint)animId, anim) || anim.empty())
-				continue;
-			blitMasked(anim[0], x, y);
-		}
+	g_system->unlockScreen();
+}
+
+void SiteScreen::renderAnimatedDrops(uint siteNum, uint32 tickMs) {
+	// Loop 1 from `_DoSiteLoop @ 168d:03f4`:
+	//   bound: siteData[+0xa]
+	//   per entry at siteData[+0x48 + i*6]: {animId, x, y}
+	//   animId == -1 ??? `_ColorCycle(x, y)` palette range (handled
+	//                  in the run() loop's frame pump as palette
+	//                  rotation; not yet implemented).
+	//   else ??? `_GetAnimation(animId)` + `_NewAnimation` then
+	//          `_UpdateAnimations @ 172b:09c1` walks a sequence
+	//          script (entries are frame indices; 0x80 = end-of-loop,
+	//          0x81 = jump command). We don't have the sequence-script
+	//          structure decoded yet, so for now we cycle through the
+	//          raw animation frames in order using a global tick.
+	if (!_mystery)
+		return;
+	const byte *site = _mystery->siteData(siteNum);
+	if (!site)
+		return;
+	const uint16 numAnims = READ_LE_UINT16(site + 0xa);
+	if (numAnims == 0 || numAnims > 16)
+		return;
+
+	Graphics::Surface *screen = g_system->lockScreen();
+	if (!screen)
+		return;
+
+	const uint32 kFramePeriodMs = 100; // ~10 FPS, in line with `_CheckFrameRate`.
+
+	for (uint i = 0; i < numAnims; i++) {
+		const uint dropOff = 0x48 + i * 6;
+		const int16 animId = (int16)READ_LE_UINT16(site + dropOff + 0);
+		if (animId < 0)
+			continue;
+		const int16 x = (int16)READ_LE_UINT16(site + dropOff + 2);
+		const int16 y = (int16)READ_LE_UINT16(site + dropOff + 4);
+		Animation anim;
+		if (!_vm->getAni().loadAnimation((uint)animId, anim) || anim.empty())
+			continue;
+		const uint frameIdx = (uint)((tickMs / kFramePeriodMs) % anim.size());
+		blitMaskedSurface(screen, anim[frameIdx], x, y);
 	}
 
 	g_system->unlockScreen();
 }
 
-void SiteScreen::renderPartner(uint siteNum) {
+void SiteScreen::captureBgSnapshot() {
+	_bgSnapshot.create(320, 200, Graphics::PixelFormat::createFormatCLUT8());
+	Graphics::Surface *screen = g_system->lockScreen();
+	if (!screen) {
+		_snapshotSite = -1;
+		return;
+	}
+	for (int row = 0; row < 200; row++) {
+		memcpy((byte *)_bgSnapshot.getBasePtr(0, row),
+			   (const byte *)screen->getBasePtr(0, row), 320);
+	}
+	g_system->unlockScreen();
+}
+
+void SiteScreen::restoreBgSnapshot() {
+	if (_bgSnapshot.w != 320 || _bgSnapshot.h != 200)
+		return;
+	g_system->copyRectToScreen(_bgSnapshot.getPixels(), _bgSnapshot.pitch,
+							   0, 0, 320, 200);
+}
+
+void SiteScreen::renderPartner(uint siteNum, uint32 tickMs) {
 	// `_DoSiteLoop @ 168d:03f4` reads `siteData[+8]` as the speaker
 	// table index, then for each (speaker ?? partner) loads
 	//   anim  = WaitAnims[speakerIdx].anim[partner]
@@ -543,28 +605,16 @@ void SiteScreen::renderPartner(uint siteNum) {
 	if (!_vm->getAni().loadAnimation(animId, anim) || anim.empty())
 		return;
 
-	// Show the first frame as a static sprite. The original updates it
-	// each `_CheckFrameRate` tick; we don't have a frame pump in the
-	// site loop yet so a static pose is enough for now.
-	const Picture &fr = anim[0];
-	const byte transp = (byte)(fr.flags >> 8);
+	// `_UpdateAnimations @ 172b:09c1` advances the partner's frame
+	// every `_CheckFrameRate` tick. We pick the frame from a global
+	// 100 ms clock so the partner cycles in sync with the animated
+	// drops.
+	const uint32 kFramePeriodMs = 100;
+	const uint frameIdx = (uint)((tickMs / kFramePeriodMs) % anim.size());
 	Graphics::Surface *screen = g_system->lockScreen();
 	if (!screen)
 		return;
-	for (int row = 0; row < fr.surface.h; row++) {
-		const int dstY = y + row;
-		if (dstY < 0 || dstY >= 200)
-			continue;
-		const byte *src = (const byte *)fr.surface.getBasePtr(0, row);
-		byte *dst = (byte *)screen->getBasePtr(0, dstY);
-		for (int col = 0; col < fr.surface.w; col++) {
-			const int dstX = x + col;
-			if (dstX < 0 || dstX >= 320)
-				continue;
-			if (src[col] != transp)
-				dst[dstX] = src[col];
-		}
-	}
+	blitMaskedSurface(screen, anim[frameIdx], x, y);
 	g_system->unlockScreen();
 }
 
diff --git a/engines/eem/site.h b/engines/eem/site.h
index 125a6cd853a..bae8dc53cc6 100644
--- a/engines/eem/site.h
+++ b/engines/eem/site.h
@@ -25,6 +25,8 @@
 #include "common/rect.h"
 #include "common/scummsys.h"
 
+#include "graphics/managed_surface.h"
+
 namespace EEM {
 
 class EEMEngine;
@@ -74,22 +76,39 @@ private:
 	/// Draw the persistent in-site partner sprite (Jake or Jenny
 	/// standing/idling) at the position from `_WaitAnims` @ 29be:021c.
 	/// Mirrors the `_GetAnimation` + `_NewAnimation` block at the tail
-	/// of `_DoSiteLoop @ 168d:03f4`.
-	void renderPartner(uint siteNum);
-
-	/// Draw the per-site NPC "drops" (the locals you click on to
-	/// trigger a clue). `_DoSiteLoop` reads `siteData[+0xa]` as the
-	/// drop count, then iterates 6-byte entries at `siteData[+0x48]`:
-	///   {anim_id (-1 = ColorCycle), x, y}.
-	/// Each non-(-1) entry is a `_NewAnimation(x, y, animId)`. We blit
-	/// the first frame as a static sprite; the original cycles via
-	/// `_UpdateAnimations`.
-	void renderDrops(uint siteNum);
+	/// of `_DoSiteLoop @ 168d:03f4`. `tickMs` selects which frame of
+	/// the partner's animation to render; in the original, frames
+	/// advance per `_CheckFrameRate` tick via `_UpdateAnimations`.
+	void renderPartner(uint siteNum, uint32 tickMs);
+
+	/// Draw the per-site `_AddDrop` static decorations (Loop 2).
+	/// `_DoSiteLoop` runs this loop with bound siteData[+0x4] and
+	/// 6-byte entries at siteData[+0xc]: {picId, x, y}. These never
+	/// animate so they go in the BG snapshot.
+	void renderStaticDrops(uint siteNum);
+
+	/// Draw the per-site animated NPCs (Loop 1) at the current tick.
+	/// `_DoSiteLoop` registers each via `_NewAnimation` (siteData[+0xa]
+	/// entries at siteData[+0x48]: {animId (-1 = ColorCycle), x, y})
+	/// and `_UpdateAnimations @ 172b:09c1` advances frame indices each
+	/// tick. We use a millis-based frame index so all NPCs cycle in
+	/// step with the global clock.
+	void renderAnimatedDrops(uint siteNum, uint32 tickMs);
+
+	/// Snapshot the post-BG, post-static-drops screen so the per-tick
+	/// frame pump can restore it without reloading PICS.DBD entries.
+	void captureBgSnapshot();
+
+	/// Restore the snapshot taken at `captureBgSnapshot` time.
+	void restoreBgSnapshot();
 
 	EEMEngine *_vm;
 	Mystery *_mystery;
-	bool _showHotspots = true;  ///< Toggle outlines with V key.
-	int _lastSiteAnim = -1;     ///< Last site we played the arrival on.
+	bool _showHotspots = true;     ///< Toggle outlines with V key.
+	int _lastSiteAnim = -1;        ///< Last site we played the arrival on.
+	int _snapshotSite = -1;        ///< Site number the snapshot belongs to.
+	Graphics::ManagedSurface _bgSnapshot;
+	uint32 _lastTickMs = 0;        ///< Last frame-pump tick in ms.
 };
 
 } // End of namespace EEM


Commit: ca825ec5b1b8e203da3defda1275ba22bd0e07c3
    https://github.com/scummvm/scummvm/commit/ca825ec5b1b8e203da3defda1275ba22bd0e07c3
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:20:38+02:00

Commit Message:
EEM: added more features to the PDA

Changed paths:
    engines/eem/eem.cpp
    engines/eem/eem.h
    engines/eem/site.cpp
    engines/eem/site.h


diff --git a/engines/eem/eem.cpp b/engines/eem/eem.cpp
index 6143f54daf9..578f3290a6f 100644
--- a/engines/eem/eem.cpp
+++ b/engines/eem/eem.cpp
@@ -987,10 +987,23 @@ void EEMEngine::doCaseSelection() {
 }
 
 void EEMEngine::doInitClues() {
-	// Mirrors `_DoInitClues` @ 1a35:0411. Sets BG 0x52 + palette 0x22,
-	// blits the Goblindroid game and book first frames, then displays
-	// the case briefing ClueBlock at InitBlock + 4. Marks the starting
-	// site (InitBlock word[1]) on `_OnSites`.
+	// Mirrors `_DoInitClues` @ 1a35:0411. The original does:
+	//   1. _AllBlack(); _GetBackground(0x52); _GetPalette(0x22);
+	//   2. _GetAnimation(gameAni); _NewAnimation(0xcd, 0x6c, ...)
+	//      _GetAnimation(bookAni); _NewAnimation(0,    99,   ...)
+	//      (case type 1 also: _NewAnimation(0x68, 0x8b, nancyAni))
+	//   3. _UpdateAnimations(); _FadeIn();
+	//   4. while (frame != gameNum) { _CheckFrameRate(); _UpdateAnimations(); }
+	//        ??? cycles through the entire game animation once. Click skips.
+	//   5. _PlayInSequence(seqId, ...) ??? plays a follow-up sequence based
+	//      on partner + case type.
+	//   6. _DisplayClue(InitBlock + 2, 1) ??? the briefing dialogue.
+	//   7. _OnSites[startSite] = 1.
+	//
+	// gameAni / bookAni / nancyAni values verified directly from Ghidra:
+	//   gameAni  = 0x17 (Jake) / 0x3b (Jenny)
+	//   bookAni  = 0x18 (Jake) / 0x3c (Jenny)
+	//   nancyAni = 0x19 (case type 1 only)
 	if (!_mystery.isLoaded())
 		return;
 
@@ -1001,11 +1014,6 @@ void EEMEngine::doInitClues() {
 	const uint16 startSite = READ_LE_UINT16(ib + 2);
 	if (startSite < Mystery::kVisitedSiteCap)
 		_mystery._onSites[startSite] = 1;
-	// Mirror the original: at briefing time the player isn't actually
-	// at any site yet ??? they pick from the map next. Set _siteNumber
-	// to the start site so the map opens centred on the only initially
-	// accessible location and the post-map site loop has a sensible
-	// resume point.
 	_mystery._siteNumber = startSite;
 	_mystery._lastSite = startSite;
 
@@ -1016,21 +1024,82 @@ void EEMEngine::doInitClues() {
 
 	const uint gameAni = _partner == 0 ? 0x17 : 0x3b;
 	const uint bookAni = _partner == 0 ? 0x18 : 0x3c;
-	Animation game, book;
-	if (_aniArchive.loadAnimation(gameAni, game) && !game.empty())
-		blitAt(game[0], 0xcd, 0x6c);
-	if (_aniArchive.loadAnimation(bookAni, book) && !book.empty())
-		blitAt(book[0], 0, 99);
-
-	// Case type 1 also places "Nancy" (a third character) at (0x68, 0x8b)
-	// per `_DoInitClues`.
+	Animation game, book, nancy;
+	const bool haveGame  = _aniArchive.loadAnimation(gameAni, game) && !game.empty();
+	const bool haveBook  = _aniArchive.loadAnimation(bookAni, book) && !book.empty();
+
 	const uint16 caseType = READ_LE_UINT16(ib);
-	if (caseType == 1) {
-		Animation nancy;
-		if (_aniArchive.loadAnimation(0x19, nancy) && !nancy.empty())
-			blitAt(nancy[0], 0x68, 0x8b);
+	const bool haveNancy = (caseType == 1)
+						  && _aniArchive.loadAnimation(0x19, nancy)
+						  && !nancy.empty();
+
+	auto blitMaskedAt = [&](const Picture &p, int x, int y) {
+		const byte transp = (byte)(p.flags >> 8);
+		Graphics::Surface *screen = g_system->lockScreen();
+		if (!screen) return;
+		for (int row = 0; row < p.surface.h; row++) {
+			const int dstY = y + row;
+			if (dstY < 0 || dstY >= screen->h) continue;
+			const byte *src = (const byte *)p.surface.getBasePtr(0, row);
+			byte *dst = (byte *)screen->getBasePtr(0, dstY);
+			for (int col = 0; col < p.surface.w; col++) {
+				const int dstX = x + col;
+				if (dstX < 0 || dstX >= screen->w) continue;
+				if (src[col] != transp)
+					dst[dstX] = src[col];
+			}
+		}
+		g_system->unlockScreen();
+	};
+
+	// Step 4 ??? cycle through the game animation once before the briefing.
+	// Mirrors the `while (uVar9 != gameNum)` loop. The original calls
+	// `_UpdateAnimations` per `_CheckFrameRate` tick (~10 fps). We use
+	// 100 ms ticks for the same cadence. Click / key skips.
+	if (haveGame || haveBook || haveNancy) {
+		const uint frameCount = haveGame ? game.size() : 8;
+		bool skip = false;
+		for (uint frame = 0; frame < frameCount && !shouldQuit() && !skip; frame++) {
+			// Restore BG + advance frame.
+			if (_picsArchive.getPicture(0x52, bg))
+				blitAt(bg, 0, 0);
+			if (haveGame)
+				blitMaskedAt(game[frame % game.size()], 0xcd, 0x6c);
+			if (haveBook)
+				blitMaskedAt(book[frame % book.size()], 0, 99);
+			if (haveNancy)
+				blitMaskedAt(nancy[frame % nancy.size()], 0x68, 0x8b);
+			g_system->updateScreen();
+
+			// Wait 100 ms or until input.
+			const uint32 wakeup = g_system->getMillis() + 100;
+			while (g_system->getMillis() < wakeup && !shouldQuit() && !skip) {
+				Common::Event ev;
+				while (g_system->getEventManager()->pollEvent(ev)) {
+					if (ev.type == Common::EVENT_LBUTTONDOWN ||
+						ev.type == Common::EVENT_KEYDOWN) {
+						skip = true;
+						break;
+					}
+				}
+				g_system->delayMillis(10);
+			}
+		}
 	}
 
+	// Composite the final frames (or first frames if skipped) so the BG
+	// is in a sensible state when displayClue overlays the speaker.
+	if (_picsArchive.getPicture(0x52, bg))
+		blitAt(bg, 0, 0);
+	if (haveGame)
+		blitMaskedAt(game[0], 0xcd, 0x6c);
+	if (haveBook)
+		blitMaskedAt(book[0], 0, 99);
+	if (haveNancy)
+		blitMaskedAt(nancy[0], 0x68, 0x8b);
+	g_system->updateScreen();
+
+	// Step 6 ??? case briefing dialogue.
 	displayClue(ib + 4);
 }
 
@@ -1269,14 +1338,39 @@ void EEMEngine::displayClue(const byte *clueBlock) {
 						}
 					}
 				}
-				// Per-balloon metadata table at 29be:0875 ??? 10-byte
-				// entries indexed by `(bubNum & 0x7f)`. Layout:
-				//   +0..1 textX inset, +2..3 textY inset, +4..5 textWidth.
-				// All entries use textX=6, textY=4 so we hard-code those
-				// constants; textWidth is read live from the table.
-				textX = bubX + 6;
-				textY = bubY + 4;
-				textW = bw - 12;
+				// Per-balloon metadata table verified from 29be:0875 ???
+				// 10-byte entries indexed by `(bubNum & 0x7f)`. Layout:
+				//   +0..1 textX inset, +2..3 textY inset, +4..5 width,
+				//   +6..7 height, +8..9 tail offset.
+				// 52 entries total; insets vary (3, 5, 6, or 8 px).
+				// The original `_DisplayClue` does:
+				//   _WordWrap(bubX + table[bubNum].x, bubY + table[bubNum].y,
+				//             table[bubNum].w, ...);
+				static const struct { uint16 x, y, w; } kBalloonTable[] = {
+					{ 6, 4, 142 }, { 6, 4, 142 }, { 6, 4, 142 }, { 6, 4, 142 },
+					{ 6, 4, 142 }, { 6, 4, 142 }, { 6, 4, 142 },
+					{ 6, 4, 224 }, { 6, 4, 224 }, { 6, 4, 224 }, { 6, 4, 224 },
+					{ 6, 4, 224 }, { 6, 4, 224 }, { 6, 4, 224 },
+					{ 6, 4, 291 }, { 6, 4, 291 }, { 6, 4, 291 }, { 6, 4, 291 },
+					{ 6, 4, 291 }, { 6, 4, 291 }, { 6, 4, 291 },
+					{ 5, 4, 155 }, { 5, 4, 155 }, { 5, 4, 155 }, { 5, 4, 155 },
+					{ 5, 4, 155 }, { 5, 4, 155 }, { 5, 4, 155 },
+					{ 5, 4, 237 }, { 5, 4, 237 }, { 5, 4, 237 }, { 5, 4, 237 },
+					{ 5, 4, 237 }, { 5, 4, 237 }, { 5, 4, 237 },
+					{ 3, 4, 155 }, { 3, 4, 155 }, { 3, 4, 155 }, { 3, 4, 155 },
+					{ 3, 4, 155 }, { 3, 4, 155 }, { 3, 4, 155 },
+					{ 5, 4, 238 }, { 5, 4, 238 }, { 5, 4, 238 }, { 5, 4, 238 },
+					{ 5, 4, 238 }, { 5, 4, 238 }, { 5, 4, 238 },
+					{ 5, 8, 158 }, { 5, 8, 176 }, { 8, 7, 142 }
+				};
+				const uint kBalloonTableSize = sizeof(kBalloonTable) /
+											   sizeof(kBalloonTable[0]);
+				const uint balloonIdx = balloonId < kBalloonTableSize
+										? balloonId : 0;
+				const auto &bm = kBalloonTable[balloonIdx];
+				textX = bubX + bm.x;
+				textY = bubY + bm.y;
+				textW = bm.w;
 				copyH = bh;
 			} else {
 				// No balloon ??? clear a band so old pixels don't bleed.
@@ -1344,109 +1438,340 @@ void EEMEngine::displayClue(const byte *clueBlock) {
 }
 
 void EEMEngine::doNotebook() {
-	// Mirrors `_DrawNotes` @ 161e:01d0 + `_HandleNoteButton`. We list every
-	// found clue with its NoteIndex point value and let the player toggle
-	// "selected" with number keys 1..9 (paged in groups of 9). The total
-	// points of selected clues feed `_SolvedCheck` during accuse.
-	if (!_font.isLoaded())
+	// Mirrors `_DoNotebook @ 161e:0500` + `_DrawNotes @ 161e:01d0` +
+	// `_HandleNoteButton @ 161e:03cb`.
+	//
+	// Layout (verified from Ghidra labels in 29be:013f / 29be:0147):
+	//   _NotebookRect = (78, 12, 288, 152)   ??? note display rectangle.
+	//   _NoteButtons (11 entries, 8 bytes each, at 29be:0147):
+	//     [0]  (134, 174, 155, 190)  decorative ??? `_HandleNoteButton(0)`
+	//                                returns immediately (i-1 unsigned > 9).
+	//     [1]  (93,  174, 115, 190)  ??? `_InterfaceHelp(0)` (handler 0x3f9)
+	//     [2]  (157, 174, 178, 190)  ??? handler 0x477   (page nav)
+	//     [3]  (5,   80,  44, 110)   ??? `_KDHelp` (host hint, 0x403)
+	//     [4]  (180, 174, 201, 190)  ??? solve / accuse  (0x436)
+	//     [5]  (204, 174, 224, 190)  ??? `_NextScreen = 5` (gallery, 0x489)
+	//     [6]  (226, 174, 247, 190)  ??? handler 0x4ab
+	//     [7]  (7,   177,  57, 200)  ??? handler 0x480   (back to map)
+	//     [8]  (35,  111,  56, 136)  ??? `_NextScreen = 3` (site)
+	//     [9]  (0, 0, 0, 0)          ??? same exit as [8]
+	//     [10] (66,  79, 267, 174)   ??? `_InterfaceHelp(0)` (note area)
+	//   Background: PIC 0x3f.
+	//   Partner anim: anim 1 (Jake) / 0xb (Jenny) at (5, 80).
+	if (!_mystery.isLoaded() || !_font.isLoaded())
 		return;
 
+	const Common::Rect kNotebookRect(78, 12, 288, 152);
+	const Common::Rect kBtnHelp1   ( 93, 174, 115, 190);  // [1]
+	const Common::Rect kBtnPagePrev(157, 174, 178, 190);  // [2]
+	const Common::Rect kBtnPartner (  5,  80,  44, 110);  // [3]
+	const Common::Rect kBtnAccuse  (180, 174, 201, 190);  // [4]
+	const Common::Rect kBtnGallery (204, 174, 224, 190);  // [5]
+	const Common::Rect kBtnPageNext(226, 174, 247, 190);  // [6]
+	const Common::Rect kBtnMap     (  7, 177,  57, 200);  // [7]
+	const Common::Rect kBtnSite    ( 35, 111,  56, 136);  // [8]
+	const Common::Rect kNoteArea   ( 66,  79, 267, 174);  // [10]
+	(void)kBtnHelp1; (void)kBtnPagePrev; (void)kBtnPageNext;
+
+	CursorMan.showMouse(true);
+
 	int page = 0;
-	const int kPerPage = 9;
+	int hoveredNoteSlot = -1;
+
+	// Build a list of found-clue indices, identical ordering to the
+	// original's iteration through `_CluesFound[]`.
+	auto buildFound = [&]() {
+		Common::Array<uint> found;
+		for (uint i = 0; i < Mystery::kCluesFoundCap; i++)
+			if (_mystery._cluesFound[i])
+				found.push_back(i);
+		return found;
+	};
 
 	auto draw = [&]() {
 		Graphics::ManagedSurface scratch(320, 200,
 			Graphics::PixelFormat::createFormatCLUT8());
 		scratch.clear();
-		_font.drawString(&scratch, "NOTEBOOK", 8, 4, 320, 0xF);
-		_font.drawString(&scratch, Common::String::format("pts: %d", _mystery.selectedPoints()), 200, 4, 320, 0xF);
 
-		// Build a list of found-clue indices.
-		Common::Array<uint> found;
-		for (uint i = 0; i < Mystery::kCluesFoundCap; i++)
-			if (_mystery._cluesFound[i])
-				found.push_back(i);
-		const int total = (int)found.size();
-		const int pages = MAX<int>(1, (total + kPerPage - 1) / kPerPage);
-		page = MIN<int>(page, pages - 1);
+		// PIC 0x3f frame.
+		Picture frame;
+		if (_picsArchive.getPicture(0x3f, frame)) {
+			const int w = MIN<int>(frame.surface.w, 320);
+			const int h = MIN<int>(frame.surface.h, 200);
+			for (int row = 0; row < h; row++)
+				memcpy((byte *)scratch.getBasePtr(0, row),
+					   (const byte *)frame.surface.getBasePtr(0, row), w);
+		}
 
-		_font.drawString(&scratch, Common::String::format("page %d/%d", page + 1, pages), 200, 16, 320, 0xF);
+		// Partner sprite at (5, 80). Anim 1 for Jake, 0xb (11) for Jenny.
+		const uint partnerAnim = (_partner == 0) ? 1 : 0xb;
+		Animation partnerAni;
+		if (_aniArchive.loadAnimation(partnerAnim, partnerAni) && !partnerAni.empty()) {
+			const uint32 now = g_system->getMillis();
+			const uint frameIdx = (uint)((now / 100) % partnerAni.size());
+			const Picture &fr = partnerAni[frameIdx];
+			const byte transp = (byte)(fr.flags >> 8);
+			for (int row = 0; row < fr.surface.h; row++) {
+				const int dstY = 80 + row;
+				if (dstY < 0 || dstY >= 200) continue;
+				const byte *src = (const byte *)fr.surface.getBasePtr(0, row);
+				byte *dst = (byte *)scratch.getBasePtr(0, dstY);
+				for (int col = 0; col < fr.surface.w; col++) {
+					const int dstX = 5 + col;
+					if (dstX < 0 || dstX >= 320) continue;
+					if (src[col] != transp)
+						dst[dstX] = src[col];
+				}
+			}
+		}
 
+		// Notes ??? `_DrawNotes` walks `_NoteIndex` for the current page,
+		// rendering each found clue's text inside `_NotebookRect` with
+		// word-wrap. Selected clues are highlighted (color 0x3c in the
+		// original's case-briefing palette).
+		const Common::Array<uint> found = buildFound();
 		const byte *ni = _mystery.noteIndex();
 		const uint16 niCount = _mystery.noteIndexCount();
-		int y = 4 + _font.getFontHeight() * 2 + 4;
-		for (int slot = 0; slot < kPerPage; slot++) {
-			const int idx = page * kPerPage + slot;
-			if (idx >= total)
-				break;
-			const uint clueId = found[idx];
-			Common::String text;
-			int pts = 0;
+
+		const int kRectX = kNotebookRect.left;
+		const int kRectY = kNotebookRect.top;
+		const int kRectW = kNotebookRect.width();
+		const int kRectH = kNotebookRect.height();
+
+		// Walk forward to the start clue of the current page.
+		// Each page renders as many clues as fit in `kRectH`.
+		int clueCursor = 0;
+		Common::Array<int> pageStarts;
+		pageStarts.push_back(0);
+		{
+			const int lineH = _font.getFontHeight() + 1;
+			int y = kRectY;
+			while (clueCursor < (int)found.size()) {
+				const uint clueId = found[clueCursor];
+				Common::String txt;
+				if (ni && clueId < niCount) {
+					const uint16 textOff = READ_LE_UINT16(ni + clueId * 4);
+					txt = parseString(_mystery.textAt(textOff),
+									  _playerName, _partner);
+				}
+				// Measure height by wrapping the text without drawing.
+				Common::Array<Common::String> wrapped;
+				_font.wordWrapText(txt, kRectW, wrapped);
+				const int h = (int)wrapped.size() * lineH;
+				if (y + h + 7 > kRectY + kRectH) {
+					// Page break before this clue.
+					y = kRectY;
+					pageStarts.push_back(clueCursor);
+				}
+				y += h + 7;
+				clueCursor++;
+			}
+			if (page >= (int)pageStarts.size())
+				page = (int)pageStarts.size() - 1;
+			if (page < 0)
+				page = 0;
+		}
+
+		// Track per-slot rectangles so the click handler can map a
+		// click in `kNoteArea` back to a clue index.
+		Common::Array<Common::Rect> slotRects;
+		Common::Array<uint> slotClues;
+
+		const int startClue = (page < (int)pageStarts.size())
+								? pageStarts[page] : 0;
+		const int endClue   = (page + 1 < (int)pageStarts.size())
+								? pageStarts[page + 1] : (int)found.size();
+
+		int y = kRectY;
+		for (int i = startClue; i < endClue; i++) {
+			const uint clueId = found[i];
+			Common::String txt;
 			if (ni && clueId < niCount) {
 				const uint16 textOff = READ_LE_UINT16(ni + clueId * 4);
-				const uint16 ptsRaw  = READ_LE_UINT16(ni + clueId * 4 + 2);
-				pts = (int)(int16)ptsRaw;
-				const Common::String raw = _mystery.textAt(textOff);
-				text = parseString(raw, _playerName, _partner);
-			}
-			if (text.empty())
-				text = Common::String::format("clue %u", clueId);
-
-			const char selMark = _mystery._noteSelected[clueId] ? '*' : ' ';
-			Common::String line = Common::String::format(
-				"%d [%c] (%d pts) %s", slot + 1, selMark, pts, text.c_str());
-			const int used = _font.drawWordWrapped(&scratch, 8, y, 304, line, 0xF);
-			y += used + 2;
-			if (y >= 192) break;
+				txt = parseString(_mystery.textAt(textOff),
+								  _playerName, _partner);
+			}
+			if (txt.empty())
+				txt = Common::String::format("clue %u", clueId);
+			// Compute wrapped lines first to know the rect.
+			Common::Array<Common::String> wrapped;
+			_font.wordWrapText(txt, kRectW, wrapped);
+			const int lineH = _font.getFontHeight() + 1;
+			const int h = (int)wrapped.size() * lineH;
+
+			// Per `_DrawNotes @ 161e:01d0`: text uses
+			// `_NoteUnselectedColor` (0x5c=cyan) for unselected and 0x3c
+			// (light yellow-white) for selected. Paint a dark "paper"
+			// rectangle behind the text first ??? without this, the
+			// notebook BG (PIC 0x3f) bleeds through and makes the cyan
+			// text hard to read on lighter pixel runs. The fill colour
+			// 0x20 maps to a dark navy across all site palettes.
+			scratch.fillRect(Common::Rect(kRectX - 2, y - 1,
+				kRectX + kRectW + 2, y + h + 1), 0x20);
+
+			const byte color = _mystery._noteSelected[clueId] ? 0x3C : 0x5C;
+			for (uint li = 0; li < wrapped.size(); li++) {
+				_font.drawString(&scratch, wrapped[li], kRectX,
+								 y + (int)li * lineH, kRectW, color);
+			}
+			slotRects.push_back(Common::Rect(kRectX, y,
+											  kRectX + kRectW, y + h));
+			slotClues.push_back(clueId);
+			y += h + 7;
 		}
+
+		// Page indicator + selected-points counter. Paint a small dark
+		// strip behind them too so the text reads on the PDA frame's
+		// upper-right corner.
+		scratch.fillRect(Common::Rect(266, 0, 320, 24), 0x20);
+		_font.drawString(&scratch, Common::String::format("p%d/%d",
+								   page + 1, (int)pageStarts.size()),
+						 270, 4, 320, 0x5C);
+		_font.drawString(&scratch, Common::String::format("%d pts",
+								   _mystery.selectedPoints()),
+						 270, 14, 320, 0x5C);
+		(void)hoveredNoteSlot;
+
 		g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
 								   0, 0, 320, 200);
 		g_system->updateScreen();
+
+		// Stash slot info on the captures so the click handler below
+		// can use it via the closure.
+		_notebookSlotRects = slotRects;
+		_notebookSlotClues = slotClues;
 	};
 
 	draw();
+
+	uint32 lastDraw = g_system->getMillis();
+
 	while (!shouldQuit()) {
 		Common::Event ev;
 		bool dirty = false;
-		bool exit  = false;
+		bool exitFlag = false;
 		while (g_system->getEventManager()->pollEvent(ev)) {
 			if (ev.type == Common::EVENT_QUIT ||
-				ev.type == Common::EVENT_RETURN_TO_LAUNCHER) { exit = true; break; }
+				ev.type == Common::EVENT_RETURN_TO_LAUNCHER) {
+				exitFlag = true;
+				break;
+			}
 			if (ev.type == Common::EVENT_KEYDOWN) {
-				if (ev.kbd.keycode == Common::KEYCODE_ESCAPE) { exit = true; break; }
-				if (ev.kbd.keycode >= Common::KEYCODE_1 && ev.kbd.keycode <= Common::KEYCODE_9) {
-					const int slot = (int)(ev.kbd.keycode - Common::KEYCODE_1);
-					Common::Array<uint> found;
-					for (uint i = 0; i < Mystery::kCluesFoundCap; i++)
-						if (_mystery._cluesFound[i])
-							found.push_back(i);
-					const int idx = page * kPerPage + slot;
-					if (idx < (int)found.size()) {
-						const uint clueId = found[idx];
-						_mystery._noteSelected[clueId] ^= 1;
-						dirty = true;
-					}
-				} else if (ev.kbd.keycode == Common::KEYCODE_TAB ||
-						   ev.kbd.keycode == Common::KEYCODE_RIGHT) {
-					page++; dirty = true;
-				} else if (ev.kbd.keycode == Common::KEYCODE_LEFT) {
+				if (ev.kbd.keycode == Common::KEYCODE_ESCAPE) {
+					exitFlag = true;
+					break;
+				}
+				if (ev.kbd.keycode == Common::KEYCODE_LEFT ||
+					ev.kbd.keycode == Common::KEYCODE_PAGEUP) {
 					if (page > 0) page--;
 					dirty = true;
+				} else if (ev.kbd.keycode == Common::KEYCODE_RIGHT ||
+						   ev.kbd.keycode == Common::KEYCODE_PAGEDOWN ||
+						   ev.kbd.keycode == Common::KEYCODE_TAB) {
+					page++;
+					dirty = true;
+				} else if (ev.kbd.keycode == Common::KEYCODE_h) {
+					doHelp();
+					dirty = true;
+				}
+			}
+			if (ev.type == Common::EVENT_LBUTTONDOWN) {
+				// Test buttons in the order the original would ???
+				// button 0 / 9 are dead zones, so check the actionable
+				// rects directly. Earlier rects "win" when overlapping
+				// (matches `_FindButton`).
+				if (kBtnSite.contains(ev.mouse.x, ev.mouse.y)) {
+					exitFlag = true;
+					break;  // back to site
+				}
+				if (kBtnMap.contains(ev.mouse.x, ev.mouse.y)) {
+					doBigMap();
+					exitFlag = true;
+					break;
+				}
+				if (kBtnPartner.contains(ev.mouse.x, ev.mouse.y)) {
+					doHelp();              // _KDHelp = host hint
+					dirty = true;
+					continue;
+				}
+				if (kBtnAccuse.contains(ev.mouse.x, ev.mouse.y)) {
+					doAccuse();
+					exitFlag = true;
+					break;
+				}
+				if (kBtnGallery.contains(ev.mouse.x, ev.mouse.y)) {
+					doGallery();
+					dirty = true;
+					continue;
+				}
+				if (kBtnPagePrev.contains(ev.mouse.x, ev.mouse.y)) {
+					if (page > 0) page--;
+					dirty = true;
+					continue;
+				}
+				if (kBtnPageNext.contains(ev.mouse.x, ev.mouse.y)) {
+					page++;
+					dirty = true;
+					continue;
+				}
+				if (kNoteArea.contains(ev.mouse.x, ev.mouse.y)) {
+					// Toggle the selection on whichever clue's text
+					// the click landed in. The original calls
+					// `_InterfaceHelp` here; that's the help screen,
+					// not selection ??? selection is in the Accuse
+					// screen. We use the area for selection because
+					// keyboard 1..9 toggling is awkward, and the
+					// resulting `_NoteSelected` state is what
+					// `_SolvedCheck` reads.
+					for (uint i = 0; i < _notebookSlotRects.size(); i++) {
+						if (_notebookSlotRects[i].contains(ev.mouse.x,
+														   ev.mouse.y)) {
+							const uint clueId = _notebookSlotClues[i];
+							_mystery._noteSelected[clueId] ^= 1;
+							dirty = true;
+							break;
+						}
+					}
+					continue;
 				}
 			}
 		}
-		if (exit) break;
-		if (dirty) draw();
+		if (exitFlag)
+			break;
+
+		const uint32 now = g_system->getMillis();
+		// Re-render every 100 ms so the partner sprite cycles frames.
+		if (dirty || now - lastDraw >= 100) {
+			draw();
+			lastDraw = now;
+		}
 		g_system->updateScreen();
 		g_system->delayMillis(15);
 	}
 }
 
 void EEMEngine::doGallery() {
-	// Mirrors `_DrawGallery` @ 158f:0046. The original loops `_NumSuspects`
-	// gallery entries (0x46 = 70 bytes each in `_GalleryData`); the first
-	// u16 of each entry is the PIC picture ID for that suspect. We render
-	// them in a row across the screen.
+	// Mirrors `_DoGallery @ 158f:065b` and `_DrawGallery @ 158f:0046`.
+	// Verified directly from the disassembly:
+	//   * Background: PIC 0x3f (same as PDA).
+	//   * Partner sprite at (5, 0x50): anim 2 (Jake) / 0x10 (Jenny).
+	//     `_NewAnimation(5, 0x50, ...)`. NOTE: gallery uses anim 2/0x10,
+	//     PDA uses 1/0xb ??? different sprites.
+	//   * Five fixed slot positions at `29be:0x116` (4 bytes per slot,
+	//     `{u16 x, u16 y}`):
+	//         slot 0 = ( 83,  14)   slot 3 = (119,  90)
+	//         slot 1 = (155,  14)   slot 4 = (191,  90)
+	//         slot 2 = (227,  14)
+	//   * For each logical suspect i in 0..NumSuspects-1:
+	//         picId   = `*(u16 *)(_GalleryData + i * 0x46)` (entry +0).
+	//         visible = `_InGallery[_NewOrder[i]] != 0`.
+	//         drawX   = positions[_NewOrder[i]].x
+	//         drawY   = positions[_NewOrder[i]].y + (0x48 - pic.height)
+	//     So portraits are BOTTOM-aligned to baselines 0x48 + pos.y.
+	//   * Click on portrait via `_SearchSuspects` ??? `MoreInfo(i)` shows
+	//     the suspect detail page. ESC returns to PDA.
+	//   * Frame-cycled @ 100ms via `_CheckFrameRate` + `_UpdateAnimations`
+	//     + `_GizmoColorCycle`.
 	if (!_mystery.isLoaded())
 		return;
 
@@ -1456,60 +1781,323 @@ void EEMEngine::doGallery() {
 		return;
 	}
 
-	Graphics::ManagedSurface scratch(320, 200,
-		Graphics::PixelFormat::createFormatCLUT8());
-	scratch.clear();
+	CursorMan.showMouse(true);
+
+	struct Slot { int x; int y; };
+	static const Slot kGallerySlots[5] = {
+		{  83,  14 }, // 0
+		{ 155,  14 }, // 1
+		{ 227,  14 }, // 2
+		{ 119,  90 }, // 3
+		{ 191,  90 }  // 4
+	};
 
-	// Use PIC 0x3f as the gallery backdrop, matching `_DoAccuseGallery`.
+	// Pre-load static elements once.
 	Picture galBg;
-	if (_picsArchive.getPicture(0x3f, galBg)) {
-		const int w = MIN<int>(galBg.surface.w, 320);
-		const int h = MIN<int>(galBg.surface.h, 200);
-		for (int row = 0; row < h; row++) {
-			memcpy((byte *)scratch.getBasePtr(0, row),
-				   (const byte *)galBg.surface.getBasePtr(0, row), w);
-		}
-	}
+	const bool haveBg = _picsArchive.getPicture(0x3f, galBg);
 
-	if (_font.isLoaded())
-		_font.drawString(&scratch, "GALLERY", 8, 4, 320, 0xF);
+	// Gallery partner anim ??? `_DoGallery` calls `_GetAnimation(uVar6)` with
+	// uVar6 = 2 (Jake) / 0x10 (Jenny). Different from PDA (1 / 0xb).
+	const uint partnerAnim = (_partner == 0) ? 2 : 0x10;
+	Animation partnerAni;
+	const bool havePartner = _aniArchive.loadAnimation(partnerAnim, partnerAni)
+							  && !partnerAni.empty();
 
 	const uint8 num = _mystery.numSuspects();
-	int slotX = 8;
-	const int slotY = 24;
-	const int slotStep = 320 / MAX<uint8>(1, num);
+
+	// Cache slot rects for click hit-testing.
+	Common::Array<Common::Rect> slotRects;
+	Common::Array<int> slotSuspect; // logical suspect index in [0, num)
+	slotRects.resize(num);
+	slotSuspect.resize(num);
 	for (uint i = 0; i < num; i++) {
-		const uint16 picId = READ_LE_UINT16(gd + i * 0x46);
-		if (picId == 0)
-			continue;
-		Picture portrait;
-		if (!_picsArchive.getPicture(picId, portrait))
-			continue;
-		const int placeX = slotX + (slotStep - portrait.surface.w) / 2;
-		const int placeY = slotY;
-		const int w = MIN<int>(portrait.surface.w, 320 - placeX);
-		const int h = MIN<int>(portrait.surface.h, 200 - placeY);
-		if (w > 0 && h > 0) {
+		slotSuspect[i] = -1;
+	}
+
+	auto drawFrame = [&]() {
+		Graphics::ManagedSurface scratch(320, 200,
+			Graphics::PixelFormat::createFormatCLUT8());
+		scratch.clear();
+
+		if (haveBg) {
+			const int bw = MIN<int>(galBg.surface.w, 320);
+			const int bh = MIN<int>(galBg.surface.h, 200);
+			for (int row = 0; row < bh; row++) {
+				memcpy((byte *)scratch.getBasePtr(0, row),
+					   (const byte *)galBg.surface.getBasePtr(0, row), bw);
+			}
+		}
+
+		// Partner sprite frame @ (5, 0x50).
+		if (havePartner) {
+			const uint32 now = g_system->getMillis();
+			const uint frameIdx = (uint)((now / 100) % partnerAni.size());
+			const Picture &fr = partnerAni[frameIdx];
+			const byte transp = (byte)(fr.flags >> 8);
+			const int px = 5, py = 0x50;
+			for (int row = 0; row < fr.surface.h; row++) {
+				const int dstY = py + row;
+				if (dstY < 0 || dstY >= 200) continue;
+				const byte *src = (const byte *)fr.surface.getBasePtr(0, row);
+				byte *dst = (byte *)scratch.getBasePtr(0, dstY);
+				for (int col = 0; col < fr.surface.w; col++) {
+					const int dstX = px + col;
+					if (dstX < 0 || dstX >= 320) continue;
+					if (src[col] != transp)
+						dst[dstX] = src[col];
+				}
+			}
+		}
+
+		// Portraits.
+		for (uint i = 0; i < num && i < Mystery::kGalleryCap; i++) {
+			slotRects[i] = Common::Rect();   // empty
+			slotSuspect[i] = -1;
+
+			const uint8 phys = _mystery._newOrder[i];
+			if (phys >= 5)
+				continue;
+			const bool discovered = _mystery._inGallery[phys] != 0;
+			if (!discovered)
+				continue;
+
+			const uint16 picId = READ_LE_UINT16(gd + i * 0x46);
+			if (picId == 0)
+				continue;
+			Picture portrait;
+			if (!_picsArchive.getPicture(picId, portrait))
+				continue;
+
+			const Slot &s = kGallerySlots[phys];
+			const int placeX = s.x;
+			const int placeY = s.y + (0x48 - portrait.surface.h);
+			const byte transp = (byte)(portrait.flags >> 8);
+
+			const int w = MIN<int>(portrait.surface.w, 320 - placeX);
+			const int h = MIN<int>(portrait.surface.h, 200 - placeY);
+			if (w <= 0 || h <= 0)
+				continue;
 			for (int row = 0; row < h; row++) {
-				memcpy((byte *)scratch.getBasePtr(placeX, placeY + row),
-					   (const byte *)portrait.surface.getBasePtr(0, row), w);
+				const int dstY = placeY + row;
+				if (dstY < 0) continue;
+				const byte *src =
+					(const byte *)portrait.surface.getBasePtr(0, row);
+				byte *dst = (byte *)scratch.getBasePtr(0, dstY);
+				for (int col = 0; col < w; col++) {
+					const int dstX = placeX + col;
+					if (src[col] != transp)
+						dst[dstX] = src[col];
+				}
+			}
+
+			// Cache rect for hit-test.
+			slotRects[i] = Common::Rect(placeX, placeY,
+										 placeX + w, placeY + h);
+			slotSuspect[i] = (int)i;
+
+			// Index label below portrait ??? original doesn't draw labels
+			// (the detail page does), but useful while MoreInfo isn't
+			// implemented.
+			if (_font.isLoaded()) {
+				Common::String label = Common::String::format("%u", i + 1);
+				_font.drawString(&scratch, label,
+								 placeX + portrait.surface.w / 2 - 3,
+								 placeY + portrait.surface.h + 2,
+								 320, 0x5C);
 			}
 		}
-		// Suspect number + discovered marker under the portrait.
+
+		// Header / hint line ??? KD's hint balloon would normally show here
+		// (`_DoAccuseGallery` plays a balloon at top), but the standalone
+		// `_DoGallery` doesn't. We add a small header for clarity.
 		if (_font.isLoaded()) {
-			const bool discovered = (i < Mystery::kGalleryCap) &&
-									_mystery._inGallery[i];
-			Common::String label = Common::String::format("%u%s",
-				i + 1, discovered ? " *" : "");
-			_font.drawString(&scratch, label, placeX + 4, placeY + h + 2, 320, 0xF);
+			_font.drawString(&scratch, "GALLERY", 60, 4, 256, 0x5C);
+			_font.drawString(&scratch, "Click suspect | ESC", 60, 188, 256, 0x5C);
 		}
-		slotX += slotStep;
-	}
 
-	g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
-							   0, 0, 320, 200);
-	g_system->updateScreen();
-	waitForInput(60000);
+		g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
+								   0, 0, 320, 200);
+		g_system->updateScreen();
+	};
+
+	drawFrame();
+	uint32 lastDraw = g_system->getMillis();
+
+	while (!shouldQuit()) {
+		Common::Event ev;
+		bool exitFlag = false;
+		while (g_system->getEventManager()->pollEvent(ev)) {
+			if (ev.type == Common::EVENT_QUIT ||
+				ev.type == Common::EVENT_RETURN_TO_LAUNCHER) {
+				return;
+			}
+			if (ev.type == Common::EVENT_KEYDOWN) {
+				if (ev.kbd.keycode == Common::KEYCODE_ESCAPE) {
+					exitFlag = true;
+					break;
+				}
+			}
+			if (ev.type == Common::EVENT_LBUTTONDOWN) {
+				// `_SearchSuspects` walks the per-slot rects and returns
+				// the suspect index. We mirror that with cached rects.
+				bool clicked = false;
+				for (uint i = 0; i < slotRects.size(); i++) {
+					if (slotSuspect[i] < 0) continue;
+					if (slotRects[i].contains(ev.mouse.x, ev.mouse.y)) {
+						// `MoreInfo(i)` ??? show the suspect detail page.
+						// Mirrors `MoreInfo @ 158f:0419`:
+						//   _RefreshGalleryBackground();
+						//   _GetPicture(*(u16*)(gd + i*0x46));
+						//   _AddPicBackground(pic, 0x94, 0xf);
+						//   _DrawGalleryNotes(gd + i*0x46);
+						//   loop until ESC or button click.
+						// Suspect data layout (verified against M1):
+						//   +0..1: picId (used here AND for gallery slot)
+						//   +8..9: number of clues for this suspect
+						//   +0xa..??: array of u16 clue IDs (terminated
+						//             by 0xFFFF if shorter than count).
+						const uint suspectIdx = (uint)slotSuspect[i];
+						const byte *suspect = gd + suspectIdx * 0x46;
+						const uint16 detailPic =
+							READ_LE_UINT16(suspect + 0);
+						const uint16 clueCount =
+							READ_LE_UINT16(suspect + 8);
+
+						Graphics::ManagedSurface ms(320, 200,
+							Graphics::PixelFormat::createFormatCLUT8());
+						ms.clear();
+						if (haveBg) {
+							const int bw = MIN<int>(galBg.surface.w, 320);
+							const int bh = MIN<int>(galBg.surface.h, 200);
+							for (int row = 0; row < bh; row++) {
+								memcpy((byte *)ms.getBasePtr(0, row),
+									   (const byte *)galBg.surface.getBasePtr(0, row), bw);
+							}
+						}
+						// Full suspect picture at (0x94, 0xf).
+						Picture detail;
+						if (_picsArchive.getPicture(detailPic, detail)) {
+							const byte transp =
+								(byte)(detail.flags >> 8);
+							const int dx = 0x94, dy = 0x0f;
+							const int dw = MIN<int>(detail.surface.w, 320 - dx);
+							const int dh = MIN<int>(detail.surface.h, 200 - dy);
+							for (int row = 0; row < dh; row++) {
+								const byte *src =
+									(const byte *)detail.surface.getBasePtr(0, row);
+								byte *dst =
+									(byte *)ms.getBasePtr(0, dy + row);
+								for (int col = 0; col < dw; col++) {
+									if (src[col] != transp)
+										dst[dx + col] = src[col];
+								}
+							}
+						}
+						// Suspect's clue notes inside _GalleryNoteRect
+						// = (78, 93, 288, 152), per 29be:0100.
+						const int rx = 78, ry = 93;
+						const int rw = 288 - 78, rh = 152 - 93;
+						// Paint a dark fill behind the entire notes area
+						// so cyan text on a possibly-cyan PIC background
+						// stays readable. Color 0x20 = dark navy in all
+						// site palettes.
+						ms.fillRect(Common::Rect(rx - 2, ry - 12,
+							rx + rw + 2, ry + rh + 12), 0x20);
+
+						const byte *ni = _mystery.noteIndex();
+						const uint16 niCount = _mystery.noteIndexCount();
+						int yPos = ry;
+						const int lineH = _font.getFontHeight() + 1;
+						bool drewAny = false;
+						for (uint k = 0; k < clueCount && k < 30; k++) {
+							const uint16 clueId =
+								READ_LE_UINT16(suspect + 0xa + k * 2);
+							if (clueId == 0xFFFF) break;
+							if (clueId >= Mystery::kCluesFoundCap ||
+								!_mystery._cluesFound[clueId])
+								continue;
+							if (!ni || clueId >= niCount) continue;
+							const uint16 textOff =
+								READ_LE_UINT16(ni + clueId * 4);
+							Common::String txt =
+								parseString(_mystery.textAt(textOff),
+											_playerName, _partner);
+							if (txt.empty()) continue;
+							const byte color =
+								_mystery._noteSelected[clueId] ? 0x3C : 0x5C;
+							const int hLine = _font.drawWordWrapped(
+								&ms, rx, yPos, rw, txt, color);
+							yPos += hLine + 7;
+							drewAny = true;
+							if (yPos + lineH > ry + rh) break;
+						}
+						if (!drewAny && _font.isLoaded()) {
+							_font.drawString(&ms,
+								"No clues yet for this suspect.",
+								rx, ry, rw, 0x5C);
+						}
+						// Header / footer text.
+						if (_font.isLoaded()) {
+							_font.drawString(&ms, "SUSPECT FILE",
+											  rx, ry - 11, rw, 0x3C);
+							_font.drawString(&ms, "(click / ESC: back)",
+											  rx, ry + rh + 2, rw, 0x3C);
+						}
+						g_system->copyRectToScreen(ms.getPixels(),
+							ms.pitch, 0, 0, 320, 200);
+						g_system->updateScreen();
+
+						// Wait for click or ESC. Drain the queued
+						// LBUTTONDOWN that triggered this MoreInfo first
+						// so we don't immediately accept it as the
+						// dismiss event.
+						g_system->delayMillis(150);
+						{
+							Common::Event drain;
+							while (g_system->getEventManager()->pollEvent(drain)) {
+								if (drain.type == Common::EVENT_QUIT ||
+									drain.type == Common::EVENT_RETURN_TO_LAUNCHER)
+									return;
+							}
+						}
+						bool back = false;
+						while (!back && !shouldQuit()) {
+							Common::Event e2;
+							while (g_system->getEventManager()->pollEvent(e2)) {
+								if (e2.type == Common::EVENT_LBUTTONDOWN ||
+									(e2.type == Common::EVENT_KEYDOWN &&
+									 (e2.kbd.keycode == Common::KEYCODE_ESCAPE ||
+									  e2.kbd.keycode == Common::KEYCODE_RETURN))) {
+									back = true;
+									break;
+								}
+								if (e2.type == Common::EVENT_QUIT ||
+									e2.type == Common::EVENT_RETURN_TO_LAUNCHER)
+									return;
+							}
+							g_system->delayMillis(20);
+						}
+						// Force gallery redraw immediately so the
+						// player isn't left looking at the dismissed
+						// MoreInfo screen until the next 100 ms tick.
+						drawFrame();
+						lastDraw = g_system->getMillis();
+						clicked = true;
+						break;
+					}
+				}
+				(void)clicked;
+			}
+		}
+		if (exitFlag) break;
+
+		const uint32 now = g_system->getMillis();
+		if (now - lastDraw >= 100) {
+			drawFrame();
+			lastDraw = now;
+		}
+		g_system->delayMillis(15);
+	}
 }
 
 void EEMEngine::doBigMap() {
diff --git a/engines/eem/eem.h b/engines/eem/eem.h
index ec8e9d27b35..85c25b26c22 100644
--- a/engines/eem/eem.h
+++ b/engines/eem/eem.h
@@ -206,6 +206,13 @@ private:
 	/// the opening-anim loop in run() to skip the rest of the chain
 	/// instead of asking the user to click through every screen.
 	bool _skipIntro = false;
+
+	/// Per-slot rectangles + clue IDs from the most recent notebook
+	/// render, populated by the `draw` lambda inside `doNotebook` and
+	/// consumed by the click handler. The original walks the notes
+	/// inline; we cache the layout to keep click hit-testing simple.
+	Common::Array<Common::Rect> _notebookSlotRects;
+	Common::Array<uint>         _notebookSlotClues;
 };
 
 } // End of namespace EEM
diff --git a/engines/eem/site.cpp b/engines/eem/site.cpp
index 30f97bc29c0..964f7707eaa 100644
--- a/engines/eem/site.cpp
+++ b/engines/eem/site.cpp
@@ -24,6 +24,8 @@
 #include "common/system.h"
 #include "common/textconsole.h"
 
+#include "graphics/paletteman.h"
+
 #include "eem/detection.h"
 #include "eem/eem.h"
 #include "eem/mystery.h"
@@ -83,6 +85,11 @@ void SiteScreen::enter(uint siteNum) {
 	captureBgSnapshot();
 	_snapshotSite = (int)siteNum;
 
+	// Cache ColorCycle palette ranges for this site so the per-tick
+	// frame pump can rotate them. Mirrors the init scan at the top of
+	// `_DoSiteLoop @ 168d:03f4`.
+	scanColorCycles(siteNum);
+
 	// Animated NPCs (Loop 1) and the persistent partner sit on top of
 	// the snapshot. Initial frame goes at tickMs=now.
 	const uint32 now = g_system->getMillis();
@@ -297,6 +304,10 @@ void SiteScreen::run() {
 			renderAnimatedDrops(cur, now);
 			renderPartner(cur, now);
 			renderHotspots(cur);
+			// Per-tick palette rotation for ColorCycle entries +
+			// hotspot marching ants. Matches `_ColorCycle(start, end)`
+			// calls inside `_DoSiteLoop @ 168d:03f4`'s main loop.
+			applyColorCycles();
 			_lastTickMs = now;
 		}
 		g_system->updateScreen();
@@ -542,6 +553,67 @@ void SiteScreen::renderAnimatedDrops(uint siteNum, uint32 tickMs) {
 	g_system->unlockScreen();
 }
 
+void SiteScreen::scanColorCycles(uint siteNum) {
+	// `_DoSiteLoop @ 168d:03f4` walks Loop 1 entries (siteData[+0xa]
+	// count, 6-byte entries at siteData[+0x48]) and stores each entry
+	// with `animId == -1` into a 5-slot table:
+	//   start palette idx = entry +2
+	//   end   palette idx = entry +4
+	// We mirror the layout exactly. Up to 5 entries are tracked (the
+	// original's `[unaff_BP + -0x12]` and `[unaff_BP + -0x1c]` arrays
+	// are 5 ?? u16 each).
+	_colorCycles.clear();
+	if (!_mystery)
+		return;
+	const byte *site = _mystery->siteData(siteNum);
+	if (!site)
+		return;
+	const uint16 numAnims = READ_LE_UINT16(site + 0xa);
+	for (uint i = 0; i < numAnims && _colorCycles.size() < 5; i++) {
+		const int16 animId = (int16)READ_LE_UINT16(site + 0x48 + i * 6);
+		if (animId != -1)
+			continue;
+		const uint16 startPal = READ_LE_UINT16(site + 0x48 + i * 6 + 2);
+		const uint16 endPal   = READ_LE_UINT16(site + 0x48 + i * 6 + 4);
+		ColorCycleRange r;
+		r.start = (uint8)startPal;
+		r.end   = (uint8)endPal;
+		if (r.end > r.start)
+			_colorCycles.push_back(r);
+	}
+}
+
+void SiteScreen::applyColorCycles() {
+	// `_ColorCycle @ 172b:2015` rotates `_fpal[start..end]` by one
+	// palette slot ??? saves [start], shifts [start..end-1] = [start+1..
+	// end], restores saved at [end] ??? then re-uploads via `_Set_Palette`.
+	// We do the same against ScummVM's palette manager. Always rotate
+	// 0xf9..0xfe for hotspot marching ants (the `_ColorCycle(0xf9,
+	// 0xfe)` call at the bottom of `_DoSiteLoop`'s main loop).
+	auto rotate = [&](uint8 start, uint8 end) {
+		if (end <= start) return;
+		const uint count = (uint)end - (uint)start + 1;
+		byte buf[256 * 3];
+		g_system->getPaletteManager()->grabPalette(buf, start, count);
+		// Save first triplet, shift, restore at end.
+		byte savedR = buf[0], savedG = buf[1], savedB = buf[2];
+		for (uint i = 0; i + 1 < count; i++) {
+			buf[i * 3 + 0] = buf[(i + 1) * 3 + 0];
+			buf[i * 3 + 1] = buf[(i + 1) * 3 + 1];
+			buf[i * 3 + 2] = buf[(i + 1) * 3 + 2];
+		}
+		buf[(count - 1) * 3 + 0] = savedR;
+		buf[(count - 1) * 3 + 1] = savedG;
+		buf[(count - 1) * 3 + 2] = savedB;
+		g_system->getPaletteManager()->setPalette(buf, start, count);
+	};
+	for (uint i = 0; i < _colorCycles.size(); i++) {
+		rotate(_colorCycles[i].start, _colorCycles[i].end);
+	}
+	// Hotspot marching ants ??? always cycled.
+	rotate(0xF9, 0xFE);
+}
+
 void SiteScreen::captureBgSnapshot() {
 	_bgSnapshot.create(320, 200, Graphics::PixelFormat::createFormatCLUT8());
 	Graphics::Surface *screen = g_system->lockScreen();
@@ -708,6 +780,19 @@ void SiteScreen::renderHotspots(uint siteNum) {
 	if (!screen)
 		return;
 
+	// Mirrors `_DrawSearchButtons @ 2404:0a8f`:
+	//   for each hotspot:
+	//     if `_Sawit(theSite, loc)` (= _SaveBuffer[hotspot[+0xa]] != 0)
+	//       _DrawRect(rect)        // outline in cycling colors 0xF9..0xFE
+	//     else
+	//       _DrawSolidRect(rect)   // outline in solid white 0xFF
+	// `_DrawRect`'s cycling colors produce a "marching ants" effect that
+	// makes already-searched hotspots visually distinct without hiding
+	// them. We approximate the cycling by rotating the start color via
+	// the global tick.
+	const uint32 tickMs = g_system->getMillis();
+	const byte cyclePhase = (byte)((tickMs / 80) & 0x07);  // 0..7
+
 	for (uint i = 0; i < count; i++) {
 		const byte *r = spots + i * 14;
 		const int16 x1 = (int16)READ_LE_UINT16(r + 0);
@@ -717,11 +802,43 @@ void SiteScreen::renderHotspots(uint siteNum) {
 		const Common::Rect rect(MAX<int>(0, x1), MAX<int>(0, y1),
 								MIN<int>(screen->w, x2),
 								MIN<int>(screen->h, y2));
-		// Hotspots flagged as "seen" get a different colour so the
-		// player knows they've already searched them.
-		const byte color =
-			(i < Mystery::kHotSpotsCap && _mystery->_hotSpotsSeen[i]) ? 0x07 : 0x0F;
-		screen->frameRect(rect, color);
+		const bool seen = (i < Mystery::kHotSpotsCap)
+						   && _mystery->_hotSpotsSeen[i];
+		if (!seen) {
+			// `_DrawSolidRect` ??? solid white outline (color 0xFF).
+			screen->frameRect(rect, 0xFF);
+		} else {
+			// `_DrawRect` ??? cycling colors 0xF9..0xFE on each pixel of
+			// the outline. We approximate per-pixel cycling with a
+			// per-rect phase shift so the rects look animated. Start
+			// color is rotated via the global clock.
+			const byte palette[6] = { 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE };
+			byte color = palette[cyclePhase % 6];
+			// Top edge
+			for (int x = rect.left; x < rect.right; x++) {
+				if (x >= 0 && x < screen->w && rect.top >= 0 && rect.top < screen->h)
+					*(byte *)screen->getBasePtr(x, rect.top) = color;
+				color = palette[(color - 0xF9 + 1) % 6];
+			}
+			// Right edge
+			for (int y = rect.top; y < rect.bottom; y++) {
+				if (rect.right - 1 >= 0 && rect.right - 1 < screen->w && y >= 0 && y < screen->h)
+					*(byte *)screen->getBasePtr(rect.right - 1, y) = color;
+				color = palette[(color - 0xF9 + 1) % 6];
+			}
+			// Bottom edge
+			for (int x = rect.right - 1; x >= rect.left; x--) {
+				if (x >= 0 && x < screen->w && rect.bottom - 1 >= 0 && rect.bottom - 1 < screen->h)
+					*(byte *)screen->getBasePtr(x, rect.bottom - 1) = color;
+				color = palette[(color - 0xF9 + 1) % 6];
+			}
+			// Left edge
+			for (int y = rect.bottom - 1; y >= rect.top; y--) {
+				if (rect.left >= 0 && rect.left < screen->w && y >= 0 && y < screen->h)
+					*(byte *)screen->getBasePtr(rect.left, y) = color;
+				color = palette[(color - 0xF9 + 1) % 6];
+			}
+		}
 	}
 
 	g_system->unlockScreen();
@@ -748,9 +865,21 @@ int SiteScreen::hotspotAtPoint(uint siteNum, int x, int y) const {
 void SiteScreen::onHotspotClicked(uint siteNum, uint hotIdx) {
 	debugC(1, kDebugSite, "Site %u: hotspot %u clicked", siteNum, hotIdx);
 
-	// Mark the hotspot itself as seen.
+	// `_DoSiteLoop @ 168d:03f4` (after _DisplayClue):
+	//   _HotSpotsSeen[hotspot[+0xa] * 2] = _HotSpotComplete;
+	// The "seen" key is the hotspotIndex field (+0xa) ??? the 1-based
+	// ordinal ??? NOT the array index. Two hotspots can share an ordinal
+	// across sites (e.g., a partner's clue you can re-read), so this
+	// matters for cross-site state.
+	const byte *spots = _mystery->hotspots(siteNum);
+	uint hotOrdinal = hotIdx; // fallback to array index
+	if (spots) {
+		hotOrdinal = READ_LE_UINT16(spots + hotIdx * 14 + 0xa);
+	}
+	if (hotOrdinal < Mystery::kHotSpotsCap)
+		_mystery->_hotSpotsSeen[hotOrdinal] = 1;
 	if (hotIdx < Mystery::kHotSpotsCap)
-		_mystery->_hotSpotsSeen[hotIdx] = 1;
+		_mystery->_hotSpotsSeen[hotIdx] = 1;  // also mark by array idx for our render
 	_mystery->_searchLocationNumber = (uint16)hotIdx;
 
 	// Bytes 8..9 of each 14-byte hotspot rect = byte offset within the
@@ -759,7 +888,6 @@ void SiteScreen::onHotspotClicked(uint siteNum, uint hotIdx) {
 	// trick on us...". `displayClue` runs the entry's side effects
 	// (`_AddNotebook` for ClueEntry +0x30..+0x39, gallery +0x26..+0x2f,
 	// onsite +0x1c..+0x25) so we don't need to touch `_cluesFound` here.
-	const byte *spots = _mystery->hotspots(siteNum);
 	if (spots) {
 		const uint16 clueOff = READ_LE_UINT16(spots + hotIdx * 14 + 8);
 		debugC(2, kDebugSite, "  hotspot %u -> clue offset 0x%04x",
diff --git a/engines/eem/site.h b/engines/eem/site.h
index bae8dc53cc6..00c1e647ea0 100644
--- a/engines/eem/site.h
+++ b/engines/eem/site.h
@@ -102,6 +102,18 @@ private:
 	/// Restore the snapshot taken at `captureBgSnapshot` time.
 	void restoreBgSnapshot();
 
+	/// Scan the site's Loop 1 entries for ColorCycle entries (animId
+	/// == -1) and cache their (start, end) palette ranges. Called from
+	/// `enter()`. Mirrors `_DoSiteLoop @ 168d:03f4`'s init scan.
+	void scanColorCycles(uint siteNum);
+
+	/// Rotate cached ColorCycle palette ranges (and 0xf9..0xfe for
+	/// hotspot marching ants) one step. Mirrors the original's per-tick
+	/// `_ColorCycle(start, end)` calls inside `_DoSiteLoop`'s main
+	/// loop. ScummVM's palette manager grabs current 8-bit RGB and
+	/// writes back the rotated values.
+	void applyColorCycles();
+
 	EEMEngine *_vm;
 	Mystery *_mystery;
 	bool _showHotspots = true;     ///< Toggle outlines with V key.
@@ -109,6 +121,11 @@ private:
 	int _snapshotSite = -1;        ///< Site number the snapshot belongs to.
 	Graphics::ManagedSurface _bgSnapshot;
 	uint32 _lastTickMs = 0;        ///< Last frame-pump tick in ms.
+
+	/// Per-site cached ColorCycle ranges. Up to 5 (matching the
+	/// original's 5-slot animation table).
+	struct ColorCycleRange { uint8 start, end; };
+	Common::Array<ColorCycleRange> _colorCycles;
 };
 
 } // End of namespace EEM


Commit: b36297d2a5918762f6f4a4e19efd9ed5f587f130
    https://github.com/scummvm/scummvm/commit/b36297d2a5918762f6f4a4e19efd9ed5f587f130
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:20:38+02:00

Commit Message:
EEM: polish PDA features: buttons now works correctly

Changed paths:
    engines/eem/eem.cpp
    engines/eem/mystery.h


diff --git a/engines/eem/eem.cpp b/engines/eem/eem.cpp
index 578f3290a6f..554c588ab32 100644
--- a/engines/eem/eem.cpp
+++ b/engines/eem/eem.cpp
@@ -1099,6 +1099,74 @@ void EEMEngine::doInitClues() {
 		blitMaskedAt(nancy[0], 0x68, 0x8b);
 	g_system->updateScreen();
 
+	// Step 5 ??? `_PlayInSequence(animSeq, 0xcd, animY)` per Ghidra:
+	//   Jake (partner=0):
+	//     caseType=1 ??? anim 0x38 at (0xcd, 0x6d)
+	//     caseType=2 ??? anim 0x37 at (0xcd, 0x6c)
+	//     caseType=3 ??? anim 0x39 at (0xcd, 0x6c)
+	//   Jenny (partner=1):
+	//     caseType=2 ??? anim 0x3a at (0xcd, 0x6c)
+	//     caseType=3 ??? anim 0x3d at (0xcd, 0x6c)
+	// `_PlayInSequence @ 172b:2d03` plays each frame at (sx-w, sy-rowoff)
+	// with mask blit, advancing one frame per `_CheckFrameRate` tick.
+	uint16 seqAni = 0xFFFF;
+	uint16 seqY   = 0x6c;
+	if (_partner == 0) {
+		switch (caseType) {
+		case 1: seqAni = 0x38; seqY = 0x6d; break;
+		case 2: seqAni = 0x37; seqY = 0x6c; break;
+		case 3: seqAni = 0x39; seqY = 0x6c; break;
+		default: break;
+		}
+	} else {
+		switch (caseType) {
+		case 2: seqAni = 0x3a; seqY = 0x6c; break;
+		case 3: seqAni = 0x3d; seqY = 0x6c; break;
+		default: break;
+		}
+	}
+	if (seqAni != 0xFFFF) {
+		Animation seq;
+		if (_aniArchive.loadAnimation(seqAni, seq) && !seq.empty()) {
+			bool skip = false;
+			for (uint frame = 0; frame < seq.size() && !shouldQuit() && !skip;
+				 frame++) {
+				const Picture &fr = seq[frame];
+				// Restore BG + base anim frames so each new frame
+				// composites cleanly.
+				if (_picsArchive.getPicture(0x52, bg))
+					blitAt(bg, 0, 0);
+				if (haveGame)
+					blitMaskedAt(game[frame % game.size()], 0xcd, 0x6c);
+				if (haveBook)
+					blitMaskedAt(book[frame % book.size()], 0, 99);
+				if (haveNancy)
+					blitMaskedAt(nancy[frame % nancy.size()], 0x68, 0x8b);
+				// Anchor: original blits at `(sx - frame.width,
+				// sy - frame.rowoff)`. `frame.rowoff` is the y-anchor
+				// in our PicData. We use width/height directly since
+				// loadAnimation places anchor at (0, 0).
+				const int dstX = (int)0xcd - (int)fr.surface.w;
+				const int dstY = (int)seqY - (int)fr.rowoff;
+				blitMaskedAt(fr, dstX, dstY);
+				g_system->updateScreen();
+				const uint32 wakeup = g_system->getMillis() + 100;
+				while (g_system->getMillis() < wakeup &&
+					   !shouldQuit() && !skip) {
+					Common::Event ev;
+					while (g_system->getEventManager()->pollEvent(ev)) {
+						if (ev.type == Common::EVENT_LBUTTONDOWN ||
+							ev.type == Common::EVENT_KEYDOWN) {
+							skip = true;
+							break;
+						}
+					}
+					g_system->delayMillis(10);
+				}
+			}
+		}
+	}
+
 	// Step 6 ??? case briefing dialogue.
 	displayClue(ib + 4);
 }
@@ -1461,17 +1529,31 @@ void EEMEngine::doNotebook() {
 	if (!_mystery.isLoaded() || !_font.isLoaded())
 		return;
 
+	// Button rects from `_NoteButtons @ 29be:0147` matched to handler
+	// addresses via the jump table at `_HandleNoteButton + 0xec` (i.e.
+	// 161e:04ec). Decoded handlers (i = rect_index, dispatch = handler[i-1]):
+	//   rect 0 (134,155) ??? no handler (i-1 underflows; original treats
+	//                      this as a decorative/no-op slot)
+	//   rect 1 (93,115)  ??? 0x03f9 = `_InterfaceHelp(0)`           (HELP)
+	//   rect 2 (157,178) ??? 0x0477 = `_NextScreen = 5`             (GALLERY)
+	//   rect 3 (5,80)    ??? 0x0403 = `_KDHelp`                     (host hint)
+	//   rect 4 (180,201) ??? 0x0436 = `_SolvedCheck` -> NextScreen=7 (SOLVE)
+	//   rect 5 (204,224) ??? 0x0489 = `_EraseNotes` + `_DrawNotes`  (PAGE NEXT)
+	//   rect 6 (226,247) ??? 0x04ab = decrement CurrentPage + redraw (PAGE PREV)
+	//   rect 7 (7,177)   ??? 0x0480 = `_NextScreen = 2`             (MAP)
+	//   rect 8 (35,111)  ??? 0x03ed = `_NextScreen = 3`             (SITE)
+	//   rect 9 (0,0)     ??? 0x03ed = same as rect 8
+	//   rect 10 (66,79)  ??? 0x03f9 = `_InterfaceHelp(0)`           (note-area help)
 	const Common::Rect kNotebookRect(78, 12, 288, 152);
-	const Common::Rect kBtnHelp1   ( 93, 174, 115, 190);  // [1]
-	const Common::Rect kBtnPagePrev(157, 174, 178, 190);  // [2]
-	const Common::Rect kBtnPartner (  5,  80,  44, 110);  // [3]
-	const Common::Rect kBtnAccuse  (180, 174, 201, 190);  // [4]
-	const Common::Rect kBtnGallery (204, 174, 224, 190);  // [5]
-	const Common::Rect kBtnPageNext(226, 174, 247, 190);  // [6]
-	const Common::Rect kBtnMap     (  7, 177,  57, 200);  // [7]
-	const Common::Rect kBtnSite    ( 35, 111,  56, 136);  // [8]
-	const Common::Rect kNoteArea   ( 66,  79, 267, 174);  // [10]
-	(void)kBtnHelp1; (void)kBtnPagePrev; (void)kBtnPageNext;
+	const Common::Rect kBtnHelp1   ( 93, 174, 115, 190);  // [1] HELP
+	const Common::Rect kBtnGallery (157, 174, 178, 190);  // [2] GALLERY
+	const Common::Rect kBtnPartner (  5,  80,  44, 110);  // [3] KD HELP
+	const Common::Rect kBtnAccuse  (180, 174, 201, 190);  // [4] SOLVE
+	const Common::Rect kBtnPageNext(204, 174, 224, 190);  // [5] PAGE NEXT
+	const Common::Rect kBtnPagePrev(226, 174, 247, 190);  // [6] PAGE PREV
+	const Common::Rect kBtnMap     (  7, 177,  57, 200);  // [7] MAP
+	const Common::Rect kBtnSite    ( 35, 111,  56, 136);  // [8] SITE
+	const Common::Rect kNoteArea   ( 66,  79, 267, 174);  // [10] note area
 
 	CursorMan.showMouse(true);
 
@@ -1593,22 +1675,16 @@ void EEMEngine::doNotebook() {
 			}
 			if (txt.empty())
 				txt = Common::String::format("clue %u", clueId);
-			// Compute wrapped lines first to know the rect.
+			// Per `_DrawNotes @ 161e:01d0`: text uses
+			// `_NoteUnselectedColor` (0x5c=cyan) for unselected and 0x3c
+			// (light yellow-white) for selected. Both contrast cleanly
+			// with the PDA screen's natural blue, so we draw text
+			// directly on PIC 0x3f without an extra fill rectangle ???
+			// matches the original design.
 			Common::Array<Common::String> wrapped;
 			_font.wordWrapText(txt, kRectW, wrapped);
 			const int lineH = _font.getFontHeight() + 1;
 			const int h = (int)wrapped.size() * lineH;
-
-			// Per `_DrawNotes @ 161e:01d0`: text uses
-			// `_NoteUnselectedColor` (0x5c=cyan) for unselected and 0x3c
-			// (light yellow-white) for selected. Paint a dark "paper"
-			// rectangle behind the text first ??? without this, the
-			// notebook BG (PIC 0x3f) bleeds through and makes the cyan
-			// text hard to read on lighter pixel runs. The fill colour
-			// 0x20 maps to a dark navy across all site palettes.
-			scratch.fillRect(Common::Rect(kRectX - 2, y - 1,
-				kRectX + kRectW + 2, y + h + 1), 0x20);
-
 			const byte color = _mystery._noteSelected[clueId] ? 0x3C : 0x5C;
 			for (uint li = 0; li < wrapped.size(); li++) {
 				_font.drawString(&scratch, wrapped[li], kRectX,
@@ -1620,10 +1696,7 @@ void EEMEngine::doNotebook() {
 			y += h + 7;
 		}
 
-		// Page indicator + selected-points counter. Paint a small dark
-		// strip behind them too so the text reads on the PDA frame's
-		// upper-right corner.
-		scratch.fillRect(Common::Rect(266, 0, 320, 24), 0x20);
+		// Page indicator + selected-points counter directly on PIC.
 		_font.drawString(&scratch, Common::String::format("p%d/%d",
 								   page + 1, (int)pageStarts.size()),
 						 270, 4, 320, 0x5C);
@@ -1704,6 +1777,13 @@ void EEMEngine::doNotebook() {
 					dirty = true;
 					continue;
 				}
+				if (kBtnHelp1.contains(ev.mouse.x, ev.mouse.y)) {
+					// rect 1 ??? `_InterfaceHelp(0)`. We use doHelp() to
+					// surface the same kind of hint pane.
+					doHelp();
+					dirty = true;
+					continue;
+				}
 				if (kBtnPagePrev.contains(ev.mouse.x, ev.mouse.y)) {
 					if (page > 0) page--;
 					dirty = true;
@@ -1849,71 +1929,71 @@ void EEMEngine::doGallery() {
 			}
 		}
 
-		// Portraits.
+		// Portraits ??? `_DrawGallery @ 158f:0046` walks suspects 0..N-1
+		// and only renders those flagged in `_InGallery[NewOrder[i]]`.
+		// Undiscovered slots are left empty in the original. We render
+		// a darkened placeholder + "?" so the player has visual feedback
+		// that suspects exist but are still unknown.
+		uint discoveredCount = 0;
 		for (uint i = 0; i < num && i < Mystery::kGalleryCap; i++) {
-			slotRects[i] = Common::Rect();   // empty
+			slotRects[i] = Common::Rect();
 			slotSuspect[i] = -1;
 
 			const uint8 phys = _mystery._newOrder[i];
 			if (phys >= 5)
 				continue;
-			const bool discovered = _mystery._inGallery[phys] != 0;
-			if (!discovered)
-				continue;
-
-			const uint16 picId = READ_LE_UINT16(gd + i * 0x46);
-			if (picId == 0)
-				continue;
-			Picture portrait;
-			if (!_picsArchive.getPicture(picId, portrait))
-				continue;
-
 			const Slot &s = kGallerySlots[phys];
-			const int placeX = s.x;
-			const int placeY = s.y + (0x48 - portrait.surface.h);
-			const byte transp = (byte)(portrait.flags >> 8);
 
-			const int w = MIN<int>(portrait.surface.w, 320 - placeX);
-			const int h = MIN<int>(portrait.surface.h, 200 - placeY);
-			if (w <= 0 || h <= 0)
-				continue;
-			for (int row = 0; row < h; row++) {
-				const int dstY = placeY + row;
-				if (dstY < 0) continue;
-				const byte *src =
-					(const byte *)portrait.surface.getBasePtr(0, row);
-				byte *dst = (byte *)scratch.getBasePtr(0, dstY);
-				for (int col = 0; col < w; col++) {
-					const int dstX = placeX + col;
-					if (src[col] != transp)
-						dst[dstX] = src[col];
-				}
-			}
-
-			// Cache rect for hit-test.
-			slotRects[i] = Common::Rect(placeX, placeY,
-										 placeX + w, placeY + h);
-			slotSuspect[i] = (int)i;
+			const bool discovered = _mystery._inGallery[phys] != 0;
+			if (discovered) {
+				const uint16 picId = READ_LE_UINT16(gd + i * 0x46);
+				Picture portrait;
+				if (picId == 0 ||
+					!_picsArchive.getPicture(picId, portrait))
+					continue;
 
-			// Index label below portrait ??? original doesn't draw labels
-			// (the detail page does), but useful while MoreInfo isn't
-			// implemented.
-			if (_font.isLoaded()) {
-				Common::String label = Common::String::format("%u", i + 1);
-				_font.drawString(&scratch, label,
-								 placeX + portrait.surface.w / 2 - 3,
-								 placeY + portrait.surface.h + 2,
-								 320, 0x5C);
+				const int placeX = s.x;
+				const int placeY = s.y + (0x48 - portrait.surface.h);
+				const byte transp = (byte)(portrait.flags >> 8);
+				const int w = MIN<int>(portrait.surface.w, 320 - placeX);
+				const int h = MIN<int>(portrait.surface.h, 200 - placeY);
+				if (w <= 0 || h <= 0)
+					continue;
+				for (int row = 0; row < h; row++) {
+					const int dstY = placeY + row;
+					if (dstY < 0) continue;
+					const byte *src =
+						(const byte *)portrait.surface.getBasePtr(0, row);
+					byte *dst = (byte *)scratch.getBasePtr(0, dstY);
+					for (int col = 0; col < w; col++) {
+						const int dstX = placeX + col;
+						if (src[col] != transp)
+							dst[dstX] = src[col];
+					}
+				}
+				slotRects[i] = Common::Rect(placeX, placeY,
+											 placeX + w, placeY + h);
+				slotSuspect[i] = (int)i;
+				discoveredCount++;
+			} else {
+				// Undiscovered placeholder ??? small framed "?" box at
+				// (s.x, s.y) sized 0x40 ?? 0x48 (typical portrait size).
+				const int phW = 0x40, phH = 0x48;
+				const int phX = s.x, phY = s.y;
+				if (phX + phW <= 320 && phY + phH <= 200) {
+					scratch.fillRect(Common::Rect(phX, phY,
+						phX + phW, phY + phH), 0x20);
+					scratch.frameRect(Common::Rect(phX, phY,
+						phX + phW, phY + phH), 0x5C);
+					if (_font.isLoaded()) {
+						_font.drawString(&scratch, "?",
+							phX + phW / 2 - 3,
+							phY + phH / 2 - 4, phW, 0x5C);
+					}
+				}
 			}
 		}
-
-		// Header / hint line ??? KD's hint balloon would normally show here
-		// (`_DoAccuseGallery` plays a balloon at top), but the standalone
-		// `_DoGallery` doesn't. We add a small header for clarity.
-		if (_font.isLoaded()) {
-			_font.drawString(&scratch, "GALLERY", 60, 4, 256, 0x5C);
-			_font.drawString(&scratch, "Click suspect | ESC", 60, 188, 256, 0x5C);
-		}
+		(void)discoveredCount;
 
 		g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
 								   0, 0, 320, 200);
@@ -1938,6 +2018,52 @@ void EEMEngine::doGallery() {
 				}
 			}
 			if (ev.type == Common::EVENT_LBUTTONDOWN) {
+				// PDA bottom-bar buttons mirror `_NoteButtons @ 29be:0147`.
+				// `_DoGallery @ 158f:065b` shares the SAME button table
+				// with `_DoNotebook` (both call `_FindButton` against the
+				// 11-entry table at 0x147). `_HandleGalleryButton @
+				// 158f:05c0` dispatches via a different jump table
+				// (158f:0645). Verified gallery button mapping:
+				//   rect 0 (134,155) ??? 0x05ef = `_NextScreen = 4` (NOTEBOOK)
+				//   rect 1 (93,115)  ??? 0x0625 = `_InterfaceHelp` (HELP)
+				//   rect 2 (157,178) ??? 0x0638 = generic exit (no-op)
+				//   rect 3 (5,80)    ??? 0x061e = `_KDHelp` (host hint)
+				//   rect 4 (180,201) ??? 0x05ff = `_SolvedCheck` -> SOLVE
+				//   rect 5 (204,224) ??? 0x0638 = generic exit
+				//   rect 6 (226,247) ??? 0x0638 = generic exit
+				//   rect 7 (7,177)   ??? 0x05f7 = `_NextScreen = 2` (MAP)
+				//   rect 8 (35,111)  ??? 0x05e4 = `_NextScreen = 3` (SITE)
+				const Common::Rect kBtnSite    ( 35, 111,  56, 136); // [8] SITE
+				const Common::Rect kBtnMap     (  7, 177,  57, 200); // [7] MAP
+				const Common::Rect kBtnAccuse  (180, 174, 201, 190); // [4] SOLVE
+				const Common::Rect kBtnNotebook(134, 174, 155, 190); // [0] NOTEBOOK (back to PDA notes)
+				const Common::Rect kBtnHelp    ( 93, 174, 115, 190); // [1] HELP
+				const Common::Rect kBtnPartner (  5,  80,  44, 110); // [3] KD HELP
+				if (kBtnSite.contains(ev.mouse.x, ev.mouse.y)) {
+					exitFlag = true; break;
+				}
+				if (kBtnMap.contains(ev.mouse.x, ev.mouse.y)) {
+					doBigMap();
+					exitFlag = true; break;
+				}
+				if (kBtnAccuse.contains(ev.mouse.x, ev.mouse.y)) {
+					doAccuse();
+					exitFlag = true; break;
+				}
+				if (kBtnNotebook.contains(ev.mouse.x, ev.mouse.y)) {
+					// Already came from notebook; exiting returns to it.
+					exitFlag = true; break;
+				}
+				if (kBtnHelp.contains(ev.mouse.x, ev.mouse.y)) {
+					doHelp();
+					lastDraw = 0;
+					continue;
+				}
+				if (kBtnPartner.contains(ev.mouse.x, ev.mouse.y)) {
+					doHelp();
+					lastDraw = 0;
+					continue;
+				}
 				// `_SearchSuspects` walks the per-slot rects and returns
 				// the suspect index. We mirror that with cached rects.
 				bool clicked = false;
@@ -1994,15 +2120,11 @@ void EEMEngine::doGallery() {
 							}
 						}
 						// Suspect's clue notes inside _GalleryNoteRect
-						// = (78, 93, 288, 152), per 29be:0100.
+						// = (78, 93, 288, 152), per 29be:0100. Cyan text
+						// renders directly on the PDA's natural blue
+						// screen ??? matches `_DrawGalleryNotes @ 158f:01f4`.
 						const int rx = 78, ry = 93;
 						const int rw = 288 - 78, rh = 152 - 93;
-						// Paint a dark fill behind the entire notes area
-						// so cyan text on a possibly-cyan PIC background
-						// stays readable. Color 0x20 = dark navy in all
-						// site palettes.
-						ms.fillRect(Common::Rect(rx - 2, ry - 12,
-							rx + rw + 2, ry + rh + 12), 0x20);
 
 						const byte *ni = _mystery.noteIndex();
 						const uint16 niCount = _mystery.noteIndexCount();
@@ -2597,65 +2719,127 @@ void EEMEngine::doAccuse() {
 	if (!_mystery.isLoaded())
 		return;
 
-	// Mirrors `_DoAccuseGallery` @ 1df2:0a31. Render gallery + prompt,
-	// accept either keyboard 1..N or a click on a suspect's portrait.
+	// Mirrors `_DoAccuseGallery @ 1df2:0a31`:
+	//   1. Show KD's hint balloon (KDTextIndex[+8] text).
+	//   2. `_GetBackground(0x3f)` ??? same backdrop as PDA / gallery.
+	//   3. `_DrawGallery()` ??? renders portraits at the standard 5 slots
+	//      (positions verified at 29be:0x116, bottom-aligned baseline 0x48).
+	//   4. Click loop dispatching on `_NoteButtons` (same table as PDA)
+	//      with a separate `_HandleAccuseNoteButton` jump table.
 	const uint8 num = _mystery.numSuspects();
 	if (num == 0)
 		return;
 
 	const byte *gd = _mystery.galleryData();
-	const int slotStep = 320 / MAX<uint8>(1, num);
-	const int slotY    = 24;
 
-	// Mirrors `_DoAccuseGallery`: load PIC 0x3f as the accuse backdrop.
+	// Verbatim from 29be:0x116 ??? same five suspect slot positions as
+	// `_DrawGallery @ 158f:0046`.
+	struct Slot { int x; int y; };
+	static const Slot kGallerySlots[5] = {
+		{  83,  14 }, { 155,  14 }, { 227,  14 },
+		{ 119,  90 }, { 191,  90 }
+	};
+
 	Picture accuseBg;
 	const bool haveAccuseBg = _picsArchive.getPicture(0x3f, accuseBg);
 
+	Common::Array<Common::Rect> slotRects;
+	Common::Array<int> slotSuspect;
+	slotRects.resize(num);
+	slotSuspect.resize(num);
+	for (uint i = 0; i < num; i++)
+		slotSuspect[i] = -1;
+
 	auto drawGallery = [&]() {
 		Graphics::ManagedSurface scratch(320, 200,
 			Graphics::PixelFormat::createFormatCLUT8());
 		scratch.clear();
 		if (haveAccuseBg) {
-			const int w = MIN<int>(accuseBg.surface.w, 320);
-			const int h = MIN<int>(accuseBg.surface.h, 200);
-			for (int row = 0; row < h; row++) {
+			const int bw = MIN<int>(accuseBg.surface.w, 320);
+			const int bh = MIN<int>(accuseBg.surface.h, 200);
+			for (int row = 0; row < bh; row++) {
 				memcpy((byte *)scratch.getBasePtr(0, row),
-					   (const byte *)accuseBg.surface.getBasePtr(0, row), w);
+					   (const byte *)accuseBg.surface.getBasePtr(0, row), bw);
 			}
 		}
-		if (_font.isLoaded())
-			_font.drawString(&scratch, "ACCUSE", 8, 4, 320, 0xF);
 
-		for (uint i = 0; i < num; i++) {
+		for (uint i = 0; i < num && i < Mystery::kGalleryCap; i++) {
+			slotRects[i] = Common::Rect();
+			slotSuspect[i] = -1;
 			if (!gd) continue;
+			const uint8 phys = _mystery._newOrder[i];
+			if (phys >= 5) continue;
+			const Slot &s = kGallerySlots[phys];
+
 			const uint16 picId = READ_LE_UINT16(gd + i * 0x46);
-			if (picId == 0)
-				continue;
+			if (picId == 0) continue;
 			Picture portrait;
 			if (!_picsArchive.getPicture(picId, portrait))
 				continue;
-			const int placeX = i * slotStep +
-							   (slotStep - portrait.surface.w) / 2;
-			const int placeY = slotY;
+
+			const int placeX = s.x;
+			const int placeY = s.y + (0x48 - portrait.surface.h);
+			const byte transp = (byte)(portrait.flags >> 8);
 			const int w = MIN<int>(portrait.surface.w, 320 - placeX);
 			const int h = MIN<int>(portrait.surface.h, 200 - placeY);
-			if (w > 0 && h > 0) {
-				for (int row = 0; row < h; row++)
-					memcpy((byte *)scratch.getBasePtr(placeX, placeY + row),
-						   (const byte *)portrait.surface.getBasePtr(0, row), w);
-			}
-			if (_font.isLoaded()) {
-				Common::String label = Common::String::format("%u", i + 1);
-				_font.drawString(&scratch, label, placeX + 4, placeY + h + 2, 320, 0xF);
+			if (w <= 0 || h <= 0) continue;
+			for (int row = 0; row < h; row++) {
+				const int dstY = placeY + row;
+				if (dstY < 0) continue;
+				const byte *src =
+					(const byte *)portrait.surface.getBasePtr(0, row);
+				byte *dst = (byte *)scratch.getBasePtr(0, dstY);
+				for (int col = 0; col < w; col++) {
+					const int dstX = placeX + col;
+					if (src[col] != transp)
+						dst[dstX] = src[col];
+				}
 			}
+			slotRects[i] = Common::Rect(placeX, placeY,
+										 placeX + w, placeY + h);
+			slotSuspect[i] = (int)i;
 		}
-		if (_font.isLoaded()) {
-			_font.drawString(&scratch, "Click a suspect or press 1..N - ESC to cancel", 8, 180, 320, 0xF);
-		}
+
 		g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
 								   0, 0, 320, 200);
 		g_system->updateScreen();
 	};
+
+	// Step 1 ??? KD hint balloon. `KDTextIndex[+8]` = third hint slot
+	// (offset 8 from the index array).
+	const byte *kdIdx = _mystery.kdTextIndex();
+	if (kdIdx) {
+		const int16 textOff = (int16)READ_LE_UINT16(kdIdx + 8);
+		if (textOff != -1) {
+			const char *raw = _mystery.textAt((uint16)textOff);
+			Common::String hint =
+				parseString(raw ? raw : "", _playerName, _partner);
+			if (!hint.empty()) {
+				// Mini ClueBlock: 1 entry, partner-0 fields. Compose
+				// just the balloon at (0x21, 0x10) (default position
+				// from `_DoAccuseGallery`).
+				Graphics::ManagedSurface ms(320, 200,
+					Graphics::PixelFormat::createFormatCLUT8());
+				ms.clear();
+				if (haveAccuseBg) {
+					const int bw = MIN<int>(accuseBg.surface.w, 320);
+					const int bh = MIN<int>(accuseBg.surface.h, 200);
+					for (int row = 0; row < bh; row++) {
+						memcpy((byte *)ms.getBasePtr(0, row),
+							   (const byte *)accuseBg.surface.getBasePtr(0, row), bw);
+					}
+				}
+				if (_font.isLoaded()) {
+					_font.drawWordWrapped(&ms, 0x52, 0x14, 218, hint, 0x5C);
+				}
+				g_system->copyRectToScreen(ms.getPixels(), ms.pitch,
+					0, 0, 320, 200);
+				g_system->updateScreen();
+				waitForInput(8000);
+			}
+		}
+	}
+
 	drawGallery();
 
 	int picked = -1;
@@ -2676,10 +2860,13 @@ void EEMEngine::doAccuse() {
 				}
 			}
 			if (ev.type == Common::EVENT_LBUTTONDOWN) {
-				const int slot = ev.mouse.x / slotStep;
-				if (slot >= 0 && slot < (int)num &&
-					ev.mouse.y >= slotY && ev.mouse.y < slotY + 120)
-					picked = slot;
+				for (uint i = 0; i < slotRects.size(); i++) {
+					if (slotSuspect[i] < 0) continue;
+					if (slotRects[i].contains(ev.mouse.x, ev.mouse.y)) {
+						picked = (int)i;
+						break;
+					}
+				}
 			}
 		}
 		g_system->updateScreen();
diff --git a/engines/eem/mystery.h b/engines/eem/mystery.h
index b0ea78677e6..96da8666f1b 100644
--- a/engines/eem/mystery.h
+++ b/engines/eem/mystery.h
@@ -101,6 +101,18 @@ public:
 	/// host hint lines.
 	const byte *kdTextIndex() const;
 
+	/// Pointer to the HintBlock; per-clue hint TextBlock offsets indexed
+	/// by `_aChain[i]` (the Nth required clue). Mirrors the
+	/// `_HintBlock` global read in `_KDHelp @ 1560:010a`.
+	const byte *hintBlock() const;
+
+	/// Read entry @p i from `_aChain` (the required-clue chain). Returns
+	/// 0xFFFF when no entry exists. Used by `_KDHelp` to walk unfound
+	/// clues for hints.
+	uint16 aChain(uint i) const {
+		return i < kChainLen ? _aChain[i] : 0xFFFF;
+	}
+
 	/// Pointer to the MapData entry for site @p siteNum (14 bytes per
 	/// entry; first u16 = sitepic, +4..7 = (x, y) on the big map).
 	const byte *mapEntry(uint siteNum) const;


Commit: 7be086ee1b8206cebbfe0ec0a9a065431c0c100b
    https://github.com/scummvm/scummvm/commit/7be086ee1b8206cebbfe0ec0a9a065431c0c100b
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:20:38+02:00

Commit Message:
EEM: polish PDA features: help interface added

Changed paths:
    engines/eem/eem.cpp
    engines/eem/eem.h


diff --git a/engines/eem/eem.cpp b/engines/eem/eem.cpp
index 554c588ab32..618c43862a3 100644
--- a/engines/eem/eem.cpp
+++ b/engines/eem/eem.cpp
@@ -1778,9 +1778,9 @@ void EEMEngine::doNotebook() {
 					continue;
 				}
 				if (kBtnHelp1.contains(ev.mouse.x, ev.mouse.y)) {
-					// rect 1 ??? `_InterfaceHelp(0)`. We use doHelp() to
-					// surface the same kind of hint pane.
-					doHelp();
+					// rect 1 ??? `_InterfaceHelp(0)`: walks `HelpData[0]` and
+					// blits PICs 0x63 / 0x1ae fullscreen for click-through.
+					doInterfaceHelp(0);
 					dirty = true;
 					continue;
 				}
@@ -2055,7 +2055,10 @@ void EEMEngine::doGallery() {
 					exitFlag = true; break;
 				}
 				if (kBtnHelp.contains(ev.mouse.x, ev.mouse.y)) {
-					doHelp();
+					// Gallery rect 1 ??? `_InterfaceHelp(0)` per jmp table at
+					// 158f:0625 (HandleGalleryButton). Same picture sequence
+					// as the notebook HELP button.
+					doInterfaceHelp(0);
 					lastDraw = 0;
 					continue;
 				}
@@ -2648,6 +2651,91 @@ void EEMEngine::doHelp() {
 	waitForInput(60000);
 }
 
+void EEMEngine::doInterfaceHelp(uint num) {
+	// Mirrors `_InterfaceHelp(num)` @ 1560:0205. The original walks
+	// `HelpData @ 29be:00c8` (5-byte entries: u8 count, then up to 2
+	// u16 picIds), `_GetPicture`s each one, blits it fullscreen via
+	// `_Rect_Move_Mask(0, 0, ...)`, and waits for click / key. ESC ends
+	// the cycle; any other input advances to the next pic.
+	//
+	// Verified from Ghidra HelpData bytes:
+	//   entry 0 (PDA / gallery HELP button): count=2, picIds = 0x0063, 0x01ae
+	//   entry 1: count=2, picIds = 0x0192, 0x01b1
+	// Only entry 0 is reachable from the PDA notebook (rect 1) and the
+	// gallery (rect 1) ??? both call `_InterfaceHelp(0)`.
+	static const uint16 kHelpPics[][2] = {
+		{ 0x0063, 0x01ae },
+		{ 0x0192, 0x01b1 },
+	};
+	if (num >= ARRAYSIZE(kHelpPics))
+		return;
+
+	debugC(1, kDebugScript, "doInterfaceHelp(%u): showing pics 0x%x, 0x%x",
+		   num, kHelpPics[num][0], kHelpPics[num][1]);
+
+	for (uint i = 0; i < 2; i++) {
+		const uint16 picId = kHelpPics[num][i];
+		Picture pic;
+		if (!_picsArchive.getPicture(picId, pic)) {
+			warning("doInterfaceHelp: getPicture(0x%x) failed", picId);
+			continue;
+		}
+		debugC(1, kDebugScript, "doInterfaceHelp: pic 0x%x = %dx%d flags=0x%x",
+			   picId, pic.surface.w, pic.surface.h, pic.flags);
+
+		// Compose a 320x200 frame (cleared) and blit the help pic at (0,0)
+		// with the original's masked-blit semantics: pixels equal to the
+		// pic's sub-mode (high byte of `pic[0]`, see `_Rect_Move_Mask`
+		// param_10 at 1000:03fc) are treated as transparent and skipped.
+		Graphics::ManagedSurface scratch(320, 200,
+			Graphics::PixelFormat::createFormatCLUT8());
+		scratch.clear();
+		const byte transp = (byte)(pic.flags >> 8);
+		const int w = MIN<int>(pic.surface.w, 320);
+		const int h = MIN<int>(pic.surface.h, 200);
+		for (int row = 0; row < h; row++) {
+			const byte *src = (const byte *)pic.surface.getBasePtr(0, row);
+			byte *dst = (byte *)scratch.getBasePtr(0, row);
+			for (int col = 0; col < w; col++) {
+				if (src[col] != transp)
+					dst[col] = src[col];
+			}
+		}
+		g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
+								   0, 0, 320, 200);
+		g_system->updateScreen();
+
+		bool escape = false;
+		while (!shouldQuit() && !escape) {
+			Common::Event ev;
+			bool advance = false;
+			while (g_system->getEventManager()->pollEvent(ev)) {
+				if (ev.type == Common::EVENT_QUIT ||
+					ev.type == Common::EVENT_RETURN_TO_LAUNCHER) {
+					return;
+				}
+				if (ev.type == Common::EVENT_LBUTTONDOWN) {
+					advance = true;
+					break;
+				}
+				if (ev.type == Common::EVENT_KEYDOWN) {
+					if (ev.kbd.keycode == Common::KEYCODE_ESCAPE)
+						escape = true;
+					else
+						advance = true;
+					break;
+				}
+			}
+			if (advance || escape)
+				break;
+			g_system->updateScreen();
+			g_system->delayMillis(15);
+		}
+		if (escape)
+			break;
+	}
+}
+
 bool EEMEngine::areYouSure() {
 	// Mirrors `_AreYouSure` @ 1a35:0a5c. Original loads PIC 0x136 for the
 	// dialog body and PIC 0x1FD/0x1FE for YES/NO. We render a minimal
@@ -2805,8 +2893,24 @@ void EEMEngine::doAccuse() {
 		g_system->updateScreen();
 	};
 
-	// Step 1 ??? KD hint balloon. `KDTextIndex[+8]` = third hint slot
-	// (offset 8 from the index array).
+	// Step 1 ??? KD hint balloon. Mirrors `_DoAccuseGallery @ 1df2:0a31`
+	// (1df2:0a4c-1df2:0afe):
+	//   text       = TextBlock + KDTextIndex[+8]
+	//   bub        = _GetKDTextBalloon(text)              ??? 1df2:0105
+	//   GetBalloon(bub)                                   ??? pic in [BP-6:-8]
+	//   if (pic.h < 0x4e)  y = (0x50 - pic.h) >> 1   else y = 1   (1df2:0a8b)
+	//   AddPicBackground(pic, 0x21, y)                    (1df2:0aab)
+	//   WordWrap(0x21 + tbl[bub].x, y + tbl[bub].y, tbl[bub].w, text, color=0, -1)
+	//   tbl base = 29be:0875, 10-byte entries: x at +0, y at +2, w at +4
+	//
+	// `_GetKDTextBalloon` (1df2:0105): when (ctype[firstChar] & 2) == 0 ???
+	//   bub = *(u16*)29be:1068 = 0x0017                   (verified)
+	// else                                                 ??? table lookup
+	//   bub = *(u16*)(29be:0fe6 + 0x1e + firstChar*2)
+	// Bit 1 in Borland's ctype = punctuation, so non-punctuation (most
+	// letters / control bytes / digits) takes the constant-balloon path.
+	// We use 0x17 as the default; the table-lookup path covers a small
+	// minority of texts (those starting with `,` `.` `:` etc.).
 	const byte *kdIdx = _mystery.kdTextIndex();
 	if (kdIdx) {
 		const int16 textOff = (int16)READ_LE_UINT16(kdIdx + 8);
@@ -2815,9 +2919,19 @@ void EEMEngine::doAccuse() {
 			Common::String hint =
 				parseString(raw ? raw : "", _playerName, _partner);
 			if (!hint.empty()) {
-				// Mini ClueBlock: 1 entry, partner-0 fields. Compose
-				// just the balloon at (0x21, 0x10) (default position
-				// from `_DoAccuseGallery`).
+				// 29be:1068 = 0x0017, the non-punctuation default.
+				const uint16 bubNum = 0x17;
+				Picture balloon;
+				const bool haveBalloon =
+					_balloonArchive.size() > (bubNum & 0x7F) &&
+					_balloonArchive.loadEntry(bubNum & 0x7F, balloon);
+
+				// 1df2:0a8b-1df2:0aa5: y = (h < 0x4e) ? (0x50-h)>>1 : 1
+				const int balloonX = 0x21;
+				int balloonY = 1;
+				if (haveBalloon && balloon.surface.h < 0x4e)
+					balloonY = (0x50 - balloon.surface.h) / 2;
+
 				Graphics::ManagedSurface ms(320, 200,
 					Graphics::PixelFormat::createFormatCLUT8());
 				ms.clear();
@@ -2829,8 +2943,29 @@ void EEMEngine::doAccuse() {
 							   (const byte *)accuseBg.surface.getBasePtr(0, row), bw);
 					}
 				}
-				if (_font.isLoaded()) {
-					_font.drawWordWrapped(&ms, 0x52, 0x14, 218, hint, 0x5C);
+				// `_Rect_Move_Mask` (1000:03fc) ??? pixels == pic[0]>>8 are
+				// transparent (verified at displayClue site).
+				if (haveBalloon) {
+					const byte transp = (byte)(balloon.flags >> 8);
+					const int bw = MIN<int>(balloon.surface.w, 320 - balloonX);
+					const int bh = MIN<int>(balloon.surface.h, 200 - balloonY);
+					for (int row = 0; row < bh; row++) {
+						const byte *src = (const byte *)balloon.surface.getBasePtr(0, row);
+						byte *dst = (byte *)ms.getBasePtr(balloonX, balloonY + row);
+						for (int col = 0; col < bw; col++) {
+							if (src[col] != transp)
+								dst[col] = src[col];
+						}
+					}
+				}
+				// 29be:0875 entry 23 (= 0x17): bytes `05 00 04 00 9b 00 96 00 23 00`
+				// ??? x=5, y=4, w=155 (verified). 1df2:0acb pushes color=0.
+				if (haveBalloon && _font.isLoaded()) {
+					_font.drawWordWrapped(&ms, balloonX + 5, balloonY + 4, 155,
+										  hint, 0);
+				} else if (_font.isLoaded()) {
+					_font.drawWordWrapped(&ms, balloonX + 5, balloonY + 4, 155,
+										  hint, 0xF);
 				}
 				g_system->copyRectToScreen(ms.getPixels(), ms.pitch,
 					0, 0, 320, 200);
diff --git a/engines/eem/eem.h b/engines/eem/eem.h
index 85c25b26c22..2e69fd2347c 100644
--- a/engines/eem/eem.h
+++ b/engines/eem/eem.h
@@ -104,6 +104,11 @@ public:
 	/// the original engine tracks via `_SawHelpHint`.
 	void doHelp();
 
+	/// Display the interface-help picture sequence. Mirrors `_InterfaceHelp`
+	/// @ 1560:0205 ??? walks `HelpData @ 29be:00c8`, blits each pic fullscreen,
+	/// and waits for click / key (ESC ends the cycle).
+	void doInterfaceHelp(uint num = 0);
+
 	/// "Are you sure?" yes/no dialog. Mirrors `_AreYouSure` @ 1a35:0a5c.
 	/// Returns true if the user picked YES.
 	bool areYouSure();


Commit: d075832f54e402f46ab2e01b0905cfe9f0777914
    https://github.com/scummvm/scummvm/commit/d075832f54e402f46ab2e01b0905cfe9f0777914
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:20:39+02:00

Commit Message:
EEM: polish PDA features: acusations

Changed paths:
    engines/eem/eem.cpp
    engines/eem/eem.h


diff --git a/engines/eem/eem.cpp b/engines/eem/eem.cpp
index 618c43862a3..2a4778f0d76 100644
--- a/engines/eem/eem.cpp
+++ b/engines/eem/eem.cpp
@@ -2736,6 +2736,60 @@ void EEMEngine::doInterfaceHelp(uint num) {
 	}
 }
 
+uint16 EEMEngine::getKDTextBalloon(byte firstChar) const {
+	// Mirrors `_GetKDTextBalloon @ 1df2:0105`:
+	//   if ((ctype[firstChar] & 2) == 0)  bub = *(u16*)29be:1068 = 0x17
+	//   else                              bub = *(u16*)(29be:0fe6+0x1e+c*2)
+	// `ctype` is Borland's `_ctype_` array at `29be:2be1`. Bit 1 (0x02) is
+	// set only for digits '0'..'9' (verified by reading the table ??? '0'..'9'
+	// each map to byte 0x02; everything else has bit 1 clear).
+	// Lookup table at 29be:1064 (= 29be:0fe6 + 0x1e + '0'*2):
+	//   '0'???0x15  '1'???0x16  '2'???0x17  '3'???0x18  '4'???0x19
+	//   '5'???0x1a  '6'???0x20  '7'???0x21  '8'???0x22  '9'???0x1e
+	// Note `*(u16*)29be:1068` (= entry for '2') is the same byte the
+	// non-digit fallback returns ??? the original encodes the constant by
+	// reusing the digit-2 slot.
+	if (firstChar < '0' || firstChar > '9')
+		return 0x17;
+	static const uint16 kDigitBalloons[10] = {
+		0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x20, 0x21, 0x22, 0x1e
+	};
+	return kDigitBalloons[firstChar - '0'];
+}
+
+bool EEMEngine::getBalloonInsets(uint16 bubNum, uint16 &xInset,
+								  uint16 &yInset, uint16 &textW) const {
+	// 52-entry, 10-bytes-each balloon-metadata table at `29be:0875`.
+	// Used at 1df2:0aef-0af9 (accuse hint) and `_DisplayClue` to position
+	// `_WordWrap` text inside the balloon. Only +0/+2/+4 are read here:
+	//   +0..1 = text X inset, +2..3 = Y inset, +4..5 = max wrap width
+	// (+6/+8 = balloon h / tail offset, both unused for text layout).
+	static const struct { uint16 x, y, w; } kTable[] = {
+		{ 6, 4, 142 }, { 6, 4, 142 }, { 6, 4, 142 }, { 6, 4, 142 },
+		{ 6, 4, 142 }, { 6, 4, 142 }, { 6, 4, 142 },
+		{ 6, 4, 224 }, { 6, 4, 224 }, { 6, 4, 224 }, { 6, 4, 224 },
+		{ 6, 4, 224 }, { 6, 4, 224 }, { 6, 4, 224 },
+		{ 6, 4, 291 }, { 6, 4, 291 }, { 6, 4, 291 }, { 6, 4, 291 },
+		{ 6, 4, 291 }, { 6, 4, 291 }, { 6, 4, 291 },
+		{ 5, 4, 155 }, { 5, 4, 155 }, { 5, 4, 155 }, { 5, 4, 155 },
+		{ 5, 4, 155 }, { 5, 4, 155 }, { 5, 4, 155 },
+		{ 5, 4, 237 }, { 5, 4, 237 }, { 5, 4, 237 }, { 5, 4, 237 },
+		{ 5, 4, 237 }, { 5, 4, 237 }, { 5, 4, 237 },
+		{ 3, 4, 155 }, { 3, 4, 155 }, { 3, 4, 155 }, { 3, 4, 155 },
+		{ 3, 4, 155 }, { 3, 4, 155 }, { 3, 4, 155 },
+		{ 5, 4, 238 }, { 5, 4, 238 }, { 5, 4, 238 }, { 5, 4, 238 },
+		{ 5, 4, 238 }, { 5, 4, 238 }, { 5, 4, 238 },
+		{ 5, 8, 158 }, { 5, 8, 176 }, { 8, 7, 142 }
+	};
+	const uint idx = bubNum & 0x7F;
+	if (idx >= ARRAYSIZE(kTable))
+		return false;
+	xInset = kTable[idx].x;
+	yInset = kTable[idx].y;
+	textW  = kTable[idx].w;
+	return true;
+}
+
 bool EEMEngine::areYouSure() {
 	// Mirrors `_AreYouSure` @ 1a35:0a5c. Original loads PIC 0x136 for the
 	// dialog body and PIC 0x1FD/0x1FE for YES/NO. We render a minimal
@@ -2838,6 +2892,7 @@ void EEMEngine::doAccuse() {
 	for (uint i = 0; i < num; i++)
 		slotSuspect[i] = -1;
 
+	int highlighted = 0;
 	auto drawGallery = [&]() {
 		Graphics::ManagedSurface scratch(320, 200,
 			Graphics::PixelFormat::createFormatCLUT8());
@@ -2857,6 +2912,11 @@ void EEMEngine::doAccuse() {
 			if (!gd) continue;
 			const uint8 phys = _mystery._newOrder[i];
 			if (phys >= 5) continue;
+			// `_DrawGallery @ 158f:00b9` skips suspects whose
+			// `_InGallery[phys]` flag is 0 ??? that's the original gate
+			// (some suspects only become visible after being met or
+			// stay hidden after a wrong accusation removes them).
+			if (_mystery._inGallery[phys] == 0) continue;
 			const Slot &s = kGallerySlots[phys];
 
 			const uint16 picId = READ_LE_UINT16(gd + i * 0x46);
@@ -2888,6 +2948,19 @@ void EEMEngine::doAccuse() {
 			slotSuspect[i] = (int)i;
 		}
 
+		// Highlight indicator. The original moves the mouse cursor
+		// to the centre of the highlighted suspect via `_PutMouseInRect`
+		// (1df2:0b8e) ??? we draw a 1px outline in palette index 0xFE
+		// (within the marching-ants cycle range 0xF9..0xFE) which is
+		// unambiguously visible under any palette without warping the
+		// player's cursor.
+		if (highlighted >= 0 && highlighted < (int)slotRects.size() &&
+			!slotRects[highlighted].isEmpty()) {
+			Common::Rect r = slotRects[highlighted];
+			r.grow(1);
+			scratch.frameRect(r, 0xFE);
+		}
+
 		g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
 								   0, 0, 320, 200);
 		g_system->updateScreen();
@@ -2895,22 +2968,13 @@ void EEMEngine::doAccuse() {
 
 	// Step 1 ??? KD hint balloon. Mirrors `_DoAccuseGallery @ 1df2:0a31`
 	// (1df2:0a4c-1df2:0afe):
-	//   text       = TextBlock + KDTextIndex[+8]
-	//   bub        = _GetKDTextBalloon(text)              ??? 1df2:0105
-	//   GetBalloon(bub)                                   ??? pic in [BP-6:-8]
-	//   if (pic.h < 0x4e)  y = (0x50 - pic.h) >> 1   else y = 1   (1df2:0a8b)
+	//   text  = TextBlock + KDTextIndex[+8]               (1df2:0a4c-0a57)
+	//   bub   = _GetKDTextBalloon(text[0])                (1df2:0a6d)
+	//   GetBalloon(bub)                                   (1df2:0a7c)
+	//   y     = (h < 0x4e) ? (0x50 - h) >> 1 : 1          (1df2:0a8b-0aa5)
 	//   AddPicBackground(pic, 0x21, y)                    (1df2:0aab)
-	//   WordWrap(0x21 + tbl[bub].x, y + tbl[bub].y, tbl[bub].w, text, color=0, -1)
-	//   tbl base = 29be:0875, 10-byte entries: x at +0, y at +2, w at +4
-	//
-	// `_GetKDTextBalloon` (1df2:0105): when (ctype[firstChar] & 2) == 0 ???
-	//   bub = *(u16*)29be:1068 = 0x0017                   (verified)
-	// else                                                 ??? table lookup
-	//   bub = *(u16*)(29be:0fe6 + 0x1e + firstChar*2)
-	// Bit 1 in Borland's ctype = punctuation, so non-punctuation (most
-	// letters / control bytes / digits) takes the constant-balloon path.
-	// We use 0x17 as the default; the table-lookup path covers a small
-	// minority of texts (those starting with `,` `.` `:` etc.).
+	//   WordWrap(0x21+tbl[bub].x, y+tbl[bub].y, tbl[bub].w, text, color=0)
+	//     tbl @ 29be:0875, 10-byte entries (1df2:0ad6-0af1)
 	const byte *kdIdx = _mystery.kdTextIndex();
 	if (kdIdx) {
 		const int16 textOff = (int16)READ_LE_UINT16(kdIdx + 8);
@@ -2919,8 +2983,14 @@ void EEMEngine::doAccuse() {
 			Common::String hint =
 				parseString(raw ? raw : "", _playerName, _partner);
 			if (!hint.empty()) {
-				// 29be:1068 = 0x0017, the non-punctuation default.
-				const uint16 bubNum = 0x17;
+				// First-char dispatch via getKDTextBalloon (1df2:0105).
+				// Note: we pass the *parsed* first char; the original
+				// reads it BEFORE `_ParseString`, but the player-name /
+				// partner-name substitutions never start with digits, so
+				// the dispatch result is the same either way.
+				const byte firstChar =
+					hint.empty() ? (byte)0 : (byte)hint[0];
+				const uint16 bubNum = getKDTextBalloon(firstChar);
 				Picture balloon;
 				const bool haveBalloon =
 					_balloonArchive.size() > (bubNum & 0x7F) &&
@@ -2943,8 +3013,8 @@ void EEMEngine::doAccuse() {
 							   (const byte *)accuseBg.surface.getBasePtr(0, row), bw);
 					}
 				}
-				// `_Rect_Move_Mask` (1000:03fc) ??? pixels == pic[0]>>8 are
-				// transparent (verified at displayClue site).
+				// Masked balloon blit ??? `_Rect_Move_Mask` (1000:03fc)
+				// skips pixels equal to `pic[0] >> 8`.
 				if (haveBalloon) {
 					const byte transp = (byte)(balloon.flags >> 8);
 					const int bw = MIN<int>(balloon.surface.w, 320 - balloonX);
@@ -2958,14 +3028,13 @@ void EEMEngine::doAccuse() {
 						}
 					}
 				}
-				// 29be:0875 entry 23 (= 0x17): bytes `05 00 04 00 9b 00 96 00 23 00`
-				// ??? x=5, y=4, w=155 (verified). 1df2:0acb pushes color=0.
-				if (haveBalloon && _font.isLoaded()) {
-					_font.drawWordWrapped(&ms, balloonX + 5, balloonY + 4, 155,
-										  hint, 0);
-				} else if (_font.isLoaded()) {
-					_font.drawWordWrapped(&ms, balloonX + 5, balloonY + 4, 155,
-										  hint, 0xF);
+				// Inset table @ 29be:0875 ??? 1df2:0acb pushes color=0.
+				uint16 tx = 5, ty = 4, tw = 155;
+				getBalloonInsets(bubNum, tx, ty, tw);
+				if (_font.isLoaded()) {
+					_font.drawWordWrapped(&ms, balloonX + tx,
+										  balloonY + ty, tw, hint,
+										  haveBalloon ? 0 : 0xF);
 				}
 				g_system->copyRectToScreen(ms.getPixels(), ms.pitch,
 					0, 0, 320, 200);
@@ -2975,9 +3044,42 @@ void EEMEngine::doAccuse() {
 		}
 	}
 
+	// Helper to find the next "alive" slot (one whose `_inGallery[phys]`
+	// flag is still set so a portrait was actually drawn). Mirrors the
+	// way the original wraps DI past empty slots.
+	auto nextLiveSlot = [&](int from, int dir) -> int {
+		const int n = (int)slotRects.size();
+		if (n <= 0) return 0;
+		for (int step = 1; step <= n; step++) {
+			int idx = (from + dir * step) % n;
+			if (idx < 0) idx += n;
+			if (!slotRects[idx].isEmpty())
+				return idx;
+		}
+		return from;
+	};
+	if (slotRects[highlighted].isEmpty())
+		highlighted = nextLiveSlot(highlighted, +1);
+
 	drawGallery();
 
+	// Wait-for-pick loop. Mirrors `_DoAccuseGallery` 1df2:0b26-1df2:0bc8:
+	//   * `_CheckFrameRate` + `_UpdateAnimations` per tick (1df2:0b2a-0b33)
+	//   * 5-entry input dispatch table @ 1df2:0bc9:
+	//       0x09 (TAB)   ??? handler 0x0b94 (cycle highlight)
+	//       0x0d (Enter) ??? handler 0x0b72 (pick = _SearchSuspects)
+	//       0x4b (LEFT)  ??? handler 0x0b94
+	//       0x4d (RIGHT) ??? handler 0x0b94
+	//       0xFFFF (mb)  ??? handler 0x0b72
+	//   * 0x0b94: `INC DI` + wraparound + `_PutMouseInRect(&Guys[DI])`,
+	//     i.e. advance highlight and warp cursor (1df2:0b94-0bb1).
+	//   * 0x0b72: `_SearchSuspects` (158f:0584) ??? mouse-rect hit-test;
+	//     if non-0xFFFF, pick that suspect.
+	// We don't warp the cursor (unfriendly under SDL); instead the
+	// highlight is drawn as a 1px outline and Enter picks it.
 	int picked = -1;
+	uint32 lastTick = g_system->getMillis();
+	bool dirty = false;
 	while (picked < 0 && !shouldQuit()) {
 		Common::Event ev;
 		while (g_system->getEventManager()->pollEvent(ev)) {
@@ -2985,13 +3087,39 @@ void EEMEngine::doAccuse() {
 				ev.type == Common::EVENT_RETURN_TO_LAUNCHER)
 				return;
 			if (ev.type == Common::EVENT_KEYDOWN) {
-				if (ev.kbd.keycode == Common::KEYCODE_ESCAPE)
+				switch (ev.kbd.keycode) {
+				case Common::KEYCODE_ESCAPE:
 					return;
-				const int k = (int)ev.kbd.keycode;
-				if (k >= Common::KEYCODE_1 && k <= Common::KEYCODE_9) {
-					const int idx = k - Common::KEYCODE_1;
-					if (idx < num)
-						picked = idx;
+				case Common::KEYCODE_TAB:
+				case Common::KEYCODE_RIGHT:
+					highlighted = nextLiveSlot(highlighted, +1);
+					dirty = true;
+					break;
+				case Common::KEYCODE_LEFT:
+					// 1df2:0b94 increments DI for LEFT too ??? but a
+					// keyboard-driven UX is friendlier with separate
+					// directions, so we mirror Right=+1 / Left=-1.
+					highlighted = nextLiveSlot(highlighted, -1);
+					dirty = true;
+					break;
+				case Common::KEYCODE_RETURN:
+				case Common::KEYCODE_KP_ENTER:
+					if (highlighted >= 0 &&
+						highlighted < (int)slotRects.size() &&
+						!slotRects[highlighted].isEmpty()) {
+						picked = highlighted;
+					}
+					break;
+				default: {
+					const int k = (int)ev.kbd.keycode;
+					if (k >= Common::KEYCODE_1 && k <= Common::KEYCODE_9) {
+						const int idx = k - Common::KEYCODE_1;
+						if (idx < num &&
+							!slotRects[idx].isEmpty())
+							picked = idx;
+					}
+					break;
+				}
 				}
 			}
 			if (ev.type == Common::EVENT_LBUTTONDOWN) {
@@ -3004,6 +3132,16 @@ void EEMEngine::doAccuse() {
 				}
 			}
 		}
+		// 100 ms tick ??? the original calls `_UpdateAnimations` per
+		// `_CheckFrameRate` (1df2:0b33). The accuse screen has no
+		// animations registered, so the tick is just a redraw cadence.
+		// We still re-render whenever the highlight moves (`dirty`).
+		const uint32 now = g_system->getMillis();
+		if (dirty || now - lastTick >= 100) {
+			drawGallery();
+			lastTick = now;
+			dirty = false;
+		}
 		g_system->updateScreen();
 		g_system->delayMillis(10);
 	}
diff --git a/engines/eem/eem.h b/engines/eem/eem.h
index 2e69fd2347c..2bc741352ff 100644
--- a/engines/eem/eem.h
+++ b/engines/eem/eem.h
@@ -109,6 +109,20 @@ public:
 	/// and waits for click / key (ESC ends the cycle).
 	void doInterfaceHelp(uint num = 0);
 
+	/// First-char-dispatch balloon picker. Mirrors `_GetKDTextBalloon @
+	/// 1df2:0105`. For digit first chars (0..9) returns balloon from the
+	/// table at `29be:1064`; for any other char returns the constant
+	/// `*(u16*)29be:1068 = 0x17`. The original branch is on Borland's
+	/// ctype-bit-1 (= digit) at `29be:2be1 + char`.
+	uint16 getKDTextBalloon(byte firstChar) const;
+
+	/// Look up balloon-text-inset metadata. Mirrors the 52-entry table at
+	/// `29be:0875`, indexed by `(bubNum & 0x7F)`. 10 bytes per entry; only
+	/// the first 3 fields (x inset, y inset, text width) are used for
+	/// rendering. Returns false if `bubNum` is outside the table.
+	bool getBalloonInsets(uint16 bubNum, uint16 &xInset, uint16 &yInset,
+						  uint16 &textW) const;
+
 	/// "Are you sure?" yes/no dialog. Mirrors `_AreYouSure` @ 1a35:0a5c.
 	/// Returns true if the user picked YES.
 	bool areYouSure();


Commit: 2ae70bdb92a852327c9ecca085393194145902f0
    https://github.com/scummvm/scummvm/commit/2ae70bdb92a852327c9ecca085393194145902f0
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:20:39+02:00

Commit Message:
EEM: basic parners animations

Changed paths:
    engines/eem/eem.cpp
    engines/eem/eem.h
    engines/eem/site.cpp


diff --git a/engines/eem/eem.cpp b/engines/eem/eem.cpp
index 2a4778f0d76..cedef8690b7 100644
--- a/engines/eem/eem.cpp
+++ b/engines/eem/eem.cpp
@@ -1307,9 +1307,20 @@ void EEMEngine::displayClue(const byte *clueBlock) {
 		//   +8..9, +10..11: bubText offset for partner 0/1 (rel. TextBlock)
 		//   +12..13, +14..15: balloon picture ID for partner 0/1
 		//   +16..17, +18..19: bubX, bubY
+		//   +0x3a..+0x3b:    KD-anim number (-1 = none)
 		// Per `_DisplayClue` @ 2404:05e6: partner 1 uses its own field
 		// set ONLY when bubText1 is not -1; otherwise it falls back to
 		// the partner 0 fields entirely. Partner 0 always uses field 0.
+
+		// Per-clue partner reaction animation. `_DisplayClue` @
+		// 2404:0635-064b checks `clueEntry[+0x3a]` and, when not -1,
+		// calls `_DoKDAnim(num)` BEFORE drawing the speaker portrait.
+		// This is what surfaces "Jenny takes a picture with a camera"
+		// (and the matching Jake gestures) during NPC searches.
+		const int16 kdAnimNum = (int16)READ_LE_UINT16(c + 0x3a);
+		if (kdAnimNum != -1)
+			playKdAnim((uint16)kdAnimNum);
+
 		const bool useP1 = (_partner == 1) &&
 			(READ_LE_UINT16(c + 10) != 0xFFFF);
 		const uint partner = useP1 ? 1 : 0;
@@ -2757,6 +2768,190 @@ uint16 EEMEngine::getKDTextBalloon(byte firstChar) const {
 	return kDigitBalloons[firstChar - '0'];
 }
 
+void EEMEngine::setPartnerEraseBg(const Graphics::ManagedSurface *bg) {
+	if (bg && bg->w == 320 && bg->h == 200) {
+		_partnerEraseBg.create(320, 200,
+			Graphics::PixelFormat::createFormatCLUT8());
+		for (int row = 0; row < 200; row++) {
+			memcpy((byte *)_partnerEraseBg.getBasePtr(0, row),
+				   (const byte *)bg->getBasePtr(0, row), 320);
+		}
+	} else {
+		_partnerEraseBg.free();
+	}
+}
+
+void EEMEngine::playKdAnim(uint16 num) {
+	// Mirrors `_DoKDAnim(num) @ 168d:028a` + `_PlayAnimation @ 172b:1f46`:
+	//   _SuspendAnimation(WaitHandle);
+	//   anim   = WaitAnims[1+num].anim[partner]   (table @ 29be:0228)
+	//   x      = WaitAnims[1+num].x[partner]
+	//   y      = WaitAnims[1+num].y[partner]
+	//   _PlayAnimation(anim, x, y, WaitHandle)
+	//     ??? registers a state-4 (one-shot) animation slot and lets
+	//       `_UpdateAnimations` walk the sequence script until 0x80,
+	//       then frees this slot and re-activates `WaitHandle`.
+	// Our port renders the partner's idle inline in each redraw rather
+	// than via a slot system, so we play the one-shot synchronously here
+	// (blocking) and resume normal idle rendering when the caller
+	// returns. That matches the user-visible effect: the partner's
+	// gesture (Jenny taking a picture, etc.) finishes before the
+	// speaker portrait + speech balloon appear.
+	//
+	// Six valid kdAnimNum entries (0..5). Verified bytes from
+	// `29be:0228`. Layout per entry: { animJake, animJenny, xJake,
+	// xJenny, yJake, yJenny }. Position is (6, 80) in every entry.
+	static const uint16 kKdAnimTable[6][6] = {
+		{ 0x03, 0x0c, 6, 6, 80, 80 }, // 0 ??? speaker idx 1 wait anim
+		{ 0x01, 0x0b, 6, 6, 80, 80 }, // 1 ??? same as PDA idle
+		{ 0x04, 0x0d, 6, 6, 80, 80 }, // 2
+		{ 0x02, 0x10, 6, 6, 80, 80 }, // 3 ??? same as gallery
+		{ 0x05, 0x05, 6, 6, 80, 80 }, // 4 ??? same anim both partners
+		{ 0x06, 0x06, 6, 6, 80, 80 }, // 5 ??? same anim both partners
+	};
+	if (num >= ARRAYSIZE(kKdAnimTable))
+		return;
+
+	const uint partner = (_partner == 0) ? 0 : 1;
+	const uint16 animId = kKdAnimTable[num][partner];
+	const int    px     = (int)kKdAnimTable[num][2 + partner];
+	const int    py     = (int)kKdAnimTable[num][4 + partner];
+
+	Animation anim;
+	if (!_aniArchive.loadAnimation(animId, anim) || anim.empty()) {
+		warning("playKdAnim(%u): anim %u failed to load", num, animId);
+		return;
+	}
+
+	// Sequence-script lookup. Entries copied verbatim from
+	// `_AnimationSequences @ 29be:22d4` walked through to the next 0x80.
+	// Each script is a u16[] of frame indices terminated by 0x80; we
+	// don't yet handle 0x81 jumps (none of the kdAnim sequences use
+	// them ??? verified). seqnum == animId for these calls (per
+	// `_PlayAnimation` 172b:1f5d push order).
+	struct Script {
+		uint16 seqnum;
+		uint8 len;
+		uint8 frames[20];  // long enough for any kdAnim script
+	};
+	static const Script kScripts[] = {
+		// seqnum 1 (29be:188a) ??? head bob
+		{ 0x01, 15, { 0,1,2,0,1,0,2,1,0,1,0,1,2,1,0 } },
+		// seqnum 2 (29be:18aa) ??? short blip then long pause
+		{ 0x02, 16, { 0,1,2,1,0,0,0,0,0,0,0,0,0,0,0,0 } },
+		// seqnum 3 (29be:18e0) ??? Jake "lift, hold, lower" gesture
+		{ 0x03,  9, { 0,1,2,3,2,2,2,1,0 } },
+		// seqnum 4 (29be:18f4) ??? bigger gesture (camera flash-style)
+		{ 0x04, 13, { 0,1,2,3,4,5,4,4,4,3,2,1,0 } },
+		// seqnum 5 (29be:1910) ??? held idle with a single peak
+		{ 0x05, 13, { 0,0,0,1,2,3,2,1,0,0,0,0,0 } },
+		// seqnum 6 (29be:192c) ??? empty (immediate END)
+		{ 0x06,  0, { 0 } },
+		// seqnum 0xb (29be:188a, same as 1) ??? Jenny PDA idle
+		{ 0x0b, 15, { 0,1,2,0,1,0,2,1,0,1,0,1,2,1,0 } },
+		// seqnum 0xc (29be:18e0, same as 3) ??? Jenny "take a picture"
+		{ 0x0c,  9, { 0,1,2,3,2,2,2,1,0 } },
+		// seqnum 0xd (29be:18f4, same as 4) ??? Jenny big gesture
+		{ 0x0d, 13, { 0,1,2,3,4,5,4,4,4,3,2,1,0 } },
+		// seqnum 0x10 (29be:1956) ??? Jenny short anim
+		{ 0x10,  9, { 0,0,0,1,0,0,0,0,0 } },
+	};
+	const uint8 *frames = nullptr;
+	uint frameCount = 0;
+	for (uint i = 0; i < ARRAYSIZE(kScripts); i++) {
+		if (kScripts[i].seqnum == animId) {
+			frames = kScripts[i].frames;
+			frameCount = kScripts[i].len;
+			break;
+		}
+	}
+	if (frameCount == 0) {
+		// Fallback: linear playback through anim cells (better than
+		// nothing if a future kdAnim references an unscripted anim).
+		frameCount = (uint)anim.size();
+	}
+
+	// Erase-source for between-frame redraw. Prefer the partner-less
+	// backdrop the caller stashed via `setPartnerEraseBg` (e.g. the
+	// site's `_bgSnapshot`, which has the static drops + frame but no
+	// partner sprite). Without that, fall back to whatever's currently
+	// on screen ??? which works for full-screen contexts (PDA / accuse /
+	// briefing) where there is no separate idle partner overlay to
+	// erase, but produces visible "ghosting" against the site's idle
+	// partner cell at (6, 80) because it has the resting pose baked in.
+	Graphics::ManagedSurface bg(320, 200,
+		Graphics::PixelFormat::createFormatCLUT8());
+	if (_partnerEraseBg.w == 320 && _partnerEraseBg.h == 200) {
+		for (int row = 0; row < 200; row++) {
+			memcpy((byte *)bg.getBasePtr(0, row),
+				   (const byte *)_partnerEraseBg.getBasePtr(0, row), 320);
+		}
+	} else {
+		Graphics::Surface *screen = g_system->lockScreen();
+		if (!screen)
+			return;
+		for (int row = 0; row < 200; row++) {
+			memcpy((byte *)bg.getBasePtr(0, row),
+				   (const byte *)screen->getBasePtr(0, row), 320);
+		}
+		g_system->unlockScreen();
+	}
+
+	for (uint i = 0; i < frameCount && !shouldQuit(); i++) {
+		const uint frameIdx = frames ? (uint)frames[i] : i;
+		if (frameIdx >= anim.size())
+			continue;
+		const Picture &fr = anim[frameIdx];
+		const byte transp = (byte)(fr.flags >> 8);
+
+		// Restore BG, then masked-blit the next frame.
+		Graphics::ManagedSurface scratch(320, 200,
+			Graphics::PixelFormat::createFormatCLUT8());
+		for (int row = 0; row < 200; row++) {
+			memcpy((byte *)scratch.getBasePtr(0, row),
+				   (const byte *)bg.getBasePtr(0, row), 320);
+		}
+		const int w = MIN<int>(fr.surface.w, 320 - px);
+		const int h = MIN<int>(fr.surface.h, 200 - py);
+		for (int row = 0; row < h; row++) {
+			const int dstY = py + row;
+			if (dstY < 0) continue;
+			const byte *src = (const byte *)fr.surface.getBasePtr(0, row);
+			byte *dst = (byte *)scratch.getBasePtr(0, dstY);
+			for (int col = 0; col < w; col++) {
+				const int dstX = px + col;
+				if (dstX < 0) continue;
+				if (src[col] != transp)
+					dst[dstX] = src[col];
+			}
+		}
+		g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
+								   0, 0, 320, 200);
+		g_system->updateScreen();
+
+		// One frame per `_CheckFrameRate` tick. The original calibrates
+		// this to ~10 fps; 100 ms matches what the rest of the engine
+		// uses for partner / NPC frame cycling.
+		const uint32 wakeup = g_system->getMillis() + 100;
+		while (g_system->getMillis() < wakeup && !shouldQuit()) {
+			Common::Event ev;
+			while (g_system->getEventManager()->pollEvent(ev)) {
+				// Drain events but don't allow skipping mid-animation ???
+				// the speaker portrait + balloon haven't been drawn yet
+				// and a click would otherwise eat the upcoming clue.
+				if (ev.type == Common::EVENT_QUIT ||
+					ev.type == Common::EVENT_RETURN_TO_LAUNCHER)
+					return;
+			}
+			g_system->delayMillis(10);
+		}
+	}
+
+	// Restore BG so the next caller (speaker portrait blit) starts clean.
+	g_system->copyRectToScreen(bg.getPixels(), bg.pitch, 0, 0, 320, 200);
+	g_system->updateScreen();
+}
+
 bool EEMEngine::getBalloonInsets(uint16 bubNum, uint16 &xInset,
 								  uint16 &yInset, uint16 &textW) const {
 	// 52-entry, 10-bytes-each balloon-metadata table at `29be:0875`.
diff --git a/engines/eem/eem.h b/engines/eem/eem.h
index 2bc741352ff..7b5c26a4be7 100644
--- a/engines/eem/eem.h
+++ b/engines/eem/eem.h
@@ -116,6 +116,26 @@ public:
 	/// ctype-bit-1 (= digit) at `29be:2be1 + char`.
 	uint16 getKDTextBalloon(byte firstChar) const;
 
+	/// Play the partner's one-shot reaction animation slot @num. Mirrors
+	/// `_DoKDAnim @ 168d:028a` + `_PlayAnimation @ 172b:1f46`. The
+	/// per-partner (animId, x, y) come from `_WaitAnims[1+num] @ 29be:0228`,
+	/// and the per-frame timing follows the sequence script that the
+	/// original would index at `_AnimationSequences[seqnum=animId]`. Used
+	/// by `displayClue` when a ClueEntry's KD-anim field (+0x3a) is set ???
+	/// e.g. Jenny's "take a picture" gesture when the player searches an
+	/// NPC. Blocks until the script's first 0x80 marker.
+	void playKdAnim(uint16 num);
+
+	/// Provide a "clean" 320x200 backdrop for the next `playKdAnim` (and
+	/// any future blocking partner-anim playback) to use as the
+	/// background-erase source. Without this, the camera animation would
+	/// composite on top of the static partner sprite from the screen and
+	/// the previous resting frame would bleed through transparent pixels.
+	/// `SiteScreen` calls this with its `_bgSnapshot` (site BG + static
+	/// drops, no NPCs / partner) before invoking `displayClue` from a
+	/// hotspot click. Pass `nullptr` to clear.
+	void setPartnerEraseBg(const Graphics::ManagedSurface *bg);
+
 	/// Look up balloon-text-inset metadata. Mirrors the 52-entry table at
 	/// `29be:0875`, indexed by `(bubNum & 0x7F)`. 10 bytes per entry; only
 	/// the first 3 fields (x inset, y inset, text width) are used for
@@ -232,6 +252,13 @@ private:
 	/// inline; we cache the layout to keep click hit-testing simple.
 	Common::Array<Common::Rect> _notebookSlotRects;
 	Common::Array<uint>         _notebookSlotClues;
+
+	/// Optional clean BG (no partner / NPC sprites) used by `playKdAnim`
+	/// to erase the partner's resting frame between camera-anim cells.
+	/// Empty when no caller has provided one (PDA / case briefing /
+	/// accuse contexts use their own composed backdrops). See
+	/// `setPartnerEraseBg`.
+	Graphics::ManagedSurface _partnerEraseBg;
 };
 
 } // End of namespace EEM
diff --git a/engines/eem/site.cpp b/engines/eem/site.cpp
index 964f7707eaa..6cd2dd402c8 100644
--- a/engines/eem/site.cpp
+++ b/engines/eem/site.cpp
@@ -114,8 +114,15 @@ void SiteScreen::enter(uint siteNum) {
 			const uint16 clueOff = READ_LE_UINT16(idx + 2);
 			if (clueOff != 0xFFFF) {
 				const byte *clueBlock = _mystery->blobAt(clueOff);
-				if (clueBlock)
+				if (clueBlock) {
+					// See onHotspotClicked ??? supply a partner-less BG
+					// so KD-anim playback (e.g. the partner's arrival
+					// camera gesture) doesn't ghost over the resting
+					// idle frame.
+					_vm->setPartnerEraseBg(&_bgSnapshot);
 					_vm->displayClue(clueBlock);
+					_vm->setPartnerEraseBg(nullptr);
+				}
 			}
 		}
 		if (siteNum < Mystery::kVisitedSiteCap)
@@ -893,8 +900,18 @@ void SiteScreen::onHotspotClicked(uint siteNum, uint hotIdx) {
 		debugC(2, kDebugSite, "  hotspot %u -> clue offset 0x%04x",
 			   hotIdx, clueOff);
 		const byte *clueBlock = _mystery->blobAt(clueOff);
-		if (clueBlock)
+		if (clueBlock) {
+			// Hand the engine our partner-less backdrop so that
+			// `_DoKDAnim` / `playKdAnim` (the camera-style reaction
+			// animation that fires when a ClueEntry has +0x3a != -1)
+			// can erase the partner's resting frame between cells
+			// instead of compositing over it. Cleared after the call
+			// so accuse / briefing contexts fall back to their own
+			// snapshots.
+			_vm->setPartnerEraseBg(&_bgSnapshot);
 			_vm->displayClue(clueBlock);
+			_vm->setPartnerEraseBg(nullptr);
+		}
 	}
 	// Caller (`SiteScreen::run`) re-renders the site after this returns.
 }


Commit: 30a356bc07e43935d404112d7a87a002c13ab407
    https://github.com/scummvm/scummvm/commit/30a356bc07e43935d404112d7a87a002c13ab407
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:20:39+02:00

Commit Message:
EEM: more parners animations, for intro and overview map

Changed paths:
    engines/eem/eem.cpp
    engines/eem/site.cpp


diff --git a/engines/eem/eem.cpp b/engines/eem/eem.cpp
index cedef8690b7..594fba894ef 100644
--- a/engines/eem/eem.cpp
+++ b/engines/eem/eem.cpp
@@ -551,27 +551,74 @@ void EEMEngine::doChoosePartner() {
 	}
 
 	setAnmPalette(Common::Path("TITLE.ANM"));
-	blitAt(background, 0, 0);
-	blitAt(girlAnim[0], kGirlX, kGirlY);
-	blitAt(boyAnim[0], kBoyX, kBoyY);
-	g_system->updateScreen();
+
+	// `_DoHappiness @ 172b:27b5`: the cursor's X column picks one of 4
+	// rects (29be:030f, all full-height); past rect 3 ??? "level 4".
+	// Each level swaps the partner's sequence script to a more / less
+	// "happy" cycle. Boy seqs at 29be:0337 (5 ?? 0x14 bytes), girl seqs
+	// at 29be:039b. Both cycle through 9 frames (the boy/girl anim
+	// cells contain 10 cells = pairs of "neutral, smile" at increasing
+	// intensity). Lifted verbatim from the binary so the gestures
+	// match the original beat-for-beat.
+	static const Common::Rect kHappyZones[4] = {
+		Common::Rect(  0, 0,  70, 200), // far left  ??? girl very happy, boy neutral
+		Common::Rect( 70, 0, 126, 200), // girl's column
+		Common::Rect(126, 0, 182, 200), // middle
+		Common::Rect(182, 0, 235, 200), // boy's column
+	};
+	static const uint8 kBoySeqs[5][9] = {
+		{ 0,0,0,0,0,0,0,1,0 }, // level 0
+		{ 2,2,2,2,2,2,2,3,2 }, // level 1
+		{ 4,4,4,4,4,4,4,5,4 }, // level 2
+		{ 6,6,6,6,6,6,7,6,6 }, // level 3
+		{ 8,8,8,8,8,8,8,8,9 }, // level 4 (cursor past zone 3)
+	};
+	static const uint8 kGirlSeqs[5][9] = {
+		{ 8,9,8,8,8,8,8,8,8 },
+		{ 6,6,6,7,6,6,6,6,6 },
+		{ 4,4,5,4,4,4,4,4,4 },
+		{ 2,2,2,2,2,2,3,2,2 },
+		{ 0,0,0,0,0,1,0,0,0 },
+	};
+	auto happinessLevel = [](int x) {
+		for (uint i = 0; i < ARRAYSIZE(kHappyZones); i++) {
+			if (kHappyZones[i].contains(x, 100))
+				return (uint)i;
+		}
+		return 4u; // past zone 3 ??? max level
+	};
+
+	int curMouseX = 0xa0;  // _DoChoosePartner sets `_SetMousePos(0xa0, 0x96)` on entry
+	int curMouseY = 0x96;
+	uint level = happinessLevel(curMouseX);
+	uint seqIdx = 0;       // step within the 9-frame seq
+
+	auto draw = [&]() {
+		blitAt(background, 0, 0);
+		const uint girlFrame = kGirlSeqs[level][seqIdx % 9];
+		const uint boyFrame  = kBoySeqs [level][seqIdx % 9];
+		blitAt(girlAnim[girlFrame % girlAnim.size()], kGirlX, kGirlY);
+		blitAt(boyAnim[boyFrame  % boyAnim.size()],  kBoyX,  kBoyY);
+		g_system->updateScreen();
+	};
+	draw();
 
 	debugC(1, kDebugGeneral, "ChoosePartner: %u boy frames at (%d,%d), "
 		   "%u girl frames at (%d,%d)",
 		   (uint)boyAnim.size(), kBoyX, kBoyY,
 		   (uint)girlAnim.size(), kGirlX, kGirlY);
 
-	uint frame = 0;
 	uint32 lastTick = g_system->getMillis();
 	while (!shouldQuit()) {
-		// Advance frame at ~5 Hz so the animations cycle gently.
-		if (g_system->getMillis() - lastTick > 200) {
+		// Advance through the 9-frame seq at 100 ms ??? `_CheckFrameRate`
+		// cadence. The seq is short and loops; matches the original
+		// `_UpdateAnimations` which restarts at curIdx=0 on the 0x80
+		// marker. Mirrors `_DoHappiness`'s rewriting of `curIdx = 0xFFFF`
+		// when the cursor crosses zones (we restart `seqIdx` instead).
+		if (g_system->getMillis() - lastTick > 100) {
 			lastTick = g_system->getMillis();
-			frame++;
-			blitAt(background, 0, 0);
-			blitAt(girlAnim[frame % girlAnim.size()], kGirlX, kGirlY);
-			blitAt(boyAnim[frame % boyAnim.size()], kBoyX, kBoyY);
-			g_system->updateScreen();
+			seqIdx = (seqIdx + 1) % 9;
+			draw();
 		}
 
 		Common::Event ev;
@@ -582,6 +629,16 @@ void EEMEngine::doChoosePartner() {
 				done = true;
 				break;
 			}
+			if (ev.type == Common::EVENT_MOUSEMOVE) {
+				curMouseX = ev.mouse.x;
+				curMouseY = ev.mouse.y;
+				const uint newLevel = happinessLevel(curMouseX);
+				if (newLevel != level) {
+					level = newLevel;
+					seqIdx = 0; // restart cycle so the gesture pops
+					draw();
+				}
+			}
 			if (ev.type == Common::EVENT_LBUTTONDOWN) {
 				_partner = (ev.mouse.x >= 160) ? 0 : 1;
 				debugC(1, kDebugGeneral, "Partner picked: %s",
@@ -667,6 +724,19 @@ void EEMEngine::doCaseSelection() {
 	Picture caseBg;
 	const bool haveCaseBg = _picsArchive.getPicture(0x41, caseBg);
 
+	// KD greeter sprite. `_CaseSelection @ 1c33:0a87` (1c33:0b7e-0ba1)
+	// loads anim 0x15 (Jake-paired) or 0x16 (Jenny-paired) and registers
+	// `_NewAnimation(0x112, 0x50, ..., seqnum=0x15, prior=1)` ??? partner-
+	// dependent because the host KD changes who's "with him" on the
+	// briefing intro frame. Runs continuously through the menu loop via
+	// `_UpdateAnimations`. We approximate with millis-based frame cycling.
+	const uint kKdAniId = (_partner == 0) ? 0x15 : 0x16;
+	Animation kdAnim;
+	const bool haveKdAnim = _aniArchive.loadAnimation(kKdAniId, kdAnim)
+							 && !kdAnim.empty();
+	const int kKdAnimX = 0x112;
+	const int kKdAnimY = 0x50;
+
 	auto draw = [&]() {
 		Graphics::ManagedSurface scratch(320, 200,
 			Graphics::PixelFormat::createFormatCLUT8());
@@ -679,6 +749,27 @@ void EEMEngine::doCaseSelection() {
 					   (const byte *)caseBg.surface.getBasePtr(0, row), w);
 			}
 		}
+
+		// KD greeter frame ??? masked-blit current animation cell at
+		// (0x112, 0x50). 100 ms tick matches the engine's `_CheckFrameRate`.
+		if (haveKdAnim) {
+			const uint32 now = g_system->getMillis();
+			const uint frameIdx = (uint)((now / 100) % kdAnim.size());
+			const Picture &fr = kdAnim[frameIdx];
+			const byte transp = (byte)(fr.flags >> 8);
+			for (int row = 0; row < fr.surface.h; row++) {
+				const int dstY = kKdAnimY + row;
+				if (dstY < 0 || dstY >= 200) continue;
+				const byte *src = (const byte *)fr.surface.getBasePtr(0, row);
+				byte *dst = (byte *)scratch.getBasePtr(0, dstY);
+				for (int col = 0; col < fr.surface.w; col++) {
+					const int dstX = kKdAnimX + col;
+					if (dstX < 0 || dstX >= 320) continue;
+					if (src[col] != transp)
+						dst[dstX] = src[col];
+				}
+			}
+		}
 		if (_font.isLoaded()) {
 			// `DrawList` @ 1c33:040d coordinates: `_TextBox + 3` for x
 			// and `DAT_29be_0d02` for y. `_TextBox` @ 29be:0d00 holds
@@ -740,11 +831,19 @@ void EEMEngine::doCaseSelection() {
 	};
 
 	draw();
+	uint32 lastTick = g_system->getMillis();
 
 	bool exitChosen = false;
 	while (!shouldQuit()) {
 		Common::Event ev;
 		bool confirmed = false;
+		// Redraw every 100 ms so the KD greeter cycles. Mirrors the
+		// `_CheckFrameRate` cadence in `_CaseSelection`'s main loop.
+		const uint32 now = g_system->getMillis();
+		if (haveKdAnim && now - lastTick >= 100) {
+			lastTick = now;
+			draw();
+		}
 		while (g_system->getEventManager()->pollEvent(ev)) {
 			if (ev.type == Common::EVENT_QUIT ||
 				ev.type == Common::EVENT_RETURN_TO_LAUNCHER)
@@ -2279,6 +2378,22 @@ void EEMEngine::doBigMap() {
 	// STAGE 1 ??? Overview: PIC 0x42 + clickable site icons.
 	// ------------------------------------------------------------------
 
+	// `_DoBigMap @ 20fe:09e7` (20fe:0a44-0a99) registers a partner sprite
+	// on the overview frame. The animation depends on `_LastScreen`:
+	//   * When LastScreen == 2 (came from the site loop) the original
+	//     plays an entrance anim (`anum-1` for Jake / Jenny) at
+	//     (0x102, 0x50), then on END swaps to the idle anim at (0xfd,
+	//     0x50). We don't track LastScreen finely enough to distinguish,
+	//     so we render the IDLE pose at (0xfd, 0x50) which is what the
+	//     player sees the rest of the time anyway.
+	//   * Idle anim ID: Jake = 0x14 (20), Jenny = 0x12 (18).
+	const uint kMapAniId = (_partner == 0) ? 0x14 : 0x12;
+	Animation mapAnim;
+	const bool haveMapAnim = _aniArchive.loadAnimation(kMapAniId, mapAnim)
+							   && !mapAnim.empty();
+	const int kMapAnimX = 0xfd;
+	const int kMapAnimY = 0x50;
+
 	auto drawOverview = [&]() {
 		Graphics::ManagedSurface scratch(320, 200,
 			Graphics::PixelFormat::createFormatCLUT8());
@@ -2349,12 +2464,36 @@ void EEMEngine::doBigMap() {
 			}
 		}
 
+		// Partner sprite ??? masked-blit at (0xfd, 0x50). Same per-tick
+		// idle the original would run via `_UpdateAnimations` once the
+		// entrance one-shot transitions out (see `_DoBigMap` 0xae3-0xae7
+		// where it swaps animId on the 0x80 marker).
+		if (haveMapAnim) {
+			const uint32 now = g_system->getMillis();
+			const uint frameIdx = (uint)((now / 100) % mapAnim.size());
+			const Picture &fr = mapAnim[frameIdx];
+			const byte transp = (byte)(fr.flags >> 8);
+			for (int row = 0; row < fr.surface.h; row++) {
+				const int dstY = kMapAnimY + row;
+				if (dstY < 0 || dstY >= 200) continue;
+				const byte *src = (const byte *)fr.surface.getBasePtr(0, row);
+				byte *dst = (byte *)scratch.getBasePtr(0, dstY);
+				for (int col = 0; col < fr.surface.w; col++) {
+					const int dstX = kMapAnimX + col;
+					if (dstX < 0 || dstX >= 320) continue;
+					if (src[col] != transp)
+						dst[dstX] = src[col];
+				}
+			}
+		}
+
 		g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
 								   0, 0, 320, 200);
 		g_system->updateScreen();
 	};
 
 	drawOverview();
+	uint32 mapLastTick = g_system->getMillis();
 
 	// Static rectangles read directly from the binary at the labelled
 	// addresses (29be:0x1596 onwards). Format is {x1, y1, x2, y2}.
@@ -2398,6 +2537,13 @@ void EEMEngine::doBigMap() {
 		}
 		if (wantZoom)
 			break;
+		// Cycle the partner-sprite frame every 100 ms (matching the
+		// original's `_CheckFrameRate` cadence inside `_DoBigMap`).
+		const uint32 now = g_system->getMillis();
+		if (haveMapAnim && now - mapLastTick >= 100) {
+			mapLastTick = now;
+			drawOverview();
+		}
 		g_system->updateScreen();
 		g_system->delayMillis(10);
 	}
@@ -2433,6 +2579,19 @@ void EEMEngine::doBigMap() {
 	int scrollX = MAX<int>(0, MIN<int>(mapW - kMapWinW, zoomX));
 	int scrollY = MAX<int>(0, MIN<int>(mapH - kMapWinH, zoomY));
 
+	// `_DoMapScreen @ 20fe:120b` (20fe:12cd-12f0): partner sprite on
+	// the detail-zoom screen. Jake = anim 0x13 (19), Jenny = anim 0x11
+	// (17). Position (0x101, 0x50) = (257, 80), seqnum 0x13. The cells
+	// here have a "looking at the map" pose, distinct from the BigMap
+	// overview entrance/idle.
+	const uint kDetailAniId = (_partner == 0) ? 0x13 : 0x11;
+	Animation detailAnim;
+	const bool haveDetailAnim = _aniArchive.loadAnimation(kDetailAniId,
+														   detailAnim)
+								  && !detailAnim.empty();
+	const int kDetailAnimX = 0x101;
+	const int kDetailAnimY = 0x50;
+
 	auto drawDetail = [&]() {
 		Graphics::ManagedSurface scratch(320, 200,
 			Graphics::PixelFormat::createFormatCLUT8());
@@ -2493,12 +2652,35 @@ void EEMEngine::doBigMap() {
 			}
 		}
 
+		// Partner sprite on the detail map. Drawn last so it sits over
+		// the frame and the BIGMAP.PIC viewport.
+		if (haveDetailAnim) {
+			const uint32 now = g_system->getMillis();
+			const uint frameIdx =
+				(uint)((now / 100) % detailAnim.size());
+			const Picture &fr = detailAnim[frameIdx];
+			const byte transp = (byte)(fr.flags >> 8);
+			for (int row = 0; row < fr.surface.h; row++) {
+				const int dstY = kDetailAnimY + row;
+				if (dstY < 0 || dstY >= 200) continue;
+				const byte *src = (const byte *)fr.surface.getBasePtr(0, row);
+				byte *dst = (byte *)scratch.getBasePtr(0, dstY);
+				for (int col = 0; col < fr.surface.w; col++) {
+					const int dstX = kDetailAnimX + col;
+					if (dstX < 0 || dstX >= 320) continue;
+					if (src[col] != transp)
+						dst[dstX] = src[col];
+				}
+			}
+		}
+
 		g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
 								   0, 0, 320, 200);
 		g_system->updateScreen();
 	};
 
 	drawDetail();
+	uint32 detailLastTick = g_system->getMillis();
 
 	while (!shouldQuit()) {
 		Common::Event ev;
@@ -2619,6 +2801,13 @@ void EEMEngine::doBigMap() {
 				}
 			}
 		}
+		// Cycle the partner sprite at 100 ms ticks (same cadence as
+		// `_DoMapScreen`'s `_CheckFrameRate` + `_UpdateAnimations` loop).
+		const uint32 now = g_system->getMillis();
+		if (haveDetailAnim && now - detailLastTick >= 100) {
+			detailLastTick = now;
+			dirty = true;
+		}
 		if (dirty)
 			drawDetail();
 		g_system->updateScreen();
diff --git a/engines/eem/site.cpp b/engines/eem/site.cpp
index 6cd2dd402c8..8e119ab07f3 100644
--- a/engines/eem/site.cpp
+++ b/engines/eem/site.cpp
@@ -653,9 +653,12 @@ void SiteScreen::renderPartner(uint siteNum, uint32 tickMs) {
 	//   +0..1 anim Jake, +2..3 anim Jenny,
 	//   +4..5 x    Jake, +6..7 x    Jenny,
 	//   +8..9 y    Jake, +10..11 y    Jenny.
-	// Verbatim copy of the bytes Ghidra dumped at 29be:021c so we
-	// don't need to ship the original data segment.
-	static const uint16 kWaitAnims[][6] = {
+	// Seven valid entries verified against the bytes at 29be:021c.
+	// Anything past entry 6 in the binary is `_SiteButtons` rect data
+	// that follows the table in memory ??? NOT continuation entries ???
+	// so we cap the table here and skip rendering for siteData[+8] >= 7
+	// (which would indicate corrupt mystery data anyway).
+	static const uint16 kWaitAnims[7][6] = {
 		{ 0x00, 0x0a, 0x06, 0x06, 0x50, 0x50 }, // 0
 		{ 0x03, 0x0c, 0x06, 0x06, 0x50, 0x50 }, // 1
 		{ 0x01, 0x0b, 0x06, 0x06, 0x50, 0x50 }, // 2
@@ -663,17 +666,17 @@ void SiteScreen::renderPartner(uint siteNum, uint32 tickMs) {
 		{ 0x02, 0x10, 0x06, 0x06, 0x50, 0x50 }, // 4
 		{ 0x05, 0x05, 0x06, 0x06, 0x50, 0x50 }, // 5
 		{ 0x06, 0x06, 0x06, 0x06, 0x50, 0x50 }, // 6
-		{ 0x00, 0x00, 0x23, 0x6f, 0x38, 0x88 }, // 7 ??? special pos
-		{ 0x07, 0xb1, 0x39, 0xc8, 0x88, 0xae }, // 8 ??? likely junk; 0xb1 anim id is suspect
-		{ 0x9d, 0xbe, 0xa3, 0xae, 0xb8, 0xbe }  // 9 ??? likely junk
 	};
 
 	const byte *site = _mystery->siteData(siteNum);
 	if (!site)
 		return;
 	const uint16 speaker = READ_LE_UINT16(site + 8);
-	if (speaker >= ARRAYSIZE(kWaitAnims))
+	if (speaker >= ARRAYSIZE(kWaitAnims)) {
+		warning("renderPartner: site %u has speakerIdx=%u out of range",
+				siteNum, speaker);
 		return;
+	}
 
 	const uint8 partner = _vm->getPartnerIndex();
 	const uint  animId  = kWaitAnims[speaker][0 + partner];


Commit: 0f7bf385b7d57e6e4e98df854fbfb483c84640ef
    https://github.com/scummvm/scummvm/commit/0f7bf385b7d57e6e4e98df854fbfb483c84640ef
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:20:40+02:00

Commit Message:
EEM: refactored the code following the original symbols

Changed paths:
  A engines/eem/clues.cpp
  A engines/eem/graphics.cpp
  A engines/eem/ui.cpp
    engines/eem/eem.cpp
    engines/eem/eem.h
    engines/eem/module.mk
    engines/eem/site.cpp


diff --git a/engines/eem/clues.cpp b/engines/eem/clues.cpp
new file mode 100644
index 00000000000..a12136c304f
--- /dev/null
+++ b/engines/eem/clues.cpp
@@ -0,0 +1,784 @@
+/* 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/debug.h"
+#include "common/events.h"
+#include "common/file.h"
+#include "common/path.h"
+#include "common/savefile.h"
+#include "common/system.h"
+#include "common/textconsole.h"
+
+#include "graphics/cursorman.h"
+#include "graphics/managed_surface.h"
+
+#include "eem/detection.h"
+#include "eem/eem.h"
+
+// EEM ??? clue / briefing pipeline (SCRIPT.C clue parts + KD.C briefing parts).
+// Everything that drives a `ClueBlock` through the displayClue / portrait /
+// balloon / notebook-side-effect flow plus the case-briefing intro that
+// preambles into the same flow.
+
+namespace EEM {
+
+namespace {
+// Picture / animation IDs verified against `_DoChoosePartner @ 1a35:0756`.
+const uint kPicChooseBackground = 0x8c; ///< `_GetBackground(0x8c)`
+const uint kAniBoy  = 8;                 ///< `_GetAnimation(8)` (Jake)
+const uint kAniGirl = 9;                 ///< `_GetAnimation(9)` (Jenny)
+
+// On-screen positions verified from `_NewAnimation` calls @ 1a35:07b9 / 07d5.
+const int kBoyX  = 0xe2; // 226
+const int kBoyY  = 0x62; // 98
+const int kGirlX = 0x42; // 66
+const int kGirlY = 0x60; // 96
+} // anonymous namespace
+
+void EEMEngine::doChoosePartner() {
+	// Mirrors _DoChoosePartner @ 1a35:0756. The original places boy + girl
+	// animations on a backdrop and polls four click rectangles (two per
+	// character) for the player's choice. We approximate by splitting the
+	// screen at x=160: left half = girl (Jenny), right half = boy (Jake).
+	Picture background;
+	if (!_picsArchive.getPicture(kPicChooseBackground, background)) {
+		warning("ChoosePartner background (%u) load failed", kPicChooseBackground);
+		return;
+	}
+
+	Animation boyAnim;
+	if (!_aniArchive.loadAnimation(kAniBoy, boyAnim) || boyAnim.empty()) {
+		warning("Boy animation (%u) load failed", kAniBoy);
+		return;
+	}
+	Animation girlAnim;
+	if (!_aniArchive.loadAnimation(kAniGirl, girlAnim) || girlAnim.empty()) {
+		warning("Girl animation (%u) load failed", kAniGirl);
+		return;
+	}
+
+	setAnmPalette(Common::Path("TITLE.ANM"));
+
+	// `_DoHappiness @ 172b:27b5`: the cursor's X column picks one of 4
+	// rects (29be:030f, all full-height); past rect 3 ??? "level 4".
+	// Each level swaps the partner's sequence script to a more / less
+	// "happy" cycle. Boy seqs at 29be:0337 (5 ?? 0x14 bytes), girl seqs
+	// at 29be:039b. Both cycle through 9 frames (the boy/girl anim
+	// cells contain 10 cells = pairs of "neutral, smile" at increasing
+	// intensity). Lifted verbatim from the binary so the gestures
+	// match the original beat-for-beat.
+	static const Common::Rect kHappyZones[4] = {
+		Common::Rect(  0, 0,  70, 200), // far left  ??? girl very happy, boy neutral
+		Common::Rect( 70, 0, 126, 200), // girl's column
+		Common::Rect(126, 0, 182, 200), // middle
+		Common::Rect(182, 0, 235, 200), // boy's column
+	};
+	static const uint8 kBoySeqs[5][9] = {
+		{ 0,0,0,0,0,0,0,1,0 }, // level 0
+		{ 2,2,2,2,2,2,2,3,2 }, // level 1
+		{ 4,4,4,4,4,4,4,5,4 }, // level 2
+		{ 6,6,6,6,6,6,7,6,6 }, // level 3
+		{ 8,8,8,8,8,8,8,8,9 }, // level 4 (cursor past zone 3)
+	};
+	static const uint8 kGirlSeqs[5][9] = {
+		{ 8,9,8,8,8,8,8,8,8 },
+		{ 6,6,6,7,6,6,6,6,6 },
+		{ 4,4,5,4,4,4,4,4,4 },
+		{ 2,2,2,2,2,2,3,2,2 },
+		{ 0,0,0,0,0,1,0,0,0 },
+	};
+	auto happinessLevel = [](int x) {
+		for (uint i = 0; i < ARRAYSIZE(kHappyZones); i++) {
+			if (kHappyZones[i].contains(x, 100))
+				return (uint)i;
+		}
+		return 4u; // past zone 3 ??? max level
+	};
+
+	// `_DoChoosePartner` opens with `_SetMousePos(0xa0, 0x96)` so the
+	// cursor lands centred between the two partners ??? start the
+	// happiness level from that initial X.
+	int curMouseX = 0xa0;
+	uint level = happinessLevel(curMouseX);
+	uint seqIdx = 0;       // step within the 9-frame seq
+
+	auto draw = [&]() {
+		blitAt(background, 0, 0);
+		const uint girlFrame = kGirlSeqs[level][seqIdx % 9];
+		const uint boyFrame  = kBoySeqs [level][seqIdx % 9];
+		blitAt(girlAnim[girlFrame % girlAnim.size()], kGirlX, kGirlY);
+		blitAt(boyAnim[boyFrame  % boyAnim.size()],  kBoyX,  kBoyY);
+		g_system->updateScreen();
+	};
+	draw();
+
+	debugC(1, kDebugGeneral, "ChoosePartner: %u boy frames at (%d,%d), "
+		   "%u girl frames at (%d,%d)",
+		   (uint)boyAnim.size(), kBoyX, kBoyY,
+		   (uint)girlAnim.size(), kGirlX, kGirlY);
+
+	uint32 lastTick = g_system->getMillis();
+	while (!shouldQuit()) {
+		// Advance through the 9-frame seq at 100 ms ??? `_CheckFrameRate`
+		// cadence. The seq is short and loops; matches the original
+		// `_UpdateAnimations` which restarts at curIdx=0 on the 0x80
+		// marker. Mirrors `_DoHappiness`'s rewriting of `curIdx = 0xFFFF`
+		// when the cursor crosses zones (we restart `seqIdx` instead).
+		if (g_system->getMillis() - lastTick > 100) {
+			lastTick = g_system->getMillis();
+			seqIdx = (seqIdx + 1) % 9;
+			draw();
+		}
+
+		Common::Event ev;
+		bool done = false;
+		while (g_system->getEventManager()->pollEvent(ev)) {
+			if (ev.type == Common::EVENT_QUIT ||
+				ev.type == Common::EVENT_RETURN_TO_LAUNCHER) {
+				done = true;
+				break;
+			}
+			if (ev.type == Common::EVENT_MOUSEMOVE) {
+				curMouseX = ev.mouse.x;
+				const uint newLevel = happinessLevel(curMouseX);
+				if (newLevel != level) {
+					level = newLevel;
+					seqIdx = 0; // restart cycle so the gesture pops
+					draw();
+				}
+			}
+			if (ev.type == Common::EVENT_LBUTTONDOWN) {
+				_partner = (ev.mouse.x >= 160) ? 0 : 1;
+				debugC(1, kDebugGeneral, "Partner picked: %s",
+					   _partner == 0 ? "Jake" : "Jennifer");
+				done = true;
+				break;
+			}
+			if (ev.type == Common::EVENT_KEYDOWN) {
+				if (ev.kbd.keycode == Common::KEYCODE_LEFT) {
+					_partner = 1; done = true; break;
+				}
+				if (ev.kbd.keycode == Common::KEYCODE_RIGHT) {
+					_partner = 0; done = true; break;
+				}
+				if (ev.kbd.keycode == Common::KEYCODE_RETURN ||
+					ev.kbd.keycode == Common::KEYCODE_ESCAPE) {
+					done = true; break;
+				}
+			}
+		}
+		if (done)
+			break;
+		g_system->updateScreen();
+		g_system->delayMillis(20);
+	}
+}
+
+void EEMEngine::doInitClues() {
+	// Mirrors `_DoInitClues` @ 1a35:0411. The original does:
+	//   1. _AllBlack(); _GetBackground(0x52); _GetPalette(0x22);
+	//   2. _GetAnimation(gameAni); _NewAnimation(0xcd, 0x6c, ...)
+	//      _GetAnimation(bookAni); _NewAnimation(0,    99,   ...)
+	//      (case type 1 also: _NewAnimation(0x68, 0x8b, nancyAni))
+	//   3. _UpdateAnimations(); _FadeIn();
+	//   4. while (frame != gameNum) { _CheckFrameRate(); _UpdateAnimations(); }
+	//        ??? cycles through the entire game animation once. Click skips.
+	//   5. _PlayInSequence(seqId, ...) ??? plays a follow-up sequence based
+	//      on partner + case type.
+	//   6. _DisplayClue(InitBlock + 2, 1) ??? the briefing dialogue.
+	//   7. _OnSites[startSite] = 1.
+	//
+	// gameAni / bookAni / nancyAni values verified directly from Ghidra:
+	//   gameAni  = 0x17 (Jake) / 0x3b (Jenny)
+	//   bookAni  = 0x18 (Jake) / 0x3c (Jenny)
+	//   nancyAni = 0x19 (case type 1 only)
+	if (!_mystery.isLoaded())
+		return;
+
+	const byte *ib = _mystery.initBlock();
+	if (!ib)
+		return;
+
+	const uint16 startSite = READ_LE_UINT16(ib + 2);
+	if (startSite < Mystery::kVisitedSiteCap)
+		_mystery._onSites[startSite] = 1;
+	_mystery._siteNumber = startSite;
+	_mystery._lastSite = startSite;
+
+	setSitePalette(0x22);
+	Picture bg;
+	if (_picsArchive.getPicture(0x52, bg))
+		blitAt(bg, 0, 0);
+
+	const uint gameAni = _partner == 0 ? 0x17 : 0x3b;
+	const uint bookAni = _partner == 0 ? 0x18 : 0x3c;
+	Animation game, book, nancy;
+	const bool haveGame  = _aniArchive.loadAnimation(gameAni, game) && !game.empty();
+	const bool haveBook  = _aniArchive.loadAnimation(bookAni, book) && !book.empty();
+
+	const uint16 caseType = READ_LE_UINT16(ib);
+	const bool haveNancy = (caseType == 1)
+						  && _aniArchive.loadAnimation(0x19, nancy)
+						  && !nancy.empty();
+
+	auto blitMaskedAt = [&](const Picture &p, int x, int y) {
+		const byte transp = (byte)(p.flags >> 8);
+		Graphics::Surface *screen = g_system->lockScreen();
+		if (!screen) return;
+		for (int row = 0; row < p.surface.h; row++) {
+			const int dstY = y + row;
+			if (dstY < 0 || dstY >= screen->h) continue;
+			const byte *src = (const byte *)p.surface.getBasePtr(0, row);
+			byte *dst = (byte *)screen->getBasePtr(0, dstY);
+			for (int col = 0; col < p.surface.w; col++) {
+				const int dstX = x + col;
+				if (dstX < 0 || dstX >= screen->w) continue;
+				if (src[col] != transp)
+					dst[dstX] = src[col];
+			}
+		}
+		g_system->unlockScreen();
+	};
+
+	// Step 4 ??? cycle through the game animation once before the briefing.
+	// Mirrors the `while (uVar9 != gameNum)` loop. The original calls
+	// `_UpdateAnimations` per `_CheckFrameRate` tick (~10 fps). We use
+	// 100 ms ticks for the same cadence. Click / key skips.
+	if (haveGame || haveBook || haveNancy) {
+		const uint frameCount = haveGame ? game.size() : 8;
+		bool skip = false;
+		for (uint frame = 0; frame < frameCount && !shouldQuit() && !skip; frame++) {
+			// Restore BG + advance frame.
+			if (_picsArchive.getPicture(0x52, bg))
+				blitAt(bg, 0, 0);
+			if (haveGame)
+				blitMaskedAt(game[frame % game.size()], 0xcd, 0x6c);
+			if (haveBook)
+				blitMaskedAt(book[frame % book.size()], 0, 99);
+			if (haveNancy)
+				blitMaskedAt(nancy[frame % nancy.size()], 0x68, 0x8b);
+			g_system->updateScreen();
+
+			// Wait 100 ms or until input.
+			const uint32 wakeup = g_system->getMillis() + 100;
+			while (g_system->getMillis() < wakeup && !shouldQuit() && !skip) {
+				Common::Event ev;
+				while (g_system->getEventManager()->pollEvent(ev)) {
+					if (ev.type == Common::EVENT_LBUTTONDOWN ||
+						ev.type == Common::EVENT_KEYDOWN) {
+						skip = true;
+						break;
+					}
+				}
+				g_system->delayMillis(10);
+			}
+		}
+	}
+
+	// Composite the final frames (or first frames if skipped) so the BG
+	// is in a sensible state when displayClue overlays the speaker.
+	if (_picsArchive.getPicture(0x52, bg))
+		blitAt(bg, 0, 0);
+	if (haveGame)
+		blitMaskedAt(game[0], 0xcd, 0x6c);
+	if (haveBook)
+		blitMaskedAt(book[0], 0, 99);
+	if (haveNancy)
+		blitMaskedAt(nancy[0], 0x68, 0x8b);
+	g_system->updateScreen();
+
+	// Step 5 ??? `_PlayInSequence(animSeq, 0xcd, animY)` per Ghidra:
+	//   Jake (partner=0):
+	//     caseType=1 ??? anim 0x38 at (0xcd, 0x6d)
+	//     caseType=2 ??? anim 0x37 at (0xcd, 0x6c)
+	//     caseType=3 ??? anim 0x39 at (0xcd, 0x6c)
+	//   Jenny (partner=1):
+	//     caseType=2 ??? anim 0x3a at (0xcd, 0x6c)
+	//     caseType=3 ??? anim 0x3d at (0xcd, 0x6c)
+	// `_PlayInSequence @ 172b:2d03` plays each frame at (sx-w, sy-rowoff)
+	// with mask blit, advancing one frame per `_CheckFrameRate` tick.
+	uint16 seqAni = 0xFFFF;
+	uint16 seqY   = 0x6c;
+	if (_partner == 0) {
+		switch (caseType) {
+		case 1: seqAni = 0x38; seqY = 0x6d; break;
+		case 2: seqAni = 0x37; seqY = 0x6c; break;
+		case 3: seqAni = 0x39; seqY = 0x6c; break;
+		default: break;
+		}
+	} else {
+		switch (caseType) {
+		case 2: seqAni = 0x3a; seqY = 0x6c; break;
+		case 3: seqAni = 0x3d; seqY = 0x6c; break;
+		default: break;
+		}
+	}
+	if (seqAni != 0xFFFF) {
+		Animation seq;
+		if (_aniArchive.loadAnimation(seqAni, seq) && !seq.empty()) {
+			bool skip = false;
+			for (uint frame = 0; frame < seq.size() && !shouldQuit() && !skip;
+				 frame++) {
+				const Picture &fr = seq[frame];
+				// Restore BG + base anim frames so each new frame
+				// composites cleanly.
+				if (_picsArchive.getPicture(0x52, bg))
+					blitAt(bg, 0, 0);
+				if (haveGame)
+					blitMaskedAt(game[frame % game.size()], 0xcd, 0x6c);
+				if (haveBook)
+					blitMaskedAt(book[frame % book.size()], 0, 99);
+				if (haveNancy)
+					blitMaskedAt(nancy[frame % nancy.size()], 0x68, 0x8b);
+				// Anchor: original blits at `(sx - frame.width,
+				// sy - frame.rowoff)`. `frame.rowoff` is the y-anchor
+				// in our PicData. We use width/height directly since
+				// loadAnimation places anchor at (0, 0).
+				const int dstX = (int)0xcd - (int)fr.surface.w;
+				const int dstY = (int)seqY - (int)fr.rowoff;
+				blitMaskedAt(fr, dstX, dstY);
+				g_system->updateScreen();
+				const uint32 wakeup = g_system->getMillis() + 100;
+				while (g_system->getMillis() < wakeup &&
+					   !shouldQuit() && !skip) {
+					Common::Event ev;
+					while (g_system->getEventManager()->pollEvent(ev)) {
+						if (ev.type == Common::EVENT_LBUTTONDOWN ||
+							ev.type == Common::EVENT_KEYDOWN) {
+							skip = true;
+							break;
+						}
+					}
+					g_system->delayMillis(10);
+				}
+			}
+		}
+	}
+
+	// Step 6 ??? case briefing dialogue.
+	displayClue(ib + 4);
+}
+
+/// Mirror `_ParseString` @ 1b66:07c3 ??? substitute the control bytes that
+/// the original engine uses as placeholders. Only the two we encounter most
+/// often (player name = 0x80, partner first name = 0x82) are substituted;
+/// other 0x8N opcodes are stripped. The original engine also handles
+/// hyphenation marks and a hint placeholder (0x89) we ignore for now.
+Common::String EEMEngine::parseString(const Common::String &raw,
+									  const Common::String &playerName,
+									  uint partner) const {
+	// Substitution opcodes from `_ParseString` @ 1b66:07c3, jump-table
+	// at 1b66:0cbe. Each handler reads `_Partner` (16-bit at 0x7918)
+	// and indexes the name table at 29be:0c28 ({Jake, Jennifer, he,
+	// she, him, her, his} as far pointers).
+	//   0x80 ??? player's typed name (auto-cap word starts) ??? uses _PlayerRecord
+	//   0x81 ??? _Partner == 0 ? "Jake"     : "Jennifer"  (chosen detective)
+	//   0x82 ??? _Partner == 0 ? "Jennifer" : "Jake"      (the OTHER one)
+	//   0x83 ??? _Partner == 0 ? "he"       : "she"
+	//   0x84 ??? _Partner == 0 ? "him"      : "her"
+	//   0x85 ??? _Partner == 0 ? "his"      : "her"
+	//   0x86..0x88 read a different gender flag at 0x7985 ??? left alone
+	//     until that flag's source is traced.
+	//   0x89 ??? KD hint placeholder (handled by caller).
+	const bool isJake = (partner == 0);
+	Common::String out;
+	for (uint i = 0; i < raw.size(); i++) {
+		const byte c = (byte)raw[i];
+		switch (c) {
+		case 0x80:
+			out += playerName;
+			break;
+		case 0x81:
+			out += isJake ? "Jake" : "Jennifer";
+			break;
+		case 0x82:
+			out += isJake ? "Jennifer" : "Jake";
+			break;
+		case 0x83:
+			out += isJake ? "he" : "she";
+			break;
+		case 0x84:
+			out += isJake ? "him" : "her";
+			break;
+		case 0x85:
+			out += isJake ? "his" : "her";
+			break;
+		case 0x86:
+		case 0x87:
+		case 0x88:
+		case 0x89:
+			// Eaten silently ??? see comment above.
+			break;
+		case 0:
+			return out;
+		case '\r':
+			break;
+		default:
+			out += (char)c;
+			break;
+		}
+	}
+	return out;
+}
+
+void EEMEngine::applyClueSideEffects(const byte *c) {
+	for (uint j = 0; j < 5; j++) {
+		const uint16 note = READ_LE_UINT16(c + 0x30 + j * 2);
+		if (note != 0xFFFF && note < Mystery::kCluesFoundCap)
+			_mystery._cluesFound[note] = 1;
+
+		const uint16 galIdx = READ_LE_UINT16(c + 0x26 + j * 2);
+		if (galIdx != 0xFFFF && galIdx < Mystery::kGalleryCap) {
+			const uint8 phys = _mystery._newOrder[galIdx];
+			if (phys < Mystery::kGalleryCap)
+				_mystery._inGallery[phys] = 1;
+		}
+
+		const uint16 siteIdx = READ_LE_UINT16(c + 0x1c + j * 2);
+		if (siteIdx != 0xFFFF) {
+			const uint16 siteVal = siteIdx & 0x7FFF;
+			if (siteVal < Mystery::kVisitedSiteCap)
+				_mystery._onSites[siteVal] = 1;
+			if (siteIdx & 0x8000)
+				_mystery._sawCONSITEs = true;
+		}
+	}
+}
+
+void EEMEngine::displayClue(const byte *clueBlock) {
+	if (!clueBlock || !_mystery.isLoaded())
+		return;
+
+	// ClueBlock layout (verified against M0.BIN):
+	//   +0..1: number (entry count)
+	//   +2..3: pic ID for entry 0 (entry N>0 uses prev entry's last 2 bytes)
+	//   +4..:  array of 62-byte entries
+	const uint16 number = READ_LE_UINT16(clueBlock);
+	debugC(1, kDebugScript, "displayClue: %u entries", number);
+	if (number == 0 || number > 32) {
+		// number==0 = no briefing (e.g. mystery 0 case-type 4); >32 is a
+		// guard against bad pointers.
+		return;
+	}
+
+	// Snapshot the current screen as the BG so character pics from
+	// earlier entries don't stack on top of each other.
+	Graphics::ManagedSurface bg(320, 200,
+		Graphics::PixelFormat::createFormatCLUT8());
+	bg.clear();
+	{
+		Graphics::Surface *screen = g_system->lockScreen();
+		if (screen) {
+			for (int row = 0; row < 200; row++) {
+				memcpy((byte *)bg.getBasePtr(0, row),
+					   (const byte *)screen->getBasePtr(0, row), 320);
+			}
+			g_system->unlockScreen();
+		}
+	}
+
+	for (uint i = 0; i < number && !shouldQuit(); i++) {
+		// Restore BG before drawing this entry's portrait + balloon.
+		g_system->copyRectToScreen(bg.getPixels(), bg.pitch, 0, 0, 320, 200);
+		const byte *c = clueBlock + 4 + i * 62;
+		// Per-partner fields:
+		//   +0..1, +2..3: tx, ty (partner 0)
+		//   +4..5, +6..7: tx, ty (partner 1)
+		//   +8..9, +10..11: bubText offset for partner 0/1 (rel. TextBlock)
+		//   +12..13, +14..15: balloon picture ID for partner 0/1
+		//   +16..17, +18..19: bubX, bubY
+		//   +0x3a..+0x3b:    KD-anim number (-1 = none)
+		// Per `_DisplayClue` @ 2404:05e6: partner 1 uses its own field
+		// set ONLY when bubText1 is not -1; otherwise it falls back to
+		// the partner 0 fields entirely. Partner 0 always uses field 0.
+
+		// Per-clue partner reaction animation. `_DisplayClue` @
+		// 2404:0635-064b checks `clueEntry[+0x3a]` and, when not -1,
+		// calls `_DoKDAnim(num)` BEFORE drawing the speaker portrait.
+		// This is what surfaces "Jenny takes a picture with a camera"
+		// (and the matching Jake gestures) during NPC searches.
+		const int16 kdAnimNum = (int16)READ_LE_UINT16(c + 0x3a);
+		if (kdAnimNum != -1)
+			playKdAnim((uint16)kdAnimNum);
+
+		const bool useP1 = (_partner == 1) &&
+			(READ_LE_UINT16(c + 10) != 0xFFFF);
+		const uint partner = useP1 ? 1 : 0;
+		const uint16 textOff = READ_LE_UINT16(c + 8 + partner * 2);
+		const bool hasText = (textOff != 0xFFFF);
+		// Partner 1 bubX/bubY at +0x14/+0x16; partner 0 at +0x10/+0x12.
+		const uint16 bubX = READ_LE_UINT16(c + (useP1 ? 0x14 : 0x10));
+		const uint16 bubY = READ_LE_UINT16(c + (useP1 ? 0x16 : 0x12));
+		const uint16 bubNum = READ_LE_UINT16(c + (useP1 ? 0x0E : 0x0C));
+		const char *raw   = hasText ? _mystery.textAt(textOff) : "";
+
+		// Speaker portrait. Mirrors `_DisplayClue`'s `pic[clues+i*62-2]`:
+		// for entry 0 the pic ID is in the ClueBlock header at +2; for
+		// later entries it sits in the previous entry's last 2 bytes.
+		// Speaker portrait position uses partner 0 fields (+0..+3) when
+		// _partner==0 or when partner 1 falls back; otherwise partner 1
+		// fields (+4..+7). Same logic as the original.
+		const uint16 charX  = READ_LE_UINT16(c + (useP1 ? 4 : 0));
+		const uint16 charY  = READ_LE_UINT16(c + (useP1 ? 6 : 2));
+		const uint16 charPicId = (i == 0)
+			? READ_LE_UINT16(clueBlock + 2)
+			: READ_LE_UINT16(c - 2);
+		if (charPicId != 0 && charPicId != 0xFFFF) {
+			Picture charPic;
+			if (_picsArchive.getPicture(charPicId, charPic) &&
+				charX < 320 && charY < 200) {
+				const int w = MIN<int>(charPic.surface.w, 320 - charX);
+				const int h = MIN<int>(charPic.surface.h, 200 - charY);
+				if (w > 0 && h > 0)
+					g_system->copyRectToScreen(charPic.surface.getPixels(),
+						charPic.surface.pitch, charX, charY, w, h);
+			}
+		}
+
+		// Substitute control bytes (0x80..0x89) ??? see `parseString` for
+		// the table. 0x81 = chosen detective, 0x82 = the other one.
+		const Common::String text = parseString(raw ? raw : "",
+												_playerName, _partner);
+
+		// Speech balloon. Mirrors `_GetBalloon` + `_AddPicBackground` in
+		// `_DisplayClue`. The original looks up per-balloon text-area
+		// metadata in a table at offset 0x875 (within `_DisplayClue`'s
+		// segment); we don't have that table decoded yet, so we use a
+		// fixed inset of 8 px from the balloon's top-left.
+		Picture balloon;
+		const uint16 balloonId = bubNum & 0x7F;
+		const bool haveBalloon = bubNum != 0xFFFF &&
+			_balloonArchive.size() > balloonId &&
+			_balloonArchive.loadEntry(balloonId, balloon);
+
+		if (_font.isLoaded() && !text.empty()) {
+			// Snapshot the current screen, overlay balloon + text, then
+			// copy the changed band back. This preserves the site BG
+			// underneath unchanged regions.
+			Graphics::Surface *screen = g_system->lockScreen();
+			if (!screen) break;
+			Graphics::ManagedSurface scratch(320, 200,
+				Graphics::PixelFormat::createFormatCLUT8());
+			for (int row = 0; row < 200; row++) {
+				memcpy((byte *)scratch.getBasePtr(0, row),
+					   (const byte *)screen->getBasePtr(0, row), 320);
+			}
+			g_system->unlockScreen();
+
+			int textX = bubX;
+			int textY = bubY;
+			int textW = MIN<int>(320 - bubX, 200);
+			int copyY = bubY;
+			int copyH = _font.getFontHeight() * 4 + 8;
+
+			if (haveBalloon) {
+				const int bw = MIN<int>(balloon.surface.w, 320 - bubX);
+				const int bh = MIN<int>(balloon.surface.h, 200 - bubY);
+				// `_AddPicBackground` passes `pic->miscflags >> 8` as
+				// the transparent colour to `_Rect_Move_Mask`. The
+				// on-disk u16 at file offset 0 maps to `Picture::flags`.
+				const byte transp = (byte)(balloon.flags >> 8);
+				// `_GetBalloon @ 172b:1d7d` mirrors the picture horizontally
+				// when `(bubNum & 0x80)` is set ??? used for right-side
+				// speakers so the tail points the other way.
+				const bool flipBalloon = (bubNum & 0x80) != 0;
+				if (bw > 0 && bh > 0) {
+					for (int row = 0; row < bh; row++) {
+						const byte *src =
+							(const byte *)balloon.surface.getBasePtr(0, row);
+						byte *dst = (byte *)scratch.getBasePtr(bubX, bubY + row);
+						for (int col = 0; col < bw; col++) {
+							const int srcCol = flipBalloon
+								? (balloon.surface.w - 1 - col)
+								: col;
+							const byte px = src[srcCol];
+							if (px != transp)
+								dst[col] = px;
+						}
+					}
+				}
+				// Per-balloon metadata table verified from 29be:0875 ???
+				// 10-byte entries indexed by `(bubNum & 0x7f)`. Layout:
+				//   +0..1 textX inset, +2..3 textY inset, +4..5 width,
+				//   +6..7 height, +8..9 tail offset.
+				// 52 entries total; insets vary (3, 5, 6, or 8 px).
+				// The original `_DisplayClue` does:
+				//   _WordWrap(bubX + table[bubNum].x, bubY + table[bubNum].y,
+				//             table[bubNum].w, ...);
+				static const struct { uint16 x, y, w; } kBalloonTable[] = {
+					{ 6, 4, 142 }, { 6, 4, 142 }, { 6, 4, 142 }, { 6, 4, 142 },
+					{ 6, 4, 142 }, { 6, 4, 142 }, { 6, 4, 142 },
+					{ 6, 4, 224 }, { 6, 4, 224 }, { 6, 4, 224 }, { 6, 4, 224 },
+					{ 6, 4, 224 }, { 6, 4, 224 }, { 6, 4, 224 },
+					{ 6, 4, 291 }, { 6, 4, 291 }, { 6, 4, 291 }, { 6, 4, 291 },
+					{ 6, 4, 291 }, { 6, 4, 291 }, { 6, 4, 291 },
+					{ 5, 4, 155 }, { 5, 4, 155 }, { 5, 4, 155 }, { 5, 4, 155 },
+					{ 5, 4, 155 }, { 5, 4, 155 }, { 5, 4, 155 },
+					{ 5, 4, 237 }, { 5, 4, 237 }, { 5, 4, 237 }, { 5, 4, 237 },
+					{ 5, 4, 237 }, { 5, 4, 237 }, { 5, 4, 237 },
+					{ 3, 4, 155 }, { 3, 4, 155 }, { 3, 4, 155 }, { 3, 4, 155 },
+					{ 3, 4, 155 }, { 3, 4, 155 }, { 3, 4, 155 },
+					{ 5, 4, 238 }, { 5, 4, 238 }, { 5, 4, 238 }, { 5, 4, 238 },
+					{ 5, 4, 238 }, { 5, 4, 238 }, { 5, 4, 238 },
+					{ 5, 8, 158 }, { 5, 8, 176 }, { 8, 7, 142 }
+				};
+				const uint kBalloonTableSize = sizeof(kBalloonTable) /
+											   sizeof(kBalloonTable[0]);
+				const uint balloonIdx = balloonId < kBalloonTableSize
+										? balloonId : 0;
+				const auto &bm = kBalloonTable[balloonIdx];
+				textX = bubX + bm.x;
+				textY = bubY + bm.y;
+				textW = bm.w;
+				copyH = bh;
+			} else {
+				// No balloon ??? clear a band so old pixels don't bleed.
+				const Common::Rect band(0, bubY, 320,
+					MIN<int>(bubY + copyH, 200));
+				scratch.fillRect(band, 0);
+				copyY = bubY;
+			}
+
+			// `_DisplayClue` @ 2404:07fe passes fontColor=0 (palette
+			// index 0 of the case-briefing palette 0x22) to `_WordWrap`.
+			// Hard-coding 0xF here gave the wrong colour.
+			_font.drawWordWrapped(&scratch, textX, textY,
+				MAX<int>(8, textW), text, 0);
+
+			g_system->copyRectToScreen(scratch.getBasePtr(0, copyY),
+				scratch.pitch, 0, copyY, 320,
+				MIN<int>(copyH, 200 - copyY));
+			g_system->updateScreen();
+		}
+
+		// Wait for click/key to advance ??? only if we drew something.
+		// ESC skips the entire dialogue rather than just one entry.
+		if (hasText || (charPicId != 0 && charPicId != 0xFFFF)) {
+			bool advance = false;
+			bool skipAll = false;
+			while (!advance && !shouldQuit()) {
+				Common::Event ev;
+				while (g_system->getEventManager()->pollEvent(ev)) {
+					if (ev.type == Common::EVENT_QUIT ||
+						ev.type == Common::EVENT_RETURN_TO_LAUNCHER) {
+						advance = true;
+						break;
+					}
+					if (ev.type == Common::EVENT_KEYDOWN &&
+						ev.kbd.keycode == Common::KEYCODE_ESCAPE) {
+						advance = true;
+						skipAll = true;
+						break;
+					}
+					if (ev.type == Common::EVENT_LBUTTONDOWN ||
+						ev.type == Common::EVENT_KEYDOWN) {
+						advance = true;
+						break;
+					}
+				}
+				// Tick the screen so the OSystem cursor follows the
+				// mouse ??? ScummVM redraws the cursor overlay only on
+				// updateScreen.
+				g_system->updateScreen();
+				g_system->delayMillis(10);
+			}
+			if (skipAll) {
+				// Apply remaining side-effects without rendering. The
+				// original silently runs the state updates even when the
+				// player skips ahead.
+				for (uint k = i; k < number; k++)
+					applyClueSideEffects(clueBlock + 4 + k * 62);
+				return;
+			}
+		}
+
+		applyClueSideEffects(c);
+	}
+}
+
+bool EEMEngine::areYouSure() {
+	// Mirrors `_AreYouSure` @ 1a35:0a5c. Original loads PIC 0x136 for the
+	// dialog body and PIC 0x1FD/0x1FE for YES/NO. We render a minimal
+	// text dialog that preserves the screen behind it.
+	if (!_font.isLoaded())
+		return true;
+
+	Graphics::Surface *screen = g_system->lockScreen();
+	Graphics::ManagedSurface saved(320, 200,
+		Graphics::PixelFormat::createFormatCLUT8());
+	if (screen) {
+		for (int row = 0; row < 200; row++) {
+			memcpy((byte *)saved.getBasePtr(0, row),
+				   (const byte *)screen->getBasePtr(0, row), 320);
+		}
+		g_system->unlockScreen();
+	}
+
+	const Common::Rect dlg(60, 70, 260, 140);
+	Graphics::ManagedSurface scratch(320, 200,
+		Graphics::PixelFormat::createFormatCLUT8());
+	for (int row = 0; row < 200; row++)
+		memcpy((byte *)scratch.getBasePtr(0, row),
+			   (const byte *)saved.getBasePtr(0, row), 320);
+	scratch.fillRect(dlg, 0);
+	scratch.frameRect(dlg, 0xF);
+	_font.drawString(&scratch, "Are you sure you want to quit?", dlg.left + 8, dlg.top + 8, 320, 0xF);
+	_font.drawString(&scratch, "Y - Yes", dlg.left + 16, dlg.top + 36, 320, 0xF);
+	_font.drawString(&scratch, "N - No", dlg.left + 100, dlg.top + 36, 320, 0xF);
+	g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
+							   0, 0, 320, 200);
+	g_system->updateScreen();
+
+	bool result = false;
+	while (!shouldQuit()) {
+		Common::Event ev;
+		bool decided = false;
+		while (g_system->getEventManager()->pollEvent(ev)) {
+			if (ev.type == Common::EVENT_QUIT ||
+				ev.type == Common::EVENT_RETURN_TO_LAUNCHER) {
+				result = true;
+				decided = true;
+				break;
+			}
+			if (ev.type == Common::EVENT_KEYDOWN) {
+				if (ev.kbd.keycode == Common::KEYCODE_y ||
+					ev.kbd.keycode == Common::KEYCODE_RETURN) {
+					result = true; decided = true; break;
+				}
+				if (ev.kbd.keycode == Common::KEYCODE_n ||
+					ev.kbd.keycode == Common::KEYCODE_ESCAPE) {
+					result = false; decided = true; break;
+				}
+			}
+		}
+		if (decided)
+			break;
+		g_system->updateScreen();
+		g_system->delayMillis(15);
+	}
+
+	// Restore the screen so the caller's UI is intact.
+	g_system->copyRectToScreen(saved.getPixels(), saved.pitch, 0, 0, 320, 200);
+	g_system->updateScreen();
+	return result;
+}
+
+} // End of namespace EEM
diff --git a/engines/eem/eem.cpp b/engines/eem/eem.cpp
index 594fba894ef..cdbc7e4ac37 100644
--- a/engines/eem/eem.cpp
+++ b/engines/eem/eem.cpp
@@ -52,20 +52,12 @@ const uint kNumSitePals = 40;  ///< SITEPALS holds 40 palettes (40 * 768 = 30720
 // Picture / palette IDs from the original code (1-based picture IDs).
 const uint kPicEAKidsLogo      = 0x54;  ///< _ShowEAKids: GetPicture(0x54)
 const uint kPicHighScoreLogo   = 0x20c; ///< _ShowHScoreLogo: GetPicture(0x20c)
-const uint kPicChooseBackground = 0x8c; ///< _DoChoosePartner: GetBackground(0x8c)
 const uint kPalEAKids          = 0x25;
 const uint kPalHighScore       = 0x27;
 
-// Animation IDs (0-based per ANI.DBX). _DoChoosePartner uses GetAnimation(8/9).
-const uint kAniBoy  = 8;
-const uint kAniGirl = 9;
-
-// On-screen positions for the boy and girl partner sprites, from
-// _DoChoosePartner: NewAnimation(0xe2, 0x62, ...) and (0x42, 0x60, ...).
-const int kBoyX  = 0xe2; // 226
-const int kBoyY  = 0x62; // 98
-const int kGirlX = 0x42; // 66
-const int kGirlY = 0x60; // 96
+// Save format. Used by `saveGameState` / `loadGameState`.
+const uint32 kSaveMagic = MKTAG('E', 'E', 'M', '0');
+const byte   kSaveVer   = 3;  ///< v2: _mysteriesSolved tracker; v3: player name
 
 // 11x16 mouse cursor ??? replaces the DOS hardware cursor wired in by
 // _InitMouse @ 152d:018b (INT 33h). The original game sets the cursor
@@ -447,829 +439,6 @@ void EEMEngine::showHighScoreLogo() {
 	waitForInput(2500);
 }
 
-void EEMEngine::doNewPlayer() {
-	// Mirrors `_NewPlayer` @ 1c33:0dda. The original draws background
-	// 0x104 + character peek pic 0x107, then shows "Please type your
-	// name" and accepts up to 12 characters until Enter. We render a
-	// minimal version: black screen + prompt.
-	if (!_font.isLoaded()) {
-		_playerName = "Detective";
-		return;
-	}
-
-	Common::String name;
-	const int maxChars = 12;
-
-	// Mirror the original: load PIC 0x104 as the name-entry backdrop.
-	// The original also slides in PIC 0x107 (a peeking character).
-	Picture bg;
-	const bool haveBG = _picsArchive.getPicture(0x104, bg);
-
-	auto draw = [&]() {
-		Graphics::ManagedSurface scratch(320, 200,
-			Graphics::PixelFormat::createFormatCLUT8());
-		scratch.clear();
-		if (haveBG) {
-			const int w = MIN<int>(bg.surface.w, 320);
-			const int h = MIN<int>(bg.surface.h, 200);
-			for (int row = 0; row < h; row++)
-				memcpy((byte *)scratch.getBasePtr(0, row),
-					   (const byte *)bg.surface.getBasePtr(0, row), w);
-		}
-		// Match the original `_NewPlayer`: `_Show_String(rw=0x28, cl=0x50)`
-		// for the prompt, then `_ShowChar(0x50, x, ???)` for typed input.
-		// (rw=row=y, cl=col=x.) Prompt at (y=40, x=80), input at (y=80, x=80).
-		_font.drawString(&scratch, "Please type your name:", 80, 40, 240, 0xF);
-		Common::String shown = name + "_";
-		_font.drawString(&scratch, shown, 80, 80, 240, 0xF);
-		g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
-								   0, 0, 320, 200);
-		g_system->updateScreen();
-	};
-	draw();
-
-	while (!shouldQuit()) {
-		Common::Event ev;
-		bool dirty = false;
-		while (g_system->getEventManager()->pollEvent(ev)) {
-			if (ev.type == Common::EVENT_QUIT ||
-				ev.type == Common::EVENT_RETURN_TO_LAUNCHER)
-				return;
-			if (ev.type != Common::EVENT_KEYDOWN)
-				continue;
-			const Common::KeyCode k = ev.kbd.keycode;
-			if (k == Common::KEYCODE_RETURN) {
-				if (name.empty())
-					name = "Detective";
-				_playerName = name;
-				return;
-			}
-			if (k == Common::KEYCODE_ESCAPE) {
-				_playerName = "Detective";
-				return;
-			}
-			if (k == Common::KEYCODE_BACKSPACE) {
-				if (!name.empty()) {
-					name.deleteLastChar();
-					dirty = true;
-				}
-				continue;
-			}
-			if (ev.kbd.ascii >= ' ' && ev.kbd.ascii < 127 &&
-				(int)name.size() < maxChars) {
-				name += (char)ev.kbd.ascii;
-				dirty = true;
-			}
-		}
-		if (dirty)
-			draw();
-		g_system->updateScreen();
-		g_system->delayMillis(15);
-	}
-}
-
-void EEMEngine::doChoosePartner() {
-	// Mirrors _DoChoosePartner @ 1a35:0756. The original places boy + girl
-	// animations on a backdrop and polls four click rectangles (two per
-	// character) for the player's choice. We approximate by splitting the
-	// screen at x=160: left half = girl (Jenny), right half = boy (Jake).
-	Picture background;
-	if (!_picsArchive.getPicture(kPicChooseBackground, background)) {
-		warning("ChoosePartner background (%u) load failed", kPicChooseBackground);
-		return;
-	}
-
-	Animation boyAnim;
-	if (!_aniArchive.loadAnimation(kAniBoy, boyAnim) || boyAnim.empty()) {
-		warning("Boy animation (%u) load failed", kAniBoy);
-		return;
-	}
-	Animation girlAnim;
-	if (!_aniArchive.loadAnimation(kAniGirl, girlAnim) || girlAnim.empty()) {
-		warning("Girl animation (%u) load failed", kAniGirl);
-		return;
-	}
-
-	setAnmPalette(Common::Path("TITLE.ANM"));
-
-	// `_DoHappiness @ 172b:27b5`: the cursor's X column picks one of 4
-	// rects (29be:030f, all full-height); past rect 3 ??? "level 4".
-	// Each level swaps the partner's sequence script to a more / less
-	// "happy" cycle. Boy seqs at 29be:0337 (5 ?? 0x14 bytes), girl seqs
-	// at 29be:039b. Both cycle through 9 frames (the boy/girl anim
-	// cells contain 10 cells = pairs of "neutral, smile" at increasing
-	// intensity). Lifted verbatim from the binary so the gestures
-	// match the original beat-for-beat.
-	static const Common::Rect kHappyZones[4] = {
-		Common::Rect(  0, 0,  70, 200), // far left  ??? girl very happy, boy neutral
-		Common::Rect( 70, 0, 126, 200), // girl's column
-		Common::Rect(126, 0, 182, 200), // middle
-		Common::Rect(182, 0, 235, 200), // boy's column
-	};
-	static const uint8 kBoySeqs[5][9] = {
-		{ 0,0,0,0,0,0,0,1,0 }, // level 0
-		{ 2,2,2,2,2,2,2,3,2 }, // level 1
-		{ 4,4,4,4,4,4,4,5,4 }, // level 2
-		{ 6,6,6,6,6,6,7,6,6 }, // level 3
-		{ 8,8,8,8,8,8,8,8,9 }, // level 4 (cursor past zone 3)
-	};
-	static const uint8 kGirlSeqs[5][9] = {
-		{ 8,9,8,8,8,8,8,8,8 },
-		{ 6,6,6,7,6,6,6,6,6 },
-		{ 4,4,5,4,4,4,4,4,4 },
-		{ 2,2,2,2,2,2,3,2,2 },
-		{ 0,0,0,0,0,1,0,0,0 },
-	};
-	auto happinessLevel = [](int x) {
-		for (uint i = 0; i < ARRAYSIZE(kHappyZones); i++) {
-			if (kHappyZones[i].contains(x, 100))
-				return (uint)i;
-		}
-		return 4u; // past zone 3 ??? max level
-	};
-
-	int curMouseX = 0xa0;  // _DoChoosePartner sets `_SetMousePos(0xa0, 0x96)` on entry
-	int curMouseY = 0x96;
-	uint level = happinessLevel(curMouseX);
-	uint seqIdx = 0;       // step within the 9-frame seq
-
-	auto draw = [&]() {
-		blitAt(background, 0, 0);
-		const uint girlFrame = kGirlSeqs[level][seqIdx % 9];
-		const uint boyFrame  = kBoySeqs [level][seqIdx % 9];
-		blitAt(girlAnim[girlFrame % girlAnim.size()], kGirlX, kGirlY);
-		blitAt(boyAnim[boyFrame  % boyAnim.size()],  kBoyX,  kBoyY);
-		g_system->updateScreen();
-	};
-	draw();
-
-	debugC(1, kDebugGeneral, "ChoosePartner: %u boy frames at (%d,%d), "
-		   "%u girl frames at (%d,%d)",
-		   (uint)boyAnim.size(), kBoyX, kBoyY,
-		   (uint)girlAnim.size(), kGirlX, kGirlY);
-
-	uint32 lastTick = g_system->getMillis();
-	while (!shouldQuit()) {
-		// Advance through the 9-frame seq at 100 ms ??? `_CheckFrameRate`
-		// cadence. The seq is short and loops; matches the original
-		// `_UpdateAnimations` which restarts at curIdx=0 on the 0x80
-		// marker. Mirrors `_DoHappiness`'s rewriting of `curIdx = 0xFFFF`
-		// when the cursor crosses zones (we restart `seqIdx` instead).
-		if (g_system->getMillis() - lastTick > 100) {
-			lastTick = g_system->getMillis();
-			seqIdx = (seqIdx + 1) % 9;
-			draw();
-		}
-
-		Common::Event ev;
-		bool done = false;
-		while (g_system->getEventManager()->pollEvent(ev)) {
-			if (ev.type == Common::EVENT_QUIT ||
-				ev.type == Common::EVENT_RETURN_TO_LAUNCHER) {
-				done = true;
-				break;
-			}
-			if (ev.type == Common::EVENT_MOUSEMOVE) {
-				curMouseX = ev.mouse.x;
-				curMouseY = ev.mouse.y;
-				const uint newLevel = happinessLevel(curMouseX);
-				if (newLevel != level) {
-					level = newLevel;
-					seqIdx = 0; // restart cycle so the gesture pops
-					draw();
-				}
-			}
-			if (ev.type == Common::EVENT_LBUTTONDOWN) {
-				_partner = (ev.mouse.x >= 160) ? 0 : 1;
-				debugC(1, kDebugGeneral, "Partner picked: %s",
-					   _partner == 0 ? "Jake" : "Jennifer");
-				done = true;
-				break;
-			}
-			if (ev.type == Common::EVENT_KEYDOWN) {
-				if (ev.kbd.keycode == Common::KEYCODE_LEFT) {
-					_partner = 1; done = true; break;
-				}
-				if (ev.kbd.keycode == Common::KEYCODE_RIGHT) {
-					_partner = 0; done = true; break;
-				}
-				if (ev.kbd.keycode == Common::KEYCODE_RETURN ||
-					ev.kbd.keycode == Common::KEYCODE_ESCAPE) {
-					done = true; break;
-				}
-			}
-		}
-		if (done)
-			break;
-		g_system->updateScreen();
-		g_system->delayMillis(20);
-	}
-}
-
-void EEMEngine::doCaseSelection() {
-	// Mirrors `_CaseSelection` @ 1c33:0a87. The original draws PIC 0x41
-	// (chooser background) plus a centred "Book %d" / "Challenge Book"
-	// header at (y=12) and then calls `_DoChoose(list)` to render the
-	// menu via `DrawList` @ 1c33:040d at (_TextBox+3, DAT_29be_0d02) =
-	// (61, 35), 12 rows ?? 10 px line height. The menu list itself is
-	// the static array at 29be:0d6a (verified via `push 0x0d6a` at
-	// 1c33:1ab4). Strings are at 29be:0ef4 onwards. Layout:
-	//   list[0]  = "----------------------------------"
-	//   list[1]  = "         Choose A Mystery"
-	//   list[2..10] = alternating menu items + separators
-	// Five selectable items: Choose A Mystery / Practice Mystery /
-	// See ScrapBook 1/2/3.
-	const uint kMaxMystery = 54;
-
-	enum MenuPick {
-		kPickChoose = 0,
-		kPickPractice,
-		kPickScrap1,
-		kPickScrap2,
-		kPickScrap3,
-		kNumPicks
-	};
-	const char *kPickLabel[kNumPicks] = {
-		"         Choose A Mystery",
-		"         Practice Mystery",
-		"         See ScrapBook 1",
-		"         See ScrapBook 2",
-		"         See ScrapBook 3"
-	};
-	// ScrapBooks aren't implemented yet ??? grey them so the player can't
-	// stop on them, mirroring the original `_Greys` mask.
-	const bool kPickEnabled[kNumPicks] = { true, true, false, false, false };
-	uint pick = kPickChoose;
-
-	const char *kSeparator = "----------------------------------";
-
-	// Click rectangles from the original `_DoChoose` @ 1c33:0514 ??? each
-	// `_InRect(_MouseX, _MouseY, addr, 0x29be)` reads one 4??u16 rect at
-	// the listed offset in segment 29be ({x1, y1, x2, y2}). We use
-	// `Common::Rect` (left/top/right/bottom) which also gives us
-	// `contains(x, y)` for hit testing.
-	const Common::Rect kOkRect      ( 12,  63,  41,  87); // 29be:0cd8 confirm
-	const Common::Rect kHelpRect    ( 12, 100,  41, 124); // 29be:0ce0 help
-	const Common::Rect kExitRect    ( 12, 137,  41, 161); // 29be:0ce8 cancel
-	const Common::Rect kUpArrowRect (240,  31, 250,  43); // 29be:0cf0 scroll up
-	const Common::Rect kDnArrowRect (240, 148, 250, 159); // 29be:0cf8 scroll dn
-	const Common::Rect kListRect    ( 58,  35, 238, 158); // 29be:0d00 list panel
-
-	// The original `_NewPlayer` set `_MouseCursor = 1` on exit; the
-	// chain of screens after it expects the cursor to stay visible.
-	// Reassert here in case anything between hid it.
-	CursorMan.showMouse(true);
-
-	// Mirrors `_CaseSelection`: load PIC 0x41 as the chooser backdrop.
-	Picture caseBg;
-	const bool haveCaseBg = _picsArchive.getPicture(0x41, caseBg);
-
-	// KD greeter sprite. `_CaseSelection @ 1c33:0a87` (1c33:0b7e-0ba1)
-	// loads anim 0x15 (Jake-paired) or 0x16 (Jenny-paired) and registers
-	// `_NewAnimation(0x112, 0x50, ..., seqnum=0x15, prior=1)` ??? partner-
-	// dependent because the host KD changes who's "with him" on the
-	// briefing intro frame. Runs continuously through the menu loop via
-	// `_UpdateAnimations`. We approximate with millis-based frame cycling.
-	const uint kKdAniId = (_partner == 0) ? 0x15 : 0x16;
-	Animation kdAnim;
-	const bool haveKdAnim = _aniArchive.loadAnimation(kKdAniId, kdAnim)
-							 && !kdAnim.empty();
-	const int kKdAnimX = 0x112;
-	const int kKdAnimY = 0x50;
-
-	auto draw = [&]() {
-		Graphics::ManagedSurface scratch(320, 200,
-			Graphics::PixelFormat::createFormatCLUT8());
-		scratch.clear();
-		if (haveCaseBg) {
-			const int w = MIN<int>(caseBg.surface.w, 320);
-			const int h = MIN<int>(caseBg.surface.h, 200);
-			for (int row = 0; row < h; row++) {
-				memcpy((byte *)scratch.getBasePtr(0, row),
-					   (const byte *)caseBg.surface.getBasePtr(0, row), w);
-			}
-		}
-
-		// KD greeter frame ??? masked-blit current animation cell at
-		// (0x112, 0x50). 100 ms tick matches the engine's `_CheckFrameRate`.
-		if (haveKdAnim) {
-			const uint32 now = g_system->getMillis();
-			const uint frameIdx = (uint)((now / 100) % kdAnim.size());
-			const Picture &fr = kdAnim[frameIdx];
-			const byte transp = (byte)(fr.flags >> 8);
-			for (int row = 0; row < fr.surface.h; row++) {
-				const int dstY = kKdAnimY + row;
-				if (dstY < 0 || dstY >= 200) continue;
-				const byte *src = (const byte *)fr.surface.getBasePtr(0, row);
-				byte *dst = (byte *)scratch.getBasePtr(0, dstY);
-				for (int col = 0; col < fr.surface.w; col++) {
-					const int dstX = kKdAnimX + col;
-					if (dstX < 0 || dstX >= 320) continue;
-					if (src[col] != transp)
-						dst[dstX] = src[col];
-				}
-			}
-		}
-		if (_font.isLoaded()) {
-			// `DrawList` @ 1c33:040d coordinates: `_TextBox + 3` for x
-			// and `DAT_29be_0d02` for y. `_TextBox` @ 29be:0d00 holds
-			// {x=58, y=35, x2=238, y2=158}. Matches the blue panel.
-			const int kListX  = 58 + 3;
-			const int kListW  = 238 - kListX;
-			const int kListY0 = 35;
-			const int kLineH  = 10;
-
-			// Top centred "Book %d" / "Challenge Book" title ??? sprintf
-			// format strings at 29be:0deb / 29be:0dfa shown via
-			// `_Show_String(0xc, (0xba - width)/2 + 0x3c, ???)` in the
-			// original. We don't track challenge tier yet so always
-			// show "Book 1".
-			const Common::String book = "Book 1";
-			const int titleW = _font.getStringWidth(book);
-			const int titleX = (0xba - titleW) / 2 + 0x3c;
-			_font.drawString(&scratch, book, titleX, 12, 320, 0xF);
-
-			// Render 11 list rows: separator + menu item pairs.
-			//   row 0  separator
-			//   row 1  Choose A Mystery
-			//   row 2  separator
-			//   row 3  Practice Mystery
-			//   ...
-			//   row 9  See ScrapBook 3
-			//   row 10 separator
-			for (int r = 0; r < 11; r++) {
-				const int y = kListY0 + r * kLineH;
-				if ((r & 1) == 0) {
-					_font.drawString(&scratch, kSeparator, kListX, y, kListW, 0x7);
-					continue;
-				}
-				const uint mp = (uint)(r >> 1);
-				const bool isSel  = (mp == pick);
-				const byte color  = isSel        ? 0xF :
-									kPickEnabled[mp] ? 0x7 : 0x8;
-				_font.drawString(&scratch, kPickLabel[mp], kListX, y, kListW, color);
-			}
-		}
-		g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
-								   0, 0, 320, 200);
-		g_system->updateScreen();
-	};
-
-	auto pickPrev = [&]() {
-		for (int i = 0; i < (int)kNumPicks; i++) {
-			pick = (pick == 0) ? (uint)(kNumPicks - 1) : pick - 1;
-			if (kPickEnabled[pick])
-				break;
-		}
-	};
-	auto pickNext = [&]() {
-		for (int i = 0; i < (int)kNumPicks; i++) {
-			pick = (pick + 1) % kNumPicks;
-			if (kPickEnabled[pick])
-				break;
-		}
-	};
-
-	draw();
-	uint32 lastTick = g_system->getMillis();
-
-	bool exitChosen = false;
-	while (!shouldQuit()) {
-		Common::Event ev;
-		bool confirmed = false;
-		// Redraw every 100 ms so the KD greeter cycles. Mirrors the
-		// `_CheckFrameRate` cadence in `_CaseSelection`'s main loop.
-		const uint32 now = g_system->getMillis();
-		if (haveKdAnim && now - lastTick >= 100) {
-			lastTick = now;
-			draw();
-		}
-		while (g_system->getEventManager()->pollEvent(ev)) {
-			if (ev.type == Common::EVENT_QUIT ||
-				ev.type == Common::EVENT_RETURN_TO_LAUNCHER)
-				return;
-			if (ev.type == Common::EVENT_LBUTTONDOWN) {
-				// OK / EXIT / HELP buttons (rectangles from `_DoChoose`).
-				if (kOkRect.contains(ev.mouse.x, ev.mouse.y)) {
-					confirmed = true;
-					break;
-				}
-				if (kExitRect.contains(ev.mouse.x, ev.mouse.y)) {
-					exitChosen = true;
-					confirmed = true;
-					break;
-				}
-				if (kHelpRect.contains(ev.mouse.x, ev.mouse.y)) {
-					// HELP placeholder ??? original calls `_DisplayHint`;
-					// our help screen is wired to `H` later in the flow.
-					continue;
-				}
-				// List panel: click on a non-separator row selects the
-				// menu entry under the cursor.
-				if (kListRect.contains(ev.mouse.x, ev.mouse.y)) {
-					const int kLineH = 10;
-					const int row = (ev.mouse.y - kListRect.top) / kLineH;
-					if ((row & 1) == 1) {
-						const uint mp = (uint)(row >> 1);
-						if (mp < kNumPicks && kPickEnabled[mp]) {
-							pick = mp;
-							draw();
-							continue;
-						}
-					}
-				}
-			}
-			if (ev.type != Common::EVENT_KEYDOWN)
-				continue;
-			const Common::KeyCode k = ev.kbd.keycode;
-			if (k == Common::KEYCODE_ESCAPE) {
-				exitChosen = true;
-				confirmed = true;
-				break;
-			}
-			if (k == Common::KEYCODE_RETURN) {
-				confirmed = true;
-				break;
-			}
-			if (k == Common::KEYCODE_UP || k == Common::KEYCODE_LEFT) {
-				pickPrev();
-				draw();
-				continue;
-			}
-			if (k == Common::KEYCODE_DOWN || k == Common::KEYCODE_RIGHT ||
-				k == Common::KEYCODE_TAB) {
-				pickNext();
-				draw();
-				continue;
-			}
-		}
-		if (confirmed) {
-			draw();
-			break;
-		}
-		g_system->updateScreen();
-		g_system->delayMillis(15);
-	}
-
-	if (shouldQuit())
-		return;
-
-	if (exitChosen) {
-		_mystery.clear();
-		_nextScreen = kScreenInvalid;
-		return;
-	}
-
-	// "Practice Mystery" is the tutorial ??? mystery 0.
-	if (pick == kPickPractice) {
-		if (!_mystery.load(0, &_rng)) {
-			warning("doCaseSelection: failed to load practice mystery");
-			_mystery.clear();
-		}
-		return;
-	}
-
-	if (pick != kPickChoose) {
-		// ScrapBooks aren't implemented; bail back to the menu loop.
-		_mystery.clear();
-		return;
-	}
-
-	// "Choose A Mystery" sub-screen: pick a specific case from the
-	// 55-mystery roster. The original opens a different list here;
-	// we approximate with the tier-aware numeric chooser we used
-	// before. Default to the first unsolved mystery.
-	uint sel = 0;
-	for (uint i = 0; i <= kMaxMystery; i++) {
-		if (i < sizeof(_mysteriesSolved) && !_mysteriesSolved[i]) {
-			sel = i;
-			break;
-		}
-	}
-
-	auto drawSubmenu = [&]() {
-		Graphics::ManagedSurface scratch(320, 200,
-			Graphics::PixelFormat::createFormatCLUT8());
-		scratch.clear();
-		if (haveCaseBg) {
-			const int w = MIN<int>(caseBg.surface.w, 320);
-			const int h = MIN<int>(caseBg.surface.h, 200);
-			for (int row = 0; row < h; row++)
-				memcpy((byte *)scratch.getBasePtr(0, row),
-					   (const byte *)caseBg.surface.getBasePtr(0, row), w);
-		}
-		if (_font.isLoaded()) {
-			const int kListX  = 61;
-			const int kListW  = 238 - kListX;
-			const int kListY0 = 35;
-			const int kLineH  = 10;
-			const int kVisible = 12;
-			int top = (int)sel - kVisible / 2;
-			if (top < 0) top = 0;
-			if (top + kVisible > (int)kMaxMystery + 1)
-				top = (int)kMaxMystery + 1 - kVisible;
-			for (int r = 0; r < kVisible; r++) {
-				const int idx = top + r;
-				if (idx > (int)kMaxMystery)
-					break;
-				char marker = ' ';
-				if ((uint)idx < sizeof(_mysteriesSolved)) {
-					if (_mysteriesSolved[idx] == 2) marker = '*';
-					else if (_mysteriesSolved[idx] == 1) marker = '+';
-				}
-				const char arrow = ((uint)idx == sel) ? '>' : ' ';
-				_font.drawString(&scratch,
-								 Common::String::format("%c %c Mystery %d", arrow, marker, idx),
-								 kListX, kListY0 + r * kLineH, kListW, 0xF);
-			}
-		}
-		g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
-								   0, 0, 320, 200);
-		g_system->updateScreen();
-	};
-
-	drawSubmenu();
-	bool confirmed = false;
-	while (!confirmed && !shouldQuit()) {
-		Common::Event ev;
-		while (g_system->getEventManager()->pollEvent(ev)) {
-			if (ev.type == Common::EVENT_QUIT ||
-				ev.type == Common::EVENT_RETURN_TO_LAUNCHER)
-				return;
-			if (ev.type == Common::EVENT_LBUTTONDOWN) {
-				// Same `_DoChoose` rectangles as the top-level menu.
-				if (kOkRect.contains(ev.mouse.x, ev.mouse.y)) {
-					confirmed = true;
-					break;
-				}
-				if (kExitRect.contains(ev.mouse.x, ev.mouse.y)) {
-					_mystery.clear();
-					return;
-				}
-				if (kUpArrowRect.contains(ev.mouse.x, ev.mouse.y)) {
-					sel = (sel == 0) ? kMaxMystery : sel - 1;
-					drawSubmenu();
-					continue;
-				}
-				if (kDnArrowRect.contains(ev.mouse.x, ev.mouse.y)) {
-					sel = (sel >= kMaxMystery) ? 0 : sel + 1;
-					drawSubmenu();
-					continue;
-				}
-				if (kListRect.contains(ev.mouse.x, ev.mouse.y)) {
-					// Pick the row under the cursor.
-					const int kLineH = 10;
-					const int kVisible = 12;
-					int top = (int)sel - kVisible / 2;
-					if (top < 0) top = 0;
-					if (top + kVisible > (int)kMaxMystery + 1)
-						top = (int)kMaxMystery + 1 - kVisible;
-					const int row = (ev.mouse.y - kListRect.top) / kLineH;
-					const int idx = top + row;
-					if (idx >= 0 && idx <= (int)kMaxMystery) {
-						sel = (uint)idx;
-						drawSubmenu();
-					}
-					continue;
-				}
-			}
-			if (ev.type != Common::EVENT_KEYDOWN)
-				continue;
-			const Common::KeyCode k = ev.kbd.keycode;
-			if (k == Common::KEYCODE_ESCAPE) {
-				_mystery.clear();
-				return;
-			}
-			if (k == Common::KEYCODE_RETURN) {
-				confirmed = true;
-				break;
-			}
-			if (k >= Common::KEYCODE_0 && k <= Common::KEYCODE_9) {
-				sel = (uint)(k - Common::KEYCODE_0);
-				drawSubmenu();
-				continue;
-			}
-			if (k == Common::KEYCODE_DOWN || k == Common::KEYCODE_TAB) {
-				sel = (sel >= kMaxMystery) ? 0 : sel + 1;
-				drawSubmenu();
-				continue;
-			}
-			if (k == Common::KEYCODE_UP) {
-				sel = (sel == 0) ? kMaxMystery : sel - 1;
-				drawSubmenu();
-				continue;
-			}
-			if (k == Common::KEYCODE_PAGEDOWN) {
-				sel = (sel + 10 > kMaxMystery) ? kMaxMystery : sel + 10;
-				drawSubmenu();
-				continue;
-			}
-			if (k == Common::KEYCODE_PAGEUP) {
-				sel = (sel < 10) ? 0 : sel - 10;
-				drawSubmenu();
-				continue;
-			}
-			if (k == Common::KEYCODE_HOME) { sel = 0; drawSubmenu(); continue; }
-			if (k == Common::KEYCODE_END)  { sel = kMaxMystery; drawSubmenu(); continue; }
-		}
-		g_system->updateScreen();
-		g_system->delayMillis(15);
-	}
-
-	if (!_mystery.load(sel, &_rng)) {
-		warning("doCaseSelection: failed to load mystery %u", sel);
-		_mystery.clear();
-		return;
-	}
-	debugC(1, kDebugMystery, "Mystery %u loaded; %u sites, %u suspects",
-		   sel, _mystery.numSites(), _mystery.numSuspects());
-}
-
-void EEMEngine::doInitClues() {
-	// Mirrors `_DoInitClues` @ 1a35:0411. The original does:
-	//   1. _AllBlack(); _GetBackground(0x52); _GetPalette(0x22);
-	//   2. _GetAnimation(gameAni); _NewAnimation(0xcd, 0x6c, ...)
-	//      _GetAnimation(bookAni); _NewAnimation(0,    99,   ...)
-	//      (case type 1 also: _NewAnimation(0x68, 0x8b, nancyAni))
-	//   3. _UpdateAnimations(); _FadeIn();
-	//   4. while (frame != gameNum) { _CheckFrameRate(); _UpdateAnimations(); }
-	//        ??? cycles through the entire game animation once. Click skips.
-	//   5. _PlayInSequence(seqId, ...) ??? plays a follow-up sequence based
-	//      on partner + case type.
-	//   6. _DisplayClue(InitBlock + 2, 1) ??? the briefing dialogue.
-	//   7. _OnSites[startSite] = 1.
-	//
-	// gameAni / bookAni / nancyAni values verified directly from Ghidra:
-	//   gameAni  = 0x17 (Jake) / 0x3b (Jenny)
-	//   bookAni  = 0x18 (Jake) / 0x3c (Jenny)
-	//   nancyAni = 0x19 (case type 1 only)
-	if (!_mystery.isLoaded())
-		return;
-
-	const byte *ib = _mystery.initBlock();
-	if (!ib)
-		return;
-
-	const uint16 startSite = READ_LE_UINT16(ib + 2);
-	if (startSite < Mystery::kVisitedSiteCap)
-		_mystery._onSites[startSite] = 1;
-	_mystery._siteNumber = startSite;
-	_mystery._lastSite = startSite;
-
-	setSitePalette(0x22);
-	Picture bg;
-	if (_picsArchive.getPicture(0x52, bg))
-		blitAt(bg, 0, 0);
-
-	const uint gameAni = _partner == 0 ? 0x17 : 0x3b;
-	const uint bookAni = _partner == 0 ? 0x18 : 0x3c;
-	Animation game, book, nancy;
-	const bool haveGame  = _aniArchive.loadAnimation(gameAni, game) && !game.empty();
-	const bool haveBook  = _aniArchive.loadAnimation(bookAni, book) && !book.empty();
-
-	const uint16 caseType = READ_LE_UINT16(ib);
-	const bool haveNancy = (caseType == 1)
-						  && _aniArchive.loadAnimation(0x19, nancy)
-						  && !nancy.empty();
-
-	auto blitMaskedAt = [&](const Picture &p, int x, int y) {
-		const byte transp = (byte)(p.flags >> 8);
-		Graphics::Surface *screen = g_system->lockScreen();
-		if (!screen) return;
-		for (int row = 0; row < p.surface.h; row++) {
-			const int dstY = y + row;
-			if (dstY < 0 || dstY >= screen->h) continue;
-			const byte *src = (const byte *)p.surface.getBasePtr(0, row);
-			byte *dst = (byte *)screen->getBasePtr(0, dstY);
-			for (int col = 0; col < p.surface.w; col++) {
-				const int dstX = x + col;
-				if (dstX < 0 || dstX >= screen->w) continue;
-				if (src[col] != transp)
-					dst[dstX] = src[col];
-			}
-		}
-		g_system->unlockScreen();
-	};
-
-	// Step 4 ??? cycle through the game animation once before the briefing.
-	// Mirrors the `while (uVar9 != gameNum)` loop. The original calls
-	// `_UpdateAnimations` per `_CheckFrameRate` tick (~10 fps). We use
-	// 100 ms ticks for the same cadence. Click / key skips.
-	if (haveGame || haveBook || haveNancy) {
-		const uint frameCount = haveGame ? game.size() : 8;
-		bool skip = false;
-		for (uint frame = 0; frame < frameCount && !shouldQuit() && !skip; frame++) {
-			// Restore BG + advance frame.
-			if (_picsArchive.getPicture(0x52, bg))
-				blitAt(bg, 0, 0);
-			if (haveGame)
-				blitMaskedAt(game[frame % game.size()], 0xcd, 0x6c);
-			if (haveBook)
-				blitMaskedAt(book[frame % book.size()], 0, 99);
-			if (haveNancy)
-				blitMaskedAt(nancy[frame % nancy.size()], 0x68, 0x8b);
-			g_system->updateScreen();
-
-			// Wait 100 ms or until input.
-			const uint32 wakeup = g_system->getMillis() + 100;
-			while (g_system->getMillis() < wakeup && !shouldQuit() && !skip) {
-				Common::Event ev;
-				while (g_system->getEventManager()->pollEvent(ev)) {
-					if (ev.type == Common::EVENT_LBUTTONDOWN ||
-						ev.type == Common::EVENT_KEYDOWN) {
-						skip = true;
-						break;
-					}
-				}
-				g_system->delayMillis(10);
-			}
-		}
-	}
-
-	// Composite the final frames (or first frames if skipped) so the BG
-	// is in a sensible state when displayClue overlays the speaker.
-	if (_picsArchive.getPicture(0x52, bg))
-		blitAt(bg, 0, 0);
-	if (haveGame)
-		blitMaskedAt(game[0], 0xcd, 0x6c);
-	if (haveBook)
-		blitMaskedAt(book[0], 0, 99);
-	if (haveNancy)
-		blitMaskedAt(nancy[0], 0x68, 0x8b);
-	g_system->updateScreen();
-
-	// Step 5 ??? `_PlayInSequence(animSeq, 0xcd, animY)` per Ghidra:
-	//   Jake (partner=0):
-	//     caseType=1 ??? anim 0x38 at (0xcd, 0x6d)
-	//     caseType=2 ??? anim 0x37 at (0xcd, 0x6c)
-	//     caseType=3 ??? anim 0x39 at (0xcd, 0x6c)
-	//   Jenny (partner=1):
-	//     caseType=2 ??? anim 0x3a at (0xcd, 0x6c)
-	//     caseType=3 ??? anim 0x3d at (0xcd, 0x6c)
-	// `_PlayInSequence @ 172b:2d03` plays each frame at (sx-w, sy-rowoff)
-	// with mask blit, advancing one frame per `_CheckFrameRate` tick.
-	uint16 seqAni = 0xFFFF;
-	uint16 seqY   = 0x6c;
-	if (_partner == 0) {
-		switch (caseType) {
-		case 1: seqAni = 0x38; seqY = 0x6d; break;
-		case 2: seqAni = 0x37; seqY = 0x6c; break;
-		case 3: seqAni = 0x39; seqY = 0x6c; break;
-		default: break;
-		}
-	} else {
-		switch (caseType) {
-		case 2: seqAni = 0x3a; seqY = 0x6c; break;
-		case 3: seqAni = 0x3d; seqY = 0x6c; break;
-		default: break;
-		}
-	}
-	if (seqAni != 0xFFFF) {
-		Animation seq;
-		if (_aniArchive.loadAnimation(seqAni, seq) && !seq.empty()) {
-			bool skip = false;
-			for (uint frame = 0; frame < seq.size() && !shouldQuit() && !skip;
-				 frame++) {
-				const Picture &fr = seq[frame];
-				// Restore BG + base anim frames so each new frame
-				// composites cleanly.
-				if (_picsArchive.getPicture(0x52, bg))
-					blitAt(bg, 0, 0);
-				if (haveGame)
-					blitMaskedAt(game[frame % game.size()], 0xcd, 0x6c);
-				if (haveBook)
-					blitMaskedAt(book[frame % book.size()], 0, 99);
-				if (haveNancy)
-					blitMaskedAt(nancy[frame % nancy.size()], 0x68, 0x8b);
-				// Anchor: original blits at `(sx - frame.width,
-				// sy - frame.rowoff)`. `frame.rowoff` is the y-anchor
-				// in our PicData. We use width/height directly since
-				// loadAnimation places anchor at (0, 0).
-				const int dstX = (int)0xcd - (int)fr.surface.w;
-				const int dstY = (int)seqY - (int)fr.rowoff;
-				blitMaskedAt(fr, dstX, dstY);
-				g_system->updateScreen();
-				const uint32 wakeup = g_system->getMillis() + 100;
-				while (g_system->getMillis() < wakeup &&
-					   !shouldQuit() && !skip) {
-					Common::Event ev;
-					while (g_system->getEventManager()->pollEvent(ev)) {
-						if (ev.type == Common::EVENT_LBUTTONDOWN ||
-							ev.type == Common::EVENT_KEYDOWN) {
-							skip = true;
-							break;
-						}
-					}
-					g_system->delayMillis(10);
-				}
-			}
-		}
-	}
-
-	// Step 6 ??? case briefing dialogue.
-	displayClue(ib + 4);
-}
-
 void EEMEngine::doSiteLoop() {
 	// Mirrors the per-mystery site loop. SiteScreen::run() handles
 	// hotspot clicks plus M (map), N (notebook), G (gallery), A (accuse),
@@ -1278,2434 +447,6 @@ void EEMEngine::doSiteLoop() {
 	screen.run();
 }
 
-/// Mirror `_ParseString` @ 1b66:07c3 ??? substitute the control bytes that
-/// the original engine uses as placeholders. Only the two we encounter most
-/// often (player name = 0x80, partner first name = 0x82) are substituted;
-/// other 0x8N opcodes are stripped. The original engine also handles
-/// hyphenation marks and a hint placeholder (0x89) we ignore for now.
-static Common::String parseString(const Common::String &raw,
-								  const Common::String &playerName,
-								  uint partner) {
-	// Substitution opcodes from `_ParseString` @ 1b66:07c3, jump-table
-	// at 1b66:0cbe. Each handler reads `_Partner` (16-bit at 0x7918)
-	// and indexes the name table at 29be:0c28 ({Jake, Jennifer, he,
-	// she, him, her, his} as far pointers).
-	//   0x80 ??? player's typed name (auto-cap word starts) ??? uses _PlayerRecord
-	//   0x81 ??? _Partner == 0 ? "Jake"     : "Jennifer"  (chosen detective)
-	//   0x82 ??? _Partner == 0 ? "Jennifer" : "Jake"      (the OTHER one)
-	//   0x83 ??? _Partner == 0 ? "he"       : "she"
-	//   0x84 ??? _Partner == 0 ? "him"      : "her"
-	//   0x85 ??? _Partner == 0 ? "his"      : "her"
-	//   0x86..0x88 read a different gender flag at 0x7985 ??? left alone
-	//     until that flag's source is traced.
-	//   0x89 ??? KD hint placeholder (handled by caller).
-	const bool isJake = (partner == 0);
-	Common::String out;
-	for (uint i = 0; i < raw.size(); i++) {
-		const byte c = (byte)raw[i];
-		switch (c) {
-		case 0x80:
-			out += playerName;
-			break;
-		case 0x81:
-			out += isJake ? "Jake" : "Jennifer";
-			break;
-		case 0x82:
-			out += isJake ? "Jennifer" : "Jake";
-			break;
-		case 0x83:
-			out += isJake ? "he" : "she";
-			break;
-		case 0x84:
-			out += isJake ? "him" : "her";
-			break;
-		case 0x85:
-			out += isJake ? "his" : "her";
-			break;
-		case 0x86:
-		case 0x87:
-		case 0x88:
-		case 0x89:
-			// Eaten silently ??? see comment above.
-			break;
-		case 0:
-			return out;
-		case '\r':
-			break;
-		default:
-			out += (char)c;
-			break;
-		}
-	}
-	return out;
-}
-
-void EEMEngine::applyClueSideEffects(const byte *c) {
-	for (uint j = 0; j < 5; j++) {
-		const uint16 note = READ_LE_UINT16(c + 0x30 + j * 2);
-		if (note != 0xFFFF && note < Mystery::kCluesFoundCap)
-			_mystery._cluesFound[note] = 1;
-
-		const uint16 galIdx = READ_LE_UINT16(c + 0x26 + j * 2);
-		if (galIdx != 0xFFFF && galIdx < Mystery::kGalleryCap) {
-			const uint8 phys = _mystery._newOrder[galIdx];
-			if (phys < Mystery::kGalleryCap)
-				_mystery._inGallery[phys] = 1;
-		}
-
-		const uint16 siteIdx = READ_LE_UINT16(c + 0x1c + j * 2);
-		if (siteIdx != 0xFFFF) {
-			const uint16 siteVal = siteIdx & 0x7FFF;
-			if (siteVal < Mystery::kVisitedSiteCap)
-				_mystery._onSites[siteVal] = 1;
-			if (siteIdx & 0x8000)
-				_mystery._sawCONSITEs = true;
-		}
-	}
-}
-
-void EEMEngine::displayClue(const byte *clueBlock) {
-	if (!clueBlock || !_mystery.isLoaded())
-		return;
-
-	// ClueBlock layout (verified against M0.BIN):
-	//   +0..1: number (entry count)
-	//   +2..3: pic ID for entry 0 (entry N>0 uses prev entry's last 2 bytes)
-	//   +4..:  array of 62-byte entries
-	const uint16 number = READ_LE_UINT16(clueBlock);
-	debugC(1, kDebugScript, "displayClue: %u entries", number);
-	if (number == 0 || number > 32) {
-		// number==0 = no briefing (e.g. mystery 0 case-type 4); >32 is a
-		// guard against bad pointers.
-		return;
-	}
-
-	// Snapshot the current screen as the BG so character pics from
-	// earlier entries don't stack on top of each other.
-	Graphics::ManagedSurface bg(320, 200,
-		Graphics::PixelFormat::createFormatCLUT8());
-	bg.clear();
-	{
-		Graphics::Surface *screen = g_system->lockScreen();
-		if (screen) {
-			for (int row = 0; row < 200; row++) {
-				memcpy((byte *)bg.getBasePtr(0, row),
-					   (const byte *)screen->getBasePtr(0, row), 320);
-			}
-			g_system->unlockScreen();
-		}
-	}
-
-	for (uint i = 0; i < number && !shouldQuit(); i++) {
-		// Restore BG before drawing this entry's portrait + balloon.
-		g_system->copyRectToScreen(bg.getPixels(), bg.pitch, 0, 0, 320, 200);
-		const byte *c = clueBlock + 4 + i * 62;
-		// Per-partner fields:
-		//   +0..1, +2..3: tx, ty (partner 0)
-		//   +4..5, +6..7: tx, ty (partner 1)
-		//   +8..9, +10..11: bubText offset for partner 0/1 (rel. TextBlock)
-		//   +12..13, +14..15: balloon picture ID for partner 0/1
-		//   +16..17, +18..19: bubX, bubY
-		//   +0x3a..+0x3b:    KD-anim number (-1 = none)
-		// Per `_DisplayClue` @ 2404:05e6: partner 1 uses its own field
-		// set ONLY when bubText1 is not -1; otherwise it falls back to
-		// the partner 0 fields entirely. Partner 0 always uses field 0.
-
-		// Per-clue partner reaction animation. `_DisplayClue` @
-		// 2404:0635-064b checks `clueEntry[+0x3a]` and, when not -1,
-		// calls `_DoKDAnim(num)` BEFORE drawing the speaker portrait.
-		// This is what surfaces "Jenny takes a picture with a camera"
-		// (and the matching Jake gestures) during NPC searches.
-		const int16 kdAnimNum = (int16)READ_LE_UINT16(c + 0x3a);
-		if (kdAnimNum != -1)
-			playKdAnim((uint16)kdAnimNum);
-
-		const bool useP1 = (_partner == 1) &&
-			(READ_LE_UINT16(c + 10) != 0xFFFF);
-		const uint partner = useP1 ? 1 : 0;
-		const uint16 textOff = READ_LE_UINT16(c + 8 + partner * 2);
-		const bool hasText = (textOff != 0xFFFF);
-		// Partner 1 bubX/bubY at +0x14/+0x16; partner 0 at +0x10/+0x12.
-		const uint16 bubX = READ_LE_UINT16(c + (useP1 ? 0x14 : 0x10));
-		const uint16 bubY = READ_LE_UINT16(c + (useP1 ? 0x16 : 0x12));
-		const uint16 bubNum = READ_LE_UINT16(c + (useP1 ? 0x0E : 0x0C));
-		const char *raw   = hasText ? _mystery.textAt(textOff) : "";
-
-		// Speaker portrait. Mirrors `_DisplayClue`'s `pic[clues+i*62-2]`:
-		// for entry 0 the pic ID is in the ClueBlock header at +2; for
-		// later entries it sits in the previous entry's last 2 bytes.
-		// Speaker portrait position uses partner 0 fields (+0..+3) when
-		// _partner==0 or when partner 1 falls back; otherwise partner 1
-		// fields (+4..+7). Same logic as the original.
-		const uint16 charX  = READ_LE_UINT16(c + (useP1 ? 4 : 0));
-		const uint16 charY  = READ_LE_UINT16(c + (useP1 ? 6 : 2));
-		const uint16 charPicId = (i == 0)
-			? READ_LE_UINT16(clueBlock + 2)
-			: READ_LE_UINT16(c - 2);
-		if (charPicId != 0 && charPicId != 0xFFFF) {
-			Picture charPic;
-			if (_picsArchive.getPicture(charPicId, charPic) &&
-				charX < 320 && charY < 200) {
-				const int w = MIN<int>(charPic.surface.w, 320 - charX);
-				const int h = MIN<int>(charPic.surface.h, 200 - charY);
-				if (w > 0 && h > 0)
-					g_system->copyRectToScreen(charPic.surface.getPixels(),
-						charPic.surface.pitch, charX, charY, w, h);
-			}
-		}
-
-		// Substitute control bytes (0x80..0x89) ??? see `parseString` for
-		// the table. 0x81 = chosen detective, 0x82 = the other one.
-		const Common::String text = parseString(raw ? raw : "",
-												_playerName, _partner);
-
-		// Speech balloon. Mirrors `_GetBalloon` + `_AddPicBackground` in
-		// `_DisplayClue`. The original looks up per-balloon text-area
-		// metadata in a table at offset 0x875 (within `_DisplayClue`'s
-		// segment); we don't have that table decoded yet, so we use a
-		// fixed inset of 8 px from the balloon's top-left.
-		Picture balloon;
-		const uint16 balloonId = bubNum & 0x7F;
-		const bool haveBalloon = bubNum != 0xFFFF &&
-			_balloonArchive.size() > balloonId &&
-			_balloonArchive.loadEntry(balloonId, balloon);
-
-		if (_font.isLoaded() && !text.empty()) {
-			// Snapshot the current screen, overlay balloon + text, then
-			// copy the changed band back. This preserves the site BG
-			// underneath unchanged regions.
-			Graphics::Surface *screen = g_system->lockScreen();
-			if (!screen) break;
-			Graphics::ManagedSurface scratch(320, 200,
-				Graphics::PixelFormat::createFormatCLUT8());
-			for (int row = 0; row < 200; row++) {
-				memcpy((byte *)scratch.getBasePtr(0, row),
-					   (const byte *)screen->getBasePtr(0, row), 320);
-			}
-			g_system->unlockScreen();
-
-			int textX = bubX;
-			int textY = bubY;
-			int textW = MIN<int>(320 - bubX, 200);
-			int copyY = bubY;
-			int copyH = _font.getFontHeight() * 4 + 8;
-
-			if (haveBalloon) {
-				const int bw = MIN<int>(balloon.surface.w, 320 - bubX);
-				const int bh = MIN<int>(balloon.surface.h, 200 - bubY);
-				// `_AddPicBackground` passes `pic->miscflags >> 8` as
-				// the transparent colour to `_Rect_Move_Mask`. The
-				// on-disk u16 at file offset 0 maps to `Picture::flags`.
-				const byte transp = (byte)(balloon.flags >> 8);
-				// `_GetBalloon @ 172b:1d7d` mirrors the picture horizontally
-				// when `(bubNum & 0x80)` is set ??? used for right-side
-				// speakers so the tail points the other way.
-				const bool flipBalloon = (bubNum & 0x80) != 0;
-				if (bw > 0 && bh > 0) {
-					for (int row = 0; row < bh; row++) {
-						const byte *src =
-							(const byte *)balloon.surface.getBasePtr(0, row);
-						byte *dst = (byte *)scratch.getBasePtr(bubX, bubY + row);
-						for (int col = 0; col < bw; col++) {
-							const int srcCol = flipBalloon
-								? (balloon.surface.w - 1 - col)
-								: col;
-							const byte px = src[srcCol];
-							if (px != transp)
-								dst[col] = px;
-						}
-					}
-				}
-				// Per-balloon metadata table verified from 29be:0875 ???
-				// 10-byte entries indexed by `(bubNum & 0x7f)`. Layout:
-				//   +0..1 textX inset, +2..3 textY inset, +4..5 width,
-				//   +6..7 height, +8..9 tail offset.
-				// 52 entries total; insets vary (3, 5, 6, or 8 px).
-				// The original `_DisplayClue` does:
-				//   _WordWrap(bubX + table[bubNum].x, bubY + table[bubNum].y,
-				//             table[bubNum].w, ...);
-				static const struct { uint16 x, y, w; } kBalloonTable[] = {
-					{ 6, 4, 142 }, { 6, 4, 142 }, { 6, 4, 142 }, { 6, 4, 142 },
-					{ 6, 4, 142 }, { 6, 4, 142 }, { 6, 4, 142 },
-					{ 6, 4, 224 }, { 6, 4, 224 }, { 6, 4, 224 }, { 6, 4, 224 },
-					{ 6, 4, 224 }, { 6, 4, 224 }, { 6, 4, 224 },
-					{ 6, 4, 291 }, { 6, 4, 291 }, { 6, 4, 291 }, { 6, 4, 291 },
-					{ 6, 4, 291 }, { 6, 4, 291 }, { 6, 4, 291 },
-					{ 5, 4, 155 }, { 5, 4, 155 }, { 5, 4, 155 }, { 5, 4, 155 },
-					{ 5, 4, 155 }, { 5, 4, 155 }, { 5, 4, 155 },
-					{ 5, 4, 237 }, { 5, 4, 237 }, { 5, 4, 237 }, { 5, 4, 237 },
-					{ 5, 4, 237 }, { 5, 4, 237 }, { 5, 4, 237 },
-					{ 3, 4, 155 }, { 3, 4, 155 }, { 3, 4, 155 }, { 3, 4, 155 },
-					{ 3, 4, 155 }, { 3, 4, 155 }, { 3, 4, 155 },
-					{ 5, 4, 238 }, { 5, 4, 238 }, { 5, 4, 238 }, { 5, 4, 238 },
-					{ 5, 4, 238 }, { 5, 4, 238 }, { 5, 4, 238 },
-					{ 5, 8, 158 }, { 5, 8, 176 }, { 8, 7, 142 }
-				};
-				const uint kBalloonTableSize = sizeof(kBalloonTable) /
-											   sizeof(kBalloonTable[0]);
-				const uint balloonIdx = balloonId < kBalloonTableSize
-										? balloonId : 0;
-				const auto &bm = kBalloonTable[balloonIdx];
-				textX = bubX + bm.x;
-				textY = bubY + bm.y;
-				textW = bm.w;
-				copyH = bh;
-			} else {
-				// No balloon ??? clear a band so old pixels don't bleed.
-				const Common::Rect band(0, bubY, 320,
-					MIN<int>(bubY + copyH, 200));
-				scratch.fillRect(band, 0);
-				copyY = bubY;
-			}
-
-			// `_DisplayClue` @ 2404:07fe passes fontColor=0 (palette
-			// index 0 of the case-briefing palette 0x22) to `_WordWrap`.
-			// Hard-coding 0xF here gave the wrong colour.
-			_font.drawWordWrapped(&scratch, textX, textY,
-				MAX<int>(8, textW), text, 0);
-
-			g_system->copyRectToScreen(scratch.getBasePtr(0, copyY),
-				scratch.pitch, 0, copyY, 320,
-				MIN<int>(copyH, 200 - copyY));
-			g_system->updateScreen();
-		}
-
-		// Wait for click/key to advance ??? only if we drew something.
-		// ESC skips the entire dialogue rather than just one entry.
-		if (hasText || (charPicId != 0 && charPicId != 0xFFFF)) {
-			bool advance = false;
-			bool skipAll = false;
-			while (!advance && !shouldQuit()) {
-				Common::Event ev;
-				while (g_system->getEventManager()->pollEvent(ev)) {
-					if (ev.type == Common::EVENT_QUIT ||
-						ev.type == Common::EVENT_RETURN_TO_LAUNCHER) {
-						advance = true;
-						break;
-					}
-					if (ev.type == Common::EVENT_KEYDOWN &&
-						ev.kbd.keycode == Common::KEYCODE_ESCAPE) {
-						advance = true;
-						skipAll = true;
-						break;
-					}
-					if (ev.type == Common::EVENT_LBUTTONDOWN ||
-						ev.type == Common::EVENT_KEYDOWN) {
-						advance = true;
-						break;
-					}
-				}
-				// Tick the screen so the OSystem cursor follows the
-				// mouse ??? ScummVM redraws the cursor overlay only on
-				// updateScreen.
-				g_system->updateScreen();
-				g_system->delayMillis(10);
-			}
-			if (skipAll) {
-				// Apply remaining side-effects without rendering. The
-				// original silently runs the state updates even when the
-				// player skips ahead.
-				for (uint k = i; k < number; k++)
-					applyClueSideEffects(clueBlock + 4 + k * 62);
-				return;
-			}
-		}
-
-		applyClueSideEffects(c);
-	}
-}
-
-void EEMEngine::doNotebook() {
-	// Mirrors `_DoNotebook @ 161e:0500` + `_DrawNotes @ 161e:01d0` +
-	// `_HandleNoteButton @ 161e:03cb`.
-	//
-	// Layout (verified from Ghidra labels in 29be:013f / 29be:0147):
-	//   _NotebookRect = (78, 12, 288, 152)   ??? note display rectangle.
-	//   _NoteButtons (11 entries, 8 bytes each, at 29be:0147):
-	//     [0]  (134, 174, 155, 190)  decorative ??? `_HandleNoteButton(0)`
-	//                                returns immediately (i-1 unsigned > 9).
-	//     [1]  (93,  174, 115, 190)  ??? `_InterfaceHelp(0)` (handler 0x3f9)
-	//     [2]  (157, 174, 178, 190)  ??? handler 0x477   (page nav)
-	//     [3]  (5,   80,  44, 110)   ??? `_KDHelp` (host hint, 0x403)
-	//     [4]  (180, 174, 201, 190)  ??? solve / accuse  (0x436)
-	//     [5]  (204, 174, 224, 190)  ??? `_NextScreen = 5` (gallery, 0x489)
-	//     [6]  (226, 174, 247, 190)  ??? handler 0x4ab
-	//     [7]  (7,   177,  57, 200)  ??? handler 0x480   (back to map)
-	//     [8]  (35,  111,  56, 136)  ??? `_NextScreen = 3` (site)
-	//     [9]  (0, 0, 0, 0)          ??? same exit as [8]
-	//     [10] (66,  79, 267, 174)   ??? `_InterfaceHelp(0)` (note area)
-	//   Background: PIC 0x3f.
-	//   Partner anim: anim 1 (Jake) / 0xb (Jenny) at (5, 80).
-	if (!_mystery.isLoaded() || !_font.isLoaded())
-		return;
-
-	// Button rects from `_NoteButtons @ 29be:0147` matched to handler
-	// addresses via the jump table at `_HandleNoteButton + 0xec` (i.e.
-	// 161e:04ec). Decoded handlers (i = rect_index, dispatch = handler[i-1]):
-	//   rect 0 (134,155) ??? no handler (i-1 underflows; original treats
-	//                      this as a decorative/no-op slot)
-	//   rect 1 (93,115)  ??? 0x03f9 = `_InterfaceHelp(0)`           (HELP)
-	//   rect 2 (157,178) ??? 0x0477 = `_NextScreen = 5`             (GALLERY)
-	//   rect 3 (5,80)    ??? 0x0403 = `_KDHelp`                     (host hint)
-	//   rect 4 (180,201) ??? 0x0436 = `_SolvedCheck` -> NextScreen=7 (SOLVE)
-	//   rect 5 (204,224) ??? 0x0489 = `_EraseNotes` + `_DrawNotes`  (PAGE NEXT)
-	//   rect 6 (226,247) ??? 0x04ab = decrement CurrentPage + redraw (PAGE PREV)
-	//   rect 7 (7,177)   ??? 0x0480 = `_NextScreen = 2`             (MAP)
-	//   rect 8 (35,111)  ??? 0x03ed = `_NextScreen = 3`             (SITE)
-	//   rect 9 (0,0)     ??? 0x03ed = same as rect 8
-	//   rect 10 (66,79)  ??? 0x03f9 = `_InterfaceHelp(0)`           (note-area help)
-	const Common::Rect kNotebookRect(78, 12, 288, 152);
-	const Common::Rect kBtnHelp1   ( 93, 174, 115, 190);  // [1] HELP
-	const Common::Rect kBtnGallery (157, 174, 178, 190);  // [2] GALLERY
-	const Common::Rect kBtnPartner (  5,  80,  44, 110);  // [3] KD HELP
-	const Common::Rect kBtnAccuse  (180, 174, 201, 190);  // [4] SOLVE
-	const Common::Rect kBtnPageNext(204, 174, 224, 190);  // [5] PAGE NEXT
-	const Common::Rect kBtnPagePrev(226, 174, 247, 190);  // [6] PAGE PREV
-	const Common::Rect kBtnMap     (  7, 177,  57, 200);  // [7] MAP
-	const Common::Rect kBtnSite    ( 35, 111,  56, 136);  // [8] SITE
-	const Common::Rect kNoteArea   ( 66,  79, 267, 174);  // [10] note area
-
-	CursorMan.showMouse(true);
-
-	int page = 0;
-	int hoveredNoteSlot = -1;
-
-	// Build a list of found-clue indices, identical ordering to the
-	// original's iteration through `_CluesFound[]`.
-	auto buildFound = [&]() {
-		Common::Array<uint> found;
-		for (uint i = 0; i < Mystery::kCluesFoundCap; i++)
-			if (_mystery._cluesFound[i])
-				found.push_back(i);
-		return found;
-	};
-
-	auto draw = [&]() {
-		Graphics::ManagedSurface scratch(320, 200,
-			Graphics::PixelFormat::createFormatCLUT8());
-		scratch.clear();
-
-		// PIC 0x3f frame.
-		Picture frame;
-		if (_picsArchive.getPicture(0x3f, frame)) {
-			const int w = MIN<int>(frame.surface.w, 320);
-			const int h = MIN<int>(frame.surface.h, 200);
-			for (int row = 0; row < h; row++)
-				memcpy((byte *)scratch.getBasePtr(0, row),
-					   (const byte *)frame.surface.getBasePtr(0, row), w);
-		}
-
-		// Partner sprite at (5, 80). Anim 1 for Jake, 0xb (11) for Jenny.
-		const uint partnerAnim = (_partner == 0) ? 1 : 0xb;
-		Animation partnerAni;
-		if (_aniArchive.loadAnimation(partnerAnim, partnerAni) && !partnerAni.empty()) {
-			const uint32 now = g_system->getMillis();
-			const uint frameIdx = (uint)((now / 100) % partnerAni.size());
-			const Picture &fr = partnerAni[frameIdx];
-			const byte transp = (byte)(fr.flags >> 8);
-			for (int row = 0; row < fr.surface.h; row++) {
-				const int dstY = 80 + row;
-				if (dstY < 0 || dstY >= 200) continue;
-				const byte *src = (const byte *)fr.surface.getBasePtr(0, row);
-				byte *dst = (byte *)scratch.getBasePtr(0, dstY);
-				for (int col = 0; col < fr.surface.w; col++) {
-					const int dstX = 5 + col;
-					if (dstX < 0 || dstX >= 320) continue;
-					if (src[col] != transp)
-						dst[dstX] = src[col];
-				}
-			}
-		}
-
-		// Notes ??? `_DrawNotes` walks `_NoteIndex` for the current page,
-		// rendering each found clue's text inside `_NotebookRect` with
-		// word-wrap. Selected clues are highlighted (color 0x3c in the
-		// original's case-briefing palette).
-		const Common::Array<uint> found = buildFound();
-		const byte *ni = _mystery.noteIndex();
-		const uint16 niCount = _mystery.noteIndexCount();
-
-		const int kRectX = kNotebookRect.left;
-		const int kRectY = kNotebookRect.top;
-		const int kRectW = kNotebookRect.width();
-		const int kRectH = kNotebookRect.height();
-
-		// Walk forward to the start clue of the current page.
-		// Each page renders as many clues as fit in `kRectH`.
-		int clueCursor = 0;
-		Common::Array<int> pageStarts;
-		pageStarts.push_back(0);
-		{
-			const int lineH = _font.getFontHeight() + 1;
-			int y = kRectY;
-			while (clueCursor < (int)found.size()) {
-				const uint clueId = found[clueCursor];
-				Common::String txt;
-				if (ni && clueId < niCount) {
-					const uint16 textOff = READ_LE_UINT16(ni + clueId * 4);
-					txt = parseString(_mystery.textAt(textOff),
-									  _playerName, _partner);
-				}
-				// Measure height by wrapping the text without drawing.
-				Common::Array<Common::String> wrapped;
-				_font.wordWrapText(txt, kRectW, wrapped);
-				const int h = (int)wrapped.size() * lineH;
-				if (y + h + 7 > kRectY + kRectH) {
-					// Page break before this clue.
-					y = kRectY;
-					pageStarts.push_back(clueCursor);
-				}
-				y += h + 7;
-				clueCursor++;
-			}
-			if (page >= (int)pageStarts.size())
-				page = (int)pageStarts.size() - 1;
-			if (page < 0)
-				page = 0;
-		}
-
-		// Track per-slot rectangles so the click handler can map a
-		// click in `kNoteArea` back to a clue index.
-		Common::Array<Common::Rect> slotRects;
-		Common::Array<uint> slotClues;
-
-		const int startClue = (page < (int)pageStarts.size())
-								? pageStarts[page] : 0;
-		const int endClue   = (page + 1 < (int)pageStarts.size())
-								? pageStarts[page + 1] : (int)found.size();
-
-		int y = kRectY;
-		for (int i = startClue; i < endClue; i++) {
-			const uint clueId = found[i];
-			Common::String txt;
-			if (ni && clueId < niCount) {
-				const uint16 textOff = READ_LE_UINT16(ni + clueId * 4);
-				txt = parseString(_mystery.textAt(textOff),
-								  _playerName, _partner);
-			}
-			if (txt.empty())
-				txt = Common::String::format("clue %u", clueId);
-			// Per `_DrawNotes @ 161e:01d0`: text uses
-			// `_NoteUnselectedColor` (0x5c=cyan) for unselected and 0x3c
-			// (light yellow-white) for selected. Both contrast cleanly
-			// with the PDA screen's natural blue, so we draw text
-			// directly on PIC 0x3f without an extra fill rectangle ???
-			// matches the original design.
-			Common::Array<Common::String> wrapped;
-			_font.wordWrapText(txt, kRectW, wrapped);
-			const int lineH = _font.getFontHeight() + 1;
-			const int h = (int)wrapped.size() * lineH;
-			const byte color = _mystery._noteSelected[clueId] ? 0x3C : 0x5C;
-			for (uint li = 0; li < wrapped.size(); li++) {
-				_font.drawString(&scratch, wrapped[li], kRectX,
-								 y + (int)li * lineH, kRectW, color);
-			}
-			slotRects.push_back(Common::Rect(kRectX, y,
-											  kRectX + kRectW, y + h));
-			slotClues.push_back(clueId);
-			y += h + 7;
-		}
-
-		// Page indicator + selected-points counter directly on PIC.
-		_font.drawString(&scratch, Common::String::format("p%d/%d",
-								   page + 1, (int)pageStarts.size()),
-						 270, 4, 320, 0x5C);
-		_font.drawString(&scratch, Common::String::format("%d pts",
-								   _mystery.selectedPoints()),
-						 270, 14, 320, 0x5C);
-		(void)hoveredNoteSlot;
-
-		g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
-								   0, 0, 320, 200);
-		g_system->updateScreen();
-
-		// Stash slot info on the captures so the click handler below
-		// can use it via the closure.
-		_notebookSlotRects = slotRects;
-		_notebookSlotClues = slotClues;
-	};
-
-	draw();
-
-	uint32 lastDraw = g_system->getMillis();
-
-	while (!shouldQuit()) {
-		Common::Event ev;
-		bool dirty = false;
-		bool exitFlag = false;
-		while (g_system->getEventManager()->pollEvent(ev)) {
-			if (ev.type == Common::EVENT_QUIT ||
-				ev.type == Common::EVENT_RETURN_TO_LAUNCHER) {
-				exitFlag = true;
-				break;
-			}
-			if (ev.type == Common::EVENT_KEYDOWN) {
-				if (ev.kbd.keycode == Common::KEYCODE_ESCAPE) {
-					exitFlag = true;
-					break;
-				}
-				if (ev.kbd.keycode == Common::KEYCODE_LEFT ||
-					ev.kbd.keycode == Common::KEYCODE_PAGEUP) {
-					if (page > 0) page--;
-					dirty = true;
-				} else if (ev.kbd.keycode == Common::KEYCODE_RIGHT ||
-						   ev.kbd.keycode == Common::KEYCODE_PAGEDOWN ||
-						   ev.kbd.keycode == Common::KEYCODE_TAB) {
-					page++;
-					dirty = true;
-				} else if (ev.kbd.keycode == Common::KEYCODE_h) {
-					doHelp();
-					dirty = true;
-				}
-			}
-			if (ev.type == Common::EVENT_LBUTTONDOWN) {
-				// Test buttons in the order the original would ???
-				// button 0 / 9 are dead zones, so check the actionable
-				// rects directly. Earlier rects "win" when overlapping
-				// (matches `_FindButton`).
-				if (kBtnSite.contains(ev.mouse.x, ev.mouse.y)) {
-					exitFlag = true;
-					break;  // back to site
-				}
-				if (kBtnMap.contains(ev.mouse.x, ev.mouse.y)) {
-					doBigMap();
-					exitFlag = true;
-					break;
-				}
-				if (kBtnPartner.contains(ev.mouse.x, ev.mouse.y)) {
-					doHelp();              // _KDHelp = host hint
-					dirty = true;
-					continue;
-				}
-				if (kBtnAccuse.contains(ev.mouse.x, ev.mouse.y)) {
-					doAccuse();
-					exitFlag = true;
-					break;
-				}
-				if (kBtnGallery.contains(ev.mouse.x, ev.mouse.y)) {
-					doGallery();
-					dirty = true;
-					continue;
-				}
-				if (kBtnHelp1.contains(ev.mouse.x, ev.mouse.y)) {
-					// rect 1 ??? `_InterfaceHelp(0)`: walks `HelpData[0]` and
-					// blits PICs 0x63 / 0x1ae fullscreen for click-through.
-					doInterfaceHelp(0);
-					dirty = true;
-					continue;
-				}
-				if (kBtnPagePrev.contains(ev.mouse.x, ev.mouse.y)) {
-					if (page > 0) page--;
-					dirty = true;
-					continue;
-				}
-				if (kBtnPageNext.contains(ev.mouse.x, ev.mouse.y)) {
-					page++;
-					dirty = true;
-					continue;
-				}
-				if (kNoteArea.contains(ev.mouse.x, ev.mouse.y)) {
-					// Toggle the selection on whichever clue's text
-					// the click landed in. The original calls
-					// `_InterfaceHelp` here; that's the help screen,
-					// not selection ??? selection is in the Accuse
-					// screen. We use the area for selection because
-					// keyboard 1..9 toggling is awkward, and the
-					// resulting `_NoteSelected` state is what
-					// `_SolvedCheck` reads.
-					for (uint i = 0; i < _notebookSlotRects.size(); i++) {
-						if (_notebookSlotRects[i].contains(ev.mouse.x,
-														   ev.mouse.y)) {
-							const uint clueId = _notebookSlotClues[i];
-							_mystery._noteSelected[clueId] ^= 1;
-							dirty = true;
-							break;
-						}
-					}
-					continue;
-				}
-			}
-		}
-		if (exitFlag)
-			break;
-
-		const uint32 now = g_system->getMillis();
-		// Re-render every 100 ms so the partner sprite cycles frames.
-		if (dirty || now - lastDraw >= 100) {
-			draw();
-			lastDraw = now;
-		}
-		g_system->updateScreen();
-		g_system->delayMillis(15);
-	}
-}
-
-void EEMEngine::doGallery() {
-	// Mirrors `_DoGallery @ 158f:065b` and `_DrawGallery @ 158f:0046`.
-	// Verified directly from the disassembly:
-	//   * Background: PIC 0x3f (same as PDA).
-	//   * Partner sprite at (5, 0x50): anim 2 (Jake) / 0x10 (Jenny).
-	//     `_NewAnimation(5, 0x50, ...)`. NOTE: gallery uses anim 2/0x10,
-	//     PDA uses 1/0xb ??? different sprites.
-	//   * Five fixed slot positions at `29be:0x116` (4 bytes per slot,
-	//     `{u16 x, u16 y}`):
-	//         slot 0 = ( 83,  14)   slot 3 = (119,  90)
-	//         slot 1 = (155,  14)   slot 4 = (191,  90)
-	//         slot 2 = (227,  14)
-	//   * For each logical suspect i in 0..NumSuspects-1:
-	//         picId   = `*(u16 *)(_GalleryData + i * 0x46)` (entry +0).
-	//         visible = `_InGallery[_NewOrder[i]] != 0`.
-	//         drawX   = positions[_NewOrder[i]].x
-	//         drawY   = positions[_NewOrder[i]].y + (0x48 - pic.height)
-	//     So portraits are BOTTOM-aligned to baselines 0x48 + pos.y.
-	//   * Click on portrait via `_SearchSuspects` ??? `MoreInfo(i)` shows
-	//     the suspect detail page. ESC returns to PDA.
-	//   * Frame-cycled @ 100ms via `_CheckFrameRate` + `_UpdateAnimations`
-	//     + `_GizmoColorCycle`.
-	if (!_mystery.isLoaded())
-		return;
-
-	const byte *gd = _mystery.galleryData();
-	if (!gd) {
-		warning("doGallery: no GalleryData in mystery %u", _mystery.number());
-		return;
-	}
-
-	CursorMan.showMouse(true);
-
-	struct Slot { int x; int y; };
-	static const Slot kGallerySlots[5] = {
-		{  83,  14 }, // 0
-		{ 155,  14 }, // 1
-		{ 227,  14 }, // 2
-		{ 119,  90 }, // 3
-		{ 191,  90 }  // 4
-	};
-
-	// Pre-load static elements once.
-	Picture galBg;
-	const bool haveBg = _picsArchive.getPicture(0x3f, galBg);
-
-	// Gallery partner anim ??? `_DoGallery` calls `_GetAnimation(uVar6)` with
-	// uVar6 = 2 (Jake) / 0x10 (Jenny). Different from PDA (1 / 0xb).
-	const uint partnerAnim = (_partner == 0) ? 2 : 0x10;
-	Animation partnerAni;
-	const bool havePartner = _aniArchive.loadAnimation(partnerAnim, partnerAni)
-							  && !partnerAni.empty();
-
-	const uint8 num = _mystery.numSuspects();
-
-	// Cache slot rects for click hit-testing.
-	Common::Array<Common::Rect> slotRects;
-	Common::Array<int> slotSuspect; // logical suspect index in [0, num)
-	slotRects.resize(num);
-	slotSuspect.resize(num);
-	for (uint i = 0; i < num; i++) {
-		slotSuspect[i] = -1;
-	}
-
-	auto drawFrame = [&]() {
-		Graphics::ManagedSurface scratch(320, 200,
-			Graphics::PixelFormat::createFormatCLUT8());
-		scratch.clear();
-
-		if (haveBg) {
-			const int bw = MIN<int>(galBg.surface.w, 320);
-			const int bh = MIN<int>(galBg.surface.h, 200);
-			for (int row = 0; row < bh; row++) {
-				memcpy((byte *)scratch.getBasePtr(0, row),
-					   (const byte *)galBg.surface.getBasePtr(0, row), bw);
-			}
-		}
-
-		// Partner sprite frame @ (5, 0x50).
-		if (havePartner) {
-			const uint32 now = g_system->getMillis();
-			const uint frameIdx = (uint)((now / 100) % partnerAni.size());
-			const Picture &fr = partnerAni[frameIdx];
-			const byte transp = (byte)(fr.flags >> 8);
-			const int px = 5, py = 0x50;
-			for (int row = 0; row < fr.surface.h; row++) {
-				const int dstY = py + row;
-				if (dstY < 0 || dstY >= 200) continue;
-				const byte *src = (const byte *)fr.surface.getBasePtr(0, row);
-				byte *dst = (byte *)scratch.getBasePtr(0, dstY);
-				for (int col = 0; col < fr.surface.w; col++) {
-					const int dstX = px + col;
-					if (dstX < 0 || dstX >= 320) continue;
-					if (src[col] != transp)
-						dst[dstX] = src[col];
-				}
-			}
-		}
-
-		// Portraits ??? `_DrawGallery @ 158f:0046` walks suspects 0..N-1
-		// and only renders those flagged in `_InGallery[NewOrder[i]]`.
-		// Undiscovered slots are left empty in the original. We render
-		// a darkened placeholder + "?" so the player has visual feedback
-		// that suspects exist but are still unknown.
-		uint discoveredCount = 0;
-		for (uint i = 0; i < num && i < Mystery::kGalleryCap; i++) {
-			slotRects[i] = Common::Rect();
-			slotSuspect[i] = -1;
-
-			const uint8 phys = _mystery._newOrder[i];
-			if (phys >= 5)
-				continue;
-			const Slot &s = kGallerySlots[phys];
-
-			const bool discovered = _mystery._inGallery[phys] != 0;
-			if (discovered) {
-				const uint16 picId = READ_LE_UINT16(gd + i * 0x46);
-				Picture portrait;
-				if (picId == 0 ||
-					!_picsArchive.getPicture(picId, portrait))
-					continue;
-
-				const int placeX = s.x;
-				const int placeY = s.y + (0x48 - portrait.surface.h);
-				const byte transp = (byte)(portrait.flags >> 8);
-				const int w = MIN<int>(portrait.surface.w, 320 - placeX);
-				const int h = MIN<int>(portrait.surface.h, 200 - placeY);
-				if (w <= 0 || h <= 0)
-					continue;
-				for (int row = 0; row < h; row++) {
-					const int dstY = placeY + row;
-					if (dstY < 0) continue;
-					const byte *src =
-						(const byte *)portrait.surface.getBasePtr(0, row);
-					byte *dst = (byte *)scratch.getBasePtr(0, dstY);
-					for (int col = 0; col < w; col++) {
-						const int dstX = placeX + col;
-						if (src[col] != transp)
-							dst[dstX] = src[col];
-					}
-				}
-				slotRects[i] = Common::Rect(placeX, placeY,
-											 placeX + w, placeY + h);
-				slotSuspect[i] = (int)i;
-				discoveredCount++;
-			} else {
-				// Undiscovered placeholder ??? small framed "?" box at
-				// (s.x, s.y) sized 0x40 ?? 0x48 (typical portrait size).
-				const int phW = 0x40, phH = 0x48;
-				const int phX = s.x, phY = s.y;
-				if (phX + phW <= 320 && phY + phH <= 200) {
-					scratch.fillRect(Common::Rect(phX, phY,
-						phX + phW, phY + phH), 0x20);
-					scratch.frameRect(Common::Rect(phX, phY,
-						phX + phW, phY + phH), 0x5C);
-					if (_font.isLoaded()) {
-						_font.drawString(&scratch, "?",
-							phX + phW / 2 - 3,
-							phY + phH / 2 - 4, phW, 0x5C);
-					}
-				}
-			}
-		}
-		(void)discoveredCount;
-
-		g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
-								   0, 0, 320, 200);
-		g_system->updateScreen();
-	};
-
-	drawFrame();
-	uint32 lastDraw = g_system->getMillis();
-
-	while (!shouldQuit()) {
-		Common::Event ev;
-		bool exitFlag = false;
-		while (g_system->getEventManager()->pollEvent(ev)) {
-			if (ev.type == Common::EVENT_QUIT ||
-				ev.type == Common::EVENT_RETURN_TO_LAUNCHER) {
-				return;
-			}
-			if (ev.type == Common::EVENT_KEYDOWN) {
-				if (ev.kbd.keycode == Common::KEYCODE_ESCAPE) {
-					exitFlag = true;
-					break;
-				}
-			}
-			if (ev.type == Common::EVENT_LBUTTONDOWN) {
-				// PDA bottom-bar buttons mirror `_NoteButtons @ 29be:0147`.
-				// `_DoGallery @ 158f:065b` shares the SAME button table
-				// with `_DoNotebook` (both call `_FindButton` against the
-				// 11-entry table at 0x147). `_HandleGalleryButton @
-				// 158f:05c0` dispatches via a different jump table
-				// (158f:0645). Verified gallery button mapping:
-				//   rect 0 (134,155) ??? 0x05ef = `_NextScreen = 4` (NOTEBOOK)
-				//   rect 1 (93,115)  ??? 0x0625 = `_InterfaceHelp` (HELP)
-				//   rect 2 (157,178) ??? 0x0638 = generic exit (no-op)
-				//   rect 3 (5,80)    ??? 0x061e = `_KDHelp` (host hint)
-				//   rect 4 (180,201) ??? 0x05ff = `_SolvedCheck` -> SOLVE
-				//   rect 5 (204,224) ??? 0x0638 = generic exit
-				//   rect 6 (226,247) ??? 0x0638 = generic exit
-				//   rect 7 (7,177)   ??? 0x05f7 = `_NextScreen = 2` (MAP)
-				//   rect 8 (35,111)  ??? 0x05e4 = `_NextScreen = 3` (SITE)
-				const Common::Rect kBtnSite    ( 35, 111,  56, 136); // [8] SITE
-				const Common::Rect kBtnMap     (  7, 177,  57, 200); // [7] MAP
-				const Common::Rect kBtnAccuse  (180, 174, 201, 190); // [4] SOLVE
-				const Common::Rect kBtnNotebook(134, 174, 155, 190); // [0] NOTEBOOK (back to PDA notes)
-				const Common::Rect kBtnHelp    ( 93, 174, 115, 190); // [1] HELP
-				const Common::Rect kBtnPartner (  5,  80,  44, 110); // [3] KD HELP
-				if (kBtnSite.contains(ev.mouse.x, ev.mouse.y)) {
-					exitFlag = true; break;
-				}
-				if (kBtnMap.contains(ev.mouse.x, ev.mouse.y)) {
-					doBigMap();
-					exitFlag = true; break;
-				}
-				if (kBtnAccuse.contains(ev.mouse.x, ev.mouse.y)) {
-					doAccuse();
-					exitFlag = true; break;
-				}
-				if (kBtnNotebook.contains(ev.mouse.x, ev.mouse.y)) {
-					// Already came from notebook; exiting returns to it.
-					exitFlag = true; break;
-				}
-				if (kBtnHelp.contains(ev.mouse.x, ev.mouse.y)) {
-					// Gallery rect 1 ??? `_InterfaceHelp(0)` per jmp table at
-					// 158f:0625 (HandleGalleryButton). Same picture sequence
-					// as the notebook HELP button.
-					doInterfaceHelp(0);
-					lastDraw = 0;
-					continue;
-				}
-				if (kBtnPartner.contains(ev.mouse.x, ev.mouse.y)) {
-					doHelp();
-					lastDraw = 0;
-					continue;
-				}
-				// `_SearchSuspects` walks the per-slot rects and returns
-				// the suspect index. We mirror that with cached rects.
-				bool clicked = false;
-				for (uint i = 0; i < slotRects.size(); i++) {
-					if (slotSuspect[i] < 0) continue;
-					if (slotRects[i].contains(ev.mouse.x, ev.mouse.y)) {
-						// `MoreInfo(i)` ??? show the suspect detail page.
-						// Mirrors `MoreInfo @ 158f:0419`:
-						//   _RefreshGalleryBackground();
-						//   _GetPicture(*(u16*)(gd + i*0x46));
-						//   _AddPicBackground(pic, 0x94, 0xf);
-						//   _DrawGalleryNotes(gd + i*0x46);
-						//   loop until ESC or button click.
-						// Suspect data layout (verified against M1):
-						//   +0..1: picId (used here AND for gallery slot)
-						//   +8..9: number of clues for this suspect
-						//   +0xa..??: array of u16 clue IDs (terminated
-						//             by 0xFFFF if shorter than count).
-						const uint suspectIdx = (uint)slotSuspect[i];
-						const byte *suspect = gd + suspectIdx * 0x46;
-						const uint16 detailPic =
-							READ_LE_UINT16(suspect + 0);
-						const uint16 clueCount =
-							READ_LE_UINT16(suspect + 8);
-
-						Graphics::ManagedSurface ms(320, 200,
-							Graphics::PixelFormat::createFormatCLUT8());
-						ms.clear();
-						if (haveBg) {
-							const int bw = MIN<int>(galBg.surface.w, 320);
-							const int bh = MIN<int>(galBg.surface.h, 200);
-							for (int row = 0; row < bh; row++) {
-								memcpy((byte *)ms.getBasePtr(0, row),
-									   (const byte *)galBg.surface.getBasePtr(0, row), bw);
-							}
-						}
-						// Full suspect picture at (0x94, 0xf).
-						Picture detail;
-						if (_picsArchive.getPicture(detailPic, detail)) {
-							const byte transp =
-								(byte)(detail.flags >> 8);
-							const int dx = 0x94, dy = 0x0f;
-							const int dw = MIN<int>(detail.surface.w, 320 - dx);
-							const int dh = MIN<int>(detail.surface.h, 200 - dy);
-							for (int row = 0; row < dh; row++) {
-								const byte *src =
-									(const byte *)detail.surface.getBasePtr(0, row);
-								byte *dst =
-									(byte *)ms.getBasePtr(0, dy + row);
-								for (int col = 0; col < dw; col++) {
-									if (src[col] != transp)
-										dst[dx + col] = src[col];
-								}
-							}
-						}
-						// Suspect's clue notes inside _GalleryNoteRect
-						// = (78, 93, 288, 152), per 29be:0100. Cyan text
-						// renders directly on the PDA's natural blue
-						// screen ??? matches `_DrawGalleryNotes @ 158f:01f4`.
-						const int rx = 78, ry = 93;
-						const int rw = 288 - 78, rh = 152 - 93;
-
-						const byte *ni = _mystery.noteIndex();
-						const uint16 niCount = _mystery.noteIndexCount();
-						int yPos = ry;
-						const int lineH = _font.getFontHeight() + 1;
-						bool drewAny = false;
-						for (uint k = 0; k < clueCount && k < 30; k++) {
-							const uint16 clueId =
-								READ_LE_UINT16(suspect + 0xa + k * 2);
-							if (clueId == 0xFFFF) break;
-							if (clueId >= Mystery::kCluesFoundCap ||
-								!_mystery._cluesFound[clueId])
-								continue;
-							if (!ni || clueId >= niCount) continue;
-							const uint16 textOff =
-								READ_LE_UINT16(ni + clueId * 4);
-							Common::String txt =
-								parseString(_mystery.textAt(textOff),
-											_playerName, _partner);
-							if (txt.empty()) continue;
-							const byte color =
-								_mystery._noteSelected[clueId] ? 0x3C : 0x5C;
-							const int hLine = _font.drawWordWrapped(
-								&ms, rx, yPos, rw, txt, color);
-							yPos += hLine + 7;
-							drewAny = true;
-							if (yPos + lineH > ry + rh) break;
-						}
-						if (!drewAny && _font.isLoaded()) {
-							_font.drawString(&ms,
-								"No clues yet for this suspect.",
-								rx, ry, rw, 0x5C);
-						}
-						// Header / footer text.
-						if (_font.isLoaded()) {
-							_font.drawString(&ms, "SUSPECT FILE",
-											  rx, ry - 11, rw, 0x3C);
-							_font.drawString(&ms, "(click / ESC: back)",
-											  rx, ry + rh + 2, rw, 0x3C);
-						}
-						g_system->copyRectToScreen(ms.getPixels(),
-							ms.pitch, 0, 0, 320, 200);
-						g_system->updateScreen();
-
-						// Wait for click or ESC. Drain the queued
-						// LBUTTONDOWN that triggered this MoreInfo first
-						// so we don't immediately accept it as the
-						// dismiss event.
-						g_system->delayMillis(150);
-						{
-							Common::Event drain;
-							while (g_system->getEventManager()->pollEvent(drain)) {
-								if (drain.type == Common::EVENT_QUIT ||
-									drain.type == Common::EVENT_RETURN_TO_LAUNCHER)
-									return;
-							}
-						}
-						bool back = false;
-						while (!back && !shouldQuit()) {
-							Common::Event e2;
-							while (g_system->getEventManager()->pollEvent(e2)) {
-								if (e2.type == Common::EVENT_LBUTTONDOWN ||
-									(e2.type == Common::EVENT_KEYDOWN &&
-									 (e2.kbd.keycode == Common::KEYCODE_ESCAPE ||
-									  e2.kbd.keycode == Common::KEYCODE_RETURN))) {
-									back = true;
-									break;
-								}
-								if (e2.type == Common::EVENT_QUIT ||
-									e2.type == Common::EVENT_RETURN_TO_LAUNCHER)
-									return;
-							}
-							g_system->delayMillis(20);
-						}
-						// Force gallery redraw immediately so the
-						// player isn't left looking at the dismissed
-						// MoreInfo screen until the next 100 ms tick.
-						drawFrame();
-						lastDraw = g_system->getMillis();
-						clicked = true;
-						break;
-					}
-				}
-				(void)clicked;
-			}
-		}
-		if (exitFlag) break;
-
-		const uint32 now = g_system->getMillis();
-		if (now - lastDraw >= 100) {
-			drawFrame();
-			lastDraw = now;
-		}
-		g_system->delayMillis(15);
-	}
-}
-
-void EEMEngine::doBigMap() {
-	// Two-stage flow that mirrors the original screen-1 wrapper at
-	// 20fe:120b and `_DoBigMap @ 20fe:09e7`:
-	//
-	//   STAGE 1 ??? Overview. PIC 0x42 + site icons drawn via the
-	//   `_DrawBigMapButtons` algorithm at BigMap coords MapData[+4/+6].
-	//   The original `_DoBigMap` returns sx/sy = (mouseX*2 - 0x74,
-	//   mouseY*2 - 0x55) when the player clicks inside `BigMapWindow`,
-	//   which is the scroll position into the SmallMap.
-	//
-	//   STAGE 2 ??? Detail zoom. PIC 0x43 frame + a 0xe9 ?? 0xab viewport
-	//   into BIGMAP.PIC at (2, 2), drawn by `DrawMap @ 20fe:1058` with
-	//   the (sx, sy) returned from stage 1. Site icons are stamped at
-	//   SmallMap coords MapData[+8/+0xa] via `_StampButtons`. Click on
-	//   a site icon ??? travel.
-	//
-	// MapData entry layout (14 bytes), verified directly from the
-	// disassembly of `_DrawBigMapButtons @ 20fe:0877` (`PUSH ES:[BX+4]`
-	// for X, `PUSH ES:[BX+6]` for Y, `CMP ES:[BX+0xc], 0` for crime)
-	// and `_StampButtons @ 20fe:0d2f` (`MOV AX, ES:[BX+8]`,
-	// `MOV AX, ES:[BX+0xa]`):
-	//   +0..3   ??? (not yet decoded)
-	//   +4..5   BigMap X
-	//   +6..7   BigMap Y
-	//   +8..9   SmallMap X
-	//   +0xa..b SmallMap Y
-	//   +0xc..d crime-flag
-
-	if (!_mystery.isLoaded())
-		return;
-
-	CursorMan.showMouse(true);
-
-	// `_GetPalette(0x24)` per `_DoBigMap @ 20fe:09e7`.
-	setSitePalette(0x24);
-
-	const Common::Rect kSetupRect(0xc7, 0x12, 0xc7 + 0x32, 0x12 + 0xa); // approx; original from globals
-	(void)kSetupRect; // not yet wired into our overlay
-
-	// ------------------------------------------------------------------
-	// STAGE 1 ??? Overview: PIC 0x42 + clickable site icons.
-	// ------------------------------------------------------------------
-
-	// `_DoBigMap @ 20fe:09e7` (20fe:0a44-0a99) registers a partner sprite
-	// on the overview frame. The animation depends on `_LastScreen`:
-	//   * When LastScreen == 2 (came from the site loop) the original
-	//     plays an entrance anim (`anum-1` for Jake / Jenny) at
-	//     (0x102, 0x50), then on END swaps to the idle anim at (0xfd,
-	//     0x50). We don't track LastScreen finely enough to distinguish,
-	//     so we render the IDLE pose at (0xfd, 0x50) which is what the
-	//     player sees the rest of the time anyway.
-	//   * Idle anim ID: Jake = 0x14 (20), Jenny = 0x12 (18).
-	const uint kMapAniId = (_partner == 0) ? 0x14 : 0x12;
-	Animation mapAnim;
-	const bool haveMapAnim = _aniArchive.loadAnimation(kMapAniId, mapAnim)
-							   && !mapAnim.empty();
-	const int kMapAnimX = 0xfd;
-	const int kMapAnimY = 0x50;
-
-	auto drawOverview = [&]() {
-		Graphics::ManagedSurface scratch(320, 200,
-			Graphics::PixelFormat::createFormatCLUT8());
-		scratch.clear();
-
-		Picture frame;
-		if (_picsArchive.getPicture(0x42, frame)) {
-			const int w = MIN<int>(frame.surface.w, 320);
-			const int h = MIN<int>(frame.surface.h, 200);
-			for (int row = 0; row < h; row++)
-				memcpy((byte *)scratch.getBasePtr(0, row),
-					   (const byte *)frame.surface.getBasePtr(0, row), w);
-		}
-
-		// Marker PICs from `_main @ 1a35:0f59`. Three globals are filled
-		// once at boot via `_GetPicture` (1-based IDs ??? entries N-1):
-		//   _DoneMarker  = PIC 0x20d  (already-searched site)
-		//   _SiteMarker  = PIC 0xc5   (default available site)
-		//   _CrimeMarker = PIC 0xc6   (crime-scene flag set)
-		// Picked per-site by `_DrawBigMapButtons @ 20fe:0877`:
-		//   1. SaveSiteComplete[i] ??? DoneMarker
-		//   2. else MapData[+0xc] != 0 ??? CrimeMarker
-		//   3. else SiteMarker
-		Picture done, normal, crimeM;
-		const bool haveDone   = _picsArchive.getPicture(0x20d, done);
-		const bool haveNormal = _picsArchive.getPicture(0xc5,  normal);
-		const bool haveCrime  = _picsArchive.getPicture(0xc6,  crimeM);
-
-		auto blitMarker = [&](const Picture &m, int x, int y) {
-			const byte transp = (byte)(m.flags >> 8);
-			for (int row = 0; row < m.surface.h; row++) {
-				const int dstY = y + row;
-				if (dstY < 0 || dstY >= 200) continue;
-				const byte *src = (const byte *)m.surface.getBasePtr(0, row);
-				byte *dst = (byte *)scratch.getBasePtr(0, dstY);
-				for (int col = 0; col < m.surface.w; col++) {
-					const int dstX = x + col;
-					if (dstX < 0 || dstX >= 320) continue;
-					if (src[col] != transp)
-						dst[dstX] = src[col];
-				}
-			}
-		};
-
-		for (uint i = 0; i < _mystery.numSites(); i++) {
-			if (!_mystery._onSites[i] && i != _mystery._siteNumber)
-				continue;
-			const byte *entry = _mystery.mapEntry(i);
-			if (!entry)
-				continue;
-			const uint16 mx    = READ_LE_UINT16(entry + 0x4);
-			const uint16 my    = READ_LE_UINT16(entry + 0x6);
-			const uint16 crime = READ_LE_UINT16(entry + 0xc);
-			const bool   done_ = (i < Mystery::kVisitedSiteCap)
-								  && _mystery._visitedSite[i];
-
-			const Picture *m = nullptr;
-			if (done_ && haveDone)            m = &done;
-			else if (crime != 0 && haveCrime) m = &crimeM;
-			else if (haveNormal)              m = &normal;
-
-			if (m)
-				blitMarker(*m, (int)mx, (int)my);
-			else {
-				// Fallback if the markers couldn't be loaded.
-				const Common::Rect mark(mx - 3, my - 3, mx + 4, my + 4);
-				scratch.fillRect(mark, 0x0F);
-			}
-		}
-
-		// Partner sprite ??? masked-blit at (0xfd, 0x50). Same per-tick
-		// idle the original would run via `_UpdateAnimations` once the
-		// entrance one-shot transitions out (see `_DoBigMap` 0xae3-0xae7
-		// where it swaps animId on the 0x80 marker).
-		if (haveMapAnim) {
-			const uint32 now = g_system->getMillis();
-			const uint frameIdx = (uint)((now / 100) % mapAnim.size());
-			const Picture &fr = mapAnim[frameIdx];
-			const byte transp = (byte)(fr.flags >> 8);
-			for (int row = 0; row < fr.surface.h; row++) {
-				const int dstY = kMapAnimY + row;
-				if (dstY < 0 || dstY >= 200) continue;
-				const byte *src = (const byte *)fr.surface.getBasePtr(0, row);
-				byte *dst = (byte *)scratch.getBasePtr(0, dstY);
-				for (int col = 0; col < fr.surface.w; col++) {
-					const int dstX = kMapAnimX + col;
-					if (dstX < 0 || dstX >= 320) continue;
-					if (src[col] != transp)
-						dst[dstX] = src[col];
-				}
-			}
-		}
-
-		g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
-								   0, 0, 320, 200);
-		g_system->updateScreen();
-	};
-
-	drawOverview();
-	uint32 mapLastTick = g_system->getMillis();
-
-	// Static rectangles read directly from the binary at the labelled
-	// addresses (29be:0x1596 onwards). Format is {x1, y1, x2, y2}.
-	const Common::Rect kBigMapWindow   (  0,   0, 247, 192); // 29be:1596
-	const Common::Rect kSetupBtnRect   (252,   4, 315,  42); // 29be:15ce
-
-	bool wantZoom = false;
-	int  zoomX = 0, zoomY = 0;
-	while (!shouldQuit()) {
-		Common::Event ev;
-		while (g_system->getEventManager()->pollEvent(ev)) {
-			if (ev.type == Common::EVENT_QUIT ||
-				ev.type == Common::EVENT_RETURN_TO_LAUNCHER)
-				return;
-			if (ev.type == Common::EVENT_KEYDOWN &&
-				ev.kbd.keycode == Common::KEYCODE_ESCAPE)
-				return;
-			if (ev.type == Common::EVENT_LBUTTONDOWN) {
-				// SetupButtonRect ??? `_NextScreen = 6` (the original's
-				// settings screen). We use it as "back to menu":
-				// abandon the current mystery and return to case
-				// selection.
-				if (kSetupBtnRect.contains(ev.mouse.x, ev.mouse.y)) {
-					_mystery.clear();
-					_nextScreen = kScreenInvalid;
-					return;
-				}
-				// Click in the BigMapWindow ??? zoom. Original formula:
-				//   sx = mouseX*2 - 0x74; sy = mouseY*2 - 0x55
-				if (kBigMapWindow.contains(ev.mouse.x, ev.mouse.y)) {
-					int sx = ev.mouse.x * 2;
-					int sy = ev.mouse.y * 2;
-					sx = (sx < 0x75) ? 0 : sx - 0x74;
-					sy = (sy < 0x56) ? 0 : sy - 0x55;
-					zoomX = sx;
-					zoomY = sy;
-					wantZoom = true;
-					break;
-				}
-			}
-		}
-		if (wantZoom)
-			break;
-		// Cycle the partner-sprite frame every 100 ms (matching the
-		// original's `_CheckFrameRate` cadence inside `_DoBigMap`).
-		const uint32 now = g_system->getMillis();
-		if (haveMapAnim && now - mapLastTick >= 100) {
-			mapLastTick = now;
-			drawOverview();
-		}
-		g_system->updateScreen();
-		g_system->delayMillis(10);
-	}
-
-	if (!wantZoom)
-		return;
-
-	// ------------------------------------------------------------------
-	// STAGE 2 ??? Detail zoom: PIC 0x43 frame + scrollable BIGMAP.PIC
-	// viewport at (2, 2), 0xe9 ?? 0xab. Click on a stamped icon ??? travel.
-	// ------------------------------------------------------------------
-
-	Common::File f;
-	if (!f.open(Common::Path("BIGMAP.PIC"))) {
-		warning("doBigMap: BIGMAP.PIC missing for detail view");
-		return;
-	}
-	const uint16 mapH = f.readUint16LE();
-	const uint16 mapW = f.readUint16LE();
-	if (mapW == 0 || mapH == 0)
-		return;
-	Common::Array<byte> mapPixels((uint32)mapW * mapH);
-	if (f.read(mapPixels.data(), mapPixels.size()) != mapPixels.size()) {
-		warning("doBigMap: short read on BIGMAP.PIC for detail view");
-		return;
-	}
-
-	const int kMapWinW = 0xe9; // 233
-	const int kMapWinH = 0xab; // 171
-	const int kMapWinX = 2;
-	const int kMapWinY = 2;
-
-	int scrollX = MAX<int>(0, MIN<int>(mapW - kMapWinW, zoomX));
-	int scrollY = MAX<int>(0, MIN<int>(mapH - kMapWinH, zoomY));
-
-	// `_DoMapScreen @ 20fe:120b` (20fe:12cd-12f0): partner sprite on
-	// the detail-zoom screen. Jake = anim 0x13 (19), Jenny = anim 0x11
-	// (17). Position (0x101, 0x50) = (257, 80), seqnum 0x13. The cells
-	// here have a "looking at the map" pose, distinct from the BigMap
-	// overview entrance/idle.
-	const uint kDetailAniId = (_partner == 0) ? 0x13 : 0x11;
-	Animation detailAnim;
-	const bool haveDetailAnim = _aniArchive.loadAnimation(kDetailAniId,
-														   detailAnim)
-								  && !detailAnim.empty();
-	const int kDetailAnimX = 0x101;
-	const int kDetailAnimY = 0x50;
-
-	auto drawDetail = [&]() {
-		Graphics::ManagedSurface scratch(320, 200,
-			Graphics::PixelFormat::createFormatCLUT8());
-		scratch.clear();
-
-		Picture frame;
-		if (_picsArchive.getPicture(0x43, frame)) {
-			const int w = MIN<int>(frame.surface.w, 320);
-			const int h = MIN<int>(frame.surface.h, 200);
-			for (int row = 0; row < h; row++)
-				memcpy((byte *)scratch.getBasePtr(0, row),
-					   (const byte *)frame.surface.getBasePtr(0, row), w);
-		}
-
-		const int copyW = MIN<int>(mapW - scrollX, kMapWinW);
-		const int copyH = MIN<int>(mapH - scrollY, kMapWinH);
-		for (int row = 0; row < copyH; row++) {
-			memcpy((byte *)scratch.getBasePtr(kMapWinX, kMapWinY + row),
-				   mapPixels.data() + (scrollY + row) * mapW + scrollX,
-				   copyW);
-		}
-
-		// Stamped site buttons. `_StampButtons @ 20fe:0d2f` does:
-		//   button = _GetButton(MapData[+0])      // BUTTON.DBD entry
-		//   destX  = MapData[+8],  destY = MapData[+0xa]
-		// then bakes the button PIC into the map bitmap. Each button
-		// sprite carries the site name baked in. We blit them on top
-		// of the BIGMAP.PIC viewport at the same SmallMap coords.
-		for (uint i = 0; i < _mystery.numSites(); i++) {
-			if (!_mystery._onSites[i] && i != _mystery._siteNumber)
-				continue;
-			const byte *entry = _mystery.mapEntry(i);
-			if (!entry) continue;
-			const uint16 buttonId = READ_LE_UINT16(entry + 0x0);
-			const uint16 mx       = READ_LE_UINT16(entry + 0x8);
-			const uint16 my       = READ_LE_UINT16(entry + 0xa);
-
-			Picture button;
-			if (!_buttonArchive.loadEntry(buttonId, button))
-				continue;
-			const int sx = (int)mx - scrollX + kMapWinX;
-			const int sy = (int)my - scrollY + kMapWinY;
-			const byte transp = (byte)(button.flags >> 8);
-
-			// Crop blit against the viewport.
-			const int x0 = MAX<int>(sx, kMapWinX);
-			const int y0 = MAX<int>(sy, kMapWinY);
-			const int x1 = MIN<int>(sx + button.surface.w, kMapWinX + kMapWinW);
-			const int y1 = MIN<int>(sy + button.surface.h, kMapWinY + kMapWinH);
-			for (int row = y0; row < y1; row++) {
-				const byte *src = (const byte *)button.surface.getBasePtr(0, row - sy);
-				byte *dst = (byte *)scratch.getBasePtr(0, row);
-				for (int col = x0; col < x1; col++) {
-					const byte px = src[col - sx];
-					if (px != transp)
-						dst[col] = px;
-				}
-			}
-		}
-
-		// Partner sprite on the detail map. Drawn last so it sits over
-		// the frame and the BIGMAP.PIC viewport.
-		if (haveDetailAnim) {
-			const uint32 now = g_system->getMillis();
-			const uint frameIdx =
-				(uint)((now / 100) % detailAnim.size());
-			const Picture &fr = detailAnim[frameIdx];
-			const byte transp = (byte)(fr.flags >> 8);
-			for (int row = 0; row < fr.surface.h; row++) {
-				const int dstY = kDetailAnimY + row;
-				if (dstY < 0 || dstY >= 200) continue;
-				const byte *src = (const byte *)fr.surface.getBasePtr(0, row);
-				byte *dst = (byte *)scratch.getBasePtr(0, dstY);
-				for (int col = 0; col < fr.surface.w; col++) {
-					const int dstX = kDetailAnimX + col;
-					if (dstX < 0 || dstX >= 320) continue;
-					if (src[col] != transp)
-						dst[dstX] = src[col];
-				}
-			}
-		}
-
-		g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
-								   0, 0, 320, 200);
-		g_system->updateScreen();
-	};
-
-	drawDetail();
-	uint32 detailLastTick = g_system->getMillis();
-
-	while (!shouldQuit()) {
-		Common::Event ev;
-		bool dirty = false;
-		while (g_system->getEventManager()->pollEvent(ev)) {
-			if (ev.type == Common::EVENT_QUIT ||
-				ev.type == Common::EVENT_RETURN_TO_LAUNCHER)
-				return;
-			if (ev.type == Common::EVENT_KEYDOWN) {
-				if (ev.kbd.keycode == Common::KEYCODE_ESCAPE)
-					return;  // exit detail back to caller (site loop / engine)
-				const int kStep = 16;
-				if (ev.kbd.keycode == Common::KEYCODE_LEFT) {
-					scrollX = MAX<int>(0, scrollX - kStep);
-					dirty = true;
-				} else if (ev.kbd.keycode == Common::KEYCODE_RIGHT) {
-					scrollX = MIN<int>(MAX<int>(0, mapW - kMapWinW),
-						scrollX + kStep);
-					dirty = true;
-				} else if (ev.kbd.keycode == Common::KEYCODE_UP) {
-					scrollY = MAX<int>(0, scrollY - kStep);
-					dirty = true;
-				} else if (ev.kbd.keycode == Common::KEYCODE_DOWN) {
-					scrollY = MIN<int>(MAX<int>(0, mapH - kMapWinH),
-						scrollY + kStep);
-					dirty = true;
-				}
-			}
-			if (ev.type == Common::EVENT_LBUTTONDOWN) {
-				// Scroll arrows + slider rects live in `SmallMapButtons`
-				// at 29be:0x159e (six 8-byte rects in order: Y-up, Y-down,
-				// X-left, X-right, right-panel, top-right) plus the
-				// dedicated `XSliderRect @ 29be:15d6` and
-				// `YSliderRect @ 29be:15de`. Format {x1,y1,x2,y2}.
-				const Common::Rect kArrowYUp   (237,   2, 247,  11);
-				const Common::Rect kArrowYDown (237, 163, 247, 172);
-				const Common::Rect kArrowXLeft (  2, 175,  12, 185);
-				const Common::Rect kArrowXRight(224, 175, 234, 185);
-				const Common::Rect kXSlider    ( 15, 175, 221, 185);
-				const Common::Rect kYSlider    (237,  14, 247, 160);
-				const Common::Rect kSetupBtn   (252,   4, 315,  42);
-
-				const int kArrowStep = 16;
-				const int kSliderRange = mapW - kMapWinW;
-				const int kSliderRangeY = mapH - kMapWinH;
-
-				if (kSetupBtn.contains(ev.mouse.x, ev.mouse.y)) {
-					// Setup button on detail too ??? `_NextScreen = 6` in
-					// the original. We treat it the same way: bail back
-					// to case selection.
-					_mystery.clear();
-					_nextScreen = kScreenInvalid;
-					return;
-				}
-				if (kArrowYUp.contains(ev.mouse.x, ev.mouse.y)) {
-					scrollY = MAX<int>(0, scrollY - kArrowStep);
-					dirty = true;
-				} else if (kArrowYDown.contains(ev.mouse.x, ev.mouse.y)) {
-					scrollY = MIN<int>(MAX<int>(0, kSliderRangeY),
-						scrollY + kArrowStep);
-					dirty = true;
-				} else if (kArrowXLeft.contains(ev.mouse.x, ev.mouse.y)) {
-					scrollX = MAX<int>(0, scrollX - kArrowStep);
-					dirty = true;
-				} else if (kArrowXRight.contains(ev.mouse.x, ev.mouse.y)) {
-					scrollX = MIN<int>(MAX<int>(0, kSliderRange),
-						scrollX + kArrowStep);
-					dirty = true;
-				} else if (kXSlider.contains(ev.mouse.x, ev.mouse.y)) {
-					// Click on X slider track ??? jump scrollX so the
-					// click position maps proportionally into the map.
-					if (kSliderRange > 0) {
-						const int t = ev.mouse.x - kXSlider.left;
-						const int tw = kXSlider.width();
-						scrollX = MAX<int>(0, MIN<int>(kSliderRange,
-							t * kSliderRange / MAX<int>(1, tw)));
-						dirty = true;
-					}
-				} else if (kYSlider.contains(ev.mouse.x, ev.mouse.y)) {
-					if (kSliderRangeY > 0) {
-						const int t = ev.mouse.y - kYSlider.top;
-						const int th = kYSlider.height();
-						scrollY = MAX<int>(0, MIN<int>(kSliderRangeY,
-							t * kSliderRangeY / MAX<int>(1, th)));
-						dirty = true;
-					}
-				} else if (ev.mouse.x >= kMapWinX &&
-						   ev.mouse.x < kMapWinX + kMapWinW &&
-						   ev.mouse.y >= kMapWinY &&
-						   ev.mouse.y < kMapWinY + kMapWinH) {
-					// Hit-test the per-site button at its actual bbox
-					// (`_StampButtons` records the rect at SmallMap +8/+0xa
-					// with the button PIC's width/height).
-					for (uint i = 0; i < _mystery.numSites(); i++) {
-						if (!_mystery._onSites[i] &&
-							i != _mystery._siteNumber)
-							continue;
-						const byte *entry = _mystery.mapEntry(i);
-						if (!entry) continue;
-						const uint16 buttonId = READ_LE_UINT16(entry + 0x0);
-						const uint16 mx       = READ_LE_UINT16(entry + 0x8);
-						const uint16 my       = READ_LE_UINT16(entry + 0xa);
-						Picture button;
-						int bw = 16, bh = 16;
-						if (_buttonArchive.loadEntry(buttonId, button)) {
-							bw = button.surface.w;
-							bh = button.surface.h;
-						}
-						const int sx = (int)mx - scrollX + kMapWinX;
-						const int sy = (int)my - scrollY + kMapWinY;
-						if (ev.mouse.x >= sx && ev.mouse.x < sx + bw &&
-							ev.mouse.y >= sy && ev.mouse.y < sy + bh) {
-							_mystery._lastSite = _mystery._siteNumber;
-							_mystery._siteNumber = (uint16)i;
-							return;
-						}
-					}
-				}
-			}
-		}
-		// Cycle the partner sprite at 100 ms ticks (same cadence as
-		// `_DoMapScreen`'s `_CheckFrameRate` + `_UpdateAnimations` loop).
-		const uint32 now = g_system->getMillis();
-		if (haveDetailAnim && now - detailLastTick >= 100) {
-			detailLastTick = now;
-			dirty = true;
-		}
-		if (dirty)
-			drawDetail();
-		g_system->updateScreen();
-		g_system->delayMillis(10);
-	}
-}
-
-void EEMEngine::doHelp() {
-	// `_KDHelp` reads two hint TextBlock offsets from `_KDTextIndex`:
-	//   word @ +0xe : first-time hint
-	//   word @ +0x10: second-time hint (cycles back to first if missing)
-	// `_SawHelpHint` toggles between them.
-	if (!_mystery.isLoaded() || !_font.isLoaded())
-		return;
-
-	const byte *kd = _mystery.kdTextIndex();
-	if (!kd)
-		return;
-
-	const uint16 hintFirst  = READ_LE_UINT16(kd + 0x0e);
-	const uint16 hintSecond = READ_LE_UINT16(kd + 0x10);
-	uint16 use = _mystery._sawHelpHint && hintSecond != 0xFFFF ? hintSecond : hintFirst;
-	if (use == 0xFFFF) {
-		debugC(1, kDebugScript, "doHelp: no hint configured");
-		return;
-	}
-	if (!_mystery._sawHelpHint && hintFirst != 0xFFFF)
-		_mystery._sawHelpHint = true;
-
-	const Common::String raw = _mystery.textAt(use);
-	const Common::String text = parseString(raw, _playerName, _partner);
-
-	Graphics::ManagedSurface scratch(320, 200,
-		Graphics::PixelFormat::createFormatCLUT8());
-	scratch.clear();
-	_font.drawString(&scratch, "HELP", 8, 4, 320, 0xF);
-	_font.drawWordWrapped(&scratch, 8, 24, 304, text, 0xF);
-	g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
-							   0, 0, 320, 200);
-	g_system->updateScreen();
-	waitForInput(60000);
-}
-
-void EEMEngine::doInterfaceHelp(uint num) {
-	// Mirrors `_InterfaceHelp(num)` @ 1560:0205. The original walks
-	// `HelpData @ 29be:00c8` (5-byte entries: u8 count, then up to 2
-	// u16 picIds), `_GetPicture`s each one, blits it fullscreen via
-	// `_Rect_Move_Mask(0, 0, ...)`, and waits for click / key. ESC ends
-	// the cycle; any other input advances to the next pic.
-	//
-	// Verified from Ghidra HelpData bytes:
-	//   entry 0 (PDA / gallery HELP button): count=2, picIds = 0x0063, 0x01ae
-	//   entry 1: count=2, picIds = 0x0192, 0x01b1
-	// Only entry 0 is reachable from the PDA notebook (rect 1) and the
-	// gallery (rect 1) ??? both call `_InterfaceHelp(0)`.
-	static const uint16 kHelpPics[][2] = {
-		{ 0x0063, 0x01ae },
-		{ 0x0192, 0x01b1 },
-	};
-	if (num >= ARRAYSIZE(kHelpPics))
-		return;
-
-	debugC(1, kDebugScript, "doInterfaceHelp(%u): showing pics 0x%x, 0x%x",
-		   num, kHelpPics[num][0], kHelpPics[num][1]);
-
-	for (uint i = 0; i < 2; i++) {
-		const uint16 picId = kHelpPics[num][i];
-		Picture pic;
-		if (!_picsArchive.getPicture(picId, pic)) {
-			warning("doInterfaceHelp: getPicture(0x%x) failed", picId);
-			continue;
-		}
-		debugC(1, kDebugScript, "doInterfaceHelp: pic 0x%x = %dx%d flags=0x%x",
-			   picId, pic.surface.w, pic.surface.h, pic.flags);
-
-		// Compose a 320x200 frame (cleared) and blit the help pic at (0,0)
-		// with the original's masked-blit semantics: pixels equal to the
-		// pic's sub-mode (high byte of `pic[0]`, see `_Rect_Move_Mask`
-		// param_10 at 1000:03fc) are treated as transparent and skipped.
-		Graphics::ManagedSurface scratch(320, 200,
-			Graphics::PixelFormat::createFormatCLUT8());
-		scratch.clear();
-		const byte transp = (byte)(pic.flags >> 8);
-		const int w = MIN<int>(pic.surface.w, 320);
-		const int h = MIN<int>(pic.surface.h, 200);
-		for (int row = 0; row < h; row++) {
-			const byte *src = (const byte *)pic.surface.getBasePtr(0, row);
-			byte *dst = (byte *)scratch.getBasePtr(0, row);
-			for (int col = 0; col < w; col++) {
-				if (src[col] != transp)
-					dst[col] = src[col];
-			}
-		}
-		g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
-								   0, 0, 320, 200);
-		g_system->updateScreen();
-
-		bool escape = false;
-		while (!shouldQuit() && !escape) {
-			Common::Event ev;
-			bool advance = false;
-			while (g_system->getEventManager()->pollEvent(ev)) {
-				if (ev.type == Common::EVENT_QUIT ||
-					ev.type == Common::EVENT_RETURN_TO_LAUNCHER) {
-					return;
-				}
-				if (ev.type == Common::EVENT_LBUTTONDOWN) {
-					advance = true;
-					break;
-				}
-				if (ev.type == Common::EVENT_KEYDOWN) {
-					if (ev.kbd.keycode == Common::KEYCODE_ESCAPE)
-						escape = true;
-					else
-						advance = true;
-					break;
-				}
-			}
-			if (advance || escape)
-				break;
-			g_system->updateScreen();
-			g_system->delayMillis(15);
-		}
-		if (escape)
-			break;
-	}
-}
-
-uint16 EEMEngine::getKDTextBalloon(byte firstChar) const {
-	// Mirrors `_GetKDTextBalloon @ 1df2:0105`:
-	//   if ((ctype[firstChar] & 2) == 0)  bub = *(u16*)29be:1068 = 0x17
-	//   else                              bub = *(u16*)(29be:0fe6+0x1e+c*2)
-	// `ctype` is Borland's `_ctype_` array at `29be:2be1`. Bit 1 (0x02) is
-	// set only for digits '0'..'9' (verified by reading the table ??? '0'..'9'
-	// each map to byte 0x02; everything else has bit 1 clear).
-	// Lookup table at 29be:1064 (= 29be:0fe6 + 0x1e + '0'*2):
-	//   '0'???0x15  '1'???0x16  '2'???0x17  '3'???0x18  '4'???0x19
-	//   '5'???0x1a  '6'???0x20  '7'???0x21  '8'???0x22  '9'???0x1e
-	// Note `*(u16*)29be:1068` (= entry for '2') is the same byte the
-	// non-digit fallback returns ??? the original encodes the constant by
-	// reusing the digit-2 slot.
-	if (firstChar < '0' || firstChar > '9')
-		return 0x17;
-	static const uint16 kDigitBalloons[10] = {
-		0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x20, 0x21, 0x22, 0x1e
-	};
-	return kDigitBalloons[firstChar - '0'];
-}
-
-void EEMEngine::setPartnerEraseBg(const Graphics::ManagedSurface *bg) {
-	if (bg && bg->w == 320 && bg->h == 200) {
-		_partnerEraseBg.create(320, 200,
-			Graphics::PixelFormat::createFormatCLUT8());
-		for (int row = 0; row < 200; row++) {
-			memcpy((byte *)_partnerEraseBg.getBasePtr(0, row),
-				   (const byte *)bg->getBasePtr(0, row), 320);
-		}
-	} else {
-		_partnerEraseBg.free();
-	}
-}
-
-void EEMEngine::playKdAnim(uint16 num) {
-	// Mirrors `_DoKDAnim(num) @ 168d:028a` + `_PlayAnimation @ 172b:1f46`:
-	//   _SuspendAnimation(WaitHandle);
-	//   anim   = WaitAnims[1+num].anim[partner]   (table @ 29be:0228)
-	//   x      = WaitAnims[1+num].x[partner]
-	//   y      = WaitAnims[1+num].y[partner]
-	//   _PlayAnimation(anim, x, y, WaitHandle)
-	//     ??? registers a state-4 (one-shot) animation slot and lets
-	//       `_UpdateAnimations` walk the sequence script until 0x80,
-	//       then frees this slot and re-activates `WaitHandle`.
-	// Our port renders the partner's idle inline in each redraw rather
-	// than via a slot system, so we play the one-shot synchronously here
-	// (blocking) and resume normal idle rendering when the caller
-	// returns. That matches the user-visible effect: the partner's
-	// gesture (Jenny taking a picture, etc.) finishes before the
-	// speaker portrait + speech balloon appear.
-	//
-	// Six valid kdAnimNum entries (0..5). Verified bytes from
-	// `29be:0228`. Layout per entry: { animJake, animJenny, xJake,
-	// xJenny, yJake, yJenny }. Position is (6, 80) in every entry.
-	static const uint16 kKdAnimTable[6][6] = {
-		{ 0x03, 0x0c, 6, 6, 80, 80 }, // 0 ??? speaker idx 1 wait anim
-		{ 0x01, 0x0b, 6, 6, 80, 80 }, // 1 ??? same as PDA idle
-		{ 0x04, 0x0d, 6, 6, 80, 80 }, // 2
-		{ 0x02, 0x10, 6, 6, 80, 80 }, // 3 ??? same as gallery
-		{ 0x05, 0x05, 6, 6, 80, 80 }, // 4 ??? same anim both partners
-		{ 0x06, 0x06, 6, 6, 80, 80 }, // 5 ??? same anim both partners
-	};
-	if (num >= ARRAYSIZE(kKdAnimTable))
-		return;
-
-	const uint partner = (_partner == 0) ? 0 : 1;
-	const uint16 animId = kKdAnimTable[num][partner];
-	const int    px     = (int)kKdAnimTable[num][2 + partner];
-	const int    py     = (int)kKdAnimTable[num][4 + partner];
-
-	Animation anim;
-	if (!_aniArchive.loadAnimation(animId, anim) || anim.empty()) {
-		warning("playKdAnim(%u): anim %u failed to load", num, animId);
-		return;
-	}
-
-	// Sequence-script lookup. Entries copied verbatim from
-	// `_AnimationSequences @ 29be:22d4` walked through to the next 0x80.
-	// Each script is a u16[] of frame indices terminated by 0x80; we
-	// don't yet handle 0x81 jumps (none of the kdAnim sequences use
-	// them ??? verified). seqnum == animId for these calls (per
-	// `_PlayAnimation` 172b:1f5d push order).
-	struct Script {
-		uint16 seqnum;
-		uint8 len;
-		uint8 frames[20];  // long enough for any kdAnim script
-	};
-	static const Script kScripts[] = {
-		// seqnum 1 (29be:188a) ??? head bob
-		{ 0x01, 15, { 0,1,2,0,1,0,2,1,0,1,0,1,2,1,0 } },
-		// seqnum 2 (29be:18aa) ??? short blip then long pause
-		{ 0x02, 16, { 0,1,2,1,0,0,0,0,0,0,0,0,0,0,0,0 } },
-		// seqnum 3 (29be:18e0) ??? Jake "lift, hold, lower" gesture
-		{ 0x03,  9, { 0,1,2,3,2,2,2,1,0 } },
-		// seqnum 4 (29be:18f4) ??? bigger gesture (camera flash-style)
-		{ 0x04, 13, { 0,1,2,3,4,5,4,4,4,3,2,1,0 } },
-		// seqnum 5 (29be:1910) ??? held idle with a single peak
-		{ 0x05, 13, { 0,0,0,1,2,3,2,1,0,0,0,0,0 } },
-		// seqnum 6 (29be:192c) ??? empty (immediate END)
-		{ 0x06,  0, { 0 } },
-		// seqnum 0xb (29be:188a, same as 1) ??? Jenny PDA idle
-		{ 0x0b, 15, { 0,1,2,0,1,0,2,1,0,1,0,1,2,1,0 } },
-		// seqnum 0xc (29be:18e0, same as 3) ??? Jenny "take a picture"
-		{ 0x0c,  9, { 0,1,2,3,2,2,2,1,0 } },
-		// seqnum 0xd (29be:18f4, same as 4) ??? Jenny big gesture
-		{ 0x0d, 13, { 0,1,2,3,4,5,4,4,4,3,2,1,0 } },
-		// seqnum 0x10 (29be:1956) ??? Jenny short anim
-		{ 0x10,  9, { 0,0,0,1,0,0,0,0,0 } },
-	};
-	const uint8 *frames = nullptr;
-	uint frameCount = 0;
-	for (uint i = 0; i < ARRAYSIZE(kScripts); i++) {
-		if (kScripts[i].seqnum == animId) {
-			frames = kScripts[i].frames;
-			frameCount = kScripts[i].len;
-			break;
-		}
-	}
-	if (frameCount == 0) {
-		// Fallback: linear playback through anim cells (better than
-		// nothing if a future kdAnim references an unscripted anim).
-		frameCount = (uint)anim.size();
-	}
-
-	// Erase-source for between-frame redraw. Prefer the partner-less
-	// backdrop the caller stashed via `setPartnerEraseBg` (e.g. the
-	// site's `_bgSnapshot`, which has the static drops + frame but no
-	// partner sprite). Without that, fall back to whatever's currently
-	// on screen ??? which works for full-screen contexts (PDA / accuse /
-	// briefing) where there is no separate idle partner overlay to
-	// erase, but produces visible "ghosting" against the site's idle
-	// partner cell at (6, 80) because it has the resting pose baked in.
-	Graphics::ManagedSurface bg(320, 200,
-		Graphics::PixelFormat::createFormatCLUT8());
-	if (_partnerEraseBg.w == 320 && _partnerEraseBg.h == 200) {
-		for (int row = 0; row < 200; row++) {
-			memcpy((byte *)bg.getBasePtr(0, row),
-				   (const byte *)_partnerEraseBg.getBasePtr(0, row), 320);
-		}
-	} else {
-		Graphics::Surface *screen = g_system->lockScreen();
-		if (!screen)
-			return;
-		for (int row = 0; row < 200; row++) {
-			memcpy((byte *)bg.getBasePtr(0, row),
-				   (const byte *)screen->getBasePtr(0, row), 320);
-		}
-		g_system->unlockScreen();
-	}
-
-	for (uint i = 0; i < frameCount && !shouldQuit(); i++) {
-		const uint frameIdx = frames ? (uint)frames[i] : i;
-		if (frameIdx >= anim.size())
-			continue;
-		const Picture &fr = anim[frameIdx];
-		const byte transp = (byte)(fr.flags >> 8);
-
-		// Restore BG, then masked-blit the next frame.
-		Graphics::ManagedSurface scratch(320, 200,
-			Graphics::PixelFormat::createFormatCLUT8());
-		for (int row = 0; row < 200; row++) {
-			memcpy((byte *)scratch.getBasePtr(0, row),
-				   (const byte *)bg.getBasePtr(0, row), 320);
-		}
-		const int w = MIN<int>(fr.surface.w, 320 - px);
-		const int h = MIN<int>(fr.surface.h, 200 - py);
-		for (int row = 0; row < h; row++) {
-			const int dstY = py + row;
-			if (dstY < 0) continue;
-			const byte *src = (const byte *)fr.surface.getBasePtr(0, row);
-			byte *dst = (byte *)scratch.getBasePtr(0, dstY);
-			for (int col = 0; col < w; col++) {
-				const int dstX = px + col;
-				if (dstX < 0) continue;
-				if (src[col] != transp)
-					dst[dstX] = src[col];
-			}
-		}
-		g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
-								   0, 0, 320, 200);
-		g_system->updateScreen();
-
-		// One frame per `_CheckFrameRate` tick. The original calibrates
-		// this to ~10 fps; 100 ms matches what the rest of the engine
-		// uses for partner / NPC frame cycling.
-		const uint32 wakeup = g_system->getMillis() + 100;
-		while (g_system->getMillis() < wakeup && !shouldQuit()) {
-			Common::Event ev;
-			while (g_system->getEventManager()->pollEvent(ev)) {
-				// Drain events but don't allow skipping mid-animation ???
-				// the speaker portrait + balloon haven't been drawn yet
-				// and a click would otherwise eat the upcoming clue.
-				if (ev.type == Common::EVENT_QUIT ||
-					ev.type == Common::EVENT_RETURN_TO_LAUNCHER)
-					return;
-			}
-			g_system->delayMillis(10);
-		}
-	}
-
-	// Restore BG so the next caller (speaker portrait blit) starts clean.
-	g_system->copyRectToScreen(bg.getPixels(), bg.pitch, 0, 0, 320, 200);
-	g_system->updateScreen();
-}
-
-bool EEMEngine::getBalloonInsets(uint16 bubNum, uint16 &xInset,
-								  uint16 &yInset, uint16 &textW) const {
-	// 52-entry, 10-bytes-each balloon-metadata table at `29be:0875`.
-	// Used at 1df2:0aef-0af9 (accuse hint) and `_DisplayClue` to position
-	// `_WordWrap` text inside the balloon. Only +0/+2/+4 are read here:
-	//   +0..1 = text X inset, +2..3 = Y inset, +4..5 = max wrap width
-	// (+6/+8 = balloon h / tail offset, both unused for text layout).
-	static const struct { uint16 x, y, w; } kTable[] = {
-		{ 6, 4, 142 }, { 6, 4, 142 }, { 6, 4, 142 }, { 6, 4, 142 },
-		{ 6, 4, 142 }, { 6, 4, 142 }, { 6, 4, 142 },
-		{ 6, 4, 224 }, { 6, 4, 224 }, { 6, 4, 224 }, { 6, 4, 224 },
-		{ 6, 4, 224 }, { 6, 4, 224 }, { 6, 4, 224 },
-		{ 6, 4, 291 }, { 6, 4, 291 }, { 6, 4, 291 }, { 6, 4, 291 },
-		{ 6, 4, 291 }, { 6, 4, 291 }, { 6, 4, 291 },
-		{ 5, 4, 155 }, { 5, 4, 155 }, { 5, 4, 155 }, { 5, 4, 155 },
-		{ 5, 4, 155 }, { 5, 4, 155 }, { 5, 4, 155 },
-		{ 5, 4, 237 }, { 5, 4, 237 }, { 5, 4, 237 }, { 5, 4, 237 },
-		{ 5, 4, 237 }, { 5, 4, 237 }, { 5, 4, 237 },
-		{ 3, 4, 155 }, { 3, 4, 155 }, { 3, 4, 155 }, { 3, 4, 155 },
-		{ 3, 4, 155 }, { 3, 4, 155 }, { 3, 4, 155 },
-		{ 5, 4, 238 }, { 5, 4, 238 }, { 5, 4, 238 }, { 5, 4, 238 },
-		{ 5, 4, 238 }, { 5, 4, 238 }, { 5, 4, 238 },
-		{ 5, 8, 158 }, { 5, 8, 176 }, { 8, 7, 142 }
-	};
-	const uint idx = bubNum & 0x7F;
-	if (idx >= ARRAYSIZE(kTable))
-		return false;
-	xInset = kTable[idx].x;
-	yInset = kTable[idx].y;
-	textW  = kTable[idx].w;
-	return true;
-}
-
-bool EEMEngine::areYouSure() {
-	// Mirrors `_AreYouSure` @ 1a35:0a5c. Original loads PIC 0x136 for the
-	// dialog body and PIC 0x1FD/0x1FE for YES/NO. We render a minimal
-	// text dialog that preserves the screen behind it.
-	if (!_font.isLoaded())
-		return true;
-
-	Graphics::Surface *screen = g_system->lockScreen();
-	Graphics::ManagedSurface saved(320, 200,
-		Graphics::PixelFormat::createFormatCLUT8());
-	if (screen) {
-		for (int row = 0; row < 200; row++) {
-			memcpy((byte *)saved.getBasePtr(0, row),
-				   (const byte *)screen->getBasePtr(0, row), 320);
-		}
-		g_system->unlockScreen();
-	}
-
-	const Common::Rect dlg(60, 70, 260, 140);
-	Graphics::ManagedSurface scratch(320, 200,
-		Graphics::PixelFormat::createFormatCLUT8());
-	for (int row = 0; row < 200; row++)
-		memcpy((byte *)scratch.getBasePtr(0, row),
-			   (const byte *)saved.getBasePtr(0, row), 320);
-	scratch.fillRect(dlg, 0);
-	scratch.frameRect(dlg, 0xF);
-	_font.drawString(&scratch, "Are you sure you want to quit?", dlg.left + 8, dlg.top + 8, 320, 0xF);
-	_font.drawString(&scratch, "Y - Yes", dlg.left + 16, dlg.top + 36, 320, 0xF);
-	_font.drawString(&scratch, "N - No", dlg.left + 100, dlg.top + 36, 320, 0xF);
-	g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
-							   0, 0, 320, 200);
-	g_system->updateScreen();
-
-	bool result = false;
-	while (!shouldQuit()) {
-		Common::Event ev;
-		bool decided = false;
-		while (g_system->getEventManager()->pollEvent(ev)) {
-			if (ev.type == Common::EVENT_QUIT ||
-				ev.type == Common::EVENT_RETURN_TO_LAUNCHER) {
-				result = true;
-				decided = true;
-				break;
-			}
-			if (ev.type == Common::EVENT_KEYDOWN) {
-				if (ev.kbd.keycode == Common::KEYCODE_y ||
-					ev.kbd.keycode == Common::KEYCODE_RETURN) {
-					result = true; decided = true; break;
-				}
-				if (ev.kbd.keycode == Common::KEYCODE_n ||
-					ev.kbd.keycode == Common::KEYCODE_ESCAPE) {
-					result = false; decided = true; break;
-				}
-			}
-		}
-		if (decided)
-			break;
-		g_system->updateScreen();
-		g_system->delayMillis(15);
-	}
-
-	// Restore the screen so the caller's UI is intact.
-	g_system->copyRectToScreen(saved.getPixels(), saved.pitch, 0, 0, 320, 200);
-	g_system->updateScreen();
-	return result;
-}
-
-void EEMEngine::doAccuse() {
-	if (!_mystery.isLoaded())
-		return;
-
-	// Mirrors `_DoAccuseGallery @ 1df2:0a31`:
-	//   1. Show KD's hint balloon (KDTextIndex[+8] text).
-	//   2. `_GetBackground(0x3f)` ??? same backdrop as PDA / gallery.
-	//   3. `_DrawGallery()` ??? renders portraits at the standard 5 slots
-	//      (positions verified at 29be:0x116, bottom-aligned baseline 0x48).
-	//   4. Click loop dispatching on `_NoteButtons` (same table as PDA)
-	//      with a separate `_HandleAccuseNoteButton` jump table.
-	const uint8 num = _mystery.numSuspects();
-	if (num == 0)
-		return;
-
-	const byte *gd = _mystery.galleryData();
-
-	// Verbatim from 29be:0x116 ??? same five suspect slot positions as
-	// `_DrawGallery @ 158f:0046`.
-	struct Slot { int x; int y; };
-	static const Slot kGallerySlots[5] = {
-		{  83,  14 }, { 155,  14 }, { 227,  14 },
-		{ 119,  90 }, { 191,  90 }
-	};
-
-	Picture accuseBg;
-	const bool haveAccuseBg = _picsArchive.getPicture(0x3f, accuseBg);
-
-	Common::Array<Common::Rect> slotRects;
-	Common::Array<int> slotSuspect;
-	slotRects.resize(num);
-	slotSuspect.resize(num);
-	for (uint i = 0; i < num; i++)
-		slotSuspect[i] = -1;
-
-	int highlighted = 0;
-	auto drawGallery = [&]() {
-		Graphics::ManagedSurface scratch(320, 200,
-			Graphics::PixelFormat::createFormatCLUT8());
-		scratch.clear();
-		if (haveAccuseBg) {
-			const int bw = MIN<int>(accuseBg.surface.w, 320);
-			const int bh = MIN<int>(accuseBg.surface.h, 200);
-			for (int row = 0; row < bh; row++) {
-				memcpy((byte *)scratch.getBasePtr(0, row),
-					   (const byte *)accuseBg.surface.getBasePtr(0, row), bw);
-			}
-		}
-
-		for (uint i = 0; i < num && i < Mystery::kGalleryCap; i++) {
-			slotRects[i] = Common::Rect();
-			slotSuspect[i] = -1;
-			if (!gd) continue;
-			const uint8 phys = _mystery._newOrder[i];
-			if (phys >= 5) continue;
-			// `_DrawGallery @ 158f:00b9` skips suspects whose
-			// `_InGallery[phys]` flag is 0 ??? that's the original gate
-			// (some suspects only become visible after being met or
-			// stay hidden after a wrong accusation removes them).
-			if (_mystery._inGallery[phys] == 0) continue;
-			const Slot &s = kGallerySlots[phys];
-
-			const uint16 picId = READ_LE_UINT16(gd + i * 0x46);
-			if (picId == 0) continue;
-			Picture portrait;
-			if (!_picsArchive.getPicture(picId, portrait))
-				continue;
-
-			const int placeX = s.x;
-			const int placeY = s.y + (0x48 - portrait.surface.h);
-			const byte transp = (byte)(portrait.flags >> 8);
-			const int w = MIN<int>(portrait.surface.w, 320 - placeX);
-			const int h = MIN<int>(portrait.surface.h, 200 - placeY);
-			if (w <= 0 || h <= 0) continue;
-			for (int row = 0; row < h; row++) {
-				const int dstY = placeY + row;
-				if (dstY < 0) continue;
-				const byte *src =
-					(const byte *)portrait.surface.getBasePtr(0, row);
-				byte *dst = (byte *)scratch.getBasePtr(0, dstY);
-				for (int col = 0; col < w; col++) {
-					const int dstX = placeX + col;
-					if (src[col] != transp)
-						dst[dstX] = src[col];
-				}
-			}
-			slotRects[i] = Common::Rect(placeX, placeY,
-										 placeX + w, placeY + h);
-			slotSuspect[i] = (int)i;
-		}
-
-		// Highlight indicator. The original moves the mouse cursor
-		// to the centre of the highlighted suspect via `_PutMouseInRect`
-		// (1df2:0b8e) ??? we draw a 1px outline in palette index 0xFE
-		// (within the marching-ants cycle range 0xF9..0xFE) which is
-		// unambiguously visible under any palette without warping the
-		// player's cursor.
-		if (highlighted >= 0 && highlighted < (int)slotRects.size() &&
-			!slotRects[highlighted].isEmpty()) {
-			Common::Rect r = slotRects[highlighted];
-			r.grow(1);
-			scratch.frameRect(r, 0xFE);
-		}
-
-		g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
-								   0, 0, 320, 200);
-		g_system->updateScreen();
-	};
-
-	// Step 1 ??? KD hint balloon. Mirrors `_DoAccuseGallery @ 1df2:0a31`
-	// (1df2:0a4c-1df2:0afe):
-	//   text  = TextBlock + KDTextIndex[+8]               (1df2:0a4c-0a57)
-	//   bub   = _GetKDTextBalloon(text[0])                (1df2:0a6d)
-	//   GetBalloon(bub)                                   (1df2:0a7c)
-	//   y     = (h < 0x4e) ? (0x50 - h) >> 1 : 1          (1df2:0a8b-0aa5)
-	//   AddPicBackground(pic, 0x21, y)                    (1df2:0aab)
-	//   WordWrap(0x21+tbl[bub].x, y+tbl[bub].y, tbl[bub].w, text, color=0)
-	//     tbl @ 29be:0875, 10-byte entries (1df2:0ad6-0af1)
-	const byte *kdIdx = _mystery.kdTextIndex();
-	if (kdIdx) {
-		const int16 textOff = (int16)READ_LE_UINT16(kdIdx + 8);
-		if (textOff != -1) {
-			const char *raw = _mystery.textAt((uint16)textOff);
-			Common::String hint =
-				parseString(raw ? raw : "", _playerName, _partner);
-			if (!hint.empty()) {
-				// First-char dispatch via getKDTextBalloon (1df2:0105).
-				// Note: we pass the *parsed* first char; the original
-				// reads it BEFORE `_ParseString`, but the player-name /
-				// partner-name substitutions never start with digits, so
-				// the dispatch result is the same either way.
-				const byte firstChar =
-					hint.empty() ? (byte)0 : (byte)hint[0];
-				const uint16 bubNum = getKDTextBalloon(firstChar);
-				Picture balloon;
-				const bool haveBalloon =
-					_balloonArchive.size() > (bubNum & 0x7F) &&
-					_balloonArchive.loadEntry(bubNum & 0x7F, balloon);
-
-				// 1df2:0a8b-1df2:0aa5: y = (h < 0x4e) ? (0x50-h)>>1 : 1
-				const int balloonX = 0x21;
-				int balloonY = 1;
-				if (haveBalloon && balloon.surface.h < 0x4e)
-					balloonY = (0x50 - balloon.surface.h) / 2;
-
-				Graphics::ManagedSurface ms(320, 200,
-					Graphics::PixelFormat::createFormatCLUT8());
-				ms.clear();
-				if (haveAccuseBg) {
-					const int bw = MIN<int>(accuseBg.surface.w, 320);
-					const int bh = MIN<int>(accuseBg.surface.h, 200);
-					for (int row = 0; row < bh; row++) {
-						memcpy((byte *)ms.getBasePtr(0, row),
-							   (const byte *)accuseBg.surface.getBasePtr(0, row), bw);
-					}
-				}
-				// Masked balloon blit ??? `_Rect_Move_Mask` (1000:03fc)
-				// skips pixels equal to `pic[0] >> 8`.
-				if (haveBalloon) {
-					const byte transp = (byte)(balloon.flags >> 8);
-					const int bw = MIN<int>(balloon.surface.w, 320 - balloonX);
-					const int bh = MIN<int>(balloon.surface.h, 200 - balloonY);
-					for (int row = 0; row < bh; row++) {
-						const byte *src = (const byte *)balloon.surface.getBasePtr(0, row);
-						byte *dst = (byte *)ms.getBasePtr(balloonX, balloonY + row);
-						for (int col = 0; col < bw; col++) {
-							if (src[col] != transp)
-								dst[col] = src[col];
-						}
-					}
-				}
-				// Inset table @ 29be:0875 ??? 1df2:0acb pushes color=0.
-				uint16 tx = 5, ty = 4, tw = 155;
-				getBalloonInsets(bubNum, tx, ty, tw);
-				if (_font.isLoaded()) {
-					_font.drawWordWrapped(&ms, balloonX + tx,
-										  balloonY + ty, tw, hint,
-										  haveBalloon ? 0 : 0xF);
-				}
-				g_system->copyRectToScreen(ms.getPixels(), ms.pitch,
-					0, 0, 320, 200);
-				g_system->updateScreen();
-				waitForInput(8000);
-			}
-		}
-	}
-
-	// Helper to find the next "alive" slot (one whose `_inGallery[phys]`
-	// flag is still set so a portrait was actually drawn). Mirrors the
-	// way the original wraps DI past empty slots.
-	auto nextLiveSlot = [&](int from, int dir) -> int {
-		const int n = (int)slotRects.size();
-		if (n <= 0) return 0;
-		for (int step = 1; step <= n; step++) {
-			int idx = (from + dir * step) % n;
-			if (idx < 0) idx += n;
-			if (!slotRects[idx].isEmpty())
-				return idx;
-		}
-		return from;
-	};
-	if (slotRects[highlighted].isEmpty())
-		highlighted = nextLiveSlot(highlighted, +1);
-
-	drawGallery();
-
-	// Wait-for-pick loop. Mirrors `_DoAccuseGallery` 1df2:0b26-1df2:0bc8:
-	//   * `_CheckFrameRate` + `_UpdateAnimations` per tick (1df2:0b2a-0b33)
-	//   * 5-entry input dispatch table @ 1df2:0bc9:
-	//       0x09 (TAB)   ??? handler 0x0b94 (cycle highlight)
-	//       0x0d (Enter) ??? handler 0x0b72 (pick = _SearchSuspects)
-	//       0x4b (LEFT)  ??? handler 0x0b94
-	//       0x4d (RIGHT) ??? handler 0x0b94
-	//       0xFFFF (mb)  ??? handler 0x0b72
-	//   * 0x0b94: `INC DI` + wraparound + `_PutMouseInRect(&Guys[DI])`,
-	//     i.e. advance highlight and warp cursor (1df2:0b94-0bb1).
-	//   * 0x0b72: `_SearchSuspects` (158f:0584) ??? mouse-rect hit-test;
-	//     if non-0xFFFF, pick that suspect.
-	// We don't warp the cursor (unfriendly under SDL); instead the
-	// highlight is drawn as a 1px outline and Enter picks it.
-	int picked = -1;
-	uint32 lastTick = g_system->getMillis();
-	bool dirty = false;
-	while (picked < 0 && !shouldQuit()) {
-		Common::Event ev;
-		while (g_system->getEventManager()->pollEvent(ev)) {
-			if (ev.type == Common::EVENT_QUIT ||
-				ev.type == Common::EVENT_RETURN_TO_LAUNCHER)
-				return;
-			if (ev.type == Common::EVENT_KEYDOWN) {
-				switch (ev.kbd.keycode) {
-				case Common::KEYCODE_ESCAPE:
-					return;
-				case Common::KEYCODE_TAB:
-				case Common::KEYCODE_RIGHT:
-					highlighted = nextLiveSlot(highlighted, +1);
-					dirty = true;
-					break;
-				case Common::KEYCODE_LEFT:
-					// 1df2:0b94 increments DI for LEFT too ??? but a
-					// keyboard-driven UX is friendlier with separate
-					// directions, so we mirror Right=+1 / Left=-1.
-					highlighted = nextLiveSlot(highlighted, -1);
-					dirty = true;
-					break;
-				case Common::KEYCODE_RETURN:
-				case Common::KEYCODE_KP_ENTER:
-					if (highlighted >= 0 &&
-						highlighted < (int)slotRects.size() &&
-						!slotRects[highlighted].isEmpty()) {
-						picked = highlighted;
-					}
-					break;
-				default: {
-					const int k = (int)ev.kbd.keycode;
-					if (k >= Common::KEYCODE_1 && k <= Common::KEYCODE_9) {
-						const int idx = k - Common::KEYCODE_1;
-						if (idx < num &&
-							!slotRects[idx].isEmpty())
-							picked = idx;
-					}
-					break;
-				}
-				}
-			}
-			if (ev.type == Common::EVENT_LBUTTONDOWN) {
-				for (uint i = 0; i < slotRects.size(); i++) {
-					if (slotSuspect[i] < 0) continue;
-					if (slotRects[i].contains(ev.mouse.x, ev.mouse.y)) {
-						picked = (int)i;
-						break;
-					}
-				}
-			}
-		}
-		// 100 ms tick ??? the original calls `_UpdateAnimations` per
-		// `_CheckFrameRate` (1df2:0b33). The accuse screen has no
-		// animations registered, so the tick is just a redraw cadence.
-		// We still re-render whenever the highlight moves (`dirty`).
-		const uint32 now = g_system->getMillis();
-		if (dirty || now - lastTick >= 100) {
-			drawGallery();
-			lastTick = now;
-			dirty = false;
-		}
-		g_system->updateScreen();
-		g_system->delayMillis(10);
-	}
-	if (picked < 0)
-		return;
-
-	// Real chain evaluation: sum point values of clues the player marked
-	// "selected" in the notebook. Mirrors `_SolvedCheck` @ 1df2:00ec.
-	const int points = _mystery.selectedPoints();
-	const bool guessedRight = _mystery.solvedCheck();
-	debugC(1, kDebugScript, "doAccuse: picked=%d selectedPts=%d -> %s",
-		   picked, points, guessedRight ? "correct" : "wrong");
-
-	// If the player hasn't marked any evidence yet, give them a hint
-	// rather than an instant fail. Mirrors the original "We're not ready
-	// to solve this mystery yet..." string at 29be:10f0.
-	if (points == 0 && _font.isLoaded()) {
-		Graphics::ManagedSurface scratch(320, 200,
-			Graphics::PixelFormat::createFormatCLUT8());
-		scratch.clear();
-		_font.drawWordWrapped(&scratch, 16, 80, 288,
-			"We're not ready to solve this mystery yet. "
-			"Let's keep investigating until we have some "
-			"more solid evidence to make our case! "
-			"(Press N in the site screen to mark clues.)",
-			0xF);
-		g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
-								   0, 0, 320, 200);
-		g_system->updateScreen();
-		waitForInput(15000);
-		return;
-	}
-
-	// Pick the ending based on the chain. For a correct accusation the
-	// original would call `_DisplayClue(_Mystery + AChain[0])`, play
-	// SCRAPBK.ANI and save progress. We load the matching `E<n>.BIN`
-	// ending text and render its pages with prev/next navigation.
-	const int endingNum = guessedRight ? picked : 0;
-	const Common::String fname = Common::String::format("E%d.BIN", endingNum);
-	Common::File f;
-	if (!f.open(Common::Path(fname))) {
-		warning("doAccuse: %s missing", fname.c_str());
-		return;
-	}
-
-	// E<n>.BIN format (verified against `_DisplayEndingPage` @ 1df2:044c):
-	//   u16 numPages
-	//   per page (10 bytes header + NUL-string):
-	//     u16 picNum
-	//     u16 x1, y1, x2, y2  (story rect)
-	//     bytes[] NUL-terminated text
-	const uint32 fileLen = f.size();
-	Common::Array<byte> blob(fileLen);
-	if (f.read(blob.data(), fileLen) != fileLen)
-		return;
-	const byte *e = blob.data();
-	const uint16 pages = READ_LE_UINT16(e);
-
-	uint pageIdx = 0;
-
-	while (!shouldQuit()) {
-		// Walk to pageIdx.
-		uint pos = 2;
-		uint cur = 0;
-		while (cur < pageIdx && pos + 10 < fileLen) {
-			const char *t = (const char *)(e + pos + 10);
-			pos += 10 + strlen(t) + 1;
-			cur++;
-		}
-		if (pos + 10 >= fileLen)
-			break;
-
-		const uint16 picNum = READ_LE_UINT16(e + pos + 0);
-		const uint16 x1     = READ_LE_UINT16(e + pos + 2);
-		const uint16 y1     = READ_LE_UINT16(e + pos + 4);
-		const uint16 x2     = READ_LE_UINT16(e + pos + 6);
-		const uint16 y2     = READ_LE_UINT16(e + pos + 8);
-		const char *raw     = (const char *)(e + pos + 10);
-		const Common::String txt = parseString(raw, _playerName, _partner);
-
-		Graphics::ManagedSurface scratch(320, 200,
-			Graphics::PixelFormat::createFormatCLUT8());
-		scratch.clear();
-
-		// Page background.
-		if (picNum != 0) {
-			Picture bg;
-			if (_picsArchive.getPicture(picNum, bg)) {
-				const int w = MIN<int>(bg.surface.w, 320);
-				const int h = MIN<int>(bg.surface.h, 200);
-				for (int row = 0; row < h; row++) {
-					memcpy((byte *)scratch.getBasePtr(0, row),
-						   (const byte *)bg.surface.getBasePtr(0, row), w);
-				}
-			}
-		}
-
-		if (_font.isLoaded()) {
-			Common::String banner = "Not enough evidence";
-			if (guessedRight)
-				banner = _mystery._firstTry ? "CORRECT - FIRST TRY!" : "CORRECT!";
-			_font.drawString(&scratch, banner, 8, 4, 320, 0xF);
-			_font.drawString(&scratch, Common::String::format("Evidence: %d/100  Suspect: %d",
-									   points, picked + 1), 8, 16, 320, 0xF);
-			const int wrapW = MAX<int>(16, x2 - x1);
-			const int wrapY = MAX<int>(28, (int)y1);
-			(void)y2;
-			_font.drawWordWrapped(&scratch, x1, wrapY, wrapW, txt, 0xF);
-			_font.drawString(&scratch, Common::String::format("page %u/%u  (Left/Right or click)",
-									   pageIdx + 1, pages), 8, 188, 320, 0xF);
-		}
-
-		g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
-								   0, 0, 320, 200);
-		g_system->updateScreen();
-
-		// Page navigation.
-		bool advance = false;
-		bool back    = false;
-		bool exit    = false;
-		while (!advance && !back && !exit && !shouldQuit()) {
-			Common::Event ev;
-			while (g_system->getEventManager()->pollEvent(ev)) {
-				if (ev.type == Common::EVENT_QUIT ||
-					ev.type == Common::EVENT_RETURN_TO_LAUNCHER) {
-					exit = true; break;
-				}
-				if (ev.type == Common::EVENT_LBUTTONDOWN) {
-					advance = true; break;
-				}
-				if (ev.type == Common::EVENT_KEYDOWN) {
-					if (ev.kbd.keycode == Common::KEYCODE_ESCAPE)
-						exit = true;
-					else if (ev.kbd.keycode == Common::KEYCODE_LEFT)
-						back = true;
-					else
-						advance = true;
-					break;
-				}
-			}
-			g_system->updateScreen();
-			g_system->delayMillis(15);
-		}
-		if (exit) break;
-		if (advance) {
-			if (pageIdx + 1 >= pages) break;
-			pageIdx++;
-		} else if (back) {
-			if (pageIdx > 0) pageIdx--;
-		}
-	}
-
-	// Mirror `_DisplayCorrect`'s scrap-book animation + solved tracking +
-	// auto-save (the original calls `_SavePlayerRecord` after a win).
-	if (guessedRight) {
-		const uint mn = _mystery.number();
-		if (mn < sizeof(_mysteriesSolved)) {
-			_mysteriesSolved[mn] = _mystery._firstTry ? 2 : 1;
-		}
-		playAnm(Common::Path("SCRAPBK.ANI"), 120, true);
-
-		// Auto-save into slot 0 (the engine's quicksave slot).
-		const Common::String desc = Common::String::format(
-			"%s ??? solved mystery %u", _playerName.c_str(), mn);
-		Common::Error err = saveGameState(0, desc, true);
-		if (err.getCode() != Common::kNoError)
-			warning("auto-save after solve failed: %s",
-					err.getDesc().c_str());
-	} else {
-		_mystery._firstTry = false;
-	}
-}
-
-// -------------------- save / load --------------------
-
-namespace {
-const uint32 kSaveMagic = MKTAG('E', 'E', 'M', '0');
-const byte   kSaveVer   = 3;  ///< v2: _mysteriesSolved tracker; v3: player name
-} // anonymous namespace
-
 bool EEMEngine::hasFeature(EngineFeature f) const {
 	// We support saving any time but loading only at startup (via the
 	// `--save-slot=N` resume path or a slot picked from the launcher).
diff --git a/engines/eem/eem.h b/engines/eem/eem.h
index 7b5c26a4be7..3777e5ac6ca 100644
--- a/engines/eem/eem.h
+++ b/engines/eem/eem.h
@@ -116,6 +116,13 @@ public:
 	/// ctype-bit-1 (= digit) at `29be:2be1 + char`.
 	uint16 getKDTextBalloon(byte firstChar) const;
 
+	/// Substitute the 0x80..0x89 control bytes the engine uses inside
+	/// `TextBlock` strings. Mirrors `_ParseString @ 1b66:07c3`; jump
+	/// table at 1b66:0cbe. Used by every clue / hint / balloon caller.
+	Common::String parseString(const Common::String &raw,
+							   const Common::String &playerName,
+							   uint partner) const;
+
 	/// Play the partner's one-shot reaction animation slot @num. Mirrors
 	/// `_DoKDAnim @ 168d:028a` + `_PlayAnimation @ 172b:1f46`. The
 	/// per-partner (animId, x, y) come from `_WaitAnims[1+num] @ 29be:0228`,
diff --git a/engines/eem/graphics.cpp b/engines/eem/graphics.cpp
new file mode 100644
index 00000000000..857dcb80ecc
--- /dev/null
+++ b/engines/eem/graphics.cpp
@@ -0,0 +1,209 @@
+/* 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/debug.h"
+#include "common/events.h"
+#include "common/file.h"
+#include "common/path.h"
+#include "common/savefile.h"
+#include "common/system.h"
+#include "common/textconsole.h"
+
+#include "graphics/cursorman.h"
+#include "graphics/managed_surface.h"
+
+#include "eem/detection.h"
+#include "eem/eem.h"
+
+// EEM ??? graphics helpers (KDGRAPH.C + KDHELP.C, the latter merged because
+// it has only three functions). Animation playback, balloon-table lookup,
+// and the help-screen primitives that share the same balloon machinery.
+
+namespace EEM {
+
+void EEMEngine::doHelp() {
+	// `_KDHelp` reads two hint TextBlock offsets from `_KDTextIndex`:
+	//   word @ +0xe : first-time hint
+	//   word @ +0x10: second-time hint (cycles back to first if missing)
+	// `_SawHelpHint` toggles between them.
+	if (!_mystery.isLoaded() || !_font.isLoaded())
+		return;
+
+	const byte *kd = _mystery.kdTextIndex();
+	if (!kd)
+		return;
+
+	const uint16 hintFirst  = READ_LE_UINT16(kd + 0x0e);
+	const uint16 hintSecond = READ_LE_UINT16(kd + 0x10);
+	uint16 use = _mystery._sawHelpHint && hintSecond != 0xFFFF ? hintSecond : hintFirst;
+	if (use == 0xFFFF) {
+		debugC(1, kDebugScript, "doHelp: no hint configured");
+		return;
+	}
+	if (!_mystery._sawHelpHint && hintFirst != 0xFFFF)
+		_mystery._sawHelpHint = true;
+
+	const Common::String raw = _mystery.textAt(use);
+	const Common::String text = parseString(raw, _playerName, _partner);
+
+	Graphics::ManagedSurface scratch(320, 200,
+		Graphics::PixelFormat::createFormatCLUT8());
+	scratch.clear();
+	_font.drawString(&scratch, "HELP", 8, 4, 320, 0xF);
+	_font.drawWordWrapped(&scratch, 8, 24, 304, text, 0xF);
+	g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
+							   0, 0, 320, 200);
+	g_system->updateScreen();
+	waitForInput(60000);
+}
+
+void EEMEngine::doInterfaceHelp(uint num) {
+	// Mirrors `_InterfaceHelp(num)` @ 1560:0205. The original walks
+	// `HelpData @ 29be:00c8` (5-byte entries: u8 count, then up to 2
+	// u16 picIds), `_GetPicture`s each one, blits it fullscreen via
+	// `_Rect_Move_Mask(0, 0, ...)`, and waits for click / key. ESC ends
+	// the cycle; any other input advances to the next pic.
+	//
+	// Verified from Ghidra HelpData bytes:
+	//   entry 0 (PDA / gallery HELP button): count=2, picIds = 0x0063, 0x01ae
+	//   entry 1: count=2, picIds = 0x0192, 0x01b1
+	// Only entry 0 is reachable from the PDA notebook (rect 1) and the
+	// gallery (rect 1) ??? both call `_InterfaceHelp(0)`.
+	static const uint16 kHelpPics[][2] = {
+		{ 0x0063, 0x01ae },
+		{ 0x0192, 0x01b1 },
+	};
+	if (num >= ARRAYSIZE(kHelpPics))
+		return;
+
+	debugC(1, kDebugScript, "doInterfaceHelp(%u): showing pics 0x%x, 0x%x",
+		   num, kHelpPics[num][0], kHelpPics[num][1]);
+
+	for (uint i = 0; i < 2; i++) {
+		const uint16 picId = kHelpPics[num][i];
+		Picture pic;
+		if (!_picsArchive.getPicture(picId, pic)) {
+			warning("doInterfaceHelp: getPicture(0x%x) failed", picId);
+			continue;
+		}
+		debugC(1, kDebugScript, "doInterfaceHelp: pic 0x%x = %dx%d flags=0x%x",
+			   picId, pic.surface.w, pic.surface.h, pic.flags);
+
+		// Compose a 320x200 frame (cleared) and blit the help pic at (0,0)
+		// with the original's masked-blit semantics: pixels equal to the
+		// pic's sub-mode (high byte of `pic[0]`, see `_Rect_Move_Mask`
+		// param_10 at 1000:03fc) are treated as transparent and skipped.
+		Graphics::ManagedSurface scratch(320, 200,
+			Graphics::PixelFormat::createFormatCLUT8());
+		scratch.clear();
+		const byte transp = (byte)(pic.flags >> 8);
+		const int w = MIN<int>(pic.surface.w, 320);
+		const int h = MIN<int>(pic.surface.h, 200);
+		for (int row = 0; row < h; row++) {
+			const byte *src = (const byte *)pic.surface.getBasePtr(0, row);
+			byte *dst = (byte *)scratch.getBasePtr(0, row);
+			for (int col = 0; col < w; col++) {
+				if (src[col] != transp)
+					dst[col] = src[col];
+			}
+		}
+		g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
+								   0, 0, 320, 200);
+		g_system->updateScreen();
+
+		bool escape = false;
+		while (!shouldQuit() && !escape) {
+			Common::Event ev;
+			bool advance = false;
+			while (g_system->getEventManager()->pollEvent(ev)) {
+				if (ev.type == Common::EVENT_QUIT ||
+					ev.type == Common::EVENT_RETURN_TO_LAUNCHER) {
+					return;
+				}
+				if (ev.type == Common::EVENT_LBUTTONDOWN) {
+					advance = true;
+					break;
+				}
+				if (ev.type == Common::EVENT_KEYDOWN) {
+					if (ev.kbd.keycode == Common::KEYCODE_ESCAPE)
+						escape = true;
+					else
+						advance = true;
+					break;
+				}
+			}
+			if (advance || escape)
+				break;
+			g_system->updateScreen();
+			g_system->delayMillis(15);
+		}
+		if (escape)
+			break;
+	}
+}
+
+void EEMEngine::setPartnerEraseBg(const Graphics::ManagedSurface *bg) {
+	if (bg && bg->w == 320 && bg->h == 200) {
+		_partnerEraseBg.create(320, 200,
+			Graphics::PixelFormat::createFormatCLUT8());
+		for (int row = 0; row < 200; row++) {
+			memcpy((byte *)_partnerEraseBg.getBasePtr(0, row),
+				   (const byte *)bg->getBasePtr(0, row), 320);
+		}
+	} else {
+		_partnerEraseBg.free();
+	}
+}
+
+bool EEMEngine::getBalloonInsets(uint16 bubNum, uint16 &xInset,
+								  uint16 &yInset, uint16 &textW) const {
+	// 52-entry, 10-bytes-each balloon-metadata table at `29be:0875`.
+	// Used at 1df2:0aef-0af9 (accuse hint) and `_DisplayClue` to position
+	// `_WordWrap` text inside the balloon. Only +0/+2/+4 are read here:
+	//   +0..1 = text X inset, +2..3 = Y inset, +4..5 = max wrap width
+	// (+6/+8 = balloon h / tail offset, both unused for text layout).
+	static const struct { uint16 x, y, w; } kTable[] = {
+		{ 6, 4, 142 }, { 6, 4, 142 }, { 6, 4, 142 }, { 6, 4, 142 },
+		{ 6, 4, 142 }, { 6, 4, 142 }, { 6, 4, 142 },
+		{ 6, 4, 224 }, { 6, 4, 224 }, { 6, 4, 224 }, { 6, 4, 224 },
+		{ 6, 4, 224 }, { 6, 4, 224 }, { 6, 4, 224 },
+		{ 6, 4, 291 }, { 6, 4, 291 }, { 6, 4, 291 }, { 6, 4, 291 },
+		{ 6, 4, 291 }, { 6, 4, 291 }, { 6, 4, 291 },
+		{ 5, 4, 155 }, { 5, 4, 155 }, { 5, 4, 155 }, { 5, 4, 155 },
+		{ 5, 4, 155 }, { 5, 4, 155 }, { 5, 4, 155 },
+		{ 5, 4, 237 }, { 5, 4, 237 }, { 5, 4, 237 }, { 5, 4, 237 },
+		{ 5, 4, 237 }, { 5, 4, 237 }, { 5, 4, 237 },
+		{ 3, 4, 155 }, { 3, 4, 155 }, { 3, 4, 155 }, { 3, 4, 155 },
+		{ 3, 4, 155 }, { 3, 4, 155 }, { 3, 4, 155 },
+		{ 5, 4, 238 }, { 5, 4, 238 }, { 5, 4, 238 }, { 5, 4, 238 },
+		{ 5, 4, 238 }, { 5, 4, 238 }, { 5, 4, 238 },
+		{ 5, 8, 158 }, { 5, 8, 176 }, { 8, 7, 142 }
+	};
+	const uint idx = bubNum & 0x7F;
+	if (idx >= ARRAYSIZE(kTable))
+		return false;
+	xInset = kTable[idx].x;
+	yInset = kTable[idx].y;
+	textW  = kTable[idx].w;
+	return true;
+}
+
+} // End of namespace EEM
diff --git a/engines/eem/module.mk b/engines/eem/module.mk
index 047409575c9..5583335ffd2 100644
--- a/engines/eem/module.mk
+++ b/engines/eem/module.mk
@@ -2,12 +2,15 @@ MODULE := engines/eem
 
 MODULE_OBJS = \
 	animation.o \
+	clues.o \
 	eem.o \
 	font.o \
+	graphics.o \
 	metaengine.o \
 	mystery.o \
 	resource.o \
-	site.o
+	site.o \
+	ui.o
 
 # This module can be built as a plugin
 ifeq ($(ENABLE_EEM), DYNAMIC_PLUGIN)
diff --git a/engines/eem/site.cpp b/engines/eem/site.cpp
index 8e119ab07f3..1e7f388e6eb 100644
--- a/engines/eem/site.cpp
+++ b/engines/eem/site.cpp
@@ -919,4 +919,174 @@ void SiteScreen::onHotspotClicked(uint siteNum, uint hotIdx) {
 	// Caller (`SiteScreen::run`) re-renders the site after this returns.
 }
 
+void EEMEngine::playKdAnim(uint16 num) {
+	// Mirrors `_DoKDAnim(num) @ 168d:028a` + `_PlayAnimation @ 172b:1f46`:
+	//   _SuspendAnimation(WaitHandle);
+	//   anim   = WaitAnims[1+num].anim[partner]   (table @ 29be:0228)
+	//   x      = WaitAnims[1+num].x[partner]
+	//   y      = WaitAnims[1+num].y[partner]
+	//   _PlayAnimation(anim, x, y, WaitHandle)
+	//     ??? registers a state-4 (one-shot) animation slot and lets
+	//       `_UpdateAnimations` walk the sequence script until 0x80,
+	//       then frees this slot and re-activates `WaitHandle`.
+	// Our port renders the partner's idle inline in each redraw rather
+	// than via a slot system, so we play the one-shot synchronously here
+	// (blocking) and resume normal idle rendering when the caller
+	// returns. That matches the user-visible effect: the partner's
+	// gesture (Jenny taking a picture, etc.) finishes before the
+	// speaker portrait + speech balloon appear.
+	//
+	// Six valid kdAnimNum entries (0..5). Verified bytes from
+	// `29be:0228`. Layout per entry: { animJake, animJenny, xJake,
+	// xJenny, yJake, yJenny }. Position is (6, 80) in every entry.
+	static const uint16 kKdAnimTable[6][6] = {
+		{ 0x03, 0x0c, 6, 6, 80, 80 }, // 0 ??? speaker idx 1 wait anim
+		{ 0x01, 0x0b, 6, 6, 80, 80 }, // 1 ??? same as PDA idle
+		{ 0x04, 0x0d, 6, 6, 80, 80 }, // 2
+		{ 0x02, 0x10, 6, 6, 80, 80 }, // 3 ??? same as gallery
+		{ 0x05, 0x05, 6, 6, 80, 80 }, // 4 ??? same anim both partners
+		{ 0x06, 0x06, 6, 6, 80, 80 }, // 5 ??? same anim both partners
+	};
+	if (num >= ARRAYSIZE(kKdAnimTable))
+		return;
+
+	const uint partner = (_partner == 0) ? 0 : 1;
+	const uint16 animId = kKdAnimTable[num][partner];
+	const int    px     = (int)kKdAnimTable[num][2 + partner];
+	const int    py     = (int)kKdAnimTable[num][4 + partner];
+
+	Animation anim;
+	if (!_aniArchive.loadAnimation(animId, anim) || anim.empty()) {
+		warning("playKdAnim(%u): anim %u failed to load", num, animId);
+		return;
+	}
+
+	// Sequence-script lookup. Entries copied verbatim from
+	// `_AnimationSequences @ 29be:22d4` walked through to the next 0x80.
+	// Each script is a u16[] of frame indices terminated by 0x80; we
+	// don't yet handle 0x81 jumps (none of the kdAnim sequences use
+	// them ??? verified). seqnum == animId for these calls (per
+	// `_PlayAnimation` 172b:1f5d push order).
+	struct Script {
+		uint16 seqnum;
+		uint8 len;
+		uint8 frames[20];  // long enough for any kdAnim script
+	};
+	static const Script kScripts[] = {
+		// seqnum 1 (29be:188a) ??? head bob
+		{ 0x01, 15, { 0,1,2,0,1,0,2,1,0,1,0,1,2,1,0 } },
+		// seqnum 2 (29be:18aa) ??? short blip then long pause
+		{ 0x02, 16, { 0,1,2,1,0,0,0,0,0,0,0,0,0,0,0,0 } },
+		// seqnum 3 (29be:18e0) ??? Jake "lift, hold, lower" gesture
+		{ 0x03,  9, { 0,1,2,3,2,2,2,1,0 } },
+		// seqnum 4 (29be:18f4) ??? bigger gesture (camera flash-style)
+		{ 0x04, 13, { 0,1,2,3,4,5,4,4,4,3,2,1,0 } },
+		// seqnum 5 (29be:1910) ??? held idle with a single peak
+		{ 0x05, 13, { 0,0,0,1,2,3,2,1,0,0,0,0,0 } },
+		// seqnum 6 (29be:192c) ??? empty (immediate END)
+		{ 0x06,  0, { 0 } },
+		// seqnum 0xb (29be:188a, same as 1) ??? Jenny PDA idle
+		{ 0x0b, 15, { 0,1,2,0,1,0,2,1,0,1,0,1,2,1,0 } },
+		// seqnum 0xc (29be:18e0, same as 3) ??? Jenny "take a picture"
+		{ 0x0c,  9, { 0,1,2,3,2,2,2,1,0 } },
+		// seqnum 0xd (29be:18f4, same as 4) ??? Jenny big gesture
+		{ 0x0d, 13, { 0,1,2,3,4,5,4,4,4,3,2,1,0 } },
+		// seqnum 0x10 (29be:1956) ??? Jenny short anim
+		{ 0x10,  9, { 0,0,0,1,0,0,0,0,0 } },
+	};
+	const uint8 *frames = nullptr;
+	uint frameCount = 0;
+	for (uint i = 0; i < ARRAYSIZE(kScripts); i++) {
+		if (kScripts[i].seqnum == animId) {
+			frames = kScripts[i].frames;
+			frameCount = kScripts[i].len;
+			break;
+		}
+	}
+	if (frameCount == 0) {
+		// Fallback: linear playback through anim cells (better than
+		// nothing if a future kdAnim references an unscripted anim).
+		frameCount = (uint)anim.size();
+	}
+
+	// Erase-source for between-frame redraw. Prefer the partner-less
+	// backdrop the caller stashed via `setPartnerEraseBg` (e.g. the
+	// site's `_bgSnapshot`, which has the static drops + frame but no
+	// partner sprite). Without that, fall back to whatever's currently
+	// on screen ??? which works for full-screen contexts (PDA / accuse /
+	// briefing) where there is no separate idle partner overlay to
+	// erase, but produces visible "ghosting" against the site's idle
+	// partner cell at (6, 80) because it has the resting pose baked in.
+	Graphics::ManagedSurface bg(320, 200,
+		Graphics::PixelFormat::createFormatCLUT8());
+	if (_partnerEraseBg.w == 320 && _partnerEraseBg.h == 200) {
+		for (int row = 0; row < 200; row++) {
+			memcpy((byte *)bg.getBasePtr(0, row),
+				   (const byte *)_partnerEraseBg.getBasePtr(0, row), 320);
+		}
+	} else {
+		Graphics::Surface *screen = g_system->lockScreen();
+		if (!screen)
+			return;
+		for (int row = 0; row < 200; row++) {
+			memcpy((byte *)bg.getBasePtr(0, row),
+				   (const byte *)screen->getBasePtr(0, row), 320);
+		}
+		g_system->unlockScreen();
+	}
+
+	for (uint i = 0; i < frameCount && !shouldQuit(); i++) {
+		const uint frameIdx = frames ? (uint)frames[i] : i;
+		if (frameIdx >= anim.size())
+			continue;
+		const Picture &fr = anim[frameIdx];
+		const byte transp = (byte)(fr.flags >> 8);
+
+		// Restore BG, then masked-blit the next frame.
+		Graphics::ManagedSurface scratch(320, 200,
+			Graphics::PixelFormat::createFormatCLUT8());
+		for (int row = 0; row < 200; row++) {
+			memcpy((byte *)scratch.getBasePtr(0, row),
+				   (const byte *)bg.getBasePtr(0, row), 320);
+		}
+		const int w = MIN<int>(fr.surface.w, 320 - px);
+		const int h = MIN<int>(fr.surface.h, 200 - py);
+		for (int row = 0; row < h; row++) {
+			const int dstY = py + row;
+			if (dstY < 0) continue;
+			const byte *src = (const byte *)fr.surface.getBasePtr(0, row);
+			byte *dst = (byte *)scratch.getBasePtr(0, dstY);
+			for (int col = 0; col < w; col++) {
+				const int dstX = px + col;
+				if (dstX < 0) continue;
+				if (src[col] != transp)
+					dst[dstX] = src[col];
+			}
+		}
+		g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
+								   0, 0, 320, 200);
+		g_system->updateScreen();
+
+		// One frame per `_CheckFrameRate` tick. The original calibrates
+		// this to ~10 fps; 100 ms matches what the rest of the engine
+		// uses for partner / NPC frame cycling.
+		const uint32 wakeup = g_system->getMillis() + 100;
+		while (g_system->getMillis() < wakeup && !shouldQuit()) {
+			Common::Event ev;
+			while (g_system->getEventManager()->pollEvent(ev)) {
+				// Drain events but don't allow skipping mid-animation ???
+				// the speaker portrait + balloon haven't been drawn yet
+				// and a click would otherwise eat the upcoming clue.
+				if (ev.type == Common::EVENT_QUIT ||
+					ev.type == Common::EVENT_RETURN_TO_LAUNCHER)
+					return;
+			}
+			g_system->delayMillis(10);
+		}
+	}
+
+	// Restore BG so the next caller (speaker portrait blit) starts clean.
+	g_system->copyRectToScreen(bg.getPixels(), bg.pitch, 0, 0, 320, 200);
+	g_system->updateScreen();
+}
 } // End of namespace EEM
diff --git a/engines/eem/ui.cpp b/engines/eem/ui.cpp
new file mode 100644
index 00000000000..03bd77f609d
--- /dev/null
+++ b/engines/eem/ui.cpp
@@ -0,0 +1,2222 @@
+/* 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/debug.h"
+#include "common/events.h"
+#include "common/file.h"
+#include "common/path.h"
+#include "common/savefile.h"
+#include "common/system.h"
+#include "common/textconsole.h"
+
+#include "graphics/cursorman.h"
+#include "graphics/managed_surface.h"
+
+#include "eem/detection.h"
+#include "eem/eem.h"
+
+// EEM ??? UI screens (NOTE.C, GALLERY.C, ACCUSE.C, MAP.C, CHOOSE.C combined).
+// Each function is a self-contained modal `EEMEngine::doX()` reachable from
+// the site loop. They share the same wait-for-input idiom and PIC 0x3f /
+// 0x41 / 0x42 / 0x43 backdrops.
+
+namespace EEM {
+
+void EEMEngine::doNewPlayer() {
+	// Mirrors `_NewPlayer` @ 1c33:0dda. The original draws background
+	// 0x104 + character peek pic 0x107, then shows "Please type your
+	// name" and accepts up to 12 characters until Enter. We render a
+	// minimal version: black screen + prompt.
+	if (!_font.isLoaded()) {
+		_playerName = "Detective";
+		return;
+	}
+
+	Common::String name;
+	const int maxChars = 12;
+
+	// Mirror the original: load PIC 0x104 as the name-entry backdrop.
+	// The original also slides in PIC 0x107 (a peeking character).
+	Picture bg;
+	const bool haveBG = _picsArchive.getPicture(0x104, bg);
+
+	auto draw = [&]() {
+		Graphics::ManagedSurface scratch(320, 200,
+			Graphics::PixelFormat::createFormatCLUT8());
+		scratch.clear();
+		if (haveBG) {
+			const int w = MIN<int>(bg.surface.w, 320);
+			const int h = MIN<int>(bg.surface.h, 200);
+			for (int row = 0; row < h; row++)
+				memcpy((byte *)scratch.getBasePtr(0, row),
+					   (const byte *)bg.surface.getBasePtr(0, row), w);
+		}
+		// Match the original `_NewPlayer`: `_Show_String(rw=0x28, cl=0x50)`
+		// for the prompt, then `_ShowChar(0x50, x, ???)` for typed input.
+		// (rw=row=y, cl=col=x.) Prompt at (y=40, x=80), input at (y=80, x=80).
+		_font.drawString(&scratch, "Please type your name:", 80, 40, 240, 0xF);
+		Common::String shown = name + "_";
+		_font.drawString(&scratch, shown, 80, 80, 240, 0xF);
+		g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
+								   0, 0, 320, 200);
+		g_system->updateScreen();
+	};
+	draw();
+
+	while (!shouldQuit()) {
+		Common::Event ev;
+		bool dirty = false;
+		while (g_system->getEventManager()->pollEvent(ev)) {
+			if (ev.type == Common::EVENT_QUIT ||
+				ev.type == Common::EVENT_RETURN_TO_LAUNCHER)
+				return;
+			if (ev.type != Common::EVENT_KEYDOWN)
+				continue;
+			const Common::KeyCode k = ev.kbd.keycode;
+			if (k == Common::KEYCODE_RETURN) {
+				if (name.empty())
+					name = "Detective";
+				_playerName = name;
+				return;
+			}
+			if (k == Common::KEYCODE_ESCAPE) {
+				_playerName = "Detective";
+				return;
+			}
+			if (k == Common::KEYCODE_BACKSPACE) {
+				if (!name.empty()) {
+					name.deleteLastChar();
+					dirty = true;
+				}
+				continue;
+			}
+			if (ev.kbd.ascii >= ' ' && ev.kbd.ascii < 127 &&
+				(int)name.size() < maxChars) {
+				name += (char)ev.kbd.ascii;
+				dirty = true;
+			}
+		}
+		if (dirty)
+			draw();
+		g_system->updateScreen();
+		g_system->delayMillis(15);
+	}
+}
+
+void EEMEngine::doCaseSelection() {
+	// Mirrors `_CaseSelection` @ 1c33:0a87. The original draws PIC 0x41
+	// (chooser background) plus a centred "Book %d" / "Challenge Book"
+	// header at (y=12) and then calls `_DoChoose(list)` to render the
+	// menu via `DrawList` @ 1c33:040d at (_TextBox+3, DAT_29be_0d02) =
+	// (61, 35), 12 rows ?? 10 px line height. The menu list itself is
+	// the static array at 29be:0d6a (verified via `push 0x0d6a` at
+	// 1c33:1ab4). Strings are at 29be:0ef4 onwards. Layout:
+	//   list[0]  = "----------------------------------"
+	//   list[1]  = "         Choose A Mystery"
+	//   list[2..10] = alternating menu items + separators
+	// Five selectable items: Choose A Mystery / Practice Mystery /
+	// See ScrapBook 1/2/3.
+	const uint kMaxMystery = 54;
+
+	enum MenuPick {
+		kPickChoose = 0,
+		kPickPractice,
+		kPickScrap1,
+		kPickScrap2,
+		kPickScrap3,
+		kNumPicks
+	};
+	const char *kPickLabel[kNumPicks] = {
+		"         Choose A Mystery",
+		"         Practice Mystery",
+		"         See ScrapBook 1",
+		"         See ScrapBook 2",
+		"         See ScrapBook 3"
+	};
+	// ScrapBooks aren't implemented yet ??? grey them so the player can't
+	// stop on them, mirroring the original `_Greys` mask.
+	const bool kPickEnabled[kNumPicks] = { true, true, false, false, false };
+	uint pick = kPickChoose;
+
+	const char *kSeparator = "----------------------------------";
+
+	// Click rectangles from the original `_DoChoose` @ 1c33:0514 ??? each
+	// `_InRect(_MouseX, _MouseY, addr, 0x29be)` reads one 4??u16 rect at
+	// the listed offset in segment 29be ({x1, y1, x2, y2}). We use
+	// `Common::Rect` (left/top/right/bottom) which also gives us
+	// `contains(x, y)` for hit testing.
+	const Common::Rect kOkRect      ( 12,  63,  41,  87); // 29be:0cd8 confirm
+	const Common::Rect kHelpRect    ( 12, 100,  41, 124); // 29be:0ce0 help
+	const Common::Rect kExitRect    ( 12, 137,  41, 161); // 29be:0ce8 cancel
+	const Common::Rect kUpArrowRect (240,  31, 250,  43); // 29be:0cf0 scroll up
+	const Common::Rect kDnArrowRect (240, 148, 250, 159); // 29be:0cf8 scroll dn
+	const Common::Rect kListRect    ( 58,  35, 238, 158); // 29be:0d00 list panel
+
+	// The original `_NewPlayer` set `_MouseCursor = 1` on exit; the
+	// chain of screens after it expects the cursor to stay visible.
+	// Reassert here in case anything between hid it.
+	CursorMan.showMouse(true);
+
+	// Mirrors `_CaseSelection`: load PIC 0x41 as the chooser backdrop.
+	Picture caseBg;
+	const bool haveCaseBg = _picsArchive.getPicture(0x41, caseBg);
+
+	// KD greeter sprite. `_CaseSelection @ 1c33:0a87` (1c33:0b7e-0ba1)
+	// loads anim 0x15 (Jake-paired) or 0x16 (Jenny-paired) and registers
+	// `_NewAnimation(0x112, 0x50, ..., seqnum=0x15, prior=1)` ??? partner-
+	// dependent because the host KD changes who's "with him" on the
+	// briefing intro frame. Runs continuously through the menu loop via
+	// `_UpdateAnimations`. We approximate with millis-based frame cycling.
+	const uint kKdAniId = (_partner == 0) ? 0x15 : 0x16;
+	Animation kdAnim;
+	const bool haveKdAnim = _aniArchive.loadAnimation(kKdAniId, kdAnim)
+							 && !kdAnim.empty();
+	const int kKdAnimX = 0x112;
+	const int kKdAnimY = 0x50;
+
+	auto draw = [&]() {
+		Graphics::ManagedSurface scratch(320, 200,
+			Graphics::PixelFormat::createFormatCLUT8());
+		scratch.clear();
+		if (haveCaseBg) {
+			const int w = MIN<int>(caseBg.surface.w, 320);
+			const int h = MIN<int>(caseBg.surface.h, 200);
+			for (int row = 0; row < h; row++) {
+				memcpy((byte *)scratch.getBasePtr(0, row),
+					   (const byte *)caseBg.surface.getBasePtr(0, row), w);
+			}
+		}
+
+		// KD greeter frame ??? masked-blit current animation cell at
+		// (0x112, 0x50). 100 ms tick matches the engine's `_CheckFrameRate`.
+		if (haveKdAnim) {
+			const uint32 now = g_system->getMillis();
+			const uint frameIdx = (uint)((now / 100) % kdAnim.size());
+			const Picture &fr = kdAnim[frameIdx];
+			const byte transp = (byte)(fr.flags >> 8);
+			for (int row = 0; row < fr.surface.h; row++) {
+				const int dstY = kKdAnimY + row;
+				if (dstY < 0 || dstY >= 200) continue;
+				const byte *src = (const byte *)fr.surface.getBasePtr(0, row);
+				byte *dst = (byte *)scratch.getBasePtr(0, dstY);
+				for (int col = 0; col < fr.surface.w; col++) {
+					const int dstX = kKdAnimX + col;
+					if (dstX < 0 || dstX >= 320) continue;
+					if (src[col] != transp)
+						dst[dstX] = src[col];
+				}
+			}
+		}
+		if (_font.isLoaded()) {
+			// `DrawList` @ 1c33:040d coordinates: `_TextBox + 3` for x
+			// and `DAT_29be_0d02` for y. `_TextBox` @ 29be:0d00 holds
+			// {x=58, y=35, x2=238, y2=158}. Matches the blue panel.
+			const int kListX  = 58 + 3;
+			const int kListW  = 238 - kListX;
+			const int kListY0 = 35;
+			const int kLineH  = 10;
+
+			// Top centred "Book %d" / "Challenge Book" title ??? sprintf
+			// format strings at 29be:0deb / 29be:0dfa shown via
+			// `_Show_String(0xc, (0xba - width)/2 + 0x3c, ???)` in the
+			// original. We don't track challenge tier yet so always
+			// show "Book 1".
+			const Common::String book = "Book 1";
+			const int titleW = _font.getStringWidth(book);
+			const int titleX = (0xba - titleW) / 2 + 0x3c;
+			_font.drawString(&scratch, book, titleX, 12, 320, 0xF);
+
+			// Render 11 list rows: separator + menu item pairs.
+			//   row 0  separator
+			//   row 1  Choose A Mystery
+			//   row 2  separator
+			//   row 3  Practice Mystery
+			//   ...
+			//   row 9  See ScrapBook 3
+			//   row 10 separator
+			for (int r = 0; r < 11; r++) {
+				const int y = kListY0 + r * kLineH;
+				if ((r & 1) == 0) {
+					_font.drawString(&scratch, kSeparator, kListX, y, kListW, 0x7);
+					continue;
+				}
+				const uint mp = (uint)(r >> 1);
+				const bool isSel  = (mp == pick);
+				const byte color  = isSel        ? 0xF :
+									kPickEnabled[mp] ? 0x7 : 0x8;
+				_font.drawString(&scratch, kPickLabel[mp], kListX, y, kListW, color);
+			}
+		}
+		g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
+								   0, 0, 320, 200);
+		g_system->updateScreen();
+	};
+
+	auto pickPrev = [&]() {
+		for (int i = 0; i < (int)kNumPicks; i++) {
+			pick = (pick == 0) ? (uint)(kNumPicks - 1) : pick - 1;
+			if (kPickEnabled[pick])
+				break;
+		}
+	};
+	auto pickNext = [&]() {
+		for (int i = 0; i < (int)kNumPicks; i++) {
+			pick = (pick + 1) % kNumPicks;
+			if (kPickEnabled[pick])
+				break;
+		}
+	};
+
+	draw();
+	uint32 lastTick = g_system->getMillis();
+
+	bool exitChosen = false;
+	while (!shouldQuit()) {
+		Common::Event ev;
+		bool confirmed = false;
+		// Redraw every 100 ms so the KD greeter cycles. Mirrors the
+		// `_CheckFrameRate` cadence in `_CaseSelection`'s main loop.
+		const uint32 now = g_system->getMillis();
+		if (haveKdAnim && now - lastTick >= 100) {
+			lastTick = now;
+			draw();
+		}
+		while (g_system->getEventManager()->pollEvent(ev)) {
+			if (ev.type == Common::EVENT_QUIT ||
+				ev.type == Common::EVENT_RETURN_TO_LAUNCHER)
+				return;
+			if (ev.type == Common::EVENT_LBUTTONDOWN) {
+				// OK / EXIT / HELP buttons (rectangles from `_DoChoose`).
+				if (kOkRect.contains(ev.mouse.x, ev.mouse.y)) {
+					confirmed = true;
+					break;
+				}
+				if (kExitRect.contains(ev.mouse.x, ev.mouse.y)) {
+					exitChosen = true;
+					confirmed = true;
+					break;
+				}
+				if (kHelpRect.contains(ev.mouse.x, ev.mouse.y)) {
+					// HELP placeholder ??? original calls `_DisplayHint`;
+					// our help screen is wired to `H` later in the flow.
+					continue;
+				}
+				// List panel: click on a non-separator row selects the
+				// menu entry under the cursor.
+				if (kListRect.contains(ev.mouse.x, ev.mouse.y)) {
+					const int kLineH = 10;
+					const int row = (ev.mouse.y - kListRect.top) / kLineH;
+					if ((row & 1) == 1) {
+						const uint mp = (uint)(row >> 1);
+						if (mp < kNumPicks && kPickEnabled[mp]) {
+							pick = mp;
+							draw();
+							continue;
+						}
+					}
+				}
+			}
+			if (ev.type != Common::EVENT_KEYDOWN)
+				continue;
+			const Common::KeyCode k = ev.kbd.keycode;
+			if (k == Common::KEYCODE_ESCAPE) {
+				exitChosen = true;
+				confirmed = true;
+				break;
+			}
+			if (k == Common::KEYCODE_RETURN) {
+				confirmed = true;
+				break;
+			}
+			if (k == Common::KEYCODE_UP || k == Common::KEYCODE_LEFT) {
+				pickPrev();
+				draw();
+				continue;
+			}
+			if (k == Common::KEYCODE_DOWN || k == Common::KEYCODE_RIGHT ||
+				k == Common::KEYCODE_TAB) {
+				pickNext();
+				draw();
+				continue;
+			}
+		}
+		if (confirmed) {
+			draw();
+			break;
+		}
+		g_system->updateScreen();
+		g_system->delayMillis(15);
+	}
+
+	if (shouldQuit())
+		return;
+
+	if (exitChosen) {
+		_mystery.clear();
+		_nextScreen = kScreenInvalid;
+		return;
+	}
+
+	// "Practice Mystery" is the tutorial ??? mystery 0.
+	if (pick == kPickPractice) {
+		if (!_mystery.load(0, &_rng)) {
+			warning("doCaseSelection: failed to load practice mystery");
+			_mystery.clear();
+		}
+		return;
+	}
+
+	if (pick != kPickChoose) {
+		// ScrapBooks aren't implemented; bail back to the menu loop.
+		_mystery.clear();
+		return;
+	}
+
+	// "Choose A Mystery" sub-screen: pick a specific case from the
+	// 55-mystery roster. The original opens a different list here;
+	// we approximate with the tier-aware numeric chooser we used
+	// before. Default to the first unsolved mystery.
+	uint sel = 0;
+	for (uint i = 0; i <= kMaxMystery; i++) {
+		if (i < sizeof(_mysteriesSolved) && !_mysteriesSolved[i]) {
+			sel = i;
+			break;
+		}
+	}
+
+	auto drawSubmenu = [&]() {
+		Graphics::ManagedSurface scratch(320, 200,
+			Graphics::PixelFormat::createFormatCLUT8());
+		scratch.clear();
+		if (haveCaseBg) {
+			const int w = MIN<int>(caseBg.surface.w, 320);
+			const int h = MIN<int>(caseBg.surface.h, 200);
+			for (int row = 0; row < h; row++)
+				memcpy((byte *)scratch.getBasePtr(0, row),
+					   (const byte *)caseBg.surface.getBasePtr(0, row), w);
+		}
+		if (_font.isLoaded()) {
+			const int kListX  = 61;
+			const int kListW  = 238 - kListX;
+			const int kListY0 = 35;
+			const int kLineH  = 10;
+			const int kVisible = 12;
+			int top = (int)sel - kVisible / 2;
+			if (top < 0) top = 0;
+			if (top + kVisible > (int)kMaxMystery + 1)
+				top = (int)kMaxMystery + 1 - kVisible;
+			for (int r = 0; r < kVisible; r++) {
+				const int idx = top + r;
+				if (idx > (int)kMaxMystery)
+					break;
+				char marker = ' ';
+				if ((uint)idx < sizeof(_mysteriesSolved)) {
+					if (_mysteriesSolved[idx] == 2) marker = '*';
+					else if (_mysteriesSolved[idx] == 1) marker = '+';
+				}
+				const char arrow = ((uint)idx == sel) ? '>' : ' ';
+				_font.drawString(&scratch,
+								 Common::String::format("%c %c Mystery %d", arrow, marker, idx),
+								 kListX, kListY0 + r * kLineH, kListW, 0xF);
+			}
+		}
+		g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
+								   0, 0, 320, 200);
+		g_system->updateScreen();
+	};
+
+	drawSubmenu();
+	bool confirmed = false;
+	while (!confirmed && !shouldQuit()) {
+		Common::Event ev;
+		while (g_system->getEventManager()->pollEvent(ev)) {
+			if (ev.type == Common::EVENT_QUIT ||
+				ev.type == Common::EVENT_RETURN_TO_LAUNCHER)
+				return;
+			if (ev.type == Common::EVENT_LBUTTONDOWN) {
+				// Same `_DoChoose` rectangles as the top-level menu.
+				if (kOkRect.contains(ev.mouse.x, ev.mouse.y)) {
+					confirmed = true;
+					break;
+				}
+				if (kExitRect.contains(ev.mouse.x, ev.mouse.y)) {
+					_mystery.clear();
+					return;
+				}
+				if (kUpArrowRect.contains(ev.mouse.x, ev.mouse.y)) {
+					sel = (sel == 0) ? kMaxMystery : sel - 1;
+					drawSubmenu();
+					continue;
+				}
+				if (kDnArrowRect.contains(ev.mouse.x, ev.mouse.y)) {
+					sel = (sel >= kMaxMystery) ? 0 : sel + 1;
+					drawSubmenu();
+					continue;
+				}
+				if (kListRect.contains(ev.mouse.x, ev.mouse.y)) {
+					// Pick the row under the cursor.
+					const int kLineH = 10;
+					const int kVisible = 12;
+					int top = (int)sel - kVisible / 2;
+					if (top < 0) top = 0;
+					if (top + kVisible > (int)kMaxMystery + 1)
+						top = (int)kMaxMystery + 1 - kVisible;
+					const int row = (ev.mouse.y - kListRect.top) / kLineH;
+					const int idx = top + row;
+					if (idx >= 0 && idx <= (int)kMaxMystery) {
+						sel = (uint)idx;
+						drawSubmenu();
+					}
+					continue;
+				}
+			}
+			if (ev.type != Common::EVENT_KEYDOWN)
+				continue;
+			const Common::KeyCode k = ev.kbd.keycode;
+			if (k == Common::KEYCODE_ESCAPE) {
+				_mystery.clear();
+				return;
+			}
+			if (k == Common::KEYCODE_RETURN) {
+				confirmed = true;
+				break;
+			}
+			if (k >= Common::KEYCODE_0 && k <= Common::KEYCODE_9) {
+				sel = (uint)(k - Common::KEYCODE_0);
+				drawSubmenu();
+				continue;
+			}
+			if (k == Common::KEYCODE_DOWN || k == Common::KEYCODE_TAB) {
+				sel = (sel >= kMaxMystery) ? 0 : sel + 1;
+				drawSubmenu();
+				continue;
+			}
+			if (k == Common::KEYCODE_UP) {
+				sel = (sel == 0) ? kMaxMystery : sel - 1;
+				drawSubmenu();
+				continue;
+			}
+			if (k == Common::KEYCODE_PAGEDOWN) {
+				sel = (sel + 10 > kMaxMystery) ? kMaxMystery : sel + 10;
+				drawSubmenu();
+				continue;
+			}
+			if (k == Common::KEYCODE_PAGEUP) {
+				sel = (sel < 10) ? 0 : sel - 10;
+				drawSubmenu();
+				continue;
+			}
+			if (k == Common::KEYCODE_HOME) { sel = 0; drawSubmenu(); continue; }
+			if (k == Common::KEYCODE_END)  { sel = kMaxMystery; drawSubmenu(); continue; }
+		}
+		g_system->updateScreen();
+		g_system->delayMillis(15);
+	}
+
+	if (!_mystery.load(sel, &_rng)) {
+		warning("doCaseSelection: failed to load mystery %u", sel);
+		_mystery.clear();
+		return;
+	}
+	debugC(1, kDebugMystery, "Mystery %u loaded; %u sites, %u suspects",
+		   sel, _mystery.numSites(), _mystery.numSuspects());
+}
+
+void EEMEngine::doNotebook() {
+	// Mirrors `_DoNotebook @ 161e:0500` + `_DrawNotes @ 161e:01d0` +
+	// `_HandleNoteButton @ 161e:03cb`.
+	//
+	// Layout (verified from Ghidra labels in 29be:013f / 29be:0147):
+	//   _NotebookRect = (78, 12, 288, 152)   ??? note display rectangle.
+	//   _NoteButtons (11 entries, 8 bytes each, at 29be:0147):
+	//     [0]  (134, 174, 155, 190)  decorative ??? `_HandleNoteButton(0)`
+	//                                returns immediately (i-1 unsigned > 9).
+	//     [1]  (93,  174, 115, 190)  ??? `_InterfaceHelp(0)` (handler 0x3f9)
+	//     [2]  (157, 174, 178, 190)  ??? handler 0x477   (page nav)
+	//     [3]  (5,   80,  44, 110)   ??? `_KDHelp` (host hint, 0x403)
+	//     [4]  (180, 174, 201, 190)  ??? solve / accuse  (0x436)
+	//     [5]  (204, 174, 224, 190)  ??? `_NextScreen = 5` (gallery, 0x489)
+	//     [6]  (226, 174, 247, 190)  ??? handler 0x4ab
+	//     [7]  (7,   177,  57, 200)  ??? handler 0x480   (back to map)
+	//     [8]  (35,  111,  56, 136)  ??? `_NextScreen = 3` (site)
+	//     [9]  (0, 0, 0, 0)          ??? same exit as [8]
+	//     [10] (66,  79, 267, 174)   ??? `_InterfaceHelp(0)` (note area)
+	//   Background: PIC 0x3f.
+	//   Partner anim: anim 1 (Jake) / 0xb (Jenny) at (5, 80).
+	if (!_mystery.isLoaded() || !_font.isLoaded())
+		return;
+
+	// Button rects from `_NoteButtons @ 29be:0147` matched to handler
+	// addresses via the jump table at `_HandleNoteButton + 0xec` (i.e.
+	// 161e:04ec). Decoded handlers (i = rect_index, dispatch = handler[i-1]):
+	//   rect 0 (134,155) ??? no handler (i-1 underflows; original treats
+	//                      this as a decorative/no-op slot)
+	//   rect 1 (93,115)  ??? 0x03f9 = `_InterfaceHelp(0)`           (HELP)
+	//   rect 2 (157,178) ??? 0x0477 = `_NextScreen = 5`             (GALLERY)
+	//   rect 3 (5,80)    ??? 0x0403 = `_KDHelp`                     (host hint)
+	//   rect 4 (180,201) ??? 0x0436 = `_SolvedCheck` -> NextScreen=7 (SOLVE)
+	//   rect 5 (204,224) ??? 0x0489 = `_EraseNotes` + `_DrawNotes`  (PAGE NEXT)
+	//   rect 6 (226,247) ??? 0x04ab = decrement CurrentPage + redraw (PAGE PREV)
+	//   rect 7 (7,177)   ??? 0x0480 = `_NextScreen = 2`             (MAP)
+	//   rect 8 (35,111)  ??? 0x03ed = `_NextScreen = 3`             (SITE)
+	//   rect 9 (0,0)     ??? 0x03ed = same as rect 8
+	//   rect 10 (66,79)  ??? 0x03f9 = `_InterfaceHelp(0)`           (note-area help)
+	const Common::Rect kNotebookRect(78, 12, 288, 152);
+	const Common::Rect kBtnHelp1   ( 93, 174, 115, 190);  // [1] HELP
+	const Common::Rect kBtnGallery (157, 174, 178, 190);  // [2] GALLERY
+	const Common::Rect kBtnPartner (  5,  80,  44, 110);  // [3] KD HELP
+	const Common::Rect kBtnAccuse  (180, 174, 201, 190);  // [4] SOLVE
+	const Common::Rect kBtnPageNext(204, 174, 224, 190);  // [5] PAGE NEXT
+	const Common::Rect kBtnPagePrev(226, 174, 247, 190);  // [6] PAGE PREV
+	const Common::Rect kBtnMap     (  7, 177,  57, 200);  // [7] MAP
+	const Common::Rect kBtnSite    ( 35, 111,  56, 136);  // [8] SITE
+	const Common::Rect kNoteArea   ( 66,  79, 267, 174);  // [10] note area
+
+	CursorMan.showMouse(true);
+
+	int page = 0;
+	int hoveredNoteSlot = -1;
+
+	// Build a list of found-clue indices, identical ordering to the
+	// original's iteration through `_CluesFound[]`.
+	auto buildFound = [&]() {
+		Common::Array<uint> found;
+		for (uint i = 0; i < Mystery::kCluesFoundCap; i++)
+			if (_mystery._cluesFound[i])
+				found.push_back(i);
+		return found;
+	};
+
+	auto draw = [&]() {
+		Graphics::ManagedSurface scratch(320, 200,
+			Graphics::PixelFormat::createFormatCLUT8());
+		scratch.clear();
+
+		// PIC 0x3f frame.
+		Picture frame;
+		if (_picsArchive.getPicture(0x3f, frame)) {
+			const int w = MIN<int>(frame.surface.w, 320);
+			const int h = MIN<int>(frame.surface.h, 200);
+			for (int row = 0; row < h; row++)
+				memcpy((byte *)scratch.getBasePtr(0, row),
+					   (const byte *)frame.surface.getBasePtr(0, row), w);
+		}
+
+		// Partner sprite at (5, 80). Anim 1 for Jake, 0xb (11) for Jenny.
+		const uint partnerAnim = (_partner == 0) ? 1 : 0xb;
+		Animation partnerAni;
+		if (_aniArchive.loadAnimation(partnerAnim, partnerAni) && !partnerAni.empty()) {
+			const uint32 now = g_system->getMillis();
+			const uint frameIdx = (uint)((now / 100) % partnerAni.size());
+			const Picture &fr = partnerAni[frameIdx];
+			const byte transp = (byte)(fr.flags >> 8);
+			for (int row = 0; row < fr.surface.h; row++) {
+				const int dstY = 80 + row;
+				if (dstY < 0 || dstY >= 200) continue;
+				const byte *src = (const byte *)fr.surface.getBasePtr(0, row);
+				byte *dst = (byte *)scratch.getBasePtr(0, dstY);
+				for (int col = 0; col < fr.surface.w; col++) {
+					const int dstX = 5 + col;
+					if (dstX < 0 || dstX >= 320) continue;
+					if (src[col] != transp)
+						dst[dstX] = src[col];
+				}
+			}
+		}
+
+		// Notes ??? `_DrawNotes` walks `_NoteIndex` for the current page,
+		// rendering each found clue's text inside `_NotebookRect` with
+		// word-wrap. Selected clues are highlighted (color 0x3c in the
+		// original's case-briefing palette).
+		const Common::Array<uint> found = buildFound();
+		const byte *ni = _mystery.noteIndex();
+		const uint16 niCount = _mystery.noteIndexCount();
+
+		const int kRectX = kNotebookRect.left;
+		const int kRectY = kNotebookRect.top;
+		const int kRectW = kNotebookRect.width();
+		const int kRectH = kNotebookRect.height();
+
+		// Walk forward to the start clue of the current page.
+		// Each page renders as many clues as fit in `kRectH`.
+		int clueCursor = 0;
+		Common::Array<int> pageStarts;
+		pageStarts.push_back(0);
+		{
+			const int lineH = _font.getFontHeight() + 1;
+			int y = kRectY;
+			while (clueCursor < (int)found.size()) {
+				const uint clueId = found[clueCursor];
+				Common::String txt;
+				if (ni && clueId < niCount) {
+					const uint16 textOff = READ_LE_UINT16(ni + clueId * 4);
+					txt = parseString(_mystery.textAt(textOff),
+									  _playerName, _partner);
+				}
+				// Measure height by wrapping the text without drawing.
+				Common::Array<Common::String> wrapped;
+				_font.wordWrapText(txt, kRectW, wrapped);
+				const int h = (int)wrapped.size() * lineH;
+				if (y + h + 7 > kRectY + kRectH) {
+					// Page break before this clue.
+					y = kRectY;
+					pageStarts.push_back(clueCursor);
+				}
+				y += h + 7;
+				clueCursor++;
+			}
+			if (page >= (int)pageStarts.size())
+				page = (int)pageStarts.size() - 1;
+			if (page < 0)
+				page = 0;
+		}
+
+		// Track per-slot rectangles so the click handler can map a
+		// click in `kNoteArea` back to a clue index.
+		Common::Array<Common::Rect> slotRects;
+		Common::Array<uint> slotClues;
+
+		const int startClue = (page < (int)pageStarts.size())
+								? pageStarts[page] : 0;
+		const int endClue   = (page + 1 < (int)pageStarts.size())
+								? pageStarts[page + 1] : (int)found.size();
+
+		int y = kRectY;
+		for (int i = startClue; i < endClue; i++) {
+			const uint clueId = found[i];
+			Common::String txt;
+			if (ni && clueId < niCount) {
+				const uint16 textOff = READ_LE_UINT16(ni + clueId * 4);
+				txt = parseString(_mystery.textAt(textOff),
+								  _playerName, _partner);
+			}
+			if (txt.empty())
+				txt = Common::String::format("clue %u", clueId);
+			// Per `_DrawNotes @ 161e:01d0`: text uses
+			// `_NoteUnselectedColor` (0x5c=cyan) for unselected and 0x3c
+			// (light yellow-white) for selected. Both contrast cleanly
+			// with the PDA screen's natural blue, so we draw text
+			// directly on PIC 0x3f without an extra fill rectangle ???
+			// matches the original design.
+			Common::Array<Common::String> wrapped;
+			_font.wordWrapText(txt, kRectW, wrapped);
+			const int lineH = _font.getFontHeight() + 1;
+			const int h = (int)wrapped.size() * lineH;
+			const byte color = _mystery._noteSelected[clueId] ? 0x3C : 0x5C;
+			for (uint li = 0; li < wrapped.size(); li++) {
+				_font.drawString(&scratch, wrapped[li], kRectX,
+								 y + (int)li * lineH, kRectW, color);
+			}
+			slotRects.push_back(Common::Rect(kRectX, y,
+											  kRectX + kRectW, y + h));
+			slotClues.push_back(clueId);
+			y += h + 7;
+		}
+
+		// Page indicator + selected-points counter directly on PIC.
+		_font.drawString(&scratch, Common::String::format("p%d/%d",
+								   page + 1, (int)pageStarts.size()),
+						 270, 4, 320, 0x5C);
+		_font.drawString(&scratch, Common::String::format("%d pts",
+								   _mystery.selectedPoints()),
+						 270, 14, 320, 0x5C);
+		(void)hoveredNoteSlot;
+
+		g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
+								   0, 0, 320, 200);
+		g_system->updateScreen();
+
+		// Stash slot info on the captures so the click handler below
+		// can use it via the closure.
+		_notebookSlotRects = slotRects;
+		_notebookSlotClues = slotClues;
+	};
+
+	draw();
+
+	uint32 lastDraw = g_system->getMillis();
+
+	while (!shouldQuit()) {
+		Common::Event ev;
+		bool dirty = false;
+		bool exitFlag = false;
+		while (g_system->getEventManager()->pollEvent(ev)) {
+			if (ev.type == Common::EVENT_QUIT ||
+				ev.type == Common::EVENT_RETURN_TO_LAUNCHER) {
+				exitFlag = true;
+				break;
+			}
+			if (ev.type == Common::EVENT_KEYDOWN) {
+				if (ev.kbd.keycode == Common::KEYCODE_ESCAPE) {
+					exitFlag = true;
+					break;
+				}
+				if (ev.kbd.keycode == Common::KEYCODE_LEFT ||
+					ev.kbd.keycode == Common::KEYCODE_PAGEUP) {
+					if (page > 0) page--;
+					dirty = true;
+				} else if (ev.kbd.keycode == Common::KEYCODE_RIGHT ||
+						   ev.kbd.keycode == Common::KEYCODE_PAGEDOWN ||
+						   ev.kbd.keycode == Common::KEYCODE_TAB) {
+					page++;
+					dirty = true;
+				} else if (ev.kbd.keycode == Common::KEYCODE_h) {
+					doHelp();
+					dirty = true;
+				}
+			}
+			if (ev.type == Common::EVENT_LBUTTONDOWN) {
+				// Test buttons in the order the original would ???
+				// button 0 / 9 are dead zones, so check the actionable
+				// rects directly. Earlier rects "win" when overlapping
+				// (matches `_FindButton`).
+				if (kBtnSite.contains(ev.mouse.x, ev.mouse.y)) {
+					exitFlag = true;
+					break;  // back to site
+				}
+				if (kBtnMap.contains(ev.mouse.x, ev.mouse.y)) {
+					doBigMap();
+					exitFlag = true;
+					break;
+				}
+				if (kBtnPartner.contains(ev.mouse.x, ev.mouse.y)) {
+					doHelp();              // _KDHelp = host hint
+					dirty = true;
+					continue;
+				}
+				if (kBtnAccuse.contains(ev.mouse.x, ev.mouse.y)) {
+					doAccuse();
+					exitFlag = true;
+					break;
+				}
+				if (kBtnGallery.contains(ev.mouse.x, ev.mouse.y)) {
+					doGallery();
+					dirty = true;
+					continue;
+				}
+				if (kBtnHelp1.contains(ev.mouse.x, ev.mouse.y)) {
+					// rect 1 ??? `_InterfaceHelp(0)`: walks `HelpData[0]` and
+					// blits PICs 0x63 / 0x1ae fullscreen for click-through.
+					doInterfaceHelp(0);
+					dirty = true;
+					continue;
+				}
+				if (kBtnPagePrev.contains(ev.mouse.x, ev.mouse.y)) {
+					if (page > 0) page--;
+					dirty = true;
+					continue;
+				}
+				if (kBtnPageNext.contains(ev.mouse.x, ev.mouse.y)) {
+					page++;
+					dirty = true;
+					continue;
+				}
+				if (kNoteArea.contains(ev.mouse.x, ev.mouse.y)) {
+					// Toggle the selection on whichever clue's text
+					// the click landed in. The original calls
+					// `_InterfaceHelp` here; that's the help screen,
+					// not selection ??? selection is in the Accuse
+					// screen. We use the area for selection because
+					// keyboard 1..9 toggling is awkward, and the
+					// resulting `_NoteSelected` state is what
+					// `_SolvedCheck` reads.
+					for (uint i = 0; i < _notebookSlotRects.size(); i++) {
+						if (_notebookSlotRects[i].contains(ev.mouse.x,
+														   ev.mouse.y)) {
+							const uint clueId = _notebookSlotClues[i];
+							_mystery._noteSelected[clueId] ^= 1;
+							dirty = true;
+							break;
+						}
+					}
+					continue;
+				}
+			}
+		}
+		if (exitFlag)
+			break;
+
+		const uint32 now = g_system->getMillis();
+		// Re-render every 100 ms so the partner sprite cycles frames.
+		if (dirty || now - lastDraw >= 100) {
+			draw();
+			lastDraw = now;
+		}
+		g_system->updateScreen();
+		g_system->delayMillis(15);
+	}
+}
+
+void EEMEngine::doGallery() {
+	// Mirrors `_DoGallery @ 158f:065b` and `_DrawGallery @ 158f:0046`.
+	// Verified directly from the disassembly:
+	//   * Background: PIC 0x3f (same as PDA).
+	//   * Partner sprite at (5, 0x50): anim 2 (Jake) / 0x10 (Jenny).
+	//     `_NewAnimation(5, 0x50, ...)`. NOTE: gallery uses anim 2/0x10,
+	//     PDA uses 1/0xb ??? different sprites.
+	//   * Five fixed slot positions at `29be:0x116` (4 bytes per slot,
+	//     `{u16 x, u16 y}`):
+	//         slot 0 = ( 83,  14)   slot 3 = (119,  90)
+	//         slot 1 = (155,  14)   slot 4 = (191,  90)
+	//         slot 2 = (227,  14)
+	//   * For each logical suspect i in 0..NumSuspects-1:
+	//         picId   = `*(u16 *)(_GalleryData + i * 0x46)` (entry +0).
+	//         visible = `_InGallery[_NewOrder[i]] != 0`.
+	//         drawX   = positions[_NewOrder[i]].x
+	//         drawY   = positions[_NewOrder[i]].y + (0x48 - pic.height)
+	//     So portraits are BOTTOM-aligned to baselines 0x48 + pos.y.
+	//   * Click on portrait via `_SearchSuspects` ??? `MoreInfo(i)` shows
+	//     the suspect detail page. ESC returns to PDA.
+	//   * Frame-cycled @ 100ms via `_CheckFrameRate` + `_UpdateAnimations`
+	//     + `_GizmoColorCycle`.
+	if (!_mystery.isLoaded())
+		return;
+
+	const byte *gd = _mystery.galleryData();
+	if (!gd) {
+		warning("doGallery: no GalleryData in mystery %u", _mystery.number());
+		return;
+	}
+
+	CursorMan.showMouse(true);
+
+	struct Slot { int x; int y; };
+	static const Slot kGallerySlots[5] = {
+		{  83,  14 }, // 0
+		{ 155,  14 }, // 1
+		{ 227,  14 }, // 2
+		{ 119,  90 }, // 3
+		{ 191,  90 }  // 4
+	};
+
+	// Pre-load static elements once.
+	Picture galBg;
+	const bool haveBg = _picsArchive.getPicture(0x3f, galBg);
+
+	// Gallery partner anim ??? `_DoGallery` calls `_GetAnimation(uVar6)` with
+	// uVar6 = 2 (Jake) / 0x10 (Jenny). Different from PDA (1 / 0xb).
+	const uint partnerAnim = (_partner == 0) ? 2 : 0x10;
+	Animation partnerAni;
+	const bool havePartner = _aniArchive.loadAnimation(partnerAnim, partnerAni)
+							  && !partnerAni.empty();
+
+	const uint8 num = _mystery.numSuspects();
+
+	// Cache slot rects for click hit-testing.
+	Common::Array<Common::Rect> slotRects;
+	Common::Array<int> slotSuspect; // logical suspect index in [0, num)
+	slotRects.resize(num);
+	slotSuspect.resize(num);
+	for (uint i = 0; i < num; i++) {
+		slotSuspect[i] = -1;
+	}
+
+	auto drawFrame = [&]() {
+		Graphics::ManagedSurface scratch(320, 200,
+			Graphics::PixelFormat::createFormatCLUT8());
+		scratch.clear();
+
+		if (haveBg) {
+			const int bw = MIN<int>(galBg.surface.w, 320);
+			const int bh = MIN<int>(galBg.surface.h, 200);
+			for (int row = 0; row < bh; row++) {
+				memcpy((byte *)scratch.getBasePtr(0, row),
+					   (const byte *)galBg.surface.getBasePtr(0, row), bw);
+			}
+		}
+
+		// Partner sprite frame @ (5, 0x50).
+		if (havePartner) {
+			const uint32 now = g_system->getMillis();
+			const uint frameIdx = (uint)((now / 100) % partnerAni.size());
+			const Picture &fr = partnerAni[frameIdx];
+			const byte transp = (byte)(fr.flags >> 8);
+			const int px = 5, py = 0x50;
+			for (int row = 0; row < fr.surface.h; row++) {
+				const int dstY = py + row;
+				if (dstY < 0 || dstY >= 200) continue;
+				const byte *src = (const byte *)fr.surface.getBasePtr(0, row);
+				byte *dst = (byte *)scratch.getBasePtr(0, dstY);
+				for (int col = 0; col < fr.surface.w; col++) {
+					const int dstX = px + col;
+					if (dstX < 0 || dstX >= 320) continue;
+					if (src[col] != transp)
+						dst[dstX] = src[col];
+				}
+			}
+		}
+
+		// Portraits ??? `_DrawGallery @ 158f:0046` walks suspects 0..N-1
+		// and only renders those flagged in `_InGallery[NewOrder[i]]`.
+		// Undiscovered slots are left empty in the original. We render
+		// a darkened placeholder + "?" so the player has visual feedback
+		// that suspects exist but are still unknown.
+		uint discoveredCount = 0;
+		for (uint i = 0; i < num && i < Mystery::kGalleryCap; i++) {
+			slotRects[i] = Common::Rect();
+			slotSuspect[i] = -1;
+
+			const uint8 phys = _mystery._newOrder[i];
+			if (phys >= 5)
+				continue;
+			const Slot &s = kGallerySlots[phys];
+
+			const bool discovered = _mystery._inGallery[phys] != 0;
+			if (discovered) {
+				const uint16 picId = READ_LE_UINT16(gd + i * 0x46);
+				Picture portrait;
+				if (picId == 0 ||
+					!_picsArchive.getPicture(picId, portrait))
+					continue;
+
+				const int placeX = s.x;
+				const int placeY = s.y + (0x48 - portrait.surface.h);
+				const byte transp = (byte)(portrait.flags >> 8);
+				const int w = MIN<int>(portrait.surface.w, 320 - placeX);
+				const int h = MIN<int>(portrait.surface.h, 200 - placeY);
+				if (w <= 0 || h <= 0)
+					continue;
+				for (int row = 0; row < h; row++) {
+					const int dstY = placeY + row;
+					if (dstY < 0) continue;
+					const byte *src =
+						(const byte *)portrait.surface.getBasePtr(0, row);
+					byte *dst = (byte *)scratch.getBasePtr(0, dstY);
+					for (int col = 0; col < w; col++) {
+						const int dstX = placeX + col;
+						if (src[col] != transp)
+							dst[dstX] = src[col];
+					}
+				}
+				slotRects[i] = Common::Rect(placeX, placeY,
+											 placeX + w, placeY + h);
+				slotSuspect[i] = (int)i;
+				discoveredCount++;
+			} else {
+				// Undiscovered placeholder ??? small framed "?" box at
+				// (s.x, s.y) sized 0x40 ?? 0x48 (typical portrait size).
+				const int phW = 0x40, phH = 0x48;
+				const int phX = s.x, phY = s.y;
+				if (phX + phW <= 320 && phY + phH <= 200) {
+					scratch.fillRect(Common::Rect(phX, phY,
+						phX + phW, phY + phH), 0x20);
+					scratch.frameRect(Common::Rect(phX, phY,
+						phX + phW, phY + phH), 0x5C);
+					if (_font.isLoaded()) {
+						_font.drawString(&scratch, "?",
+							phX + phW / 2 - 3,
+							phY + phH / 2 - 4, phW, 0x5C);
+					}
+				}
+			}
+		}
+		(void)discoveredCount;
+
+		g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
+								   0, 0, 320, 200);
+		g_system->updateScreen();
+	};
+
+	drawFrame();
+	uint32 lastDraw = g_system->getMillis();
+
+	while (!shouldQuit()) {
+		Common::Event ev;
+		bool exitFlag = false;
+		while (g_system->getEventManager()->pollEvent(ev)) {
+			if (ev.type == Common::EVENT_QUIT ||
+				ev.type == Common::EVENT_RETURN_TO_LAUNCHER) {
+				return;
+			}
+			if (ev.type == Common::EVENT_KEYDOWN) {
+				if (ev.kbd.keycode == Common::KEYCODE_ESCAPE) {
+					exitFlag = true;
+					break;
+				}
+			}
+			if (ev.type == Common::EVENT_LBUTTONDOWN) {
+				// PDA bottom-bar buttons mirror `_NoteButtons @ 29be:0147`.
+				// `_DoGallery @ 158f:065b` shares the SAME button table
+				// with `_DoNotebook` (both call `_FindButton` against the
+				// 11-entry table at 0x147). `_HandleGalleryButton @
+				// 158f:05c0` dispatches via a different jump table
+				// (158f:0645). Verified gallery button mapping:
+				//   rect 0 (134,155) ??? 0x05ef = `_NextScreen = 4` (NOTEBOOK)
+				//   rect 1 (93,115)  ??? 0x0625 = `_InterfaceHelp` (HELP)
+				//   rect 2 (157,178) ??? 0x0638 = generic exit (no-op)
+				//   rect 3 (5,80)    ??? 0x061e = `_KDHelp` (host hint)
+				//   rect 4 (180,201) ??? 0x05ff = `_SolvedCheck` -> SOLVE
+				//   rect 5 (204,224) ??? 0x0638 = generic exit
+				//   rect 6 (226,247) ??? 0x0638 = generic exit
+				//   rect 7 (7,177)   ??? 0x05f7 = `_NextScreen = 2` (MAP)
+				//   rect 8 (35,111)  ??? 0x05e4 = `_NextScreen = 3` (SITE)
+				const Common::Rect kBtnSite    ( 35, 111,  56, 136); // [8] SITE
+				const Common::Rect kBtnMap     (  7, 177,  57, 200); // [7] MAP
+				const Common::Rect kBtnAccuse  (180, 174, 201, 190); // [4] SOLVE
+				const Common::Rect kBtnNotebook(134, 174, 155, 190); // [0] NOTEBOOK (back to PDA notes)
+				const Common::Rect kBtnHelp    ( 93, 174, 115, 190); // [1] HELP
+				const Common::Rect kBtnPartner (  5,  80,  44, 110); // [3] KD HELP
+				if (kBtnSite.contains(ev.mouse.x, ev.mouse.y)) {
+					exitFlag = true; break;
+				}
+				if (kBtnMap.contains(ev.mouse.x, ev.mouse.y)) {
+					doBigMap();
+					exitFlag = true; break;
+				}
+				if (kBtnAccuse.contains(ev.mouse.x, ev.mouse.y)) {
+					doAccuse();
+					exitFlag = true; break;
+				}
+				if (kBtnNotebook.contains(ev.mouse.x, ev.mouse.y)) {
+					// Already came from notebook; exiting returns to it.
+					exitFlag = true; break;
+				}
+				if (kBtnHelp.contains(ev.mouse.x, ev.mouse.y)) {
+					// Gallery rect 1 ??? `_InterfaceHelp(0)` per jmp table at
+					// 158f:0625 (HandleGalleryButton). Same picture sequence
+					// as the notebook HELP button.
+					doInterfaceHelp(0);
+					lastDraw = 0;
+					continue;
+				}
+				if (kBtnPartner.contains(ev.mouse.x, ev.mouse.y)) {
+					doHelp();
+					lastDraw = 0;
+					continue;
+				}
+				// `_SearchSuspects` walks the per-slot rects and returns
+				// the suspect index. We mirror that with cached rects.
+				bool clicked = false;
+				for (uint i = 0; i < slotRects.size(); i++) {
+					if (slotSuspect[i] < 0) continue;
+					if (slotRects[i].contains(ev.mouse.x, ev.mouse.y)) {
+						// `MoreInfo(i)` ??? show the suspect detail page.
+						// Mirrors `MoreInfo @ 158f:0419`:
+						//   _RefreshGalleryBackground();
+						//   _GetPicture(*(u16*)(gd + i*0x46));
+						//   _AddPicBackground(pic, 0x94, 0xf);
+						//   _DrawGalleryNotes(gd + i*0x46);
+						//   loop until ESC or button click.
+						// Suspect data layout (verified against M1):
+						//   +0..1: picId (used here AND for gallery slot)
+						//   +8..9: number of clues for this suspect
+						//   +0xa..??: array of u16 clue IDs (terminated
+						//             by 0xFFFF if shorter than count).
+						const uint suspectIdx = (uint)slotSuspect[i];
+						const byte *suspect = gd + suspectIdx * 0x46;
+						const uint16 detailPic =
+							READ_LE_UINT16(suspect + 0);
+						const uint16 clueCount =
+							READ_LE_UINT16(suspect + 8);
+
+						Graphics::ManagedSurface ms(320, 200,
+							Graphics::PixelFormat::createFormatCLUT8());
+						ms.clear();
+						if (haveBg) {
+							const int bw = MIN<int>(galBg.surface.w, 320);
+							const int bh = MIN<int>(galBg.surface.h, 200);
+							for (int row = 0; row < bh; row++) {
+								memcpy((byte *)ms.getBasePtr(0, row),
+									   (const byte *)galBg.surface.getBasePtr(0, row), bw);
+							}
+						}
+						// Full suspect picture at (0x94, 0xf).
+						Picture detail;
+						if (_picsArchive.getPicture(detailPic, detail)) {
+							const byte transp =
+								(byte)(detail.flags >> 8);
+							const int dx = 0x94, dy = 0x0f;
+							const int dw = MIN<int>(detail.surface.w, 320 - dx);
+							const int dh = MIN<int>(detail.surface.h, 200 - dy);
+							for (int row = 0; row < dh; row++) {
+								const byte *src =
+									(const byte *)detail.surface.getBasePtr(0, row);
+								byte *dst =
+									(byte *)ms.getBasePtr(0, dy + row);
+								for (int col = 0; col < dw; col++) {
+									if (src[col] != transp)
+										dst[dx + col] = src[col];
+								}
+							}
+						}
+						// Suspect's clue notes inside _GalleryNoteRect
+						// = (78, 93, 288, 152), per 29be:0100. Cyan text
+						// renders directly on the PDA's natural blue
+						// screen ??? matches `_DrawGalleryNotes @ 158f:01f4`.
+						const int rx = 78, ry = 93;
+						const int rw = 288 - 78, rh = 152 - 93;
+
+						const byte *ni = _mystery.noteIndex();
+						const uint16 niCount = _mystery.noteIndexCount();
+						int yPos = ry;
+						const int lineH = _font.getFontHeight() + 1;
+						bool drewAny = false;
+						for (uint k = 0; k < clueCount && k < 30; k++) {
+							const uint16 clueId =
+								READ_LE_UINT16(suspect + 0xa + k * 2);
+							if (clueId == 0xFFFF) break;
+							if (clueId >= Mystery::kCluesFoundCap ||
+								!_mystery._cluesFound[clueId])
+								continue;
+							if (!ni || clueId >= niCount) continue;
+							const uint16 textOff =
+								READ_LE_UINT16(ni + clueId * 4);
+							Common::String txt =
+								parseString(_mystery.textAt(textOff),
+											_playerName, _partner);
+							if (txt.empty()) continue;
+							const byte color =
+								_mystery._noteSelected[clueId] ? 0x3C : 0x5C;
+							const int hLine = _font.drawWordWrapped(
+								&ms, rx, yPos, rw, txt, color);
+							yPos += hLine + 7;
+							drewAny = true;
+							if (yPos + lineH > ry + rh) break;
+						}
+						if (!drewAny && _font.isLoaded()) {
+							_font.drawString(&ms,
+								"No clues yet for this suspect.",
+								rx, ry, rw, 0x5C);
+						}
+						// Header / footer text.
+						if (_font.isLoaded()) {
+							_font.drawString(&ms, "SUSPECT FILE",
+											  rx, ry - 11, rw, 0x3C);
+							_font.drawString(&ms, "(click / ESC: back)",
+											  rx, ry + rh + 2, rw, 0x3C);
+						}
+						g_system->copyRectToScreen(ms.getPixels(),
+							ms.pitch, 0, 0, 320, 200);
+						g_system->updateScreen();
+
+						// Wait for click or ESC. Drain the queued
+						// LBUTTONDOWN that triggered this MoreInfo first
+						// so we don't immediately accept it as the
+						// dismiss event.
+						g_system->delayMillis(150);
+						{
+							Common::Event drain;
+							while (g_system->getEventManager()->pollEvent(drain)) {
+								if (drain.type == Common::EVENT_QUIT ||
+									drain.type == Common::EVENT_RETURN_TO_LAUNCHER)
+									return;
+							}
+						}
+						bool back = false;
+						while (!back && !shouldQuit()) {
+							Common::Event e2;
+							while (g_system->getEventManager()->pollEvent(e2)) {
+								if (e2.type == Common::EVENT_LBUTTONDOWN ||
+									(e2.type == Common::EVENT_KEYDOWN &&
+									 (e2.kbd.keycode == Common::KEYCODE_ESCAPE ||
+									  e2.kbd.keycode == Common::KEYCODE_RETURN))) {
+									back = true;
+									break;
+								}
+								if (e2.type == Common::EVENT_QUIT ||
+									e2.type == Common::EVENT_RETURN_TO_LAUNCHER)
+									return;
+							}
+							g_system->delayMillis(20);
+						}
+						// Force gallery redraw immediately so the
+						// player isn't left looking at the dismissed
+						// MoreInfo screen until the next 100 ms tick.
+						drawFrame();
+						lastDraw = g_system->getMillis();
+						clicked = true;
+						break;
+					}
+				}
+				(void)clicked;
+			}
+		}
+		if (exitFlag) break;
+
+		const uint32 now = g_system->getMillis();
+		if (now - lastDraw >= 100) {
+			drawFrame();
+			lastDraw = now;
+		}
+		g_system->delayMillis(15);
+	}
+}
+
+void EEMEngine::doBigMap() {
+	// Two-stage flow that mirrors the original screen-1 wrapper at
+	// 20fe:120b and `_DoBigMap @ 20fe:09e7`:
+	//
+	//   STAGE 1 ??? Overview. PIC 0x42 + site icons drawn via the
+	//   `_DrawBigMapButtons` algorithm at BigMap coords MapData[+4/+6].
+	//   The original `_DoBigMap` returns sx/sy = (mouseX*2 - 0x74,
+	//   mouseY*2 - 0x55) when the player clicks inside `BigMapWindow`,
+	//   which is the scroll position into the SmallMap.
+	//
+	//   STAGE 2 ??? Detail zoom. PIC 0x43 frame + a 0xe9 ?? 0xab viewport
+	//   into BIGMAP.PIC at (2, 2), drawn by `DrawMap @ 20fe:1058` with
+	//   the (sx, sy) returned from stage 1. Site icons are stamped at
+	//   SmallMap coords MapData[+8/+0xa] via `_StampButtons`. Click on
+	//   a site icon ??? travel.
+	//
+	// MapData entry layout (14 bytes), verified directly from the
+	// disassembly of `_DrawBigMapButtons @ 20fe:0877` (`PUSH ES:[BX+4]`
+	// for X, `PUSH ES:[BX+6]` for Y, `CMP ES:[BX+0xc], 0` for crime)
+	// and `_StampButtons @ 20fe:0d2f` (`MOV AX, ES:[BX+8]`,
+	// `MOV AX, ES:[BX+0xa]`):
+	//   +0..3   ??? (not yet decoded)
+	//   +4..5   BigMap X
+	//   +6..7   BigMap Y
+	//   +8..9   SmallMap X
+	//   +0xa..b SmallMap Y
+	//   +0xc..d crime-flag
+
+	if (!_mystery.isLoaded())
+		return;
+
+	CursorMan.showMouse(true);
+
+	// `_GetPalette(0x24)` per `_DoBigMap @ 20fe:09e7`.
+	setSitePalette(0x24);
+
+	const Common::Rect kSetupRect(0xc7, 0x12, 0xc7 + 0x32, 0x12 + 0xa); // approx; original from globals
+	(void)kSetupRect; // not yet wired into our overlay
+
+	// ------------------------------------------------------------------
+	// STAGE 1 ??? Overview: PIC 0x42 + clickable site icons.
+	// ------------------------------------------------------------------
+
+	// `_DoBigMap @ 20fe:09e7` (20fe:0a44-0a99) registers a partner sprite
+	// on the overview frame. The animation depends on `_LastScreen`:
+	//   * When LastScreen == 2 (came from the site loop) the original
+	//     plays an entrance anim (`anum-1` for Jake / Jenny) at
+	//     (0x102, 0x50), then on END swaps to the idle anim at (0xfd,
+	//     0x50). We don't track LastScreen finely enough to distinguish,
+	//     so we render the IDLE pose at (0xfd, 0x50) which is what the
+	//     player sees the rest of the time anyway.
+	//   * Idle anim ID: Jake = 0x14 (20), Jenny = 0x12 (18).
+	const uint kMapAniId = (_partner == 0) ? 0x14 : 0x12;
+	Animation mapAnim;
+	const bool haveMapAnim = _aniArchive.loadAnimation(kMapAniId, mapAnim)
+							   && !mapAnim.empty();
+	const int kMapAnimX = 0xfd;
+	const int kMapAnimY = 0x50;
+
+	auto drawOverview = [&]() {
+		Graphics::ManagedSurface scratch(320, 200,
+			Graphics::PixelFormat::createFormatCLUT8());
+		scratch.clear();
+
+		Picture frame;
+		if (_picsArchive.getPicture(0x42, frame)) {
+			const int w = MIN<int>(frame.surface.w, 320);
+			const int h = MIN<int>(frame.surface.h, 200);
+			for (int row = 0; row < h; row++)
+				memcpy((byte *)scratch.getBasePtr(0, row),
+					   (const byte *)frame.surface.getBasePtr(0, row), w);
+		}
+
+		// Marker PICs from `_main @ 1a35:0f59`. Three globals are filled
+		// once at boot via `_GetPicture` (1-based IDs ??? entries N-1):
+		//   _DoneMarker  = PIC 0x20d  (already-searched site)
+		//   _SiteMarker  = PIC 0xc5   (default available site)
+		//   _CrimeMarker = PIC 0xc6   (crime-scene flag set)
+		// Picked per-site by `_DrawBigMapButtons @ 20fe:0877`:
+		//   1. SaveSiteComplete[i] ??? DoneMarker
+		//   2. else MapData[+0xc] != 0 ??? CrimeMarker
+		//   3. else SiteMarker
+		Picture done, normal, crimeM;
+		const bool haveDone   = _picsArchive.getPicture(0x20d, done);
+		const bool haveNormal = _picsArchive.getPicture(0xc5,  normal);
+		const bool haveCrime  = _picsArchive.getPicture(0xc6,  crimeM);
+
+		auto blitMarker = [&](const Picture &m, int x, int y) {
+			const byte transp = (byte)(m.flags >> 8);
+			for (int row = 0; row < m.surface.h; row++) {
+				const int dstY = y + row;
+				if (dstY < 0 || dstY >= 200) continue;
+				const byte *src = (const byte *)m.surface.getBasePtr(0, row);
+				byte *dst = (byte *)scratch.getBasePtr(0, dstY);
+				for (int col = 0; col < m.surface.w; col++) {
+					const int dstX = x + col;
+					if (dstX < 0 || dstX >= 320) continue;
+					if (src[col] != transp)
+						dst[dstX] = src[col];
+				}
+			}
+		};
+
+		for (uint i = 0; i < _mystery.numSites(); i++) {
+			if (!_mystery._onSites[i] && i != _mystery._siteNumber)
+				continue;
+			const byte *entry = _mystery.mapEntry(i);
+			if (!entry)
+				continue;
+			const uint16 mx    = READ_LE_UINT16(entry + 0x4);
+			const uint16 my    = READ_LE_UINT16(entry + 0x6);
+			const uint16 crime = READ_LE_UINT16(entry + 0xc);
+			const bool   done_ = (i < Mystery::kVisitedSiteCap)
+								  && _mystery._visitedSite[i];
+
+			const Picture *m = nullptr;
+			if (done_ && haveDone)            m = &done;
+			else if (crime != 0 && haveCrime) m = &crimeM;
+			else if (haveNormal)              m = &normal;
+
+			if (m)
+				blitMarker(*m, (int)mx, (int)my);
+			else {
+				// Fallback if the markers couldn't be loaded.
+				const Common::Rect mark(mx - 3, my - 3, mx + 4, my + 4);
+				scratch.fillRect(mark, 0x0F);
+			}
+		}
+
+		// Partner sprite ??? masked-blit at (0xfd, 0x50). Same per-tick
+		// idle the original would run via `_UpdateAnimations` once the
+		// entrance one-shot transitions out (see `_DoBigMap` 0xae3-0xae7
+		// where it swaps animId on the 0x80 marker).
+		if (haveMapAnim) {
+			const uint32 now = g_system->getMillis();
+			const uint frameIdx = (uint)((now / 100) % mapAnim.size());
+			const Picture &fr = mapAnim[frameIdx];
+			const byte transp = (byte)(fr.flags >> 8);
+			for (int row = 0; row < fr.surface.h; row++) {
+				const int dstY = kMapAnimY + row;
+				if (dstY < 0 || dstY >= 200) continue;
+				const byte *src = (const byte *)fr.surface.getBasePtr(0, row);
+				byte *dst = (byte *)scratch.getBasePtr(0, dstY);
+				for (int col = 0; col < fr.surface.w; col++) {
+					const int dstX = kMapAnimX + col;
+					if (dstX < 0 || dstX >= 320) continue;
+					if (src[col] != transp)
+						dst[dstX] = src[col];
+				}
+			}
+		}
+
+		g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
+								   0, 0, 320, 200);
+		g_system->updateScreen();
+	};
+
+	drawOverview();
+	uint32 mapLastTick = g_system->getMillis();
+
+	// Static rectangles read directly from the binary at the labelled
+	// addresses (29be:0x1596 onwards). Format is {x1, y1, x2, y2}.
+	const Common::Rect kBigMapWindow   (  0,   0, 247, 192); // 29be:1596
+	const Common::Rect kSetupBtnRect   (252,   4, 315,  42); // 29be:15ce
+
+	bool wantZoom = false;
+	int  zoomX = 0, zoomY = 0;
+	while (!shouldQuit()) {
+		Common::Event ev;
+		while (g_system->getEventManager()->pollEvent(ev)) {
+			if (ev.type == Common::EVENT_QUIT ||
+				ev.type == Common::EVENT_RETURN_TO_LAUNCHER)
+				return;
+			if (ev.type == Common::EVENT_KEYDOWN &&
+				ev.kbd.keycode == Common::KEYCODE_ESCAPE)
+				return;
+			if (ev.type == Common::EVENT_LBUTTONDOWN) {
+				// SetupButtonRect ??? `_NextScreen = 6` (the original's
+				// settings screen). We use it as "back to menu":
+				// abandon the current mystery and return to case
+				// selection.
+				if (kSetupBtnRect.contains(ev.mouse.x, ev.mouse.y)) {
+					_mystery.clear();
+					_nextScreen = kScreenInvalid;
+					return;
+				}
+				// Click in the BigMapWindow ??? zoom. Original formula:
+				//   sx = mouseX*2 - 0x74; sy = mouseY*2 - 0x55
+				if (kBigMapWindow.contains(ev.mouse.x, ev.mouse.y)) {
+					int sx = ev.mouse.x * 2;
+					int sy = ev.mouse.y * 2;
+					sx = (sx < 0x75) ? 0 : sx - 0x74;
+					sy = (sy < 0x56) ? 0 : sy - 0x55;
+					zoomX = sx;
+					zoomY = sy;
+					wantZoom = true;
+					break;
+				}
+			}
+		}
+		if (wantZoom)
+			break;
+		// Cycle the partner-sprite frame every 100 ms (matching the
+		// original's `_CheckFrameRate` cadence inside `_DoBigMap`).
+		const uint32 now = g_system->getMillis();
+		if (haveMapAnim && now - mapLastTick >= 100) {
+			mapLastTick = now;
+			drawOverview();
+		}
+		g_system->updateScreen();
+		g_system->delayMillis(10);
+	}
+
+	if (!wantZoom)
+		return;
+
+	// ------------------------------------------------------------------
+	// STAGE 2 ??? Detail zoom: PIC 0x43 frame + scrollable BIGMAP.PIC
+	// viewport at (2, 2), 0xe9 ?? 0xab. Click on a stamped icon ??? travel.
+	// ------------------------------------------------------------------
+
+	Common::File f;
+	if (!f.open(Common::Path("BIGMAP.PIC"))) {
+		warning("doBigMap: BIGMAP.PIC missing for detail view");
+		return;
+	}
+	const uint16 mapH = f.readUint16LE();
+	const uint16 mapW = f.readUint16LE();
+	if (mapW == 0 || mapH == 0)
+		return;
+	Common::Array<byte> mapPixels((uint32)mapW * mapH);
+	if (f.read(mapPixels.data(), mapPixels.size()) != mapPixels.size()) {
+		warning("doBigMap: short read on BIGMAP.PIC for detail view");
+		return;
+	}
+
+	const int kMapWinW = 0xe9; // 233
+	const int kMapWinH = 0xab; // 171
+	const int kMapWinX = 2;
+	const int kMapWinY = 2;
+
+	int scrollX = MAX<int>(0, MIN<int>(mapW - kMapWinW, zoomX));
+	int scrollY = MAX<int>(0, MIN<int>(mapH - kMapWinH, zoomY));
+
+	// `_DoMapScreen @ 20fe:120b` (20fe:12cd-12f0): partner sprite on
+	// the detail-zoom screen. Jake = anim 0x13 (19), Jenny = anim 0x11
+	// (17). Position (0x101, 0x50) = (257, 80), seqnum 0x13. The cells
+	// here have a "looking at the map" pose, distinct from the BigMap
+	// overview entrance/idle.
+	const uint kDetailAniId = (_partner == 0) ? 0x13 : 0x11;
+	Animation detailAnim;
+	const bool haveDetailAnim = _aniArchive.loadAnimation(kDetailAniId,
+														   detailAnim)
+								  && !detailAnim.empty();
+	const int kDetailAnimX = 0x101;
+	const int kDetailAnimY = 0x50;
+
+	auto drawDetail = [&]() {
+		Graphics::ManagedSurface scratch(320, 200,
+			Graphics::PixelFormat::createFormatCLUT8());
+		scratch.clear();
+
+		Picture frame;
+		if (_picsArchive.getPicture(0x43, frame)) {
+			const int w = MIN<int>(frame.surface.w, 320);
+			const int h = MIN<int>(frame.surface.h, 200);
+			for (int row = 0; row < h; row++)
+				memcpy((byte *)scratch.getBasePtr(0, row),
+					   (const byte *)frame.surface.getBasePtr(0, row), w);
+		}
+
+		const int copyW = MIN<int>(mapW - scrollX, kMapWinW);
+		const int copyH = MIN<int>(mapH - scrollY, kMapWinH);
+		for (int row = 0; row < copyH; row++) {
+			memcpy((byte *)scratch.getBasePtr(kMapWinX, kMapWinY + row),
+				   mapPixels.data() + (scrollY + row) * mapW + scrollX,
+				   copyW);
+		}
+
+		// Stamped site buttons. `_StampButtons @ 20fe:0d2f` does:
+		//   button = _GetButton(MapData[+0])      // BUTTON.DBD entry
+		//   destX  = MapData[+8],  destY = MapData[+0xa]
+		// then bakes the button PIC into the map bitmap. Each button
+		// sprite carries the site name baked in. We blit them on top
+		// of the BIGMAP.PIC viewport at the same SmallMap coords.
+		for (uint i = 0; i < _mystery.numSites(); i++) {
+			if (!_mystery._onSites[i] && i != _mystery._siteNumber)
+				continue;
+			const byte *entry = _mystery.mapEntry(i);
+			if (!entry) continue;
+			const uint16 buttonId = READ_LE_UINT16(entry + 0x0);
+			const uint16 mx       = READ_LE_UINT16(entry + 0x8);
+			const uint16 my       = READ_LE_UINT16(entry + 0xa);
+
+			Picture button;
+			if (!_buttonArchive.loadEntry(buttonId, button))
+				continue;
+			const int sx = (int)mx - scrollX + kMapWinX;
+			const int sy = (int)my - scrollY + kMapWinY;
+			const byte transp = (byte)(button.flags >> 8);
+
+			// Crop blit against the viewport.
+			const int x0 = MAX<int>(sx, kMapWinX);
+			const int y0 = MAX<int>(sy, kMapWinY);
+			const int x1 = MIN<int>(sx + button.surface.w, kMapWinX + kMapWinW);
+			const int y1 = MIN<int>(sy + button.surface.h, kMapWinY + kMapWinH);
+			for (int row = y0; row < y1; row++) {
+				const byte *src = (const byte *)button.surface.getBasePtr(0, row - sy);
+				byte *dst = (byte *)scratch.getBasePtr(0, row);
+				for (int col = x0; col < x1; col++) {
+					const byte px = src[col - sx];
+					if (px != transp)
+						dst[col] = px;
+				}
+			}
+		}
+
+		// Partner sprite on the detail map. Drawn last so it sits over
+		// the frame and the BIGMAP.PIC viewport.
+		if (haveDetailAnim) {
+			const uint32 now = g_system->getMillis();
+			const uint frameIdx =
+				(uint)((now / 100) % detailAnim.size());
+			const Picture &fr = detailAnim[frameIdx];
+			const byte transp = (byte)(fr.flags >> 8);
+			for (int row = 0; row < fr.surface.h; row++) {
+				const int dstY = kDetailAnimY + row;
+				if (dstY < 0 || dstY >= 200) continue;
+				const byte *src = (const byte *)fr.surface.getBasePtr(0, row);
+				byte *dst = (byte *)scratch.getBasePtr(0, dstY);
+				for (int col = 0; col < fr.surface.w; col++) {
+					const int dstX = kDetailAnimX + col;
+					if (dstX < 0 || dstX >= 320) continue;
+					if (src[col] != transp)
+						dst[dstX] = src[col];
+				}
+			}
+		}
+
+		g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
+								   0, 0, 320, 200);
+		g_system->updateScreen();
+	};
+
+	drawDetail();
+	uint32 detailLastTick = g_system->getMillis();
+
+	while (!shouldQuit()) {
+		Common::Event ev;
+		bool dirty = false;
+		while (g_system->getEventManager()->pollEvent(ev)) {
+			if (ev.type == Common::EVENT_QUIT ||
+				ev.type == Common::EVENT_RETURN_TO_LAUNCHER)
+				return;
+			if (ev.type == Common::EVENT_KEYDOWN) {
+				if (ev.kbd.keycode == Common::KEYCODE_ESCAPE)
+					return;  // exit detail back to caller (site loop / engine)
+				const int kStep = 16;
+				if (ev.kbd.keycode == Common::KEYCODE_LEFT) {
+					scrollX = MAX<int>(0, scrollX - kStep);
+					dirty = true;
+				} else if (ev.kbd.keycode == Common::KEYCODE_RIGHT) {
+					scrollX = MIN<int>(MAX<int>(0, mapW - kMapWinW),
+						scrollX + kStep);
+					dirty = true;
+				} else if (ev.kbd.keycode == Common::KEYCODE_UP) {
+					scrollY = MAX<int>(0, scrollY - kStep);
+					dirty = true;
+				} else if (ev.kbd.keycode == Common::KEYCODE_DOWN) {
+					scrollY = MIN<int>(MAX<int>(0, mapH - kMapWinH),
+						scrollY + kStep);
+					dirty = true;
+				}
+			}
+			if (ev.type == Common::EVENT_LBUTTONDOWN) {
+				// Scroll arrows + slider rects live in `SmallMapButtons`
+				// at 29be:0x159e (six 8-byte rects in order: Y-up, Y-down,
+				// X-left, X-right, right-panel, top-right) plus the
+				// dedicated `XSliderRect @ 29be:15d6` and
+				// `YSliderRect @ 29be:15de`. Format {x1,y1,x2,y2}.
+				const Common::Rect kArrowYUp   (237,   2, 247,  11);
+				const Common::Rect kArrowYDown (237, 163, 247, 172);
+				const Common::Rect kArrowXLeft (  2, 175,  12, 185);
+				const Common::Rect kArrowXRight(224, 175, 234, 185);
+				const Common::Rect kXSlider    ( 15, 175, 221, 185);
+				const Common::Rect kYSlider    (237,  14, 247, 160);
+				const Common::Rect kSetupBtn   (252,   4, 315,  42);
+
+				const int kArrowStep = 16;
+				const int kSliderRange = mapW - kMapWinW;
+				const int kSliderRangeY = mapH - kMapWinH;
+
+				if (kSetupBtn.contains(ev.mouse.x, ev.mouse.y)) {
+					// Setup button on detail too ??? `_NextScreen = 6` in
+					// the original. We treat it the same way: bail back
+					// to case selection.
+					_mystery.clear();
+					_nextScreen = kScreenInvalid;
+					return;
+				}
+				if (kArrowYUp.contains(ev.mouse.x, ev.mouse.y)) {
+					scrollY = MAX<int>(0, scrollY - kArrowStep);
+					dirty = true;
+				} else if (kArrowYDown.contains(ev.mouse.x, ev.mouse.y)) {
+					scrollY = MIN<int>(MAX<int>(0, kSliderRangeY),
+						scrollY + kArrowStep);
+					dirty = true;
+				} else if (kArrowXLeft.contains(ev.mouse.x, ev.mouse.y)) {
+					scrollX = MAX<int>(0, scrollX - kArrowStep);
+					dirty = true;
+				} else if (kArrowXRight.contains(ev.mouse.x, ev.mouse.y)) {
+					scrollX = MIN<int>(MAX<int>(0, kSliderRange),
+						scrollX + kArrowStep);
+					dirty = true;
+				} else if (kXSlider.contains(ev.mouse.x, ev.mouse.y)) {
+					// Click on X slider track ??? jump scrollX so the
+					// click position maps proportionally into the map.
+					if (kSliderRange > 0) {
+						const int t = ev.mouse.x - kXSlider.left;
+						const int tw = kXSlider.width();
+						scrollX = MAX<int>(0, MIN<int>(kSliderRange,
+							t * kSliderRange / MAX<int>(1, tw)));
+						dirty = true;
+					}
+				} else if (kYSlider.contains(ev.mouse.x, ev.mouse.y)) {
+					if (kSliderRangeY > 0) {
+						const int t = ev.mouse.y - kYSlider.top;
+						const int th = kYSlider.height();
+						scrollY = MAX<int>(0, MIN<int>(kSliderRangeY,
+							t * kSliderRangeY / MAX<int>(1, th)));
+						dirty = true;
+					}
+				} else if (ev.mouse.x >= kMapWinX &&
+						   ev.mouse.x < kMapWinX + kMapWinW &&
+						   ev.mouse.y >= kMapWinY &&
+						   ev.mouse.y < kMapWinY + kMapWinH) {
+					// Hit-test the per-site button at its actual bbox
+					// (`_StampButtons` records the rect at SmallMap +8/+0xa
+					// with the button PIC's width/height).
+					for (uint i = 0; i < _mystery.numSites(); i++) {
+						if (!_mystery._onSites[i] &&
+							i != _mystery._siteNumber)
+							continue;
+						const byte *entry = _mystery.mapEntry(i);
+						if (!entry) continue;
+						const uint16 buttonId = READ_LE_UINT16(entry + 0x0);
+						const uint16 mx       = READ_LE_UINT16(entry + 0x8);
+						const uint16 my       = READ_LE_UINT16(entry + 0xa);
+						Picture button;
+						int bw = 16, bh = 16;
+						if (_buttonArchive.loadEntry(buttonId, button)) {
+							bw = button.surface.w;
+							bh = button.surface.h;
+						}
+						const int sx = (int)mx - scrollX + kMapWinX;
+						const int sy = (int)my - scrollY + kMapWinY;
+						if (ev.mouse.x >= sx && ev.mouse.x < sx + bw &&
+							ev.mouse.y >= sy && ev.mouse.y < sy + bh) {
+							_mystery._lastSite = _mystery._siteNumber;
+							_mystery._siteNumber = (uint16)i;
+							return;
+						}
+					}
+				}
+			}
+		}
+		// Cycle the partner sprite at 100 ms ticks (same cadence as
+		// `_DoMapScreen`'s `_CheckFrameRate` + `_UpdateAnimations` loop).
+		const uint32 now = g_system->getMillis();
+		if (haveDetailAnim && now - detailLastTick >= 100) {
+			detailLastTick = now;
+			dirty = true;
+		}
+		if (dirty)
+			drawDetail();
+		g_system->updateScreen();
+		g_system->delayMillis(10);
+	}
+}
+
+uint16 EEMEngine::getKDTextBalloon(byte firstChar) const {
+	// Mirrors `_GetKDTextBalloon @ 1df2:0105`:
+	//   if ((ctype[firstChar] & 2) == 0)  bub = *(u16*)29be:1068 = 0x17
+	//   else                              bub = *(u16*)(29be:0fe6+0x1e+c*2)
+	// `ctype` is Borland's `_ctype_` array at `29be:2be1`. Bit 1 (0x02) is
+	// set only for digits '0'..'9' (verified by reading the table ??? '0'..'9'
+	// each map to byte 0x02; everything else has bit 1 clear).
+	// Lookup table at 29be:1064 (= 29be:0fe6 + 0x1e + '0'*2):
+	//   '0'???0x15  '1'???0x16  '2'???0x17  '3'???0x18  '4'???0x19
+	//   '5'???0x1a  '6'???0x20  '7'???0x21  '8'???0x22  '9'???0x1e
+	// Note `*(u16*)29be:1068` (= entry for '2') is the same byte the
+	// non-digit fallback returns ??? the original encodes the constant by
+	// reusing the digit-2 slot.
+	if (firstChar < '0' || firstChar > '9')
+		return 0x17;
+	static const uint16 kDigitBalloons[10] = {
+		0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x20, 0x21, 0x22, 0x1e
+	};
+	return kDigitBalloons[firstChar - '0'];
+}
+
+void EEMEngine::doAccuse() {
+	if (!_mystery.isLoaded())
+		return;
+
+	// Mirrors `_DoAccuseGallery @ 1df2:0a31`:
+	//   1. Show KD's hint balloon (KDTextIndex[+8] text).
+	//   2. `_GetBackground(0x3f)` ??? same backdrop as PDA / gallery.
+	//   3. `_DrawGallery()` ??? renders portraits at the standard 5 slots
+	//      (positions verified at 29be:0x116, bottom-aligned baseline 0x48).
+	//   4. Click loop dispatching on `_NoteButtons` (same table as PDA)
+	//      with a separate `_HandleAccuseNoteButton` jump table.
+	const uint8 num = _mystery.numSuspects();
+	if (num == 0)
+		return;
+
+	const byte *gd = _mystery.galleryData();
+
+	// Verbatim from 29be:0x116 ??? same five suspect slot positions as
+	// `_DrawGallery @ 158f:0046`.
+	struct Slot { int x; int y; };
+	static const Slot kGallerySlots[5] = {
+		{  83,  14 }, { 155,  14 }, { 227,  14 },
+		{ 119,  90 }, { 191,  90 }
+	};
+
+	Picture accuseBg;
+	const bool haveAccuseBg = _picsArchive.getPicture(0x3f, accuseBg);
+
+	Common::Array<Common::Rect> slotRects;
+	Common::Array<int> slotSuspect;
+	slotRects.resize(num);
+	slotSuspect.resize(num);
+	for (uint i = 0; i < num; i++)
+		slotSuspect[i] = -1;
+
+	int highlighted = 0;
+	auto drawGallery = [&]() {
+		Graphics::ManagedSurface scratch(320, 200,
+			Graphics::PixelFormat::createFormatCLUT8());
+		scratch.clear();
+		if (haveAccuseBg) {
+			const int bw = MIN<int>(accuseBg.surface.w, 320);
+			const int bh = MIN<int>(accuseBg.surface.h, 200);
+			for (int row = 0; row < bh; row++) {
+				memcpy((byte *)scratch.getBasePtr(0, row),
+					   (const byte *)accuseBg.surface.getBasePtr(0, row), bw);
+			}
+		}
+
+		for (uint i = 0; i < num && i < Mystery::kGalleryCap; i++) {
+			slotRects[i] = Common::Rect();
+			slotSuspect[i] = -1;
+			if (!gd) continue;
+			const uint8 phys = _mystery._newOrder[i];
+			if (phys >= 5) continue;
+			// `_DrawGallery @ 158f:00b9` skips suspects whose
+			// `_InGallery[phys]` flag is 0 ??? that's the original gate
+			// (some suspects only become visible after being met or
+			// stay hidden after a wrong accusation removes them).
+			if (_mystery._inGallery[phys] == 0) continue;
+			const Slot &s = kGallerySlots[phys];
+
+			const uint16 picId = READ_LE_UINT16(gd + i * 0x46);
+			if (picId == 0) continue;
+			Picture portrait;
+			if (!_picsArchive.getPicture(picId, portrait))
+				continue;
+
+			const int placeX = s.x;
+			const int placeY = s.y + (0x48 - portrait.surface.h);
+			const byte transp = (byte)(portrait.flags >> 8);
+			const int w = MIN<int>(portrait.surface.w, 320 - placeX);
+			const int h = MIN<int>(portrait.surface.h, 200 - placeY);
+			if (w <= 0 || h <= 0) continue;
+			for (int row = 0; row < h; row++) {
+				const int dstY = placeY + row;
+				if (dstY < 0) continue;
+				const byte *src =
+					(const byte *)portrait.surface.getBasePtr(0, row);
+				byte *dst = (byte *)scratch.getBasePtr(0, dstY);
+				for (int col = 0; col < w; col++) {
+					const int dstX = placeX + col;
+					if (src[col] != transp)
+						dst[dstX] = src[col];
+				}
+			}
+			slotRects[i] = Common::Rect(placeX, placeY,
+										 placeX + w, placeY + h);
+			slotSuspect[i] = (int)i;
+		}
+
+		// Highlight indicator. The original moves the mouse cursor
+		// to the centre of the highlighted suspect via `_PutMouseInRect`
+		// (1df2:0b8e) ??? we draw a 1px outline in palette index 0xFE
+		// (within the marching-ants cycle range 0xF9..0xFE) which is
+		// unambiguously visible under any palette without warping the
+		// player's cursor.
+		if (highlighted >= 0 && highlighted < (int)slotRects.size() &&
+			!slotRects[highlighted].isEmpty()) {
+			Common::Rect r = slotRects[highlighted];
+			r.grow(1);
+			scratch.frameRect(r, 0xFE);
+		}
+
+		g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
+								   0, 0, 320, 200);
+		g_system->updateScreen();
+	};
+
+	// Step 1 ??? KD hint balloon. Mirrors `_DoAccuseGallery @ 1df2:0a31`
+	// (1df2:0a4c-1df2:0afe):
+	//   text  = TextBlock + KDTextIndex[+8]               (1df2:0a4c-0a57)
+	//   bub   = _GetKDTextBalloon(text[0])                (1df2:0a6d)
+	//   GetBalloon(bub)                                   (1df2:0a7c)
+	//   y     = (h < 0x4e) ? (0x50 - h) >> 1 : 1          (1df2:0a8b-0aa5)
+	//   AddPicBackground(pic, 0x21, y)                    (1df2:0aab)
+	//   WordWrap(0x21+tbl[bub].x, y+tbl[bub].y, tbl[bub].w, text, color=0)
+	//     tbl @ 29be:0875, 10-byte entries (1df2:0ad6-0af1)
+	const byte *kdIdx = _mystery.kdTextIndex();
+	if (kdIdx) {
+		const int16 textOff = (int16)READ_LE_UINT16(kdIdx + 8);
+		if (textOff != -1) {
+			const char *raw = _mystery.textAt((uint16)textOff);
+			Common::String hint =
+				parseString(raw ? raw : "", _playerName, _partner);
+			if (!hint.empty()) {
+				// First-char dispatch via getKDTextBalloon (1df2:0105).
+				// Note: we pass the *parsed* first char; the original
+				// reads it BEFORE `_ParseString`, but the player-name /
+				// partner-name substitutions never start with digits, so
+				// the dispatch result is the same either way.
+				const byte firstChar =
+					hint.empty() ? (byte)0 : (byte)hint[0];
+				const uint16 bubNum = getKDTextBalloon(firstChar);
+				Picture balloon;
+				const bool haveBalloon =
+					_balloonArchive.size() > (bubNum & 0x7F) &&
+					_balloonArchive.loadEntry(bubNum & 0x7F, balloon);
+
+				// 1df2:0a8b-1df2:0aa5: y = (h < 0x4e) ? (0x50-h)>>1 : 1
+				const int balloonX = 0x21;
+				int balloonY = 1;
+				if (haveBalloon && balloon.surface.h < 0x4e)
+					balloonY = (0x50 - balloon.surface.h) / 2;
+
+				Graphics::ManagedSurface ms(320, 200,
+					Graphics::PixelFormat::createFormatCLUT8());
+				ms.clear();
+				if (haveAccuseBg) {
+					const int bw = MIN<int>(accuseBg.surface.w, 320);
+					const int bh = MIN<int>(accuseBg.surface.h, 200);
+					for (int row = 0; row < bh; row++) {
+						memcpy((byte *)ms.getBasePtr(0, row),
+							   (const byte *)accuseBg.surface.getBasePtr(0, row), bw);
+					}
+				}
+				// Masked balloon blit ??? `_Rect_Move_Mask` (1000:03fc)
+				// skips pixels equal to `pic[0] >> 8`.
+				if (haveBalloon) {
+					const byte transp = (byte)(balloon.flags >> 8);
+					const int bw = MIN<int>(balloon.surface.w, 320 - balloonX);
+					const int bh = MIN<int>(balloon.surface.h, 200 - balloonY);
+					for (int row = 0; row < bh; row++) {
+						const byte *src = (const byte *)balloon.surface.getBasePtr(0, row);
+						byte *dst = (byte *)ms.getBasePtr(balloonX, balloonY + row);
+						for (int col = 0; col < bw; col++) {
+							if (src[col] != transp)
+								dst[col] = src[col];
+						}
+					}
+				}
+				// Inset table @ 29be:0875 ??? 1df2:0acb pushes color=0.
+				uint16 tx = 5, ty = 4, tw = 155;
+				getBalloonInsets(bubNum, tx, ty, tw);
+				if (_font.isLoaded()) {
+					_font.drawWordWrapped(&ms, balloonX + tx,
+										  balloonY + ty, tw, hint,
+										  haveBalloon ? 0 : 0xF);
+				}
+				g_system->copyRectToScreen(ms.getPixels(), ms.pitch,
+					0, 0, 320, 200);
+				g_system->updateScreen();
+				waitForInput(8000);
+			}
+		}
+	}
+
+	// Helper to find the next "alive" slot (one whose `_inGallery[phys]`
+	// flag is still set so a portrait was actually drawn). Mirrors the
+	// way the original wraps DI past empty slots.
+	auto nextLiveSlot = [&](int from, int dir) -> int {
+		const int n = (int)slotRects.size();
+		if (n <= 0) return 0;
+		for (int step = 1; step <= n; step++) {
+			int idx = (from + dir * step) % n;
+			if (idx < 0) idx += n;
+			if (!slotRects[idx].isEmpty())
+				return idx;
+		}
+		return from;
+	};
+	if (slotRects[highlighted].isEmpty())
+		highlighted = nextLiveSlot(highlighted, +1);
+
+	drawGallery();
+
+	// Wait-for-pick loop. Mirrors `_DoAccuseGallery` 1df2:0b26-1df2:0bc8:
+	//   * `_CheckFrameRate` + `_UpdateAnimations` per tick (1df2:0b2a-0b33)
+	//   * 5-entry input dispatch table @ 1df2:0bc9:
+	//       0x09 (TAB)   ??? handler 0x0b94 (cycle highlight)
+	//       0x0d (Enter) ??? handler 0x0b72 (pick = _SearchSuspects)
+	//       0x4b (LEFT)  ??? handler 0x0b94
+	//       0x4d (RIGHT) ??? handler 0x0b94
+	//       0xFFFF (mb)  ??? handler 0x0b72
+	//   * 0x0b94: `INC DI` + wraparound + `_PutMouseInRect(&Guys[DI])`,
+	//     i.e. advance highlight and warp cursor (1df2:0b94-0bb1).
+	//   * 0x0b72: `_SearchSuspects` (158f:0584) ??? mouse-rect hit-test;
+	//     if non-0xFFFF, pick that suspect.
+	// We don't warp the cursor (unfriendly under SDL); instead the
+	// highlight is drawn as a 1px outline and Enter picks it.
+	int picked = -1;
+	uint32 lastTick = g_system->getMillis();
+	bool dirty = false;
+	while (picked < 0 && !shouldQuit()) {
+		Common::Event ev;
+		while (g_system->getEventManager()->pollEvent(ev)) {
+			if (ev.type == Common::EVENT_QUIT ||
+				ev.type == Common::EVENT_RETURN_TO_LAUNCHER)
+				return;
+			if (ev.type == Common::EVENT_KEYDOWN) {
+				switch (ev.kbd.keycode) {
+				case Common::KEYCODE_ESCAPE:
+					return;
+				case Common::KEYCODE_TAB:
+				case Common::KEYCODE_RIGHT:
+					highlighted = nextLiveSlot(highlighted, +1);
+					dirty = true;
+					break;
+				case Common::KEYCODE_LEFT:
+					// 1df2:0b94 increments DI for LEFT too ??? but a
+					// keyboard-driven UX is friendlier with separate
+					// directions, so we mirror Right=+1 / Left=-1.
+					highlighted = nextLiveSlot(highlighted, -1);
+					dirty = true;
+					break;
+				case Common::KEYCODE_RETURN:
+				case Common::KEYCODE_KP_ENTER:
+					if (highlighted >= 0 &&
+						highlighted < (int)slotRects.size() &&
+						!slotRects[highlighted].isEmpty()) {
+						picked = highlighted;
+					}
+					break;
+				default: {
+					const int k = (int)ev.kbd.keycode;
+					if (k >= Common::KEYCODE_1 && k <= Common::KEYCODE_9) {
+						const int idx = k - Common::KEYCODE_1;
+						if (idx < num &&
+							!slotRects[idx].isEmpty())
+							picked = idx;
+					}
+					break;
+				}
+				}
+			}
+			if (ev.type == Common::EVENT_LBUTTONDOWN) {
+				for (uint i = 0; i < slotRects.size(); i++) {
+					if (slotSuspect[i] < 0) continue;
+					if (slotRects[i].contains(ev.mouse.x, ev.mouse.y)) {
+						picked = (int)i;
+						break;
+					}
+				}
+			}
+		}
+		// 100 ms tick ??? the original calls `_UpdateAnimations` per
+		// `_CheckFrameRate` (1df2:0b33). The accuse screen has no
+		// animations registered, so the tick is just a redraw cadence.
+		// We still re-render whenever the highlight moves (`dirty`).
+		const uint32 now = g_system->getMillis();
+		if (dirty || now - lastTick >= 100) {
+			drawGallery();
+			lastTick = now;
+			dirty = false;
+		}
+		g_system->updateScreen();
+		g_system->delayMillis(10);
+	}
+	if (picked < 0)
+		return;
+
+	// Real chain evaluation: sum point values of clues the player marked
+	// "selected" in the notebook. Mirrors `_SolvedCheck` @ 1df2:00ec.
+	const int points = _mystery.selectedPoints();
+	const bool guessedRight = _mystery.solvedCheck();
+	debugC(1, kDebugScript, "doAccuse: picked=%d selectedPts=%d -> %s",
+		   picked, points, guessedRight ? "correct" : "wrong");
+
+	// If the player hasn't marked any evidence yet, give them a hint
+	// rather than an instant fail. Mirrors the original "We're not ready
+	// to solve this mystery yet..." string at 29be:10f0.
+	if (points == 0 && _font.isLoaded()) {
+		Graphics::ManagedSurface scratch(320, 200,
+			Graphics::PixelFormat::createFormatCLUT8());
+		scratch.clear();
+		_font.drawWordWrapped(&scratch, 16, 80, 288,
+			"We're not ready to solve this mystery yet. "
+			"Let's keep investigating until we have some "
+			"more solid evidence to make our case! "
+			"(Press N in the site screen to mark clues.)",
+			0xF);
+		g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
+								   0, 0, 320, 200);
+		g_system->updateScreen();
+		waitForInput(15000);
+		return;
+	}
+
+	// Pick the ending based on the chain. For a correct accusation the
+	// original would call `_DisplayClue(_Mystery + AChain[0])`, play
+	// SCRAPBK.ANI and save progress. We load the matching `E<n>.BIN`
+	// ending text and render its pages with prev/next navigation.
+	const int endingNum = guessedRight ? picked : 0;
+	const Common::String fname = Common::String::format("E%d.BIN", endingNum);
+	Common::File f;
+	if (!f.open(Common::Path(fname))) {
+		warning("doAccuse: %s missing", fname.c_str());
+		return;
+	}
+
+	// E<n>.BIN format (verified against `_DisplayEndingPage` @ 1df2:044c):
+	//   u16 numPages
+	//   per page (10 bytes header + NUL-string):
+	//     u16 picNum
+	//     u16 x1, y1, x2, y2  (story rect)
+	//     bytes[] NUL-terminated text
+	const uint32 fileLen = f.size();
+	Common::Array<byte> blob(fileLen);
+	if (f.read(blob.data(), fileLen) != fileLen)
+		return;
+	const byte *e = blob.data();
+	const uint16 pages = READ_LE_UINT16(e);
+
+	uint pageIdx = 0;
+
+	while (!shouldQuit()) {
+		// Walk to pageIdx.
+		uint pos = 2;
+		uint cur = 0;
+		while (cur < pageIdx && pos + 10 < fileLen) {
+			const char *t = (const char *)(e + pos + 10);
+			pos += 10 + strlen(t) + 1;
+			cur++;
+		}
+		if (pos + 10 >= fileLen)
+			break;
+
+		const uint16 picNum = READ_LE_UINT16(e + pos + 0);
+		const uint16 x1     = READ_LE_UINT16(e + pos + 2);
+		const uint16 y1     = READ_LE_UINT16(e + pos + 4);
+		const uint16 x2     = READ_LE_UINT16(e + pos + 6);
+		const uint16 y2     = READ_LE_UINT16(e + pos + 8);
+		const char *raw     = (const char *)(e + pos + 10);
+		const Common::String txt = parseString(raw, _playerName, _partner);
+
+		Graphics::ManagedSurface scratch(320, 200,
+			Graphics::PixelFormat::createFormatCLUT8());
+		scratch.clear();
+
+		// Page background.
+		if (picNum != 0) {
+			Picture bg;
+			if (_picsArchive.getPicture(picNum, bg)) {
+				const int w = MIN<int>(bg.surface.w, 320);
+				const int h = MIN<int>(bg.surface.h, 200);
+				for (int row = 0; row < h; row++) {
+					memcpy((byte *)scratch.getBasePtr(0, row),
+						   (const byte *)bg.surface.getBasePtr(0, row), w);
+				}
+			}
+		}
+
+		if (_font.isLoaded()) {
+			Common::String banner = "Not enough evidence";
+			if (guessedRight)
+				banner = _mystery._firstTry ? "CORRECT - FIRST TRY!" : "CORRECT!";
+			_font.drawString(&scratch, banner, 8, 4, 320, 0xF);
+			_font.drawString(&scratch, Common::String::format("Evidence: %d/100  Suspect: %d",
+									   points, picked + 1), 8, 16, 320, 0xF);
+			const int wrapW = MAX<int>(16, x2 - x1);
+			const int wrapY = MAX<int>(28, (int)y1);
+			(void)y2;
+			_font.drawWordWrapped(&scratch, x1, wrapY, wrapW, txt, 0xF);
+			_font.drawString(&scratch, Common::String::format("page %u/%u  (Left/Right or click)",
+									   pageIdx + 1, pages), 8, 188, 320, 0xF);
+		}
+
+		g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
+								   0, 0, 320, 200);
+		g_system->updateScreen();
+
+		// Page navigation.
+		bool advance = false;
+		bool back    = false;
+		bool exit    = false;
+		while (!advance && !back && !exit && !shouldQuit()) {
+			Common::Event ev;
+			while (g_system->getEventManager()->pollEvent(ev)) {
+				if (ev.type == Common::EVENT_QUIT ||
+					ev.type == Common::EVENT_RETURN_TO_LAUNCHER) {
+					exit = true; break;
+				}
+				if (ev.type == Common::EVENT_LBUTTONDOWN) {
+					advance = true; break;
+				}
+				if (ev.type == Common::EVENT_KEYDOWN) {
+					if (ev.kbd.keycode == Common::KEYCODE_ESCAPE)
+						exit = true;
+					else if (ev.kbd.keycode == Common::KEYCODE_LEFT)
+						back = true;
+					else
+						advance = true;
+					break;
+				}
+			}
+			g_system->updateScreen();
+			g_system->delayMillis(15);
+		}
+		if (exit) break;
+		if (advance) {
+			if (pageIdx + 1 >= pages) break;
+			pageIdx++;
+		} else if (back) {
+			if (pageIdx > 0) pageIdx--;
+		}
+	}
+
+	// Mirror `_DisplayCorrect`'s scrap-book animation + solved tracking +
+	// auto-save (the original calls `_SavePlayerRecord` after a win).
+	if (guessedRight) {
+		const uint mn = _mystery.number();
+		if (mn < sizeof(_mysteriesSolved)) {
+			_mysteriesSolved[mn] = _mystery._firstTry ? 2 : 1;
+		}
+		playAnm(Common::Path("SCRAPBK.ANI"), 120, true);
+
+		// Auto-save into slot 0 (the engine's quicksave slot).
+		const Common::String desc = Common::String::format(
+			"%s ??? solved mystery %u", _playerName.c_str(), mn);
+		Common::Error err = saveGameState(0, desc, true);
+		if (err.getCode() != Common::kNoError)
+			warning("auto-save after solve failed: %s",
+					err.getDesc().c_str());
+	} else {
+		_mystery._firstTry = false;
+	}
+}
+
+} // End of namespace EEM


Commit: 7bb7d3b82e69e044876a35ca8ddff7964cddd5e4
    https://github.com/scummvm/scummvm/commit/7bb7d3b82e69e044876a35ca8ddff7964cddd5e4
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:20:40+02:00

Commit Message:
EEM: enforce code standards

Changed paths:
    engines/eem/clues.cpp
    engines/eem/eem.cpp
    engines/eem/site.cpp
    engines/eem/ui.cpp


diff --git a/engines/eem/clues.cpp b/engines/eem/clues.cpp
index a12136c304f..f667ce49a73 100644
--- a/engines/eem/clues.cpp
+++ b/engines/eem/clues.cpp
@@ -242,15 +242,18 @@ void EEMEngine::doInitClues() {
 	auto blitMaskedAt = [&](const Picture &p, int x, int y) {
 		const byte transp = (byte)(p.flags >> 8);
 		Graphics::Surface *screen = g_system->lockScreen();
-		if (!screen) return;
+		if (!screen)
+			return;
 		for (int row = 0; row < p.surface.h; row++) {
 			const int dstY = y + row;
-			if (dstY < 0 || dstY >= screen->h) continue;
+			if (dstY < 0 || dstY >= screen->h)
+				continue;
 			const byte *src = (const byte *)p.surface.getBasePtr(0, row);
 			byte *dst = (byte *)screen->getBasePtr(0, dstY);
 			for (int col = 0; col < p.surface.w; col++) {
 				const int dstX = x + col;
-				if (dstX < 0 || dstX >= screen->w) continue;
+				if (dstX < 0 || dstX >= screen->w)
+					continue;
 				if (src[col] != transp)
 					dst[dstX] = src[col];
 			}
@@ -319,16 +322,33 @@ void EEMEngine::doInitClues() {
 	uint16 seqY   = 0x6c;
 	if (_partner == 0) {
 		switch (caseType) {
-		case 1: seqAni = 0x38; seqY = 0x6d; break;
-		case 2: seqAni = 0x37; seqY = 0x6c; break;
-		case 3: seqAni = 0x39; seqY = 0x6c; break;
-		default: break;
+		case 1:
+			seqAni = 0x38;
+			seqY = 0x6d;
+			break;
+		case 2:
+			seqAni = 0x37;
+			seqY = 0x6c;
+			break;
+		case 3:
+			seqAni = 0x39;
+			seqY = 0x6c;
+			break;
+		default:
+			break;
 		}
 	} else {
 		switch (caseType) {
-		case 2: seqAni = 0x3a; seqY = 0x6c; break;
-		case 3: seqAni = 0x3d; seqY = 0x6c; break;
-		default: break;
+		case 2:
+			seqAni = 0x3a;
+			seqY = 0x6c;
+			break;
+		case 3:
+			seqAni = 0x3d;
+			seqY = 0x6c;
+			break;
+		default:
+			break;
 		}
 	}
 	if (seqAni != 0xFFFF) {
@@ -574,7 +594,8 @@ void EEMEngine::displayClue(const byte *clueBlock) {
 			// copy the changed band back. This preserves the site BG
 			// underneath unchanged regions.
 			Graphics::Surface *screen = g_system->lockScreen();
-			if (!screen) break;
+			if (!screen)
+				break;
 			Graphics::ManagedSurface scratch(320, 200,
 				Graphics::PixelFormat::createFormatCLUT8());
 			for (int row = 0; row < 200; row++) {
diff --git a/engines/eem/eem.cpp b/engines/eem/eem.cpp
index cdbc7e4ac37..b33fb699ae1 100644
--- a/engines/eem/eem.cpp
+++ b/engines/eem/eem.cpp
@@ -147,7 +147,8 @@ Common::Error EEMEngine::run() {
 				doSiteLoop();
 			while (!shouldQuit()) {
 				doCaseSelection();
-				if (!_mystery.isLoaded()) break;
+				if (!_mystery.isLoaded())
+					break;
 				doInitClues();
 				doBigMap();
 				if (_mystery.isLoaded())
diff --git a/engines/eem/site.cpp b/engines/eem/site.cpp
index 1e7f388e6eb..f0eb4342801 100644
--- a/engines/eem/site.cpp
+++ b/engines/eem/site.cpp
@@ -465,12 +465,14 @@ static void blitMaskedSurface(Graphics::Surface *screen,
 	const byte transp = (byte)(p.flags >> 8);
 	for (int row = 0; row < p.surface.h; row++) {
 		const int dstY = y + row;
-		if (dstY < 0 || dstY >= screen->h) continue;
+		if (dstY < 0 || dstY >= screen->h)
+			continue;
 		const byte *src = (const byte *)p.surface.getBasePtr(0, row);
 		byte *dst = (byte *)screen->getBasePtr(0, dstY);
 		for (int col = 0; col < p.surface.w; col++) {
 			const int dstX = x + col;
-			if (dstX < 0 || dstX >= screen->w) continue;
+			if (dstX < 0 || dstX >= screen->w)
+				continue;
 			if (src[col] != transp)
 				dst[dstX] = src[col];
 		}
@@ -598,12 +600,15 @@ void SiteScreen::applyColorCycles() {
 	// 0xf9..0xfe for hotspot marching ants (the `_ColorCycle(0xf9,
 	// 0xfe)` call at the bottom of `_DoSiteLoop`'s main loop).
 	auto rotate = [&](uint8 start, uint8 end) {
-		if (end <= start) return;
+		if (end <= start)
+			return;
 		const uint count = (uint)end - (uint)start + 1;
 		byte buf[256 * 3];
 		g_system->getPaletteManager()->grabPalette(buf, start, count);
 		// Save first triplet, shift, restore at end.
-		byte savedR = buf[0], savedG = buf[1], savedB = buf[2];
+		const byte savedR = buf[0];
+		const byte savedG = buf[1];
+		const byte savedB = buf[2];
 		for (uint i = 0; i + 1 < count; i++) {
 			buf[i * 3 + 0] = buf[(i + 1) * 3 + 0];
 			buf[i * 3 + 1] = buf[(i + 1) * 3 + 1];
@@ -1053,12 +1058,14 @@ void EEMEngine::playKdAnim(uint16 num) {
 		const int h = MIN<int>(fr.surface.h, 200 - py);
 		for (int row = 0; row < h; row++) {
 			const int dstY = py + row;
-			if (dstY < 0) continue;
+			if (dstY < 0)
+				continue;
 			const byte *src = (const byte *)fr.surface.getBasePtr(0, row);
 			byte *dst = (byte *)scratch.getBasePtr(0, dstY);
 			for (int col = 0; col < w; col++) {
 				const int dstX = px + col;
-				if (dstX < 0) continue;
+				if (dstX < 0)
+					continue;
 				if (src[col] != transp)
 					dst[dstX] = src[col];
 			}
diff --git a/engines/eem/ui.cpp b/engines/eem/ui.cpp
index 03bd77f609d..724e88e81ca 100644
--- a/engines/eem/ui.cpp
+++ b/engines/eem/ui.cpp
@@ -214,12 +214,14 @@ void EEMEngine::doCaseSelection() {
 			const byte transp = (byte)(fr.flags >> 8);
 			for (int row = 0; row < fr.surface.h; row++) {
 				const int dstY = kKdAnimY + row;
-				if (dstY < 0 || dstY >= 200) continue;
+				if (dstY < 0 || dstY >= 200)
+					continue;
 				const byte *src = (const byte *)fr.surface.getBasePtr(0, row);
 				byte *dst = (byte *)scratch.getBasePtr(0, dstY);
 				for (int col = 0; col < fr.surface.w; col++) {
 					const int dstX = kKdAnimX + col;
-					if (dstX < 0 || dstX >= 320) continue;
+					if (dstX < 0 || dstX >= 320)
+						continue;
 					if (src[col] != transp)
 						dst[dstX] = src[col];
 				}
@@ -420,7 +422,8 @@ void EEMEngine::doCaseSelection() {
 			const int kLineH  = 10;
 			const int kVisible = 12;
 			int top = (int)sel - kVisible / 2;
-			if (top < 0) top = 0;
+			if (top < 0)
+				top = 0;
 			if (top + kVisible > (int)kMaxMystery + 1)
 				top = (int)kMaxMystery + 1 - kVisible;
 			for (int r = 0; r < kVisible; r++) {
@@ -429,8 +432,10 @@ void EEMEngine::doCaseSelection() {
 					break;
 				char marker = ' ';
 				if ((uint)idx < sizeof(_mysteriesSolved)) {
-					if (_mysteriesSolved[idx] == 2) marker = '*';
-					else if (_mysteriesSolved[idx] == 1) marker = '+';
+					if (_mysteriesSolved[idx] == 2)
+						marker = '*';
+					else if (_mysteriesSolved[idx] == 1)
+						marker = '+';
 				}
 				const char arrow = ((uint)idx == sel) ? '>' : ' ';
 				_font.drawString(&scratch,
@@ -476,7 +481,8 @@ void EEMEngine::doCaseSelection() {
 					const int kLineH = 10;
 					const int kVisible = 12;
 					int top = (int)sel - kVisible / 2;
-					if (top < 0) top = 0;
+					if (top < 0)
+						top = 0;
 					if (top + kVisible > (int)kMaxMystery + 1)
 						top = (int)kMaxMystery + 1 - kVisible;
 					const int row = (ev.mouse.y - kListRect.top) / kLineH;
@@ -630,12 +636,14 @@ void EEMEngine::doNotebook() {
 			const byte transp = (byte)(fr.flags >> 8);
 			for (int row = 0; row < fr.surface.h; row++) {
 				const int dstY = 80 + row;
-				if (dstY < 0 || dstY >= 200) continue;
+				if (dstY < 0 || dstY >= 200)
+					continue;
 				const byte *src = (const byte *)fr.surface.getBasePtr(0, row);
 				byte *dst = (byte *)scratch.getBasePtr(0, dstY);
 				for (int col = 0; col < fr.surface.w; col++) {
 					const int dstX = 5 + col;
-					if (dstX < 0 || dstX >= 320) continue;
+					if (dstX < 0 || dstX >= 320)
+						continue;
 					if (src[col] != transp)
 						dst[dstX] = src[col];
 				}
@@ -771,7 +779,8 @@ void EEMEngine::doNotebook() {
 				}
 				if (ev.kbd.keycode == Common::KEYCODE_LEFT ||
 					ev.kbd.keycode == Common::KEYCODE_PAGEUP) {
-					if (page > 0) page--;
+					if (page > 0)
+						page--;
 					dirty = true;
 				} else if (ev.kbd.keycode == Common::KEYCODE_RIGHT ||
 						   ev.kbd.keycode == Common::KEYCODE_PAGEDOWN ||
@@ -820,7 +829,8 @@ void EEMEngine::doNotebook() {
 					continue;
 				}
 				if (kBtnPagePrev.contains(ev.mouse.x, ev.mouse.y)) {
-					if (page > 0) page--;
+					if (page > 0)
+						page--;
 					dirty = true;
 					continue;
 				}
@@ -952,12 +962,14 @@ void EEMEngine::doGallery() {
 			const int px = 5, py = 0x50;
 			for (int row = 0; row < fr.surface.h; row++) {
 				const int dstY = py + row;
-				if (dstY < 0 || dstY >= 200) continue;
+				if (dstY < 0 || dstY >= 200)
+					continue;
 				const byte *src = (const byte *)fr.surface.getBasePtr(0, row);
 				byte *dst = (byte *)scratch.getBasePtr(0, dstY);
 				for (int col = 0; col < fr.surface.w; col++) {
 					const int dstX = px + col;
-					if (dstX < 0 || dstX >= 320) continue;
+					if (dstX < 0 || dstX >= 320)
+						continue;
 					if (src[col] != transp)
 						dst[dstX] = src[col];
 				}
@@ -996,7 +1008,8 @@ void EEMEngine::doGallery() {
 					continue;
 				for (int row = 0; row < h; row++) {
 					const int dstY = placeY + row;
-					if (dstY < 0) continue;
+					if (dstY < 0)
+						continue;
 					const byte *src =
 						(const byte *)portrait.surface.getBasePtr(0, row);
 					byte *dst = (byte *)scratch.getBasePtr(0, dstY);
@@ -1106,7 +1119,8 @@ void EEMEngine::doGallery() {
 				// the suspect index. We mirror that with cached rects.
 				bool clicked = false;
 				for (uint i = 0; i < slotRects.size(); i++) {
-					if (slotSuspect[i] < 0) continue;
+					if (slotSuspect[i] < 0)
+						continue;
 					if (slotRects[i].contains(ev.mouse.x, ev.mouse.y)) {
 						// `MoreInfo(i)` ??? show the suspect detail page.
 						// Mirrors `MoreInfo @ 158f:0419`:
@@ -1172,24 +1186,28 @@ void EEMEngine::doGallery() {
 						for (uint k = 0; k < clueCount && k < 30; k++) {
 							const uint16 clueId =
 								READ_LE_UINT16(suspect + 0xa + k * 2);
-							if (clueId == 0xFFFF) break;
+							if (clueId == 0xFFFF)
+								break;
 							if (clueId >= Mystery::kCluesFoundCap ||
 								!_mystery._cluesFound[clueId])
 								continue;
-							if (!ni || clueId >= niCount) continue;
+							if (!ni || clueId >= niCount)
+								continue;
 							const uint16 textOff =
 								READ_LE_UINT16(ni + clueId * 4);
 							Common::String txt =
 								parseString(_mystery.textAt(textOff),
 											_playerName, _partner);
-							if (txt.empty()) continue;
+							if (txt.empty())
+								continue;
 							const byte color =
 								_mystery._noteSelected[clueId] ? 0x3C : 0x5C;
 							const int hLine = _font.drawWordWrapped(
 								&ms, rx, yPos, rw, txt, color);
 							yPos += hLine + 7;
 							drewAny = true;
-							if (yPos + lineH > ry + rh) break;
+							if (yPos + lineH > ry + rh)
+								break;
 						}
 						if (!drewAny && _font.isLoaded()) {
 							_font.drawString(&ms,
@@ -1249,7 +1267,8 @@ void EEMEngine::doGallery() {
 				(void)clicked;
 			}
 		}
-		if (exitFlag) break;
+		if (exitFlag)
+			break;
 
 		const uint32 now = g_system->getMillis();
 		if (now - lastDraw >= 100) {
@@ -1351,12 +1370,14 @@ void EEMEngine::doBigMap() {
 			const byte transp = (byte)(m.flags >> 8);
 			for (int row = 0; row < m.surface.h; row++) {
 				const int dstY = y + row;
-				if (dstY < 0 || dstY >= 200) continue;
+				if (dstY < 0 || dstY >= 200)
+					continue;
 				const byte *src = (const byte *)m.surface.getBasePtr(0, row);
 				byte *dst = (byte *)scratch.getBasePtr(0, dstY);
 				for (int col = 0; col < m.surface.w; col++) {
 					const int dstX = x + col;
-					if (dstX < 0 || dstX >= 320) continue;
+					if (dstX < 0 || dstX >= 320)
+						continue;
 					if (src[col] != transp)
 						dst[dstX] = src[col];
 				}
@@ -1376,9 +1397,12 @@ void EEMEngine::doBigMap() {
 								  && _mystery._visitedSite[i];
 
 			const Picture *m = nullptr;
-			if (done_ && haveDone)            m = &done;
-			else if (crime != 0 && haveCrime) m = &crimeM;
-			else if (haveNormal)              m = &normal;
+			if (done_ && haveDone)
+				m = &done;
+			else if (crime != 0 && haveCrime)
+				m = &crimeM;
+			else if (haveNormal)
+				m = &normal;
 
 			if (m)
 				blitMarker(*m, (int)mx, (int)my);
@@ -1400,12 +1424,14 @@ void EEMEngine::doBigMap() {
 			const byte transp = (byte)(fr.flags >> 8);
 			for (int row = 0; row < fr.surface.h; row++) {
 				const int dstY = kMapAnimY + row;
-				if (dstY < 0 || dstY >= 200) continue;
+				if (dstY < 0 || dstY >= 200)
+					continue;
 				const byte *src = (const byte *)fr.surface.getBasePtr(0, row);
 				byte *dst = (byte *)scratch.getBasePtr(0, dstY);
 				for (int col = 0; col < fr.surface.w; col++) {
 					const int dstX = kMapAnimX + col;
-					if (dstX < 0 || dstX >= 320) continue;
+					if (dstX < 0 || dstX >= 320)
+						continue;
 					if (src[col] != transp)
 						dst[dstX] = src[col];
 				}
@@ -1426,7 +1452,8 @@ void EEMEngine::doBigMap() {
 	const Common::Rect kSetupBtnRect   (252,   4, 315,  42); // 29be:15ce
 
 	bool wantZoom = false;
-	int  zoomX = 0, zoomY = 0;
+	int zoomX = 0;
+	int zoomY = 0;
 	while (!shouldQuit()) {
 		Common::Event ev;
 		while (g_system->getEventManager()->pollEvent(ev)) {
@@ -1549,7 +1576,8 @@ void EEMEngine::doBigMap() {
 			if (!_mystery._onSites[i] && i != _mystery._siteNumber)
 				continue;
 			const byte *entry = _mystery.mapEntry(i);
-			if (!entry) continue;
+			if (!entry)
+				continue;
 			const uint16 buttonId = READ_LE_UINT16(entry + 0x0);
 			const uint16 mx       = READ_LE_UINT16(entry + 0x8);
 			const uint16 my       = READ_LE_UINT16(entry + 0xa);
@@ -1587,12 +1615,14 @@ void EEMEngine::doBigMap() {
 			const byte transp = (byte)(fr.flags >> 8);
 			for (int row = 0; row < fr.surface.h; row++) {
 				const int dstY = kDetailAnimY + row;
-				if (dstY < 0 || dstY >= 200) continue;
+				if (dstY < 0 || dstY >= 200)
+					continue;
 				const byte *src = (const byte *)fr.surface.getBasePtr(0, row);
 				byte *dst = (byte *)scratch.getBasePtr(0, dstY);
 				for (int col = 0; col < fr.surface.w; col++) {
 					const int dstX = kDetailAnimX + col;
-					if (dstX < 0 || dstX >= 320) continue;
+					if (dstX < 0 || dstX >= 320)
+						continue;
 					if (src[col] != transp)
 						dst[dstX] = src[col];
 				}
@@ -1704,12 +1734,14 @@ void EEMEngine::doBigMap() {
 							i != _mystery._siteNumber)
 							continue;
 						const byte *entry = _mystery.mapEntry(i);
-						if (!entry) continue;
+						if (!entry)
+							continue;
 						const uint16 buttonId = READ_LE_UINT16(entry + 0x0);
 						const uint16 mx       = READ_LE_UINT16(entry + 0x8);
 						const uint16 my       = READ_LE_UINT16(entry + 0xa);
 						Picture button;
-						int bw = 16, bh = 16;
+						int bw = 16;
+						int bh = 16;
 						if (_buttonArchive.loadEntry(buttonId, button)) {
 							bw = button.surface.w;
 							bh = button.surface.h;
@@ -1813,18 +1845,22 @@ void EEMEngine::doAccuse() {
 		for (uint i = 0; i < num && i < Mystery::kGalleryCap; i++) {
 			slotRects[i] = Common::Rect();
 			slotSuspect[i] = -1;
-			if (!gd) continue;
+			if (!gd)
+				continue;
 			const uint8 phys = _mystery._newOrder[i];
-			if (phys >= 5) continue;
+			if (phys >= 5)
+				continue;
 			// `_DrawGallery @ 158f:00b9` skips suspects whose
 			// `_InGallery[phys]` flag is 0 ??? that's the original gate
 			// (some suspects only become visible after being met or
 			// stay hidden after a wrong accusation removes them).
-			if (_mystery._inGallery[phys] == 0) continue;
+			if (_mystery._inGallery[phys] == 0)
+				continue;
 			const Slot &s = kGallerySlots[phys];
 
 			const uint16 picId = READ_LE_UINT16(gd + i * 0x46);
-			if (picId == 0) continue;
+			if (picId == 0)
+				continue;
 			Picture portrait;
 			if (!_picsArchive.getPicture(picId, portrait))
 				continue;
@@ -1834,10 +1870,12 @@ void EEMEngine::doAccuse() {
 			const byte transp = (byte)(portrait.flags >> 8);
 			const int w = MIN<int>(portrait.surface.w, 320 - placeX);
 			const int h = MIN<int>(portrait.surface.h, 200 - placeY);
-			if (w <= 0 || h <= 0) continue;
+			if (w <= 0 || h <= 0)
+				continue;
 			for (int row = 0; row < h; row++) {
 				const int dstY = placeY + row;
-				if (dstY < 0) continue;
+				if (dstY < 0)
+					continue;
 				const byte *src =
 					(const byte *)portrait.surface.getBasePtr(0, row);
 				byte *dst = (byte *)scratch.getBasePtr(0, dstY);
@@ -1933,7 +1971,9 @@ void EEMEngine::doAccuse() {
 					}
 				}
 				// Inset table @ 29be:0875 ??? 1df2:0acb pushes color=0.
-				uint16 tx = 5, ty = 4, tw = 155;
+				uint16 tx = 5;
+				uint16 ty = 4;
+				uint16 tw = 155;
 				getBalloonInsets(bubNum, tx, ty, tw);
 				if (_font.isLoaded()) {
 					_font.drawWordWrapped(&ms, balloonX + tx,
@@ -1953,10 +1993,12 @@ void EEMEngine::doAccuse() {
 	// way the original wraps DI past empty slots.
 	auto nextLiveSlot = [&](int from, int dir) -> int {
 		const int n = (int)slotRects.size();
-		if (n <= 0) return 0;
+		if (n <= 0)
+			return 0;
 		for (int step = 1; step <= n; step++) {
 			int idx = (from + dir * step) % n;
-			if (idx < 0) idx += n;
+			if (idx < 0)
+				idx += n;
 			if (!slotRects[idx].isEmpty())
 				return idx;
 		}
@@ -2028,7 +2070,8 @@ void EEMEngine::doAccuse() {
 			}
 			if (ev.type == Common::EVENT_LBUTTONDOWN) {
 				for (uint i = 0; i < slotRects.size(); i++) {
-					if (slotSuspect[i] < 0) continue;
+					if (slotSuspect[i] < 0)
+						continue;
 					if (slotRects[i].contains(ev.mouse.x, ev.mouse.y)) {
 						picked = (int)i;
 						break;
@@ -2189,12 +2232,15 @@ void EEMEngine::doAccuse() {
 			g_system->updateScreen();
 			g_system->delayMillis(15);
 		}
-		if (exit) break;
+		if (exit)
+			break;
 		if (advance) {
-			if (pageIdx + 1 >= pages) break;
+			if (pageIdx + 1 >= pages)
+				break;
 			pageIdx++;
 		} else if (back) {
-			if (pageIdx > 0) pageIdx--;
+			if (pageIdx > 0)
+				pageIdx--;
 		}
 	}
 


Commit: a4dd19f31d7285fa9e00ac8d78a339a76cae1751
    https://github.com/scummvm/scummvm/commit/a4dd19f31d7285fa9e00ac8d78a339a76cae1751
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:20:40+02:00

Commit Message:
EEM: removed lambdas

Changed paths:
    engines/eem/clues.cpp
    engines/eem/eem.h
    engines/eem/site.cpp
    engines/eem/ui.cpp


diff --git a/engines/eem/clues.cpp b/engines/eem/clues.cpp
index f667ce49a73..da14c8990ca 100644
--- a/engines/eem/clues.cpp
+++ b/engines/eem/clues.cpp
@@ -46,6 +46,47 @@ const uint kPicChooseBackground = 0x8c; ///< `_GetBackground(0x8c)`
 const uint kAniBoy  = 8;                 ///< `_GetAnimation(8)` (Jake)
 const uint kAniGirl = 9;                 ///< `_GetAnimation(9)` (Jenny)
 
+// `_DoHappiness @ 172b:27b5`: cursor X picks one of 4 rects; past
+// rect 3 is treated as level 4. Verbatim from `29be:030f`.
+const Common::Rect kHappyZones[4] = {
+	Common::Rect(  0, 0,  70, 200), // far left  ??? girl very happy, boy neutral
+	Common::Rect( 70, 0, 126, 200), // girl's column
+	Common::Rect(126, 0, 182, 200), // middle
+	Common::Rect(182, 0, 235, 200), // boy's column
+};
+
+uint happinessLevel(int x) {
+	for (uint i = 0; i < ARRAYSIZE(kHappyZones); i++) {
+		if (kHappyZones[i].contains(x, 100))
+			return i;
+	}
+	return 4; // past zone 3 ??? max level
+}
+
+// Lock the framebuffer, masked-blit `p` at (x, y), unlock. The transparent
+// colour is the high byte of `p.flags` per `_Rect_Move_Mask @ 1000:03fc`.
+void blitMaskedToScreen(const Picture &p, int x, int y) {
+	const byte transp = (byte)(p.flags >> 8);
+	Graphics::Surface *screen = g_system->lockScreen();
+	if (!screen)
+		return;
+	for (int row = 0; row < p.surface.h; row++) {
+		const int dstY = y + row;
+		if (dstY < 0 || dstY >= screen->h)
+			continue;
+		const byte *src = (const byte *)p.surface.getBasePtr(0, row);
+		byte *dst = (byte *)screen->getBasePtr(0, dstY);
+		for (int col = 0; col < p.surface.w; col++) {
+			const int dstX = x + col;
+			if (dstX < 0 || dstX >= screen->w)
+				continue;
+			if (src[col] != transp)
+				dst[dstX] = src[col];
+		}
+	}
+	g_system->unlockScreen();
+}
+
 // On-screen positions verified from `_NewAnimation` calls @ 1a35:07b9 / 07d5.
 const int kBoyX  = 0xe2; // 226
 const int kBoyY  = 0x62; // 98
@@ -85,12 +126,6 @@ void EEMEngine::doChoosePartner() {
 	// cells contain 10 cells = pairs of "neutral, smile" at increasing
 	// intensity). Lifted verbatim from the binary so the gestures
 	// match the original beat-for-beat.
-	static const Common::Rect kHappyZones[4] = {
-		Common::Rect(  0, 0,  70, 200), // far left  ??? girl very happy, boy neutral
-		Common::Rect( 70, 0, 126, 200), // girl's column
-		Common::Rect(126, 0, 182, 200), // middle
-		Common::Rect(182, 0, 235, 200), // boy's column
-	};
 	static const uint8 kBoySeqs[5][9] = {
 		{ 0,0,0,0,0,0,0,1,0 }, // level 0
 		{ 2,2,2,2,2,2,2,3,2 }, // level 1
@@ -105,13 +140,6 @@ void EEMEngine::doChoosePartner() {
 		{ 2,2,2,2,2,2,3,2,2 },
 		{ 0,0,0,0,0,1,0,0,0 },
 	};
-	auto happinessLevel = [](int x) {
-		for (uint i = 0; i < ARRAYSIZE(kHappyZones); i++) {
-			if (kHappyZones[i].contains(x, 100))
-				return (uint)i;
-		}
-		return 4u; // past zone 3 ??? max level
-	};
 
 	// `_DoChoosePartner` opens with `_SetMousePos(0xa0, 0x96)` so the
 	// cursor lands centred between the two partners ??? start the
@@ -120,15 +148,13 @@ void EEMEngine::doChoosePartner() {
 	uint level = happinessLevel(curMouseX);
 	uint seqIdx = 0;       // step within the 9-frame seq
 
-	auto draw = [&]() {
-		blitAt(background, 0, 0);
-		const uint girlFrame = kGirlSeqs[level][seqIdx % 9];
-		const uint boyFrame  = kBoySeqs [level][seqIdx % 9];
-		blitAt(girlAnim[girlFrame % girlAnim.size()], kGirlX, kGirlY);
-		blitAt(boyAnim[boyFrame  % boyAnim.size()],  kBoyX,  kBoyY);
-		g_system->updateScreen();
-	};
-	draw();
+	// Initial render ??? pose 0 of whichever zone the cursor opens in.
+	blitAt(background, 0, 0);
+	blitAt(girlAnim[kGirlSeqs[level][seqIdx % 9] % girlAnim.size()],
+		   kGirlX, kGirlY);
+	blitAt(boyAnim [kBoySeqs [level][seqIdx % 9] % boyAnim.size()],
+		   kBoyX, kBoyY);
+	g_system->updateScreen();
 
 	debugC(1, kDebugGeneral, "ChoosePartner: %u boy frames at (%d,%d), "
 		   "%u girl frames at (%d,%d)",
@@ -145,7 +171,12 @@ void EEMEngine::doChoosePartner() {
 		if (g_system->getMillis() - lastTick > 100) {
 			lastTick = g_system->getMillis();
 			seqIdx = (seqIdx + 1) % 9;
-			draw();
+			blitAt(background, 0, 0);
+			blitAt(girlAnim[kGirlSeqs[level][seqIdx % 9] % girlAnim.size()],
+				   kGirlX, kGirlY);
+			blitAt(boyAnim [kBoySeqs [level][seqIdx % 9] % boyAnim.size()],
+				   kBoyX, kBoyY);
+			g_system->updateScreen();
 		}
 
 		Common::Event ev;
@@ -162,7 +193,12 @@ void EEMEngine::doChoosePartner() {
 				if (newLevel != level) {
 					level = newLevel;
 					seqIdx = 0; // restart cycle so the gesture pops
-					draw();
+					blitAt(background, 0, 0);
+					blitAt(girlAnim[kGirlSeqs[level][seqIdx % 9] % girlAnim.size()],
+						   kGirlX, kGirlY);
+					blitAt(boyAnim [kBoySeqs [level][seqIdx % 9] % boyAnim.size()],
+						   kBoyX, kBoyY);
+					g_system->updateScreen();
 				}
 			}
 			if (ev.type == Common::EVENT_LBUTTONDOWN) {
@@ -239,28 +275,6 @@ void EEMEngine::doInitClues() {
 						  && _aniArchive.loadAnimation(0x19, nancy)
 						  && !nancy.empty();
 
-	auto blitMaskedAt = [&](const Picture &p, int x, int y) {
-		const byte transp = (byte)(p.flags >> 8);
-		Graphics::Surface *screen = g_system->lockScreen();
-		if (!screen)
-			return;
-		for (int row = 0; row < p.surface.h; row++) {
-			const int dstY = y + row;
-			if (dstY < 0 || dstY >= screen->h)
-				continue;
-			const byte *src = (const byte *)p.surface.getBasePtr(0, row);
-			byte *dst = (byte *)screen->getBasePtr(0, dstY);
-			for (int col = 0; col < p.surface.w; col++) {
-				const int dstX = x + col;
-				if (dstX < 0 || dstX >= screen->w)
-					continue;
-				if (src[col] != transp)
-					dst[dstX] = src[col];
-			}
-		}
-		g_system->unlockScreen();
-	};
-
 	// Step 4 ??? cycle through the game animation once before the briefing.
 	// Mirrors the `while (uVar9 != gameNum)` loop. The original calls
 	// `_UpdateAnimations` per `_CheckFrameRate` tick (~10 fps). We use
@@ -273,11 +287,11 @@ void EEMEngine::doInitClues() {
 			if (_picsArchive.getPicture(0x52, bg))
 				blitAt(bg, 0, 0);
 			if (haveGame)
-				blitMaskedAt(game[frame % game.size()], 0xcd, 0x6c);
+				blitMaskedToScreen(game[frame % game.size()], 0xcd, 0x6c);
 			if (haveBook)
-				blitMaskedAt(book[frame % book.size()], 0, 99);
+				blitMaskedToScreen(book[frame % book.size()], 0, 99);
 			if (haveNancy)
-				blitMaskedAt(nancy[frame % nancy.size()], 0x68, 0x8b);
+				blitMaskedToScreen(nancy[frame % nancy.size()], 0x68, 0x8b);
 			g_system->updateScreen();
 
 			// Wait 100 ms or until input.
@@ -301,11 +315,11 @@ void EEMEngine::doInitClues() {
 	if (_picsArchive.getPicture(0x52, bg))
 		blitAt(bg, 0, 0);
 	if (haveGame)
-		blitMaskedAt(game[0], 0xcd, 0x6c);
+		blitMaskedToScreen(game[0], 0xcd, 0x6c);
 	if (haveBook)
-		blitMaskedAt(book[0], 0, 99);
+		blitMaskedToScreen(book[0], 0, 99);
 	if (haveNancy)
-		blitMaskedAt(nancy[0], 0x68, 0x8b);
+		blitMaskedToScreen(nancy[0], 0x68, 0x8b);
 	g_system->updateScreen();
 
 	// Step 5 ??? `_PlayInSequence(animSeq, 0xcd, animY)` per Ghidra:
@@ -363,18 +377,18 @@ void EEMEngine::doInitClues() {
 				if (_picsArchive.getPicture(0x52, bg))
 					blitAt(bg, 0, 0);
 				if (haveGame)
-					blitMaskedAt(game[frame % game.size()], 0xcd, 0x6c);
+					blitMaskedToScreen(game[frame % game.size()], 0xcd, 0x6c);
 				if (haveBook)
-					blitMaskedAt(book[frame % book.size()], 0, 99);
+					blitMaskedToScreen(book[frame % book.size()], 0, 99);
 				if (haveNancy)
-					blitMaskedAt(nancy[frame % nancy.size()], 0x68, 0x8b);
+					blitMaskedToScreen(nancy[frame % nancy.size()], 0x68, 0x8b);
 				// Anchor: original blits at `(sx - frame.width,
 				// sy - frame.rowoff)`. `frame.rowoff` is the y-anchor
 				// in our PicData. We use width/height directly since
 				// loadAnimation places anchor at (0, 0).
 				const int dstX = (int)0xcd - (int)fr.surface.w;
 				const int dstY = (int)seqY - (int)fr.rowoff;
-				blitMaskedAt(fr, dstX, dstY);
+				blitMaskedToScreen(fr, dstX, dstY);
 				g_system->updateScreen();
 				const uint32 wakeup = g_system->getMillis() + 100;
 				while (g_system->getMillis() < wakeup &&
diff --git a/engines/eem/eem.h b/engines/eem/eem.h
index 3777e5ac6ca..35bf5100ef4 100644
--- a/engines/eem/eem.h
+++ b/engines/eem/eem.h
@@ -163,6 +163,24 @@ private:
 	 */
 	void screenDriver();
 
+	/// Re-render helpers used by the corresponding `doX()` modal screens.
+	/// Each replaces what would otherwise be a `[&]()` capture-everything
+	/// lambda inside the `doX()` body; called from the screen's redraw
+	/// triggers (input changes, frame ticks). The state these need is
+	/// passed via reference parameters or read off engine members.
+	void drawNotebookFrame(int &page);
+	void drawGalleryFrame(const byte *gd, uint8 numSuspects,
+						  Common::Array<Common::Rect> &slotRects,
+						  Common::Array<int> &slotSuspect);
+	void drawBigMapOverview();
+	void drawBigMapDetail(int scrollX, int scrollY,
+						  const Common::Array<byte> &mapPixels,
+						  uint16 mapW, uint16 mapH);
+	void drawAccuseGallery(uint8 numSuspects, const byte *gd,
+						   int highlighted,
+						   Common::Array<Common::Rect> &slotRects,
+						   Common::Array<int> &slotSuspect);
+
 	/**
 	 * Open the five .DBD/.DBX archive pairs the way _InitGraphicsSystem
 	 * @ 172b:0145 does at boot.
diff --git a/engines/eem/site.cpp b/engines/eem/site.cpp
index f0eb4342801..df3bc56cbd3 100644
--- a/engines/eem/site.cpp
+++ b/engines/eem/site.cpp
@@ -33,6 +33,58 @@
 
 namespace EEM {
 
+namespace {
+
+// Masked blit a Picture into a ManagedSurface. Pixels equal to `transp`
+// (the high byte of `pic.flags`, per `_Rect_Move_Mask @ 1000:03fc`) are
+// skipped. Used by `enterSiteAnim` for both skateboard + KD slide-in
+// passes; the surface is the in-memory frame buffer that gets pushed
+// to the screen each tick.
+void blitFrame(Graphics::ManagedSurface &dst, const Picture &p,
+			   int x, int y, byte transp) {
+	const int w = p.surface.w;
+	const int h = p.surface.h;
+	for (int row = 0; row < h; row++) {
+		const int dstY = y + row;
+		if (dstY < 0 || dstY >= 200)
+			continue;
+		const byte *src = (const byte *)p.surface.getBasePtr(0, row);
+		byte *out = (byte *)dst.getBasePtr(0, dstY);
+		for (int col = 0; col < w; col++) {
+			const int dstX = x + col;
+			if (dstX < 0 || dstX >= 320)
+				continue;
+			if (src[col] != transp)
+				out[dstX] = src[col];
+		}
+	}
+}
+
+// Rotate one VGA palette range by one slot. Mirrors `_ColorCycle @
+// 172b:2015` ??? used by both the per-site Loop-1 ColorCycle entries and
+// the always-on hotspot marching-ants range 0xF9..0xFE.
+void cyclePaletteRange(uint8 start, uint8 end) {
+	if (end <= start)
+		return;
+	const uint count = (uint)end - (uint)start + 1;
+	byte buf[256 * 3];
+	g_system->getPaletteManager()->grabPalette(buf, start, count);
+	const byte savedR = buf[0];
+	const byte savedG = buf[1];
+	const byte savedB = buf[2];
+	for (uint i = 0; i + 1 < count; i++) {
+		buf[i * 3 + 0] = buf[(i + 1) * 3 + 0];
+		buf[i * 3 + 1] = buf[(i + 1) * 3 + 1];
+		buf[i * 3 + 2] = buf[(i + 1) * 3 + 2];
+	}
+	buf[(count - 1) * 3 + 0] = savedR;
+	buf[(count - 1) * 3 + 1] = savedG;
+	buf[(count - 1) * 3 + 2] = savedB;
+	g_system->getPaletteManager()->setPalette(buf, start, count);
+}
+
+} // anonymous namespace
+
 void SiteScreen::enter(uint siteNum) {
 	if (!_mystery || !_mystery->isLoaded()) {
 		warning("SiteScreen::enter: no mystery loaded");
@@ -353,25 +405,6 @@ void SiteScreen::enterSiteAnim() {
 	}
 	g_system->unlockScreen();
 
-	auto blitFrame = [](Graphics::ManagedSurface &dst, const Picture &p,
-						int x, int y, byte transp) {
-		const int w = p.surface.w, h = p.surface.h;
-		for (int row = 0; row < h; row++) {
-			const int dstY = y + row;
-			if (dstY < 0 || dstY >= 200)
-				continue;
-			const byte *src = (const byte *)p.surface.getBasePtr(0, row);
-			byte *out = (byte *)dst.getBasePtr(0, dstY);
-			for (int col = 0; col < w; col++) {
-				const int dstX = x + col;
-				if (dstX < 0 || dstX >= 320)
-					continue;
-				if (src[col] != transp)
-					out[dstX] = src[col];
-			}
-		}
-	};
-
 	// Phase 1 ??? skateboard scroll. `_GetAnimation(6 | 0xe)`.
 	Animation skate;
 	if (_vm->getAni().loadAnimation(kSkateAni, skate) && !skate.empty()) {
@@ -599,31 +632,11 @@ void SiteScreen::applyColorCycles() {
 	// We do the same against ScummVM's palette manager. Always rotate
 	// 0xf9..0xfe for hotspot marching ants (the `_ColorCycle(0xf9,
 	// 0xfe)` call at the bottom of `_DoSiteLoop`'s main loop).
-	auto rotate = [&](uint8 start, uint8 end) {
-		if (end <= start)
-			return;
-		const uint count = (uint)end - (uint)start + 1;
-		byte buf[256 * 3];
-		g_system->getPaletteManager()->grabPalette(buf, start, count);
-		// Save first triplet, shift, restore at end.
-		const byte savedR = buf[0];
-		const byte savedG = buf[1];
-		const byte savedB = buf[2];
-		for (uint i = 0; i + 1 < count; i++) {
-			buf[i * 3 + 0] = buf[(i + 1) * 3 + 0];
-			buf[i * 3 + 1] = buf[(i + 1) * 3 + 1];
-			buf[i * 3 + 2] = buf[(i + 1) * 3 + 2];
-		}
-		buf[(count - 1) * 3 + 0] = savedR;
-		buf[(count - 1) * 3 + 1] = savedG;
-		buf[(count - 1) * 3 + 2] = savedB;
-		g_system->getPaletteManager()->setPalette(buf, start, count);
-	};
 	for (uint i = 0; i < _colorCycles.size(); i++) {
-		rotate(_colorCycles[i].start, _colorCycles[i].end);
+		cyclePaletteRange(_colorCycles[i].start, _colorCycles[i].end);
 	}
 	// Hotspot marching ants ??? always cycled.
-	rotate(0xF9, 0xFE);
+	cyclePaletteRange(0xF9, 0xFE);
 }
 
 void SiteScreen::captureBgSnapshot() {
diff --git a/engines/eem/ui.cpp b/engines/eem/ui.cpp
index 724e88e81ca..2ea76273497 100644
--- a/engines/eem/ui.cpp
+++ b/engines/eem/ui.cpp
@@ -40,6 +40,183 @@
 
 namespace EEM {
 
+namespace {
+
+// Return the next non-empty slot in `slotRects` starting from `from`,
+// stepping by `dir` (+1 or -1) with wraparound. Used by the accuse
+// gallery's keyboard-cycle (TAB / arrow keys) ??? mirrors the way
+// `_PutMouseInRect` skips eliminated suspects in the original.
+int nextLiveSlot(const Common::Array<Common::Rect> &slotRects,
+				 int from, int dir) {
+	const int n = (int)slotRects.size();
+	if (n <= 0)
+		return 0;
+	for (int step = 1; step <= n; step++) {
+		int idx = (from + dir * step) % n;
+		if (idx < 0)
+			idx += n;
+		if (!slotRects[idx].isEmpty())
+			return idx;
+	}
+	return from;
+}
+
+// Snapshot of `doCaseSelection`'s captured locals, used by
+// `drawCaseSelectionFrame` (which replaces the original lambda). Lives
+// on the stack inside `doCaseSelection`; never escapes.
+struct CaseSelectionView {
+	EEMEngine *vm;
+	const Picture *caseBg;
+	bool haveCaseBg;
+	const Animation *kdAnim;
+	bool haveKdAnim;
+	int kdAnimX;
+	int kdAnimY;
+	const char *separator;
+	const char *const *pickLabel;
+	const bool *pickEnabled;
+	uint pick;
+};
+
+// Per-mystery sub-chooser ("Choose A Mystery") view.
+struct CaseSubmenuView {
+	EEMEngine *vm;
+	const Picture *caseBg;
+	bool haveCaseBg;
+	const byte *mysteriesSolved;
+	uint mysteriesSolvedSize;
+	uint sel;
+	uint maxMystery;
+};
+
+void drawCaseSubmenu(const CaseSubmenuView &v) {
+	Graphics::ManagedSurface scratch(320, 200,
+		Graphics::PixelFormat::createFormatCLUT8());
+	scratch.clear();
+	if (v.haveCaseBg) {
+		const int w = MIN<int>(v.caseBg->surface.w, 320);
+		const int h = MIN<int>(v.caseBg->surface.h, 200);
+		for (int row = 0; row < h; row++)
+			memcpy((byte *)scratch.getBasePtr(0, row),
+				   (const byte *)v.caseBg->surface.getBasePtr(0, row), w);
+	}
+	if (v.vm->getFont().isLoaded()) {
+		const int kListX  = 61;
+		const int kListW  = 238 - kListX;
+		const int kListY0 = 35;
+		const int kLineH  = 10;
+		const int kVisible = 12;
+		int top = (int)v.sel - kVisible / 2;
+		if (top < 0)
+			top = 0;
+		if (top + kVisible > (int)v.maxMystery + 1)
+			top = (int)v.maxMystery + 1 - kVisible;
+		for (int r = 0; r < kVisible; r++) {
+			const int idx = top + r;
+			if (idx > (int)v.maxMystery)
+				break;
+			char marker = ' ';
+			if ((uint)idx < v.mysteriesSolvedSize) {
+				if (v.mysteriesSolved[idx] == 2)
+					marker = '*';
+				else if (v.mysteriesSolved[idx] == 1)
+					marker = '+';
+			}
+			const char arrow = ((uint)idx == v.sel) ? '>' : ' ';
+			v.vm->getFont().drawString(&scratch,
+				Common::String::format("%c %c Mystery %d", arrow, marker, idx),
+				kListX, kListY0 + r * kLineH, kListW, 0xF);
+		}
+	}
+	g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
+							   0, 0, 320, 200);
+	g_system->updateScreen();
+}
+
+void drawCaseSelectionFrame(const CaseSelectionView &v) {
+	Graphics::ManagedSurface scratch(320, 200,
+		Graphics::PixelFormat::createFormatCLUT8());
+	scratch.clear();
+	if (v.haveCaseBg) {
+		const int w = MIN<int>(v.caseBg->surface.w, 320);
+		const int h = MIN<int>(v.caseBg->surface.h, 200);
+		for (int row = 0; row < h; row++) {
+			memcpy((byte *)scratch.getBasePtr(0, row),
+				   (const byte *)v.caseBg->surface.getBasePtr(0, row), w);
+		}
+	}
+
+	// KD greeter frame ??? masked-blit current animation cell at
+	// (0x112, 0x50). 100 ms tick matches the engine's `_CheckFrameRate`.
+	if (v.haveKdAnim) {
+		const uint32 now = g_system->getMillis();
+		const uint frameIdx = (uint)((now / 100) % v.kdAnim->size());
+		const Picture &fr = (*v.kdAnim)[frameIdx];
+		const byte transp = (byte)(fr.flags >> 8);
+		for (int row = 0; row < fr.surface.h; row++) {
+			const int dstY = v.kdAnimY + row;
+			if (dstY < 0 || dstY >= 200)
+				continue;
+			const byte *src = (const byte *)fr.surface.getBasePtr(0, row);
+			byte *dst = (byte *)scratch.getBasePtr(0, dstY);
+			for (int col = 0; col < fr.surface.w; col++) {
+				const int dstX = v.kdAnimX + col;
+				if (dstX < 0 || dstX >= 320)
+					continue;
+				if (src[col] != transp)
+					dst[dstX] = src[col];
+			}
+		}
+	}
+	if (v.vm->getFont().isLoaded()) {
+		// `DrawList` @ 1c33:040d coordinates: `_TextBox + 3` for x
+		// and `DAT_29be_0d02` for y. `_TextBox` @ 29be:0d00 holds
+		// {x=58, y=35, x2=238, y2=158}. Matches the blue panel.
+		const int kListX  = 58 + 3;
+		const int kListW  = 238 - kListX;
+		const int kListY0 = 35;
+		const int kLineH  = 10;
+
+		// Top centred "Book %d" / "Challenge Book" title ??? sprintf
+		// format strings at 29be:0deb / 29be:0dfa shown via
+		// `_Show_String(0xc, (0xba - width)/2 + 0x3c, ???)` in the
+		// original. We don't track challenge tier yet so always
+		// show "Book 1".
+		const Common::String book = "Book 1";
+		const int titleW = v.vm->getFont().getStringWidth(book);
+		const int titleX = (0xba - titleW) / 2 + 0x3c;
+		v.vm->getFont().drawString(&scratch, book, titleX, 12, 320, 0xF);
+
+		// Render 11 list rows: separator + menu item pairs.
+		//   row 0  separator
+		//   row 1  Choose A Mystery
+		//   row 2  separator
+		//   row 3  Practice Mystery
+		//   ...
+		//   row 9  See ScrapBook 3
+		//   row 10 separator
+		for (int r = 0; r < 11; r++) {
+			const int y = kListY0 + r * kLineH;
+			if ((r & 1) == 0) {
+				v.vm->getFont().drawString(&scratch, v.separator,
+										   kListX, y, kListW, 0x7);
+				continue;
+			}
+			const uint mp = (uint)(r >> 1);
+			const bool isSel  = (mp == v.pick);
+			const byte color  = isSel             ? 0xF :
+								v.pickEnabled[mp] ? 0x7 : 0x8;
+			v.vm->getFont().drawString(&scratch, v.pickLabel[mp],
+									   kListX, y, kListW, color);
+		}
+	}
+	g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
+							   0, 0, 320, 200);
+	g_system->updateScreen();
+}
+
+} // anonymous namespace
+
 void EEMEngine::doNewPlayer() {
 	// Mirrors `_NewPlayer` @ 1c33:0dda. The original draws background
 	// 0x104 + character peek pic 0x107, then shows "Please type your
@@ -58,28 +235,24 @@ void EEMEngine::doNewPlayer() {
 	Picture bg;
 	const bool haveBG = _picsArchive.getPicture(0x104, bg);
 
-	auto draw = [&]() {
-		Graphics::ManagedSurface scratch(320, 200,
-			Graphics::PixelFormat::createFormatCLUT8());
-		scratch.clear();
-		if (haveBG) {
-			const int w = MIN<int>(bg.surface.w, 320);
-			const int h = MIN<int>(bg.surface.h, 200);
-			for (int row = 0; row < h; row++)
-				memcpy((byte *)scratch.getBasePtr(0, row),
-					   (const byte *)bg.surface.getBasePtr(0, row), w);
-		}
-		// Match the original `_NewPlayer`: `_Show_String(rw=0x28, cl=0x50)`
-		// for the prompt, then `_ShowChar(0x50, x, ???)` for typed input.
-		// (rw=row=y, cl=col=x.) Prompt at (y=40, x=80), input at (y=80, x=80).
-		_font.drawString(&scratch, "Please type your name:", 80, 40, 240, 0xF);
-		Common::String shown = name + "_";
-		_font.drawString(&scratch, shown, 80, 80, 240, 0xF);
-		g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
-								   0, 0, 320, 200);
-		g_system->updateScreen();
-	};
-	draw();
+	// Match the original `_NewPlayer`: `_Show_String(rw=0x28, cl=0x50)`
+	// for the prompt, then `_ShowChar(0x50, x, ???)` for typed input.
+	// (rw=row=y, cl=col=x.) Prompt at (y=40, x=80), input at (y=80, x=80).
+	Graphics::ManagedSurface scratch(320, 200,
+		Graphics::PixelFormat::createFormatCLUT8());
+	scratch.clear();
+	if (haveBG) {
+		const int w = MIN<int>(bg.surface.w, 320);
+		const int h = MIN<int>(bg.surface.h, 200);
+		for (int row = 0; row < h; row++)
+			memcpy((byte *)scratch.getBasePtr(0, row),
+				   (const byte *)bg.surface.getBasePtr(0, row), w);
+	}
+	_font.drawString(&scratch, "Please type your name:", 80, 40, 240, 0xF);
+	_font.drawString(&scratch, name + "_", 80, 80, 240, 0xF);
+	g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
+							   0, 0, 320, 200);
+	g_system->updateScreen();
 
 	while (!shouldQuit()) {
 		Common::Event ev;
@@ -114,8 +287,24 @@ void EEMEngine::doNewPlayer() {
 				dirty = true;
 			}
 		}
-		if (dirty)
-			draw();
+		if (dirty) {
+			// Re-render with the updated `name`. Same body as the
+			// initial render above ??? only `name + "_"` changes.
+			scratch.clear();
+			if (haveBG) {
+				const int w = MIN<int>(bg.surface.w, 320);
+				const int h = MIN<int>(bg.surface.h, 200);
+				for (int row = 0; row < h; row++)
+					memcpy((byte *)scratch.getBasePtr(0, row),
+						   (const byte *)bg.surface.getBasePtr(0, row), w);
+			}
+			_font.drawString(&scratch, "Please type your name:",
+							 80, 40, 240, 0xF);
+			_font.drawString(&scratch, name + "_", 80, 80, 240, 0xF);
+			g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
+									   0, 0, 320, 200);
+			g_system->updateScreen();
+		}
 		g_system->updateScreen();
 		g_system->delayMillis(15);
 	}
@@ -192,102 +381,20 @@ void EEMEngine::doCaseSelection() {
 	const int kKdAnimX = 0x112;
 	const int kKdAnimY = 0x50;
 
-	auto draw = [&]() {
-		Graphics::ManagedSurface scratch(320, 200,
-			Graphics::PixelFormat::createFormatCLUT8());
-		scratch.clear();
-		if (haveCaseBg) {
-			const int w = MIN<int>(caseBg.surface.w, 320);
-			const int h = MIN<int>(caseBg.surface.h, 200);
-			for (int row = 0; row < h; row++) {
-				memcpy((byte *)scratch.getBasePtr(0, row),
-					   (const byte *)caseBg.surface.getBasePtr(0, row), w);
-			}
-		}
-
-		// KD greeter frame ??? masked-blit current animation cell at
-		// (0x112, 0x50). 100 ms tick matches the engine's `_CheckFrameRate`.
-		if (haveKdAnim) {
-			const uint32 now = g_system->getMillis();
-			const uint frameIdx = (uint)((now / 100) % kdAnim.size());
-			const Picture &fr = kdAnim[frameIdx];
-			const byte transp = (byte)(fr.flags >> 8);
-			for (int row = 0; row < fr.surface.h; row++) {
-				const int dstY = kKdAnimY + row;
-				if (dstY < 0 || dstY >= 200)
-					continue;
-				const byte *src = (const byte *)fr.surface.getBasePtr(0, row);
-				byte *dst = (byte *)scratch.getBasePtr(0, dstY);
-				for (int col = 0; col < fr.surface.w; col++) {
-					const int dstX = kKdAnimX + col;
-					if (dstX < 0 || dstX >= 320)
-						continue;
-					if (src[col] != transp)
-						dst[dstX] = src[col];
-				}
-			}
-		}
-		if (_font.isLoaded()) {
-			// `DrawList` @ 1c33:040d coordinates: `_TextBox + 3` for x
-			// and `DAT_29be_0d02` for y. `_TextBox` @ 29be:0d00 holds
-			// {x=58, y=35, x2=238, y2=158}. Matches the blue panel.
-			const int kListX  = 58 + 3;
-			const int kListW  = 238 - kListX;
-			const int kListY0 = 35;
-			const int kLineH  = 10;
-
-			// Top centred "Book %d" / "Challenge Book" title ??? sprintf
-			// format strings at 29be:0deb / 29be:0dfa shown via
-			// `_Show_String(0xc, (0xba - width)/2 + 0x3c, ???)` in the
-			// original. We don't track challenge tier yet so always
-			// show "Book 1".
-			const Common::String book = "Book 1";
-			const int titleW = _font.getStringWidth(book);
-			const int titleX = (0xba - titleW) / 2 + 0x3c;
-			_font.drawString(&scratch, book, titleX, 12, 320, 0xF);
-
-			// Render 11 list rows: separator + menu item pairs.
-			//   row 0  separator
-			//   row 1  Choose A Mystery
-			//   row 2  separator
-			//   row 3  Practice Mystery
-			//   ...
-			//   row 9  See ScrapBook 3
-			//   row 10 separator
-			for (int r = 0; r < 11; r++) {
-				const int y = kListY0 + r * kLineH;
-				if ((r & 1) == 0) {
-					_font.drawString(&scratch, kSeparator, kListX, y, kListW, 0x7);
-					continue;
-				}
-				const uint mp = (uint)(r >> 1);
-				const bool isSel  = (mp == pick);
-				const byte color  = isSel        ? 0xF :
-									kPickEnabled[mp] ? 0x7 : 0x8;
-				_font.drawString(&scratch, kPickLabel[mp], kListX, y, kListW, color);
-			}
-		}
-		g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
-								   0, 0, 320, 200);
-		g_system->updateScreen();
-	};
-
-	auto pickPrev = [&]() {
-		for (int i = 0; i < (int)kNumPicks; i++) {
-			pick = (pick == 0) ? (uint)(kNumPicks - 1) : pick - 1;
-			if (kPickEnabled[pick])
-				break;
-		}
-	};
-	auto pickNext = [&]() {
-		for (int i = 0; i < (int)kNumPicks; i++) {
-			pick = (pick + 1) % kNumPicks;
-			if (kPickEnabled[pick])
-				break;
-		}
-	};
-
-	draw();
+	CaseSelectionView v;
+	v.vm = this;
+	v.caseBg = &caseBg;
+	v.haveCaseBg = haveCaseBg;
+	v.kdAnim = &kdAnim;
+	v.haveKdAnim = haveKdAnim;
+	v.kdAnimX = kKdAnimX;
+	v.kdAnimY = kKdAnimY;
+	v.separator = kSeparator;
+	v.pickLabel = kPickLabel;
+	v.pickEnabled = kPickEnabled;
+	v.pick = pick;
+
+	drawCaseSelectionFrame(v);
 	uint32 lastTick = g_system->getMillis();
 
 	bool exitChosen = false;
@@ -299,7 +406,8 @@ void EEMEngine::doCaseSelection() {
 		const uint32 now = g_system->getMillis();
 		if (haveKdAnim && now - lastTick >= 100) {
 			lastTick = now;
-			draw();
+			v.pick = pick;
+			drawCaseSelectionFrame(v);
 		}
 		while (g_system->getEventManager()->pollEvent(ev)) {
 			if (ev.type == Common::EVENT_QUIT ||
@@ -330,7 +438,8 @@ void EEMEngine::doCaseSelection() {
 						const uint mp = (uint)(row >> 1);
 						if (mp < kNumPicks && kPickEnabled[mp]) {
 							pick = mp;
-							draw();
+							v.pick = pick;
+							drawCaseSelectionFrame(v);
 							continue;
 						}
 					}
@@ -349,19 +458,34 @@ void EEMEngine::doCaseSelection() {
 				break;
 			}
 			if (k == Common::KEYCODE_UP || k == Common::KEYCODE_LEFT) {
-				pickPrev();
-				draw();
+				// Cycle backwards through enabled picks (mirrors the
+				// `_DoChoose` arrow handlers @ 1c33:0514). Loop is
+				// bounded by `kNumPicks` so a row of all-disabled picks
+				// can't spin forever.
+				for (int i = 0; i < (int)kNumPicks; i++) {
+					pick = (pick == 0) ? (uint)(kNumPicks - 1) : pick - 1;
+					if (kPickEnabled[pick])
+						break;
+				}
+				v.pick = pick;
+				drawCaseSelectionFrame(v);
 				continue;
 			}
 			if (k == Common::KEYCODE_DOWN || k == Common::KEYCODE_RIGHT ||
 				k == Common::KEYCODE_TAB) {
-				pickNext();
-				draw();
+				for (int i = 0; i < (int)kNumPicks; i++) {
+					pick = (pick + 1) % kNumPicks;
+					if (kPickEnabled[pick])
+						break;
+				}
+				v.pick = pick;
+				drawCaseSelectionFrame(v);
 				continue;
 			}
 		}
 		if (confirmed) {
-			draw();
+			v.pick = pick;
+			drawCaseSelectionFrame(v);
 			break;
 		}
 		g_system->updateScreen();
@@ -404,51 +528,16 @@ void EEMEngine::doCaseSelection() {
 		}
 	}
 
-	auto drawSubmenu = [&]() {
-		Graphics::ManagedSurface scratch(320, 200,
-			Graphics::PixelFormat::createFormatCLUT8());
-		scratch.clear();
-		if (haveCaseBg) {
-			const int w = MIN<int>(caseBg.surface.w, 320);
-			const int h = MIN<int>(caseBg.surface.h, 200);
-			for (int row = 0; row < h; row++)
-				memcpy((byte *)scratch.getBasePtr(0, row),
-					   (const byte *)caseBg.surface.getBasePtr(0, row), w);
-		}
-		if (_font.isLoaded()) {
-			const int kListX  = 61;
-			const int kListW  = 238 - kListX;
-			const int kListY0 = 35;
-			const int kLineH  = 10;
-			const int kVisible = 12;
-			int top = (int)sel - kVisible / 2;
-			if (top < 0)
-				top = 0;
-			if (top + kVisible > (int)kMaxMystery + 1)
-				top = (int)kMaxMystery + 1 - kVisible;
-			for (int r = 0; r < kVisible; r++) {
-				const int idx = top + r;
-				if (idx > (int)kMaxMystery)
-					break;
-				char marker = ' ';
-				if ((uint)idx < sizeof(_mysteriesSolved)) {
-					if (_mysteriesSolved[idx] == 2)
-						marker = '*';
-					else if (_mysteriesSolved[idx] == 1)
-						marker = '+';
-				}
-				const char arrow = ((uint)idx == sel) ? '>' : ' ';
-				_font.drawString(&scratch,
-								 Common::String::format("%c %c Mystery %d", arrow, marker, idx),
-								 kListX, kListY0 + r * kLineH, kListW, 0xF);
-			}
-		}
-		g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
-								   0, 0, 320, 200);
-		g_system->updateScreen();
-	};
+	CaseSubmenuView sv;
+	sv.vm = this;
+	sv.caseBg = &caseBg;
+	sv.haveCaseBg = haveCaseBg;
+	sv.mysteriesSolved = _mysteriesSolved;
+	sv.mysteriesSolvedSize = sizeof(_mysteriesSolved);
+	sv.sel = sel;
+	sv.maxMystery = kMaxMystery;
 
-	drawSubmenu();
+	drawCaseSubmenu(sv);
 	bool confirmed = false;
 	while (!confirmed && !shouldQuit()) {
 		Common::Event ev;
@@ -468,12 +557,14 @@ void EEMEngine::doCaseSelection() {
 				}
 				if (kUpArrowRect.contains(ev.mouse.x, ev.mouse.y)) {
 					sel = (sel == 0) ? kMaxMystery : sel - 1;
-					drawSubmenu();
+					sv.sel = sel;
+					drawCaseSubmenu(sv);
 					continue;
 				}
 				if (kDnArrowRect.contains(ev.mouse.x, ev.mouse.y)) {
 					sel = (sel >= kMaxMystery) ? 0 : sel + 1;
-					drawSubmenu();
+					sv.sel = sel;
+					drawCaseSubmenu(sv);
 					continue;
 				}
 				if (kListRect.contains(ev.mouse.x, ev.mouse.y)) {
@@ -489,7 +580,8 @@ void EEMEngine::doCaseSelection() {
 					const int idx = top + row;
 					if (idx >= 0 && idx <= (int)kMaxMystery) {
 						sel = (uint)idx;
-						drawSubmenu();
+						sv.sel = sel;
+						drawCaseSubmenu(sv);
 					}
 					continue;
 				}
@@ -507,31 +599,36 @@ void EEMEngine::doCaseSelection() {
 			}
 			if (k >= Common::KEYCODE_0 && k <= Common::KEYCODE_9) {
 				sel = (uint)(k - Common::KEYCODE_0);
-				drawSubmenu();
+				sv.sel = sel;
+				drawCaseSubmenu(sv);
 				continue;
 			}
 			if (k == Common::KEYCODE_DOWN || k == Common::KEYCODE_TAB) {
 				sel = (sel >= kMaxMystery) ? 0 : sel + 1;
-				drawSubmenu();
+				sv.sel = sel;
+				drawCaseSubmenu(sv);
 				continue;
 			}
 			if (k == Common::KEYCODE_UP) {
 				sel = (sel == 0) ? kMaxMystery : sel - 1;
-				drawSubmenu();
+				sv.sel = sel;
+				drawCaseSubmenu(sv);
 				continue;
 			}
 			if (k == Common::KEYCODE_PAGEDOWN) {
 				sel = (sel + 10 > kMaxMystery) ? kMaxMystery : sel + 10;
-				drawSubmenu();
+				sv.sel = sel;
+				drawCaseSubmenu(sv);
 				continue;
 			}
 			if (k == Common::KEYCODE_PAGEUP) {
 				sel = (sel < 10) ? 0 : sel - 10;
-				drawSubmenu();
+				sv.sel = sel;
+				drawCaseSubmenu(sv);
 				continue;
 			}
-			if (k == Common::KEYCODE_HOME) { sel = 0; drawSubmenu(); continue; }
-			if (k == Common::KEYCODE_END)  { sel = kMaxMystery; drawSubmenu(); continue; }
+			if (k == Common::KEYCODE_HOME) { sel = 0; sv.sel = sel; drawCaseSubmenu(sv); continue; }
+			if (k == Common::KEYCODE_END)  { sel = kMaxMystery; sv.sel = sel; drawCaseSubmenu(sv); continue; }
 		}
 		g_system->updateScreen();
 		g_system->delayMillis(15);
@@ -585,7 +682,6 @@ void EEMEngine::doNotebook() {
 	//   rect 8 (35,111)  ??? 0x03ed = `_NextScreen = 3`             (SITE)
 	//   rect 9 (0,0)     ??? 0x03ed = same as rect 8
 	//   rect 10 (66,79)  ??? 0x03f9 = `_InterfaceHelp(0)`           (note-area help)
-	const Common::Rect kNotebookRect(78, 12, 288, 152);
 	const Common::Rect kBtnHelp1   ( 93, 174, 115, 190);  // [1] HELP
 	const Common::Rect kBtnGallery (157, 174, 178, 190);  // [2] GALLERY
 	const Common::Rect kBtnPartner (  5,  80,  44, 110);  // [3] KD HELP
@@ -600,165 +696,9 @@ void EEMEngine::doNotebook() {
 
 	int page = 0;
 	int hoveredNoteSlot = -1;
+	(void)hoveredNoteSlot;
 
-	// Build a list of found-clue indices, identical ordering to the
-	// original's iteration through `_CluesFound[]`.
-	auto buildFound = [&]() {
-		Common::Array<uint> found;
-		for (uint i = 0; i < Mystery::kCluesFoundCap; i++)
-			if (_mystery._cluesFound[i])
-				found.push_back(i);
-		return found;
-	};
-
-	auto draw = [&]() {
-		Graphics::ManagedSurface scratch(320, 200,
-			Graphics::PixelFormat::createFormatCLUT8());
-		scratch.clear();
-
-		// PIC 0x3f frame.
-		Picture frame;
-		if (_picsArchive.getPicture(0x3f, frame)) {
-			const int w = MIN<int>(frame.surface.w, 320);
-			const int h = MIN<int>(frame.surface.h, 200);
-			for (int row = 0; row < h; row++)
-				memcpy((byte *)scratch.getBasePtr(0, row),
-					   (const byte *)frame.surface.getBasePtr(0, row), w);
-		}
-
-		// Partner sprite at (5, 80). Anim 1 for Jake, 0xb (11) for Jenny.
-		const uint partnerAnim = (_partner == 0) ? 1 : 0xb;
-		Animation partnerAni;
-		if (_aniArchive.loadAnimation(partnerAnim, partnerAni) && !partnerAni.empty()) {
-			const uint32 now = g_system->getMillis();
-			const uint frameIdx = (uint)((now / 100) % partnerAni.size());
-			const Picture &fr = partnerAni[frameIdx];
-			const byte transp = (byte)(fr.flags >> 8);
-			for (int row = 0; row < fr.surface.h; row++) {
-				const int dstY = 80 + row;
-				if (dstY < 0 || dstY >= 200)
-					continue;
-				const byte *src = (const byte *)fr.surface.getBasePtr(0, row);
-				byte *dst = (byte *)scratch.getBasePtr(0, dstY);
-				for (int col = 0; col < fr.surface.w; col++) {
-					const int dstX = 5 + col;
-					if (dstX < 0 || dstX >= 320)
-						continue;
-					if (src[col] != transp)
-						dst[dstX] = src[col];
-				}
-			}
-		}
-
-		// Notes ??? `_DrawNotes` walks `_NoteIndex` for the current page,
-		// rendering each found clue's text inside `_NotebookRect` with
-		// word-wrap. Selected clues are highlighted (color 0x3c in the
-		// original's case-briefing palette).
-		const Common::Array<uint> found = buildFound();
-		const byte *ni = _mystery.noteIndex();
-		const uint16 niCount = _mystery.noteIndexCount();
-
-		const int kRectX = kNotebookRect.left;
-		const int kRectY = kNotebookRect.top;
-		const int kRectW = kNotebookRect.width();
-		const int kRectH = kNotebookRect.height();
-
-		// Walk forward to the start clue of the current page.
-		// Each page renders as many clues as fit in `kRectH`.
-		int clueCursor = 0;
-		Common::Array<int> pageStarts;
-		pageStarts.push_back(0);
-		{
-			const int lineH = _font.getFontHeight() + 1;
-			int y = kRectY;
-			while (clueCursor < (int)found.size()) {
-				const uint clueId = found[clueCursor];
-				Common::String txt;
-				if (ni && clueId < niCount) {
-					const uint16 textOff = READ_LE_UINT16(ni + clueId * 4);
-					txt = parseString(_mystery.textAt(textOff),
-									  _playerName, _partner);
-				}
-				// Measure height by wrapping the text without drawing.
-				Common::Array<Common::String> wrapped;
-				_font.wordWrapText(txt, kRectW, wrapped);
-				const int h = (int)wrapped.size() * lineH;
-				if (y + h + 7 > kRectY + kRectH) {
-					// Page break before this clue.
-					y = kRectY;
-					pageStarts.push_back(clueCursor);
-				}
-				y += h + 7;
-				clueCursor++;
-			}
-			if (page >= (int)pageStarts.size())
-				page = (int)pageStarts.size() - 1;
-			if (page < 0)
-				page = 0;
-		}
-
-		// Track per-slot rectangles so the click handler can map a
-		// click in `kNoteArea` back to a clue index.
-		Common::Array<Common::Rect> slotRects;
-		Common::Array<uint> slotClues;
-
-		const int startClue = (page < (int)pageStarts.size())
-								? pageStarts[page] : 0;
-		const int endClue   = (page + 1 < (int)pageStarts.size())
-								? pageStarts[page + 1] : (int)found.size();
-
-		int y = kRectY;
-		for (int i = startClue; i < endClue; i++) {
-			const uint clueId = found[i];
-			Common::String txt;
-			if (ni && clueId < niCount) {
-				const uint16 textOff = READ_LE_UINT16(ni + clueId * 4);
-				txt = parseString(_mystery.textAt(textOff),
-								  _playerName, _partner);
-			}
-			if (txt.empty())
-				txt = Common::String::format("clue %u", clueId);
-			// Per `_DrawNotes @ 161e:01d0`: text uses
-			// `_NoteUnselectedColor` (0x5c=cyan) for unselected and 0x3c
-			// (light yellow-white) for selected. Both contrast cleanly
-			// with the PDA screen's natural blue, so we draw text
-			// directly on PIC 0x3f without an extra fill rectangle ???
-			// matches the original design.
-			Common::Array<Common::String> wrapped;
-			_font.wordWrapText(txt, kRectW, wrapped);
-			const int lineH = _font.getFontHeight() + 1;
-			const int h = (int)wrapped.size() * lineH;
-			const byte color = _mystery._noteSelected[clueId] ? 0x3C : 0x5C;
-			for (uint li = 0; li < wrapped.size(); li++) {
-				_font.drawString(&scratch, wrapped[li], kRectX,
-								 y + (int)li * lineH, kRectW, color);
-			}
-			slotRects.push_back(Common::Rect(kRectX, y,
-											  kRectX + kRectW, y + h));
-			slotClues.push_back(clueId);
-			y += h + 7;
-		}
-
-		// Page indicator + selected-points counter directly on PIC.
-		_font.drawString(&scratch, Common::String::format("p%d/%d",
-								   page + 1, (int)pageStarts.size()),
-						 270, 4, 320, 0x5C);
-		_font.drawString(&scratch, Common::String::format("%d pts",
-								   _mystery.selectedPoints()),
-						 270, 14, 320, 0x5C);
-		(void)hoveredNoteSlot;
-
-		g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
-								   0, 0, 320, 200);
-		g_system->updateScreen();
-
-		// Stash slot info on the captures so the click handler below
-		// can use it via the closure.
-		_notebookSlotRects = slotRects;
-		_notebookSlotClues = slotClues;
-	};
-
-	draw();
+	drawNotebookFrame(page);
 
 	uint32 lastDraw = g_system->getMillis();
 
@@ -867,7 +807,7 @@ void EEMEngine::doNotebook() {
 		const uint32 now = g_system->getMillis();
 		// Re-render every 100 ms so the partner sprite cycles frames.
 		if (dirty || now - lastDraw >= 100) {
-			draw();
+			drawNotebookFrame(page);
 			lastDraw = now;
 		}
 		g_system->updateScreen();
@@ -875,6 +815,165 @@ void EEMEngine::doNotebook() {
 	}
 }
 
+void EEMEngine::drawNotebookFrame(int &page) {
+	// PDA notebook redraw ??? formerly the `draw` lambda inside `doNotebook`.
+	// Mirrors `_DrawNotes @ 161e:01d0` for the per-page note layout, plus
+	// the partner-sprite blit at (5, 80) (`_NewAnimation` from
+	// `_DoNotebook @ 161e:0500`). Uses `_notebookSlotRects` /
+	// `_notebookSlotClues` to publish the per-page slot layout to the
+	// click handler in `doNotebook`.
+	const Common::Rect kNotebookRect(78, 12, 288, 152);
+
+	Graphics::ManagedSurface scratch(320, 200,
+		Graphics::PixelFormat::createFormatCLUT8());
+	scratch.clear();
+
+	// PIC 0x3f frame.
+	Picture frame;
+	if (_picsArchive.getPicture(0x3f, frame)) {
+		const int w = MIN<int>(frame.surface.w, 320);
+		const int h = MIN<int>(frame.surface.h, 200);
+		for (int row = 0; row < h; row++)
+			memcpy((byte *)scratch.getBasePtr(0, row),
+				   (const byte *)frame.surface.getBasePtr(0, row), w);
+	}
+
+	// Partner sprite at (5, 80). Anim 1 for Jake, 0xb (11) for Jenny.
+	const uint partnerAnim = (_partner == 0) ? 1 : 0xb;
+	Animation partnerAni;
+	if (_aniArchive.loadAnimation(partnerAnim, partnerAni) && !partnerAni.empty()) {
+		const uint32 now = g_system->getMillis();
+		const uint frameIdx = (uint)((now / 100) % partnerAni.size());
+		const Picture &fr = partnerAni[frameIdx];
+		const byte transp = (byte)(fr.flags >> 8);
+		for (int row = 0; row < fr.surface.h; row++) {
+			const int dstY = 80 + row;
+			if (dstY < 0 || dstY >= 200)
+				continue;
+			const byte *src = (const byte *)fr.surface.getBasePtr(0, row);
+			byte *dst = (byte *)scratch.getBasePtr(0, dstY);
+			for (int col = 0; col < fr.surface.w; col++) {
+				const int dstX = 5 + col;
+				if (dstX < 0 || dstX >= 320)
+					continue;
+				if (src[col] != transp)
+					dst[dstX] = src[col];
+			}
+		}
+	}
+
+	// Notes ??? `_DrawNotes` walks `_NoteIndex` for the current page,
+	// rendering each found clue's text inside `_NotebookRect` with
+	// word-wrap. Selected clues are highlighted (color 0x3c in the
+	// original's case-briefing palette).
+	// Build a list of found-clue indices, identical ordering to the
+	// original's iteration through `_CluesFound[]`.
+	Common::Array<uint> found;
+	for (uint i = 0; i < Mystery::kCluesFoundCap; i++) {
+		if (_mystery._cluesFound[i])
+			found.push_back(i);
+	}
+	const byte *ni = _mystery.noteIndex();
+	const uint16 niCount = _mystery.noteIndexCount();
+
+	const int kRectX = kNotebookRect.left;
+	const int kRectY = kNotebookRect.top;
+	const int kRectW = kNotebookRect.width();
+	const int kRectH = kNotebookRect.height();
+
+	// Walk forward to the start clue of the current page.
+	// Each page renders as many clues as fit in `kRectH`.
+	int clueCursor = 0;
+	Common::Array<int> pageStarts;
+	pageStarts.push_back(0);
+	{
+		const int lineH = _font.getFontHeight() + 1;
+		int y = kRectY;
+		while (clueCursor < (int)found.size()) {
+			const uint clueId = found[clueCursor];
+			Common::String txt;
+			if (ni && clueId < niCount) {
+				const uint16 textOff = READ_LE_UINT16(ni + clueId * 4);
+				txt = parseString(_mystery.textAt(textOff),
+								  _playerName, _partner);
+			}
+			// Measure height by wrapping the text without drawing.
+			Common::Array<Common::String> wrapped;
+			_font.wordWrapText(txt, kRectW, wrapped);
+			const int h = (int)wrapped.size() * lineH;
+			if (y + h + 7 > kRectY + kRectH) {
+				// Page break before this clue.
+				y = kRectY;
+				pageStarts.push_back(clueCursor);
+			}
+			y += h + 7;
+			clueCursor++;
+		}
+		if (page >= (int)pageStarts.size())
+			page = (int)pageStarts.size() - 1;
+		if (page < 0)
+			page = 0;
+	}
+
+	// Track per-slot rectangles so the click handler can map a
+	// click in `kNoteArea` back to a clue index.
+	Common::Array<Common::Rect> slotRects;
+	Common::Array<uint> slotClues;
+
+	const int startClue = (page < (int)pageStarts.size())
+							? pageStarts[page] : 0;
+	const int endClue   = (page + 1 < (int)pageStarts.size())
+							? pageStarts[page + 1] : (int)found.size();
+
+	int y = kRectY;
+	for (int i = startClue; i < endClue; i++) {
+		const uint clueId = found[i];
+		Common::String txt;
+		if (ni && clueId < niCount) {
+			const uint16 textOff = READ_LE_UINT16(ni + clueId * 4);
+			txt = parseString(_mystery.textAt(textOff),
+							  _playerName, _partner);
+		}
+		if (txt.empty())
+			txt = Common::String::format("clue %u", clueId);
+		// Per `_DrawNotes @ 161e:01d0`: text uses
+		// `_NoteUnselectedColor` (0x5c=cyan) for unselected and 0x3c
+		// (light yellow-white) for selected. Both contrast cleanly
+		// with the PDA screen's natural blue, so we draw text
+		// directly on PIC 0x3f without an extra fill rectangle ???
+		// matches the original design.
+		Common::Array<Common::String> wrapped;
+		_font.wordWrapText(txt, kRectW, wrapped);
+		const int lineH = _font.getFontHeight() + 1;
+		const int h = (int)wrapped.size() * lineH;
+		const byte color = _mystery._noteSelected[clueId] ? 0x3C : 0x5C;
+		for (uint li = 0; li < wrapped.size(); li++) {
+			_font.drawString(&scratch, wrapped[li], kRectX,
+							 y + (int)li * lineH, kRectW, color);
+		}
+		slotRects.push_back(Common::Rect(kRectX, y,
+										  kRectX + kRectW, y + h));
+		slotClues.push_back(clueId);
+		y += h + 7;
+	}
+
+	// Page indicator + selected-points counter directly on PIC.
+	_font.drawString(&scratch, Common::String::format("p%d/%d",
+							   page + 1, (int)pageStarts.size()),
+					 270, 4, 320, 0x5C);
+	_font.drawString(&scratch, Common::String::format("%d pts",
+							   _mystery.selectedPoints()),
+					 270, 14, 320, 0x5C);
+
+	g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
+							   0, 0, 320, 200);
+	g_system->updateScreen();
+
+	// Publish slot info to `doNotebook`'s click handler.
+	_notebookSlotRects = slotRects;
+	_notebookSlotClues = slotClues;
+}
+
 void EEMEngine::doGallery() {
 	// Mirrors `_DoGallery @ 158f:065b` and `_DrawGallery @ 158f:0046`.
 	// Verified directly from the disassembly:
@@ -908,26 +1007,11 @@ void EEMEngine::doGallery() {
 
 	CursorMan.showMouse(true);
 
-	struct Slot { int x; int y; };
-	static const Slot kGallerySlots[5] = {
-		{  83,  14 }, // 0
-		{ 155,  14 }, // 1
-		{ 227,  14 }, // 2
-		{ 119,  90 }, // 3
-		{ 191,  90 }  // 4
-	};
-
-	// Pre-load static elements once.
+	// Pre-load PIC 0x3f for the MoreInfo backdrop blit further down.
+	// (`drawGalleryFrame` reloads it on its own per-call too.)
 	Picture galBg;
 	const bool haveBg = _picsArchive.getPicture(0x3f, galBg);
 
-	// Gallery partner anim ??? `_DoGallery` calls `_GetAnimation(uVar6)` with
-	// uVar6 = 2 (Jake) / 0x10 (Jenny). Different from PDA (1 / 0xb).
-	const uint partnerAnim = (_partner == 0) ? 2 : 0x10;
-	Animation partnerAni;
-	const bool havePartner = _aniArchive.loadAnimation(partnerAnim, partnerAni)
-							  && !partnerAni.empty();
-
 	const uint8 num = _mystery.numSuspects();
 
 	// Cache slot rects for click hit-testing.
@@ -939,116 +1023,7 @@ void EEMEngine::doGallery() {
 		slotSuspect[i] = -1;
 	}
 
-	auto drawFrame = [&]() {
-		Graphics::ManagedSurface scratch(320, 200,
-			Graphics::PixelFormat::createFormatCLUT8());
-		scratch.clear();
-
-		if (haveBg) {
-			const int bw = MIN<int>(galBg.surface.w, 320);
-			const int bh = MIN<int>(galBg.surface.h, 200);
-			for (int row = 0; row < bh; row++) {
-				memcpy((byte *)scratch.getBasePtr(0, row),
-					   (const byte *)galBg.surface.getBasePtr(0, row), bw);
-			}
-		}
-
-		// Partner sprite frame @ (5, 0x50).
-		if (havePartner) {
-			const uint32 now = g_system->getMillis();
-			const uint frameIdx = (uint)((now / 100) % partnerAni.size());
-			const Picture &fr = partnerAni[frameIdx];
-			const byte transp = (byte)(fr.flags >> 8);
-			const int px = 5, py = 0x50;
-			for (int row = 0; row < fr.surface.h; row++) {
-				const int dstY = py + row;
-				if (dstY < 0 || dstY >= 200)
-					continue;
-				const byte *src = (const byte *)fr.surface.getBasePtr(0, row);
-				byte *dst = (byte *)scratch.getBasePtr(0, dstY);
-				for (int col = 0; col < fr.surface.w; col++) {
-					const int dstX = px + col;
-					if (dstX < 0 || dstX >= 320)
-						continue;
-					if (src[col] != transp)
-						dst[dstX] = src[col];
-				}
-			}
-		}
-
-		// Portraits ??? `_DrawGallery @ 158f:0046` walks suspects 0..N-1
-		// and only renders those flagged in `_InGallery[NewOrder[i]]`.
-		// Undiscovered slots are left empty in the original. We render
-		// a darkened placeholder + "?" so the player has visual feedback
-		// that suspects exist but are still unknown.
-		uint discoveredCount = 0;
-		for (uint i = 0; i < num && i < Mystery::kGalleryCap; i++) {
-			slotRects[i] = Common::Rect();
-			slotSuspect[i] = -1;
-
-			const uint8 phys = _mystery._newOrder[i];
-			if (phys >= 5)
-				continue;
-			const Slot &s = kGallerySlots[phys];
-
-			const bool discovered = _mystery._inGallery[phys] != 0;
-			if (discovered) {
-				const uint16 picId = READ_LE_UINT16(gd + i * 0x46);
-				Picture portrait;
-				if (picId == 0 ||
-					!_picsArchive.getPicture(picId, portrait))
-					continue;
-
-				const int placeX = s.x;
-				const int placeY = s.y + (0x48 - portrait.surface.h);
-				const byte transp = (byte)(portrait.flags >> 8);
-				const int w = MIN<int>(portrait.surface.w, 320 - placeX);
-				const int h = MIN<int>(portrait.surface.h, 200 - placeY);
-				if (w <= 0 || h <= 0)
-					continue;
-				for (int row = 0; row < h; row++) {
-					const int dstY = placeY + row;
-					if (dstY < 0)
-						continue;
-					const byte *src =
-						(const byte *)portrait.surface.getBasePtr(0, row);
-					byte *dst = (byte *)scratch.getBasePtr(0, dstY);
-					for (int col = 0; col < w; col++) {
-						const int dstX = placeX + col;
-						if (src[col] != transp)
-							dst[dstX] = src[col];
-					}
-				}
-				slotRects[i] = Common::Rect(placeX, placeY,
-											 placeX + w, placeY + h);
-				slotSuspect[i] = (int)i;
-				discoveredCount++;
-			} else {
-				// Undiscovered placeholder ??? small framed "?" box at
-				// (s.x, s.y) sized 0x40 ?? 0x48 (typical portrait size).
-				const int phW = 0x40, phH = 0x48;
-				const int phX = s.x, phY = s.y;
-				if (phX + phW <= 320 && phY + phH <= 200) {
-					scratch.fillRect(Common::Rect(phX, phY,
-						phX + phW, phY + phH), 0x20);
-					scratch.frameRect(Common::Rect(phX, phY,
-						phX + phW, phY + phH), 0x5C);
-					if (_font.isLoaded()) {
-						_font.drawString(&scratch, "?",
-							phX + phW / 2 - 3,
-							phY + phH / 2 - 4, phW, 0x5C);
-					}
-				}
-			}
-		}
-		(void)discoveredCount;
-
-		g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
-								   0, 0, 320, 200);
-		g_system->updateScreen();
-	};
-
-	drawFrame();
+	drawGalleryFrame(gd, num, slotRects, slotSuspect);
 	uint32 lastDraw = g_system->getMillis();
 
 	while (!shouldQuit()) {
@@ -1258,7 +1233,7 @@ void EEMEngine::doGallery() {
 						// Force gallery redraw immediately so the
 						// player isn't left looking at the dismissed
 						// MoreInfo screen until the next 100 ms tick.
-						drawFrame();
+						drawGalleryFrame(gd, num, slotRects, slotSuspect);
 						lastDraw = g_system->getMillis();
 						clicked = true;
 						break;
@@ -1272,13 +1247,139 @@ void EEMEngine::doGallery() {
 
 		const uint32 now = g_system->getMillis();
 		if (now - lastDraw >= 100) {
-			drawFrame();
+			drawGalleryFrame(gd, num, slotRects, slotSuspect);
 			lastDraw = now;
 		}
 		g_system->delayMillis(15);
 	}
 }
 
+void EEMEngine::drawGalleryFrame(const byte *gd, uint8 numSuspects,
+								  Common::Array<Common::Rect> &slotRects,
+								  Common::Array<int> &slotSuspect) {
+	// Gallery redraw ??? formerly the `drawFrame` lambda inside `doGallery`.
+	// Mirrors `_DrawGallery @ 158f:0046`: PIC 0x3f frame + partner sprite
+	// at (5, 0x50) + suspect portraits in their `_NewOrder` slots.
+	struct Slot { int x; int y; };
+	static const Slot kGallerySlots[5] = {
+		{  83,  14 }, // 0
+		{ 155,  14 }, // 1
+		{ 227,  14 }, // 2
+		{ 119,  90 }, // 3
+		{ 191,  90 }  // 4
+	};
+
+	Picture galBg;
+	const bool haveBg = _picsArchive.getPicture(0x3f, galBg);
+	const uint partnerAnim = (_partner == 0) ? 2 : 0x10;
+	Animation partnerAni;
+	const bool havePartner = _aniArchive.loadAnimation(partnerAnim, partnerAni)
+							  && !partnerAni.empty();
+
+	Graphics::ManagedSurface scratch(320, 200,
+		Graphics::PixelFormat::createFormatCLUT8());
+	scratch.clear();
+
+	if (haveBg) {
+		const int bw = MIN<int>(galBg.surface.w, 320);
+		const int bh = MIN<int>(galBg.surface.h, 200);
+		for (int row = 0; row < bh; row++) {
+			memcpy((byte *)scratch.getBasePtr(0, row),
+				   (const byte *)galBg.surface.getBasePtr(0, row), bw);
+		}
+	}
+
+	// Partner sprite frame @ (5, 0x50).
+	if (havePartner) {
+		const uint32 now = g_system->getMillis();
+		const uint frameIdx = (uint)((now / 100) % partnerAni.size());
+		const Picture &fr = partnerAni[frameIdx];
+		const byte transp = (byte)(fr.flags >> 8);
+		const int px = 5;
+		const int py = 0x50;
+		for (int row = 0; row < fr.surface.h; row++) {
+			const int dstY = py + row;
+			if (dstY < 0 || dstY >= 200)
+				continue;
+			const byte *src = (const byte *)fr.surface.getBasePtr(0, row);
+			byte *dst = (byte *)scratch.getBasePtr(0, dstY);
+			for (int col = 0; col < fr.surface.w; col++) {
+				const int dstX = px + col;
+				if (dstX < 0 || dstX >= 320)
+					continue;
+				if (src[col] != transp)
+					dst[dstX] = src[col];
+			}
+		}
+	}
+
+	// Portraits ??? `_DrawGallery @ 158f:0046` walks suspects 0..N-1 and
+	// only renders those flagged in `_InGallery[NewOrder[i]]`.
+	for (uint i = 0; i < numSuspects && i < Mystery::kGalleryCap; i++) {
+		slotRects[i] = Common::Rect();
+		slotSuspect[i] = -1;
+
+		const uint8 phys = _mystery._newOrder[i];
+		if (phys >= 5)
+			continue;
+		const Slot &s = kGallerySlots[phys];
+
+		const bool discovered = _mystery._inGallery[phys] != 0;
+		if (discovered) {
+			const uint16 picId = READ_LE_UINT16(gd + i * 0x46);
+			Picture portrait;
+			if (picId == 0 ||
+				!_picsArchive.getPicture(picId, portrait))
+				continue;
+
+			const int placeX = s.x;
+			const int placeY = s.y + (0x48 - portrait.surface.h);
+			const byte transp = (byte)(portrait.flags >> 8);
+			const int w = MIN<int>(portrait.surface.w, 320 - placeX);
+			const int h = MIN<int>(portrait.surface.h, 200 - placeY);
+			if (w <= 0 || h <= 0)
+				continue;
+			for (int row = 0; row < h; row++) {
+				const int dstY = placeY + row;
+				if (dstY < 0)
+					continue;
+				const byte *src =
+					(const byte *)portrait.surface.getBasePtr(0, row);
+				byte *dst = (byte *)scratch.getBasePtr(0, dstY);
+				for (int col = 0; col < w; col++) {
+					const int dstX = placeX + col;
+					if (src[col] != transp)
+						dst[dstX] = src[col];
+				}
+			}
+			slotRects[i] = Common::Rect(placeX, placeY,
+										 placeX + w, placeY + h);
+			slotSuspect[i] = (int)i;
+		} else {
+			// Undiscovered placeholder ??? small framed "?" box.
+			const int phW = 0x40;
+			const int phH = 0x48;
+			const int phX = s.x;
+			const int phY = s.y;
+			if (phX + phW <= 320 && phY + phH <= 200) {
+				scratch.fillRect(Common::Rect(phX, phY,
+					phX + phW, phY + phH), 0x20);
+				scratch.frameRect(Common::Rect(phX, phY,
+					phX + phW, phY + phH), 0x5C);
+				if (_font.isLoaded()) {
+					_font.drawString(&scratch, "?",
+						phX + phW / 2 - 3,
+						phY + phH / 2 - 4, phW, 0x5C);
+				}
+			}
+		}
+	}
+
+	g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
+							   0, 0, 320, 200);
+	g_system->updateScreen();
+}
+
 void EEMEngine::doBigMap() {
 	// Two-stage flow that mirrors the original screen-1 wrapper at
 	// 20fe:120b and `_DoBigMap @ 20fe:09e7`:
@@ -1322,128 +1423,7 @@ void EEMEngine::doBigMap() {
 	// STAGE 1 ??? Overview: PIC 0x42 + clickable site icons.
 	// ------------------------------------------------------------------
 
-	// `_DoBigMap @ 20fe:09e7` (20fe:0a44-0a99) registers a partner sprite
-	// on the overview frame. The animation depends on `_LastScreen`:
-	//   * When LastScreen == 2 (came from the site loop) the original
-	//     plays an entrance anim (`anum-1` for Jake / Jenny) at
-	//     (0x102, 0x50), then on END swaps to the idle anim at (0xfd,
-	//     0x50). We don't track LastScreen finely enough to distinguish,
-	//     so we render the IDLE pose at (0xfd, 0x50) which is what the
-	//     player sees the rest of the time anyway.
-	//   * Idle anim ID: Jake = 0x14 (20), Jenny = 0x12 (18).
-	const uint kMapAniId = (_partner == 0) ? 0x14 : 0x12;
-	Animation mapAnim;
-	const bool haveMapAnim = _aniArchive.loadAnimation(kMapAniId, mapAnim)
-							   && !mapAnim.empty();
-	const int kMapAnimX = 0xfd;
-	const int kMapAnimY = 0x50;
-
-	auto drawOverview = [&]() {
-		Graphics::ManagedSurface scratch(320, 200,
-			Graphics::PixelFormat::createFormatCLUT8());
-		scratch.clear();
-
-		Picture frame;
-		if (_picsArchive.getPicture(0x42, frame)) {
-			const int w = MIN<int>(frame.surface.w, 320);
-			const int h = MIN<int>(frame.surface.h, 200);
-			for (int row = 0; row < h; row++)
-				memcpy((byte *)scratch.getBasePtr(0, row),
-					   (const byte *)frame.surface.getBasePtr(0, row), w);
-		}
-
-		// Marker PICs from `_main @ 1a35:0f59`. Three globals are filled
-		// once at boot via `_GetPicture` (1-based IDs ??? entries N-1):
-		//   _DoneMarker  = PIC 0x20d  (already-searched site)
-		//   _SiteMarker  = PIC 0xc5   (default available site)
-		//   _CrimeMarker = PIC 0xc6   (crime-scene flag set)
-		// Picked per-site by `_DrawBigMapButtons @ 20fe:0877`:
-		//   1. SaveSiteComplete[i] ??? DoneMarker
-		//   2. else MapData[+0xc] != 0 ??? CrimeMarker
-		//   3. else SiteMarker
-		Picture done, normal, crimeM;
-		const bool haveDone   = _picsArchive.getPicture(0x20d, done);
-		const bool haveNormal = _picsArchive.getPicture(0xc5,  normal);
-		const bool haveCrime  = _picsArchive.getPicture(0xc6,  crimeM);
-
-		auto blitMarker = [&](const Picture &m, int x, int y) {
-			const byte transp = (byte)(m.flags >> 8);
-			for (int row = 0; row < m.surface.h; row++) {
-				const int dstY = y + row;
-				if (dstY < 0 || dstY >= 200)
-					continue;
-				const byte *src = (const byte *)m.surface.getBasePtr(0, row);
-				byte *dst = (byte *)scratch.getBasePtr(0, dstY);
-				for (int col = 0; col < m.surface.w; col++) {
-					const int dstX = x + col;
-					if (dstX < 0 || dstX >= 320)
-						continue;
-					if (src[col] != transp)
-						dst[dstX] = src[col];
-				}
-			}
-		};
-
-		for (uint i = 0; i < _mystery.numSites(); i++) {
-			if (!_mystery._onSites[i] && i != _mystery._siteNumber)
-				continue;
-			const byte *entry = _mystery.mapEntry(i);
-			if (!entry)
-				continue;
-			const uint16 mx    = READ_LE_UINT16(entry + 0x4);
-			const uint16 my    = READ_LE_UINT16(entry + 0x6);
-			const uint16 crime = READ_LE_UINT16(entry + 0xc);
-			const bool   done_ = (i < Mystery::kVisitedSiteCap)
-								  && _mystery._visitedSite[i];
-
-			const Picture *m = nullptr;
-			if (done_ && haveDone)
-				m = &done;
-			else if (crime != 0 && haveCrime)
-				m = &crimeM;
-			else if (haveNormal)
-				m = &normal;
-
-			if (m)
-				blitMarker(*m, (int)mx, (int)my);
-			else {
-				// Fallback if the markers couldn't be loaded.
-				const Common::Rect mark(mx - 3, my - 3, mx + 4, my + 4);
-				scratch.fillRect(mark, 0x0F);
-			}
-		}
-
-		// Partner sprite ??? masked-blit at (0xfd, 0x50). Same per-tick
-		// idle the original would run via `_UpdateAnimations` once the
-		// entrance one-shot transitions out (see `_DoBigMap` 0xae3-0xae7
-		// where it swaps animId on the 0x80 marker).
-		if (haveMapAnim) {
-			const uint32 now = g_system->getMillis();
-			const uint frameIdx = (uint)((now / 100) % mapAnim.size());
-			const Picture &fr = mapAnim[frameIdx];
-			const byte transp = (byte)(fr.flags >> 8);
-			for (int row = 0; row < fr.surface.h; row++) {
-				const int dstY = kMapAnimY + row;
-				if (dstY < 0 || dstY >= 200)
-					continue;
-				const byte *src = (const byte *)fr.surface.getBasePtr(0, row);
-				byte *dst = (byte *)scratch.getBasePtr(0, dstY);
-				for (int col = 0; col < fr.surface.w; col++) {
-					const int dstX = kMapAnimX + col;
-					if (dstX < 0 || dstX >= 320)
-						continue;
-					if (src[col] != transp)
-						dst[dstX] = src[col];
-				}
-			}
-		}
-
-		g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
-								   0, 0, 320, 200);
-		g_system->updateScreen();
-	};
-
-	drawOverview();
+	drawBigMapOverview();
 	uint32 mapLastTick = g_system->getMillis();
 
 	// Static rectangles read directly from the binary at the labelled
@@ -1492,9 +1472,9 @@ void EEMEngine::doBigMap() {
 		// Cycle the partner-sprite frame every 100 ms (matching the
 		// original's `_CheckFrameRate` cadence inside `_DoBigMap`).
 		const uint32 now = g_system->getMillis();
-		if (haveMapAnim && now - mapLastTick >= 100) {
+		if (now - mapLastTick >= 100) {
 			mapLastTick = now;
-			drawOverview();
+			drawBigMapOverview();
 		}
 		g_system->updateScreen();
 		g_system->delayMillis(10);
@@ -1531,110 +1511,7 @@ void EEMEngine::doBigMap() {
 	int scrollX = MAX<int>(0, MIN<int>(mapW - kMapWinW, zoomX));
 	int scrollY = MAX<int>(0, MIN<int>(mapH - kMapWinH, zoomY));
 
-	// `_DoMapScreen @ 20fe:120b` (20fe:12cd-12f0): partner sprite on
-	// the detail-zoom screen. Jake = anim 0x13 (19), Jenny = anim 0x11
-	// (17). Position (0x101, 0x50) = (257, 80), seqnum 0x13. The cells
-	// here have a "looking at the map" pose, distinct from the BigMap
-	// overview entrance/idle.
-	const uint kDetailAniId = (_partner == 0) ? 0x13 : 0x11;
-	Animation detailAnim;
-	const bool haveDetailAnim = _aniArchive.loadAnimation(kDetailAniId,
-														   detailAnim)
-								  && !detailAnim.empty();
-	const int kDetailAnimX = 0x101;
-	const int kDetailAnimY = 0x50;
-
-	auto drawDetail = [&]() {
-		Graphics::ManagedSurface scratch(320, 200,
-			Graphics::PixelFormat::createFormatCLUT8());
-		scratch.clear();
-
-		Picture frame;
-		if (_picsArchive.getPicture(0x43, frame)) {
-			const int w = MIN<int>(frame.surface.w, 320);
-			const int h = MIN<int>(frame.surface.h, 200);
-			for (int row = 0; row < h; row++)
-				memcpy((byte *)scratch.getBasePtr(0, row),
-					   (const byte *)frame.surface.getBasePtr(0, row), w);
-		}
-
-		const int copyW = MIN<int>(mapW - scrollX, kMapWinW);
-		const int copyH = MIN<int>(mapH - scrollY, kMapWinH);
-		for (int row = 0; row < copyH; row++) {
-			memcpy((byte *)scratch.getBasePtr(kMapWinX, kMapWinY + row),
-				   mapPixels.data() + (scrollY + row) * mapW + scrollX,
-				   copyW);
-		}
-
-		// Stamped site buttons. `_StampButtons @ 20fe:0d2f` does:
-		//   button = _GetButton(MapData[+0])      // BUTTON.DBD entry
-		//   destX  = MapData[+8],  destY = MapData[+0xa]
-		// then bakes the button PIC into the map bitmap. Each button
-		// sprite carries the site name baked in. We blit them on top
-		// of the BIGMAP.PIC viewport at the same SmallMap coords.
-		for (uint i = 0; i < _mystery.numSites(); i++) {
-			if (!_mystery._onSites[i] && i != _mystery._siteNumber)
-				continue;
-			const byte *entry = _mystery.mapEntry(i);
-			if (!entry)
-				continue;
-			const uint16 buttonId = READ_LE_UINT16(entry + 0x0);
-			const uint16 mx       = READ_LE_UINT16(entry + 0x8);
-			const uint16 my       = READ_LE_UINT16(entry + 0xa);
-
-			Picture button;
-			if (!_buttonArchive.loadEntry(buttonId, button))
-				continue;
-			const int sx = (int)mx - scrollX + kMapWinX;
-			const int sy = (int)my - scrollY + kMapWinY;
-			const byte transp = (byte)(button.flags >> 8);
-
-			// Crop blit against the viewport.
-			const int x0 = MAX<int>(sx, kMapWinX);
-			const int y0 = MAX<int>(sy, kMapWinY);
-			const int x1 = MIN<int>(sx + button.surface.w, kMapWinX + kMapWinW);
-			const int y1 = MIN<int>(sy + button.surface.h, kMapWinY + kMapWinH);
-			for (int row = y0; row < y1; row++) {
-				const byte *src = (const byte *)button.surface.getBasePtr(0, row - sy);
-				byte *dst = (byte *)scratch.getBasePtr(0, row);
-				for (int col = x0; col < x1; col++) {
-					const byte px = src[col - sx];
-					if (px != transp)
-						dst[col] = px;
-				}
-			}
-		}
-
-		// Partner sprite on the detail map. Drawn last so it sits over
-		// the frame and the BIGMAP.PIC viewport.
-		if (haveDetailAnim) {
-			const uint32 now = g_system->getMillis();
-			const uint frameIdx =
-				(uint)((now / 100) % detailAnim.size());
-			const Picture &fr = detailAnim[frameIdx];
-			const byte transp = (byte)(fr.flags >> 8);
-			for (int row = 0; row < fr.surface.h; row++) {
-				const int dstY = kDetailAnimY + row;
-				if (dstY < 0 || dstY >= 200)
-					continue;
-				const byte *src = (const byte *)fr.surface.getBasePtr(0, row);
-				byte *dst = (byte *)scratch.getBasePtr(0, dstY);
-				for (int col = 0; col < fr.surface.w; col++) {
-					const int dstX = kDetailAnimX + col;
-					if (dstX < 0 || dstX >= 320)
-						continue;
-					if (src[col] != transp)
-						dst[dstX] = src[col];
-				}
-			}
-		}
-
-		g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
-								   0, 0, 320, 200);
-		g_system->updateScreen();
-	};
-
-	drawDetail();
+	drawBigMapDetail(scrollX, scrollY, mapPixels, mapW, mapH);
 	uint32 detailLastTick = g_system->getMillis();
 
 	while (!shouldQuit()) {
@@ -1761,17 +1638,231 @@ void EEMEngine::doBigMap() {
 		// Cycle the partner sprite at 100 ms ticks (same cadence as
 		// `_DoMapScreen`'s `_CheckFrameRate` + `_UpdateAnimations` loop).
 		const uint32 now = g_system->getMillis();
-		if (haveDetailAnim && now - detailLastTick >= 100) {
+		if (now - detailLastTick >= 100) {
 			detailLastTick = now;
 			dirty = true;
 		}
 		if (dirty)
-			drawDetail();
+			drawBigMapDetail(scrollX, scrollY, mapPixels, mapW, mapH);
 		g_system->updateScreen();
 		g_system->delayMillis(10);
 	}
 }
 
+void EEMEngine::drawBigMapOverview() {
+	// Map-overview redraw ??? formerly the `drawOverview` lambda inside
+	// `doBigMap`. PIC 0x42 frame + per-site marker (Done / Crime / Site
+	// per `_DrawBigMapButtons @ 20fe:0877`) + the partner idle sprite.
+	// `_DoBigMap @ 20fe:09e7` (`_NewAnimation` block at 20fe:0a44-0a99)
+	// registers the partner: when `_LastScreen == 2` it plays an
+	// entrance one-shot at (0x102, 0x50) and on END swaps to the idle
+	// at (0xfd, 0x50). We don't track LastScreen finely enough so we
+	// always render the IDLE pose at (0xfd, 0x50). Idle anim ID:
+	// Jake = 0x14 (20), Jenny = 0x12 (18).
+	Graphics::ManagedSurface scratch(320, 200,
+		Graphics::PixelFormat::createFormatCLUT8());
+	scratch.clear();
+
+	Picture frame;
+	if (_picsArchive.getPicture(0x42, frame)) {
+		const int w = MIN<int>(frame.surface.w, 320);
+		const int h = MIN<int>(frame.surface.h, 200);
+		for (int row = 0; row < h; row++)
+			memcpy((byte *)scratch.getBasePtr(0, row),
+				   (const byte *)frame.surface.getBasePtr(0, row), w);
+	}
+
+	// Marker PICs from `_main @ 1a35:0f59`. Three globals are filled
+	// once at boot via `_GetPicture` (1-based IDs):
+	//   _DoneMarker  = PIC 0x20d  (already-searched site)
+	//   _SiteMarker  = PIC 0xc5   (default available site)
+	//   _CrimeMarker = PIC 0xc6   (crime-scene flag set)
+	Picture done;
+	Picture normal;
+	Picture crimeM;
+	const bool haveDone   = _picsArchive.getPicture(0x20d, done);
+	const bool haveNormal = _picsArchive.getPicture(0xc5,  normal);
+	const bool haveCrime  = _picsArchive.getPicture(0xc6,  crimeM);
+
+	for (uint i = 0; i < _mystery.numSites(); i++) {
+		if (!_mystery._onSites[i] && i != _mystery._siteNumber)
+			continue;
+		const byte *entry = _mystery.mapEntry(i);
+		if (!entry)
+			continue;
+		const uint16 mx    = READ_LE_UINT16(entry + 0x4);
+		const uint16 my    = READ_LE_UINT16(entry + 0x6);
+		const uint16 crime = READ_LE_UINT16(entry + 0xc);
+		const bool   done_ = (i < Mystery::kVisitedSiteCap)
+							  && _mystery._visitedSite[i];
+
+		const Picture *m = nullptr;
+		if (done_ && haveDone)
+			m = &done;
+		else if (crime != 0 && haveCrime)
+			m = &crimeM;
+		else if (haveNormal)
+			m = &normal;
+
+		if (m) {
+			// Masked-blit the marker PIC.
+			const byte transp = (byte)(m->flags >> 8);
+			for (int row = 0; row < m->surface.h; row++) {
+				const int dstY = (int)my + row;
+				if (dstY < 0 || dstY >= 200)
+					continue;
+				const byte *src = (const byte *)m->surface.getBasePtr(0, row);
+				byte *dst = (byte *)scratch.getBasePtr(0, dstY);
+				for (int col = 0; col < m->surface.w; col++) {
+					const int dstX = (int)mx + col;
+					if (dstX < 0 || dstX >= 320)
+						continue;
+					if (src[col] != transp)
+						dst[dstX] = src[col];
+				}
+			}
+		} else {
+			// Fallback if the markers couldn't be loaded.
+			const Common::Rect mark(mx - 3, my - 3, mx + 4, my + 4);
+			scratch.fillRect(mark, 0x0F);
+		}
+	}
+
+	// Partner idle sprite at (0xfd, 0x50). Jake = anim 0x14, Jenny = 0x12.
+	const uint kMapAniId = (_partner == 0) ? 0x14 : 0x12;
+	Animation mapAnim;
+	if (_aniArchive.loadAnimation(kMapAniId, mapAnim) && !mapAnim.empty()) {
+		const uint32 now = g_system->getMillis();
+		const uint frameIdx = (uint)((now / 100) % mapAnim.size());
+		const Picture &fr = mapAnim[frameIdx];
+		const byte transp = (byte)(fr.flags >> 8);
+		const int kMapAnimX = 0xfd;
+		const int kMapAnimY = 0x50;
+		for (int row = 0; row < fr.surface.h; row++) {
+			const int dstY = kMapAnimY + row;
+			if (dstY < 0 || dstY >= 200)
+				continue;
+			const byte *src = (const byte *)fr.surface.getBasePtr(0, row);
+			byte *dst = (byte *)scratch.getBasePtr(0, dstY);
+			for (int col = 0; col < fr.surface.w; col++) {
+				const int dstX = kMapAnimX + col;
+				if (dstX < 0 || dstX >= 320)
+					continue;
+				if (src[col] != transp)
+					dst[dstX] = src[col];
+			}
+		}
+	}
+
+	g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
+							   0, 0, 320, 200);
+	g_system->updateScreen();
+}
+
+void EEMEngine::drawBigMapDetail(int scrollX, int scrollY,
+								 const Common::Array<byte> &mapPixels,
+								 uint16 mapW, uint16 mapH) {
+	// Map-detail redraw ??? formerly the `drawDetail` lambda inside
+	// `doBigMap`. PIC 0x43 frame + a 0xe9 ?? 0xab BIGMAP.PIC viewport at
+	// (2, 2), stamped site buttons, and the partner sprite at (0x101,
+	// 0x50) ??? `_DoMapScreen @ 20fe:120b` (`_NewAnimation` at
+	// 20fe:12cd-12f0, anim 0x13 Jake / 0x11 Jenny, seqnum 0x13).
+	const int kMapWinW = 0xe9;
+	const int kMapWinH = 0xab;
+	const int kMapWinX = 2;
+	const int kMapWinY = 2;
+
+	Graphics::ManagedSurface scratch(320, 200,
+		Graphics::PixelFormat::createFormatCLUT8());
+	scratch.clear();
+
+	Picture frame;
+	if (_picsArchive.getPicture(0x43, frame)) {
+		const int w = MIN<int>(frame.surface.w, 320);
+		const int h = MIN<int>(frame.surface.h, 200);
+		for (int row = 0; row < h; row++)
+			memcpy((byte *)scratch.getBasePtr(0, row),
+				   (const byte *)frame.surface.getBasePtr(0, row), w);
+	}
+
+	const int copyW = MIN<int>(mapW - scrollX, kMapWinW);
+	const int copyH = MIN<int>(mapH - scrollY, kMapWinH);
+	for (int row = 0; row < copyH; row++) {
+		memcpy((byte *)scratch.getBasePtr(kMapWinX, kMapWinY + row),
+			   mapPixels.data() + (scrollY + row) * mapW + scrollX,
+			   copyW);
+	}
+
+	// Stamped site buttons. `_StampButtons @ 20fe:0d2f`:
+	//   button = _GetButton(MapData[+0])
+	//   destX  = MapData[+8]
+	//   destY  = MapData[+0xa]
+	for (uint i = 0; i < _mystery.numSites(); i++) {
+		if (!_mystery._onSites[i] && i != _mystery._siteNumber)
+			continue;
+		const byte *entry = _mystery.mapEntry(i);
+		if (!entry)
+			continue;
+		const uint16 buttonId = READ_LE_UINT16(entry + 0x0);
+		const uint16 mx       = READ_LE_UINT16(entry + 0x8);
+		const uint16 my       = READ_LE_UINT16(entry + 0xa);
+
+		Picture button;
+		if (!_buttonArchive.loadEntry(buttonId, button))
+			continue;
+		const int sx = (int)mx - scrollX + kMapWinX;
+		const int sy = (int)my - scrollY + kMapWinY;
+		const byte transp = (byte)(button.flags >> 8);
+
+		// Crop blit against the viewport.
+		const int x0 = MAX<int>(sx, kMapWinX);
+		const int y0 = MAX<int>(sy, kMapWinY);
+		const int x1 = MIN<int>(sx + button.surface.w, kMapWinX + kMapWinW);
+		const int y1 = MIN<int>(sy + button.surface.h, kMapWinY + kMapWinH);
+		for (int row = y0; row < y1; row++) {
+			const byte *src = (const byte *)button.surface.getBasePtr(0, row - sy);
+			byte *dst = (byte *)scratch.getBasePtr(0, row);
+			for (int col = x0; col < x1; col++) {
+				const byte px = src[col - sx];
+				if (px != transp)
+					dst[col] = px;
+			}
+		}
+	}
+
+	// Partner sprite on the detail map (drawn last to sit over the
+	// frame and the BIGMAP.PIC viewport).
+	const uint kDetailAniId = (_partner == 0) ? 0x13 : 0x11;
+	Animation detailAnim;
+	if (_aniArchive.loadAnimation(kDetailAniId, detailAnim) &&
+		!detailAnim.empty()) {
+		const uint32 now = g_system->getMillis();
+		const uint frameIdx = (uint)((now / 100) % detailAnim.size());
+		const Picture &fr = detailAnim[frameIdx];
+		const byte transp = (byte)(fr.flags >> 8);
+		const int kDetailAnimX = 0x101;
+		const int kDetailAnimY = 0x50;
+		for (int row = 0; row < fr.surface.h; row++) {
+			const int dstY = kDetailAnimY + row;
+			if (dstY < 0 || dstY >= 200)
+				continue;
+			const byte *src = (const byte *)fr.surface.getBasePtr(0, row);
+			byte *dst = (byte *)scratch.getBasePtr(0, dstY);
+			for (int col = 0; col < fr.surface.w; col++) {
+				const int dstX = kDetailAnimX + col;
+				if (dstX < 0 || dstX >= 320)
+					continue;
+				if (src[col] != transp)
+					dst[dstX] = src[col];
+			}
+		}
+	}
+
+	g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
+							   0, 0, 320, 200);
+	g_system->updateScreen();
+}
+
 uint16 EEMEngine::getKDTextBalloon(byte firstChar) const {
 	// Mirrors `_GetKDTextBalloon @ 1df2:0105`:
 	//   if ((ctype[firstChar] & 2) == 0)  bub = *(u16*)29be:1068 = 0x17
@@ -1812,12 +1903,6 @@ void EEMEngine::doAccuse() {
 
 	// Verbatim from 29be:0x116 ??? same five suspect slot positions as
 	// `_DrawGallery @ 158f:0046`.
-	struct Slot { int x; int y; };
-	static const Slot kGallerySlots[5] = {
-		{  83,  14 }, { 155,  14 }, { 227,  14 },
-		{ 119,  90 }, { 191,  90 }
-	};
-
 	Picture accuseBg;
 	const bool haveAccuseBg = _picsArchive.getPicture(0x3f, accuseBg);
 
@@ -1829,84 +1914,6 @@ void EEMEngine::doAccuse() {
 		slotSuspect[i] = -1;
 
 	int highlighted = 0;
-	auto drawGallery = [&]() {
-		Graphics::ManagedSurface scratch(320, 200,
-			Graphics::PixelFormat::createFormatCLUT8());
-		scratch.clear();
-		if (haveAccuseBg) {
-			const int bw = MIN<int>(accuseBg.surface.w, 320);
-			const int bh = MIN<int>(accuseBg.surface.h, 200);
-			for (int row = 0; row < bh; row++) {
-				memcpy((byte *)scratch.getBasePtr(0, row),
-					   (const byte *)accuseBg.surface.getBasePtr(0, row), bw);
-			}
-		}
-
-		for (uint i = 0; i < num && i < Mystery::kGalleryCap; i++) {
-			slotRects[i] = Common::Rect();
-			slotSuspect[i] = -1;
-			if (!gd)
-				continue;
-			const uint8 phys = _mystery._newOrder[i];
-			if (phys >= 5)
-				continue;
-			// `_DrawGallery @ 158f:00b9` skips suspects whose
-			// `_InGallery[phys]` flag is 0 ??? that's the original gate
-			// (some suspects only become visible after being met or
-			// stay hidden after a wrong accusation removes them).
-			if (_mystery._inGallery[phys] == 0)
-				continue;
-			const Slot &s = kGallerySlots[phys];
-
-			const uint16 picId = READ_LE_UINT16(gd + i * 0x46);
-			if (picId == 0)
-				continue;
-			Picture portrait;
-			if (!_picsArchive.getPicture(picId, portrait))
-				continue;
-
-			const int placeX = s.x;
-			const int placeY = s.y + (0x48 - portrait.surface.h);
-			const byte transp = (byte)(portrait.flags >> 8);
-			const int w = MIN<int>(portrait.surface.w, 320 - placeX);
-			const int h = MIN<int>(portrait.surface.h, 200 - placeY);
-			if (w <= 0 || h <= 0)
-				continue;
-			for (int row = 0; row < h; row++) {
-				const int dstY = placeY + row;
-				if (dstY < 0)
-					continue;
-				const byte *src =
-					(const byte *)portrait.surface.getBasePtr(0, row);
-				byte *dst = (byte *)scratch.getBasePtr(0, dstY);
-				for (int col = 0; col < w; col++) {
-					const int dstX = placeX + col;
-					if (src[col] != transp)
-						dst[dstX] = src[col];
-				}
-			}
-			slotRects[i] = Common::Rect(placeX, placeY,
-										 placeX + w, placeY + h);
-			slotSuspect[i] = (int)i;
-		}
-
-		// Highlight indicator. The original moves the mouse cursor
-		// to the centre of the highlighted suspect via `_PutMouseInRect`
-		// (1df2:0b8e) ??? we draw a 1px outline in palette index 0xFE
-		// (within the marching-ants cycle range 0xF9..0xFE) which is
-		// unambiguously visible under any palette without warping the
-		// player's cursor.
-		if (highlighted >= 0 && highlighted < (int)slotRects.size() &&
-			!slotRects[highlighted].isEmpty()) {
-			Common::Rect r = slotRects[highlighted];
-			r.grow(1);
-			scratch.frameRect(r, 0xFE);
-		}
-
-		g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
-								   0, 0, 320, 200);
-		g_system->updateScreen();
-	};
 
 	// Step 1 ??? KD hint balloon. Mirrors `_DoAccuseGallery @ 1df2:0a31`
 	// (1df2:0a4c-1df2:0afe):
@@ -1991,23 +1998,10 @@ void EEMEngine::doAccuse() {
 	// Helper to find the next "alive" slot (one whose `_inGallery[phys]`
 	// flag is still set so a portrait was actually drawn). Mirrors the
 	// way the original wraps DI past empty slots.
-	auto nextLiveSlot = [&](int from, int dir) -> int {
-		const int n = (int)slotRects.size();
-		if (n <= 0)
-			return 0;
-		for (int step = 1; step <= n; step++) {
-			int idx = (from + dir * step) % n;
-			if (idx < 0)
-				idx += n;
-			if (!slotRects[idx].isEmpty())
-				return idx;
-		}
-		return from;
-	};
 	if (slotRects[highlighted].isEmpty())
-		highlighted = nextLiveSlot(highlighted, +1);
+		highlighted = nextLiveSlot(slotRects, highlighted, +1);
 
-	drawGallery();
+	drawAccuseGallery(num, gd, highlighted, slotRects, slotSuspect);
 
 	// Wait-for-pick loop. Mirrors `_DoAccuseGallery` 1df2:0b26-1df2:0bc8:
 	//   * `_CheckFrameRate` + `_UpdateAnimations` per tick (1df2:0b2a-0b33)
@@ -2038,14 +2032,14 @@ void EEMEngine::doAccuse() {
 					return;
 				case Common::KEYCODE_TAB:
 				case Common::KEYCODE_RIGHT:
-					highlighted = nextLiveSlot(highlighted, +1);
+					highlighted = nextLiveSlot(slotRects, highlighted, +1);
 					dirty = true;
 					break;
 				case Common::KEYCODE_LEFT:
 					// 1df2:0b94 increments DI for LEFT too ??? but a
 					// keyboard-driven UX is friendlier with separate
 					// directions, so we mirror Right=+1 / Left=-1.
-					highlighted = nextLiveSlot(highlighted, -1);
+					highlighted = nextLiveSlot(slotRects, highlighted, -1);
 					dirty = true;
 					break;
 				case Common::KEYCODE_RETURN:
@@ -2085,7 +2079,7 @@ void EEMEngine::doAccuse() {
 		// We still re-render whenever the highlight moves (`dirty`).
 		const uint32 now = g_system->getMillis();
 		if (dirty || now - lastTick >= 100) {
-			drawGallery();
+			drawAccuseGallery(num, gd, highlighted, slotRects, slotSuspect);
 			lastTick = now;
 			dirty = false;
 		}
@@ -2265,4 +2259,96 @@ void EEMEngine::doAccuse() {
 	}
 }
 
+void EEMEngine::drawAccuseGallery(uint8 numSuspects, const byte *gd,
+								   int highlighted,
+								   Common::Array<Common::Rect> &slotRects,
+								   Common::Array<int> &slotSuspect) {
+	// Accuse-gallery redraw ??? formerly the `drawGallery` lambda inside
+	// `doAccuse`. Mirrors `_DoAccuseGallery @ 1df2:0a31` portrait grid:
+	// PIC 0x3f backdrop, suspect portraits at the 5 fixed slots, and a
+	// 1-px outline (palette index 0xFE) around the highlighted slot.
+	struct Slot { int x; int y; };
+	static const Slot kGallerySlots[5] = {
+		{  83,  14 }, { 155,  14 }, { 227,  14 },
+		{ 119,  90 }, { 191,  90 }
+	};
+
+	Picture accuseBg;
+	const bool haveAccuseBg = _picsArchive.getPicture(0x3f, accuseBg);
+
+	Graphics::ManagedSurface scratch(320, 200,
+		Graphics::PixelFormat::createFormatCLUT8());
+	scratch.clear();
+	if (haveAccuseBg) {
+		const int bw = MIN<int>(accuseBg.surface.w, 320);
+		const int bh = MIN<int>(accuseBg.surface.h, 200);
+		for (int row = 0; row < bh; row++) {
+			memcpy((byte *)scratch.getBasePtr(0, row),
+				   (const byte *)accuseBg.surface.getBasePtr(0, row), bw);
+		}
+	}
+
+	for (uint i = 0; i < numSuspects && i < Mystery::kGalleryCap; i++) {
+		slotRects[i] = Common::Rect();
+		slotSuspect[i] = -1;
+		if (!gd)
+			continue;
+		const uint8 phys = _mystery._newOrder[i];
+		if (phys >= 5)
+			continue;
+		// `_DrawGallery @ 158f:00b9` skips suspects whose
+		// `_InGallery[phys]` flag is 0 ??? that's the original gate.
+		if (_mystery._inGallery[phys] == 0)
+			continue;
+		const Slot &s = kGallerySlots[phys];
+
+		const uint16 picId = READ_LE_UINT16(gd + i * 0x46);
+		if (picId == 0)
+			continue;
+		Picture portrait;
+		if (!_picsArchive.getPicture(picId, portrait))
+			continue;
+
+		const int placeX = s.x;
+		const int placeY = s.y + (0x48 - portrait.surface.h);
+		const byte transp = (byte)(portrait.flags >> 8);
+		const int w = MIN<int>(portrait.surface.w, 320 - placeX);
+		const int h = MIN<int>(portrait.surface.h, 200 - placeY);
+		if (w <= 0 || h <= 0)
+			continue;
+		for (int row = 0; row < h; row++) {
+			const int dstY = placeY + row;
+			if (dstY < 0)
+				continue;
+			const byte *src =
+				(const byte *)portrait.surface.getBasePtr(0, row);
+			byte *dst = (byte *)scratch.getBasePtr(0, dstY);
+			for (int col = 0; col < w; col++) {
+				const int dstX = placeX + col;
+				if (src[col] != transp)
+					dst[dstX] = src[col];
+			}
+		}
+		slotRects[i] = Common::Rect(placeX, placeY,
+									 placeX + w, placeY + h);
+		slotSuspect[i] = (int)i;
+	}
+
+	// Highlight indicator. The original moves the mouse cursor to the
+	// centre of the highlighted suspect via `_PutMouseInRect` (1df2:0b8e);
+	// we draw a 1-px outline in palette index 0xFE instead, which sits
+	// inside the marching-ants cycle range 0xF9..0xFE and is visible
+	// under any palette without warping the player's cursor.
+	if (highlighted >= 0 && highlighted < (int)slotRects.size() &&
+		!slotRects[highlighted].isEmpty()) {
+		Common::Rect r = slotRects[highlighted];
+		r.grow(1);
+		scratch.frameRect(r, 0xFE);
+	}
+
+	g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
+							   0, 0, 320, 200);
+	g_system->updateScreen();
+}
+
 } // End of namespace EEM


Commit: 2fac0e6820d5d8b739c839f9d3e3718455c254f3
    https://github.com/scummvm/scummvm/commit/2fac0e6820d5d8b739c839f9d3e3718455c254f3
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:20:41+02:00

Commit Message:
EEM: removed static and use only EEM namespace

Changed paths:
    engines/eem/clues.cpp
    engines/eem/detection.cpp
    engines/eem/eem.cpp
    engines/eem/font.cpp
    engines/eem/graphics.cpp
    engines/eem/resource.cpp
    engines/eem/site.cpp
    engines/eem/ui.cpp


diff --git a/engines/eem/clues.cpp b/engines/eem/clues.cpp
index da14c8990ca..e5fc4df255c 100644
--- a/engines/eem/clues.cpp
+++ b/engines/eem/clues.cpp
@@ -40,8 +40,9 @@
 
 namespace EEM {
 
-namespace {
 // Picture / animation IDs verified against `_DoChoosePartner @ 1a35:0756`.
+// `const` at namespace scope already implies internal linkage in C++,
+// so no `static` needed.
 const uint kPicChooseBackground = 0x8c; ///< `_GetBackground(0x8c)`
 const uint kAniBoy  = 8;                 ///< `_GetAnimation(8)` (Jake)
 const uint kAniGirl = 9;                 ///< `_GetAnimation(9)` (Jenny)
@@ -55,6 +56,32 @@ const Common::Rect kHappyZones[4] = {
 	Common::Rect(182, 0, 235, 200), // boy's column
 };
 
+// On-screen positions verified from `_NewAnimation` calls @ 1a35:07b9 / 07d5.
+const int kBoyX  = 0xe2; // 226
+const int kBoyY  = 0x62; // 98
+const int kGirlX = 0x42; // 66
+const int kGirlY = 0x60; // 96
+
+// `_DoHappiness @ 172b:27b5`: each cursor zone swaps the partner's
+// sequence script to a more / less "happy" cycle. Boy seqs lifted
+// verbatim from `29be:0337` (5 ?? 0x14 bytes), girl seqs from
+// `29be:039b`. Both cycle through 9 frames (the boy/girl anim cells
+// contain 10 cells = pairs of "neutral, smile" at increasing intensity).
+const uint8 kBoySeqs[5][9] = {
+	{ 0,0,0,0,0,0,0,1,0 }, // level 0
+	{ 2,2,2,2,2,2,2,3,2 }, // level 1
+	{ 4,4,4,4,4,4,4,5,4 }, // level 2
+	{ 6,6,6,6,6,6,7,6,6 }, // level 3
+	{ 8,8,8,8,8,8,8,8,9 }, // level 4 (cursor past zone 3)
+};
+const uint8 kGirlSeqs[5][9] = {
+	{ 8,9,8,8,8,8,8,8,8 },
+	{ 6,6,6,7,6,6,6,6,6 },
+	{ 4,4,5,4,4,4,4,4,4 },
+	{ 2,2,2,2,2,2,3,2,2 },
+	{ 0,0,0,0,0,1,0,0,0 },
+};
+
 uint happinessLevel(int x) {
 	for (uint i = 0; i < ARRAYSIZE(kHappyZones); i++) {
 		if (kHappyZones[i].contains(x, 100))
@@ -87,13 +114,6 @@ void blitMaskedToScreen(const Picture &p, int x, int y) {
 	g_system->unlockScreen();
 }
 
-// On-screen positions verified from `_NewAnimation` calls @ 1a35:07b9 / 07d5.
-const int kBoyX  = 0xe2; // 226
-const int kBoyY  = 0x62; // 98
-const int kGirlX = 0x42; // 66
-const int kGirlY = 0x60; // 96
-} // anonymous namespace
-
 void EEMEngine::doChoosePartner() {
 	// Mirrors _DoChoosePartner @ 1a35:0756. The original places boy + girl
 	// animations on a backdrop and polls four click rectangles (two per
@@ -119,27 +139,9 @@ void EEMEngine::doChoosePartner() {
 	setAnmPalette(Common::Path("TITLE.ANM"));
 
 	// `_DoHappiness @ 172b:27b5`: the cursor's X column picks one of 4
-	// rects (29be:030f, all full-height); past rect 3 ??? "level 4".
-	// Each level swaps the partner's sequence script to a more / less
-	// "happy" cycle. Boy seqs at 29be:0337 (5 ?? 0x14 bytes), girl seqs
-	// at 29be:039b. Both cycle through 9 frames (the boy/girl anim
-	// cells contain 10 cells = pairs of "neutral, smile" at increasing
-	// intensity). Lifted verbatim from the binary so the gestures
-	// match the original beat-for-beat.
-	static const uint8 kBoySeqs[5][9] = {
-		{ 0,0,0,0,0,0,0,1,0 }, // level 0
-		{ 2,2,2,2,2,2,2,3,2 }, // level 1
-		{ 4,4,4,4,4,4,4,5,4 }, // level 2
-		{ 6,6,6,6,6,6,7,6,6 }, // level 3
-		{ 8,8,8,8,8,8,8,8,9 }, // level 4 (cursor past zone 3)
-	};
-	static const uint8 kGirlSeqs[5][9] = {
-		{ 8,9,8,8,8,8,8,8,8 },
-		{ 6,6,6,7,6,6,6,6,6 },
-		{ 4,4,5,4,4,4,4,4,4 },
-		{ 2,2,2,2,2,2,3,2,2 },
-		{ 0,0,0,0,0,1,0,0,0 },
-	};
+	// rects (29be:030f, all full-height); past rect 3 ??? "level 4". The
+	// per-zone sequence scripts (`kBoySeqs` / `kGirlSeqs`) live at file
+	// scope above so the gestures match the original beat-for-beat.
 
 	// `_DoChoosePartner` opens with `_SetMousePos(0xa0, 0x96)` so the
 	// cursor lands centred between the two partners ??? start the
@@ -650,39 +652,19 @@ void EEMEngine::displayClue(const byte *clueBlock) {
 						}
 					}
 				}
-				// Per-balloon metadata table verified from 29be:0875 ???
-				// 10-byte entries indexed by `(bubNum & 0x7f)`. Layout:
-				//   +0..1 textX inset, +2..3 textY inset, +4..5 width,
-				//   +6..7 height, +8..9 tail offset.
-				// 52 entries total; insets vary (3, 5, 6, or 8 px).
-				// The original `_DisplayClue` does:
-				//   _WordWrap(bubX + table[bubNum].x, bubY + table[bubNum].y,
-				//             table[bubNum].w, ...);
-				static const struct { uint16 x, y, w; } kBalloonTable[] = {
-					{ 6, 4, 142 }, { 6, 4, 142 }, { 6, 4, 142 }, { 6, 4, 142 },
-					{ 6, 4, 142 }, { 6, 4, 142 }, { 6, 4, 142 },
-					{ 6, 4, 224 }, { 6, 4, 224 }, { 6, 4, 224 }, { 6, 4, 224 },
-					{ 6, 4, 224 }, { 6, 4, 224 }, { 6, 4, 224 },
-					{ 6, 4, 291 }, { 6, 4, 291 }, { 6, 4, 291 }, { 6, 4, 291 },
-					{ 6, 4, 291 }, { 6, 4, 291 }, { 6, 4, 291 },
-					{ 5, 4, 155 }, { 5, 4, 155 }, { 5, 4, 155 }, { 5, 4, 155 },
-					{ 5, 4, 155 }, { 5, 4, 155 }, { 5, 4, 155 },
-					{ 5, 4, 237 }, { 5, 4, 237 }, { 5, 4, 237 }, { 5, 4, 237 },
-					{ 5, 4, 237 }, { 5, 4, 237 }, { 5, 4, 237 },
-					{ 3, 4, 155 }, { 3, 4, 155 }, { 3, 4, 155 }, { 3, 4, 155 },
-					{ 3, 4, 155 }, { 3, 4, 155 }, { 3, 4, 155 },
-					{ 5, 4, 238 }, { 5, 4, 238 }, { 5, 4, 238 }, { 5, 4, 238 },
-					{ 5, 4, 238 }, { 5, 4, 238 }, { 5, 4, 238 },
-					{ 5, 8, 158 }, { 5, 8, 176 }, { 8, 7, 142 }
-				};
-				const uint kBalloonTableSize = sizeof(kBalloonTable) /
-											   sizeof(kBalloonTable[0]);
-				const uint balloonIdx = balloonId < kBalloonTableSize
-										? balloonId : 0;
-				const auto &bm = kBalloonTable[balloonIdx];
-				textX = bubX + bm.x;
-				textY = bubY + bm.y;
-				textW = bm.w;
+				// Per-balloon metadata from `29be:0875` (52 ?? 10 bytes,
+				// indexed by `bubNum & 0x7F`). The original `_DisplayClue`
+				// does `_WordWrap(bubX + table[bub].x, bubY + table[bub].y,
+				// table[bub].w, ...)`. `getBalloonInsets` is the shared
+				// accessor (defined in `graphics.cpp`); fall back to the
+				// (5, 4, 155) entry-23 inset if the lookup fails.
+				uint16 bx = 5;
+				uint16 by = 4;
+				uint16 bw_ = 155;
+				getBalloonInsets(balloonId, bx, by, bw_);
+				textX = bubX + bx;
+				textY = bubY + by;
+				textW = bw_;
 				copyH = bh;
 			} else {
 				// No balloon ??? clear a band so old pixels don't bleed.
diff --git a/engines/eem/detection.cpp b/engines/eem/detection.cpp
index 0666827a3db..27371f0c29e 100644
--- a/engines/eem/detection.cpp
+++ b/engines/eem/detection.cpp
@@ -26,7 +26,7 @@
 
 namespace EEM {
 
-static const PlainGameDescriptor eemGames[] = {
+const PlainGameDescriptor eemGames[] = {
 	{ "eem", "Eagle Eye Mysteries" },
 	{ nullptr, nullptr }
 };
@@ -46,7 +46,7 @@ const ADGameDescription gameDescriptions[] = {
 	AD_TABLE_END_MARKER
 };
 
-static const DebugChannelDef debugFlagList[] = {
+const DebugChannelDef debugFlagList[] = {
 	{ kDebugGeneral, "general", "General debug" },
 	{ kDebugScript,  "script",  "Script execution" },
 	{ kDebugMystery, "mystery", "Mystery loading and state" },
diff --git a/engines/eem/eem.cpp b/engines/eem/eem.cpp
index b33fb699ae1..86ddd0a9096 100644
--- a/engines/eem/eem.cpp
+++ b/engines/eem/eem.cpp
@@ -45,7 +45,6 @@
 
 namespace EEM {
 
-namespace {
 const uint kPalSize = 768;     ///< 256 colors * 3 bytes
 const uint kNumSitePals = 40;  ///< SITEPALS holds 40 palettes (40 * 768 = 30720)
 
@@ -87,7 +86,6 @@ const byte kCursorPalette[] = {
 	0x00, 0x00, 0x00, // 1 ??? outline
 	0xFF, 0xFF, 0xFF  // 2 ??? fill
 };
-} // anonymous namespace
 
 EEMEngine::EEMEngine(OSystem *syst, const ADGameDescription *gameDesc)
 	: Engine(syst), _gameDescription(gameDesc), _rng("eem"),
diff --git a/engines/eem/font.cpp b/engines/eem/font.cpp
index 19f40c1b77e..4d00ab79fb1 100644
--- a/engines/eem/font.cpp
+++ b/engines/eem/font.cpp
@@ -42,7 +42,7 @@ namespace EEM {
 // and 'a' to the lowercase glyph (so the original engine renders all
 // text in lowercase). We route uppercase ASCII letters to the uppercase
 // glyph slots (33..58) for proper mixed-case rendering.
-static const byte kCharToGlyph[128] = {
+const byte kCharToGlyph[128] = {
 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -61,7 +61,7 @@ static const byte kCharToGlyph[128] = {
 	0x52, 0x53, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00  // 0x78..0x7F 'x','y','z'..
 };
 
-static inline byte mapChar(uint32 c) {
+inline byte mapChar(uint32 c) {
 	return c < 128 ? kCharToGlyph[c] : 0;
 }
 
diff --git a/engines/eem/graphics.cpp b/engines/eem/graphics.cpp
index 857dcb80ecc..3955914eb98 100644
--- a/engines/eem/graphics.cpp
+++ b/engines/eem/graphics.cpp
@@ -39,6 +39,42 @@
 
 namespace EEM {
 
+// `_InterfaceHelp(num)` @ 1560:0205 reads `HelpData @ 29be:00c8` (5-byte
+// entries: u8 count, then up to 2 u16 picIds). Verified bytes:
+//   entry 0 (PDA / gallery HELP button): count=2, picIds = 0x0063, 0x01ae
+//   entry 1: count=2, picIds = 0x0192, 0x01b1
+// Only entry 0 is reachable from the PDA notebook (rect 1) and the
+// gallery (rect 1) ??? both call `_InterfaceHelp(0)`.
+const uint16 kHelpPics[][2] = {
+	{ 0x0063, 0x01ae },
+	{ 0x0192, 0x01b1 },
+};
+
+// 52-entry, 10-bytes-each balloon-metadata table at `29be:0875`.
+// Used at 1df2:0aef-0af9 (accuse hint) and `_DisplayClue` to position
+// `_WordWrap` text inside the balloon. Only +0/+2/+4 are read by
+// `getBalloonInsets`:
+//   +0..1 = text X inset, +2..3 = Y inset, +4..5 = max wrap width
+// (+6/+8 = balloon h / tail offset, both unused for text layout).
+struct BalloonInsets { uint16 x; uint16 y; uint16 w; };
+const BalloonInsets kBalloonInsetTable[] = {
+	{ 6, 4, 142 }, { 6, 4, 142 }, { 6, 4, 142 }, { 6, 4, 142 },
+	{ 6, 4, 142 }, { 6, 4, 142 }, { 6, 4, 142 },
+	{ 6, 4, 224 }, { 6, 4, 224 }, { 6, 4, 224 }, { 6, 4, 224 },
+	{ 6, 4, 224 }, { 6, 4, 224 }, { 6, 4, 224 },
+	{ 6, 4, 291 }, { 6, 4, 291 }, { 6, 4, 291 }, { 6, 4, 291 },
+	{ 6, 4, 291 }, { 6, 4, 291 }, { 6, 4, 291 },
+	{ 5, 4, 155 }, { 5, 4, 155 }, { 5, 4, 155 }, { 5, 4, 155 },
+	{ 5, 4, 155 }, { 5, 4, 155 }, { 5, 4, 155 },
+	{ 5, 4, 237 }, { 5, 4, 237 }, { 5, 4, 237 }, { 5, 4, 237 },
+	{ 5, 4, 237 }, { 5, 4, 237 }, { 5, 4, 237 },
+	{ 3, 4, 155 }, { 3, 4, 155 }, { 3, 4, 155 }, { 3, 4, 155 },
+	{ 3, 4, 155 }, { 3, 4, 155 }, { 3, 4, 155 },
+	{ 5, 4, 238 }, { 5, 4, 238 }, { 5, 4, 238 }, { 5, 4, 238 },
+	{ 5, 4, 238 }, { 5, 4, 238 }, { 5, 4, 238 },
+	{ 5, 8, 158 }, { 5, 8, 176 }, { 8, 7, 142 }
+};
+
 void EEMEngine::doHelp() {
 	// `_KDHelp` reads two hint TextBlock offsets from `_KDTextIndex`:
 	//   word @ +0xe : first-time hint
@@ -82,15 +118,8 @@ void EEMEngine::doInterfaceHelp(uint num) {
 	// `_Rect_Move_Mask(0, 0, ...)`, and waits for click / key. ESC ends
 	// the cycle; any other input advances to the next pic.
 	//
-	// Verified from Ghidra HelpData bytes:
-	//   entry 0 (PDA / gallery HELP button): count=2, picIds = 0x0063, 0x01ae
-	//   entry 1: count=2, picIds = 0x0192, 0x01b1
-	// Only entry 0 is reachable from the PDA notebook (rect 1) and the
-	// gallery (rect 1) ??? both call `_InterfaceHelp(0)`.
-	static const uint16 kHelpPics[][2] = {
-		{ 0x0063, 0x01ae },
-		{ 0x0192, 0x01b1 },
-	};
+	// `kHelpPics` lives at file scope above; see comment there for the
+	// HelpData decoding.
 	if (num >= ARRAYSIZE(kHelpPics))
 		return;
 
@@ -175,34 +204,13 @@ void EEMEngine::setPartnerEraseBg(const Graphics::ManagedSurface *bg) {
 
 bool EEMEngine::getBalloonInsets(uint16 bubNum, uint16 &xInset,
 								  uint16 &yInset, uint16 &textW) const {
-	// 52-entry, 10-bytes-each balloon-metadata table at `29be:0875`.
-	// Used at 1df2:0aef-0af9 (accuse hint) and `_DisplayClue` to position
-	// `_WordWrap` text inside the balloon. Only +0/+2/+4 are read here:
-	//   +0..1 = text X inset, +2..3 = Y inset, +4..5 = max wrap width
-	// (+6/+8 = balloon h / tail offset, both unused for text layout).
-	static const struct { uint16 x, y, w; } kTable[] = {
-		{ 6, 4, 142 }, { 6, 4, 142 }, { 6, 4, 142 }, { 6, 4, 142 },
-		{ 6, 4, 142 }, { 6, 4, 142 }, { 6, 4, 142 },
-		{ 6, 4, 224 }, { 6, 4, 224 }, { 6, 4, 224 }, { 6, 4, 224 },
-		{ 6, 4, 224 }, { 6, 4, 224 }, { 6, 4, 224 },
-		{ 6, 4, 291 }, { 6, 4, 291 }, { 6, 4, 291 }, { 6, 4, 291 },
-		{ 6, 4, 291 }, { 6, 4, 291 }, { 6, 4, 291 },
-		{ 5, 4, 155 }, { 5, 4, 155 }, { 5, 4, 155 }, { 5, 4, 155 },
-		{ 5, 4, 155 }, { 5, 4, 155 }, { 5, 4, 155 },
-		{ 5, 4, 237 }, { 5, 4, 237 }, { 5, 4, 237 }, { 5, 4, 237 },
-		{ 5, 4, 237 }, { 5, 4, 237 }, { 5, 4, 237 },
-		{ 3, 4, 155 }, { 3, 4, 155 }, { 3, 4, 155 }, { 3, 4, 155 },
-		{ 3, 4, 155 }, { 3, 4, 155 }, { 3, 4, 155 },
-		{ 5, 4, 238 }, { 5, 4, 238 }, { 5, 4, 238 }, { 5, 4, 238 },
-		{ 5, 4, 238 }, { 5, 4, 238 }, { 5, 4, 238 },
-		{ 5, 8, 158 }, { 5, 8, 176 }, { 8, 7, 142 }
-	};
+	// `kBalloonInsetTable` lives at file scope above; see comment there.
 	const uint idx = bubNum & 0x7F;
-	if (idx >= ARRAYSIZE(kTable))
+	if (idx >= ARRAYSIZE(kBalloonInsetTable))
 		return false;
-	xInset = kTable[idx].x;
-	yInset = kTable[idx].y;
-	textW  = kTable[idx].w;
+	xInset = kBalloonInsetTable[idx].x;
+	yInset = kBalloonInsetTable[idx].y;
+	textW  = kBalloonInsetTable[idx].w;
 	return true;
 }
 
diff --git a/engines/eem/resource.cpp b/engines/eem/resource.cpp
index 15fc9981d46..91886e837fb 100644
--- a/engines/eem/resource.cpp
+++ b/engines/eem/resource.cpp
@@ -80,7 +80,7 @@ void DBDArchive::close() {
  * Read one 12-byte frame header + payload at the current stream position.
  * Shared between picture and animation loaders since the layout is the same.
  */
-static bool readFrame(Common::SeekableReadStream &stream, bool compressed, Picture &out) {
+bool readFrame(Common::SeekableReadStream &stream, bool compressed, Picture &out) {
 	out.flags             = stream.readUint16LE();
 	const uint16 height   = stream.readUint16LE();
 	const uint16 width    = stream.readUint16LE();
diff --git a/engines/eem/site.cpp b/engines/eem/site.cpp
index df3bc56cbd3..4b641dc441c 100644
--- a/engines/eem/site.cpp
+++ b/engines/eem/site.cpp
@@ -33,8 +33,6 @@
 
 namespace EEM {
 
-namespace {
-
 // Masked blit a Picture into a ManagedSurface. Pixels equal to `transp`
 // (the high byte of `pic.flags`, per `_Rect_Move_Mask @ 1000:03fc`) are
 // skipped. Used by `enterSiteAnim` for both skateboard + KD slide-in
@@ -60,6 +58,31 @@ void blitFrame(Graphics::ManagedSurface &dst, const Picture &p,
 	}
 }
 
+// Mask-aware blit from a Picture into a `Graphics::Surface` (the
+// locked framebuffer). Same pixel-mask semantics as `blitFrame`.
+// Used by hotspot/NPC rendering inside `SiteScreen::renderHotspots`
+// and `renderStaticDrops`.
+void blitMaskedSurface(Graphics::Surface *screen, const Picture &p,
+					   int x, int y) {
+	if (!screen)
+		return;
+	const byte transp = (byte)(p.flags >> 8);
+	for (int row = 0; row < p.surface.h; row++) {
+		const int dstY = y + row;
+		if (dstY < 0 || dstY >= screen->h)
+			continue;
+		const byte *src = (const byte *)p.surface.getBasePtr(0, row);
+		byte *dst = (byte *)screen->getBasePtr(0, dstY);
+		for (int col = 0; col < p.surface.w; col++) {
+			const int dstX = x + col;
+			if (dstX < 0 || dstX >= screen->w)
+				continue;
+			if (src[col] != transp)
+				dst[dstX] = src[col];
+		}
+	}
+}
+
 // Rotate one VGA palette range by one slot. Mirrors `_ColorCycle @
 // 172b:2015` ??? used by both the per-site Loop-1 ColorCycle entries and
 // the always-on hotspot marching-ants range 0xF9..0xFE.
@@ -83,7 +106,68 @@ void cyclePaletteRange(uint8 start, uint8 end) {
 	g_system->getPaletteManager()->setPalette(buf, start, count);
 }
 
-} // anonymous namespace
+// Per-speaker partner-position table verified against `_WaitAnims @
+// 29be:021c`. 12 bytes per entry, indexed by `siteData[+8]`. Layout:
+//   +0..1 anim Jake, +2..3 anim Jenny,
+//   +4..5 x    Jake, +6..7 x    Jenny,
+//   +8..9 y    Jake, +10..11 y    Jenny.
+// Seven valid entries ??? anything past entry 6 in the binary is
+// `_SiteButtons` rect data that follows the table in memory.
+const uint16 kWaitAnims[7][6] = {
+	{ 0x00, 0x0a, 0x06, 0x06, 0x50, 0x50 }, // 0
+	{ 0x03, 0x0c, 0x06, 0x06, 0x50, 0x50 }, // 1
+	{ 0x01, 0x0b, 0x06, 0x06, 0x50, 0x50 }, // 2
+	{ 0x04, 0x0d, 0x06, 0x06, 0x50, 0x50 }, // 3
+	{ 0x02, 0x10, 0x06, 0x06, 0x50, 0x50 }, // 4
+	{ 0x05, 0x05, 0x06, 0x06, 0x50, 0x50 }, // 5
+	{ 0x06, 0x06, 0x06, 0x06, 0x50, 0x50 }, // 6
+};
+
+// `_DoKDAnim` lookup table. Six valid kdAnimNum entries (0..5)
+// verified from `29be:0228`. Layout per entry: { animJake, animJenny,
+// xJake, xJenny, yJake, yJenny }. Position is (6, 80) in every entry.
+const uint16 kKdAnimTable[6][6] = {
+	{ 0x03, 0x0c, 6, 6, 80, 80 }, // 0 ??? speaker idx 1 wait anim
+	{ 0x01, 0x0b, 6, 6, 80, 80 }, // 1 ??? same as PDA idle
+	{ 0x04, 0x0d, 6, 6, 80, 80 }, // 2
+	{ 0x02, 0x10, 6, 6, 80, 80 }, // 3 ??? same as gallery
+	{ 0x05, 0x05, 6, 6, 80, 80 }, // 4 ??? same anim both partners
+	{ 0x06, 0x06, 6, 6, 80, 80 }, // 5 ??? same anim both partners
+};
+
+// Sequence-script lookup. Entries copied verbatim from
+// `_AnimationSequences @ 29be:22d4` walked through to the next 0x80.
+// Each script is a u16[] of frame indices terminated by 0x80; we
+// don't yet handle 0x81 jumps (none of the kdAnim sequences use
+// them ??? verified). seqnum == animId for these calls (per
+// `_PlayAnimation` 172b:1f5d push order).
+struct KdScript {
+	uint16 seqnum;
+	uint8 len;
+	uint8 frames[20];  // long enough for any kdAnim script
+};
+const KdScript kKdScripts[] = {
+	// seqnum 1 (29be:188a) ??? head bob
+	{ 0x01, 15, { 0,1,2,0,1,0,2,1,0,1,0,1,2,1,0 } },
+	// seqnum 2 (29be:18aa) ??? short blip then long pause
+	{ 0x02, 16, { 0,1,2,1,0,0,0,0,0,0,0,0,0,0,0,0 } },
+	// seqnum 3 (29be:18e0) ??? Jake "lift, hold, lower" gesture
+	{ 0x03,  9, { 0,1,2,3,2,2,2,1,0 } },
+	// seqnum 4 (29be:18f4) ??? bigger gesture (camera flash-style)
+	{ 0x04, 13, { 0,1,2,3,4,5,4,4,4,3,2,1,0 } },
+	// seqnum 5 (29be:1910) ??? held idle with a single peak
+	{ 0x05, 13, { 0,0,0,1,2,3,2,1,0,0,0,0,0 } },
+	// seqnum 6 (29be:192c) ??? empty (immediate END)
+	{ 0x06,  0, { 0 } },
+	// seqnum 0xb (29be:188a, same as 1) ??? Jenny PDA idle
+	{ 0x0b, 15, { 0,1,2,0,1,0,2,1,0,1,0,1,2,1,0 } },
+	// seqnum 0xc (29be:18e0, same as 3) ??? Jenny "take a picture"
+	{ 0x0c,  9, { 0,1,2,3,2,2,2,1,0 } },
+	// seqnum 0xd (29be:18f4, same as 4) ??? Jenny big gesture
+	{ 0x0d, 13, { 0,1,2,3,4,5,4,4,4,3,2,1,0 } },
+	// seqnum 0x10 (29be:1956) ??? Jenny short anim
+	{ 0x10,  9, { 0,0,0,1,0,0,0,0,0 } },
+};
 
 void SiteScreen::enter(uint siteNum) {
 	if (!_mystery || !_mystery->isLoaded()) {
@@ -490,28 +574,6 @@ void SiteScreen::enterSiteAnim() {
 	}
 }
 
-// Mask-aware blit from a Picture into a Graphics::Surface.
-static void blitMaskedSurface(Graphics::Surface *screen,
-							  const Picture &p, int x, int y) {
-	if (!screen)
-		return;
-	const byte transp = (byte)(p.flags >> 8);
-	for (int row = 0; row < p.surface.h; row++) {
-		const int dstY = y + row;
-		if (dstY < 0 || dstY >= screen->h)
-			continue;
-		const byte *src = (const byte *)p.surface.getBasePtr(0, row);
-		byte *dst = (byte *)screen->getBasePtr(0, dstY);
-		for (int col = 0; col < p.surface.w; col++) {
-			const int dstX = x + col;
-			if (dstX < 0 || dstX >= screen->w)
-				continue;
-			if (src[col] != transp)
-				dst[dstX] = src[col];
-		}
-	}
-}
-
 void SiteScreen::renderStaticDrops(uint siteNum) {
 	// Loop 2 from `_DoSiteLoop @ 168d:03f4`:
 	//   bound: siteData[+0x4]   (verified at 168d:05c0:
@@ -671,21 +733,9 @@ void SiteScreen::renderPartner(uint siteNum, uint32 tickMs) {
 	//   +0..1 anim Jake, +2..3 anim Jenny,
 	//   +4..5 x    Jake, +6..7 x    Jenny,
 	//   +8..9 y    Jake, +10..11 y    Jenny.
-	// Seven valid entries verified against the bytes at 29be:021c.
-	// Anything past entry 6 in the binary is `_SiteButtons` rect data
-	// that follows the table in memory ??? NOT continuation entries ???
-	// so we cap the table here and skip rendering for siteData[+8] >= 7
-	// (which would indicate corrupt mystery data anyway).
-	static const uint16 kWaitAnims[7][6] = {
-		{ 0x00, 0x0a, 0x06, 0x06, 0x50, 0x50 }, // 0
-		{ 0x03, 0x0c, 0x06, 0x06, 0x50, 0x50 }, // 1
-		{ 0x01, 0x0b, 0x06, 0x06, 0x50, 0x50 }, // 2
-		{ 0x04, 0x0d, 0x06, 0x06, 0x50, 0x50 }, // 3
-		{ 0x02, 0x10, 0x06, 0x06, 0x50, 0x50 }, // 4
-		{ 0x05, 0x05, 0x06, 0x06, 0x50, 0x50 }, // 5
-		{ 0x06, 0x06, 0x06, 0x06, 0x50, 0x50 }, // 6
-	};
-
+	// `kWaitAnims` lives at file scope above; we cap rendering at
+	// `speaker < 7` since anything past entry 6 is the `_SiteButtons`
+	// rect data that follows the table in the binary.
 	const byte *site = _mystery->siteData(siteNum);
 	if (!site)
 		return;
@@ -954,17 +1004,7 @@ void EEMEngine::playKdAnim(uint16 num) {
 	// gesture (Jenny taking a picture, etc.) finishes before the
 	// speaker portrait + speech balloon appear.
 	//
-	// Six valid kdAnimNum entries (0..5). Verified bytes from
-	// `29be:0228`. Layout per entry: { animJake, animJenny, xJake,
-	// xJenny, yJake, yJenny }. Position is (6, 80) in every entry.
-	static const uint16 kKdAnimTable[6][6] = {
-		{ 0x03, 0x0c, 6, 6, 80, 80 }, // 0 ??? speaker idx 1 wait anim
-		{ 0x01, 0x0b, 6, 6, 80, 80 }, // 1 ??? same as PDA idle
-		{ 0x04, 0x0d, 6, 6, 80, 80 }, // 2
-		{ 0x02, 0x10, 6, 6, 80, 80 }, // 3 ??? same as gallery
-		{ 0x05, 0x05, 6, 6, 80, 80 }, // 4 ??? same anim both partners
-		{ 0x06, 0x06, 6, 6, 80, 80 }, // 5 ??? same anim both partners
-	};
+	// `kKdAnimTable` and `kKdScripts` live at file scope above.
 	if (num >= ARRAYSIZE(kKdAnimTable))
 		return;
 
@@ -979,45 +1019,12 @@ void EEMEngine::playKdAnim(uint16 num) {
 		return;
 	}
 
-	// Sequence-script lookup. Entries copied verbatim from
-	// `_AnimationSequences @ 29be:22d4` walked through to the next 0x80.
-	// Each script is a u16[] of frame indices terminated by 0x80; we
-	// don't yet handle 0x81 jumps (none of the kdAnim sequences use
-	// them ??? verified). seqnum == animId for these calls (per
-	// `_PlayAnimation` 172b:1f5d push order).
-	struct Script {
-		uint16 seqnum;
-		uint8 len;
-		uint8 frames[20];  // long enough for any kdAnim script
-	};
-	static const Script kScripts[] = {
-		// seqnum 1 (29be:188a) ??? head bob
-		{ 0x01, 15, { 0,1,2,0,1,0,2,1,0,1,0,1,2,1,0 } },
-		// seqnum 2 (29be:18aa) ??? short blip then long pause
-		{ 0x02, 16, { 0,1,2,1,0,0,0,0,0,0,0,0,0,0,0,0 } },
-		// seqnum 3 (29be:18e0) ??? Jake "lift, hold, lower" gesture
-		{ 0x03,  9, { 0,1,2,3,2,2,2,1,0 } },
-		// seqnum 4 (29be:18f4) ??? bigger gesture (camera flash-style)
-		{ 0x04, 13, { 0,1,2,3,4,5,4,4,4,3,2,1,0 } },
-		// seqnum 5 (29be:1910) ??? held idle with a single peak
-		{ 0x05, 13, { 0,0,0,1,2,3,2,1,0,0,0,0,0 } },
-		// seqnum 6 (29be:192c) ??? empty (immediate END)
-		{ 0x06,  0, { 0 } },
-		// seqnum 0xb (29be:188a, same as 1) ??? Jenny PDA idle
-		{ 0x0b, 15, { 0,1,2,0,1,0,2,1,0,1,0,1,2,1,0 } },
-		// seqnum 0xc (29be:18e0, same as 3) ??? Jenny "take a picture"
-		{ 0x0c,  9, { 0,1,2,3,2,2,2,1,0 } },
-		// seqnum 0xd (29be:18f4, same as 4) ??? Jenny big gesture
-		{ 0x0d, 13, { 0,1,2,3,4,5,4,4,4,3,2,1,0 } },
-		// seqnum 0x10 (29be:1956) ??? Jenny short anim
-		{ 0x10,  9, { 0,0,0,1,0,0,0,0,0 } },
-	};
 	const uint8 *frames = nullptr;
 	uint frameCount = 0;
-	for (uint i = 0; i < ARRAYSIZE(kScripts); i++) {
-		if (kScripts[i].seqnum == animId) {
-			frames = kScripts[i].frames;
-			frameCount = kScripts[i].len;
+	for (uint i = 0; i < ARRAYSIZE(kKdScripts); i++) {
+		if (kKdScripts[i].seqnum == animId) {
+			frames = kKdScripts[i].frames;
+			frameCount = kKdScripts[i].len;
 			break;
 		}
 	}
diff --git a/engines/eem/ui.cpp b/engines/eem/ui.cpp
index 2ea76273497..7c3d181afdd 100644
--- a/engines/eem/ui.cpp
+++ b/engines/eem/ui.cpp
@@ -40,7 +40,24 @@
 
 namespace EEM {
 
-namespace {
+// Five fixed gallery slot positions verified at `29be:0x116`. Used by
+// both `_DrawGallery @ 158f:0046` (notebook gallery) and the accuse
+// portrait grid; the layout is identical so we share the table.
+struct GallerySlot { int x; int y; };
+const GallerySlot kGallerySlots[5] = {
+	{  83,  14 }, // 0
+	{ 155,  14 }, // 1
+	{ 227,  14 }, // 2
+	{ 119,  90 }, // 3
+	{ 191,  90 }  // 4
+};
+
+// `_GetKDTextBalloon @ 1df2:0105` digit-balloon table @ `29be:1064`:
+//   '0'???0x15  '1'???0x16  '2'???0x17  '3'???0x18  '4'???0x19
+//   '5'???0x1a  '6'???0x20  '7'???0x21  '8'???0x22  '9'???0x1e
+const uint16 kDigitBalloons[10] = {
+	0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x20, 0x21, 0x22, 0x1e
+};
 
 // Return the next non-empty slot in `slotRects` starting from `from`,
 // stepping by `dir` (+1 or -1) with wraparound. Used by the accuse
@@ -215,8 +232,6 @@ void drawCaseSelectionFrame(const CaseSelectionView &v) {
 	g_system->updateScreen();
 }
 
-} // anonymous namespace
-
 void EEMEngine::doNewPlayer() {
 	// Mirrors `_NewPlayer` @ 1c33:0dda. The original draws background
 	// 0x104 + character peek pic 0x107, then shows "Please type your
@@ -1259,16 +1274,8 @@ void EEMEngine::drawGalleryFrame(const byte *gd, uint8 numSuspects,
 								  Common::Array<int> &slotSuspect) {
 	// Gallery redraw ??? formerly the `drawFrame` lambda inside `doGallery`.
 	// Mirrors `_DrawGallery @ 158f:0046`: PIC 0x3f frame + partner sprite
-	// at (5, 0x50) + suspect portraits in their `_NewOrder` slots.
-	struct Slot { int x; int y; };
-	static const Slot kGallerySlots[5] = {
-		{  83,  14 }, // 0
-		{ 155,  14 }, // 1
-		{ 227,  14 }, // 2
-		{ 119,  90 }, // 3
-		{ 191,  90 }  // 4
-	};
-
+	// at (5, 0x50) + suspect portraits in their `_NewOrder` slots. Slot
+	// positions live in `kGallerySlots` in this file's anon namespace.
 	Picture galBg;
 	const bool haveBg = _picsArchive.getPicture(0x3f, galBg);
 	const uint partnerAnim = (_partner == 0) ? 2 : 0x10;
@@ -1322,7 +1329,7 @@ void EEMEngine::drawGalleryFrame(const byte *gd, uint8 numSuspects,
 		const uint8 phys = _mystery._newOrder[i];
 		if (phys >= 5)
 			continue;
-		const Slot &s = kGallerySlots[phys];
+		const GallerySlot &s = kGallerySlots[phys];
 
 		const bool discovered = _mystery._inGallery[phys] != 0;
 		if (discovered) {
@@ -1878,9 +1885,7 @@ uint16 EEMEngine::getKDTextBalloon(byte firstChar) const {
 	// reusing the digit-2 slot.
 	if (firstChar < '0' || firstChar > '9')
 		return 0x17;
-	static const uint16 kDigitBalloons[10] = {
-		0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x20, 0x21, 0x22, 0x1e
-	};
+	// `kDigitBalloons` lives at file scope above.
 	return kDigitBalloons[firstChar - '0'];
 }
 
@@ -2265,14 +2270,9 @@ void EEMEngine::drawAccuseGallery(uint8 numSuspects, const byte *gd,
 								   Common::Array<int> &slotSuspect) {
 	// Accuse-gallery redraw ??? formerly the `drawGallery` lambda inside
 	// `doAccuse`. Mirrors `_DoAccuseGallery @ 1df2:0a31` portrait grid:
-	// PIC 0x3f backdrop, suspect portraits at the 5 fixed slots, and a
-	// 1-px outline (palette index 0xFE) around the highlighted slot.
-	struct Slot { int x; int y; };
-	static const Slot kGallerySlots[5] = {
-		{  83,  14 }, { 155,  14 }, { 227,  14 },
-		{ 119,  90 }, { 191,  90 }
-	};
-
+	// PIC 0x3f backdrop, suspect portraits at the 5 fixed slots
+	// (`kGallerySlots` in this file's anon namespace), and a 1-px
+	// outline (palette index 0xFE) around the highlighted slot.
 	Picture accuseBg;
 	const bool haveAccuseBg = _picsArchive.getPicture(0x3f, accuseBg);
 
@@ -2300,7 +2300,7 @@ void EEMEngine::drawAccuseGallery(uint8 numSuspects, const byte *gd,
 		// `_InGallery[phys]` flag is 0 ??? that's the original gate.
 		if (_mystery._inGallery[phys] == 0)
 			continue;
-		const Slot &s = kGallerySlots[phys];
+		const GallerySlot &s = kGallerySlots[phys];
 
 		const uint16 picId = READ_LE_UINT16(gd + i * 0x46);
 		if (picId == 0)


Commit: d44daf096d7f970c7ed8f165b5f98edcd9974878
    https://github.com/scummvm/scummvm/commit/d44daf096d7f970c7ed8f165b5f98edcd9974878
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:20:41+02:00

Commit Message:
EEM: implemented midi music

Changed paths:
  A engines/eem/music.cpp
  A engines/eem/music.h
    engines/eem/eem.cpp
    engines/eem/eem.h
    engines/eem/module.mk
    engines/eem/site.cpp
    engines/eem/ui.cpp


diff --git a/engines/eem/eem.cpp b/engines/eem/eem.cpp
index 86ddd0a9096..0895b869273 100644
--- a/engines/eem/eem.cpp
+++ b/engines/eem/eem.cpp
@@ -35,6 +35,7 @@
 
 #include "eem/detection.h"
 #include "eem/eem.h"
+#include "eem/music.h"
 #include "eem/site.h"
 
 #include "common/config-manager.h"
@@ -94,6 +95,7 @@ EEMEngine::EEMEngine(OSystem *syst, const ADGameDescription *gameDesc)
 }
 
 EEMEngine::~EEMEngine() {
+	delete _music;
 }
 
 Common::Error EEMEngine::run() {
@@ -110,6 +112,10 @@ Common::Error EEMEngine::run() {
 	if (!_font.load(Common::Path("FONT.FNT")))
 		warning("FONT.FNT failed to load; text will not render");
 
+	// MIDI music player. Mirrors `_InitMIDI @ 20a2:013a`. Constructed
+	// here (after `initGraphics` so the OSystem's timer/mixer is up).
+	_music = new MusicPlayer();
+
 	// _InitMouse @ 152d:018b in the original ??? install our 11x16 arrow,
 	// using palette index 0 as the transparency key. The cursor is left
 	// hidden through the opening anims and switched on at NewPlayer /
@@ -156,18 +162,34 @@ Common::Error EEMEngine::run() {
 		}
 	}
 
-	// Reproduces _DoOpeningAnims @ 2520:082a (sans audio):
+	// Reproduces _DoOpeningAnims @ 2520:082a:
 	//   EA Kids logo (PIC) -> HighScore Productions logo (PIC) ->
-	//   Storm Software logo (BOLT.ANM) -> 20 character-intro animations
-	//   (ANIM01.A .. ANIM20.A) -> TITLE.ANM. Click / any key skips a
-	//   single clip; ESC skips the rest of the chain (waitForInput /
-	//   playAnm raise `_skipIntro` so each subsequent step bails out).
+	//   Storm Software logo (BOLT.ANM) -> [music starts] -> 20
+	//   character-intro animations (ANIM01.A..ANIM20.A) -> [music
+	//   restarts] -> TITLE.ANM. Click / any key skips a single clip;
+	//   ESC skips the rest of the chain (waitForInput / playAnm raise
+	//   `_skipIntro` so each subsequent step bails out).
+	//
+	// Music timing (verified at 2520:0883 and 2520:0918):
+	//   - The three logos and `_InitMysterySounds(0x3c)` all run BEFORE
+	//     any `_MIDIPlayFile` call ??? those segments are voice-only.
+	//   - Theme starts with `_LoopMIDI = 0x7fff` right before the
+	//     ANIM01..ANIM20 loop (2520:0883).
+	//   - After the loop the original calls `_CleanMysterySounds` and
+	//     then `_MIDIPlayFile("theme.xmi")` again with `_LoopMIDI =
+	//     0xffff` (2520:0918) to restart the theme for TITLE.ANM.
+	//   - `_StopMIDI()` runs on keypress at the title screen
+	//     (2520:094c).
 	_skipIntro = false;
 	showEAKidsLogo();
 	if (!shouldQuit() && !_skipIntro)
 		showHighScoreLogo();
 	if (!shouldQuit() && !_skipIntro)
 		playAnm(Common::Path("BOLT.ANM"));
+	// Theme begins HERE ??? after the three silent logos, before the
+	// character-intro reel.
+	if (!shouldQuit() && !_skipIntro && _music)
+		_music->playFile(Common::Path("THEME.XMI"), /*loop=*/true);
 	for (int i = 1; i <= 20 && !shouldQuit() && !_skipIntro; i++) {
 		Common::String name = Common::String::format("ANIM%02d.A", i);
 		playAnm(Common::Path(name));
@@ -176,6 +198,10 @@ Common::Error EEMEngine::run() {
 		if (!shouldQuit() && !_skipIntro && i != 20)
 			waitForInput(2000);
 	}
+	// Restart the theme for TITLE.ANM ??? matches the second
+	// `_MIDIPlayFile("theme.xmi")` call at 2520:0918.
+	if (!shouldQuit() && !_skipIntro && _music)
+		_music->playFile(Common::Path("THEME.XMI"), /*loop=*/true);
 	if (!shouldQuit() && !_skipIntro)
 		playAnm(Common::Path("TITLE.ANM"), 120, /*holdLastFrame=*/true);
 	_skipIntro = false;
@@ -187,6 +213,12 @@ Common::Error EEMEngine::run() {
 	// the interactive screens (matches `_MouseCursor = 1` at the tail
 	// of `_NewPlayer`).
 	CursorMan.showMouse(true);
+
+	// Stop the title music ??? the original `_NewPlayer / _DoChoosePartner`
+	// screens have no music until the briefing's `_PlayInSequence` /
+	// per-mystery `_StartTravelMusic` kicks in.
+	if (_music)
+		_music->stop();
 	if (!shouldQuit())
 		doNewPlayer();
 	if (!shouldQuit())
@@ -446,6 +478,30 @@ void EEMEngine::doSiteLoop() {
 	screen.run();
 }
 
+void EEMEngine::startTravelMusic() {
+	// Mirrors `_StartTravelMusic @ 20a2:0595`:
+	//
+	//   for (num = _SiteNumber; num > 4; num -= 5) {}
+	//   if (_MIDIAvailable && _MusicEnabled) {
+	//       if (_IsMIDIPlaying()) _StopMIDI();
+	//       _MIDIPlay(num);
+	//   }
+	//
+	// Five travel tracks: MUS00000.XMI .. MUS00004.XMI, picked by
+	// `_SiteNumber % 5`. The original always loops travel music (the
+	// `_LoopMIDI` global isn't reset between site changes).
+	if (!_music || !_mystery.isLoaded())
+		return;
+	const uint num = _mystery._siteNumber % 5;
+	_music->playMus(num, /*loop=*/true);
+}
+
+void EEMEngine::syncSoundSettings() {
+	Engine::syncSoundSettings();
+	if (_music)
+		_music->syncVolume();
+}
+
 bool EEMEngine::hasFeature(EngineFeature f) const {
 	// We support saving any time but loading only at startup (via the
 	// `--save-slot=N` resume path or a slot picked from the launcher).
diff --git a/engines/eem/eem.h b/engines/eem/eem.h
index 35bf5100ef4..e2f3e0c2ce4 100644
--- a/engines/eem/eem.h
+++ b/engines/eem/eem.h
@@ -39,6 +39,8 @@
 
 namespace EEM {
 
+class MusicPlayer;
+
 /**
  * Screen IDs used by the original ScreenDriver dispatch table at 1a35:0e5e.
  * The table holds 14 (id, handler) entries; the loop iterates until it finds
@@ -243,6 +245,21 @@ private:
 	/// minus the live ANI sequence playback.
 	void doInitClues();
 
+public:
+	/// Mirrors `_StartTravelMusic @ 20a2:0595`. Picks `MUS%05d.XMI`
+	/// based on `_mystery._siteNumber % 5` and starts it (looping). The
+	/// site loop calls this each time `enter(siteNum)` runs so the
+	/// music changes as the player travels between sites.
+	void startTravelMusic();
+
+	/// Forwarded from `Engine::syncSoundSettings`. Re-pulls the user's
+	/// `music_volume` slider into the MIDI player's `_masterVolume`,
+	/// otherwise the AdLib output stays at whatever the slider was at
+	/// the moment `_music` was constructed (and the live launcher
+	/// changes to the volume slider have no effect).
+	void syncSoundSettings() override;
+private:
+
 	Common::String _playerName;  ///< Substituted into 0x80 placeholders
 
 	/// Per-mystery solved state. 0 = unsolved, 1 = solved, 2 = solved
@@ -284,6 +301,12 @@ private:
 	/// accuse contexts use their own composed backdrops). See
 	/// `setPartnerEraseBg`.
 	Graphics::ManagedSurface _partnerEraseBg;
+
+	/// XMIDI music player. Mirrors the original `MIDI.C` family
+	/// (`_MIDIPlayFile`, `_MIDIPlay`, `_StopMIDI`, `_StartTravelMusic`
+	/// at 20a2:00e2-05c9). Constructed lazily during `run()` once the
+	/// MIDI driver / timer system is up.
+	MusicPlayer *_music = nullptr;
 };
 
 } // End of namespace EEM
diff --git a/engines/eem/module.mk b/engines/eem/module.mk
index 5583335ffd2..ce7975e1edc 100644
--- a/engines/eem/module.mk
+++ b/engines/eem/module.mk
@@ -7,6 +7,7 @@ MODULE_OBJS = \
 	font.o \
 	graphics.o \
 	metaengine.o \
+	music.o \
 	mystery.o \
 	resource.o \
 	site.o \
diff --git a/engines/eem/music.cpp b/engines/eem/music.cpp
new file mode 100644
index 00000000000..a1a40adb60e
--- /dev/null
+++ b/engines/eem/music.cpp
@@ -0,0 +1,174 @@
+/* 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 "audio/midiparser.h"
+#include "audio/miles.h"
+
+#include "common/debug.h"
+#include "common/file.h"
+#include "common/textconsole.h"
+
+#include "eem/detection.h"
+#include "eem/music.h"
+
+namespace EEM {
+
+MusicPlayer::MusicPlayer() {
+	// Mirrors `_InitMIDI @ 20a2:013a` which used `_AIL_register_driver`
+	// to walk the .ADV files (ADLIB.ADV, SBFM.ADV, MT32MPU.ADV, etc.)
+	// and pick a backend. We honour the launcher's "Music driver"
+	// setting and only force AdLib / MT-32 paths through Miles when the
+	// detected device matches.
+	const MidiDriver::DeviceHandle dev =
+		MidiDriver::detectDevice(MDT_MIDI | MDT_ADLIB);
+	const MusicType musicType = MidiDriver::getMusicType(dev);
+
+	switch (musicType) {
+	case MT_ADLIB:
+		// `_MIDIPlayFile` (20a2:024c) opens "SAMPLE.AD" (29be:14d6) and
+		// installs every patch the sequence requests via
+		// `_AIL_install_timbre`. ScummVM's Miles AdLib driver does the
+		// same: it reads SAMPLE.AD on construction and serves timbres
+		// out of that bank, which is what makes the notes match the
+		// 1993 release. SAMPLE.OPL would be the OPL3 variant; the game
+		// only ships SAMPLE.AD, so an empty path falls back to OPL2.
+		_milesAudioMode = true;
+		_driver = Audio::MidiDriver_Miles_AdLib_create(
+			Common::Path("SAMPLE.AD"), Common::Path());
+		break;
+	case MT_MT32:
+		// `MT32MPU.ADV` was the original MT-32 driver; ScummVM has no
+		// Miles MT-32 instrument bank for EEM, so we use the standard
+		// MT-32 driver and let the XMIDI's own program changes drive
+		// the patch selection.
+		_milesAudioMode = true;
+		_driver = Audio::MidiDriver_Miles_MT32_create(Common::Path());
+		break;
+	default:
+		_milesAudioMode = false;
+		createDriver(MDT_MIDI | MDT_ADLIB);
+		break;
+	}
+
+	if (_driver) {
+		const int ret = _driver->open();
+		if (ret != 0) {
+			warning("MusicPlayer: MidiDriver::open() failed (%d)", ret);
+			delete _driver;
+			_driver = nullptr;
+		} else {
+			// No GM/MT-32 reset for AdLib (Miles AdLib handles its own
+			// state); for MT-32/GM the original would've sent its own
+			// initialisation patches via `_AIL_install_timbre`, but we
+			// don't have those banks for non-AdLib devices.
+			if (musicType != MT_ADLIB) {
+				if (musicType == MT_MT32 || _nativeMT32)
+					_driver->sendMT32Reset();
+				else
+					_driver->sendGMReset();
+			}
+			_driver->setTimerCallback(this, &timerCallback);
+		}
+	} else {
+		debugC(1, kDebugSound, "MusicPlayer: no MIDI driver ??? music disabled");
+	}
+}
+
+void MusicPlayer::send(uint32 b) {
+	// Miles drivers (both AdLib and MT-32) implement their own per-
+	// source-channel mixing and timbre installation, so just forward
+	// the raw event. Going through `MidiPlayer::send` would re-wrap
+	// CC 7 against `_masterVolume` AND remap the source channel via
+	// `sendToChannel`/`allocateChannel`, both of which the Miles driver
+	// already handles internally (and break the timbre selection if
+	// double-applied).
+	if (_milesAudioMode) {
+		_driver->send(b);
+		return;
+	}
+	Audio::MidiPlayer::send(b);
+}
+
+void MusicPlayer::playFile(const Common::Path &xmiPath, bool loop) {
+	if (!_driver)
+		return;
+
+	Common::StackLock lock(_mutex);
+	stop();
+
+	// Mirrors `_MIDIPlayFile`'s `_fopen` + `_fread` (20a2:024c-029e).
+	Common::File f;
+	if (!f.open(xmiPath)) {
+		warning("MusicPlayer: %s missing", xmiPath.toString().c_str());
+		return;
+	}
+	const uint32 size = f.size();
+	if (size == 0) {
+		warning("MusicPlayer: %s is empty", xmiPath.toString().c_str());
+		return;
+	}
+	_xmiData.resize(size);
+	if (f.read(_xmiData.data(), size) != size) {
+		warning("MusicPlayer: short read on %s",
+				xmiPath.toString().c_str());
+		_xmiData.clear();
+		return;
+	}
+
+	_parser = MidiParser::createParser_XMIDI(nullptr, nullptr, 0);
+	_parser->setMidiDriver(this);
+	_parser->setTimerRate(_driver->getBaseTempo());
+	_parser->property(MidiParser::mpCenterPitchWheelOnUnload, 1);
+	_parser->property(MidiParser::mpSendSustainOffOnNotesOff, 1);
+
+	if (!_parser->loadMusic(_xmiData.data(), _xmiData.size())) {
+		warning("MusicPlayer: XMIDI parser rejected %s",
+				xmiPath.toString().c_str());
+		delete _parser;
+		_parser = nullptr;
+		_xmiData.clear();
+		return;
+	}
+
+	// Mirrors `_LoopMIDI = 0xFFFF` (the count register the original
+	// engine uses for indefinite looping in `_DoOpeningAnims`).
+	_isLooping = loop;
+	_parser->property(MidiParser::mpAutoLoop, loop ? 1 : 0);
+	_parser->setTrack(0);
+
+	// Pull the launcher's music_volume slider into `_masterVolume` so
+	// the non-Miles `Audio::MidiPlayer::send` path scales correctly.
+	// (Miles drivers do their own volume handling on the Multisource
+	// path, but they also honour `MidiDriver::syncSoundSettings` which
+	// `Engine::syncSoundSettings` triggers.)
+	syncVolume();
+	_isPlaying = true;
+	debugC(1, kDebugSound, "MusicPlayer: playing %s (%u bytes, loop=%d, miles=%d)",
+		   xmiPath.toString().c_str(), size, loop, _milesAudioMode);
+}
+
+void MusicPlayer::playMus(uint num, bool loop) {
+	// Format string verified at `29be:1525` ("mus%05d.xmi").
+	const Common::String name = Common::String::format("MUS%05u.XMI", num);
+	playFile(Common::Path(name), loop);
+}
+
+} // End of namespace EEM
diff --git a/engines/eem/music.h b/engines/eem/music.h
new file mode 100644
index 00000000000..f02830cf5cf
--- /dev/null
+++ b/engines/eem/music.h
@@ -0,0 +1,95 @@
+/* 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 EEM_MUSIC_H
+#define EEM_MUSIC_H
+
+#include "audio/midiplayer.h"
+
+#include "common/array.h"
+#include "common/path.h"
+#include "common/scummsys.h"
+
+namespace EEM {
+
+/**
+ * MIDI music player. Mirrors the original `MIDI.C` source file in
+ * `EEMCD.EXE` (the `_MIDIPlayFile / _MIDIPlay / _StopMIDI /
+ * _IsMIDIPlaying / _StartTravelMusic` family at `20a2:00e2-05c9`).
+ *
+ * The original engine uses Miles Audio Interface Library (AIL):
+ *
+ *   - `_InitMIDI @ 20a2:013a` calls `_AIL_register_driver` against
+ *     `ADLIB.ADV` / `SBFM.ADV` / `MT32MPU.ADV` and reserves a timbre
+ *     cache via `_AIL_define_timbre_cache`.
+ *   - `_MIDIPlayFile @ 20a2:024c` opens the .XMI, calls
+ *     `_AIL_register_sequence`, then loops over the sequence's
+ *     `_AIL_timbre_request` results. For every (bank, patch) pair the
+ *     driver asks for, it pulls the AdLib instrument definition from
+ *     **`SAMPLE.AD`** (string at `29be:14d6`) via `_load_global_timbre`
+ *     and installs it through `_AIL_install_timbre`. Only after every
+ *     patch is loaded does it call `_AIL_start_sequence`.
+ *
+ * Without those custom timbres, ScummVM's generic AdLib synth falls
+ * back to its built-in default timbre table ??? same notes, very
+ * different timbres. ScummVM ships a Miles AdLib driver
+ * (`Audio::MidiDriver_Miles_AdLib_create`) that loads `SAMPLE.AD` and
+ * implements the same install-on-demand workflow, so we use it for
+ * AdLib output. MT-32 / GM fall back to the generic driver via
+ * `Audio::MidiPlayer::createDriver`.
+ *
+ * Available music files in the game directory:
+ *   - THEME.XMI   ??? opening anims (looping) + title screen
+ *   - MUS00000.XMI..MUS00004.XMI ??? per-site travel music
+ *     (`_StartTravelMusic` picks one via `_SiteNumber % 5`)
+ *   - MUS00005.XMI ??? winner ending (`_DisplayCorrect` @ 1df2:0789)
+ *   - MUS00006.XMI ??? loser ending (`_DisplayAlibi` @ 1df2:018a)
+ */
+class MusicPlayer : public Audio::MidiPlayer {
+public:
+	MusicPlayer();
+
+	/// Mirrors `_MIDIPlayFile @ 20a2:024c`. Reads the .XMI from the game
+	/// directory and starts playing. `loop=true` mirrors the
+	/// `_LoopMIDI = 0xFFFF` writes inside `_DoOpeningAnims` (theme music).
+	void playFile(const Common::Path &xmiPath, bool loop = false);
+
+	/// Mirrors `_MIDIPlay(num) @ 20a2:047d`. Composes the filename
+	/// "MUS%05u.XMI" and plays it. Used by `_StartTravelMusic`,
+	/// `_DisplayCorrect` (winner), `_DisplayAlibi` (loser).
+	void playMus(uint num, bool loop = false);
+
+	// In Miles AdLib mode the driver allocates its own AdLib voice
+	// pool and consumes the XMIDI's source-channel byte directly, so we
+	// must NOT route through `Audio::MidiPlayer::sendToChannel` (which
+	// remaps every source channel through `allocateChannel()` and
+	// breaks the timbre selection / volume scaling the Miles driver
+	// performs internally). Same workaround Toltecs / SAGA use.
+	void send(uint32 b) override;
+
+private:
+	bool _milesAudioMode = false;
+	Common::Array<byte> _xmiData;
+};
+
+} // End of namespace EEM
+
+#endif
diff --git a/engines/eem/site.cpp b/engines/eem/site.cpp
index 4b641dc441c..ea50d7f56c5 100644
--- a/engines/eem/site.cpp
+++ b/engines/eem/site.cpp
@@ -191,6 +191,11 @@ void SiteScreen::enter(uint siteNum) {
 	debugC(1, kDebugSite, "Entering site %u (%u hotspots)",
 		   siteNum, _mystery->hotspotCount(siteNum));
 
+	// `_DoTravel @ 168d:02da` calls `_StartTravelMusic` after the
+	// destination is set. We do the same here so the music swaps as
+	// the player moves between sites.
+	_vm->startTravelMusic();
+
 	// Palette: original `_BuildBackground` calls `GetPalette(sitenum + 1)`
 	// where sitenum is the global SITES.DBD index (= the per-mystery
 	// `sitepic` field), not the per-mystery site index.
diff --git a/engines/eem/ui.cpp b/engines/eem/ui.cpp
index 7c3d181afdd..fe306ef463d 100644
--- a/engines/eem/ui.cpp
+++ b/engines/eem/ui.cpp
@@ -32,6 +32,7 @@
 
 #include "eem/detection.h"
 #include "eem/eem.h"
+#include "eem/music.h"
 
 // EEM ??? UI screens (NOTE.C, GALLERY.C, ACCUSE.C, MAP.C, CHOOSE.C combined).
 // Each function is a self-contained modal `EEMEngine::doX()` reachable from
@@ -2250,6 +2251,12 @@ void EEMEngine::doAccuse() {
 		if (mn < sizeof(_mysteriesSolved)) {
 			_mysteriesSolved[mn] = _mystery._firstTry ? 2 : 1;
 		}
+
+		// `_DisplayCorrect @ 1df2:073c` calls `_MIDIPlay(5)` (1df2:0789)
+		// before `_DifferenceAnimation("scrapbk.ani")` to swap from the
+		// travel music to the winner cue.
+		if (_music)
+			_music->playMus(5, /*loop=*/false);
 		playAnm(Common::Path("SCRAPBK.ANI"), 120, true);
 
 		// Auto-save into slot 0 (the engine's quicksave slot).


Commit: ffc4f5135bce80fbc259c39850fa74c3f8d13187
    https://github.com/scummvm/scummvm/commit/ffc4f5135bce80fbc259c39850fa74c3f8d13187
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:20:41+02:00

Commit Message:
EEM: implemented digitalized sound

Changed paths:
  A engines/eem/audio.cpp
  A engines/eem/audio.h
    engines/eem/clues.cpp
    engines/eem/eem.cpp
    engines/eem/eem.h
    engines/eem/module.mk
    engines/eem/site.cpp
    engines/eem/ui.cpp


diff --git a/engines/eem/audio.cpp b/engines/eem/audio.cpp
new file mode 100644
index 00000000000..fab34529b71
--- /dev/null
+++ b/engines/eem/audio.cpp
@@ -0,0 +1,345 @@
+/* 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 "audio/decoders/raw.h"
+#include "audio/decoders/voc.h"
+
+#include "common/compression/dcl.h"
+#include "common/debug.h"
+#include "common/endian.h"
+#include "common/events.h"
+#include "common/memstream.h"
+#include "common/substream.h"
+#include "common/system.h"
+#include "common/textconsole.h"
+
+#include "eem/audio.h"
+#include "eem/detection.h"
+#include "eem/eem.h"
+
+namespace EEM {
+
+AudioPlayer::AudioPlayer(EEMEngine *vm) :
+	_vm(vm), _mixer(g_system->getMixer()) {
+}
+
+AudioPlayer::~AudioPlayer() {
+	stopAll();
+}
+
+void AudioPlayer::stopAll() {
+	stopVoice();
+	stopSpool();
+	cleanMysterySounds();
+}
+
+// VOC playback --------------------------------------------------------
+
+void AudioPlayer::playVoc(const Common::Path &vocPath) {
+	stopVoice();
+
+	// Mirrors `_LoadSoundName`'s `_fopen` (1ff1:02ac).
+	Common::File *f = new Common::File();
+	if (!f->open(vocPath)) {
+		warning("AudioPlayer: %s missing", vocPath.toString().c_str());
+		delete f;
+		return;
+	}
+
+	Audio::SeekableAudioStream *stream =
+		Audio::makeVOCStream(f, Audio::FLAG_UNSIGNED, DisposeAfterUse::YES);
+	if (!stream) {
+		warning("AudioPlayer: %s is not a valid VOC",
+				vocPath.toString().c_str());
+		return;
+	}
+
+	// `_PlayVoice` (1ff1:023e) goes through `_AIL_play_VOC_file` on the
+	// digital channel ??? we route through `kSpeechSoundType` so the
+	// launcher's "Speech volume" slider applies.
+	_mixer->playStream(Audio::Mixer::kSpeechSoundType, &_voiceHandle,
+					   stream, -1, Audio::Mixer::kMaxChannelVolume,
+					   0, DisposeAfterUse::YES);
+	debugC(1, kDebugSound, "AudioPlayer: playVoc(%s)",
+		   vocPath.toString().c_str());
+}
+
+bool AudioPlayer::isVoicePlaying() const {
+	return _mixer->isSoundHandleActive(_voiceHandle);
+}
+
+void AudioPlayer::waitForVoiceDone(uint32 maxMs) {
+	// Mirrors the wait loop at `_WaitForVoiceDone @ 1ff1:0221` ??? pumps
+	// events (so animations + abort-on-click still work) while waiting
+	// for the AIL voice channel to drain.
+	const uint32 startMs = g_system->getMillis();
+	while (isVoicePlaying() && !_vm->shouldQuit() &&
+		   g_system->getMillis() - startMs < maxMs) {
+		Common::Event event;
+		while (g_system->getEventManager()->pollEvent(event)) {
+			if (event.type == Common::EVENT_QUIT ||
+				event.type == Common::EVENT_RETURN_TO_LAUNCHER ||
+				event.type == Common::EVENT_LBUTTONDOWN ||
+				event.type == Common::EVENT_KEYDOWN) {
+				stopVoice();
+				return;
+			}
+		}
+		g_system->updateScreen();
+		g_system->delayMillis(10);
+	}
+}
+
+void AudioPlayer::stopVoice() {
+	if (_mixer->isSoundHandleActive(_voiceHandle))
+		_mixer->stopHandle(_voiceHandle);
+}
+
+// Spool sound ---------------------------------------------------------
+
+bool AudioPlayer::readSdxIndex(const Common::Path &sdxPath) {
+	// Mirrors `_ReadLong(&MysterySounds, size, sdx)` at 202f:0647 ???
+	// reads the entire .SDX into memory; each 12-byte entry is
+	// (u32 offset, u32 compressed_size, u32 uncompressed_size).
+	Common::File f;
+	if (!f.open(sdxPath)) {
+		warning("AudioPlayer: %s missing", sdxPath.toString().c_str());
+		return false;
+	}
+	const uint32 size = f.size();
+	if (size == 0 || (size % 12) != 0) {
+		warning("AudioPlayer: %s has invalid size %u",
+				sdxPath.toString().c_str(), size);
+		return false;
+	}
+	const uint count = size / 12;
+	_sdxIndex.resize(count);
+	for (uint i = 0; i < count; i++) {
+		_sdxIndex[i].offset           = f.readUint32LE();
+		_sdxIndex[i].compressedSize   = f.readUint32LE();
+		_sdxIndex[i].uncompressedSize = f.readUint32LE();
+	}
+	return true;
+}
+
+bool AudioPlayer::initMysterySounds(uint mysteryNum) {
+	// Mirrors `_InitMysterySounds @ 202f:05cb` ??? calls
+	// `_CleanMysterySounds` first, then sprintf-opens `m%u.sdx` (string
+	// at 29be:144f) and `m%u.sdb` (29be:145b).
+	cleanMysterySounds();
+
+	const Common::String sdxName = Common::String::format("M%u.SDX", mysteryNum);
+	const Common::String sdbName = Common::String::format("M%u.SDB", mysteryNum);
+	const Common::Path sdxPath(sdxName);
+	const Common::Path sdbPath(sdbName);
+
+	if (!readSdxIndex(sdxPath)) {
+		_sdxIndex.clear();
+		return false;
+	}
+	_sdbPath = sdbPath;
+	_currentMystery = (int)mysteryNum;
+	debugC(1, kDebugSound, "AudioPlayer: mystery %u ??? %u sounds",
+		   mysteryNum, (uint)_sdxIndex.size());
+	return true;
+}
+
+void AudioPlayer::cleanMysterySounds() {
+	stopSpool();
+	_sdxIndex.clear();
+	_sdbPath = Common::Path();
+	_currentMystery = -1;
+}
+
+void AudioPlayer::playPcmBuffer(byte *pcm, uint32 size, uint sampleRate,
+								Audio::SoundHandle &handle,
+								Audio::Mixer::SoundType type) {
+	// `Audio::makeRawStream` takes ownership and `free()`s the buffer
+	// on stream destruction ??? so the caller must `malloc` (not `new`)
+	// the PCM buffer.
+	Audio::SeekableAudioStream *stream =
+		Audio::makeRawStream(pcm, size, sampleRate, Audio::FLAG_UNSIGNED,
+							 DisposeAfterUse::YES);
+	if (!stream) {
+		free(pcm);
+		warning("AudioPlayer: makeRawStream failed");
+		return;
+	}
+	_mixer->playStream(type, &handle, stream, -1,
+					   Audio::Mixer::kMaxChannelVolume, 0,
+					   DisposeAfterUse::YES);
+}
+
+void AudioPlayer::spoolSound(uint num) {
+	if (_currentMystery < 0 || num >= _sdxIndex.size()) {
+		warning("AudioPlayer: spoolSound(%u) ??? invalid index (%d, %u)",
+				num, _currentMystery, (uint)_sdxIndex.size());
+		return;
+	}
+	const SoundEntry &entry = _sdxIndex[num];
+
+	stopSpool();
+
+	Common::File sdb;
+	if (!sdb.open(_sdbPath)) {
+		warning("AudioPlayer: %s missing", _sdbPath.toString().c_str());
+		return;
+	}
+	if (!sdb.seek(entry.offset)) {
+		warning("AudioPlayer: %s seek to %u failed",
+				_sdbPath.toString().c_str(), entry.offset);
+		return;
+	}
+
+	// Mirrors the two `_fgetc(in)` reads at 202f:02da-e1: byte 0 =
+	// Sound Blaster Time Constant, byte 1 = total AIL playback blocks.
+	// Convert TC -> sample rate via the standard SB formula. The block
+	// count is only used internally by the AIL DDS pipeline; ScummVM's
+	// mixer doesn't need it.
+	const byte tc          = sdb.readByte();
+	(void)sdb.readByte(); // total blocks ??? unused outside AIL
+
+	// SB Time Constant: rate = 1000000 / (256 - tc). e.g. tc=0xD2 ???
+	// 22 kHz. Guard against the degenerate tc=0xFF (would divide by 1
+	// ??? 1 MHz, well above what the mixer can resample sanely).
+	const uint sampleRate = (tc < 0xFF)
+		? (uint)(1000000u / (256u - tc))
+		: 44100u;
+
+	byte *pcm = nullptr;
+	uint32 audioSize = 0;
+
+	if (entry.compressedSize == entry.uncompressedSize) {
+		// `_UncompressedSound @ 202f:03e6` ??? already raw PCM. The
+		// `_SpoolSound` equality check at 202f:06e6 is `comp == uncomp`;
+		// in that case `len = uncompressed_size` bytes follow the
+		// 2-byte header. Original reads in 16 KB chunks; we slurp the
+		// lot at once.
+		audioSize = entry.uncompressedSize;
+		pcm = (byte *)malloc(audioSize);
+		if (!pcm) {
+			warning("AudioPlayer: spoolSound %u oom (%u bytes)",
+					num, audioSize);
+			return;
+		}
+		if (sdb.read(pcm, audioSize) != audioSize) {
+			warning("AudioPlayer: short read on uncompressed sound %u", num);
+			free(pcm);
+			return;
+		}
+	} else {
+		// `_DeCompressSound @ 202f:02ad` ??? `EXPLODE @ 25c6:0d01`
+		// (PKWARE DCL "Implode") with READDISKSOUND/WRITESOUND
+		// callbacks. EXPLODE drives both ends via its OWN end-of-stream
+		// marker (length token 519); the SDX `compressed_size` /
+		// `uncompressed_size` are loose hints, NOT exact lengths ???
+		// 202f:0332 even computes `destSize - 2` and never reads it.
+		// ScummVM's fixed-size `decompressDCL` overload errors when
+		// the actual output exceeds our pre-allocated buffer (which
+		// we saw on every M0 clue voice). Use the dynamic-sized
+		// overload instead ??? it lets the DCL stream terminate at its
+		// own marker. Source is bounded to the rest of the SDB so
+		// the bit reader can't fall off the end.
+		const uint32 streamStart = (uint32)sdb.pos();
+		Common::SeekableSubReadStream sub(&sdb, streamStart,
+										   (uint32)sdb.size(),
+										   DisposeAfterUse::NO);
+		Common::SeekableReadStream *out = Common::decompressDCL(&sub);
+		if (!out) {
+			warning("AudioPlayer: DCL decompression failed on sound %u "
+					"(comp=%u, uncomp=%u)",
+					num, entry.compressedSize, entry.uncompressedSize);
+			return;
+		}
+		audioSize = (uint32)out->size();
+		pcm = (byte *)malloc(audioSize);
+		if (!pcm) {
+			warning("AudioPlayer: spoolSound %u oom after DCL (%u bytes)",
+					num, audioSize);
+			delete out;
+			return;
+		}
+		out->read(pcm, audioSize);
+		delete out;
+	}
+
+	debugC(1, kDebugSound,
+		   "AudioPlayer: spoolSound(%u) tc=0x%02x rate=%u size=%u %s",
+		   num, tc, sampleRate, audioSize,
+		   entry.compressedSize == entry.uncompressedSize ? "raw" : "DCL");
+
+	// `_AIL_start_digital_playback` at 202f:040c ??? we route through
+	// `kSFXSoundType` so the launcher's "SFX volume" slider applies
+	// (this is the same slider the original would've targeted via
+	// `_AIL_set_digital_master_volume`). Voice clips on the spool path
+	// are gameplay SFX, not the speech-only VOC stream.
+	playPcmBuffer(pcm, audioSize, sampleRate, _spoolHandle,
+				  Audio::Mixer::kSFXSoundType);
+}
+
+bool AudioPlayer::isSpoolPlaying() const {
+	return _mixer->isSoundHandleActive(_spoolHandle);
+}
+
+void AudioPlayer::waitForSpoolDone(uint32 maxMs) {
+	const uint32 startMs = g_system->getMillis();
+	while (isSpoolPlaying() && !_vm->shouldQuit() &&
+		   g_system->getMillis() - startMs < maxMs) {
+		Common::Event event;
+		while (g_system->getEventManager()->pollEvent(event)) {
+			if (event.type == Common::EVENT_QUIT ||
+				event.type == Common::EVENT_RETURN_TO_LAUNCHER ||
+				event.type == Common::EVENT_LBUTTONDOWN ||
+				event.type == Common::EVENT_KEYDOWN) {
+				stopSpool();
+				return;
+			}
+		}
+		g_system->updateScreen();
+		g_system->delayMillis(10);
+	}
+}
+
+void AudioPlayer::stopSpool() {
+	if (_mixer->isSoundHandleActive(_spoolHandle))
+		_mixer->stopHandle(_spoolHandle);
+}
+
+void AudioPlayer::sayKDDigital(const byte *kdTextIndex, uint kdspeak,
+							   uint partner) {
+	if (!kdTextIndex || _currentMystery < 0)
+		return;
+	// `_SayKDDigital @ 2404:0fbc`:
+	//   iVar1 = kdspeak * 2;
+	//   if (_Partner == 0) iVar1++;            // Jake offset
+	//   sound = *(u16 *)(KDDigitalIndex + (iVar1 + 1) * 2) - 1;
+	//   _SpoolSound(sound);
+	// KDDigitalIndex sits 18 bytes (`+ 0x12`) after KDTextIndex per
+	// `_ReadMystery` 2404:0163-0167.
+	const byte *digital = kdTextIndex + 0x12;
+	const uint slot = (kdspeak * 2) + (partner == 0 ? 1u : 0u) + 1u;
+	const uint16 raw = READ_LE_UINT16(digital + slot * 2);
+	if (raw == 0 || raw == 0xFFFF)
+		return;
+	spoolSound((uint)(raw - 1));
+}
+
+} // End of namespace EEM
diff --git a/engines/eem/audio.h b/engines/eem/audio.h
new file mode 100644
index 00000000000..c9238b0b3f4
--- /dev/null
+++ b/engines/eem/audio.h
@@ -0,0 +1,159 @@
+/* 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 EEM_AUDIO_H
+#define EEM_AUDIO_H
+
+#include "audio/mixer.h"
+#include "audio/audiostream.h"
+
+#include "common/array.h"
+#include "common/file.h"
+#include "common/path.h"
+#include "common/scummsys.h"
+
+namespace EEM {
+
+class EEMEngine;
+
+/**
+ * Non-MIDI audio (digitised voice + sound effects). Mirrors the
+ * original `SOUND.C` / `SPOOLSND.C` source files in `EEMCD.EXE` ???
+ * the AIL digital playback path used for both standalone .VOC files
+ * and the per-mystery `M%d.SDB` spool stream.
+ *
+ * Two pathways:
+ *
+ * 1. **VOC playback** ??? `_LoadSoundName @ 1ff1:0299` reads a Creative
+ *    Voice File into memory and `_PlayVoice @ 1ff1:023e` hands it to
+ *    `_AIL_play_VOC_file`. Used for THUNDER.VOC (Storm logo),
+ *    PHONE.VOC (briefing), JEN.VOC / JAKE.VOC (partner choose).
+ *
+ * 2. **Spool stream** ??? `_InitMysterySounds @ 202f:05cb` opens
+ *    `m%d.sdx` (29be:144f) and `m%d.sdb` (29be:145b) for the active
+ *    mystery. `_SpoolSound(num) @ 202f:068d` indexes into the SDX
+ *    table (12 bytes per entry: u32 file_offset, u32 compressed_size,
+ *    u32 uncompressed_size). If sizes match it streams via
+ *    `_UncompressedSound @ 202f:03e6`; otherwise it EXPLODE-decompresses
+ *    via `_DeCompressSound @ 202f:02ad`.
+ *
+ *    Each entry's data starts with 2 metadata bytes ??? Sound Blaster
+ *    Time Constant (`rate = 1000000 / (256 - tc)`) and the AIL block
+ *    count ??? followed by the (optionally) PKWARE-DCL-compressed
+ *    8-bit unsigned PCM stream. We use ScummVM's `Common::decompressDCL`
+ *    + `Audio::makeRawStream` to reach the same audio at the same
+ *    sample rate.
+ *
+ * Mystery 60 (`M60.SDB/SDX`) holds the 19 voiceovers played between
+ * ANIM01..ANIM20 in `_DoOpeningAnims` (it loads `_InitMysterySounds(0x3c)`
+ * before the loop and `_SpoolSound(uVar3 - 1)` between every clip).
+ * Mysteries 0..55 hold each case's per-clue voice plus the partner's
+ * digital lines (`_KDDigitalIndex` table within the .SD blob).
+ */
+class AudioPlayer {
+public:
+	explicit AudioPlayer(EEMEngine *vm);
+	~AudioPlayer();
+
+	// VOC playback ----------------------------------------------------
+
+	/// Mirrors `_LoadSoundName` + `_PlayVoice`. Loads the named .VOC
+	/// from the game directory and hands it to the speech mixer
+	/// channel. A new `playVoc` cancels any prior voice.
+	void playVoc(const Common::Path &vocPath);
+
+	/// Mirrors `_VoicePlaying @ 1ff1:01f9`.
+	bool isVoicePlaying() const;
+
+	/// Mirrors `_WaitForVoiceDone @ 1ff1:0221`. Blocks (with frame /
+	/// event pumping, like the rest of the engine's busy-loops) until
+	/// the voice clip finishes. Returns early if the user clicks /
+	/// presses a key ??? same abort behaviour the original
+	/// `_AIL_stop_digital_playback` callback installed.
+	void waitForVoiceDone(uint32 maxMs = 60000);
+
+	/// Mirrors `_StopTheVoice @ 1ff1:0283`.
+	void stopVoice();
+
+	// Mystery sound spool ---------------------------------------------
+
+	/// Mirrors `_InitMysterySounds @ 202f:05cb`. Loads `M%u.SDX` into
+	/// memory and remembers the corresponding `M%u.SDB` path.
+	bool initMysterySounds(uint mysteryNum);
+
+	/// Mirrors `_CleanMysterySounds @ 202f:05a5`.
+	void cleanMysterySounds();
+
+	/// Mirrors `_SpoolSound @ 202f:068d`. Reads + decompresses entry
+	/// `num` from the active SDB and queues it for SFX playback. The
+	/// original blocks until playback finishes ??? for ScummVM we let
+	/// the mixer run asynchronously and expose `waitForSpoolDone` so
+	/// callers that need the original "block-then-continue" semantics
+	/// can opt in.
+	void spoolSound(uint num);
+
+	/// Mirrors the abort-on-input wait loop inside `_UncompressedSound`
+	/// / `_DeCompressSound`. Returns when the spool clip finishes or
+	/// the user clicks / presses a key.
+	void waitForSpoolDone(uint32 maxMs = 60000);
+
+	/// Mirrors the immediate `_AIL_stop_digital_playback` exit.
+	void stopSpool();
+
+	bool isSpoolPlaying() const;
+
+	/// Mirrors `_SayKDDigital(kdspeak) @ 2404:0fbc`. Each mystery
+	/// embeds a `KDDigitalIndex` table immediately after the 18-byte
+	/// `KDTextIndex` header (set up by `_ReadMystery @ 2404:008f`:
+	/// `_KDDigitalIndex = _KDTextIndex + 0x12`). The table is two
+	/// 1-based sound indices per `kdspeak` slot ??? Jen at +2, Jake at
+	/// +4 from each entry's start (+1 word for the unused header
+	/// slot). Pass the mystery's `kdTextIndex()` pointer.
+	void sayKDDigital(const byte *kdTextIndex, uint kdspeak, uint partner);
+
+	/// Mirrors `_QuitSounds @ 1ff1:03c5`.
+	void stopAll();
+
+private:
+	struct SoundEntry {
+		uint32 offset;
+		uint32 compressedSize;
+		uint32 uncompressedSize;
+	};
+
+	bool readSdxIndex(const Common::Path &sdxPath);
+	void playPcmBuffer(byte *pcm, uint32 size, uint sampleRate,
+					   Audio::SoundHandle &handle,
+					   Audio::Mixer::SoundType type);
+
+	EEMEngine *_vm = nullptr;
+	Audio::Mixer *_mixer = nullptr;
+	Audio::SoundHandle _voiceHandle;
+	Audio::SoundHandle _spoolHandle;
+
+	Common::Array<SoundEntry> _sdxIndex;
+	Common::Path _sdbPath;
+	int _currentMystery = -1;
+};
+
+} // End of namespace EEM
+
+#endif
diff --git a/engines/eem/clues.cpp b/engines/eem/clues.cpp
index e5fc4df255c..a3907d32ef3 100644
--- a/engines/eem/clues.cpp
+++ b/engines/eem/clues.cpp
@@ -30,6 +30,7 @@
 #include "graphics/cursorman.h"
 #include "graphics/managed_surface.h"
 
+#include "eem/audio.h"
 #include "eem/detection.h"
 #include "eem/eem.h"
 
@@ -228,6 +229,15 @@ void EEMEngine::doChoosePartner() {
 		g_system->updateScreen();
 		g_system->delayMillis(20);
 	}
+
+	// Mirrors the tail of `_DoChoosePartner @ 1a35:097f` ??? once the
+	// player commits to a partner, load and play their intro VOC
+	// (`jen.voc` for Jenny, `jake.voc` for Jake; strings at 29be:0af1 /
+	// 29be:0af9) and block on `_WaitForVoiceDone`.
+	if (_audio) {
+		_audio->playVoc(Common::Path(_partner == 0 ? "JAKE.VOC" : "JEN.VOC"));
+		_audio->waitForVoiceDone();
+	}
 }
 
 void EEMEngine::doInitClues() {
@@ -409,6 +419,15 @@ void EEMEngine::doInitClues() {
 		}
 	}
 
+	// `_DoInitClues` plays `phone.voc` (29be:0acc) ONLY when caseType == 2
+	// (the "incoming call" briefing variant). Verified at 1a35:05a2 ???
+	// the gate is `iVar1 == 2 && _VoiceAvailable`. Other case types open
+	// straight into the briefing dialogue without it.
+	if (caseType == 2 && _audio) {
+		_audio->playVoc(Common::Path("PHONE.VOC"));
+		_audio->waitForVoiceDone();
+	}
+
 	// Step 6 ??? case briefing dialogue.
 	displayClue(ib + 4);
 }
@@ -686,6 +705,20 @@ void EEMEngine::displayClue(const byte *clueBlock) {
 			g_system->updateScreen();
 		}
 
+		// `_DisplayClue` @ 2404:0833-085a ??? after the balloon is drawn,
+		// spool the per-clue voice. Each ClueEntry stores two 1-based
+		// sound indices: `+0x18` for partner=Jenny and `+0x1a` for
+		// partner=Jake (verified against 2404:0823-0834). Index 0 / -1
+		// = no audio. The original blocks until the line ends; we run
+		// async (the wait happens implicitly while the player reads).
+		if (_audio) {
+			const uint16 voiceJenny = READ_LE_UINT16(c + 0x18);
+			const uint16 voiceJake  = READ_LE_UINT16(c + 0x1a);
+			const uint16 voice = (_partner == 0) ? voiceJake : voiceJenny;
+			if (voice != 0 && voice != 0xFFFF)
+				_audio->spoolSound((uint)(voice - 1));
+		}
+
 		// Wait for click/key to advance ??? only if we drew something.
 		// ESC skips the entire dialogue rather than just one entry.
 		if (hasText || (charPicId != 0 && charPicId != 0xFFFF)) {
diff --git a/engines/eem/eem.cpp b/engines/eem/eem.cpp
index 0895b869273..c6e2552cf79 100644
--- a/engines/eem/eem.cpp
+++ b/engines/eem/eem.cpp
@@ -33,6 +33,7 @@
 #include "graphics/cursorman.h"
 #include "graphics/paletteman.h"
 
+#include "eem/audio.h"
 #include "eem/detection.h"
 #include "eem/eem.h"
 #include "eem/music.h"
@@ -95,6 +96,7 @@ EEMEngine::EEMEngine(OSystem *syst, const ADGameDescription *gameDesc)
 }
 
 EEMEngine::~EEMEngine() {
+	delete _audio;
 	delete _music;
 }
 
@@ -116,6 +118,12 @@ Common::Error EEMEngine::run() {
 	// here (after `initGraphics` so the OSystem's timer/mixer is up).
 	_music = new MusicPlayer();
 
+	// Digital audio (VOC + spool). Mirrors `_InitDrivers @ 1ff1:0368`
+	// which `_AIL_register_driver`s SBDIG.ADV / PASDIG.ADV alongside
+	// the MIDI driver.
+	_audio = new AudioPlayer(this);
+	syncSoundSettings();
+
 	// _InitMouse @ 152d:018b in the original ??? install our 11x16 arrow,
 	// using palette index 0 as the transparency key. The cursor is left
 	// hidden through the opening anims and switched on at NewPlayer /
@@ -184,8 +192,22 @@ Common::Error EEMEngine::run() {
 	showEAKidsLogo();
 	if (!shouldQuit() && !_skipIntro)
 		showHighScoreLogo();
-	if (!shouldQuit() && !_skipIntro)
+	// Storm Software logo: voice + animation. The original at
+	// `_ShowStormLogo @ 2520:0707` calls `_LoadSoundName("thunder.voc")`
+	// (29be:177d) and passes the buffer to `OpenDifferenceAnimation_Sound`
+	// so the thunder roar plays alongside the lightning bolt.
+	if (!shouldQuit() && !_skipIntro) {
+		if (_audio)
+			_audio->playVoc(Common::Path("THUNDER.VOC"));
 		playAnm(Common::Path("BOLT.ANM"));
+		if (_audio)
+			_audio->stopVoice();
+	}
+	// `_InitMysterySounds(0x3c)` at 2520:086a ??? load M60.SDX/SDB so
+	// `_SpoolSound(uVar3 - 1)` between the ANIM01..ANIM20 anims has
+	// data to draw from.
+	if (!shouldQuit() && !_skipIntro && _audio)
+		_audio->initMysterySounds(60);
 	// Theme begins HERE ??? after the three silent logos, before the
 	// character-intro reel.
 	if (!shouldQuit() && !_skipIntro && _music)
@@ -193,11 +215,18 @@ Common::Error EEMEngine::run() {
 	for (int i = 1; i <= 20 && !shouldQuit() && !_skipIntro; i++) {
 		Common::String name = Common::String::format("ANIM%02d.A", i);
 		playAnm(Common::Path(name));
-		// Between anims the original plays a voice clip via _SpoolSound;
-		// without audio we still want a beat so each scene reads.
-		if (!shouldQuit() && !_skipIntro && i != 20)
-			waitForInput(2000);
+		// `_SpoolSound(uVar3 - 1)` at 2520:08c2 ??? the per-character VO
+		// plays AFTER each anim except the last (`if (uVar3 != 0x14)`
+		// at 2520:08a8). Original blocks until done; we run async and
+		// wait so the next anim doesn't start before the line ends.
+		if (!shouldQuit() && !_skipIntro && i != 20 && _audio) {
+			_audio->spoolSound((uint)(i - 1));
+			_audio->waitForSpoolDone();
+		}
 	}
+	// `_CleanMysterySounds` at 2520:0903 ??? release M60 before the title.
+	if (_audio)
+		_audio->cleanMysterySounds();
 	// Restart the theme for TITLE.ANM ??? matches the second
 	// `_MIDIPlayFile("theme.xmi")` call at 2520:0918.
 	if (!shouldQuit() && !_skipIntro && _music)
@@ -590,6 +619,11 @@ Common::Error EEMEngine::loadGameState(int slot) {
 		delete in;
 		return Common::kReadingFailed;
 	}
+	// `_ReadMystery @ 2404:008f` calls `_InitMysterySounds(_MysteryNumber)`
+	// at the tail (2404:0298) so the SDB index is in place for clue and
+	// partner-speech spool sounds.
+	if (_audio)
+		_audio->initMysterySounds(mysteryNum);
 
 	Common::Serializer s(in, nullptr);
 	s.setVersion(ver);
diff --git a/engines/eem/eem.h b/engines/eem/eem.h
index e2f3e0c2ce4..4eded42e068 100644
--- a/engines/eem/eem.h
+++ b/engines/eem/eem.h
@@ -39,6 +39,7 @@
 
 namespace EEM {
 
+class AudioPlayer;
 class MusicPlayer;
 
 /**
@@ -307,6 +308,13 @@ private:
 	/// at 20a2:00e2-05c9). Constructed lazily during `run()` once the
 	/// MIDI driver / timer system is up.
 	MusicPlayer *_music = nullptr;
+
+	/// Digitised audio (voice + SFX). Mirrors `SOUND.C` / `SPOOLSND.C`
+	/// ??? VOC playback (`_PlayVoice @ 1ff1:023e`) and the per-mystery
+	/// SDB spool (`_SpoolSound @ 202f:068d` / `_InitMysterySounds @
+	/// 202f:05cb`). Constructed alongside `_music` in `run()`.
+public:
+	AudioPlayer *_audio = nullptr;
 };
 
 } // End of namespace EEM
diff --git a/engines/eem/module.mk b/engines/eem/module.mk
index ce7975e1edc..6879b600e85 100644
--- a/engines/eem/module.mk
+++ b/engines/eem/module.mk
@@ -2,6 +2,7 @@ MODULE := engines/eem
 
 MODULE_OBJS = \
 	animation.o \
+	audio.o \
 	clues.o \
 	eem.o \
 	font.o \
diff --git a/engines/eem/site.cpp b/engines/eem/site.cpp
index ea50d7f56c5..e6122b5d6eb 100644
--- a/engines/eem/site.cpp
+++ b/engines/eem/site.cpp
@@ -26,6 +26,7 @@
 
 #include "graphics/paletteman.h"
 
+#include "eem/audio.h"
 #include "eem/detection.h"
 #include "eem/eem.h"
 #include "eem/mystery.h"
@@ -380,6 +381,8 @@ void SiteScreen::run() {
 				case Common::KEYCODE_r:
 					// Restart the mystery from scratch (mirrors `_ReloadMystery`).
 					if (_mystery->load(_mystery->number())) {
+						if (_vm->_audio)
+							_vm->_audio->initMysterySounds(_mystery->number());
 						cur = 0;
 						enter(cur);
 					}
diff --git a/engines/eem/ui.cpp b/engines/eem/ui.cpp
index fe306ef463d..121a83d440f 100644
--- a/engines/eem/ui.cpp
+++ b/engines/eem/ui.cpp
@@ -30,6 +30,7 @@
 #include "graphics/cursorman.h"
 #include "graphics/managed_surface.h"
 
+#include "eem/audio.h"
 #include "eem/detection.h"
 #include "eem/eem.h"
 #include "eem/music.h"
@@ -522,6 +523,8 @@ void EEMEngine::doCaseSelection() {
 		if (!_mystery.load(0, &_rng)) {
 			warning("doCaseSelection: failed to load practice mystery");
 			_mystery.clear();
+		} else if (_audio) {
+			_audio->initMysterySounds(0);
 		}
 		return;
 	}
@@ -655,6 +658,8 @@ void EEMEngine::doCaseSelection() {
 		_mystery.clear();
 		return;
 	}
+	if (_audio)
+		_audio->initMysterySounds(sel);
 	debugC(1, kDebugMystery, "Mystery %u loaded; %u sites, %u suspects",
 		   sel, _mystery.numSites(), _mystery.numSuspects());
 }


Commit: f7b94e57211894e3e0858fc49a441e62e7f69f83
    https://github.com/scummvm/scummvm/commit/f7b94e57211894e3e0858fc49a441e62e7f69f83
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:20:42+02:00

Commit Message:
EEM: profile handling in save games

Changed paths:
    engines/eem/eem.cpp
    engines/eem/eem.h
    engines/eem/ui.cpp


diff --git a/engines/eem/eem.cpp b/engines/eem/eem.cpp
index c6e2552cf79..cade4889101 100644
--- a/engines/eem/eem.cpp
+++ b/engines/eem/eem.cpp
@@ -56,9 +56,15 @@ const uint kPicHighScoreLogo   = 0x20c; ///< _ShowHScoreLogo: GetPicture(0x20c)
 const uint kPalEAKids          = 0x25;
 const uint kPalHighScore       = 0x27;
 
-// Save format. Used by `saveGameState` / `loadGameState`.
-const uint32 kSaveMagic = MKTAG('E', 'E', 'M', '0');
-const byte   kSaveVer   = 3;  ///< v2: _mysteriesSolved tracker; v3: player name
+// Save body version, used by the `Common::Serializer` inside
+// `saveGameStream`/`loadGameStream`. The framework's extended-save
+// header (description / thumbnail / playtime) is appended/parsed
+// separately by `Engine::saveGameState` / `MetaEngine::readSavegameHeader`,
+// so we don't need a magic word or our own metadata fields.
+//
+//   v1 ??? initial schema: name + mysteriesSolved + partner +
+//        optional mystery sub-state.
+const byte kSaveBodyVer = 1;
 
 // 11x16 mouse cursor ??? replaces the DOS hardware cursor wired in by
 // _InitMouse @ 152d:018b (INT 33h). The original game sets the cursor
@@ -541,99 +547,176 @@ bool EEMEngine::hasFeature(EngineFeature f) const {
 }
 
 bool EEMEngine::canLoadGameStateCurrently(Common::U32String *) {
-	return false;  // Loading is startup-only.
+	// Loading mid-mystery would replace `_mystery._data` while
+	// pointers into it are alive on the stack inside `displayClue`
+	// etc. Profile picking still works via `loadProfile` from the
+	// menu screens before a mystery loads.
+	return !_mystery.isLoaded();
 }
 
 bool EEMEngine::canSaveGameStateCurrently(Common::U32String *) {
-	return _mystery.isLoaded();
+	// Profile saves (no mystery loaded) are always OK; mid-mystery
+	// snapshots only after the active case has fully initialised.
+	return true;
 }
 
-Common::Error EEMEngine::saveGameState(int slot, const Common::String &desc, bool isAutosave) {
-	Common::OutSaveFile *out = getSaveFileManager()->openForSaving(getSaveStateName(slot));
-	if (!out)
-		return Common::kCreatingFileFailed;
-
-	out->writeUint32BE(kSaveMagic);
-	out->writeByte(kSaveVer);
-
-	// Header: description + ScummVM extended save metadata are appended
-	// automatically when `EngineFeature::kSavesUseExtendedFormat` is set;
-	// our save body just carries the engine state.
-	(void)desc;
+Common::Error EEMEngine::saveGameStream(Common::WriteStream *stream,
+										 bool isAutosave) {
 	(void)isAutosave;
 
-	uint16 mysteryNum = (uint16)_mystery.number();
-	out->writeUint16LE(mysteryNum);
-	out->writeByte(_partner);
-	out->write(_mysteriesSolved, sizeof(_mysteriesSolved));
-
-	// v3: persist the player name so save-slot resume restores it.
-	out->writeUint16LE((uint16)_playerName.size());
-	out->writeString(_playerName);
+	Common::Serializer s(nullptr, stream);
+	s.setVersion(kSaveBodyVer);
+
+	// Profile-level state ??? mirrors the original `_PlayerRecord` body
+	// at `2d5d:3f6a` (159 bytes, written by `_SavePlayerRecord @
+	// 1c33:034f`). The `_PlayerRecord` layout is:
+	//   +0x00..+0x0b : player name (12 chars, null-padded)
+	//   +0x0c..+0x1f : random ID bytes used by `_GenerateFilename`
+	//                  (29be:0dbf "C:\EEMCDSAV\%s.PLR") ??? irrelevant to
+	//                  ScummVM saves which key on slot, not filename.
+	//   +0x20..+0x28 : derived 8-char .PLR basename ??? likewise unused.
+	//   +0x2d        : voice-enable flag (`DAT_2d5d_3f97`, default 1).
+	//   +0x2f        : chain stage (`DAT_2d5d_3f99`, 1=A, 2=B, 3=C ???
+	//                  `_DisplayCorrect` advances it once every case
+	//                  in the current set is solved).
+	//   +0x31..+0xa6 : `mysteriesSolved[55]` u16 (0=unsolved, 1=solved,
+	//                  2=solved on first try) ??? `_DisplayCorrect`
+	//                  writes 1 always, 2 when `_FirstTry != 0`.
+	//
+	// We persist the gameplay-meaningful subset (name + solved table +
+	// partner) and skip the filename-derivation bytes. The voice /
+	// chain-stage fields aren't yet wired into the C++ port; we save
+	// space for them so they slot in without a version bump.
+	s.syncString(_playerName);
+	s.syncBytes(_mysteriesSolved, sizeof(_mysteriesSolved));
+	s.syncAsByte(_partner);
+
+	// ScummVM-only extension: persist the in-progress mystery so the
+	// player can resume mid-case. The original engine has no such
+	// notion ??? `_LoadGame @ 2404:0dc7` simply loads a fresh mystery,
+	// it doesn't preserve site progress. The flag lets a profile save
+	// stay valid even when no mystery is loaded (e.g. fresh profile).
+	bool hasMystery = _mystery.isLoaded();
+	s.syncAsByte(hasMystery);
+	if (hasMystery) {
+		uint16 mysteryNum = (uint16)_mystery.number();
+		s.syncAsUint16LE(mysteryNum);
+		_mystery.syncState(s);
+	}
 
 	debugC(1, kDebugGeneral,
-		   "Saved slot %d: mystery=%u partner=%u name=%s autosave=%d",
-		   slot, mysteryNum, _partner, _playerName.c_str(), isAutosave ? 1 : 0);
+		   "Saved profile name=%s partner=%u mystery=%d autosave=%d",
+		   _playerName.c_str(), _partner,
+		   hasMystery ? (int)_mystery.number() : -1,
+		   isAutosave ? 1 : 0);
+	return Common::kNoError;
+}
 
-	Common::Serializer s(nullptr, out);
-	s.setVersion(kSaveVer);
-	_mystery.syncState(s);
+Common::Error EEMEngine::loadGameStream(Common::SeekableReadStream *stream) {
+	Common::Serializer s(stream, nullptr);
+	s.setVersion(kSaveBodyVer);
+
+	s.syncString(_playerName);
+	if (_playerName.empty())
+		_playerName = "Detective";
+
+	s.syncBytes(_mysteriesSolved, sizeof(_mysteriesSolved));
+	s.syncAsByte(_partner);
+
+	bool hasMystery = false;
+	s.syncAsByte(hasMystery);
+	if (hasMystery) {
+		uint16 mysteryNum = 0;
+		s.syncAsUint16LE(mysteryNum);
+		if (!_mystery.load(mysteryNum, &_rng)) {
+			_mystery.clear();
+			return Common::kReadingFailed;
+		}
+		// `_ReadMystery @ 2404:008f` calls `_InitMysterySounds` at the
+		// tail (2404:0298) so the SDB index is in place for clue and
+		// partner-speech spool sounds.
+		if (_audio)
+			_audio->initMysterySounds(mysteryNum);
+		_mystery.syncState(s);
+	} else {
+		_mystery.clear();
+	}
 
-	out->finalize();
-	delete out;
+	debugC(1, kDebugGeneral,
+		   "Loaded profile name=%s partner=%u mystery=%d",
+		   _playerName.c_str(), _partner,
+		   _mystery.isLoaded() ? (int)_mystery.number() : -1);
 	return Common::kNoError;
 }
 
-Common::Error EEMEngine::loadGameState(int slot) {
-	Common::InSaveFile *in = getSaveFileManager()->openForLoading(getSaveStateName(slot));
-	if (!in)
-		return Common::kReadingFailed;
+SaveStateList EEMEngine::listProfiles() const {
+	// Mirrors `_findfirst("*.PLR")` in `screen8_handler @ 1c33:1012`.
+	return getMetaEngine()->listSaves(_targetName.c_str());
+}
 
-	if (in->readUint32BE() != kSaveMagic) {
-		delete in;
-		return Common::kUnknownError;
-	}
-	const byte ver = in->readByte();
-	if (ver > kSaveVer) {
-		delete in;
-		return Common::kUnknownError;
-	}
+Common::Error EEMEngine::saveProfile(const Common::String &name) {
+	if (name.empty())
+		return Common::kCreatingFileFailed;
 
-	const uint16 mysteryNum = in->readUint16LE();
-	_partner = in->readByte();
-	if (ver >= 2)
-		in->read(_mysteriesSolved, sizeof(_mysteriesSolved));
-	else
-		memset(_mysteriesSolved, 0, sizeof(_mysteriesSolved));
-
-	if (ver >= 3) {
-		const uint16 nameLen = in->readUint16LE();
-		Common::String name;
-		for (uint16 i = 0; i < nameLen && i < 64; i++)
-			name += (char)in->readByte();
-		_playerName = name.empty() ? Common::String("Detective") : name;
+	const SaveStateList saves = listProfiles();
+
+	// Slot lookup by description: if a save with this profile name
+	// already exists, overwrite it. Same as Wetlands' `saveProfile`.
+	int slot = -1;
+	for (auto &s : saves) {
+		if (s.getDescription() == name) {
+			slot = s.getSaveSlot();
+			break;
+		}
 	}
 
-	if (!_mystery.load(mysteryNum, &_rng)) {
-		_mystery.clear();
-		delete in;
-		return Common::kReadingFailed;
+	// New profile ??? pick the lowest unused slot. The MetaEngine caps
+	// us at 99 by default (`getMaximumSaveSlot`); 25 was the DOS
+	// original's limit (`screen8_handler` walks `*.PLR` up to 25
+	// entries in `local_8c[0x19][2]`).
+	if (slot < 0) {
+		const int maxSlot = getMetaEngine()->getMaximumSaveSlot();
+		Common::Array<bool> used(maxSlot + 1);
+		for (auto &s : saves) {
+			const int sl = s.getSaveSlot();
+			if (sl >= 0 && sl <= maxSlot)
+				used[sl] = true;
+		}
+		for (int i = 0; i <= maxSlot; i++) {
+			if (!used[i]) {
+				slot = i;
+				break;
+			}
+		}
+		if (slot < 0)
+			return Common::kCreatingFileFailed;
 	}
-	// `_ReadMystery @ 2404:008f` calls `_InitMysterySounds(_MysteryNumber)`
-	// at the tail (2404:0298) so the SDB index is in place for clue and
-	// partner-speech spool sounds.
-	if (_audio)
-		_audio->initMysterySounds(mysteryNum);
 
-	Common::Serializer s(in, nullptr);
-	s.setVersion(ver);
-	_mystery.syncState(s);
+	_playerName = name;
+	debugC(1, kDebugGeneral, "saveProfile(%s) -> slot %d",
+		   name.c_str(), slot);
+	return saveGameState(slot, name, /*isAutosave=*/false);
+}
 
-	delete in;
-	debugC(1, kDebugGeneral,
-		   "Loaded slot %d: mystery=%u partner=%u name=%s",
-		   slot, mysteryNum, _partner, _playerName.c_str());
-	return Common::kNoError;
+bool EEMEngine::loadProfile(const Common::String &name) {
+	if (name.empty())
+		return false;
+
+	const SaveStateList saves = listProfiles();
+	for (auto &s : saves) {
+		if (s.getDescription() == name) {
+			const Common::Error err = loadGameState(s.getSaveSlot());
+			if (err.getCode() == Common::kNoError) {
+				debugC(1, kDebugGeneral, "loadProfile(%s) <- slot %d",
+					   name.c_str(), s.getSaveSlot());
+				return true;
+			}
+			break;
+		}
+	}
+	debugC(1, kDebugGeneral, "loadProfile(%s) ??? no matching slot",
+		   name.c_str());
+	return false;
 }
 
 void EEMEngine::screenDriver() {
diff --git a/engines/eem/eem.h b/engines/eem/eem.h
index 4eded42e068..f1aa061d57d 100644
--- a/engines/eem/eem.h
+++ b/engines/eem/eem.h
@@ -29,8 +29,11 @@
 #include "common/random.h"
 #include "common/scummsys.h"
 
+#include "common/serializer.h"
+
 #include "engines/advancedDetector.h"
 #include "engines/engine.h"
+#include "engines/savestate.h"
 
 #include "eem/animation.h"
 #include "eem/font.h"
@@ -66,8 +69,37 @@ public:
 	bool hasFeature(EngineFeature f) const override;
 	bool canLoadGameStateCurrently(Common::U32String *msg = nullptr) override;
 	bool canSaveGameStateCurrently(Common::U32String *msg = nullptr) override;
-	Common::Error loadGameState(int slot) override;
-	Common::Error saveGameState(int slot, const Common::String &desc, bool isAutosave) override;
+
+	// ScummVM extended-save hooks. The base `Engine::saveGameState` /
+	// `loadGameState` write/read the framework header (description,
+	// thumbnail, playtime, version) around our body via these
+	// streams. We keep all per-profile state in the body, with a
+	// single `Common::Serializer` version so future field additions
+	// stay backward-compatible.
+	Common::Error saveGameStream(Common::WriteStream *stream,
+								  bool isAutosave = false) override;
+	Common::Error loadGameStream(Common::SeekableReadStream *stream) override;
+
+	// Per-profile save helpers. The original `_PlayerRecord` lives at
+	// `2d5d:3f6a` (159 bytes) and is written by `_SavePlayerRecord @
+	// 1c33:034f` to `C:\EEMCDSAV\<name>.PLR`. The DOS launcher screen
+	// `screen8_handler @ 1c33:1012` walks `*.PLR`, lets the player
+	// pick a profile, and calls `_LoadPlayerRecord`. We mirror the
+	// pattern by mapping each ScummVM save slot to one profile (slot
+	// description = player name) ??? same approach Wetlands uses.
+
+	/// Mirrors `_SavePlayerRecord @ 1c33:034f`. Saves into the slot
+	/// whose description matches @p name, or the lowest unused slot
+	/// if no match. Returns the kNoError on success.
+	Common::Error saveProfile(const Common::String &name);
+
+	/// Mirrors `_LoadPlayerRecord @ 1c33:03a6`. Returns false if no
+	/// slot has @p name as its description.
+	bool loadProfile(const Common::String &name);
+
+	/// Mirrors the `_findfirst("*.PLR")` walk inside
+	/// `screen8_handler`. Sorted by slot.
+	SaveStateList listProfiles() const;
 
 	const ADGameDescription *_gameDescription;
 
diff --git a/engines/eem/ui.cpp b/engines/eem/ui.cpp
index 121a83d440f..c78ea11d37d 100644
--- a/engines/eem/ui.cpp
+++ b/engines/eem/ui.cpp
@@ -284,7 +284,19 @@ void EEMEngine::doNewPlayer() {
 			if (k == Common::KEYCODE_RETURN) {
 				if (name.empty())
 					name = "Detective";
-				_playerName = name;
+				// Mirrors `_NewPlayer @ 1c33:0dda` tail (1c33:0fa0+):
+				// after the name is committed, try `_LoadPlayerRecord`
+				// ??? if it returns 0 (no existing .PLR), zero out the
+				// per-profile state and call `_SavePlayerRecord` to
+				// create a fresh profile file. Same flow here, mapped
+				// onto ScummVM save slots via name ??? description.
+				if (!loadProfile(name)) {
+					_playerName = name;
+					memset(_mysteriesSolved, 0, sizeof(_mysteriesSolved));
+					_mystery.clear();
+					_partner = 0;
+					saveProfile(name);
+				}
 				return;
 			}
 			if (k == Common::KEYCODE_ESCAPE) {
@@ -2264,12 +2276,16 @@ void EEMEngine::doAccuse() {
 			_music->playMus(5, /*loop=*/false);
 		playAnm(Common::Path("SCRAPBK.ANI"), 120, true);
 
-		// Auto-save into slot 0 (the engine's quicksave slot).
-		const Common::String desc = Common::String::format(
-			"%s ??? solved mystery %u", _playerName.c_str(), mn);
-		Common::Error err = saveGameState(0, desc, true);
+		// Mirrors `_SavePlayerRecord` at 1df2:0857 ??? once the
+		// `_mysteriesSolved` table is updated, the original
+		// immediately persists the player record so the win sticks
+		// even if the player quits before reaching the menu. We do
+		// the same by writing back to the active profile (the slot
+		// keyed on `_playerName`) rather than clobbering slot 0 like
+		// a generic quicksave.
+		const Common::Error err = saveProfile(_playerName);
 		if (err.getCode() != Common::kNoError)
-			warning("auto-save after solve failed: %s",
+			warning("saveProfile after solve failed: %s",
 					err.getDesc().c_str());
 	} else {
 		_mystery._firstTry = false;


Commit: df22a74a072253524335ade26962be591e1c6bb5
    https://github.com/scummvm/scummvm/commit/df22a74a072253524335ade26962be591e1c6bb5
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:20:42+02:00

Commit Message:
EEM: improved animations and added missing UI features

Changed paths:
    engines/eem/audio.cpp
    engines/eem/audio.h
    engines/eem/clues.cpp
    engines/eem/eem.cpp
    engines/eem/eem.h
    engines/eem/site.cpp
    engines/eem/site.h
    engines/eem/ui.cpp


diff --git a/engines/eem/audio.cpp b/engines/eem/audio.cpp
index fab34529b71..f4719af0bb1 100644
--- a/engines/eem/audio.cpp
+++ b/engines/eem/audio.cpp
@@ -54,6 +54,17 @@ void AudioPlayer::stopAll() {
 // VOC playback --------------------------------------------------------
 
 void AudioPlayer::playVoc(const Common::Path &vocPath) {
+	// Voice / digital audio is gated by the `DAT_2d5d_3f97` flag in
+	// the original ??? verified at every callsite (`_DoChoosePartner @
+	// 1a35:098c`, `_DisplayClue @ 2404:0845`, `_DoOpeningAnims @
+	// 2520:08a8`, etc.). Setup-screen toggle and `_NewPlayer` fresh-
+	// profile init both rewrite that flag. We pull it into the audio
+	// player so callers don't have to duplicate the check.
+	if (!_voiceEnabled) {
+		debugC(2, kDebugSound, "AudioPlayer: voice disabled, skipping %s",
+			   vocPath.toString().c_str());
+		return;
+	}
 	stopVoice();
 
 	// Mirrors `_LoadSoundName`'s `_fopen` (1ff1:02ac).
@@ -189,6 +200,11 @@ void AudioPlayer::playPcmBuffer(byte *pcm, uint32 size, uint sampleRate,
 }
 
 void AudioPlayer::spoolSound(uint num) {
+	if (!_voiceEnabled) {
+		debugC(2, kDebugSound,
+			   "AudioPlayer: voice disabled, skipping spoolSound(%u)", num);
+		return;
+	}
 	if (_currentMystery < 0 || num >= _sdxIndex.size()) {
 		warning("AudioPlayer: spoolSound(%u) ??? invalid index (%d, %u)",
 				num, _currentMystery, (uint)_sdxIndex.size());
diff --git a/engines/eem/audio.h b/engines/eem/audio.h
index c9238b0b3f4..921e00e900b 100644
--- a/engines/eem/audio.h
+++ b/engines/eem/audio.h
@@ -73,6 +73,17 @@ public:
 	explicit AudioPlayer(EEMEngine *vm);
 	~AudioPlayer();
 
+	/// Mirrors the gate in every original audio call site
+	/// (`_DisplayClue` 2404:0845, `_DoChoosePartner` 1a35:098c,
+	/// `_DoOpeningAnims` 2520:08a8, `_DisplayCorrect` 1df2:0780, ...)
+	/// ??? every `_PlayVoice` / `_SpoolSound` is wrapped in
+	/// `if ((DAT_2d5d_3f97 != 0) && (_VoiceAvailable != 0))`. The
+	/// engine pulls `_voiceOn` (= `DAT_2d5d_3f97`) into here so we
+	/// can early-return at the audio boundary instead of duplicating
+	/// the gate at every call site.
+	void setVoiceEnabled(bool enabled) { _voiceEnabled = enabled; }
+	bool voiceEnabled() const { return _voiceEnabled; }
+
 	// VOC playback ----------------------------------------------------
 
 	/// Mirrors `_LoadSoundName` + `_PlayVoice`. Loads the named .VOC
@@ -152,6 +163,7 @@ private:
 	Common::Array<SoundEntry> _sdxIndex;
 	Common::Path _sdbPath;
 	int _currentMystery = -1;
+	bool _voiceEnabled = true;
 };
 
 } // End of namespace EEM
diff --git a/engines/eem/clues.cpp b/engines/eem/clues.cpp
index a3907d32ef3..1ad73525d3d 100644
--- a/engines/eem/clues.cpp
+++ b/engines/eem/clues.cpp
@@ -33,6 +33,7 @@
 #include "eem/audio.h"
 #include "eem/detection.h"
 #include "eem/eem.h"
+#include "eem/site.h"
 
 // EEM ??? clue / briefing pipeline (SCRIPT.C clue parts + KD.C briefing parts).
 // Everything that drives a `ClueBlock` through the displayClue / portrait /
@@ -295,15 +296,44 @@ void EEMEngine::doInitClues() {
 		const uint frameCount = haveGame ? game.size() : 8;
 		bool skip = false;
 		for (uint frame = 0; frame < frameCount && !shouldQuit() && !skip; frame++) {
-			// Restore BG + advance frame.
+			// Restore BG + advance frame. The original always uses
+			// Jake's anim IDs (0x17/0x18/0x19) as the SCRIPT keys
+			// even when Jenny's CELL data is loaded ??? verified at
+			// `_DoInitClues @ 1a35:0507`/`0541` where
+			// `_NewAnimation(..., (PicData *)CONCAT22(0x17, ...), ...)`
+			// hard-codes the script index to 0x17. So we look up
+			// `partnerFrameAtTick(0x17, ...)` regardless of partner.
+			// This gives us the correct cadence ??? book holds on its
+			// "thinking" pose (cell 8) for 16 ticks instead of
+			// flipbook-cycling, and nancy waits 18 ticks before her
+			// late-arrival count-up.
 			if (_picsArchive.getPicture(0x52, bg))
 				blitAt(bg, 0, 0);
-			if (haveGame)
-				blitMaskedToScreen(game[frame % game.size()], 0xcd, 0x6c);
-			if (haveBook)
-				blitMaskedToScreen(book[frame % book.size()], 0, 99);
-			if (haveNancy)
-				blitMaskedToScreen(nancy[frame % nancy.size()], 0x68, 0x8b);
+			const uint32 t = frame * 100;
+			// All three briefing anims (game/book/nancy) go through
+			// the original `_NewAnimation` path so per-frame anchors
+			// apply. Use `blitAnimFrameAnchored` against a locked
+			// screen surface so the briefing partner / book / nancy
+			// translate cleanly between cells instead of pinning at
+			// the same top-left.
+			Graphics::Surface *scr = g_system->lockScreen();
+			if (!scr) {
+				skip = true;
+				break;
+			}
+			if (haveGame) {
+				const uint f = partnerFrameAtTick(0x17, (uint)game.size(), t);
+				blitAnimFrameAnchored(scr, game[f], 0xcd, 0x6c);
+			}
+			if (haveBook) {
+				const uint f = partnerFrameAtTick(0x18, (uint)book.size(), t);
+				blitAnimFrameAnchored(scr, book[f], 0, 99);
+			}
+			if (haveNancy) {
+				const uint f = partnerFrameAtTick(0x19, (uint)nancy.size(), t);
+				blitAnimFrameAnchored(scr, nancy[f], 0x68, 0x8b);
+			}
+			g_system->unlockScreen();
 			g_system->updateScreen();
 
 			// Wait 100 ms or until input.
diff --git a/engines/eem/eem.cpp b/engines/eem/eem.cpp
index cade4889101..fa895d640a6 100644
--- a/engines/eem/eem.cpp
+++ b/engines/eem/eem.cpp
@@ -61,10 +61,7 @@ const uint kPalHighScore       = 0x27;
 // header (description / thumbnail / playtime) is appended/parsed
 // separately by `Engine::saveGameState` / `MetaEngine::readSavegameHeader`,
 // so we don't need a magic word or our own metadata fields.
-//
-//   v1 ??? initial schema: name + mysteriesSolved + partner +
-//        optional mystery sub-state.
-const byte kSaveBodyVer = 1;
+const byte kSaveBodyVer = 3;
 
 // 11x16 mouse cursor ??? replaces the DOS hardware cursor wired in by
 // _InitMouse @ 152d:018b (INT 33h). The original game sets the cursor
@@ -128,6 +125,7 @@ Common::Error EEMEngine::run() {
 	// which `_AIL_register_driver`s SBDIG.ADV / PASDIG.ADV alongside
 	// the MIDI driver.
 	_audio = new AudioPlayer(this);
+	_audio->setVoiceEnabled(_voiceOn);
 	syncSoundSettings();
 
 	// _InitMouse @ 152d:018b in the original ??? install our 11x16 arrow,
@@ -146,36 +144,31 @@ Common::Error EEMEngine::run() {
 
 	// If the user chose "Load" before pressing Play, the framework
 	// invokes `loadGameState` which sets up `_mystery` and `_partner`.
-	// Honour that by skipping the intros and going straight to the
-	// loaded mystery's site loop.
+	// Honour that by skipping the intros and dropping into the screen-
+	// driver loop just past the briefing ??? the loaded mystery already
+	// knows its current site, so we resume at MAP (the original's
+	// post-briefing state, set by handler 0 at 1a35:0e1d).
 	const int wantedSave = ConfMan.hasKey("save_slot")
 		? ConfMan.getInt("save_slot") : -1;
+	bool resumed = false;
 	if (wantedSave >= 0) {
 		const Common::Error err = loadGameState(wantedSave);
 		if (err.getCode() == Common::kNoError && _mystery.isLoaded()) {
 			debugC(1, kDebugGeneral, "Resuming from slot %d at mystery %u",
 				   wantedSave, _mystery.number());
 			CursorMan.showMouse(true);
-			doInitClues();
-			// Original screen 0 ??? screen 1: after the briefing the
-			// game opens the map (function at 20fe:120b ??? _DoBigMap)
-			// and only enters a site once the player clicks on one.
-			doBigMap();
-			if (_mystery.isLoaded())
-				doSiteLoop();
-			while (!shouldQuit()) {
-				doCaseSelection();
-				if (!_mystery.isLoaded())
-					break;
-				doInitClues();
-				doBigMap();
-				if (_mystery.isLoaded())
-					doSiteLoop();
-			}
-			return Common::kNoError;
+			_nextScreen = kScreenMap;
+			resumed = true;
 		}
 	}
 
+	// Skip the entire intro chain (logos + anims + name entry +
+	// partner pick) when resuming a saved profile ??? the partner is
+	// already known, the player has already named themselves, and the
+	// loaded mystery's site loop is what they want to see again.
+	if (resumed)
+		goto screen_loop;
+
 	// Reproduces _DoOpeningAnims @ 2520:082a:
 	//   EA Kids logo (PIC) -> HighScore Productions logo (PIC) ->
 	//   Storm Software logo (BOLT.ANM) -> [music starts] -> 20
@@ -254,36 +247,120 @@ Common::Error EEMEngine::run() {
 	// per-mystery `_StartTravelMusic` kicks in.
 	if (_music)
 		_music->stop();
+	// Profile pick (or fresh creation) ??? `screen8_handler @ 1c33:1012`.
+	// `doProfilePicker` lists existing profiles via `listProfiles()`
+	// and falls through to `doNewPlayer` if none exist or the user
+	// picks "[New Player]".
 	if (!shouldQuit())
-		doNewPlayer();
+		doProfilePicker();
 	if (!shouldQuit())
 		doChoosePartner();
-	if (!shouldQuit())
-		doCaseSelection();
-	if (!shouldQuit() && _mystery.isLoaded()) {
-		// Mark the starting site as active and display the case briefing.
-		// `_DoInitClues` @ 1a35:0411 ??? case briefing.
-		doInitClues();
-		// Original screen 0 ??? screen 1: after the briefing the game
-		// opens the map (function at 20fe:120b ??? `_DoBigMap`) and only
-		// enters a site once the player clicks on one.
-		doBigMap();
-		if (_mystery.isLoaded())
-			doSiteLoop();
 
-		// After a case, loop back to CaseSelection.
-		while (!shouldQuit()) {
+	// Now drop into the screen-driver state machine ??? same pattern as
+	// `_ScreenDriver @ 1a35:0dc1` + the per-screen handlers in the
+	// table at 1a35:0e5e. The original sets `_NextScreen` either
+	// directly (e.g. `_DisplayCorrect` writes 12 = ACTION) or via the
+	// jumptable handlers (e.g. handler 0 calls `_DoInitClues` then
+	// writes 1 = MAP). The handlers here mirror that exactly: each
+	// case body runs the screen and updates `_nextScreen` for the next
+	// iteration. Sentinel `kScreenInvalid` (0xFFFF) ends the loop ???
+	// same as the original's table-end marker.
+	//
+	// Initial value `kScreenAction` matches the original flow at the
+	// tail of `_DoChoosePartner @ 1a35:099d` which sets
+	// `_NextScreen = 0xc` once the partner has been picked. (The
+	// resume path above bypasses this and seeds `kScreenMap` instead.)
+	//
+	// Mid-mystery profile resume: if the profile picker loaded a
+	// save whose `hasMystery` flag was set, `_mystery.isLoaded()` is
+	// true here and the player just re-picked their partner. Drop
+	// straight to MAP rather than ACTION so they don't have to walk
+	// back through the case picker (which would `_mystery.load()`
+	// fresh and discard their site / clue progress). The original
+	// has no equivalent ??? it persists only profile-level state via
+	// `_PlayerRecord`, not in-progress mysteries ??? so this is a
+	// ScummVM-only ergonomics improvement.
+	if (!shouldQuit() && !resumed)
+		_nextScreen = _mystery.isLoaded() ? kScreenMap : kScreenAction;
+screen_loop:
+	while (!shouldQuit() && _nextScreen != kScreenInvalid) {
+		const ScreenId current = (ScreenId)_nextScreen;
+		debugC(1, kDebugGeneral, "screenDriver: id=%d", (int)current);
+
+		switch (current) {
+		case kScreenAction:
+			// Post-mystery menu. `_ActionScreen` sets _NextScreen via
+			// its action jumptable (1c33:1be1) ??? see `doActionScreen`.
+			doActionScreen();
+			break;
+
+		case kScreenChooseMystery:
+			// Handler 10 at 1a35:0e0e calls `_DoChooseMystery` which
+			// presets `_NextScreen = 0` (INIT_CLUES) before
+			// `_CaseSelection`. If the picker bails out without
+			// loading a mystery (no `_ReadMystery` call), drop back
+			// to ACTION instead of falling into a missing case.
 			doCaseSelection();
-			if (!_mystery.isLoaded())
-				break;
+			_nextScreen = _mystery.isLoaded() ? kScreenInitClues
+											  : kScreenAction;
+			break;
+
+		case kScreenInitClues:
+			// Handler 0 at 1a35:0e14 runs `_PreLoad` + `_DoInitClues`
+			// then writes `_NextScreen = 1` (MAP).
 			doInitClues();
-			// Original screen 0 ??? screen 1: after the briefing the
-			// game opens the map (function at 20fe:120b ??? _DoBigMap)
-			// and only enters a site once the player clicks on one.
+			_nextScreen = _mystery.isLoaded() ? kScreenMap
+											  : kScreenAction;
+			break;
+
+		case kScreenMap:
+		case kScreenMapAlt:
+			// Handler 1/2 at 1a35:0e25 calls `_DoMapScreen @
+			// 20fe:120b` which manages its own `_NextScreen` writes ???
+			// 3 (a site was clicked), 6 (setup), or 0xffff (quit).
+			// Our `doBigMap` keeps the original's "click site, then
+			// enter the site loop" behaviour inline; once it returns
+			// the natural next state is SITE.
 			doBigMap();
-			if (_mystery.isLoaded())
-				doSiteLoop();
+			if (!_mystery.isLoaded())
+				_nextScreen = kScreenAction;
+			else if (_nextScreen == current)
+				_nextScreen = kScreenSite;
+			break;
+
+		case kScreenSite:
+			// Handler 3 at 1a35:0e2c calls `_DoSiteLoop @
+			// 168d:03f4`. Our `doSiteLoop` is a complete loop ???
+			// notebook / gallery / accuse / map are dispatched
+			// inline within `SiteScreen::run`. The accusation tail
+			// in `doAccuse` (see ui.cpp) writes the next screen:
+			// kScreenAction on win (matches `_DisplayCorrect @
+			// 1df2:0895` writing 0xc) or kScreenSite on lose
+			// (matches `_DisplayAlibi @ 1df2:043f` snapping back
+			// via `_LastScreen`).
+			doSiteLoop();
+			if (!_mystery.isLoaded())
+				_nextScreen = kScreenAction;
+			else if (_nextScreen == current)
+				_nextScreen = kScreenInvalid;  // user quit
+			break;
+
+		case kScreenSetup:
+			// Handler 6 at 1a35:0e48 calls `_DoSetup @ 1f78:044e`.
+			// Reachable via the BigMap setup button which writes
+			// `_NextScreen = 6` (verified at 20fe:0c33). The
+			// original sets `_NextScreen = _LastScreen` on entry,
+			// then the toggle UI returns when ESC / Back is hit;
+			// `doSetup` sets `_nextScreen` itself.
+			doSetup();
+			break;
+
+		default:
+			warning("screenDriver: unhandled screen id %d", (int)current);
+			_nextScreen = kScreenInvalid;
+			break;
 		}
+		_lastScreen = current;
 	}
 
 	debugC(1, kDebugGeneral, "EEM engine exiting");
@@ -564,8 +641,14 @@ Common::Error EEMEngine::saveGameStream(Common::WriteStream *stream,
 										 bool isAutosave) {
 	(void)isAutosave;
 
+	// Body header: one byte version. `Common::Serializer::setVersion`
+	// alone doesn't write/read the version ??? we emit it explicitly so
+	// `loadGameStream` knows which fields are present. Older saves
+	// (v1) lack `_chainStage`; newer ones include it.
 	Common::Serializer s(nullptr, stream);
 	s.setVersion(kSaveBodyVer);
+	byte ver = kSaveBodyVer;
+	s.syncAsByte(ver);
 
 	// Profile-level state ??? mirrors the original `_PlayerRecord` body
 	// at `2d5d:3f6a` (159 bytes, written by `_SavePlayerRecord @
@@ -590,6 +673,10 @@ Common::Error EEMEngine::saveGameStream(Common::WriteStream *stream,
 	s.syncString(_playerName);
 	s.syncBytes(_mysteriesSolved, sizeof(_mysteriesSolved));
 	s.syncAsByte(_partner);
+	// v2+: chain-stage tier (1=Junior, 2=Senior, 3=Master).
+	s.syncAsByte(_chainStage);
+	// v3+: voice on/off flag (DAT_2d5d_3f97).
+	s.syncAsByte(_voiceOn);
 
 	// ScummVM-only extension: persist the in-progress mystery so the
 	// player can resume mid-case. The original engine has no such
@@ -605,8 +692,8 @@ Common::Error EEMEngine::saveGameStream(Common::WriteStream *stream,
 	}
 
 	debugC(1, kDebugGeneral,
-		   "Saved profile name=%s partner=%u mystery=%d autosave=%d",
-		   _playerName.c_str(), _partner,
+		   "Saved profile name=%s partner=%u stage=%u mystery=%d autosave=%d",
+		   _playerName.c_str(), _partner, _chainStage,
 		   hasMystery ? (int)_mystery.number() : -1,
 		   isAutosave ? 1 : 0);
 	return Common::kNoError;
@@ -614,7 +701,14 @@ Common::Error EEMEngine::saveGameStream(Common::WriteStream *stream,
 
 Common::Error EEMEngine::loadGameStream(Common::SeekableReadStream *stream) {
 	Common::Serializer s(stream, nullptr);
-	s.setVersion(kSaveBodyVer);
+	byte ver = 0;
+	s.syncAsByte(ver);
+	if (ver > kSaveBodyVer) {
+		warning("loadGameStream: save body version %u newer than %u ??? refusing",
+				ver, kSaveBodyVer);
+		return Common::kReadingFailed;
+	}
+	s.setVersion(ver);
 
 	s.syncString(_playerName);
 	if (_playerName.empty())
@@ -622,6 +716,10 @@ Common::Error EEMEngine::loadGameStream(Common::SeekableReadStream *stream) {
 
 	s.syncBytes(_mysteriesSolved, sizeof(_mysteriesSolved));
 	s.syncAsByte(_partner);
+	s.syncAsByte(_chainStage);
+	s.syncAsByte(_voiceOn);
+	if (_audio)
+		_audio->setVoiceEnabled(_voiceOn);
 
 	bool hasMystery = false;
 	s.syncAsByte(hasMystery);
diff --git a/engines/eem/eem.h b/engines/eem/eem.h
index f1aa061d57d..e97bd592712 100644
--- a/engines/eem/eem.h
+++ b/engines/eem/eem.h
@@ -46,14 +46,59 @@ class AudioPlayer;
 class MusicPlayer;
 
 /**
- * Screen IDs used by the original ScreenDriver dispatch table at 1a35:0e5e.
- * The table holds 14 (id, handler) entries; the loop iterates until it finds
- * a matching id and calls its handler. ID 0xFFFF is the exit sentinel.
+ * Screen IDs used by the original `_ScreenDriver` dispatch table at
+ * 1a35:0e5e (and the fallback at 1a35:0e54). 14 entries total: each
+ * one is a screen ID + a near function pointer at offset +0x1c. The
+ * driver does `JMP word ptr CS:[BX + 0x1c]`, so handlers tail-call ???
+ * each one runs the screen and updates `_NextScreen` for the next
+ * trip through the dispatcher.
+ *
+ * IDs and their handlers (verified from disassembly at 1a35:0dec..0e4f):
+ *
+ *   0  INIT_CLUES  ??? `_PreLoad` + `_DoInitClues`, sets _NextScreen=1
+ *   1  MAP         ??? `_DoMapScreen` @ 20fe:120b (sets _NextScreen
+ *                    inside; 3 = a site was clicked, 6 = setup, etc.)
+ *   2  MAP         ??? same handler as 1 (alternate entry, used when
+ *                    `_LastScreen == 2` to swap the briefcase anim)
+ *   3  SITE        ??? `_DoSiteLoop` @ 168d:03f4
+ *   4  NOTEBOOK    ??? `_DoNotebook` @ 161e:0500
+ *   5  GALLERY     ??? `_DoGallery` @ 158f:065b
+ *   6  SETUP       ??? `_DoSetup` @ 1f78:044e
+ *   7  ACCUSE      ??? `_DoAccuse` @ 1df2:0bdd (win ??? 12, lose ??? last)
+ *   8  PROFILE     ??? `screen8_handler` @ 1c33:1012; tail sets =9
+ *   9  PARTNER     ??? `_DoChoosePartner` @ 1a35:0756; sets =0xc inside
+ *   10 (0xa) CHOOSE_MYSTERY ??? `_DoChooseMystery` + `_CaseSelection`;
+ *                    starts with _NextScreen=0 so a successful pick
+ *                    falls through to INIT_CLUES.
+ *   11 (0xb) TITLE  ??? set _NextScreen=8 then dispatch (TITLE.ANM is
+ *                    actually shown earlier by `_DoOpeningAnims`, this
+ *                    handler is the post-intro "fall into PROFILE"
+ *                    redirect)
+ *   12 (0xc) ACTION ??? `_ActionScreen` @ 1c33:195b ??? post-mystery menu
+ *                    ("Solve a Mystery", scrapbook, more mysteries,
+ *                    setup). Action 1 sets =10 (CHOOSE_MYSTERY).
+ *   0xFFFF SENTINEL ??? exit loop
+ *
+ * Screen-driver state writes verified via xrefs to `_NextScreen @
+ * 2d5d:3f26`: `_DisplayCorrect` writes 0xc (winner returns to ACTION),
+ * `_DisplayAlibi` writes `_LastScreen` (loser snaps back), and
+ * `_DoSiteLoop` writes 1/3/4 plus 0xffff on ESC.
  */
 enum ScreenId {
-	kScreenInvalid       = 0xFFFF,
-	kScreenChoosePartner = 0x09,  ///< _DoChoosePartner @ 1a35:0756 (boy/girl picker)
-	kScreenTitle         = 0x0B   ///< _ShowTitlePage @ 1a35:06b7
+	kScreenInvalid        = 0xFFFF,
+	kScreenInitClues      = 0x00,
+	kScreenMap            = 0x01,
+	kScreenMapAlt         = 0x02,
+	kScreenSite           = 0x03,
+	kScreenNotebook       = 0x04,
+	kScreenGallery        = 0x05,
+	kScreenSetup          = 0x06,
+	kScreenAccuse         = 0x07,
+	kScreenProfile        = 0x08,
+	kScreenChoosePartner  = 0x09,
+	kScreenChooseMystery  = 0x0A,
+	kScreenTitle          = 0x0B,
+	kScreenAction         = 0x0C
 };
 
 class EEMEngine : public Engine {
@@ -268,11 +313,55 @@ private:
 	// Screen handlers ??? port targets in screens/ later.
 	void showEAKidsLogo();
 	void showHighScoreLogo();
+
+	/// Profile selector ??? mirrors `screen8_handler @ 1c33:1012`.
+	/// Walks `listProfiles()`, draws the list of existing profile
+	/// names plus a "[New Player]" entry, and either calls
+	/// `loadProfile(name)` on a click or falls through to
+	/// `doNewPlayer()` if the user picks "New". When no profiles
+	/// exist, behaves identically to `doNewPlayer()` (the original
+	/// also bypasses the picker when `local_20 == 0` ??? see
+	/// 1c33:1170: `if (saves == 0) _NewPlayer();`).
+	void doProfilePicker();
 	void doNewPlayer();          ///< Mirrors `_NewPlayer` @ 1c33:0dda
 	void doChoosePartner();
+
+	/// Display the per-mystery ending pages from `E<num>.BIN`.
+	/// Mirrors `_DisplayEnding @ 1df2:0548` + `_DisplayEndingPage
+	/// @ 1df2:044c`. The file format is a 2-byte page count followed
+	/// by N pages, each `{ u16 picNum, u16 x1, u16 y1, u16 x2, u16 y2,
+	/// char text[] (null-terminated, ParseString placeholders) }`.
+	/// Blocks until the player clicks past the last page or hits ESC.
+	/// `_ShowOneScrap @ 1f78:0773` is just `_DisplayEnding(num, 1)`,
+	/// so this same call covers the post-mystery scrapbook view from
+	/// the action menu.
+	void doShowEnding(uint num);
 	void doCaseSelection();
 	void doSiteLoop();
 
+	/// Post-mystery action menu. Mirrors `_ActionScreen @ 1c33:195b` ???
+	/// the screen the original returns to after `_DisplayCorrect`
+	/// (winner) or after the player explicitly leaves a case via the
+	/// PROFILE ??? PARTNER chain. The original offers up to 5 choices
+	/// gated on the player's chain stage (`DAT_2d5d_3f99`):
+	///   1: "Solve a Mystery" (set _NextScreen=10 ??? CHOOSE_MYSTERY)
+	///   3: replay the last solved case (`_ReloadMystery(0)` callsite)
+	///   5: scrapbook viewer (`_ShowOneScrap(0, 1)` callsite)
+	///   7: chain-stage advance (cmp `_3f99 == 1`)
+	///   9: chain-stage advance (cmp `_3f99 == 2` / `== 3`)
+	///   sentinel: exit / back to PARTNER
+	/// We start with just option 1 wired (the practical loop) plus
+	/// quit; the others slot in as their underlying screens land.
+	void doActionScreen();
+
+	/// Setup / preferences screen. Mirrors `_DoSetup @ 1f78:044e` ???
+	/// per-profile preferences (voice on/off via `DAT_2d5d_3f97`,
+	/// partner pick via SwapColors on Kid1/Kid2 rects). Reachable
+	/// from BigMap's setup button (sets `_NextScreen = 6` per
+	/// `_DoBigMap @ 20fe:0c33`). Returns to whatever
+	/// `_lastScreen` was ??? typically MAP.
+	void doSetup();
+
 	/// Render the case briefing background + game/book decorations and
 	/// display the briefing ClueBlock. Mirrors `_DoInitClues` @ 1a35:0411
 	/// minus the live ANI sequence playback.
@@ -300,6 +389,28 @@ private:
 	/// original `_DisplayCorrect` flow.
 	uint8 _mysteriesSolved[55] = {};
 
+	/// Current chain/tier the player is at ??? mirrors `DAT_2d5d_3f99`
+	/// (`_PlayerRecord +0x2f`):
+	///   1 = Junior detective  (mysteries  1 .. 24, "A chain")
+	///   2 = Senior detective  (mysteries 25 .. 48, "B chain")
+	///   3 = Master detective  (mysteries 49 .. 54, "C chain")
+	/// Initialized to 1 in `_NewPlayer @ 1c33:0fa3` and bumped by
+	/// `_DisplayCorrect @ 1df2:0853` once every mystery in the current
+	/// tier is solved (range checks at 1df2:080d / 0824 / 0837). The
+	/// value also gates `_CaseSelection`'s book label and selection
+	/// list (1c33:0a87 onwards).
+	uint8 _chainStage = 1;
+
+	/// Voice / digital-audio enable flag. Mirrors `DAT_2d5d_3f97`
+	/// (`_PlayerRecord +0x2d`). Set to 1 by `_NewPlayer @ 1c33:0fa3`,
+	/// toggled by the SoundOn / SoundOff hot-rects in `_DoSetup @
+	/// 1f78:044e` (verified at `_SetupSettings` 1f78:0076 reading
+	/// the same byte to colour the on/off labels). Gates every
+	/// `_PlayVoice` and `_SpoolSound` call site (clue voices,
+	/// partner speech, intro VO etc. ??? see `_DoChoosePartner`,
+	/// `_DisplayClue`, `_SayKDDigital` xrefs).
+	bool _voiceOn = true;
+
 	Common::RandomSource _rng;
 
 	DBDArchive _picsArchive;     ///< PICS.DBD/.DBX (sprites, buttons, frame backgrounds)
@@ -347,6 +458,12 @@ private:
 	/// 202f:05cb`). Constructed alongside `_music` in `run()`.
 public:
 	AudioPlayer *_audio = nullptr;
+
+	/// Public setter for `_nextScreen` so site loop / inline screens
+	/// can drive the screen-driver state machine without making the
+	/// member itself public. Mirrors the original's direct write to
+	/// `_NextScreen @ 2d5d:3f26` from anywhere in the engine.
+	void setNextScreen(ScreenId s) { _nextScreen = s; }
 };
 
 } // End of namespace EEM
diff --git a/engines/eem/site.cpp b/engines/eem/site.cpp
index e6122b5d6eb..42c5429d44e 100644
--- a/engines/eem/site.cpp
+++ b/engines/eem/site.cpp
@@ -65,6 +65,12 @@ void blitFrame(Graphics::ManagedSurface &dst, const Picture &p,
 // and `renderStaticDrops`.
 void blitMaskedSurface(Graphics::Surface *screen, const Picture &p,
 					   int x, int y) {
+	// Top-left semantics. Used for static drops (`_AddDrop @
+	// 172b:1a77` which calls `_Rect_Move_Mask(..., x, y, ...)` with
+	// the raw (x, y) and ignores per-frame anchors) and any other
+	// non-animated overlay. Animation rendering should go through
+	// `blitAnimFrameAnchored` instead so per-frame anchor offsets
+	// (miscflags = X, rowoff = Y) apply correctly.
 	if (!screen)
 		return;
 	const byte transp = (byte)(p.flags >> 8);
@@ -84,6 +90,40 @@ void blitMaskedSurface(Graphics::Surface *screen, const Picture &p,
 	}
 }
 
+void blitAnimFrameAnchored(Graphics::Surface *screen, const Picture &p,
+						   int anchorX, int anchorY) {
+	// `_UpdateAnimations @ 172b:09c1` blits each animation frame at
+	// `(anchor_x - puVar5[4], anchor_y - puVar5[3])` where puVar5[3]/[4]
+	// are the per-frame `rowoff` / `miscflags` values from the
+	// 16-byte PicData header. Both are SIGNED 16-bit anchor offsets
+	// ??? when frames have varying anchors (anim 0x14 BigMap walk-
+	// cycle has miscflags = -2 per cell, anim 0x07 has rowoff up to
+	// 61), the sprite actually translates across the screen as it
+	// cycles through cells. Without this, the partner "shakes in
+	// place" instead of walking. (Transparency still comes from
+	// `flags >> 8`, verified at the `_Rect_Move_Mask(..., *thePic >>
+	// 8)` call ??? NOT from miscflags as an earlier comment claimed.)
+	if (!screen)
+		return;
+	const int blitX = anchorX - (int)(int16)p.miscflags;
+	const int blitY = anchorY - (int)(int16)p.rowoff;
+	const byte transp = (byte)(p.flags >> 8);
+	for (int row = 0; row < p.surface.h; row++) {
+		const int dstY = blitY + row;
+		if (dstY < 0 || dstY >= screen->h)
+			continue;
+		const byte *src = (const byte *)p.surface.getBasePtr(0, row);
+		byte *dst = (byte *)screen->getBasePtr(0, dstY);
+		for (int col = 0; col < p.surface.w; col++) {
+			const int dstX = blitX + col;
+			if (dstX < 0 || dstX >= screen->w)
+				continue;
+			if (src[col] != transp)
+				dst[dstX] = src[col];
+		}
+	}
+}
+
 // Rotate one VGA palette range by one slot. Mirrors `_ColorCycle @
 // 172b:2015` ??? used by both the per-site Loop-1 ColorCycle entries and
 // the always-on hotspot marching-ants range 0xF9..0xFE.
@@ -136,40 +176,287 @@ const uint16 kKdAnimTable[6][6] = {
 	{ 0x06, 0x06, 6, 6, 80, 80 }, // 5 ??? same anim both partners
 };
 
-// Sequence-script lookup. Entries copied verbatim from
-// `_AnimationSequences @ 29be:22d4` walked through to the next 0x80.
-// Each script is a u16[] of frame indices terminated by 0x80; we
-// don't yet handle 0x81 jumps (none of the kdAnim sequences use
-// them ??? verified). seqnum == animId for these calls (per
-// `_PlayAnimation` 172b:1f5d push order).
-struct KdScript {
+// Animation script table. Mirrors `_AnimationSequences @ 29be:22d4`
+// (a 55-entry table of far ptrs, each pointing to a u16-frame-index
+// stream terminated by 0x80; 0x81 marks a jump that we don't see in
+// the partner subset and so don't yet implement).
+//
+// `_NewAnimation @ 172b:06e1` reads the script via
+// `_AnimationSequences[anim_id]` and stores the pointer in
+// `DAT_2d5d_3eaf[i*0xb]`. `_UpdateAnimations @ 172b:09c1` then walks
+// it one entry per `_CheckFrameRate` tick (~100 ms): the value at
+// `script[index]` is the frame to render; 0x80 resets index to 0
+// (loop). So a script like `[0,0,0,0,0,0,0,0,0,2]` renders nine ticks
+// of frame 0 then one tick of frame 2 ??? the natural "blink with long
+// idle hold" cadence.
+//
+// We use the same scripts for the wait anims (`renderPartner`) AND
+// the kd-clue reaction anims (`playKdAnim`), since both call
+// `_NewAnimation` in the original ??? only the state field differs (1
+// = looping, 4 = one-shot). seqnum == animId per `_PlayAnimation`
+// 172b:1f5d push order.
+//
+// Each entry was read directly from the EXE via Ghidra; cross-checked
+// against `_NewAnimation`'s read. Frame counts include only the
+// playable frames (the trailing 0x80 is the terminator, not a frame).
+struct AnimScript {
 	uint16 seqnum;
 	uint8 len;
-	uint8 frames[20];  // long enough for any kdAnim script
+	uint8 frames[28];  // longest partner-subset script is 26 frames (anim 2)
 };
-const KdScript kKdScripts[] = {
-	// seqnum 1 (29be:188a) ??? head bob
+const AnimScript kAnimScripts[] = {
+	// 0x00 (29be:185e) ??? Jake speaker-0 wait: nine idle, one blink, loop
+	{ 0x00, 10, { 0,0,0,0,0,0,0,0,0,2 } },
+	// 0x01 (29be:188a) ??? Jake PDA idle: alternating head bob with peak
 	{ 0x01, 15, { 0,1,2,0,1,0,2,1,0,1,0,1,2,1,0 } },
-	// seqnum 2 (29be:18aa) ??? short blip then long pause
-	{ 0x02, 16, { 0,1,2,1,0,0,0,0,0,0,0,0,0,0,0,0 } },
-	// seqnum 3 (29be:18e0) ??? Jake "lift, hold, lower" gesture
+	// 0x02 (29be:18aa) ??? Jake gallery: brief wave, long hold, second
+	// wave, hold (CONFIRMED 26 frames ??? earlier table was truncated to
+	// 16 which dropped the second wave cycle).
+	{ 0x02, 26, { 0,1,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,1,0,0,0,0,0,0 } },
+	// 0x03 (29be:18e0) ??? Jake "lift, hold, lower" gesture
 	{ 0x03,  9, { 0,1,2,3,2,2,2,1,0 } },
-	// seqnum 4 (29be:18f4) ??? bigger gesture (camera flash-style)
+	// 0x04 (29be:18f4) ??? Jake bigger gesture (camera flash-style)
 	{ 0x04, 13, { 0,1,2,3,4,5,4,4,4,3,2,1,0 } },
-	// seqnum 5 (29be:1910) ??? held idle with a single peak
+	// 0x05 (29be:1910) ??? Jake/Jenny shared (speaker 5): held idle, peak
 	{ 0x05, 13, { 0,0,0,1,2,3,2,1,0,0,0,0,0 } },
-	// seqnum 6 (29be:192c) ??? empty (immediate END)
+	// 0x06 (29be:192c) ??? speaker 6 partner: empty (immediate END,
+	// renders nothing ??? verified at 29be:192c byte 0 = 0x80)
 	{ 0x06,  0, { 0 } },
-	// seqnum 0xb (29be:188a, same as 1) ??? Jenny PDA idle
+	// 0x07 (29be:192e) ??? Jake walk-cycle (10 frames: 0..9)
+	{ 0x07, 10, { 0,1,2,3,4,5,6,7,8,9 } },
+	// 0x08 (29be:1944) ??? Jake stand-still with very late blink
+	{ 0x08,  8, { 0,0,0,0,0,0,0,1 } },
+	// 0x09 (29be:1956) ??? short blip animation
+	{ 0x09,  9, { 0,0,0,1,0,0,0,0,0 } },
+	// 0x0a ??? Jenny speaker-0 wait (alias of 0x00 in the binary)
+	{ 0x0a, 10, { 0,0,0,0,0,0,0,0,0,2 } },
+	// 0x0b ??? Jenny PDA idle (alias of 0x01)
 	{ 0x0b, 15, { 0,1,2,0,1,0,2,1,0,1,0,1,2,1,0 } },
-	// seqnum 0xc (29be:18e0, same as 3) ??? Jenny "take a picture"
+	// 0x0c ??? Jenny "take a picture" (alias of 0x03)
 	{ 0x0c,  9, { 0,1,2,3,2,2,2,1,0 } },
-	// seqnum 0xd (29be:18f4, same as 4) ??? Jenny big gesture
+	// 0x0d ??? Jenny big gesture (alias of 0x04)
 	{ 0x0d, 13, { 0,1,2,3,4,5,4,4,4,3,2,1,0 } },
-	// seqnum 0x10 (29be:1956) ??? Jenny short anim
+	// 0x0e ??? alias of 0x06 (empty)
+	{ 0x0e,  0, { 0 } },
+	// 0x0f ??? alias of 0x09
+	{ 0x0f,  9, { 0,0,0,1,0,0,0,0,0 } },
+	// 0x10 ??? Jenny gallery wait (alias of 0x09 ??? verified at 29be:1956)
 	{ 0x10,  9, { 0,0,0,1,0,0,0,0,0 } },
+	// 0x11 (29be:1992) ??? Jenny entrance count-up: 0..7. Used by
+	// `_DoBigMap` when `_LastScreen == 2` (BigMap entrance one-shot).
+	{ 0x11,  8, { 0,1,2,3,4,5,6,7 } },
+	// 0x12 (29be:197e) ??? Jake entrance count-down 8..0. Used by
+	// `_DoBigMap` (entrance one-shot, partner-specific exit cell).
+	{ 0x12,  9, { 8,7,6,5,4,3,2,1,0 } },
+	// 0x13 (29be:1992, alias of 0x11) ??? Jake walk-cycle 0..7,
+	// looped during BigMap idle.
+	{ 0x13,  8, { 0,1,2,3,4,5,6,7 } },
+	// 0x14 (29be:196a) ??? BigMap idle walk-cycle 0..8 (9 cells),
+	// partner shifts feet while you pick a site.
+	{ 0x14,  9, { 0,1,2,3,4,5,6,7,8 } },
+	// 0x15 (29be:185e, alias of 0x00) ??? Jake CaseSelection greeter:
+	// nine idle, one blink, loop. Same blink cadence as the site
+	// loop's wait anim (animID 0x00).
+	{ 0x15, 10, { 0,0,0,0,0,0,0,0,0,2 } },
+	// 0x16 (29be:185e, alias of 0x00) ??? Jenny CaseSelection greeter,
+	// same blink script as 0x15.
+	{ 0x16, 10, { 0,0,0,0,0,0,0,0,0,2 } },
+	// Briefing animations ??? `_DoInitClues @ 1a35:0411` calls
+	// `_NewAnimation(..., (PicData *)CONCAT22(0x17, ...), 1, ...)`
+	// for the game animation (always anim ID 0x17 ??? even Jenny's
+	// briefing reuses Jake's SCRIPT, even though the loaded ANI.DBD
+	// cells come from her partner-specific entry 0x3b). Same pattern
+	// for book (0x18 always) and nancy (0x19 always).
+	//
+	// AnimScript len was 28 ??? these scripts overflow that. Bump
+	// `frames[]` is fine because we just need to fit 30 frames per
+	// briefing entry. We size `frames` to 36 so all five scripts fit
+	// (longest is 0x18 at 30 frames).
+};
+static_assert(true, "see kAnimScriptsLong below for >28-frame scripts");
+
+// Scripts longer than 28 frames live here so the main `kAnimScripts`
+// table can keep its tight `frames[28]` storage (the lookup in
+// `findAnimScript` checks both arrays). Used for the briefing
+// animations whose original scripts run 30 frames each.
+struct AnimScriptLong {
+	uint16 seqnum;
+	uint8 len;
+	uint8 frames[36];
+};
+const AnimScriptLong kAnimScriptsLong[] = {
+	// 0x17 (29be:221a) ??? briefing game count-up 0..29 (30 frames),
+	// drives the per-tick frame walk of the game piece animation
+	// during `_DoInitClues`.
+	{ 0x17, 30, { 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,
+				  20,21,22,23,24,25,26,27,28,29 } },
+	// 0x18 (29be:2296) ??? briefing book: counts up to cell 8 then
+	// holds for 16 ticks (the "thinking" pose) then count up 9..15.
+	{ 0x18, 30, { 0,1,2,3,4,5,6,7,8,8,8,8,8,8,8,8,
+				  8,8,8,8,8,8,8,9,10,11,12,13,14,15 } },
+	// 0x19 (29be:2258) ??? briefing nancy: 18 idle ticks then
+	// count-up 1..12 (the late-arriving sidekick pose).
+	{ 0x19, 30, { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+				  1,2,3,4,5,6,7,8,9,10,11,12 } },
 };
 
+// Look up the script for `seqnum`. Returns the frame array + length,
+// or `(nullptr, 0)` if no script is known ??? caller falls back to
+// flipbook cycling so unknown anims still animate (just without idle
+// holds).
+struct AnimScriptRef {
+	const uint8 *frames;
+	uint8 len;
+};
+static AnimScriptRef findAnimScript(uint16 seqnum) {
+	for (uint i = 0; i < ARRAYSIZE(kAnimScripts); i++) {
+		if (kAnimScripts[i].seqnum == seqnum) {
+			AnimScriptRef r;
+			r.frames = kAnimScripts[i].frames;
+			r.len = kAnimScripts[i].len;
+			return r;
+		}
+	}
+	for (uint i = 0; i < ARRAYSIZE(kAnimScriptsLong); i++) {
+		if (kAnimScriptsLong[i].seqnum == seqnum) {
+			AnimScriptRef r;
+			r.frames = kAnimScriptsLong[i].frames;
+			r.len = kAnimScriptsLong[i].len;
+			return r;
+		}
+	}
+	AnimScriptRef r;
+	r.frames = nullptr;
+	r.len = 0;
+	return r;
+}
+
+void auditPartnerAnims(EEMEngine *vm) {
+	// Cross-check every registered partner-subset script against the
+	// underlying ANI.DBD entry it references. If the script asks for
+	// a frame past the anim's actual frame count, the visible result
+	// is "missing frames" ??? that's the user-reported symptom we
+	// want to catch and fix here, not paper over with a clamp.
+	if (!vm)
+		return;
+	DBDArchive &ani = vm->getAni();
+
+	// Helper: audit one (id, frames, len) tuple ??? log a warning if
+	// the script asks for a frame the ANI.DBD entry doesn't have.
+	struct Walker {
+		static void check(DBDArchive &ani, uint16 id, const uint8 *frames, uint8 len) {
+			if (len == 0)
+				return;
+			Animation a;
+			if (!ani.loadAnimation(id, a) || a.empty()) {
+				debugC(1, kDebugSite,
+					   "auditPartnerAnims: anim 0x%02x failed to load", id);
+				return;
+			}
+			uint maxRequested = 0;
+			for (uint j = 0; j < len; j++)
+				if (frames[j] > maxRequested)
+					maxRequested = frames[j];
+			if (maxRequested >= a.size()) {
+				warning("anim 0x%02x: script wants frame %u but ANI.DBD has "
+						"only %u ??? frames will be clamped (verify script "
+						"reading from `_AnimationSequences[0x%02x]` against "
+						"Ghidra)",
+						id, maxRequested, (uint)a.size(), id);
+			} else {
+				debugC(2, kDebugSite,
+					   "anim 0x%02x: %u cells, script max=%u, len=%u",
+					   id, (uint)a.size(), maxRequested, len);
+			}
+		}
+	};
+
+	for (uint i = 0; i < ARRAYSIZE(kAnimScripts); i++)
+		Walker::check(ani, kAnimScripts[i].seqnum,
+					  kAnimScripts[i].frames, kAnimScripts[i].len);
+	for (uint i = 0; i < ARRAYSIZE(kAnimScriptsLong); i++)
+		Walker::check(ani, kAnimScriptsLong[i].seqnum,
+					  kAnimScriptsLong[i].frames, kAnimScriptsLong[i].len);
+
+	// Per-frame anchor-offset audit. The original `_UpdateAnimations
+	// @ 172b:09c1` blits each frame at `(anchor_x - frame.miscflags,
+	// anchor_y - frame.rowoff)`. Our `blitMaskedSurface` ignores
+	// those offsets ??? it treats the WaitAnims (anchor_x, anchor_y)
+	// as the top-left. That's fine when every frame has
+	// `miscflags == 0 && rowoff == 0`; if any frame has a non-zero
+	// anchor, the partner sprite jumps between cells. Log the
+	// offending IDs so we know whether to plumb anchors through
+	// `partnerFrameAtTick` callers.
+	// Audit covers every animID we register a script for ??? the
+	// partner-subset (0x00..0x16, used by the wait anims, kd-clue
+	// reactions, BigMap, Notebook, Gallery, CaseSelection greeter)
+	// AND the briefing-subset (0x17..0x19, the
+	// game/book/nancy anims driven by `_DoInitClues @ 1a35:0411`).
+	const uint16 partnerIds[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05,
+								   0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c,
+								   0x0d, 0x0f, 0x10, 0x11, 0x12, 0x13,
+								   0x14, 0x15, 0x16, 0x17, 0x18, 0x19 };
+	for (uint i = 0; i < ARRAYSIZE(partnerIds); i++) {
+		const uint16 id = partnerIds[i];
+		Animation a;
+		if (!ani.loadAnimation(id, a) || a.empty())
+			continue;
+		bool anyAnchor = false;
+		int rowMin = 0, rowMax = 0, miscMin = 0, miscMax = 0;
+		for (uint f = 0; f < a.size(); f++) {
+			const Picture &fr = a[f];
+			const int sRow  = (int)(int16)fr.rowoff;
+			const int sMisc = (int)(int16)fr.miscflags;
+			if (sRow != 0 || sMisc != 0) {
+				if (!anyAnchor) {
+					rowMin = rowMax = sRow;
+					miscMin = miscMax = sMisc;
+					anyAnchor = true;
+				} else {
+					rowMin = MIN(rowMin, sRow);
+					rowMax = MAX(rowMax, sRow);
+					miscMin = MIN(miscMin, sMisc);
+					miscMax = MAX(miscMax, sMisc);
+				}
+			}
+		}
+		if (anyAnchor) {
+			// `_UpdateAnimations @ 172b:09c1` reads these as signed
+			// 16-bit values via `puVar5[3]/[4]`, so log them with
+			// the sign preserved ??? earlier the log printed unsigned
+			// 65534 instead of -2.
+			debugC(1, kDebugSite,
+				   "anim 0x%02x: per-frame anchor (rowoff [%d..%d], "
+				   "miscflags [%d..%d]) ??? handled by "
+				   "`blitAnimFrameAnchored`",
+				   id, rowMin, rowMax, miscMin, miscMax);
+		}
+	}
+}
+
+// Pick the frame index to render at `tickMs` for the looping
+// animation `seqnum` whose underlying ANI.DBD entry has `numFrames`
+// frames. Mirrors the looping path of `_UpdateAnimations`: walk the
+// script one entry per ~100 ms `_CheckFrameRate` tick, wrap on the
+// 0x80 terminator. Exposed (non-static) so the BigMap, CaseSelection
+// greeter, Notebook, and Gallery render paths in `ui.cpp` can use the
+// same cadence ??? without this, every off-site partner rendering
+// flipbook-cycles ALL cells of the ANI entry (no idle holds, no
+// timing variations) which is the user-reported "constantly looping"
+// symptom.
+uint partnerFrameAtTick(uint16 seqnum, uint numFrames, uint32 tickMs) {
+	const AnimScriptRef s = findAnimScript(seqnum);
+	const uint kFramePeriodMs = 100;
+	if (!s.frames || s.len == 0)
+		return numFrames > 0 ? (uint)((tickMs / kFramePeriodMs) % numFrames) : 0;
+	const uint scriptIdx = (uint)((tickMs / kFramePeriodMs) % s.len);
+	const uint frame     = s.frames[scriptIdx];
+	// The script can in theory request a frame that's outside the
+	// animation's actual frame count (a misencoded script). Clamp so
+	// we don't read past `anim[]` in the caller.
+	return (numFrames > 0) ? MIN<uint>(frame, numFrames - 1) : 0;
+}
+
 void SiteScreen::enter(uint siteNum) {
 	if (!_mystery || !_mystery->isLoaded()) {
 		warning("SiteScreen::enter: no mystery loaded");
@@ -181,6 +468,13 @@ void SiteScreen::enter(uint siteNum) {
 		return;
 	}
 
+	// Reset the wait-anim phase so the partner starts fresh from
+	// script[0] when entering. Mirrors `_DoSiteLoop @ 168d:0436`
+	// where `_NewAnimation` sets the new slot's frame index to
+	// 0xffff (= -1, becomes 0 on the first `_UpdateAnimations`
+	// tick).
+	_waitPhaseAnchor = g_system->getMillis();
+
 	// Capture whether this is the first time the player enters this
 	// site BEFORE we mark it visited ??? `_DoSiteLoop @ 168d:03f4`
 	// uses the same check to decide whether to play the arrival
@@ -347,8 +641,18 @@ void SiteScreen::run() {
 			case Common::EVENT_KEYDOWN:
 				switch (event.kbd.keycode) {
 				case Common::KEYCODE_ESCAPE:
-					if (_vm->areYouSure())
+					if (_vm->areYouSure()) {
+						// Mirrors `_DoSiteLoop @ 168d:07b7` ESC path:
+						// `_NextScreen = 1` (back to MAP) after the
+						// areYouSure confirm. Without explicitly
+						// writing it here, the run() loop would see
+						// _nextScreen unchanged and treat it as a
+						// quit-engine signal ??? abandoning the case.
+						// Going to MAP keeps the case alive so the
+						// player can continue from a different site.
+						_vm->setNextScreen(kScreenMap);
 						return;
+					}
 					enter(cur);
 					break;
 				case Common::KEYCODE_m:
@@ -646,8 +950,6 @@ void SiteScreen::renderAnimatedDrops(uint siteNum, uint32 tickMs) {
 	if (!screen)
 		return;
 
-	const uint32 kFramePeriodMs = 100; // ~10 FPS, in line with `_CheckFrameRate`.
-
 	for (uint i = 0; i < numAnims; i++) {
 		const uint dropOff = 0x48 + i * 6;
 		const int16 animId = (int16)READ_LE_UINT16(site + dropOff + 0);
@@ -658,8 +960,12 @@ void SiteScreen::renderAnimatedDrops(uint siteNum, uint32 tickMs) {
 		Animation anim;
 		if (!_vm->getAni().loadAnimation((uint)animId, anim) || anim.empty())
 			continue;
-		const uint frameIdx = (uint)((tickMs / kFramePeriodMs) % anim.size());
-		blitMaskedSurface(screen, anim[frameIdx], x, y);
+		const uint frameIdx = partnerFrameAtTick((uint16)animId,
+												  (uint)anim.size(), tickMs);
+		// Animated drops go through `_NewAnimation` in the original,
+		// so `_UpdateAnimations` applies per-frame anchor offsets ???
+		// route through the anchored blitter.
+		blitAnimFrameAnchored(screen, anim[frameIdx], x, y);
 	}
 
 	g_system->unlockScreen();
@@ -763,16 +1069,31 @@ void SiteScreen::renderPartner(uint siteNum, uint32 tickMs) {
 	if (!_vm->getAni().loadAnimation(animId, anim) || anim.empty())
 		return;
 
-	// `_UpdateAnimations @ 172b:09c1` advances the partner's frame
-	// every `_CheckFrameRate` tick. We pick the frame from a global
-	// 100 ms clock so the partner cycles in sync with the animated
-	// drops.
-	const uint32 kFramePeriodMs = 100;
-	const uint frameIdx = (uint)((tickMs / kFramePeriodMs) % anim.size());
+	// `_UpdateAnimations @ 172b:09c1` walks the per-anim script (from
+	// `_AnimationSequences[seqnum]`) one entry per `_CheckFrameRate`
+	// tick (~100 ms): render `script[index]`, advance, wrap on 0x80.
+	// That's how the original gets long idle holds with brief blinks
+	// ??? naive flipbook cycling (`tick % nFrames`) loses those pauses
+	// and makes the partner constantly fidget. `partnerFrameAt` picks
+	// the right frame; if no script is registered for this anim it
+	// falls back to flipbook so unknown anims still move.
+	// Use the relative phase anchor instead of the raw `tickMs` so
+	// the wait anim resumes from script[0] after each kdAnim
+	// one-shot ends ??? matching the original's `_PlayAnimation @
+	// 172b:1f5d` resetting the resumed slot's frame index to
+	// 0xffff. Without this, the wait anim snaps mid-cycle every
+	// time we return from a clue display.
+	const uint32 elapsed = (tickMs >= _waitPhaseAnchor)
+							? (tickMs - _waitPhaseAnchor)
+							: tickMs;
+	const uint frameIdx = partnerFrameAtTick((uint16)animId,
+											  (uint)anim.size(), elapsed);
 	Graphics::Surface *screen = g_system->lockScreen();
 	if (!screen)
 		return;
-	blitMaskedSurface(screen, anim[frameIdx], x, y);
+	// Partner sprite ??? anchor-aware blit so per-frame `miscflags` /
+	// `rowoff` apply (e.g. the BigMap walk-cycle's -2 px shift).
+	blitAnimFrameAnchored(screen, anim[frameIdx], x, y);
 	g_system->unlockScreen();
 }
 
@@ -1012,7 +1333,7 @@ void EEMEngine::playKdAnim(uint16 num) {
 	// gesture (Jenny taking a picture, etc.) finishes before the
 	// speaker portrait + speech balloon appear.
 	//
-	// `kKdAnimTable` and `kKdScripts` live at file scope above.
+	// `kKdAnimTable` and `kAnimScripts` live at file scope above.
 	if (num >= ARRAYSIZE(kKdAnimTable))
 		return;
 
@@ -1027,15 +1348,14 @@ void EEMEngine::playKdAnim(uint16 num) {
 		return;
 	}
 
-	const uint8 *frames = nullptr;
-	uint frameCount = 0;
-	for (uint i = 0; i < ARRAYSIZE(kKdScripts); i++) {
-		if (kKdScripts[i].seqnum == animId) {
-			frames = kKdScripts[i].frames;
-			frameCount = kKdScripts[i].len;
-			break;
-		}
-	}
+	// `_DoKDAnim` (168d:028a) calls `_PlayAnimation` with state=4 (one-
+	// shot), which `_UpdateAnimations` walks until it sees the 0x80
+	// terminator and then frees the slot. The same script the
+	// site-loop wait anim uses (looping) is what the one-shot plays
+	// through ONCE here.
+	const AnimScriptRef s = findAnimScript(animId);
+	const uint8 *frames = s.frames;
+	uint frameCount     = s.len;
 	if (frameCount == 0) {
 		// Fallback: linear playback through anim cells (better than
 		// nothing if a future kdAnim references an unscripted anim).
@@ -1082,22 +1402,13 @@ void EEMEngine::playKdAnim(uint16 num) {
 			memcpy((byte *)scratch.getBasePtr(0, row),
 				   (const byte *)bg.getBasePtr(0, row), 320);
 		}
-		const int w = MIN<int>(fr.surface.w, 320 - px);
-		const int h = MIN<int>(fr.surface.h, 200 - py);
-		for (int row = 0; row < h; row++) {
-			const int dstY = py + row;
-			if (dstY < 0)
-				continue;
-			const byte *src = (const byte *)fr.surface.getBasePtr(0, row);
-			byte *dst = (byte *)scratch.getBasePtr(0, dstY);
-			for (int col = 0; col < w; col++) {
-				const int dstX = px + col;
-				if (dstX < 0)
-					continue;
-				if (src[col] != transp)
-					dst[dstX] = src[col];
-			}
-		}
+		// Anchor-aware: kdAnim cells (0x03/0x04/0x0c/0x0d ...) have
+		// non-zero per-frame `miscflags`/`rowoff` (anim 0x03 has
+		// rowoff up to 9, anim 0x04 has miscflags = -2). Without
+		// applying those, the camera-flash gesture pop-up appears
+		// at a fixed pixel rather than translating across cells.
+		(void)transp;  // anchored blitter recomputes from p.flags
+		blitAnimFrameAnchored(scratch.surfacePtr(), fr, px, py);
 		g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
 								   0, 0, 320, 200);
 		g_system->updateScreen();
diff --git a/engines/eem/site.h b/engines/eem/site.h
index 00c1e647ea0..51129bfed74 100644
--- a/engines/eem/site.h
+++ b/engines/eem/site.h
@@ -27,11 +27,35 @@
 
 #include "graphics/managed_surface.h"
 
+#include "eem/resource.h"  // Picture / Animation
+
 namespace EEM {
 
 class EEMEngine;
 class Mystery;
 
+/// Pick the frame index to render at `tickMs` for animation
+/// `seqnum`. Walks the script registered in `kAnimScripts` (mirrors
+/// `_UpdateAnimations @ 172b:09c1`'s looping path) at one entry per
+/// 100 ms tick, wrapping on the script's 0x80 terminator. Falls
+/// back to flipbook (`tick % numFrames`) when no script is
+/// registered. `numFrames` is the underlying ANI.DBD entry's cell
+/// count ??? used both for the fallback path and to clamp script
+/// values that point past the asset.
+uint partnerFrameAtTick(uint16 seqnum, uint numFrames, uint32 tickMs);
+
+/// Anchor-aware masked blit. Mirrors the per-frame anchor offset
+/// math in `_UpdateAnimations @ 172b:09c1`:
+/// `blit_x = anchor_x - frame.miscflags`, `blit_y = anchor_y -
+/// frame.rowoff`. Use for any animation rendered through the
+/// `_NewAnimation` path in the original (partner sprites, animated
+/// drops, briefing animations) ??? without it, frames with non-zero
+/// per-cell anchors (e.g. anim 0x14 BigMap walk-cycle's miscflags
+/// = -2 shift) "shake in place" instead of translating across
+/// the screen as they're meant to.
+void blitAnimFrameAnchored(Graphics::Surface *screen, const Picture &p,
+						   int anchorX, int anchorY);
+
 /// One hotspot (search rectangle) within a site, 14 bytes on disk.
 struct Hotspot {
 	int16  x1, y1, x2, y2;     ///< rectangle in screen coordinates
@@ -122,6 +146,22 @@ private:
 	Graphics::ManagedSurface _bgSnapshot;
 	uint32 _lastTickMs = 0;        ///< Last frame-pump tick in ms.
 
+	/// Wall-clock timestamp at which the partner's wait animation
+	/// "started" (or last restarted). The site loop renders the
+	/// wait sprite at `partnerFrameAtTick(animId, ..., now -
+	/// _waitPhaseAnchor)` so the script position is RELATIVE to
+	/// this anchor, not the global clock. Bump it on:
+	///   - entry to a new site (mirrors `_NewAnimation` setting
+	///     the slot's frame index to 0xffff at site setup, see
+	///     `_DoSiteLoop @ 168d:0436`)
+	///   - return from a one-shot kdAnim (mirrors `_PlayAnimation
+	///     @ 172b:1f5d` writing 0xffff to the resumed slot's frame
+	///     index when state=4 chains back to WaitHandle)
+	/// Without this, the wait anim "snaps" to wherever the global
+	/// clock dictates each time the partner reappears, instead of
+	/// resuming from script[0].
+	uint32 _waitPhaseAnchor = 0;
+
 	/// Per-site cached ColorCycle ranges. Up to 5 (matching the
 	/// original's 5-slot animation table).
 	struct ColorCycleRange { uint8 start, end; };
diff --git a/engines/eem/ui.cpp b/engines/eem/ui.cpp
index c78ea11d37d..55554a7ff30 100644
--- a/engines/eem/ui.cpp
+++ b/engines/eem/ui.cpp
@@ -34,6 +34,7 @@
 #include "eem/detection.h"
 #include "eem/eem.h"
 #include "eem/music.h"
+#include "eem/site.h"
 
 // EEM ??? UI screens (NOTE.C, GALLERY.C, ACCUSE.C, MAP.C, CHOOSE.C combined).
 // Each function is a self-contained modal `EEMEngine::doX()` reachable from
@@ -89,6 +90,7 @@ struct CaseSelectionView {
 	bool haveCaseBg;
 	const Animation *kdAnim;
 	bool haveKdAnim;
+	uint16 kdAnimId;     ///< 0x15 / 0x16 ??? looked up in kAnimScripts
 	int kdAnimX;
 	int kdAnimY;
 	const char *separator;
@@ -166,26 +168,24 @@ void drawCaseSelectionFrame(const CaseSelectionView &v) {
 	}
 
 	// KD greeter frame ??? masked-blit current animation cell at
-	// (0x112, 0x50). 100 ms tick matches the engine's `_CheckFrameRate`.
+	// (0x112, 0x50). 100 ms tick matches `_CheckFrameRate`. The
+	// original `_CaseSelection @ 1c33:0a87` calls `_NewAnimation(...,
+	// CONCAT22(0x15, ...), ..., seqnum=0x15, ...)` so the script
+	// key is 0x15 regardless of partner ??? even Jenny's CELLS (loaded
+	// via animID 0x16 = ANI.DBD slot) get driven by Jake's 0x15
+	// blink script. Both 0x15 and 0x16 are aliases of 0x00 in our
+	// table so the result is identical, but routing through 0x15
+	// matches the binary.
 	if (v.haveKdAnim) {
 		const uint32 now = g_system->getMillis();
-		const uint frameIdx = (uint)((now / 100) % v.kdAnim->size());
-		const Picture &fr = (*v.kdAnim)[frameIdx];
-		const byte transp = (byte)(fr.flags >> 8);
-		for (int row = 0; row < fr.surface.h; row++) {
-			const int dstY = v.kdAnimY + row;
-			if (dstY < 0 || dstY >= 200)
-				continue;
-			const byte *src = (const byte *)fr.surface.getBasePtr(0, row);
-			byte *dst = (byte *)scratch.getBasePtr(0, dstY);
-			for (int col = 0; col < fr.surface.w; col++) {
-				const int dstX = v.kdAnimX + col;
-				if (dstX < 0 || dstX >= 320)
-					continue;
-				if (src[col] != transp)
-					dst[dstX] = src[col];
-			}
-		}
+		const uint frameIdx = partnerFrameAtTick(0x15,
+												  (uint)v.kdAnim->size(), now);
+		// Anchor-aware blit. Same rendering path used everywhere
+		// the partner is registered through `_NewAnimation` in the
+		// original.
+		blitAnimFrameAnchored(scratch.surfacePtr(),
+							  (*v.kdAnim)[frameIdx],
+							  v.kdAnimX, v.kdAnimY);
 	}
 	if (v.vm->getFont().isLoaded()) {
 		// `DrawList` @ 1c33:040d coordinates: `_TextBox + 3` for x
@@ -234,6 +234,138 @@ void drawCaseSelectionFrame(const CaseSelectionView &v) {
 	g_system->updateScreen();
 }
 
+void EEMEngine::doProfilePicker() {
+	// Mirrors `screen8_handler @ 1c33:1012`. The original walks
+	// `*.PLR` files in `C:\EEMCDSAV\` (max 25), reads the first 12
+	// bytes of each (the player-name field of `_PlayerRecord`), and
+	// hands the list to `_DoChoose`. If no profiles exist (loop hits
+	// `local_20 == 0` at 1c33:1170), it falls straight into
+	// `_NewPlayer`. Selecting an entry calls `_LoadPlayerRecord` and
+	// returns; selecting the "exit" sentinel goes back to title.
+	const SaveStateList saves = listProfiles();
+	if (saves.empty()) {
+		doNewPlayer();
+		return;
+	}
+
+	if (!_font.isLoaded()) {
+		// No font means we can't render the picker ??? fall through.
+		doNewPlayer();
+		return;
+	}
+
+	// Build the visible list: existing profile names + "[New Player]".
+	struct Entry {
+		Common::String label;
+		int slot;       ///< -1 means "create new"
+	};
+	Common::Array<Entry> entries;
+	for (const SaveStateDescriptor &s : saves) {
+		Entry e;
+		e.label = s.getDescription();
+		e.slot  = s.getSaveSlot();
+		entries.push_back(e);
+	}
+	Entry newEntry;
+	newEntry.label = "[New Player]";
+	newEntry.slot  = -1;
+	entries.push_back(newEntry);
+
+	int sel = 0;
+	bool done = false;
+
+	auto draw = [&]() {
+		Graphics::ManagedSurface scratch(320, 200,
+			Graphics::PixelFormat::createFormatCLUT8());
+		scratch.clear();
+		Picture bg;
+		if (_picsArchive.getPicture(0x104, bg)) {
+			const int w = MIN<int>(bg.surface.w, 320);
+			const int h = MIN<int>(bg.surface.h, 200);
+			for (int row = 0; row < h; row++)
+				memcpy((byte *)scratch.getBasePtr(0, row),
+					   (const byte *)bg.surface.getBasePtr(0, row), w);
+		}
+		_font.drawString(&scratch, "Pick a player:", 80, 30, 220, 0xF);
+		const int kLineH = 12;
+		for (uint i = 0; i < entries.size(); i++) {
+			const byte color = ((int)i == sel) ? 0xF : 0x8;
+			_font.drawString(&scratch, entries[i].label,
+							 80, 60 + (int)i * kLineH, 220, color);
+		}
+		g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
+								   0, 0, 320, 200);
+		g_system->updateScreen();
+	};
+	draw();
+
+	while (!done && !shouldQuit()) {
+		Common::Event ev;
+		bool dirty = false;
+		bool committed = false;
+		while (g_system->getEventManager()->pollEvent(ev)) {
+			if (ev.type == Common::EVENT_QUIT ||
+				ev.type == Common::EVENT_RETURN_TO_LAUNCHER) {
+				_playerName = "Detective";
+				return;
+			}
+			if (ev.type == Common::EVENT_KEYDOWN) {
+				switch (ev.kbd.keycode) {
+				case Common::KEYCODE_UP:
+					sel = (sel + (int)entries.size() - 1) % (int)entries.size();
+					dirty = true;
+					break;
+				case Common::KEYCODE_DOWN:
+					sel = (sel + 1) % (int)entries.size();
+					dirty = true;
+					break;
+				case Common::KEYCODE_RETURN:
+				case Common::KEYCODE_KP_ENTER:
+					committed = true;
+					break;
+				case Common::KEYCODE_ESCAPE:
+					_playerName = "Detective";
+					return;
+				default:
+					break;
+				}
+			}
+			if (ev.type == Common::EVENT_LBUTTONDOWN) {
+				const int kLineH = 12;
+				const int hit = (ev.mouse.y - 60) / kLineH;
+				if (hit >= 0 && hit < (int)entries.size()) {
+					sel = hit;
+					committed = true;
+				}
+			}
+			if (committed)
+				break;
+		}
+		if (committed) {
+			done = true;
+			break;
+		}
+		if (dirty)
+			draw();
+		g_system->updateScreen();
+		g_system->delayMillis(15);
+	}
+
+	const Entry &e = entries[sel];
+	if (e.slot < 0) {
+		doNewPlayer();
+	} else {
+		// Mirrors `_LoadPlayerRecord` at 1c33:1281 ??? slot found,
+		// load it. The save body re-fills `_playerName`, partner,
+		// chain stage, mysteriesSolved.
+		if (!loadProfile(e.label)) {
+			warning("doProfilePicker: failed to load profile '%s' at slot %d",
+					e.label.c_str(), e.slot);
+			doNewPlayer();
+		}
+	}
+}
+
 void EEMEngine::doNewPlayer() {
 	// Mirrors `_NewPlayer` @ 1c33:0dda. The original draws background
 	// 0x104 + character peek pic 0x107, then shows "Please type your
@@ -295,6 +427,10 @@ void EEMEngine::doNewPlayer() {
 					memset(_mysteriesSolved, 0, sizeof(_mysteriesSolved));
 					_mystery.clear();
 					_partner = 0;
+					// `_NewPlayer @ 1c33:0fa3` writes
+					// `DAT_2d5d_3f99 = 1` ??? fresh profiles always
+					// start at the Junior tier.
+					_chainStage = 1;
 					saveProfile(name);
 				}
 				return;
@@ -339,6 +475,395 @@ void EEMEngine::doNewPlayer() {
 	}
 }
 
+void EEMEngine::doShowEnding(uint num) {
+	// Mirrors `_DisplayEnding @ 1df2:0548` + `_DisplayEndingPage @
+	// 1df2:044c`. File format (verified by reading E0.BIN's bytes):
+	//   u16 pageCount
+	//   for each page:
+	//     u16 picNum
+	//     u16 x1, y1, x2, y2  (story rect ??? passed to WordWrap)
+	//     char text[]        (null-terminated, ParseString opcodes)
+	//
+	// The original walks pages with PrevPage / NextPage rects (29be:
+	// 0x..); we approximate with mouse-click / Enter = next, ESC =
+	// exit. ParseString substitutes `\x80` (player name) and the rest
+	// of the 0x80..0x89 placeholder family so the rendered text
+	// reads as the correct player + partner combo.
+	const Common::String fname = Common::String::format("E%u.BIN", num);
+	Common::File f;
+	if (!f.open(Common::Path(fname))) {
+		warning("doShowEnding: %s missing", fname.c_str());
+		return;
+	}
+	const uint32 size = f.size();
+	if (size < 2) {
+		warning("doShowEnding: %s too small (%u bytes)",
+				fname.c_str(), size);
+		return;
+	}
+	Common::Array<byte> buf(size);
+	if (f.read(buf.data(), size) != size) {
+		warning("doShowEnding: %s short read", fname.c_str());
+		return;
+	}
+
+	const uint16 pageCount = READ_LE_UINT16(buf.data());
+	if (pageCount == 0)
+		return;
+
+	// Walk page records. Each page header is 10 bytes; text is
+	// null-terminated and follows the header.
+	uint pageOffsets[8];   // ENDING_RANGE_MAX from `_DisplayEnding`
+	const uint kMaxPages = MIN<uint>(pageCount,
+									 (uint)(sizeof(pageOffsets) / sizeof(uint)));
+	uint cursor = 2;
+	for (uint p = 0; p < kMaxPages; p++) {
+		pageOffsets[p] = cursor;
+		if (cursor + 10 >= size)
+			break;
+		// Skip the 10-byte header and find the null terminator.
+		cursor += 10;
+		while (cursor < size && buf[cursor] != 0)
+			cursor++;
+		cursor++;  // past the null
+	}
+
+	uint pageIdx = 0;
+	bool dirty = true;
+	while (!shouldQuit() && pageIdx < kMaxPages) {
+		if (dirty) {
+			const uint off = pageOffsets[pageIdx];
+			if (off + 10 >= size)
+				break;
+			const uint16 picNum = READ_LE_UINT16(buf.data() + off);
+			const uint16 x1     = READ_LE_UINT16(buf.data() + off + 2);
+			const uint16 y1     = READ_LE_UINT16(buf.data() + off + 4);
+			const uint16 x2     = READ_LE_UINT16(buf.data() + off + 6);
+			(void)READ_LE_UINT16(buf.data() + off + 8);  // y2 (unused ??? WordWrap2 takes width only)
+
+			// Halve the rect coords: ending pages use 320x400 logical
+			// coords (x2=0x128=296, y2=0xa8=168 in our test file
+			// E0.BIN ??? both already 320x200 mode 13h). No conversion.
+			Picture bg;
+			Graphics::ManagedSurface scratch(320, 200,
+				Graphics::PixelFormat::createFormatCLUT8());
+			scratch.clear();
+			if (_picsArchive.getPicture(picNum, bg)) {
+				const int w = MIN<int>(bg.surface.w, 320);
+				const int h = MIN<int>(bg.surface.h, 200);
+				for (int row = 0; row < h; row++)
+					memcpy((byte *)scratch.getBasePtr(0, row),
+						   (const byte *)bg.surface.getBasePtr(0, row), w);
+			}
+
+			// Story text. The bytes are a null-terminated string with
+			// `_ParseString` placeholders (0x80 = player name, 0x82
+			// = partner first name, etc.).
+			const char *raw = (const char *)buf.data() + off + 10;
+			const Common::String text = parseString(raw, _playerName, _partner);
+
+			if (_font.isLoaded() && x2 > x1) {
+				const int textW = MIN<int>((int)x2 - (int)x1, 320 - (int)x1);
+				_font.drawWordWrapped(&scratch, (int)x1, (int)y1,
+									  textW, text, 0xF);
+			}
+
+			// Page indicator at top-right ("page 1/3").
+			if (_font.isLoaded() && kMaxPages > 1) {
+				const Common::String hdr = Common::String::format(
+					"%u/%u", (unsigned)pageIdx + 1, (unsigned)kMaxPages);
+				_font.drawString(&scratch, hdr, 280, 4, 32, 0xF);
+			}
+
+			g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
+									   0, 0, 320, 200);
+			g_system->updateScreen();
+			dirty = false;
+		}
+
+		Common::Event ev;
+		while (g_system->getEventManager()->pollEvent(ev)) {
+			if (ev.type == Common::EVENT_QUIT ||
+				ev.type == Common::EVENT_RETURN_TO_LAUNCHER)
+				return;
+			if (ev.type == Common::EVENT_KEYDOWN) {
+				switch (ev.kbd.keycode) {
+				case Common::KEYCODE_ESCAPE:
+					return;
+				case Common::KEYCODE_LEFT:
+				case Common::KEYCODE_PAGEUP:
+					if (pageIdx > 0) {
+						pageIdx--;
+						dirty = true;
+					}
+					break;
+				case Common::KEYCODE_RIGHT:
+				case Common::KEYCODE_PAGEDOWN:
+				case Common::KEYCODE_RETURN:
+				case Common::KEYCODE_KP_ENTER:
+				case Common::KEYCODE_SPACE:
+					pageIdx++;
+					dirty = true;
+					break;
+				default:
+					break;
+				}
+			}
+			if (ev.type == Common::EVENT_LBUTTONDOWN) {
+				pageIdx++;
+				dirty = true;
+			}
+		}
+		g_system->updateScreen();
+		g_system->delayMillis(15);
+	}
+}
+
+void EEMEngine::doSetup() {
+	// Mirrors `_DoSetup @ 1f78:044e`. Loads BG pic 0x40 +
+	// state-button pics 0x9b/0x9c/0x9d/0x9e. The original wires 13
+	// hot-rects (`_SetupButtons @ 29be:1218`) but only two of them
+	// drive persistent state ??? `Kid1`/`Kid2` (partner) and
+	// `SoundOn`/`SoundOff` (voice flag, `DAT_2d5d_3f97`). The rest
+	// are reset/help/return buttons. We render a minimal text
+	// version: two toggle lines, click to flip, ESC to leave.
+	if (!_font.isLoaded()) {
+		_nextScreen = (ScreenId)_lastScreen;
+		return;
+	}
+
+	const Common::Rect kBackBtn(120, 170, 200, 188);
+	const Common::Rect kPartnerToggle(40, 60, 280, 78);
+	const Common::Rect kVoiceToggle  (40, 90, 280, 108);
+
+	auto draw = [&]() {
+		Graphics::ManagedSurface scratch(320, 200,
+			Graphics::PixelFormat::createFormatCLUT8());
+		scratch.clear();
+		Picture bg;
+		if (_picsArchive.getPicture(0x40, bg)) {
+			const int w = MIN<int>(bg.surface.w, 320);
+			const int h = MIN<int>(bg.surface.h, 200);
+			for (int row = 0; row < h; row++)
+				memcpy((byte *)scratch.getBasePtr(0, row),
+					   (const byte *)bg.surface.getBasePtr(0, row), w);
+		}
+		_font.drawString(&scratch, "Setup", 140, 30, 80, 0xF);
+		const Common::String partnerLine = Common::String::format(
+			"Partner: %s   (click to switch)",
+			_partner == 0 ? "Jake" : "Jenny");
+		_font.drawString(&scratch, partnerLine, 50, 64, 240, 0xF);
+		const Common::String voiceLine = Common::String::format(
+			"Voice:   %s   (click to toggle)",
+			_voiceOn ? "ON" : "OFF");
+		_font.drawString(&scratch, voiceLine, 50, 94, 240, 0xF);
+		_font.drawString(&scratch, "[ Back ]", 130, 174, 80, 0xF);
+		g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
+								   0, 0, 320, 200);
+		g_system->updateScreen();
+	};
+	draw();
+
+	while (!shouldQuit()) {
+		Common::Event ev;
+		bool dirty = false;
+		while (g_system->getEventManager()->pollEvent(ev)) {
+			if (ev.type == Common::EVENT_QUIT ||
+				ev.type == Common::EVENT_RETURN_TO_LAUNCHER) {
+				_nextScreen = kScreenInvalid;
+				return;
+			}
+			if (ev.type == Common::EVENT_KEYDOWN) {
+				if (ev.kbd.keycode == Common::KEYCODE_ESCAPE ||
+					ev.kbd.keycode == Common::KEYCODE_RETURN) {
+					// `_DoSetup @ 1f78:044a` writes `_NextScreen =
+					// _LastScreen` on entry. Returning means we just
+					// dispatch back to whichever screen called us.
+					_nextScreen = (ScreenId)_lastScreen;
+					if (_nextScreen == kScreenSetup ||
+						_nextScreen == kScreenInvalid)
+						_nextScreen = kScreenMap;
+					if (_voiceOn)
+						saveProfile(_playerName);
+					return;
+				}
+			}
+			if (ev.type == Common::EVENT_LBUTTONDOWN) {
+				if (kBackBtn.contains(ev.mouse.x, ev.mouse.y)) {
+					_nextScreen = (ScreenId)_lastScreen;
+					if (_nextScreen == kScreenSetup ||
+						_nextScreen == kScreenInvalid)
+						_nextScreen = kScreenMap;
+					saveProfile(_playerName);
+					return;
+				}
+				if (kPartnerToggle.contains(ev.mouse.x, ev.mouse.y)) {
+					_partner = _partner == 0 ? 1 : 0;
+					dirty = true;
+				}
+				if (kVoiceToggle.contains(ev.mouse.x, ev.mouse.y)) {
+					_voiceOn = !_voiceOn;
+					if (_audio)
+						_audio->setVoiceEnabled(_voiceOn);
+					dirty = true;
+				}
+			}
+		}
+		if (dirty)
+			draw();
+		g_system->updateScreen();
+		g_system->delayMillis(15);
+	}
+}
+
+void EEMEngine::doActionScreen() {
+	// Mirrors `_ActionScreen @ 1c33:195b` ??? the post-mystery menu the
+	// original loops back to after `_DisplayCorrect` (winner). The
+	// original draws PIC 0x122 (Cok), 0x124 (Cexit), then calls
+	// `_DoChoose(ActionNames)` and dispatches the result via the
+	// jumptable at 1c33:1be1 (verified to set `_NextScreen` for each
+	// action ??? entry 1 ??? 10 = CHOOSE_MYSTERY, entry 3 ???
+	// `_ReloadMystery`, etc.).
+	//
+	// We render a minimal text version of the menu ??? the original's
+	// background pic + "Cok" / "Cexit" overlays land once the
+	// `_DoChoose` UI lands. For now: two practical entries ??? "Solve a
+	// Mystery" (the only one whose underlying screen is wired) and
+	// "Quit". Sets `_nextScreen` exactly the same way the original
+	// jumptable does:
+	//   "Solve a Mystery"   ??? kScreenChooseMystery (matches handler 1
+	//                          at 1c33:1add: `MOV [_NextScreen], 0xa`)
+	//   "Quit"              ??? kScreenInvalid (matches the sentinel
+	//                          handler at 1c33:1afa)
+	if (!_font.isLoaded()) {
+		_nextScreen = kScreenChooseMystery;
+		return;
+	}
+
+	enum ActionKind {
+		kActSolve,         // ??? kScreenChooseMystery (action 1, 1c33:1add)
+		kActScrapbook,     // ??? doShowEnding(lastSolved) (action 5, 1c33:1b13)
+		kActQuit
+	};
+	struct Entry {
+		const char *label;
+		ActionKind  kind;
+	};
+	// Locate the highest-numbered solved mystery ??? that's the one
+	// the player most recently completed, and what the action-menu
+	// "Look at My Books" entry replays. If none solved yet, hide
+	// the entry (matches the original's grey-out at 1c33:19f3 where
+	// `local_24[5] = 1` before the chain-stage check toggles it).
+	int lastSolved = -1;
+	for (int i = (int)sizeof(_mysteriesSolved) - 1; i >= 0; i--) {
+		if (_mysteriesSolved[i] != 0) {
+			lastSolved = i;
+			break;
+		}
+	}
+
+	Common::Array<Entry> entries;
+	Entry solveEntry; solveEntry.label = "Solve a Mystery"; solveEntry.kind = kActSolve;
+	entries.push_back(solveEntry);
+	if (lastSolved >= 0) {
+		Entry sb; sb.label = "Look at My Books"; sb.kind = kActScrapbook;
+		entries.push_back(sb);
+	}
+	Entry quitEntry; quitEntry.label = "Quit"; quitEntry.kind = kActQuit;
+	entries.push_back(quitEntry);
+	const int kCount = (int)entries.size();
+
+	int sel = 0;
+	auto draw = [&]() {
+		Graphics::ManagedSurface scratch(320, 200,
+			Graphics::PixelFormat::createFormatCLUT8());
+		scratch.clear();
+		Picture bg;
+		if (_picsArchive.getPicture(0x104, bg)) {
+			const int w = MIN<int>(bg.surface.w, 320);
+			const int h = MIN<int>(bg.surface.h, 200);
+			for (int row = 0; row < h; row++)
+				memcpy((byte *)scratch.getBasePtr(0, row),
+					   (const byte *)bg.surface.getBasePtr(0, row), w);
+		}
+		_font.drawString(&scratch, "What now?", 100, 30, 220, 0xF);
+		for (int i = 0; i < kCount; i++) {
+			const byte color = (i == sel) ? 0xF : 0x8;
+			_font.drawString(&scratch, entries[i].label,
+							 100, 60 + i * 14, 220, color);
+		}
+		g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
+								   0, 0, 320, 200);
+		g_system->updateScreen();
+	};
+	draw();
+
+	while (!shouldQuit()) {
+		Common::Event ev;
+		bool dirty = false;
+		bool committed = false;
+		while (g_system->getEventManager()->pollEvent(ev)) {
+			if (ev.type == Common::EVENT_QUIT ||
+				ev.type == Common::EVENT_RETURN_TO_LAUNCHER) {
+				_nextScreen = kScreenInvalid;
+				return;
+			}
+			if (ev.type == Common::EVENT_KEYDOWN) {
+				switch (ev.kbd.keycode) {
+				case Common::KEYCODE_UP:
+					sel = (sel + kCount - 1) % kCount;
+					dirty = true;
+					break;
+				case Common::KEYCODE_DOWN:
+					sel = (sel + 1) % kCount;
+					dirty = true;
+					break;
+				case Common::KEYCODE_RETURN:
+				case Common::KEYCODE_KP_ENTER:
+					committed = true;
+					break;
+				case Common::KEYCODE_ESCAPE:
+					_nextScreen = kScreenInvalid;
+					return;
+				default:
+					break;
+				}
+			}
+			if (ev.type == Common::EVENT_LBUTTONDOWN) {
+				const int hit = (ev.mouse.y - 60) / 14;
+				if (hit >= 0 && hit < kCount) {
+					sel = hit;
+					committed = true;
+				}
+			}
+			if (committed)
+				break;
+		}
+		if (committed) {
+			switch (entries[sel].kind) {
+			case kActSolve:
+				_nextScreen = kScreenChooseMystery;
+				return;
+			case kActScrapbook:
+				if (lastSolved >= 0)
+					doShowEnding((uint)lastSolved);
+				// Fall through to redraw the menu after viewing.
+				draw();
+				continue;
+			case kActQuit:
+				_nextScreen = kScreenInvalid;
+				return;
+			}
+			return;
+		}
+		if (dirty)
+			draw();
+		g_system->updateScreen();
+		g_system->delayMillis(15);
+	}
+	_nextScreen = kScreenInvalid;
+}
+
 void EEMEngine::doCaseSelection() {
 	// Mirrors `_CaseSelection` @ 1c33:0a87. The original draws PIC 0x41
 	// (chooser background) plus a centred "Book %d" / "Challenge Book"
@@ -416,6 +941,7 @@ void EEMEngine::doCaseSelection() {
 	v.haveCaseBg = haveCaseBg;
 	v.kdAnim = &kdAnim;
 	v.haveKdAnim = haveKdAnim;
+	v.kdAnimId = (uint16)kKdAniId;
 	v.kdAnimX = kKdAnimX;
 	v.kdAnimY = kKdAnimY;
 	v.separator = kSeparator;
@@ -548,11 +1074,25 @@ void EEMEngine::doCaseSelection() {
 	}
 
 	// "Choose A Mystery" sub-screen: pick a specific case from the
-	// 55-mystery roster. The original opens a different list here;
-	// we approximate with the tier-aware numeric chooser we used
-	// before. Default to the first unsolved mystery.
-	uint sel = 0;
-	for (uint i = 0; i <= kMaxMystery; i++) {
+	// 55-mystery roster. `_CaseSelection @ 1c33:0a87` only shows
+	// mysteries in the player's current chain stage:
+	//   stage 1 (Junior) ??? 1..24    (start = `iVar1 = 1`     @ 1c33:0aff)
+	//   stage 2 (Senior) ??? 25..48   (start = `iVar1 = 0x19`)
+	//   stage 3 (Master) ??? 49..54   (start = `iVar1 = 0x31`)
+	// The original passes `&DAT_2d5d_3f9b + iVar1` as the grey-mask
+	// pointer so DoChoose lights up only the tier-relevant entries.
+	// We clamp `sel` to the tier range and pre-seed it to the first
+	// unsolved case in that range.
+	uint stageLo = 1, stageHi = 0x18;
+	switch (_chainStage) {
+	case 2: stageLo = 0x19; stageHi = 0x30; break;
+	case 3: stageLo = 0x31; stageHi = 0x36; break;
+	default: break;  // stage 1 (or fallback)
+	}
+	if (stageHi > kMaxMystery)
+		stageHi = kMaxMystery;
+	uint sel = stageLo;
+	for (uint i = stageLo; i <= stageHi; i++) {
 		if (i < sizeof(_mysteriesSolved) && !_mysteriesSolved[i]) {
 			sel = i;
 			break;
@@ -587,13 +1127,13 @@ void EEMEngine::doCaseSelection() {
 					return;
 				}
 				if (kUpArrowRect.contains(ev.mouse.x, ev.mouse.y)) {
-					sel = (sel == 0) ? kMaxMystery : sel - 1;
+					sel = (sel <= stageLo) ? stageHi : sel - 1;
 					sv.sel = sel;
 					drawCaseSubmenu(sv);
 					continue;
 				}
 				if (kDnArrowRect.contains(ev.mouse.x, ev.mouse.y)) {
-					sel = (sel >= kMaxMystery) ? 0 : sel + 1;
+					sel = (sel >= stageHi) ? stageLo : sel + 1;
 					sv.sel = sel;
 					drawCaseSubmenu(sv);
 					continue;
@@ -635,31 +1175,31 @@ void EEMEngine::doCaseSelection() {
 				continue;
 			}
 			if (k == Common::KEYCODE_DOWN || k == Common::KEYCODE_TAB) {
-				sel = (sel >= kMaxMystery) ? 0 : sel + 1;
+				sel = (sel >= stageHi) ? stageLo : sel + 1;
 				sv.sel = sel;
 				drawCaseSubmenu(sv);
 				continue;
 			}
 			if (k == Common::KEYCODE_UP) {
-				sel = (sel == 0) ? kMaxMystery : sel - 1;
+				sel = (sel <= stageLo) ? stageHi : sel - 1;
 				sv.sel = sel;
 				drawCaseSubmenu(sv);
 				continue;
 			}
 			if (k == Common::KEYCODE_PAGEDOWN) {
-				sel = (sel + 10 > kMaxMystery) ? kMaxMystery : sel + 10;
+				sel = (sel + 10 > stageHi) ? stageHi : sel + 10;
 				sv.sel = sel;
 				drawCaseSubmenu(sv);
 				continue;
 			}
 			if (k == Common::KEYCODE_PAGEUP) {
-				sel = (sel < 10) ? 0 : sel - 10;
+				sel = (sel < stageLo + 10) ? stageLo : sel - 10;
 				sv.sel = sel;
 				drawCaseSubmenu(sv);
 				continue;
 			}
-			if (k == Common::KEYCODE_HOME) { sel = 0; sv.sel = sel; drawCaseSubmenu(sv); continue; }
-			if (k == Common::KEYCODE_END)  { sel = kMaxMystery; sv.sel = sel; drawCaseSubmenu(sv); continue; }
+			if (k == Common::KEYCODE_HOME) { sel = stageLo; sv.sel = sel; drawCaseSubmenu(sv); continue; }
+			if (k == Common::KEYCODE_END)  { sel = stageHi; sv.sel = sel; drawCaseSubmenu(sv); continue; }
 		}
 		g_system->updateScreen();
 		g_system->delayMillis(15);
@@ -871,28 +1411,25 @@ void EEMEngine::drawNotebookFrame(int &page) {
 				   (const byte *)frame.surface.getBasePtr(0, row), w);
 	}
 
-	// Partner sprite at (5, 80). Anim 1 for Jake, 0xb (11) for Jenny.
+	// Partner sprite at (5, 80). Anim 1 for Jake, 0xb (11) for Jenny
+	// for CELLS, but the original `_DoNotebook @ 161e:0500` always
+	// uses script 0x01 (verified by `CONCAT22(1, ...)` in its
+	// `_NewAnimation` call at 161e:054c). Both 0x01 and 0x0b have
+	// the SAME script in `kAnimScripts` (alias), so both lookups
+	// produce identical results ??? but routing through 0x01
+	// matches the original verbatim.
 	const uint partnerAnim = (_partner == 0) ? 1 : 0xb;
 	Animation partnerAni;
 	if (_aniArchive.loadAnimation(partnerAnim, partnerAni) && !partnerAni.empty()) {
 		const uint32 now = g_system->getMillis();
-		const uint frameIdx = (uint)((now / 100) % partnerAni.size());
-		const Picture &fr = partnerAni[frameIdx];
-		const byte transp = (byte)(fr.flags >> 8);
-		for (int row = 0; row < fr.surface.h; row++) {
-			const int dstY = 80 + row;
-			if (dstY < 0 || dstY >= 200)
-				continue;
-			const byte *src = (const byte *)fr.surface.getBasePtr(0, row);
-			byte *dst = (byte *)scratch.getBasePtr(0, dstY);
-			for (int col = 0; col < fr.surface.w; col++) {
-				const int dstX = 5 + col;
-				if (dstX < 0 || dstX >= 320)
-					continue;
-				if (src[col] != transp)
-					dst[dstX] = src[col];
-			}
-		}
+		const uint frameIdx = partnerFrameAtTick(0x01,
+												  (uint)partnerAni.size(), now);
+		// Anchor-aware blit. The PDA partner (anim 0x01/0x0b) cells
+		// have miscflags = rowoff = 0 in the audit, but routing
+		// through `blitAnimFrameAnchored` is harmless and keeps the
+		// rendering path consistent with the BigMap partner.
+		blitAnimFrameAnchored(scratch.surfacePtr(),
+							  partnerAni[frameIdx], 5, 80);
 	}
 
 	// Notes ??? `_DrawNotes` walks `_NoteIndex` for the current page,
@@ -1314,28 +1851,23 @@ void EEMEngine::drawGalleryFrame(const byte *gd, uint8 numSuspects,
 		}
 	}
 
-	// Partner sprite frame @ (5, 0x50).
+	// Partner sprite frame @ (5, 0x50). The original `_DoGallery @
+	// 158f:065b` registers `_NewAnimation(..., CONCAT22(2, ...), ...)`
+	// ??? script key 0x02 regardless of partner. Jake's 0x02 script
+	// (26 frames, brief wave + long hold + second wave) is what
+	// drives BOTH partners' cells. Earlier our port used 0x10 for
+	// Jenny, which is a 9-frame short blip ??? so Jenny was missing
+	// 17 frames of the wave-and-pause cadence that Jake has.
 	if (havePartner) {
 		const uint32 now = g_system->getMillis();
-		const uint frameIdx = (uint)((now / 100) % partnerAni.size());
-		const Picture &fr = partnerAni[frameIdx];
-		const byte transp = (byte)(fr.flags >> 8);
-		const int px = 5;
-		const int py = 0x50;
-		for (int row = 0; row < fr.surface.h; row++) {
-			const int dstY = py + row;
-			if (dstY < 0 || dstY >= 200)
-				continue;
-			const byte *src = (const byte *)fr.surface.getBasePtr(0, row);
-			byte *dst = (byte *)scratch.getBasePtr(0, dstY);
-			for (int col = 0; col < fr.surface.w; col++) {
-				const int dstX = px + col;
-				if (dstX < 0 || dstX >= 320)
-					continue;
-				if (src[col] != transp)
-					dst[dstX] = src[col];
-			}
-		}
+		const uint frameIdx = partnerFrameAtTick(0x02,
+												  (uint)partnerAni.size(), now);
+		// Anchor-aware blit, consistent with site-loop / BigMap
+		// rendering paths. Anim 0x02 has rowoff = miscflags = 0
+		// per the audit but the anchored blitter is still the
+		// right semantic for an `_NewAnimation`-rendered sprite.
+		blitAnimFrameAnchored(scratch.surfacePtr(),
+							  partnerAni[frameIdx], 5, 0x50);
 	}
 
 	// Portraits ??? `_DrawGallery @ 158f:0046` walks suspects 0..N-1 and
@@ -1470,12 +2002,13 @@ void EEMEngine::doBigMap() {
 				return;
 			if (ev.type == Common::EVENT_LBUTTONDOWN) {
 				// SetupButtonRect ??? `_NextScreen = 6` (the original's
-				// settings screen). We use it as "back to menu":
-				// abandon the current mystery and return to case
-				// selection.
+				// settings screen, mirrors `_DoBigMap @ 20fe:0c33`
+				// where it pushes `_PressButton` then writes
+				// `_NextScreen = 6`). Now wired to the actual
+				// `doSetup` handler instead of dropping the player
+				// out to the launcher.
 				if (kSetupBtnRect.contains(ev.mouse.x, ev.mouse.y)) {
-					_mystery.clear();
-					_nextScreen = kScreenInvalid;
+					_nextScreen = kScreenSetup;
 					return;
 				}
 				// Click in the BigMapWindow ??? zoom. Original formula:
@@ -1753,30 +2286,24 @@ void EEMEngine::drawBigMapOverview() {
 		}
 	}
 
-	// Partner idle sprite at (0xfd, 0x50). Jake = anim 0x14, Jenny = 0x12.
+	// Partner idle sprite at (0xfd, 0x50). Jake = anim 0x14, Jenny = 0x12
+	// for the loaded CELLS, but the original `_DoBigMap @ 20fe:0a47`
+	// always passes `CONCAT22(0x14, ...)` to `_NewAnimation` so the
+	// SCRIPT key is 0x14 (`[0..8]` count-up) regardless of partner.
+	// Without this, Jenny was running 0x12's count-DOWN script
+	// `[8..0]` over her cells ??? visually backwards from the original.
 	const uint kMapAniId = (_partner == 0) ? 0x14 : 0x12;
 	Animation mapAnim;
 	if (_aniArchive.loadAnimation(kMapAniId, mapAnim) && !mapAnim.empty()) {
 		const uint32 now = g_system->getMillis();
-		const uint frameIdx = (uint)((now / 100) % mapAnim.size());
-		const Picture &fr = mapAnim[frameIdx];
-		const byte transp = (byte)(fr.flags >> 8);
-		const int kMapAnimX = 0xfd;
-		const int kMapAnimY = 0x50;
-		for (int row = 0; row < fr.surface.h; row++) {
-			const int dstY = kMapAnimY + row;
-			if (dstY < 0 || dstY >= 200)
-				continue;
-			const byte *src = (const byte *)fr.surface.getBasePtr(0, row);
-			byte *dst = (byte *)scratch.getBasePtr(0, dstY);
-			for (int col = 0; col < fr.surface.w; col++) {
-				const int dstX = kMapAnimX + col;
-				if (dstX < 0 || dstX >= 320)
-					continue;
-				if (src[col] != transp)
-					dst[dstX] = src[col];
-			}
-		}
+		const uint frameIdx = partnerFrameAtTick(0x14,
+												  (uint)mapAnim.size(), now);
+		// Anchor-aware: the BigMap walk-cycle has miscflags = -2 per
+		// cell, so the partner shifts left as it cycles ??? without the
+		// anchor adjustment the sprite "shakes in place" instead of
+		// walking forward.
+		blitAnimFrameAnchored(scratch.surfacePtr(), mapAnim[frameIdx],
+							  0xfd, 0x50);
 	}
 
 	g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
@@ -1856,31 +2383,20 @@ void EEMEngine::drawBigMapDetail(int scrollX, int scrollY,
 	}
 
 	// Partner sprite on the detail map (drawn last to sit over the
-	// frame and the BIGMAP.PIC viewport).
+	// frame and the BIGMAP.PIC viewport). The original always passes
+	// `CONCAT22(0x13, ...)` to `_NewAnimation` (i.e. script ID 0x13)
+	// regardless of partner ??? verified at `_DoBigMap @ 20fe:0a47`.
+	// So we look up script 0x13 for both partners while still
+	// loading the partner-specific CELLS via `kDetailAniId`.
 	const uint kDetailAniId = (_partner == 0) ? 0x13 : 0x11;
 	Animation detailAnim;
 	if (_aniArchive.loadAnimation(kDetailAniId, detailAnim) &&
 		!detailAnim.empty()) {
 		const uint32 now = g_system->getMillis();
-		const uint frameIdx = (uint)((now / 100) % detailAnim.size());
-		const Picture &fr = detailAnim[frameIdx];
-		const byte transp = (byte)(fr.flags >> 8);
-		const int kDetailAnimX = 0x101;
-		const int kDetailAnimY = 0x50;
-		for (int row = 0; row < fr.surface.h; row++) {
-			const int dstY = kDetailAnimY + row;
-			if (dstY < 0 || dstY >= 200)
-				continue;
-			const byte *src = (const byte *)fr.surface.getBasePtr(0, row);
-			byte *dst = (byte *)scratch.getBasePtr(0, dstY);
-			for (int col = 0; col < fr.surface.w; col++) {
-				const int dstX = kDetailAnimX + col;
-				if (dstX < 0 || dstX >= 320)
-					continue;
-				if (src[col] != transp)
-					dst[dstX] = src[col];
-			}
-		}
+		const uint frameIdx = partnerFrameAtTick(0x13,
+												  (uint)detailAnim.size(), now);
+		blitAnimFrameAnchored(scratch.surfacePtr(),
+							  detailAnim[frameIdx], 0x101, 0x50);
 	}
 
 	g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
@@ -2269,6 +2785,37 @@ void EEMEngine::doAccuse() {
 			_mysteriesSolved[mn] = _mystery._firstTry ? 2 : 1;
 		}
 
+		// Mirrors the chain-advancement loop at `_DisplayCorrect @
+		// 1df2:0824-0850`. Skip mystery 0 (the practice case) per the
+		// `if (_MysteryNumber != 0)` guard at 1df2:080d, then check
+		// every mystery in the current tier:
+		//   stage 1 ??? check  1..0x18 (24 mysteries, "A chain")
+		//   stage 2 ??? check 0x19..0x30 (24 mysteries, "B chain")
+		//   stage 3 ??? check 0x31..0x36 (6 mysteries, "C chain")
+		// If every solve flag in that range is non-zero, bump the
+		// stage. Original increments unconditionally (3???4 is harmless
+		// since no further range covers it); we cap at 3 for clarity.
+		if (mn != 0) {
+			uint lo = 0, hi = 0;
+			switch (_chainStage) {
+			case 1: lo = 1;    hi = 0x18; break;
+			case 2: lo = 0x19; hi = 0x30; break;
+			case 3: lo = 0x31; hi = 0x36; break;
+			default: break;
+			}
+			bool allSolved = (hi >= lo);
+			for (uint i = lo; i <= hi && allSolved; i++) {
+				if (i >= sizeof(_mysteriesSolved) || _mysteriesSolved[i] == 0)
+					allSolved = false;
+			}
+			if (allSolved && _chainStage < 3) {
+				_chainStage++;
+				debugC(1, kDebugMystery,
+					   "chainStage advanced to %u after solving mystery %u",
+					   _chainStage, mn);
+			}
+		}
+
 		// `_DisplayCorrect @ 1df2:073c` calls `_MIDIPlay(5)` (1df2:0789)
 		// before `_DifferenceAnimation("scrapbk.ani")` to swap from the
 		// travel music to the winner cue.
@@ -2276,6 +2823,17 @@ void EEMEngine::doAccuse() {
 			_music->playMus(5, /*loop=*/false);
 		playAnm(Common::Path("SCRAPBK.ANI"), 120, true);
 
+		// `_DisplayCorrect @ 1df2:07ac` then calls `_DisplayClue`
+		// against the briefing's winning ClueBlock at
+		// `_Mystery + _MysteryIndex[0x10]` BEFORE `_ShowOneScrap`.
+		// We don't render that intermediate ClueBlock yet (it ties
+		// into the chain-A/B/C result selection); skip straight to
+		// the per-mystery ending pages ??? the same screen
+		// `_ShowOneScrap @ 1f78:0773` displays via
+		// `_DisplayEnding(num, 1)`. Players see the per-mystery
+		// resolution text on top of the ending pic.
+		doShowEnding(mn);
+
 		// Mirrors `_SavePlayerRecord` at 1df2:0857 ??? once the
 		// `_mysteriesSolved` table is updated, the original
 		// immediately persists the player record so the win sticks
@@ -2287,8 +2845,22 @@ void EEMEngine::doAccuse() {
 		if (err.getCode() != Common::kNoError)
 			warning("saveProfile after solve failed: %s",
 					err.getDesc().c_str());
+
+		// `_DisplayCorrect @ 1df2:0895` writes `_NextScreen = 0xc`
+		// ??? the winner returns to the post-mystery `_ActionScreen`.
+		// Free the mystery first so the loop can break out cleanly:
+		// `_DeleteSavedGame` at 1df2:0851 + `_FreeMystery` at
+		// 1df2:08a4 do the same.
+		_mystery.clear();
+		_nextScreen = kScreenAction;
 	} else {
 		_mystery._firstTry = false;
+		// `_DisplayAlibi @ 1df2:043f` writes `_NextScreen =
+		// _LastScreen` ??? drop back to wherever the player accused
+		// from. With no mystery state to unload, the site loop
+		// resumes naturally.
+		_nextScreen = _lastScreen != kScreenInvalid
+						? (ScreenId)_lastScreen : kScreenSite;
 	}
 }
 


Commit: 9c88ee5bc94b0f42d1f1edc32ced135c61533386
    https://github.com/scummvm/scummvm/commit/9c88ee5bc94b0f42d1f1edc32ced135c61533386
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:20:42+02:00

Commit Message:
EEM: fixed the map animation

Changed paths:
    engines/eem/eem.h
    engines/eem/site.cpp
    engines/eem/site.h
    engines/eem/ui.cpp


diff --git a/engines/eem/eem.h b/engines/eem/eem.h
index e97bd592712..af19e0c43fc 100644
--- a/engines/eem/eem.h
+++ b/engines/eem/eem.h
@@ -252,10 +252,11 @@ private:
 	void drawGalleryFrame(const byte *gd, uint8 numSuspects,
 						  Common::Array<Common::Rect> &slotRects,
 						  Common::Array<int> &slotSuspect);
-	void drawBigMapOverview();
+	void drawBigMapOverview(uint32 elapsedMs);
 	void drawBigMapDetail(int scrollX, int scrollY,
 						  const Common::Array<byte> &mapPixels,
-						  uint16 mapW, uint16 mapH);
+						  uint16 mapW, uint16 mapH,
+						  uint32 elapsedMs);
 	void drawAccuseGallery(uint8 numSuspects, const byte *gd,
 						   int highlighted,
 						   Common::Array<Common::Rect> &slotRects,
diff --git a/engines/eem/site.cpp b/engines/eem/site.cpp
index 42c5429d44e..54d3673f501 100644
--- a/engines/eem/site.cpp
+++ b/engines/eem/site.cpp
@@ -457,6 +457,49 @@ uint partnerFrameAtTick(uint16 seqnum, uint numFrames, uint32 tickMs) {
 	return (numFrames > 0) ? MIN<uint>(frame, numFrames - 1) : 0;
 }
 
+// Generic "play `unfold` once, then loop `waitSeq` forever" walker.
+// Mirrors the original's slot-script-swap idiom: the entrance script
+// runs to its 0x80 terminator, then the slot's script pointer is
+// rewritten to a looping wait sequence (e.g. `_BigMapWaitSeq @
+// 29be:1574`, `_SmallMapWaitSeq @ 29be:1548`). `partnerFrameAtTick`
+// can't model that swap on its own (it always wraps on the same
+// script), hence this helper.
+static uint oneShotThenLoopFrameAtTick(const uint8 *unfold, uint unfoldLen,
+									   const uint8 *waitSeq, uint waitSeqLen,
+									   uint numFrames, uint32 elapsedMs) {
+	const uint kFramePeriodMs = 100;
+	const uint tick = elapsedMs / kFramePeriodMs;
+	const uint frame = (tick < unfoldLen)
+		? unfold[tick]
+		: waitSeq[(tick - unfoldLen) % waitSeqLen];
+	return (numFrames > 0) ? MIN<uint>(frame, numFrames - 1) : 0;
+}
+
+uint bigMapPartnerFrameAtTick(uint numFrames, uint32 elapsedMs) {
+	// Script 0x14 @ 29be:196a (count-up 0..8, 0x80) ??? on terminator,
+	// `_DoBigMap` swaps to `_BigMapWaitSeq` @ 29be:1574
+	// (9,9,9,9,10,9,9,9,9, 0x80) ??? open-map hold with a fidget.
+	static const uint8 kUnfold[]  = { 0, 1, 2, 3, 4, 5, 6, 7, 8 };
+	static const uint8 kWaitSeq[] = { 9, 9, 9, 9, 10, 9, 9, 9, 9 };
+	return oneShotThenLoopFrameAtTick(kUnfold, ARRAYSIZE(kUnfold),
+									  kWaitSeq, ARRAYSIZE(kWaitSeq),
+									  numFrames, elapsedMs);
+}
+
+uint bigMapDetailPartnerFrameAtTick(uint numFrames, uint32 elapsedMs) {
+	// Script 0x13 @ 29be:1992 (count-up 0..7, 0x80) ??? on terminator,
+	// `_DoMapScreen @ 20fe:1390` swaps to `_SmallMapWaitSeq` @ 29be:1548
+	// (18 entries: hold cell 7 with a single cell-10 fidget) ??? fidget
+	// every ~1.8 s.
+	static const uint8 kUnfold[]  = { 0, 1, 2, 3, 4, 5, 6, 7 };
+	static const uint8 kWaitSeq[] = {
+		7, 7, 7, 10, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7
+	};
+	return oneShotThenLoopFrameAtTick(kUnfold, ARRAYSIZE(kUnfold),
+									  kWaitSeq, ARRAYSIZE(kWaitSeq),
+									  numFrames, elapsedMs);
+}
+
 void SiteScreen::enter(uint siteNum) {
 	if (!_mystery || !_mystery->isLoaded()) {
 		warning("SiteScreen::enter: no mystery loaded");
diff --git a/engines/eem/site.h b/engines/eem/site.h
index 51129bfed74..33f6f23ab55 100644
--- a/engines/eem/site.h
+++ b/engines/eem/site.h
@@ -44,6 +44,26 @@ class Mystery;
 /// values that point past the asset.
 uint partnerFrameAtTick(uint16 seqnum, uint numFrames, uint32 tickMs);
 
+/// BigMap-overview partner frame: plays the count-up 0..8 once (the
+/// "unfold the map" pose), then loops the `_BigMapWaitSeq` hold
+/// (9,9,9,9,10,9,9,9,9). Mirrors `_DoBigMap @ 20fe:09e7`'s two-phase
+/// dispatch ??? the slot starts on script 0x14 (count-up @ 29be:196a)
+/// and on the 0x80 terminator the slot's script pointer is rewritten
+/// to `_BigMapWaitSeq @ 29be:1574`. `partnerFrameAtTick` can't model
+/// that swap on its own (it always loops), so without this helper the
+/// unfold cycles forever instead of resting on the open-map pose.
+/// `elapsedMs` is the time since the BigMap was opened.
+uint bigMapPartnerFrameAtTick(uint numFrames, uint32 elapsedMs);
+
+/// BigMap-detail (zoomed view) partner frame. Same two-phase shape as
+/// `bigMapPartnerFrameAtTick`, but the original `_DoMapScreen @ 20fe:120b`
+/// uses script 0x13 (count-up 0..7 @ 29be:1992) for the unfold and
+/// swaps the slot pointer to `_SmallMapWaitSeq @ 29be:1548` (an
+/// 18-frame hold of cell 7 with a single cell-10 fidget) on the
+/// terminator (`MOV [BX+0x789f],0x1548` at 20fe:1390). `elapsedMs`
+/// is the time since the detail screen was opened.
+uint bigMapDetailPartnerFrameAtTick(uint numFrames, uint32 elapsedMs);
+
 /// Anchor-aware masked blit. Mirrors the per-frame anchor offset
 /// math in `_UpdateAnimations @ 172b:09c1`:
 /// `blit_x = anchor_x - frame.miscflags`, `blit_y = anchor_y -
diff --git a/engines/eem/ui.cpp b/engines/eem/ui.cpp
index 55554a7ff30..7d8666b36ca 100644
--- a/engines/eem/ui.cpp
+++ b/engines/eem/ui.cpp
@@ -1980,8 +1980,15 @@ void EEMEngine::doBigMap() {
 	// STAGE 1 ??? Overview: PIC 0x42 + clickable site icons.
 	// ------------------------------------------------------------------
 
-	drawBigMapOverview();
-	uint32 mapLastTick = g_system->getMillis();
+	// Anchor for the partner-sprite timeline. `_DoBigMap`'s
+	// `_NewAnimation` call seeds the slot's frame index to 0xffff so
+	// the first `_UpdateAnimations` tick starts at script[0]; we mirror
+	// that by passing elapsed-since-open (zero on the first paint) into
+	// `bigMapPartnerFrameAtTick`, which plays the unfold once and then
+	// loops the wait sequence.
+	const uint32 mapStartTick = g_system->getMillis();
+	drawBigMapOverview(0);
+	uint32 mapLastTick = mapStartTick;
 
 	// Static rectangles read directly from the binary at the labelled
 	// addresses (29be:0x1596 onwards). Format is {x1, y1, x2, y2}.
@@ -2032,7 +2039,7 @@ void EEMEngine::doBigMap() {
 		const uint32 now = g_system->getMillis();
 		if (now - mapLastTick >= 100) {
 			mapLastTick = now;
-			drawBigMapOverview();
+			drawBigMapOverview(now - mapStartTick);
 		}
 		g_system->updateScreen();
 		g_system->delayMillis(10);
@@ -2069,8 +2076,12 @@ void EEMEngine::doBigMap() {
 	int scrollX = MAX<int>(0, MIN<int>(mapW - kMapWinW, zoomX));
 	int scrollY = MAX<int>(0, MIN<int>(mapH - kMapWinH, zoomY));
 
-	drawBigMapDetail(scrollX, scrollY, mapPixels, mapW, mapH);
-	uint32 detailLastTick = g_system->getMillis();
+	// Anchor the detail-screen partner timeline (mirrors `_DoMapScreen`'s
+	// `_NewAnimation` seeding the slot's frame index to 0xffff). The
+	// unfold (script 0x13) plays once, then `_SmallMapWaitSeq` loops.
+	const uint32 detailStartTick = g_system->getMillis();
+	drawBigMapDetail(scrollX, scrollY, mapPixels, mapW, mapH, 0);
+	uint32 detailLastTick = detailStartTick;
 
 	while (!shouldQuit()) {
 		Common::Event ev;
@@ -2201,13 +2212,14 @@ void EEMEngine::doBigMap() {
 			dirty = true;
 		}
 		if (dirty)
-			drawBigMapDetail(scrollX, scrollY, mapPixels, mapW, mapH);
+			drawBigMapDetail(scrollX, scrollY, mapPixels, mapW, mapH,
+							 now - detailStartTick);
 		g_system->updateScreen();
 		g_system->delayMillis(10);
 	}
 }
 
-void EEMEngine::drawBigMapOverview() {
+void EEMEngine::drawBigMapOverview(uint32 elapsedMs) {
 	// Map-overview redraw ??? formerly the `drawOverview` lambda inside
 	// `doBigMap`. PIC 0x42 frame + per-site marker (Done / Crime / Site
 	// per `_DrawBigMapButtons @ 20fe:0877`) + the partner idle sprite.
@@ -2217,6 +2229,11 @@ void EEMEngine::drawBigMapOverview() {
 	// at (0xfd, 0x50). We don't track LastScreen finely enough so we
 	// always render the IDLE pose at (0xfd, 0x50). Idle anim ID:
 	// Jake = 0x14 (20), Jenny = 0x12 (18).
+	//
+	// `elapsedMs` is the time since `doBigMap` opened ??? the partner-sprite
+	// timeline anchor. `bigMapPartnerFrameAtTick` uses it to play the
+	// unfold script (0..8) once, then loop `_BigMapWaitSeq` (the open-map
+	// hold). Without that anchor the unfold would loop indefinitely.
 	Graphics::ManagedSurface scratch(320, 200,
 		Graphics::PixelFormat::createFormatCLUT8());
 	scratch.clear();
@@ -2295,9 +2312,8 @@ void EEMEngine::drawBigMapOverview() {
 	const uint kMapAniId = (_partner == 0) ? 0x14 : 0x12;
 	Animation mapAnim;
 	if (_aniArchive.loadAnimation(kMapAniId, mapAnim) && !mapAnim.empty()) {
-		const uint32 now = g_system->getMillis();
-		const uint frameIdx = partnerFrameAtTick(0x14,
-												  (uint)mapAnim.size(), now);
+		const uint frameIdx = bigMapPartnerFrameAtTick((uint)mapAnim.size(),
+													   elapsedMs);
 		// Anchor-aware: the BigMap walk-cycle has miscflags = -2 per
 		// cell, so the partner shifts left as it cycles ??? without the
 		// anchor adjustment the sprite "shakes in place" instead of
@@ -2313,12 +2329,17 @@ void EEMEngine::drawBigMapOverview() {
 
 void EEMEngine::drawBigMapDetail(int scrollX, int scrollY,
 								 const Common::Array<byte> &mapPixels,
-								 uint16 mapW, uint16 mapH) {
+								 uint16 mapW, uint16 mapH,
+								 uint32 elapsedMs) {
 	// Map-detail redraw ??? formerly the `drawDetail` lambda inside
 	// `doBigMap`. PIC 0x43 frame + a 0xe9 ?? 0xab BIGMAP.PIC viewport at
 	// (2, 2), stamped site buttons, and the partner sprite at (0x101,
 	// 0x50) ??? `_DoMapScreen @ 20fe:120b` (`_NewAnimation` at
 	// 20fe:12cd-12f0, anim 0x13 Jake / 0x11 Jenny, seqnum 0x13).
+	//
+	// `elapsedMs` is the time since the detail screen was opened ???
+	// `bigMapDetailPartnerFrameAtTick` uses it to play the unfold once
+	// and then loop `_SmallMapWaitSeq`.
 	const int kMapWinW = 0xe9;
 	const int kMapWinH = 0xab;
 	const int kMapWinX = 2;
@@ -2392,9 +2413,8 @@ void EEMEngine::drawBigMapDetail(int scrollX, int scrollY,
 	Animation detailAnim;
 	if (_aniArchive.loadAnimation(kDetailAniId, detailAnim) &&
 		!detailAnim.empty()) {
-		const uint32 now = g_system->getMillis();
-		const uint frameIdx = partnerFrameAtTick(0x13,
-												  (uint)detailAnim.size(), now);
+		const uint frameIdx = bigMapDetailPartnerFrameAtTick(
+				(uint)detailAnim.size(), elapsedMs);
 		blitAnimFrameAnchored(scratch.surfacePtr(),
 							  detailAnim[frameIdx], 0x101, 0x50);
 	}


Commit: ef46d69023cf56dd56aeef76f538ba43d8fcef99
    https://github.com/scummvm/scummvm/commit/ef46d69023cf56dd56aeef76f538ba43d8fcef99
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:20:43+02:00

Commit Message:
EEM: fixed missing frames in partner animations

Changed paths:
    engines/eem/clues.cpp
    engines/eem/site.cpp


diff --git a/engines/eem/clues.cpp b/engines/eem/clues.cpp
index 1ad73525d3d..d2635e3823d 100644
--- a/engines/eem/clues.cpp
+++ b/engines/eem/clues.cpp
@@ -601,8 +601,22 @@ void EEMEngine::displayClue(const byte *clueBlock) {
 		// This is what surfaces "Jenny takes a picture with a camera"
 		// (and the matching Jake gestures) during NPC searches.
 		const int16 kdAnimNum = (int16)READ_LE_UINT16(c + 0x3a);
-		if (kdAnimNum != -1)
+		if (kdAnimNum != -1) {
 			playKdAnim((uint16)kdAnimNum);
+			// `playKdAnim` leaves the screen on the partner-less
+			// `_partnerEraseBg`, mirroring its between-frame erase
+			// source. Re-stamp `bg` so the partner sprite captured at
+			// `displayClue` entry returns before we draw the speaker
+			// portrait + balloon. Without this the bubble lands on a
+			// partner-less screen and the partner appears invisible
+			// for the entire first iteration. Mirrors the original's
+			// `_UpdateAnimations @ 172b:09c1` swap on `0x80`: the
+			// kdAnim slot is freed and the WaitHandle (partner) slot
+			// is reactivated with frame index 0xffff, so on the next
+			// tick the partner renders at script[0] of its wait anim.
+			g_system->copyRectToScreen(bg.getPixels(), bg.pitch,
+									   0, 0, 320, 200);
+		}
 
 		const bool useP1 = (_partner == 1) &&
 			(READ_LE_UINT16(c + 10) != 0xFFFF);
diff --git a/engines/eem/site.cpp b/engines/eem/site.cpp
index 54d3673f501..062f81f04ba 100644
--- a/engines/eem/site.cpp
+++ b/engines/eem/site.cpp
@@ -894,6 +894,13 @@ void SiteScreen::enterSiteAnim() {
 	// frame-by-frame anchors decrease as the animation progresses):
 	//   destX = -frame.miscflags    (on-disk byte 8 = anchor X)
 	//   destY = kKDY - frame.rowoff (on-disk byte 6 = anchor Y)
+	// Both anchors are SIGNED int16 (mirrors `blitAnimFrameAnchored` /
+	// `_UpdateAnimations @ 172b:09c1`). The original's `-pPVar8->width`
+	// negation wraps in 16-bit on DOS, so a frame with `miscflags = -2`
+	// (0xFFFE) lands at destX = +2 in the original. Without the int16
+	// re-cast our 32-bit negation produces destX = -65534 and the
+	// second-to-last frame (which has a non-zero anchor in anim 7/0xf)
+	// clips entirely off-screen, leaving a one-tick partner-less gap.
 	// Each frame waits one `_CheckFrameRate` tick ??? we use 80 ms which
 	// matches the original's ~12 FPS pacing.
 	Animation kd;
@@ -903,8 +910,8 @@ void SiteScreen::enterSiteAnim() {
 			 frameIdx++) {
 			const Picture &fr = kd[frameIdx];
 			const byte transp = (byte)(fr.flags >> 8);
-			const int destX = -(int)fr.miscflags;
-			const int destY = kKDY - (int)fr.rowoff;
+			const int destX = -(int)(int16)fr.miscflags;
+			const int destY = kKDY - (int)(int16)fr.rowoff;
 
 			Graphics::ManagedSurface scratch(320, 200,
 				Graphics::PixelFormat::createFormatCLUT8());


Commit: d7268b5a91f241aea153b8685404f40cc2b1a8a7
    https://github.com/scummvm/scummvm/commit/d7268b5a91f241aea153b8685404f40cc2b1a8a7
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:20:43+02:00

Commit Message:
EEM: better handling of saves

Changed paths:
    engines/eem/eem.cpp
    engines/eem/eem.h
    engines/eem/ui.cpp


diff --git a/engines/eem/eem.cpp b/engines/eem/eem.cpp
index fa895d640a6..529970dcbdf 100644
--- a/engines/eem/eem.cpp
+++ b/engines/eem/eem.cpp
@@ -749,7 +749,20 @@ Common::Error EEMEngine::loadGameStream(Common::SeekableReadStream *stream) {
 
 SaveStateList EEMEngine::listProfiles() const {
 	// Mirrors `_findfirst("*.PLR")` in `screen8_handler @ 1c33:1012`.
-	return getMetaEngine()->listSaves(_targetName.c_str());
+	// We disable autosave (`getAutosaveSlot()` returns -1) but a
+	// pre-existing slot-0 file from a previous run, or one written by
+	// another engine using the same target, would still show up here
+	// and pollute the picker. Filter it out so the picker treats slot
+	// 0 as if it didn't exist ??? matching the original which never
+	// has an autosave concept.
+	SaveStateList saves = getMetaEngine()->listSaves(_targetName.c_str());
+	for (uint i = 0; i < saves.size(); ) {
+		if (saves[i].getSaveSlot() == 0)
+			saves.remove_at(i);
+		else
+			i++;
+	}
+	return saves;
 }
 
 Common::Error EEMEngine::saveProfile(const Common::String &name) {
diff --git a/engines/eem/eem.h b/engines/eem/eem.h
index af19e0c43fc..97b8b162ece 100644
--- a/engines/eem/eem.h
+++ b/engines/eem/eem.h
@@ -115,6 +115,15 @@ public:
 	bool canLoadGameStateCurrently(Common::U32String *msg = nullptr) override;
 	bool canSaveGameStateCurrently(Common::U32String *msg = nullptr) override;
 
+	// Disable ScummVM's periodic autosave. The original engine's
+	// `screen8_handler @ 1c33:1012` builds the profile picker by
+	// walking every `*.PLR` file in the save dir, and we mirror that
+	// via `listProfiles() ??? MetaEngine::listSaves`. Letting the
+	// framework write a slot-0 autosave creates a phantom profile
+	// that shows up on the picker as a real save. Returning -1 tells
+	// the framework to skip autosave entirely.
+	int getAutosaveSlot() const override { return -1; }
+
 	// ScummVM extended-save hooks. The base `Engine::saveGameState` /
 	// `loadGameState` write/read the framework header (description,
 	// thumbnail, playtime, version) around our body via these
diff --git a/engines/eem/ui.cpp b/engines/eem/ui.cpp
index 7d8666b36ca..c4d9c5282f4 100644
--- a/engines/eem/ui.cpp
+++ b/engines/eem/ui.cpp
@@ -242,6 +242,15 @@ void EEMEngine::doProfilePicker() {
 	// `local_20 == 0` at 1c33:1170), it falls straight into
 	// `_NewPlayer`. Selecting an entry calls `_LoadPlayerRecord` and
 	// returns; selecting the "exit" sentinel goes back to title.
+
+	// Palette reset. `screen8_handler` runs `_FadeOut(); _GetPalette(0);
+	// _GetBackground(0x104);` before the picker, so the BG always
+	// renders against SITEPALS index 0 regardless of which intro
+	// palette was active last. Without this, skipping out of an intro
+	// anim (THEME / ANIM01..20 / TITLE) leaves the previous video's
+	// palette in place and the picker draws with the wrong colours.
+	setSitePalette(0);
+
 	const SaveStateList saves = listProfiles();
 	if (saves.empty()) {
 		doNewPlayer();
@@ -274,6 +283,16 @@ void EEMEngine::doProfilePicker() {
 	int sel = 0;
 	bool done = false;
 
+	// Picker geometry: `DrawList @ 1c33:040d` is called from
+	// `screen8_handler @ 1c33:1012` with `(_TextBox + 3, DAT_29be_0d02)`.
+	// `_TextBox @ 29be:0d00` holds {x1=58, y1=35, x2=238, y2=158} so
+	// the list origin is (61, 35), 10 px per row, max 12 visible
+	// rows. The "Pick a player" caption is part of the BG (PIC 0x104)
+	// ??? `screen8_handler` never draws it as text ??? so an extra
+	// `drawString` would overlay on top of the baked-in heading.
+	const int kListX = 61;
+	const int kListY = 35;
+	const int kLineH = 10;
 	auto draw = [&]() {
 		Graphics::ManagedSurface scratch(320, 200,
 			Graphics::PixelFormat::createFormatCLUT8());
@@ -286,12 +305,10 @@ void EEMEngine::doProfilePicker() {
 				memcpy((byte *)scratch.getBasePtr(0, row),
 					   (const byte *)bg.surface.getBasePtr(0, row), w);
 		}
-		_font.drawString(&scratch, "Pick a player:", 80, 30, 220, 0xF);
-		const int kLineH = 12;
 		for (uint i = 0; i < entries.size(); i++) {
 			const byte color = ((int)i == sel) ? 0xF : 0x8;
 			_font.drawString(&scratch, entries[i].label,
-							 80, 60 + (int)i * kLineH, 220, color);
+							 kListX, kListY + (int)i * kLineH, 220, color);
 		}
 		g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
 								   0, 0, 320, 200);
@@ -331,8 +348,7 @@ void EEMEngine::doProfilePicker() {
 				}
 			}
 			if (ev.type == Common::EVENT_LBUTTONDOWN) {
-				const int kLineH = 12;
-				const int hit = (ev.mouse.y - 60) / kLineH;
+				const int hit = (ev.mouse.y - kListY) / kLineH;
 				if (hit >= 0 && hit < (int)entries.size()) {
 					sel = hit;
 					committed = true;


Commit: e87d591b963cb98745dd48c701b119690865f74f
    https://github.com/scummvm/scummvm/commit/e87d591b963cb98745dd48c701b119690865f74f
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:20:43+02:00

Commit Message:
EEM: remove unused screen

Changed paths:
    engines/eem/eem.cpp
    engines/eem/eem.h
    engines/eem/ui.cpp


diff --git a/engines/eem/eem.cpp b/engines/eem/eem.cpp
index 529970dcbdf..46fa0497f45 100644
--- a/engines/eem/eem.cpp
+++ b/engines/eem/eem.cpp
@@ -266,22 +266,31 @@ Common::Error EEMEngine::run() {
 	// iteration. Sentinel `kScreenInvalid` (0xFFFF) ends the loop ???
 	// same as the original's table-end marker.
 	//
-	// Initial value `kScreenAction` matches the original flow at the
-	// tail of `_DoChoosePartner @ 1a35:099d` which sets
-	// `_NextScreen = 0xc` once the partner has been picked. (The
-	// resume path above bypasses this and seeds `kScreenMap` instead.)
+	// `_DoChoosePartner @ 1a35:099d` sets `_NextScreen = 0xc` (= the
+	// original `_ActionScreen` ??? "Choose A Mystery / Practice Mystery /
+	// See ScrapBook 1..3"). In our port that menu lives inside
+	// `doCaseSelection`, which already mirrors `_ActionScreen`'s 5-entry
+	// list (verified against `ActionNames @ 29be:0d6a`) and rolls the
+	// individual-mystery picker (`_CaseSelection @ 1c33:0a87`) into the
+	// same flow. So we route straight to `kScreenChooseMystery`; the
+	// `kScreenAction` (= 0xc) state still exists for the post-win path
+	// (`_DisplayCorrect @ 1df2:0895` writes 0xc) but its handler also
+	// dispatches `doCaseSelection`. The previous `doActionScreen` was a
+	// synthetic stub ("What now?" / "Solve a Mystery" / "Look at My
+	// Books") whose strings are nowhere in the binary ??? confirmed by a
+	// `search_strings` for "What" returning zero matches.
 	//
 	// Mid-mystery profile resume: if the profile picker loaded a
 	// save whose `hasMystery` flag was set, `_mystery.isLoaded()` is
 	// true here and the player just re-picked their partner. Drop
-	// straight to MAP rather than ACTION so they don't have to walk
-	// back through the case picker (which would `_mystery.load()`
-	// fresh and discard their site / clue progress). The original
-	// has no equivalent ??? it persists only profile-level state via
-	// `_PlayerRecord`, not in-progress mysteries ??? so this is a
-	// ScummVM-only ergonomics improvement.
+	// straight to MAP rather than the action menu so they don't have
+	// to walk back through the case picker (which would
+	// `_mystery.load()` fresh and discard their site / clue
+	// progress). The original has no equivalent ??? it persists only
+	// profile-level state via `_PlayerRecord`, not in-progress
+	// mysteries ??? so this is a ScummVM-only ergonomics improvement.
 	if (!shouldQuit() && !resumed)
-		_nextScreen = _mystery.isLoaded() ? kScreenMap : kScreenAction;
+		_nextScreen = _mystery.isLoaded() ? kScreenMap : kScreenChooseMystery;
 screen_loop:
 	while (!shouldQuit() && _nextScreen != kScreenInvalid) {
 		const ScreenId current = (ScreenId)_nextScreen;
@@ -289,20 +298,27 @@ screen_loop:
 
 		switch (current) {
 		case kScreenAction:
-			// Post-mystery menu. `_ActionScreen` sets _NextScreen via
-			// its action jumptable (1c33:1be1) ??? see `doActionScreen`.
-			doActionScreen();
+			// Post-mystery menu. The original's `_ActionScreen @
+			// 1c33:195b` shows the 5-entry "Choose A Mystery /
+			// Practice / ScrapBook" picker; `doCaseSelection` is
+			// our port of that exact menu (plus the individual-case
+			// sub-picker the original handles in `_CaseSelection @
+			// 1c33:0a87`). After the player picks, fall through to
+			// the same routing the `kScreenChooseMystery` case uses.
+			// Reachable from `_DisplayCorrect`'s 0xc write after a
+			// solve (see `ui.cpp` `_nextScreen = kScreenAction`).
+			doCaseSelection();
+			_nextScreen = _mystery.isLoaded() ? kScreenInitClues
+											  : kScreenInvalid;
 			break;
 
 		case kScreenChooseMystery:
 			// Handler 10 at 1a35:0e0e calls `_DoChooseMystery` which
 			// presets `_NextScreen = 0` (INIT_CLUES) before
-			// `_CaseSelection`. If the picker bails out without
-			// loading a mystery (no `_ReadMystery` call), drop back
-			// to ACTION instead of falling into a missing case.
+			// `_CaseSelection`. Same dispatch as `kScreenAction`.
 			doCaseSelection();
 			_nextScreen = _mystery.isLoaded() ? kScreenInitClues
-											  : kScreenAction;
+											  : kScreenInvalid;
 			break;
 
 		case kScreenInitClues:
@@ -310,7 +326,7 @@ screen_loop:
 			// then writes `_NextScreen = 1` (MAP).
 			doInitClues();
 			_nextScreen = _mystery.isLoaded() ? kScreenMap
-											  : kScreenAction;
+											  : kScreenChooseMystery;
 			break;
 
 		case kScreenMap:
@@ -323,7 +339,7 @@ screen_loop:
 			// the natural next state is SITE.
 			doBigMap();
 			if (!_mystery.isLoaded())
-				_nextScreen = kScreenAction;
+				_nextScreen = kScreenChooseMystery;
 			else if (_nextScreen == current)
 				_nextScreen = kScreenSite;
 			break;
@@ -340,7 +356,7 @@ screen_loop:
 			// via `_LastScreen`).
 			doSiteLoop();
 			if (!_mystery.isLoaded())
-				_nextScreen = kScreenAction;
+				_nextScreen = kScreenChooseMystery;
 			else if (_nextScreen == current)
 				_nextScreen = kScreenInvalid;  // user quit
 			break;
diff --git a/engines/eem/eem.h b/engines/eem/eem.h
index 97b8b162ece..ab7389a4b9e 100644
--- a/engines/eem/eem.h
+++ b/engines/eem/eem.h
@@ -349,21 +349,6 @@ private:
 	void doCaseSelection();
 	void doSiteLoop();
 
-	/// Post-mystery action menu. Mirrors `_ActionScreen @ 1c33:195b` ???
-	/// the screen the original returns to after `_DisplayCorrect`
-	/// (winner) or after the player explicitly leaves a case via the
-	/// PROFILE ??? PARTNER chain. The original offers up to 5 choices
-	/// gated on the player's chain stage (`DAT_2d5d_3f99`):
-	///   1: "Solve a Mystery" (set _NextScreen=10 ??? CHOOSE_MYSTERY)
-	///   3: replay the last solved case (`_ReloadMystery(0)` callsite)
-	///   5: scrapbook viewer (`_ShowOneScrap(0, 1)` callsite)
-	///   7: chain-stage advance (cmp `_3f99 == 1`)
-	///   9: chain-stage advance (cmp `_3f99 == 2` / `== 3`)
-	///   sentinel: exit / back to PARTNER
-	/// We start with just option 1 wired (the practical loop) plus
-	/// quit; the others slot in as their underlying screens land.
-	void doActionScreen();
-
 	/// Setup / preferences screen. Mirrors `_DoSetup @ 1f78:044e` ???
 	/// per-profile preferences (voice on/off via `DAT_2d5d_3f97`,
 	/// partner pick via SwapColors on Kid1/Kid2 rects). Reachable
diff --git a/engines/eem/ui.cpp b/engines/eem/ui.cpp
index c4d9c5282f4..f91ae57ffa7 100644
--- a/engines/eem/ui.cpp
+++ b/engines/eem/ui.cpp
@@ -732,154 +732,6 @@ void EEMEngine::doSetup() {
 	}
 }
 
-void EEMEngine::doActionScreen() {
-	// Mirrors `_ActionScreen @ 1c33:195b` ??? the post-mystery menu the
-	// original loops back to after `_DisplayCorrect` (winner). The
-	// original draws PIC 0x122 (Cok), 0x124 (Cexit), then calls
-	// `_DoChoose(ActionNames)` and dispatches the result via the
-	// jumptable at 1c33:1be1 (verified to set `_NextScreen` for each
-	// action ??? entry 1 ??? 10 = CHOOSE_MYSTERY, entry 3 ???
-	// `_ReloadMystery`, etc.).
-	//
-	// We render a minimal text version of the menu ??? the original's
-	// background pic + "Cok" / "Cexit" overlays land once the
-	// `_DoChoose` UI lands. For now: two practical entries ??? "Solve a
-	// Mystery" (the only one whose underlying screen is wired) and
-	// "Quit". Sets `_nextScreen` exactly the same way the original
-	// jumptable does:
-	//   "Solve a Mystery"   ??? kScreenChooseMystery (matches handler 1
-	//                          at 1c33:1add: `MOV [_NextScreen], 0xa`)
-	//   "Quit"              ??? kScreenInvalid (matches the sentinel
-	//                          handler at 1c33:1afa)
-	if (!_font.isLoaded()) {
-		_nextScreen = kScreenChooseMystery;
-		return;
-	}
-
-	enum ActionKind {
-		kActSolve,         // ??? kScreenChooseMystery (action 1, 1c33:1add)
-		kActScrapbook,     // ??? doShowEnding(lastSolved) (action 5, 1c33:1b13)
-		kActQuit
-	};
-	struct Entry {
-		const char *label;
-		ActionKind  kind;
-	};
-	// Locate the highest-numbered solved mystery ??? that's the one
-	// the player most recently completed, and what the action-menu
-	// "Look at My Books" entry replays. If none solved yet, hide
-	// the entry (matches the original's grey-out at 1c33:19f3 where
-	// `local_24[5] = 1` before the chain-stage check toggles it).
-	int lastSolved = -1;
-	for (int i = (int)sizeof(_mysteriesSolved) - 1; i >= 0; i--) {
-		if (_mysteriesSolved[i] != 0) {
-			lastSolved = i;
-			break;
-		}
-	}
-
-	Common::Array<Entry> entries;
-	Entry solveEntry; solveEntry.label = "Solve a Mystery"; solveEntry.kind = kActSolve;
-	entries.push_back(solveEntry);
-	if (lastSolved >= 0) {
-		Entry sb; sb.label = "Look at My Books"; sb.kind = kActScrapbook;
-		entries.push_back(sb);
-	}
-	Entry quitEntry; quitEntry.label = "Quit"; quitEntry.kind = kActQuit;
-	entries.push_back(quitEntry);
-	const int kCount = (int)entries.size();
-
-	int sel = 0;
-	auto draw = [&]() {
-		Graphics::ManagedSurface scratch(320, 200,
-			Graphics::PixelFormat::createFormatCLUT8());
-		scratch.clear();
-		Picture bg;
-		if (_picsArchive.getPicture(0x104, bg)) {
-			const int w = MIN<int>(bg.surface.w, 320);
-			const int h = MIN<int>(bg.surface.h, 200);
-			for (int row = 0; row < h; row++)
-				memcpy((byte *)scratch.getBasePtr(0, row),
-					   (const byte *)bg.surface.getBasePtr(0, row), w);
-		}
-		_font.drawString(&scratch, "What now?", 100, 30, 220, 0xF);
-		for (int i = 0; i < kCount; i++) {
-			const byte color = (i == sel) ? 0xF : 0x8;
-			_font.drawString(&scratch, entries[i].label,
-							 100, 60 + i * 14, 220, color);
-		}
-		g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
-								   0, 0, 320, 200);
-		g_system->updateScreen();
-	};
-	draw();
-
-	while (!shouldQuit()) {
-		Common::Event ev;
-		bool dirty = false;
-		bool committed = false;
-		while (g_system->getEventManager()->pollEvent(ev)) {
-			if (ev.type == Common::EVENT_QUIT ||
-				ev.type == Common::EVENT_RETURN_TO_LAUNCHER) {
-				_nextScreen = kScreenInvalid;
-				return;
-			}
-			if (ev.type == Common::EVENT_KEYDOWN) {
-				switch (ev.kbd.keycode) {
-				case Common::KEYCODE_UP:
-					sel = (sel + kCount - 1) % kCount;
-					dirty = true;
-					break;
-				case Common::KEYCODE_DOWN:
-					sel = (sel + 1) % kCount;
-					dirty = true;
-					break;
-				case Common::KEYCODE_RETURN:
-				case Common::KEYCODE_KP_ENTER:
-					committed = true;
-					break;
-				case Common::KEYCODE_ESCAPE:
-					_nextScreen = kScreenInvalid;
-					return;
-				default:
-					break;
-				}
-			}
-			if (ev.type == Common::EVENT_LBUTTONDOWN) {
-				const int hit = (ev.mouse.y - 60) / 14;
-				if (hit >= 0 && hit < kCount) {
-					sel = hit;
-					committed = true;
-				}
-			}
-			if (committed)
-				break;
-		}
-		if (committed) {
-			switch (entries[sel].kind) {
-			case kActSolve:
-				_nextScreen = kScreenChooseMystery;
-				return;
-			case kActScrapbook:
-				if (lastSolved >= 0)
-					doShowEnding((uint)lastSolved);
-				// Fall through to redraw the menu after viewing.
-				draw();
-				continue;
-			case kActQuit:
-				_nextScreen = kScreenInvalid;
-				return;
-			}
-			return;
-		}
-		if (dirty)
-			draw();
-		g_system->updateScreen();
-		g_system->delayMillis(15);
-	}
-	_nextScreen = kScreenInvalid;
-}
-
 void EEMEngine::doCaseSelection() {
 	// Mirrors `_CaseSelection` @ 1c33:0a87. The original draws PIC 0x41
 	// (chooser background) plus a centred "Book %d" / "Challenge Book"


Commit: 253e2f0bb20aad598b1e3948dde3439cba853301
    https://github.com/scummvm/scummvm/commit/253e2f0bb20aad598b1e3948dde3439cba853301
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:20:44+02:00

Commit Message:
EEM: improve the setup screen with actually triggers to the code relevant

Changed paths:
    engines/eem/ui.cpp


diff --git a/engines/eem/ui.cpp b/engines/eem/ui.cpp
index f91ae57ffa7..d857dce9877 100644
--- a/engines/eem/ui.cpp
+++ b/engines/eem/ui.cpp
@@ -636,21 +636,79 @@ void EEMEngine::doShowEnding(uint num) {
 }
 
 void EEMEngine::doSetup() {
-	// Mirrors `_DoSetup @ 1f78:044e`. Loads BG pic 0x40 +
-	// state-button pics 0x9b/0x9c/0x9d/0x9e. The original wires 13
-	// hot-rects (`_SetupButtons @ 29be:1218`) but only two of them
-	// drive persistent state ??? `Kid1`/`Kid2` (partner) and
-	// `SoundOn`/`SoundOff` (voice flag, `DAT_2d5d_3f97`). The rest
-	// are reset/help/return buttons. We render a minimal text
-	// version: two toggle lines, click to flip, ESC to leave.
+	// Mirrors `_DoSetup @ 1f78:044e`. The setup screen is BG `PIC 0x40`
+	// (loaded once on entry) with every label baked in ??? "Setup",
+	// "Partner", "Sound", "Music", the "Jake"/"Jenny"/"On"/"Off"
+	// option strings, etc. ??? all rendered in palette key `0xFE`. The
+	// original then runs `_SetupSettings @ 1f78:000d` which uses
+	// `_SwapColors @ 172b:1d2a` to recolour those `0xFE` pixels per
+	// label rect: `0x15` for the active state, `0` for the inactive
+	// one. So nothing is drawn as text; the visible state of each
+	// toggle is purely a per-rect colour swap on top of `PIC 0x40`.
+	//
+	// Click hit-tests go through `_SetupButtons @ 29be:1218` ??? 13??
+	// 8-byte rects. Each click runs `HandleSetupButton @ 1f78:0158`,
+	// which dispatches via the 12-entry jumptable at `1f78:0436`.
+	// Verified handler map (decompiled at each jumptable target):
+	//   [0]  ( 20, 44, 39, 61)   Partner toggle (1f78:017a)
+	//   [1]  ( 20, 87, 39,104)   Voice toggle   (1f78:0196 ??? DAT_2d5d_3f97)
+	//   [2]  ( 20,127, 39,144)   back to profile (NextScreen=8)
+	//   [3]  (281, 43,299, 60)   ScrapBook 1   (_ShowScrapbook(0,1))
+	//   [4]  (281, 62,299, 79)   ScrapBook 2   gated chainStage>=2
+	//   [5]  (281, 81,299, 98)   ScrapBook 3   gated chainStage>=3
+	//   [6]  (281,108,299,125)   Save game     (_SaveGame @ 2404:0c87)
+	//   [7]  (281,127,299,144)   New Case      (NextScreen=0xa)
+	//   [8]  ( 53,153,108,183)   Done          (SI=1, exit)
+	//   [9]  (145,163,174,187)   Help          (_InterfaceHelp(1))
+	//   [10] (212,153,266,184)   Quit          (_AreYouSure ??? NextScreen=0xffff)
+	//   [11] ( 81, 25,238, 37)   Credits       (PIC 0x208 fullscreen)
+	//   [12] ( 11,  1,  3,  3)   debug placeholder
+	// Highlight rects (`Kid1 @ 29be:1320` / `Kid2 @ 29be:1328` /
+	// `SoundOn @ 29be:1330` / `SoundOff @ 29be:1338`) drive
+	// `_SwapColors`; they're not click targets in the original.
 	if (!_font.isLoaded()) {
 		_nextScreen = (ScreenId)_lastScreen;
 		return;
 	}
 
-	const Common::Rect kBackBtn(120, 170, 200, 188);
-	const Common::Rect kPartnerToggle(40, 60, 280, 78);
-	const Common::Rect kVoiceToggle  (40, 90, 280, 108);
+	// Original button rects (`_SetupButtons` indices wired here).
+	const Common::Rect kPartnerBtn   ( 20,  44,  39,  61); // [0]
+	const Common::Rect kVoiceBtn     ( 20,  87,  39, 104); // [1]
+	const Common::Rect kProfileBtn   ( 20, 127,  39, 144); // [2]
+	const Common::Rect kScrap1Btn    (281,  43, 299,  60); // [3]
+	const Common::Rect kScrap2Btn    (281,  62, 299,  79); // [4]
+	const Common::Rect kScrap3Btn    (281,  81, 299,  98); // [5]
+	const Common::Rect kSaveBtn      (281, 108, 299, 125); // [6]
+	const Common::Rect kNewCaseBtn   (281, 127, 299, 144); // [7]
+	const Common::Rect kDoneBtn      ( 53, 153, 108, 183); // [8]
+	const Common::Rect kHelpBtn      (145, 163, 174, 187); // [9]
+	const Common::Rect kQuitBtn      (212, 153, 266, 184); // [10]
+	const Common::Rect kCreditsBtn   ( 81,  25, 238,  37); // [11]
+	// Highlight / fallback-click rects.
+	const Common::Rect kKid1Rect     ( 99,  44, 148,  52);
+	const Common::Rect kKid2Rect     ( 99,  54, 148,  62);
+	const Common::Rect kSoundOnRect  (106,  86, 125,  94);
+	const Common::Rect kSoundOffRect (106,  96, 125, 104);
+
+	// Pixel-level color-key swap. Mirrors `_SwapColors @ 172b:1d2a`:
+	// for each pixel in `r` whose value equals `from`, replace with
+	// `to`. `0xFE` is the BG's text-key color; `0x15` is the active
+	// (bright) palette index, `0x00` the inactive one ??? both verified
+	// in `_SetupSettings @ 1f78:000d`.
+	auto swapColors = [](Graphics::ManagedSurface &dst,
+						 const Common::Rect &r, byte from, byte to) {
+		const int x1 = MAX<int>(0, r.left);
+		const int y1 = MAX<int>(0, r.top);
+		const int x2 = MIN<int>(320, r.right);
+		const int y2 = MIN<int>(200, r.bottom);
+		for (int y = y1; y < y2; y++) {
+			byte *row = (byte *)dst.getBasePtr(0, y);
+			for (int x = x1; x < x2; x++) {
+				if (row[x] == from)
+					row[x] = to;
+			}
+		}
+	};
 
 	auto draw = [&]() {
 		Graphics::ManagedSurface scratch(320, 200,
@@ -664,22 +722,121 @@ void EEMEngine::doSetup() {
 				memcpy((byte *)scratch.getBasePtr(0, row),
 					   (const byte *)bg.surface.getBasePtr(0, row), w);
 		}
-		_font.drawString(&scratch, "Setup", 140, 30, 80, 0xF);
-		const Common::String partnerLine = Common::String::format(
-			"Partner: %s   (click to switch)",
-			_partner == 0 ? "Jake" : "Jenny");
-		_font.drawString(&scratch, partnerLine, 50, 64, 240, 0xF);
-		const Common::String voiceLine = Common::String::format(
-			"Voice:   %s   (click to toggle)",
-			_voiceOn ? "ON" : "OFF");
-		_font.drawString(&scratch, voiceLine, 50, 94, 240, 0xF);
-		_font.drawString(&scratch, "[ Back ]", 130, 174, 80, 0xF);
+
+		const byte kKey    = 0xFE;
+		const byte kBright = 0x15;
+		const byte kDim    = 0x00;
+		swapColors(scratch, kKid1Rect, kKey,
+				   _partner == 0 ? kBright : kDim);
+		swapColors(scratch, kKid2Rect, kKey,
+				   _partner == 1 ? kBright : kDim);
+		swapColors(scratch, kSoundOnRect,  kKey,
+				   _voiceOn ? kBright : kDim);
+		swapColors(scratch, kSoundOffRect, kKey,
+				   _voiceOn ? kDim : kBright);
+
 		g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
 								   0, 0, 320, 200);
 		g_system->updateScreen();
 	};
 	draw();
 
+	// Modal "Are you sure?" yes/no prompt. Mirrors `_AreYouSure @
+	// 1a35:0a5c` ??? the original draws a centred message, listens for
+	// Y/Enter (confirm) or N/ESC (cancel). We render a minimal
+	// overlay with Y / N keys (and click on left/right halves) so
+	// the Quit button gives the player a chance to back out.
+	auto areYouSure = [&]() -> bool {
+		Graphics::ManagedSurface scratch(320, 200,
+			Graphics::PixelFormat::createFormatCLUT8());
+		Graphics::Surface *cur = g_system->lockScreen();
+		if (cur) {
+			for (int row = 0; row < 200; row++)
+				memcpy((byte *)scratch.getBasePtr(0, row),
+					   (const byte *)cur->getBasePtr(0, row), 320);
+			g_system->unlockScreen();
+		}
+		const Common::Rect kBox(80, 80, 240, 120);
+		scratch.fillRect(kBox, 0x00);
+		_font.drawString(&scratch, "Are you sure?", 100, 88, 200, 0xF);
+		_font.drawString(&scratch, "Y = yes   N = no", 100, 102, 200, 0xF);
+		g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
+								   0, 0, 320, 200);
+		g_system->updateScreen();
+		while (!shouldQuit()) {
+			Common::Event ev;
+			while (g_system->getEventManager()->pollEvent(ev)) {
+				if (ev.type == Common::EVENT_QUIT ||
+					ev.type == Common::EVENT_RETURN_TO_LAUNCHER)
+					return true;
+				if (ev.type == Common::EVENT_KEYDOWN) {
+					const Common::KeyCode k = ev.kbd.keycode;
+					if (k == Common::KEYCODE_y ||
+						k == Common::KEYCODE_RETURN)
+						return true;
+					if (k == Common::KEYCODE_n ||
+						k == Common::KEYCODE_ESCAPE)
+						return false;
+				}
+				if (ev.type == Common::EVENT_LBUTTONDOWN) {
+					return ev.mouse.x < 160;
+				}
+			}
+			g_system->delayMillis(15);
+		}
+		return false;
+	};
+
+	// Fullscreen-pic modal. Mirrors `_InterfaceHelp @ 1560:0205`'s
+	// per-frame loop: blit pic, wait for click/key, advance / exit.
+	// Used by Credits (single PIC 0x208) and Help (we reuse for a
+	// minimal stub since the help-pic table at `_InterfaceHelp`'s
+	// offset isn't fully decoded yet).
+	auto showFullscreenPic = [&](uint16 picId) {
+		Picture pic;
+		if (!_picsArchive.getPicture(picId, pic)) {
+			warning("doSetup: PIC %u missing", (uint)picId);
+			return;
+		}
+		Graphics::ManagedSurface scratch(320, 200,
+			Graphics::PixelFormat::createFormatCLUT8());
+		scratch.clear();
+		const int w = MIN<int>(pic.surface.w, 320);
+		const int h = MIN<int>(pic.surface.h, 200);
+		for (int row = 0; row < h; row++)
+			memcpy((byte *)scratch.getBasePtr(0, row),
+				   (const byte *)pic.surface.getBasePtr(0, row), w);
+		g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
+								   0, 0, 320, 200);
+		g_system->updateScreen();
+		while (!shouldQuit()) {
+			Common::Event ev;
+			while (g_system->getEventManager()->pollEvent(ev)) {
+				if (ev.type == Common::EVENT_QUIT ||
+					ev.type == Common::EVENT_RETURN_TO_LAUNCHER ||
+					ev.type == Common::EVENT_KEYDOWN ||
+					ev.type == Common::EVENT_LBUTTONDOWN)
+					return;
+			}
+			g_system->delayMillis(15);
+		}
+	};
+
+	auto leaveSetup = [&]() {
+		// `_DoSetup`'s entry writes `_NextScreen = _LastScreen`. We
+		// honor any handler that has already overridden `_nextScreen`
+		// (Credits / Save don't, but New Case / Quit do). Otherwise
+		// fall back to `_lastScreen`.
+		if (_nextScreen == kScreenSetup) {
+			_nextScreen = (ScreenId)_lastScreen;
+			if (_nextScreen == kScreenSetup ||
+				_nextScreen == kScreenInvalid)
+				_nextScreen = kScreenMap;
+		}
+		saveProfile(_playerName);
+	};
+
+	_nextScreen = kScreenSetup;  // sentinel ??? leaveSetup picks the real target
 	while (!shouldQuit()) {
 		Common::Event ev;
 		bool dirty = false;
@@ -692,38 +849,131 @@ void EEMEngine::doSetup() {
 			if (ev.type == Common::EVENT_KEYDOWN) {
 				if (ev.kbd.keycode == Common::KEYCODE_ESCAPE ||
 					ev.kbd.keycode == Common::KEYCODE_RETURN) {
-					// `_DoSetup @ 1f78:044a` writes `_NextScreen =
-					// _LastScreen` on entry. Returning means we just
-					// dispatch back to whichever screen called us.
-					_nextScreen = (ScreenId)_lastScreen;
-					if (_nextScreen == kScreenSetup ||
-						_nextScreen == kScreenInvalid)
-						_nextScreen = kScreenMap;
-					if (_voiceOn)
-						saveProfile(_playerName);
+					leaveSetup();
 					return;
 				}
 			}
-			if (ev.type == Common::EVENT_LBUTTONDOWN) {
-				if (kBackBtn.contains(ev.mouse.x, ev.mouse.y)) {
-					_nextScreen = (ScreenId)_lastScreen;
-					if (_nextScreen == kScreenSetup ||
-						_nextScreen == kScreenInvalid)
-						_nextScreen = kScreenMap;
-					saveProfile(_playerName);
-					return;
-				}
-				if (kPartnerToggle.contains(ev.mouse.x, ev.mouse.y)) {
-					_partner = _partner == 0 ? 1 : 0;
+			if (ev.type != Common::EVENT_LBUTTONDOWN)
+				continue;
+			const int mx = ev.mouse.x;
+			const int my = ev.mouse.y;
+
+			// Partner toggle (button [0]) ??? original has no symmetric
+			// right-side button (the [3] rect is ScrapBook 1, not a
+			// partner arrow). Direct clicks on the Jake/Jenny labels
+			// are accepted as a more intuitive fallback.
+			if (kPartnerBtn.contains(mx, my)) {
+				_partner = _partner == 0 ? 1 : 0;
+				dirty = true;
+				continue;
+			}
+			if (kKid1Rect.contains(mx, my)) {
+				if (_partner != 0) { _partner = 0; dirty = true; }
+				continue;
+			}
+			if (kKid2Rect.contains(mx, my)) {
+				if (_partner != 1) { _partner = 1; dirty = true; }
+				continue;
+			}
+
+			// Voice toggle (button [1]).
+			if (kVoiceBtn.contains(mx, my)) {
+				_voiceOn = !_voiceOn;
+				if (_audio)
+					_audio->setVoiceEnabled(_voiceOn);
+				dirty = true;
+				continue;
+			}
+			if (kSoundOnRect.contains(mx, my)) {
+				if (!_voiceOn) {
+					_voiceOn = true;
+					if (_audio)
+						_audio->setVoiceEnabled(_voiceOn);
 					dirty = true;
 				}
-				if (kVoiceToggle.contains(ev.mouse.x, ev.mouse.y)) {
-					_voiceOn = !_voiceOn;
+				continue;
+			}
+			if (kSoundOffRect.contains(mx, my)) {
+				if (_voiceOn) {
+					_voiceOn = false;
 					if (_audio)
 						_audio->setVoiceEnabled(_voiceOn);
 					dirty = true;
 				}
+				continue;
+			}
+
+			// New Case (button [7]). Original handler at 1f78:01ad
+			// sets `_NextScreen = 0xa` (CHOOSE_MYSTERY) and exits the
+			// dispatch loop with SI=1.
+			if (kNewCaseBtn.contains(mx, my)) {
+				saveProfile(_playerName);
+				_nextScreen = kScreenChooseMystery;
+				return;
+			}
+
+			// Save (button [6]). Original calls `_SaveGame @
+			// 2404:0c87` and stays in the setup loop. Our save is
+			// profile-scoped (one slot per player name) ??? same effect.
+			if (kSaveBtn.contains(mx, my)) {
+				saveProfile(_playerName);
+				continue;
+			}
+
+			// Done (button [8]). Original handler is just `MOV SI,1;
+			// JMP exit` ??? `_NextScreen` stays at whatever entry set it
+			// to (= `_LastScreen`).
+			if (kDoneBtn.contains(mx, my)) {
+				leaveSetup();
+				return;
+			}
+
+			// Quit (button [10]). Original: `_AreYouSure(0)` ???
+			// confirmed ??? `_NextScreen = 0xffff` (sentinel quit).
+			if (kQuitBtn.contains(mx, my)) {
+				if (areYouSure()) {
+					_nextScreen = kScreenInvalid;
+					return;
+				}
+				dirty = true;  // restore the BG after the prompt
+				continue;
 			}
+
+			// Help (button [9]). Original calls `_InterfaceHelp(1)`,
+			// which walks a sequence of help-pic IDs from a runtime
+			// table we haven't fully decoded. PIC 0x4F is the in-game
+			// help backdrop the briefing chain reuses; we show that
+			// as a stub until the full table is wired.
+			if (kHelpBtn.contains(mx, my)) {
+				showFullscreenPic(0x4F);
+				dirty = true;
+				continue;
+			}
+
+			// Credits (button [11]). Original handler at 1f78:025a
+			// loads PIC 0x208 and blits it fullscreen, then waits
+			// for any input.
+			if (kCreditsBtn.contains(mx, my)) {
+				showFullscreenPic(0x208);
+				// PIC 0x208 has its own palette baked into the BG
+				// dump via `_GetPicture`; the original restores via
+				// `_GetPalette` on return. Reset to setup palette
+				// (SITEPALS index 0) so the setup BG renders right.
+				setSitePalette(0);
+				dirty = true;
+				continue;
+			}
+
+			// Profile (button [2]). Goes back to the profile picker
+			// in the original (`_NextScreen = 8`). Treat the same way
+			// as Done for now ??? switching profiles mid-game isn't
+			// wired in our port and would discard mystery state.
+			if (kProfileBtn.contains(mx, my)) {
+				leaveSetup();
+				return;
+			}
+
+			(void)kScrap1Btn; (void)kScrap2Btn; (void)kScrap3Btn;
 		}
 		if (dirty)
 			draw();


Commit: ae83ff8a9f033760539254101d11b6718943bc25
    https://github.com/scummvm/scummvm/commit/ae83ff8a9f033760539254101d11b6718943bc25
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:20:44+02:00

Commit Message:
EEM: help screen in setup screen implemented

Changed paths:
    engines/eem/ui.cpp


diff --git a/engines/eem/ui.cpp b/engines/eem/ui.cpp
index d857dce9877..9822cf9f53d 100644
--- a/engines/eem/ui.cpp
+++ b/engines/eem/ui.cpp
@@ -787,25 +787,40 @@ void EEMEngine::doSetup() {
 		return false;
 	};
 
-	// Fullscreen-pic modal. Mirrors `_InterfaceHelp @ 1560:0205`'s
-	// per-frame loop: blit pic, wait for click/key, advance / exit.
-	// Used by Credits (single PIC 0x208) and Help (we reuse for a
-	// minimal stub since the help-pic table at `_InterfaceHelp`'s
-	// offset isn't fully decoded yet).
-	auto showFullscreenPic = [&](uint16 picId) {
+	// Render `picId` and block until click/key. Returns the pressed
+	// keycode (KEYCODE_ESCAPE for an explicit bail, KEYCODE_INVALID
+	// for a click or any other key). When `transparent` is true,
+	// preserve the current screen behind and overlay `picId` with
+	// its transparent colour key ??? mirrors `_InterfaceHelp @
+	// 1560:0205` calling `_Rect_Move_Mask` with the pic's
+	// `miscflags >> 8` as the transp byte. When false, do a raw
+	// fullscreen blit ??? mirrors the credits handler at 1f78:0281
+	// using `_vga_fbuffvid`.
+	auto showFullscreenPic = [&](uint16 picId,
+								  bool transparent) -> Common::KeyCode {
 		Picture pic;
 		if (!_picsArchive.getPicture(picId, pic)) {
 			warning("doSetup: PIC %u missing", (uint)picId);
-			return;
+			return Common::KEYCODE_INVALID;
 		}
 		Graphics::ManagedSurface scratch(320, 200,
 			Graphics::PixelFormat::createFormatCLUT8());
-		scratch.clear();
-		const int w = MIN<int>(pic.surface.w, 320);
-		const int h = MIN<int>(pic.surface.h, 200);
-		for (int row = 0; row < h; row++)
-			memcpy((byte *)scratch.getBasePtr(0, row),
-				   (const byte *)pic.surface.getBasePtr(0, row), w);
+		if (transparent) {
+			// Preserve the current screen so the help PIC's
+			// transparent pixels show the setup BG underneath.
+			Graphics::Surface *cur = g_system->lockScreen();
+			if (cur) {
+				for (int row = 0; row < 200; row++)
+					memcpy((byte *)scratch.getBasePtr(0, row),
+						   (const byte *)cur->getBasePtr(0, row), 320);
+				g_system->unlockScreen();
+			}
+			const byte transp = (byte)(pic.flags >> 8);
+			scratch.transBlitFrom(pic.surface, (uint32)transp);
+		} else {
+			scratch.clear();
+			scratch.simpleBlitFrom(pic.surface);
+		}
 		g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
 								   0, 0, 320, 200);
 		g_system->updateScreen();
@@ -813,13 +828,16 @@ void EEMEngine::doSetup() {
 			Common::Event ev;
 			while (g_system->getEventManager()->pollEvent(ev)) {
 				if (ev.type == Common::EVENT_QUIT ||
-					ev.type == Common::EVENT_RETURN_TO_LAUNCHER ||
-					ev.type == Common::EVENT_KEYDOWN ||
-					ev.type == Common::EVENT_LBUTTONDOWN)
-					return;
+					ev.type == Common::EVENT_RETURN_TO_LAUNCHER)
+					return Common::KEYCODE_ESCAPE;
+				if (ev.type == Common::EVENT_KEYDOWN)
+					return ev.kbd.keycode;
+				if (ev.type == Common::EVENT_LBUTTONDOWN)
+					return Common::KEYCODE_INVALID;
 			}
 			g_system->delayMillis(15);
 		}
+		return Common::KEYCODE_ESCAPE;
 	};
 
 	auto leaveSetup = [&]() {
@@ -939,22 +957,48 @@ void EEMEngine::doSetup() {
 				continue;
 			}
 
-			// Help (button [9]). Original calls `_InterfaceHelp(1)`,
-			// which walks a sequence of help-pic IDs from a runtime
-			// table we haven't fully decoded. PIC 0x4F is the in-game
-			// help backdrop the briefing chain reuses; we show that
-			// as a stub until the full table is wired.
+			// Help (button [9]). Original `_InterfaceHelp(1) @
+			// 1560:0205` walks the help-pic table at `29be:00c8`:
+			// each `num` slot is 5 bytes ??? count + two u16 PIC IDs.
+			// For num=1: count=2, pics = {0x0192, 0x01B1}. The
+			// original blits each pic with `_Rect_Move_Mask` ??? a
+			// MASKED blit whose transparent colour is the pic's
+			// `miscflags >> 8`, so the setup BG shows through. It
+			// also hides the cursor (`MOV [0x3a00], 0` + `_RemoveMouse`
+			// at the top of `_InterfaceHelp`, 1560:0216-021c). ESC
+			// at any point breaks out (1560:02b3 sets uVar5 = count).
 			if (kHelpBtn.contains(mx, my)) {
-				showFullscreenPic(0x4F);
+				static const uint16 kHelp1Pics[] = { 0x0192, 0x01B1 };
+				CursorMan.showMouse(false);
+				for (uint i = 0; i < ARRAYSIZE(kHelp1Pics); i++) {
+					// Re-render the setup BG before each help PIC so
+					// each one overlays a clean canvas. Without this,
+					// `showFullscreenPic`'s `lockScreen` snapshot would
+					// pick up the previous PIC and the two help cards
+					// would composite together. Mirrors the original's
+					// `_vga_fvidvid(0)` call at the tail of every
+					// `_InterfaceHelp` iteration (1560:02e5), which
+					// restores the back-buffer BG between cards.
+					draw();
+					const Common::KeyCode k =
+						showFullscreenPic(kHelp1Pics[i], /*transparent=*/true);
+					if (k == Common::KEYCODE_ESCAPE)
+						break;
+				}
+				CursorMan.showMouse(true);
 				dirty = true;
 				continue;
 			}
 
 			// Credits (button [11]). Original handler at 1f78:025a
-			// loads PIC 0x208 and blits it fullscreen, then waits
-			// for any input.
+			// loads PIC 0x208, hides the cursor (`MOV [0x3a00], 0`
+			// at 1f78:0269 + `_RemoveMouse @ 1000:542f` at 1f78:026F),
+			// blits it fullscreen via `_vga_fbuffvid` (raw copy, no
+			// mask), then waits for any input.
 			if (kCreditsBtn.contains(mx, my)) {
-				showFullscreenPic(0x208);
+				CursorMan.showMouse(false);
+				showFullscreenPic(0x208, /*transparent=*/false);
+				CursorMan.showMouse(true);
 				// PIC 0x208 has its own palette baked into the BG
 				// dump via `_GetPicture`; the original restores via
 				// `_GetPalette` on return. Reset to setup palette


Commit: 754e723072acc7bbc79c86a451dcecfa1d5d858d
    https://github.com/scummvm/scummvm/commit/754e723072acc7bbc79c86a451dcecfa1d5d858d
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:20:44+02:00

Commit Message:
EEM: setup screen accesible in zoom map screen

Changed paths:
    engines/eem/ui.cpp


diff --git a/engines/eem/ui.cpp b/engines/eem/ui.cpp
index 9822cf9f53d..87ab366765e 100644
--- a/engines/eem/ui.cpp
+++ b/engines/eem/ui.cpp
@@ -2291,11 +2291,18 @@ void EEMEngine::doBigMap() {
 				const int kSliderRangeY = mapH - kMapWinH;
 
 				if (kSetupBtn.contains(ev.mouse.x, ev.mouse.y)) {
-					// Setup button on detail too ??? `_NextScreen = 6` in
-					// the original. We treat it the same way: bail back
-					// to case selection.
-					_mystery.clear();
-					_nextScreen = kScreenInvalid;
+					// `_DoMapScreen @ 20fe:1560` writes `_NextScreen
+					// = 6` (= kScreenSetup) and `INC [BP-8]` to bail
+					// out of the detail loop ??? verified via the byte
+					// search for `c7 06 16 79 06 00`, which finds the
+					// imm at exactly this site and `_DoBigMap @
+					// 20fe:0c33`. Same `SetupButtonRect @ 29be:15ce`
+					// rect used by both the overview and the detail
+					// (no per-screen rect duplication in the binary).
+					// The detail/zoom state is lost on return because
+					// the screen driver re-enters BigMap at stage 1 ???
+					// this matches the original behaviour.
+					_nextScreen = kScreenSetup;
 					return;
 				}
 				if (kArrowYUp.contains(ev.mouse.x, ev.mouse.y)) {


Commit: 92c6607481ab1258822aa61cde27ff0116e09e8f
    https://github.com/scummvm/scummvm/commit/92c6607481ab1258822aa61cde27ff0116e09e8f
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:20:45+02:00

Commit Message:
EEM: improved navigation on PDA

Changed paths:
    engines/eem/graphics.cpp
    engines/eem/ui.cpp


diff --git a/engines/eem/graphics.cpp b/engines/eem/graphics.cpp
index 3955914eb98..351036153f0 100644
--- a/engines/eem/graphics.cpp
+++ b/engines/eem/graphics.cpp
@@ -114,9 +114,13 @@ void EEMEngine::doHelp() {
 void EEMEngine::doInterfaceHelp(uint num) {
 	// Mirrors `_InterfaceHelp(num)` @ 1560:0205. The original walks
 	// `HelpData @ 29be:00c8` (5-byte entries: u8 count, then up to 2
-	// u16 picIds), `_GetPicture`s each one, blits it fullscreen via
-	// `_Rect_Move_Mask(0, 0, ...)`, and waits for click / key. ESC ends
-	// the cycle; any other input advances to the next pic.
+	// u16 picIds), `_GetPicture`s each one, blits it via
+	// `_Rect_Move_Mask(0, 0, ...)` (a MASKED blit on top of the
+	// existing screen ??? transparent pixels show the caller's BG), and
+	// waits for click / key. ESC at `1560:02b3` skips to end. The
+	// function also hides the cursor at the top (`MOV [0x3a00], 0` at
+	// 1560:0216 + `_RemoveMouse @ 1000:542f` at 1560:021c) and
+	// restores it at the tail (`_DrawMouse @ 1000:5429` at 1560:02e8).
 	//
 	// `kHelpPics` lives at file scope above; see comment there for the
 	// HelpData decoding.
@@ -126,6 +130,26 @@ void EEMEngine::doInterfaceHelp(uint num) {
 	debugC(1, kDebugScript, "doInterfaceHelp(%u): showing pics 0x%x, 0x%x",
 		   num, kHelpPics[num][0], kHelpPics[num][1]);
 
+	// Snapshot the caller's screen ONCE so each help PIC overlays the
+	// same clean BG. Without this, after the first PIC is dismissed the
+	// second snapshot would include the first PIC's pixels and the two
+	// would composite together ??? same gotcha as the setup-screen help
+	// loop fix in `doSetup`.
+	Graphics::ManagedSurface bg(320, 200,
+		Graphics::PixelFormat::createFormatCLUT8());
+	{
+		Graphics::Surface *cur = g_system->lockScreen();
+		if (cur) {
+			for (int row = 0; row < 200; row++)
+				memcpy((byte *)bg.getBasePtr(0, row),
+					   (const byte *)cur->getBasePtr(0, row), 320);
+			g_system->unlockScreen();
+		}
+	}
+
+	const bool wasShown = CursorMan.isVisible();
+	CursorMan.showMouse(false);
+
 	for (uint i = 0; i < 2; i++) {
 		const uint16 picId = kHelpPics[num][i];
 		Picture pic;
@@ -136,24 +160,15 @@ void EEMEngine::doInterfaceHelp(uint num) {
 		debugC(1, kDebugScript, "doInterfaceHelp: pic 0x%x = %dx%d flags=0x%x",
 			   picId, pic.surface.w, pic.surface.h, pic.flags);
 
-		// Compose a 320x200 frame (cleared) and blit the help pic at (0,0)
-		// with the original's masked-blit semantics: pixels equal to the
-		// pic's sub-mode (high byte of `pic[0]`, see `_Rect_Move_Mask`
-		// param_10 at 1000:03fc) are treated as transparent and skipped.
+		// Compose a 320x200 frame from the clean BG snapshot and overlay
+		// the help pic with `transBlitFrom` ??? `Graphics::ManagedSurface`'s
+		// masked blit (transparent colour = the pic's `flags >> 8`,
+		// matching `_Rect_Move_Mask`'s param_10 at 1000:03fc).
 		Graphics::ManagedSurface scratch(320, 200,
 			Graphics::PixelFormat::createFormatCLUT8());
-		scratch.clear();
+		scratch.simpleBlitFrom(bg);
 		const byte transp = (byte)(pic.flags >> 8);
-		const int w = MIN<int>(pic.surface.w, 320);
-		const int h = MIN<int>(pic.surface.h, 200);
-		for (int row = 0; row < h; row++) {
-			const byte *src = (const byte *)pic.surface.getBasePtr(0, row);
-			byte *dst = (byte *)scratch.getBasePtr(0, row);
-			for (int col = 0; col < w; col++) {
-				if (src[col] != transp)
-					dst[col] = src[col];
-			}
-		}
+		scratch.transBlitFrom(pic.surface, (uint32)transp);
 		g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
 								   0, 0, 320, 200);
 		g_system->updateScreen();
@@ -165,6 +180,8 @@ void EEMEngine::doInterfaceHelp(uint num) {
 			while (g_system->getEventManager()->pollEvent(ev)) {
 				if (ev.type == Common::EVENT_QUIT ||
 					ev.type == Common::EVENT_RETURN_TO_LAUNCHER) {
+					if (wasShown)
+						CursorMan.showMouse(true);
 					return;
 				}
 				if (ev.type == Common::EVENT_LBUTTONDOWN) {
@@ -187,6 +204,9 @@ void EEMEngine::doInterfaceHelp(uint num) {
 		if (escape)
 			break;
 	}
+
+	if (wasShown)
+		CursorMan.showMouse(true);
 }
 
 void EEMEngine::setPartnerEraseBg(const Graphics::ManagedSurface *bg) {
diff --git a/engines/eem/ui.cpp b/engines/eem/ui.cpp
index 87ab366765e..9375f894a11 100644
--- a/engines/eem/ui.cpp
+++ b/engines/eem/ui.cpp
@@ -1425,7 +1425,13 @@ void EEMEngine::doNotebook() {
 	const Common::Rect kBtnPagePrev(226, 174, 247, 190);  // [6] PAGE PREV
 	const Common::Rect kBtnMap     (  7, 177,  57, 200);  // [7] MAP
 	const Common::Rect kBtnSite    ( 35, 111,  56, 136);  // [8] SITE
-	const Common::Rect kNoteArea   ( 66,  79, 267, 174);  // [10] note area
+	const Common::Rect kBtnHelp2   (267, 174, 288, 190);  // [10] extra HELP
+	// (`_NoteButtons @ 29be:0147` actually has rect [10] at
+	// (267,174,288,190) ??? small button on the right of the bottom
+	// bar that the original handler dispatch table at 161e:04ec
+	// routes to `_InterfaceHelp(0)` again. Earlier this rect was
+	// mis-noted as a "note area" of (66,79,267,174) ??? that
+	// rectangle exists nowhere in the binary's button table.)
 
 	CursorMan.showMouse(true);
 
@@ -1514,26 +1520,31 @@ void EEMEngine::doNotebook() {
 					dirty = true;
 					continue;
 				}
-				if (kNoteArea.contains(ev.mouse.x, ev.mouse.y)) {
-					// Toggle the selection on whichever clue's text
-					// the click landed in. The original calls
-					// `_InterfaceHelp` here; that's the help screen,
-					// not selection ??? selection is in the Accuse
-					// screen. We use the area for selection because
-					// keyboard 1..9 toggling is awkward, and the
-					// resulting `_NoteSelected` state is what
-					// `_SolvedCheck` reads.
-					for (uint i = 0; i < _notebookSlotRects.size(); i++) {
-						if (_notebookSlotRects[i].contains(ev.mouse.x,
-														   ev.mouse.y)) {
-							const uint clueId = _notebookSlotClues[i];
-							_mystery._noteSelected[clueId] ^= 1;
-							dirty = true;
-							break;
-						}
-					}
+				if (kBtnHelp2.contains(ev.mouse.x, ev.mouse.y)) {
+					// `_NoteButtons[10]` ??? handler 0x03f9 = same
+					// `_InterfaceHelp(0)` as button [1].
+					doInterfaceHelp(0);
+					dirty = true;
 					continue;
 				}
+				// Click on a clue's slot rect ??? toggle selection. The
+				// original `_DoNotebook` doesn't do this ??? note
+				// selection lives in the accuse screen there ??? but
+				// keyboard 1..9 toggling is awkward, and the resulting
+				// `_NoteSelected` state is what `_SolvedCheck` reads
+				// either way. Slot rects are the per-clue rectangles
+				// `drawNotebookFrame` publishes, so this just
+				// reproduces the visible-text-bbox click without the
+				// previous bogus outer-area gate.
+				for (uint i = 0; i < _notebookSlotRects.size(); i++) {
+					if (_notebookSlotRects[i].contains(ev.mouse.x,
+													   ev.mouse.y)) {
+						const uint clueId = _notebookSlotClues[i];
+						_mystery._noteSelected[clueId] ^= 1;
+						dirty = true;
+						break;
+					}
+				}
 			}
 		}
 		if (exitFlag)
@@ -1982,6 +1993,12 @@ void EEMEngine::doGallery() {
 			drawGalleryFrame(gd, num, slotRects, slotSuspect);
 			lastDraw = now;
 		}
+		// `g_system->updateScreen()` is what tells the framework to
+		// re-render the cursor at its current mouse position; without
+		// it here, the cursor only refreshes when `drawGalleryFrame`
+		// runs (every 100 ms) and visibly lags the mouse. Match
+		// `doNotebook`'s per-tick `updateScreen()` cadence (line 1548).
+		g_system->updateScreen();
 		g_system->delayMillis(15);
 	}
 }


Commit: d87214f047517949fbaa941fa107d740d0cd19f6
    https://github.com/scummvm/scummvm/commit/d87214f047517949fbaa941fa107d740d0cd19f6
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:20:45+02:00

Commit Message:
EEM: basic accusation flow in the PDA

Changed paths:
    engines/eem/graphics.cpp
    engines/eem/mystery.cpp
    engines/eem/mystery.h
    engines/eem/ui.cpp


diff --git a/engines/eem/graphics.cpp b/engines/eem/graphics.cpp
index 351036153f0..0718f6c1471 100644
--- a/engines/eem/graphics.cpp
+++ b/engines/eem/graphics.cpp
@@ -163,12 +163,20 @@ void EEMEngine::doInterfaceHelp(uint num) {
 		// Compose a 320x200 frame from the clean BG snapshot and overlay
 		// the help pic with `transBlitFrom` ??? `Graphics::ManagedSurface`'s
 		// masked blit (transparent colour = the pic's `flags >> 8`,
-		// matching `_Rect_Move_Mask`'s param_10 at 1000:03fc).
+		// matching `_Rect_Move_Mask`'s param_10 at 1000:03fc). Pass an
+		// explicit `destPos` of (0, 0) ??? the no-destPos overload at
+		// managed_surface.cpp:738 scales src to fill `this`'s rect,
+		// stretching the help PIC to 320x200 instead of placing it at
+		// native size. The original `_Rect_Move_Mask` passes destX=0,
+		// destY=0 with copy-width = pic[+4] (= `pic.surface.w`) and
+		// copy-height = pic[+2] (= `pic.surface.h`) ??? i.e. native size,
+		// not stretched.
 		Graphics::ManagedSurface scratch(320, 200,
 			Graphics::PixelFormat::createFormatCLUT8());
 		scratch.simpleBlitFrom(bg);
 		const byte transp = (byte)(pic.flags >> 8);
-		scratch.transBlitFrom(pic.surface, (uint32)transp);
+		scratch.transBlitFrom(pic.surface, Common::Point(0, 0),
+							  (uint32)transp);
 		g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
 								   0, 0, 320, 200);
 		g_system->updateScreen();
diff --git a/engines/eem/mystery.cpp b/engines/eem/mystery.cpp
index e3b4051a14a..39c854535d9 100644
--- a/engines/eem/mystery.cpp
+++ b/engines/eem/mystery.cpp
@@ -224,6 +224,31 @@ const byte *Mystery::mapEntry(uint siteNum) const {
 	return _data.data() + off;
 }
 
+bool Mystery::isGuilty(uint suspectIdx) const {
+	// `_WITCH @ 1df2:089f`: `if (GalleryData[i*0x46 + 0x02] == -1)
+	// _DisplayCorrect(); else _DisplayAlibi(...)`. Innocent suspects
+	// store their alibi-text TextBlock offset at +0x02; the guilty
+	// one stores the sentinel 0xFFFF.
+	const byte *gd = galleryData();
+	if (!gd || suspectIdx >= _numSuspects)
+		return false;
+	const uint16 off = READ_LE_UINT16(gd + suspectIdx * 0x46 + 0x02);
+	return off == 0xFFFF;
+}
+
+uint16 Mystery::alibiTextOffset(uint suspectIdx) const {
+	const byte *gd = galleryData();
+	if (!gd || suspectIdx >= _numSuspects)
+		return 0xFFFF;
+	return READ_LE_UINT16(gd + suspectIdx * 0x46 + 0x02);
+}
+
+const byte *Mystery::solvedClueBlock() const {
+	if (!isLoaded() || _solvedOffset == 0 || _solvedOffset >= _data.size())
+		return nullptr;
+	return _data.data() + _solvedOffset;
+}
+
 int Mystery::selectedPoints() const {
 	const byte *ni = noteIndex();
 	const uint16 cnt = noteIndexCount();
diff --git a/engines/eem/mystery.h b/engines/eem/mystery.h
index 96da8666f1b..dadaec00a75 100644
--- a/engines/eem/mystery.h
+++ b/engines/eem/mystery.h
@@ -152,6 +152,23 @@ public:
 	/// True when `selectedPoints() > 99`. Mirrors `_SolvedCheck`.
 	bool solvedCheck() const { return selectedPoints() > 99; }
 
+	/// True iff suspect @p suspectIdx is the case's guilty party. The
+	/// guilty marker is `GalleryData[suspectIdx * 0x46 + 0x02] ==
+	/// 0xFFFF` ??? innocent suspects store their alibi text offset there;
+	/// the guilty suspect uses the sentinel. Verified at `_WITCH @
+	/// 1df2:089f` (`if (psVar1->field_0x2 == -1) _DisplayCorrect();
+	/// else _DisplayAlibi(...)`).
+	bool isGuilty(uint suspectIdx) const;
+
+	/// TextBlock offset of suspect @p suspectIdx's alibi text. Returns
+	/// 0xFFFF for the guilty suspect (no alibi).
+	uint16 alibiTextOffset(uint suspectIdx) const;
+
+	/// Pointer to the win-clueblock (`MysteryIndex[+0x10]` =
+	/// `_solvedOffset`). Mirrors `_DisplayCorrect`'s
+	/// `_DisplayClue(_Mystery + MysteryIndex[+0x10], 0)` at 1df2:0769.
+	const byte *solvedClueBlock() const;
+
 	/// Per-mystery runtime state, zeroed at load time.
 	uint8  _cluesFound[kCluesFoundCap]   = {};
 	uint8  _noteSelected[kCluesFoundCap] = {};  ///< Mirror `_NoteSelected`
diff --git a/engines/eem/ui.cpp b/engines/eem/ui.cpp
index 9375f894a11..fa27ce1cf86 100644
--- a/engines/eem/ui.cpp
+++ b/engines/eem/ui.cpp
@@ -816,7 +816,12 @@ void EEMEngine::doSetup() {
 				g_system->unlockScreen();
 			}
 			const byte transp = (byte)(pic.flags >> 8);
-			scratch.transBlitFrom(pic.surface, (uint32)transp);
+			// Explicit destPos ??? the no-destPos overload of
+			// `transBlitFrom` (managed_surface.cpp:738) stretches
+			// src to dst dimensions, scaling the PIC to 320x200.
+			// The original `_Rect_Move_Mask` blits at native size.
+			scratch.transBlitFrom(pic.surface, Common::Point(0, 0),
+								  (uint32)transp);
 		} else {
 			scratch.clear();
 			scratch.simpleBlitFrom(pic.surface);
@@ -2834,25 +2839,43 @@ void EEMEngine::doAccuse() {
 	if (picked < 0)
 		return;
 
-	// Real chain evaluation: sum point values of clues the player marked
-	// "selected" in the notebook. Mirrors `_SolvedCheck` @ 1df2:00ec.
-	const int points = _mystery.selectedPoints();
-	const bool guessedRight = _mystery.solvedCheck();
-	debugC(1, kDebugScript, "doAccuse: picked=%d selectedPts=%d -> %s",
-		   picked, points, guessedRight ? "correct" : "wrong");
-
-	// If the player hasn't marked any evidence yet, give them a hint
-	// rather than an instant fail. Mirrors the original "We're not ready
-	// to solve this mystery yet..." string at 29be:10f0.
-	if (points == 0 && _font.isLoaded()) {
+	// Real chain evaluation. Mirrors the original two-gate accusation:
+	//   1. `_AccuseEntry @ 1df2:0ff8` checks `_GetFoundPoints() >= 100`
+	//      ??? gates whether the suspect picker is even reachable. We
+	//      replicate this with `_SolvedCheck ??? selectedPoints > 99`,
+	//      since our port marks clues in the notebook (port deviation;
+	//      the original gates on the auto-sorted top-5 found clues
+	//      via `_GetFoundPoints @ 1df2:0098`).
+	//   2. `_WITCH @ 1df2:089f` checks `GalleryData[picked*0x46+0x02] ==
+	//      0xFFFF`. Innocent suspects store an alibi-text TextBlock
+	//      offset there; the guilty one uses the sentinel.
+	// Both must hold for `_DisplayCorrect`; otherwise it's an alibi.
+	const int points          = _mystery.selectedPoints();
+	const bool enoughEvidence = _mystery.solvedCheck();
+	const bool pickedGuilty   = _mystery.isGuilty((uint)picked);
+	const bool guessedRight   = enoughEvidence && pickedGuilty;
+	debugC(1, kDebugScript,
+		   "doAccuse: picked=%d selectedPts=%d evidence=%s guilty=%s -> %s",
+		   picked, points,
+		   enoughEvidence ? "yes" : "no",
+		   pickedGuilty ? "yes" : "no",
+		   guessedRight ? "correct" : "wrong");
+
+	// Insufficient evidence: same hint the original shows when
+	// `_AccuseEntry` returns 0 (string at `_KDTextIndex[0]`).
+	if (!enoughEvidence && _font.isLoaded()) {
 		Graphics::ManagedSurface scratch(320, 200,
 			Graphics::PixelFormat::createFormatCLUT8());
 		scratch.clear();
 		_font.drawWordWrapped(&scratch, 16, 80, 288,
-			"We're not ready to solve this mystery yet. "
-			"Let's keep investigating until we have some "
-			"more solid evidence to make our case! "
-			"(Press N in the site screen to mark clues.)",
+			points == 0
+				? "We're not ready to solve this mystery yet. "
+				  "Let's keep investigating until we have some "
+				  "more solid evidence to make our case! "
+				  "(Press N in the site screen to mark clues.)"
+				: "We don't have quite enough solid evidence yet. "
+				  "Let's review our notes and find a few more "
+				  "clues before we accuse anyone.",
 			0xF);
 		g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
 								   0, 0, 320, 200);
@@ -2861,131 +2884,72 @@ void EEMEngine::doAccuse() {
 		return;
 	}
 
-	// Pick the ending based on the chain. For a correct accusation the
-	// original would call `_DisplayClue(_Mystery + AChain[0])`, play
-	// SCRAPBK.ANI and save progress. We load the matching `E<n>.BIN`
-	// ending text and render its pages with prev/next navigation.
-	const int endingNum = guessedRight ? picked : 0;
-	const Common::String fname = Common::String::format("E%d.BIN", endingNum);
-	Common::File f;
-	if (!f.open(Common::Path(fname))) {
-		warning("doAccuse: %s missing", fname.c_str());
-		return;
-	}
-
-	// E<n>.BIN format (verified against `_DisplayEndingPage` @ 1df2:044c):
-	//   u16 numPages
-	//   per page (10 bytes header + NUL-string):
-	//     u16 picNum
-	//     u16 x1, y1, x2, y2  (story rect)
-	//     bytes[] NUL-terminated text
-	const uint32 fileLen = f.size();
-	Common::Array<byte> blob(fileLen);
-	if (f.read(blob.data(), fileLen) != fileLen)
-		return;
-	const byte *e = blob.data();
-	const uint16 pages = READ_LE_UINT16(e);
-
-	uint pageIdx = 0;
-
-	while (!shouldQuit()) {
-		// Walk to pageIdx.
-		uint pos = 2;
-		uint cur = 0;
-		while (cur < pageIdx && pos + 10 < fileLen) {
-			const char *t = (const char *)(e + pos + 10);
-			pos += 10 + strlen(t) + 1;
-			cur++;
+	// Wrong suspect: render alibi flow. Mirrors `_DisplayAlibi @
+	// 1df2:0145` ??? shows the suspect's alibi text + their picture on
+	// PIC 0x3e, then returns to the accuse gallery for another try.
+	// Skips MIDI 6 (alibi music) and the per-partner voice line ??? the
+	// text rendering alone is enough to convey "this suspect is
+	// innocent". `_FirstTry` is cleared so a subsequent correct pick
+	// no longer counts as a first-try win (1df2:0445 -> _FirstTry=0).
+	if (!guessedRight) {
+		const uint16 alibiOff = _mystery.alibiTextOffset((uint)picked);
+		Common::String alibi;
+		if (gd && alibiOff != 0xFFFF) {
+			const char *raw = _mystery.textAt(alibiOff);
+			if (raw)
+				alibi = parseString(raw, _playerName, _partner);
 		}
-		if (pos + 10 >= fileLen)
-			break;
-
-		const uint16 picNum = READ_LE_UINT16(e + pos + 0);
-		const uint16 x1     = READ_LE_UINT16(e + pos + 2);
-		const uint16 y1     = READ_LE_UINT16(e + pos + 4);
-		const uint16 x2     = READ_LE_UINT16(e + pos + 6);
-		const uint16 y2     = READ_LE_UINT16(e + pos + 8);
-		const char *raw     = (const char *)(e + pos + 10);
-		const Common::String txt = parseString(raw, _playerName, _partner);
+		Picture alibiBg;
+		const bool haveAlibiBg =
+			_picsArchive.getPicture(0x3e, alibiBg);
+		Picture suspect;
+		const uint16 picId = gd
+			? READ_LE_UINT16(gd + (uint)picked * 0x46)
+			: 0;
+		const bool haveSuspect = picId != 0 &&
+			_picsArchive.getPicture(picId, suspect);
 
 		Graphics::ManagedSurface scratch(320, 200,
 			Graphics::PixelFormat::createFormatCLUT8());
 		scratch.clear();
-
-		// Page background.
-		if (picNum != 0) {
-			Picture bg;
-			if (_picsArchive.getPicture(picNum, bg)) {
-				const int w = MIN<int>(bg.surface.w, 320);
-				const int h = MIN<int>(bg.surface.h, 200);
-				for (int row = 0; row < h; row++) {
-					memcpy((byte *)scratch.getBasePtr(0, row),
-						   (const byte *)bg.surface.getBasePtr(0, row), w);
-				}
-			}
+		if (haveAlibiBg) {
+			scratch.simpleBlitFrom(alibiBg.surface);
 		}
-
-		if (_font.isLoaded()) {
-			Common::String banner = "Not enough evidence";
-			if (guessedRight)
-				banner = _mystery._firstTry ? "CORRECT - FIRST TRY!" : "CORRECT!";
-			_font.drawString(&scratch, banner, 8, 4, 320, 0xF);
-			_font.drawString(&scratch, Common::String::format("Evidence: %d/100  Suspect: %d",
-									   points, picked + 1), 8, 16, 320, 0xF);
-			const int wrapW = MAX<int>(16, x2 - x1);
-			const int wrapY = MAX<int>(28, (int)y1);
-			(void)y2;
-			_font.drawWordWrapped(&scratch, x1, wrapY, wrapW, txt, 0xF);
-			_font.drawString(&scratch, Common::String::format("page %u/%u  (Left/Right or click)",
-									   pageIdx + 1, pages), 8, 188, 320, 0xF);
+		if (haveSuspect) {
+			// Original `_DisplayAlibi` blits the suspect at (0x82, py)
+			// where py varies by balloon size; we use a fixed centred
+			// position since we don't render the balloon shapes yet.
+			const byte transp = (byte)(suspect.flags >> 8);
+			scratch.transBlitFrom(suspect.surface,
+								  Common::Point(0x82, 0x40),
+								  (uint32)transp);
+		}
+		if (_font.isLoaded() && !alibi.empty()) {
+			_font.drawWordWrapped(&scratch, 16, 8, 200, alibi, 0xF);
+			_font.drawString(&scratch, "(click / ESC: back)",
+							 16, 188, 200, 0xF);
 		}
-
 		g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
 								   0, 0, 320, 200);
 		g_system->updateScreen();
+		waitForInput(20000);
 
-		// Page navigation.
-		bool advance = false;
-		bool back    = false;
-		bool exit    = false;
-		while (!advance && !back && !exit && !shouldQuit()) {
-			Common::Event ev;
-			while (g_system->getEventManager()->pollEvent(ev)) {
-				if (ev.type == Common::EVENT_QUIT ||
-					ev.type == Common::EVENT_RETURN_TO_LAUNCHER) {
-					exit = true; break;
-				}
-				if (ev.type == Common::EVENT_LBUTTONDOWN) {
-					advance = true; break;
-				}
-				if (ev.type == Common::EVENT_KEYDOWN) {
-					if (ev.kbd.keycode == Common::KEYCODE_ESCAPE)
-						exit = true;
-					else if (ev.kbd.keycode == Common::KEYCODE_LEFT)
-						back = true;
-					else
-						advance = true;
-					break;
-				}
-			}
-			g_system->updateScreen();
-			g_system->delayMillis(15);
-		}
-		if (exit)
-			break;
-		if (advance) {
-			if (pageIdx + 1 >= pages)
-				break;
-			pageIdx++;
-		} else if (back) {
-			if (pageIdx > 0)
-				pageIdx--;
-		}
+		_mystery._firstTry = false;
+		// `_DisplayAlibi @ 1df2:043f` writes `_NextScreen =
+		// _LastScreen` so the player drops back to wherever they
+		// accused from (typically the site loop). With no mystery
+		// state to unload, the site loop resumes naturally.
+		_nextScreen = _lastScreen != kScreenInvalid
+						? (ScreenId)_lastScreen : kScreenSite;
+		return;
 	}
 
-	// Mirror `_DisplayCorrect`'s scrap-book animation + solved tracking +
-	// auto-save (the original calls `_SavePlayerRecord` after a win).
-	if (guessedRight) {
+	// Right suspect ??? full win flow. Mirrors `_DisplayCorrect @
+	// 1df2:073c`: mark mystery solved, advance chain stage if the
+	// tier is complete, swap MIDI to the win cue, run SCRAPBK.ANI,
+	// show the per-mystery ending, save the profile, return to the
+	// action menu (`_NextScreen = 0xc` at 1df2:0895).
+	{
 		const uint mn = _mystery.number();
 		if (mn < sizeof(_mysteriesSolved)) {
 			_mysteriesSolved[mn] = _mystery._firstTry ? 2 : 1;
@@ -3059,14 +3023,6 @@ void EEMEngine::doAccuse() {
 		// 1df2:08a4 do the same.
 		_mystery.clear();
 		_nextScreen = kScreenAction;
-	} else {
-		_mystery._firstTry = false;
-		// `_DisplayAlibi @ 1df2:043f` writes `_NextScreen =
-		// _LastScreen` ??? drop back to wherever the player accused
-		// from. With no mystery state to unload, the site loop
-		// resumes naturally.
-		_nextScreen = _lastScreen != kScreenInvalid
-						? (ScreenId)_lastScreen : kScreenSite;
 	}
 }
 
@@ -3079,6 +3035,15 @@ void EEMEngine::drawAccuseGallery(uint8 numSuspects, const byte *gd,
 	// PIC 0x3f backdrop, suspect portraits at the 5 fixed slots
 	// (`kGallerySlots` in this file's anon namespace), and a 1-px
 	// outline (palette index 0xFE) around the highlighted slot.
+	//
+	// Partner sprite at (5, 0x50): the original `_DoAccuse @ 1df2:0bdd`
+	// registers `_NewAnimation(5, 0x50, partnerCells, script=2, prior=1)`
+	// (1df2:0c30) BEFORE calling `_DoAccuseGallery`, then `_DrawGallery`
+	// calls `_DrawActiveAnimations` (158f:00a3) which re-renders the
+	// slot every frame. Without an explicit blit here, our port's
+	// accuse screen comes out partner-less. Anim CELLS are 2 (Jake) /
+	// 0x10 (Jenny); SCRIPT key is 0x02 for both partners (matches the
+	// `CONCAT22(2, ...)` arg verified at 1df2:0c2e).
 	Picture accuseBg;
 	const bool haveAccuseBg = _picsArchive.getPicture(0x3f, accuseBg);
 
@@ -3094,6 +3059,21 @@ void EEMEngine::drawAccuseGallery(uint8 numSuspects, const byte *gd,
 		}
 	}
 
+	// Partner sprite, drawn BEFORE portraits so the suspect grid
+	// covers it where they overlap (the gallery slots start at
+	// y=14 / y=90, partner is at y=0x50=80 ??? no overlap, so order
+	// is purely defensive).
+	const uint partnerAnim = (_partner == 0) ? 2 : 0x10;
+	Animation partnerAni;
+	if (_aniArchive.loadAnimation(partnerAnim, partnerAni) &&
+		!partnerAni.empty()) {
+		const uint32 now = g_system->getMillis();
+		const uint frameIdx = partnerFrameAtTick(0x02,
+												  (uint)partnerAni.size(), now);
+		blitAnimFrameAnchored(scratch.surfacePtr(),
+							  partnerAni[frameIdx], 5, 0x50);
+	}
+
 	for (uint i = 0; i < numSuspects && i < Mystery::kGalleryCap; i++) {
 		slotRects[i] = Common::Rect();
 		slotSuspect[i] = -1;


Commit: 3b1738c38bf9c7dfa7603961bec4a65af09a597d
    https://github.com/scummvm/scummvm/commit/3b1738c38bf9c7dfa7603961bec4a65af09a597d
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:20:45+02:00

Commit Message:
EEM: basic scrapbook code

Changed paths:
    engines/eem/eem.h
    engines/eem/ui.cpp


diff --git a/engines/eem/eem.h b/engines/eem/eem.h
index ab7389a4b9e..345a6f8a36a 100644
--- a/engines/eem/eem.h
+++ b/engines/eem/eem.h
@@ -346,6 +346,17 @@ private:
 	/// so this same call covers the post-mystery scrapbook view from
 	/// the action menu.
 	void doShowEnding(uint num);
+
+	/// Walk every solved mystery in tier @p stage (1=Junior, 2=Senior,
+	/// 3=Master) and display each one's ending pages in sequence.
+	/// Mirrors `_ShowScrapbook(stage, 0) @ 1f78:0642`: the original
+	/// computes the mystery range from `(stage - 1) * 0x18 + 1` to
+	/// `(stage - 1) * 0x18 + 0x18` and skips entries whose
+	/// `_3f9b[i] == 0` (unsolved) so the scrapbook only contains
+	/// completed cases. Used by both the setup-screen ScrapBook
+	/// buttons and the action-menu "See ScrapBook 1/2/3" entries.
+	void doShowScrapbook(uint stage);
+
 	void doCaseSelection();
 	void doSiteLoop();
 
diff --git a/engines/eem/ui.cpp b/engines/eem/ui.cpp
index fa27ce1cf86..4d092f292cb 100644
--- a/engines/eem/ui.cpp
+++ b/engines/eem/ui.cpp
@@ -635,6 +635,39 @@ void EEMEngine::doShowEnding(uint num) {
 	}
 }
 
+void EEMEngine::doShowScrapbook(uint stage) {
+	// Mirrors `_ShowScrapbook(stage, 0) @ 1f78:0642`. Walk the
+	// stage's mystery range and call `_DisplayEnding` on each solved
+	// mystery. The original splits the 55 cases into three tiers:
+	//   stage 1 (Junior) ??? mysteries  1..0x18 (24 cases)
+	//   stage 2 (Senior) ??? mysteries 0x19..0x30 (24 cases)
+	//   stage 3 (Master) ??? mysteries 0x31..0x36 (6 cases)
+	// Each tier's range is `lo = (stage-1)*0x18 + 1`, `hi = lo + 0x17`
+	// (verified at 1f78:064b: `iVar1 = (param_1 - 1) * 0x18; uVar2 =
+	// iVar1 + 1`). The current-stage filter at 1f78:065e
+	// (`if (DAT_2d5d_3f99 == param_1)`) skips unsolved mysteries
+	// inside the player's CURRENT tier ??? completed tiers show every
+	// mystery regardless. We mirror that exactly.
+	if (stage < 1 || stage > 3)
+		return;
+	const uint lo = (stage - 1) * 0x18 + 1;
+	const uint hi = lo + 0x17;
+	const bool currentTier = (stage == _chainStage);
+
+	for (uint m = lo; m <= hi; m++) {
+		if (m >= sizeof(_mysteriesSolved))
+			break;
+		// Current-tier filter (1f78:0664). Completed tiers show all
+		// 24 entries; the active tier hides unsolved ones because
+		// the player hasn't earned that scrapbook page yet.
+		if (currentTier && _mysteriesSolved[m] == 0)
+			continue;
+		doShowEnding(m);
+		if (shouldQuit())
+			return;
+	}
+}
+
 void EEMEngine::doSetup() {
 	// Mirrors `_DoSetup @ 1f78:044e`. The setup screen is BG `PIC 0x40`
 	// (loaded once on entry) with every label baked in ??? "Setup",
@@ -1022,7 +1055,35 @@ void EEMEngine::doSetup() {
 				return;
 			}
 
-			(void)kScrap1Btn; (void)kScrap2Btn; (void)kScrap3Btn;
+			// ScrapBook 1 / 2 / 3 (buttons [3] / [4] / [5]). Original
+			// handlers at 1f78:021F (`_ShowScrapbook(0, 1)`) /
+			// 1f78:022E (gated chain >= 2 / `_ShowScrapbook(0, 2)`) /
+			// 1f78:0244 (gated chain >= 3 / `_ShowScrapbook(0, 3)`).
+			// Convert the original's `(0, stage)` invocation into our
+			// `doShowScrapbook(stage)` (we collapse the param_1=0
+			// "no-current-mystery" indirection ??? relevant only for
+			// the post-win callsite).
+			auto runScrapbook = [&](uint stage) {
+				CursorMan.showMouse(false);
+				doShowScrapbook(stage);
+				CursorMan.showMouse(true);
+				setSitePalette(0);
+			};
+			if (kScrap1Btn.contains(mx, my)) {
+				runScrapbook(1);
+				dirty = true;
+				continue;
+			}
+			if (kScrap2Btn.contains(mx, my) && _chainStage >= 2) {
+				runScrapbook(2);
+				dirty = true;
+				continue;
+			}
+			if (kScrap3Btn.contains(mx, my) && _chainStage >= 3) {
+				runScrapbook(3);
+				dirty = true;
+				continue;
+			}
 		}
 		if (dirty)
 			draw();
@@ -1061,9 +1122,24 @@ void EEMEngine::doCaseSelection() {
 		"         See ScrapBook 2",
 		"         See ScrapBook 3"
 	};
-	// ScrapBooks aren't implemented yet ??? grey them so the player can't
-	// stop on them, mirroring the original `_Greys` mask.
-	const bool kPickEnabled[kNumPicks] = { true, true, false, false, false };
+	// ScrapBook entries are gated by chain stage exactly as the
+	// original `_ActionScreen @ 1c33:195b` does at 1c33:19f3-19f7:
+	//   stage 1 + nothing solved ??? ScrapBook 1/2/3 all greyed
+	//   stage 1 + ???1 solved      ??? ScrapBook 1 enabled, 2/3 greyed
+	//   stage 2                  ??? ScrapBook 1 enabled, 2 enabled, 3 greyed
+	//   stage 3                  ??? all three enabled
+	// (`_3f9b[i] != 0` over the tier's mystery range is the per-tier
+	// gate; we approximate "any in tier" by checking _chainStage and
+	// any solved flag.)
+	bool anySolved1 = false;
+	for (uint i = 1; i <= 0x18 && i < sizeof(_mysteriesSolved); i++)
+		if (_mysteriesSolved[i]) { anySolved1 = true; break; }
+	const bool scrap1On = anySolved1 || _chainStage >= 2;
+	const bool scrap2On = _chainStage >= 2;
+	const bool scrap3On = _chainStage >= 3;
+	const bool kPickEnabled[kNumPicks] = {
+		true, true, scrap1On, scrap2On, scrap3On
+	};
 	uint pick = kPickChoose;
 
 	const char *kSeparator = "----------------------------------";
@@ -1234,8 +1310,19 @@ void EEMEngine::doCaseSelection() {
 		return;
 	}
 
-	if (pick != kPickChoose) {
-		// ScrapBooks aren't implemented; bail back to the menu loop.
+	if (pick == kPickScrap1 || pick == kPickScrap2 || pick == kPickScrap3) {
+		// `_ActionScreen` handlers at 1c33:1B13 / 1B26 / 1B40 each
+		// call `_ShowScrapbook(0, stage)` for the matching tier
+		// (verified at the action-handler jumptable bytes
+		// `01 03 05 07 09 ff` paired with handlers at 1c33:1be1).
+		// The picker here is meant to LEAVE the mystery state untouched
+		// ??? viewing the scrapbook never starts a new case.
+		const uint stage = (pick == kPickScrap1) ? 1
+						 : (pick == kPickScrap2) ? 2 : 3;
+		CursorMan.showMouse(false);
+		doShowScrapbook(stage);
+		CursorMan.showMouse(true);
+		setSitePalette(0);
 		_mystery.clear();
 		return;
 	}
@@ -1976,6 +2063,12 @@ void EEMEngine::doGallery() {
 									e2.type == Common::EVENT_RETURN_TO_LAUNCHER)
 									return;
 							}
+							// Per-tick `updateScreen()` so the SDL cursor
+							// follows the mouse ??? without it the cursor
+							// freezes on entry to the MoreInfo screen
+							// (we never repaint here, so the cursor never
+							// gets drawn at its current position).
+							g_system->updateScreen();
 							g_system->delayMillis(20);
 						}
 						// Force gallery redraw immediately so the


Commit: b4dc4f62742265b7b62335720033115b3e37eeba
    https://github.com/scummvm/scummvm/commit/b4dc4f62742265b7b62335720033115b3e37eeba
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:20:46+02:00

Commit Message:
EEM: added more missing animations

Changed paths:
    engines/eem/ui.cpp


diff --git a/engines/eem/ui.cpp b/engines/eem/ui.cpp
index 4d092f292cb..45cea187910 100644
--- a/engines/eem/ui.cpp
+++ b/engines/eem/ui.cpp
@@ -1962,6 +1962,32 @@ void EEMEngine::doGallery() {
 									   (const byte *)galBg.surface.getBasePtr(0, row), bw);
 							}
 						}
+						// Partner sprite at (5, 0x50). The original
+						// `MoreInfo @ 158f:0419` calls
+						// `_RefreshGalleryBackground` (clears the
+						// portrait grid) but the partner anim slot
+						// registered by `_DoGallery` keeps painting
+						// at every `_UpdateAnimations` tick ??? the
+						// suspect detail pic covers the right side
+						// only (drawn at 0x94, 0xf), so the partner
+						// stays visible on the left. Without this
+						// blit the suspect-detail screen has no
+						// partner.
+						{
+							const uint partnerAnim =
+								(_partner == 0) ? 2 : 0x10;
+							Animation partnerAni;
+							if (_aniArchive.loadAnimation(partnerAnim,
+														   partnerAni) &&
+								!partnerAni.empty()) {
+								const uint32 now = g_system->getMillis();
+								const uint frameIdx =
+									partnerFrameAtTick(0x02,
+										(uint)partnerAni.size(), now);
+								blitAnimFrameAnchored(ms.surfacePtr(),
+									partnerAni[frameIdx], 5, 0x50);
+							}
+						}
 						// Full suspect picture at (0x94, 0xf).
 						Picture detail;
 						if (_picsArchive.getPicture(detailPic, detail)) {
@@ -2794,15 +2820,39 @@ void EEMEngine::doAccuse() {
 				if (haveBalloon && balloon.surface.h < 0x4e)
 					balloonY = (0x50 - balloon.surface.h) / 2;
 
+				// Render the gallery FIRST so the balloon snapshot
+				// includes the partner sprite. The original
+				// `_DoAccuseGallery @ 1df2:0a31` does this implicitly:
+				// `_NewAnimation` registered the partner slot at
+				// (5, 0x50) before reaching this point, then
+				// `_GetBackground(0x3f)` + `_DrawGallery` paint the
+				// portraits, and `_UpdateAnimations` keeps the partner
+				// visible underneath the balloon overlay. Without this,
+				// the player sees an 8-second partner-less screen
+				// while reading the hint.
+				drawAccuseGallery(num, gd, /*highlighted=*/-1,
+								  slotRects, slotSuspect);
+
 				Graphics::ManagedSurface ms(320, 200,
 					Graphics::PixelFormat::createFormatCLUT8());
 				ms.clear();
-				if (haveAccuseBg) {
-					const int bw = MIN<int>(accuseBg.surface.w, 320);
-					const int bh = MIN<int>(accuseBg.surface.h, 200);
-					for (int row = 0; row < bh; row++) {
-						memcpy((byte *)ms.getBasePtr(0, row),
-							   (const byte *)accuseBg.surface.getBasePtr(0, row), bw);
+				{
+					Graphics::Surface *cur = g_system->lockScreen();
+					if (cur) {
+						for (int row = 0; row < 200; row++)
+							memcpy((byte *)ms.getBasePtr(0, row),
+								   (const byte *)cur->getBasePtr(0, row), 320);
+						g_system->unlockScreen();
+					} else if (haveAccuseBg) {
+						// Fallback: lockScreen failed somehow; at least
+						// fill from PIC 0x3f so we don't render against
+						// stale memory.
+						const int bw = MIN<int>(accuseBg.surface.w, 320);
+						const int bh = MIN<int>(accuseBg.surface.h, 200);
+						for (int row = 0; row < bh; row++) {
+							memcpy((byte *)ms.getBasePtr(0, row),
+								   (const byte *)accuseBg.surface.getBasePtr(0, row), bw);
+						}
 					}
 				}
 				// Masked balloon blit ??? `_Rect_Move_Mask` (1000:03fc)


Commit: 256eff166c411dc493e6636e730ac726eef5f38a
    https://github.com/scummvm/scummvm/commit/256eff166c411dc493e6636e730ac726eef5f38a
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:20:46+02:00

Commit Message:
EEM: stop music/sound in certain points of the game

Changed paths:
    engines/eem/clues.cpp
    engines/eem/eem.cpp
    engines/eem/eem.h
    engines/eem/site.cpp


diff --git a/engines/eem/clues.cpp b/engines/eem/clues.cpp
index d2635e3823d..69a805fffd8 100644
--- a/engines/eem/clues.cpp
+++ b/engines/eem/clues.cpp
@@ -336,11 +336,21 @@ void EEMEngine::doInitClues() {
 			g_system->unlockScreen();
 			g_system->updateScreen();
 
-			// Wait 100 ms or until input.
+			// Wait 100 ms or until input. ESC also stops any pending
+			// voice / spool so audio doesn't bleed past the briefing
+			// when the player skips early ??? without this the per-clue
+			// voice line that `displayClue` would spool keeps playing
+			// after we've moved on to the MAP.
 			const uint32 wakeup = g_system->getMillis() + 100;
 			while (g_system->getMillis() < wakeup && !shouldQuit() && !skip) {
 				Common::Event ev;
 				while (g_system->getEventManager()->pollEvent(ev)) {
+					if (ev.type == Common::EVENT_KEYDOWN &&
+						ev.kbd.keycode == Common::KEYCODE_ESCAPE) {
+						interruptAudio();
+						skip = true;
+						break;
+					}
 					if (ev.type == Common::EVENT_LBUTTONDOWN ||
 						ev.type == Common::EVENT_KEYDOWN) {
 						skip = true;
@@ -437,6 +447,12 @@ void EEMEngine::doInitClues() {
 					   !shouldQuit() && !skip) {
 					Common::Event ev;
 					while (g_system->getEventManager()->pollEvent(ev)) {
+						if (ev.type == Common::EVENT_KEYDOWN &&
+							ev.kbd.keycode == Common::KEYCODE_ESCAPE) {
+							interruptAudio();
+							skip = true;
+							break;
+						}
 						if (ev.type == Common::EVENT_LBUTTONDOWN ||
 							ev.type == Common::EVENT_KEYDOWN) {
 							skip = true;
@@ -780,6 +796,13 @@ void EEMEngine::displayClue(const byte *clueBlock) {
 						ev.kbd.keycode == Common::KEYCODE_ESCAPE) {
 						advance = true;
 						skipAll = true;
+						// Cut the per-clue voice line that was just
+						// spooled ??? without this the voice keeps
+						// playing past the dialog dismissal and
+						// bleeds into the next screen (e.g. the
+						// case-briefing voice still talking on the
+						// MAP after ESC).
+						interruptAudio();
 						break;
 					}
 					if (ev.type == Common::EVENT_LBUTTONDOWN ||
diff --git a/engines/eem/eem.cpp b/engines/eem/eem.cpp
index 46fa0497f45..9cf6ab97843 100644
--- a/engines/eem/eem.cpp
+++ b/engines/eem/eem.cpp
@@ -456,6 +456,22 @@ bool EEMEngine::setAnmPalette(const Common::Path &anmPath) {
 	return true;
 }
 
+void EEMEngine::interruptAudio() {
+	// Mirrors `_CleanMysterySounds @ 202f:05a5` + `_StopMIDI @
+	// 20a2:0512` ??? the original calls both whenever the player aborts
+	// the opening-anim chain or dismisses the title (`_DoOpeningAnims
+	// @ 2520:082a` writes `_LoopMIDI = 0; _StopMIDI();` after the
+	// title-input loop). We expose the same combined stop on every
+	// ESC handler so currently-playing music + voice + spool actually
+	// halt instead of bleeding through into the next screen.
+	if (_audio) {
+		_audio->stopVoice();
+		_audio->stopSpool();
+	}
+	if (_music)
+		_music->stop();
+}
+
 void EEMEngine::playAnm(const Common::Path &path, uint frameDelayMs, bool holdLastFrame) {
 	ANMDecoder anm;
 	if (!anm.open(path)) {
@@ -483,7 +499,8 @@ void EEMEngine::playAnm(const Common::Path &path, uint frameDelayMs, bool holdLa
 		// the frame-rate calibration logic from _GetSpeedRating is wired up.
 		// ESC additionally sets `_skipIntro` so the opening-anim chain in
 		// run() bails out of the whole sequence instead of advancing to
-		// the next clip.
+		// the next clip ??? and stops every active audio channel so the
+		// theme music / voice spool don't bleed past the abort.
 		const uint32 frameStart = g_system->getMillis();
 		bool aborted = false;
 		while (g_system->getMillis() - frameStart < frameDelayMs && !aborted) {
@@ -496,8 +513,10 @@ void EEMEngine::playAnm(const Common::Path &path, uint frameDelayMs, bool holdLa
 					break;
 				}
 				if (event.type == Common::EVENT_KEYDOWN) {
-					if (event.kbd.keycode == Common::KEYCODE_ESCAPE)
+					if (event.kbd.keycode == Common::KEYCODE_ESCAPE) {
 						_skipIntro = true;
+						interruptAudio();
+					}
 					aborted = true;
 					break;
 				}
@@ -523,8 +542,10 @@ void EEMEngine::playAnm(const Common::Path &path, uint frameDelayMs, bool holdLa
 					break;
 				}
 				if (ev.type == Common::EVENT_KEYDOWN) {
-					if (ev.kbd.keycode == Common::KEYCODE_ESCAPE)
+					if (ev.kbd.keycode == Common::KEYCODE_ESCAPE) {
 						_skipIntro = true;
+						interruptAudio();
+					}
 					clicked = true;
 					break;
 				}
@@ -534,6 +555,12 @@ void EEMEngine::playAnm(const Common::Path &path, uint frameDelayMs, bool holdLa
 			g_system->updateScreen();
 			g_system->delayMillis(20);
 		}
+		// `_DoOpeningAnims @ 2520:0945` writes `_LoopMIDI = 0;
+		// _StopMIDI();` once the title-input loop exits ??? so the
+		// theme stops the moment the player dismisses the title,
+		// regardless of whether they used ESC or clicked.
+		if (_music)
+			_music->stop();
 	}
 }
 
@@ -549,7 +576,10 @@ void EEMEngine::blitAt(const Picture &pic, int x, int y) {
 
 void EEMEngine::waitForInput(uint32 maxMs) {
 	// ESC additionally raises `_skipIntro` so the opening-anim chain
-	// can fast-forward past the rest of the sequence.
+	// can fast-forward past the rest of the sequence, and stops any
+	// active audio so the theme / voice / spool don't bleed past
+	// the abort. Mirrors the `_CleanMysterySounds` + `_StopMIDI`
+	// pair around the title wait in `_DoOpeningAnims`.
 	const uint32 startMs = g_system->getMillis();
 	while (!shouldQuit() && (g_system->getMillis() - startMs < maxMs)) {
 		Common::Event event;
@@ -560,8 +590,10 @@ void EEMEngine::waitForInput(uint32 maxMs) {
 				return;
 			}
 			if (event.type == Common::EVENT_KEYDOWN) {
-				if (event.kbd.keycode == Common::KEYCODE_ESCAPE)
+				if (event.kbd.keycode == Common::KEYCODE_ESCAPE) {
 					_skipIntro = true;
+					interruptAudio();
+				}
 				return;
 			}
 		}
@@ -616,12 +648,25 @@ void EEMEngine::startTravelMusic() {
 	//   }
 	//
 	// Five travel tracks: MUS00000.XMI .. MUS00004.XMI, picked by
-	// `_SiteNumber % 5`. The original always loops travel music (the
-	// `_LoopMIDI` global isn't reset between site changes).
+	// `_SiteNumber % 5`. ONE-SHOT ??? `_DoOpeningAnims @ 2520:0945`
+	// resets `_LoopMIDI = 0` after the title-screen wait, and
+	// `_StartTravelMusic` doesn't write to it; combined with
+	// `_DoSiteLoop @ 168d:06c0` which waits for the track to play
+	// out and then calls `_StopMIDI()` before the interactive phase
+	// begins, the original effectively plays travel music ONCE
+	// during the entrance animation only ??? the site investigation
+	// itself runs without music. Our previous `loop=true` made the
+	// music never end, leaving travel music droning through site
+	// investigation, accuse, gallery, etc.
 	if (!_music || !_mystery.isLoaded())
 		return;
 	const uint num = _mystery._siteNumber % 5;
-	_music->playMus(num, /*loop=*/true);
+	_music->playMus(num, /*loop=*/false);
+}
+
+void EEMEngine::stopMusic() {
+	if (_music)
+		_music->stop();
 }
 
 void EEMEngine::syncSoundSettings() {
diff --git a/engines/eem/eem.h b/engines/eem/eem.h
index 345a6f8a36a..2d95c0102a1 100644
--- a/engines/eem/eem.h
+++ b/engines/eem/eem.h
@@ -320,6 +320,18 @@ private:
 	void playAnm(const Common::Path &path, uint frameDelayMs = 120,
 				 bool holdLastFrame = false);
 
+	/// Stop every active audio channel ??? voice, sound spool, and
+	/// MIDI. Mirrors the `_CleanMysterySounds @ 202f:05a5` +
+	/// `_StopMIDI @ 20a2:0512` pair the original triggers when the
+	/// player aborts the opening-anim chain or dismisses the title
+	/// (`_DoOpeningAnims @ 2520:082a` writes `_LoopMIDI = 0;
+	/// _StopMIDI();` after the title-input loop, and
+	/// `_CleanMysterySounds` is called twice ??? once after the loop
+	/// and once before TITLE.ANM). Called from every ESC handler in
+	/// the intro / title chain so the theme music + voice spool
+	/// don't bleed past the abort.
+	void interruptAudio();
+
 	// Screen handlers ??? port targets in screens/ later.
 	void showEAKidsLogo();
 	void showHighScoreLogo();
@@ -380,6 +392,12 @@ public:
 	/// music changes as the player travels between sites.
 	void startTravelMusic();
 
+	/// Stop any currently playing MIDI track. Mirrors `_StopMIDI @
+	/// 20a2:0512` ??? used by `_DoSiteLoop @ 168d:06c0` after the
+	/// one-shot travel track plays out and by `_DisplayCorrect /
+	/// _DisplayAlibi` between MIDI cues.
+	void stopMusic();
+
 	/// Forwarded from `Engine::syncSoundSettings`. Re-pulls the user's
 	/// `music_volume` slider into the MIDI player's `_masterVolume`,
 	/// otherwise the AdLib output stays at whatever the slider was at
diff --git a/engines/eem/site.cpp b/engines/eem/site.cpp
index 062f81f04ba..34a354085f1 100644
--- a/engines/eem/site.cpp
+++ b/engines/eem/site.cpp
@@ -554,6 +554,14 @@ void SiteScreen::enter(uint siteNum) {
 		renderBackground(siteNum);
 	}
 
+	// Stop the travel music explicitly. `_DoSiteLoop @ 168d:06c0`
+	// waits for the one-shot travel track to play out and then calls
+	// `_StopMIDI()` before the interactive site phase begins ???
+	// blocking the engine while it spins. We just stop now so the
+	// site investigation runs without music (matches the original
+	// silent-investigation phase).
+	_vm->stopMusic();
+
 	// Static drops (Loop 2 from `_DoSiteLoop`) ??? no animation, baked
 	// into the BG snapshot the run() pump uses to restore.
 	renderStaticDrops(siteNum);


Commit: fb9922f887b5af752198719e6043a983dbb5f653
    https://github.com/scummvm/scummvm/commit/fb9922f887b5af752198719e6043a983dbb5f653
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:20:46+02:00

Commit Message:
EEM: added missing text bubbles here and there

Changed paths:
    engines/eem/graphics.cpp
    engines/eem/mystery.cpp
    engines/eem/site.cpp
    engines/eem/ui.cpp


diff --git a/engines/eem/graphics.cpp b/engines/eem/graphics.cpp
index 0718f6c1471..ed8955c555b 100644
--- a/engines/eem/graphics.cpp
+++ b/engines/eem/graphics.cpp
@@ -30,6 +30,7 @@
 #include "graphics/cursorman.h"
 #include "graphics/managed_surface.h"
 
+#include "eem/audio.h"
 #include "eem/detection.h"
 #include "eem/eem.h"
 
@@ -76,38 +77,167 @@ const BalloonInsets kBalloonInsetTable[] = {
 };
 
 void EEMEngine::doHelp() {
-	// `_KDHelp` reads two hint TextBlock offsets from `_KDTextIndex`:
-	//   word @ +0xe : first-time hint
-	//   word @ +0x10: second-time hint (cycles back to first if missing)
-	// `_SawHelpHint` toggles between them.
+	// Mirrors `_KDHelp @ 1560:010a`. The original walks the first two
+	// entries of `_AChain` (the puzzle's required-clue chain ??? the
+	// "spine" of evidence the player must collect):
+	//
+	//   for (i = 0; i < 2; i++) {
+	//       if (_AChain[i] != -1 && _HintBlock[i] != -1 &&
+	//           _CluesFound[_AChain[i]] == 0) {
+	//           _DisplayHint(TextBlock + _HintBlock[i], i + 10);
+	//           shown++; break;
+	//       }
+	//       if (_HintBlock[i] != -1) defined++;
+	//   }
+	//   if (!shown) {
+	//       // Fall back to the generic KD hint: KDTextIndex[+0xe]
+	//       // (first time) / KDTextIndex[+0x10] (second time, toggled
+	//       // by _SawHelpHint). If neither chain entry had a hint
+	//       // defined, show the global "no hints" sentinel instead.
+	//       _DisplayHint(...);
+	//   }
+	//
+	// So this is a SMART per-puzzle hint: the partner points the
+	// player at whichever chain clue they haven't yet found, only
+	// falling back to the generic "let's keep looking" line when
+	// every chain hint has been triggered already.
 	if (!_mystery.isLoaded() || !_font.isLoaded())
 		return;
 
-	const byte *kd = _mystery.kdTextIndex();
+	const byte *kd  = _mystery.kdTextIndex();
+	const byte *hb  = _mystery.hintBlock();
 	if (!kd)
 		return;
 
-	const uint16 hintFirst  = READ_LE_UINT16(kd + 0x0e);
-	const uint16 hintSecond = READ_LE_UINT16(kd + 0x10);
-	uint16 use = _mystery._sawHelpHint && hintSecond != 0xFFFF ? hintSecond : hintFirst;
-	if (use == 0xFFFF) {
-		debugC(1, kDebugScript, "doHelp: no hint configured");
+	uint16 chosenText = 0xFFFF;
+	int    soundNum   = 0;
+	bool   anyHintDefined = false;
+
+	if (hb) {
+		for (uint i = 0; i < 2; i++) {
+			const uint16 chainClue = _mystery.aChain(i);
+			if (chainClue == 0xFFFF)
+				continue;
+			const uint16 hintOff = READ_LE_UINT16(hb + i * 2);
+			if (hintOff == 0xFFFF)
+				continue;
+			anyHintDefined = true;
+			if (chainClue < Mystery::kCluesFoundCap &&
+				_mystery._cluesFound[chainClue] == 0) {
+				chosenText = hintOff;
+				soundNum   = (int)i + 10;
+				break;
+			}
+		}
+	}
+
+	if (chosenText == 0xFFFF) {
+		// No unfound chain clue had a hint to give ??? fall back to the
+		// generic KD hint (or the "no hints defined" sentinel if the
+		// chain has no hints at all). Mirrors the second arm of
+		// `_KDHelp` (1560:0152-019b).
+		if (anyHintDefined) {
+			const uint16 hintFirst  = READ_LE_UINT16(kd + 0x0e);
+			const uint16 hintSecond = READ_LE_UINT16(kd + 0x10);
+			if (!_mystery._sawHelpHint && hintFirst != 0xFFFF) {
+				chosenText = hintFirst;
+				soundNum   = 7;
+				_mystery._sawHelpHint = true;
+			} else if (hintSecond != 0xFFFF) {
+				chosenText = hintSecond;
+				soundNum   = 8;
+			}
+		}
+		// Else: keep chosenText == 0xFFFF ??? original would render
+		// `NoHints` (a "There are no hints defined for this Mystery"
+		// string at 29be:00d3); we just bail.
+	}
+
+	if (chosenText == 0xFFFF) {
+		debugC(1, kDebugScript, "doHelp: no hint available");
 		return;
 	}
-	if (!_mystery._sawHelpHint && hintFirst != 0xFFFF)
-		_mystery._sawHelpHint = true;
 
-	const Common::String raw = _mystery.textAt(use);
-	const Common::String text = parseString(raw, _playerName, _partner);
+	const Common::String raw  = _mystery.textAt(chosenText);
+	Common::String text = parseString(raw, _playerName, _partner);
 
-	Graphics::ManagedSurface scratch(320, 200,
+	// Render as a speech-balloon overlay, exactly mirroring
+	// `_DisplayHint @ 1560:0009`:
+	//
+	//   _GetKDTextBalloon(text, &bub);             // first-char dispatch
+	//   _GetBalloon(bub);                           // load balloon pic
+	//   y = (h < 0x4e) ? (0x50 - h) >> 1 : 1;       // vertical centre
+	//   _AddPicBackground(balloon, 0x21, y);        // overlay on screen
+	//   _WordWrap(0x21+tbl[bub].x, y+tbl[bub].y,   // text inside balloon
+	//             tbl[bub].w, text, -1, color=0);
+	//   _SayKDDigital(snd);                          // partner voice
+	//   _Wait();
+	//
+	// The balloon BG is the caller's CURRENT screen ??? site / PDA /
+	// gallery ??? not a cleared scratch.
+	Graphics::ManagedSurface ms(320, 200,
 		Graphics::PixelFormat::createFormatCLUT8());
-	scratch.clear();
-	_font.drawString(&scratch, "HELP", 8, 4, 320, 0xF);
-	_font.drawWordWrapped(&scratch, 8, 24, 304, text, 0xF);
-	g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
+	ms.clear();
+	{
+		Graphics::Surface *cur = g_system->lockScreen();
+		if (cur) {
+			for (int row = 0; row < 200; row++)
+				memcpy((byte *)ms.getBasePtr(0, row),
+					   (const byte *)cur->getBasePtr(0, row), 320);
+			g_system->unlockScreen();
+		}
+	}
+
+	// Balloon shape dispatch via `_GetKDTextBalloon @ 1df2:0105` ???
+	// based on the first char of the parsed text. Digits select a
+	// specific balloon variant; non-digit defaults to `0x17`. The
+	// digit, when present, is THEN consumed from the displayed
+	// text ??? mirrors `_DisplayAlibi @ 1df2:0145`'s `str = pbVar7 + 1`
+	// advance after using `*str` for `bindx`. Without this the hint
+	// renders like "1Try checking the kitchen..." with a stray
+	// leading digit. `_GetKDTextBalloon` itself doesn't strip it
+	// (verified at 1df2:0105 ??? it just reads `*str`), so the caller
+	// has to.
+	const byte firstChar =
+		text.empty() ? (byte)0 : (byte)text[0];
+	const uint16 bubNum = getKDTextBalloon(firstChar);
+	if (firstChar >= '0' && firstChar <= '9')
+		text.deleteChar(0);
+	Picture balloon;
+	const bool haveBalloon =
+		_balloonArchive.size() > (bubNum & 0x7F) &&
+		_balloonArchive.loadEntry(bubNum & 0x7F, balloon);
+
+	const int balloonX = 0x21;
+	int balloonY = 1;
+	if (haveBalloon && balloon.surface.h < 0x4e)
+		balloonY = (0x50 - balloon.surface.h) / 2;
+
+	if (haveBalloon) {
+		const byte transp = (byte)(balloon.flags >> 8);
+		ms.transBlitFrom(balloon.surface,
+						 Common::Point(balloonX, balloonY),
+						 (uint32)transp);
+	}
+
+	// Balloon-relative text insets from the table at `29be:0875`
+	// (10 bytes per entry: x, y, max-width, ...).
+	uint16 tx = 5, ty = 4, tw = 155;
+	getBalloonInsets(bubNum, tx, ty, tw);
+	_font.drawWordWrapped(&ms, balloonX + tx, balloonY + ty, tw, text,
+						  haveBalloon ? 0 : 0xF);
+
+	g_system->copyRectToScreen(ms.getPixels(), ms.pitch,
 							   0, 0, 320, 200);
 	g_system->updateScreen();
+
+	// `_DisplayHint @ 1560:0009` plays `_SayKDDigital(soundnum)` ???
+	// partner-specific voice line keyed to which hint type fired (10
+	// = first chain hint, 11 = second, 7 / 8 = generic KD).
+	if (_audio && _mystery.kdTextIndex() && soundNum > 0)
+		_audio->sayKDDigital(_mystery.kdTextIndex(), (uint)soundNum,
+							 _partner);
+
 	waitForInput(60000);
 }
 
diff --git a/engines/eem/mystery.cpp b/engines/eem/mystery.cpp
index 39c854535d9..6b6bfbfc56f 100644
--- a/engines/eem/mystery.cpp
+++ b/engines/eem/mystery.cpp
@@ -243,6 +243,16 @@ uint16 Mystery::alibiTextOffset(uint suspectIdx) const {
 	return READ_LE_UINT16(gd + suspectIdx * 0x46 + 0x02);
 }
 
+const byte *Mystery::hintBlock() const {
+	// Header word at index 9 (`_hintOffset`) ??? used by `_KDHelp @
+	// 1560:010a`'s per-chain-clue hint table. Each pair-of-bytes is
+	// a TextBlock offset for the corresponding `_AChain` entry, or
+	// `0xFFFF` if no hint is defined for that chain position.
+	if (!isLoaded() || _hintOffset == 0 || _hintOffset >= _data.size())
+		return nullptr;
+	return _data.data() + _hintOffset;
+}
+
 const byte *Mystery::solvedClueBlock() const {
 	if (!isLoaded() || _solvedOffset == 0 || _solvedOffset >= _data.size())
 		return nullptr;
diff --git a/engines/eem/site.cpp b/engines/eem/site.cpp
index 34a354085f1..e444fc4b8b7 100644
--- a/engines/eem/site.cpp
+++ b/engines/eem/site.cpp
@@ -668,6 +668,16 @@ void SiteScreen::run() {
 				// trigger a hotspot underneath.
 				const Common::Rect kBtnNotebook(35, 111, 56, 136);
 				const Common::Rect kBtnMap     ( 7, 177, 57, 200);
+				// Partner area ??? port-only enhancement so the player
+				// can click the host sprite for a hint, mirroring the
+				// PDA's rect-3 / gallery's rect-3 behaviour. The
+				// original site loop's `_FindButton(&SiteButtons, 2,
+				// ...)` only checks notebook + map, but the same
+				// partner-click ??? `_KDHelp` shortcut is wired in
+				// `_HandleNoteButton[3]` (0x0403) and
+				// `_HandleGalleryButton[3]` (0x061e). Rect matches
+				// the PDA / gallery `kBtnPartner` (5, 80, 44, 110).
+				const Common::Rect kBtnPartner ( 5,  80, 44, 110);
 				if (kBtnNotebook.contains(event.mouse.x, event.mouse.y)) {
 					_vm->doNotebook();
 					enter(cur);
@@ -680,6 +690,11 @@ void SiteScreen::run() {
 					enter(cur);
 					break;
 				}
+				if (kBtnPartner.contains(event.mouse.x, event.mouse.y)) {
+					_vm->doHelp();
+					enter(cur);
+					break;
+				}
 				const int idx = hotspotAtPoint(cur, event.mouse.x, event.mouse.y);
 				if (idx >= 0) {
 					onHotspotClicked(cur, (uint)idx);
diff --git a/engines/eem/ui.cpp b/engines/eem/ui.cpp
index 45cea187910..4c6efe1b600 100644
--- a/engines/eem/ui.cpp
+++ b/engines/eem/ui.cpp
@@ -2757,19 +2757,115 @@ void EEMEngine::doAccuse() {
 	if (!_mystery.isLoaded())
 		return;
 
-	// Mirrors `_DoAccuseGallery @ 1df2:0a31`:
-	//   1. Show KD's hint balloon (KDTextIndex[+8] text).
-	//   2. `_GetBackground(0x3f)` ??? same backdrop as PDA / gallery.
-	//   3. `_DrawGallery()` ??? renders portraits at the standard 5 slots
-	//      (positions verified at 29be:0x116, bottom-aligned baseline 0x48).
-	//   4. Click loop dispatching on `_NoteButtons` (same table as PDA)
-	//      with a separate `_HandleAccuseNoteButton` jump table.
+	// Mirrors `_DoAccuse @ 1df2:0bdd` + `_DoAccuseGallery @ 1df2:0a31`:
+	//   1. The original notes-selection screen runs first (PIC 0x1A7).
+	//      We skip it because note-selection lives in the PDA in our
+	//      port ??? the accuse path is reached via the SOLVE button.
+	//   2. EVIDENCE GATE: `_DoAccuse @ 1df2:0bdd` calls `_SolvedCheck`
+	//      (1df2:0c75) before `_DoAccuseGallery`. If `selectedPoints
+	//      <= 99`, the original shows a partner balloon
+	//      (`KDTextIndex[+6]` text + `_SayKDDigital(3)`) and returns
+	//      to `_LastScreen` ??? the suspect picker is NEVER opened.
+	//      Our previous flow opened the picker and only checked after
+	//      the player committed a suspect; that let the player accuse
+	//      without the required evidence.
+	//   3. If the gate passes:
+	//      a. KD intro balloon (`KDTextIndex[+8]` text + `_SayKDDigital(4)`).
+	//      b. `_GetBackground(0x3f)` + `_DrawGallery()` ??? portraits at
+	//         the 5 fixed slots (`29be:0x116`).
+	//      c. Click loop on portraits ??? `_WITCH(picked)` ??? guilty/alibi.
 	const uint8 num = _mystery.numSuspects();
 	if (num == 0)
 		return;
 
 	const byte *gd = _mystery.galleryData();
 
+	// Evidence gate. `_DoAccuse @ 1df2:0c75` runs `_SolvedCheck`
+	// before opening the suspect picker; on failure it renders a
+	// partner balloon over the CURRENT screen (the PDA in the
+	// original) and returns. We render the same hint over the
+	// caller's screen and bail back to `_lastScreen` without ever
+	// touching the gallery BG.
+	if (!_mystery.solvedCheck()) {
+		const byte *kdIdx = _mystery.kdTextIndex();
+		const int16 hintOff = kdIdx
+			? (int16)READ_LE_UINT16(kdIdx + 6)
+			: -1;
+		Common::String hint;
+		if (hintOff != -1)
+			hint = parseString(_mystery.textAt((uint16)hintOff),
+							   _playerName, _partner);
+		if (hint.empty()) {
+			// Fallback if `KDTextIndex[+6]` isn't set in this mystery.
+			hint = (_mystery.selectedPoints() == 0)
+				? "We're not ready to solve this mystery yet. "
+				  "Let's keep investigating until we have some "
+				  "more solid evidence."
+				: "We don't have quite enough evidence yet. "
+				  "Let's review our notes and find a few more "
+				  "clues before we accuse anyone.";
+		}
+
+		// Compose balloon overlay on the current screen. Mirrors the
+		// `_GetKDTextBalloon` + `_GetBalloon` + `_AddPicBackground`
+		// + `_WordWrap` sequence at 1df2:0c8d-0cd1.
+		Graphics::ManagedSurface ms(320, 200,
+			Graphics::PixelFormat::createFormatCLUT8());
+		ms.clear();
+		Graphics::Surface *cur = g_system->lockScreen();
+		if (cur) {
+			for (int row = 0; row < 200; row++)
+				memcpy((byte *)ms.getBasePtr(0, row),
+					   (const byte *)cur->getBasePtr(0, row), 320);
+			g_system->unlockScreen();
+		}
+		const byte firstChar =
+			hint.empty() ? (byte)0 : (byte)hint[0];
+		const uint16 bubNum = getKDTextBalloon(firstChar);
+		// Strip the digit prefix used for balloon dispatch ??? it's
+		// consumed by the original at `_DisplayAlibi @ 1df2:0163`
+		// (`str = pbVar7 + 1`) and shouldn't appear in the rendered
+		// text. `_GetKDTextBalloon` itself doesn't advance past it.
+		if (firstChar >= '0' && firstChar <= '9')
+			hint.deleteChar(0);
+		Picture balloon;
+		const bool haveBalloon =
+			_balloonArchive.size() > (bubNum & 0x7F) &&
+			_balloonArchive.loadEntry(bubNum & 0x7F, balloon);
+		const int balloonX = 0x21;
+		int balloonY = 1;
+		if (haveBalloon && balloon.surface.h < 0x4e)
+			balloonY = (0x50 - balloon.surface.h) / 2;
+		if (haveBalloon) {
+			const byte transp = (byte)(balloon.flags >> 8);
+			ms.transBlitFrom(balloon.surface,
+							 Common::Point(balloonX, balloonY),
+							 (uint32)transp);
+		}
+		uint16 tx = 5, ty = 4, tw = 155;
+		getBalloonInsets(bubNum, tx, ty, tw);
+		if (_font.isLoaded()) {
+			_font.drawWordWrapped(&ms, balloonX + tx,
+								  balloonY + ty, tw, hint,
+								  haveBalloon ? 0 : 0xF);
+		}
+		g_system->copyRectToScreen(ms.getPixels(), ms.pitch,
+								   0, 0, 320, 200);
+		g_system->updateScreen();
+
+		// `_DoAccuse @ 1df2:0cd9` plays `_SayKDDigital(3)` ???
+		// partner-specific "not enough evidence" voice line.
+		if (_audio && kdIdx)
+			_audio->sayKDDigital(kdIdx, 3, _partner);
+
+		waitForInput(20000);
+		// `_DoAccuse @ 1df2:0ce5` writes `_NextScreen = _LastScreen`
+		// so the player drops back where they came from.
+		_nextScreen = _lastScreen != kScreenInvalid
+						? (ScreenId)_lastScreen : kScreenSite;
+		return;
+	}
+
 	// Verbatim from 29be:0x116 ??? same five suspect slot positions as
 	// `_DrawGallery @ 158f:0046`.
 	Picture accuseBg;
@@ -2809,6 +2905,14 @@ void EEMEngine::doAccuse() {
 				const byte firstChar =
 					hint.empty() ? (byte)0 : (byte)hint[0];
 				const uint16 bubNum = getKDTextBalloon(firstChar);
+				// Strip the digit prefix used for balloon dispatch.
+				// `_DisplayAlibi @ 1df2:0163` does `str = pbVar7 + 1`
+				// after using `*str` for `bindx`. Same pattern used by
+				// `_DisplayHint`: digit picks the bubble shape AND is
+				// then consumed from the rendered text. Without this
+				// the intro balloon shows e.g. "1Ready to solve?".
+				if (firstChar >= '0' && firstChar <= '9')
+					hint.deleteChar(0);
 				Picture balloon;
 				const bool haveBalloon =
 					_balloonArchive.size() > (bubNum & 0x7F) &&
@@ -2985,48 +3089,22 @@ void EEMEngine::doAccuse() {
 	// Real chain evaluation. Mirrors the original two-gate accusation:
 	//   1. `_AccuseEntry @ 1df2:0ff8` checks `_GetFoundPoints() >= 100`
 	//      ??? gates whether the suspect picker is even reachable. We
-	//      replicate this with `_SolvedCheck ??? selectedPoints > 99`,
-	//      since our port marks clues in the notebook (port deviation;
-	//      the original gates on the auto-sorted top-5 found clues
-	//      via `_GetFoundPoints @ 1df2:0098`).
+	//      `_SolvedCheck ??? selectedPoints > 99` is now gated at the
+	//      TOP of `doAccuse` ??? by the time we reach this point we
+	//      already know `solvedCheck()` was true (the picker wouldn't
+	//      have opened otherwise).
 	//   2. `_WITCH @ 1df2:089f` checks `GalleryData[picked*0x46+0x02] ==
 	//      0xFFFF`. Innocent suspects store an alibi-text TextBlock
 	//      offset there; the guilty one uses the sentinel.
-	// Both must hold for `_DisplayCorrect`; otherwise it's an alibi.
 	const int points          = _mystery.selectedPoints();
-	const bool enoughEvidence = _mystery.solvedCheck();
 	const bool pickedGuilty   = _mystery.isGuilty((uint)picked);
-	const bool guessedRight   = enoughEvidence && pickedGuilty;
+	const bool guessedRight   = pickedGuilty;
 	debugC(1, kDebugScript,
-		   "doAccuse: picked=%d selectedPts=%d evidence=%s guilty=%s -> %s",
+		   "doAccuse: picked=%d selectedPts=%d guilty=%s -> %s",
 		   picked, points,
-		   enoughEvidence ? "yes" : "no",
 		   pickedGuilty ? "yes" : "no",
 		   guessedRight ? "correct" : "wrong");
 
-	// Insufficient evidence: same hint the original shows when
-	// `_AccuseEntry` returns 0 (string at `_KDTextIndex[0]`).
-	if (!enoughEvidence && _font.isLoaded()) {
-		Graphics::ManagedSurface scratch(320, 200,
-			Graphics::PixelFormat::createFormatCLUT8());
-		scratch.clear();
-		_font.drawWordWrapped(&scratch, 16, 80, 288,
-			points == 0
-				? "We're not ready to solve this mystery yet. "
-				  "Let's keep investigating until we have some "
-				  "more solid evidence to make our case! "
-				  "(Press N in the site screen to mark clues.)"
-				: "We don't have quite enough solid evidence yet. "
-				  "Let's review our notes and find a few more "
-				  "clues before we accuse anyone.",
-			0xF);
-		g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
-								   0, 0, 320, 200);
-		g_system->updateScreen();
-		waitForInput(15000);
-		return;
-	}
-
 	// Wrong suspect: render alibi flow. Mirrors `_DisplayAlibi @
 	// 1df2:0145` ??? shows the suspect's alibi text + their picture on
 	// PIC 0x3e, then returns to the accuse gallery for another try.
@@ -3130,21 +3208,31 @@ void EEMEngine::doAccuse() {
 		}
 
 		// `_DisplayCorrect @ 1df2:073c` calls `_MIDIPlay(5)` (1df2:0789)
-		// before `_DifferenceAnimation("scrapbk.ani")` to swap from the
-		// travel music to the winner cue.
+		// before `_DisplayClue` to swap from the travel music to the
+		// winner cue.
 		if (_music)
 			_music->playMus(5, /*loop=*/false);
+
+		// `_DisplayCorrect @ 1df2:07ac` calls
+		// `_DisplayClue(_Mystery + _MysteryIndex[0x10], 0)` BEFORE the
+		// scrapbook animation. That clueblock is the partner's
+		// chain-by-chain RECAP ??? they enumerate every required clue
+		// (`Look at this ??? the suspect was here at 8pm`, `... and
+		// remember the broken vase from the kitchen`, `... so it had
+		// to be X!`) and arrive at the conclusion. Without rendering
+		// it the player goes straight from suspect-pick to the
+		// scrapbook anim and misses the deduction entirely.
+		const byte *solved = _mystery.solvedClueBlock();
+		if (solved)
+			displayClue(solved);
+
+		// `_DifferenceAnimation("scrapbk.ani")` (1df2:0848) ??? the
+		// physical scrapbook flip animation that introduces the
+		// per-mystery ending pages.
 		playAnm(Common::Path("SCRAPBK.ANI"), 120, true);
 
-		// `_DisplayCorrect @ 1df2:07ac` then calls `_DisplayClue`
-		// against the briefing's winning ClueBlock at
-		// `_Mystery + _MysteryIndex[0x10]` BEFORE `_ShowOneScrap`.
-		// We don't render that intermediate ClueBlock yet (it ties
-		// into the chain-A/B/C result selection); skip straight to
-		// the per-mystery ending pages ??? the same screen
-		// `_ShowOneScrap @ 1f78:0773` displays via
-		// `_DisplayEnding(num, 1)`. Players see the per-mystery
-		// resolution text on top of the ending pic.
+		// `_ShowOneScrap @ 1f78:0773` is `_DisplayEnding(num, 1)` ???
+		// the multi-page per-mystery ending narrative.
 		doShowEnding(mn);
 
 		// Mirrors `_SavePlayerRecord` at 1df2:0857 ??? once the


Commit: 27d73fdbfe92cb2107975cfb780c5c7ba5237b8c
    https://github.com/scummvm/scummvm/commit/27d73fdbfe92cb2107975cfb780c5c7ba5237b8c
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:20:47+02:00

Commit Message:
EEM: dedicated code for accusation window

Changed paths:
    engines/eem/eem.h
    engines/eem/ui.cpp


diff --git a/engines/eem/eem.h b/engines/eem/eem.h
index 2d95c0102a1..525cf4773f7 100644
--- a/engines/eem/eem.h
+++ b/engines/eem/eem.h
@@ -188,6 +188,14 @@ public:
 	/// Mirrors `_DoAccuseGallery` @ 1df2:0a31 + `_DisplayEnding` @ 1df2:0548.
 	void doAccuse();
 
+	/// Show the accuse-notes screen (PIC 0x1A7, the red "accuse-mode"
+	/// BG with selectable clue list + "N clues" remaining counter).
+	/// Mirrors the outer loop of `_DoAccuse @ 1df2:0bdd`. Returns
+	/// true if the player committed (selected the chain-required
+	/// number of clues and clicked SOLVE), false if they exited via
+	/// ESC / back. Called from `doAccuse` before the evidence gate.
+	bool doAccuseNotes();
+
 	/// Show a host hint from `KDTextIndex`. Mirrors `_KDHelp` @ 1560:010a +
 	/// `_DisplayHint` @ 1560:0009. Cycles between the two hint slots that
 	/// the original engine tracks via `_SawHelpHint`.
diff --git a/engines/eem/ui.cpp b/engines/eem/ui.cpp
index 4c6efe1b600..9956e137bd9 100644
--- a/engines/eem/ui.cpp
+++ b/engines/eem/ui.cpp
@@ -1792,13 +1792,15 @@ void EEMEngine::drawNotebookFrame(int &page) {
 		y += h + 7;
 	}
 
-	// Page indicator + selected-points counter directly on PIC.
+	// Page indicator only ??? the original `_DrawNotes @ 161e:01d0`
+	// has no points display in the PDA notebook (the per-clue point
+	// values are SPOILERS for the chain weighting that the engine
+	// uses internally for `_GetSelectedPoints`). Showing the running
+	// total tells the player exactly when they have enough evidence
+	// to solve, which deflates the deduction step.
 	_font.drawString(&scratch, Common::String::format("p%d/%d",
 							   page + 1, (int)pageStarts.size()),
 					 270, 4, 320, 0x5C);
-	_font.drawString(&scratch, Common::String::format("%d pts",
-							   _mystery.selectedPoints()),
-					 270, 14, 320, 0x5C);
 
 	g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
 							   0, 0, 320, 200);
@@ -2753,33 +2755,340 @@ uint16 EEMEngine::getKDTextBalloon(byte firstChar) const {
 	return kDigitBalloons[firstChar - '0'];
 }
 
+bool EEMEngine::doAccuseNotes() {
+	// Mirrors the accuse-notes screen at the head of `_DoAccuse @
+	// 1df2:0bdd`:
+	//   * BG: PIC 0x1A7 (the red "accuse-mode" backdrop).
+	//   * `_AccuseNoteRect @ 29be:1048` = (79, 27, 304, 159) holds
+	//     the rendered clue list.
+	//   * Counter at `(0xd1, 0xb)` = `(209, 11)` shows "N clue(s)"
+	//     remaining (`_UpdateSelectionCount @ 1df2:08dd`,
+	//     `_Show_String(0xb, 0xd1, ...)`).
+	//   * Expected count = `6 - DAT_2d5d_3f99`:
+	//       chainStage 1 ??? 5 clues, 2 ??? 4 clues, 3 ??? 3 clues.
+	//   * `_NoteUnselectedColor = 1` (red) for unselected, `0x3c`
+	//     for selected (1df2:0c2c sets it on entry).
+	//   * Click on a clue toggles its selection
+	//     (`_SearchNoteAreas` + `_SwapColors`).
+	//   * Click `_NoteButtons[2]` (rect at `(157, 174, 178, 190)`,
+	//     the original's "go to gallery" button) jumps to the
+	//     evidence check; `_HandleAccuseNoteButton(2)` returns 2
+	//     and the outer loop forces `uStack_8 = uStack_a` to
+	//     trigger `_SolvedCheck`.
+	//   * ESC sets `_NextScreen = 3` and exits.
+	if (!_mystery.isLoaded() || !_font.isLoaded())
+		return false;
+	const byte *ni = _mystery.noteIndex();
+	const uint16 niCount = _mystery.noteIndexCount();
+	if (!ni)
+		return false;
+
+	Picture accuseBg;
+	const bool haveBg = _picsArchive.getPicture(0x1a7, accuseBg);
+
+	// Required count for solving ??? `6 - chainStage`.
+	const uint expected = (_chainStage >= 1 && _chainStage <= 3)
+		? (uint)(6 - _chainStage)
+		: 5;
+
+	// Build the list of FOUND clue IDs (in clue-ID order; the
+	// original's `_DrawNotes(NULL, 100, ...)` walks `_CluesFound[]`
+	// the same way).
+	Common::Array<uint> found;
+	for (uint i = 0; i < niCount && i < Mystery::kCluesFoundCap; i++) {
+		if (_mystery._cluesFound[i])
+			found.push_back(i);
+	}
+
+	// `_AccuseNoteRect` (79, 27, 304, 159) ??? text wrap area.
+	const int rectX = 79;
+	const int rectY = 27;
+	const int rectW = 304 - 79;
+	const int rectH = 159 - 27;
+
+	// `_NoteButtons` rects (verified at `29be:0147`). `_DoAccuse`
+	// re-uses the same table as `_DoNotebook`, but only SOLVE /
+	// PAGE NEXT / PAGE PREV do anything; others sit inert.
+	// `_HandleAccuseNoteButton @ 1df2:0990` returns `DI` (initialised
+	// to 0) and only sets `DI = 2` in the `i == 4` branch (asm:
+	// `1df2:09b2: MOV DI, 0x2`). The outer loop's `iVar6 == 2` test
+	// at `1df2:0db2` is checking the HANDLER'S RETURN VALUE, not the
+	// button INDEX ??? earlier comment had this backwards. So the
+	// SOLVE rect is `[4]` (180, 174, 201, 190), the same icon the
+	// PDA uses to trigger the accuse flow in the first place.
+	const Common::Rect kBtnSolve   (180, 174, 201, 190); // [4] SOLVE
+	const Common::Rect kBtnPageNext(204, 174, 224, 190); // [5] PAGE NEXT
+	const Common::Rect kBtnPagePrev(226, 174, 247, 190); // [6] PAGE PREV
+	const Common::Rect kBtnPartner (  5,  80,  44, 110); // [3] KD HELP
+
+	// Per-page slot rects + their clue IDs (for click hit-testing).
+	Common::Array<Common::Rect> slotRects;
+	Common::Array<uint> slotClues;
+
+	int page = 0;
+	int pageBreaks[16];
+	int numPages = 1;
+	pageBreaks[0] = 0;
+
+	auto rebuildPagination = [&]() {
+		numPages = 1;
+		pageBreaks[0] = 0;
+		const int lineH = _font.getFontHeight() + 1;
+		int y = rectY;
+		for (uint i = 0; i < found.size(); i++) {
+			const uint clueId = found[i];
+			Common::String txt;
+			if (clueId < niCount) {
+				const uint16 textOff = READ_LE_UINT16(ni + clueId * 4);
+				txt = parseString(_mystery.textAt(textOff),
+								   _playerName, _partner);
+			}
+			Common::Array<Common::String> wrapped;
+			_font.wordWrapText(txt, rectW, wrapped);
+			const int h = (int)wrapped.size() * lineH;
+			if (y + h + 7 > rectY + rectH) {
+				if (numPages < (int)ARRAYSIZE(pageBreaks)) {
+					pageBreaks[numPages++] = (int)i;
+					y = rectY;
+				}
+			}
+			y += h + 7;
+		}
+		if (page >= numPages)
+			page = numPages - 1;
+		if (page < 0)
+			page = 0;
+	};
+
+	auto draw = [&]() {
+		Graphics::ManagedSurface scratch(320, 200,
+			Graphics::PixelFormat::createFormatCLUT8());
+		scratch.clear();
+		if (haveBg)
+			scratch.simpleBlitFrom(accuseBg.surface);
+
+		// Partner sprite at (5, 0x50). The original `_DoAccuse @
+		// 1df2:0c2c` does `_NewAnimation(5, 0x50, partnerCells,
+		// script=2, prior=1)` ??? same anim cells as the gallery
+		// (anim 2 for Jake / 0x10 for Jenny) with script 0x02. The
+		// `_UpdateAnimations` loop in `_DoAccuse @ 1df2:0bfa` keeps
+		// the slot painting through the entire selection screen;
+		// without an explicit blit here the player sees a partner-
+		// less accuse-mode screen.
+		const uint partnerAnim = (_partner == 0) ? 2 : 0x10;
+		Animation partnerAni;
+		if (_aniArchive.loadAnimation(partnerAnim, partnerAni) &&
+			!partnerAni.empty()) {
+			const uint32 now = g_system->getMillis();
+			const uint frameIdx = partnerFrameAtTick(0x02,
+													  (uint)partnerAni.size(), now);
+			blitAnimFrameAnchored(scratch.surfacePtr(),
+								  partnerAni[frameIdx], 5, 0x50);
+		}
+
+		// Clue list inside `_AccuseNoteRect`. Selected = 0x3c (yellow),
+		// unselected = 1 (red), per `_NoteUnselectedColor = 1` set at
+		// 1df2:0c25 ??? the red colour is what gives the screen its
+		// "accuse-mode" look together with PIC 0x1A7.
+		slotRects.clear();
+		slotClues.clear();
+		const int lineH = _font.getFontHeight() + 1;
+		const int startIdx = pageBreaks[page];
+		const int endIdx   = (page + 1 < numPages)
+			? pageBreaks[page + 1]
+			: (int)found.size();
+		int y = rectY;
+		uint selectedCount = 0;
+		for (uint i = 0; i < found.size(); i++) {
+			if (_mystery._noteSelected[found[i]])
+				selectedCount++;
+		}
+		for (int i = startIdx; i < endIdx; i++) {
+			const uint clueId = found[i];
+			Common::String txt;
+			if (clueId < niCount) {
+				const uint16 textOff = READ_LE_UINT16(ni + clueId * 4);
+				txt = parseString(_mystery.textAt(textOff),
+								   _playerName, _partner);
+			}
+			if (txt.empty())
+				txt = Common::String::format("clue %u", clueId);
+			Common::Array<Common::String> wrapped;
+			_font.wordWrapText(txt, rectW, wrapped);
+			const int h = (int)wrapped.size() * lineH;
+			const byte color = _mystery._noteSelected[clueId] ? 0x3c : 0x01;
+			for (uint li = 0; li < wrapped.size(); li++) {
+				_font.drawString(&scratch, wrapped[li], rectX,
+								 y + (int)li * lineH, rectW, color);
+			}
+			slotRects.push_back(Common::Rect(rectX, y,
+											  rectX + rectW, y + h));
+			slotClues.push_back(clueId);
+			y += h + 7;
+		}
+
+		// Counter ??? `_UpdateSelectionCount(remaining)` at (0xd1, 0xb).
+		const uint remaining = (selectedCount < expected)
+			? expected - selectedCount
+			: 0;
+		const Common::String counter = Common::String::format("%u %s",
+			remaining, remaining == 1 ? "clue" : "clues");
+		_font.drawString(&scratch, counter, 209, 11, 100, 0x0F);
+
+		if (numPages > 1) {
+			_font.drawString(&scratch,
+				Common::String::format("p%d/%d", page + 1, numPages),
+				rectX, 11, 60, 0x0F);
+		}
+
+		g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
+								   0, 0, 320, 200);
+		g_system->updateScreen();
+	};
+
+	rebuildPagination();
+	draw();
+
+	while (!shouldQuit()) {
+		Common::Event ev;
+		bool dirty = false;
+		while (g_system->getEventManager()->pollEvent(ev)) {
+			if (ev.type == Common::EVENT_QUIT ||
+				ev.type == Common::EVENT_RETURN_TO_LAUNCHER)
+				return false;
+			if (ev.type == Common::EVENT_KEYDOWN) {
+				if (ev.kbd.keycode == Common::KEYCODE_ESCAPE)
+					return false;
+				if (ev.kbd.keycode == Common::KEYCODE_LEFT &&
+					page > 0) {
+					page--;
+					dirty = true;
+				} else if (ev.kbd.keycode == Common::KEYCODE_RIGHT &&
+						   page + 1 < numPages) {
+					page++;
+					dirty = true;
+				}
+			}
+			if (ev.type == Common::EVENT_LBUTTONDOWN) {
+				const int mx = ev.mouse.x;
+				const int my = ev.mouse.y;
+				// Page navigation ??? `_NoteButtons[5]` / `[6]`,
+				// dispatched in `_HandleAccuseNoteButton @
+				// 1df2:0990`. Only effective if there's another
+				// page in that direction; mirrors the
+				// `1 < _CurrentPage` guard at 1df2:09a8 and the
+				// `_NextClue != -1` guard at 1df2:099e.
+				if (kBtnPageNext.contains(mx, my)) {
+					if (page + 1 < numPages) {
+						page++;
+						dirty = true;
+					}
+					continue;
+				}
+				if (kBtnPagePrev.contains(mx, my)) {
+					if (page > 0) {
+						page--;
+						dirty = true;
+					}
+					continue;
+				}
+				// Partner click ??? `_NoteButtons[3]`. Original
+				// `_HandleAccuseNoteButton` doesn't dispatch this
+				// (no case for i == 3), but the rect is still in
+				// the table; we wire it to the puzzle hint so the
+				// player can ask the partner what to look for
+				// without leaving accuse mode.
+				if (kBtnPartner.contains(mx, my)) {
+					doHelp();
+					dirty = true;
+					continue;
+				}
+				if (kBtnSolve.contains(mx, my)) {
+					// Count selected.
+					uint selected = 0;
+					for (uint i = 0; i < found.size(); i++) {
+						if (_mystery._noteSelected[found[i]])
+							selected++;
+					}
+					if (selected == expected) {
+						// Commit ??? let the caller do the
+						// `_SolvedCheck` + suspect picker dance.
+						return true;
+					}
+					// Wrong count ??? `_DoAccuse` only triggers the
+					// check when `uStack_8 == uStack_a`; we just
+					// stay in the loop so the player can keep
+					// adjusting.
+					continue;
+				}
+				// Toggle clue under cursor.
+				for (uint i = 0; i < slotRects.size(); i++) {
+					if (slotRects[i].contains(mx, my)) {
+						const uint clueId = slotClues[i];
+						_mystery._noteSelected[clueId] ^= 1;
+						dirty = true;
+						break;
+					}
+				}
+			}
+		}
+		if (dirty)
+			draw();
+		// Per-tick redraw so the partner sprite cycles. Same
+		// 100 ms cadence as `_CheckFrameRate` + `_UpdateAnimations`
+		// in the original (1df2:0bfa).
+		static uint32 sLastTick = 0;
+		const uint32 now = g_system->getMillis();
+		if (now - sLastTick >= 100) {
+			sLastTick = now;
+			draw();
+		}
+		g_system->updateScreen();
+		g_system->delayMillis(15);
+	}
+	return false;
+}
+
 void EEMEngine::doAccuse() {
-	if (!_mystery.isLoaded())
+	if (!_mystery.isLoaded() || !_font.isLoaded())
 		return;
 
 	// Mirrors `_DoAccuse @ 1df2:0bdd` + `_DoAccuseGallery @ 1df2:0a31`:
-	//   1. The original notes-selection screen runs first (PIC 0x1A7).
-	//      We skip it because note-selection lives in the PDA in our
-	//      port ??? the accuse path is reached via the SOLVE button.
-	//   2. EVIDENCE GATE: `_DoAccuse @ 1df2:0bdd` calls `_SolvedCheck`
-	//      (1df2:0c75) before `_DoAccuseGallery`. If `selectedPoints
-	//      <= 99`, the original shows a partner balloon
-	//      (`KDTextIndex[+6]` text + `_SayKDDigital(3)`) and returns
-	//      to `_LastScreen` ??? the suspect picker is NEVER opened.
-	//      Our previous flow opened the picker and only checked after
-	//      the player committed a suspect; that let the player accuse
-	//      without the required evidence.
-	//   3. If the gate passes:
-	//      a. KD intro balloon (`KDTextIndex[+8]` text + `_SayKDDigital(4)`).
-	//      b. `_GetBackground(0x3f)` + `_DrawGallery()` ??? portraits at
-	//         the 5 fixed slots (`29be:0x116`).
-	//      c. Click loop on portraits ??? `_WITCH(picked)` ??? guilty/alibi.
+	//   1. ACCUSE-NOTES SCREEN (PIC 0x1A7, the red "accuse-mode" BG):
+	//      `_DrawNotes(_AccuseNoteRect, NULL, 100, _NoteSelected)`
+	//      lists every found clue inside the rect at `29be:1048` =
+	//      `(79, 27, 304, 159)`. `_UpdateSelectionCount(remaining)`
+	//      shows "N clue(s)" at `(209, 11)` (ASM: `_Show_String(0xb,
+	//      0xd1, ...)` at 1df2:0907). `_NoteUnselectedColor = 1` is
+	//      the dim red used for unselected entries; selected ones
+	//      get `0x3c`. Click toggles via `_SearchNoteAreas` +
+	//      `_SwapColors`. Expected count = `6 - DAT_2d5d_3f99`
+	//      (= 6 - chainStage):
+	//          stage 1 ??? 5 clues, stage 2 ??? 4, stage 3 ??? 3.
+	//      When the count matches, `_SolvedCheck` decides:
+	//          fail ??? KD "not enough evidence" balloon ??? return.
+	//          pass ??? `_DoAccuseGallery()` (suspect picker).
+	//   2. KD intro balloon (`KDTextIndex[+8]` + `_SayKDDigital(4)`).
+	//   3. `_GetBackground(0x3f)` + `_DrawGallery()` ??? portraits at
+	//      the 5 fixed slots (`29be:0x116`).
+	//   4. Click loop on portraits ??? `_WITCH(picked)` ??? guilty/alibi.
 	const uint8 num = _mystery.numSuspects();
 	if (num == 0)
 		return;
 
 	const byte *gd = _mystery.galleryData();
 
+	// ACCUSE-NOTES SCREEN ??? let the player commit which N clues they
+	// believe solve the case. Mirrors the click-driven selection of
+	// `_DoAccuse @ 1df2:0bdd`'s outer loop. ESC / cancel returns to
+	// the site (matches `_DoAccuse @ 1df2:0c11` writing
+	// `_NextScreen = 3` on ESC).
+	if (!doAccuseNotes()) {
+		_nextScreen = _lastScreen != kScreenInvalid
+						? (ScreenId)_lastScreen : kScreenSite;
+		return;
+	}
+
 	// Evidence gate. `_DoAccuse @ 1df2:0c75` runs `_SolvedCheck`
 	// before opening the suspect picker; on failure it renders a
 	// partner balloon over the CURRENT screen (the PDA in the


Commit: 4b19ad71a33e7cbb764e5ada33cb4c4662a5301e
    https://github.com/scummvm/scummvm/commit/4b19ad71a33e7cbb764e5ada33cb4c4662a5301e
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:20:47+02:00

Commit Message:
EEM: missing background when accusation resulted in the correct answer

Changed paths:
    engines/eem/ui.cpp


diff --git a/engines/eem/ui.cpp b/engines/eem/ui.cpp
index 9956e137bd9..962e06210e4 100644
--- a/engines/eem/ui.cpp
+++ b/engines/eem/ui.cpp
@@ -3516,19 +3516,50 @@ void EEMEngine::doAccuse() {
 			}
 		}
 
-		// `_DisplayCorrect @ 1df2:073c` calls `_MIDIPlay(5)` (1df2:0789)
-		// before `_DisplayClue` to swap from the travel music to the
-		// winner cue.
+		// `_DisplayCorrect @ 1df2:073c` order:
+		//   1df2:0773  _AllBlack();
+		//   1df2:0776  _BuildBackground(5, 0x42, 0x14);  // conclusion BG
+		//   1df2:0780  _FadeIn();
+		//   1df2:0789  _MIDIPlay(5);                      // win music
+		//   1df2:07ac  _DisplayClue(MysteryIndex[+0x10]); // chain recap
+		// `_BuildBackground @ 172b:13e2` loads PIC 0x3D (the standard
+		// frame) and overlays SITES.DBD entry 5 at (0x42, 0x14), then
+		// sets palette via `_GetPalette(sitenum + 1)` = palette 6.
+		// Without this BG the chain-recap balloons render on top of
+		// the accuse-gallery BG (PIC 0x3F + suspect portraits), which
+		// is visually jarring ??? the conclusion is supposed to play
+		// against the dedicated "office / desk" scene.
+		Graphics::Surface *blk = g_system->lockScreen();
+		if (blk) {
+			memset(blk->getPixels(), 0, 320 * 200);
+			g_system->unlockScreen();
+		}
+		setSitePalette(6); // sitenum + 1 per `_GetPalette` call
+		Picture frame, scene;
+		if (_picsArchive.loadEntry(0x3d, frame)) {
+			g_system->copyRectToScreen(frame.surface.getPixels(),
+									   frame.surface.pitch, 0, 0,
+									   frame.surface.w, frame.surface.h);
+		}
+		if (5 < _sitesArchive.size() &&
+			_sitesArchive.loadEntry(5, scene)) {
+			const int sx = 0x42, sy = 0x14;
+			const int sw = MIN<int>(scene.surface.w, 320 - sx);
+			const int sh = MIN<int>(scene.surface.h, 200 - sy);
+			if (sw > 0 && sh > 0)
+				g_system->copyRectToScreen(scene.surface.getPixels(),
+										   scene.surface.pitch, sx, sy,
+										   sw, sh);
+		}
+		g_system->updateScreen();
+
 		if (_music)
 			_music->playMus(5, /*loop=*/false);
 
-		// `_DisplayCorrect @ 1df2:07ac` calls
-		// `_DisplayClue(_Mystery + _MysteryIndex[0x10], 0)` BEFORE the
-		// scrapbook animation. That clueblock is the partner's
-		// chain-by-chain RECAP ??? they enumerate every required clue
-		// (`Look at this ??? the suspect was here at 8pm`, `... and
-		// remember the broken vase from the kitchen`, `... so it had
-		// to be X!`) and arrive at the conclusion. Without rendering
+		// Chain-by-chain RECAP. Partner enumerates every required
+		// clue ("Look at this ??? the suspect was here at 8pm", "... and
+		// remember the broken vase from the kitchen", "... so it had
+		// to be X!") and arrives at the conclusion. Without rendering
 		// it the player goes straight from suspect-pick to the
 		// scrapbook anim and misses the deduction entirely.
 		const byte *solved = _mystery.solvedClueBlock();


Commit: 1e6b78b869f2d96642e1e34fd9286cda2db590ab
    https://github.com/scummvm/scummvm/commit/1e6b78b869f2d96642e1e34fd9286cda2db590ab
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:20:47+02:00

Commit Message:
EEM: implement wrong accusation

Changed paths:
    engines/eem/eem.cpp
    engines/eem/eem.h
    engines/eem/site.cpp
    engines/eem/ui.cpp


diff --git a/engines/eem/eem.cpp b/engines/eem/eem.cpp
index 9cf6ab97843..5b419853d44 100644
--- a/engines/eem/eem.cpp
+++ b/engines/eem/eem.cpp
@@ -143,21 +143,36 @@ Common::Error EEMEngine::run() {
 	debugC(1, kDebugGeneral, "EEM engine starting");
 
 	// If the user chose "Load" before pressing Play, the framework
-	// invokes `loadGameState` which sets up `_mystery` and `_partner`.
-	// Honour that by skipping the intros and dropping into the screen-
-	// driver loop just past the briefing ??? the loaded mystery already
-	// knows its current site, so we resume at MAP (the original's
-	// post-briefing state, set by handler 0 at 1a35:0e1d).
+	// invokes `loadGameState` which sets up `_playerName`, `_partner`,
+	// `_mysteriesSolved`, and (optionally) `_mystery`. Honour that by
+	// skipping the intros ??? the player has already typed their name
+	// and picked a partner, so the title chain + profile picker +
+	// partner picker would all be redundant.
+	//
+	//   * Save HAS a mystery in progress ??? resume at MAP (mirrors the
+	//     original's post-briefing state, handler 0 at 1a35:0e1d).
+	//   * Save has NO mystery ??? drop into the case-selection screen
+	//     (`kScreenChooseMystery`) so the player can pick which case
+	//     to play. This matches what the original `_ActionScreen`
+	//     leads to ??? without the redundant action menu in front.
 	const int wantedSave = ConfMan.hasKey("save_slot")
 		? ConfMan.getInt("save_slot") : -1;
 	bool resumed = false;
 	if (wantedSave >= 0) {
 		const Common::Error err = loadGameState(wantedSave);
-		if (err.getCode() == Common::kNoError && _mystery.isLoaded()) {
-			debugC(1, kDebugGeneral, "Resuming from slot %d at mystery %u",
-				   wantedSave, _mystery.number());
+		if (err.getCode() == Common::kNoError) {
 			CursorMan.showMouse(true);
-			_nextScreen = kScreenMap;
+			if (_mystery.isLoaded()) {
+				debugC(1, kDebugGeneral,
+					   "Resuming from slot %d at mystery %u",
+					   wantedSave, _mystery.number());
+				_nextScreen = kScreenMap;
+			} else {
+				debugC(1, kDebugGeneral,
+					   "Resuming profile from slot %d (no mystery ??? "
+					   "??? case selection)", wantedSave);
+				_nextScreen = kScreenChooseMystery;
+			}
 			resumed = true;
 		}
 	}
diff --git a/engines/eem/eem.h b/engines/eem/eem.h
index 525cf4773f7..d58629c9061 100644
--- a/engines/eem/eem.h
+++ b/engines/eem/eem.h
@@ -170,6 +170,9 @@ public:
 	/// followed by 62-byte ClueEntries. Mirrors _DisplayClue @ 2404:05e6.
 	void displayClue(const byte *clueBlock);
 
+	/// Active player name (saved as the profile-save description).
+	const Common::String &playerName() const { return _playerName; }
+
 	/// Apply a single ClueEntry's side effects ??? notebook adds, gallery
 	/// updates, site flags. Called both by `displayClue` after a normal
 	/// click-through and when the player ESC-skips a multi-entry clue.
diff --git a/engines/eem/site.cpp b/engines/eem/site.cpp
index e444fc4b8b7..b453150eb3c 100644
--- a/engines/eem/site.cpp
+++ b/engines/eem/site.cpp
@@ -1374,6 +1374,12 @@ void SiteScreen::onHotspotClicked(uint siteNum, uint hotIdx) {
 			   hotIdx, clueOff);
 		const byte *clueBlock = _mystery->blobAt(clueOff);
 		if (clueBlock) {
+			// Snapshot `_cluesFound` BEFORE the clue display so we
+			// can detect if any new clue was actually collected
+			// (vs. a re-read of an already-found clue) ??? only worth
+			// auto-saving when the player makes progress.
+			byte before[Mystery::kCluesFoundCap];
+			memcpy(before, _mystery->_cluesFound, sizeof(before));
 			// Hand the engine our partner-less backdrop so that
 			// `_DoKDAnim` / `playKdAnim` (the camera-style reaction
 			// animation that fires when a ClueEntry has +0x3a != -1)
@@ -1384,6 +1390,26 @@ void SiteScreen::onHotspotClicked(uint siteNum, uint hotIdx) {
 			_vm->setPartnerEraseBg(&_bgSnapshot);
 			_vm->displayClue(clueBlock);
 			_vm->setPartnerEraseBg(nullptr);
+			// Auto-save when a new clue is found. The original
+			// engine has no autosave (saving is a manual SETUP
+			// button, `_SaveGame @ 2404:0c87`); we add the autosave
+			// here so the player never loses mystery progress.
+			// Detected via 0???1 transition in `_cluesFound[]` (set
+			// by `applyClueSideEffects` inside `displayClue`).
+			bool foundNewClue = false;
+			for (uint i = 0; i < Mystery::kCluesFoundCap; i++) {
+				if (!before[i] && _mystery->_cluesFound[i]) {
+					foundNewClue = true;
+					break;
+				}
+			}
+			if (foundNewClue) {
+				const Common::Error err =
+					_vm->saveProfile(_vm->playerName());
+				if (err.getCode() != Common::kNoError)
+					warning("auto-save after clue failed: %s",
+							err.getDesc().c_str());
+			}
 		}
 	}
 	// Caller (`SiteScreen::run`) re-renders the site after this returns.
diff --git a/engines/eem/ui.cpp b/engines/eem/ui.cpp
index 962e06210e4..9ee920fb720 100644
--- a/engines/eem/ui.cpp
+++ b/engines/eem/ui.cpp
@@ -3414,14 +3414,31 @@ void EEMEngine::doAccuse() {
 		   pickedGuilty ? "yes" : "no",
 		   guessedRight ? "correct" : "wrong");
 
-	// Wrong suspect: render alibi flow. Mirrors `_DisplayAlibi @
-	// 1df2:0145` ??? shows the suspect's alibi text + their picture on
-	// PIC 0x3e, then returns to the accuse gallery for another try.
-	// Skips MIDI 6 (alibi music) and the per-partner voice line ??? the
-	// text rendering alone is enough to convey "this suspect is
-	// innocent". `_FirstTry` is cleared so a subsequent correct pick
-	// no longer counts as a first-try win (1df2:0445 -> _FirstTry=0).
+	// Wrong suspect: full alibi flow. Mirrors `_DisplayAlibi @
+	// 1df2:0145`:
+	//   1. Plays MIDI 6 (loser sting) and waits for it to finish while
+	//      the gallery is still on screen (1df2:0184-1df2:0192).
+	//   2. Draws PIC 0x3e + the suspect's speech balloon + their
+	//      portrait at (0x82, py), where the balloon shape comes from
+	//      `AlibiBubbles[bindx]` (table @ 29be:1050) and bindx is the
+	//      digit-prefix on the alibi text (else 2). bindx<8 centres the
+	//      balloon horizontally; bindx>=8 pins it at x=0x21.
+	//   3. Plays the suspect's voice via `_SpoolSound(talk - 1)` where
+	//      `talk = (Partner == 0) ? gd[+0x6] : gd[+0x0]` (1df2:0258),
+	//      then waits for a click.
+	//   4. Overlays the partner's reaction balloon (text @
+	//      `KDTextIndex[+10]`) at (0x21, y) and plays
+	//      `_SayKDDigital(5)`.
+	//   5. Clears `_FirstTry` (1df2:0447) and returns to LastScreen.
 	if (!guessedRight) {
+		// Balloon-shape table @ 29be:1050 ??? 16 entries ?? u16.
+		static const uint16 kAlibiBubbles[16] = {
+			0x002B, 0x002C, 0x002D, 0x002E,
+			0x00AB, 0x00AC, 0x00AD, 0x00AE,
+			0x001D, 0x001E, 0x0015, 0x0016,
+			0x0017, 0x0018, 0x0019, 0x001A,
+		};
+
 		const uint16 alibiOff = _mystery.alibiTextOffset((uint)picked);
 		Common::String alibi;
 		if (gd && alibiOff != 0xFFFF) {
@@ -3429,46 +3446,230 @@ void EEMEngine::doAccuse() {
 			if (raw)
 				alibi = parseString(raw, _playerName, _partner);
 		}
+		// Digit-prefix dispatch ??? `_DisplayAlibi @ 1df2:0163` reads
+		// `*str` for `bindx` and advances `str = pbVar7 + 1` so the
+		// digit doesn't reach the renderer. Non-digit first chars fall
+		// through to the default bindx=2 (1df2:015e).
+		uint bindx = 2;
+		const byte firstChar = alibi.empty() ? (byte)0 : (byte)alibi[0];
+		if (firstChar >= '0' && firstChar <= '9') {
+			bindx = (uint)(firstChar - '0');
+			alibi.deleteChar(0);
+		}
+		if (bindx >= 16)
+			bindx = 2;
+		const uint16 bubNum = kAlibiBubbles[bindx];
+
 		Picture alibiBg;
-		const bool haveAlibiBg =
-			_picsArchive.getPicture(0x3e, alibiBg);
+		const bool haveAlibiBg = _picsArchive.getPicture(0x3e, alibiBg);
 		Picture suspect;
 		const uint16 picId = gd
 			? READ_LE_UINT16(gd + (uint)picked * 0x46)
 			: 0;
 		const bool haveSuspect = picId != 0 &&
 			_picsArchive.getPicture(picId, suspect);
+		Picture balloon;
+		const bool haveBalloon =
+			_balloonArchive.size() > (bubNum & 0x7F) &&
+			_balloonArchive.loadEntry(bubNum & 0x7F, balloon);
 
-		Graphics::ManagedSurface scratch(320, 200,
+		// Position math from 1df2:01a4-1df2:0207. py is the suspect
+		// portrait's Y; defaults to 0x5a, only overridden in the
+		// bindx<8 branch when the balloon is too tall to fit.
+		int balloonX = 0x21;
+		int balloonY = 1;
+		int py = 0x5a;
+		if (bindx < 8) {
+			const int bw = haveBalloon ? balloon.surface.w : 0;
+			const int bh = haveBalloon ? balloon.surface.h : 0;
+			balloonX = (320 - bw) / 2;
+			if (bh < 0x5a) {
+				balloonY = (0x5a - bh) / 2;
+			} else {
+				balloonY = 1;
+				py = bh;
+			}
+		} else {
+			const int bh = haveBalloon ? balloon.surface.h : 0;
+			balloonX = 0x21;
+			balloonY = (bh < 0x4f) ? (0x50 - bh) / 2 : 1;
+		}
+
+		// `base` = BG + suspect + partner sprite ??? the persistent layer
+		// that survives across both balloon phases. The original engine
+		// keeps PIC 0x3e in the master BG buffer (16000), `_AddPicBackground`
+		// commits the suspect there, and the partner animation
+		// registered by `_DoAccuse @ 1df2:0c30` is re-blitted by every
+		// `_Repaint` via `_DrawActiveAnimations`. We don't have a
+		// slot-based animation system, so we manually keep a "base"
+		// surface and re-draw the partner frame for each phase.
+		Graphics::ManagedSurface base(320, 200,
 			Graphics::PixelFormat::createFormatCLUT8());
-		scratch.clear();
-		if (haveAlibiBg) {
-			scratch.simpleBlitFrom(alibiBg.surface);
-		}
+		base.clear();
+		if (haveAlibiBg)
+			base.simpleBlitFrom(alibiBg.surface);
 		if (haveSuspect) {
-			// Original `_DisplayAlibi` blits the suspect at (0x82, py)
-			// where py varies by balloon size; we use a fixed centred
-			// position since we don't render the balloon shapes yet.
 			const byte transp = (byte)(suspect.flags >> 8);
-			scratch.transBlitFrom(suspect.surface,
-								  Common::Point(0x82, 0x40),
+			base.transBlitFrom(suspect.surface,
+							   Common::Point(0x82, py),
+							   (uint32)transp);
+		}
+		// Partner sprite at (5, 0x50). Anim cells: 2 (Jake) / 0x10
+		// (Jenny); script key 0x02 ??? same indices `_DoAccuse @
+		// 1df2:0c30` uses for its `_NewAnimation` call. Partner is
+		// drawn AFTER the suspect so it doesn't get clipped by the
+		// portrait if their bounding boxes graze.
+		const uint partnerAnim = (_partner == 0) ? 2 : 0x10;
+		Animation partnerAni;
+		const bool havePartner =
+			_aniArchive.loadAnimation(partnerAnim, partnerAni) &&
+			!partnerAni.empty();
+
+		// Alibi-phase scratch = base + alibi balloon + alibi text +
+		// partner sprite (animation slot drawn on top per
+		// `_DrawActiveAnimations`).
+		Graphics::ManagedSurface scratch(320, 200,
+			Graphics::PixelFormat::createFormatCLUT8());
+		scratch.simpleBlitFrom(base);
+		if (haveBalloon) {
+			const byte transp = (byte)(balloon.flags >> 8);
+			scratch.transBlitFrom(balloon.surface,
+								  Common::Point(balloonX, balloonY),
 								  (uint32)transp);
 		}
+		// Balloon-text inset table @ 29be:0875 ??? same dispatch as KD
+		// balloons. WordWrap color is 0 inside a balloon (1df2:0240).
+		uint16 tx = 5, ty = 4, tw = 155;
+		getBalloonInsets(bubNum, tx, ty, tw);
 		if (_font.isLoaded() && !alibi.empty()) {
-			_font.drawWordWrapped(&scratch, 16, 8, 200, alibi, 0xF);
-			_font.drawString(&scratch, "(click / ESC: back)",
-							 16, 188, 200, 0xF);
+			_font.drawWordWrapped(&scratch, balloonX + tx,
+								  balloonY + ty, tw, alibi,
+								  haveBalloon ? 0 : 0xF);
+		}
+		if (havePartner) {
+			const uint frameIdx = partnerFrameAtTick(0x02,
+				(uint)partnerAni.size(), g_system->getMillis());
+			blitAnimFrameAnchored(scratch.surfacePtr(),
+								  partnerAni[frameIdx], 5, 0x50);
 		}
+
+		// Step 1 ??? alibi music. Original blocks until MIDI 6 ends with
+		// the gallery still on screen. We poll `_music->isPlaying`;
+		// click/ESC aborts early.
+		if (_music) {
+			_music->playMus(6, /*loop=*/false);
+			const uint32 musStart = g_system->getMillis();
+			bool aborted = false;
+			while (_music->isPlaying() && !shouldQuit() && !aborted) {
+				Common::Event ev;
+				while (g_system->getEventManager()->pollEvent(ev)) {
+					if (ev.type == Common::EVENT_QUIT ||
+						ev.type == Common::EVENT_RETURN_TO_LAUNCHER)
+						return;
+					if (ev.type == Common::EVENT_KEYDOWN ||
+						ev.type == Common::EVENT_LBUTTONDOWN) {
+						aborted = true;
+						break;
+					}
+				}
+				// Hard cap so we never get stuck if MIDI never reports
+				// finish (some sound configurations).
+				if (g_system->getMillis() - musStart > 10000)
+					break;
+				g_system->updateScreen();
+				g_system->delayMillis(20);
+			}
+			_music->stop();
+		}
+
+		// Step 2 ??? flip the alibi scene to screen + play suspect voice.
+		// `talk = (Partner==0) ? gd[+0x6] : gd[+0x0]` (1df2:0252-0258);
+		// indices are 1-based so subtract 1 before SpoolSound.
 		g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
 								   0, 0, 320, 200);
 		g_system->updateScreen();
-		waitForInput(20000);
+		if (_audio && gd) {
+			const uint16 alibiVoice =
+				READ_LE_UINT16(gd + (uint)picked * 0x46 + 0x00);
+			const uint16 jakeVoice =
+				READ_LE_UINT16(gd + (uint)picked * 0x46 + 0x06);
+			const uint16 talk =
+				(_partner == 0) ? jakeVoice : alibiVoice;
+			if (talk != 0)
+				_audio->spoolSound((uint)(talk - 1));
+		}
+		waitForInput(60000);
+
+		// Step 3 ??? partner reaction balloon. Mirrors 1df2:026e-1df2:02b6.
+		// Rebuild scratch from `base` (BG + suspect + partner sprite)
+		// so the alibi balloon + text don't bleed through. The
+		// original's `_Repaint` re-renders the master BG (which still
+		// has the alibi balloon committed), but the engine ALSO calls
+		// `_GetBackground(0x3e)` again before `_AddPicBackground` for
+		// each new balloon ??? flushing the master back to a clean state.
+		// We achieve the same end result by restoring `base` here.
+		// `_SayKDDigital(5)` auto-cancels the still-playing alibi voice
+		// (spoolSound calls stopSpool internally) so no explicit stop.
+		const byte *reactIdx = _mystery.kdTextIndex();
+		if (reactIdx) {
+			const int16 reactOff = (int16)READ_LE_UINT16(reactIdx + 10);
+			Common::String react;
+			if (reactOff != -1) {
+				const char *raw = _mystery.textAt((uint16)reactOff);
+				if (raw)
+					react = parseString(raw, _playerName, _partner);
+			}
+			if (!react.empty()) {
+				const byte rChar = (byte)react[0];
+				const uint16 rBub = getKDTextBalloon(rChar);
+				if (rChar >= '0' && rChar <= '9')
+					react.deleteChar(0);
+				Picture rBalloon;
+				const bool haveR =
+					_balloonArchive.size() > (rBub & 0x7F) &&
+					_balloonArchive.loadEntry(rBub & 0x7F, rBalloon);
+				const int rX = 0x21;
+				int rY = 1;
+				if (haveR && rBalloon.surface.h < 0x4e)
+					rY = (0x50 - rBalloon.surface.h) / 2;
+
+				// Reset to a clean BG + suspect, then layer the new
+				// balloon and the (refreshed) partner frame on top.
+				scratch.simpleBlitFrom(base);
+				if (haveR) {
+					const byte transp = (byte)(rBalloon.flags >> 8);
+					scratch.transBlitFrom(rBalloon.surface,
+										   Common::Point(rX, rY),
+										   (uint32)transp);
+				}
+				uint16 rtx = 5, rty = 4, rtw = 155;
+				getBalloonInsets(rBub, rtx, rty, rtw);
+				if (_font.isLoaded()) {
+					_font.drawWordWrapped(&scratch, rX + rtx,
+										  rY + rty, rtw, react,
+										  haveR ? 0 : 0xF);
+				}
+				if (havePartner) {
+					const uint frameIdx = partnerFrameAtTick(0x02,
+						(uint)partnerAni.size(),
+						g_system->getMillis());
+					blitAnimFrameAnchored(scratch.surfacePtr(),
+										  partnerAni[frameIdx],
+										  5, 0x50);
+				}
+				g_system->copyRectToScreen(scratch.getPixels(),
+					scratch.pitch, 0, 0, 320, 200);
+				g_system->updateScreen();
+				if (_audio)
+					_audio->sayKDDigital(reactIdx, 5, _partner);
+			}
+		}
+		waitForInput(60000);
 
 		_mystery._firstTry = false;
 		// `_DisplayAlibi @ 1df2:043f` writes `_NextScreen =
-		// _LastScreen` so the player drops back to wherever they
-		// accused from (typically the site loop). With no mystery
-		// state to unload, the site loop resumes naturally.
+		// _LastScreen`. The original returns the player to the caller
+		// (PDA / site / map) for another try.
 		_nextScreen = _lastScreen != kScreenInvalid
 						? (ScreenId)_lastScreen : kScreenSite;
 		return;


Commit: ddb7949f39880d791315b1b621db0b8437bdaa54
    https://github.com/scummvm/scummvm/commit/ddb7949f39880d791315b1b621db0b8437bdaa54
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:20:48+02:00

Commit Message:
EEM: fixed some issues in the win screen/newspaper

Changed paths:
    engines/eem/eem.h
    engines/eem/site.cpp
    engines/eem/ui.cpp


diff --git a/engines/eem/eem.h b/engines/eem/eem.h
index d58629c9061..875d62f608c 100644
--- a/engines/eem/eem.h
+++ b/engines/eem/eem.h
@@ -368,7 +368,19 @@ private:
 	/// `_ShowOneScrap @ 1f78:0773` is just `_DisplayEnding(num, 1)`,
 	/// so this same call covers the post-mystery scrapbook view from
 	/// the action menu.
-	void doShowEnding(uint num);
+	///
+	/// `firstPage=true` opens at page 0; `false` opens at the last
+	/// page (used by `doShowScrapbook` when navigating backwards
+	/// between mysteries ??? mirrors the `local_8 = 0` write at
+	/// `_ShowScrapbook @ 1f78:067e`).
+	///
+	/// Returns the direction the user wants the caller to navigate:
+	///   -1 ??? previous mystery (LEFT pressed on the first page or
+	///        click in `PrevPageRect` while on first page),
+	///    0 ??? exit the scrapbook (ESC or click outside both rects),
+	///   +1 ??? next mystery (RIGHT/SPACE/Enter/click on last page).
+	/// Mirrors `_DisplayEnding`'s `[BP-0x18]` return at 1df2:0723.
+	int doShowEnding(uint num, bool firstPage = true);
 
 	/// Walk every solved mystery in tier @p stage (1=Junior, 2=Senior,
 	/// 3=Master) and display each one's ending pages in sequence.
diff --git a/engines/eem/site.cpp b/engines/eem/site.cpp
index b453150eb3c..4131e6dfafb 100644
--- a/engines/eem/site.cpp
+++ b/engines/eem/site.cpp
@@ -813,6 +813,15 @@ void SiteScreen::run() {
 		if (exitRequested)
 			return;
 
+		// `doAccuse` on a win clears the mystery (so the screen driver
+		// can route to the post-mystery menu). Notebook / Gallery /
+		// hotspot paths route through this same loop, so a transitive
+		// `doAccuse` may have wiped `_mystery` underneath us ??? exit
+		// immediately rather than tick another frame against stale BG
+		// snapshots / hotspot tables.
+		if (!_mystery || !_mystery->isLoaded())
+			return;
+
 		// Per-tick frame pump (mirrors `_CheckFrameRate` +
 		// `_UpdateAnimations` at the top of `_DoSiteLoop`'s main loop).
 		// Restore the static BG snapshot, redraw animated NPCs +
diff --git a/engines/eem/ui.cpp b/engines/eem/ui.cpp
index 9ee920fb720..07430ce1012 100644
--- a/engines/eem/ui.cpp
+++ b/engines/eem/ui.cpp
@@ -491,7 +491,7 @@ void EEMEngine::doNewPlayer() {
 	}
 }
 
-void EEMEngine::doShowEnding(uint num) {
+int EEMEngine::doShowEnding(uint num, bool firstPage) {
 	// Mirrors `_DisplayEnding @ 1df2:0548` + `_DisplayEndingPage @
 	// 1df2:044c`. File format (verified by reading E0.BIN's bytes):
 	//   u16 pageCount
@@ -500,32 +500,63 @@ void EEMEngine::doShowEnding(uint num) {
 	//     u16 x1, y1, x2, y2  (story rect ??? passed to WordWrap)
 	//     char text[]        (null-terminated, ParseString opcodes)
 	//
-	// The original walks pages with PrevPage / NextPage rects (29be:
-	// 0x..); we approximate with mouse-click / Enter = next, ESC =
-	// exit. ParseString substitutes `\x80` (player name) and the rest
-	// of the 0x80..0x89 placeholder family so the rendered text
-	// reads as the correct player + partner combo.
+	// The original swaps the font: `_FreeFont(); _LoadFont("tiny.fnt")`
+	// at 1df2:055f-1df2:0563, calls `_GetPalette(0)` (site palette 0),
+	// then for each page `_GetBackground(picNum)` +
+	// `_WordWrap2(x1, y1, x2-x1, text, fontColor=0, dropColor=-1)`. The
+	// fontColor=0 draws in palette index 0 (the newspaper's body-text
+	// colour), with no drop shadow. Verified at the call site asm
+	// 1df2:04cf-1df2:04f4 (Ghidra mis-paired the two trailing args).
+	//
+	// Page navigation mirrors the original key/click handlers
+	// (1df2:0689 / 1df2:06a0): LEFT decrements pageIdx, RIGHT (or
+	// SPACE / Enter / click) increments it. Hitting the boundary
+	// (LEFT on page 0, RIGHT on last page) sets `[BP-0x18]` to -1 / 1
+	// respectively and exits ??? that return value is what
+	// `_ShowScrapbook` uses to walk forward / backward through
+	// solved mysteries (see 1f78:0664-1f78:069c). ESC and clicks
+	// outside both PrevPage / NextPage rects exit with `[BP-0x18]=0`.
+	//
+	// `firstPage=false` opens the ending at the LAST page (used by
+	// `doShowScrapbook` after a "previous mystery" navigation ???
+	// matches `local_8 = 0` written before the back-step at
+	// 1f78:067e).
 	const Common::String fname = Common::String::format("E%u.BIN", num);
 	Common::File f;
 	if (!f.open(Common::Path(fname))) {
 		warning("doShowEnding: %s missing", fname.c_str());
-		return;
+		return 0;
 	}
 	const uint32 size = f.size();
 	if (size < 2) {
 		warning("doShowEnding: %s too small (%u bytes)",
 				fname.c_str(), size);
-		return;
+		return 0;
 	}
 	Common::Array<byte> buf(size);
 	if (f.read(buf.data(), size) != size) {
 		warning("doShowEnding: %s short read", fname.c_str());
-		return;
+		return 0;
 	}
 
 	const uint16 pageCount = READ_LE_UINT16(buf.data());
 	if (pageCount == 0)
-		return;
+		return 0;
+
+	// Mirrors 1df2:0558-1df2:056a ??? `_FreeFont(); _LoadFont(tiny.fnt)`.
+	// The newspaper layout uses TINY.FNT (smaller glyphs) so the body
+	// copy fits in the columns. `_LoadFont(font.fnt)` is restored at
+	// 1df2:0625 after the page loop.
+	EEMFont tinyFont;
+	const bool haveTinyFont = tinyFont.load(Common::Path("TINY.FNT"));
+	if (!haveTinyFont)
+		warning("doShowEnding: TINY.FNT failed to load ??? falling back");
+
+	// Mirrors 1df2:055f `_GetPalette(0)` ??? site palette 0 is the
+	// shared "newspaper" CLUT for ending pages. The newspaper body
+	// text in particular is palette index 0 (= newspaper black) so we
+	// MUST switch palettes before rendering.
+	setSitePalette(0);
 
 	// Walk page records. Each page header is 10 bytes; text is
 	// null-terminated and follows the header.
@@ -544,9 +575,11 @@ void EEMEngine::doShowEnding(uint num) {
 		cursor++;  // past the null
 	}
 
-	uint pageIdx = 0;
+	uint pageIdx = firstPage ? 0 : (kMaxPages - 1);
+	int direction = 0;     // -1 / 0 / +1, see header doc.
+	bool exitLoop = false;
 	bool dirty = true;
-	while (!shouldQuit() && pageIdx < kMaxPages) {
+	while (!shouldQuit() && !exitLoop) {
 		if (dirty) {
 			const uint off = pageOffsets[pageIdx];
 			if (off + 10 >= size)
@@ -557,9 +590,6 @@ void EEMEngine::doShowEnding(uint num) {
 			const uint16 x2     = READ_LE_UINT16(buf.data() + off + 6);
 			(void)READ_LE_UINT16(buf.data() + off + 8);  // y2 (unused ??? WordWrap2 takes width only)
 
-			// Halve the rect coords: ending pages use 320x400 logical
-			// coords (x2=0x128=296, y2=0xa8=168 in our test file
-			// E0.BIN ??? both already 320x200 mode 13h). No conversion.
 			Picture bg;
 			Graphics::ManagedSurface scratch(320, 200,
 				Graphics::PixelFormat::createFormatCLUT8());
@@ -578,13 +608,20 @@ void EEMEngine::doShowEnding(uint num) {
 			const char *raw = (const char *)buf.data() + off + 10;
 			const Common::String text = parseString(raw, _playerName, _partner);
 
-			if (_font.isLoaded() && x2 > x1) {
+			// Use TINY.FNT (`_LoadFont(@29be:10a5)` at 1df2:055f-0563)
+			// and color 0 (`_WordWrap2(...,0,-1)` per asm at 1df2:04cf,
+			// not 0xF as Ghidra's decompile output suggests). Falls
+			// back to the main font if TINY.FNT failed to load.
+			const EEMFont &renderFont = haveTinyFont ? tinyFont : _font;
+			if (renderFont.isLoaded() && x2 > x1) {
 				const int textW = MIN<int>((int)x2 - (int)x1, 320 - (int)x1);
-				_font.drawWordWrapped(&scratch, (int)x1, (int)y1,
-									  textW, text, 0xF);
+				renderFont.drawWordWrapped(&scratch, (int)x1, (int)y1,
+										   textW, text, 0);
 			}
 
-			// Page indicator at top-right ("page 1/3").
+			// Page indicator at top-right ("page 1/3"). Stays in the
+			// main font + color 0xF so it doesn't blend into the
+			// newspaper masthead.
 			if (_font.isLoaded() && kMaxPages > 1) {
 				const Common::String hdr = Common::String::format(
 					"%u/%u", (unsigned)pageIdx + 1, (unsigned)kMaxPages);
@@ -600,14 +637,25 @@ void EEMEngine::doShowEnding(uint num) {
 		Common::Event ev;
 		while (g_system->getEventManager()->pollEvent(ev)) {
 			if (ev.type == Common::EVENT_QUIT ||
-				ev.type == Common::EVENT_RETURN_TO_LAUNCHER)
-				return;
+				ev.type == Common::EVENT_RETURN_TO_LAUNCHER) {
+				return 0;
+			}
 			if (ev.type == Common::EVENT_KEYDOWN) {
 				switch (ev.kbd.keycode) {
 				case Common::KEYCODE_ESCAPE:
-					return;
+					// ESC is the ONLY way out of the newspaper view.
+					// Departs from the original `_DisplayEnding`, which
+					// also exits on boundary arrow keys / clicks
+					// (1df2:0689 / 1df2:06a0); the boundary-exit path is
+					// what fed `[BP-0x18]` to `_ShowScrapbook` for
+					// per-mystery scrapbook navigation. We don't expose
+					// that ??? clicking ESC closes the scrapbook entirely.
+					direction = 0;
+					exitLoop = true;
+					break;
 				case Common::KEYCODE_LEFT:
 				case Common::KEYCODE_PAGEUP:
+					// Clamp at page 0 ??? never exit on LEFT.
 					if (pageIdx > 0) {
 						pageIdx--;
 						dirty = true;
@@ -618,21 +666,45 @@ void EEMEngine::doShowEnding(uint num) {
 				case Common::KEYCODE_RETURN:
 				case Common::KEYCODE_KP_ENTER:
 				case Common::KEYCODE_SPACE:
-					pageIdx++;
-					dirty = true;
+				case Common::KEYCODE_TAB:
+					// Clamp at last page ??? never exit on RIGHT either.
+					if (pageIdx + 1 < kMaxPages) {
+						pageIdx++;
+						dirty = true;
+					}
 					break;
 				default:
 					break;
 				}
+				if (exitLoop)
+					break;
 			}
 			if (ev.type == Common::EVENT_LBUTTONDOWN) {
-				pageIdx++;
-				dirty = true;
+				// Mouse clicks shift pages too ??? never exit on click.
+				// Use the original PrevPage/NextPage rect split for
+				// click direction (29be:1078 / 29be:1080); clicks
+				// outside both rects fall through to next-page so the
+				// player still gets some feedback.
+				const Common::Rect kPrevPageRect(0, 0, 27, 200);
+				if (kPrevPageRect.contains(ev.mouse.x, ev.mouse.y)) {
+					if (pageIdx > 0) {
+						pageIdx--;
+						dirty = true;
+					}
+				} else {
+					if (pageIdx + 1 < kMaxPages) {
+						pageIdx++;
+						dirty = true;
+					}
+				}
+				if (exitLoop)
+					break;
 			}
 		}
 		g_system->updateScreen();
 		g_system->delayMillis(15);
 	}
+	return direction;
 }
 
 void EEMEngine::doShowScrapbook(uint stage) {
@@ -654,7 +726,14 @@ void EEMEngine::doShowScrapbook(uint stage) {
 	const uint hi = lo + 0x17;
 	const bool currentTier = (stage == _chainStage);
 
-	for (uint m = lo; m <= hi; m++) {
+	// `doShowEnding` only reports `direction = 0` now (ESC), so we
+	// can't use the original 1f78:067e/0698 forward-backward walk.
+	// Instead iterate every solved mystery in the tier linearly:
+	// each ESC closes the current newspaper and moves us to the
+	// next solved entry. Departs from `_ShowScrapbook @ 1f78:0642`
+	// (which let the player browse via the boundary keys), but
+	// matches the user-requested "ESC is the only exit" rule.
+	for (uint m = lo; m <= hi && !shouldQuit(); m++) {
 		if (m >= sizeof(_mysteriesSolved))
 			break;
 		// Current-tier filter (1f78:0664). Completed tiers show all
@@ -662,9 +741,7 @@ void EEMEngine::doShowScrapbook(uint stage) {
 		// the player hasn't earned that scrapbook page yet.
 		if (currentTier && _mysteriesSolved[m] == 0)
 			continue;
-		doShowEnding(m);
-		if (shouldQuit())
-			return;
+		(void)doShowEnding(m, /*firstPage=*/true);
 	}
 }
 
@@ -3752,6 +3829,30 @@ void EEMEngine::doAccuse() {
 										   scene.surface.pitch, sx, sy,
 										   sw, sh);
 		}
+
+		// Partner sprite at (5, 0x50). The original `_DoAccuse @
+		// 1df2:0c30` registered the partner anim BEFORE entering the
+		// gallery; that slot stays active across `_DisplayCorrect`'s
+		// `_BuildBackground` (which calls `_Repaint` ???
+		// `_DrawActiveAnimations` and re-blits the partner over the
+		// fresh BG). We don't have a slot system, so manually stamp
+		// the resting frame here. `displayClue` snapshots the screen
+		// on entry, so the partner ends up baked into its BG and is
+		// preserved across every clue iteration.
+		const uint partnerAnim = (_partner == 0) ? 2 : 0x10;
+		Animation partnerAni;
+		if (_aniArchive.loadAnimation(partnerAnim, partnerAni) &&
+			!partnerAni.empty()) {
+			Graphics::Surface *screen = g_system->lockScreen();
+			if (screen) {
+				const uint frameIdx = partnerFrameAtTick(0x02,
+					(uint)partnerAni.size(),
+					g_system->getMillis());
+				blitAnimFrameAnchored(screen, partnerAni[frameIdx],
+									  5, 0x50);
+				g_system->unlockScreen();
+			}
+		}
 		g_system->updateScreen();
 
 		if (_music)


Commit: 3761f15c6e38792ba88b59f34f36e1eb5b6042c7
    https://github.com/scummvm/scummvm/commit/3761f15c6e38792ba88b59f34f36e1eb5b6042c7
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:20:48+02:00

Commit Message:
EEM: fixed missing screen updates

Changed paths:
    engines/eem/eem.cpp
    engines/eem/site.cpp


diff --git a/engines/eem/eem.cpp b/engines/eem/eem.cpp
index 5b419853d44..8386f08be13 100644
--- a/engines/eem/eem.cpp
+++ b/engines/eem/eem.cpp
@@ -536,6 +536,12 @@ void EEMEngine::playAnm(const Common::Path &path, uint frameDelayMs, bool holdLa
 					break;
 				}
 			}
+			// Refresh ScummVM's cursor overlay every tick ??? without
+			// this the cursor only redraws when the next frame is
+			// blitted (every `frameDelayMs` ms, ~8 Hz at 120 ms),
+			// which the user perceives as choppy / unresponsive
+			// during long animations like SCRAPBK.ANI.
+			g_system->updateScreen();
 			g_system->delayMillis(5);
 		}
 		if (aborted)
diff --git a/engines/eem/site.cpp b/engines/eem/site.cpp
index 4131e6dfafb..5869b827d53 100644
--- a/engines/eem/site.cpp
+++ b/engines/eem/site.cpp
@@ -1523,7 +1523,11 @@ void EEMEngine::playKdAnim(uint16 num) {
 
 		// One frame per `_CheckFrameRate` tick. The original calibrates
 		// this to ~10 fps; 100 ms matches what the rest of the engine
-		// uses for partner / NPC frame cycling.
+		// uses for partner / NPC frame cycling. Pump `updateScreen`
+		// inside the inner wait so ScummVM's cursor overlay refreshes
+		// at the same rate as the wait granularity (10 ms ??? 100 Hz)
+		// rather than only on the per-frame redraw ??? without this the
+		// cursor only refreshes once every 100 ms during the anim.
 		const uint32 wakeup = g_system->getMillis() + 100;
 		while (g_system->getMillis() < wakeup && !shouldQuit()) {
 			Common::Event ev;
@@ -1535,6 +1539,7 @@ void EEMEngine::playKdAnim(uint16 num) {
 					ev.type == Common::EVENT_RETURN_TO_LAUNCHER)
 					return;
 			}
+			g_system->updateScreen();
 			g_system->delayMillis(10);
 		}
 	}


Commit: f9a5f920da384227cedd99d5f7b924dd5e3abbe7
    https://github.com/scummvm/scummvm/commit/f9a5f920da384227cedd99d5f7b924dd5e3abbe7
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:20:48+02:00

Commit Message:
EEM: correctly load game when a mystery is solved

Changed paths:
    engines/eem/ui.cpp


diff --git a/engines/eem/ui.cpp b/engines/eem/ui.cpp
index 07430ce1012..1d0dc8091d9 100644
--- a/engines/eem/ui.cpp
+++ b/engines/eem/ui.cpp
@@ -1238,6 +1238,14 @@ void EEMEngine::doCaseSelection() {
 	// Reassert here in case anything between hid it.
 	CursorMan.showMouse(true);
 
+	// Reassert site palette 0 (the case-selection / chooser CLUT). In
+	// the normal flow `doProfilePicker` (or the post-screen reset paths
+	// at lines 1402 / 1147 / 1121) leaves us on palette 0 already, but
+	// the launcher-resume path jumps straight here from `_AllBlack`
+	// (palette = all-zero) ??? without this the BG renders into a black
+	// CLUT and the player sees an empty screen.
+	setSitePalette(0);
+
 	// Mirrors `_CaseSelection`: load PIC 0x41 as the chooser backdrop.
 	Picture caseBg;
 	const bool haveCaseBg = _picsArchive.getPicture(0x41, caseBg);
@@ -3880,21 +3888,23 @@ void EEMEngine::doAccuse() {
 		// Mirrors `_SavePlayerRecord` at 1df2:0857 ??? once the
 		// `_mysteriesSolved` table is updated, the original
 		// immediately persists the player record so the win sticks
-		// even if the player quits before reaching the menu. We do
-		// the same by writing back to the active profile (the slot
-		// keyed on `_playerName`) rather than clobbering slot 0 like
-		// a generic quicksave.
+		// even if the player quits before reaching the menu.
+		//
+		// Order matters: `_mystery.clear()` BEFORE `saveProfile` so the
+		// save records `hasMystery=false`. Otherwise the next load of
+		// this profile sees the just-won mystery still loaded and the
+		// screen driver routes to its map (forcing the player to
+		// replay the win flow). Mirrors `_DisplayCorrect @ 1df2:0851`
+		// (`_DeleteSavedGame` removes the in-progress save before
+		// `_SavePlayerRecord` writes the post-win profile).
+		_mystery.clear();
 		const Common::Error err = saveProfile(_playerName);
 		if (err.getCode() != Common::kNoError)
 			warning("saveProfile after solve failed: %s",
 					err.getDesc().c_str());
 
-		// `_DisplayCorrect @ 1df2:0895` writes `_NextScreen = 0xc`
-		// ??? the winner returns to the post-mystery `_ActionScreen`.
-		// Free the mystery first so the loop can break out cleanly:
-		// `_DeleteSavedGame` at 1df2:0851 + `_FreeMystery` at
-		// 1df2:08a4 do the same.
-		_mystery.clear();
+		// `_DisplayCorrect @ 1df2:0895` writes `_NextScreen = 0xc` ???
+		// the winner returns to the post-mystery `_ActionScreen`.
 		_nextScreen = kScreenAction;
 	}
 }


Commit: 1131e4825f98fc8f307bc9dd2c0dd43506747398
    https://github.com/scummvm/scummvm/commit/1131e4825f98fc8f307bc9dd2c0dd43506747398
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:20:49+02:00

Commit Message:
EEM: implemented screen to load mysteries from different books

Changed paths:
    engines/eem/ui.cpp


diff --git a/engines/eem/ui.cpp b/engines/eem/ui.cpp
index 1d0dc8091d9..1737c1d60ac 100644
--- a/engines/eem/ui.cpp
+++ b/engines/eem/ui.cpp
@@ -99,17 +99,65 @@ struct CaseSelectionView {
 	uint pick;
 };
 
+// Mystery list shown in the "Choose A Mystery" sub-screen. Mirrors
+// `_DoChooseMystery @ 1a35:02b7`: opens BOOK%u.NME (CRLF-separated
+// ASCII strings, last entry is whitespace = sentinel), reads up to 25
+// lines ?? 40 bytes each, hands the array to `_CaseSelection`. We
+// preserve the trailing whitespace line as the original sentinel
+// since `_DoChoose @ 1c33:0514` walks until `*piVar3 == 0 && piVar3[1]
+// == 0` ??? but for our renderer we just keep the names array.
+Common::StringArray loadBookNames(uint book) {
+	Common::StringArray names;
+	const Common::String fname = Common::String::format("BOOK%u.NME", book);
+	Common::File f;
+	if (!f.open(Common::Path(fname))) {
+		warning("loadBookNames: %s missing", fname.c_str());
+		return names;
+	}
+	while (!f.eos()) {
+		Common::String line = f.readLine();
+		if (f.eos() && line.empty())
+			break;
+		// `_fgets` in the original reads CRLF terminators with the line;
+		// `Common::File::readLine` strips them, so `line` here is the
+		// text only. Trim trailing whitespace so the sentinel "        "
+		// last entry doesn't render as a blank scrollable row.
+		while (!line.empty() &&
+			   (line.lastChar() == ' ' || line.lastChar() == '\t' ||
+				line.lastChar() == '\r'))
+			line.deleteLastChar();
+		if (line.empty())
+			continue;
+		names.push_back(line);
+	}
+	return names;
+}
+
 // Per-mystery sub-chooser ("Choose A Mystery") view.
+//
+// `names` are the entries from BOOK%d.NME (in display order ??? index 0
+// = first case in the tier, mystery number = `tierLo + index`).
+// `solvedFlags` is a parallel bool array indicating which entries are
+// already solved (greyed and unselectable in `_DoChoose`).
+// `topRow` is the scroll position; up to 12 entries are visible.
+// `selRow` is the highlighted row (0-based within the names array).
 struct CaseSubmenuView {
 	EEMEngine *vm;
 	const Picture *caseBg;
 	bool haveCaseBg;
-	const byte *mysteriesSolved;
-	uint mysteriesSolvedSize;
-	uint sel;
-	uint maxMystery;
+	const Common::StringArray *names;
+	const Common::Array<bool> *solvedFlags;
+	uint topRow;
+	uint selRow;
+	uint book;            ///< 1..3 ??? for the "Book N" / "Challenge Book" title
 };
 
+// Mirrors `_DoChoose`'s `DrawList @ 1c33:040d`. 12 visible rows ?? 10 px
+// at (61, 35); colour palette: 0x13 = highlighted (selected), 0x1B =
+// greyed (already solved), 0x5C = default. We approximate with the
+// closest indices of site palette 0 ??? 0xF (white) / 0x7 (medium grey)
+// / 0x8 (dark grey) ??? since we don't decode the original CLUT byte
+// ramp.
 void drawCaseSubmenu(const CaseSubmenuView &v) {
 	Graphics::ManagedSurface scratch(320, 200,
 		Graphics::PixelFormat::createFormatCLUT8());
@@ -121,34 +169,70 @@ void drawCaseSubmenu(const CaseSubmenuView &v) {
 			memcpy((byte *)scratch.getBasePtr(0, row),
 				   (const byte *)v.caseBg->surface.getBasePtr(0, row), w);
 	}
-	if (v.vm->getFont().isLoaded()) {
-		const int kListX  = 61;
-		const int kListW  = 238 - kListX;
-		const int kListY0 = 35;
-		const int kLineH  = 10;
-		const int kVisible = 12;
-		int top = (int)v.sel - kVisible / 2;
-		if (top < 0)
-			top = 0;
-		if (top + kVisible > (int)v.maxMystery + 1)
-			top = (int)v.maxMystery + 1 - kVisible;
-		for (int r = 0; r < kVisible; r++) {
-			const int idx = top + r;
-			if (idx > (int)v.maxMystery)
-				break;
-			char marker = ' ';
-			if ((uint)idx < v.mysteriesSolvedSize) {
-				if (v.mysteriesSolved[idx] == 2)
-					marker = '*';
-				else if (v.mysteriesSolved[idx] == 1)
-					marker = '+';
-			}
-			const char arrow = ((uint)idx == v.sel) ? '>' : ' ';
-			v.vm->getFont().drawString(&scratch,
-				Common::String::format("%c %c Mystery %d", arrow, marker, idx),
-				kListX, kListY0 + r * kLineH, kListW, 0xF);
+	if (!v.vm->getFont().isLoaded() || !v.names)
+		return;
+
+	// Top centred title. `_CaseSelection @ 1c33:0aa3` formats "Book %d"
+	// for tiers 1/2 and "Challenge Book" (sprintf with no arg) for
+	// tier 3. `_Show_String(0xc, (0xba - width)/2 + 0x3c, ???, 0x10)`
+	// places it horizontally centred over the panel.
+	const Common::String title = (v.book == 3)
+		? Common::String("Challenge Book")
+		: Common::String::format("Book %u", v.book);
+	const int titleW = v.vm->getFont().getStringWidth(title);
+	const int titleX = (0xba - titleW) / 2 + 0x3c;
+	v.vm->getFont().drawString(&scratch, title, titleX, 12, 320, 0xF);
+
+	const int kListX  = 61;
+	const int kListW  = 238 - kListX;
+	const int kListY0 = 35;
+	const int kLineH  = 10;
+	const int kVisible = 12;
+	const uint count = (uint)v.names->size();
+
+	for (int r = 0; r < kVisible; r++) {
+		const uint idx = v.topRow + (uint)r;
+		if (idx >= count)
+			break;
+		const Common::String &name = (*v.names)[idx];
+		byte color = 0xF;  // default
+		if (idx == v.selRow) {
+			color = 0xF;   // highlighted
+		} else if (v.solvedFlags && idx < v.solvedFlags->size() &&
+				   (*v.solvedFlags)[idx]) {
+			color = 0x8;   // greyed (already solved)
+		} else {
+			color = 0x7;   // normal
 		}
+		v.vm->getFont().drawString(&scratch, name,
+			kListX, kListY0 + r * kLineH, kListW, color);
+	}
+
+	// Selection arrow at the left edge of the highlighted row ??? the
+	// original highlights via colour change but adding an arrow makes
+	// the keyboard-driven path obvious.
+	if (v.selRow >= v.topRow && v.selRow < v.topRow + (uint)kVisible) {
+		const int r = (int)(v.selRow - v.topRow);
+		v.vm->getFont().drawString(&scratch, ">",
+			kListX - 6, kListY0 + r * kLineH, 6, 0xF);
 	}
+
+	// Scrollbar thumb. `DrawThumb @ 1c33:????` renders a thumb at
+	// (240, 45..146) proportional to scroll position. We draw a
+	// 1-px outlined block to indicate the same range.
+	if (count > (uint)kVisible) {
+		const int trackY0 = 45;
+		const int trackH  = 146 - 45;
+		const int thumbH  = MAX<int>(8, (trackH * kVisible) / (int)count);
+		const int travel  = trackH - thumbH;
+		const int pos = (int)v.topRow * travel /
+						MAX<int>(1, (int)count - kVisible);
+		const Common::Rect thumb(240, trackY0 + pos,
+								  250, trackY0 + pos + thumbH);
+		scratch.fillRect(thumb, 0x8);
+		scratch.frameRect(thumb, 0xF);
+	}
+
 	g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
 							   0, 0, 320, 200);
 	g_system->updateScreen();
@@ -1413,52 +1497,94 @@ void EEMEngine::doCaseSelection() {
 	}
 
 	// "Choose A Mystery" sub-screen: pick a specific case from the
-	// 55-mystery roster. `_CaseSelection @ 1c33:0a87` only shows
-	// mysteries in the player's current chain stage:
-	//   stage 1 (Junior) ??? 1..24    (start = `iVar1 = 1`     @ 1c33:0aff)
-	//   stage 2 (Senior) ??? 25..48   (start = `iVar1 = 0x19`)
-	//   stage 3 (Master) ??? 49..54   (start = `iVar1 = 0x31`)
-	// The original passes `&DAT_2d5d_3f9b + iVar1` as the grey-mask
-	// pointer so DoChoose lights up only the tier-relevant entries.
-	// We clamp `sel` to the tier range and pre-seed it to the first
-	// unsolved case in that range.
+	// chain-stage's roster. Mirrors `_DoChooseMystery @ 1a35:02b7` +
+	// `_CaseSelection @ 1c33:0a87`:
+	//   stage 1 (Junior, BOOK1.NME) ??? mysteries  1..24
+	//   stage 2 (Senior, BOOK2.NME) ??? mysteries 25..48
+	//   stage 3 (Master, BOOK3.NME) ??? mysteries 49..54
+	// `_DoChooseMystery` opens BOOK<stage>.NME and reads up to 25
+	// CRLF-terminated lines into a 25-entry FAR-pointer array passed
+	// to `_CaseSelection`. The grey mask `_Greys = &mysteriesSolved +
+	// stageLo` (1c33:0b22) makes already-solved entries unselectable.
 	uint stageLo = 1, stageHi = 0x18;
+	uint book = 1;
 	switch (_chainStage) {
-	case 2: stageLo = 0x19; stageHi = 0x30; break;
-	case 3: stageLo = 0x31; stageHi = 0x36; break;
+	case 2: stageLo = 0x19; stageHi = 0x30; book = 2; break;
+	case 3: stageLo = 0x31; stageHi = 0x36; book = 3; break;
 	default: break;  // stage 1 (or fallback)
 	}
 	if (stageHi > kMaxMystery)
 		stageHi = kMaxMystery;
-	uint sel = stageLo;
-	for (uint i = stageLo; i <= stageHi; i++) {
-		if (i < sizeof(_mysteriesSolved) && !_mysteriesSolved[i]) {
-			sel = i;
-			break;
-		}
+
+	const Common::StringArray names = loadBookNames(book);
+	if (names.empty()) {
+		warning("doCaseSelection: BOOK%u.NME failed to load ??? bailing",
+				book);
+		_mystery.clear();
+		return;
 	}
+	const uint listLen = MIN<uint>((uint)names.size(), stageHi - stageLo + 1);
+
+	// Per-row solved flags. `_DoChoose @ 1c33:0521` skips solved entries
+	// when seeding the initial selection (`while *_Greys[select] != 0`)
+	// and again per-click via the same mask check.
+	Common::Array<bool> solvedFlags;
+	solvedFlags.resize(listLen);
+	for (uint i = 0; i < listLen; i++) {
+		const uint mn = stageLo + i;
+		solvedFlags[i] =
+			mn < sizeof(_mysteriesSolved) && _mysteriesSolved[mn] != 0;
+	}
+
+	// Seed the selection at the first unsolved entry ??? same as
+	// `_DoChoose`'s `while (*Greys[select] != 0) select++;` loop at
+	// 1c33:0524.
+	uint selRow = 0;
+	while (selRow < listLen && solvedFlags[selRow])
+		selRow++;
+	if (selRow >= listLen)
+		selRow = 0;  // every case solved ??? let player re-pick
+	uint topRow = 0;
+	const uint kVisible = 12;
+	if (selRow >= kVisible) {
+		topRow = selRow - kVisible / 2;
+		if (topRow + kVisible > listLen)
+			topRow = listLen > kVisible ? listLen - kVisible : 0;
+	}
+
+	auto clampTopRow = [&](uint &t) {
+		if (listLen <= kVisible) {
+			t = 0;
+			return;
+		}
+		const uint maxTop = listLen - kVisible;
+		if (t > maxTop)
+			t = maxTop;
+	};
 
 	CaseSubmenuView sv;
 	sv.vm = this;
 	sv.caseBg = &caseBg;
 	sv.haveCaseBg = haveCaseBg;
-	sv.mysteriesSolved = _mysteriesSolved;
-	sv.mysteriesSolvedSize = sizeof(_mysteriesSolved);
-	sv.sel = sel;
-	sv.maxMystery = kMaxMystery;
+	sv.names = &names;
+	sv.solvedFlags = &solvedFlags;
+	sv.topRow = topRow;
+	sv.selRow = selRow;
+	sv.book = book;
 
 	drawCaseSubmenu(sv);
 	bool confirmed = false;
 	while (!confirmed && !shouldQuit()) {
 		Common::Event ev;
+		bool dirty = false;
 		while (g_system->getEventManager()->pollEvent(ev)) {
 			if (ev.type == Common::EVENT_QUIT ||
 				ev.type == Common::EVENT_RETURN_TO_LAUNCHER)
 				return;
 			if (ev.type == Common::EVENT_LBUTTONDOWN) {
-				// Same `_DoChoose` rectangles as the top-level menu.
 				if (kOkRect.contains(ev.mouse.x, ev.mouse.y)) {
-					confirmed = true;
+					if (selRow < listLen && !solvedFlags[selRow])
+						confirmed = true;
 					break;
 				}
 				if (kExitRect.contains(ev.mouse.x, ev.mouse.y)) {
@@ -1466,35 +1592,32 @@ void EEMEngine::doCaseSelection() {
 					return;
 				}
 				if (kUpArrowRect.contains(ev.mouse.x, ev.mouse.y)) {
-					sel = (sel <= stageLo) ? stageHi : sel - 1;
-					sv.sel = sel;
-					drawCaseSubmenu(sv);
+					if (topRow > 0) { topRow--; dirty = true; }
 					continue;
 				}
 				if (kDnArrowRect.contains(ev.mouse.x, ev.mouse.y)) {
-					sel = (sel >= stageHi) ? stageLo : sel + 1;
-					sv.sel = sel;
-					drawCaseSubmenu(sv);
+					topRow++;
+					clampTopRow(topRow);
+					dirty = true;
 					continue;
 				}
 				if (kListRect.contains(ev.mouse.x, ev.mouse.y)) {
-					// Pick the row under the cursor.
+					// Pick the row under the cursor ??? mirrors
+					// 1c33:0635 `i = (MouseY - DAT_29be_0d02) / 10;`.
 					const int kLineH = 10;
-					const int kVisible = 12;
-					int top = (int)sel - kVisible / 2;
-					if (top < 0)
-						top = 0;
-					if (top + kVisible > (int)kMaxMystery + 1)
-						top = (int)kMaxMystery + 1 - kVisible;
-					const int row = (ev.mouse.y - kListRect.top) / kLineH;
-					const int idx = top + row;
-					if (idx >= 0 && idx <= (int)kMaxMystery) {
-						sel = (uint)idx;
-						sv.sel = sel;
-						drawCaseSubmenu(sv);
-					}
+					const int row = (ev.mouse.y - 35) / kLineH;
+					if (row < 0 || row >= (int)kVisible)
+						continue;
+					const uint idx = topRow + (uint)row;
+					if (idx >= listLen)
+						continue;
+					if (solvedFlags[idx])
+						continue;  // greyed entries ignore clicks
+					selRow = idx;
+					dirty = true;
 					continue;
 				}
+				continue;
 			}
 			if (ev.type != Common::EVENT_KEYDOWN)
 				continue;
@@ -1503,56 +1626,80 @@ void EEMEngine::doCaseSelection() {
 				_mystery.clear();
 				return;
 			}
-			if (k == Common::KEYCODE_RETURN) {
-				confirmed = true;
+			if (k == Common::KEYCODE_RETURN ||
+				k == Common::KEYCODE_KP_ENTER) {
+				if (selRow < listLen && !solvedFlags[selRow])
+					confirmed = true;
 				break;
 			}
-			if (k >= Common::KEYCODE_0 && k <= Common::KEYCODE_9) {
-				sel = (uint)(k - Common::KEYCODE_0);
-				sv.sel = sel;
-				drawCaseSubmenu(sv);
-				continue;
-			}
 			if (k == Common::KEYCODE_DOWN || k == Common::KEYCODE_TAB) {
-				sel = (sel >= stageHi) ? stageLo : sel + 1;
-				sv.sel = sel;
-				drawCaseSubmenu(sv);
+				if (selRow + 1 < listLen) {
+					selRow++;
+					if (selRow >= topRow + kVisible) {
+						topRow = selRow - kVisible + 1;
+						clampTopRow(topRow);
+					}
+					dirty = true;
+				}
 				continue;
 			}
 			if (k == Common::KEYCODE_UP) {
-				sel = (sel <= stageLo) ? stageHi : sel - 1;
-				sv.sel = sel;
-				drawCaseSubmenu(sv);
+				if (selRow > 0) {
+					selRow--;
+					if (selRow < topRow)
+						topRow = selRow;
+					dirty = true;
+				}
 				continue;
 			}
 			if (k == Common::KEYCODE_PAGEDOWN) {
-				sel = (sel + 10 > stageHi) ? stageHi : sel + 10;
-				sv.sel = sel;
-				drawCaseSubmenu(sv);
+				selRow = MIN<uint>(selRow + kVisible, listLen - 1);
+				if (selRow >= topRow + kVisible) {
+					topRow = selRow - kVisible + 1;
+					clampTopRow(topRow);
+				}
+				dirty = true;
 				continue;
 			}
 			if (k == Common::KEYCODE_PAGEUP) {
-				sel = (sel < stageLo + 10) ? stageLo : sel - 10;
-				sv.sel = sel;
-				drawCaseSubmenu(sv);
+				selRow = (selRow >= kVisible) ? selRow - kVisible : 0;
+				if (selRow < topRow)
+					topRow = selRow;
+				dirty = true;
 				continue;
 			}
-			if (k == Common::KEYCODE_HOME) { sel = stageLo; sv.sel = sel; drawCaseSubmenu(sv); continue; }
-			if (k == Common::KEYCODE_END)  { sel = stageHi; sv.sel = sel; drawCaseSubmenu(sv); continue; }
+			if (k == Common::KEYCODE_HOME) {
+				selRow = 0;
+				topRow = 0;
+				dirty = true;
+				continue;
+			}
+			if (k == Common::KEYCODE_END) {
+				selRow = listLen - 1;
+				topRow = listLen > kVisible ? listLen - kVisible : 0;
+				dirty = true;
+				continue;
+			}
+		}
+		if (dirty) {
+			sv.topRow = topRow;
+			sv.selRow = selRow;
+			drawCaseSubmenu(sv);
 		}
 		g_system->updateScreen();
 		g_system->delayMillis(15);
 	}
 
-	if (!_mystery.load(sel, &_rng)) {
-		warning("doCaseSelection: failed to load mystery %u", sel);
+	const uint mn = stageLo + selRow;
+	if (!_mystery.load(mn, &_rng)) {
+		warning("doCaseSelection: failed to load mystery %u", mn);
 		_mystery.clear();
 		return;
 	}
 	if (_audio)
-		_audio->initMysterySounds(sel);
+		_audio->initMysterySounds(mn);
 	debugC(1, kDebugMystery, "Mystery %u loaded; %u sites, %u suspects",
-		   sel, _mystery.numSites(), _mystery.numSuspects());
+		   mn, _mystery.numSites(), _mystery.numSuspects());
 }
 
 void EEMEngine::doNotebook() {


Commit: c3f24c4280f65bc095c473e6363785e09e9076f9
    https://github.com/scummvm/scummvm/commit/c3f24c4280f65bc095c473e6363785e09e9076f9
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:20:49+02:00

Commit Message:
EEM: unlock different scrapbooks until the end

Changed paths:
    engines/eem/ui.cpp


diff --git a/engines/eem/ui.cpp b/engines/eem/ui.cpp
index 1737c1d60ac..1622c1cf8cb 100644
--- a/engines/eem/ui.cpp
+++ b/engines/eem/ui.cpp
@@ -1283,25 +1283,44 @@ void EEMEngine::doCaseSelection() {
 		"         See ScrapBook 2",
 		"         See ScrapBook 3"
 	};
-	// ScrapBook entries are gated by chain stage exactly as the
-	// original `_ActionScreen @ 1c33:195b` does at 1c33:19f3-19f7:
-	//   stage 1 + nothing solved ??? ScrapBook 1/2/3 all greyed
-	//   stage 1 + ???1 solved      ??? ScrapBook 1 enabled, 2/3 greyed
-	//   stage 2                  ??? ScrapBook 1 enabled, 2 enabled, 3 greyed
-	//   stage 3                  ??? all three enabled
-	// (`_3f9b[i] != 0` over the tier's mystery range is the per-tier
-	// gate; we approximate "any in tier" by checking _chainStage and
-	// any solved flag.)
+	// Menu entry gating per `_ActionScreen @ 1c33:195b` ??? the asm at
+	// 1c33:19d1-1a70 sets greys[] based on chain stage AND per-tier
+	// solve count:
+	//   stage 1 ??? grey ScrapBook 2/3; grey ScrapBook 1 if no tier-1 solves
+	//   stage 2 ??? grey Practice + ScrapBook 3; grey ScrapBook 2 if no tier-2 solves
+	//   stage 3 ??? grey Practice; grey ScrapBook 3 if no tier-3 solves
+	//   stage 4 ??? grey Choose + Practice (post-completion read-only state)
+	// In other words: each tier's ScrapBook unlocks as soon as you've
+	// solved your first case in that tier. Practice Mystery is only
+	// available at stage 1. Choose A Mystery is greyed once every case
+	// in every tier is solved (stage 4).
 	bool anySolved1 = false;
 	for (uint i = 1; i <= 0x18 && i < sizeof(_mysteriesSolved); i++)
 		if (_mysteriesSolved[i]) { anySolved1 = true; break; }
-	const bool scrap1On = anySolved1 || _chainStage >= 2;
-	const bool scrap2On = _chainStage >= 2;
-	const bool scrap3On = _chainStage >= 3;
+	bool anySolved2 = false;
+	for (uint i = 0x19; i <= 0x30 && i < sizeof(_mysteriesSolved); i++)
+		if (_mysteriesSolved[i]) { anySolved2 = true; break; }
+	bool anySolved3 = false;
+	for (uint i = 0x31; i <= 0x36 && i < sizeof(_mysteriesSolved); i++)
+		if (_mysteriesSolved[i]) { anySolved3 = true; break; }
+
+	const bool chooseOn   = _chainStage < 4;
+	const bool practiceOn = _chainStage <= 1;
+	const bool scrap1On =
+		_chainStage >= 2 || (_chainStage == 1 && anySolved1);
+	const bool scrap2On =
+		_chainStage >= 3 || (_chainStage == 2 && anySolved2);
+	const bool scrap3On =
+		_chainStage >= 4 || (_chainStage == 3 && anySolved3);
 	const bool kPickEnabled[kNumPicks] = {
-		true, true, scrap1On, scrap2On, scrap3On
+		chooseOn, practiceOn, scrap1On, scrap2On, scrap3On
 	};
-	uint pick = kPickChoose;
+	// Seed selection on the first enabled entry ??? at stage 4 the
+	// `Choose A Mystery` default is greyed, so we land on ScrapBook 1.
+	uint pick = 0;
+	for (uint i = 0; i < kNumPicks; i++) {
+		if (kPickEnabled[i]) { pick = i; break; }
+	}
 
 	const char *kSeparator = "----------------------------------";
 
@@ -1383,7 +1402,11 @@ void EEMEngine::doCaseSelection() {
 			if (ev.type == Common::EVENT_LBUTTONDOWN) {
 				// OK / EXIT / HELP buttons (rectangles from `_DoChoose`).
 				if (kOkRect.contains(ev.mouse.x, ev.mouse.y)) {
-					confirmed = true;
+					// Greyed entries can't be confirmed (mirrors
+					// `_DoChoose @ 1c33:0635` ??? clicks on a `_Greys[i]
+					// != 0` row are ignored before `select` is set).
+					if (kPickEnabled[pick])
+						confirmed = true;
 					break;
 				}
 				if (kExitRect.contains(ev.mouse.x, ev.mouse.y)) {
@@ -1420,8 +1443,10 @@ void EEMEngine::doCaseSelection() {
 				confirmed = true;
 				break;
 			}
-			if (k == Common::KEYCODE_RETURN) {
-				confirmed = true;
+			if (k == Common::KEYCODE_RETURN ||
+				k == Common::KEYCODE_KP_ENTER) {
+				if (kPickEnabled[pick])
+					confirmed = true;
 				break;
 			}
 			if (k == Common::KEYCODE_UP || k == Common::KEYCODE_LEFT) {
@@ -3941,7 +3966,13 @@ void EEMEngine::doAccuse() {
 				if (i >= sizeof(_mysteriesSolved) || _mysteriesSolved[i] == 0)
 					allSolved = false;
 			}
-			if (allSolved && _chainStage < 3) {
+			// `_DisplayCorrect @ 1df2:0852` increments unconditionally
+			// when every case in the current tier is solved ??? including
+			// past stage 3 (so a stage-4 endgame state exists in the
+			// `.PLR` save format). `_ActionScreen @ 1c33:19d1` gates
+			// the menu on `if (3 < _chainStage)` to grey
+			// Choose-A-Mystery and Practice once everything's solved.
+			if (allSolved && _chainStage < 4) {
 				_chainStage++;
 				debugC(1, kDebugMystery,
 					   "chainStage advanced to %u after solving mystery %u",


Commit: ab8cd8380c3799ffec83050126e667d367d65994
    https://github.com/scummvm/scummvm/commit/ab8cd8380c3799ffec83050126e667d367d65994
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:20:49+02:00

Commit Message:
EEM: fixed animation issue

Changed paths:
    engines/eem/clues.cpp


diff --git a/engines/eem/clues.cpp b/engines/eem/clues.cpp
index 69a805fffd8..cd9362494e9 100644
--- a/engines/eem/clues.cpp
+++ b/engines/eem/clues.cpp
@@ -434,12 +434,18 @@ void EEMEngine::doInitClues() {
 					blitMaskedToScreen(book[frame % book.size()], 0, 99);
 				if (haveNancy)
 					blitMaskedToScreen(nancy[frame % nancy.size()], 0x68, 0x8b);
-				// Anchor: original blits at `(sx - frame.width,
-				// sy - frame.rowoff)`. `frame.rowoff` is the y-anchor
-				// in our PicData. We use width/height directly since
-				// loadAnimation places anchor at (0, 0).
-				const int dstX = (int)0xcd - (int)fr.surface.w;
-				const int dstY = (int)seqY - (int)fr.rowoff;
+				// Anchor: `_PlayInSequence @ 172b:2d35-2d50` does
+				//   dstX = sx - cell[+0x8]     ; miscflags (signed)
+				//   dstY = sy - cell[+0x6]     ; rowoff   (signed)
+				// Ghidra's C decompile mis-labels `cell[+0x8]` as
+				// `width`; the actual asm is `SUB AX, ES:[BX+0x8]`,
+				// which is the per-frame X anchor offset stored in our
+				// `Picture::miscflags` field. Using `fr.surface.w`
+				// shifted every frame by the cell width and made the
+				// briefing partner appear duplicated next to the BG-
+				// baked figure (mystery 1 office briefing).
+				const int dstX = (int)0xcd - (int)(int16)fr.miscflags;
+				const int dstY = (int)seqY - (int)(int16)fr.rowoff;
 				blitMaskedToScreen(fr, dstX, dstY);
 				g_system->updateScreen();
 				const uint32 wakeup = g_system->getMillis() + 100;


Commit: 62063295f02fce3abad575c1b7ae25c1518322e8
    https://github.com/scummvm/scummvm/commit/62063295f02fce3abad575c1b7ae25c1518322e8
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:20:50+02:00

Commit Message:
EEM: unseen hotspot effect

Changed paths:
    engines/eem/site.cpp


diff --git a/engines/eem/site.cpp b/engines/eem/site.cpp
index 5869b827d53..4ebb6389f0d 100644
--- a/engines/eem/site.cpp
+++ b/engines/eem/site.cpp
@@ -541,6 +541,29 @@ void SiteScreen::enter(uint siteNum) {
 	const uint16 sitepic = sd ? READ_LE_UINT16(sd) : 0;
 	_vm->setSitePaletteForSite(sitepic);
 
+	// SITEPALS ships with palette indices 0xF9..0xFE all set to a
+	// uniform yellow (`3F 3E 00` = R=63 G=62 B=0 for every site palette
+	// in the file), so the original `_DrawRect`'s cycling colour
+	// pattern + `_ColorCycle(0xF9, 0xFE)` produced a uniformly-coloured
+	// outline with no visible movement ??? the "marching ants" pattern
+	// was a placeholder that never lit up. Override those entries with
+	// a 6-step yellow ramp here so the existing per-tick rotation
+	// (`applyColorCycles ??? cyclePaletteRange(0xF9, 0xFE)`) creates a
+	// pulsing glow on unsearched hotspots. Done after `setSitePalette`
+	// so the per-site palette load doesn't clobber the ramp.
+	{
+		// 6-step yellow glow: dark ??? bright ??? dim ??? ...
+		static const byte kAntsGlow[6 * 3] = {
+			0x40, 0x40, 0x00, // F9 ??? dim
+			0x80, 0x80, 0x00, // FA
+			0xC0, 0xC0, 0x00, // FB
+			0xFF, 0xFF, 0x40, // FC ??? peak
+			0xC0, 0xC0, 0x00, // FD
+			0x80, 0x80, 0x00, // FE
+		};
+		g_system->getPaletteManager()->setPalette(kAntsGlow, 0xF9, 6);
+	}
+
 	renderBackground(siteNum);
 
 	// `_DoSiteLoop @ 168d:03f4` plays `_EnterSiteAnim` whenever
@@ -1271,16 +1294,24 @@ void SiteScreen::renderHotspots(uint siteNum) {
 
 	// Mirrors `_DrawSearchButtons @ 2404:0a8f`:
 	//   for each hotspot:
-	//     if `_Sawit(theSite, loc)` (= _SaveBuffer[hotspot[+0xa]] != 0)
-	//       _DrawRect(rect)        // outline in cycling colors 0xF9..0xFE
-	//     else
-	//       _DrawSolidRect(rect)   // outline in solid white 0xFF
-	// `_DrawRect`'s cycling colors produce a "marching ants" effect that
-	// makes already-searched hotspots visually distinct without hiding
-	// them. We approximate the cycling by rotating the start color via
-	// the global tick.
+	//     if `_Sawit(theSite, loc) == 0` (NOT seen yet):
+	//       `_DrawRect(rect)`       ??? outline in cycling colors
+	//                                 0xF9..0xFE; `_ColorCycle(0xF9,
+	//                                 0xFE)` rotates them every tick ???
+	//                                 "marching ants" glow that draws
+	//                                 the player's eye to unsearched
+	//                                 spots.
+	//     else (seen):
+	//       `_DrawSolidRect(rect)` ??? outline in solid colour 0xFF.
+	// (Verified at the actual asm `2404:0af6 OR AX,AX; 2404:0af8 JZ` ???
+	// the C-level decompile mis-reordered the if/else branches.)
+	//
+	// We don't need per-pixel colour cycling here because palette
+	// `0xF9..0xFE` is already rotated by `applyColorCycles` each tick;
+	// drawing the outline with any single colour in that range will
+	// pulse on its own. We pick a phased start so adjacent hotspots
+	// don't all glow in lock-step.
 	const uint32 tickMs = g_system->getMillis();
-	const byte cyclePhase = (byte)((tickMs / 80) & 0x07);  // 0..7
 
 	for (uint i = 0; i < count; i++) {
 		const byte *r = spots + i * 14;
@@ -1293,39 +1324,44 @@ void SiteScreen::renderHotspots(uint siteNum) {
 								MIN<int>(screen->h, y2));
 		const bool seen = (i < Mystery::kHotSpotsCap)
 						   && _mystery->_hotSpotsSeen[i];
-		if (!seen) {
-			// `_DrawSolidRect` ??? solid white outline (color 0xFF).
+		if (seen) {
+			// `_DrawSolidRect` (172b:0506) ??? outline in palette
+			// index 0xFF (a fixed, non-cycling colour) so already-
+			// found hotspots visually retreat into the BG.
 			screen->frameRect(rect, 0xFF);
 		} else {
-			// `_DrawRect` ??? cycling colors 0xF9..0xFE on each pixel of
-			// the outline. We approximate per-pixel cycling with a
-			// per-rect phase shift so the rects look animated. Start
-			// color is rotated via the global clock.
-			const byte palette[6] = { 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE };
-			byte color = palette[cyclePhase % 6];
+			// `_DrawRect` (172b:03e2) ??? outline in palette indices
+			// 0xF9..0xFE which `_ColorCycle(0xF9, 0xFE)` rotates
+			// every tick. Walk all four edges incrementing the
+			// colour per pixel, exactly like the original.
+			byte color = (byte)(0xF9 + ((i + (tickMs / 80)) & 0x07) % 6);
+			auto bumpColor = [&]() {
+				const byte next = (byte)(color + 1);
+				color = (next > 0xFE) ? (byte)0xF9 : next;
+			};
 			// Top edge
 			for (int x = rect.left; x < rect.right; x++) {
 				if (x >= 0 && x < screen->w && rect.top >= 0 && rect.top < screen->h)
 					*(byte *)screen->getBasePtr(x, rect.top) = color;
-				color = palette[(color - 0xF9 + 1) % 6];
+				bumpColor();
 			}
 			// Right edge
 			for (int y = rect.top; y < rect.bottom; y++) {
 				if (rect.right - 1 >= 0 && rect.right - 1 < screen->w && y >= 0 && y < screen->h)
 					*(byte *)screen->getBasePtr(rect.right - 1, y) = color;
-				color = palette[(color - 0xF9 + 1) % 6];
+				bumpColor();
 			}
 			// Bottom edge
 			for (int x = rect.right - 1; x >= rect.left; x--) {
 				if (x >= 0 && x < screen->w && rect.bottom - 1 >= 0 && rect.bottom - 1 < screen->h)
 					*(byte *)screen->getBasePtr(x, rect.bottom - 1) = color;
-				color = palette[(color - 0xF9 + 1) % 6];
+				bumpColor();
 			}
 			// Left edge
 			for (int y = rect.bottom - 1; y >= rect.top; y--) {
 				if (rect.left >= 0 && rect.left < screen->w && y >= 0 && y < screen->h)
 					*(byte *)screen->getBasePtr(rect.left, y) = color;
-				color = palette[(color - 0xF9 + 1) % 6];
+				bumpColor();
 			}
 		}
 	}


Commit: 8fa2ec9202bfa34f4d52fb99bfca3aa87e795ae0
    https://github.com/scummvm/scummvm/commit/8fa2ec9202bfa34f4d52fb99bfca3aa87e795ae0
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:20:50+02:00

Commit Message:
EEM: make sure sound effect played are correct (or don't play any)

Changed paths:
    engines/eem/clues.cpp


diff --git a/engines/eem/clues.cpp b/engines/eem/clues.cpp
index cd9362494e9..3b460988ce2 100644
--- a/engines/eem/clues.cpp
+++ b/engines/eem/clues.cpp
@@ -773,16 +773,28 @@ void EEMEngine::displayClue(const byte *clueBlock) {
 
 		// `_DisplayClue` @ 2404:0833-085a ??? after the balloon is drawn,
 		// spool the per-clue voice. Each ClueEntry stores two 1-based
-		// sound indices: `+0x18` for partner=Jenny and `+0x1a` for
-		// partner=Jake (verified against 2404:0823-0834). Index 0 / -1
-		// = no audio. The original blocks until the line ends; we run
-		// async (the wait happens implicitly while the player reads).
+		// sound indices: `+0x18` (Jenny voice) and `+0x1a` (Jake voice).
+		//
+		// Critical gate (verified at 2404:0833):
+		//   if (clue[+0x18] != 0 && voiceOn && voiceAvail) {
+		//       iVar6 = clue[+0x18];           // Jenny default
+		//       if (Partner == 0) iVar6 = clue[+0x1a];  // Jake override
+		//       _SpoolSound(iVar6 - 1);
+		//   }
+		// The condition gates on the JENNY slot regardless of partner.
+		// Some hotspot ClueBlocks define `+0x1a` (Jake voice) but leave
+		// `+0x18` at 0 ??? for those, the original engine plays nothing
+		// and the entry is text-only. Our previous code gated on the
+		// partner-selected slot and ended up firing unrelated voices
+		// (e.g., a "no audio" entry triggering Jake's spoolSound).
 		if (_audio) {
 			const uint16 voiceJenny = READ_LE_UINT16(c + 0x18);
-			const uint16 voiceJake  = READ_LE_UINT16(c + 0x1a);
-			const uint16 voice = (_partner == 0) ? voiceJake : voiceJenny;
-			if (voice != 0 && voice != 0xFFFF)
-				_audio->spoolSound((uint)(voice - 1));
+			if (voiceJenny != 0 && voiceJenny != 0xFFFF) {
+				const uint16 voiceJake = READ_LE_UINT16(c + 0x1a);
+				const uint16 voice = (_partner == 0) ? voiceJake : voiceJenny;
+				if (voice != 0 && voice != 0xFFFF)
+					_audio->spoolSound((uint)(voice - 1));
+			}
 		}
 
 		// Wait for click/key to advance ??? only if we drew something.


Commit: 15962d464e03a707fda1ccd69f5fca471567028a
    https://github.com/scummvm/scummvm/commit/15962d464e03a707fda1ccd69f5fca471567028a
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:20:50+02:00

Commit Message:
EEM: removed debug keys

Changed paths:
    engines/eem/site.cpp
    engines/eem/ui.cpp


diff --git a/engines/eem/site.cpp b/engines/eem/site.cpp
index 4ebb6389f0d..f7a605baf4e 100644
--- a/engines/eem/site.cpp
+++ b/engines/eem/site.cpp
@@ -728,104 +728,20 @@ void SiteScreen::run() {
 			}
 
 			case Common::EVENT_KEYDOWN:
-				switch (event.kbd.keycode) {
-				case Common::KEYCODE_ESCAPE:
+				// `_DoSiteLoop @ 168d:07e1` only dispatches on the
+				// 6-entry table at `168d:09d5` (TAB / ENTER / arrow
+				// keys for hotspot cursor cycling) plus ESC handled
+				// separately at 168d:07a9. We don't implement the
+				// hotspot cursor cycling ??? clicks are the primary
+				// interaction ??? so the only keyboard binding kept
+				// here is ESC (matches `_ESCHit` ??? "Are you sure?"
+				// ??? MAP).
+				if (event.kbd.keycode == Common::KEYCODE_ESCAPE) {
 					if (_vm->areYouSure()) {
-						// Mirrors `_DoSiteLoop @ 168d:07b7` ESC path:
-						// `_NextScreen = 1` (back to MAP) after the
-						// areYouSure confirm. Without explicitly
-						// writing it here, the run() loop would see
-						// _nextScreen unchanged and treat it as a
-						// quit-engine signal ??? abandoning the case.
-						// Going to MAP keeps the case alive so the
-						// player can continue from a different site.
 						_vm->setNextScreen(kScreenMap);
 						return;
 					}
 					enter(cur);
-					break;
-				case Common::KEYCODE_m:
-					_vm->doBigMap();
-					// Either way the map covered the site ??? re-render.
-					if (_mystery->_siteNumber < _mystery->numSites())
-						cur = _mystery->_siteNumber;
-					enter(cur);
-					break;
-				case Common::KEYCODE_n:
-					_vm->doNotebook();
-					enter(cur);
-					break;
-				case Common::KEYCODE_g:
-					_vm->doGallery();
-					enter(cur);
-					break;
-				case Common::KEYCODE_a:
-					_vm->doAccuse();
-					exitRequested = true;
-					break;
-				case Common::KEYCODE_h:
-					_vm->doHelp();
-					enter(cur);
-					break;
-				case Common::KEYCODE_v:
-					_showHotspots = !_showHotspots;
-					enter(cur);
-					break;
-				case Common::KEYCODE_r:
-					// Restart the mystery from scratch (mirrors `_ReloadMystery`).
-					if (_mystery->load(_mystery->number())) {
-						if (_vm->_audio)
-							_vm->_audio->initMysterySounds(_mystery->number());
-						cur = 0;
-						enter(cur);
-					}
-					break;
-				case Common::KEYCODE_QUESTION:
-				case Common::KEYCODE_F1: {
-					if (_vm->getFont().isLoaded()) {
-						Graphics::ManagedSurface help(320, 200,
-							Graphics::PixelFormat::createFormatCLUT8());
-						help.clear();
-						const EEMFont &fnt = _vm->getFont();
-						int y = 8;
-						const char *lines[] = {
-							"EAGLE EYE MYSTERIES ??? keys",
-							"",
-							"  click   search a hotspot",
-							"  V       toggle hotspot outlines",
-							"  M       map (travel between sites)",
-							"  N       notebook (mark evidence with 1..9)",
-							"  G       gallery (suspect portraits)",
-							"  H       hint from the case host",
-							"  A       accuse a suspect",
-							"  R       restart current mystery",
-							"  Tab     next site (cycle)",
-							"  F5      save / load (ScummVM dialog)",
-							"  ? / F1  this help",
-							"  Esc     quit (with confirm)",
-							"",
-							"Notebook: select evidence with 1..9.",
-							"Selected-points > 99 wins the case."
-						};
-						for (uint i = 0; i < sizeof(lines)/sizeof(lines[0]); i++) {
-							fnt.drawString(&help, lines[i], 8, y, 320, 0xF);
-							y += fnt.getFontHeight() + 1;
-						}
-						g_system->copyRectToScreen(help.getPixels(),
-							help.pitch, 0, 0, 320, 200);
-						g_system->updateScreen();
-						_vm->waitForInput(60000);
-						enter(cur);
-					}
-					break;
-				}
-				case Common::KEYCODE_TAB:
-					_mystery->_lastSite = cur;
-					cur = (cur + 1) % _mystery->numSites();
-					enter(cur);
-					break;
-				default:
-					break;
 				}
 				break;
 
@@ -1251,34 +1167,6 @@ void SiteScreen::renderBackground(uint siteNum) {
 }
 
 void SiteScreen::renderHotspots(uint siteNum) {
-	// HUD overlay: site number, found clues, selected points. Drawn at
-	// the BOTTOM of the screen so the scene's top row stays visible ???
-	// 320x200 mode 13h has a small bottom strip that the original engine
-	// uses for tool buttons; we repurpose it for the HUD.
-	if (_vm->getFont().isLoaded()) {
-		Graphics::Surface *screen = g_system->lockScreen();
-		if (screen) {
-			uint cluesFound = 0;
-			for (uint i = 0; i < Mystery::kCluesFoundCap; i++)
-				if (_mystery->_cluesFound[i])
-					cluesFound++;
-			Common::String hud = Common::String::format(
-				"Site %u  Clues %u  Pts %d   M N G H A R V Tab ?",
-				siteNum, cluesFound, _mystery->selectedPoints());
-			const int hudY = 192;
-			screen->fillRect(Common::Rect(0, hudY, 320, 200), 0);
-			Graphics::ManagedSurface mgr(320, 9,
-				Graphics::PixelFormat::createFormatCLUT8());
-			mgr.clear();
-			_vm->getFont().drawString(&mgr, hud, 4, 0, 320, 0x0F);
-			for (int row = 0; row < 8; row++) {
-				memcpy((byte *)screen->getBasePtr(0, hudY + row),
-					   (const byte *)mgr.getBasePtr(0, row), 320);
-			}
-			g_system->unlockScreen();
-		}
-	}
-
 	// Hotspot outlines (`_DrawSearchButtons`): toggle via V.
 	if (!_showHotspots)
 		return;
diff --git a/engines/eem/ui.cpp b/engines/eem/ui.cpp
index 1622c1cf8cb..e9db217e7be 100644
--- a/engines/eem/ui.cpp
+++ b/engines/eem/ui.cpp
@@ -1817,9 +1817,6 @@ void EEMEngine::doNotebook() {
 						   ev.kbd.keycode == Common::KEYCODE_TAB) {
 					page++;
 					dirty = true;
-				} else if (ev.kbd.keycode == Common::KEYCODE_h) {
-					doHelp();
-					dirty = true;
 				}
 			}
 			if (ev.type == Common::EVENT_LBUTTONDOWN) {


Commit: 7a9ad68ef6ea6bf7c9ca8772f293802bc0305c42
    https://github.com/scummvm/scummvm/commit/7a9ad68ef6ea6bf7c9ca8772f293802bc0305c42
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:20:51+02:00

Commit Message:
EEM: use blit API

Changed paths:
    engines/eem/graphics.cpp
    engines/eem/site.cpp
    engines/eem/ui.cpp


diff --git a/engines/eem/graphics.cpp b/engines/eem/graphics.cpp
index ed8955c555b..33a3536c454 100644
--- a/engines/eem/graphics.cpp
+++ b/engines/eem/graphics.cpp
@@ -181,9 +181,7 @@ void EEMEngine::doHelp() {
 	{
 		Graphics::Surface *cur = g_system->lockScreen();
 		if (cur) {
-			for (int row = 0; row < 200; row++)
-				memcpy((byte *)ms.getBasePtr(0, row),
-					   (const byte *)cur->getBasePtr(0, row), 320);
+			ms.simpleBlitFrom(*cur);
 			g_system->unlockScreen();
 		}
 	}
@@ -270,9 +268,7 @@ void EEMEngine::doInterfaceHelp(uint num) {
 	{
 		Graphics::Surface *cur = g_system->lockScreen();
 		if (cur) {
-			for (int row = 0; row < 200; row++)
-				memcpy((byte *)bg.getBasePtr(0, row),
-					   (const byte *)cur->getBasePtr(0, row), 320);
+			bg.simpleBlitFrom(*cur);
 			g_system->unlockScreen();
 		}
 	}
@@ -351,10 +347,7 @@ void EEMEngine::setPartnerEraseBg(const Graphics::ManagedSurface *bg) {
 	if (bg && bg->w == 320 && bg->h == 200) {
 		_partnerEraseBg.create(320, 200,
 			Graphics::PixelFormat::createFormatCLUT8());
-		for (int row = 0; row < 200; row++) {
-			memcpy((byte *)_partnerEraseBg.getBasePtr(0, row),
-				   (const byte *)bg->getBasePtr(0, row), 320);
-		}
+		_partnerEraseBg.simpleBlitFrom(*bg);
 	} else {
 		_partnerEraseBg.free();
 	}
diff --git a/engines/eem/site.cpp b/engines/eem/site.cpp
index f7a605baf4e..35adeb52f90 100644
--- a/engines/eem/site.cpp
+++ b/engines/eem/site.cpp
@@ -41,22 +41,7 @@ namespace EEM {
 // to the screen each tick.
 void blitFrame(Graphics::ManagedSurface &dst, const Picture &p,
 			   int x, int y, byte transp) {
-	const int w = p.surface.w;
-	const int h = p.surface.h;
-	for (int row = 0; row < h; row++) {
-		const int dstY = y + row;
-		if (dstY < 0 || dstY >= 200)
-			continue;
-		const byte *src = (const byte *)p.surface.getBasePtr(0, row);
-		byte *out = (byte *)dst.getBasePtr(0, dstY);
-		for (int col = 0; col < w; col++) {
-			const int dstX = x + col;
-			if (dstX < 0 || dstX >= 320)
-				continue;
-			if (src[col] != transp)
-				out[dstX] = src[col];
-		}
-	}
+	dst.transBlitFrom(p.surface, Common::Point(x, y), (uint32)transp);
 }
 
 // Mask-aware blit from a Picture into a `Graphics::Surface` (the
@@ -809,10 +794,7 @@ void SiteScreen::enterSiteAnim() {
 		return;
 	Graphics::ManagedSurface bg(320, 200,
 		Graphics::PixelFormat::createFormatCLUT8());
-	for (int row = 0; row < 200; row++) {
-		memcpy((byte *)bg.getBasePtr(0, row),
-			   (const byte *)screen->getBasePtr(0, row), 320);
-	}
+	bg.simpleBlitFrom(*screen);
 	g_system->unlockScreen();
 
 	// Phase 1 ??? skateboard scroll. `_GetAnimation(6 | 0xe)`.
@@ -833,10 +815,7 @@ void SiteScreen::enterSiteAnim() {
 		while (x + spriteW > 0 && !_vm->shouldQuit()) {
 			Graphics::ManagedSurface scratch(320, 200,
 				Graphics::PixelFormat::createFormatCLUT8());
-			for (int row = 0; row < 200; row++) {
-				memcpy((byte *)scratch.getBasePtr(0, row),
-					   (const byte *)bg.getBasePtr(0, row), 320);
-			}
+			scratch.simpleBlitFrom(bg);
 			blitFrame(scratch, skate[frameIdx], x, y, transp);
 			g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
 									   0, 0, 320, 200);
@@ -886,10 +865,7 @@ void SiteScreen::enterSiteAnim() {
 
 			Graphics::ManagedSurface scratch(320, 200,
 				Graphics::PixelFormat::createFormatCLUT8());
-			for (int row = 0; row < 200; row++) {
-				memcpy((byte *)scratch.getBasePtr(0, row),
-					   (const byte *)bg.getBasePtr(0, row), 320);
-			}
+			scratch.simpleBlitFrom(bg);
 			blitFrame(scratch, fr, destX, destY, transp);
 			g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
 									   0, 0, 320, 200);
@@ -1043,10 +1019,7 @@ void SiteScreen::captureBgSnapshot() {
 		_snapshotSite = -1;
 		return;
 	}
-	for (int row = 0; row < 200; row++) {
-		memcpy((byte *)_bgSnapshot.getBasePtr(0, row),
-			   (const byte *)screen->getBasePtr(0, row), 320);
-	}
+	_bgSnapshot.simpleBlitFrom(*screen);
 	g_system->unlockScreen();
 }
 
@@ -1405,18 +1378,12 @@ void EEMEngine::playKdAnim(uint16 num) {
 	Graphics::ManagedSurface bg(320, 200,
 		Graphics::PixelFormat::createFormatCLUT8());
 	if (_partnerEraseBg.w == 320 && _partnerEraseBg.h == 200) {
-		for (int row = 0; row < 200; row++) {
-			memcpy((byte *)bg.getBasePtr(0, row),
-				   (const byte *)_partnerEraseBg.getBasePtr(0, row), 320);
-		}
+		bg.simpleBlitFrom(_partnerEraseBg);
 	} else {
 		Graphics::Surface *screen = g_system->lockScreen();
 		if (!screen)
 			return;
-		for (int row = 0; row < 200; row++) {
-			memcpy((byte *)bg.getBasePtr(0, row),
-				   (const byte *)screen->getBasePtr(0, row), 320);
-		}
+		bg.simpleBlitFrom(*screen);
 		g_system->unlockScreen();
 	}
 
@@ -1430,10 +1397,7 @@ void EEMEngine::playKdAnim(uint16 num) {
 		// Restore BG, then masked-blit the next frame.
 		Graphics::ManagedSurface scratch(320, 200,
 			Graphics::PixelFormat::createFormatCLUT8());
-		for (int row = 0; row < 200; row++) {
-			memcpy((byte *)scratch.getBasePtr(0, row),
-				   (const byte *)bg.getBasePtr(0, row), 320);
-		}
+		scratch.simpleBlitFrom(bg);
 		// Anchor-aware: kdAnim cells (0x03/0x04/0x0c/0x0d ...) have
 		// non-zero per-frame `miscflags`/`rowoff` (anim 0x03 has
 		// rowoff up to 9, anim 0x04 has miscflags = -2). Without
diff --git a/engines/eem/ui.cpp b/engines/eem/ui.cpp
index e9db217e7be..86dbe5af528 100644
--- a/engines/eem/ui.cpp
+++ b/engines/eem/ui.cpp
@@ -162,13 +162,8 @@ void drawCaseSubmenu(const CaseSubmenuView &v) {
 	Graphics::ManagedSurface scratch(320, 200,
 		Graphics::PixelFormat::createFormatCLUT8());
 	scratch.clear();
-	if (v.haveCaseBg) {
-		const int w = MIN<int>(v.caseBg->surface.w, 320);
-		const int h = MIN<int>(v.caseBg->surface.h, 200);
-		for (int row = 0; row < h; row++)
-			memcpy((byte *)scratch.getBasePtr(0, row),
-				   (const byte *)v.caseBg->surface.getBasePtr(0, row), w);
-	}
+	if (v.haveCaseBg)
+		scratch.simpleBlitFrom(v.caseBg->surface);
 	if (!v.vm->getFont().isLoaded() || !v.names)
 		return;
 
@@ -242,14 +237,8 @@ void drawCaseSelectionFrame(const CaseSelectionView &v) {
 	Graphics::ManagedSurface scratch(320, 200,
 		Graphics::PixelFormat::createFormatCLUT8());
 	scratch.clear();
-	if (v.haveCaseBg) {
-		const int w = MIN<int>(v.caseBg->surface.w, 320);
-		const int h = MIN<int>(v.caseBg->surface.h, 200);
-		for (int row = 0; row < h; row++) {
-			memcpy((byte *)scratch.getBasePtr(0, row),
-				   (const byte *)v.caseBg->surface.getBasePtr(0, row), w);
-		}
-	}
+	if (v.haveCaseBg)
+		scratch.simpleBlitFrom(v.caseBg->surface);
 
 	// KD greeter frame ??? masked-blit current animation cell at
 	// (0x112, 0x50). 100 ms tick matches `_CheckFrameRate`. The
@@ -382,13 +371,8 @@ void EEMEngine::doProfilePicker() {
 			Graphics::PixelFormat::createFormatCLUT8());
 		scratch.clear();
 		Picture bg;
-		if (_picsArchive.getPicture(0x104, bg)) {
-			const int w = MIN<int>(bg.surface.w, 320);
-			const int h = MIN<int>(bg.surface.h, 200);
-			for (int row = 0; row < h; row++)
-				memcpy((byte *)scratch.getBasePtr(0, row),
-					   (const byte *)bg.surface.getBasePtr(0, row), w);
-		}
+		if (_picsArchive.getPicture(0x104, bg))
+			scratch.simpleBlitFrom(bg.surface);
 		for (uint i = 0; i < entries.size(); i++) {
 			const byte color = ((int)i == sel) ? 0xF : 0x8;
 			_font.drawString(&scratch, entries[i].label,
@@ -490,13 +474,8 @@ void EEMEngine::doNewPlayer() {
 	Graphics::ManagedSurface scratch(320, 200,
 		Graphics::PixelFormat::createFormatCLUT8());
 	scratch.clear();
-	if (haveBG) {
-		const int w = MIN<int>(bg.surface.w, 320);
-		const int h = MIN<int>(bg.surface.h, 200);
-		for (int row = 0; row < h; row++)
-			memcpy((byte *)scratch.getBasePtr(0, row),
-				   (const byte *)bg.surface.getBasePtr(0, row), w);
-	}
+	if (haveBG)
+		scratch.simpleBlitFrom(bg.surface);
 	_font.drawString(&scratch, "Please type your name:", 80, 40, 240, 0xF);
 	_font.drawString(&scratch, name + "_", 80, 80, 240, 0xF);
 	g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
@@ -556,13 +535,8 @@ void EEMEngine::doNewPlayer() {
 			// Re-render with the updated `name`. Same body as the
 			// initial render above ??? only `name + "_"` changes.
 			scratch.clear();
-			if (haveBG) {
-				const int w = MIN<int>(bg.surface.w, 320);
-				const int h = MIN<int>(bg.surface.h, 200);
-				for (int row = 0; row < h; row++)
-					memcpy((byte *)scratch.getBasePtr(0, row),
-						   (const byte *)bg.surface.getBasePtr(0, row), w);
-			}
+			if (haveBG)
+				scratch.simpleBlitFrom(bg.surface);
 			_font.drawString(&scratch, "Please type your name:",
 							 80, 40, 240, 0xF);
 			_font.drawString(&scratch, name + "_", 80, 80, 240, 0xF);
@@ -678,13 +652,8 @@ int EEMEngine::doShowEnding(uint num, bool firstPage) {
 			Graphics::ManagedSurface scratch(320, 200,
 				Graphics::PixelFormat::createFormatCLUT8());
 			scratch.clear();
-			if (_picsArchive.getPicture(picNum, bg)) {
-				const int w = MIN<int>(bg.surface.w, 320);
-				const int h = MIN<int>(bg.surface.h, 200);
-				for (int row = 0; row < h; row++)
-					memcpy((byte *)scratch.getBasePtr(0, row),
-						   (const byte *)bg.surface.getBasePtr(0, row), w);
-			}
+			if (_picsArchive.getPicture(picNum, bg))
+				scratch.simpleBlitFrom(bg.surface);
 
 			// Story text. The bytes are a null-terminated string with
 			// `_ParseString` placeholders (0x80 = player name, 0x82
@@ -909,13 +878,8 @@ void EEMEngine::doSetup() {
 			Graphics::PixelFormat::createFormatCLUT8());
 		scratch.clear();
 		Picture bg;
-		if (_picsArchive.getPicture(0x40, bg)) {
-			const int w = MIN<int>(bg.surface.w, 320);
-			const int h = MIN<int>(bg.surface.h, 200);
-			for (int row = 0; row < h; row++)
-				memcpy((byte *)scratch.getBasePtr(0, row),
-					   (const byte *)bg.surface.getBasePtr(0, row), w);
-		}
+		if (_picsArchive.getPicture(0x40, bg))
+			scratch.simpleBlitFrom(bg.surface);
 
 		const byte kKey    = 0xFE;
 		const byte kBright = 0x15;
@@ -1922,13 +1886,8 @@ void EEMEngine::drawNotebookFrame(int &page) {
 
 	// PIC 0x3f frame.
 	Picture frame;
-	if (_picsArchive.getPicture(0x3f, frame)) {
-		const int w = MIN<int>(frame.surface.w, 320);
-		const int h = MIN<int>(frame.surface.h, 200);
-		for (int row = 0; row < h; row++)
-			memcpy((byte *)scratch.getBasePtr(0, row),
-				   (const byte *)frame.surface.getBasePtr(0, row), w);
-	}
+	if (_picsArchive.getPicture(0x3f, frame))
+		scratch.simpleBlitFrom(frame.surface);
 
 	// Partner sprite at (5, 80). Anim 1 for Jake, 0xb (11) for Jenny
 	// for CELLS, but the original `_DoNotebook @ 161e:0500` always
@@ -2805,13 +2764,8 @@ void EEMEngine::drawBigMapOverview(uint32 elapsedMs) {
 	scratch.clear();
 
 	Picture frame;
-	if (_picsArchive.getPicture(0x42, frame)) {
-		const int w = MIN<int>(frame.surface.w, 320);
-		const int h = MIN<int>(frame.surface.h, 200);
-		for (int row = 0; row < h; row++)
-			memcpy((byte *)scratch.getBasePtr(0, row),
-				   (const byte *)frame.surface.getBasePtr(0, row), w);
-	}
+	if (_picsArchive.getPicture(0x42, frame))
+		scratch.simpleBlitFrom(frame.surface);
 
 	// Marker PICs from `_main @ 1a35:0f59`. Three globals are filled
 	// once at boot via `_GetPicture` (1-based IDs):
@@ -2916,13 +2870,8 @@ void EEMEngine::drawBigMapDetail(int scrollX, int scrollY,
 	scratch.clear();
 
 	Picture frame;
-	if (_picsArchive.getPicture(0x43, frame)) {
-		const int w = MIN<int>(frame.surface.w, 320);
-		const int h = MIN<int>(frame.surface.h, 200);
-		for (int row = 0; row < h; row++)
-			memcpy((byte *)scratch.getBasePtr(0, row),
-				   (const byte *)frame.surface.getBasePtr(0, row), w);
-	}
+	if (_picsArchive.getPicture(0x43, frame))
+		scratch.simpleBlitFrom(frame.surface);
 
 	const int copyW = MIN<int>(mapW - scrollX, kMapWinW);
 	const int copyH = MIN<int>(mapH - scrollY, kMapWinH);


Commit: 0ccaaeb7c40435f712a55d22b502c6e3c52fae1e
    https://github.com/scummvm/scummvm/commit/0ccaaeb7c40435f712a55d22b502c6e3c52fae1e
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:20:51+02:00

Commit Message:
EEM: initial support for floppy version

Changed paths:
    engines/eem/audio.cpp
    engines/eem/clues.cpp
    engines/eem/detection.cpp
    engines/eem/eem.cpp
    engines/eem/eem.h
    engines/eem/music.cpp
    engines/eem/music.h
    engines/eem/mystery.cpp
    engines/eem/mystery.h
    engines/eem/resource.cpp
    engines/eem/ui.cpp


diff --git a/engines/eem/audio.cpp b/engines/eem/audio.cpp
index f4719af0bb1..4c2e2a4bb15 100644
--- a/engines/eem/audio.cpp
+++ b/engines/eem/audio.cpp
@@ -205,9 +205,17 @@ void AudioPlayer::spoolSound(uint num) {
 			   "AudioPlayer: voice disabled, skipping spoolSound(%u)", num);
 		return;
 	}
-	if (_currentMystery < 0 || num >= _sdxIndex.size()) {
-		warning("AudioPlayer: spoolSound(%u) ??? invalid index (%d, %u)",
-				num, _currentMystery, (uint)_sdxIndex.size());
+	if (_currentMystery < 0) {
+		// No SDB/SDX bundle is loaded ??? floppy install (no `M*.SDB`)
+		// or pre-mystery state. Silently no-op; per-voice VOC playback
+		// for floppy lives elsewhere (TODO).
+		debugC(2, kDebugSound,
+			   "AudioPlayer: spoolSound(%u) skipped (no mystery sounds)", num);
+		return;
+	}
+	if (num >= _sdxIndex.size()) {
+		warning("AudioPlayer: spoolSound(%u) ??? index out of range (%u)",
+				num, (uint)_sdxIndex.size());
 		return;
 	}
 	const SoundEntry &entry = _sdxIndex[num];
diff --git a/engines/eem/clues.cpp b/engines/eem/clues.cpp
index 3b460988ce2..1c893ae553b 100644
--- a/engines/eem/clues.cpp
+++ b/engines/eem/clues.cpp
@@ -236,7 +236,21 @@ void EEMEngine::doChoosePartner() {
 	// (`jen.voc` for Jenny, `jake.voc` for Jake; strings at 29be:0af1 /
 	// 29be:0af9) and block on `_WaitForVoiceDone`.
 	if (_audio) {
-		_audio->playVoc(Common::Path(_partner == 0 ? "JAKE.VOC" : "JEN.VOC"));
+		// CD: standalone clips `JAKE.VOC` / `JEN.VOC`. Floppy uses
+		// per-partner-and-event voice tables at `2608:0F0E` (Jake) /
+		// `2608:0F76` (Jenny), each 25 entries ?? FAR ptr to a VOC
+		// filename. After a partner pick, `FUN_19bb_0858 @ 19bb:0858`
+		// calls `FUN_1f4e_0305(0x14)` which loads voice slot 20 from
+		// the table for the chosen partner:
+		//   Jake  slot 20 ??? `2608:116B = "m-0113sl.voc"`
+		//   Jenny slot 20 ??? `2608:12AE = "f-0140sl.voc"`
+		Common::String voc;
+		if (isFloppy()) {
+			voc = (_partner == 0) ? "M-0113SL.VOC" : "F-0140SL.VOC";
+		} else {
+			voc = (_partner == 0) ? "JAKE.VOC" : "JEN.VOC";
+		}
+		_audio->playVoc(Common::Path(voc));
 		_audio->waitForVoiceDone();
 	}
 }
@@ -266,11 +280,28 @@ void EEMEngine::doInitClues() {
 	if (!ib)
 		return;
 
-	const uint16 startSite = READ_LE_UINT16(ib + 2);
-	if (startSite < Mystery::kVisitedSiteCap)
-		_mystery._onSites[startSite] = 1;
-	_mystery._siteNumber = startSite;
-	_mystery._lastSite = startSite;
+	// CD InitBlock starts with `u16 caseType; u16 startSite; <clue block>`.
+	// Floppy InitBlock starts with `u8 caseType; u8 nSubjects; subjects[];
+	// u8 nDialog; dialog_records[]` ??? no embedded `startSite`. Verified in
+	// `FUN_19bb_042f` (floppy briefing) where `cVar1 = *(buffer +
+	// initOffset)` reads caseType as a single byte and the dialog loop
+	// uses `local_e = byte[initOffset + 1 + nSubjects + 1]`.
+	const bool floppy = isFloppy();
+	const uint16 caseType = floppy ? (uint16)ib[0] : READ_LE_UINT16(ib);
+
+	if (!floppy) {
+		const uint16 startSite = READ_LE_UINT16(ib + 2);
+		if (startSite < Mystery::kVisitedSiteCap)
+			_mystery._onSites[startSite] = 1;
+		_mystery._siteNumber = startSite;
+		_mystery._lastSite = startSite;
+	} else {
+		// Floppy doesn't store a startSite in the InitBlock; default to 0
+		// so the first BigMap entry is reachable.
+		_mystery._onSites[0] = 1;
+		_mystery._siteNumber = 0;
+		_mystery._lastSite = 0;
+	}
 
 	setSitePalette(0x22);
 	Picture bg;
@@ -283,7 +314,6 @@ void EEMEngine::doInitClues() {
 	const bool haveGame  = _aniArchive.loadAnimation(gameAni, game) && !game.empty();
 	const bool haveBook  = _aniArchive.loadAnimation(bookAni, book) && !book.empty();
 
-	const uint16 caseType = READ_LE_UINT16(ib);
 	const bool haveNancy = (caseType == 1)
 						  && _aniArchive.loadAnimation(0x19, nancy)
 						  && !nancy.empty();
@@ -476,12 +506,24 @@ void EEMEngine::doInitClues() {
 	// the gate is `iVar1 == 2 && _VoiceAvailable`. Other case types open
 	// straight into the briefing dialogue without it.
 	if (caseType == 2 && _audio) {
-		_audio->playVoc(Common::Path("PHONE.VOC"));
+		// Floppy ships `PHONESL.VOC` instead of CD's `PHONE.VOC` (the
+		// `_LoadSoundName` call site at 2608:1107-110c hands the
+		// floppy filename to the same loader).
+		_audio->playVoc(Common::Path(isFloppy() ? "PHONESL.VOC" : "PHONE.VOC"));
 		_audio->waitForVoiceDone();
 	}
 
-	// Step 6 ??? case briefing dialogue.
-	displayClue(ib + 4);
+	// Step 6 ??? case briefing dialogue. CD InitBlock has the clue block
+	// at +4 (after `u16 caseType; u16 startSite`); floppy uses an
+	// entirely different format ??? `u8 caseType; u8 nSubjects;
+	// subjects[nSubjects]; u8 nDialog; dialog_records[nDialog]` ???
+	// dispatched via `FUN_22dc_05c8 @ 22dc:05c8` per record (a
+	// different on-screen format from the CD clue blocks we render
+	// here). Skip the briefing display on floppy until the dialog-record
+	// renderer is ported; the briefing animations + composited final
+	// frames remain on screen until the user clicks past the BigMap.
+	if (!floppy)
+		displayClue(ib + 4);
 }
 
 /// Mirror `_ParseString` @ 1b66:07c3 ??? substitute the control bytes that
diff --git a/engines/eem/detection.cpp b/engines/eem/detection.cpp
index 27371f0c29e..402a5a89cef 100644
--- a/engines/eem/detection.cpp
+++ b/engines/eem/detection.cpp
@@ -42,6 +42,16 @@ const ADGameDescription gameDescriptions[] = {
 		ADGF_NO_FLAGS,
 		GUIO1(GUIO_NONE)
 	},
+	{
+		"eem",
+		"Floppy",
+		AD_ENTRY2s("EEM.EXE",   "692a5e6e7f4516d6e40c1f80cbc1b2cc", 109542,
+				   "PICS.DBD",  "26b97e8586f798ea90440e88d3d527cd", 959160),
+		Common::EN_ANY,
+		Common::kPlatformDOS,
+		ADGF_NO_FLAGS,
+		GUIO1(GUIO_NONE)
+	},
 
 	AD_TABLE_END_MARKER
 };
diff --git a/engines/eem/eem.cpp b/engines/eem/eem.cpp
index 8386f08be13..7ecc68da136 100644
--- a/engines/eem/eem.cpp
+++ b/engines/eem/eem.cpp
@@ -96,6 +96,13 @@ EEMEngine::EEMEngine(OSystem *syst, const ADGameDescription *gameDesc)
 	: Engine(syst), _gameDescription(gameDesc), _rng("eem"),
 	  _playerName("Detective"),
 	  _lastScreen(kScreenInvalid), _nextScreen(kScreenTitle), _partner(0) {
+	// `ADGameDescription::extra` is set by the matching entry in
+	// `gameDescriptions[]` ("CD" or "Floppy"). Keep variant detection
+	// purely string-based so a future re-release with a different
+	// `extra` tag falls back to CD-style asset paths.
+	_variant = (gameDesc && gameDesc->extra &&
+				Common::String(gameDesc->extra).contains("Floppy"))
+				 ? kVariantFloppy : kVariantCD;
 }
 
 EEMEngine::~EEMEngine() {
@@ -119,7 +126,7 @@ Common::Error EEMEngine::run() {
 
 	// MIDI music player. Mirrors `_InitMIDI @ 20a2:013a`. Constructed
 	// here (after `initGraphics` so the OSystem's timer/mixer is up).
-	_music = new MusicPlayer();
+	_music = new MusicPlayer(isFloppy());
 
 	// Digital audio (VOC + spool). Mirrors `_InitDrivers @ 1ff1:0368`
 	// which `_AIL_register_driver`s SBDIG.ADV / PASDIG.ADV alongside
@@ -203,50 +210,70 @@ Common::Error EEMEngine::run() {
 	//   - `_StopMIDI()` runs on keypress at the title screen
 	//     (2520:094c).
 	_skipIntro = false;
-	showEAKidsLogo();
-	if (!shouldQuit() && !_skipIntro)
-		showHighScoreLogo();
-	// Storm Software logo: voice + animation. The original at
-	// `_ShowStormLogo @ 2520:0707` calls `_LoadSoundName("thunder.voc")`
-	// (29be:177d) and passes the buffer to `OpenDifferenceAnimation_Sound`
-	// so the thunder roar plays alongside the lightning bolt.
-	if (!shouldQuit() && !_skipIntro) {
-		if (_audio)
-			_audio->playVoc(Common::Path("THUNDER.VOC"));
-		playAnm(Common::Path("BOLT.ANM"));
-		if (_audio)
-			_audio->stopVoice();
-	}
-	// `_InitMysterySounds(0x3c)` at 2520:086a ??? load M60.SDX/SDB so
-	// `_SpoolSound(uVar3 - 1)` between the ANIM01..ANIM20 anims has
-	// data to draw from.
-	if (!shouldQuit() && !_skipIntro && _audio)
-		_audio->initMysterySounds(60);
-	// Theme begins HERE ??? after the three silent logos, before the
-	// character-intro reel.
-	if (!shouldQuit() && !_skipIntro && _music)
-		_music->playFile(Common::Path("THEME.XMI"), /*loop=*/true);
-	for (int i = 1; i <= 20 && !shouldQuit() && !_skipIntro; i++) {
-		Common::String name = Common::String::format("ANIM%02d.A", i);
-		playAnm(Common::Path(name));
-		// `_SpoolSound(uVar3 - 1)` at 2520:08c2 ??? the per-character VO
-		// plays AFTER each anim except the last (`if (uVar3 != 0x14)`
-		// at 2520:08a8). Original blocks until done; we run async and
-		// wait so the next anim doesn't start before the line ends.
-		if (!shouldQuit() && !_skipIntro && i != 20 && _audio) {
-			_audio->spoolSound((uint)(i - 1));
-			_audio->waitForSpoolDone();
+	if (isFloppy()) {
+		// Floppy opening ??? strings verified via Ghidra of `EEM.EXE`
+		// floppy at `2608:1513` ("movie.anm"), `2608:14F2` ("title.anm"),
+		// `2608:151D` ("theme.xmi"). The floppy ships only those three
+		// intro assets (no `BOLT.ANM`, no `ANIM01..20.A` reel, no
+		// `THUNDER.VOC`). Order: `MOVIE.ANM` plays as the intro
+		// cinematic with theme music, then `TITLE.ANM` holds the title
+		// screen until the player clicks.
+		if (!shouldQuit() && !_skipIntro && _music)
+			_music->playFile(Common::Path("THEME.XMI"), /*loop=*/true);
+		if (!shouldQuit() && !_skipIntro)
+			playAnm(Common::Path("MOVIE.ANM"), 120,
+					/*holdLastFrame=*/false);
+		if (!shouldQuit() && !_skipIntro)
+			playAnm(Common::Path("TITLE.ANM"), 120,
+					/*holdLastFrame=*/true);
+	} else {
+		showEAKidsLogo();
+		if (!shouldQuit() && !_skipIntro)
+			showHighScoreLogo();
+		// Storm Software logo: voice + animation. The original at
+		// `_ShowStormLogo @ 2520:0707` calls `_LoadSoundName(
+		// "thunder.voc")` (29be:177d) and passes the buffer to
+		// `OpenDifferenceAnimation_Sound` so the thunder roar plays
+		// alongside the lightning bolt.
+		if (!shouldQuit() && !_skipIntro) {
+			if (_audio)
+				_audio->playVoc(Common::Path("THUNDER.VOC"));
+			playAnm(Common::Path("BOLT.ANM"));
+			if (_audio)
+				_audio->stopVoice();
 		}
+		// `_InitMysterySounds(0x3c)` at 2520:086a ??? load M60.SDX/SDB
+		// so `_SpoolSound(uVar3 - 1)` between the ANIM01..ANIM20 anims
+		// has data to draw from.
+		if (!shouldQuit() && !_skipIntro && _audio)
+			_audio->initMysterySounds(60);
+		// Theme begins HERE ??? after the three silent logos, before
+		// the character-intro reel.
+		if (!shouldQuit() && !_skipIntro && _music)
+			_music->playFile(Common::Path("THEME.XMI"), /*loop=*/true);
+		for (int i = 1; i <= 20 && !shouldQuit() && !_skipIntro; i++) {
+			Common::String name = Common::String::format("ANIM%02d.A", i);
+			playAnm(Common::Path(name));
+			// `_SpoolSound(uVar3 - 1)` at 2520:08c2 ??? per-character
+			// VO after each anim except the last (`if (uVar3 != 0x14)`
+			// at 2520:08a8). Original blocks until done; we run async
+			// and wait so the next anim doesn't start prematurely.
+			if (!shouldQuit() && !_skipIntro && i != 20 && _audio) {
+				_audio->spoolSound((uint)(i - 1));
+				_audio->waitForSpoolDone();
+			}
+		}
+		// `_CleanMysterySounds` at 2520:0903 ??? release M60 before the
+		// title.
+		if (_audio)
+			_audio->cleanMysterySounds();
+		// Restart the theme for TITLE.ANM ??? matches the second
+		// `_MIDIPlayFile("theme.xmi")` call at 2520:0918.
+		if (!shouldQuit() && !_skipIntro && _music)
+			_music->playFile(Common::Path("THEME.XMI"), /*loop=*/true);
+		if (!shouldQuit() && !_skipIntro)
+			playAnm(Common::Path("TITLE.ANM"), 120, /*holdLastFrame=*/true);
 	}
-	// `_CleanMysterySounds` at 2520:0903 ??? release M60 before the title.
-	if (_audio)
-		_audio->cleanMysterySounds();
-	// Restart the theme for TITLE.ANM ??? matches the second
-	// `_MIDIPlayFile("theme.xmi")` call at 2520:0918.
-	if (!shouldQuit() && !_skipIntro && _music)
-		_music->playFile(Common::Path("THEME.XMI"), /*loop=*/true);
-	if (!shouldQuit() && !_skipIntro)
-		playAnm(Common::Path("TITLE.ANM"), 120, /*holdLastFrame=*/true);
 	_skipIntro = false;
 
 	// After the title chain, the original goes Title (B) -> screen 8
@@ -814,8 +841,12 @@ Common::Error EEMEngine::loadGameStream(Common::SeekableReadStream *stream) {
 		}
 		// `_ReadMystery @ 2404:008f` calls `_InitMysterySounds` at the
 		// tail (2404:0298) so the SDB index is in place for clue and
-		// partner-speech spool sounds.
-		if (_audio)
+		// partner-speech spool sounds. Floppy ships individual
+		// `M-XXXX.VOC` files instead of the bundled SDB / SDX archive,
+		// so skip the init there to avoid spamming "missing" warnings;
+		// `spoolSound` then silently no-ops via the `_currentMystery <
+		// 0` guard until the per-voice VOC mapping is wired up.
+		if (_audio && !isFloppy())
 			_audio->initMysterySounds(mysteryNum);
 		_mystery.syncState(s);
 	} else {
diff --git a/engines/eem/eem.h b/engines/eem/eem.h
index 875d62f608c..1e54e852c45 100644
--- a/engines/eem/eem.h
+++ b/engines/eem/eem.h
@@ -101,6 +101,18 @@ enum ScreenId {
 	kScreenAction         = 0x0C
 };
 
+/// Distribution variant. Selected at engine init from the
+/// `ADGameDescription::extra` field set by `gameDescriptions[]` in
+/// `detection.cpp`. Used to gate filename selection (TRAVEL-N.XMI
+/// vs MUS%05u.XMI, FANFARE2.XMI vs MUS00005.XMI, PHONESL.VOC vs
+/// PHONE.VOC), opening-anim flow (MOVIE.ANM vs ANIM01..20.A) and
+/// per-variant sound effects (DING.VOC / NEWSCAN.VOC only ship with
+/// the floppy release).
+enum Variant {
+	kVariantCD     = 0,
+	kVariantFloppy = 1,
+};
+
 class EEMEngine : public Engine {
 public:
 	EEMEngine(OSystem *syst, const ADGameDescription *gameDesc);
@@ -110,6 +122,8 @@ public:
 
 	const char *getGameId() const;
 	Common::Platform getPlatform() const;
+	Variant getVariant() const { return _variant; }
+	bool isFloppy() const { return _variant == kVariantFloppy; }
 
 	bool hasFeature(EngineFeature f) const override;
 	bool canLoadGameStateCurrently(Common::U32String *msg = nullptr) override;
@@ -156,6 +170,7 @@ public:
 	SaveStateList listProfiles() const;
 
 	const ADGameDescription *_gameDescription;
+	Variant _variant = kVariantCD;
 
 	DBDArchive &getPics()    { return _picsArchive; }
 	DBDArchive &getAni()     { return _aniArchive; }
diff --git a/engines/eem/music.cpp b/engines/eem/music.cpp
index a1a40adb60e..80842663a09 100644
--- a/engines/eem/music.cpp
+++ b/engines/eem/music.cpp
@@ -31,7 +31,7 @@
 
 namespace EEM {
 
-MusicPlayer::MusicPlayer() {
+MusicPlayer::MusicPlayer(bool isFloppy) : _isFloppy(isFloppy) {
 	// Mirrors `_InitMIDI @ 20a2:013a` which used `_AIL_register_driver`
 	// to walk the .ADV files (ADLIB.ADV, SBFM.ADV, MT32MPU.ADV, etc.)
 	// and pick a backend. We honour the launcher's "Music driver"
@@ -166,7 +166,31 @@ void MusicPlayer::playFile(const Common::Path &xmiPath, bool loop) {
 }
 
 void MusicPlayer::playMus(uint num, bool loop) {
-	// Format string verified at `29be:1525` ("mus%05d.xmi").
+	// CD format string verified at `29be:1525` ("mus%05d.xmi").
+	// Floppy maps the same numeric slots to its own filenames:
+	//   0..4 ??? travel music. The floppy table at 2608:1399-13cd holds
+	//          5 entries (Travel-6, Travel-4, Travel-7, Travel-1,
+	//          Travel-8) used by `_StartTravelMusic` via
+	//          `siteNumber % 5`.
+	//   5    ??? FANFARE2.XMI (winner). String at 2608:0c64.
+	//   6    ??? no equivalent in floppy install (the loser sting in
+	//          `_DisplayAlibi` is CD-only); skip.
+	if (_isFloppy) {
+		static const char *const kTravelTracks[5] = {
+			"Travel-6.XMI", "Travel-4.XMI", "Travel-7.XMI",
+			"Travel-1.XMI", "Travel-8.XMI",
+		};
+		Common::String name;
+		if (num < 5) {
+			name = kTravelTracks[num];
+		} else if (num == 5) {
+			name = "FANFARE2.XMI";
+		} else {
+			return; // num == 6 (loser sting): not present on floppy
+		}
+		playFile(Common::Path(name), loop);
+		return;
+	}
 	const Common::String name = Common::String::format("MUS%05u.XMI", num);
 	playFile(Common::Path(name), loop);
 }
diff --git a/engines/eem/music.h b/engines/eem/music.h
index f02830cf5cf..50dedeb9eb7 100644
--- a/engines/eem/music.h
+++ b/engines/eem/music.h
@@ -65,7 +65,7 @@ namespace EEM {
  */
 class MusicPlayer : public Audio::MidiPlayer {
 public:
-	MusicPlayer();
+	explicit MusicPlayer(bool isFloppy = false);
 
 	/// Mirrors `_MIDIPlayFile @ 20a2:024c`. Reads the .XMI from the game
 	/// directory and starts playing. `loop=true` mirrors the
@@ -73,7 +73,8 @@ public:
 	void playFile(const Common::Path &xmiPath, bool loop = false);
 
 	/// Mirrors `_MIDIPlay(num) @ 20a2:047d`. Composes the filename
-	/// "MUS%05u.XMI" and plays it. Used by `_StartTravelMusic`,
+	/// "MUS%05u.XMI" (CD) or maps to TRAVEL-N.XMI / FANFARE2.XMI
+	/// (floppy) and plays it. Used by `_StartTravelMusic`,
 	/// `_DisplayCorrect` (winner), `_DisplayAlibi` (loser).
 	void playMus(uint num, bool loop = false);
 
@@ -87,6 +88,7 @@ public:
 
 private:
 	bool _milesAudioMode = false;
+	const bool _isFloppy;
 	Common::Array<byte> _xmiData;
 };
 
diff --git a/engines/eem/mystery.cpp b/engines/eem/mystery.cpp
index 6b6bfbfc56f..c94b7aa9180 100644
--- a/engines/eem/mystery.cpp
+++ b/engines/eem/mystery.cpp
@@ -44,6 +44,10 @@ void Mystery::clear() {
 	_solvedOffset = _hintOffset = 0;
 	_numSites = 0;
 	_numSuspects = _numCONSITEs = _numCOFFSITEs = 0;
+	_isFloppy = false;
+	_floppySuspectsOff = _floppyHintBlockOff = _floppyNoteIndexOff = 0;
+	_floppyGalleryOff = _floppyTextOff = _floppyKDTextOff = 0;
+	_floppySolvedOff = 0;
 	memset(_aChain, 0, sizeof(_aChain));
 	memset(_bChain, 0, sizeof(_bChain));
 	memset(_cChain, 0, sizeof(_cChain));
@@ -86,6 +90,118 @@ bool Mystery::load(uint num, Common::RandomSource *rng) {
 	_data = staging;
 	_number = num;
 
+	// Floppy `M*.BIN` uses a completely different header layout from
+	// the CD release. Verified via Ghidra of `EEM.EXE` floppy:
+	//
+	//   `_ReadMystery_Floppy @ 22dc:0178` parses M0..M54 into pointers
+	//    held in a global table at 28da:3c87+. The header offsets are:
+	//
+	//     header[+0..+1]   ???
+	//     header[+2..+3]   ???
+	//     header[+4..+5]   pointer ??? SUSPECTS section
+	//                      (count byte, then 0xb-byte entries; entry[+4] =
+	//                       pic ID, entry[+10] = recolor flag)
+	//                      (`_FloppySuspectsPtr` @ 28da:3c8b)
+	//     header[+6..+7]   pointer ??? ???       (28da:3c9f)
+	//     header[+8..+9]   pointer ??? NOTES section
+	//                      (7-byte entries indexed by clue ID; used by
+	//                       floppy `_DrawNotes` @ 15e0:01e8)
+	//                      (`DAT_28da_3c9b`)
+	//     header[+0xa..+b] pointer ??? GALLERY-PORTRAITS section
+	//                      (count byte, then variable-length entries
+	//                       `5 + name_len` bytes; entry[+0..+1] = u16
+	//                       picID, entry[+4] = name length)
+	//                      (`_FloppyGalleryPtr` @ 28da:3c87, count =
+	//                       `_FloppyNumSuspects` @ 28da:004b)
+	//     header[+0xc..+d] pointer ??? TEXT block (alibi text base; used in
+	//                       floppy `_DisplayAlibi` @ 1d40:00df)
+	//     header[+0x10..1] pointer ??? KDTextIndex (`_FloppyKDTextIndexPtr`
+	//                       @ 28da:3c93)
+	//     header[+0x12..3] pointer ??? ???       (28da:3c8f)
+	//
+	//   There is NO fixed-offset numSites / numSuspects / numCONSITEs
+	//   field ??? counts are stored as the FIRST byte of each section.
+	//   The CD release refactored this into a flat header at fixed
+	//   offsets; our `Mystery::load` here parses the CD layout.
+	//
+	// Detect the variant from the first u16: CD M0 starts with `0x003e`
+	// (initOffset = 62), floppy M0 starts with `0x2286` (a section
+	// pointer near end-of-file). When the first u16 is too high to be
+	// a CD `_initOffset`, parse as floppy.
+	if (readU16(0) > 0x100) {
+		_isFloppy = true;
+		// Section-pointer header verified via Ghidra of floppy
+		// `_ReadMystery_Floppy @ 22dc:0178`,
+		// `_DoSiteLoop_Floppy @ 1652:03a3`,
+		// and `FUN_1fed_07ed` (BigMap site iteration):
+		//
+		//   header[+4]   ??? SITES section
+		//                  count byte + 0xb-byte entries; entry[+4] =
+		//                  pic ID for BigMap marker, [+6..7] = u16 X,
+		//                  [+8..9] = u16 Y, [+10] = recolor flag.
+		//   header[+6]   ??? SITE INDEX (array of u16 offsets to per-site
+		//                  data structs; site[+0]=picOff,
+		//                  site[+2]=clueBlockOff, site[+8]=speakerInfo)
+		//   header[+8]   ??? NOTES (7-byte entries / clue ID)
+		//   header[+0xa] ??? SUSPECTS / GALLERY portraits
+		//   header[+0xc] ??? TEXT block base
+		//   header[+0x10] ??? KDTextIndex
+		//   header[+0x12] ??? SOLVED CLUE CHAIN
+		_floppySuspectsOff  = readU16(0x04);  // SITES
+		_floppyHintBlockOff = readU16(0x06);  // SITE INDEX
+		_floppyNoteIndexOff = readU16(0x08);
+		_floppyGalleryOff   = readU16(0x0a);  // SUSPECTS
+		_floppyTextOff      = readU16(0x0c);
+		_floppyKDTextOff    = readU16(0x10);
+		_floppySolvedOff    = readU16(0x12);
+
+		// header[+0] (the first u16) holds the InitBlock byte offset on
+		// floppy too ??? verified at `FUN_19bb_042f` where `*DAT_28da_3ca5`
+		// (deref'd as int *) reads the first u16 of the buffer and uses
+		// it as `cVar1 = *(buffer + initOffset)` (caseType byte).
+		_initOffset = readU16(0x00);
+
+		// Counts: first byte of each section. Verified at
+		// `FUN_1fed_07ed` (`uVar3 = *_FloppySuspectsPtr` then iterates)
+		// and `FUN_154e_0045` (`DAT_28da_004b = *DAT_28da_3c87`).
+		const byte *sitesSec = (_floppySuspectsOff < _data.size())
+								? _data.data() + _floppySuspectsOff : nullptr;
+		const byte *susSec   = (_floppyGalleryOff < _data.size())
+								? _data.data() + _floppyGalleryOff : nullptr;
+		_numSites    = sitesSec ? *sitesSec : 0;
+		_numSuspects = susSec   ? *susSec   : 0;
+		_numCONSITEs = 0;
+		_numCOFFSITEs = 0;
+
+		// Point CD-shaped accessor offsets at the floppy equivalents
+		// so existing accessors return the right base for floppy:
+		//   siteIndexEntry() ??? floppy site index (header[+6])
+		//   noteIndex()       ??? floppy notes (header[+8])
+		//   galleryData()     ??? floppy suspects (header[+0xa])
+		//   textAt()          ??? floppy text block (header[+0xc])
+		//   kdTextIndex()     ??? floppy KDTextIndex (header[+0x10])
+		//   solvedClueBlock() ??? floppy solved chain (header[+0x12])
+		// Per-section LAYOUTS still differ from CD, so consumers
+		// walking entries need `isFloppy()` branches.
+		_siteIndexOffset = _floppyHintBlockOff;
+		_noteOffset      = _floppyNoteIndexOff;
+		_galleryOffset   = _floppyGalleryOff;
+		_textOffset      = _floppyTextOff;
+		_kdTextOffset    = _floppyKDTextOff;
+		_solvedOffset    = _floppySolvedOff;
+		_hintOffset      = _floppyHintBlockOff;
+
+		debugC(1, kDebugMystery,
+			   "Mystery::load(%u) floppy: sites=0x%04x siteIdx=0x%04x "
+			   "notes=0x%04x suspects=0x%04x text=0x%04x kd=0x%04x "
+			   "solved=0x%04x  numSites=%u numSuspects=%u",
+			   num, _floppySuspectsOff, _floppyHintBlockOff,
+			   _floppyNoteIndexOff, _floppyGalleryOff, _floppyTextOff,
+			   _floppyKDTextOff, _floppySolvedOff,
+			   _numSites, _numSuspects);
+		return true;
+	}
+
 	// Header is 16-bit-word indexed (matches `int *piVar1 = __Mystery; piVar1[N]`).
 	_initOffset      = readU16(0  * 2);
 	_mapOffset       = readU16(2  * 2);
@@ -102,6 +218,16 @@ bool Mystery::load(uint num, Common::RandomSource *rng) {
 	_numCONSITEs = (uint8)readU16(14 * 2);
 	_numCOFFSITEs = (uint8)readU16(15 * 2);
 
+	// Defensive clamp. The floppy mystery file format uses a different
+	// header layout (verified by comparing M0.BIN: CD has `numSites =
+	// readU16(0x14) = 3`; floppy has `readU16(0x14) = 0x1925`,
+	// obviously not a site count). Without a clamp, downstream loops
+	// over `_onSites` / `_visitedSite` (capacity 20) blow past the
+	// array end. Until the floppy format is fully supported, cap at
+	// the array capacity so the engine fails gracefully.
+	if (_numSites > kVisitedSiteCap)
+		_numSites = kVisitedSiteCap;
+
 	for (uint i = 0; i < kChainLen; i++) {
 		_aChain[i] = readU16((16 + i) * 2);
 		_bChain[i] = readU16((21 + i) * 2);
@@ -218,6 +344,18 @@ const byte *Mystery::kdTextIndex() const {
 const byte *Mystery::mapEntry(uint siteNum) const {
 	if (!isLoaded() || siteNum >= _numSites)
 		return nullptr;
+	if (_isFloppy) {
+		// Floppy SITES section: byte[0] = count, then 11-byte entries.
+		// Verified at `FUN_1fed_07ed` (BigMap site iteration) where
+		// `pcVar2 = _FloppySuspectsPtr` (header[+4]) and the loop reads
+		// `*(int *)(pcVar2 + i*0xb + 7)` (X) and `*(int *)(pcVar2 + i*0xb
+		// + 9)` (Y) ??? the +7/+9 offsets are 1-based because pcVar2[0]
+		// holds the count, so entry stride 11 starts at byte 1.
+		const uint off = _floppySuspectsOff + 1 + siteNum * 11;
+		if (off + 11 > _data.size())
+			return nullptr;
+		return _data.data() + off;
+	}
 	const uint off = _mapOffset + siteNum * 14;
 	if (off + 14 > _data.size())
 		return nullptr;
diff --git a/engines/eem/mystery.h b/engines/eem/mystery.h
index dadaec00a75..4b23fa4ea27 100644
--- a/engines/eem/mystery.h
+++ b/engines/eem/mystery.h
@@ -209,6 +209,20 @@ private:
 	uint16 _bChain[kChainLen] = {};
 	uint16 _cChain[kChainLen] = {};
 
+	// Floppy variant uses a completely different header (see comment
+	// in `Mystery::load`). When `_isFloppy` is true, the CD-shaped
+	// `_initOffset / _siteIndexOffset / etc.` fields are unset and the
+	// floppy section pointers below are populated from the floppy
+	// header offsets verified at `_ReadMystery_Floppy @ 22dc:0178`.
+	bool   _isFloppy = false;
+	uint16 _floppySuspectsOff = 0;   ///< header[+4]  ??? suspects
+	uint16 _floppyHintBlockOff = 0;  ///< header[+6]  ??? hint???clue table
+	uint16 _floppyNoteIndexOff = 0;  ///< header[+8]  ??? notes (7B/clue)
+	uint16 _floppyGalleryOff = 0;    ///< header[+0xa] ??? gallery portraits
+	uint16 _floppyTextOff = 0;       ///< header[+0xc] ??? text block
+	uint16 _floppyKDTextOff = 0;     ///< header[+0x10] ??? KDTextIndex
+	uint16 _floppySolvedOff = 0;     ///< header[+0x12] ??? solved clue chain
+
 	uint16 readU16(uint offset) const;
 };
 
diff --git a/engines/eem/resource.cpp b/engines/eem/resource.cpp
index 91886e837fb..a08ce19f85f 100644
--- a/engines/eem/resource.cpp
+++ b/engines/eem/resource.cpp
@@ -115,7 +115,15 @@ bool readFrame(Common::SeekableReadStream &stream, bool compressed, Picture &out
 
 bool DBDArchive::loadEntry(uint num, Picture &out) {
 	if (num >= _index.size()) {
-		warning("DBDArchive::loadEntry: %u out of range (max %u)", num, _index.size());
+		// Out-of-range picture IDs are non-fatal ??? every caller already
+		// checks the return value (e.g., `haveDone`/`haveCrime` in
+		// `drawBigMapOverview`). The floppy's PICS.DBD ships fewer
+		// entries than the CD (e.g., the BigMap done-marker `0x20D` is
+		// CD-only), so this fires routinely on floppy and shouldn't
+		// be a `warning`.
+		debugC(2, kDebugGfx,
+			   "DBDArchive::loadEntry: %u out of range (max %u)",
+			   num, (uint)_index.size());
 		return false;
 	}
 
diff --git a/engines/eem/ui.cpp b/engines/eem/ui.cpp
index 86dbe5af528..460217ce9d0 100644
--- a/engines/eem/ui.cpp
+++ b/engines/eem/ui.cpp
@@ -1462,7 +1462,7 @@ void EEMEngine::doCaseSelection() {
 		if (!_mystery.load(0, &_rng)) {
 			warning("doCaseSelection: failed to load practice mystery");
 			_mystery.clear();
-		} else if (_audio) {
+		} else if (_audio && !isFloppy()) {
 			_audio->initMysterySounds(0);
 		}
 		return;
@@ -1685,7 +1685,7 @@ void EEMEngine::doCaseSelection() {
 		_mystery.clear();
 		return;
 	}
-	if (_audio)
+	if (_audio && !isFloppy())
 		_audio->initMysterySounds(mn);
 	debugC(1, kDebugMystery, "Mystery %u loaded; %u sites, %u suspects",
 		   mn, _mystery.numSites(), _mystery.numSuspects());
@@ -2699,8 +2699,11 @@ void EEMEngine::doBigMap() {
 						   ev.mouse.y < kMapWinY + kMapWinH) {
 					// Hit-test the per-site button at its actual bbox
 					// (`_StampButtons` records the rect at SmallMap +8/+0xa
-					// with the button PIC's width/height).
-					for (uint i = 0; i < _mystery.numSites(); i++) {
+					// with the button PIC's width/height). Floppy entries
+					// have a different shape so skip the SmallMap hit-test
+					// (we still get clicks via the BigMap overview path).
+					const bool fmap = _mystery.isLoaded() && isFloppy();
+					for (uint i = 0; !fmap && i < _mystery.numSites(); i++) {
 						if (!_mystery._onSites[i] &&
 							i != _mystery._siteNumber)
 							continue;
@@ -2785,9 +2788,20 @@ void EEMEngine::drawBigMapOverview(uint32 elapsedMs) {
 		const byte *entry = _mystery.mapEntry(i);
 		if (!entry)
 			continue;
-		const uint16 mx    = READ_LE_UINT16(entry + 0x4);
-		const uint16 my    = READ_LE_UINT16(entry + 0x6);
-		const uint16 crime = READ_LE_UINT16(entry + 0xc);
+		// CD entries are 14 bytes: X at +4, Y at +6, crime at +12.
+		// Floppy entries are 11 bytes: X at +6, Y at +8, recolor at +10.
+		// Floppy layout verified at `FUN_1fed_07ed` (BigMap iteration):
+		//   `*(int *)(pcVar2 + i*0xb + 7)` (= entry+6, X u16)
+		//   `*(int *)(pcVar2 + i*0xb + 9)` (= entry+8, Y u16)
+		//   `pcVar2[i*0xb + 0xb]` (= entry+10, recolor flag ??? non-zero
+		//   selects the crime-marker PIC over the regular site marker).
+		const bool floppy  = _mystery.isLoaded() && isFloppy();
+		const uint16 mx    = floppy ? READ_LE_UINT16(entry + 0x6)
+									: READ_LE_UINT16(entry + 0x4);
+		const uint16 my    = floppy ? READ_LE_UINT16(entry + 0x8)
+									: READ_LE_UINT16(entry + 0x6);
+		const uint16 crime = floppy ? (uint16)entry[0xa]
+									: READ_LE_UINT16(entry + 0xc);
 		const bool   done_ = (i < Mystery::kVisitedSiteCap)
 							  && _mystery._visitedSite[i];
 
@@ -2885,7 +2899,12 @@ void EEMEngine::drawBigMapDetail(int scrollX, int scrollY,
 	//   button = _GetButton(MapData[+0])
 	//   destX  = MapData[+8]
 	//   destY  = MapData[+0xa]
-	for (uint i = 0; i < _mystery.numSites(); i++) {
+	// Floppy mystery 0 ships no SmallMap detail layer, but mapEntry() still
+	// returns the floppy SITES row which has a different shape (no
+	// per-button PIC ID, X/Y at +6/+8) ??? skip the per-site stamp on floppy
+	// to avoid stamping garbage button IDs from the wrong field offsets.
+	const bool floppyMap = _mystery.isLoaded() && isFloppy();
+	for (uint i = 0; !floppyMap && i < _mystery.numSites(); i++) {
 		if (!_mystery._onSites[i] && i != _mystery._siteNumber)
 			continue;
 		const byte *entry = _mystery.mapEntry(i);


Commit: a55adf77b7cd802486f60a28d8171a6dfd7407aa
    https://github.com/scummvm/scummvm/commit/a55adf77b7cd802486f60a28d8171a6dfd7407aa
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:20:51+02:00

Commit Message:
EEM: implement conversations for the floppy version

Changed paths:
    engines/eem/clues.cpp
    engines/eem/eem.cpp
    engines/eem/eem.h
    engines/eem/mystery.cpp
    engines/eem/site.cpp
    engines/eem/ui.cpp


diff --git a/engines/eem/clues.cpp b/engines/eem/clues.cpp
index 1c893ae553b..e765bff0107 100644
--- a/engines/eem/clues.cpp
+++ b/engines/eem/clues.cpp
@@ -296,9 +296,15 @@ void EEMEngine::doInitClues() {
 		_mystery._siteNumber = startSite;
 		_mystery._lastSite = startSite;
 	} else {
-		// Floppy doesn't store a startSite in the InitBlock; default to 0
-		// so the first BigMap entry is reachable.
-		_mystery._onSites[0] = 1;
+		// Floppy InitBlock has no startSite. The floppy BigMap iterator
+		// `FUN_1fed_07ed` walks every site in the SITES section
+		// unconditionally (no `_OnSites` gate) ??? verified at the floppy
+		// `_DoMapScreen @ 1fed:1060` which stamps every site marker
+		// before the interaction loop. Mark every loaded site as visible
+		// so our `_onSites`-gated overview stamps the same set.
+		const uint sites = _mystery.numSites();
+		for (uint s = 0; s < sites && s < Mystery::kVisitedSiteCap; s++)
+			_mystery._onSites[s] = 1;
 		_mystery._siteNumber = 0;
 		_mystery._lastSite = 0;
 	}
@@ -514,15 +520,14 @@ void EEMEngine::doInitClues() {
 	}
 
 	// Step 6 ??? case briefing dialogue. CD InitBlock has the clue block
-	// at +4 (after `u16 caseType; u16 startSite`); floppy uses an
-	// entirely different format ??? `u8 caseType; u8 nSubjects;
-	// subjects[nSubjects]; u8 nDialog; dialog_records[nDialog]` ???
-	// dispatched via `FUN_22dc_05c8 @ 22dc:05c8` per record (a
-	// different on-screen format from the CD clue blocks we render
-	// here). Skip the briefing display on floppy until the dialog-record
-	// renderer is ported; the briefing animations + composited final
-	// frames remain on screen until the user clicks past the BigMap.
-	if (!floppy)
+	// at +4 (after `u16 caseType; u16 startSite`); floppy uses
+	// `u8 caseType; u8 nSubjects; subjects[nSubjects]; u8 nDialog;
+	// dialog_records[nDialog]` (each record `11 + textCount` bytes),
+	// dispatched via `FUN_22dc_05c8 @ 22dc:05c8`. We render dialog
+	// records ourselves on floppy.
+	if (floppy)
+		displayFloppyBriefing(ib);
+	else
 		displayClue(ib + 4);
 }
 
@@ -891,6 +896,193 @@ void EEMEngine::displayClue(const byte *clueBlock) {
 	}
 }
 
+void EEMEngine::displayFloppyBriefing(const byte *initBlock) {
+	// Floppy briefing ??? mirrors the dialog loop at the tail of
+	// `FUN_19bb_042f @ 19bb:042f`:
+	//   nSubjects = ib[1]; nDialog = ib[2 + nSubjects];
+	//   records   = ib + 3 + nSubjects;
+	// Each record is dispatched through `FUN_22dc_05c8 @ 22dc:05c8`,
+	// which reads:
+	//   u16 picID    @ +0     (character portrait, 0 = skip pic)
+	//   u16 picX     @ +2
+	//   u8  picY     @ +4
+	//   u8  balloon  @ +5     (low 7 bits = balloon idx, +0x80 = mirror)
+	//   u16 ballX    @ +6
+	//   u8  ballY    @ +8
+	//   u8  sound    @ +9     (high bit = play voice, low 7 bits = slot)
+	//   u8  textCount@ +10
+	//   u8  textIdx[]@ +11    (1 byte per ??? low 7 bits = NOTES idx)
+	// Text offsets in NOTES are ABSOLUTE byte offsets into the mystery
+	// buffer (verified by note 0 of M0.BIN at file offset 0xd0 holding
+	// "Hello, ..., I'm ... Eagle!"), so we read text via
+	// `mystery.blobAt(noteEntry[+2..3])` for Jake / `+4..5` for Jenny.
+	if (!initBlock || !isFloppy() || !_font.isLoaded())
+		return;
+
+	const uint8 nSubjects = initBlock[1];
+	const uint8 nDialog   = initBlock[2 + nSubjects];
+	const byte *rec       = initBlock + 3 + nSubjects;
+
+	const byte *notes   = _mystery.noteIndex();
+	const byte *bufBase = _mystery.blobAt(0);
+	if (!notes || !bufBase)
+		return;
+
+	// Snapshot the current screen so each record restores the
+	// briefing background between bubbles.
+	Graphics::ManagedSurface bg(320, 200,
+		Graphics::PixelFormat::createFormatCLUT8());
+	{
+		Graphics::Surface *screen = g_system->lockScreen();
+		if (screen) {
+			for (int row = 0; row < 200; row++) {
+				memcpy((byte *)bg.getBasePtr(0, row),
+					   (const byte *)screen->getBasePtr(0, row), 320);
+			}
+			g_system->unlockScreen();
+		}
+	}
+
+	for (uint i = 0; i < nDialog && !shouldQuit(); i++) {
+		const uint16 picID    = READ_LE_UINT16(rec + 0);
+		const uint16 picX     = READ_LE_UINT16(rec + 2);
+		const uint8  picY     = rec[4];
+		const uint8  balByte  = rec[5];
+		const uint16 ballX    = READ_LE_UINT16(rec + 6);
+		const uint8  ballY    = rec[8];
+		const uint8  textCount= rec[10];
+
+		// Build the full text by concatenating each note's per-partner
+		// text string (parsed for `%s`-style placeholders).
+		Common::String raw;
+		for (uint t = 0; t < textCount; t++) {
+			const uint8  idx     = rec[11 + t] & 0x7f;
+			const uint16 textOff = (_partner == 0)
+				? READ_LE_UINT16(notes + idx * 7 + 2)
+				: READ_LE_UINT16(notes + idx * 7 + 4);
+			const char *line = (const char *)(bufBase + textOff);
+			if (t > 0)
+				raw += ' ';
+			raw += line;
+		}
+		const Common::String text = parseString(raw, _playerName, _partner);
+
+		// Restore briefing BG before drawing this bubble.
+		g_system->copyRectToScreen(bg.getPixels(), bg.pitch, 0, 0, 320, 200);
+
+		// Optional character portrait.
+		if (picID != 0 && picID != 0xFFFF) {
+			Picture pic;
+			if (_picsArchive.getPicture(picID, pic))
+				blitAt(pic, picX, picY);
+		}
+
+		// Compose balloon + text on a scratch surface.
+		Graphics::ManagedSurface scratch(320, 200,
+			Graphics::PixelFormat::createFormatCLUT8());
+		{
+			Graphics::Surface *screen = g_system->lockScreen();
+			if (screen) {
+				for (int row = 0; row < 200; row++) {
+					memcpy((byte *)scratch.getBasePtr(0, row),
+						   (const byte *)screen->getBasePtr(0, row), 320);
+				}
+				g_system->unlockScreen();
+			}
+		}
+
+		Picture balloon;
+		const uint16 balloonId  = balByte & 0x7F;
+		const bool   flipBall   = (balByte & 0x80) != 0;
+		const bool   haveBalloon = balByte != 0xFF &&
+			_balloonArchive.size() > balloonId &&
+			_balloonArchive.loadEntry(balloonId, balloon);
+		uint16 textWidth = 142;
+		uint16 textXIns  = 6;
+		uint16 textYIns  = 4;
+		if (haveBalloon) {
+			const int bw = MIN<int>(balloon.surface.w, 320 - ballX);
+			const int bh = MIN<int>(balloon.surface.h, 200 - ballY);
+			const byte transp = (byte)(balloon.flags >> 8);
+			for (int row = 0; row < bh; row++) {
+				const byte *src =
+					(const byte *)balloon.surface.getBasePtr(0, row);
+				byte *dst =
+					(byte *)scratch.getBasePtr(ballX, ballY + row);
+				for (int col = 0; col < bw; col++) {
+					const int srcCol = flipBall
+						? (balloon.surface.w - 1 - col) : col;
+					const byte px = src[srcCol];
+					if (px != transp)
+						dst[col] = px;
+				}
+			}
+			getBalloonInsets(balloonId, textXIns, textYIns, textWidth);
+		}
+
+		const int textX = ballX + textXIns;
+		const int textY = ballY + textYIns;
+		_font.drawWordWrapped(&scratch, textX, textY,
+			MAX<int>(8, (int)textWidth), text, 0);
+
+		g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
+								   0, 0, 320, 200);
+		g_system->updateScreen();
+
+		// Drain pending events from the previous bubble (or upstream
+		// animation skip) so a single Enter press doesn't burst-advance
+		// through multiple bubbles. Without this, key-repeat or stacked
+		// KEYUP/KEYDOWN events from one keystroke could roll past
+		// several records before the user could even read them.
+		{
+			Common::Event drain;
+			while (g_system->getEventManager()->pollEvent(drain)) {}
+		}
+		// Minimum visible time per bubble ??? guards against accidental
+		// rapid-fire advances from accumulated input.
+		const uint32 minVisibleMs = 250;
+		const uint32 startedAt = g_system->getMillis();
+
+		// Wait for click / key. ESC skips the rest of the briefing.
+		// Inside the loop we ignore input until at least `minVisibleMs`
+		// has elapsed since the bubble was drawn ??? combined with the
+		// pre-loop event drain, this prevents one keystroke (or its
+		// auto-repeat tail) from advancing several bubbles back to back.
+		bool advance = false;
+		bool skipAll = false;
+		while (!advance && !shouldQuit()) {
+			Common::Event ev;
+			while (g_system->getEventManager()->pollEvent(ev)) {
+				if (ev.type == Common::EVENT_QUIT ||
+					ev.type == Common::EVENT_RETURN_TO_LAUNCHER) {
+					advance = true;
+					break;
+				}
+				if (g_system->getMillis() - startedAt < minVisibleMs)
+					continue;
+				if (ev.type == Common::EVENT_KEYDOWN &&
+					ev.kbd.keycode == Common::KEYCODE_ESCAPE) {
+					advance = true;
+					skipAll = true;
+					interruptAudio();
+					break;
+				}
+				if (ev.type == Common::EVENT_LBUTTONDOWN ||
+					ev.type == Common::EVENT_KEYDOWN) {
+					advance = true;
+					break;
+				}
+			}
+			g_system->updateScreen();
+			g_system->delayMillis(10);
+		}
+		if (skipAll)
+			return;
+
+		rec += 11 + textCount;
+	}
+}
+
 bool EEMEngine::areYouSure() {
 	// Mirrors `_AreYouSure` @ 1a35:0a5c. Original loads PIC 0x136 for the
 	// dialog body and PIC 0x1FD/0x1FE for YES/NO. We render a minimal
diff --git a/engines/eem/eem.cpp b/engines/eem/eem.cpp
index 7ecc68da136..23a34e6201f 100644
--- a/engines/eem/eem.cpp
+++ b/engines/eem/eem.cpp
@@ -53,8 +53,10 @@ const uint kNumSitePals = 40;  ///< SITEPALS holds 40 palettes (40 * 768 = 30720
 // Picture / palette IDs from the original code (1-based picture IDs).
 const uint kPicEAKidsLogo      = 0x54;  ///< _ShowEAKids: GetPicture(0x54)
 const uint kPicHighScoreLogo   = 0x20c; ///< _ShowHScoreLogo: GetPicture(0x20c)
+const uint kPicStormLogo       = 0x20b; ///< Floppy storm-logo still: PIC 0x20b
 const uint kPalEAKids          = 0x25;
 const uint kPalHighScore       = 0x27;
+const uint kPalStormLogo       = 0x26;  ///< Floppy `FUN_23d2_0605` palette idx
 
 // Save body version, used by the `Common::Serializer` inside
 // `saveGameStream`/`loadGameStream`. The framework's extended-save
@@ -211,15 +213,35 @@ Common::Error EEMEngine::run() {
 	//     (2520:094c).
 	_skipIntro = false;
 	if (isFloppy()) {
-		// Floppy opening ??? strings verified via Ghidra of `EEM.EXE`
-		// floppy at `2608:1513` ("movie.anm"), `2608:14F2` ("title.anm"),
-		// `2608:151D` ("theme.xmi"). The floppy ships only those three
-		// intro assets (no `BOLT.ANM`, no `ANIM01..20.A` reel, no
-		// `THUNDER.VOC`). Order: `MOVIE.ANM` plays as the intro
-		// cinematic with theme music, then `TITLE.ANM` holds the title
-		// screen until the player clicks.
+		// Floppy opening ??? driven by `FUN_23d2_039c @ 23d2:039c`:
+		//   FUN_23d2_0170()  ??? clear palette
+		//   FUN_23d2_004b()  ??? set up timer
+		//   FUN_23d2_050c()  ??? show PIC 0x54 (EA Kids logo, palette 0x25)
+		//   FUN_23d2_06c6()  ??? show PIC 0x20c (High Score logo, palette 0x27)
+		//   FUN_23d2_0605()  ??? show PIC 0x20b (Storm Software, palette
+		//                      0x26) AND play voice slot 25 (thunder.voc
+		//                      ??? verified via the Jake voice table at
+		//                      `2608:0f0e` slot 25 ??? `2608:11ac` =
+		//                      "thunder.voc").
+		//   _MIDIPlayFile("theme.xmi", loop=1)
+		//   _PlayANM(idx=0) ??? CHAT.ANM (filename table at `2608:14fe`
+		//                     index 0 ??? "chat.anm" at `2608:150a`).
+		//   _PlayANM(idx=1) ??? MOVIE.ANM (table index 1 ??? `2608:1513`).
+		// `TITLE.ANM` is shown later by screen-`0xb` handler `@
+		// 19bb:0ebc` once the intro driver returns. The thunder VOC
+		// alongside the storm logo is the "intro voice" the user heard
+		// missing ??? without it the lightning-bolt logo plays silently.
+		if (!shouldQuit() && !_skipIntro)
+			showEAKidsLogo();
+		if (!shouldQuit() && !_skipIntro)
+			showHighScoreLogo();
+		if (!shouldQuit() && !_skipIntro)
+			showFloppyStormLogo();
 		if (!shouldQuit() && !_skipIntro && _music)
 			_music->playFile(Common::Path("THEME.XMI"), /*loop=*/true);
+		if (!shouldQuit() && !_skipIntro)
+			playAnm(Common::Path("CHAT.ANM"), 120,
+					/*holdLastFrame=*/false);
 		if (!shouldQuit() && !_skipIntro)
 			playAnm(Common::Path("MOVIE.ANM"), 120,
 					/*holdLastFrame=*/false);
@@ -678,6 +700,29 @@ void EEMEngine::showHighScoreLogo() {
 	waitForInput(2500);
 }
 
+void EEMEngine::showFloppyStormLogo() {
+	// Floppy storm-logo splash ??? `FUN_23d2_0605 @ 23d2:0605`:
+	//   GetPicture(0x20b); BlitToVGA;
+	//   if (sound) { LoadVOC(slot 25 = "thunder.voc"); PlayVOC(...); }
+	//   GetPalette(0x26); FadeIn; wait 50 ticks; FadeOut.
+	// CD plays `BOLT.ANM` at this slot with `THUNDER.VOC` overlaid; the
+	// floppy uses a static still + the same VOC. Without the VOC the
+	// lightning logo plays silently ??? the user noticed.
+	Picture pic;
+	if (!_picsArchive.getPicture(kPicStormLogo, pic)) {
+		warning("Storm logo (%u) load failed", kPicStormLogo);
+		return;
+	}
+	blitAt(pic, 0, 0);
+	setSitePalette(kPalStormLogo);
+	g_system->updateScreen();
+	if (_audio)
+		_audio->playVoc(Common::Path("THUNDER.VOC"));
+	waitForInput(2500);
+	if (_audio)
+		_audio->stopVoice();
+}
+
 void EEMEngine::doSiteLoop() {
 	// Mirrors the per-mystery site loop. SiteScreen::run() handles
 	// hotspot clicks plus M (map), N (notebook), G (gallery), A (accuse),
diff --git a/engines/eem/eem.h b/engines/eem/eem.h
index 1e54e852c45..74046faec8d 100644
--- a/engines/eem/eem.h
+++ b/engines/eem/eem.h
@@ -361,6 +361,7 @@ private:
 	// Screen handlers ??? port targets in screens/ later.
 	void showEAKidsLogo();
 	void showHighScoreLogo();
+	void showFloppyStormLogo();
 
 	/// Profile selector ??? mirrors `screen8_handler @ 1c33:1012`.
 	/// Walks `listProfiles()`, draws the list of existing profile
@@ -423,6 +424,21 @@ private:
 	/// minus the live ANI sequence playback.
 	void doInitClues();
 
+	/// Floppy variant of the briefing dialog renderer. Walks the dialog
+	/// record list at the tail of the floppy InitBlock (per
+	/// `FUN_19bb_042f` and `FUN_22dc_05c8 @ 22dc:05c8`) and renders one
+	/// speech balloon per record. Each record is `11 + textCount` bytes:
+	///   +0..1  picID (character portrait, 0 = none)
+	///   +2..3  picX
+	///   +4     picY
+	///   +5     balloonId | (mirror_flag << 7)
+	///   +6..7  balloonX
+	///   +8     balloonY
+	///   +9     soundFlag (high bit) | sound slot (low 7 bits)
+	///   +10    textCount
+	///   +11..  text indices (1 byte each, low 7 bits = NOTES idx)
+	void displayFloppyBriefing(const byte *initBlock);
+
 public:
 	/// Mirrors `_StartTravelMusic @ 20a2:0595`. Picks `MUS%05d.XMI`
 	/// based on `_mystery._siteNumber % 5` and starts it (looping). The
diff --git a/engines/eem/mystery.cpp b/engines/eem/mystery.cpp
index c94b7aa9180..437a8f62499 100644
--- a/engines/eem/mystery.cpp
+++ b/engines/eem/mystery.cpp
@@ -266,8 +266,12 @@ bool Mystery::load(uint num, Common::RandomSource *rng) {
 const byte *Mystery::siteIndexEntry(uint siteNum) const {
 	if (!isLoaded() || siteNum >= _numSites)
 		return nullptr;
-	const uint off = _siteIndexOffset + siteNum * 6;
-	if (off + 6 > _data.size())
+	// Floppy site index uses 2-byte (u16) entries ??? verified at
+	// `_DoSiteLoop_Floppy @ 1652:03d2` reading `*(int *)
+	// ((int)_FloppySiteIndexPtr + siteNum * 2)`. CD uses 6-byte rows.
+	const uint stride = _isFloppy ? 2 : 6;
+	const uint off = _siteIndexOffset + siteNum * stride;
+	if (off + stride > _data.size())
 		return nullptr;
 	return _data.data() + off;
 }
@@ -286,6 +290,14 @@ const byte *Mystery::hotspots(uint siteNum) const {
 	const byte *idx = siteIndexEntry(siteNum);
 	if (!idx)
 		return nullptr;
+	// Floppy site index is only 2 bytes per entry; `idx + 4` would
+	// read into the NEXT entry's offset, returning garbage. The
+	// floppy hotspot list lives inside the site-data sub-blob (see
+	// `_DoSiteLoop_Floppy @ 1652:03a3` walking 5-byte drop entries
+	// from `*site_data + 2`); don't fake a CD-style result here ???
+	// SiteScreen guards floppy paths separately.
+	if (_isFloppy)
+		return nullptr;
 	const uint16 hotspotOff = READ_LE_UINT16(idx + 4);
 	if (hotspotOff >= _data.size())
 		return nullptr;
@@ -293,6 +305,8 @@ const byte *Mystery::hotspots(uint siteNum) const {
 }
 
 uint16 Mystery::hotspotCount(uint siteNum) const {
+	if (_isFloppy)
+		return 0;
 	const byte *site = siteData(siteNum);
 	if (!site || (size_t)(site - _data.data()) + 8 > _data.size())
 		return 0;
diff --git a/engines/eem/site.cpp b/engines/eem/site.cpp
index 35adeb52f90..80610200252 100644
--- a/engines/eem/site.cpp
+++ b/engines/eem/site.cpp
@@ -522,8 +522,25 @@ void SiteScreen::enter(uint siteNum) {
 	// Palette: original `_BuildBackground` calls `GetPalette(sitenum + 1)`
 	// where sitenum is the global SITES.DBD index (= the per-mystery
 	// `sitepic` field), not the per-mystery site index.
+	//
+	// Floppy site_data layout differs (per `_DoSiteLoop_Floppy @
+	// 1652:03f4`): the FIRST u16 is the *offset* to a drops sub-struct
+	// whose byte 0 is the small SITES.DBD picID. Without this branch
+	// we'd dereference the offset directly and call setSitePalette
+	// with a value in the thousands ??? the user reported `index 9275
+	// out of range` for site 2.
 	const byte *sd = _mystery->siteData(siteNum);
-	const uint16 sitepic = sd ? READ_LE_UINT16(sd) : 0;
+	uint16 sitepic = 0;
+	if (sd) {
+		if (_vm->isFloppy()) {
+			const uint16 dropsOff = READ_LE_UINT16(sd);
+			const byte *drops = _mystery->blobAt(dropsOff);
+			if (drops)
+				sitepic = (uint16)drops[0];
+		} else {
+			sitepic = READ_LE_UINT16(sd);
+		}
+	}
 	_vm->setSitePaletteForSite(sitepic);
 
 	// SITEPALS ships with palette indices 0xF9..0xFE all set to a
@@ -980,6 +997,16 @@ void SiteScreen::scanColorCycles(uint siteNum) {
 	_colorCycles.clear();
 	if (!_mystery)
 		return;
+	// Floppy site data has a different layout (per `_DoSiteLoop_Floppy
+	// @ 1652:03a3`: site index is 2-byte u16 entries, site data starts
+	// with an offset to a sub-structure with picID + 5-byte hotspot
+	// entries ??? there's no `[+0xa]` anim count at the same place). The
+	// CD-shaped offsets read garbage and run past the buffer end. Until
+	// the floppy color-cycle layout is reverse-engineered, skip the
+	// scan: the floppy still does palette F9..FE rotation in its own
+	// driver, but our hotspot palette override works without it.
+	if (_vm && _vm->isFloppy())
+		return;
 	const byte *site = _mystery->siteData(siteNum);
 	if (!site)
 		return;
@@ -1044,6 +1071,15 @@ void SiteScreen::renderPartner(uint siteNum, uint32 tickMs) {
 	// `kWaitAnims` lives at file scope above; we cap rendering at
 	// `speaker < 7` since anything past entry 6 is the `_SiteButtons`
 	// rect data that follows the table in the binary.
+	// Floppy site data has a different shape: site_data+8 is a u16
+	// OFFSET to a 10-byte speakerInfo struct (verified at
+	// `_DoSiteLoop_Floppy @ 1652:042b` reading `*(undefined2 *)
+	// (DAT_28da_0172) = anim_id_jake` etc.), not an index into the
+	// `kWaitAnims` table the CD uses. Skip the partner render until
+	// the floppy speakerInfo is wired up ??? without this guard we
+	// dereference garbage entries past the table end.
+	if (_vm && _vm->isFloppy())
+		return;
 	const byte *site = _mystery->siteData(siteNum);
 	if (!site)
 		return;
@@ -1118,8 +1154,25 @@ void SiteScreen::renderBackground(uint siteNum) {
 	// ??? the dbi entry stride is 10 bytes, no -1 adjustment). Our
 	// previous `loadEntry(sitepic - 1)` was off by one, which is why
 	// the tutorial mystery rendered scenes from neighbouring cases.
+	// Floppy site_data stores the picID one indirection deeper:
+	// `*site_data` (u16) ??? drops sub-struct, `drops[0]` (byte) is the
+	// SITES.DBD entry. CD uses the u16 directly. Verified at
+	// `FUN_16e2_12fd @ 16e2:12fd` (called as
+	// `FUN_16e2_12fd(*local_12, 0x42, 0x14)` from
+	// `_DoSiteLoop_Floppy`, where `*local_12` is the byte at
+	// drops_struct+0 via the `(undefined1 *)` cast).
 	const byte *site = _mystery->siteData(siteNum);
-	const uint16 sitepic = site ? READ_LE_UINT16(site) : 0;
+	uint16 sitepic = 0;
+	if (site) {
+		if (_vm->isFloppy()) {
+			const uint16 dropsOff = READ_LE_UINT16(site);
+			const byte *drops = _mystery->blobAt(dropsOff);
+			if (drops)
+				sitepic = (uint16)drops[0];
+		} else {
+			sitepic = READ_LE_UINT16(site);
+		}
+	}
 	Picture scene;
 	bool haveScene = false;
 	if (sitepic < _vm->getSites().size())
diff --git a/engines/eem/ui.cpp b/engines/eem/ui.cpp
index 460217ce9d0..7e7e9de005f 100644
--- a/engines/eem/ui.cpp
+++ b/engines/eem/ui.cpp
@@ -2699,24 +2699,45 @@ void EEMEngine::doBigMap() {
 						   ev.mouse.y < kMapWinY + kMapWinH) {
 					// Hit-test the per-site button at its actual bbox
 					// (`_StampButtons` records the rect at SmallMap +8/+0xa
-					// with the button PIC's width/height). Floppy entries
-					// have a different shape so skip the SmallMap hit-test
-					// (we still get clicks via the BigMap overview path).
+					// with the button PIC's width/height).
 					const bool fmap = _mystery.isLoaded() && isFloppy();
-					for (uint i = 0; !fmap && i < _mystery.numSites(); i++) {
+					for (uint i = 0; i < _mystery.numSites(); i++) {
 						if (!_mystery._onSites[i] &&
 							i != _mystery._siteNumber)
 							continue;
 						const byte *entry = _mystery.mapEntry(i);
 						if (!entry)
 							continue;
-						const uint16 buttonId = READ_LE_UINT16(entry + 0x0);
-						const uint16 mx       = READ_LE_UINT16(entry + 0x8);
-						const uint16 my       = READ_LE_UINT16(entry + 0xa);
+						uint16 mx;
+						uint16 my;
+						uint16 buttonId = 0;
+						if (fmap) {
+							// Floppy detail view: click rect on
+							// BIGMAP.PIC at (+0, +2), per
+							// `FUN_1fed_0c3e`'s write to
+							// DAT_28da_3aee/DAT_28da_3af0.
+							mx = READ_LE_UINT16(entry + 0x0);
+							my = READ_LE_UINT16(entry + 0x2);
+						} else {
+							buttonId = READ_LE_UINT16(entry + 0x0);
+							mx       = READ_LE_UINT16(entry + 0x8);
+							my       = READ_LE_UINT16(entry + 0xa);
+						}
 						Picture button;
 						int bw = 16;
 						int bh = 16;
-						if (_buttonArchive.loadEntry(buttonId, button)) {
+						if (fmap) {
+							// Floppy uses the global site-marker PIC for
+							// every site; the recolor flag at +10 picks
+							// the crime variant. Sized off whichever PIC
+							// is loaded successfully.
+							Picture m;
+							const uint pic = (entry[0xa] != 0) ? 0xc6 : 0xc5;
+							if (_picsArchive.getPicture(pic, m)) {
+								bw = m.surface.w;
+								bh = m.surface.h;
+							}
+						} else if (_buttonArchive.loadEntry(buttonId, button)) {
 							bw = button.surface.w;
 							bh = button.surface.h;
 						}
@@ -2899,24 +2920,50 @@ void EEMEngine::drawBigMapDetail(int scrollX, int scrollY,
 	//   button = _GetButton(MapData[+0])
 	//   destX  = MapData[+8]
 	//   destY  = MapData[+0xa]
-	// Floppy mystery 0 ships no SmallMap detail layer, but mapEntry() still
-	// returns the floppy SITES row which has a different shape (no
-	// per-button PIC ID, X/Y at +6/+8) ??? skip the per-site stamp on floppy
-	// to avoid stamping garbage button IDs from the wrong field offsets.
+	// Floppy SITES rows are 11 bytes ??? no per-button PIC ID. Use the
+	// regular site / crime marker PICs (0xc5 / 0xc6) keyed off the
+	// recolor flag at +10, same logic as the overview stamp.
 	const bool floppyMap = _mystery.isLoaded() && isFloppy();
-	for (uint i = 0; !floppyMap && i < _mystery.numSites(); i++) {
+	Picture floppySiteM;
+	Picture floppyCrimeM;
+	const bool haveFloppySite  = floppyMap && _picsArchive.getPicture(0xc5, floppySiteM);
+	const bool haveFloppyCrime = floppyMap && _picsArchive.getPicture(0xc6, floppyCrimeM);
+	for (uint i = 0; i < _mystery.numSites(); i++) {
 		if (!_mystery._onSites[i] && i != _mystery._siteNumber)
 			continue;
 		const byte *entry = _mystery.mapEntry(i);
 		if (!entry)
 			continue;
-		const uint16 buttonId = READ_LE_UINT16(entry + 0x0);
-		const uint16 mx       = READ_LE_UINT16(entry + 0x8);
-		const uint16 my       = READ_LE_UINT16(entry + 0xa);
-
+		uint16 mx;
+		uint16 my;
 		Picture button;
-		if (!_buttonArchive.loadEntry(buttonId, button))
-			continue;
+		if (floppyMap) {
+			// Floppy SITES rows carry TWO position pairs:
+			//   (+0, +2) = position on BIGMAP.PIC (the zoomed view).
+			//              Used by `FUN_1fed_0c3e @ 1fed:0c3e` for
+			//              suspect-portrait stamping AND as the
+			//              click bbox on the zoomed map.
+			//   (+6, +8) = position on the overview PIC 0x42.
+			//              Used by `FUN_1fed_07ed @ 1fed:07ed`.
+			// The detail view scrolls BIGMAP.PIC, so we need the
+			// (+0, +2) coords here. Recolor flag at +10 still
+			// selects crime vs site marker.
+			mx = READ_LE_UINT16(entry + 0x0);
+			my = READ_LE_UINT16(entry + 0x2);
+			const bool useCrime = entry[0xa] != 0;
+			if (useCrime && haveFloppyCrime)
+				button = floppyCrimeM;
+			else if (haveFloppySite)
+				button = floppySiteM;
+			else
+				continue;
+		} else {
+			const uint16 buttonId = READ_LE_UINT16(entry + 0x0);
+			mx                    = READ_LE_UINT16(entry + 0x8);
+			my                    = READ_LE_UINT16(entry + 0xa);
+			if (!_buttonArchive.loadEntry(buttonId, button))
+				continue;
+		}
 		const int sx = (int)mx - scrollX + kMapWinX;
 		const int sy = (int)my - scrollY + kMapWinY;
 		const byte transp = (byte)(button.flags >> 8);


Commit: 3ba1ac49ac1ddc11fa16766af84344b5dec53a16
    https://github.com/scummvm/scummvm/commit/3ba1ac49ac1ddc11fa16766af84344b5dec53a16
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:20:52+02:00

Commit Message:
EEM: npc conversations for the floppy version

Changed paths:
    engines/eem/clues.cpp
    engines/eem/eem.h
    engines/eem/mystery.cpp
    engines/eem/mystery.h
    engines/eem/site.cpp
    engines/eem/site.h
    engines/eem/ui.cpp


diff --git a/engines/eem/clues.cpp b/engines/eem/clues.cpp
index e765bff0107..8e4cd5798d1 100644
--- a/engines/eem/clues.cpp
+++ b/engines/eem/clues.cpp
@@ -896,13 +896,9 @@ void EEMEngine::displayClue(const byte *clueBlock) {
 	}
 }
 
-void EEMEngine::displayFloppyBriefing(const byte *initBlock) {
-	// Floppy briefing ??? mirrors the dialog loop at the tail of
-	// `FUN_19bb_042f @ 19bb:042f`:
-	//   nSubjects = ib[1]; nDialog = ib[2 + nSubjects];
-	//   records   = ib + 3 + nSubjects;
-	// Each record is dispatched through `FUN_22dc_05c8 @ 22dc:05c8`,
-	// which reads:
+void EEMEngine::displayFloppyDialogRecords(const byte *rec, uint count) {
+	// Render `count` consecutive floppy dialog records starting at
+	// `rec`. Per `FUN_22dc_05c8 @ 22dc:05c8`, each record is:
 	//   u16 picID    @ +0     (character portrait, 0 = skip pic)
 	//   u16 picX     @ +2
 	//   u8  picY     @ +4
@@ -916,13 +912,9 @@ void EEMEngine::displayFloppyBriefing(const byte *initBlock) {
 	// buffer (verified by note 0 of M0.BIN at file offset 0xd0 holding
 	// "Hello, ..., I'm ... Eagle!"), so we read text via
 	// `mystery.blobAt(noteEntry[+2..3])` for Jake / `+4..5` for Jenny.
-	if (!initBlock || !isFloppy() || !_font.isLoaded())
+	if (!rec || !isFloppy() || !_font.isLoaded() || count == 0)
 		return;
 
-	const uint8 nSubjects = initBlock[1];
-	const uint8 nDialog   = initBlock[2 + nSubjects];
-	const byte *rec       = initBlock + 3 + nSubjects;
-
 	const byte *notes   = _mystery.noteIndex();
 	const byte *bufBase = _mystery.blobAt(0);
 	if (!notes || !bufBase)
@@ -943,7 +935,7 @@ void EEMEngine::displayFloppyBriefing(const byte *initBlock) {
 		}
 	}
 
-	for (uint i = 0; i < nDialog && !shouldQuit(); i++) {
+	for (uint i = 0; i < count && !shouldQuit(); i++) {
 		const uint16 picID    = READ_LE_UINT16(rec + 0);
 		const uint16 picX     = READ_LE_UINT16(rec + 2);
 		const uint8  picY     = rec[4];
@@ -953,17 +945,35 @@ void EEMEngine::displayFloppyBriefing(const byte *initBlock) {
 		const uint8  textCount= rec[10];
 
 		// Build the full text by concatenating each note's per-partner
-		// text string (parsed for `%s`-style placeholders).
+		// text string (parsed for `%s`-style placeholders). Bounds-
+		// check every offset against the mystery blob; if any byte
+		// looks out of range we just stop appending ??? corrupt offsets
+		// from a misparsed record would otherwise dereference past the
+		// buffer and SIGBUS inside `strlen`.
+		const uint32 dsz       = _mystery.dataSize();
+		const uint32 notesBase = (uint32)(notes - bufBase);
 		Common::String raw;
 		for (uint t = 0; t < textCount; t++) {
-			const uint8  idx     = rec[11 + t] & 0x7f;
+			const uint8  idx        = rec[11 + t] & 0x7f;
+			const uint32 noteAbs    = notesBase + (uint32)idx * 7;
+			if (noteAbs + 6 > dsz)
+				break;
 			const uint16 textOff = (_partner == 0)
 				? READ_LE_UINT16(notes + idx * 7 + 2)
 				: READ_LE_UINT16(notes + idx * 7 + 4);
+			if (textOff >= dsz)
+				break;
 			const char *line = (const char *)(bufBase + textOff);
+			// Find the NUL terminator within the blob ??? refuse to
+			// strlen past the end.
+			uint32 lineLen = 0;
+			while (textOff + lineLen < dsz && line[lineLen] != 0)
+				lineLen++;
+			if (textOff + lineLen >= dsz)
+				break;
 			if (t > 0)
 				raw += ' ';
-			raw += line;
+			raw += Common::String(line, lineLen);
 		}
 		const Common::String text = parseString(raw, _playerName, _partner);
 
@@ -1083,6 +1093,105 @@ void EEMEngine::displayFloppyBriefing(const byte *initBlock) {
 	}
 }
 
+void EEMEngine::displayFloppyBriefing(const byte *initBlock) {
+	// Floppy briefing ??? `FUN_19bb_042f @ 19bb:042f` walks
+	// `nDialog = ib[2 + nSubjects]` records starting at
+	// `ib + 3 + nSubjects`. Each record is rendered identically to a
+	// hotspot dialog record (same `FUN_22dc_05c8` callee), so we
+	// share `displayFloppyDialogRecords`.
+	if (!initBlock || !isFloppy())
+		return;
+	const uint8 nSubjects = initBlock[1];
+	const uint8 nDialog   = initBlock[2 + nSubjects];
+	const byte *rec       = initBlock + 3 + nSubjects;
+	displayFloppyDialogRecords(rec, nDialog);
+}
+
+void EEMEngine::displayFloppyHotspotDialog(uint siteNum, uint hotIdx) {
+	// Floppy hotspot click ??? mirrors `FUN_22dc_0b80 @ 22dc:0b80` +
+	// `FUN_1652_00e6 @ 1652:00e6` + `FUN_1652_006c @ 1652:006c`.
+	// Each site stores a per-hotspot dialog list at
+	// `site_data[+6..7]`. The list is laid out as:
+	//   for each hotspot in order:
+	//     main record (11 + textCount bytes)
+	//     u8 contFlags  (low 7 bits = continuation count, high bit
+	//                    used by FUN_1652_00e6 to drive the partner
+	//                    pose ??? irrelevant for text rendering)
+	//     contCount ?? { record (11 + textCount bytes) }
+	// We walk past `hotIdx` hotspots, then dispatch the matched main
+	// record + its continuation chain through the same renderer
+	// `displayFloppyBriefing` uses.
+	if (!_mystery.isLoaded() || !isFloppy())
+		return;
+	const byte *site = _mystery.siteData(siteNum);
+	if (!site)
+		return;
+	const uint16 dlgListOff = READ_LE_UINT16(site + 6);
+	const byte *bufBase = _mystery.blobAt(0);
+	if (!bufBase || dlgListOff == 0 || dlgListOff >= _mystery.dataSize())
+		return;
+	uint32 off = dlgListOff;
+	for (uint h = 0; h < hotIdx; h++) {
+		// Skip main record.
+		const byte *rec = bufBase + off;
+		off += 11 + rec[10];
+		// Read continuation count and skip those records.
+		const uint contCount = bufBase[off] & 0x7F;
+		off += 1;
+		for (uint c = 0; c < contCount; c++) {
+			const byte *cr = bufBase + off;
+			off += 11 + cr[10];
+		}
+	}
+	if (off >= _mystery.dataSize())
+		return;
+	// Layout per hotspot is:
+	//   main record (11 + textCount bytes)
+	//   1 byte: continuation count (high bit = partner-pose flag,
+	//           low 7 bits = number of follow-up records)
+	//   continuation records (each 11 + textCount bytes, tightly packed)
+	// `displayFloppyDialogRecords` walks tightly so we have to call it
+	// twice ??? once for the main record, once for the continuations ???
+	// otherwise the second iteration treats the cont-count byte as a
+	// record header and runs off the buffer.
+	//
+	// Each `displayFloppyDialogRecords` call snapshots the screen to
+	// know what to redraw between bubbles. If we just call it twice
+	// back-to-back the second snapshot includes the first call's last
+	// bubble, so the second bubble draws on top of the first one (the
+	// "background isn't redrawn" glitch the user reported). Capture
+	// the clean site BG here and restore it between the two calls so
+	// every record snapshots a bubble-free background.
+	Graphics::ManagedSurface siteBG(320, 200,
+		Graphics::PixelFormat::createFormatCLUT8());
+	{
+		Graphics::Surface *screen = g_system->lockScreen();
+		if (screen) {
+			siteBG.simpleBlitFrom(*screen);
+			g_system->unlockScreen();
+		}
+	}
+	const byte *mainRec = bufBase + off;
+	displayFloppyDialogRecords(mainRec, 1);
+	const uint mainLen = 11u + (uint)mainRec[10];
+	if (off + mainLen >= _mystery.dataSize())
+		return;
+	const uint contCount = (uint)(bufBase[off + mainLen] & 0x7F);
+	if (contCount == 0)
+		return;
+	const uint32 contOff = off + mainLen + 1;
+	if (contOff >= _mystery.dataSize())
+		return;
+	// Wipe the main bubble before the continuation chain snapshots the
+	// screen ??? otherwise the first continuation bubble treats the
+	// post-main-bubble image as its background and the main balloon
+	// pixels persist behind every following balloon.
+	g_system->copyRectToScreen(siteBG.getPixels(), siteBG.pitch,
+							   0, 0, 320, 200);
+	g_system->updateScreen();
+	displayFloppyDialogRecords(bufBase + contOff, contCount);
+}
+
 bool EEMEngine::areYouSure() {
 	// Mirrors `_AreYouSure` @ 1a35:0a5c. Original loads PIC 0x136 for the
 	// dialog body and PIC 0x1FD/0x1FE for YES/NO. We render a minimal
diff --git a/engines/eem/eem.h b/engines/eem/eem.h
index 74046faec8d..0b926e5655b 100644
--- a/engines/eem/eem.h
+++ b/engines/eem/eem.h
@@ -185,6 +185,13 @@ public:
 	/// followed by 62-byte ClueEntries. Mirrors _DisplayClue @ 2404:05e6.
 	void displayClue(const byte *clueBlock);
 
+	/// Floppy hotspot click ??? locate the dialog records for the
+	/// clicked hotspot in the site's per-hotspot dialog list at
+	/// `site_data[+6]` and dispatch them through
+	/// `displayFloppyDialogRecords`. Mirrors `FUN_22dc_0b80 +
+	/// FUN_1652_00e6 + FUN_1652_006c`.
+	void displayFloppyHotspotDialog(uint siteNum, uint hotIdx);
+
 	/// Active player name (saved as the profile-save description).
 	const Common::String &playerName() const { return _playerName; }
 
@@ -439,6 +446,12 @@ private:
 	///   +11..  text indices (1 byte each, low 7 bits = NOTES idx)
 	void displayFloppyBriefing(const byte *initBlock);
 
+	/// Render `count` consecutive floppy dialog records starting at
+	/// `rec`. Shared between briefing and hotspot click handlers since
+	/// the original engine uses the same `FUN_22dc_05c8 @ 22dc:05c8`
+	/// renderer in both contexts.
+	void displayFloppyDialogRecords(const byte *rec, uint count);
+
 public:
 	/// Mirrors `_StartTravelMusic @ 20a2:0595`. Picks `MUS%05d.XMI`
 	/// based on `_mystery._siteNumber % 5` and starts it (looping). The
diff --git a/engines/eem/mystery.cpp b/engines/eem/mystery.cpp
index 437a8f62499..98f48a1d78e 100644
--- a/engines/eem/mystery.cpp
+++ b/engines/eem/mystery.cpp
@@ -287,17 +287,25 @@ const byte *Mystery::siteData(uint siteNum) const {
 }
 
 const byte *Mystery::hotspots(uint siteNum) const {
+	if (_isFloppy) {
+		// Floppy: hotspot table sits inside the per-site sub-blob.
+		// `site_data[+4..5]` is a u16 file offset to a header byte
+		// (count) + N??8-byte rectangles (x1, y1, x2, y2 as u16s) ???
+		// verified at `FUN_22dc_0b80 @ 22dc:0b80` (the click hit-test
+		// loop reads `*(byte *)(buf + site_data[+4])` for the count
+		// then `FUN_14c9_0039(... buf + site_data[+4] + 1 + i*8)`
+		// for each rectangle).
+		const byte *site = siteData(siteNum);
+		if (!site || (size_t)(site - _data.data()) + 6 > _data.size())
+			return nullptr;
+		const uint16 hotspotOff = READ_LE_UINT16(site + 4);
+		if (hotspotOff == 0 || hotspotOff + 1 > _data.size())
+			return nullptr;
+		return _data.data() + hotspotOff + 1;
+	}
 	const byte *idx = siteIndexEntry(siteNum);
 	if (!idx)
 		return nullptr;
-	// Floppy site index is only 2 bytes per entry; `idx + 4` would
-	// read into the NEXT entry's offset, returning garbage. The
-	// floppy hotspot list lives inside the site-data sub-blob (see
-	// `_DoSiteLoop_Floppy @ 1652:03a3` walking 5-byte drop entries
-	// from `*site_data + 2`); don't fake a CD-style result here ???
-	// SiteScreen guards floppy paths separately.
-	if (_isFloppy)
-		return nullptr;
 	const uint16 hotspotOff = READ_LE_UINT16(idx + 4);
 	if (hotspotOff >= _data.size())
 		return nullptr;
@@ -305,8 +313,15 @@ const byte *Mystery::hotspots(uint siteNum) const {
 }
 
 uint16 Mystery::hotspotCount(uint siteNum) const {
-	if (_isFloppy)
-		return 0;
+	if (_isFloppy) {
+		const byte *site = siteData(siteNum);
+		if (!site || (size_t)(site - _data.data()) + 6 > _data.size())
+			return 0;
+		const uint16 hotspotOff = READ_LE_UINT16(site + 4);
+		if (hotspotOff == 0 || hotspotOff >= _data.size())
+			return 0;
+		return (uint16)_data[hotspotOff];
+	}
 	const byte *site = siteData(siteNum);
 	if (!site || (size_t)(site - _data.data()) + 8 > _data.size())
 		return 0;
diff --git a/engines/eem/mystery.h b/engines/eem/mystery.h
index 4b23fa4ea27..251320604aa 100644
--- a/engines/eem/mystery.h
+++ b/engines/eem/mystery.h
@@ -141,6 +141,9 @@ public:
 		return offset < _data.size() ? _data.data() + offset : nullptr;
 	}
 
+	/// Total mystery blob size in bytes (for bounds checks).
+	uint32 dataSize() const { return (uint32)_data.size(); }
+
 	/// Synchronize the per-mystery runtime state for save/load. The fixed
 	/// arrays serialize first, then the booleans and counters.
 	void syncState(Common::Serializer &s);
diff --git a/engines/eem/site.cpp b/engines/eem/site.cpp
index 80610200252..1add3abbd4c 100644
--- a/engines/eem/site.cpp
+++ b/engines/eem/site.cpp
@@ -588,8 +588,13 @@ void SiteScreen::enter(uint siteNum) {
 	_vm->stopMusic();
 
 	// Static drops (Loop 2 from `_DoSiteLoop`) ??? no animation, baked
-	// into the BG snapshot the run() pump uses to restore.
-	renderStaticDrops(siteNum);
+	// into the BG snapshot the run() pump uses to restore. Floppy
+	// stores them in a different shape (drops sub-struct after the
+	// site_data offset), so dispatch on `isFloppy()`.
+	if (_vm->isFloppy())
+		renderFloppyDrops(siteNum);
+	else
+		renderStaticDrops(siteNum);
 
 	// Snapshot the static layers so per-tick animation re-blits don't
 	// have to re-load PIC 0x43, the SITES.DBD scene, or each
@@ -643,7 +648,10 @@ void SiteScreen::enter(uint siteNum) {
 		// balloon residues; refresh the site so the player returns to
 		// a clean state. Re-build the snapshot too.
 		renderBackground(siteNum);
-		renderStaticDrops(siteNum);
+		if (_vm->isFloppy())
+			renderFloppyDrops(siteNum);
+		else
+			renderStaticDrops(siteNum);
 		captureBgSnapshot();
 		_snapshotSite = (int)siteNum;
 		const uint32 nowAfter = g_system->getMillis();
@@ -938,6 +946,53 @@ void SiteScreen::renderStaticDrops(uint siteNum) {
 	g_system->unlockScreen();
 }
 
+void SiteScreen::renderFloppyDrops(uint siteNum) {
+	// Floppy drops live inside the drops sub-struct pointed to by
+	// `*site_data` (u16 offset). Verified from the call site in
+	// `_DoSiteLoop_Floppy @ 1652:0418`:
+	//   FUN_16e2_18eb(
+	//     *(u16 *)(local_1a + i*5),          // arg1 = u16 picID @ +0..1
+	//     *(u16 *)(local_1a + i*5 + 2),      // arg2 = u16 X     @ +2..3
+	//     local_1a[i*5 + 4]                  // arg3 = u8  Y     @ +4
+	//   );
+	// Inside `FUN_16e2_18eb @ 16e2:18eb`, `arg1 - 1` indexes the
+	// PICS.DBX table at `2608:4537` (loaded from `PICS.DBX` by
+	// `FUN_16e2_0149 @ 16e2:0149`); `arg2/arg3` become destX/destY.
+	// drops_struct[0] = BG picID (rendered separately by
+	// `renderBackground`), drops_struct[1] = drop count.
+	if (!_mystery)
+		return;
+	const byte *site = _mystery->siteData(siteNum);
+	if (!site)
+		return;
+	const uint16 dropsOff = READ_LE_UINT16(site);
+	const byte *drops = _mystery->blobAt(dropsOff);
+	if (!drops)
+		return;
+	const uint8 count = drops[1];
+
+	Graphics::Surface *screen = g_system->lockScreen();
+	if (!screen)
+		return;
+
+	for (uint i = 0; i < count; i++) {
+		const byte *e = drops + 2 + i * 5;
+		const uint16 picID = READ_LE_UINT16(e + 0);
+		const int16  x     = (int16)READ_LE_UINT16(e + 2);
+		const int16  y     = (int16)e[4];
+		if (picID == 0)
+			continue;
+		// `getPicture(num)` already does `loadEntry(num - 1)` (see
+		// `resource.h:100`), matching the `picID - 1` index the
+		// original passes to PICS.DBD.
+		Picture pic;
+		if (!_vm->getPics().getPicture((uint)picID, pic))
+			continue;
+		blitMaskedSurface(screen, pic, x, y);
+	}
+	g_system->unlockScreen();
+}
+
 void SiteScreen::renderAnimatedDrops(uint siteNum, uint32 tickMs) {
 	// Loop 1 from `_DoSiteLoop @ 168d:03f4`:
 	//   bound: siteData[+0xa]
@@ -1071,29 +1126,46 @@ void SiteScreen::renderPartner(uint siteNum, uint32 tickMs) {
 	// `kWaitAnims` lives at file scope above; we cap rendering at
 	// `speaker < 7` since anything past entry 6 is the `_SiteButtons`
 	// rect data that follows the table in the binary.
-	// Floppy site data has a different shape: site_data+8 is a u16
-	// OFFSET to a 10-byte speakerInfo struct (verified at
-	// `_DoSiteLoop_Floppy @ 1652:042b` reading `*(undefined2 *)
-	// (DAT_28da_0172) = anim_id_jake` etc.), not an index into the
-	// `kWaitAnims` table the CD uses. Skip the partner render until
-	// the floppy speakerInfo is wired up ??? without this guard we
-	// dereference garbage entries past the table end.
-	if (_vm && _vm->isFloppy())
-		return;
 	const byte *site = _mystery->siteData(siteNum);
 	if (!site)
 		return;
-	const uint16 speaker = READ_LE_UINT16(site + 8);
-	if (speaker >= ARRAYSIZE(kWaitAnims)) {
-		warning("renderPartner: site %u has speakerIdx=%u out of range",
-				siteNum, speaker);
-		return;
-	}
-
 	const uint8 partner = _vm->getPartnerIndex();
-	const uint  animId  = kWaitAnims[speaker][0 + partner];
-	const int   x       = (int)(int16)kWaitAnims[speaker][2 + partner];
-	const int   y       = (int)(int16)kWaitAnims[speaker][4 + partner];
+	uint   animId;
+	int    x;
+	int    y;
+	if (_vm->isFloppy()) {
+		// Floppy: site_data+8 is a u16 OFFSET to a 10-byte
+		// speakerInfo struct (per `_DoSiteLoop_Floppy @ 1652:042b`):
+		//   bytes 0..1  Jake anim ID  (u16)
+		//   bytes 2..3  Jake X        (u16)
+		//   byte  4     Jake Y        (u8)
+		//   bytes 5..6  Jenny anim ID (u16)
+		//   bytes 7..8  Jenny X       (u16)
+		//   byte  9     Jenny Y       (u8)
+		const uint16 spkOff = READ_LE_UINT16(site + 8);
+		const byte *spk = _mystery->blobAt(spkOff);
+		if (!spk)
+			return;
+		if (partner == 0) {
+			animId = READ_LE_UINT16(spk + 0);
+			x      = (int)READ_LE_UINT16(spk + 2);
+			y      = (int)spk[4];
+		} else {
+			animId = READ_LE_UINT16(spk + 5);
+			x      = (int)READ_LE_UINT16(spk + 7);
+			y      = (int)spk[9];
+		}
+	} else {
+		const uint16 speaker = READ_LE_UINT16(site + 8);
+		if (speaker >= ARRAYSIZE(kWaitAnims)) {
+			warning("renderPartner: site %u has speakerIdx=%u out of range",
+					siteNum, speaker);
+			return;
+		}
+		animId = kWaitAnims[speaker][0 + partner];
+		x      = (int)(int16)kWaitAnims[speaker][2 + partner];
+		y      = (int)(int16)kWaitAnims[speaker][4 + partner];
+	}
 
 	Animation anim;
 	if (!_vm->getAni().loadAnimation(animId, anim) || anim.empty())
@@ -1227,8 +1299,13 @@ void SiteScreen::renderHotspots(uint siteNum) {
 	// don't all glow in lock-step.
 	const uint32 tickMs = g_system->getMillis();
 
+	// CD hotspot rows are 14 bytes each (rect + 6 bytes of clue
+	// metadata). Floppy stores plain 8-byte rectangles only ??? clue
+	// data lives in a separate dialog-record list at `site_data[+6]`,
+	// keyed by hotspot index. Verified at `FUN_22dc_0b80 @ 22dc:0b80`.
+	const uint stride = _vm && _vm->isFloppy() ? 8 : 14;
 	for (uint i = 0; i < count; i++) {
-		const byte *r = spots + i * 14;
+		const byte *r = spots + i * stride;
 		const int16 x1 = (int16)READ_LE_UINT16(r + 0);
 		const int16 y1 = (int16)READ_LE_UINT16(r + 2);
 		const int16 x2 = (int16)READ_LE_UINT16(r + 4);
@@ -1289,8 +1366,9 @@ int SiteScreen::hotspotAtPoint(uint siteNum, int x, int y) const {
 	if (!spots)
 		return -1;
 
+	const uint stride = _vm && _vm->isFloppy() ? 8 : 14;
 	for (uint i = 0; i < count; i++) {
-		const byte *r = spots + i * 14;
+		const byte *r = spots + i * stride;
 		const int16 x1 = (int16)READ_LE_UINT16(r + 0);
 		const int16 y1 = (int16)READ_LE_UINT16(r + 2);
 		const int16 x2 = (int16)READ_LE_UINT16(r + 4);
@@ -1304,6 +1382,21 @@ int SiteScreen::hotspotAtPoint(uint siteNum, int x, int y) const {
 void SiteScreen::onHotspotClicked(uint siteNum, uint hotIdx) {
 	debugC(1, kDebugSite, "Site %u: hotspot %u clicked", siteNum, hotIdx);
 
+	// Floppy: hotspot rectangles are plain 8-byte rects (no clue
+	// metadata at +0xa or +8); the dialog records live in a separate
+	// per-hotspot list at `site_data[+6]`. Dispatch through the
+	// floppy-specific renderer and mark the click rectangle seen by
+	// array index (the only ordinal we have on floppy).
+	if (_vm->isFloppy()) {
+		if (hotIdx < Mystery::kHotSpotsCap)
+			_mystery->_hotSpotsSeen[hotIdx] = 1;
+		_mystery->_searchLocationNumber = (uint16)hotIdx;
+		_vm->setPartnerEraseBg(&_bgSnapshot);
+		_vm->displayFloppyHotspotDialog(siteNum, hotIdx);
+		_vm->setPartnerEraseBg(nullptr);
+		return;
+	}
+
 	// `_DoSiteLoop @ 168d:03f4` (after _DisplayClue):
 	//   _HotSpotsSeen[hotspot[+0xa] * 2] = _HotSpotComplete;
 	// The "seen" key is the hotspotIndex field (+0xa) ??? the 1-based
diff --git a/engines/eem/site.h b/engines/eem/site.h
index 33f6f23ab55..23621409806 100644
--- a/engines/eem/site.h
+++ b/engines/eem/site.h
@@ -131,6 +131,13 @@ private:
 	/// animate so they go in the BG snapshot.
 	void renderStaticDrops(uint siteNum);
 
+	/// Floppy variant: drops live inside the drops sub-struct
+	/// (`*site_data` ??? drops; `drops[1]` = count; entries at
+	/// `drops + 2` are 5 bytes each: {u16 X, u16 Y, byte picID}).
+	/// PIC entries are loaded from PICS.DBD with `picID - 1`. Per
+	/// `_DoSiteLoop_Floppy @ 1652:0418` and `FUN_16e2_18eb`.
+	void renderFloppyDrops(uint siteNum);
+
 	/// Draw the per-site animated NPCs (Loop 1) at the current tick.
 	/// `_DoSiteLoop` registers each via `_NewAnimation` (siteData[+0xa]
 	/// entries at siteData[+0x48]: {animId (-1 = ColorCycle), x, y})
diff --git a/engines/eem/ui.cpp b/engines/eem/ui.cpp
index 7e7e9de005f..a7589a566f6 100644
--- a/engines/eem/ui.cpp
+++ b/engines/eem/ui.cpp
@@ -2710,14 +2710,15 @@ void EEMEngine::doBigMap() {
 							continue;
 						uint16 mx;
 						uint16 my;
-						uint16 buttonId = 0;
+						uint16 buttonId;
 						if (fmap) {
 							// Floppy detail view: click rect on
-							// BIGMAP.PIC at (+0, +2), per
-							// `FUN_1fed_0c3e`'s write to
-							// DAT_28da_3aee/DAT_28da_3af0.
+							// BIGMAP.PIC at (+0, +2), labelled BUTTON.DBD
+							// entry ID at entry+4 (per
+							// `FUN_1fed_0c3e @ 1fed:0c3e`).
 							mx = READ_LE_UINT16(entry + 0x0);
 							my = READ_LE_UINT16(entry + 0x2);
+							buttonId = (uint16)entry[0x4];
 						} else {
 							buttonId = READ_LE_UINT16(entry + 0x0);
 							mx       = READ_LE_UINT16(entry + 0x8);
@@ -2726,18 +2727,7 @@ void EEMEngine::doBigMap() {
 						Picture button;
 						int bw = 16;
 						int bh = 16;
-						if (fmap) {
-							// Floppy uses the global site-marker PIC for
-							// every site; the recolor flag at +10 picks
-							// the crime variant. Sized off whichever PIC
-							// is loaded successfully.
-							Picture m;
-							const uint pic = (entry[0xa] != 0) ? 0xc6 : 0xc5;
-							if (_picsArchive.getPicture(pic, m)) {
-								bw = m.surface.w;
-								bh = m.surface.h;
-							}
-						} else if (_buttonArchive.loadEntry(buttonId, button)) {
+						if (_buttonArchive.loadEntry(buttonId, button)) {
 							bw = button.surface.w;
 							bh = button.surface.h;
 						}
@@ -2916,18 +2906,18 @@ void EEMEngine::drawBigMapDetail(int scrollX, int scrollY,
 			   copyW);
 	}
 
-	// Stamped site buttons. `_StampButtons @ 20fe:0d2f`:
+	// Stamped site buttons. `_StampButtons @ 20fe:0d2f` (CD):
 	//   button = _GetButton(MapData[+0])
 	//   destX  = MapData[+8]
 	//   destY  = MapData[+0xa]
-	// Floppy SITES rows are 11 bytes ??? no per-button PIC ID. Use the
-	// regular site / crime marker PICs (0xc5 / 0xc6) keyed off the
-	// recolor flag at +10, same logic as the overview stamp.
+	// Floppy uses `FUN_1fed_0c3e @ 1fed:0c3e`: for each SITES row, the
+	// byte at entry+4 is a BUTTON.DBD entry ID (loaded via
+	// `FUN_16e2_1838 @ 16e2:1838`, which opens `button.dbd` ??? string
+	// at `2608:0558`). The labelled button is stamped at
+	// `(entry+0..1, entry+2..3)` on BIGMAP.PIC. These are the same
+	// per-site labelled buttons the CD uses, just keyed off a
+	// different field offset.
 	const bool floppyMap = _mystery.isLoaded() && isFloppy();
-	Picture floppySiteM;
-	Picture floppyCrimeM;
-	const bool haveFloppySite  = floppyMap && _picsArchive.getPicture(0xc5, floppySiteM);
-	const bool haveFloppyCrime = floppyMap && _picsArchive.getPicture(0xc6, floppyCrimeM);
 	for (uint i = 0; i < _mystery.numSites(); i++) {
 		if (!_mystery._onSites[i] && i != _mystery._siteNumber)
 			continue;
@@ -2938,24 +2928,10 @@ void EEMEngine::drawBigMapDetail(int scrollX, int scrollY,
 		uint16 my;
 		Picture button;
 		if (floppyMap) {
-			// Floppy SITES rows carry TWO position pairs:
-			//   (+0, +2) = position on BIGMAP.PIC (the zoomed view).
-			//              Used by `FUN_1fed_0c3e @ 1fed:0c3e` for
-			//              suspect-portrait stamping AND as the
-			//              click bbox on the zoomed map.
-			//   (+6, +8) = position on the overview PIC 0x42.
-			//              Used by `FUN_1fed_07ed @ 1fed:07ed`.
-			// The detail view scrolls BIGMAP.PIC, so we need the
-			// (+0, +2) coords here. Recolor flag at +10 still
-			// selects crime vs site marker.
 			mx = READ_LE_UINT16(entry + 0x0);
 			my = READ_LE_UINT16(entry + 0x2);
-			const bool useCrime = entry[0xa] != 0;
-			if (useCrime && haveFloppyCrime)
-				button = floppyCrimeM;
-			else if (haveFloppySite)
-				button = floppySiteM;
-			else
+			const uint16 buttonId = (uint16)entry[0x4];
+			if (!_buttonArchive.loadEntry(buttonId, button))
 				continue;
 		} else {
 			const uint16 buttonId = READ_LE_UINT16(entry + 0x0);


Commit: 20eba94c78e88e73d1a33ee7d41f1bf23186f9fd
    https://github.com/scummvm/scummvm/commit/20eba94c78e88e73d1a33ee7d41f1bf23186f9fd
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:20:52+02:00

Commit Message:
EEM: improved hints and audio in the floppy version

Changed paths:
    engines/eem/audio.cpp
    engines/eem/audio.h
    engines/eem/clues.cpp
    engines/eem/graphics.cpp
    engines/eem/mystery.cpp
    engines/eem/ui.cpp


diff --git a/engines/eem/audio.cpp b/engines/eem/audio.cpp
index 4c2e2a4bb15..6ed89dcdf0c 100644
--- a/engines/eem/audio.cpp
+++ b/engines/eem/audio.cpp
@@ -199,6 +199,39 @@ void AudioPlayer::playPcmBuffer(byte *pcm, uint32 size, uint sampleRate,
 					   DisposeAfterUse::YES);
 }
 
+// Floppy per-partner voice table ??? verified by reading the raw filename
+// pointers at `2608:0f0e` (Jake) and `2608:0f76` (Jenny) in EEM.EXE,
+// each 26 ?? FAR-ptr to a NUL-terminated `*.voc` filename. Indexed by
+// `_LoadSoundName_Floppy @ 1f4e:0305`. Slots match across partners:
+//   12 = PHONESL.VOC, 20 = partner intro, 25 = THUNDER.VOC.
+static const char *const kFloppyJakeVoiceTable[26] = {
+	"DING.VOC",       "M-0083SL.VOC", "M-0085SL.VOC", "NEWSCAN.VOC",
+	"M-0089SL.VOC",   "M-0091SL.VOC", "M-0092SL.VOC", "NEWSSHRT.VOC",
+	"M-0096SL.VOC",   "M-0102SL.VOC", "M-0104SL.VOC", "M-0107SL.VOC",
+	"PHONESL.VOC",    "M-0113SL.VOC", "DING.VOC",     "DING.VOC",
+	"M-0054SL.VOC",   "M-0014SL.VOC", "M-0012SL.VOC", "SQUAK2SL.VOC",
+	"M-0113SL.VOC",   "B-0006SL.VOC", "B-0003SL.VOC", "B-0004SL.VOC",
+	"M-0163SL.VOC",   "THUNDER.VOC",
+};
+static const char *const kFloppyJennyVoiceTable[26] = {
+	"DING.VOC",       "F-0194SL.VOC", "F-0191SL.VOC", "NEWSCAN.VOC",
+	"F-0187SL.VOC",   "F-0184SL.VOC", "F-0181SL.VOC", "NEWSSHRT.VOC",
+	"F-0177SL.VOC",   "F-0170SL.VOC", "F-0168SL.VOC", "F-0166SL.VOC",
+	"PHONESL.VOC",    "F-0161SL.VOC", "DING.VOC",     "DING.VOC",
+	"F-0067SL.VOC",   "F-0016SL.VOC", "F-0013SL.VOC", "SQUAK2SL.VOC",
+	"F-0140SL.VOC",   "B-0006SL.VOC", "B-0003SL.VOC", "B-0004SL.VOC",
+	"F-0165SL.VOC",   "THUNDER.VOC",
+};
+
+void AudioPlayer::playFloppyVoiceSlot(uint slot, uint partner) {
+	if (slot >= 26)
+		slot = 0;  // mirrors `_LoadSoundName_Floppy`'s `if (0x19 < slot) slot = 0;`
+	const char *name = (partner == 0)
+		? kFloppyJakeVoiceTable[slot]
+		: kFloppyJennyVoiceTable[slot];
+	playVoc(Common::Path(name));
+}
+
 void AudioPlayer::spoolSound(uint num) {
 	if (!_voiceEnabled) {
 		debugC(2, kDebugSound,
diff --git a/engines/eem/audio.h b/engines/eem/audio.h
index 921e00e900b..12667f1179d 100644
--- a/engines/eem/audio.h
+++ b/engines/eem/audio.h
@@ -104,6 +104,13 @@ public:
 	/// Mirrors `_StopTheVoice @ 1ff1:0283`.
 	void stopVoice();
 
+	/// Floppy variant: play a VOC by 0..25 slot index in the per-partner
+	/// voice table. Mirrors `_LoadSoundName_Floppy @ 1f4e:0305` which
+	/// indexes the table at `2608:0f0e` (Jake) / `2608:0f76` (Jenny).
+	/// `partner` is 0 for Jake, 1 for Jenny. Common slots: 12 =
+	/// PHONESL.VOC, 20 = partner intro voice, 25 = THUNDER.VOC.
+	void playFloppyVoiceSlot(uint slot, uint partner);
+
 	// Mystery sound spool ---------------------------------------------
 
 	/// Mirrors `_InitMysterySounds @ 202f:05cb`. Loads `M%u.SDX` into
diff --git a/engines/eem/clues.cpp b/engines/eem/clues.cpp
index 8e4cd5798d1..d53fb7f57ae 100644
--- a/engines/eem/clues.cpp
+++ b/engines/eem/clues.cpp
@@ -236,21 +236,16 @@ void EEMEngine::doChoosePartner() {
 	// (`jen.voc` for Jenny, `jake.voc` for Jake; strings at 29be:0af1 /
 	// 29be:0af9) and block on `_WaitForVoiceDone`.
 	if (_audio) {
-		// CD: standalone clips `JAKE.VOC` / `JEN.VOC`. Floppy uses
-		// per-partner-and-event voice tables at `2608:0F0E` (Jake) /
-		// `2608:0F76` (Jenny), each 25 entries ?? FAR ptr to a VOC
-		// filename. After a partner pick, `FUN_19bb_0858 @ 19bb:0858`
-		// calls `FUN_1f4e_0305(0x14)` which loads voice slot 20 from
-		// the table for the chosen partner:
-		//   Jake  slot 20 ??? `2608:116B = "m-0113sl.voc"`
-		//   Jenny slot 20 ??? `2608:12AE = "f-0140sl.voc"`
-		Common::String voc;
 		if (isFloppy()) {
-			voc = (_partner == 0) ? "M-0113SL.VOC" : "F-0140SL.VOC";
+			// Floppy `_DoChoosePartner_Floppy @ 19bb:0a8e` calls
+			// `_LoadSoundName_Floppy(0x14)` (= slot 20) which the
+			// per-partner table at `2608:0f0e` / `2608:0f76` resolves
+			// to `m-0113sl.voc` (Jake) or `f-0140sl.voc` (Jenny).
+			_audio->playFloppyVoiceSlot(0x14, _partner);
 		} else {
-			voc = (_partner == 0) ? "JAKE.VOC" : "JEN.VOC";
+			_audio->playVoc(Common::Path(
+				(_partner == 0) ? "JAKE.VOC" : "JEN.VOC"));
 		}
-		_audio->playVoc(Common::Path(voc));
 		_audio->waitForVoiceDone();
 	}
 }
@@ -507,16 +502,25 @@ void EEMEngine::doInitClues() {
 		}
 	}
 
-	// `_DoInitClues` plays `phone.voc` (29be:0acc) ONLY when caseType == 2
-	// (the "incoming call" briefing variant). Verified at 1a35:05a2 ???
-	// the gate is `iVar1 == 2 && _VoiceAvailable`. Other case types open
-	// straight into the briefing dialogue without it.
-	if (caseType == 2 && _audio) {
-		// Floppy ships `PHONESL.VOC` instead of CD's `PHONE.VOC` (the
-		// `_LoadSoundName` call site at 2608:1107-110c hands the
-		// floppy filename to the same loader).
-		_audio->playVoc(Common::Path(isFloppy() ? "PHONESL.VOC" : "PHONE.VOC"));
-		_audio->waitForVoiceDone();
+	// `_DoInitClues` plays a setup-voice ONLY for caseType 2/3.
+	//   CD: caseType 2 ??? PHONE.VOC. caseType 3 ??? no voice.
+	//   Floppy `_DoInitClues_Floppy @ 19bb:042f`:
+	//     caseType 2 ??? `_LoadSoundName_Floppy(slot 0xc)` = PHONESL.VOC
+	//     caseType 3 ??? `_LoadSoundName_Floppy(slot 3)`   = NEWSCAN.VOC
+	// (newspaper-scanner sting for the "TV/news anchor" briefing
+	// variant). Other case types open straight into the briefing
+	// dialogue without it.
+	if (_audio) {
+		if (caseType == 2) {
+			if (floppy)
+				_audio->playFloppyVoiceSlot(0x0c, _partner);
+			else
+				_audio->playVoc(Common::Path("PHONE.VOC"));
+			_audio->waitForVoiceDone();
+		} else if (caseType == 3 && floppy) {
+			_audio->playFloppyVoiceSlot(0x03, _partner);
+			_audio->waitForVoiceDone();
+		}
 	}
 
 	// Step 6 ??? case briefing dialogue. CD InitBlock has the clue block
@@ -944,17 +948,39 @@ void EEMEngine::displayFloppyDialogRecords(const byte *rec, uint count) {
 		const uint8  ballY    = rec[8];
 		const uint8  textCount= rec[10];
 
+		// Per-record voice playback. `FUN_22dc_05c8 @ 22dc:05c8`:
+		//   if ((rec[+9] & 0x80) && voiceOption && voiceAvail) {
+		//       slot = rec[+9] & 0x7f;
+		//       _LoadSoundName_Floppy(slot); _PlayVoc(...);
+		//   }
+		// The slot indexes the per-partner table at `2608:0f0e` /
+		// `2608:0f76` (= our `playFloppyVoiceSlot`). Sound is started
+		// BEFORE the bubble renders so it plays while the player reads.
+		if ((rec[9] & 0x80) != 0 && _audio) {
+			const uint slot = rec[9] & 0x7f;
+			_audio->playFloppyVoiceSlot(slot, _partner);
+		}
+
 		// Build the full text by concatenating each note's per-partner
 		// text string (parsed for `%s`-style placeholders). Bounds-
 		// check every offset against the mystery blob; if any byte
 		// looks out of range we just stop appending ??? corrupt offsets
 		// from a misparsed record would otherwise dereference past the
 		// buffer and SIGBUS inside `strlen`.
+		//
+		// Each text index also flags `_cluesFound[idx]` so the PDA /
+		// notebook ("DrawNotes") later renders the clue. Mirrors
+		// `FUN_22dc_05c8 @ 22dc:091a`:
+		//   ((undefined1 *)&DAT_28da_3c08)[textIdx & 0x7f] = 1;
+		// `DAT_28da_3c08` is the floppy `TextSeen` array; we reuse the
+		// `_cluesFound` flag store since both index 0..127 by note id.
 		const uint32 dsz       = _mystery.dataSize();
 		const uint32 notesBase = (uint32)(notes - bufBase);
 		Common::String raw;
 		for (uint t = 0; t < textCount; t++) {
 			const uint8  idx        = rec[11 + t] & 0x7f;
+			if (idx < Mystery::kCluesFoundCap)
+				_mystery._cluesFound[idx] = 1;
 			const uint32 noteAbs    = notesBase + (uint32)idx * 7;
 			if (noteAbs + 6 > dsz)
 				break;
@@ -975,6 +1001,23 @@ void EEMEngine::displayFloppyDialogRecords(const byte *rec, uint count) {
 				raw += ' ';
 			raw += Common::String(line, lineLen);
 		}
+
+		// Suspect/gallery side effect ??? `FUN_22dc_05c8 @ 22dc:08eb`:
+		//   if ((rec[+9] & 0x80) == 0 && rec[+9] != 0)
+		//       *(u16 *)(0x5d20 + table[0x2d65 + rec[+9]] * 2) = 1;
+		// Byte 9 doubles as the voice slot (when high bit set) or as a
+		// suspect identifier (low 7 bits, when high bit clear). The
+		// 0x5d20 array is the per-mystery "suspect found in gallery"
+		// flag table that `FUN_154e_0045` reads at gallery render time.
+		// We don't model the per-mystery shuffle table at 0x2d65
+		// (`_NewOrder` is set to identity in `_ReadMystery_Floppy` for
+		// our port), so byte9-1 indexes `_inGallery` directly.
+		const uint8 b9 = rec[9];
+		if ((b9 & 0x80) == 0 && b9 != 0) {
+			const uint slot = (uint)b9 - 1;
+			if (slot < Mystery::kGalleryCap)
+				_mystery._inGallery[slot] = 1;
+		}
 		const Common::String text = parseString(raw, _playerName, _partner);
 
 		// Restore briefing BG before drawing this bubble.
diff --git a/engines/eem/graphics.cpp b/engines/eem/graphics.cpp
index 33a3536c454..5af91578ac5 100644
--- a/engines/eem/graphics.cpp
+++ b/engines/eem/graphics.cpp
@@ -76,7 +76,211 @@ const BalloonInsets kBalloonInsetTable[] = {
 	{ 5, 8, 158 }, { 5, 8, 176 }, { 8, 7, 142 }
 };
 
+// Floppy KDHelp hotspot-searched check. Mirrors
+// `FUN_22dc_096c @ 22dc:096c`: walks the per-site dialog records at
+// `site_data[+6]` to skip `hotspotIdx` hotspots, then returns the
+// `_cluesFound` flag for that hotspot's first text index.
+static bool floppyHotspotSearched(EEM::Mystery &mystery, uint siteIdx,
+								   uint hotspotIdx) {
+	const byte *site = mystery.siteData(siteIdx);
+	if (!site)
+		return false;
+	const uint16 dlgListOff = READ_LE_UINT16(site + 6);
+	const byte *bufBase = mystery.blobAt(0);
+	if (!bufBase || dlgListOff == 0 || dlgListOff >= mystery.dataSize())
+		return false;
+	uint32 off = dlgListOff;
+	for (uint h = 0; h < hotspotIdx; h++) {
+		const byte *rec = bufBase + off;
+		off += 11u + (uint)rec[10];
+		if (off >= mystery.dataSize())
+			return false;
+		const uint contCount = (uint)(bufBase[off] & 0x7F);
+		off += 1;
+		for (uint c = 0; c < contCount; c++) {
+			const byte *cr = bufBase + off;
+			off += 11u + (uint)cr[10];
+			if (off >= mystery.dataSize())
+				return false;
+		}
+	}
+	if (off + 11 >= mystery.dataSize())
+		return false;
+	const byte *mainRec = bufBase + off;
+	const uint8 textIdx = mainRec[11] & 0x7F;
+	return textIdx < EEM::Mystery::kCluesFoundCap &&
+		   mystery._cluesFound[textIdx] != 0;
+}
+
 void EEMEngine::doHelp() {
+	// Floppy uses a totally different hint mechanism ??? per-mystery
+	// `H<n>.BIN` data files (one per case). Format verified at
+	// `FUN_1503_0001 @ 1503:0001` (loader, format string at
+	// `2608:0154` = "h%d.bin") + `FUN_1503_01a5 @ 1503:01a5`
+	// (consumer):
+	//   byte 0 = numChainHints
+	//   numChainHints ?? { byte siteIdx; byte hotspotIdx; }
+	//   byte = numExtraHints
+	//   numExtraHints ?? { byte siteIdx; byte hotspotIdx; }
+	//   asciiz string 1  ("[balloon-digit]Let's go to <site>...")
+	//   asciiz string 2  (alternate hint)
+	//   asciiz string 3  (post-solve hint, used when score ??? 100)
+	// Selection logic: if any chain hotspot is unsearched ??? string 1.
+	// Else if any extra hotspot is unsearched ??? string 2. Else if
+	// `selectedPoints() ??? 100` ??? string 3.
+	if (isFloppy() && _mystery.isLoaded()) {
+		const Common::String filename = Common::String::format("H%u.BIN",
+															   _mystery.number());
+		Common::File hf;
+		if (!hf.open(Common::Path(filename))) {
+			warning("doHelp: cannot open %s", filename.c_str());
+			return;
+		}
+		const uint32 hsz = hf.size();
+		Common::Array<byte> hbuf;
+		hbuf.resize(hsz);
+		if (hf.read(hbuf.data(), hsz) != hsz)
+			return;
+		const byte *hd = hbuf.data();
+
+		const uint chainCount = hd[0];
+		uint off = 1;
+		uint chainEnd = off + chainCount * 2;
+		if (chainEnd >= hsz)
+			return;
+		const uint extraCount = hd[chainEnd];
+		uint extraStart = chainEnd + 1;
+		uint extraEnd = extraStart + extraCount * 2;
+		if (extraEnd >= hsz)
+			return;
+		// Three NUL-terminated strings follow.
+		const char *str1 = (const char *)(hd + extraEnd);
+		const char *str2 = nullptr;
+		const char *str3 = nullptr;
+		const char *p = str1;
+		while ((uint)((const byte *)p - hd) < hsz && *p != 0) p++;
+		if ((uint)((const byte *)p - hd) >= hsz) return;
+		str2 = p + 1;
+		p = str2;
+		while ((uint)((const byte *)p - hd) < hsz && *p != 0) p++;
+		if ((uint)((const byte *)p - hd) >= hsz) return;
+		str3 = p + 1;
+
+		const char *chosen = nullptr;
+		bool anyChainUnseen = false;
+		for (uint i = 0; i < chainCount; i++) {
+			const uint8 siteIdx    = hd[off + i * 2 + 0];
+			const uint8 hotspotIdx = hd[off + i * 2 + 1];
+			if (!floppyHotspotSearched(_mystery, siteIdx, hotspotIdx)) {
+				anyChainUnseen = true;
+				break;
+			}
+		}
+		bool anyExtraUnseen = false;
+		if (!anyChainUnseen) {
+			for (uint i = 0; i < extraCount; i++) {
+				const uint8 siteIdx    = hd[extraStart + i * 2 + 0];
+				const uint8 hotspotIdx = hd[extraStart + i * 2 + 1];
+				if (!floppyHotspotSearched(_mystery, siteIdx, hotspotIdx)) {
+					anyExtraUnseen = true;
+					break;
+				}
+			}
+		}
+		if (anyChainUnseen)
+			chosen = str1;
+		else if (anyExtraUnseen)
+			chosen = str2;
+		else if (_mystery.selectedPoints() >= 100)
+			chosen = str3;
+		if (!chosen || *chosen == 0)
+			return;
+
+		// Strip leading balloon-digit byte. `_GetKDTextBalloon @
+		// 1df2:0105` (= floppy `FUN_1d40_009f`) doesn't take the
+		// digit's *value* ??? it indexes the per-character table at
+		// `2608:0c14` by the literal byte, so '0'..'9' map to a
+		// non-trivial balloon-id sequence. Verified bytes at
+		// `2608:0c44` (= 0xc14 + '0'):
+		//   '0'???0x15, '1'???0x16, '2'???0x17, '3'???0x18, '4'???0x19,
+		//   '5'???0x1a, '6'???0x1c, '7'???0x1d, '8'???0x1e, '9'???0x0a.
+		// Without this map the previous (digit - '0') version asked
+		// `getBalloonInsets` for balloon 0 (text width 142) instead of
+		// the correct balloon 21 (text width 155), which is why the
+		// hint bubble rendered narrower than the original.
+		static const uint8 kFloppyDigitToBalloon[10] = {
+			0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1c, 0x1d, 0x1e, 0x0a
+		};
+		uint balloonIdx = 0x17;
+		const char *txt = chosen;
+		if (*txt >= '0' && *txt <= '9') {
+			balloonIdx = kFloppyDigitToBalloon[(int)(*txt - '0')];
+			txt++;
+		}
+
+		Common::String text = parseString(Common::String(txt),
+										   _playerName, _partner);
+		Graphics::ManagedSurface ms(320, 200,
+			Graphics::PixelFormat::createFormatCLUT8());
+		ms.clear();
+		{
+			Graphics::Surface *cur = g_system->lockScreen();
+			if (cur) {
+				ms.simpleBlitFrom(*cur);
+				g_system->unlockScreen();
+			}
+		}
+		Picture balloon;
+		const bool haveBalloon = _balloonArchive.size() > balloonIdx &&
+			_balloonArchive.loadEntry(balloonIdx, balloon);
+		uint16 balloonY = 1;
+		if (haveBalloon) {
+			const uint h = (uint)balloon.surface.h;
+			if (h < 0x4e)
+				balloonY = (uint16)((0x50 - h) >> 1);
+			const byte transp = (byte)(balloon.flags >> 8);
+			for (int row = 0; row < balloon.surface.h && balloonY + row < 200;
+				 row++) {
+				const byte *src =
+					(const byte *)balloon.surface.getBasePtr(0, row);
+				byte *dst = (byte *)ms.getBasePtr(0x21, balloonY + row);
+				for (int col = 0; col < balloon.surface.w && 0x21 + col < 320;
+					 col++) {
+					if (src[col] != transp)
+						dst[col] = src[col];
+				}
+			}
+		}
+		uint16 bx = 5;
+		uint16 by = 4;
+		uint16 bw = 142;
+		getBalloonInsets(balloonIdx, bx, by, bw);
+		_font.drawWordWrapped(&ms, 0x21 + bx, balloonY + by,
+							  MAX<int>(8, (int)bw), text, 0);
+		g_system->copyRectToScreen(ms.getPixels(), ms.pitch, 0, 0, 320, 200);
+		g_system->updateScreen();
+
+		// Wait for click ??? KD hint dismisses on any input.
+		while (!shouldQuit()) {
+			Common::Event ev;
+			bool advance = false;
+			while (g_system->getEventManager()->pollEvent(ev)) {
+				if (ev.type == Common::EVENT_QUIT ||
+					ev.type == Common::EVENT_RETURN_TO_LAUNCHER ||
+					ev.type == Common::EVENT_LBUTTONDOWN ||
+					ev.type == Common::EVENT_KEYDOWN) {
+					advance = true;
+					break;
+				}
+			}
+			if (advance)
+				break;
+			g_system->updateScreen();
+			g_system->delayMillis(10);
+		}
+		return;
+	}
+
 	// Mirrors `_KDHelp @ 1560:010a`. The original walks the first two
 	// entries of `_AChain` (the puzzle's required-clue chain ??? the
 	// "spine" of evidence the player must collect):
diff --git a/engines/eem/mystery.cpp b/engines/eem/mystery.cpp
index 98f48a1d78e..781d81895a0 100644
--- a/engines/eem/mystery.cpp
+++ b/engines/eem/mystery.cpp
@@ -359,9 +359,14 @@ uint16 Mystery::noteIndexCount() const {
 	if (!isLoaded())
 		return 0;
 	// NoteIndex runs from _noteOffset to the start of GalleryData.
+	// CD entries are 4 bytes (`u16 textOff; u16 points`); floppy
+	// entries are 7 bytes (`u16 ?; u16 jakeOff; u16 jennyOff; u8
+	// score`) ??? verified at `FUN_22dc_05c8 @ 22dc:0843` reading
+	// `*(int *)(notes + idx*7 + 2)` (Jake) / `+4` (Jenny).
 	if (_galleryOffset <= _noteOffset)
 		return 0;
-	return (uint16)((_galleryOffset - _noteOffset) / 4);
+	const uint stride = _isFloppy ? 7 : 4;
+	return (uint16)((_galleryOffset - _noteOffset) / stride);
 }
 
 const byte *Mystery::kdTextIndex() const {
diff --git a/engines/eem/ui.cpp b/engines/eem/ui.cpp
index a7589a566f6..42321a6c626 100644
--- a/engines/eem/ui.cpp
+++ b/engines/eem/ui.cpp
@@ -1934,17 +1934,41 @@ void EEMEngine::drawNotebookFrame(int &page) {
 	int clueCursor = 0;
 	Common::Array<int> pageStarts;
 	pageStarts.push_back(0);
+	// Floppy NoteIndex entries are 7 bytes (`u16 ?; u16 jakeOff; u16
+	// jennyOff; u8 score`) with ABSOLUTE byte offsets into the mystery
+	// blob, while CD entries are 4 bytes with offsets relative to the
+	// TextBlock at header[+0xc]. Resolve the right text for the active
+	// partner / variant once per render.
+	const bool floppyNb = isFloppy();
+	const byte *bufBase = _mystery.blobAt(0);
+	const uint32 mysSz  = _mystery.dataSize();
+	auto noteText = [&](uint clueId) -> Common::String {
+		if (!ni || clueId >= niCount)
+			return Common::String();
+		if (floppyNb && bufBase) {
+			const uint stride = 7;
+			const uint16 textOff = (_partner == 0)
+				? READ_LE_UINT16(ni + clueId * stride + 2)
+				: READ_LE_UINT16(ni + clueId * stride + 4);
+			if (textOff == 0 || textOff >= mysSz)
+				return Common::String();
+			const char *p = (const char *)(bufBase + textOff);
+			uint32 len = 0;
+			while (textOff + len < mysSz && p[len] != 0)
+				len++;
+			return parseString(Common::String(p, len),
+							   _playerName, _partner);
+		}
+		const uint16 textOff = READ_LE_UINT16(ni + clueId * 4);
+		return parseString(_mystery.textAt(textOff),
+						   _playerName, _partner);
+	};
 	{
 		const int lineH = _font.getFontHeight() + 1;
 		int y = kRectY;
 		while (clueCursor < (int)found.size()) {
 			const uint clueId = found[clueCursor];
-			Common::String txt;
-			if (ni && clueId < niCount) {
-				const uint16 textOff = READ_LE_UINT16(ni + clueId * 4);
-				txt = parseString(_mystery.textAt(textOff),
-								  _playerName, _partner);
-			}
+			Common::String txt = noteText(clueId);
 			// Measure height by wrapping the text without drawing.
 			Common::Array<Common::String> wrapped;
 			_font.wordWrapText(txt, kRectW, wrapped);
@@ -1976,12 +2000,7 @@ void EEMEngine::drawNotebookFrame(int &page) {
 	int y = kRectY;
 	for (int i = startClue; i < endClue; i++) {
 		const uint clueId = found[i];
-		Common::String txt;
-		if (ni && clueId < niCount) {
-			const uint16 textOff = READ_LE_UINT16(ni + clueId * 4);
-			txt = parseString(_mystery.textAt(textOff),
-							  _playerName, _partner);
-		}
+		Common::String txt = noteText(clueId);
 		if (txt.empty())
 			txt = Common::String::format("clue %u", clueId);
 		// Per `_DrawNotes @ 161e:01d0`: text uses


Commit: 4992f3c7045f98da9019e034cdab0719746cbeb8
    https://github.com/scummvm/scummvm/commit/4992f3c7045f98da9019e034cdab0719746cbeb8
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:20:52+02:00

Commit Message:
EEM: text pagination for some conversations

Changed paths:
    engines/eem/clues.cpp
    engines/eem/font.cpp
    engines/eem/font.h


diff --git a/engines/eem/clues.cpp b/engines/eem/clues.cpp
index d53fb7f57ae..645d6086112 100644
--- a/engines/eem/clues.cpp
+++ b/engines/eem/clues.cpp
@@ -589,6 +589,19 @@ Common::String EEMEngine::parseString(const Common::String &raw,
 			return out;
 		case '\r':
 			break;
+		case '^':
+			// `_WordWrap @ 1b03:0456` treats `^` as a forced line
+			// break (sets `cur_width = max_width`, forcing the next
+			// loop turn to wrap at the previous space and skip the
+			// `^` itself). Without this conversion the caret falls
+			// through the default case and renders as a literal,
+			// pushing the line past the balloon's visual edge ??? the
+			// "bubbles aren't large enough" symptom. Promote it to
+			// `\n` so ScummVM's `Font::wordWrapText` (which honours
+			// embedded newlines) picks the same break point the
+			// original engine did.
+			out += '\n';
+			break;
 		default:
 			out += (char)c;
 			break;
@@ -939,6 +952,39 @@ void EEMEngine::displayFloppyDialogRecords(const byte *rec, uint count) {
 		}
 	}
 
+	const uint32 dsz       = _mystery.dataSize();
+	const uint32 notesBase = (uint32)(notes - bufBase);
+
+	auto waitForClick = [&]() -> bool {
+		// Drain pending events first so a previous keystroke's tail
+		// doesn't auto-advance the new page.
+		Common::Event drain;
+		while (g_system->getEventManager()->pollEvent(drain)) {}
+		const uint32 minVisibleMs = 250;
+		const uint32 startedAt = g_system->getMillis();
+		while (!shouldQuit()) {
+			Common::Event ev;
+			while (g_system->getEventManager()->pollEvent(ev)) {
+				if (ev.type == Common::EVENT_QUIT ||
+					ev.type == Common::EVENT_RETURN_TO_LAUNCHER)
+					return true;  // skip
+				if (g_system->getMillis() - startedAt < minVisibleMs)
+					continue;
+				if (ev.type == Common::EVENT_KEYDOWN &&
+					ev.kbd.keycode == Common::KEYCODE_ESCAPE) {
+					interruptAudio();
+					return true;  // skip
+				}
+				if (ev.type == Common::EVENT_LBUTTONDOWN ||
+					ev.type == Common::EVENT_KEYDOWN)
+					return false;  // advance one page
+			}
+			g_system->updateScreen();
+			g_system->delayMillis(10);
+		}
+		return true;
+	};
+
 	for (uint i = 0; i < count && !shouldQuit(); i++) {
 		const uint16 picID    = READ_LE_UINT16(rec + 0);
 		const uint16 picX     = READ_LE_UINT16(rec + 2);
@@ -948,102 +994,22 @@ void EEMEngine::displayFloppyDialogRecords(const byte *rec, uint count) {
 		const uint8  ballY    = rec[8];
 		const uint8  textCount= rec[10];
 
-		// Per-record voice playback. `FUN_22dc_05c8 @ 22dc:05c8`:
-		//   if ((rec[+9] & 0x80) && voiceOption && voiceAvail) {
-		//       slot = rec[+9] & 0x7f;
-		//       _LoadSoundName_Floppy(slot); _PlayVoc(...);
-		//   }
-		// The slot indexes the per-partner table at `2608:0f0e` /
-		// `2608:0f76` (= our `playFloppyVoiceSlot`). Sound is started
-		// BEFORE the bubble renders so it plays while the player reads.
+		// Per-record voice (byte 9 high bit) ??? see comment in original
+		// header.
 		if ((rec[9] & 0x80) != 0 && _audio) {
 			const uint slot = rec[9] & 0x7f;
 			_audio->playFloppyVoiceSlot(slot, _partner);
 		}
-
-		// Build the full text by concatenating each note's per-partner
-		// text string (parsed for `%s`-style placeholders). Bounds-
-		// check every offset against the mystery blob; if any byte
-		// looks out of range we just stop appending ??? corrupt offsets
-		// from a misparsed record would otherwise dereference past the
-		// buffer and SIGBUS inside `strlen`.
-		//
-		// Each text index also flags `_cluesFound[idx]` so the PDA /
-		// notebook ("DrawNotes") later renders the clue. Mirrors
-		// `FUN_22dc_05c8 @ 22dc:091a`:
-		//   ((undefined1 *)&DAT_28da_3c08)[textIdx & 0x7f] = 1;
-		// `DAT_28da_3c08` is the floppy `TextSeen` array; we reuse the
-		// `_cluesFound` flag store since both index 0..127 by note id.
-		const uint32 dsz       = _mystery.dataSize();
-		const uint32 notesBase = (uint32)(notes - bufBase);
-		Common::String raw;
-		for (uint t = 0; t < textCount; t++) {
-			const uint8  idx        = rec[11 + t] & 0x7f;
-			if (idx < Mystery::kCluesFoundCap)
-				_mystery._cluesFound[idx] = 1;
-			const uint32 noteAbs    = notesBase + (uint32)idx * 7;
-			if (noteAbs + 6 > dsz)
-				break;
-			const uint16 textOff = (_partner == 0)
-				? READ_LE_UINT16(notes + idx * 7 + 2)
-				: READ_LE_UINT16(notes + idx * 7 + 4);
-			if (textOff >= dsz)
-				break;
-			const char *line = (const char *)(bufBase + textOff);
-			// Find the NUL terminator within the blob ??? refuse to
-			// strlen past the end.
-			uint32 lineLen = 0;
-			while (textOff + lineLen < dsz && line[lineLen] != 0)
-				lineLen++;
-			if (textOff + lineLen >= dsz)
-				break;
-			if (t > 0)
-				raw += ' ';
-			raw += Common::String(line, lineLen);
-		}
-
-		// Suspect/gallery side effect ??? `FUN_22dc_05c8 @ 22dc:08eb`:
-		//   if ((rec[+9] & 0x80) == 0 && rec[+9] != 0)
-		//       *(u16 *)(0x5d20 + table[0x2d65 + rec[+9]] * 2) = 1;
-		// Byte 9 doubles as the voice slot (when high bit set) or as a
-		// suspect identifier (low 7 bits, when high bit clear). The
-		// 0x5d20 array is the per-mystery "suspect found in gallery"
-		// flag table that `FUN_154e_0045` reads at gallery render time.
-		// We don't model the per-mystery shuffle table at 0x2d65
-		// (`_NewOrder` is set to identity in `_ReadMystery_Floppy` for
-		// our port), so byte9-1 indexes `_inGallery` directly.
+		// Suspect-found side effect for byte 9 with high bit clear.
 		const uint8 b9 = rec[9];
 		if ((b9 & 0x80) == 0 && b9 != 0) {
 			const uint slot = (uint)b9 - 1;
 			if (slot < Mystery::kGalleryCap)
 				_mystery._inGallery[slot] = 1;
 		}
-		const Common::String text = parseString(raw, _playerName, _partner);
-
-		// Restore briefing BG before drawing this bubble.
-		g_system->copyRectToScreen(bg.getPixels(), bg.pitch, 0, 0, 320, 200);
-
-		// Optional character portrait.
-		if (picID != 0 && picID != 0xFFFF) {
-			Picture pic;
-			if (_picsArchive.getPicture(picID, pic))
-				blitAt(pic, picX, picY);
-		}
-
-		// Compose balloon + text on a scratch surface.
-		Graphics::ManagedSurface scratch(320, 200,
-			Graphics::PixelFormat::createFormatCLUT8());
-		{
-			Graphics::Surface *screen = g_system->lockScreen();
-			if (screen) {
-				for (int row = 0; row < 200; row++) {
-					memcpy((byte *)scratch.getBasePtr(0, row),
-						   (const byte *)screen->getBasePtr(0, row), 320);
-				}
-				g_system->unlockScreen();
-			}
-		}
 
+		// Pre-load balloon picture + insets once per record (constant
+		// across all paginated text indices).
 		Picture balloon;
 		const uint16 balloonId  = balByte & 0x7F;
 		const bool   flipBall   = (balByte & 0x80) != 0;
@@ -1053,81 +1019,134 @@ void EEMEngine::displayFloppyDialogRecords(const byte *rec, uint count) {
 		uint16 textWidth = 142;
 		uint16 textXIns  = 6;
 		uint16 textYIns  = 4;
-		if (haveBalloon) {
-			const int bw = MIN<int>(balloon.surface.w, 320 - ballX);
-			const int bh = MIN<int>(balloon.surface.h, 200 - ballY);
-			const byte transp = (byte)(balloon.flags >> 8);
-			for (int row = 0; row < bh; row++) {
-				const byte *src =
-					(const byte *)balloon.surface.getBasePtr(0, row);
-				byte *dst =
-					(byte *)scratch.getBasePtr(ballX, ballY + row);
-				for (int col = 0; col < bw; col++) {
-					const int srcCol = flipBall
-						? (balloon.surface.w - 1 - col) : col;
-					const byte px = src[srcCol];
-					if (px != transp)
-						dst[col] = px;
-				}
-			}
+		if (haveBalloon)
 			getBalloonInsets(balloonId, textXIns, textYIns, textWidth);
-		}
-
 		const int textX = ballX + textXIns;
-		const int textY = ballY + textYIns;
-		_font.drawWordWrapped(&scratch, textX, textY,
-			MAX<int>(8, (int)textWidth), text, 0);
+		const int balloonH = haveBalloon ? balloon.surface.h : 200;
+		const int lineH    = _font.getFontHeight() + 1;
+
+		// Pagination state ??? `FUN_22dc_05c8`'s text-idx loop uses
+		// `local_1c` (set from the PREVIOUS text's flag bit) to decide
+		// between "fresh page" (redraw balloon, restart Y at top) and
+		// "continuation" (append below previous lines). We mirror that
+		// state machine so multi-text records render the same way the
+		// original does ??? without it our impl concatenates every text
+		// idx into ONE balloon and the result spills out the bottom
+		// (the user's "bubbles aren't large enough" screenshot).
+		bool firstPage  = true;
+		int  cursorY    = ballY + textYIns;
+		bool skipAll    = false;
+
+		for (uint t = 0; t < textCount && !shouldQuit() && !skipAll; t++) {
+			const uint8 idxByte = rec[11 + t];
+			const uint8 idx     = idxByte & 0x7f;
+			if (idx < Mystery::kCluesFoundCap)
+				_mystery._cluesFound[idx] = 1;
+			const uint32 noteAbs = notesBase + (uint32)idx * 7;
+			if (noteAbs + 6 > dsz)
+				break;
+			const uint16 textOff = (_partner == 0)
+				? READ_LE_UINT16(notes + idx * 7 + 2)
+				: READ_LE_UINT16(notes + idx * 7 + 4);
+			if (textOff >= dsz)
+				break;
+			const char *linePtr = (const char *)(bufBase + textOff);
+			uint32 lineLen = 0;
+			while (textOff + lineLen < dsz && linePtr[lineLen] != 0)
+				lineLen++;
+			Common::String raw(linePtr, lineLen);
+			const Common::String text =
+				parseString(raw, _playerName, _partner);
 
-		g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
-								   0, 0, 320, 200);
-		g_system->updateScreen();
+			// Render this text page.
+			Graphics::ManagedSurface scratch(320, 200,
+				Graphics::PixelFormat::createFormatCLUT8());
+			scratch.simpleBlitFrom(*bg.surfacePtr());
+
+			if (firstPage) {
+				// Optional character portrait ??? only draws once per
+				// fresh page (matches the original which only redraws
+				// the balloon on `local_1c == 0`).
+				if (picID != 0 && picID != 0xFFFF) {
+					Picture pic;
+					if (_picsArchive.getPicture(picID, pic)) {
+						const byte transpC = (byte)(pic.flags >> 8);
+						const int pw = MIN<int>(pic.surface.w, 320 - picX);
+						const int ph = MIN<int>(pic.surface.h, 200 - picY);
+						for (int row = 0; row < ph; row++) {
+							const byte *src = (const byte *)
+								pic.surface.getBasePtr(0, row);
+							byte *dst = (byte *)
+								scratch.getBasePtr(picX, picY + row);
+							for (int col = 0; col < pw; col++) {
+								if (src[col] != transpC)
+									dst[col] = src[col];
+							}
+						}
+					}
+				}
+				if (haveBalloon) {
+					const int bw = MIN<int>(balloon.surface.w, 320 - ballX);
+					const int bh = MIN<int>(balloonH, 200 - ballY);
+					const byte transp = (byte)(balloon.flags >> 8);
+					for (int row = 0; row < bh; row++) {
+						const byte *src = (const byte *)
+							balloon.surface.getBasePtr(0, row);
+						byte *dst = (byte *)
+							scratch.getBasePtr(ballX, ballY + row);
+						for (int col = 0; col < bw; col++) {
+							const int srcCol = flipBall
+								? (balloon.surface.w - 1 - col)
+								: col;
+							const byte px = src[srcCol];
+							if (px != transp)
+								dst[col] = px;
+						}
+					}
+				}
+				cursorY = ballY + textYIns;
+			}
 
-		// Drain pending events from the previous bubble (or upstream
-		// animation skip) so a single Enter press doesn't burst-advance
-		// through multiple bubbles. Without this, key-repeat or stacked
-		// KEYUP/KEYDOWN events from one keystroke could roll past
-		// several records before the user could even read them.
-		{
-			Common::Event drain;
-			while (g_system->getEventManager()->pollEvent(drain)) {}
-		}
-		// Minimum visible time per bubble ??? guards against accidental
-		// rapid-fire advances from accumulated input.
-		const uint32 minVisibleMs = 250;
-		const uint32 startedAt = g_system->getMillis();
+			// Wrap text into lines and draw each at cursorY.
+			Common::Array<Common::String> lines;
+			_font.wordWrapText(text, MAX<int>(8, (int)textWidth), lines);
+			for (uint l = 0; l < lines.size(); l++) {
+				_font.drawString(&scratch, lines[l], textX,
+								  cursorY + (int)l * lineH,
+								  MAX<int>(8, (int)textWidth), 0);
+			}
+			cursorY += (int)lines.size() * lineH;
 
-		// Wait for click / key. ESC skips the rest of the briefing.
-		// Inside the loop we ignore input until at least `minVisibleMs`
-		// has elapsed since the bubble was drawn ??? combined with the
-		// pre-loop event drain, this prevents one keystroke (or its
-		// auto-repeat tail) from advancing several bubbles back to back.
-		bool advance = false;
-		bool skipAll = false;
-		while (!advance && !shouldQuit()) {
-			Common::Event ev;
-			while (g_system->getEventManager()->pollEvent(ev)) {
-				if (ev.type == Common::EVENT_QUIT ||
-					ev.type == Common::EVENT_RETURN_TO_LAUNCHER) {
-					advance = true;
-					break;
+			g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
+									   0, 0, 320, 200);
+			g_system->updateScreen();
+
+			// Decide pagination for the NEXT text idx based on THIS
+			// text's high bit.
+			const bool textHighBit = (idxByte & 0x80) != 0;
+			const bool isLastText  = (t + 1 == textCount);
+
+			if (!isLastText) {
+				if (textHighBit) {
+					// Continuation flag ??? next text appends below
+					// without waiting.
+					firstPage = false;
+				} else {
+					// New page next: wait for click, then redraw
+					// balloon for the next text.
+					if (waitForClick()) {
+						skipAll = true;
+						break;
+					}
+					firstPage = true;
 				}
-				if (g_system->getMillis() - startedAt < minVisibleMs)
-					continue;
-				if (ev.type == Common::EVENT_KEYDOWN &&
-					ev.kbd.keycode == Common::KEYCODE_ESCAPE) {
-					advance = true;
+			} else {
+				// Last text in record ??? wait for the user's click
+				// before moving on (mirrors the caller's
+				// `FUN_16e2_1a7f()` after every `FUN_22dc_05c8` call).
+				if (waitForClick())
 					skipAll = true;
-					interruptAudio();
-					break;
-				}
-				if (ev.type == Common::EVENT_LBUTTONDOWN ||
-					ev.type == Common::EVENT_KEYDOWN) {
-					advance = true;
-					break;
-				}
 			}
-			g_system->updateScreen();
-			g_system->delayMillis(10);
 		}
 		if (skipAll)
 			return;
diff --git a/engines/eem/font.cpp b/engines/eem/font.cpp
index 4d00ab79fb1..c8c42360550 100644
--- a/engines/eem/font.cpp
+++ b/engines/eem/font.cpp
@@ -80,6 +80,7 @@ bool EEMFont::load(const Common::Path &path) {
 	}
 	_glyphs.resize(numChars);
 	_maxHeight = _maxWidth = 0;
+	_lineHeight = 0;
 
 	for (uint i = 0; i < numChars; i++) {
 		FontGlyph &g = _glyphs[i];
@@ -99,6 +100,19 @@ bool EEMFont::load(const Common::Path &path) {
 			_maxWidth = g.widthBits;
 	}
 
+	// `_LoadFont @ 1b03:0220` sets the line stride to the FIRST
+	// glyph's height (= the space character) ??? `DAT_28da_30ca =
+	// DAT_28da_30ce` (the first byte of glyph 0 = its height). Tall
+	// descender glyphs ('g', 'j', 'p', 'q', 'y') hang into the next
+	// row by design, which is how the original engine keeps balloon
+	// text dense enough to fit. Using `_maxHeight` (the descender-
+	// inflated value) added 2???3 px per line and made every multi-
+	// line bubble overflow its graphic vertically ??? verbatim user-
+	// reported "bubbles aren't large enough" symptom.
+	_lineHeight = !_glyphs.empty() ? _glyphs[0].height : _maxHeight;
+	if (_lineHeight == 0)
+		_lineHeight = _maxHeight;
+
 	debugC(1, kDebugGfx, "Font %s loaded: %u glyphs, max %ux%u",
 		   path.toString().c_str(), numChars, _maxWidth, _maxHeight);
 	return true;
diff --git a/engines/eem/font.h b/engines/eem/font.h
index 6ccfd6d4946..ae5a0638d7b 100644
--- a/engines/eem/font.h
+++ b/engines/eem/font.h
@@ -61,7 +61,9 @@ public:
 	bool isLoaded() const { return !_glyphs.empty(); }
 
 	// --- Graphics::Font overrides ---
-	int getFontHeight() const override { return _maxHeight; }
+	int getFontHeight() const override {
+		return _lineHeight ? _lineHeight : _maxHeight;
+	}
 	int getMaxCharWidth() const override { return _maxWidth; }
 	int getCharWidth(uint32 chr) const override;
 	void drawChar(Graphics::Surface *dst, uint32 chr, int x, int y,
@@ -76,8 +78,9 @@ public:
 
 private:
 	Common::Array<FontGlyph> _glyphs;
-	uint16 _maxHeight = 0;
-	uint16 _maxWidth  = 0;
+	uint16 _maxHeight  = 0;
+	uint16 _maxWidth   = 0;
+	uint16 _lineHeight = 0;  ///< First glyph height (= original line stride)
 };
 
 } // End of namespace EEM


Commit: fe87003b43856887061690a2c8fd6e6d919bf733
    https://github.com/scummvm/scummvm/commit/fe87003b43856887061690a2c8fd6e6d919bf733
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:20:53+02:00

Commit Message:
EEM: draw enter indicators for conversations in floppy version

Changed paths:
    engines/eem/clues.cpp
    engines/eem/eem.h
    engines/eem/graphics.cpp


diff --git a/engines/eem/clues.cpp b/engines/eem/clues.cpp
index 645d6086112..2034ed40127 100644
--- a/engines/eem/clues.cpp
+++ b/engines/eem/clues.cpp
@@ -913,7 +913,8 @@ void EEMEngine::displayClue(const byte *clueBlock) {
 	}
 }
 
-void EEMEngine::displayFloppyDialogRecords(const byte *rec, uint count) {
+void EEMEngine::displayFloppyDialogRecords(const byte *rec, uint count,
+											uint lastIndicator) {
 	// Render `count` consecutive floppy dialog records starting at
 	// `rec`. Per `FUN_22dc_05c8 @ 22dc:05c8`, each record is:
 	//   u16 picID    @ +0     (character portrait, 0 = skip pic)
@@ -1117,35 +1118,68 @@ void EEMEngine::displayFloppyDialogRecords(const byte *rec, uint count) {
 			}
 			cursorY += (int)lines.size() * lineH;
 
-			g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
-									   0, 0, 320, 200);
-			g_system->updateScreen();
-
 			// Decide pagination for the NEXT text idx based on THIS
 			// text's high bit.
 			const bool textHighBit = (idxByte & 0x80) != 0;
 			const bool isLastText  = (t + 1 == textCount);
-
+			const bool isLastRec   = (i + 1 == count);
+
+			// Stamp the "click to continue" indicator (PIC 0xa0
+			// "more" arrow / PIC 0xa1 end indicator) before
+			// flipping to wait. Mirrors `_DisplayHotspotClue_Floppy
+			// @ 22dc:08aa` (mid-page) and `@ 22dc:08c0`
+			// (end-of-record). We skip drawing it on the very last
+			// click of the very last record when the caller passes
+			// `lastIndicator == 0` ??? that's the original `param_2 ==
+			// 0` "no indicator" case.
+			bool waitNeeded   = false;
+			bool drawArrow    = false;
+			bool useEndPic    = false;
 			if (!isLastText) {
 				if (textHighBit) {
-					// Continuation flag ??? next text appends below
-					// without waiting.
+					// continuation ??? no wait, no indicator
 					firstPage = false;
 				} else {
-					// New page next: wait for click, then redraw
-					// balloon for the next text.
-					if (waitForClick()) {
-						skipAll = true;
-						break;
-					}
-					firstPage = true;
+					waitNeeded = true;
+					drawArrow  = true;          // PIC 0xa0
+					useEndPic  = false;
 				}
 			} else {
-				// Last text in record ??? wait for the user's click
-				// before moving on (mirrors the caller's
-				// `FUN_16e2_1a7f()` after every `FUN_22dc_05c8` call).
-				if (waitForClick())
+				// Last text in this record.
+				waitNeeded = true;
+				if (!isLastRec) {
+					// More records follow ??? original passes
+					// `param_2 = 1` ??? PIC 0xa0.
+					drawArrow = true;
+					useEndPic = false;
+				} else {
+					// Last record. Use caller-supplied indicator.
+					if (lastIndicator == 1) {
+						drawArrow = true;
+						useEndPic = false;
+					} else if (lastIndicator == 2) {
+						drawArrow = true;
+						useEndPic = true;       // PIC 0xa1
+					}
+				}
+			}
+
+			if (drawArrow) {
+				drawFloppyBubbleIndicator(scratch, balloonId, ballX,
+										   ballY, useEndPic);
+			}
+
+			g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
+									   0, 0, 320, 200);
+			g_system->updateScreen();
+
+			if (waitNeeded) {
+				if (waitForClick()) {
 					skipAll = true;
+					break;
+				}
+				if (!isLastText && !textHighBit)
+					firstPage = true;
 			}
 		}
 		if (skipAll)
@@ -1234,11 +1268,23 @@ void EEMEngine::displayFloppyHotspotDialog(uint siteNum, uint hotIdx) {
 		}
 	}
 	const byte *mainRec = bufBase + off;
-	displayFloppyDialogRecords(mainRec, 1);
 	const uint mainLen = 11u + (uint)mainRec[10];
-	if (off + mainLen >= _mystery.dataSize())
-		return;
-	const uint contCount = (uint)(bufBase[off + mainLen] & 0x7F);
+	uint contCount = 0;
+	uint contFlagsByte = 0;
+	if (off + mainLen < _mystery.dataSize()) {
+		contFlagsByte = bufBase[off + mainLen];
+		contCount = contFlagsByte & 0x7F;
+	}
+	// `_HandleHotspotClick_Floppy @ 1652:00e6` derives the main
+	// record's `param_2` from the continuation byte:
+	//   contFlagsByte == 0  ??? 0 (no indicator, only one record)
+	//   high bit set         ??? 1 (PIC 0xa0, "more" arrow)
+	//   low 7 bits non-zero  ??? 2 (PIC 0xa1, alternate end)
+	uint mainIndicator = 0;
+	if (contFlagsByte != 0) {
+		mainIndicator = (contFlagsByte & 0x80) ? 1 : 2;
+	}
+	displayFloppyDialogRecords(mainRec, 1, mainIndicator);
 	if (contCount == 0)
 		return;
 	const uint32 contOff = off + mainLen + 1;
@@ -1251,7 +1297,13 @@ void EEMEngine::displayFloppyHotspotDialog(uint siteNum, uint hotIdx) {
 	g_system->copyRectToScreen(siteBG.getPixels(), siteBG.pitch,
 							   0, 0, 320, 200);
 	g_system->updateScreen();
-	displayFloppyDialogRecords(bufBase + contOff, contCount);
+	// Continuation chain: `_DisplayDialogContinuations_Floppy @
+	// 1652:006c` passes `param_2 + -1 != 0` (= 1 if more records
+	// follow, 0 on the last). Our renderer maps that automatically
+	// because mid-batch records always get PIC 0xa0; the last record
+	// uses our `lastIndicator` argument (= 0, no indicator on the
+	// final continuation).
+	displayFloppyDialogRecords(bufBase + contOff, contCount, 0);
 }
 
 bool EEMEngine::areYouSure() {
diff --git a/engines/eem/eem.h b/engines/eem/eem.h
index 0b926e5655b..4be4198ad29 100644
--- a/engines/eem/eem.h
+++ b/engines/eem/eem.h
@@ -266,11 +266,24 @@ public:
 	void setPartnerEraseBg(const Graphics::ManagedSurface *bg);
 
 	/// Look up balloon-text-inset metadata. Mirrors the 52-entry table at
-	/// `29be:0875`, indexed by `(bubNum & 0x7F)`. 10 bytes per entry; only
-	/// the first 3 fields (x inset, y inset, text width) are used for
-	/// rendering. Returns false if `bubNum` is outside the table.
+	/// `29be:0875` (CD) / `2608:05f9` (floppy), indexed by
+	/// `(bubNum & 0x7F)`. 10 bytes per entry; the first 3 fields
+	/// (x inset, y inset, text width) are used for text wrap, the last
+	/// 2 (indicator dX/dY) by `drawFloppyBubbleIndicator`. Returns
+	/// false if `bubNum` is outside the table.
 	bool getBalloonInsets(uint16 bubNum, uint16 &xInset, uint16 &yInset,
 						  uint16 &textW) const;
+	bool getBalloonIndicatorPos(uint16 bubNum, uint16 &dx,
+								 uint16 &dy) const;
+
+	/// Stamp the floppy "click to continue" indicator (PIC 0xa0 for
+	/// `endIndicator==false`, PIC 0xa1 otherwise) onto @p dst at the
+	/// position derived from the balloon-inset table. Mirrors
+	/// `FUN_22dc_05c8 @ 22dc:08aa` (mid-page) and `@ 22dc:08c0`
+	/// (end-of-record).
+	void drawFloppyBubbleIndicator(Graphics::ManagedSurface &dst,
+								   uint16 bubNum, int ballX, int ballY,
+								   bool endIndicator);
 
 	/// "Are you sure?" yes/no dialog. Mirrors `_AreYouSure` @ 1a35:0a5c.
 	/// Returns true if the user picked YES.
@@ -449,8 +462,13 @@ private:
 	/// Render `count` consecutive floppy dialog records starting at
 	/// `rec`. Shared between briefing and hotspot click handlers since
 	/// the original engine uses the same `FUN_22dc_05c8 @ 22dc:05c8`
-	/// renderer in both contexts.
-	void displayFloppyDialogRecords(const byte *rec, uint count);
+	/// renderer in both contexts. `lastIndicator` is the `param_2`
+	/// equivalent for the LAST record in the batch ??? 0 = no
+	/// indicator, 1 = PIC 0xa0 (red "more" arrow), 2 = PIC 0xa1
+	/// (alternate end indicator). Records before the last always get
+	/// PIC 0xa0.
+	void displayFloppyDialogRecords(const byte *rec, uint count,
+									 uint lastIndicator = 0);
 
 public:
 	/// Mirrors `_StartTravelMusic @ 20a2:0595`. Picks `MUS%05d.XMI`
diff --git a/engines/eem/graphics.cpp b/engines/eem/graphics.cpp
index 5af91578ac5..4b9eae7cbf8 100644
--- a/engines/eem/graphics.cpp
+++ b/engines/eem/graphics.cpp
@@ -51,29 +51,38 @@ const uint16 kHelpPics[][2] = {
 	{ 0x0192, 0x01b1 },
 };
 
-// 52-entry, 10-bytes-each balloon-metadata table at `29be:0875`.
-// Used at 1df2:0aef-0af9 (accuse hint) and `_DisplayClue` to position
-// `_WordWrap` text inside the balloon. Only +0/+2/+4 are read by
-// `getBalloonInsets`:
-//   +0..1 = text X inset, +2..3 = Y inset, +4..5 = max wrap width
-// (+6/+8 = balloon h / tail offset, both unused for text layout).
-struct BalloonInsets { uint16 x; uint16 y; uint16 w; };
+// 52-entry, 10-bytes-each balloon-metadata table at `29be:0875` (CD)
+// / `2608:05f9` (floppy). Used by `_DisplayClue` to position
+// `_WordWrap` text inside the balloon AND to position the
+// "click to continue" indicator drawn by
+// `_DisplayHotspotClue_Floppy @ 22dc:05c8`. Layout per entry:
+//   +0..1 = text X inset, +2..3 = text Y inset
+//   +4..5 = wrap width
+//   +6..7 = "more / end" indicator X within the balloon
+//   +8..9 = "more / end" indicator Y within the balloon
+struct BalloonInsets {
+	uint16 x;
+	uint16 y;
+	uint16 w;
+	uint16 indDX;
+	uint16 indDY;
+};
 const BalloonInsets kBalloonInsetTable[] = {
-	{ 6, 4, 142 }, { 6, 4, 142 }, { 6, 4, 142 }, { 6, 4, 142 },
-	{ 6, 4, 142 }, { 6, 4, 142 }, { 6, 4, 142 },
-	{ 6, 4, 224 }, { 6, 4, 224 }, { 6, 4, 224 }, { 6, 4, 224 },
-	{ 6, 4, 224 }, { 6, 4, 224 }, { 6, 4, 224 },
-	{ 6, 4, 291 }, { 6, 4, 291 }, { 6, 4, 291 }, { 6, 4, 291 },
-	{ 6, 4, 291 }, { 6, 4, 291 }, { 6, 4, 291 },
-	{ 5, 4, 155 }, { 5, 4, 155 }, { 5, 4, 155 }, { 5, 4, 155 },
-	{ 5, 4, 155 }, { 5, 4, 155 }, { 5, 4, 155 },
-	{ 5, 4, 237 }, { 5, 4, 237 }, { 5, 4, 237 }, { 5, 4, 237 },
-	{ 5, 4, 237 }, { 5, 4, 237 }, { 5, 4, 237 },
-	{ 3, 4, 155 }, { 3, 4, 155 }, { 3, 4, 155 }, { 3, 4, 155 },
-	{ 3, 4, 155 }, { 3, 4, 155 }, { 3, 4, 155 },
-	{ 5, 4, 238 }, { 5, 4, 238 }, { 5, 4, 238 }, { 5, 4, 238 },
-	{ 5, 4, 238 }, { 5, 4, 238 }, { 5, 4, 238 },
-	{ 5, 8, 158 }, { 5, 8, 176 }, { 8, 7, 142 }
+	{ 6, 4, 142, 150,  6 }, { 6, 4, 142, 150, 14 }, { 6, 4, 142, 150, 22 }, { 6, 4, 142, 150, 40 },
+	{ 6, 4, 142, 150, 50 }, { 6, 4, 142, 150, 70 }, { 6, 4, 142, 150, 86 },
+	{ 6, 4, 224, 233,  6 }, { 6, 4, 224, 233, 14 }, { 6, 4, 224, 233, 23 }, { 6, 4, 224, 233, 41 },
+	{ 6, 4, 224, 233, 59 }, { 6, 4, 224, 233, 68 }, { 6, 4, 224, 233, 77 },
+	{ 6, 4, 291, 300,  6 }, { 6, 4, 291, 300, 14 }, { 6, 4, 291, 300, 23 }, { 6, 4, 291, 300, 32 },
+	{ 6, 4, 291, 300, 50 }, { 6, 4, 291, 300, 69 }, { 6, 4, 291, 300, 77 },
+	{ 5, 4, 155, 150, 14 }, { 5, 4, 155, 150, 25 }, { 5, 4, 155, 150, 35 }, { 5, 4, 155, 150, 52 },
+	{ 5, 4, 155, 150, 61 }, { 5, 4, 155, 150, 78 }, { 5, 4, 155, 150, 81 },
+	{ 5, 4, 237, 233, 15 }, { 5, 4, 237, 233, 24 }, { 5, 4, 237, 233, 42 }, { 5, 4, 237, 233, 50 },
+	{ 5, 4, 237, 233, 69 }, { 5, 4, 237, 233, 80 }, { 5, 4, 237, 233, 80 },
+	{ 3, 4, 155, 150, 15 }, { 3, 4, 155, 150, 24 }, { 3, 4, 155, 150, 34 }, { 3, 4, 155, 150, 40 },
+	{ 3, 4, 155, 150, 61 }, { 3, 4, 155, 150, 82 }, { 3, 4, 155, 150, 88 },
+	{ 5, 4, 238, 232, 16 }, { 5, 4, 238, 232, 25 }, { 5, 4, 238, 232, 34 }, { 5, 4, 238, 232, 51 },
+	{ 5, 4, 238, 232, 66 }, { 5, 4, 238, 232, 71 }, { 5, 4, 238, 232, 95 },
+	{ 5, 8, 158, 160, 45 }, { 5, 8, 176, 176, 50 }, { 8, 7, 142, 142, 71 }
 };
 
 // Floppy KDHelp hotspot-searched check. Mirrors
@@ -569,4 +578,48 @@ bool EEMEngine::getBalloonInsets(uint16 bubNum, uint16 &xInset,
 	return true;
 }
 
+bool EEMEngine::getBalloonIndicatorPos(uint16 bubNum, uint16 &dx,
+										uint16 &dy) const {
+	const uint idx = bubNum & 0x7F;
+	if (idx >= ARRAYSIZE(kBalloonInsetTable))
+		return false;
+	dx = kBalloonInsetTable[idx].indDX;
+	dy = kBalloonInsetTable[idx].indDY;
+	return true;
+}
+
+void EEMEngine::drawFloppyBubbleIndicator(Graphics::ManagedSurface &dst,
+										   uint16 bubNum, int ballX, int ballY,
+										   bool endIndicator) {
+	// Mirrors `_DisplayHotspotClue_Floppy @ 22dc:08c0` (end-of-record)
+	// and `@ 22dc:08aa` (mid-pagination). Both grab a pre-loaded PIC
+	// (`DAT_28da_3034 = PIC 0xa0` for "more pages",
+	//  `DAT_28da_3030 = PIC 0xa1` for "end indicator") and stamp it at
+	// `(ballX + insetTable[bubNum].indDX,
+	//   ballY + insetTable[bubNum].indDY)` via `_AddPicBackground`.
+	uint16 dx = 0;
+	uint16 dy = 0;
+	if (!getBalloonIndicatorPos(bubNum, dx, dy))
+		return;
+	const uint picId = endIndicator ? 0xa1 : 0xa0;
+	Picture pic;
+	if (!_picsArchive.getPicture(picId, pic))
+		return;
+	const int x = ballX + (int)dx;
+	const int y = ballY + (int)dy;
+	const byte transp = (byte)(pic.flags >> 8);
+	const int pw = MIN<int>(pic.surface.w, 320 - x);
+	const int ph = MIN<int>(pic.surface.h, 200 - y);
+	if (x < 0 || y < 0 || pw <= 0 || ph <= 0)
+		return;
+	for (int row = 0; row < ph; row++) {
+		const byte *src = (const byte *)pic.surface.getBasePtr(0, row);
+		byte *out = (byte *)dst.getBasePtr(x, y + row);
+		for (int col = 0; col < pw; col++) {
+			if (src[col] != transp)
+				out[col] = src[col];
+		}
+	}
+}
+
 } // End of namespace EEM


Commit: bfdd2c33ecfb0363422d914db71b1a5ad40a3101
    https://github.com/scummvm/scummvm/commit/bfdd2c33ecfb0363422d914db71b1a5ad40a3101
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:20:53+02:00

Commit Message:
EEM: initial support for spanish release and fixes for the floppy version

Changed paths:
    engines/eem/clues.cpp
    engines/eem/detection.cpp
    engines/eem/eem.cpp
    engines/eem/eem.h
    engines/eem/mystery.cpp
    engines/eem/mystery.h
    engines/eem/ui.cpp


diff --git a/engines/eem/clues.cpp b/engines/eem/clues.cpp
index 2034ed40127..677566620eb 100644
--- a/engines/eem/clues.cpp
+++ b/engines/eem/clues.cpp
@@ -1002,11 +1002,25 @@ void EEMEngine::displayFloppyDialogRecords(const byte *rec, uint count,
 			_audio->playFloppyVoiceSlot(slot, _partner);
 		}
 		// Suspect-found side effect for byte 9 with high bit clear.
+		// Mirrors `_DisplayHotspotClue_Floppy @ 22dc:0908..0922`:
+		//   if ((rec[9] & 0x80) == 0 && rec[9] != 0)
+		//     _InGallery_Floppy[_GalleryShuffleSeed_Floppy[rec[9]]] = 1;
+		// `_GalleryShuffleSeed_Floppy` is a byte array at DS:0x2d65 that
+		// overlaps `_NewOrder_Floppy` at DS:0x2d66 by one byte: i.e.
+		// `_GalleryShuffleSeed[i+1] == _NewOrder[i]` (verified by
+		// comparing the two writes in `_ReadMystery_Floppy @ 22dc:0178`).
+		// So in our terms the mapping is `_inGallery[_newOrder[b9 - 1]]`.
+		// Skipping `_newOrder` (as we did before) drew the right portrait
+		// only when `_newOrder` happened to be identity ??? randomized
+		// shuffles dropped one of the two M0 suspects.
 		const uint8 b9 = rec[9];
 		if ((b9 & 0x80) == 0 && b9 != 0) {
-			const uint slot = (uint)b9 - 1;
-			if (slot < Mystery::kGalleryCap)
-				_mystery._inGallery[slot] = 1;
+			const uint logicalIdx = (uint)b9 - 1;
+			if (logicalIdx < Mystery::kGalleryCap) {
+				const uint8 slot = _mystery._newOrder[logicalIdx];
+				if (slot < Mystery::kGalleryCap)
+					_mystery._inGallery[slot] = 1;
+			}
 		}
 
 		// Pre-load balloon picture + insets once per record (constant
@@ -1332,8 +1346,13 @@ bool EEMEngine::areYouSure() {
 			   (const byte *)saved.getBasePtr(0, row), 320);
 	scratch.fillRect(dlg, 0);
 	scratch.frameRect(dlg, 0xF);
-	_font.drawString(&scratch, "Are you sure you want to quit?", dlg.left + 8, dlg.top + 8, 320, 0xF);
-	_font.drawString(&scratch, "Y - Yes", dlg.left + 16, dlg.top + 36, 320, 0xF);
+	_font.drawString(&scratch,
+		isSpanish() ? "Estas seguro que quieres salir?"
+					: "Are you sure you want to quit?",
+		dlg.left + 8, dlg.top + 8, 320, 0xF);
+	_font.drawString(&scratch,
+		isSpanish() ? "S - Si" : "Y - Yes",
+		dlg.left + 16, dlg.top + 36, 320, 0xF);
 	_font.drawString(&scratch, "N - No", dlg.left + 100, dlg.top + 36, 320, 0xF);
 	g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
 							   0, 0, 320, 200);
@@ -1351,7 +1370,9 @@ bool EEMEngine::areYouSure() {
 				break;
 			}
 			if (ev.type == Common::EVENT_KEYDOWN) {
+				// Spanish prompt is "S - Si" ??? accept both Y and S.
 				if (ev.kbd.keycode == Common::KEYCODE_y ||
+					ev.kbd.keycode == Common::KEYCODE_s ||
 					ev.kbd.keycode == Common::KEYCODE_RETURN) {
 					result = true; decided = true; break;
 				}
diff --git a/engines/eem/detection.cpp b/engines/eem/detection.cpp
index 402a5a89cef..60967493537 100644
--- a/engines/eem/detection.cpp
+++ b/engines/eem/detection.cpp
@@ -52,6 +52,20 @@ const ADGameDescription gameDescriptions[] = {
 		ADGF_NO_FLAGS,
 		GUIO1(GUIO_NONE)
 	},
+	{
+		// Spanish floppy release ??? same EEM.EXE binary as the English
+		// floppy (the engine code is identical), with a localised
+		// PICS.DBD that swaps the embedded English image text for
+		// Spanish equivalents.
+		"eem",
+		"Floppy",
+		AD_ENTRY2s("EEM.EXE",   "692a5e6e7f4516d6e40c1f80cbc1b2cc", 109542,
+				   "PICS.DBD",  "199150e7d612f87477814bc5f4a1967a", 955332),
+		Common::ES_ESP,
+		Common::kPlatformDOS,
+		ADGF_NO_FLAGS,
+		GUIO1(GUIO_NONE)
+	},
 
 	AD_TABLE_END_MARKER
 };
diff --git a/engines/eem/eem.cpp b/engines/eem/eem.cpp
index 23a34e6201f..03a2449d26b 100644
--- a/engines/eem/eem.cpp
+++ b/engines/eem/eem.cpp
@@ -105,6 +105,7 @@ EEMEngine::EEMEngine(OSystem *syst, const ADGameDescription *gameDesc)
 	_variant = (gameDesc && gameDesc->extra &&
 				Common::String(gameDesc->extra).contains("Floppy"))
 				 ? kVariantFloppy : kVariantCD;
+	_language = gameDesc ? gameDesc->language : Common::EN_ANY;
 }
 
 EEMEngine::~EEMEngine() {
diff --git a/engines/eem/eem.h b/engines/eem/eem.h
index 4be4198ad29..e78e44066c3 100644
--- a/engines/eem/eem.h
+++ b/engines/eem/eem.h
@@ -25,6 +25,7 @@
 #define EEM_EEM_H
 
 #include "common/array.h"
+#include "common/language.h"
 #include "common/platform.h"
 #include "common/random.h"
 #include "common/scummsys.h"
@@ -171,6 +172,13 @@ public:
 
 	const ADGameDescription *_gameDescription;
 	Variant _variant = kVariantCD;
+	Common::Language _language = Common::EN_ANY;
+
+	/// True for the Spanish floppy release. Used to swap hardcoded
+	/// English UI strings (menus, name prompt, notebook labels,
+	/// fallback hint copy) for their Spanish equivalents extracted
+	/// from EEM.EXE in `eem-full-game/floppy-es/`.
+	bool isSpanish() const { return _language == Common::ES_ESP; }
 
 	DBDArchive &getPics()    { return _picsArchive; }
 	DBDArchive &getAni()     { return _aniArchive; }
@@ -212,6 +220,7 @@ public:
 	/// Run the accuse flow (pick suspect, evaluate chains, show ending).
 	/// Mirrors `_DoAccuseGallery` @ 1df2:0a31 + `_DisplayEnding` @ 1df2:0548.
 	void doAccuse();
+	void doAccuseFloppy();
 
 	/// Show the accuse-notes screen (PIC 0x1A7, the red "accuse-mode"
 	/// BG with selectable clue list + "N clues" remaining counter).
diff --git a/engines/eem/mystery.cpp b/engines/eem/mystery.cpp
index 781d81895a0..06ce1d53f7b 100644
--- a/engines/eem/mystery.cpp
+++ b/engines/eem/mystery.cpp
@@ -191,6 +191,30 @@ bool Mystery::load(uint num, Common::RandomSource *rng) {
 		_solvedOffset    = _floppySolvedOff;
 		_hintOffset      = _floppyHintBlockOff;
 
+		// Per-mystery runtime state ??? mirror `_ReadMystery_Floppy @
+		// 22dc:0178` zeroing `_TextSeen_Floppy` / `_InGallery_Floppy` and
+		// seeding `_NewOrder_Floppy[0]` (random) + the `+1`-shift loop
+		// that fills the rest. We use the identity mapping `[0..N-1]` so
+		// dialog `byte9` (1-based logical idx) maps to `_inGallery[idx -
+		// 1]` and the gallery render iterates the same indices. Without
+		// this init the floppy load path returns with `_newOrder` left
+		// at whatever `clear()` zeroed it to, so every byte9>0 dialog
+		// resolves through `_newOrder[logicalIdx]==0` and the second
+		// suspect overwrites the first slot.
+		memset(_cluesFound, 0, sizeof(_cluesFound));
+		memset(_noteSelected, 0, sizeof(_noteSelected));
+		memset(_hotSpotsSeen, 0, sizeof(_hotSpotsSeen));
+		memset(_inGallery, 0, sizeof(_inGallery));
+		(void)rng;
+		for (uint i = 0; i < kGalleryCap; i++)
+			_newOrder[i] = (uint8)i;
+		memset(_visitedSite, 0, sizeof(_visitedSite));
+		memset(_onSites, 0, sizeof(_onSites));
+		_sawCOFFSITEs = _sawCONSITEs = _sawHelpHint = _solvedPuzzle = false;
+		_firstTry = true;
+		_searchLocationNumber = _siteNumber = 0xFFFF;
+		_lastSite = 0x1B;
+
 		debugC(1, kDebugMystery,
 			   "Mystery::load(%u) floppy: sites=0x%04x siteIdx=0x%04x "
 			   "notes=0x%04x suspects=0x%04x text=0x%04x kd=0x%04x "
@@ -396,11 +420,46 @@ const byte *Mystery::mapEntry(uint siteNum) const {
 	return _data.data() + off;
 }
 
+const byte *Mystery::floppySuspectEntry(uint suspectIdx) const {
+	// Floppy gallery section: byte[0] = numSuspects, then per suspect a
+	// variable-size record of `5 + nameLen` bytes (verified at
+	// `_DrawGallery_Floppy @ 154e:00b6` advancing `iVar7 += pbVar4[4]
+	// + 5`):
+	//   u16 +0  picID (gallery portrait, BUTTON.DBD entry)
+	//   u16 +2  alibi marker (0xFFFF = guilty; else high byte = index
+	//           into TEXT_BLOCK alibi-offset table at header[+0xc])
+	//   u8  +4  name length
+	//   u8  +5..+5+nameLen-1  name string
+	if (!_isFloppy || !isLoaded())
+		return nullptr;
+	const byte *gd = _data.data() + _galleryOffset;
+	if (gd + 1 > _data.data() + _data.size())
+		return nullptr;
+	const uint8 numSus = gd[0];
+	if (suspectIdx >= numSus)
+		return nullptr;
+	const byte *p = gd + 1;
+	const byte *bufEnd = _data.data() + _data.size();
+	for (uint i = 0; i < suspectIdx; i++) {
+		if (p + 5 > bufEnd)
+			return nullptr;
+		p += 5 + (uint)p[4];
+	}
+	if (p + 5 > bufEnd)
+		return nullptr;
+	return p;
+}
+
 bool Mystery::isGuilty(uint suspectIdx) const {
-	// `_WITCH @ 1df2:089f`: `if (GalleryData[i*0x46 + 0x02] == -1)
+	// `_WITCH @ 1df2:089f` (CD): `if (GalleryData[i*0x46 + 0x02] == -1)
 	// _DisplayCorrect(); else _DisplayAlibi(...)`. Innocent suspects
 	// store their alibi-text TextBlock offset at +0x02; the guilty
-	// one stores the sentinel 0xFFFF.
+	// one stores the sentinel 0xFFFF. Floppy uses the same convention
+	// at suspect entry +2..3 but with variable-stride entries.
+	if (_isFloppy) {
+		const byte *e = floppySuspectEntry(suspectIdx);
+		return e && READ_LE_UINT16(e + 2) == 0xFFFF;
+	}
 	const byte *gd = galleryData();
 	if (!gd || suspectIdx >= _numSuspects)
 		return false;
@@ -409,6 +468,26 @@ bool Mystery::isGuilty(uint suspectIdx) const {
 }
 
 uint16 Mystery::alibiTextOffset(uint suspectIdx) const {
+	if (_isFloppy) {
+		// Floppy alibi: u16 at suspect +2..3 carries TWO things:
+		// 0xFFFF = guilty, otherwise the HIGH BYTE indexes the
+		// TEXT_BLOCK table (header[+0xc]), each entry u16 = absolute
+		// alibi-text offset in the buffer. Verified at
+		// `_DisplayAlibi_Floppy @ 1d40:0145` reading
+		// `*(int *)(textBlock + ((byte *)entry)[3] * 2)`. The result
+		// is an ABSOLUTE offset; caller reads via `blobAt(off)`.
+		const byte *e = floppySuspectEntry(suspectIdx);
+		if (!e)
+			return 0xFFFF;
+		const uint16 alibi = READ_LE_UINT16(e + 2);
+		if (alibi == 0xFFFF)
+			return 0xFFFF;
+		const uint8 idx = (uint8)(alibi >> 8);
+		const uint32 base = _textOffset;
+		if ((uint32)idx * 2 + 2 > _data.size() - base)
+			return 0xFFFF;
+		return READ_LE_UINT16(_data.data() + base + (uint32)idx * 2);
+	}
 	const byte *gd = galleryData();
 	if (!gd || suspectIdx >= _numSuspects)
 		return 0xFFFF;
@@ -436,6 +515,38 @@ int Mystery::selectedPoints() const {
 	const uint16 cnt = noteIndexCount();
 	if (!ni || cnt == 0)
 		return 0;
+	if (_isFloppy) {
+		// Floppy `_GetSelectedPoints_Floppy @ 1d40:0c23`: collect the
+		// per-note score (note +6 byte) for every clue with TextSeen
+		// set, sort descending, sum the top 5. Mirrors
+		// `_SortNotesByScore_Floppy @ 1d40:000e` + the top-5 fold.
+		uint8 scores[Mystery::kCluesFoundCap] = {};
+		uint scoreCount = 0;
+		const uint maxIdx = MIN<uint>(cnt, kCluesFoundCap);
+		for (uint i = 0; i < maxIdx; i++) {
+			if (_cluesFound[i] == 0)
+				continue;
+			scores[scoreCount++] = ni[i * 7 + 6];
+		}
+		// Partial selection sort for top 5.
+		const uint topN = MIN<uint>(5u, scoreCount);
+		for (uint k = 0; k < topN; k++) {
+			uint best = k;
+			for (uint j = k + 1; j < scoreCount; j++) {
+				if (scores[j] > scores[best])
+					best = j;
+			}
+			if (best != k) {
+				uint8 tmp = scores[k];
+				scores[k] = scores[best];
+				scores[best] = tmp;
+			}
+		}
+		int total = 0;
+		for (uint k = 0; k < topN; k++)
+			total += scores[k];
+		return total;
+	}
 	int total = 0;
 	for (uint i = 0; i < cnt && i < kCluesFoundCap; i++) {
 		if (!_noteSelected[i])
@@ -453,6 +564,22 @@ void Mystery::syncState(Common::Serializer &s) {
 	s.syncArray(_hotSpotsSeen, kHotSpotsCap, Common::Serializer::Uint16LE);
 	s.syncArray(_inGallery,    kGalleryCap,  Common::Serializer::Uint16LE);
 	s.syncBytes(_newOrder, kGalleryCap);
+	// Save profiles created before the floppy `_newOrder` identity init
+	// was added stored all-zeros here. Loading that back over the
+	// identity set by `Mystery::load()` makes every dialog with byte9>0
+	// resolve to slot 0 (so the second suspect found at a CONSITE
+	// overwrites the first). Detect the legacy zero-fill on read and
+	// rebuild the identity mapping that the loader produced.
+	if (s.isLoading()) {
+		bool allZero = true;
+		for (uint i = 0; i < kGalleryCap; i++) {
+			if (_newOrder[i] != 0) { allZero = false; break; }
+		}
+		if (allZero) {
+			for (uint i = 0; i < kGalleryCap; i++)
+				_newOrder[i] = (uint8)i;
+		}
+	}
 	s.syncArray(_visitedSite, kVisitedSiteCap, Common::Serializer::Uint16LE);
 	s.syncArray(_onSites,     kVisitedSiteCap, Common::Serializer::Uint16LE);
 	s.syncAsByte(_sawCOFFSITEs);
diff --git a/engines/eem/mystery.h b/engines/eem/mystery.h
index 251320604aa..82c881803cd 100644
--- a/engines/eem/mystery.h
+++ b/engines/eem/mystery.h
@@ -91,6 +91,13 @@ public:
 	/// First u16 of each entry is the PIC picture ID for that suspect.
 	const byte *galleryData() const;
 
+	/// Floppy variable-stride suspect record. Returns nullptr on CD or
+	/// when @p suspectIdx is out of range. Walks the gallery section
+	/// (`5 + nameLen` bytes per suspect) to land on the requested entry.
+	/// Layout: u16 picID, u16 alibiMarker (0xFFFF = guilty),
+	/// u8 nameLen, nameLen bytes of name.
+	const byte *floppySuspectEntry(uint suspectIdx) const;
+
 	/// Pointer to the NoteIndex array (4 bytes per entry: u16 textOff + u16 pts).
 	const byte *noteIndex() const;
 
diff --git a/engines/eem/ui.cpp b/engines/eem/ui.cpp
index 42321a6c626..fc5b53f4b0a 100644
--- a/engines/eem/ui.cpp
+++ b/engines/eem/ui.cpp
@@ -55,6 +55,19 @@ const GallerySlot kGallerySlots[5] = {
 	{ 191,  90 }  // 4
 };
 
+// Floppy gallery slot positions verified at `2608:0x16c` (5 ??
+// {u16 x, u16 y}) ??? read by `_DrawGallery_Floppy @ 154e:0045`'s
+// `[BX + 0x16c]` (x) and `[BX + 0x16e]` (y) loads. The floppy
+// layout is shifted left ~0x30 px relative to CD: row 0 starts at
+// x=0x53 (vs 0x53???0x83 on CD) and the bottom row at x=0x77 / 0xbf.
+const GallerySlot kFloppyGallerySlots[5] = {
+	{ 0x53, 0x0e }, // 0
+	{ 0x9b, 0x0e }, // 1
+	{ 0xe3, 0x0e }, // 2
+	{ 0x77, 0x5a }, // 3
+	{ 0xbf, 0x5a }  // 4
+};
+
 // `_GetKDTextBalloon @ 1df2:0105` digit-balloon table @ `29be:1064`:
 //   '0'???0x15  '1'???0x16  '2'???0x17  '3'???0x18  '4'???0x19
 //   '5'???0x1a  '6'???0x20  '7'???0x21  '8'???0x22  '9'???0x1e
@@ -170,10 +183,12 @@ void drawCaseSubmenu(const CaseSubmenuView &v) {
 	// Top centred title. `_CaseSelection @ 1c33:0aa3` formats "Book %d"
 	// for tiers 1/2 and "Challenge Book" (sprintf with no arg) for
 	// tier 3. `_Show_String(0xc, (0xba - width)/2 + 0x3c, ???, 0x10)`
-	// places it horizontally centred over the panel.
+	// places it horizontally centred over the panel. Spanish floppy
+	// uses "Lib. %u" / "Libro de Retos" (verified in Spanish EEM.EXE).
+	const bool spanish = v.vm && v.vm->isSpanish();
 	const Common::String title = (v.book == 3)
-		? Common::String("Challenge Book")
-		: Common::String::format("Book %u", v.book);
+		? Common::String(spanish ? "Libro de Retos" : "Challenge Book")
+		: Common::String::format(spanish ? "Lib. %u" : "Book %u", v.book);
 	const int titleW = v.vm->getFont().getStringWidth(title);
 	const int titleX = (0xba - titleW) / 2 + 0x3c;
 	v.vm->getFont().drawString(&scratch, title, titleX, 12, 320, 0xF);
@@ -274,7 +289,8 @@ void drawCaseSelectionFrame(const CaseSelectionView &v) {
 		// `_Show_String(0xc, (0xba - width)/2 + 0x3c, ???)` in the
 		// original. We don't track challenge tier yet so always
 		// show "Book 1".
-		const Common::String book = "Book 1";
+		const Common::String book = v.vm->isSpanish()
+			? Common::String("Lib. 1") : Common::String("Book 1");
 		const int titleW = v.vm->getFont().getStringWidth(book);
 		const int titleX = (0xba - titleW) / 2 + 0x3c;
 		v.vm->getFont().drawString(&scratch, book, titleX, 12, 320, 0xF);
@@ -468,6 +484,12 @@ void EEMEngine::doNewPlayer() {
 	Picture bg;
 	const bool haveBG = _picsArchive.getPicture(0x104, bg);
 
+	// Localized name-entry prompt. Spanish text is taken from the
+	// Spanish floppy EEM.EXE ("Teclea tu nombre"). The colon suffix is
+	// our own ??? the original DOS prompt has none.
+	const char *prompt = isSpanish()
+		? "Teclea tu nombre:" : "Please type your name:";
+
 	// Match the original `_NewPlayer`: `_Show_String(rw=0x28, cl=0x50)`
 	// for the prompt, then `_ShowChar(0x50, x, ???)` for typed input.
 	// (rw=row=y, cl=col=x.) Prompt at (y=40, x=80), input at (y=80, x=80).
@@ -476,7 +498,7 @@ void EEMEngine::doNewPlayer() {
 	scratch.clear();
 	if (haveBG)
 		scratch.simpleBlitFrom(bg.surface);
-	_font.drawString(&scratch, "Please type your name:", 80, 40, 240, 0xF);
+	_font.drawString(&scratch, prompt, 80, 40, 240, 0xF);
 	_font.drawString(&scratch, name + "_", 80, 80, 240, 0xF);
 	g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
 							   0, 0, 320, 200);
@@ -537,8 +559,7 @@ void EEMEngine::doNewPlayer() {
 			scratch.clear();
 			if (haveBG)
 				scratch.simpleBlitFrom(bg.surface);
-			_font.drawString(&scratch, "Please type your name:",
-							 80, 40, 240, 0xF);
+			_font.drawString(&scratch, prompt, 80, 40, 240, 0xF);
 			_font.drawString(&scratch, name + "_", 80, 80, 240, 0xF);
 			g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
 									   0, 0, 320, 200);
@@ -916,8 +937,12 @@ void EEMEngine::doSetup() {
 		}
 		const Common::Rect kBox(80, 80, 240, 120);
 		scratch.fillRect(kBox, 0x00);
-		_font.drawString(&scratch, "Are you sure?", 100, 88, 200, 0xF);
-		_font.drawString(&scratch, "Y = yes   N = no", 100, 102, 200, 0xF);
+		_font.drawString(&scratch,
+			isSpanish() ? "Estas seguro?" : "Are you sure?",
+			100, 88, 200, 0xF);
+		_font.drawString(&scratch,
+			isSpanish() ? "S = si   N = no" : "Y = yes   N = no",
+			100, 102, 200, 0xF);
 		g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
 								   0, 0, 320, 200);
 		g_system->updateScreen();
@@ -929,7 +954,9 @@ void EEMEngine::doSetup() {
 					return true;
 				if (ev.type == Common::EVENT_KEYDOWN) {
 					const Common::KeyCode k = ev.kbd.keycode;
+					// Spanish prompt is "S = si" ??? accept both Y and S.
 					if (k == Common::KEYCODE_y ||
+						k == Common::KEYCODE_s ||
 						k == Common::KEYCODE_RETURN)
 						return true;
 					if (k == Common::KEYCODE_n ||
@@ -1240,13 +1267,24 @@ void EEMEngine::doCaseSelection() {
 		kPickScrap3,
 		kNumPicks
 	};
-	const char *kPickLabel[kNumPicks] = {
+	// Localized action-menu labels. Spanish text is taken verbatim
+	// from the Spanish floppy EEM.EXE (`eem-full-game/floppy-es/`):
+	// "Elegir Misterio" / "Caso de Practica" / "Ver Recortes  1..3".
+	const char *kPickLabelEN[kNumPicks] = {
 		"         Choose A Mystery",
 		"         Practice Mystery",
 		"         See ScrapBook 1",
 		"         See ScrapBook 2",
 		"         See ScrapBook 3"
 	};
+	const char *kPickLabelES[kNumPicks] = {
+		"         Elegir Misterio ",
+		"         Caso de Practica",
+		"         Ver Recortes  1",
+		"         Ver Recortes  2",
+		"         Ver Recortes  3"
+	};
+	const char * const *kPickLabel = isSpanish() ? kPickLabelES : kPickLabelEN;
 	// Menu entry gating per `_ActionScreen @ 1c33:195b` ??? the asm at
 	// 1c33:19d1-1a70 sets greys[] based on chain stage AND per-tier
 	// solve count:
@@ -2002,7 +2040,8 @@ void EEMEngine::drawNotebookFrame(int &page) {
 		const uint clueId = found[i];
 		Common::String txt = noteText(clueId);
 		if (txt.empty())
-			txt = Common::String::format("clue %u", clueId);
+			txt = Common::String::format(
+				isSpanish() ? "nota %u" : "clue %u", clueId);
 		// Per `_DrawNotes @ 161e:01d0`: text uses
 		// `_NoteUnselectedColor` (0x5c=cyan) for unselected and 0x3c
 		// (light yellow-white) for selected. Both contrast cleanly
@@ -2173,17 +2212,34 @@ void EEMEngine::doGallery() {
 						//   _AddPicBackground(pic, 0x94, 0xf);
 						//   _DrawGalleryNotes(gd + i*0x46);
 						//   loop until ESC or button click.
-						// Suspect data layout (verified against M1):
-						//   +0..1: picId (used here AND for gallery slot)
-						//   +8..9: number of clues for this suspect
-						//   +0xa..??: array of u16 clue IDs (terminated
-						//             by 0xFFFF if shorter than count).
+						// Suspect data layout differs by variant:
+						//   * CD (`158f:0419`): fixed 0x46-byte stride.
+						//     +0..1   picId
+						//     +8..9   clue count (u16)
+						//     +0xa..  array of u16 clue IDs (max 30,
+						//             terminated by 0xFFFF if short).
+						//   * Floppy (`_MoreInfo_Floppy = 154e:042b` ???
+						//     `FUN_154e_0201` ??? `FUN_15e0_01e8`):
+						//     variable-stride.
+						//     +0..1   picId
+						//     +2..3   alibi (0xFFFF = guilty)
+						//     +4      clue count (u8)
+						//     +5..    u8 clue IDs (per the asm at
+						//             154e:020e..0282 which calls
+						//             `FUN_15e0_01e8(rect, entry+5,
+						//                            entry[4], NULL)`).
+						const bool floppyMI = isFloppy();
 						const uint suspectIdx = (uint)slotSuspect[i];
-						const byte *suspect = gd + suspectIdx * 0x46;
+						const byte *suspect = floppyMI
+							? _mystery.floppySuspectEntry(suspectIdx)
+							: gd + suspectIdx * 0x46;
+						if (!suspect)
+							break;
 						const uint16 detailPic =
 							READ_LE_UINT16(suspect + 0);
-						const uint16 clueCount =
-							READ_LE_UINT16(suspect + 8);
+						const uint clueCount = floppyMI
+							? (uint)suspect[4]
+							: READ_LE_UINT16(suspect + 8);
 
 						Graphics::ManagedSurface ms(320, 200,
 							Graphics::PixelFormat::createFormatCLUT8());
@@ -2253,18 +2309,33 @@ void EEMEngine::doGallery() {
 						int yPos = ry;
 						const int lineH = _font.getFontHeight() + 1;
 						bool drewAny = false;
-						for (uint k = 0; k < clueCount && k < 30; k++) {
-							const uint16 clueId =
-								READ_LE_UINT16(suspect + 0xa + k * 2);
-							if (clueId == 0xFFFF)
+						const uint clueMax = floppyMI ? clueCount : 30u;
+						for (uint k = 0; k < clueCount && k < clueMax; k++) {
+							const uint16 clueId = floppyMI
+								? (uint16)suspect[5 + k]
+								: READ_LE_UINT16(suspect + 0xa + k * 2);
+							if (!floppyMI && clueId == 0xFFFF)
 								break;
 							if (clueId >= Mystery::kCluesFoundCap ||
 								!_mystery._cluesFound[clueId])
 								continue;
 							if (!ni || clueId >= niCount)
 								continue;
-							const uint16 textOff =
-								READ_LE_UINT16(ni + clueId * 4);
+							// Floppy notes are 7-byte entries: u16 ?,
+							// u16 jakeOff, u16 jennyOff, u8 score. The
+							// partner-specific text offset is at +2
+							// (Jake) or +4 (Jenny) ??? verified at
+							// `FUN_22dc_05c8 @ 22dc:0843`. CD notes are
+							// 4 bytes: u16 textOff, u16 score.
+							uint16 textOff;
+							if (floppyMI) {
+								const uint partnerByte = (_partner == 0)
+									? 2 : 4;
+								textOff = READ_LE_UINT16(
+									ni + clueId * 7 + partnerByte);
+							} else {
+								textOff = READ_LE_UINT16(ni + clueId * 4);
+							}
 							Common::String txt =
 								parseString(_mystery.textAt(textOff),
 											_playerName, _partner);
@@ -2281,15 +2352,20 @@ void EEMEngine::doGallery() {
 						}
 						if (!drewAny && _font.isLoaded()) {
 							_font.drawString(&ms,
-								"No clues yet for this suspect.",
+								isSpanish()
+									? "Aun no hay pistas para este sospechoso."
+									: "No clues yet for this suspect.",
 								rx, ry, rw, 0x5C);
 						}
 						// Header / footer text.
 						if (_font.isLoaded()) {
-							_font.drawString(&ms, "SUSPECT FILE",
-											  rx, ry - 11, rw, 0x3C);
-							_font.drawString(&ms, "(click / ESC: back)",
-											  rx, ry + rh + 2, rw, 0x3C);
+							_font.drawString(&ms,
+								isSpanish() ? "EXPEDIENTE" : "SUSPECT FILE",
+								rx, ry - 11, rw, 0x3C);
+							_font.drawString(&ms,
+								isSpanish() ? "(clic / ESC: volver)"
+											: "(click / ESC: back)",
+								rx, ry + rh + 2, rw, 0x3C);
 						}
 						g_system->copyRectToScreen(ms.getPixels(),
 							ms.pitch, 0, 0, 320, 200);
@@ -2407,8 +2483,17 @@ void EEMEngine::drawGalleryFrame(const byte *gd, uint8 numSuspects,
 							  partnerAni[frameIdx], 5, 0x50);
 	}
 
-	// Portraits ??? `_DrawGallery @ 158f:0046` walks suspects 0..N-1 and
-	// only renders those flagged in `_InGallery[NewOrder[i]]`.
+	// Portraits ??? `_DrawGallery @ 158f:0046` (CD) /
+	// `_DrawGallery_Floppy @ 154e:0045` (floppy) walks suspects 0..N-1
+	// and only renders those flagged in `_InGallery[NewOrder[i]]`.
+	// Layout differs by variant:
+	//   * CD: fixed 0x46-byte stride, slot positions at `kGallerySlots`.
+	//   * Floppy: variable-stride entries (5 + entry[4] bytes per
+	//     suspect), slot positions at `kFloppyGallerySlots` (verified
+	//     at `2608:0x16c`).
+	const bool floppy = isFloppy();
+	const GallerySlot * const slots =
+		floppy ? kFloppyGallerySlots : kGallerySlots;
 	for (uint i = 0; i < numSuspects && i < Mystery::kGalleryCap; i++) {
 		slotRects[i] = Common::Rect();
 		slotSuspect[i] = -1;
@@ -2416,11 +2501,16 @@ void EEMEngine::drawGalleryFrame(const byte *gd, uint8 numSuspects,
 		const uint8 phys = _mystery._newOrder[i];
 		if (phys >= 5)
 			continue;
-		const GallerySlot &s = kGallerySlots[phys];
+		const GallerySlot &s = slots[phys];
 
 		const bool discovered = _mystery._inGallery[phys] != 0;
 		if (discovered) {
-			const uint16 picId = READ_LE_UINT16(gd + i * 0x46);
+			const byte *entry = floppy
+				? _mystery.floppySuspectEntry(i)
+				: gd + i * 0x46;
+			if (!entry)
+				continue;
+			const uint16 picId = READ_LE_UINT16(entry);
 			Picture portrait;
 			if (picId == 0 ||
 				!_picsArchive.getPicture(picId, portrait))
@@ -3176,7 +3266,8 @@ bool EEMEngine::doAccuseNotes() {
 								   _playerName, _partner);
 			}
 			if (txt.empty())
-				txt = Common::String::format("clue %u", clueId);
+				txt = Common::String::format(
+					isSpanish() ? "nota %u" : "clue %u", clueId);
 			Common::Array<Common::String> wrapped;
 			_font.wordWrapText(txt, rectW, wrapped);
 			const int h = (int)wrapped.size() * lineH;
@@ -3195,8 +3286,12 @@ bool EEMEngine::doAccuseNotes() {
 		const uint remaining = (selectedCount < expected)
 			? expected - selectedCount
 			: 0;
-		const Common::String counter = Common::String::format("%u %s",
-			remaining, remaining == 1 ? "clue" : "clues");
+		// Spanish floppy uses "nota/notas" (verified in Spanish EEM.EXE).
+		const char *clueWord = isSpanish()
+			? (remaining == 1 ? "nota" : "notas")
+			: (remaining == 1 ? "clue" : "clues");
+		const Common::String counter =
+			Common::String::format("%u %s", remaining, clueWord);
 		_font.drawString(&scratch, counter, 209, 11, 100, 0x0F);
 
 		if (numPages > 1) {
@@ -3317,6 +3412,22 @@ void EEMEngine::doAccuse() {
 	if (!_mystery.isLoaded() || !_font.isLoaded())
 		return;
 
+	// Floppy accusation flow is structurally different from the CD's:
+	// the suspect gallery section uses variable-stride entries (5 +
+	// nameLen bytes, NOT 0x46), the score is computed from the top-5
+	// note scores in `_TextSeen` (not `_NoteSelected`), and the win
+	// path renders the solved-chain dialog records at header[+0x12]
+	// rather than a single ClueBlock. Dispatch to a dedicated handler
+	// that mirrors `_KDHelp_Floppy` + `_HandleNoteButton_Floppy` (button
+	// 4 ??? `FUN_1d40_11fd` "ready to solve" gate) +
+	// `FUN_1d40_0c79` (gallery picker) + `_DisplayCorrect_Floppy` /
+	// `_DisplayAlibi_Floppy`.
+	if (isFloppy()) {
+		doAccuseFloppy();
+		return;
+	}
+
+
 	// Mirrors `_DoAccuse @ 1df2:0bdd` + `_DoAccuseGallery @ 1df2:0a31`:
 	//   1. ACCUSE-NOTES SCREEN (PIC 0x1A7, the red "accuse-mode" BG):
 	//      `_DrawNotes(_AccuseNoteRect, NULL, 100, _NoteSelected)`
@@ -3370,13 +3481,22 @@ void EEMEngine::doAccuse() {
 							   _playerName, _partner);
 		if (hint.empty()) {
 			// Fallback if `KDTextIndex[+6]` isn't set in this mystery.
-			hint = (_mystery.selectedPoints() == 0)
-				? "We're not ready to solve this mystery yet. "
-				  "Let's keep investigating until we have some "
-				  "more solid evidence."
-				: "We don't have quite enough evidence yet. "
-				  "Let's review our notes and find a few more "
-				  "clues before we accuse anyone.";
+			// Spanish text from the Spanish floppy EEM.EXE ??? there's
+			// only the single "1Necesitamos buscar pistas..." string
+			// in the binary, so use it for both the zero-points and
+			// some-points cases.
+			if (isSpanish()) {
+				hint = "Necesitamos buscar pistas antes de resolver "
+					   "el misterio. Investiguemos un poco mas!";
+			} else {
+				hint = (_mystery.selectedPoints() == 0)
+					? "We're not ready to solve this mystery yet. "
+					  "Let's keep investigating until we have some "
+					  "more solid evidence."
+					: "We don't have quite enough evidence yet. "
+					  "Let's review our notes and find a few more "
+					  "clues before we accuse anyone.";
+			}
 		}
 
 		// Compose balloon overlay on the current screen. Mirrors the
@@ -4094,6 +4214,417 @@ void EEMEngine::doAccuse() {
 	}
 }
 
+void EEMEngine::doAccuseFloppy() {
+	// Floppy accuse flow ??? mirrors:
+	//   `_KDHelp_Floppy / FUN_1d40_11fd @ 1d40:11fd` (score gate +
+	//        partner "ready / not ready" balloon)
+	//   `FUN_1d40_0e07 @ 1d40:0e07`   (clue-selection screen ??? skipped
+	//        in this MVP; selectedPoints() already takes the top-5)
+	//   `FUN_1d40_0c79 @ 1d40:0c79`   (gallery picker)
+	//   `_DisplayCorrect_Floppy @ 1d40:0894`
+	//   `_DisplayAlibi_Floppy   @ 1d40:00df`
+	const byte *kdIdx     = _mystery.kdTextIndex();
+	const byte *bufBase   = _mystery.blobAt(0);
+	const uint32 mysSize  = _mystery.dataSize();
+	if (!kdIdx || !bufBase)
+		return;
+
+	// Helper: render one KDTextIndex string as a centred KD balloon
+	// over the current screen, mirroring `_DisplayHint_Floppy @
+	// 1503:00ca` (= `FUN_1d40_11fd`'s body). `kdSlot` is the index
+	// into KDTextIndex (0..N) ??? entries are u16 absolute text
+	// offsets.
+	auto showFloppyKDHint = [&](uint kdSlot) {
+		if ((uint)(kdSlot * 2) + 2 > (uint)(mysSize - (kdIdx - bufBase)))
+			return;
+		const uint16 textOff = READ_LE_UINT16(kdIdx + kdSlot * 2);
+		if (textOff == 0 || textOff >= mysSize)
+			return;
+		const char *p = (const char *)(bufBase + textOff);
+		uint32 lineLen = 0;
+		while (textOff + lineLen < mysSize && p[lineLen] != 0)
+			lineLen++;
+		if (lineLen == 0)
+			return;
+		Common::String raw(p, lineLen);
+		// Digit prefix ??? balloon variant (per `_GetKDTextBalloon_Floppy
+		// @ 1d40:009f` and the `_KDBalloonByChar_Floppy` table at
+		// `2608:0c14 + char`).
+		static const uint8 kDigitToBalloon[10] = {
+			0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1c, 0x1d, 0x1e, 0x0a
+		};
+		uint balloonIdx = 0x17;
+		const char *txt = raw.c_str();
+		if (*txt >= '0' && *txt <= '9') {
+			balloonIdx = kDigitToBalloon[(int)(*txt - '0')];
+			txt++;
+		}
+		Common::String text =
+			parseString(Common::String(txt), _playerName, _partner);
+		Graphics::ManagedSurface ms(320, 200,
+			Graphics::PixelFormat::createFormatCLUT8());
+		Graphics::Surface *cur = g_system->lockScreen();
+		if (cur) {
+			ms.simpleBlitFrom(*cur);
+			g_system->unlockScreen();
+		}
+		Picture balloon;
+		const bool haveBalloon = _balloonArchive.size() > balloonIdx &&
+			_balloonArchive.loadEntry(balloonIdx, balloon);
+		uint16 balloonY = 1;
+		if (haveBalloon) {
+			const uint h = (uint)balloon.surface.h;
+			if (h < 0x4e)
+				balloonY = (uint16)((0x50 - h) >> 1);
+			const byte transp = (byte)(balloon.flags >> 8);
+			for (int row = 0; row < balloon.surface.h && balloonY + row < 200;
+				 row++) {
+				const byte *src =
+					(const byte *)balloon.surface.getBasePtr(0, row);
+				byte *dst = (byte *)ms.getBasePtr(0x21, balloonY + row);
+				for (int col = 0; col < balloon.surface.w && 0x21 + col < 320;
+					 col++) {
+					if (src[col] != transp)
+						dst[col] = src[col];
+				}
+			}
+		}
+		uint16 bx = 5;
+		uint16 by = 4;
+		uint16 bw = 142;
+		getBalloonInsets(balloonIdx, bx, by, bw);
+		_font.drawWordWrapped(&ms, 0x21 + bx, balloonY + by,
+							  MAX<int>(8, (int)bw), text, 0);
+		g_system->copyRectToScreen(ms.getPixels(), ms.pitch, 0, 0, 320, 200);
+		g_system->updateScreen();
+		// Wait for click.
+		while (!shouldQuit()) {
+			Common::Event ev;
+			bool advance = false;
+			while (g_system->getEventManager()->pollEvent(ev)) {
+				if (ev.type == Common::EVENT_QUIT ||
+					ev.type == Common::EVENT_RETURN_TO_LAUNCHER ||
+					ev.type == Common::EVENT_LBUTTONDOWN ||
+					ev.type == Common::EVENT_KEYDOWN) {
+					advance = true;
+					break;
+				}
+			}
+			if (advance)
+				break;
+			g_system->updateScreen();
+			g_system->delayMillis(10);
+		}
+	};
+
+	// `FUN_1d40_11fd` score gate. Picks one of three KD strings and
+	// returns 1 only when score >= 100.
+	const int score = _mystery.selectedPoints();
+	uint kdSlot;
+	bool readyToSolve;
+	if (score < 50) {
+		kdSlot = 0;        // KDTextIndex[0] ??? "we've barely started"
+		readyToSolve = false;
+	} else if (score < 100) {
+		kdSlot = 1;        // KDTextIndex[1] ??? "getting closer"
+		readyToSolve = false;
+	} else {
+		kdSlot = 2;        // KDTextIndex[2] ??? "ready to solve"
+		readyToSolve = true;
+	}
+	showFloppyKDHint(kdSlot);
+	if (!readyToSolve) {
+		_nextScreen = _lastScreen != kScreenInvalid
+			? (ScreenId)_lastScreen : kScreenSite;
+		return;
+	}
+
+	// `FUN_1d40_0c79` ??? gallery picker. Show "Which suspect?" KD
+	// hint at slot 4 (KDTextIndex[+8]/2 = entry index 4), then
+	// render the gallery and wait for click.
+	showFloppyKDHint(4);
+
+	// Build slot list. Floppy gallery section: byte0 = numSuspects,
+	// then variable-stride entries.
+	const uint8 num = _mystery.numSuspects();
+	if (num == 0) {
+		_nextScreen = _lastScreen != kScreenInvalid
+			? (ScreenId)_lastScreen : kScreenSite;
+		return;
+	}
+
+	// Verbatim from `_DrawGallery_Floppy @ 154e:0050` ??? slot positions
+	// at `2608:016c` (5 ?? {u16 x, u16 y}).
+	struct GallerySlot { int x, y; };
+	static const GallerySlot kFloppySlots[5] = {
+		{ 0x53, 0x0e }, { 0x9b, 0x0e }, { 0xe3, 0x0e },
+		{ 0x77, 0x5a }, { 0xbf, 0x5a },
+	};
+
+	Picture accuseBg;
+	const bool haveAccuseBg = _picsArchive.getPicture(0x3f, accuseBg);
+
+	auto drawGallery = [&](int highlighted,
+						   Common::Array<Common::Rect> &rects,
+						   Common::Array<int> &suspects) {
+		Graphics::ManagedSurface scratch(320, 200,
+			Graphics::PixelFormat::createFormatCLUT8());
+		scratch.clear();
+		if (haveAccuseBg)
+			scratch.simpleBlitFrom(accuseBg.surface);
+
+		// Partner sprite (anim 2 / 0x10) ??? same as CD doAccuse.
+		const uint partnerAnim = (_partner == 0) ? 2 : 0x10;
+		Animation partnerAni;
+		if (_aniArchive.loadAnimation(partnerAnim, partnerAni) &&
+			!partnerAni.empty()) {
+			const uint32 now = g_system->getMillis();
+			const uint frameIdx = partnerFrameAtTick(0x02,
+													  (uint)partnerAni.size(), now);
+			blitAnimFrameAnchored(scratch.surfacePtr(),
+								  partnerAni[frameIdx], 5, 0x50);
+		}
+
+		rects.resize(num);
+		suspects.resize(num);
+		for (uint i = 0; i < num; i++) {
+			rects[i] = Common::Rect();
+			suspects[i] = -1;
+			const uint8 phys = _mystery._newOrder[i];
+			if (phys >= 5)
+				continue;
+			if (_mystery._inGallery[phys] == 0)
+				continue;
+			const byte *e = _mystery.floppySuspectEntry(i);
+			if (!e)
+				continue;
+			const uint16 picId = READ_LE_UINT16(e + 0);
+			if (picId == 0)
+				continue;
+			Picture portrait;
+			if (!_picsArchive.getPicture(picId, portrait))
+				continue;
+			const GallerySlot &s = kFloppySlots[phys];
+			// `_DrawGallery_Floppy @ 154e:00ed` bottom-aligns to baseline
+			// 0x48 (= 72), same as CD: `placeY = s.y + (0x48 - h)`.
+			const int placeX = s.x;
+			const int placeY = s.y + (0x48 - portrait.surface.h);
+			const byte transp = (byte)(portrait.flags >> 8);
+			scratch.transBlitFrom(portrait.surface,
+								  Common::Point(placeX, placeY),
+								  (uint32)transp);
+			rects[i] = Common::Rect(placeX, placeY,
+									 placeX + portrait.surface.w,
+									 placeY + portrait.surface.h);
+			suspects[i] = (int)i;
+			if (highlighted == (int)i) {
+				scratch.frameRect(rects[i], 0xFE);
+			}
+		}
+
+		g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
+								   0, 0, 320, 200);
+		g_system->updateScreen();
+	};
+
+	Common::Array<Common::Rect> slotRects;
+	Common::Array<int> slotSuspect;
+	int highlighted = 0;
+	int picked = -1;
+	drawGallery(highlighted, slotRects, slotSuspect);
+
+	uint32 lastTick = g_system->getMillis();
+	while (picked < 0 && !shouldQuit()) {
+		Common::Event ev;
+		while (g_system->getEventManager()->pollEvent(ev)) {
+			if (ev.type == Common::EVENT_QUIT ||
+				ev.type == Common::EVENT_RETURN_TO_LAUNCHER)
+				return;
+			if (ev.type == Common::EVENT_KEYDOWN) {
+				if (ev.kbd.keycode == Common::KEYCODE_ESCAPE)
+					return;
+				if (ev.kbd.keycode == Common::KEYCODE_TAB ||
+					ev.kbd.keycode == Common::KEYCODE_RIGHT) {
+					highlighted = (highlighted + 1) % MAX<int>(1, (int)num);
+					drawGallery(highlighted, slotRects, slotSuspect);
+				} else if (ev.kbd.keycode == Common::KEYCODE_LEFT) {
+					highlighted = (highlighted + (int)num - 1) %
+								  MAX<int>(1, (int)num);
+					drawGallery(highlighted, slotRects, slotSuspect);
+				} else if ((ev.kbd.keycode == Common::KEYCODE_RETURN ||
+							ev.kbd.keycode == Common::KEYCODE_KP_ENTER) &&
+						   highlighted < (int)slotRects.size() &&
+						   !slotRects[highlighted].isEmpty()) {
+					picked = slotSuspect[highlighted];
+				}
+			}
+			if (ev.type == Common::EVENT_LBUTTONDOWN) {
+				for (uint i = 0; i < slotRects.size(); i++) {
+					if (slotSuspect[i] >= 0 &&
+						slotRects[i].contains(ev.mouse.x, ev.mouse.y)) {
+						picked = slotSuspect[i];
+						break;
+					}
+				}
+			}
+		}
+		const uint32 now = g_system->getMillis();
+		if (now - lastTick >= 100) {
+			drawGallery(highlighted, slotRects, slotSuspect);
+			lastTick = now;
+		}
+		g_system->updateScreen();
+		g_system->delayMillis(10);
+	}
+	if (picked < 0)
+		return;
+
+	const bool guilty = _mystery.isGuilty((uint)picked);
+
+	if (guilty) {
+		// `_DisplayCorrect_Floppy @ 1d40:0894` ??? load PIC 5 (win BG),
+		// walk the solved-clue chain at header[+0x12]: byte0 = count,
+		// then `count` dialog records (same FUN_22dc_05c8 layout). We
+		// reuse `displayFloppyDialogRecords` for the rendering.
+		Picture winBg;
+		if (_picsArchive.getPicture(5, winBg))
+			blitAt(winBg, 0, 0);
+		setSitePalette(0x22);
+		g_system->updateScreen();
+
+		const byte *chain = _mystery.solvedClueBlock();
+		if (chain) {
+			const uint count = chain[0];
+			displayFloppyDialogRecords(chain + 1, count, 0);
+		}
+
+		// Mark mystery solved ??? equivalent to
+		// `((u16 *)0x5d20)... actually 0x3054[mysteryNum] = 1` which we
+		// translate to clearing the in-progress mystery + saving the
+		// profile (matches CD `doAccuse` win path).
+		if (_partner == 0)
+			_mystery._firstTry = false;
+		_mystery._solvedPuzzle = true;
+		_mystery.clear();
+		(void)saveProfile(_playerName);
+		_nextScreen = kScreenAction;
+		return;
+	}
+
+	// Innocent ??? `_DisplayAlibi_Floppy @ 1d40:00df`:
+	//   1. Load PIC 0x3e (alibi BG), draw at (0x42, 0x14).
+	//   2. Load suspect picID, draw portrait at (0x82, 0x5a).
+	//   3. Read alibi text via TEXT_BLOCK[suspect[+3] * 2]; render in
+	//      a balloon picked by digit-prefix dispatch.
+	//   4. Voice from per-partner table at suspect_entry[+5..]
+	//      (we approximate using slot 13/14 ??? Jake/Jenny "wrong"
+	//      sting; original picks per-suspect).
+	//   5. Wait for click, then KD reaction balloon
+	//      (KDTextIndex[+0x10]/2 = slot 8) + voice slot 5.
+	const byte *susp = _mystery.floppySuspectEntry((uint)picked);
+	uint16 picId = 0;
+	uint16 alibiOff = 0xFFFF;
+	if (susp) {
+		picId    = READ_LE_UINT16(susp + 0);
+		alibiOff = _mystery.alibiTextOffset((uint)picked);
+	}
+
+	Picture alibiBg;
+	const bool haveAlibiBg = _picsArchive.getPicture(0x3e, alibiBg);
+	Picture suspectPic;
+	const bool haveSuspect = picId != 0 &&
+		_picsArchive.getPicture(picId, suspectPic);
+
+	Common::String alibi;
+	if (alibiOff != 0xFFFF && alibiOff < mysSize) {
+		const char *raw = (const char *)(bufBase + alibiOff);
+		uint32 lineLen = 0;
+		while (alibiOff + lineLen < mysSize && raw[lineLen] != 0)
+			lineLen++;
+		alibi = parseString(Common::String(raw, lineLen),
+							_playerName, _partner);
+	}
+
+	// Digit-prefix ??? alibi balloon idx. CD has its own table at
+	// 29be:1050; floppy uses the same KD digit???balloon mapping for
+	// the alibi too (verified by inspecting `_DisplayAlibi_Floppy`'s
+	// `_GetKDTextBalloon_Floppy` call).
+	static const uint8 kDigitToBalloon[10] = {
+		0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1c, 0x1d, 0x1e, 0x0a
+	};
+	uint balloonIdx = 0x17;
+	if (!alibi.empty() && alibi[0] >= '0' && alibi[0] <= '9') {
+		balloonIdx = kDigitToBalloon[(int)(alibi[0] - '0')];
+		alibi.deleteChar(0);
+	}
+
+	// Compose alibi screen.
+	Graphics::ManagedSurface scene(320, 200,
+		Graphics::PixelFormat::createFormatCLUT8());
+	scene.clear();
+	if (haveAlibiBg)
+		scene.simpleBlitFrom(alibiBg.surface);
+	if (haveSuspect) {
+		const byte transp = (byte)(suspectPic.flags >> 8);
+		scene.transBlitFrom(suspectPic.surface,
+							 Common::Point(0x82, 0x5a),
+							 (uint32)transp);
+	}
+	Picture balloon;
+	const bool haveBalloon = _balloonArchive.size() > balloonIdx &&
+		_balloonArchive.loadEntry(balloonIdx, balloon);
+	int balloonX = 0x21;
+	int balloonY = 1;
+	if (haveBalloon) {
+		if (balloon.surface.h < 0x4e)
+			balloonY = (0x50 - balloon.surface.h) / 2;
+		const byte transp = (byte)(balloon.flags >> 8);
+		scene.transBlitFrom(balloon.surface,
+							 Common::Point(balloonX, balloonY),
+							 (uint32)transp);
+	}
+	uint16 tx = 5, ty = 4, tw = 155;
+	getBalloonInsets(balloonIdx, tx, ty, tw);
+	if (!alibi.empty()) {
+		_font.drawWordWrapped(&scene, balloonX + tx, balloonY + ty,
+							  MAX<int>(8, (int)tw), alibi, 0);
+	}
+	g_system->copyRectToScreen(scene.getPixels(), scene.pitch, 0, 0, 320, 200);
+	g_system->updateScreen();
+
+	// Voice ??? suspect alibi voice. The original picks per-suspect from
+	// the alibi voice table at `2608:0c5e + suspectIdx`; we don't have
+	// the table indexed, so play slot 13 (= F-0161SL.VOC / M-0113SL.VOC)
+	// as a generic alibi cue.
+	if (_audio)
+		_audio->playFloppyVoiceSlot(13, _partner);
+
+	while (!shouldQuit()) {
+		Common::Event ev;
+		bool advance = false;
+		while (g_system->getEventManager()->pollEvent(ev)) {
+			if (ev.type == Common::EVENT_QUIT ||
+				ev.type == Common::EVENT_RETURN_TO_LAUNCHER ||
+				ev.type == Common::EVENT_LBUTTONDOWN ||
+				ev.type == Common::EVENT_KEYDOWN) {
+				advance = true;
+				break;
+			}
+		}
+		if (advance)
+			break;
+		g_system->updateScreen();
+		g_system->delayMillis(10);
+	}
+
+	// Partner reaction ??? KDTextIndex slot 8 (= +0x10).
+	showFloppyKDHint(8);
+
+	_mystery._firstTry = false;
+	_nextScreen = _lastScreen != kScreenInvalid
+		? (ScreenId)_lastScreen : kScreenSite;
+}
+
 void EEMEngine::drawAccuseGallery(uint8 numSuspects, const byte *gd,
 								   int highlighted,
 								   Common::Array<Common::Rect> &slotRects,


Commit: 9c41e30159c5cdce8fdf1120bf5df3deeea5f953
    https://github.com/scummvm/scummvm/commit/9c41e30159c5cdce8fdf1120bf5df3deeea5f953
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:20:53+02:00

Commit Message:
EEM: make sure all the clues are there

Changed paths:
    engines/eem/clues.cpp
    engines/eem/site.cpp


diff --git a/engines/eem/clues.cpp b/engines/eem/clues.cpp
index 677566620eb..1420e324a7f 100644
--- a/engines/eem/clues.cpp
+++ b/engines/eem/clues.cpp
@@ -956,6 +956,29 @@ void EEMEngine::displayFloppyDialogRecords(const byte *rec, uint count,
 	const uint32 dsz       = _mystery.dataSize();
 	const uint32 notesBase = (uint32)(notes - bufBase);
 
+	// Pre-mark every text index across all records as "seen" up front.
+	// `_DisplayHotspotClue_Floppy @ 22dc:05c8` writes
+	// `_TextSeen_Floppy[idx] = 1` inside its render loop, but the
+	// original's `_WaitForClick` blocks until input ??? so the loop always
+	// runs to completion and every text gets marked. Our `waitForClick`
+	// honours ESC as "skip all" (sets `skipAll`/break), which matches
+	// player expectations for fast-forward but used to drop the seen-bit
+	// for every text after the ESC point. Pre-marking restores parity:
+	// ESC fast-forwards the *visual* but the notebook always reflects
+	// every clue the player would have seen if they'd clicked through.
+	{
+		const byte *r = rec;
+		for (uint i = 0; i < count; i++) {
+			const uint8 tc = r[10];
+			for (uint t = 0; t < tc; t++) {
+				const uint8 idx = r[11 + t] & 0x7f;
+				if (idx < Mystery::kCluesFoundCap)
+					_mystery._cluesFound[idx] = 1;
+			}
+			r += 11 + tc;
+		}
+	}
+
 	auto waitForClick = [&]() -> bool {
 		// Drain pending events first so a previous keystroke's tail
 		// doesn't auto-advance the new page.
diff --git a/engines/eem/site.cpp b/engines/eem/site.cpp
index 1add3abbd4c..9b07bfb5d49 100644
--- a/engines/eem/site.cpp
+++ b/engines/eem/site.cpp
@@ -1391,9 +1391,31 @@ void SiteScreen::onHotspotClicked(uint siteNum, uint hotIdx) {
 		if (hotIdx < Mystery::kHotSpotsCap)
 			_mystery->_hotSpotsSeen[hotIdx] = 1;
 		_mystery->_searchLocationNumber = (uint16)hotIdx;
+		// Snapshot `_cluesFound` BEFORE the floppy dialog so we can
+		// auto-save only when a new note (= clue text idx) is added.
+		// The floppy clue-side-effect path lives in
+		// `displayFloppyDialogRecords` (see clues.cpp), not in
+		// `displayClue` / `applyClueSideEffects`, so the CD-side
+		// autosave below would never trigger for floppy.
+		byte before[Mystery::kCluesFoundCap];
+		memcpy(before, _mystery->_cluesFound, sizeof(before));
 		_vm->setPartnerEraseBg(&_bgSnapshot);
 		_vm->displayFloppyHotspotDialog(siteNum, hotIdx);
 		_vm->setPartnerEraseBg(nullptr);
+		bool foundNewClue = false;
+		for (uint i = 0; i < Mystery::kCluesFoundCap; i++) {
+			if (!before[i] && _mystery->_cluesFound[i]) {
+				foundNewClue = true;
+				break;
+			}
+		}
+		if (foundNewClue) {
+			const Common::Error err =
+				_vm->saveProfile(_vm->playerName());
+			if (err.getCode() != Common::kNoError)
+				warning("auto-save after floppy clue failed: %s",
+						err.getDesc().c_str());
+		}
 		return;
 	}
 


Commit: c5336a89fde423fede72417d7583c5b3aa3b124f
    https://github.com/scummvm/scummvm/commit/c5336a89fde423fede72417d7583c5b3aa3b124f
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:20:54+02:00

Commit Message:
EEM: setup screen for floppy screen

Changed paths:
    engines/eem/ui.cpp


diff --git a/engines/eem/ui.cpp b/engines/eem/ui.cpp
index fc5b53f4b0a..289a90a3e06 100644
--- a/engines/eem/ui.cpp
+++ b/engines/eem/ui.cpp
@@ -820,15 +820,26 @@ void EEMEngine::doShowScrapbook(uint stage) {
 }
 
 void EEMEngine::doSetup() {
-	// Mirrors `_DoSetup @ 1f78:044e`. The setup screen is BG `PIC 0x40`
-	// (loaded once on entry) with every label baked in ??? "Setup",
-	// "Partner", "Sound", "Music", the "Jake"/"Jenny"/"On"/"Off"
-	// option strings, etc. ??? all rendered in palette key `0xFE`. The
-	// original then runs `_SetupSettings @ 1f78:000d` which uses
-	// `_SwapColors @ 172b:1d2a` to recolour those `0xFE` pixels per
-	// label rect: `0x15` for the active state, `0` for the inactive
-	// one. So nothing is drawn as text; the visible state of each
-	// toggle is purely a per-rect colour swap on top of `PIC 0x40`.
+	// Mirrors `_DoSetup @ 1f78:044e` (CD) and `_DoSetup_Floppy @
+	// 1ee2:0387` (floppy). Both variants share the same PIC 0x40 BG
+	// with text labels baked in, the same 13 button rectangles at
+	// `_SetupButtons @ 29be:1218` (CD) / `2608:0d8c` (floppy), and the
+	// same 4 highlight rectangles at `0xe94..0xeb3` (Kid1 / Kid2 /
+	// SoundOn / SoundOff). Floppy additionally pre-loads a few overlay
+	// PICs (0x9b..0x9e + 0x1fa) that the CD uses on demand, but the
+	// behaviour is identical ??? colour-key swap on `0xFE` to indicate
+	// active state, click dispatch via the same 12-entry handler
+	// jumptable. So a single shared handler covers both variants.
+	//
+	// The setup screen is BG `PIC 0x40` (loaded once on entry) with
+	// every label baked in ??? "Setup", "Partner", "Sound", "Music",
+	// the "Jake"/"Jenny"/"On"/"Off" option strings, etc. ??? all
+	// rendered in palette key `0xFE`. The original then runs
+	// `_SetupSettings @ 1f78:000d` which uses `_SwapColors @
+	// 172b:1d2a` to recolour those `0xFE` pixels per label rect:
+	// `0x15` for the active state, `0` for the inactive one. So
+	// nothing is drawn as text; the visible state of each toggle is
+	// purely a per-rect colour swap on top of `PIC 0x40`.
 	//
 	// Click hit-tests go through `_SetupButtons @ 29be:1218` ??? 13??
 	// 8-byte rects. Each click runs `HandleSetupButton @ 1f78:0158`,
@@ -2618,9 +2629,18 @@ void EEMEngine::doBigMap() {
 	uint32 mapLastTick = mapStartTick;
 
 	// Static rectangles read directly from the binary at the labelled
-	// addresses (29be:0x1596 onwards). Format is {x1, y1, x2, y2}.
+	// addresses (CD `29be:0x1596` / floppy `2608:0x13fe..0x143e`).
+	// Format is {x1, y1, x2, y2}. The floppy click table at
+	// `2608:1436` (verified at `_BigMapInteractionLoop_Floppy @
+	// 1fed:0a3a`) puts the setup button at (251, 3, 315, 42) ??? 1 px
+	// up and 1 px left of the CD's (252, 4, 315, 42). The floppy
+	// PIC 0x42 BG paints the visible button border at the same
+	// pixels, so use the variant-specific rect to match the
+	// hit-test region the original uses for that variant.
 	const Common::Rect kBigMapWindow   (  0,   0, 247, 192); // 29be:1596
-	const Common::Rect kSetupBtnRect   (252,   4, 315,  42); // 29be:15ce
+	const Common::Rect kSetupBtnRect   = isFloppy()
+		? Common::Rect(251, 3, 315, 42)   // 2608:1436
+		: Common::Rect(252, 4, 315, 42);  // 29be:15ce
 
 	bool wantZoom = false;
 	int zoomX = 0;
@@ -2749,7 +2769,9 @@ void EEMEngine::doBigMap() {
 				const Common::Rect kArrowXRight(224, 175, 234, 185);
 				const Common::Rect kXSlider    ( 15, 175, 221, 185);
 				const Common::Rect kYSlider    (237,  14, 247, 160);
-				const Common::Rect kSetupBtn   (252,   4, 315,  42);
+				const Common::Rect kSetupBtn = isFloppy()
+					? Common::Rect(251, 3, 315, 42)   // 2608:1436
+					: Common::Rect(252, 4, 315, 42);  // 29be:15ce
 
 				const int kArrowStep = 16;
 				const int kSliderRange = mapW - kMapWinW;


Commit: 021009a600d68e647c7195cf6bc31a590e2e32a9
    https://github.com/scummvm/scummvm/commit/021009a600d68e647c7195cf6bc31a590e2e32a9
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:20:54+02:00

Commit Message:
EEM: correctly validate clues in floppy version

Changed paths:
    engines/eem/ui.cpp


diff --git a/engines/eem/ui.cpp b/engines/eem/ui.cpp
index 289a90a3e06..479c062e623 100644
--- a/engines/eem/ui.cpp
+++ b/engines/eem/ui.cpp
@@ -1983,11 +1983,24 @@ void EEMEngine::drawNotebookFrame(int &page) {
 	int clueCursor = 0;
 	Common::Array<int> pageStarts;
 	pageStarts.push_back(0);
-	// Floppy NoteIndex entries are 7 bytes (`u16 ?; u16 jakeOff; u16
-	// jennyOff; u8 score`) with ABSOLUTE byte offsets into the mystery
-	// blob, while CD entries are 4 bytes with offsets relative to the
-	// TextBlock at header[+0xc]. Resolve the right text for the active
-	// partner / variant once per render.
+	// Floppy NoteIndex entries are 7 bytes:
+	//   +0..1  clue text offset (ABSOLUTE byte offset into the
+	//          mystery blob; partner-agnostic clue statement ???
+	//          this is what the notebook displays).
+	//   +2..3  Jake's spoken line offset (used by
+	//          `_DisplayHotspotClue_Floppy` when Jake narrates a
+	//          dialog record ??? NOT for the notebook).
+	//   +4..5  Jenny's spoken line offset (same, for Jenny).
+	//   +6     score.
+	// Verified at `_DrawNotes_Floppy / FUN_15e0_01e8`'s
+	// `*(int *)(notes + idx * 7)` access (no `+2`/`+4` shift ???
+	// always reads byte +0). The earlier port used the dialog
+	// spoken line for the notebook, which showed the partner's
+	// "Hi, Jake! Hi, Jenny!" intro instead of the actual clue
+	// statement ("We received a note that says..."). CD entries
+	// are 4 bytes with offsets relative to the TextBlock at
+	// header[+0xc]. Resolve the right text for the active variant
+	// once per render.
 	const bool floppyNb = isFloppy();
 	const byte *bufBase = _mystery.blobAt(0);
 	const uint32 mysSz  = _mystery.dataSize();
@@ -1995,10 +2008,7 @@ void EEMEngine::drawNotebookFrame(int &page) {
 		if (!ni || clueId >= niCount)
 			return Common::String();
 		if (floppyNb && bufBase) {
-			const uint stride = 7;
-			const uint16 textOff = (_partner == 0)
-				? READ_LE_UINT16(ni + clueId * stride + 2)
-				: READ_LE_UINT16(ni + clueId * stride + 4);
+			const uint16 textOff = READ_LE_UINT16(ni + clueId * 7);
 			if (textOff == 0 || textOff >= mysSz)
 				return Common::String();
 			const char *p = (const char *)(bufBase + textOff);
@@ -2332,24 +2342,46 @@ void EEMEngine::doGallery() {
 								continue;
 							if (!ni || clueId >= niCount)
 								continue;
-							// Floppy notes are 7-byte entries: u16 ?,
-							// u16 jakeOff, u16 jennyOff, u8 score. The
-							// partner-specific text offset is at +2
-							// (Jake) or +4 (Jenny) ??? verified at
-							// `FUN_22dc_05c8 @ 22dc:0843`. CD notes are
-							// 4 bytes: u16 textOff, u16 score.
-							uint16 textOff;
+							// Floppy notes are 7-byte entries:
+							//   +0..1 clue text (absolute offset)
+							//   +2..3 Jake spoken line
+							//   +4..5 Jenny spoken line
+							//   +6    score
+							// `_DrawNotes_Floppy / FUN_15e0_01e8`'s
+							// `*(int *)(notes + idx * 7)` shows the
+							// notebook always uses +0 ??? the partner-
+							// agnostic clue statement. The +2/+4
+							// offsets are the partner spoken lines used
+							// by `FUN_22dc_05c8` when rendering dialog
+							// records (NOT this notebook view). CD
+							// notes are 4 bytes: u16 textOff, u16
+							// score.
+							Common::String txt;
 							if (floppyMI) {
-								const uint partnerByte = (_partner == 0)
-									? 2 : 4;
-								textOff = READ_LE_UINT16(
-									ni + clueId * 7 + partnerByte);
+								const uint16 textOff =
+									READ_LE_UINT16(ni + clueId * 7);
+								const byte *bb = _mystery.blobAt(0);
+								const uint32 dsz =
+									_mystery.dataSize();
+								if (bb && textOff != 0 &&
+									textOff < dsz) {
+									const char *p =
+										(const char *)(bb + textOff);
+									uint32 len = 0;
+									while (textOff + len < dsz &&
+										   p[len] != 0)
+										len++;
+									txt = parseString(
+										Common::String(p, len),
+										_playerName, _partner);
+								}
 							} else {
-								textOff = READ_LE_UINT16(ni + clueId * 4);
+								const uint16 textOff =
+									READ_LE_UINT16(ni + clueId * 4);
+								txt = parseString(
+									_mystery.textAt(textOff),
+									_playerName, _partner);
 							}
-							Common::String txt =
-								parseString(_mystery.textAt(textOff),
-											_playerName, _partner);
 							if (txt.empty())
 								continue;
 							const byte color =
@@ -3162,6 +3194,13 @@ bool EEMEngine::doAccuseNotes() {
 	Picture accuseBg;
 	const bool haveBg = _picsArchive.getPicture(0x1a7, accuseBg);
 
+	// Reset selection on entry. `FUN_1d40_0e07 @ 1d40:0e34` (floppy)
+	// explicitly zeroes the 0x7f-byte `_NoteSelected_Floppy` array
+	// before drawing, so the player always starts with a clean board.
+	// Without this, leftover selections from a failed attempt persist
+	// and the post-selection 100-point gate gets the wrong sum.
+	memset(_mystery._noteSelected, 0, sizeof(_mystery._noteSelected));
+
 	// Required count for solving ??? `6 - chainStage`.
 	const uint expected = (_chainStage >= 1 && _chainStage <= 3)
 		? (uint)(6 - _chainStage)
@@ -3206,6 +3245,37 @@ bool EEMEngine::doAccuseNotes() {
 	int numPages = 1;
 	pageBreaks[0] = 0;
 
+	// Variant-aware text resolver. CD note entries are 4 bytes
+	// (u16 textOff RELATIVE to TextBlock, u16 score), so the offset
+	// is added to `_textOffset` via `Mystery::textAt`. Floppy
+	// entries are 7 bytes:
+	//   +0..1  clue text offset (ABSOLUTE byte offset into the
+	//          mystery blob ??? partner-agnostic clue statement;
+	//          this is what the notebook displays).
+	//   +2..3  Jake spoken-line offset (`FUN_22dc_05c8 @ 22dc:0843`).
+	//   +4..5  Jenny spoken-line offset.
+	//   +6     score (`FUN_1d40_0c48`).
+	// `_DrawNotes_Floppy / FUN_15e0_01e8` reads `*(int *)(notes +
+	// idx * 7)` ??? always byte +0 ??? for the notebook text.
+	// Reading +2/+4 here showed the partner's "Hi, Jake! Hi,
+	// Jenny!" intro instead of the actual clue statement.
+	const bool floppyNote = isFloppy();
+	const byte *bufBaseNotes = _mystery.blobAt(0);
+	auto noteText = [&](uint clueId) -> Common::String {
+		if (floppyNote) {
+			const uint16 textOff = READ_LE_UINT16(ni + clueId * 7);
+			if (textOff == 0 || textOff >= _mystery.dataSize() ||
+				!bufBaseNotes)
+				return Common::String();
+			return parseString(
+				(const char *)(bufBaseNotes + textOff),
+				_playerName, _partner);
+		}
+		const uint16 textOff = READ_LE_UINT16(ni + clueId * 4);
+		return parseString(_mystery.textAt(textOff),
+						   _playerName, _partner);
+	};
+
 	auto rebuildPagination = [&]() {
 		numPages = 1;
 		pageBreaks[0] = 0;
@@ -3214,11 +3284,8 @@ bool EEMEngine::doAccuseNotes() {
 		for (uint i = 0; i < found.size(); i++) {
 			const uint clueId = found[i];
 			Common::String txt;
-			if (clueId < niCount) {
-				const uint16 textOff = READ_LE_UINT16(ni + clueId * 4);
-				txt = parseString(_mystery.textAt(textOff),
-								   _playerName, _partner);
-			}
+			if (clueId < niCount)
+				txt = noteText(clueId);
 			Common::Array<Common::String> wrapped;
 			_font.wordWrapText(txt, rectW, wrapped);
 			const int h = (int)wrapped.size() * lineH;
@@ -3282,11 +3349,8 @@ bool EEMEngine::doAccuseNotes() {
 		for (int i = startIdx; i < endIdx; i++) {
 			const uint clueId = found[i];
 			Common::String txt;
-			if (clueId < niCount) {
-				const uint16 textOff = READ_LE_UINT16(ni + clueId * 4);
-				txt = parseString(_mystery.textAt(textOff),
-								   _playerName, _partner);
-			}
+			if (clueId < niCount)
+				txt = noteText(clueId);
 			if (txt.empty())
 				txt = Common::String::format(
 					isSpanish() ? "nota %u" : "clue %u", clueId);
@@ -3408,6 +3472,40 @@ bool EEMEngine::doAccuseNotes() {
 						const uint clueId = slotClues[i];
 						_mystery._noteSelected[clueId] ^= 1;
 						dirty = true;
+
+						// Debug: dump current user-selected score so
+						// the post-selection 100-point gate behaviour
+						// is visible while picking. Mirrors the
+						// floppy `FUN_1d40_0c48` (sum of `note[+6]`
+						// across `_NoteSelected != 0`) and the CD
+						// `selectedPoints()` (sum of `note[+2]` u16
+						// across `_NoteSelected != 0`).
+						{
+							int total = 0;
+							uint selectedCount = 0;
+							const uint maxIdx = MIN<uint>(niCount,
+								Mystery::kCluesFoundCap);
+							const bool floppy = isFloppy();
+							for (uint j = 0; j < maxIdx; j++) {
+								if (!_mystery._noteSelected[j])
+									continue;
+								selectedCount++;
+								if (floppy) {
+									total += (int)ni[j * 7 + 6];
+								} else {
+									const int16 pts =
+										(int16)READ_LE_UINT16(
+											ni + j * 4 + 2);
+									total += (int)pts;
+								}
+							}
+							warning("EEM accuse: clue %u %s "
+									"(selected=%u points=%d)",
+									clueId,
+									_mystery._noteSelected[clueId]
+										? "ON" : "OFF",
+									selectedCount, total);
+						}
 						break;
 					}
 				}
@@ -4361,6 +4459,53 @@ void EEMEngine::doAccuseFloppy() {
 		return;
 	}
 
+	// Clue selection screen ??? `FUN_1d40_0e07 @ 1d40:0e07`. The user
+	// must pick exactly `6 - chainStage` clues from the found list,
+	// rendered on the red accuse-mode BG (PIC 0x1A7). Verified by
+	// the asm at 1d40:0e34 reading `local_c = 6 - DAT_28da_3052`,
+	// matching the CD's `_DoAccuse @ 1df2:0bdd` expected count.
+	// `doAccuseNotes()` already handles this UI for both variants
+	// (note text reading is variant-aware via `noteTextOff`); it
+	// returns true on commit, false on ESC.
+	if (!doAccuseNotes()) {
+		_nextScreen = _lastScreen != kScreenInvalid
+			? (ScreenId)_lastScreen : kScreenSite;
+		return;
+	}
+
+	// Second score gate ??? based on the CLUES THE USER ACTUALLY
+	// PICKED. `_DoAccuse_Floppy @ 1d40:0e07` (after the commit
+	// branch at 1d40:0f3a) reads `local_a = FUN_1d40_0c48()` (=
+	// sum of byte +6 across selected note entries) and:
+	//   if (local_a < 100) ??? KDTextIndex[+6] hint (slot 3),
+	//                        bail back to last screen.
+	//   else              ??? call FUN_1d40_0c79 (gallery picker).
+	// Note this is distinct from the entry gate on
+	// `_GetSelectedPoints_Floppy @ 1d40:0c23` (which uses the
+	// auto-top-5 of FOUND clues ??? what `Mystery::selectedPoints`
+	// returns on floppy). A player can be "ready to solve" by score
+	// yet still pick the wrong subset; that's the case this gate
+	// catches.
+	int userSelectedScore = 0;
+	{
+		const byte *ni2 = _mystery.noteIndex();
+		const uint16 niCount2 = _mystery.noteIndexCount();
+		if (ni2) {
+			const uint maxIdx = MIN<uint>(niCount2,
+										   Mystery::kCluesFoundCap);
+			for (uint i = 0; i < maxIdx; i++) {
+				if (_mystery._noteSelected[i])
+					userSelectedScore += (int)ni2[i * 7 + 6];
+			}
+		}
+	}
+	if (userSelectedScore < 100) {
+		showFloppyKDHint(3);
+		_nextScreen = _lastScreen != kScreenInvalid
+			? (ScreenId)_lastScreen : kScreenSite;
+		return;
+	}
+
 	// `FUN_1d40_0c79` ??? gallery picker. Show "Which suspect?" KD
 	// hint at slot 4 (KDTextIndex[+8]/2 = entry index 4), then
 	// render the gallery and wait for click.
@@ -4504,28 +4649,146 @@ void EEMEngine::doAccuseFloppy() {
 	const bool guilty = _mystery.isGuilty((uint)picked);
 
 	if (guilty) {
-		// `_DisplayCorrect_Floppy @ 1d40:0894` ??? load PIC 5 (win BG),
-		// walk the solved-clue chain at header[+0x12]: byte0 = count,
-		// then `count` dialog records (same FUN_22dc_05c8 layout). We
-		// reuse `displayFloppyDialogRecords` for the rendering.
-		Picture winBg;
-		if (_picsArchive.getPicture(5, winBg))
-			blitAt(winBg, 0, 0);
-		setSitePalette(0x22);
-		g_system->updateScreen();
+		// Win path. Mirrors `_DisplayCorrect_Floppy @ 1d40:0894`:
+		//   1d40:08a0  _BuildBackground_Floppy(5, 0x42, 0x14);
+		//                 ??? PIC 0x3d frame + SITES entry 5 at (0x42,
+		//                   0x14), palette = sitenum + 1 = 6.
+		//   1d40:08b1  _FadeIn();
+		//   1d40:08c0  _MIDIPlayFile("travel-2.xmi");
+		//   1d40:08d0  walk solved chain via _DisplayHotspotClue_Floppy
+		//                 + _WaitForClick per record. Mid-recap: when
+		//                 only 3 records remain, play TITLE.ANM(0)
+		//                 (transition graphic; not yet ported).
+		//   1d40:0939  ((u16 *)0x3054)[mysteryNum] =
+		//                  _firstTry ? 2 : 1;
+		//   1d40:0941  tier-promotion check (advance _chainStage when
+		//                 every mystery in the current tier is solved).
+		//   1d40:0982  _SavePlayerRecord  (= saveProfile)
+		//   1d40:0985  _DeleteMysteryFile (= mystery cleanup)
+		//   1d40:09b0  _NextScreen = 0xc.
+		const uint mn = _mystery.number();
+
+		// Conclusion BG composition. `_BuildBackground_Floppy @
+		// 16e2:12fd` blits PIC 0x3d (the desk frame) onto a cleared
+		// page, then overlays SITES.DBD entry 5 (the conclusion
+		// artwork) at (param_2, param_3) = (0x42, 0x14). Palette
+		// becomes `sitenum + 1` = 6. Same composition the CD `doAccuse`
+		// win flow performs (see ui.cpp:4145-4166), just driven from
+		// the floppy data archives.
+		{
+			Graphics::Surface *blk = g_system->lockScreen();
+			if (blk) {
+				memset(blk->getPixels(), 0, 320 * 200);
+				g_system->unlockScreen();
+			}
+			setSitePalette(6);
+			Picture frame;
+			if (_picsArchive.loadEntry(0x3d, frame)) {
+				g_system->copyRectToScreen(frame.surface.getPixels(),
+					frame.surface.pitch, 0, 0,
+					frame.surface.w, frame.surface.h);
+			}
+			Picture scene;
+			if (5 < _sitesArchive.size() &&
+				_sitesArchive.loadEntry(5, scene)) {
+				const int sx = 0x42, sy = 0x14;
+				const int sw = MIN<int>(scene.surface.w, 320 - sx);
+				const int sh = MIN<int>(scene.surface.h, 200 - sy);
+				if (sw > 0 && sh > 0)
+					g_system->copyRectToScreen(scene.surface.getPixels(),
+						scene.surface.pitch, sx, sy, sw, sh);
+			}
 
+			// Partner sprite at (5, 0x50). The original keeps its
+			// `_NewAnimation` slot active across `_DisplayCorrect`'s
+			// `_BuildBackground_Floppy`, so `_UpdateAnimations` keeps
+			// re-stamping the partner over the fresh BG. We don't have
+			// a slot system, so manually bake the resting frame into
+			// the BG before the recap kicks off ??? `displayFloppyDialog
+			// Records` snapshots the screen on entry and the partner
+			// stays visible across every chain record. Without this
+			// the win sequence ends up partner-less. Anim 2 (Jake) /
+			// 0x10 (Jenny), script key 0x02 ??? same as the gallery
+			// picker and the CD accuse path (ui.cpp:4177).
+			const uint partnerAnim = (_partner == 0) ? 2 : 0x10;
+			Animation partnerAni;
+			if (_aniArchive.loadAnimation(partnerAnim, partnerAni) &&
+				!partnerAni.empty()) {
+				Graphics::Surface *screen = g_system->lockScreen();
+				if (screen) {
+					const uint frameIdx = partnerFrameAtTick(0x02,
+						(uint)partnerAni.size(),
+						g_system->getMillis());
+					blitAnimFrameAnchored(screen, partnerAni[frameIdx],
+										  5, 0x50);
+					g_system->unlockScreen();
+				}
+			}
+			g_system->updateScreen();
+		}
+
+		// Win music ??? TRAVEL-2.XMI (`2608:0c84`, played via
+		// `_MIDIPlayFile` at `_DisplayCorrect_Floppy @ 1d40:08c0`).
+		// The CD path uses internal MUS index 5; the floppy plays the
+		// XMI directly by filename. `_voiceOn` (= `DAT_28da_3050`) is
+		// the gate the original checks first.
+		if (_music && _voiceOn)
+			_music->playFile(Common::Path("travel-2.xmi"), false);
+
+		// Walk the solved-clue chain. Header[+0x12] points at a
+		// `count` byte followed by `count` dialog records (same layout
+		// as hotspot dialogs).
 		const byte *chain = _mystery.solvedClueBlock();
 		if (chain) {
 			const uint count = chain[0];
 			displayFloppyDialogRecords(chain + 1, count, 0);
 		}
 
-		// Mark mystery solved ??? equivalent to
-		// `((u16 *)0x5d20)... actually 0x3054[mysteryNum] = 1` which we
-		// translate to clearing the in-progress mystery + saving the
-		// profile (matches CD `doAccuse` win path).
-		if (_partner == 0)
-			_mystery._firstTry = false;
+		// Mark mystery solved with first-try bonus tracking.
+		// `_DisplayCorrect_Floppy @ 1d40:0939`:
+		//   ((u16 *)0x3054)[mysteryNum] = 1;
+		//   if (DAT_28da_35df != 0)
+		//       ((u16 *)0x3054)[iVar5] = 2;
+		// `DAT_28da_35df` is `_firstTry`; it starts at 1 on
+		// `_ReadMystery_Floppy` and is cleared to 0 by
+		// `_DisplayAlibi_Floppy` on a wrong accusation. So 2 = won on
+		// first try, 1 = won after at least one alibi.
+		if (mn < sizeof(_mysteriesSolved))
+			_mysteriesSolved[mn] = _mystery._firstTry ? 2 : 1;
+
+		// Tier-promotion check. `_DisplayCorrect_Floppy @
+		// 1d40:0941..0978` walks the current tier's mystery range and
+		// advances `DAT_28da_3052` (= `_chainStage`) when every entry
+		// is non-zero. Skip mystery 0 (practice case) per the
+		// `if (DAT_2608_149c != 0)` guard at 1d40:093f.
+		if (mn != 0) {
+			uint lo = 0, hi = 0;
+			switch (_chainStage) {
+			case 1: lo = 1;    hi = 0x18; break;
+			case 2: lo = 0x19; hi = 0x30; break;
+			case 3: lo = 0x31; hi = 0x36; break;
+			default: break;
+			}
+			bool allSolved = (hi >= lo);
+			for (uint i = lo; i <= hi && allSolved; i++) {
+				if (i >= sizeof(_mysteriesSolved) ||
+					_mysteriesSolved[i] == 0)
+					allSolved = false;
+			}
+			if (allSolved && _chainStage < 4) {
+				_chainStage++;
+				debugC(1, kDebugMystery,
+					   "chainStage advanced to %u after solving mystery %u",
+					   _chainStage, mn);
+			}
+		}
+
+		// Persist progress before clearing the in-progress mystery.
+		// `_DisplayCorrect_Floppy @ 1d40:0982` calls
+		// `_SavePlayerRecord` then `FUN_22dc_0dbd` (which deletes the
+		// per-mystery save file via DOS int 21h). Order matters here
+		// for the same reason as the CD path ??? clear ??? save so the
+		// profile records `hasMystery=false`.
 		_mystery._solvedPuzzle = true;
 		_mystery.clear();
 		(void)saveProfile(_playerName);
@@ -4534,15 +4797,24 @@ void EEMEngine::doAccuseFloppy() {
 	}
 
 	// Innocent ??? `_DisplayAlibi_Floppy @ 1d40:00df`:
-	//   1. Load PIC 0x3e (alibi BG), draw at (0x42, 0x14).
-	//   2. Load suspect picID, draw portrait at (0x82, 0x5a).
-	//   3. Read alibi text via TEXT_BLOCK[suspect[+3] * 2]; render in
-	//      a balloon picked by digit-prefix dispatch.
-	//   4. Voice from per-partner table at suspect_entry[+5..]
-	//      (we approximate using slot 13/14 ??? Jake/Jenny "wrong"
-	//      sting; original picks per-suspect).
-	//   5. Wait for click, then KD reaction balloon
-	//      (KDTextIndex[+0x10]/2 = slot 8) + voice slot 5.
+	//   1d40:00fb  _GetBackground(0x3e);
+	//   1d40:0103  _GetPicture(suspect.picID);
+	//   1d40:010c  _AddPicBackground(susp_pic, 0x82, 0x5a);
+	//   1d40:0125  _MIDIPlayFile("fanfare2.xmi");  // alibi sting
+	//   1d40:0145  alibi-balloon table at `2608:0c0a + first_char`
+	//                  (separate from the KD digit???balloon table at
+	//                  `2608:0c14` ??? yes, 0xc0a, NOT 0xc14, verified
+	//                  via the `*(char *)(*local_a + 0xc0a)` access).
+	//                  Default = `DAT_2608_0c3c` = 0x2c.
+	//   1d40:01a3  balloon centred:
+	//                  bx = (0x140 - balloon.w) / 2;
+	//                  by = (0x5a  - balloon.h) / 2;
+	//   1d40:01d5  WordWrap alibi text inside balloon, _WaitForClick.
+	//   1d40:01ee  KD reaction at KDTextIndex[+10] = slot 5
+	//                  (NOT slot 8 ??? slot 8 is the gallery prompt).
+	//                  Balloon at (0x21, (0x50 - h) / 2).
+	//   1d40:0247  _NextScreen = _LastScreen;
+	//   1d40:024b  _firstTry = 0;
 	const byte *susp = _mystery.floppySuspectEntry((uint)picked);
 	uint16 picId = 0;
 	uint16 alibiOff = 0xFFFF;
@@ -4551,6 +4823,11 @@ void EEMEngine::doAccuseFloppy() {
 		alibiOff = _mystery.alibiTextOffset((uint)picked);
 	}
 
+	// Alibi-screen MIDI sting (FANFARE2.XMI). `_MIDIPlayFile` is
+	// gated on `_voiceOn` (= `DAT_28da_3050`) in the original.
+	if (_music && _voiceOn)
+		_music->playFile(Common::Path("fanfare2.xmi"), false);
+
 	Picture alibiBg;
 	const bool haveAlibiBg = _picsArchive.getPicture(0x3e, alibiBg);
 	Picture suspectPic;
@@ -4567,18 +4844,24 @@ void EEMEngine::doAccuseFloppy() {
 							_playerName, _partner);
 	}
 
-	// Digit-prefix ??? alibi balloon idx. CD has its own table at
-	// 29be:1050; floppy uses the same KD digit???balloon mapping for
-	// the alibi too (verified by inspecting `_DisplayAlibi_Floppy`'s
-	// `_GetKDTextBalloon_Floppy` call).
-	static const uint8 kDigitToBalloon[10] = {
-		0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1c, 0x1d, 0x1e, 0x0a
+	// Alibi balloon dispatch. The original reads the alibi-balloon
+	// table at `2608:0c0a + first_char` ??? a SEPARATE table from the
+	// KD-hint table at `2608:0c14`. For digits '0'..'9' the values at
+	// `2608:0c3a..0c43` are { 0x2a, 0x2b, 0x2c, 0x2d, 0xaa, 0xab,
+	// 0xac, 0xad, 0x09, 0x0a }; default (non-digit char) is
+	// `DAT_2608_0c3c` = 0x2c. The high-bit values (0xAA..0xAD) are
+	// `_GetBalloon`'s "mirrored" flag ??? the low 7 bits are the
+	// balloon idx, top bit flips horizontally.
+	static const uint8 kFloppyAlibiBalloonByDigit[10] = {
+		0x2a, 0x2b, 0x2c, 0x2d, 0xaa, 0xab, 0xac, 0xad, 0x09, 0x0a
 	};
-	uint balloonIdx = 0x17;
+	uint balloonRaw = 0x2c; // default `DAT_2608_0c3c`
 	if (!alibi.empty() && alibi[0] >= '0' && alibi[0] <= '9') {
-		balloonIdx = kDigitToBalloon[(int)(alibi[0] - '0')];
+		balloonRaw = kFloppyAlibiBalloonByDigit[(int)(alibi[0] - '0')];
 		alibi.deleteChar(0);
 	}
+	const uint balloonIdx = balloonRaw & 0x7F;
+	const bool flipBalloon = (balloonRaw & 0x80) != 0;
 
 	// Compose alibi screen.
 	Graphics::ManagedSurface scene(320, 200,
@@ -4595,15 +4878,35 @@ void EEMEngine::doAccuseFloppy() {
 	Picture balloon;
 	const bool haveBalloon = _balloonArchive.size() > balloonIdx &&
 		_balloonArchive.loadEntry(balloonIdx, balloon);
+	// Centred per `_DisplayAlibi_Floppy @ 1d40:01a0`:
+	//   uVar2 = (0x140 - balloon.w) / 2;  // x
+	//   uVar3 = (0x5a  - balloon.h) / 2;  // y (anchor in top-half)
 	int balloonX = 0x21;
 	int balloonY = 1;
 	if (haveBalloon) {
-		if (balloon.surface.h < 0x4e)
-			balloonY = (0x50 - balloon.surface.h) / 2;
+		balloonX = (320 - balloon.surface.w) / 2;
+		balloonY = (0x5a - balloon.surface.h) / 2;
+		if (balloonX < 0) balloonX = 0;
+		if (balloonY < 0) balloonY = 0;
 		const byte transp = (byte)(balloon.flags >> 8);
-		scene.transBlitFrom(balloon.surface,
-							 Common::Point(balloonX, balloonY),
-							 (uint32)transp);
+		// `_GetBalloon`'s mirror flag (high bit of the table value)
+		// flips the balloon horizontally ??? the original applies it
+		// inside the blit primitive. We emulate by reading the source
+		// row in reverse.
+		for (int row = 0; row < balloon.surface.h && balloonY + row < 200;
+			 row++) {
+			const byte *src =
+				(const byte *)balloon.surface.getBasePtr(0, row);
+			byte *dst = (byte *)scene.getBasePtr(balloonX, balloonY + row);
+			for (int col = 0;
+				 col < balloon.surface.w && balloonX + col < 320; col++) {
+				const int srcCol = flipBalloon
+					? (balloon.surface.w - 1 - col) : col;
+				const byte px = src[srcCol];
+				if (px != transp)
+					dst[col] = px;
+			}
+		}
 	}
 	uint16 tx = 5, ty = 4, tw = 155;
 	getBalloonInsets(balloonIdx, tx, ty, tw);
@@ -4614,12 +4917,11 @@ void EEMEngine::doAccuseFloppy() {
 	g_system->copyRectToScreen(scene.getPixels(), scene.pitch, 0, 0, 320, 200);
 	g_system->updateScreen();
 
-	// Voice ??? suspect alibi voice. The original picks per-suspect from
-	// the alibi voice table at `2608:0c5e + suspectIdx`; we don't have
-	// the table indexed, so play slot 13 (= F-0161SL.VOC / M-0113SL.VOC)
-	// as a generic alibi cue.
-	if (_audio)
-		_audio->playFloppyVoiceSlot(13, _partner);
+	// `_DisplayAlibi_Floppy` does NOT play any per-suspect VOC ??? the
+	// alibi table at `2608:0c5e` (3 bytes: 0x15, 0x16, 0x17) is
+	// referenced by the post-win scrapbook (`FUN_1d40_05b7`), not the
+	// alibi screen. The MIDI sting we kicked off above carries the
+	// audio.
 
 	while (!shouldQuit()) {
 		Common::Event ev;
@@ -4639,8 +4941,13 @@ void EEMEngine::doAccuseFloppy() {
 		g_system->delayMillis(10);
 	}
 
-	// Partner reaction ??? KDTextIndex slot 8 (= +0x10).
-	showFloppyKDHint(8);
+	// Partner reaction ??? `KDTextIndex[+10]` is byte offset 10 = u16
+	// stride entry 5 (NOT 8). Verified at
+	// `_DisplayAlibi_Floppy @ 1d40:01ee`:
+	//   `iVar4 = MysteryOff + *(KDTextIndex + 10);`
+	// Our `showFloppyKDHint(slot)` reads `kdIdx + slot * 2`, so slot
+	// 5 is the right argument here.
+	showFloppyKDHint(5);
 
 	_mystery._firstTry = false;
 	_nextScreen = _lastScreen != kScreenInvalid


Commit: 7aa9357fb6ba37941294d886d596224b222a2cab
    https://github.com/scummvm/scummvm/commit/7aa9357fb6ba37941294d886d596224b222a2cab
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:20:54+02:00

Commit Message:
EEM: simplify savegame handling

Changed paths:
    engines/eem/eem.cpp
    engines/eem/eem.h
    engines/eem/mystery.cpp


diff --git a/engines/eem/eem.cpp b/engines/eem/eem.cpp
index 03a2449d26b..5f1d15f0c15 100644
--- a/engines/eem/eem.cpp
+++ b/engines/eem/eem.cpp
@@ -58,12 +58,7 @@ const uint kPalEAKids          = 0x25;
 const uint kPalHighScore       = 0x27;
 const uint kPalStormLogo       = 0x26;  ///< Floppy `FUN_23d2_0605` palette idx
 
-// Save body version, used by the `Common::Serializer` inside
-// `saveGameStream`/`loadGameStream`. The framework's extended-save
-// header (description / thumbnail / playtime) is appended/parsed
-// separately by `Engine::saveGameState` / `MetaEngine::readSavegameHeader`,
-// so we don't need a magic word or our own metadata fields.
-const byte kSaveBodyVer = 3;
+const byte kSaveBodyVer = 1;
 
 // 11x16 mouse cursor ??? replaces the DOS hardware cursor wired in by
 // _InitMouse @ 152d:018b (INT 33h). The original game sets the cursor
@@ -797,9 +792,8 @@ Common::Error EEMEngine::saveGameStream(Common::WriteStream *stream,
 	(void)isAutosave;
 
 	// Body header: one byte version. `Common::Serializer::setVersion`
-	// alone doesn't write/read the version ??? we emit it explicitly so
-	// `loadGameStream` knows which fields are present. Older saves
-	// (v1) lack `_chainStage`; newer ones include it.
+	// alone doesn't write/read the version, so emit it explicitly and
+	// require an exact match on load.
 	Common::Serializer s(nullptr, stream);
 	s.setVersion(kSaveBodyVer);
 	byte ver = kSaveBodyVer;
@@ -821,16 +815,12 @@ Common::Error EEMEngine::saveGameStream(Common::WriteStream *stream,
 	//                  2=solved on first try) ??? `_DisplayCorrect`
 	//                  writes 1 always, 2 when `_FirstTry != 0`.
 	//
-	// We persist the gameplay-meaningful subset (name + solved table +
-	// partner) and skip the filename-derivation bytes. The voice /
-	// chain-stage fields aren't yet wired into the C++ port; we save
-	// space for them so they slot in without a version bump.
+	// We persist the gameplay-meaningful subset and skip the original
+	// filename-derivation bytes.
 	s.syncString(_playerName);
 	s.syncBytes(_mysteriesSolved, sizeof(_mysteriesSolved));
 	s.syncAsByte(_partner);
-	// v2+: chain-stage tier (1=Junior, 2=Senior, 3=Master).
 	s.syncAsByte(_chainStage);
-	// v3+: voice on/off flag (DAT_2d5d_3f97).
 	s.syncAsByte(_voiceOn);
 
 	// ScummVM-only extension: persist the in-progress mystery so the
@@ -858,8 +848,8 @@ Common::Error EEMEngine::loadGameStream(Common::SeekableReadStream *stream) {
 	Common::Serializer s(stream, nullptr);
 	byte ver = 0;
 	s.syncAsByte(ver);
-	if (ver > kSaveBodyVer) {
-		warning("loadGameStream: save body version %u newer than %u ??? refusing",
+	if (ver != kSaveBodyVer) {
+		warning("loadGameStream: unsupported save body version %u (expected %u)",
 				ver, kSaveBodyVer);
 		return Common::kReadingFailed;
 	}
diff --git a/engines/eem/eem.h b/engines/eem/eem.h
index e78e44066c3..44a90e4fdf4 100644
--- a/engines/eem/eem.h
+++ b/engines/eem/eem.h
@@ -142,9 +142,8 @@ public:
 	// ScummVM extended-save hooks. The base `Engine::saveGameState` /
 	// `loadGameState` write/read the framework header (description,
 	// thumbnail, playtime, version) around our body via these
-	// streams. We keep all per-profile state in the body, with a
-	// single `Common::Serializer` version so future field additions
-	// stay backward-compatible.
+	// streams. We keep all per-profile state in the body and only
+	// accept the current private body layout.
 	Common::Error saveGameStream(Common::WriteStream *stream,
 								  bool isAutosave = false) override;
 	Common::Error loadGameStream(Common::SeekableReadStream *stream) override;
diff --git a/engines/eem/mystery.cpp b/engines/eem/mystery.cpp
index 06ce1d53f7b..3b6cb6b3c98 100644
--- a/engines/eem/mystery.cpp
+++ b/engines/eem/mystery.cpp
@@ -564,22 +564,6 @@ void Mystery::syncState(Common::Serializer &s) {
 	s.syncArray(_hotSpotsSeen, kHotSpotsCap, Common::Serializer::Uint16LE);
 	s.syncArray(_inGallery,    kGalleryCap,  Common::Serializer::Uint16LE);
 	s.syncBytes(_newOrder, kGalleryCap);
-	// Save profiles created before the floppy `_newOrder` identity init
-	// was added stored all-zeros here. Loading that back over the
-	// identity set by `Mystery::load()` makes every dialog with byte9>0
-	// resolve to slot 0 (so the second suspect found at a CONSITE
-	// overwrites the first). Detect the legacy zero-fill on read and
-	// rebuild the identity mapping that the loader produced.
-	if (s.isLoading()) {
-		bool allZero = true;
-		for (uint i = 0; i < kGalleryCap; i++) {
-			if (_newOrder[i] != 0) { allZero = false; break; }
-		}
-		if (allZero) {
-			for (uint i = 0; i < kGalleryCap; i++)
-				_newOrder[i] = (uint8)i;
-		}
-	}
 	s.syncArray(_visitedSite, kVisitedSiteCap, Common::Serializer::Uint16LE);
 	s.syncArray(_onSites,     kVisitedSiteCap, Common::Serializer::Uint16LE);
 	s.syncAsByte(_sawCOFFSITEs);


Commit: 2b45b48e89ec1b28769d043fe3a43feb879ea9cd
    https://github.com/scummvm/scummvm/commit/2b45b48e89ec1b28769d043fe3a43feb879ea9cd
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:20:55+02:00

Commit Message:
EEM: fix invalid state transition in the handling of the setup screen

Changed paths:
    engines/eem/eem.cpp
    engines/eem/site.cpp
    engines/eem/ui.cpp


diff --git a/engines/eem/eem.cpp b/engines/eem/eem.cpp
index 5f1d15f0c15..9be01f7b182 100644
--- a/engines/eem/eem.cpp
+++ b/engines/eem/eem.cpp
@@ -392,8 +392,9 @@ screen_loop:
 		case kScreenMap:
 		case kScreenMapAlt:
 			// Handler 1/2 at 1a35:0e25 calls `_DoMapScreen @
-			// 20fe:120b` which manages its own `_NextScreen` writes ???
-			// 3 (a site was clicked), 6 (setup), or 0xffff (quit).
+			// 20fe:120b` (floppy: 19bb:0ef3 -> 1fed map code),
+			// which manages its own `_NextScreen` writes ??? 3 (a site
+			// was clicked), 6 (setup), or 0xffff (quit).
 			// Our `doBigMap` keeps the original's "click site, then
 			// enter the site loop" behaviour inline; once it returns
 			// the natural next state is SITE.
@@ -406,14 +407,9 @@ screen_loop:
 
 		case kScreenSite:
 			// Handler 3 at 1a35:0e2c calls `_DoSiteLoop @
-			// 168d:03f4`. Our `doSiteLoop` is a complete loop ???
-			// notebook / gallery / accuse / map are dispatched
-			// inline within `SiteScreen::run`. The accusation tail
-			// in `doAccuse` (see ui.cpp) writes the next screen:
-			// kScreenAction on win (matches `_DisplayCorrect @
-			// 1df2:0895` writing 0xc) or kScreenSite on lose
-			// (matches `_DisplayAlibi @ 1df2:043f` snapping back
-			// via `_LastScreen`).
+			// 168d:03f4` (floppy's equivalent dispatches through
+			// 1652). Site writes `_NextScreen` for PDA / map rather
+			// than entering those screens as nested modals.
 			doSiteLoop();
 			if (!_mystery.isLoaded())
 				_nextScreen = kScreenChooseMystery;
@@ -421,6 +417,28 @@ screen_loop:
 				_nextScreen = kScreenInvalid;  // user quit
 			break;
 
+		case kScreenNotebook:
+			// Handler 4 calls the PDA notebook screen. Its button
+			// handler writes 2 (map), 3 (site), 5 (gallery), or 7
+			// (accuse) and then returns to this dispatcher.
+			doNotebook();
+			if (!_mystery.isLoaded() && _nextScreen != kScreenAction)
+				_nextScreen = kScreenChooseMystery;
+			else if (_nextScreen == current)
+				_nextScreen = kScreenSite;
+			break;
+
+		case kScreenGallery:
+			// Handler 5 calls the suspect gallery. Like the original,
+			// ESC and the site button write 3, the map button writes
+			// 2, and the PDA button writes 4.
+			doGallery();
+			if (!_mystery.isLoaded() && _nextScreen != kScreenAction)
+				_nextScreen = kScreenChooseMystery;
+			else if (_nextScreen == current)
+				_nextScreen = kScreenSite;
+			break;
+
 		case kScreenSetup:
 			// Handler 6 at 1a35:0e48 calls `_DoSetup @ 1f78:044e`.
 			// Reachable via the BigMap setup button which writes
@@ -431,6 +449,17 @@ screen_loop:
 			doSetup();
 			break;
 
+		case kScreenAccuse:
+			// Handler 7 runs the accusation flow. A failed accusation
+			// returns to `_LastScreen`; a correct solution writes
+			// 0xc (ACTION).
+			doAccuse();
+			if (!_mystery.isLoaded() && _nextScreen != kScreenAction)
+				_nextScreen = kScreenChooseMystery;
+			else if (_nextScreen == current)
+				_nextScreen = kScreenSite;
+			break;
+
 		default:
 			warning("screenDriver: unhandled screen id %d", (int)current);
 			_nextScreen = kScreenInvalid;
diff --git a/engines/eem/site.cpp b/engines/eem/site.cpp
index 9b07bfb5d49..0938072384b 100644
--- a/engines/eem/site.cpp
+++ b/engines/eem/site.cpp
@@ -695,7 +695,8 @@ void SiteScreen::run() {
 				//   Button 0: (35, 111) - (56, 136)  ??? notebook
 				//                                       (`_NextScreen = 4`)
 				//   Button 1: (7, 177)  - (57, 200)  ??? map
-				//                                       (`_NextScreen = 1`)
+				//                                       (CD `_NextScreen = 1`,
+				//                                       floppy = 2)
 				// Test the buttons before falling through to hotspots so
 				// a click on the PDA / map icon doesn't accidentally
 				// trigger a hotspot underneath.
@@ -712,16 +713,14 @@ void SiteScreen::run() {
 				// the PDA / gallery `kBtnPartner` (5, 80, 44, 110).
 				const Common::Rect kBtnPartner ( 5,  80, 44, 110);
 				if (kBtnNotebook.contains(event.mouse.x, event.mouse.y)) {
-					_vm->doNotebook();
-					enter(cur);
-					break;
+					_vm->setNextScreen(kScreenNotebook);
+					return;
 				}
 				if (kBtnMap.contains(event.mouse.x, event.mouse.y)) {
-					_vm->doBigMap();
-					if (_mystery->_siteNumber < _mystery->numSites())
-						cur = _mystery->_siteNumber;
-					enter(cur);
-					break;
+					// CD writes `_NextScreen = 1`; floppy writes 2.
+					_vm->setNextScreen(_vm->isFloppy() ? kScreenMapAlt
+													   : kScreenMap);
+					return;
 				}
 				if (kBtnPartner.contains(event.mouse.x, event.mouse.y)) {
 					_vm->doHelp();
@@ -748,7 +747,8 @@ void SiteScreen::run() {
 				// ??? MAP).
 				if (event.kbd.keycode == Common::KEYCODE_ESCAPE) {
 					if (_vm->areYouSure()) {
-						_vm->setNextScreen(kScreenMap);
+						_vm->setNextScreen(_vm->isFloppy() ? kScreenMapAlt
+														   : kScreenMap);
 						return;
 					}
 					enter(cur);
@@ -762,10 +762,7 @@ void SiteScreen::run() {
 		if (exitRequested)
 			return;
 
-		// `doAccuse` on a win clears the mystery (so the screen driver
-		// can route to the post-mystery menu). Notebook / Gallery /
-		// hotspot paths route through this same loop, so a transitive
-		// `doAccuse` may have wiped `_mystery` underneath us ??? exit
+		// Hotspot side effects can invalidate the active mystery; exit
 		// immediately rather than tick another frame against stale BG
 		// snapshots / hotspot tables.
 		if (!_mystery || !_mystery->isLoaded())
diff --git a/engines/eem/ui.cpp b/engines/eem/ui.cpp
index 479c062e623..06c0377e833 100644
--- a/engines/eem/ui.cpp
+++ b/engines/eem/ui.cpp
@@ -1812,11 +1812,13 @@ void EEMEngine::doNotebook() {
 		while (g_system->getEventManager()->pollEvent(ev)) {
 			if (ev.type == Common::EVENT_QUIT ||
 				ev.type == Common::EVENT_RETURN_TO_LAUNCHER) {
+				_nextScreen = kScreenInvalid;
 				exitFlag = true;
 				break;
 			}
 			if (ev.type == Common::EVENT_KEYDOWN) {
 				if (ev.kbd.keycode == Common::KEYCODE_ESCAPE) {
+					_nextScreen = kScreenSite;
 					exitFlag = true;
 					break;
 				}
@@ -1838,11 +1840,12 @@ void EEMEngine::doNotebook() {
 				// rects directly. Earlier rects "win" when overlapping
 				// (matches `_FindButton`).
 				if (kBtnSite.contains(ev.mouse.x, ev.mouse.y)) {
+					_nextScreen = kScreenSite;
 					exitFlag = true;
 					break;  // back to site
 				}
 				if (kBtnMap.contains(ev.mouse.x, ev.mouse.y)) {
-					doBigMap();
+					_nextScreen = kScreenMapAlt;
 					exitFlag = true;
 					break;
 				}
@@ -1852,14 +1855,14 @@ void EEMEngine::doNotebook() {
 					continue;
 				}
 				if (kBtnAccuse.contains(ev.mouse.x, ev.mouse.y)) {
-					doAccuse();
+					_nextScreen = kScreenAccuse;
 					exitFlag = true;
 					break;
 				}
 				if (kBtnGallery.contains(ev.mouse.x, ev.mouse.y)) {
-					doGallery();
-					dirty = true;
-					continue;
+					_nextScreen = kScreenGallery;
+					exitFlag = true;
+					break;
 				}
 				if (kBtnHelp1.contains(ev.mouse.x, ev.mouse.y)) {
 					// rect 1 ??? `_InterfaceHelp(0)`: walks `HelpData[0]` and
@@ -2161,10 +2164,12 @@ void EEMEngine::doGallery() {
 		while (g_system->getEventManager()->pollEvent(ev)) {
 			if (ev.type == Common::EVENT_QUIT ||
 				ev.type == Common::EVENT_RETURN_TO_LAUNCHER) {
+				_nextScreen = kScreenInvalid;
 				return;
 			}
 			if (ev.type == Common::EVENT_KEYDOWN) {
 				if (ev.kbd.keycode == Common::KEYCODE_ESCAPE) {
+					_nextScreen = kScreenSite;
 					exitFlag = true;
 					break;
 				}
@@ -2192,18 +2197,19 @@ void EEMEngine::doGallery() {
 				const Common::Rect kBtnHelp    ( 93, 174, 115, 190); // [1] HELP
 				const Common::Rect kBtnPartner (  5,  80,  44, 110); // [3] KD HELP
 				if (kBtnSite.contains(ev.mouse.x, ev.mouse.y)) {
+					_nextScreen = kScreenSite;
 					exitFlag = true; break;
 				}
 				if (kBtnMap.contains(ev.mouse.x, ev.mouse.y)) {
-					doBigMap();
+					_nextScreen = kScreenMapAlt;
 					exitFlag = true; break;
 				}
 				if (kBtnAccuse.contains(ev.mouse.x, ev.mouse.y)) {
-					doAccuse();
+					_nextScreen = kScreenAccuse;
 					exitFlag = true; break;
 				}
 				if (kBtnNotebook.contains(ev.mouse.x, ev.mouse.y)) {
-					// Already came from notebook; exiting returns to it.
+					_nextScreen = kScreenNotebook;
 					exitFlag = true; break;
 				}
 				if (kBtnHelp.contains(ev.mouse.x, ev.mouse.y)) {


Commit: 1cf324b52d5caa884d4ba69809121b48d356202e
    https://github.com/scummvm/scummvm/commit/1cf324b52d5caa884d4ba69809121b48d356202e
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:20:55+02:00

Commit Message:
EEM: correctly save and reload viewed clues

Changed paths:
    engines/eem/site.cpp
    engines/eem/site.h


diff --git a/engines/eem/site.cpp b/engines/eem/site.cpp
index 0938072384b..a36c0188248 100644
--- a/engines/eem/site.cpp
+++ b/engines/eem/site.cpp
@@ -1297,10 +1297,11 @@ void SiteScreen::renderHotspots(uint siteNum) {
 	const uint32 tickMs = g_system->getMillis();
 
 	// CD hotspot rows are 14 bytes each (rect + 6 bytes of clue
-	// metadata). Floppy stores plain 8-byte rectangles only ??? clue
-	// data lives in a separate dialog-record list at `site_data[+6]`,
-	// keyed by hotspot index. Verified at `FUN_22dc_0b80 @ 22dc:0b80`.
-	const uint stride = _vm && _vm->isFloppy() ? 8 : 14;
+	// metadata). The seen key is the zero-based mystery-wide ordinal
+	// at +0xa, not this site's local row index. Floppy stores plain
+	// 8-byte rectangles only, so its seen key remains the row index.
+	const bool floppy = _vm && _vm->isFloppy();
+	const uint stride = floppy ? 8 : 14;
 	for (uint i = 0; i < count; i++) {
 		const byte *r = spots + i * stride;
 		const int16 x1 = (int16)READ_LE_UINT16(r + 0);
@@ -1310,8 +1311,9 @@ void SiteScreen::renderHotspots(uint siteNum) {
 		const Common::Rect rect(MAX<int>(0, x1), MAX<int>(0, y1),
 								MIN<int>(screen->w, x2),
 								MIN<int>(screen->h, y2));
-		const bool seen = (i < Mystery::kHotSpotsCap)
-						   && _mystery->_hotSpotsSeen[i];
+		const uint seenKey = floppy ? i : READ_LE_UINT16(r + 0xa);
+		const bool seen = seenKey < Mystery::kHotSpotsCap &&
+						   _mystery->_hotSpotsSeen[seenKey];
 		if (seen) {
 			// `_DrawSolidRect` (172b:0506) ??? outline in palette
 			// index 0xFF (a fixed, non-cycling colour) so already-
@@ -1418,10 +1420,10 @@ void SiteScreen::onHotspotClicked(uint siteNum, uint hotIdx) {
 
 	// `_DoSiteLoop @ 168d:03f4` (after _DisplayClue):
 	//   _HotSpotsSeen[hotspot[+0xa] * 2] = _HotSpotComplete;
-	// The "seen" key is the hotspotIndex field (+0xa) ??? the 1-based
-	// ordinal ??? NOT the array index. Two hotspots can share an ordinal
-	// across sites (e.g., a partner's clue you can re-read), so this
-	// matters for cross-site state.
+	// The seen key is the hotspotIndex field (+0xa) ??? a zero-based
+	// mystery-wide ordinal ??? NOT the site's local row index. Using the
+	// local index makes unrelated hotspots on later sites inherit the
+	// first site's seen state after travel or reload.
 	const byte *spots = _mystery->hotspots(siteNum);
 	uint hotOrdinal = hotIdx; // fallback to array index
 	if (spots) {
@@ -1429,8 +1431,6 @@ void SiteScreen::onHotspotClicked(uint siteNum, uint hotIdx) {
 	}
 	if (hotOrdinal < Mystery::kHotSpotsCap)
 		_mystery->_hotSpotsSeen[hotOrdinal] = 1;
-	if (hotIdx < Mystery::kHotSpotsCap)
-		_mystery->_hotSpotsSeen[hotIdx] = 1;  // also mark by array idx for our render
 	_mystery->_searchLocationNumber = (uint16)hotIdx;
 
 	// Bytes 8..9 of each 14-byte hotspot rect = byte offset within the
diff --git a/engines/eem/site.h b/engines/eem/site.h
index 23621409806..97e83c087e4 100644
--- a/engines/eem/site.h
+++ b/engines/eem/site.h
@@ -80,7 +80,7 @@ void blitAnimFrameAnchored(Graphics::Surface *screen, const Picture &p,
 struct Hotspot {
 	int16  x1, y1, x2, y2;     ///< rectangle in screen coordinates
 	uint16 clueOffset;          ///< +8: byte offset of ClueBlock in the mystery file
-	uint16 hotspotIndex;        ///< +10: 1-based hotspot ordinal within the site
+	uint16 hotspotIndex;        ///< +10: zero-based mystery-wide seen ordinal
 	uint16 extra;               ///< +12: unknown (zero in M0..M54)
 
 	Common::Rect rect() const { return Common::Rect(x1, y1, x2, y2); }


Commit: ec75e6ec08ee26087cb3fdd2ee290504ede16aa3
    https://github.com/scummvm/scummvm/commit/ec75e6ec08ee26087cb3fdd2ee290504ede16aa3
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:20:55+02:00

Commit Message:
EEM: avoid saving slot 0

Changed paths:
    engines/eem/eem.cpp
    engines/eem/eem.h


diff --git a/engines/eem/eem.cpp b/engines/eem/eem.cpp
index 9be01f7b182..8326cf54c36 100644
--- a/engines/eem/eem.cpp
+++ b/engines/eem/eem.cpp
@@ -959,10 +959,13 @@ Common::Error EEMEngine::saveProfile(const Common::String &name) {
 		}
 	}
 
-	// New profile ??? pick the lowest unused slot. The MetaEngine caps
-	// us at 99 by default (`getMaximumSaveSlot`); 25 was the DOS
-	// original's limit (`screen8_handler` walks `*.PLR` up to 25
-	// entries in `local_8c[0x19][2]`).
+	// New profile ??? pick the lowest unused visible slot. Slot 0 is
+	// filtered out by `listProfiles()` because it is ScummVM's
+	// conventional autosave slot, so allocating a new profile there
+	// makes the profile disappear from the picker on the next refresh.
+	// The MetaEngine caps us at 99 by default (`getMaximumSaveSlot`);
+	// 25 was the DOS original's limit (`screen8_handler` walks `*.PLR`
+	// up to 25 entries in `local_8c[0x19][2]`).
 	if (slot < 0) {
 		const int maxSlot = getMetaEngine()->getMaximumSaveSlot();
 		Common::Array<bool> used(maxSlot + 1);
@@ -971,7 +974,7 @@ Common::Error EEMEngine::saveProfile(const Common::String &name) {
 			if (sl >= 0 && sl <= maxSlot)
 				used[sl] = true;
 		}
-		for (int i = 0; i <= maxSlot; i++) {
+		for (int i = 1; i <= maxSlot; i++) {
 			if (!used[i]) {
 				slot = i;
 				break;
diff --git a/engines/eem/eem.h b/engines/eem/eem.h
index 44a90e4fdf4..f73c751f87e 100644
--- a/engines/eem/eem.h
+++ b/engines/eem/eem.h
@@ -157,8 +157,8 @@ public:
 	// description = player name) ??? same approach Wetlands uses.
 
 	/// Mirrors `_SavePlayerRecord @ 1c33:034f`. Saves into the slot
-	/// whose description matches @p name, or the lowest unused slot
-	/// if no match. Returns the kNoError on success.
+	/// whose description matches @p name, or the lowest unused visible
+	/// slot if no match. Returns the kNoError on success.
 	Common::Error saveProfile(const Common::String &name);
 
 	/// Mirrors `_LoadPlayerRecord @ 1c33:03a6`. Returns false if no


Commit: 2a0ec6f7e2ab4d704e0ac3b2ab32d7e9dad85f1e
    https://github.com/scummvm/scummvm/commit/2a0ec6f7e2ab4d704e0ac3b2ab32d7e9dad85f1e
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:20:56+02:00

Commit Message:
EEM: bring virtual keyboard when typing name

Changed paths:
    engines/eem/ui.cpp


diff --git a/engines/eem/ui.cpp b/engines/eem/ui.cpp
index 06c0377e833..7f045894eba 100644
--- a/engines/eem/ui.cpp
+++ b/engines/eem/ui.cpp
@@ -504,13 +504,18 @@ void EEMEngine::doNewPlayer() {
 							   0, 0, 320, 200);
 	g_system->updateScreen();
 
-	while (!shouldQuit()) {
+	g_system->setFeatureState(OSystem::kFeatureVirtualKeyboard, true);
+
+	bool done = false;
+	while (!done && !shouldQuit()) {
 		Common::Event ev;
 		bool dirty = false;
 		while (g_system->getEventManager()->pollEvent(ev)) {
 			if (ev.type == Common::EVENT_QUIT ||
-				ev.type == Common::EVENT_RETURN_TO_LAUNCHER)
-				return;
+				ev.type == Common::EVENT_RETURN_TO_LAUNCHER) {
+				done = true;
+				break;
+			}
 			if (ev.type != Common::EVENT_KEYDOWN)
 				continue;
 			const Common::KeyCode k = ev.kbd.keycode;
@@ -534,11 +539,13 @@ void EEMEngine::doNewPlayer() {
 					_chainStage = 1;
 					saveProfile(name);
 				}
-				return;
+				done = true;
+				break;
 			}
 			if (k == Common::KEYCODE_ESCAPE) {
 				_playerName = "Detective";
-				return;
+				done = true;
+				break;
 			}
 			if (k == Common::KEYCODE_BACKSPACE) {
 				if (!name.empty()) {
@@ -568,6 +575,8 @@ void EEMEngine::doNewPlayer() {
 		g_system->updateScreen();
 		g_system->delayMillis(15);
 	}
+
+	g_system->setFeatureState(OSystem::kFeatureVirtualKeyboard, false);
 }
 
 int EEMEngine::doShowEnding(uint num, bool firstPage) {


Commit: 8a575a0b4f4e2a1dbbdc9e3ef0e959f16286ad04
    https://github.com/scummvm/scummvm/commit/8a575a0b4f4e2a1dbbdc9e3ef0e959f16286ad04
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:20:56+02:00

Commit Message:
EEM: correctly render background during enter animation

Changed paths:
    engines/eem/site.cpp


diff --git a/engines/eem/site.cpp b/engines/eem/site.cpp
index a36c0188248..1672ef45a61 100644
--- a/engines/eem/site.cpp
+++ b/engines/eem/site.cpp
@@ -573,9 +573,19 @@ void SiteScreen::enter(uint siteNum) {
 	// the arrival on so re-entries (after notebook/map/etc.) don't
 	// repeat the animation.
 	if ((int)siteNum != _lastSiteAnim) {
+		// `_EnterSiteAnim` snapshots the current screen, so populate
+		// that temporary background with the same site layers that
+		// should already be visible behind the arriving partner.
+		if (_vm->isFloppy())
+			renderFloppyDrops(siteNum);
+		else
+			renderStaticDrops(siteNum);
+		renderAnimatedDrops(siteNum, g_system->getMillis());
 		enterSiteAnim();
 		_lastSiteAnim = (int)siteNum;
-		// Re-paint the BG; the arrival animation drew on top of it.
+		// Re-paint the BG; the normal snapshot below should contain
+		// only the static layers, while animated NPCs are redrawn per
+		// tick by the frame pump.
 		renderBackground(siteNum);
 	}
 


Commit: dcb37c0a03cf1ea2246acdac995e0379a13ee515
    https://github.com/scummvm/scummvm/commit/dcb37c0a03cf1ea2246acdac995e0379a13ee515
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:20:56+02:00

Commit Message:
EEM: original mouse pointer and new feature to hide highlight boxes

Changed paths:
    engines/eem/detection.cpp
    engines/eem/detection.h
    engines/eem/eem.cpp
    engines/eem/eem.h
    engines/eem/metaengine.cpp
    engines/eem/site.cpp
    engines/eem/site.h


diff --git a/engines/eem/detection.cpp b/engines/eem/detection.cpp
index 60967493537..d0b9b5800f2 100644
--- a/engines/eem/detection.cpp
+++ b/engines/eem/detection.cpp
@@ -40,7 +40,7 @@ const ADGameDescription gameDescriptions[] = {
 		Common::EN_ANY,
 		Common::kPlatformDOS,
 		ADGF_NO_FLAGS,
-		GUIO1(GUIO_NONE)
+		GUIO1(GAMEOPTION_HIDE_HIGHLIGHT_BOXES)
 	},
 	{
 		"eem",
@@ -50,7 +50,7 @@ const ADGameDescription gameDescriptions[] = {
 		Common::EN_ANY,
 		Common::kPlatformDOS,
 		ADGF_NO_FLAGS,
-		GUIO1(GUIO_NONE)
+		GUIO1(GAMEOPTION_HIDE_HIGHLIGHT_BOXES)
 	},
 	{
 		// Spanish floppy release ??? same EEM.EXE binary as the English
@@ -64,7 +64,7 @@ const ADGameDescription gameDescriptions[] = {
 		Common::ES_ESP,
 		Common::kPlatformDOS,
 		ADGF_NO_FLAGS,
-		GUIO1(GUIO_NONE)
+		GUIO1(GAMEOPTION_HIDE_HIGHLIGHT_BOXES)
 	},
 
 	AD_TABLE_END_MARKER
diff --git a/engines/eem/detection.h b/engines/eem/detection.h
index 395daab1c77..7d74db6cd7f 100644
--- a/engines/eem/detection.h
+++ b/engines/eem/detection.h
@@ -26,6 +26,8 @@
 
 namespace EEM {
 
+#define GAMEOPTION_HIDE_HIGHLIGHT_BOXES GUIO_GAMEOPTIONS1
+
 enum EEMDebugChannels {
 	kDebugGeneral = 1 << 0,
 	kDebugScript  = 1 << 1,
diff --git a/engines/eem/eem.cpp b/engines/eem/eem.cpp
index 8326cf54c36..8959705a931 100644
--- a/engines/eem/eem.cpp
+++ b/engines/eem/eem.cpp
@@ -57,13 +57,14 @@ const uint kPicStormLogo       = 0x20b; ///< Floppy storm-logo still: PIC 0x20b
 const uint kPalEAKids          = 0x25;
 const uint kPalHighScore       = 0x27;
 const uint kPalStormLogo       = 0x26;  ///< Floppy `FUN_23d2_0605` palette idx
+const uint kPicMousePointer    = 0x50;  ///< Original startup pointer; 0x51 is the wait cursor
 
 const byte kSaveBodyVer = 1;
 
-// 11x16 mouse cursor ??? replaces the DOS hardware cursor wired in by
-// _InitMouse @ 152d:018b (INT 33h). The original game sets the cursor
-// visible/hidden via _MouseCursor; we leave it on once the screens
-// that need it (ChoosePartner, CaseSelection, sites) are reached.
+// Fallback 11x16 mouse cursor used if the selected PIC pointer cannot be
+// loaded. The original game sets the cursor visible/hidden via
+// _MouseCursor; we leave it on once the screens that need it
+// (ChoosePartner, CaseSelection, sites) are reached.
 //   0 = transparent, 1 = black outline, 2 = white fill
 const byte kCursorBitmap[11 * 16] = {
 	1,1,0,0,0,0,0,0,0,0,0,
@@ -88,11 +89,85 @@ const byte kCursorPalette[] = {
 	0x00, 0x00, 0x00, // 1 ??? outline
 	0xFF, 0xFF, 0xFF  // 2 ??? fill
 };
+const byte kCursorHotspotPalette[] = {
+	0x00, 0x00, 0x00, // 0 ??? transparent (key)
+	0xFF, 0x00, 0x00, // 1 ??? red outline
+	0xFF, 0xFF, 0xFF  // 2 ??? white fill
+};
+
+static void setHotspotCursorPalette(const Picture &cursor, byte transparent) {
+	byte palette[kPalSize];
+	bool used[256];
+	memset(used, 0, sizeof(used));
+
+	g_system->getPaletteManager()->grabPalette(palette, 0, 256);
+
+	for (int y = 0; y < cursor.surface.h; y++) {
+		const byte *src = (const byte *)cursor.surface.getBasePtr(0, y);
+		for (int x = 0; x < cursor.surface.w; x++) {
+			if (src[x] != transparent)
+				used[src[x]] = true;
+		}
+	}
+
+	int minLuma = 255;
+	int maxLuma = 0;
+	for (uint i = 0; i < 256; i++) {
+		if (!used[i])
+			continue;
+		const byte *rgb = palette + i * 3;
+		const int luma = (rgb[0] * 30 + rgb[1] * 59 + rgb[2] * 11) / 100;
+		minLuma = MIN(minLuma, luma);
+		maxLuma = MAX(maxLuma, luma);
+	}
+	const int outlineThreshold = (minLuma + maxLuma) / 2;
+
+	for (uint i = 0; i < 256; i++) {
+		if (!used[i])
+			continue;
+		byte *rgb = palette + i * 3;
+		const int luma = (rgb[0] * 30 + rgb[1] * 59 + rgb[2] * 11) / 100;
+		if (luma <= outlineThreshold) {
+			rgb[0] = 0xFF;
+			rgb[1] = 0x00;
+			rgb[2] = 0x00;
+		} else {
+			rgb[0] = 0xFF;
+			rgb[1] = 0xFF;
+			rgb[2] = 0xFF;
+		}
+	}
+
+	CursorMan.replaceCursorPalette(palette, 0, 256);
+}
+
+static void installMouseCursor(DBDArchive &pics, bool hotspot) {
+	Picture cursor;
+	if (pics.getPicture(kPicMousePointer, cursor) && !cursor.surface.empty()) {
+		const byte transparent = (byte)(cursor.flags >> 8);
+		CursorMan.replaceCursor(cursor.surface.rawSurface(), 0, 0,
+								transparent);
+		if (hotspot)
+			setHotspotCursorPalette(cursor, transparent);
+		else
+			CursorMan.replaceCursorPalette(nullptr, 0, 0);
+		return;
+	}
+
+	warning("EEM: mouse cursor PIC 0x%x missing; using fallback cursor",
+			kPicMousePointer);
+	CursorMan.replaceCursor(kCursorBitmap, 11, 16, 0, 0, 0);
+	CursorMan.replaceCursorPalette(hotspot ? kCursorHotspotPalette
+										   : kCursorPalette,
+								   0, 3);
+}
 
 EEMEngine::EEMEngine(OSystem *syst, const ADGameDescription *gameDesc)
 	: Engine(syst), _gameDescription(gameDesc), _rng("eem"),
 	  _playerName("Detective"),
 	  _lastScreen(kScreenInvalid), _nextScreen(kScreenTitle), _partner(0) {
+	ConfMan.registerDefault("hide_highlight_boxes", false);
+
 	// `ADGameDescription::extra` is set by the matching entry in
 	// `gameDescriptions[]` ("CD" or "Floppy"). Keep variant detection
 	// purely string-based so a future re-release with a different
@@ -133,12 +208,14 @@ Common::Error EEMEngine::run() {
 	_audio->setVoiceEnabled(_voiceOn);
 	syncSoundSettings();
 
-	// _InitMouse @ 152d:018b in the original ??? install our 11x16 arrow,
-	// using palette index 0 as the transparency key. The cursor is left
-	// hidden through the opening anims and switched on at NewPlayer /
-	// ChoosePartner where the player actually clicks.
-	CursorMan.replaceCursor(kCursorBitmap, 11, 16, 0, 0, 0);
-	CursorMan.replaceCursorPalette(kCursorPalette, 0, 3);
+	// CD `_main @ 1a35:0f59` and floppy `_main_Floppy @ 19bb:1012`
+	// both load `_GetPicture(0x50)` as the active mouse pointer before
+	// calling `_InitMouse`. PIC 0x51 is present in both archives but has
+	// no executable xrefs and appears to be the wait cursor.
+	// CD's `_SwitchMouse` supports swapping to a hotspot cursor ID stored
+	// at search record +0x0c, but the shipped CD mystery data only uses
+	// cursor 0; floppy search records have no cursor-id field.
+	installMouseCursor(_picsArchive, false);
 	CursorMan.showMouse(false);
 
 	// _AllBlack @ 172b:0d4b paints the screen black before the first handler.
@@ -472,6 +549,15 @@ screen_loop:
 	return Common::kNoError;
 }
 
+void EEMEngine::setHotspotMouseCursor(bool active) {
+	active = active && ConfMan.getBool("hide_highlight_boxes");
+	if (_hotspotMouseCursor == active)
+		return;
+
+	_hotspotMouseCursor = active;
+	installMouseCursor(_picsArchive, active);
+}
+
 bool EEMEngine::openArchives() {
 	// _InitGraphicsSystem @ 172b:0145 opens these five .DBD/.DBX pairs.
 	if (!_picsArchive.open(Common::Path("PICS.DBD"), Common::Path("PICS.DBX"))) {
@@ -754,6 +840,7 @@ void EEMEngine::doSiteLoop() {
 	// Tab (next site), ESC (exit).
 	SiteScreen screen(this, &_mystery);
 	screen.run();
+	setHotspotMouseCursor(false);
 }
 
 void EEMEngine::startTravelMusic() {
diff --git a/engines/eem/eem.h b/engines/eem/eem.h
index f73c751f87e..3cf5f0e11f1 100644
--- a/engines/eem/eem.h
+++ b/engines/eem/eem.h
@@ -188,6 +188,10 @@ public:
 	const EEMFont &getFont() const { return _font; }
 	uint8       getPartnerIndex() const { return _partner; }
 
+	/// Switch to a lightly tinted cursor while the mouse is over a
+	/// searchable hotspot and the highlight boxes are hidden.
+	void setHotspotMouseCursor(bool active);
+
 	/// Display one ClueBlock. @p clueBlock points at the u16 frame count
 	/// followed by 62-byte ClueEntries. Mirrors _DisplayClue @ 2404:05e6.
 	void displayClue(const byte *clueBlock);
@@ -563,6 +567,8 @@ private:
 	/// `setPartnerEraseBg`.
 	Graphics::ManagedSurface _partnerEraseBg;
 
+	bool _hotspotMouseCursor = false;
+
 	/// XMIDI music player. Mirrors the original `MIDI.C` family
 	/// (`_MIDIPlayFile`, `_MIDIPlay`, `_StopMIDI`, `_StartTravelMusic`
 	/// at 20a2:00e2-05c9). Constructed lazily during `run()` once the
diff --git a/engines/eem/metaengine.cpp b/engines/eem/metaengine.cpp
index 21dee682b00..52b2c5e78b5 100644
--- a/engines/eem/metaengine.cpp
+++ b/engines/eem/metaengine.cpp
@@ -22,12 +22,31 @@
 #include "base/plugins.h"
 #include "engines/advancedDetector.h"
 
+#include "common/translation.h"
+
+#include "eem/detection.h"
 #include "eem/eem.h"
 
 #include "common/system.h"
 
 namespace EEM {
 
+static const ADExtraGuiOptionsMap optionsList[] = {
+	{
+		GAMEOPTION_HIDE_HIGHLIGHT_BOXES,
+		{
+			_s("Hide the highlight boxes"),
+			_s("Hide the boxes that highlight searchable clue locations."),
+			"hide_highlight_boxes",
+			false,
+			0,
+			0
+		}
+	},
+
+	AD_EXTRA_GUI_OPTIONS_TERMINATOR
+};
+
 const char *EEMEngine::getGameId() const {
 	return _gameDescription->gameId;
 }
@@ -44,6 +63,10 @@ public:
 		return "eem";
 	}
 
+	const ADExtraGuiOptionsMap *getAdvancedExtraGuiOptions() const override {
+		return EEM::optionsList;
+	}
+
 	Common::Error createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const override {
 		*engine = new EEM::EEMEngine(syst, desc);
 		return Common::kNoError;
diff --git a/engines/eem/site.cpp b/engines/eem/site.cpp
index 1672ef45a61..8a1cddc1ee2 100644
--- a/engines/eem/site.cpp
+++ b/engines/eem/site.cpp
@@ -19,6 +19,7 @@
  *
  */
 
+#include "common/config-manager.h"
 #include "common/debug.h"
 #include "common/events.h"
 #include "common/system.h"
@@ -687,6 +688,8 @@ void SiteScreen::run() {
 	if (cur >= _mystery->numSites())
 		cur = 0;
 	enter(cur);
+	Common::Point mouse = g_system->getEventManager()->getMousePos();
+	updateHotspotCursor(cur, mouse.x, mouse.y);
 
 	while (!_vm->shouldQuit()) {
 		Common::Event event;
@@ -695,8 +698,13 @@ void SiteScreen::run() {
 			switch (event.type) {
 			case Common::EVENT_QUIT:
 			case Common::EVENT_RETURN_TO_LAUNCHER:
+				_vm->setHotspotMouseCursor(false);
 				return;
 
+			case Common::EVENT_MOUSEMOVE:
+				updateHotspotCursor(cur, event.mouse.x, event.mouse.y);
+				break;
+
 			case Common::EVENT_LBUTTONDOWN: {
 				// On-screen UI buttons. `_DoSiteLoop @ 168d:03f4` calls
 				//   _FindButton(&SiteButtons, 2, MouseX, MouseY)
@@ -723,25 +731,31 @@ void SiteScreen::run() {
 				// the PDA / gallery `kBtnPartner` (5, 80, 44, 110).
 				const Common::Rect kBtnPartner ( 5,  80, 44, 110);
 				if (kBtnNotebook.contains(event.mouse.x, event.mouse.y)) {
+					_vm->setHotspotMouseCursor(false);
 					_vm->setNextScreen(kScreenNotebook);
 					return;
 				}
 				if (kBtnMap.contains(event.mouse.x, event.mouse.y)) {
+					_vm->setHotspotMouseCursor(false);
 					// CD writes `_NextScreen = 1`; floppy writes 2.
 					_vm->setNextScreen(_vm->isFloppy() ? kScreenMapAlt
 													   : kScreenMap);
 					return;
 				}
 				if (kBtnPartner.contains(event.mouse.x, event.mouse.y)) {
+					_vm->setHotspotMouseCursor(false);
 					_vm->doHelp();
 					enter(cur);
+					updateHotspotCursor(cur, event.mouse.x, event.mouse.y);
 					break;
 				}
 				const int idx = hotspotAtPoint(cur, event.mouse.x, event.mouse.y);
 				if (idx >= 0) {
+					_vm->setHotspotMouseCursor(false);
 					onHotspotClicked(cur, (uint)idx);
 					// Restore the site BG after the clue overlay.
 					enter(cur);
+					updateHotspotCursor(cur, event.mouse.x, event.mouse.y);
 				}
 				break;
 			}
@@ -756,12 +770,15 @@ void SiteScreen::run() {
 				// here is ESC (matches `_ESCHit` ??? "Are you sure?"
 				// ??? MAP).
 				if (event.kbd.keycode == Common::KEYCODE_ESCAPE) {
+					_vm->setHotspotMouseCursor(false);
 					if (_vm->areYouSure()) {
 						_vm->setNextScreen(_vm->isFloppy() ? kScreenMapAlt
 														   : kScreenMap);
 						return;
 					}
 					enter(cur);
+					mouse = g_system->getEventManager()->getMousePos();
+					updateHotspotCursor(cur, mouse.x, mouse.y);
 				}
 				break;
 
@@ -1272,8 +1289,10 @@ void SiteScreen::renderBackground(uint siteNum) {
 }
 
 void SiteScreen::renderHotspots(uint siteNum) {
-	// Hotspot outlines (`_DrawSearchButtons`): toggle via V.
-	if (!_showHotspots)
+	// Hotspot outlines (`_DrawSearchButtons`). The original always
+	// draws these; the port exposes an optional game setting to hide
+	// them for players who do not want location hints.
+	if (ConfMan.getBool("hide_highlight_boxes"))
 		return;
 
 	const byte *spots = _mystery->hotspots(siteNum);
@@ -1388,6 +1407,12 @@ int SiteScreen::hotspotAtPoint(uint siteNum, int x, int y) const {
 	return -1;
 }
 
+void SiteScreen::updateHotspotCursor(uint siteNum, int x, int y) {
+	if (!_vm)
+		return;
+	_vm->setHotspotMouseCursor(hotspotAtPoint(siteNum, x, y) >= 0);
+}
+
 void SiteScreen::onHotspotClicked(uint siteNum, uint hotIdx) {
 	debugC(1, kDebugSite, "Site %u: hotspot %u clicked", siteNum, hotIdx);
 
diff --git a/engines/eem/site.h b/engines/eem/site.h
index 97e83c087e4..112092edcda 100644
--- a/engines/eem/site.h
+++ b/engines/eem/site.h
@@ -81,7 +81,7 @@ struct Hotspot {
 	int16  x1, y1, x2, y2;     ///< rectangle in screen coordinates
 	uint16 clueOffset;          ///< +8: byte offset of ClueBlock in the mystery file
 	uint16 hotspotIndex;        ///< +10: zero-based mystery-wide seen ordinal
-	uint16 extra;               ///< +12: unknown (zero in M0..M54)
+	uint16 extra;               ///< +12: CD cursor ID for _SwitchMouse; shipped data uses 0
 
 	Common::Rect rect() const { return Common::Rect(x1, y1, x2, y2); }
 };
@@ -109,6 +109,7 @@ private:
 	void renderBackground(uint siteNum);
 	void renderHotspots(uint siteNum);
 	int  hotspotAtPoint(uint siteNum, int x, int y) const;
+	void updateHotspotCursor(uint siteNum, int x, int y);
 	void onHotspotClicked(uint siteNum, uint hotIdx);
 
 	/// Play the partner's site-arrival sequence once `_LastSite !=
@@ -167,7 +168,6 @@ private:
 
 	EEMEngine *_vm;
 	Mystery *_mystery;
-	bool _showHotspots = true;     ///< Toggle outlines with V key.
 	int _lastSiteAnim = -1;        ///< Last site we played the arrival on.
 	int _snapshotSite = -1;        ///< Site number the snapshot belongs to.
 	Graphics::ManagedSurface _bgSnapshot;


Commit: 00c6438de3a7ea38aa01d5784664fdf5aaf65103
    https://github.com/scummvm/scummvm/commit/00c6438de3a7ea38aa01d5784664fdf5aaf65103
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:20:57+02:00

Commit Message:
EEM: correctly blit npc pictures

Changed paths:
    engines/eem/clues.cpp


diff --git a/engines/eem/clues.cpp b/engines/eem/clues.cpp
index 1420e324a7f..27efcc6c2f6 100644
--- a/engines/eem/clues.cpp
+++ b/engines/eem/clues.cpp
@@ -93,7 +93,8 @@ uint happinessLevel(int x) {
 }
 
 // Lock the framebuffer, masked-blit `p` at (x, y), unlock. The transparent
-// colour is the high byte of `p.flags` per `_Rect_Move_Mask @ 1000:03fc`.
+// colour is the high byte of `p.flags`; `_AddPicBackground @ 172b:0ed4`
+// pushes `word ptr ES:[BX] >> 8` as `_Rect_Move_Mask`'s mask byte.
 void blitMaskedToScreen(const Picture &p, int x, int y) {
 	const byte transp = (byte)(p.flags >> 8);
 	Graphics::Surface *screen = g_system->lockScreen();
@@ -730,11 +731,7 @@ void EEMEngine::displayClue(const byte *clueBlock) {
 			Picture charPic;
 			if (_picsArchive.getPicture(charPicId, charPic) &&
 				charX < 320 && charY < 200) {
-				const int w = MIN<int>(charPic.surface.w, 320 - charX);
-				const int h = MIN<int>(charPic.surface.h, 200 - charY);
-				if (w > 0 && h > 0)
-					g_system->copyRectToScreen(charPic.surface.getPixels(),
-						charPic.surface.pitch, charX, charY, w, h);
+				blitMaskedToScreen(charPic, charX, charY);
 			}
 		}
 


Commit: 057711225d26c5bb95abef5475121c5ffe8a7c6b
    https://github.com/scummvm/scummvm/commit/057711225d26c5bb95abef5475121c5ffe8a7c6b
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:20:57+02:00

Commit Message:
EEM: implemented the quit dialog

Changed paths:
    engines/eem/clues.cpp
    engines/eem/ui.cpp


diff --git a/engines/eem/clues.cpp b/engines/eem/clues.cpp
index 27efcc6c2f6..62cf4047ec2 100644
--- a/engines/eem/clues.cpp
+++ b/engines/eem/clues.cpp
@@ -117,6 +117,16 @@ void blitMaskedToScreen(const Picture &p, int x, int y) {
 	g_system->unlockScreen();
 }
 
+void blitRawToScreen(const Picture &p, int x, int y) {
+	const int w = MIN<int>(p.surface.w, 320 - x);
+	const int h = MIN<int>(p.surface.h, 200 - y);
+	if (x < 0 || y < 0 || w <= 0 || h <= 0)
+		return;
+
+	g_system->copyRectToScreen(p.surface.getPixels(), p.surface.pitch,
+							   x, y, w, h);
+}
+
 void EEMEngine::doChoosePartner() {
 	// Mirrors _DoChoosePartner @ 1a35:0756. The original places boy + girl
 	// animations on a backdrop and polls four click rectangles (two per
@@ -1341,12 +1351,6 @@ void EEMEngine::displayFloppyHotspotDialog(uint siteNum, uint hotIdx) {
 }
 
 bool EEMEngine::areYouSure() {
-	// Mirrors `_AreYouSure` @ 1a35:0a5c. Original loads PIC 0x136 for the
-	// dialog body and PIC 0x1FD/0x1FE for YES/NO. We render a minimal
-	// text dialog that preserves the screen behind it.
-	if (!_font.isLoaded())
-		return true;
-
 	Graphics::Surface *screen = g_system->lockScreen();
 	Graphics::ManagedSurface saved(320, 200,
 		Graphics::PixelFormat::createFormatCLUT8());
@@ -1358,24 +1362,65 @@ bool EEMEngine::areYouSure() {
 		g_system->unlockScreen();
 	}
 
-	const Common::Rect dlg(60, 70, 260, 140);
-	Graphics::ManagedSurface scratch(320, 200,
-		Graphics::PixelFormat::createFormatCLUT8());
-	for (int row = 0; row < 200; row++)
-		memcpy((byte *)scratch.getBasePtr(0, row),
-			   (const byte *)saved.getBasePtr(0, row), 320);
-	scratch.fillRect(dlg, 0);
-	scratch.frameRect(dlg, 0xF);
-	_font.drawString(&scratch,
-		isSpanish() ? "Estas seguro que quieres salir?"
-					: "Are you sure you want to quit?",
-		dlg.left + 8, dlg.top + 8, 320, 0xF);
-	_font.drawString(&scratch,
-		isSpanish() ? "S - Si" : "Y - Yes",
-		dlg.left + 16, dlg.top + 36, 320, 0xF);
-	_font.drawString(&scratch, "N - No", dlg.left + 100, dlg.top + 36, 320, 0xF);
-	g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
-							   0, 0, 320, 200);
+	// CD `_AreYouSure @ 1a35:0a5c` and floppy `FUN_19bb_0b43` both:
+	//   * load PIC 0x136 as the dialog body,
+	//   * load PIC 0x1fd / 0x1fe as the pressed YES / NO button images,
+	//   * center the dialog,
+	//   * hit-test YES at (x+0x0c,y+0x23)-(x+0x20,y+0x32),
+	//     and NO at (x+0x60,y+0x23)-(x+0x74,y+0x32).
+	Picture dialogPic;
+	Picture yesPic;
+	Picture noPic;
+	const bool haveOriginalDialog =
+		_picsArchive.getPicture(0x136, dialogPic) &&
+		_picsArchive.getPicture(0x1fd, yesPic) &&
+		_picsArchive.getPicture(0x1fe, noPic);
+
+	Common::Rect yesRect;
+	Common::Rect noRect;
+	int yesX = 0;
+	int yesY = 0;
+	int noX = 0;
+	int noY = 0;
+
+	if (haveOriginalDialog) {
+		const int x = (320 - dialogPic.surface.w) / 2;
+		const int y = (200 - dialogPic.surface.h) / 2;
+		yesX = x + 0x0c;
+		yesY = y + 0x23;
+		noX = x + 0x60;
+		noY = y + 0x23;
+		yesRect = Common::Rect(yesX, yesY, x + 0x20 + 1, y + 0x32 + 1);
+		noRect = Common::Rect(noX, noY, x + 0x74 + 1, y + 0x32 + 1);
+		blitMaskedToScreen(dialogPic, x, y);
+	} else if (_font.isLoaded()) {
+		const Common::Rect dlg(60, 70, 260, 140);
+		yesRect = Common::Rect(dlg.left + 16, dlg.top + 34,
+							   dlg.left + 84, dlg.top + 54);
+		noRect = Common::Rect(dlg.left + 100, dlg.top + 34,
+							  dlg.left + 160, dlg.top + 54);
+
+		Graphics::ManagedSurface scratch(320, 200,
+			Graphics::PixelFormat::createFormatCLUT8());
+		for (int row = 0; row < 200; row++)
+			memcpy((byte *)scratch.getBasePtr(0, row),
+				   (const byte *)saved.getBasePtr(0, row), 320);
+		scratch.fillRect(dlg, 0);
+		scratch.frameRect(dlg, 0xF);
+		_font.drawString(&scratch,
+			isSpanish() ? "Estas seguro que quieres salir?"
+						: "Are you sure you want to quit?",
+			dlg.left + 8, dlg.top + 8, 320, 0xF);
+		_font.drawString(&scratch,
+			isSpanish() ? "S - Si" : "Y - Yes",
+			dlg.left + 16, dlg.top + 36, 320, 0xF);
+		_font.drawString(&scratch, "N - No", dlg.left + 100,
+						 dlg.top + 36, 320, 0xF);
+		g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
+								   0, 0, 320, 200);
+	} else {
+		return true;
+	}
 	g_system->updateScreen();
 
 	bool result = false;
@@ -1401,6 +1446,24 @@ bool EEMEngine::areYouSure() {
 					result = false; decided = true; break;
 				}
 			}
+			if (ev.type == Common::EVENT_LBUTTONDOWN) {
+				if (yesRect.contains(ev.mouse.x, ev.mouse.y)) {
+					if (haveOriginalDialog) {
+						blitRawToScreen(yesPic, yesX, yesY);
+						g_system->updateScreen();
+						g_system->delayMillis(90);
+					}
+					result = true; decided = true; break;
+				}
+				if (noRect.contains(ev.mouse.x, ev.mouse.y)) {
+					if (haveOriginalDialog) {
+						blitRawToScreen(noPic, noX, noY);
+						g_system->updateScreen();
+						g_system->delayMillis(90);
+					}
+					result = false; decided = true; break;
+				}
+			}
 		}
 		if (decided)
 			break;
diff --git a/engines/eem/ui.cpp b/engines/eem/ui.cpp
index 7f045894eba..ca62f428557 100644
--- a/engines/eem/ui.cpp
+++ b/engines/eem/ui.cpp
@@ -940,58 +940,6 @@ void EEMEngine::doSetup() {
 	};
 	draw();
 
-	// Modal "Are you sure?" yes/no prompt. Mirrors `_AreYouSure @
-	// 1a35:0a5c` ??? the original draws a centred message, listens for
-	// Y/Enter (confirm) or N/ESC (cancel). We render a minimal
-	// overlay with Y / N keys (and click on left/right halves) so
-	// the Quit button gives the player a chance to back out.
-	auto areYouSure = [&]() -> bool {
-		Graphics::ManagedSurface scratch(320, 200,
-			Graphics::PixelFormat::createFormatCLUT8());
-		Graphics::Surface *cur = g_system->lockScreen();
-		if (cur) {
-			for (int row = 0; row < 200; row++)
-				memcpy((byte *)scratch.getBasePtr(0, row),
-					   (const byte *)cur->getBasePtr(0, row), 320);
-			g_system->unlockScreen();
-		}
-		const Common::Rect kBox(80, 80, 240, 120);
-		scratch.fillRect(kBox, 0x00);
-		_font.drawString(&scratch,
-			isSpanish() ? "Estas seguro?" : "Are you sure?",
-			100, 88, 200, 0xF);
-		_font.drawString(&scratch,
-			isSpanish() ? "S = si   N = no" : "Y = yes   N = no",
-			100, 102, 200, 0xF);
-		g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
-								   0, 0, 320, 200);
-		g_system->updateScreen();
-		while (!shouldQuit()) {
-			Common::Event ev;
-			while (g_system->getEventManager()->pollEvent(ev)) {
-				if (ev.type == Common::EVENT_QUIT ||
-					ev.type == Common::EVENT_RETURN_TO_LAUNCHER)
-					return true;
-				if (ev.type == Common::EVENT_KEYDOWN) {
-					const Common::KeyCode k = ev.kbd.keycode;
-					// Spanish prompt is "S = si" ??? accept both Y and S.
-					if (k == Common::KEYCODE_y ||
-						k == Common::KEYCODE_s ||
-						k == Common::KEYCODE_RETURN)
-						return true;
-					if (k == Common::KEYCODE_n ||
-						k == Common::KEYCODE_ESCAPE)
-						return false;
-				}
-				if (ev.type == Common::EVENT_LBUTTONDOWN) {
-					return ev.mouse.x < 160;
-				}
-			}
-			g_system->delayMillis(15);
-		}
-		return false;
-	};
-
 	// Render `picId` and block until click/key. Returns the pressed
 	// keycode (KEYCODE_ESCAPE for an explicit bail, KEYCODE_INVALID
 	// for a click or any other key). When `transparent` is true,


Commit: 6ce915b5a9648c1dfb93e79ae687719840a6d84d
    https://github.com/scummvm/scummvm/commit/6ce915b5a9648c1dfb93e79ae687719840a6d84d
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:20:57+02:00

Commit Message:
EEM: implemented the (im)patience animation

Changed paths:
    engines/eem/site.cpp
    engines/eem/site.h


diff --git a/engines/eem/site.cpp b/engines/eem/site.cpp
index 8a1cddc1ee2..c47e8882f4d 100644
--- a/engines/eem/site.cpp
+++ b/engines/eem/site.cpp
@@ -286,6 +286,18 @@ const AnimScriptLong kAnimScriptsLong[] = {
 				  1,2,3,4,5,6,7,8,9,10,11,12 } },
 };
 
+// `_PatientSequence` and `_ImpatientSequence` are standalone script
+// pointers, not entries in `_AnimationSequences`. CD has the data but
+// never calls the switchers; floppy calls them from `_DoSiteLoop_Floppy`.
+// We intentionally enable the same switch for both builds.
+static const uint8 kPatientSequence[]   = { 0,0,0,0,0,0,0,0,0,2 };
+static const uint8 kImpatientSequence[] = { 0,1,0,1,0,1,0,1,2,1 };
+
+// Test-shortened impatience delay. The original stores an hour-rounded
+// wall-clock value via DOS gettime; this keeps the same reset/switch
+// behavior but makes the feature observable during normal testing.
+static const uint32 kImpatienceDelayMs = 60 * 1000;
+
 // Look up the script for `seqnum`. Returns the frame array + length,
 // or `(nullptr, 0)` if no script is known ??? caller falls back to
 // flipbook cycling so unknown anims still animate (just without idle
@@ -317,6 +329,16 @@ static AnimScriptRef findAnimScript(uint16 seqnum) {
 	return r;
 }
 
+static uint frameFromScriptAtTick(const uint8 *frames, uint len,
+								  uint numFrames, uint32 tickMs) {
+	const uint kFramePeriodMs = 100;
+	if (!frames || len == 0)
+		return numFrames > 0 ? (uint)((tickMs / kFramePeriodMs) % numFrames) : 0;
+	const uint scriptIdx = (uint)((tickMs / kFramePeriodMs) % len);
+	const uint frame     = frames[scriptIdx];
+	return (numFrames > 0) ? MIN<uint>(frame, numFrames - 1) : 0;
+}
+
 void auditPartnerAnims(EEMEngine *vm) {
 	// Cross-check every registered partner-subset script against the
 	// underlying ANI.DBD entry it references. If the script asks for
@@ -432,15 +454,10 @@ void auditPartnerAnims(EEMEngine *vm) {
 // symptom.
 uint partnerFrameAtTick(uint16 seqnum, uint numFrames, uint32 tickMs) {
 	const AnimScriptRef s = findAnimScript(seqnum);
-	const uint kFramePeriodMs = 100;
-	if (!s.frames || s.len == 0)
-		return numFrames > 0 ? (uint)((tickMs / kFramePeriodMs) % numFrames) : 0;
-	const uint scriptIdx = (uint)((tickMs / kFramePeriodMs) % s.len);
-	const uint frame     = s.frames[scriptIdx];
 	// The script can in theory request a frame that's outside the
 	// animation's actual frame count (a misencoded script). Clamp so
 	// we don't read past `anim[]` in the caller.
-	return (numFrames > 0) ? MIN<uint>(frame, numFrames - 1) : 0;
+	return frameFromScriptAtTick(s.frames, s.len, numFrames, tickMs);
 }
 
 // Generic "play `unfold` once, then loop `waitSeq` forever" walker.
@@ -486,7 +503,7 @@ uint bigMapDetailPartnerFrameAtTick(uint numFrames, uint32 elapsedMs) {
 									  numFrames, elapsedMs);
 }
 
-void SiteScreen::enter(uint siteNum) {
+void SiteScreen::enter(uint siteNum, bool resetPartnerMood) {
 	if (!_mystery || !_mystery->isLoaded()) {
 		warning("SiteScreen::enter: no mystery loaded");
 		return;
@@ -503,6 +520,10 @@ void SiteScreen::enter(uint siteNum) {
 	// 0xffff (= -1, becomes 0 on the first `_UpdateAnimations`
 	// tick).
 	_waitPhaseAnchor = g_system->getMillis();
+	if (resetPartnerMood) {
+		_partnerWaitMood = kPartnerWaitDefault;
+		initImpatienceCounter();
+	}
 
 	// Capture whether this is the first time the player enters this
 	// site BEFORE we mark it visited ??? `_DoSiteLoop @ 168d:03f4`
@@ -675,6 +696,30 @@ void SiteScreen::enter(uint siteNum) {
 	}
 }
 
+void SiteScreen::initImpatienceCounter() {
+	_impatientDeadlineMs = g_system->getMillis() + kImpatienceDelayMs;
+}
+
+bool SiteScreen::checkImpatienceCounter() {
+	const uint32 now = g_system->getMillis();
+	const bool impatient = (int32)(now - _impatientDeadlineMs) >= 0;
+	if (impatient)
+		initImpatienceCounter();
+	return impatient;
+}
+
+void SiteScreen::notePartnerActivity() {
+	// Mirrors `_Switch2Patient(WaitHandle)` plus
+	// `_InitImpatientCounter()` in the floppy site loop, but only for
+	// deliberate actions in the port: clicks and key presses. Passive
+	// mouse movement should not make impatience impossible to see.
+	const bool wasImpatient = _partnerWaitMood == kPartnerWaitImpatient;
+	_partnerWaitMood = kPartnerWaitPatient;
+	initImpatienceCounter();
+	if (wasImpatient)
+		debugC(1, kDebugSite, "Partner impatience: reset to patient");
+}
+
 void SiteScreen::run() {
 	if (!_mystery || !_mystery->isLoaded())
 		return;
@@ -731,11 +776,13 @@ void SiteScreen::run() {
 				// the PDA / gallery `kBtnPartner` (5, 80, 44, 110).
 				const Common::Rect kBtnPartner ( 5,  80, 44, 110);
 				if (kBtnNotebook.contains(event.mouse.x, event.mouse.y)) {
+					notePartnerActivity();
 					_vm->setHotspotMouseCursor(false);
 					_vm->setNextScreen(kScreenNotebook);
 					return;
 				}
 				if (kBtnMap.contains(event.mouse.x, event.mouse.y)) {
+					notePartnerActivity();
 					_vm->setHotspotMouseCursor(false);
 					// CD writes `_NextScreen = 1`; floppy writes 2.
 					_vm->setNextScreen(_vm->isFloppy() ? kScreenMapAlt
@@ -745,7 +792,8 @@ void SiteScreen::run() {
 				if (kBtnPartner.contains(event.mouse.x, event.mouse.y)) {
 					_vm->setHotspotMouseCursor(false);
 					_vm->doHelp();
-					enter(cur);
+					notePartnerActivity();
+					enter(cur, false);
 					updateHotspotCursor(cur, event.mouse.x, event.mouse.y);
 					break;
 				}
@@ -754,13 +802,17 @@ void SiteScreen::run() {
 					_vm->setHotspotMouseCursor(false);
 					onHotspotClicked(cur, (uint)idx);
 					// Restore the site BG after the clue overlay.
-					enter(cur);
+					notePartnerActivity();
+					enter(cur, false);
 					updateHotspotCursor(cur, event.mouse.x, event.mouse.y);
+				} else {
+					notePartnerActivity();
 				}
 				break;
 			}
 
 			case Common::EVENT_KEYDOWN:
+				notePartnerActivity();
 				// `_DoSiteLoop @ 168d:07e1` only dispatches on the
 				// 6-entry table at `168d:09d5` (TAB / ENTER / arrow
 				// keys for hotspot cursor cycling) plus ESC handled
@@ -776,7 +828,7 @@ void SiteScreen::run() {
 														   : kScreenMap);
 						return;
 					}
-					enter(cur);
+					enter(cur, false);
 					mouse = g_system->getEventManager()->getMousePos();
 					updateHotspotCursor(cur, mouse.x, mouse.y);
 				}
@@ -803,6 +855,10 @@ void SiteScreen::run() {
 		// park as the original.
 		const uint32 now = g_system->getMillis();
 		if (_snapshotSite == (int)cur && now - _lastTickMs >= 100) {
+			if (checkImpatienceCounter()) {
+				_partnerWaitMood = kPartnerWaitImpatient;
+				debugC(1, kDebugSite, "Partner impatience: switched to impatient");
+			}
 			restoreBgSnapshot();
 			renderAnimatedDrops(cur, now);
 			renderPartner(cur, now);
@@ -1212,8 +1268,19 @@ void SiteScreen::renderPartner(uint siteNum, uint32 tickMs) {
 	const uint32 elapsed = (tickMs >= _waitPhaseAnchor)
 							? (tickMs - _waitPhaseAnchor)
 							: tickMs;
-	const uint frameIdx = partnerFrameAtTick((uint16)animId,
-											  (uint)anim.size(), elapsed);
+	uint frameIdx = 0;
+	if (_partnerWaitMood == kPartnerWaitImpatient) {
+		frameIdx = frameFromScriptAtTick(kImpatientSequence,
+										 ARRAYSIZE(kImpatientSequence),
+										 (uint)anim.size(), elapsed);
+	} else if (_partnerWaitMood == kPartnerWaitPatient) {
+		frameIdx = frameFromScriptAtTick(kPatientSequence,
+										 ARRAYSIZE(kPatientSequence),
+										 (uint)anim.size(), elapsed);
+	} else {
+		frameIdx = partnerFrameAtTick((uint16)animId,
+									  (uint)anim.size(), elapsed);
+	}
 	Graphics::Surface *screen = g_system->lockScreen();
 	if (!screen)
 		return;
diff --git a/engines/eem/site.h b/engines/eem/site.h
index 112092edcda..06bc8595369 100644
--- a/engines/eem/site.h
+++ b/engines/eem/site.h
@@ -100,7 +100,7 @@ public:
 		: _vm(vm), _mystery(mystery) {}
 
 	/** Enter site @p siteNum. Renders BG + hotspots; runs the input loop. */
-	void enter(uint siteNum);
+	void enter(uint siteNum, bool resetPartnerMood = true);
 
 	/// Run the per-mystery loop: site -> map -> next site, ESC exits.
 	void run();
@@ -111,6 +111,9 @@ private:
 	int  hotspotAtPoint(uint siteNum, int x, int y) const;
 	void updateHotspotCursor(uint siteNum, int x, int y);
 	void onHotspotClicked(uint siteNum, uint hotIdx);
+	void initImpatienceCounter();
+	bool checkImpatienceCounter();
+	void notePartnerActivity();
 
 	/// Play the partner's site-arrival sequence once `_LastSite !=
 	/// _SiteNumber`. Mirrors `_EnterSiteAnim @ 1000:9b21` ??? animation
@@ -168,10 +171,17 @@ private:
 
 	EEMEngine *_vm;
 	Mystery *_mystery;
+	enum PartnerWaitMood {
+		kPartnerWaitDefault,
+		kPartnerWaitPatient,
+		kPartnerWaitImpatient
+	};
 	int _lastSiteAnim = -1;        ///< Last site we played the arrival on.
 	int _snapshotSite = -1;        ///< Site number the snapshot belongs to.
 	Graphics::ManagedSurface _bgSnapshot;
 	uint32 _lastTickMs = 0;        ///< Last frame-pump tick in ms.
+	uint32 _impatientDeadlineMs = 0; ///< Test-shortened impatience deadline.
+	PartnerWaitMood _partnerWaitMood = kPartnerWaitDefault;
 
 	/// Wall-clock timestamp at which the partner's wait animation
 	/// "started" (or last restarted). The site loop renders the


Commit: 3e9ba587e4e062b839b93940b8f8c410d44e4876
    https://github.com/scummvm/scummvm/commit/3e9ba587e4e062b839b93940b8f8c410d44e4876
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:20:58+02:00

Commit Message:
EEM: improved scrapbook code

Changed paths:
    engines/eem/eem.h
    engines/eem/ui.cpp


diff --git a/engines/eem/eem.h b/engines/eem/eem.h
index 3cf5f0e11f1..792db1288d2 100644
--- a/engines/eem/eem.h
+++ b/engines/eem/eem.h
@@ -412,7 +412,8 @@ private:
 	/// @ 1df2:044c`. The file format is a 2-byte page count followed
 	/// by N pages, each `{ u16 picNum, u16 x1, u16 y1, u16 x2, u16 y2,
 	/// char text[] (null-terminated, ParseString placeholders) }`.
-	/// Blocks until the player clicks past the last page or hits ESC.
+	/// Blocks until the player exits the ending view or crosses either
+	/// page boundary.
 	/// `_ShowOneScrap @ 1f78:0773` is just `_DisplayEnding(num, 1)`,
 	/// so this same call covers the post-mystery scrapbook view from
 	/// the action menu.
@@ -430,14 +431,15 @@ private:
 	/// Mirrors `_DisplayEnding`'s `[BP-0x18]` return at 1df2:0723.
 	int doShowEnding(uint num, bool firstPage = true);
 
-	/// Walk every solved mystery in tier @p stage (1=Junior, 2=Senior,
-	/// 3=Master) and display each one's ending pages in sequence.
+	/// Browse the scrapbook tier @p stage (1=Junior, 2=Senior,
+	/// 3=Master), moving between mystery endings with the original
+	/// `_DisplayEnding` return direction.
 	/// Mirrors `_ShowScrapbook(stage, 0) @ 1f78:0642`: the original
 	/// computes the mystery range from `(stage - 1) * 0x18 + 1` to
-	/// `(stage - 1) * 0x18 + 0x18` and skips entries whose
-	/// `_3f9b[i] == 0` (unsolved) so the scrapbook only contains
-	/// completed cases. Used by both the setup-screen ScrapBook
-	/// buttons and the action-menu "See ScrapBook 1/2/3" entries.
+	/// `(stage - 1) * 0x18 + 0x18`. If this is the current chain stage,
+	/// unsolved entries are skipped; completed tiers are already solved.
+	/// Used by both the setup-screen ScrapBook buttons and the
+	/// action-menu "See ScrapBook 1/2/3" entries.
 	void doShowScrapbook(uint stage);
 
 	void doCaseSelection();
diff --git a/engines/eem/ui.cpp b/engines/eem/ui.cpp
index ca62f428557..b746dd267bc 100644
--- a/engines/eem/ui.cpp
+++ b/engines/eem/ui.cpp
@@ -649,21 +649,24 @@ int EEMEngine::doShowEnding(uint num, bool firstPage) {
 	// Walk page records. Each page header is 10 bytes; text is
 	// null-terminated and follows the header.
 	uint pageOffsets[8];   // ENDING_RANGE_MAX from `_DisplayEnding`
-	const uint kMaxPages = MIN<uint>(pageCount,
-									 (uint)(sizeof(pageOffsets) / sizeof(uint)));
+	const uint maxPages = MIN<uint>(pageCount,
+									(uint)(sizeof(pageOffsets) / sizeof(uint)));
 	uint cursor = 2;
-	for (uint p = 0; p < kMaxPages; p++) {
-		pageOffsets[p] = cursor;
+	uint validPages = 0;
+	for (uint p = 0; p < maxPages; p++) {
 		if (cursor + 10 >= size)
 			break;
+		pageOffsets[validPages++] = cursor;
 		// Skip the 10-byte header and find the null terminator.
 		cursor += 10;
 		while (cursor < size && buf[cursor] != 0)
 			cursor++;
 		cursor++;  // past the null
 	}
+	if (validPages == 0)
+		return 0;
 
-	uint pageIdx = firstPage ? 0 : (kMaxPages - 1);
+	uint pageIdx = firstPage ? 0 : (validPages - 1);
 	int direction = 0;     // -1 / 0 / +1, see header doc.
 	bool exitLoop = false;
 	bool dirty = true;
@@ -702,15 +705,6 @@ int EEMEngine::doShowEnding(uint num, bool firstPage) {
 										   textW, text, 0);
 			}
 
-			// Page indicator at top-right ("page 1/3"). Stays in the
-			// main font + color 0xF so it doesn't blend into the
-			// newspaper masthead.
-			if (_font.isLoaded() && kMaxPages > 1) {
-				const Common::String hdr = Common::String::format(
-					"%u/%u", (unsigned)pageIdx + 1, (unsigned)kMaxPages);
-				_font.drawString(&scratch, hdr, 280, 4, 32, 0xF);
-			}
-
 			g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
 									   0, 0, 320, 200);
 			g_system->updateScreen();
@@ -726,22 +720,17 @@ int EEMEngine::doShowEnding(uint num, bool firstPage) {
 			if (ev.type == Common::EVENT_KEYDOWN) {
 				switch (ev.kbd.keycode) {
 				case Common::KEYCODE_ESCAPE:
-					// ESC is the ONLY way out of the newspaper view.
-					// Departs from the original `_DisplayEnding`, which
-					// also exits on boundary arrow keys / clicks
-					// (1df2:0689 / 1df2:06a0); the boundary-exit path is
-					// what fed `[BP-0x18]` to `_ShowScrapbook` for
-					// per-mystery scrapbook navigation. We don't expose
-					// that ??? clicking ESC closes the scrapbook entirely.
 					direction = 0;
 					exitLoop = true;
 					break;
 				case Common::KEYCODE_LEFT:
 				case Common::KEYCODE_PAGEUP:
-					// Clamp at page 0 ??? never exit on LEFT.
 					if (pageIdx > 0) {
 						pageIdx--;
 						dirty = true;
+					} else {
+						direction = -1;
+						exitLoop = true;
 					}
 					break;
 				case Common::KEYCODE_RIGHT:
@@ -750,10 +739,12 @@ int EEMEngine::doShowEnding(uint num, bool firstPage) {
 				case Common::KEYCODE_KP_ENTER:
 				case Common::KEYCODE_SPACE:
 				case Common::KEYCODE_TAB:
-					// Clamp at last page ??? never exit on RIGHT either.
-					if (pageIdx + 1 < kMaxPages) {
+					if (pageIdx + 1 < validPages) {
 						pageIdx++;
 						dirty = true;
+					} else {
+						direction = 1;
+						exitLoop = true;
 					}
 					break;
 				default:
@@ -763,22 +754,30 @@ int EEMEngine::doShowEnding(uint num, bool firstPage) {
 					break;
 			}
 			if (ev.type == Common::EVENT_LBUTTONDOWN) {
-				// Mouse clicks shift pages too ??? never exit on click.
-				// Use the original PrevPage/NextPage rect split for
-				// click direction (29be:1078 / 29be:1080); clicks
-				// outside both rects fall through to next-page so the
-				// player still gets some feedback.
-				const Common::Rect kPrevPageRect(0, 0, 27, 200);
+				// Original PrevPage/NextPage rects at 29be:1078 /
+				// 29be:1080. Clicks outside both rects exit with
+				// direction 0.
+				const Common::Rect kPrevPageRect(0, 0, 28, 200);
+				const Common::Rect kNextPageRect(28, 7, 317, 197);
 				if (kPrevPageRect.contains(ev.mouse.x, ev.mouse.y)) {
 					if (pageIdx > 0) {
 						pageIdx--;
 						dirty = true;
+					} else {
+						direction = -1;
+						exitLoop = true;
 					}
-				} else {
-					if (pageIdx + 1 < kMaxPages) {
+				} else if (kNextPageRect.contains(ev.mouse.x, ev.mouse.y)) {
+					if (pageIdx + 1 < validPages) {
 						pageIdx++;
 						dirty = true;
+					} else {
+						direction = 1;
+						exitLoop = true;
 					}
+				} else {
+					direction = 0;
+					exitLoop = true;
 				}
 				if (exitLoop)
 					break;
@@ -791,40 +790,53 @@ int EEMEngine::doShowEnding(uint num, bool firstPage) {
 }
 
 void EEMEngine::doShowScrapbook(uint stage) {
-	// Mirrors `_ShowScrapbook(stage, 0) @ 1f78:0642`. Walk the
-	// stage's mystery range and call `_DisplayEnding` on each solved
-	// mystery. The original splits the 55 cases into three tiers:
-	//   stage 1 (Junior) ??? mysteries  1..0x18 (24 cases)
-	//   stage 2 (Senior) ??? mysteries 0x19..0x30 (24 cases)
-	//   stage 3 (Master) ??? mysteries 0x31..0x36 (6 cases)
-	// Each tier's range is `lo = (stage-1)*0x18 + 1`, `hi = lo + 0x17`
-	// (verified at 1f78:064b: `iVar1 = (param_1 - 1) * 0x18; uVar2 =
-	// iVar1 + 1`). The current-stage filter at 1f78:065e
-	// (`if (DAT_2d5d_3f99 == param_1)`) skips unsolved mysteries
-	// inside the player's CURRENT tier ??? completed tiers show every
-	// mystery regardless. We mirror that exactly.
+	// Mirrors `_ShowScrapbook(stage, 0) @ 1f78:0642`. The original
+	// splits the cases into 24-entry tiers, then feeds each ending
+	// viewer return direction back into the tier walk:
+	//   -1 -> previous mystery, opened at its last page
+	//    0 -> close scrapbook
+	//   +1 -> next mystery, opened at its first page
+	// When the requested tier is the player's current chain stage, the
+	// original skips unsolved entries; completed tiers are already solved,
+	// so they are shown as a full range.
 	if (stage < 1 || stage > 3)
 		return;
-	const uint lo = (stage - 1) * 0x18 + 1;
-	const uint hi = lo + 0x17;
+	const int solvedCount =
+		(int)(sizeof(_mysteriesSolved) / sizeof(_mysteriesSolved[0]));
+	const int lo = (int)(stage - 1) * 0x18 + 1;
+	const int hi = MIN<int>(lo + 0x18, solvedCount);
+	if (lo >= hi)
+		return;
 	const bool currentTier = (stage == _chainStage);
 
-	// `doShowEnding` only reports `direction = 0` now (ESC), so we
-	// can't use the original 1f78:067e/0698 forward-backward walk.
-	// Instead iterate every solved mystery in the tier linearly:
-	// each ESC closes the current newspaper and moves us to the
-	// next solved entry. Departs from `_ShowScrapbook @ 1f78:0642`
-	// (which let the player browse via the boundary keys), but
-	// matches the user-requested "ESC is the only exit" rule.
-	for (uint m = lo; m <= hi && !shouldQuit(); m++) {
-		if (m >= sizeof(_mysteriesSolved))
+	int mystery = lo;
+	if (currentTier) {
+		while (mystery < hi && _mysteriesSolved[mystery] == 0)
+			mystery++;
+	}
+
+	bool firstPage = true;
+	while (!shouldQuit() && mystery >= lo && mystery < hi) {
+		const int direction = doShowEnding((uint)mystery, firstPage);
+		if (direction < 0) {
+			if (mystery == lo)
+				break;
+			mystery--;
+			if (currentTier) {
+				while (mystery >= lo && _mysteriesSolved[mystery] == 0)
+					mystery--;
+			}
+			firstPage = false;
+		} else if (direction > 0) {
+			mystery++;
+			if (currentTier) {
+				while (mystery < hi && _mysteriesSolved[mystery] == 0)
+					mystery++;
+			}
+			firstPage = true;
+		} else {
 			break;
-		// Current-tier filter (1f78:0664). Completed tiers show all
-		// 24 entries; the active tier hides unsolved ones because
-		// the player hasn't earned that scrapbook page yet.
-		if (currentTier && _mysteriesSolved[m] == 0)
-			continue;
-		(void)doShowEnding(m, /*firstPage=*/true);
+		}
 	}
 }
 
@@ -1176,31 +1188,29 @@ void EEMEngine::doSetup() {
 			}
 
 			// ScrapBook 1 / 2 / 3 (buttons [3] / [4] / [5]). Original
-			// handlers at 1f78:021F (`_ShowScrapbook(0, 1)`) /
-			// 1f78:022E (gated chain >= 2 / `_ShowScrapbook(0, 2)`) /
-			// 1f78:0244 (gated chain >= 3 / `_ShowScrapbook(0, 3)`).
-			// Convert the original's `(0, stage)` invocation into our
-			// `doShowScrapbook(stage)` (we collapse the param_1=0
-			// "no-current-mystery" indirection ??? relevant only for
-			// the post-win callsite).
-			auto runScrapbook = [&](uint stage) {
+			// handlers call `_ShowScrapbook(stage, 0)`, with stages 2
+			// and 3 gated by chain progress.
+			if (kScrap1Btn.contains(mx, my)) {
 				CursorMan.showMouse(false);
-				doShowScrapbook(stage);
+				doShowScrapbook(1);
 				CursorMan.showMouse(true);
 				setSitePalette(0);
-			};
-			if (kScrap1Btn.contains(mx, my)) {
-				runScrapbook(1);
 				dirty = true;
 				continue;
 			}
 			if (kScrap2Btn.contains(mx, my) && _chainStage >= 2) {
-				runScrapbook(2);
+				CursorMan.showMouse(false);
+				doShowScrapbook(2);
+				CursorMan.showMouse(true);
+				setSitePalette(0);
 				dirty = true;
 				continue;
 			}
 			if (kScrap3Btn.contains(mx, my) && _chainStage >= 3) {
-				runScrapbook(3);
+				CursorMan.showMouse(false);
+				doShowScrapbook(3);
+				CursorMan.showMouse(true);
+				setSitePalette(0);
 				dirty = true;
 				continue;
 			}


Commit: 5042edef3af445d4d7d272f15601179d6a51b6ac
    https://github.com/scummvm/scummvm/commit/5042edef3af445d4d7d272f15601179d6a51b6ac
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:20:58+02:00

Commit Message:
EEM: highlight scrapbook interactive zones

Changed paths:
    engines/eem/eem.cpp
    engines/eem/eem.h
    engines/eem/ui.cpp


diff --git a/engines/eem/eem.cpp b/engines/eem/eem.cpp
index 8959705a931..6ca48a2a385 100644
--- a/engines/eem/eem.cpp
+++ b/engines/eem/eem.cpp
@@ -61,6 +61,10 @@ const uint kPicMousePointer    = 0x50;  ///< Original startup pointer; 0x51 is t
 
 const byte kSaveBodyVer = 1;
 
+// Internal test switch: populate ScrapBook 1 at startup without exposing a
+// game option or changing save format. Set false before release.
+const bool kDebugPopulateScrapbook1AtStartup = false;
+
 // Fallback 11x16 mouse cursor used if the selected PIC pointer cannot be
 // loaded. The original game sets the cursor visible/hidden via
 // _MouseCursor; we leave it on once the screens that need it
@@ -89,13 +93,13 @@ const byte kCursorPalette[] = {
 	0x00, 0x00, 0x00, // 1 ??? outline
 	0xFF, 0xFF, 0xFF  // 2 ??? fill
 };
-const byte kCursorHotspotPalette[] = {
+const byte kCursorInteractivePalette[] = {
 	0x00, 0x00, 0x00, // 0 ??? transparent (key)
 	0xFF, 0x00, 0x00, // 1 ??? red outline
 	0xFF, 0xFF, 0xFF  // 2 ??? white fill
 };
 
-static void setHotspotCursorPalette(const Picture &cursor, byte transparent) {
+static void setInteractiveCursorPalette(const Picture &cursor, byte transparent) {
 	byte palette[kPalSize];
 	bool used[256];
 	memset(used, 0, sizeof(used));
@@ -141,14 +145,14 @@ static void setHotspotCursorPalette(const Picture &cursor, byte transparent) {
 	CursorMan.replaceCursorPalette(palette, 0, 256);
 }
 
-static void installMouseCursor(DBDArchive &pics, bool hotspot) {
+static void installMouseCursor(DBDArchive &pics, bool interactive) {
 	Picture cursor;
 	if (pics.getPicture(kPicMousePointer, cursor) && !cursor.surface.empty()) {
 		const byte transparent = (byte)(cursor.flags >> 8);
 		CursorMan.replaceCursor(cursor.surface.rawSurface(), 0, 0,
 								transparent);
-		if (hotspot)
-			setHotspotCursorPalette(cursor, transparent);
+		if (interactive)
+			setInteractiveCursorPalette(cursor, transparent);
 		else
 			CursorMan.replaceCursorPalette(nullptr, 0, 0);
 		return;
@@ -157,8 +161,8 @@ static void installMouseCursor(DBDArchive &pics, bool hotspot) {
 	warning("EEM: mouse cursor PIC 0x%x missing; using fallback cursor",
 			kPicMousePointer);
 	CursorMan.replaceCursor(kCursorBitmap, 11, 16, 0, 0, 0);
-	CursorMan.replaceCursorPalette(hotspot ? kCursorHotspotPalette
-										   : kCursorPalette,
+	CursorMan.replaceCursorPalette(interactive ? kCursorInteractivePalette
+											   : kCursorPalette,
 								   0, 3);
 }
 
@@ -183,6 +187,17 @@ EEMEngine::~EEMEngine() {
 	delete _music;
 }
 
+void EEMEngine::applyStartupTestOverrides() {
+	if (!kDebugPopulateScrapbook1AtStartup)
+		return;
+
+	for (uint i = 1; i <= 0x18 && i < sizeof(_mysteriesSolved); i++)
+		_mysteriesSolved[i] = 1;
+
+	debugC(1, kDebugGeneral,
+		   "startup test override: populated ScrapBook 1 mystery flags");
+}
+
 Common::Error EEMEngine::run() {
 	// _SetMode13X @ 1000:0358 enters VGA mode 13h (320x200x256).
 	initGraphics(320, 200);
@@ -243,6 +258,7 @@ Common::Error EEMEngine::run() {
 	if (wantedSave >= 0) {
 		const Common::Error err = loadGameState(wantedSave);
 		if (err.getCode() == Common::kNoError) {
+			applyStartupTestOverrides();
 			CursorMan.showMouse(true);
 			if (_mystery.isLoaded()) {
 				debugC(1, kDebugGeneral,
@@ -390,6 +406,8 @@ Common::Error EEMEngine::run() {
 	// picks "[New Player]".
 	if (!shouldQuit())
 		doProfilePicker();
+	if (!shouldQuit())
+		applyStartupTestOverrides();
 	if (!shouldQuit())
 		doChoosePartner();
 
@@ -549,15 +567,18 @@ screen_loop:
 	return Common::kNoError;
 }
 
-void EEMEngine::setHotspotMouseCursor(bool active) {
-	active = active && ConfMan.getBool("hide_highlight_boxes");
-	if (_hotspotMouseCursor == active)
+void EEMEngine::setInteractiveMouseCursor(bool active) {
+	if (_interactiveMouseCursor == active)
 		return;
 
-	_hotspotMouseCursor = active;
+	_interactiveMouseCursor = active;
 	installMouseCursor(_picsArchive, active);
 }
 
+void EEMEngine::setHotspotMouseCursor(bool active) {
+	setInteractiveMouseCursor(active);
+}
+
 bool EEMEngine::openArchives() {
 	// _InitGraphicsSystem @ 172b:0145 opens these five .DBD/.DBX pairs.
 	if (!_picsArchive.open(Common::Path("PICS.DBD"), Common::Path("PICS.DBX"))) {
diff --git a/engines/eem/eem.h b/engines/eem/eem.h
index 792db1288d2..d6080d93fbd 100644
--- a/engines/eem/eem.h
+++ b/engines/eem/eem.h
@@ -188,8 +188,12 @@ public:
 	const EEMFont &getFont() const { return _font; }
 	uint8       getPartnerIndex() const { return _partner; }
 
-	/// Switch to a lightly tinted cursor while the mouse is over a
-	/// searchable hotspot and the highlight boxes are hidden.
+	/// Switch to the red-outline cursor used to hint at interactive
+	/// regions that do not have their own original highlight art.
+	void setInteractiveMouseCursor(bool active);
+
+	/// Switch to the interactive cursor while the mouse is over a
+	/// searchable hotspot.
 	void setHotspotMouseCursor(bool active);
 
 	/// Display one ClueBlock. @p clueBlock points at the u16 frame count
@@ -302,6 +306,8 @@ public:
 	bool areYouSure();
 
 private:
+	void applyStartupTestOverrides();
+
 	/**
 	 * Central dispatch loop matching the original _ScreenDriver @ 1a35:0dc1.
 	 * Each iteration calls the screen handler that matches _nextScreen.
@@ -426,7 +432,7 @@ private:
 	/// Returns the direction the user wants the caller to navigate:
 	///   -1 ??? previous mystery (LEFT pressed on the first page or
 	///        click in `PrevPageRect` while on first page),
-	///    0 ??? exit the scrapbook (ESC or click outside both rects),
+	///    0 ??? exit the scrapbook (ESC / quit; central clicks are ignored),
 	///   +1 ??? next mystery (RIGHT/SPACE/Enter/click on last page).
 	/// Mirrors `_DisplayEnding`'s `[BP-0x18]` return at 1df2:0723.
 	int doShowEnding(uint num, bool firstPage = true);
@@ -569,7 +575,7 @@ private:
 	/// `setPartnerEraseBg`.
 	Graphics::ManagedSurface _partnerEraseBg;
 
-	bool _hotspotMouseCursor = false;
+	bool _interactiveMouseCursor = false;
 
 	/// XMIDI music player. Mirrors the original `MIDI.C` family
 	/// (`_MIDIPlayFile`, `_MIDIPlay`, `_StopMIDI`, `_StartTravelMusic`
diff --git a/engines/eem/ui.cpp b/engines/eem/ui.cpp
index b746dd267bc..d7dd55bf19d 100644
--- a/engines/eem/ui.cpp
+++ b/engines/eem/ui.cpp
@@ -55,6 +55,9 @@ const GallerySlot kGallerySlots[5] = {
 	{ 191,  90 }  // 4
 };
 
+constexpr Common::Rect kEndingPrevPageRect(Common::Point(0, 0), 28, 200);
+constexpr Common::Rect kEndingNextPageRect(Common::Point(292, 0), 28, 200);
+
 // Floppy gallery slot positions verified at `2608:0x16c` (5 ??
 // {u16 x, u16 y}) ??? read by `_DrawGallery_Floppy @ 154e:0045`'s
 // `[BX + 0x16c]` (x) and `[BX + 0x16e]` (y) loads. The floppy
@@ -596,14 +599,14 @@ int EEMEngine::doShowEnding(uint num, bool firstPage) {
 	// colour), with no drop shadow. Verified at the call site asm
 	// 1df2:04cf-1df2:04f4 (Ghidra mis-paired the two trailing args).
 	//
-	// Page navigation mirrors the original key/click handlers
+	// Keyboard page navigation mirrors the original handlers
 	// (1df2:0689 / 1df2:06a0): LEFT decrements pageIdx, RIGHT (or
-	// SPACE / Enter / click) increments it. Hitting the boundary
-	// (LEFT on page 0, RIGHT on last page) sets `[BP-0x18]` to -1 / 1
-	// respectively and exits ??? that return value is what
-	// `_ShowScrapbook` uses to walk forward / backward through
-	// solved mysteries (see 1f78:0664-1f78:069c). ESC and clicks
-	// outside both PrevPage / NextPage rects exit with `[BP-0x18]=0`.
+	// SPACE / Enter) increments it. Hitting the boundary (LEFT on page
+	// 0, RIGHT on last page) sets `[BP-0x18]` to -1 / 1 respectively
+	// and exits ??? that return value is what `_ShowScrapbook` uses to
+	// walk forward / backward through solved mysteries (see
+	// 1f78:0664-1f78:069c). Mouse navigation is intentionally limited
+	// to the red-highlighted edge rects so central page clicks are ignored.
 	//
 	// `firstPage=false` opens the ending at the LAST page (used by
 	// `doShowScrapbook` after a "previous mystery" navigation ???
@@ -645,6 +648,7 @@ int EEMEngine::doShowEnding(uint num, bool firstPage) {
 	// text in particular is palette index 0 (= newspaper black) so we
 	// MUST switch palettes before rendering.
 	setSitePalette(0);
+	CursorMan.showMouse(true);
 
 	// Walk page records. Each page header is 10 bytes; text is
 	// null-terminated and follows the header.
@@ -670,6 +674,11 @@ int EEMEngine::doShowEnding(uint num, bool firstPage) {
 	int direction = 0;     // -1 / 0 / +1, see header doc.
 	bool exitLoop = false;
 	bool dirty = true;
+	const Common::Point mousePos = g_system->getEventManager()->getMousePos();
+	setInteractiveMouseCursor(kEndingPrevPageRect.contains(mousePos.x,
+														   mousePos.y) ||
+							  kEndingNextPageRect.contains(mousePos.x,
+														   mousePos.y));
 	while (!shouldQuit() && !exitLoop) {
 		if (dirty) {
 			const uint off = pageOffsets[pageIdx];
@@ -715,8 +724,14 @@ int EEMEngine::doShowEnding(uint num, bool firstPage) {
 		while (g_system->getEventManager()->pollEvent(ev)) {
 			if (ev.type == Common::EVENT_QUIT ||
 				ev.type == Common::EVENT_RETURN_TO_LAUNCHER) {
-				return 0;
+				direction = 0;
+				exitLoop = true;
+				break;
 			}
+			if (ev.type == Common::EVENT_MOUSEMOVE)
+				setInteractiveMouseCursor(
+					kEndingPrevPageRect.contains(ev.mouse.x, ev.mouse.y) ||
+					kEndingNextPageRect.contains(ev.mouse.x, ev.mouse.y));
 			if (ev.type == Common::EVENT_KEYDOWN) {
 				switch (ev.kbd.keycode) {
 				case Common::KEYCODE_ESCAPE:
@@ -754,12 +769,10 @@ int EEMEngine::doShowEnding(uint num, bool firstPage) {
 					break;
 			}
 			if (ev.type == Common::EVENT_LBUTTONDOWN) {
-				// Original PrevPage/NextPage rects at 29be:1078 /
-				// 29be:1080. Clicks outside both rects exit with
-				// direction 0.
-				const Common::Rect kPrevPageRect(0, 0, 28, 200);
-				const Common::Rect kNextPageRect(28, 7, 317, 197);
-				if (kPrevPageRect.contains(ev.mouse.x, ev.mouse.y)) {
+				setInteractiveMouseCursor(
+					kEndingPrevPageRect.contains(ev.mouse.x, ev.mouse.y) ||
+					kEndingNextPageRect.contains(ev.mouse.x, ev.mouse.y));
+				if (kEndingPrevPageRect.contains(ev.mouse.x, ev.mouse.y)) {
 					if (pageIdx > 0) {
 						pageIdx--;
 						dirty = true;
@@ -767,7 +780,7 @@ int EEMEngine::doShowEnding(uint num, bool firstPage) {
 						direction = -1;
 						exitLoop = true;
 					}
-				} else if (kNextPageRect.contains(ev.mouse.x, ev.mouse.y)) {
+				} else if (kEndingNextPageRect.contains(ev.mouse.x, ev.mouse.y)) {
 					if (pageIdx + 1 < validPages) {
 						pageIdx++;
 						dirty = true;
@@ -775,9 +788,6 @@ int EEMEngine::doShowEnding(uint num, bool firstPage) {
 						direction = 1;
 						exitLoop = true;
 					}
-				} else {
-					direction = 0;
-					exitLoop = true;
 				}
 				if (exitLoop)
 					break;
@@ -786,6 +796,7 @@ int EEMEngine::doShowEnding(uint num, bool firstPage) {
 		g_system->updateScreen();
 		g_system->delayMillis(15);
 	}
+	setInteractiveMouseCursor(false);
 	return direction;
 }
 
@@ -1191,25 +1202,19 @@ void EEMEngine::doSetup() {
 			// handlers call `_ShowScrapbook(stage, 0)`, with stages 2
 			// and 3 gated by chain progress.
 			if (kScrap1Btn.contains(mx, my)) {
-				CursorMan.showMouse(false);
 				doShowScrapbook(1);
-				CursorMan.showMouse(true);
 				setSitePalette(0);
 				dirty = true;
 				continue;
 			}
 			if (kScrap2Btn.contains(mx, my) && _chainStage >= 2) {
-				CursorMan.showMouse(false);
 				doShowScrapbook(2);
-				CursorMan.showMouse(true);
 				setSitePalette(0);
 				dirty = true;
 				continue;
 			}
 			if (kScrap3Btn.contains(mx, my) && _chainStage >= 3) {
-				CursorMan.showMouse(false);
 				doShowScrapbook(3);
-				CursorMan.showMouse(true);
 				setSitePalette(0);
 				dirty = true;
 				continue;
@@ -1493,9 +1498,7 @@ void EEMEngine::doCaseSelection() {
 		// ??? viewing the scrapbook never starts a new case.
 		const uint stage = (pick == kPickScrap1) ? 1
 						 : (pick == kPickScrap2) ? 2 : 3;
-		CursorMan.showMouse(false);
 		doShowScrapbook(stage);
-		CursorMan.showMouse(true);
 		setSitePalette(0);
 		_mystery.clear();
 		return;


Commit: 916c34e2b2163222ad0cba2f06518cc53a604cd4
    https://github.com/scummvm/scummvm/commit/916c34e2b2163222ad0cba2f06518cc53a604cd4
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:20:58+02:00

Commit Message:
EEM: highlight pda interactive zones

Changed paths:
    engines/eem/site.cpp
    engines/eem/ui.cpp


diff --git a/engines/eem/site.cpp b/engines/eem/site.cpp
index c47e8882f4d..1a71f83e417 100644
--- a/engines/eem/site.cpp
+++ b/engines/eem/site.cpp
@@ -35,6 +35,10 @@
 
 namespace EEM {
 
+constexpr Common::Rect kSitePdaRect(Common::Point(35, 111), 21, 25);
+constexpr Common::Rect kSitePartnerFootMapRect(Common::Point(7, 177), 50, 23);
+constexpr Common::Rect kSitePartnerHeadHintRect(Common::Point(5, 80), 39, 30);
+
 // Masked blit a Picture into a ManagedSurface. Pixels equal to `transp`
 // (the high byte of `pic.flags`, per `_Rect_Move_Mask @ 1000:03fc`) are
 // skipped. Used by `enterSiteAnim` for both skateboard + KD slide-in
@@ -761,27 +765,24 @@ void SiteScreen::run() {
 				//                                       (CD `_NextScreen = 1`,
 				//                                       floppy = 2)
 				// Test the buttons before falling through to hotspots so
-				// a click on the PDA / map icon doesn't accidentally
+				// a click on the PDA / partner foot doesn't accidentally
 				// trigger a hotspot underneath.
-				const Common::Rect kBtnNotebook(35, 111, 56, 136);
-				const Common::Rect kBtnMap     ( 7, 177, 57, 200);
-				// Partner area ??? port-only enhancement so the player
+				// Partner head is a port-only enhancement so the player
 				// can click the host sprite for a hint, mirroring the
 				// PDA's rect-3 / gallery's rect-3 behaviour. The
 				// original site loop's `_FindButton(&SiteButtons, 2,
 				// ...)` only checks notebook + map, but the same
-				// partner-click ??? `_KDHelp` shortcut is wired in
+				// partner-click -> `_KDHelp` shortcut is wired in
 				// `_HandleNoteButton[3]` (0x0403) and
 				// `_HandleGalleryButton[3]` (0x061e). Rect matches
 				// the PDA / gallery `kBtnPartner` (5, 80, 44, 110).
-				const Common::Rect kBtnPartner ( 5,  80, 44, 110);
-				if (kBtnNotebook.contains(event.mouse.x, event.mouse.y)) {
+				if (kSitePdaRect.contains(event.mouse.x, event.mouse.y)) {
 					notePartnerActivity();
 					_vm->setHotspotMouseCursor(false);
 					_vm->setNextScreen(kScreenNotebook);
 					return;
 				}
-				if (kBtnMap.contains(event.mouse.x, event.mouse.y)) {
+				if (kSitePartnerFootMapRect.contains(event.mouse.x, event.mouse.y)) {
 					notePartnerActivity();
 					_vm->setHotspotMouseCursor(false);
 					// CD writes `_NextScreen = 1`; floppy writes 2.
@@ -789,7 +790,7 @@ void SiteScreen::run() {
 													   : kScreenMap);
 					return;
 				}
-				if (kBtnPartner.contains(event.mouse.x, event.mouse.y)) {
+				if (kSitePartnerHeadHintRect.contains(event.mouse.x, event.mouse.y)) {
 					_vm->setHotspotMouseCursor(false);
 					_vm->doHelp();
 					notePartnerActivity();
@@ -1477,7 +1478,10 @@ int SiteScreen::hotspotAtPoint(uint siteNum, int x, int y) const {
 void SiteScreen::updateHotspotCursor(uint siteNum, int x, int y) {
 	if (!_vm)
 		return;
-	_vm->setHotspotMouseCursor(hotspotAtPoint(siteNum, x, y) >= 0);
+	const bool siteControl = kSitePdaRect.contains(x, y) ||
+							 kSitePartnerFootMapRect.contains(x, y) ||
+							 kSitePartnerHeadHintRect.contains(x, y);
+	_vm->setHotspotMouseCursor(siteControl || hotspotAtPoint(siteNum, x, y) >= 0);
 }
 
 void SiteScreen::onHotspotClicked(uint siteNum, uint hotIdx) {
diff --git a/engines/eem/ui.cpp b/engines/eem/ui.cpp
index d7dd55bf19d..0eec0d2fe5e 100644
--- a/engines/eem/ui.cpp
+++ b/engines/eem/ui.cpp
@@ -58,6 +58,55 @@ const GallerySlot kGallerySlots[5] = {
 constexpr Common::Rect kEndingPrevPageRect(Common::Point(0, 0), 28, 200);
 constexpr Common::Rect kEndingNextPageRect(Common::Point(292, 0), 28, 200);
 
+constexpr Common::Rect kPdaHelpRect(Common::Point(93, 174), 22, 16);
+constexpr Common::Rect kPdaNotebookRect(Common::Point(134, 174), 21, 16);
+constexpr Common::Rect kPdaGalleryRect(Common::Point(157, 174), 21, 16);
+constexpr Common::Rect kPdaPartnerHeadHintRect(Common::Point(5, 80), 39, 30);
+constexpr Common::Rect kPdaAccuseRect(Common::Point(180, 174), 21, 16);
+constexpr Common::Rect kPdaPageNextRect(Common::Point(204, 174), 20, 16);
+constexpr Common::Rect kPdaPagePrevRect(Common::Point(226, 174), 21, 16);
+constexpr Common::Rect kPdaHelp2Rect(Common::Point(267, 174), 21, 16);
+constexpr Common::Rect kPdaPartnerFootMapRect(Common::Point(7, 177), 50, 23);
+constexpr Common::Rect kPdaSiteRect(Common::Point(35, 111), 21, 25);
+
+bool notebookButtonAt(int x, int y) {
+	return kPdaHelpRect.contains(x, y) ||
+		   kPdaGalleryRect.contains(x, y) ||
+		   kPdaPartnerHeadHintRect.contains(x, y) ||
+		   kPdaAccuseRect.contains(x, y) ||
+		   kPdaPageNextRect.contains(x, y) ||
+		   kPdaPagePrevRect.contains(x, y) ||
+		   kPdaHelp2Rect.contains(x, y) ||
+		   kPdaPartnerFootMapRect.contains(x, y) ||
+		   kPdaSiteRect.contains(x, y);
+}
+
+bool galleryButtonAt(int x, int y) {
+	return kPdaSiteRect.contains(x, y) ||
+		   kPdaPartnerFootMapRect.contains(x, y) ||
+		   kPdaAccuseRect.contains(x, y) ||
+		   kPdaNotebookRect.contains(x, y) ||
+		   kPdaHelpRect.contains(x, y) ||
+		   kPdaPartnerHeadHintRect.contains(x, y);
+}
+
+bool rectListContains(const Common::Array<Common::Rect> &rects, int x, int y) {
+	for (uint i = 0; i < rects.size(); i++) {
+		if (rects[i].contains(x, y))
+			return true;
+	}
+	return false;
+}
+
+bool gallerySlotAt(const Common::Array<Common::Rect> &rects,
+				   const Common::Array<int> &suspects, int x, int y) {
+	for (uint i = 0; i < rects.size() && i < suspects.size(); i++) {
+		if (suspects[i] >= 0 && rects[i].contains(x, y))
+			return true;
+	}
+	return false;
+}
+
 // Floppy gallery slot positions verified at `2608:0x16c` (5 ??
 // {u16 x, u16 y}) ??? read by `_DrawGallery_Floppy @ 154e:0045`'s
 // `[BX + 0x16c]` (x) and `[BX + 0x16e]` (y) loads. The floppy
@@ -1749,15 +1798,6 @@ void EEMEngine::doNotebook() {
 	//   rect 8 (35,111)  ??? 0x03ed = `_NextScreen = 3`             (SITE)
 	//   rect 9 (0,0)     ??? 0x03ed = same as rect 8
 	//   rect 10 (66,79)  ??? 0x03f9 = `_InterfaceHelp(0)`           (note-area help)
-	const Common::Rect kBtnHelp1   ( 93, 174, 115, 190);  // [1] HELP
-	const Common::Rect kBtnGallery (157, 174, 178, 190);  // [2] GALLERY
-	const Common::Rect kBtnPartner (  5,  80,  44, 110);  // [3] KD HELP
-	const Common::Rect kBtnAccuse  (180, 174, 201, 190);  // [4] SOLVE
-	const Common::Rect kBtnPageNext(204, 174, 224, 190);  // [5] PAGE NEXT
-	const Common::Rect kBtnPagePrev(226, 174, 247, 190);  // [6] PAGE PREV
-	const Common::Rect kBtnMap     (  7, 177,  57, 200);  // [7] MAP
-	const Common::Rect kBtnSite    ( 35, 111,  56, 136);  // [8] SITE
-	const Common::Rect kBtnHelp2   (267, 174, 288, 190);  // [10] extra HELP
 	// (`_NoteButtons @ 29be:0147` actually has rect [10] at
 	// (267,174,288,190) ??? small button on the right of the bottom
 	// bar that the original handler dispatch table at 161e:04ec
@@ -1772,6 +1812,10 @@ void EEMEngine::doNotebook() {
 	(void)hoveredNoteSlot;
 
 	drawNotebookFrame(page);
+	Common::Point mouse = g_system->getEventManager()->getMousePos();
+	setInteractiveMouseCursor(notebookButtonAt(mouse.x, mouse.y) ||
+							  rectListContains(_notebookSlotRects,
+											   mouse.x, mouse.y));
 
 	uint32 lastDraw = g_system->getMillis();
 
@@ -1786,6 +1830,13 @@ void EEMEngine::doNotebook() {
 				exitFlag = true;
 				break;
 			}
+			if (ev.type == Common::EVENT_MOUSEMOVE) {
+				setInteractiveMouseCursor(notebookButtonAt(ev.mouse.x,
+														   ev.mouse.y) ||
+										  rectListContains(_notebookSlotRects,
+														   ev.mouse.x,
+														   ev.mouse.y));
+			}
 			if (ev.type == Common::EVENT_KEYDOWN) {
 				if (ev.kbd.keycode == Common::KEYCODE_ESCAPE) {
 					_nextScreen = kScreenSite;
@@ -1809,52 +1860,55 @@ void EEMEngine::doNotebook() {
 				// button 0 / 9 are dead zones, so check the actionable
 				// rects directly. Earlier rects "win" when overlapping
 				// (matches `_FindButton`).
-				if (kBtnSite.contains(ev.mouse.x, ev.mouse.y)) {
+				if (kPdaSiteRect.contains(ev.mouse.x, ev.mouse.y)) {
 					_nextScreen = kScreenSite;
 					exitFlag = true;
 					break;  // back to site
 				}
-				if (kBtnMap.contains(ev.mouse.x, ev.mouse.y)) {
+				if (kPdaPartnerFootMapRect.contains(ev.mouse.x, ev.mouse.y)) {
 					_nextScreen = kScreenMapAlt;
 					exitFlag = true;
 					break;
 				}
-				if (kBtnPartner.contains(ev.mouse.x, ev.mouse.y)) {
+				if (kPdaPartnerHeadHintRect.contains(ev.mouse.x, ev.mouse.y)) {
+					setInteractiveMouseCursor(false);
 					doHelp();              // _KDHelp = host hint
 					dirty = true;
 					continue;
 				}
-				if (kBtnAccuse.contains(ev.mouse.x, ev.mouse.y)) {
+				if (kPdaAccuseRect.contains(ev.mouse.x, ev.mouse.y)) {
 					_nextScreen = kScreenAccuse;
 					exitFlag = true;
 					break;
 				}
-				if (kBtnGallery.contains(ev.mouse.x, ev.mouse.y)) {
+				if (kPdaGalleryRect.contains(ev.mouse.x, ev.mouse.y)) {
 					_nextScreen = kScreenGallery;
 					exitFlag = true;
 					break;
 				}
-				if (kBtnHelp1.contains(ev.mouse.x, ev.mouse.y)) {
+				if (kPdaHelpRect.contains(ev.mouse.x, ev.mouse.y)) {
 					// rect 1 ??? `_InterfaceHelp(0)`: walks `HelpData[0]` and
 					// blits PICs 0x63 / 0x1ae fullscreen for click-through.
+					setInteractiveMouseCursor(false);
 					doInterfaceHelp(0);
 					dirty = true;
 					continue;
 				}
-				if (kBtnPagePrev.contains(ev.mouse.x, ev.mouse.y)) {
+				if (kPdaPagePrevRect.contains(ev.mouse.x, ev.mouse.y)) {
 					if (page > 0)
 						page--;
 					dirty = true;
 					continue;
 				}
-				if (kBtnPageNext.contains(ev.mouse.x, ev.mouse.y)) {
+				if (kPdaPageNextRect.contains(ev.mouse.x, ev.mouse.y)) {
 					page++;
 					dirty = true;
 					continue;
 				}
-				if (kBtnHelp2.contains(ev.mouse.x, ev.mouse.y)) {
+				if (kPdaHelp2Rect.contains(ev.mouse.x, ev.mouse.y)) {
 					// `_NoteButtons[10]` ??? handler 0x03f9 = same
 					// `_InterfaceHelp(0)` as button [1].
+					setInteractiveMouseCursor(false);
 					doInterfaceHelp(0);
 					dirty = true;
 					continue;
@@ -1887,10 +1941,15 @@ void EEMEngine::doNotebook() {
 		if (dirty || now - lastDraw >= 100) {
 			drawNotebookFrame(page);
 			lastDraw = now;
+			mouse = g_system->getEventManager()->getMousePos();
+			setInteractiveMouseCursor(notebookButtonAt(mouse.x, mouse.y) ||
+									  rectListContains(_notebookSlotRects,
+													   mouse.x, mouse.y));
 		}
 		g_system->updateScreen();
 		g_system->delayMillis(15);
 	}
+	setInteractiveMouseCursor(false);
 }
 
 void EEMEngine::drawNotebookFrame(int &page) {
@@ -2126,6 +2185,10 @@ void EEMEngine::doGallery() {
 	}
 
 	drawGalleryFrame(gd, num, slotRects, slotSuspect);
+	Common::Point mouse = g_system->getEventManager()->getMousePos();
+	setInteractiveMouseCursor(galleryButtonAt(mouse.x, mouse.y) ||
+							  gallerySlotAt(slotRects, slotSuspect,
+											mouse.x, mouse.y));
 	uint32 lastDraw = g_system->getMillis();
 
 	while (!shouldQuit()) {
@@ -2135,8 +2198,17 @@ void EEMEngine::doGallery() {
 			if (ev.type == Common::EVENT_QUIT ||
 				ev.type == Common::EVENT_RETURN_TO_LAUNCHER) {
 				_nextScreen = kScreenInvalid;
+				setInteractiveMouseCursor(false);
 				return;
 			}
+			if (ev.type == Common::EVENT_MOUSEMOVE) {
+				setInteractiveMouseCursor(galleryButtonAt(ev.mouse.x,
+														  ev.mouse.y) ||
+										  gallerySlotAt(slotRects,
+														slotSuspect,
+														ev.mouse.x,
+														ev.mouse.y));
+			}
 			if (ev.type == Common::EVENT_KEYDOWN) {
 				if (ev.kbd.keycode == Common::KEYCODE_ESCAPE) {
 					_nextScreen = kScreenSite;
@@ -2160,37 +2232,37 @@ void EEMEngine::doGallery() {
 				//   rect 6 (226,247) ??? 0x0638 = generic exit
 				//   rect 7 (7,177)   ??? 0x05f7 = `_NextScreen = 2` (MAP)
 				//   rect 8 (35,111)  ??? 0x05e4 = `_NextScreen = 3` (SITE)
-				const Common::Rect kBtnSite    ( 35, 111,  56, 136); // [8] SITE
-				const Common::Rect kBtnMap     (  7, 177,  57, 200); // [7] MAP
-				const Common::Rect kBtnAccuse  (180, 174, 201, 190); // [4] SOLVE
-				const Common::Rect kBtnNotebook(134, 174, 155, 190); // [0] NOTEBOOK (back to PDA notes)
-				const Common::Rect kBtnHelp    ( 93, 174, 115, 190); // [1] HELP
-				const Common::Rect kBtnPartner (  5,  80,  44, 110); // [3] KD HELP
-				if (kBtnSite.contains(ev.mouse.x, ev.mouse.y)) {
+				if (kPdaSiteRect.contains(ev.mouse.x, ev.mouse.y)) {
 					_nextScreen = kScreenSite;
-					exitFlag = true; break;
+					exitFlag = true;
+					break;
 				}
-				if (kBtnMap.contains(ev.mouse.x, ev.mouse.y)) {
+				if (kPdaPartnerFootMapRect.contains(ev.mouse.x, ev.mouse.y)) {
 					_nextScreen = kScreenMapAlt;
-					exitFlag = true; break;
+					exitFlag = true;
+					break;
 				}
-				if (kBtnAccuse.contains(ev.mouse.x, ev.mouse.y)) {
+				if (kPdaAccuseRect.contains(ev.mouse.x, ev.mouse.y)) {
 					_nextScreen = kScreenAccuse;
-					exitFlag = true; break;
+					exitFlag = true;
+					break;
 				}
-				if (kBtnNotebook.contains(ev.mouse.x, ev.mouse.y)) {
+				if (kPdaNotebookRect.contains(ev.mouse.x, ev.mouse.y)) {
 					_nextScreen = kScreenNotebook;
-					exitFlag = true; break;
+					exitFlag = true;
+					break;
 				}
-				if (kBtnHelp.contains(ev.mouse.x, ev.mouse.y)) {
+				if (kPdaHelpRect.contains(ev.mouse.x, ev.mouse.y)) {
 					// Gallery rect 1 ??? `_InterfaceHelp(0)` per jmp table at
 					// 158f:0625 (HandleGalleryButton). Same picture sequence
 					// as the notebook HELP button.
+					setInteractiveMouseCursor(false);
 					doInterfaceHelp(0);
 					lastDraw = 0;
 					continue;
 				}
-				if (kBtnPartner.contains(ev.mouse.x, ev.mouse.y)) {
+				if (kPdaPartnerHeadHintRect.contains(ev.mouse.x, ev.mouse.y)) {
+					setInteractiveMouseCursor(false);
 					doHelp();
 					lastDraw = 0;
 					continue;
@@ -2228,18 +2300,18 @@ void EEMEngine::doGallery() {
 						const bool floppyMI = isFloppy();
 						const uint suspectIdx = (uint)slotSuspect[i];
 						const byte *suspect = floppyMI
-							? _mystery.floppySuspectEntry(suspectIdx)
-							: gd + suspectIdx * 0x46;
+												  ? _mystery.floppySuspectEntry(suspectIdx)
+												  : gd + suspectIdx * 0x46;
 						if (!suspect)
 							break;
 						const uint16 detailPic =
 							READ_LE_UINT16(suspect + 0);
 						const uint clueCount = floppyMI
-							? (uint)suspect[4]
-							: READ_LE_UINT16(suspect + 8);
+												   ? (uint)suspect[4]
+												   : READ_LE_UINT16(suspect + 8);
 
 						Graphics::ManagedSurface ms(320, 200,
-							Graphics::PixelFormat::createFormatCLUT8());
+													Graphics::PixelFormat::createFormatCLUT8());
 						ms.clear();
 						if (haveBg) {
 							const int bw = MIN<int>(galBg.surface.w, 320);
@@ -2260,19 +2332,20 @@ void EEMEngine::doGallery() {
 						// stays visible on the left. Without this
 						// blit the suspect-detail screen has no
 						// partner.
+						setInteractiveMouseCursor(false);
 						{
 							const uint partnerAnim =
 								(_partner == 0) ? 2 : 0x10;
 							Animation partnerAni;
 							if (_aniArchive.loadAnimation(partnerAnim,
-														   partnerAni) &&
+														  partnerAni) &&
 								!partnerAni.empty()) {
 								const uint32 now = g_system->getMillis();
 								const uint frameIdx =
 									partnerFrameAtTick(0x02,
-										(uint)partnerAni.size(), now);
+													   (uint)partnerAni.size(), now);
 								blitAnimFrameAnchored(ms.surfacePtr(),
-									partnerAni[frameIdx], 5, 0x50);
+													  partnerAni[frameIdx], 5, 0x50);
 							}
 						}
 						// Full suspect picture at (0x94, 0xf).
@@ -2399,8 +2472,10 @@ void EEMEngine::doGallery() {
 							Common::Event drain;
 							while (g_system->getEventManager()->pollEvent(drain)) {
 								if (drain.type == Common::EVENT_QUIT ||
-									drain.type == Common::EVENT_RETURN_TO_LAUNCHER)
+									drain.type == Common::EVENT_RETURN_TO_LAUNCHER) {
+									setInteractiveMouseCursor(false);
 									return;
+								}
 							}
 						}
 						bool back = false;
@@ -2415,8 +2490,10 @@ void EEMEngine::doGallery() {
 									break;
 								}
 								if (e2.type == Common::EVENT_QUIT ||
-									e2.type == Common::EVENT_RETURN_TO_LAUNCHER)
+									e2.type == Common::EVENT_RETURN_TO_LAUNCHER) {
+									setInteractiveMouseCursor(false);
 									return;
+								}
 							}
 							// Per-tick `updateScreen()` so the SDL cursor
 							// follows the mouse ??? without it the cursor
@@ -2431,6 +2508,13 @@ void EEMEngine::doGallery() {
 						// MoreInfo screen until the next 100 ms tick.
 						drawGalleryFrame(gd, num, slotRects, slotSuspect);
 						lastDraw = g_system->getMillis();
+						mouse = g_system->getEventManager()->getMousePos();
+						setInteractiveMouseCursor(galleryButtonAt(mouse.x,
+																  mouse.y) ||
+												  gallerySlotAt(slotRects,
+																slotSuspect,
+																mouse.x,
+																mouse.y));
 						clicked = true;
 						break;
 					}
@@ -2445,6 +2529,10 @@ void EEMEngine::doGallery() {
 		if (now - lastDraw >= 100) {
 			drawGalleryFrame(gd, num, slotRects, slotSuspect);
 			lastDraw = now;
+			mouse = g_system->getEventManager()->getMousePos();
+			setInteractiveMouseCursor(galleryButtonAt(mouse.x, mouse.y) ||
+									  gallerySlotAt(slotRects, slotSuspect,
+													mouse.x, mouse.y));
 		}
 		// `g_system->updateScreen()` is what tells the framework to
 		// re-render the cursor at its current mouse position; without
@@ -2454,6 +2542,7 @@ void EEMEngine::doGallery() {
 		g_system->updateScreen();
 		g_system->delayMillis(15);
 	}
+	setInteractiveMouseCursor(false);
 }
 
 void EEMEngine::drawGalleryFrame(const byte *gd, uint8 numSuspects,
@@ -4283,7 +4372,9 @@ void EEMEngine::doAccuse() {
 		playAnm(Common::Path("SCRAPBK.ANI"), 120, true);
 
 		// `_ShowOneScrap @ 1f78:0773` is `_DisplayEnding(num, 1)` ???
-		// the multi-page per-mystery ending narrative.
+		// the multi-page per-mystery ending narrative. This shares the
+		// same red edge cursor + edge-only mouse navigation used by the
+		// scrapbook browser.
 		doShowEnding(mn);
 
 		// Mirrors `_SavePlayerRecord` at 1df2:0857 ??? once the


Commit: 3354f1af87df5f24b18641559fee9c4ca1f54e54
    https://github.com/scummvm/scummvm/commit/3354f1af87df5f24b18641559fee9c4ca1f54e54
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:20:59+02:00

Commit Message:
EEM: handle initial clues for the CD version

Changed paths:
    engines/eem/clues.cpp


diff --git a/engines/eem/clues.cpp b/engines/eem/clues.cpp
index 62cf4047ec2..35396daedfc 100644
--- a/engines/eem/clues.cpp
+++ b/engines/eem/clues.cpp
@@ -64,6 +64,29 @@ const int kBoyY  = 0x62; // 98
 const int kGirlX = 0x42; // 66
 const int kGirlY = 0x60; // 96
 
+uint markClueBlockNotebookEntries(Mystery &mystery, const byte *clueBlock) {
+	if (!clueBlock)
+		return 0;
+
+	const uint16 number = READ_LE_UINT16(clueBlock);
+	if (number == 0 || number > 32)
+		return 0;
+
+	uint marked = 0;
+	for (uint i = 0; i < number; i++) {
+		const byte *entry = clueBlock + 4 + i * 62;
+		for (uint j = 0; j < 5; j++) {
+			const uint16 note = READ_LE_UINT16(entry + 0x30 + j * 2);
+			if (note != 0xFFFF && note < Mystery::kCluesFoundCap &&
+				mystery._cluesFound[note] == 0) {
+				mystery._cluesFound[note] = 1;
+				marked++;
+			}
+		}
+	}
+	return marked;
+}
+
 // `_DoHappiness @ 172b:27b5`: each cursor zone swaps the partner's
 // sequence script to a more / less "happy" cycle. Boy seqs lifted
 // verbatim from `29be:0337` (5 ?? 0x14 bytes), girl seqs from
@@ -540,10 +563,24 @@ void EEMEngine::doInitClues() {
 	// dialog_records[nDialog]` (each record `11 + textCount` bytes),
 	// dispatched via `FUN_22dc_05c8 @ 22dc:05c8`. We render dialog
 	// records ourselves on floppy.
-	if (floppy)
+	if (floppy) {
 		displayFloppyBriefing(ib);
-	else
-		displayClue(ib + 4);
+	} else {
+		const byte *briefingClues = ib + 4;
+		// Ghidra confirms CD `_DoInitClues` enters the normal
+		// `_DisplayClue(_InitBlock + 2, 1)` path, and `_DisplayClue`
+		// calls `_AddNotebook` for each ClueEntry note list at
+		// +0x30..+0x39. These starting notes are required before the
+		// first PDA visit, so mark them explicitly here. The regular
+		// displayClue side-effect pass is idempotent and still handles
+		// gallery/site updates in the original order.
+		const uint marked = markClueBlockNotebookEntries(_mystery, briefingClues);
+		if (marked != 0)
+			debugC(1, kDebugScript,
+				   "doInitClues: marked %u CD briefing notebook entries",
+				   marked);
+		displayClue(briefingClues);
+	}
 }
 
 /// Mirror `_ParseString` @ 1b66:07c3 ??? substitute the control bytes that


Commit: 7ac0106b653f132bfecd216dd58e2834786184ff
    https://github.com/scummvm/scummvm/commit/7ac0106b653f132bfecd216dd58e2834786184ff
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:20:59+02:00

Commit Message:
EEM: improved animations

Changed paths:
    engines/eem/animation.cpp
    engines/eem/animation.h
    engines/eem/clues.cpp
    engines/eem/eem.cpp
    engines/eem/eem.h


diff --git a/engines/eem/animation.cpp b/engines/eem/animation.cpp
index 929fc18025e..34364c6bf12 100644
--- a/engines/eem/animation.cpp
+++ b/engines/eem/animation.cpp
@@ -141,6 +141,14 @@ void ANMDecoder::getPalette8(byte *out) const {
 		out[i] = (byte)(_palette[i] << 2);
 }
 
+void ANMDecoder::seedFrameBuffer(const byte *pixels, uint pitch) {
+	if (!pixels || _buffer.empty() || _width == 0 || _height == 0)
+		return;
+
+	for (uint y = 0; y < _height; y++)
+		memcpy(_buffer.data() + y * _width, pixels + y * pitch, _width);
+}
+
 const byte *ANMDecoder::nextFrame() {
 	if (_nextFrameIdx >= _frameCount)
 		return nullptr;
diff --git a/engines/eem/animation.h b/engines/eem/animation.h
index 9397872e2fd..09b6e0406d1 100644
--- a/engines/eem/animation.h
+++ b/engines/eem/animation.h
@@ -67,6 +67,9 @@ public:
 	/// Fill @p out (768 bytes) with the 8-bit-shifted palette. Convenience.
 	void getPalette8(byte *out) const;
 
+	/// Seed the persistent frame buffer from the current 8-bit screen pixels.
+	void seedFrameBuffer(const byte *pixels, uint pitch);
+
 	/**
 	 * Decode the next frame in place into the internal buffer. Returns a
 	 * pointer to the @c width()*@c height() byte image, or nullptr at EOF.
diff --git a/engines/eem/clues.cpp b/engines/eem/clues.cpp
index 35396daedfc..82805c95635 100644
--- a/engines/eem/clues.cpp
+++ b/engines/eem/clues.cpp
@@ -427,17 +427,23 @@ void EEMEngine::doInitClues() {
 		}
 	}
 
-	// Composite the final frames (or first frames if skipped) so the BG
-	// is in a sensible state when displayClue overlays the speaker.
-	if (_picsArchive.getPicture(0x52, bg))
-		blitAt(bg, 0, 0);
-	if (haveGame)
-		blitMaskedToScreen(game[0], 0xcd, 0x6c);
-	if (haveBook)
-		blitMaskedToScreen(book[0], 0, 99);
-	if (haveNancy)
-		blitMaskedToScreen(nancy[0], 0x68, 0x8b);
-	g_system->updateScreen();
+	// Freeze the completed setup animation as the base for
+	// `_PlayInSequence`. The original clears the registered animations
+	// before playing the short case-type overlay, so the game/book/nancy
+	// cells do not keep cycling or wrap back to cell 0 underneath it.
+	Graphics::ManagedSurface briefingBase(320, 200,
+		Graphics::PixelFormat::createFormatCLUT8());
+	briefingBase.clear();
+	{
+		Graphics::Surface *screen = g_system->lockScreen();
+		if (screen) {
+			for (int row = 0; row < 200; row++) {
+				memcpy((byte *)briefingBase.getBasePtr(0, row),
+					   (const byte *)screen->getBasePtr(0, row), 320);
+			}
+			g_system->unlockScreen();
+		}
+	}
 
 	// Step 5 ??? `_PlayInSequence(animSeq, 0xcd, animY)` per Ghidra:
 	//   Jake (partner=0):
@@ -489,16 +495,11 @@ void EEMEngine::doInitClues() {
 			for (uint frame = 0; frame < seq.size() && !shouldQuit() && !skip;
 				 frame++) {
 				const Picture &fr = seq[frame];
-				// Restore BG + base anim frames so each new frame
-				// composites cleanly.
-				if (_picsArchive.getPicture(0x52, bg))
-					blitAt(bg, 0, 0);
-				if (haveGame)
-					blitMaskedToScreen(game[frame % game.size()], 0xcd, 0x6c);
-				if (haveBook)
-					blitMaskedToScreen(book[frame % book.size()], 0, 99);
-				if (haveNancy)
-					blitMaskedToScreen(nancy[frame % nancy.size()], 0x68, 0x8b);
+				// Restore the frozen setup frame so the short overlay
+				// does not make the setup animation wrap to frame 0.
+				g_system->copyRectToScreen(briefingBase.getPixels(),
+										   briefingBase.pitch, 0, 0,
+										   320, 200);
 				// Anchor: `_PlayInSequence @ 172b:2d35-2d50` does
 				//   dstX = sx - cell[+0x8]     ; miscflags (signed)
 				//   dstY = sy - cell[+0x6]     ; rowoff   (signed)
diff --git a/engines/eem/eem.cpp b/engines/eem/eem.cpp
index 6ca48a2a385..58dcfaa572b 100644
--- a/engines/eem/eem.cpp
+++ b/engines/eem/eem.cpp
@@ -99,6 +99,34 @@ const byte kCursorInteractivePalette[] = {
 	0xFF, 0xFF, 0xFF  // 2 ??? white fill
 };
 
+static void fadeCurrentPaletteToBlack(uint delayMs = 8) {
+	byte start[kPalSize];
+	byte stepPal[kPalSize];
+	g_system->getPaletteManager()->grabPalette(start, 0, 256);
+
+	for (int step = 15; step >= 0; step--) {
+		for (uint i = 0; i < kPalSize; i++)
+			stepPal[i] = (byte)((uint)start[i] * step / 16);
+		g_system->getPaletteManager()->setPalette(stepPal, 0, 256);
+		g_system->updateScreen();
+		if (delayMs)
+			g_system->delayMillis(delayMs);
+	}
+}
+
+static void fadePaletteFromBlack(const byte *target, uint delayMs = 8) {
+	byte stepPal[kPalSize];
+
+	for (uint step = 1; step <= 16; step++) {
+		for (uint i = 0; i < kPalSize; i++)
+			stepPal[i] = (byte)((uint)target[i] * step / 16);
+		g_system->getPaletteManager()->setPalette(stepPal, 0, 256);
+		g_system->updateScreen();
+		if (delayMs)
+			g_system->delayMillis(delayMs);
+	}
+}
+
 static void setInteractiveCursorPalette(const Picture &cursor, byte transparent) {
 	byte palette[kPalSize];
 	bool used[256];
@@ -349,7 +377,10 @@ Common::Error EEMEngine::run() {
 		if (!shouldQuit() && !_skipIntro) {
 			if (_audio)
 				_audio->playVoc(Common::Path("THUNDER.VOC"));
-			playAnm(Common::Path("BOLT.ANM"));
+			playAnm(Common::Path("BOLT.ANM"), 120,
+					/*holdLastFrame=*/false, /*fadeIn=*/true);
+			waitForInput(1800);
+			fadeCurrentPaletteToBlack();
 			if (_audio)
 				_audio->stopVoice();
 		}
@@ -363,8 +394,12 @@ Common::Error EEMEngine::run() {
 		if (!shouldQuit() && !_skipIntro && _music)
 			_music->playFile(Common::Path("THEME.XMI"), /*loop=*/true);
 		for (int i = 1; i <= 20 && !shouldQuit() && !_skipIntro; i++) {
+			const bool fadeIn = (i == 1 || i == 5);
+			if (i == 5)
+				fadeCurrentPaletteToBlack();
 			Common::String name = Common::String::format("ANIM%02d.A", i);
-			playAnm(Common::Path(name));
+			playAnm(Common::Path(name), 120,
+					/*holdLastFrame=*/false, fadeIn);
 			// `_SpoolSound(uVar3 - 1)` at 2520:08c2 ??? per-character
 			// VO after each anim except the last (`if (uVar3 != 0x14)`
 			// at 2520:08a8). Original blocks until done; we run async
@@ -383,7 +418,8 @@ Common::Error EEMEngine::run() {
 		if (!shouldQuit() && !_skipIntro && _music)
 			_music->playFile(Common::Path("THEME.XMI"), /*loop=*/true);
 		if (!shouldQuit() && !_skipIntro)
-			playAnm(Common::Path("TITLE.ANM"), 120, /*holdLastFrame=*/true);
+			playAnm(Common::Path("TITLE.ANM"), 120,
+					/*holdLastFrame=*/true, /*fadeIn=*/true);
 	}
 	_skipIntro = false;
 
@@ -668,7 +704,8 @@ void EEMEngine::interruptAudio() {
 		_music->stop();
 }
 
-void EEMEngine::playAnm(const Common::Path &path, uint frameDelayMs, bool holdLastFrame) {
+void EEMEngine::playAnm(const Common::Path &path, uint frameDelayMs,
+						bool holdLastFrame, bool fadeIn) {
 	ANMDecoder anm;
 	if (!anm.open(path)) {
 		warning("playAnm: %s missing", path.toString().c_str());
@@ -677,17 +714,32 @@ void EEMEngine::playAnm(const Common::Path &path, uint frameDelayMs, bool holdLa
 
 	byte palette[768];
 	anm.getPalette8(palette);
-	g_system->getPaletteManager()->setPalette(palette, 0, 256);
 
 	const uint16 w = anm.width();
 	const uint16 h = anm.height();
+	{
+		Graphics::Surface *screen = g_system->lockScreen();
+		if (screen) {
+			if (screen->w >= w && screen->h >= h)
+				anm.seedFrameBuffer((const byte *)screen->getBasePtr(0, 0), screen->pitch);
+			g_system->unlockScreen();
+		}
+	}
 
+	bool paletteApplied = false;
 	while (!shouldQuit()) {
 		const byte *frame = anm.nextFrame();
 		if (!frame)
 			break;
 
 		g_system->copyRectToScreen(frame, w, 0, 0, w, h);
+		if (!paletteApplied) {
+			if (fadeIn)
+				fadePaletteFromBlack(palette);
+			else
+				g_system->getPaletteManager()->setPalette(palette, 0, 256);
+			paletteApplied = true;
+		}
 		g_system->updateScreen();
 
 		// Drain events and let the user skip with click/key. The original
diff --git a/engines/eem/eem.h b/engines/eem/eem.h
index d6080d93fbd..6f463b4da69 100644
--- a/engines/eem/eem.h
+++ b/engines/eem/eem.h
@@ -380,9 +380,12 @@ private:
 	 *
 	 * If @p holdLastFrame is true the call blocks on the final frame
 	 * until the user clicks or hits a key ??? used for the title screen.
+	 * If @p fadeIn is true the first decoded frame is copied while the
+	 * palette is black, then the animation palette is ramped in like
+	 * `_OpenFadeIn`.
 	 */
 	void playAnm(const Common::Path &path, uint frameDelayMs = 120,
-				 bool holdLastFrame = false);
+				 bool holdLastFrame = false, bool fadeIn = false);
 
 	/// Stop every active audio channel ??? voice, sound spool, and
 	/// MIDI. Mirrors the `_CleanMysterySounds @ 202f:05a5` +


Commit: 9653bf379238d61f7faab0be2d4a5eaa9ad72abe
    https://github.com/scummvm/scummvm/commit/9653bf379238d61f7faab0be2d4a5eaa9ad72abe
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:20:59+02:00

Commit Message:
EEM: do not close the game after the end of the scrapbook

Changed paths:
    engines/eem/eem.cpp
    engines/eem/ui.cpp


diff --git a/engines/eem/eem.cpp b/engines/eem/eem.cpp
index 58dcfaa572b..35b76dab571 100644
--- a/engines/eem/eem.cpp
+++ b/engines/eem/eem.cpp
@@ -498,18 +498,20 @@ screen_loop:
 			// the same routing the `kScreenChooseMystery` case uses.
 			// Reachable from `_DisplayCorrect`'s 0xc write after a
 			// solve (see `ui.cpp` `_nextScreen = kScreenAction`).
+			_nextScreen = kScreenInvalid;
 			doCaseSelection();
-			_nextScreen = _mystery.isLoaded() ? kScreenInitClues
-											  : kScreenInvalid;
+			if (_mystery.isLoaded())
+				_nextScreen = kScreenInitClues;
 			break;
 
 		case kScreenChooseMystery:
 			// Handler 10 at 1a35:0e0e calls `_DoChooseMystery` which
 			// presets `_NextScreen = 0` (INIT_CLUES) before
 			// `_CaseSelection`. Same dispatch as `kScreenAction`.
+			_nextScreen = kScreenInvalid;
 			doCaseSelection();
-			_nextScreen = _mystery.isLoaded() ? kScreenInitClues
-											  : kScreenInvalid;
+			if (_mystery.isLoaded())
+				_nextScreen = kScreenInitClues;
 			break;
 
 		case kScreenInitClues:
diff --git a/engines/eem/ui.cpp b/engines/eem/ui.cpp
index 0eec0d2fe5e..caaf8bcdeed 100644
--- a/engines/eem/ui.cpp
+++ b/engines/eem/ui.cpp
@@ -879,20 +879,28 @@ void EEMEngine::doShowScrapbook(uint stage) {
 	while (!shouldQuit() && mystery >= lo && mystery < hi) {
 		const int direction = doShowEnding((uint)mystery, firstPage);
 		if (direction < 0) {
-			if (mystery == lo)
-				break;
-			mystery--;
+			int prevMystery = mystery - 1;
 			if (currentTier) {
-				while (mystery >= lo && _mysteriesSolved[mystery] == 0)
-					mystery--;
+				while (prevMystery >= lo && _mysteriesSolved[prevMystery] == 0)
+					prevMystery--;
+			}
+			if (prevMystery < lo) {
+				firstPage = true;
+				continue;
 			}
+			mystery = prevMystery;
 			firstPage = false;
 		} else if (direction > 0) {
-			mystery++;
+			int nextMystery = mystery + 1;
 			if (currentTier) {
-				while (mystery < hi && _mysteriesSolved[mystery] == 0)
-					mystery++;
+				while (nextMystery < hi && _mysteriesSolved[nextMystery] == 0)
+					nextMystery++;
+			}
+			if (nextMystery >= hi) {
+				firstPage = false;
+				continue;
 			}
+			mystery = nextMystery;
 			firstPage = true;
 		} else {
 			break;
@@ -1550,6 +1558,7 @@ void EEMEngine::doCaseSelection() {
 		doShowScrapbook(stage);
 		setSitePalette(0);
 		_mystery.clear();
+		_nextScreen = kScreenChooseMystery;
 		return;
 	}
 


Commit: 5c5a2d1ccf54671d91256011c89f7151b89f328c
    https://github.com/scummvm/scummvm/commit/5c5a2d1ccf54671d91256011c89f7151b89f328c
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:21:00+02:00

Commit Message:
EEM: honor the music config

Changed paths:
    engines/eem/clues.cpp
    engines/eem/eem.cpp
    engines/eem/eem.h
    engines/eem/site.cpp
    engines/eem/site.h
    engines/eem/ui.cpp


diff --git a/engines/eem/clues.cpp b/engines/eem/clues.cpp
index 82805c95635..708754fbd11 100644
--- a/engines/eem/clues.cpp
+++ b/engines/eem/clues.cpp
@@ -1065,7 +1065,8 @@ void EEMEngine::displayFloppyDialogRecords(const byte *rec, uint count,
 
 		// Per-record voice (byte 9 high bit) ??? see comment in original
 		// header.
-		if ((rec[9] & 0x80) != 0 && _audio) {
+		const bool playedRecordVoice = (rec[9] & 0x80) != 0 && _audio;
+		if (playedRecordVoice) {
 			const uint slot = rec[9] & 0x7f;
 			_audio->playFloppyVoiceSlot(slot, _partner);
 		}
@@ -1264,8 +1265,14 @@ void EEMEngine::displayFloppyDialogRecords(const byte *rec, uint count,
 					firstPage = true;
 			}
 		}
-		if (skipAll)
+		if (skipAll) {
+			if (playedRecordVoice)
+				_audio->stopVoice();
 			return;
+		}
+
+		if (playedRecordVoice)
+			_audio->stopVoice();
 
 		rec += 11 + textCount;
 	}
diff --git a/engines/eem/eem.cpp b/engines/eem/eem.cpp
index 35b76dab571..4513d010beb 100644
--- a/engines/eem/eem.cpp
+++ b/engines/eem/eem.cpp
@@ -938,12 +938,33 @@ void EEMEngine::startTravelMusic() {
 	// itself runs without music. Our previous `loop=true` made the
 	// music never end, leaving travel music droning through site
 	// investigation, accuse, gallery, etc.
-	if (!_music || !_mystery.isLoaded())
+	if (!_music || !_mystery.isLoaded() || !_voiceOn)
 		return;
 	const uint num = _mystery._siteNumber % 5;
 	_music->playMus(num, /*loop=*/false);
 }
 
+void EEMEngine::waitForMusicDone(uint32 maxMs) {
+	if (!_music)
+		return;
+
+	const uint32 startMs = g_system->getMillis();
+	while (_music->isPlaying() && !shouldQuit() &&
+		   g_system->getMillis() - startMs < maxMs) {
+		Common::Event ev;
+		while (g_system->getEventManager()->pollEvent(ev)) {
+			if (ev.type == Common::EVENT_QUIT ||
+				ev.type == Common::EVENT_RETURN_TO_LAUNCHER) {
+				stopMusic();
+				return;
+			}
+		}
+		g_system->updateScreen();
+		g_system->delayMillis(20);
+	}
+	stopMusic();
+}
+
 void EEMEngine::stopMusic() {
 	if (_music)
 		_music->stop();
@@ -1064,6 +1085,7 @@ Common::Error EEMEngine::loadGameStream(Common::SeekableReadStream *stream) {
 		s.syncAsUint16LE(mysteryNum);
 		if (!_mystery.load(mysteryNum, &_rng)) {
 			_mystery.clear();
+			resetSiteArrivalState();
 			return Common::kReadingFailed;
 		}
 		// `_ReadMystery @ 2404:008f` calls `_InitMysterySounds` at the
@@ -1076,8 +1098,13 @@ Common::Error EEMEngine::loadGameStream(Common::SeekableReadStream *stream) {
 		if (_audio && !isFloppy())
 			_audio->initMysterySounds(mysteryNum);
 		_mystery.syncState(s);
+		if (_mystery._siteNumber < _mystery.numSites())
+			setSiteArrivalState(_mystery._siteNumber);
+		else
+			resetSiteArrivalState();
 	} else {
 		_mystery.clear();
+		resetSiteArrivalState();
 	}
 
 	debugC(1, kDebugGeneral,
diff --git a/engines/eem/eem.h b/engines/eem/eem.h
index 6f463b4da69..78f7a5557bb 100644
--- a/engines/eem/eem.h
+++ b/engines/eem/eem.h
@@ -495,11 +495,16 @@ private:
 
 public:
 	/// Mirrors `_StartTravelMusic @ 20a2:0595`. Picks `MUS%05d.XMI`
-	/// based on `_mystery._siteNumber % 5` and starts it (looping). The
+	/// based on `_mystery._siteNumber % 5` and starts it one-shot. The
 	/// site loop calls this each time `enter(siteNum)` runs so the
 	/// music changes as the player travels between sites.
 	void startTravelMusic();
 
+	/// Block until the current MIDI cue finishes, then stop/unload it.
+	/// Mirrors the `_IsMIDIPlaying` spin + `_StopMIDI` cleanup in
+	/// `_DoSiteLoop` after the CD travel cue.
+	void waitForMusicDone(uint32 maxMs = 60000);
+
 	/// Stop any currently playing MIDI track. Mirrors `_StopMIDI @
 	/// 20a2:0512` ??? used by `_DoSiteLoop @ 168d:06c0` after the
 	/// one-shot travel track plays out and by `_DisplayCorrect /
@@ -580,6 +585,11 @@ private:
 
 	bool _interactiveMouseCursor = false;
 
+	/// Site whose entrance animation has already played in the current
+	/// mystery. Kept on the engine, not SiteScreen, because PDA/gallery
+	/// screens destroy and recreate SiteScreen when returning to the site.
+	int _lastSiteArrivalAnim = -1;
+
 	/// XMIDI music player. Mirrors the original `MIDI.C` family
 	/// (`_MIDIPlayFile`, `_MIDIPlay`, `_StopMIDI`, `_StartTravelMusic`
 	/// at 20a2:00e2-05c9). Constructed lazily during `run()` once the
@@ -598,6 +608,18 @@ public:
 	/// member itself public. Mirrors the original's direct write to
 	/// `_NextScreen @ 2d5d:3f26` from anywhere in the engine.
 	void setNextScreen(ScreenId s) { _nextScreen = s; }
+	bool shouldPlaySiteArrival(uint siteNum) const {
+		return _lastSiteArrivalAnim != (int)siteNum;
+	}
+	void markSiteArrivalPlayed(uint siteNum) {
+		_lastSiteArrivalAnim = (int)siteNum;
+	}
+	void resetSiteArrivalState() {
+		_lastSiteArrivalAnim = -1;
+	}
+	void setSiteArrivalState(uint siteNum) {
+		_lastSiteArrivalAnim = (int)siteNum;
+	}
 };
 
 } // End of namespace EEM
diff --git a/engines/eem/site.cpp b/engines/eem/site.cpp
index 1a71f83e417..eeb7d78bd3d 100644
--- a/engines/eem/site.cpp
+++ b/engines/eem/site.cpp
@@ -540,10 +540,13 @@ void SiteScreen::enter(uint siteNum, bool resetPartnerMood) {
 	debugC(1, kDebugSite, "Entering site %u (%u hotspots)",
 		   siteNum, _mystery->hotspotCount(siteNum));
 
+	const bool playArrival = _vm->shouldPlaySiteArrival(siteNum);
+
 	// `_DoTravel @ 168d:02da` calls `_StartTravelMusic` after the
 	// destination is set. We do the same here so the music swaps as
 	// the player moves between sites.
-	_vm->startTravelMusic();
+	if (playArrival)
+		_vm->startTravelMusic();
 
 	// Palette: original `_BuildBackground` calls `GetPalette(sitenum + 1)`
 	// where sitenum is the global SITES.DBD index (= the per-mystery
@@ -595,10 +598,10 @@ void SiteScreen::enter(uint siteNum, bool resetPartnerMood) {
 	renderBackground(siteNum);
 
 	// `_DoSiteLoop @ 168d:03f4` plays `_EnterSiteAnim` whenever
-	// `_LastSite != _SiteNumber`. We track the last site we *played*
-	// the arrival on so re-entries (after notebook/map/etc.) don't
-	// repeat the animation.
-	if ((int)siteNum != _lastSiteAnim) {
+	// `_LastSite != _SiteNumber`. Keep our guard on the engine rather
+	// than on SiteScreen, because PDA/gallery returns recreate
+	// SiteScreen and must not replay the arrival.
+	if (playArrival) {
 		// `_EnterSiteAnim` snapshots the current screen, so populate
 		// that temporary background with the same site layers that
 		// should already be visible behind the arriving partner.
@@ -608,21 +611,15 @@ void SiteScreen::enter(uint siteNum, bool resetPartnerMood) {
 			renderStaticDrops(siteNum);
 		renderAnimatedDrops(siteNum, g_system->getMillis());
 		enterSiteAnim();
-		_lastSiteAnim = (int)siteNum;
+		_vm->markSiteArrivalPlayed(siteNum);
+		if (!_vm->isFloppy())
+			_vm->waitForMusicDone();
 		// Re-paint the BG; the normal snapshot below should contain
 		// only the static layers, while animated NPCs are redrawn per
 		// tick by the frame pump.
 		renderBackground(siteNum);
 	}
 
-	// Stop the travel music explicitly. `_DoSiteLoop @ 168d:06c0`
-	// waits for the one-shot travel track to play out and then calls
-	// `_StopMIDI()` before the interactive site phase begins ???
-	// blocking the engine while it spins. We just stop now so the
-	// site investigation runs without music (matches the original
-	// silent-investigation phase).
-	_vm->stopMusic();
-
 	// Static drops (Loop 2 from `_DoSiteLoop`) ??? no animation, baked
 	// into the BG snapshot the run() pump uses to restore. Floppy
 	// stores them in a different shape (drops sub-struct after the
@@ -742,12 +739,12 @@ void SiteScreen::run() {
 
 	while (!_vm->shouldQuit()) {
 		Common::Event event;
-		bool exitRequested = false;
 		while (g_system->getEventManager()->pollEvent(event)) {
 			switch (event.type) {
 			case Common::EVENT_QUIT:
 			case Common::EVENT_RETURN_TO_LAUNCHER:
 				_vm->setHotspotMouseCursor(false);
+				_vm->stopMusic();
 				return;
 
 			case Common::EVENT_MOUSEMOVE:
@@ -759,9 +756,9 @@ void SiteScreen::run() {
 				//   _FindButton(&SiteButtons, 2, MouseX, MouseY)
 				// where `SiteButtons` is two 8-byte rectangles at
 				// 29be:0x274 (verified via 168d:0729-0848):
-				//   Button 0: (35, 111) - (56, 136)  ??? notebook
+				//   Button 0: (35, 111) - (56, 136)  -> notebook
 				//                                       (`_NextScreen = 4`)
-				//   Button 1: (7, 177)  - (57, 200)  ??? map
+				//   Button 1: (7, 177)  - (57, 200)  -> map
 				//                                       (CD `_NextScreen = 1`,
 				//                                       floppy = 2)
 				// Test the buttons before falling through to hotspots so
@@ -780,6 +777,7 @@ void SiteScreen::run() {
 					notePartnerActivity();
 					_vm->setHotspotMouseCursor(false);
 					_vm->setNextScreen(kScreenNotebook);
+					_vm->stopMusic();
 					return;
 				}
 				if (kSitePartnerFootMapRect.contains(event.mouse.x, event.mouse.y)) {
@@ -788,6 +786,7 @@ void SiteScreen::run() {
 					// CD writes `_NextScreen = 1`; floppy writes 2.
 					_vm->setNextScreen(_vm->isFloppy() ? kScreenMapAlt
 													   : kScreenMap);
+					_vm->stopMusic();
 					return;
 				}
 				if (kSitePartnerHeadHintRect.contains(event.mouse.x, event.mouse.y)) {
@@ -818,15 +817,14 @@ void SiteScreen::run() {
 				// 6-entry table at `168d:09d5` (TAB / ENTER / arrow
 				// keys for hotspot cursor cycling) plus ESC handled
 				// separately at 168d:07a9. We don't implement the
-				// hotspot cursor cycling ??? clicks are the primary
-				// interaction ??? so the only keyboard binding kept
-				// here is ESC (matches `_ESCHit` ??? "Are you sure?"
-				// ??? MAP).
+				// hotspot cursor cycling, so the only keyboard binding kept
+				// here is ESC (matches `_ESCHit` -> "Are you sure?" -> MAP).
 				if (event.kbd.keycode == Common::KEYCODE_ESCAPE) {
 					_vm->setHotspotMouseCursor(false);
 					if (_vm->areYouSure()) {
 						_vm->setNextScreen(_vm->isFloppy() ? kScreenMapAlt
 														   : kScreenMap);
+						_vm->stopMusic();
 						return;
 					}
 					enter(cur, false);
@@ -839,14 +837,14 @@ void SiteScreen::run() {
 				break;
 			}
 		}
-		if (exitRequested)
-			return;
 
 		// Hotspot side effects can invalidate the active mystery; exit
 		// immediately rather than tick another frame against stale BG
 		// snapshots / hotspot tables.
-		if (!_mystery || !_mystery->isLoaded())
+		if (!_mystery || !_mystery->isLoaded()) {
+			_vm->stopMusic();
 			return;
+		}
 
 		// Per-tick frame pump (mirrors `_CheckFrameRate` +
 		// `_UpdateAnimations` at the top of `_DoSiteLoop`'s main loop).
diff --git a/engines/eem/site.h b/engines/eem/site.h
index 06bc8595369..d6f6557c381 100644
--- a/engines/eem/site.h
+++ b/engines/eem/site.h
@@ -176,7 +176,6 @@ private:
 		kPartnerWaitPatient,
 		kPartnerWaitImpatient
 	};
-	int _lastSiteAnim = -1;        ///< Last site we played the arrival on.
 	int _snapshotSite = -1;        ///< Site number the snapshot belongs to.
 	Graphics::ManagedSurface _bgSnapshot;
 	uint32 _lastTickMs = 0;        ///< Last frame-pump tick in ms.
diff --git a/engines/eem/ui.cpp b/engines/eem/ui.cpp
index caaf8bcdeed..fdc71dc6695 100644
--- a/engines/eem/ui.cpp
+++ b/engines/eem/ui.cpp
@@ -1540,8 +1540,11 @@ void EEMEngine::doCaseSelection() {
 		if (!_mystery.load(0, &_rng)) {
 			warning("doCaseSelection: failed to load practice mystery");
 			_mystery.clear();
-		} else if (_audio && !isFloppy()) {
-			_audio->initMysterySounds(0);
+			resetSiteArrivalState();
+		} else {
+			resetSiteArrivalState();
+			if (_audio && !isFloppy())
+				_audio->initMysterySounds(0);
 		}
 		return;
 	}
@@ -1762,6 +1765,7 @@ void EEMEngine::doCaseSelection() {
 		_mystery.clear();
 		return;
 	}
+	resetSiteArrivalState();
 	if (_audio && !isFloppy())
 		_audio->initMysterySounds(mn);
 	debugC(1, kDebugMystery, "Mystery %u loaded; %u sites, %u suspects",
@@ -4134,7 +4138,7 @@ void EEMEngine::doAccuse() {
 		// Step 1 ??? alibi music. Original blocks until MIDI 6 ends with
 		// the gallery still on screen. We poll `_music->isPlaying`;
 		// click/ESC aborts early.
-		if (_music) {
+		if (_music && _voiceOn) {
 			_music->playMus(6, /*loop=*/false);
 			const uint32 musStart = g_system->getMillis();
 			bool aborted = false;
@@ -4142,8 +4146,10 @@ void EEMEngine::doAccuse() {
 				Common::Event ev;
 				while (g_system->getEventManager()->pollEvent(ev)) {
 					if (ev.type == Common::EVENT_QUIT ||
-						ev.type == Common::EVENT_RETURN_TO_LAUNCHER)
+						ev.type == Common::EVENT_RETURN_TO_LAUNCHER) {
+						_music->stop();
 						return;
+					}
 					if (ev.type == Common::EVENT_KEYDOWN ||
 						ev.type == Common::EVENT_LBUTTONDOWN) {
 						aborted = true;
@@ -4362,7 +4368,7 @@ void EEMEngine::doAccuse() {
 		}
 		g_system->updateScreen();
 
-		if (_music)
+		if (_music && _voiceOn)
 			_music->playMus(5, /*loop=*/false);
 
 		// Chain-by-chain RECAP. Partner enumerates every required
@@ -4374,6 +4380,8 @@ void EEMEngine::doAccuse() {
 		const byte *solved = _mystery.solvedClueBlock();
 		if (solved)
 			displayClue(solved);
+		if (_music && _voiceOn)
+			_music->stop();
 
 		// `_DifferenceAnimation("scrapbk.ani")` (1df2:0848) ??? the
 		// physical scrapbook flip animation that introduces the
@@ -4741,6 +4749,7 @@ void EEMEngine::doAccuseFloppy() {
 		//                 every mystery in the current tier is solved).
 		//   1d40:0982  _SavePlayerRecord  (= saveProfile)
 		//   1d40:0985  _DeleteMysteryFile (= mystery cleanup)
+		//   1d40:0991  MakeSolvedSound = 1; ShowOneScrap(mystery)
 		//   1d40:09b0  _NextScreen = 0xc.
 		const uint mn = _mystery.number();
 
@@ -4819,6 +4828,8 @@ void EEMEngine::doAccuseFloppy() {
 			const uint count = chain[0];
 			displayFloppyDialogRecords(chain + 1, count, 0);
 		}
+		if (_music && _voiceOn)
+			_music->stop();
 
 		// Mark mystery solved with first-try bonus tracking.
 		// `_DisplayCorrect_Floppy @ 1d40:0939`:
@@ -4859,6 +4870,27 @@ void EEMEngine::doAccuseFloppy() {
 			}
 		}
 
+		// `_DisplayCorrect_Floppy` sets `MakeSolvedSound` before the
+		// newly solved ending is shown. `FUN_1d40_05b7` reads byte 0 of
+		// `E<num>.BIN` and maps values 0..2 through the table at
+		// `2608:0c5e` to VOC slots 0x15, 0x16, 0x17.
+		if (_audio && _voiceOn) {
+			Common::File ending;
+			const Common::String fname =
+				Common::String::format("E%u.BIN", mn);
+			if (ending.open(Common::Path(fname)) && ending.size() > 0) {
+				static const uint8 kSolvedVoiceSlot[3] = {
+					0x15, 0x16, 0x17
+				};
+				const byte type = ending.readByte();
+				if (type < ARRAYSIZE(kSolvedVoiceSlot)) {
+					_audio->playFloppyVoiceSlot(kSolvedVoiceSlot[type],
+												_partner);
+					_audio->waitForVoiceDone();
+				}
+			}
+		}
+
 		// Persist progress before clearing the in-progress mystery.
 		// `_DisplayCorrect_Floppy @ 1d40:0982` calls
 		// `_SavePlayerRecord` then `FUN_22dc_0dbd` (which deletes the
@@ -5024,6 +5056,8 @@ void EEMEngine::doAccuseFloppy() {
 	// Our `showFloppyKDHint(slot)` reads `kdIdx + slot * 2`, so slot
 	// 5 is the right argument here.
 	showFloppyKDHint(5);
+	if (_music && _voiceOn)
+		_music->stop();
 
 	_mystery._firstTry = false;
 	_nextScreen = _lastScreen != kScreenInvalid


Commit: a1b4c596bc9ce82061e417eb84ff6816f12f411f
    https://github.com/scummvm/scummvm/commit/a1b4c596bc9ce82061e417eb84ff6816f12f411f
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:21:00+02:00

Commit Message:
EEM: added missing voices when selecting clues

Changed paths:
    engines/eem/mystery.cpp
    engines/eem/mystery.h
    engines/eem/ui.cpp


diff --git a/engines/eem/mystery.cpp b/engines/eem/mystery.cpp
index 3b6cb6b3c98..efbada6cab1 100644
--- a/engines/eem/mystery.cpp
+++ b/engines/eem/mystery.cpp
@@ -558,6 +558,43 @@ int Mystery::selectedPoints() const {
 	return total;
 }
 
+int Mystery::foundPoints() const {
+	const byte *ni = noteIndex();
+	const uint16 cnt = noteIndexCount();
+	if (!ni || cnt == 0)
+		return 0;
+
+	uint16 scores[Mystery::kCluesFoundCap] = {};
+	uint scoreCount = 0;
+	const uint maxIdx = MIN<uint>(cnt, kCluesFoundCap);
+	for (uint i = 0; i < maxIdx; i++) {
+		if (_cluesFound[i] == 0)
+			continue;
+		const uint16 pts = _isFloppy ? ni[i * 7 + 6]
+									 : READ_LE_UINT16(ni + i * 4 + 2);
+		scores[scoreCount++] = pts;
+	}
+
+	const uint topN = MIN<uint>(5u, scoreCount);
+	for (uint k = 0; k < topN; k++) {
+		uint best = k;
+		for (uint j = k + 1; j < scoreCount; j++) {
+			if (scores[j] > scores[best])
+				best = j;
+		}
+		if (best != k) {
+			const uint16 tmp = scores[k];
+			scores[k] = scores[best];
+			scores[best] = tmp;
+		}
+	}
+
+	int total = 0;
+	for (uint k = 0; k < topN; k++)
+		total += scores[k];
+	return total;
+}
+
 void Mystery::syncState(Common::Serializer &s) {
 	s.syncBytes(_cluesFound, kCluesFoundCap);
 	s.syncBytes(_noteSelected, kCluesFoundCap);
diff --git a/engines/eem/mystery.h b/engines/eem/mystery.h
index 82c881803cd..c9694d1965e 100644
--- a/engines/eem/mystery.h
+++ b/engines/eem/mystery.h
@@ -159,6 +159,10 @@ public:
 	/// `_GetSelectedPoints` @ 1df2:00bd.
 	int selectedPoints() const;
 
+	/// Sum of the top five point values among found notebook entries.
+	/// Mirrors `_GetFoundPoints` @ 1df2:0098.
+	int foundPoints() const;
+
 	/// True when `selectedPoints() > 99`. Mirrors `_SolvedCheck`.
 	bool solvedCheck() const { return selectedPoints() > 99; }
 
diff --git a/engines/eem/ui.cpp b/engines/eem/ui.cpp
index fdc71dc6695..5e1b23a8c12 100644
--- a/engines/eem/ui.cpp
+++ b/engines/eem/ui.cpp
@@ -3625,6 +3625,83 @@ void EEMEngine::doAccuse() {
 		return;
 	}
 
+	// `_AccuseEntry @ 1df2:0ff8` runs before the red accuse-notes
+	// picker. It scores the top five FOUND clues, says how close the
+	// player is, and only lets `_DoAccuse` continue when that score is
+	// at least 0x65.
+	const byte *entryKdIdx = _mystery.kdTextIndex();
+	if (!entryKdIdx)
+		return;
+	const int foundPoints = _mystery.foundPoints();
+	uint entryKDSpeak = 0;
+	uint16 entryTextOff = 0xFFFF;
+	bool canAccuse = false;
+	Common::String entryText;
+	if (foundPoints == 0) {
+		entryKDSpeak = 9;
+		entryText = "3We're not ready to solve this mystery yet.  "
+					"Let's keep investigating until we have some more solid "
+					"evidence to make our case!";
+	} else if (foundPoints < 0x32) {
+		entryKDSpeak = 0;
+		entryTextOff = READ_LE_UINT16(entryKdIdx + 0);
+	} else if (foundPoints < 0x65) {
+		entryKDSpeak = 1;
+		entryTextOff = READ_LE_UINT16(entryKdIdx + 2);
+	} else {
+		entryKDSpeak = 2;
+		entryTextOff = READ_LE_UINT16(entryKdIdx + 4);
+		canAccuse = true;
+	}
+	if (entryText.empty() && entryTextOff != 0xFFFF) {
+		entryText = parseString(_mystery.textAt(entryTextOff),
+								_playerName, _partner);
+	}
+	if (!entryText.empty()) {
+		Graphics::ManagedSurface ms(320, 200,
+			Graphics::PixelFormat::createFormatCLUT8());
+		ms.clear();
+		Graphics::Surface *cur = g_system->lockScreen();
+		if (cur) {
+			ms.simpleBlitFrom(*cur);
+			g_system->unlockScreen();
+		}
+		const byte firstChar = (byte)entryText[0];
+		const uint16 bubNum = getKDTextBalloon(firstChar);
+		if (firstChar >= '0' && firstChar <= '9')
+			entryText.deleteChar(0);
+		Picture balloon;
+		const bool haveBalloon =
+			_balloonArchive.size() > (bubNum & 0x7F) &&
+			_balloonArchive.loadEntry(bubNum & 0x7F, balloon);
+		const int balloonX = 0x21;
+		int balloonY = 1;
+		if (haveBalloon && balloon.surface.h < 0x4e)
+			balloonY = (0x50 - balloon.surface.h) / 2;
+		if (haveBalloon) {
+			const byte transp = (byte)(balloon.flags >> 8);
+			ms.transBlitFrom(balloon.surface,
+							 Common::Point(balloonX, balloonY),
+							 (uint32)transp);
+		}
+		uint16 tx = 5, ty = 4, tw = 155;
+		getBalloonInsets(bubNum, tx, ty, tw);
+		if (_font.isLoaded()) {
+			_font.drawWordWrapped(&ms, balloonX + tx, balloonY + ty,
+								  tw, entryText, haveBalloon ? 0 : 0xF);
+		}
+		g_system->copyRectToScreen(ms.getPixels(), ms.pitch,
+								   0, 0, 320, 200);
+		g_system->updateScreen();
+		if (_audio)
+			_audio->sayKDDigital(entryKdIdx, entryKDSpeak, _partner);
+		waitForInput(60000);
+	}
+	if (!canAccuse) {
+		_nextScreen = _lastScreen != kScreenInvalid
+						? (ScreenId)_lastScreen : kScreenSite;
+		return;
+	}
 
 	// Mirrors `_DoAccuse @ 1df2:0bdd` + `_DoAccuseGallery @ 1df2:0a31`:
 	//   1. ACCUSE-NOTES SCREEN (PIC 0x1A7, the red "accuse-mode" BG):
@@ -3780,6 +3857,7 @@ void EEMEngine::doAccuse() {
 	//   AddPicBackground(pic, 0x21, y)                    (1df2:0aab)
 	//   WordWrap(0x21+tbl[bub].x, y+tbl[bub].y, tbl[bub].w, text, color=0)
 	//     tbl @ 29be:0875, 10-byte entries (1df2:0ad6-0af1)
+	//   _SayKDDigital(4); _Wait();                        (1df2:0b09-0b11)
 	const byte *kdIdx = _mystery.kdTextIndex();
 	if (kdIdx) {
 		const int16 textOff = (int16)READ_LE_UINT16(kdIdx + 8);
@@ -3878,6 +3956,8 @@ void EEMEngine::doAccuse() {
 				g_system->copyRectToScreen(ms.getPixels(), ms.pitch,
 					0, 0, 320, 200);
 				g_system->updateScreen();
+				if (_audio)
+					_audio->sayKDDigital(kdIdx, 4, _partner);
 				waitForInput(8000);
 			}
 		}
@@ -3978,13 +4058,13 @@ void EEMEngine::doAccuse() {
 		return;
 
 	// Real chain evaluation. Mirrors the original two-gate accusation:
-	//   1. `_AccuseEntry @ 1df2:0ff8` checks `_GetFoundPoints() >= 100`
-	//      ??? gates whether the suspect picker is even reachable. We
-	//      `_SolvedCheck ??? selectedPoints > 99` is now gated at the
-	//      TOP of `doAccuse` ??? by the time we reach this point we
-	//      already know `solvedCheck()` was true (the picker wouldn't
-	//      have opened otherwise).
-	//   2. `_WITCH @ 1df2:089f` checks `GalleryData[picked*0x46+0x02] ==
+	//   1. `_AccuseEntry @ 1df2:0ff8` checked top-five found points
+	//      before the note picker, so by this point the player has
+	//      enough evidence to attempt an accusation.
+	//   2. `_SolvedCheck @ 1df2:00ec` checked selectedPoints > 99
+	//      before opening this suspect picker, so the clue set is
+	//      valid if we got here.
+	//   3. `_WITCH @ 1df2:089f` checks `GalleryData[picked*0x46+0x02] ==
 	//      0xFFFF`. Innocent suspects store an alibi-text TextBlock
 	//      offset there; the guilty one uses the sentinel.
 	const int points          = _mystery.selectedPoints();


Commit: 631819628568d080bcc5a8548a45b444f92c6b2d
    https://github.com/scummvm/scummvm/commit/631819628568d080bcc5a8548a45b444f92c6b2d
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:21:00+02:00

Commit Message:
EEM: don't allow to select more than 5 clues in accusation mode

Changed paths:
    engines/eem/ui.cpp


diff --git a/engines/eem/ui.cpp b/engines/eem/ui.cpp
index 5e1b23a8c12..2ff97c65045 100644
--- a/engines/eem/ui.cpp
+++ b/engines/eem/ui.cpp
@@ -3256,12 +3256,13 @@ bool EEMEngine::doAccuseNotes() {
 	//     for selected (1df2:0c2c sets it on entry).
 	//   * Click on a clue toggles its selection
 	//     (`_SearchNoteAreas` + `_SwapColors`).
-	//   * Click `_NoteButtons[2]` (rect at `(157, 174, 178, 190)`,
-	//     the original's "go to gallery" button) jumps to the
-	//     evidence check; `_HandleAccuseNoteButton(2)` returns 2
-	//     and the outer loop forces `uStack_8 = uStack_a` to
-	//     trigger `_SolvedCheck`.
-	//   * ESC sets `_NextScreen = 3` and exits.
+	//   * Click `_NoteButtons[4]` (rect at `(180, 174, 201, 190)`,
+	//     the original's solve button) jumps to the evidence check;
+	//     `_HandleAccuseNoteButton(4)` returns 2 and the outer loop
+	//     forces `uStack_8 = uStack_a` to trigger `_SolvedCheck`.
+	//   * CD and floppy originals only cancel this screen through ESC.
+	//     ScummVM also wires the visible PDA navigation buttons so the
+	//     accusation can be abandoned without a keyboard.
 	if (!_mystery.isLoaded() || !_font.isLoaded())
 		return false;
 	const byte *ni = _mystery.noteIndex();
@@ -3300,8 +3301,10 @@ bool EEMEngine::doAccuseNotes() {
 	const int rectH = 159 - 27;
 
 	// `_NoteButtons` rects (verified at `29be:0147`). `_DoAccuse`
-	// re-uses the same table as `_DoNotebook`, but only SOLVE /
-	// PAGE NEXT / PAGE PREV do anything; others sit inert.
+	// re-uses the same table as `_DoNotebook`, but the original
+	// handler only routes SOLVE / PAGE NEXT / PAGE PREV; ScummVM
+	// additionally routes the visible site/map/notebook/gallery
+	// buttons below for pointer-only cancellation.
 	// `_HandleAccuseNoteButton @ 1df2:0990` returns `DI` (initialised
 	// to 0) and only sets `DI = 2` in the `i == 4` branch (asm:
 	// `1df2:09b2: MOV DI, 0x2`). The outer loop's `iVar6 == 2` test
@@ -3471,17 +3474,31 @@ bool EEMEngine::doAccuseNotes() {
 
 	rebuildPagination();
 	draw();
+	Common::Point mouse = g_system->getEventManager()->getMousePos();
+	setInteractiveMouseCursor(notebookButtonAt(mouse.x, mouse.y) ||
+							  kPdaNotebookRect.contains(mouse.x, mouse.y) ||
+							  rectListContains(slotRects, mouse.x, mouse.y));
 
 	while (!shouldQuit()) {
 		Common::Event ev;
 		bool dirty = false;
 		while (g_system->getEventManager()->pollEvent(ev)) {
 			if (ev.type == Common::EVENT_QUIT ||
-				ev.type == Common::EVENT_RETURN_TO_LAUNCHER)
+				ev.type == Common::EVENT_RETURN_TO_LAUNCHER) {
+				_nextScreen = kScreenInvalid;
 				return false;
+			}
+			if (ev.type == Common::EVENT_MOUSEMOVE) {
+				setInteractiveMouseCursor(
+					notebookButtonAt(ev.mouse.x, ev.mouse.y) ||
+					kPdaNotebookRect.contains(ev.mouse.x, ev.mouse.y) ||
+					rectListContains(slotRects, ev.mouse.x, ev.mouse.y));
+			}
 			if (ev.type == Common::EVENT_KEYDOWN) {
-				if (ev.kbd.keycode == Common::KEYCODE_ESCAPE)
+				if (ev.kbd.keycode == Common::KEYCODE_ESCAPE) {
+					_nextScreen = kScreenSite;
 					return false;
+				}
 				if (ev.kbd.keycode == Common::KEYCODE_LEFT &&
 					page > 0) {
 					page--;
@@ -3495,6 +3512,29 @@ bool EEMEngine::doAccuseNotes() {
 			if (ev.type == Common::EVENT_LBUTTONDOWN) {
 				const int mx = ev.mouse.x;
 				const int my = ev.mouse.y;
+				if (kPdaSiteRect.contains(mx, my)) {
+					_nextScreen = kScreenSite;
+					return false;
+				}
+				if (kPdaPartnerFootMapRect.contains(mx, my)) {
+					_nextScreen = kScreenMapAlt;
+					return false;
+				}
+				if (kPdaNotebookRect.contains(mx, my)) {
+					_nextScreen = kScreenNotebook;
+					return false;
+				}
+				if (kPdaGalleryRect.contains(mx, my)) {
+					_nextScreen = kScreenGallery;
+					return false;
+				}
+				if (kPdaHelpRect.contains(mx, my) ||
+					kPdaHelp2Rect.contains(mx, my)) {
+					setInteractiveMouseCursor(false);
+					doInterfaceHelp(0);
+					dirty = true;
+					continue;
+				}
 				// Page navigation ??? `_NoteButtons[5]` / `[6]`,
 				// dispatched in `_HandleAccuseNoteButton @
 				// 1df2:0990`. Only effective if there's another
@@ -3548,42 +3588,18 @@ bool EEMEngine::doAccuseNotes() {
 				for (uint i = 0; i < slotRects.size(); i++) {
 					if (slotRects[i].contains(mx, my)) {
 						const uint clueId = slotClues[i];
-						_mystery._noteSelected[clueId] ^= 1;
-						dirty = true;
-
-						// Debug: dump current user-selected score so
-						// the post-selection 100-point gate behaviour
-						// is visible while picking. Mirrors the
-						// floppy `FUN_1d40_0c48` (sum of `note[+6]`
-						// across `_NoteSelected != 0`) and the CD
-						// `selectedPoints()` (sum of `note[+2]` u16
-						// across `_NoteSelected != 0`).
-						{
-							int total = 0;
-							uint selectedCount = 0;
-							const uint maxIdx = MIN<uint>(niCount,
-								Mystery::kCluesFoundCap);
-							const bool floppy = isFloppy();
-							for (uint j = 0; j < maxIdx; j++) {
-								if (!_mystery._noteSelected[j])
-									continue;
-								selectedCount++;
-								if (floppy) {
-									total += (int)ni[j * 7 + 6];
-								} else {
-									const int16 pts =
-										(int16)READ_LE_UINT16(
-											ni + j * 4 + 2);
-									total += (int)pts;
-								}
+						if (!_mystery._noteSelected[clueId]) {
+							uint selected = 0;
+							for (uint j = 0; j < found.size(); j++) {
+								if (_mystery._noteSelected[found[j]])
+									selected++;
 							}
-							warning("EEM accuse: clue %u %s "
-									"(selected=%u points=%d)",
-									clueId,
-									_mystery._noteSelected[clueId]
-										? "ON" : "OFF",
-									selectedCount, total);
+							if (selected >= expected)
+								break;
 						}
+						_mystery._noteSelected[clueId] =
+							_mystery._noteSelected[clueId] ? 0 : 1;
+						dirty = true;
 						break;
 					}
 				}
@@ -3730,12 +3746,14 @@ void EEMEngine::doAccuse() {
 
 	// ACCUSE-NOTES SCREEN ??? let the player commit which N clues they
 	// believe solve the case. Mirrors the click-driven selection of
-	// `_DoAccuse @ 1df2:0bdd`'s outer loop. ESC / cancel returns to
-	// the site (matches `_DoAccuse @ 1df2:0c11` writing
-	// `_NextScreen = 3` on ESC).
+	// `_DoAccuse @ 1df2:0bdd`'s outer loop. ESC returns to the site
+	// (matches `_DoAccuse @ 1df2:0c11` writing `_NextScreen = 3`);
+	// pointer navigation can also leave for the PDA, gallery, or map.
 	if (!doAccuseNotes()) {
-		_nextScreen = _lastScreen != kScreenInvalid
-						? (ScreenId)_lastScreen : kScreenSite;
+		if (_nextScreen == kScreenAccuse) {
+			_nextScreen = _lastScreen != kScreenInvalid
+							? (ScreenId)_lastScreen : kScreenSite;
+		}
 		return;
 	}
 
@@ -4630,10 +4648,12 @@ void EEMEngine::doAccuseFloppy() {
 	// matching the CD's `_DoAccuse @ 1df2:0bdd` expected count.
 	// `doAccuseNotes()` already handles this UI for both variants
 	// (note text reading is variant-aware via `noteTextOff`); it
-	// returns true on commit, false on ESC.
+	// returns true on commit, false on ESC / pointer navigation.
 	if (!doAccuseNotes()) {
-		_nextScreen = _lastScreen != kScreenInvalid
-			? (ScreenId)_lastScreen : kScreenSite;
+		if (_nextScreen == kScreenAccuse) {
+			_nextScreen = _lastScreen != kScreenInvalid
+				? (ScreenId)_lastScreen : kScreenSite;
+		}
 		return;
 	}
 


Commit: a0d9c693402c09a30564a36fc98ad223c16591b7
    https://github.com/scummvm/scummvm/commit/a0d9c693402c09a30564a36fc98ad223c16591b7
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:21:01+02:00

Commit Message:
EEM: fixed regression on music handling during enter animation

Changed paths:
    engines/eem/eem.cpp
    engines/eem/eem.h
    engines/eem/site.cpp
    engines/eem/site.h


diff --git a/engines/eem/eem.cpp b/engines/eem/eem.cpp
index 4513d010beb..f7523be122b 100644
--- a/engines/eem/eem.cpp
+++ b/engines/eem/eem.cpp
@@ -958,6 +958,11 @@ void EEMEngine::waitForMusicDone(uint32 maxMs) {
 				stopMusic();
 				return;
 			}
+			if (ev.type == Common::EVENT_KEYDOWN ||
+				ev.type == Common::EVENT_LBUTTONDOWN) {
+				stopMusic();
+				return;
+			}
 		}
 		g_system->updateScreen();
 		g_system->delayMillis(20);
diff --git a/engines/eem/eem.h b/engines/eem/eem.h
index 78f7a5557bb..ae1a6de44d8 100644
--- a/engines/eem/eem.h
+++ b/engines/eem/eem.h
@@ -500,9 +500,9 @@ public:
 	/// music changes as the player travels between sites.
 	void startTravelMusic();
 
-	/// Block until the current MIDI cue finishes, then stop/unload it.
-	/// Mirrors the `_IsMIDIPlaying` spin + `_StopMIDI` cleanup in
-	/// `_DoSiteLoop` after the CD travel cue.
+	/// Block until the current MIDI cue finishes or the player skips it,
+	/// then stop/unload it. Mirrors the `_IsMIDIPlaying` spin +
+	/// `_StopMIDI` cleanup in `_DoSiteLoop` after the CD travel cue.
 	void waitForMusicDone(uint32 maxMs = 60000);
 
 	/// Stop any currently playing MIDI track. Mirrors `_StopMIDI @
diff --git a/engines/eem/site.cpp b/engines/eem/site.cpp
index eeb7d78bd3d..a5051688000 100644
--- a/engines/eem/site.cpp
+++ b/engines/eem/site.cpp
@@ -610,10 +610,14 @@ void SiteScreen::enter(uint siteNum, bool resetPartnerMood) {
 		else
 			renderStaticDrops(siteNum);
 		renderAnimatedDrops(siteNum, g_system->getMillis());
-		enterSiteAnim();
+		const bool skippedArrival = enterSiteAnim();
 		_vm->markSiteArrivalPlayed(siteNum);
-		if (!_vm->isFloppy())
-			_vm->waitForMusicDone();
+		if (!_vm->isFloppy()) {
+			if (skippedArrival)
+				_vm->stopMusic();
+			else
+				_vm->waitForMusicDone();
+		}
 		// Re-paint the BG; the normal snapshot below should contain
 		// only the static layers, while animated NPCs are redrawn per
 		// tick by the frame pump.
@@ -873,7 +877,7 @@ void SiteScreen::run() {
 	}
 }
 
-void SiteScreen::enterSiteAnim() {
+bool SiteScreen::enterSiteAnim() {
 	// Mirrors `_EnterSiteAnim @ 1000:9b21`. Two phases, both partner
 	// dependent:
 	//   Phase 1 ??? skateboard scroll: anim 6 (Jake) / 0xe (Jenny). Sprite
@@ -886,7 +890,7 @@ void SiteScreen::enterSiteAnim() {
 	// motion (a runtime-calibrated speed value); we use a fixed 4 px
 	// per tick which feels close to the DOS pacing.
 	if (!_vm || !_mystery)
-		return;
+		return false;
 	const uint8 partner = _vm->getPartnerIndex();
 	const uint kSkateAni = (partner == 0) ? 6  : 0xe;
 	const uint kKDAni    = (partner == 0) ? 7  : 0xf;
@@ -895,7 +899,7 @@ void SiteScreen::enterSiteAnim() {
 	// Snapshot the current screen so we can restore between frames.
 	Graphics::Surface *screen = g_system->lockScreen();
 	if (!screen)
-		return;
+		return false;
 	Graphics::ManagedSurface bg(320, 200,
 		Graphics::PixelFormat::createFormatCLUT8());
 	bg.simpleBlitFrom(*screen);
@@ -929,7 +933,7 @@ void SiteScreen::enterSiteAnim() {
 			while (g_system->getEventManager()->pollEvent(ev)) {
 				if (ev.type == Common::EVENT_KEYDOWN ||
 					ev.type == Common::EVENT_LBUTTONDOWN) {
-					return; // user-skip ??? bail out of the animation
+					return true; // user-skip
 				}
 			}
 
@@ -979,12 +983,13 @@ void SiteScreen::enterSiteAnim() {
 			while (g_system->getEventManager()->pollEvent(ev)) {
 				if (ev.type == Common::EVENT_KEYDOWN ||
 					ev.type == Common::EVENT_LBUTTONDOWN) {
-					return;
+					return true;
 				}
 			}
 			g_system->delayMillis(80);
 		}
 	}
+	return false;
 }
 
 void SiteScreen::renderStaticDrops(uint siteNum) {
diff --git a/engines/eem/site.h b/engines/eem/site.h
index d6f6557c381..07b34874d07 100644
--- a/engines/eem/site.h
+++ b/engines/eem/site.h
@@ -119,7 +119,8 @@ private:
 	/// _SiteNumber`. Mirrors `_EnterSiteAnim @ 1000:9b21` ??? animation
 	/// 6 (Jake) / 14 (Jenny) skateboards in from the right edge along
 	/// the bottom, then animation 7 / 15 slides KD in from the left.
-	void enterSiteAnim();
+	/// Returns true when the player skipped it with input.
+	bool enterSiteAnim();
 
 	/// Draw the persistent in-site partner sprite (Jake or Jenny
 	/// standing/idling) at the position from `_WaitAnims` @ 29be:021c.


Commit: 33e2ff6fdad0c6c961f09bd403367bd04b75be34
    https://github.com/scummvm/scummvm/commit/33e2ff6fdad0c6c961f09bd403367bd04b75be34
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:21:01+02:00

Commit Message:
EEM: implemented badge handling when a case is over

Changed paths:
    engines/eem/ui.cpp


diff --git a/engines/eem/ui.cpp b/engines/eem/ui.cpp
index 2ff97c65045..30dfb17f7f6 100644
--- a/engines/eem/ui.cpp
+++ b/engines/eem/ui.cpp
@@ -57,6 +57,9 @@ const GallerySlot kGallerySlots[5] = {
 
 constexpr Common::Rect kEndingPrevPageRect(Common::Point(0, 0), 28, 200);
 constexpr Common::Rect kEndingNextPageRect(Common::Point(292, 0), 28, 200);
+constexpr uint16 kFloppyEndingBackgroundPic = 0x8b;
+constexpr uint16 kFirstTryBadgePic = 0x205;
+constexpr Common::Point kFirstTryBadgePos(0x1e, 9);
 
 constexpr Common::Rect kPdaHelpRect(Common::Point(93, 174), 22, 16);
 constexpr Common::Rect kPdaNotebookRect(Common::Point(134, 174), 21, 16);
@@ -633,12 +636,15 @@ void EEMEngine::doNewPlayer() {
 
 int EEMEngine::doShowEnding(uint num, bool firstPage) {
 	// Mirrors `_DisplayEnding @ 1df2:0548` + `_DisplayEndingPage @
-	// 1df2:044c`. File format (verified by reading E0.BIN's bytes):
+	// 1df2:044c` on CD and `FUN_1d40_05b7` + `FUN_1d40_031e` on
+	// floppy. CD ending file format:
 	//   u16 pageCount
 	//   for each page:
 	//     u16 picNum
 	//     u16 x1, y1, x2, y2  (story rect ??? passed to WordWrap)
 	//     char text[]        (null-terminated, ParseString opcodes)
+	// Floppy files prepend a small title header and use a shared
+	// newspaper background (PIC 0x8b) plus per-page overlay pictures.
 	//
 	// The original swaps the font: `_FreeFont(); _LoadFont("tiny.fnt")`
 	// at 1df2:055f-1df2:0563, calls `_GetPalette(0)` (site palette 0),
@@ -679,10 +685,6 @@ int EEMEngine::doShowEnding(uint num, bool firstPage) {
 		return 0;
 	}
 
-	const uint16 pageCount = READ_LE_UINT16(buf.data());
-	if (pageCount == 0)
-		return 0;
-
 	// Mirrors 1df2:0558-1df2:056a ??? `_FreeFont(); _LoadFont(tiny.fnt)`.
 	// The newspaper layout uses TINY.FNT (smaller glyphs) so the body
 	// copy fits in the columns. `_LoadFont(font.fnt)` is restored at
@@ -699,26 +701,69 @@ int EEMEngine::doShowEnding(uint num, bool firstPage) {
 	setSitePalette(0);
 	CursorMan.showMouse(true);
 
-	// Walk page records. Each page header is 10 bytes; text is
-	// null-terminated and follows the header.
+	const bool floppyEnding = isFloppy();
 	uint pageOffsets[8];   // ENDING_RANGE_MAX from `_DisplayEnding`
-	const uint maxPages = MIN<uint>(pageCount,
-									(uint)(sizeof(pageOffsets) / sizeof(uint)));
-	uint cursor = 2;
+	const uint pageOffsetCap =
+		(uint)(sizeof(pageOffsets) / sizeof(pageOffsets[0]));
 	uint validPages = 0;
-	for (uint p = 0; p < maxPages; p++) {
-		if (cursor + 10 >= size)
-			break;
-		pageOffsets[validPages++] = cursor;
-		// Skip the 10-byte header and find the null terminator.
-		cursor += 10;
-		while (cursor < size && buf[cursor] != 0)
-			cursor++;
-		cursor++;  // past the null
+
+	if (floppyEnding) {
+		// Floppy `E<num>.BIN` starts with:
+		//   u8 type, 3 bytes of title metadata, char title[], u8 pageCount
+		// followed by pages:
+		//   u8 overlayCount, N * { u16 picNum, u16 x, u8 y },
+		//   u16 x1, y1, x2, y2, char text[].
+		uint titleEnd = 4;
+		while (titleEnd < size && buf[titleEnd] != 0)
+			titleEnd++;
+		if (titleEnd + 2 >= size)
+			return 0;
+		const uint pageCount = buf[titleEnd + 1];
+		uint cursor = titleEnd + 2;
+		const uint maxPages = MIN<uint>(pageCount, pageOffsetCap);
+		for (uint p = 0; p < maxPages; p++) {
+			if (cursor >= size)
+				break;
+			const uint pageStart = cursor;
+			const uint overlayCount = buf[cursor++];
+			const uint overlaysSize = overlayCount * 5;
+			if (cursor + overlaysSize + 8 >= size)
+				break;
+			cursor += overlaysSize + 8;
+			while (cursor < size && buf[cursor] != 0)
+				cursor++;
+			if (cursor >= size)
+				break;
+			pageOffsets[validPages++] = pageStart;
+			cursor++;  // past the null
+		}
+	} else {
+		const uint16 pageCount = READ_LE_UINT16(buf.data());
+		if (pageCount == 0)
+			return 0;
+		const uint maxPages = MIN<uint>(pageCount, pageOffsetCap);
+		uint cursor = 2;
+		for (uint p = 0; p < maxPages; p++) {
+			if (cursor + 10 >= size)
+				break;
+			pageOffsets[validPages++] = cursor;
+			// Skip the 10-byte header and find the null terminator.
+			cursor += 10;
+			while (cursor < size && buf[cursor] != 0)
+				cursor++;
+			cursor++;  // past the null
+		}
 	}
 	if (validPages == 0)
 		return 0;
 
+	const bool showFirstTryBadge =
+		num < sizeof(_mysteriesSolved) && _mysteriesSolved[num] == 2;
+	Picture firstTryBadge;
+	const bool haveFirstTryBadge =
+		showFirstTryBadge &&
+		_picsArchive.getPicture(kFirstTryBadgePic, firstTryBadge);
+
 	uint pageIdx = firstPage ? 0 : (validPages - 1);
 	int direction = 0;     // -1 / 0 / +1, see header doc.
 	bool exitLoop = false;
@@ -731,25 +776,68 @@ int EEMEngine::doShowEnding(uint num, bool firstPage) {
 	while (!shouldQuit() && !exitLoop) {
 		if (dirty) {
 			const uint off = pageOffsets[pageIdx];
-			if (off + 10 >= size)
-				break;
-			const uint16 picNum = READ_LE_UINT16(buf.data() + off);
-			const uint16 x1     = READ_LE_UINT16(buf.data() + off + 2);
-			const uint16 y1     = READ_LE_UINT16(buf.data() + off + 4);
-			const uint16 x2     = READ_LE_UINT16(buf.data() + off + 6);
-			(void)READ_LE_UINT16(buf.data() + off + 8);  // y2 (unused ??? WordWrap2 takes width only)
-
-			Picture bg;
+			uint16 x1 = 0;
+			uint16 y1 = 0;
+			uint16 x2 = 0;
+			const char *raw = nullptr;
 			Graphics::ManagedSurface scratch(320, 200,
 				Graphics::PixelFormat::createFormatCLUT8());
 			scratch.clear();
-			if (_picsArchive.getPicture(picNum, bg))
-				scratch.simpleBlitFrom(bg.surface);
+
+			if (floppyEnding) {
+				Picture bg;
+				if (_picsArchive.getPicture(kFloppyEndingBackgroundPic, bg))
+					scratch.simpleBlitFrom(bg.surface);
+
+				uint cursor = off;
+				if (cursor >= size)
+					break;
+				const uint overlayCount = buf[cursor++];
+				for (uint i = 0; i < overlayCount; i++) {
+					if (cursor + 5 > size)
+						break;
+					const uint16 picNum = READ_LE_UINT16(buf.data() + cursor);
+					const uint16 px = READ_LE_UINT16(buf.data() + cursor + 2);
+					const byte py = buf[cursor + 4];
+					Picture overlay;
+					if (_picsArchive.getPicture(picNum, overlay)) {
+						const byte transp = (byte)(overlay.flags >> 8);
+						scratch.transBlitFrom(overlay.surface,
+											  Common::Point(px, py), transp);
+					}
+					cursor += 5;
+				}
+				if (cursor + 8 >= size)
+					break;
+				x1 = READ_LE_UINT16(buf.data() + cursor);
+				y1 = READ_LE_UINT16(buf.data() + cursor + 2);
+				x2 = READ_LE_UINT16(buf.data() + cursor + 4);
+				(void)READ_LE_UINT16(buf.data() + cursor + 6);
+				raw = (const char *)buf.data() + cursor + 8;
+			} else {
+				if (off + 10 >= size)
+					break;
+				const uint16 picNum = READ_LE_UINT16(buf.data() + off);
+				x1 = READ_LE_UINT16(buf.data() + off + 2);
+				y1 = READ_LE_UINT16(buf.data() + off + 4);
+				x2 = READ_LE_UINT16(buf.data() + off + 6);
+				(void)READ_LE_UINT16(buf.data() + off + 8);  // y2 (unused ??? WordWrap2 takes width only)
+
+				Picture bg;
+				if (_picsArchive.getPicture(picNum, bg))
+					scratch.simpleBlitFrom(bg.surface);
+				raw = (const char *)buf.data() + off + 10;
+			}
+
+			if (pageIdx == 0 && haveFirstTryBadge) {
+				const byte transp = (byte)(firstTryBadge.flags >> 8);
+				scratch.transBlitFrom(firstTryBadge.surface,
+									  kFirstTryBadgePos, transp);
+			}
 
 			// Story text. The bytes are a null-terminated string with
 			// `_ParseString` placeholders (0x80 = player name, 0x82
 			// = partner first name, etc.).
-			const char *raw = (const char *)buf.data() + off + 10;
 			const Common::String text = parseString(raw, _playerName, _partner);
 
 			// Use TINY.FNT (`_LoadFont(@29be:10a5)` at 1df2:055f-0563)
@@ -4991,6 +5079,13 @@ void EEMEngine::doAccuseFloppy() {
 			}
 		}
 
+		// `_DisplayCorrect_Floppy @ 1d40:0991` calls
+		// `FUN_1ee2_06ac(mystery)`, whose body is just
+		// `FUN_1d40_05b7(mystery, 1)` plus font cleanup. That is the
+		// floppy ending/scrapbook viewer, and it receives the
+		// first-try badge state from the solved table entry above.
+		doShowEnding(mn);
+
 		// Persist progress before clearing the in-progress mystery.
 		// `_DisplayCorrect_Floppy @ 1d40:0982` calls
 		// `_SavePlayerRecord` then `FUN_22dc_0dbd` (which deletes the


Commit: 1423b3442bf1f4ac52bd25aa65dc419ac14ea055
    https://github.com/scummvm/scummvm/commit/1423b3442bf1f4ac52bd25aa65dc419ac14ea055
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:21:01+02:00

Commit Message:
EEM: shiny map markers

Changed paths:
    engines/eem/site.h
    engines/eem/ui.cpp


diff --git a/engines/eem/site.h b/engines/eem/site.h
index 07b34874d07..110c560df05 100644
--- a/engines/eem/site.h
+++ b/engines/eem/site.h
@@ -76,6 +76,11 @@ uint bigMapDetailPartnerFrameAtTick(uint numFrames, uint32 elapsedMs);
 void blitAnimFrameAnchored(Graphics::Surface *screen, const Picture &p,
 						   int anchorX, int anchorY);
 
+/// Rotate one VGA palette range by one slot. Mirrors `_ColorCycle`
+/// and is used by site color cycles, hotspot marching ants, and the
+/// BigMap marker shine.
+void cyclePaletteRange(uint8 start, uint8 end);
+
 /// One hotspot (search rectangle) within a site, 14 bytes on disk.
 struct Hotspot {
 	int16  x1, y1, x2, y2;     ///< rectangle in screen coordinates
diff --git a/engines/eem/ui.cpp b/engines/eem/ui.cpp
index 30dfb17f7f6..a5612b345a0 100644
--- a/engines/eem/ui.cpp
+++ b/engines/eem/ui.cpp
@@ -2885,6 +2885,8 @@ void EEMEngine::doBigMap() {
 		if (now - mapLastTick >= 100) {
 			mapLastTick = now;
 			drawBigMapOverview(now - mapStartTick);
+			cyclePaletteRange(0xf7, 0xfa);
+			cyclePaletteRange(0xfb, 0xfe);
 		}
 		g_system->updateScreen();
 		g_system->delayMillis(10);


Commit: 9e532d8f9691ea295ab990fcaa42adb912630cea
    https://github.com/scummvm/scummvm/commit/9e532d8f9691ea295ab990fcaa42adb912630cea
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:21:02+02:00

Commit Message:
EEM: allow to come back from zoomed map to the overview

Changed paths:
    engines/eem/ui.cpp


diff --git a/engines/eem/ui.cpp b/engines/eem/ui.cpp
index a5612b345a0..7ed2cac176e 100644
--- a/engines/eem/ui.cpp
+++ b/engines/eem/ui.cpp
@@ -2806,286 +2806,313 @@ void EEMEngine::doBigMap() {
 
 	CursorMan.showMouse(true);
 
-	// `_GetPalette(0x24)` per `_DoBigMap @ 20fe:09e7`.
-	setSitePalette(0x24);
-
-	const Common::Rect kSetupRect(0xc7, 0x12, 0xc7 + 0x32, 0x12 + 0xa); // approx; original from globals
-	(void)kSetupRect; // not yet wired into our overlay
-
-	// ------------------------------------------------------------------
-	// STAGE 1 ??? Overview: PIC 0x42 + clickable site icons.
-	// ------------------------------------------------------------------
-
-	// Anchor for the partner-sprite timeline. `_DoBigMap`'s
-	// `_NewAnimation` call seeds the slot's frame index to 0xffff so
-	// the first `_UpdateAnimations` tick starts at script[0]; we mirror
-	// that by passing elapsed-since-open (zero on the first paint) into
-	// `bigMapPartnerFrameAtTick`, which plays the unfold once and then
-	// loops the wait sequence.
-	const uint32 mapStartTick = g_system->getMillis();
-	drawBigMapOverview(0);
-	uint32 mapLastTick = mapStartTick;
-
-	// Static rectangles read directly from the binary at the labelled
-	// addresses (CD `29be:0x1596` / floppy `2608:0x13fe..0x143e`).
-	// Format is {x1, y1, x2, y2}. The floppy click table at
-	// `2608:1436` (verified at `_BigMapInteractionLoop_Floppy @
-	// 1fed:0a3a`) puts the setup button at (251, 3, 315, 42) ??? 1 px
-	// up and 1 px left of the CD's (252, 4, 315, 42). The floppy
-	// PIC 0x42 BG paints the visible button border at the same
-	// pixels, so use the variant-specific rect to match the
-	// hit-test region the original uses for that variant.
-	const Common::Rect kBigMapWindow   (  0,   0, 247, 192); // 29be:1596
-	const Common::Rect kSetupBtnRect   = isFloppy()
-		? Common::Rect(251, 3, 315, 42)   // 2608:1436
-		: Common::Rect(252, 4, 315, 42);  // 29be:15ce
-
-	bool wantZoom = false;
-	int zoomX = 0;
-	int zoomY = 0;
 	while (!shouldQuit()) {
-		Common::Event ev;
-		while (g_system->getEventManager()->pollEvent(ev)) {
-			if (ev.type == Common::EVENT_QUIT ||
-				ev.type == Common::EVENT_RETURN_TO_LAUNCHER)
-				return;
-			if (ev.type == Common::EVENT_KEYDOWN &&
-				ev.kbd.keycode == Common::KEYCODE_ESCAPE)
-				return;
-			if (ev.type == Common::EVENT_LBUTTONDOWN) {
-				// SetupButtonRect ??? `_NextScreen = 6` (the original's
-				// settings screen, mirrors `_DoBigMap @ 20fe:0c33`
-				// where it pushes `_PressButton` then writes
-				// `_NextScreen = 6`). Now wired to the actual
-				// `doSetup` handler instead of dropping the player
-				// out to the launcher.
-				if (kSetupBtnRect.contains(ev.mouse.x, ev.mouse.y)) {
-					_nextScreen = kScreenSetup;
+		setInteractiveMouseCursor(false);
+		setSitePalette(0x24); // `_GetPalette(0x24)` per `_DoBigMap @ 20fe:09e7`.
+
+		// ------------------------------------------------------------------
+		// STAGE 1 ??? Overview: PIC 0x42 + clickable site icons.
+		// ------------------------------------------------------------------
+
+		// Anchor for the partner-sprite timeline. `_DoBigMap`'s
+		// `_NewAnimation` call seeds the slot's frame index to 0xffff so
+		// the first `_UpdateAnimations` tick starts at script[0]; we mirror
+		// that by passing elapsed-since-open (zero on the first paint) into
+		// `bigMapPartnerFrameAtTick`, which plays the unfold once and then
+		// loops the wait sequence.
+		const uint32 mapStartTick = g_system->getMillis();
+		drawBigMapOverview(0);
+		uint32 mapLastTick = mapStartTick;
+
+		// Static rectangles read directly from the binary at the labelled
+		// addresses (CD `29be:0x1596` / floppy `2608:0x13fe..0x143e`).
+		// Format is {x1, y1, x2, y2}. The floppy click table at
+		// `2608:1436` (verified at `_BigMapInteractionLoop_Floppy @
+		// 1fed:0a3a`) puts the setup button at (251, 3, 315, 42) ??? 1 px
+		// up and 1 px left of the CD's (252, 4, 315, 42). The floppy
+		// PIC 0x42 BG paints the visible button border at the same
+		// pixels, so use the variant-specific rect to match the
+		// hit-test region the original uses for that variant.
+		const Common::Rect kBigMapWindow(0, 0, 247, 192); // 29be:1596
+		const Common::Rect kSetupBtnRect = isFloppy()
+			? Common::Rect(251, 3, 315, 42)   // 2608:1436
+			: Common::Rect(252, 4, 315, 42);  // 29be:15ce
+
+		bool wantZoom = false;
+		int zoomX = 0;
+		int zoomY = 0;
+		while (!shouldQuit()) {
+			Common::Event ev;
+			while (g_system->getEventManager()->pollEvent(ev)) {
+				if (ev.type == Common::EVENT_QUIT ||
+					ev.type == Common::EVENT_RETURN_TO_LAUNCHER)
 					return;
-				}
-				// Click in the BigMapWindow ??? zoom. Original formula:
-				//   sx = mouseX*2 - 0x74; sy = mouseY*2 - 0x55
-				if (kBigMapWindow.contains(ev.mouse.x, ev.mouse.y)) {
-					int sx = ev.mouse.x * 2;
-					int sy = ev.mouse.y * 2;
-					sx = (sx < 0x75) ? 0 : sx - 0x74;
-					sy = (sy < 0x56) ? 0 : sy - 0x55;
-					zoomX = sx;
-					zoomY = sy;
-					wantZoom = true;
-					break;
+				if (ev.type == Common::EVENT_KEYDOWN &&
+					ev.kbd.keycode == Common::KEYCODE_ESCAPE)
+					return;
+				if (ev.type == Common::EVENT_LBUTTONDOWN) {
+					// SetupButtonRect ??? `_NextScreen = 6` (the original's
+					// settings screen, mirrors `_DoBigMap @ 20fe:0c33`
+					// where it pushes `_PressButton` then writes
+					// `_NextScreen = 6`). Now wired to the actual
+					// `doSetup` handler instead of dropping the player
+					// out to the launcher.
+					if (kSetupBtnRect.contains(ev.mouse.x, ev.mouse.y)) {
+						_nextScreen = kScreenSetup;
+						return;
+					}
+					// Click in the BigMapWindow ??? zoom. Original formula:
+					//   sx = mouseX*2 - 0x74; sy = mouseY*2 - 0x55
+					if (kBigMapWindow.contains(ev.mouse.x, ev.mouse.y)) {
+						int sx = ev.mouse.x * 2;
+						int sy = ev.mouse.y * 2;
+						sx = (sx < 0x75) ? 0 : sx - 0x74;
+						sy = (sy < 0x56) ? 0 : sy - 0x55;
+						zoomX = sx;
+						zoomY = sy;
+						wantZoom = true;
+						break;
+					}
 				}
 			}
-		}
-		if (wantZoom)
-			break;
-		// Cycle the partner-sprite frame every 100 ms (matching the
-		// original's `_CheckFrameRate` cadence inside `_DoBigMap`).
-		const uint32 now = g_system->getMillis();
-		if (now - mapLastTick >= 100) {
-			mapLastTick = now;
-			drawBigMapOverview(now - mapStartTick);
-			cyclePaletteRange(0xf7, 0xfa);
-			cyclePaletteRange(0xfb, 0xfe);
-		}
-		g_system->updateScreen();
-		g_system->delayMillis(10);
-	}
-
-	if (!wantZoom)
-		return;
-
-	// ------------------------------------------------------------------
-	// STAGE 2 ??? Detail zoom: PIC 0x43 frame + scrollable BIGMAP.PIC
-	// viewport at (2, 2), 0xe9 ?? 0xab. Click on a stamped icon ??? travel.
-	// ------------------------------------------------------------------
+			if (wantZoom)
+				break;
 
-	Common::File f;
-	if (!f.open(Common::Path("BIGMAP.PIC"))) {
-		warning("doBigMap: BIGMAP.PIC missing for detail view");
-		return;
-	}
-	const uint16 mapH = f.readUint16LE();
-	const uint16 mapW = f.readUint16LE();
-	if (mapW == 0 || mapH == 0)
-		return;
-	Common::Array<byte> mapPixels((uint32)mapW * mapH);
-	if (f.read(mapPixels.data(), mapPixels.size()) != mapPixels.size()) {
-		warning("doBigMap: short read on BIGMAP.PIC for detail view");
-		return;
-	}
+			// Cycle the partner-sprite frame every 100 ms (matching the
+			// original's `_CheckFrameRate` cadence inside `_DoBigMap`).
+			const uint32 now = g_system->getMillis();
+			if (now - mapLastTick >= 100) {
+				mapLastTick = now;
+				drawBigMapOverview(now - mapStartTick);
+				cyclePaletteRange(0xf7, 0xfa);
+				cyclePaletteRange(0xfb, 0xfe);
+			}
+			g_system->updateScreen();
+			g_system->delayMillis(10);
+		}
 
-	const int kMapWinW = 0xe9; // 233
-	const int kMapWinH = 0xab; // 171
-	const int kMapWinX = 2;
-	const int kMapWinY = 2;
+		if (!wantZoom)
+			return;
 
-	int scrollX = MAX<int>(0, MIN<int>(mapW - kMapWinW, zoomX));
-	int scrollY = MAX<int>(0, MIN<int>(mapH - kMapWinH, zoomY));
+		// ------------------------------------------------------------------
+		// STAGE 2 ??? Detail zoom: PIC 0x43 frame + scrollable BIGMAP.PIC
+		// viewport at (2, 2), 0xe9 ?? 0xab. Click on a stamped icon ??? travel.
+		// ------------------------------------------------------------------
 
-	// Anchor the detail-screen partner timeline (mirrors `_DoMapScreen`'s
-	// `_NewAnimation` seeding the slot's frame index to 0xffff). The
-	// unfold (script 0x13) plays once, then `_SmallMapWaitSeq` loops.
-	const uint32 detailStartTick = g_system->getMillis();
-	drawBigMapDetail(scrollX, scrollY, mapPixels, mapW, mapH, 0);
-	uint32 detailLastTick = detailStartTick;
+		Common::File f;
+		if (!f.open(Common::Path("BIGMAP.PIC"))) {
+			warning("doBigMap: BIGMAP.PIC missing for detail view");
+			return;
+		}
+		const uint16 mapH = f.readUint16LE();
+		const uint16 mapW = f.readUint16LE();
+		if (mapW == 0 || mapH == 0)
+			return;
+		Common::Array<byte> mapPixels((uint32)mapW * mapH);
+		if (f.read(mapPixels.data(), mapPixels.size()) != mapPixels.size()) {
+			warning("doBigMap: short read on BIGMAP.PIC for detail view");
+			return;
+		}
 
-	while (!shouldQuit()) {
-		Common::Event ev;
-		bool dirty = false;
-		while (g_system->getEventManager()->pollEvent(ev)) {
-			if (ev.type == Common::EVENT_QUIT ||
-				ev.type == Common::EVENT_RETURN_TO_LAUNCHER)
-				return;
-			if (ev.type == Common::EVENT_KEYDOWN) {
-				if (ev.kbd.keycode == Common::KEYCODE_ESCAPE)
-					return;  // exit detail back to caller (site loop / engine)
-				const int kStep = 16;
-				if (ev.kbd.keycode == Common::KEYCODE_LEFT) {
-					scrollX = MAX<int>(0, scrollX - kStep);
-					dirty = true;
-				} else if (ev.kbd.keycode == Common::KEYCODE_RIGHT) {
-					scrollX = MIN<int>(MAX<int>(0, mapW - kMapWinW),
-						scrollX + kStep);
-					dirty = true;
-				} else if (ev.kbd.keycode == Common::KEYCODE_UP) {
-					scrollY = MAX<int>(0, scrollY - kStep);
-					dirty = true;
-				} else if (ev.kbd.keycode == Common::KEYCODE_DOWN) {
-					scrollY = MIN<int>(MAX<int>(0, mapH - kMapWinH),
-						scrollY + kStep);
-					dirty = true;
-				}
-			}
-			if (ev.type == Common::EVENT_LBUTTONDOWN) {
-				// Scroll arrows + slider rects live in `SmallMapButtons`
-				// at 29be:0x159e (six 8-byte rects in order: Y-up, Y-down,
-				// X-left, X-right, right-panel, top-right) plus the
-				// dedicated `XSliderRect @ 29be:15d6` and
-				// `YSliderRect @ 29be:15de`. Format {x1,y1,x2,y2}.
-				const Common::Rect kArrowYUp   (237,   2, 247,  11);
-				const Common::Rect kArrowYDown (237, 163, 247, 172);
-				const Common::Rect kArrowXLeft (  2, 175,  12, 185);
-				const Common::Rect kArrowXRight(224, 175, 234, 185);
-				const Common::Rect kXSlider    ( 15, 175, 221, 185);
-				const Common::Rect kYSlider    (237,  14, 247, 160);
-				const Common::Rect kSetupBtn = isFloppy()
-					? Common::Rect(251, 3, 315, 42)   // 2608:1436
-					: Common::Rect(252, 4, 315, 42);  // 29be:15ce
-
-				const int kArrowStep = 16;
-				const int kSliderRange = mapW - kMapWinW;
-				const int kSliderRangeY = mapH - kMapWinH;
-
-				if (kSetupBtn.contains(ev.mouse.x, ev.mouse.y)) {
-					// `_DoMapScreen @ 20fe:1560` writes `_NextScreen
-					// = 6` (= kScreenSetup) and `INC [BP-8]` to bail
-					// out of the detail loop ??? verified via the byte
-					// search for `c7 06 16 79 06 00`, which finds the
-					// imm at exactly this site and `_DoBigMap @
-					// 20fe:0c33`. Same `SetupButtonRect @ 29be:15ce`
-					// rect used by both the overview and the detail
-					// (no per-screen rect duplication in the binary).
-					// The detail/zoom state is lost on return because
-					// the screen driver re-enters BigMap at stage 1 ???
-					// this matches the original behaviour.
-					_nextScreen = kScreenSetup;
+		const int kMapWinW = 0xe9; // 233
+		const int kMapWinH = 0xab; // 171
+		const int kMapWinX = 2;
+		const int kMapWinY = 2;
+
+		int scrollX = MAX<int>(0, MIN<int>(mapW - kMapWinW, zoomX));
+		int scrollY = MAX<int>(0, MIN<int>(mapH - kMapWinH, zoomY));
+
+		// Anchor the detail-screen partner timeline (mirrors `_DoMapScreen`'s
+		// `_NewAnimation` seeding the slot's frame index to 0xffff). The
+		// unfold (script 0x13) plays once, then `_SmallMapWaitSeq` loops.
+		const uint32 detailStartTick = g_system->getMillis();
+		drawBigMapDetail(scrollX, scrollY, mapPixels, mapW, mapH, 0);
+		uint32 detailLastTick = detailStartTick;
+		bool returnToOverview = false;
+
+		// `SmallMapButtons[4]` is the large right-side panel below setup:
+		// CD/floppy both store `(252, 43, 320, 200)`. Its handler at
+		// CD `20fe:156c` kills the zoom animation, sets `_NextScreen = 1`
+		// and calls `_DoBigMap` again, so mouse players can return to the
+		// overview without leaving the map screen.
+		const Common::Rect kBigMapReturnRect(252, 43, 320, 200);
+		const Common::Rect kArrowYUp(237, 2, 247, 11);
+		const Common::Rect kArrowYDown(237, 163, 247, 172);
+		const Common::Rect kArrowXLeft(2, 175, 12, 185);
+		const Common::Rect kArrowXRight(224, 175, 234, 185);
+		const Common::Rect kXSlider(15, 175, 221, 185);
+		const Common::Rect kYSlider(237, 14, 247, 160);
+		const Common::Rect kDetailSetupBtn = isFloppy()
+			? Common::Rect(251, 3, 315, 42)   // 2608:1436
+			: Common::Rect(252, 4, 315, 42);  // 29be:15ce
+		const int kArrowStep = 16;
+		const int kSliderRange = mapW - kMapWinW;
+		const int kSliderRangeY = mapH - kMapWinH;
+		const Common::Point detailMouse =
+			g_system->getEventManager()->getMousePos();
+		setInteractiveMouseCursor(
+			kBigMapReturnRect.contains(detailMouse.x, detailMouse.y) ||
+			kDetailSetupBtn.contains(detailMouse.x, detailMouse.y));
+
+		while (!shouldQuit() && !returnToOverview) {
+			Common::Event ev;
+			bool dirty = false;
+			while (g_system->getEventManager()->pollEvent(ev)) {
+				if (ev.type == Common::EVENT_QUIT ||
+					ev.type == Common::EVENT_RETURN_TO_LAUNCHER) {
+					setInteractiveMouseCursor(false);
 					return;
 				}
-				if (kArrowYUp.contains(ev.mouse.x, ev.mouse.y)) {
-					scrollY = MAX<int>(0, scrollY - kArrowStep);
-					dirty = true;
-				} else if (kArrowYDown.contains(ev.mouse.x, ev.mouse.y)) {
-					scrollY = MIN<int>(MAX<int>(0, kSliderRangeY),
-						scrollY + kArrowStep);
-					dirty = true;
-				} else if (kArrowXLeft.contains(ev.mouse.x, ev.mouse.y)) {
-					scrollX = MAX<int>(0, scrollX - kArrowStep);
-					dirty = true;
-				} else if (kArrowXRight.contains(ev.mouse.x, ev.mouse.y)) {
-					scrollX = MIN<int>(MAX<int>(0, kSliderRange),
-						scrollX + kArrowStep);
-					dirty = true;
-				} else if (kXSlider.contains(ev.mouse.x, ev.mouse.y)) {
-					// Click on X slider track ??? jump scrollX so the
-					// click position maps proportionally into the map.
-					if (kSliderRange > 0) {
-						const int t = ev.mouse.x - kXSlider.left;
-						const int tw = kXSlider.width();
-						scrollX = MAX<int>(0, MIN<int>(kSliderRange,
-							t * kSliderRange / MAX<int>(1, tw)));
-						dirty = true;
+				if (ev.type == Common::EVENT_KEYDOWN) {
+					if (ev.kbd.keycode == Common::KEYCODE_ESCAPE) {
+						setInteractiveMouseCursor(false);
+						return;  // exit detail back to caller (site loop / engine)
 					}
-				} else if (kYSlider.contains(ev.mouse.x, ev.mouse.y)) {
-					if (kSliderRangeY > 0) {
-						const int t = ev.mouse.y - kYSlider.top;
-						const int th = kYSlider.height();
-						scrollY = MAX<int>(0, MIN<int>(kSliderRangeY,
-							t * kSliderRangeY / MAX<int>(1, th)));
+					const int kStep = 16;
+					if (ev.kbd.keycode == Common::KEYCODE_LEFT) {
+						scrollX = MAX<int>(0, scrollX - kStep);
+						dirty = true;
+					} else if (ev.kbd.keycode == Common::KEYCODE_RIGHT) {
+						scrollX = MIN<int>(MAX<int>(0, mapW - kMapWinW),
+							scrollX + kStep);
+						dirty = true;
+					} else if (ev.kbd.keycode == Common::KEYCODE_UP) {
+						scrollY = MAX<int>(0, scrollY - kStep);
+						dirty = true;
+					} else if (ev.kbd.keycode == Common::KEYCODE_DOWN) {
+						scrollY = MIN<int>(MAX<int>(0, mapH - kMapWinH),
+							scrollY + kStep);
 						dirty = true;
 					}
-				} else if (ev.mouse.x >= kMapWinX &&
-						   ev.mouse.x < kMapWinX + kMapWinW &&
-						   ev.mouse.y >= kMapWinY &&
-						   ev.mouse.y < kMapWinY + kMapWinH) {
-					// Hit-test the per-site button at its actual bbox
-					// (`_StampButtons` records the rect at SmallMap +8/+0xa
-					// with the button PIC's width/height).
-					const bool fmap = _mystery.isLoaded() && isFloppy();
-					for (uint i = 0; i < _mystery.numSites(); i++) {
-						if (!_mystery._onSites[i] &&
-							i != _mystery._siteNumber)
-							continue;
-						const byte *entry = _mystery.mapEntry(i);
-						if (!entry)
-							continue;
-						uint16 mx;
-						uint16 my;
-						uint16 buttonId;
-						if (fmap) {
-							// Floppy detail view: click rect on
-							// BIGMAP.PIC at (+0, +2), labelled BUTTON.DBD
-							// entry ID at entry+4 (per
-							// `FUN_1fed_0c3e @ 1fed:0c3e`).
-							mx = READ_LE_UINT16(entry + 0x0);
-							my = READ_LE_UINT16(entry + 0x2);
-							buttonId = (uint16)entry[0x4];
-						} else {
-							buttonId = READ_LE_UINT16(entry + 0x0);
-							mx       = READ_LE_UINT16(entry + 0x8);
-							my       = READ_LE_UINT16(entry + 0xa);
+				}
+				if (ev.type == Common::EVENT_MOUSEMOVE)
+					setInteractiveMouseCursor(
+						kBigMapReturnRect.contains(ev.mouse.x, ev.mouse.y) ||
+						kDetailSetupBtn.contains(ev.mouse.x, ev.mouse.y));
+				if (ev.type == Common::EVENT_LBUTTONDOWN) {
+					setInteractiveMouseCursor(
+						kBigMapReturnRect.contains(ev.mouse.x, ev.mouse.y) ||
+						kDetailSetupBtn.contains(ev.mouse.x, ev.mouse.y));
+					if (kDetailSetupBtn.contains(ev.mouse.x, ev.mouse.y)) {
+						// `_DoMapScreen @ 20fe:1560` writes `_NextScreen
+						// = 6` (= kScreenSetup) and `INC [BP-8]` to bail
+						// out of the detail loop ??? verified via the byte
+						// search for `c7 06 16 79 06 00`, which finds the
+						// imm at exactly this site and `_DoBigMap @
+						// 20fe:0c33`. Same `SetupButtonRect @ 29be:15ce`
+						// rect used by both the overview and the detail
+						// (no per-screen rect duplication in the binary).
+						// The detail/zoom state is lost on return because
+						// the screen driver re-enters BigMap at stage 1 ???
+						// this matches the original behaviour.
+						_nextScreen = kScreenSetup;
+						setInteractiveMouseCursor(false);
+						return;
+					}
+					if (kBigMapReturnRect.contains(ev.mouse.x, ev.mouse.y)) {
+						returnToOverview = true;
+						break;
+					} else if (kArrowYUp.contains(ev.mouse.x, ev.mouse.y)) {
+						scrollY = MAX<int>(0, scrollY - kArrowStep);
+						dirty = true;
+					} else if (kArrowYDown.contains(ev.mouse.x, ev.mouse.y)) {
+						scrollY = MIN<int>(MAX<int>(0, kSliderRangeY),
+							scrollY + kArrowStep);
+						dirty = true;
+					} else if (kArrowXLeft.contains(ev.mouse.x, ev.mouse.y)) {
+						scrollX = MAX<int>(0, scrollX - kArrowStep);
+						dirty = true;
+					} else if (kArrowXRight.contains(ev.mouse.x, ev.mouse.y)) {
+						scrollX = MIN<int>(MAX<int>(0, kSliderRange),
+							scrollX + kArrowStep);
+						dirty = true;
+					} else if (kXSlider.contains(ev.mouse.x, ev.mouse.y)) {
+						// Click on X slider track ??? jump scrollX so the
+						// click position maps proportionally into the map.
+						if (kSliderRange > 0) {
+							const int t = ev.mouse.x - kXSlider.left;
+							const int tw = kXSlider.width();
+							scrollX = MAX<int>(0, MIN<int>(kSliderRange,
+								t * kSliderRange / MAX<int>(1, tw)));
+							dirty = true;
 						}
-						Picture button;
-						int bw = 16;
-						int bh = 16;
-						if (_buttonArchive.loadEntry(buttonId, button)) {
-							bw = button.surface.w;
-							bh = button.surface.h;
+					} else if (kYSlider.contains(ev.mouse.x, ev.mouse.y)) {
+						if (kSliderRangeY > 0) {
+							const int t = ev.mouse.y - kYSlider.top;
+							const int th = kYSlider.height();
+							scrollY = MAX<int>(0, MIN<int>(kSliderRangeY,
+								t * kSliderRangeY / MAX<int>(1, th)));
+							dirty = true;
 						}
-						const int sx = (int)mx - scrollX + kMapWinX;
-						const int sy = (int)my - scrollY + kMapWinY;
-						if (ev.mouse.x >= sx && ev.mouse.x < sx + bw &&
-							ev.mouse.y >= sy && ev.mouse.y < sy + bh) {
-							_mystery._lastSite = _mystery._siteNumber;
-							_mystery._siteNumber = (uint16)i;
-							return;
+					} else if (ev.mouse.x >= kMapWinX &&
+							   ev.mouse.x < kMapWinX + kMapWinW &&
+							   ev.mouse.y >= kMapWinY &&
+							   ev.mouse.y < kMapWinY + kMapWinH) {
+						// Hit-test the per-site button at its actual bbox
+						// (`_StampButtons` records the rect at SmallMap +8/+0xa
+						// with the button PIC's width/height).
+						const bool fmap = _mystery.isLoaded() && isFloppy();
+						for (uint i = 0; i < _mystery.numSites(); i++) {
+							if (!_mystery._onSites[i] &&
+								i != _mystery._siteNumber)
+								continue;
+							const byte *entry = _mystery.mapEntry(i);
+							if (!entry)
+								continue;
+							uint16 mx;
+							uint16 my;
+							uint16 buttonId;
+							if (fmap) {
+								// Floppy detail view: click rect on
+								// BIGMAP.PIC at (+0, +2), labelled BUTTON.DBD
+								// entry ID at entry+4 (per
+								// `FUN_1fed_0c3e @ 1fed:0c3e`).
+								mx = READ_LE_UINT16(entry + 0x0);
+								my = READ_LE_UINT16(entry + 0x2);
+								buttonId = (uint16)entry[0x4];
+							} else {
+								buttonId = READ_LE_UINT16(entry + 0x0);
+								mx       = READ_LE_UINT16(entry + 0x8);
+								my       = READ_LE_UINT16(entry + 0xa);
+							}
+							Picture button;
+							int bw = 16;
+							int bh = 16;
+							if (_buttonArchive.loadEntry(buttonId, button)) {
+								bw = button.surface.w;
+								bh = button.surface.h;
+							}
+							const int sx = (int)mx - scrollX + kMapWinX;
+							const int sy = (int)my - scrollY + kMapWinY;
+							if (ev.mouse.x >= sx && ev.mouse.x < sx + bw &&
+								ev.mouse.y >= sy && ev.mouse.y < sy + bh) {
+								_mystery._lastSite = _mystery._siteNumber;
+								_mystery._siteNumber = (uint16)i;
+								setInteractiveMouseCursor(false);
+								return;
+							}
 						}
 					}
 				}
 			}
+			if (returnToOverview)
+				break;
+
+			// Cycle the partner sprite at 100 ms ticks (same cadence as
+			// `_DoMapScreen`'s `_CheckFrameRate` + `_UpdateAnimations` loop).
+			const uint32 now = g_system->getMillis();
+			if (now - detailLastTick >= 100) {
+				detailLastTick = now;
+				dirty = true;
+			}
+			if (dirty)
+				drawBigMapDetail(scrollX, scrollY, mapPixels, mapW, mapH,
+					now - detailStartTick);
+			g_system->updateScreen();
+			g_system->delayMillis(10);
 		}
-		// Cycle the partner sprite at 100 ms ticks (same cadence as
-		// `_DoMapScreen`'s `_CheckFrameRate` + `_UpdateAnimations` loop).
-		const uint32 now = g_system->getMillis();
-		if (now - detailLastTick >= 100) {
-			detailLastTick = now;
-			dirty = true;
-		}
-		if (dirty)
-			drawBigMapDetail(scrollX, scrollY, mapPixels, mapW, mapH,
-							 now - detailStartTick);
-		g_system->updateScreen();
-		g_system->delayMillis(10);
+		if (!returnToOverview)
+			return;
 	}
 }
 


Commit: ba0b1afbc3d03fcd8782a0e8ed0aa7f06c040fba
    https://github.com/scummvm/scummvm/commit/ba0b1afbc3d03fcd8782a0e8ed0aa7f06c040fba
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:21:02+02:00

Commit Message:
EEM: mt32 music

Changed paths:
    engines/eem/detection.cpp
    engines/eem/music.cpp
    engines/eem/music.h


diff --git a/engines/eem/detection.cpp b/engines/eem/detection.cpp
index d0b9b5800f2..fd620dd89f4 100644
--- a/engines/eem/detection.cpp
+++ b/engines/eem/detection.cpp
@@ -31,6 +31,8 @@ const PlainGameDescriptor eemGames[] = {
 	{ nullptr, nullptr }
 };
 
+#define GUI_OPTIONS_EEM GUIO3(GAMEOPTION_HIDE_HIGHLIGHT_BOXES, GUIO_MIDIADLIB, GUIO_MIDIMT32)
+
 const ADGameDescription gameDescriptions[] = {
 	{
 		"eem",
@@ -40,7 +42,7 @@ const ADGameDescription gameDescriptions[] = {
 		Common::EN_ANY,
 		Common::kPlatformDOS,
 		ADGF_NO_FLAGS,
-		GUIO1(GAMEOPTION_HIDE_HIGHLIGHT_BOXES)
+		GUI_OPTIONS_EEM
 	},
 	{
 		"eem",
@@ -50,7 +52,7 @@ const ADGameDescription gameDescriptions[] = {
 		Common::EN_ANY,
 		Common::kPlatformDOS,
 		ADGF_NO_FLAGS,
-		GUIO1(GAMEOPTION_HIDE_HIGHLIGHT_BOXES)
+		GUI_OPTIONS_EEM
 	},
 	{
 		// Spanish floppy release ??? same EEM.EXE binary as the English
@@ -64,7 +66,7 @@ const ADGameDescription gameDescriptions[] = {
 		Common::ES_ESP,
 		Common::kPlatformDOS,
 		ADGF_NO_FLAGS,
-		GUIO1(GAMEOPTION_HIDE_HIGHLIGHT_BOXES)
+		GUI_OPTIONS_EEM
 	},
 
 	AD_TABLE_END_MARKER
diff --git a/engines/eem/music.cpp b/engines/eem/music.cpp
index 80842663a09..1bd50f83cce 100644
--- a/engines/eem/music.cpp
+++ b/engines/eem/music.cpp
@@ -22,6 +22,7 @@
 #include "audio/midiparser.h"
 #include "audio/miles.h"
 
+#include "common/config-manager.h"
 #include "common/debug.h"
 #include "common/file.h"
 #include "common/textconsole.h"
@@ -31,15 +32,23 @@
 
 namespace EEM {
 
+namespace {
+
+const int kMidiDriverFlags = MDT_MIDI | MDT_ADLIB | MDT_PREFER_MT32;
+
+} // End of anonymous namespace
+
 MusicPlayer::MusicPlayer(bool isFloppy) : _isFloppy(isFloppy) {
 	// Mirrors `_InitMIDI @ 20a2:013a` which used `_AIL_register_driver`
 	// to walk the .ADV files (ADLIB.ADV, SBFM.ADV, MT32MPU.ADV, etc.)
 	// and pick a backend. We honour the launcher's "Music driver"
-	// setting and only force AdLib / MT-32 paths through Miles when the
-	// detected device matches.
+	// setting while preferring MT-32 when no concrete device was chosen,
+	// like other ScummVM engines with native MT-32 scores.
 	const MidiDriver::DeviceHandle dev =
-		MidiDriver::detectDevice(MDT_MIDI | MDT_ADLIB);
-	const MusicType musicType = MidiDriver::getMusicType(dev);
+		MidiDriver::detectDevice(kMidiDriverFlags);
+	MusicType musicType = MidiDriver::getMusicType(dev);
+	if (musicType == MT_GM && ConfMan.getBool("native_mt32"))
+		musicType = MT_MT32;
 
 	switch (musicType) {
 	case MT_ADLIB:
@@ -64,7 +73,7 @@ MusicPlayer::MusicPlayer(bool isFloppy) : _isFloppy(isFloppy) {
 		break;
 	default:
 		_milesAudioMode = false;
-		createDriver(MDT_MIDI | MDT_ADLIB);
+		createDriver(kMidiDriverFlags);
 		break;
 	}
 
diff --git a/engines/eem/music.h b/engines/eem/music.h
index 50dedeb9eb7..7e8c1fc817d 100644
--- a/engines/eem/music.h
+++ b/engines/eem/music.h
@@ -53,8 +53,8 @@ namespace EEM {
  * different timbres. ScummVM ships a Miles AdLib driver
  * (`Audio::MidiDriver_Miles_AdLib_create`) that loads `SAMPLE.AD` and
  * implements the same install-on-demand workflow, so we use it for
- * AdLib output. MT-32 / GM fall back to the generic driver via
- * `Audio::MidiPlayer::createDriver`.
+ * AdLib output. MT-32 uses ScummVM's Miles MT-32 driver path, and any
+ * other MIDI output falls back to `Audio::MidiPlayer::createDriver`.
  *
  * Available music files in the game directory:
  *   - THEME.XMI   ??? opening anims (looping) + title screen


Commit: fd806599d283c57063b40afa2a092f3993df2828
    https://github.com/scummvm/scummvm/commit/fd806599d283c57063b40afa2a092f3993df2828
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:21:02+02:00

Commit Message:
EEM: message sliding animation for some screens

Changed paths:
    engines/eem/ui.cpp


diff --git a/engines/eem/ui.cpp b/engines/eem/ui.cpp
index 7ed2cac176e..af1a43a8767 100644
--- a/engines/eem/ui.cpp
+++ b/engines/eem/ui.cpp
@@ -72,6 +72,18 @@ constexpr Common::Rect kPdaHelp2Rect(Common::Point(267, 174), 21, 16);
 constexpr Common::Rect kPdaPartnerFootMapRect(Common::Point(7, 177), 50, 23);
 constexpr Common::Rect kPdaSiteRect(Common::Point(35, 111), 21, 25);
 
+constexpr uint16 kProfilePickerRevealPic = 0x105;
+constexpr int kProfilePickerRevealX = 0x3e;
+constexpr int kProfilePickerRevealY = 0xb3;
+
+constexpr uint16 kNameEntryPeekPic = 0x107;
+constexpr int kNameEntryPeekX = 0x3e;
+constexpr int kNameEntryPeekY = 0xb3;
+
+constexpr uint16 kCaseSelectionRevealPic = 0x53;
+constexpr int kCaseSelectionRevealX = 0x3e;
+constexpr int kCaseSelectionRevealY = 0xb2;
+
 bool notebookButtonAt(int x, int y) {
 	return kPdaHelpRect.contains(x, y) ||
 		   kPdaGalleryRect.contains(x, y) ||
@@ -149,6 +161,143 @@ int nextLiveSlot(const Common::Array<Common::Rect> &slotRects,
 	return from;
 }
 
+void copyToScreen(Graphics::ManagedSurface &scratch) {
+	g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
+							   0, 0, 320, 200);
+	g_system->updateScreen();
+}
+
+void blitMaskedPicSlice(Graphics::ManagedSurface &dst, const Picture &pic,
+						int srcX, int srcY, int w, int h,
+						int dstX, int dstY) {
+	if (pic.surface.empty() || w <= 0 || h <= 0)
+		return;
+
+	const byte transp = (byte)(pic.flags >> 8);
+	for (int row = 0; row < h; row++) {
+		const int sy = srcY + row;
+		const int dy = dstY + row;
+		if (sy < 0 || sy >= pic.surface.h || dy < 0 || dy >= dst.h)
+			continue;
+		for (int col = 0; col < w; col++) {
+			const int sx = srcX + col;
+			const int dx = dstX + col;
+			if (sx < 0 || sx >= pic.surface.w || dx < 0 || dx >= dst.w)
+				continue;
+			const byte c = *(const byte *)pic.surface.getBasePtr(sx, sy);
+			if (c != transp)
+				*(byte *)dst.getBasePtr(dx, dy) = c;
+		}
+	}
+}
+
+void blitMaskedPic(Graphics::ManagedSurface &dst, const Picture &pic,
+				   int x, int y) {
+	blitMaskedPicSlice(dst, pic, 0, 0, pic.surface.w, pic.surface.h, x, y);
+}
+
+void blitMaskedPicRightReveal(Graphics::ManagedSurface &dst,
+							  const Picture &pic, int x, int y,
+							  int visibleW) {
+	const int w = CLIP<int>(visibleW, 0, pic.surface.w);
+	if (w == 0)
+		return;
+	blitMaskedPicSlice(dst, pic, 0, 0, w, pic.surface.h,
+					   x + pic.surface.w - w, y);
+}
+
+void blitMaskedPicBottomReveal(Graphics::ManagedSurface &dst,
+							   const Picture &pic, int x, int y,
+							   int visibleH) {
+	const int h = CLIP<int>(visibleH, 0, pic.surface.h);
+	if (h == 0)
+		return;
+	blitMaskedPicSlice(dst, pic, 0, 0, pic.surface.w, h,
+					   x, y + pic.surface.h - h);
+}
+
+bool pumpQuitEvents(EEMEngine *vm) {
+	Common::Event ev;
+	while (g_system->getEventManager()->pollEvent(ev)) {
+		if (ev.type == Common::EVENT_QUIT ||
+			ev.type == Common::EVENT_RETURN_TO_LAUNCHER)
+			return true;
+	}
+	return vm && vm->shouldQuit();
+}
+
+void drawCaseBookTitle(Graphics::ManagedSurface &scratch, const EEMEngine *vm,
+					   uint book) {
+	if (!vm || !vm->getFont().isLoaded())
+		return;
+
+	const bool spanish = vm->isSpanish();
+	const Common::String title = (book == 3)
+		? Common::String(spanish ? "Libro de Retos" : "Challenge Book")
+		: Common::String::format(spanish ? "Lib. %u" : "Book %u", book);
+	const int titleW = vm->getFont().getStringWidth(title);
+	const int titleX = (0xba - titleW) / 2 + 0x3c;
+	vm->getFont().drawString(&scratch, title, titleX, 12, 320, 0xF);
+}
+
+void drawNameEntryFrame(EEMEngine *vm, const Picture *bg, bool haveBG,
+						const Picture *peek, const Common::String &name,
+						const char *prompt) {
+	Graphics::ManagedSurface scratch(320, 200,
+		Graphics::PixelFormat::createFormatCLUT8());
+	scratch.clear();
+	if (haveBG)
+		scratch.simpleBlitFrom(bg->surface);
+	if (peek)
+		blitMaskedPic(scratch, *peek, kNameEntryPeekX, kNameEntryPeekY);
+	vm->getFont().drawString(&scratch, prompt, 80, 40, 240, 0xF);
+	vm->getFont().drawString(&scratch, name + "_", 80, 80, 240, 0xF);
+	copyToScreen(scratch);
+}
+
+bool animateNameEntryPeek(EEMEngine *vm, const Picture *bg, bool haveBG,
+						  const Picture *peek) {
+	if (!peek || peek->surface.empty())
+		return false;
+
+	for (int w = 1; w <= peek->surface.w; w++) {
+		if (pumpQuitEvents(vm))
+			return true;
+		Graphics::ManagedSurface scratch(320, 200,
+			Graphics::PixelFormat::createFormatCLUT8());
+		scratch.clear();
+		if (haveBG)
+			scratch.simpleBlitFrom(bg->surface);
+		blitMaskedPicRightReveal(scratch, *peek,
+								  kNameEntryPeekX, kNameEntryPeekY, w);
+		copyToScreen(scratch);
+		g_system->delayMillis(10);
+	}
+	return false;
+}
+
+bool animateProfilePickerReveal(EEMEngine *vm, const Picture *bg,
+								bool haveBG, const Picture *reveal) {
+	if (!reveal || reveal->surface.empty())
+		return false;
+
+	for (int h = 1; h <= reveal->surface.h; h++) {
+		if (pumpQuitEvents(vm))
+			return true;
+		Graphics::ManagedSurface scratch(320, 200,
+			Graphics::PixelFormat::createFormatCLUT8());
+		scratch.clear();
+		if (haveBG)
+			scratch.simpleBlitFrom(bg->surface);
+		blitMaskedPicBottomReveal(scratch, *reveal,
+								   kProfilePickerRevealX,
+								   kProfilePickerRevealY, h);
+		copyToScreen(scratch);
+		g_system->delayMillis(10);
+	}
+	return false;
+}
+
 // Snapshot of `doCaseSelection`'s captured locals, used by
 // `drawCaseSelectionFrame` (which replaces the original lambda). Lives
 // on the stack inside `doCaseSelection`; never escapes.
@@ -156,6 +305,8 @@ struct CaseSelectionView {
 	EEMEngine *vm;
 	const Picture *caseBg;
 	bool haveCaseBg;
+	const Picture *revealPic;
+	bool haveRevealPic;
 	const Animation *kdAnim;
 	bool haveKdAnim;
 	uint16 kdAnimId;     ///< 0x15 / 0x16 ??? looked up in kAnimScripts
@@ -165,6 +316,7 @@ struct CaseSelectionView {
 	const char *const *pickLabel;
 	const bool *pickEnabled;
 	uint pick;
+	uint book;
 };
 
 // Mystery list shown in the "Choose A Mystery" sub-screen. Mirrors
@@ -213,6 +365,12 @@ struct CaseSubmenuView {
 	EEMEngine *vm;
 	const Picture *caseBg;
 	bool haveCaseBg;
+	const Picture *revealPic;
+	bool haveRevealPic;
+	const Animation *kdAnim;
+	bool haveKdAnim;
+	int kdAnimX;
+	int kdAnimY;
 	const Common::StringArray *names;
 	const Common::Array<bool> *solvedFlags;
 	uint topRow;
@@ -220,6 +378,72 @@ struct CaseSubmenuView {
 	uint book;            ///< 1..3 ??? for the "Book N" / "Challenge Book" title
 };
 
+void drawCaseGreeter(Graphics::ManagedSurface &scratch,
+					 const Animation *kdAnim, bool haveKdAnim,
+					 int kdAnimX, int kdAnimY) {
+	if (!haveKdAnim || !kdAnim || kdAnim->empty())
+		return;
+
+	// `_CaseSelection` registers the partner-specific ANI slot, but
+	// drives it with script 0x15 regardless of partner. The chooser
+	// loop advances it through `_UpdateAnimations` after each
+	// `_CheckFrameRate` tick.
+	const uint32 now = g_system->getMillis();
+	const uint frameIdx = partnerFrameAtTick(0x15, (uint)kdAnim->size(), now);
+	blitAnimFrameAnchored(scratch.surfacePtr(), (*kdAnim)[frameIdx],
+						  kdAnimX, kdAnimY);
+}
+
+void drawCaseBase(Graphics::ManagedSurface &scratch, EEMEngine *vm,
+				  const Picture *caseBg, bool haveCaseBg,
+				  const Picture *revealPic, bool haveRevealPic,
+				  const Animation *kdAnim, bool haveKdAnim,
+				  int kdAnimX, int kdAnimY, uint book) {
+	scratch.clear();
+	if (haveCaseBg && caseBg)
+		scratch.simpleBlitFrom(caseBg->surface);
+	if (haveRevealPic && revealPic)
+		blitMaskedPic(scratch, *revealPic,
+					   kCaseSelectionRevealX, kCaseSelectionRevealY);
+	drawCaseBookTitle(scratch, vm, book);
+	drawCaseGreeter(scratch, kdAnim, haveKdAnim, kdAnimX, kdAnimY);
+}
+
+bool animateCaseSelectionReveal(EEMEngine *vm, const Picture *caseBg,
+								bool haveCaseBg, const Picture *revealPic,
+								bool haveRevealPic, const Animation *kdAnim,
+								bool haveKdAnim, int kdAnimX, int kdAnimY,
+								uint book) {
+	if (!haveRevealPic || !revealPic || revealPic->surface.empty())
+		return false;
+
+	const int steps = vm && vm->isFloppy()
+		? revealPic->surface.w : revealPic->surface.h;
+	for (int i = 1; i <= steps; i++) {
+		if (pumpQuitEvents(vm))
+			return true;
+		Graphics::ManagedSurface scratch(320, 200,
+			Graphics::PixelFormat::createFormatCLUT8());
+		scratch.clear();
+		if (haveCaseBg && caseBg)
+			scratch.simpleBlitFrom(caseBg->surface);
+		if (vm && vm->isFloppy()) {
+			blitMaskedPicRightReveal(scratch, *revealPic,
+									  kCaseSelectionRevealX,
+									  kCaseSelectionRevealY, i);
+		} else {
+			blitMaskedPicBottomReveal(scratch, *revealPic,
+									   kCaseSelectionRevealX,
+									   kCaseSelectionRevealY, i);
+		}
+		drawCaseBookTitle(scratch, vm, book);
+		drawCaseGreeter(scratch, kdAnim, haveKdAnim, kdAnimX, kdAnimY);
+		copyToScreen(scratch);
+		g_system->delayMillis(8);
+	}
+	return false;
+}
+
 // Mirrors `_DoChoose`'s `DrawList @ 1c33:040d`. 12 visible rows ?? 10 px
 // at (61, 35); colour palette: 0x13 = highlighted (selected), 0x1B =
 // greyed (already solved), 0x5C = default. We approximate with the
@@ -229,25 +453,12 @@ struct CaseSubmenuView {
 void drawCaseSubmenu(const CaseSubmenuView &v) {
 	Graphics::ManagedSurface scratch(320, 200,
 		Graphics::PixelFormat::createFormatCLUT8());
-	scratch.clear();
-	if (v.haveCaseBg)
-		scratch.simpleBlitFrom(v.caseBg->surface);
+	drawCaseBase(scratch, v.vm, v.caseBg, v.haveCaseBg,
+				 v.revealPic, v.haveRevealPic,
+				 v.kdAnim, v.haveKdAnim, v.kdAnimX, v.kdAnimY, v.book);
 	if (!v.vm->getFont().isLoaded() || !v.names)
 		return;
 
-	// Top centred title. `_CaseSelection @ 1c33:0aa3` formats "Book %d"
-	// for tiers 1/2 and "Challenge Book" (sprintf with no arg) for
-	// tier 3. `_Show_String(0xc, (0xba - width)/2 + 0x3c, ???, 0x10)`
-	// places it horizontally centred over the panel. Spanish floppy
-	// uses "Lib. %u" / "Libro de Retos" (verified in Spanish EEM.EXE).
-	const bool spanish = v.vm && v.vm->isSpanish();
-	const Common::String title = (v.book == 3)
-		? Common::String(spanish ? "Libro de Retos" : "Challenge Book")
-		: Common::String::format(spanish ? "Lib. %u" : "Book %u", v.book);
-	const int titleW = v.vm->getFont().getStringWidth(title);
-	const int titleX = (0xba - titleW) / 2 + 0x3c;
-	v.vm->getFont().drawString(&scratch, title, titleX, 12, 320, 0xF);
-
 	const int kListX  = 61;
 	const int kListW  = 238 - kListX;
 	const int kListY0 = 35;
@@ -298,38 +509,16 @@ void drawCaseSubmenu(const CaseSubmenuView &v) {
 		scratch.frameRect(thumb, 0xF);
 	}
 
-	g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
-							   0, 0, 320, 200);
-	g_system->updateScreen();
+	copyToScreen(scratch);
 }
 
 void drawCaseSelectionFrame(const CaseSelectionView &v) {
 	Graphics::ManagedSurface scratch(320, 200,
 		Graphics::PixelFormat::createFormatCLUT8());
-	scratch.clear();
-	if (v.haveCaseBg)
-		scratch.simpleBlitFrom(v.caseBg->surface);
-
-	// KD greeter frame ??? masked-blit current animation cell at
-	// (0x112, 0x50). 100 ms tick matches `_CheckFrameRate`. The
-	// original `_CaseSelection @ 1c33:0a87` calls `_NewAnimation(...,
-	// CONCAT22(0x15, ...), ..., seqnum=0x15, ...)` so the script
-	// key is 0x15 regardless of partner ??? even Jenny's CELLS (loaded
-	// via animID 0x16 = ANI.DBD slot) get driven by Jake's 0x15
-	// blink script. Both 0x15 and 0x16 are aliases of 0x00 in our
-	// table so the result is identical, but routing through 0x15
-	// matches the binary.
-	if (v.haveKdAnim) {
-		const uint32 now = g_system->getMillis();
-		const uint frameIdx = partnerFrameAtTick(0x15,
-												  (uint)v.kdAnim->size(), now);
-		// Anchor-aware blit. Same rendering path used everywhere
-		// the partner is registered through `_NewAnimation` in the
-		// original.
-		blitAnimFrameAnchored(scratch.surfacePtr(),
-							  (*v.kdAnim)[frameIdx],
-							  v.kdAnimX, v.kdAnimY);
-	}
+	drawCaseBase(scratch, v.vm, v.caseBg, v.haveCaseBg,
+				 v.revealPic, v.haveRevealPic,
+				 v.kdAnim, v.haveKdAnim, v.kdAnimX, v.kdAnimY, v.book);
+
 	if (v.vm->getFont().isLoaded()) {
 		// `DrawList` @ 1c33:040d coordinates: `_TextBox + 3` for x
 		// and `DAT_29be_0d02` for y. `_TextBox` @ 29be:0d00 holds
@@ -339,17 +528,6 @@ void drawCaseSelectionFrame(const CaseSelectionView &v) {
 		const int kListY0 = 35;
 		const int kLineH  = 10;
 
-		// Top centred "Book %d" / "Challenge Book" title ??? sprintf
-		// format strings at 29be:0deb / 29be:0dfa shown via
-		// `_Show_String(0xc, (0xba - width)/2 + 0x3c, ???)` in the
-		// original. We don't track challenge tier yet so always
-		// show "Book 1".
-		const Common::String book = v.vm->isSpanish()
-			? Common::String("Lib. 1") : Common::String("Book 1");
-		const int titleW = v.vm->getFont().getStringWidth(book);
-		const int titleX = (0xba - titleW) / 2 + 0x3c;
-		v.vm->getFont().drawString(&scratch, book, titleX, 12, 320, 0xF);
-
 		// Render 11 list rows: separator + menu item pairs.
 		//   row 0  separator
 		//   row 1  Choose A Mystery
@@ -373,9 +551,7 @@ void drawCaseSelectionFrame(const CaseSelectionView &v) {
 									   kListX, y, kListW, color);
 		}
 	}
-	g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
-							   0, 0, 320, 200);
-	g_system->updateScreen();
+	copyToScreen(scratch);
 }
 
 void EEMEngine::doProfilePicker() {
@@ -426,14 +602,18 @@ void EEMEngine::doProfilePicker() {
 
 	int sel = 0;
 	bool done = false;
+	Picture bg;
+	const bool haveBG = _picsArchive.getPicture(0x104, bg);
+	Picture reveal;
+	const bool haveReveal =
+		_picsArchive.getPicture(kProfilePickerRevealPic, reveal);
 
 	// Picker geometry: `DrawList @ 1c33:040d` is called from
 	// `screen8_handler @ 1c33:1012` with `(_TextBox + 3, DAT_29be_0d02)`.
 	// `_TextBox @ 29be:0d00` holds {x1=58, y1=35, x2=238, y2=158} so
 	// the list origin is (61, 35), 10 px per row, max 12 visible
-	// rows. The "Pick a player" caption is part of the BG (PIC 0x104)
-	// ??? `screen8_handler` never draws it as text ??? so an extra
-	// `drawString` would overlay on top of the baked-in heading.
+	// rows. `screen8_handler` slides PIC 0x105 into the lower strip
+	// before calling `_DoChoose`; it does not draw that caption as text.
 	const int kListX = 61;
 	const int kListY = 35;
 	const int kLineH = 10;
@@ -441,18 +621,21 @@ void EEMEngine::doProfilePicker() {
 		Graphics::ManagedSurface scratch(320, 200,
 			Graphics::PixelFormat::createFormatCLUT8());
 		scratch.clear();
-		Picture bg;
-		if (_picsArchive.getPicture(0x104, bg))
+		if (haveBG)
 			scratch.simpleBlitFrom(bg.surface);
+		if (haveReveal)
+			blitMaskedPic(scratch, reveal,
+						   kProfilePickerRevealX, kProfilePickerRevealY);
 		for (uint i = 0; i < entries.size(); i++) {
 			const byte color = ((int)i == sel) ? 0xF : 0x8;
 			_font.drawString(&scratch, entries[i].label,
 							 kListX, kListY + (int)i * kLineH, 220, color);
 		}
-		g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
-								   0, 0, 320, 200);
-		g_system->updateScreen();
+		copyToScreen(scratch);
 	};
+	if (animateProfilePickerReveal(this, &bg, haveBG,
+								   haveReveal ? &reveal : nullptr))
+		return;
 	draw();
 
 	while (!done && !shouldQuit()) {
@@ -524,8 +707,7 @@ void EEMEngine::doProfilePicker() {
 void EEMEngine::doNewPlayer() {
 	// Mirrors `_NewPlayer` @ 1c33:0dda. The original draws background
 	// 0x104 + character peek pic 0x107, then shows "Please type your
-	// name" and accepts up to 12 characters until Enter. We render a
-	// minimal version: black screen + prompt.
+	// name" and accepts up to 12 characters until Enter.
 	if (!_font.isLoaded()) {
 		_playerName = "Detective";
 		return;
@@ -538,6 +720,8 @@ void EEMEngine::doNewPlayer() {
 	// The original also slides in PIC 0x107 (a peeking character).
 	Picture bg;
 	const bool haveBG = _picsArchive.getPicture(0x104, bg);
+	Picture peek;
+	const bool havePeek = _picsArchive.getPicture(kNameEntryPeekPic, peek);
 
 	// Localized name-entry prompt. Spanish text is taken from the
 	// Spanish floppy EEM.EXE ("Teclea tu nombre"). The colon suffix is
@@ -545,19 +729,13 @@ void EEMEngine::doNewPlayer() {
 	const char *prompt = isSpanish()
 		? "Teclea tu nombre:" : "Please type your name:";
 
+	if (animateNameEntryPeek(this, &bg, haveBG, havePeek ? &peek : nullptr))
+		return;
 	// Match the original `_NewPlayer`: `_Show_String(rw=0x28, cl=0x50)`
 	// for the prompt, then `_ShowChar(0x50, x, ???)` for typed input.
 	// (rw=row=y, cl=col=x.) Prompt at (y=40, x=80), input at (y=80, x=80).
-	Graphics::ManagedSurface scratch(320, 200,
-		Graphics::PixelFormat::createFormatCLUT8());
-	scratch.clear();
-	if (haveBG)
-		scratch.simpleBlitFrom(bg.surface);
-	_font.drawString(&scratch, prompt, 80, 40, 240, 0xF);
-	_font.drawString(&scratch, name + "_", 80, 80, 240, 0xF);
-	g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
-							   0, 0, 320, 200);
-	g_system->updateScreen();
+	drawNameEntryFrame(this, &bg, haveBG, havePeek ? &peek : nullptr,
+					   name, prompt);
 
 	g_system->setFeatureState(OSystem::kFeatureVirtualKeyboard, true);
 
@@ -616,16 +794,8 @@ void EEMEngine::doNewPlayer() {
 			}
 		}
 		if (dirty) {
-			// Re-render with the updated `name`. Same body as the
-			// initial render above ??? only `name + "_"` changes.
-			scratch.clear();
-			if (haveBG)
-				scratch.simpleBlitFrom(bg.surface);
-			_font.drawString(&scratch, prompt, 80, 40, 240, 0xF);
-			_font.drawString(&scratch, name + "_", 80, 80, 240, 0xF);
-			g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
-									   0, 0, 320, 200);
-			g_system->updateScreen();
+			drawNameEntryFrame(this, &bg, haveBG, havePeek ? &peek : nullptr,
+							   name, prompt);
 		}
 		g_system->updateScreen();
 		g_system->delayMillis(15);
@@ -1482,6 +1652,9 @@ void EEMEngine::doCaseSelection() {
 	// Mirrors `_CaseSelection`: load PIC 0x41 as the chooser backdrop.
 	Picture caseBg;
 	const bool haveCaseBg = _picsArchive.getPicture(0x41, caseBg);
+	Picture revealPic;
+	const bool haveRevealPic =
+		_picsArchive.getPicture(kCaseSelectionRevealPic, revealPic);
 
 	// KD greeter sprite. `_CaseSelection @ 1c33:0a87` (1c33:0b7e-0ba1)
 	// loads anim 0x15 (Jake-paired) or 0x16 (Jenny-paired) and registers
@@ -1495,11 +1668,15 @@ void EEMEngine::doCaseSelection() {
 							 && !kdAnim.empty();
 	const int kKdAnimX = 0x112;
 	const int kKdAnimY = 0x50;
+	const uint caseBook = (_chainStage == 3) ? 3 :
+						  (_chainStage == 2) ? 2 : 1;
 
 	CaseSelectionView v;
 	v.vm = this;
 	v.caseBg = &caseBg;
 	v.haveCaseBg = haveCaseBg;
+	v.revealPic = &revealPic;
+	v.haveRevealPic = haveRevealPic;
 	v.kdAnim = &kdAnim;
 	v.haveKdAnim = haveKdAnim;
 	v.kdAnimId = (uint16)kKdAniId;
@@ -1509,7 +1686,13 @@ void EEMEngine::doCaseSelection() {
 	v.pickLabel = kPickLabel;
 	v.pickEnabled = kPickEnabled;
 	v.pick = pick;
+	v.book = caseBook;
 
+	if (animateCaseSelectionReveal(this, &caseBg, haveCaseBg,
+								   &revealPic, haveRevealPic,
+								   &kdAnim, haveKdAnim,
+								   kKdAnimX, kKdAnimY, caseBook))
+		return;
 	drawCaseSelectionFrame(v);
 	uint32 lastTick = g_system->getMillis();
 
@@ -1723,13 +1906,25 @@ void EEMEngine::doCaseSelection() {
 	sv.vm = this;
 	sv.caseBg = &caseBg;
 	sv.haveCaseBg = haveCaseBg;
+	sv.revealPic = &revealPic;
+	sv.haveRevealPic = haveRevealPic;
+	sv.kdAnim = &kdAnim;
+	sv.haveKdAnim = haveKdAnim;
+	sv.kdAnimX = kKdAnimX;
+	sv.kdAnimY = kKdAnimY;
 	sv.names = &names;
 	sv.solvedFlags = &solvedFlags;
 	sv.topRow = topRow;
 	sv.selRow = selRow;
 	sv.book = book;
 
+	if (animateCaseSelectionReveal(this, &caseBg, haveCaseBg,
+								   &revealPic, haveRevealPic,
+								   &kdAnim, haveKdAnim,
+								   kKdAnimX, kKdAnimY, book))
+		return;
 	drawCaseSubmenu(sv);
+	uint32 submenuLastTick = g_system->getMillis();
 	bool confirmed = false;
 	while (!confirmed && !shouldQuit()) {
 		Common::Event ev;
@@ -1838,7 +2033,11 @@ void EEMEngine::doCaseSelection() {
 				continue;
 			}
 		}
-		if (dirty) {
+		const uint32 now = g_system->getMillis();
+		const bool animTick = haveKdAnim && now - submenuLastTick >= 100;
+		if (animTick)
+			submenuLastTick = now;
+		if (dirty || animTick) {
 			sv.topRow = topRow;
 			sv.selRow = selRow;
 			drawCaseSubmenu(sv);


Commit: 6494a017697d0d4e9bd35559e0801598bd51c591
    https://github.com/scummvm/scummvm/commit/6494a017697d0d4e9bd35559e0801598bd51c591
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:21:03+02:00

Commit Message:
EEM: complete handler for screen profile

Changed paths:
    engines/eem/eem.cpp
    engines/eem/ui.cpp


diff --git a/engines/eem/eem.cpp b/engines/eem/eem.cpp
index f7523be122b..dba23490674 100644
--- a/engines/eem/eem.cpp
+++ b/engines/eem/eem.cpp
@@ -582,6 +582,27 @@ screen_loop:
 			doSetup();
 			break;
 
+		case kScreenProfile:
+			// Handler 8 is the player/profile picker. CD
+			// `screen8_handler @ 1c33:1012` loads an existing player
+			// record or runs `_NewPlayer`; floppy
+			// `_HandleScreen8_NewPlayer_Floppy @ 19bb:0ec2` then
+			// writes screen 9. Mirror that route inline: after the
+			// profile is selected, choose a partner, then continue to
+			// the selected profile's loaded case if ScummVM save state
+			// had one, otherwise to case selection.
+			_nextScreen = kScreenInvalid;
+			_mystery.clear();
+			doProfilePicker();
+			if (!shouldQuit())
+				applyStartupTestOverrides();
+			if (!shouldQuit())
+				doChoosePartner();
+			if (!shouldQuit())
+				_nextScreen = _mystery.isLoaded() ? kScreenMap
+												  : kScreenChooseMystery;
+			break;
+
 		case kScreenAccuse:
 			// Handler 7 runs the accusation flow. A failed accusation
 			// returns to `_LastScreen`; a correct solution writes
diff --git a/engines/eem/ui.cpp b/engines/eem/ui.cpp
index af1a43a8767..8a2fab8286a 100644
--- a/engines/eem/ui.cpp
+++ b/engines/eem/ui.cpp
@@ -75,6 +75,18 @@ constexpr Common::Rect kPdaSiteRect(Common::Point(35, 111), 21, 25);
 constexpr uint16 kProfilePickerRevealPic = 0x105;
 constexpr int kProfilePickerRevealX = 0x3e;
 constexpr int kProfilePickerRevealY = 0xb3;
+constexpr int kProfileListX = 61;
+constexpr int kProfileListY = 35;
+constexpr int kProfileListW = 220;
+constexpr int kProfileLineH = 10;
+constexpr int kProfileVisibleRows = 12;
+constexpr Common::Rect kChooserOkRect(Common::Point(12, 63), 29, 24);
+constexpr Common::Rect kChooserHelpRect(Common::Point(12, 100), 29, 24);
+constexpr Common::Rect kChooserExitRect(Common::Point(12, 137), 29, 24);
+constexpr Common::Rect kChooserUpArrowRect(Common::Point(240, 31), 10, 12);
+constexpr Common::Rect kChooserDnArrowRect(Common::Point(240, 148), 10, 11);
+constexpr Common::Rect kChooserListRect(Common::Point(58, 35), 180, 123);
+constexpr Common::Rect kChooserNewPlayerRect(Common::Point(61, 176), 185, 15);
 
 constexpr uint16 kNameEntryPeekPic = 0x107;
 constexpr int kNameEntryPeekX = 0x3e;
@@ -298,6 +310,62 @@ bool animateProfilePickerReveal(EEMEngine *vm, const Picture *bg,
 	return false;
 }
 
+struct ProfilePickerEntry {
+	Common::String label;
+	int slot;       ///< -1 means "create new"
+};
+
+struct ProfilePickerView {
+	EEMEngine *vm;
+	const Picture *bg;
+	bool haveBG;
+	const Picture *reveal;
+	bool haveReveal;
+	const Common::Array<ProfilePickerEntry> *entries;
+	int selected;
+	int start;
+};
+
+void clampProfileScroll(int &selected, int &start, int count) {
+	if (count <= 0) {
+		selected = 0;
+		start = 0;
+		return;
+	}
+	selected = CLIP<int>(selected, 0, count - 1);
+	const int maxStart = MAX<int>(0, count - kProfileVisibleRows);
+	start = CLIP<int>(start, 0, maxStart);
+	if (selected < start)
+		start = selected;
+	if (selected >= start + kProfileVisibleRows)
+		start = selected - kProfileVisibleRows + 1;
+	start = CLIP<int>(start, 0, maxStart);
+}
+
+void drawProfilePickerFrame(const ProfilePickerView &v) {
+	Graphics::ManagedSurface scratch(320, 200,
+		Graphics::PixelFormat::createFormatCLUT8());
+	scratch.clear();
+	if (v.haveBG)
+		scratch.simpleBlitFrom(v.bg->surface);
+	if (v.haveReveal)
+		blitMaskedPic(scratch, *v.reveal,
+					   kProfilePickerRevealX, kProfilePickerRevealY);
+
+	const int count = v.entries ? (int)v.entries->size() : 0;
+	for (int row = 0; row < kProfileVisibleRows; row++) {
+		const int idx = v.start + row;
+		if (idx >= count)
+			break;
+		const byte color = idx == v.selected ? 0xF : 0x8;
+		v.vm->getFont().drawString(&scratch, (*v.entries)[idx].label,
+									kProfileListX,
+									kProfileListY + row * kProfileLineH,
+									kProfileListW, color);
+	}
+	copyToScreen(scratch);
+}
+
 // Snapshot of `doCaseSelection`'s captured locals, used by
 // `drawCaseSelectionFrame` (which replaces the original lambda). Lives
 // on the stack inside `doCaseSelection`; never escapes.
@@ -561,7 +629,8 @@ void EEMEngine::doProfilePicker() {
 	// hands the list to `_DoChoose`. If no profiles exist (loop hits
 	// `local_20 == 0` at 1c33:1170), it falls straight into
 	// `_NewPlayer`. Selecting an entry calls `_LoadPlayerRecord` and
-	// returns; selecting the "exit" sentinel goes back to title.
+	// returns; the 0xfffe / 0xffff chooser sentinels both enter
+	// `_NewPlayer` in this screen.
 
 	// Palette reset. `screen8_handler` runs `_FadeOut(); _GetPalette(0);
 	// _GetBackground(0x104);` before the picker, so the BG always
@@ -584,24 +653,26 @@ void EEMEngine::doProfilePicker() {
 	}
 
 	// Build the visible list: existing profile names + "[New Player]".
-	struct Entry {
-		Common::String label;
-		int slot;       ///< -1 means "create new"
-	};
-	Common::Array<Entry> entries;
+	// The DOS picker also has a bottom click area at 29be:0d08 that
+	// returns 0xfffe and immediately enters `_NewPlayer`; keeping the
+	// explicit row makes that affordance visible in ScummVM while the
+	// bottom rect remains active too.
+	Common::Array<ProfilePickerEntry> entries;
 	for (const SaveStateDescriptor &s : saves) {
-		Entry e;
+		ProfilePickerEntry e;
 		e.label = s.getDescription();
 		e.slot  = s.getSaveSlot();
 		entries.push_back(e);
 	}
-	Entry newEntry;
+	ProfilePickerEntry newEntry;
 	newEntry.label = "[New Player]";
 	newEntry.slot  = -1;
 	entries.push_back(newEntry);
 
 	int sel = 0;
+	int start = 0;
 	bool done = false;
+	bool createNew = false;
 	Picture bg;
 	const bool haveBG = _picsArchive.getPicture(0x104, bg);
 	Picture reveal;
@@ -614,29 +685,19 @@ void EEMEngine::doProfilePicker() {
 	// the list origin is (61, 35), 10 px per row, max 12 visible
 	// rows. `screen8_handler` slides PIC 0x105 into the lower strip
 	// before calling `_DoChoose`; it does not draw that caption as text.
-	const int kListX = 61;
-	const int kListY = 35;
-	const int kLineH = 10;
-	auto draw = [&]() {
-		Graphics::ManagedSurface scratch(320, 200,
-			Graphics::PixelFormat::createFormatCLUT8());
-		scratch.clear();
-		if (haveBG)
-			scratch.simpleBlitFrom(bg.surface);
-		if (haveReveal)
-			blitMaskedPic(scratch, reveal,
-						   kProfilePickerRevealX, kProfilePickerRevealY);
-		for (uint i = 0; i < entries.size(); i++) {
-			const byte color = ((int)i == sel) ? 0xF : 0x8;
-			_font.drawString(&scratch, entries[i].label,
-							 kListX, kListY + (int)i * kLineH, 220, color);
-		}
-		copyToScreen(scratch);
-	};
+	ProfilePickerView view;
+	view.vm = this;
+	view.bg = &bg;
+	view.haveBG = haveBG;
+	view.reveal = &reveal;
+	view.haveReveal = haveReveal;
+	view.entries = &entries;
+	view.selected = sel;
+	view.start = start;
 	if (animateProfilePickerReveal(this, &bg, haveBG,
 								   haveReveal ? &reveal : nullptr))
 		return;
-	draw();
+	drawProfilePickerFrame(view);
 
 	while (!done && !shouldQuit()) {
 		Common::Event ev;
@@ -652,10 +713,24 @@ void EEMEngine::doProfilePicker() {
 				switch (ev.kbd.keycode) {
 				case Common::KEYCODE_UP:
 					sel = (sel + (int)entries.size() - 1) % (int)entries.size();
+					clampProfileScroll(sel, start, (int)entries.size());
 					dirty = true;
 					break;
 				case Common::KEYCODE_DOWN:
 					sel = (sel + 1) % (int)entries.size();
+					clampProfileScroll(sel, start, (int)entries.size());
+					dirty = true;
+					break;
+				case Common::KEYCODE_PAGEUP:
+					start -= kProfileVisibleRows;
+					sel = start;
+					clampProfileScroll(sel, start, (int)entries.size());
+					dirty = true;
+					break;
+				case Common::KEYCODE_PAGEDOWN:
+					start += kProfileVisibleRows;
+					sel = start;
+					clampProfileScroll(sel, start, (int)entries.size());
 					dirty = true;
 					break;
 				case Common::KEYCODE_RETURN:
@@ -663,17 +738,64 @@ void EEMEngine::doProfilePicker() {
 					committed = true;
 					break;
 				case Common::KEYCODE_ESCAPE:
-					_playerName = "Detective";
-					return;
+					createNew = true;
+					committed = true;
+					break;
 				default:
 					break;
 				}
 			}
 			if (ev.type == Common::EVENT_LBUTTONDOWN) {
-				const int hit = (ev.mouse.y - kListY) / kLineH;
-				if (hit >= 0 && hit < (int)entries.size()) {
-					sel = hit;
+				if (kChooserOkRect.contains(ev.mouse.x, ev.mouse.y)) {
 					committed = true;
+					break;
+				}
+				if (kChooserExitRect.contains(ev.mouse.x, ev.mouse.y) ||
+					kChooserNewPlayerRect.contains(ev.mouse.x, ev.mouse.y)) {
+					createNew = true;
+					committed = true;
+					break;
+				}
+				if (kChooserUpArrowRect.contains(ev.mouse.x, ev.mouse.y)) {
+					if (start > 0) {
+						start--;
+						if (sel >= start + kProfileVisibleRows)
+							sel = start + kProfileVisibleRows - 1;
+						clampProfileScroll(sel, start, (int)entries.size());
+						dirty = true;
+					}
+					break;
+				}
+				if (kChooserDnArrowRect.contains(ev.mouse.x, ev.mouse.y)) {
+					const int maxStart = MAX<int>(0,
+						(int)entries.size() - kProfileVisibleRows);
+					if (start < maxStart) {
+						start++;
+						if (sel < start)
+							sel = start;
+						clampProfileScroll(sel, start, (int)entries.size());
+						dirty = true;
+					}
+					break;
+				}
+				if (kChooserHelpRect.contains(ev.mouse.x, ev.mouse.y)) {
+					// `screen8_handler` sets `Chelp = 0`, so the
+					// original ignores this chooser button here.
+					break;
+				}
+				if (kChooserListRect.contains(ev.mouse.x, ev.mouse.y)) {
+					const int hit = (ev.mouse.y - kProfileListY) /
+									kProfileLineH;
+					const int idx = start + hit;
+					if (hit >= 0 && hit < kProfileVisibleRows &&
+						idx >= 0 && idx < (int)entries.size()) {
+						if (idx == sel) {
+							committed = true;
+							break;
+						}
+						sel = idx;
+						dirty = true;
+					}
 				}
 			}
 			if (committed)
@@ -683,13 +805,21 @@ void EEMEngine::doProfilePicker() {
 			done = true;
 			break;
 		}
-		if (dirty)
-			draw();
+		if (dirty) {
+			view.selected = sel;
+			view.start = start;
+			drawProfilePickerFrame(view);
+		}
 		g_system->updateScreen();
 		g_system->delayMillis(15);
 	}
 
-	const Entry &e = entries[sel];
+	if (createNew) {
+		doNewPlayer();
+		return;
+	}
+
+	const ProfilePickerEntry &e = entries[sel];
 	if (e.slot < 0) {
 		doNewPlayer();
 	} else {
@@ -1504,12 +1634,13 @@ void EEMEngine::doSetup() {
 				continue;
 			}
 
-			// Profile (button [2]). Goes back to the profile picker
-			// in the original (`_NextScreen = 8`). Treat the same way
-			// as Done for now ??? switching profiles mid-game isn't
-			// wired in our port and would discard mystery state.
+			// Profile (button [2]). Original handler writes
+			// `_NextScreen = 8`, returning to the player/profile
+			// picker. Save the current profile settings first, then
+			// let the screen driver run profile ??? partner ??? case/map.
 			if (kProfileBtn.contains(mx, my)) {
-				leaveSetup();
+				saveProfile(_playerName);
+				_nextScreen = kScreenProfile;
 				return;
 			}
 


Commit: a1da71fd4dea62eee29dc3530910a2cb6f07fa99
    https://github.com/scummvm/scummvm/commit/a1da71fd4dea62eee29dc3530910a2cb6f07fa99
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:21:03+02:00

Commit Message:
EEM: missing button code to reload the current profile

Changed paths:
    engines/eem/ui.cpp


diff --git a/engines/eem/ui.cpp b/engines/eem/ui.cpp
index 8a2fab8286a..986ee11a41f 100644
--- a/engines/eem/ui.cpp
+++ b/engines/eem/ui.cpp
@@ -1859,8 +1859,8 @@ void EEMEngine::doCaseSelection() {
 					break;
 				}
 				if (kHelpRect.contains(ev.mouse.x, ev.mouse.y)) {
-					// HELP placeholder ??? original calls `_DisplayHint`;
-					// our help screen is wired to `H` later in the flow.
+					// `_ActionScreen` sets `Chelp = 0`, so `_DoChoose`
+					// ignores this middle button on the top-level menu.
 					continue;
 				}
 				// List panel: click on a non-separator row selects the
@@ -2074,6 +2074,18 @@ void EEMEngine::doCaseSelection() {
 					_mystery.clear();
 					return;
 				}
+				if (kHelpRect.contains(ev.mouse.x, ev.mouse.y)) {
+					// In original `_CaseSelection`, this button is
+					// PIC 0x123 and returns 0xfffe, which calls
+					// `_ChooseSavedGame`. The ScummVM port stores the
+					// in-progress case in the profile save instead of
+					// original per-mystery files, so route to screen 8
+					// (profile picker) as the load/resume path.
+					saveProfile(_playerName);
+					_mystery.clear();
+					_nextScreen = kScreenProfile;
+					return;
+				}
 				if (kUpArrowRect.contains(ev.mouse.x, ev.mouse.y)) {
 					if (topRow > 0) { topRow--; dirty = true; }
 					continue;


Commit: d3fcc8c249b223d7ff8407e8e0f77237b9edb8aa
    https://github.com/scummvm/scummvm/commit/d3fcc8c249b223d7ff8407e8e0f77237b9edb8aa
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:21:03+02:00

Commit Message:
EEM: implement missing animations for floppy release

Changed paths:
    engines/eem/eem.cpp
    engines/eem/eem.h
    engines/eem/mystery.cpp
    engines/eem/mystery.h
    engines/eem/site.cpp
    engines/eem/ui.cpp


diff --git a/engines/eem/eem.cpp b/engines/eem/eem.cpp
index dba23490674..12596f8fe42 100644
--- a/engines/eem/eem.cpp
+++ b/engines/eem/eem.cpp
@@ -283,6 +283,7 @@ Common::Error EEMEngine::run() {
 	const int wantedSave = ConfMan.hasKey("save_slot")
 		? ConfMan.getInt("save_slot") : -1;
 	bool resumed = false;
+	bool skippedIntro = false;
 	if (wantedSave >= 0) {
 		const Common::Error err = loadGameState(wantedSave);
 		if (err.getCode() == Common::kNoError) {
@@ -362,9 +363,6 @@ Common::Error EEMEngine::run() {
 		if (!shouldQuit() && !_skipIntro)
 			playAnm(Common::Path("MOVIE.ANM"), 120,
 					/*holdLastFrame=*/false);
-		if (!shouldQuit() && !_skipIntro)
-			playAnm(Common::Path("TITLE.ANM"), 120,
-					/*holdLastFrame=*/true);
 	} else {
 		showEAKidsLogo();
 		if (!shouldQuit() && !_skipIntro)
@@ -421,8 +419,14 @@ Common::Error EEMEngine::run() {
 			playAnm(Common::Path("TITLE.ANM"), 120,
 					/*holdLastFrame=*/true, /*fadeIn=*/true);
 	}
+	skippedIntro = _skipIntro;
 	_skipIntro = false;
 
+	if (isFloppy() && !shouldQuit() && !skippedIntro) {
+		_nextScreen = kScreenTitle;
+		goto screen_loop;
+	}
+
 	// After the title chain, the original goes Title (B) -> screen 8
 	// (NewPlayer / saved-record selection) -> screen 9 (ChoosePartner) ->
 	// screen A (CaseSelection) -> site loop. We mirror the same order.
@@ -488,6 +492,22 @@ screen_loop:
 		debugC(1, kDebugGeneral, "screenDriver: id=%d", (int)current);
 
 		switch (current) {
+		case kScreenTitle:
+			// Floppy handler 0xb (`_HandleScreen11_Title_Floppy`) calls
+			// `_DoTitle_Floppy`, whose `_PlayTitleANM_Floppy(1)` file
+			// table entry is `TITLE.ANM`. The opening driver stops after
+			// `MOVIE.ANM`; this live screen owns the title wait and then
+			// writes `_NextScreen = 8` for the profile picker.
+			_nextScreen = kScreenProfile;
+			if (isFloppy()) {
+				CursorMan.showMouse(false);
+				playAnm(Common::Path("TITLE.ANM"), 120,
+						/*holdLastFrame=*/true, /*fadeIn=*/true);
+				_skipIntro = false;
+				CursorMan.showMouse(true);
+			}
+			break;
+
 		case kScreenAction:
 			// Post-mystery menu. The original's `_ActionScreen @
 			// 1c33:195b` shows the 5-entry "Choose A Mystery /
diff --git a/engines/eem/eem.h b/engines/eem/eem.h
index ae1a6de44d8..44c5bc4a905 100644
--- a/engines/eem/eem.h
+++ b/engines/eem/eem.h
@@ -71,10 +71,10 @@ class MusicPlayer;
  *   10 (0xa) CHOOSE_MYSTERY ??? `_DoChooseMystery` + `_CaseSelection`;
  *                    starts with _NextScreen=0 so a successful pick
  *                    falls through to INIT_CLUES.
- *   11 (0xb) TITLE  ??? set _NextScreen=8 then dispatch (TITLE.ANM is
- *                    actually shown earlier by `_DoOpeningAnims`, this
- *                    handler is the post-intro "fall into PROFILE"
- *                    redirect)
+ *   11 (0xb) TITLE  ??? floppy `_DoTitle_Floppy` plays TITLE.ANM, waits
+ *                    for input, then writes =8. CD shows TITLE.ANM in
+ *                    `_DoOpeningAnims`, so it usually never enters this
+ *                    handler.
  *   12 (0xc) ACTION ??? `_ActionScreen` @ 1c33:195b ??? post-mystery menu
  *                    ("Solve a Mystery", scrapbook, more mysteries,
  *                    setup). Action 1 sets =10 (CHOOSE_MYSTERY).
diff --git a/engines/eem/mystery.cpp b/engines/eem/mystery.cpp
index efbada6cab1..c6079dac3f7 100644
--- a/engines/eem/mystery.cpp
+++ b/engines/eem/mystery.cpp
@@ -48,6 +48,8 @@ void Mystery::clear() {
 	_floppySuspectsOff = _floppyHintBlockOff = _floppyNoteIndexOff = 0;
 	_floppyGalleryOff = _floppyTextOff = _floppyKDTextOff = 0;
 	_floppySolvedOff = 0;
+	_floppySiteAnimData.clear();
+	memset(_floppySiteAnimSiteOff, 0, sizeof(_floppySiteAnimSiteOff));
 	memset(_aChain, 0, sizeof(_aChain));
 	memset(_bChain, 0, sizeof(_bChain));
 	memset(_cChain, 0, sizeof(_cChain));
@@ -214,6 +216,7 @@ bool Mystery::load(uint num, Common::RandomSource *rng) {
 		_firstTry = true;
 		_searchLocationNumber = _siteNumber = 0xFFFF;
 		_lastSite = 0x1B;
+		loadFloppySiteAnimData();
 
 		debugC(1, kDebugMystery,
 			   "Mystery::load(%u) floppy: sites=0x%04x siteIdx=0x%04x "
@@ -310,6 +313,74 @@ const byte *Mystery::siteData(uint siteNum) const {
 	return _data.data() + dataOff;
 }
 
+const byte *Mystery::floppySiteAnimData(uint siteNum) const {
+	if (!_isFloppy || siteNum >= kVisitedSiteCap)
+		return nullptr;
+	const uint16 off = _floppySiteAnimSiteOff[siteNum];
+	if (off == 0 || off >= _floppySiteAnimData.size())
+		return nullptr;
+	return _floppySiteAnimData.data() + off;
+}
+
+void Mystery::loadFloppySiteAnimData() {
+	_floppySiteAnimData.clear();
+	memset(_floppySiteAnimSiteOff, 0, sizeof(_floppySiteAnimSiteOff));
+
+	Common::File f;
+	if (!f.open(Common::Path("ANI.BIN"))) {
+		warning("Mystery::loadFloppySiteAnimData: ANI.BIN missing");
+		return;
+	}
+
+	const int32 size = f.size();
+	if (size <= 0 || size > 0xFFFF) {
+		warning("Mystery::loadFloppySiteAnimData: invalid ANI.BIN size %d",
+				size);
+		return;
+	}
+
+	_floppySiteAnimData.resize((uint)size);
+	if (f.read(_floppySiteAnimData.data(), (uint32)size) != (uint32)size) {
+		warning("Mystery::loadFloppySiteAnimData: short ANI.BIN read");
+		_floppySiteAnimData.clear();
+		return;
+	}
+
+	const uint tableOff = _number * 2;
+	if (tableOff + 2 > _floppySiteAnimData.size())
+		return;
+	uint32 pos = READ_LE_UINT16(_floppySiteAnimData.data() + tableOff);
+	if (pos == 0 || pos >= _floppySiteAnimData.size())
+		return;
+
+	for (uint site = 0; site < _numSites && site < kVisitedSiteCap; site++) {
+		const uint32 start = pos;
+		if (pos + 1 > _floppySiteAnimData.size())
+			break;
+		const uint cycles = _floppySiteAnimData[pos++];
+		if (pos + cycles * 2 + 1 > _floppySiteAnimData.size()) {
+			warning("Mystery::loadFloppySiteAnimData: malformed cycles "
+					"for mystery %u site %u", _number, site);
+			break;
+		}
+		pos += cycles * 2;
+
+		const uint anims = _floppySiteAnimData[pos++];
+		if (pos + anims * 4 > _floppySiteAnimData.size()) {
+			warning("Mystery::loadFloppySiteAnimData: malformed anims "
+					"for mystery %u site %u", _number, site);
+			break;
+		}
+		_floppySiteAnimSiteOff[site] = (uint16)start;
+		pos += anims * 4;
+	}
+
+	debugC(1, kDebugMystery,
+		   "Mystery::loadFloppySiteAnimData(%u): base=0x%04x sites=%u",
+		   _number, READ_LE_UINT16(_floppySiteAnimData.data() + tableOff),
+		   _numSites);
+}
+
 const byte *Mystery::hotspots(uint siteNum) const {
 	if (_isFloppy) {
 		// Floppy: hotspot table sits inside the per-site sub-blob.
diff --git a/engines/eem/mystery.h b/engines/eem/mystery.h
index c9694d1965e..eed403b0bdb 100644
--- a/engines/eem/mystery.h
+++ b/engines/eem/mystery.h
@@ -131,6 +131,11 @@ public:
 	/// referenced by SiteIndex[@p siteNum].
 	const byte *siteData(uint siteNum) const;
 
+	/// Floppy-only pointer to the matching `ANI.BIN` per-site animation
+	/// block. Layout: u8 cycleCount, cycleCount ?? {u8 start, u8 end},
+	/// u8 animCount, animCount ?? {u8 animId, u16 x, u8 y}.
+	const byte *floppySiteAnimData(uint siteNum) const;
+
 	/// Pointer to the hotspot rectangle array for site @p siteNum.
 	/// Each rect is 14 bytes: x1, y1, x2, y2, then 6 bytes of clue data.
 	const byte *hotspots(uint siteNum) const;
@@ -236,8 +241,11 @@ private:
 	uint16 _floppyTextOff = 0;       ///< header[+0xc] ??? text block
 	uint16 _floppyKDTextOff = 0;     ///< header[+0x10] ??? KDTextIndex
 	uint16 _floppySolvedOff = 0;     ///< header[+0x12] ??? solved clue chain
+	Common::Array<byte> _floppySiteAnimData;
+	uint16 _floppySiteAnimSiteOff[kVisitedSiteCap] = {};
 
 	uint16 readU16(uint offset) const;
+	void loadFloppySiteAnimData();
 };
 
 } // End of namespace EEM
diff --git a/engines/eem/site.cpp b/engines/eem/site.cpp
index a5051688000..f48d44dc276 100644
--- a/engines/eem/site.cpp
+++ b/engines/eem/site.cpp
@@ -1083,7 +1083,7 @@ void SiteScreen::renderAnimatedDrops(uint siteNum, uint32 tickMs) {
 	//   per entry at siteData[+0x48 + i*6]: {animId, x, y}
 	//   animId == -1 ??? `_ColorCycle(x, y)` palette range (handled
 	//                  in the run() loop's frame pump as palette
-	//                  rotation; not yet implemented).
+	//                  rotation).
 	//   else ??? `_GetAnimation(animId)` + `_NewAnimation` then
 	//          `_UpdateAnimations @ 172b:09c1` walks a sequence
 	//          script (entries are frame indices; 0x80 = end-of-loop,
@@ -1092,6 +1092,49 @@ void SiteScreen::renderAnimatedDrops(uint siteNum, uint32 tickMs) {
 	//          raw animation frames in order using a global tick.
 	if (!_mystery)
 		return;
+
+	if (_vm && _vm->isFloppy()) {
+		// Floppy extra site anims live in `ANI.BIN`, not in the
+		// mystery's site_data. `_ReadMystery_Floppy` asks
+		// `_GetSiteAnimData_Floppy(mystery)` for the case block, then
+		// walks one entry per site:
+		//   u8 cycleCount, cycleCount ?? {u8 start, u8 end},
+		//   u8 animCount, animCount ?? {u8 animId, u16 x, u8 y}.
+		// `_DoSiteLoop_Floppy` registers at most four of these in local
+		// animation slots, so cap the draw loop the same way.
+		const byte *siteAnim = _mystery->floppySiteAnimData(siteNum);
+		if (!siteAnim)
+			return;
+		const uint cycles = siteAnim[0];
+		const byte *animList = siteAnim + 1 + cycles * 2;
+		const uint animCount = animList[0];
+		animList++;
+		if (animCount == 0)
+			return;
+
+		Graphics::Surface *screen = g_system->lockScreen();
+		if (!screen)
+			return;
+
+		const uint maxAnims = MIN<uint>(animCount, 4);
+		for (uint i = 0; i < maxAnims; i++) {
+			const byte *e = animList + i * 4;
+			const uint animId = e[0];
+			const int16 x = (int16)READ_LE_UINT16(e + 1);
+			const int16 y = (int16)e[3];
+			Animation anim;
+			if (!_vm->getAni().loadAnimation(animId, anim) || anim.empty())
+				continue;
+			const uint frameIdx = partnerFrameAtTick((uint16)animId,
+													  (uint)anim.size(),
+													  tickMs);
+			blitAnimFrameAnchored(screen, anim[frameIdx], x, y);
+		}
+
+		g_system->unlockScreen();
+		return;
+	}
+
 	const byte *site = _mystery->siteData(siteNum);
 	if (!site)
 		return;
@@ -1136,16 +1179,22 @@ void SiteScreen::scanColorCycles(uint siteNum) {
 	_colorCycles.clear();
 	if (!_mystery)
 		return;
-	// Floppy site data has a different layout (per `_DoSiteLoop_Floppy
-	// @ 1652:03a3`: site index is 2-byte u16 entries, site data starts
-	// with an offset to a sub-structure with picID + 5-byte hotspot
-	// entries ??? there's no `[+0xa]` anim count at the same place). The
-	// CD-shaped offsets read garbage and run past the buffer end. Until
-	// the floppy color-cycle layout is reverse-engineered, skip the
-	// scan: the floppy still does palette F9..FE rotation in its own
-	// driver, but our hotspot palette override works without it.
-	if (_vm && _vm->isFloppy())
+
+	if (_vm && _vm->isFloppy()) {
+		const byte *siteAnim = _mystery->floppySiteAnimData(siteNum);
+		if (!siteAnim)
+			return;
+		const uint cycles = siteAnim[0];
+		for (uint i = 0; i < cycles && _colorCycles.size() < 5; i++) {
+			ColorCycleRange r;
+			r.start = siteAnim[1 + i * 2];
+			r.end = siteAnim[1 + i * 2 + 1];
+			if (r.end > r.start)
+				_colorCycles.push_back(r);
+		}
 		return;
+	}
+
 	const byte *site = _mystery->siteData(siteNum);
 	if (!site)
 		return;
diff --git a/engines/eem/ui.cpp b/engines/eem/ui.cpp
index 986ee11a41f..eeb01b38ac1 100644
--- a/engines/eem/ui.cpp
+++ b/engines/eem/ui.cpp
@@ -134,6 +134,19 @@ bool gallerySlotAt(const Common::Array<Common::Rect> &rects,
 	return false;
 }
 
+const byte *advanceFloppyDialogRecords(const byte *rec, uint count,
+									   const byte *end) {
+	for (uint i = 0; i < count; i++) {
+		if (!rec || rec + 11 > end)
+			return nullptr;
+		const uint len = 11u + rec[10];
+		if (rec + len > end)
+			return nullptr;
+		rec += len;
+	}
+	return rec;
+}
+
 // Floppy gallery slot positions verified at `2608:0x16c` (5 ??
 // {u16 x, u16 y}) ??? read by `_DrawGallery_Floppy @ 154e:0045`'s
 // `[BX + 0x16c]` (x) and `[BX + 0x16e]` (y) loads. The floppy
@@ -5300,8 +5313,8 @@ void EEMEngine::doAccuseFloppy() {
 		//   1d40:08c0  _MIDIPlayFile("travel-2.xmi");
 		//   1d40:08d0  walk solved chain via _DisplayHotspotClue_Floppy
 		//                 + _WaitForClick per record. Mid-recap: when
-		//                 only 3 records remain, play TITLE.ANM(0)
-		//                 (transition graphic; not yet ported).
+		//                 only 3 records remain, play
+		//                 _PlayTitleANM_Floppy(0) = SCRAPBK.ANI.
 		//   1d40:0939  ((u16 *)0x3054)[mysteryNum] =
 		//                  _firstTry ? 2 : 1;
 		//   1d40:0941  tier-promotion check (advance _chainStage when
@@ -5381,11 +5394,34 @@ void EEMEngine::doAccuseFloppy() {
 
 		// Walk the solved-clue chain. Header[+0x12] points at a
 		// `count` byte followed by `count` dialog records (same layout
-		// as hotspot dialogs).
+		// as hotspot dialogs). When only three records remain, the
+		// original clears animation slots and calls
+		// `_PlayTitleANM_Floppy(0)`. The title helper's file table maps
+		// index 0 to `SCRAPBK.ANI` and index 1 to `TITLE.ANM`, so this
+		// is the same scrapbook flip transition used by the CD win flow,
+		// just inserted before the last three floppy recap records.
 		const byte *chain = _mystery.solvedClueBlock();
 		if (chain) {
 			const uint count = chain[0];
-			displayFloppyDialogRecords(chain + 1, count, 0);
+			const byte *records = chain + 1;
+			const byte *end = bufBase + mysSize;
+			if (count > 3) {
+				const uint beforeScrapbook = count - 3;
+				const byte *tail =
+					advanceFloppyDialogRecords(records, beforeScrapbook,
+											   end);
+				if (tail) {
+					displayFloppyDialogRecords(records, beforeScrapbook, 1);
+					playAnm(Common::Path("SCRAPBK.ANI"), 120,
+							/*holdLastFrame=*/false, /*fadeIn=*/true);
+					displayFloppyDialogRecords(tail, 3, 1);
+				} else {
+					warning("doAccuseFloppy: malformed solved chain");
+					displayFloppyDialogRecords(records, count, 1);
+				}
+			} else {
+				displayFloppyDialogRecords(records, count, 1);
+			}
 		}
 		if (_music && _voiceOn)
 			_music->stop();


Commit: 2e25ab2f46f3701803e1dbd02f20cf1fb3515b84
    https://github.com/scummvm/scummvm/commit/2e25ab2f46f3701803e1dbd02f20cf1fb3515b84
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:21:04+02:00

Commit Message:
EEM: corrected text size

Changed paths:
    engines/eem/clues.cpp
    engines/eem/font.cpp
    engines/eem/ui.cpp


diff --git a/engines/eem/clues.cpp b/engines/eem/clues.cpp
index 708754fbd11..2d72d4e45b1 100644
--- a/engines/eem/clues.cpp
+++ b/engines/eem/clues.cpp
@@ -1107,7 +1107,7 @@ void EEMEngine::displayFloppyDialogRecords(const byte *rec, uint count,
 			getBalloonInsets(balloonId, textXIns, textYIns, textWidth);
 		const int textX = ballX + textXIns;
 		const int balloonH = haveBalloon ? balloon.surface.h : 200;
-		const int lineH    = _font.getFontHeight() + 1;
+		const int lineH    = _font.getFontHeight();
 
 		// Pagination state ??? `FUN_22dc_05c8`'s text-idx loop uses
 		// `local_1c` (set from the PREVIOUS text's flag bit) to decide
diff --git a/engines/eem/font.cpp b/engines/eem/font.cpp
index c8c42360550..36d584c62b5 100644
--- a/engines/eem/font.cpp
+++ b/engines/eem/font.cpp
@@ -130,7 +130,7 @@ int EEMFont::drawWordWrapped(Graphics::ManagedSurface *dst, int x, int y,
 							 uint32 color) const {
 	Common::Array<Common::String> lines;
 	wordWrapText(s, width, lines);
-	const int lineH = getFontHeight() + 1;
+	const int lineH = getFontHeight();
 	for (uint i = 0; i < lines.size(); i++)
 		drawString(dst, lines[i], x, y + (int)i * lineH, width, color);
 	return (int)lines.size() * lineH;
diff --git a/engines/eem/ui.cpp b/engines/eem/ui.cpp
index eeb01b38ac1..61854c997bf 100644
--- a/engines/eem/ui.cpp
+++ b/engines/eem/ui.cpp
@@ -2511,7 +2511,7 @@ void EEMEngine::drawNotebookFrame(int &page) {
 						   _playerName, _partner);
 	};
 	{
-		const int lineH = _font.getFontHeight() + 1;
+		const int lineH = _font.getFontHeight();
 		int y = kRectY;
 		while (clueCursor < (int)found.size()) {
 			const uint clueId = found[clueCursor];
@@ -2559,7 +2559,7 @@ void EEMEngine::drawNotebookFrame(int &page) {
 		// matches the original design.
 		Common::Array<Common::String> wrapped;
 		_font.wordWrapText(txt, kRectW, wrapped);
-		const int lineH = _font.getFontHeight() + 1;
+		const int lineH = _font.getFontHeight();
 		const int h = (int)wrapped.size() * lineH;
 		const byte color = _mystery._noteSelected[clueId] ? 0x3C : 0x5C;
 		for (uint li = 0; li < wrapped.size(); li++) {
@@ -2833,7 +2833,7 @@ void EEMEngine::doGallery() {
 						const byte *ni = _mystery.noteIndex();
 						const uint16 niCount = _mystery.noteIndexCount();
 						int yPos = ry;
-						const int lineH = _font.getFontHeight() + 1;
+						const int lineH = _font.getFontHeight();
 						bool drewAny = false;
 						const uint clueMax = floppyMI ? clueCount : 30u;
 						for (uint k = 0; k < clueCount && k < clueMax; k++) {
@@ -3832,7 +3832,7 @@ bool EEMEngine::doAccuseNotes() {
 	auto rebuildPagination = [&]() {
 		numPages = 1;
 		pageBreaks[0] = 0;
-		const int lineH = _font.getFontHeight() + 1;
+		const int lineH = _font.getFontHeight();
 		int y = rectY;
 		for (uint i = 0; i < found.size(); i++) {
 			const uint clueId = found[i];
@@ -3888,7 +3888,7 @@ bool EEMEngine::doAccuseNotes() {
 		// "accuse-mode" look together with PIC 0x1A7.
 		slotRects.clear();
 		slotClues.clear();
-		const int lineH = _font.getFontHeight() + 1;
+		const int lineH = _font.getFontHeight();
 		const int startIdx = pageBreaks[page];
 		const int endIdx   = (page + 1 < numPages)
 			? pageBreaks[page + 1]


Commit: e84bf0e7981aeae007259442c6d73c90f4297919
    https://github.com/scummvm/scummvm/commit/e84bf0e7981aeae007259442c6d73c90f4297919
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:21:04+02:00

Commit Message:
EEM: keep proper track of clues in floppy version

Changed paths:
    engines/eem/mystery.cpp
    engines/eem/mystery.h
    engines/eem/ui.cpp


diff --git a/engines/eem/mystery.cpp b/engines/eem/mystery.cpp
index c6079dac3f7..5982e5c5fc5 100644
--- a/engines/eem/mystery.cpp
+++ b/engines/eem/mystery.cpp
@@ -464,6 +464,23 @@ uint16 Mystery::noteIndexCount() const {
 	return (uint16)((_galleryOffset - _noteOffset) / stride);
 }
 
+bool Mystery::noteHasNotebookText(uint clueId) const {
+	const byte *ni = noteIndex();
+	const uint16 cnt = noteIndexCount();
+	if (!ni || clueId >= cnt)
+		return false;
+
+	if (!_isFloppy)
+		return true;
+
+	// `_DrawNotes_Floppy @ 15e0:01e8` first checks `_TextSeen[idx]`,
+	// then skips the row when `*(u16 *)(notes + idx * 7) == 0`.
+	// Many floppy dialog records are spoken-only lines: they must still
+	// be marked seen so site dialog does not repeat, but they are not
+	// notebook clues and should not render fallback "note N" labels.
+	return READ_LE_UINT16(ni + clueId * 7) != 0;
+}
+
 const byte *Mystery::kdTextIndex() const {
 	if (!isLoaded() || _kdTextOffset >= _data.size())
 		return nullptr;
diff --git a/engines/eem/mystery.h b/engines/eem/mystery.h
index eed403b0bdb..8ef1c584715 100644
--- a/engines/eem/mystery.h
+++ b/engines/eem/mystery.h
@@ -104,6 +104,12 @@ public:
 	/// Number of entries in NoteIndex.
 	uint16 noteIndexCount() const;
 
+	/// True when @p clueId has a visible notebook/accuse text entry.
+	/// Floppy dialog text indices may be spoken-only records with a
+	/// zero notebook text offset; those are marked seen but skipped by
+	/// `_DrawNotes_Floppy`.
+	bool noteHasNotebookText(uint clueId) const;
+
 	/// Pointer to the KDTextIndex; first u16s are TextBlock offsets for
 	/// host hint lines.
 	const byte *kdTextIndex() const;
diff --git a/engines/eem/ui.cpp b/engines/eem/ui.cpp
index 61854c997bf..2493117b0a2 100644
--- a/engines/eem/ui.cpp
+++ b/engines/eem/ui.cpp
@@ -2455,7 +2455,7 @@ void EEMEngine::drawNotebookFrame(int &page) {
 	// original's iteration through `_CluesFound[]`.
 	Common::Array<uint> found;
 	for (uint i = 0; i < Mystery::kCluesFoundCap; i++) {
-		if (_mystery._cluesFound[i])
+		if (_mystery._cluesFound[i] && _mystery.noteHasNotebookText(i))
 			found.push_back(i);
 	}
 	const byte *ni = _mystery.noteIndex();
@@ -3762,7 +3762,7 @@ bool EEMEngine::doAccuseNotes() {
 	// the same way).
 	Common::Array<uint> found;
 	for (uint i = 0; i < niCount && i < Mystery::kCluesFoundCap; i++) {
-		if (_mystery._cluesFound[i])
+		if (_mystery._cluesFound[i] && _mystery.noteHasNotebookText(i))
 			found.push_back(i);
 	}
 


Commit: 821f9a0ba79a98e7a24744164a5e6dc3503f59d5
    https://github.com/scummvm/scummvm/commit/821f9a0ba79a98e7a24744164a5e6dc3503f59d5
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:21:04+02:00

Commit Message:
EEM: keep proper track of visited places in floppy version

Changed paths:
    engines/eem/ui.cpp


diff --git a/engines/eem/ui.cpp b/engines/eem/ui.cpp
index 2493117b0a2..fed7083fb99 100644
--- a/engines/eem/ui.cpp
+++ b/engines/eem/ui.cpp
@@ -55,6 +55,43 @@ const GallerySlot kGallerySlots[5] = {
 	{ 191,  90 }  // 4
 };
 
+byte mapVisitedMarkerColor(byte color) {
+	switch (color) {
+	case 0xf7:
+	case 0xfb:
+	case 0xfd:
+		return 0x1b;
+	case 0xf8:
+	case 0xf9:
+	case 0xfa:
+	case 0xfc:
+	case 0xfe:
+		return 0x19;
+	default:
+		return color;
+	}
+}
+
+void blitBigMapMarker(Graphics::ManagedSurface &dstSurface, const Picture &marker,
+					  int x, int y, bool useVisitedColors) {
+	const byte transp = (byte)(marker.flags >> 8);
+	for (int row = 0; row < marker.surface.h; row++) {
+		const int dstY = y + row;
+		if (dstY < 0 || dstY >= dstSurface.h)
+			continue;
+		const byte *src = (const byte *)marker.surface.getBasePtr(0, row);
+		byte *dst = (byte *)dstSurface.getBasePtr(0, dstY);
+		for (int col = 0; col < marker.surface.w; col++) {
+			const int dstX = x + col;
+			if (dstX < 0 || dstX >= dstSurface.w)
+				continue;
+			if (src[col] != transp)
+				dst[dstX] = useVisitedColors ? mapVisitedMarkerColor(src[col])
+											  : src[col];
+		}
+	}
+}
+
 constexpr Common::Rect kEndingPrevPageRect(Common::Point(0, 0), 28, 200);
 constexpr Common::Rect kEndingNextPageRect(Common::Point(292, 0), 28, 200);
 constexpr uint16 kFloppyEndingBackgroundPic = 0x8b;
@@ -3499,6 +3536,9 @@ void EEMEngine::drawBigMapOverview(uint32 elapsedMs) {
 	//   _DoneMarker  = PIC 0x20d  (already-searched site)
 	//   _SiteMarker  = PIC 0xc5   (default available site)
 	//   _CrimeMarker = PIC 0xc6   (crime-scene flag set)
+	// Floppy's `PICS.DBX` has 524 entries, so PIC 0x20d is CD-only.
+	// Its pixels are the same 7x8 marker as PIC 0xc5, with the animated
+	// 0xfb/0xfc interior remapped to static 0x1b/0x19 blue.
 	Picture done;
 	Picture normal;
 	Picture crimeM;
@@ -3530,30 +3570,20 @@ void EEMEngine::drawBigMapOverview(uint32 elapsedMs) {
 							  && _mystery._visitedSite[i];
 
 		const Picture *m = nullptr;
+		bool useVisitedColors = false;
 		if (done_ && haveDone)
 			m = &done;
-		else if (crime != 0 && haveCrime)
+		else if (done_ && haveNormal) {
+			m = &normal;
+			useVisitedColors = true;
+		} else if (crime != 0 && haveCrime)
 			m = &crimeM;
 		else if (haveNormal)
 			m = &normal;
 
 		if (m) {
-			// Masked-blit the marker PIC.
-			const byte transp = (byte)(m->flags >> 8);
-			for (int row = 0; row < m->surface.h; row++) {
-				const int dstY = (int)my + row;
-				if (dstY < 0 || dstY >= 200)
-					continue;
-				const byte *src = (const byte *)m->surface.getBasePtr(0, row);
-				byte *dst = (byte *)scratch.getBasePtr(0, dstY);
-				for (int col = 0; col < m->surface.w; col++) {
-					const int dstX = (int)mx + col;
-					if (dstX < 0 || dstX >= 320)
-						continue;
-					if (src[col] != transp)
-						dst[dstX] = src[col];
-				}
-			}
+			blitBigMapMarker(scratch, *m, (int)mx, (int)my,
+							  useVisitedColors);
 		} else {
 			// Fallback if the markers couldn't be loaded.
 			const Common::Rect mark(mx - 3, my - 3, mx + 4, my + 4);


Commit: f00c88734cb88901f496fb46d4c0a281939b6d73
    https://github.com/scummvm/scummvm/commit/f00c88734cb88901f496fb46d4c0a281939b6d73
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:21:05+02:00

Commit Message:
EEM: blit accuse partner in floppy version

Changed paths:
    engines/eem/ui.cpp


diff --git a/engines/eem/ui.cpp b/engines/eem/ui.cpp
index fed7083fb99..88aa3939004 100644
--- a/engines/eem/ui.cpp
+++ b/engines/eem/ui.cpp
@@ -92,6 +92,21 @@ void blitBigMapMarker(Graphics::ManagedSurface &dstSurface, const Picture &marke
 	}
 }
 
+void blitAccusePartner(Graphics::ManagedSurface &dstSurface,
+					   DBDArchive &aniArchive, uint8 partner,
+					   uint32 tickMs) {
+	const uint partnerAnim = (partner == 0) ? 2 : 0x10;
+	Animation partnerAni;
+	if (aniArchive.loadAnimation(partnerAnim, partnerAni) &&
+		!partnerAni.empty()) {
+		const uint frameIdx = partnerFrameAtTick(0x02,
+												 (uint)partnerAni.size(),
+												 tickMs);
+		blitAnimFrameAnchored(dstSurface.surfacePtr(),
+							  partnerAni[frameIdx], 5, 0x50);
+	}
+}
+
 constexpr Common::Rect kEndingPrevPageRect(Common::Point(0, 0), 28, 200);
 constexpr Common::Rect kEndingNextPageRect(Common::Point(292, 0), 28, 200);
 constexpr uint16 kFloppyEndingBackgroundPic = 0x8b;
@@ -5654,6 +5669,11 @@ void EEMEngine::doAccuseFloppy() {
 		_font.drawWordWrapped(&scene, balloonX + tx, balloonY + ty,
 							  MAX<int>(8, (int)tw), alibi, 0);
 	}
+	// The floppy original keeps the accuse partner animation slot alive
+	// across `_DisplayAlibi_Floppy` and restores the screen with active
+	// animations between alibi phases. Stamp the same resting frame here
+	// before the KD reaction helper snapshots the current screen.
+	blitAccusePartner(scene, _aniArchive, _partner, g_system->getMillis());
 	g_system->copyRectToScreen(scene.getPixels(), scene.pitch, 0, 0, 320, 200);
 	g_system->updateScreen();
 


Commit: f2d1372a85de0c8056c30a4a0fb5e97f7aedb432
    https://github.com/scummvm/scummvm/commit/f2d1372a85de0c8056c30a4a0fb5e97f7aedb432
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:21:05+02:00

Commit Message:
EEM: use the correct initial menu screens

Changed paths:
    engines/eem/eem.cpp
    engines/eem/eem.h
    engines/eem/ui.cpp


diff --git a/engines/eem/eem.cpp b/engines/eem/eem.cpp
index 12596f8fe42..da33f550739 100644
--- a/engines/eem/eem.cpp
+++ b/engines/eem/eem.cpp
@@ -68,7 +68,7 @@ const bool kDebugPopulateScrapbook1AtStartup = false;
 // Fallback 11x16 mouse cursor used if the selected PIC pointer cannot be
 // loaded. The original game sets the cursor visible/hidden via
 // _MouseCursor; we leave it on once the screens that need it
-// (ChoosePartner, CaseSelection, sites) are reached.
+// (ChoosePartner, ActionScreen, CaseSelection, sites) are reached.
 //   0 = transparent, 1 = black outline, 2 = white fill
 const byte kCursorBitmap[11 * 16] = {
 	1,1,0,0,0,0,0,0,0,0,0,
@@ -276,10 +276,8 @@ Common::Error EEMEngine::run() {
 	//
 	//   * Save HAS a mystery in progress ??? resume at MAP (mirrors the
 	//     original's post-briefing state, handler 0 at 1a35:0e1d).
-	//   * Save has NO mystery ??? drop into the case-selection screen
-	//     (`kScreenChooseMystery`) so the player can pick which case
-	//     to play. This matches what the original `_ActionScreen`
-	//     leads to ??? without the redundant action menu in front.
+	//   * Save has NO mystery ??? drop into `_ActionScreen`, same as the
+	//     original after partner selection.
 	const int wantedSave = ConfMan.hasKey("save_slot")
 		? ConfMan.getInt("save_slot") : -1;
 	bool resumed = false;
@@ -297,8 +295,8 @@ Common::Error EEMEngine::run() {
 			} else {
 				debugC(1, kDebugGeneral,
 					   "Resuming profile from slot %d (no mystery ??? "
-					   "??? case selection)", wantedSave);
-				_nextScreen = kScreenChooseMystery;
+					   "??? action screen)", wantedSave);
+				_nextScreen = kScreenAction;
 			}
 			resumed = true;
 		}
@@ -429,7 +427,8 @@ Common::Error EEMEngine::run() {
 
 	// After the title chain, the original goes Title (B) -> screen 8
 	// (NewPlayer / saved-record selection) -> screen 9 (ChoosePartner) ->
-	// screen A (CaseSelection) -> site loop. We mirror the same order.
+	// screen C (ActionScreen). Choosing a mystery there enters screen A
+	// (CaseSelection) and then the site loop.
 	// Mouse stays hidden through the opening anims; show it now for
 	// the interactive screens (matches `_MouseCursor = 1` at the tail
 	// of `_NewPlayer`).
@@ -463,17 +462,9 @@ Common::Error EEMEngine::run() {
 	//
 	// `_DoChoosePartner @ 1a35:099d` sets `_NextScreen = 0xc` (= the
 	// original `_ActionScreen` ??? "Choose A Mystery / Practice Mystery /
-	// See ScrapBook 1..3"). In our port that menu lives inside
-	// `doCaseSelection`, which already mirrors `_ActionScreen`'s 5-entry
-	// list (verified against `ActionNames @ 29be:0d6a`) and rolls the
-	// individual-mystery picker (`_CaseSelection @ 1c33:0a87`) into the
-	// same flow. So we route straight to `kScreenChooseMystery`; the
-	// `kScreenAction` (= 0xc) state still exists for the post-win path
-	// (`_DisplayCorrect @ 1df2:0895` writes 0xc) but its handler also
-	// dispatches `doCaseSelection`. The previous `doActionScreen` was a
-	// synthetic stub ("What now?" / "Solve a Mystery" / "Look at My
-	// Books") whose strings are nowhere in the binary ??? confirmed by a
-	// `search_strings` for "What" returning zero matches.
+	// See ScrapBook 1..3"). That screen is separate from handler 10's
+	// `_DoChooseMystery` / `_CaseSelection`, which is where the "Book N"
+	// title is drawn.
 	//
 	// Mid-mystery profile resume: if the profile picker loaded a
 	// save whose `hasMystery` flag was set, `_mystery.isLoaded()` is
@@ -485,7 +476,7 @@ Common::Error EEMEngine::run() {
 	// profile-level state via `_PlayerRecord`, not in-progress
 	// mysteries ??? so this is a ScummVM-only ergonomics improvement.
 	if (!shouldQuit() && !resumed)
-		_nextScreen = _mystery.isLoaded() ? kScreenMap : kScreenChooseMystery;
+		_nextScreen = _mystery.isLoaded() ? kScreenMap : kScreenAction;
 screen_loop:
 	while (!shouldQuit() && _nextScreen != kScreenInvalid) {
 		const ScreenId current = (ScreenId)_nextScreen;
@@ -509,28 +500,23 @@ screen_loop:
 			break;
 
 		case kScreenAction:
-			// Post-mystery menu. The original's `_ActionScreen @
-			// 1c33:195b` shows the 5-entry "Choose A Mystery /
-			// Practice / ScrapBook" picker; `doCaseSelection` is
-			// our port of that exact menu (plus the individual-case
-			// sub-picker the original handles in `_CaseSelection @
-			// 1c33:0a87`). After the player picks, fall through to
-			// the same routing the `kScreenChooseMystery` case uses.
-			// Reachable from `_DisplayCorrect`'s 0xc write after a
-			// solve (see `ui.cpp` `_nextScreen = kScreenAction`).
+			// Top-level post-profile / post-mystery menu. `_ActionScreen
+			// @ 1c33:195b` shows the 5-entry "Choose A Mystery /
+			// Practice / ScrapBook" picker and writes screen 0xa only
+			// when the player picks "Choose A Mystery".
 			_nextScreen = kScreenInvalid;
-			doCaseSelection();
-			if (_mystery.isLoaded())
+			doActionScreen();
+			if (_nextScreen == kScreenInvalid && _mystery.isLoaded())
 				_nextScreen = kScreenInitClues;
 			break;
 
 		case kScreenChooseMystery:
 			// Handler 10 at 1a35:0e0e calls `_DoChooseMystery` which
 			// presets `_NextScreen = 0` (INIT_CLUES) before
-			// `_CaseSelection`. Same dispatch as `kScreenAction`.
+			// `_CaseSelection`.
 			_nextScreen = kScreenInvalid;
 			doCaseSelection();
-			if (_mystery.isLoaded())
+			if (_nextScreen == kScreenInvalid && _mystery.isLoaded())
 				_nextScreen = kScreenInitClues;
 			break;
 
@@ -539,7 +525,7 @@ screen_loop:
 			// then writes `_NextScreen = 1` (MAP).
 			doInitClues();
 			_nextScreen = _mystery.isLoaded() ? kScreenMap
-											  : kScreenChooseMystery;
+											  : kScreenAction;
 			break;
 
 		case kScreenMap:
@@ -553,7 +539,7 @@ screen_loop:
 			// the natural next state is SITE.
 			doBigMap();
 			if (!_mystery.isLoaded())
-				_nextScreen = kScreenChooseMystery;
+				_nextScreen = kScreenAction;
 			else if (_nextScreen == current)
 				_nextScreen = kScreenSite;
 			break;
@@ -565,7 +551,7 @@ screen_loop:
 			// than entering those screens as nested modals.
 			doSiteLoop();
 			if (!_mystery.isLoaded())
-				_nextScreen = kScreenChooseMystery;
+				_nextScreen = kScreenAction;
 			else if (_nextScreen == current)
 				_nextScreen = kScreenInvalid;  // user quit
 			break;
@@ -576,7 +562,7 @@ screen_loop:
 			// (accuse) and then returns to this dispatcher.
 			doNotebook();
 			if (!_mystery.isLoaded() && _nextScreen != kScreenAction)
-				_nextScreen = kScreenChooseMystery;
+				_nextScreen = kScreenAction;
 			else if (_nextScreen == current)
 				_nextScreen = kScreenSite;
 			break;
@@ -587,7 +573,7 @@ screen_loop:
 			// 2, and the PDA button writes 4.
 			doGallery();
 			if (!_mystery.isLoaded() && _nextScreen != kScreenAction)
-				_nextScreen = kScreenChooseMystery;
+				_nextScreen = kScreenAction;
 			else if (_nextScreen == current)
 				_nextScreen = kScreenSite;
 			break;
@@ -620,7 +606,7 @@ screen_loop:
 				doChoosePartner();
 			if (!shouldQuit())
 				_nextScreen = _mystery.isLoaded() ? kScreenMap
-												  : kScreenChooseMystery;
+												  : kScreenAction;
 			break;
 
 		case kScreenAccuse:
@@ -629,7 +615,7 @@ screen_loop:
 			// 0xc (ACTION).
 			doAccuse();
 			if (!_mystery.isLoaded() && _nextScreen != kScreenAction)
-				_nextScreen = kScreenChooseMystery;
+				_nextScreen = kScreenAction;
 			else if (_nextScreen == current)
 				_nextScreen = kScreenSite;
 			break;
diff --git a/engines/eem/eem.h b/engines/eem/eem.h
index 44c5bc4a905..bc63f993db1 100644
--- a/engines/eem/eem.h
+++ b/engines/eem/eem.h
@@ -75,9 +75,9 @@ class MusicPlayer;
  *                    for input, then writes =8. CD shows TITLE.ANM in
  *                    `_DoOpeningAnims`, so it usually never enters this
  *                    handler.
- *   12 (0xc) ACTION ??? `_ActionScreen` @ 1c33:195b ??? post-mystery menu
- *                    ("Solve a Mystery", scrapbook, more mysteries,
- *                    setup). Action 1 sets =10 (CHOOSE_MYSTERY).
+ *   12 (0xc) ACTION ??? `_ActionScreen` @ 1c33:195b ??? Choose A Mystery /
+ *                    Practice Mystery / See ScrapBook 1..3. Action 1
+ *                    sets =10 (CHOOSE_MYSTERY).
  *   0xFFFF SENTINEL ??? exit loop
  *
  * Screen-driver state writes verified via xrefs to `_NextScreen @
@@ -451,6 +451,7 @@ private:
 	/// action-menu "See ScrapBook 1/2/3" entries.
 	void doShowScrapbook(uint stage);
 
+	void doActionScreen();
 	void doCaseSelection();
 	void doSiteLoop();
 
diff --git a/engines/eem/ui.cpp b/engines/eem/ui.cpp
index 88aa3939004..d7986c80c89 100644
--- a/engines/eem/ui.cpp
+++ b/engines/eem/ui.cpp
@@ -147,6 +147,13 @@ constexpr int kNameEntryPeekY = 0xb3;
 constexpr uint16 kCaseSelectionRevealPic = 0x53;
 constexpr int kCaseSelectionRevealX = 0x3e;
 constexpr int kCaseSelectionRevealY = 0xb2;
+constexpr uint16 kActionScreenBackgroundPic = 0x104;
+constexpr uint16 kActionScreenDecorPic = 0x0009;
+constexpr int kActionScreenDecorX = 10;
+constexpr int kActionScreenDecorY = 0x87;
+constexpr byte kChooserCycleStart = 0x6f;
+constexpr byte kChooserCycleEnd = 0x73;
+constexpr uint32 kChooserCycleMillis = 100;
 
 bool notebookButtonAt(int x, int y) {
 	return kPdaHelpRect.contains(x, y) ||
@@ -244,6 +251,13 @@ void copyToScreen(Graphics::ManagedSurface &scratch) {
 	g_system->updateScreen();
 }
 
+void cycleChooserPalette() {
+	// `_DoChoose` / `_DoListPicker_Floppy` rotate 0x6f..0x73 on each
+	// frame tick while waiting for input. These colors are used by the
+	// animated chooser surface baked into the menu backgrounds.
+	cyclePaletteRange(kChooserCycleStart, kChooserCycleEnd);
+}
+
 void blitMaskedPicSlice(Graphics::ManagedSurface &dst, const Picture &pic,
 						int srcX, int srcY, int w, int h,
 						int dstX, int dstY) {
@@ -431,25 +445,19 @@ void drawProfilePickerFrame(const ProfilePickerView &v) {
 	copyToScreen(scratch);
 }
 
-// Snapshot of `doCaseSelection`'s captured locals, used by
-// `drawCaseSelectionFrame` (which replaces the original lambda). Lives
-// on the stack inside `doCaseSelection`; never escapes.
-struct CaseSelectionView {
+// Snapshot of `doActionScreen`'s captured locals, used by
+// `drawActionMenuFrame`. Lives on the stack inside `doActionScreen`;
+// never escapes.
+struct ActionMenuView {
 	EEMEngine *vm;
-	const Picture *caseBg;
-	bool haveCaseBg;
-	const Picture *revealPic;
-	bool haveRevealPic;
-	const Animation *kdAnim;
-	bool haveKdAnim;
-	uint16 kdAnimId;     ///< 0x15 / 0x16 ??? looked up in kAnimScripts
-	int kdAnimX;
-	int kdAnimY;
+	const Picture *bg;
+	bool haveBg;
+	const Picture *decor;
+	bool haveDecor;
 	const char *separator;
 	const char *const *pickLabel;
 	const bool *pickEnabled;
 	uint pick;
-	uint book;
 };
 
 // Mystery list shown in the "Choose A Mystery" sub-screen. Mirrors
@@ -486,6 +494,16 @@ Common::StringArray loadBookNames(uint book) {
 	return names;
 }
 
+void clampCaseTopRow(uint &topRow, uint listLen, uint visibleRows) {
+	if (listLen <= visibleRows) {
+		topRow = 0;
+		return;
+	}
+	const uint maxTop = listLen - visibleRows;
+	if (topRow > maxTop)
+		topRow = maxTop;
+}
+
 // Per-mystery sub-chooser ("Choose A Mystery") view.
 //
 // `names` are the entries from BOOK%d.NME (in display order ??? index 0
@@ -645,12 +663,15 @@ void drawCaseSubmenu(const CaseSubmenuView &v) {
 	copyToScreen(scratch);
 }
 
-void drawCaseSelectionFrame(const CaseSelectionView &v) {
+void drawActionMenuFrame(const ActionMenuView &v) {
 	Graphics::ManagedSurface scratch(320, 200,
 		Graphics::PixelFormat::createFormatCLUT8());
-	drawCaseBase(scratch, v.vm, v.caseBg, v.haveCaseBg,
-				 v.revealPic, v.haveRevealPic,
-				 v.kdAnim, v.haveKdAnim, v.kdAnimX, v.kdAnimY, v.book);
+	scratch.clear();
+	if (v.haveBg && v.bg)
+		scratch.simpleBlitFrom(v.bg->surface);
+	if (v.haveDecor && v.decor)
+		blitMaskedPic(scratch, *v.decor,
+					   kActionScreenDecorX, kActionScreenDecorY);
 
 	if (v.vm->getFont().isLoaded()) {
 		// `DrawList` @ 1c33:040d coordinates: `_TextBox + 3` for x
@@ -763,6 +784,7 @@ void EEMEngine::doProfilePicker() {
 								   haveReveal ? &reveal : nullptr))
 		return;
 	drawProfilePickerFrame(view);
+	uint32 chooserLastTick = g_system->getMillis();
 
 	while (!done && !shouldQuit()) {
 		Common::Event ev;
@@ -875,6 +897,11 @@ void EEMEngine::doProfilePicker() {
 			view.start = start;
 			drawProfilePickerFrame(view);
 		}
+		const uint32 now = g_system->getMillis();
+		if (now - chooserLastTick >= kChooserCycleMillis) {
+			chooserLastTick = now;
+			cycleChooserPalette();
+		}
 		g_system->updateScreen();
 		g_system->delayMillis(15);
 	}
@@ -1738,21 +1765,18 @@ void EEMEngine::doSetup() {
 	}
 }
 
-void EEMEngine::doCaseSelection() {
-	// Mirrors `_CaseSelection` @ 1c33:0a87. The original draws PIC 0x41
-	// (chooser background) plus a centred "Book %d" / "Challenge Book"
-	// header at (y=12) and then calls `_DoChoose(list)` to render the
-	// menu via `DrawList` @ 1c33:040d at (_TextBox+3, DAT_29be_0d02) =
-	// (61, 35), 12 rows ?? 10 px line height. The menu list itself is
-	// the static array at 29be:0d6a (verified via `push 0x0d6a` at
-	// 1c33:1ab4). Strings are at 29be:0ef4 onwards. Layout:
+void EEMEngine::doActionScreen() {
+	// Mirrors `_ActionScreen` @ 1c33:195b. The original draws background
+	// PIC 0x104 plus PIC 9 at (10, 0x87), then calls `_DoChoose` with
+	// `ActionNames @ 29be:0d6a`. The "Book N" heading belongs only to
+	// `_CaseSelection`, so this top-level menu intentionally does not
+	// draw one.
+	// Layout:
 	//   list[0]  = "----------------------------------"
 	//   list[1]  = "         Choose A Mystery"
 	//   list[2..10] = alternating menu items + separators
 	// Five selectable items: Choose A Mystery / Practice Mystery /
 	// See ScrapBook 1/2/3.
-	const uint kMaxMystery = 54;
-
 	enum MenuPick {
 		kPickChoose = 0,
 		kPickPractice,
@@ -1828,8 +1852,6 @@ void EEMEngine::doCaseSelection() {
 	const Common::Rect kOkRect      ( 12,  63,  41,  87); // 29be:0cd8 confirm
 	const Common::Rect kHelpRect    ( 12, 100,  41, 124); // 29be:0ce0 help
 	const Common::Rect kExitRect    ( 12, 137,  41, 161); // 29be:0ce8 cancel
-	const Common::Rect kUpArrowRect (240,  31, 250,  43); // 29be:0cf0 scroll up
-	const Common::Rect kDnArrowRect (240, 148, 250, 159); // 29be:0cf8 scroll dn
 	const Common::Rect kListRect    ( 58,  35, 238, 158); // 29be:0d00 list panel
 
 	// The original `_NewPlayer` set `_MouseCursor = 1` on exit; the
@@ -1837,7 +1859,7 @@ void EEMEngine::doCaseSelection() {
 	// Reassert here in case anything between hid it.
 	CursorMan.showMouse(true);
 
-	// Reassert site palette 0 (the case-selection / chooser CLUT). In
+	// Reassert site palette 0 (the chooser CLUT). In
 	// the normal flow `doProfilePicker` (or the post-screen reset paths
 	// at lines 1402 / 1147 / 1121) leaves us on palette 0 already, but
 	// the launcher-resume path jumps straight here from `_AllBlack`
@@ -1845,65 +1867,29 @@ void EEMEngine::doCaseSelection() {
 	// CLUT and the player sees an empty screen.
 	setSitePalette(0);
 
-	// Mirrors `_CaseSelection`: load PIC 0x41 as the chooser backdrop.
-	Picture caseBg;
-	const bool haveCaseBg = _picsArchive.getPicture(0x41, caseBg);
-	Picture revealPic;
-	const bool haveRevealPic =
-		_picsArchive.getPicture(kCaseSelectionRevealPic, revealPic);
-
-	// KD greeter sprite. `_CaseSelection @ 1c33:0a87` (1c33:0b7e-0ba1)
-	// loads anim 0x15 (Jake-paired) or 0x16 (Jenny-paired) and registers
-	// `_NewAnimation(0x112, 0x50, ..., seqnum=0x15, prior=1)` ??? partner-
-	// dependent because the host KD changes who's "with him" on the
-	// briefing intro frame. Runs continuously through the menu loop via
-	// `_UpdateAnimations`. We approximate with millis-based frame cycling.
-	const uint kKdAniId = (_partner == 0) ? 0x15 : 0x16;
-	Animation kdAnim;
-	const bool haveKdAnim = _aniArchive.loadAnimation(kKdAniId, kdAnim)
-							 && !kdAnim.empty();
-	const int kKdAnimX = 0x112;
-	const int kKdAnimY = 0x50;
-	const uint caseBook = (_chainStage == 3) ? 3 :
-						  (_chainStage == 2) ? 2 : 1;
+	Picture bg;
+	const bool haveBg = _picsArchive.getPicture(kActionScreenBackgroundPic, bg);
+	Picture decor;
+	const bool haveDecor = _picsArchive.getPicture(kActionScreenDecorPic, decor);
 
-	CaseSelectionView v;
+	ActionMenuView v;
 	v.vm = this;
-	v.caseBg = &caseBg;
-	v.haveCaseBg = haveCaseBg;
-	v.revealPic = &revealPic;
-	v.haveRevealPic = haveRevealPic;
-	v.kdAnim = &kdAnim;
-	v.haveKdAnim = haveKdAnim;
-	v.kdAnimId = (uint16)kKdAniId;
-	v.kdAnimX = kKdAnimX;
-	v.kdAnimY = kKdAnimY;
+	v.bg = &bg;
+	v.haveBg = haveBg;
+	v.decor = &decor;
+	v.haveDecor = haveDecor;
 	v.separator = kSeparator;
 	v.pickLabel = kPickLabel;
 	v.pickEnabled = kPickEnabled;
 	v.pick = pick;
-	v.book = caseBook;
 
-	if (animateCaseSelectionReveal(this, &caseBg, haveCaseBg,
-								   &revealPic, haveRevealPic,
-								   &kdAnim, haveKdAnim,
-								   kKdAnimX, kKdAnimY, caseBook))
-		return;
-	drawCaseSelectionFrame(v);
-	uint32 lastTick = g_system->getMillis();
+	drawActionMenuFrame(v);
+	uint32 chooserLastTick = g_system->getMillis();
 
 	bool exitChosen = false;
 	while (!shouldQuit()) {
 		Common::Event ev;
 		bool confirmed = false;
-		// Redraw every 100 ms so the KD greeter cycles. Mirrors the
-		// `_CheckFrameRate` cadence in `_CaseSelection`'s main loop.
-		const uint32 now = g_system->getMillis();
-		if (haveKdAnim && now - lastTick >= 100) {
-			lastTick = now;
-			v.pick = pick;
-			drawCaseSelectionFrame(v);
-		}
 		while (g_system->getEventManager()->pollEvent(ev)) {
 			if (ev.type == Common::EVENT_QUIT ||
 				ev.type == Common::EVENT_RETURN_TO_LAUNCHER)
@@ -1938,7 +1924,7 @@ void EEMEngine::doCaseSelection() {
 						if (mp < kNumPicks && kPickEnabled[mp]) {
 							pick = mp;
 							v.pick = pick;
-							drawCaseSelectionFrame(v);
+							drawActionMenuFrame(v);
 							continue;
 						}
 					}
@@ -1969,7 +1955,7 @@ void EEMEngine::doCaseSelection() {
 						break;
 				}
 				v.pick = pick;
-				drawCaseSelectionFrame(v);
+				drawActionMenuFrame(v);
 				continue;
 			}
 			if (k == Common::KEYCODE_DOWN || k == Common::KEYCODE_RIGHT ||
@@ -1980,15 +1966,20 @@ void EEMEngine::doCaseSelection() {
 						break;
 				}
 				v.pick = pick;
-				drawCaseSelectionFrame(v);
+				drawActionMenuFrame(v);
 				continue;
 			}
 		}
 		if (confirmed) {
 			v.pick = pick;
-			drawCaseSelectionFrame(v);
+			drawActionMenuFrame(v);
 			break;
 		}
+		const uint32 now = g_system->getMillis();
+		if (now - chooserLastTick >= kChooserCycleMillis) {
+			chooserLastTick = now;
+			cycleChooserPalette();
+		}
 		g_system->updateScreen();
 		g_system->delayMillis(15);
 	}
@@ -1997,15 +1988,20 @@ void EEMEngine::doCaseSelection() {
 		return;
 
 	if (exitChosen) {
-		_mystery.clear();
-		_nextScreen = kScreenInvalid;
+		if (areYouSure()) {
+			_mystery.clear();
+			_nextScreen = kScreenInvalid;
+		} else {
+			drawActionMenuFrame(v);
+			_nextScreen = kScreenAction;
+		}
 		return;
 	}
 
 	// "Practice Mystery" is the tutorial ??? mystery 0.
 	if (pick == kPickPractice) {
 		if (!_mystery.load(0, &_rng)) {
-			warning("doCaseSelection: failed to load practice mystery");
+			warning("doActionScreen: failed to load practice mystery");
 			_mystery.clear();
 			resetSiteArrivalState();
 		} else {
@@ -2021,49 +2017,72 @@ void EEMEngine::doCaseSelection() {
 		// call `_ShowScrapbook(0, stage)` for the matching tier
 		// (verified at the action-handler jumptable bytes
 		// `01 03 05 07 09 ff` paired with handlers at 1c33:1be1).
-		// The picker here is meant to LEAVE the mystery state untouched
-		// ??? viewing the scrapbook never starts a new case.
+		// Viewing the scrapbook never starts a new case; return to
+		// the same action menu afterwards.
 		const uint stage = (pick == kPickScrap1) ? 1
 						 : (pick == kPickScrap2) ? 2 : 3;
 		doShowScrapbook(stage);
 		setSitePalette(0);
 		_mystery.clear();
-		_nextScreen = kScreenChooseMystery;
+		_nextScreen = kScreenAction;
 		return;
 	}
 
-	// "Choose A Mystery" sub-screen: pick a specific case from the
-	// chain-stage's roster. Mirrors `_DoChooseMystery @ 1a35:02b7` +
-	// `_CaseSelection @ 1c33:0a87`:
-	//   stage 1 (Junior, BOOK1.NME) ??? mysteries  1..24
-	//   stage 2 (Senior, BOOK2.NME) ??? mysteries 25..48
-	//   stage 3 (Master, BOOK3.NME) ??? mysteries 49..54
-	// `_DoChooseMystery` opens BOOK<stage>.NME and reads up to 25
-	// CRLF-terminated lines into a 25-entry FAR-pointer array passed
-	// to `_CaseSelection`. The grey mask `_Greys = &mysteriesSolved +
-	// stageLo` (1c33:0b22) makes already-solved entries unselectable.
+	_nextScreen = kScreenChooseMystery;
+}
+
+void EEMEngine::doCaseSelection() {
+	// Mirrors `_DoChooseMystery @ 1a35:02b7` + `_CaseSelection @
+	// 1c33:0a87`. `_DoChooseMystery` loads BOOK<stage>.NME and
+	// `_CaseSelection` draws PIC 0x41 plus the centered "Book N" /
+	// "Challenge Book" title. This screen is entered only after the
+	// player selects "Choose A Mystery" on `_ActionScreen`.
+	const uint kMaxMystery = 54;
+
+	CursorMan.showMouse(true);
+	setSitePalette(0);
+	_mystery.clear();
+	resetSiteArrivalState();
+
+	Picture caseBg;
+	const bool haveCaseBg = _picsArchive.getPicture(0x41, caseBg);
+	Picture revealPic;
+	const bool haveRevealPic =
+		_picsArchive.getPicture(kCaseSelectionRevealPic, revealPic);
+
+	// KD greeter sprite. `_CaseSelection @ 1c33:0a87` loads anim 0x15
+	// (Jake-paired) or 0x16 (Jenny-paired), then runs it through the
+	// chooser loop via `_UpdateAnimations`.
+	const uint kKdAniId = (_partner == 0) ? 0x15 : 0x16;
+	Animation kdAnim;
+	const bool haveKdAnim = _aniArchive.loadAnimation(kKdAniId, kdAnim)
+							 && !kdAnim.empty();
+	const int kKdAnimX = 0x112;
+	const int kKdAnimY = 0x50;
+
+	// Stage roster:
+	//   stage 1 (Junior, BOOK1.NME) -> mysteries  1..24
+	//   stage 2 (Senior, BOOK2.NME) -> mysteries 25..48
+	//   stage 3 (Master, BOOK3.NME) -> mysteries 49..54
 	uint stageLo = 1, stageHi = 0x18;
 	uint book = 1;
 	switch (_chainStage) {
 	case 2: stageLo = 0x19; stageHi = 0x30; book = 2; break;
 	case 3: stageLo = 0x31; stageHi = 0x36; book = 3; break;
-	default: break;  // stage 1 (or fallback)
+	default: break;
 	}
 	if (stageHi > kMaxMystery)
 		stageHi = kMaxMystery;
 
 	const Common::StringArray names = loadBookNames(book);
 	if (names.empty()) {
-		warning("doCaseSelection: BOOK%u.NME failed to load ??? bailing",
-				book);
-		_mystery.clear();
+		warning("doCaseSelection: BOOK%u.NME failed to load", book);
 		return;
 	}
 	const uint listLen = MIN<uint>((uint)names.size(), stageHi - stageLo + 1);
 
 	// Per-row solved flags. `_DoChoose @ 1c33:0521` skips solved entries
-	// when seeding the initial selection (`while *_Greys[select] != 0`)
-	// and again per-click via the same mask check.
+	// when seeding the initial selection and ignores clicks on them.
 	Common::Array<bool> solvedFlags;
 	solvedFlags.resize(listLen);
 	for (uint i = 0; i < listLen; i++) {
@@ -2072,32 +2091,19 @@ void EEMEngine::doCaseSelection() {
 			mn < sizeof(_mysteriesSolved) && _mysteriesSolved[mn] != 0;
 	}
 
-	// Seed the selection at the first unsolved entry ??? same as
-	// `_DoChoose`'s `while (*Greys[select] != 0) select++;` loop at
-	// 1c33:0524.
 	uint selRow = 0;
 	while (selRow < listLen && solvedFlags[selRow])
 		selRow++;
 	if (selRow >= listLen)
-		selRow = 0;  // every case solved ??? let player re-pick
+		selRow = 0;
+
 	uint topRow = 0;
 	const uint kVisible = 12;
 	if (selRow >= kVisible) {
 		topRow = selRow - kVisible / 2;
-		if (topRow + kVisible > listLen)
-			topRow = listLen > kVisible ? listLen - kVisible : 0;
+		clampCaseTopRow(topRow, listLen, kVisible);
 	}
 
-	auto clampTopRow = [&](uint &t) {
-		if (listLen <= kVisible) {
-			t = 0;
-			return;
-		}
-		const uint maxTop = listLen - kVisible;
-		if (t > maxTop)
-			t = maxTop;
-	};
-
 	CaseSubmenuView sv;
 	sv.vm = this;
 	sv.caseBg = &caseBg;
@@ -2130,49 +2136,43 @@ void EEMEngine::doCaseSelection() {
 				ev.type == Common::EVENT_RETURN_TO_LAUNCHER)
 				return;
 			if (ev.type == Common::EVENT_LBUTTONDOWN) {
-				if (kOkRect.contains(ev.mouse.x, ev.mouse.y)) {
+				if (kChooserOkRect.contains(ev.mouse.x, ev.mouse.y)) {
 					if (selRow < listLen && !solvedFlags[selRow])
 						confirmed = true;
 					break;
 				}
-				if (kExitRect.contains(ev.mouse.x, ev.mouse.y)) {
+				if (kChooserExitRect.contains(ev.mouse.x, ev.mouse.y)) {
 					_mystery.clear();
 					return;
 				}
-				if (kHelpRect.contains(ev.mouse.x, ev.mouse.y)) {
-					// In original `_CaseSelection`, this button is
-					// PIC 0x123 and returns 0xfffe, which calls
-					// `_ChooseSavedGame`. The ScummVM port stores the
-					// in-progress case in the profile save instead of
-					// original per-mystery files, so route to screen 8
-					// (profile picker) as the load/resume path.
+				if (kChooserHelpRect.contains(ev.mouse.x, ev.mouse.y)) {
+					// Original `_CaseSelection` returns 0xfffe here,
+					// then `_ChooseSavedGame` runs. ScummVM stores
+					// in-progress cases in the profile, so route to
+					// the profile picker instead.
 					saveProfile(_playerName);
 					_mystery.clear();
 					_nextScreen = kScreenProfile;
 					return;
 				}
-				if (kUpArrowRect.contains(ev.mouse.x, ev.mouse.y)) {
+				if (kChooserUpArrowRect.contains(ev.mouse.x, ev.mouse.y)) {
 					if (topRow > 0) { topRow--; dirty = true; }
 					continue;
 				}
-				if (kDnArrowRect.contains(ev.mouse.x, ev.mouse.y)) {
+				if (kChooserDnArrowRect.contains(ev.mouse.x, ev.mouse.y)) {
 					topRow++;
-					clampTopRow(topRow);
+					clampCaseTopRow(topRow, listLen, kVisible);
 					dirty = true;
 					continue;
 				}
-				if (kListRect.contains(ev.mouse.x, ev.mouse.y)) {
-					// Pick the row under the cursor ??? mirrors
-					// 1c33:0635 `i = (MouseY - DAT_29be_0d02) / 10;`.
+				if (kChooserListRect.contains(ev.mouse.x, ev.mouse.y)) {
 					const int kLineH = 10;
-					const int row = (ev.mouse.y - 35) / kLineH;
+					const int row = (ev.mouse.y - kChooserListRect.top) / kLineH;
 					if (row < 0 || row >= (int)kVisible)
 						continue;
 					const uint idx = topRow + (uint)row;
-					if (idx >= listLen)
+					if (idx >= listLen || solvedFlags[idx])
 						continue;
-					if (solvedFlags[idx])
-						continue;  // greyed entries ignore clicks
 					selRow = idx;
 					dirty = true;
 					continue;
@@ -2197,7 +2197,7 @@ void EEMEngine::doCaseSelection() {
 					selRow++;
 					if (selRow >= topRow + kVisible) {
 						topRow = selRow - kVisible + 1;
-						clampTopRow(topRow);
+						clampCaseTopRow(topRow, listLen, kVisible);
 					}
 					dirty = true;
 				}
@@ -2216,7 +2216,7 @@ void EEMEngine::doCaseSelection() {
 				selRow = MIN<uint>(selRow + kVisible, listLen - 1);
 				if (selRow >= topRow + kVisible) {
 					topRow = selRow - kVisible + 1;
-					clampTopRow(topRow);
+					clampCaseTopRow(topRow, listLen, kVisible);
 				}
 				dirty = true;
 				continue;
@@ -2242,10 +2242,12 @@ void EEMEngine::doCaseSelection() {
 			}
 		}
 		const uint32 now = g_system->getMillis();
-		const bool animTick = haveKdAnim && now - submenuLastTick >= 100;
-		if (animTick)
+		const bool chooserTick = now - submenuLastTick >= kChooserCycleMillis;
+		if (chooserTick) {
 			submenuLastTick = now;
-		if (dirty || animTick) {
+			cycleChooserPalette();
+		}
+		if (dirty || (chooserTick && haveKdAnim)) {
 			sv.topRow = topRow;
 			sv.selRow = selRow;
 			drawCaseSubmenu(sv);
@@ -2254,6 +2256,9 @@ void EEMEngine::doCaseSelection() {
 		g_system->delayMillis(15);
 	}
 
+	if (shouldQuit())
+		return;
+
 	const uint mn = stageLo + selRow;
 	if (!_mystery.load(mn, &_rng)) {
 		warning("doCaseSelection: failed to load mystery %u", mn);


Commit: 2398f5722175044e49a6729d7a77a32c3249ed82
    https://github.com/scummvm/scummvm/commit/2398f5722175044e49a6729d7a77a32c3249ed82
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:21:05+02:00

Commit Message:
EEM: refresh the correct part of the screen during case introductions

Changed paths:
    engines/eem/clues.cpp


diff --git a/engines/eem/clues.cpp b/engines/eem/clues.cpp
index 2d72d4e45b1..b6cc6b79c10 100644
--- a/engines/eem/clues.cpp
+++ b/engines/eem/clues.cpp
@@ -340,7 +340,8 @@ void EEMEngine::doInitClues() {
 
 	setSitePalette(0x22);
 	Picture bg;
-	if (_picsArchive.getPicture(0x52, bg))
+	const bool haveBriefingBg = _picsArchive.getPicture(0x52, bg);
+	if (haveBriefingBg)
 		blitAt(bg, 0, 0);
 
 	const uint gameAni = _partner == 0 ? 0x17 : 0x3b;
@@ -372,7 +373,7 @@ void EEMEngine::doInitClues() {
 			// "thinking" pose (cell 8) for 16 ticks instead of
 			// flipbook-cycling, and nancy waits 18 ticks before her
 			// late-arrival count-up.
-			if (_picsArchive.getPicture(0x52, bg))
+			if (haveBriefingBg)
 				blitAt(bg, 0, 0);
 			const uint32 t = frame * 100;
 			// All three briefing anims (game/book/nancy) go through
@@ -427,20 +428,38 @@ void EEMEngine::doInitClues() {
 		}
 	}
 
-	// Freeze the completed setup animation as the base for
-	// `_PlayInSequence`. The original clears the registered animations
-	// before playing the short case-type overlay, so the game/book/nancy
-	// cells do not keep cycling or wrap back to cell 0 underneath it.
+	// Freeze only the same setup-animation band the original bakes into
+	// its background buffers before clearing the registered animations:
+	// `_VidramRectCopy(0, 0x5a, 0x28, 0x6d, 16000, 48000/32000)`.
+	// Width is in mode-X columns, so 0x28 columns = 160 pixels. This
+	// preserves the lower-left book/Nancy area but intentionally drops the
+	// right-side game animation; `_PlayInSequence` redraws that character
+	// over a clean background next. Preserving the full screen leaves the
+	// old right-side Jake/Jenny frame underneath the sequence.
 	Graphics::ManagedSurface briefingBase(320, 200,
 		Graphics::PixelFormat::createFormatCLUT8());
 	briefingBase.clear();
+	if (haveBriefingBg) {
+		briefingBase.simpleBlitFrom(bg.surface);
+	} else {
+		Graphics::Surface *screen = g_system->lockScreen();
+		if (screen) {
+			briefingBase.simpleBlitFrom(*screen);
+			g_system->unlockScreen();
+		}
+	}
 	{
+		const int preserveX = 0;
+		const int preserveY = 0x5a;
+		const int preserveW = 0x28 * 4;
+		const int preserveH = 0x6d;
+		const Common::Rect preserveRect(preserveX, preserveY,
+										preserveX + preserveW,
+										preserveY + preserveH);
 		Graphics::Surface *screen = g_system->lockScreen();
 		if (screen) {
-			for (int row = 0; row < 200; row++) {
-				memcpy((byte *)briefingBase.getBasePtr(0, row),
-					   (const byte *)screen->getBasePtr(0, row), 320);
-			}
+			briefingBase.simpleBlitFrom(*screen, preserveRect,
+										Common::Point(preserveX, preserveY));
 			g_system->unlockScreen();
 		}
 	}


Commit: d00eeb0022a1966b8e1c78cbb5e77bcd4605336c
    https://github.com/scummvm/scummvm/commit/d00eeb0022a1966b8e1c78cbb5e77bcd4605336c
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:21:06+02:00

Commit Message:
EEM: optional better fitted balloon dialog code

Changed paths:
    engines/eem/clues.cpp
    engines/eem/detection.cpp
    engines/eem/detection.h
    engines/eem/eem.cpp
    engines/eem/eem.h
    engines/eem/graphics.cpp
    engines/eem/metaengine.cpp
    engines/eem/ui.cpp


diff --git a/engines/eem/clues.cpp b/engines/eem/clues.cpp
index b6cc6b79c10..b427294b306 100644
--- a/engines/eem/clues.cpp
+++ b/engines/eem/clues.cpp
@@ -808,12 +808,11 @@ void EEMEngine::displayClue(const byte *clueBlock) {
 												_playerName, _partner);
 
 		// Speech balloon. Mirrors `_GetBalloon` + `_AddPicBackground` in
-		// `_DisplayClue`. The original looks up per-balloon text-area
-		// metadata in a table at offset 0x875 (within `_DisplayClue`'s
-		// segment); we don't have that table decoded yet, so we use a
-		// fixed inset of 8 px from the balloon's top-left.
+		// `_DisplayClue`, with the shared balloon metadata table used for
+		// text placement and the fitted balloon variant.
+		const uint16 fittedBubNum = fitBalloonToText(bubNum, text);
 		Picture balloon;
-		const uint16 balloonId = bubNum & 0x7F;
+		const uint16 balloonId = fittedBubNum & 0x7F;
 		const bool haveBalloon = bubNum != 0xFFFF &&
 			_balloonArchive.size() > balloonId &&
 			_balloonArchive.loadEntry(balloonId, balloon);
@@ -849,7 +848,7 @@ void EEMEngine::displayClue(const byte *clueBlock) {
 				// `_GetBalloon @ 172b:1d7d` mirrors the picture horizontally
 				// when `(bubNum & 0x80)` is set ??? used for right-side
 				// speakers so the tail points the other way.
-				const bool flipBalloon = (bubNum & 0x80) != 0;
+				const bool flipBalloon = (fittedBubNum & 0x80) != 0;
 				if (bw > 0 && bh > 0) {
 					for (int row = 0; row < bh; row++) {
 						const byte *src =
@@ -1111,11 +1110,57 @@ void EEMEngine::displayFloppyDialogRecords(const byte *rec, uint count,
 			}
 		}
 
+		Common::String fitText;
+		Common::String fitPage;
+		uint16 fitXIns = 0;
+		uint16 fitYIns = 0;
+		uint16 fitWidth = 142;
+		getBalloonInsets(balByte, fitXIns, fitYIns, fitWidth);
+		uint fitTextLines = 0;
+		for (uint t = 0; t < textCount; t++) {
+			const uint8 idxByte = rec[11 + t];
+			const uint8 idx = idxByte & 0x7f;
+			const uint32 noteAbs = notesBase + (uint32)idx * 7;
+			if (noteAbs + 6 > dsz)
+				continue;
+			const uint16 textOff = (_partner == 0)
+				? READ_LE_UINT16(notes + idx * 7 + 2)
+				: READ_LE_UINT16(notes + idx * 7 + 4);
+			if (textOff >= dsz)
+				continue;
+			const char *linePtr = (const char *)(bufBase + textOff);
+			uint32 lineLen = 0;
+			while (textOff + lineLen < dsz && linePtr[lineLen] != 0)
+				lineLen++;
+			Common::String raw(linePtr, lineLen);
+			Common::String parsed = parseString(raw, _playerName, _partner);
+			if (!parsed.empty()) {
+				if (!fitPage.empty())
+					fitPage += '\n';
+				fitPage += parsed;
+			}
+			const bool continuePage =
+				(idxByte & 0x80) != 0 && t + 1 < textCount;
+			if (!continuePage) {
+				Common::Array<Common::String> wrapped;
+				_font.wordWrapText(fitPage, MAX<int>(8, (int)fitWidth),
+					wrapped);
+				if (wrapped.size() > fitTextLines ||
+					(wrapped.size() == fitTextLines &&
+					 fitPage.size() > fitText.size())) {
+					fitText = fitPage;
+					fitTextLines = wrapped.size();
+				}
+				fitPage.clear();
+			}
+		}
+
 		// Pre-load balloon picture + insets once per record (constant
 		// across all paginated text indices).
+		const uint16 fittedBalByte = fitBalloonToText(balByte, fitText);
 		Picture balloon;
-		const uint16 balloonId  = balByte & 0x7F;
-		const bool   flipBall   = (balByte & 0x80) != 0;
+		const uint16 balloonId  = fittedBalByte & 0x7F;
+		const bool   flipBall   = (fittedBalByte & 0x80) != 0;
 		const bool   haveBalloon = balByte != 0xFF &&
 			_balloonArchive.size() > balloonId &&
 			_balloonArchive.loadEntry(balloonId, balloon);
diff --git a/engines/eem/detection.cpp b/engines/eem/detection.cpp
index fd620dd89f4..7fe94f56f39 100644
--- a/engines/eem/detection.cpp
+++ b/engines/eem/detection.cpp
@@ -31,7 +31,8 @@ const PlainGameDescriptor eemGames[] = {
 	{ nullptr, nullptr }
 };
 
-#define GUI_OPTIONS_EEM GUIO3(GAMEOPTION_HIDE_HIGHLIGHT_BOXES, GUIO_MIDIADLIB, GUIO_MIDIMT32)
+#define GUI_OPTIONS_EEM_FLOPPY GUIO3(GAMEOPTION_HIDE_HIGHLIGHT_BOXES, GUIO_MIDIADLIB, GUIO_MIDIMT32)
+#define GUI_OPTIONS_EEM_CD     GUIO4(GAMEOPTION_HIDE_HIGHLIGHT_BOXES, GAMEOPTION_FIT_DIALOG_BALLOONS, GUIO_MIDIADLIB, GUIO_MIDIMT32)
 
 const ADGameDescription gameDescriptions[] = {
 	{
@@ -42,7 +43,7 @@ const ADGameDescription gameDescriptions[] = {
 		Common::EN_ANY,
 		Common::kPlatformDOS,
 		ADGF_NO_FLAGS,
-		GUI_OPTIONS_EEM
+		GUI_OPTIONS_EEM_CD
 	},
 	{
 		"eem",
@@ -52,7 +53,7 @@ const ADGameDescription gameDescriptions[] = {
 		Common::EN_ANY,
 		Common::kPlatformDOS,
 		ADGF_NO_FLAGS,
-		GUI_OPTIONS_EEM
+		GUI_OPTIONS_EEM_FLOPPY
 	},
 	{
 		// Spanish floppy release ??? same EEM.EXE binary as the English
@@ -66,7 +67,7 @@ const ADGameDescription gameDescriptions[] = {
 		Common::ES_ESP,
 		Common::kPlatformDOS,
 		ADGF_NO_FLAGS,
-		GUI_OPTIONS_EEM
+		GUI_OPTIONS_EEM_FLOPPY
 	},
 
 	AD_TABLE_END_MARKER
diff --git a/engines/eem/detection.h b/engines/eem/detection.h
index 7d74db6cd7f..3ad726bb5f0 100644
--- a/engines/eem/detection.h
+++ b/engines/eem/detection.h
@@ -26,7 +26,8 @@
 
 namespace EEM {
 
-#define GAMEOPTION_HIDE_HIGHLIGHT_BOXES GUIO_GAMEOPTIONS1
+#define GAMEOPTION_HIDE_HIGHLIGHT_BOXES   GUIO_GAMEOPTIONS1
+#define GAMEOPTION_FIT_DIALOG_BALLOONS    GUIO_GAMEOPTIONS2
 
 enum EEMDebugChannels {
 	kDebugGeneral = 1 << 0,
diff --git a/engines/eem/eem.cpp b/engines/eem/eem.cpp
index da33f550739..6d4acc41d88 100644
--- a/engines/eem/eem.cpp
+++ b/engines/eem/eem.cpp
@@ -199,6 +199,7 @@ EEMEngine::EEMEngine(OSystem *syst, const ADGameDescription *gameDesc)
 	  _playerName("Detective"),
 	  _lastScreen(kScreenInvalid), _nextScreen(kScreenTitle), _partner(0) {
 	ConfMan.registerDefault("hide_highlight_boxes", false);
+	ConfMan.registerDefault("fit_dialog_balloons", false);
 
 	// `ADGameDescription::extra` is set by the matching entry in
 	// `gameDescriptions[]` ("CD" or "Floppy"). Keep variant detection
diff --git a/engines/eem/eem.h b/engines/eem/eem.h
index bc63f993db1..32eb70388f5 100644
--- a/engines/eem/eem.h
+++ b/engines/eem/eem.h
@@ -254,6 +254,11 @@ public:
 	/// ctype-bit-1 (= digit) at `29be:2be1 + char`.
 	uint16 getKDTextBalloon(byte firstChar) const;
 
+	/// Cleanup for overly tall original speech balloons. Keeps the original
+	/// bubble family and mirror bit, but picks a shorter sibling when the
+	/// wrapped text leaves enough empty lines.
+	uint16 fitBalloonToText(uint16 bubNum, const Common::String &text);
+
 	/// Substitute the 0x80..0x89 control bytes the engine uses inside
 	/// `TextBlock` strings. Mirrors `_ParseString @ 1b66:07c3`; jump
 	/// table at 1b66:0cbe. Used by every clue / hint / balloon caller.
diff --git a/engines/eem/graphics.cpp b/engines/eem/graphics.cpp
index 4b9eae7cbf8..ca91bcc0349 100644
--- a/engines/eem/graphics.cpp
+++ b/engines/eem/graphics.cpp
@@ -19,6 +19,7 @@
  *
  */
 
+#include "common/config-manager.h"
 #include "common/debug.h"
 #include "common/events.h"
 #include "common/file.h"
@@ -85,6 +86,52 @@ const BalloonInsets kBalloonInsetTable[] = {
 	{ 5, 8, 158, 160, 45 }, { 5, 8, 176, 176, 50 }, { 8, 7, 142, 142, 71 }
 };
 
+struct BalloonFamily {
+	uint16 first;
+	uint16 last;
+};
+
+const BalloonFamily kBalloonFamilies[] = {
+	{ 0x00, 0x06 },
+	{ 0x07, 0x0d },
+	{ 0x0e, 0x14 },
+	{ 0x15, 0x1b },
+	{ 0x1c, 0x22 },
+	{ 0x23, 0x29 },
+	{ 0x2a, 0x30 },
+	{ 0x31, 0x31 },
+	{ 0x32, 0x32 },
+	{ 0x33, 0x33 },
+};
+
+bool findBalloonFamily(uint16 balloonId, uint16 &first, uint16 &last) {
+	for (uint i = 0; i < ARRAYSIZE(kBalloonFamilies); i++) {
+		if (balloonId >= kBalloonFamilies[i].first &&
+			balloonId <= kBalloonFamilies[i].last) {
+			first = kBalloonFamilies[i].first;
+			last  = kBalloonFamilies[i].last;
+			return true;
+		}
+	}
+	first = last = balloonId;
+	return false;
+}
+
+// Lines that fit inside balloon @p balloonId. The metadata-table `indDY`
+// is the designed bottom of the text area, NOT the indicator-drawing
+// position alone ??? for families 3, 4, 6 and the singletons the bubble
+// graphic continues below `indDY` with shadow / tail decoration that
+// text must not encroach on. Image height is therefore an overestimate;
+// `indDY` is the artist-intended last text line.
+uint getBalloonLineCapacity(uint16 balloonId, int lineH) {
+	const uint idx = balloonId & 0x7F;
+	if (idx >= ARRAYSIZE(kBalloonInsetTable) || lineH <= 0)
+		return 0;
+
+	const BalloonInsets &insets = kBalloonInsetTable[idx];
+	return MAX<uint>(1, ((int)insets.indDY - (int)insets.y) / lineH + 1);
+}
+
 // Floppy KDHelp hotspot-searched check. Mirrors
 // `FUN_22dc_096c @ 22dc:096c`: walks the per-site dialog records at
 // `site_data[+6]` to skip `hotspotIdx` hotspots, then returns the
@@ -229,6 +276,7 @@ void EEMEngine::doHelp() {
 
 		Common::String text = parseString(Common::String(txt),
 										   _playerName, _partner);
+		balloonIdx = fitBalloonToText((uint16)balloonIdx, text) & 0x7F;
 		Graphics::ManagedSurface ms(320, 200,
 			Graphics::PixelFormat::createFormatCLUT8());
 		ms.clear();
@@ -411,9 +459,10 @@ void EEMEngine::doHelp() {
 	// has to.
 	const byte firstChar =
 		text.empty() ? (byte)0 : (byte)text[0];
-	const uint16 bubNum = getKDTextBalloon(firstChar);
+	uint16 bubNum = getKDTextBalloon(firstChar);
 	if (firstChar >= '0' && firstChar <= '9')
 		text.deleteChar(0);
+	bubNum = fitBalloonToText(bubNum, text);
 	Picture balloon;
 	const bool haveBalloon =
 		_balloonArchive.size() > (bubNum & 0x7F) &&
@@ -566,6 +615,72 @@ void EEMEngine::setPartnerEraseBg(const Graphics::ManagedSurface *bg) {
 	}
 }
 
+uint16 EEMEngine::fitBalloonToText(uint16 bubNum,
+								   const Common::String &text) {
+	// Opt-in via the "Better fit for dialog balloons" game option, and
+	// CD-only ??? the floppy build's balloon archive / inset table hasn't
+	// been validated for shrinking yet, so leave it on the original
+	// artist-chosen bubble.
+	if (isFloppy() || !ConfMan.getBool("fit_dialog_balloons"))
+		return bubNum;
+
+	const uint16 originalId = bubNum & 0x7F;
+	if (bubNum == 0xFFFF || text.empty() || !_font.isLoaded() ||
+		originalId >= ARRAYSIZE(kBalloonInsetTable))
+		return bubNum;
+
+	const BalloonInsets &originalInsets = kBalloonInsetTable[originalId];
+	const int lineH = _font.getFontHeight();
+	const uint originalCapacity = getBalloonLineCapacity(originalId, lineH);
+	if (originalCapacity == 0)
+		return bubNum;
+
+	Common::Array<Common::String> lines;
+	_font.wordWrapText(text, MAX<int>(8, (int)originalInsets.w), lines);
+	if (lines.empty())
+		return bubNum;
+
+	const uint usedLines = lines.size();
+	if (usedLines > originalCapacity)
+		return bubNum;
+
+	uint16 familyFirst = 0;
+	uint16 familyLast = 0;
+	if (!findBalloonFamily(originalId, familyFirst, familyLast))
+		return bubNum;
+
+	uint16 chosenId = originalId;
+	uint chosenCapacity = originalCapacity;
+	debug(
+		   "fitBalloonToText: original 0x%02x, usedLines=%u, capacity=%u, family=0x%02x-0x%02x",
+		   (int)originalId, usedLines, originalCapacity,
+		   (int)familyFirst, (int)familyLast);
+	while (chosenId > familyFirst) {
+		if (chosenCapacity < usedLines)
+			break;
+
+		const uint16 candidateId = chosenId - 1;
+		if (candidateId >= ARRAYSIZE(kBalloonInsetTable) ||
+			kBalloonInsetTable[candidateId].w != originalInsets.w)
+			break;
+		const uint candidateCapacity =
+			getBalloonLineCapacity(candidateId, lineH);
+		if (candidateCapacity < usedLines)
+			break;
+		chosenId = candidateId;
+		chosenCapacity = candidateCapacity;
+	}
+
+	if (chosenId == originalId)
+		return bubNum;
+
+	debug(
+		   "fitBalloonToText: 0x%02x -> 0x%02x (%u lines, capacity %u -> %u)",
+		   (int)originalId, (int)chosenId, usedLines,
+		   originalCapacity, chosenCapacity);
+	return (bubNum & 0x80) | chosenId;
+}
+
 bool EEMEngine::getBalloonInsets(uint16 bubNum, uint16 &xInset,
 								  uint16 &yInset, uint16 &textW) const {
 	// `kBalloonInsetTable` lives at file scope above; see comment there.
diff --git a/engines/eem/metaengine.cpp b/engines/eem/metaengine.cpp
index 52b2c5e78b5..04e778c35bc 100644
--- a/engines/eem/metaengine.cpp
+++ b/engines/eem/metaengine.cpp
@@ -43,6 +43,18 @@ static const ADExtraGuiOptionsMap optionsList[] = {
 			0
 		}
 	},
+	{
+		GAMEOPTION_FIT_DIALOG_BALLOONS,
+		{
+			_s("Better fit for dialog balloons"),
+			_s("Pick a smaller speech-bubble graphic when the wrapped "
+			   "text doesn't need the original size."),
+			"fit_dialog_balloons",
+			false,
+			0,
+			0
+		}
+	},
 
 	AD_EXTRA_GUI_OPTIONS_TERMINATOR
 };
diff --git a/engines/eem/ui.cpp b/engines/eem/ui.cpp
index d7986c80c89..1c3c0863398 100644
--- a/engines/eem/ui.cpp
+++ b/engines/eem/ui.cpp
@@ -4205,9 +4205,10 @@ void EEMEngine::doAccuse() {
 			g_system->unlockScreen();
 		}
 		const byte firstChar = (byte)entryText[0];
-		const uint16 bubNum = getKDTextBalloon(firstChar);
+		uint16 bubNum = getKDTextBalloon(firstChar);
 		if (firstChar >= '0' && firstChar <= '9')
 			entryText.deleteChar(0);
+		bubNum = fitBalloonToText(bubNum, entryText);
 		Picture balloon;
 		const bool haveBalloon =
 			_balloonArchive.size() > (bubNum & 0x7F) &&
@@ -4329,13 +4330,14 @@ void EEMEngine::doAccuse() {
 		}
 		const byte firstChar =
 			hint.empty() ? (byte)0 : (byte)hint[0];
-		const uint16 bubNum = getKDTextBalloon(firstChar);
+		uint16 bubNum = getKDTextBalloon(firstChar);
 		// Strip the digit prefix used for balloon dispatch ??? it's
 		// consumed by the original at `_DisplayAlibi @ 1df2:0163`
 		// (`str = pbVar7 + 1`) and shouldn't appear in the rendered
 		// text. `_GetKDTextBalloon` itself doesn't advance past it.
 		if (firstChar >= '0' && firstChar <= '9')
 			hint.deleteChar(0);
+		bubNum = fitBalloonToText(bubNum, hint);
 		Picture balloon;
 		const bool haveBalloon =
 			_balloonArchive.size() > (bubNum & 0x7F) &&
@@ -4413,7 +4415,7 @@ void EEMEngine::doAccuse() {
 				// the dispatch result is the same either way.
 				const byte firstChar =
 					hint.empty() ? (byte)0 : (byte)hint[0];
-				const uint16 bubNum = getKDTextBalloon(firstChar);
+				uint16 bubNum = getKDTextBalloon(firstChar);
 				// Strip the digit prefix used for balloon dispatch.
 				// `_DisplayAlibi @ 1df2:0163` does `str = pbVar7 + 1`
 				// after using `*str` for `bindx`. Same pattern used by
@@ -4422,6 +4424,7 @@ void EEMEngine::doAccuse() {
 				// the intro balloon shows e.g. "1Ready to solve?".
 				if (firstChar >= '0' && firstChar <= '9')
 					hint.deleteChar(0);
+				bubNum = fitBalloonToText(bubNum, hint);
 				Picture balloon;
 				const bool haveBalloon =
 					_balloonArchive.size() > (bubNum & 0x7F) &&
@@ -4660,7 +4663,8 @@ void EEMEngine::doAccuse() {
 		}
 		if (bindx >= 16)
 			bindx = 2;
-		const uint16 bubNum = kAlibiBubbles[bindx];
+		uint16 bubNum = kAlibiBubbles[bindx];
+		bubNum = fitBalloonToText(bubNum, alibi);
 
 		Picture alibiBg;
 		const bool haveAlibiBg = _picsArchive.getPicture(0x3e, alibiBg);
@@ -4825,9 +4829,10 @@ void EEMEngine::doAccuse() {
 			}
 			if (!react.empty()) {
 				const byte rChar = (byte)react[0];
-				const uint16 rBub = getKDTextBalloon(rChar);
+				uint16 rBub = getKDTextBalloon(rChar);
 				if (rChar >= '0' && rChar <= '9')
 					react.deleteChar(0);
+				rBub = fitBalloonToText(rBub, react);
 				Picture rBalloon;
 				const bool haveR =
 					_balloonArchive.size() > (rBub & 0x7F) &&
@@ -5085,6 +5090,7 @@ void EEMEngine::doAccuseFloppy() {
 		}
 		Common::String text =
 			parseString(Common::String(txt), _playerName, _partner);
+		balloonIdx = fitBalloonToText((uint16)balloonIdx, text) & 0x7F;
 		Graphics::ManagedSurface ms(320, 200,
 			Graphics::PixelFormat::createFormatCLUT8());
 		Graphics::Surface *cur = g_system->lockScreen();
@@ -5620,6 +5626,7 @@ void EEMEngine::doAccuseFloppy() {
 		balloonRaw = kFloppyAlibiBalloonByDigit[(int)(alibi[0] - '0')];
 		alibi.deleteChar(0);
 	}
+	balloonRaw = fitBalloonToText((uint16)balloonRaw, alibi);
 	const uint balloonIdx = balloonRaw & 0x7F;
 	const bool flipBalloon = (balloonRaw & 0x80) != 0;
 


Commit: 8ca74796331ffae0b9f49a844ed190491bb8313b
    https://github.com/scummvm/scummvm/commit/8ca74796331ffae0b9f49a844ed190491bb8313b
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:21:06+02:00

Commit Message:
EEM: only some keys will advance dialog

Changed paths:
    engines/eem/clues.cpp
    engines/eem/eem.cpp


diff --git a/engines/eem/clues.cpp b/engines/eem/clues.cpp
index b427294b306..68a66b1aa0d 100644
--- a/engines/eem/clues.cpp
+++ b/engines/eem/clues.cpp
@@ -926,6 +926,9 @@ void EEMEngine::displayClue(const byte *clueBlock) {
 
 		// Wait for click/key to advance ??? only if we drew something.
 		// ESC skips the entire dialogue rather than just one entry.
+		// Only Return / KP-Enter / Space advance one entry; other keys
+		// are ignored so accidental keystrokes don't blow past dialog
+		// the player hasn't finished reading.
 		if (hasText || (charPicId != 0 && charPicId != 0xFFFF)) {
 			bool advance = false;
 			bool skipAll = false;
@@ -950,8 +953,14 @@ void EEMEngine::displayClue(const byte *clueBlock) {
 						interruptAudio();
 						break;
 					}
-					if (ev.type == Common::EVENT_LBUTTONDOWN ||
-						ev.type == Common::EVENT_KEYDOWN) {
+					if (ev.type == Common::EVENT_LBUTTONDOWN) {
+						advance = true;
+						break;
+					}
+					if (ev.type == Common::EVENT_KEYDOWN &&
+						(ev.kbd.keycode == Common::KEYCODE_RETURN ||
+						 ev.kbd.keycode == Common::KEYCODE_KP_ENTER ||
+						 ev.kbd.keycode == Common::KEYCODE_SPACE)) {
 						advance = true;
 						break;
 					}
diff --git a/engines/eem/eem.cpp b/engines/eem/eem.cpp
index 6d4acc41d88..85a533fee88 100644
--- a/engines/eem/eem.cpp
+++ b/engines/eem/eem.cpp
@@ -864,6 +864,10 @@ void EEMEngine::waitForInput(uint32 maxMs) {
 	// active audio so the theme / voice / spool don't bleed past
 	// the abort. Mirrors the `_CleanMysterySounds` + `_StopMIDI`
 	// pair around the title wait in `_DoOpeningAnims`.
+	//
+	// Only Return / KP-Enter / Space / Escape advance ??? letting any key
+	// dismiss balloons makes typing-while-reading (or a stuck modifier)
+	// blow past dialog the player hasn't finished reading.
 	const uint32 startMs = g_system->getMillis();
 	while (!shouldQuit() && (g_system->getMillis() - startMs < maxMs)) {
 		Common::Event event;
@@ -877,8 +881,13 @@ void EEMEngine::waitForInput(uint32 maxMs) {
 				if (event.kbd.keycode == Common::KEYCODE_ESCAPE) {
 					_skipIntro = true;
 					interruptAudio();
+					return;
+				}
+				if (event.kbd.keycode == Common::KEYCODE_RETURN ||
+					event.kbd.keycode == Common::KEYCODE_KP_ENTER ||
+					event.kbd.keycode == Common::KEYCODE_SPACE) {
+					return;
 				}
-				return;
 			}
 		}
 		g_system->updateScreen();


Commit: 794eeec90c42c5472ff7aed52707120024529c6a
    https://github.com/scummvm/scummvm/commit/794eeec90c42c5472ff7aed52707120024529c6a
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:21:06+02:00

Commit Message:
EEM: strips leading spaces

Changed paths:
    engines/eem/clues.cpp


diff --git a/engines/eem/clues.cpp b/engines/eem/clues.cpp
index 68a66b1aa0d..27089a2696f 100644
--- a/engines/eem/clues.cpp
+++ b/engines/eem/clues.cpp
@@ -675,7 +675,25 @@ Common::String EEMEngine::parseString(const Common::String &raw,
 			break;
 		}
 	}
-	return out;
+
+	// Strip leading spaces at the start of each emitted line. Mirrors
+	// `_DoWordWrap @ 1b66:04a7`, which advances past spaces at the
+	// start of every output line via `for (; str[last] == ' '; last++)`.
+	// ~60% of mystery-text strings carry 1-2 leading spaces in the data
+	// (verified across all CD M*.BIN files); the original WordWrap
+	// discards them, so we do the same before the text reaches
+	// `Font::wordWrapText` (which only trims at wrap-induced line
+	// boundaries, not at start-of-input or after an embedded '\n').
+	Common::String cleaned;
+	bool atLineStart = true;
+	for (uint i = 0; i < out.size(); i++) {
+		const char ch = out[i];
+		if (atLineStart && ch == ' ')
+			continue;
+		cleaned += ch;
+		atLineStart = (ch == '\n');
+	}
+	return cleaned;
 }
 
 void EEMEngine::applyClueSideEffects(const byte *c) {


Commit: 3a87e8fc489eaca75c90176ee46adc12c1dcdaab
    https://github.com/scummvm/scummvm/commit/3a87e8fc489eaca75c90176ee46adc12c1dcdaab
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:21:07+02:00

Commit Message:
EEM: refactory doGallery into several functions

Changed paths:
    engines/eem/eem.h
    engines/eem/ui.cpp


diff --git a/engines/eem/eem.h b/engines/eem/eem.h
index 32eb70388f5..8578d1a2d6c 100644
--- a/engines/eem/eem.h
+++ b/engines/eem/eem.h
@@ -221,6 +221,18 @@ public:
 	/// Show suspect gallery. Mirrors `_DrawGallery` @ 158f:0046.
 	void doGallery();
 
+	/// Suspect-detail view inside the gallery. Mirrors
+	/// `MoreInfo @ 158f:0419`: paints PIC 0x3f + suspect picture at
+	/// (0x94, 0x0f), paginates the suspect's found clues inside the
+	/// `_GalleryNoteRect`, and dispatches PDA bottom-bar buttons via
+	/// `_HandleMoreButton @ 158f:027d`. Sets `_nextScreen` and
+	/// returns true when the user picks a button that should exit
+	/// `doGallery` outright (NOTEBOOK / ACCUSE / MAP); returns false
+	/// for plain dismissal (ESC / GALLERY button) so the caller stays
+	/// on the portrait grid.
+	bool moreInfo(const byte *gd, uint suspectIdx,
+				   const Picture &galBg, bool haveBg);
+
 	/// Show big map; click chooses next site. Mirrors `_DoBigMap` @ 20fe:09e7.
 	void doBigMap();
 
diff --git a/engines/eem/ui.cpp b/engines/eem/ui.cpp
index 1c3c0863398..696a5e389d8 100644
--- a/engines/eem/ui.cpp
+++ b/engines/eem/ui.cpp
@@ -2787,235 +2787,9 @@ void EEMEngine::doGallery() {
 					if (slotSuspect[i] < 0)
 						continue;
 					if (slotRects[i].contains(ev.mouse.x, ev.mouse.y)) {
-						// `MoreInfo(i)` ??? show the suspect detail page.
-						// Mirrors `MoreInfo @ 158f:0419`:
-						//   _RefreshGalleryBackground();
-						//   _GetPicture(*(u16*)(gd + i*0x46));
-						//   _AddPicBackground(pic, 0x94, 0xf);
-						//   _DrawGalleryNotes(gd + i*0x46);
-						//   loop until ESC or button click.
-						// Suspect data layout differs by variant:
-						//   * CD (`158f:0419`): fixed 0x46-byte stride.
-						//     +0..1   picId
-						//     +8..9   clue count (u16)
-						//     +0xa..  array of u16 clue IDs (max 30,
-						//             terminated by 0xFFFF if short).
-						//   * Floppy (`_MoreInfo_Floppy = 154e:042b` ???
-						//     `FUN_154e_0201` ??? `FUN_15e0_01e8`):
-						//     variable-stride.
-						//     +0..1   picId
-						//     +2..3   alibi (0xFFFF = guilty)
-						//     +4      clue count (u8)
-						//     +5..    u8 clue IDs (per the asm at
-						//             154e:020e..0282 which calls
-						//             `FUN_15e0_01e8(rect, entry+5,
-						//                            entry[4], NULL)`).
-						const bool floppyMI = isFloppy();
-						const uint suspectIdx = (uint)slotSuspect[i];
-						const byte *suspect = floppyMI
-												  ? _mystery.floppySuspectEntry(suspectIdx)
-												  : gd + suspectIdx * 0x46;
-						if (!suspect)
-							break;
-						const uint16 detailPic =
-							READ_LE_UINT16(suspect + 0);
-						const uint clueCount = floppyMI
-												   ? (uint)suspect[4]
-												   : READ_LE_UINT16(suspect + 8);
-
-						Graphics::ManagedSurface ms(320, 200,
-													Graphics::PixelFormat::createFormatCLUT8());
-						ms.clear();
-						if (haveBg) {
-							const int bw = MIN<int>(galBg.surface.w, 320);
-							const int bh = MIN<int>(galBg.surface.h, 200);
-							for (int row = 0; row < bh; row++) {
-								memcpy((byte *)ms.getBasePtr(0, row),
-									   (const byte *)galBg.surface.getBasePtr(0, row), bw);
-							}
-						}
-						// Partner sprite at (5, 0x50). The original
-						// `MoreInfo @ 158f:0419` calls
-						// `_RefreshGalleryBackground` (clears the
-						// portrait grid) but the partner anim slot
-						// registered by `_DoGallery` keeps painting
-						// at every `_UpdateAnimations` tick ??? the
-						// suspect detail pic covers the right side
-						// only (drawn at 0x94, 0xf), so the partner
-						// stays visible on the left. Without this
-						// blit the suspect-detail screen has no
-						// partner.
-						setInteractiveMouseCursor(false);
-						{
-							const uint partnerAnim =
-								(_partner == 0) ? 2 : 0x10;
-							Animation partnerAni;
-							if (_aniArchive.loadAnimation(partnerAnim,
-														  partnerAni) &&
-								!partnerAni.empty()) {
-								const uint32 now = g_system->getMillis();
-								const uint frameIdx =
-									partnerFrameAtTick(0x02,
-													   (uint)partnerAni.size(), now);
-								blitAnimFrameAnchored(ms.surfacePtr(),
-													  partnerAni[frameIdx], 5, 0x50);
-							}
-						}
-						// Full suspect picture at (0x94, 0xf).
-						Picture detail;
-						if (_picsArchive.getPicture(detailPic, detail)) {
-							const byte transp =
-								(byte)(detail.flags >> 8);
-							const int dx = 0x94, dy = 0x0f;
-							const int dw = MIN<int>(detail.surface.w, 320 - dx);
-							const int dh = MIN<int>(detail.surface.h, 200 - dy);
-							for (int row = 0; row < dh; row++) {
-								const byte *src =
-									(const byte *)detail.surface.getBasePtr(0, row);
-								byte *dst =
-									(byte *)ms.getBasePtr(0, dy + row);
-								for (int col = 0; col < dw; col++) {
-									if (src[col] != transp)
-										dst[dx + col] = src[col];
-								}
-							}
-						}
-						// Suspect's clue notes inside _GalleryNoteRect
-						// = (78, 93, 288, 152), per 29be:0100. Cyan text
-						// renders directly on the PDA's natural blue
-						// screen ??? matches `_DrawGalleryNotes @ 158f:01f4`.
-						const int rx = 78, ry = 93;
-						const int rw = 288 - 78, rh = 152 - 93;
-
-						const byte *ni = _mystery.noteIndex();
-						const uint16 niCount = _mystery.noteIndexCount();
-						int yPos = ry;
-						const int lineH = _font.getFontHeight();
-						bool drewAny = false;
-						const uint clueMax = floppyMI ? clueCount : 30u;
-						for (uint k = 0; k < clueCount && k < clueMax; k++) {
-							const uint16 clueId = floppyMI
-								? (uint16)suspect[5 + k]
-								: READ_LE_UINT16(suspect + 0xa + k * 2);
-							if (!floppyMI && clueId == 0xFFFF)
-								break;
-							if (clueId >= Mystery::kCluesFoundCap ||
-								!_mystery._cluesFound[clueId])
-								continue;
-							if (!ni || clueId >= niCount)
-								continue;
-							// Floppy notes are 7-byte entries:
-							//   +0..1 clue text (absolute offset)
-							//   +2..3 Jake spoken line
-							//   +4..5 Jenny spoken line
-							//   +6    score
-							// `_DrawNotes_Floppy / FUN_15e0_01e8`'s
-							// `*(int *)(notes + idx * 7)` shows the
-							// notebook always uses +0 ??? the partner-
-							// agnostic clue statement. The +2/+4
-							// offsets are the partner spoken lines used
-							// by `FUN_22dc_05c8` when rendering dialog
-							// records (NOT this notebook view). CD
-							// notes are 4 bytes: u16 textOff, u16
-							// score.
-							Common::String txt;
-							if (floppyMI) {
-								const uint16 textOff =
-									READ_LE_UINT16(ni + clueId * 7);
-								const byte *bb = _mystery.blobAt(0);
-								const uint32 dsz =
-									_mystery.dataSize();
-								if (bb && textOff != 0 &&
-									textOff < dsz) {
-									const char *p =
-										(const char *)(bb + textOff);
-									uint32 len = 0;
-									while (textOff + len < dsz &&
-										   p[len] != 0)
-										len++;
-									txt = parseString(
-										Common::String(p, len),
-										_playerName, _partner);
-								}
-							} else {
-								const uint16 textOff =
-									READ_LE_UINT16(ni + clueId * 4);
-								txt = parseString(
-									_mystery.textAt(textOff),
-									_playerName, _partner);
-							}
-							if (txt.empty())
-								continue;
-							const byte color =
-								_mystery._noteSelected[clueId] ? 0x3C : 0x5C;
-							const int hLine = _font.drawWordWrapped(
-								&ms, rx, yPos, rw, txt, color);
-							yPos += hLine + 7;
-							drewAny = true;
-							if (yPos + lineH > ry + rh)
-								break;
-						}
-						if (!drewAny && _font.isLoaded()) {
-							_font.drawString(&ms,
-								isSpanish()
-									? "Aun no hay pistas para este sospechoso."
-									: "No clues yet for this suspect.",
-								rx, ry, rw, 0x5C);
-						}
-						// Header / footer text.
-						if (_font.isLoaded()) {
-							_font.drawString(&ms,
-								isSpanish() ? "EXPEDIENTE" : "SUSPECT FILE",
-								rx, ry - 11, rw, 0x3C);
-							_font.drawString(&ms,
-								isSpanish() ? "(clic / ESC: volver)"
-											: "(click / ESC: back)",
-								rx, ry + rh + 2, rw, 0x3C);
-						}
-						g_system->copyRectToScreen(ms.getPixels(),
-							ms.pitch, 0, 0, 320, 200);
-						g_system->updateScreen();
-
-						// Wait for click or ESC. Drain the queued
-						// LBUTTONDOWN that triggered this MoreInfo first
-						// so we don't immediately accept it as the
-						// dismiss event.
-						g_system->delayMillis(150);
-						{
-							Common::Event drain;
-							while (g_system->getEventManager()->pollEvent(drain)) {
-								if (drain.type == Common::EVENT_QUIT ||
-									drain.type == Common::EVENT_RETURN_TO_LAUNCHER) {
-									setInteractiveMouseCursor(false);
-									return;
-								}
-							}
-						}
-						bool back = false;
-						while (!back && !shouldQuit()) {
-							Common::Event e2;
-							while (g_system->getEventManager()->pollEvent(e2)) {
-								if (e2.type == Common::EVENT_LBUTTONDOWN ||
-									(e2.type == Common::EVENT_KEYDOWN &&
-									 (e2.kbd.keycode == Common::KEYCODE_ESCAPE ||
-									  e2.kbd.keycode == Common::KEYCODE_RETURN))) {
-									back = true;
-									break;
-								}
-								if (e2.type == Common::EVENT_QUIT ||
-									e2.type == Common::EVENT_RETURN_TO_LAUNCHER) {
-									setInteractiveMouseCursor(false);
-									return;
-								}
-							}
-							// Per-tick `updateScreen()` so the SDL cursor
-							// follows the mouse ??? without it the cursor
-							// freezes on entry to the MoreInfo screen
-							// (we never repaint here, so the cursor never
-							// gets drawn at its current position).
-							g_system->updateScreen();
-							g_system->delayMillis(20);
-						}
+						if (moreInfo(gd, (uint)slotSuspect[i],
+									 galBg, haveBg))
+							exitFlag = true;
 						// Force gallery redraw immediately so the
 						// player isn't left looking at the dismissed
 						// MoreInfo screen until the next 100 ms tick.
@@ -3058,6 +2832,358 @@ void EEMEngine::doGallery() {
 	setInteractiveMouseCursor(false);
 }
 
+bool EEMEngine::moreInfo(const byte *gd, uint suspectIdx,
+						  const Picture &galBg, bool haveBg) {
+	// Suspect-detail page. Mirrors `MoreInfo @ 158f:0419`:
+	//   _RefreshGalleryBackground();
+	//   _GetPicture(*(u16*)(gd + i*0x46));
+	//   _AddPicBackground(pic, 0x94, 0xf);
+	//   _DrawGalleryNotes(gd + i*0x46);
+	//   loop until ESC or button click.
+	// Suspect data layout differs by variant:
+	//   * CD (`158f:0419`): fixed 0x46-byte stride.
+	//     +0..1   picId
+	//     +8..9   clue count (u16)
+	//     +0xa..  array of u16 clue IDs (max 30,
+	//             terminated by 0xFFFF if short).
+	//   * Floppy (`_MoreInfo_Floppy = 154e:042b` ???
+	//     `FUN_154e_0201` ??? `FUN_15e0_01e8`):
+	//     variable-stride.
+	//     +0..1   picId
+	//     +2..3   alibi (0xFFFF = guilty)
+	//     +4      clue count (u8)
+	//     +5..    u8 clue IDs (per the asm at
+	//             154e:020e..0282 which calls
+	//             `FUN_15e0_01e8(rect, entry+5,
+	//                            entry[4], NULL)`).
+	const bool floppyMI = isFloppy();
+	const byte *suspect = floppyMI
+							  ? _mystery.floppySuspectEntry(suspectIdx)
+							  : gd + suspectIdx * 0x46;
+	if (!suspect)
+		return false;
+	const uint16 detailPic = READ_LE_UINT16(suspect + 0);
+	const uint clueCount = floppyMI
+							   ? (uint)suspect[4]
+							   : READ_LE_UINT16(suspect + 8);
+
+	setInteractiveMouseCursor(false);
+
+	// Suspect's clue notes inside _GalleryNoteRect
+	// = (78, 93, 288, 152), per 29be:0100. Cyan text
+	// renders directly on the PDA's natural blue
+	// screen ??? matches `_DrawGalleryNotes @ 158f:01f4`.
+	const int rx = 78, ry = 93;
+	const int rw = 288 - 78, rh = 152 - 93;
+	const int lineH = _font.getFontHeight();
+	const uint clueMax = floppyMI ? clueCount : 30u;
+	const byte *ni = _mystery.noteIndex();
+	const uint16 niCount = _mystery.noteIndexCount();
+
+	// Pagination matches `_DrawNotes @ 161e:01d0` / `MoreInfo @
+	// 158f:0419`: the original tracks `_NextClue` across redraws and
+	// only emits clues whose wrapped height fits the rect, deferring
+	// the rest to the next page. `_PageBreaks[]` stores the
+	// entry-point for each page so PREV (button 6 / case 6 at
+	// 158f:03b8 ??? `SUB _CurrentPage, 2; _NextClue = _PageBreaks[...]`)
+	// can step back. We mirror that with an explicit stack of
+	// page-start indices: forward push, back pop.
+	uint pageStart = 0;
+	Common::Array<uint> pageStack;
+	bool back = false;
+	bool exitGallery = false;
+	bool isFirstShow = true;
+
+	while (!back && !shouldQuit()) {
+		Graphics::ManagedSurface ms(320, 200,
+			Graphics::PixelFormat::createFormatCLUT8());
+		ms.clear();
+		if (haveBg) {
+			const int bw = MIN<int>(galBg.surface.w, 320);
+			const int bh = MIN<int>(galBg.surface.h, 200);
+			for (int row = 0; row < bh; row++) {
+				memcpy((byte *)ms.getBasePtr(0, row),
+					   (const byte *)galBg.surface.getBasePtr(0, row), bw);
+			}
+		}
+		// Partner sprite at (5, 0x50). The original `MoreInfo @
+		// 158f:0419` calls `_RefreshGalleryBackground` (clears the
+		// portrait grid) but the partner anim slot registered by
+		// `_DoGallery` keeps painting at every `_UpdateAnimations`
+		// tick ??? the suspect detail pic covers the right side only
+		// (drawn at 0x94, 0xf), so the partner stays visible on the
+		// left. Re-blit per page so the frame stays current.
+		{
+			const uint partnerAnim = (_partner == 0) ? 2 : 0x10;
+			Animation partnerAni;
+			if (_aniArchive.loadAnimation(partnerAnim, partnerAni) &&
+				!partnerAni.empty()) {
+				const uint32 now = g_system->getMillis();
+				const uint frameIdx = partnerFrameAtTick(0x02,
+					(uint)partnerAni.size(), now);
+				blitAnimFrameAnchored(ms.surfacePtr(),
+									  partnerAni[frameIdx], 5, 0x50);
+			}
+		}
+		// Full suspect picture at (0x94, 0xf).
+		Picture detail;
+		if (_picsArchive.getPicture(detailPic, detail)) {
+			const byte transp = (byte)(detail.flags >> 8);
+			const int dx = 0x94, dy = 0x0f;
+			const int dw = MIN<int>(detail.surface.w, 320 - dx);
+			const int dh = MIN<int>(detail.surface.h, 200 - dy);
+			for (int row = 0; row < dh; row++) {
+				const byte *src = (const byte *)
+					detail.surface.getBasePtr(0, row);
+				byte *dst = (byte *)ms.getBasePtr(0, dy + row);
+				for (int col = 0; col < dw; col++) {
+					if (src[col] != transp)
+						dst[dx + col] = src[col];
+				}
+			}
+		}
+
+		// Walk the clue list from `pageStart`. Skip clues that aren't
+		// found / lack a note entry, then measure the wrapped height
+		// before drawing. If a clue would overflow and we've already
+		// drawn at least one, defer it to the next page; if it's the
+		// FIRST clue on this page (one clue too tall to ever fit),
+		// draw it anyway so progress is guaranteed.
+		int yPos = ry;
+		bool drewAny = false;
+		uint k = pageStart;
+		bool reachedEnd = false;
+		for (; k < clueCount && k < clueMax; k++) {
+			const uint16 clueId = floppyMI
+				? (uint16)suspect[5 + k]
+				: READ_LE_UINT16(suspect + 0xa + k * 2);
+			if (!floppyMI && clueId == 0xFFFF) {
+				reachedEnd = true;
+				break;
+			}
+			if (clueId >= Mystery::kCluesFoundCap ||
+				!_mystery._cluesFound[clueId])
+				continue;
+			if (!ni || clueId >= niCount)
+				continue;
+			// Floppy notes: 7-byte entries (+0..1 clue text, +2..3 Jake
+			// spoken, +4..5 Jenny spoken, +6 score). Notebook always
+			// uses +0 (partner-agnostic statement) per `FUN_15e0_01e8`.
+			// CD notes are 4-byte: u16 textOff + u16 score.
+			Common::String txt;
+			if (floppyMI) {
+				const uint16 textOff = READ_LE_UINT16(ni + clueId * 7);
+				const byte *bb = _mystery.blobAt(0);
+				const uint32 dsz = _mystery.dataSize();
+				if (bb && textOff != 0 && textOff < dsz) {
+					const char *p = (const char *)(bb + textOff);
+					uint32 len = 0;
+					while (textOff + len < dsz && p[len] != 0)
+						len++;
+					txt = parseString(Common::String(p, len),
+									  _playerName, _partner);
+				}
+			} else {
+				const uint16 textOff = READ_LE_UINT16(ni + clueId * 4);
+				txt = parseString(_mystery.textAt(textOff),
+								  _playerName, _partner);
+			}
+			if (txt.empty())
+				continue;
+
+			Common::Array<Common::String> wrapped;
+			_font.wordWrapText(txt, MAX<int>(8, rw), wrapped);
+			const int hClue = (int)wrapped.size() * lineH;
+			if (yPos + hClue > ry + rh && drewAny) {
+				// Defer to next page.
+				break;
+			}
+			const byte color = _mystery._noteSelected[clueId]
+								   ? 0x3C : 0x5C;
+			for (uint l = 0; l < wrapped.size(); l++) {
+				_font.drawString(&ms, wrapped[l], rx,
+					yPos + (int)l * lineH, MAX<int>(8, rw), color);
+			}
+			yPos += hClue + 7;
+			drewAny = true;
+		}
+		if (k >= clueCount || k >= clueMax)
+			reachedEnd = true;
+		const uint pageEnd = k;
+		const bool hasMore = !reachedEnd;
+		const bool hasPrev = !pageStack.empty();
+
+		if (pageStart == 0 && !drewAny && _font.isLoaded()) {
+			_font.drawString(&ms,
+				isSpanish()
+					? "Aun no hay pistas para este sospechoso."
+					: "No clues yet for this suspect.",
+				rx, ry, MAX<int>(8, rw), 0x5C);
+		}
+		// Header / footer text. The PDA's NEXT (case 5) and PREV
+		// (case 6) icons are visible on the bottom bar ??? the footer
+		// just covers dismissal here.
+		if (_font.isLoaded()) {
+			_font.drawString(&ms,
+				isSpanish() ? "EXPEDIENTE" : "SUSPECT FILE",
+				rx, ry - 11, MAX<int>(8, rw), 0x3C);
+			_font.drawString(&ms,
+				isSpanish() ? "(ESC: volver)" : "(ESC: back)",
+				rx, ry + rh + 2, MAX<int>(8, rw), 0x3C);
+		}
+		g_system->copyRectToScreen(ms.getPixels(), ms.pitch,
+			0, 0, 320, 200);
+		g_system->updateScreen();
+
+		// Drain the queued LBUTTONDOWN that brought us into MoreInfo
+		// so it doesn't get caught by the input loop below. Subsequent
+		// pages don't need this.
+		if (isFirstShow) {
+			isFirstShow = false;
+			g_system->delayMillis(150);
+			Common::Event drain;
+			while (g_system->getEventManager()->pollEvent(drain)) {
+				if (drain.type == Common::EVENT_QUIT ||
+					drain.type == Common::EVENT_RETURN_TO_LAUNCHER) {
+					_nextScreen = kScreenInvalid;
+					return true;
+				}
+			}
+		}
+
+		// Wait for input. Each PDA button maps to a distinct action,
+		// mirroring `_HandleMoreButton @ 158f:027d`:
+		//   case 0 NOTEBOOK    -> NextScreen=4
+		//   case 1 HELP        -> _InterfaceHelp(0)
+		//   case 2 GALLERY     -> close MoreInfo
+		//   case 3 PARTNER head-> _KDHelp + redraw
+		//   case 4 ACCUSE      -> NextScreen=7
+		//   case 5 PAGE NEXT   -> next page
+		//   case 6 PAGE PREV   -> prev page
+		//   case 7 MAP         -> NextScreen=2
+		//   case 8 SITE        -> no-op
+		//   case 10 HELP (alt) -> _InterfaceHelp(0)
+		// Clicks outside any known button rect are no-ops, matching
+		// the original (which short-circuits on `_FindButton == -1`).
+		// ESC closes; PgDn / PgUp / Return / Backspace also paginate
+		// as a modern accessibility convenience.
+		bool advance = false;
+		bool prev = false;
+		bool redraw = false;
+		while (!back && !advance && !prev && !redraw && !shouldQuit()) {
+			Common::Event e2;
+			while (g_system->getEventManager()->pollEvent(e2)) {
+				if (e2.type == Common::EVENT_QUIT ||
+					e2.type == Common::EVENT_RETURN_TO_LAUNCHER) {
+					_nextScreen = kScreenInvalid;
+					return true;
+				}
+				if (e2.type == Common::EVENT_KEYDOWN &&
+					e2.kbd.keycode == Common::KEYCODE_ESCAPE) {
+					back = true;
+					break;
+				}
+				if (e2.type == Common::EVENT_LBUTTONDOWN) {
+					const int mx = e2.mouse.x;
+					const int my = e2.mouse.y;
+					debugC(2, kDebugGfx,
+						"MoreInfo click (%d,%d) hasMore=%d hasPrev=%d",
+						mx, my, (int)hasMore, (int)hasPrev);
+					if (kPdaNotebookRect.contains(mx, my)) {
+						_nextScreen = kScreenNotebook;
+						exitGallery = true;
+						back = true;
+						break;
+					}
+					if (kPdaAccuseRect.contains(mx, my)) {
+						_nextScreen = kScreenAccuse;
+						exitGallery = true;
+						back = true;
+						break;
+					}
+					if (kPdaPartnerFootMapRect.contains(mx, my)) {
+						_nextScreen = kScreenMapAlt;
+						exitGallery = true;
+						back = true;
+						break;
+					}
+					if (kPdaHelpRect.contains(mx, my) ||
+						kPdaHelp2Rect.contains(mx, my)) {
+						// Help overlay; re-render the current page
+						// after it dismisses.
+						setInteractiveMouseCursor(false);
+						doInterfaceHelp(0);
+						redraw = true;
+						break;
+					}
+					if (kPdaGalleryRect.contains(mx, my)) {
+						// Case 2: close MoreInfo and return to the
+						// portrait grid.
+						back = true;
+						break;
+					}
+					if (kPdaPageNextRect.contains(mx, my)) {
+						if (hasMore)
+							advance = true;
+						break;
+					}
+					if (kPdaPagePrevRect.contains(mx, my)) {
+						if (hasPrev)
+							prev = true;
+						break;
+					}
+					if (kPdaPartnerHeadHintRect.contains(mx, my)) {
+						// Case 3: ask KD for a hint then redraw the
+						// current page over the clean BG.
+						setInteractiveMouseCursor(false);
+						doHelp();
+						redraw = true;
+						break;
+					}
+					// Case 8 SITE and click outside any known button
+					// = no-op. The original `_FindButton`
+					// short-circuits when no rect matches.
+					break;
+				}
+				if (e2.type == Common::EVENT_KEYDOWN && hasMore &&
+					(e2.kbd.keycode == Common::KEYCODE_RETURN ||
+					 e2.kbd.keycode == Common::KEYCODE_KP_ENTER ||
+					 e2.kbd.keycode == Common::KEYCODE_SPACE ||
+					 e2.kbd.keycode == Common::KEYCODE_PAGEDOWN ||
+					 e2.kbd.keycode == Common::KEYCODE_RIGHT)) {
+					advance = true;
+					break;
+				}
+				if (e2.type == Common::EVENT_KEYDOWN && hasPrev &&
+					(e2.kbd.keycode == Common::KEYCODE_BACKSPACE ||
+					 e2.kbd.keycode == Common::KEYCODE_LEFT ||
+					 e2.kbd.keycode == Common::KEYCODE_PAGEUP)) {
+					prev = true;
+					break;
+				}
+			}
+			// Per-tick `updateScreen()` so the SDL cursor follows the
+			// mouse ??? without it the cursor freezes on entry to the
+			// MoreInfo screen.
+			g_system->updateScreen();
+			g_system->delayMillis(20);
+		}
+
+		if (advance) {
+			pageStack.push_back(pageStart);
+			pageStart = pageEnd;
+		} else if (prev && !pageStack.empty()) {
+			pageStart = pageStack.back();
+			pageStack.pop_back();
+		}
+		// `redraw` falls through with the same `pageStart` and
+		// re-iterates the outer while, repainting on top of whatever
+		// the help overlay left behind.
+	}
+
+	return exitGallery;
+}
+
 void EEMEngine::drawGalleryFrame(const byte *gd, uint8 numSuspects,
 								  Common::Array<Common::Rect> &slotRects,
 								  Common::Array<int> &slotSuspect) {


Commit: b25891a6870885f6b151bcfade71f373976f243d
    https://github.com/scummvm/scummvm/commit/b25891a6870885f6b151bcfade71f373976f243d
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:21:07+02:00

Commit Message:
EEM: openMainMenuDialog when using ESC in most of the screens

Changed paths:
    engines/eem/site.cpp
    engines/eem/ui.cpp


diff --git a/engines/eem/site.cpp b/engines/eem/site.cpp
index f48d44dc276..8b4c6c3fb25 100644
--- a/engines/eem/site.cpp
+++ b/engines/eem/site.cpp
@@ -817,20 +817,15 @@ void SiteScreen::run() {
 
 			case Common::EVENT_KEYDOWN:
 				notePartnerActivity();
-				// `_DoSiteLoop @ 168d:07e1` only dispatches on the
-				// 6-entry table at `168d:09d5` (TAB / ENTER / arrow
-				// keys for hotspot cursor cycling) plus ESC handled
-				// separately at 168d:07a9. We don't implement the
-				// hotspot cursor cycling, so the only keyboard binding kept
-				// here is ESC (matches `_ESCHit` -> "Are you sure?" -> MAP).
+				// `_DoSiteLoop @ 168d:07e1` originally routed ESC
+				// through `_ESCHit` -> "Are you sure?" -> MAP. The
+				// site has visible MAP / SETUP / etc. controls along
+				// the top bar, so ESC now opens the ScummVM in-game
+				// menu (save / load / quit) instead of the back-nav
+				// shortcut.
 				if (event.kbd.keycode == Common::KEYCODE_ESCAPE) {
 					_vm->setHotspotMouseCursor(false);
-					if (_vm->areYouSure()) {
-						_vm->setNextScreen(_vm->isFloppy() ? kScreenMapAlt
-														   : kScreenMap);
-						_vm->stopMusic();
-						return;
-					}
+					_vm->openMainMenuDialog();
 					enter(cur, false);
 					mouse = g_system->getEventManager()->getMousePos();
 					updateHotspotCursor(cur, mouse.x, mouse.y);
diff --git a/engines/eem/ui.cpp b/engines/eem/ui.cpp
index 696a5e389d8..fc49dbbf559 100644
--- a/engines/eem/ui.cpp
+++ b/engines/eem/ui.cpp
@@ -1934,9 +1934,8 @@ void EEMEngine::doActionScreen() {
 				continue;
 			const Common::KeyCode k = ev.kbd.keycode;
 			if (k == Common::KEYCODE_ESCAPE) {
-				exitChosen = true;
-				confirmed = true;
-				break;
+				openMainMenuDialog();
+				continue;
 			}
 			if (k == Common::KEYCODE_RETURN ||
 				k == Common::KEYCODE_KP_ENTER) {
@@ -2173,6 +2172,13 @@ void EEMEngine::doCaseSelection() {
 					const uint idx = topRow + (uint)row;
 					if (idx >= listLen || solvedFlags[idx])
 						continue;
+					// Second click on the already-selected row counts as
+					// the OK button ??? saves the player a trip down to the
+					// bottom-bar after picking a mystery.
+					if (idx == selRow) {
+						confirmed = true;
+						break;
+					}
 					selRow = idx;
 					dirty = true;
 					continue;
@@ -2183,8 +2189,8 @@ void EEMEngine::doCaseSelection() {
 				continue;
 			const Common::KeyCode k = ev.kbd.keycode;
 			if (k == Common::KEYCODE_ESCAPE) {
-				_mystery.clear();
-				return;
+				openMainMenuDialog();
+				continue;
 			}
 			if (k == Common::KEYCODE_RETURN ||
 				k == Common::KEYCODE_KP_ENTER) {
@@ -2352,9 +2358,8 @@ void EEMEngine::doNotebook() {
 			}
 			if (ev.type == Common::EVENT_KEYDOWN) {
 				if (ev.kbd.keycode == Common::KEYCODE_ESCAPE) {
-					_nextScreen = kScreenSite;
-					exitFlag = true;
-					break;
+					openMainMenuDialog();
+					continue;
 				}
 				if (ev.kbd.keycode == Common::KEYCODE_LEFT ||
 					ev.kbd.keycode == Common::KEYCODE_PAGEUP) {
@@ -2724,9 +2729,8 @@ void EEMEngine::doGallery() {
 			}
 			if (ev.type == Common::EVENT_KEYDOWN) {
 				if (ev.kbd.keycode == Common::KEYCODE_ESCAPE) {
-					_nextScreen = kScreenSite;
-					exitFlag = true;
-					break;
+					openMainMenuDialog();
+					continue;
 				}
 			}
 			if (ev.type == Common::EVENT_LBUTTONDOWN) {
@@ -3080,7 +3084,8 @@ bool EEMEngine::moreInfo(const byte *gd, uint suspectIdx,
 				}
 				if (e2.type == Common::EVENT_KEYDOWN &&
 					e2.kbd.keycode == Common::KEYCODE_ESCAPE) {
-					back = true;
+					openMainMenuDialog();
+					redraw = true;
 					break;
 				}
 				if (e2.type == Common::EVENT_LBUTTONDOWN) {
@@ -3386,8 +3391,10 @@ void EEMEngine::doBigMap() {
 					ev.type == Common::EVENT_RETURN_TO_LAUNCHER)
 					return;
 				if (ev.type == Common::EVENT_KEYDOWN &&
-					ev.kbd.keycode == Common::KEYCODE_ESCAPE)
-					return;
+					ev.kbd.keycode == Common::KEYCODE_ESCAPE) {
+					openMainMenuDialog();
+					continue;
+				}
 				if (ev.type == Common::EVENT_LBUTTONDOWN) {
 					// SetupButtonRect ??? `_NextScreen = 6` (the original's
 					// settings screen, mirrors `_DoBigMap @ 20fe:0c33`
@@ -3503,8 +3510,9 @@ void EEMEngine::doBigMap() {
 				}
 				if (ev.type == Common::EVENT_KEYDOWN) {
 					if (ev.kbd.keycode == Common::KEYCODE_ESCAPE) {
-						setInteractiveMouseCursor(false);
-						return;  // exit detail back to caller (site loop / engine)
+						openMainMenuDialog();
+						dirty = true;
+						continue;
 					}
 					const int kStep = 16;
 					if (ev.kbd.keycode == Common::KEYCODE_LEFT) {
@@ -4144,8 +4152,9 @@ bool EEMEngine::doAccuseNotes() {
 			}
 			if (ev.type == Common::EVENT_KEYDOWN) {
 				if (ev.kbd.keycode == Common::KEYCODE_ESCAPE) {
-					_nextScreen = kScreenSite;
-					return false;
+					openMainMenuDialog();
+					dirty = true;
+					continue;
 				}
 				if (ev.kbd.keycode == Common::KEYCODE_LEFT &&
 					page > 0) {
@@ -4666,7 +4675,9 @@ void EEMEngine::doAccuse() {
 			if (ev.type == Common::EVENT_KEYDOWN) {
 				switch (ev.kbd.keycode) {
 				case Common::KEYCODE_ESCAPE:
-					return;
+					openMainMenuDialog();
+					dirty = true;
+					continue;
 				case Common::KEYCODE_TAB:
 				case Common::KEYCODE_RIGHT:
 					highlighted = nextLiveSlot(slotRects, highlighted, +1);
@@ -5446,8 +5457,11 @@ void EEMEngine::doAccuseFloppy() {
 				ev.type == Common::EVENT_RETURN_TO_LAUNCHER)
 				return;
 			if (ev.type == Common::EVENT_KEYDOWN) {
-				if (ev.kbd.keycode == Common::KEYCODE_ESCAPE)
-					return;
+				if (ev.kbd.keycode == Common::KEYCODE_ESCAPE) {
+					openMainMenuDialog();
+					drawGallery(highlighted, slotRects, slotSuspect);
+					continue;
+				}
 				if (ev.kbd.keycode == Common::KEYCODE_TAB ||
 					ev.kbd.keycode == Common::KEYCODE_RIGHT) {
 					highlighted = (highlighted + 1) % MAX<int>(1, (int)num);


Commit: a6c48ae179ca0ad35dd46f346abe8a9ddd19aced
    https://github.com/scummvm/scummvm/commit/a6c48ae179ca0ad35dd46f346abe8a9ddd19aced
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:21:07+02:00

Commit Message:
EEM: improved animation precision with correct framerate and full script table

Changed paths:
    engines/eem/site.cpp


diff --git a/engines/eem/site.cpp b/engines/eem/site.cpp
index 8b4c6c3fb25..a6a7647c956 100644
--- a/engines/eem/site.cpp
+++ b/engines/eem/site.cpp
@@ -251,6 +251,41 @@ const AnimScript kAnimScripts[] = {
 	// 0x16 (29be:185e, alias of 0x00) ??? Jenny CaseSelection greeter,
 	// same blink script as 0x15.
 	{ 0x16, 10, { 0,0,0,0,0,0,0,0,0,2 } },
+	// Site / drop scripts ???28 frames ??? see `_AnimationSequences @
+	// 29be:22d4`. Many of these are short count-ups used by ambient
+	// animations (people walking, vehicles passing) that the original
+	// drives one entry per `_CheckFrameRate` tick (~140 ms). Without
+	// these, our generic fallback cycles through every animation cell
+	// at one entry per tick and the ambient anims look 2-3?? too fast.
+	// 0x1b (29be:192e, alias of 0x07) ??? walk-cycle 0..9.
+	{ 0x1b, 10, { 0,1,2,3,4,5,6,7,8,9 } },
+	// 0x1c (29be:21a8) ??? short 6-frame count-up.
+	{ 0x1c,  6, { 0,1,2,3,4,5 } },
+	// 0x1d (29be:21a8, alias of 0x1c).
+	{ 0x1d,  6, { 0,1,2,3,4,5 } },
+	// 0x21 (29be:1b86) ??? paired-step idle bob.
+	{ 0x21, 14, { 0,0,0,1,1,2,2,3,3,3,2,2,1,1 } },
+	// 0x25 (29be:218a) ??? 3-frame trigger.
+	{ 0x25,  3, { 0,1,2 } },
+	// 0x26 (29be:1d3e) ??? count-up 0..17 (18 frames).
+	{ 0x26, 18, { 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17 } },
+	// 0x27 (29be:1d64) ??? count-up 0..11 (12 frames).
+	{ 0x27, 12, { 0,1,2,3,4,5,6,7,8,9,10,11 } },
+	// 0x2a (29be:1e50) ??? 5-step micro-anim.
+	{ 0x2a,  5, { 0,1,2,2,3 } },
+	// 0x2e (29be:21ce) ??? count-up 0..12 (13 frames).
+	{ 0x2e, 13, { 0,1,2,3,4,5,6,7,8,9,10,11,12 } },
+	// 0x2f (29be:21ea) ??? count-up 0..22 (23 frames).
+	{ 0x2f, 23, { 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,
+				  20,21,22 } },
+	// 0x32 (29be:219c) ??? count-up 0..4 (5 frames).
+	{ 0x32,  5, { 0,1,2,3,4 } },
+	// 0x33 (29be:2192) ??? count-up 0..3 (4 frames).
+	{ 0x33,  4, { 0,1,2,3 } },
+	// 0x34 (29be:219c, alias of 0x32).
+	{ 0x34,  5, { 0,1,2,3,4 } },
+	// 0x35 (29be:21b6) ??? count-up 0..10 (11 frames).
+	{ 0x35, 11, { 0,1,2,3,4,5,6,7,8,9,10 } },
 	// Briefing animations ??? `_DoInitClues @ 1a35:0411` calls
 	// `_NewAnimation(..., (PicData *)CONCAT22(0x17, ...), 1, ...)`
 	// for the game animation (always anim ID 0x17 ??? even Jenny's
@@ -265,29 +300,228 @@ const AnimScript kAnimScripts[] = {
 };
 static_assert(true, "see kAnimScriptsLong below for >28-frame scripts");
 
-// Scripts longer than 28 frames live here so the main `kAnimScripts`
-// table can keep its tight `frames[28]` storage (the lookup in
-// `findAnimScript` checks both arrays). Used for the briefing
-// animations whose original scripts run 30 frames each.
+// Scripts longer than 28 frames live here. The lookup in
+// `findAnimScript` checks both arrays. Stored as
+// `(seqnum, len, ptr)` so each script can be any length without
+// bloating every entry ??? the longest (0x22) runs 115 frames.
 struct AnimScriptLong {
 	uint16 seqnum;
-	uint8 len;
-	uint8 frames[36];
+	uint16 len;
+	const uint8 *frames;
+};
+
+// Briefing animations ??? `_DoInitClues @ 1a35:0411` calls
+// `_NewAnimation(..., (PicData *)CONCAT22(0x17, ...), 1, ...)` for the
+// game animation (always anim ID 0x17 ??? even Jenny's briefing reuses
+// Jake's SCRIPT, even though the loaded ANI.DBD cells come from her
+// partner-specific entry 0x3b). Same pattern for book (0x18) / nancy
+// (0x19).
+static const uint8 kScript17[] = {
+	0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,
+	20,21,22,23,24,25,26,27,28,29
+};
+static const uint8 kScript18[] = {
+	0,1,2,3,4,5,6,7,8,8,8,8,8,8,8,8,
+	8,8,8,8,8,8,8,9,10,11,12,13,14,15
+};
+static const uint8 kScript19[] = {
+	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+	1,2,3,4,5,6,7,8,9,10,11,12
+};
+
+// Site / NPC drop scripts (29be:22d4 entries 0x1a..0x36 minus the
+// short ones that fit in `kAnimScripts`). Many entries deliberately
+// repeat the same frame several times ??? that's the original's
+// "frame-hold" mechanism (the per-tick walk advances exactly one
+// entry, so K repeats hold the frame for K * `kFramePeriodMs` ???
+// K * 140 ms). Without these scripts our generic fallback cycles
+// through every animation cell at one entry per tick, which is the
+// "site animations run too fast" symptom.
+
+// 0x1a (29be:19a4) ??? count-up 0..7, long idle hold, repeat 1..7,
+// idle, mirror 7..0, idle (77 entries).
+static const uint8 kScript1a[] = {
+	0,1,2,3,4,5,6,7,
+	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+	1,2,3,4,5,6,7,
+	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+	7,6,5,4,3,2,1,
+	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+};
+// 0x1e (29be:1a40) ??? slow walk-stutter with idle tail (76 entries).
+static const uint8 kScript1e[] = {
+	0,1,2,3,3,3,3,4,4,3,4,4,4,4,4,3,
+	5,5,5,5,5,5,5,5,4,4,4,4,4,4,4,4,
+	5,5,5,5,5,5,6,5,6,5,7,7,7,7,7,7,
+	7,8,7,7,7,7,7,8,
+	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+};
+// 0x1f (29be:1ada) ??? 0..5, idle, 0..5, idle, 6..8 alternation,
+// idle (50 entries).
+static const uint8 kScript1f[] = {
+	0,1,2,3,4,5,
+	0,0,0,0,
+	1,2,3,4,5,
+	0,0,0,0,0,
+	6,7,8,8,8,7,6,7,8,8,8,7,
+	6,7,8,8,8,7,6,7,8,8,8,7,
+	6,
+	0,0,0,0,0
+};
+// 0x20 (29be:1b40) ??? count-up 0..33 (34 frames).
+static const uint8 kScript20[] = {
+	0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,
+	20,21,22,23,24,25,26,27,28,29,30,31,32,33
+};
+// 0x22 (29be:1ba4) ??? long held-frame walker 0..22 with idle tail
+// (115 entries; most frames held 4-7 ticks each).
+static const uint8 kScript22[] = {
+	0,
+	1,1,1,1,1,
+	2,2,2,2,2,
+	3,3,3,3,3,
+	4,4,4,4,
+	5,5,5,5,
+	6,6,6,6,
+	7,7,7,7,
+	8,8,8,8,
+	9,9,9,9,
+	10,10,10,10,10,
+	11,11,11,11,11,
+	12,12,12,12,12,12,
+	13,13,13,13,13,
+	14,14,14,14,
+	15,15,15,15,
+	16,16,16,16,16,16,16,
+	17,17,17,17,
+	18,18,18,18,
+	19,19,19,19,
+	20,20,20,20,
+	21,21,21,21,
+	22,22,22,22,
+	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+};
+// 0x23 (29be:1c8c) ??? 29 entries: 0, 6 holds of 1, count-up 2..4,
+// down-up gesture, 5 idle frames.
+static const uint8 kScript23[] = {
+	0,1,1,1,1,1,1,
+	2,3,4,3,2,
+	5,5,5,
+	2,3,4,3,3,3,3,3,
+	0,0,0,0,0,0
 };
+// 0x24 (29be:1cc8) ??? bell-curve hold (58 entries): 0,0, 1,1, 2,2,
+// 3 held for 26 ticks, mirror back, idle.
+static const uint8 kScript24[] = {
+	0,0,1,1,2,2,
+	3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
+	2,2,1,1,
+	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+};
+// 0x28 (29be:1d7e) ??? gentle hold 0..3 with long hold on 3, mirror
+// back, idle (45 entries).
+static const uint8 kScript28[] = {
+	0,1,1,2,2,
+	3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
+	2,2,1,1,
+	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+};
+// 0x29 (29be:1dda) ??? paired-step count-up 0..21 plus idle
+// (58 entries).
+static const uint8 kScript29[] = {
+	0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,
+	11,11,12,12,13,13,14,14,15,15,16,16,17,17,18,18,19,19,
+	20,20,21,21,
+	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+};
+// 0x2b (29be:1e5c) ??? count-up 0..11 with each frame held 4 ticks
+// (48 entries).
+static const uint8 kScript2b[] = {
+	0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,
+	4,4,4,4,5,5,5,5,6,6,6,6,7,7,7,7,
+	8,8,8,8,9,9,9,9,10,10,10,10,11,11,11,11
+};
+// 0x2c (29be:1ebe) ??? alternation walk 0..19 with idle tail
+// (54 entries).
+static const uint8 kScript2c[] = {
+	0,1,2,3,4,5,
+	0,
+	6,7,8,9,10,10,10,10,10,10,
+	11,11,11,
+	12,13,14,15,
+	0,
+	16,17,18,19,
+	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+};
+// 0x2d (29be:1f2c) ??? count-up 0..11 with each frame held 8 ticks
+// (96 entries).
+static const uint8 kScript2d[] = {
+	0,0,0,0,0,0,0,0,
+	1,1,1,1,1,1,1,1,
+	2,2,2,2,2,2,2,2,
+	3,3,3,3,3,3,3,3,
+	4,4,4,4,4,4,4,4,
+	5,5,5,5,5,5,5,5,
+	6,6,6,6,6,6,6,6,
+	7,7,7,7,7,7,7,7,
+	8,8,8,8,8,8,8,8,
+	9,9,9,9,9,9,9,9,
+	10,10,10,10,10,10,10,10,
+	11,11,11,11,11,11,11,11
+};
+// 0x30 (29be:1fee) ??? 0,0, count-up 1..19, idle, mirror down, extra
+// idle (86 entries).
+static const uint8 kScript30[] = {
+	0,0,
+	1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,
+	0,0,0,0,0,0,0,0,0,0,
+	19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,
+	5,4,4,3,3,2,2,1,1,
+	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+};
+// 0x31 (29be:209c) ??? paired-step idle alternations (57 entries).
+static const uint8 kScript31[] = {
+	0,0,0,1,1,1,
+	0,0,0,1,1,1,
+	2,2,2,3,3,3,
+	2,2,2,3,3,3,
+	4,4,4,5,5,5,
+	4,4,4,5,5,5,
+	3,3,3,2,2,2,
+	3,3,3,2,2,2,
+	1,1,1,
+	0,0,0,
+	1,1,1
+};
+// 0x36 (29be:2110) ??? 0..8 forward, 1..8 forward, frame 1 held 20
+// ticks, 8..0 mirror, idle tail (60 entries).
+static const uint8 kScript36[] = {
+	0,1,2,3,4,5,6,7,8,
+	1,2,3,4,5,6,7,8,
+	1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+	8,7,6,5,4,3,2,1,
+	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+};
+
 const AnimScriptLong kAnimScriptsLong[] = {
-	// 0x17 (29be:221a) ??? briefing game count-up 0..29 (30 frames),
-	// drives the per-tick frame walk of the game piece animation
-	// during `_DoInitClues`.
-	{ 0x17, 30, { 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,
-				  20,21,22,23,24,25,26,27,28,29 } },
-	// 0x18 (29be:2296) ??? briefing book: counts up to cell 8 then
-	// holds for 16 ticks (the "thinking" pose) then count up 9..15.
-	{ 0x18, 30, { 0,1,2,3,4,5,6,7,8,8,8,8,8,8,8,8,
-				  8,8,8,8,8,8,8,9,10,11,12,13,14,15 } },
-	// 0x19 (29be:2258) ??? briefing nancy: 18 idle ticks then
-	// count-up 1..12 (the late-arriving sidekick pose).
-	{ 0x19, 30, { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-				  1,2,3,4,5,6,7,8,9,10,11,12 } },
+	{ 0x17, 30,  kScript17 },
+	{ 0x18, 30,  kScript18 },
+	{ 0x19, 30,  kScript19 },
+	{ 0x1a, 77,  kScript1a },
+	{ 0x1e, 76,  kScript1e },
+	{ 0x1f, 50,  kScript1f },
+	{ 0x20, 34,  kScript20 },
+	{ 0x22, 115, kScript22 },
+	{ 0x23, 29,  kScript23 },
+	{ 0x24, 58,  kScript24 },
+	{ 0x28, 45,  kScript28 },
+	{ 0x29, 58,  kScript29 },
+	{ 0x2b, 48,  kScript2b },
+	{ 0x2c, 54,  kScript2c },
+	{ 0x2d, 96,  kScript2d },
+	{ 0x30, 86,  kScript30 },
+	{ 0x31, 57,  kScript31 },
+	{ 0x36, 60,  kScript36 },
 };
 
 // `_PatientSequence` and `_ImpatientSequence` are standalone script
@@ -308,7 +542,7 @@ static const uint32 kImpatienceDelayMs = 60 * 1000;
 // holds).
 struct AnimScriptRef {
 	const uint8 *frames;
-	uint8 len;
+	uint16 len;
 };
 static AnimScriptRef findAnimScript(uint16 seqnum) {
 	for (uint i = 0; i < ARRAYSIZE(kAnimScripts); i++) {
@@ -333,9 +567,17 @@ static AnimScriptRef findAnimScript(uint16 seqnum) {
 	return r;
 }
 
+// Original frame period from `_InitFrameCounter @ 1a35:01ae`:
+// `LastFrame = (cs_within_hour) + 0xe`, with `cs_within_hour` =
+// `((ti_min * 60) + ti_sec) * 100 + ti_hund` (Borland C `struct time`
+// memory order is min, hour, hund, sec). The `+ 0xe` is 14
+// centiseconds ??? ~140 ms per frame, matching `_CheckFrameRate @
+// 1a35:0204`. Earlier 100 ms ran the partner / hotspot animations
+// roughly 1.4?? faster than the original.
+static const uint kFramePeriodMs = 140;
+
 static uint frameFromScriptAtTick(const uint8 *frames, uint len,
 								  uint numFrames, uint32 tickMs) {
-	const uint kFramePeriodMs = 100;
 	if (!frames || len == 0)
 		return numFrames > 0 ? (uint)((tickMs / kFramePeriodMs) % numFrames) : 0;
 	const uint scriptIdx = (uint)((tickMs / kFramePeriodMs) % len);
@@ -474,7 +716,6 @@ uint partnerFrameAtTick(uint16 seqnum, uint numFrames, uint32 tickMs) {
 static uint oneShotThenLoopFrameAtTick(const uint8 *unfold, uint unfoldLen,
 									   const uint8 *waitSeq, uint waitSeqLen,
 									   uint numFrames, uint32 elapsedMs) {
-	const uint kFramePeriodMs = 100;
 	const uint tick = elapsedMs / kFramePeriodMs;
 	const uint frame = (tick < unfoldLen)
 		? unfold[tick]
@@ -849,10 +1090,12 @@ void SiteScreen::run() {
 		// `_UpdateAnimations` at the top of `_DoSiteLoop`'s main loop).
 		// Restore the static BG snapshot, redraw animated NPCs +
 		// partner at the current frame, then re-render hotspots on
-		// top. We tick at 100 ms (~10 FPS) which is in the same ball
-		// park as the original.
+		// top. The original ticks at 14 cs (~140 ms, see
+		// `kFramePeriodMs` above) ??? we matched 100 ms before, which
+		// ran site animations ~1.4?? too fast.
 		const uint32 now = g_system->getMillis();
-		if (_snapshotSite == (int)cur && now - _lastTickMs >= 100) {
+		if (_snapshotSite == (int)cur &&
+			now - _lastTickMs >= kFramePeriodMs) {
 			if (checkImpatienceCounter()) {
 				_partnerWaitMood = kPartnerWaitImpatient;
 				debugC(1, kDebugSite, "Partner impatience: switched to impatient");


Commit: 28556038dfe1d4ec44622e0ffd332ad464b0ff5d
    https://github.com/scummvm/scummvm/commit/28556038dfe1d4ec44622e0ffd332ad464b0ff5d
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:21:08+02:00

Commit Message:
EEM: use simpleBlitFrom

Changed paths:
    engines/eem/clues.cpp
    engines/eem/ui.cpp


diff --git a/engines/eem/clues.cpp b/engines/eem/clues.cpp
index 27089a2696f..c167e3f12d3 100644
--- a/engines/eem/clues.cpp
+++ b/engines/eem/clues.cpp
@@ -744,10 +744,7 @@ void EEMEngine::displayClue(const byte *clueBlock) {
 	{
 		Graphics::Surface *screen = g_system->lockScreen();
 		if (screen) {
-			for (int row = 0; row < 200; row++) {
-				memcpy((byte *)bg.getBasePtr(0, row),
-					   (const byte *)screen->getBasePtr(0, row), 320);
-			}
+			bg.simpleBlitFrom(*screen);
 			g_system->unlockScreen();
 		}
 	}
@@ -844,10 +841,7 @@ void EEMEngine::displayClue(const byte *clueBlock) {
 				break;
 			Graphics::ManagedSurface scratch(320, 200,
 				Graphics::PixelFormat::createFormatCLUT8());
-			for (int row = 0; row < 200; row++) {
-				memcpy((byte *)scratch.getBasePtr(0, row),
-					   (const byte *)screen->getBasePtr(0, row), 320);
-			}
+			scratch.simpleBlitFrom(*screen);
 			g_system->unlockScreen();
 
 			int textX = bubX;
@@ -1035,10 +1029,7 @@ void EEMEngine::displayFloppyDialogRecords(const byte *rec, uint count,
 	{
 		Graphics::Surface *screen = g_system->lockScreen();
 		if (screen) {
-			for (int row = 0; row < 200; row++) {
-				memcpy((byte *)bg.getBasePtr(0, row),
-					   (const byte *)screen->getBasePtr(0, row), 320);
-			}
+			bg.simpleBlitFrom(*screen);
 			g_system->unlockScreen();
 		}
 	}
@@ -1491,10 +1482,7 @@ bool EEMEngine::areYouSure() {
 	Graphics::ManagedSurface saved(320, 200,
 		Graphics::PixelFormat::createFormatCLUT8());
 	if (screen) {
-		for (int row = 0; row < 200; row++) {
-			memcpy((byte *)saved.getBasePtr(0, row),
-				   (const byte *)screen->getBasePtr(0, row), 320);
-		}
+		saved.simpleBlitFrom(*screen);
 		g_system->unlockScreen();
 	}
 
@@ -1538,9 +1526,7 @@ bool EEMEngine::areYouSure() {
 
 		Graphics::ManagedSurface scratch(320, 200,
 			Graphics::PixelFormat::createFormatCLUT8());
-		for (int row = 0; row < 200; row++)
-			memcpy((byte *)scratch.getBasePtr(0, row),
-				   (const byte *)saved.getBasePtr(0, row), 320);
+		scratch.simpleBlitFrom(saved);
 		scratch.fillRect(dlg, 0);
 		scratch.frameRect(dlg, 0xF);
 		_font.drawString(&scratch,
diff --git a/engines/eem/ui.cpp b/engines/eem/ui.cpp
index fc49dbbf559..004e38dda5e 100644
--- a/engines/eem/ui.cpp
+++ b/engines/eem/ui.cpp
@@ -1523,9 +1523,7 @@ void EEMEngine::doSetup() {
 			// transparent pixels show the setup BG underneath.
 			Graphics::Surface *cur = g_system->lockScreen();
 			if (cur) {
-				for (int row = 0; row < 200; row++)
-					memcpy((byte *)scratch.getBasePtr(0, row),
-						   (const byte *)cur->getBasePtr(0, row), 320);
+				scratch.simpleBlitFrom(*cur);
 				g_system->unlockScreen();
 			}
 			const byte transp = (byte)(pic.flags >> 8);
@@ -2902,14 +2900,8 @@ bool EEMEngine::moreInfo(const byte *gd, uint suspectIdx,
 		Graphics::ManagedSurface ms(320, 200,
 			Graphics::PixelFormat::createFormatCLUT8());
 		ms.clear();
-		if (haveBg) {
-			const int bw = MIN<int>(galBg.surface.w, 320);
-			const int bh = MIN<int>(galBg.surface.h, 200);
-			for (int row = 0; row < bh; row++) {
-				memcpy((byte *)ms.getBasePtr(0, row),
-					   (const byte *)galBg.surface.getBasePtr(0, row), bw);
-			}
-		}
+		if (haveBg)
+			ms.simpleBlitFrom(galBg.surface);
 		// Partner sprite at (5, 0x50). The original `MoreInfo @
 		// 158f:0419` calls `_RefreshGalleryBackground` (clears the
 		// portrait grid) but the partner anim slot registered by
@@ -3207,14 +3199,8 @@ void EEMEngine::drawGalleryFrame(const byte *gd, uint8 numSuspects,
 		Graphics::PixelFormat::createFormatCLUT8());
 	scratch.clear();
 
-	if (haveBg) {
-		const int bw = MIN<int>(galBg.surface.w, 320);
-		const int bh = MIN<int>(galBg.surface.h, 200);
-		for (int row = 0; row < bh; row++) {
-			memcpy((byte *)scratch.getBasePtr(0, row),
-				   (const byte *)galBg.surface.getBasePtr(0, row), bw);
-		}
-	}
+	if (haveBg)
+		scratch.simpleBlitFrom(galBg.surface);
 
 	// Partner sprite frame @ (5, 0x50). The original `_DoGallery @
 	// 158f:065b` registers `_NewAnimation(..., CONCAT22(2, ...), ...)`
@@ -4458,9 +4444,7 @@ void EEMEngine::doAccuse() {
 		ms.clear();
 		Graphics::Surface *cur = g_system->lockScreen();
 		if (cur) {
-			for (int row = 0; row < 200; row++)
-				memcpy((byte *)ms.getBasePtr(0, row),
-					   (const byte *)cur->getBasePtr(0, row), 320);
+			ms.simpleBlitFrom(*cur);
 			g_system->unlockScreen();
 		}
 		const byte firstChar =
@@ -4590,20 +4574,14 @@ void EEMEngine::doAccuse() {
 				{
 					Graphics::Surface *cur = g_system->lockScreen();
 					if (cur) {
-						for (int row = 0; row < 200; row++)
-							memcpy((byte *)ms.getBasePtr(0, row),
-								   (const byte *)cur->getBasePtr(0, row), 320);
+						ms.simpleBlitFrom(*cur);
 						g_system->unlockScreen();
 					} else if (haveAccuseBg) {
 						// Fallback: lockScreen failed somehow; at least
 						// fill from PIC 0x3f so we don't render against
-						// stale memory.
-						const int bw = MIN<int>(accuseBg.surface.w, 320);
-						const int bh = MIN<int>(accuseBg.surface.h, 200);
-						for (int row = 0; row < bh; row++) {
-							memcpy((byte *)ms.getBasePtr(0, row),
-								   (const byte *)accuseBg.surface.getBasePtr(0, row), bw);
-						}
+						// stale memory. simpleBlitFrom auto-clips, so
+						// no MIN(w, 320)/MIN(h, 200) needed.
+						ms.simpleBlitFrom(accuseBg.surface);
 					}
 				}
 				// Masked balloon blit ??? `_Rect_Move_Mask` (1000:03fc)
@@ -5892,14 +5870,8 @@ void EEMEngine::drawAccuseGallery(uint8 numSuspects, const byte *gd,
 	Graphics::ManagedSurface scratch(320, 200,
 		Graphics::PixelFormat::createFormatCLUT8());
 	scratch.clear();
-	if (haveAccuseBg) {
-		const int bw = MIN<int>(accuseBg.surface.w, 320);
-		const int bh = MIN<int>(accuseBg.surface.h, 200);
-		for (int row = 0; row < bh; row++) {
-			memcpy((byte *)scratch.getBasePtr(0, row),
-				   (const byte *)accuseBg.surface.getBasePtr(0, row), bw);
-		}
-	}
+	if (haveAccuseBg)
+		scratch.simpleBlitFrom(accuseBg.surface);
 
 	// Partner sprite, drawn BEFORE portraits so the suspect grid
 	// covers it where they overlap (the gallery slots start at


Commit: b47044e739601668ae672ce3192961e01af7d5d2
    https://github.com/scummvm/scummvm/commit/b47044e739601668ae672ce3192961e01af7d5d2
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:21:08+02:00

Commit Message:
EEM: more simpleBlitFrom

Changed paths:
    engines/eem/ui.cpp


diff --git a/engines/eem/ui.cpp b/engines/eem/ui.cpp
index 004e38dda5e..f427f7b57ac 100644
--- a/engines/eem/ui.cpp
+++ b/engines/eem/ui.cpp
@@ -2925,18 +2925,8 @@ bool EEMEngine::moreInfo(const byte *gd, uint suspectIdx,
 		Picture detail;
 		if (_picsArchive.getPicture(detailPic, detail)) {
 			const byte transp = (byte)(detail.flags >> 8);
-			const int dx = 0x94, dy = 0x0f;
-			const int dw = MIN<int>(detail.surface.w, 320 - dx);
-			const int dh = MIN<int>(detail.surface.h, 200 - dy);
-			for (int row = 0; row < dh; row++) {
-				const byte *src = (const byte *)
-					detail.surface.getBasePtr(0, row);
-				byte *dst = (byte *)ms.getBasePtr(0, dy + row);
-				for (int col = 0; col < dw; col++) {
-					if (src[col] != transp)
-						dst[dx + col] = src[col];
-				}
-			}
+			ms.transBlitFrom(detail.surface,
+							 Common::Point(0x94, 0x0f), transp);
 		}
 
 		// Walk the clue list from `pageStart`. Skip clues that aren't
@@ -4588,16 +4578,9 @@ void EEMEngine::doAccuse() {
 				// skips pixels equal to `pic[0] >> 8`.
 				if (haveBalloon) {
 					const byte transp = (byte)(balloon.flags >> 8);
-					const int bw = MIN<int>(balloon.surface.w, 320 - balloonX);
-					const int bh = MIN<int>(balloon.surface.h, 200 - balloonY);
-					for (int row = 0; row < bh; row++) {
-						const byte *src = (const byte *)balloon.surface.getBasePtr(0, row);
-						byte *dst = (byte *)ms.getBasePtr(balloonX, balloonY + row);
-						for (int col = 0; col < bw; col++) {
-							if (src[col] != transp)
-								dst[col] = src[col];
-						}
-					}
+					ms.transBlitFrom(balloon.surface,
+									 Common::Point(balloonX, balloonY),
+									 transp);
 				}
 				// Inset table @ 29be:0875 ??? 1df2:0acb pushes color=0.
 				uint16 tx = 5;


Commit: e40f51e2dc7190b652aabb5a68eb83aee2774ead
    https://github.com/scummvm/scummvm/commit/e40f51e2dc7190b652aabb5a68eb83aee2774ead
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:21:08+02:00

Commit Message:
EEM: use transBlitFrom

Changed paths:
    engines/eem/clues.cpp
    engines/eem/ui.cpp


diff --git a/engines/eem/clues.cpp b/engines/eem/clues.cpp
index c167e3f12d3..4de2cddd03e 100644
--- a/engines/eem/clues.cpp
+++ b/engines/eem/clues.cpp
@@ -857,24 +857,16 @@ void EEMEngine::displayClue(const byte *clueBlock) {
 				// the transparent colour to `_Rect_Move_Mask`. The
 				// on-disk u16 at file offset 0 maps to `Picture::flags`.
 				const byte transp = (byte)(balloon.flags >> 8);
-				// `_GetBalloon @ 172b:1d7d` mirrors the picture horizontally
-				// when `(bubNum & 0x80)` is set ??? used for right-side
-				// speakers so the tail points the other way.
+				// `_GetBalloon @ 172b:1d7d` mirrors the picture
+				// horizontally when `(bubNum & 0x80)` is set ??? used
+				// for right-side speakers so the tail points the
+				// other way. ScummVM's `transBlitFrom` exposes the
+				// same via its `flipped` argument.
 				const bool flipBalloon = (fittedBubNum & 0x80) != 0;
 				if (bw > 0 && bh > 0) {
-					for (int row = 0; row < bh; row++) {
-						const byte *src =
-							(const byte *)balloon.surface.getBasePtr(0, row);
-						byte *dst = (byte *)scratch.getBasePtr(bubX, bubY + row);
-						for (int col = 0; col < bw; col++) {
-							const int srcCol = flipBalloon
-								? (balloon.surface.w - 1 - col)
-								: col;
-							const byte px = src[srcCol];
-							if (px != transp)
-								dst[col] = px;
-						}
-					}
+					scratch.transBlitFrom(balloon.surface,
+										  Common::Point(bubX, bubY),
+										  transp, flipBalloon);
 				}
 				// Per-balloon metadata from `29be:0875` (52 ?? 10 bytes,
 				// indexed by `bubNum & 0x7F`). The original `_DisplayClue`
@@ -1188,7 +1180,6 @@ void EEMEngine::displayFloppyDialogRecords(const byte *rec, uint count,
 		if (haveBalloon)
 			getBalloonInsets(balloonId, textXIns, textYIns, textWidth);
 		const int textX = ballX + textXIns;
-		const int balloonH = haveBalloon ? balloon.surface.h : 200;
 		const int lineH    = _font.getFontHeight();
 
 		// Pagination state ??? `FUN_22dc_05c8`'s text-idx loop uses
@@ -1252,23 +1243,10 @@ void EEMEngine::displayFloppyDialogRecords(const byte *rec, uint count,
 					}
 				}
 				if (haveBalloon) {
-					const int bw = MIN<int>(balloon.surface.w, 320 - ballX);
-					const int bh = MIN<int>(balloonH, 200 - ballY);
 					const byte transp = (byte)(balloon.flags >> 8);
-					for (int row = 0; row < bh; row++) {
-						const byte *src = (const byte *)
-							balloon.surface.getBasePtr(0, row);
-						byte *dst = (byte *)
-							scratch.getBasePtr(ballX, ballY + row);
-						for (int col = 0; col < bw; col++) {
-							const int srcCol = flipBall
-								? (balloon.surface.w - 1 - col)
-								: col;
-							const byte px = src[srcCol];
-							if (px != transp)
-								dst[col] = px;
-						}
-					}
+					scratch.transBlitFrom(balloon.surface,
+										  Common::Point(ballX, ballY),
+										  transp, flipBall);
 				}
 				cursorY = ballY + textYIns;
 			}
diff --git a/engines/eem/ui.cpp b/engines/eem/ui.cpp
index f427f7b57ac..6e9fd72df76 100644
--- a/engines/eem/ui.cpp
+++ b/engines/eem/ui.cpp
@@ -3773,11 +3773,8 @@ void EEMEngine::drawBigMapDetail(int scrollX, int scrollY,
 
 	const int copyW = MIN<int>(mapW - scrollX, kMapWinW);
 	const int copyH = MIN<int>(mapH - scrollY, kMapWinH);
-	for (int row = 0; row < copyH; row++) {
-		memcpy((byte *)scratch.getBasePtr(kMapWinX, kMapWinY + row),
-			   mapPixels.data() + (scrollY + row) * mapW + scrollX,
-			   copyW);
-	}
+	scratch.copyRectToSurface(mapPixels.data() + scrollY * mapW + scrollX,
+							  mapW, kMapWinX, kMapWinY, copyW, copyH);
 
 	// Stamped site buttons. `_StampButtons @ 20fe:0d2f` (CD):
 	//   button = _GetButton(MapData[+0])
@@ -5205,17 +5202,8 @@ void EEMEngine::doAccuseFloppy() {
 			if (h < 0x4e)
 				balloonY = (uint16)((0x50 - h) >> 1);
 			const byte transp = (byte)(balloon.flags >> 8);
-			for (int row = 0; row < balloon.surface.h && balloonY + row < 200;
-				 row++) {
-				const byte *src =
-					(const byte *)balloon.surface.getBasePtr(0, row);
-				byte *dst = (byte *)ms.getBasePtr(0x21, balloonY + row);
-				for (int col = 0; col < balloon.surface.w && 0x21 + col < 320;
-					 col++) {
-					if (src[col] != transp)
-						dst[col] = src[col];
-				}
-			}
+			ms.transBlitFrom(balloon.surface,
+							 Common::Point(0x21, balloonY), transp);
 		}
 		uint16 bx = 5;
 		uint16 by = 4;
@@ -5759,22 +5747,11 @@ void EEMEngine::doAccuseFloppy() {
 		const byte transp = (byte)(balloon.flags >> 8);
 		// `_GetBalloon`'s mirror flag (high bit of the table value)
 		// flips the balloon horizontally ??? the original applies it
-		// inside the blit primitive. We emulate by reading the source
-		// row in reverse.
-		for (int row = 0; row < balloon.surface.h && balloonY + row < 200;
-			 row++) {
-			const byte *src =
-				(const byte *)balloon.surface.getBasePtr(0, row);
-			byte *dst = (byte *)scene.getBasePtr(balloonX, balloonY + row);
-			for (int col = 0;
-				 col < balloon.surface.w && balloonX + col < 320; col++) {
-				const int srcCol = flipBalloon
-					? (balloon.surface.w - 1 - col) : col;
-				const byte px = src[srcCol];
-				if (px != transp)
-					dst[col] = px;
-			}
-		}
+		// inside the blit primitive; ScummVM's `transBlitFrom` exposes
+		// the same via its `flipped` argument.
+		scene.transBlitFrom(balloon.surface,
+							Common::Point(balloonX, balloonY),
+							transp, flipBalloon);
 	}
 	uint16 tx = 5, ty = 4, tw = 155;
 	getBalloonInsets(balloonIdx, tx, ty, tw);


Commit: a2554f53bd0499a73fc939be4c6edd77ebfd1876
    https://github.com/scummvm/scummvm/commit/a2554f53bd0499a73fc939be4c6edd77ebfd1876
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:21:09+02:00

Commit Message:
EEM: stop conversations when you skip the last phrase

Changed paths:
    engines/eem/clues.cpp
    engines/eem/eem.cpp
    engines/eem/eem.h


diff --git a/engines/eem/clues.cpp b/engines/eem/clues.cpp
index 4de2cddd03e..05fd6b64a5d 100644
--- a/engines/eem/clues.cpp
+++ b/engines/eem/clues.cpp
@@ -413,7 +413,7 @@ void EEMEngine::doInitClues() {
 				while (g_system->getEventManager()->pollEvent(ev)) {
 					if (ev.type == Common::EVENT_KEYDOWN &&
 						ev.kbd.keycode == Common::KEYCODE_ESCAPE) {
-						interruptAudio();
+						interruptAudio(/*stopMusicToo=*/false);
 						skip = true;
 						break;
 					}
@@ -540,7 +540,7 @@ void EEMEngine::doInitClues() {
 					while (g_system->getEventManager()->pollEvent(ev)) {
 						if (ev.type == Common::EVENT_KEYDOWN &&
 							ev.kbd.keycode == Common::KEYCODE_ESCAPE) {
-							interruptAudio();
+							interruptAudio(/*stopMusicToo=*/false);
 							skip = true;
 							break;
 						}
@@ -953,8 +953,9 @@ void EEMEngine::displayClue(const byte *clueBlock) {
 						// playing past the dialog dismissal and
 						// bleeds into the next screen (e.g. the
 						// case-briefing voice still talking on the
-						// MAP after ESC).
-						interruptAudio();
+						// MAP after ESC). Site / briefing MIDI
+						// stays ??? only voice + spool halt here.
+						interruptAudio(/*stopMusicToo=*/false);
 						break;
 					}
 					if (ev.type == Common::EVENT_LBUTTONDOWN) {
@@ -987,6 +988,15 @@ void EEMEngine::displayClue(const byte *clueBlock) {
 
 		applyClueSideEffects(c);
 	}
+
+	// `_DisplayClue @ 2404:05e6` lets the per-clue voice bleed past
+	// the last entry's dismissal ??? `_Wait()` returns on ANY input
+	// without touching the digital playback. We deliberately diverge
+	// (mirroring `_StopTheVoice @ 1ff1:0283`'s effect, voice only,
+	// MIDI untouched) so the briefing voice line doesn't keep talking
+	// over the next screen when the player clicks through the final
+	// entry.
+	interruptAudio(/*stopMusicToo=*/false);
 }
 
 void EEMEngine::displayFloppyDialogRecords(const byte *rec, uint count,
@@ -1069,7 +1079,7 @@ void EEMEngine::displayFloppyDialogRecords(const byte *rec, uint count,
 					continue;
 				if (ev.type == Common::EVENT_KEYDOWN &&
 					ev.kbd.keycode == Common::KEYCODE_ESCAPE) {
-					interruptAudio();
+					interruptAudio(/*stopMusicToo=*/false);
 					return true;  // skip
 				}
 				if (ev.type == Common::EVENT_LBUTTONDOWN ||
diff --git a/engines/eem/eem.cpp b/engines/eem/eem.cpp
index 85a533fee88..5f527ba1faf 100644
--- a/engines/eem/eem.cpp
+++ b/engines/eem/eem.cpp
@@ -718,19 +718,19 @@ bool EEMEngine::setAnmPalette(const Common::Path &anmPath) {
 	return true;
 }
 
-void EEMEngine::interruptAudio() {
+void EEMEngine::interruptAudio(bool stopMusicToo) {
 	// Mirrors `_CleanMysterySounds @ 202f:05a5` + `_StopMIDI @
 	// 20a2:0512` ??? the original calls both whenever the player aborts
 	// the opening-anim chain or dismisses the title (`_DoOpeningAnims
 	// @ 2520:082a` writes `_LoopMIDI = 0; _StopMIDI();` after the
-	// title-input loop). We expose the same combined stop on every
-	// ESC handler so currently-playing music + voice + spool actually
-	// halt instead of bleeding through into the next screen.
+	// title-input loop). Conversation / clue-dialog skip paths pass
+	// `stopMusicToo = false` so the site / briefing MIDI keeps going
+	// across an ESC ??? only the per-line voice + spool need to stop.
 	if (_audio) {
 		_audio->stopVoice();
 		_audio->stopSpool();
 	}
-	if (_music)
+	if (stopMusicToo && _music)
 		_music->stop();
 }
 
diff --git a/engines/eem/eem.h b/engines/eem/eem.h
index 8578d1a2d6c..c942013d1cc 100644
--- a/engines/eem/eem.h
+++ b/engines/eem/eem.h
@@ -414,7 +414,12 @@ private:
 	/// and once before TITLE.ANM). Called from every ESC handler in
 	/// the intro / title chain so the theme music + voice spool
 	/// don't bleed past the abort.
-	void interruptAudio();
+	/// Stop currently-playing voice / spooled SFX. Pass `stopMusic =
+	/// true` (the default ??? matches `_CleanMysterySounds + _StopMIDI`)
+	/// to also halt the MIDI track; conversation / dialog skip paths
+	/// pass `false` so the site / briefing music keeps going across an
+	/// ESC.
+	void interruptAudio(bool stopMusicToo = true);
 
 	// Screen handlers ??? port targets in screens/ later.
 	void showEAKidsLogo();


Commit: 9ca6f602c444b6ff8ea1aabca2ffa971bc17aa8f
    https://github.com/scummvm/scummvm/commit/9ca6f602c444b6ff8ea1aabca2ffa971bc17aa8f
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:21:09+02:00

Commit Message:
EEM: fixed highlighting bug when the cursor was moved during conversations

Changed paths:
    engines/eem/clues.cpp
    engines/eem/eem.cpp
    engines/eem/site.cpp


diff --git a/engines/eem/clues.cpp b/engines/eem/clues.cpp
index 05fd6b64a5d..63b384498ca 100644
--- a/engines/eem/clues.cpp
+++ b/engines/eem/clues.cpp
@@ -934,6 +934,14 @@ void EEMEngine::displayClue(const byte *clueBlock) {
 		// are ignored so accidental keystrokes don't blow past dialog
 		// the player hasn't finished reading.
 		if (hasText || (charPicId != 0 && charPicId != 0xFFFF)) {
+			// Click during a clue dialog only dismisses ??? there are no
+			// hover-interactive areas. Drop the highlighted cursor
+			// state the site loop may have left set so the player
+			// doesn't see a "clickable" cursor stuck on top of the
+			// balloon. The post-dialog `MOUSEMOVE` in the site loop
+			// will re-evaluate against hotspots and re-enable as
+			// needed.
+			setInteractiveMouseCursor(false);
 			bool advance = false;
 			bool skipAll = false;
 			while (!advance && !shouldQuit()) {
@@ -944,6 +952,13 @@ void EEMEngine::displayClue(const byte *clueBlock) {
 						advance = true;
 						break;
 					}
+					if (ev.type == Common::EVENT_MOUSEMOVE) {
+						// Keep the cursor non-interactive across moves
+						// inside the dialog (defensive ??? in case some
+						// other code path tries to set it).
+						setInteractiveMouseCursor(false);
+						continue;
+					}
 					if (ev.type == Common::EVENT_KEYDOWN &&
 						ev.kbd.keycode == Common::KEYCODE_ESCAPE) {
 						advance = true;
@@ -1067,6 +1082,11 @@ void EEMEngine::displayFloppyDialogRecords(const byte *rec, uint count,
 		// doesn't auto-advance the new page.
 		Common::Event drain;
 		while (g_system->getEventManager()->pollEvent(drain)) {}
+		// Click during the floppy dialog only dismisses ??? no
+		// hover-interactive areas. Clear any stuck highlight from the
+		// site loop so the cursor stays a normal pointer over the
+		// balloon.
+		setInteractiveMouseCursor(false);
 		const uint32 minVisibleMs = 250;
 		const uint32 startedAt = g_system->getMillis();
 		while (!shouldQuit()) {
@@ -1075,6 +1095,10 @@ void EEMEngine::displayFloppyDialogRecords(const byte *rec, uint count,
 				if (ev.type == Common::EVENT_QUIT ||
 					ev.type == Common::EVENT_RETURN_TO_LAUNCHER)
 					return true;  // skip
+				if (ev.type == Common::EVENT_MOUSEMOVE) {
+					setInteractiveMouseCursor(false);
+					continue;
+				}
 				if (g_system->getMillis() - startedAt < minVisibleMs)
 					continue;
 				if (ev.type == Common::EVENT_KEYDOWN &&
diff --git a/engines/eem/eem.cpp b/engines/eem/eem.cpp
index 5f527ba1faf..de24dbedb90 100644
--- a/engines/eem/eem.cpp
+++ b/engines/eem/eem.cpp
@@ -868,6 +868,14 @@ void EEMEngine::waitForInput(uint32 maxMs) {
 	// Only Return / KP-Enter / Space / Escape advance ??? letting any key
 	// dismiss balloons makes typing-while-reading (or a stuck modifier)
 	// blow past dialog the player hasn't finished reading.
+	//
+	// Drop the highlighted-cursor state any caller (site loop, gallery,
+	// notebook hover-handler) may have left on. While a balloon /
+	// intro is showing, click anywhere just dismisses ??? no
+	// hover-interactive areas ??? so a "clickable" cursor over the
+	// dialog is misleading. The caller's MOUSEMOVE handler will
+	// re-enable the highlight after we return if appropriate.
+	setInteractiveMouseCursor(false);
 	const uint32 startMs = g_system->getMillis();
 	while (!shouldQuit() && (g_system->getMillis() - startMs < maxMs)) {
 		Common::Event event;
@@ -877,6 +885,12 @@ void EEMEngine::waitForInput(uint32 maxMs) {
 				event.type == Common::EVENT_LBUTTONDOWN) {
 				return;
 			}
+			if (event.type == Common::EVENT_MOUSEMOVE) {
+				// Defensive: keep the cursor non-interactive across
+				// moves while the dialog is up.
+				setInteractiveMouseCursor(false);
+				continue;
+			}
 			if (event.type == Common::EVENT_KEYDOWN) {
 				if (event.kbd.keycode == Common::KEYCODE_ESCAPE) {
 					_skipIntro = true;
diff --git a/engines/eem/site.cpp b/engines/eem/site.cpp
index a6a7647c956..b141704a1fe 100644
--- a/engines/eem/site.cpp
+++ b/engines/eem/site.cpp
@@ -1039,7 +1039,12 @@ void SiteScreen::run() {
 					_vm->doHelp();
 					notePartnerActivity();
 					enter(cur, false);
-					updateHotspotCursor(cur, event.mouse.x, event.mouse.y);
+					// Re-evaluate cursor against the CURRENT pointer
+					// position, not the click that opened the help.
+					// The player may have moved off the partner-head
+					// area during the dialog.
+					mouse = g_system->getEventManager()->getMousePos();
+					updateHotspotCursor(cur, mouse.x, mouse.y);
 					break;
 				}
 				const int idx = hotspotAtPoint(cur, event.mouse.x, event.mouse.y);
@@ -1049,7 +1054,12 @@ void SiteScreen::run() {
 					// Restore the site BG after the clue overlay.
 					notePartnerActivity();
 					enter(cur, false);
-					updateHotspotCursor(cur, event.mouse.x, event.mouse.y);
+					// Use CURRENT pointer position ??? the click pos is
+					// still inside the hotspot rect, so reusing it
+					// would leave the "clickable" cursor stuck after
+					// the conversation even if the player moved off.
+					mouse = g_system->getEventManager()->getMousePos();
+					updateHotspotCursor(cur, mouse.x, mouse.y);
 				} else {
 					notePartnerActivity();
 				}


Commit: b39c08a950680c8e7a1e952e4af1756af28102c0
    https://github.com/scummvm/scummvm/commit/b39c08a950680c8e7a1e952e4af1756af28102c0
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:21:09+02:00

Commit Message:
EEM: added palette fadeout for the intro

Changed paths:
    engines/eem/eem.cpp
    engines/eem/eem.h
    engines/eem/site.cpp
    engines/eem/site.h


diff --git a/engines/eem/eem.cpp b/engines/eem/eem.cpp
index de24dbedb90..0af062785e1 100644
--- a/engines/eem/eem.cpp
+++ b/engines/eem/eem.cpp
@@ -686,17 +686,23 @@ bool EEMEngine::loadSitePalettes() {
 	return true;
 }
 
-void EEMEngine::setSitePalette(uint num) {
-	if (num >= kNumSitePals || _sitePals.size() < (num + 1) * kPalSize) {
-		warning("setSitePalette: index %u out of range", num);
-		return;
-	}
+bool EEMEngine::getSitePalette(uint num, byte *out) const {
+	if (num >= kNumSitePals || _sitePals.size() < (num + 1) * kPalSize)
+		return false;
 	// SITEPALS stores 6-bit VGA-DAC values (0..63); ScummVM expects 8-bit
 	// (0..255), so left-shift by 2 like the original VGA hardware did.
 	const byte *src = _sitePals.data() + num * kPalSize;
-	byte expanded[kPalSize];
 	for (uint i = 0; i < kPalSize; i++)
-		expanded[i] = (byte)(src[i] << 2);
+		out[i] = (byte)(src[i] << 2);
+	return true;
+}
+
+void EEMEngine::setSitePalette(uint num) {
+	byte expanded[kPalSize];
+	if (!getSitePalette(num, expanded)) {
+		warning("setSitePalette: index %u out of range", num);
+		return;
+	}
 	g_system->getPaletteManager()->setPalette(expanded, 0, 256);
 }
 
@@ -910,8 +916,18 @@ void EEMEngine::waitForInput(uint32 maxMs) {
 }
 
 void EEMEngine::showEAKidsLogo() {
-	// Mirrors _ShowEAKids @ 2520:05f0 (without the color-cycle loop):
-	// GetPicture(0x54), MemoryCopy to VGA, GetPalette(0x25), setmany.
+	// Mirrors `_ShowEAKids @ 2520:05f0`. The original:
+	//   1. GetPicture(0x54) + MemoryCopy to VGA + GetPalette(0x25).
+	//   2. FRAME_RATE = 25; for j in 0..1, for u in 0..0x36 (= 55):
+	//        OpenColorCycle(0x01, 0x6e)   // bg / outer ring shimmer
+	//        OpenColorCycle(0x81, 0xee)   // inner gradient shimmer
+	//        every 8 ticks: OpenColorCycle(0x70, 0x80)  // mid band
+	//   3. After the 110-tick loop: 5 more cycles of 0x70..0x80.
+	//   4. Wait 0x23 (= 35) more frames.
+	//   5. _OpenFadeOut.
+	// The cycling is what gives the EA Kids logo its characteristic
+	// shifting glow ??? it's NOT a static logo. ESC / click skips the
+	// remaining cycle.
 	Picture pic;
 	if (!_picsArchive.getPicture(kPicEAKidsLogo, pic)) {
 		warning("EA Kids logo (%u) load failed", kPicEAKidsLogo);
@@ -920,21 +936,95 @@ void EEMEngine::showEAKidsLogo() {
 	blitAt(pic, 0, 0);
 	setSitePalette(kPalEAKids);
 	g_system->updateScreen();
-	waitForInput(2500);
+
+	// 25 fps ??? 40 ms / tick. Two outer iterations ?? 55 ticks each = 110
+	// ticks of palette rotation. The first inner-loop iteration of each
+	// outer pass rotates the in-memory palette once *before* applying
+	// to VGA (original gates the wait + setmany on `show != 0`); we
+	// just call the setter every tick ??? the visible cycle starts
+	// immediately, which is the same end result.
+	const uint kFrameMs = 40;
+	int delayCount = 8;
+	bool aborted = false;
+	for (uint outer = 0; outer < 2 && !aborted && !shouldQuit(); outer++) {
+		for (uint i = 0; i < 0x37 && !aborted && !shouldQuit(); i++) {
+			cyclePaletteRangeReverse(0x01, 0x6e);
+			cyclePaletteRangeReverse(0x81, 0xee);
+			delayCount--;
+			if (delayCount == 0) {
+				delayCount = 8;
+				cyclePaletteRangeReverse(0x70, 0x80);
+			}
+			g_system->updateScreen();
+
+			// Tick wait + skip detection.
+			const uint32 frameEnd = g_system->getMillis() + kFrameMs;
+			while (g_system->getMillis() < frameEnd && !aborted) {
+				Common::Event ev;
+				while (g_system->getEventManager()->pollEvent(ev)) {
+					if (ev.type == Common::EVENT_QUIT ||
+						ev.type == Common::EVENT_RETURN_TO_LAUNCHER) {
+						_skipIntro = true;
+						return;
+					}
+					if (ev.type == Common::EVENT_KEYDOWN ||
+						ev.type == Common::EVENT_LBUTTONDOWN) {
+						if (ev.type == Common::EVENT_KEYDOWN &&
+							ev.kbd.keycode == Common::KEYCODE_ESCAPE) {
+							_skipIntro = true;
+						}
+						aborted = true;
+						break;
+					}
+				}
+				g_system->delayMillis(5);
+			}
+		}
+	}
+
+	if (aborted)
+		return;
+
+	// Tail: 5 more rotations of the mid band, then 35 idle frames.
+	for (uint i = 0; i < 5 && !shouldQuit(); i++)
+		cyclePaletteRangeReverse(0x70, 0x80);
+	g_system->updateScreen();
+	waitForInput(0x23 * kFrameMs);
+
+	// `_OpenFadeOut @ 2520:0093` ??? 16 linear steps from current palette
+	// to black. Without this, the EA Kids logo cuts hard to the next
+	// screen.
+	fadeCurrentPaletteToBlack();
 }
 
 void EEMEngine::showHighScoreLogo() {
-	// Mirrors _ShowHScoreLogo @ 2520:0799 (without the wait-loop):
-	// GetPicture(0x20c), MemoryCopy to VGA, GetPalette(0x27), FadeIn.
+	// Mirrors `_ShowHScoreLogo @ 2520:0799`:
+	//   GetPicture(0x20c) + MemoryCopy to VGA + GetPalette(0x27) +
+	//   _OpenFadeIn + 50-tick wait at 25 fps + _OpenFadeOut.
 	Picture pic;
 	if (!_picsArchive.getPicture(kPicHighScoreLogo, pic)) {
 		warning("HighScore logo (%u) load failed", kPicHighScoreLogo);
 		return;
 	}
 	blitAt(pic, 0, 0);
-	setSitePalette(kPalHighScore);
+
+	// Load target palette into a buffer, force a black palette, then
+	// fade in ??? without the explicit black step we'd flash the full
+	// logo briefly between blit and fade.
+	byte target[kPalSize];
+	if (!getSitePalette(kPalHighScore, target)) {
+		warning("HighScore palette (%u) load failed", kPalHighScore);
+		return;
+	}
+	byte black[kPalSize] = {};
+	g_system->getPaletteManager()->setPalette(black, 0, 256);
 	g_system->updateScreen();
-	waitForInput(2500);
+	fadePaletteFromBlack(target);
+
+	// 50 ticks at 25 fps = ~2 s.
+	waitForInput(2000);
+
+	fadeCurrentPaletteToBlack();
 }
 
 void EEMEngine::showFloppyStormLogo() {
@@ -951,11 +1041,23 @@ void EEMEngine::showFloppyStormLogo() {
 		return;
 	}
 	blitAt(pic, 0, 0);
-	setSitePalette(kPalStormLogo);
+
+	byte target[kPalSize];
+	if (!getSitePalette(kPalStormLogo, target)) {
+		warning("Storm palette (%u) load failed", kPalStormLogo);
+		return;
+	}
+	byte black[kPalSize] = {};
+	g_system->getPaletteManager()->setPalette(black, 0, 256);
 	g_system->updateScreen();
+
 	if (_audio)
 		_audio->playVoc(Common::Path("THUNDER.VOC"));
-	waitForInput(2500);
+
+	fadePaletteFromBlack(target);
+	waitForInput(2000);
+	fadeCurrentPaletteToBlack();
+
 	if (_audio)
 		_audio->stopVoice();
 }
diff --git a/engines/eem/eem.h b/engines/eem/eem.h
index c942013d1cc..26cb2c8f00d 100644
--- a/engines/eem/eem.h
+++ b/engines/eem/eem.h
@@ -368,6 +368,13 @@ private:
 	 */
 	void setSitePalette(uint num);
 
+	/// Fill @p out (256 ?? 3 bytes) with the SITEPALS palette at index
+	/// @p num, expanded from VGA's 6-bit DAC range to 8-bit. Returns
+	/// false if the index is out of range. Used when callers need the
+	/// palette for fade-in (set black first, then fade) without
+	/// flashing the target on screen.
+	bool getSitePalette(uint num, byte *out) const;
+
 	/**
 	 * Upload a 6-bit VGA palette read from the head of an .ANM file (the
 	 * first 0x300 bytes per Load_Sequence @ 2503:0006). Used until the
diff --git a/engines/eem/site.cpp b/engines/eem/site.cpp
index b141704a1fe..f309a2d76bc 100644
--- a/engines/eem/site.cpp
+++ b/engines/eem/site.cpp
@@ -137,6 +137,32 @@ void cyclePaletteRange(uint8 start, uint8 end) {
 	g_system->getPaletteManager()->setPalette(buf, start, count);
 }
 
+void cyclePaletteRangeReverse(uint8 start, uint8 end) {
+	// Mirrors `_OpenColorCycle @ 2520:04f7`: save the END color, shift
+	// every entry up by one (END-1 ??? END, ...), wrap saved END to
+	// START. Visually colors march from start toward end ??? opposite
+	// direction from `cyclePaletteRange`. Used by the EA Kids /
+	// HighScore logo cycles.
+	if (end <= start)
+		return;
+	const uint count = (uint)end - (uint)start + 1;
+	byte buf[256 * 3];
+	g_system->getPaletteManager()->grabPalette(buf, start, count);
+	const uint last = count - 1;
+	const byte savedR = buf[last * 3 + 0];
+	const byte savedG = buf[last * 3 + 1];
+	const byte savedB = buf[last * 3 + 2];
+	for (uint i = last; i > 0; i--) {
+		buf[i * 3 + 0] = buf[(i - 1) * 3 + 0];
+		buf[i * 3 + 1] = buf[(i - 1) * 3 + 1];
+		buf[i * 3 + 2] = buf[(i - 1) * 3 + 2];
+	}
+	buf[0] = savedR;
+	buf[1] = savedG;
+	buf[2] = savedB;
+	g_system->getPaletteManager()->setPalette(buf, start, count);
+}
+
 // Per-speaker partner-position table verified against `_WaitAnims @
 // 29be:021c`. 12 bytes per entry, indexed by `siteData[+8]`. Layout:
 //   +0..1 anim Jake, +2..3 anim Jenny,
diff --git a/engines/eem/site.h b/engines/eem/site.h
index 110c560df05..606edf14ffe 100644
--- a/engines/eem/site.h
+++ b/engines/eem/site.h
@@ -81,6 +81,12 @@ void blitAnimFrameAnchored(Graphics::Surface *screen, const Picture &p,
 /// BigMap marker shine.
 void cyclePaletteRange(uint8 start, uint8 end);
 
+/// Rotate one VGA palette range by one slot in the OPPOSITE direction.
+/// Mirrors `_OpenColorCycle @ 2520:04f7` (CD) / `_ReverseColorCycle_Floppy`
+/// ??? used by the opening-anim logos (EA Kids, etc.) where the cycle
+/// shifts END???START rather than START???END.
+void cyclePaletteRangeReverse(uint8 start, uint8 end);
+
 /// One hotspot (search rectangle) within a site, 14 bytes on disk.
 struct Hotspot {
 	int16  x1, y1, x2, y2;     ///< rectangle in screen coordinates


Commit: 59304750cf0a6c8c92e2177078140834f58dcfb0
    https://github.com/scummvm/scummvm/commit/59304750cf0a6c8c92e2177078140834f58dcfb0
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:21:10+02:00

Commit Message:
EEM: reviewed and improved comments across the engine

Changed paths:
    engines/eem/animation.h
    engines/eem/audio.cpp
    engines/eem/audio.h
    engines/eem/clues.cpp
    engines/eem/detection.cpp
    engines/eem/eem.cpp
    engines/eem/eem.h
    engines/eem/font.cpp
    engines/eem/font.h
    engines/eem/graphics.cpp
    engines/eem/music.cpp
    engines/eem/music.h
    engines/eem/mystery.cpp
    engines/eem/mystery.h
    engines/eem/resource.cpp
    engines/eem/resource.h
    engines/eem/site.cpp
    engines/eem/site.h
    engines/eem/ui.cpp


diff --git a/engines/eem/animation.h b/engines/eem/animation.h
index 09b6e0406d1..fa5c7b8f4c2 100644
--- a/engines/eem/animation.h
+++ b/engines/eem/animation.h
@@ -30,20 +30,13 @@
 namespace EEM {
 
 /**
- * Decoder for the engine's full-screen difference animations.
- *
- * Used for `BOLT.ANM`, `TITLE.ANM`, `ANIM01.A` .. `ANIM20.A`. The format is
- * documented by Load_Sequence @ 2503:0006 / OpenDifferenceAnimation @ 2520:0337:
- *
- *   - 0x300 bytes : 6-bit VGA palette
- *   - u16        : frame count
- *   - 12 bytes   : header (height @ +2, width @ +4, rest unused)
- *   - frames*u16 : packed length per frame
- *   - per frame  : `lengths[i]` bytes of RLE-packed delta data
- *
- * Each packed frame is unpacked by the custom `_ASM_Decompress` RLE
- * (1000:0953) into the persistent `_buffer`; skip opcodes preserve pixels
- * from the previous frame, which is how the difference encoding works.
+ * Decoder for BOLT.ANM, TITLE.ANM, ANIM01.A..ANIM20.A. Format from
+ * Load_Sequence @ 2503:0006 / OpenDifferenceAnimation @ 2520:0337:
+ *   - 0x300 bytes: 6-bit VGA palette
+ *   - u16: frame count
+ *   - 12 bytes: header (height @ +2, width @ +4, rest unused)
+ *   - frames*u16: packed length per frame
+ *   - per frame: lengths[i] bytes of RLE delta data (asmDecompress).
  */
 class ANMDecoder {
 public:
@@ -89,11 +82,8 @@ private:
 	uint16 _nextFrameIdx = 0;
 };
 
-/**
- * Decompress a single frame's RLE payload in place. Mirrors _ASM_Decompress
- * @ 1000:0953 byte-for-byte. @p dst already holds the previous frame; skip
- * opcodes leave those pixels untouched.
- */
+/// _ASM_Decompress @ 1000:0953. dst holds the previous frame; skip opcodes
+/// leave those pixels untouched (difference encoding).
 void asmDecompress(const byte *src, uint srcSize, byte *dst, uint dstSize);
 
 } // End of namespace EEM
diff --git a/engines/eem/audio.cpp b/engines/eem/audio.cpp
index 6ed89dcdf0c..3954fbb02f9 100644
--- a/engines/eem/audio.cpp
+++ b/engines/eem/audio.cpp
@@ -51,15 +51,8 @@ void AudioPlayer::stopAll() {
 	cleanMysterySounds();
 }
 
-// VOC playback --------------------------------------------------------
-
 void AudioPlayer::playVoc(const Common::Path &vocPath) {
-	// Voice / digital audio is gated by the `DAT_2d5d_3f97` flag in
-	// the original ??? verified at every callsite (`_DoChoosePartner @
-	// 1a35:098c`, `_DisplayClue @ 2404:0845`, `_DoOpeningAnims @
-	// 2520:08a8`, etc.). Setup-screen toggle and `_NewPlayer` fresh-
-	// profile init both rewrite that flag. We pull it into the audio
-	// player so callers don't have to duplicate the check.
+	// `_voiceEnabled` mirrors DAT_2d5d_3f97 (setup-screen voice toggle).
 	if (!_voiceEnabled) {
 		debugC(2, kDebugSound, "AudioPlayer: voice disabled, skipping %s",
 			   vocPath.toString().c_str());
@@ -67,7 +60,6 @@ void AudioPlayer::playVoc(const Common::Path &vocPath) {
 	}
 	stopVoice();
 
-	// Mirrors `_LoadSoundName`'s `_fopen` (1ff1:02ac).
 	Common::File *f = new Common::File();
 	if (!f->open(vocPath)) {
 		warning("AudioPlayer: %s missing", vocPath.toString().c_str());
@@ -83,9 +75,7 @@ void AudioPlayer::playVoc(const Common::Path &vocPath) {
 		return;
 	}
 
-	// `_PlayVoice` (1ff1:023e) goes through `_AIL_play_VOC_file` on the
-	// digital channel ??? we route through `kSpeechSoundType` so the
-	// launcher's "Speech volume" slider applies.
+	// _PlayVoice @ 1ff1:023e ??? route through kSpeechSoundType for the launcher slider.
 	_mixer->playStream(Audio::Mixer::kSpeechSoundType, &_voiceHandle,
 					   stream, -1, Audio::Mixer::kMaxChannelVolume,
 					   0, DisposeAfterUse::YES);
@@ -98,9 +88,8 @@ bool AudioPlayer::isVoicePlaying() const {
 }
 
 void AudioPlayer::waitForVoiceDone(uint32 maxMs) {
-	// Mirrors the wait loop at `_WaitForVoiceDone @ 1ff1:0221` ??? pumps
-	// events (so animations + abort-on-click still work) while waiting
-	// for the AIL voice channel to drain.
+	// _WaitForVoiceDone @ 1ff1:0221 ??? pumps events while the AIL voice channel
+	// drains, so animations + abort-on-click keep working during the wait.
 	const uint32 startMs = g_system->getMillis();
 	while (isVoicePlaying() && !_vm->shouldQuit() &&
 		   g_system->getMillis() - startMs < maxMs) {
@@ -124,12 +113,9 @@ void AudioPlayer::stopVoice() {
 		_mixer->stopHandle(_voiceHandle);
 }
 
-// Spool sound ---------------------------------------------------------
-
+// _ReadLong(&MysterySounds, size, sdx) @ 202f:0647.
+// 12-byte entries: (u32 offset, u32 compressed_size, u32 uncompressed_size).
 bool AudioPlayer::readSdxIndex(const Common::Path &sdxPath) {
-	// Mirrors `_ReadLong(&MysterySounds, size, sdx)` at 202f:0647 ???
-	// reads the entire .SDX into memory; each 12-byte entry is
-	// (u32 offset, u32 compressed_size, u32 uncompressed_size).
 	Common::File f;
 	if (!f.open(sdxPath)) {
 		warning("AudioPlayer: %s missing", sdxPath.toString().c_str());
@@ -151,10 +137,8 @@ bool AudioPlayer::readSdxIndex(const Common::Path &sdxPath) {
 	return true;
 }
 
+// _InitMysterySounds @ 202f:05cb. Strings "m%u.sdx" @ 29be:144f, "m%u.sdb" @ 29be:145b.
 bool AudioPlayer::initMysterySounds(uint mysteryNum) {
-	// Mirrors `_InitMysterySounds @ 202f:05cb` ??? calls
-	// `_CleanMysterySounds` first, then sprintf-opens `m%u.sdx` (string
-	// at 29be:144f) and `m%u.sdb` (29be:145b).
 	cleanMysterySounds();
 
 	const Common::String sdxName = Common::String::format("M%u.SDX", mysteryNum);
@@ -180,12 +164,11 @@ void AudioPlayer::cleanMysterySounds() {
 	_currentMystery = -1;
 }
 
+// pcm must be allocated with malloc(): Audio::makeRawStream takes ownership
+// and frees it via free() on stream destruction (NOT delete/delete[]).
 void AudioPlayer::playPcmBuffer(byte *pcm, uint32 size, uint sampleRate,
 								Audio::SoundHandle &handle,
 								Audio::Mixer::SoundType type) {
-	// `Audio::makeRawStream` takes ownership and `free()`s the buffer
-	// on stream destruction ??? so the caller must `malloc` (not `new`)
-	// the PCM buffer.
 	Audio::SeekableAudioStream *stream =
 		Audio::makeRawStream(pcm, size, sampleRate, Audio::FLAG_UNSIGNED,
 							 DisposeAfterUse::YES);
@@ -199,11 +182,10 @@ void AudioPlayer::playPcmBuffer(byte *pcm, uint32 size, uint sampleRate,
 					   DisposeAfterUse::YES);
 }
 
-// Floppy per-partner voice table ??? verified by reading the raw filename
-// pointers at `2608:0f0e` (Jake) and `2608:0f76` (Jenny) in EEM.EXE,
-// each 26 ?? FAR-ptr to a NUL-terminated `*.voc` filename. Indexed by
-// `_LoadSoundName_Floppy @ 1f4e:0305`. Slots match across partners:
-//   12 = PHONESL.VOC, 20 = partner intro, 25 = THUNDER.VOC.
+// Floppy per-partner voice tables, indexed by _LoadSoundName_Floppy @ 1f4e:0305.
+// Filename FAR-ptr arrays at 2608:0f0e (Jake) and 2608:0f76 (Jenny), each
+// 26 * FAR-ptr to a NUL-terminated `*.voc` name. Slots align across partners,
+// e.g. 12 = PHONESL.VOC, 20 = partner intro, 25 = THUNDER.VOC.
 static const char *const kFloppyJakeVoiceTable[26] = {
 	"DING.VOC",       "M-0083SL.VOC", "M-0085SL.VOC", "NEWSCAN.VOC",
 	"M-0089SL.VOC",   "M-0091SL.VOC", "M-0092SL.VOC", "NEWSSHRT.VOC",
@@ -225,7 +207,7 @@ static const char *const kFloppyJennyVoiceTable[26] = {
 
 void AudioPlayer::playFloppyVoiceSlot(uint slot, uint partner) {
 	if (slot >= 26)
-		slot = 0;  // mirrors `_LoadSoundName_Floppy`'s `if (0x19 < slot) slot = 0;`
+		slot = 0;  // _LoadSoundName_Floppy: if (0x19 < slot) slot = 0;
 	const char *name = (partner == 0)
 		? kFloppyJakeVoiceTable[slot]
 		: kFloppyJennyVoiceTable[slot];
@@ -239,9 +221,7 @@ void AudioPlayer::spoolSound(uint num) {
 		return;
 	}
 	if (_currentMystery < 0) {
-		// No SDB/SDX bundle is loaded ??? floppy install (no `M*.SDB`)
-		// or pre-mystery state. Silently no-op; per-voice VOC playback
-		// for floppy lives elsewhere (TODO).
+		// No SDB/SDX bundle loaded (floppy install or pre-mystery state).
 		debugC(2, kDebugSound,
 			   "AudioPlayer: spoolSound(%u) skipped (no mystery sounds)", num);
 		return;
@@ -266,17 +246,14 @@ void AudioPlayer::spoolSound(uint num) {
 		return;
 	}
 
-	// Mirrors the two `_fgetc(in)` reads at 202f:02da-e1: byte 0 =
-	// Sound Blaster Time Constant, byte 1 = total AIL playback blocks.
-	// Convert TC -> sample rate via the standard SB formula. The block
-	// count is only used internally by the AIL DDS pipeline; ScummVM's
-	// mixer doesn't need it.
+	// _UncompressedSound 2-byte header @ 202f:02da-e1:
+	//   byte 0 = Sound Blaster Time Constant
+	//   byte 1 = total AIL playback blocks (internal to AIL DDS; unused here)
 	const byte tc          = sdb.readByte();
-	(void)sdb.readByte(); // total blocks ??? unused outside AIL
+	(void)sdb.readByte(); // AIL block count, unused outside AIL
 
-	// SB Time Constant: rate = 1000000 / (256 - tc). e.g. tc=0xD2 ???
-	// 22 kHz. Guard against the degenerate tc=0xFF (would divide by 1
-	// ??? 1 MHz, well above what the mixer can resample sanely).
+	// SB Time Constant formula: rate = 1000000 / (256 - tc).
+	// e.g. tc=0xD2 -> 22 kHz. tc=0xFF would divide by 1 (1 MHz, nonsense); clamp.
 	const uint sampleRate = (tc < 0xFF)
 		? (uint)(1000000u / (256u - tc))
 		: 44100u;
@@ -285,11 +262,7 @@ void AudioPlayer::spoolSound(uint num) {
 	uint32 audioSize = 0;
 
 	if (entry.compressedSize == entry.uncompressedSize) {
-		// `_UncompressedSound @ 202f:03e6` ??? already raw PCM. The
-		// `_SpoolSound` equality check at 202f:06e6 is `comp == uncomp`;
-		// in that case `len = uncompressed_size` bytes follow the
-		// 2-byte header. Original reads in 16 KB chunks; we slurp the
-		// lot at once.
+		// _UncompressedSound @ 202f:03e6 ??? raw PCM follows the 2-byte header.
 		audioSize = entry.uncompressedSize;
 		pcm = (byte *)malloc(audioSize);
 		if (!pcm) {
@@ -303,18 +276,15 @@ void AudioPlayer::spoolSound(uint num) {
 			return;
 		}
 	} else {
-		// `_DeCompressSound @ 202f:02ad` ??? `EXPLODE @ 25c6:0d01`
-		// (PKWARE DCL "Implode") with READDISKSOUND/WRITESOUND
-		// callbacks. EXPLODE drives both ends via its OWN end-of-stream
-		// marker (length token 519); the SDX `compressed_size` /
-		// `uncompressed_size` are loose hints, NOT exact lengths ???
-		// 202f:0332 even computes `destSize - 2` and never reads it.
-		// ScummVM's fixed-size `decompressDCL` overload errors when
-		// the actual output exceeds our pre-allocated buffer (which
-		// we saw on every M0 clue voice). Use the dynamic-sized
-		// overload instead ??? it lets the DCL stream terminate at its
-		// own marker. Source is bounded to the rest of the SDB so
-		// the bit reader can't fall off the end.
+		// _DeCompressSound @ 202f:02ad -> EXPLODE @ 25c6:0d01 (PKWARE DCL Implode),
+		// driven by READDISKSOUND/WRITESOUND callbacks. EXPLODE terminates on its
+		// own length token 519 (end-of-stream marker), not on the SDX sizes:
+		// 202f:0332 even computes `destSize - 2` and never reads it. The SDX
+		// `compressed_size` / `uncompressed_size` fields are loose hints.
+		// ScummVM's fixed-size `decompressDCL` overload errors when actual output
+		// exceeds the pre-allocated buffer (observed on M0 clue voices), so use
+		// the dynamic-sized overload and let DCL terminate at its own marker.
+		// Source is bounded to the rest of the SDB so the bit reader stays in range.
 		const uint32 streamStart = (uint32)sdb.pos();
 		Common::SeekableSubReadStream sub(&sdb, streamStart,
 										   (uint32)sdb.size(),
@@ -343,11 +313,7 @@ void AudioPlayer::spoolSound(uint num) {
 		   num, tc, sampleRate, audioSize,
 		   entry.compressedSize == entry.uncompressedSize ? "raw" : "DCL");
 
-	// `_AIL_start_digital_playback` at 202f:040c ??? we route through
-	// `kSFXSoundType` so the launcher's "SFX volume" slider applies
-	// (this is the same slider the original would've targeted via
-	// `_AIL_set_digital_master_volume`). Voice clips on the spool path
-	// are gameplay SFX, not the speech-only VOC stream.
+	// _AIL_start_digital_playback @ 202f:040c. Spool clips are gameplay SFX.
 	playPcmBuffer(pcm, audioSize, sampleRate, _spoolHandle,
 				  Audio::Mixer::kSFXSoundType);
 }
@@ -380,17 +346,13 @@ void AudioPlayer::stopSpool() {
 		_mixer->stopHandle(_spoolHandle);
 }
 
+// _SayKDDigital @ 2404:0fbc.
+//   slot = kdspeak * 2 + (partner == Jake ? 1 : 0); sound = digital[slot+1] - 1
+// KDDigitalIndex = KDTextIndex + 0x12 (set by _ReadMystery @ 2404:0163-0167).
 void AudioPlayer::sayKDDigital(const byte *kdTextIndex, uint kdspeak,
 							   uint partner) {
 	if (!kdTextIndex || _currentMystery < 0)
 		return;
-	// `_SayKDDigital @ 2404:0fbc`:
-	//   iVar1 = kdspeak * 2;
-	//   if (_Partner == 0) iVar1++;            // Jake offset
-	//   sound = *(u16 *)(KDDigitalIndex + (iVar1 + 1) * 2) - 1;
-	//   _SpoolSound(sound);
-	// KDDigitalIndex sits 18 bytes (`+ 0x12`) after KDTextIndex per
-	// `_ReadMystery` 2404:0163-0167.
 	const byte *digital = kdTextIndex + 0x12;
 	const uint slot = (kdspeak * 2) + (partner == 0 ? 1u : 0u) + 1u;
 	const uint16 raw = READ_LE_UINT16(digital + slot * 2);
diff --git a/engines/eem/audio.h b/engines/eem/audio.h
index 12667f1179d..1d986ac47bb 100644
--- a/engines/eem/audio.h
+++ b/engines/eem/audio.h
@@ -35,119 +35,84 @@ namespace EEM {
 class EEMEngine;
 
 /**
- * Non-MIDI audio (digitised voice + sound effects). Mirrors the
- * original `SOUND.C` / `SPOOLSND.C` source files in `EEMCD.EXE` ???
- * the AIL digital playback path used for both standalone .VOC files
- * and the per-mystery `M%d.SDB` spool stream.
+ * Non-MIDI audio (digitised voice + SFX). Mirrors SOUND.C / SPOOLSND.C.
  *
- * Two pathways:
+ * - VOC files (THUNDER.VOC, PHONE.VOC, JEN.VOC/JAKE.VOC): _LoadSoundName +
+ *   _PlayVoice @ 1ff1:0299 / 1ff1:023e via _AIL_play_VOC_file.
+ * - Mystery spool stream: _InitMysterySounds @ 202f:05cb opens M%d.SDX/.SDB.
+ *   Each SDX entry is (offset, compSize, uncompSize). Equal sizes => raw PCM
+ *   (_UncompressedSound @ 202f:03e6); otherwise PKWARE DCL Implode
+ *   (_DeCompressSound @ 202f:02ad). Each blob starts with SB Time Constant +
+ *   AIL block count, then 8-bit unsigned PCM.
  *
- * 1. **VOC playback** ??? `_LoadSoundName @ 1ff1:0299` reads a Creative
- *    Voice File into memory and `_PlayVoice @ 1ff1:023e` hands it to
- *    `_AIL_play_VOC_file`. Used for THUNDER.VOC (Storm logo),
- *    PHONE.VOC (briefing), JEN.VOC / JAKE.VOC (partner choose).
- *
- * 2. **Spool stream** ??? `_InitMysterySounds @ 202f:05cb` opens
- *    `m%d.sdx` (29be:144f) and `m%d.sdb` (29be:145b) for the active
- *    mystery. `_SpoolSound(num) @ 202f:068d` indexes into the SDX
- *    table (12 bytes per entry: u32 file_offset, u32 compressed_size,
- *    u32 uncompressed_size). If sizes match it streams via
- *    `_UncompressedSound @ 202f:03e6`; otherwise it EXPLODE-decompresses
- *    via `_DeCompressSound @ 202f:02ad`.
- *
- *    Each entry's data starts with 2 metadata bytes ??? Sound Blaster
- *    Time Constant (`rate = 1000000 / (256 - tc)`) and the AIL block
- *    count ??? followed by the (optionally) PKWARE-DCL-compressed
- *    8-bit unsigned PCM stream. We use ScummVM's `Common::decompressDCL`
- *    + `Audio::makeRawStream` to reach the same audio at the same
- *    sample rate.
- *
- * Mystery 60 (`M60.SDB/SDX`) holds the 19 voiceovers played between
- * ANIM01..ANIM20 in `_DoOpeningAnims` (it loads `_InitMysterySounds(0x3c)`
- * before the loop and `_SpoolSound(uVar3 - 1)` between every clip).
- * Mysteries 0..55 hold each case's per-clue voice plus the partner's
- * digital lines (`_KDDigitalIndex` table within the .SD blob).
+ * M60.SDB/SDX holds the 19 voiceovers between ANIM01..ANIM20 in
+ * _DoOpeningAnims (loaded via _InitMysterySounds(0x3c)).
+ * M0..M55 hold per-mystery clue voice + partner KDDigital lines.
  */
 class AudioPlayer {
 public:
 	explicit AudioPlayer(EEMEngine *vm);
 	~AudioPlayer();
 
-	/// Mirrors the gate in every original audio call site
-	/// (`_DisplayClue` 2404:0845, `_DoChoosePartner` 1a35:098c,
-	/// `_DoOpeningAnims` 2520:08a8, `_DisplayCorrect` 1df2:0780, ...)
-	/// ??? every `_PlayVoice` / `_SpoolSound` is wrapped in
-	/// `if ((DAT_2d5d_3f97 != 0) && (_VoiceAvailable != 0))`. The
-	/// engine pulls `_voiceOn` (= `DAT_2d5d_3f97`) into here so we
-	/// can early-return at the audio boundary instead of duplicating
-	/// the gate at every call site.
+	/// Setup-screen voice toggle. Mirrors DAT_2d5d_3f97 ??? every original
+	/// `_PlayVoice` / `_SpoolSound` is wrapped in
+	/// `if ((DAT_2d5d_3f97 != 0) && (_VoiceAvailable != 0))`; we pull the
+	/// flag in here so callers don't duplicate the gate.
 	void setVoiceEnabled(bool enabled) { _voiceEnabled = enabled; }
 	bool voiceEnabled() const { return _voiceEnabled; }
 
-	// VOC playback ----------------------------------------------------
-
-	/// Mirrors `_LoadSoundName` + `_PlayVoice`. Loads the named .VOC
-	/// from the game directory and hands it to the speech mixer
-	/// channel. A new `playVoc` cancels any prior voice.
+	/// Loads the named .VOC from the game directory and hands it to the
+	/// speech mixer channel. Mirrors _LoadSoundName + _PlayVoice
+	/// @ 1ff1:0299 / 1ff1:023e. A new playVoc cancels any prior voice.
 	void playVoc(const Common::Path &vocPath);
 
-	/// Mirrors `_VoicePlaying @ 1ff1:01f9`.
+	/// _VoicePlaying @ 1ff1:01f9.
 	bool isVoicePlaying() const;
 
-	/// Mirrors `_WaitForVoiceDone @ 1ff1:0221`. Blocks (with frame /
-	/// event pumping, like the rest of the engine's busy-loops) until
-	/// the voice clip finishes. Returns early if the user clicks /
-	/// presses a key ??? same abort behaviour the original
-	/// `_AIL_stop_digital_playback` callback installed.
+	/// _WaitForVoiceDone @ 1ff1:0221. Blocks (with frame + event pumping)
+	/// until the voice clip finishes; returns early on click / keypress.
 	void waitForVoiceDone(uint32 maxMs = 60000);
 
-	/// Mirrors `_StopTheVoice @ 1ff1:0283`.
+	/// _StopTheVoice @ 1ff1:0283.
 	void stopVoice();
 
-	/// Floppy variant: play a VOC by 0..25 slot index in the per-partner
-	/// voice table. Mirrors `_LoadSoundName_Floppy @ 1f4e:0305` which
-	/// indexes the table at `2608:0f0e` (Jake) / `2608:0f76` (Jenny).
-	/// `partner` is 0 for Jake, 1 for Jenny. Common slots: 12 =
-	/// PHONESL.VOC, 20 = partner intro voice, 25 = THUNDER.VOC.
+	/// Play a floppy VOC by 0..25 slot index in the per-partner voice
+	/// table. Mirrors _LoadSoundName_Floppy @ 1f4e:0305 (tables at
+	/// 2608:0f0e Jake / 2608:0f76 Jenny). partner: 0=Jake, 1=Jenny.
+	/// Common slots: 12 = PHONESL.VOC, 20 = partner intro, 25 = THUNDER.VOC.
 	void playFloppyVoiceSlot(uint slot, uint partner);
 
-	// Mystery sound spool ---------------------------------------------
-
-	/// Mirrors `_InitMysterySounds @ 202f:05cb`. Loads `M%u.SDX` into
-	/// memory and remembers the corresponding `M%u.SDB` path.
+	/// Loads M%u.SDX into memory and remembers the matching M%u.SDB path.
+	/// Mirrors _InitMysterySounds @ 202f:05cb.
 	bool initMysterySounds(uint mysteryNum);
 
-	/// Mirrors `_CleanMysterySounds @ 202f:05a5`.
+	/// _CleanMysterySounds @ 202f:05a5.
 	void cleanMysterySounds();
 
-	/// Mirrors `_SpoolSound @ 202f:068d`. Reads + decompresses entry
-	/// `num` from the active SDB and queues it for SFX playback. The
-	/// original blocks until playback finishes ??? for ScummVM we let
-	/// the mixer run asynchronously and expose `waitForSpoolDone` so
-	/// callers that need the original "block-then-continue" semantics
-	/// can opt in.
+	/// Reads + decompresses SDX entry `num` from the active SDB and queues
+	/// it for SFX playback. Mirrors _SpoolSound @ 202f:068d. Original blocks
+	/// until playback finishes; we run async ??? use waitForSpoolDone to opt
+	/// in to the original block-then-continue semantics.
 	void spoolSound(uint num);
 
-	/// Mirrors the abort-on-input wait loop inside `_UncompressedSound`
-	/// / `_DeCompressSound`. Returns when the spool clip finishes or
-	/// the user clicks / presses a key.
+	/// Wait loop inside _UncompressedSound / _DeCompressSound; aborts on
+	/// click / keypress (same abort behaviour as the original).
 	void waitForSpoolDone(uint32 maxMs = 60000);
 
-	/// Mirrors the immediate `_AIL_stop_digital_playback` exit.
+	/// Immediate _AIL_stop_digital_playback exit.
 	void stopSpool();
 
 	bool isSpoolPlaying() const;
 
-	/// Mirrors `_SayKDDigital(kdspeak) @ 2404:0fbc`. Each mystery
-	/// embeds a `KDDigitalIndex` table immediately after the 18-byte
-	/// `KDTextIndex` header (set up by `_ReadMystery @ 2404:008f`:
-	/// `_KDDigitalIndex = _KDTextIndex + 0x12`). The table is two
-	/// 1-based sound indices per `kdspeak` slot ??? Jen at +2, Jake at
-	/// +4 from each entry's start (+1 word for the unused header
-	/// slot). Pass the mystery's `kdTextIndex()` pointer.
+	/// Mirrors _SayKDDigital(kdspeak) @ 2404:0fbc. Each mystery embeds a
+	/// KDDigitalIndex table 18 bytes (+0x12) after its KDTextIndex header
+	/// (set up by _ReadMystery @ 2404:008f: `_KDDigitalIndex = _KDTextIndex
+	/// + 0x12`). Table is two 1-based sound indices per kdspeak slot ??? Jen
+	/// at +2, Jake at +4 from each entry start (+1 word skips an unused
+	/// header slot). Pass the mystery's kdTextIndex() pointer.
 	void sayKDDigital(const byte *kdTextIndex, uint kdspeak, uint partner);
 
-	/// Mirrors `_QuitSounds @ 1ff1:03c5`.
+	/// _QuitSounds @ 1ff1:03c5.
 	void stopAll();
 
 private:
diff --git a/engines/eem/clues.cpp b/engines/eem/clues.cpp
index 63b384498ca..ebe7383137a 100644
--- a/engines/eem/clues.cpp
+++ b/engines/eem/clues.cpp
@@ -35,30 +35,25 @@
 #include "eem/eem.h"
 #include "eem/site.h"
 
-// EEM ??? clue / briefing pipeline (SCRIPT.C clue parts + KD.C briefing parts).
-// Everything that drives a `ClueBlock` through the displayClue / portrait /
-// balloon / notebook-side-effect flow plus the case-briefing intro that
-// preambles into the same flow.
+// Clue / briefing pipeline (SCRIPT.C + KD.C).
 
 namespace EEM {
 
-// Picture / animation IDs verified against `_DoChoosePartner @ 1a35:0756`.
-// `const` at namespace scope already implies internal linkage in C++,
-// so no `static` needed.
-const uint kPicChooseBackground = 0x8c; ///< `_GetBackground(0x8c)`
-const uint kAniBoy  = 8;                 ///< `_GetAnimation(8)` (Jake)
-const uint kAniGirl = 9;                 ///< `_GetAnimation(9)` (Jenny)
+// _DoChoosePartner @ 1a35:0756.
+const uint kPicChooseBackground = 0x8c; // _GetBackground(0x8c)
+const uint kAniBoy  = 8;                // Jake
+const uint kAniGirl = 9;                // Jenny
 
-// `_DoHappiness @ 172b:27b5`: cursor X picks one of 4 rects; past
-// rect 3 is treated as level 4. Verbatim from `29be:030f`.
+// _DoHappiness @ 172b:27b5 ??? cursor X picks one of 4 rects @ 29be:030f.
+// Past rect 3 = level 4.
 const Common::Rect kHappyZones[4] = {
-	Common::Rect(  0, 0,  70, 200), // far left  ??? girl very happy, boy neutral
+	Common::Rect(  0, 0,  70, 200), // far left ??? girl very happy, boy neutral
 	Common::Rect( 70, 0, 126, 200), // girl's column
 	Common::Rect(126, 0, 182, 200), // middle
 	Common::Rect(182, 0, 235, 200), // boy's column
 };
 
-// On-screen positions verified from `_NewAnimation` calls @ 1a35:07b9 / 07d5.
+// _NewAnimation positions @ 1a35:07b9 / 07d5.
 const int kBoyX  = 0xe2; // 226
 const int kBoyY  = 0x62; // 98
 const int kGirlX = 0x42; // 66
@@ -87,11 +82,9 @@ uint markClueBlockNotebookEntries(Mystery &mystery, const byte *clueBlock) {
 	return marked;
 }
 
-// `_DoHappiness @ 172b:27b5`: each cursor zone swaps the partner's
-// sequence script to a more / less "happy" cycle. Boy seqs lifted
-// verbatim from `29be:0337` (5 ?? 0x14 bytes), girl seqs from
-// `29be:039b`. Both cycle through 9 frames (the boy/girl anim cells
-// contain 10 cells = pairs of "neutral, smile" at increasing intensity).
+// _DoHappiness @ 172b:27b5 ??? per-zone sequence scripts.
+// Boy seqs @ 29be:0337 (5 ?? 0x14 bytes), girl seqs @ 29be:039b. 9 frames each;
+// the anim cells contain 10 cells = pairs of (neutral, smile) at 5 intensities.
 const uint8 kBoySeqs[5][9] = {
 	{ 0,0,0,0,0,0,0,1,0 }, // level 0
 	{ 2,2,2,2,2,2,2,3,2 }, // level 1
@@ -115,9 +108,10 @@ uint happinessLevel(int x) {
 	return 4; // past zone 3 ??? max level
 }
 
-// Lock the framebuffer, masked-blit `p` at (x, y), unlock. The transparent
-// colour is the high byte of `p.flags`; `_AddPicBackground @ 172b:0ed4`
-// pushes `word ptr ES:[BX] >> 8` as `_Rect_Move_Mask`'s mask byte.
+// Masked blit: transparent colour = high byte of p.flags.
+// _AddPicBackground @ 172b:0ed4 pushes `word ptr ES:[BX] >> 8` as
+// _Rect_Move_Mask's mask byte (the on-disk u16 at file offset 0 maps to
+// Picture::flags).
 void blitMaskedToScreen(const Picture &p, int x, int y) {
 	const byte transp = (byte)(p.flags >> 8);
 	Graphics::Surface *screen = g_system->lockScreen();
@@ -150,11 +144,10 @@ void blitRawToScreen(const Picture &p, int x, int y) {
 							   x, y, w, h);
 }
 
+// _DoChoosePartner @ 1a35:0756. The original places boy + girl animations
+// on a backdrop and polls four click rectangles (two per character); we
+// approximate with a single split at x=160 (left=Jenny, right=Jake).
 void EEMEngine::doChoosePartner() {
-	// Mirrors _DoChoosePartner @ 1a35:0756. The original places boy + girl
-	// animations on a backdrop and polls four click rectangles (two per
-	// character) for the player's choice. We approximate by splitting the
-	// screen at x=160: left half = girl (Jenny), right half = boy (Jake).
 	Picture background;
 	if (!_picsArchive.getPicture(kPicChooseBackground, background)) {
 		warning("ChoosePartner background (%u) load failed", kPicChooseBackground);
@@ -174,19 +167,11 @@ void EEMEngine::doChoosePartner() {
 
 	setAnmPalette(Common::Path("TITLE.ANM"));
 
-	// `_DoHappiness @ 172b:27b5`: the cursor's X column picks one of 4
-	// rects (29be:030f, all full-height); past rect 3 ??? "level 4". The
-	// per-zone sequence scripts (`kBoySeqs` / `kGirlSeqs`) live at file
-	// scope above so the gestures match the original beat-for-beat.
-
-	// `_DoChoosePartner` opens with `_SetMousePos(0xa0, 0x96)` so the
-	// cursor lands centred between the two partners ??? start the
-	// happiness level from that initial X.
+	// _DoChoosePartner opens with _SetMousePos(0xa0, 0x96).
 	int curMouseX = 0xa0;
 	uint level = happinessLevel(curMouseX);
-	uint seqIdx = 0;       // step within the 9-frame seq
+	uint seqIdx = 0;
 
-	// Initial render ??? pose 0 of whichever zone the cursor opens in.
 	blitAt(background, 0, 0);
 	blitAt(girlAnim[kGirlSeqs[level][seqIdx % 9] % girlAnim.size()],
 		   kGirlX, kGirlY);
@@ -201,11 +186,10 @@ void EEMEngine::doChoosePartner() {
 
 	uint32 lastTick = g_system->getMillis();
 	while (!shouldQuit()) {
-		// Advance through the 9-frame seq at 100 ms ??? `_CheckFrameRate`
-		// cadence. The seq is short and loops; matches the original
-		// `_UpdateAnimations` which restarts at curIdx=0 on the 0x80
-		// marker. Mirrors `_DoHappiness`'s rewriting of `curIdx = 0xFFFF`
-		// when the cursor crosses zones (we restart `seqIdx` instead).
+		// 100ms tick: matches _CheckFrameRate's ~10 fps cadence. The seq
+		// is short and loops; _UpdateAnimations restarts at curIdx=0 on
+		// the 0x80 marker. _DoHappiness rewrites curIdx=0xFFFF on zone
+		// change (here we restart seqIdx).
 		if (g_system->getMillis() - lastTick > 100) {
 			lastTick = g_system->getMillis();
 			seqIdx = (seqIdx + 1) % 9;
@@ -265,16 +249,14 @@ void EEMEngine::doChoosePartner() {
 		g_system->delayMillis(20);
 	}
 
-	// Mirrors the tail of `_DoChoosePartner @ 1a35:097f` ??? once the
-	// player commits to a partner, load and play their intro VOC
-	// (`jen.voc` for Jenny, `jake.voc` for Jake; strings at 29be:0af1 /
-	// 29be:0af9) and block on `_WaitForVoiceDone`.
+	// Partner intro VOC (jake.voc / jen.voc @ 29be:0af9 / 29be:0af1),
+	// tail of _DoChoosePartner @ 1a35:097f.
 	if (_audio) {
 		if (isFloppy()) {
-			// Floppy `_DoChoosePartner_Floppy @ 19bb:0a8e` calls
-			// `_LoadSoundName_Floppy(0x14)` (= slot 20) which the
-			// per-partner table at `2608:0f0e` / `2608:0f76` resolves
-			// to `m-0113sl.voc` (Jake) or `f-0140sl.voc` (Jenny).
+			// Floppy _DoChoosePartner_Floppy @ 19bb:0a8e calls
+			// _LoadSoundName_Floppy(0x14) (slot 20); per-partner tables at
+			// 2608:0f0e / 2608:0f76 resolve to m-0113sl.voc (Jake) or
+			// f-0140sl.voc (Jenny).
 			_audio->playFloppyVoiceSlot(0x14, _partner);
 		} else {
 			_audio->playVoc(Common::Path(
@@ -284,24 +266,16 @@ void EEMEngine::doChoosePartner() {
 	}
 }
 
+// _DoInitClues @ 1a35:0411. Sequence:
+//   1. BG 0x52, palette 0x22
+//   2. anims: game @ (0xcd, 0x6c), book @ (0, 99), nancy @ (0x68, 0x8b) on caseType=1
+//   3. cycle game anim once (click skips)
+//   4. _PlayInSequence per partner+caseType
+//   5. _DisplayClue(InitBlock + 2, 1) ??? briefing dialogue
+//   6. _OnSites[startSite] = 1
+// Anim IDs: gameAni = 0x17 (Jake) / 0x3b (Jenny);
+// bookAni = 0x18 / 0x3c; nancyAni = 0x19 (caseType 1 only).
 void EEMEngine::doInitClues() {
-	// Mirrors `_DoInitClues` @ 1a35:0411. The original does:
-	//   1. _AllBlack(); _GetBackground(0x52); _GetPalette(0x22);
-	//   2. _GetAnimation(gameAni); _NewAnimation(0xcd, 0x6c, ...)
-	//      _GetAnimation(bookAni); _NewAnimation(0,    99,   ...)
-	//      (case type 1 also: _NewAnimation(0x68, 0x8b, nancyAni))
-	//   3. _UpdateAnimations(); _FadeIn();
-	//   4. while (frame != gameNum) { _CheckFrameRate(); _UpdateAnimations(); }
-	//        ??? cycles through the entire game animation once. Click skips.
-	//   5. _PlayInSequence(seqId, ...) ??? plays a follow-up sequence based
-	//      on partner + case type.
-	//   6. _DisplayClue(InitBlock + 2, 1) ??? the briefing dialogue.
-	//   7. _OnSites[startSite] = 1.
-	//
-	// gameAni / bookAni / nancyAni values verified directly from Ghidra:
-	//   gameAni  = 0x17 (Jake) / 0x3b (Jenny)
-	//   bookAni  = 0x18 (Jake) / 0x3c (Jenny)
-	//   nancyAni = 0x19 (case type 1 only)
 	if (!_mystery.isLoaded())
 		return;
 
@@ -309,12 +283,9 @@ void EEMEngine::doInitClues() {
 	if (!ib)
 		return;
 
-	// CD InitBlock starts with `u16 caseType; u16 startSite; <clue block>`.
-	// Floppy InitBlock starts with `u8 caseType; u8 nSubjects; subjects[];
-	// u8 nDialog; dialog_records[]` ??? no embedded `startSite`. Verified in
-	// `FUN_19bb_042f` (floppy briefing) where `cVar1 = *(buffer +
-	// initOffset)` reads caseType as a single byte and the dialog loop
-	// uses `local_e = byte[initOffset + 1 + nSubjects + 1]`.
+	// CD InitBlock: u16 caseType; u16 startSite; <clue block>.
+	// Floppy InitBlock (FUN_19bb_042f): u8 caseType; u8 nSubjects;
+	// subjects[]; u8 nDialog; dialog_records[]. No startSite.
 	const bool floppy = isFloppy();
 	const uint16 caseType = floppy ? (uint16)ib[0] : READ_LE_UINT16(ib);
 
@@ -325,12 +296,8 @@ void EEMEngine::doInitClues() {
 		_mystery._siteNumber = startSite;
 		_mystery._lastSite = startSite;
 	} else {
-		// Floppy InitBlock has no startSite. The floppy BigMap iterator
-		// `FUN_1fed_07ed` walks every site in the SITES section
-		// unconditionally (no `_OnSites` gate) ??? verified at the floppy
-		// `_DoMapScreen @ 1fed:1060` which stamps every site marker
-		// before the interaction loop. Mark every loaded site as visible
-		// so our `_onSites`-gated overview stamps the same set.
+		// Floppy _DoMapScreen @ 1fed:1060 (FUN_1fed_07ed) walks every
+		// site unconditionally ??? mirror that by marking all visible.
 		const uint sites = _mystery.numSites();
 		for (uint s = 0; s < sites && s < Mystery::kVisitedSiteCap; s++)
 			_mystery._onSites[s] = 1;
@@ -354,34 +321,17 @@ void EEMEngine::doInitClues() {
 						  && _aniArchive.loadAnimation(0x19, nancy)
 						  && !nancy.empty();
 
-	// Step 4 ??? cycle through the game animation once before the briefing.
-	// Mirrors the `while (uVar9 != gameNum)` loop. The original calls
-	// `_UpdateAnimations` per `_CheckFrameRate` tick (~10 fps). We use
-	// 100 ms ticks for the same cadence. Click / key skips.
+	// Cycle game animation once (10 fps = _CheckFrameRate cadence).
+	// _DoInitClues @ 1a35:0507/0541 hard-codes the SCRIPT index to Jake's
+	// IDs (0x17/0x18/0x19) regardless of partner, so look up scripts by
+	// those IDs unconditionally.
 	if (haveGame || haveBook || haveNancy) {
 		const uint frameCount = haveGame ? game.size() : 8;
 		bool skip = false;
 		for (uint frame = 0; frame < frameCount && !shouldQuit() && !skip; frame++) {
-			// Restore BG + advance frame. The original always uses
-			// Jake's anim IDs (0x17/0x18/0x19) as the SCRIPT keys
-			// even when Jenny's CELL data is loaded ??? verified at
-			// `_DoInitClues @ 1a35:0507`/`0541` where
-			// `_NewAnimation(..., (PicData *)CONCAT22(0x17, ...), ...)`
-			// hard-codes the script index to 0x17. So we look up
-			// `partnerFrameAtTick(0x17, ...)` regardless of partner.
-			// This gives us the correct cadence ??? book holds on its
-			// "thinking" pose (cell 8) for 16 ticks instead of
-			// flipbook-cycling, and nancy waits 18 ticks before her
-			// late-arrival count-up.
 			if (haveBriefingBg)
 				blitAt(bg, 0, 0);
 			const uint32 t = frame * 100;
-			// All three briefing anims (game/book/nancy) go through
-			// the original `_NewAnimation` path so per-frame anchors
-			// apply. Use `blitAnimFrameAnchored` against a locked
-			// screen surface so the briefing partner / book / nancy
-			// translate cleanly between cells instead of pinning at
-			// the same top-left.
 			Graphics::Surface *scr = g_system->lockScreen();
 			if (!scr) {
 				skip = true;
@@ -402,11 +352,7 @@ void EEMEngine::doInitClues() {
 			g_system->unlockScreen();
 			g_system->updateScreen();
 
-			// Wait 100 ms or until input. ESC also stops any pending
-			// voice / spool so audio doesn't bleed past the briefing
-			// when the player skips early ??? without this the per-clue
-			// voice line that `displayClue` would spool keeps playing
-			// after we've moved on to the MAP.
+			// ESC interrupts voice/spool so audio doesn't bleed into the MAP.
 			const uint32 wakeup = g_system->getMillis() + 100;
 			while (g_system->getMillis() < wakeup && !shouldQuit() && !skip) {
 				Common::Event ev;
@@ -428,14 +374,11 @@ void EEMEngine::doInitClues() {
 		}
 	}
 
-	// Freeze only the same setup-animation band the original bakes into
-	// its background buffers before clearing the registered animations:
-	// `_VidramRectCopy(0, 0x5a, 0x28, 0x6d, 16000, 48000/32000)`.
-	// Width is in mode-X columns, so 0x28 columns = 160 pixels. This
-	// preserves the lower-left book/Nancy area but intentionally drops the
-	// right-side game animation; `_PlayInSequence` redraws that character
-	// over a clean background next. Preserving the full screen leaves the
-	// old right-side Jake/Jenny frame underneath the sequence.
+	// _VidramRectCopy(0, 0x5a, 0x28, 0x6d, 16000, 48000/32000): freeze the
+	// lower-left book/Nancy band the original bakes into BG buffers before
+	// clearing registered animations. Width is in mode-X cols (0x28 = 160px).
+	// Intentionally drops the right-side game anim; _PlayInSequence redraws
+	// that character over a clean BG next.
 	Graphics::ManagedSurface briefingBase(320, 200,
 		Graphics::PixelFormat::createFormatCLUT8());
 	briefingBase.clear();
@@ -464,16 +407,12 @@ void EEMEngine::doInitClues() {
 		}
 	}
 
-	// Step 5 ??? `_PlayInSequence(animSeq, 0xcd, animY)` per Ghidra:
-	//   Jake (partner=0):
-	//     caseType=1 ??? anim 0x38 at (0xcd, 0x6d)
-	//     caseType=2 ??? anim 0x37 at (0xcd, 0x6c)
-	//     caseType=3 ??? anim 0x39 at (0xcd, 0x6c)
-	//   Jenny (partner=1):
-	//     caseType=2 ??? anim 0x3a at (0xcd, 0x6c)
-	//     caseType=3 ??? anim 0x3d at (0xcd, 0x6c)
-	// `_PlayInSequence @ 172b:2d03` plays each frame at (sx-w, sy-rowoff)
-	// with mask blit, advancing one frame per `_CheckFrameRate` tick.
+	// _PlayInSequence @ 172b:2d03. Anim selection per partner + caseType:
+	//   Jake:  caseType 1 -> 0x38 @ (0xcd, 0x6d)
+	//          caseType 2 -> 0x37 @ (0xcd, 0x6c)
+	//          caseType 3 -> 0x39 @ (0xcd, 0x6c)
+	//   Jenny: caseType 2 -> 0x3a @ (0xcd, 0x6c)
+	//          caseType 3 -> 0x3d @ (0xcd, 0x6c)
 	uint16 seqAni = 0xFFFF;
 	uint16 seqY   = 0x6c;
 	if (_partner == 0) {
@@ -514,21 +453,13 @@ void EEMEngine::doInitClues() {
 			for (uint frame = 0; frame < seq.size() && !shouldQuit() && !skip;
 				 frame++) {
 				const Picture &fr = seq[frame];
-				// Restore the frozen setup frame so the short overlay
-				// does not make the setup animation wrap to frame 0.
 				g_system->copyRectToScreen(briefingBase.getPixels(),
 										   briefingBase.pitch, 0, 0,
 										   320, 200);
-				// Anchor: `_PlayInSequence @ 172b:2d35-2d50` does
-				//   dstX = sx - cell[+0x8]     ; miscflags (signed)
-				//   dstY = sy - cell[+0x6]     ; rowoff   (signed)
-				// Ghidra's C decompile mis-labels `cell[+0x8]` as
-				// `width`; the actual asm is `SUB AX, ES:[BX+0x8]`,
-				// which is the per-frame X anchor offset stored in our
-				// `Picture::miscflags` field. Using `fr.surface.w`
-				// shifted every frame by the cell width and made the
-				// briefing partner appear duplicated next to the BG-
-				// baked figure (mystery 1 office briefing).
+				// _PlayInSequence @ 172b:2d35-2d50:
+				//   dstX = sx - cell[+0x8]   ; signed X anchor (miscflags)
+				//   dstY = sy - cell[+0x6]   ; signed Y anchor (rowoff)
+				// asm `SUB AX, ES:[BX+0x8]` ??? NOT the cell width.
 				const int dstX = (int)0xcd - (int)(int16)fr.miscflags;
 				const int dstY = (int)seqY - (int)(int16)fr.rowoff;
 				blitMaskedToScreen(fr, dstX, dstY);
@@ -556,14 +487,11 @@ void EEMEngine::doInitClues() {
 		}
 	}
 
-	// `_DoInitClues` plays a setup-voice ONLY for caseType 2/3.
-	//   CD: caseType 2 ??? PHONE.VOC. caseType 3 ??? no voice.
-	//   Floppy `_DoInitClues_Floppy @ 19bb:042f`:
-	//     caseType 2 ??? `_LoadSoundName_Floppy(slot 0xc)` = PHONESL.VOC
-	//     caseType 3 ??? `_LoadSoundName_Floppy(slot 3)`   = NEWSCAN.VOC
-	// (newspaper-scanner sting for the "TV/news anchor" briefing
-	// variant). Other case types open straight into the briefing
-	// dialogue without it.
+	// Setup voice (caseType 2/3 only):
+	//   CD:     caseType 2 -> PHONE.VOC
+	//   Floppy (_DoInitClues_Floppy @ 19bb:042f):
+	//     caseType 2 -> slot 0xc (PHONESL.VOC)
+	//     caseType 3 -> slot 3   (NEWSCAN.VOC, news-anchor variant)
 	if (_audio) {
 		if (caseType == 2) {
 			if (floppy)
@@ -577,23 +505,15 @@ void EEMEngine::doInitClues() {
 		}
 	}
 
-	// Step 6 ??? case briefing dialogue. CD InitBlock has the clue block
-	// at +4 (after `u16 caseType; u16 startSite`); floppy uses
-	// `u8 caseType; u8 nSubjects; subjects[nSubjects]; u8 nDialog;
-	// dialog_records[nDialog]` (each record `11 + textCount` bytes),
-	// dispatched via `FUN_22dc_05c8 @ 22dc:05c8`. We render dialog
-	// records ourselves on floppy.
+	// Briefing dialogue. CD: clue block @ ib+4 (after caseType,startSite).
+	// Floppy: dialog records dispatched via FUN_22dc_05c8 @ 22dc:05c8
+	// (record size = 11 + textCount bytes).
 	if (floppy) {
 		displayFloppyBriefing(ib);
 	} else {
 		const byte *briefingClues = ib + 4;
-		// Ghidra confirms CD `_DoInitClues` enters the normal
-		// `_DisplayClue(_InitBlock + 2, 1)` path, and `_DisplayClue`
-		// calls `_AddNotebook` for each ClueEntry note list at
-		// +0x30..+0x39. These starting notes are required before the
-		// first PDA visit, so mark them explicitly here. The regular
-		// displayClue side-effect pass is idempotent and still handles
-		// gallery/site updates in the original order.
+		// _DisplayClue calls _AddNotebook for each ClueEntry note list at
+		// +0x30..+0x39. Mark starting notes before the first PDA visit.
 		const uint marked = markClueBlockNotebookEntries(_mystery, briefingClues);
 		if (marked != 0)
 			debugC(1, kDebugScript,
@@ -603,27 +523,20 @@ void EEMEngine::doInitClues() {
 	}
 }
 
-/// Mirror `_ParseString` @ 1b66:07c3 ??? substitute the control bytes that
-/// the original engine uses as placeholders. Only the two we encounter most
-/// often (player name = 0x80, partner first name = 0x82) are substituted;
-/// other 0x8N opcodes are stripped. The original engine also handles
-/// hyphenation marks and a hint placeholder (0x89) we ignore for now.
+// _ParseString @ 1b66:07c3 (jump table @ 1b66:0cbe). Each handler reads
+// _Partner (u16 @ 0x7918) and indexes the name table @ 29be:0c28
+// ({Jake, Jennifer, he, she, him, her, his} as far pointers).
+//   0x80 player name (auto-cap word starts, uses _PlayerRecord)
+//   0x81 _Partner == 0 ? "Jake"     : "Jennifer"  (chosen detective)
+//   0x82 _Partner == 0 ? "Jennifer" : "Jake"      (the OTHER one)
+//   0x83 _Partner == 0 ? "he"       : "she"
+//   0x84 _Partner == 0 ? "him"      : "her"
+//   0x85 _Partner == 0 ? "his"      : "her"
+//   0x86..0x88 read a separate gender flag @ 0x7985 (TODO).
+//   0x89 KD hint placeholder (caller handles).
 Common::String EEMEngine::parseString(const Common::String &raw,
 									  const Common::String &playerName,
 									  uint partner) const {
-	// Substitution opcodes from `_ParseString` @ 1b66:07c3, jump-table
-	// at 1b66:0cbe. Each handler reads `_Partner` (16-bit at 0x7918)
-	// and indexes the name table at 29be:0c28 ({Jake, Jennifer, he,
-	// she, him, her, his} as far pointers).
-	//   0x80 ??? player's typed name (auto-cap word starts) ??? uses _PlayerRecord
-	//   0x81 ??? _Partner == 0 ? "Jake"     : "Jennifer"  (chosen detective)
-	//   0x82 ??? _Partner == 0 ? "Jennifer" : "Jake"      (the OTHER one)
-	//   0x83 ??? _Partner == 0 ? "he"       : "she"
-	//   0x84 ??? _Partner == 0 ? "him"      : "her"
-	//   0x85 ??? _Partner == 0 ? "his"      : "her"
-	//   0x86..0x88 read a different gender flag at 0x7985 ??? left alone
-	//     until that flag's source is traced.
-	//   0x89 ??? KD hint placeholder (handled by caller).
 	const bool isJake = (partner == 0);
 	Common::String out;
 	for (uint i = 0; i < raw.size(); i++) {
@@ -665,7 +578,7 @@ Common::String EEMEngine::parseString(const Common::String &raw,
 			// through the default case and renders as a literal,
 			// pushing the line past the balloon's visual edge ??? the
 			// "bubbles aren't large enough" symptom. Promote it to
-			// `\n` so ScummVM's `Font::wordWrapText` (which honours
+			// `\n` so `Font::wordWrapText` (which honours
 			// embedded newlines) picks the same break point the
 			// original engine did.
 			out += '\n';
@@ -720,24 +633,21 @@ void EEMEngine::applyClueSideEffects(const byte *c) {
 	}
 }
 
+// ClueBlock layout:
+//   +0..1: number (entry count; 0 = no briefing)
+//   +2..3: pic ID for entry 0; entry N>0 uses (entry-1).lastWord
+//   +4..:  array of 62-byte entries
 void EEMEngine::displayClue(const byte *clueBlock) {
 	if (!clueBlock || !_mystery.isLoaded())
 		return;
 
-	// ClueBlock layout (verified against M0.BIN):
-	//   +0..1: number (entry count)
-	//   +2..3: pic ID for entry 0 (entry N>0 uses prev entry's last 2 bytes)
-	//   +4..:  array of 62-byte entries
 	const uint16 number = READ_LE_UINT16(clueBlock);
 	debugC(1, kDebugScript, "displayClue: %u entries", number);
-	if (number == 0 || number > 32) {
-		// number==0 = no briefing (e.g. mystery 0 case-type 4); >32 is a
-		// guard against bad pointers.
+	// number == 0 = no briefing (e.g. mystery 0 case-type 4); >32 = bad ptr.
+	if (number == 0 || number > 32)
 		return;
-	}
 
-	// Snapshot the current screen as the BG so character pics from
-	// earlier entries don't stack on top of each other.
+	// Snapshot BG so per-entry character pics don't stack.
 	Graphics::ManagedSurface bg(320, 200,
 		Graphics::PixelFormat::createFormatCLUT8());
 	bg.clear();
@@ -749,40 +659,29 @@ void EEMEngine::displayClue(const byte *clueBlock) {
 		}
 	}
 
+	// ClueEntry layout (62 bytes):
+	//   +0..1, +2..3:   p0 tx, ty       +4..5, +6..7:   p1 tx, ty
+	//   +8..9, +10..11: bubText offset p0/p1 (rel. TextBlock; -1 = none)
+	//   +12..13, +14..15: balloon pic ID p0/p1
+	//   +0x10/+0x12: bubX, bubY (partner 0)
+	//   +0x14/+0x16: bubX, bubY (partner 1)
+	//   +0x18: Jenny voice (1-based)
+	//   +0x1a: Jake  voice (1-based)
+	//   +0x30..+0x39: 5 notebook entries (-1 terminated)
+	//   +0x3a..+0x3b: KD-anim number (-1 = none)
+	// _DisplayClue @ 2404:05e6: partner 1 uses its own field set only when
+	// bubText1 != -1; otherwise falls back to partner 0 fields entirely.
+	// Partner 0 always uses field 0.
 	for (uint i = 0; i < number && !shouldQuit(); i++) {
-		// Restore BG before drawing this entry's portrait + balloon.
 		g_system->copyRectToScreen(bg.getPixels(), bg.pitch, 0, 0, 320, 200);
 		const byte *c = clueBlock + 4 + i * 62;
-		// Per-partner fields:
-		//   +0..1, +2..3: tx, ty (partner 0)
-		//   +4..5, +6..7: tx, ty (partner 1)
-		//   +8..9, +10..11: bubText offset for partner 0/1 (rel. TextBlock)
-		//   +12..13, +14..15: balloon picture ID for partner 0/1
-		//   +16..17, +18..19: bubX, bubY
-		//   +0x3a..+0x3b:    KD-anim number (-1 = none)
-		// Per `_DisplayClue` @ 2404:05e6: partner 1 uses its own field
-		// set ONLY when bubText1 is not -1; otherwise it falls back to
-		// the partner 0 fields entirely. Partner 0 always uses field 0.
-
-		// Per-clue partner reaction animation. `_DisplayClue` @
-		// 2404:0635-064b checks `clueEntry[+0x3a]` and, when not -1,
-		// calls `_DoKDAnim(num)` BEFORE drawing the speaker portrait.
-		// This is what surfaces "Jenny takes a picture with a camera"
-		// (and the matching Jake gestures) during NPC searches.
+
+		// _DisplayClue @ 2404:0635-064b: _DoKDAnim(num) runs before the
+		// speaker portrait.
 		const int16 kdAnimNum = (int16)READ_LE_UINT16(c + 0x3a);
 		if (kdAnimNum != -1) {
 			playKdAnim((uint16)kdAnimNum);
-			// `playKdAnim` leaves the screen on the partner-less
-			// `_partnerEraseBg`, mirroring its between-frame erase
-			// source. Re-stamp `bg` so the partner sprite captured at
-			// `displayClue` entry returns before we draw the speaker
-			// portrait + balloon. Without this the bubble lands on a
-			// partner-less screen and the partner appears invisible
-			// for the entire first iteration. Mirrors the original's
-			// `_UpdateAnimations @ 172b:09c1` swap on `0x80`: the
-			// kdAnim slot is freed and the WaitHandle (partner) slot
-			// is reactivated with frame index 0xffff, so on the next
-			// tick the partner renders at script[0] of its wait anim.
+			// _UpdateAnimations @ 172b:09c1 reactivates the wait anim.
 			g_system->copyRectToScreen(bg.getPixels(), bg.pitch,
 									   0, 0, 320, 200);
 		}
@@ -798,12 +697,8 @@ void EEMEngine::displayClue(const byte *clueBlock) {
 		const uint16 bubNum = READ_LE_UINT16(c + (useP1 ? 0x0E : 0x0C));
 		const char *raw   = hasText ? _mystery.textAt(textOff) : "";
 
-		// Speaker portrait. Mirrors `_DisplayClue`'s `pic[clues+i*62-2]`:
-		// for entry 0 the pic ID is in the ClueBlock header at +2; for
-		// later entries it sits in the previous entry's last 2 bytes.
-		// Speaker portrait position uses partner 0 fields (+0..+3) when
-		// _partner==0 or when partner 1 falls back; otherwise partner 1
-		// fields (+4..+7). Same logic as the original.
+		// Speaker portrait: pic[clues + i*62 - 2]. Entry 0 ID is in
+		// ClueBlock +2; entries N>0 read (entry-1)+0x3c (last word).
 		const uint16 charX  = READ_LE_UINT16(c + (useP1 ? 4 : 0));
 		const uint16 charY  = READ_LE_UINT16(c + (useP1 ? 6 : 2));
 		const uint16 charPicId = (i == 0)
@@ -817,14 +712,10 @@ void EEMEngine::displayClue(const byte *clueBlock) {
 			}
 		}
 
-		// Substitute control bytes (0x80..0x89) ??? see `parseString` for
-		// the table. 0x81 = chosen detective, 0x82 = the other one.
 		const Common::String text = parseString(raw ? raw : "",
 												_playerName, _partner);
 
-		// Speech balloon. Mirrors `_GetBalloon` + `_AddPicBackground` in
-		// `_DisplayClue`, with the shared balloon metadata table used for
-		// text placement and the fitted balloon variant.
+		// Speech balloon: _GetBalloon + _AddPicBackground.
 		const uint16 fittedBubNum = fitBalloonToText(bubNum, text);
 		Picture balloon;
 		const uint16 balloonId = fittedBubNum & 0x7F;
@@ -833,9 +724,6 @@ void EEMEngine::displayClue(const byte *clueBlock) {
 			_balloonArchive.loadEntry(balloonId, balloon);
 
 		if (_font.isLoaded() && !text.empty()) {
-			// Snapshot the current screen, overlay balloon + text, then
-			// copy the changed band back. This preserves the site BG
-			// underneath unchanged regions.
 			Graphics::Surface *screen = g_system->lockScreen();
 			if (!screen)
 				break;
@@ -853,27 +741,21 @@ void EEMEngine::displayClue(const byte *clueBlock) {
 			if (haveBalloon) {
 				const int bw = MIN<int>(balloon.surface.w, 320 - bubX);
 				const int bh = MIN<int>(balloon.surface.h, 200 - bubY);
-				// `_AddPicBackground` passes `pic->miscflags >> 8` as
-				// the transparent colour to `_Rect_Move_Mask`. The
-				// on-disk u16 at file offset 0 maps to `Picture::flags`.
+				// _AddPicBackground: transparent colour = pic->miscflags >> 8.
 				const byte transp = (byte)(balloon.flags >> 8);
-				// `_GetBalloon @ 172b:1d7d` mirrors the picture
-				// horizontally when `(bubNum & 0x80)` is set ??? used
-				// for right-side speakers so the tail points the
-				// other way. ScummVM's `transBlitFrom` exposes the
-				// same via its `flipped` argument.
+				// _GetBalloon @ 172b:1d7d mirrors horizontally when
+				// (bubNum & 0x80) ??? for right-side speakers.
 				const bool flipBalloon = (fittedBubNum & 0x80) != 0;
 				if (bw > 0 && bh > 0) {
 					scratch.transBlitFrom(balloon.surface,
 										  Common::Point(bubX, bubY),
 										  transp, flipBalloon);
 				}
-				// Per-balloon metadata from `29be:0875` (52 ?? 10 bytes,
-				// indexed by `bubNum & 0x7F`). The original `_DisplayClue`
-				// does `_WordWrap(bubX + table[bub].x, bubY + table[bub].y,
-				// table[bub].w, ...)`. `getBalloonInsets` is the shared
-				// accessor (defined in `graphics.cpp`); fall back to the
-				// (5, 4, 155) entry-23 inset if the lookup fails.
+				// Per-balloon insets at 29be:0875 (52 x 10 bytes,
+				// indexed by bubNum & 0x7F). _DisplayClue does
+				// _WordWrap(bubX + table[bub].x, bubY + table[bub].y,
+				//           table[bub].w, ...).
+				// Fallback (5, 4, 155) = entry-23 inset.
 				uint16 bx = 5;
 				uint16 by = 4;
 				uint16 bw_ = 155;
@@ -883,16 +765,15 @@ void EEMEngine::displayClue(const byte *clueBlock) {
 				textW = bw_;
 				copyH = bh;
 			} else {
-				// No balloon ??? clear a band so old pixels don't bleed.
+				// No balloon: clear band.
 				const Common::Rect band(0, bubY, 320,
 					MIN<int>(bubY + copyH, 200));
 				scratch.fillRect(band, 0);
 				copyY = bubY;
 			}
 
-			// `_DisplayClue` @ 2404:07fe passes fontColor=0 (palette
-			// index 0 of the case-briefing palette 0x22) to `_WordWrap`.
-			// Hard-coding 0xF here gave the wrong colour.
+			// _DisplayClue @ 2404:07fe passes fontColor=0 (palette
+			// index 0 of case-briefing palette 0x22).
 			_font.drawWordWrapped(&scratch, textX, textY,
 				MAX<int>(8, textW), text, 0);
 
@@ -902,22 +783,14 @@ void EEMEngine::displayClue(const byte *clueBlock) {
 			g_system->updateScreen();
 		}
 
-		// `_DisplayClue` @ 2404:0833-085a ??? after the balloon is drawn,
-		// spool the per-clue voice. Each ClueEntry stores two 1-based
-		// sound indices: `+0x18` (Jenny voice) and `+0x1a` (Jake voice).
-		//
-		// Critical gate (verified at 2404:0833):
+		// _DisplayClue @ 2404:0833-085a ??? per-clue voice gate:
 		//   if (clue[+0x18] != 0 && voiceOn && voiceAvail) {
-		//       iVar6 = clue[+0x18];           // Jenny default
-		//       if (Partner == 0) iVar6 = clue[+0x1a];  // Jake override
+		//       iVar6 = clue[+0x18];                  // Jenny default
+		//       if (Partner == 0) iVar6 = clue[+0x1a]; // Jake override
 		//       _SpoolSound(iVar6 - 1);
 		//   }
-		// The condition gates on the JENNY slot regardless of partner.
-		// Some hotspot ClueBlocks define `+0x1a` (Jake voice) but leave
-		// `+0x18` at 0 ??? for those, the original engine plays nothing
-		// and the entry is text-only. Our previous code gated on the
-		// partner-selected slot and ended up firing unrelated voices
-		// (e.g., a "no audio" entry triggering Jake's spoolSound).
+		// Gate is on the Jenny slot regardless of partner; entries with
+		// +0x18 == 0 but +0x1a set are text-only.
 		if (_audio) {
 			const uint16 voiceJenny = READ_LE_UINT16(c + 0x18);
 			if (voiceJenny != 0 && voiceJenny != 0xFFFF) {
@@ -928,19 +801,10 @@ void EEMEngine::displayClue(const byte *clueBlock) {
 			}
 		}
 
-		// Wait for click/key to advance ??? only if we drew something.
-		// ESC skips the entire dialogue rather than just one entry.
-		// Only Return / KP-Enter / Space advance one entry; other keys
-		// are ignored so accidental keystrokes don't blow past dialog
-		// the player hasn't finished reading.
+		// Wait for click/key. ESC skips entire dialog; Return / KP-Enter /
+		// Space advance one entry.
 		if (hasText || (charPicId != 0 && charPicId != 0xFFFF)) {
-			// Click during a clue dialog only dismisses ??? there are no
-			// hover-interactive areas. Drop the highlighted cursor
-			// state the site loop may have left set so the player
-			// doesn't see a "clickable" cursor stuck on top of the
-			// balloon. The post-dialog `MOUSEMOVE` in the site loop
-			// will re-evaluate against hotspots and re-enable as
-			// needed.
+			// Clear any leftover highlighted-cursor state from the site loop.
 			setInteractiveMouseCursor(false);
 			bool advance = false;
 			bool skipAll = false;
@@ -953,9 +817,6 @@ void EEMEngine::displayClue(const byte *clueBlock) {
 						break;
 					}
 					if (ev.type == Common::EVENT_MOUSEMOVE) {
-						// Keep the cursor non-interactive across moves
-						// inside the dialog (defensive ??? in case some
-						// other code path tries to set it).
 						setInteractiveMouseCursor(false);
 						continue;
 					}
@@ -963,13 +824,7 @@ void EEMEngine::displayClue(const byte *clueBlock) {
 						ev.kbd.keycode == Common::KEYCODE_ESCAPE) {
 						advance = true;
 						skipAll = true;
-						// Cut the per-clue voice line that was just
-						// spooled ??? without this the voice keeps
-						// playing past the dialog dismissal and
-						// bleeds into the next screen (e.g. the
-						// case-briefing voice still talking on the
-						// MAP after ESC). Site / briefing MIDI
-						// stays ??? only voice + spool halt here.
+						// Cut voice + spool only (keep MIDI).
 						interruptAudio(/*stopMusicToo=*/false);
 						break;
 					}
@@ -985,16 +840,11 @@ void EEMEngine::displayClue(const byte *clueBlock) {
 						break;
 					}
 				}
-				// Tick the screen so the OSystem cursor follows the
-				// mouse ??? ScummVM redraws the cursor overlay only on
-				// updateScreen.
 				g_system->updateScreen();
 				g_system->delayMillis(10);
 			}
 			if (skipAll) {
-				// Apply remaining side-effects without rendering. The
-				// original silently runs the state updates even when the
-				// player skips ahead.
+				// Apply remaining side-effects without rendering.
 				for (uint k = i; k < number; k++)
 					applyClueSideEffects(clueBlock + 4 + k * 62);
 				return;
@@ -1004,13 +854,8 @@ void EEMEngine::displayClue(const byte *clueBlock) {
 		applyClueSideEffects(c);
 	}
 
-	// `_DisplayClue @ 2404:05e6` lets the per-clue voice bleed past
-	// the last entry's dismissal ??? `_Wait()` returns on ANY input
-	// without touching the digital playback. We deliberately diverge
-	// (mirroring `_StopTheVoice @ 1ff1:0283`'s effect, voice only,
-	// MIDI untouched) so the briefing voice line doesn't keep talking
-	// over the next screen when the player clicks through the final
-	// entry.
+	// _StopTheVoice @ 1ff1:0283 effect (voice only, keep MIDI). Diverges
+	// from _DisplayClue @ 2404:05e6 which lets voice bleed past dismissal.
 	interruptAudio(/*stopMusicToo=*/false);
 }
 
@@ -1027,10 +872,9 @@ void EEMEngine::displayFloppyDialogRecords(const byte *rec, uint count,
 	//   u8  sound    @ +9     (high bit = play voice, low 7 bits = slot)
 	//   u8  textCount@ +10
 	//   u8  textIdx[]@ +11    (1 byte per ??? low 7 bits = NOTES idx)
-	// Text offsets in NOTES are ABSOLUTE byte offsets into the mystery
-	// buffer (verified by note 0 of M0.BIN at file offset 0xd0 holding
-	// "Hello, ..., I'm ... Eagle!"), so we read text via
-	// `mystery.blobAt(noteEntry[+2..3])` for Jake / `+4..5` for Jenny.
+	// NOTES text offsets are absolute byte offsets into the mystery
+	// buffer; read via mystery.blobAt(noteEntry[+2..3]) for Jake,
+	// +4..5 for Jenny.
 	if (!rec || !isFloppy() || !_font.isLoaded() || count == 0)
 		return;
 
@@ -1039,8 +883,7 @@ void EEMEngine::displayFloppyDialogRecords(const byte *rec, uint count,
 	if (!notes || !bufBase)
 		return;
 
-	// Snapshot the current screen so each record restores the
-	// briefing background between bubbles.
+	// Snapshot BG for between-bubble restores.
 	Graphics::ManagedSurface bg(320, 200,
 		Graphics::PixelFormat::createFormatCLUT8());
 	{
@@ -1054,16 +897,9 @@ void EEMEngine::displayFloppyDialogRecords(const byte *rec, uint count,
 	const uint32 dsz       = _mystery.dataSize();
 	const uint32 notesBase = (uint32)(notes - bufBase);
 
-	// Pre-mark every text index across all records as "seen" up front.
-	// `_DisplayHotspotClue_Floppy @ 22dc:05c8` writes
-	// `_TextSeen_Floppy[idx] = 1` inside its render loop, but the
-	// original's `_WaitForClick` blocks until input ??? so the loop always
-	// runs to completion and every text gets marked. Our `waitForClick`
-	// honours ESC as "skip all" (sets `skipAll`/break), which matches
-	// player expectations for fast-forward but used to drop the seen-bit
-	// for every text after the ESC point. Pre-marking restores parity:
-	// ESC fast-forwards the *visual* but the notebook always reflects
-	// every clue the player would have seen if they'd clicked through.
+	// _DisplayHotspotClue_Floppy @ 22dc:05c8 writes _TextSeen_Floppy[idx]=1
+	// inline per text. Pre-mark all up front so ESC-fast-forward still
+	// records every clue in the notebook.
 	{
 		const byte *r = rec;
 		for (uint i = 0; i < count; i++) {
@@ -1078,14 +914,9 @@ void EEMEngine::displayFloppyDialogRecords(const byte *rec, uint count,
 	}
 
 	auto waitForClick = [&]() -> bool {
-		// Drain pending events first so a previous keystroke's tail
-		// doesn't auto-advance the new page.
+		// Drain pending events so a stale keystroke doesn't auto-advance.
 		Common::Event drain;
 		while (g_system->getEventManager()->pollEvent(drain)) {}
-		// Click during the floppy dialog only dismisses ??? no
-		// hover-interactive areas. Clear any stuck highlight from the
-		// site loop so the cursor stays a normal pointer over the
-		// balloon.
 		setInteractiveMouseCursor(false);
 		const uint32 minVisibleMs = 250;
 		const uint32 startedAt = g_system->getMillis();
@@ -1125,25 +956,18 @@ void EEMEngine::displayFloppyDialogRecords(const byte *rec, uint count,
 		const uint8  ballY    = rec[8];
 		const uint8  textCount= rec[10];
 
-		// Per-record voice (byte 9 high bit) ??? see comment in original
-		// header.
+		// byte 9: high bit = play voice slot.
 		const bool playedRecordVoice = (rec[9] & 0x80) != 0 && _audio;
 		if (playedRecordVoice) {
 			const uint slot = rec[9] & 0x7f;
 			_audio->playFloppyVoiceSlot(slot, _partner);
 		}
-		// Suspect-found side effect for byte 9 with high bit clear.
-		// Mirrors `_DisplayHotspotClue_Floppy @ 22dc:0908..0922`:
+		// _DisplayHotspotClue_Floppy @ 22dc:0908..0922 suspect-found:
 		//   if ((rec[9] & 0x80) == 0 && rec[9] != 0)
 		//     _InGallery_Floppy[_GalleryShuffleSeed_Floppy[rec[9]]] = 1;
-		// `_GalleryShuffleSeed_Floppy` is a byte array at DS:0x2d65 that
-		// overlaps `_NewOrder_Floppy` at DS:0x2d66 by one byte: i.e.
-		// `_GalleryShuffleSeed[i+1] == _NewOrder[i]` (verified by
-		// comparing the two writes in `_ReadMystery_Floppy @ 22dc:0178`).
-		// So in our terms the mapping is `_inGallery[_newOrder[b9 - 1]]`.
-		// Skipping `_newOrder` (as we did before) drew the right portrait
-		// only when `_newOrder` happened to be identity ??? randomized
-		// shuffles dropped one of the two M0 suspects.
+		// _GalleryShuffleSeed (DS:0x2d65) overlaps _NewOrder (DS:0x2d66)
+		// by one byte: GalleryShuffleSeed[i+1] == NewOrder[i]. So:
+		// _inGallery[_newOrder[b9 - 1]].
 		const uint8 b9 = rec[9];
 		if ((b9 & 0x80) == 0 && b9 != 0) {
 			const uint logicalIdx = (uint)b9 - 1;
@@ -1216,14 +1040,9 @@ void EEMEngine::displayFloppyDialogRecords(const byte *rec, uint count,
 		const int textX = ballX + textXIns;
 		const int lineH    = _font.getFontHeight();
 
-		// Pagination state ??? `FUN_22dc_05c8`'s text-idx loop uses
-		// `local_1c` (set from the PREVIOUS text's flag bit) to decide
-		// between "fresh page" (redraw balloon, restart Y at top) and
-		// "continuation" (append below previous lines). We mirror that
-		// state machine so multi-text records render the same way the
-		// original does ??? without it our impl concatenates every text
-		// idx into ONE balloon and the result spills out the bottom
-		// (the user's "bubbles aren't large enough" screenshot).
+		// FUN_22dc_05c8 pagination via local_1c (set from previous text's
+		// flag bit): 0 = fresh page (redraw balloon, Y at top),
+		// 1 = continuation (append below previous lines).
 		bool firstPage  = true;
 		int  cursorY    = ballY + textYIns;
 		bool skipAll    = false;
@@ -1255,9 +1074,7 @@ void EEMEngine::displayFloppyDialogRecords(const byte *rec, uint count,
 			scratch.simpleBlitFrom(*bg.surfacePtr());
 
 			if (firstPage) {
-				// Optional character portrait ??? only draws once per
-				// fresh page (matches the original which only redraws
-				// the balloon on `local_1c == 0`).
+				// Original only redraws balloon + portrait on local_1c == 0.
 				if (picID != 0 && picID != 0xFFFF) {
 					Picture pic;
 					if (_picsArchive.getPicture(picID, pic)) {
@@ -1285,7 +1102,6 @@ void EEMEngine::displayFloppyDialogRecords(const byte *rec, uint count,
 				cursorY = ballY + textYIns;
 			}
 
-			// Wrap text into lines and draw each at cursorY.
 			Common::Array<Common::String> lines;
 			_font.wordWrapText(text, MAX<int>(8, (int)textWidth), lines);
 			for (uint l = 0; l < lines.size(); l++) {
@@ -1295,26 +1111,20 @@ void EEMEngine::displayFloppyDialogRecords(const byte *rec, uint count,
 			}
 			cursorY += (int)lines.size() * lineH;
 
-			// Decide pagination for the NEXT text idx based on THIS
-			// text's high bit.
+			// Pagination for next text idx is driven by this text's high bit.
 			const bool textHighBit = (idxByte & 0x80) != 0;
 			const bool isLastText  = (t + 1 == textCount);
 			const bool isLastRec   = (i + 1 == count);
 
-			// Stamp the "click to continue" indicator (PIC 0xa0
-			// "more" arrow / PIC 0xa1 end indicator) before
-			// flipping to wait. Mirrors `_DisplayHotspotClue_Floppy
-			// @ 22dc:08aa` (mid-page) and `@ 22dc:08c0`
-			// (end-of-record). We skip drawing it on the very last
-			// click of the very last record when the caller passes
-			// `lastIndicator == 0` ??? that's the original `param_2 ==
-			// 0` "no indicator" case.
+			// "Click to continue" indicator: PIC 0xa0 ("more" arrow) or
+			// PIC 0xa1 (end). _DisplayHotspotClue_Floppy @ 22dc:08aa
+			// (mid-page) / @ 22dc:08c0 (end-of-record). lastIndicator == 0
+			// matches the original param_2 == 0 "no indicator" case.
 			bool waitNeeded   = false;
 			bool drawArrow    = false;
 			bool useEndPic    = false;
 			if (!isLastText) {
 				if (textHighBit) {
-					// continuation ??? no wait, no indicator
 					firstPage = false;
 				} else {
 					waitNeeded = true;
@@ -1322,15 +1132,12 @@ void EEMEngine::displayFloppyDialogRecords(const byte *rec, uint count,
 					useEndPic  = false;
 				}
 			} else {
-				// Last text in this record.
 				waitNeeded = true;
 				if (!isLastRec) {
-					// More records follow ??? original passes
-					// `param_2 = 1` ??? PIC 0xa0.
+					// More records follow ??? param_2 = 1 ??? PIC 0xa0.
 					drawArrow = true;
 					useEndPic = false;
 				} else {
-					// Last record. Use caller-supplied indicator.
 					if (lastIndicator == 1) {
 						drawArrow = true;
 						useEndPic = false;
@@ -1373,11 +1180,8 @@ void EEMEngine::displayFloppyDialogRecords(const byte *rec, uint count,
 }
 
 void EEMEngine::displayFloppyBriefing(const byte *initBlock) {
-	// Floppy briefing ??? `FUN_19bb_042f @ 19bb:042f` walks
-	// `nDialog = ib[2 + nSubjects]` records starting at
-	// `ib + 3 + nSubjects`. Each record is rendered identically to a
-	// hotspot dialog record (same `FUN_22dc_05c8` callee), so we
-	// share `displayFloppyDialogRecords`.
+	// FUN_19bb_042f @ 19bb:042f: walks nDialog = ib[2 + nSubjects] records
+	// starting at ib + 3 + nSubjects, dispatching each to FUN_22dc_05c8.
 	if (!initBlock || !isFloppy())
 		return;
 	const uint8 nSubjects = initBlock[1];
@@ -1387,19 +1191,12 @@ void EEMEngine::displayFloppyBriefing(const byte *initBlock) {
 }
 
 void EEMEngine::displayFloppyHotspotDialog(uint siteNum, uint hotIdx) {
-	// Floppy hotspot click ??? mirrors `FUN_22dc_0b80 @ 22dc:0b80` +
-	// `FUN_1652_00e6 @ 1652:00e6` + `FUN_1652_006c @ 1652:006c`.
-	// Each site stores a per-hotspot dialog list at
-	// `site_data[+6..7]`. The list is laid out as:
-	//   for each hotspot in order:
+	// FUN_22dc_0b80 @ 22dc:0b80 + FUN_1652_00e6 @ 1652:00e6 +
+	// FUN_1652_006c @ 1652:006c. site_data[+6..7] -> per-hotspot list:
+	//   for each hotspot:
 	//     main record (11 + textCount bytes)
-	//     u8 contFlags  (low 7 bits = continuation count, high bit
-	//                    used by FUN_1652_00e6 to drive the partner
-	//                    pose ??? irrelevant for text rendering)
-	//     contCount ?? { record (11 + textCount bytes) }
-	// We walk past `hotIdx` hotspots, then dispatch the matched main
-	// record + its continuation chain through the same renderer
-	// `displayFloppyBriefing` uses.
+	//     u8 contFlags  (low 7 = cont count, high bit = partner pose)
+	//     contCount x { record (11 + textCount bytes) }
 	if (!_mystery.isLoaded() || !isFloppy())
 		return;
 	const byte *site = _mystery.siteData(siteNum);
@@ -1411,10 +1208,8 @@ void EEMEngine::displayFloppyHotspotDialog(uint siteNum, uint hotIdx) {
 		return;
 	uint32 off = dlgListOff;
 	for (uint h = 0; h < hotIdx; h++) {
-		// Skip main record.
 		const byte *rec = bufBase + off;
 		off += 11 + rec[10];
-		// Read continuation count and skip those records.
 		const uint contCount = bufBase[off] & 0x7F;
 		off += 1;
 		for (uint c = 0; c < contCount; c++) {
@@ -1424,23 +1219,8 @@ void EEMEngine::displayFloppyHotspotDialog(uint siteNum, uint hotIdx) {
 	}
 	if (off >= _mystery.dataSize())
 		return;
-	// Layout per hotspot is:
-	//   main record (11 + textCount bytes)
-	//   1 byte: continuation count (high bit = partner-pose flag,
-	//           low 7 bits = number of follow-up records)
-	//   continuation records (each 11 + textCount bytes, tightly packed)
-	// `displayFloppyDialogRecords` walks tightly so we have to call it
-	// twice ??? once for the main record, once for the continuations ???
-	// otherwise the second iteration treats the cont-count byte as a
-	// record header and runs off the buffer.
-	//
-	// Each `displayFloppyDialogRecords` call snapshots the screen to
-	// know what to redraw between bubbles. If we just call it twice
-	// back-to-back the second snapshot includes the first call's last
-	// bubble, so the second bubble draws on top of the first one (the
-	// "background isn't redrawn" glitch the user reported). Capture
-	// the clean site BG here and restore it between the two calls so
-	// every record snapshots a bubble-free background.
+	// Snapshot clean site BG and restore between main + continuation calls
+	// so each displayFloppyDialogRecords sees a bubble-free background.
 	Graphics::ManagedSurface siteBG(320, 200,
 		Graphics::PixelFormat::createFormatCLUT8());
 	{
@@ -1458,11 +1238,10 @@ void EEMEngine::displayFloppyHotspotDialog(uint siteNum, uint hotIdx) {
 		contFlagsByte = bufBase[off + mainLen];
 		contCount = contFlagsByte & 0x7F;
 	}
-	// `_HandleHotspotClick_Floppy @ 1652:00e6` derives the main
-	// record's `param_2` from the continuation byte:
-	//   contFlagsByte == 0  ??? 0 (no indicator, only one record)
-	//   high bit set         ??? 1 (PIC 0xa0, "more" arrow)
-	//   low 7 bits non-zero  ??? 2 (PIC 0xa1, alternate end)
+	// _HandleHotspotClick_Floppy @ 1652:00e6 derives param_2:
+	//   contFlagsByte == 0  -> 0 (no indicator)
+	//   high bit set        -> 1 (PIC 0xa0, "more")
+	//   low 7 bits non-zero -> 2 (PIC 0xa1, alt end)
 	uint mainIndicator = 0;
 	if (contFlagsByte != 0) {
 		mainIndicator = (contFlagsByte & 0x80) ? 1 : 2;
@@ -1473,19 +1252,12 @@ void EEMEngine::displayFloppyHotspotDialog(uint siteNum, uint hotIdx) {
 	const uint32 contOff = off + mainLen + 1;
 	if (contOff >= _mystery.dataSize())
 		return;
-	// Wipe the main bubble before the continuation chain snapshots the
-	// screen ??? otherwise the first continuation bubble treats the
-	// post-main-bubble image as its background and the main balloon
-	// pixels persist behind every following balloon.
+	// Wipe main bubble so the continuation chain snapshots a clean BG.
 	g_system->copyRectToScreen(siteBG.getPixels(), siteBG.pitch,
 							   0, 0, 320, 200);
 	g_system->updateScreen();
-	// Continuation chain: `_DisplayDialogContinuations_Floppy @
-	// 1652:006c` passes `param_2 + -1 != 0` (= 1 if more records
-	// follow, 0 on the last). Our renderer maps that automatically
-	// because mid-batch records always get PIC 0xa0; the last record
-	// uses our `lastIndicator` argument (= 0, no indicator on the
-	// final continuation).
+	// _DisplayDialogContinuations_Floppy @ 1652:006c: lastIndicator=0
+	// means no indicator on the final continuation.
 	displayFloppyDialogRecords(bufBase + contOff, contCount, 0);
 }
 
@@ -1498,12 +1270,10 @@ bool EEMEngine::areYouSure() {
 		g_system->unlockScreen();
 	}
 
-	// CD `_AreYouSure @ 1a35:0a5c` and floppy `FUN_19bb_0b43` both:
-	//   * load PIC 0x136 as the dialog body,
-	//   * load PIC 0x1fd / 0x1fe as the pressed YES / NO button images,
-	//   * center the dialog,
-	//   * hit-test YES at (x+0x0c,y+0x23)-(x+0x20,y+0x32),
-	//     and NO at (x+0x60,y+0x23)-(x+0x74,y+0x32).
+	// _AreYouSure @ 1a35:0a5c (CD) / FUN_19bb_0b43 (floppy):
+	//   PIC 0x136 = dialog body; PIC 0x1fd/0x1fe = YES/NO pressed buttons.
+	//   YES hit: (x+0x0c, y+0x23) .. (x+0x20, y+0x32)
+	//   NO  hit: (x+0x60, y+0x23) .. (x+0x74, y+0x32)
 	Picture dialogPic;
 	Picture yesPic;
 	Picture noPic;
@@ -1569,7 +1339,7 @@ bool EEMEngine::areYouSure() {
 				break;
 			}
 			if (ev.type == Common::EVENT_KEYDOWN) {
-				// Spanish prompt is "S - Si" ??? accept both Y and S.
+				// Spanish prompt is "S - Si": accept Y and S.
 				if (ev.kbd.keycode == Common::KEYCODE_y ||
 					ev.kbd.keycode == Common::KEYCODE_s ||
 					ev.kbd.keycode == Common::KEYCODE_RETURN) {
@@ -1605,7 +1375,6 @@ bool EEMEngine::areYouSure() {
 		g_system->delayMillis(15);
 	}
 
-	// Restore the screen so the caller's UI is intact.
 	g_system->copyRectToScreen(saved.getPixels(), saved.pitch, 0, 0, 320, 200);
 	g_system->updateScreen();
 	return result;
diff --git a/engines/eem/detection.cpp b/engines/eem/detection.cpp
index 7fe94f56f39..48cf5629a1d 100644
--- a/engines/eem/detection.cpp
+++ b/engines/eem/detection.cpp
@@ -56,10 +56,7 @@ const ADGameDescription gameDescriptions[] = {
 		GUI_OPTIONS_EEM_FLOPPY
 	},
 	{
-		// Spanish floppy release ??? same EEM.EXE binary as the English
-		// floppy (the engine code is identical), with a localised
-		// PICS.DBD that swaps the embedded English image text for
-		// Spanish equivalents.
+		// Spanish floppy: same EEM.EXE as English floppy, localised PICS.DBD.
 		"eem",
 		"Floppy",
 		AD_ENTRY2s("EEM.EXE",   "692a5e6e7f4516d6e40c1f80cbc1b2cc", 109542,
diff --git a/engines/eem/eem.cpp b/engines/eem/eem.cpp
index 0af062785e1..b809bf86c41 100644
--- a/engines/eem/eem.cpp
+++ b/engines/eem/eem.cpp
@@ -47,28 +47,25 @@
 
 namespace EEM {
 
-const uint kPalSize = 768;     ///< 256 colors * 3 bytes
-const uint kNumSitePals = 40;  ///< SITEPALS holds 40 palettes (40 * 768 = 30720)
+const uint kPalSize = 768;
+const uint kNumSitePals = 40;  // SITEPALS: 40 * 768 = 30720
 
-// Picture / palette IDs from the original code (1-based picture IDs).
-const uint kPicEAKidsLogo      = 0x54;  ///< _ShowEAKids: GetPicture(0x54)
-const uint kPicHighScoreLogo   = 0x20c; ///< _ShowHScoreLogo: GetPicture(0x20c)
-const uint kPicStormLogo       = 0x20b; ///< Floppy storm-logo still: PIC 0x20b
+// 1-based picture/palette IDs.
+const uint kPicEAKidsLogo      = 0x54;  // _ShowEAKids
+const uint kPicHighScoreLogo   = 0x20c; // _ShowHScoreLogo
+const uint kPicStormLogo       = 0x20b; // Floppy storm-logo still
 const uint kPalEAKids          = 0x25;
 const uint kPalHighScore       = 0x27;
-const uint kPalStormLogo       = 0x26;  ///< Floppy `FUN_23d2_0605` palette idx
-const uint kPicMousePointer    = 0x50;  ///< Original startup pointer; 0x51 is the wait cursor
+const uint kPalStormLogo       = 0x26;  // Floppy FUN_23d2_0605
+const uint kPicMousePointer    = 0x50;  // 0x51 is the wait cursor
 
 const byte kSaveBodyVer = 1;
 
-// Internal test switch: populate ScrapBook 1 at startup without exposing a
-// game option or changing save format. Set false before release.
+// Test switch: populate ScrapBook 1 at startup without exposing a game
+// option or changing save format. Set false before release.
 const bool kDebugPopulateScrapbook1AtStartup = false;
 
-// Fallback 11x16 mouse cursor used if the selected PIC pointer cannot be
-// loaded. The original game sets the cursor visible/hidden via
-// _MouseCursor; we leave it on once the screens that need it
-// (ChoosePartner, ActionScreen, CaseSelection, sites) are reached.
+// Fallback 11x16 cursor used if PIC pointer load fails.
 //   0 = transparent, 1 = black outline, 2 = white fill
 const byte kCursorBitmap[11 * 16] = {
 	1,1,0,0,0,0,0,0,0,0,0,
@@ -89,14 +86,14 @@ const byte kCursorBitmap[11 * 16] = {
 	0,0,0,0,0,0,1,2,2,1,0
 };
 const byte kCursorPalette[] = {
-	0x00, 0x00, 0x00, // 0 ??? transparent (key)
-	0x00, 0x00, 0x00, // 1 ??? outline
-	0xFF, 0xFF, 0xFF  // 2 ??? fill
+	0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00,
+	0xFF, 0xFF, 0xFF
 };
 const byte kCursorInteractivePalette[] = {
-	0x00, 0x00, 0x00, // 0 ??? transparent (key)
-	0xFF, 0x00, 0x00, // 1 ??? red outline
-	0xFF, 0xFF, 0xFF  // 2 ??? white fill
+	0x00, 0x00, 0x00,
+	0xFF, 0x00, 0x00,
+	0xFF, 0xFF, 0xFF
 };
 
 static void fadeCurrentPaletteToBlack(uint delayMs = 8) {
@@ -201,10 +198,6 @@ EEMEngine::EEMEngine(OSystem *syst, const ADGameDescription *gameDesc)
 	ConfMan.registerDefault("hide_highlight_boxes", false);
 	ConfMan.registerDefault("fit_dialog_balloons", false);
 
-	// `ADGameDescription::extra` is set by the matching entry in
-	// `gameDescriptions[]` ("CD" or "Floppy"). Keep variant detection
-	// purely string-based so a future re-release with a different
-	// `extra` tag falls back to CD-style asset paths.
 	_variant = (gameDesc && gameDesc->extra &&
 				Common::String(gameDesc->extra).contains("Floppy"))
 				 ? kVariantFloppy : kVariantCD;
@@ -228,7 +221,7 @@ void EEMEngine::applyStartupTestOverrides() {
 }
 
 Common::Error EEMEngine::run() {
-	// _SetMode13X @ 1000:0358 enters VGA mode 13h (320x200x256).
+	// _SetMode13X @ 1000:0358 ??? VGA mode 13h.
 	initGraphics(320, 200);
 
 	if (!openArchives())
@@ -237,48 +230,36 @@ Common::Error EEMEngine::run() {
 	if (!loadSitePalettes())
 		return Common::Error(Common::kReadingFailed, "SITEPALS load failed");
 
-	// _LoadFont @ 1b66:023c ??? main 8 px bitmap font.
+	// _LoadFont @ 1b66:023c.
 	if (!_font.load(Common::Path("FONT.FNT")))
 		warning("FONT.FNT failed to load; text will not render");
 
-	// MIDI music player. Mirrors `_InitMIDI @ 20a2:013a`. Constructed
-	// here (after `initGraphics` so the OSystem's timer/mixer is up).
+	// _InitMIDI @ 20a2:013a.
 	_music = new MusicPlayer(isFloppy());
 
-	// Digital audio (VOC + spool). Mirrors `_InitDrivers @ 1ff1:0368`
-	// which `_AIL_register_driver`s SBDIG.ADV / PASDIG.ADV alongside
-	// the MIDI driver.
+	// _InitDrivers @ 1ff1:0368 (SBDIG.ADV / PASDIG.ADV).
 	_audio = new AudioPlayer(this);
 	_audio->setVoiceEnabled(_voiceOn);
 	syncSoundSettings();
 
-	// CD `_main @ 1a35:0f59` and floppy `_main_Floppy @ 19bb:1012`
-	// both load `_GetPicture(0x50)` as the active mouse pointer before
-	// calling `_InitMouse`. PIC 0x51 is present in both archives but has
-	// no executable xrefs and appears to be the wait cursor.
+	// CD `_main @ 1a35:0f59` and floppy `_main_Floppy @ 19bb:1012` both
+	// load `_GetPicture(0x50)` as the active mouse pointer before
+	// `_InitMouse`. PIC 0x51 is present in both archives but has no
+	// executable xrefs and appears to be the wait cursor.
 	// CD's `_SwitchMouse` supports swapping to a hotspot cursor ID stored
 	// at search record +0x0c, but the shipped CD mystery data only uses
 	// cursor 0; floppy search records have no cursor-id field.
 	installMouseCursor(_picsArchive, false);
 	CursorMan.showMouse(false);
 
-	// _AllBlack @ 172b:0d4b paints the screen black before the first handler.
+	// _AllBlack @ 172b:0d4b.
 	byte black[3 * 256] = { 0 };
 	g_system->getPaletteManager()->setPalette(black, 0, 256);
 
 	debugC(1, kDebugGeneral, "EEM engine starting");
 
-	// If the user chose "Load" before pressing Play, the framework
-	// invokes `loadGameState` which sets up `_playerName`, `_partner`,
-	// `_mysteriesSolved`, and (optionally) `_mystery`. Honour that by
-	// skipping the intros ??? the player has already typed their name
-	// and picked a partner, so the title chain + profile picker +
-	// partner picker would all be redundant.
-	//
-	//   * Save HAS a mystery in progress ??? resume at MAP (mirrors the
-	//     original's post-briefing state, handler 0 at 1a35:0e1d).
-	//   * Save has NO mystery ??? drop into `_ActionScreen`, same as the
-	//     original after partner selection.
+	// Resume from save: mystery in progress ??? MAP (handler 0 @ 1a35:0e1d);
+	// otherwise ??? ACTION.
 	const int wantedSave = ConfMan.hasKey("save_slot")
 		? ConfMan.getInt("save_slot") : -1;
 	bool resumed = false;
@@ -303,51 +284,40 @@ Common::Error EEMEngine::run() {
 		}
 	}
 
-	// Skip the entire intro chain (logos + anims + name entry +
-	// partner pick) when resuming a saved profile ??? the partner is
-	// already known, the player has already named themselves, and the
-	// loaded mystery's site loop is what they want to see again.
 	if (resumed)
 		goto screen_loop;
 
-	// Reproduces _DoOpeningAnims @ 2520:082a:
-	//   EA Kids logo (PIC) -> HighScore Productions logo (PIC) ->
-	//   Storm Software logo (BOLT.ANM) -> [music starts] -> 20
-	//   character-intro animations (ANIM01.A..ANIM20.A) -> [music
-	//   restarts] -> TITLE.ANM. Click / any key skips a single clip;
-	//   ESC skips the rest of the chain (waitForInput / playAnm raise
-	//   `_skipIntro` so each subsequent step bails out).
-	//
-	// Music timing (verified at 2520:0883 and 2520:0918):
-	//   - The three logos and `_InitMysterySounds(0x3c)` all run BEFORE
-	//     any `_MIDIPlayFile` call ??? those segments are voice-only.
+	// _DoOpeningAnims @ 2520:082a:
+	//   EA Kids logo (PIC) -> HighScore logo (PIC) -> Storm logo
+	//   (BOLT.ANM) -> [music starts] -> 20 character-intro anims
+	//   (ANIM01.A..ANIM20.A) -> [music restarts] -> TITLE.ANM. Click /
+	//   any key skips one clip; ESC raises _skipIntro so each step bails
+	//   out.
+	// Music timing (2520:0883 + 2520:0918):
+	//   - The three logos and `_InitMysterySounds(0x3c)` run BEFORE any
+	//     `_MIDIPlayFile` ??? those segments are voice-only.
 	//   - Theme starts with `_LoopMIDI = 0x7fff` right before the
 	//     ANIM01..ANIM20 loop (2520:0883).
-	//   - After the loop the original calls `_CleanMysterySounds` and
-	//     then `_MIDIPlayFile("theme.xmi")` again with `_LoopMIDI =
-	//     0xffff` (2520:0918) to restart the theme for TITLE.ANM.
-	//   - `_StopMIDI()` runs on keypress at the title screen
-	//     (2520:094c).
+	//   - After the loop the original calls `_CleanMysterySounds` then
+	//     `_MIDIPlayFile("theme.xmi")` again with `_LoopMIDI = 0xffff`
+	//     (2520:0918) to restart for TITLE.ANM.
+	//   - `_StopMIDI()` runs on keypress at the title screen (2520:094c).
 	_skipIntro = false;
 	if (isFloppy()) {
-		// Floppy opening ??? driven by `FUN_23d2_039c @ 23d2:039c`:
+		// Floppy opening ??? `FUN_23d2_039c @ 23d2:039c`:
 		//   FUN_23d2_0170()  ??? clear palette
 		//   FUN_23d2_004b()  ??? set up timer
-		//   FUN_23d2_050c()  ??? show PIC 0x54 (EA Kids logo, palette 0x25)
-		//   FUN_23d2_06c6()  ??? show PIC 0x20c (High Score logo, palette 0x27)
-		//   FUN_23d2_0605()  ??? show PIC 0x20b (Storm Software, palette
-		//                      0x26) AND play voice slot 25 (thunder.voc
-		//                      ??? verified via the Jake voice table at
-		//                      `2608:0f0e` slot 25 ??? `2608:11ac` =
-		//                      "thunder.voc").
+		//   FUN_23d2_050c()  ??? PIC 0x54 (EA Kids, palette 0x25)
+		//   FUN_23d2_06c6()  ??? PIC 0x20c (High Score, palette 0x27)
+		//   FUN_23d2_0605()  ??? PIC 0x20b (Storm, palette 0x26) AND
+		//                      voice slot 25 = "thunder.voc" (via Jake
+		//                      voice table 2608:0f0e slot 25 ???
+		//                      2608:11ac).
 		//   _MIDIPlayFile("theme.xmi", loop=1)
-		//   _PlayANM(idx=0) ??? CHAT.ANM (filename table at `2608:14fe`
-		//                     index 0 ??? "chat.anm" at `2608:150a`).
-		//   _PlayANM(idx=1) ??? MOVIE.ANM (table index 1 ??? `2608:1513`).
-		// `TITLE.ANM` is shown later by screen-`0xb` handler `@
-		// 19bb:0ebc` once the intro driver returns. The thunder VOC
-		// alongside the storm logo is the "intro voice" the user heard
-		// missing ??? without it the lightning-bolt logo plays silently.
+		//   _PlayANM(0) ??? CHAT.ANM (filename table 2608:14fe[0] ???
+		//                  "chat.anm" at 2608:150a)
+		//   _PlayANM(1) ??? MOVIE.ANM (table[1] ??? 2608:1513)
+		// TITLE.ANM is shown later by screen-0xb handler @ 19bb:0ebc.
 		if (!shouldQuit() && !_skipIntro)
 			showEAKidsLogo();
 		if (!shouldQuit() && !_skipIntro)
@@ -366,11 +336,10 @@ Common::Error EEMEngine::run() {
 		showEAKidsLogo();
 		if (!shouldQuit() && !_skipIntro)
 			showHighScoreLogo();
-		// Storm Software logo: voice + animation. The original at
-		// `_ShowStormLogo @ 2520:0707` calls `_LoadSoundName(
-		// "thunder.voc")` (29be:177d) and passes the buffer to
-		// `OpenDifferenceAnimation_Sound` so the thunder roar plays
-		// alongside the lightning bolt.
+		// Storm Software logo: voice + animation. `_ShowStormLogo @
+		// 2520:0707` calls `_LoadSoundName("thunder.voc")` (29be:177d)
+		// and passes the buffer to `OpenDifferenceAnimation_Sound` so
+		// the thunder roar plays alongside the lightning-bolt BOLT.ANM.
 		if (!shouldQuit() && !_skipIntro) {
 			if (_audio)
 				_audio->playVoc(Common::Path("THUNDER.VOC"));
@@ -381,13 +350,9 @@ Common::Error EEMEngine::run() {
 			if (_audio)
 				_audio->stopVoice();
 		}
-		// `_InitMysterySounds(0x3c)` at 2520:086a ??? load M60.SDX/SDB
-		// so `_SpoolSound(uVar3 - 1)` between the ANIM01..ANIM20 anims
-		// has data to draw from.
+		// _InitMysterySounds(0x3c) @ 2520:086a ??? load M60.SDX/SDB.
 		if (!shouldQuit() && !_skipIntro && _audio)
 			_audio->initMysterySounds(60);
-		// Theme begins HERE ??? after the three silent logos, before
-		// the character-intro reel.
 		if (!shouldQuit() && !_skipIntro && _music)
 			_music->playFile(Common::Path("THEME.XMI"), /*loop=*/true);
 		for (int i = 1; i <= 20 && !shouldQuit() && !_skipIntro; i++) {
@@ -397,21 +362,17 @@ Common::Error EEMEngine::run() {
 			Common::String name = Common::String::format("ANIM%02d.A", i);
 			playAnm(Common::Path(name), 120,
 					/*holdLastFrame=*/false, fadeIn);
-			// `_SpoolSound(uVar3 - 1)` at 2520:08c2 ??? per-character
-			// VO after each anim except the last (`if (uVar3 != 0x14)`
-			// at 2520:08a8). Original blocks until done; we run async
-			// and wait so the next anim doesn't start prematurely.
+			// _SpoolSound(uVar3 - 1) @ 2520:08c2 ??? per-anim VO, skipped
+			// when uVar3 == 0x14 @ 2520:08a8.
 			if (!shouldQuit() && !_skipIntro && i != 20 && _audio) {
 				_audio->spoolSound((uint)(i - 1));
 				_audio->waitForSpoolDone();
 			}
 		}
-		// `_CleanMysterySounds` at 2520:0903 ??? release M60 before the
-		// title.
+		// _CleanMysterySounds @ 2520:0903.
 		if (_audio)
 			_audio->cleanMysterySounds();
-		// Restart the theme for TITLE.ANM ??? matches the second
-		// `_MIDIPlayFile("theme.xmi")` call at 2520:0918.
+		// _MIDIPlayFile("theme.xmi") @ 2520:0918.
 		if (!shouldQuit() && !_skipIntro && _music)
 			_music->playFile(Common::Path("THEME.XMI"), /*loop=*/true);
 		if (!shouldQuit() && !_skipIntro)
@@ -426,24 +387,13 @@ Common::Error EEMEngine::run() {
 		goto screen_loop;
 	}
 
-	// After the title chain, the original goes Title (B) -> screen 8
-	// (NewPlayer / saved-record selection) -> screen 9 (ChoosePartner) ->
-	// screen C (ActionScreen). Choosing a mystery there enters screen A
-	// (CaseSelection) and then the site loop.
-	// Mouse stays hidden through the opening anims; show it now for
-	// the interactive screens (matches `_MouseCursor = 1` at the tail
-	// of `_NewPlayer`).
+	// Title(B) -> screen 8 (profile) -> 9 (partner) -> C (action) ->
+	// A (case selection) -> site loop.
 	CursorMan.showMouse(true);
 
-	// Stop the title music ??? the original `_NewPlayer / _DoChoosePartner`
-	// screens have no music until the briefing's `_PlayInSequence` /
-	// per-mystery `_StartTravelMusic` kicks in.
 	if (_music)
 		_music->stop();
-	// Profile pick (or fresh creation) ??? `screen8_handler @ 1c33:1012`.
-	// `doProfilePicker` lists existing profiles via `listProfiles()`
-	// and falls through to `doNewPlayer` if none exist or the user
-	// picks "[New Player]".
+	// screen8_handler @ 1c33:1012.
 	if (!shouldQuit())
 		doProfilePicker();
 	if (!shouldQuit())
@@ -451,31 +401,16 @@ Common::Error EEMEngine::run() {
 	if (!shouldQuit())
 		doChoosePartner();
 
-	// Now drop into the screen-driver state machine ??? same pattern as
-	// `_ScreenDriver @ 1a35:0dc1` + the per-screen handlers in the
-	// table at 1a35:0e5e. The original sets `_NextScreen` either
-	// directly (e.g. `_DisplayCorrect` writes 12 = ACTION) or via the
-	// jumptable handlers (e.g. handler 0 calls `_DoInitClues` then
-	// writes 1 = MAP). The handlers here mirror that exactly: each
-	// case body runs the screen and updates `_nextScreen` for the next
-	// iteration. Sentinel `kScreenInvalid` (0xFFFF) ends the loop ???
-	// same as the original's table-end marker.
-	//
-	// `_DoChoosePartner @ 1a35:099d` sets `_NextScreen = 0xc` (= the
-	// original `_ActionScreen` ??? "Choose A Mystery / Practice Mystery /
-	// See ScrapBook 1..3"). That screen is separate from handler 10's
-	// `_DoChooseMystery` / `_CaseSelection`, which is where the "Book N"
-	// title is drawn.
-	//
-	// Mid-mystery profile resume: if the profile picker loaded a
-	// save whose `hasMystery` flag was set, `_mystery.isLoaded()` is
-	// true here and the player just re-picked their partner. Drop
-	// straight to MAP rather than the action menu so they don't have
-	// to walk back through the case picker (which would
-	// `_mystery.load()` fresh and discard their site / clue
-	// progress). The original has no equivalent ??? it persists only
-	// profile-level state via `_PlayerRecord`, not in-progress
-	// mysteries ??? so this is a ScummVM-only ergonomics improvement.
+	// Drop into the screen-driver state machine ??? same pattern as
+	// `_ScreenDriver @ 1a35:0dc1` + the per-screen handler table at
+	// 1a35:0e5e. Sentinel `kScreenInvalid` (0xFFFF) ends the loop.
+	// `_DoChoosePartner @ 1a35:099d` writes `_NextScreen = 0xc` (the
+	// original `_ActionScreen`, which is separate from handler 10's
+	// `_DoChooseMystery` / `_CaseSelection`).
+	// Mid-mystery resume: if the loaded save had `hasMystery` set,
+	// drop straight to MAP rather than the action menu so the player
+	// doesn't walk back through the case picker (which would
+	// `_mystery.load()` fresh and discard site / clue progress).
 	if (!shouldQuit() && !resumed)
 		_nextScreen = _mystery.isLoaded() ? kScreenMap : kScreenAction;
 screen_loop:
@@ -485,11 +420,9 @@ screen_loop:
 
 		switch (current) {
 		case kScreenTitle:
-			// Floppy handler 0xb (`_HandleScreen11_Title_Floppy`) calls
-			// `_DoTitle_Floppy`, whose `_PlayTitleANM_Floppy(1)` file
-			// table entry is `TITLE.ANM`. The opening driver stops after
-			// `MOVIE.ANM`; this live screen owns the title wait and then
-			// writes `_NextScreen = 8` for the profile picker.
+			// Floppy handler 0xb _HandleScreen11_Title_Floppy ->
+			// _DoTitle_Floppy -> _PlayTitleANM_Floppy(1)=TITLE.ANM.
+			// Writes _NextScreen=8 (profile picker).
 			_nextScreen = kScreenProfile;
 			if (isFloppy()) {
 				CursorMan.showMouse(false);
@@ -503,8 +436,8 @@ screen_loop:
 		case kScreenAction:
 			// Top-level post-profile / post-mystery menu. `_ActionScreen
 			// @ 1c33:195b` shows the 5-entry "Choose A Mystery /
-			// Practice / ScrapBook" picker and writes screen 0xa only
-			// when the player picks "Choose A Mystery".
+			// Practice / ScrapBook 1..3" picker; writes _NextScreen=0xa
+			// only when the player picks "Choose A Mystery".
 			_nextScreen = kScreenInvalid;
 			doActionScreen();
 			if (_nextScreen == kScreenInvalid && _mystery.isLoaded())
@@ -512,9 +445,8 @@ screen_loop:
 			break;
 
 		case kScreenChooseMystery:
-			// Handler 10 at 1a35:0e0e calls `_DoChooseMystery` which
-			// presets `_NextScreen = 0` (INIT_CLUES) before
-			// `_CaseSelection`.
+			// Handler 10 @ 1a35:0e0e -> _DoChooseMystery (presets
+			// _NextScreen=0 INIT_CLUES) -> _CaseSelection.
 			_nextScreen = kScreenInvalid;
 			doCaseSelection();
 			if (_nextScreen == kScreenInvalid && _mystery.isLoaded())
@@ -522,8 +454,8 @@ screen_loop:
 			break;
 
 		case kScreenInitClues:
-			// Handler 0 at 1a35:0e14 runs `_PreLoad` + `_DoInitClues`
-			// then writes `_NextScreen = 1` (MAP).
+			// Handler 0 @ 1a35:0e14 -> _PreLoad + _DoInitClues, writes
+			// _NextScreen=1 (MAP).
 			doInitClues();
 			_nextScreen = _mystery.isLoaded() ? kScreenMap
 											  : kScreenAction;
@@ -531,13 +463,11 @@ screen_loop:
 
 		case kScreenMap:
 		case kScreenMapAlt:
-			// Handler 1/2 at 1a35:0e25 calls `_DoMapScreen @
-			// 20fe:120b` (floppy: 19bb:0ef3 -> 1fed map code),
-			// which manages its own `_NextScreen` writes ??? 3 (a site
-			// was clicked), 6 (setup), or 0xffff (quit).
-			// Our `doBigMap` keeps the original's "click site, then
-			// enter the site loop" behaviour inline; once it returns
-			// the natural next state is SITE.
+			// Handler 1/2 @ 1a35:0e25 -> `_DoMapScreen @ 20fe:120b`
+			// (floppy 19bb:0ef3 -> 1fed map code), which manages its
+			// own _NextScreen writes: 3 (site clicked), 6 (setup), or
+			// 0xffff (quit). After `doBigMap` returns the natural
+			// next state is SITE.
 			doBigMap();
 			if (!_mystery.isLoaded())
 				_nextScreen = kScreenAction;
@@ -546,21 +476,19 @@ screen_loop:
 			break;
 
 		case kScreenSite:
-			// Handler 3 at 1a35:0e2c calls `_DoSiteLoop @
-			// 168d:03f4` (floppy's equivalent dispatches through
-			// 1652). Site writes `_NextScreen` for PDA / map rather
-			// than entering those screens as nested modals.
+			// Handler 3 @ 1a35:0e2c -> `_DoSiteLoop @ 168d:03f4`
+			// (floppy dispatches through 1652). Site writes _NextScreen
+			// for PDA / map rather than entering those as nested modals.
 			doSiteLoop();
 			if (!_mystery.isLoaded())
 				_nextScreen = kScreenAction;
 			else if (_nextScreen == current)
-				_nextScreen = kScreenInvalid;  // user quit
+				_nextScreen = kScreenInvalid;
 			break;
 
 		case kScreenNotebook:
-			// Handler 4 calls the PDA notebook screen. Its button
-			// handler writes 2 (map), 3 (site), 5 (gallery), or 7
-			// (accuse) and then returns to this dispatcher.
+			// Handler 4 ??? PDA notebook screen. Button handler writes
+			// 2 (map), 3 (site), 5 (gallery), or 7 (accuse).
 			doNotebook();
 			if (!_mystery.isLoaded() && _nextScreen != kScreenAction)
 				_nextScreen = kScreenAction;
@@ -569,9 +497,8 @@ screen_loop:
 			break;
 
 		case kScreenGallery:
-			// Handler 5 calls the suspect gallery. Like the original,
-			// ESC and the site button write 3, the map button writes
-			// 2, and the PDA button writes 4.
+			// Handler 5 ??? suspect gallery. ESC and the site button
+			// write 3, the map button writes 2, the PDA button writes 4.
 			doGallery();
 			if (!_mystery.isLoaded() && _nextScreen != kScreenAction)
 				_nextScreen = kScreenAction;
@@ -580,24 +507,15 @@ screen_loop:
 			break;
 
 		case kScreenSetup:
-			// Handler 6 at 1a35:0e48 calls `_DoSetup @ 1f78:044e`.
-			// Reachable via the BigMap setup button which writes
-			// `_NextScreen = 6` (verified at 20fe:0c33). The
-			// original sets `_NextScreen = _LastScreen` on entry,
-			// then the toggle UI returns when ESC / Back is hit;
-			// `doSetup` sets `_nextScreen` itself.
+			// Handler 6 @ 1a35:0e48 -> _DoSetup @ 1f78:044e. Entered
+			// from BigMap setup button (_NextScreen=6 @ 20fe:0c33).
 			doSetup();
 			break;
 
 		case kScreenProfile:
-			// Handler 8 is the player/profile picker. CD
-			// `screen8_handler @ 1c33:1012` loads an existing player
-			// record or runs `_NewPlayer`; floppy
-			// `_HandleScreen8_NewPlayer_Floppy @ 19bb:0ec2` then
-			// writes screen 9. Mirror that route inline: after the
-			// profile is selected, choose a partner, then continue to
-			// the selected profile's loaded case if ScummVM save state
-			// had one, otherwise to case selection.
+			// Handler 8: CD screen8_handler @ 1c33:1012 ->
+			// _NewPlayer; floppy _HandleScreen8_NewPlayer_Floppy @
+			// 19bb:0ec2 writes screen 9.
 			_nextScreen = kScreenInvalid;
 			_mystery.clear();
 			doProfilePicker();
@@ -611,9 +529,8 @@ screen_loop:
 			break;
 
 		case kScreenAccuse:
-			// Handler 7 runs the accusation flow. A failed accusation
-			// returns to `_LastScreen`; a correct solution writes
-			// 0xc (ACTION).
+			// Handler 7 ??? accusation flow. Failed accusation returns to
+			// `_LastScreen`; a correct solution writes 0xc (ACTION).
 			doAccuse();
 			if (!_mystery.isLoaded() && _nextScreen != kScreenAction)
 				_nextScreen = kScreenAction;
@@ -646,7 +563,7 @@ void EEMEngine::setHotspotMouseCursor(bool active) {
 }
 
 bool EEMEngine::openArchives() {
-	// _InitGraphicsSystem @ 172b:0145 opens these five .DBD/.DBX pairs.
+	// _InitGraphicsSystem @ 172b:0145.
 	if (!_picsArchive.open(Common::Path("PICS.DBD"), Common::Path("PICS.DBX"))) {
 		warning("PICS archive missing");
 		return false;
@@ -655,8 +572,6 @@ bool EEMEngine::openArchives() {
 		warning("ANI archive missing");
 		return false;
 	}
-	// SITES + BALLOON are optional for the boot path but needed for site
-	// rendering and clue display.
 	if (!_sitesArchive.open(Common::Path("SITES.DBD"), Common::Path("SITES.DBX")))
 		warning("SITES archive missing ??? site backgrounds disabled");
 	if (!_balloonArchive.open(Common::Path("BALLOON.DBD"), Common::Path("BALLOON.DBX")))
@@ -689,8 +604,8 @@ bool EEMEngine::loadSitePalettes() {
 bool EEMEngine::getSitePalette(uint num, byte *out) const {
 	if (num >= kNumSitePals || _sitePals.size() < (num + 1) * kPalSize)
 		return false;
-	// SITEPALS stores 6-bit VGA-DAC values (0..63); ScummVM expects 8-bit
-	// (0..255), so left-shift by 2 like the original VGA hardware did.
+	// SITEPALS stores 6-bit VGA-DAC values (0..63); ScummVM expects
+	// 8-bit (0..255), so left-shift by 2 like the original VGA hardware.
 	const byte *src = _sitePals.data() + num * kPalSize;
 	for (uint i = 0; i < kPalSize; i++)
 		out[i] = (byte)(src[i] << 2);
@@ -726,12 +641,12 @@ bool EEMEngine::setAnmPalette(const Common::Path &anmPath) {
 
 void EEMEngine::interruptAudio(bool stopMusicToo) {
 	// Mirrors `_CleanMysterySounds @ 202f:05a5` + `_StopMIDI @
-	// 20a2:0512` ??? the original calls both whenever the player aborts
-	// the opening-anim chain or dismisses the title (`_DoOpeningAnims
-	// @ 2520:082a` writes `_LoopMIDI = 0; _StopMIDI();` after the
-	// title-input loop). Conversation / clue-dialog skip paths pass
-	// `stopMusicToo = false` so the site / briefing MIDI keeps going
-	// across an ESC ??? only the per-line voice + spool need to stop.
+	// 20a2:0512` ??? both fire when the player aborts the opening-anim
+	// chain or dismisses the title (`_DoOpeningAnims` writes
+	// `_LoopMIDI = 0; _StopMIDI();` after the title-input loop).
+	// Conversation / clue-dialog skip paths pass `stopMusicToo = false`
+	// so the site / briefing MIDI keeps going across an ESC ??? only the
+	// per-line voice + spool need to stop.
 	if (_audio) {
 		_audio->stopVoice();
 		_audio->stopSpool();
@@ -778,13 +693,8 @@ void EEMEngine::playAnm(const Common::Path &path, uint frameDelayMs,
 		}
 		g_system->updateScreen();
 
-		// Drain events and let the user skip with click/key. The original
-		// uses _CheckFrameRate / _kbhit; we use a simple fixed delay until
-		// the frame-rate calibration logic from _GetSpeedRating is wired up.
-		// ESC additionally sets `_skipIntro` so the opening-anim chain in
-		// run() bails out of the whole sequence instead of advancing to
-		// the next clip ??? and stops every active audio channel so the
-		// theme music / voice spool don't bleed past the abort.
+		// Original uses _CheckFrameRate / _kbhit; fixed delay here.
+		// ESC sets _skipIntro and interrupts audio.
 		const uint32 frameStart = g_system->getMillis();
 		bool aborted = false;
 		while (g_system->getMillis() - frameStart < frameDelayMs && !aborted) {
@@ -805,11 +715,10 @@ void EEMEngine::playAnm(const Common::Path &path, uint frameDelayMs,
 					break;
 				}
 			}
-			// Refresh ScummVM's cursor overlay every tick ??? without
-			// this the cursor only redraws when the next frame is
-			// blitted (every `frameDelayMs` ms, ~8 Hz at 120 ms),
-			// which the user perceives as choppy / unresponsive
-			// during long animations like SCRAPBK.ANI.
+			// Refresh cursor overlay every tick ??? otherwise
+			// the cursor only redraws when the next frame is blitted
+			// (~8 Hz at 120 ms), perceived as choppy during long
+			// animations like SCRAPBK.ANI.
 			g_system->updateScreen();
 			g_system->delayMillis(5);
 		}
@@ -818,9 +727,7 @@ void EEMEngine::playAnm(const Common::Path &path, uint frameDelayMs,
 	}
 
 	if (holdLastFrame && !shouldQuit() && !_skipIntro) {
-		// Mirror the wait-loop at the end of `_DoOpeningAnims`:
-		//   while (!keyDataAvailable) ;
-		// We accept either a click or a key.
+		// _DoOpeningAnims tail: while (!keyDataAvailable).
 		while (!shouldQuit()) {
 			Common::Event ev;
 			bool clicked = false;
@@ -845,10 +752,7 @@ void EEMEngine::playAnm(const Common::Path &path, uint frameDelayMs,
 			g_system->updateScreen();
 			g_system->delayMillis(20);
 		}
-		// `_DoOpeningAnims @ 2520:0945` writes `_LoopMIDI = 0;
-		// _StopMIDI();` once the title-input loop exits ??? so the
-		// theme stops the moment the player dismisses the title,
-		// regardless of whether they used ESC or clicked.
+		// _DoOpeningAnims @ 2520:0945: _LoopMIDI=0; _StopMIDI().
 		if (_music)
 			_music->stop();
 	}
@@ -865,22 +769,9 @@ void EEMEngine::blitAt(const Picture &pic, int x, int y) {
 }
 
 void EEMEngine::waitForInput(uint32 maxMs) {
-	// ESC additionally raises `_skipIntro` so the opening-anim chain
-	// can fast-forward past the rest of the sequence, and stops any
-	// active audio so the theme / voice / spool don't bleed past
-	// the abort. Mirrors the `_CleanMysterySounds` + `_StopMIDI`
-	// pair around the title wait in `_DoOpeningAnims`.
-	//
-	// Only Return / KP-Enter / Space / Escape advance ??? letting any key
-	// dismiss balloons makes typing-while-reading (or a stuck modifier)
-	// blow past dialog the player hasn't finished reading.
-	//
-	// Drop the highlighted-cursor state any caller (site loop, gallery,
-	// notebook hover-handler) may have left on. While a balloon /
-	// intro is showing, click anywhere just dismisses ??? no
-	// hover-interactive areas ??? so a "clickable" cursor over the
-	// dialog is misleading. The caller's MOUSEMOVE handler will
-	// re-enable the highlight after we return if appropriate.
+	// ESC: _skipIntro + interruptAudio (matches _CleanMysterySounds +
+	// _StopMIDI around _DoOpeningAnims title wait).
+	// Only Return/KP-Enter/Space/Escape advance.
 	setInteractiveMouseCursor(false);
 	const uint32 startMs = g_system->getMillis();
 	while (!shouldQuit() && (g_system->getMillis() - startMs < maxMs)) {
@@ -892,8 +783,6 @@ void EEMEngine::waitForInput(uint32 maxMs) {
 				return;
 			}
 			if (event.type == Common::EVENT_MOUSEMOVE) {
-				// Defensive: keep the cursor non-interactive across
-				// moves while the dialog is up.
 				setInteractiveMouseCursor(false);
 				continue;
 			}
@@ -916,18 +805,17 @@ void EEMEngine::waitForInput(uint32 maxMs) {
 }
 
 void EEMEngine::showEAKidsLogo() {
-	// Mirrors `_ShowEAKids @ 2520:05f0`. The original:
+	// _ShowEAKids @ 2520:05f0:
 	//   1. GetPicture(0x54) + MemoryCopy to VGA + GetPalette(0x25).
 	//   2. FRAME_RATE = 25; for j in 0..1, for u in 0..0x36 (= 55):
 	//        OpenColorCycle(0x01, 0x6e)   // bg / outer ring shimmer
 	//        OpenColorCycle(0x81, 0xee)   // inner gradient shimmer
 	//        every 8 ticks: OpenColorCycle(0x70, 0x80)  // mid band
-	//   3. After the 110-tick loop: 5 more cycles of 0x70..0x80.
-	//   4. Wait 0x23 (= 35) more frames.
+	//   3. Tail: 5 more cycles of 0x70..0x80.
+	//   4. Wait 0x23 (=35) more frames.
 	//   5. _OpenFadeOut.
-	// The cycling is what gives the EA Kids logo its characteristic
-	// shifting glow ??? it's NOT a static logo. ESC / click skips the
-	// remaining cycle.
+	// The cycling is what gives the EA Kids logo its shifting glow ???
+	// it's NOT a static logo.
 	Picture pic;
 	if (!_picsArchive.getPicture(kPicEAKidsLogo, pic)) {
 		warning("EA Kids logo (%u) load failed", kPicEAKidsLogo);
@@ -937,12 +825,7 @@ void EEMEngine::showEAKidsLogo() {
 	setSitePalette(kPalEAKids);
 	g_system->updateScreen();
 
-	// 25 fps ??? 40 ms / tick. Two outer iterations ?? 55 ticks each = 110
-	// ticks of palette rotation. The first inner-loop iteration of each
-	// outer pass rotates the in-memory palette once *before* applying
-	// to VGA (original gates the wait + setmany on `show != 0`); we
-	// just call the setter every tick ??? the visible cycle starts
-	// immediately, which is the same end result.
+	// 25 fps -> 40 ms / tick.
 	const uint kFrameMs = 40;
 	int delayCount = 8;
 	bool aborted = false;
@@ -957,7 +840,6 @@ void EEMEngine::showEAKidsLogo() {
 			}
 			g_system->updateScreen();
 
-			// Tick wait + skip detection.
 			const uint32 frameEnd = g_system->getMillis() + kFrameMs;
 			while (g_system->getMillis() < frameEnd && !aborted) {
 				Common::Event ev;
@@ -985,22 +867,19 @@ void EEMEngine::showEAKidsLogo() {
 	if (aborted)
 		return;
 
-	// Tail: 5 more rotations of the mid band, then 35 idle frames.
 	for (uint i = 0; i < 5 && !shouldQuit(); i++)
 		cyclePaletteRangeReverse(0x70, 0x80);
 	g_system->updateScreen();
 	waitForInput(0x23 * kFrameMs);
 
 	// `_OpenFadeOut @ 2520:0093` ??? 16 linear steps from current palette
-	// to black. Without this, the EA Kids logo cuts hard to the next
-	// screen.
+	// to black.
 	fadeCurrentPaletteToBlack();
 }
 
 void EEMEngine::showHighScoreLogo() {
-	// Mirrors `_ShowHScoreLogo @ 2520:0799`:
-	//   GetPicture(0x20c) + MemoryCopy to VGA + GetPalette(0x27) +
-	//   _OpenFadeIn + 50-tick wait at 25 fps + _OpenFadeOut.
+	// _ShowHScoreLogo @ 2520:0799: PIC 0x20c + palette 0x27;
+	// _OpenFadeIn; 50-tick wait @ 25 fps; _OpenFadeOut.
 	Picture pic;
 	if (!_picsArchive.getPicture(kPicHighScoreLogo, pic)) {
 		warning("HighScore logo (%u) load failed", kPicHighScoreLogo);
@@ -1008,9 +887,7 @@ void EEMEngine::showHighScoreLogo() {
 	}
 	blitAt(pic, 0, 0);
 
-	// Load target palette into a buffer, force a black palette, then
-	// fade in ??? without the explicit black step we'd flash the full
-	// logo briefly between blit and fade.
+	// Force black before fade-in to avoid a 1-frame full-logo flash.
 	byte target[kPalSize];
 	if (!getSitePalette(kPalHighScore, target)) {
 		warning("HighScore palette (%u) load failed", kPalHighScore);
@@ -1021,7 +898,7 @@ void EEMEngine::showHighScoreLogo() {
 	g_system->updateScreen();
 	fadePaletteFromBlack(target);
 
-	// 50 ticks at 25 fps = ~2 s.
+	// 50 ticks @ 25 fps.
 	waitForInput(2000);
 
 	fadeCurrentPaletteToBlack();
@@ -1032,9 +909,8 @@ void EEMEngine::showFloppyStormLogo() {
 	//   GetPicture(0x20b); BlitToVGA;
 	//   if (sound) { LoadVOC(slot 25 = "thunder.voc"); PlayVOC(...); }
 	//   GetPalette(0x26); FadeIn; wait 50 ticks; FadeOut.
-	// CD plays `BOLT.ANM` at this slot with `THUNDER.VOC` overlaid; the
-	// floppy uses a static still + the same VOC. Without the VOC the
-	// lightning logo plays silently ??? the user noticed.
+	// CD plays `BOLT.ANM` here with `THUNDER.VOC` overlaid; floppy uses
+	// a static still + the same VOC.
 	Picture pic;
 	if (!_picsArchive.getPicture(kPicStormLogo, pic)) {
 		warning("Storm logo (%u) load failed", kPicStormLogo);
@@ -1063,34 +939,27 @@ void EEMEngine::showFloppyStormLogo() {
 }
 
 void EEMEngine::doSiteLoop() {
-	// Mirrors the per-mystery site loop. SiteScreen::run() handles
-	// hotspot clicks plus M (map), N (notebook), G (gallery), A (accuse),
-	// Tab (next site), ESC (exit).
+	// Per-mystery site loop. SiteScreen::run() handles hotspot clicks
+	// plus M (map), N (notebook), G (gallery), A (accuse), Tab (next
+	// site), ESC (exit).
 	SiteScreen screen(this, &_mystery);
 	screen.run();
 	setHotspotMouseCursor(false);
 }
 
 void EEMEngine::startTravelMusic() {
-	// Mirrors `_StartTravelMusic @ 20a2:0595`:
-	//
+	// _StartTravelMusic @ 20a2:0595:
 	//   for (num = _SiteNumber; num > 4; num -= 5) {}
 	//   if (_MIDIAvailable && _MusicEnabled) {
 	//       if (_IsMIDIPlaying()) _StopMIDI();
 	//       _MIDIPlay(num);
 	//   }
-	//
-	// Five travel tracks: MUS00000.XMI .. MUS00004.XMI, picked by
-	// `_SiteNumber % 5`. ONE-SHOT ??? `_DoOpeningAnims @ 2520:0945`
-	// resets `_LoopMIDI = 0` after the title-screen wait, and
-	// `_StartTravelMusic` doesn't write to it; combined with
-	// `_DoSiteLoop @ 168d:06c0` which waits for the track to play
-	// out and then calls `_StopMIDI()` before the interactive phase
-	// begins, the original effectively plays travel music ONCE
-	// during the entrance animation only ??? the site investigation
-	// itself runs without music. Our previous `loop=true` made the
-	// music never end, leaving travel music droning through site
-	// investigation, accuse, gallery, etc.
+	// Five travel tracks (MUS00000.XMI..MUS00004.XMI), picked by
+	// `_SiteNumber % 5`. ONE-SHOT ??? `_DoOpeningAnims @ 2520:0945` resets
+	// `_LoopMIDI = 0` after the title-screen wait, and the function
+	// doesn't write it; combined with `_DoSiteLoop @ 168d:06c0` calling
+	// `_StopMIDI()` before the interactive phase, travel music plays
+	// ONCE during the entrance animation only.
 	if (!_music || !_mystery.isLoaded() || !_voiceOn)
 		return;
 	const uint num = _mystery._siteNumber % 5;
@@ -1144,16 +1013,10 @@ bool EEMEngine::hasFeature(EngineFeature f) const {
 }
 
 bool EEMEngine::canLoadGameStateCurrently(Common::U32String *) {
-	// Loading mid-mystery would replace `_mystery._data` while
-	// pointers into it are alive on the stack inside `displayClue`
-	// etc. Profile picking still works via `loadProfile` from the
-	// menu screens before a mystery loads.
 	return !_mystery.isLoaded();
 }
 
 bool EEMEngine::canSaveGameStateCurrently(Common::U32String *) {
-	// Profile saves (no mystery loaded) are always OK; mid-mystery
-	// snapshots only after the active case has fully initialised.
 	return true;
 }
 
@@ -1161,31 +1024,27 @@ Common::Error EEMEngine::saveGameStream(Common::WriteStream *stream,
 										 bool isAutosave) {
 	(void)isAutosave;
 
-	// Body header: one byte version. `Common::Serializer::setVersion`
-	// alone doesn't write/read the version, so emit it explicitly and
-	// require an exact match on load.
+	// Body header: explicit 1-byte version.
 	Common::Serializer s(nullptr, stream);
 	s.setVersion(kSaveBodyVer);
 	byte ver = kSaveBodyVer;
 	s.syncAsByte(ver);
 
-	// Profile-level state ??? mirrors the original `_PlayerRecord` body
-	// at `2d5d:3f6a` (159 bytes, written by `_SavePlayerRecord @
-	// 1c33:034f`). The `_PlayerRecord` layout is:
+	// _PlayerRecord body @ 2d5d:3f6a (159 bytes, written by
+	// _SavePlayerRecord @ 1c33:034f). Layout:
 	//   +0x00..+0x0b : player name (12 chars, null-padded)
-	//   +0x0c..+0x1f : random ID bytes used by `_GenerateFilename`
+	//   +0x0c..+0x1f : random ID bytes for `_GenerateFilename`
 	//                  (29be:0dbf "C:\EEMCDSAV\%s.PLR") ??? irrelevant to
 	//                  ScummVM saves which key on slot, not filename.
 	//   +0x20..+0x28 : derived 8-char .PLR basename ??? likewise unused.
-	//   +0x2d        : voice-enable flag (`DAT_2d5d_3f97`, default 1).
-	//   +0x2f        : chain stage (`DAT_2d5d_3f99`, 1=A, 2=B, 3=C ???
-	//                  `_DisplayCorrect` advances it once every case
-	//                  in the current set is solved).
+	//   +0x2d        : voice-enable flag (`DAT_2d5d_3f97`, default 1)
+	//   +0x2f        : chain stage (`DAT_2d5d_3f99`, 1=A, 2=B, 3=C;
+	//                  `_DisplayCorrect` advances once every case in the
+	//                  current set is solved)
 	//   +0x31..+0xa6 : `mysteriesSolved[55]` u16 (0=unsolved, 1=solved,
-	//                  2=solved on first try) ??? `_DisplayCorrect`
-	//                  writes 1 always, 2 when `_FirstTry != 0`.
-	//
-	// We persist the gameplay-meaningful subset and skip the original
+	//                  2=solved on first try ??? `_DisplayCorrect` writes
+	//                  1 always, 2 when `_FirstTry != 0`)
+	// We persist the gameplay-meaningful subset and skip the original's
 	// filename-derivation bytes.
 	s.syncString(_playerName);
 	s.syncBytes(_mysteriesSolved, sizeof(_mysteriesSolved));
@@ -1193,11 +1052,8 @@ Common::Error EEMEngine::saveGameStream(Common::WriteStream *stream,
 	s.syncAsByte(_chainStage);
 	s.syncAsByte(_voiceOn);
 
-	// ScummVM-only extension: persist the in-progress mystery so the
-	// player can resume mid-case. The original engine has no such
-	// notion ??? `_LoadGame @ 2404:0dc7` simply loads a fresh mystery,
-	// it doesn't preserve site progress. The flag lets a profile save
-	// stay valid even when no mystery is loaded (e.g. fresh profile).
+	// Mid-case resume: persist in-progress mystery (no equivalent in
+	// _LoadGame @ 2404:0dc7).
 	bool hasMystery = _mystery.isLoaded();
 	s.syncAsByte(hasMystery);
 	if (hasMystery) {
@@ -1246,13 +1102,13 @@ Common::Error EEMEngine::loadGameStream(Common::SeekableReadStream *stream) {
 			resetSiteArrivalState();
 			return Common::kReadingFailed;
 		}
-		// `_ReadMystery @ 2404:008f` calls `_InitMysterySounds` at the
-		// tail (2404:0298) so the SDB index is in place for clue and
+		// `_ReadMystery @ 2404:008f` tail-calls `_InitMysterySounds`
+		// (2404:0298) so the SDB index is in place for clue and
 		// partner-speech spool sounds. Floppy ships individual
-		// `M-XXXX.VOC` files instead of the bundled SDB / SDX archive,
-		// so skip the init there to avoid spamming "missing" warnings;
-		// `spoolSound` then silently no-ops via the `_currentMystery <
-		// 0` guard until the per-voice VOC mapping is wired up.
+		// `M-XXXX.VOC` files instead of the bundled SDB/SDX archive,
+		// so we skip the init there to avoid "missing" warnings;
+		// `spoolSound` then no-ops via its `_currentMystery < 0` guard
+		// until the per-voice VOC mapping is wired up.
 		if (_audio && !isFloppy())
 			_audio->initMysterySounds(mysteryNum);
 		_mystery.syncState(s);
@@ -1273,13 +1129,9 @@ Common::Error EEMEngine::loadGameStream(Common::SeekableReadStream *stream) {
 }
 
 SaveStateList EEMEngine::listProfiles() const {
-	// Mirrors `_findfirst("*.PLR")` in `screen8_handler @ 1c33:1012`.
-	// We disable autosave (`getAutosaveSlot()` returns -1) but a
-	// pre-existing slot-0 file from a previous run, or one written by
-	// another engine using the same target, would still show up here
-	// and pollute the picker. Filter it out so the picker treats slot
-	// 0 as if it didn't exist ??? matching the original which never
-	// has an autosave concept.
+	// _findfirst("*.PLR") in screen8_handler @ 1c33:1012.
+	// Filter out slot 0 (ScummVM autosave) to match the original which
+	// has no autosave concept.
 	SaveStateList saves = getMetaEngine()->listSaves(_targetName.c_str());
 	for (uint i = 0; i < saves.size(); ) {
 		if (saves[i].getSaveSlot() == 0)
@@ -1296,8 +1148,7 @@ Common::Error EEMEngine::saveProfile(const Common::String &name) {
 
 	const SaveStateList saves = listProfiles();
 
-	// Slot lookup by description: if a save with this profile name
-	// already exists, overwrite it. Same as Wetlands' `saveProfile`.
+	// Overwrite on matching description.
 	int slot = -1;
 	for (auto &s : saves) {
 		if (s.getDescription() == name) {
@@ -1306,13 +1157,8 @@ Common::Error EEMEngine::saveProfile(const Common::String &name) {
 		}
 	}
 
-	// New profile ??? pick the lowest unused visible slot. Slot 0 is
-	// filtered out by `listProfiles()` because it is ScummVM's
-	// conventional autosave slot, so allocating a new profile there
-	// makes the profile disappear from the picker on the next refresh.
-	// The MetaEngine caps us at 99 by default (`getMaximumSaveSlot`);
-	// 25 was the DOS original's limit (`screen8_handler` walks `*.PLR`
-	// up to 25 entries in `local_8c[0x19][2]`).
+	// New profile: pick lowest unused slot >=1 (slot 0 is autosave;
+	// DOS limit was 25 per screen8_handler local_8c[0x19][2]).
 	if (slot < 0) {
 		const int maxSlot = getMetaEngine()->getMaximumSaveSlot();
 		Common::Array<bool> used(maxSlot + 1);
@@ -1359,8 +1205,6 @@ bool EEMEngine::loadProfile(const Common::String &name) {
 }
 
 void EEMEngine::screenDriver() {
-	// Placeholder for the eventual dispatch table (one entry per ScreenId).
-	// run() currently calls handlers directly until the title path lands.
 }
 
 } // End of namespace EEM
diff --git a/engines/eem/eem.h b/engines/eem/eem.h
index 26cb2c8f00d..2d3973a4d97 100644
--- a/engines/eem/eem.h
+++ b/engines/eem/eem.h
@@ -46,45 +46,36 @@ namespace EEM {
 class AudioPlayer;
 class MusicPlayer;
 
-/**
- * Screen IDs used by the original `_ScreenDriver` dispatch table at
- * 1a35:0e5e (and the fallback at 1a35:0e54). 14 entries total: each
- * one is a screen ID + a near function pointer at offset +0x1c. The
- * driver does `JMP word ptr CS:[BX + 0x1c]`, so handlers tail-call ???
- * each one runs the screen and updates `_NextScreen` for the next
- * trip through the dispatcher.
- *
- * IDs and their handlers (verified from disassembly at 1a35:0dec..0e4f):
- *
- *   0  INIT_CLUES  ??? `_PreLoad` + `_DoInitClues`, sets _NextScreen=1
- *   1  MAP         ??? `_DoMapScreen` @ 20fe:120b (sets _NextScreen
- *                    inside; 3 = a site was clicked, 6 = setup, etc.)
- *   2  MAP         ??? same handler as 1 (alternate entry, used when
- *                    `_LastScreen == 2` to swap the briefcase anim)
- *   3  SITE        ??? `_DoSiteLoop` @ 168d:03f4
- *   4  NOTEBOOK    ??? `_DoNotebook` @ 161e:0500
- *   5  GALLERY     ??? `_DoGallery` @ 158f:065b
- *   6  SETUP       ??? `_DoSetup` @ 1f78:044e
- *   7  ACCUSE      ??? `_DoAccuse` @ 1df2:0bdd (win ??? 12, lose ??? last)
- *   8  PROFILE     ??? `screen8_handler` @ 1c33:1012; tail sets =9
- *   9  PARTNER     ??? `_DoChoosePartner` @ 1a35:0756; sets =0xc inside
- *   10 (0xa) CHOOSE_MYSTERY ??? `_DoChooseMystery` + `_CaseSelection`;
- *                    starts with _NextScreen=0 so a successful pick
- *                    falls through to INIT_CLUES.
- *   11 (0xb) TITLE  ??? floppy `_DoTitle_Floppy` plays TITLE.ANM, waits
- *                    for input, then writes =8. CD shows TITLE.ANM in
- *                    `_DoOpeningAnims`, so it usually never enters this
- *                    handler.
- *   12 (0xc) ACTION ??? `_ActionScreen` @ 1c33:195b ??? Choose A Mystery /
- *                    Practice Mystery / See ScrapBook 1..3. Action 1
- *                    sets =10 (CHOOSE_MYSTERY).
- *   0xFFFF SENTINEL ??? exit loop
- *
- * Screen-driver state writes verified via xrefs to `_NextScreen @
- * 2d5d:3f26`: `_DisplayCorrect` writes 0xc (winner returns to ACTION),
- * `_DisplayAlibi` writes `_LastScreen` (loser snaps back), and
- * `_DoSiteLoop` writes 1/3/4 plus 0xffff on ESC.
- */
+/// _ScreenDriver dispatch table @ 1a35:0e5e (fallback @ 1a35:0e54).
+/// 14 entries total: each is a screen ID + near fn ptr at +0x1c; driver
+/// tail-calls via `JMP word ptr CS:[BX + 0x1c]`. Handler bodies update
+/// _NextScreen before returning. Handlers @ 1a35:0dec..0e4f:
+///
+///   0  INIT_CLUES ??? `_PreLoad` + `_DoInitClues`, writes _NextScreen=1
+///   1  MAP        ??? `_DoMapScreen @ 20fe:120b` (writes 3=site clicked,
+///                   6=setup, 0xffff=quit)
+///   2  MAP_ALT    ??? same handler as 1 (used when `_LastScreen == 2` to
+///                   swap the briefcase anim)
+///   3  SITE       ??? `_DoSiteLoop @ 168d:03f4`
+///   4  NOTEBOOK   ??? `_DoNotebook @ 161e:0500`
+///   5  GALLERY    ??? `_DoGallery @ 158f:065b`
+///   6  SETUP      ??? `_DoSetup @ 1f78:044e`
+///   7  ACCUSE     ??? `_DoAccuse @ 1df2:0bdd` (win ??? 0xc, lose ??? _LastScreen)
+///   8  PROFILE    ??? `screen8_handler @ 1c33:1012`; tail sets =9
+///   9  PARTNER    ??? `_DoChoosePartner @ 1a35:0756`; sets =0xc inside
+///   0xa CHOOSE_MYSTERY ??? `_DoChooseMystery` + `_CaseSelection`; presets
+///                   _NextScreen=0 so a successful pick falls through to
+///                   INIT_CLUES
+///   0xb TITLE     ??? floppy `_DoTitle_Floppy` plays TITLE.ANM, writes =8.
+///                   CD shows TITLE.ANM in `_DoOpeningAnims`, so this
+///                   handler is usually unused there.
+///   0xc ACTION    ??? `_ActionScreen @ 1c33:195b` ??? Choose A Mystery /
+///                   Practice / See ScrapBook 1..3. Action 1 sets =0xa.
+///   0xFFFF SENTINEL ??? exit loop
+///
+/// State writes (via xrefs to `_NextScreen @ 2d5d:3f26`): `_DisplayCorrect`
+/// writes 0xc, `_DisplayAlibi` writes `_LastScreen`, `_DoSiteLoop` writes
+/// 1/3/4 plus 0xffff on ESC.
 enum ScreenId {
 	kScreenInvalid        = 0xFFFF,
 	kScreenInitClues      = 0x00,
@@ -102,13 +93,12 @@ enum ScreenId {
 	kScreenAction         = 0x0C
 };
 
-/// Distribution variant. Selected at engine init from the
-/// `ADGameDescription::extra` field set by `gameDescriptions[]` in
-/// `detection.cpp`. Used to gate filename selection (TRAVEL-N.XMI
-/// vs MUS%05u.XMI, FANFARE2.XMI vs MUS00005.XMI, PHONESL.VOC vs
-/// PHONE.VOC), opening-anim flow (MOVIE.ANM vs ANIM01..20.A) and
-/// per-variant sound effects (DING.VOC / NEWSCAN.VOC only ship with
-/// the floppy release).
+/// Distribution variant from `ADGameDescription::extra` (set by
+/// `gameDescriptions[]` in `detection.cpp`). Gates filename selection
+/// (TRAVEL-N.XMI vs MUS%05u.XMI, FANFARE2.XMI vs MUS00005.XMI,
+/// PHONESL.VOC vs PHONE.VOC), opening-anim flow (MOVIE.ANM vs
+/// ANIM01..20.A), and per-variant SFX (DING.VOC / NEWSCAN.VOC ship only
+/// with floppy).
 enum Variant {
 	kVariantCD     = 0,
 	kVariantFloppy = 1,
@@ -130,53 +120,35 @@ public:
 	bool canLoadGameStateCurrently(Common::U32String *msg = nullptr) override;
 	bool canSaveGameStateCurrently(Common::U32String *msg = nullptr) override;
 
-	// Disable ScummVM's periodic autosave. The original engine's
-	// `screen8_handler @ 1c33:1012` builds the profile picker by
-	// walking every `*.PLR` file in the save dir, and we mirror that
-	// via `listProfiles() ??? MetaEngine::listSaves`. Letting the
-	// framework write a slot-0 autosave creates a phantom profile
-	// that shows up on the picker as a real save. Returning -1 tells
-	// the framework to skip autosave entirely.
+	// Autosave disabled: profile picker (screen8_handler @ 1c33:1012)
+	// lists `*.PLR` and a slot-0 autosave would appear as a profile.
 	int getAutosaveSlot() const override { return -1; }
 
-	// ScummVM extended-save hooks. The base `Engine::saveGameState` /
-	// `loadGameState` write/read the framework header (description,
-	// thumbnail, playtime, version) around our body via these
-	// streams. We keep all per-profile state in the body and only
-	// accept the current private body layout.
 	Common::Error saveGameStream(Common::WriteStream *stream,
 								  bool isAutosave = false) override;
 	Common::Error loadGameStream(Common::SeekableReadStream *stream) override;
 
-	// Per-profile save helpers. The original `_PlayerRecord` lives at
-	// `2d5d:3f6a` (159 bytes) and is written by `_SavePlayerRecord @
-	// 1c33:034f` to `C:\EEMCDSAV\<name>.PLR`. The DOS launcher screen
-	// `screen8_handler @ 1c33:1012` walks `*.PLR`, lets the player
-	// pick a profile, and calls `_LoadPlayerRecord`. We mirror the
-	// pattern by mapping each ScummVM save slot to one profile (slot
-	// description = player name) ??? same approach Wetlands uses.
-
-	/// Mirrors `_SavePlayerRecord @ 1c33:034f`. Saves into the slot
-	/// whose description matches @p name, or the lowest unused visible
-	/// slot if no match. Returns the kNoError on success.
+	// Per-profile saves: `_PlayerRecord` @ 2d5d:3f6a (159 bytes), written
+	// by `_SavePlayerRecord @ 1c33:034f` to `C:\EEMCDSAV\<name>.PLR`.
+	// Each save slot maps to one profile (slot description = the
+	// player name) ??? same approach Wetlands uses. `screen8_handler @
+	// 1c33:1012` walks `*.PLR`, lets the player pick a profile, and calls
+	// `_LoadPlayerRecord`.
+
+	/// `_SavePlayerRecord @ 1c33:034f`.
 	Common::Error saveProfile(const Common::String &name);
 
-	/// Mirrors `_LoadPlayerRecord @ 1c33:03a6`. Returns false if no
-	/// slot has @p name as its description.
+	/// `_LoadPlayerRecord @ 1c33:03a6`.
 	bool loadProfile(const Common::String &name);
 
-	/// Mirrors the `_findfirst("*.PLR")` walk inside
-	/// `screen8_handler`. Sorted by slot.
+	/// `_findfirst("*.PLR")` walk inside `screen8_handler`.
 	SaveStateList listProfiles() const;
 
 	const ADGameDescription *_gameDescription;
 	Variant _variant = kVariantCD;
 	Common::Language _language = Common::EN_ANY;
 
-	/// True for the Spanish floppy release. Used to swap hardcoded
-	/// English UI strings (menus, name prompt, notebook labels,
-	/// fallback hint copy) for their Spanish equivalents extracted
-	/// from EEM.EXE in `eem-full-game/floppy-es/`.
+	/// Spanish floppy release.
 	bool isSpanish() const { return _language == Common::ES_ESP; }
 
 	DBDArchive &getPics()    { return _picsArchive; }
@@ -188,156 +160,113 @@ public:
 	const EEMFont &getFont() const { return _font; }
 	uint8       getPartnerIndex() const { return _partner; }
 
-	/// Switch to the red-outline cursor used to hint at interactive
-	/// regions that do not have their own original highlight art.
+	/// Red-outline cursor for interactive regions without original highlight art.
 	void setInteractiveMouseCursor(bool active);
 
-	/// Switch to the interactive cursor while the mouse is over a
-	/// searchable hotspot.
+	/// Interactive cursor over searchable hotspots.
 	void setHotspotMouseCursor(bool active);
 
-	/// Display one ClueBlock. @p clueBlock points at the u16 frame count
-	/// followed by 62-byte ClueEntries. Mirrors _DisplayClue @ 2404:05e6.
+	/// `_DisplayClue @ 2404:05e6`. @p clueBlock points at the u16 frame
+	/// count followed by 62-byte ClueEntries.
 	void displayClue(const byte *clueBlock);
 
-	/// Floppy hotspot click ??? locate the dialog records for the
-	/// clicked hotspot in the site's per-hotspot dialog list at
-	/// `site_data[+6]` and dispatch them through
-	/// `displayFloppyDialogRecords`. Mirrors `FUN_22dc_0b80 +
-	/// FUN_1652_00e6 + FUN_1652_006c`.
+	/// Floppy hotspot click. `FUN_22dc_0b80 + FUN_1652_00e6 + FUN_1652_006c`.
+	/// Locates dialog records in site_data[+6] and dispatches them.
 	void displayFloppyHotspotDialog(uint siteNum, uint hotIdx);
 
-	/// Active player name (saved as the profile-save description).
+	/// Active player name (= profile-save description).
 	const Common::String &playerName() const { return _playerName; }
 
-	/// Apply a single ClueEntry's side effects ??? notebook adds, gallery
-	/// updates, site flags. Called both by `displayClue` after a normal
-	/// click-through and when the player ESC-skips a multi-entry clue.
+	/// Apply a ClueEntry's side effects (notebook, gallery, site flags).
 	void applyClueSideEffects(const byte *entry);
 
-	/// Show clue/notebook screen. Mirrors `_DrawNotes` @ 161e:01d0.
+	/// `_DrawNotes @ 161e:01d0`.
 	void doNotebook();
 
-	/// Show suspect gallery. Mirrors `_DrawGallery` @ 158f:0046.
+	/// `_DrawGallery @ 158f:0046`.
 	void doGallery();
 
-	/// Suspect-detail view inside the gallery. Mirrors
-	/// `MoreInfo @ 158f:0419`: paints PIC 0x3f + suspect picture at
-	/// (0x94, 0x0f), paginates the suspect's found clues inside the
-	/// `_GalleryNoteRect`, and dispatches PDA bottom-bar buttons via
-	/// `_HandleMoreButton @ 158f:027d`. Sets `_nextScreen` and
-	/// returns true when the user picks a button that should exit
-	/// `doGallery` outright (NOTEBOOK / ACCUSE / MAP); returns false
-	/// for plain dismissal (ESC / GALLERY button) so the caller stays
-	/// on the portrait grid.
+	/// `MoreInfo @ 158f:0419`. Suspect-detail view inside the gallery; button
+	/// dispatch via `_HandleMoreButton @ 158f:027d`. Returns true if the
+	/// caller should exit `doGallery` (NOTEBOOK / ACCUSE / MAP).
 	bool moreInfo(const byte *gd, uint suspectIdx,
 				   const Picture &galBg, bool haveBg);
 
-	/// Show big map; click chooses next site. Mirrors `_DoBigMap` @ 20fe:09e7.
+	/// `_DoBigMap @ 20fe:09e7`.
 	void doBigMap();
 
-	/// Run the accuse flow (pick suspect, evaluate chains, show ending).
-	/// Mirrors `_DoAccuseGallery` @ 1df2:0a31 + `_DisplayEnding` @ 1df2:0548.
+	/// Accuse flow. `_DoAccuseGallery @ 1df2:0a31` + `_DisplayEnding @ 1df2:0548`.
 	void doAccuse();
 	void doAccuseFloppy();
 
-	/// Show the accuse-notes screen (PIC 0x1A7, the red "accuse-mode"
-	/// BG with selectable clue list + "N clues" remaining counter).
-	/// Mirrors the outer loop of `_DoAccuse @ 1df2:0bdd`. Returns
-	/// true if the player committed (selected the chain-required
-	/// number of clues and clicked SOLVE), false if they exited via
-	/// ESC / back. Called from `doAccuse` before the evidence gate.
+	/// Accuse-notes screen (PIC 0x1A7). Outer loop of `_DoAccuse @ 1df2:0bdd`.
+	/// Returns true if the player committed (SOLVE clicked), false on ESC.
 	bool doAccuseNotes();
 
-	/// Show a host hint from `KDTextIndex`. Mirrors `_KDHelp` @ 1560:010a +
-	/// `_DisplayHint` @ 1560:0009. Cycles between the two hint slots that
-	/// the original engine tracks via `_SawHelpHint`.
+	/// `_KDHelp @ 1560:010a` + `_DisplayHint @ 1560:0009`. Cycles two
+	/// hint slots tracked via `_SawHelpHint`.
 	void doHelp();
 
-	/// Display the interface-help picture sequence. Mirrors `_InterfaceHelp`
-	/// @ 1560:0205 ??? walks `HelpData @ 29be:00c8`, blits each pic fullscreen,
-	/// and waits for click / key (ESC ends the cycle).
+	/// `_InterfaceHelp @ 1560:0205`. Walks `HelpData @ 29be:00c8`.
 	void doInterfaceHelp(uint num = 0);
 
-	/// First-char-dispatch balloon picker. Mirrors `_GetKDTextBalloon @
-	/// 1df2:0105`. For digit first chars (0..9) returns balloon from the
-	/// table at `29be:1064`; for any other char returns the constant
-	/// `*(u16*)29be:1068 = 0x17`. The original branch is on Borland's
-	/// ctype-bit-1 (= digit) at `29be:2be1 + char`.
+	/// `_GetKDTextBalloon @ 1df2:0105`. Digits (0..9) ??? table @ 29be:1064;
+	/// otherwise `*(u16*)29be:1068 = 0x17`.
 	uint16 getKDTextBalloon(byte firstChar) const;
 
-	/// Cleanup for overly tall original speech balloons. Keeps the original
-	/// bubble family and mirror bit, but picks a shorter sibling when the
-	/// wrapped text leaves enough empty lines.
+	/// Pick a shorter balloon sibling when wrapped text leaves empty lines.
 	uint16 fitBalloonToText(uint16 bubNum, const Common::String &text);
 
-	/// Substitute the 0x80..0x89 control bytes the engine uses inside
-	/// `TextBlock` strings. Mirrors `_ParseString @ 1b66:07c3`; jump
-	/// table at 1b66:0cbe. Used by every clue / hint / balloon caller.
+	/// `_ParseString @ 1b66:07c3`. Substitutes 0x80..0x89 control bytes;
+	/// jump table @ 1b66:0cbe.
 	Common::String parseString(const Common::String &raw,
 							   const Common::String &playerName,
 							   uint partner) const;
 
-	/// Play the partner's one-shot reaction animation slot @num. Mirrors
-	/// `_DoKDAnim @ 168d:028a` + `_PlayAnimation @ 172b:1f46`. The
-	/// per-partner (animId, x, y) come from `_WaitAnims[1+num] @ 29be:0228`,
-	/// and the per-frame timing follows the sequence script that the
-	/// original would index at `_AnimationSequences[seqnum=animId]`. Used
-	/// by `displayClue` when a ClueEntry's KD-anim field (+0x3a) is set ???
-	/// e.g. Jenny's "take a picture" gesture when the player searches an
-	/// NPC. Blocks until the script's first 0x80 marker.
+	/// `_DoKDAnim @ 168d:028a` + `_PlayAnimation @ 172b:1f46`. Per-partner
+	/// (animId, x, y) from `_WaitAnims[1+num] @ 29be:0228`. Blocks until
+	/// the script's first 0x80 marker.
 	void playKdAnim(uint16 num);
 
-	/// Provide a "clean" 320x200 backdrop for the next `playKdAnim` (and
-	/// any future blocking partner-anim playback) to use as the
+	/// Provide a "clean" 320x200 backdrop (site BG + static drops, no
+	/// NPCs / partner) for the next `playKdAnim` to use as the
 	/// background-erase source. Without this, the camera animation would
-	/// composite on top of the static partner sprite from the screen and
-	/// the previous resting frame would bleed through transparent pixels.
-	/// `SiteScreen` calls this with its `_bgSnapshot` (site BG + static
-	/// drops, no NPCs / partner) before invoking `displayClue` from a
-	/// hotspot click. Pass `nullptr` to clear.
+	/// composite on top of the static partner sprite and the previous
+	/// resting frame would bleed through transparent pixels. `SiteScreen`
+	/// passes its `_bgSnapshot` before `displayClue` from a hotspot click.
+	/// Pass `nullptr` to clear.
 	void setPartnerEraseBg(const Graphics::ManagedSurface *bg);
 
-	/// Look up balloon-text-inset metadata. Mirrors the 52-entry table at
-	/// `29be:0875` (CD) / `2608:05f9` (floppy), indexed by
-	/// `(bubNum & 0x7F)`. 10 bytes per entry; the first 3 fields
-	/// (x inset, y inset, text width) are used for text wrap, the last
-	/// 2 (indicator dX/dY) by `drawFloppyBubbleIndicator`. Returns
-	/// false if `bubNum` is outside the table.
+	/// Balloon-text-inset metadata. 52-entry table @ 29be:0875 (CD) /
+	/// 2608:05f9 (floppy), indexed by `(bubNum & 0x7F)`. 10 bytes per
+	/// entry: the first 3 fields (x inset, y inset, text width) are used
+	/// for text wrap; the last 2 (indicator dX/dY) by
+	/// `drawFloppyBubbleIndicator`. Returns false if `bubNum` is outside
+	/// the table.
 	bool getBalloonInsets(uint16 bubNum, uint16 &xInset, uint16 &yInset,
 						  uint16 &textW) const;
 	bool getBalloonIndicatorPos(uint16 bubNum, uint16 &dx,
 								 uint16 &dy) const;
 
-	/// Stamp the floppy "click to continue" indicator (PIC 0xa0 for
-	/// `endIndicator==false`, PIC 0xa1 otherwise) onto @p dst at the
-	/// position derived from the balloon-inset table. Mirrors
-	/// `FUN_22dc_05c8 @ 22dc:08aa` (mid-page) and `@ 22dc:08c0`
-	/// (end-of-record).
+	/// `FUN_22dc_05c8 @ 22dc:08aa` (mid-page) / `@ 22dc:08c0` (end).
+	/// PIC 0xa0 if !endIndicator else PIC 0xa1.
 	void drawFloppyBubbleIndicator(Graphics::ManagedSurface &dst,
 								   uint16 bubNum, int ballX, int ballY,
 								   bool endIndicator);
 
-	/// "Are you sure?" yes/no dialog. Mirrors `_AreYouSure` @ 1a35:0a5c.
-	/// Returns true if the user picked YES.
+	/// `_AreYouSure @ 1a35:0a5c`. Returns true on YES.
 	bool areYouSure();
 
 private:
 	void applyStartupTestOverrides();
 
-	/**
-	 * Central dispatch loop matching the original _ScreenDriver @ 1a35:0dc1.
-	 * Each iteration calls the screen handler that matches _nextScreen.
-	 * Handlers update _lastScreen / _nextScreen and return; the loop exits
-	 * when _nextScreen == kScreenInvalid.
-	 */
+	/// Central dispatch loop matching `_ScreenDriver @ 1a35:0dc1`. Each
+	/// iteration calls the handler that matches `_nextScreen`; handlers
+	/// update `_lastScreen` / `_nextScreen` and return. Loop exits when
+	/// `_nextScreen == kScreenInvalid`.
 	void screenDriver();
 
-	/// Re-render helpers used by the corresponding `doX()` modal screens.
-	/// Each replaces what would otherwise be a `[&]()` capture-everything
-	/// lambda inside the `doX()` body; called from the screen's redraw
-	/// triggers (input changes, frame ticks). The state these need is
-	/// passed via reference parameters or read off engine members.
+	/// Re-render helpers for the corresponding `doX()` modal screens.
 	void drawNotebookFrame(int &page);
 	void drawGalleryFrame(const byte *gd, uint8 numSuspects,
 						  Common::Array<Common::Rect> &slotRects,
@@ -352,34 +281,22 @@ private:
 						   Common::Array<Common::Rect> &slotRects,
 						   Common::Array<int> &slotSuspect);
 
-	/**
-	 * Open the five .DBD/.DBX archive pairs the way _InitGraphicsSystem
-	 * @ 172b:0145 does at boot.
-	 */
+	/// Open the five .DBD/.DBX archive pairs. `_InitGraphicsSystem @ 172b:0145`.
 	bool openArchives();
 
-	/** Slurp SITEPALS into @c _sitePals. Mirrors _ReadPalettes @ 172b:0d89. */
+	/// `_ReadPalettes @ 172b:0d89`. Slurps SITEPALS into `_sitePals`.
 	bool loadSitePalettes();
 
-	/**
-	 * Upload palette index @p num (one of 40 stored in SITEPALS) to the
-	 * screen, with the VGA-DAC 6-bit-to-8-bit shift. Mirrors _GetPalette
-	 * @ 172b:0e80 followed by _setmany @ 1000:0930.
-	 */
+	/// `_GetPalette @ 172b:0e80` + `_setmany @ 1000:0930`. Uploads one of
+	/// 40 SITEPALS palettes with the VGA-DAC 6???8 bit shift.
 	void setSitePalette(uint num);
 
-	/// Fill @p out (256 ?? 3 bytes) with the SITEPALS palette at index
-	/// @p num, expanded from VGA's 6-bit DAC range to 8-bit. Returns
-	/// false if the index is out of range. Used when callers need the
-	/// palette for fade-in (set black first, then fade) without
-	/// flashing the target on screen.
+	/// Fill @p out (256 ?? 3 bytes) with SITEPALS palette @p num, expanded
+	/// from 6-bit DAC to 8-bit. False if out of range.
 	bool getSitePalette(uint num, byte *out) const;
 
-	/**
-	 * Upload a 6-bit VGA palette read from the head of an .ANM file (the
-	 * first 0x300 bytes per Load_Sequence @ 2503:0006). Used until the
-	 * full title-page animation chain is wired in.
-	 */
+	/// Upload the 6-bit VGA palette from the head of an .ANM file
+	/// (first 0x300 bytes per `Load_Sequence @ 2503:0006`).
 	bool setAnmPalette(const Common::Path &anmPath);
 
 public:
@@ -387,256 +304,168 @@ public:
 	void setSitePaletteForSite(uint siteNum) { setSitePalette(siteNum + 1); }
 private:
 
-	/** Blit @p pic to @p x, @p y on screen. */
 	void blitAt(const Picture &pic, int x, int y);
 
-	/** Hold the current frame for up to @p maxMs or until the user inputs. */
 public:
 	void waitForInput(uint32 maxMs);
 private:
 
-	/**
-	 * Play a difference-encoded animation file (.ANM / .A) on the full
-	 * 320x200 screen. Mirrors the data flow of `OpenDifferenceAnimation`
-	 * @ 2520:0337 ??? `Load_Sequence` + `Play_Sequence`. Audio cues are
-	 * skipped for now. The default frame delay is 120 ms to match the
-	 * original FRAME_RATE = 0x78 used by `_DoOpeningAnims`.
-	 *
-	 * If @p holdLastFrame is true the call blocks on the final frame
-	 * until the user clicks or hits a key ??? used for the title screen.
-	 * If @p fadeIn is true the first decoded frame is copied while the
-	 * palette is black, then the animation palette is ramped in like
-	 * `_OpenFadeIn`.
-	 */
+	/// Play a difference-encoded animation file (.ANM / .A) on the full
+	/// 320x200 screen. Mirrors the data flow of `OpenDifferenceAnimation
+	/// @ 2520:0337` ??? `Load_Sequence` + `Play_Sequence`. Audio cues are
+	/// skipped for now. The default 120 ms frame delay matches the
+	/// original `FRAME_RATE = 0x78` used by `_DoOpeningAnims`.
+	/// If @p holdLastFrame is true the call blocks on the final frame
+	/// until the user clicks or hits a key ??? used for the title screen.
+	/// If @p fadeIn is true the first decoded frame is copied while the
+	/// palette is black, then ramped in like `_OpenFadeIn`.
 	void playAnm(const Common::Path &path, uint frameDelayMs = 120,
 				 bool holdLastFrame = false, bool fadeIn = false);
 
-	/// Stop every active audio channel ??? voice, sound spool, and
-	/// MIDI. Mirrors the `_CleanMysterySounds @ 202f:05a5` +
-	/// `_StopMIDI @ 20a2:0512` pair the original triggers when the
-	/// player aborts the opening-anim chain or dismisses the title
-	/// (`_DoOpeningAnims @ 2520:082a` writes `_LoopMIDI = 0;
-	/// _StopMIDI();` after the title-input loop, and
-	/// `_CleanMysterySounds` is called twice ??? once after the loop
-	/// and once before TITLE.ANM). Called from every ESC handler in
-	/// the intro / title chain so the theme music + voice spool
-	/// don't bleed past the abort.
-	/// Stop currently-playing voice / spooled SFX. Pass `stopMusic =
-	/// true` (the default ??? matches `_CleanMysterySounds + _StopMIDI`)
-	/// to also halt the MIDI track; conversation / dialog skip paths
-	/// pass `false` so the site / briefing music keeps going across an
-	/// ESC.
+	/// `_CleanMysterySounds @ 202f:05a5` + `_StopMIDI @ 20a2:0512`.
+	/// `stopMusicToo=false` keeps MIDI playing across dialog skips.
 	void interruptAudio(bool stopMusicToo = true);
 
-	// Screen handlers ??? port targets in screens/ later.
 	void showEAKidsLogo();
 	void showHighScoreLogo();
 	void showFloppyStormLogo();
 
-	/// Profile selector ??? mirrors `screen8_handler @ 1c33:1012`.
-	/// Walks `listProfiles()`, draws the list of existing profile
-	/// names plus a "[New Player]" entry, and either calls
-	/// `loadProfile(name)` on a click or falls through to
-	/// `doNewPlayer()` if the user picks "New". When no profiles
-	/// exist, behaves identically to `doNewPlayer()` (the original
-	/// also bypasses the picker when `local_20 == 0` ??? see
-	/// 1c33:1170: `if (saves == 0) _NewPlayer();`).
+	/// `screen8_handler @ 1c33:1012`. Profile selector ??? walks
+	/// `listProfiles()`, falls through to `doNewPlayer()` if "New" or
+	/// no profiles exist (1c33:1170: `if (saves == 0) _NewPlayer();`).
 	void doProfilePicker();
-	void doNewPlayer();          ///< Mirrors `_NewPlayer` @ 1c33:0dda
+	void doNewPlayer();          ///< `_NewPlayer @ 1c33:0dda`.
 	void doChoosePartner();
 
-	/// Display the per-mystery ending pages from `E<num>.BIN`.
-	/// Mirrors `_DisplayEnding @ 1df2:0548` + `_DisplayEndingPage
-	/// @ 1df2:044c`. The file format is a 2-byte page count followed
-	/// by N pages, each `{ u16 picNum, u16 x1, u16 y1, u16 x2, u16 y2,
-	/// char text[] (null-terminated, ParseString placeholders) }`.
-	/// Blocks until the player exits the ending view or crosses either
-	/// page boundary.
+	/// Display the per-mystery ending pages from `E<num>.BIN`. Mirrors
+	/// `_DisplayEnding @ 1df2:0548` + `_DisplayEndingPage @ 1df2:044c`.
+	/// File format: u16 page count, then N pages of
+	/// `{ u16 picNum, u16 x1, u16 y1, u16 x2, u16 y2,
+	///    char text[] (null-terminated, ParseString placeholders) }`.
 	/// `_ShowOneScrap @ 1f78:0773` is just `_DisplayEnding(num, 1)`,
-	/// so this same call covers the post-mystery scrapbook view from
-	/// the action menu.
-	///
-	/// `firstPage=true` opens at page 0; `false` opens at the last
-	/// page (used by `doShowScrapbook` when navigating backwards
-	/// between mysteries ??? mirrors the `local_8 = 0` write at
-	/// `_ShowScrapbook @ 1f78:067e`).
-	///
-	/// Returns the direction the user wants the caller to navigate:
-	///   -1 ??? previous mystery (LEFT pressed on the first page or
-	///        click in `PrevPageRect` while on first page),
-	///    0 ??? exit the scrapbook (ESC / quit; central clicks are ignored),
+	/// so this call also covers the post-mystery scrapbook view.
+	/// `firstPage=true` opens at page 0; `false` opens at the last page
+	/// (back-nav from `doShowScrapbook`, mirrors the `local_8 = 0` write
+	/// at `_ShowScrapbook @ 1f78:067e`).
+	/// Returns the caller's nav direction (per `[BP-0x18]` @ 1df2:0723):
+	///   -1 ??? previous mystery (LEFT on first page or click in PrevPageRect),
+	///    0 ??? exit scrapbook (ESC / quit),
 	///   +1 ??? next mystery (RIGHT/SPACE/Enter/click on last page).
-	/// Mirrors `_DisplayEnding`'s `[BP-0x18]` return at 1df2:0723.
 	int doShowEnding(uint num, bool firstPage = true);
 
-	/// Browse the scrapbook tier @p stage (1=Junior, 2=Senior,
-	/// 3=Master), moving between mystery endings with the original
-	/// `_DisplayEnding` return direction.
-	/// Mirrors `_ShowScrapbook(stage, 0) @ 1f78:0642`: the original
-	/// computes the mystery range from `(stage - 1) * 0x18 + 1` to
-	/// `(stage - 1) * 0x18 + 0x18`. If this is the current chain stage,
-	/// unsolved entries are skipped; completed tiers are already solved.
-	/// Used by both the setup-screen ScrapBook buttons and the
-	/// action-menu "See ScrapBook 1/2/3" entries.
+	/// `_ShowScrapbook(stage, 0) @ 1f78:0642`. Mystery range
+	/// `(stage-1)*0x18+1 .. (stage-1)*0x18+0x18`; skips unsolved
+	/// entries in the current chain stage.
 	void doShowScrapbook(uint stage);
 
 	void doActionScreen();
 	void doCaseSelection();
 	void doSiteLoop();
 
-	/// Setup / preferences screen. Mirrors `_DoSetup @ 1f78:044e` ???
-	/// per-profile preferences (voice on/off via `DAT_2d5d_3f97`,
-	/// partner pick via SwapColors on Kid1/Kid2 rects). Reachable
-	/// from BigMap's setup button (sets `_NextScreen = 6` per
-	/// `_DoBigMap @ 20fe:0c33`). Returns to whatever
-	/// `_lastScreen` was ??? typically MAP.
+	/// `_DoSetup @ 1f78:044e`. Voice on/off (`DAT_2d5d_3f97`), partner
+	/// pick via SwapColors on Kid1/Kid2 rects.
 	void doSetup();
 
-	/// Render the case briefing background + game/book decorations and
-	/// display the briefing ClueBlock. Mirrors `_DoInitClues` @ 1a35:0411
-	/// minus the live ANI sequence playback.
+	/// `_DoInitClues @ 1a35:0411` (minus live ANI sequence playback).
 	void doInitClues();
 
-	/// Floppy variant of the briefing dialog renderer. Walks the dialog
-	/// record list at the tail of the floppy InitBlock (per
-	/// `FUN_19bb_042f` and `FUN_22dc_05c8 @ 22dc:05c8`) and renders one
-	/// speech balloon per record. Each record is `11 + textCount` bytes:
-	///   +0..1  picID (character portrait, 0 = none)
-	///   +2..3  picX
-	///   +4     picY
-	///   +5     balloonId | (mirror_flag << 7)
-	///   +6..7  balloonX
-	///   +8     balloonY
-	///   +9     soundFlag (high bit) | sound slot (low 7 bits)
-	///   +10    textCount
-	///   +11..  text indices (1 byte each, low 7 bits = NOTES idx)
+	/// Floppy briefing dialog renderer. Walks the dialog record list at
+	/// the tail of the floppy InitBlock (`FUN_19bb_042f`, `FUN_22dc_05c8
+	/// @ 22dc:05c8`). Record = 11 + textCount bytes:
+	///   +0..1 picID  +2..3 picX  +4 picY
+	///   +5 balloonId|(mirror<<7)  +6..7 balloonX  +8 balloonY
+	///   +9 soundFlag|slot  +10 textCount  +11.. text indices
 	void displayFloppyBriefing(const byte *initBlock);
 
-	/// Render `count` consecutive floppy dialog records starting at
-	/// `rec`. Shared between briefing and hotspot click handlers since
-	/// the original engine uses the same `FUN_22dc_05c8 @ 22dc:05c8`
-	/// renderer in both contexts. `lastIndicator` is the `param_2`
-	/// equivalent for the LAST record in the batch ??? 0 = no
-	/// indicator, 1 = PIC 0xa0 (red "more" arrow), 2 = PIC 0xa1
-	/// (alternate end indicator). Records before the last always get
-	/// PIC 0xa0.
+	/// `FUN_22dc_05c8 @ 22dc:05c8`. Renders `count` floppy dialog records.
+	/// `lastIndicator`: 0 = none, 1 = PIC 0xa0, 2 = PIC 0xa1 (records
+	/// before the last always get PIC 0xa0).
 	void displayFloppyDialogRecords(const byte *rec, uint count,
 									 uint lastIndicator = 0);
 
 public:
-	/// Mirrors `_StartTravelMusic @ 20a2:0595`. Picks `MUS%05d.XMI`
-	/// based on `_mystery._siteNumber % 5` and starts it one-shot. The
-	/// site loop calls this each time `enter(siteNum)` runs so the
-	/// music changes as the player travels between sites.
+	/// `_StartTravelMusic @ 20a2:0595`. Picks `MUS%05d.XMI` from
+	/// `_mystery._siteNumber % 5`, one-shot.
 	void startTravelMusic();
 
-	/// Block until the current MIDI cue finishes or the player skips it,
-	/// then stop/unload it. Mirrors the `_IsMIDIPlaying` spin +
-	/// `_StopMIDI` cleanup in `_DoSiteLoop` after the CD travel cue.
+	/// `_IsMIDIPlaying` spin + `_StopMIDI` cleanup in `_DoSiteLoop`.
 	void waitForMusicDone(uint32 maxMs = 60000);
 
-	/// Stop any currently playing MIDI track. Mirrors `_StopMIDI @
-	/// 20a2:0512` ??? used by `_DoSiteLoop @ 168d:06c0` after the
-	/// one-shot travel track plays out and by `_DisplayCorrect /
-	/// _DisplayAlibi` between MIDI cues.
+	/// `_StopMIDI @ 20a2:0512`.
 	void stopMusic();
 
-	/// Forwarded from `Engine::syncSoundSettings`. Re-pulls the user's
-	/// `music_volume` slider into the MIDI player's `_masterVolume`,
-	/// otherwise the AdLib output stays at whatever the slider was at
-	/// the moment `_music` was constructed (and the live launcher
-	/// changes to the volume slider have no effect).
+	/// `Engine::syncSoundSettings` override. Re-pulls `music_volume`
+	/// into the MIDI player's `_masterVolume`.
 	void syncSoundSettings() override;
 private:
 
-	Common::String _playerName;  ///< Substituted into 0x80 placeholders
+	Common::String _playerName;  ///< Substituted into 0x80 placeholders.
 
-	/// Per-mystery solved state. 0 = unsolved, 1 = solved, 2 = solved
-	/// on first try. Mirrors `_PlayerRecord.SolvedMysteries[55]` in the
-	/// original `_DisplayCorrect` flow.
+	/// `_PlayerRecord.SolvedMysteries[55]`. 0=unsolved, 1=solved, 2=first-try.
 	uint8 _mysteriesSolved[55] = {};
 
-	/// Current chain/tier the player is at ??? mirrors `DAT_2d5d_3f99`
+	/// Current chain/tier the player is at ??? `DAT_2d5d_3f99`
 	/// (`_PlayerRecord +0x2f`):
-	///   1 = Junior detective  (mysteries  1 .. 24, "A chain")
-	///   2 = Senior detective  (mysteries 25 .. 48, "B chain")
-	///   3 = Master detective  (mysteries 49 .. 54, "C chain")
-	/// Initialized to 1 in `_NewPlayer @ 1c33:0fa3` and bumped by
+	///   1 = Junior detective  (mysteries  1..24, "A chain")
+	///   2 = Senior detective  (mysteries 25..48, "B chain")
+	///   3 = Master detective  (mysteries 49..54, "C chain")
+	/// Initialized to 1 in `_NewPlayer @ 1c33:0fa3`; bumped by
 	/// `_DisplayCorrect @ 1df2:0853` once every mystery in the current
-	/// tier is solved (range checks at 1df2:080d / 0824 / 0837). The
-	/// value also gates `_CaseSelection`'s book label and selection
-	/// list (1c33:0a87 onwards).
+	/// tier is solved (range checks @ 1df2:080d / 0824 / 0837). Also
+	/// gates `_CaseSelection`'s book label and selection list
+	/// (1c33:0a87 onwards).
 	uint8 _chainStage = 1;
 
-	/// Voice / digital-audio enable flag. Mirrors `DAT_2d5d_3f97`
-	/// (`_PlayerRecord +0x2d`). Set to 1 by `_NewPlayer @ 1c33:0fa3`,
-	/// toggled by the SoundOn / SoundOff hot-rects in `_DoSetup @
-	/// 1f78:044e` (verified at `_SetupSettings` 1f78:0076 reading
-	/// the same byte to colour the on/off labels). Gates every
-	/// `_PlayVoice` and `_SpoolSound` call site (clue voices,
-	/// partner speech, intro VO etc. ??? see `_DoChoosePartner`,
-	/// `_DisplayClue`, `_SayKDDigital` xrefs).
+	/// Voice / digital-audio enable flag. `DAT_2d5d_3f97` (`_PlayerRecord
+	/// +0x2d`). Set to 1 by `_NewPlayer @ 1c33:0fa3`, toggled by the
+	/// SoundOn / SoundOff hot-rects in `_DoSetup @ 1f78:044e`. Gates
+	/// every `_PlayVoice` / `_SpoolSound` call site (clue voices,
+	/// partner speech, intro VO).
 	bool _voiceOn = true;
 
 	Common::RandomSource _rng;
 
-	DBDArchive _picsArchive;     ///< PICS.DBD/.DBX (sprites, buttons, frame backgrounds)
-	DBDArchive _aniArchive;      ///< ANI.DBD/.DBX (multi-frame character animations)
-	DBDArchive _sitesArchive;    ///< SITES.DBD/.DBX (one full-screen scene per site)
-	DBDArchive _balloonArchive;  ///< BALLOON.DBD/.DBX (speech-balloon sprites)
-	DBDArchive _buttonArchive;   ///< BUTTON.DBD/.DBX (per-site labeled map buttons; `_GetButton`)
-	Mystery    _mystery;         ///< Currently-loaded case file (M<n>.BIN)
-	EEMFont    _font;            ///< FONT.FNT - main 8 px font
+	DBDArchive _picsArchive;     ///< PICS.DBD/.DBX
+	DBDArchive _aniArchive;      ///< ANI.DBD/.DBX
+	DBDArchive _sitesArchive;    ///< SITES.DBD/.DBX
+	DBDArchive _balloonArchive;  ///< BALLOON.DBD/.DBX
+	DBDArchive _buttonArchive;   ///< BUTTON.DBD/.DBX (`_GetButton`)
+	Mystery    _mystery;         ///< M<n>.BIN
+	EEMFont    _font;            ///< FONT.FNT (8 px)
 
-	Common::Array<byte> _sitePals; ///< 40 x 768 bytes of 6-bit VGA palettes
+	Common::Array<byte> _sitePals; ///< 40 ?? 768 bytes, 6-bit VGA.
 
-	uint16 _lastScreen;  ///< Mirrors _LastScreen @ 2d5d:3f24
-	uint16 _nextScreen;  ///< Mirrors _NextScreen @ 2d5d:3f26
-	uint8  _partner;     ///< Mirrors _Partner: 0 = boy (Jake), 1 = girl (Jenny)
+	uint16 _lastScreen;  ///< `_LastScreen @ 2d5d:3f24`.
+	uint16 _nextScreen;  ///< `_NextScreen @ 2d5d:3f26`.
+	uint8  _partner;     ///< `_Partner`: 0=Jake, 1=Jenny.
 
-	/// Set when ESC is pressed during an intro animation or logo. Tells
-	/// the opening-anim loop in run() to skip the rest of the chain
-	/// instead of asking the user to click through every screen.
+	/// ESC during intro: skip remaining opening-anim chain.
 	bool _skipIntro = false;
 
-	/// Per-slot rectangles + clue IDs from the most recent notebook
-	/// render, populated by the `draw` lambda inside `doNotebook` and
-	/// consumed by the click handler. The original walks the notes
-	/// inline; we cache the layout to keep click hit-testing simple.
+	/// Cached notebook slot rects + clue IDs for click hit-testing.
 	Common::Array<Common::Rect> _notebookSlotRects;
 	Common::Array<uint>         _notebookSlotClues;
 
-	/// Optional clean BG (no partner / NPC sprites) used by `playKdAnim`
-	/// to erase the partner's resting frame between camera-anim cells.
-	/// Empty when no caller has provided one (PDA / case briefing /
-	/// accuse contexts use their own composed backdrops). See
-	/// `setPartnerEraseBg`.
+	/// Clean BG (no partner/NPC) used by `playKdAnim` between camera-anim
+	/// cells. See `setPartnerEraseBg`.
 	Graphics::ManagedSurface _partnerEraseBg;
 
 	bool _interactiveMouseCursor = false;
 
-	/// Site whose entrance animation has already played in the current
-	/// mystery. Kept on the engine, not SiteScreen, because PDA/gallery
-	/// screens destroy and recreate SiteScreen when returning to the site.
+	/// Site whose entrance animation has already played this mystery.
+	/// Lives on the engine because PDA/gallery destroys+recreates SiteScreen.
 	int _lastSiteArrivalAnim = -1;
 
-	/// XMIDI music player. Mirrors the original `MIDI.C` family
-	/// (`_MIDIPlayFile`, `_MIDIPlay`, `_StopMIDI`, `_StartTravelMusic`
-	/// at 20a2:00e2-05c9). Constructed lazily during `run()` once the
-	/// MIDI driver / timer system is up.
+	/// `MIDI.C` family (`_MIDIPlayFile`/`_MIDIPlay`/`_StopMIDI`/
+	/// `_StartTravelMusic` @ 20a2:00e2-05c9). Constructed lazily in `run()`.
 	MusicPlayer *_music = nullptr;
 
-	/// Digitised audio (voice + SFX). Mirrors `SOUND.C` / `SPOOLSND.C`
-	/// ??? VOC playback (`_PlayVoice @ 1ff1:023e`) and the per-mystery
-	/// SDB spool (`_SpoolSound @ 202f:068d` / `_InitMysterySounds @
-	/// 202f:05cb`). Constructed alongside `_music` in `run()`.
+	/// `SOUND.C` / `SPOOLSND.C`. `_PlayVoice @ 1ff1:023e`,
+	/// `_SpoolSound @ 202f:068d`, `_InitMysterySounds @ 202f:05cb`.
 public:
 	AudioPlayer *_audio = nullptr;
 
-	/// Public setter for `_nextScreen` so site loop / inline screens
-	/// can drive the screen-driver state machine without making the
-	/// member itself public. Mirrors the original's direct write to
-	/// `_NextScreen @ 2d5d:3f26` from anywhere in the engine.
+	/// `_NextScreen @ 2d5d:3f26` writer for site loop / inline screens.
 	void setNextScreen(ScreenId s) { _nextScreen = s; }
 	bool shouldPlaySiteArrival(uint siteNum) const {
 		return _lastSiteArrivalAnim != (int)siteNum;
diff --git a/engines/eem/font.cpp b/engines/eem/font.cpp
index 36d584c62b5..a5fba38d7f1 100644
--- a/engines/eem/font.cpp
+++ b/engines/eem/font.cpp
@@ -30,18 +30,13 @@
 
 namespace EEM {
 
-// Character ??? glyph translation table.
-//
-// FONT.FNT layout (verified by dumping glyph bitmaps):
-//   index 0..26  : ' ' .. ':' (punctuation, digits)
-//   index 27..32 : ';' '<' '=' '>' '?' '@'
-//   index 33..58 : UPPERCASE A..Z
-//   index 59..84 : lowercase a..z
-//
-// The CHR2FNT segment table at 29b6:0000 in the original maps both 'A'
-// and 'a' to the lowercase glyph (so the original engine renders all
-// text in lowercase). We route uppercase ASCII letters to the uppercase
-// glyph slots (33..58) for proper mixed-case rendering.
+// CHR2FNT @ 29b6:0000. FONT.FNT layout:
+//   0..26  : ' ' .. ':'
+//   27..32 : ';' '<' '=' '>' '?' '@'
+//   33..58 : A..Z
+//   59..84 : a..z
+// Original aliases uppercase to lowercase glyphs; we route uppercase
+// ASCII to slots 33..58 for mixed-case rendering.
 const byte kCharToGlyph[128] = {
 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -100,15 +95,10 @@ bool EEMFont::load(const Common::Path &path) {
 			_maxWidth = g.widthBits;
 	}
 
-	// `_LoadFont @ 1b03:0220` sets the line stride to the FIRST
-	// glyph's height (= the space character) ??? `DAT_28da_30ca =
-	// DAT_28da_30ce` (the first byte of glyph 0 = its height). Tall
-	// descender glyphs ('g', 'j', 'p', 'q', 'y') hang into the next
-	// row by design, which is how the original engine keeps balloon
-	// text dense enough to fit. Using `_maxHeight` (the descender-
-	// inflated value) added 2???3 px per line and made every multi-
-	// line bubble overflow its graphic vertically ??? verbatim user-
-	// reported "bubbles aren't large enough" symptom.
+	// _LoadFont @ 1b03:0220 sets line stride to the first glyph's
+	// height (DAT_28da_30ca = DAT_28da_30ce, space glyph height).
+	// Descenders ('g','j','p','q','y') intentionally overhang into
+	// the next row.
 	_lineHeight = !_glyphs.empty() ? _glyphs[0].height : _maxHeight;
 	if (_lineHeight == 0)
 		_lineHeight = _maxHeight;
diff --git a/engines/eem/font.h b/engines/eem/font.h
index ae5a0638d7b..56275b8018e 100644
--- a/engines/eem/font.h
+++ b/engines/eem/font.h
@@ -40,18 +40,13 @@ struct FontGlyph {
 };
 
 /**
- * Loader for the engine's `.FNT` files (FONT.FNT, SYSTEM.FNT, TINY.FNT,
- * 8PNTTHIN.FNT). Mirrors `_LoadFont` @ 1b66:023c.
+ * Loader for .FNT files (FONT.FNT, SYSTEM.FNT, TINY.FNT, 8PNTTHIN.FNT).
+ * _LoadFont @ 1b66:023c.
  *
- * File layout:
- *   - u16 numChars
- *   - per char: u8 height, u8 widthBits, u8 sizeBytes, bytes[sizeBytes] bitmap
+ * Layout: u16 numChars, then per char u8 height, u8 widthBits,
+ * u8 sizeBytes, bytes[sizeBytes] bitmap.
  *
- * Subclasses `Graphics::Font` so callers can use the standard
- * `drawString` / `drawStringUnboxed` / `wordWrapText` helpers without
- * us reimplementing them. Lookups go through a 128-byte char???glyph
- * translation table extracted from CHR2FNT (segment 29b6:0000) ??? the
- * font is uppercase-only with lowercase aliased to uppercase glyphs.
+ * Char ??? glyph table extracted from CHR2FNT (29b6:0000).
  */
 class EEMFont : public Graphics::Font {
 public:
@@ -70,9 +65,8 @@ public:
 				  uint32 color) const override;
 	using Graphics::Font::drawChar;  // keep ManagedSurface overload
 
-	/// Convenience wrap-and-draw helper that uses the inherited
-	/// `wordWrapText` to break @p s into lines and then `drawString`
-	/// to render each. Returns the total height drawn.
+	/// Wrap @p s to @p width via the inherited `wordWrapText` and draw
+	/// each line at (x, y) downward. Returns total height drawn.
 	int drawWordWrapped(Graphics::ManagedSurface *dst, int x, int y,
 						int width, const Common::String &s, uint32 color) const;
 
@@ -80,7 +74,7 @@ private:
 	Common::Array<FontGlyph> _glyphs;
 	uint16 _maxHeight  = 0;
 	uint16 _maxWidth   = 0;
-	uint16 _lineHeight = 0;  ///< First glyph height (= original line stride)
+	uint16 _lineHeight = 0;  ///< First glyph height (original line stride)
 };
 
 } // End of namespace EEM
diff --git a/engines/eem/graphics.cpp b/engines/eem/graphics.cpp
index ca91bcc0349..c11505fa1dd 100644
--- a/engines/eem/graphics.cpp
+++ b/engines/eem/graphics.cpp
@@ -35,29 +35,25 @@
 #include "eem/detection.h"
 #include "eem/eem.h"
 
-// EEM ??? graphics helpers (KDGRAPH.C + KDHELP.C, the latter merged because
-// it has only three functions). Animation playback, balloon-table lookup,
-// and the help-screen primitives that share the same balloon machinery.
-
 namespace EEM {
 
-// `_InterfaceHelp(num)` @ 1560:0205 reads `HelpData @ 29be:00c8` (5-byte
-// entries: u8 count, then up to 2 u16 picIds). Verified bytes:
+// HelpData @ 29be:00c8, read by _InterfaceHelp @ 1560:0205. 5-byte entries:
+// u8 count, then up to 2 u16 picIds. Verified bytes:
 //   entry 0 (PDA / gallery HELP button): count=2, picIds = 0x0063, 0x01ae
-//   entry 1: count=2, picIds = 0x0192, 0x01b1
-// Only entry 0 is reachable from the PDA notebook (rect 1) and the
-// gallery (rect 1) ??? both call `_InterfaceHelp(0)`.
+//   entry 1:                              count=2, picIds = 0x0192, 0x01b1
+// Only entry 0 is reachable from the PDA notebook (rect 1) and the gallery
+// (rect 1) ??? both call _InterfaceHelp(0).
 const uint16 kHelpPics[][2] = {
 	{ 0x0063, 0x01ae },
 	{ 0x0192, 0x01b1 },
 };
 
-// 52-entry, 10-bytes-each balloon-metadata table at `29be:0875` (CD)
-// / `2608:05f9` (floppy). Used by `_DisplayClue` to position
-// `_WordWrap` text inside the balloon AND to position the
-// "click to continue" indicator drawn by
-// `_DisplayHotspotClue_Floppy @ 22dc:05c8`. Layout per entry:
-//   +0..1 = text X inset, +2..3 = text Y inset
+// kBalloonInsetTable: 52 entries x 10 bytes @ 29be:0875 (CD) / 2608:05f9
+// (floppy), indexed by (bubNum & 0x7F). Used by _DisplayClue to position
+// _WordWrap text inside the balloon AND by _DisplayHotspotClue_Floppy
+// @ 22dc:05c8 to position the "click to continue" indicator. Layout per entry:
+//   +0..1 = text X inset
+//   +2..3 = text Y inset
 //   +4..5 = wrap width
 //   +6..7 = "more / end" indicator X within the balloon
 //   +8..9 = "more / end" indicator Y within the balloon
@@ -117,12 +113,8 @@ bool findBalloonFamily(uint16 balloonId, uint16 &first, uint16 &last) {
 	return false;
 }
 
-// Lines that fit inside balloon @p balloonId. The metadata-table `indDY`
-// is the designed bottom of the text area, NOT the indicator-drawing
-// position alone ??? for families 3, 4, 6 and the singletons the bubble
-// graphic continues below `indDY` with shadow / tail decoration that
-// text must not encroach on. Image height is therefore an overestimate;
-// `indDY` is the artist-intended last text line.
+// indDY is the artist-intended last text line, not just the indicator Y:
+// families 3, 4, 6 and singletons have shadow/tail decoration below indDY.
 uint getBalloonLineCapacity(uint16 balloonId, int lineH) {
 	const uint idx = balloonId & 0x7F;
 	if (idx >= ARRAYSIZE(kBalloonInsetTable) || lineH <= 0)
@@ -132,10 +124,9 @@ uint getBalloonLineCapacity(uint16 balloonId, int lineH) {
 	return MAX<uint>(1, ((int)insets.indDY - (int)insets.y) / lineH + 1);
 }
 
-// Floppy KDHelp hotspot-searched check. Mirrors
-// `FUN_22dc_096c @ 22dc:096c`: walks the per-site dialog records at
-// `site_data[+6]` to skip `hotspotIdx` hotspots, then returns the
-// `_cluesFound` flag for that hotspot's first text index.
+// FUN_22dc_096c @ 22dc:096c: walks per-site dialog records at site_data[+6]
+// to skip hotspotIdx hotspots, returns _cluesFound flag for hotspot's first
+// text index.
 static bool floppyHotspotSearched(EEM::Mystery &mystery, uint siteIdx,
 								   uint hotspotIdx) {
 	const byte *site = mystery.siteData(siteIdx);
@@ -169,21 +160,14 @@ static bool floppyHotspotSearched(EEM::Mystery &mystery, uint siteIdx,
 }
 
 void EEMEngine::doHelp() {
-	// Floppy uses a totally different hint mechanism ??? per-mystery
-	// `H<n>.BIN` data files (one per case). Format verified at
-	// `FUN_1503_0001 @ 1503:0001` (loader, format string at
-	// `2608:0154` = "h%d.bin") + `FUN_1503_01a5 @ 1503:01a5`
-	// (consumer):
-	//   byte 0 = numChainHints
-	//   numChainHints ?? { byte siteIdx; byte hotspotIdx; }
-	//   byte = numExtraHints
-	//   numExtraHints ?? { byte siteIdx; byte hotspotIdx; }
-	//   asciiz string 1  ("[balloon-digit]Let's go to <site>...")
-	//   asciiz string 2  (alternate hint)
-	//   asciiz string 3  (post-solve hint, used when score ??? 100)
-	// Selection logic: if any chain hotspot is unsearched ??? string 1.
-	// Else if any extra hotspot is unsearched ??? string 2. Else if
-	// `selectedPoints() ??? 100` ??? string 3.
+	// Floppy per-mystery H<n>.BIN hint files. Loader FUN_1503_0001 @ 1503:0001
+	// (format string "h%d.bin" @ 2608:0154), consumer FUN_1503_01a5 @ 1503:01a5.
+	// Format:
+	//   byte numChainHints; numChainHints ?? { byte siteIdx; byte hotspotIdx; }
+	//   byte numExtraHints; numExtraHints ?? { byte siteIdx; byte hotspotIdx; }
+	//   asciiz str1, str2, str3 (post-solve, score >= 100)
+	// Selection: any chain hotspot unsearched -> str1; else any extra
+	// unsearched -> str2; else selectedPoints() >= 100 -> str3.
 	if (isFloppy() && _mystery.isLoaded()) {
 		const Common::String filename = Common::String::format("H%u.BIN",
 															   _mystery.number());
@@ -252,18 +236,9 @@ void EEMEngine::doHelp() {
 		if (!chosen || *chosen == 0)
 			return;
 
-		// Strip leading balloon-digit byte. `_GetKDTextBalloon @
-		// 1df2:0105` (= floppy `FUN_1d40_009f`) doesn't take the
-		// digit's *value* ??? it indexes the per-character table at
-		// `2608:0c14` by the literal byte, so '0'..'9' map to a
-		// non-trivial balloon-id sequence. Verified bytes at
-		// `2608:0c44` (= 0xc14 + '0'):
-		//   '0'???0x15, '1'???0x16, '2'???0x17, '3'???0x18, '4'???0x19,
-		//   '5'???0x1a, '6'???0x1c, '7'???0x1d, '8'???0x1e, '9'???0x0a.
-		// Without this map the previous (digit - '0') version asked
-		// `getBalloonInsets` for balloon 0 (text width 142) instead of
-		// the correct balloon 21 (text width 155), which is why the
-		// hint bubble rendered narrower than the original.
+		// _GetKDTextBalloon @ 1df2:0105 (floppy FUN_1d40_009f) indexes the
+		// per-character table at 2608:0c14 by the literal byte. Bytes at
+		// 2608:0c44 (= 0xc14 + '0') give the '0'..'9' -> balloon-id mapping:
 		static const uint8 kFloppyDigitToBalloon[10] = {
 			0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1c, 0x1d, 0x1e, 0x0a
 		};
@@ -317,7 +292,6 @@ void EEMEngine::doHelp() {
 		g_system->copyRectToScreen(ms.getPixels(), ms.pitch, 0, 0, 320, 200);
 		g_system->updateScreen();
 
-		// Wait for click ??? KD hint dismisses on any input.
 		while (!shouldQuit()) {
 			Common::Event ev;
 			bool advance = false;
@@ -338,9 +312,9 @@ void EEMEngine::doHelp() {
 		return;
 	}
 
-	// Mirrors `_KDHelp @ 1560:010a`. The original walks the first two
-	// entries of `_AChain` (the puzzle's required-clue chain ??? the
-	// "spine" of evidence the player must collect):
+	// Mirrors _KDHelp @ 1560:010a. Walks the first two _AChain entries
+	// (the puzzle's required-clue chain ??? the "spine" of evidence the
+	// player must collect):
 	//
 	//   for (i = 0; i < 2; i++) {
 	//       if (_AChain[i] != -1 && _HintBlock[i] != -1 &&
@@ -351,17 +325,16 @@ void EEMEngine::doHelp() {
 	//       if (_HintBlock[i] != -1) defined++;
 	//   }
 	//   if (!shown) {
-	//       // Fall back to the generic KD hint: KDTextIndex[+0xe]
-	//       // (first time) / KDTextIndex[+0x10] (second time, toggled
-	//       // by _SawHelpHint). If neither chain entry had a hint
-	//       // defined, show the global "no hints" sentinel instead.
+	//       // Generic KD hint: KDTextIndex[+0xe] (first time) /
+	//       // KDTextIndex[+0x10] (second time, toggled by _SawHelpHint).
+	//       // If no chain hint was ever defined, render the "no hints"
+	//       // sentinel instead.
 	//       _DisplayHint(...);
 	//   }
 	//
-	// So this is a SMART per-puzzle hint: the partner points the
-	// player at whichever chain clue they haven't yet found, only
-	// falling back to the generic "let's keep looking" line when
-	// every chain hint has been triggered already.
+	// SMART per-puzzle hint: partner points at whichever chain clue the
+	// player hasn't found yet, only falling back to the generic line once
+	// every chain hint has been triggered.
 	if (!_mystery.isLoaded() || !_font.isLoaded())
 		return;
 
@@ -393,10 +366,7 @@ void EEMEngine::doHelp() {
 	}
 
 	if (chosenText == 0xFFFF) {
-		// No unfound chain clue had a hint to give ??? fall back to the
-		// generic KD hint (or the "no hints defined" sentinel if the
-		// chain has no hints at all). Mirrors the second arm of
-		// `_KDHelp` (1560:0152-019b).
+		// Second arm of _KDHelp (1560:0152-019b): generic KD hint fallback.
 		if (anyHintDefined) {
 			const uint16 hintFirst  = READ_LE_UINT16(kd + 0x0e);
 			const uint16 hintSecond = READ_LE_UINT16(kd + 0x10);
@@ -409,9 +379,7 @@ void EEMEngine::doHelp() {
 				soundNum   = 8;
 			}
 		}
-		// Else: keep chosenText == 0xFFFF ??? original would render
-		// `NoHints` (a "There are no hints defined for this Mystery"
-		// string at 29be:00d3); we just bail.
+		// Else: original would render NoHints string @ 29be:00d3; we bail.
 	}
 
 	if (chosenText == 0xFFFF) {
@@ -422,20 +390,19 @@ void EEMEngine::doHelp() {
 	const Common::String raw  = _mystery.textAt(chosenText);
 	Common::String text = parseString(raw, _playerName, _partner);
 
-	// Render as a speech-balloon overlay, exactly mirroring
-	// `_DisplayHint @ 1560:0009`:
+	// Render as a speech-balloon overlay, mirroring _DisplayHint @ 1560:0009:
 	//
 	//   _GetKDTextBalloon(text, &bub);             // first-char dispatch
-	//   _GetBalloon(bub);                           // load balloon pic
-	//   y = (h < 0x4e) ? (0x50 - h) >> 1 : 1;       // vertical centre
-	//   _AddPicBackground(balloon, 0x21, y);        // overlay on screen
+	//   _GetBalloon(bub);                          // load balloon pic
+	//   y = (h < 0x4e) ? (0x50 - h) >> 1 : 1;      // vertical centre
+	//   _AddPicBackground(balloon, 0x21, y);       // overlay on screen
 	//   _WordWrap(0x21+tbl[bub].x, y+tbl[bub].y,   // text inside balloon
 	//             tbl[bub].w, text, -1, color=0);
-	//   _SayKDDigital(snd);                          // partner voice
+	//   _SayKDDigital(snd);                        // partner voice
 	//   _Wait();
 	//
-	// The balloon BG is the caller's CURRENT screen ??? site / PDA /
-	// gallery ??? not a cleared scratch.
+	// BG is the caller's CURRENT screen (site / PDA / gallery), not a cleared
+	// scratch.
 	Graphics::ManagedSurface ms(320, 200,
 		Graphics::PixelFormat::createFormatCLUT8());
 	ms.clear();
@@ -447,16 +414,13 @@ void EEMEngine::doHelp() {
 		}
 	}
 
-	// Balloon shape dispatch via `_GetKDTextBalloon @ 1df2:0105` ???
-	// based on the first char of the parsed text. Digits select a
-	// specific balloon variant; non-digit defaults to `0x17`. The
-	// digit, when present, is THEN consumed from the displayed
-	// text ??? mirrors `_DisplayAlibi @ 1df2:0145`'s `str = pbVar7 + 1`
-	// advance after using `*str` for `bindx`. Without this the hint
-	// renders like "1Try checking the kitchen..." with a stray
-	// leading digit. `_GetKDTextBalloon` itself doesn't strip it
-	// (verified at 1df2:0105 ??? it just reads `*str`), so the caller
-	// has to.
+	// Balloon shape dispatch via _GetKDTextBalloon @ 1df2:0105 ??? based on
+	// the first char of the parsed text. Digits select a specific balloon
+	// variant; non-digit defaults to 0x17. The digit, when present, is
+	// THEN consumed from the displayed text ??? mirrors _DisplayAlibi
+	// @ 1df2:0145's `str = pbVar7 + 1` advance after reading `*str` for
+	// bindx. _GetKDTextBalloon itself doesn't strip it (1df2:0105 just
+	// reads `*str`), so the caller has to.
 	const byte firstChar =
 		text.empty() ? (byte)0 : (byte)text[0];
 	uint16 bubNum = getKDTextBalloon(firstChar);
@@ -480,8 +444,6 @@ void EEMEngine::doHelp() {
 						 (uint32)transp);
 	}
 
-	// Balloon-relative text insets from the table at `29be:0875`
-	// (10 bytes per entry: x, y, max-width, ...).
 	uint16 tx = 5, ty = 4, tw = 155;
 	getBalloonInsets(bubNum, tx, ty, tw);
 	_font.drawWordWrapped(&ms, balloonX + tx, balloonY + ty, tw, text,
@@ -491,9 +453,10 @@ void EEMEngine::doHelp() {
 							   0, 0, 320, 200);
 	g_system->updateScreen();
 
-	// `_DisplayHint @ 1560:0009` plays `_SayKDDigital(soundnum)` ???
-	// partner-specific voice line keyed to which hint type fired (10
-	// = first chain hint, 11 = second, 7 / 8 = generic KD).
+	// _DisplayHint @ 1560:0009 plays _SayKDDigital(soundnum) ??? a
+	// partner-specific voice line keyed to which hint type fired:
+	//   10 = first chain hint, 11 = second chain hint,
+	//    7 = generic KD (first), 8 = generic KD (second).
 	if (_audio && _mystery.kdTextIndex() && soundNum > 0)
 		_audio->sayKDDigital(_mystery.kdTextIndex(), (uint)soundNum,
 							 _partner);
@@ -502,29 +465,22 @@ void EEMEngine::doHelp() {
 }
 
 void EEMEngine::doInterfaceHelp(uint num) {
-	// Mirrors `_InterfaceHelp(num)` @ 1560:0205. The original walks
-	// `HelpData @ 29be:00c8` (5-byte entries: u8 count, then up to 2
-	// u16 picIds), `_GetPicture`s each one, blits it via
-	// `_Rect_Move_Mask(0, 0, ...)` (a MASKED blit on top of the
-	// existing screen ??? transparent pixels show the caller's BG), and
-	// waits for click / key. ESC at `1560:02b3` skips to end. The
-	// function also hides the cursor at the top (`MOV [0x3a00], 0` at
-	// 1560:0216 + `_RemoveMouse @ 1000:542f` at 1560:021c) and
-	// restores it at the tail (`_DrawMouse @ 1000:5429` at 1560:02e8).
-	//
-	// `kHelpPics` lives at file scope above; see comment there for the
-	// HelpData decoding.
+	// Mirrors _InterfaceHelp(num) @ 1560:0205. The original walks
+	// HelpData @ 29be:00c8 (5-byte entries: u8 count, then up to 2 u16
+	// picIds), _GetPictures each one, blits via _Rect_Move_Mask(0, 0, ...)
+	// (a MASKED blit on top of the existing screen ??? transparent pixels
+	// show the caller's BG), and waits for click / key. ESC at 1560:02b3
+	// skips to the end. The function hides the cursor at the top
+	// (MOV [0x3a00], 0 @ 1560:0216 + _RemoveMouse @ 1000:542f at
+	// 1560:021c) and restores it at the tail (_DrawMouse @ 1000:5429
+	// at 1560:02e8). See kHelpPics comment for HelpData decoding.
 	if (num >= ARRAYSIZE(kHelpPics))
 		return;
 
 	debugC(1, kDebugScript, "doInterfaceHelp(%u): showing pics 0x%x, 0x%x",
 		   num, kHelpPics[num][0], kHelpPics[num][1]);
 
-	// Snapshot the caller's screen ONCE so each help PIC overlays the
-	// same clean BG. Without this, after the first PIC is dismissed the
-	// second snapshot would include the first PIC's pixels and the two
-	// would composite together ??? same gotcha as the setup-screen help
-	// loop fix in `doSetup`.
+	// Snapshot caller's screen once: each PIC overlays the same clean BG.
 	Graphics::ManagedSurface bg(320, 200,
 		Graphics::PixelFormat::createFormatCLUT8());
 	{
@@ -548,17 +504,8 @@ void EEMEngine::doInterfaceHelp(uint num) {
 		debugC(1, kDebugScript, "doInterfaceHelp: pic 0x%x = %dx%d flags=0x%x",
 			   picId, pic.surface.w, pic.surface.h, pic.flags);
 
-		// Compose a 320x200 frame from the clean BG snapshot and overlay
-		// the help pic with `transBlitFrom` ??? `Graphics::ManagedSurface`'s
-		// masked blit (transparent colour = the pic's `flags >> 8`,
-		// matching `_Rect_Move_Mask`'s param_10 at 1000:03fc). Pass an
-		// explicit `destPos` of (0, 0) ??? the no-destPos overload at
-		// managed_surface.cpp:738 scales src to fill `this`'s rect,
-		// stretching the help PIC to 320x200 instead of placing it at
-		// native size. The original `_Rect_Move_Mask` passes destX=0,
-		// destY=0 with copy-width = pic[+4] (= `pic.surface.w`) and
-		// copy-height = pic[+2] (= `pic.surface.h`) ??? i.e. native size,
-		// not stretched.
+		// transBlitFrom transp = pic.flags >> 8 matches _Rect_Move_Mask param_10
+		// @ 1000:03fc. Explicit (0,0) destPos: no-arg overload stretches to fill.
 		Graphics::ManagedSurface scratch(320, 200,
 			Graphics::PixelFormat::createFormatCLUT8());
 		scratch.simpleBlitFrom(bg);
@@ -617,10 +564,7 @@ void EEMEngine::setPartnerEraseBg(const Graphics::ManagedSurface *bg) {
 
 uint16 EEMEngine::fitBalloonToText(uint16 bubNum,
 								   const Common::String &text) {
-	// Opt-in via the "Better fit for dialog balloons" game option, and
-	// CD-only ??? the floppy build's balloon archive / inset table hasn't
-	// been validated for shrinking yet, so leave it on the original
-	// artist-chosen bubble.
+	// Opt-in via "fit_dialog_balloons", CD only (floppy table unvalidated).
 	if (isFloppy() || !ConfMan.getBool("fit_dialog_balloons"))
 		return bubNum;
 
@@ -683,7 +627,6 @@ uint16 EEMEngine::fitBalloonToText(uint16 bubNum,
 
 bool EEMEngine::getBalloonInsets(uint16 bubNum, uint16 &xInset,
 								  uint16 &yInset, uint16 &textW) const {
-	// `kBalloonInsetTable` lives at file scope above; see comment there.
 	const uint idx = bubNum & 0x7F;
 	if (idx >= ARRAYSIZE(kBalloonInsetTable))
 		return false;
@@ -706,12 +649,12 @@ bool EEMEngine::getBalloonIndicatorPos(uint16 bubNum, uint16 &dx,
 void EEMEngine::drawFloppyBubbleIndicator(Graphics::ManagedSurface &dst,
 										   uint16 bubNum, int ballX, int ballY,
 										   bool endIndicator) {
-	// Mirrors `_DisplayHotspotClue_Floppy @ 22dc:08c0` (end-of-record)
-	// and `@ 22dc:08aa` (mid-pagination). Both grab a pre-loaded PIC
-	// (`DAT_28da_3034 = PIC 0xa0` for "more pages",
-	//  `DAT_28da_3030 = PIC 0xa1` for "end indicator") and stamp it at
-	// `(ballX + insetTable[bubNum].indDX,
-	//   ballY + insetTable[bubNum].indDY)` via `_AddPicBackground`.
+	// Mirrors _DisplayHotspotClue_Floppy @ 22dc:08c0 (end-of-record) and
+	// @ 22dc:08aa (mid-pagination). Both grab a pre-loaded PIC:
+	//   DAT_28da_3034 = PIC 0xa0  "more pages" indicator
+	//   DAT_28da_3030 = PIC 0xa1  "end" indicator
+	// and stamp it at (ballX + insetTable[bubNum].indDX,
+	//                  ballY + insetTable[bubNum].indDY) via _AddPicBackground.
 	uint16 dx = 0;
 	uint16 dy = 0;
 	if (!getBalloonIndicatorPos(bubNum, dx, dy))
diff --git a/engines/eem/music.cpp b/engines/eem/music.cpp
index 1bd50f83cce..c1fd4bf26b0 100644
--- a/engines/eem/music.cpp
+++ b/engines/eem/music.cpp
@@ -39,11 +39,9 @@ const int kMidiDriverFlags = MDT_MIDI | MDT_ADLIB | MDT_PREFER_MT32;
 } // End of anonymous namespace
 
 MusicPlayer::MusicPlayer(bool isFloppy) : _isFloppy(isFloppy) {
-	// Mirrors `_InitMIDI @ 20a2:013a` which used `_AIL_register_driver`
-	// to walk the .ADV files (ADLIB.ADV, SBFM.ADV, MT32MPU.ADV, etc.)
-	// and pick a backend. We honour the launcher's "Music driver"
-	// setting while preferring MT-32 when no concrete device was chosen,
-	// like other ScummVM engines with native MT-32 scores.
+	// _InitMIDI @ 20a2:013a ??? `_AIL_register_driver` against
+	// ADLIB.ADV / SBFM.ADV / MT32MPU.ADV. We honour the launcher's
+	// "Music driver" setting and prefer MT-32 when unset.
 	const MidiDriver::DeviceHandle dev =
 		MidiDriver::detectDevice(kMidiDriverFlags);
 	MusicType musicType = MidiDriver::getMusicType(dev);
@@ -52,22 +50,21 @@ MusicPlayer::MusicPlayer(bool isFloppy) : _isFloppy(isFloppy) {
 
 	switch (musicType) {
 	case MT_ADLIB:
-		// `_MIDIPlayFile` (20a2:024c) opens "SAMPLE.AD" (29be:14d6) and
-		// installs every patch the sequence requests via
+		// _MIDIPlayFile @ 20a2:024c opens SAMPLE.AD (string at 29be:14d6)
+		// and installs every patch the sequence requests via
 		// `_AIL_install_timbre`. ScummVM's Miles AdLib driver does the
-		// same: it reads SAMPLE.AD on construction and serves timbres
-		// out of that bank, which is what makes the notes match the
-		// 1993 release. SAMPLE.OPL would be the OPL3 variant; the game
-		// only ships SAMPLE.AD, so an empty path falls back to OPL2.
+		// same on-demand install from SAMPLE.AD, which is what makes
+		// the notes match the 1993 release; the generic AdLib fallback
+		// would use ScummVM's built-in timbres instead. SAMPLE.OPL would
+		// be the OPL3 variant ??? the game only ships SAMPLE.AD, so the
+		// empty second path falls back to OPL2.
 		_milesAudioMode = true;
 		_driver = Audio::MidiDriver_Miles_AdLib_create(
 			Common::Path("SAMPLE.AD"), Common::Path());
 		break;
 	case MT_MT32:
-		// `MT32MPU.ADV` was the original MT-32 driver; ScummVM has no
-		// Miles MT-32 instrument bank for EEM, so we use the standard
-		// MT-32 driver and let the XMIDI's own program changes drive
-		// the patch selection.
+		// MT32MPU.ADV in the original. No Miles MT-32 bank ships with
+		// EEM, so use the standard MT-32 driver.
 		_milesAudioMode = true;
 		_driver = Audio::MidiDriver_Miles_MT32_create(Common::Path());
 		break;
@@ -84,10 +81,7 @@ MusicPlayer::MusicPlayer(bool isFloppy) : _isFloppy(isFloppy) {
 			delete _driver;
 			_driver = nullptr;
 		} else {
-			// No GM/MT-32 reset for AdLib (Miles AdLib handles its own
-			// state); for MT-32/GM the original would've sent its own
-			// initialisation patches via `_AIL_install_timbre`, but we
-			// don't have those banks for non-AdLib devices.
+			// Miles AdLib handles its own reset.
 			if (musicType != MT_ADLIB) {
 				if (musicType == MT_MT32 || _nativeMT32)
 					_driver->sendMT32Reset();
@@ -103,12 +97,12 @@ MusicPlayer::MusicPlayer(bool isFloppy) : _isFloppy(isFloppy) {
 
 void MusicPlayer::send(uint32 b) {
 	// Miles drivers (both AdLib and MT-32) implement their own per-
-	// source-channel mixing and timbre installation, so just forward
-	// the raw event. Going through `MidiPlayer::send` would re-wrap
-	// CC 7 against `_masterVolume` AND remap the source channel via
-	// `sendToChannel`/`allocateChannel`, both of which the Miles driver
-	// already handles internally (and break the timbre selection if
-	// double-applied).
+	// source-channel mixing and timbre installation, so forward the raw
+	// event. Going through `MidiPlayer::send` would re-wrap CC 7 against
+	// `_masterVolume` AND remap the source channel via
+	// `sendToChannel` / `allocateChannel`, both of which the Miles
+	// driver already handles internally (double-applying breaks the
+	// timbre selection).
 	if (_milesAudioMode) {
 		_driver->send(b);
 		return;
@@ -123,7 +117,7 @@ void MusicPlayer::playFile(const Common::Path &xmiPath, bool loop) {
 	Common::StackLock lock(_mutex);
 	stop();
 
-	// Mirrors `_MIDIPlayFile`'s `_fopen` + `_fread` (20a2:024c-029e).
+	// _MIDIPlayFile @ 20a2:024c-029e (_fopen + _fread).
 	Common::File f;
 	if (!f.open(xmiPath)) {
 		warning("MusicPlayer: %s missing", xmiPath.toString().c_str());
@@ -157,17 +151,15 @@ void MusicPlayer::playFile(const Common::Path &xmiPath, bool loop) {
 		return;
 	}
 
-	// Mirrors `_LoopMIDI = 0xFFFF` (the count register the original
-	// engine uses for indefinite looping in `_DoOpeningAnims`).
+	// _LoopMIDI = 0xFFFF in _DoOpeningAnims.
 	_isLooping = loop;
 	_parser->property(MidiParser::mpAutoLoop, loop ? 1 : 0);
 	_parser->setTrack(0);
 
 	// Pull the launcher's music_volume slider into `_masterVolume` so
 	// the non-Miles `Audio::MidiPlayer::send` path scales correctly.
-	// (Miles drivers do their own volume handling on the Multisource
-	// path, but they also honour `MidiDriver::syncSoundSettings` which
-	// `Engine::syncSoundSettings` triggers.)
+	// (Miles drivers handle volume themselves but also honour
+	// `MidiDriver::syncSoundSettings` via `Engine::syncSoundSettings`.)
 	syncVolume();
 	_isPlaying = true;
 	debugC(1, kDebugSound, "MusicPlayer: playing %s (%u bytes, loop=%d, miles=%d)",
@@ -175,15 +167,14 @@ void MusicPlayer::playFile(const Common::Path &xmiPath, bool loop) {
 }
 
 void MusicPlayer::playMus(uint num, bool loop) {
-	// CD format string verified at `29be:1525` ("mus%05d.xmi").
-	// Floppy maps the same numeric slots to its own filenames:
-	//   0..4 ??? travel music. The floppy table at 2608:1399-13cd holds
-	//          5 entries (Travel-6, Travel-4, Travel-7, Travel-1,
-	//          Travel-8) used by `_StartTravelMusic` via
-	//          `siteNumber % 5`.
+	// CD format string "mus%05d.xmi" at 29be:1525. Floppy maps the same
+	// numeric slots to different filenames:
+	//   0..4 ??? travel music. Table at 2608:1399-13cd holds 5 entries
+	//          (Travel-6, Travel-4, Travel-7, Travel-1, Travel-8) used by
+	//          `_StartTravelMusic` via `siteNumber % 5`.
 	//   5    ??? FANFARE2.XMI (winner). String at 2608:0c64.
-	//   6    ??? no equivalent in floppy install (the loser sting in
-	//          `_DisplayAlibi` is CD-only); skip.
+	//   6    ??? no equivalent on floppy (loser sting in `_DisplayAlibi`
+	//          is CD-only); skip.
 	if (_isFloppy) {
 		static const char *const kTravelTracks[5] = {
 			"Travel-6.XMI", "Travel-4.XMI", "Travel-7.XMI",
diff --git a/engines/eem/music.h b/engines/eem/music.h
index 7e8c1fc817d..59dac57e30d 100644
--- a/engines/eem/music.h
+++ b/engines/eem/music.h
@@ -31,59 +31,37 @@
 namespace EEM {
 
 /**
- * MIDI music player. Mirrors the original `MIDI.C` source file in
- * `EEMCD.EXE` (the `_MIDIPlayFile / _MIDIPlay / _StopMIDI /
- * _IsMIDIPlaying / _StartTravelMusic` family at `20a2:00e2-05c9`).
+ * MIDI music player. Mirrors MIDI.C in EEMCD.EXE
+ * (_MIDIPlayFile / _MIDIPlay / _StopMIDI / _IsMIDIPlaying /
+ * _StartTravelMusic family at 20a2:00e2-05c9).
  *
- * The original engine uses Miles Audio Interface Library (AIL):
+ * Original uses Miles AIL: _InitMIDI @ 20a2:013a registers
+ * ADLIB.ADV / SBFM.ADV / MT32MPU.ADV; _MIDIPlayFile @ 20a2:024c
+ * installs AdLib timbres from SAMPLE.AD (29be:14d6) via
+ * _AIL_install_timbre before _AIL_start_sequence. We use
+ * Audio::MidiDriver_Miles_AdLib_create (loads SAMPLE.AD) for AdLib
+ * and the Miles MT-32 driver for MT-32.
  *
- *   - `_InitMIDI @ 20a2:013a` calls `_AIL_register_driver` against
- *     `ADLIB.ADV` / `SBFM.ADV` / `MT32MPU.ADV` and reserves a timbre
- *     cache via `_AIL_define_timbre_cache`.
- *   - `_MIDIPlayFile @ 20a2:024c` opens the .XMI, calls
- *     `_AIL_register_sequence`, then loops over the sequence's
- *     `_AIL_timbre_request` results. For every (bank, patch) pair the
- *     driver asks for, it pulls the AdLib instrument definition from
- *     **`SAMPLE.AD`** (string at `29be:14d6`) via `_load_global_timbre`
- *     and installs it through `_AIL_install_timbre`. Only after every
- *     patch is loaded does it call `_AIL_start_sequence`.
- *
- * Without those custom timbres, ScummVM's generic AdLib synth falls
- * back to its built-in default timbre table ??? same notes, very
- * different timbres. ScummVM ships a Miles AdLib driver
- * (`Audio::MidiDriver_Miles_AdLib_create`) that loads `SAMPLE.AD` and
- * implements the same install-on-demand workflow, so we use it for
- * AdLib output. MT-32 uses ScummVM's Miles MT-32 driver path, and any
- * other MIDI output falls back to `Audio::MidiPlayer::createDriver`.
- *
- * Available music files in the game directory:
- *   - THEME.XMI   ??? opening anims (looping) + title screen
- *   - MUS00000.XMI..MUS00004.XMI ??? per-site travel music
- *     (`_StartTravelMusic` picks one via `_SiteNumber % 5`)
- *   - MUS00005.XMI ??? winner ending (`_DisplayCorrect` @ 1df2:0789)
- *   - MUS00006.XMI ??? loser ending (`_DisplayAlibi` @ 1df2:018a)
+ * Music files:
+ *   THEME.XMI ??? opening anims + title.
+ *   MUS00000..MUS00004 ??? travel music (siteNumber % 5).
+ *   MUS00005 ??? winner (_DisplayCorrect @ 1df2:0789).
+ *   MUS00006 ??? loser  (_DisplayAlibi  @ 1df2:018a).
  */
 class MusicPlayer : public Audio::MidiPlayer {
 public:
 	explicit MusicPlayer(bool isFloppy = false);
 
-	/// Mirrors `_MIDIPlayFile @ 20a2:024c`. Reads the .XMI from the game
-	/// directory and starts playing. `loop=true` mirrors the
-	/// `_LoopMIDI = 0xFFFF` writes inside `_DoOpeningAnims` (theme music).
+	/// _MIDIPlayFile @ 20a2:024c. loop=true mirrors
+	/// _LoopMIDI = 0xFFFF in _DoOpeningAnims.
 	void playFile(const Common::Path &xmiPath, bool loop = false);
 
-	/// Mirrors `_MIDIPlay(num) @ 20a2:047d`. Composes the filename
-	/// "MUS%05u.XMI" (CD) or maps to TRAVEL-N.XMI / FANFARE2.XMI
-	/// (floppy) and plays it. Used by `_StartTravelMusic`,
-	/// `_DisplayCorrect` (winner), `_DisplayAlibi` (loser).
+	/// _MIDIPlay(num) @ 20a2:047d. CD: "MUS%05u.XMI";
+	/// floppy: TRAVEL-N.XMI / FANFARE2.XMI.
 	void playMus(uint num, bool loop = false);
 
-	// In Miles AdLib mode the driver allocates its own AdLib voice
-	// pool and consumes the XMIDI's source-channel byte directly, so we
-	// must NOT route through `Audio::MidiPlayer::sendToChannel` (which
-	// remaps every source channel through `allocateChannel()` and
-	// breaks the timbre selection / volume scaling the Miles driver
-	// performs internally). Same workaround Toltecs / SAGA use.
+	// Miles drivers handle source-channel routing themselves; bypass
+	// Audio::MidiPlayer::sendToChannel. Same workaround as Toltecs / SAGA.
 	void send(uint32 b) override;
 
 private:
diff --git a/engines/eem/mystery.cpp b/engines/eem/mystery.cpp
index 5982e5c5fc5..06892c0735a 100644
--- a/engines/eem/mystery.cpp
+++ b/engines/eem/mystery.cpp
@@ -92,63 +92,23 @@ bool Mystery::load(uint num, Common::RandomSource *rng) {
 	_data = staging;
 	_number = num;
 
-	// Floppy `M*.BIN` uses a completely different header layout from
-	// the CD release. Verified via Ghidra of `EEM.EXE` floppy:
-	//
-	//   `_ReadMystery_Floppy @ 22dc:0178` parses M0..M54 into pointers
-	//    held in a global table at 28da:3c87+. The header offsets are:
-	//
-	//     header[+0..+1]   ???
-	//     header[+2..+3]   ???
-	//     header[+4..+5]   pointer ??? SUSPECTS section
-	//                      (count byte, then 0xb-byte entries; entry[+4] =
-	//                       pic ID, entry[+10] = recolor flag)
-	//                      (`_FloppySuspectsPtr` @ 28da:3c8b)
-	//     header[+6..+7]   pointer ??? ???       (28da:3c9f)
-	//     header[+8..+9]   pointer ??? NOTES section
-	//                      (7-byte entries indexed by clue ID; used by
-	//                       floppy `_DrawNotes` @ 15e0:01e8)
-	//                      (`DAT_28da_3c9b`)
-	//     header[+0xa..+b] pointer ??? GALLERY-PORTRAITS section
-	//                      (count byte, then variable-length entries
-	//                       `5 + name_len` bytes; entry[+0..+1] = u16
-	//                       picID, entry[+4] = name length)
-	//                      (`_FloppyGalleryPtr` @ 28da:3c87, count =
-	//                       `_FloppyNumSuspects` @ 28da:004b)
-	//     header[+0xc..+d] pointer ??? TEXT block (alibi text base; used in
-	//                       floppy `_DisplayAlibi` @ 1d40:00df)
-	//     header[+0x10..1] pointer ??? KDTextIndex (`_FloppyKDTextIndexPtr`
-	//                       @ 28da:3c93)
-	//     header[+0x12..3] pointer ??? ???       (28da:3c8f)
-	//
-	//   There is NO fixed-offset numSites / numSuspects / numCONSITEs
-	//   field ??? counts are stored as the FIRST byte of each section.
-	//   The CD release refactored this into a flat header at fixed
-	//   offsets; our `Mystery::load` here parses the CD layout.
-	//
-	// Detect the variant from the first u16: CD M0 starts with `0x003e`
-	// (initOffset = 62), floppy M0 starts with `0x2286` (a section
-	// pointer near end-of-file). When the first u16 is too high to be
-	// a CD `_initOffset`, parse as floppy.
+	// Floppy M*.BIN uses a different header layout from CD.
+	// _ReadMystery_Floppy @ 22dc:0178. Section-pointer header:
+	//   header[+0]    InitBlock byte offset (caseType byte at *(buf+initOff))
+	//   header[+4]    SITES: count byte + 11-byte entries
+	//                 (entry[+4]=picID, [+6..7]=u16 X, [+8..9]=u16 Y, [+10]=recolor)
+	//   header[+6]    SITE INDEX (array of u16 offsets to per-site structs)
+	//   header[+8]    NOTES (7-byte entries / clue ID; _DrawNotes_Floppy @ 15e0:01e8)
+	//   header[+0xa]  GALLERY portraits: count byte + variable `5+nameLen` entries
+	//                 (entry[+0..1]=u16 picID, [+4]=nameLen)
+	//   header[+0xc]  TEXT block (alibi base; _DisplayAlibi_Floppy @ 1d40:00df)
+	//   header[+0x10] KDTextIndex
+	//   header[+0x12] SOLVED CLUE CHAIN
+	// Counts live in the FIRST BYTE of each section, not the header.
+	// Detect via first u16: CD M0 = 0x003e (initOffset), floppy M0 = 0x2286
+	// (section pointer near EOF).
 	if (readU16(0) > 0x100) {
 		_isFloppy = true;
-		// Section-pointer header verified via Ghidra of floppy
-		// `_ReadMystery_Floppy @ 22dc:0178`,
-		// `_DoSiteLoop_Floppy @ 1652:03a3`,
-		// and `FUN_1fed_07ed` (BigMap site iteration):
-		//
-		//   header[+4]   ??? SITES section
-		//                  count byte + 0xb-byte entries; entry[+4] =
-		//                  pic ID for BigMap marker, [+6..7] = u16 X,
-		//                  [+8..9] = u16 Y, [+10] = recolor flag.
-		//   header[+6]   ??? SITE INDEX (array of u16 offsets to per-site
-		//                  data structs; site[+0]=picOff,
-		//                  site[+2]=clueBlockOff, site[+8]=speakerInfo)
-		//   header[+8]   ??? NOTES (7-byte entries / clue ID)
-		//   header[+0xa] ??? SUSPECTS / GALLERY portraits
-		//   header[+0xc] ??? TEXT block base
-		//   header[+0x10] ??? KDTextIndex
-		//   header[+0x12] ??? SOLVED CLUE CHAIN
 		_floppySuspectsOff  = readU16(0x04);  // SITES
 		_floppyHintBlockOff = readU16(0x06);  // SITE INDEX
 		_floppyNoteIndexOff = readU16(0x08);
@@ -156,16 +116,8 @@ bool Mystery::load(uint num, Common::RandomSource *rng) {
 		_floppyTextOff      = readU16(0x0c);
 		_floppyKDTextOff    = readU16(0x10);
 		_floppySolvedOff    = readU16(0x12);
-
-		// header[+0] (the first u16) holds the InitBlock byte offset on
-		// floppy too ??? verified at `FUN_19bb_042f` where `*DAT_28da_3ca5`
-		// (deref'd as int *) reads the first u16 of the buffer and uses
-		// it as `cVar1 = *(buffer + initOffset)` (caseType byte).
 		_initOffset = readU16(0x00);
 
-		// Counts: first byte of each section. Verified at
-		// `FUN_1fed_07ed` (`uVar3 = *_FloppySuspectsPtr` then iterates)
-		// and `FUN_154e_0045` (`DAT_28da_004b = *DAT_28da_3c87`).
 		const byte *sitesSec = (_floppySuspectsOff < _data.size())
 								? _data.data() + _floppySuspectsOff : nullptr;
 		const byte *susSec   = (_floppyGalleryOff < _data.size())
@@ -175,16 +127,9 @@ bool Mystery::load(uint num, Common::RandomSource *rng) {
 		_numCONSITEs = 0;
 		_numCOFFSITEs = 0;
 
-		// Point CD-shaped accessor offsets at the floppy equivalents
-		// so existing accessors return the right base for floppy:
-		//   siteIndexEntry() ??? floppy site index (header[+6])
-		//   noteIndex()       ??? floppy notes (header[+8])
-		//   galleryData()     ??? floppy suspects (header[+0xa])
-		//   textAt()          ??? floppy text block (header[+0xc])
-		//   kdTextIndex()     ??? floppy KDTextIndex (header[+0x10])
-		//   solvedClueBlock() ??? floppy solved chain (header[+0x12])
-		// Per-section LAYOUTS still differ from CD, so consumers
-		// walking entries need `isFloppy()` branches.
+		// Point CD-shaped accessors at the floppy equivalents.
+		// Per-section LAYOUTS still differ, so consumers walking entries
+		// need `isFloppy()` branches.
 		_siteIndexOffset = _floppyHintBlockOff;
 		_noteOffset      = _floppyNoteIndexOff;
 		_galleryOffset   = _floppyGalleryOff;
@@ -193,16 +138,9 @@ bool Mystery::load(uint num, Common::RandomSource *rng) {
 		_solvedOffset    = _floppySolvedOff;
 		_hintOffset      = _floppyHintBlockOff;
 
-		// Per-mystery runtime state ??? mirror `_ReadMystery_Floppy @
-		// 22dc:0178` zeroing `_TextSeen_Floppy` / `_InGallery_Floppy` and
-		// seeding `_NewOrder_Floppy[0]` (random) + the `+1`-shift loop
-		// that fills the rest. We use the identity mapping `[0..N-1]` so
-		// dialog `byte9` (1-based logical idx) maps to `_inGallery[idx -
-		// 1]` and the gallery render iterates the same indices. Without
-		// this init the floppy load path returns with `_newOrder` left
-		// at whatever `clear()` zeroed it to, so every byte9>0 dialog
-		// resolves through `_newOrder[logicalIdx]==0` and the second
-		// suspect overwrites the first slot.
+		// Per-mystery runtime state ??? mirrors _ReadMystery_Floppy @ 22dc:0178.
+		// _newOrder uses identity mapping [0..N-1] so dialog byte9 (1-based
+		// logical idx) maps to _inGallery[idx - 1].
 		memset(_cluesFound, 0, sizeof(_cluesFound));
 		memset(_noteSelected, 0, sizeof(_noteSelected));
 		memset(_hotSpotsSeen, 0, sizeof(_hotSpotsSeen));
@@ -245,13 +183,10 @@ bool Mystery::load(uint num, Common::RandomSource *rng) {
 	_numCONSITEs = (uint8)readU16(14 * 2);
 	_numCOFFSITEs = (uint8)readU16(15 * 2);
 
-	// Defensive clamp. The floppy mystery file format uses a different
-	// header layout (verified by comparing M0.BIN: CD has `numSites =
-	// readU16(0x14) = 3`; floppy has `readU16(0x14) = 0x1925`,
-	// obviously not a site count). Without a clamp, downstream loops
-	// over `_onSites` / `_visitedSite` (capacity 20) blow past the
-	// array end. Until the floppy format is fully supported, cap at
-	// the array capacity so the engine fails gracefully.
+	// Defensive clamp: the floppy header layout differs (M0.BIN CD has
+	// numSites=readU16(0x14)=3; floppy has readU16(0x14)=0x1925, clearly
+	// not a count). Cap to `_onSites` / `_visitedSite` array capacity so
+	// downstream loops don't walk off the end on malformed/floppy files.
 	if (_numSites > kVisitedSiteCap)
 		_numSites = kVisitedSiteCap;
 
@@ -261,17 +196,14 @@ bool Mystery::load(uint num, Common::RandomSource *rng) {
 		_cChain[i] = readU16((26 + i) * 2);
 	}
 
-	// Per-mystery runtime state ??? `_ReadMystery` zeroes these at load.
+	// Per-mystery runtime state ??? _ReadMystery zeroes these at load.
+	// _newOrder uses identity mapping; original randomly cycles gallery
+	// positions but requires matching changes in both clue side-effect
+	// and rendering paths.
 	memset(_cluesFound, 0, sizeof(_cluesFound));
 	memset(_noteSelected, 0, sizeof(_noteSelected));
 	memset(_hotSpotsSeen, 0, sizeof(_hotSpotsSeen));
 	memset(_inGallery, 0, sizeof(_inGallery));
-	// `_NewOrder` in the original randomly cycles the gallery positions
-	// per playthrough. For consistency between clue side-effects (which
-	// write to `_inGallery[_newOrder[galIdx]]`) and gallery rendering
-	// (which iterates logical indices), we keep the identity mapping.
-	// If the original's randomized positioning is required later, both
-	// the side-effect path AND the rendering path need to use it together.
 	(void)rng;
 	for (uint i = 0; i < kGalleryCap; i++)
 		_newOrder[i] = (uint8)i;
@@ -280,7 +212,7 @@ bool Mystery::load(uint num, Common::RandomSource *rng) {
 	_sawCOFFSITEs = _sawCONSITEs = _sawHelpHint = _solvedPuzzle = false;
 	_firstTry = true;
 	_searchLocationNumber = _siteNumber = 0xFFFF;
-	_lastSite = 0x1B; // Sentinel matching _ReadMystery's `_LastSite = 0x1b`.
+	_lastSite = 0x1B; // _ReadMystery _LastSite sentinel.
 
 	debugC(1, kDebugMystery, "Loaded %s (%d B): %u sites, %u suspects, "
 		   "CON=%u COFF=%u, init=0x%04x site=0x%04x text=0x%04x",
@@ -293,9 +225,8 @@ bool Mystery::load(uint num, Common::RandomSource *rng) {
 const byte *Mystery::siteIndexEntry(uint siteNum) const {
 	if (!isLoaded() || siteNum >= _numSites)
 		return nullptr;
-	// Floppy site index uses 2-byte (u16) entries ??? verified at
-	// `_DoSiteLoop_Floppy @ 1652:03d2` reading `*(int *)
-	// ((int)_FloppySiteIndexPtr + siteNum * 2)`. CD uses 6-byte rows.
+	// Floppy site index: 2-byte u16 entries (_DoSiteLoop_Floppy @ 1652:03d2).
+	// CD: 6-byte rows.
 	const uint stride = _isFloppy ? 2 : 6;
 	const uint off = _siteIndexOffset + siteNum * stride;
 	if (off + stride > _data.size())
@@ -383,13 +314,9 @@ void Mystery::loadFloppySiteAnimData() {
 
 const byte *Mystery::hotspots(uint siteNum) const {
 	if (_isFloppy) {
-		// Floppy: hotspot table sits inside the per-site sub-blob.
-		// `site_data[+4..5]` is a u16 file offset to a header byte
-		// (count) + N??8-byte rectangles (x1, y1, x2, y2 as u16s) ???
-		// verified at `FUN_22dc_0b80 @ 22dc:0b80` (the click hit-test
-		// loop reads `*(byte *)(buf + site_data[+4])` for the count
-		// then `FUN_14c9_0039(... buf + site_data[+4] + 1 + i*8)`
-		// for each rectangle).
+		// Floppy hotspot table inside per-site sub-blob (FUN_22dc_0b80 @ 22dc:0b80).
+		// site_data[+4..5] = u16 file offset to count byte + N x 8-byte rects
+		// (x1, y1, x2, y2 as u16s).
 		const byte *site = siteData(siteNum);
 		if (!site || (size_t)(site - _data.data()) + 6 > _data.size())
 			return nullptr;
@@ -453,11 +380,10 @@ const byte *Mystery::noteIndex() const {
 uint16 Mystery::noteIndexCount() const {
 	if (!isLoaded())
 		return 0;
-	// NoteIndex runs from _noteOffset to the start of GalleryData.
-	// CD entries are 4 bytes (`u16 textOff; u16 points`); floppy
-	// entries are 7 bytes (`u16 ?; u16 jakeOff; u16 jennyOff; u8
-	// score`) ??? verified at `FUN_22dc_05c8 @ 22dc:0843` reading
-	// `*(int *)(notes + idx*7 + 2)` (Jake) / `+4` (Jenny).
+	// NoteIndex runs from _noteOffset to start of GalleryData.
+	// CD entries: 4 bytes (u16 textOff; u16 points).
+	// Floppy entries: 7 bytes (u16 ?; u16 jakeOff; u16 jennyOff; u8 score)
+	// per FUN_22dc_05c8 @ 22dc:0843.
 	if (_galleryOffset <= _noteOffset)
 		return 0;
 	const uint stride = _isFloppy ? 7 : 4;
@@ -473,11 +399,8 @@ bool Mystery::noteHasNotebookText(uint clueId) const {
 	if (!_isFloppy)
 		return true;
 
-	// `_DrawNotes_Floppy @ 15e0:01e8` first checks `_TextSeen[idx]`,
-	// then skips the row when `*(u16 *)(notes + idx * 7) == 0`.
-	// Many floppy dialog records are spoken-only lines: they must still
-	// be marked seen so site dialog does not repeat, but they are not
-	// notebook clues and should not render fallback "note N" labels.
+	// _DrawNotes_Floppy @ 15e0:01e8 skips rows with notes[idx*7..idx*7+1] == 0.
+	// Spoken-only dialog records are marked seen but have no notebook text.
 	return READ_LE_UINT16(ni + clueId * 7) != 0;
 }
 
@@ -491,12 +414,9 @@ const byte *Mystery::mapEntry(uint siteNum) const {
 	if (!isLoaded() || siteNum >= _numSites)
 		return nullptr;
 	if (_isFloppy) {
-		// Floppy SITES section: byte[0] = count, then 11-byte entries.
-		// Verified at `FUN_1fed_07ed` (BigMap site iteration) where
-		// `pcVar2 = _FloppySuspectsPtr` (header[+4]) and the loop reads
-		// `*(int *)(pcVar2 + i*0xb + 7)` (X) and `*(int *)(pcVar2 + i*0xb
-		// + 9)` (Y) ??? the +7/+9 offsets are 1-based because pcVar2[0]
-		// holds the count, so entry stride 11 starts at byte 1.
+		// Floppy SITES section (FUN_1fed_07ed): byte[0]=count, then 11-byte
+		// entries starting at byte 1.
+		//   +4 picID, +6..7 u16 X, +8..9 u16 Y, +10 recolor.
 		const uint off = _floppySuspectsOff + 1 + siteNum * 11;
 		if (off + 11 > _data.size())
 			return nullptr;
@@ -509,15 +429,12 @@ const byte *Mystery::mapEntry(uint siteNum) const {
 }
 
 const byte *Mystery::floppySuspectEntry(uint suspectIdx) const {
-	// Floppy gallery section: byte[0] = numSuspects, then per suspect a
-	// variable-size record of `5 + nameLen` bytes (verified at
-	// `_DrawGallery_Floppy @ 154e:00b6` advancing `iVar7 += pbVar4[4]
-	// + 5`):
-	//   u16 +0  picID (gallery portrait, BUTTON.DBD entry)
-	//   u16 +2  alibi marker (0xFFFF = guilty; else high byte = index
-	//           into TEXT_BLOCK alibi-offset table at header[+0xc])
-	//   u8  +4  name length
-	//   u8  +5..+5+nameLen-1  name string
+	// Floppy gallery section (_DrawGallery_Floppy @ 154e:00b6).
+	// byte[0]=numSuspects, then per suspect a `5 + nameLen` record:
+	//   u16 +0 picID (BUTTON.DBD entry)
+	//   u16 +2 alibi marker (0xFFFF=guilty; else hi byte indexes TEXT_BLOCK)
+	//   u8  +4 nameLen
+	//   u8  +5.. name string
 	if (!_isFloppy || !isLoaded())
 		return nullptr;
 	const byte *gd = _data.data() + _galleryOffset;
@@ -539,11 +456,9 @@ const byte *Mystery::floppySuspectEntry(uint suspectIdx) const {
 }
 
 bool Mystery::isGuilty(uint suspectIdx) const {
-	// `_WITCH @ 1df2:089f` (CD): `if (GalleryData[i*0x46 + 0x02] == -1)
-	// _DisplayCorrect(); else _DisplayAlibi(...)`. Innocent suspects
-	// store their alibi-text TextBlock offset at +0x02; the guilty
-	// one stores the sentinel 0xFFFF. Floppy uses the same convention
-	// at suspect entry +2..3 but with variable-stride entries.
+	// _WITCH @ 1df2:089f (CD): GalleryData[i*0x46 + 0x02] == 0xFFFF marks
+	// guilty; innocent suspects store their alibi TextBlock offset there.
+	// Floppy uses same convention at suspect entry +2..3 (variable stride).
 	if (_isFloppy) {
 		const byte *e = floppySuspectEntry(suspectIdx);
 		return e && READ_LE_UINT16(e + 2) == 0xFFFF;
@@ -557,13 +472,9 @@ bool Mystery::isGuilty(uint suspectIdx) const {
 
 uint16 Mystery::alibiTextOffset(uint suspectIdx) const {
 	if (_isFloppy) {
-		// Floppy alibi: u16 at suspect +2..3 carries TWO things:
-		// 0xFFFF = guilty, otherwise the HIGH BYTE indexes the
-		// TEXT_BLOCK table (header[+0xc]), each entry u16 = absolute
-		// alibi-text offset in the buffer. Verified at
-		// `_DisplayAlibi_Floppy @ 1d40:0145` reading
-		// `*(int *)(textBlock + ((byte *)entry)[3] * 2)`. The result
-		// is an ABSOLUTE offset; caller reads via `blobAt(off)`.
+		// Floppy alibi (_DisplayAlibi_Floppy @ 1d40:0145): u16 at suspect +2..3.
+		// 0xFFFF = guilty; else high byte indexes the TEXT_BLOCK table at
+		// header[+0xc], each entry u16 = absolute alibi-text offset.
 		const byte *e = floppySuspectEntry(suspectIdx);
 		if (!e)
 			return 0xFFFF;
@@ -583,10 +494,8 @@ uint16 Mystery::alibiTextOffset(uint suspectIdx) const {
 }
 
 const byte *Mystery::hintBlock() const {
-	// Header word at index 9 (`_hintOffset`) ??? used by `_KDHelp @
-	// 1560:010a`'s per-chain-clue hint table. Each pair-of-bytes is
-	// a TextBlock offset for the corresponding `_AChain` entry, or
-	// `0xFFFF` if no hint is defined for that chain position.
+	// Header word[9] _hintOffset (_KDHelp @ 1560:010a). Each u16 is a
+	// TextBlock offset for the corresponding _AChain entry, or 0xFFFF.
 	if (!isLoaded() || _hintOffset == 0 || _hintOffset >= _data.size())
 		return nullptr;
 	return _data.data() + _hintOffset;
@@ -604,10 +513,8 @@ int Mystery::selectedPoints() const {
 	if (!ni || cnt == 0)
 		return 0;
 	if (_isFloppy) {
-		// Floppy `_GetSelectedPoints_Floppy @ 1d40:0c23`: collect the
-		// per-note score (note +6 byte) for every clue with TextSeen
-		// set, sort descending, sum the top 5. Mirrors
-		// `_SortNotesByScore_Floppy @ 1d40:000e` + the top-5 fold.
+		// _GetSelectedPoints_Floppy @ 1d40:0c23: per-note score (note +6)
+		// for each TextSeen clue, sort descending, sum top 5.
 		uint8 scores[Mystery::kCluesFoundCap] = {};
 		uint scoreCount = 0;
 		const uint maxIdx = MIN<uint>(cnt, kCluesFoundCap);
@@ -616,7 +523,7 @@ int Mystery::selectedPoints() const {
 				continue;
 			scores[scoreCount++] = ni[i * 7 + 6];
 		}
-		// Partial selection sort for top 5.
+		// Partial selection sort for the top 5 scores.
 		const uint topN = MIN<uint>(5u, scoreCount);
 		for (uint k = 0; k < topN; k++) {
 			uint best = k;
@@ -639,7 +546,7 @@ int Mystery::selectedPoints() const {
 	for (uint i = 0; i < cnt && i < kCluesFoundCap; i++) {
 		if (!_noteSelected[i])
 			continue;
-		// Each NoteIndex entry is 4 bytes: u16 textOff + u16 points.
+		// CD NoteIndex entry: 4 bytes = u16 textOff + u16 points (at +2).
 		const uint16 pts = READ_LE_UINT16(ni + i * 4 + 2);
 		total += (int)(int16)pts;
 	}
diff --git a/engines/eem/mystery.h b/engines/eem/mystery.h
index 8ef1c584715..07545cf6e23 100644
--- a/engines/eem/mystery.h
+++ b/engines/eem/mystery.h
@@ -30,31 +30,23 @@
 
 namespace EEM {
 
-/**
- * One mystery (case file) loaded from `M<n>.BIN`.
- *
- * Mirrors the layout established by `_ReadMystery` @ 2404:008f:
- *
- *   word[0]  = InitBlock byte offset
- *   word[2]  = MapData byte offset
- *   word[3]  = SiteIndex byte offset
- *   word[4]  = TextBlock byte offset
- *   word[5]  = NoteIndex byte offset
- *   word[6]  = GalleryData byte offset
- *   word[7]  = KDTextIndex byte offset
- *   word[8]  = SolvedClues byte offset
- *   word[9]  = HintBlock byte offset
- *   word[10] = NumSites + start of MysteryStats
- *   word[13] = NumSuspects (low byte)
- *   word[14] = NumCONSITEs
- *   word[15] = NumCOFFSITEs
- *   word[16..20] = AChain (5 words)
- *   word[21..25] = BChain
- *   word[26..30] = CChain
- *
- * Per-mystery state is reset every time a mystery is loaded; chains and
- * indices are pointers into the in-memory `_data` blob.
- */
+/// Mystery file M<n>.BIN. CD header layout from _ReadMystery @ 2404:008f:
+///   word[0]      InitBlock offset
+///   word[2]      MapData offset
+///   word[3]      SiteIndex offset
+///   word[4]      TextBlock offset
+///   word[5]      NoteIndex offset
+///   word[6]      GalleryData offset
+///   word[7]      KDTextIndex offset
+///   word[8]      SolvedClues offset
+///   word[9]      HintBlock offset
+///   word[10]     NumSites
+///   word[13]     NumSuspects (low byte)
+///   word[14]     NumCONSITEs
+///   word[15]     NumCOFFSITEs
+///   word[16..20] AChain (5 words)
+///   word[21..25] BChain
+///   word[26..30] CChain
 class Mystery {
 public:
 	static const uint kNumChains       = 3;
@@ -67,14 +59,11 @@ public:
 	Mystery() = default;
 	~Mystery() = default;
 
-	/// Load `M<num>.BIN` and reset per-mystery state. Returns false on error.
+	/// Load M<num>.BIN and reset per-mystery state. Returns false on error.
 	bool load(uint num, class Common::RandomSource *rng = nullptr);
 
-	/// Drop the loaded mystery and zero per-mystery state. Safe to call
-	/// at any time; `isLoaded()` returns false afterward.
 	void clear();
 
-	/// True once `load()` succeeded and offsets are valid.
 	bool isLoaded() const { return !_data.empty(); }
 
 	uint number() const { return _number; }
@@ -83,120 +72,93 @@ public:
 	uint8  numCONSITEs() const { return _numCONSITEs; }
 	uint8  numCOFFSITEs() const { return _numCOFFSITEs; }
 
-	/// Pointer to the InitBlock (case briefing).
-	/// _InitBlock @ 2d5d:?? = mystery + word[0] in `_ReadMystery`.
+	/// InitBlock (case briefing) at mystery + word[0].
 	const byte *initBlock() const;
 
-	/// Pointer to the GalleryData; one 0x46-byte entry per suspect.
-	/// First u16 of each entry is the PIC picture ID for that suspect.
+	/// GalleryData: 0x46-byte entry per suspect; first u16 = PIC picture ID.
 	const byte *galleryData() const;
 
 	/// Floppy variable-stride suspect record. Returns nullptr on CD or
-	/// when @p suspectIdx is out of range. Walks the gallery section
-	/// (`5 + nameLen` bytes per suspect) to land on the requested entry.
-	/// Layout: u16 picID, u16 alibiMarker (0xFFFF = guilty),
+	/// out-of-range. Layout: u16 picID, u16 alibiMarker (0xFFFF=guilty),
 	/// u8 nameLen, nameLen bytes of name.
 	const byte *floppySuspectEntry(uint suspectIdx) const;
 
-	/// Pointer to the NoteIndex array (4 bytes per entry: u16 textOff + u16 pts).
+	/// NoteIndex array (4 bytes per entry: u16 textOff + u16 pts).
 	const byte *noteIndex() const;
 
-	/// Number of entries in NoteIndex.
 	uint16 noteIndexCount() const;
 
-	/// True when @p clueId has a visible notebook/accuse text entry.
-	/// Floppy dialog text indices may be spoken-only records with a
-	/// zero notebook text offset; those are marked seen but skipped by
-	/// `_DrawNotes_Floppy`.
+	/// True when clueId has a notebook text entry. Floppy dialog records
+	/// may be spoken-only with zero notebook offset, skipped by _DrawNotes_Floppy.
 	bool noteHasNotebookText(uint clueId) const;
 
-	/// Pointer to the KDTextIndex; first u16s are TextBlock offsets for
-	/// host hint lines.
+	/// KDTextIndex; first u16s are TextBlock offsets for host hint lines.
 	const byte *kdTextIndex() const;
 
-	/// Pointer to the HintBlock; per-clue hint TextBlock offsets indexed
-	/// by `_aChain[i]` (the Nth required clue). Mirrors the
-	/// `_HintBlock` global read in `_KDHelp @ 1560:010a`.
+	/// HintBlock (_KDHelp @ 1560:010a). Per-clue hint TextBlock offsets
+	/// indexed by _aChain[i].
 	const byte *hintBlock() const;
 
-	/// Read entry @p i from `_aChain` (the required-clue chain). Returns
-	/// 0xFFFF when no entry exists. Used by `_KDHelp` to walk unfound
-	/// clues for hints.
+	/// Entry @p i of the required-clue chain. Returns 0xFFFF when out of
+	/// range. Walked by `_KDHelp` to find unfound clues for hints.
 	uint16 aChain(uint i) const {
 		return i < kChainLen ? _aChain[i] : 0xFFFF;
 	}
 
-	/// Pointer to the MapData entry for site @p siteNum (14 bytes per
-	/// entry; first u16 = sitepic, +4..7 = (x, y) on the big map).
+	/// MapData entry for siteNum: 14 bytes; first u16 = sitepic, +4..7 = (x, y).
 	const byte *mapEntry(uint siteNum) const;
 
-	/// Pointer to the SiteIndex entry for site @p siteNum (6 bytes per site).
+	/// SiteIndex entry for siteNum (6 bytes per site on CD).
 	const byte *siteIndexEntry(uint siteNum) const;
 
-	/// Pointer to the SiteData (sitepic, travel, hotspot count, ...)
-	/// referenced by SiteIndex[@p siteNum].
+	/// SiteData (sitepic, travel, hotspot count, ...) per SiteIndex[siteNum].
 	const byte *siteData(uint siteNum) const;
 
-	/// Floppy-only pointer to the matching `ANI.BIN` per-site animation
-	/// block. Layout: u8 cycleCount, cycleCount ?? {u8 start, u8 end},
-	/// u8 animCount, animCount ?? {u8 animId, u16 x, u8 y}.
+	/// Floppy ANI.BIN per-site animation block. Layout:
+	/// u8 cycleCount, cycleCount x {u8 start, u8 end},
+	/// u8 animCount, animCount x {u8 animId, u16 x, u8 y}.
 	const byte *floppySiteAnimData(uint siteNum) const;
 
-	/// Pointer to the hotspot rectangle array for site @p siteNum.
-	/// Each rect is 14 bytes: x1, y1, x2, y2, then 6 bytes of clue data.
+	/// Hotspot rectangle array for siteNum (14 bytes each: x1,y1,x2,y2 + clue).
 	const byte *hotspots(uint siteNum) const;
 
-	/// Number of hotspots in site @p siteNum.
 	uint16 hotspotCount(uint siteNum) const;
 
-	/// Pointer to a NUL-terminated string at TextBlock+ at p offset.
+	/// NUL-terminated string at TextBlock + offset.
 	const char *textAt(uint16 offset) const;
 
-	/// Pointer at byte offset @p offset within the mystery blob, or null
-	/// if out of range. Used to chase ClueBlock pointers stored in
-	/// hotspot data.
+	/// Pointer at byte @p offset within the mystery blob, or null if out
+	/// of range. Used to chase ClueBlock pointers stored in hotspot data.
 	const byte *blobAt(uint32 offset) const {
 		return offset < _data.size() ? _data.data() + offset : nullptr;
 	}
 
-	/// Total mystery blob size in bytes (for bounds checks).
 	uint32 dataSize() const { return (uint32)_data.size(); }
 
-	/// Synchronize the per-mystery runtime state for save/load. The fixed
-	/// arrays serialize first, then the booleans and counters.
 	void syncState(Common::Serializer &s);
 
-	/// Sum of point values of every selected notebook entry. Mirrors
-	/// `_GetSelectedPoints` @ 1df2:00bd.
+	/// _GetSelectedPoints @ 1df2:00bd.
 	int selectedPoints() const;
 
-	/// Sum of the top five point values among found notebook entries.
-	/// Mirrors `_GetFoundPoints` @ 1df2:0098.
+	/// _GetFoundPoints @ 1df2:0098 ??? sum of top 5 found notebook entries.
 	int foundPoints() const;
 
-	/// True when `selectedPoints() > 99`. Mirrors `_SolvedCheck`.
 	bool solvedCheck() const { return selectedPoints() > 99; }
 
-	/// True iff suspect @p suspectIdx is the case's guilty party. The
-	/// guilty marker is `GalleryData[suspectIdx * 0x46 + 0x02] ==
-	/// 0xFFFF` ??? innocent suspects store their alibi text offset there;
-	/// the guilty suspect uses the sentinel. Verified at `_WITCH @
-	/// 1df2:089f` (`if (psVar1->field_0x2 == -1) _DisplayCorrect();
-	/// else _DisplayAlibi(...)`).
+	/// _WITCH @ 1df2:089f. GalleryData[i*0x46 + 0x02] == 0xFFFF marks the
+	/// guilty suspect; innocent suspects store their alibi TextBlock offset.
 	bool isGuilty(uint suspectIdx) const;
 
-	/// TextBlock offset of suspect @p suspectIdx's alibi text. Returns
-	/// 0xFFFF for the guilty suspect (no alibi).
+	/// TextBlock offset of suspect's alibi text. 0xFFFF for guilty suspect.
 	uint16 alibiTextOffset(uint suspectIdx) const;
 
-	/// Pointer to the win-clueblock (`MysteryIndex[+0x10]` =
-	/// `_solvedOffset`). Mirrors `_DisplayCorrect`'s
-	/// `_DisplayClue(_Mystery + MysteryIndex[+0x10], 0)` at 1df2:0769.
+	/// Win-clueblock at `MysteryIndex[+0x10]` = `_solvedOffset`. Used by
+	/// `_DisplayCorrect` @ 1df2:0769 (`_DisplayClue(_Mystery + MysteryIndex[+0x10], 0)`).
 	const byte *solvedClueBlock() const;
 
 	/// Per-mystery runtime state, zeroed at load time.
 	uint8  _cluesFound[kCluesFoundCap]   = {};
-	uint8  _noteSelected[kCluesFoundCap] = {};  ///< Mirror `_NoteSelected`
+	uint8  _noteSelected[kCluesFoundCap] = {};  ///< _NoteSelected
 	uint16 _hotSpotsSeen[kHotSpotsCap]   = {};
 	uint16 _inGallery[kGalleryCap]       = {};
 	uint8  _newOrder[kGalleryCap]        = {};
@@ -234,19 +196,15 @@ private:
 	uint16 _bChain[kChainLen] = {};
 	uint16 _cChain[kChainLen] = {};
 
-	// Floppy variant uses a completely different header (see comment
-	// in `Mystery::load`). When `_isFloppy` is true, the CD-shaped
-	// `_initOffset / _siteIndexOffset / etc.` fields are unset and the
-	// floppy section pointers below are populated from the floppy
-	// header offsets verified at `_ReadMystery_Floppy @ 22dc:0178`.
+	// Floppy variant ??? see Mystery::load. _ReadMystery_Floppy @ 22dc:0178.
 	bool   _isFloppy = false;
-	uint16 _floppySuspectsOff = 0;   ///< header[+4]  ??? suspects
-	uint16 _floppyHintBlockOff = 0;  ///< header[+6]  ??? hint???clue table
-	uint16 _floppyNoteIndexOff = 0;  ///< header[+8]  ??? notes (7B/clue)
-	uint16 _floppyGalleryOff = 0;    ///< header[+0xa] ??? gallery portraits
-	uint16 _floppyTextOff = 0;       ///< header[+0xc] ??? text block
-	uint16 _floppyKDTextOff = 0;     ///< header[+0x10] ??? KDTextIndex
-	uint16 _floppySolvedOff = 0;     ///< header[+0x12] ??? solved clue chain
+	uint16 _floppySuspectsOff = 0;   ///< header[+4]    suspects
+	uint16 _floppyHintBlockOff = 0;  ///< header[+6]    hint -> clue table
+	uint16 _floppyNoteIndexOff = 0;  ///< header[+8]    notes (7B/clue)
+	uint16 _floppyGalleryOff = 0;    ///< header[+0xa]  gallery portraits
+	uint16 _floppyTextOff = 0;       ///< header[+0xc]  text block
+	uint16 _floppyKDTextOff = 0;     ///< header[+0x10] KDTextIndex
+	uint16 _floppySolvedOff = 0;     ///< header[+0x12] solved clue chain
 	Common::Array<byte> _floppySiteAnimData;
 	uint16 _floppySiteAnimSiteOff[kVisitedSiteCap] = {};
 
diff --git a/engines/eem/resource.cpp b/engines/eem/resource.cpp
index a08ce19f85f..deb49700d57 100644
--- a/engines/eem/resource.cpp
+++ b/engines/eem/resource.cpp
@@ -53,7 +53,7 @@ bool DBDArchive::open(const Common::Path &dbdName, const Common::Path &dbxName)
 		return false;
 	}
 
-	// _InitGraphicsSystem @ 172b:0145 reads 10 bytes per entry until EOF.
+	// _InitGraphicsSystem @ 172b:0145: 10-byte entries until EOF.
 	const int32 dbxSize = dbx.size();
 	_index.reserve(dbxSize / 10);
 	while (dbx.pos() + 10 <= dbxSize) {
@@ -76,10 +76,8 @@ void DBDArchive::close() {
 	_index.clear();
 }
 
-/**
- * Read one 12-byte frame header + payload at the current stream position.
- * Shared between picture and animation loaders since the layout is the same.
- */
+/// Read one 12-byte frame header + payload at the current stream position.
+/// Shared between picture and animation loaders since the layout is identical.
 bool readFrame(Common::SeekableReadStream &stream, bool compressed, Picture &out) {
 	out.flags             = stream.readUint16LE();
 	const uint16 height   = stream.readUint16LE();
@@ -116,11 +114,10 @@ bool readFrame(Common::SeekableReadStream &stream, bool compressed, Picture &out
 bool DBDArchive::loadEntry(uint num, Picture &out) {
 	if (num >= _index.size()) {
 		// Out-of-range picture IDs are non-fatal ??? every caller already
-		// checks the return value (e.g., `haveDone`/`haveCrime` in
-		// `drawBigMapOverview`). The floppy's PICS.DBD ships fewer
-		// entries than the CD (e.g., the BigMap done-marker `0x20D` is
-		// CD-only), so this fires routinely on floppy and shouldn't
-		// be a `warning`.
+		// checks the return value (e.g. `haveDone` / `haveCrime` in
+		// `drawBigMapOverview`). Floppy PICS.DBD ships fewer entries than
+		// CD (e.g. BigMap done-marker 0x20D is CD-only), so this fires
+		// routinely on floppy and stays at debug level rather than warning.
 		debugC(2, kDebugGfx,
 			   "DBDArchive::loadEntry: %u out of range (max %u)",
 			   num, (uint)_index.size());
@@ -133,8 +130,7 @@ bool DBDArchive::loadEntry(uint num, Picture &out) {
 		return false;
 	}
 
-	// Mirrors _GetFromDB @ 172b:105d. The 2-byte word read first matches
-	// loadAnimation's frame-count read; for picture entries it is always 1.
+	// _GetFromDB @ 172b:105d. Leading u16 = frame count (always 1 for pictures).
 	(void)_dbd.readUint16LE();
 	return readFrame(_dbd, entry.compressed != 0, out);
 }
@@ -151,7 +147,7 @@ bool DBDArchive::loadAnimation(uint num, Animation &out) {
 		return false;
 	}
 
-	// Mirrors _GetAnimation @ 172b:163a: u16 frame count, then N frames.
+	// _GetAnimation @ 172b:163a: u16 frame count, then N frames.
 	const uint16 frameCount = _dbd.readUint16LE();
 	if (frameCount == 0 || frameCount > 256) {
 		warning("DBDArchive::loadAnimation: %u has implausible frame count %u",
diff --git a/engines/eem/resource.h b/engines/eem/resource.h
index 846bf0f1135..a984f323d35 100644
--- a/engines/eem/resource.h
+++ b/engines/eem/resource.h
@@ -31,79 +31,47 @@
 
 namespace EEM {
 
-/**
- * Index entry for a .DBD/.DBX archive pair (10 bytes on disk).
- *
- * Mirrors the original `dbi` struct read by _InitGraphicsSystem @ 172b:0145
- * in 10-byte chunks until EOF. Each entry locates one resource blob in the
- * companion .DBD container.
- */
+/// dbi struct from _InitGraphicsSystem @ 172b:0145 (10 bytes per entry).
 struct DBEntry {
-	uint32 offset;     ///< Byte offset of the entry in the .DBD file.
-	uint16 compressed; ///< Non-zero if the payload is PKWARE DCL ("Implode") packed.
-	uint32 size;       ///< Total size of the entry on disk (including 14-byte header).
+	uint32 offset;     ///< Byte offset in the .DBD file.
+	uint16 compressed; ///< Non-zero = PKWARE DCL ("Implode") packed payload.
+	uint32 size;       ///< Total entry size on disk (incl. 14-byte header).
 };
 
-/**
- * 8-bit indexed picture decoded from a .DBD entry.
- *
- * The original engine's PicData is a 16-byte struct; we keep the descriptive
- * fields here and let `Graphics::ManagedSurface` own the pixel data so the
- * rest of the engine can blit/scale/clip with the standard API.
- */
+/// 8-bit indexed picture decoded from a .DBD entry. Original PicData is
+/// 16 bytes; pixels live in Graphics::ManagedSurface.
 struct Picture {
-	uint16 flags     = 0; ///< +0  high byte = sub-mode used by some sprites
-	uint16 rowoff    = 0; ///< +6  row offset (used by some clipped sprites)
-	uint16 miscflags = 0; ///< +8  high byte = transparent-mask flag
+	uint16 flags     = 0; ///< +0  high byte: sub-mode
+	uint16 rowoff    = 0; ///< +6  row offset (clipped sprites)
+	uint16 miscflags = 0; ///< +8  high byte: transparent-mask flag
 	uint16 compsize  = 0; ///< +10 packed payload size on disk
 	Graphics::ManagedSurface surface;
 };
 
-/// Multi-frame animation as stored in ANI.DBD ??? a sequence of Pictures.
+/// Multi-frame animation from ANI.DBD.
 typedef Common::Array<Picture> Animation;
 
-/**
- * Reader for a .DBD + .DBX archive pair.
- *
- * The original engine has five such pairs: PICS, SITES, ANI, BALLOON, BUTTON.
- * Each .DBX is parsed once into an in-memory `_index`; reads of individual
- * entries seek into the .DBD on demand and (when flagged) decompress with
- * `Common::decompressDCL`.
- */
+/// .DBD + .DBX archive pair (PICS, SITES, ANI, BALLOON, BUTTON).
 class DBDArchive {
 public:
 	DBDArchive();
 	~DBDArchive();
 
-	/**
-	 * Open both halves of an archive. @p dbdName / @p dbxName are looked up
-	 * via SearchMan, so case is normalized for us. Returns false if either
-	 * file is missing or the index is malformed.
-	 */
+	/// Open both halves; returns false if either file is missing/malformed.
 	bool open(const Common::Path &dbdName, const Common::Path &dbxName);
 	void close();
 
-	/** Number of entries in the index. */
 	uint32 size() const { return _index.size(); }
 
-	/**
-	 * Load entry @p num (0-based index), decompressing if needed.
-	 * Returns true on success. Mirrors _GetFromDB @ 172b:105d.
-	 */
+	/// Load entry num (0-based), decompressing if needed.
+	/// _GetFromDB @ 172b:105d.
 	bool loadEntry(uint num, Picture &out);
 
-	/**
-	 * Convenience wrapper that mirrors the engine's 1-based picture API:
-	 * `_GetPicture(num)` calls `_GetFromDB(..., num - 1)`. Use this when
-	 * porting code that references picture IDs by their original number.
-	 */
+	/// 1-based wrapper matching the engine's _GetPicture(num) -> _GetFromDB(num - 1).
 	bool getPicture(uint num, Picture &out) { return loadEntry(num - 1, out); }
 
-	/**
-	 * Load a multi-frame animation entry. Mirrors _GetAnimation @ 172b:163a:
-	 * read u16 frameCount, then for each frame read a 12-byte header and
-	 * decompress the payload. Used for ANI.DBD entries.
-	 */
+	/// Multi-frame entry. _GetAnimation @ 172b:163a: u16 frameCount,
+	/// then per frame a 12-byte header + payload.
 	bool loadAnimation(uint num, Animation &out);
 
 private:
diff --git a/engines/eem/site.cpp b/engines/eem/site.cpp
index f309a2d76bc..167f9d0d1cd 100644
--- a/engines/eem/site.cpp
+++ b/engines/eem/site.cpp
@@ -39,28 +39,20 @@ constexpr Common::Rect kSitePdaRect(Common::Point(35, 111), 21, 25);
 constexpr Common::Rect kSitePartnerFootMapRect(Common::Point(7, 177), 50, 23);
 constexpr Common::Rect kSitePartnerHeadHintRect(Common::Point(5, 80), 39, 30);
 
-// Masked blit a Picture into a ManagedSurface. Pixels equal to `transp`
-// (the high byte of `pic.flags`, per `_Rect_Move_Mask @ 1000:03fc`) are
-// skipped. Used by `enterSiteAnim` for both skateboard + KD slide-in
-// passes; the surface is the in-memory frame buffer that gets pushed
-// to the screen each tick.
+// Masked blit using `transp` = high byte of `pic.flags` (`_Rect_Move_Mask @ 1000:03fc`).
 void blitFrame(Graphics::ManagedSurface &dst, const Picture &p,
 			   int x, int y, byte transp) {
 	dst.transBlitFrom(p.surface, Common::Point(x, y), (uint32)transp);
 }
 
-// Mask-aware blit from a Picture into a `Graphics::Surface` (the
-// locked framebuffer). Same pixel-mask semantics as `blitFrame`.
-// Used by hotspot/NPC rendering inside `SiteScreen::renderHotspots`
-// and `renderStaticDrops`.
+// Top-left masked blit. `_AddDrop @ 172b:1a77` calls
+// `_Rect_Move_Mask(..., x, y, ...)` with the raw (x, y) and IGNORES
+// per-frame anchor offsets ??? so this is the correct path for static
+// drops and any non-animated overlay. Animations must route through
+// `blitAnimFrameAnchored` instead so per-frame anchor offsets
+// (miscflags = X, rowoff = Y) apply correctly.
 void blitMaskedSurface(Graphics::Surface *screen, const Picture &p,
 					   int x, int y) {
-	// Top-left semantics. Used for static drops (`_AddDrop @
-	// 172b:1a77` which calls `_Rect_Move_Mask(..., x, y, ...)` with
-	// the raw (x, y) and ignores per-frame anchors) and any other
-	// non-animated overlay. Animation rendering should go through
-	// `blitAnimFrameAnchored` instead so per-frame anchor offsets
-	// (miscflags = X, rowoff = Y) apply correctly.
 	if (!screen)
 		return;
 	const byte transp = (byte)(p.flags >> 8);
@@ -82,17 +74,10 @@ void blitMaskedSurface(Graphics::Surface *screen, const Picture &p,
 
 void blitAnimFrameAnchored(Graphics::Surface *screen, const Picture &p,
 						   int anchorX, int anchorY) {
-	// `_UpdateAnimations @ 172b:09c1` blits each animation frame at
-	// `(anchor_x - puVar5[4], anchor_y - puVar5[3])` where puVar5[3]/[4]
-	// are the per-frame `rowoff` / `miscflags` values from the
-	// 16-byte PicData header. Both are SIGNED 16-bit anchor offsets
-	// ??? when frames have varying anchors (anim 0x14 BigMap walk-
-	// cycle has miscflags = -2 per cell, anim 0x07 has rowoff up to
-	// 61), the sprite actually translates across the screen as it
-	// cycles through cells. Without this, the partner "shakes in
-	// place" instead of walking. (Transparency still comes from
-	// `flags >> 8`, verified at the `_Rect_Move_Mask(..., *thePic >>
-	// 8)` call ??? NOT from miscflags as an earlier comment claimed.)
+	// `_UpdateAnimations @ 172b:09c1`: blit at
+	//   (anchor_x - puVar5[4], anchor_y - puVar5[3])
+	// where puVar5[3]/[4] are per-frame rowoff/miscflags (signed int16)
+	// from the 16-byte PicData header. Transparency = flags >> 8.
 	if (!screen)
 		return;
 	const int blitX = anchorX - (int)(int16)p.miscflags;
@@ -114,9 +99,11 @@ void blitAnimFrameAnchored(Graphics::Surface *screen, const Picture &p,
 	}
 }
 
-// Rotate one VGA palette range by one slot. Mirrors `_ColorCycle @
-// 172b:2015` ??? used by both the per-site Loop-1 ColorCycle entries and
-// the always-on hotspot marching-ants range 0xF9..0xFE.
+// `_ColorCycle @ 172b:2015` ??? rotate `_fpal[start..end]` by one slot:
+// save [start], shift [start..end-1] = [start+1..end], restore saved at
+// [end], then re-upload via `_Set_Palette`. We do the same against
+// ScummVM's palette manager. Used by per-site Loop-1 ColorCycle entries
+// and the always-on hotspot marching-ants range 0xF9..0xFE.
 void cyclePaletteRange(uint8 start, uint8 end) {
 	if (end <= start)
 		return;
@@ -138,11 +125,10 @@ void cyclePaletteRange(uint8 start, uint8 end) {
 }
 
 void cyclePaletteRangeReverse(uint8 start, uint8 end) {
-	// Mirrors `_OpenColorCycle @ 2520:04f7`: save the END color, shift
-	// every entry up by one (END-1 ??? END, ...), wrap saved END to
-	// START. Visually colors march from start toward end ??? opposite
-	// direction from `cyclePaletteRange`. Used by the EA Kids /
-	// HighScore logo cycles.
+	// `_OpenColorCycle @ 2520:04f7`: save END, shift every entry up by
+	// one (END-1 ??? END, ...), wrap saved END to START. Visually colors
+	// march from start toward end ??? opposite direction from
+	// `cyclePaletteRange`. Used by the EA Kids / HighScore logo cycles.
 	if (end <= start)
 		return;
 	const uint count = (uint)end - (uint)start + 1;
@@ -163,13 +149,11 @@ void cyclePaletteRangeReverse(uint8 start, uint8 end) {
 	g_system->getPaletteManager()->setPalette(buf, start, count);
 }
 
-// Per-speaker partner-position table verified against `_WaitAnims @
-// 29be:021c`. 12 bytes per entry, indexed by `siteData[+8]`. Layout:
+// `_WaitAnims @ 29be:021c`. 12 bytes per entry, indexed by `siteData[+8]`:
 //   +0..1 anim Jake, +2..3 anim Jenny,
 //   +4..5 x    Jake, +6..7 x    Jenny,
 //   +8..9 y    Jake, +10..11 y    Jenny.
-// Seven valid entries ??? anything past entry 6 in the binary is
-// `_SiteButtons` rect data that follows the table in memory.
+// 7 entries; past entry 6 is `_SiteButtons` rect data.
 const uint16 kWaitAnims[7][6] = {
 	{ 0x00, 0x0a, 0x06, 0x06, 0x50, 0x50 }, // 0
 	{ 0x03, 0x0c, 0x06, 0x06, 0x50, 0x50 }, // 1
@@ -180,9 +164,8 @@ const uint16 kWaitAnims[7][6] = {
 	{ 0x06, 0x06, 0x06, 0x06, 0x50, 0x50 }, // 6
 };
 
-// `_DoKDAnim` lookup table. Six valid kdAnimNum entries (0..5)
-// verified from `29be:0228`. Layout per entry: { animJake, animJenny,
-// xJake, xJenny, yJake, yJenny }. Position is (6, 80) in every entry.
+// `_DoKDAnim` table @ 29be:0228. 6 entries (kdAnimNum 0..5).
+// Layout: { animJake, animJenny, xJake, xJenny, yJake, yJenny }.
 const uint16 kKdAnimTable[6][6] = {
 	{ 0x03, 0x0c, 6, 6, 80, 80 }, // 0 ??? speaker idx 1 wait anim
 	{ 0x01, 0x0b, 6, 6, 80, 80 }, // 1 ??? same as PDA idle
@@ -192,29 +175,27 @@ const uint16 kKdAnimTable[6][6] = {
 	{ 0x06, 0x06, 6, 6, 80, 80 }, // 5 ??? same anim both partners
 };
 
-// Animation script table. Mirrors `_AnimationSequences @ 29be:22d4`
-// (a 55-entry table of far ptrs, each pointing to a u16-frame-index
-// stream terminated by 0x80; 0x81 marks a jump that we don't see in
-// the partner subset and so don't yet implement).
-//
-// `_NewAnimation @ 172b:06e1` reads the script via
+// Animation script table ??? mirrors `_AnimationSequences @ 29be:22d4`
+// (55-entry table of far ptrs, each pointing to a u16-frame-index
+// stream). `_NewAnimation @ 172b:06e1` reads the script via
 // `_AnimationSequences[anim_id]` and stores the pointer in
-// `DAT_2d5d_3eaf[i*0xb]`. `_UpdateAnimations @ 172b:09c1` then walks
-// it one entry per `_CheckFrameRate` tick (~100 ms): the value at
-// `script[index]` is the frame to render; 0x80 resets index to 0
-// (loop). So a script like `[0,0,0,0,0,0,0,0,0,2]` renders nine ticks
-// of frame 0 then one tick of frame 2 ??? the natural "blink with long
-// idle hold" cadence.
+// `DAT_2d5d_3eaf[i*0xb]`; `_UpdateAnimations @ 172b:09c1` then walks it
+// one entry per `_CheckFrameRate` tick (~140 ms).
 //
-// We use the same scripts for the wait anims (`renderPartner`) AND
-// the kd-clue reaction anims (`playKdAnim`), since both call
-// `_NewAnimation` in the original ??? only the state field differs (1
-// = looping, 4 = one-shot). seqnum == animId per `_PlayAnimation`
-// 172b:1f5d push order.
+// Script byte format:
+//   0x80         = restart (loop back to index 0; terminator for one-shots)
+//   0x81 N       = jump to byte N (not used in partner subset)
+//   other        = frame index to render this tick
 //
-// Each entry was read directly from the EXE via Ghidra; cross-checked
-// against `_NewAnimation`'s read. Frame counts include only the
-// playable frames (the trailing 0x80 is the terminator, not a frame).
+// Repeated frames are the original's "frame-hold" mechanism: per-tick
+// walk advances exactly one entry, so K repeats hold the frame for
+// K * `kFramePeriodMs` ??? K * 140 ms (e.g. [0,0,0,0,0,0,0,0,0,2] ???
+// nine ticks of frame 0, one tick of frame 2 = "blink with long
+// idle hold"). Same scripts serve wait anims (looping) and kd-clue
+// reactions (state-4 one-shot ??? see `_PlayAnimation`); state field
+// in the slot differentiates them. `seqnum == animId` per
+// `_PlayAnimation @ 172b:1f5d` push order.
+// Length counts only playable frames; trailing 0x80 is not a frame.
 struct AnimScript {
 	uint16 seqnum;
 	uint8 len;
@@ -225,9 +206,7 @@ const AnimScript kAnimScripts[] = {
 	{ 0x00, 10, { 0,0,0,0,0,0,0,0,0,2 } },
 	// 0x01 (29be:188a) ??? Jake PDA idle: alternating head bob with peak
 	{ 0x01, 15, { 0,1,2,0,1,0,2,1,0,1,0,1,2,1,0 } },
-	// 0x02 (29be:18aa) ??? Jake gallery: brief wave, long hold, second
-	// wave, hold (CONFIRMED 26 frames ??? earlier table was truncated to
-	// 16 which dropped the second wave cycle).
+	// 0x02 (29be:18aa) ??? Jake gallery: brief wave, long hold, second wave, hold.
 	{ 0x02, 26, { 0,1,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,1,0,0,0,0,0,0 } },
 	// 0x03 (29be:18e0) ??? Jake "lift, hold, lower" gesture
 	{ 0x03,  9, { 0,1,2,3,2,2,2,1,0 } },
@@ -235,8 +214,7 @@ const AnimScript kAnimScripts[] = {
 	{ 0x04, 13, { 0,1,2,3,4,5,4,4,4,3,2,1,0 } },
 	// 0x05 (29be:1910) ??? Jake/Jenny shared (speaker 5): held idle, peak
 	{ 0x05, 13, { 0,0,0,1,2,3,2,1,0,0,0,0,0 } },
-	// 0x06 (29be:192c) ??? speaker 6 partner: empty (immediate END,
-	// renders nothing ??? verified at 29be:192c byte 0 = 0x80)
+	// 0x06 (29be:192c) ??? empty (byte 0 = 0x80, immediate END).
 	{ 0x06,  0, { 0 } },
 	// 0x07 (29be:192e) ??? Jake walk-cycle (10 frames: 0..9)
 	{ 0x07, 10, { 0,1,2,3,4,5,6,7,8,9 } },
@@ -256,33 +234,21 @@ const AnimScript kAnimScripts[] = {
 	{ 0x0e,  0, { 0 } },
 	// 0x0f ??? alias of 0x09
 	{ 0x0f,  9, { 0,0,0,1,0,0,0,0,0 } },
-	// 0x10 ??? Jenny gallery wait (alias of 0x09 ??? verified at 29be:1956)
+	// 0x10 (29be:1956, alias of 0x09) ??? Jenny gallery wait.
 	{ 0x10,  9, { 0,0,0,1,0,0,0,0,0 } },
-	// 0x11 (29be:1992) ??? Jenny entrance count-up: 0..7. Used by
-	// `_DoBigMap` when `_LastScreen == 2` (BigMap entrance one-shot).
+	// 0x11 (29be:1992) ??? Jenny BigMap entrance count-up 0..7 (one-shot).
 	{ 0x11,  8, { 0,1,2,3,4,5,6,7 } },
-	// 0x12 (29be:197e) ??? Jake entrance count-down 8..0. Used by
-	// `_DoBigMap` (entrance one-shot, partner-specific exit cell).
+	// 0x12 (29be:197e) ??? Jake BigMap entrance count-down 8..0 (one-shot).
 	{ 0x12,  9, { 8,7,6,5,4,3,2,1,0 } },
-	// 0x13 (29be:1992, alias of 0x11) ??? Jake walk-cycle 0..7,
-	// looped during BigMap idle.
+	// 0x13 (29be:1992, alias of 0x11) ??? Jake BigMap idle walk-cycle.
 	{ 0x13,  8, { 0,1,2,3,4,5,6,7 } },
-	// 0x14 (29be:196a) ??? BigMap idle walk-cycle 0..8 (9 cells),
-	// partner shifts feet while you pick a site.
+	// 0x14 (29be:196a) ??? BigMap idle walk-cycle 0..8.
 	{ 0x14,  9, { 0,1,2,3,4,5,6,7,8 } },
-	// 0x15 (29be:185e, alias of 0x00) ??? Jake CaseSelection greeter:
-	// nine idle, one blink, loop. Same blink cadence as the site
-	// loop's wait anim (animID 0x00).
+	// 0x15 (29be:185e, alias of 0x00) ??? Jake CaseSelection greeter.
 	{ 0x15, 10, { 0,0,0,0,0,0,0,0,0,2 } },
-	// 0x16 (29be:185e, alias of 0x00) ??? Jenny CaseSelection greeter,
-	// same blink script as 0x15.
+	// 0x16 (29be:185e, alias of 0x00) ??? Jenny CaseSelection greeter.
 	{ 0x16, 10, { 0,0,0,0,0,0,0,0,0,2 } },
-	// Site / drop scripts ???28 frames ??? see `_AnimationSequences @
-	// 29be:22d4`. Many of these are short count-ups used by ambient
-	// animations (people walking, vehicles passing) that the original
-	// drives one entry per `_CheckFrameRate` tick (~140 ms). Without
-	// these, our generic fallback cycles through every animation cell
-	// at one entry per tick and the ambient anims look 2-3?? too fast.
+	// Site / drop scripts ???28 frames (`_AnimationSequences @ 29be:22d4`).
 	// 0x1b (29be:192e, alias of 0x07) ??? walk-cycle 0..9.
 	{ 0x1b, 10, { 0,1,2,3,4,5,6,7,8,9 } },
 	// 0x1c (29be:21a8) ??? short 6-frame count-up.
@@ -312,36 +278,20 @@ const AnimScript kAnimScripts[] = {
 	{ 0x34,  5, { 0,1,2,3,4 } },
 	// 0x35 (29be:21b6) ??? count-up 0..10 (11 frames).
 	{ 0x35, 11, { 0,1,2,3,4,5,6,7,8,9,10 } },
-	// Briefing animations ??? `_DoInitClues @ 1a35:0411` calls
-	// `_NewAnimation(..., (PicData *)CONCAT22(0x17, ...), 1, ...)`
-	// for the game animation (always anim ID 0x17 ??? even Jenny's
-	// briefing reuses Jake's SCRIPT, even though the loaded ANI.DBD
-	// cells come from her partner-specific entry 0x3b). Same pattern
-	// for book (0x18 always) and nancy (0x19 always).
-	//
-	// AnimScript len was 28 ??? these scripts overflow that. Bump
-	// `frames[]` is fine because we just need to fit 30 frames per
-	// briefing entry. We size `frames` to 36 so all five scripts fit
-	// (longest is 0x18 at 30 frames).
 };
-static_assert(true, "see kAnimScriptsLong below for >28-frame scripts");
 
-// Scripts longer than 28 frames live here. The lookup in
-// `findAnimScript` checks both arrays. Stored as
-// `(seqnum, len, ptr)` so each script can be any length without
-// bloating every entry ??? the longest (0x22) runs 115 frames.
+// Scripts longer than 28 frames (>fits-inline limit). `findAnimScript`
+// checks both this array and `kAnimScripts`. Longest is 0x22 (115 frames).
 struct AnimScriptLong {
 	uint16 seqnum;
 	uint16 len;
 	const uint8 *frames;
 };
 
-// Briefing animations ??? `_DoInitClues @ 1a35:0411` calls
-// `_NewAnimation(..., (PicData *)CONCAT22(0x17, ...), 1, ...)` for the
-// game animation (always anim ID 0x17 ??? even Jenny's briefing reuses
-// Jake's SCRIPT, even though the loaded ANI.DBD cells come from her
-// partner-specific entry 0x3b). Same pattern for book (0x18) / nancy
-// (0x19).
+// Briefing animations ??? `_DoInitClues @ 1a35:0411` always uses anim ID
+// 0x17 (game) / 0x18 (book) / 0x19 (nancy) regardless of partner; the
+// per-partner ANI.DBD cells come from a separate entry (e.g. 0x3b for
+// Jenny's briefing).
 static const uint8 kScript17[] = {
 	0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,
 	20,21,22,23,24,25,26,27,28,29
@@ -355,17 +305,10 @@ static const uint8 kScript19[] = {
 	1,2,3,4,5,6,7,8,9,10,11,12
 };
 
-// Site / NPC drop scripts (29be:22d4 entries 0x1a..0x36 minus the
-// short ones that fit in `kAnimScripts`). Many entries deliberately
-// repeat the same frame several times ??? that's the original's
-// "frame-hold" mechanism (the per-tick walk advances exactly one
-// entry, so K repeats hold the frame for K * `kFramePeriodMs` ???
-// K * 140 ms). Without these scripts our generic fallback cycles
-// through every animation cell at one entry per tick, which is the
-// "site animations run too fast" symptom.
-
-// 0x1a (29be:19a4) ??? count-up 0..7, long idle hold, repeat 1..7,
-// idle, mirror 7..0, idle (77 entries).
+// Site / NPC drop scripts (29be:22d4 entries 0x1a..0x36). Repeated
+// frames are the original's frame-hold mechanism (one entry per tick).
+
+// 0x1a (29be:19a4) ??? count-up 0..7, idle hold, repeat 1..7, idle, mirror 7..0, idle.
 static const uint8 kScript1a[] = {
 	0,1,2,3,4,5,6,7,
 	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
@@ -552,8 +495,9 @@ const AnimScriptLong kAnimScriptsLong[] = {
 
 // `_PatientSequence` and `_ImpatientSequence` are standalone script
 // pointers, not entries in `_AnimationSequences`. CD has the data but
-// never calls the switchers; floppy calls them from `_DoSiteLoop_Floppy`.
-// We intentionally enable the same switch for both builds.
+// never calls the switchers; floppy calls them from `_DoSiteLoop_Floppy`
+// (via `_Switch2Patient` / `_Switch2Impatient`). We intentionally enable
+// the same switch for both builds.
 static const uint8 kPatientSequence[]   = { 0,0,0,0,0,0,0,0,0,2 };
 static const uint8 kImpatientSequence[] = { 0,1,0,1,0,1,0,1,2,1 };
 
@@ -562,10 +506,6 @@ static const uint8 kImpatientSequence[] = { 0,1,0,1,0,1,0,1,2,1 };
 // behavior but makes the feature observable during normal testing.
 static const uint32 kImpatienceDelayMs = 60 * 1000;
 
-// Look up the script for `seqnum`. Returns the frame array + length,
-// or `(nullptr, 0)` if no script is known ??? caller falls back to
-// flipbook cycling so unknown anims still animate (just without idle
-// holds).
 struct AnimScriptRef {
 	const uint8 *frames;
 	uint16 len;
@@ -594,12 +534,11 @@ static AnimScriptRef findAnimScript(uint16 seqnum) {
 }
 
 // Original frame period from `_InitFrameCounter @ 1a35:01ae`:
-// `LastFrame = (cs_within_hour) + 0xe`, with `cs_within_hour` =
-// `((ti_min * 60) + ti_sec) * 100 + ti_hund` (Borland C `struct time`
-// memory order is min, hour, hund, sec). The `+ 0xe` is 14
-// centiseconds ??? ~140 ms per frame, matching `_CheckFrameRate @
-// 1a35:0204`. Earlier 100 ms ran the partner / hotspot animations
-// roughly 1.4?? faster than the original.
+//   LastFrame    = cs_within_hour + 0xe
+//   cs_within_hour = ((ti_min * 60) + ti_sec) * 100 + ti_hund
+// (Borland C `struct time` memory order is min, hour, hund, sec.)
+// `+ 0xe` is 14 centiseconds ??? ~140 ms per frame, matching
+// `_CheckFrameRate @ 1a35:0204`.
 static const uint kFramePeriodMs = 140;
 
 static uint frameFromScriptAtTick(const uint8 *frames, uint len,
@@ -612,17 +551,12 @@ static uint frameFromScriptAtTick(const uint8 *frames, uint len,
 }
 
 void auditPartnerAnims(EEMEngine *vm) {
-	// Cross-check every registered partner-subset script against the
-	// underlying ANI.DBD entry it references. If the script asks for
-	// a frame past the anim's actual frame count, the visible result
-	// is "missing frames" ??? that's the user-reported symptom we
-	// want to catch and fix here, not paper over with a clamp.
+	// Cross-check every script against the ANI.DBD entry it references;
+	// warn on out-of-range frame requests.
 	if (!vm)
 		return;
 	DBDArchive &ani = vm->getAni();
 
-	// Helper: audit one (id, frames, len) tuple ??? log a warning if
-	// the script asks for a frame the ANI.DBD entry doesn't have.
 	struct Walker {
 		static void check(DBDArchive &ani, uint16 id, const uint8 *frames, uint8 len) {
 			if (len == 0)
@@ -639,10 +573,8 @@ void auditPartnerAnims(EEMEngine *vm) {
 					maxRequested = frames[j];
 			if (maxRequested >= a.size()) {
 				warning("anim 0x%02x: script wants frame %u but ANI.DBD has "
-						"only %u ??? frames will be clamped (verify script "
-						"reading from `_AnimationSequences[0x%02x]` against "
-						"Ghidra)",
-						id, maxRequested, (uint)a.size(), id);
+						"only %u ??? frames will be clamped",
+						id, maxRequested, (uint)a.size());
 			} else {
 				debugC(2, kDebugSite,
 					   "anim 0x%02x: %u cells, script max=%u, len=%u",
@@ -658,20 +590,9 @@ void auditPartnerAnims(EEMEngine *vm) {
 		Walker::check(ani, kAnimScriptsLong[i].seqnum,
 					  kAnimScriptsLong[i].frames, kAnimScriptsLong[i].len);
 
-	// Per-frame anchor-offset audit. The original `_UpdateAnimations
-	// @ 172b:09c1` blits each frame at `(anchor_x - frame.miscflags,
-	// anchor_y - frame.rowoff)`. Our `blitMaskedSurface` ignores
-	// those offsets ??? it treats the WaitAnims (anchor_x, anchor_y)
-	// as the top-left. That's fine when every frame has
-	// `miscflags == 0 && rowoff == 0`; if any frame has a non-zero
-	// anchor, the partner sprite jumps between cells. Log the
-	// offending IDs so we know whether to plumb anchors through
-	// `partnerFrameAtTick` callers.
-	// Audit covers every animID we register a script for ??? the
-	// partner-subset (0x00..0x16, used by the wait anims, kd-clue
-	// reactions, BigMap, Notebook, Gallery, CaseSelection greeter)
-	// AND the briefing-subset (0x17..0x19, the
-	// game/book/nancy anims driven by `_DoInitClues @ 1a35:0411`).
+	// Per-frame anchor-offset audit (`_UpdateAnimations @ 172b:09c1`
+	// applies (anchor_x - miscflags, anchor_y - rowoff)). Log non-zero
+	// anchors so we know which anims need `blitAnimFrameAnchored`.
 	const uint16 partnerIds[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05,
 								   0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c,
 								   0x0d, 0x0f, 0x10, 0x11, 0x12, 0x13,
@@ -701,10 +622,7 @@ void auditPartnerAnims(EEMEngine *vm) {
 			}
 		}
 		if (anyAnchor) {
-			// `_UpdateAnimations @ 172b:09c1` reads these as signed
-			// 16-bit values via `puVar5[3]/[4]`, so log them with
-			// the sign preserved ??? earlier the log printed unsigned
-			// 65534 instead of -2.
+			// Signed int16 (puVar5[3]/[4] in `_UpdateAnimations`).
 			debugC(1, kDebugSite,
 				   "anim 0x%02x: per-frame anchor (rowoff [%d..%d], "
 				   "miscflags [%d..%d]) ??? handled by "
@@ -714,31 +632,27 @@ void auditPartnerAnims(EEMEngine *vm) {
 	}
 }
 
-// Pick the frame index to render at `tickMs` for the looping
-// animation `seqnum` whose underlying ANI.DBD entry has `numFrames`
-// frames. Mirrors the looping path of `_UpdateAnimations`: walk the
-// script one entry per ~100 ms `_CheckFrameRate` tick, wrap on the
-// 0x80 terminator. Exposed (non-static) so the BigMap, CaseSelection
-// greeter, Notebook, and Gallery render paths in `ui.cpp` can use the
-// same cadence ??? without this, every off-site partner rendering
-// flipbook-cycles ALL cells of the ANI entry (no idle holds, no
-// timing variations) which is the user-reported "constantly looping"
-// symptom.
+// Looping path of `_UpdateAnimations`: walk the script one entry per
+// `_CheckFrameRate` tick (`kFramePeriodMs` ~= 140 ms), wrap on 0x80.
+// If `seqnum` has no registered script, falls back to flipbook
+// (`tick % numFrames`) so unknown anims still move. The script can in
+// theory request a frame past the asset's actual cell count (misencoded
+// script) ??? `frameFromScriptAtTick` clamps to `numFrames - 1` so the
+// caller doesn't read past `anim[]`. Exported (non-static) so BigMap,
+// CaseSelection greeter, Notebook, and Gallery render paths in
+// `ui.cpp` use the same cadence.
 uint partnerFrameAtTick(uint16 seqnum, uint numFrames, uint32 tickMs) {
 	const AnimScriptRef s = findAnimScript(seqnum);
-	// The script can in theory request a frame that's outside the
-	// animation's actual frame count (a misencoded script). Clamp so
-	// we don't read past `anim[]` in the caller.
 	return frameFromScriptAtTick(s.frames, s.len, numFrames, tickMs);
 }
 
-// Generic "play `unfold` once, then loop `waitSeq` forever" walker.
-// Mirrors the original's slot-script-swap idiom: the entrance script
-// runs to its 0x80 terminator, then the slot's script pointer is
-// rewritten to a looping wait sequence (e.g. `_BigMapWaitSeq @
-// 29be:1574`, `_SmallMapWaitSeq @ 29be:1548`). `partnerFrameAtTick`
-// can't model that swap on its own (it always wraps on the same
-// script), hence this helper.
+// Play `unfold` once, then loop `waitSeq` forever. Mirrors the
+// original's slot-script-swap idiom: the entrance script runs to its
+// 0x80 terminator, then the slot's script pointer is rewritten to a
+// looping wait sequence (e.g. `_BigMapWaitSeq @ 29be:1574`,
+// `_SmallMapWaitSeq @ 29be:1548`). `partnerFrameAtTick` can't model
+// that swap on its own (it always wraps on the same script), hence
+// this helper.
 static uint oneShotThenLoopFrameAtTick(const uint8 *unfold, uint unfoldLen,
 									   const uint8 *waitSeq, uint waitSeqLen,
 									   uint numFrames, uint32 elapsedMs) {
@@ -750,9 +664,10 @@ static uint oneShotThenLoopFrameAtTick(const uint8 *unfold, uint unfoldLen,
 }
 
 uint bigMapPartnerFrameAtTick(uint numFrames, uint32 elapsedMs) {
-	// Script 0x14 @ 29be:196a (count-up 0..8, 0x80) ??? on terminator,
-	// `_DoBigMap` swaps to `_BigMapWaitSeq` @ 29be:1574
-	// (9,9,9,9,10,9,9,9,9, 0x80) ??? open-map hold with a fidget.
+	// Slot starts on script 0x14 (count-up 0..8 @ 29be:196a). On 0x80
+	// terminator, `_DoBigMap` rewrites the slot's script pointer to
+	// `_BigMapWaitSeq @ 29be:1574` = (9,9,9,9,10,9,9,9,9, 0x80) ??? the
+	// open-map hold with a fidget.
 	static const uint8 kUnfold[]  = { 0, 1, 2, 3, 4, 5, 6, 7, 8 };
 	static const uint8 kWaitSeq[] = { 9, 9, 9, 9, 10, 9, 9, 9, 9 };
 	return oneShotThenLoopFrameAtTick(kUnfold, ARRAYSIZE(kUnfold),
@@ -761,10 +676,10 @@ uint bigMapPartnerFrameAtTick(uint numFrames, uint32 elapsedMs) {
 }
 
 uint bigMapDetailPartnerFrameAtTick(uint numFrames, uint32 elapsedMs) {
-	// Script 0x13 @ 29be:1992 (count-up 0..7, 0x80) ??? on terminator,
-	// `_DoMapScreen @ 20fe:1390` swaps to `_SmallMapWaitSeq` @ 29be:1548
-	// (18 entries: hold cell 7 with a single cell-10 fidget) ??? fidget
-	// every ~1.8 s.
+	// Slot starts on script 0x13 (count-up 0..7 @ 29be:1992). On 0x80
+	// terminator, `_DoMapScreen @ 20fe:1390` rewrites the slot pointer
+	// (`MOV [BX+0x789f],0x1548`) to `_SmallMapWaitSeq @ 29be:1548` =
+	// 18 entries holding cell 7 with a single cell-10 fidget (~1.8 s).
 	static const uint8 kUnfold[]  = { 0, 1, 2, 3, 4, 5, 6, 7 };
 	static const uint8 kWaitSeq[] = {
 		7, 7, 7, 10, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7
@@ -785,21 +700,16 @@ void SiteScreen::enter(uint siteNum, bool resetPartnerMood) {
 		return;
 	}
 
-	// Reset the wait-anim phase so the partner starts fresh from
-	// script[0] when entering. Mirrors `_DoSiteLoop @ 168d:0436`
-	// where `_NewAnimation` sets the new slot's frame index to
-	// 0xffff (= -1, becomes 0 on the first `_UpdateAnimations`
-	// tick).
+	// `_DoSiteLoop @ 168d:0436`: `_NewAnimation` sets the new slot's
+	// frame index to 0xffff (-1) ??? starts at script[0].
 	_waitPhaseAnchor = g_system->getMillis();
 	if (resetPartnerMood) {
 		_partnerWaitMood = kPartnerWaitDefault;
 		initImpatienceCounter();
 	}
 
-	// Capture whether this is the first time the player enters this
-	// site BEFORE we mark it visited ??? `_DoSiteLoop @ 168d:03f4`
-	// uses the same check to decide whether to play the arrival
-	// dialog: `if (_VisitedSite[_SiteNumber] == 0) _DisplayClue(...)`.
+	// `_DoSiteLoop @ 168d:03f4`:
+	//   if (_VisitedSite[_SiteNumber] == 0) _DisplayClue(...);
 	const bool firstVisit = (siteNum < Mystery::kVisitedSiteCap)
 							 && (_mystery->_visitedSite[siteNum] == 0);
 
@@ -809,22 +719,14 @@ void SiteScreen::enter(uint siteNum, bool resetPartnerMood) {
 
 	const bool playArrival = _vm->shouldPlaySiteArrival(siteNum);
 
-	// `_DoTravel @ 168d:02da` calls `_StartTravelMusic` after the
-	// destination is set. We do the same here so the music swaps as
-	// the player moves between sites.
+	// `_DoTravel @ 168d:02da` calls `_StartTravelMusic`.
 	if (playArrival)
 		_vm->startTravelMusic();
 
-	// Palette: original `_BuildBackground` calls `GetPalette(sitenum + 1)`
-	// where sitenum is the global SITES.DBD index (= the per-mystery
-	// `sitepic` field), not the per-mystery site index.
-	//
-	// Floppy site_data layout differs (per `_DoSiteLoop_Floppy @
-	// 1652:03f4`): the FIRST u16 is the *offset* to a drops sub-struct
-	// whose byte 0 is the small SITES.DBD picID. Without this branch
-	// we'd dereference the offset directly and call setSitePalette
-	// with a value in the thousands ??? the user reported `index 9275
-	// out of range` for site 2.
+	// `_BuildBackground` calls `GetPalette(sitenum + 1)` ??? sitenum is the
+	// global SITES.DBD index (per-mystery `sitepic` field).
+	// Floppy (`_DoSiteLoop_Floppy @ 1652:03f4`): first u16 of site_data is
+	// an offset to a drops sub-struct whose byte 0 is the SITES.DBD picID.
 	const byte *sd = _mystery->siteData(siteNum);
 	uint16 sitepic = 0;
 	if (sd) {
@@ -839,18 +741,11 @@ void SiteScreen::enter(uint siteNum, bool resetPartnerMood) {
 	}
 	_vm->setSitePaletteForSite(sitepic);
 
-	// SITEPALS ships with palette indices 0xF9..0xFE all set to a
-	// uniform yellow (`3F 3E 00` = R=63 G=62 B=0 for every site palette
-	// in the file), so the original `_DrawRect`'s cycling colour
-	// pattern + `_ColorCycle(0xF9, 0xFE)` produced a uniformly-coloured
-	// outline with no visible movement ??? the "marching ants" pattern
-	// was a placeholder that never lit up. Override those entries with
-	// a 6-step yellow ramp here so the existing per-tick rotation
-	// (`applyColorCycles ??? cyclePaletteRange(0xF9, 0xFE)`) creates a
-	// pulsing glow on unsearched hotspots. Done after `setSitePalette`
-	// so the per-site palette load doesn't clobber the ramp.
+	// SITEPALS ships palette 0xF9..0xFE as uniform yellow (3F 3E 00),
+	// so original marching-ants was a placeholder. Override with a
+	// 6-step yellow ramp so `cyclePaletteRange(0xF9, 0xFE)` produces a
+	// visible pulse on unsearched hotspots.
 	{
-		// 6-step yellow glow: dark ??? bright ??? dim ??? ...
 		static const byte kAntsGlow[6 * 3] = {
 			0x40, 0x40, 0x00, // F9 ??? dim
 			0x80, 0x80, 0x00, // FA
@@ -864,14 +759,11 @@ void SiteScreen::enter(uint siteNum, bool resetPartnerMood) {
 
 	renderBackground(siteNum);
 
-	// `_DoSiteLoop @ 168d:03f4` plays `_EnterSiteAnim` whenever
-	// `_LastSite != _SiteNumber`. Keep our guard on the engine rather
-	// than on SiteScreen, because PDA/gallery returns recreate
-	// SiteScreen and must not replay the arrival.
+	// `_DoSiteLoop @ 168d:03f4` plays `_EnterSiteAnim` when
+	// `_LastSite != _SiteNumber`. Guard lives on the engine so PDA/gallery
+	// re-entry doesn't replay arrival.
 	if (playArrival) {
-		// `_EnterSiteAnim` snapshots the current screen, so populate
-		// that temporary background with the same site layers that
-		// should already be visible behind the arriving partner.
+		// `_EnterSiteAnim` snapshots the screen, so populate the BG first.
 		if (_vm->isFloppy())
 			renderFloppyDrops(siteNum);
 		else
@@ -885,34 +777,21 @@ void SiteScreen::enter(uint siteNum, bool resetPartnerMood) {
 			else
 				_vm->waitForMusicDone();
 		}
-		// Re-paint the BG; the normal snapshot below should contain
-		// only the static layers, while animated NPCs are redrawn per
-		// tick by the frame pump.
 		renderBackground(siteNum);
 	}
 
-	// Static drops (Loop 2 from `_DoSiteLoop`) ??? no animation, baked
-	// into the BG snapshot the run() pump uses to restore. Floppy
-	// stores them in a different shape (drops sub-struct after the
-	// site_data offset), so dispatch on `isFloppy()`.
+	// Loop 2 from `_DoSiteLoop`: static drops (baked into snapshot).
 	if (_vm->isFloppy())
 		renderFloppyDrops(siteNum);
 	else
 		renderStaticDrops(siteNum);
 
-	// Snapshot the static layers so per-tick animation re-blits don't
-	// have to re-load PIC 0x43, the SITES.DBD scene, or each
-	// `_AddDrop` PIC every frame.
 	captureBgSnapshot();
 	_snapshotSite = (int)siteNum;
 
-	// Cache ColorCycle palette ranges for this site so the per-tick
-	// frame pump can rotate them. Mirrors the init scan at the top of
-	// `_DoSiteLoop @ 168d:03f4`.
 	scanColorCycles(siteNum);
 
-	// Animated NPCs (Loop 1) and the persistent partner sit on top of
-	// the snapshot. Initial frame goes at tickMs=now.
+	// Loop 1 (animated NPCs) + partner on top of the snapshot.
 	const uint32 now = g_system->getMillis();
 	renderAnimatedDrops(siteNum, now);
 	renderPartner(siteNum, now);
@@ -920,15 +799,12 @@ void SiteScreen::enter(uint siteNum, bool resetPartnerMood) {
 	renderHotspots(siteNum);
 	g_system->updateScreen();
 
-	// First-visit dialog. `_DoSiteLoop @ 168d:03f4` does:
+	// First-visit dialog. `_DoSiteLoop @ 168d:03f4`:
 	//   if (_VisitedSite[_SiteNumber] == 0) {
 	//       _DisplayClue(_Mystery + SiteIndex[siteNum*6 + 2], 1);
 	//       _VisitedSite[_SiteNumber] = 1;
 	//   }
-	// `SiteIndex[+2..+3]` is the byte offset (within the mystery
-	// buffer) of a ClueBlock that holds the partner's arrival
-	// dialogue. We've kept the +2 field undocumented up to now ???
-	// this confirms it's the entry-clue offset.
+	// SiteIndex[+2..+3] = byte offset of entry-clue ClueBlock.
 	if (firstVisit) {
 		const byte *idx = _mystery->siteIndexEntry(siteNum);
 		if (idx) {
@@ -936,10 +812,7 @@ void SiteScreen::enter(uint siteNum, bool resetPartnerMood) {
 			if (clueOff != 0xFFFF) {
 				const byte *clueBlock = _mystery->blobAt(clueOff);
 				if (clueBlock) {
-					// See onHotspotClicked ??? supply a partner-less BG
-					// so KD-anim playback (e.g. the partner's arrival
-					// camera gesture) doesn't ghost over the resting
-					// idle frame.
+					// Partner-less BG so KD-anim doesn't ghost over the idle.
 					_vm->setPartnerEraseBg(&_bgSnapshot);
 					_vm->displayClue(clueBlock);
 					_vm->setPartnerEraseBg(nullptr);
@@ -948,9 +821,7 @@ void SiteScreen::enter(uint siteNum, bool resetPartnerMood) {
 		}
 		if (siteNum < Mystery::kVisitedSiteCap)
 			_mystery->_visitedSite[siteNum] = 1;
-		// The dialog overlay will have left the screen with portrait /
-		// balloon residues; refresh the site so the player returns to
-		// a clean state. Re-build the snapshot too.
+		// Dialog overlay leaves portrait/balloon residue; refresh & re-snapshot.
 		renderBackground(siteNum);
 		if (_vm->isFloppy())
 			renderFloppyDrops(siteNum);
@@ -981,10 +852,8 @@ bool SiteScreen::checkImpatienceCounter() {
 }
 
 void SiteScreen::notePartnerActivity() {
-	// Mirrors `_Switch2Patient(WaitHandle)` plus
-	// `_InitImpatientCounter()` in the floppy site loop, but only for
-	// deliberate actions in the port: clicks and key presses. Passive
-	// mouse movement should not make impatience impossible to see.
+	// `_Switch2Patient(WaitHandle)` + `_InitImpatientCounter()` from
+	// the floppy site loop. Triggered on clicks/keys only (not mouse-move).
 	const bool wasImpatient = _partnerWaitMood == kPartnerWaitImpatient;
 	_partnerWaitMood = kPartnerWaitPatient;
 	initImpatienceCounter();
@@ -996,11 +865,7 @@ void SiteScreen::run() {
 	if (!_mystery || !_mystery->isLoaded())
 		return;
 
-	// The caller (run() in eem.cpp) is responsible for bringing the
-	// player into a site via the map first, so `_siteNumber` is
-	// already set to the destination they picked. Resuming a save
-	// also restores `_siteNumber`. Start there instead of forcing
-	// site 0 each time.
+	// Caller seeds `_siteNumber` (via map pick or save restore).
 	uint cur = _mystery->_siteNumber;
 	if (cur >= _mystery->numSites())
 		cur = 0;
@@ -1023,27 +888,14 @@ void SiteScreen::run() {
 				break;
 
 			case Common::EVENT_LBUTTONDOWN: {
-				// On-screen UI buttons. `_DoSiteLoop @ 168d:03f4` calls
+				// `_DoSiteLoop @ 168d:03f4` calls
 				//   _FindButton(&SiteButtons, 2, MouseX, MouseY)
-				// where `SiteButtons` is two 8-byte rectangles at
-				// 29be:0x274 (verified via 168d:0729-0848):
-				//   Button 0: (35, 111) - (56, 136)  -> notebook
-				//                                       (`_NextScreen = 4`)
-				//   Button 1: (7, 177)  - (57, 200)  -> map
-				//                                       (CD `_NextScreen = 1`,
-				//                                       floppy = 2)
-				// Test the buttons before falling through to hotspots so
-				// a click on the PDA / partner foot doesn't accidentally
-				// trigger a hotspot underneath.
-				// Partner head is a port-only enhancement so the player
-				// can click the host sprite for a hint, mirroring the
-				// PDA's rect-3 / gallery's rect-3 behaviour. The
-				// original site loop's `_FindButton(&SiteButtons, 2,
-				// ...)` only checks notebook + map, but the same
-				// partner-click -> `_KDHelp` shortcut is wired in
-				// `_HandleNoteButton[3]` (0x0403) and
-				// `_HandleGalleryButton[3]` (0x061e). Rect matches
-				// the PDA / gallery `kBtnPartner` (5, 80, 44, 110).
+				// `SiteButtons` @ 29be:0274 ??? two 8-byte rects:
+				//   Button 0: (35,111)-(56,136)  notebook (_NextScreen=4)
+				//   Button 1: (7,177)-(57,200)   map (CD=1, floppy=2)
+				// Partner-head click is port-only: `_KDHelp` shortcut
+				// mirroring `_HandleNoteButton[3]` (0x0403) /
+				// `_HandleGalleryButton[3]` (0x061e). Rect = (5,80,44,110).
 				if (kSitePdaRect.contains(event.mouse.x, event.mouse.y)) {
 					notePartnerActivity();
 					_vm->setHotspotMouseCursor(false);
@@ -1054,7 +906,7 @@ void SiteScreen::run() {
 				if (kSitePartnerFootMapRect.contains(event.mouse.x, event.mouse.y)) {
 					notePartnerActivity();
 					_vm->setHotspotMouseCursor(false);
-					// CD writes `_NextScreen = 1`; floppy writes 2.
+					// CD: _NextScreen=1, floppy=2.
 					_vm->setNextScreen(_vm->isFloppy() ? kScreenMapAlt
 													   : kScreenMap);
 					_vm->stopMusic();
@@ -1065,10 +917,7 @@ void SiteScreen::run() {
 					_vm->doHelp();
 					notePartnerActivity();
 					enter(cur, false);
-					// Re-evaluate cursor against the CURRENT pointer
-					// position, not the click that opened the help.
-					// The player may have moved off the partner-head
-					// area during the dialog.
+					// Re-evaluate cursor against CURRENT pointer position.
 					mouse = g_system->getEventManager()->getMousePos();
 					updateHotspotCursor(cur, mouse.x, mouse.y);
 					break;
@@ -1077,13 +926,9 @@ void SiteScreen::run() {
 				if (idx >= 0) {
 					_vm->setHotspotMouseCursor(false);
 					onHotspotClicked(cur, (uint)idx);
-					// Restore the site BG after the clue overlay.
 					notePartnerActivity();
 					enter(cur, false);
-					// Use CURRENT pointer position ??? the click pos is
-					// still inside the hotspot rect, so reusing it
-					// would leave the "clickable" cursor stuck after
-					// the conversation even if the player moved off.
+					// Use CURRENT pointer position (click pos still in rect).
 					mouse = g_system->getEventManager()->getMousePos();
 					updateHotspotCursor(cur, mouse.x, mouse.y);
 				} else {
@@ -1094,12 +939,8 @@ void SiteScreen::run() {
 
 			case Common::EVENT_KEYDOWN:
 				notePartnerActivity();
-				// `_DoSiteLoop @ 168d:07e1` originally routed ESC
-				// through `_ESCHit` -> "Are you sure?" -> MAP. The
-				// site has visible MAP / SETUP / etc. controls along
-				// the top bar, so ESC now opens the ScummVM in-game
-				// menu (save / load / quit) instead of the back-nav
-				// shortcut.
+				// `_DoSiteLoop @ 168d:07e1` routes ESC via `_ESCHit` ???
+				// "Are you sure?" ??? MAP. Port reroutes to ScummVM menu.
 				if (event.kbd.keycode == Common::KEYCODE_ESCAPE) {
 					_vm->setHotspotMouseCursor(false);
 					_vm->openMainMenuDialog();
@@ -1114,21 +955,15 @@ void SiteScreen::run() {
 			}
 		}
 
-		// Hotspot side effects can invalidate the active mystery; exit
-		// immediately rather than tick another frame against stale BG
-		// snapshots / hotspot tables.
+		// Hotspot side effects can invalidate the mystery; exit before
+		// ticking another frame against stale snapshots.
 		if (!_mystery || !_mystery->isLoaded()) {
 			_vm->stopMusic();
 			return;
 		}
 
-		// Per-tick frame pump (mirrors `_CheckFrameRate` +
-		// `_UpdateAnimations` at the top of `_DoSiteLoop`'s main loop).
-		// Restore the static BG snapshot, redraw animated NPCs +
-		// partner at the current frame, then re-render hotspots on
-		// top. The original ticks at 14 cs (~140 ms, see
-		// `kFramePeriodMs` above) ??? we matched 100 ms before, which
-		// ran site animations ~1.4?? too fast.
+		// Per-tick frame pump: `_CheckFrameRate` + `_UpdateAnimations`
+		// at the top of `_DoSiteLoop`'s main loop. 14 cs (~140 ms).
 		const uint32 now = g_system->getMillis();
 		if (_snapshotSite == (int)cur &&
 			now - _lastTickMs >= kFramePeriodMs) {
@@ -1140,9 +975,7 @@ void SiteScreen::run() {
 			renderAnimatedDrops(cur, now);
 			renderPartner(cur, now);
 			renderHotspots(cur);
-			// Per-tick palette rotation for ColorCycle entries +
-			// hotspot marching ants. Matches `_ColorCycle(start, end)`
-			// calls inside `_DoSiteLoop @ 168d:03f4`'s main loop.
+			// `_ColorCycle(start, end)` per tick (`_DoSiteLoop @ 168d:03f4`).
 			applyColorCycles();
 			_lastTickMs = now;
 		}
@@ -1152,17 +985,12 @@ void SiteScreen::run() {
 }
 
 bool SiteScreen::enterSiteAnim() {
-	// Mirrors `_EnterSiteAnim @ 1000:9b21`. Two phases, both partner
-	// dependent:
-	//   Phase 1 ??? skateboard scroll: anim 6 (Jake) / 0xe (Jenny). Sprite
-	//             starts at (320 - sprite_w, 199 - sprite_h) and slides
-	//             left until off-screen.
-	//   Phase 2 ??? KD slide-in: anim 7 (Jake) / 0xf (Jenny). Sprite enters
-	//             from x = -sprite_w at y = 0x8b (Jake) / 0x8e (Jenny)
-	//             and slides until x = 0.
-	// Original cycles frames every `_MoveSkateBoardPixels` worth of
-	// motion (a runtime-calibrated speed value); we use a fixed 4 px
-	// per tick which feels close to the DOS pacing.
+	// `_EnterSiteAnim @ 1000:9b21`. Two phases (partner-dependent):
+	//   Phase 1 ??? skateboard scroll: anim 6 (Jake) / 0xe (Jenny).
+	//             Slides from (320-w, 199-h) leftward off-screen.
+	//   Phase 2 ??? KD slide-in: anim 7 (Jake) / 0xf (Jenny).
+	//             Slides from x=-w at y=0x8b/0x8e until x=0.
+	// Frame rate uses `_MoveSkateBoardPixels` (runtime); we use 4 px/tick.
 	if (!_vm || !_mystery)
 		return false;
 	const uint8 partner = _vm->getPartnerIndex();
@@ -1170,7 +998,6 @@ bool SiteScreen::enterSiteAnim() {
 	const uint kKDAni    = (partner == 0) ? 7  : 0xf;
 	const int  kKDY      = (partner == 0) ? 0x8b : 0x8e;
 
-	// Snapshot the current screen so we can restore between frames.
 	Graphics::Surface *screen = g_system->lockScreen();
 	if (!screen)
 		return false;
@@ -1179,11 +1006,10 @@ bool SiteScreen::enterSiteAnim() {
 	bg.simpleBlitFrom(*screen);
 	g_system->unlockScreen();
 
-	// Phase 1 ??? skateboard scroll. `_GetAnimation(6 | 0xe)`.
+	// Phase 1 ??? skateboard scroll.
 	Animation skate;
 	if (_vm->getAni().loadAnimation(kSkateAni, skate) && !skate.empty()) {
-		// `iVar4 = 199 - sprite_h`, `uVar5 = 320 - sprite_w` from the
-		// original; sprite_h/w come from the FIRST frame.
+		// `iVar4 = 199 - sprite_h`, `uVar5 = 320 - sprite_w` (frame 0).
 		const int spriteH = skate[0].surface.h;
 		const int spriteW = skate[0].surface.w;
 		int x = (320 - spriteW) & ~3;            // 4-px aligned (mode-X)
@@ -1221,20 +1047,11 @@ bool SiteScreen::enterSiteAnim() {
 		}
 	}
 
-	// Phase 2 ??? KD slide-in. From `_EnterSiteAnim` each frame is blitted
-	// at its OWN anchor offsets (the sprite "walks in" because the
-	// frame-by-frame anchors decrease as the animation progresses):
-	//   destX = -frame.miscflags    (on-disk byte 8 = anchor X)
-	//   destY = kKDY - frame.rowoff (on-disk byte 6 = anchor Y)
-	// Both anchors are SIGNED int16 (mirrors `blitAnimFrameAnchored` /
-	// `_UpdateAnimations @ 172b:09c1`). The original's `-pPVar8->width`
-	// negation wraps in 16-bit on DOS, so a frame with `miscflags = -2`
-	// (0xFFFE) lands at destX = +2 in the original. Without the int16
-	// re-cast our 32-bit negation produces destX = -65534 and the
-	// second-to-last frame (which has a non-zero anchor in anim 7/0xf)
-	// clips entirely off-screen, leaving a one-tick partner-less gap.
-	// Each frame waits one `_CheckFrameRate` tick ??? we use 80 ms which
-	// matches the original's ~12 FPS pacing.
+	// Phase 2 ??? KD slide-in. `_EnterSiteAnim` per-frame:
+	//   destX = -frame.miscflags    (signed int16; byte 8 = anchor X)
+	//   destY = kKDY - frame.rowoff (signed int16; byte 6 = anchor Y)
+	// 16-bit negation: miscflags=-2 (0xFFFE) ??? destX=+2.
+	// ~80 ms per frame (~12 FPS, one `_CheckFrameRate` tick).
 	Animation kd;
 	if (_vm->getAni().loadAnimation(kKDAni, kd) && !kd.empty()) {
 		for (uint frameIdx = 0;
@@ -1268,13 +1085,9 @@ bool SiteScreen::enterSiteAnim() {
 
 void SiteScreen::renderStaticDrops(uint siteNum) {
 	// Loop 2 from `_DoSiteLoop @ 168d:03f4`:
-	//   bound: siteData[+0x4]   (verified at 168d:05c0:
-	//          `MOV ES:[BX+0x4], DI; CMP ES:[BX+0x4], DI`)
-	//   per entry at siteData[+0xc + i*6]: {picId, x, y}
-	//   each ??? `_AddDrop(picId, x, y)` (`_AddDrop @ 172b:1a77`):
-	//   loads PIC `picId-1` from PICS.DBD and blits with miscflags
-	//   high-byte as the transparent colour. These NEVER cycle, so
-	//   they belong to the BG snapshot.
+	//   count @ siteData[+0x4], entries @ siteData[+0xc + i*6]: {picId, x, y}.
+	//   `_AddDrop @ 172b:1a77` loads PIC picId-1 from PICS.DBD,
+	//   blits with miscflags high-byte as transparency.
 	if (!_mystery)
 		return;
 	const byte *site = _mystery->siteData(siteNum);
@@ -1305,19 +1118,11 @@ void SiteScreen::renderStaticDrops(uint siteNum) {
 }
 
 void SiteScreen::renderFloppyDrops(uint siteNum) {
-	// Floppy drops live inside the drops sub-struct pointed to by
-	// `*site_data` (u16 offset). Verified from the call site in
-	// `_DoSiteLoop_Floppy @ 1652:0418`:
-	//   FUN_16e2_18eb(
-	//     *(u16 *)(local_1a + i*5),          // arg1 = u16 picID @ +0..1
-	//     *(u16 *)(local_1a + i*5 + 2),      // arg2 = u16 X     @ +2..3
-	//     local_1a[i*5 + 4]                  // arg3 = u8  Y     @ +4
-	//   );
-	// Inside `FUN_16e2_18eb @ 16e2:18eb`, `arg1 - 1` indexes the
-	// PICS.DBX table at `2608:4537` (loaded from `PICS.DBX` by
-	// `FUN_16e2_0149 @ 16e2:0149`); `arg2/arg3` become destX/destY.
-	// drops_struct[0] = BG picID (rendered separately by
-	// `renderBackground`), drops_struct[1] = drop count.
+	// Floppy drops: drops sub-struct pointed to by `*site_data` u16 offset.
+	// `_DoSiteLoop_Floppy @ 1652:0418` ??? `FUN_16e2_18eb @ 16e2:18eb`:
+	//   entry stride 5: u16 picID @ +0, u16 X @ +2, u8 Y @ +4.
+	//   picID-1 indexes PICS.DBX table @ 2608:4537.
+	// drops_struct[0] = BG picID (separate), drops_struct[1] = drop count.
 	if (!_mystery)
 		return;
 	const byte *site = _mystery->siteData(siteNum);
@@ -1340,9 +1145,7 @@ void SiteScreen::renderFloppyDrops(uint siteNum) {
 		const int16  y     = (int16)e[4];
 		if (picID == 0)
 			continue;
-		// `getPicture(num)` already does `loadEntry(num - 1)` (see
-		// `resource.h:100`), matching the `picID - 1` index the
-		// original passes to PICS.DBD.
+		// `getPicture(num)` does `loadEntry(num - 1)` (resource.h:100).
 		Picture pic;
 		if (!_vm->getPics().getPicture((uint)picID, pic))
 			continue;
@@ -1353,29 +1156,19 @@ void SiteScreen::renderFloppyDrops(uint siteNum) {
 
 void SiteScreen::renderAnimatedDrops(uint siteNum, uint32 tickMs) {
 	// Loop 1 from `_DoSiteLoop @ 168d:03f4`:
-	//   bound: siteData[+0xa]
-	//   per entry at siteData[+0x48 + i*6]: {animId, x, y}
-	//   animId == -1 ??? `_ColorCycle(x, y)` palette range (handled
-	//                  in the run() loop's frame pump as palette
-	//                  rotation).
-	//   else ??? `_GetAnimation(animId)` + `_NewAnimation` then
-	//          `_UpdateAnimations @ 172b:09c1` walks a sequence
-	//          script (entries are frame indices; 0x80 = end-of-loop,
-	//          0x81 = jump command). We don't have the sequence-script
-	//          structure decoded yet, so for now we cycle through the
-	//          raw animation frames in order using a global tick.
+	//   count @ siteData[+0xa], entries @ siteData[+0x48 + i*6]: {animId, x, y}.
+	//   animId == -1 ??? `_ColorCycle(x, y)` (palette rotation, separate path).
+	//   else ??? `_GetAnimation` + `_NewAnimation` + `_UpdateAnimations @ 172b:09c1`
+	//          walks a sequence script (0x80=loop, 0x81=jump).
 	if (!_mystery)
 		return;
 
 	if (_vm && _vm->isFloppy()) {
-		// Floppy extra site anims live in `ANI.BIN`, not in the
-		// mystery's site_data. `_ReadMystery_Floppy` asks
-		// `_GetSiteAnimData_Floppy(mystery)` for the case block, then
-		// walks one entry per site:
+		// Floppy site anims live in ANI.BIN (per-case block from
+		// `_GetSiteAnimData_Floppy`). Per-site:
 		//   u8 cycleCount, cycleCount ?? {u8 start, u8 end},
 		//   u8 animCount, animCount ?? {u8 animId, u16 x, u8 y}.
-		// `_DoSiteLoop_Floppy` registers at most four of these in local
-		// animation slots, so cap the draw loop the same way.
+		// `_DoSiteLoop_Floppy` caps at 4 slots.
 		const byte *siteAnim = _mystery->floppySiteAnimData(siteNum);
 		if (!siteAnim)
 			return;
@@ -1432,9 +1225,6 @@ void SiteScreen::renderAnimatedDrops(uint siteNum, uint32 tickMs) {
 			continue;
 		const uint frameIdx = partnerFrameAtTick((uint16)animId,
 												  (uint)anim.size(), tickMs);
-		// Animated drops go through `_NewAnimation` in the original,
-		// so `_UpdateAnimations` applies per-frame anchor offsets ???
-		// route through the anchored blitter.
 		blitAnimFrameAnchored(screen, anim[frameIdx], x, y);
 	}
 
@@ -1442,14 +1232,8 @@ void SiteScreen::renderAnimatedDrops(uint siteNum, uint32 tickMs) {
 }
 
 void SiteScreen::scanColorCycles(uint siteNum) {
-	// `_DoSiteLoop @ 168d:03f4` walks Loop 1 entries (siteData[+0xa]
-	// count, 6-byte entries at siteData[+0x48]) and stores each entry
-	// with `animId == -1` into a 5-slot table:
-	//   start palette idx = entry +2
-	//   end   palette idx = entry +4
-	// We mirror the layout exactly. Up to 5 entries are tracked (the
-	// original's `[unaff_BP + -0x12]` and `[unaff_BP + -0x1c]` arrays
-	// are 5 ?? u16 each).
+	// `_DoSiteLoop @ 168d:03f4`: Loop 1 entries with animId==-1 are
+	// ColorCycle palette ranges (start @ +2, end @ +4). Max 5 slots.
 	_colorCycles.clear();
 	if (!_mystery)
 		return;
@@ -1488,16 +1272,11 @@ void SiteScreen::scanColorCycles(uint siteNum) {
 }
 
 void SiteScreen::applyColorCycles() {
-	// `_ColorCycle @ 172b:2015` rotates `_fpal[start..end]` by one
-	// palette slot ??? saves [start], shifts [start..end-1] = [start+1..
-	// end], restores saved at [end] ??? then re-uploads via `_Set_Palette`.
-	// We do the same against ScummVM's palette manager. Always rotate
-	// 0xf9..0xfe for hotspot marching ants (the `_ColorCycle(0xf9,
-	// 0xfe)` call at the bottom of `_DoSiteLoop`'s main loop).
+	// `_ColorCycle @ 172b:2015` per range. Always rotate 0xF9..0xFE
+	// (hotspot marching ants ??? `_ColorCycle(0xf9, 0xfe)` in `_DoSiteLoop`).
 	for (uint i = 0; i < _colorCycles.size(); i++) {
 		cyclePaletteRange(_colorCycles[i].start, _colorCycles[i].end);
 	}
-	// Hotspot marching ants ??? always cycled.
 	cyclePaletteRange(0xF9, 0xFE);
 }
 
@@ -1521,18 +1300,13 @@ void SiteScreen::restoreBgSnapshot() {
 
 void SiteScreen::renderPartner(uint siteNum, uint32 tickMs) {
 	// `_DoSiteLoop @ 168d:03f4` reads `siteData[+8]` as the speaker
-	// table index, then for each (speaker ?? partner) loads
-	//   anim  = WaitAnims[speakerIdx].anim[partner]
-	//   x     = WaitAnims[speakerIdx].x[partner]
-	//   y     = WaitAnims[speakerIdx].y[partner]
-	// from the table at `_WaitAnims @ 29be:021c`. Each entry is
-	// 12 bytes / 6 u16:
-	//   +0..1 anim Jake, +2..3 anim Jenny,
-	//   +4..5 x    Jake, +6..7 x    Jenny,
-	//   +8..9 y    Jake, +10..11 y    Jenny.
-	// `kWaitAnims` lives at file scope above; we cap rendering at
-	// `speaker < 7` since anything past entry 6 is the `_SiteButtons`
-	// rect data that follows the table in the binary.
+	// table index, then for each (speaker x partner) loads:
+	//   anim = WaitAnims[speakerIdx].anim[partner]
+	//   x    = WaitAnims[speakerIdx].x[partner]
+	//   y    = WaitAnims[speakerIdx].y[partner]
+	// from `_WaitAnims @ 29be:021c` (see `kWaitAnims` at file scope for
+	// 12-byte / 6-u16 entry layout). Rendering caps at speaker < 7
+	// (entries past 6 are `_SiteButtons` rect data in the binary).
 	const byte *site = _mystery->siteData(siteNum);
 	if (!site)
 		return;
@@ -1541,14 +1315,10 @@ void SiteScreen::renderPartner(uint siteNum, uint32 tickMs) {
 	int    x;
 	int    y;
 	if (_vm->isFloppy()) {
-		// Floppy: site_data+8 is a u16 OFFSET to a 10-byte
-		// speakerInfo struct (per `_DoSiteLoop_Floppy @ 1652:042b`):
-		//   bytes 0..1  Jake anim ID  (u16)
-		//   bytes 2..3  Jake X        (u16)
-		//   byte  4     Jake Y        (u8)
-		//   bytes 5..6  Jenny anim ID (u16)
-		//   bytes 7..8  Jenny X       (u16)
-		//   byte  9     Jenny Y       (u8)
+		// `_DoSiteLoop_Floppy @ 1652:042b`: site_data+8 is a u16 OFFSET
+		// to a 10-byte speakerInfo struct:
+		//   +0..1 Jake anim, +2..3 Jake X, +4 Jake Y,
+		//   +5..6 Jenny anim, +7..8 Jenny X, +9 Jenny Y.
 		const uint16 spkOff = READ_LE_UINT16(site + 8);
 		const byte *spk = _mystery->blobAt(spkOff);
 		if (!spk)
@@ -1578,20 +1348,9 @@ void SiteScreen::renderPartner(uint siteNum, uint32 tickMs) {
 	if (!_vm->getAni().loadAnimation(animId, anim) || anim.empty())
 		return;
 
-	// `_UpdateAnimations @ 172b:09c1` walks the per-anim script (from
-	// `_AnimationSequences[seqnum]`) one entry per `_CheckFrameRate`
-	// tick (~100 ms): render `script[index]`, advance, wrap on 0x80.
-	// That's how the original gets long idle holds with brief blinks
-	// ??? naive flipbook cycling (`tick % nFrames`) loses those pauses
-	// and makes the partner constantly fidget. `partnerFrameAt` picks
-	// the right frame; if no script is registered for this anim it
-	// falls back to flipbook so unknown anims still move.
-	// Use the relative phase anchor instead of the raw `tickMs` so
-	// the wait anim resumes from script[0] after each kdAnim
-	// one-shot ends ??? matching the original's `_PlayAnimation @
-	// 172b:1f5d` resetting the resumed slot's frame index to
-	// 0xffff. Without this, the wait anim snaps mid-cycle every
-	// time we return from a clue display.
+	// Relative phase anchor (not raw tickMs) so wait anim resumes
+	// from script[0] after each kdAnim one-shot ??? matches
+	// `_PlayAnimation @ 172b:1f5d` reset to frame 0xffff.
 	const uint32 elapsed = (tickMs >= _waitPhaseAnchor)
 							? (tickMs - _waitPhaseAnchor)
 							: tickMs;
@@ -1611,24 +1370,17 @@ void SiteScreen::renderPartner(uint siteNum, uint32 tickMs) {
 	Graphics::Surface *screen = g_system->lockScreen();
 	if (!screen)
 		return;
-	// Partner sprite ??? anchor-aware blit so per-frame `miscflags` /
-	// `rowoff` apply (e.g. the BigMap walk-cycle's -2 px shift).
 	blitAnimFrameAnchored(screen, anim[frameIdx], x, y);
 	g_system->unlockScreen();
 }
 
 void SiteScreen::renderBackground(uint siteNum) {
-	// Mirrors `_BuildBackground(sitepic, 0x42, 0x14)` as called from
-	// `_DoSiteLoop @ 168d:03f4` and `_DisplayCorrect`. The original:
-	//   1. Loads frame via `_GetFromDB(_PicIndex, 0x3d)` ??? that's
-	//      DBI entry 0x3d (0-based). Note this is NOT the same as
-	//      `_GetPicture(0x3d)` which would index entry 0x3c. Our
-	//      `loadEntry(0x3d)` matches the original directly.
-	//   2. Loads SITES.DBD entry by `sitepic` (0-based, from
-	//      SiteData[+0..+1]).
-	//   3. `_Rect_Move(... x, y, 48000, ...)` composes the scene at
-	//      (x, y) = (0x42, 0x14) = (66, 20) on top of the frame.
-	//   4. `_GetPalette(sitepic + 1)` ??? per-site palette.
+	// `_BuildBackground(sitepic, 0x42, 0x14)` from `_DoSiteLoop @
+	// 168d:03f4` / `_DisplayCorrect`:
+	//   1. `_GetFromDB(_PicIndex, 0x3d)` ??? DBI entry 0x3d (0-based).
+	//   2. SITES.DBD entry `sitepic` (0-based, SiteData[+0..+1]).
+	//   3. `_Rect_Move(..., 0x42, 0x14, 48000, ...)` composes at (66, 20).
+	//   4. `_GetPalette(sitepic + 1)`.
 	Picture frame;
 	if (_vm->getPics().loadEntry(0x3d, frame)) {
 		g_system->copyRectToScreen(frame.surface.getPixels(),
@@ -1636,21 +1388,11 @@ void SiteScreen::renderBackground(uint siteNum) {
 								   0, 0, frame.surface.w, frame.surface.h);
 	}
 
-	// `_BuildBackground @ 172b:13e2` calls
-	//   `_GetFromDB(_siteFile, &_SiteDBIndex, sitenum)`
-	// with the value at SiteData[+0] passed straight through. The
-	// `_GetFromDB` callee uses that as a **0-based** index into
-	// SITES.DBD (verified at 172b:14c8: `MOV BX, [BP+0x6]; IMUL BX, BX, 0xa`
-	// ??? the dbi entry stride is 10 bytes, no -1 adjustment). Our
-	// previous `loadEntry(sitepic - 1)` was off by one, which is why
-	// the tutorial mystery rendered scenes from neighbouring cases.
-	// Floppy site_data stores the picID one indirection deeper:
-	// `*site_data` (u16) ??? drops sub-struct, `drops[0]` (byte) is the
-	// SITES.DBD entry. CD uses the u16 directly. Verified at
-	// `FUN_16e2_12fd @ 16e2:12fd` (called as
-	// `FUN_16e2_12fd(*local_12, 0x42, 0x14)` from
-	// `_DoSiteLoop_Floppy`, where `*local_12` is the byte at
-	// drops_struct+0 via the `(undefined1 *)` cast).
+	// `_BuildBackground @ 172b:13e2` ??? `_GetFromDB(_siteFile,
+	// &_SiteDBIndex, sitenum)` uses SiteData[+0] as a 0-based SITES.DBD
+	// index (stride 10, asm `IMUL BX,BX,0xa` @ 172b:14c8; no -1 adjust).
+	// Floppy: site_data[0] is u16 offset to drops sub-struct; drops[0]
+	// byte is the SITES.DBD index (`FUN_16e2_12fd @ 16e2:12fd`).
 	const byte *site = _mystery->siteData(siteNum);
 	uint16 sitepic = 0;
 	if (site) {
@@ -1670,8 +1412,7 @@ void SiteScreen::renderBackground(uint siteNum) {
 	if (!haveScene)
 		haveScene = _vm->getPics().getPicture(sitepic + 1, scene);
 	if (haveScene) {
-		// Hard-coded composition position from `_BuildBackground`:
-		//   `_Rect_Move(0, 0, h, ..., 0x42, 0x14, 48000, h, w)`.
+		// `_Rect_Move(0, 0, h, ..., 0x42, 0x14, 48000, h, w)`.
 		const int x = 0x42;
 		const int y = 0x14;
 		const int w = MIN<int>(scene.surface.w, 320 - x);
@@ -1683,9 +1424,7 @@ void SiteScreen::renderBackground(uint siteNum) {
 }
 
 void SiteScreen::renderHotspots(uint siteNum) {
-	// Hotspot outlines (`_DrawSearchButtons`). The original always
-	// draws these; the port exposes an optional game setting to hide
-	// them for players who do not want location hints.
+	// `_DrawSearchButtons`. Port adds optional "hide hint" setting.
 	if (ConfMan.getBool("hide_highlight_boxes"))
 		return;
 
@@ -1698,31 +1437,31 @@ void SiteScreen::renderHotspots(uint siteNum) {
 	if (!screen)
 		return;
 
-	// Mirrors `_DrawSearchButtons @ 2404:0a8f`:
+	// `_DrawSearchButtons @ 2404:0a8f`:
 	//   for each hotspot:
-	//     if `_Sawit(theSite, loc) == 0` (NOT seen yet):
-	//       `_DrawRect(rect)`       ??? outline in cycling colors
-	//                                 0xF9..0xFE; `_ColorCycle(0xF9,
-	//                                 0xFE)` rotates them every tick ???
-	//                                 "marching ants" glow that draws
-	//                                 the player's eye to unsearched
-	//                                 spots.
+	//     if _Sawit(theSite, loc) == 0 (NOT seen yet):
+	//       _DrawRect(rect)       ??? outline in cycling colors
+	//                                0xF9..0xFE; `_ColorCycle(0xF9, 0xFE)`
+	//                                rotates them every tick ??? "marching
+	//                                ants" glow on unsearched spots.
 	//     else (seen):
-	//       `_DrawSolidRect(rect)` ??? outline in solid colour 0xFF.
-	// (Verified at the actual asm `2404:0af6 OR AX,AX; 2404:0af8 JZ` ???
-	// the C-level decompile mis-reordered the if/else branches.)
-	//
-	// We don't need per-pixel colour cycling here because palette
-	// `0xF9..0xFE` is already rotated by `applyColorCycles` each tick;
-	// drawing the outline with any single colour in that range will
-	// pulse on its own. We pick a phased start so adjacent hotspots
-	// don't all glow in lock-step.
+	//       _DrawSolidRect(rect)  ??? outline in solid colour 0xFF.
+	// (Branch order per asm `2404:0af6 OR AX,AX; 2404:0af8 JZ` ??? the
+	// C-level decompile mis-reordered the if/else.)
+	// Palette 0xF9..0xFE is already rotated by `applyColorCycles` each
+	// tick, so drawing a single colour from that range pulses on its
+	// own. Phase the start colour by hotspot index so adjacent spots
+	// don't glow in lock-step.
 	const uint32 tickMs = g_system->getMillis();
 
-	// CD hotspot rows are 14 bytes each (rect + 6 bytes of clue
-	// metadata). The seen key is the zero-based mystery-wide ordinal
-	// at +0xa, not this site's local row index. Floppy stores plain
-	// 8-byte rectangles only, so its seen key remains the row index.
+	// CD hotspot row = 14 bytes:
+	//   +0..1 x1, +2..3 y1, +4..5 x2, +6..7 y2   (rect, screen coords)
+	//   +8..9   clueOffset    (u16 byte offset to ClueBlock in mystery)
+	//   +0xa..b hotspotIndex  (zero-based mystery-wide seen ordinal)
+	//   +0xc..d extra         (CD cursor ID for `_SwitchMouse`; shipped = 0)
+	// Seen key = the +0xa ordinal (so unrelated hotspots on later sites
+	// don't inherit the first site's seen state after travel/reload).
+	// Floppy = 8-byte plain rect only; seen key falls back to row index.
 	const bool floppy = _vm && _vm->isFloppy();
 	const uint stride = floppy ? 8 : 14;
 	for (uint i = 0; i < count; i++) {
@@ -1738,15 +1477,12 @@ void SiteScreen::renderHotspots(uint siteNum) {
 		const bool seen = seenKey < Mystery::kHotSpotsCap &&
 						   _mystery->_hotSpotsSeen[seenKey];
 		if (seen) {
-			// `_DrawSolidRect` (172b:0506) ??? outline in palette
-			// index 0xFF (a fixed, non-cycling colour) so already-
-			// found hotspots visually retreat into the BG.
+			// `_DrawSolidRect @ 172b:0506` ??? solid 0xFF (non-cycling).
 			screen->frameRect(rect, 0xFF);
 		} else {
-			// `_DrawRect` (172b:03e2) ??? outline in palette indices
-			// 0xF9..0xFE which `_ColorCycle(0xF9, 0xFE)` rotates
-			// every tick. Walk all four edges incrementing the
-			// colour per pixel, exactly like the original.
+			// `_DrawRect @ 172b:03e2` ??? walk all four edges incrementing
+			// the colour per pixel through palette indices 0xF9..0xFE,
+			// which `_ColorCycle(0xF9, 0xFE)` rotates every tick.
 			byte color = (byte)(0xF9 + ((i + (tickMs / 80)) & 0x07) % 6);
 			auto bumpColor = [&]() {
 				const byte next = (byte)(color + 1);
@@ -1813,21 +1549,16 @@ void SiteScreen::updateHotspotCursor(uint siteNum, int x, int y) {
 void SiteScreen::onHotspotClicked(uint siteNum, uint hotIdx) {
 	debugC(1, kDebugSite, "Site %u: hotspot %u clicked", siteNum, hotIdx);
 
-	// Floppy: hotspot rectangles are plain 8-byte rects (no clue
-	// metadata at +0xa or +8); the dialog records live in a separate
-	// per-hotspot list at `site_data[+6]`. Dispatch through the
-	// floppy-specific renderer and mark the click rectangle seen by
-	// array index (the only ordinal we have on floppy).
+	// Floppy: 8-byte rects only (no clue metadata @ +0xa/+8). Dialog
+	// records live in a separate list @ `site_data[+6]`. Seen key =
+	// array index (only ordinal available).
 	if (_vm->isFloppy()) {
 		if (hotIdx < Mystery::kHotSpotsCap)
 			_mystery->_hotSpotsSeen[hotIdx] = 1;
 		_mystery->_searchLocationNumber = (uint16)hotIdx;
-		// Snapshot `_cluesFound` BEFORE the floppy dialog so we can
-		// auto-save only when a new note (= clue text idx) is added.
-		// The floppy clue-side-effect path lives in
-		// `displayFloppyDialogRecords` (see clues.cpp), not in
-		// `displayClue` / `applyClueSideEffects`, so the CD-side
-		// autosave below would never trigger for floppy.
+		// Snapshot `_cluesFound` before dialog ??? autosave on new clue.
+		// Floppy side-effect path is `displayFloppyDialogRecords`
+		// (clues.cpp), not `displayClue` ??? autosave must be duplicated.
 		byte before[Mystery::kCluesFoundCap];
 		memcpy(before, _mystery->_cluesFound, sizeof(before));
 		_vm->setPartnerEraseBg(&_bgSnapshot);
@@ -1852,10 +1583,7 @@ void SiteScreen::onHotspotClicked(uint siteNum, uint hotIdx) {
 
 	// `_DoSiteLoop @ 168d:03f4` (after _DisplayClue):
 	//   _HotSpotsSeen[hotspot[+0xa] * 2] = _HotSpotComplete;
-	// The seen key is the hotspotIndex field (+0xa) ??? a zero-based
-	// mystery-wide ordinal ??? NOT the site's local row index. Using the
-	// local index makes unrelated hotspots on later sites inherit the
-	// first site's seen state after travel or reload.
+	// Seen key = hotspotIndex @ +0xa (mystery-wide ordinal, NOT local row).
 	const byte *spots = _mystery->hotspots(siteNum);
 	uint hotOrdinal = hotIdx; // fallback to array index
 	if (spots) {
@@ -1865,40 +1593,24 @@ void SiteScreen::onHotspotClicked(uint siteNum, uint hotIdx) {
 		_mystery->_hotSpotsSeen[hotOrdinal] = 1;
 	_mystery->_searchLocationNumber = (uint16)hotIdx;
 
-	// Bytes 8..9 of each 14-byte hotspot rect = byte offset within the
-	// mystery blob pointing at a ClueBlock. Verified against M0.BIN:
-	// site 0 hotspot 0 -> 0x0502 -> "Hi! I think somebody's playing a
-	// trick on us...". `displayClue` runs the entry's side effects
-	// (`_AddNotebook` for ClueEntry +0x30..+0x39, gallery +0x26..+0x2f,
-	// onsite +0x1c..+0x25) so we don't need to touch `_cluesFound` here.
+	// Bytes 8..9 of each 14-byte hotspot rect = byte offset to ClueBlock.
+	// `displayClue` handles side effects (`_AddNotebook` for ClueEntry
+	// +0x30..+0x39, gallery +0x26..+0x2f, onsite +0x1c..+0x25).
 	if (spots) {
 		const uint16 clueOff = READ_LE_UINT16(spots + hotIdx * 14 + 8);
 		debugC(2, kDebugSite, "  hotspot %u -> clue offset 0x%04x",
 			   hotIdx, clueOff);
 		const byte *clueBlock = _mystery->blobAt(clueOff);
 		if (clueBlock) {
-			// Snapshot `_cluesFound` BEFORE the clue display so we
-			// can detect if any new clue was actually collected
-			// (vs. a re-read of an already-found clue) ??? only worth
-			// auto-saving when the player makes progress.
+			// Snapshot `_cluesFound` ??? detect new-clue 0???1 ??? autosave.
 			byte before[Mystery::kCluesFoundCap];
 			memcpy(before, _mystery->_cluesFound, sizeof(before));
-			// Hand the engine our partner-less backdrop so that
-			// `_DoKDAnim` / `playKdAnim` (the camera-style reaction
-			// animation that fires when a ClueEntry has +0x3a != -1)
-			// can erase the partner's resting frame between cells
-			// instead of compositing over it. Cleared after the call
-			// so accuse / briefing contexts fall back to their own
-			// snapshots.
+			// Partner-less BG for `_DoKDAnim` / `playKdAnim` to erase
+			// the resting idle (fires when ClueEntry +0x3a != -1).
 			_vm->setPartnerEraseBg(&_bgSnapshot);
 			_vm->displayClue(clueBlock);
 			_vm->setPartnerEraseBg(nullptr);
-			// Auto-save when a new clue is found. The original
-			// engine has no autosave (saving is a manual SETUP
-			// button, `_SaveGame @ 2404:0c87`); we add the autosave
-			// here so the player never loses mystery progress.
-			// Detected via 0???1 transition in `_cluesFound[]` (set
-			// by `applyClueSideEffects` inside `displayClue`).
+			// Port-only autosave (original = manual `_SaveGame @ 2404:0c87`).
 			bool foundNewClue = false;
 			for (uint i = 0; i < Mystery::kCluesFoundCap; i++) {
 				if (!before[i] && _mystery->_cluesFound[i]) {
@@ -1915,26 +1627,23 @@ void SiteScreen::onHotspotClicked(uint siteNum, uint hotIdx) {
 			}
 		}
 	}
-	// Caller (`SiteScreen::run`) re-renders the site after this returns.
 }
 
 void EEMEngine::playKdAnim(uint16 num) {
 	// Mirrors `_DoKDAnim(num) @ 168d:028a` + `_PlayAnimation @ 172b:1f46`:
 	//   _SuspendAnimation(WaitHandle);
-	//   anim   = WaitAnims[1+num].anim[partner]   (table @ 29be:0228)
-	//   x      = WaitAnims[1+num].x[partner]
-	//   y      = WaitAnims[1+num].y[partner]
+	//   anim = kKdAnimTable[num].anim[partner]   (@ 29be:0228)
+	//   x    = kKdAnimTable[num].x[partner]
+	//   y    = kKdAnimTable[num].y[partner]
 	//   _PlayAnimation(anim, x, y, WaitHandle)
-	//     ??? registers a state-4 (one-shot) animation slot and lets
-	//       `_UpdateAnimations` walk the sequence script until 0x80,
-	//       then frees this slot and re-activates `WaitHandle`.
-	// Our port renders the partner's idle inline in each redraw rather
-	// than via a slot system, so we play the one-shot synchronously here
+	//     -> registers a state-4 (one-shot) animation slot and lets
+	//        `_UpdateAnimations` walk the script until 0x80, then
+	//        frees the slot and re-activates `WaitHandle`.
+	// Port renders the partner's idle inline in each redraw rather
+	// than via a slot system, so we play the one-shot synchronously
 	// (blocking) and resume normal idle rendering when the caller
-	// returns. That matches the user-visible effect: the partner's
-	// gesture (Jenny taking a picture, etc.) finishes before the
-	// speaker portrait + speech balloon appear.
-	//
+	// returns ??? matches the visible effect (partner gesture finishes
+	// before the speaker portrait + speech balloon appear).
 	// `kKdAnimTable` and `kAnimScripts` live at file scope above.
 	if (num >= ARRAYSIZE(kKdAnimTable))
 		return;
@@ -1950,28 +1659,17 @@ void EEMEngine::playKdAnim(uint16 num) {
 		return;
 	}
 
-	// `_DoKDAnim` (168d:028a) calls `_PlayAnimation` with state=4 (one-
-	// shot), which `_UpdateAnimations` walks until it sees the 0x80
-	// terminator and then frees the slot. The same script the
-	// site-loop wait anim uses (looping) is what the one-shot plays
-	// through ONCE here.
+	// State-4 one-shot walks the same (looping) script through once.
 	const AnimScriptRef s = findAnimScript(animId);
 	const uint8 *frames = s.frames;
 	uint frameCount     = s.len;
 	if (frameCount == 0) {
-		// Fallback: linear playback through anim cells (better than
-		// nothing if a future kdAnim references an unscripted anim).
+		// Fallback: linear playback through anim cells.
 		frameCount = (uint)anim.size();
 	}
 
-	// Erase-source for between-frame redraw. Prefer the partner-less
-	// backdrop the caller stashed via `setPartnerEraseBg` (e.g. the
-	// site's `_bgSnapshot`, which has the static drops + frame but no
-	// partner sprite). Without that, fall back to whatever's currently
-	// on screen ??? which works for full-screen contexts (PDA / accuse /
-	// briefing) where there is no separate idle partner overlay to
-	// erase, but produces visible "ghosting" against the site's idle
-	// partner cell at (6, 80) because it has the resting pose baked in.
+	// Erase-source: caller-stashed partner-less BG (via `setPartnerEraseBg`)
+	// or fall back to current screen (works for full-screen contexts).
 	Graphics::ManagedSurface bg(320, 200,
 		Graphics::PixelFormat::createFormatCLUT8());
 	if (_partnerEraseBg.w == 320 && _partnerEraseBg.h == 200) {
@@ -1991,35 +1689,27 @@ void EEMEngine::playKdAnim(uint16 num) {
 		const Picture &fr = anim[frameIdx];
 		const byte transp = (byte)(fr.flags >> 8);
 
-		// Restore BG, then masked-blit the next frame.
 		Graphics::ManagedSurface scratch(320, 200,
 			Graphics::PixelFormat::createFormatCLUT8());
 		scratch.simpleBlitFrom(bg);
 		// Anchor-aware: kdAnim cells (0x03/0x04/0x0c/0x0d ...) have
-		// non-zero per-frame `miscflags`/`rowoff` (anim 0x03 has
-		// rowoff up to 9, anim 0x04 has miscflags = -2). Without
-		// applying those, the camera-flash gesture pop-up appears
-		// at a fixed pixel rather than translating across cells.
+		// non-zero per-frame `miscflags`/`rowoff` (anim 0x03 has rowoff
+		// up to 9, anim 0x04 has miscflags = -2). Routes through
+		// `blitAnimFrameAnchored` so the gesture translates across
+		// cells instead of pinning to a fixed pixel.
 		(void)transp;  // anchored blitter recomputes from p.flags
 		blitAnimFrameAnchored(scratch.surfacePtr(), fr, px, py);
 		g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
 								   0, 0, 320, 200);
 		g_system->updateScreen();
 
-		// One frame per `_CheckFrameRate` tick. The original calibrates
-		// this to ~10 fps; 100 ms matches what the rest of the engine
-		// uses for partner / NPC frame cycling. Pump `updateScreen`
-		// inside the inner wait so ScummVM's cursor overlay refreshes
-		// at the same rate as the wait granularity (10 ms ??? 100 Hz)
-		// rather than only on the per-frame redraw ??? without this the
-		// cursor only refreshes once every 100 ms during the anim.
+		// 100 ms per frame (~10 fps). Pump updateScreen inside the wait
+		// so cursor overlay refreshes at 100 Hz.
 		const uint32 wakeup = g_system->getMillis() + 100;
 		while (g_system->getMillis() < wakeup && !shouldQuit()) {
 			Common::Event ev;
 			while (g_system->getEventManager()->pollEvent(ev)) {
-				// Drain events but don't allow skipping mid-animation ???
-				// the speaker portrait + balloon haven't been drawn yet
-				// and a click would otherwise eat the upcoming clue.
+				// Drain events; don't skip mid-anim (would eat upcoming clue).
 				if (ev.type == Common::EVENT_QUIT ||
 					ev.type == Common::EVENT_RETURN_TO_LAUNCHER)
 					return;
@@ -2029,7 +1719,6 @@ void EEMEngine::playKdAnim(uint16 num) {
 		}
 	}
 
-	// Restore BG so the next caller (speaker portrait blit) starts clean.
 	g_system->copyRectToScreen(bg.getPixels(), bg.pitch, 0, 0, 320, 200);
 	g_system->updateScreen();
 }
diff --git a/engines/eem/site.h b/engines/eem/site.h
index 606edf14ffe..895d2124a9f 100644
--- a/engines/eem/site.h
+++ b/engines/eem/site.h
@@ -34,57 +34,46 @@ namespace EEM {
 class EEMEngine;
 class Mystery;
 
-/// Pick the frame index to render at `tickMs` for animation
-/// `seqnum`. Walks the script registered in `kAnimScripts` (mirrors
-/// `_UpdateAnimations @ 172b:09c1`'s looping path) at one entry per
-/// 100 ms tick, wrapping on the script's 0x80 terminator. Falls
-/// back to flipbook (`tick % numFrames`) when no script is
-/// registered. `numFrames` is the underlying ANI.DBD entry's cell
-/// count ??? used both for the fallback path and to clamp script
-/// values that point past the asset.
+/// partnerFrameAtTick: frame index for `seqnum` at `tickMs`. Walks `kAnimScripts`
+/// at `kFramePeriodMs` (~140 ms = `_CheckFrameRate` cadence) per entry; wraps
+/// on the script's 0x80 terminator. Falls back to flipbook (`tick % numFrames`)
+/// when no script is registered. `numFrames` is the ANI.DBD entry's cell count,
+/// used both for the fallback and to clamp script values past the asset.
+/// Mirrors the looping path of `_UpdateAnimations @ 172b:09c1`.
 uint partnerFrameAtTick(uint16 seqnum, uint numFrames, uint32 tickMs);
 
-/// BigMap-overview partner frame: plays the count-up 0..8 once (the
-/// "unfold the map" pose), then loops the `_BigMapWaitSeq` hold
-/// (9,9,9,9,10,9,9,9,9). Mirrors `_DoBigMap @ 20fe:09e7`'s two-phase
-/// dispatch ??? the slot starts on script 0x14 (count-up @ 29be:196a)
-/// and on the 0x80 terminator the slot's script pointer is rewritten
-/// to `_BigMapWaitSeq @ 29be:1574`. `partnerFrameAtTick` can't model
-/// that swap on its own (it always loops), so without this helper the
-/// unfold cycles forever instead of resting on the open-map pose.
-/// `elapsedMs` is the time since the BigMap was opened.
+/// bigMapPartnerFrameAtTick: count-up 0..8 once, then loop `_BigMapWaitSeq`
+/// (9,9,9,9,10,9,9,9,9). Mirrors `_DoBigMap @ 20fe:09e7` two-phase swap from
+/// script 0x14 (count-up @ 29be:196a) to `_BigMapWaitSeq @ 29be:1574`.
 uint bigMapPartnerFrameAtTick(uint numFrames, uint32 elapsedMs);
 
-/// BigMap-detail (zoomed view) partner frame. Same two-phase shape as
-/// `bigMapPartnerFrameAtTick`, but the original `_DoMapScreen @ 20fe:120b`
-/// uses script 0x13 (count-up 0..7 @ 29be:1992) for the unfold and
-/// swaps the slot pointer to `_SmallMapWaitSeq @ 29be:1548` (an
-/// 18-frame hold of cell 7 with a single cell-10 fidget) on the
-/// terminator (`MOV [BX+0x789f],0x1548` at 20fe:1390). `elapsedMs`
-/// is the time since the detail screen was opened.
+/// bigMapDetailPartnerFrameAtTick: zoomed-view partner frame. Same two-phase shape
+/// as `bigMapPartnerFrameAtTick`. `_DoMapScreen @ 20fe:120b` runs script 0x13
+/// (count-up 0..7 @ 29be:1992) then swaps to `_SmallMapWaitSeq @ 29be:1548`
+/// (`MOV [BX+0x789f],0x1548` at 20fe:1390).
 uint bigMapDetailPartnerFrameAtTick(uint numFrames, uint32 elapsedMs);
 
-/// Anchor-aware masked blit. Mirrors the per-frame anchor offset
-/// math in `_UpdateAnimations @ 172b:09c1`:
-/// `blit_x = anchor_x - frame.miscflags`, `blit_y = anchor_y -
-/// frame.rowoff`. Use for any animation rendered through the
-/// `_NewAnimation` path in the original (partner sprites, animated
-/// drops, briefing animations) ??? without it, frames with non-zero
-/// per-cell anchors (e.g. anim 0x14 BigMap walk-cycle's miscflags
-/// = -2 shift) "shake in place" instead of translating across
-/// the screen as they're meant to.
+/// blitAnimFrameAnchored: mask-blit at (anchorX - frame.miscflags,
+/// anchorY - frame.rowoff). Mirrors per-frame anchor math in
+/// `_UpdateAnimations @ 172b:09c1`. Both anchors are SIGNED int16
+/// (e.g. anim 0x14 BigMap walk-cycle has miscflags = -2 per cell, so
+/// the sprite translates across the screen as it cycles). Use for any
+/// animation rendered through `_NewAnimation` in the original ??? partner
+/// sprites, animated drops, briefing animations. Transparency comes
+/// from `flags >> 8` (NOT miscflags).
 void blitAnimFrameAnchored(Graphics::Surface *screen, const Picture &p,
 						   int anchorX, int anchorY);
 
-/// Rotate one VGA palette range by one slot. Mirrors `_ColorCycle`
-/// and is used by site color cycles, hotspot marching ants, and the
-/// BigMap marker shine.
+/// Rotate one VGA palette range by one slot (START???END direction).
+/// Mirrors `_ColorCycle`. Used by per-site Loop-1 ColorCycle entries,
+/// hotspot marching ants (0xF9..0xFE), and the BigMap marker shine.
 void cyclePaletteRange(uint8 start, uint8 end);
 
-/// Rotate one VGA palette range by one slot in the OPPOSITE direction.
-/// Mirrors `_OpenColorCycle @ 2520:04f7` (CD) / `_ReverseColorCycle_Floppy`
-/// ??? used by the opening-anim logos (EA Kids, etc.) where the cycle
-/// shifts END???START rather than START???END.
+/// Rotate one VGA palette range by one slot in the OPPOSITE direction
+/// (END???START): save END, shift every entry up by one (END-1 ??? END, ...),
+/// wrap saved END to START. Mirrors `_OpenColorCycle @ 2520:04f7` (CD) /
+/// `_ReverseColorCycle_Floppy`. Used by opening-anim logos (EA Kids, etc.)
+/// where the cycle shifts END???START rather than START???END.
 void cyclePaletteRangeReverse(uint8 start, uint8 end);
 
 /// One hotspot (search rectangle) within a site, 14 bytes on disk.
@@ -97,14 +86,8 @@ struct Hotspot {
 	Common::Rect rect() const { return Common::Rect(x1, y1, x2, y2); }
 };
 
-/**
- * Site / scene controller.
- *
- * Walks the mystery's SiteIndex to render one site at a time, polls hotspots
- * for the player's search clicks, and dispatches clue display. Mirrors the
- * site loop driven by `_DrawSearchButtons` @ 2404:0a8f and `_SearchButtons`
- * @ 2404:0bfb.
- */
+/// Site / scene controller. Mirrors `_DrawSearchButtons @ 2404:0a8f` /
+/// `_SearchButtons @ 2404:0bfb` site loop.
 class SiteScreen {
 public:
 	SiteScreen(EEMEngine *vm, Mystery *mystery)
@@ -126,40 +109,28 @@ private:
 	bool checkImpatienceCounter();
 	void notePartnerActivity();
 
-	/// Play the partner's site-arrival sequence once `_LastSite !=
-	/// _SiteNumber`. Mirrors `_EnterSiteAnim @ 1000:9b21` ??? animation
-	/// 6 (Jake) / 14 (Jenny) skateboards in from the right edge along
-	/// the bottom, then animation 7 / 15 slides KD in from the left.
-	/// Returns true when the player skipped it with input.
+	/// Partner site-arrival sequence (when `_LastSite != _SiteNumber`).
+	/// Mirrors `_EnterSiteAnim @ 1000:9b21`: anim 6/14 (Jake/Jenny) skateboards
+	/// in from right, then anim 7/15 slides KD in from left. Returns true if skipped.
 	bool enterSiteAnim();
 
-	/// Draw the persistent in-site partner sprite (Jake or Jenny
-	/// standing/idling) at the position from `_WaitAnims` @ 29be:021c.
-	/// Mirrors the `_GetAnimation` + `_NewAnimation` block at the tail
-	/// of `_DoSiteLoop @ 168d:03f4`. `tickMs` selects which frame of
-	/// the partner's animation to render; in the original, frames
-	/// advance per `_CheckFrameRate` tick via `_UpdateAnimations`.
+	/// renderPartner: persistent in-site partner sprite at `_WaitAnims @ 29be:021c`.
+	/// Mirrors `_GetAnimation` + `_NewAnimation` tail of `_DoSiteLoop @ 168d:03f4`.
 	void renderPartner(uint siteNum, uint32 tickMs);
 
-	/// Draw the per-site `_AddDrop` static decorations (Loop 2).
-	/// `_DoSiteLoop` runs this loop with bound siteData[+0x4] and
-	/// 6-byte entries at siteData[+0xc]: {picId, x, y}. These never
-	/// animate so they go in the BG snapshot.
+	/// renderStaticDrops: `_AddDrop` static decorations (Loop 2).
+	/// siteData[+0x4] count, siteData[+0xc] entries (6 bytes: {picId, x, y}).
 	void renderStaticDrops(uint siteNum);
 
-	/// Floppy variant: drops live inside the drops sub-struct
-	/// (`*site_data` ??? drops; `drops[1]` = count; entries at
-	/// `drops + 2` are 5 bytes each: {u16 X, u16 Y, byte picID}).
-	/// PIC entries are loaded from PICS.DBD with `picID - 1`. Per
-	/// `_DoSiteLoop_Floppy @ 1652:0418` and `FUN_16e2_18eb`.
+	/// renderFloppyDrops: floppy variant. `*site_data`???drops; drops[1]=count;
+	/// entries at drops+2 are 5 bytes ({u16 X, u16 Y, byte picID}); PIC loaded as picID-1.
+	/// Per `_DoSiteLoop_Floppy @ 1652:0418` and `FUN_16e2_18eb`.
 	void renderFloppyDrops(uint siteNum);
 
-	/// Draw the per-site animated NPCs (Loop 1) at the current tick.
-	/// `_DoSiteLoop` registers each via `_NewAnimation` (siteData[+0xa]
-	/// entries at siteData[+0x48]: {animId (-1 = ColorCycle), x, y})
-	/// and `_UpdateAnimations @ 172b:09c1` advances frame indices each
-	/// tick. We use a millis-based frame index so all NPCs cycle in
-	/// step with the global clock.
+	/// renderAnimatedDrops: per-site animated NPCs (Loop 1) at current tick.
+	/// `_DoSiteLoop` registers each via `_NewAnimation` (siteData[+0xa] count,
+	/// siteData[+0x48] entries: {animId (-1=ColorCycle), x, y}); frames advance via
+	/// `_UpdateAnimations @ 172b:09c1`.
 	void renderAnimatedDrops(uint siteNum, uint32 tickMs);
 
 	/// Snapshot the post-BG, post-static-drops screen so the per-tick
@@ -169,16 +140,12 @@ private:
 	/// Restore the snapshot taken at `captureBgSnapshot` time.
 	void restoreBgSnapshot();
 
-	/// Scan the site's Loop 1 entries for ColorCycle entries (animId
-	/// == -1) and cache their (start, end) palette ranges. Called from
-	/// `enter()`. Mirrors `_DoSiteLoop @ 168d:03f4`'s init scan.
+	/// scanColorCycles: scan Loop 1 for ColorCycle entries (animId == -1),
+	/// cache (start, end) palette ranges. Mirrors `_DoSiteLoop @ 168d:03f4` init scan.
 	void scanColorCycles(uint siteNum);
 
-	/// Rotate cached ColorCycle palette ranges (and 0xf9..0xfe for
-	/// hotspot marching ants) one step. Mirrors the original's per-tick
-	/// `_ColorCycle(start, end)` calls inside `_DoSiteLoop`'s main
-	/// loop. ScummVM's palette manager grabs current 8-bit RGB and
-	/// writes back the rotated values.
+	/// applyColorCycles: rotate cached ColorCycle ranges + 0xf9..0xfe (hotspot ants)
+	/// one step. Mirrors per-tick `_ColorCycle(start, end)` calls in `_DoSiteLoop`.
 	void applyColorCycles();
 
 	EEMEngine *_vm;
@@ -194,24 +161,13 @@ private:
 	uint32 _impatientDeadlineMs = 0; ///< Test-shortened impatience deadline.
 	PartnerWaitMood _partnerWaitMood = kPartnerWaitDefault;
 
-	/// Wall-clock timestamp at which the partner's wait animation
-	/// "started" (or last restarted). The site loop renders the
-	/// wait sprite at `partnerFrameAtTick(animId, ..., now -
-	/// _waitPhaseAnchor)` so the script position is RELATIVE to
-	/// this anchor, not the global clock. Bump it on:
-	///   - entry to a new site (mirrors `_NewAnimation` setting
-	///     the slot's frame index to 0xffff at site setup, see
-	///     `_DoSiteLoop @ 168d:0436`)
-	///   - return from a one-shot kdAnim (mirrors `_PlayAnimation
-	///     @ 172b:1f5d` writing 0xffff to the resumed slot's frame
-	///     index when state=4 chains back to WaitHandle)
-	/// Without this, the wait anim "snaps" to wherever the global
-	/// clock dictates each time the partner reappears, instead of
-	/// resuming from script[0].
+	/// Wall-clock anchor for partner wait anim phase: rendered as
+	/// `partnerFrameAtTick(animId, ..., now - _waitPhaseAnchor)`.
+	/// Bumped on site entry (`_NewAnimation` writes 0xffff at `_DoSiteLoop @ 168d:0436`)
+	/// and on return from a one-shot kdAnim (`_PlayAnimation @ 172b:1f5d` state=4).
 	uint32 _waitPhaseAnchor = 0;
 
-	/// Per-site cached ColorCycle ranges. Up to 5 (matching the
-	/// original's 5-slot animation table).
+	/// Per-site cached ColorCycle ranges (up to 5, matching original 5-slot anim table).
 	struct ColorCycleRange { uint8 start, end; };
 	Common::Array<ColorCycleRange> _colorCycles;
 };
diff --git a/engines/eem/ui.cpp b/engines/eem/ui.cpp
index 6e9fd72df76..f8c888d1843 100644
--- a/engines/eem/ui.cpp
+++ b/engines/eem/ui.cpp
@@ -36,16 +36,10 @@
 #include "eem/music.h"
 #include "eem/site.h"
 
-// EEM ??? UI screens (NOTE.C, GALLERY.C, ACCUSE.C, MAP.C, CHOOSE.C combined).
-// Each function is a self-contained modal `EEMEngine::doX()` reachable from
-// the site loop. They share the same wait-for-input idiom and PIC 0x3f /
-// 0x41 / 0x42 / 0x43 backdrops.
-
 namespace EEM {
 
-// Five fixed gallery slot positions verified at `29be:0x116`. Used by
-// both `_DrawGallery @ 158f:0046` (notebook gallery) and the accuse
-// portrait grid; the layout is identical so we share the table.
+// Gallery slot positions @ 29be:0x116. Used by `_DrawGallery @ 158f:0046`
+// and the accuse portrait grid.
 struct GallerySlot { int x; int y; };
 const GallerySlot kGallerySlots[5] = {
 	{  83,  14 }, // 0
@@ -206,11 +200,8 @@ const byte *advanceFloppyDialogRecords(const byte *rec, uint count,
 	return rec;
 }
 
-// Floppy gallery slot positions verified at `2608:0x16c` (5 ??
-// {u16 x, u16 y}) ??? read by `_DrawGallery_Floppy @ 154e:0045`'s
-// `[BX + 0x16c]` (x) and `[BX + 0x16e]` (y) loads. The floppy
-// layout is shifted left ~0x30 px relative to CD: row 0 starts at
-// x=0x53 (vs 0x53???0x83 on CD) and the bottom row at x=0x77 / 0xbf.
+// Floppy gallery slot positions @ 2608:0x16c. Read by `_DrawGallery_Floppy
+// @ 154e:0045`'s [BX + 0x16c] (x) and [BX + 0x16e] (y) loads.
 const GallerySlot kFloppyGallerySlots[5] = {
 	{ 0x53, 0x0e }, // 0
 	{ 0x9b, 0x0e }, // 1
@@ -219,17 +210,15 @@ const GallerySlot kFloppyGallerySlots[5] = {
 	{ 0xbf, 0x5a }  // 4
 };
 
-// `_GetKDTextBalloon @ 1df2:0105` digit-balloon table @ `29be:1064`:
+// `_GetKDTextBalloon @ 1df2:0105` digit-balloon table @ 29be:1064:
 //   '0'???0x15  '1'???0x16  '2'???0x17  '3'???0x18  '4'???0x19
 //   '5'???0x1a  '6'???0x20  '7'???0x21  '8'???0x22  '9'???0x1e
 const uint16 kDigitBalloons[10] = {
 	0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x20, 0x21, 0x22, 0x1e
 };
 
-// Return the next non-empty slot in `slotRects` starting from `from`,
-// stepping by `dir` (+1 or -1) with wraparound. Used by the accuse
-// gallery's keyboard-cycle (TAB / arrow keys) ??? mirrors the way
-// `_PutMouseInRect` skips eliminated suspects in the original.
+// Next non-empty slot in `slotRects` from `from`, stepping by `dir` with
+// wraparound. Mirrors `_PutMouseInRect` skipping eliminated suspects.
 int nextLiveSlot(const Common::Array<Common::Rect> &slotRects,
 				 int from, int dir) {
 	const int n = (int)slotRects.size();
@@ -252,9 +241,7 @@ void copyToScreen(Graphics::ManagedSurface &scratch) {
 }
 
 void cycleChooserPalette() {
-	// `_DoChoose` / `_DoListPicker_Floppy` rotate 0x6f..0x73 on each
-	// frame tick while waiting for input. These colors are used by the
-	// animated chooser surface baked into the menu backgrounds.
+	// `_DoChoose` / `_DoListPicker_Floppy` rotate 0x6f..0x73 per tick.
 	cyclePaletteRange(kChooserCycleStart, kChooserCycleEnd);
 }
 
@@ -445,9 +432,6 @@ void drawProfilePickerFrame(const ProfilePickerView &v) {
 	copyToScreen(scratch);
 }
 
-// Snapshot of `doActionScreen`'s captured locals, used by
-// `drawActionMenuFrame`. Lives on the stack inside `doActionScreen`;
-// never escapes.
 struct ActionMenuView {
 	EEMEngine *vm;
 	const Picture *bg;
@@ -460,13 +444,9 @@ struct ActionMenuView {
 	uint pick;
 };
 
-// Mystery list shown in the "Choose A Mystery" sub-screen. Mirrors
-// `_DoChooseMystery @ 1a35:02b7`: opens BOOK%u.NME (CRLF-separated
-// ASCII strings, last entry is whitespace = sentinel), reads up to 25
-// lines ?? 40 bytes each, hands the array to `_CaseSelection`. We
-// preserve the trailing whitespace line as the original sentinel
-// since `_DoChoose @ 1c33:0514` walks until `*piVar3 == 0 && piVar3[1]
-// == 0` ??? but for our renderer we just keep the names array.
+// `_DoChooseMystery @ 1a35:02b7` ??? opens BOOK%u.NME (CRLF strings, up
+// to 25 ?? 40 bytes, whitespace-line sentinel). `_DoChoose @ 1c33:0514`
+// walks until {0, 0}.
 Common::StringArray loadBookNames(uint book) {
 	Common::StringArray names;
 	const Common::String fname = Common::String::format("BOOK%u.NME", book);
@@ -479,10 +459,7 @@ Common::StringArray loadBookNames(uint book) {
 		Common::String line = f.readLine();
 		if (f.eos() && line.empty())
 			break;
-		// `_fgets` in the original reads CRLF terminators with the line;
-		// `Common::File::readLine` strips them, so `line` here is the
-		// text only. Trim trailing whitespace so the sentinel "        "
-		// last entry doesn't render as a blank scrollable row.
+		// Trim trailing whitespace so the sentinel line doesn't render.
 		while (!line.empty() &&
 			   (line.lastChar() == ' ' || line.lastChar() == '\t' ||
 				line.lastChar() == '\r'))
@@ -504,14 +481,8 @@ void clampCaseTopRow(uint &topRow, uint listLen, uint visibleRows) {
 		topRow = maxTop;
 }
 
-// Per-mystery sub-chooser ("Choose A Mystery") view.
-//
-// `names` are the entries from BOOK%d.NME (in display order ??? index 0
-// = first case in the tier, mystery number = `tierLo + index`).
-// `solvedFlags` is a parallel bool array indicating which entries are
-// already solved (greyed and unselectable in `_DoChoose`).
-// `topRow` is the scroll position; up to 12 entries are visible.
-// `selRow` is the highlighted row (0-based within the names array).
+// "Choose A Mystery" sub-chooser view. names = BOOK%d.NME entries
+// (mystery number = tierLo + index). solvedFlags marks greyed entries.
 struct CaseSubmenuView {
 	EEMEngine *vm;
 	const Picture *caseBg;
@@ -535,10 +506,8 @@ void drawCaseGreeter(Graphics::ManagedSurface &scratch,
 	if (!haveKdAnim || !kdAnim || kdAnim->empty())
 		return;
 
-	// `_CaseSelection` registers the partner-specific ANI slot, but
-	// drives it with script 0x15 regardless of partner. The chooser
-	// loop advances it through `_UpdateAnimations` after each
-	// `_CheckFrameRate` tick.
+	// `_CaseSelection` drives the partner ANI with script 0x15 regardless
+	// of partner.
 	const uint32 now = g_system->getMillis();
 	const uint frameIdx = partnerFrameAtTick(0x15, (uint)kdAnim->size(), now);
 	blitAnimFrameAnchored(scratch.surfacePtr(), (*kdAnim)[frameIdx],
@@ -595,12 +564,9 @@ bool animateCaseSelectionReveal(EEMEngine *vm, const Picture *caseBg,
 	return false;
 }
 
-// Mirrors `_DoChoose`'s `DrawList @ 1c33:040d`. 12 visible rows ?? 10 px
-// at (61, 35); colour palette: 0x13 = highlighted (selected), 0x1B =
-// greyed (already solved), 0x5C = default. We approximate with the
-// closest indices of site palette 0 ??? 0xF (white) / 0x7 (medium grey)
-// / 0x8 (dark grey) ??? since we don't decode the original CLUT byte
-// ramp.
+// `_DoChoose`'s `DrawList @ 1c33:040d`. 12 rows ?? 10 px at (61, 35).
+// Original colours 0x13 sel / 0x1B greyed / 0x5C default approximated
+// here as 0xF / 0x8 / 0x7 from site palette 0.
 void drawCaseSubmenu(const CaseSubmenuView &v) {
 	Graphics::ManagedSurface scratch(320, 200,
 		Graphics::PixelFormat::createFormatCLUT8());
@@ -635,18 +601,14 @@ void drawCaseSubmenu(const CaseSubmenuView &v) {
 			kListX, kListY0 + r * kLineH, kListW, color);
 	}
 
-	// Selection arrow at the left edge of the highlighted row ??? the
-	// original highlights via colour change but adding an arrow makes
-	// the keyboard-driven path obvious.
+	// Selection arrow (ScummVM-only ??? original uses colour change).
 	if (v.selRow >= v.topRow && v.selRow < v.topRow + (uint)kVisible) {
 		const int r = (int)(v.selRow - v.topRow);
 		v.vm->getFont().drawString(&scratch, ">",
 			kListX - 6, kListY0 + r * kLineH, 6, 0xF);
 	}
 
-	// Scrollbar thumb. `DrawThumb @ 1c33:????` renders a thumb at
-	// (240, 45..146) proportional to scroll position. We draw a
-	// 1-px outlined block to indicate the same range.
+	// Scrollbar thumb at (240, 45..146), proportional to scroll position.
 	if (count > (uint)kVisible) {
 		const int trackY0 = 45;
 		const int trackH  = 146 - 45;
@@ -674,22 +636,14 @@ void drawActionMenuFrame(const ActionMenuView &v) {
 					   kActionScreenDecorX, kActionScreenDecorY);
 
 	if (v.vm->getFont().isLoaded()) {
-		// `DrawList` @ 1c33:040d coordinates: `_TextBox + 3` for x
-		// and `DAT_29be_0d02` for y. `_TextBox` @ 29be:0d00 holds
-		// {x=58, y=35, x2=238, y2=158}. Matches the blue panel.
+		// `DrawList @ 1c33:040d`. _TextBox @ 29be:0d00 = {58, 35, 238, 158}.
 		const int kListX  = 58 + 3;
 		const int kListW  = 238 - kListX;
 		const int kListY0 = 35;
 		const int kLineH  = 10;
 
-		// Render 11 list rows: separator + menu item pairs.
-		//   row 0  separator
-		//   row 1  Choose A Mystery
-		//   row 2  separator
-		//   row 3  Practice Mystery
-		//   ...
-		//   row 9  See ScrapBook 3
-		//   row 10 separator
+		// 11 rows: separator/item pairs (0=sep, 1=Choose A Mystery, ...,
+		// 9=See Scrapbook 3, 10=sep).
 		for (int r = 0; r < 11; r++) {
 			const int y = kListY0 + r * kLineH;
 			if ((r & 1) == 0) {
@@ -709,21 +663,11 @@ void drawActionMenuFrame(const ActionMenuView &v) {
 }
 
 void EEMEngine::doProfilePicker() {
-	// Mirrors `screen8_handler @ 1c33:1012`. The original walks
-	// `*.PLR` files in `C:\EEMCDSAV\` (max 25), reads the first 12
-	// bytes of each (the player-name field of `_PlayerRecord`), and
-	// hands the list to `_DoChoose`. If no profiles exist (loop hits
-	// `local_20 == 0` at 1c33:1170), it falls straight into
-	// `_NewPlayer`. Selecting an entry calls `_LoadPlayerRecord` and
-	// returns; the 0xfffe / 0xffff chooser sentinels both enter
-	// `_NewPlayer` in this screen.
-
-	// Palette reset. `screen8_handler` runs `_FadeOut(); _GetPalette(0);
-	// _GetBackground(0x104);` before the picker, so the BG always
-	// renders against SITEPALS index 0 regardless of which intro
-	// palette was active last. Without this, skipping out of an intro
-	// anim (THEME / ANIM01..20 / TITLE) leaves the previous video's
-	// palette in place and the picker draws with the wrong colours.
+	// `screen8_handler @ 1c33:1012` ??? walks *.PLR (max 25), reads 12-byte
+	// player-name field, hands list to `_DoChoose`. No profiles or
+	// 0xfffe/0xffff sentinel enters `_NewPlayer`.
+
+	// `screen8_handler` does `_FadeOut(); _GetPalette(0); _GetBackground(0x104)`.
 	setSitePalette(0);
 
 	const SaveStateList saves = listProfiles();
@@ -733,16 +677,12 @@ void EEMEngine::doProfilePicker() {
 	}
 
 	if (!_font.isLoaded()) {
-		// No font means we can't render the picker ??? fall through.
 		doNewPlayer();
 		return;
 	}
 
-	// Build the visible list: existing profile names + "[New Player]".
-	// The DOS picker also has a bottom click area at 29be:0d08 that
-	// returns 0xfffe and immediately enters `_NewPlayer`; keeping the
-	// explicit row makes that affordance visible in ScummVM while the
-	// bottom rect remains active too.
+	// Existing profiles + "[New Player]". DOS bottom click area @ 29be:0d08
+	// returns 0xfffe ??? `_NewPlayer`; rect remains active too.
 	Common::Array<ProfilePickerEntry> entries;
 	for (const SaveStateDescriptor &s : saves) {
 		ProfilePickerEntry e;
@@ -765,12 +705,8 @@ void EEMEngine::doProfilePicker() {
 	const bool haveReveal =
 		_picsArchive.getPicture(kProfilePickerRevealPic, reveal);
 
-	// Picker geometry: `DrawList @ 1c33:040d` is called from
-	// `screen8_handler @ 1c33:1012` with `(_TextBox + 3, DAT_29be_0d02)`.
-	// `_TextBox @ 29be:0d00` holds {x1=58, y1=35, x2=238, y2=158} so
-	// the list origin is (61, 35), 10 px per row, max 12 visible
-	// rows. `screen8_handler` slides PIC 0x105 into the lower strip
-	// before calling `_DoChoose`; it does not draw that caption as text.
+	// Geometry: list origin (61, 35), 10 px/row, 12 visible. PIC 0x105
+	// slides into lower strip via `screen8_handler` before `_DoChoose`.
 	ProfilePickerView view;
 	view.vm = this;
 	view.bg = &bg;
@@ -866,8 +802,7 @@ void EEMEngine::doProfilePicker() {
 					break;
 				}
 				if (kChooserHelpRect.contains(ev.mouse.x, ev.mouse.y)) {
-					// `screen8_handler` sets `Chelp = 0`, so the
-					// original ignores this chooser button here.
+					// `screen8_handler` sets Chelp=0 ??? button ignored.
 					break;
 				}
 				if (kChooserListRect.contains(ev.mouse.x, ev.mouse.y)) {
@@ -915,9 +850,7 @@ void EEMEngine::doProfilePicker() {
 	if (e.slot < 0) {
 		doNewPlayer();
 	} else {
-		// Mirrors `_LoadPlayerRecord` at 1c33:1281 ??? slot found,
-		// load it. The save body re-fills `_playerName`, partner,
-		// chain stage, mysteriesSolved.
+		// `_LoadPlayerRecord @ 1c33:1281`.
 		if (!loadProfile(e.label)) {
 			warning("doProfilePicker: failed to load profile '%s' at slot %d",
 					e.label.c_str(), e.slot);
@@ -927,9 +860,8 @@ void EEMEngine::doProfilePicker() {
 }
 
 void EEMEngine::doNewPlayer() {
-	// Mirrors `_NewPlayer` @ 1c33:0dda. The original draws background
-	// 0x104 + character peek pic 0x107, then shows "Please type your
-	// name" and accepts up to 12 characters until Enter.
+	// `_NewPlayer @ 1c33:0dda` ??? BG 0x104 + peek pic 0x107, prompt for
+	// up to 12 chars.
 	if (!_font.isLoaded()) {
 		_playerName = "Detective";
 		return;
@@ -938,24 +870,18 @@ void EEMEngine::doNewPlayer() {
 	Common::String name;
 	const int maxChars = 12;
 
-	// Mirror the original: load PIC 0x104 as the name-entry backdrop.
-	// The original also slides in PIC 0x107 (a peeking character).
 	Picture bg;
 	const bool haveBG = _picsArchive.getPicture(0x104, bg);
 	Picture peek;
 	const bool havePeek = _picsArchive.getPicture(kNameEntryPeekPic, peek);
 
-	// Localized name-entry prompt. Spanish text is taken from the
-	// Spanish floppy EEM.EXE ("Teclea tu nombre"). The colon suffix is
-	// our own ??? the original DOS prompt has none.
+	// Spanish from EEM.EXE ("Teclea tu nombre"); colon added.
 	const char *prompt = isSpanish()
 		? "Teclea tu nombre:" : "Please type your name:";
 
 	if (animateNameEntryPeek(this, &bg, haveBG, havePeek ? &peek : nullptr))
 		return;
-	// Match the original `_NewPlayer`: `_Show_String(rw=0x28, cl=0x50)`
-	// for the prompt, then `_ShowChar(0x50, x, ???)` for typed input.
-	// (rw=row=y, cl=col=x.) Prompt at (y=40, x=80), input at (y=80, x=80).
+	// Prompt (y=40, x=80), input (y=80, x=80).
 	drawNameEntryFrame(this, &bg, haveBG, havePeek ? &peek : nullptr,
 					   name, prompt);
 
@@ -977,20 +903,14 @@ void EEMEngine::doNewPlayer() {
 			if (k == Common::KEYCODE_RETURN) {
 				if (name.empty())
 					name = "Detective";
-				// Mirrors `_NewPlayer @ 1c33:0dda` tail (1c33:0fa0+):
-				// after the name is committed, try `_LoadPlayerRecord`
-				// ??? if it returns 0 (no existing .PLR), zero out the
-				// per-profile state and call `_SavePlayerRecord` to
-				// create a fresh profile file. Same flow here, mapped
-				// onto ScummVM save slots via name ??? description.
+				// `_NewPlayer @ 1c33:0fa0+`: `_LoadPlayerRecord` ??? if
+				// missing, zero state and `_SavePlayerRecord`.
 				if (!loadProfile(name)) {
 					_playerName = name;
 					memset(_mysteriesSolved, 0, sizeof(_mysteriesSolved));
 					_mystery.clear();
 					_partner = 0;
-					// `_NewPlayer @ 1c33:0fa3` writes
-					// `DAT_2d5d_3f99 = 1` ??? fresh profiles always
-					// start at the Junior tier.
+					// `_NewPlayer @ 1c33:0fa3`: DAT_2d5d_3f99 = 1 (Junior).
 					_chainStage = 1;
 					saveProfile(name);
 				}
@@ -1027,38 +947,24 @@ void EEMEngine::doNewPlayer() {
 }
 
 int EEMEngine::doShowEnding(uint num, bool firstPage) {
-	// Mirrors `_DisplayEnding @ 1df2:0548` + `_DisplayEndingPage @
-	// 1df2:044c` on CD and `FUN_1d40_05b7` + `FUN_1d40_031e` on
-	// floppy. CD ending file format:
+	// `_DisplayEnding @ 1df2:0548` + `_DisplayEndingPage @ 1df2:044c`
+	// (CD); `FUN_1d40_05b7` + `FUN_1d40_031e` (floppy).
+	// CD E<num>.BIN format:
 	//   u16 pageCount
-	//   for each page:
-	//     u16 picNum
-	//     u16 x1, y1, x2, y2  (story rect ??? passed to WordWrap)
-	//     char text[]        (null-terminated, ParseString opcodes)
-	// Floppy files prepend a small title header and use a shared
-	// newspaper background (PIC 0x8b) plus per-page overlay pictures.
-	//
-	// The original swaps the font: `_FreeFont(); _LoadFont("tiny.fnt")`
-	// at 1df2:055f-1df2:0563, calls `_GetPalette(0)` (site palette 0),
-	// then for each page `_GetBackground(picNum)` +
-	// `_WordWrap2(x1, y1, x2-x1, text, fontColor=0, dropColor=-1)`. The
-	// fontColor=0 draws in palette index 0 (the newspaper's body-text
-	// colour), with no drop shadow. Verified at the call site asm
-	// 1df2:04cf-1df2:04f4 (Ghidra mis-paired the two trailing args).
-	//
-	// Keyboard page navigation mirrors the original handlers
-	// (1df2:0689 / 1df2:06a0): LEFT decrements pageIdx, RIGHT (or
-	// SPACE / Enter) increments it. Hitting the boundary (LEFT on page
-	// 0, RIGHT on last page) sets `[BP-0x18]` to -1 / 1 respectively
-	// and exits ??? that return value is what `_ShowScrapbook` uses to
-	// walk forward / backward through solved mysteries (see
-	// 1f78:0664-1f78:069c). Mouse navigation is intentionally limited
-	// to the red-highlighted edge rects so central page clicks are ignored.
-	//
-	// `firstPage=false` opens the ending at the LAST page (used by
-	// `doShowScrapbook` after a "previous mystery" navigation ???
-	// matches `local_8 = 0` written before the back-step at
-	// 1f78:067e).
+	//   per page: u16 picNum
+	//             u16 x1, y1, x2, y2   (story rect ??? passed to WordWrap2)
+	//             char text[]          (null-terminated, ParseString opcodes)
+	// Floppy: small title header + shared newspaper BG (PIC 0x8b) +
+	//   per-page overlay pictures.
+	// Render: _FreeFont; _LoadFont("tiny.fnt") @ 1df2:055f; _GetPalette(0);
+	// per-page _GetBackground(picNum) + _WordWrap2(x1, y1, x2-x1, text,
+	// fontColor=0, dropColor=-1). fontColor=0 is the newspaper body-text
+	// colour (asm 1df2:04cf-04f4 ??? Ghidra mis-paired the trailing args).
+	// LEFT/RIGHT page nav (1df2:0689 / 06a0): boundary sets [BP-0x18] to
+	// -1 / +1 ??? that return value drives `_ShowScrapbook` walking forward/
+	// back through solved mysteries (1f78:0664-069c).
+	// firstPage=false opens at LAST page (used by doShowScrapbook after
+	// "previous mystery"; matches `local_8 = 0` @ 1f78:067e).
 	const Common::String fname = Common::String::format("E%u.BIN", num);
 	Common::File f;
 	if (!f.open(Common::Path(fname))) {
@@ -1077,19 +983,13 @@ int EEMEngine::doShowEnding(uint num, bool firstPage) {
 		return 0;
 	}
 
-	// Mirrors 1df2:0558-1df2:056a ??? `_FreeFont(); _LoadFont(tiny.fnt)`.
-	// The newspaper layout uses TINY.FNT (smaller glyphs) so the body
-	// copy fits in the columns. `_LoadFont(font.fnt)` is restored at
-	// 1df2:0625 after the page loop.
+	// 1df2:0558-056a: TINY.FNT; restored at 1df2:0625.
 	EEMFont tinyFont;
 	const bool haveTinyFont = tinyFont.load(Common::Path("TINY.FNT"));
 	if (!haveTinyFont)
 		warning("doShowEnding: TINY.FNT failed to load ??? falling back");
 
-	// Mirrors 1df2:055f `_GetPalette(0)` ??? site palette 0 is the
-	// shared "newspaper" CLUT for ending pages. The newspaper body
-	// text in particular is palette index 0 (= newspaper black) so we
-	// MUST switch palettes before rendering.
+	// 1df2:055f `_GetPalette(0)` ??? newspaper CLUT (body text = idx 0).
 	setSitePalette(0);
 	CursorMan.showMouse(true);
 
@@ -1227,15 +1127,10 @@ int EEMEngine::doShowEnding(uint num, bool firstPage) {
 									  kFirstTryBadgePos, transp);
 			}
 
-			// Story text. The bytes are a null-terminated string with
-			// `_ParseString` placeholders (0x80 = player name, 0x82
-			// = partner first name, etc.).
+			// `_ParseString` placeholders (0x80=name, 0x82=partner first).
 			const Common::String text = parseString(raw, _playerName, _partner);
 
-			// Use TINY.FNT (`_LoadFont(@29be:10a5)` at 1df2:055f-0563)
-			// and color 0 (`_WordWrap2(...,0,-1)` per asm at 1df2:04cf,
-			// not 0xF as Ghidra's decompile output suggests). Falls
-			// back to the main font if TINY.FNT failed to load.
+			// TINY.FNT + color 0 (asm 1df2:04cf ??? not 0xF as Ghidra shows).
 			const EEMFont &renderFont = haveTinyFont ? tinyFont : _font;
 			if (renderFont.isLoaded() && x2 > x1) {
 				const int textW = MIN<int>((int)x2 - (int)x1, 320 - (int)x1);
@@ -1330,15 +1225,9 @@ int EEMEngine::doShowEnding(uint num, bool firstPage) {
 }
 
 void EEMEngine::doShowScrapbook(uint stage) {
-	// Mirrors `_ShowScrapbook(stage, 0) @ 1f78:0642`. The original
-	// splits the cases into 24-entry tiers, then feeds each ending
-	// viewer return direction back into the tier walk:
-	//   -1 -> previous mystery, opened at its last page
-	//    0 -> close scrapbook
-	//   +1 -> next mystery, opened at its first page
-	// When the requested tier is the player's current chain stage, the
-	// original skips unsolved entries; completed tiers are already solved,
-	// so they are shown as a full range.
+	// `_ShowScrapbook(stage, 0) @ 1f78:0642`. 24-entry tiers; ending
+	// viewer returns -1/0/+1 for prev/close/next. Current tier skips
+	// unsolved entries.
 	if (stage < 1 || stage > 3)
 		return;
 	const int solvedCount =
@@ -1389,31 +1278,13 @@ void EEMEngine::doShowScrapbook(uint stage) {
 }
 
 void EEMEngine::doSetup() {
-	// Mirrors `_DoSetup @ 1f78:044e` (CD) and `_DoSetup_Floppy @
-	// 1ee2:0387` (floppy). Both variants share the same PIC 0x40 BG
-	// with text labels baked in, the same 13 button rectangles at
-	// `_SetupButtons @ 29be:1218` (CD) / `2608:0d8c` (floppy), and the
-	// same 4 highlight rectangles at `0xe94..0xeb3` (Kid1 / Kid2 /
-	// SoundOn / SoundOff). Floppy additionally pre-loads a few overlay
-	// PICs (0x9b..0x9e + 0x1fa) that the CD uses on demand, but the
-	// behaviour is identical ??? colour-key swap on `0xFE` to indicate
-	// active state, click dispatch via the same 12-entry handler
-	// jumptable. So a single shared handler covers both variants.
-	//
-	// The setup screen is BG `PIC 0x40` (loaded once on entry) with
-	// every label baked in ??? "Setup", "Partner", "Sound", "Music",
-	// the "Jake"/"Jenny"/"On"/"Off" option strings, etc. ??? all
-	// rendered in palette key `0xFE`. The original then runs
-	// `_SetupSettings @ 1f78:000d` which uses `_SwapColors @
-	// 172b:1d2a` to recolour those `0xFE` pixels per label rect:
-	// `0x15` for the active state, `0` for the inactive one. So
-	// nothing is drawn as text; the visible state of each toggle is
-	// purely a per-rect colour swap on top of `PIC 0x40`.
-	//
-	// Click hit-tests go through `_SetupButtons @ 29be:1218` ??? 13??
-	// 8-byte rects. Each click runs `HandleSetupButton @ 1f78:0158`,
-	// which dispatches via the 12-entry jumptable at `1f78:0436`.
-	// Verified handler map (decompiled at each jumptable target):
+	// `_DoSetup @ 1f78:044e` (CD) / `_DoSetup_Floppy @ 1ee2:0387`.
+	// PIC 0x40 BG with labels baked in palette key 0xFE; `_SetupSettings
+	// @ 1f78:000d` runs `_SwapColors @ 172b:1d2a` per label rect
+	// (0x15 active, 0x00 inactive ??? nothing drawn as text). 13?? 8-byte
+	// click rects at `_SetupButtons @ 29be:1218` (CD) / 2608:0d8c (floppy);
+	// `HandleSetupButton @ 1f78:0158` dispatches via 12-entry jumptable
+	// at 1f78:0436. Rect map (x1, y1, x2, y2 ??? handler):
 	//   [0]  ( 20, 44, 39, 61)   Partner toggle (1f78:017a)
 	//   [1]  ( 20, 87, 39,104)   Voice toggle   (1f78:0196 ??? DAT_2d5d_3f97)
 	//   [2]  ( 20,127, 39,144)   back to profile (NextScreen=8)
@@ -1427,9 +1298,8 @@ void EEMEngine::doSetup() {
 	//   [10] (212,153,266,184)   Quit          (_AreYouSure ??? NextScreen=0xffff)
 	//   [11] ( 81, 25,238, 37)   Credits       (PIC 0x208 fullscreen)
 	//   [12] ( 11,  1,  3,  3)   debug placeholder
-	// Highlight rects (`Kid1 @ 29be:1320` / `Kid2 @ 29be:1328` /
-	// `SoundOn @ 29be:1330` / `SoundOff @ 29be:1338`) drive
-	// `_SwapColors`; they're not click targets in the original.
+	// Highlight rects (Kid1/Kid2/SoundOn/SoundOff @ 29be:1320..1338) drive
+	// `_SwapColors` only ??? not click targets in the original.
 	if (!_font.isLoaded()) {
 		_nextScreen = (ScreenId)_lastScreen;
 		return;
@@ -1454,11 +1324,9 @@ void EEMEngine::doSetup() {
 	const Common::Rect kSoundOnRect  (106,  86, 125,  94);
 	const Common::Rect kSoundOffRect (106,  96, 125, 104);
 
-	// Pixel-level color-key swap. Mirrors `_SwapColors @ 172b:1d2a`:
-	// for each pixel in `r` whose value equals `from`, replace with
-	// `to`. `0xFE` is the BG's text-key color; `0x15` is the active
-	// (bright) palette index, `0x00` the inactive one ??? both verified
-	// in `_SetupSettings @ 1f78:000d`.
+	// `_SwapColors @ 172b:1d2a` ??? replace pixels in r where value==from
+	// with to. 0xFE = BG text-key; 0x15 = active palette index, 0x00 =
+	// inactive (set by `_SetupSettings @ 1f78:000d`).
 	auto swapColors = [](Graphics::ManagedSurface &dst,
 						 const Common::Rect &r, byte from, byte to) {
 		const int x1 = MAX<int>(0, r.left);
@@ -1500,15 +1368,9 @@ void EEMEngine::doSetup() {
 	};
 	draw();
 
-	// Render `picId` and block until click/key. Returns the pressed
-	// keycode (KEYCODE_ESCAPE for an explicit bail, KEYCODE_INVALID
-	// for a click or any other key). When `transparent` is true,
-	// preserve the current screen behind and overlay `picId` with
-	// its transparent colour key ??? mirrors `_InterfaceHelp @
-	// 1560:0205` calling `_Rect_Move_Mask` with the pic's
-	// `miscflags >> 8` as the transp byte. When false, do a raw
-	// fullscreen blit ??? mirrors the credits handler at 1f78:0281
-	// using `_vga_fbuffvid`.
+	// Render picId and block until input. transparent=true: overlay via
+	// `_Rect_Move_Mask` (`_InterfaceHelp @ 1560:0205`). false: raw fullscreen
+	// blit via `_vga_fbuffvid` (credits @ 1f78:0281).
 	auto showFullscreenPic = [&](uint16 picId,
 								  bool transparent) -> Common::KeyCode {
 		Picture pic;
@@ -1519,18 +1381,13 @@ void EEMEngine::doSetup() {
 		Graphics::ManagedSurface scratch(320, 200,
 			Graphics::PixelFormat::createFormatCLUT8());
 		if (transparent) {
-			// Preserve the current screen so the help PIC's
-			// transparent pixels show the setup BG underneath.
 			Graphics::Surface *cur = g_system->lockScreen();
 			if (cur) {
 				scratch.simpleBlitFrom(*cur);
 				g_system->unlockScreen();
 			}
 			const byte transp = (byte)(pic.flags >> 8);
-			// Explicit destPos ??? the no-destPos overload of
-			// `transBlitFrom` (managed_surface.cpp:738) stretches
-			// src to dst dimensions, scaling the PIC to 320x200.
-			// The original `_Rect_Move_Mask` blits at native size.
+			// Explicit destPos ??? no-destPos overload stretches to dst.
 			scratch.transBlitFrom(pic.surface, Common::Point(0, 0),
 								  (uint32)transp);
 		} else {
@@ -1557,10 +1414,8 @@ void EEMEngine::doSetup() {
 	};
 
 	auto leaveSetup = [&]() {
-		// `_DoSetup`'s entry writes `_NextScreen = _LastScreen`. We
-		// honor any handler that has already overridden `_nextScreen`
-		// (Credits / Save don't, but New Case / Quit do). Otherwise
-		// fall back to `_lastScreen`.
+		// `_DoSetup` entry: _NextScreen = _LastScreen. Fall back to it
+		// unless a handler already overrode _nextScreen.
 		if (_nextScreen == kScreenSetup) {
 			_nextScreen = (ScreenId)_lastScreen;
 			if (_nextScreen == kScreenSetup ||
@@ -1570,7 +1425,7 @@ void EEMEngine::doSetup() {
 		saveProfile(_playerName);
 	};
 
-	_nextScreen = kScreenSetup;  // sentinel ??? leaveSetup picks the real target
+	_nextScreen = kScreenSetup;  // sentinel ??? leaveSetup picks target
 	while (!shouldQuit()) {
 		Common::Event ev;
 		bool dirty = false;
@@ -1592,10 +1447,8 @@ void EEMEngine::doSetup() {
 			const int mx = ev.mouse.x;
 			const int my = ev.mouse.y;
 
-			// Partner toggle (button [0]) ??? original has no symmetric
-			// right-side button (the [3] rect is ScrapBook 1, not a
-			// partner arrow). Direct clicks on the Jake/Jenny labels
-			// are accepted as a more intuitive fallback.
+			// Partner toggle [0]. Direct Jake/Jenny label clicks are a
+			// ScummVM-only fallback.
 			if (kPartnerBtn.contains(mx, my)) {
 				_partner = _partner == 0 ? 1 : 0;
 				dirty = true;
@@ -1610,7 +1463,7 @@ void EEMEngine::doSetup() {
 				continue;
 			}
 
-			// Voice toggle (button [1]).
+			// Voice toggle [1].
 			if (kVoiceBtn.contains(mx, my)) {
 				_voiceOn = !_voiceOn;
 				if (_audio)
@@ -1637,33 +1490,26 @@ void EEMEngine::doSetup() {
 				continue;
 			}
 
-			// New Case (button [7]). Original handler at 1f78:01ad
-			// sets `_NextScreen = 0xa` (CHOOSE_MYSTERY) and exits the
-			// dispatch loop with SI=1.
+			// New Case [7] @ 1f78:01ad ??? NextScreen=0xa (CHOOSE_MYSTERY).
 			if (kNewCaseBtn.contains(mx, my)) {
 				saveProfile(_playerName);
 				_nextScreen = kScreenChooseMystery;
 				return;
 			}
 
-			// Save (button [6]). Original calls `_SaveGame @
-			// 2404:0c87` and stays in the setup loop. Our save is
-			// profile-scoped (one slot per player name) ??? same effect.
+			// Save [6] ??? `_SaveGame @ 2404:0c87`.
 			if (kSaveBtn.contains(mx, my)) {
 				saveProfile(_playerName);
 				continue;
 			}
 
-			// Done (button [8]). Original handler is just `MOV SI,1;
-			// JMP exit` ??? `_NextScreen` stays at whatever entry set it
-			// to (= `_LastScreen`).
+			// Done [8] ??? MOV SI,1; JMP exit (NextScreen stays = LastScreen).
 			if (kDoneBtn.contains(mx, my)) {
 				leaveSetup();
 				return;
 			}
 
-			// Quit (button [10]). Original: `_AreYouSure(0)` ???
-			// confirmed ??? `_NextScreen = 0xffff` (sentinel quit).
+			// Quit [10] ??? `_AreYouSure(0)` ??? NextScreen=0xffff.
 			if (kQuitBtn.contains(mx, my)) {
 				if (areYouSure()) {
 					_nextScreen = kScreenInvalid;
@@ -1673,28 +1519,17 @@ void EEMEngine::doSetup() {
 				continue;
 			}
 
-			// Help (button [9]). Original `_InterfaceHelp(1) @
-			// 1560:0205` walks the help-pic table at `29be:00c8`:
-			// each `num` slot is 5 bytes ??? count + two u16 PIC IDs.
-			// For num=1: count=2, pics = {0x0192, 0x01B1}. The
-			// original blits each pic with `_Rect_Move_Mask` ??? a
-			// MASKED blit whose transparent colour is the pic's
-			// `miscflags >> 8`, so the setup BG shows through. It
-			// also hides the cursor (`MOV [0x3a00], 0` + `_RemoveMouse`
-			// at the top of `_InterfaceHelp`, 1560:0216-021c). ESC
-			// at any point breaks out (1560:02b3 sets uVar5 = count).
+			// Help [9] ??? `_InterfaceHelp(1) @ 1560:0205`. Help-pic table
+			// @ 29be:00c8: each `num` slot = 5 bytes (count + two u16
+			// PIC IDs). num=1 ??? count=2, pics = {0x0192, 0x01B1}. Each
+			// pic blitted via `_Rect_Move_Mask` (transparent = pic
+			// `miscflags >> 8`, so setup BG shows through). Also hides
+			// cursor (1560:0216-021c). ESC breaks (1560:02b3).
 			if (kHelpBtn.contains(mx, my)) {
 				static const uint16 kHelp1Pics[] = { 0x0192, 0x01B1 };
 				CursorMan.showMouse(false);
 				for (uint i = 0; i < ARRAYSIZE(kHelp1Pics); i++) {
-					// Re-render the setup BG before each help PIC so
-					// each one overlays a clean canvas. Without this,
-					// `showFullscreenPic`'s `lockScreen` snapshot would
-					// pick up the previous PIC and the two help cards
-					// would composite together. Mirrors the original's
-					// `_vga_fvidvid(0)` call at the tail of every
-					// `_InterfaceHelp` iteration (1560:02e5), which
-					// restores the back-buffer BG between cards.
+					// Restore BG between cards (1560:02e5 `_vga_fvidvid(0)`).
 					draw();
 					const Common::KeyCode k =
 						showFullscreenPic(kHelp1Pics[i], /*transparent=*/true);
@@ -1706,37 +1541,25 @@ void EEMEngine::doSetup() {
 				continue;
 			}
 
-			// Credits (button [11]). Original handler at 1f78:025a
-			// loads PIC 0x208, hides the cursor (`MOV [0x3a00], 0`
-			// at 1f78:0269 + `_RemoveMouse @ 1000:542f` at 1f78:026F),
-			// blits it fullscreen via `_vga_fbuffvid` (raw copy, no
-			// mask), then waits for any input.
+			// Credits [11] @ 1f78:025a ??? PIC 0x208 fullscreen.
 			if (kCreditsBtn.contains(mx, my)) {
 				CursorMan.showMouse(false);
 				showFullscreenPic(0x208, /*transparent=*/false);
 				CursorMan.showMouse(true);
-				// PIC 0x208 has its own palette baked into the BG
-				// dump via `_GetPicture`; the original restores via
-				// `_GetPalette` on return. Reset to setup palette
-				// (SITEPALS index 0) so the setup BG renders right.
+				// PIC 0x208 has its own baked palette; restore site 0.
 				setSitePalette(0);
 				dirty = true;
 				continue;
 			}
 
-			// Profile (button [2]). Original handler writes
-			// `_NextScreen = 8`, returning to the player/profile
-			// picker. Save the current profile settings first, then
-			// let the screen driver run profile ??? partner ??? case/map.
+			// Profile [2] ??? NextScreen=8.
 			if (kProfileBtn.contains(mx, my)) {
 				saveProfile(_playerName);
 				_nextScreen = kScreenProfile;
 				return;
 			}
 
-			// ScrapBook 1 / 2 / 3 (buttons [3] / [4] / [5]). Original
-			// handlers call `_ShowScrapbook(stage, 0)`, with stages 2
-			// and 3 gated by chain progress.
+			// ScrapBook [3]/[4]/[5] ??? `_ShowScrapbook(stage, 0)`.
 			if (kScrap1Btn.contains(mx, my)) {
 				doShowScrapbook(1);
 				setSitePalette(0);
@@ -1764,17 +1587,9 @@ void EEMEngine::doSetup() {
 }
 
 void EEMEngine::doActionScreen() {
-	// Mirrors `_ActionScreen` @ 1c33:195b. The original draws background
-	// PIC 0x104 plus PIC 9 at (10, 0x87), then calls `_DoChoose` with
-	// `ActionNames @ 29be:0d6a`. The "Book N" heading belongs only to
-	// `_CaseSelection`, so this top-level menu intentionally does not
-	// draw one.
-	// Layout:
-	//   list[0]  = "----------------------------------"
-	//   list[1]  = "         Choose A Mystery"
-	//   list[2..10] = alternating menu items + separators
-	// Five selectable items: Choose A Mystery / Practice Mystery /
-	// See ScrapBook 1/2/3.
+	// `_ActionScreen @ 1c33:195b` ??? BG PIC 0x104 + PIC 9 @ (10, 0x87),
+	// `_DoChoose` with ActionNames @ 29be:0d6a. 5 picks alternating with
+	// separators.
 	enum MenuPick {
 		kPickChoose = 0,
 		kPickPractice,
@@ -1783,9 +1598,7 @@ void EEMEngine::doActionScreen() {
 		kPickScrap3,
 		kNumPicks
 	};
-	// Localized action-menu labels. Spanish text is taken verbatim
-	// from the Spanish floppy EEM.EXE (`eem-full-game/floppy-es/`):
-	// "Elegir Misterio" / "Caso de Practica" / "Ver Recortes  1..3".
+	// Spanish from EEM.EXE floppy-es.
 	const char *kPickLabelEN[kNumPicks] = {
 		"         Choose A Mystery",
 		"         Practice Mystery",
@@ -1801,17 +1614,11 @@ void EEMEngine::doActionScreen() {
 		"         Ver Recortes  3"
 	};
 	const char * const *kPickLabel = isSpanish() ? kPickLabelES : kPickLabelEN;
-	// Menu entry gating per `_ActionScreen @ 1c33:195b` ??? the asm at
-	// 1c33:19d1-1a70 sets greys[] based on chain stage AND per-tier
-	// solve count:
-	//   stage 1 ??? grey ScrapBook 2/3; grey ScrapBook 1 if no tier-1 solves
-	//   stage 2 ??? grey Practice + ScrapBook 3; grey ScrapBook 2 if no tier-2 solves
-	//   stage 3 ??? grey Practice; grey ScrapBook 3 if no tier-3 solves
-	//   stage 4 ??? grey Choose + Practice (post-completion read-only state)
-	// In other words: each tier's ScrapBook unlocks as soon as you've
-	// solved your first case in that tier. Practice Mystery is only
-	// available at stage 1. Choose A Mystery is greyed once every case
-	// in every tier is solved (stage 4).
+	// Gating @ 1c33:19d1-1a70 by chain stage + per-tier solves:
+	//   stage 1: grey SB2/3; SB1 needs any tier-1 solve
+	//   stage 2: grey Practice + SB3; SB2 needs tier-2 solve
+	//   stage 3: grey Practice; SB3 needs tier-3 solve
+	//   stage 4: grey Choose + Practice
 	bool anySolved1 = false;
 	for (uint i = 1; i <= 0x18 && i < sizeof(_mysteriesSolved); i++)
 		if (_mysteriesSolved[i]) { anySolved1 = true; break; }
@@ -1833,8 +1640,7 @@ void EEMEngine::doActionScreen() {
 	const bool kPickEnabled[kNumPicks] = {
 		chooseOn, practiceOn, scrap1On, scrap2On, scrap3On
 	};
-	// Seed selection on the first enabled entry ??? at stage 4 the
-	// `Choose A Mystery` default is greyed, so we land on ScrapBook 1.
+	// Seed selection on first enabled entry.
 	uint pick = 0;
 	for (uint i = 0; i < kNumPicks; i++) {
 		if (kPickEnabled[i]) { pick = i; break; }
@@ -1842,27 +1648,15 @@ void EEMEngine::doActionScreen() {
 
 	const char *kSeparator = "----------------------------------";
 
-	// Click rectangles from the original `_DoChoose` @ 1c33:0514 ??? each
-	// `_InRect(_MouseX, _MouseY, addr, 0x29be)` reads one 4??u16 rect at
-	// the listed offset in segment 29be ({x1, y1, x2, y2}). We use
-	// `Common::Rect` (left/top/right/bottom) which also gives us
-	// `contains(x, y)` for hit testing.
+	// `_DoChoose @ 1c33:0514` click rects (4??u16 {x1,y1,x2,y2} in seg 29be):
 	const Common::Rect kOkRect      ( 12,  63,  41,  87); // 29be:0cd8 confirm
 	const Common::Rect kHelpRect    ( 12, 100,  41, 124); // 29be:0ce0 help
 	const Common::Rect kExitRect    ( 12, 137,  41, 161); // 29be:0ce8 cancel
 	const Common::Rect kListRect    ( 58,  35, 238, 158); // 29be:0d00 list panel
 
-	// The original `_NewPlayer` set `_MouseCursor = 1` on exit; the
-	// chain of screens after it expects the cursor to stay visible.
-	// Reassert here in case anything between hid it.
 	CursorMan.showMouse(true);
 
-	// Reassert site palette 0 (the chooser CLUT). In
-	// the normal flow `doProfilePicker` (or the post-screen reset paths
-	// at lines 1402 / 1147 / 1121) leaves us on palette 0 already, but
-	// the launcher-resume path jumps straight here from `_AllBlack`
-	// (palette = all-zero) ??? without this the BG renders into a black
-	// CLUT and the player sees an empty screen.
+	// Launcher-resume path can enter from `_AllBlack` palette.
 	setSitePalette(0);
 
 	Picture bg;
@@ -1893,11 +1687,8 @@ void EEMEngine::doActionScreen() {
 				ev.type == Common::EVENT_RETURN_TO_LAUNCHER)
 				return;
 			if (ev.type == Common::EVENT_LBUTTONDOWN) {
-				// OK / EXIT / HELP buttons (rectangles from `_DoChoose`).
 				if (kOkRect.contains(ev.mouse.x, ev.mouse.y)) {
-					// Greyed entries can't be confirmed (mirrors
-					// `_DoChoose @ 1c33:0635` ??? clicks on a `_Greys[i]
-					// != 0` row are ignored before `select` is set).
+					// Greyed entries ignored (`_DoChoose @ 1c33:0635`).
 					if (kPickEnabled[pick])
 						confirmed = true;
 					break;
@@ -1908,12 +1699,9 @@ void EEMEngine::doActionScreen() {
 					break;
 				}
 				if (kHelpRect.contains(ev.mouse.x, ev.mouse.y)) {
-					// `_ActionScreen` sets `Chelp = 0`, so `_DoChoose`
-					// ignores this middle button on the top-level menu.
+					// `_ActionScreen` sets Chelp=0.
 					continue;
 				}
-				// List panel: click on a non-separator row selects the
-				// menu entry under the cursor.
 				if (kListRect.contains(ev.mouse.x, ev.mouse.y)) {
 					const int kLineH = 10;
 					const int row = (ev.mouse.y - kListRect.top) / kLineH;
@@ -1942,10 +1730,7 @@ void EEMEngine::doActionScreen() {
 				break;
 			}
 			if (k == Common::KEYCODE_UP || k == Common::KEYCODE_LEFT) {
-				// Cycle backwards through enabled picks (mirrors the
-				// `_DoChoose` arrow handlers @ 1c33:0514). Loop is
-				// bounded by `kNumPicks` so a row of all-disabled picks
-				// can't spin forever.
+				// `_DoChoose` arrow handlers @ 1c33:0514, bounded loop.
 				for (int i = 0; i < (int)kNumPicks; i++) {
 					pick = (pick == 0) ? (uint)(kNumPicks - 1) : pick - 1;
 					if (kPickEnabled[pick])
@@ -2010,12 +1795,8 @@ void EEMEngine::doActionScreen() {
 	}
 
 	if (pick == kPickScrap1 || pick == kPickScrap2 || pick == kPickScrap3) {
-		// `_ActionScreen` handlers at 1c33:1B13 / 1B26 / 1B40 each
-		// call `_ShowScrapbook(0, stage)` for the matching tier
-		// (verified at the action-handler jumptable bytes
-		// `01 03 05 07 09 ff` paired with handlers at 1c33:1be1).
-		// Viewing the scrapbook never starts a new case; return to
-		// the same action menu afterwards.
+		// `_ActionScreen` handlers @ 1c33:1B13 / 1B26 / 1B40 ???
+		// `_ShowScrapbook(0, stage)`. Returns here afterwards.
 		const uint stage = (pick == kPickScrap1) ? 1
 						 : (pick == kPickScrap2) ? 2 : 3;
 		doShowScrapbook(stage);
@@ -2029,11 +1810,8 @@ void EEMEngine::doActionScreen() {
 }
 
 void EEMEngine::doCaseSelection() {
-	// Mirrors `_DoChooseMystery @ 1a35:02b7` + `_CaseSelection @
-	// 1c33:0a87`. `_DoChooseMystery` loads BOOK<stage>.NME and
-	// `_CaseSelection` draws PIC 0x41 plus the centered "Book N" /
-	// "Challenge Book" title. This screen is entered only after the
-	// player selects "Choose A Mystery" on `_ActionScreen`.
+	// `_DoChooseMystery @ 1a35:02b7` + `_CaseSelection @ 1c33:0a87` ???
+	// loads BOOK<stage>.NME, draws PIC 0x41 + centered "Book N" title.
 	const uint kMaxMystery = 54;
 
 	CursorMan.showMouse(true);
@@ -2047,9 +1825,7 @@ void EEMEngine::doCaseSelection() {
 	const bool haveRevealPic =
 		_picsArchive.getPicture(kCaseSelectionRevealPic, revealPic);
 
-	// KD greeter sprite. `_CaseSelection @ 1c33:0a87` loads anim 0x15
-	// (Jake-paired) or 0x16 (Jenny-paired), then runs it through the
-	// chooser loop via `_UpdateAnimations`.
+	// `_CaseSelection @ 1c33:0a87` greeter ANI 0x15 (Jake) / 0x16 (Jenny).
 	const uint kKdAniId = (_partner == 0) ? 0x15 : 0x16;
 	Animation kdAnim;
 	const bool haveKdAnim = _aniArchive.loadAnimation(kKdAniId, kdAnim)
@@ -2057,10 +1833,8 @@ void EEMEngine::doCaseSelection() {
 	const int kKdAnimX = 0x112;
 	const int kKdAnimY = 0x50;
 
-	// Stage roster:
-	//   stage 1 (Junior, BOOK1.NME) -> mysteries  1..24
-	//   stage 2 (Senior, BOOK2.NME) -> mysteries 25..48
-	//   stage 3 (Master, BOOK3.NME) -> mysteries 49..54
+	// stage 1 (Junior, BOOK1.NME) = 1..24, stage 2 (Senior, BOOK2.NME) =
+	// 25..48, stage 3 (Master, BOOK3.NME) = 49..54.
 	uint stageLo = 1, stageHi = 0x18;
 	uint book = 1;
 	switch (_chainStage) {
@@ -2078,8 +1852,7 @@ void EEMEngine::doCaseSelection() {
 	}
 	const uint listLen = MIN<uint>((uint)names.size(), stageHi - stageLo + 1);
 
-	// Per-row solved flags. `_DoChoose @ 1c33:0521` skips solved entries
-	// when seeding the initial selection and ignores clicks on them.
+	// Solved-row gating @ `_DoChoose @ 1c33:0521`.
 	Common::Array<bool> solvedFlags;
 	solvedFlags.resize(listLen);
 	for (uint i = 0; i < listLen; i++) {
@@ -2143,10 +1916,8 @@ void EEMEngine::doCaseSelection() {
 					return;
 				}
 				if (kChooserHelpRect.contains(ev.mouse.x, ev.mouse.y)) {
-					// Original `_CaseSelection` returns 0xfffe here,
-					// then `_ChooseSavedGame` runs. ScummVM stores
-					// in-progress cases in the profile, so route to
-					// the profile picker instead.
+					// Original returns 0xfffe ??? `_ChooseSavedGame`. ScummVM
+					// stores in-progress cases per profile.
 					saveProfile(_playerName);
 					_mystery.clear();
 					_nextScreen = kScreenProfile;
@@ -2170,9 +1941,7 @@ void EEMEngine::doCaseSelection() {
 					const uint idx = topRow + (uint)row;
 					if (idx >= listLen || solvedFlags[idx])
 						continue;
-					// Second click on the already-selected row counts as
-					// the OK button ??? saves the player a trip down to the
-					// bottom-bar after picking a mystery.
+					// Second click on selected row = OK (ScummVM ergonomic).
 					if (idx == selRow) {
 						confirmed = true;
 						break;
@@ -2277,51 +2046,25 @@ void EEMEngine::doCaseSelection() {
 }
 
 void EEMEngine::doNotebook() {
-	// Mirrors `_DoNotebook @ 161e:0500` + `_DrawNotes @ 161e:01d0` +
-	// `_HandleNoteButton @ 161e:03cb`.
-	//
-	// Layout (verified from Ghidra labels in 29be:013f / 29be:0147):
-	//   _NotebookRect = (78, 12, 288, 152)   ??? note display rectangle.
-	//   _NoteButtons (11 entries, 8 bytes each, at 29be:0147):
-	//     [0]  (134, 174, 155, 190)  decorative ??? `_HandleNoteButton(0)`
-	//                                returns immediately (i-1 unsigned > 9).
-	//     [1]  (93,  174, 115, 190)  ??? `_InterfaceHelp(0)` (handler 0x3f9)
-	//     [2]  (157, 174, 178, 190)  ??? handler 0x477   (page nav)
-	//     [3]  (5,   80,  44, 110)   ??? `_KDHelp` (host hint, 0x403)
-	//     [4]  (180, 174, 201, 190)  ??? solve / accuse  (0x436)
-	//     [5]  (204, 174, 224, 190)  ??? `_NextScreen = 5` (gallery, 0x489)
-	//     [6]  (226, 174, 247, 190)  ??? handler 0x4ab
-	//     [7]  (7,   177,  57, 200)  ??? handler 0x480   (back to map)
-	//     [8]  (35,  111,  56, 136)  ??? `_NextScreen = 3` (site)
-	//     [9]  (0, 0, 0, 0)          ??? same exit as [8]
-	//     [10] (66,  79, 267, 174)   ??? `_InterfaceHelp(0)` (note area)
-	//   Background: PIC 0x3f.
-	//   Partner anim: anim 1 (Jake) / 0xb (Jenny) at (5, 80).
+	// `_DoNotebook @ 161e:0500` + `_DrawNotes @ 161e:01d0` +
+	// `_HandleNoteButton @ 161e:03cb`. _NotebookRect = (78, 12, 288, 152).
+	// _NoteButtons @ 29be:0147 ??? 11 rects ?? 8 bytes. Jumptable @ 161e:04ec
+	// dispatches handler[i-1] (rect 0's i-1 underflows = decorative slot):
+	//   [0] (134,174,155,190)  decorative ??? no handler
+	//   [1] ( 93,174,115,190)  HELP ??? `_InterfaceHelp(0)`           (0x3f9)
+	//   [2] (157,174,178,190)  GALLERY ??? `_NextScreen = 5`          (0x477)
+	//   [3] (  5, 80, 44,110)  host hint ??? `_KDHelp`                (0x403)
+	//   [4] (180,174,201,190)  SOLVE ??? `_SolvedCheck` ??? NextScreen=7 (0x436)
+	//   [5] (204,174,224,190)  PAGE NEXT ??? `_EraseNotes` + redraw   (0x489)
+	//   [6] (226,174,247,190)  PAGE PREV ??? CurrentPage-- + redraw   (0x4ab)
+	//   [7] (  7,177, 57,200)  MAP ??? `_NextScreen = 2`              (0x480)
+	//   [8] ( 35,111, 56,136)  SITE ??? `_NextScreen = 3`             (0x3ed)
+	//   [9] (  0,  0,  0,  0)  same exit as [8]
+	//   [10] (267,174,288,190) ??? `_InterfaceHelp(0)` again          (0x3f9)
+	// BG PIC 0x3f; partner ANI 1 (Jake) / 0xb (Jenny) at (5, 80).
 	if (!_mystery.isLoaded() || !_font.isLoaded())
 		return;
 
-	// Button rects from `_NoteButtons @ 29be:0147` matched to handler
-	// addresses via the jump table at `_HandleNoteButton + 0xec` (i.e.
-	// 161e:04ec). Decoded handlers (i = rect_index, dispatch = handler[i-1]):
-	//   rect 0 (134,155) ??? no handler (i-1 underflows; original treats
-	//                      this as a decorative/no-op slot)
-	//   rect 1 (93,115)  ??? 0x03f9 = `_InterfaceHelp(0)`           (HELP)
-	//   rect 2 (157,178) ??? 0x0477 = `_NextScreen = 5`             (GALLERY)
-	//   rect 3 (5,80)    ??? 0x0403 = `_KDHelp`                     (host hint)
-	//   rect 4 (180,201) ??? 0x0436 = `_SolvedCheck` -> NextScreen=7 (SOLVE)
-	//   rect 5 (204,224) ??? 0x0489 = `_EraseNotes` + `_DrawNotes`  (PAGE NEXT)
-	//   rect 6 (226,247) ??? 0x04ab = decrement CurrentPage + redraw (PAGE PREV)
-	//   rect 7 (7,177)   ??? 0x0480 = `_NextScreen = 2`             (MAP)
-	//   rect 8 (35,111)  ??? 0x03ed = `_NextScreen = 3`             (SITE)
-	//   rect 9 (0,0)     ??? 0x03ed = same as rect 8
-	//   rect 10 (66,79)  ??? 0x03f9 = `_InterfaceHelp(0)`           (note-area help)
-	// (`_NoteButtons @ 29be:0147` actually has rect [10] at
-	// (267,174,288,190) ??? small button on the right of the bottom
-	// bar that the original handler dispatch table at 161e:04ec
-	// routes to `_InterfaceHelp(0)` again. Earlier this rect was
-	// mis-noted as a "note area" of (66,79,267,174) ??? that
-	// rectangle exists nowhere in the binary's button table.)
-
 	CursorMan.showMouse(true);
 
 	int page = 0;
@@ -2372,10 +2115,7 @@ void EEMEngine::doNotebook() {
 				}
 			}
 			if (ev.type == Common::EVENT_LBUTTONDOWN) {
-				// Test buttons in the order the original would ???
-				// button 0 / 9 are dead zones, so check the actionable
-				// rects directly. Earlier rects "win" when overlapping
-				// (matches `_FindButton`).
+				// Earlier rects win on overlap (matches `_FindButton`).
 				if (kPdaSiteRect.contains(ev.mouse.x, ev.mouse.y)) {
 					_nextScreen = kScreenSite;
 					exitFlag = true;
@@ -2403,8 +2143,7 @@ void EEMEngine::doNotebook() {
 					break;
 				}
 				if (kPdaHelpRect.contains(ev.mouse.x, ev.mouse.y)) {
-					// rect 1 ??? `_InterfaceHelp(0)`: walks `HelpData[0]` and
-					// blits PICs 0x63 / 0x1ae fullscreen for click-through.
+					// rect 1 ??? `_InterfaceHelp(0)` (PICs 0x63 / 0x1ae).
 					setInteractiveMouseCursor(false);
 					doInterfaceHelp(0);
 					dirty = true;
@@ -2422,22 +2161,14 @@ void EEMEngine::doNotebook() {
 					continue;
 				}
 				if (kPdaHelp2Rect.contains(ev.mouse.x, ev.mouse.y)) {
-					// `_NoteButtons[10]` ??? handler 0x03f9 = same
-					// `_InterfaceHelp(0)` as button [1].
+					// _NoteButtons[10] ??? handler 0x03f9 = same as [1].
 					setInteractiveMouseCursor(false);
 					doInterfaceHelp(0);
 					dirty = true;
 					continue;
 				}
-				// Click on a clue's slot rect ??? toggle selection. The
-				// original `_DoNotebook` doesn't do this ??? note
-				// selection lives in the accuse screen there ??? but
-				// keyboard 1..9 toggling is awkward, and the resulting
-				// `_NoteSelected` state is what `_SolvedCheck` reads
-				// either way. Slot rects are the per-clue rectangles
-				// `drawNotebookFrame` publishes, so this just
-				// reproduces the visible-text-bbox click without the
-				// previous bogus outer-area gate.
+				// ScummVM-only: click on clue slot toggles _NoteSelected
+				// (original toggles only in accuse screen).
 				for (uint i = 0; i < _notebookSlotRects.size(); i++) {
 					if (_notebookSlotRects[i].contains(ev.mouse.x,
 													   ev.mouse.y)) {
@@ -2453,7 +2184,7 @@ void EEMEngine::doNotebook() {
 			break;
 
 		const uint32 now = g_system->getMillis();
-		// Re-render every 100 ms so the partner sprite cycles frames.
+		// Re-render every 100 ms for partner sprite cycle.
 		if (dirty || now - lastDraw >= 100) {
 			drawNotebookFrame(page);
 			lastDraw = now;
@@ -2469,50 +2200,31 @@ void EEMEngine::doNotebook() {
 }
 
 void EEMEngine::drawNotebookFrame(int &page) {
-	// PDA notebook redraw ??? formerly the `draw` lambda inside `doNotebook`.
-	// Mirrors `_DrawNotes @ 161e:01d0` for the per-page note layout, plus
-	// the partner-sprite blit at (5, 80) (`_NewAnimation` from
-	// `_DoNotebook @ 161e:0500`). Uses `_notebookSlotRects` /
-	// `_notebookSlotClues` to publish the per-page slot layout to the
-	// click handler in `doNotebook`.
+	// `_DrawNotes @ 161e:01d0` per-page layout + partner sprite at (5,80)
+	// from `_DoNotebook @ 161e:0500`.
 	const Common::Rect kNotebookRect(78, 12, 288, 152);
 
 	Graphics::ManagedSurface scratch(320, 200,
 		Graphics::PixelFormat::createFormatCLUT8());
 	scratch.clear();
 
-	// PIC 0x3f frame.
 	Picture frame;
 	if (_picsArchive.getPicture(0x3f, frame))
 		scratch.simpleBlitFrom(frame.surface);
 
-	// Partner sprite at (5, 80). Anim 1 for Jake, 0xb (11) for Jenny
-	// for CELLS, but the original `_DoNotebook @ 161e:0500` always
-	// uses script 0x01 (verified by `CONCAT22(1, ...)` in its
-	// `_NewAnimation` call at 161e:054c). Both 0x01 and 0x0b have
-	// the SAME script in `kAnimScripts` (alias), so both lookups
-	// produce identical results ??? but routing through 0x01
-	// matches the original verbatim.
+	// Partner ANI 1/0xb (cells); script 0x01 (`_NewAnimation @ 161e:054c`).
 	const uint partnerAnim = (_partner == 0) ? 1 : 0xb;
 	Animation partnerAni;
 	if (_aniArchive.loadAnimation(partnerAnim, partnerAni) && !partnerAni.empty()) {
 		const uint32 now = g_system->getMillis();
 		const uint frameIdx = partnerFrameAtTick(0x01,
 												  (uint)partnerAni.size(), now);
-		// Anchor-aware blit. The PDA partner (anim 0x01/0x0b) cells
-		// have miscflags = rowoff = 0 in the audit, but routing
-		// through `blitAnimFrameAnchored` is harmless and keeps the
-		// rendering path consistent with the BigMap partner.
 		blitAnimFrameAnchored(scratch.surfacePtr(),
 							  partnerAni[frameIdx], 5, 80);
 	}
 
-	// Notes ??? `_DrawNotes` walks `_NoteIndex` for the current page,
-	// rendering each found clue's text inside `_NotebookRect` with
-	// word-wrap. Selected clues are highlighted (color 0x3c in the
-	// original's case-briefing palette).
-	// Build a list of found-clue indices, identical ordering to the
-	// original's iteration through `_CluesFound[]`.
+	// `_DrawNotes` walks `_NoteIndex` for current page; word-wraps each
+	// found clue in `_NotebookRect`. Selected = color 0x3c.
 	Common::Array<uint> found;
 	for (uint i = 0; i < Mystery::kCluesFoundCap; i++) {
 		if (_mystery._cluesFound[i] && _mystery.noteHasNotebookText(i))
@@ -2526,29 +2238,14 @@ void EEMEngine::drawNotebookFrame(int &page) {
 	const int kRectW = kNotebookRect.width();
 	const int kRectH = kNotebookRect.height();
 
-	// Walk forward to the start clue of the current page.
-	// Each page renders as many clues as fit in `kRectH`.
+	// Walk forward to start clue of current page; fits as many as kRectH.
 	int clueCursor = 0;
 	Common::Array<int> pageStarts;
 	pageStarts.push_back(0);
-	// Floppy NoteIndex entries are 7 bytes:
-	//   +0..1  clue text offset (ABSOLUTE byte offset into the
-	//          mystery blob; partner-agnostic clue statement ???
-	//          this is what the notebook displays).
-	//   +2..3  Jake's spoken line offset (used by
-	//          `_DisplayHotspotClue_Floppy` when Jake narrates a
-	//          dialog record ??? NOT for the notebook).
-	//   +4..5  Jenny's spoken line offset (same, for Jenny).
-	//   +6     score.
-	// Verified at `_DrawNotes_Floppy / FUN_15e0_01e8`'s
-	// `*(int *)(notes + idx * 7)` access (no `+2`/`+4` shift ???
-	// always reads byte +0). The earlier port used the dialog
-	// spoken line for the notebook, which showed the partner's
-	// "Hi, Jake! Hi, Jenny!" intro instead of the actual clue
-	// statement ("We received a note that says..."). CD entries
-	// are 4 bytes with offsets relative to the TextBlock at
-	// header[+0xc]. Resolve the right text for the active variant
-	// once per render.
+	// Floppy NoteIndex entry (7 bytes): +0 clueTextOff (absolute, what
+	// the notebook displays), +2 Jake spoken, +4 Jenny spoken, +6 score.
+	// `_DrawNotes_Floppy / FUN_15e0_01e8` reads only byte +0.
+	// CD entries are 4 bytes, offsets relative to TextBlock @ header[+0xc].
 	const bool floppyNb = isFloppy();
 	const byte *bufBase = _mystery.blobAt(0);
 	const uint32 mysSz  = _mystery.dataSize();
@@ -2594,8 +2291,7 @@ void EEMEngine::drawNotebookFrame(int &page) {
 			page = 0;
 	}
 
-	// Track per-slot rectangles so the click handler can map a
-	// click in `kNoteArea` back to a clue index.
+	// Per-slot rects published to the click handler.
 	Common::Array<Common::Rect> slotRects;
 	Common::Array<uint> slotClues;
 
@@ -2611,12 +2307,7 @@ void EEMEngine::drawNotebookFrame(int &page) {
 		if (txt.empty())
 			txt = Common::String::format(
 				isSpanish() ? "nota %u" : "clue %u", clueId);
-		// Per `_DrawNotes @ 161e:01d0`: text uses
-		// `_NoteUnselectedColor` (0x5c=cyan) for unselected and 0x3c
-		// (light yellow-white) for selected. Both contrast cleanly
-		// with the PDA screen's natural blue, so we draw text
-		// directly on PIC 0x3f without an extra fill rectangle ???
-		// matches the original design.
+		// `_DrawNotes @ 161e:01d0`: 0x5c unselected, 0x3c selected.
 		Common::Array<Common::String> wrapped;
 		_font.wordWrapText(txt, kRectW, wrapped);
 		const int lineH = _font.getFontHeight();
@@ -2632,12 +2323,7 @@ void EEMEngine::drawNotebookFrame(int &page) {
 		y += h + 7;
 	}
 
-	// Page indicator only ??? the original `_DrawNotes @ 161e:01d0`
-	// has no points display in the PDA notebook (the per-clue point
-	// values are SPOILERS for the chain weighting that the engine
-	// uses internally for `_GetSelectedPoints`). Showing the running
-	// total tells the player exactly when they have enough evidence
-	// to solve, which deflates the deduction step.
+	// Page indicator only (original has no points display).
 	_font.drawString(&scratch, Common::String::format("p%d/%d",
 							   page + 1, (int)pageStarts.size()),
 					 270, 4, 320, 0x5C);
@@ -2652,27 +2338,11 @@ void EEMEngine::drawNotebookFrame(int &page) {
 }
 
 void EEMEngine::doGallery() {
-	// Mirrors `_DoGallery @ 158f:065b` and `_DrawGallery @ 158f:0046`.
-	// Verified directly from the disassembly:
-	//   * Background: PIC 0x3f (same as PDA).
-	//   * Partner sprite at (5, 0x50): anim 2 (Jake) / 0x10 (Jenny).
-	//     `_NewAnimation(5, 0x50, ...)`. NOTE: gallery uses anim 2/0x10,
-	//     PDA uses 1/0xb ??? different sprites.
-	//   * Five fixed slot positions at `29be:0x116` (4 bytes per slot,
-	//     `{u16 x, u16 y}`):
-	//         slot 0 = ( 83,  14)   slot 3 = (119,  90)
-	//         slot 1 = (155,  14)   slot 4 = (191,  90)
-	//         slot 2 = (227,  14)
-	//   * For each logical suspect i in 0..NumSuspects-1:
-	//         picId   = `*(u16 *)(_GalleryData + i * 0x46)` (entry +0).
-	//         visible = `_InGallery[_NewOrder[i]] != 0`.
-	//         drawX   = positions[_NewOrder[i]].x
-	//         drawY   = positions[_NewOrder[i]].y + (0x48 - pic.height)
-	//     So portraits are BOTTOM-aligned to baselines 0x48 + pos.y.
-	//   * Click on portrait via `_SearchSuspects` ??? `MoreInfo(i)` shows
-	//     the suspect detail page. ESC returns to PDA.
-	//   * Frame-cycled @ 100ms via `_CheckFrameRate` + `_UpdateAnimations`
-	//     + `_GizmoColorCycle`.
+	// `_DoGallery @ 158f:065b` + `_DrawGallery @ 158f:0046`. BG PIC 0x3f.
+	// Partner ANI 2 (Jake) / 0x10 (Jenny) at (5, 0x50). 5 slot positions
+	// @ 29be:0x116. Per suspect i: picId = *(u16*)(_GalleryData + i*0x46);
+	// visible = _InGallery[_NewOrder[i]]; drawY = pos.y + (0x48 - pic.h)
+	// (bottom-aligned to baseline). Click ??? `_SearchSuspects` ??? moreInfo.
 	if (!_mystery.isLoaded())
 		return;
 
@@ -2732,21 +2402,17 @@ void EEMEngine::doGallery() {
 				}
 			}
 			if (ev.type == Common::EVENT_LBUTTONDOWN) {
-				// PDA bottom-bar buttons mirror `_NoteButtons @ 29be:0147`.
-				// `_DoGallery @ 158f:065b` shares the SAME button table
-				// with `_DoNotebook` (both call `_FindButton` against the
-				// 11-entry table at 0x147). `_HandleGalleryButton @
-				// 158f:05c0` dispatches via a different jump table
-				// (158f:0645). Verified gallery button mapping:
-				//   rect 0 (134,155) ??? 0x05ef = `_NextScreen = 4` (NOTEBOOK)
-				//   rect 1 (93,115)  ??? 0x0625 = `_InterfaceHelp` (HELP)
-				//   rect 2 (157,178) ??? 0x0638 = generic exit (no-op)
-				//   rect 3 (5,80)    ??? 0x061e = `_KDHelp` (host hint)
-				//   rect 4 (180,201) ??? 0x05ff = `_SolvedCheck` -> SOLVE
-				//   rect 5 (204,224) ??? 0x0638 = generic exit
-				//   rect 6 (226,247) ??? 0x0638 = generic exit
-				//   rect 7 (7,177)   ??? 0x05f7 = `_NextScreen = 2` (MAP)
-				//   rect 8 (35,111)  ??? 0x05e4 = `_NextScreen = 3` (SITE)
+				// Shares _NoteButtons @ 29be:0147 with doNotebook;
+				// _HandleGalleryButton @ 158f:05c0 jumptable @ 158f:0645:
+				//   [0] (134,155) ??? NOTEBOOK = NextScreen=4   (0x5ef)
+				//   [1] ( 93,115) ??? HELP = _InterfaceHelp(0)  (0x625)
+				//   [2] (157,178) ??? generic exit (no-op)      (0x638)
+				//   [3] (  5, 80) ??? _KDHelp (host hint)       (0x61e)
+				//   [4] (180,201) ??? _SolvedCheck ??? SOLVE      (0x5ff)
+				//   [5] (204,224) ??? generic exit              (0x638)
+				//   [6] (226,247) ??? generic exit              (0x638)
+				//   [7] (  7,177) ??? MAP = NextScreen=2        (0x5f7)
+				//   [8] ( 35,111) ??? SITE = NextScreen=3       (0x5e4)
 				if (kPdaSiteRect.contains(ev.mouse.x, ev.mouse.y)) {
 					_nextScreen = kScreenSite;
 					exitFlag = true;
@@ -2768,9 +2434,7 @@ void EEMEngine::doGallery() {
 					break;
 				}
 				if (kPdaHelpRect.contains(ev.mouse.x, ev.mouse.y)) {
-					// Gallery rect 1 ??? `_InterfaceHelp(0)` per jmp table at
-					// 158f:0625 (HandleGalleryButton). Same picture sequence
-					// as the notebook HELP button.
+					// rect 1 ??? `_InterfaceHelp(0)` (158f:0625).
 					setInteractiveMouseCursor(false);
 					doInterfaceHelp(0);
 					lastDraw = 0;
@@ -2782,8 +2446,7 @@ void EEMEngine::doGallery() {
 					lastDraw = 0;
 					continue;
 				}
-				// `_SearchSuspects` walks the per-slot rects and returns
-				// the suspect index. We mirror that with cached rects.
+				// `_SearchSuspects` ??? per-slot rect ??? suspect index.
 				bool clicked = false;
 				for (uint i = 0; i < slotRects.size(); i++) {
 					if (slotSuspect[i] < 0)
@@ -2792,9 +2455,6 @@ void EEMEngine::doGallery() {
 						if (moreInfo(gd, (uint)slotSuspect[i],
 									 galBg, haveBg))
 							exitFlag = true;
-						// Force gallery redraw immediately so the
-						// player isn't left looking at the dismissed
-						// MoreInfo screen until the next 100 ms tick.
 						drawGalleryFrame(gd, num, slotRects, slotSuspect);
 						lastDraw = g_system->getMillis();
 						mouse = g_system->getEventManager()->getMousePos();
@@ -2823,11 +2483,6 @@ void EEMEngine::doGallery() {
 									  gallerySlotAt(slotRects, slotSuspect,
 													mouse.x, mouse.y));
 		}
-		// `g_system->updateScreen()` is what tells the framework to
-		// re-render the cursor at its current mouse position; without
-		// it here, the cursor only refreshes when `drawGalleryFrame`
-		// runs (every 100 ms) and visibly lags the mouse. Match
-		// `doNotebook`'s per-tick `updateScreen()` cadence (line 1548).
 		g_system->updateScreen();
 		g_system->delayMillis(15);
 	}
@@ -2836,28 +2491,13 @@ void EEMEngine::doGallery() {
 
 bool EEMEngine::moreInfo(const byte *gd, uint suspectIdx,
 						  const Picture &galBg, bool haveBg) {
-	// Suspect-detail page. Mirrors `MoreInfo @ 158f:0419`:
-	//   _RefreshGalleryBackground();
-	//   _GetPicture(*(u16*)(gd + i*0x46));
-	//   _AddPicBackground(pic, 0x94, 0xf);
-	//   _DrawGalleryNotes(gd + i*0x46);
-	//   loop until ESC or button click.
-	// Suspect data layout differs by variant:
-	//   * CD (`158f:0419`): fixed 0x46-byte stride.
-	//     +0..1   picId
-	//     +8..9   clue count (u16)
-	//     +0xa..  array of u16 clue IDs (max 30,
-	//             terminated by 0xFFFF if short).
-	//   * Floppy (`_MoreInfo_Floppy = 154e:042b` ???
-	//     `FUN_154e_0201` ??? `FUN_15e0_01e8`):
-	//     variable-stride.
-	//     +0..1   picId
-	//     +2..3   alibi (0xFFFF = guilty)
-	//     +4      clue count (u8)
-	//     +5..    u8 clue IDs (per the asm at
-	//             154e:020e..0282 which calls
-	//             `FUN_15e0_01e8(rect, entry+5,
-	//                            entry[4], NULL)`).
+	// `MoreInfo @ 158f:0419` ??? suspect detail. PIC = entry+0,
+	// _AddPicBackground at (0x94, 0xf), notes via _DrawGalleryNotes.
+	// CD layout (0x46-byte stride): +0 picId, +8 clueCount(u16), +0xa
+	// clue IDs(u16, 0xFFFF terminator, max 30).
+	// Floppy (`_MoreInfo_Floppy = 154e:042b` ??? `FUN_15e0_01e8`):
+	// variable stride. +0 picId, +2 alibi (0xFFFF = guilty), +4 count(u8),
+	// +5 clue IDs(u8).
 	const bool floppyMI = isFloppy();
 	const byte *suspect = floppyMI
 							  ? _mystery.floppySuspectEntry(suspectIdx)
@@ -2871,10 +2511,8 @@ bool EEMEngine::moreInfo(const byte *gd, uint suspectIdx,
 
 	setInteractiveMouseCursor(false);
 
-	// Suspect's clue notes inside _GalleryNoteRect
-	// = (78, 93, 288, 152), per 29be:0100. Cyan text
-	// renders directly on the PDA's natural blue
-	// screen ??? matches `_DrawGalleryNotes @ 158f:01f4`.
+	// _GalleryNoteRect = (78, 93, 288, 152) @ 29be:0100.
+	// `_DrawGalleryNotes @ 158f:01f4`.
 	const int rx = 78, ry = 93;
 	const int rw = 288 - 78, rh = 152 - 93;
 	const int lineH = _font.getFontHeight();
@@ -2882,14 +2520,7 @@ bool EEMEngine::moreInfo(const byte *gd, uint suspectIdx,
 	const byte *ni = _mystery.noteIndex();
 	const uint16 niCount = _mystery.noteIndexCount();
 
-	// Pagination matches `_DrawNotes @ 161e:01d0` / `MoreInfo @
-	// 158f:0419`: the original tracks `_NextClue` across redraws and
-	// only emits clues whose wrapped height fits the rect, deferring
-	// the rest to the next page. `_PageBreaks[]` stores the
-	// entry-point for each page so PREV (button 6 / case 6 at
-	// 158f:03b8 ??? `SUB _CurrentPage, 2; _NextClue = _PageBreaks[...]`)
-	// can step back. We mirror that with an explicit stack of
-	// page-start indices: forward push, back pop.
+	// Pagination via `_NextClue` + `_PageBreaks[]` (case 6 @ 158f:03b8).
 	uint pageStart = 0;
 	Common::Array<uint> pageStack;
 	bool back = false;
@@ -2902,13 +2533,7 @@ bool EEMEngine::moreInfo(const byte *gd, uint suspectIdx,
 		ms.clear();
 		if (haveBg)
 			ms.simpleBlitFrom(galBg.surface);
-		// Partner sprite at (5, 0x50). The original `MoreInfo @
-		// 158f:0419` calls `_RefreshGalleryBackground` (clears the
-		// portrait grid) but the partner anim slot registered by
-		// `_DoGallery` keeps painting at every `_UpdateAnimations`
-		// tick ??? the suspect detail pic covers the right side only
-		// (drawn at 0x94, 0xf), so the partner stays visible on the
-		// left. Re-blit per page so the frame stays current.
+		// Partner sprite at (5, 0x50). Re-blitted per page.
 		{
 			const uint partnerAnim = (_partner == 0) ? 2 : 0x10;
 			Animation partnerAni;
@@ -2921,7 +2546,6 @@ bool EEMEngine::moreInfo(const byte *gd, uint suspectIdx,
 									  partnerAni[frameIdx], 5, 0x50);
 			}
 		}
-		// Full suspect picture at (0x94, 0xf).
 		Picture detail;
 		if (_picsArchive.getPicture(detailPic, detail)) {
 			const byte transp = (byte)(detail.flags >> 8);
@@ -2929,12 +2553,8 @@ bool EEMEngine::moreInfo(const byte *gd, uint suspectIdx,
 							 Common::Point(0x94, 0x0f), transp);
 		}
 
-		// Walk the clue list from `pageStart`. Skip clues that aren't
-		// found / lack a note entry, then measure the wrapped height
-		// before drawing. If a clue would overflow and we've already
-		// drawn at least one, defer it to the next page; if it's the
-		// FIRST clue on this page (one clue too tall to ever fit),
-		// draw it anyway so progress is guaranteed.
+		// Walk clues from pageStart; defer overflow to next page unless
+		// first clue is too tall to ever fit.
 		int yPos = ry;
 		bool drewAny = false;
 		uint k = pageStart;
@@ -2952,10 +2572,8 @@ bool EEMEngine::moreInfo(const byte *gd, uint suspectIdx,
 				continue;
 			if (!ni || clueId >= niCount)
 				continue;
-			// Floppy notes: 7-byte entries (+0..1 clue text, +2..3 Jake
-			// spoken, +4..5 Jenny spoken, +6 score). Notebook always
-			// uses +0 (partner-agnostic statement) per `FUN_15e0_01e8`.
-			// CD notes are 4-byte: u16 textOff + u16 score.
+			// Floppy: 7-byte entry (+0 text, +2 Jake, +4 Jenny, +6 score).
+			// CD: 4-byte entry (u16 textOff + u16 score).
 			Common::String txt;
 			if (floppyMI) {
 				const uint16 textOff = READ_LE_UINT16(ni + clueId * 7);
@@ -3006,9 +2624,7 @@ bool EEMEngine::moreInfo(const byte *gd, uint suspectIdx,
 					: "No clues yet for this suspect.",
 				rx, ry, MAX<int>(8, rw), 0x5C);
 		}
-		// Header / footer text. The PDA's NEXT (case 5) and PREV
-		// (case 6) icons are visible on the bottom bar ??? the footer
-		// just covers dismissal here.
+		// Header / footer text.
 		if (_font.isLoaded()) {
 			_font.drawString(&ms,
 				isSpanish() ? "EXPEDIENTE" : "SUSPECT FILE",
@@ -3021,9 +2637,7 @@ bool EEMEngine::moreInfo(const byte *gd, uint suspectIdx,
 			0, 0, 320, 200);
 		g_system->updateScreen();
 
-		// Drain the queued LBUTTONDOWN that brought us into MoreInfo
-		// so it doesn't get caught by the input loop below. Subsequent
-		// pages don't need this.
+		// Drain the LBUTTONDOWN that opened MoreInfo (first page only).
 		if (isFirstShow) {
 			isFirstShow = false;
 			g_system->delayMillis(150);
@@ -3037,22 +2651,10 @@ bool EEMEngine::moreInfo(const byte *gd, uint suspectIdx,
 			}
 		}
 
-		// Wait for input. Each PDA button maps to a distinct action,
-		// mirroring `_HandleMoreButton @ 158f:027d`:
-		//   case 0 NOTEBOOK    -> NextScreen=4
-		//   case 1 HELP        -> _InterfaceHelp(0)
-		//   case 2 GALLERY     -> close MoreInfo
-		//   case 3 PARTNER head-> _KDHelp + redraw
-		//   case 4 ACCUSE      -> NextScreen=7
-		//   case 5 PAGE NEXT   -> next page
-		//   case 6 PAGE PREV   -> prev page
-		//   case 7 MAP         -> NextScreen=2
-		//   case 8 SITE        -> no-op
-		//   case 10 HELP (alt) -> _InterfaceHelp(0)
-		// Clicks outside any known button rect are no-ops, matching
-		// the original (which short-circuits on `_FindButton == -1`).
-		// ESC closes; PgDn / PgUp / Return / Backspace also paginate
-		// as a modern accessibility convenience.
+		// `_HandleMoreButton @ 158f:027d`:
+		//   [0] NOTEBOOK???4, [1] HELP???_InterfaceHelp, [2] GALLERY (close),
+		//   [3] _KDHelp, [4] ACCUSE???7, [5] PAGE_NEXT, [6] PAGE_PREV,
+		//   [7] MAP???2, [8] SITE noop, [10] HELP alt.
 		bool advance = false;
 		bool prev = false;
 		bool redraw = false;
@@ -3096,16 +2698,13 @@ bool EEMEngine::moreInfo(const byte *gd, uint suspectIdx,
 					}
 					if (kPdaHelpRect.contains(mx, my) ||
 						kPdaHelp2Rect.contains(mx, my)) {
-						// Help overlay; re-render the current page
-						// after it dismisses.
 						setInteractiveMouseCursor(false);
 						doInterfaceHelp(0);
 						redraw = true;
 						break;
 					}
 					if (kPdaGalleryRect.contains(mx, my)) {
-						// Case 2: close MoreInfo and return to the
-						// portrait grid.
+						// Case 2: close MoreInfo.
 						back = true;
 						break;
 					}
@@ -3120,16 +2719,13 @@ bool EEMEngine::moreInfo(const byte *gd, uint suspectIdx,
 						break;
 					}
 					if (kPdaPartnerHeadHintRect.contains(mx, my)) {
-						// Case 3: ask KD for a hint then redraw the
-						// current page over the clean BG.
+						// Case 3: _KDHelp.
 						setInteractiveMouseCursor(false);
 						doHelp();
 						redraw = true;
 						break;
 					}
-					// Case 8 SITE and click outside any known button
-					// = no-op. The original `_FindButton`
-					// short-circuits when no rect matches.
+					// Case 8 SITE / non-button = no-op.
 					break;
 				}
 				if (e2.type == Common::EVENT_KEYDOWN && hasMore &&
@@ -3149,9 +2745,6 @@ bool EEMEngine::moreInfo(const byte *gd, uint suspectIdx,
 					break;
 				}
 			}
-			// Per-tick `updateScreen()` so the SDL cursor follows the
-			// mouse ??? without it the cursor freezes on entry to the
-			// MoreInfo screen.
 			g_system->updateScreen();
 			g_system->delayMillis(20);
 		}
@@ -3163,9 +2756,6 @@ bool EEMEngine::moreInfo(const byte *gd, uint suspectIdx,
 			pageStart = pageStack.back();
 			pageStack.pop_back();
 		}
-		// `redraw` falls through with the same `pageStart` and
-		// re-iterates the outer while, repainting on top of whatever
-		// the help overlay left behind.
 	}
 
 	return exitGallery;
@@ -3293,32 +2883,15 @@ void EEMEngine::drawGalleryFrame(const byte *gd, uint8 numSuspects,
 }
 
 void EEMEngine::doBigMap() {
-	// Two-stage flow that mirrors the original screen-1 wrapper at
-	// 20fe:120b and `_DoBigMap @ 20fe:09e7`:
-	//
-	//   STAGE 1 ??? Overview. PIC 0x42 + site icons drawn via the
-	//   `_DrawBigMapButtons` algorithm at BigMap coords MapData[+4/+6].
-	//   The original `_DoBigMap` returns sx/sy = (mouseX*2 - 0x74,
-	//   mouseY*2 - 0x55) when the player clicks inside `BigMapWindow`,
-	//   which is the scroll position into the SmallMap.
-	//
-	//   STAGE 2 ??? Detail zoom. PIC 0x43 frame + a 0xe9 ?? 0xab viewport
-	//   into BIGMAP.PIC at (2, 2), drawn by `DrawMap @ 20fe:1058` with
-	//   the (sx, sy) returned from stage 1. Site icons are stamped at
-	//   SmallMap coords MapData[+8/+0xa] via `_StampButtons`. Click on
-	//   a site icon ??? travel.
-	//
-	// MapData entry layout (14 bytes), verified directly from the
-	// disassembly of `_DrawBigMapButtons @ 20fe:0877` (`PUSH ES:[BX+4]`
-	// for X, `PUSH ES:[BX+6]` for Y, `CMP ES:[BX+0xc], 0` for crime)
-	// and `_StampButtons @ 20fe:0d2f` (`MOV AX, ES:[BX+8]`,
-	// `MOV AX, ES:[BX+0xa]`):
-	//   +0..3   ??? (not yet decoded)
-	//   +4..5   BigMap X
-	//   +6..7   BigMap Y
-	//   +8..9   SmallMap X
-	//   +0xa..b SmallMap Y
-	//   +0xc..d crime-flag
+	// `_DoBigMap @ 20fe:09e7` two stage:
+	//   Stage 1 (Overview): PIC 0x42 + site icons at MapData[+4/+6]
+	//     (`_DrawBigMapButtons @ 20fe:0877`). Click in BigMapWindow
+	//     returns scroll (mouseX*2 - 0x74, mouseY*2 - 0x55).
+	//   Stage 2 (Detail): PIC 0x43 frame + 0xe9??0xab BIGMAP.PIC viewport
+	//     at (2,2). Icons stamped at MapData[+8/+0xa] (`_StampButtons @
+	//     20fe:0d2f`). Click icon = travel.
+	// MapData entry (14 bytes): +0..3 ???, +4 BigMapX, +6 BigMapY,
+	//   +8 SmallMapX, +0xa SmallMapY, +0xc crime-flag.
 
 	if (!_mystery.isLoaded())
 		return;
@@ -3327,35 +2900,20 @@ void EEMEngine::doBigMap() {
 
 	while (!shouldQuit()) {
 		setInteractiveMouseCursor(false);
-		setSitePalette(0x24); // `_GetPalette(0x24)` per `_DoBigMap @ 20fe:09e7`.
+		setSitePalette(0x24); // `_GetPalette(0x24)` @ `_DoBigMap`.
 
-		// ------------------------------------------------------------------
-		// STAGE 1 ??? Overview: PIC 0x42 + clickable site icons.
-		// ------------------------------------------------------------------
-
-		// Anchor for the partner-sprite timeline. `_DoBigMap`'s
-		// `_NewAnimation` call seeds the slot's frame index to 0xffff so
-		// the first `_UpdateAnimations` tick starts at script[0]; we mirror
-		// that by passing elapsed-since-open (zero on the first paint) into
-		// `bigMapPartnerFrameAtTick`, which plays the unfold once and then
+		// Stage 1: Overview. mapStartTick anchors partner timeline;
+		// `_NewAnimation` seeds frame to 0xffff so unfold plays once then
 		// loops the wait sequence.
 		const uint32 mapStartTick = g_system->getMillis();
 		drawBigMapOverview(0);
 		uint32 mapLastTick = mapStartTick;
 
-		// Static rectangles read directly from the binary at the labelled
-		// addresses (CD `29be:0x1596` / floppy `2608:0x13fe..0x143e`).
-		// Format is {x1, y1, x2, y2}. The floppy click table at
-		// `2608:1436` (verified at `_BigMapInteractionLoop_Floppy @
-		// 1fed:0a3a`) puts the setup button at (251, 3, 315, 42) ??? 1 px
-		// up and 1 px left of the CD's (252, 4, 315, 42). The floppy
-		// PIC 0x42 BG paints the visible button border at the same
-		// pixels, so use the variant-specific rect to match the
-		// hit-test region the original uses for that variant.
-		const Common::Rect kBigMapWindow(0, 0, 247, 192); // 29be:1596
+		// Rects @ CD 29be:1596 / floppy 2608:13fe (1 px diff on Setup).
+		const Common::Rect kBigMapWindow(0, 0, 247, 192);
 		const Common::Rect kSetupBtnRect = isFloppy()
-			? Common::Rect(251, 3, 315, 42)   // 2608:1436
-			: Common::Rect(252, 4, 315, 42);  // 29be:15ce
+			? Common::Rect(251, 3, 315, 42)
+			: Common::Rect(252, 4, 315, 42);
 
 		bool wantZoom = false;
 		int zoomX = 0;
@@ -3372,18 +2930,12 @@ void EEMEngine::doBigMap() {
 					continue;
 				}
 				if (ev.type == Common::EVENT_LBUTTONDOWN) {
-					// SetupButtonRect ??? `_NextScreen = 6` (the original's
-					// settings screen, mirrors `_DoBigMap @ 20fe:0c33`
-					// where it pushes `_PressButton` then writes
-					// `_NextScreen = 6`). Now wired to the actual
-					// `doSetup` handler instead of dropping the player
-					// out to the launcher.
+					// Setup ??? NextScreen=6 (`_DoBigMap @ 20fe:0c33`).
 					if (kSetupBtnRect.contains(ev.mouse.x, ev.mouse.y)) {
 						_nextScreen = kScreenSetup;
 						return;
 					}
-					// Click in the BigMapWindow ??? zoom. Original formula:
-					//   sx = mouseX*2 - 0x74; sy = mouseY*2 - 0x55
+					// BigMapWindow click ??? zoom (sx=x*2-0x74, sy=y*2-0x55).
 					if (kBigMapWindow.contains(ev.mouse.x, ev.mouse.y)) {
 						int sx = ev.mouse.x * 2;
 						int sy = ev.mouse.y * 2;
@@ -3399,8 +2951,7 @@ void EEMEngine::doBigMap() {
 			if (wantZoom)
 				break;
 
-			// Cycle the partner-sprite frame every 100 ms (matching the
-			// original's `_CheckFrameRate` cadence inside `_DoBigMap`).
+			// `_CheckFrameRate` cadence ??? 100 ms.
 			const uint32 now = g_system->getMillis();
 			if (now - mapLastTick >= 100) {
 				mapLastTick = now;
@@ -3415,11 +2966,7 @@ void EEMEngine::doBigMap() {
 		if (!wantZoom)
 			return;
 
-		// ------------------------------------------------------------------
-		// STAGE 2 ??? Detail zoom: PIC 0x43 frame + scrollable BIGMAP.PIC
-		// viewport at (2, 2), 0xe9 ?? 0xab. Click on a stamped icon ??? travel.
-		// ------------------------------------------------------------------
-
+		// Stage 2: Detail. PIC 0x43 + BIGMAP.PIC viewport.
 		Common::File f;
 		if (!f.open(Common::Path("BIGMAP.PIC"))) {
 			warning("doBigMap: BIGMAP.PIC missing for detail view");
@@ -3443,19 +2990,13 @@ void EEMEngine::doBigMap() {
 		int scrollX = MAX<int>(0, MIN<int>(mapW - kMapWinW, zoomX));
 		int scrollY = MAX<int>(0, MIN<int>(mapH - kMapWinH, zoomY));
 
-		// Anchor the detail-screen partner timeline (mirrors `_DoMapScreen`'s
-		// `_NewAnimation` seeding the slot's frame index to 0xffff). The
-		// unfold (script 0x13) plays once, then `_SmallMapWaitSeq` loops.
+		// Detail partner timeline (script 0x13 unfold, then wait seq).
 		const uint32 detailStartTick = g_system->getMillis();
 		drawBigMapDetail(scrollX, scrollY, mapPixels, mapW, mapH, 0);
 		uint32 detailLastTick = detailStartTick;
 		bool returnToOverview = false;
 
-		// `SmallMapButtons[4]` is the large right-side panel below setup:
-		// CD/floppy both store `(252, 43, 320, 200)`. Its handler at
-		// CD `20fe:156c` kills the zoom animation, sets `_NextScreen = 1`
-		// and calls `_DoBigMap` again, so mouse players can return to the
-		// overview without leaving the map screen.
+		// `SmallMapButtons[4]` @ 20fe:156c ??? return to overview.
 		const Common::Rect kBigMapReturnRect(252, 43, 320, 200);
 		const Common::Rect kArrowYUp(237, 2, 247, 11);
 		const Common::Rect kArrowYDown(237, 163, 247, 172);
@@ -3464,8 +3005,8 @@ void EEMEngine::doBigMap() {
 		const Common::Rect kXSlider(15, 175, 221, 185);
 		const Common::Rect kYSlider(237, 14, 247, 160);
 		const Common::Rect kDetailSetupBtn = isFloppy()
-			? Common::Rect(251, 3, 315, 42)   // 2608:1436
-			: Common::Rect(252, 4, 315, 42);  // 29be:15ce
+			? Common::Rect(251, 3, 315, 42)
+			: Common::Rect(252, 4, 315, 42);
 		const int kArrowStep = 16;
 		const int kSliderRange = mapW - kMapWinW;
 		const int kSliderRangeY = mapH - kMapWinH;
@@ -3516,17 +3057,7 @@ void EEMEngine::doBigMap() {
 						kBigMapReturnRect.contains(ev.mouse.x, ev.mouse.y) ||
 						kDetailSetupBtn.contains(ev.mouse.x, ev.mouse.y));
 					if (kDetailSetupBtn.contains(ev.mouse.x, ev.mouse.y)) {
-						// `_DoMapScreen @ 20fe:1560` writes `_NextScreen
-						// = 6` (= kScreenSetup) and `INC [BP-8]` to bail
-						// out of the detail loop ??? verified via the byte
-						// search for `c7 06 16 79 06 00`, which finds the
-						// imm at exactly this site and `_DoBigMap @
-						// 20fe:0c33`. Same `SetupButtonRect @ 29be:15ce`
-						// rect used by both the overview and the detail
-						// (no per-screen rect duplication in the binary).
-						// The detail/zoom state is lost on return because
-						// the screen driver re-enters BigMap at stage 1 ???
-						// this matches the original behaviour.
+						// `_DoMapScreen @ 20fe:1560` ??? NextScreen=6.
 						_nextScreen = kScreenSetup;
 						setInteractiveMouseCursor(false);
 						return;
@@ -3549,8 +3080,6 @@ void EEMEngine::doBigMap() {
 							scrollX + kArrowStep);
 						dirty = true;
 					} else if (kXSlider.contains(ev.mouse.x, ev.mouse.y)) {
-						// Click on X slider track ??? jump scrollX so the
-						// click position maps proportionally into the map.
 						if (kSliderRange > 0) {
 							const int t = ev.mouse.x - kXSlider.left;
 							const int tw = kXSlider.width();
@@ -3570,9 +3099,7 @@ void EEMEngine::doBigMap() {
 							   ev.mouse.x < kMapWinX + kMapWinW &&
 							   ev.mouse.y >= kMapWinY &&
 							   ev.mouse.y < kMapWinY + kMapWinH) {
-						// Hit-test the per-site button at its actual bbox
-						// (`_StampButtons` records the rect at SmallMap +8/+0xa
-						// with the button PIC's width/height).
+						// Per-site bbox from `_StampButtons` (SmallMap +8/+0xa).
 						const bool fmap = _mystery.isLoaded() && isFloppy();
 						for (uint i = 0; i < _mystery.numSites(); i++) {
 							if (!_mystery._onSites[i] &&
@@ -3585,10 +3112,8 @@ void EEMEngine::doBigMap() {
 							uint16 my;
 							uint16 buttonId;
 							if (fmap) {
-								// Floppy detail view: click rect on
-								// BIGMAP.PIC at (+0, +2), labelled BUTTON.DBD
-								// entry ID at entry+4 (per
-								// `FUN_1fed_0c3e @ 1fed:0c3e`).
+								// Floppy: rect +0/+2; BUTTON.DBD id at +4
+								// (`FUN_1fed_0c3e @ 1fed:0c3e`).
 								mx = READ_LE_UINT16(entry + 0x0);
 								my = READ_LE_UINT16(entry + 0x2);
 								buttonId = (uint16)entry[0x4];
@@ -3620,8 +3145,7 @@ void EEMEngine::doBigMap() {
 			if (returnToOverview)
 				break;
 
-			// Cycle the partner sprite at 100 ms ticks (same cadence as
-			// `_DoMapScreen`'s `_CheckFrameRate` + `_UpdateAnimations` loop).
+			// 100 ms partner cycle.
 			const uint32 now = g_system->getMillis();
 			if (now - detailLastTick >= 100) {
 				detailLastTick = now;
@@ -3639,20 +3163,9 @@ void EEMEngine::doBigMap() {
 }
 
 void EEMEngine::drawBigMapOverview(uint32 elapsedMs) {
-	// Map-overview redraw ??? formerly the `drawOverview` lambda inside
-	// `doBigMap`. PIC 0x42 frame + per-site marker (Done / Crime / Site
-	// per `_DrawBigMapButtons @ 20fe:0877`) + the partner idle sprite.
-	// `_DoBigMap @ 20fe:09e7` (`_NewAnimation` block at 20fe:0a44-0a99)
-	// registers the partner: when `_LastScreen == 2` it plays an
-	// entrance one-shot at (0x102, 0x50) and on END swaps to the idle
-	// at (0xfd, 0x50). We don't track LastScreen finely enough so we
-	// always render the IDLE pose at (0xfd, 0x50). Idle anim ID:
-	// Jake = 0x14 (20), Jenny = 0x12 (18).
-	//
-	// `elapsedMs` is the time since `doBigMap` opened ??? the partner-sprite
-	// timeline anchor. `bigMapPartnerFrameAtTick` uses it to play the
-	// unfold script (0..8) once, then loop `_BigMapWaitSeq` (the open-map
-	// hold). Without that anchor the unfold would loop indefinitely.
+	// PIC 0x42 + per-site Done/Crime/Site marker (`_DrawBigMapButtons @
+	// 20fe:0877`) + partner idle at (0xfd, 0x50). Idle ANI: Jake=0x14,
+	// Jenny=0x12. elapsedMs anchors unfold???wait timeline.
 	Graphics::ManagedSurface scratch(320, 200,
 		Graphics::PixelFormat::createFormatCLUT8());
 	scratch.clear();
@@ -3661,14 +3174,9 @@ void EEMEngine::drawBigMapOverview(uint32 elapsedMs) {
 	if (_picsArchive.getPicture(0x42, frame))
 		scratch.simpleBlitFrom(frame.surface);
 
-	// Marker PICs from `_main @ 1a35:0f59`. Three globals are filled
-	// once at boot via `_GetPicture` (1-based IDs):
-	//   _DoneMarker  = PIC 0x20d  (already-searched site)
-	//   _SiteMarker  = PIC 0xc5   (default available site)
-	//   _CrimeMarker = PIC 0xc6   (crime-scene flag set)
-	// Floppy's `PICS.DBX` has 524 entries, so PIC 0x20d is CD-only.
-	// Its pixels are the same 7x8 marker as PIC 0xc5, with the animated
-	// 0xfb/0xfc interior remapped to static 0x1b/0x19 blue.
+	// Marker PICs from `_main @ 1a35:0f59`:
+	//   _DoneMarker = 0x20d, _SiteMarker = 0xc5, _CrimeMarker = 0xc6.
+	// 0x20d is CD-only (floppy PICS.DBX has 524 entries).
 	Picture done;
 	Picture normal;
 	Picture crimeM;
@@ -3715,27 +3223,19 @@ void EEMEngine::drawBigMapOverview(uint32 elapsedMs) {
 			blitBigMapMarker(scratch, *m, (int)mx, (int)my,
 							  useVisitedColors);
 		} else {
-			// Fallback if the markers couldn't be loaded.
 			const Common::Rect mark(mx - 3, my - 3, mx + 4, my + 4);
 			scratch.fillRect(mark, 0x0F);
 		}
 	}
 
-	// Partner idle sprite at (0xfd, 0x50). Jake = anim 0x14, Jenny = 0x12
-	// for the loaded CELLS, but the original `_DoBigMap @ 20fe:0a47`
-	// always passes `CONCAT22(0x14, ...)` to `_NewAnimation` so the
-	// SCRIPT key is 0x14 (`[0..8]` count-up) regardless of partner.
-	// Without this, Jenny was running 0x12's count-DOWN script
-	// `[8..0]` over her cells ??? visually backwards from the original.
+	// Partner idle at (0xfd, 0x50). `_DoBigMap @ 20fe:0a47` always passes
+	// script 0x14 (count-up) to `_NewAnimation` regardless of partner.
 	const uint kMapAniId = (_partner == 0) ? 0x14 : 0x12;
 	Animation mapAnim;
 	if (_aniArchive.loadAnimation(kMapAniId, mapAnim) && !mapAnim.empty()) {
 		const uint frameIdx = bigMapPartnerFrameAtTick((uint)mapAnim.size(),
 													   elapsedMs);
-		// Anchor-aware: the BigMap walk-cycle has miscflags = -2 per
-		// cell, so the partner shifts left as it cycles ??? without the
-		// anchor adjustment the sprite "shakes in place" instead of
-		// walking forward.
+		// BigMap walk-cycle miscflags = -2 (anchor shift left).
 		blitAnimFrameAnchored(scratch.surfacePtr(), mapAnim[frameIdx],
 							  0xfd, 0x50);
 	}
@@ -3749,15 +3249,9 @@ void EEMEngine::drawBigMapDetail(int scrollX, int scrollY,
 								 const Common::Array<byte> &mapPixels,
 								 uint16 mapW, uint16 mapH,
 								 uint32 elapsedMs) {
-	// Map-detail redraw ??? formerly the `drawDetail` lambda inside
-	// `doBigMap`. PIC 0x43 frame + a 0xe9 ?? 0xab BIGMAP.PIC viewport at
-	// (2, 2), stamped site buttons, and the partner sprite at (0x101,
-	// 0x50) ??? `_DoMapScreen @ 20fe:120b` (`_NewAnimation` at
-	// 20fe:12cd-12f0, anim 0x13 Jake / 0x11 Jenny, seqnum 0x13).
-	//
-	// `elapsedMs` is the time since the detail screen was opened ???
-	// `bigMapDetailPartnerFrameAtTick` uses it to play the unfold once
-	// and then loop `_SmallMapWaitSeq`.
+	// PIC 0x43 + 0xe9??0xab BIGMAP.PIC viewport at (2,2), stamped buttons,
+	// partner at (0x101, 0x50). `_DoMapScreen @ 20fe:120b` (ANI 0x13 Jake
+	// / 0x11 Jenny, seq 0x13). elapsedMs anchors unfold???wait.
 	const int kMapWinW = 0xe9;
 	const int kMapWinH = 0xab;
 	const int kMapWinX = 2;
@@ -3776,17 +3270,8 @@ void EEMEngine::drawBigMapDetail(int scrollX, int scrollY,
 	scratch.copyRectToSurface(mapPixels.data() + scrollY * mapW + scrollX,
 							  mapW, kMapWinX, kMapWinY, copyW, copyH);
 
-	// Stamped site buttons. `_StampButtons @ 20fe:0d2f` (CD):
-	//   button = _GetButton(MapData[+0])
-	//   destX  = MapData[+8]
-	//   destY  = MapData[+0xa]
-	// Floppy uses `FUN_1fed_0c3e @ 1fed:0c3e`: for each SITES row, the
-	// byte at entry+4 is a BUTTON.DBD entry ID (loaded via
-	// `FUN_16e2_1838 @ 16e2:1838`, which opens `button.dbd` ??? string
-	// at `2608:0558`). The labelled button is stamped at
-	// `(entry+0..1, entry+2..3)` on BIGMAP.PIC. These are the same
-	// per-site labelled buttons the CD uses, just keyed off a
-	// different field offset.
+	// `_StampButtons @ 20fe:0d2f` (CD): button=MapData[+0], dst=+8/+0xa.
+	// Floppy `FUN_1fed_0c3e @ 1fed:0c3e`: button.dbd id at +4, rect +0/+2.
 	const bool floppyMap = _mystery.isLoaded() && isFloppy();
 	for (uint i = 0; i < _mystery.numSites(); i++) {
 		if (!_mystery._onSites[i] && i != _mystery._siteNumber)
@@ -3830,12 +3315,7 @@ void EEMEngine::drawBigMapDetail(int scrollX, int scrollY,
 		}
 	}
 
-	// Partner sprite on the detail map (drawn last to sit over the
-	// frame and the BIGMAP.PIC viewport). The original always passes
-	// `CONCAT22(0x13, ...)` to `_NewAnimation` (i.e. script ID 0x13)
-	// regardless of partner ??? verified at `_DoBigMap @ 20fe:0a47`.
-	// So we look up script 0x13 for both partners while still
-	// loading the partner-specific CELLS via `kDetailAniId`.
+	// Always script 0x13 (`_NewAnimation @ _DoBigMap 20fe:0a47`).
 	const uint kDetailAniId = (_partner == 0) ? 0x13 : 0x11;
 	Animation detailAnim;
 	if (_aniArchive.loadAnimation(kDetailAniId, detailAnim) &&
@@ -3852,46 +3332,24 @@ void EEMEngine::drawBigMapDetail(int scrollX, int scrollY,
 }
 
 uint16 EEMEngine::getKDTextBalloon(byte firstChar) const {
-	// Mirrors `_GetKDTextBalloon @ 1df2:0105`:
+	// `_GetKDTextBalloon @ 1df2:0105`:
 	//   if ((ctype[firstChar] & 2) == 0)  bub = *(u16*)29be:1068 = 0x17
 	//   else                              bub = *(u16*)(29be:0fe6+0x1e+c*2)
-	// `ctype` is Borland's `_ctype_` array at `29be:2be1`. Bit 1 (0x02) is
-	// set only for digits '0'..'9' (verified by reading the table ??? '0'..'9'
-	// each map to byte 0x02; everything else has bit 1 clear).
-	// Lookup table at 29be:1064 (= 29be:0fe6 + 0x1e + '0'*2):
-	//   '0'???0x15  '1'???0x16  '2'???0x17  '3'???0x18  '4'???0x19
-	//   '5'???0x1a  '6'???0x20  '7'???0x21  '8'???0x22  '9'???0x1e
-	// Note `*(u16*)29be:1068` (= entry for '2') is the same byte the
-	// non-digit fallback returns ??? the original encodes the constant by
-	// reusing the digit-2 slot.
+	// `ctype` is Borland's `_ctype_` at 29be:2be1; bit 1 (0x02) is set
+	// only for '0'..'9'. Digit table @ 29be:1064 (see kDigitBalloons).
+	// `*(u16*)29be:1068` = entry for '2' = 0x17 ??? original reuses the
+	// digit-2 slot as the non-digit fallback constant.
 	if (firstChar < '0' || firstChar > '9')
 		return 0x17;
-	// `kDigitBalloons` lives at file scope above.
 	return kDigitBalloons[firstChar - '0'];
 }
 
 bool EEMEngine::doAccuseNotes() {
-	// Mirrors the accuse-notes screen at the head of `_DoAccuse @
-	// 1df2:0bdd`:
-	//   * BG: PIC 0x1A7 (the red "accuse-mode" backdrop).
-	//   * `_AccuseNoteRect @ 29be:1048` = (79, 27, 304, 159) holds
-	//     the rendered clue list.
-	//   * Counter at `(0xd1, 0xb)` = `(209, 11)` shows "N clue(s)"
-	//     remaining (`_UpdateSelectionCount @ 1df2:08dd`,
-	//     `_Show_String(0xb, 0xd1, ...)`).
-	//   * Expected count = `6 - DAT_2d5d_3f99`:
-	//       chainStage 1 ??? 5 clues, 2 ??? 4 clues, 3 ??? 3 clues.
-	//   * `_NoteUnselectedColor = 1` (red) for unselected, `0x3c`
-	//     for selected (1df2:0c2c sets it on entry).
-	//   * Click on a clue toggles its selection
-	//     (`_SearchNoteAreas` + `_SwapColors`).
-	//   * Click `_NoteButtons[4]` (rect at `(180, 174, 201, 190)`,
-	//     the original's solve button) jumps to the evidence check;
-	//     `_HandleAccuseNoteButton(4)` returns 2 and the outer loop
-	//     forces `uStack_8 = uStack_a` to trigger `_SolvedCheck`.
-	//   * CD and floppy originals only cancel this screen through ESC.
-	//     ScummVM also wires the visible PDA navigation buttons so the
-	//     accusation can be abandoned without a keyboard.
+	// `_DoAccuse @ 1df2:0bdd` head. BG PIC 0x1A7. `_AccuseNoteRect @
+	// 29be:1048` = (79, 27, 304, 159). Counter @ (209, 11) shows
+	// `6 - chainStage` (stage 1=5, 2=4, 3=3 clues). Unselected color 1
+	// (red), selected 0x3c. Click toggles selection; `_NoteButtons[4]`
+	// (180,174,201,190) SOLVE ??? `_HandleAccuseNoteButton` returns 2.
 	if (!_mystery.isLoaded() || !_font.isLoaded())
 		return false;
 	const byte *ni = _mystery.noteIndex();
@@ -3902,49 +3360,31 @@ bool EEMEngine::doAccuseNotes() {
 	Picture accuseBg;
 	const bool haveBg = _picsArchive.getPicture(0x1a7, accuseBg);
 
-	// Reset selection on entry. `FUN_1d40_0e07 @ 1d40:0e34` (floppy)
-	// explicitly zeroes the 0x7f-byte `_NoteSelected_Floppy` array
-	// before drawing, so the player always starts with a clean board.
-	// Without this, leftover selections from a failed attempt persist
-	// and the post-selection 100-point gate gets the wrong sum.
+	// `FUN_1d40_0e07 @ 1d40:0e34` zeroes _NoteSelected_Floppy on entry.
 	memset(_mystery._noteSelected, 0, sizeof(_mystery._noteSelected));
 
-	// Required count for solving ??? `6 - chainStage`.
 	const uint expected = (_chainStage >= 1 && _chainStage <= 3)
 		? (uint)(6 - _chainStage)
 		: 5;
 
-	// Build the list of FOUND clue IDs (in clue-ID order; the
-	// original's `_DrawNotes(NULL, 100, ...)` walks `_CluesFound[]`
-	// the same way).
+	// `_DrawNotes(NULL, 100, ...)` walks `_CluesFound[]`.
 	Common::Array<uint> found;
 	for (uint i = 0; i < niCount && i < Mystery::kCluesFoundCap; i++) {
 		if (_mystery._cluesFound[i] && _mystery.noteHasNotebookText(i))
 			found.push_back(i);
 	}
 
-	// `_AccuseNoteRect` (79, 27, 304, 159) ??? text wrap area.
 	const int rectX = 79;
 	const int rectY = 27;
 	const int rectW = 304 - 79;
 	const int rectH = 159 - 27;
 
-	// `_NoteButtons` rects (verified at `29be:0147`). `_DoAccuse`
-	// re-uses the same table as `_DoNotebook`, but the original
-	// handler only routes SOLVE / PAGE NEXT / PAGE PREV; ScummVM
-	// additionally routes the visible site/map/notebook/gallery
-	// buttons below for pointer-only cancellation.
-	// `_HandleAccuseNoteButton @ 1df2:0990` returns `DI` (initialised
-	// to 0) and only sets `DI = 2` in the `i == 4` branch (asm:
-	// `1df2:09b2: MOV DI, 0x2`). The outer loop's `iVar6 == 2` test
-	// at `1df2:0db2` is checking the HANDLER'S RETURN VALUE, not the
-	// button INDEX ??? earlier comment had this backwards. So the
-	// SOLVE rect is `[4]` (180, 174, 201, 190), the same icon the
-	// PDA uses to trigger the accuse flow in the first place.
-	const Common::Rect kBtnSolve   (180, 174, 201, 190); // [4] SOLVE
-	const Common::Rect kBtnPageNext(204, 174, 224, 190); // [5] PAGE NEXT
-	const Common::Rect kBtnPagePrev(226, 174, 247, 190); // [6] PAGE PREV
-	const Common::Rect kBtnPartner (  5,  80,  44, 110); // [3] KD HELP
+	// `_NoteButtons` @ 29be:0147. `_HandleAccuseNoteButton @ 1df2:0990`
+	// returns DI=2 for i==4 (SOLVE).
+	const Common::Rect kBtnSolve   (180, 174, 201, 190); // [4]
+	const Common::Rect kBtnPageNext(204, 174, 224, 190); // [5]
+	const Common::Rect kBtnPagePrev(226, 174, 247, 190); // [6]
+	const Common::Rect kBtnPartner (  5,  80,  44, 110); // [3]
 
 	// Per-page slot rects + their clue IDs (for click hit-testing).
 	Common::Array<Common::Rect> slotRects;
@@ -3955,20 +3395,9 @@ bool EEMEngine::doAccuseNotes() {
 	int numPages = 1;
 	pageBreaks[0] = 0;
 
-	// Variant-aware text resolver. CD note entries are 4 bytes
-	// (u16 textOff RELATIVE to TextBlock, u16 score), so the offset
-	// is added to `_textOffset` via `Mystery::textAt`. Floppy
-	// entries are 7 bytes:
-	//   +0..1  clue text offset (ABSOLUTE byte offset into the
-	//          mystery blob ??? partner-agnostic clue statement;
-	//          this is what the notebook displays).
-	//   +2..3  Jake spoken-line offset (`FUN_22dc_05c8 @ 22dc:0843`).
-	//   +4..5  Jenny spoken-line offset.
-	//   +6     score (`FUN_1d40_0c48`).
-	// `_DrawNotes_Floppy / FUN_15e0_01e8` reads `*(int *)(notes +
-	// idx * 7)` ??? always byte +0 ??? for the notebook text.
-	// Reading +2/+4 here showed the partner's "Hi, Jake! Hi,
-	// Jenny!" intro instead of the actual clue statement.
+	// CD note: 4 bytes (u16 textOff rel TextBlock, u16 score).
+	// Floppy: 7 bytes (+0 abs textOff, +2 Jake, +4 Jenny, +6 score).
+	// Notebook always uses +0 (`FUN_15e0_01e8`).
 	const bool floppyNote = isFloppy();
 	const byte *bufBaseNotes = _mystery.blobAt(0);
 	auto noteText = [&](uint clueId) -> Common::String {
@@ -4020,14 +3449,8 @@ bool EEMEngine::doAccuseNotes() {
 		if (haveBg)
 			scratch.simpleBlitFrom(accuseBg.surface);
 
-		// Partner sprite at (5, 0x50). The original `_DoAccuse @
-		// 1df2:0c2c` does `_NewAnimation(5, 0x50, partnerCells,
-		// script=2, prior=1)` ??? same anim cells as the gallery
-		// (anim 2 for Jake / 0x10 for Jenny) with script 0x02. The
-		// `_UpdateAnimations` loop in `_DoAccuse @ 1df2:0bfa` keeps
-		// the slot painting through the entire selection screen;
-		// without an explicit blit here the player sees a partner-
-		// less accuse-mode screen.
+		// Partner at (5, 0x50). `_DoAccuse @ 1df2:0c2c`: ANI 2/0x10,
+		// script 2, prior 1.
 		const uint partnerAnim = (_partner == 0) ? 2 : 0x10;
 		Animation partnerAni;
 		if (_aniArchive.loadAnimation(partnerAnim, partnerAni) &&
@@ -4039,10 +3462,7 @@ bool EEMEngine::doAccuseNotes() {
 								  partnerAni[frameIdx], 5, 0x50);
 		}
 
-		// Clue list inside `_AccuseNoteRect`. Selected = 0x3c (yellow),
-		// unselected = 1 (red), per `_NoteUnselectedColor = 1` set at
-		// 1df2:0c25 ??? the red colour is what gives the screen its
-		// "accuse-mode" look together with PIC 0x1A7.
+		// Selected=0x3c, unselected=1 (`_NoteUnselectedColor` @ 1df2:0c25).
 		slotRects.clear();
 		slotClues.clear();
 		const int lineH = _font.getFontHeight();
@@ -4078,11 +3498,11 @@ bool EEMEngine::doAccuseNotes() {
 			y += h + 7;
 		}
 
-		// Counter ??? `_UpdateSelectionCount(remaining)` at (0xd1, 0xb).
+		// `_UpdateSelectionCount(remaining)` @ (0xd1, 0xb).
 		const uint remaining = (selectedCount < expected)
 			? expected - selectedCount
 			: 0;
-		// Spanish floppy uses "nota/notas" (verified in Spanish EEM.EXE).
+		// Spanish floppy uses "nota/notas".
 		const char *clueWord = isSpanish()
 			? (remaining == 1 ? "nota" : "notas")
 			: (remaining == 1 ? "clue" : "clues");
@@ -4165,12 +3585,7 @@ bool EEMEngine::doAccuseNotes() {
 					dirty = true;
 					continue;
 				}
-				// Page navigation ??? `_NoteButtons[5]` / `[6]`,
-				// dispatched in `_HandleAccuseNoteButton @
-				// 1df2:0990`. Only effective if there's another
-				// page in that direction; mirrors the
-				// `1 < _CurrentPage` guard at 1df2:09a8 and the
-				// `_NextClue != -1` guard at 1df2:099e.
+				// `_NoteButtons[5]/[6]` ??? page nav guards @ 1df2:09a8/099e.
 				if (kBtnPageNext.contains(mx, my)) {
 					if (page + 1 < numPages) {
 						page++;
@@ -4185,33 +3600,22 @@ bool EEMEngine::doAccuseNotes() {
 					}
 					continue;
 				}
-				// Partner click ??? `_NoteButtons[3]`. Original
-				// `_HandleAccuseNoteButton` doesn't dispatch this
-				// (no case for i == 3), but the rect is still in
-				// the table; we wire it to the puzzle hint so the
-				// player can ask the partner what to look for
-				// without leaving accuse mode.
+				// `_NoteButtons[3]` ??? ScummVM hint, original ignores.
 				if (kBtnPartner.contains(mx, my)) {
 					doHelp();
 					dirty = true;
 					continue;
 				}
 				if (kBtnSolve.contains(mx, my)) {
-					// Count selected.
 					uint selected = 0;
 					for (uint i = 0; i < found.size(); i++) {
 						if (_mystery._noteSelected[found[i]])
 							selected++;
 					}
 					if (selected == expected) {
-						// Commit ??? let the caller do the
-						// `_SolvedCheck` + suspect picker dance.
+						// `_DoAccuse` gate `uStack_8 == uStack_a`.
 						return true;
 					}
-					// Wrong count ??? `_DoAccuse` only triggers the
-					// check when `uStack_8 == uStack_a`; we just
-					// stay in the loop so the player can keep
-					// adjusting.
 					continue;
 				}
 				// Toggle clue under cursor.
@@ -4237,9 +3641,7 @@ bool EEMEngine::doAccuseNotes() {
 		}
 		if (dirty)
 			draw();
-		// Per-tick redraw so the partner sprite cycles. Same
-		// 100 ms cadence as `_CheckFrameRate` + `_UpdateAnimations`
-		// in the original (1df2:0bfa).
+		// 100 ms `_CheckFrameRate` cadence @ 1df2:0bfa.
 		static uint32 sLastTick = 0;
 		const uint32 now = g_system->getMillis();
 		if (now - sLastTick >= 100) {
@@ -4256,25 +3658,17 @@ void EEMEngine::doAccuse() {
 	if (!_mystery.isLoaded() || !_font.isLoaded())
 		return;
 
-	// Floppy accusation flow is structurally different from the CD's:
-	// the suspect gallery section uses variable-stride entries (5 +
-	// nameLen bytes, NOT 0x46), the score is computed from the top-5
-	// note scores in `_TextSeen` (not `_NoteSelected`), and the win
-	// path renders the solved-chain dialog records at header[+0x12]
-	// rather than a single ClueBlock. Dispatch to a dedicated handler
-	// that mirrors `_KDHelp_Floppy` + `_HandleNoteButton_Floppy` (button
-	// 4 ??? `FUN_1d40_11fd` "ready to solve" gate) +
-	// `FUN_1d40_0c79` (gallery picker) + `_DisplayCorrect_Floppy` /
-	// `_DisplayAlibi_Floppy`.
+	// Floppy: variable-stride entries (5 + nameLen, not 0x46), top-5
+	// `_TextSeen` scoring (not `_NoteSelected`), header[+0x12] win-dialog.
+	// Handled by doAccuseFloppy (`FUN_1d40_11fd`, `FUN_1d40_0c79`,
+	// `_DisplayCorrect_Floppy`, `_DisplayAlibi_Floppy`).
 	if (isFloppy()) {
 		doAccuseFloppy();
 		return;
 	}
 
-	// `_AccuseEntry @ 1df2:0ff8` runs before the red accuse-notes
-	// picker. It scores the top five FOUND clues, says how close the
-	// player is, and only lets `_DoAccuse` continue when that score is
-	// at least 0x65.
+	// `_AccuseEntry @ 1df2:0ff8` ??? gates picker on top-5 found clue
+	// score (???0x65 required).
 	const byte *entryKdIdx = _mystery.kdTextIndex();
 	if (!entryKdIdx)
 		return;
@@ -4350,36 +3744,19 @@ void EEMEngine::doAccuse() {
 		return;
 	}
 
-	// Mirrors `_DoAccuse @ 1df2:0bdd` + `_DoAccuseGallery @ 1df2:0a31`:
-	//   1. ACCUSE-NOTES SCREEN (PIC 0x1A7, the red "accuse-mode" BG):
-	//      `_DrawNotes(_AccuseNoteRect, NULL, 100, _NoteSelected)`
-	//      lists every found clue inside the rect at `29be:1048` =
-	//      `(79, 27, 304, 159)`. `_UpdateSelectionCount(remaining)`
-	//      shows "N clue(s)" at `(209, 11)` (ASM: `_Show_String(0xb,
-	//      0xd1, ...)` at 1df2:0907). `_NoteUnselectedColor = 1` is
-	//      the dim red used for unselected entries; selected ones
-	//      get `0x3c`. Click toggles via `_SearchNoteAreas` +
-	//      `_SwapColors`. Expected count = `6 - DAT_2d5d_3f99`
-	//      (= 6 - chainStage):
-	//          stage 1 ??? 5 clues, stage 2 ??? 4, stage 3 ??? 3.
-	//      When the count matches, `_SolvedCheck` decides:
-	//          fail ??? KD "not enough evidence" balloon ??? return.
-	//          pass ??? `_DoAccuseGallery()` (suspect picker).
+	// `_DoAccuse @ 1df2:0bdd` + `_DoAccuseGallery @ 1df2:0a31`:
+	//   1. Accuse-notes (PIC 0x1A7) ??? pick `6 - chainStage` clues.
+	//      Pass `_SolvedCheck` ??? gallery; fail ??? hint + return.
 	//   2. KD intro balloon (`KDTextIndex[+8]` + `_SayKDDigital(4)`).
-	//   3. `_GetBackground(0x3f)` + `_DrawGallery()` ??? portraits at
-	//      the 5 fixed slots (`29be:0x116`).
-	//   4. Click loop on portraits ??? `_WITCH(picked)` ??? guilty/alibi.
+	//   3. PIC 0x3f + `_DrawGallery` portraits at 5 slots (29be:0x116).
+	//   4. Click portrait ??? `_WITCH(picked)` ??? guilty/alibi.
 	const uint8 num = _mystery.numSuspects();
 	if (num == 0)
 		return;
 
 	const byte *gd = _mystery.galleryData();
 
-	// ACCUSE-NOTES SCREEN ??? let the player commit which N clues they
-	// believe solve the case. Mirrors the click-driven selection of
-	// `_DoAccuse @ 1df2:0bdd`'s outer loop. ESC returns to the site
-	// (matches `_DoAccuse @ 1df2:0c11` writing `_NextScreen = 3`);
-	// pointer navigation can also leave for the PDA, gallery, or map.
+	// `_DoAccuse @ 1df2:0c11` outer loop; ESC ??? NextScreen=3.
 	if (!doAccuseNotes()) {
 		if (_nextScreen == kScreenAccuse) {
 			_nextScreen = _lastScreen != kScreenInvalid
@@ -4388,12 +3765,7 @@ void EEMEngine::doAccuse() {
 		return;
 	}
 
-	// Evidence gate. `_DoAccuse @ 1df2:0c75` runs `_SolvedCheck`
-	// before opening the suspect picker; on failure it renders a
-	// partner balloon over the CURRENT screen (the PDA in the
-	// original) and returns. We render the same hint over the
-	// caller's screen and bail back to `_lastScreen` without ever
-	// touching the gallery BG.
+	// `_DoAccuse @ 1df2:0c75` ??? `_SolvedCheck` gate.
 	if (!_mystery.solvedCheck()) {
 		const byte *kdIdx = _mystery.kdTextIndex();
 		const int16 hintOff = kdIdx
@@ -4404,11 +3776,8 @@ void EEMEngine::doAccuse() {
 			hint = parseString(_mystery.textAt((uint16)hintOff),
 							   _playerName, _partner);
 		if (hint.empty()) {
-			// Fallback if `KDTextIndex[+6]` isn't set in this mystery.
-			// Spanish text from the Spanish floppy EEM.EXE ??? there's
-			// only the single "1Necesitamos buscar pistas..." string
-			// in the binary, so use it for both the zero-points and
-			// some-points cases.
+			// Fallback if `KDTextIndex[+6]` missing. Spanish floppy
+			// has only one "Necesitamos buscar pistas" string.
 			if (isSpanish()) {
 				hint = "Necesitamos buscar pistas antes de resolver "
 					   "el misterio. Investiguemos un poco mas!";
@@ -4423,9 +3792,8 @@ void EEMEngine::doAccuse() {
 			}
 		}
 
-		// Compose balloon overlay on the current screen. Mirrors the
-		// `_GetKDTextBalloon` + `_GetBalloon` + `_AddPicBackground`
-		// + `_WordWrap` sequence at 1df2:0c8d-0cd1.
+		// Balloon overlay (`_GetKDTextBalloon` + `_GetBalloon` +
+		// `_AddPicBackground` + `_WordWrap` @ 1df2:0c8d-0cd1).
 		Graphics::ManagedSurface ms(320, 200,
 			Graphics::PixelFormat::createFormatCLUT8());
 		ms.clear();
@@ -4437,10 +3805,7 @@ void EEMEngine::doAccuse() {
 		const byte firstChar =
 			hint.empty() ? (byte)0 : (byte)hint[0];
 		uint16 bubNum = getKDTextBalloon(firstChar);
-		// Strip the digit prefix used for balloon dispatch ??? it's
-		// consumed by the original at `_DisplayAlibi @ 1df2:0163`
-		// (`str = pbVar7 + 1`) and shouldn't appear in the rendered
-		// text. `_GetKDTextBalloon` itself doesn't advance past it.
+		// Strip digit prefix (`_DisplayAlibi @ 1df2:0163` `str=pbVar7+1`).
 		if (firstChar >= '0' && firstChar <= '9')
 			hint.deleteChar(0);
 		bubNum = fitBalloonToText(bubNum, hint);
@@ -4469,21 +3834,17 @@ void EEMEngine::doAccuse() {
 								   0, 0, 320, 200);
 		g_system->updateScreen();
 
-		// `_DoAccuse @ 1df2:0cd9` plays `_SayKDDigital(3)` ???
-		// partner-specific "not enough evidence" voice line.
+		// `_SayKDDigital(3)` @ 1df2:0cd9.
 		if (_audio && kdIdx)
 			_audio->sayKDDigital(kdIdx, 3, _partner);
 
 		waitForInput(20000);
-		// `_DoAccuse @ 1df2:0ce5` writes `_NextScreen = _LastScreen`
-		// so the player drops back where they came from.
+		// `_DoAccuse @ 1df2:0ce5` ??? NextScreen = LastScreen.
 		_nextScreen = _lastScreen != kScreenInvalid
 						? (ScreenId)_lastScreen : kScreenSite;
 		return;
 	}
 
-	// Verbatim from 29be:0x116 ??? same five suspect slot positions as
-	// `_DrawGallery @ 158f:0046`.
 	Picture accuseBg;
 	const bool haveAccuseBg = _picsArchive.getPicture(0x3f, accuseBg);
 
@@ -4496,16 +3857,9 @@ void EEMEngine::doAccuse() {
 
 	int highlighted = 0;
 
-	// Step 1 ??? KD hint balloon. Mirrors `_DoAccuseGallery @ 1df2:0a31`
-	// (1df2:0a4c-1df2:0afe):
-	//   text  = TextBlock + KDTextIndex[+8]               (1df2:0a4c-0a57)
-	//   bub   = _GetKDTextBalloon(text[0])                (1df2:0a6d)
-	//   GetBalloon(bub)                                   (1df2:0a7c)
-	//   y     = (h < 0x4e) ? (0x50 - h) >> 1 : 1          (1df2:0a8b-0aa5)
-	//   AddPicBackground(pic, 0x21, y)                    (1df2:0aab)
-	//   WordWrap(0x21+tbl[bub].x, y+tbl[bub].y, tbl[bub].w, text, color=0)
-	//     tbl @ 29be:0875, 10-byte entries (1df2:0ad6-0af1)
-	//   _SayKDDigital(4); _Wait();                        (1df2:0b09-0b11)
+	// KD hint balloon @ `_DoAccuseGallery @ 1df2:0a31` (0a4c-0afe).
+	// y = (h < 0x4e) ? (0x50-h)>>1 : 1. Inset table @ 29be:0875.
+	// `_SayKDDigital(4)`.
 	const byte *kdIdx = _mystery.kdTextIndex();
 	if (kdIdx) {
 		const int16 textOff = (int16)READ_LE_UINT16(kdIdx + 8);
@@ -4514,20 +3868,10 @@ void EEMEngine::doAccuse() {
 			Common::String hint =
 				parseString(raw ? raw : "", _playerName, _partner);
 			if (!hint.empty()) {
-				// First-char dispatch via getKDTextBalloon (1df2:0105).
-				// Note: we pass the *parsed* first char; the original
-				// reads it BEFORE `_ParseString`, but the player-name /
-				// partner-name substitutions never start with digits, so
-				// the dispatch result is the same either way.
 				const byte firstChar =
 					hint.empty() ? (byte)0 : (byte)hint[0];
 				uint16 bubNum = getKDTextBalloon(firstChar);
-				// Strip the digit prefix used for balloon dispatch.
-				// `_DisplayAlibi @ 1df2:0163` does `str = pbVar7 + 1`
-				// after using `*str` for `bindx`. Same pattern used by
-				// `_DisplayHint`: digit picks the bubble shape AND is
-				// then consumed from the rendered text. Without this
-				// the intro balloon shows e.g. "1Ready to solve?".
+				// Strip digit prefix (`_DisplayAlibi @ 1df2:0163`).
 				if (firstChar >= '0' && firstChar <= '9')
 					hint.deleteChar(0);
 				bubNum = fitBalloonToText(bubNum, hint);
@@ -4536,22 +3880,12 @@ void EEMEngine::doAccuse() {
 					_balloonArchive.size() > (bubNum & 0x7F) &&
 					_balloonArchive.loadEntry(bubNum & 0x7F, balloon);
 
-				// 1df2:0a8b-1df2:0aa5: y = (h < 0x4e) ? (0x50-h)>>1 : 1
 				const int balloonX = 0x21;
 				int balloonY = 1;
 				if (haveBalloon && balloon.surface.h < 0x4e)
 					balloonY = (0x50 - balloon.surface.h) / 2;
 
-				// Render the gallery FIRST so the balloon snapshot
-				// includes the partner sprite. The original
-				// `_DoAccuseGallery @ 1df2:0a31` does this implicitly:
-				// `_NewAnimation` registered the partner slot at
-				// (5, 0x50) before reaching this point, then
-				// `_GetBackground(0x3f)` + `_DrawGallery` paint the
-				// portraits, and `_UpdateAnimations` keeps the partner
-				// visible underneath the balloon overlay. Without this,
-				// the player sees an 8-second partner-less screen
-				// while reading the hint.
+				// Render gallery first so the snapshot includes partner.
 				drawAccuseGallery(num, gd, /*highlighted=*/-1,
 								  slotRects, slotSuspect);
 
@@ -4564,22 +3898,18 @@ void EEMEngine::doAccuse() {
 						ms.simpleBlitFrom(*cur);
 						g_system->unlockScreen();
 					} else if (haveAccuseBg) {
-						// Fallback: lockScreen failed somehow; at least
-						// fill from PIC 0x3f so we don't render against
-						// stale memory. simpleBlitFrom auto-clips, so
-						// no MIN(w, 320)/MIN(h, 200) needed.
+						// Fallback if lockScreen failed.
 						ms.simpleBlitFrom(accuseBg.surface);
 					}
 				}
-				// Masked balloon blit ??? `_Rect_Move_Mask` (1000:03fc)
-				// skips pixels equal to `pic[0] >> 8`.
+				// `_Rect_Move_Mask` (1000:03fc) ??? transp = pic[0]>>8.
 				if (haveBalloon) {
 					const byte transp = (byte)(balloon.flags >> 8);
 					ms.transBlitFrom(balloon.surface,
 									 Common::Point(balloonX, balloonY),
 									 transp);
 				}
-				// Inset table @ 29be:0875 ??? 1df2:0acb pushes color=0.
+				// Inset table @ 29be:0875.
 				uint16 tx = 5;
 				uint16 ty = 4;
 				uint16 tw = 155;
@@ -4599,9 +3929,7 @@ void EEMEngine::doAccuse() {
 		}
 	}
 
-	// Helper to find the next "alive" slot (one whose `_inGallery[phys]`
-	// flag is still set so a portrait was actually drawn). Mirrors the
-	// way the original wraps DI past empty slots.
+	// Wrap past empty slots (matches original DI advance).
 	if (slotRects[highlighted].isEmpty())
 		highlighted = nextLiveSlot(slotRects, highlighted, +1);
 
@@ -4679,10 +4007,7 @@ void EEMEngine::doAccuse() {
 				}
 			}
 		}
-		// 100 ms tick ??? the original calls `_UpdateAnimations` per
-		// `_CheckFrameRate` (1df2:0b33). The accuse screen has no
-		// animations registered, so the tick is just a redraw cadence.
-		// We still re-render whenever the highlight moves (`dirty`).
+		// 100 ms `_CheckFrameRate` @ 1df2:0b33.
 		const uint32 now = g_system->getMillis();
 		if (dirty || now - lastTick >= 100) {
 			drawAccuseGallery(num, gd, highlighted, slotRects, slotSuspect);
@@ -4695,16 +4020,8 @@ void EEMEngine::doAccuse() {
 	if (picked < 0)
 		return;
 
-	// Real chain evaluation. Mirrors the original two-gate accusation:
-	//   1. `_AccuseEntry @ 1df2:0ff8` checked top-five found points
-	//      before the note picker, so by this point the player has
-	//      enough evidence to attempt an accusation.
-	//   2. `_SolvedCheck @ 1df2:00ec` checked selectedPoints > 99
-	//      before opening this suspect picker, so the clue set is
-	//      valid if we got here.
-	//   3. `_WITCH @ 1df2:089f` checks `GalleryData[picked*0x46+0x02] ==
-	//      0xFFFF`. Innocent suspects store an alibi-text TextBlock
-	//      offset there; the guilty one uses the sentinel.
+	// `_WITCH @ 1df2:089f` ??? guilty when gd[picked*0x46+0x02] == 0xFFFF;
+	// innocents store an alibi TextBlock offset there.
 	const int points          = _mystery.selectedPoints();
 	const bool pickedGuilty   = _mystery.isGuilty((uint)picked);
 	const bool guessedRight   = pickedGuilty;
@@ -4714,24 +4031,16 @@ void EEMEngine::doAccuse() {
 		   pickedGuilty ? "yes" : "no",
 		   guessedRight ? "correct" : "wrong");
 
-	// Wrong suspect: full alibi flow. Mirrors `_DisplayAlibi @
-	// 1df2:0145`:
-	//   1. Plays MIDI 6 (loser sting) and waits for it to finish while
-	//      the gallery is still on screen (1df2:0184-1df2:0192).
-	//   2. Draws PIC 0x3e + the suspect's speech balloon + their
-	//      portrait at (0x82, py), where the balloon shape comes from
-	//      `AlibiBubbles[bindx]` (table @ 29be:1050) and bindx is the
-	//      digit-prefix on the alibi text (else 2). bindx<8 centres the
-	//      balloon horizontally; bindx>=8 pins it at x=0x21.
-	//   3. Plays the suspect's voice via `_SpoolSound(talk - 1)` where
-	//      `talk = (Partner == 0) ? gd[+0x6] : gd[+0x0]` (1df2:0258),
-	//      then waits for a click.
-	//   4. Overlays the partner's reaction balloon (text @
-	//      `KDTextIndex[+10]`) at (0x21, y) and plays
-	//      `_SayKDDigital(5)`.
-	//   5. Clears `_FirstTry` (1df2:0447) and returns to LastScreen.
+	// `_DisplayAlibi @ 1df2:0145`:
+	//   1. MIDI 6 loser sting (gallery still visible).
+	//   2. PIC 0x3e + suspect balloon + portrait at (0x82, py).
+	//      bindx = digit prefix (else 2). bindx<8 centres balloon,
+	//      bindx>=8 pins at x=0x21.
+	//   3. `_SpoolSound(talk-1)` where talk = partner==0 ? gd[+6] : gd[+0].
+	//   4. Partner reaction balloon @ KDTextIndex[+10], `_SayKDDigital(5)`.
+	//   5. _FirstTry = 0; NextScreen = LastScreen (1df2:043f).
 	if (!guessedRight) {
-		// Balloon-shape table @ 29be:1050 ??? 16 entries ?? u16.
+		// Balloon-shape table @ 29be:1050.
 		static const uint16 kAlibiBubbles[16] = {
 			0x002B, 0x002C, 0x002D, 0x002E,
 			0x00AB, 0x00AC, 0x00AD, 0x00AE,
@@ -4746,10 +4055,7 @@ void EEMEngine::doAccuse() {
 			if (raw)
 				alibi = parseString(raw, _playerName, _partner);
 		}
-		// Digit-prefix dispatch ??? `_DisplayAlibi @ 1df2:0163` reads
-		// `*str` for `bindx` and advances `str = pbVar7 + 1` so the
-		// digit doesn't reach the renderer. Non-digit first chars fall
-		// through to the default bindx=2 (1df2:015e).
+		// `_DisplayAlibi @ 1df2:0163` ??? bindx = digit prefix, else 2.
 		uint bindx = 2;
 		const byte firstChar = alibi.empty() ? (byte)0 : (byte)alibi[0];
 		if (firstChar >= '0' && firstChar <= '9') {
@@ -4774,9 +4080,7 @@ void EEMEngine::doAccuse() {
 			_balloonArchive.size() > (bubNum & 0x7F) &&
 			_balloonArchive.loadEntry(bubNum & 0x7F, balloon);
 
-		// Position math from 1df2:01a4-1df2:0207. py is the suspect
-		// portrait's Y; defaults to 0x5a, only overridden in the
-		// bindx<8 branch when the balloon is too tall to fit.
+		// Position math @ 1df2:01a4-0207.
 		int balloonX = 0x21;
 		int balloonY = 1;
 		int py = 0x5a;
@@ -4796,14 +4100,7 @@ void EEMEngine::doAccuse() {
 			balloonY = (bh < 0x4f) ? (0x50 - bh) / 2 : 1;
 		}
 
-		// `base` = BG + suspect + partner sprite ??? the persistent layer
-		// that survives across both balloon phases. The original engine
-		// keeps PIC 0x3e in the master BG buffer (16000), `_AddPicBackground`
-		// commits the suspect there, and the partner animation
-		// registered by `_DoAccuse @ 1df2:0c30` is re-blitted by every
-		// `_Repaint` via `_DrawActiveAnimations`. We don't have a
-		// slot-based animation system, so we manually keep a "base"
-		// surface and re-draw the partner frame for each phase.
+		// `base` = BG + suspect + partner. Survives both balloon phases.
 		Graphics::ManagedSurface base(320, 200,
 			Graphics::PixelFormat::createFormatCLUT8());
 		base.clear();
@@ -4815,20 +4112,15 @@ void EEMEngine::doAccuse() {
 							   Common::Point(0x82, py),
 							   (uint32)transp);
 		}
-		// Partner sprite at (5, 0x50). Anim cells: 2 (Jake) / 0x10
-		// (Jenny); script key 0x02 ??? same indices `_DoAccuse @
-		// 1df2:0c30` uses for its `_NewAnimation` call. Partner is
-		// drawn AFTER the suspect so it doesn't get clipped by the
-		// portrait if their bounding boxes graze.
+		// Partner at (5, 0x50). ANI 2/0x10, script 0x02 (`_DoAccuse
+		// @ 1df2:0c30`). Drawn after suspect.
 		const uint partnerAnim = (_partner == 0) ? 2 : 0x10;
 		Animation partnerAni;
 		const bool havePartner =
 			_aniArchive.loadAnimation(partnerAnim, partnerAni) &&
 			!partnerAni.empty();
 
-		// Alibi-phase scratch = base + alibi balloon + alibi text +
-		// partner sprite (animation slot drawn on top per
-		// `_DrawActiveAnimations`).
+		// scratch = base + alibi balloon/text + partner.
 		Graphics::ManagedSurface scratch(320, 200,
 			Graphics::PixelFormat::createFormatCLUT8());
 		scratch.simpleBlitFrom(base);
@@ -4838,8 +4130,7 @@ void EEMEngine::doAccuse() {
 								  Common::Point(balloonX, balloonY),
 								  (uint32)transp);
 		}
-		// Balloon-text inset table @ 29be:0875 ??? same dispatch as KD
-		// balloons. WordWrap color is 0 inside a balloon (1df2:0240).
+		// Inset table @ 29be:0875. Color 0 inside balloon (1df2:0240).
 		uint16 tx = 5, ty = 4, tw = 155;
 		getBalloonInsets(bubNum, tx, ty, tw);
 		if (_font.isLoaded() && !alibi.empty()) {
@@ -4854,9 +4145,7 @@ void EEMEngine::doAccuse() {
 								  partnerAni[frameIdx], 5, 0x50);
 		}
 
-		// Step 1 ??? alibi music. Original blocks until MIDI 6 ends with
-		// the gallery still on screen. We poll `_music->isPlaying`;
-		// click/ESC aborts early.
+		// MIDI 6 ??? blocks until done (or click/ESC aborts).
 		if (_music && _voiceOn) {
 			_music->playMus(6, /*loop=*/false);
 			const uint32 musStart = g_system->getMillis();
@@ -4875,8 +4164,7 @@ void EEMEngine::doAccuse() {
 						break;
 					}
 				}
-				// Hard cap so we never get stuck if MIDI never reports
-				// finish (some sound configurations).
+				// Hard cap if MIDI never reports finish.
 				if (g_system->getMillis() - musStart > 10000)
 					break;
 				g_system->updateScreen();
@@ -4885,9 +4173,8 @@ void EEMEngine::doAccuse() {
 			_music->stop();
 		}
 
-		// Step 2 ??? flip the alibi scene to screen + play suspect voice.
-		// `talk = (Partner==0) ? gd[+0x6] : gd[+0x0]` (1df2:0252-0258);
-		// indices are 1-based so subtract 1 before SpoolSound.
+		// Suspect voice. talk = partner==0 ? gd[+0x6] : gd[+0x0] (1df2:0252).
+		// 1-based, so SpoolSound(talk-1).
 		g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
 								   0, 0, 320, 200);
 		g_system->updateScreen();
@@ -4903,16 +4190,8 @@ void EEMEngine::doAccuse() {
 		}
 		waitForInput(60000);
 
-		// Step 3 ??? partner reaction balloon. Mirrors 1df2:026e-1df2:02b6.
-		// Rebuild scratch from `base` (BG + suspect + partner sprite)
-		// so the alibi balloon + text don't bleed through. The
-		// original's `_Repaint` re-renders the master BG (which still
-		// has the alibi balloon committed), but the engine ALSO calls
-		// `_GetBackground(0x3e)` again before `_AddPicBackground` for
-		// each new balloon ??? flushing the master back to a clean state.
-		// We achieve the same end result by restoring `base` here.
-		// `_SayKDDigital(5)` auto-cancels the still-playing alibi voice
-		// (spoolSound calls stopSpool internally) so no explicit stop.
+		// Partner reaction @ 1df2:026e-02b6. Rebuild from `base` so alibi
+		// balloon clears. `_SayKDDigital(5)` auto-cancels alibi voice.
 		const byte *reactIdx = _mystery.kdTextIndex();
 		if (reactIdx) {
 			const int16 reactOff = (int16)READ_LE_UINT16(reactIdx + 10);
@@ -4937,8 +4216,6 @@ void EEMEngine::doAccuse() {
 				if (haveR && rBalloon.surface.h < 0x4e)
 					rY = (0x50 - rBalloon.surface.h) / 2;
 
-				// Reset to a clean BG + suspect, then layer the new
-				// balloon and the (refreshed) partner frame on top.
 				scratch.simpleBlitFrom(base);
 				if (haveR) {
 					const byte transp = (byte)(rBalloon.flags >> 8);
@@ -4971,35 +4248,22 @@ void EEMEngine::doAccuse() {
 		waitForInput(60000);
 
 		_mystery._firstTry = false;
-		// `_DisplayAlibi @ 1df2:043f` writes `_NextScreen =
-		// _LastScreen`. The original returns the player to the caller
-		// (PDA / site / map) for another try.
+		// `_DisplayAlibi @ 1df2:043f` ??? NextScreen = LastScreen.
 		_nextScreen = _lastScreen != kScreenInvalid
 						? (ScreenId)_lastScreen : kScreenSite;
 		return;
 	}
 
-	// Right suspect ??? full win flow. Mirrors `_DisplayCorrect @
-	// 1df2:073c`: mark mystery solved, advance chain stage if the
-	// tier is complete, swap MIDI to the win cue, run SCRAPBK.ANI,
-	// show the per-mystery ending, save the profile, return to the
-	// action menu (`_NextScreen = 0xc` at 1df2:0895).
+	// Win ??? `_DisplayCorrect @ 1df2:073c`: mark solved, advance chain,
+	// MIDI 5, SCRAPBK.ANI, ending, save, NextScreen=0xc (1df2:0895).
 	{
 		const uint mn = _mystery.number();
 		if (mn < sizeof(_mysteriesSolved)) {
 			_mysteriesSolved[mn] = _mystery._firstTry ? 2 : 1;
 		}
 
-		// Mirrors the chain-advancement loop at `_DisplayCorrect @
-		// 1df2:0824-0850`. Skip mystery 0 (the practice case) per the
-		// `if (_MysteryNumber != 0)` guard at 1df2:080d, then check
-		// every mystery in the current tier:
-		//   stage 1 ??? check  1..0x18 (24 mysteries, "A chain")
-		//   stage 2 ??? check 0x19..0x30 (24 mysteries, "B chain")
-		//   stage 3 ??? check 0x31..0x36 (6 mysteries, "C chain")
-		// If every solve flag in that range is non-zero, bump the
-		// stage. Original increments unconditionally (3???4 is harmless
-		// since no further range covers it); we cap at 3 for clarity.
+		// Chain advance @ 1df2:0824-0850. Skip mystery 0 (practice).
+		// stage 1: 1..0x18, stage 2: 0x19..0x30, stage 3: 0x31..0x36.
 		if (mn != 0) {
 			uint lo = 0, hi = 0;
 			switch (_chainStage) {
@@ -5013,12 +4277,7 @@ void EEMEngine::doAccuse() {
 				if (i >= sizeof(_mysteriesSolved) || _mysteriesSolved[i] == 0)
 					allSolved = false;
 			}
-			// `_DisplayCorrect @ 1df2:0852` increments unconditionally
-			// when every case in the current tier is solved ??? including
-			// past stage 3 (so a stage-4 endgame state exists in the
-			// `.PLR` save format). `_ActionScreen @ 1c33:19d1` gates
-			// the menu on `if (3 < _chainStage)` to grey
-			// Choose-A-Mystery and Practice once everything's solved.
+			// 1df2:0852 increments past 3 (stage-4 endgame); cap at 4.
 			if (allSolved && _chainStage < 4) {
 				_chainStage++;
 				debugC(1, kDebugMystery,
@@ -5027,25 +4286,17 @@ void EEMEngine::doAccuse() {
 			}
 		}
 
-		// `_DisplayCorrect @ 1df2:073c` order:
-		//   1df2:0773  _AllBlack();
-		//   1df2:0776  _BuildBackground(5, 0x42, 0x14);  // conclusion BG
-		//   1df2:0780  _FadeIn();
-		//   1df2:0789  _MIDIPlay(5);                      // win music
-		//   1df2:07ac  _DisplayClue(MysteryIndex[+0x10]); // chain recap
-		// `_BuildBackground @ 172b:13e2` loads PIC 0x3D (the standard
-		// frame) and overlays SITES.DBD entry 5 at (0x42, 0x14), then
-		// sets palette via `_GetPalette(sitenum + 1)` = palette 6.
-		// Without this BG the chain-recap balloons render on top of
-		// the accuse-gallery BG (PIC 0x3F + suspect portraits), which
-		// is visually jarring ??? the conclusion is supposed to play
-		// against the dedicated "office / desk" scene.
+		// `_DisplayCorrect @ 1df2:073c`:
+		//   _AllBlack; _BuildBackground(5, 0x42, 0x14); _FadeIn;
+		//   _MIDIPlay(5); _DisplayClue(MysteryIndex[+0x10]).
+		// `_BuildBackground @ 172b:13e2` = PIC 0x3D + SITES entry 5 at
+		// (0x42, 0x14), palette = sitenum+1 = 6.
 		Graphics::Surface *blk = g_system->lockScreen();
 		if (blk) {
 			memset(blk->getPixels(), 0, 320 * 200);
 			g_system->unlockScreen();
 		}
-		setSitePalette(6); // sitenum + 1 per `_GetPalette` call
+		setSitePalette(6); // sitenum + 1 (`_GetPalette`).
 		Picture frame, scene;
 		if (_picsArchive.loadEntry(0x3d, frame)) {
 			g_system->copyRectToScreen(frame.surface.getPixels(),
@@ -5063,15 +4314,7 @@ void EEMEngine::doAccuse() {
 										   sw, sh);
 		}
 
-		// Partner sprite at (5, 0x50). The original `_DoAccuse @
-		// 1df2:0c30` registered the partner anim BEFORE entering the
-		// gallery; that slot stays active across `_DisplayCorrect`'s
-		// `_BuildBackground` (which calls `_Repaint` ???
-		// `_DrawActiveAnimations` and re-blits the partner over the
-		// fresh BG). We don't have a slot system, so manually stamp
-		// the resting frame here. `displayClue` snapshots the screen
-		// on entry, so the partner ends up baked into its BG and is
-		// preserved across every clue iteration.
+		// Stamp partner at (5, 0x50); displayClue snapshots screen.
 		const uint partnerAnim = (_partner == 0) ? 2 : 0x10;
 		Animation partnerAni;
 		if (_aniArchive.loadAnimation(partnerAnim, partnerAni) &&
@@ -5091,62 +4334,40 @@ void EEMEngine::doAccuse() {
 		if (_music && _voiceOn)
 			_music->playMus(5, /*loop=*/false);
 
-		// Chain-by-chain RECAP. Partner enumerates every required
-		// clue ("Look at this ??? the suspect was here at 8pm", "... and
-		// remember the broken vase from the kitchen", "... so it had
-		// to be X!") and arrives at the conclusion. Without rendering
-		// it the player goes straight from suspect-pick to the
-		// scrapbook anim and misses the deduction entirely.
+		// Chain recap ??? partner enumerates required clues.
 		const byte *solved = _mystery.solvedClueBlock();
 		if (solved)
 			displayClue(solved);
 		if (_music && _voiceOn)
 			_music->stop();
 
-		// `_DifferenceAnimation("scrapbk.ani")` (1df2:0848) ??? the
-		// physical scrapbook flip animation that introduces the
-		// per-mystery ending pages.
+		// `_DifferenceAnimation("scrapbk.ani")` @ 1df2:0848.
 		playAnm(Common::Path("SCRAPBK.ANI"), 120, true);
 
-		// `_ShowOneScrap @ 1f78:0773` is `_DisplayEnding(num, 1)` ???
-		// the multi-page per-mystery ending narrative. This shares the
-		// same red edge cursor + edge-only mouse navigation used by the
-		// scrapbook browser.
+		// `_ShowOneScrap @ 1f78:0773` = `_DisplayEnding(num, 1)`.
 		doShowEnding(mn);
 
-		// Mirrors `_SavePlayerRecord` at 1df2:0857 ??? once the
-		// `_mysteriesSolved` table is updated, the original
-		// immediately persists the player record so the win sticks
-		// even if the player quits before reaching the menu.
-		//
-		// Order matters: `_mystery.clear()` BEFORE `saveProfile` so the
-		// save records `hasMystery=false`. Otherwise the next load of
-		// this profile sees the just-won mystery still loaded and the
-		// screen driver routes to its map (forcing the player to
-		// replay the win flow). Mirrors `_DisplayCorrect @ 1df2:0851`
-		// (`_DeleteSavedGame` removes the in-progress save before
-		// `_SavePlayerRecord` writes the post-win profile).
+		// `_SavePlayerRecord` @ 1df2:0857. Order: clear() before save
+		// so hasMystery=false (matches `_DeleteSavedGame` @ 1df2:0851).
 		_mystery.clear();
 		const Common::Error err = saveProfile(_playerName);
 		if (err.getCode() != Common::kNoError)
 			warning("saveProfile after solve failed: %s",
 					err.getDesc().c_str());
 
-		// `_DisplayCorrect @ 1df2:0895` writes `_NextScreen = 0xc` ???
-		// the winner returns to the post-mystery `_ActionScreen`.
+		// `_DisplayCorrect @ 1df2:0895` ??? NextScreen = 0xc.
 		_nextScreen = kScreenAction;
 	}
 }
 
 void EEMEngine::doAccuseFloppy() {
-	// Floppy accuse flow ??? mirrors:
-	//   `_KDHelp_Floppy / FUN_1d40_11fd @ 1d40:11fd` (score gate +
-	//        partner "ready / not ready" balloon)
-	//   `FUN_1d40_0e07 @ 1d40:0e07`   (clue-selection screen ??? skipped
-	//        in this MVP; selectedPoints() already takes the top-5)
-	//   `FUN_1d40_0c79 @ 1d40:0c79`   (gallery picker)
-	//   `_DisplayCorrect_Floppy @ 1d40:0894`
-	//   `_DisplayAlibi_Floppy   @ 1d40:00df`
+	// Floppy accuse:
+	//   `_KDHelp_Floppy / FUN_1d40_11fd @ 1d40:11fd` ??? score gate.
+	//   `FUN_1d40_0e07` ??? clue selection (skipped; selectedPoints()
+	//     already takes top-5).
+	//   `FUN_1d40_0c79 @ 1d40:0c79` ??? gallery picker.
+	//   `_DisplayCorrect_Floppy @ 1d40:0894`.
+	//   `_DisplayAlibi_Floppy @ 1d40:00df`.
 	const byte *kdIdx     = _mystery.kdTextIndex();
 	const byte *bufBase   = _mystery.blobAt(0);
 	const uint32 mysSize  = _mystery.dataSize();
@@ -5171,9 +4392,8 @@ void EEMEngine::doAccuseFloppy() {
 		if (lineLen == 0)
 			return;
 		Common::String raw(p, lineLen);
-		// Digit prefix ??? balloon variant (per `_GetKDTextBalloon_Floppy
-		// @ 1d40:009f` and the `_KDBalloonByChar_Floppy` table at
-		// `2608:0c14 + char`).
+		// Digit ??? balloon variant (`_GetKDTextBalloon_Floppy @ 1d40:009f`
+		// + table @ 2608:0c14).
 		static const uint8 kDigitToBalloon[10] = {
 			0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1c, 0x1d, 0x1e, 0x0a
 		};
@@ -5233,19 +4453,18 @@ void EEMEngine::doAccuseFloppy() {
 		}
 	};
 
-	// `FUN_1d40_11fd` score gate. Picks one of three KD strings and
-	// returns 1 only when score >= 100.
+	// `FUN_1d40_11fd` ??? score gate, ready when score >= 100.
 	const int score = _mystery.selectedPoints();
 	uint kdSlot;
 	bool readyToSolve;
 	if (score < 50) {
-		kdSlot = 0;        // KDTextIndex[0] ??? "we've barely started"
+		kdSlot = 0;        // "we've barely started"
 		readyToSolve = false;
 	} else if (score < 100) {
-		kdSlot = 1;        // KDTextIndex[1] ??? "getting closer"
+		kdSlot = 1;        // "getting closer"
 		readyToSolve = false;
 	} else {
-		kdSlot = 2;        // KDTextIndex[2] ??? "ready to solve"
+		kdSlot = 2;        // "ready to solve"
 		readyToSolve = true;
 	}
 	showFloppyKDHint(kdSlot);
@@ -5255,14 +4474,8 @@ void EEMEngine::doAccuseFloppy() {
 		return;
 	}
 
-	// Clue selection screen ??? `FUN_1d40_0e07 @ 1d40:0e07`. The user
-	// must pick exactly `6 - chainStage` clues from the found list,
-	// rendered on the red accuse-mode BG (PIC 0x1A7). Verified by
-	// the asm at 1d40:0e34 reading `local_c = 6 - DAT_28da_3052`,
-	// matching the CD's `_DoAccuse @ 1df2:0bdd` expected count.
-	// `doAccuseNotes()` already handles this UI for both variants
-	// (note text reading is variant-aware via `noteTextOff`); it
-	// returns true on commit, false on ESC / pointer navigation.
+	// Clue selection ??? `FUN_1d40_0e07 @ 1d40:0e07`. Pick `6 - chainStage`
+	// clues (1d40:0e34: `local_c = 6 - DAT_28da_3052`).
 	if (!doAccuseNotes()) {
 		if (_nextScreen == kScreenAccuse) {
 			_nextScreen = _lastScreen != kScreenInvalid
@@ -5271,19 +4484,8 @@ void EEMEngine::doAccuseFloppy() {
 		return;
 	}
 
-	// Second score gate ??? based on the CLUES THE USER ACTUALLY
-	// PICKED. `_DoAccuse_Floppy @ 1d40:0e07` (after the commit
-	// branch at 1d40:0f3a) reads `local_a = FUN_1d40_0c48()` (=
-	// sum of byte +6 across selected note entries) and:
-	//   if (local_a < 100) ??? KDTextIndex[+6] hint (slot 3),
-	//                        bail back to last screen.
-	//   else              ??? call FUN_1d40_0c79 (gallery picker).
-	// Note this is distinct from the entry gate on
-	// `_GetSelectedPoints_Floppy @ 1d40:0c23` (which uses the
-	// auto-top-5 of FOUND clues ??? what `Mystery::selectedPoints`
-	// returns on floppy). A player can be "ready to solve" by score
-	// yet still pick the wrong subset; that's the case this gate
-	// catches.
+	// 2nd score gate on USER-PICKED clues (1d40:0f3a, FUN_1d40_0c48 =
+	// sum of byte +6 across selected entries). <100 ??? slot 3 hint.
 	int userSelectedScore = 0;
 	{
 		const byte *ni2 = _mystery.noteIndex();
@@ -5304,13 +4506,10 @@ void EEMEngine::doAccuseFloppy() {
 		return;
 	}
 
-	// `FUN_1d40_0c79` ??? gallery picker. Show "Which suspect?" KD
-	// hint at slot 4 (KDTextIndex[+8]/2 = entry index 4), then
-	// render the gallery and wait for click.
+	// `FUN_1d40_0c79` ??? gallery picker. KDTextIndex slot 4 prompt.
 	showFloppyKDHint(4);
 
-	// Build slot list. Floppy gallery section: byte0 = numSuspects,
-	// then variable-stride entries.
+	// Floppy gallery: byte0 = numSuspects, then variable-stride entries.
 	const uint8 num = _mystery.numSuspects();
 	if (num == 0) {
 		_nextScreen = _lastScreen != kScreenInvalid
@@ -5318,8 +4517,7 @@ void EEMEngine::doAccuseFloppy() {
 		return;
 	}
 
-	// Verbatim from `_DrawGallery_Floppy @ 154e:0050` ??? slot positions
-	// at `2608:016c` (5 ?? {u16 x, u16 y}).
+	// `_DrawGallery_Floppy @ 154e:0050` slots @ 2608:016c.
 	struct GallerySlot { int x, y; };
 	static const GallerySlot kFloppySlots[5] = {
 		{ 0x53, 0x0e }, { 0x9b, 0x0e }, { 0xe3, 0x0e },
@@ -5338,7 +4536,6 @@ void EEMEngine::doAccuseFloppy() {
 		if (haveAccuseBg)
 			scratch.simpleBlitFrom(accuseBg.surface);
 
-		// Partner sprite (anim 2 / 0x10) ??? same as CD doAccuse.
 		const uint partnerAnim = (_partner == 0) ? 2 : 0x10;
 		Animation partnerAni;
 		if (_aniArchive.loadAnimation(partnerAnim, partnerAni) &&
@@ -5370,8 +4567,7 @@ void EEMEngine::doAccuseFloppy() {
 			if (!_picsArchive.getPicture(picId, portrait))
 				continue;
 			const GallerySlot &s = kFloppySlots[phys];
-			// `_DrawGallery_Floppy @ 154e:00ed` bottom-aligns to baseline
-			// 0x48 (= 72), same as CD: `placeY = s.y + (0x48 - h)`.
+			// Bottom-align to baseline 0x48 (`154e:00ed`).
 			const int placeX = s.x;
 			const int placeY = s.y + (0x48 - portrait.surface.h);
 			const byte transp = (byte)(portrait.flags >> 8);
@@ -5450,33 +4646,16 @@ void EEMEngine::doAccuseFloppy() {
 	const bool guilty = _mystery.isGuilty((uint)picked);
 
 	if (guilty) {
-		// Win path. Mirrors `_DisplayCorrect_Floppy @ 1d40:0894`:
-		//   1d40:08a0  _BuildBackground_Floppy(5, 0x42, 0x14);
-		//                 ??? PIC 0x3d frame + SITES entry 5 at (0x42,
-		//                   0x14), palette = sitenum + 1 = 6.
-		//   1d40:08b1  _FadeIn();
-		//   1d40:08c0  _MIDIPlayFile("travel-2.xmi");
-		//   1d40:08d0  walk solved chain via _DisplayHotspotClue_Floppy
-		//                 + _WaitForClick per record. Mid-recap: when
-		//                 only 3 records remain, play
-		//                 _PlayTitleANM_Floppy(0) = SCRAPBK.ANI.
-		//   1d40:0939  ((u16 *)0x3054)[mysteryNum] =
-		//                  _firstTry ? 2 : 1;
-		//   1d40:0941  tier-promotion check (advance _chainStage when
-		//                 every mystery in the current tier is solved).
-		//   1d40:0982  _SavePlayerRecord  (= saveProfile)
-		//   1d40:0985  _DeleteMysteryFile (= mystery cleanup)
-		//   1d40:0991  MakeSolvedSound = 1; ShowOneScrap(mystery)
-		//   1d40:09b0  _NextScreen = 0xc.
+		// `_DisplayCorrect_Floppy @ 1d40:0894`:
+		//   _BuildBackground_Floppy(5, 0x42, 0x14); _FadeIn;
+		//   _MIDIPlayFile("travel-2.xmi");
+		//   Walk solved chain, SCRAPBK.ANI when 3 records left.
+		//   Mark solved, tier-promote, SavePlayerRecord,
+		//   ShowOneScrap, NextScreen = 0xc.
 		const uint mn = _mystery.number();
 
-		// Conclusion BG composition. `_BuildBackground_Floppy @
-		// 16e2:12fd` blits PIC 0x3d (the desk frame) onto a cleared
-		// page, then overlays SITES.DBD entry 5 (the conclusion
-		// artwork) at (param_2, param_3) = (0x42, 0x14). Palette
-		// becomes `sitenum + 1` = 6. Same composition the CD `doAccuse`
-		// win flow performs (see ui.cpp:4145-4166), just driven from
-		// the floppy data archives.
+		// `_BuildBackground_Floppy @ 16e2:12fd` ??? PIC 0x3d + SITES entry 5
+		// at (0x42, 0x14), palette 6.
 		{
 			Graphics::Surface *blk = g_system->lockScreen();
 			if (blk) {
@@ -5501,17 +4680,8 @@ void EEMEngine::doAccuseFloppy() {
 						scene.surface.pitch, sx, sy, sw, sh);
 			}
 
-			// Partner sprite at (5, 0x50). The original keeps its
-			// `_NewAnimation` slot active across `_DisplayCorrect`'s
-			// `_BuildBackground_Floppy`, so `_UpdateAnimations` keeps
-			// re-stamping the partner over the fresh BG. We don't have
-			// a slot system, so manually bake the resting frame into
-			// the BG before the recap kicks off ??? `displayFloppyDialog
-			// Records` snapshots the screen on entry and the partner
-			// stays visible across every chain record. Without this
-			// the win sequence ends up partner-less. Anim 2 (Jake) /
-			// 0x10 (Jenny), script key 0x02 ??? same as the gallery
-			// picker and the CD accuse path (ui.cpp:4177).
+			// Stamp partner at (5, 0x50); displayFloppyDialogRecords
+			// snapshots screen. ANI 2/0x10, script 0x02.
 			const uint partnerAnim = (_partner == 0) ? 2 : 0x10;
 			Animation partnerAni;
 			if (_aniArchive.loadAnimation(partnerAnim, partnerAni) &&
@@ -5529,22 +4699,12 @@ void EEMEngine::doAccuseFloppy() {
 			g_system->updateScreen();
 		}
 
-		// Win music ??? TRAVEL-2.XMI (`2608:0c84`, played via
-		// `_MIDIPlayFile` at `_DisplayCorrect_Floppy @ 1d40:08c0`).
-		// The CD path uses internal MUS index 5; the floppy plays the
-		// XMI directly by filename. `_voiceOn` (= `DAT_28da_3050`) is
-		// the gate the original checks first.
+		// TRAVEL-2.XMI @ 2608:0c84 (`_MIDIPlayFile` @ 1d40:08c0).
 		if (_music && _voiceOn)
 			_music->playFile(Common::Path("travel-2.xmi"), false);
 
-		// Walk the solved-clue chain. Header[+0x12] points at a
-		// `count` byte followed by `count` dialog records (same layout
-		// as hotspot dialogs). When only three records remain, the
-		// original clears animation slots and calls
-		// `_PlayTitleANM_Floppy(0)`. The title helper's file table maps
-		// index 0 to `SCRAPBK.ANI` and index 1 to `TITLE.ANM`, so this
-		// is the same scrapbook flip transition used by the CD win flow,
-		// just inserted before the last three floppy recap records.
+		// Walk solved chain (header[+0x12]: count byte + dialog records).
+		// SCRAPBK.ANI fires when 3 records remain (`_PlayTitleANM_Floppy(0)`).
 		const byte *chain = _mystery.solvedClueBlock();
 		if (chain) {
 			const uint count = chain[0];
@@ -5571,23 +4731,11 @@ void EEMEngine::doAccuseFloppy() {
 		if (_music && _voiceOn)
 			_music->stop();
 
-		// Mark mystery solved with first-try bonus tracking.
-		// `_DisplayCorrect_Floppy @ 1d40:0939`:
-		//   ((u16 *)0x3054)[mysteryNum] = 1;
-		//   if (DAT_28da_35df != 0)
-		//       ((u16 *)0x3054)[iVar5] = 2;
-		// `DAT_28da_35df` is `_firstTry`; it starts at 1 on
-		// `_ReadMystery_Floppy` and is cleared to 0 by
-		// `_DisplayAlibi_Floppy` on a wrong accusation. So 2 = won on
-		// first try, 1 = won after at least one alibi.
+		// 1d40:0939 ??? solved[mn] = _firstTry ? 2 : 1.
 		if (mn < sizeof(_mysteriesSolved))
 			_mysteriesSolved[mn] = _mystery._firstTry ? 2 : 1;
 
-		// Tier-promotion check. `_DisplayCorrect_Floppy @
-		// 1d40:0941..0978` walks the current tier's mystery range and
-		// advances `DAT_28da_3052` (= `_chainStage`) when every entry
-		// is non-zero. Skip mystery 0 (practice case) per the
-		// `if (DAT_2608_149c != 0)` guard at 1d40:093f.
+		// Tier promotion @ 1d40:0941..0978. Skip mystery 0 (practice).
 		if (mn != 0) {
 			uint lo = 0, hi = 0;
 			switch (_chainStage) {
@@ -5610,10 +4758,8 @@ void EEMEngine::doAccuseFloppy() {
 			}
 		}
 
-		// `_DisplayCorrect_Floppy` sets `MakeSolvedSound` before the
-		// newly solved ending is shown. `FUN_1d40_05b7` reads byte 0 of
-		// `E<num>.BIN` and maps values 0..2 through the table at
-		// `2608:0c5e` to VOC slots 0x15, 0x16, 0x17.
+		// `MakeSolvedSound`. `FUN_1d40_05b7` maps E<num>.BIN byte 0 (0..2)
+		// via table @ 2608:0c5e to VOC slots 0x15/0x16/0x17.
 		if (_audio && _voiceOn) {
 			Common::File ending;
 			const Common::String fname =
@@ -5631,19 +4777,10 @@ void EEMEngine::doAccuseFloppy() {
 			}
 		}
 
-		// `_DisplayCorrect_Floppy @ 1d40:0991` calls
-		// `FUN_1ee2_06ac(mystery)`, whose body is just
-		// `FUN_1d40_05b7(mystery, 1)` plus font cleanup. That is the
-		// floppy ending/scrapbook viewer, and it receives the
-		// first-try badge state from the solved table entry above.
+		// 1d40:0991 ??? FUN_1ee2_06ac (= FUN_1d40_05b7 + font cleanup).
 		doShowEnding(mn);
 
-		// Persist progress before clearing the in-progress mystery.
-		// `_DisplayCorrect_Floppy @ 1d40:0982` calls
-		// `_SavePlayerRecord` then `FUN_22dc_0dbd` (which deletes the
-		// per-mystery save file via DOS int 21h). Order matters here
-		// for the same reason as the CD path ??? clear ??? save so the
-		// profile records `hasMystery=false`.
+		// 1d40:0982 _SavePlayerRecord. clear() before save.
 		_mystery._solvedPuzzle = true;
 		_mystery.clear();
 		(void)saveProfile(_playerName);
@@ -5651,25 +4788,12 @@ void EEMEngine::doAccuseFloppy() {
 		return;
 	}
 
-	// Innocent ??? `_DisplayAlibi_Floppy @ 1d40:00df`:
-	//   1d40:00fb  _GetBackground(0x3e);
-	//   1d40:0103  _GetPicture(suspect.picID);
-	//   1d40:010c  _AddPicBackground(susp_pic, 0x82, 0x5a);
-	//   1d40:0125  _MIDIPlayFile("fanfare2.xmi");  // alibi sting
-	//   1d40:0145  alibi-balloon table at `2608:0c0a + first_char`
-	//                  (separate from the KD digit???balloon table at
-	//                  `2608:0c14` ??? yes, 0xc0a, NOT 0xc14, verified
-	//                  via the `*(char *)(*local_a + 0xc0a)` access).
-	//                  Default = `DAT_2608_0c3c` = 0x2c.
-	//   1d40:01a3  balloon centred:
-	//                  bx = (0x140 - balloon.w) / 2;
-	//                  by = (0x5a  - balloon.h) / 2;
-	//   1d40:01d5  WordWrap alibi text inside balloon, _WaitForClick.
-	//   1d40:01ee  KD reaction at KDTextIndex[+10] = slot 5
-	//                  (NOT slot 8 ??? slot 8 is the gallery prompt).
-	//                  Balloon at (0x21, (0x50 - h) / 2).
-	//   1d40:0247  _NextScreen = _LastScreen;
-	//   1d40:024b  _firstTry = 0;
+	// `_DisplayAlibi_Floppy @ 1d40:00df`:
+	//   BG 0x3e + suspect pic at (0x82, 0x5a); MIDI fanfare2.xmi.
+	//   Balloon table @ 2608:0c0a (default 0x2c). Centered:
+	//     bx = (0x140-w)/2, by = (0x5a-h)/2.
+	//   KD reaction @ KDTextIndex[+10] = slot 5; NextScreen = LastScreen;
+	//   _firstTry = 0.
 	const byte *susp = _mystery.floppySuspectEntry((uint)picked);
 	uint16 picId = 0;
 	uint16 alibiOff = 0xFFFF;
@@ -5678,8 +4802,7 @@ void EEMEngine::doAccuseFloppy() {
 		alibiOff = _mystery.alibiTextOffset((uint)picked);
 	}
 
-	// Alibi-screen MIDI sting (FANFARE2.XMI). `_MIDIPlayFile` is
-	// gated on `_voiceOn` (= `DAT_28da_3050`) in the original.
+	// FANFARE2.XMI alibi sting (`_MIDIPlayFile` gated on _voiceOn).
 	if (_music && _voiceOn)
 		_music->playFile(Common::Path("fanfare2.xmi"), false);
 
@@ -5699,18 +4822,12 @@ void EEMEngine::doAccuseFloppy() {
 							_playerName, _partner);
 	}
 
-	// Alibi balloon dispatch. The original reads the alibi-balloon
-	// table at `2608:0c0a + first_char` ??? a SEPARATE table from the
-	// KD-hint table at `2608:0c14`. For digits '0'..'9' the values at
-	// `2608:0c3a..0c43` are { 0x2a, 0x2b, 0x2c, 0x2d, 0xaa, 0xab,
-	// 0xac, 0xad, 0x09, 0x0a }; default (non-digit char) is
-	// `DAT_2608_0c3c` = 0x2c. The high-bit values (0xAA..0xAD) are
-	// `_GetBalloon`'s "mirrored" flag ??? the low 7 bits are the
-	// balloon idx, top bit flips horizontally.
+	// Alibi balloon table @ 2608:0c0a (NOT 0c14). Digits @ 2608:0c3a..0c43.
+	// Default 0x2c. High bit = mirror flag.
 	static const uint8 kFloppyAlibiBalloonByDigit[10] = {
 		0x2a, 0x2b, 0x2c, 0x2d, 0xaa, 0xab, 0xac, 0xad, 0x09, 0x0a
 	};
-	uint balloonRaw = 0x2c; // default `DAT_2608_0c3c`
+	uint balloonRaw = 0x2c; // DAT_2608_0c3c
 	if (!alibi.empty() && alibi[0] >= '0' && alibi[0] <= '9') {
 		balloonRaw = kFloppyAlibiBalloonByDigit[(int)(alibi[0] - '0')];
 		alibi.deleteChar(0);
@@ -5734,9 +4851,7 @@ void EEMEngine::doAccuseFloppy() {
 	Picture balloon;
 	const bool haveBalloon = _balloonArchive.size() > balloonIdx &&
 		_balloonArchive.loadEntry(balloonIdx, balloon);
-	// Centred per `_DisplayAlibi_Floppy @ 1d40:01a0`:
-	//   uVar2 = (0x140 - balloon.w) / 2;  // x
-	//   uVar3 = (0x5a  - balloon.h) / 2;  // y (anchor in top-half)
+	// Centered @ 1d40:01a0: x=(0x140-w)/2, y=(0x5a-h)/2.
 	int balloonX = 0x21;
 	int balloonY = 1;
 	if (haveBalloon) {
@@ -5745,10 +4860,7 @@ void EEMEngine::doAccuseFloppy() {
 		if (balloonX < 0) balloonX = 0;
 		if (balloonY < 0) balloonY = 0;
 		const byte transp = (byte)(balloon.flags >> 8);
-		// `_GetBalloon`'s mirror flag (high bit of the table value)
-		// flips the balloon horizontally ??? the original applies it
-		// inside the blit primitive; ScummVM's `transBlitFrom` exposes
-		// the same via its `flipped` argument.
+		// Mirror flag (high bit) flips balloon horizontally.
 		scene.transBlitFrom(balloon.surface,
 							Common::Point(balloonX, balloonY),
 							transp, flipBalloon);
@@ -5759,19 +4871,13 @@ void EEMEngine::doAccuseFloppy() {
 		_font.drawWordWrapped(&scene, balloonX + tx, balloonY + ty,
 							  MAX<int>(8, (int)tw), alibi, 0);
 	}
-	// The floppy original keeps the accuse partner animation slot alive
-	// across `_DisplayAlibi_Floppy` and restores the screen with active
-	// animations between alibi phases. Stamp the same resting frame here
-	// before the KD reaction helper snapshots the current screen.
+	// Stamp partner resting frame before KD reaction snapshots screen.
 	blitAccusePartner(scene, _aniArchive, _partner, g_system->getMillis());
 	g_system->copyRectToScreen(scene.getPixels(), scene.pitch, 0, 0, 320, 200);
 	g_system->updateScreen();
 
-	// `_DisplayAlibi_Floppy` does NOT play any per-suspect VOC ??? the
-	// alibi table at `2608:0c5e` (3 bytes: 0x15, 0x16, 0x17) is
-	// referenced by the post-win scrapbook (`FUN_1d40_05b7`), not the
-	// alibi screen. The MIDI sting we kicked off above carries the
-	// audio.
+	// No per-suspect VOC ??? alibi table @ 2608:0c5e is for post-win
+	// scrapbook (`FUN_1d40_05b7`), not alibi.
 
 	while (!shouldQuit()) {
 		Common::Event ev;
@@ -5791,12 +4897,7 @@ void EEMEngine::doAccuseFloppy() {
 		g_system->delayMillis(10);
 	}
 
-	// Partner reaction ??? `KDTextIndex[+10]` is byte offset 10 = u16
-	// stride entry 5 (NOT 8). Verified at
-	// `_DisplayAlibi_Floppy @ 1d40:01ee`:
-	//   `iVar4 = MysteryOff + *(KDTextIndex + 10);`
-	// Our `showFloppyKDHint(slot)` reads `kdIdx + slot * 2`, so slot
-	// 5 is the right argument here.
+	// `_DisplayAlibi_Floppy @ 1d40:01ee` ??? KDTextIndex[+10] = slot 5.
 	showFloppyKDHint(5);
 	if (_music && _voiceOn)
 		_music->stop();
@@ -5810,20 +4911,8 @@ void EEMEngine::drawAccuseGallery(uint8 numSuspects, const byte *gd,
 								   int highlighted,
 								   Common::Array<Common::Rect> &slotRects,
 								   Common::Array<int> &slotSuspect) {
-	// Accuse-gallery redraw ??? formerly the `drawGallery` lambda inside
-	// `doAccuse`. Mirrors `_DoAccuseGallery @ 1df2:0a31` portrait grid:
-	// PIC 0x3f backdrop, suspect portraits at the 5 fixed slots
-	// (`kGallerySlots` in this file's anon namespace), and a 1-px
-	// outline (palette index 0xFE) around the highlighted slot.
-	//
-	// Partner sprite at (5, 0x50): the original `_DoAccuse @ 1df2:0bdd`
-	// registers `_NewAnimation(5, 0x50, partnerCells, script=2, prior=1)`
-	// (1df2:0c30) BEFORE calling `_DoAccuseGallery`, then `_DrawGallery`
-	// calls `_DrawActiveAnimations` (158f:00a3) which re-renders the
-	// slot every frame. Without an explicit blit here, our port's
-	// accuse screen comes out partner-less. Anim CELLS are 2 (Jake) /
-	// 0x10 (Jenny); SCRIPT key is 0x02 for both partners (matches the
-	// `CONCAT22(2, ...)` arg verified at 1df2:0c2e).
+	// `_DoAccuseGallery @ 1df2:0a31` portrait grid. PIC 0x3f + 5 slots.
+	// Partner (5, 0x50) ANI 2/0x10, script 0x02 (`_DoAccuse @ 1df2:0c30`).
 	Picture accuseBg;
 	const bool haveAccuseBg = _picsArchive.getPicture(0x3f, accuseBg);
 
@@ -5833,10 +4922,7 @@ void EEMEngine::drawAccuseGallery(uint8 numSuspects, const byte *gd,
 	if (haveAccuseBg)
 		scratch.simpleBlitFrom(accuseBg.surface);
 
-	// Partner sprite, drawn BEFORE portraits so the suspect grid
-	// covers it where they overlap (the gallery slots start at
-	// y=14 / y=90, partner is at y=0x50=80 ??? no overlap, so order
-	// is purely defensive).
+	// Partner drawn first; defensive (no slot overlap).
 	const uint partnerAnim = (_partner == 0) ? 2 : 0x10;
 	Animation partnerAni;
 	if (_aniArchive.loadAnimation(partnerAnim, partnerAni) &&
@@ -5856,8 +4942,7 @@ void EEMEngine::drawAccuseGallery(uint8 numSuspects, const byte *gd,
 		const uint8 phys = _mystery._newOrder[i];
 		if (phys >= 5)
 			continue;
-		// `_DrawGallery @ 158f:00b9` skips suspects whose
-		// `_InGallery[phys]` flag is 0 ??? that's the original gate.
+		// `_DrawGallery @ 158f:00b9` skip on `_InGallery[phys]==0`.
 		if (_mystery._inGallery[phys] == 0)
 			continue;
 		const GallerySlot &s = kGallerySlots[phys];
@@ -5894,11 +4979,7 @@ void EEMEngine::drawAccuseGallery(uint8 numSuspects, const byte *gd,
 		slotSuspect[i] = (int)i;
 	}
 
-	// Highlight indicator. The original moves the mouse cursor to the
-	// centre of the highlighted suspect via `_PutMouseInRect` (1df2:0b8e);
-	// we draw a 1-px outline in palette index 0xFE instead, which sits
-	// inside the marching-ants cycle range 0xF9..0xFE and is visible
-	// under any palette without warping the player's cursor.
+	// Highlight outline (original uses `_PutMouseInRect` @ 1df2:0b8e).
 	if (highlighted >= 0 && highlighted < (int)slotRects.size() &&
 		!slotRects[highlighted].isEmpty()) {
 		Common::Rect r = slotRects[highlighted];


Commit: 37e6dc1ba6887c4b784d40246036e1d73cde2107
    https://github.com/scummvm/scummvm/commit/37e6dc1ba6887c4b784d40246036e1d73cde2107
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:21:10+02:00

Commit Message:
EEM: enforced code standards, mostly removed one-lined statements

Changed paths:
    engines/eem/clues.cpp
    engines/eem/eem.cpp
    engines/eem/eem.h
    engines/eem/graphics.cpp
    engines/eem/music.h
    engines/eem/ui.cpp


diff --git a/engines/eem/clues.cpp b/engines/eem/clues.cpp
index ebe7383137a..cedbf6d1319 100644
--- a/engines/eem/clues.cpp
+++ b/engines/eem/clues.cpp
@@ -359,7 +359,7 @@ void EEMEngine::doInitClues() {
 				while (g_system->getEventManager()->pollEvent(ev)) {
 					if (ev.type == Common::EVENT_KEYDOWN &&
 						ev.kbd.keycode == Common::KEYCODE_ESCAPE) {
-						interruptAudio(/*stopMusicToo=*/false);
+						interruptAudio(/* stopMusicToo= */ false);
 						skip = true;
 						break;
 					}
@@ -471,7 +471,7 @@ void EEMEngine::doInitClues() {
 					while (g_system->getEventManager()->pollEvent(ev)) {
 						if (ev.type == Common::EVENT_KEYDOWN &&
 							ev.kbd.keycode == Common::KEYCODE_ESCAPE) {
-							interruptAudio(/*stopMusicToo=*/false);
+							interruptAudio(/* stopMusicToo= */ false);
 							skip = true;
 							break;
 						}
@@ -756,13 +756,13 @@ void EEMEngine::displayClue(const byte *clueBlock) {
 				// _WordWrap(bubX + table[bub].x, bubY + table[bub].y,
 				//           table[bub].w, ...).
 				// Fallback (5, 4, 155) = entry-23 inset.
-				uint16 bx = 5;
-				uint16 by = 4;
-				uint16 bw_ = 155;
-				getBalloonInsets(balloonId, bx, by, bw_);
-				textX = bubX + bx;
-				textY = bubY + by;
-				textW = bw_;
+				uint16 insetX = 5;
+				uint16 insetY = 4;
+				uint16 insetW = 155;
+				getBalloonInsets(balloonId, insetX, insetY, insetW);
+				textX = bubX + insetX;
+				textY = bubY + insetY;
+				textW = insetW;
 				copyH = bh;
 			} else {
 				// No balloon: clear band.
@@ -825,7 +825,7 @@ void EEMEngine::displayClue(const byte *clueBlock) {
 						advance = true;
 						skipAll = true;
 						// Cut voice + spool only (keep MIDI).
-						interruptAudio(/*stopMusicToo=*/false);
+						interruptAudio(/* stopMusicToo= */ false);
 						break;
 					}
 					if (ev.type == Common::EVENT_LBUTTONDOWN) {
@@ -856,7 +856,7 @@ void EEMEngine::displayClue(const byte *clueBlock) {
 
 	// _StopTheVoice @ 1ff1:0283 effect (voice only, keep MIDI). Diverges
 	// from _DisplayClue @ 2404:05e6 which lets voice bleed past dismissal.
-	interruptAudio(/*stopMusicToo=*/false);
+	interruptAudio(/* stopMusicToo= */ false);
 }
 
 void EEMEngine::displayFloppyDialogRecords(const byte *rec, uint count,
@@ -934,7 +934,7 @@ void EEMEngine::displayFloppyDialogRecords(const byte *rec, uint count,
 					continue;
 				if (ev.type == Common::EVENT_KEYDOWN &&
 					ev.kbd.keycode == Common::KEYCODE_ESCAPE) {
-					interruptAudio(/*stopMusicToo=*/false);
+					interruptAudio(/* stopMusicToo= */ false);
 					return true;  // skip
 				}
 				if (ev.type == Common::EVENT_LBUTTONDOWN ||
diff --git a/engines/eem/eem.cpp b/engines/eem/eem.cpp
index b809bf86c41..0afc5f5dc9f 100644
--- a/engines/eem/eem.cpp
+++ b/engines/eem/eem.cpp
@@ -285,7 +285,7 @@ Common::Error EEMEngine::run() {
 	}
 
 	if (resumed)
-		goto screen_loop;
+		goto screenLoop;
 
 	// _DoOpeningAnims @ 2520:082a:
 	//   EA Kids logo (PIC) -> HighScore logo (PIC) -> Storm logo
@@ -325,13 +325,13 @@ Common::Error EEMEngine::run() {
 		if (!shouldQuit() && !_skipIntro)
 			showFloppyStormLogo();
 		if (!shouldQuit() && !_skipIntro && _music)
-			_music->playFile(Common::Path("THEME.XMI"), /*loop=*/true);
+			_music->playFile(Common::Path("THEME.XMI"), /* loop= */ true);
 		if (!shouldQuit() && !_skipIntro)
 			playAnm(Common::Path("CHAT.ANM"), 120,
-					/*holdLastFrame=*/false);
+					/* holdLastFrame= */ false);
 		if (!shouldQuit() && !_skipIntro)
 			playAnm(Common::Path("MOVIE.ANM"), 120,
-					/*holdLastFrame=*/false);
+					/* holdLastFrame= */ false);
 	} else {
 		showEAKidsLogo();
 		if (!shouldQuit() && !_skipIntro)
@@ -344,7 +344,7 @@ Common::Error EEMEngine::run() {
 			if (_audio)
 				_audio->playVoc(Common::Path("THUNDER.VOC"));
 			playAnm(Common::Path("BOLT.ANM"), 120,
-					/*holdLastFrame=*/false, /*fadeIn=*/true);
+					/* holdLastFrame= */ false, /* fadeIn= */ true);
 			waitForInput(1800);
 			fadeCurrentPaletteToBlack();
 			if (_audio)
@@ -354,14 +354,14 @@ Common::Error EEMEngine::run() {
 		if (!shouldQuit() && !_skipIntro && _audio)
 			_audio->initMysterySounds(60);
 		if (!shouldQuit() && !_skipIntro && _music)
-			_music->playFile(Common::Path("THEME.XMI"), /*loop=*/true);
+			_music->playFile(Common::Path("THEME.XMI"), /* loop= */ true);
 		for (int i = 1; i <= 20 && !shouldQuit() && !_skipIntro; i++) {
 			const bool fadeIn = (i == 1 || i == 5);
 			if (i == 5)
 				fadeCurrentPaletteToBlack();
 			Common::String name = Common::String::format("ANIM%02d.A", i);
 			playAnm(Common::Path(name), 120,
-					/*holdLastFrame=*/false, fadeIn);
+					/* holdLastFrame= */ false, fadeIn);
 			// _SpoolSound(uVar3 - 1) @ 2520:08c2 ??? per-anim VO, skipped
 			// when uVar3 == 0x14 @ 2520:08a8.
 			if (!shouldQuit() && !_skipIntro && i != 20 && _audio) {
@@ -374,17 +374,17 @@ Common::Error EEMEngine::run() {
 			_audio->cleanMysterySounds();
 		// _MIDIPlayFile("theme.xmi") @ 2520:0918.
 		if (!shouldQuit() && !_skipIntro && _music)
-			_music->playFile(Common::Path("THEME.XMI"), /*loop=*/true);
+			_music->playFile(Common::Path("THEME.XMI"), /* loop= */ true);
 		if (!shouldQuit() && !_skipIntro)
 			playAnm(Common::Path("TITLE.ANM"), 120,
-					/*holdLastFrame=*/true, /*fadeIn=*/true);
+					/* holdLastFrame= */ true, /* fadeIn= */ true);
 	}
 	skippedIntro = _skipIntro;
 	_skipIntro = false;
 
 	if (isFloppy() && !shouldQuit() && !skippedIntro) {
 		_nextScreen = kScreenTitle;
-		goto screen_loop;
+		goto screenLoop;
 	}
 
 	// Title(B) -> screen 8 (profile) -> 9 (partner) -> C (action) ->
@@ -413,7 +413,7 @@ Common::Error EEMEngine::run() {
 	// `_mystery.load()` fresh and discard site / clue progress).
 	if (!shouldQuit() && !resumed)
 		_nextScreen = _mystery.isLoaded() ? kScreenMap : kScreenAction;
-screen_loop:
+screenLoop:
 	while (!shouldQuit() && _nextScreen != kScreenInvalid) {
 		const ScreenId current = (ScreenId)_nextScreen;
 		debugC(1, kDebugGeneral, "screenDriver: id=%d", (int)current);
@@ -427,7 +427,7 @@ screen_loop:
 			if (isFloppy()) {
 				CursorMan.showMouse(false);
 				playAnm(Common::Path("TITLE.ANM"), 120,
-						/*holdLastFrame=*/true, /*fadeIn=*/true);
+						/* holdLastFrame= */ true, /* fadeIn= */ true);
 				_skipIntro = false;
 				CursorMan.showMouse(true);
 			}
@@ -963,7 +963,7 @@ void EEMEngine::startTravelMusic() {
 	if (!_music || !_mystery.isLoaded() || !_voiceOn)
 		return;
 	const uint num = _mystery._siteNumber % 5;
-	_music->playMus(num, /*loop=*/false);
+	_music->playMus(num, /* loop= */ false);
 }
 
 void EEMEngine::waitForMusicDone(uint32 maxMs) {
@@ -1180,7 +1180,7 @@ Common::Error EEMEngine::saveProfile(const Common::String &name) {
 	_playerName = name;
 	debugC(1, kDebugGeneral, "saveProfile(%s) -> slot %d",
 		   name.c_str(), slot);
-	return saveGameState(slot, name, /*isAutosave=*/false);
+	return saveGameState(slot, name, /* isAutosave= */ false);
 }
 
 bool EEMEngine::loadProfile(const Common::String &name) {
diff --git a/engines/eem/eem.h b/engines/eem/eem.h
index 2d3973a4d97..5935445df7a 100644
--- a/engines/eem/eem.h
+++ b/engines/eem/eem.h
@@ -154,7 +154,7 @@ public:
 	DBDArchive &getPics()    { return _picsArchive; }
 	DBDArchive &getAni()     { return _aniArchive; }
 	DBDArchive &getSites()   { return _sitesArchive; }
-	DBDArchive &getBalloons(){ return _balloonArchive; }
+	DBDArchive &getBalloons() { return _balloonArchive; }
 	DBDArchive &getButtons() { return _buttonArchive; }
 	Mystery    &getMystery() { return _mystery; }
 	const EEMFont &getFont() const { return _font; }
diff --git a/engines/eem/graphics.cpp b/engines/eem/graphics.cpp
index c11505fa1dd..8251aee33f7 100644
--- a/engines/eem/graphics.cpp
+++ b/engines/eem/graphics.cpp
@@ -198,12 +198,16 @@ void EEMEngine::doHelp() {
 		const char *str2 = nullptr;
 		const char *str3 = nullptr;
 		const char *p = str1;
-		while ((uint)((const byte *)p - hd) < hsz && *p != 0) p++;
-		if ((uint)((const byte *)p - hd) >= hsz) return;
+		while ((uint)((const byte *)p - hd) < hsz && *p != 0)
+			p++;
+		if ((uint)((const byte *)p - hd) >= hsz)
+			return;
 		str2 = p + 1;
 		p = str2;
-		while ((uint)((const byte *)p - hd) < hsz && *p != 0) p++;
-		if ((uint)((const byte *)p - hd) >= hsz) return;
+		while ((uint)((const byte *)p - hd) < hsz && *p != 0)
+			p++;
+		if ((uint)((const byte *)p - hd) >= hsz)
+			return;
 		str3 = p + 1;
 
 		const char *chosen = nullptr;
diff --git a/engines/eem/music.h b/engines/eem/music.h
index 59dac57e30d..11001a4280a 100644
--- a/engines/eem/music.h
+++ b/engines/eem/music.h
@@ -60,8 +60,8 @@ public:
 	/// floppy: TRAVEL-N.XMI / FANFARE2.XMI.
 	void playMus(uint num, bool loop = false);
 
-	// Miles drivers handle source-channel routing themselves; bypass
-	// Audio::MidiPlayer::sendToChannel. Same workaround as Toltecs / SAGA.
+	// WORKAROUND: Miles drivers handle source-channel routing themselves;
+	// bypass Audio::MidiPlayer::sendToChannel. Same as Toltecs / SAGA.
 	void send(uint32 b) override;
 
 private:
diff --git a/engines/eem/ui.cpp b/engines/eem/ui.cpp
index f8c888d1843..21a8881c7cc 100644
--- a/engines/eem/ui.cpp
+++ b/engines/eem/ui.cpp
@@ -1455,11 +1455,17 @@ void EEMEngine::doSetup() {
 				continue;
 			}
 			if (kKid1Rect.contains(mx, my)) {
-				if (_partner != 0) { _partner = 0; dirty = true; }
+				if (_partner != 0) {
+					_partner = 0;
+					dirty = true;
+				}
 				continue;
 			}
 			if (kKid2Rect.contains(mx, my)) {
-				if (_partner != 1) { _partner = 1; dirty = true; }
+				if (_partner != 1) {
+					_partner = 1;
+					dirty = true;
+				}
 				continue;
 			}
 
@@ -1532,7 +1538,7 @@ void EEMEngine::doSetup() {
 					// Restore BG between cards (1560:02e5 `_vga_fvidvid(0)`).
 					draw();
 					const Common::KeyCode k =
-						showFullscreenPic(kHelp1Pics[i], /*transparent=*/true);
+						showFullscreenPic(kHelp1Pics[i], /* transparent= */ true);
 					if (k == Common::KEYCODE_ESCAPE)
 						break;
 				}
@@ -1544,7 +1550,7 @@ void EEMEngine::doSetup() {
 			// Credits [11] @ 1f78:025a ??? PIC 0x208 fullscreen.
 			if (kCreditsBtn.contains(mx, my)) {
 				CursorMan.showMouse(false);
-				showFullscreenPic(0x208, /*transparent=*/false);
+				showFullscreenPic(0x208, /* transparent= */ false);
 				CursorMan.showMouse(true);
 				// PIC 0x208 has its own baked palette; restore site 0.
 				setSitePalette(0);
@@ -1620,14 +1626,26 @@ void EEMEngine::doActionScreen() {
 	//   stage 3: grey Practice; SB3 needs tier-3 solve
 	//   stage 4: grey Choose + Practice
 	bool anySolved1 = false;
-	for (uint i = 1; i <= 0x18 && i < sizeof(_mysteriesSolved); i++)
-		if (_mysteriesSolved[i]) { anySolved1 = true; break; }
+	for (uint i = 1; i <= 0x18 && i < sizeof(_mysteriesSolved); i++) {
+		if (_mysteriesSolved[i]) {
+			anySolved1 = true;
+			break;
+		}
+	}
 	bool anySolved2 = false;
-	for (uint i = 0x19; i <= 0x30 && i < sizeof(_mysteriesSolved); i++)
-		if (_mysteriesSolved[i]) { anySolved2 = true; break; }
+	for (uint i = 0x19; i <= 0x30 && i < sizeof(_mysteriesSolved); i++) {
+		if (_mysteriesSolved[i]) {
+			anySolved2 = true;
+			break;
+		}
+	}
 	bool anySolved3 = false;
-	for (uint i = 0x31; i <= 0x36 && i < sizeof(_mysteriesSolved); i++)
-		if (_mysteriesSolved[i]) { anySolved3 = true; break; }
+	for (uint i = 0x31; i <= 0x36 && i < sizeof(_mysteriesSolved); i++) {
+		if (_mysteriesSolved[i]) {
+			anySolved3 = true;
+			break;
+		}
+	}
 
 	const bool chooseOn   = _chainStage < 4;
 	const bool practiceOn = _chainStage <= 1;
@@ -1643,7 +1661,10 @@ void EEMEngine::doActionScreen() {
 	// Seed selection on first enabled entry.
 	uint pick = 0;
 	for (uint i = 0; i < kNumPicks; i++) {
-		if (kPickEnabled[i]) { pick = i; break; }
+		if (kPickEnabled[i]) {
+			pick = i;
+			break;
+		}
 	}
 
 	const char *kSeparator = "----------------------------------";
@@ -1838,9 +1859,18 @@ void EEMEngine::doCaseSelection() {
 	uint stageLo = 1, stageHi = 0x18;
 	uint book = 1;
 	switch (_chainStage) {
-	case 2: stageLo = 0x19; stageHi = 0x30; book = 2; break;
-	case 3: stageLo = 0x31; stageHi = 0x36; book = 3; break;
-	default: break;
+	case 2:
+		stageLo = 0x19;
+		stageHi = 0x30;
+		book = 2;
+		break;
+	case 3:
+		stageLo = 0x31;
+		stageHi = 0x36;
+		book = 3;
+		break;
+	default:
+		break;
 	}
 	if (stageHi > kMaxMystery)
 		stageHi = kMaxMystery;
@@ -1924,7 +1954,10 @@ void EEMEngine::doCaseSelection() {
 					return;
 				}
 				if (kChooserUpArrowRect.contains(ev.mouse.x, ev.mouse.y)) {
-					if (topRow > 0) { topRow--; dirty = true; }
+					if (topRow > 0) {
+						topRow--;
+						dirty = true;
+					}
 					continue;
 				}
 				if (kChooserDnArrowRect.contains(ev.mouse.x, ev.mouse.y)) {
@@ -3204,20 +3237,21 @@ void EEMEngine::drawBigMapOverview(uint32 elapsedMs) {
 									: READ_LE_UINT16(entry + 0x6);
 		const uint16 crime = floppy ? (uint16)entry[0xa]
 									: READ_LE_UINT16(entry + 0xc);
-		const bool   done_ = (i < Mystery::kVisitedSiteCap)
-							  && _mystery._visitedSite[i];
+		const bool isDone = (i < Mystery::kVisitedSiteCap)
+							 && _mystery._visitedSite[i];
 
 		const Picture *m = nullptr;
 		bool useVisitedColors = false;
-		if (done_ && haveDone)
+		if (isDone && haveDone) {
 			m = &done;
-		else if (done_ && haveNormal) {
+		} else if (isDone && haveNormal) {
 			m = &normal;
 			useVisitedColors = true;
-		} else if (crime != 0 && haveCrime)
+		} else if (crime != 0 && haveCrime) {
 			m = &crimeM;
-		else if (haveNormal)
+		} else if (haveNormal) {
 			m = &normal;
+		}
 
 		if (m) {
 			blitBigMapMarker(scratch, *m, (int)mx, (int)my,
@@ -3886,7 +3920,7 @@ void EEMEngine::doAccuse() {
 					balloonY = (0x50 - balloon.surface.h) / 2;
 
 				// Render gallery first so the snapshot includes partner.
-				drawAccuseGallery(num, gd, /*highlighted=*/-1,
+				drawAccuseGallery(num, gd, /* highlighted= */ -1,
 								  slotRects, slotSuspect);
 
 				Graphics::ManagedSurface ms(320, 200,
@@ -4147,7 +4181,7 @@ void EEMEngine::doAccuse() {
 
 		// MIDI 6 ??? blocks until done (or click/ESC aborts).
 		if (_music && _voiceOn) {
-			_music->playMus(6, /*loop=*/false);
+			_music->playMus(6, /* loop= */ false);
 			const uint32 musStart = g_system->getMillis();
 			bool aborted = false;
 			while (_music->isPlaying() && !shouldQuit() && !aborted) {
@@ -4265,12 +4299,23 @@ void EEMEngine::doAccuse() {
 		// Chain advance @ 1df2:0824-0850. Skip mystery 0 (practice).
 		// stage 1: 1..0x18, stage 2: 0x19..0x30, stage 3: 0x31..0x36.
 		if (mn != 0) {
-			uint lo = 0, hi = 0;
+			uint lo = 0;
+			uint hi = 0;
 			switch (_chainStage) {
-			case 1: lo = 1;    hi = 0x18; break;
-			case 2: lo = 0x19; hi = 0x30; break;
-			case 3: lo = 0x31; hi = 0x36; break;
-			default: break;
+			case 1:
+				lo = 1;
+				hi = 0x18;
+				break;
+			case 2:
+				lo = 0x19;
+				hi = 0x30;
+				break;
+			case 3:
+				lo = 0x31;
+				hi = 0x36;
+				break;
+			default:
+				break;
 			}
 			bool allSolved = (hi >= lo);
 			for (uint i = lo; i <= hi && allSolved; i++) {
@@ -4332,7 +4377,7 @@ void EEMEngine::doAccuse() {
 		g_system->updateScreen();
 
 		if (_music && _voiceOn)
-			_music->playMus(5, /*loop=*/false);
+			_music->playMus(5, /* loop= */ false);
 
 		// Chain recap ??? partner enumerates required clues.
 		const byte *solved = _mystery.solvedClueBlock();
@@ -4718,7 +4763,7 @@ void EEMEngine::doAccuseFloppy() {
 				if (tail) {
 					displayFloppyDialogRecords(records, beforeScrapbook, 1);
 					playAnm(Common::Path("SCRAPBK.ANI"), 120,
-							/*holdLastFrame=*/false, /*fadeIn=*/true);
+							/* holdLastFrame= */ false, /* fadeIn= */ true);
 					displayFloppyDialogRecords(tail, 3, 1);
 				} else {
 					warning("doAccuseFloppy: malformed solved chain");
@@ -4737,12 +4782,23 @@ void EEMEngine::doAccuseFloppy() {
 
 		// Tier promotion @ 1d40:0941..0978. Skip mystery 0 (practice).
 		if (mn != 0) {
-			uint lo = 0, hi = 0;
+			uint lo = 0;
+			uint hi = 0;
 			switch (_chainStage) {
-			case 1: lo = 1;    hi = 0x18; break;
-			case 2: lo = 0x19; hi = 0x30; break;
-			case 3: lo = 0x31; hi = 0x36; break;
-			default: break;
+			case 1:
+				lo = 1;
+				hi = 0x18;
+				break;
+			case 2:
+				lo = 0x19;
+				hi = 0x30;
+				break;
+			case 3:
+				lo = 0x31;
+				hi = 0x36;
+				break;
+			default:
+				break;
 			}
 			bool allSolved = (hi >= lo);
 			for (uint i = lo; i <= hi && allSolved; i++) {
@@ -4857,8 +4913,10 @@ void EEMEngine::doAccuseFloppy() {
 	if (haveBalloon) {
 		balloonX = (320 - balloon.surface.w) / 2;
 		balloonY = (0x5a - balloon.surface.h) / 2;
-		if (balloonX < 0) balloonX = 0;
-		if (balloonY < 0) balloonY = 0;
+		if (balloonX < 0)
+			balloonX = 0;
+		if (balloonY < 0)
+			balloonY = 0;
 		const byte transp = (byte)(balloon.flags >> 8);
 		// Mirror flag (high bit) flips balloon horizontally.
 		scene.transBlitFrom(balloon.surface,


Commit: c3c78f3e10f03d724f584c6027e75f66eed3e735
    https://github.com/scummvm/scummvm/commit/c3c78f3e10f03d724f584c6027e75f66eed3e735
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:21:10+02:00

Commit Message:
EEM: removed lambdas in favor of explicit functions

Changed paths:
    engines/eem/clues.cpp
    engines/eem/eem.h
    engines/eem/site.cpp
    engines/eem/ui.cpp


diff --git a/engines/eem/clues.cpp b/engines/eem/clues.cpp
index cedbf6d1319..39aca26fb17 100644
--- a/engines/eem/clues.cpp
+++ b/engines/eem/clues.cpp
@@ -859,6 +859,40 @@ void EEMEngine::displayClue(const byte *clueBlock) {
 	interruptAudio(/* stopMusicToo= */ false);
 }
 
+bool EEMEngine::floppyDialogWaitForClick() {
+	// Drain pending events so a stale keystroke doesn't auto-advance.
+	Common::Event drain;
+	while (g_system->getEventManager()->pollEvent(drain)) {}
+	setInteractiveMouseCursor(false);
+	const uint32 minVisibleMs = 250;
+	const uint32 startedAt = g_system->getMillis();
+	while (!shouldQuit()) {
+		Common::Event ev;
+		while (g_system->getEventManager()->pollEvent(ev)) {
+			if (ev.type == Common::EVENT_QUIT ||
+				ev.type == Common::EVENT_RETURN_TO_LAUNCHER)
+				return true;  // skip
+			if (ev.type == Common::EVENT_MOUSEMOVE) {
+				setInteractiveMouseCursor(false);
+				continue;
+			}
+			if (g_system->getMillis() - startedAt < minVisibleMs)
+				continue;
+			if (ev.type == Common::EVENT_KEYDOWN &&
+				ev.kbd.keycode == Common::KEYCODE_ESCAPE) {
+				interruptAudio(/* stopMusicToo= */ false);
+				return true;  // skip
+			}
+			if (ev.type == Common::EVENT_LBUTTONDOWN ||
+				ev.type == Common::EVENT_KEYDOWN)
+				return false;  // advance one page
+		}
+		g_system->updateScreen();
+		g_system->delayMillis(10);
+	}
+	return true;
+}
+
 void EEMEngine::displayFloppyDialogRecords(const byte *rec, uint count,
 											uint lastIndicator) {
 	// Render `count` consecutive floppy dialog records starting at
@@ -913,40 +947,6 @@ void EEMEngine::displayFloppyDialogRecords(const byte *rec, uint count,
 		}
 	}
 
-	auto waitForClick = [&]() -> bool {
-		// Drain pending events so a stale keystroke doesn't auto-advance.
-		Common::Event drain;
-		while (g_system->getEventManager()->pollEvent(drain)) {}
-		setInteractiveMouseCursor(false);
-		const uint32 minVisibleMs = 250;
-		const uint32 startedAt = g_system->getMillis();
-		while (!shouldQuit()) {
-			Common::Event ev;
-			while (g_system->getEventManager()->pollEvent(ev)) {
-				if (ev.type == Common::EVENT_QUIT ||
-					ev.type == Common::EVENT_RETURN_TO_LAUNCHER)
-					return true;  // skip
-				if (ev.type == Common::EVENT_MOUSEMOVE) {
-					setInteractiveMouseCursor(false);
-					continue;
-				}
-				if (g_system->getMillis() - startedAt < minVisibleMs)
-					continue;
-				if (ev.type == Common::EVENT_KEYDOWN &&
-					ev.kbd.keycode == Common::KEYCODE_ESCAPE) {
-					interruptAudio(/* stopMusicToo= */ false);
-					return true;  // skip
-				}
-				if (ev.type == Common::EVENT_LBUTTONDOWN ||
-					ev.type == Common::EVENT_KEYDOWN)
-					return false;  // advance one page
-			}
-			g_system->updateScreen();
-			g_system->delayMillis(10);
-		}
-		return true;
-	};
-
 	for (uint i = 0; i < count && !shouldQuit(); i++) {
 		const uint16 picID    = READ_LE_UINT16(rec + 0);
 		const uint16 picX     = READ_LE_UINT16(rec + 2);
@@ -1158,7 +1158,7 @@ void EEMEngine::displayFloppyDialogRecords(const byte *rec, uint count,
 			g_system->updateScreen();
 
 			if (waitNeeded) {
-				if (waitForClick()) {
+				if (floppyDialogWaitForClick()) {
 					skipAll = true;
 					break;
 				}
diff --git a/engines/eem/eem.h b/engines/eem/eem.h
index 5935445df7a..6d83cdc4da9 100644
--- a/engines/eem/eem.h
+++ b/engines/eem/eem.h
@@ -25,6 +25,7 @@
 #define EEM_EEM_H
 
 #include "common/array.h"
+#include "common/keyboard.h"
 #include "common/language.h"
 #include "common/platform.h"
 #include "common/random.h"
@@ -268,6 +269,52 @@ private:
 
 	/// Re-render helpers for the corresponding `doX()` modal screens.
 	void drawNotebookFrame(int &page);
+
+	/// Resolve a single NoteIndex entry to displayable notebook text.
+	/// Handles the CD (4-byte) vs floppy (7-byte) entry strides.
+	Common::String notebookNoteText(uint clueId, const byte *ni,
+									uint16 niCount, bool floppyNb,
+									const byte *bufBase,
+									uint32 mysSz) const;
+
+	/// Shared state for `doAccuseNotes` helpers (text lookup, pagination,
+	/// render). Pointer fields refer to locals owned by `doAccuseNotes`.
+	struct AccuseNotesCtx {
+		const byte *ni;
+		uint16 niCount;
+		bool floppyNote;
+		const byte *bufBaseNotes;
+		const Common::Array<uint> *found;
+		int rectX, rectY, rectW, rectH;
+		uint expected;
+		bool haveBg;
+		const Picture *accuseBg;
+		Common::Array<Common::Rect> *slotRects;
+		Common::Array<uint> *slotClues;
+		int *pageBreaks;
+		int pageBreaksCap;
+		int *numPages;
+		int *page;
+	};
+
+	/// One NoteIndex entry as displayable accuse-screen text.
+	Common::String accuseNoteText(uint clueId, const AccuseNotesCtx &ctx) const;
+
+	/// Recompute `pageBreaks`/`numPages` after `_noteSelected` or font changes.
+	void accuseRebuildPagination(const AccuseNotesCtx &ctx);
+
+	/// Render one frame of the accuse-notes screen.
+	void accuseDrawScreen(const AccuseNotesCtx &ctx);
+
+	/// Floppy accuse helpers: KD-balloon hint (`_DisplayHint_Floppy @
+	/// 1503:00ca`) and the suspect portrait grid
+	/// (`_DrawGallery_Floppy @ 154e:0050`).
+	void floppyKDHint(uint kdSlot, const byte *kdIdx,
+					  const byte *bufBase, uint32 mysSize);
+	void accuseDrawGallery(int highlighted,
+						   Common::Array<Common::Rect> &rects,
+						   Common::Array<int> &suspects, uint8 num,
+						   bool haveAccuseBg, const Picture &accuseBg);
 	void drawGalleryFrame(const byte *gd, uint8 numSuspects,
 						  Common::Array<Common::Rect> &slotRects,
 						  Common::Array<int> &slotSuspect);
@@ -366,6 +413,12 @@ private:
 	/// pick via SwapColors on Kid1/Kid2 rects.
 	void doSetup();
 
+	/// `doSetup` helpers: redraw BG + label highlights, render a help/
+	/// credits card with blocking input wait, and the shared exit fallback.
+	void setupDrawScreen();
+	Common::KeyCode setupShowFullscreenPic(uint16 picId, bool transparent);
+	void setupLeave();
+
 	/// `_DoInitClues @ 1a35:0411` (minus live ANI sequence playback).
 	void doInitClues();
 
@@ -383,6 +436,11 @@ private:
 	void displayFloppyDialogRecords(const byte *rec, uint count,
 									 uint lastIndicator = 0);
 
+	/// Wait for a click/keypress to advance a floppy dialog page.
+	/// Returns true if the user requested to skip the rest (ESC/QUIT),
+	/// false to advance one page.
+	bool floppyDialogWaitForClick();
+
 public:
 	/// `_StartTravelMusic @ 20a2:0595`. Picks `MUS%05d.XMI` from
 	/// `_mystery._siteNumber % 5`, one-shot.
diff --git a/engines/eem/site.cpp b/engines/eem/site.cpp
index 167f9d0d1cd..47bd881a88d 100644
--- a/engines/eem/site.cpp
+++ b/engines/eem/site.cpp
@@ -1423,6 +1423,11 @@ void SiteScreen::renderBackground(uint siteNum) {
 	}
 }
 
+void bumpHotspotEdgeColor(byte &color) {
+	const byte next = (byte)(color + 1);
+	color = (next > 0xFE) ? (byte)0xF9 : next;
+}
+
 void SiteScreen::renderHotspots(uint siteNum) {
 	// `_DrawSearchButtons`. Port adds optional "hide hint" setting.
 	if (ConfMan.getBool("hide_highlight_boxes"))
@@ -1484,33 +1489,29 @@ void SiteScreen::renderHotspots(uint siteNum) {
 			// the colour per pixel through palette indices 0xF9..0xFE,
 			// which `_ColorCycle(0xF9, 0xFE)` rotates every tick.
 			byte color = (byte)(0xF9 + ((i + (tickMs / 80)) & 0x07) % 6);
-			auto bumpColor = [&]() {
-				const byte next = (byte)(color + 1);
-				color = (next > 0xFE) ? (byte)0xF9 : next;
-			};
 			// Top edge
 			for (int x = rect.left; x < rect.right; x++) {
 				if (x >= 0 && x < screen->w && rect.top >= 0 && rect.top < screen->h)
 					*(byte *)screen->getBasePtr(x, rect.top) = color;
-				bumpColor();
+				bumpHotspotEdgeColor(color);
 			}
 			// Right edge
 			for (int y = rect.top; y < rect.bottom; y++) {
 				if (rect.right - 1 >= 0 && rect.right - 1 < screen->w && y >= 0 && y < screen->h)
 					*(byte *)screen->getBasePtr(rect.right - 1, y) = color;
-				bumpColor();
+				bumpHotspotEdgeColor(color);
 			}
 			// Bottom edge
 			for (int x = rect.right - 1; x >= rect.left; x--) {
 				if (x >= 0 && x < screen->w && rect.bottom - 1 >= 0 && rect.bottom - 1 < screen->h)
 					*(byte *)screen->getBasePtr(x, rect.bottom - 1) = color;
-				bumpColor();
+				bumpHotspotEdgeColor(color);
 			}
 			// Left edge
 			for (int y = rect.bottom - 1; y >= rect.top; y--) {
 				if (rect.left >= 0 && rect.left < screen->w && y >= 0 && y < screen->h)
 					*(byte *)screen->getBasePtr(rect.left, y) = color;
-				bumpColor();
+				bumpHotspotEdgeColor(color);
 			}
 		}
 	}
diff --git a/engines/eem/ui.cpp b/engines/eem/ui.cpp
index 21a8881c7cc..6e5c9124516 100644
--- a/engines/eem/ui.cpp
+++ b/engines/eem/ui.cpp
@@ -86,6 +86,31 @@ void blitBigMapMarker(Graphics::ManagedSurface &dstSurface, const Picture &marke
 	}
 }
 
+// Setup-screen highlight rects (referenced by both `doSetup` and the
+// helper `EEMEngine::setupDrawScreen`). `_SetupHighlights @ 29be:1320`.
+constexpr Common::Rect kSetupKid1Rect    (Common::Point( 99,  44),  49,  8);
+constexpr Common::Rect kSetupKid2Rect    (Common::Point( 99,  54),  49,  8);
+constexpr Common::Rect kSetupSoundOnRect (Common::Point(106,  86),  19,  8);
+constexpr Common::Rect kSetupSoundOffRect(Common::Point(106,  96),  19,  8);
+
+// `_SwapColors @ 172b:1d2a` ??? replace pixels in r where value==from
+// with to. 0xFE = BG text-key; 0x15 = active palette index, 0x00 =
+// inactive (set by `_SetupSettings @ 1f78:000d`).
+void swapColors(Graphics::ManagedSurface &dst,
+					   const Common::Rect &r, byte from, byte to) {
+	const int x1 = MAX<int>(0, r.left);
+	const int y1 = MAX<int>(0, r.top);
+	const int x2 = MIN<int>(320, r.right);
+	const int y2 = MIN<int>(200, r.bottom);
+	for (int y = y1; y < y2; y++) {
+		byte *row = (byte *)dst.getBasePtr(0, y);
+		for (int x = x1; x < x2; x++) {
+			if (row[x] == from)
+				row[x] = to;
+		}
+	}
+}
+
 void blitAccusePartner(Graphics::ManagedSurface &dstSurface,
 					   DBDArchive &aniArchive, uint8 partner,
 					   uint32 tickMs) {
@@ -1318,114 +1343,15 @@ void EEMEngine::doSetup() {
 	const Common::Rect kHelpBtn      (145, 163, 174, 187); // [9]
 	const Common::Rect kQuitBtn      (212, 153, 266, 184); // [10]
 	const Common::Rect kCreditsBtn   ( 81,  25, 238,  37); // [11]
-	// Highlight / fallback-click rects.
-	const Common::Rect kKid1Rect     ( 99,  44, 148,  52);
-	const Common::Rect kKid2Rect     ( 99,  54, 148,  62);
-	const Common::Rect kSoundOnRect  (106,  86, 125,  94);
-	const Common::Rect kSoundOffRect (106,  96, 125, 104);
-
-	// `_SwapColors @ 172b:1d2a` ??? replace pixels in r where value==from
-	// with to. 0xFE = BG text-key; 0x15 = active palette index, 0x00 =
-	// inactive (set by `_SetupSettings @ 1f78:000d`).
-	auto swapColors = [](Graphics::ManagedSurface &dst,
-						 const Common::Rect &r, byte from, byte to) {
-		const int x1 = MAX<int>(0, r.left);
-		const int y1 = MAX<int>(0, r.top);
-		const int x2 = MIN<int>(320, r.right);
-		const int y2 = MIN<int>(200, r.bottom);
-		for (int y = y1; y < y2; y++) {
-			byte *row = (byte *)dst.getBasePtr(0, y);
-			for (int x = x1; x < x2; x++) {
-				if (row[x] == from)
-					row[x] = to;
-			}
-		}
-	};
+	// Highlight / fallback-click rects (file-scope copies in `kSetup*Rect`).
+	const Common::Rect &kKid1Rect     = kSetupKid1Rect;
+	const Common::Rect &kKid2Rect     = kSetupKid2Rect;
+	const Common::Rect &kSoundOnRect  = kSetupSoundOnRect;
+	const Common::Rect &kSoundOffRect = kSetupSoundOffRect;
 
-	auto draw = [&]() {
-		Graphics::ManagedSurface scratch(320, 200,
-			Graphics::PixelFormat::createFormatCLUT8());
-		scratch.clear();
-		Picture bg;
-		if (_picsArchive.getPicture(0x40, bg))
-			scratch.simpleBlitFrom(bg.surface);
-
-		const byte kKey    = 0xFE;
-		const byte kBright = 0x15;
-		const byte kDim    = 0x00;
-		swapColors(scratch, kKid1Rect, kKey,
-				   _partner == 0 ? kBright : kDim);
-		swapColors(scratch, kKid2Rect, kKey,
-				   _partner == 1 ? kBright : kDim);
-		swapColors(scratch, kSoundOnRect,  kKey,
-				   _voiceOn ? kBright : kDim);
-		swapColors(scratch, kSoundOffRect, kKey,
-				   _voiceOn ? kDim : kBright);
+	setupDrawScreen();
 
-		g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
-								   0, 0, 320, 200);
-		g_system->updateScreen();
-	};
-	draw();
-
-	// Render picId and block until input. transparent=true: overlay via
-	// `_Rect_Move_Mask` (`_InterfaceHelp @ 1560:0205`). false: raw fullscreen
-	// blit via `_vga_fbuffvid` (credits @ 1f78:0281).
-	auto showFullscreenPic = [&](uint16 picId,
-								  bool transparent) -> Common::KeyCode {
-		Picture pic;
-		if (!_picsArchive.getPicture(picId, pic)) {
-			warning("doSetup: PIC %u missing", (uint)picId);
-			return Common::KEYCODE_INVALID;
-		}
-		Graphics::ManagedSurface scratch(320, 200,
-			Graphics::PixelFormat::createFormatCLUT8());
-		if (transparent) {
-			Graphics::Surface *cur = g_system->lockScreen();
-			if (cur) {
-				scratch.simpleBlitFrom(*cur);
-				g_system->unlockScreen();
-			}
-			const byte transp = (byte)(pic.flags >> 8);
-			// Explicit destPos ??? no-destPos overload stretches to dst.
-			scratch.transBlitFrom(pic.surface, Common::Point(0, 0),
-								  (uint32)transp);
-		} else {
-			scratch.clear();
-			scratch.simpleBlitFrom(pic.surface);
-		}
-		g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
-								   0, 0, 320, 200);
-		g_system->updateScreen();
-		while (!shouldQuit()) {
-			Common::Event ev;
-			while (g_system->getEventManager()->pollEvent(ev)) {
-				if (ev.type == Common::EVENT_QUIT ||
-					ev.type == Common::EVENT_RETURN_TO_LAUNCHER)
-					return Common::KEYCODE_ESCAPE;
-				if (ev.type == Common::EVENT_KEYDOWN)
-					return ev.kbd.keycode;
-				if (ev.type == Common::EVENT_LBUTTONDOWN)
-					return Common::KEYCODE_INVALID;
-			}
-			g_system->delayMillis(15);
-		}
-		return Common::KEYCODE_ESCAPE;
-	};
-
-	auto leaveSetup = [&]() {
-		// `_DoSetup` entry: _NextScreen = _LastScreen. Fall back to it
-		// unless a handler already overrode _nextScreen.
-		if (_nextScreen == kScreenSetup) {
-			_nextScreen = (ScreenId)_lastScreen;
-			if (_nextScreen == kScreenSetup ||
-				_nextScreen == kScreenInvalid)
-				_nextScreen = kScreenMap;
-		}
-		saveProfile(_playerName);
-	};
-
-	_nextScreen = kScreenSetup;  // sentinel ??? leaveSetup picks target
+	_nextScreen = kScreenSetup;  // sentinel ??? setupLeave picks target
 	while (!shouldQuit()) {
 		Common::Event ev;
 		bool dirty = false;
@@ -1438,7 +1364,7 @@ void EEMEngine::doSetup() {
 			if (ev.type == Common::EVENT_KEYDOWN) {
 				if (ev.kbd.keycode == Common::KEYCODE_ESCAPE ||
 					ev.kbd.keycode == Common::KEYCODE_RETURN) {
-					leaveSetup();
+					setupLeave();
 					return;
 				}
 			}
@@ -1511,7 +1437,7 @@ void EEMEngine::doSetup() {
 
 			// Done [8] ??? MOV SI,1; JMP exit (NextScreen stays = LastScreen).
 			if (kDoneBtn.contains(mx, my)) {
-				leaveSetup();
+				setupLeave();
 				return;
 			}
 
@@ -1536,9 +1462,9 @@ void EEMEngine::doSetup() {
 				CursorMan.showMouse(false);
 				for (uint i = 0; i < ARRAYSIZE(kHelp1Pics); i++) {
 					// Restore BG between cards (1560:02e5 `_vga_fvidvid(0)`).
-					draw();
+					setupDrawScreen();
 					const Common::KeyCode k =
-						showFullscreenPic(kHelp1Pics[i], /* transparent= */ true);
+						setupShowFullscreenPic(kHelp1Pics[i], /* transparent= */ true);
 					if (k == Common::KEYCODE_ESCAPE)
 						break;
 				}
@@ -1550,7 +1476,7 @@ void EEMEngine::doSetup() {
 			// Credits [11] @ 1f78:025a ??? PIC 0x208 fullscreen.
 			if (kCreditsBtn.contains(mx, my)) {
 				CursorMan.showMouse(false);
-				showFullscreenPic(0x208, /* transparent= */ false);
+				setupShowFullscreenPic(0x208, /* transparent= */ false);
 				CursorMan.showMouse(true);
 				// PIC 0x208 has its own baked palette; restore site 0.
 				setSitePalette(0);
@@ -1586,12 +1512,93 @@ void EEMEngine::doSetup() {
 			}
 		}
 		if (dirty)
-			draw();
+			setupDrawScreen();
 		g_system->updateScreen();
 		g_system->delayMillis(15);
 	}
 }
 
+void EEMEngine::setupDrawScreen() {
+	Graphics::ManagedSurface scratch(320, 200,
+		Graphics::PixelFormat::createFormatCLUT8());
+	scratch.clear();
+	Picture bg;
+	if (_picsArchive.getPicture(0x40, bg))
+		scratch.simpleBlitFrom(bg.surface);
+
+	const byte kKey    = 0xFE;
+	const byte kBright = 0x15;
+	const byte kDim    = 0x00;
+	swapColors(scratch, kSetupKid1Rect, kKey,
+			   _partner == 0 ? kBright : kDim);
+	swapColors(scratch, kSetupKid2Rect, kKey,
+			   _partner == 1 ? kBright : kDim);
+	swapColors(scratch, kSetupSoundOnRect,  kKey,
+			   _voiceOn ? kBright : kDim);
+	swapColors(scratch, kSetupSoundOffRect, kKey,
+			   _voiceOn ? kDim : kBright);
+
+	g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
+							   0, 0, 320, 200);
+	g_system->updateScreen();
+}
+
+// Render picId and block until input. transparent=true: overlay via
+// `_Rect_Move_Mask` (`_InterfaceHelp @ 1560:0205`). false: raw fullscreen
+// blit via `_vga_fbuffvid` (credits @ 1f78:0281).
+Common::KeyCode EEMEngine::setupShowFullscreenPic(uint16 picId, bool transparent) {
+	Picture pic;
+	if (!_picsArchive.getPicture(picId, pic)) {
+		warning("doSetup: PIC %u missing", (uint)picId);
+		return Common::KEYCODE_INVALID;
+	}
+	Graphics::ManagedSurface scratch(320, 200,
+		Graphics::PixelFormat::createFormatCLUT8());
+	if (transparent) {
+		Graphics::Surface *cur = g_system->lockScreen();
+		if (cur) {
+			scratch.simpleBlitFrom(*cur);
+			g_system->unlockScreen();
+		}
+		const byte transp = (byte)(pic.flags >> 8);
+		// Explicit destPos ??? no-destPos overload stretches to dst.
+		scratch.transBlitFrom(pic.surface, Common::Point(0, 0),
+							  (uint32)transp);
+	} else {
+		scratch.clear();
+		scratch.simpleBlitFrom(pic.surface);
+	}
+	g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
+							   0, 0, 320, 200);
+	g_system->updateScreen();
+	while (!shouldQuit()) {
+		Common::Event ev;
+		while (g_system->getEventManager()->pollEvent(ev)) {
+			if (ev.type == Common::EVENT_QUIT ||
+				ev.type == Common::EVENT_RETURN_TO_LAUNCHER)
+				return Common::KEYCODE_ESCAPE;
+			if (ev.type == Common::EVENT_KEYDOWN)
+				return ev.kbd.keycode;
+			if (ev.type == Common::EVENT_LBUTTONDOWN)
+				return Common::KEYCODE_INVALID;
+		}
+		g_system->delayMillis(15);
+	}
+	return Common::KEYCODE_ESCAPE;
+}
+
+void EEMEngine::setupLeave() {
+	// `_DoSetup` entry: _NextScreen = _LastScreen. Fall back to it
+	// unless a handler already overrode _nextScreen.
+	if (_nextScreen == kScreenSetup) {
+		_nextScreen = (ScreenId)_lastScreen;
+		if (_nextScreen == kScreenSetup ||
+			_nextScreen == kScreenInvalid)
+			_nextScreen = kScreenMap;
+	}
+	saveProfile(_playerName);
+}
+
 void EEMEngine::doActionScreen() {
 	// `_ActionScreen @ 1c33:195b` ??? BG PIC 0x104 + PIC 9 @ (10, 0x87),
 	// `_DoChoose` with ActionNames @ 29be:0d6a. 5 picks alternating with
@@ -2232,6 +2239,28 @@ void EEMEngine::doNotebook() {
 	setInteractiveMouseCursor(false);
 }
 
+Common::String EEMEngine::notebookNoteText(uint clueId, const byte *ni,
+										   uint16 niCount, bool floppyNb,
+										   const byte *bufBase,
+										   uint32 mysSz) const {
+	if (!ni || clueId >= niCount)
+		return Common::String();
+	if (floppyNb && bufBase) {
+		const uint16 textOff = READ_LE_UINT16(ni + clueId * 7);
+		if (textOff == 0 || textOff >= mysSz)
+			return Common::String();
+		const char *p = (const char *)(bufBase + textOff);
+		uint32 len = 0;
+		while (textOff + len < mysSz && p[len] != 0)
+			len++;
+		return parseString(Common::String(p, len),
+						   _playerName, _partner);
+	}
+	const uint16 textOff = READ_LE_UINT16(ni + clueId * 4);
+	return parseString(_mystery.textAt(textOff),
+					   _playerName, _partner);
+}
+
 void EEMEngine::drawNotebookFrame(int &page) {
 	// `_DrawNotes @ 161e:01d0` per-page layout + partner sprite at (5,80)
 	// from `_DoNotebook @ 161e:0500`.
@@ -2282,30 +2311,13 @@ void EEMEngine::drawNotebookFrame(int &page) {
 	const bool floppyNb = isFloppy();
 	const byte *bufBase = _mystery.blobAt(0);
 	const uint32 mysSz  = _mystery.dataSize();
-	auto noteText = [&](uint clueId) -> Common::String {
-		if (!ni || clueId >= niCount)
-			return Common::String();
-		if (floppyNb && bufBase) {
-			const uint16 textOff = READ_LE_UINT16(ni + clueId * 7);
-			if (textOff == 0 || textOff >= mysSz)
-				return Common::String();
-			const char *p = (const char *)(bufBase + textOff);
-			uint32 len = 0;
-			while (textOff + len < mysSz && p[len] != 0)
-				len++;
-			return parseString(Common::String(p, len),
-							   _playerName, _partner);
-		}
-		const uint16 textOff = READ_LE_UINT16(ni + clueId * 4);
-		return parseString(_mystery.textAt(textOff),
-						   _playerName, _partner);
-	};
 	{
 		const int lineH = _font.getFontHeight();
 		int y = kRectY;
 		while (clueCursor < (int)found.size()) {
 			const uint clueId = found[clueCursor];
-			Common::String txt = noteText(clueId);
+			Common::String txt = notebookNoteText(clueId, ni, niCount,
+												  floppyNb, bufBase, mysSz);
 			// Measure height by wrapping the text without drawing.
 			Common::Array<Common::String> wrapped;
 			_font.wordWrapText(txt, kRectW, wrapped);
@@ -2336,7 +2348,8 @@ void EEMEngine::drawNotebookFrame(int &page) {
 	int y = kRectY;
 	for (int i = startClue; i < endClue; i++) {
 		const uint clueId = found[i];
-		Common::String txt = noteText(clueId);
+		Common::String txt = notebookNoteText(clueId, ni, niCount,
+											  floppyNb, bufBase, mysSz);
 		if (txt.empty())
 			txt = Common::String::format(
 				isSpanish() ? "nota %u" : "clue %u", clueId);
@@ -3378,6 +3391,132 @@ uint16 EEMEngine::getKDTextBalloon(byte firstChar) const {
 	return kDigitBalloons[firstChar - '0'];
 }
 
+Common::String EEMEngine::accuseNoteText(uint clueId,
+										 const AccuseNotesCtx &ctx) const {
+	if (ctx.floppyNote) {
+		const uint16 textOff = READ_LE_UINT16(ctx.ni + clueId * 7);
+		if (textOff == 0 || textOff >= _mystery.dataSize() ||
+			!ctx.bufBaseNotes)
+			return Common::String();
+		return parseString(
+			(const char *)(ctx.bufBaseNotes + textOff),
+			_playerName, _partner);
+	}
+	const uint16 textOff = READ_LE_UINT16(ctx.ni + clueId * 4);
+	return parseString(_mystery.textAt(textOff),
+					   _playerName, _partner);
+}
+
+void EEMEngine::accuseRebuildPagination(const AccuseNotesCtx &ctx) {
+	*ctx.numPages = 1;
+	ctx.pageBreaks[0] = 0;
+	const int lineH = _font.getFontHeight();
+	int y = ctx.rectY;
+	const Common::Array<uint> &found = *ctx.found;
+	for (uint i = 0; i < found.size(); i++) {
+		const uint clueId = found[i];
+		Common::String txt;
+		if (clueId < ctx.niCount)
+			txt = accuseNoteText(clueId, ctx);
+		Common::Array<Common::String> wrapped;
+		_font.wordWrapText(txt, ctx.rectW, wrapped);
+		const int h = (int)wrapped.size() * lineH;
+		if (y + h + 7 > ctx.rectY + ctx.rectH) {
+			if (*ctx.numPages < ctx.pageBreaksCap) {
+				ctx.pageBreaks[(*ctx.numPages)++] = (int)i;
+				y = ctx.rectY;
+			}
+		}
+		y += h + 7;
+	}
+	if (*ctx.page >= *ctx.numPages)
+		*ctx.page = *ctx.numPages - 1;
+	if (*ctx.page < 0)
+		*ctx.page = 0;
+}
+
+void EEMEngine::accuseDrawScreen(const AccuseNotesCtx &ctx) {
+	Graphics::ManagedSurface scratch(320, 200,
+		Graphics::PixelFormat::createFormatCLUT8());
+	scratch.clear();
+	if (ctx.haveBg)
+		scratch.simpleBlitFrom(ctx.accuseBg->surface);
+
+	// Partner at (5, 0x50). `_DoAccuse @ 1df2:0c2c`: ANI 2/0x10,
+	// script 2, prior 1.
+	const uint partnerAnim = (_partner == 0) ? 2 : 0x10;
+	Animation partnerAni;
+	if (_aniArchive.loadAnimation(partnerAnim, partnerAni) &&
+		!partnerAni.empty()) {
+		const uint32 now = g_system->getMillis();
+		const uint frameIdx = partnerFrameAtTick(0x02,
+												  (uint)partnerAni.size(), now);
+		blitAnimFrameAnchored(scratch.surfacePtr(),
+							  partnerAni[frameIdx], 5, 0x50);
+	}
+
+	// Selected=0x3c, unselected=1 (`_NoteUnselectedColor` @ 1df2:0c25).
+	Common::Array<Common::Rect> &slotRects = *ctx.slotRects;
+	Common::Array<uint> &slotClues = *ctx.slotClues;
+	const Common::Array<uint> &found = *ctx.found;
+	slotRects.clear();
+	slotClues.clear();
+	const int lineH = _font.getFontHeight();
+	const int startIdx = ctx.pageBreaks[*ctx.page];
+	const int endIdx   = (*ctx.page + 1 < *ctx.numPages)
+		? ctx.pageBreaks[*ctx.page + 1]
+		: (int)found.size();
+	int y = ctx.rectY;
+	uint selectedCount = 0;
+	for (uint i = 0; i < found.size(); i++) {
+		if (_mystery._noteSelected[found[i]])
+			selectedCount++;
+	}
+	for (int i = startIdx; i < endIdx; i++) {
+		const uint clueId = found[i];
+		Common::String txt;
+		if (clueId < ctx.niCount)
+			txt = accuseNoteText(clueId, ctx);
+		if (txt.empty())
+			txt = Common::String::format(
+				isSpanish() ? "nota %u" : "clue %u", clueId);
+		Common::Array<Common::String> wrapped;
+		_font.wordWrapText(txt, ctx.rectW, wrapped);
+		const int h = (int)wrapped.size() * lineH;
+		const byte color = _mystery._noteSelected[clueId] ? 0x3c : 0x01;
+		for (uint li = 0; li < wrapped.size(); li++) {
+			_font.drawString(&scratch, wrapped[li], ctx.rectX,
+							 y + (int)li * lineH, ctx.rectW, color);
+		}
+		slotRects.push_back(Common::Rect(ctx.rectX, y,
+										  ctx.rectX + ctx.rectW, y + h));
+		slotClues.push_back(clueId);
+		y += h + 7;
+	}
+
+	// `_UpdateSelectionCount(remaining)` @ (0xd1, 0xb).
+	const uint remaining = (selectedCount < ctx.expected)
+		? ctx.expected - selectedCount
+		: 0;
+	// Spanish floppy uses "nota/notas".
+	const char *clueWord = isSpanish()
+		? (remaining == 1 ? "nota" : "notas")
+		: (remaining == 1 ? "clue" : "clues");
+	const Common::String counter =
+		Common::String::format("%u %s", remaining, clueWord);
+	_font.drawString(&scratch, counter, 209, 11, 100, 0x0F);
+
+	if (*ctx.numPages > 1) {
+		_font.drawString(&scratch,
+			Common::String::format("p%d/%d", *ctx.page + 1, *ctx.numPages),
+			ctx.rectX, 11, 60, 0x0F);
+	}
+
+	g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
+							   0, 0, 320, 200);
+	g_system->updateScreen();
+}
+
 bool EEMEngine::doAccuseNotes() {
 	// `_DoAccuse @ 1df2:0bdd` head. BG PIC 0x1A7. `_AccuseNoteRect @
 	// 29be:1048` = (79, 27, 304, 159). Counter @ (209, 11) shows
@@ -3434,129 +3573,29 @@ bool EEMEngine::doAccuseNotes() {
 	// Notebook always uses +0 (`FUN_15e0_01e8`).
 	const bool floppyNote = isFloppy();
 	const byte *bufBaseNotes = _mystery.blobAt(0);
-	auto noteText = [&](uint clueId) -> Common::String {
-		if (floppyNote) {
-			const uint16 textOff = READ_LE_UINT16(ni + clueId * 7);
-			if (textOff == 0 || textOff >= _mystery.dataSize() ||
-				!bufBaseNotes)
-				return Common::String();
-			return parseString(
-				(const char *)(bufBaseNotes + textOff),
-				_playerName, _partner);
-		}
-		const uint16 textOff = READ_LE_UINT16(ni + clueId * 4);
-		return parseString(_mystery.textAt(textOff),
-						   _playerName, _partner);
-	};
-
-	auto rebuildPagination = [&]() {
-		numPages = 1;
-		pageBreaks[0] = 0;
-		const int lineH = _font.getFontHeight();
-		int y = rectY;
-		for (uint i = 0; i < found.size(); i++) {
-			const uint clueId = found[i];
-			Common::String txt;
-			if (clueId < niCount)
-				txt = noteText(clueId);
-			Common::Array<Common::String> wrapped;
-			_font.wordWrapText(txt, rectW, wrapped);
-			const int h = (int)wrapped.size() * lineH;
-			if (y + h + 7 > rectY + rectH) {
-				if (numPages < (int)ARRAYSIZE(pageBreaks)) {
-					pageBreaks[numPages++] = (int)i;
-					y = rectY;
-				}
-			}
-			y += h + 7;
-		}
-		if (page >= numPages)
-			page = numPages - 1;
-		if (page < 0)
-			page = 0;
-	};
-
-	auto draw = [&]() {
-		Graphics::ManagedSurface scratch(320, 200,
-			Graphics::PixelFormat::createFormatCLUT8());
-		scratch.clear();
-		if (haveBg)
-			scratch.simpleBlitFrom(accuseBg.surface);
-
-		// Partner at (5, 0x50). `_DoAccuse @ 1df2:0c2c`: ANI 2/0x10,
-		// script 2, prior 1.
-		const uint partnerAnim = (_partner == 0) ? 2 : 0x10;
-		Animation partnerAni;
-		if (_aniArchive.loadAnimation(partnerAnim, partnerAni) &&
-			!partnerAni.empty()) {
-			const uint32 now = g_system->getMillis();
-			const uint frameIdx = partnerFrameAtTick(0x02,
-													  (uint)partnerAni.size(), now);
-			blitAnimFrameAnchored(scratch.surfacePtr(),
-								  partnerAni[frameIdx], 5, 0x50);
-		}
-
-		// Selected=0x3c, unselected=1 (`_NoteUnselectedColor` @ 1df2:0c25).
-		slotRects.clear();
-		slotClues.clear();
-		const int lineH = _font.getFontHeight();
-		const int startIdx = pageBreaks[page];
-		const int endIdx   = (page + 1 < numPages)
-			? pageBreaks[page + 1]
-			: (int)found.size();
-		int y = rectY;
-		uint selectedCount = 0;
-		for (uint i = 0; i < found.size(); i++) {
-			if (_mystery._noteSelected[found[i]])
-				selectedCount++;
-		}
-		for (int i = startIdx; i < endIdx; i++) {
-			const uint clueId = found[i];
-			Common::String txt;
-			if (clueId < niCount)
-				txt = noteText(clueId);
-			if (txt.empty())
-				txt = Common::String::format(
-					isSpanish() ? "nota %u" : "clue %u", clueId);
-			Common::Array<Common::String> wrapped;
-			_font.wordWrapText(txt, rectW, wrapped);
-			const int h = (int)wrapped.size() * lineH;
-			const byte color = _mystery._noteSelected[clueId] ? 0x3c : 0x01;
-			for (uint li = 0; li < wrapped.size(); li++) {
-				_font.drawString(&scratch, wrapped[li], rectX,
-								 y + (int)li * lineH, rectW, color);
-			}
-			slotRects.push_back(Common::Rect(rectX, y,
-											  rectX + rectW, y + h));
-			slotClues.push_back(clueId);
-			y += h + 7;
-		}
-
-		// `_UpdateSelectionCount(remaining)` @ (0xd1, 0xb).
-		const uint remaining = (selectedCount < expected)
-			? expected - selectedCount
-			: 0;
-		// Spanish floppy uses "nota/notas".
-		const char *clueWord = isSpanish()
-			? (remaining == 1 ? "nota" : "notas")
-			: (remaining == 1 ? "clue" : "clues");
-		const Common::String counter =
-			Common::String::format("%u %s", remaining, clueWord);
-		_font.drawString(&scratch, counter, 209, 11, 100, 0x0F);
-
-		if (numPages > 1) {
-			_font.drawString(&scratch,
-				Common::String::format("p%d/%d", page + 1, numPages),
-				rectX, 11, 60, 0x0F);
-		}
-
-		g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
-								   0, 0, 320, 200);
-		g_system->updateScreen();
-	};
 
-	rebuildPagination();
-	draw();
+	AccuseNotesCtx ctx;
+	ctx.ni            = ni;
+	ctx.niCount       = niCount;
+	ctx.floppyNote    = floppyNote;
+	ctx.bufBaseNotes  = bufBaseNotes;
+	ctx.found         = &found;
+	ctx.rectX         = rectX;
+	ctx.rectY         = rectY;
+	ctx.rectW         = rectW;
+	ctx.rectH         = rectH;
+	ctx.expected      = expected;
+	ctx.haveBg        = haveBg;
+	ctx.accuseBg      = &accuseBg;
+	ctx.slotRects     = &slotRects;
+	ctx.slotClues     = &slotClues;
+	ctx.pageBreaks    = pageBreaks;
+	ctx.pageBreaksCap = (int)ARRAYSIZE(pageBreaks);
+	ctx.numPages      = &numPages;
+	ctx.page          = &page;
+
+	accuseRebuildPagination(ctx);
+	accuseDrawScreen(ctx);
 	Common::Point mouse = g_system->getEventManager()->getMousePos();
 	setInteractiveMouseCursor(notebookButtonAt(mouse.x, mouse.y) ||
 							  kPdaNotebookRect.contains(mouse.x, mouse.y) ||
@@ -3674,13 +3713,13 @@ bool EEMEngine::doAccuseNotes() {
 			}
 		}
 		if (dirty)
-			draw();
+			accuseDrawScreen(ctx);
 		// 100 ms `_CheckFrameRate` cadence @ 1df2:0bfa.
 		static uint32 sLastTick = 0;
 		const uint32 now = g_system->getMillis();
 		if (now - sLastTick >= 100) {
 			sLastTick = now;
-			draw();
+			accuseDrawScreen(ctx);
 		}
 		g_system->updateScreen();
 		g_system->delayMillis(15);
@@ -4405,6 +4444,148 @@ void EEMEngine::doAccuse() {
 	}
 }
 
+// Render one KDTextIndex string as a centred KD balloon over the current
+// screen, mirroring `_DisplayHint_Floppy @ 1503:00ca` (= `FUN_1d40_11fd`'s
+// body). `kdSlot` is the index into KDTextIndex (0..N) ??? entries are u16
+// absolute text offsets.
+void EEMEngine::floppyKDHint(uint kdSlot, const byte *kdIdx,
+							 const byte *bufBase, uint32 mysSize) {
+	if ((uint)(kdSlot * 2) + 2 > (uint)(mysSize - (kdIdx - bufBase)))
+		return;
+	const uint16 textOff = READ_LE_UINT16(kdIdx + kdSlot * 2);
+	if (textOff == 0 || textOff >= mysSize)
+		return;
+	const char *p = (const char *)(bufBase + textOff);
+	uint32 lineLen = 0;
+	while (textOff + lineLen < mysSize && p[lineLen] != 0)
+		lineLen++;
+	if (lineLen == 0)
+		return;
+	Common::String raw(p, lineLen);
+	// Digit ??? balloon variant (`_GetKDTextBalloon_Floppy @ 1d40:009f`
+	// + table @ 2608:0c14).
+	static const uint8 kDigitToBalloon[10] = {
+		0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1c, 0x1d, 0x1e, 0x0a
+	};
+	uint balloonIdx = 0x17;
+	const char *txt = raw.c_str();
+	if (*txt >= '0' && *txt <= '9') {
+		balloonIdx = kDigitToBalloon[(int)(*txt - '0')];
+		txt++;
+	}
+	Common::String text =
+		parseString(Common::String(txt), _playerName, _partner);
+	balloonIdx = fitBalloonToText((uint16)balloonIdx, text) & 0x7F;
+	Graphics::ManagedSurface ms(320, 200,
+		Graphics::PixelFormat::createFormatCLUT8());
+	Graphics::Surface *cur = g_system->lockScreen();
+	if (cur) {
+		ms.simpleBlitFrom(*cur);
+		g_system->unlockScreen();
+	}
+	Picture balloon;
+	const bool haveBalloon = _balloonArchive.size() > balloonIdx &&
+		_balloonArchive.loadEntry(balloonIdx, balloon);
+	uint16 balloonY = 1;
+	if (haveBalloon) {
+		const uint h = (uint)balloon.surface.h;
+		if (h < 0x4e)
+			balloonY = (uint16)((0x50 - h) >> 1);
+		const byte transp = (byte)(balloon.flags >> 8);
+		ms.transBlitFrom(balloon.surface,
+						 Common::Point(0x21, balloonY), transp);
+	}
+	uint16 bx = 5;
+	uint16 by = 4;
+	uint16 bw = 142;
+	getBalloonInsets(balloonIdx, bx, by, bw);
+	_font.drawWordWrapped(&ms, 0x21 + bx, balloonY + by,
+						  MAX<int>(8, (int)bw), text, 0);
+	g_system->copyRectToScreen(ms.getPixels(), ms.pitch, 0, 0, 320, 200);
+	g_system->updateScreen();
+	// Wait for click.
+	while (!shouldQuit()) {
+		Common::Event ev;
+		bool advance = false;
+		while (g_system->getEventManager()->pollEvent(ev)) {
+			if (ev.type == Common::EVENT_QUIT ||
+				ev.type == Common::EVENT_RETURN_TO_LAUNCHER ||
+				ev.type == Common::EVENT_LBUTTONDOWN ||
+				ev.type == Common::EVENT_KEYDOWN) {
+				advance = true;
+				break;
+			}
+		}
+		if (advance)
+			break;
+		g_system->updateScreen();
+		g_system->delayMillis(10);
+	}
+}
+
+void EEMEngine::accuseDrawGallery(int highlighted,
+								  Common::Array<Common::Rect> &rects,
+								  Common::Array<int> &suspects, uint8 num,
+								  bool haveAccuseBg,
+								  const Picture &accuseBg) {
+	Graphics::ManagedSurface scratch(320, 200,
+		Graphics::PixelFormat::createFormatCLUT8());
+	scratch.clear();
+	if (haveAccuseBg)
+		scratch.simpleBlitFrom(accuseBg.surface);
+
+	const uint partnerAnim = (_partner == 0) ? 2 : 0x10;
+	Animation partnerAni;
+	if (_aniArchive.loadAnimation(partnerAnim, partnerAni) &&
+		!partnerAni.empty()) {
+		const uint32 now = g_system->getMillis();
+		const uint frameIdx = partnerFrameAtTick(0x02,
+												  (uint)partnerAni.size(), now);
+		blitAnimFrameAnchored(scratch.surfacePtr(),
+							  partnerAni[frameIdx], 5, 0x50);
+	}
+
+	rects.resize(num);
+	suspects.resize(num);
+	for (uint i = 0; i < num; i++) {
+		rects[i] = Common::Rect();
+		suspects[i] = -1;
+		const uint8 phys = _mystery._newOrder[i];
+		if (phys >= 5)
+			continue;
+		if (_mystery._inGallery[phys] == 0)
+			continue;
+		const byte *e = _mystery.floppySuspectEntry(i);
+		if (!e)
+			continue;
+		const uint16 picId = READ_LE_UINT16(e + 0);
+		if (picId == 0)
+			continue;
+		Picture portrait;
+		if (!_picsArchive.getPicture(picId, portrait))
+			continue;
+		const GallerySlot &s = kFloppyGallerySlots[phys];
+		// Bottom-align to baseline 0x48 (`154e:00ed`).
+		const int placeX = s.x;
+		const int placeY = s.y + (0x48 - portrait.surface.h);
+		const byte transp = (byte)(portrait.flags >> 8);
+		scratch.transBlitFrom(portrait.surface,
+							  Common::Point(placeX, placeY),
+							  (uint32)transp);
+		rects[i] = Common::Rect(placeX, placeY,
+								 placeX + portrait.surface.w,
+								 placeY + portrait.surface.h);
+		suspects[i] = (int)i;
+		if (highlighted == (int)i) {
+			scratch.frameRect(rects[i], 0xFE);
+		}
+	}
+
+	g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
+							   0, 0, 320, 200);
+	g_system->updateScreen();
+}
+
 void EEMEngine::doAccuseFloppy() {
 	// Floppy accuse:
 	//   `_KDHelp_Floppy / FUN_1d40_11fd @ 1d40:11fd` ??? score gate.
@@ -4419,85 +4600,6 @@ void EEMEngine::doAccuseFloppy() {
 	if (!kdIdx || !bufBase)
 		return;
 
-	// Helper: render one KDTextIndex string as a centred KD balloon
-	// over the current screen, mirroring `_DisplayHint_Floppy @
-	// 1503:00ca` (= `FUN_1d40_11fd`'s body). `kdSlot` is the index
-	// into KDTextIndex (0..N) ??? entries are u16 absolute text
-	// offsets.
-	auto showFloppyKDHint = [&](uint kdSlot) {
-		if ((uint)(kdSlot * 2) + 2 > (uint)(mysSize - (kdIdx - bufBase)))
-			return;
-		const uint16 textOff = READ_LE_UINT16(kdIdx + kdSlot * 2);
-		if (textOff == 0 || textOff >= mysSize)
-			return;
-		const char *p = (const char *)(bufBase + textOff);
-		uint32 lineLen = 0;
-		while (textOff + lineLen < mysSize && p[lineLen] != 0)
-			lineLen++;
-		if (lineLen == 0)
-			return;
-		Common::String raw(p, lineLen);
-		// Digit ??? balloon variant (`_GetKDTextBalloon_Floppy @ 1d40:009f`
-		// + table @ 2608:0c14).
-		static const uint8 kDigitToBalloon[10] = {
-			0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1c, 0x1d, 0x1e, 0x0a
-		};
-		uint balloonIdx = 0x17;
-		const char *txt = raw.c_str();
-		if (*txt >= '0' && *txt <= '9') {
-			balloonIdx = kDigitToBalloon[(int)(*txt - '0')];
-			txt++;
-		}
-		Common::String text =
-			parseString(Common::String(txt), _playerName, _partner);
-		balloonIdx = fitBalloonToText((uint16)balloonIdx, text) & 0x7F;
-		Graphics::ManagedSurface ms(320, 200,
-			Graphics::PixelFormat::createFormatCLUT8());
-		Graphics::Surface *cur = g_system->lockScreen();
-		if (cur) {
-			ms.simpleBlitFrom(*cur);
-			g_system->unlockScreen();
-		}
-		Picture balloon;
-		const bool haveBalloon = _balloonArchive.size() > balloonIdx &&
-			_balloonArchive.loadEntry(balloonIdx, balloon);
-		uint16 balloonY = 1;
-		if (haveBalloon) {
-			const uint h = (uint)balloon.surface.h;
-			if (h < 0x4e)
-				balloonY = (uint16)((0x50 - h) >> 1);
-			const byte transp = (byte)(balloon.flags >> 8);
-			ms.transBlitFrom(balloon.surface,
-							 Common::Point(0x21, balloonY), transp);
-		}
-		uint16 bx = 5;
-		uint16 by = 4;
-		uint16 bw = 142;
-		getBalloonInsets(balloonIdx, bx, by, bw);
-		_font.drawWordWrapped(&ms, 0x21 + bx, balloonY + by,
-							  MAX<int>(8, (int)bw), text, 0);
-		g_system->copyRectToScreen(ms.getPixels(), ms.pitch, 0, 0, 320, 200);
-		g_system->updateScreen();
-		// Wait for click.
-		while (!shouldQuit()) {
-			Common::Event ev;
-			bool advance = false;
-			while (g_system->getEventManager()->pollEvent(ev)) {
-				if (ev.type == Common::EVENT_QUIT ||
-					ev.type == Common::EVENT_RETURN_TO_LAUNCHER ||
-					ev.type == Common::EVENT_LBUTTONDOWN ||
-					ev.type == Common::EVENT_KEYDOWN) {
-					advance = true;
-					break;
-				}
-			}
-			if (advance)
-				break;
-			g_system->updateScreen();
-			g_system->delayMillis(10);
-		}
-	};
-
 	// `FUN_1d40_11fd` ??? score gate, ready when score >= 100.
 	const int score = _mystery.selectedPoints();
 	uint kdSlot;
@@ -4512,7 +4614,7 @@ void EEMEngine::doAccuseFloppy() {
 		kdSlot = 2;        // "ready to solve"
 		readyToSolve = true;
 	}
-	showFloppyKDHint(kdSlot);
+	floppyKDHint(kdSlot, kdIdx, bufBase, mysSize);
 	if (!readyToSolve) {
 		_nextScreen = _lastScreen != kScreenInvalid
 			? (ScreenId)_lastScreen : kScreenSite;
@@ -4545,14 +4647,14 @@ void EEMEngine::doAccuseFloppy() {
 		}
 	}
 	if (userSelectedScore < 100) {
-		showFloppyKDHint(3);
+		floppyKDHint(3, kdIdx, bufBase, mysSize);
 		_nextScreen = _lastScreen != kScreenInvalid
 			? (ScreenId)_lastScreen : kScreenSite;
 		return;
 	}
 
 	// `FUN_1d40_0c79` ??? gallery picker. KDTextIndex slot 4 prompt.
-	showFloppyKDHint(4);
+	floppyKDHint(4, kdIdx, bufBase, mysSize);
 
 	// Floppy gallery: byte0 = numSuspects, then variable-stride entries.
 	const uint8 num = _mystery.numSuspects();
@@ -4562,82 +4664,15 @@ void EEMEngine::doAccuseFloppy() {
 		return;
 	}
 
-	// `_DrawGallery_Floppy @ 154e:0050` slots @ 2608:016c.
-	struct GallerySlot { int x, y; };
-	static const GallerySlot kFloppySlots[5] = {
-		{ 0x53, 0x0e }, { 0x9b, 0x0e }, { 0xe3, 0x0e },
-		{ 0x77, 0x5a }, { 0xbf, 0x5a },
-	};
-
 	Picture accuseBg;
 	const bool haveAccuseBg = _picsArchive.getPicture(0x3f, accuseBg);
 
-	auto drawGallery = [&](int highlighted,
-						   Common::Array<Common::Rect> &rects,
-						   Common::Array<int> &suspects) {
-		Graphics::ManagedSurface scratch(320, 200,
-			Graphics::PixelFormat::createFormatCLUT8());
-		scratch.clear();
-		if (haveAccuseBg)
-			scratch.simpleBlitFrom(accuseBg.surface);
-
-		const uint partnerAnim = (_partner == 0) ? 2 : 0x10;
-		Animation partnerAni;
-		if (_aniArchive.loadAnimation(partnerAnim, partnerAni) &&
-			!partnerAni.empty()) {
-			const uint32 now = g_system->getMillis();
-			const uint frameIdx = partnerFrameAtTick(0x02,
-													  (uint)partnerAni.size(), now);
-			blitAnimFrameAnchored(scratch.surfacePtr(),
-								  partnerAni[frameIdx], 5, 0x50);
-		}
-
-		rects.resize(num);
-		suspects.resize(num);
-		for (uint i = 0; i < num; i++) {
-			rects[i] = Common::Rect();
-			suspects[i] = -1;
-			const uint8 phys = _mystery._newOrder[i];
-			if (phys >= 5)
-				continue;
-			if (_mystery._inGallery[phys] == 0)
-				continue;
-			const byte *e = _mystery.floppySuspectEntry(i);
-			if (!e)
-				continue;
-			const uint16 picId = READ_LE_UINT16(e + 0);
-			if (picId == 0)
-				continue;
-			Picture portrait;
-			if (!_picsArchive.getPicture(picId, portrait))
-				continue;
-			const GallerySlot &s = kFloppySlots[phys];
-			// Bottom-align to baseline 0x48 (`154e:00ed`).
-			const int placeX = s.x;
-			const int placeY = s.y + (0x48 - portrait.surface.h);
-			const byte transp = (byte)(portrait.flags >> 8);
-			scratch.transBlitFrom(portrait.surface,
-								  Common::Point(placeX, placeY),
-								  (uint32)transp);
-			rects[i] = Common::Rect(placeX, placeY,
-									 placeX + portrait.surface.w,
-									 placeY + portrait.surface.h);
-			suspects[i] = (int)i;
-			if (highlighted == (int)i) {
-				scratch.frameRect(rects[i], 0xFE);
-			}
-		}
-
-		g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
-								   0, 0, 320, 200);
-		g_system->updateScreen();
-	};
-
 	Common::Array<Common::Rect> slotRects;
 	Common::Array<int> slotSuspect;
 	int highlighted = 0;
 	int picked = -1;
-	drawGallery(highlighted, slotRects, slotSuspect);
+	accuseDrawGallery(highlighted, slotRects, slotSuspect, num,
+					  haveAccuseBg, accuseBg);
 
 	uint32 lastTick = g_system->getMillis();
 	while (picked < 0 && !shouldQuit()) {
@@ -4649,17 +4684,20 @@ void EEMEngine::doAccuseFloppy() {
 			if (ev.type == Common::EVENT_KEYDOWN) {
 				if (ev.kbd.keycode == Common::KEYCODE_ESCAPE) {
 					openMainMenuDialog();
-					drawGallery(highlighted, slotRects, slotSuspect);
+					accuseDrawGallery(highlighted, slotRects, slotSuspect,
+									  num, haveAccuseBg, accuseBg);
 					continue;
 				}
 				if (ev.kbd.keycode == Common::KEYCODE_TAB ||
 					ev.kbd.keycode == Common::KEYCODE_RIGHT) {
 					highlighted = (highlighted + 1) % MAX<int>(1, (int)num);
-					drawGallery(highlighted, slotRects, slotSuspect);
+					accuseDrawGallery(highlighted, slotRects, slotSuspect,
+									  num, haveAccuseBg, accuseBg);
 				} else if (ev.kbd.keycode == Common::KEYCODE_LEFT) {
 					highlighted = (highlighted + (int)num - 1) %
 								  MAX<int>(1, (int)num);
-					drawGallery(highlighted, slotRects, slotSuspect);
+					accuseDrawGallery(highlighted, slotRects, slotSuspect,
+									  num, haveAccuseBg, accuseBg);
 				} else if ((ev.kbd.keycode == Common::KEYCODE_RETURN ||
 							ev.kbd.keycode == Common::KEYCODE_KP_ENTER) &&
 						   highlighted < (int)slotRects.size() &&
@@ -4679,7 +4717,8 @@ void EEMEngine::doAccuseFloppy() {
 		}
 		const uint32 now = g_system->getMillis();
 		if (now - lastTick >= 100) {
-			drawGallery(highlighted, slotRects, slotSuspect);
+			accuseDrawGallery(highlighted, slotRects, slotSuspect, num,
+							  haveAccuseBg, accuseBg);
 			lastTick = now;
 		}
 		g_system->updateScreen();
@@ -4956,7 +4995,7 @@ void EEMEngine::doAccuseFloppy() {
 	}
 
 	// `_DisplayAlibi_Floppy @ 1d40:01ee` ??? KDTextIndex[+10] = slot 5.
-	showFloppyKDHint(5);
+	floppyKDHint(5, kdIdx, bufBase, mysSize);
 	if (_music && _voiceOn)
 		_music->stop();
 


Commit: b8ebea90adecad7133d828add76452026b3249ea
    https://github.com/scummvm/scummvm/commit/b8ebea90adecad7133d828add76452026b3249ea
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:21:11+02:00

Commit Message:
EEM: correct implementation of OpenColorCycle

Changed paths:
    engines/eem/eem.cpp


diff --git a/engines/eem/eem.cpp b/engines/eem/eem.cpp
index 0afc5f5dc9f..89c89a88b48 100644
--- a/engines/eem/eem.cpp
+++ b/engines/eem/eem.cpp
@@ -804,76 +804,128 @@ void EEMEngine::waitForInput(uint32 maxMs) {
 	}
 }
 
+// _OpenColorCycle @ 2520:04f7. Rotate `fpal[start..end]` by one slot:
+//   saved = fpal[end]
+//   for u = end..start+1: fpal[u] = fpal[u-1]
+//   fpal[start] = saved
+// If `show`, upload `end - start` entries (fpal[start..end-1]) ??? note that
+// fpal[end] is rotated in memory but intentionally not uploaded each tick.
+static void openColorCycle(byte *fpal, uint8 start, uint8 end, bool show) {
+	if (end <= start)
+		return;
+	const byte savedR = fpal[end * 3 + 0];
+	const byte savedG = fpal[end * 3 + 1];
+	const byte savedB = fpal[end * 3 + 2];
+	for (uint u = end; u > start; u--) {
+		fpal[u * 3 + 0] = fpal[(u - 1) * 3 + 0];
+		fpal[u * 3 + 1] = fpal[(u - 1) * 3 + 1];
+		fpal[u * 3 + 2] = fpal[(u - 1) * 3 + 2];
+	}
+	fpal[start * 3 + 0] = savedR;
+	fpal[start * 3 + 1] = savedG;
+	fpal[start * 3 + 2] = savedB;
+	if (show) {
+		g_system->getPaletteManager()->setPalette(fpal + start * 3, start,
+												   end - start);
+	}
+}
+
 void EEMEngine::showEAKidsLogo() {
 	// _ShowEAKids @ 2520:05f0:
-	//   1. GetPicture(0x54) + MemoryCopy to VGA + GetPalette(0x25).
-	//   2. FRAME_RATE = 25; for j in 0..1, for u in 0..0x36 (= 55):
-	//        OpenColorCycle(0x01, 0x6e)   // bg / outer ring shimmer
-	//        OpenColorCycle(0x81, 0xee)   // inner gradient shimmer
-	//        every 8 ticks: OpenColorCycle(0x70, 0x80)  // mid band
-	//   3. Tail: 5 more cycles of 0x70..0x80.
-	//   4. Wait 0x23 (=35) more frames.
-	//   5. _OpenFadeOut.
-	// The cycling is what gives the EA Kids logo its shifting glow ???
-	// it's NOT a static logo.
+	//   _GetPicture(0x54) + memcpy to 0xa000 (VGA).
+	//   _GetPalette(0x25) loads pal 0x25 into _fpal (NOT uploaded to DAC).
+	//   FRAME_RATE = 0x19 (25 fps); _InitFrameReg.
+	//   for j in 0..1: show = j;
+	//     for u in 0..0x37 (= 55):
+	//       if (show) wait for next 25-fps tick (abort on key/click).
+	//       _OpenColorCycle(0x01, 0x6e, show)   // bg / outer ring shimmer
+	//       _OpenColorCycle(0x81, 0xee, show)   // inner gradient shimmer
+	//       if (--delay == 0) {
+	//         delay = 8;
+	//         _OpenColorCycle(0x70, 0x80, show) // mid band
+	//       }
+	//   if (!abort) {
+	//     for i in 0..5: _OpenColorCycle(0x70, 0x80, 1);
+	//     for i in 0..0x23: wait one frame;
+	//   }
+	//   _OpenFadeOut().
+	//
+	// Pass 1 (j=0, show=0) pre-rolls _fpal 55 frames in memory only ??? no
+	// DAC upload, no frame sync. Pass 2 (j=1, show=1) uploads each shift
+	// at 25 fps. Without the pre-roll, the logo first appears at the
+	// unrotated palette-0x25 phase instead of the intended "55-shifts-in"
+	// phase.
 	Picture pic;
 	if (!_picsArchive.getPicture(kPicEAKidsLogo, pic)) {
 		warning("EA Kids logo (%u) load failed", kPicEAKidsLogo);
 		return;
 	}
 	blitAt(pic, 0, 0);
-	setSitePalette(kPalEAKids);
-	g_system->updateScreen();
 
-	// 25 fps -> 40 ms / tick.
-	const uint kFrameMs = 40;
-	int delayCount = 8;
+	// _GetPalette(0x25) ??? load into our shadow buffer; do not upload.
+	// The logo bitmap is on screen but invisible until the first
+	// _OpenColorCycle(..., show=1) upload in pass 2 lights it up.
+	byte fpal[kPalSize];
+	if (!getSitePalette(kPalEAKids, fpal)) {
+		warning("EA Kids palette (%u) load failed", kPalEAKids);
+		return;
+	}
+
+	const uint kFrameMs = 40;  // FRAME_RATE = 0x19 (25 fps).
 	bool aborted = false;
-	for (uint outer = 0; outer < 2 && !aborted && !shouldQuit(); outer++) {
-		for (uint i = 0; i < 0x37 && !aborted && !shouldQuit(); i++) {
-			cyclePaletteRangeReverse(0x01, 0x6e);
-			cyclePaletteRangeReverse(0x81, 0xee);
-			delayCount--;
-			if (delayCount == 0) {
-				delayCount = 8;
-				cyclePaletteRangeReverse(0x70, 0x80);
-			}
-			g_system->updateScreen();
 
-			const uint32 frameEnd = g_system->getMillis() + kFrameMs;
-			while (g_system->getMillis() < frameEnd && !aborted) {
-				Common::Event ev;
-				while (g_system->getEventManager()->pollEvent(ev)) {
-					if (ev.type == Common::EVENT_QUIT ||
-						ev.type == Common::EVENT_RETURN_TO_LAUNCHER) {
-						_skipIntro = true;
-						return;
-					}
-					if (ev.type == Common::EVENT_KEYDOWN ||
-						ev.type == Common::EVENT_LBUTTONDOWN) {
-						if (ev.type == Common::EVENT_KEYDOWN &&
-							ev.kbd.keycode == Common::KEYCODE_ESCAPE) {
+	for (uint j = 0; j < 2 && !aborted && !shouldQuit(); j++) {
+		const bool show = (j != 0);
+		int delayCount = 8;
+
+		for (uint u = 0; u < 0x37 && !aborted && !shouldQuit(); u++) {
+			if (show) {
+				const uint32 frameEnd = g_system->getMillis() + kFrameMs;
+				while (g_system->getMillis() < frameEnd && !aborted) {
+					Common::Event ev;
+					while (g_system->getEventManager()->pollEvent(ev)) {
+						if (ev.type == Common::EVENT_QUIT ||
+							ev.type == Common::EVENT_RETURN_TO_LAUNCHER) {
 							_skipIntro = true;
+							return;
+						}
+						if (ev.type == Common::EVENT_KEYDOWN ||
+							ev.type == Common::EVENT_LBUTTONDOWN) {
+							if (ev.type == Common::EVENT_KEYDOWN &&
+								ev.kbd.keycode == Common::KEYCODE_ESCAPE) {
+								_skipIntro = true;
+							}
+							aborted = true;
+							break;
 						}
-						aborted = true;
-						break;
 					}
+					g_system->delayMillis(5);
 				}
-				g_system->delayMillis(5);
 			}
+
+			openColorCycle(fpal, 0x01, 0x6e, show);
+			openColorCycle(fpal, 0x81, 0xee, show);
+			delayCount--;
+			if (delayCount == 0) {
+				delayCount = 8;
+				openColorCycle(fpal, 0x70, 0x80, show);
+			}
+			if (show)
+				g_system->updateScreen();
 		}
 	}
 
-	if (aborted)
+	if (aborted) {
+		fadeCurrentPaletteToBlack();
 		return;
+	}
 
 	for (uint i = 0; i < 5 && !shouldQuit(); i++)
-		cyclePaletteRangeReverse(0x70, 0x80);
+		openColorCycle(fpal, 0x70, 0x80, true);
 	g_system->updateScreen();
 	waitForInput(0x23 * kFrameMs);
 
-	// `_OpenFadeOut @ 2520:0093` ??? 16 linear steps from current palette
-	// to black.
+	// _OpenFadeOut @ 2520:0093 ??? 16 linear steps from current palette to black.
 	fadeCurrentPaletteToBlack();
 }
 


Commit: ad29adf75013f363dc72fb775af4b03c68b3c3de
    https://github.com/scummvm/scummvm/commit/ad29adf75013f363dc72fb775af4b03c68b3c3de
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:21:11+02:00

Commit Message:
EEM: fixed bug when picking up the phone earlier than expected

Changed paths:
    engines/eem/clues.cpp


diff --git a/engines/eem/clues.cpp b/engines/eem/clues.cpp
index 39aca26fb17..c435ab85c66 100644
--- a/engines/eem/clues.cpp
+++ b/engines/eem/clues.cpp
@@ -407,6 +407,28 @@ void EEMEngine::doInitClues() {
 		}
 	}
 
+	// Setup voice ??? _DoInitClues @ 1a35:061d runs this BEFORE _PlayInSequence
+	// (i.e. the phone rings, the player hears it ring out, and only then
+	// does Jake/Jenny reach for the receiver):
+	//   CD:     caseType 2 -> PHONE.VOC then _WaitForVoiceDone
+	//   Floppy (_DoInitClues_Floppy @ 19bb:042f):
+	//     caseType 2 -> slot 0xc (PHONESL.VOC)
+	//     caseType 3 -> slot 3   (NEWSCAN.VOC, news-anchor variant)
+	// While the voice plays the screen continues showing the game anim's
+	// last frame (saved into briefingBase above).
+	if (_audio) {
+		if (caseType == 2) {
+			if (floppy)
+				_audio->playFloppyVoiceSlot(0x0c, _partner);
+			else
+				_audio->playVoc(Common::Path("PHONE.VOC"));
+			_audio->waitForVoiceDone();
+		} else if (caseType == 3 && floppy) {
+			_audio->playFloppyVoiceSlot(0x03, _partner);
+			_audio->waitForVoiceDone();
+		}
+	}
+
 	// _PlayInSequence @ 172b:2d03. Anim selection per partner + caseType:
 	//   Jake:  caseType 1 -> 0x38 @ (0xcd, 0x6d)
 	//          caseType 2 -> 0x37 @ (0xcd, 0x6c)
@@ -487,24 +509,6 @@ void EEMEngine::doInitClues() {
 		}
 	}
 
-	// Setup voice (caseType 2/3 only):
-	//   CD:     caseType 2 -> PHONE.VOC
-	//   Floppy (_DoInitClues_Floppy @ 19bb:042f):
-	//     caseType 2 -> slot 0xc (PHONESL.VOC)
-	//     caseType 3 -> slot 3   (NEWSCAN.VOC, news-anchor variant)
-	if (_audio) {
-		if (caseType == 2) {
-			if (floppy)
-				_audio->playFloppyVoiceSlot(0x0c, _partner);
-			else
-				_audio->playVoc(Common::Path("PHONE.VOC"));
-			_audio->waitForVoiceDone();
-		} else if (caseType == 3 && floppy) {
-			_audio->playFloppyVoiceSlot(0x03, _partner);
-			_audio->waitForVoiceDone();
-		}
-	}
-
 	// Briefing dialogue. CD: clue block @ ib+4 (after caseType,startSite).
 	// Floppy: dialog records dispatched via FUN_22dc_05c8 @ 22dc:05c8
 	// (record size = 11 + textCount bytes).


Commit: b69b12c24432f19666c632e1c0312a5cc0e768a2
    https://github.com/scummvm/scummvm/commit/b69b12c24432f19666c632e1c0312a5cc0e768a2
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:21:11+02:00

Commit Message:
EEM: fixed bug in Nancy animation

Changed paths:
    engines/eem/clues.cpp


diff --git a/engines/eem/clues.cpp b/engines/eem/clues.cpp
index c435ab85c66..b68b3076ff4 100644
--- a/engines/eem/clues.cpp
+++ b/engines/eem/clues.cpp
@@ -321,17 +321,27 @@ void EEMEngine::doInitClues() {
 						  && _aniArchive.loadAnimation(0x19, nancy)
 						  && !nancy.empty();
 
-	// Cycle game animation once (10 fps = _CheckFrameRate cadence).
-	// _DoInitClues @ 1a35:0507/0541 hard-codes the SCRIPT index to Jake's
-	// IDs (0x17/0x18/0x19) regardless of partner, so look up scripts by
-	// those IDs unconditionally.
+	// Cycle game animation once at _CheckFrameRate cadence (~7 fps, 140 ms
+	// per _UpdateAnimations call ??? `LastFrame + 0xe` cs in _InitFrameCounter
+	// @ 1a35:01ae). Original loop @ 1a35:0507:
+	//   uVar9 = 1;
+	//   while (uVar9 != gameNum) {
+	//     if (_CheckFrameRate || skipped) { _UpdateAnimations(); uVar9++; }
+	//   }
+	// So `gameNum - 1` _UpdateAnimations calls; each call advances every
+	// registered slot by one script tick. _DoInitClues @ 1a35:0507/0541
+	// hard-codes the SCRIPT index to Jake's IDs (0x17/0x18/0x19) regardless
+	// of partner, so we look up scripts by those IDs unconditionally.
 	if (haveGame || haveBook || haveNancy) {
-		const uint frameCount = haveGame ? game.size() : 8;
+		const uint kCheckFrameRateMs = 140;
+		const uint baseFrames = haveGame ? game.size() : 8;
+		// `gameNum - 1` ticks: scriptIdx 0..gameNum-2.
+		const uint frameCount = (baseFrames > 0) ? baseFrames - 1 : 0;
 		bool skip = false;
 		for (uint frame = 0; frame < frameCount && !shouldQuit() && !skip; frame++) {
 			if (haveBriefingBg)
 				blitAt(bg, 0, 0);
-			const uint32 t = frame * 100;
+			const uint32 t = frame * kCheckFrameRateMs;
 			Graphics::Surface *scr = g_system->lockScreen();
 			if (!scr) {
 				skip = true;
@@ -353,7 +363,7 @@ void EEMEngine::doInitClues() {
 			g_system->updateScreen();
 
 			// ESC interrupts voice/spool so audio doesn't bleed into the MAP.
-			const uint32 wakeup = g_system->getMillis() + 100;
+			const uint32 wakeup = g_system->getMillis() + kCheckFrameRateMs;
 			while (g_system->getMillis() < wakeup && !shouldQuit() && !skip) {
 				Common::Event ev;
 				while (g_system->getEventManager()->pollEvent(ev)) {


Commit: 24a3e5a40cd63cb210c684a5454c93567da757e1
    https://github.com/scummvm/scummvm/commit/24a3e5a40cd63cb210c684a5454c93567da757e1
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:21:12+02:00

Commit Message:
EEM: fixed global-constructors from kHappyZones

Changed paths:
    engines/eem/clues.cpp


diff --git a/engines/eem/clues.cpp b/engines/eem/clues.cpp
index b68b3076ff4..0f8a56ff977 100644
--- a/engines/eem/clues.cpp
+++ b/engines/eem/clues.cpp
@@ -45,12 +45,13 @@ const uint kAniBoy  = 8;                // Jake
 const uint kAniGirl = 9;                // Jenny
 
 // _DoHappiness @ 172b:27b5 ??? cursor X picks one of 4 rects @ 29be:030f.
-// Past rect 3 = level 4.
-const Common::Rect kHappyZones[4] = {
-	Common::Rect(  0, 0,  70, 200), // far left ??? girl very happy, boy neutral
-	Common::Rect( 70, 0, 126, 200), // girl's column
-	Common::Rect(126, 0, 182, 200), // middle
-	Common::Rect(182, 0, 235, 200), // boy's column
+// Past rect 3 = level 4. Constexpr (Point, w, h) form to avoid a global
+// constructor (-Wglobal-constructors).
+constexpr Common::Rect kHappyZones[4] = {
+	Common::Rect(Common::Point(  0, 0),  70, 200), // far left ??? girl very happy, boy neutral
+	Common::Rect(Common::Point( 70, 0),  56, 200), // girl's column
+	Common::Rect(Common::Point(126, 0),  56, 200), // middle
+	Common::Rect(Common::Point(182, 0),  53, 200), // boy's column
 };
 
 // _NewAnimation positions @ 1a35:07b9 / 07d5.


Commit: 12776d655aa8e45ed88d02e26c45d7f73d60a5eb
    https://github.com/scummvm/scummvm/commit/12776d655aa8e45ed88d02e26c45d7f73d60a5eb
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:21:12+02:00

Commit Message:
EEM: removed the 'create new' workaround

Changed paths:
    engines/eem/ui.cpp


diff --git a/engines/eem/ui.cpp b/engines/eem/ui.cpp
index 6e5c9124516..048e8df5efb 100644
--- a/engines/eem/ui.cpp
+++ b/engines/eem/ui.cpp
@@ -403,7 +403,7 @@ bool animateProfilePickerReveal(EEMEngine *vm, const Picture *bg,
 
 struct ProfilePickerEntry {
 	Common::String label;
-	int slot;       ///< -1 means "create new"
+	int slot;
 };
 
 struct ProfilePickerView {
@@ -706,8 +706,11 @@ void EEMEngine::doProfilePicker() {
 		return;
 	}
 
-	// Existing profiles + "[New Player]". DOS bottom click area @ 29be:0d08
-	// returns 0xfffe ??? `_NewPlayer`; rect remains active too.
+	// Existing profiles only ??? the original `screen8_handler @ 1c33:1012`
+	// passes the bare *.PLR list to `_DoChoose` with NO synthesized "new
+	// player" entry. _DoChoose returns 0xfffe / 0xffff for the bottom click
+	// rect @ 29be:0d08 / ESC, which routes to `_NewPlayer` (handled below
+	// via kChooserNewPlayerRect / KEYCODE_ESCAPE).
 	Common::Array<ProfilePickerEntry> entries;
 	for (const SaveStateDescriptor &s : saves) {
 		ProfilePickerEntry e;
@@ -715,10 +718,6 @@ void EEMEngine::doProfilePicker() {
 		e.slot  = s.getSaveSlot();
 		entries.push_back(e);
 	}
-	ProfilePickerEntry newEntry;
-	newEntry.label = "[New Player]";
-	newEntry.slot  = -1;
-	entries.push_back(newEntry);
 
 	int sel = 0;
 	int start = 0;
@@ -871,16 +870,12 @@ void EEMEngine::doProfilePicker() {
 		return;
 	}
 
+	// `_LoadPlayerRecord @ 1c33:1281`.
 	const ProfilePickerEntry &e = entries[sel];
-	if (e.slot < 0) {
+	if (!loadProfile(e.label)) {
+		warning("doProfilePicker: failed to load profile '%s' at slot %d",
+				e.label.c_str(), e.slot);
 		doNewPlayer();
-	} else {
-		// `_LoadPlayerRecord @ 1c33:1281`.
-		if (!loadProfile(e.label)) {
-			warning("doProfilePicker: failed to load profile '%s' at slot %d",
-					e.label.c_str(), e.slot);
-			doNewPlayer();
-		}
 	}
 }
 


Commit: 613dac165b5ddaf8a5ddc528c0caf6abcea644c8
    https://github.com/scummvm/scummvm/commit/613dac165b5ddaf8a5ddc528c0caf6abcea644c8
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:21:12+02:00

Commit Message:
EEM: improved animation in the PDA

Changed paths:
    engines/eem/ui.cpp


diff --git a/engines/eem/ui.cpp b/engines/eem/ui.cpp
index 048e8df5efb..503874824e6 100644
--- a/engines/eem/ui.cpp
+++ b/engines/eem/ui.cpp
@@ -2113,6 +2113,7 @@ void EEMEngine::doNotebook() {
 											   mouse.x, mouse.y));
 
 	uint32 lastDraw = g_system->getMillis();
+	uint32 gizmoLastTick = lastDraw;
 
 	while (!shouldQuit()) {
 		Common::Event ev;
@@ -2228,6 +2229,12 @@ void EEMEngine::doNotebook() {
 									  rectListContains(_notebookSlotRects,
 													   mouse.x, mouse.y));
 		}
+		// _GizmoColorCycle @ 1c33:0002 ??? `_DoNotebook` rotates 0x6f..0x73 each
+		// _CheckFrameRate tick (the PDA gizmo / LED indicator shimmer).
+		if (now - gizmoLastTick >= kChooserCycleMillis) {
+			gizmoLastTick = now;
+			cycleChooserPalette();
+		}
 		g_system->updateScreen();
 		g_system->delayMillis(15);
 	}
@@ -2417,6 +2424,7 @@ void EEMEngine::doGallery() {
 							  gallerySlotAt(slotRects, slotSuspect,
 											mouse.x, mouse.y));
 	uint32 lastDraw = g_system->getMillis();
+	uint32 gizmoLastTick = lastDraw;
 
 	while (!shouldQuit()) {
 		Common::Event ev;
@@ -2524,6 +2532,11 @@ void EEMEngine::doGallery() {
 									  gallerySlotAt(slotRects, slotSuspect,
 													mouse.x, mouse.y));
 		}
+		// _GizmoColorCycle @ 1c33:0002 ??? `_DoGallery` rotates 0x6f..0x73 each tick.
+		if (now - gizmoLastTick >= kChooserCycleMillis) {
+			gizmoLastTick = now;
+			cycleChooserPalette();
+		}
 		g_system->updateScreen();
 		g_system->delayMillis(15);
 	}
@@ -2699,6 +2712,7 @@ bool EEMEngine::moreInfo(const byte *gd, uint suspectIdx,
 		bool advance = false;
 		bool prev = false;
 		bool redraw = false;
+		uint32 gizmoLastTick = g_system->getMillis();
 		while (!back && !advance && !prev && !redraw && !shouldQuit()) {
 			Common::Event e2;
 			while (g_system->getEventManager()->pollEvent(e2)) {
@@ -2786,6 +2800,13 @@ bool EEMEngine::moreInfo(const byte *gd, uint suspectIdx,
 					break;
 				}
 			}
+			// _GizmoColorCycle @ 1c33:0002 ??? `MoreInfo` (158f:0480) rotates
+			// 0x6f..0x73 each tick.
+			const uint32 now = g_system->getMillis();
+			if (now - gizmoLastTick >= kChooserCycleMillis) {
+				gizmoLastTick = now;
+				cycleChooserPalette();
+			}
 			g_system->updateScreen();
 			g_system->delayMillis(20);
 		}


Commit: 23baebb9a800604e43f16a02f7fe5bf5935aa3c3
    https://github.com/scummvm/scummvm/commit/23baebb9a800604e43f16a02f7fe5bf5935aa3c3
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:21:13+02:00

Commit Message:
EEM: removed incorrect attribution in the headers

Changed paths:
    engines/eem/eem.h


diff --git a/engines/eem/eem.h b/engines/eem/eem.h
index 6d83cdc4da9..8ba5a135353 100644
--- a/engines/eem/eem.h
+++ b/engines/eem/eem.h
@@ -17,7 +17,6 @@
  * You should have received a copy of the GNU General Public License
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
- * Based on the original engine code by EA Kids / Storm Software (1994).
  *
  */
 


Commit: d901f38d663f85784e2f5664fe659272bbe547e2
    https://github.com/scummvm/scummvm/commit/d901f38d663f85784e2f5664fe659272bbe547e2
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:21:13+02:00

Commit Message:
EEM: reduced code duplication using transBlitFrom

Changed paths:
    engines/eem/clues.cpp
    engines/eem/graphics.cpp
    engines/eem/site.cpp
    engines/eem/ui.cpp


diff --git a/engines/eem/clues.cpp b/engines/eem/clues.cpp
index 0f8a56ff977..fcb47cdb06d 100644
--- a/engines/eem/clues.cpp
+++ b/engines/eem/clues.cpp
@@ -114,23 +114,17 @@ uint happinessLevel(int x) {
 // _Rect_Move_Mask's mask byte (the on-disk u16 at file offset 0 maps to
 // Picture::flags).
 void blitMaskedToScreen(const Picture &p, int x, int y) {
-	const byte transp = (byte)(p.flags >> 8);
 	Graphics::Surface *screen = g_system->lockScreen();
 	if (!screen)
 		return;
-	for (int row = 0; row < p.surface.h; row++) {
-		const int dstY = y + row;
-		if (dstY < 0 || dstY >= screen->h)
-			continue;
-		const byte *src = (const byte *)p.surface.getBasePtr(0, row);
-		byte *dst = (byte *)screen->getBasePtr(0, dstY);
-		for (int col = 0; col < p.surface.w; col++) {
-			const int dstX = x + col;
-			if (dstX < 0 || dstX >= screen->w)
-				continue;
-			if (src[col] != transp)
-				dst[dstX] = src[col];
-		}
+	const Common::Rect dst = Common::Rect(x, y, x + p.surface.w,
+										  y + p.surface.h)
+		.findIntersectingRect(Common::Rect(screen->w, screen->h));
+	if (!dst.isEmpty()) {
+		const Common::Rect src(dst.left - x, dst.top - y,
+							   dst.right - x, dst.bottom - y);
+		screen->copyRectToSurfaceWithKey(p.surface, dst.left, dst.top,
+										 src, (uint32)(byte)(p.flags >> 8));
 	}
 	g_system->unlockScreen();
 }
@@ -1093,19 +1087,9 @@ void EEMEngine::displayFloppyDialogRecords(const byte *rec, uint count,
 				if (picID != 0 && picID != 0xFFFF) {
 					Picture pic;
 					if (_picsArchive.getPicture(picID, pic)) {
-						const byte transpC = (byte)(pic.flags >> 8);
-						const int pw = MIN<int>(pic.surface.w, 320 - picX);
-						const int ph = MIN<int>(pic.surface.h, 200 - picY);
-						for (int row = 0; row < ph; row++) {
-							const byte *src = (const byte *)
-								pic.surface.getBasePtr(0, row);
-							byte *dst = (byte *)
-								scratch.getBasePtr(picX, picY + row);
-							for (int col = 0; col < pw; col++) {
-								if (src[col] != transpC)
-									dst[col] = src[col];
-							}
-						}
+						scratch.transBlitFrom(pic.surface,
+											  Common::Point(picX, picY),
+											  (uint32)(byte)(pic.flags >> 8));
 					}
 				}
 				if (haveBalloon) {
diff --git a/engines/eem/graphics.cpp b/engines/eem/graphics.cpp
index 8251aee33f7..544e66c7f50 100644
--- a/engines/eem/graphics.cpp
+++ b/engines/eem/graphics.cpp
@@ -274,18 +274,9 @@ void EEMEngine::doHelp() {
 			const uint h = (uint)balloon.surface.h;
 			if (h < 0x4e)
 				balloonY = (uint16)((0x50 - h) >> 1);
-			const byte transp = (byte)(balloon.flags >> 8);
-			for (int row = 0; row < balloon.surface.h && balloonY + row < 200;
-				 row++) {
-				const byte *src =
-					(const byte *)balloon.surface.getBasePtr(0, row);
-				byte *dst = (byte *)ms.getBasePtr(0x21, balloonY + row);
-				for (int col = 0; col < balloon.surface.w && 0x21 + col < 320;
-					 col++) {
-					if (src[col] != transp)
-						dst[col] = src[col];
-				}
-			}
+			ms.transBlitFrom(balloon.surface,
+							 Common::Point(0x21, balloonY),
+							 (uint32)(byte)(balloon.flags >> 8));
 		}
 		uint16 bx = 5;
 		uint16 by = 4;
@@ -669,19 +660,8 @@ void EEMEngine::drawFloppyBubbleIndicator(Graphics::ManagedSurface &dst,
 		return;
 	const int x = ballX + (int)dx;
 	const int y = ballY + (int)dy;
-	const byte transp = (byte)(pic.flags >> 8);
-	const int pw = MIN<int>(pic.surface.w, 320 - x);
-	const int ph = MIN<int>(pic.surface.h, 200 - y);
-	if (x < 0 || y < 0 || pw <= 0 || ph <= 0)
-		return;
-	for (int row = 0; row < ph; row++) {
-		const byte *src = (const byte *)pic.surface.getBasePtr(0, row);
-		byte *out = (byte *)dst.getBasePtr(x, y + row);
-		for (int col = 0; col < pw; col++) {
-			if (src[col] != transp)
-				out[col] = src[col];
-		}
-	}
+	dst.transBlitFrom(pic.surface, Common::Point(x, y),
+					  (uint32)(byte)(pic.flags >> 8));
 }
 
 } // End of namespace EEM
diff --git a/engines/eem/site.cpp b/engines/eem/site.cpp
index 47bd881a88d..b0b5a2e1226 100644
--- a/engines/eem/site.cpp
+++ b/engines/eem/site.cpp
@@ -45,6 +45,24 @@ void blitFrame(Graphics::ManagedSurface &dst, const Picture &p,
 	dst.transBlitFrom(p.surface, Common::Point(x, y), (uint32)transp);
 }
 
+// Masked top-left blit onto a locked screen surface. Clips both src and
+// dst against the screen, then delegates to copyRectToSurfaceWithKey
+// (Graphics::Surface's transparent-key blit).
+static void keyBlitToScreen(Graphics::Surface *screen, const Picture &p,
+							int x, int y) {
+	if (!screen || p.surface.empty())
+		return;
+	const Common::Rect dst = Common::Rect(x, y, x + p.surface.w,
+										  y + p.surface.h)
+		.findIntersectingRect(Common::Rect(screen->w, screen->h));
+	if (dst.isEmpty())
+		return;
+	const Common::Rect src(dst.left - x, dst.top - y,
+						   dst.right - x, dst.bottom - y);
+	screen->copyRectToSurfaceWithKey(p.surface, dst.left, dst.top,
+									 src, (uint32)(byte)(p.flags >> 8));
+}
+
 // Top-left masked blit. `_AddDrop @ 172b:1a77` calls
 // `_Rect_Move_Mask(..., x, y, ...)` with the raw (x, y) and IGNORES
 // per-frame anchor offsets ??? so this is the correct path for static
@@ -53,50 +71,18 @@ void blitFrame(Graphics::ManagedSurface &dst, const Picture &p,
 // (miscflags = X, rowoff = Y) apply correctly.
 void blitMaskedSurface(Graphics::Surface *screen, const Picture &p,
 					   int x, int y) {
-	if (!screen)
-		return;
-	const byte transp = (byte)(p.flags >> 8);
-	for (int row = 0; row < p.surface.h; row++) {
-		const int dstY = y + row;
-		if (dstY < 0 || dstY >= screen->h)
-			continue;
-		const byte *src = (const byte *)p.surface.getBasePtr(0, row);
-		byte *dst = (byte *)screen->getBasePtr(0, dstY);
-		for (int col = 0; col < p.surface.w; col++) {
-			const int dstX = x + col;
-			if (dstX < 0 || dstX >= screen->w)
-				continue;
-			if (src[col] != transp)
-				dst[dstX] = src[col];
-		}
-	}
+	keyBlitToScreen(screen, p, x, y);
 }
 
+// `_UpdateAnimations @ 172b:09c1`: blit at
+//   (anchor_x - puVar5[4], anchor_y - puVar5[3])
+// where puVar5[3]/[4] are per-frame rowoff/miscflags (signed int16) from
+// the 16-byte PicData header. Transparency = flags >> 8.
 void blitAnimFrameAnchored(Graphics::Surface *screen, const Picture &p,
 						   int anchorX, int anchorY) {
-	// `_UpdateAnimations @ 172b:09c1`: blit at
-	//   (anchor_x - puVar5[4], anchor_y - puVar5[3])
-	// where puVar5[3]/[4] are per-frame rowoff/miscflags (signed int16)
-	// from the 16-byte PicData header. Transparency = flags >> 8.
-	if (!screen)
-		return;
-	const int blitX = anchorX - (int)(int16)p.miscflags;
-	const int blitY = anchorY - (int)(int16)p.rowoff;
-	const byte transp = (byte)(p.flags >> 8);
-	for (int row = 0; row < p.surface.h; row++) {
-		const int dstY = blitY + row;
-		if (dstY < 0 || dstY >= screen->h)
-			continue;
-		const byte *src = (const byte *)p.surface.getBasePtr(0, row);
-		byte *dst = (byte *)screen->getBasePtr(0, dstY);
-		for (int col = 0; col < p.surface.w; col++) {
-			const int dstX = blitX + col;
-			if (dstX < 0 || dstX >= screen->w)
-				continue;
-			if (src[col] != transp)
-				dst[dstX] = src[col];
-		}
-	}
+	keyBlitToScreen(screen, p,
+					anchorX - (int)(int16)p.miscflags,
+					anchorY - (int)(int16)p.rowoff);
 }
 
 // `_ColorCycle @ 172b:2015` ??? rotate `_fpal[start..end]` by one slot:
diff --git a/engines/eem/ui.cpp b/engines/eem/ui.cpp
index 503874824e6..c1398a0fc48 100644
--- a/engines/eem/ui.cpp
+++ b/engines/eem/ui.cpp
@@ -275,23 +275,9 @@ void blitMaskedPicSlice(Graphics::ManagedSurface &dst, const Picture &pic,
 						int dstX, int dstY) {
 	if (pic.surface.empty() || w <= 0 || h <= 0)
 		return;
-
-	const byte transp = (byte)(pic.flags >> 8);
-	for (int row = 0; row < h; row++) {
-		const int sy = srcY + row;
-		const int dy = dstY + row;
-		if (sy < 0 || sy >= pic.surface.h || dy < 0 || dy >= dst.h)
-			continue;
-		for (int col = 0; col < w; col++) {
-			const int sx = srcX + col;
-			const int dx = dstX + col;
-			if (sx < 0 || sx >= pic.surface.w || dx < 0 || dx >= dst.w)
-				continue;
-			const byte c = *(const byte *)pic.surface.getBasePtr(sx, sy);
-			if (c != transp)
-				*(byte *)dst.getBasePtr(dx, dy) = c;
-		}
-	}
+	const Common::Rect srcRect(srcX, srcY, srcX + w, srcY + h);
+	dst.transBlitFrom(pic.surface, srcRect, Common::Point(dstX, dstY),
+					  (uint32)(byte)(pic.flags >> 8));
 }
 
 void blitMaskedPic(Graphics::ManagedSurface &dst, const Picture &pic,
@@ -2898,24 +2884,13 @@ void EEMEngine::drawGalleryFrame(const byte *gd, uint8 numSuspects,
 
 			const int placeX = s.x;
 			const int placeY = s.y + (0x48 - portrait.surface.h);
-			const byte transp = (byte)(portrait.flags >> 8);
 			const int w = MIN<int>(portrait.surface.w, 320 - placeX);
 			const int h = MIN<int>(portrait.surface.h, 200 - placeY);
 			if (w <= 0 || h <= 0)
 				continue;
-			for (int row = 0; row < h; row++) {
-				const int dstY = placeY + row;
-				if (dstY < 0)
-					continue;
-				const byte *src =
-					(const byte *)portrait.surface.getBasePtr(0, row);
-				byte *dst = (byte *)scratch.getBasePtr(0, dstY);
-				for (int col = 0; col < w; col++) {
-					const int dstX = placeX + col;
-					if (src[col] != transp)
-						dst[dstX] = src[col];
-				}
-			}
+			scratch.transBlitFrom(portrait.surface,
+								  Common::Point(placeX, placeY),
+								  (uint32)(byte)(portrait.flags >> 8));
 			slotRects[i] = Common::Rect(placeX, placeY,
 										 placeX + w, placeY + h);
 			slotSuspect[i] = (int)i;
@@ -3360,21 +3335,16 @@ void EEMEngine::drawBigMapDetail(int scrollX, int scrollY,
 		}
 		const int sx = (int)mx - scrollX + kMapWinX;
 		const int sy = (int)my - scrollY + kMapWinY;
-		const byte transp = (byte)(button.flags >> 8);
-
-		// Crop blit against the viewport.
+		// Crop the button blit against the viewport.
 		const int x0 = MAX<int>(sx, kMapWinX);
 		const int y0 = MAX<int>(sy, kMapWinY);
 		const int x1 = MIN<int>(sx + button.surface.w, kMapWinX + kMapWinW);
 		const int y1 = MIN<int>(sy + button.surface.h, kMapWinY + kMapWinH);
-		for (int row = y0; row < y1; row++) {
-			const byte *src = (const byte *)button.surface.getBasePtr(0, row - sy);
-			byte *dst = (byte *)scratch.getBasePtr(0, row);
-			for (int col = x0; col < x1; col++) {
-				const byte px = src[col - sx];
-				if (px != transp)
-					dst[col] = px;
-			}
+		if (x1 > x0 && y1 > y0) {
+			const Common::Rect srcRect(x0 - sx, y0 - sy, x1 - sx, y1 - sy);
+			scratch.transBlitFrom(button.surface, srcRect,
+								  Common::Point(x0, y0),
+								  (uint32)(byte)(button.flags >> 8));
 		}
 	}
 
@@ -5069,24 +5039,13 @@ void EEMEngine::drawAccuseGallery(uint8 numSuspects, const byte *gd,
 
 		const int placeX = s.x;
 		const int placeY = s.y + (0x48 - portrait.surface.h);
-		const byte transp = (byte)(portrait.flags >> 8);
 		const int w = MIN<int>(portrait.surface.w, 320 - placeX);
 		const int h = MIN<int>(portrait.surface.h, 200 - placeY);
 		if (w <= 0 || h <= 0)
 			continue;
-		for (int row = 0; row < h; row++) {
-			const int dstY = placeY + row;
-			if (dstY < 0)
-				continue;
-			const byte *src =
-				(const byte *)portrait.surface.getBasePtr(0, row);
-			byte *dst = (byte *)scratch.getBasePtr(0, dstY);
-			for (int col = 0; col < w; col++) {
-				const int dstX = placeX + col;
-				if (src[col] != transp)
-					dst[dstX] = src[col];
-			}
-		}
+		scratch.transBlitFrom(portrait.surface,
+							  Common::Point(placeX, placeY),
+							  (uint32)(byte)(portrait.flags >> 8));
 		slotRects[i] = Common::Rect(placeX, placeY,
 									 placeX + w, placeY + h);
 		slotSuspect[i] = (int)i;


Commit: e34c624ee4211dcce40b9693f53caf32364d7e5a
    https://github.com/scummvm/scummvm/commit/e34c624ee4211dcce40b9693f53caf32364d7e5a
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:21:13+02:00

Commit Message:
EEM: reduced code duplication in the PDA rendering

Changed paths:
    engines/eem/ui.cpp


diff --git a/engines/eem/ui.cpp b/engines/eem/ui.cpp
index c1398a0fc48..0416fea7120 100644
--- a/engines/eem/ui.cpp
+++ b/engines/eem/ui.cpp
@@ -111,19 +111,50 @@ void swapColors(Graphics::ManagedSurface &dst,
 	}
 }
 
-void blitAccusePartner(Graphics::ManagedSurface &dstSurface,
-					   DBDArchive &aniArchive, uint8 partner,
-					   uint32 tickMs) {
-	const uint partnerAnim = (partner == 0) ? 2 : 0x10;
-	Animation partnerAni;
-	if (aniArchive.loadAnimation(partnerAnim, partnerAni) &&
-		!partnerAni.empty()) {
-		const uint frameIdx = partnerFrameAtTick(0x02,
-												 (uint)partnerAni.size(),
-												 tickMs);
-		blitAnimFrameAnchored(dstSurface.surfacePtr(),
-							  partnerAni[frameIdx], 5, 0x50);
-	}
+// PDA-frame partner sprite specs. `(5, 0x50)` is the partner-at-desk anchor
+// shared by _DoNotebook (script 0x01, anim 1/0xb) and _DoGallery / _DoAccuse
+// / _MoreInfo (script 0x02, anim 2/0x10).
+struct PdaPartnerSpec {
+	uint16 scriptId;
+	uint16 animJake;
+	uint16 animJenny;
+	int    anchorX;
+	int    anchorY;
+};
+constexpr PdaPartnerSpec kPdaGalleryPartner { 0x02, 0x02, 0x10, 5, 0x50 };
+constexpr PdaPartnerSpec kPdaNotebookPartner{ 0x01, 0x01, 0x0b, 5, 0x50 };
+
+// Lookup current frame for spec; returns nullptr if anim load fails. `outAni`
+// owns the loaded cells; the returned pointer is valid until `outAni` goes
+// out of scope.
+const Picture *partnerFrameFor(DBDArchive &aniArchive, uint8 partner,
+							   const PdaPartnerSpec &spec, uint32 tickMs,
+							   Animation &outAni) {
+	const uint animId = (partner == 0) ? spec.animJake : spec.animJenny;
+	if (!aniArchive.loadAnimation(animId, outAni) || outAni.empty())
+		return nullptr;
+	const uint frameIdx = partnerFrameAtTick(spec.scriptId,
+											  (uint)outAni.size(), tickMs);
+	return &outAni[frameIdx];
+}
+
+void blitPdaPartner(Graphics::ManagedSurface &dst, DBDArchive &aniArchive,
+					uint8 partner, const PdaPartnerSpec &spec,
+					uint32 tickMs) {
+	Animation ani;
+	if (const Picture *fr = partnerFrameFor(aniArchive, partner, spec,
+											tickMs, ani))
+		blitAnimFrameAnchored(dst.surfacePtr(), *fr, spec.anchorX,
+							  spec.anchorY);
+}
+
+void blitPdaPartner(Graphics::Surface *screen, DBDArchive &aniArchive,
+					uint8 partner, const PdaPartnerSpec &spec,
+					uint32 tickMs) {
+	Animation ani;
+	if (const Picture *fr = partnerFrameFor(aniArchive, partner, spec,
+											tickMs, ani))
+		blitAnimFrameAnchored(screen, *fr, spec.anchorX, spec.anchorY);
 }
 
 constexpr Common::Rect kEndingPrevPageRect(Common::Point(0, 0), 28, 200);
@@ -2263,15 +2294,8 @@ void EEMEngine::drawNotebookFrame(int &page) {
 		scratch.simpleBlitFrom(frame.surface);
 
 	// Partner ANI 1/0xb (cells); script 0x01 (`_NewAnimation @ 161e:054c`).
-	const uint partnerAnim = (_partner == 0) ? 1 : 0xb;
-	Animation partnerAni;
-	if (_aniArchive.loadAnimation(partnerAnim, partnerAni) && !partnerAni.empty()) {
-		const uint32 now = g_system->getMillis();
-		const uint frameIdx = partnerFrameAtTick(0x01,
-												  (uint)partnerAni.size(), now);
-		blitAnimFrameAnchored(scratch.surfacePtr(),
-							  partnerAni[frameIdx], 5, 80);
-	}
+	blitPdaPartner(scratch, _aniArchive, _partner, kPdaNotebookPartner,
+				   g_system->getMillis());
 
 	// `_DrawNotes` walks `_NoteIndex` for current page; word-wraps each
 	// found clue in `_NotebookRect`. Selected = color 0x3c.
@@ -2574,18 +2598,8 @@ bool EEMEngine::moreInfo(const byte *gd, uint suspectIdx,
 		if (haveBg)
 			ms.simpleBlitFrom(galBg.surface);
 		// Partner sprite at (5, 0x50). Re-blitted per page.
-		{
-			const uint partnerAnim = (_partner == 0) ? 2 : 0x10;
-			Animation partnerAni;
-			if (_aniArchive.loadAnimation(partnerAnim, partnerAni) &&
-				!partnerAni.empty()) {
-				const uint32 now = g_system->getMillis();
-				const uint frameIdx = partnerFrameAtTick(0x02,
-					(uint)partnerAni.size(), now);
-				blitAnimFrameAnchored(ms.surfacePtr(),
-									  partnerAni[frameIdx], 5, 0x50);
-			}
-		}
+		blitPdaPartner(ms, _aniArchive, _partner, kPdaGalleryPartner,
+					   g_system->getMillis());
 		Picture detail;
 		if (_picsArchive.getPicture(detailPic, detail)) {
 			const byte transp = (byte)(detail.flags >> 8);
@@ -2818,10 +2832,6 @@ void EEMEngine::drawGalleryFrame(const byte *gd, uint8 numSuspects,
 	// positions live in `kGallerySlots` in this file's anon namespace.
 	Picture galBg;
 	const bool haveBg = _picsArchive.getPicture(0x3f, galBg);
-	const uint partnerAnim = (_partner == 0) ? 2 : 0x10;
-	Animation partnerAni;
-	const bool havePartner = _aniArchive.loadAnimation(partnerAnim, partnerAni)
-							  && !partnerAni.empty();
 
 	Graphics::ManagedSurface scratch(320, 200,
 		Graphics::PixelFormat::createFormatCLUT8());
@@ -2830,24 +2840,13 @@ void EEMEngine::drawGalleryFrame(const byte *gd, uint8 numSuspects,
 	if (haveBg)
 		scratch.simpleBlitFrom(galBg.surface);
 
-	// Partner sprite frame @ (5, 0x50). The original `_DoGallery @
-	// 158f:065b` registers `_NewAnimation(..., CONCAT22(2, ...), ...)`
-	// ??? script key 0x02 regardless of partner. Jake's 0x02 script
-	// (26 frames, brief wave + long hold + second wave) is what
-	// drives BOTH partners' cells. Earlier our port used 0x10 for
-	// Jenny, which is a 9-frame short blip ??? so Jenny was missing
-	// 17 frames of the wave-and-pause cadence that Jake has.
-	if (havePartner) {
-		const uint32 now = g_system->getMillis();
-		const uint frameIdx = partnerFrameAtTick(0x02,
-												  (uint)partnerAni.size(), now);
-		// Anchor-aware blit, consistent with site-loop / BigMap
-		// rendering paths. Anim 0x02 has rowoff = miscflags = 0
-		// per the audit but the anchored blitter is still the
-		// right semantic for an `_NewAnimation`-rendered sprite.
-		blitAnimFrameAnchored(scratch.surfacePtr(),
-							  partnerAni[frameIdx], 5, 0x50);
-	}
+	// Partner sprite frame @ (5, 0x50). `_DoGallery @ 158f:065b` registers
+	// _NewAnimation(..., CONCAT22(2, ...), ...) ??? script key 0x02 regardless
+	// of partner. Jake's 26-frame 0x02 script (brief wave + long hold +
+	// second wave) drives BOTH partners' cells (Jenny's own 0x10 anim is
+	// only 9 frames so it wouldn't cover the full cadence).
+	blitPdaPartner(scratch, _aniArchive, _partner, kPdaGalleryPartner,
+				   g_system->getMillis());
 
 	// Portraits ??? `_DrawGallery @ 158f:0046` (CD) /
 	// `_DrawGallery_Floppy @ 154e:0045` (floppy) walks suspects 0..N-1
@@ -3430,16 +3429,8 @@ void EEMEngine::accuseDrawScreen(const AccuseNotesCtx &ctx) {
 
 	// Partner at (5, 0x50). `_DoAccuse @ 1df2:0c2c`: ANI 2/0x10,
 	// script 2, prior 1.
-	const uint partnerAnim = (_partner == 0) ? 2 : 0x10;
-	Animation partnerAni;
-	if (_aniArchive.loadAnimation(partnerAnim, partnerAni) &&
-		!partnerAni.empty()) {
-		const uint32 now = g_system->getMillis();
-		const uint frameIdx = partnerFrameAtTick(0x02,
-												  (uint)partnerAni.size(), now);
-		blitAnimFrameAnchored(scratch.surfacePtr(),
-							  partnerAni[frameIdx], 5, 0x50);
-	}
+	blitPdaPartner(scratch, _aniArchive, _partner, kPdaGalleryPartner,
+				   g_system->getMillis());
 
 	// Selected=0x3c, unselected=1 (`_NoteUnselectedColor` @ 1df2:0c25).
 	Common::Array<Common::Rect> &slotRects = *ctx.slotRects;
@@ -4173,11 +4164,6 @@ void EEMEngine::doAccuse() {
 		}
 		// Partner at (5, 0x50). ANI 2/0x10, script 0x02 (`_DoAccuse
 		// @ 1df2:0c30`). Drawn after suspect.
-		const uint partnerAnim = (_partner == 0) ? 2 : 0x10;
-		Animation partnerAni;
-		const bool havePartner =
-			_aniArchive.loadAnimation(partnerAnim, partnerAni) &&
-			!partnerAni.empty();
 
 		// scratch = base + alibi balloon/text + partner.
 		Graphics::ManagedSurface scratch(320, 200,
@@ -4197,12 +4183,8 @@ void EEMEngine::doAccuse() {
 								  balloonY + ty, tw, alibi,
 								  haveBalloon ? 0 : 0xF);
 		}
-		if (havePartner) {
-			const uint frameIdx = partnerFrameAtTick(0x02,
-				(uint)partnerAni.size(), g_system->getMillis());
-			blitAnimFrameAnchored(scratch.surfacePtr(),
-								  partnerAni[frameIdx], 5, 0x50);
-		}
+		blitPdaPartner(scratch, _aniArchive, _partner, kPdaGalleryPartner,
+					   g_system->getMillis());
 
 		// MIDI 6 ??? blocks until done (or click/ESC aborts).
 		if (_music && _voiceOn) {
@@ -4289,14 +4271,8 @@ void EEMEngine::doAccuse() {
 										  rY + rty, rtw, react,
 										  haveR ? 0 : 0xF);
 				}
-				if (havePartner) {
-					const uint frameIdx = partnerFrameAtTick(0x02,
-						(uint)partnerAni.size(),
-						g_system->getMillis());
-					blitAnimFrameAnchored(scratch.surfacePtr(),
-										  partnerAni[frameIdx],
-										  5, 0x50);
-				}
+				blitPdaPartner(scratch, _aniArchive, _partner,
+							   kPdaGalleryPartner, g_system->getMillis());
 				g_system->copyRectToScreen(scratch.getPixels(),
 					scratch.pitch, 0, 0, 320, 200);
 				g_system->updateScreen();
@@ -4385,19 +4361,10 @@ void EEMEngine::doAccuse() {
 		}
 
 		// Stamp partner at (5, 0x50); displayClue snapshots screen.
-		const uint partnerAnim = (_partner == 0) ? 2 : 0x10;
-		Animation partnerAni;
-		if (_aniArchive.loadAnimation(partnerAnim, partnerAni) &&
-			!partnerAni.empty()) {
-			Graphics::Surface *screen = g_system->lockScreen();
-			if (screen) {
-				const uint frameIdx = partnerFrameAtTick(0x02,
-					(uint)partnerAni.size(),
-					g_system->getMillis());
-				blitAnimFrameAnchored(screen, partnerAni[frameIdx],
-									  5, 0x50);
-				g_system->unlockScreen();
-			}
+		if (Graphics::Surface *screen = g_system->lockScreen()) {
+			blitPdaPartner(screen, _aniArchive, _partner,
+						   kPdaGalleryPartner, g_system->getMillis());
+			g_system->unlockScreen();
 		}
 		g_system->updateScreen();
 
@@ -4520,16 +4487,8 @@ void EEMEngine::accuseDrawGallery(int highlighted,
 	if (haveAccuseBg)
 		scratch.simpleBlitFrom(accuseBg.surface);
 
-	const uint partnerAnim = (_partner == 0) ? 2 : 0x10;
-	Animation partnerAni;
-	if (_aniArchive.loadAnimation(partnerAnim, partnerAni) &&
-		!partnerAni.empty()) {
-		const uint32 now = g_system->getMillis();
-		const uint frameIdx = partnerFrameAtTick(0x02,
-												  (uint)partnerAni.size(), now);
-		blitAnimFrameAnchored(scratch.surfacePtr(),
-							  partnerAni[frameIdx], 5, 0x50);
-	}
+	blitPdaPartner(scratch, _aniArchive, _partner, kPdaGalleryPartner,
+				   g_system->getMillis());
 
 	rects.resize(num);
 	suspects.resize(num);
@@ -4752,19 +4711,10 @@ void EEMEngine::doAccuseFloppy() {
 
 			// Stamp partner at (5, 0x50); displayFloppyDialogRecords
 			// snapshots screen. ANI 2/0x10, script 0x02.
-			const uint partnerAnim = (_partner == 0) ? 2 : 0x10;
-			Animation partnerAni;
-			if (_aniArchive.loadAnimation(partnerAnim, partnerAni) &&
-				!partnerAni.empty()) {
-				Graphics::Surface *screen = g_system->lockScreen();
-				if (screen) {
-					const uint frameIdx = partnerFrameAtTick(0x02,
-						(uint)partnerAni.size(),
-						g_system->getMillis());
-					blitAnimFrameAnchored(screen, partnerAni[frameIdx],
-										  5, 0x50);
-					g_system->unlockScreen();
-				}
+			if (Graphics::Surface *screen = g_system->lockScreen()) {
+				blitPdaPartner(screen, _aniArchive, _partner,
+							   kPdaGalleryPartner, g_system->getMillis());
+				g_system->unlockScreen();
 			}
 			g_system->updateScreen();
 		}
@@ -4955,7 +4905,8 @@ void EEMEngine::doAccuseFloppy() {
 							  MAX<int>(8, (int)tw), alibi, 0);
 	}
 	// Stamp partner resting frame before KD reaction snapshots screen.
-	blitAccusePartner(scene, _aniArchive, _partner, g_system->getMillis());
+	blitPdaPartner(scene, _aniArchive, _partner, kPdaGalleryPartner,
+				   g_system->getMillis());
 	g_system->copyRectToScreen(scene.getPixels(), scene.pitch, 0, 0, 320, 200);
 	g_system->updateScreen();
 
@@ -5006,16 +4957,8 @@ void EEMEngine::drawAccuseGallery(uint8 numSuspects, const byte *gd,
 		scratch.simpleBlitFrom(accuseBg.surface);
 
 	// Partner drawn first; defensive (no slot overlap).
-	const uint partnerAnim = (_partner == 0) ? 2 : 0x10;
-	Animation partnerAni;
-	if (_aniArchive.loadAnimation(partnerAnim, partnerAni) &&
-		!partnerAni.empty()) {
-		const uint32 now = g_system->getMillis();
-		const uint frameIdx = partnerFrameAtTick(0x02,
-												  (uint)partnerAni.size(), now);
-		blitAnimFrameAnchored(scratch.surfacePtr(),
-							  partnerAni[frameIdx], 5, 0x50);
-	}
+	blitPdaPartner(scratch, _aniArchive, _partner, kPdaGalleryPartner,
+				   g_system->getMillis());
 
 	for (uint i = 0; i < numSuspects && i < Mystery::kGalleryCap; i++) {
 		slotRects[i] = Common::Rect();


Commit: 40c2459237d7641db42ef98a4222bd5b4b8b9192
    https://github.com/scummvm/scummvm/commit/40c2459237d7641db42ef98a4222bd5b4b8b9192
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:21:14+02:00

Commit Message:
EEM: some comment fixes and clarifications

Changed paths:
    engines/eem/clues.cpp
    engines/eem/eem.cpp
    engines/eem/ui.cpp


diff --git a/engines/eem/clues.cpp b/engines/eem/clues.cpp
index fcb47cdb06d..03233dafb3f 100644
--- a/engines/eem/clues.cpp
+++ b/engines/eem/clues.cpp
@@ -541,7 +541,13 @@ void EEMEngine::doInitClues() {
 //   0x83 _Partner == 0 ? "he"       : "she"
 //   0x84 _Partner == 0 ? "him"      : "her"
 //   0x85 _Partner == 0 ? "his"      : "her"
-//   0x86..0x88 read a separate gender flag @ 0x7985 (TODO).
+//   0x86..0x88 mirror 0x83..0x85 but branch on a separate gender flag
+//     (DAT_29be_7985, handlers @ 1b66:0ad2/0b41/0bb0). That flag has no
+//     writers anywhere in EEMCD.EXE ??? only the three handlers read it ???
+//     and no shipping mystery text (CD or floppy) contains the 0x86/0x87
+//     /0x88 bytes inside parseString-formatted strings. The opcodes are
+//     dead in the original engine and dead in our data, so we drop them
+//     silently (which matches the always-0 flag path in DOS: he/him/his).
 //   0x89 KD hint placeholder (caller handles).
 Common::String EEMEngine::parseString(const Common::String &raw,
 									  const Common::String &playerName,
@@ -572,8 +578,12 @@ Common::String EEMEngine::parseString(const Common::String &raw,
 		case 0x86:
 		case 0x87:
 		case 0x88:
+			// Stubbed suspect-gender pronouns; the DOS flag they branch
+			// on is never written and no mystery msg uses these bytes
+			// (see jumptable comment above).
+			break;
 		case 0x89:
-			// Eaten silently ??? see comment above.
+			// KD hint placeholder (caller handles before this point).
 			break;
 		case 0:
 			return out;
@@ -602,7 +612,7 @@ Common::String EEMEngine::parseString(const Common::String &raw,
 	// `_DoWordWrap @ 1b66:04a7`, which advances past spaces at the
 	// start of every output line via `for (; str[last] == ' '; last++)`.
 	// ~60% of mystery-text strings carry 1-2 leading spaces in the data
-	// (verified across all CD M*.BIN files); the original WordWrap
+	// of the original WordWrap
 	// discards them, so we do the same before the text reaches
 	// `Font::wordWrapText` (which only trims at wrap-induced line
 	// boundaries, not at start-of-input or after an embedded '\n').
diff --git a/engines/eem/eem.cpp b/engines/eem/eem.cpp
index 89c89a88b48..088f8a8d5d4 100644
--- a/engines/eem/eem.cpp
+++ b/engines/eem/eem.cpp
@@ -1182,7 +1182,7 @@ Common::Error EEMEngine::loadGameStream(Common::SeekableReadStream *stream) {
 
 SaveStateList EEMEngine::listProfiles() const {
 	// _findfirst("*.PLR") in screen8_handler @ 1c33:1012.
-	// Filter out slot 0 (ScummVM autosave) to match the original which
+	// Filter out slot 0 (autosave) to match the original which
 	// has no autosave concept.
 	SaveStateList saves = getMetaEngine()->listSaves(_targetName.c_str());
 	for (uint i = 0; i < saves.size(); ) {
diff --git a/engines/eem/ui.cpp b/engines/eem/ui.cpp
index 0416fea7120..2d36b72f204 100644
--- a/engines/eem/ui.cpp
+++ b/engines/eem/ui.cpp
@@ -1993,7 +1993,7 @@ void EEMEngine::doCaseSelection() {
 					const uint idx = topRow + (uint)row;
 					if (idx >= listLen || solvedFlags[idx])
 						continue;
-					// Second click on selected row = OK (ScummVM ergonomic).
+					// Second click on selected row = OK (QoL fix).
 					if (idx == selRow) {
 						confirmed = true;
 						break;
@@ -2854,8 +2854,7 @@ void EEMEngine::drawGalleryFrame(const byte *gd, uint8 numSuspects,
 	// Layout differs by variant:
 	//   * CD: fixed 0x46-byte stride, slot positions at `kGallerySlots`.
 	//   * Floppy: variable-stride entries (5 + entry[4] bytes per
-	//     suspect), slot positions at `kFloppyGallerySlots` (verified
-	//     at `2608:0x16c`).
+	//     suspect), slot positions at `kFloppyGallerySlots` (`2608:0x16c`).
 	const bool floppy = isFloppy();
 	const GallerySlot * const slots =
 		floppy ? kFloppyGallerySlots : kGallerySlots;
@@ -3228,7 +3227,7 @@ void EEMEngine::drawBigMapOverview(uint32 elapsedMs) {
 			continue;
 		// CD entries are 14 bytes: X at +4, Y at +6, crime at +12.
 		// Floppy entries are 11 bytes: X at +6, Y at +8, recolor at +10.
-		// Floppy layout verified at `FUN_1fed_07ed` (BigMap iteration):
+		// Floppy layout: `FUN_1fed_07ed` (BigMap iteration):
 		//   `*(int *)(pcVar2 + i*0xb + 7)` (= entry+6, X u16)
 		//   `*(int *)(pcVar2 + i*0xb + 9)` (= entry+8, Y u16)
 		//   `pcVar2[i*0xb + 0xb]` (= entry+10, recolor flag ??? non-zero


Commit: 0a11cfeeded82cb19bf471d0a78ec6525cb4e4fb
    https://github.com/scummvm/scummvm/commit/0a11cfeeded82cb19bf471d0a78ec6525cb4e4fb
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:21:14+02:00

Commit Message:
EEM: use named constants here and there

Changed paths:
    engines/eem/clues.cpp
    engines/eem/eem.cpp
    engines/eem/eem.h
    engines/eem/graphics.cpp
    engines/eem/site.cpp
    engines/eem/ui.cpp


diff --git a/engines/eem/clues.cpp b/engines/eem/clues.cpp
index 03233dafb3f..ee88f8a8a43 100644
--- a/engines/eem/clues.cpp
+++ b/engines/eem/clues.cpp
@@ -130,8 +130,8 @@ void blitMaskedToScreen(const Picture &p, int x, int y) {
 }
 
 void blitRawToScreen(const Picture &p, int x, int y) {
-	const int w = MIN<int>(p.surface.w, 320 - x);
-	const int h = MIN<int>(p.surface.h, 200 - y);
+	const int w = MIN<int>(p.surface.w, kScreenWidth - x);
+	const int h = MIN<int>(p.surface.h, kScreenHeight - y);
 	if (x < 0 || y < 0 || w <= 0 || h <= 0)
 		return;
 
@@ -219,18 +219,18 @@ void EEMEngine::doChoosePartner() {
 				}
 			}
 			if (ev.type == Common::EVENT_LBUTTONDOWN) {
-				_partner = (ev.mouse.x >= 160) ? 0 : 1;
+				_partner = (ev.mouse.x >= 160) ? kPartnerJake : kPartnerJenny;
 				debugC(1, kDebugGeneral, "Partner picked: %s",
-					   _partner == 0 ? "Jake" : "Jennifer");
+					   _partner == kPartnerJake ? "Jake" : "Jennifer");
 				done = true;
 				break;
 			}
 			if (ev.type == Common::EVENT_KEYDOWN) {
 				if (ev.kbd.keycode == Common::KEYCODE_LEFT) {
-					_partner = 1; done = true; break;
+					_partner = kPartnerJenny; done = true; break;
 				}
 				if (ev.kbd.keycode == Common::KEYCODE_RIGHT) {
-					_partner = 0; done = true; break;
+					_partner = kPartnerJake; done = true; break;
 				}
 				if (ev.kbd.keycode == Common::KEYCODE_RETURN ||
 					ev.kbd.keycode == Common::KEYCODE_ESCAPE) {
@@ -255,7 +255,7 @@ void EEMEngine::doChoosePartner() {
 			_audio->playFloppyVoiceSlot(0x14, _partner);
 		} else {
 			_audio->playVoc(Common::Path(
-				(_partner == 0) ? "JAKE.VOC" : "JEN.VOC"));
+				(_partner == kPartnerJake) ? "JAKE.VOC" : "JEN.VOC"));
 		}
 		_audio->waitForVoiceDone();
 	}
@@ -306,8 +306,8 @@ void EEMEngine::doInitClues() {
 	if (haveBriefingBg)
 		blitAt(bg, 0, 0);
 
-	const uint gameAni = _partner == 0 ? 0x17 : 0x3b;
-	const uint bookAni = _partner == 0 ? 0x18 : 0x3c;
+	const uint gameAni = _partner == kPartnerJake ? 0x17 : 0x3b;
+	const uint bookAni = _partner == kPartnerJake ? 0x18 : 0x3c;
 	Animation game, book, nancy;
 	const bool haveGame  = _aniArchive.loadAnimation(gameAni, game) && !game.empty();
 	const bool haveBook  = _aniArchive.loadAnimation(bookAni, book) && !book.empty();
@@ -384,7 +384,7 @@ void EEMEngine::doInitClues() {
 	// clearing registered animations. Width is in mode-X cols (0x28 = 160px).
 	// Intentionally drops the right-side game anim; _PlayInSequence redraws
 	// that character over a clean BG next.
-	Graphics::ManagedSurface briefingBase(320, 200,
+	Graphics::ManagedSurface briefingBase(kScreenWidth, kScreenHeight,
 		Graphics::PixelFormat::createFormatCLUT8());
 	briefingBase.clear();
 	if (haveBriefingBg) {
@@ -442,7 +442,7 @@ void EEMEngine::doInitClues() {
 	//          caseType 3 -> 0x3d @ (0xcd, 0x6c)
 	uint16 seqAni = 0xFFFF;
 	uint16 seqY   = 0x6c;
-	if (_partner == 0) {
+	if (_partner == kPartnerJake) {
 		switch (caseType) {
 		case 1:
 			seqAni = 0x38;
@@ -482,7 +482,7 @@ void EEMEngine::doInitClues() {
 				const Picture &fr = seq[frame];
 				g_system->copyRectToScreen(briefingBase.getPixels(),
 										   briefingBase.pitch, 0, 0,
-										   320, 200);
+										   kScreenWidth, kScreenHeight);
 				// _PlayInSequence @ 172b:2d35-2d50:
 				//   dstX = sx - cell[+0x8]   ; signed X anchor (miscflags)
 				//   dstY = sy - cell[+0x6]   ; signed Y anchor (rowoff)
@@ -667,7 +667,7 @@ void EEMEngine::displayClue(const byte *clueBlock) {
 		return;
 
 	// Snapshot BG so per-entry character pics don't stack.
-	Graphics::ManagedSurface bg(320, 200,
+	Graphics::ManagedSurface bg(kScreenWidth, kScreenHeight,
 		Graphics::PixelFormat::createFormatCLUT8());
 	bg.clear();
 	{
@@ -692,7 +692,7 @@ void EEMEngine::displayClue(const byte *clueBlock) {
 	// bubText1 != -1; otherwise falls back to partner 0 fields entirely.
 	// Partner 0 always uses field 0.
 	for (uint i = 0; i < number && !shouldQuit(); i++) {
-		g_system->copyRectToScreen(bg.getPixels(), bg.pitch, 0, 0, 320, 200);
+		g_system->copyRectToScreen(bg.getPixels(), bg.pitch, 0, 0, kScreenWidth, kScreenHeight);
 		const byte *c = clueBlock + 4 + i * 62;
 
 		// _DisplayClue @ 2404:0635-064b: _DoKDAnim(num) runs before the
@@ -702,10 +702,10 @@ void EEMEngine::displayClue(const byte *clueBlock) {
 			playKdAnim((uint16)kdAnimNum);
 			// _UpdateAnimations @ 172b:09c1 reactivates the wait anim.
 			g_system->copyRectToScreen(bg.getPixels(), bg.pitch,
-									   0, 0, 320, 200);
+									   0, 0, kScreenWidth, kScreenHeight);
 		}
 
-		const bool useP1 = (_partner == 1) &&
+		const bool useP1 = (_partner == kPartnerJenny) &&
 			(READ_LE_UINT16(c + 10) != 0xFFFF);
 		const uint partner = useP1 ? 1 : 0;
 		const uint16 textOff = READ_LE_UINT16(c + 8 + partner * 2);
@@ -726,7 +726,7 @@ void EEMEngine::displayClue(const byte *clueBlock) {
 		if (charPicId != 0 && charPicId != 0xFFFF) {
 			Picture charPic;
 			if (_picsArchive.getPicture(charPicId, charPic) &&
-				charX < 320 && charY < 200) {
+				charX < kScreenWidth && charY < kScreenHeight) {
 				blitMaskedToScreen(charPic, charX, charY);
 			}
 		}
@@ -746,20 +746,20 @@ void EEMEngine::displayClue(const byte *clueBlock) {
 			Graphics::Surface *screen = g_system->lockScreen();
 			if (!screen)
 				break;
-			Graphics::ManagedSurface scratch(320, 200,
+			Graphics::ManagedSurface scratch(kScreenWidth, kScreenHeight,
 				Graphics::PixelFormat::createFormatCLUT8());
 			scratch.simpleBlitFrom(*screen);
 			g_system->unlockScreen();
 
 			int textX = bubX;
 			int textY = bubY;
-			int textW = MIN<int>(320 - bubX, 200);
+			int textW = MIN<int>(kScreenWidth - bubX, 200);
 			int copyY = bubY;
 			int copyH = _font.getFontHeight() * 4 + 8;
 
 			if (haveBalloon) {
-				const int bw = MIN<int>(balloon.surface.w, 320 - bubX);
-				const int bh = MIN<int>(balloon.surface.h, 200 - bubY);
+				const int bw = MIN<int>(balloon.surface.w, kScreenWidth - bubX);
+				const int bh = MIN<int>(balloon.surface.h, kScreenHeight - bubY);
 				// _AddPicBackground: transparent colour = pic->miscflags >> 8.
 				const byte transp = (byte)(balloon.flags >> 8);
 				// _GetBalloon @ 172b:1d7d mirrors horizontally when
@@ -785,8 +785,8 @@ void EEMEngine::displayClue(const byte *clueBlock) {
 				copyH = bh;
 			} else {
 				// No balloon: clear band.
-				const Common::Rect band(0, bubY, 320,
-					MIN<int>(bubY + copyH, 200));
+				const Common::Rect band(0, bubY, kScreenWidth,
+					MIN<int>(bubY + copyH, kScreenHeight));
 				scratch.fillRect(band, 0);
 				copyY = bubY;
 			}
@@ -797,8 +797,8 @@ void EEMEngine::displayClue(const byte *clueBlock) {
 				MAX<int>(8, textW), text, 0);
 
 			g_system->copyRectToScreen(scratch.getBasePtr(0, copyY),
-				scratch.pitch, 0, copyY, 320,
-				MIN<int>(copyH, 200 - copyY));
+				scratch.pitch, 0, copyY, kScreenWidth,
+				MIN<int>(copyH, kScreenHeight - copyY));
 			g_system->updateScreen();
 		}
 
@@ -814,7 +814,7 @@ void EEMEngine::displayClue(const byte *clueBlock) {
 			const uint16 voiceJenny = READ_LE_UINT16(c + 0x18);
 			if (voiceJenny != 0 && voiceJenny != 0xFFFF) {
 				const uint16 voiceJake = READ_LE_UINT16(c + 0x1a);
-				const uint16 voice = (_partner == 0) ? voiceJake : voiceJenny;
+				const uint16 voice = (_partner == kPartnerJake) ? voiceJake : voiceJenny;
 				if (voice != 0 && voice != 0xFFFF)
 					_audio->spoolSound((uint)(voice - 1));
 			}
@@ -937,7 +937,7 @@ void EEMEngine::displayFloppyDialogRecords(const byte *rec, uint count,
 		return;
 
 	// Snapshot BG for between-bubble restores.
-	Graphics::ManagedSurface bg(320, 200,
+	Graphics::ManagedSurface bg(kScreenWidth, kScreenHeight,
 		Graphics::PixelFormat::createFormatCLUT8());
 	{
 		Graphics::Surface *screen = g_system->lockScreen();
@@ -1010,7 +1010,7 @@ void EEMEngine::displayFloppyDialogRecords(const byte *rec, uint count,
 			const uint32 noteAbs = notesBase + (uint32)idx * 7;
 			if (noteAbs + 6 > dsz)
 				continue;
-			const uint16 textOff = (_partner == 0)
+			const uint16 textOff = (_partner == kPartnerJake)
 				? READ_LE_UINT16(notes + idx * 7 + 2)
 				: READ_LE_UINT16(notes + idx * 7 + 4);
 			if (textOff >= dsz)
@@ -1074,7 +1074,7 @@ void EEMEngine::displayFloppyDialogRecords(const byte *rec, uint count,
 			const uint32 noteAbs = notesBase + (uint32)idx * 7;
 			if (noteAbs + 6 > dsz)
 				break;
-			const uint16 textOff = (_partner == 0)
+			const uint16 textOff = (_partner == kPartnerJake)
 				? READ_LE_UINT16(notes + idx * 7 + 2)
 				: READ_LE_UINT16(notes + idx * 7 + 4);
 			if (textOff >= dsz)
@@ -1088,7 +1088,7 @@ void EEMEngine::displayFloppyDialogRecords(const byte *rec, uint count,
 				parseString(raw, _playerName, _partner);
 
 			// Render this text page.
-			Graphics::ManagedSurface scratch(320, 200,
+			Graphics::ManagedSurface scratch(kScreenWidth, kScreenHeight,
 				Graphics::PixelFormat::createFormatCLUT8());
 			scratch.simpleBlitFrom(*bg.surfacePtr());
 
@@ -1163,7 +1163,7 @@ void EEMEngine::displayFloppyDialogRecords(const byte *rec, uint count,
 			}
 
 			g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
-									   0, 0, 320, 200);
+									   0, 0, kScreenWidth, kScreenHeight);
 			g_system->updateScreen();
 
 			if (waitNeeded) {
@@ -1230,7 +1230,7 @@ void EEMEngine::displayFloppyHotspotDialog(uint siteNum, uint hotIdx) {
 		return;
 	// Snapshot clean site BG and restore between main + continuation calls
 	// so each displayFloppyDialogRecords sees a bubble-free background.
-	Graphics::ManagedSurface siteBG(320, 200,
+	Graphics::ManagedSurface siteBG(kScreenWidth, kScreenHeight,
 		Graphics::PixelFormat::createFormatCLUT8());
 	{
 		Graphics::Surface *screen = g_system->lockScreen();
@@ -1263,7 +1263,7 @@ void EEMEngine::displayFloppyHotspotDialog(uint siteNum, uint hotIdx) {
 		return;
 	// Wipe main bubble so the continuation chain snapshots a clean BG.
 	g_system->copyRectToScreen(siteBG.getPixels(), siteBG.pitch,
-							   0, 0, 320, 200);
+							   0, 0, kScreenWidth, kScreenHeight);
 	g_system->updateScreen();
 	// _DisplayDialogContinuations_Floppy @ 1652:006c: lastIndicator=0
 	// means no indicator on the final continuation.
@@ -1272,7 +1272,7 @@ void EEMEngine::displayFloppyHotspotDialog(uint siteNum, uint hotIdx) {
 
 bool EEMEngine::areYouSure() {
 	Graphics::Surface *screen = g_system->lockScreen();
-	Graphics::ManagedSurface saved(320, 200,
+	Graphics::ManagedSurface saved(kScreenWidth, kScreenHeight,
 		Graphics::PixelFormat::createFormatCLUT8());
 	if (screen) {
 		saved.simpleBlitFrom(*screen);
@@ -1299,8 +1299,8 @@ bool EEMEngine::areYouSure() {
 	int noY = 0;
 
 	if (haveOriginalDialog) {
-		const int x = (320 - dialogPic.surface.w) / 2;
-		const int y = (200 - dialogPic.surface.h) / 2;
+		const int x = (kScreenWidth - dialogPic.surface.w) / 2;
+		const int y = (kScreenHeight - dialogPic.surface.h) / 2;
 		yesX = x + 0x0c;
 		yesY = y + 0x23;
 		noX = x + 0x60;
@@ -1315,7 +1315,7 @@ bool EEMEngine::areYouSure() {
 		noRect = Common::Rect(dlg.left + 100, dlg.top + 34,
 							  dlg.left + 160, dlg.top + 54);
 
-		Graphics::ManagedSurface scratch(320, 200,
+		Graphics::ManagedSurface scratch(kScreenWidth, kScreenHeight,
 			Graphics::PixelFormat::createFormatCLUT8());
 		scratch.simpleBlitFrom(saved);
 		scratch.fillRect(dlg, 0);
@@ -1323,14 +1323,14 @@ bool EEMEngine::areYouSure() {
 		_font.drawString(&scratch,
 			isSpanish() ? "Estas seguro que quieres salir?"
 						: "Are you sure you want to quit?",
-			dlg.left + 8, dlg.top + 8, 320, 0xF);
+			dlg.left + 8, dlg.top + 8, kScreenWidth, 0xF);
 		_font.drawString(&scratch,
 			isSpanish() ? "S - Si" : "Y - Yes",
-			dlg.left + 16, dlg.top + 36, 320, 0xF);
+			dlg.left + 16, dlg.top + 36, kScreenWidth, 0xF);
 		_font.drawString(&scratch, "N - No", dlg.left + 100,
-						 dlg.top + 36, 320, 0xF);
+						 dlg.top + 36, kScreenWidth, 0xF);
 		g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
-								   0, 0, 320, 200);
+								   0, 0, kScreenWidth, kScreenHeight);
 	} else {
 		return true;
 	}
@@ -1384,7 +1384,7 @@ bool EEMEngine::areYouSure() {
 		g_system->delayMillis(15);
 	}
 
-	g_system->copyRectToScreen(saved.getPixels(), saved.pitch, 0, 0, 320, 200);
+	g_system->copyRectToScreen(saved.getPixels(), saved.pitch, 0, 0, kScreenWidth, kScreenHeight);
 	g_system->updateScreen();
 	return result;
 }
diff --git a/engines/eem/eem.cpp b/engines/eem/eem.cpp
index 088f8a8d5d4..5118851df06 100644
--- a/engines/eem/eem.cpp
+++ b/engines/eem/eem.cpp
@@ -222,7 +222,7 @@ void EEMEngine::applyStartupTestOverrides() {
 
 Common::Error EEMEngine::run() {
 	// _SetMode13X @ 1000:0358 ??? VGA mode 13h.
-	initGraphics(320, 200);
+	initGraphics(kScreenWidth, kScreenHeight);
 
 	if (!openArchives())
 		return Common::Error(Common::kReadingFailed, "EEM archive open failed");
@@ -760,8 +760,8 @@ void EEMEngine::playAnm(const Common::Path &path, uint frameDelayMs,
 
 void EEMEngine::blitAt(const Picture &pic, int x, int y) {
 	// Clip against the 320x200 frame buffer.
-	const int w = MIN<int>(pic.surface.w, 320 - x);
-	const int h = MIN<int>(pic.surface.h, 200 - y);
+	const int w = MIN<int>(pic.surface.w, kScreenWidth - x);
+	const int h = MIN<int>(pic.surface.h, kScreenHeight - y);
 	if (w <= 0 || h <= 0)
 		return;
 	g_system->copyRectToScreen(pic.surface.getPixels(), pic.surface.pitch,
diff --git a/engines/eem/eem.h b/engines/eem/eem.h
index 8ba5a135353..8c06aa64cb9 100644
--- a/engines/eem/eem.h
+++ b/engines/eem/eem.h
@@ -104,6 +104,17 @@ enum Variant {
 	kVariantFloppy = 1,
 };
 
+/// `_Partner @ 29be:7918`. Selected at the partner-pick screen
+/// (_DoChoosePartner @ 1a35:0756) and persisted in the player profile.
+enum Partner {
+	kPartnerJake  = 0,
+	kPartnerJenny = 1,
+};
+
+/// VGA mode 13h dimensions (initGraphics(320, 200) in `EEMEngine::run`).
+constexpr int kScreenWidth  = 320;
+constexpr int kScreenHeight = 200;
+
 class EEMEngine : public Engine {
 public:
 	EEMEngine(OSystem *syst, const ADGameDescription *gameDesc);
diff --git a/engines/eem/graphics.cpp b/engines/eem/graphics.cpp
index 544e66c7f50..b80eaa85730 100644
--- a/engines/eem/graphics.cpp
+++ b/engines/eem/graphics.cpp
@@ -256,7 +256,7 @@ void EEMEngine::doHelp() {
 		Common::String text = parseString(Common::String(txt),
 										   _playerName, _partner);
 		balloonIdx = fitBalloonToText((uint16)balloonIdx, text) & 0x7F;
-		Graphics::ManagedSurface ms(320, 200,
+		Graphics::ManagedSurface ms(kScreenWidth, kScreenHeight,
 			Graphics::PixelFormat::createFormatCLUT8());
 		ms.clear();
 		{
@@ -284,7 +284,7 @@ void EEMEngine::doHelp() {
 		getBalloonInsets(balloonIdx, bx, by, bw);
 		_font.drawWordWrapped(&ms, 0x21 + bx, balloonY + by,
 							  MAX<int>(8, (int)bw), text, 0);
-		g_system->copyRectToScreen(ms.getPixels(), ms.pitch, 0, 0, 320, 200);
+		g_system->copyRectToScreen(ms.getPixels(), ms.pitch, 0, 0, kScreenWidth, kScreenHeight);
 		g_system->updateScreen();
 
 		while (!shouldQuit()) {
@@ -398,7 +398,7 @@ void EEMEngine::doHelp() {
 	//
 	// BG is the caller's CURRENT screen (site / PDA / gallery), not a cleared
 	// scratch.
-	Graphics::ManagedSurface ms(320, 200,
+	Graphics::ManagedSurface ms(kScreenWidth, kScreenHeight,
 		Graphics::PixelFormat::createFormatCLUT8());
 	ms.clear();
 	{
@@ -445,7 +445,7 @@ void EEMEngine::doHelp() {
 						  haveBalloon ? 0 : 0xF);
 
 	g_system->copyRectToScreen(ms.getPixels(), ms.pitch,
-							   0, 0, 320, 200);
+							   0, 0, kScreenWidth, kScreenHeight);
 	g_system->updateScreen();
 
 	// _DisplayHint @ 1560:0009 plays _SayKDDigital(soundnum) ??? a
@@ -476,7 +476,7 @@ void EEMEngine::doInterfaceHelp(uint num) {
 		   num, kHelpPics[num][0], kHelpPics[num][1]);
 
 	// Snapshot caller's screen once: each PIC overlays the same clean BG.
-	Graphics::ManagedSurface bg(320, 200,
+	Graphics::ManagedSurface bg(kScreenWidth, kScreenHeight,
 		Graphics::PixelFormat::createFormatCLUT8());
 	{
 		Graphics::Surface *cur = g_system->lockScreen();
@@ -501,14 +501,14 @@ void EEMEngine::doInterfaceHelp(uint num) {
 
 		// transBlitFrom transp = pic.flags >> 8 matches _Rect_Move_Mask param_10
 		// @ 1000:03fc. Explicit (0,0) destPos: no-arg overload stretches to fill.
-		Graphics::ManagedSurface scratch(320, 200,
+		Graphics::ManagedSurface scratch(kScreenWidth, kScreenHeight,
 			Graphics::PixelFormat::createFormatCLUT8());
 		scratch.simpleBlitFrom(bg);
 		const byte transp = (byte)(pic.flags >> 8);
 		scratch.transBlitFrom(pic.surface, Common::Point(0, 0),
 							  (uint32)transp);
 		g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
-								   0, 0, 320, 200);
+								   0, 0, kScreenWidth, kScreenHeight);
 		g_system->updateScreen();
 
 		bool escape = false;
@@ -548,8 +548,8 @@ void EEMEngine::doInterfaceHelp(uint num) {
 }
 
 void EEMEngine::setPartnerEraseBg(const Graphics::ManagedSurface *bg) {
-	if (bg && bg->w == 320 && bg->h == 200) {
-		_partnerEraseBg.create(320, 200,
+	if (bg && bg->w == kScreenWidth && bg->h == kScreenHeight) {
+		_partnerEraseBg.create(kScreenWidth, kScreenHeight,
 			Graphics::PixelFormat::createFormatCLUT8());
 		_partnerEraseBg.simpleBlitFrom(*bg);
 	} else {
diff --git a/engines/eem/site.cpp b/engines/eem/site.cpp
index b0b5a2e1226..d523b1e3f5b 100644
--- a/engines/eem/site.cpp
+++ b/engines/eem/site.cpp
@@ -987,7 +987,7 @@ bool SiteScreen::enterSiteAnim() {
 	Graphics::Surface *screen = g_system->lockScreen();
 	if (!screen)
 		return false;
-	Graphics::ManagedSurface bg(320, 200,
+	Graphics::ManagedSurface bg(kScreenWidth, kScreenHeight,
 		Graphics::PixelFormat::createFormatCLUT8());
 	bg.simpleBlitFrom(*screen);
 	g_system->unlockScreen();
@@ -995,10 +995,10 @@ bool SiteScreen::enterSiteAnim() {
 	// Phase 1 ??? skateboard scroll.
 	Animation skate;
 	if (_vm->getAni().loadAnimation(kSkateAni, skate) && !skate.empty()) {
-		// `iVar4 = 199 - sprite_h`, `uVar5 = 320 - sprite_w` (frame 0).
+		// `iVar4 = 199 - sprite_h`, `uVar5 = kScreenWidth - sprite_w` (frame 0).
 		const int spriteH = skate[0].surface.h;
 		const int spriteW = skate[0].surface.w;
-		int x = (320 - spriteW) & ~3;            // 4-px aligned (mode-X)
+		int x = (kScreenWidth - spriteW) & ~3;            // 4-px aligned (mode-X)
 		const int y = 199 - spriteH;
 		const byte transp = (byte)(skate[0].flags >> 8);
 		uint frameIdx = 0;
@@ -1007,12 +1007,12 @@ bool SiteScreen::enterSiteAnim() {
 		const int kFrameTicks = 0xc;    // original switches frame at 12 px
 
 		while (x + spriteW > 0 && !_vm->shouldQuit()) {
-			Graphics::ManagedSurface scratch(320, 200,
+			Graphics::ManagedSurface scratch(kScreenWidth, kScreenHeight,
 				Graphics::PixelFormat::createFormatCLUT8());
 			scratch.simpleBlitFrom(bg);
 			blitFrame(scratch, skate[frameIdx], x, y, transp);
 			g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
-									   0, 0, 320, 200);
+									   0, 0, kScreenWidth, kScreenHeight);
 			g_system->updateScreen();
 
 			Common::Event ev;
@@ -1048,12 +1048,12 @@ bool SiteScreen::enterSiteAnim() {
 			const int destX = -(int)(int16)fr.miscflags;
 			const int destY = kKDY - (int)(int16)fr.rowoff;
 
-			Graphics::ManagedSurface scratch(320, 200,
+			Graphics::ManagedSurface scratch(kScreenWidth, kScreenHeight,
 				Graphics::PixelFormat::createFormatCLUT8());
 			scratch.simpleBlitFrom(bg);
 			blitFrame(scratch, fr, destX, destY, transp);
 			g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
-									   0, 0, 320, 200);
+									   0, 0, kScreenWidth, kScreenHeight);
 			g_system->updateScreen();
 
 			Common::Event ev;
@@ -1267,7 +1267,7 @@ void SiteScreen::applyColorCycles() {
 }
 
 void SiteScreen::captureBgSnapshot() {
-	_bgSnapshot.create(320, 200, Graphics::PixelFormat::createFormatCLUT8());
+	_bgSnapshot.create(kScreenWidth, kScreenHeight, Graphics::PixelFormat::createFormatCLUT8());
 	Graphics::Surface *screen = g_system->lockScreen();
 	if (!screen) {
 		_snapshotSite = -1;
@@ -1278,10 +1278,10 @@ void SiteScreen::captureBgSnapshot() {
 }
 
 void SiteScreen::restoreBgSnapshot() {
-	if (_bgSnapshot.w != 320 || _bgSnapshot.h != 200)
+	if (_bgSnapshot.w != kScreenWidth || _bgSnapshot.h != kScreenHeight)
 		return;
 	g_system->copyRectToScreen(_bgSnapshot.getPixels(), _bgSnapshot.pitch,
-							   0, 0, 320, 200);
+							   0, 0, kScreenWidth, kScreenHeight);
 }
 
 void SiteScreen::renderPartner(uint siteNum, uint32 tickMs) {
@@ -1401,8 +1401,8 @@ void SiteScreen::renderBackground(uint siteNum) {
 		// `_Rect_Move(0, 0, h, ..., 0x42, 0x14, 48000, h, w)`.
 		const int x = 0x42;
 		const int y = 0x14;
-		const int w = MIN<int>(scene.surface.w, 320 - x);
-		const int h = MIN<int>(scene.surface.h, 200 - y);
+		const int w = MIN<int>(scene.surface.w, kScreenWidth - x);
+		const int h = MIN<int>(scene.surface.h, kScreenHeight - y);
 		if (w > 0 && h > 0)
 			g_system->copyRectToScreen(scene.surface.getPixels(),
 									   scene.surface.pitch, x, y, w, h);
@@ -1635,7 +1635,7 @@ void EEMEngine::playKdAnim(uint16 num) {
 	if (num >= ARRAYSIZE(kKdAnimTable))
 		return;
 
-	const uint partner = (_partner == 0) ? 0 : 1;
+	const uint partner = (_partner == kPartnerJake) ? 0 : 1;
 	const uint16 animId = kKdAnimTable[num][partner];
 	const int    px     = (int)kKdAnimTable[num][2 + partner];
 	const int    py     = (int)kKdAnimTable[num][4 + partner];
@@ -1657,9 +1657,9 @@ void EEMEngine::playKdAnim(uint16 num) {
 
 	// Erase-source: caller-stashed partner-less BG (via `setPartnerEraseBg`)
 	// or fall back to current screen (works for full-screen contexts).
-	Graphics::ManagedSurface bg(320, 200,
+	Graphics::ManagedSurface bg(kScreenWidth, kScreenHeight,
 		Graphics::PixelFormat::createFormatCLUT8());
-	if (_partnerEraseBg.w == 320 && _partnerEraseBg.h == 200) {
+	if (_partnerEraseBg.w == kScreenWidth && _partnerEraseBg.h == kScreenHeight) {
 		bg.simpleBlitFrom(_partnerEraseBg);
 	} else {
 		Graphics::Surface *screen = g_system->lockScreen();
@@ -1676,7 +1676,7 @@ void EEMEngine::playKdAnim(uint16 num) {
 		const Picture &fr = anim[frameIdx];
 		const byte transp = (byte)(fr.flags >> 8);
 
-		Graphics::ManagedSurface scratch(320, 200,
+		Graphics::ManagedSurface scratch(kScreenWidth, kScreenHeight,
 			Graphics::PixelFormat::createFormatCLUT8());
 		scratch.simpleBlitFrom(bg);
 		// Anchor-aware: kdAnim cells (0x03/0x04/0x0c/0x0d ...) have
@@ -1687,7 +1687,7 @@ void EEMEngine::playKdAnim(uint16 num) {
 		(void)transp;  // anchored blitter recomputes from p.flags
 		blitAnimFrameAnchored(scratch.surfacePtr(), fr, px, py);
 		g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
-								   0, 0, 320, 200);
+								   0, 0, kScreenWidth, kScreenHeight);
 		g_system->updateScreen();
 
 		// 100 ms per frame (~10 fps). Pump updateScreen inside the wait
@@ -1706,7 +1706,7 @@ void EEMEngine::playKdAnim(uint16 num) {
 		}
 	}
 
-	g_system->copyRectToScreen(bg.getPixels(), bg.pitch, 0, 0, 320, 200);
+	g_system->copyRectToScreen(bg.getPixels(), bg.pitch, 0, 0, kScreenWidth, kScreenHeight);
 	g_system->updateScreen();
 }
 } // End of namespace EEM
diff --git a/engines/eem/ui.cpp b/engines/eem/ui.cpp
index 2d36b72f204..507d27d0506 100644
--- a/engines/eem/ui.cpp
+++ b/engines/eem/ui.cpp
@@ -100,8 +100,8 @@ void swapColors(Graphics::ManagedSurface &dst,
 					   const Common::Rect &r, byte from, byte to) {
 	const int x1 = MAX<int>(0, r.left);
 	const int y1 = MAX<int>(0, r.top);
-	const int x2 = MIN<int>(320, r.right);
-	const int y2 = MIN<int>(200, r.bottom);
+	const int x2 = MIN<int>(kScreenWidth, r.right);
+	const int y2 = MIN<int>(kScreenHeight, r.bottom);
 	for (int y = y1; y < y2; y++) {
 		byte *row = (byte *)dst.getBasePtr(0, y);
 		for (int x = x1; x < x2; x++) {
@@ -157,8 +157,8 @@ void blitPdaPartner(Graphics::Surface *screen, DBDArchive &aniArchive,
 		blitAnimFrameAnchored(screen, *fr, spec.anchorX, spec.anchorY);
 }
 
-constexpr Common::Rect kEndingPrevPageRect(Common::Point(0, 0), 28, 200);
-constexpr Common::Rect kEndingNextPageRect(Common::Point(292, 0), 28, 200);
+constexpr Common::Rect kEndingPrevPageRect(Common::Point(0, 0), 28, kScreenHeight);
+constexpr Common::Rect kEndingNextPageRect(Common::Point(292, 0), 28, kScreenHeight);
 constexpr uint16 kFloppyEndingBackgroundPic = 0x8b;
 constexpr uint16 kFirstTryBadgePic = 0x205;
 constexpr Common::Point kFirstTryBadgePos(0x1e, 9);
@@ -292,7 +292,7 @@ int nextLiveSlot(const Common::Array<Common::Rect> &slotRects,
 
 void copyToScreen(Graphics::ManagedSurface &scratch) {
 	g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
-							   0, 0, 320, 200);
+							   0, 0, kScreenWidth, kScreenHeight);
 	g_system->updateScreen();
 }
 
@@ -357,13 +357,13 @@ void drawCaseBookTitle(Graphics::ManagedSurface &scratch, const EEMEngine *vm,
 		: Common::String::format(spanish ? "Lib. %u" : "Book %u", book);
 	const int titleW = vm->getFont().getStringWidth(title);
 	const int titleX = (0xba - titleW) / 2 + 0x3c;
-	vm->getFont().drawString(&scratch, title, titleX, 12, 320, 0xF);
+	vm->getFont().drawString(&scratch, title, titleX, 12, kScreenWidth, 0xF);
 }
 
 void drawNameEntryFrame(EEMEngine *vm, const Picture *bg, bool haveBG,
 						const Picture *peek, const Common::String &name,
 						const char *prompt) {
-	Graphics::ManagedSurface scratch(320, 200,
+	Graphics::ManagedSurface scratch(kScreenWidth, kScreenHeight,
 		Graphics::PixelFormat::createFormatCLUT8());
 	scratch.clear();
 	if (haveBG)
@@ -383,7 +383,7 @@ bool animateNameEntryPeek(EEMEngine *vm, const Picture *bg, bool haveBG,
 	for (int w = 1; w <= peek->surface.w; w++) {
 		if (pumpQuitEvents(vm))
 			return true;
-		Graphics::ManagedSurface scratch(320, 200,
+		Graphics::ManagedSurface scratch(kScreenWidth, kScreenHeight,
 			Graphics::PixelFormat::createFormatCLUT8());
 		scratch.clear();
 		if (haveBG)
@@ -404,7 +404,7 @@ bool animateProfilePickerReveal(EEMEngine *vm, const Picture *bg,
 	for (int h = 1; h <= reveal->surface.h; h++) {
 		if (pumpQuitEvents(vm))
 			return true;
-		Graphics::ManagedSurface scratch(320, 200,
+		Graphics::ManagedSurface scratch(kScreenWidth, kScreenHeight,
 			Graphics::PixelFormat::createFormatCLUT8());
 		scratch.clear();
 		if (haveBG)
@@ -451,7 +451,7 @@ void clampProfileScroll(int &selected, int &start, int count) {
 }
 
 void drawProfilePickerFrame(const ProfilePickerView &v) {
-	Graphics::ManagedSurface scratch(320, 200,
+	Graphics::ManagedSurface scratch(kScreenWidth, kScreenHeight,
 		Graphics::PixelFormat::createFormatCLUT8());
 	scratch.clear();
 	if (v.haveBG)
@@ -584,7 +584,7 @@ bool animateCaseSelectionReveal(EEMEngine *vm, const Picture *caseBg,
 	for (int i = 1; i <= steps; i++) {
 		if (pumpQuitEvents(vm))
 			return true;
-		Graphics::ManagedSurface scratch(320, 200,
+		Graphics::ManagedSurface scratch(kScreenWidth, kScreenHeight,
 			Graphics::PixelFormat::createFormatCLUT8());
 		scratch.clear();
 		if (haveCaseBg && caseBg)
@@ -610,7 +610,7 @@ bool animateCaseSelectionReveal(EEMEngine *vm, const Picture *caseBg,
 // Original colours 0x13 sel / 0x1B greyed / 0x5C default approximated
 // here as 0xF / 0x8 / 0x7 from site palette 0.
 void drawCaseSubmenu(const CaseSubmenuView &v) {
-	Graphics::ManagedSurface scratch(320, 200,
+	Graphics::ManagedSurface scratch(kScreenWidth, kScreenHeight,
 		Graphics::PixelFormat::createFormatCLUT8());
 	drawCaseBase(scratch, v.vm, v.caseBg, v.haveCaseBg,
 				 v.revealPic, v.haveRevealPic,
@@ -668,7 +668,7 @@ void drawCaseSubmenu(const CaseSubmenuView &v) {
 }
 
 void drawActionMenuFrame(const ActionMenuView &v) {
-	Graphics::ManagedSurface scratch(320, 200,
+	Graphics::ManagedSurface scratch(kScreenWidth, kScreenHeight,
 		Graphics::PixelFormat::createFormatCLUT8());
 	scratch.clear();
 	if (v.haveBg && v.bg)
@@ -946,7 +946,7 @@ void EEMEngine::doNewPlayer() {
 					_playerName = name;
 					memset(_mysteriesSolved, 0, sizeof(_mysteriesSolved));
 					_mystery.clear();
-					_partner = 0;
+					_partner = kPartnerJake;
 					// `_NewPlayer @ 1c33:0fa3`: DAT_2d5d_3f99 = 1 (Junior).
 					_chainStage = 1;
 					saveProfile(name);
@@ -1109,7 +1109,7 @@ int EEMEngine::doShowEnding(uint num, bool firstPage) {
 			uint16 y1 = 0;
 			uint16 x2 = 0;
 			const char *raw = nullptr;
-			Graphics::ManagedSurface scratch(320, 200,
+			Graphics::ManagedSurface scratch(kScreenWidth, kScreenHeight,
 				Graphics::PixelFormat::createFormatCLUT8());
 			scratch.clear();
 
@@ -1170,13 +1170,13 @@ int EEMEngine::doShowEnding(uint num, bool firstPage) {
 			// TINY.FNT + color 0 (asm 1df2:04cf ??? not 0xF as Ghidra shows).
 			const EEMFont &renderFont = haveTinyFont ? tinyFont : _font;
 			if (renderFont.isLoaded() && x2 > x1) {
-				const int textW = MIN<int>((int)x2 - (int)x1, 320 - (int)x1);
+				const int textW = MIN<int>((int)x2 - (int)x1, kScreenWidth - (int)x1);
 				renderFont.drawWordWrapped(&scratch, (int)x1, (int)y1,
 										   textW, text, 0);
 			}
 
 			g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
-									   0, 0, 320, 200);
+									   0, 0, kScreenWidth, kScreenHeight);
 			g_system->updateScreen();
 			dirty = false;
 		}
@@ -1388,20 +1388,20 @@ void EEMEngine::doSetup() {
 			// Partner toggle [0]. Direct Jake/Jenny label clicks are a
 			// ScummVM-only fallback.
 			if (kPartnerBtn.contains(mx, my)) {
-				_partner = _partner == 0 ? 1 : 0;
+				_partner = _partner == kPartnerJake ? kPartnerJenny : kPartnerJake;
 				dirty = true;
 				continue;
 			}
 			if (kKid1Rect.contains(mx, my)) {
-				if (_partner != 0) {
-					_partner = 0;
+				if (_partner != kPartnerJake) {
+					_partner = kPartnerJake;
 					dirty = true;
 				}
 				continue;
 			}
 			if (kKid2Rect.contains(mx, my)) {
-				if (_partner != 1) {
-					_partner = 1;
+				if (_partner != kPartnerJenny) {
+					_partner = kPartnerJenny;
 					dirty = true;
 				}
 				continue;
@@ -1531,7 +1531,7 @@ void EEMEngine::doSetup() {
 }
 
 void EEMEngine::setupDrawScreen() {
-	Graphics::ManagedSurface scratch(320, 200,
+	Graphics::ManagedSurface scratch(kScreenWidth, kScreenHeight,
 		Graphics::PixelFormat::createFormatCLUT8());
 	scratch.clear();
 	Picture bg;
@@ -1542,16 +1542,16 @@ void EEMEngine::setupDrawScreen() {
 	const byte kBright = 0x15;
 	const byte kDim    = 0x00;
 	swapColors(scratch, kSetupKid1Rect, kKey,
-			   _partner == 0 ? kBright : kDim);
+			   _partner == kPartnerJake ? kBright : kDim);
 	swapColors(scratch, kSetupKid2Rect, kKey,
-			   _partner == 1 ? kBright : kDim);
+			   _partner == kPartnerJenny ? kBright : kDim);
 	swapColors(scratch, kSetupSoundOnRect,  kKey,
 			   _voiceOn ? kBright : kDim);
 	swapColors(scratch, kSetupSoundOffRect, kKey,
 			   _voiceOn ? kDim : kBright);
 
 	g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
-							   0, 0, 320, 200);
+							   0, 0, kScreenWidth, kScreenHeight);
 	g_system->updateScreen();
 }
 
@@ -1564,7 +1564,7 @@ Common::KeyCode EEMEngine::setupShowFullscreenPic(uint16 picId, bool transparent
 		warning("doSetup: PIC %u missing", (uint)picId);
 		return Common::KEYCODE_INVALID;
 	}
-	Graphics::ManagedSurface scratch(320, 200,
+	Graphics::ManagedSurface scratch(kScreenWidth, kScreenHeight,
 		Graphics::PixelFormat::createFormatCLUT8());
 	if (transparent) {
 		Graphics::Surface *cur = g_system->lockScreen();
@@ -1581,7 +1581,7 @@ Common::KeyCode EEMEngine::setupShowFullscreenPic(uint16 picId, bool transparent
 		scratch.simpleBlitFrom(pic.surface);
 	}
 	g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
-							   0, 0, 320, 200);
+							   0, 0, kScreenWidth, kScreenHeight);
 	g_system->updateScreen();
 	while (!shouldQuit()) {
 		Common::Event ev;
@@ -1866,7 +1866,7 @@ void EEMEngine::doCaseSelection() {
 		_picsArchive.getPicture(kCaseSelectionRevealPic, revealPic);
 
 	// `_CaseSelection @ 1c33:0a87` greeter ANI 0x15 (Jake) / 0x16 (Jenny).
-	const uint kKdAniId = (_partner == 0) ? 0x15 : 0x16;
+	const uint kKdAniId = (_partner == kPartnerJake) ? 0x15 : 0x16;
 	Animation kdAnim;
 	const bool haveKdAnim = _aniArchive.loadAnimation(kKdAniId, kdAnim)
 							 && !kdAnim.empty();
@@ -2285,7 +2285,7 @@ void EEMEngine::drawNotebookFrame(int &page) {
 	// from `_DoNotebook @ 161e:0500`.
 	const Common::Rect kNotebookRect(78, 12, 288, 152);
 
-	Graphics::ManagedSurface scratch(320, 200,
+	Graphics::ManagedSurface scratch(kScreenWidth, kScreenHeight,
 		Graphics::PixelFormat::createFormatCLUT8());
 	scratch.clear();
 
@@ -2384,10 +2384,10 @@ void EEMEngine::drawNotebookFrame(int &page) {
 	// Page indicator only (original has no points display).
 	_font.drawString(&scratch, Common::String::format("p%d/%d",
 							   page + 1, (int)pageStarts.size()),
-					 270, 4, 320, 0x5C);
+					 270, 4, kScreenWidth, 0x5C);
 
 	g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
-							   0, 0, 320, 200);
+							   0, 0, kScreenWidth, kScreenHeight);
 	g_system->updateScreen();
 
 	// Publish slot info to `doNotebook`'s click handler.
@@ -2592,7 +2592,7 @@ bool EEMEngine::moreInfo(const byte *gd, uint suspectIdx,
 	bool isFirstShow = true;
 
 	while (!back && !shouldQuit()) {
-		Graphics::ManagedSurface ms(320, 200,
+		Graphics::ManagedSurface ms(kScreenWidth, kScreenHeight,
 			Graphics::PixelFormat::createFormatCLUT8());
 		ms.clear();
 		if (haveBg)
@@ -2688,7 +2688,7 @@ bool EEMEngine::moreInfo(const byte *gd, uint suspectIdx,
 				rx, ry + rh + 2, MAX<int>(8, rw), 0x3C);
 		}
 		g_system->copyRectToScreen(ms.getPixels(), ms.pitch,
-			0, 0, 320, 200);
+			0, 0, kScreenWidth, kScreenHeight);
 		g_system->updateScreen();
 
 		// Drain the LBUTTONDOWN that opened MoreInfo (first page only).
@@ -2833,7 +2833,7 @@ void EEMEngine::drawGalleryFrame(const byte *gd, uint8 numSuspects,
 	Picture galBg;
 	const bool haveBg = _picsArchive.getPicture(0x3f, galBg);
 
-	Graphics::ManagedSurface scratch(320, 200,
+	Graphics::ManagedSurface scratch(kScreenWidth, kScreenHeight,
 		Graphics::PixelFormat::createFormatCLUT8());
 	scratch.clear();
 
@@ -2882,8 +2882,8 @@ void EEMEngine::drawGalleryFrame(const byte *gd, uint8 numSuspects,
 
 			const int placeX = s.x;
 			const int placeY = s.y + (0x48 - portrait.surface.h);
-			const int w = MIN<int>(portrait.surface.w, 320 - placeX);
-			const int h = MIN<int>(portrait.surface.h, 200 - placeY);
+			const int w = MIN<int>(portrait.surface.w, kScreenWidth - placeX);
+			const int h = MIN<int>(portrait.surface.h, kScreenHeight - placeY);
 			if (w <= 0 || h <= 0)
 				continue;
 			scratch.transBlitFrom(portrait.surface,
@@ -2898,7 +2898,7 @@ void EEMEngine::drawGalleryFrame(const byte *gd, uint8 numSuspects,
 			const int phH = 0x48;
 			const int phX = s.x;
 			const int phY = s.y;
-			if (phX + phW <= 320 && phY + phH <= 200) {
+			if (phX + phW <= kScreenWidth && phY + phH <= kScreenHeight) {
 				scratch.fillRect(Common::Rect(phX, phY,
 					phX + phW, phY + phH), 0x20);
 				scratch.frameRect(Common::Rect(phX, phY,
@@ -2913,7 +2913,7 @@ void EEMEngine::drawGalleryFrame(const byte *gd, uint8 numSuspects,
 	}
 
 	g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
-							   0, 0, 320, 200);
+							   0, 0, kScreenWidth, kScreenHeight);
 	g_system->updateScreen();
 }
 
@@ -3032,7 +3032,7 @@ void EEMEngine::doBigMap() {
 		bool returnToOverview = false;
 
 		// `SmallMapButtons[4]` @ 20fe:156c ??? return to overview.
-		const Common::Rect kBigMapReturnRect(252, 43, 320, 200);
+		const Common::Rect kBigMapReturnRect(252, 43, kScreenWidth, kScreenHeight);
 		const Common::Rect kArrowYUp(237, 2, 247, 11);
 		const Common::Rect kArrowYDown(237, 163, 247, 172);
 		const Common::Rect kArrowXLeft(2, 175, 12, 185);
@@ -3201,7 +3201,7 @@ void EEMEngine::drawBigMapOverview(uint32 elapsedMs) {
 	// PIC 0x42 + per-site Done/Crime/Site marker (`_DrawBigMapButtons @
 	// 20fe:0877`) + partner idle at (0xfd, 0x50). Idle ANI: Jake=0x14,
 	// Jenny=0x12. elapsedMs anchors unfold???wait timeline.
-	Graphics::ManagedSurface scratch(320, 200,
+	Graphics::ManagedSurface scratch(kScreenWidth, kScreenHeight,
 		Graphics::PixelFormat::createFormatCLUT8());
 	scratch.clear();
 
@@ -3266,7 +3266,7 @@ void EEMEngine::drawBigMapOverview(uint32 elapsedMs) {
 
 	// Partner idle at (0xfd, 0x50). `_DoBigMap @ 20fe:0a47` always passes
 	// script 0x14 (count-up) to `_NewAnimation` regardless of partner.
-	const uint kMapAniId = (_partner == 0) ? 0x14 : 0x12;
+	const uint kMapAniId = (_partner == kPartnerJake) ? 0x14 : 0x12;
 	Animation mapAnim;
 	if (_aniArchive.loadAnimation(kMapAniId, mapAnim) && !mapAnim.empty()) {
 		const uint frameIdx = bigMapPartnerFrameAtTick((uint)mapAnim.size(),
@@ -3277,7 +3277,7 @@ void EEMEngine::drawBigMapOverview(uint32 elapsedMs) {
 	}
 
 	g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
-							   0, 0, 320, 200);
+							   0, 0, kScreenWidth, kScreenHeight);
 	g_system->updateScreen();
 }
 
@@ -3293,7 +3293,7 @@ void EEMEngine::drawBigMapDetail(int scrollX, int scrollY,
 	const int kMapWinX = 2;
 	const int kMapWinY = 2;
 
-	Graphics::ManagedSurface scratch(320, 200,
+	Graphics::ManagedSurface scratch(kScreenWidth, kScreenHeight,
 		Graphics::PixelFormat::createFormatCLUT8());
 	scratch.clear();
 
@@ -3347,7 +3347,7 @@ void EEMEngine::drawBigMapDetail(int scrollX, int scrollY,
 	}
 
 	// Always script 0x13 (`_NewAnimation @ _DoBigMap 20fe:0a47`).
-	const uint kDetailAniId = (_partner == 0) ? 0x13 : 0x11;
+	const uint kDetailAniId = (_partner == kPartnerJake) ? 0x13 : 0x11;
 	Animation detailAnim;
 	if (_aniArchive.loadAnimation(kDetailAniId, detailAnim) &&
 		!detailAnim.empty()) {
@@ -3358,7 +3358,7 @@ void EEMEngine::drawBigMapDetail(int scrollX, int scrollY,
 	}
 
 	g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
-							   0, 0, 320, 200);
+							   0, 0, kScreenWidth, kScreenHeight);
 	g_system->updateScreen();
 }
 
@@ -3420,7 +3420,7 @@ void EEMEngine::accuseRebuildPagination(const AccuseNotesCtx &ctx) {
 }
 
 void EEMEngine::accuseDrawScreen(const AccuseNotesCtx &ctx) {
-	Graphics::ManagedSurface scratch(320, 200,
+	Graphics::ManagedSurface scratch(kScreenWidth, kScreenHeight,
 		Graphics::PixelFormat::createFormatCLUT8());
 	scratch.clear();
 	if (ctx.haveBg)
@@ -3489,7 +3489,7 @@ void EEMEngine::accuseDrawScreen(const AccuseNotesCtx &ctx) {
 	}
 
 	g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
-							   0, 0, 320, 200);
+							   0, 0, kScreenWidth, kScreenHeight);
 	g_system->updateScreen();
 }
 
@@ -3747,7 +3747,7 @@ void EEMEngine::doAccuse() {
 								_playerName, _partner);
 	}
 	if (!entryText.empty()) {
-		Graphics::ManagedSurface ms(320, 200,
+		Graphics::ManagedSurface ms(kScreenWidth, kScreenHeight,
 			Graphics::PixelFormat::createFormatCLUT8());
 		ms.clear();
 		Graphics::Surface *cur = g_system->lockScreen();
@@ -3781,7 +3781,7 @@ void EEMEngine::doAccuse() {
 								  tw, entryText, haveBalloon ? 0 : 0xF);
 		}
 		g_system->copyRectToScreen(ms.getPixels(), ms.pitch,
-								   0, 0, 320, 200);
+								   0, 0, kScreenWidth, kScreenHeight);
 		g_system->updateScreen();
 		if (_audio)
 			_audio->sayKDDigital(entryKdIdx, entryKDSpeak, _partner);
@@ -3843,7 +3843,7 @@ void EEMEngine::doAccuse() {
 
 		// Balloon overlay (`_GetKDTextBalloon` + `_GetBalloon` +
 		// `_AddPicBackground` + `_WordWrap` @ 1df2:0c8d-0cd1).
-		Graphics::ManagedSurface ms(320, 200,
+		Graphics::ManagedSurface ms(kScreenWidth, kScreenHeight,
 			Graphics::PixelFormat::createFormatCLUT8());
 		ms.clear();
 		Graphics::Surface *cur = g_system->lockScreen();
@@ -3880,7 +3880,7 @@ void EEMEngine::doAccuse() {
 								  haveBalloon ? 0 : 0xF);
 		}
 		g_system->copyRectToScreen(ms.getPixels(), ms.pitch,
-								   0, 0, 320, 200);
+								   0, 0, kScreenWidth, kScreenHeight);
 		g_system->updateScreen();
 
 		// `_SayKDDigital(3)` @ 1df2:0cd9.
@@ -3938,7 +3938,7 @@ void EEMEngine::doAccuse() {
 				drawAccuseGallery(num, gd, /* highlighted= */ -1,
 								  slotRects, slotSuspect);
 
-				Graphics::ManagedSurface ms(320, 200,
+				Graphics::ManagedSurface ms(kScreenWidth, kScreenHeight,
 					Graphics::PixelFormat::createFormatCLUT8());
 				ms.clear();
 				{
@@ -3969,7 +3969,7 @@ void EEMEngine::doAccuse() {
 										  haveBalloon ? 0 : 0xF);
 				}
 				g_system->copyRectToScreen(ms.getPixels(), ms.pitch,
-					0, 0, 320, 200);
+					0, 0, kScreenWidth, kScreenHeight);
 				g_system->updateScreen();
 				if (_audio)
 					_audio->sayKDDigital(kdIdx, 4, _partner);
@@ -4136,7 +4136,7 @@ void EEMEngine::doAccuse() {
 		if (bindx < 8) {
 			const int bw = haveBalloon ? balloon.surface.w : 0;
 			const int bh = haveBalloon ? balloon.surface.h : 0;
-			balloonX = (320 - bw) / 2;
+			balloonX = (kScreenWidth - bw) / 2;
 			if (bh < 0x5a) {
 				balloonY = (0x5a - bh) / 2;
 			} else {
@@ -4150,7 +4150,7 @@ void EEMEngine::doAccuse() {
 		}
 
 		// `base` = BG + suspect + partner. Survives both balloon phases.
-		Graphics::ManagedSurface base(320, 200,
+		Graphics::ManagedSurface base(kScreenWidth, kScreenHeight,
 			Graphics::PixelFormat::createFormatCLUT8());
 		base.clear();
 		if (haveAlibiBg)
@@ -4165,7 +4165,7 @@ void EEMEngine::doAccuse() {
 		// @ 1df2:0c30`). Drawn after suspect.
 
 		// scratch = base + alibi balloon/text + partner.
-		Graphics::ManagedSurface scratch(320, 200,
+		Graphics::ManagedSurface scratch(kScreenWidth, kScreenHeight,
 			Graphics::PixelFormat::createFormatCLUT8());
 		scratch.simpleBlitFrom(base);
 		if (haveBalloon) {
@@ -4216,7 +4216,7 @@ void EEMEngine::doAccuse() {
 		// Suspect voice. talk = partner==0 ? gd[+0x6] : gd[+0x0] (1df2:0252).
 		// 1-based, so SpoolSound(talk-1).
 		g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
-								   0, 0, 320, 200);
+								   0, 0, kScreenWidth, kScreenHeight);
 		g_system->updateScreen();
 		if (_audio && gd) {
 			const uint16 alibiVoice =
@@ -4224,7 +4224,7 @@ void EEMEngine::doAccuse() {
 			const uint16 jakeVoice =
 				READ_LE_UINT16(gd + (uint)picked * 0x46 + 0x06);
 			const uint16 talk =
-				(_partner == 0) ? jakeVoice : alibiVoice;
+				(_partner == kPartnerJake) ? jakeVoice : alibiVoice;
 			if (talk != 0)
 				_audio->spoolSound((uint)(talk - 1));
 		}
@@ -4273,7 +4273,7 @@ void EEMEngine::doAccuse() {
 				blitPdaPartner(scratch, _aniArchive, _partner,
 							   kPdaGalleryPartner, g_system->getMillis());
 				g_system->copyRectToScreen(scratch.getPixels(),
-					scratch.pitch, 0, 0, 320, 200);
+					scratch.pitch, 0, 0, kScreenWidth, kScreenHeight);
 				g_system->updateScreen();
 				if (_audio)
 					_audio->sayKDDigital(reactIdx, 5, _partner);
@@ -4338,7 +4338,7 @@ void EEMEngine::doAccuse() {
 		// (0x42, 0x14), palette = sitenum+1 = 6.
 		Graphics::Surface *blk = g_system->lockScreen();
 		if (blk) {
-			memset(blk->getPixels(), 0, 320 * 200);
+			memset(blk->getPixels(), 0, kScreenWidth * kScreenHeight);
 			g_system->unlockScreen();
 		}
 		setSitePalette(6); // sitenum + 1 (`_GetPalette`).
@@ -4351,8 +4351,8 @@ void EEMEngine::doAccuse() {
 		if (5 < _sitesArchive.size() &&
 			_sitesArchive.loadEntry(5, scene)) {
 			const int sx = 0x42, sy = 0x14;
-			const int sw = MIN<int>(scene.surface.w, 320 - sx);
-			const int sh = MIN<int>(scene.surface.h, 200 - sy);
+			const int sw = MIN<int>(scene.surface.w, kScreenWidth - sx);
+			const int sh = MIN<int>(scene.surface.h, kScreenHeight - sy);
 			if (sw > 0 && sh > 0)
 				g_system->copyRectToScreen(scene.surface.getPixels(),
 										   scene.surface.pitch, sx, sy,
@@ -4428,7 +4428,7 @@ void EEMEngine::floppyKDHint(uint kdSlot, const byte *kdIdx,
 	Common::String text =
 		parseString(Common::String(txt), _playerName, _partner);
 	balloonIdx = fitBalloonToText((uint16)balloonIdx, text) & 0x7F;
-	Graphics::ManagedSurface ms(320, 200,
+	Graphics::ManagedSurface ms(kScreenWidth, kScreenHeight,
 		Graphics::PixelFormat::createFormatCLUT8());
 	Graphics::Surface *cur = g_system->lockScreen();
 	if (cur) {
@@ -4453,7 +4453,7 @@ void EEMEngine::floppyKDHint(uint kdSlot, const byte *kdIdx,
 	getBalloonInsets(balloonIdx, bx, by, bw);
 	_font.drawWordWrapped(&ms, 0x21 + bx, balloonY + by,
 						  MAX<int>(8, (int)bw), text, 0);
-	g_system->copyRectToScreen(ms.getPixels(), ms.pitch, 0, 0, 320, 200);
+	g_system->copyRectToScreen(ms.getPixels(), ms.pitch, 0, 0, kScreenWidth, kScreenHeight);
 	g_system->updateScreen();
 	// Wait for click.
 	while (!shouldQuit()) {
@@ -4480,7 +4480,7 @@ void EEMEngine::accuseDrawGallery(int highlighted,
 								  Common::Array<int> &suspects, uint8 num,
 								  bool haveAccuseBg,
 								  const Picture &accuseBg) {
-	Graphics::ManagedSurface scratch(320, 200,
+	Graphics::ManagedSurface scratch(kScreenWidth, kScreenHeight,
 		Graphics::PixelFormat::createFormatCLUT8());
 	scratch.clear();
 	if (haveAccuseBg)
@@ -4526,7 +4526,7 @@ void EEMEngine::accuseDrawGallery(int highlighted,
 	}
 
 	g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
-							   0, 0, 320, 200);
+							   0, 0, kScreenWidth, kScreenHeight);
 	g_system->updateScreen();
 }
 
@@ -4687,7 +4687,7 @@ void EEMEngine::doAccuseFloppy() {
 		{
 			Graphics::Surface *blk = g_system->lockScreen();
 			if (blk) {
-				memset(blk->getPixels(), 0, 320 * 200);
+				memset(blk->getPixels(), 0, kScreenWidth * kScreenHeight);
 				g_system->unlockScreen();
 			}
 			setSitePalette(6);
@@ -4701,8 +4701,8 @@ void EEMEngine::doAccuseFloppy() {
 			if (5 < _sitesArchive.size() &&
 				_sitesArchive.loadEntry(5, scene)) {
 				const int sx = 0x42, sy = 0x14;
-				const int sw = MIN<int>(scene.surface.w, 320 - sx);
-				const int sh = MIN<int>(scene.surface.h, 200 - sy);
+				const int sw = MIN<int>(scene.surface.w, kScreenWidth - sx);
+				const int sh = MIN<int>(scene.surface.h, kScreenHeight - sy);
 				if (sw > 0 && sh > 0)
 					g_system->copyRectToScreen(scene.surface.getPixels(),
 						scene.surface.pitch, sx, sy, sw, sh);
@@ -4867,7 +4867,7 @@ void EEMEngine::doAccuseFloppy() {
 	const bool flipBalloon = (balloonRaw & 0x80) != 0;
 
 	// Compose alibi screen.
-	Graphics::ManagedSurface scene(320, 200,
+	Graphics::ManagedSurface scene(kScreenWidth, kScreenHeight,
 		Graphics::PixelFormat::createFormatCLUT8());
 	scene.clear();
 	if (haveAlibiBg)
@@ -4885,7 +4885,7 @@ void EEMEngine::doAccuseFloppy() {
 	int balloonX = 0x21;
 	int balloonY = 1;
 	if (haveBalloon) {
-		balloonX = (320 - balloon.surface.w) / 2;
+		balloonX = (kScreenWidth - balloon.surface.w) / 2;
 		balloonY = (0x5a - balloon.surface.h) / 2;
 		if (balloonX < 0)
 			balloonX = 0;
@@ -4906,7 +4906,7 @@ void EEMEngine::doAccuseFloppy() {
 	// Stamp partner resting frame before KD reaction snapshots screen.
 	blitPdaPartner(scene, _aniArchive, _partner, kPdaGalleryPartner,
 				   g_system->getMillis());
-	g_system->copyRectToScreen(scene.getPixels(), scene.pitch, 0, 0, 320, 200);
+	g_system->copyRectToScreen(scene.getPixels(), scene.pitch, 0, 0, kScreenWidth, kScreenHeight);
 	g_system->updateScreen();
 
 	// No per-suspect VOC ??? alibi table @ 2608:0c5e is for post-win
@@ -4949,7 +4949,7 @@ void EEMEngine::drawAccuseGallery(uint8 numSuspects, const byte *gd,
 	Picture accuseBg;
 	const bool haveAccuseBg = _picsArchive.getPicture(0x3f, accuseBg);
 
-	Graphics::ManagedSurface scratch(320, 200,
+	Graphics::ManagedSurface scratch(kScreenWidth, kScreenHeight,
 		Graphics::PixelFormat::createFormatCLUT8());
 	scratch.clear();
 	if (haveAccuseBg)
@@ -4981,8 +4981,8 @@ void EEMEngine::drawAccuseGallery(uint8 numSuspects, const byte *gd,
 
 		const int placeX = s.x;
 		const int placeY = s.y + (0x48 - portrait.surface.h);
-		const int w = MIN<int>(portrait.surface.w, 320 - placeX);
-		const int h = MIN<int>(portrait.surface.h, 200 - placeY);
+		const int w = MIN<int>(portrait.surface.w, kScreenWidth - placeX);
+		const int h = MIN<int>(portrait.surface.h, kScreenHeight - placeY);
 		if (w <= 0 || h <= 0)
 			continue;
 		scratch.transBlitFrom(portrait.surface,
@@ -5002,7 +5002,7 @@ void EEMEngine::drawAccuseGallery(uint8 numSuspects, const byte *gd,
 	}
 
 	g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
-							   0, 0, 320, 200);
+							   0, 0, kScreenWidth, kScreenHeight);
 	g_system->updateScreen();
 }
 


Commit: 9cd879dd5cd34cd252e7551b92553f9ce05290df
    https://github.com/scummvm/scummvm/commit/9cd879dd5cd34cd252e7551b92553f9ce05290df
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:21:14+02:00

Commit Message:
EEM: removed useless usage of static

Changed paths:
    engines/eem/audio.cpp
    engines/eem/eem.cpp
    engines/eem/graphics.cpp
    engines/eem/metaengine.cpp
    engines/eem/music.cpp
    engines/eem/site.cpp


diff --git a/engines/eem/audio.cpp b/engines/eem/audio.cpp
index 3954fbb02f9..10df7170c3c 100644
--- a/engines/eem/audio.cpp
+++ b/engines/eem/audio.cpp
@@ -186,7 +186,7 @@ void AudioPlayer::playPcmBuffer(byte *pcm, uint32 size, uint sampleRate,
 // Filename FAR-ptr arrays at 2608:0f0e (Jake) and 2608:0f76 (Jenny), each
 // 26 * FAR-ptr to a NUL-terminated `*.voc` name. Slots align across partners,
 // e.g. 12 = PHONESL.VOC, 20 = partner intro, 25 = THUNDER.VOC.
-static const char *const kFloppyJakeVoiceTable[26] = {
+const char *const kFloppyJakeVoiceTable[26] = {
 	"DING.VOC",       "M-0083SL.VOC", "M-0085SL.VOC", "NEWSCAN.VOC",
 	"M-0089SL.VOC",   "M-0091SL.VOC", "M-0092SL.VOC", "NEWSSHRT.VOC",
 	"M-0096SL.VOC",   "M-0102SL.VOC", "M-0104SL.VOC", "M-0107SL.VOC",
@@ -195,7 +195,7 @@ static const char *const kFloppyJakeVoiceTable[26] = {
 	"M-0113SL.VOC",   "B-0006SL.VOC", "B-0003SL.VOC", "B-0004SL.VOC",
 	"M-0163SL.VOC",   "THUNDER.VOC",
 };
-static const char *const kFloppyJennyVoiceTable[26] = {
+const char *const kFloppyJennyVoiceTable[26] = {
 	"DING.VOC",       "F-0194SL.VOC", "F-0191SL.VOC", "NEWSCAN.VOC",
 	"F-0187SL.VOC",   "F-0184SL.VOC", "F-0181SL.VOC", "NEWSSHRT.VOC",
 	"F-0177SL.VOC",   "F-0170SL.VOC", "F-0168SL.VOC", "F-0166SL.VOC",
diff --git a/engines/eem/eem.cpp b/engines/eem/eem.cpp
index 5118851df06..9dfe48519f7 100644
--- a/engines/eem/eem.cpp
+++ b/engines/eem/eem.cpp
@@ -96,7 +96,7 @@ const byte kCursorInteractivePalette[] = {
 	0xFF, 0xFF, 0xFF
 };
 
-static void fadeCurrentPaletteToBlack(uint delayMs = 8) {
+void fadeCurrentPaletteToBlack(uint delayMs = 8) {
 	byte start[kPalSize];
 	byte stepPal[kPalSize];
 	g_system->getPaletteManager()->grabPalette(start, 0, 256);
@@ -111,7 +111,7 @@ static void fadeCurrentPaletteToBlack(uint delayMs = 8) {
 	}
 }
 
-static void fadePaletteFromBlack(const byte *target, uint delayMs = 8) {
+void fadePaletteFromBlack(const byte *target, uint delayMs = 8) {
 	byte stepPal[kPalSize];
 
 	for (uint step = 1; step <= 16; step++) {
@@ -124,7 +124,7 @@ static void fadePaletteFromBlack(const byte *target, uint delayMs = 8) {
 	}
 }
 
-static void setInteractiveCursorPalette(const Picture &cursor, byte transparent) {
+void setInteractiveCursorPalette(const Picture &cursor, byte transparent) {
 	byte palette[kPalSize];
 	bool used[256];
 	memset(used, 0, sizeof(used));
@@ -170,7 +170,7 @@ static void setInteractiveCursorPalette(const Picture &cursor, byte transparent)
 	CursorMan.replaceCursorPalette(palette, 0, 256);
 }
 
-static void installMouseCursor(DBDArchive &pics, bool interactive) {
+void installMouseCursor(DBDArchive &pics, bool interactive) {
 	Picture cursor;
 	if (pics.getPicture(kPicMousePointer, cursor) && !cursor.surface.empty()) {
 		const byte transparent = (byte)(cursor.flags >> 8);
@@ -810,7 +810,7 @@ void EEMEngine::waitForInput(uint32 maxMs) {
 //   fpal[start] = saved
 // If `show`, upload `end - start` entries (fpal[start..end-1]) ??? note that
 // fpal[end] is rotated in memory but intentionally not uploaded each tick.
-static void openColorCycle(byte *fpal, uint8 start, uint8 end, bool show) {
+void openColorCycle(byte *fpal, uint8 start, uint8 end, bool show) {
 	if (end <= start)
 		return;
 	const byte savedR = fpal[end * 3 + 0];
diff --git a/engines/eem/graphics.cpp b/engines/eem/graphics.cpp
index b80eaa85730..8f8441b0c41 100644
--- a/engines/eem/graphics.cpp
+++ b/engines/eem/graphics.cpp
@@ -127,7 +127,7 @@ uint getBalloonLineCapacity(uint16 balloonId, int lineH) {
 // FUN_22dc_096c @ 22dc:096c: walks per-site dialog records at site_data[+6]
 // to skip hotspotIdx hotspots, returns _cluesFound flag for hotspot's first
 // text index.
-static bool floppyHotspotSearched(EEM::Mystery &mystery, uint siteIdx,
+bool floppyHotspotSearched(EEM::Mystery &mystery, uint siteIdx,
 								   uint hotspotIdx) {
 	const byte *site = mystery.siteData(siteIdx);
 	if (!site)
diff --git a/engines/eem/metaengine.cpp b/engines/eem/metaengine.cpp
index 04e778c35bc..ee16e3e10d3 100644
--- a/engines/eem/metaengine.cpp
+++ b/engines/eem/metaengine.cpp
@@ -31,7 +31,7 @@
 
 namespace EEM {
 
-static const ADExtraGuiOptionsMap optionsList[] = {
+const ADExtraGuiOptionsMap optionsList[] = {
 	{
 		GAMEOPTION_HIDE_HIGHLIGHT_BOXES,
 		{
diff --git a/engines/eem/music.cpp b/engines/eem/music.cpp
index c1fd4bf26b0..31c51f225c5 100644
--- a/engines/eem/music.cpp
+++ b/engines/eem/music.cpp
@@ -32,12 +32,8 @@
 
 namespace EEM {
 
-namespace {
-
 const int kMidiDriverFlags = MDT_MIDI | MDT_ADLIB | MDT_PREFER_MT32;
 
-} // End of anonymous namespace
-
 MusicPlayer::MusicPlayer(bool isFloppy) : _isFloppy(isFloppy) {
 	// _InitMIDI @ 20a2:013a ??? `_AIL_register_driver` against
 	// ADLIB.ADV / SBFM.ADV / MT32MPU.ADV. We honour the launcher's
diff --git a/engines/eem/site.cpp b/engines/eem/site.cpp
index d523b1e3f5b..0f9fc01f99a 100644
--- a/engines/eem/site.cpp
+++ b/engines/eem/site.cpp
@@ -48,7 +48,7 @@ void blitFrame(Graphics::ManagedSurface &dst, const Picture &p,
 // Masked top-left blit onto a locked screen surface. Clips both src and
 // dst against the screen, then delegates to copyRectToSurfaceWithKey
 // (Graphics::Surface's transparent-key blit).
-static void keyBlitToScreen(Graphics::Surface *screen, const Picture &p,
+void keyBlitToScreen(Graphics::Surface *screen, const Picture &p,
 							int x, int y) {
 	if (!screen || p.surface.empty())
 		return;
@@ -278,15 +278,15 @@ struct AnimScriptLong {
 // 0x17 (game) / 0x18 (book) / 0x19 (nancy) regardless of partner; the
 // per-partner ANI.DBD cells come from a separate entry (e.g. 0x3b for
 // Jenny's briefing).
-static const uint8 kScript17[] = {
+const uint8 kScript17[] = {
 	0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,
 	20,21,22,23,24,25,26,27,28,29
 };
-static const uint8 kScript18[] = {
+const uint8 kScript18[] = {
 	0,1,2,3,4,5,6,7,8,8,8,8,8,8,8,8,
 	8,8,8,8,8,8,8,9,10,11,12,13,14,15
 };
-static const uint8 kScript19[] = {
+const uint8 kScript19[] = {
 	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
 	1,2,3,4,5,6,7,8,9,10,11,12
 };
@@ -295,7 +295,7 @@ static const uint8 kScript19[] = {
 // frames are the original's frame-hold mechanism (one entry per tick).
 
 // 0x1a (29be:19a4) ??? count-up 0..7, idle hold, repeat 1..7, idle, mirror 7..0, idle.
-static const uint8 kScript1a[] = {
+const uint8 kScript1a[] = {
 	0,1,2,3,4,5,6,7,
 	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
 	1,2,3,4,5,6,7,
@@ -304,7 +304,7 @@ static const uint8 kScript1a[] = {
 	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
 };
 // 0x1e (29be:1a40) ??? slow walk-stutter with idle tail (76 entries).
-static const uint8 kScript1e[] = {
+const uint8 kScript1e[] = {
 	0,1,2,3,3,3,3,4,4,3,4,4,4,4,4,3,
 	5,5,5,5,5,5,5,5,4,4,4,4,4,4,4,4,
 	5,5,5,5,5,5,6,5,6,5,7,7,7,7,7,7,
@@ -313,7 +313,7 @@ static const uint8 kScript1e[] = {
 };
 // 0x1f (29be:1ada) ??? 0..5, idle, 0..5, idle, 6..8 alternation,
 // idle (50 entries).
-static const uint8 kScript1f[] = {
+const uint8 kScript1f[] = {
 	0,1,2,3,4,5,
 	0,0,0,0,
 	1,2,3,4,5,
@@ -324,13 +324,13 @@ static const uint8 kScript1f[] = {
 	0,0,0,0,0
 };
 // 0x20 (29be:1b40) ??? count-up 0..33 (34 frames).
-static const uint8 kScript20[] = {
+const uint8 kScript20[] = {
 	0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,
 	20,21,22,23,24,25,26,27,28,29,30,31,32,33
 };
 // 0x22 (29be:1ba4) ??? long held-frame walker 0..22 with idle tail
 // (115 entries; most frames held 4-7 ticks each).
-static const uint8 kScript22[] = {
+const uint8 kScript22[] = {
 	0,
 	1,1,1,1,1,
 	2,2,2,2,2,
@@ -358,7 +358,7 @@ static const uint8 kScript22[] = {
 };
 // 0x23 (29be:1c8c) ??? 29 entries: 0, 6 holds of 1, count-up 2..4,
 // down-up gesture, 5 idle frames.
-static const uint8 kScript23[] = {
+const uint8 kScript23[] = {
 	0,1,1,1,1,1,1,
 	2,3,4,3,2,
 	5,5,5,
@@ -367,7 +367,7 @@ static const uint8 kScript23[] = {
 };
 // 0x24 (29be:1cc8) ??? bell-curve hold (58 entries): 0,0, 1,1, 2,2,
 // 3 held for 26 ticks, mirror back, idle.
-static const uint8 kScript24[] = {
+const uint8 kScript24[] = {
 	0,0,1,1,2,2,
 	3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
 	2,2,1,1,
@@ -375,7 +375,7 @@ static const uint8 kScript24[] = {
 };
 // 0x28 (29be:1d7e) ??? gentle hold 0..3 with long hold on 3, mirror
 // back, idle (45 entries).
-static const uint8 kScript28[] = {
+const uint8 kScript28[] = {
 	0,1,1,2,2,
 	3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
 	2,2,1,1,
@@ -383,7 +383,7 @@ static const uint8 kScript28[] = {
 };
 // 0x29 (29be:1dda) ??? paired-step count-up 0..21 plus idle
 // (58 entries).
-static const uint8 kScript29[] = {
+const uint8 kScript29[] = {
 	0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,
 	11,11,12,12,13,13,14,14,15,15,16,16,17,17,18,18,19,19,
 	20,20,21,21,
@@ -391,14 +391,14 @@ static const uint8 kScript29[] = {
 };
 // 0x2b (29be:1e5c) ??? count-up 0..11 with each frame held 4 ticks
 // (48 entries).
-static const uint8 kScript2b[] = {
+const uint8 kScript2b[] = {
 	0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,
 	4,4,4,4,5,5,5,5,6,6,6,6,7,7,7,7,
 	8,8,8,8,9,9,9,9,10,10,10,10,11,11,11,11
 };
 // 0x2c (29be:1ebe) ??? alternation walk 0..19 with idle tail
 // (54 entries).
-static const uint8 kScript2c[] = {
+const uint8 kScript2c[] = {
 	0,1,2,3,4,5,
 	0,
 	6,7,8,9,10,10,10,10,10,10,
@@ -410,7 +410,7 @@ static const uint8 kScript2c[] = {
 };
 // 0x2d (29be:1f2c) ??? count-up 0..11 with each frame held 8 ticks
 // (96 entries).
-static const uint8 kScript2d[] = {
+const uint8 kScript2d[] = {
 	0,0,0,0,0,0,0,0,
 	1,1,1,1,1,1,1,1,
 	2,2,2,2,2,2,2,2,
@@ -426,7 +426,7 @@ static const uint8 kScript2d[] = {
 };
 // 0x30 (29be:1fee) ??? 0,0, count-up 1..19, idle, mirror down, extra
 // idle (86 entries).
-static const uint8 kScript30[] = {
+const uint8 kScript30[] = {
 	0,0,
 	1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,
 	0,0,0,0,0,0,0,0,0,0,
@@ -435,7 +435,7 @@ static const uint8 kScript30[] = {
 	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
 };
 // 0x31 (29be:209c) ??? paired-step idle alternations (57 entries).
-static const uint8 kScript31[] = {
+const uint8 kScript31[] = {
 	0,0,0,1,1,1,
 	0,0,0,1,1,1,
 	2,2,2,3,3,3,
@@ -450,7 +450,7 @@ static const uint8 kScript31[] = {
 };
 // 0x36 (29be:2110) ??? 0..8 forward, 1..8 forward, frame 1 held 20
 // ticks, 8..0 mirror, idle tail (60 entries).
-static const uint8 kScript36[] = {
+const uint8 kScript36[] = {
 	0,1,2,3,4,5,6,7,8,
 	1,2,3,4,5,6,7,8,
 	1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
@@ -484,19 +484,19 @@ const AnimScriptLong kAnimScriptsLong[] = {
 // never calls the switchers; floppy calls them from `_DoSiteLoop_Floppy`
 // (via `_Switch2Patient` / `_Switch2Impatient`). We intentionally enable
 // the same switch for both builds.
-static const uint8 kPatientSequence[]   = { 0,0,0,0,0,0,0,0,0,2 };
-static const uint8 kImpatientSequence[] = { 0,1,0,1,0,1,0,1,2,1 };
+const uint8 kPatientSequence[]   = { 0,0,0,0,0,0,0,0,0,2 };
+const uint8 kImpatientSequence[] = { 0,1,0,1,0,1,0,1,2,1 };
 
 // Test-shortened impatience delay. The original stores an hour-rounded
 // wall-clock value via DOS gettime; this keeps the same reset/switch
 // behavior but makes the feature observable during normal testing.
-static const uint32 kImpatienceDelayMs = 60 * 1000;
+const uint32 kImpatienceDelayMs = 60 * 1000;
 
 struct AnimScriptRef {
 	const uint8 *frames;
 	uint16 len;
 };
-static AnimScriptRef findAnimScript(uint16 seqnum) {
+AnimScriptRef findAnimScript(uint16 seqnum) {
 	for (uint i = 0; i < ARRAYSIZE(kAnimScripts); i++) {
 		if (kAnimScripts[i].seqnum == seqnum) {
 			AnimScriptRef r;
@@ -525,9 +525,9 @@ static AnimScriptRef findAnimScript(uint16 seqnum) {
 // (Borland C `struct time` memory order is min, hour, hund, sec.)
 // `+ 0xe` is 14 centiseconds ??? ~140 ms per frame, matching
 // `_CheckFrameRate @ 1a35:0204`.
-static const uint kFramePeriodMs = 140;
+const uint kFramePeriodMs = 140;
 
-static uint frameFromScriptAtTick(const uint8 *frames, uint len,
+uint frameFromScriptAtTick(const uint8 *frames, uint len,
 								  uint numFrames, uint32 tickMs) {
 	if (!frames || len == 0)
 		return numFrames > 0 ? (uint)((tickMs / kFramePeriodMs) % numFrames) : 0;
@@ -639,7 +639,7 @@ uint partnerFrameAtTick(uint16 seqnum, uint numFrames, uint32 tickMs) {
 // `_SmallMapWaitSeq @ 29be:1548`). `partnerFrameAtTick` can't model
 // that swap on its own (it always wraps on the same script), hence
 // this helper.
-static uint oneShotThenLoopFrameAtTick(const uint8 *unfold, uint unfoldLen,
+uint oneShotThenLoopFrameAtTick(const uint8 *unfold, uint unfoldLen,
 									   const uint8 *waitSeq, uint waitSeqLen,
 									   uint numFrames, uint32 elapsedMs) {
 	const uint tick = elapsedMs / kFramePeriodMs;


Commit: f7890714707e231e068bca32bc05939ee5baade7
    https://github.com/scummvm/scummvm/commit/f7890714707e231e068bca32bc05939ee5baade7
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:21:15+02:00

Commit Message:
CI: temporary enable CI in github

Changed paths:
    .github/workflows/ci.yml


diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index aa19879897e..5a7badb96c9 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -1,5 +1,12 @@
 name: CI
-on: [push, pull_request]
+# Temporarily scoped to dev-eem to fix merge-blocking CI issues without
+# burning runner minutes on every push. Revert to `on: [push, pull_request]`
+# before opening upstream PRs from other branches.
+on:
+  push:
+    branches: [dev-eem]
+  pull_request:
+    branches: [dev-eem]
 #  schedule:
 #    - cron: '0 0-23/4 * * *'
 permissions:


Commit: ba065aa1a9a6a4b0979c3e09aa197a4deea33c5f
    https://github.com/scummvm/scummvm/commit/ba065aa1a9a6a4b0979c3e09aa197a4deea33c5f
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:21:15+02:00

Commit Message:
CI: reverted temporary enable CI in github commit

Changed paths:
    .github/workflows/ci.yml


diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 5a7badb96c9..aa19879897e 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -1,12 +1,5 @@
 name: CI
-# Temporarily scoped to dev-eem to fix merge-blocking CI issues without
-# burning runner minutes on every push. Revert to `on: [push, pull_request]`
-# before opening upstream PRs from other branches.
-on:
-  push:
-    branches: [dev-eem]
-  pull_request:
-    branches: [dev-eem]
+on: [push, pull_request]
 #  schedule:
 #    - cron: '0 0-23/4 * * *'
 permissions:


Commit: 8bcc62ad5d61919786b859a2dadb3cb2ad5a9312
    https://github.com/scummvm/scummvm/commit/8bcc62ad5d61919786b859a2dadb3cb2ad5a9312
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:21:15+02:00

Commit Message:
EEM: removed duplicated definitions

Changed paths:
    engines/eem/eem.h
    engines/eem/site.cpp
    engines/eem/ui.cpp


diff --git a/engines/eem/eem.h b/engines/eem/eem.h
index 8c06aa64cb9..04234a5a89b 100644
--- a/engines/eem/eem.h
+++ b/engines/eem/eem.h
@@ -28,6 +28,7 @@
 #include "common/language.h"
 #include "common/platform.h"
 #include "common/random.h"
+#include "common/rect.h"
 #include "common/scummsys.h"
 
 #include "common/serializer.h"
@@ -115,6 +116,13 @@ enum Partner {
 constexpr int kScreenWidth  = 320;
 constexpr int kScreenHeight = 200;
 
+/// Shared PDA-frame navigation rects (PIC 0x3f) ??? reachable from Site,
+/// Notebook, Gallery, Accuse, and MoreInfo. Original `_NoteButtons` table
+/// @ 29be:0147 + the site-screen hit tests in `_DoSiteLoop @ 168d:03f4`.
+constexpr Common::Rect kPdaSiteRect             (Common::Point(35, 111), 21, 25);
+constexpr Common::Rect kPdaPartnerFootMapRect   (Common::Point( 7, 177), 50, 23);
+constexpr Common::Rect kPdaPartnerHeadHintRect  (Common::Point( 5,  80), 39, 30);
+
 class EEMEngine : public Engine {
 public:
 	EEMEngine(OSystem *syst, const ADGameDescription *gameDesc);
diff --git a/engines/eem/site.cpp b/engines/eem/site.cpp
index 0f9fc01f99a..8358e1d6fe2 100644
--- a/engines/eem/site.cpp
+++ b/engines/eem/site.cpp
@@ -35,10 +35,6 @@
 
 namespace EEM {
 
-constexpr Common::Rect kSitePdaRect(Common::Point(35, 111), 21, 25);
-constexpr Common::Rect kSitePartnerFootMapRect(Common::Point(7, 177), 50, 23);
-constexpr Common::Rect kSitePartnerHeadHintRect(Common::Point(5, 80), 39, 30);
-
 // Masked blit using `transp` = high byte of `pic.flags` (`_Rect_Move_Mask @ 1000:03fc`).
 void blitFrame(Graphics::ManagedSurface &dst, const Picture &p,
 			   int x, int y, byte transp) {
@@ -882,14 +878,14 @@ void SiteScreen::run() {
 				// Partner-head click is port-only: `_KDHelp` shortcut
 				// mirroring `_HandleNoteButton[3]` (0x0403) /
 				// `_HandleGalleryButton[3]` (0x061e). Rect = (5,80,44,110).
-				if (kSitePdaRect.contains(event.mouse.x, event.mouse.y)) {
+				if (kPdaSiteRect.contains(event.mouse.x, event.mouse.y)) {
 					notePartnerActivity();
 					_vm->setHotspotMouseCursor(false);
 					_vm->setNextScreen(kScreenNotebook);
 					_vm->stopMusic();
 					return;
 				}
-				if (kSitePartnerFootMapRect.contains(event.mouse.x, event.mouse.y)) {
+				if (kPdaPartnerFootMapRect.contains(event.mouse.x, event.mouse.y)) {
 					notePartnerActivity();
 					_vm->setHotspotMouseCursor(false);
 					// CD: _NextScreen=1, floppy=2.
@@ -898,7 +894,7 @@ void SiteScreen::run() {
 					_vm->stopMusic();
 					return;
 				}
-				if (kSitePartnerHeadHintRect.contains(event.mouse.x, event.mouse.y)) {
+				if (kPdaPartnerHeadHintRect.contains(event.mouse.x, event.mouse.y)) {
 					_vm->setHotspotMouseCursor(false);
 					_vm->doHelp();
 					notePartnerActivity();
@@ -1527,9 +1523,9 @@ int SiteScreen::hotspotAtPoint(uint siteNum, int x, int y) const {
 void SiteScreen::updateHotspotCursor(uint siteNum, int x, int y) {
 	if (!_vm)
 		return;
-	const bool siteControl = kSitePdaRect.contains(x, y) ||
-							 kSitePartnerFootMapRect.contains(x, y) ||
-							 kSitePartnerHeadHintRect.contains(x, y);
+	const bool siteControl = kPdaSiteRect.contains(x, y) ||
+							 kPdaPartnerFootMapRect.contains(x, y) ||
+							 kPdaPartnerHeadHintRect.contains(x, y);
 	_vm->setHotspotMouseCursor(siteControl || hotspotAtPoint(siteNum, x, y) >= 0);
 }
 
diff --git a/engines/eem/ui.cpp b/engines/eem/ui.cpp
index 507d27d0506..7ef17cafaa4 100644
--- a/engines/eem/ui.cpp
+++ b/engines/eem/ui.cpp
@@ -163,16 +163,16 @@ constexpr uint16 kFloppyEndingBackgroundPic = 0x8b;
 constexpr uint16 kFirstTryBadgePic = 0x205;
 constexpr Common::Point kFirstTryBadgePos(0x1e, 9);
 
+// kPdaSiteRect / kPdaPartnerFootMapRect / kPdaPartnerHeadHintRect live in
+// eem.h (shared with site.cpp). The button-row rects below are PDA-screen-
+// only (`_NoteButtons @ 29be:0147`).
 constexpr Common::Rect kPdaHelpRect(Common::Point(93, 174), 22, 16);
 constexpr Common::Rect kPdaNotebookRect(Common::Point(134, 174), 21, 16);
 constexpr Common::Rect kPdaGalleryRect(Common::Point(157, 174), 21, 16);
-constexpr Common::Rect kPdaPartnerHeadHintRect(Common::Point(5, 80), 39, 30);
 constexpr Common::Rect kPdaAccuseRect(Common::Point(180, 174), 21, 16);
 constexpr Common::Rect kPdaPageNextRect(Common::Point(204, 174), 20, 16);
 constexpr Common::Rect kPdaPagePrevRect(Common::Point(226, 174), 21, 16);
 constexpr Common::Rect kPdaHelp2Rect(Common::Point(267, 174), 21, 16);
-constexpr Common::Rect kPdaPartnerFootMapRect(Common::Point(7, 177), 50, 23);
-constexpr Common::Rect kPdaSiteRect(Common::Point(35, 111), 21, 25);
 
 constexpr uint16 kProfilePickerRevealPic = 0x105;
 constexpr int kProfilePickerRevealX = 0x3e;


Commit: 79188e6bd223fc5dd10476f7916af06c93b2cc84
    https://github.com/scummvm/scummvm/commit/79188e6bd223fc5dd10476f7916af06c93b2cc84
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:21:16+02:00

Commit Message:
EEM: renamed _ASM_Decompress to avoid confusion

Changed paths:
    engines/eem/animation.cpp
    engines/eem/animation.h


diff --git a/engines/eem/animation.cpp b/engines/eem/animation.cpp
index 34364c6bf12..c52ee496293 100644
--- a/engines/eem/animation.cpp
+++ b/engines/eem/animation.cpp
@@ -27,7 +27,7 @@
 
 namespace EEM {
 
-void asmDecompress(const byte *src, uint srcSize, byte *dst, uint dstSize) {
+void decodeAnmFrameRLE(const byte *src, uint srcSize, byte *dst, uint dstSize) {
 	const byte *srcEnd = src + srcSize;
 	byte *dstEnd = dst + dstSize;
 
@@ -162,7 +162,7 @@ const byte *ANMDecoder::nextFrame() {
 		return nullptr;
 	}
 
-	asmDecompress(_packed.data(), packedSize, _buffer.data(), _buffer.size());
+	decodeAnmFrameRLE(_packed.data(), packedSize, _buffer.data(), _buffer.size());
 	_nextFrameIdx++;
 	return _buffer.data();
 }
diff --git a/engines/eem/animation.h b/engines/eem/animation.h
index fa5c7b8f4c2..51c07315f79 100644
--- a/engines/eem/animation.h
+++ b/engines/eem/animation.h
@@ -36,7 +36,7 @@ namespace EEM {
  *   - u16: frame count
  *   - 12 bytes: header (height @ +2, width @ +4, rest unused)
  *   - frames*u16: packed length per frame
- *   - per frame: lengths[i] bytes of RLE delta data (asmDecompress).
+ *   - per frame: lengths[i] bytes of RLE delta data (decodeAnmFrameRLE).
  */
 class ANMDecoder {
 public:
@@ -82,9 +82,11 @@ private:
 	uint16 _nextFrameIdx = 0;
 };
 
-/// _ASM_Decompress @ 1000:0953. dst holds the previous frame; skip opcodes
-/// leave those pixels untouched (difference encoding).
-void asmDecompress(const byte *src, uint srcSize, byte *dst, uint dstSize);
+/// Decode a single ANM frame's RLE-encoded delta payload into @p dst.
+/// `dst` already holds the previous frame; skip opcodes leave those pixels
+/// untouched (difference encoding). Original symbol: `_ASM_Decompress`
+/// @ 1000:0953.
+void decodeAnmFrameRLE(const byte *src, uint srcSize, byte *dst, uint dstSize);
 
 } // End of namespace EEM
 


Commit: 2b9c008c66397e6bba2cacbd3dbac8b3040f1e51
    https://github.com/scummvm/scummvm/commit/2b9c008c66397e6bba2cacbd3dbac8b3040f1e51
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:21:16+02:00

Commit Message:
EEM: add end of notes in the PDA rendering

Changed paths:
    engines/eem/eem.h
    engines/eem/ui.cpp


diff --git a/engines/eem/eem.h b/engines/eem/eem.h
index 04234a5a89b..3115e45d7d4 100644
--- a/engines/eem/eem.h
+++ b/engines/eem/eem.h
@@ -518,10 +518,6 @@ private:
 	/// ESC during intro: skip remaining opening-anim chain.
 	bool _skipIntro = false;
 
-	/// Cached notebook slot rects + clue IDs for click hit-testing.
-	Common::Array<Common::Rect> _notebookSlotRects;
-	Common::Array<uint>         _notebookSlotClues;
-
 	/// Clean BG (no partner/NPC) used by `playKdAnim` between camera-anim
 	/// cells. See `setPartnerEraseBg`.
 	Graphics::ManagedSurface _partnerEraseBg;
diff --git a/engines/eem/ui.cpp b/engines/eem/ui.cpp
index 7ef17cafaa4..ca54e079b9e 100644
--- a/engines/eem/ui.cpp
+++ b/engines/eem/ui.cpp
@@ -2125,9 +2125,7 @@ void EEMEngine::doNotebook() {
 
 	drawNotebookFrame(page);
 	Common::Point mouse = g_system->getEventManager()->getMousePos();
-	setInteractiveMouseCursor(notebookButtonAt(mouse.x, mouse.y) ||
-							  rectListContains(_notebookSlotRects,
-											   mouse.x, mouse.y));
+	setInteractiveMouseCursor(notebookButtonAt(mouse.x, mouse.y));
 
 	uint32 lastDraw = g_system->getMillis();
 	uint32 gizmoLastTick = lastDraw;
@@ -2145,9 +2143,6 @@ void EEMEngine::doNotebook() {
 			}
 			if (ev.type == Common::EVENT_MOUSEMOVE) {
 				setInteractiveMouseCursor(notebookButtonAt(ev.mouse.x,
-														   ev.mouse.y) ||
-										  rectListContains(_notebookSlotRects,
-														   ev.mouse.x,
 														   ev.mouse.y));
 			}
 			if (ev.type == Common::EVENT_KEYDOWN) {
@@ -2220,17 +2215,10 @@ void EEMEngine::doNotebook() {
 					dirty = true;
 					continue;
 				}
-				// ScummVM-only: click on clue slot toggles _NoteSelected
-				// (original toggles only in accuse screen).
-				for (uint i = 0; i < _notebookSlotRects.size(); i++) {
-					if (_notebookSlotRects[i].contains(ev.mouse.x,
-													   ev.mouse.y)) {
-						const uint clueId = _notebookSlotClues[i];
-						_mystery._noteSelected[clueId] ^= 1;
-						dirty = true;
-						break;
-					}
-				}
+				// The notebook screen is read-only in the original:
+				// `_DoNotebook @ 161e:0500` only checks clicks against
+				// `_NoteButtons` (11 rects). Clue toggling lives in the
+				// accuse screen.
 			}
 		}
 		if (exitFlag)
@@ -2242,9 +2230,7 @@ void EEMEngine::doNotebook() {
 			drawNotebookFrame(page);
 			lastDraw = now;
 			mouse = g_system->getEventManager()->getMousePos();
-			setInteractiveMouseCursor(notebookButtonAt(mouse.x, mouse.y) ||
-									  rectListContains(_notebookSlotRects,
-													   mouse.x, mouse.y));
+			setInteractiveMouseCursor(notebookButtonAt(mouse.x, mouse.y));
 		}
 		// _GizmoColorCycle @ 1c33:0002 ??? `_DoNotebook` rotates 0x6f..0x73 each
 		// _CheckFrameRate tick (the PDA gizmo / LED indicator shimmer).
@@ -2348,10 +2334,10 @@ void EEMEngine::drawNotebookFrame(int &page) {
 			page = 0;
 	}
 
-	// Per-slot rects published to the click handler.
-	Common::Array<Common::Rect> slotRects;
-	Common::Array<uint> slotClues;
-
+	// `_DrawNotes @ 161e:01d0` is read-only on this screen ??? clue selection
+	// is driven exclusively from the accuse screen. The 0x3C / 0x5C colour
+	// choice still reflects `_NoteSelected[]` so picks made in accuse remain
+	// visible when the player flips back here.
 	const int startClue = (page < (int)pageStarts.size())
 							? pageStarts[page] : 0;
 	const int endClue   = (page + 1 < (int)pageStarts.size())
@@ -2365,7 +2351,6 @@ void EEMEngine::drawNotebookFrame(int &page) {
 		if (txt.empty())
 			txt = Common::String::format(
 				isSpanish() ? "nota %u" : "clue %u", clueId);
-		// `_DrawNotes @ 161e:01d0`: 0x5c unselected, 0x3c selected.
 		Common::Array<Common::String> wrapped;
 		_font.wordWrapText(txt, kRectW, wrapped);
 		const int lineH = _font.getFontHeight();
@@ -2375,24 +2360,22 @@ void EEMEngine::drawNotebookFrame(int &page) {
 			_font.drawString(&scratch, wrapped[li], kRectX,
 							 y + (int)li * lineH, kRectW, color);
 		}
-		slotRects.push_back(Common::Rect(kRectX, y,
-										  kRectX + kRectW, y + h));
-		slotClues.push_back(clueId);
 		y += h + 7;
 	}
 
-	// Page indicator only (original has no points display).
-	_font.drawString(&scratch, Common::String::format("p%d/%d",
-							   page + 1, (int)pageStarts.size()),
-					 270, 4, kScreenWidth, 0x5C);
+	// `_DrawNotes @ 161e:01d0` appends a terminator line at the bottom of
+	// the last page once the clue list is exhausted ??? string @ 29be:01f4.
+	const bool isLastPage = (page + 1 >= (int)pageStarts.size());
+	if (isLastPage) {
+		const char *kEndMarker = isSpanish()
+			? "-- Fin de las notas --"
+			: "-- End of notes --";
+		_font.drawString(&scratch, kEndMarker, kRectX, y, kRectW, 0x5C);
+	}
 
 	g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
 							   0, 0, kScreenWidth, kScreenHeight);
 	g_system->updateScreen();
-
-	// Publish slot info to `doNotebook`'s click handler.
-	_notebookSlotRects = slotRects;
-	_notebookSlotClues = slotClues;
 }
 
 void EEMEngine::doGallery() {


Commit: c9a5bbc56c706c24039210f01702ba3f6740b650
    https://github.com/scummvm/scummvm/commit/c9a5bbc56c706c24039210f01702ba3f6740b650
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:21:16+02:00

Commit Message:
EEM: add an option to skip repeated cases

Changed paths:
    engines/eem/detection.cpp
    engines/eem/detection.h
    engines/eem/eem.cpp
    engines/eem/eem.h
    engines/eem/metaengine.cpp
    engines/eem/ui.cpp


diff --git a/engines/eem/detection.cpp b/engines/eem/detection.cpp
index 48cf5629a1d..17ff91e826b 100644
--- a/engines/eem/detection.cpp
+++ b/engines/eem/detection.cpp
@@ -31,8 +31,8 @@ const PlainGameDescriptor eemGames[] = {
 	{ nullptr, nullptr }
 };
 
-#define GUI_OPTIONS_EEM_FLOPPY GUIO3(GAMEOPTION_HIDE_HIGHLIGHT_BOXES, GUIO_MIDIADLIB, GUIO_MIDIMT32)
-#define GUI_OPTIONS_EEM_CD     GUIO4(GAMEOPTION_HIDE_HIGHLIGHT_BOXES, GAMEOPTION_FIT_DIALOG_BALLOONS, GUIO_MIDIADLIB, GUIO_MIDIMT32)
+#define GUI_OPTIONS_EEM_FLOPPY GUIO4(GAMEOPTION_HIDE_HIGHLIGHT_BOXES, GAMEOPTION_SKIP_REPEATED_CASES, GUIO_MIDIADLIB, GUIO_MIDIMT32)
+#define GUI_OPTIONS_EEM_CD     GUIO5(GAMEOPTION_HIDE_HIGHLIGHT_BOXES, GAMEOPTION_FIT_DIALOG_BALLOONS, GAMEOPTION_SKIP_REPEATED_CASES, GUIO_MIDIADLIB, GUIO_MIDIMT32)
 
 const ADGameDescription gameDescriptions[] = {
 	{
diff --git a/engines/eem/detection.h b/engines/eem/detection.h
index 3ad726bb5f0..937fbe6a4e3 100644
--- a/engines/eem/detection.h
+++ b/engines/eem/detection.h
@@ -28,6 +28,7 @@ namespace EEM {
 
 #define GAMEOPTION_HIDE_HIGHLIGHT_BOXES   GUIO_GAMEOPTIONS1
 #define GAMEOPTION_FIT_DIALOG_BALLOONS    GUIO_GAMEOPTIONS2
+#define GAMEOPTION_SKIP_REPEATED_CASES    GUIO_GAMEOPTIONS3
 
 enum EEMDebugChannels {
 	kDebugGeneral = 1 << 0,
diff --git a/engines/eem/eem.cpp b/engines/eem/eem.cpp
index 9dfe48519f7..2ad4b74f687 100644
--- a/engines/eem/eem.cpp
+++ b/engines/eem/eem.cpp
@@ -197,6 +197,7 @@ EEMEngine::EEMEngine(OSystem *syst, const ADGameDescription *gameDesc)
 	  _lastScreen(kScreenInvalid), _nextScreen(kScreenTitle), _partner(0) {
 	ConfMan.registerDefault("hide_highlight_boxes", false);
 	ConfMan.registerDefault("fit_dialog_balloons", false);
+	ConfMan.registerDefault("skip_repeated_cases", false);
 
 	_variant = (gameDesc && gameDesc->extra &&
 				Common::String(gameDesc->extra).contains("Floppy"))
@@ -220,6 +221,69 @@ void EEMEngine::applyStartupTestOverrides() {
 		   "startup test override: populated ScrapBook 1 mystery flags");
 }
 
+bool EEMEngine::areMysteriesSolved(uint lo, uint hi) const {
+	if (hi < lo)
+		return false;
+	for (uint i = lo; i <= hi; i++) {
+		if (i >= sizeof(_mysteriesSolved) || _mysteriesSolved[i] == 0)
+			return false;
+	}
+	return true;
+}
+
+void EEMEngine::advanceChainStageAfterSolve(uint mysteryNum) {
+	if (mysteryNum == 0 || _chainStage >= 4)
+		return;
+
+	uint lo = 0;
+	uint hi = 0;
+	switch (_chainStage) {
+	case 1:
+		lo = 1;
+		hi = 0x18;
+		break;
+	case 2:
+		lo = 0x19;
+		hi = 0x30;
+		break;
+	case 3:
+		lo = 0x31;
+		hi = 0x36;
+		break;
+	default:
+		return;
+	}
+
+	if (!areMysteriesSolved(lo, hi))
+		return;
+
+	const uint oldStage = _chainStage;
+	// Book 2 repeats the Book 1 cases; this option keeps the original solve
+	// state but jumps the profile's active chain straight to Book 3.
+	if (_chainStage == 1 && ConfMan.getBool("skip_repeated_cases"))
+		_chainStage = 3;
+	else
+		_chainStage++;
+
+	debugC(1, kDebugMystery,
+		   "chainStage advanced from %u to %u after solving mystery %u",
+		   oldStage, _chainStage, mysteryNum);
+}
+
+void EEMEngine::applySkipRepeatedCasesOption() {
+	if (!ConfMan.getBool("skip_repeated_cases"))
+		return;
+	if (_mystery.isLoaded())
+		return;
+	if (_chainStage <= 2 && areMysteriesSolved(1, 0x18)) {
+		const uint oldStage = _chainStage;
+		_chainStage = 3;
+		debugC(1, kDebugMystery,
+			   "skip_repeated_cases advanced chainStage from %u to %u",
+			   oldStage, _chainStage);
+	}
+}
+
 Common::Error EEMEngine::run() {
 	// _SetMode13X @ 1000:0358 ??? VGA mode 13h.
 	initGraphics(kScreenWidth, kScreenHeight);
@@ -268,6 +332,7 @@ Common::Error EEMEngine::run() {
 		const Common::Error err = loadGameState(wantedSave);
 		if (err.getCode() == Common::kNoError) {
 			applyStartupTestOverrides();
+			applySkipRepeatedCasesOption();
 			CursorMan.showMouse(true);
 			if (_mystery.isLoaded()) {
 				debugC(1, kDebugGeneral,
@@ -398,6 +463,8 @@ Common::Error EEMEngine::run() {
 		doProfilePicker();
 	if (!shouldQuit())
 		applyStartupTestOverrides();
+	if (!shouldQuit())
+		applySkipRepeatedCasesOption();
 	if (!shouldQuit())
 		doChoosePartner();
 
@@ -521,6 +588,8 @@ screenLoop:
 			doProfilePicker();
 			if (!shouldQuit())
 				applyStartupTestOverrides();
+			if (!shouldQuit())
+				applySkipRepeatedCasesOption();
 			if (!shouldQuit())
 				doChoosePartner();
 			if (!shouldQuit())
@@ -1172,6 +1241,7 @@ Common::Error EEMEngine::loadGameStream(Common::SeekableReadStream *stream) {
 		_mystery.clear();
 		resetSiteArrivalState();
 	}
+	applySkipRepeatedCasesOption();
 
 	debugC(1, kDebugGeneral,
 		   "Loaded profile name=%s partner=%u mystery=%d",
diff --git a/engines/eem/eem.h b/engines/eem/eem.h
index 3115e45d7d4..e2ebb1151d0 100644
--- a/engines/eem/eem.h
+++ b/engines/eem/eem.h
@@ -278,6 +278,9 @@ public:
 
 private:
 	void applyStartupTestOverrides();
+	bool areMysteriesSolved(uint lo, uint hi) const;
+	void advanceChainStageAfterSolve(uint mysteryNum);
+	void applySkipRepeatedCasesOption();
 
 	/// Central dispatch loop matching `_ScreenDriver @ 1a35:0dc1`. Each
 	/// iteration calls the handler that matches `_nextScreen`; handlers
diff --git a/engines/eem/metaengine.cpp b/engines/eem/metaengine.cpp
index ee16e3e10d3..d3efe9c89cc 100644
--- a/engines/eem/metaengine.cpp
+++ b/engines/eem/metaengine.cpp
@@ -55,6 +55,17 @@ const ADExtraGuiOptionsMap optionsList[] = {
 			0
 		}
 	},
+	{
+		GAMEOPTION_SKIP_REPEATED_CASES,
+		{
+			_s("Skip repeated cases"),
+			_s("Skip all Book 2 cases and jump to Book 3 once Book 1 is complete."),
+			"skip_repeated_cases",
+			false,
+			0,
+			0
+		}
+	},
 
 	AD_EXTRA_GUI_OPTIONS_TERMINATOR
 };
diff --git a/engines/eem/ui.cpp b/engines/eem/ui.cpp
index ca54e079b9e..3a29e5252cf 100644
--- a/engines/eem/ui.cpp
+++ b/engines/eem/ui.cpp
@@ -4281,38 +4281,7 @@ void EEMEngine::doAccuse() {
 
 		// Chain advance @ 1df2:0824-0850. Skip mystery 0 (practice).
 		// stage 1: 1..0x18, stage 2: 0x19..0x30, stage 3: 0x31..0x36.
-		if (mn != 0) {
-			uint lo = 0;
-			uint hi = 0;
-			switch (_chainStage) {
-			case 1:
-				lo = 1;
-				hi = 0x18;
-				break;
-			case 2:
-				lo = 0x19;
-				hi = 0x30;
-				break;
-			case 3:
-				lo = 0x31;
-				hi = 0x36;
-				break;
-			default:
-				break;
-			}
-			bool allSolved = (hi >= lo);
-			for (uint i = lo; i <= hi && allSolved; i++) {
-				if (i >= sizeof(_mysteriesSolved) || _mysteriesSolved[i] == 0)
-					allSolved = false;
-			}
-			// 1df2:0852 increments past 3 (stage-4 endgame); cap at 4.
-			if (allSolved && _chainStage < 4) {
-				_chainStage++;
-				debugC(1, kDebugMystery,
-					   "chainStage advanced to %u after solving mystery %u",
-					   _chainStage, mn);
-			}
-		}
+		advanceChainStageAfterSolve(mn);
 
 		// `_DisplayCorrect @ 1df2:073c`:
 		//   _AllBlack; _BuildBackground(5, 0x42, 0x14); _FadeIn;
@@ -4738,38 +4707,7 @@ void EEMEngine::doAccuseFloppy() {
 			_mysteriesSolved[mn] = _mystery._firstTry ? 2 : 1;
 
 		// Tier promotion @ 1d40:0941..0978. Skip mystery 0 (practice).
-		if (mn != 0) {
-			uint lo = 0;
-			uint hi = 0;
-			switch (_chainStage) {
-			case 1:
-				lo = 1;
-				hi = 0x18;
-				break;
-			case 2:
-				lo = 0x19;
-				hi = 0x30;
-				break;
-			case 3:
-				lo = 0x31;
-				hi = 0x36;
-				break;
-			default:
-				break;
-			}
-			bool allSolved = (hi >= lo);
-			for (uint i = lo; i <= hi && allSolved; i++) {
-				if (i >= sizeof(_mysteriesSolved) ||
-					_mysteriesSolved[i] == 0)
-					allSolved = false;
-			}
-			if (allSolved && _chainStage < 4) {
-				_chainStage++;
-				debugC(1, kDebugMystery,
-					   "chainStage advanced to %u after solving mystery %u",
-					   _chainStage, mn);
-			}
-		}
+		advanceChainStageAfterSolve(mn);
 
 		// `MakeSolvedSound`. `FUN_1d40_05b7` maps E<num>.BIN byte 0 (0..2)
 		// via table @ 2608:0c5e to VOC slots 0x15/0x16/0x17.


Commit: f2c5be745a959c9ce15f92350b0ee5282c358e2a
    https://github.com/scummvm/scummvm/commit/f2c5be745a959c9ce15f92350b0ee5282c358e2a
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:21:17+02:00

Commit Message:
EEM: make sure screenshots contain all the elements from sites

Changed paths:
    engines/eem/site.cpp
    engines/eem/site.h


diff --git a/engines/eem/site.cpp b/engines/eem/site.cpp
index 8358e1d6fe2..7c9112d97a4 100644
--- a/engines/eem/site.cpp
+++ b/engines/eem/site.cpp
@@ -35,6 +35,27 @@
 
 namespace EEM {
 
+const uint kSiteBackendActionFlushPriority = 100;
+
+class SiteBackendActionObserverRegistration {
+public:
+	SiteBackendActionObserverRegistration(Common::EventObserver *observer)
+		: _dispatcher(g_system->getEventManager()->getEventDispatcher()),
+		  _observer(observer) {
+		if (_dispatcher)
+			_dispatcher->registerObserver(_observer, kSiteBackendActionFlushPriority, false);
+	}
+
+	~SiteBackendActionObserverRegistration() {
+		if (_dispatcher)
+			_dispatcher->unregisterObserver(_observer);
+	}
+
+private:
+	Common::EventDispatcher *_dispatcher;
+	Common::EventObserver *_observer;
+};
+
 // Masked blit using `transp` = high byte of `pic.flags` (`_Rect_Move_Mask @ 1000:03fc`).
 void blitFrame(Graphics::ManagedSurface &dst, const Picture &p,
 			   int x, int y, byte transp) {
@@ -843,6 +864,18 @@ void SiteScreen::notePartnerActivity() {
 		debugC(1, kDebugSite, "Partner impatience: reset to patient");
 }
 
+bool SiteScreen::notifyEvent(const Common::Event &event) {
+	if (event.type != Common::EVENT_CUSTOM_BACKEND_ACTION_START)
+		return false;
+
+	if (_snapshotSite < 0 || g_system->isOverlayVisible())
+		return false;
+
+	syncCompositedScreen();
+	g_system->updateScreen();
+	return false;
+}
+
 void SiteScreen::run() {
 	if (!_mystery || !_mystery->isLoaded())
 		return;
@@ -851,6 +884,8 @@ void SiteScreen::run() {
 	uint cur = _mystery->_siteNumber;
 	if (cur >= _mystery->numSites())
 		cur = 0;
+	_snapshotSite = -1;
+	SiteBackendActionObserverRegistration backendActionRegistration(this);
 	enter(cur);
 	Common::Point mouse = g_system->getEventManager()->getMousePos();
 	updateHotspotCursor(cur, mouse.x, mouse.y);
@@ -1280,6 +1315,25 @@ void SiteScreen::restoreBgSnapshot() {
 							   0, 0, kScreenWidth, kScreenHeight);
 }
 
+void SiteScreen::syncCompositedScreen() {
+	// OpenGL screenshots read the current backbuffer. After a buffer swap,
+	// that backbuffer can contain an older site frame unless the engine keeps
+	// presenting the full composited screen, even while no animation tick
+	// fired. Copying the current game surface through copyRectToScreen keeps
+	// screenshots in sync without backend-specific changes.
+	Graphics::ManagedSurface snapshot(kScreenWidth, kScreenHeight,
+		Graphics::PixelFormat::createFormatCLUT8());
+
+	Graphics::Surface *screen = g_system->lockScreen();
+	if (!screen)
+		return;
+	snapshot.simpleBlitFrom(*screen);
+	g_system->unlockScreen();
+
+	g_system->copyRectToScreen(snapshot.getPixels(), snapshot.pitch,
+							   0, 0, kScreenWidth, kScreenHeight);
+}
+
 void SiteScreen::renderPartner(uint siteNum, uint32 tickMs) {
 	// `_DoSiteLoop @ 168d:03f4` reads `siteData[+8]` as the speaker
 	// table index, then for each (speaker x partner) loads:
diff --git a/engines/eem/site.h b/engines/eem/site.h
index 895d2124a9f..1719e68c47f 100644
--- a/engines/eem/site.h
+++ b/engines/eem/site.h
@@ -22,6 +22,7 @@
 #ifndef EEM_SITE_H
 #define EEM_SITE_H
 
+#include "common/events.h"
 #include "common/rect.h"
 #include "common/scummsys.h"
 
@@ -88,7 +89,7 @@ struct Hotspot {
 
 /// Site / scene controller. Mirrors `_DrawSearchButtons @ 2404:0a8f` /
 /// `_SearchButtons @ 2404:0bfb` site loop.
-class SiteScreen {
+class SiteScreen : public Common::EventObserver {
 public:
 	SiteScreen(EEMEngine *vm, Mystery *mystery)
 		: _vm(vm), _mystery(mystery) {}
@@ -100,6 +101,8 @@ public:
 	void run();
 
 private:
+	bool notifyEvent(const Common::Event &event) override;
+
 	void renderBackground(uint siteNum);
 	void renderHotspots(uint siteNum);
 	int  hotspotAtPoint(uint siteNum, int x, int y) const;
@@ -140,6 +143,12 @@ private:
 	/// Restore the snapshot taken at `captureBgSnapshot` time.
 	void restoreBgSnapshot();
 
+	/// Push pixels written through `lockScreen()` back through the backend's
+	/// normal screen-copy path. The SDL/OpenGL screenshot code captures the
+	/// presented backend buffer, so site frames must not leave partner/NPC
+	/// sprites only in the locked software surface.
+	void syncCompositedScreen();
+
 	/// scanColorCycles: scan Loop 1 for ColorCycle entries (animId == -1),
 	/// cache (start, end) palette ranges. Mirrors `_DoSiteLoop @ 168d:03f4` init scan.
 	void scanColorCycles(uint siteNum);


Commit: 24fe380eedd014bab00afd035e6f0289875fb93e
    https://github.com/scummvm/scummvm/commit/24fe380eedd014bab00afd035e6f0289875fb93e
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:21:17+02:00

Commit Message:
EEM: fixed invalid reference to a sound clip in practice mystery

Changed paths:
    engines/eem/ui.cpp


diff --git a/engines/eem/ui.cpp b/engines/eem/ui.cpp
index 3a29e5252cf..98960cd0b83 100644
--- a/engines/eem/ui.cpp
+++ b/engines/eem/ui.cpp
@@ -3707,6 +3707,7 @@ void EEMEngine::doAccuse() {
 	const int foundPoints = _mystery.foundPoints();
 	uint entryKDSpeak = 0;
 	uint16 entryTextOff = 0xFFFF;
+	uint16 entryVoiceOverride = 0xFFFF;
 	bool canAccuse = false;
 	Common::String entryText;
 	if (foundPoints == 0) {
@@ -3714,6 +3715,11 @@ void EEMEngine::doAccuse() {
 		entryText = "3We're not ready to solve this mystery yet.  "
 					"Let's keep investigating until we have some more solid "
 					"evidence to make our case!";
+		// Practice mystery M0 ships the matching ZeroText takes as the
+		// final two SDB entries, but its KD digital table points kdspeak 9
+		// at earlier hint clips. Use the otherwise unreferenced pair.
+		if (_mystery.number() == 0)
+			entryVoiceOverride = (_partner == kPartnerJake) ? 105 : 104;
 	} else if (foundPoints < 0x32) {
 		entryKDSpeak = 0;
 		entryTextOff = READ_LE_UINT16(entryKdIdx + 0);
@@ -3766,8 +3772,12 @@ void EEMEngine::doAccuse() {
 		g_system->copyRectToScreen(ms.getPixels(), ms.pitch,
 								   0, 0, kScreenWidth, kScreenHeight);
 		g_system->updateScreen();
-		if (_audio)
-			_audio->sayKDDigital(entryKdIdx, entryKDSpeak, _partner);
+		if (_audio) {
+			if (entryVoiceOverride != 0xFFFF)
+				_audio->spoolSound(entryVoiceOverride);
+			else
+				_audio->sayKDDigital(entryKdIdx, entryKDSpeak, _partner);
+		}
 		waitForInput(60000);
 	}
 	if (!canAccuse) {
@@ -4330,7 +4340,8 @@ void EEMEngine::doAccuse() {
 			_music->stop();
 
 		// `_DifferenceAnimation("scrapbk.ani")` @ 1df2:0848.
-		playAnm(Common::Path("SCRAPBK.ANI"), 120, true);
+		playAnm(Common::Path("SCRAPBK.ANI"), 120,
+				/* holdLastFrame= */ false);
 
 		// `_ShowOneScrap @ 1f78:0773` = `_DisplayEnding(num, 1)`.
 		doShowEnding(mn);


Commit: 32e5d42d209c4cae47fa86047b2b73bb5c456aed
    https://github.com/scummvm/scummvm/commit/32e5d42d209c4cae47fa86047b2b73bb5c456aed
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:21:17+02:00

Commit Message:
EEM: implemented restored content from the floppy data into the CD one

Changed paths:
  A devtools/create_eem/README.md
  A devtools/create_eem/create_scrapbk_extra.py
  A devtools/create_eem/files/eem/SCRAPBK_EXTRA.ANI
  A devtools/create_eem/files/eem/version.txt
  A dists/engine-data/eem.dat
    dists/engine-data/README
    dists/engine-data/engine_data.mk
    engines/eem/detection.cpp
    engines/eem/detection.h
    engines/eem/eem.cpp
    engines/eem/eem.h
    engines/eem/metaengine.cpp
    engines/eem/ui.cpp


diff --git a/devtools/create_eem/README.md b/devtools/create_eem/README.md
new file mode 100644
index 00000000000..94234b2f217
--- /dev/null
+++ b/devtools/create_eem/README.md
@@ -0,0 +1,19 @@
+EEM engine data
+-------------------------------------------------------------------------------
+
+`SCRAPBK_EXTRA.ANI` stores the three floppy-only dialog records displayed
+after `SCRAPBK.ANI` and before the scrapbook page. The CD executable omits
+those records from its solved recap data. The restored subtitles are kept for
+all books, but voice indices are only attached for Book 1 mysteries because
+matching unused CD clips could not be identified reliably for Books 2 and 3.
+
+Generate the file from extracted English floppy and CD data:
+
+  ./create_scrapbk_extra.py \
+      --floppy-dir ../../eem-full-game/floppy/EAKIDS/EEM \
+      --cd-dir ../../eem-full-game/cd
+
+Then rebuild the distributable data archive from this directory:
+
+  cd files
+  zip -r9 ../../../dists/engine-data/eem.dat .
diff --git a/devtools/create_eem/create_scrapbk_extra.py b/devtools/create_eem/create_scrapbk_extra.py
new file mode 100755
index 00000000000..2b0b73a47f2
--- /dev/null
+++ b/devtools/create_eem/create_scrapbk_extra.py
@@ -0,0 +1,398 @@
+#!/usr/bin/env python3
+#
+# Extract the floppy-only pre-scrapbook dialog records into an EEM engine
+# data file consumed by the CD runtime.
+
+from __future__ import annotations
+
+import argparse
+import struct
+from dataclasses import dataclass
+from pathlib import Path
+
+
+MAGIC = b"EEMSBX02"
+VERSION = 2
+CASE_COUNT = 55
+NO_VOICE = 0xFFFF
+RESTORED_VOICE_FIRST_CASE = 1
+RESTORED_VOICE_LAST_CASE = 24
+
+
+ at dataclass(frozen=True)
+class ScrapbookRecord:
+    pic_id: int
+    pic_x: int
+    pic_y: int
+    balloon: int
+    balloon_x: int
+    balloon_y: int
+    jake_text: bytes
+    jenny_text: bytes
+    voice_jake: int = NO_VOICE
+    voice_jenny: int = NO_VOICE
+    voice_nancy: int = NO_VOICE
+
+
+def read_u16(data: bytes, offset: int) -> int:
+    if offset < 0 or offset + 2 > len(data):
+        return 0
+    return struct.unpack_from("<H", data, offset)[0]
+
+
+def c_string(data: bytes, offset: int) -> bytes:
+    if offset <= 0 or offset >= len(data):
+        return b""
+    end = data.find(b"\0", offset)
+    if end < 0:
+        end = len(data)
+    return data[offset:end]
+
+
+def valid_floppy_record(data: bytes, offset: int) -> bool:
+    if offset < 0 or offset + 11 > len(data):
+        return False
+    return offset + 11 + data[offset + 10] <= len(data)
+
+
+def sorted_case_numbers(data_dir: Path) -> list[int]:
+    out: list[int] = []
+    for path in data_dir.glob("M*.BIN"):
+        stem = path.stem
+        if len(stem) > 1 and stem[1:].isdigit():
+            out.append(int(stem[1:]))
+    return sorted(out)
+
+
+def extract_floppy_tail(path: Path) -> list[ScrapbookRecord]:
+    data = path.read_bytes()
+    if len(data) < 0x14 or read_u16(data, 0) <= 0x100:
+        raise ValueError(f"{path} is not an EEM floppy mystery file")
+
+    notes_off = read_u16(data, 0x08)
+    solved_off = read_u16(data, 0x12)
+    if solved_off <= 0 or solved_off >= len(data):
+        raise ValueError(f"{path} has no valid solved chain")
+
+    count = data[solved_off]
+    pos = solved_off + 1
+    record_offsets: list[int] = []
+    for _ in range(count):
+        if not valid_floppy_record(data, pos):
+            raise ValueError(f"{path} has a malformed solved-chain record at 0x{pos:04x}")
+        record_offsets.append(pos)
+        pos += 11 + data[pos + 10]
+
+    if len(record_offsets) < 3:
+        return []
+
+    records: list[ScrapbookRecord] = []
+    for offset in record_offsets[-3:]:
+        text_count = data[offset + 10]
+        if text_count != 1:
+            raise ValueError(
+                f"{path} tail record at 0x{offset:04x} has {text_count} text entries"
+            )
+
+        note_idx = data[offset + 11] & 0x7F
+        note_entry = notes_off + note_idx * 7
+        if note_entry + 6 > len(data):
+            raise ValueError(f"{path} tail record references invalid note {note_idx}")
+
+        records.append(ScrapbookRecord(
+            pic_id=read_u16(data, offset + 0),
+            pic_x=read_u16(data, offset + 2),
+            pic_y=data[offset + 4],
+            balloon=data[offset + 5],
+            balloon_x=read_u16(data, offset + 6),
+            balloon_y=data[offset + 8],
+            jake_text=c_string(data, read_u16(data, note_entry + 2)),
+            jenny_text=c_string(data, read_u16(data, note_entry + 4)),
+        ))
+
+    return records
+
+
+def sdx_entry_count(cd_dir: Path, case_num: int) -> int:
+    path = cd_dir / f"M{case_num}.SDX"
+    if not path.exists():
+        return 0
+    return path.stat().st_size // 12
+
+
+def valid_cd_clue_block(data: bytes, offset: int) -> bool:
+    if offset in (0, 0xFFFF) or offset + 4 > len(data):
+        return False
+    count = read_u16(data, offset)
+    return 0 < count <= 64 and offset + 4 + count * 62 <= len(data)
+
+
+def add_voice_ref(used: set[int], raw_voice: int, max_entries: int) -> None:
+    if raw_voice in (0, 0xFFFF):
+        return
+    if raw_voice <= max_entries:
+        used.add(raw_voice - 1)
+
+
+def collect_cd_voice_usage(
+    cd_dir: Path,
+    case_num: int,
+) -> tuple[set[int], list[int], list[int]]:
+    path = cd_dir / f"M{case_num}.BIN"
+    if not path.exists():
+        return set(), [], []
+
+    data = path.read_bytes()
+    max_entries = sdx_entry_count(cd_dir, case_num)
+    used: set[int] = set()
+    solved_jenny: list[int] = []
+    solved_jake: list[int] = []
+
+    def parse_clue_block(offset: int, solved: bool = False) -> None:
+        if not valid_cd_clue_block(data, offset):
+            return
+        count = read_u16(data, offset)
+        for entry_idx in range(count):
+            entry = offset + 4 + entry_idx * 62
+            voice_jenny = read_u16(data, entry + 0x18)
+            voice_jake = read_u16(data, entry + 0x1A)
+
+            # Original _DisplayClue gates voice playback on the Jenny/default
+            # slot; a Jake-only value with a zero Jenny slot is not played.
+            if voice_jenny in (0, 0xFFFF):
+                continue
+            add_voice_ref(used, voice_jenny, max_entries)
+            if solved and voice_jenny <= max_entries:
+                solved_jenny.append(voice_jenny - 1)
+
+            add_voice_ref(used, voice_jake, max_entries)
+            if solved and voice_jake not in (0, 0xFFFF) and voice_jake <= max_entries:
+                solved_jake.append(voice_jake - 1)
+
+    parse_clue_block(read_u16(data, 0x00) + 4)
+    parse_clue_block(read_u16(data, 0x10), solved=True)
+
+    site_index_off = read_u16(data, 0x06)
+    num_sites = read_u16(data, 0x14)
+    for site_idx in range(min(num_sites, 128)):
+        site_index = site_index_off + site_idx * 6
+        if site_index + 6 > len(data):
+            break
+        site_data_off = read_u16(data, site_index)
+        parse_clue_block(read_u16(data, site_index + 2))
+
+        if site_data_off + 8 > len(data):
+            continue
+        hotspot_count = read_u16(data, site_data_off + 6)
+        hotspot_table = read_u16(data, site_index + 4)
+        for hot_idx in range(min(hotspot_count, 300)):
+            hotspot = hotspot_table + hot_idx * 14
+            if hotspot + 14 > len(data):
+                break
+            parse_clue_block(read_u16(data, hotspot + 8))
+
+    gallery_off = read_u16(data, 0x0C)
+    num_suspects = read_u16(data, 0x1A)
+    if gallery_off and gallery_off + num_suspects * 0x46 <= len(data):
+        for suspect_idx in range(num_suspects):
+            entry = gallery_off + suspect_idx * 0x46
+            add_voice_ref(used, read_u16(data, entry + 4), max_entries)
+            add_voice_ref(used, read_u16(data, entry + 6), max_entries)
+
+    kd_text_off = read_u16(data, 0x0E)
+    if kd_text_off and kd_text_off + 0x12 < len(data):
+        digital = kd_text_off + 0x12
+        for kdspeak in (0, 1, 2, 3, 4, 5, 7, 8, 9, 10, 11):
+            for slot in (kdspeak * 2 + 1, kdspeak * 2 + 2):
+                raw_voice = read_u16(data, digital + slot * 2)
+                add_voice_ref(used, raw_voice, max_entries)
+
+    return used, solved_jenny, solved_jake
+
+
+def contiguous_unused_after(used: set[int], sequence: list[int], max_entries: int) -> list[int]:
+    if not sequence:
+        return []
+
+    out: list[int] = []
+    entry = sequence[-1] + 1
+    while entry < max_entries and entry not in used:
+        out.append(entry)
+        entry += 1
+    return out
+
+
+def sound_duration(cd_dir: Path, case_num: int, entry: int) -> float:
+    sdx_path = cd_dir / f"M{case_num}.SDX"
+    sdb_path = cd_dir / f"M{case_num}.SDB"
+    if not sdx_path.exists() or not sdb_path.exists():
+        return 0.0
+
+    sdx_data = sdx_path.read_bytes()
+    if entry < 0 or entry * 12 + 12 > len(sdx_data):
+        return 0.0
+
+    offset = struct.unpack_from("<I", sdx_data, entry * 12)[0]
+    uncompressed_size = struct.unpack_from("<I", sdx_data, entry * 12 + 8)[0]
+    with sdb_path.open("rb") as sdb:
+        sdb.seek(offset)
+        tc_raw = sdb.read(1)
+    if not tc_raw:
+        return 0.0
+
+    tc = tc_raw[0]
+    sample_rate = 1000000 // (256 - tc) if tc < 0xFF else 44100
+    return uncompressed_size / sample_rate if sample_rate else 0.0
+
+
+def find_nancy_voice(
+    cd_dir: Path,
+    case_num: int,
+    used: set[int],
+    partner_voices: set[int],
+    first_partner_voice: int,
+) -> int:
+    max_entries = sdx_entry_count(cd_dir, case_num)
+
+    for entry in range(max(0, first_partner_voice), max_entries):
+        if entry in used or entry in partner_voices:
+            continue
+        if sound_duration(cd_dir, case_num, entry) < 1.2:
+            continue
+        return entry
+
+    return NO_VOICE
+
+
+def attach_cd_voices(cd_dir: Path, case_num: int,
+                     records: list[ScrapbookRecord]) -> list[ScrapbookRecord]:
+    if len(records) < 3:
+        return records
+    if not RESTORED_VOICE_FIRST_CASE <= case_num <= RESTORED_VOICE_LAST_CASE:
+        return records
+
+    used, solved_jenny, solved_jake = collect_cd_voice_usage(cd_dir, case_num)
+    max_entries = sdx_entry_count(cd_dir, case_num)
+    jenny_candidates = contiguous_unused_after(used, solved_jenny, max_entries)
+    jake_candidates = contiguous_unused_after(used, solved_jake, max_entries)
+
+    voice_jenny = jenny_candidates[:2] if len(jenny_candidates) >= 2 else []
+    voice_jake = jake_candidates[:2] if len(jake_candidates) >= 2 else []
+    partner_voices = set(voice_jenny) | set(voice_jake)
+    voice_nancy = NO_VOICE
+    if len(voice_jenny) >= 2 and len(voice_jake) >= 2:
+        first_partner_voice = min(voice_jenny[0], voice_jake[0])
+        voice_nancy = find_nancy_voice(cd_dir, case_num, used,
+                                       partner_voices, first_partner_voice)
+
+    out: list[ScrapbookRecord] = []
+    for idx, record in enumerate(records):
+        extra_idx = idx - 1
+        out.append(ScrapbookRecord(
+            pic_id=record.pic_id,
+            pic_x=record.pic_x,
+            pic_y=record.pic_y,
+            balloon=record.balloon,
+            balloon_x=record.balloon_x,
+            balloon_y=record.balloon_y,
+            jake_text=record.jake_text,
+            jenny_text=record.jenny_text,
+            voice_jake=voice_jake[extra_idx] if 0 <= extra_idx < len(voice_jake) else NO_VOICE,
+            voice_jenny=voice_jenny[extra_idx] if 0 <= extra_idx < len(voice_jenny) else NO_VOICE,
+            voice_nancy=voice_nancy if idx == 0 else NO_VOICE,
+        ))
+    return out
+
+
+def encode_records(cases: list[list[ScrapbookRecord]]) -> bytes:
+    table_size = CASE_COUNT * 8
+    header = bytearray()
+    header += MAGIC
+    header += struct.pack("<HH", VERSION, CASE_COUNT)
+    table = bytearray(table_size)
+    body = bytearray()
+
+    for case_num, records in enumerate(cases):
+        if not records:
+            continue
+
+        offset = len(header) + table_size + len(body)
+        struct.pack_into("<IHH", table, case_num * 8, offset, len(records), 0)
+        for record in records:
+            if len(record.jake_text) > 0xFFFF or len(record.jenny_text) > 0xFFFF:
+                raise ValueError(f"case {case_num} contains a text string longer than 65535 bytes")
+            body += struct.pack(
+                "<HHBBHBBHHHHH",
+                record.pic_id,
+                record.pic_x,
+                record.pic_y,
+                record.balloon,
+                record.balloon_x,
+                record.balloon_y,
+                0,
+                record.voice_jake,
+                record.voice_jenny,
+                record.voice_nancy,
+                len(record.jake_text),
+                len(record.jenny_text),
+            )
+            body += record.jake_text
+            body += record.jenny_text
+
+    return bytes(header + table + body)
+
+
+def create_scrapbook_extra(floppy_dir: Path, cd_dir: Path, out_path: Path) -> None:
+    cases: list[list[ScrapbookRecord]] = [[] for _ in range(CASE_COUNT)]
+    voiced_cases = 0
+    nancy_cases = 0
+
+    for case_num in sorted_case_numbers(floppy_dir):
+        if case_num == 0 or case_num >= CASE_COUNT:
+            continue
+        records = extract_floppy_tail(floppy_dir / f"M{case_num}.BIN")
+        records = attach_cd_voices(cd_dir, case_num, records)
+        if any(record.voice_jake != NO_VOICE or record.voice_jenny != NO_VOICE
+               for record in records):
+            voiced_cases += 1
+        if any(record.voice_nancy != NO_VOICE for record in records):
+            nancy_cases += 1
+        cases[case_num] = records
+
+    out_path.parent.mkdir(parents=True, exist_ok=True)
+    out_path.write_bytes(encode_records(cases))
+    print(f"Wrote {out_path}")
+    print(f"Cases with text: {sum(1 for records in cases if records)}")
+    print(f"Cases with mapped CD voice clips: {voiced_cases}")
+    print(f"Cases with mapped Nancy clips: {nancy_cases}")
+
+
+def main() -> None:
+    parser = argparse.ArgumentParser(
+        description="Create EEM SCRAPBK_EXTRA.ANI from floppy mystery data."
+    )
+    parser.add_argument(
+        "--floppy-dir",
+        type=Path,
+        default=Path("../../eem-full-game/floppy/EAKIDS/EEM"),
+        help="Directory containing English floppy M*.BIN data.",
+    )
+    parser.add_argument(
+        "--cd-dir",
+        type=Path,
+        default=Path("../../eem-full-game/cd"),
+        help="Directory containing CD M*.BIN/M*.SDX data for voice mapping.",
+    )
+    parser.add_argument(
+        "--out",
+        type=Path,
+        default=Path("files/eem/SCRAPBK_EXTRA.ANI"),
+        help="Output file inside devtools/create_eem/files/eem.",
+    )
+    args = parser.parse_args()
+
+    create_scrapbook_extra(args.floppy_dir, args.cd_dir, args.out)
+
+
+if __name__ == "__main__":
+    main()
diff --git a/devtools/create_eem/files/eem/SCRAPBK_EXTRA.ANI b/devtools/create_eem/files/eem/SCRAPBK_EXTRA.ANI
new file mode 100644
index 00000000000..add4d3b906c
Binary files /dev/null and b/devtools/create_eem/files/eem/SCRAPBK_EXTRA.ANI differ
diff --git a/devtools/create_eem/files/eem/version.txt b/devtools/create_eem/files/eem/version.txt
new file mode 100644
index 00000000000..d3827e75a5c
--- /dev/null
+++ b/devtools/create_eem/files/eem/version.txt
@@ -0,0 +1 @@
+1.0
diff --git a/dists/engine-data/README b/dists/engine-data/README
index 1a68494852b..15c2dec6087 100644
--- a/dists/engine-data/README
+++ b/dists/engine-data/README
@@ -27,6 +27,10 @@ Those informations were stored in the original executables.
 darkseed.dat:
 This file contains essential game data used by the Darkseed engine.
 
+eem.dat:
+This file contains floppy-only scrapbook dialog data used by the Eagle Eye
+Mysteries engine.
+
 drascula.dat:
 This file contains essential game data used by the Drascula engine.
 
diff --git a/dists/engine-data/eem.dat b/dists/engine-data/eem.dat
new file mode 100644
index 00000000000..ae70fa2fe91
Binary files /dev/null and b/dists/engine-data/eem.dat differ
diff --git a/dists/engine-data/engine_data.mk b/dists/engine-data/engine_data.mk
index dbe5527a65d..25341375dbd 100644
--- a/dists/engine-data/engine_data.mk
+++ b/dists/engine-data/engine_data.mk
@@ -13,6 +13,9 @@ endif
 ifdef ENABLE_DARKSEED
 DIST_FILES_LIST += dists/engine-data/darkseed.dat
 endif
+ifdef ENABLE_EEM
+DIST_FILES_LIST += dists/engine-data/eem.dat
+endif
 ifdef ENABLE_DRASCULA
 DIST_FILES_LIST += dists/engine-data/drascula.dat
 endif
diff --git a/engines/eem/detection.cpp b/engines/eem/detection.cpp
index 17ff91e826b..fab534952a9 100644
--- a/engines/eem/detection.cpp
+++ b/engines/eem/detection.cpp
@@ -32,7 +32,7 @@ const PlainGameDescriptor eemGames[] = {
 };
 
 #define GUI_OPTIONS_EEM_FLOPPY GUIO4(GAMEOPTION_HIDE_HIGHLIGHT_BOXES, GAMEOPTION_SKIP_REPEATED_CASES, GUIO_MIDIADLIB, GUIO_MIDIMT32)
-#define GUI_OPTIONS_EEM_CD     GUIO5(GAMEOPTION_HIDE_HIGHLIGHT_BOXES, GAMEOPTION_FIT_DIALOG_BALLOONS, GAMEOPTION_SKIP_REPEATED_CASES, GUIO_MIDIADLIB, GUIO_MIDIMT32)
+#define GUI_OPTIONS_EEM_CD     GUIO6(GAMEOPTION_HIDE_HIGHLIGHT_BOXES, GAMEOPTION_FIT_DIALOG_BALLOONS, GAMEOPTION_SKIP_REPEATED_CASES, GAMEOPTION_RESTORED_CONTENT, GUIO_MIDIADLIB, GUIO_MIDIMT32)
 
 const ADGameDescription gameDescriptions[] = {
 	{
diff --git a/engines/eem/detection.h b/engines/eem/detection.h
index 937fbe6a4e3..cac7a171f60 100644
--- a/engines/eem/detection.h
+++ b/engines/eem/detection.h
@@ -29,6 +29,7 @@ namespace EEM {
 #define GAMEOPTION_HIDE_HIGHLIGHT_BOXES   GUIO_GAMEOPTIONS1
 #define GAMEOPTION_FIT_DIALOG_BALLOONS    GUIO_GAMEOPTIONS2
 #define GAMEOPTION_SKIP_REPEATED_CASES    GUIO_GAMEOPTIONS3
+#define GAMEOPTION_RESTORED_CONTENT       GUIO_GAMEOPTIONS4
 
 enum EEMDebugChannels {
 	kDebugGeneral = 1 << 0,
diff --git a/engines/eem/eem.cpp b/engines/eem/eem.cpp
index 2ad4b74f687..f6a17a87ca9 100644
--- a/engines/eem/eem.cpp
+++ b/engines/eem/eem.cpp
@@ -21,6 +21,7 @@
 
 #include "common/debug.h"
 #include "common/debug-channels.h"
+#include "common/engine_data.h"
 #include "common/error.h"
 #include "common/events.h"
 #include "common/file.h"
@@ -198,6 +199,7 @@ EEMEngine::EEMEngine(OSystem *syst, const ADGameDescription *gameDesc)
 	ConfMan.registerDefault("hide_highlight_boxes", false);
 	ConfMan.registerDefault("fit_dialog_balloons", false);
 	ConfMan.registerDefault("skip_repeated_cases", false);
+	ConfMan.registerDefault("restored_content", false);
 
 	_variant = (gameDesc && gameDesc->extra &&
 				Common::String(gameDesc->extra).contains("Floppy"))
@@ -288,6 +290,17 @@ Common::Error EEMEngine::run() {
 	// _SetMode13X @ 1000:0358 ??? VGA mode 13h.
 	initGraphics(kScreenWidth, kScreenHeight);
 
+	if (!isFloppy() && ConfMan.getBool("restored_content")) {
+		Common::U32String engineDataError;
+		if (!Common::load_engine_data("eem.dat", "eem", 1, 0,
+									  engineDataError)) {
+			warning("EEM restored content unavailable: %s",
+					Common::String(engineDataError).c_str());
+		} else {
+			_restoredContentDataLoaded = true;
+		}
+	}
+
 	if (!openArchives())
 		return Common::Error(Common::kReadingFailed, "EEM archive open failed");
 
diff --git a/engines/eem/eem.h b/engines/eem/eem.h
index e2ebb1151d0..8b44120f0bc 100644
--- a/engines/eem/eem.h
+++ b/engines/eem/eem.h
@@ -332,6 +332,7 @@ private:
 	/// (`_DrawGallery_Floppy @ 154e:0050`).
 	void floppyKDHint(uint kdSlot, const byte *kdIdx,
 					  const byte *bufBase, uint32 mysSize);
+	void displayScrapbookExtra(uint mysteryNum);
 	void accuseDrawGallery(int highlighted,
 						   Common::Array<Common::Rect> &rects,
 						   Common::Array<int> &suspects, uint8 num,
@@ -531,6 +532,8 @@ private:
 	/// Lives on the engine because PDA/gallery destroys+recreates SiteScreen.
 	int _lastSiteArrivalAnim = -1;
 
+	bool _restoredContentDataLoaded = false;
+
 	/// `MIDI.C` family (`_MIDIPlayFile`/`_MIDIPlay`/`_StopMIDI`/
 	/// `_StartTravelMusic` @ 20a2:00e2-05c9). Constructed lazily in `run()`.
 	MusicPlayer *_music = nullptr;
diff --git a/engines/eem/metaengine.cpp b/engines/eem/metaengine.cpp
index d3efe9c89cc..c2f01eafc03 100644
--- a/engines/eem/metaengine.cpp
+++ b/engines/eem/metaengine.cpp
@@ -66,6 +66,17 @@ const ADExtraGuiOptionsMap optionsList[] = {
 			0
 		}
 	},
+	{
+		GAMEOPTION_RESTORED_CONTENT,
+		{
+			_s("Enable restored content"),
+			_s("Restore floppy release extras in the CD version, including pre-scrapbook conversations and first-try badges."),
+			"restored_content",
+			false,
+			0,
+			0
+		}
+	},
 
 	AD_EXTRA_GUI_OPTIONS_TERMINATOR
 };
diff --git a/engines/eem/ui.cpp b/engines/eem/ui.cpp
index 98960cd0b83..803cc20cf40 100644
--- a/engines/eem/ui.cpp
+++ b/engines/eem/ui.cpp
@@ -20,6 +20,7 @@
  */
 
 #include "common/debug.h"
+#include "common/config-manager.h"
 #include "common/events.h"
 #include "common/file.h"
 #include "common/path.h"
@@ -204,6 +205,16 @@ constexpr int kActionScreenDecorY = 0x87;
 constexpr byte kChooserCycleStart = 0x6f;
 constexpr byte kChooserCycleEnd = 0x73;
 constexpr uint32 kChooserCycleMillis = 100;
+const char kScrapbookExtraFilename[] = "SCRAPBK_EXTRA.ANI";
+const byte kScrapbookExtraMagic[] = {
+	'E', 'E', 'M', 'S', 'B', 'X', '0', '2'
+};
+constexpr uint16 kScrapbookExtraVersion = 2;
+constexpr uint16 kScrapbookExtraCaseCount = 55;
+constexpr uint16 kScrapbookExtraNoVoice = 0xFFFF;
+constexpr uint16 kScrapbookExtraMaxRecords = 16;
+constexpr uint kRestoredContentFirstMystery = 1;
+constexpr uint kRestoredContentLastMystery = 0x18;
 
 bool notebookButtonAt(int x, int y) {
 	return kPdaHelpRect.contains(x, y) ||
@@ -234,6 +245,26 @@ bool rectListContains(const Common::Array<Common::Rect> &rects, int x, int y) {
 	return false;
 }
 
+bool readScrapbookExtraText(Common::File &file, uint16 size,
+							Common::String &out) {
+	out.clear();
+	if (size == 0)
+		return true;
+
+	Common::Array<char> buf;
+	buf.resize(size);
+	if (file.read(buf.data(), size) != size)
+		return false;
+
+	out = Common::String(buf.data(), size);
+	return true;
+}
+
+bool restoredContentVoiceAppliesTo(uint mysteryNum) {
+	return mysteryNum >= kRestoredContentFirstMystery &&
+		   mysteryNum <= kRestoredContentLastMystery;
+}
+
 bool gallerySlotAt(const Common::Array<Common::Rect> &rects,
 				   const Common::Array<int> &suspects, int x, int y) {
 	for (uint i = 0; i < rects.size() && i < suspects.size(); i++) {
@@ -1087,7 +1118,8 @@ int EEMEngine::doShowEnding(uint num, bool firstPage) {
 		return 0;
 
 	const bool showFirstTryBadge =
-		num < sizeof(_mysteriesSolved) && _mysteriesSolved[num] == 2;
+		num < sizeof(_mysteriesSolved) && _mysteriesSolved[num] == 2 &&
+		(floppyEnding || ConfMan.getBool("restored_content"));
 	Picture firstTryBadge;
 	const bool haveFirstTryBadge =
 		showFirstTryBadge &&
@@ -4343,6 +4375,8 @@ void EEMEngine::doAccuse() {
 		playAnm(Common::Path("SCRAPBK.ANI"), 120,
 				/* holdLastFrame= */ false);
 
+		displayScrapbookExtra(mn);
+
 		// `_ShowOneScrap @ 1f78:0773` = `_DisplayEnding(num, 1)`.
 		doShowEnding(mn);
 
@@ -4438,6 +4472,146 @@ void EEMEngine::floppyKDHint(uint kdSlot, const byte *kdIdx,
 	}
 }
 
+void EEMEngine::displayScrapbookExtra(uint mysteryNum) {
+	if (isFloppy() || !ConfMan.getBool("restored_content") ||
+		!_restoredContentDataLoaded ||
+		mysteryNum >= kScrapbookExtraCaseCount || !_font.isLoaded())
+		return;
+
+	Common::File file;
+	if (!file.open(Common::Path(kScrapbookExtraFilename))) {
+		warning("EEM restored content unavailable: %s missing",
+				kScrapbookExtraFilename);
+		return;
+	}
+
+	byte magic[sizeof(kScrapbookExtraMagic)];
+	if (file.read(magic, sizeof(magic)) != sizeof(magic) ||
+		memcmp(magic, kScrapbookExtraMagic, sizeof(magic)) != 0)
+		return;
+
+	const uint16 version = file.readUint16LE();
+	const uint16 caseCount = file.readUint16LE();
+	if (version != kScrapbookExtraVersion ||
+		caseCount > kScrapbookExtraCaseCount ||
+		mysteryNum >= caseCount)
+		return;
+
+	const uint32 tableOffset = (uint32)sizeof(kScrapbookExtraMagic) + 4 +
+		mysteryNum * 8;
+	if (!file.seek(tableOffset))
+		return;
+
+	const uint32 recordsOffset = file.readUint32LE();
+	const uint16 recordCount = file.readUint16LE();
+	(void)file.readUint16LE();
+	if (recordsOffset == 0 || recordCount == 0 ||
+		recordCount > kScrapbookExtraMaxRecords ||
+		!file.seek(recordsOffset))
+		return;
+
+	Graphics::ManagedSurface bg(kScreenWidth, kScreenHeight,
+		Graphics::PixelFormat::createFormatCLUT8());
+	bg.clear();
+	if (Graphics::Surface *screen = g_system->lockScreen()) {
+		bg.simpleBlitFrom(*screen);
+		g_system->unlockScreen();
+	}
+
+	for (uint recordIdx = 0; recordIdx < recordCount && !shouldQuit();
+		 recordIdx++) {
+		const uint16 picId = file.readUint16LE();
+		const uint16 picX = file.readUint16LE();
+		const uint8 picY = file.readByte();
+		const uint8 balloonRaw = file.readByte();
+		const uint16 balloonX = file.readUint16LE();
+		const uint8 balloonY = file.readByte();
+		(void)file.readByte();
+		const uint16 voiceJake = file.readUint16LE();
+		const uint16 voiceJenny = file.readUint16LE();
+		const uint16 voiceNancy = file.readUint16LE();
+		const uint16 jakeTextSize = file.readUint16LE();
+		const uint16 jennyTextSize = file.readUint16LE();
+
+		if (file.eos() || file.err())
+			return;
+
+		Common::String rawJake;
+		Common::String rawJenny;
+		if (!readScrapbookExtraText(file, jakeTextSize, rawJake) ||
+			!readScrapbookExtraText(file, jennyTextSize, rawJenny))
+			return;
+
+		Common::String text = parseString(
+			_partner == kPartnerJake ? rawJake : rawJenny,
+			_playerName, _partner);
+		if (text.empty())
+			continue;
+
+		Graphics::ManagedSurface scratch(kScreenWidth, kScreenHeight,
+			Graphics::PixelFormat::createFormatCLUT8());
+		scratch.simpleBlitFrom(*bg.surfacePtr());
+
+		if (picId != 0 && picId != 0xFFFF) {
+			Picture pic;
+			if (_picsArchive.getPicture(picId, pic) &&
+				picX < kScreenWidth && picY < kScreenHeight) {
+				const byte transp = (byte)(pic.flags >> 8);
+				scratch.transBlitFrom(pic.surface,
+									  Common::Point(picX, picY),
+									  (uint32)transp);
+			}
+		}
+
+		const uint16 fittedBalloon = fitBalloonToText(balloonRaw, text);
+		const uint16 balloonId = fittedBalloon & 0x7F;
+		const bool flipBalloon = (fittedBalloon & 0x80) != 0;
+		Picture balloon;
+		const bool haveBalloon = balloonRaw != 0xFF &&
+			_balloonArchive.size() > balloonId &&
+			_balloonArchive.loadEntry(balloonId, balloon);
+
+		uint16 textXInset = 5;
+		uint16 textYInset = 4;
+		uint16 textWidth = 155;
+		if (haveBalloon) {
+			const byte transp = (byte)(balloon.flags >> 8);
+			scratch.transBlitFrom(balloon.surface,
+								  Common::Point(balloonX, balloonY),
+								  (uint32)transp, flipBalloon);
+			getBalloonInsets(balloonId, textXInset, textYInset, textWidth);
+		}
+
+		_font.drawWordWrapped(&scratch, balloonX + textXInset,
+							  balloonY + textYInset,
+							  MAX<int>(8, (int)textWidth), text,
+							  haveBalloon ? 0 : 0xF);
+
+		g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
+								   0, 0, kScreenWidth, kScreenHeight);
+		g_system->updateScreen();
+
+		if (_audio && restoredContentVoiceAppliesTo(mysteryNum)) {
+			uint16 voice = kScrapbookExtraNoVoice;
+			if (recordIdx == 0 && voiceNancy != kScrapbookExtraNoVoice) {
+				voice = voiceNancy;
+			} else if (recordIdx == 1) {
+				voice = (_partner == kPartnerJake) ? voiceJake : voiceJenny;
+			} else {
+				voice = (_partner == kPartnerJake) ? voiceJenny : voiceJake;
+			}
+			if (voice != kScrapbookExtraNoVoice)
+				_audio->spoolSound(voice);
+		}
+
+		const bool skipRest = floppyDialogWaitForClick();
+		if (_audio)
+			_audio->stopSpool();
+		if (skipRest)
+			return;
+	}
+}
+
 void EEMEngine::accuseDrawGallery(int highlighted,
 								  Common::Array<Common::Rect> &rects,
 								  Common::Array<int> &suspects, uint8 num,


Commit: 02418babc46f9de3402039152d424abedec7413e
    https://github.com/scummvm/scummvm/commit/02418babc46f9de3402039152d424abedec7413e
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:21:18+02:00

Commit Message:
EEM: fixes and missing code for the floppy release

Changed paths:
    engines/eem/eem.h
    engines/eem/graphics.cpp
    engines/eem/site.cpp
    engines/eem/site.h


diff --git a/engines/eem/eem.h b/engines/eem/eem.h
index 8b44120f0bc..3a405a74f95 100644
--- a/engines/eem/eem.h
+++ b/engines/eem/eem.h
@@ -193,6 +193,10 @@ public:
 	/// Locates dialog records in site_data[+6] and dispatches them.
 	void displayFloppyHotspotDialog(uint siteNum, uint hotIdx);
 
+	/// `_HotspotSearched_Floppy @ 22dc:096c`. Walks the per-hotspot dialog
+	/// records and returns whether the hotspot's searched text index was seen.
+	bool floppyHotspotSearched(uint siteNum, uint hotspotIdx) const;
+
 	/// Active player name (= profile-save description).
 	const Common::String &playerName() const { return _playerName; }
 
diff --git a/engines/eem/graphics.cpp b/engines/eem/graphics.cpp
index 8f8441b0c41..81c553eb35f 100644
--- a/engines/eem/graphics.cpp
+++ b/engines/eem/graphics.cpp
@@ -124,39 +124,50 @@ uint getBalloonLineCapacity(uint16 balloonId, int lineH) {
 	return MAX<uint>(1, ((int)insets.indDY - (int)insets.y) / lineH + 1);
 }
 
-// FUN_22dc_096c @ 22dc:096c: walks per-site dialog records at site_data[+6]
-// to skip hotspotIdx hotspots, returns _cluesFound flag for hotspot's first
-// text index.
-bool floppyHotspotSearched(EEM::Mystery &mystery, uint siteIdx,
-								   uint hotspotIdx) {
-	const byte *site = mystery.siteData(siteIdx);
+bool EEMEngine::floppyHotspotSearched(uint siteIdx, uint hotspotIdx) const {
+	// FUN_22dc_096c @ 22dc:096c: walks per-site dialog records at
+	// site_data[+6] to skip hotspotIdx hotspots, then returns _TextSeen for
+	// the selected hotspot's searched text index.
+	const byte *site = _mystery.siteData(siteIdx);
 	if (!site)
 		return false;
 	const uint16 dlgListOff = READ_LE_UINT16(site + 6);
-	const byte *bufBase = mystery.blobAt(0);
-	if (!bufBase || dlgListOff == 0 || dlgListOff >= mystery.dataSize())
+	const byte *bufBase = _mystery.blobAt(0);
+	const uint32 dsz = _mystery.dataSize();
+	if (!bufBase || dlgListOff == 0 || dlgListOff >= dsz)
 		return false;
 	uint32 off = dlgListOff;
 	for (uint h = 0; h < hotspotIdx; h++) {
-		const byte *rec = bufBase + off;
-		off += 11u + (uint)rec[10];
-		if (off >= mystery.dataSize())
+		if (off + 10 >= dsz)
+			return false;
+		const uint32 mainLen = 11u + (uint)bufBase[off + 10];
+		off += mainLen;
+		if (off >= dsz)
 			return false;
 		const uint contCount = (uint)(bufBase[off] & 0x7F);
 		off += 1;
 		for (uint c = 0; c < contCount; c++) {
-			const byte *cr = bufBase + off;
-			off += 11u + (uint)cr[10];
-			if (off >= mystery.dataSize())
+			if (off + 10 >= dsz)
+				return false;
+			off += 11u + (uint)bufBase[off + 10];
+			if (off >= dsz)
 				return false;
 		}
 	}
-	if (off + 11 >= mystery.dataSize())
+	if (off + 10 >= dsz)
+		return false;
+	const uint32 mainLen = 11u + (uint)bufBase[off + 10];
+	const uint32 contFlagsOff = off + mainLen;
+	if (contFlagsOff >= dsz)
+		return false;
+	uint32 searchedRecOff = off;
+	if ((bufBase[contFlagsOff] & 0x7F) != 0)
+		searchedRecOff = contFlagsOff + 1;
+	if (searchedRecOff + 11 >= dsz || bufBase[searchedRecOff + 10] == 0)
 		return false;
-	const byte *mainRec = bufBase + off;
-	const uint8 textIdx = mainRec[11] & 0x7F;
+	const uint8 textIdx = bufBase[searchedRecOff + 11] & 0x7F;
 	return textIdx < EEM::Mystery::kCluesFoundCap &&
-		   mystery._cluesFound[textIdx] != 0;
+		   _mystery._cluesFound[textIdx] != 0;
 }
 
 void EEMEngine::doHelp() {
@@ -215,7 +226,7 @@ void EEMEngine::doHelp() {
 		for (uint i = 0; i < chainCount; i++) {
 			const uint8 siteIdx    = hd[off + i * 2 + 0];
 			const uint8 hotspotIdx = hd[off + i * 2 + 1];
-			if (!floppyHotspotSearched(_mystery, siteIdx, hotspotIdx)) {
+			if (!floppyHotspotSearched(siteIdx, hotspotIdx)) {
 				anyChainUnseen = true;
 				break;
 			}
@@ -225,7 +236,7 @@ void EEMEngine::doHelp() {
 			for (uint i = 0; i < extraCount; i++) {
 				const uint8 siteIdx    = hd[extraStart + i * 2 + 0];
 				const uint8 hotspotIdx = hd[extraStart + i * 2 + 1];
-				if (!floppyHotspotSearched(_mystery, siteIdx, hotspotIdx)) {
+				if (!floppyHotspotSearched(siteIdx, hotspotIdx)) {
 					anyExtraUnseen = true;
 					break;
 				}
diff --git a/engines/eem/site.cpp b/engines/eem/site.cpp
index 7c9112d97a4..90a2bc011de 100644
--- a/engines/eem/site.cpp
+++ b/engines/eem/site.cpp
@@ -1352,7 +1352,7 @@ void SiteScreen::renderPartner(uint siteNum, uint32 tickMs) {
 	int    y;
 	if (_vm->isFloppy()) {
 		// `_DoSiteLoop_Floppy @ 1652:042b`: site_data+8 is a u16 OFFSET
-		// to a 10-byte speakerInfo struct:
+		// to `_SpeakerInfo_Floppy`; the first 10 bytes are the idle pair:
 		//   +0..1 Jake anim, +2..3 Jake X, +4 Jake Y,
 		//   +5..6 Jenny anim, +7..8 Jenny X, +9 Jenny Y.
 		const uint16 spkOff = READ_LE_UINT16(site + 8);
@@ -1410,6 +1410,44 @@ void SiteScreen::renderPartner(uint siteNum, uint32 tickMs) {
 	g_system->unlockScreen();
 }
 
+bool SiteScreen::renderFloppyHotspotPartnerPose(uint siteNum) {
+	if (!_vm || !_vm->isFloppy() || !_mystery)
+		return false;
+
+	const byte *site = _mystery->siteData(siteNum);
+	if (!site)
+		return false;
+
+	const uint16 spkOff = READ_LE_UINT16(site + 8);
+	// `_OnSiteHotspotClicked_Floppy @ 1652:017a`: after restoring the site
+	// it loads the active pair from `_SpeakerInfo_Floppy + 0x28/0x2d`.
+	const uint poseOff = (_vm->getPartnerIndex() == kPartnerJake)
+		? 0x28 : 0x2d;
+	if ((uint32)spkOff + poseOff + 5 > _mystery->dataSize())
+		return false;
+
+	const byte *pose = _mystery->blobAt((uint32)spkOff + poseOff);
+	if (!pose)
+		return false;
+
+	const uint16 animId = READ_LE_UINT16(pose + 0);
+	const int x = (int)READ_LE_UINT16(pose + 2);
+	const int y = (int)pose[4];
+
+	Animation anim;
+	if (!_vm->getAni().loadAnimation(animId, anim) || anim.empty())
+		return false;
+
+	Graphics::Surface *screen = g_system->lockScreen();
+	if (!screen)
+		return false;
+
+	const uint frameIdx = partnerFrameAtTick(animId, (uint)anim.size(), 0);
+	blitAnimFrameAnchored(screen, anim[frameIdx], x, y);
+	g_system->unlockScreen();
+	return true;
+}
+
 void SiteScreen::renderBackground(uint siteNum) {
 	// `_BuildBackground(sitepic, 0x42, 0x14)` from `_DoSiteLoop @
 	// 168d:03f4` / `_DisplayCorrect`:
@@ -1464,6 +1502,17 @@ void bumpHotspotEdgeColor(byte &color) {
 	color = (next > 0xFE) ? (byte)0xF9 : next;
 }
 
+byte currentWhitePaletteIndex(byte fallback) {
+	byte palette[256 * 3];
+	g_system->getPaletteManager()->grabPalette(palette, 0, 256);
+	for (uint i = 0; i < 256; i++) {
+		const byte *rgb = palette + i * 3;
+		if (rgb[0] >= 0xFC && rgb[1] >= 0xFC && rgb[2] >= 0xFC)
+			return (byte)i;
+	}
+	return fallback;
+}
+
 void SiteScreen::renderHotspots(uint siteNum) {
 	// `_DrawSearchButtons`. Port adds optional "hide hint" setting.
 	if (ConfMan.getBool("hide_highlight_boxes"))
@@ -1502,9 +1551,14 @@ void SiteScreen::renderHotspots(uint siteNum) {
 	//   +0xc..d extra         (CD cursor ID for `_SwitchMouse`; shipped = 0)
 	// Seen key = the +0xa ordinal (so unrelated hotspots on later sites
 	// don't inherit the first site's seen state after travel/reload).
-	// Floppy = 8-byte plain rect only; seen key falls back to row index.
+	// Floppy = 8-byte plain rect only; searched state is derived by
+	// walking the dialog record list, like `_HotspotSearched_Floppy`.
 	const bool floppy = _vm && _vm->isFloppy();
 	const uint stride = floppy ? 8 : 14;
+	// The floppy SITEPALS has at least one searchable site where 0xFF is
+	// yellow. The CD corrected that palette data; for floppy, draw with an
+	// existing white entry from the current palette instead of changing it.
+	const byte searchedColor = floppy ? currentWhitePaletteIndex(0xFF) : 0xFF;
 	for (uint i = 0; i < count; i++) {
 		const byte *r = spots + i * stride;
 		const int16 x1 = (int16)READ_LE_UINT16(r + 0);
@@ -1514,12 +1568,17 @@ void SiteScreen::renderHotspots(uint siteNum) {
 		const Common::Rect rect(MAX<int>(0, x1), MAX<int>(0, y1),
 								MIN<int>(screen->w, x2),
 								MIN<int>(screen->h, y2));
-		const uint seenKey = floppy ? i : READ_LE_UINT16(r + 0xa);
-		const bool seen = seenKey < Mystery::kHotSpotsCap &&
-						   _mystery->_hotSpotsSeen[seenKey];
+		bool seen = false;
+		if (floppy) {
+			seen = _vm->floppyHotspotSearched(siteNum, i);
+		} else {
+			const uint seenKey = READ_LE_UINT16(r + 0xa);
+			seen = seenKey < Mystery::kHotSpotsCap &&
+				   _mystery->_hotSpotsSeen[seenKey];
+		}
 		if (seen) {
-			// `_DrawSolidRect @ 172b:0506` ??? solid 0xFF (non-cycling).
-			screen->frameRect(rect, 0xFF);
+			// `_DrawSolidRect @ 172b:0506` ??? solid, non-cycling outline.
+			screen->frameRect(rect, searchedColor);
 		} else {
 			// `_DrawRect @ 172b:03e2` ??? walk all four edges incrementing
 			// the colour per pixel through palette indices 0xF9..0xFE,
@@ -1587,20 +1646,29 @@ void SiteScreen::onHotspotClicked(uint siteNum, uint hotIdx) {
 	debugC(1, kDebugSite, "Site %u: hotspot %u clicked", siteNum, hotIdx);
 
 	// Floppy: 8-byte rects only (no clue metadata @ +0xa/+8). Dialog
-	// records live in a separate list @ `site_data[+6]`. Seen key =
-	// array index (only ordinal available).
+	// records live in a separate list @ `site_data[+6]`.
 	if (_vm->isFloppy()) {
 		if (hotIdx < Mystery::kHotSpotsCap)
 			_mystery->_hotSpotsSeen[hotIdx] = 1;
 		_mystery->_searchLocationNumber = (uint16)hotIdx;
+
 		// Snapshot `_cluesFound` before dialog ??? autosave on new clue.
 		// Floppy side-effect path is `displayFloppyDialogRecords`
 		// (clues.cpp), not `displayClue` ??? autosave must be duplicated.
 		byte before[Mystery::kCluesFoundCap];
 		memcpy(before, _mystery->_cluesFound, sizeof(before));
+
+		restoreBgSnapshot();
+		const uint32 now = g_system->getMillis();
+		renderAnimatedDrops(siteNum, now);
+		if (!renderFloppyHotspotPartnerPose(siteNum))
+			renderPartner(siteNum, now);
+		g_system->updateScreen();
+
 		_vm->setPartnerEraseBg(&_bgSnapshot);
 		_vm->displayFloppyHotspotDialog(siteNum, hotIdx);
 		_vm->setPartnerEraseBg(nullptr);
+
 		bool foundNewClue = false;
 		for (uint i = 0; i < Mystery::kCluesFoundCap; i++) {
 			if (!before[i] && _mystery->_cluesFound[i]) {
diff --git a/engines/eem/site.h b/engines/eem/site.h
index 1719e68c47f..cc1b798e453 100644
--- a/engines/eem/site.h
+++ b/engines/eem/site.h
@@ -121,6 +121,10 @@ private:
 	/// Mirrors `_GetAnimation` + `_NewAnimation` tail of `_DoSiteLoop @ 168d:03f4`.
 	void renderPartner(uint siteNum, uint32 tickMs);
 
+	/// Floppy active speaker pose shown before `_HandleHotspotClick_Floppy`.
+	/// Uses `_SpeakerInfo_Floppy + 0x28/0x2d`, replacing the idle partner.
+	bool renderFloppyHotspotPartnerPose(uint siteNum);
+
 	/// renderStaticDrops: `_AddDrop` static decorations (Loop 2).
 	/// siteData[+0x4] count, siteData[+0xc] entries (6 bytes: {picId, x, y}).
 	void renderStaticDrops(uint siteNum);


Commit: 558a00c546844d6a51f146cf6bc3140d11283767
    https://github.com/scummvm/scummvm/commit/558a00c546844d6a51f146cf6bc3140d11283767
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:21:18+02:00

Commit Message:
EEM: use skip api instead of (void)

Changed paths:
    engines/eem/animation.cpp
    engines/eem/audio.cpp
    engines/eem/resource.cpp
    engines/eem/ui.cpp


diff --git a/engines/eem/animation.cpp b/engines/eem/animation.cpp
index c52ee496293..bf534e23d5f 100644
--- a/engines/eem/animation.cpp
+++ b/engines/eem/animation.cpp
@@ -101,12 +101,10 @@ bool ANMDecoder::open(const Common::Path &path) {
 		return false;
 	}
 
-	(void)_file.readUint16LE();        // header[+0]: ignored
+	_file.skip(2);                     // header[+0]: ignored
 	_height = _file.readUint16LE();    // header[+2]
 	_width  = _file.readUint16LE();    // header[+4]
-	(void)_file.readUint16LE();        // header[+6]
-	(void)_file.readUint16LE();        // header[+8]
-	(void)_file.readUint16LE();        // header[+10]
+	_file.skip(6);                     // header[+6..+10]: ignored
 
 	if (_width == 0 || _height == 0) {
 		warning("ANMDecoder: zero dimensions in %s", path.toString().c_str());
diff --git a/engines/eem/audio.cpp b/engines/eem/audio.cpp
index 10df7170c3c..b20f5553138 100644
--- a/engines/eem/audio.cpp
+++ b/engines/eem/audio.cpp
@@ -250,7 +250,7 @@ void AudioPlayer::spoolSound(uint num) {
 	//   byte 0 = Sound Blaster Time Constant
 	//   byte 1 = total AIL playback blocks (internal to AIL DDS; unused here)
 	const byte tc          = sdb.readByte();
-	(void)sdb.readByte(); // AIL block count, unused outside AIL
+	sdb.skip(1); // AIL block count, unused outside AIL
 
 	// SB Time Constant formula: rate = 1000000 / (256 - tc).
 	// e.g. tc=0xD2 -> 22 kHz. tc=0xFF would divide by 1 (1 MHz, nonsense); clamp.
diff --git a/engines/eem/resource.cpp b/engines/eem/resource.cpp
index deb49700d57..d8722ceb598 100644
--- a/engines/eem/resource.cpp
+++ b/engines/eem/resource.cpp
@@ -131,7 +131,7 @@ bool DBDArchive::loadEntry(uint num, Picture &out) {
 	}
 
 	// _GetFromDB @ 172b:105d. Leading u16 = frame count (always 1 for pictures).
-	(void)_dbd.readUint16LE();
+	_dbd.skip(2);
 	return readFrame(_dbd, entry.compressed != 0, out);
 }
 
diff --git a/engines/eem/ui.cpp b/engines/eem/ui.cpp
index 803cc20cf40..420c799701c 100644
--- a/engines/eem/ui.cpp
+++ b/engines/eem/ui.cpp
@@ -4504,7 +4504,7 @@ void EEMEngine::displayScrapbookExtra(uint mysteryNum) {
 
 	const uint32 recordsOffset = file.readUint32LE();
 	const uint16 recordCount = file.readUint16LE();
-	(void)file.readUint16LE();
+	file.skip(2);                  // reserved
 	if (recordsOffset == 0 || recordCount == 0 ||
 		recordCount > kScrapbookExtraMaxRecords ||
 		!file.seek(recordsOffset))
@@ -4526,7 +4526,7 @@ void EEMEngine::displayScrapbookExtra(uint mysteryNum) {
 		const uint8 balloonRaw = file.readByte();
 		const uint16 balloonX = file.readUint16LE();
 		const uint8 balloonY = file.readByte();
-		(void)file.readByte();
+		file.skip(1);              // reserved
 		const uint16 voiceJake = file.readUint16LE();
 		const uint16 voiceJenny = file.readUint16LE();
 		const uint16 voiceNancy = file.readUint16LE();


Commit: 209d590655e95cccf581ef3caafced6266617060
    https://github.com/scummvm/scummvm/commit/209d590655e95cccf581ef3caafced6266617060
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:21:19+02:00

Commit Message:
EEM: moved cleanMysterySound after sdx/sdb declarations

Changed paths:
    engines/eem/audio.cpp


diff --git a/engines/eem/audio.cpp b/engines/eem/audio.cpp
index b20f5553138..1a553b47c00 100644
--- a/engines/eem/audio.cpp
+++ b/engines/eem/audio.cpp
@@ -139,13 +139,13 @@ bool AudioPlayer::readSdxIndex(const Common::Path &sdxPath) {
 
 // _InitMysterySounds @ 202f:05cb. Strings "m%u.sdx" @ 29be:144f, "m%u.sdb" @ 29be:145b.
 bool AudioPlayer::initMysterySounds(uint mysteryNum) {
-	cleanMysterySounds();
-
 	const Common::String sdxName = Common::String::format("M%u.SDX", mysteryNum);
 	const Common::String sdbName = Common::String::format("M%u.SDB", mysteryNum);
 	const Common::Path sdxPath(sdxName);
 	const Common::Path sdbPath(sdbName);
 
+	cleanMysterySounds();
+
 	if (!readSdxIndex(sdxPath)) {
 		_sdxIndex.clear();
 		return false;


Commit: a789df41d3064cf78e1bb1d8490676d6ff5ab174
    https://github.com/scummvm/scummvm/commit/a789df41d3064cf78e1bb1d8490676d6ff5ab174
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:21:19+02:00

Commit Message:
EEM: reduced some technical comments

Changed paths:
    engines/eem/clues.cpp


diff --git a/engines/eem/clues.cpp b/engines/eem/clues.cpp
index ee88f8a8a43..f4774f6fc9d 100644
--- a/engines/eem/clues.cpp
+++ b/engines/eem/clues.cpp
@@ -316,17 +316,12 @@ void EEMEngine::doInitClues() {
 						  && _aniArchive.loadAnimation(0x19, nancy)
 						  && !nancy.empty();
 
-	// Cycle game animation once at _CheckFrameRate cadence (~7 fps, 140 ms
-	// per _UpdateAnimations call ??? `LastFrame + 0xe` cs in _InitFrameCounter
-	// @ 1a35:01ae). Original loop @ 1a35:0507:
-	//   uVar9 = 1;
-	//   while (uVar9 != gameNum) {
-	//     if (_CheckFrameRate || skipped) { _UpdateAnimations(); uVar9++; }
-	//   }
-	// So `gameNum - 1` _UpdateAnimations calls; each call advances every
+	// Cycle the game animation once at _CheckFrameRate cadence (~7 fps, 140 ms
+	// per tick; _InitFrameCounter @ 1a35:01ae). The original loop @ 1a35:0507
+	// makes `gameNum - 1` _UpdateAnimations calls, each advancing every
 	// registered slot by one script tick. _DoInitClues @ 1a35:0507/0541
-	// hard-codes the SCRIPT index to Jake's IDs (0x17/0x18/0x19) regardless
-	// of partner, so we look up scripts by those IDs unconditionally.
+	// hard-codes Jake's script IDs (0x17/0x18/0x19) regardless of partner, so
+	// we look up scripts by those IDs unconditionally.
 	if (haveGame || haveBook || haveNancy) {
 		const uint kCheckFrameRateMs = 140;
 		const uint baseFrames = haveGame ? game.size() : 8;
@@ -802,14 +797,9 @@ void EEMEngine::displayClue(const byte *clueBlock) {
 			g_system->updateScreen();
 		}
 
-		// _DisplayClue @ 2404:0833-085a ??? per-clue voice gate:
-		//   if (clue[+0x18] != 0 && voiceOn && voiceAvail) {
-		//       iVar6 = clue[+0x18];                  // Jenny default
-		//       if (Partner == 0) iVar6 = clue[+0x1a]; // Jake override
-		//       _SpoolSound(iVar6 - 1);
-		//   }
-		// Gate is on the Jenny slot regardless of partner; entries with
-		// +0x18 == 0 but +0x1a set are text-only.
+		// _DisplayClue @ 2404:0833-085a ??? per-clue voice gate. The gate is on
+		// the Jenny slot regardless of partner; entries with +0x18 == 0 but
+		// +0x1a set are text-only.
 		if (_audio) {
 			const uint16 voiceJenny = READ_LE_UINT16(c + 0x18);
 			if (voiceJenny != 0 && voiceJenny != 0xFFFF) {


Commit: c63d19aea9e9f83d26c965f851b7b7b0a8c40e90
    https://github.com/scummvm/scummvm/commit/c63d19aea9e9f83d26c965f851b7b7b0a8c40e90
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:21:19+02:00

Commit Message:
EEM: remove dead code related with fallback pointer

Changed paths:
    engines/eem/eem.cpp


diff --git a/engines/eem/eem.cpp b/engines/eem/eem.cpp
index f6a17a87ca9..e531f32c460 100644
--- a/engines/eem/eem.cpp
+++ b/engines/eem/eem.cpp
@@ -66,37 +66,6 @@ const byte kSaveBodyVer = 1;
 // option or changing save format. Set false before release.
 const bool kDebugPopulateScrapbook1AtStartup = false;
 
-// Fallback 11x16 cursor used if PIC pointer load fails.
-//   0 = transparent, 1 = black outline, 2 = white fill
-const byte kCursorBitmap[11 * 16] = {
-	1,1,0,0,0,0,0,0,0,0,0,
-	1,2,1,0,0,0,0,0,0,0,0,
-	1,2,2,1,0,0,0,0,0,0,0,
-	1,2,2,2,1,0,0,0,0,0,0,
-	1,2,2,2,2,1,0,0,0,0,0,
-	1,2,2,2,2,2,1,0,0,0,0,
-	1,2,2,2,2,2,2,1,0,0,0,
-	1,2,2,2,2,2,2,2,1,0,0,
-	1,2,2,2,2,2,2,2,2,1,0,
-	1,2,2,2,2,2,2,2,2,2,1,
-	1,2,2,2,2,2,1,0,0,0,0,
-	1,2,1,0,1,2,2,1,0,0,0,
-	1,1,0,0,1,2,2,1,0,0,0,
-	0,0,0,0,0,1,2,2,1,0,0,
-	0,0,0,0,0,1,2,2,1,0,0,
-	0,0,0,0,0,0,1,2,2,1,0
-};
-const byte kCursorPalette[] = {
-	0x00, 0x00, 0x00,
-	0x00, 0x00, 0x00,
-	0xFF, 0xFF, 0xFF
-};
-const byte kCursorInteractivePalette[] = {
-	0x00, 0x00, 0x00,
-	0xFF, 0x00, 0x00,
-	0xFF, 0xFF, 0xFF
-};
-
 void fadeCurrentPaletteToBlack(uint delayMs = 8) {
 	byte start[kPalSize];
 	byte stepPal[kPalSize];
@@ -173,23 +142,15 @@ void setInteractiveCursorPalette(const Picture &cursor, byte transparent) {
 
 void installMouseCursor(DBDArchive &pics, bool interactive) {
 	Picture cursor;
-	if (pics.getPicture(kPicMousePointer, cursor) && !cursor.surface.empty()) {
-		const byte transparent = (byte)(cursor.flags >> 8);
-		CursorMan.replaceCursor(cursor.surface.rawSurface(), 0, 0,
-								transparent);
-		if (interactive)
-			setInteractiveCursorPalette(cursor, transparent);
-		else
-			CursorMan.replaceCursorPalette(nullptr, 0, 0);
-		return;
-	}
+	if (!pics.getPicture(kPicMousePointer, cursor) || cursor.surface.empty())
+		error("EEM: mouse cursor PIC 0x%x missing", kPicMousePointer);
 
-	warning("EEM: mouse cursor PIC 0x%x missing; using fallback cursor",
-			kPicMousePointer);
-	CursorMan.replaceCursor(kCursorBitmap, 11, 16, 0, 0, 0);
-	CursorMan.replaceCursorPalette(interactive ? kCursorInteractivePalette
-											   : kCursorPalette,
-								   0, 3);
+	const byte transparent = (byte)(cursor.flags >> 8);
+	CursorMan.replaceCursor(cursor.surface.rawSurface(), 0, 0, transparent);
+	if (interactive)
+		setInteractiveCursorPalette(cursor, transparent);
+	else
+		CursorMan.replaceCursorPalette(nullptr, 0, 0);
 }
 
 EEMEngine::EEMEngine(OSystem *syst, const ADGameDescription *gameDesc)


Commit: 79bf202d8cb02e2f41f86b80aa0a71cc23b7ce12
    https://github.com/scummvm/scummvm/commit/79bf202d8cb02e2f41f86b80aa0a71cc23b7ce12
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:21:20+02:00

Commit Message:
EEM: boy -> jake, girl -> jenny

Changed paths:
    engines/eem/clues.cpp


diff --git a/engines/eem/clues.cpp b/engines/eem/clues.cpp
index f4774f6fc9d..3f70172723b 100644
--- a/engines/eem/clues.cpp
+++ b/engines/eem/clues.cpp
@@ -41,24 +41,24 @@ namespace EEM {
 
 // _DoChoosePartner @ 1a35:0756.
 const uint kPicChooseBackground = 0x8c; // _GetBackground(0x8c)
-const uint kAniBoy  = 8;                // Jake
-const uint kAniGirl = 9;                // Jenny
+const uint kAniJake  = 8;
+const uint kAniJenny = 9;
 
 // _DoHappiness @ 172b:27b5 ??? cursor X picks one of 4 rects @ 29be:030f.
 // Past rect 3 = level 4. Constexpr (Point, w, h) form to avoid a global
 // constructor (-Wglobal-constructors).
 constexpr Common::Rect kHappyZones[4] = {
-	Common::Rect(Common::Point(  0, 0),  70, 200), // far left ??? girl very happy, boy neutral
-	Common::Rect(Common::Point( 70, 0),  56, 200), // girl's column
+	Common::Rect(Common::Point(  0, 0),  70, 200), // far left ??? Jenny very happy, Jake neutral
+	Common::Rect(Common::Point( 70, 0),  56, 200), // Jenny's column
 	Common::Rect(Common::Point(126, 0),  56, 200), // middle
-	Common::Rect(Common::Point(182, 0),  53, 200), // boy's column
+	Common::Rect(Common::Point(182, 0),  53, 200), // Jake's column
 };
 
 // _NewAnimation positions @ 1a35:07b9 / 07d5.
-const int kBoyX  = 0xe2; // 226
-const int kBoyY  = 0x62; // 98
-const int kGirlX = 0x42; // 66
-const int kGirlY = 0x60; // 96
+const int kJakeX  = 0xe2; // 226
+const int kJakeY  = 0x62; // 98
+const int kJennyX = 0x42; // 66
+const int kJennyY = 0x60; // 96
 
 uint markClueBlockNotebookEntries(Mystery &mystery, const byte *clueBlock) {
 	if (!clueBlock)
@@ -84,16 +84,16 @@ uint markClueBlockNotebookEntries(Mystery &mystery, const byte *clueBlock) {
 }
 
 // _DoHappiness @ 172b:27b5 ??? per-zone sequence scripts.
-// Boy seqs @ 29be:0337 (5 ?? 0x14 bytes), girl seqs @ 29be:039b. 9 frames each;
+// Jake seqs @ 29be:0337 (5 ?? 0x14 bytes), Jenny seqs @ 29be:039b. 9 frames each;
 // the anim cells contain 10 cells = pairs of (neutral, smile) at 5 intensities.
-const uint8 kBoySeqs[5][9] = {
+const uint8 kJakeSeqs[5][9] = {
 	{ 0,0,0,0,0,0,0,1,0 }, // level 0
 	{ 2,2,2,2,2,2,2,3,2 }, // level 1
 	{ 4,4,4,4,4,4,4,5,4 }, // level 2
 	{ 6,6,6,6,6,6,7,6,6 }, // level 3
 	{ 8,8,8,8,8,8,8,8,9 }, // level 4 (cursor past zone 3)
 };
-const uint8 kGirlSeqs[5][9] = {
+const uint8 kJennySeqs[5][9] = {
 	{ 8,9,8,8,8,8,8,8,8 },
 	{ 6,6,6,7,6,6,6,6,6 },
 	{ 4,4,5,4,4,4,4,4,4 },
@@ -139,7 +139,7 @@ void blitRawToScreen(const Picture &p, int x, int y) {
 							   x, y, w, h);
 }
 
-// _DoChoosePartner @ 1a35:0756. The original places boy + girl animations
+// _DoChoosePartner @ 1a35:0756. The original places Jake + Jenny animations
 // on a backdrop and polls four click rectangles (two per character); we
 // approximate with a single split at x=160 (left=Jenny, right=Jake).
 void EEMEngine::doChoosePartner() {
@@ -149,14 +149,14 @@ void EEMEngine::doChoosePartner() {
 		return;
 	}
 
-	Animation boyAnim;
-	if (!_aniArchive.loadAnimation(kAniBoy, boyAnim) || boyAnim.empty()) {
-		warning("Boy animation (%u) load failed", kAniBoy);
+	Animation jakeAnim;
+	if (!_aniArchive.loadAnimation(kAniJake, jakeAnim) || jakeAnim.empty()) {
+		warning("Jake animation (%u) load failed", kAniJake);
 		return;
 	}
-	Animation girlAnim;
-	if (!_aniArchive.loadAnimation(kAniGirl, girlAnim) || girlAnim.empty()) {
-		warning("Girl animation (%u) load failed", kAniGirl);
+	Animation jennyAnim;
+	if (!_aniArchive.loadAnimation(kAniJenny, jennyAnim) || jennyAnim.empty()) {
+		warning("Jenny animation (%u) load failed", kAniJenny);
 		return;
 	}
 
@@ -168,16 +168,16 @@ void EEMEngine::doChoosePartner() {
 	uint seqIdx = 0;
 
 	blitAt(background, 0, 0);
-	blitAt(girlAnim[kGirlSeqs[level][seqIdx % 9] % girlAnim.size()],
-		   kGirlX, kGirlY);
-	blitAt(boyAnim [kBoySeqs [level][seqIdx % 9] % boyAnim.size()],
-		   kBoyX, kBoyY);
+	blitAt(jennyAnim[kJennySeqs[level][seqIdx % 9] % jennyAnim.size()],
+		   kJennyX, kJennyY);
+	blitAt(jakeAnim [kJakeSeqs [level][seqIdx % 9] % jakeAnim.size()],
+		   kJakeX, kJakeY);
 	g_system->updateScreen();
 
-	debugC(1, kDebugGeneral, "ChoosePartner: %u boy frames at (%d,%d), "
-		   "%u girl frames at (%d,%d)",
-		   (uint)boyAnim.size(), kBoyX, kBoyY,
-		   (uint)girlAnim.size(), kGirlX, kGirlY);
+	debugC(1, kDebugGeneral, "ChoosePartner: %u Jake frames at (%d,%d), "
+		   "%u Jenny frames at (%d,%d)",
+		   (uint)jakeAnim.size(), kJakeX, kJakeY,
+		   (uint)jennyAnim.size(), kJennyX, kJennyY);
 
 	uint32 lastTick = g_system->getMillis();
 	while (!shouldQuit()) {
@@ -189,10 +189,10 @@ void EEMEngine::doChoosePartner() {
 			lastTick = g_system->getMillis();
 			seqIdx = (seqIdx + 1) % 9;
 			blitAt(background, 0, 0);
-			blitAt(girlAnim[kGirlSeqs[level][seqIdx % 9] % girlAnim.size()],
-				   kGirlX, kGirlY);
-			blitAt(boyAnim [kBoySeqs [level][seqIdx % 9] % boyAnim.size()],
-				   kBoyX, kBoyY);
+			blitAt(jennyAnim[kJennySeqs[level][seqIdx % 9] % jennyAnim.size()],
+				   kJennyX, kJennyY);
+			blitAt(jakeAnim [kJakeSeqs [level][seqIdx % 9] % jakeAnim.size()],
+				   kJakeX, kJakeY);
 			g_system->updateScreen();
 		}
 
@@ -211,10 +211,10 @@ void EEMEngine::doChoosePartner() {
 					level = newLevel;
 					seqIdx = 0; // restart cycle so the gesture pops
 					blitAt(background, 0, 0);
-					blitAt(girlAnim[kGirlSeqs[level][seqIdx % 9] % girlAnim.size()],
-						   kGirlX, kGirlY);
-					blitAt(boyAnim [kBoySeqs [level][seqIdx % 9] % boyAnim.size()],
-						   kBoyX, kBoyY);
+					blitAt(jennyAnim[kJennySeqs[level][seqIdx % 9] % jennyAnim.size()],
+						   kJennyX, kJennyY);
+					blitAt(jakeAnim [kJakeSeqs [level][seqIdx % 9] % jakeAnim.size()],
+						   kJakeX, kJakeY);
 					g_system->updateScreen();
 				}
 			}


Commit: 8f54582d7d8cfdab891a8630ee6e5c0a6c8535d3
    https://github.com/scummvm/scummvm/commit/8f54582d7d8cfdab891a8630ee6e5c0a6c8535d3
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:21:20+02:00

Commit Message:
EEM: EEM2 proof-of-concept (intro)

Changed paths:
    engines/eem/detection.cpp
    engines/eem/eem.cpp
    engines/eem/eem.h


diff --git a/engines/eem/detection.cpp b/engines/eem/detection.cpp
index fab534952a9..62d6c3901bd 100644
--- a/engines/eem/detection.cpp
+++ b/engines/eem/detection.cpp
@@ -28,6 +28,7 @@ namespace EEM {
 
 const PlainGameDescriptor eemGames[] = {
 	{ "eem", "Eagle Eye Mysteries" },
+	{ "eem2", "Eagle Eye Mysteries in London" },
 	{ nullptr, nullptr }
 };
 
@@ -66,6 +67,20 @@ const ADGameDescription gameDescriptions[] = {
 		ADGF_NO_FLAGS,
 		GUI_OPTIONS_EEM_FLOPPY
 	},
+	{
+		// Eagle Eye Mysteries in London (EEM2CD.EXE) ??? proof of concept.
+		// The sequel reuses this engine's resource formats (DBD/DBX
+		// archives, SITEPALS palettes, FONT.FNT), so only the opening
+		// screens load for now; flagged unstable until properly supported.
+		"eem2",
+		"CD",
+		AD_ENTRY2s("EEM2CD.EXE", "211a376b23a1b6259d0c36cf46d26ed4", 172560,
+				   "PICS.DBD",   "da0b13a117bc3a207aec907c05769cd8", 2972988),
+		Common::EN_ANY,
+		Common::kPlatformDOS,
+		ADGF_UNSTABLE,
+		GUIO2(GUIO_MIDIADLIB, GUIO_MIDIMT32)
+	},
 
 	AD_TABLE_END_MARKER
 };
diff --git a/engines/eem/eem.cpp b/engines/eem/eem.cpp
index e531f32c460..56e839889e2 100644
--- a/engines/eem/eem.cpp
+++ b/engines/eem/eem.cpp
@@ -165,6 +165,11 @@ EEMEngine::EEMEngine(OSystem *syst, const ADGameDescription *gameDesc)
 	_variant = (gameDesc && gameDesc->extra &&
 				Common::String(gameDesc->extra).contains("Floppy"))
 				 ? kVariantFloppy : kVariantCD;
+	// EEM2 ("...in London") ships as a separate detection entry (gameId
+	// "eem2"); it reuses this engine but only the opening screens work.
+	if (gameDesc && gameDesc->gameId &&
+		Common::String(gameDesc->gameId) == "eem2")
+		_variant = kVariantLondonCD;
 	_language = gameDesc ? gameDesc->language : Common::EN_ANY;
 }
 
@@ -296,6 +301,17 @@ Common::Error EEMEngine::run() {
 
 	debugC(1, kDebugGeneral, "EEM engine starting");
 
+	// EEM2 ("Eagle Eye Mysteries in London") proof of concept. The archives,
+	// SITEPALS. palettes, font and mouse cursor above all load from EEM2's
+	// data using the same code paths as EEM1, which already proves the
+	// shared resource formats. Everything below here (saves, mystery/site
+	// state machine) is EEM1-specific, so the PoC just renders EEM2's
+	// opening logo screens and exits.
+	if (isLondon()) {
+		runLondonScreensPoc();
+		return Common::kNoError;
+	}
+
 	// Resume from save: mystery in progress ??? MAP (handler 0 @ 1a35:0e1d);
 	// otherwise ??? ACTION.
 	const int wantedSave = ConfMan.hasKey("save_slot")
@@ -630,8 +646,11 @@ bool EEMEngine::openArchives() {
 
 bool EEMEngine::loadSitePalettes() {
 	Common::File f;
-	if (!f.open(Common::Path("SITEPALS"))) {
-		warning("SITEPALS missing");
+	// EEM1 ships "SITEPALS" (40 palettes); EEM2/London ships "SITEPALS."
+	// with a trailing dot (63 palettes). _ReadPalettes @ 17ee:0cdb.
+	const char *palFile = isLondon() ? "SITEPALS." : "SITEPALS";
+	if (!f.open(Common::Path(palFile))) {
+		warning("%s missing", palFile);
 		return false;
 	}
 	_sitePals.resize(f.size());
@@ -645,7 +664,9 @@ bool EEMEngine::loadSitePalettes() {
 }
 
 bool EEMEngine::getSitePalette(uint num, byte *out) const {
-	if (num >= kNumSitePals || _sitePals.size() < (num + 1) * kPalSize)
+	// EEM1 SITEPALS has kNumSitePals (40) entries; EEM2/London SITEPALS.
+	// has 63. Validate against the actual loaded buffer so both work.
+	if (_sitePals.size() < (num + 1) * kPalSize)
 		return false;
 	// SITEPALS stores 6-bit VGA-DAC values (0..63); ScummVM expects
 	// 8-bit (0..255), so left-shift by 2 like the original VGA hardware.
@@ -999,6 +1020,65 @@ void EEMEngine::showHighScoreLogo() {
 	fadeCurrentPaletteToBlack();
 }
 
+void EEMEngine::showLondonLogo(uint picId, uint palId, uint holdMs) {
+	// Parameterised twin of `showHighScoreLogo` for EEM2's opening logos
+	// (`_ShowEAKids` @ 2721:05e3 and `_ShowHScoreLogo` @ 2721:084d both do
+	// _GetPicture(pic) -> blit -> _GetPalette(pal) -> fade in -> hold ->
+	// fade out).
+	Picture pic;
+	if (!_picsArchive.getPicture(picId, pic) || pic.surface.empty()) {
+		warning("London logo PIC 0x%x load failed", picId);
+		return;
+	}
+	blitAt(pic, 0, 0);
+
+	byte target[kPalSize];
+	if (!getSitePalette(palId, target)) {
+		warning("London palette 0x%x load failed", palId);
+		return;
+	}
+	byte black[kPalSize] = {};
+	g_system->getPaletteManager()->setPalette(black, 0, 256);
+	g_system->updateScreen();
+	fadePaletteFromBlack(target);
+
+	waitForInput(holdMs);
+	fadeCurrentPaletteToBlack();
+}
+
+void EEMEngine::runLondonScreensPoc() {
+	// EEM2 opening logos ??? still-image portion of `_DoOpeningAnims`
+	// @ 2721:08e6. Picture/palette IDs come from RE of EEM2CD.EXE:
+	//   _ShowEAKids     @ 2721:05e3 ??? _GetPicture(0x54),  _GetPalette(0x3c)
+	//   _ShowStormLogo  @ 2721:0729 ??? BOLT.ANM + THUNDER.VOC
+	//   _ShowHScoreLogo @ 2721:084d ??? _GetPicture(0x356), _GetPalette(0x3d)
+	CursorMan.showMouse(false);
+	debugC(1, kDebugGeneral, "EEM2 (London) PoC: rendering opening screens");
+
+	// EA Kids logo.
+	if (!shouldQuit())
+		showLondonLogo(0x54, 0x3c, 2500);
+
+	// Storm Software lightning-bolt animation with thunder (same BOLT.ANM
+	// container as EEM1 CD; the palette lives in the ANM file header).
+	if (!shouldQuit()) {
+		if (_audio)
+			_audio->playVoc(Common::Path("THUNDER.VOC"));
+		playAnm(Common::Path("BOLT.ANM"), 120, /* holdLastFrame= */ false,
+				/* fadeIn= */ true);
+		waitForInput(1500);
+		fadeCurrentPaletteToBlack();
+		if (_audio)
+			_audio->stopVoice();
+	}
+
+	// High Score Productions logo.
+	if (!shouldQuit())
+		showLondonLogo(0x356, 0x3d, 2500);
+
+	debugC(1, kDebugGeneral, "EEM2 (London) PoC: opening screens done");
+}
+
 void EEMEngine::showFloppyStormLogo() {
 	// Floppy storm-logo splash ??? `FUN_23d2_0605 @ 23d2:0605`:
 	//   GetPicture(0x20b); BlitToVGA;
diff --git a/engines/eem/eem.h b/engines/eem/eem.h
index 3a405a74f95..accf569bb2d 100644
--- a/engines/eem/eem.h
+++ b/engines/eem/eem.h
@@ -101,8 +101,9 @@ enum ScreenId {
 /// ANIM01..20.A), and per-variant SFX (DING.VOC / NEWSCAN.VOC ship only
 /// with floppy).
 enum Variant {
-	kVariantCD     = 0,
-	kVariantFloppy = 1,
+	kVariantCD       = 0,
+	kVariantFloppy   = 1,
+	kVariantLondonCD = 2, ///< Eagle Eye Mysteries in London (EEM2CD.EXE) ??? PoC.
 };
 
 /// `_Partner @ 29be:7918`. Selected at the partner-pick screen
@@ -134,6 +135,10 @@ public:
 	Common::Platform getPlatform() const;
 	Variant getVariant() const { return _variant; }
 	bool isFloppy() const { return _variant == kVariantFloppy; }
+	/// EEM2 ("Eagle Eye Mysteries in London"). Proof-of-concept variant:
+	/// same DBD/palette/font formats, but a 63-entry "SITEPALS." file and
+	/// different opening picture/palette IDs.
+	bool isLondon() const { return _variant == kVariantLondonCD; }
 
 	bool hasFeature(EngineFeature f) const override;
 	bool canLoadGameStateCurrently(Common::U32String *msg = nullptr) override;
@@ -403,6 +408,15 @@ private:
 	void showHighScoreLogo();
 	void showFloppyStormLogo();
 
+	// --- EEM2 ("...in London") proof of concept ---
+	/// Reproduce the opening-logo portion of EEM2's `_DoOpeningAnims`
+	/// (@ 2721:08e6): EA Kids (PIC 0x54, pal 0x3c) -> Storm (BOLT.ANM) ->
+	/// High Score (PIC 0x356, pal 0x3d), then stop.
+	void runLondonScreensPoc();
+	/// Blit a full-screen still PIC and fade it in / hold / out using the
+	/// given SITEPALS. palette index.
+	void showLondonLogo(uint picId, uint palId, uint holdMs);
+
 	/// `screen8_handler @ 1c33:1012`. Profile selector ??? walks
 	/// `listProfiles()`, falls through to `doNewPlayer()` if "New" or
 	/// no profiles exist (1c33:1170: `if (saves == 0) _NewPlayer();`).


Commit: ff0025ab9680d7d6323d66fdd7a1d79f57cfbad2
    https://github.com/scummvm/scummvm/commit/ff0025ab9680d7d6323d66fdd7a1d79f57cfbad2
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:21:20+02:00

Commit Message:
EEM: Added a more complete intro and character selection for EEM2

Changed paths:
    engines/eem/eem.cpp
    engines/eem/eem.h


diff --git a/engines/eem/eem.cpp b/engines/eem/eem.cpp
index 56e839889e2..32f03c7d982 100644
--- a/engines/eem/eem.cpp
+++ b/engines/eem/eem.cpp
@@ -1047,36 +1047,93 @@ void EEMEngine::showLondonLogo(uint picId, uint palId, uint holdMs) {
 }
 
 void EEMEngine::runLondonScreensPoc() {
-	// EEM2 opening logos ??? still-image portion of `_DoOpeningAnims`
-	// @ 2721:08e6. Picture/palette IDs come from RE of EEM2CD.EXE:
-	//   _ShowEAKids     @ 2721:05e3 ??? _GetPicture(0x54),  _GetPalette(0x3c)
-	//   _ShowStormLogo  @ 2721:0729 ??? BOLT.ANM + THUNDER.VOC
-	//   _ShowHScoreLogo @ 2721:084d ??? _GetPicture(0x356), _GetPalette(0x3d)
+	// Full opening sequence ??? EEM2 `_DoOpeningAnims` @ 2721:08e6:
+	//   _ShowEAKids   @ 2721:05e3 ??? PIC 0x54,  palette 0x3c
+	//   FUN_2721_07be @ 2721:07be ??? PIC 0x20c, palette 0x3e (publisher logo)
+	//   _ShowStormLogo@ 2721:0729 ??? anim 0 ("bolt.anm") + "thunder.voc"
+	//   _MIDIPlay(0x65)=MUS00101.XMI; anim 1 ("movie.anm")
+	//   _MIDIPlay(0x66)=MUS00102.XMI (loop); anim 2 ("wave.anm"), looped
+	//   _MIDIPlay(0x67)=MUS00103.XMI; fade out
+	// Anim names come from the table @ DS:1b54 -> {bolt,movie,wave}.anm;
+	// _MIDIPlay(n) maps to MUS%05d.XMI (n).
+	const uint32 kHoldForever = 0xFFFFFFFFu;
 	CursorMan.showMouse(false);
-	debugC(1, kDebugGeneral, "EEM2 (London) PoC: rendering opening screens");
+	_skipIntro = false;
+	debugC(1, kDebugGeneral, "EEM2 (London) PoC: opening sequence");
 
-	// EA Kids logo.
-	if (!shouldQuit())
-		showLondonLogo(0x54, 0x3c, 2500);
+	// Two still logos.
+	if (!shouldQuit() && !_skipIntro)
+		showLondonLogo(0x54, 0x3c, 2500);   // EA Kids
+	if (!shouldQuit() && !_skipIntro)
+		showLondonLogo(0x20c, 0x3e, 2500);  // publisher logo (FUN_2721_07be)
 
-	// Storm Software lightning-bolt animation with thunder (same BOLT.ANM
-	// container as EEM1 CD; the palette lives in the ANM file header).
-	if (!shouldQuit()) {
+	// Storm Software ??? bolt.anm with the thunder roar.
+	if (!shouldQuit() && !_skipIntro) {
 		if (_audio)
 			_audio->playVoc(Common::Path("THUNDER.VOC"));
 		playAnm(Common::Path("BOLT.ANM"), 120, /* holdLastFrame= */ false,
 				/* fadeIn= */ true);
-		waitForInput(1500);
-		fadeCurrentPaletteToBlack();
 		if (_audio)
 			_audio->stopVoice();
+		fadeCurrentPaletteToBlack();
 	}
 
-	// High Score Productions logo.
+	// Intro movie with its theme (MUS00101.XMI).
+	if (!shouldQuit() && !_skipIntro && _music)
+		_music->playFile(Common::Path("MUS00101.XMI"), /* loop= */ false);
+	if (!shouldQuit() && !_skipIntro)
+		playAnm(Common::Path("MOVIE.ANM"), 120, /* holdLastFrame= */ false,
+				/* fadeIn= */ true);
+
+	// Animated title (wave.anm) over the looping theme (MUS00102.XMI);
+	// a click / key advances to character creation. The original loops
+	// wave.anm; the PoC plays it once, holds the last frame and waits.
+	if (!shouldQuit() && !_skipIntro && _music)
+		_music->playFile(Common::Path("MUS00102.XMI"), /* loop= */ true);
+	if (!shouldQuit() && !_skipIntro)
+		playAnm(Common::Path("WAVE.ANM"), 120, /* holdLastFrame= */ true,
+				/* fadeIn= */ true);
+	if (!shouldQuit() && !_skipIntro)
+		waitForInput(kHoldForever);
+	if (_music)
+		_music->stop();
+	_skipIntro = false;
+
+	// Character-selection screen.
 	if (!shouldQuit())
-		showLondonLogo(0x356, 0x3d, 2500);
+		showLondonCharSelect();
 
-	debugC(1, kDebugGeneral, "EEM2 (London) PoC: opening screens done");
+	debugC(1, kDebugGeneral, "EEM2 (London) PoC: done");
+}
+
+void EEMEngine::showLondonCharSelect() {
+	// `_NewPlayer` @ 1cd3:0f27 opens the character-creation screen with
+	// `_GetPalette(0)` + `_GetBackground(0xc)` (PICS picture 0xc), then
+	// reads a name and toggles Jake/Jenny (left/right arrow -> DAT_4c4c,
+	// Enter confirms). This PoC renders the screen; wiring up the
+	// interactive name + partner entry is the next step.
+	const uint32 kHoldForever = 0xFFFFFFFFu;
+	debugC(1, kDebugGeneral, "EEM2 (London) PoC: character-selection screen");
+
+	Picture bg;
+	if (!_picsArchive.getPicture(0xc, bg) || bg.surface.empty()) {
+		warning("London char-select background PIC 0xc load failed");
+		return;
+	}
+	blitAt(bg, 0, 0);
+
+	byte target[kPalSize];
+	byte black[kPalSize] = {};
+	g_system->getPaletteManager()->setPalette(black, 0, 256);
+	g_system->updateScreen();
+	if (getSitePalette(0, target))
+		fadePaletteFromBlack(target);
+	else
+		warning("London char-select: palette 0 unavailable");
+
+	CursorMan.showMouse(true);
+	setInteractiveMouseCursor(true);
+	waitForInput(kHoldForever);
 }
 
 void EEMEngine::showFloppyStormLogo() {
diff --git a/engines/eem/eem.h b/engines/eem/eem.h
index accf569bb2d..8fcb1abeec3 100644
--- a/engines/eem/eem.h
+++ b/engines/eem/eem.h
@@ -409,13 +409,16 @@ private:
 	void showFloppyStormLogo();
 
 	// --- EEM2 ("...in London") proof of concept ---
-	/// Reproduce the opening-logo portion of EEM2's `_DoOpeningAnims`
-	/// (@ 2721:08e6): EA Kids (PIC 0x54, pal 0x3c) -> Storm (BOLT.ANM) ->
-	/// High Score (PIC 0x356, pal 0x3d), then stop.
+	/// Full opening sequence + character-selection screen ??? EEM2's
+	/// `_DoOpeningAnims` @ 2721:08e6 then `_NewPlayer` @ 1cd3:0f27.
 	void runLondonScreensPoc();
 	/// Blit a full-screen still PIC and fade it in / hold / out using the
 	/// given SITEPALS. palette index.
 	void showLondonLogo(uint picId, uint palId, uint holdMs);
+	/// Render EEM2's character-creation screen (`_NewPlayer`: palette 0 +
+	/// background PIC 0xc, where the player types a name and picks
+	/// Jake/Jenny). PoC display; interactive entry is still TODO.
+	void showLondonCharSelect();
 
 	/// `screen8_handler @ 1c33:1012`. Profile selector ??? walks
 	/// `listProfiles()`, falls through to `doNewPlayer()` if "New" or


Commit: 3fb2a3ea4488b5f83767141883896bf51569311a
    https://github.com/scummvm/scummvm/commit/3fb2a3ea4488b5f83767141883896bf51569311a
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:21:21+02:00

Commit Message:
EEM: load case menu is working in EEM2

Changed paths:
    engines/eem/eem.cpp
    engines/eem/ui.cpp


diff --git a/engines/eem/eem.cpp b/engines/eem/eem.cpp
index 32f03c7d982..92527a49871 100644
--- a/engines/eem/eem.cpp
+++ b/engines/eem/eem.cpp
@@ -1099,41 +1099,134 @@ void EEMEngine::runLondonScreensPoc() {
 		_music->stop();
 	_skipIntro = false;
 
-	// Character-selection screen.
+	// Character creation (name + Jake/Jenny), then the case-selection menu.
 	if (!shouldQuit())
 		showLondonCharSelect();
+	// Reuse EEM1's `doCaseSelection()` verbatim ??? EEM2 shares its assets
+	// (background PIC 0x41, partner greeter ANI 0x15/0x16, BOOK*.NME, the
+	// generic chooser). The mystery load at the end is guarded out for
+	// London since EEM2's case data isn't ported yet.
+	if (!shouldQuit())
+		doCaseSelection();
 
 	debugC(1, kDebugGeneral, "EEM2 (London) PoC: done");
 }
 
 void EEMEngine::showLondonCharSelect() {
-	// `_NewPlayer` @ 1cd3:0f27 opens the character-creation screen with
-	// `_GetPalette(0)` + `_GetBackground(0xc)` (PICS picture 0xc), then
-	// reads a name and toggles Jake/Jenny (left/right arrow -> DAT_4c4c,
-	// Enter confirms). This PoC renders the screen; wiring up the
-	// interactive name + partner entry is the next step.
-	const uint32 kHoldForever = 0xFFFFFFFFu;
-	debugC(1, kDebugGeneral, "EEM2 (London) PoC: character-selection screen");
+	// `_NewPlayer` @ 1cd3:0f27 ??? the character-creation screen: palette 0 +
+	// background PIC 0xc, type a name, then pick Jake/Jenny with the
+	// left/right arrows (stored at DAT_4c4c) and confirm with Enter. The
+	// original's name-field / Jake-Jenny box rects are runtime-initialised
+	// (BSS, beyond the EXE image), so the overlay positions here are
+	// approximate over EEM2's PIC 0xc artwork ??? tune once visible. Text
+	// uses the shared engine font.
+	debugC(1, kDebugGeneral, "EEM2 (London) PoC: character creation");
 
 	Picture bg;
-	if (!_picsArchive.getPicture(0xc, bg) || bg.surface.empty()) {
-		warning("London char-select background PIC 0xc load failed");
-		return;
-	}
-	blitAt(bg, 0, 0);
+	const bool haveBg = _picsArchive.getPicture(0xc, bg) && !bg.surface.empty();
 
-	byte target[kPalSize];
+	byte pal[kPalSize];
 	byte black[kPalSize] = {};
 	g_system->getPaletteManager()->setPalette(black, 0, 256);
 	g_system->updateScreen();
-	if (getSitePalette(0, target))
-		fadePaletteFromBlack(target);
-	else
-		warning("London char-select: palette 0 unavailable");
+	const bool havePal = getSitePalette(0, pal);
 
-	CursorMan.showMouse(true);
-	setInteractiveMouseCursor(true);
-	waitForInput(kHoldForever);
+	CursorMan.showMouse(false);
+
+	Common::String name;
+	const uint kMaxName = 12;
+	uint8 partner = kPartnerJake;
+	bool nameDone = false, blink = true, fadedIn = false;
+	uint32 blinkMs = g_system->getMillis();
+	bool done = false, needRedraw = true;
+
+	while (!done && !shouldQuit()) {
+		if (needRedraw) {
+			Graphics::ManagedSurface scratch(kScreenWidth, kScreenHeight,
+				Graphics::PixelFormat::createFormatCLUT8());
+			scratch.clear();
+			if (haveBg)
+				scratch.simpleBlitFrom(bg.surface);
+			if (getFont().isLoaded()) {
+				getFont().drawString(&scratch,
+					isSpanish() ? "Escribe tu nombre:" : "Type your name:",
+					80, 36, 240, 0xF);
+				Common::String shown = name;
+				if (!nameDone && blink)
+					shown += "_";
+				getFont().drawString(&scratch, shown, 80, 52, 240, 0xF);
+				getFont().drawString(&scratch, "Jake", 104, 116, 80,
+					partner == kPartnerJake ? 0xF : 0x8);
+				getFont().drawString(&scratch, "Jenny", 184, 116, 80,
+					partner == kPartnerJenny ? 0xF : 0x8);
+				getFont().drawString(&scratch,
+					nameDone ? (isSpanish() ? "Flechas eligen - Enter juega"
+											: "Arrows choose - Enter to play")
+							 : (isSpanish() ? "Enter para continuar"
+											: "Enter to continue"),
+					80, 150, 240, 0x7);
+			}
+			g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
+									   0, 0, kScreenWidth, kScreenHeight);
+			if (!fadedIn) {
+				if (havePal)
+					fadePaletteFromBlack(pal);
+				fadedIn = true;
+			}
+			g_system->updateScreen();
+			needRedraw = false;
+		}
+
+		Common::Event ev;
+		while (g_system->getEventManager()->pollEvent(ev)) {
+			if (ev.type == Common::EVENT_QUIT ||
+				ev.type == Common::EVENT_RETURN_TO_LAUNCHER)
+				return;
+			if (ev.type != Common::EVENT_KEYDOWN)
+				continue;
+			const Common::KeyCode k = ev.kbd.keycode;
+			if (!nameDone) {
+				if ((k == Common::KEYCODE_RETURN ||
+					 k == Common::KEYCODE_KP_ENTER) && !name.empty())
+					nameDone = true;
+				else if (k == Common::KEYCODE_BACKSPACE && !name.empty())
+					name.deleteLastChar();
+				else if (ev.kbd.ascii >= ' ' && ev.kbd.ascii < 127 &&
+						 name.size() < kMaxName)
+					name += (char)ev.kbd.ascii;
+			} else {
+				if (k == Common::KEYCODE_LEFT)
+					partner = kPartnerJake;
+				else if (k == Common::KEYCODE_RIGHT)
+					partner = kPartnerJenny;
+				else if (k == Common::KEYCODE_RETURN ||
+						 k == Common::KEYCODE_KP_ENTER)
+					done = true;
+			}
+			needRedraw = true;
+		}
+
+		const uint32 now = g_system->getMillis();
+		if (!nameDone && now - blinkMs >= 400) {
+			blinkMs = now;
+			blink = !blink;
+			needRedraw = true;
+		}
+		g_system->delayMillis(15);
+	}
+	if (shouldQuit())
+		return;
+
+	// Commit the profile so the reused case-selection menu has valid state
+	// (player name shown in clue text, partner greeter ANI 0x15/0x16,
+	// Junior chain stage -> BOOK1.NME, nothing solved yet).
+	_playerName = name.empty() ? Common::String("Detective") : name;
+	_partner = partner;
+	_chainStage = 1;
+	for (uint i = 0; i < sizeof(_mysteriesSolved); i++)
+		_mysteriesSolved[i] = 0;
+	debugC(1, kDebugGeneral, "London PoC: player='%s' partner=%s",
+		   _playerName.c_str(), partner == kPartnerJake ? "Jake" : "Jenny");
 }
 
 void EEMEngine::showFloppyStormLogo() {
diff --git a/engines/eem/ui.cpp b/engines/eem/ui.cpp
index 420c799701c..6be8928886a 100644
--- a/engines/eem/ui.cpp
+++ b/engines/eem/ui.cpp
@@ -2117,6 +2117,14 @@ void EEMEngine::doCaseSelection() {
 		return;
 
 	const uint mn = stageLo + selRow;
+	if (isLondon()) {
+		// EEM2 PoC: the menu (PIC 0x41, ANI 0x15/0x16, BOOK*.NME) is shared
+		// with EEM1, but EEM2's mystery data (M*.BIN/E*.BIN) isn't ported,
+		// so stop here instead of parsing it with the EEM1 loader.
+		debugC(1, kDebugMystery,
+			   "London PoC: selected mystery %u (load not implemented)", mn);
+		return;
+	}
 	if (!_mystery.load(mn, &_rng)) {
 		warning("doCaseSelection: failed to load mystery %u", mn);
 		_mystery.clear();


Commit: a477a58ccf0f2a5bc7e70172d8ef6c77710e2e09
    https://github.com/scummvm/scummvm/commit/a477a58ccf0f2a5bc7e70172d8ef6c77710e2e09
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:21:21+02:00

Commit Message:
EEM: add more code in the partner selection screen

Changed paths:
    engines/eem/eem.cpp


diff --git a/engines/eem/eem.cpp b/engines/eem/eem.cpp
index 92527a49871..4355db818d8 100644
--- a/engines/eem/eem.cpp
+++ b/engines/eem/eem.cpp
@@ -1113,15 +1113,23 @@ void EEMEngine::runLondonScreensPoc() {
 }
 
 void EEMEngine::showLondonCharSelect() {
-	// `_NewPlayer` @ 1cd3:0f27 ??? the character-creation screen: palette 0 +
-	// background PIC 0xc, type a name, then pick Jake/Jenny with the
-	// left/right arrows (stored at DAT_4c4c) and confirm with Enter. The
-	// original's name-field / Jake-Jenny box rects are runtime-initialised
-	// (BSS, beyond the EXE image), so the overlay positions here are
-	// approximate over EEM2's PIC 0xc artwork ??? tune once visible. Text
-	// uses the shared engine font.
+	// `_NewPlayer` @ 1cd3:0f27 ??? character creation over background PIC 0xc
+	// (palette 0). Two text fields then a Jake/Jenny pick (left/right arrow
+	// 0x4b/0x4d -> DAT_4c4c, Enter confirms). The field/box rects are
+	// constants in EEM2's data segment (read at 2bca:0e3a); they map to the
+	// `pr` player record's FirstName[12] / LastName[20]:
+	//   first name : (54,75)-(151,85)    last name : (167,75)-(266,85)
+	//   Jake box   : (110,116)-(120,122) Jenny box : (190,116)-(200,122)
 	debugC(1, kDebugGeneral, "EEM2 (London) PoC: character creation");
 
+	const Common::Rect kFirstRect(54, 75, 151, 85);
+	const Common::Rect kLastRect(167, 75, 266, 85);
+	const Common::Rect kJakeBox(110, 116, 120, 122);
+	const Common::Rect kJennyBox(190, 116, 200, 122);
+	const uint kMaxFirst = 12, kMaxLast = 20;
+	const uint8 kInkColor = 0x0F;     // typed-name ink
+	const uint8 kHiBox    = 0x0F;     // selected-partner box highlight
+
 	Picture bg;
 	const bool haveBg = _picsArchive.getPicture(0xc, bg) && !bg.surface.empty();
 
@@ -1133,12 +1141,12 @@ void EEMEngine::showLondonCharSelect() {
 
 	CursorMan.showMouse(false);
 
-	Common::String name;
-	const uint kMaxName = 12;
+	enum { kFieldFirst = 0, kFieldLast = 1, kFieldPartner = 2 };
+	int field = kFieldFirst;
+	Common::String first, last;
 	uint8 partner = kPartnerJake;
-	bool nameDone = false, blink = true, fadedIn = false;
+	bool blink = true, fadedIn = false, done = false, needRedraw = true;
 	uint32 blinkMs = g_system->getMillis();
-	bool done = false, needRedraw = true;
 
 	while (!done && !shouldQuit()) {
 		if (needRedraw) {
@@ -1148,24 +1156,22 @@ void EEMEngine::showLondonCharSelect() {
 			if (haveBg)
 				scratch.simpleBlitFrom(bg.surface);
 			if (getFont().isLoaded()) {
-				getFont().drawString(&scratch,
-					isSpanish() ? "Escribe tu nombre:" : "Type your name:",
-					80, 36, 240, 0xF);
-				Common::String shown = name;
-				if (!nameDone && blink)
-					shown += "_";
-				getFont().drawString(&scratch, shown, 80, 52, 240, 0xF);
-				getFont().drawString(&scratch, "Jake", 104, 116, 80,
-					partner == kPartnerJake ? 0xF : 0x8);
-				getFont().drawString(&scratch, "Jenny", 184, 116, 80,
-					partner == kPartnerJenny ? 0xF : 0x8);
-				getFont().drawString(&scratch,
-					nameDone ? (isSpanish() ? "Flechas eligen - Enter juega"
-											: "Arrows choose - Enter to play")
-							 : (isSpanish() ? "Enter para continuar"
-											: "Enter to continue"),
-					80, 150, 240, 0x7);
+				Common::String f = first;
+				if (field == kFieldFirst && blink)
+					f += "_";
+				Common::String l = last;
+				if (field == kFieldLast && blink)
+					l += "_";
+				getFont().drawString(&scratch, f, kFirstRect.left + 2,
+									 kFirstRect.top + 1, kFirstRect.width(),
+									 kInkColor);
+				getFont().drawString(&scratch, l, kLastRect.left + 2,
+									 kLastRect.top + 1, kLastRect.width(),
+									 kInkColor);
 			}
+			// Highlight the selected partner's indicator box.
+			scratch.fillRect(partner == kPartnerJake ? kJakeBox : kJennyBox,
+							 kHiBox);
 			g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
 									   0, 0, kScreenWidth, kScreenHeight);
 			if (!fadedIn) {
@@ -1185,29 +1191,34 @@ void EEMEngine::showLondonCharSelect() {
 			if (ev.type != Common::EVENT_KEYDOWN)
 				continue;
 			const Common::KeyCode k = ev.kbd.keycode;
-			if (!nameDone) {
-				if ((k == Common::KEYCODE_RETURN ||
-					 k == Common::KEYCODE_KP_ENTER) && !name.empty())
-					nameDone = true;
-				else if (k == Common::KEYCODE_BACKSPACE && !name.empty())
-					name.deleteLastChar();
+			const bool enter = (k == Common::KEYCODE_RETURN ||
+								k == Common::KEYCODE_KP_ENTER);
+			if (field == kFieldFirst || field == kFieldLast) {
+				Common::String &buf = (field == kFieldFirst) ? first : last;
+				const uint cap = (field == kFieldFirst) ? kMaxFirst : kMaxLast;
+				if ((enter || k == Common::KEYCODE_TAB) &&
+					(field == kFieldLast || !first.empty()))
+					field++;  // advance to last name, then to partner pick
+				else if (k == Common::KEYCODE_BACKSPACE && !buf.empty())
+					buf.deleteLastChar();
 				else if (ev.kbd.ascii >= ' ' && ev.kbd.ascii < 127 &&
-						 name.size() < kMaxName)
-					name += (char)ev.kbd.ascii;
-			} else {
+						 buf.size() < cap)
+					buf += (char)ev.kbd.ascii;
+			} else {  // partner pick
 				if (k == Common::KEYCODE_LEFT)
 					partner = kPartnerJake;
 				else if (k == Common::KEYCODE_RIGHT)
 					partner = kPartnerJenny;
-				else if (k == Common::KEYCODE_RETURN ||
-						 k == Common::KEYCODE_KP_ENTER)
+				else if (k == Common::KEYCODE_BACKSPACE)
+					field = kFieldLast;  // back to editing
+				else if (enter)
 					done = true;
 			}
 			needRedraw = true;
 		}
 
 		const uint32 now = g_system->getMillis();
-		if (!nameDone && now - blinkMs >= 400) {
+		if (field != kFieldPartner && now - blinkMs >= 400) {
 			blinkMs = now;
 			blink = !blink;
 			needRedraw = true;
@@ -1218,9 +1229,11 @@ void EEMEngine::showLondonCharSelect() {
 		return;
 
 	// Commit the profile so the reused case-selection menu has valid state
-	// (player name shown in clue text, partner greeter ANI 0x15/0x16,
-	// Junior chain stage -> BOOK1.NME, nothing solved yet).
-	_playerName = name.empty() ? Common::String("Detective") : name;
+	// (player name in clue text, partner greeter ANI 0x15/0x16, Junior
+	// chain stage -> BOOK1.NME, nothing solved yet).
+	if (first.empty())
+		first = "Detective";
+	_playerName = last.empty() ? first : (first + " " + last);
 	_partner = partner;
 	_chainStage = 1;
 	for (uint i = 0; i < sizeof(_mysteriesSolved); i++)


Commit: e69c835219a58d26adcac729f3b7d773f93e0d42
    https://github.com/scummvm/scummvm/commit/e69c835219a58d26adcac729f3b7d773f93e0d42
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:21:21+02:00

Commit Message:
EEM: corrected palette

Changed paths:
    engines/eem/clues.cpp
    engines/eem/eem.cpp
    engines/eem/ui.cpp


diff --git a/engines/eem/clues.cpp b/engines/eem/clues.cpp
index 3f70172723b..1c84992d616 100644
--- a/engines/eem/clues.cpp
+++ b/engines/eem/clues.cpp
@@ -300,7 +300,12 @@ void EEMEngine::doInitClues() {
 		_mystery._lastSite = 0;
 	}
 
-	setSitePalette(0x22);
+	// Case-briefing palette. EEM1 `_DoInitClues` uses SITEPALS index 0x22;
+	// EEM2 `_DoInitClues` @ 1abf:03b3 does `_GetBackground(0x52)` then
+	// `_GetPalette(0x39)` (its 63-entry SITEPALS. shifts the UI palettes).
+	// The briefing partner animation is an ANI sprite drawn under the screen
+	// palette, so the same index fixes both the background and the anim.
+	setSitePalette(isLondon() ? 0x39 : 0x22);
 	Picture bg;
 	const bool haveBriefingBg = _picsArchive.getPicture(0x52, bg);
 	if (haveBriefingBg)
diff --git a/engines/eem/eem.cpp b/engines/eem/eem.cpp
index 4355db818d8..66d5b11f08e 100644
--- a/engines/eem/eem.cpp
+++ b/engines/eem/eem.cpp
@@ -301,17 +301,6 @@ Common::Error EEMEngine::run() {
 
 	debugC(1, kDebugGeneral, "EEM engine starting");
 
-	// EEM2 ("Eagle Eye Mysteries in London") proof of concept. The archives,
-	// SITEPALS. palettes, font and mouse cursor above all load from EEM2's
-	// data using the same code paths as EEM1, which already proves the
-	// shared resource formats. Everything below here (saves, mystery/site
-	// state machine) is EEM1-specific, so the PoC just renders EEM2's
-	// opening logo screens and exits.
-	if (isLondon()) {
-		runLondonScreensPoc();
-		return Common::kNoError;
-	}
-
 	// Resume from save: mystery in progress ??? MAP (handler 0 @ 1a35:0e1d);
 	// otherwise ??? ACTION.
 	const int wantedSave = ConfMan.hasKey("save_slot")
@@ -342,6 +331,31 @@ Common::Error EEMEngine::run() {
 	if (resumed)
 		goto screenLoop;
 
+	// EEM2 ("Eagle Eye Mysteries in London") proof of concept: opening
+	// sequence + character creation, then start the training case (mystery
+	// 0 ??? what a new detective plays first) and reuse the engine's gameplay
+	// screen loop. EEM2's mystery data format matches EEM1-CD, so the shared
+	// Mystery parser + InitClues (case intro) / BigMap / Site handlers apply.
+	if (isLondon()) {
+		runLondonScreensPoc();
+		if (!shouldQuit()) {
+			CursorMan.showMouse(true);
+			if (_mystery.load(0, &_rng)) {
+				resetSiteArrivalState();
+				if (_audio)
+					_audio->initMysterySounds(0);
+				debugC(1, kDebugMystery,
+					   "London: training mystery 0 loaded ??? %u sites, %u suspects",
+					   _mystery.numSites(), _mystery.numSuspects());
+				_nextScreen = kScreenInitClues;
+			} else {
+				warning("London: training mystery 0 (M0.BIN) failed to load");
+				_nextScreen = kScreenInvalid;
+			}
+		}
+		goto screenLoop;
+	}
+
 	// _DoOpeningAnims @ 2520:082a:
 	//   EA Kids logo (PIC) -> HighScore logo (PIC) -> Storm logo
 	//   (BOLT.ANM) -> [music starts] -> 20 character-intro anims
@@ -1099,17 +1113,14 @@ void EEMEngine::runLondonScreensPoc() {
 		_music->stop();
 	_skipIntro = false;
 
-	// Character creation (name + Jake/Jenny), then the case-selection menu.
+	// Character creation (name + Jake/Jenny). The caller then loads the
+	// training case (mystery 0) and enters the shared gameplay screen loop
+	// (case intro -> map -> site); the case-selection menu is reachable
+	// later via the action screen.
 	if (!shouldQuit())
 		showLondonCharSelect();
-	// Reuse EEM1's `doCaseSelection()` verbatim ??? EEM2 shares its assets
-	// (background PIC 0x41, partner greeter ANI 0x15/0x16, BOOK*.NME, the
-	// generic chooser). The mystery load at the end is guarded out for
-	// London since EEM2's case data isn't ported yet.
-	if (!shouldQuit())
-		doCaseSelection();
 
-	debugC(1, kDebugGeneral, "EEM2 (London) PoC: done");
+	debugC(1, kDebugGeneral, "EEM2 (London) PoC: intro + character creation done");
 }
 
 void EEMEngine::showLondonCharSelect() {
diff --git a/engines/eem/ui.cpp b/engines/eem/ui.cpp
index 6be8928886a..0d6cb294edb 100644
--- a/engines/eem/ui.cpp
+++ b/engines/eem/ui.cpp
@@ -2958,7 +2958,9 @@ void EEMEngine::doBigMap() {
 
 	while (!shouldQuit()) {
 		setInteractiveMouseCursor(false);
-		setSitePalette(0x24); // `_GetPalette(0x24)` @ `_DoBigMap`.
+		// `_GetPalette(0x24)` @ EEM1 `_DoBigMap`; EEM2 `_DoBigMap`
+		// @ 2237:0a04 uses `_GetPalette(0x3b)` (shifted UI palettes).
+		setSitePalette(isLondon() ? 0x3b : 0x24);
 
 		// Stage 1: Overview. mapStartTick anchors partner timeline;
 		// `_NewAnimation` seeds frame to 0xffff so unfold plays once then


Commit: 29bef8261a7ee1b937bc269b7661f8f10100518f
    https://github.com/scummvm/scummvm/commit/29bef8261a7ee1b937bc269b7661f8f10100518f
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:21:22+02:00

Commit Message:
EEM: play correctly initial animation in London

Changed paths:
    engines/eem/clues.cpp
    engines/eem/detection.cpp
    engines/eem/eem.cpp
    engines/eem/eem.h
    engines/eem/ui.cpp


diff --git a/engines/eem/clues.cpp b/engines/eem/clues.cpp
index 1c84992d616..fc35a14407a 100644
--- a/engines/eem/clues.cpp
+++ b/engines/eem/clues.cpp
@@ -29,6 +29,7 @@
 
 #include "graphics/cursorman.h"
 #include "graphics/managed_surface.h"
+#include "graphics/paletteman.h"
 
 #include "eem/audio.h"
 #include "eem/detection.h"
@@ -261,56 +262,87 @@ void EEMEngine::doChoosePartner() {
 	}
 }
 
-// _DoInitClues @ 1a35:0411. Sequence:
-//   1. BG 0x52, palette 0x22
-//   2. anims: game @ (0xcd, 0x6c), book @ (0, 99), nancy @ (0x68, 0x8b) on caseType=1
-//   3. cycle game anim once (click skips)
-//   4. _PlayInSequence per partner+caseType
-//   5. _DisplayClue(InitBlock + 2, 1) ??? briefing dialogue
-//   6. _OnSites[startSite] = 1
-// Anim IDs: gameAni = 0x17 (Jake) / 0x3b (Jenny);
-// bookAni = 0x18 / 0x3c; nancyAni = 0x19 (caseType 1 only).
-void EEMEngine::doInitClues() {
-	if (!_mystery.isLoaded())
-		return;
-
-	const byte *ib = _mystery.initBlock();
-	if (!ib)
-		return;
+// EEM2 case-intro animation ??? `_DoInitClues` @ 1abf:03b3. Ghidra-confirmed
+// flow (decompiled prologue):
+//   _AllBlack(); _GetBackground(0x52); _GetPalette(0x39);
+//   anim = _GetAnimation(DAT_4bd4 ? 0x71 : 0x18);   // Jake 0x18 / Jenny 0x71
+//   _NewAnimation(0xd2, 0x3f, anim, ...);            // registered at (210,63)
+//   _UpdateAnimations(); _FadeIn();                  // draw frame 0, then fade
+//   while (i != _max()) { _CheckFrameRate(); _UpdateAnimations(); kbhit->skip }
+//   if (caseType == 1) { _LoadSoundName("phone1.voc"); _PlayVoice; _Wait... }
+// So, unlike EEM1's game/book/nancy cycle + `_PlayInSequence`, EEM2 registers
+// ONE partner animation drawn by `_UpdateAnimations` ??? hence we anchor frames
+// with `blitAnimFrameAnchored` ((0xd2 - miscflags, 0x3f - rowoff), per
+// `_UpdateAnimations @ 172b:09c1`), the same helper the map/site screens use.
+void EEMEngine::playLondonInitCluesAnim(uint16 caseType, const Picture &bg,
+										bool haveBriefingBg) {
+	const uint introAni = (_partner == kPartnerJake) ? 0x18 : 0x71;
+	const int kAnchorX = 0xd2, kAnchorY = 0x3f;  // _NewAnimation(0xd2, 0x3f)
+	Animation anim;
+	const bool haveAnim =
+		_aniArchive.loadAnimation(introAni, anim) && !anim.empty();
+
+	// _AllBlack(): blank the palette so the _FadeIn after frame 0 reveals the
+	// briefing (palette 0x39 ??? EEM2's 63-entry SITEPALS. shifts EEM1's UI set).
+	byte pal[kPalSize];
+	const bool havePal = getSitePalette(0x39, pal);
+	byte black[kPalSize] = {};
+	g_system->getPaletteManager()->setPalette(black, 0, 256);
+	g_system->updateScreen();
 
-	// CD InitBlock: u16 caseType; u16 startSite; <clue block>.
-	// Floppy InitBlock (FUN_19bb_042f): u8 caseType; u8 nSubjects;
-	// subjects[]; u8 nDialog; dialog_records[]. No startSite.
-	const bool floppy = isFloppy();
-	const uint16 caseType = floppy ? (uint16)ib[0] : READ_LE_UINT16(ib);
+	bool skip = false;
+	const uint frames = haveAnim ? (uint)anim.size() : 1;
+	for (uint frame = 0; frame < frames && !shouldQuit() && !skip; frame++) {
+		if (haveBriefingBg)
+			blitAt(bg, 0, 0);
+		if (haveAnim) {
+			Graphics::Surface *scr = g_system->lockScreen();
+			if (scr) {
+				blitAnimFrameAnchored(scr, anim[frame], kAnchorX, kAnchorY);
+				g_system->unlockScreen();
+			}
+		}
+		if (frame == 0 && havePal)
+			fadePaletteFromBlack(pal);  // _FadeIn @ 1abf:03b3
+		else
+			g_system->updateScreen();
 
-	if (!floppy) {
-		const uint16 startSite = READ_LE_UINT16(ib + 2);
-		if (startSite < Mystery::kVisitedSiteCap)
-			_mystery._onSites[startSite] = 1;
-		_mystery._siteNumber = startSite;
-		_mystery._lastSite = startSite;
-	} else {
-		// Floppy _DoMapScreen @ 1fed:1060 (FUN_1fed_07ed) walks every
-		// site unconditionally ??? mirror that by marking all visible.
-		const uint sites = _mystery.numSites();
-		for (uint s = 0; s < sites && s < Mystery::kVisitedSiteCap; s++)
-			_mystery._onSites[s] = 1;
-		_mystery._siteNumber = 0;
-		_mystery._lastSite = 0;
+		const uint32 wakeup = g_system->getMillis() + 140;
+		while (g_system->getMillis() < wakeup && !shouldQuit() && !skip) {
+			Common::Event ev;
+			while (g_system->getEventManager()->pollEvent(ev)) {
+				if (ev.type == Common::EVENT_KEYDOWN &&
+					ev.kbd.keycode == Common::KEYCODE_ESCAPE) {
+					interruptAudio(/* stopMusicToo= */ false);
+					skip = true;
+					break;
+				}
+				if (ev.type == Common::EVENT_LBUTTONDOWN ||
+					ev.type == Common::EVENT_KEYDOWN) {
+					skip = true;
+					break;
+				}
+			}
+			g_system->delayMillis(10);
+		}
 	}
 
-	// Case-briefing palette. EEM1 `_DoInitClues` uses SITEPALS index 0x22;
-	// EEM2 `_DoInitClues` @ 1abf:03b3 does `_GetBackground(0x52)` then
-	// `_GetPalette(0x39)` (its 63-entry SITEPALS. shifts the UI palettes).
-	// The briefing partner animation is an ANI sprite drawn under the screen
-	// palette, so the same index fixes both the background and the anim.
-	setSitePalette(isLondon() ? 0x39 : 0x22);
-	Picture bg;
-	const bool haveBriefingBg = _picsArchive.getPicture(0x52, bg);
-	if (haveBriefingBg)
-		blitAt(bg, 0, 0);
+	// caseType 1 rings phone1.voc (EEM1's PHONE.VOC fires on caseType 2).
+	if (_audio && caseType == 1) {
+		_audio->playVoc(Common::Path("phone1.voc"));
+		_audio->waitForVoiceDone();
+	}
+}
 
+// EEM1 CD/floppy case-intro animation ??? `_DoInitClues @ 1a35:0411`:
+//   anims registered: game @ (0xcd,0x6c) [0x17 Jake / 0x3b Jenny],
+//   book @ (0,99) [0x18 / 0x3c], nancy @ (0x68,0x8b) [0x19, caseType 1 only];
+//   cycle the game anim once (click skips), then `_PlayInSequence @ 172b:2d03`
+//   plays the partner entrance per partner+caseType. A phone/news voice rings
+//   first on CD caseType 2 / floppy caseType 2-3. (London: see
+//   playLondonInitCluesAnim.)
+void EEMEngine::playCdFloppyInitCluesAnim(uint16 caseType, bool floppy,
+										  const Picture &bg, bool haveBriefingBg) {
 	const uint gameAni = _partner == kPartnerJake ? 0x17 : 0x3b;
 	const uint bookAni = _partner == kPartnerJake ? 0x18 : 0x3c;
 	Animation game, book, nancy;
@@ -513,6 +545,62 @@ void EEMEngine::doInitClues() {
 			}
 		}
 	}
+}
+
+// _DoInitClues @ 1a35:0411 (EEM1) / 1abf:03b3 (EEM2). Sequence:
+//   1. mark the start site / visited sites from the InitBlock
+//   2. BG PIC 0x52 + briefing palette (EEM1 0x22 / EEM2 0x39)
+//   3. partner entrance animation (CD/floppy or London variant)
+//   4. _DisplayClue(InitBlock + 2) ??? briefing dialogue
+void EEMEngine::doInitClues() {
+	if (!_mystery.isLoaded())
+		return;
+
+	const byte *ib = _mystery.initBlock();
+	if (!ib)
+		return;
+
+	// CD InitBlock: u16 caseType; u16 startSite; <clue block>.
+	// Floppy InitBlock (FUN_19bb_042f): u8 caseType; u8 nSubjects;
+	// subjects[]; u8 nDialog; dialog_records[]. No startSite.
+	const bool floppy = isFloppy();
+	const uint16 caseType = floppy ? (uint16)ib[0] : READ_LE_UINT16(ib);
+
+	if (!floppy) {
+		const uint16 startSite = READ_LE_UINT16(ib + 2);
+		if (startSite < Mystery::kVisitedSiteCap)
+			_mystery._onSites[startSite] = 1;
+		_mystery._siteNumber = startSite;
+		_mystery._lastSite = startSite;
+	} else {
+		// Floppy _DoMapScreen @ 1fed:1060 (FUN_1fed_07ed) walks every
+		// site unconditionally ??? mirror that by marking all visible.
+		const uint sites = _mystery.numSites();
+		for (uint s = 0; s < sites && s < Mystery::kVisitedSiteCap; s++)
+			_mystery._onSites[s] = 1;
+		_mystery._siteNumber = 0;
+		_mystery._lastSite = 0;
+	}
+
+	// Case-briefing palette. EEM1 `_DoInitClues` uses SITEPALS index 0x22;
+	// EEM2 `_DoInitClues` @ 1abf:03b3 does `_GetBackground(0x52)` then
+	// `_GetPalette(0x39)` (its 63-entry SITEPALS. shifts the UI palettes).
+	// The briefing partner animation is an ANI sprite drawn under the screen
+	// palette, so the same index fixes both the background and the anim.
+	setSitePalette(isLondon() ? 0x39 : 0x22);
+	Picture bg;
+	const bool haveBriefingBg = _picsArchive.getPicture(0x52, bg);
+	if (haveBriefingBg)
+		blitAt(bg, 0, 0);
+
+	// Case-intro partner animation. EEM2 (`_DoInitClues` @ 1abf:03b3) plays a
+	// single partner anim (Jake 0x18 / Jenny 0x71); EEM1 CD/floppy runs a
+	// game/book/nancy cycle + `_PlayInSequence` entrance. Both then feed the
+	// shared briefing dialogue below.
+	if (isLondon())
+		playLondonInitCluesAnim(caseType, bg, haveBriefingBg);
+	else
+		playCdFloppyInitCluesAnim(caseType, floppy, bg, haveBriefingBg);
 
 	// Briefing dialogue. CD: clue block @ ib+4 (after caseType,startSite).
 	// Floppy: dialog records dispatched via FUN_22dc_05c8 @ 22dc:05c8
diff --git a/engines/eem/detection.cpp b/engines/eem/detection.cpp
index 62d6c3901bd..714b2d81821 100644
--- a/engines/eem/detection.cpp
+++ b/engines/eem/detection.cpp
@@ -68,10 +68,10 @@ const ADGameDescription gameDescriptions[] = {
 		GUI_OPTIONS_EEM_FLOPPY
 	},
 	{
-		// Eagle Eye Mysteries in London (EEM2CD.EXE) ??? proof of concept.
-		// The sequel reuses this engine's resource formats (DBD/DBX
-		// archives, SITEPALS palettes, FONT.FNT), so only the opening
-		// screens load for now; flagged unstable until properly supported.
+		// Eagle Eye Mysteries in London (EEM2CD.EXE), the sequel.
+		// It reuses this engine's resource formats (DBD/DBX archives,
+		// SITEPALS palettes, FONT.FNT); reimplementation is in progress,
+		// so it stays flagged unstable until fully supported.
 		"eem2",
 		"CD",
 		AD_ENTRY2s("EEM2CD.EXE", "211a376b23a1b6259d0c36cf46d26ed4", 172560,
diff --git a/engines/eem/eem.cpp b/engines/eem/eem.cpp
index 66d5b11f08e..9b78267ec4c 100644
--- a/engines/eem/eem.cpp
+++ b/engines/eem/eem.cpp
@@ -48,7 +48,6 @@
 
 namespace EEM {
 
-const uint kPalSize = 768;
 const uint kNumSitePals = 40;  // SITEPALS: 40 * 768 = 30720
 
 // 1-based picture/palette IDs.
@@ -66,7 +65,7 @@ const byte kSaveBodyVer = 1;
 // option or changing save format. Set false before release.
 const bool kDebugPopulateScrapbook1AtStartup = false;
 
-void fadeCurrentPaletteToBlack(uint delayMs = 8) {
+void fadeCurrentPaletteToBlack(uint delayMs) {
 	byte start[kPalSize];
 	byte stepPal[kPalSize];
 	g_system->getPaletteManager()->grabPalette(start, 0, 256);
@@ -81,7 +80,7 @@ void fadeCurrentPaletteToBlack(uint delayMs = 8) {
 	}
 }
 
-void fadePaletteFromBlack(const byte *target, uint delayMs = 8) {
+void fadePaletteFromBlack(const byte *target, uint delayMs) {
 	byte stepPal[kPalSize];
 
 	for (uint step = 1; step <= 16; step++) {
@@ -331,13 +330,13 @@ Common::Error EEMEngine::run() {
 	if (resumed)
 		goto screenLoop;
 
-	// EEM2 ("Eagle Eye Mysteries in London") proof of concept: opening
-	// sequence + character creation, then start the training case (mystery
-	// 0 ??? what a new detective plays first) and reuse the engine's gameplay
-	// screen loop. EEM2's mystery data format matches EEM1-CD, so the shared
-	// Mystery parser + InitClues (case intro) / BigMap / Site handlers apply.
+	// EEM2 ("Eagle Eye Mysteries in London"): opening sequence + character
+	// creation, then start the training case (mystery 0 ??? what a new
+	// detective plays first) and reuse the engine's gameplay screen loop.
+	// EEM2's mystery data format matches EEM1-CD, so the shared Mystery
+	// parser + InitClues (case intro) / BigMap / Site handlers apply.
 	if (isLondon()) {
-		runLondonScreensPoc();
+		runLondonStartup();
 		if (!shouldQuit()) {
 			CursorMan.showMouse(true);
 			if (_mystery.load(0, &_rng)) {
@@ -1060,7 +1059,7 @@ void EEMEngine::showLondonLogo(uint picId, uint palId, uint holdMs) {
 	fadeCurrentPaletteToBlack();
 }
 
-void EEMEngine::runLondonScreensPoc() {
+void EEMEngine::runLondonStartup() {
 	// Full opening sequence ??? EEM2 `_DoOpeningAnims` @ 2721:08e6:
 	//   _ShowEAKids   @ 2721:05e3 ??? PIC 0x54,  palette 0x3c
 	//   FUN_2721_07be @ 2721:07be ??? PIC 0x20c, palette 0x3e (publisher logo)
@@ -1073,7 +1072,7 @@ void EEMEngine::runLondonScreensPoc() {
 	const uint32 kHoldForever = 0xFFFFFFFFu;
 	CursorMan.showMouse(false);
 	_skipIntro = false;
-	debugC(1, kDebugGeneral, "EEM2 (London) PoC: opening sequence");
+	debugC(1, kDebugGeneral, "EEM2 (London): opening sequence");
 
 	// Two still logos.
 	if (!shouldQuit() && !_skipIntro)
@@ -1101,7 +1100,7 @@ void EEMEngine::runLondonScreensPoc() {
 
 	// Animated title (wave.anm) over the looping theme (MUS00102.XMI);
 	// a click / key advances to character creation. The original loops
-	// wave.anm; the PoC plays it once, holds the last frame and waits.
+	// wave.anm; we play it once, hold the last frame and wait.
 	if (!shouldQuit() && !_skipIntro && _music)
 		_music->playFile(Common::Path("MUS00102.XMI"), /* loop= */ true);
 	if (!shouldQuit() && !_skipIntro)
@@ -1120,7 +1119,7 @@ void EEMEngine::runLondonScreensPoc() {
 	if (!shouldQuit())
 		showLondonCharSelect();
 
-	debugC(1, kDebugGeneral, "EEM2 (London) PoC: intro + character creation done");
+	debugC(1, kDebugGeneral, "EEM2 (London): intro + character creation done");
 }
 
 void EEMEngine::showLondonCharSelect() {
@@ -1131,7 +1130,7 @@ void EEMEngine::showLondonCharSelect() {
 	// `pr` player record's FirstName[12] / LastName[20]:
 	//   first name : (54,75)-(151,85)    last name : (167,75)-(266,85)
 	//   Jake box   : (110,116)-(120,122) Jenny box : (190,116)-(200,122)
-	debugC(1, kDebugGeneral, "EEM2 (London) PoC: character creation");
+	debugC(1, kDebugGeneral, "EEM2 (London): character creation");
 
 	const Common::Rect kFirstRect(54, 75, 151, 85);
 	const Common::Rect kLastRect(167, 75, 266, 85);
@@ -1249,7 +1248,7 @@ void EEMEngine::showLondonCharSelect() {
 	_chainStage = 1;
 	for (uint i = 0; i < sizeof(_mysteriesSolved); i++)
 		_mysteriesSolved[i] = 0;
-	debugC(1, kDebugGeneral, "London PoC: player='%s' partner=%s",
+	debugC(1, kDebugGeneral, "London: player='%s' partner=%s",
 		   _playerName.c_str(), partner == kPartnerJake ? "Jake" : "Jenny");
 }
 
diff --git a/engines/eem/eem.h b/engines/eem/eem.h
index 8fcb1abeec3..773e6a9532c 100644
--- a/engines/eem/eem.h
+++ b/engines/eem/eem.h
@@ -47,6 +47,14 @@ namespace EEM {
 class AudioPlayer;
 class MusicPlayer;
 
+/// VGA palette size in bytes (256 colours ?? RGB). Defined in eem.cpp.
+const uint kPalSize = 768;
+
+/// Palette fade helpers (defined in eem.cpp): ramp the VGA palette to / from
+/// black over 16 steps, matching `_FadeToBlack` / `_OpenFadeIn`.
+void fadeCurrentPaletteToBlack(uint delayMs = 8);
+void fadePaletteFromBlack(const byte *target, uint delayMs = 8);
+
 /// _ScreenDriver dispatch table @ 1a35:0e5e (fallback @ 1a35:0e54).
 /// 14 entries total: each is a screen ID + near fn ptr at +0x1c; driver
 /// tail-calls via `JMP word ptr CS:[BX + 0x1c]`. Handler bodies update
@@ -103,7 +111,7 @@ enum ScreenId {
 enum Variant {
 	kVariantCD       = 0,
 	kVariantFloppy   = 1,
-	kVariantLondonCD = 2, ///< Eagle Eye Mysteries in London (EEM2CD.EXE) ??? PoC.
+	kVariantLondonCD = 2, ///< Eagle Eye Mysteries in London (EEM2CD.EXE).
 };
 
 /// `_Partner @ 29be:7918`. Selected at the partner-pick screen
@@ -135,9 +143,9 @@ public:
 	Common::Platform getPlatform() const;
 	Variant getVariant() const { return _variant; }
 	bool isFloppy() const { return _variant == kVariantFloppy; }
-	/// EEM2 ("Eagle Eye Mysteries in London"). Proof-of-concept variant:
-	/// same DBD/palette/font formats, but a 63-entry "SITEPALS." file and
-	/// different opening picture/palette IDs.
+	/// EEM2 ("Eagle Eye Mysteries in London") ??? reimplementation in progress.
+	/// Shares the EEM1 DBD/palette/font formats, but ships a 63-entry
+	/// "SITEPALS." file and uses different picture/palette IDs per screen.
 	bool isLondon() const { return _variant == kVariantLondonCD; }
 
 	bool hasFeature(EngineFeature f) const override;
@@ -408,17 +416,24 @@ private:
 	void showHighScoreLogo();
 	void showFloppyStormLogo();
 
-	// --- EEM2 ("...in London") proof of concept ---
-	/// Full opening sequence + character-selection screen ??? EEM2's
-	/// `_DoOpeningAnims` @ 2721:08e6 then `_NewPlayer` @ 1cd3:0f27.
-	void runLondonScreensPoc();
+	// --- EEM2 ("Eagle Eye Mysteries in London") ??? reimplementation in progress ---
+	/// Opening sequence + character creation ??? EEM2's `_DoOpeningAnims`
+	/// @ 2721:08e6 then `_NewPlayer` @ 1cd3:0f27.
+	void runLondonStartup();
 	/// Blit a full-screen still PIC and fade it in / hold / out using the
 	/// given SITEPALS. palette index.
 	void showLondonLogo(uint picId, uint palId, uint holdMs);
-	/// Render EEM2's character-creation screen (`_NewPlayer`: palette 0 +
-	/// background PIC 0xc, where the player types a name and picks
-	/// Jake/Jenny). PoC display; interactive entry is still TODO.
+	/// EEM2 character creation (`_NewPlayer`: palette 0 + background PIC 0xc):
+	/// first/last name entry + Jake/Jenny selection.
 	void showLondonCharSelect();
+	/// EEM2 case-intro animation (`_DoInitClues` @ 1abf:03b3): single partner
+	/// anim (Jake 0x18 / Jenny 0x71) faded in, then phone1.voc on caseType 1.
+	void playLondonInitCluesAnim(uint16 caseType, const Picture &bg,
+								 bool haveBriefingBg);
+	/// EEM1 CD/floppy case-intro animation (`_DoInitClues` @ 1a35:0411):
+	/// game/book/nancy cycle + `_PlayInSequence` partner entrance.
+	void playCdFloppyInitCluesAnim(uint16 caseType, bool floppy,
+								   const Picture &bg, bool haveBriefingBg);
 
 	/// `screen8_handler @ 1c33:1012`. Profile selector ??? walks
 	/// `listProfiles()`, falls through to `doNewPlayer()` if "New" or
diff --git a/engines/eem/ui.cpp b/engines/eem/ui.cpp
index 0d6cb294edb..7a5d552084e 100644
--- a/engines/eem/ui.cpp
+++ b/engines/eem/ui.cpp
@@ -2118,11 +2118,11 @@ void EEMEngine::doCaseSelection() {
 
 	const uint mn = stageLo + selRow;
 	if (isLondon()) {
-		// EEM2 PoC: the menu (PIC 0x41, ANI 0x15/0x16, BOOK*.NME) is shared
-		// with EEM1, but EEM2's mystery data (M*.BIN/E*.BIN) isn't ported,
-		// so stop here instead of parsing it with the EEM1 loader.
+		// EEM2: the menu (PIC 0x41, ANI 0x15/0x16, BOOK*.NME) is shared with
+		// EEM1, but loading a chosen mystery from the menu isn't wired up yet
+		// (the training case is started directly from run()), so stop here.
 		debugC(1, kDebugMystery,
-			   "London PoC: selected mystery %u (load not implemented)", mn);
+			   "London: selected mystery %u (menu load not implemented yet)", mn);
 		return;
 	}
 	if (!_mystery.load(mn, &_rng)) {


Commit: 24d75f3cc78870ad0dc7372561fa5541e3fbccf8
    https://github.com/scummvm/scummvm/commit/24d75f3cc78870ad0dc7372561fa5541e3fbccf8
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:21:22+02:00

Commit Message:
EEM: training case in London plays renders the first site

Changed paths:
    engines/eem/clues.cpp


diff --git a/engines/eem/clues.cpp b/engines/eem/clues.cpp
index fc35a14407a..8a314cac743 100644
--- a/engines/eem/clues.cpp
+++ b/engines/eem/clues.cpp
@@ -61,7 +61,8 @@ const int kJakeY  = 0x62; // 98
 const int kJennyX = 0x42; // 66
 const int kJennyY = 0x60; // 96
 
-uint markClueBlockNotebookEntries(Mystery &mystery, const byte *clueBlock) {
+uint markClueBlockNotebookEntries(Mystery &mystery, const byte *clueBlock,
+								  bool isLondon) {
 	if (!clueBlock)
 		return 0;
 
@@ -69,11 +70,15 @@ uint markClueBlockNotebookEntries(Mystery &mystery, const byte *clueBlock) {
 	if (number == 0 || number > 32)
 		return 0;
 
+	// EEM2 entries are 84 bytes (0x54) with the notebook list at entry+0x40;
+	// EEM1 entries are 62 bytes with the list at entry+0x30. See displayClue.
+	const uint stride    = isLondon ? 0x54 : 62;
+	const uint noteOffat = isLondon ? 0x3c : 0x30;
 	uint marked = 0;
 	for (uint i = 0; i < number; i++) {
-		const byte *entry = clueBlock + 4 + i * 62;
+		const byte *entry = clueBlock + 4 + i * stride;
 		for (uint j = 0; j < 5; j++) {
-			const uint16 note = READ_LE_UINT16(entry + 0x30 + j * 2);
+			const uint16 note = READ_LE_UINT16(entry + noteOffat + j * 2);
 			if (note != 0xFFFF && note < Mystery::kCluesFoundCap &&
 				mystery._cluesFound[note] == 0) {
 				mystery._cluesFound[note] = 1;
@@ -611,7 +616,8 @@ void EEMEngine::doInitClues() {
 		const byte *briefingClues = ib + 4;
 		// _DisplayClue calls _AddNotebook for each ClueEntry note list at
 		// +0x30..+0x39. Mark starting notes before the first PDA visit.
-		const uint marked = markClueBlockNotebookEntries(_mystery, briefingClues);
+		const uint marked = markClueBlockNotebookEntries(_mystery, briefingClues,
+														 isLondon());
 		if (marked != 0)
 			debugC(1, kDebugScript,
 				   "doInitClues: marked %u CD briefing notebook entries",
@@ -717,6 +723,37 @@ Common::String EEMEngine::parseString(const Common::String &raw,
 }
 
 void EEMEngine::applyClueSideEffects(const byte *c) {
+	if (isLondon()) {
+		// EEM2 `_DisplayClue @ 2542:05bd` per-entry side effects. With the
+		// shared `c = entryBase + 4` convention the EEM2 fields land at:
+		//   onsite  entry+0x22 (= c+0x1e), 5 ?? u16, high bit = CONSITE flag
+		//   offsite entry+0x2c (= c+0x28), 5 ?? u16, clears the site
+		//   notebook entry+0x40 (= c+0x3c), 5 ?? u16 -> _AddNotebook
+		// (EEM2 has no gallery list here ??? that region is the onsite array.)
+		for (uint j = 0; j < 5; j++) {
+			const uint16 note = READ_LE_UINT16(c + 0x3c + j * 2);
+			if (note != 0xFFFF && note < Mystery::kCluesFoundCap)
+				_mystery._cluesFound[note] = 1;
+
+			const uint16 onIdx = READ_LE_UINT16(c + 0x1e + j * 2);
+			if (onIdx != 0xFFFF) {
+				const uint16 siteVal = onIdx & 0x7FFF;
+				if (siteVal < Mystery::kVisitedSiteCap)
+					_mystery._onSites[siteVal] = 1;
+				if (onIdx & 0x8000)
+					_mystery._sawCONSITEs = true;
+			}
+
+			const uint16 offIdx = READ_LE_UINT16(c + 0x28 + j * 2);
+			if (offIdx != 0xFFFF && (offIdx & 0x8000) == 0) {
+				const uint16 siteVal = offIdx & 0x7FFF;
+				if (siteVal < Mystery::kVisitedSiteCap)
+					_mystery._onSites[siteVal] = 0;
+			}
+		}
+		return;
+	}
+
 	for (uint j = 0; j < 5; j++) {
 		const uint16 note = READ_LE_UINT16(c + 0x30 + j * 2);
 		if (note != 0xFFFF && note < Mystery::kCluesFoundCap)
@@ -754,6 +791,13 @@ void EEMEngine::displayClue(const byte *clueBlock) {
 	if (number == 0 || number > 32)
 		return;
 
+	// ClueEntry stride. EEM1 `_DisplayClue @ 2404:05e6` packs 62-byte entries;
+	// EEM2 `_DisplayClue @ 2542:05bd` indexes `theClue + i*0x54` (84 bytes). The
+	// per-entry fields up to +0x1e are layout-identical (so entry 0 ??? at the
+	// shared base clueBlock+4 ??? reads the same either way); only the stride and
+	// the tail fields (KD anim, notebook, onsite) move. See applyClueSideEffects.
+	const uint stride = isLondon() ? 0x54 : 62;
+
 	// Snapshot BG so per-entry character pics don't stack.
 	Graphics::ManagedSurface bg(kScreenWidth, kScreenHeight,
 		Graphics::PixelFormat::createFormatCLUT8());
@@ -781,11 +825,12 @@ void EEMEngine::displayClue(const byte *clueBlock) {
 	// Partner 0 always uses field 0.
 	for (uint i = 0; i < number && !shouldQuit(); i++) {
 		g_system->copyRectToScreen(bg.getPixels(), bg.pitch, 0, 0, kScreenWidth, kScreenHeight);
-		const byte *c = clueBlock + 4 + i * 62;
+		const byte *c = clueBlock + 4 + i * stride;
 
 		// _DisplayClue @ 2404:0635-064b: _DoKDAnim(num) runs before the
-		// speaker portrait.
-		const int16 kdAnimNum = (int16)READ_LE_UINT16(c + 0x3a);
+		// speaker portrait. EEM1 stores the KD-anim number at +0x3a; EEM2
+		// `_DisplayClue @ 2542:05bd` reads it at entry+0x52 (= c+0x4e).
+		const int16 kdAnimNum = (int16)READ_LE_UINT16(c + (isLondon() ? 0x4e : 0x3a));
 		if (kdAnimNum != -1) {
 			playKdAnim((uint16)kdAnimNum);
 			// _UpdateAnimations @ 172b:09c1 reactivates the wait anim.
@@ -884,10 +929,17 @@ void EEMEngine::displayClue(const byte *clueBlock) {
 			_font.drawWordWrapped(&scratch, textX, textY,
 				MAX<int>(8, textW), text, 0);
 
-			g_system->copyRectToScreen(scratch.getBasePtr(0, copyY),
-				scratch.pitch, 0, copyY, kScreenWidth,
-				MIN<int>(copyH, kScreenHeight - copyY));
-			g_system->updateScreen();
+			// Clamp to the screen: a malformed entry (e.g. a clue-format
+			// mismatch) must never hand copyRectToScreen a negative height,
+			// which would underflow to a multi-GB memcpy.
+			copyY = CLIP<int>(copyY, 0, kScreenHeight - 1);
+			const int copyRows = CLIP<int>(MIN<int>(copyH, kScreenHeight - copyY),
+										   0, kScreenHeight - copyY);
+			if (copyRows > 0) {
+				g_system->copyRectToScreen(scratch.getBasePtr(0, copyY),
+					scratch.pitch, 0, copyY, kScreenWidth, copyRows);
+				g_system->updateScreen();
+			}
 		}
 
 		// _DisplayClue @ 2404:0833-085a ??? per-clue voice gate. The gate is on
@@ -948,7 +1000,7 @@ void EEMEngine::displayClue(const byte *clueBlock) {
 			if (skipAll) {
 				// Apply remaining side-effects without rendering.
 				for (uint k = i; k < number; k++)
-					applyClueSideEffects(clueBlock + 4 + k * 62);
+					applyClueSideEffects(clueBlock + 4 + k * stride);
 				return;
 			}
 		}


Commit: ea2453f9bc1643313f59c51435ace96f597d70df
    https://github.com/scummvm/scummvm/commit/ea2453f9bc1643313f59c51435ace96f597d70df
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:21:22+02:00

Commit Message:
EEM: improved animation in London

Changed paths:
    engines/eem/clues.cpp
    engines/eem/eem.cpp
    engines/eem/site.cpp
    engines/eem/site.h


diff --git a/engines/eem/clues.cpp b/engines/eem/clues.cpp
index 8a314cac743..fcfe31c2f39 100644
--- a/engines/eem/clues.cpp
+++ b/engines/eem/clues.cpp
@@ -295,15 +295,23 @@ void EEMEngine::playLondonInitCluesAnim(uint16 caseType, const Picture &bg,
 	g_system->getPaletteManager()->setPalette(black, 0, 256);
 	g_system->updateScreen();
 
+	// Drive the cells through the animation script (`_NewAnimation` prior
+	// 0x18 -> `_AnimationSequences[0x18]`), not a raw 0..N-1 sweep ??? the
+	// frame shown each tick is `script[tick]`, not `tick`. For EEM2 that
+	// script is the count-up {0..16}, but routing through `partnerFrameAtTick`
+	// keeps this faithful if the cells/script ever diverge and matches the map/
+	// site partner rendering. One pass = one script cell per ~140 ms tick.
 	bool skip = false;
 	const uint frames = haveAnim ? (uint)anim.size() : 1;
 	for (uint frame = 0; frame < frames && !shouldQuit() && !skip; frame++) {
 		if (haveBriefingBg)
 			blitAt(bg, 0, 0);
 		if (haveAnim) {
+			const uint cell =
+				partnerFrameAtTick(0x18, (uint)anim.size(), frame * 140);
 			Graphics::Surface *scr = g_system->lockScreen();
 			if (scr) {
-				blitAnimFrameAnchored(scr, anim[frame], kAnchorX, kAnchorY);
+				blitAnimFrameAnchored(scr, anim[cell], kAnchorX, kAnchorY);
 				g_system->unlockScreen();
 			}
 		}
diff --git a/engines/eem/eem.cpp b/engines/eem/eem.cpp
index 9b78267ec4c..57975e48bea 100644
--- a/engines/eem/eem.cpp
+++ b/engines/eem/eem.cpp
@@ -165,10 +165,13 @@ EEMEngine::EEMEngine(OSystem *syst, const ADGameDescription *gameDesc)
 				Common::String(gameDesc->extra).contains("Floppy"))
 				 ? kVariantFloppy : kVariantCD;
 	// EEM2 ("...in London") ships as a separate detection entry (gameId
-	// "eem2"); it reuses this engine but only the opening screens work.
+	// "eem2"); it reuses this engine with London-specific data.
 	if (gameDesc && gameDesc->gameId &&
 		Common::String(gameDesc->gameId) == "eem2")
 		_variant = kVariantLondonCD;
+	// EEM2 ships its own `_AnimationSequences` ??? many partner/KD scripts
+	// differ from EEM1's, so route `findAnimScript` to the EEM2 table.
+	setLondonAnimScripts(isLondon());
 	_language = gameDesc ? gameDesc->language : Common::EN_ANY;
 }
 
diff --git a/engines/eem/site.cpp b/engines/eem/site.cpp
index 90a2bc011de..466c9e13427 100644
--- a/engines/eem/site.cpp
+++ b/engines/eem/site.cpp
@@ -509,11 +509,67 @@ const uint8 kImpatientSequence[] = { 0,1,0,1,0,1,0,1,2,1 };
 // behavior but makes the feature observable during normal testing.
 const uint32 kImpatienceDelayMs = 60 * 1000;
 
+// EEM2 ("Eagle Eye Mysteries in London") animation scripts. EEM2 ships its own
+// `_AnimationSequences @ 2bca:2e2e` (read from EEM2CD.EXE) and MANY partner/KD
+// scripts differ from EEM1's ??? e.g. 0x18 is a plain count-up 0..16 in EEM2 but
+// "0..8, hold, 9..15" in EEM1 (`kScript18`); 0x14 is 0..10 vs 0..8; the PDA /
+// gesture / gallery scripts (0x01/0x03/0x04/0x05/0x06/0x0b/0x0c/0x0d/0x0e) are
+// all different lengths. Using EEM1's scripts on EEM2's cells plays the wrong
+// frame sequence (visible corruption). These override the EEM1 tables when the
+// London variant is active; any seqnum not listed falls through to the shared
+// EEM1 scripts below. Only the scripts that actually differ are listed.
+const uint8 kScript06London[] = {
+	0,1,2,3,4,5,6,7,8,9,10,11,11,11,5,6,7,8,9,10,11,11,11,
+	5,6,7,8,9,10,11,11,11,5,4,3,2,1,0
+};
+const AnimScript kAnimScriptsLondon[] = {
+	{ 0x01,  6, { 0,1,2,3,4,5 } },                                  // PDA idle
+	{ 0x03, 15, { 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14 } },           // gesture
+	{ 0x04, 23, { 0,1,2,3,4,5,5,5,6,5,5,5,5,6,5,5,5,5,4,3,2,1,0 } },// big gesture
+	{ 0x05, 11, { 0,1,2,3,4,4,4,3,2,1,0 } },
+	{ 0x0b,  6, { 0,1,2,3,4,5 } },                                  // Jenny PDA
+	{ 0x0c, 15, { 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14 } },           // Jenny gesture
+	{ 0x0d, 23, { 0,1,2,3,4,5,5,5,6,5,5,5,5,6,5,5,5,5,4,3,2,1,0 } },
+	{ 0x0e, 22, { 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,20 } },
+	{ 0x14, 11, { 0,1,2,3,4,5,6,7,8,9,10 } },                       // BigMap walk
+	{ 0x18, 17, { 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16 } },     // case-intro entrance
+	{ 0x19,  2, { 0,1 } },
+	{ 0x1a,  4, { 0,1,2,3 } },
+};
+const AnimScriptLong kAnimScriptsLondonLong[] = {
+	{ 0x06, 38, kScript06London },
+};
+
+// Set true for the London variant so findAnimScript uses the EEM2 tables.
+bool g_londonAnimScripts = false;
+
+void setLondonAnimScripts(bool enabled) {
+	g_londonAnimScripts = enabled;
+}
+
 struct AnimScriptRef {
 	const uint8 *frames;
 	uint16 len;
 };
 AnimScriptRef findAnimScript(uint16 seqnum) {
+	if (g_londonAnimScripts) {
+		for (uint i = 0; i < ARRAYSIZE(kAnimScriptsLondon); i++) {
+			if (kAnimScriptsLondon[i].seqnum == seqnum) {
+				AnimScriptRef r;
+				r.frames = kAnimScriptsLondon[i].frames;
+				r.len = kAnimScriptsLondon[i].len;
+				return r;
+			}
+		}
+		for (uint i = 0; i < ARRAYSIZE(kAnimScriptsLondonLong); i++) {
+			if (kAnimScriptsLondonLong[i].seqnum == seqnum) {
+				AnimScriptRef r;
+				r.frames = kAnimScriptsLondonLong[i].frames;
+				r.len = kAnimScriptsLondonLong[i].len;
+				return r;
+			}
+		}
+	}
 	for (uint i = 0; i < ARRAYSIZE(kAnimScripts); i++) {
 		if (kAnimScripts[i].seqnum == seqnum) {
 			AnimScriptRef r;
diff --git a/engines/eem/site.h b/engines/eem/site.h
index cc1b798e453..9f71863586e 100644
--- a/engines/eem/site.h
+++ b/engines/eem/site.h
@@ -43,6 +43,11 @@ class Mystery;
 /// Mirrors the looping path of `_UpdateAnimations @ 172b:09c1`.
 uint partnerFrameAtTick(uint16 seqnum, uint numFrames, uint32 tickMs);
 
+/// Select the EEM2 ("London") animation-script table inside `findAnimScript`.
+/// EEM2 ships its own `_AnimationSequences`; many partner/KD scripts differ
+/// from EEM1's, so the engine must use the EEM2 sequences for that variant.
+void setLondonAnimScripts(bool enabled);
+
 /// bigMapPartnerFrameAtTick: count-up 0..8 once, then loop `_BigMapWaitSeq`
 /// (9,9,9,9,10,9,9,9,9). Mirrors `_DoBigMap @ 20fe:09e7` two-phase swap from
 /// script 0x14 (count-up @ 29be:196a) to `_BigMapWaitSeq @ 29be:1574`.


Commit: dd0a8761aaaf881825137ea7e1f6432d634db178
    https://github.com/scummvm/scummvm/commit/dd0a8761aaaf881825137ea7e1f6432d634db178
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:21:23+02:00

Commit Message:
EEM: added more animation from London

Changed paths:
    engines/eem/site.cpp


diff --git a/engines/eem/site.cpp b/engines/eem/site.cpp
index 466c9e13427..0404614f1fd 100644
--- a/engines/eem/site.cpp
+++ b/engines/eem/site.cpp
@@ -178,6 +178,20 @@ const uint16 kKdAnimTable[6][6] = {
 	{ 0x06, 0x06, 6, 6, 80, 80 }, // 5 ??? same anim both partners
 };
 
+// EEM2 `_DoKDAnim @ 1717:05bf` table @ 2bca:0238 (read from EEM2CD.EXE).
+// Same { animJake, animJenny, xJake, xJenny, yJake, yJenny } layout. Positions
+// differ (x???2, y???78 vs EEM1's 6/80) and Jenny's reactions 4/5 use distinct
+// anims 0x55/0x2d (EEM1 reused 0x05/0x06 for both partners). Selected by
+// `playKdAnim` when the London variant is active.
+const uint16 kKdAnimTableLondon[6][6] = {
+	{ 0x03, 0x0c, 3, 2, 66, 65 }, // 0
+	{ 0x01, 0x0b, 2, 2, 78, 78 }, // 1
+	{ 0x04, 0x0d, 2, 2, 78, 78 }, // 2
+	{ 0x02, 0x10, 2, 2, 78, 78 }, // 3
+	{ 0x05, 0x55, 2, 2, 78, 78 }, // 4 ??? Jenny uses 0x55
+	{ 0x06, 0x2d, 2, 2, 78, 78 }, // 5 ??? Jenny uses 0x2d
+};
+
 // Animation script table ??? mirrors `_AnimationSequences @ 29be:22d4`
 // (55-entry table of far ptrs, each pointing to a u16-frame-index
 // stream). `_NewAnimation @ 172b:06e1` reads the script via
@@ -510,17 +524,62 @@ const uint8 kImpatientSequence[] = { 0,1,0,1,0,1,0,1,2,1 };
 const uint32 kImpatienceDelayMs = 60 * 1000;
 
 // EEM2 ("Eagle Eye Mysteries in London") animation scripts. EEM2 ships its own
-// `_AnimationSequences @ 2bca:2e2e` (read from EEM2CD.EXE) and MANY partner/KD
-// scripts differ from EEM1's ??? e.g. 0x18 is a plain count-up 0..16 in EEM2 but
-// "0..8, hold, 9..15" in EEM1 (`kScript18`); 0x14 is 0..10 vs 0..8; the PDA /
-// gesture / gallery scripts (0x01/0x03/0x04/0x05/0x06/0x0b/0x0c/0x0d/0x0e) are
-// all different lengths. Using EEM1's scripts on EEM2's cells plays the wrong
+// `_AnimationSequences @ 2bca:2e2e` (read from EEM2CD.EXE); essentially every
+// non-trivial partner / KD / site script differs from EEM1's ??? e.g. 0x18 is a
+// plain count-up 0..16 in EEM2 vs "0..8, hold, 9..15" in EEM1 (`kScript18`),
+// 0x14 is 0..10 vs 0..8. Using EEM1's scripts on EEM2's cells plays the wrong
 // frame sequence (visible corruption). These override the EEM1 tables when the
 // London variant is active; any seqnum not listed falls through to the shared
-// EEM1 scripts below. Only the scripts that actually differ are listed.
+// EEM1 scripts below. Only the seqnums that actually differ are listed.
+//
+// NOTE: EEM2 scripts 0x27/0x2e/0x30 end with a `0x81 N` jump (loop back to
+// entry N) rather than a 0x80 restart. `frameFromScriptAtTick` has no jump
+// support (EEM1 never used it), so the flat frame list is stored: correct for a
+// one-shot play-through, but a looped play replays the intro instead of just
+// the post-jump tail. Acceptable for these site NPC fidgets; revisit if needed.
 const uint8 kScript06London[] = {
-	0,1,2,3,4,5,6,7,8,9,10,11,11,11,5,6,7,8,9,10,11,11,11,
-	5,6,7,8,9,10,11,11,11,5,4,3,2,1,0
+	0,1,2,3,4,5,6,7,8,9,10,11,11,11,5,6,7,8,9,10,
+	11,11,11,5,6,7,8,9,10,11,11,11,5,4,3,2,1,0,
+};
+const uint8 kScript1dLondon[] = {
+	0,1,0,1,1,0,0,2,3,4,5,6,7,8,9,1,1,1,1,0,
+	0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+	2,2,2,2,2,2,
+};
+const uint8 kScript1eLondon[] = {
+	0,1,0,1,0,1,1,0,0,2,2,2,2,3,3,4,4,5,6,6,
+	4,3,2,1,0,1,0,1,0,1,1,0,0,
+};
+const uint8 kScript1fLondon[] = {
+	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+	0,1,1,1,1,2,1,3,3,4,5,6,5,6,4,1,1,1,
+};
+const uint8 kScript23London[] = {
+	0,0,0,0,1,1,2,2,3,3,3,4,4,3,3,3,4,4,1,0,
+	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+};
+const uint8 kScript25London[] = {
+	0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,
+	19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,
+	19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,
+};
+const uint8 kScript27London[] = {  // 0x81 jump->22
+	0,0,0,0,0,0,0,0,0,0,1,0,1,2,3,2,4,5,6,6,
+	7,6,8,6,7,7,7,7,8,
+};
+const uint8 kScript2bLondon[] = {
+	0,1,0,1,1,1,0,1,2,2,2,2,3,4,3,4,3,2,1,0,
+	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+};
+const uint8 kScript2dLondon[] = {
+	0,1,2,3,4,5,6,7,8,9,10,11,11,11,5,6,7,8,9,10,
+	11,11,11,5,6,7,8,9,10,11,11,11,5,4,3,2,1,0,
+};
+const uint8 kScript31London[] = {
+	32,32,32,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,
+	17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,
+	37,38,39,40,41,42,43,44,45,46,32,32,32,32,32,32,32,32,32,32,
+	32,32,
 };
 const AnimScript kAnimScriptsLondon[] = {
 	{ 0x01,  6, { 0,1,2,3,4,5 } },                                  // PDA idle
@@ -535,9 +594,38 @@ const AnimScript kAnimScriptsLondon[] = {
 	{ 0x18, 17, { 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16 } },     // case-intro entrance
 	{ 0x19,  2, { 0,1 } },
 	{ 0x1a,  4, { 0,1,2,3 } },
+	{ 0x1b, 12, { 0,0,1,1,2,2,3,3,4,4,5,5 } },
+	{ 0x1c, 19, { 11,11,11,0,1,2,3,4,5,6,7,8,9,10,11,11,11,11,11 } },
+	{ 0x20, 19, { 0,1,1,2,3,3,0,0,1,1,1,0,3,2,0,1,4,4,4 } },
+	{ 0x21,  5, { 0,0,1,1,2 } },
+	{ 0x22, 10, { 0,0,0,1,0,1,0,1,1,1 } },
+	{ 0x24, 18, { 0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 } },
+	{ 0x26, 20, { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,2,1,3 } },
+	{ 0x28, 16, { 0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7 } },
+	{ 0x29, 14, { 0,1,2,3,4,4,3,4,3,4,3,2,1,0 } },
+	{ 0x2a, 23, { 0,0,0,1,2,3,2,1,0,0,0,0,1,2,3,2,1,0,1,2,3,2,1 } },
+	{ 0x2c, 22, { 15,15,15,15,15,15,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15 } },
+	{ 0x2e, 16, { 0,0,0,1,2,3,4,5,6,7,8,9,10,11,12,12 } },          // 0x81 jump->15
+	{ 0x2f,  6, { 0,1,2,3,4,5 } },
+	{ 0x30, 26, { 18,18,18,18,18,18,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,18 } }, // 0x81 jump->25
+	{ 0x32, 27, { 0,0,0,0,1,1,0,0,0,1,0,0,0,0,0,0,0,0,1,1,0,0,0,1,0,0,1 } },
+	{ 0x33, 27, { 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,0,1,2,3,4,4,4,0,1,2,3 } },
+	{ 0x34,  4, { 0,1,2,3 } },
+	{ 0x35, 21, { 9,9,9,9,9,9,9,9,9,9,9,9,0,1,2,3,4,5,6,7,8 } },
+	{ 0x36, 12, { 0,0,0,0,0,1,2,3,4,5,6,6 } },
+	{ 0x55, 11, { 0,1,2,3,4,4,4,3,2,1,0 } },                        // KD Jenny reaction-4
 };
 const AnimScriptLong kAnimScriptsLondonLong[] = {
 	{ 0x06, 38, kScript06London },
+	{ 0x1d, 46, kScript1dLondon },
+	{ 0x1e, 33, kScript1eLondon },
+	{ 0x1f, 38, kScript1fLondon },
+	{ 0x23, 39, kScript23London },
+	{ 0x25, 59, kScript25London },
+	{ 0x27, 29, kScript27London },
+	{ 0x2b, 39, kScript2bLondon },
+	{ 0x2d, 38, kScript2dLondon },
+	{ 0x31, 62, kScript31London },
 };
 
 // Set true for the London variant so findAnimScript uses the EEM2 tables.
@@ -1805,14 +1893,16 @@ void EEMEngine::playKdAnim(uint16 num) {
 	// (blocking) and resume normal idle rendering when the caller
 	// returns ??? matches the visible effect (partner gesture finishes
 	// before the speaker portrait + speech balloon appear).
-	// `kKdAnimTable` and `kAnimScripts` live at file scope above.
+	// `kKdAnimTable` and `kAnimScripts` live at file scope above. EEM2 ships a
+	// different table (positions + Jenny reactions) ??? use it for London.
 	if (num >= ARRAYSIZE(kKdAnimTable))
 		return;
 
+	const uint16 (*kdTable)[6] = isLondon() ? kKdAnimTableLondon : kKdAnimTable;
 	const uint partner = (_partner == kPartnerJake) ? 0 : 1;
-	const uint16 animId = kKdAnimTable[num][partner];
-	const int    px     = (int)kKdAnimTable[num][2 + partner];
-	const int    py     = (int)kKdAnimTable[num][4 + partner];
+	const uint16 animId = kdTable[num][partner];
+	const int    px     = (int)kdTable[num][2 + partner];
+	const int    py     = (int)kdTable[num][4 + partner];
 
 	Animation anim;
 	if (!_aniArchive.loadAnimation(animId, anim) || anim.empty()) {


Commit: a5d0ea9233de74aed7ed1565873ce88de247609f
    https://github.com/scummvm/scummvm/commit/a5d0ea9233de74aed7ed1565873ce88de247609f
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:21:23+02:00

Commit Message:
EEM: mouse cursor change from London

Changed paths:
    engines/eem/eem.cpp
    engines/eem/eem.h
    engines/eem/site.cpp
    engines/eem/site.h


diff --git a/engines/eem/eem.cpp b/engines/eem/eem.cpp
index 57975e48bea..5207a68524e 100644
--- a/engines/eem/eem.cpp
+++ b/engines/eem/eem.cpp
@@ -59,6 +59,15 @@ const uint kPalHighScore       = 0x27;
 const uint kPalStormLogo       = 0x26;  // Floppy FUN_23d2_0605
 const uint kPicMousePointer    = 0x50;  // 0x51 is the wait cursor
 
+// EEM2 cursor table ??? `_main @ 1abf:0faf` loads seven cursor PICs into
+// `_AnimationObjects`-adjacent slots and `_SwitchMouse @ 17ee:2c83` activates
+// one by index. Order matches the slot order: 0 arrow, 1 wait, 2/3 examine,
+// 4 Jake hand, 5 Jenny hand, 6 approach. Indexed by the site search-record
+// cursor id (row +0xc).
+const uint16 kLondonCursorPics[7] = {
+	0x50, 0x51, 0x206, 0xa1, 0x207, 0x20b, 0x35e
+};
+
 const byte kSaveBodyVer = 1;
 
 // Test switch: populate ScrapBook 1 at startup without exposing a game
@@ -634,9 +643,44 @@ void EEMEngine::setInteractiveMouseCursor(bool active) {
 }
 
 void EEMEngine::setHotspotMouseCursor(bool active) {
+	// EEM2 swaps the cursor SHAPE per hotspot (see setSiteHotspotCursorId), so
+	// the EEM1 red-outline recolor doesn't apply. The bool path only resets to
+	// the default arrow (cursor 0) when leaving a hotspot / the site loop.
+	if (isLondon()) {
+		if (!active)
+			setSiteHotspotCursorId(0);
+		return;
+	}
 	setInteractiveMouseCursor(active);
 }
 
+void EEMEngine::setSiteHotspotCursorId(int cursorId) {
+	if (!isLondon())
+		return;
+	// `_SwitchMouse @ 17ee:2c83`: cursor 4 (Jake hand) becomes 5 for Jenny.
+	if (cursorId == 4 && _partner == kPartnerJenny)
+		cursorId = 5;
+	if (cursorId < 0 || cursorId >= (int)ARRAYSIZE(kLondonCursorPics))
+		cursorId = 0;
+	if (cursorId == _siteCursorId)
+		return;
+
+	Picture cursor;
+	if (!_picsArchive.getPicture(kLondonCursorPics[cursorId], cursor) ||
+		cursor.surface.empty()) {
+		warning("EEM2: cursor %d (PIC 0x%x) missing", cursorId,
+				kLondonCursorPics[cursorId]);
+		return;
+	}
+	// Each cursor carries its own transparent colour (flags >> 8) and uses the
+	// active screen palette (no separate cursor palette, unlike the EEM1
+	// red-outline highlight). Hotspot is the top-left (rowoff/misc are 0).
+	const byte transparent = (byte)(cursor.flags >> 8);
+	CursorMan.replaceCursor(cursor.surface.rawSurface(), 0, 0, transparent);
+	CursorMan.replaceCursorPalette(nullptr, 0, 0);
+	_siteCursorId = cursorId;
+}
+
 bool EEMEngine::openArchives() {
 	// _InitGraphicsSystem @ 172b:0145.
 	if (!_picsArchive.open(Common::Path("PICS.DBD"), Common::Path("PICS.DBX"))) {
diff --git a/engines/eem/eem.h b/engines/eem/eem.h
index 773e6a9532c..bb504769b3d 100644
--- a/engines/eem/eem.h
+++ b/engines/eem/eem.h
@@ -198,6 +198,12 @@ public:
 	/// Interactive cursor over searchable hotspots.
 	void setHotspotMouseCursor(bool active);
 
+	/// EEM2 `_SwitchMouse @ 17ee:2c83`: swap the cursor SHAPE to one of the
+	/// seven loaded cursors by ID (0 arrow, 1 wait, 2/3 examine, 4/5 partner
+	/// hand, 6 approach). London site hotspots carry a cursor ID at row +0xc;
+	/// EEM1 cursors are all 0 so this is a no-op there.
+	void setSiteHotspotCursorId(int cursorId);
+
 	/// `_DisplayClue @ 2404:05e6`. @p clueBlock points at the u16 frame
 	/// count followed by 62-byte ClueEntries.
 	void displayClue(const byte *clueBlock);
@@ -563,6 +569,9 @@ private:
 	Graphics::ManagedSurface _partnerEraseBg;
 
 	bool _interactiveMouseCursor = false;
+	/// Active EEM2 cursor shape (index into `kLondonCursorPics`). -1 forces a
+	/// reload on the next `setSiteHotspotCursorId`.
+	int _siteCursorId = 0;
 
 	/// Site whose entrance animation has already played this mystery.
 	/// Lives on the engine because PDA/gallery destroys+recreates SiteScreen.
diff --git a/engines/eem/site.cpp b/engines/eem/site.cpp
index 0404614f1fd..7598fad3163 100644
--- a/engines/eem/site.cpp
+++ b/engines/eem/site.cpp
@@ -1777,13 +1777,31 @@ int SiteScreen::hotspotAtPoint(uint siteNum, int x, int y) const {
 	return -1;
 }
 
+// CD hotspot row +0xc..d: cursor id for `_SwitchMouse` (EEM1 ships 0; EEM2
+// uses 2/3 examine, etc.). Floppy rows are 8-byte rects with no cursor field.
+int SiteScreen::hotspotCursorId(uint siteNum, int idx) const {
+	if (idx < 0 || (_vm && _vm->isFloppy()))
+		return 0;
+	const byte *spots = _mystery->hotspots(siteNum);
+	if (!spots || (uint)idx >= _mystery->hotspotCount(siteNum))
+		return 0;
+	return (int)READ_LE_UINT16(spots + idx * 14 + 0xc);
+}
+
 void SiteScreen::updateHotspotCursor(uint siteNum, int x, int y) {
 	if (!_vm)
 		return;
+	const int idx = hotspotAtPoint(siteNum, x, y);
+	if (_vm->isLondon()) {
+		// EEM2 (`_DoSiteLoop` @ FUN_1717_07ab) swaps the cursor shape to the
+		// hovered hotspot's own cursor id; off any hotspot it is the arrow (0).
+		_vm->setSiteHotspotCursorId(idx >= 0 ? hotspotCursorId(siteNum, idx) : 0);
+		return;
+	}
 	const bool siteControl = kPdaSiteRect.contains(x, y) ||
 							 kPdaPartnerFootMapRect.contains(x, y) ||
 							 kPdaPartnerHeadHintRect.contains(x, y);
-	_vm->setHotspotMouseCursor(siteControl || hotspotAtPoint(siteNum, x, y) >= 0);
+	_vm->setHotspotMouseCursor(siteControl || idx >= 0);
 }
 
 void SiteScreen::onHotspotClicked(uint siteNum, uint hotIdx) {
diff --git a/engines/eem/site.h b/engines/eem/site.h
index 9f71863586e..78513dd4d4c 100644
--- a/engines/eem/site.h
+++ b/engines/eem/site.h
@@ -111,6 +111,8 @@ private:
 	void renderBackground(uint siteNum);
 	void renderHotspots(uint siteNum);
 	int  hotspotAtPoint(uint siteNum, int x, int y) const;
+	/// EEM2 cursor id at CD hotspot row +0xc (0 for floppy / EEM1).
+	int  hotspotCursorId(uint siteNum, int idx) const;
 	void updateHotspotCursor(uint siteNum, int x, int y);
 	void onHotspotClicked(uint siteNum, uint hotIdx);
 	void initImpatienceCounter();


Commit: 27f48984c620e5747ff055ece33e730ebf5f57c9
    https://github.com/scummvm/scummvm/commit/27f48984c620e5747ff055ece33e730ebf5f57c9
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:21:23+02:00

Commit Message:
EEM: jump effect from applyClue in London

Changed paths:
    engines/eem/clues.cpp
    engines/eem/mystery.cpp
    engines/eem/mystery.h
    engines/eem/site.cpp
    engines/eem/ui.cpp


diff --git a/engines/eem/clues.cpp b/engines/eem/clues.cpp
index fcfe31c2f39..3fccb8ed644 100644
--- a/engines/eem/clues.cpp
+++ b/engines/eem/clues.cpp
@@ -737,6 +737,7 @@ void EEMEngine::applyClueSideEffects(const byte *c) {
 		//   onsite  entry+0x22 (= c+0x1e), 5 ?? u16, high bit = CONSITE flag
 		//   offsite entry+0x2c (= c+0x28), 5 ?? u16, clears the site
 		//   notebook entry+0x40 (= c+0x3c), 5 ?? u16 -> _AddNotebook
+		//   jump    entry+0x4a (= c+0x46), destination site for direct travel
 		// (EEM2 has no gallery list here ??? that region is the onsite array.)
 		for (uint j = 0; j < 5; j++) {
 			const uint16 note = READ_LE_UINT16(c + 0x3c + j * 2);
@@ -759,6 +760,9 @@ void EEMEngine::applyClueSideEffects(const byte *c) {
 					_mystery._onSites[siteVal] = 0;
 			}
 		}
+		const uint16 jumpSite = READ_LE_UINT16(c + 0x46);
+		if (jumpSite != 0xFFFF)
+			_mystery._pendingSiteJump = jumpSite;
 		return;
 	}
 
diff --git a/engines/eem/mystery.cpp b/engines/eem/mystery.cpp
index 06892c0735a..bb87dbc9939 100644
--- a/engines/eem/mystery.cpp
+++ b/engines/eem/mystery.cpp
@@ -64,6 +64,9 @@ void Mystery::clear() {
 	_firstTry = true;
 	_searchLocationNumber = _siteNumber = 0xFFFF;
 	_lastSite = 0x1B;
+	_pendingSiteJump = 0;
+	_siteReturnDepth = 0;
+	memset(_siteReturnStack, 0, sizeof(_siteReturnStack));
 }
 
 bool Mystery::load(uint num, Common::RandomSource *rng) {
@@ -154,6 +157,9 @@ bool Mystery::load(uint num, Common::RandomSource *rng) {
 		_firstTry = true;
 		_searchLocationNumber = _siteNumber = 0xFFFF;
 		_lastSite = 0x1B;
+		_pendingSiteJump = 0;
+		_siteReturnDepth = 0;
+		memset(_siteReturnStack, 0, sizeof(_siteReturnStack));
 		loadFloppySiteAnimData();
 
 		debugC(1, kDebugMystery,
@@ -213,6 +219,9 @@ bool Mystery::load(uint num, Common::RandomSource *rng) {
 	_firstTry = true;
 	_searchLocationNumber = _siteNumber = 0xFFFF;
 	_lastSite = 0x1B; // _ReadMystery _LastSite sentinel.
+	_pendingSiteJump = 0;
+	_siteReturnDepth = 0;
+	memset(_siteReturnStack, 0, sizeof(_siteReturnStack));
 
 	debugC(1, kDebugMystery, "Loaded %s (%d B): %u sites, %u suspects, "
 		   "CON=%u COFF=%u, init=0x%04x site=0x%04x text=0x%04x",
@@ -606,6 +615,13 @@ void Mystery::syncState(Common::Serializer &s) {
 	s.syncAsUint16LE(_searchLocationNumber);
 	s.syncAsUint16LE(_siteNumber);
 	s.syncAsUint16LE(_lastSite);
+	if (s.isLoading())
+		_pendingSiteJump = 0;
+	s.syncAsUint16LE(_siteReturnDepth);
+	s.syncArray(_siteReturnStack, kVisitedSiteCap,
+				Common::Serializer::Uint16LE);
+	if (_siteReturnDepth > kVisitedSiteCap)
+		_siteReturnDepth = 0;
 }
 
 } // End of namespace EEM
diff --git a/engines/eem/mystery.h b/engines/eem/mystery.h
index 07545cf6e23..7f3d16589c3 100644
--- a/engines/eem/mystery.h
+++ b/engines/eem/mystery.h
@@ -172,6 +172,9 @@ public:
 	uint16 _searchLocationNumber = 0xFFFF;
 	uint16 _siteNumber           = 0xFFFF;
 	uint16 _lastSite             = 0xFFFF;
+	uint16 _pendingSiteJump      = 0;      ///< EEM2 _DisplayClue destination site (DAT_2bca_0282).
+	uint16 _siteReturnDepth      = 0;      ///< EEM2 nested site return stack depth (DAT_2bca_0280).
+	uint16 _siteReturnStack[kVisitedSiteCap] = {};
 
 private:
 	Common::Array<byte> _data;
diff --git a/engines/eem/site.cpp b/engines/eem/site.cpp
index 7598fad3163..dfe94eb3a02 100644
--- a/engines/eem/site.cpp
+++ b/engines/eem/site.cpp
@@ -1028,6 +1028,9 @@ void SiteScreen::run() {
 	uint cur = _mystery->_siteNumber;
 	if (cur >= _mystery->numSites())
 		cur = 0;
+	_mystery->_pendingSiteJump = 0;
+	if (_mystery->_siteReturnDepth > Mystery::kVisitedSiteCap)
+		_mystery->_siteReturnDepth = 0;
 	_snapshotSite = -1;
 	SiteBackendActionObserverRegistration backendActionRegistration(this);
 	enter(cur);
@@ -1067,6 +1070,25 @@ void SiteScreen::run() {
 				if (kPdaPartnerFootMapRect.contains(event.mouse.x, event.mouse.y)) {
 					notePartnerActivity();
 					_vm->setHotspotMouseCursor(false);
+					if (_vm->isLondon() && _mystery->_siteReturnDepth != 0) {
+						const uint16 depth = --_mystery->_siteReturnDepth;
+						const uint16 returnSite = _mystery->_siteReturnStack[depth];
+						_mystery->_siteReturnStack[depth] = 0;
+						if (returnSite < _mystery->numSites()) {
+							debugC(1, kDebugSite,
+								   "London: returning from site %u to site %u",
+								   cur, returnSite);
+							_mystery->_lastSite = (uint16)cur;
+							_mystery->_siteNumber = returnSite;
+							cur = returnSite;
+							enter(cur);
+							mouse = g_system->getEventManager()->getMousePos();
+							updateHotspotCursor(cur, mouse.x, mouse.y);
+							break;
+						}
+						warning("London site return target %u out of range",
+								returnSite);
+					}
 					// CD: _NextScreen=1, floppy=2.
 					_vm->setNextScreen(_vm->isFloppy() ? kScreenMapAlt
 													   : kScreenMap);
@@ -1086,9 +1108,35 @@ void SiteScreen::run() {
 				const int idx = hotspotAtPoint(cur, event.mouse.x, event.mouse.y);
 				if (idx >= 0) {
 					_vm->setHotspotMouseCursor(false);
+					_mystery->_pendingSiteJump = 0;
 					onHotspotClicked(cur, (uint)idx);
 					notePartnerActivity();
-					enter(cur, false);
+					const uint16 jumpSite = _mystery->_pendingSiteJump;
+					_mystery->_pendingSiteJump = 0;
+					if (_vm->isLondon() && jumpSite != 0) {
+						if (jumpSite < _mystery->numSites()) {
+							if (_mystery->_siteReturnDepth < Mystery::kVisitedSiteCap) {
+								_mystery->_siteReturnStack[_mystery->_siteReturnDepth++] =
+									(uint16)cur;
+							} else {
+								warning("London site return stack full; "
+										"jumping without return site");
+							}
+							debugC(1, kDebugSite,
+								   "London: hotspot jump from site %u to site %u",
+								   cur, jumpSite);
+							_mystery->_lastSite = (uint16)cur;
+							_mystery->_siteNumber = jumpSite;
+							cur = jumpSite;
+							enter(cur);
+						} else {
+							warning("London hotspot jump target %u out of range",
+									jumpSite);
+							enter(cur, false);
+						}
+					} else {
+						enter(cur, false);
+					}
 					// Use CURRENT pointer position (click pos still in rect).
 					mouse = g_system->getEventManager()->getMousePos();
 					updateHotspotCursor(cur, mouse.x, mouse.y);
diff --git a/engines/eem/ui.cpp b/engines/eem/ui.cpp
index 7a5d552084e..048bc040fea 100644
--- a/engines/eem/ui.cpp
+++ b/engines/eem/ui.cpp
@@ -2954,6 +2954,12 @@ void EEMEngine::doBigMap() {
 	if (!_mystery.isLoaded())
 		return;
 
+	if (isLondon()) {
+		_mystery._pendingSiteJump = 0;
+		_mystery._siteReturnDepth = 0;
+		memset(_mystery._siteReturnStack, 0, sizeof(_mystery._siteReturnStack));
+	}
+
 	CursorMan.showMouse(true);
 
 	while (!shouldQuit()) {


Commit: 17cf0205f9b70d574feff551544faa77b30236fc
    https://github.com/scummvm/scummvm/commit/17cf0205f9b70d574feff551544faa77b30236fc
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:21:24+02:00

Commit Message:
EEM: correct parner handling in London

Changed paths:
    engines/eem/clues.cpp
    engines/eem/site.cpp


diff --git a/engines/eem/clues.cpp b/engines/eem/clues.cpp
index 3fccb8ed644..e75fdbda8ff 100644
--- a/engines/eem/clues.cpp
+++ b/engines/eem/clues.cpp
@@ -865,9 +865,12 @@ void EEMEngine::displayClue(const byte *clueBlock) {
 		// ClueBlock +2; entries N>0 read (entry-1)+0x3c (last word).
 		const uint16 charX  = READ_LE_UINT16(c + (useP1 ? 4 : 0));
 		const uint16 charY  = READ_LE_UINT16(c + (useP1 ? 6 : 2));
-		const uint16 charPicId = (i == 0)
+		uint16 charPicId = (i == 0)
 			? READ_LE_UINT16(clueBlock + 2)
 			: READ_LE_UINT16(c - 2);
+		if (isLondon() && charPicId == 0x13e &&
+			_partner == kPartnerJake)
+			charPicId = 0x13f;
 		if (charPicId != 0 && charPicId != 0xFFFF) {
 			Picture charPic;
 			if (_picsArchive.getPicture(charPicId, charPic) &&
diff --git a/engines/eem/site.cpp b/engines/eem/site.cpp
index dfe94eb3a02..0aa84dc739b 100644
--- a/engines/eem/site.cpp
+++ b/engines/eem/site.cpp
@@ -192,6 +192,18 @@ const uint16 kKdAnimTableLondon[6][6] = {
 	{ 0x06, 0x2d, 2, 2, 78, 78 }, // 5 ??? Jenny uses 0x2d
 };
 
+// EEM2 `_WaitAnims @ 2bca:022c`. Entry 0 precedes `_DoKDAnim`'s
+// table; entries 1..6 overlap `kKdAnimTableLondon`, same as EEM1.
+const uint16 kWaitAnimsLondon[7][6] = {
+	{ 0x00, 0x0a, 2, 2, 78, 78 }, // 0
+	{ 0x03, 0x0c, 3, 2, 66, 65 }, // 1
+	{ 0x01, 0x0b, 2, 2, 78, 78 }, // 2
+	{ 0x04, 0x0d, 2, 2, 78, 78 }, // 3
+	{ 0x02, 0x10, 2, 2, 78, 78 }, // 4
+	{ 0x05, 0x55, 2, 2, 78, 78 }, // 5
+	{ 0x06, 0x2d, 2, 2, 78, 78 }, // 6
+};
+
 // Animation script table ??? mirrors `_AnimationSequences @ 29be:22d4`
 // (55-entry table of far ptrs, each pointing to a u16-frame-index
 // stream). `_NewAnimation @ 172b:06e1` reads the script via
@@ -1215,6 +1227,39 @@ bool SiteScreen::enterSiteAnim() {
 	bg.simpleBlitFrom(*screen);
 	g_system->unlockScreen();
 
+	if (_vm->isLondon()) {
+		// EEM2 `_EnterSiteAnim @ 17ee:27a4`: no skateboard phase.
+		// It plays the partner-specific slide-in animation at anchor
+		// (0, 0x50/0x4e), using `_CheckFrameRate` between cells.
+		const uint animId = (partner == 0) ? 7 : 0xf;
+		const int anchorY = (partner == 0) ? 0x50 : 0x4e;
+		Animation anim;
+		if (!_vm->getAni().loadAnimation(animId, anim) || anim.empty())
+			return false;
+		for (uint frameIdx = 0;
+			 frameIdx < anim.size() && !_vm->shouldQuit();
+			 frameIdx++) {
+			Graphics::ManagedSurface scratch(kScreenWidth, kScreenHeight,
+				Graphics::PixelFormat::createFormatCLUT8());
+			scratch.simpleBlitFrom(bg);
+			blitAnimFrameAnchored(scratch.surfacePtr(), anim[frameIdx],
+								  0, anchorY);
+			g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
+									   0, 0, kScreenWidth, kScreenHeight);
+			g_system->updateScreen();
+
+			Common::Event ev;
+			while (g_system->getEventManager()->pollEvent(ev)) {
+				if (ev.type == Common::EVENT_KEYDOWN ||
+					ev.type == Common::EVENT_LBUTTONDOWN) {
+					return true;
+				}
+			}
+			g_system->delayMillis(kFramePeriodMs);
+		}
+		return false;
+	}
+
 	// Phase 1 ??? skateboard scroll.
 	Animation skate;
 	if (_vm->getAni().loadAnimation(kSkateAni, skate) && !skate.empty()) {
@@ -1312,9 +1357,12 @@ void SiteScreen::renderStaticDrops(uint siteNum) {
 
 	for (uint i = 0; i < numStatic; i++) {
 		const uint dropOff = 0xc + i * 6;
-		const uint16 picId = READ_LE_UINT16(site + dropOff + 0);
+		uint16 picId = READ_LE_UINT16(site + dropOff + 0);
 		const int16  x     = (int16)READ_LE_UINT16(site + dropOff + 2);
 		const int16  y     = (int16)READ_LE_UINT16(site + dropOff + 4);
+		if (_vm->isLondon() && picId == 0x140 &&
+			_vm->getPartnerIndex() == kPartnerJake)
+			picId = 0x141;
 		if (picId == 0)
 			continue;
 		Picture pic;
@@ -1562,14 +1610,16 @@ void SiteScreen::renderPartner(uint siteNum, uint32 tickMs) {
 		}
 	} else {
 		const uint16 speaker = READ_LE_UINT16(site + 8);
+		const uint16 (*waitTable)[6] = _vm->isLondon()
+			? kWaitAnimsLondon : kWaitAnims;
 		if (speaker >= ARRAYSIZE(kWaitAnims)) {
 			warning("renderPartner: site %u has speakerIdx=%u out of range",
 					siteNum, speaker);
 			return;
 		}
-		animId = kWaitAnims[speaker][0 + partner];
-		x      = (int)(int16)kWaitAnims[speaker][2 + partner];
-		y      = (int)(int16)kWaitAnims[speaker][4 + partner];
+		animId = waitTable[speaker][0 + partner];
+		x      = (int)(int16)waitTable[speaker][2 + partner];
+		y      = (int)(int16)waitTable[speaker][4 + partner];
 	}
 
 	Animation anim;


Commit: b8f606891d2c512bc6b67e9d81b7856acfa76ed4
    https://github.com/scummvm/scummvm/commit/b8f606891d2c512bc6b67e9d81b7856acfa76ed4
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:21:24+02:00

Commit Message:
EEM: implemented traveling animation in London

Changed paths:
    engines/eem/eem.cpp
    engines/eem/eem.h
    engines/eem/site.cpp
    engines/eem/site.h


diff --git a/engines/eem/eem.cpp b/engines/eem/eem.cpp
index 5207a68524e..36fe8d4fc8b 100644
--- a/engines/eem/eem.cpp
+++ b/engines/eem/eem.cpp
@@ -780,7 +780,8 @@ void EEMEngine::interruptAudio(bool stopMusicToo) {
 }
 
 void EEMEngine::playAnm(const Common::Path &path, uint frameDelayMs,
-						bool holdLastFrame, bool fadeIn) {
+						bool holdLastFrame, bool fadeIn,
+						bool setSkipIntroOnEsc) {
 	ANMDecoder anm;
 	if (!anm.open(path)) {
 		warning("playAnm: %s missing", path.toString().c_str());
@@ -832,8 +833,10 @@ void EEMEngine::playAnm(const Common::Path &path, uint frameDelayMs,
 				}
 				if (event.type == Common::EVENT_KEYDOWN) {
 					if (event.kbd.keycode == Common::KEYCODE_ESCAPE) {
-						_skipIntro = true;
-						interruptAudio();
+						if (setSkipIntroOnEsc) {
+							_skipIntro = true;
+							interruptAudio();
+						}
 					}
 					aborted = true;
 					break;
@@ -864,8 +867,10 @@ void EEMEngine::playAnm(const Common::Path &path, uint frameDelayMs,
 				}
 				if (ev.type == Common::EVENT_KEYDOWN) {
 					if (ev.kbd.keycode == Common::KEYCODE_ESCAPE) {
-						_skipIntro = true;
-						interruptAudio();
+						if (setSkipIntroOnEsc) {
+							_skipIntro = true;
+							interruptAudio();
+						}
 					}
 					clicked = true;
 					break;
@@ -1361,6 +1366,23 @@ void EEMEngine::startTravelMusic() {
 	_music->playMus(num, /* loop= */ false);
 }
 
+void EEMEngine::startLondonTravelMusic(uint8 travelKind) {
+	// EEM2 `_DoTravel @ 1717:06ae`: travelKind * 6 indexes this table,
+	// then rand() picks one of three u16 MUS IDs.
+	static const uint16 kLondonTravelMusic[4][3] = {
+		{ 0,  0,  0 },
+		{ 3, 22, 25 },
+		{ 7, 23, 17 },
+		{ 10, 21, 24 },
+	};
+	if (!_music || !_voiceOn || travelKind == 0 ||
+		travelKind >= ARRAYSIZE(kLondonTravelMusic))
+		return;
+
+	const uint track = kLondonTravelMusic[travelKind][_rng.getRandomNumber(2)];
+	_music->playMus(track, /* loop= */ false);
+}
+
 void EEMEngine::waitForMusicDone(uint32 maxMs) {
 	if (!_music)
 		return;
diff --git a/engines/eem/eem.h b/engines/eem/eem.h
index bb504769b3d..907dacb7d3a 100644
--- a/engines/eem/eem.h
+++ b/engines/eem/eem.h
@@ -400,7 +400,6 @@ private:
 
 public:
 	void waitForInput(uint32 maxMs);
-private:
 
 	/// Play a difference-encoded animation file (.ANM / .A) on the full
 	/// 320x200 screen. Mirrors the data flow of `OpenDifferenceAnimation
@@ -412,8 +411,10 @@ private:
 	/// If @p fadeIn is true the first decoded frame is copied while the
 	/// palette is black, then ramped in like `_OpenFadeIn`.
 	void playAnm(const Common::Path &path, uint frameDelayMs = 120,
-				 bool holdLastFrame = false, bool fadeIn = false);
+				 bool holdLastFrame = false, bool fadeIn = false,
+				 bool setSkipIntroOnEsc = true);
 
+private:
 	/// `_CleanMysterySounds @ 202f:05a5` + `_StopMIDI @ 20a2:0512`.
 	/// `stopMusicToo=false` keeps MIDI playing across dialog skips.
 	void interruptAudio(bool stopMusicToo = true);
@@ -519,6 +520,10 @@ public:
 	/// `Engine::syncSoundSettings` override. Re-pulls `music_volume`
 	/// into the MIDI player's `_masterVolume`.
 	void syncSoundSettings() override;
+
+	/// EEM2 `_DoTravel @ 1717:0622` transition music. The matrix entry
+	/// (1..3) chooses one of three short one-shot MUS tracks at random.
+	void startLondonTravelMusic(uint8 travelKind);
 private:
 
 	Common::String _playerName;  ///< Substituted into 0x80 placeholders.
diff --git a/engines/eem/site.cpp b/engines/eem/site.cpp
index 0aa84dc739b..c07ac1df532 100644
--- a/engines/eem/site.cpp
+++ b/engines/eem/site.cpp
@@ -22,6 +22,9 @@
 #include "common/config-manager.h"
 #include "common/debug.h"
 #include "common/events.h"
+#include "common/file.h"
+#include "common/path.h"
+#include "common/str.h"
 #include "common/system.h"
 #include "common/textconsole.h"
 
@@ -56,6 +59,30 @@ private:
 	Common::EventObserver *_observer;
 };
 
+const uint kLondonTravelMatrixDim = 60;
+const uint kLondonTravelFrameDelayMs = 120;
+const uint16 kLondonTravelLastSiteSentinel = 0x1b;
+
+bool londonTravelSitePic(const Mystery *mystery, uint siteNum, uint16 &sitePic) {
+	if (!mystery || !mystery->isLoaded())
+		return false;
+
+	if (siteNum < mystery->numSites()) {
+		const byte *sd = mystery->siteData(siteNum);
+		if (!sd)
+			return false;
+		sitePic = READ_LE_UINT16(sd);
+		return true;
+	}
+
+	if (siteNum == kLondonTravelLastSiteSentinel || siteNum == 0xffff) {
+		sitePic = kLondonTravelLastSiteSentinel;
+		return true;
+	}
+
+	return false;
+}
+
 // Masked blit using `transp` = high byte of `pic.flags` (`_Rect_Move_Mask @ 1000:03fc`).
 void blitFrame(Graphics::ManagedSurface &dst, const Picture &p,
 			   int x, int y, byte transp) {
@@ -848,6 +875,61 @@ uint bigMapDetailPartnerFrameAtTick(uint numFrames, uint32 elapsedMs) {
 									  numFrames, elapsedMs);
 }
 
+bool SiteScreen::playLondonTravelAnimation(uint fromSite, uint toSite) {
+	if (!_vm || !_mystery || !_vm->isLondon())
+		return false;
+
+	uint16 fromPic = 0;
+	uint16 toPic = 0;
+	if (!londonTravelSitePic(_mystery, fromSite, fromPic) ||
+		!londonTravelSitePic(_mystery, toSite, toPic))
+		return false;
+	if (fromPic >= kLondonTravelMatrixDim || toPic >= kLondonTravelMatrixDim)
+		return false;
+
+	Common::File matrix;
+	if (!matrix.open(Common::Path("TRAVEL.BIN"))) {
+		warning("London travel: TRAVEL.BIN missing");
+		return false;
+	}
+
+	const uint32 matrixOff = (uint32)fromPic * kLondonTravelMatrixDim + toPic;
+	if (!matrix.seek(matrixOff)) {
+		warning("London travel: seek to matrix offset %u failed", matrixOff);
+		return false;
+	}
+
+	byte travelKind = 0;
+	if (matrix.read(&travelKind, 1) != 1) {
+		warning("London travel: short read at matrix offset %u", matrixOff);
+		return false;
+	}
+	if (travelKind == 0)
+		return false;
+	if (travelKind > 3) {
+		warning("London travel: invalid matrix value %u for pic %u -> %u",
+				travelKind, fromPic, toPic);
+		return false;
+	}
+
+	// EEM2 `_DoTravel @ 1717:06ed`: kind 3 forces partner suffix 0,
+	// so the shipped set is TRAVEL00/01/10/11/20.ANM.
+	const uint partnerSuffix = (travelKind == 3) ? 0 : _vm->getPartnerIndex();
+	const Common::String name = Common::String::format("TRAVEL%u%u.ANM",
+		(uint)travelKind - 1, partnerSuffix);
+
+	debugC(1, kDebugSite,
+		   "London travel: site %u/pic %u -> site %u/pic %u, kind=%u, anim=%s",
+		   fromSite, fromPic, toSite, toPic, travelKind, name.c_str());
+	_vm->startLondonTravelMusic(travelKind);
+	fadeCurrentPaletteToBlack();
+	_vm->playAnm(Common::Path(name), kLondonTravelFrameDelayMs,
+				 /* holdLastFrame= */ false, /* fadeIn= */ true,
+				 /* setSkipIntroOnEsc= */ false);
+	_vm->stopMusic();
+	return true;
+}
+
 void SiteScreen::enter(uint siteNum, bool resetPartnerMood) {
 	if (!_mystery || !_mystery->isLoaded()) {
 		warning("SiteScreen::enter: no mystery loaded");
@@ -878,9 +960,16 @@ void SiteScreen::enter(uint siteNum, bool resetPartnerMood) {
 
 	const bool playArrival = _vm->shouldPlaySiteArrival(siteNum);
 
-	// `_DoTravel @ 168d:02da` calls `_StartTravelMusic`.
-	if (playArrival)
-		_vm->startTravelMusic();
+	if (playArrival) {
+		if (_vm->isLondon()) {
+			// EEM2 `HandleLondonSiteLoop @ 1717:083a` calls `_DoTravel`
+			// before `_BuildBackground` for the destination site.
+			playLondonTravelAnimation(_mystery->_lastSite, siteNum);
+		} else {
+			// `_DoTravel @ 168d:02da` calls `_StartTravelMusic`.
+			_vm->startTravelMusic();
+		}
+	}
 
 	// `_BuildBackground` calls `GetPalette(sitenum + 1)` ??? sitenum is the
 	// global SITES.DBD index (per-mystery `sitepic` field).
@@ -930,7 +1019,7 @@ void SiteScreen::enter(uint siteNum, bool resetPartnerMood) {
 		renderAnimatedDrops(siteNum, g_system->getMillis());
 		const bool skippedArrival = enterSiteAnim();
 		_vm->markSiteArrivalPlayed(siteNum);
-		if (!_vm->isFloppy()) {
+		if (!_vm->isFloppy() && !_vm->isLondon()) {
 			if (skippedArrival)
 				_vm->stopMusic();
 			else
diff --git a/engines/eem/site.h b/engines/eem/site.h
index 78513dd4d4c..c91f0cffeb8 100644
--- a/engines/eem/site.h
+++ b/engines/eem/site.h
@@ -118,6 +118,7 @@ private:
 	void initImpatienceCounter();
 	bool checkImpatienceCounter();
 	void notePartnerActivity();
+	bool playLondonTravelAnimation(uint fromSite, uint toSite);
 
 	/// Partner site-arrival sequence (when `_LastSite != _SiteNumber`).
 	/// Mirrors `_EnterSiteAnim @ 1000:9b21`: anim 6/14 (Jake/Jenny) skateboards


Commit: 279d74a5413c753078808263c59160fed3317fe6
    https://github.com/scummvm/scummvm/commit/279d74a5413c753078808263c59160fed3317fe6
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:21:25+02:00

Commit Message:
EEM: implemented edutaintment intro video of some locations in London

Changed paths:
    engines/eem/eem.h
    engines/eem/site.cpp
    engines/eem/ui.cpp


diff --git a/engines/eem/eem.h b/engines/eem/eem.h
index 907dacb7d3a..710573ff2b9 100644
--- a/engines/eem/eem.h
+++ b/engines/eem/eem.h
@@ -517,6 +517,10 @@ public:
 	/// `_StopMIDI @ 20a2:0512`.
 	void stopMusic();
 
+	/// EEM2 `_DoApproach @ 1717:009b`: London-only place information
+	/// screen with a short VIDEOnn.A clip and text pages from Ann.BIN.
+	bool doLondonApproach(uint16 approachId);
+
 	/// `Engine::syncSoundSettings` override. Re-pulls `music_volume`
 	/// into the MIDI player's `_masterVolume`.
 	void syncSoundSettings() override;
diff --git a/engines/eem/site.cpp b/engines/eem/site.cpp
index c07ac1df532..5dba04a5f01 100644
--- a/engines/eem/site.cpp
+++ b/engines/eem/site.cpp
@@ -958,13 +958,19 @@ void SiteScreen::enter(uint siteNum, bool resetPartnerMood) {
 	debugC(1, kDebugSite, "Entering site %u (%u hotspots)",
 		   siteNum, _mystery->hotspotCount(siteNum));
 
+	const byte *sd = _mystery->siteData(siteNum);
 	const bool playArrival = _vm->shouldPlaySiteArrival(siteNum);
 
 	if (playArrival) {
 		if (_vm->isLondon()) {
 			// EEM2 `HandleLondonSiteLoop @ 1717:083a` calls `_DoTravel`
 			// before `_BuildBackground` for the destination site.
-			playLondonTravelAnimation(_mystery->_lastSite, siteNum);
+			bool showedApproach = false;
+			const uint16 approachId = sd ? READ_LE_UINT16(sd + 2) : 0xffff;
+			if (firstVisit && approachId != 0xffff)
+				showedApproach = _vm->doLondonApproach(approachId);
+			if (!showedApproach)
+				playLondonTravelAnimation(_mystery->_lastSite, siteNum);
 		} else {
 			// `_DoTravel @ 168d:02da` calls `_StartTravelMusic`.
 			_vm->startTravelMusic();
@@ -975,7 +981,6 @@ void SiteScreen::enter(uint siteNum, bool resetPartnerMood) {
 	// global SITES.DBD index (per-mystery `sitepic` field).
 	// Floppy (`_DoSiteLoop_Floppy @ 1652:03f4`): first u16 of site_data is
 	// an offset to a drops sub-struct whose byte 0 is the SITES.DBD picID.
-	const byte *sd = _mystery->siteData(siteNum);
 	uint16 sitepic = 0;
 	if (sd) {
 		if (_vm->isFloppy()) {
diff --git a/engines/eem/ui.cpp b/engines/eem/ui.cpp
index 048bc040fea..82e3ee518c3 100644
--- a/engines/eem/ui.cpp
+++ b/engines/eem/ui.cpp
@@ -19,6 +19,7 @@
  *
  */
 
+#include "common/algorithm.h"
 #include "common/debug.h"
 #include "common/config-manager.h"
 #include "common/events.h"
@@ -50,6 +51,33 @@ const GallerySlot kGallerySlots[5] = {
 	{ 191,  90 }  // 4
 };
 
+struct LondonApproachData {
+	uint16 videoId = 0;
+	uint16 videoX = 0;
+	uint16 videoY = 0;
+	Common::Rect textRect;
+	Common::Array<Common::String> pages;
+};
+
+struct LondonApproachButton {
+	int x1;
+	int y1;
+	int x2;
+	int y2;
+	int x;
+	int y;
+	uint16 picId;
+
+	Common::Rect rect() const { return Common::Rect(x1, y1, x2, y2); }
+};
+
+const LondonApproachButton kLondonApproachButtons[4] = {
+	{  10, 139,  35, 157,  11, 139, 0x361 }, // Done
+	{  10, 172,  35, 190,  11, 172, 0x362 }, // Play
+	{ 287, 172, 307, 190, 287, 172, 0x35f }, // Next
+	{ 287, 139, 307, 157, 287, 139, 0x360 }, // Previous
+};
+
 byte mapVisitedMarkerColor(byte color) {
 	switch (color) {
 	case 0xf7:
@@ -87,6 +115,75 @@ void blitBigMapMarker(Graphics::ManagedSurface &dstSurface, const Picture &marke
 	}
 }
 
+bool loadLondonApproachData(uint16 approachId, LondonApproachData &out) {
+	Common::File f;
+	const Common::String name = Common::String::format("A%u.BIN", approachId);
+	if (!f.open(Common::Path(name))) {
+		warning("London approach: cannot open %s", name.c_str());
+		return false;
+	}
+
+	const uint32 size = f.size();
+	if (size < 16) {
+		warning("London approach: %s is too short", name.c_str());
+		return false;
+	}
+
+	Common::Array<byte> data;
+	data.resize(size);
+	if (f.read(data.data(), size) != size) {
+		warning("London approach: short read on %s", name.c_str());
+		return false;
+	}
+
+	out.videoId = READ_LE_UINT16(data.data() + 0);
+	out.videoX = READ_LE_UINT16(data.data() + 2);
+	out.videoY = READ_LE_UINT16(data.data() + 4);
+	const int16 x1 = (int16)READ_LE_UINT16(data.data() + 6);
+	const int16 y1 = (int16)READ_LE_UINT16(data.data() + 8);
+	const int16 x2 = (int16)READ_LE_UINT16(data.data() + 10);
+	const int16 y2 = (int16)READ_LE_UINT16(data.data() + 12);
+	out.textRect = Common::Rect(x1, y1, x2, y2);
+
+	const uint16 pageCount = READ_LE_UINT16(data.data() + 14);
+	out.pages.clear();
+	uint32 pos = 16;
+	for (uint16 i = 0; i < pageCount && pos < size; i++) {
+		const uint32 start = pos;
+		while (pos < size && data[pos] != 0)
+			pos++;
+		out.pages.push_back(Common::String((const char *)data.data() + start,
+										   pos - start));
+		if (pos < size)
+			pos++;
+	}
+	return out.videoId != 0 && !out.pages.empty();
+}
+
+bool decodeLondonApproachFirstFrame(uint16 videoId,
+									Graphics::ManagedSurface &base,
+									byte *palette) {
+	const Common::String name = Common::String::format("VIDEO%02u.A", videoId);
+	ANMDecoder anm;
+	if (!anm.open(Common::Path(name))) {
+		warning("London approach: cannot open %s", name.c_str());
+		return false;
+	}
+
+	anm.getPalette8(palette);
+	const byte *frame = anm.nextFrame();
+	if (!frame) {
+		warning("London approach: %s has no first frame", name.c_str());
+		return false;
+	}
+
+	base.clear();
+	const int w = MIN<int>(anm.width(), kScreenWidth);
+	const int h = MIN<int>(anm.height(), kScreenHeight);
+	base.copyRectToSurface(frame, anm.width(), 0, 0, w, h);
+	return true;
+}
+
 // Setup-screen highlight rects (referenced by both `doSetup` and the
 // helper `EEMEngine::setupDrawScreen`). `_SetupHighlights @ 29be:1320`.
 constexpr Common::Rect kSetupKid1Rect    (Common::Point( 99,  44),  49,  8);
@@ -2979,7 +3076,8 @@ void EEMEngine::doBigMap() {
 		const Common::Rect kBigMapWindow(0, 0, 247, 192);
 		const Common::Rect kSetupBtnRect = isFloppy()
 			? Common::Rect(251, 3, 315, 42)
-			: Common::Rect(252, 4, 315, 42);
+			: (isLondon() ? Common::Rect(252, 1, 315, 42)
+						  : Common::Rect(252, 4, 315, 42));
 
 		bool wantZoom = false;
 		int zoomX = 0;
@@ -3022,8 +3120,13 @@ void EEMEngine::doBigMap() {
 			if (now - mapLastTick >= 100) {
 				mapLastTick = now;
 				drawBigMapOverview(now - mapStartTick);
-				cyclePaletteRange(0xf7, 0xfa);
-				cyclePaletteRange(0xfb, 0xfe);
+				if (isLondon()) {
+					cyclePaletteRange(0xf4, 0xf9);
+					cyclePaletteRange(0xfa, 0xff);
+				} else {
+					cyclePaletteRange(0xf7, 0xfa);
+					cyclePaletteRange(0xfb, 0xfe);
+				}
 			}
 			g_system->updateScreen();
 			g_system->delayMillis(10);
@@ -3068,12 +3171,17 @@ void EEMEngine::doBigMap() {
 		const Common::Rect kArrowYDown(237, 163, 247, 172);
 		const Common::Rect kArrowXLeft(2, 175, 12, 185);
 		const Common::Rect kArrowXRight(224, 175, 234, 185);
-		const Common::Rect kXSlider(15, 175, 221, 185);
-		const Common::Rect kYSlider(237, 14, 247, 160);
+		const Common::Rect kXSlider = isLondon()
+			? Common::Rect(15, 176, 220, 184)
+			: Common::Rect(15, 175, 221, 185);
+		const Common::Rect kYSlider = isLondon()
+			? Common::Rect(238, 16, 246, 158)
+			: Common::Rect(237, 14, 247, 160);
 		const Common::Rect kDetailSetupBtn = isFloppy()
 			? Common::Rect(251, 3, 315, 42)
-			: Common::Rect(252, 4, 315, 42);
-		const int kArrowStep = 16;
+			: (isLondon() ? Common::Rect(251, 3, 315, 42)
+						  : Common::Rect(252, 4, 315, 42));
+		const int kArrowStep = isLondon() ? 8 : 16;
 		const int kSliderRange = mapW - kMapWinW;
 		const int kSliderRangeY = mapH - kMapWinH;
 		const Common::Point detailMouse =
@@ -3085,6 +3193,7 @@ void EEMEngine::doBigMap() {
 		while (!shouldQuit() && !returnToOverview) {
 			Common::Event ev;
 			bool dirty = false;
+			bool frameTick = false;
 			while (g_system->getEventManager()->pollEvent(ev)) {
 				if (ev.type == Common::EVENT_QUIT ||
 					ev.type == Common::EVENT_RETURN_TO_LAUNCHER) {
@@ -3097,7 +3206,7 @@ void EEMEngine::doBigMap() {
 						dirty = true;
 						continue;
 					}
-					const int kStep = 16;
+					const int kStep = isLondon() ? 8 : 16;
 					if (ev.kbd.keycode == Common::KEYCODE_LEFT) {
 						scrollX = MAX<int>(0, scrollX - kStep);
 						dirty = true;
@@ -3167,6 +3276,11 @@ void EEMEngine::doBigMap() {
 							   ev.mouse.y < kMapWinY + kMapWinH) {
 						// Per-site bbox from `_StampButtons` (SmallMap +8/+0xa).
 						const bool fmap = _mystery.isLoaded() && isFloppy();
+						struct DetailMapHit {
+							uint site;
+							Common::Rect rect;
+						};
+						Common::Array<DetailMapHit> hits;
 						for (uint i = 0; i < _mystery.numSites(); i++) {
 							if (!_mystery._onSites[i] &&
 								i != _mystery._siteNumber)
@@ -3197,10 +3311,23 @@ void EEMEngine::doBigMap() {
 							}
 							const int sx = (int)mx - scrollX + kMapWinX;
 							const int sy = (int)my - scrollY + kMapWinY;
-							if (ev.mouse.x >= sx && ev.mouse.x < sx + bw &&
-								ev.mouse.y >= sy && ev.mouse.y < sy + bh) {
+							const Common::Rect r(sx, sy, sx + bw, sy + bh);
+							if (r.intersects(Common::Rect(kMapWinX, kMapWinY,
+									kMapWinX + kMapWinW, kMapWinY + kMapWinH))) {
+								DetailMapHit hit = { i, r };
+								hits.push_back(hit);
+							}
+						}
+						Common::sort(hits.begin(), hits.end(),
+							[](const DetailMapHit &a, const DetailMapHit &b) {
+								if (a.rect.top != b.rect.top)
+									return a.rect.top < b.rect.top;
+								return a.rect.left < b.rect.left;
+							});
+						for (uint i = 0; i < hits.size(); i++) {
+							if (hits[i].rect.contains(ev.mouse.x, ev.mouse.y)) {
 								_mystery._lastSite = _mystery._siteNumber;
-								_mystery._siteNumber = (uint16)i;
+								_mystery._siteNumber = (uint16)hits[i].site;
 								setInteractiveMouseCursor(false);
 								return;
 							}
@@ -3216,10 +3343,15 @@ void EEMEngine::doBigMap() {
 			if (now - detailLastTick >= 100) {
 				detailLastTick = now;
 				dirty = true;
+				frameTick = true;
 			}
 			if (dirty)
 				drawBigMapDetail(scrollX, scrollY, mapPixels, mapW, mapH,
 					now - detailStartTick);
+			if (frameTick && isLondon()) {
+				cyclePaletteRange(0xee, 0xf2);
+				cyclePaletteRange(0xea, 0xed);
+			}
 			g_system->updateScreen();
 			g_system->delayMillis(10);
 		}
@@ -3228,6 +3360,215 @@ void EEMEngine::doBigMap() {
 	}
 }
 
+bool EEMEngine::doLondonApproach(uint16 approachId) {
+	if (!isLondon())
+		return false;
+
+	LondonApproachData data;
+	if (!loadLondonApproachData(approachId, data))
+		return false;
+
+	Graphics::ManagedSurface base(kScreenWidth, kScreenHeight,
+		Graphics::PixelFormat::createFormatCLUT8());
+	byte palette[768] = {};
+	const bool haveVideo =
+		decodeLondonApproachFirstFrame(data.videoId, base, palette);
+	if (!haveVideo)
+		base.clear();
+
+	Picture buttonPics[ARRAYSIZE(kLondonApproachButtons)];
+	bool haveButtons[ARRAYSIZE(kLondonApproachButtons)] = {};
+	for (uint i = 0; i < ARRAYSIZE(kLondonApproachButtons); i++)
+		haveButtons[i] = _picsArchive.getPicture(
+			kLondonApproachButtons[i].picId, buttonPics[i]);
+
+	auto drawButtonFallback = [&](Graphics::ManagedSurface &dst, uint idx) {
+		static const char *const kLabels[4] = { "OK", "PLAY", ">", "<" };
+		const Common::Rect r = kLondonApproachButtons[idx].rect();
+		dst.fillRect(r, 0);
+		_font.drawString(&dst, kLabels[idx], r.left + 1, r.top + 5,
+						 r.width(), 0x0f);
+	};
+
+	auto drawScreen = [&](uint page) {
+		Graphics::ManagedSurface scratch(kScreenWidth, kScreenHeight,
+			Graphics::PixelFormat::createFormatCLUT8());
+		scratch.simpleBlitFrom(base);
+
+		const Common::Rect textRect =
+			data.textRect.findIntersectingRect(Common::Rect(kScreenWidth, kScreenHeight));
+		if (!textRect.isEmpty()) {
+			scratch.fillRect(textRect, 0);
+			if (page < data.pages.size()) {
+				Common::Array<Common::String> wrapped;
+				_font.wordWrapText(data.pages[page], MAX<int>(8, textRect.width()),
+								   wrapped);
+				const int lineH = _font.getFontHeight();
+				const int maxLines = MAX<int>(1, textRect.height() / lineH);
+				for (uint i = 0; i < wrapped.size() && (int)i < maxLines; i++) {
+					_font.drawString(&scratch, wrapped[i], textRect.left,
+									 textRect.top + (int)i * lineH,
+									 textRect.width(), 0x0f);
+				}
+			}
+		}
+
+		for (uint i = 0; i < ARRAYSIZE(kLondonApproachButtons); i++) {
+			const LondonApproachButton &b = kLondonApproachButtons[i];
+			if (haveButtons[i]) {
+				scratch.transBlitFrom(buttonPics[i].surface,
+									  Common::Point(b.x, b.y),
+									  (uint32)(byte)(buttonPics[i].flags >> 8));
+			} else {
+				drawButtonFallback(scratch, i);
+			}
+		}
+
+		g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
+								   0, 0, kScreenWidth, kScreenHeight);
+		g_system->updateScreen();
+	};
+
+	auto playVideo = [&]() {
+		const Common::String name =
+			Common::String::format("VIDEO%02u.A", data.videoId);
+		ANMDecoder anm;
+		if (!anm.open(Common::Path(name))) {
+			warning("London approach: cannot open %s", name.c_str());
+			return;
+		}
+
+		// Frame 0 is already the static background. Replay frames 1..end
+		// into the upper video area, leaving text/buttons untouched.
+		(void)anm.nextFrame();
+		const int copyW = MIN<int>(anm.width(), kScreenWidth - (int)data.videoX);
+		const int copyH = MIN<int>(0x82, MIN<int>(anm.height(),
+			kScreenHeight - (int)data.videoY));
+		if (copyW <= 0 || copyH <= 0)
+			return;
+
+		while (!shouldQuit()) {
+			const byte *frame = anm.nextFrame();
+			if (!frame)
+				break;
+			g_system->copyRectToScreen(
+				frame + data.videoY * anm.width() + data.videoX,
+				anm.width(), data.videoX, data.videoY, copyW, copyH);
+			g_system->updateScreen();
+
+			const uint32 start = g_system->getMillis();
+			bool skip = false;
+			while (g_system->getMillis() - start < 120 && !skip) {
+				Common::Event ev;
+				while (g_system->getEventManager()->pollEvent(ev)) {
+					if (ev.type == Common::EVENT_QUIT ||
+						ev.type == Common::EVENT_RETURN_TO_LAUNCHER) {
+						return;
+					}
+					if (ev.type == Common::EVENT_LBUTTONDOWN ||
+						ev.type == Common::EVENT_KEYDOWN) {
+						skip = true;
+						break;
+					}
+				}
+				g_system->delayMillis(5);
+			}
+			if (skip)
+				break;
+		}
+	};
+
+	fadeCurrentPaletteToBlack();
+	CursorMan.showMouse(true);
+	setSiteHotspotCursorId(6);
+	if (_music && _voiceOn)
+		_music->playMus(0x27, /* loop= */ false);
+
+	uint page = 0;
+	drawScreen(page);
+	if (haveVideo)
+		fadePaletteFromBlack(palette);
+	else
+		setSitePalette(0x3b);
+	bool done = false;
+	while (!shouldQuit() && !done) {
+		Common::Event ev;
+		while (g_system->getEventManager()->pollEvent(ev)) {
+			if (ev.type == Common::EVENT_QUIT ||
+				ev.type == Common::EVENT_RETURN_TO_LAUNCHER) {
+				done = true;
+				break;
+			}
+			if (ev.type == Common::EVENT_MOUSEMOVE) {
+				bool overButton = false;
+				for (uint i = 0; i < ARRAYSIZE(kLondonApproachButtons); i++) {
+					if (kLondonApproachButtons[i].rect().contains(ev.mouse.x,
+																 ev.mouse.y)) {
+						overButton = true;
+						break;
+					}
+				}
+				setInteractiveMouseCursor(overButton);
+			}
+			if (ev.type == Common::EVENT_KEYDOWN) {
+				if (ev.kbd.keycode == Common::KEYCODE_ESCAPE ||
+					ev.kbd.keycode == Common::KEYCODE_RETURN ||
+					ev.kbd.keycode == Common::KEYCODE_KP_ENTER) {
+					done = true;
+					break;
+				}
+				if (ev.kbd.keycode == Common::KEYCODE_SPACE) {
+					playVideo();
+					drawScreen(page);
+				} else if (ev.kbd.keycode == Common::KEYCODE_RIGHT ||
+						   ev.kbd.keycode == Common::KEYCODE_DOWN) {
+					if (page + 1 < data.pages.size()) {
+						page++;
+						drawScreen(page);
+					}
+				} else if (ev.kbd.keycode == Common::KEYCODE_LEFT ||
+						   ev.kbd.keycode == Common::KEYCODE_UP) {
+					if (page > 0) {
+						page--;
+						drawScreen(page);
+					}
+				}
+			}
+			if (ev.type == Common::EVENT_LBUTTONDOWN) {
+				if (kLondonApproachButtons[0].rect().contains(ev.mouse.x,
+															 ev.mouse.y)) {
+					done = true;
+					break;
+				}
+				if (kLondonApproachButtons[1].rect().contains(ev.mouse.x,
+															 ev.mouse.y)) {
+					playVideo();
+					drawScreen(page);
+				} else if (kLondonApproachButtons[2].rect().contains(ev.mouse.x,
+																	ev.mouse.y)) {
+					if (page + 1 < data.pages.size()) {
+						page++;
+						drawScreen(page);
+					}
+				} else if (kLondonApproachButtons[3].rect().contains(ev.mouse.x,
+																	ev.mouse.y)) {
+					if (page > 0) {
+						page--;
+						drawScreen(page);
+					}
+				}
+			}
+		}
+		g_system->updateScreen();
+		g_system->delayMillis(10);
+	}
+
+	setInteractiveMouseCursor(false);
+	setSiteHotspotCursorId(0);
+	stopMusic();
+	return true;
+}
+
 void EEMEngine::drawBigMapOverview(uint32 elapsedMs) {
 	// PIC 0x42 + per-site Done/Crime/Site marker (`_DrawBigMapButtons @
 	// 20fe:0877`) + partner idle at (0xfd, 0x50). Idle ANI: Jake=0x14,


Commit: 6ddfb2227b29c4295c5066c48fcd203902331572
    https://github.com/scummvm/scummvm/commit/6ddfb2227b29c4295c5066c48fcd203902331572
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:21:25+02:00

Commit Message:
EEM: fix big map markers in London

Changed paths:
    engines/eem/ui.cpp


diff --git a/engines/eem/ui.cpp b/engines/eem/ui.cpp
index 82e3ee518c3..63bd909451f 100644
--- a/engines/eem/ui.cpp
+++ b/engines/eem/ui.cpp
@@ -3581,13 +3581,14 @@ void EEMEngine::drawBigMapOverview(uint32 elapsedMs) {
 	if (_picsArchive.getPicture(0x42, frame))
 		scratch.simpleBlitFrom(frame.surface);
 
-	// Marker PICs from `_main @ 1a35:0f59`:
-	//   _DoneMarker = 0x20d, _SiteMarker = 0xc5, _CrimeMarker = 0xc6.
-	// 0x20d is CD-only (floppy PICS.DBX has 524 entries).
+	// Marker PICs from `_main`:
+	//   EEM1 CD @ 1a35:0f59: Done=0x20d, Site=0xc5, Crime=0xc6.
+	//   EEM2 CD @ 1abf:11a6: Done=0x006, Site=0xc5, Crime=0xc6.
+	// In EEM2, 0x20d is a normal scene/character frame, not a map marker.
 	Picture done;
 	Picture normal;
 	Picture crimeM;
-	const bool haveDone   = _picsArchive.getPicture(0x20d, done);
+	const bool haveDone   = _picsArchive.getPicture(isLondon() ? 0x006 : 0x20d, done);
 	const bool haveNormal = _picsArchive.getPicture(0xc5,  normal);
 	const bool haveCrime  = _picsArchive.getPicture(0xc6,  crimeM);
 


Commit: a35a1dc2d51307a386a15158b9145354c21f3ac8
    https://github.com/scummvm/scummvm/commit/a35a1dc2d51307a386a15158b9145354c21f3ac8
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:21:25+02:00

Commit Message:
EEM: implement player creation/loading in London

Changed paths:
    engines/eem/eem.cpp
    engines/eem/eem.h
    engines/eem/ui.cpp


diff --git a/engines/eem/eem.cpp b/engines/eem/eem.cpp
index 36fe8d4fc8b..80013325a49 100644
--- a/engines/eem/eem.cpp
+++ b/engines/eem/eem.cpp
@@ -74,6 +74,53 @@ const byte kSaveBodyVer = 1;
 // option or changing save format. Set false before release.
 const bool kDebugPopulateScrapbook1AtStartup = false;
 
+Common::String makeLondonProfileDisplayName(const Common::String &first,
+											 const Common::String &last) {
+	Common::String cleanFirst = first;
+	Common::String cleanLast = last;
+	cleanFirst.trim();
+	cleanLast.trim();
+	if (cleanLast.empty())
+		return cleanFirst;
+	if (cleanFirst.empty())
+		return cleanLast;
+	return cleanFirst + " " + cleanLast;
+}
+
+Common::String makeLondonProfileKey(const Common::String &first,
+									 const Common::String &last) {
+	// EEM2 `_GenerateFilename @ 1cd3:02fe`: copy the first 7 bytes of
+	// FirstName, pad to 8 from LastName, then replace spaces/dots with '_'.
+	Common::String cleanFirst = first;
+	Common::String cleanLast = last;
+	cleanFirst.trim();
+	cleanLast.trim();
+	Common::String key;
+	for (uint i = 0; i < cleanFirst.size() && key.size() < 7; i++)
+		key += cleanFirst[i];
+	for (uint i = 0; i < cleanLast.size() && key.size() < 8; i++)
+		key += cleanLast[i];
+	for (uint i = 0; i < key.size(); i++) {
+		if (key[i] == ' ' || key[i] == '.')
+			key.setChar('_', i);
+	}
+	key.toUppercase();
+	return key;
+}
+
+Common::String londonProfileKeyFromDisplayName(const Common::String &name) {
+	Common::String clean = name;
+	clean.trim();
+	const size_t split = clean.findFirstOf(' ');
+	if (split == Common::String::npos)
+		return makeLondonProfileKey(clean, Common::String());
+
+	Common::String first = clean.substr(0, split);
+	Common::String last = clean.substr(split + 1);
+	last.trim();
+	return makeLondonProfileKey(first, last);
+}
+
 void fadeCurrentPaletteToBlack(uint delayMs) {
 	byte start[kPalSize];
 	byte stepPal[kPalSize];
@@ -342,26 +389,22 @@ Common::Error EEMEngine::run() {
 	if (resumed)
 		goto screenLoop;
 
-	// EEM2 ("Eagle Eye Mysteries in London"): opening sequence + character
-	// creation, then start the training case (mystery 0 ??? what a new
-	// detective plays first) and reuse the engine's gameplay screen loop.
-	// EEM2's mystery data format matches EEM1-CD, so the shared Mystery
-	// parser + InitClues (case intro) / BigMap / Site handlers apply.
+	// EEM2 ("Eagle Eye Mysteries in London"): opening sequence, then screen
+	// 8 profile selection. A freshly-created detective starts the training
+	// case (M0); an existing profile resumes its saved menu/map state.
 	if (isLondon()) {
 		runLondonStartup();
 		if (!shouldQuit()) {
 			CursorMan.showMouse(true);
-			if (_mystery.load(0, &_rng)) {
-				resetSiteArrivalState();
-				if (_audio)
-					_audio->initMysterySounds(0);
-				debugC(1, kDebugMystery,
-					   "London: training mystery 0 loaded ??? %u sites, %u suspects",
-					   _mystery.numSites(), _mystery.numSuspects());
-				_nextScreen = kScreenInitClues;
+			applyStartupTestOverrides();
+			applySkipRepeatedCasesOption();
+			if (_mystery.isLoaded()) {
+				_nextScreen = kScreenMap;
+			} else if (_profileCreatedThisSession) {
+				_nextScreen = startLondonTrainingMystery()
+					? kScreenInitClues : kScreenInvalid;
 			} else {
-				warning("London: training mystery 0 (M0.BIN) failed to load");
-				_nextScreen = kScreenInvalid;
+				_nextScreen = kScreenAction;
 			}
 		}
 		goto screenLoop;
@@ -605,11 +648,17 @@ screenLoop:
 				applyStartupTestOverrides();
 			if (!shouldQuit())
 				applySkipRepeatedCasesOption();
-			if (!shouldQuit())
+			if (!shouldQuit() && !isLondon())
 				doChoosePartner();
-			if (!shouldQuit())
-				_nextScreen = _mystery.isLoaded() ? kScreenMap
-												  : kScreenAction;
+			if (!shouldQuit()) {
+				if (_mystery.isLoaded())
+					_nextScreen = kScreenMap;
+				else if (isLondon() && _profileCreatedThisSession)
+					_nextScreen = startLondonTrainingMystery()
+						? kScreenInitClues : kScreenInvalid;
+				else
+					_nextScreen = kScreenAction;
+			}
 			break;
 
 		case kScreenAccuse:
@@ -1111,6 +1160,21 @@ void EEMEngine::showLondonLogo(uint picId, uint palId, uint holdMs) {
 	fadeCurrentPaletteToBlack();
 }
 
+bool EEMEngine::startLondonTrainingMystery() {
+	if (_mystery.load(0, &_rng)) {
+		resetSiteArrivalState();
+		if (_audio)
+			_audio->initMysterySounds(0);
+		debugC(1, kDebugMystery,
+			   "London: training mystery 0 loaded ??? %u sites, %u suspects",
+			   _mystery.numSites(), _mystery.numSuspects());
+		return true;
+	}
+
+	warning("London: training mystery 0 (M0.BIN) failed to load");
+	return false;
+}
+
 void EEMEngine::runLondonStartup() {
 	// Full opening sequence ??? EEM2 `_DoOpeningAnims` @ 2721:08e6:
 	//   _ShowEAKids   @ 2721:05e3 ??? PIC 0x54,  palette 0x3c
@@ -1164,14 +1228,14 @@ void EEMEngine::runLondonStartup() {
 		_music->stop();
 	_skipIntro = false;
 
-	// Character creation (name + Jake/Jenny). The caller then loads the
-	// training case (mystery 0) and enters the shared gameplay screen loop
-	// (case intro -> map -> site); the case-selection menu is reachable
-	// later via the action screen.
-	if (!shouldQuit())
-		showLondonCharSelect();
+	// Screen 8 profile selection. If no profile exists or the player chooses
+	// the bottom "new player" sentinel, this opens London `_NewPlayer`.
+	if (!shouldQuit()) {
+		CursorMan.showMouse(true);
+		doProfilePicker();
+	}
 
-	debugC(1, kDebugGeneral, "EEM2 (London): intro + character creation done");
+	debugC(1, kDebugGeneral, "EEM2 (London): intro + profile selection done");
 }
 
 void EEMEngine::showLondonCharSelect() {
@@ -1202,12 +1266,15 @@ void EEMEngine::showLondonCharSelect() {
 	const bool havePal = getSitePalette(0, pal);
 
 	CursorMan.showMouse(false);
+	_profileCreatedThisSession = false;
 
 	enum { kFieldFirst = 0, kFieldLast = 1, kFieldPartner = 2 };
 	int field = kFieldFirst;
 	Common::String first, last;
 	uint8 partner = kPartnerJake;
 	bool blink = true, fadedIn = false, done = false, needRedraw = true;
+	bool partnerReady = false;
+	bool partnerCanConfirm = true;
 	uint32 blinkMs = g_system->getMillis();
 
 	while (!done && !shouldQuit()) {
@@ -1243,6 +1310,8 @@ void EEMEngine::showLondonCharSelect() {
 			}
 			g_system->updateScreen();
 			needRedraw = false;
+			if (field == kFieldPartner)
+				partnerReady = true;
 		}
 
 		Common::Event ev;
@@ -1250,6 +1319,20 @@ void EEMEngine::showLondonCharSelect() {
 			if (ev.type == Common::EVENT_QUIT ||
 				ev.type == Common::EVENT_RETURN_TO_LAUNCHER)
 				return;
+			if (field == kFieldPartner && ev.type == Common::EVENT_LBUTTONDOWN) {
+				if (!partnerReady)
+					continue;
+				partner = (ev.mouse.x < 160) ? kPartnerJake : kPartnerJenny;
+				done = true;
+				needRedraw = true;
+				break;
+			}
+			if (field == kFieldPartner && ev.type == Common::EVENT_KEYUP &&
+				(ev.kbd.keycode == Common::KEYCODE_RETURN ||
+				 ev.kbd.keycode == Common::KEYCODE_KP_ENTER)) {
+				partnerCanConfirm = true;
+				continue;
+			}
 			if (ev.type != Common::EVENT_KEYDOWN)
 				continue;
 			const Common::KeyCode k = ev.kbd.keycode;
@@ -1258,25 +1341,39 @@ void EEMEngine::showLondonCharSelect() {
 			if (field == kFieldFirst || field == kFieldLast) {
 				Common::String &buf = (field == kFieldFirst) ? first : last;
 				const uint cap = (field == kFieldFirst) ? kMaxFirst : kMaxLast;
-				if ((enter || k == Common::KEYCODE_TAB) &&
-					(field == kFieldLast || !first.empty()))
-					field++;  // advance to last name, then to partner pick
-				else if (k == Common::KEYCODE_BACKSPACE && !buf.empty())
+				if (enter || k == Common::KEYCODE_TAB) {
+					Common::String clean = buf;
+					clean.trim();
+					if (!clean.empty()) {
+						field++;  // advance to last name, then to partner pick
+						if (field == kFieldPartner) {
+							partnerReady = false;
+							partnerCanConfirm = false;
+						}
+					}
+				} else if (k == Common::KEYCODE_BACKSPACE && !buf.empty()) {
 					buf.deleteLastChar();
-				else if (ev.kbd.ascii >= ' ' && ev.kbd.ascii < 127 &&
-						 buf.size() < cap)
+				} else if (ev.kbd.ascii >= ' ' && ev.kbd.ascii < 127 &&
+						   buf.size() < cap) {
 					buf += (char)ev.kbd.ascii;
+				}
 			} else {  // partner pick
-				if (k == Common::KEYCODE_LEFT)
+				if (k == Common::KEYCODE_LEFT) {
 					partner = kPartnerJake;
-				else if (k == Common::KEYCODE_RIGHT)
+					partnerCanConfirm = true;
+				} else if (k == Common::KEYCODE_RIGHT) {
 					partner = kPartnerJenny;
-				else if (k == Common::KEYCODE_BACKSPACE)
+					partnerCanConfirm = true;
+				} else if (k == Common::KEYCODE_BACKSPACE) {
 					field = kFieldLast;  // back to editing
-				else if (enter)
+					partnerCanConfirm = true;
+				} else if (enter && partnerReady && partnerCanConfirm) {
 					done = true;
+				}
 			}
 			needRedraw = true;
+			if (field == kFieldPartner && !partnerReady)
+				break;
 		}
 
 		const uint32 now = g_system->getMillis();
@@ -1290,18 +1387,54 @@ void EEMEngine::showLondonCharSelect() {
 	if (shouldQuit())
 		return;
 
-	// Commit the profile so the reused case-selection menu has valid state
-	// (player name in clue text, partner greeter ANI 0x15/0x16, Junior
-	// chain stage -> BOOK1.NME, nothing solved yet).
-	if (first.empty())
-		first = "Detective";
-	_playerName = last.empty() ? first : (first + " " + last);
+	Common::Event staleEvent;
+	while (g_system->getEventManager()->pollEvent(staleEvent)) {
+		if (staleEvent.type == Common::EVENT_QUIT ||
+			staleEvent.type == Common::EVENT_RETURN_TO_LAUNCHER)
+			return;
+	}
+
+	const Common::String displayName =
+		makeLondonProfileDisplayName(first, last);
+	const Common::String profileKey = makeLondonProfileKey(first, last);
+
+	for (const SaveStateDescriptor &s : listProfiles()) {
+		if (londonProfileKeyFromDisplayName(s.getDescription()) != profileKey)
+			continue;
+
+		const Common::Error err = loadGameState(s.getSaveSlot());
+		if (err.getCode() == Common::kNoError) {
+			_profileCreatedThisSession = false;
+			debugC(1, kDebugGeneral,
+				   "London: existing player key=%s loaded from slot %d (%s)",
+				   profileKey.c_str(), s.getSaveSlot(),
+				   s.getDescription().c_str());
+			return;
+		}
+		warning("London: failed to load existing profile '%s' at slot %d",
+				s.getDescription().c_str(), s.getSaveSlot());
+	}
+
+	// New profile. EEM2 `_NewPlayer @ 1cd3:0f27` initializes the player
+	// record only when `_LoadPlayerRecord` failed, then immediately saves it.
+	_playerName = displayName.empty() ? "Detective" : displayName;
 	_partner = partner;
 	_chainStage = 1;
+	_voiceOn = true;
+	if (_audio)
+		_audio->setVoiceEnabled(_voiceOn);
 	for (uint i = 0; i < sizeof(_mysteriesSolved); i++)
 		_mysteriesSolved[i] = 0;
-	debugC(1, kDebugGeneral, "London: player='%s' partner=%s",
-		   _playerName.c_str(), partner == kPartnerJake ? "Jake" : "Jenny");
+	_mystery.clear();
+	resetSiteArrivalState();
+	_profileCreatedThisSession = true;
+	const Common::Error err = saveProfile(_playerName);
+	if (err.getCode() != Common::kNoError)
+		warning("London: failed to save new profile '%s': %s",
+				_playerName.c_str(), err.getDesc().c_str());
+	debugC(1, kDebugGeneral, "London: new player='%s' key=%s partner=%s",
+		   _playerName.c_str(), profileKey.c_str(),
+		   partner == kPartnerJake ? "Jake" : "Jenny");
 }
 
 void EEMEngine::showFloppyStormLogo() {
@@ -1565,11 +1698,15 @@ Common::Error EEMEngine::saveProfile(const Common::String &name) {
 		return Common::kCreatingFileFailed;
 
 	const SaveStateList saves = listProfiles();
+	const Common::String londonKey =
+		isLondon() ? londonProfileKeyFromDisplayName(name) : Common::String();
 
 	// Overwrite on matching description.
 	int slot = -1;
 	for (auto &s : saves) {
-		if (s.getDescription() == name) {
+		if (s.getDescription() == name ||
+			(isLondon() &&
+			 londonProfileKeyFromDisplayName(s.getDescription()) == londonKey)) {
 			slot = s.getSaveSlot();
 			break;
 		}
@@ -1606,8 +1743,12 @@ bool EEMEngine::loadProfile(const Common::String &name) {
 		return false;
 
 	const SaveStateList saves = listProfiles();
+	const Common::String londonKey =
+		isLondon() ? londonProfileKeyFromDisplayName(name) : Common::String();
 	for (auto &s : saves) {
-		if (s.getDescription() == name) {
+		if (s.getDescription() == name ||
+			(isLondon() &&
+			 londonProfileKeyFromDisplayName(s.getDescription()) == londonKey)) {
 			const Common::Error err = loadGameState(s.getSaveSlot());
 			if (err.getCode() == Common::kNoError) {
 				debugC(1, kDebugGeneral, "loadProfile(%s) <- slot %d",
diff --git a/engines/eem/eem.h b/engines/eem/eem.h
index 710573ff2b9..786c41a089c 100644
--- a/engines/eem/eem.h
+++ b/engines/eem/eem.h
@@ -425,8 +425,10 @@ private:
 
 	// --- EEM2 ("Eagle Eye Mysteries in London") ??? reimplementation in progress ---
 	/// Opening sequence + character creation ??? EEM2's `_DoOpeningAnims`
-	/// @ 2721:08e6 then `_NewPlayer` @ 1cd3:0f27.
+	/// @ 2721:08e6 then screen 8 profile selection.
 	void runLondonStartup();
+	/// Start London mystery 0 for a freshly-created detective.
+	bool startLondonTrainingMystery();
 	/// Blit a full-screen still PIC and fade it in / hold / out using the
 	/// given SITEPALS. palette index.
 	void showLondonLogo(uint picId, uint palId, uint holdMs);
@@ -554,6 +556,10 @@ private:
 	/// partner speech, intro VO).
 	bool _voiceOn = true;
 
+	/// Set by the profile/new-player screens. London uses it to decide whether
+	/// to start the training mystery or resume the loaded profile's menu/state.
+	bool _profileCreatedThisSession = false;
+
 	Common::RandomSource _rng;
 
 	DBDArchive _picsArchive;     ///< PICS.DBD/.DBX
diff --git a/engines/eem/ui.cpp b/engines/eem/ui.cpp
index 63bd909451f..d2be151d480 100644
--- a/engines/eem/ui.cpp
+++ b/engines/eem/ui.cpp
@@ -836,18 +836,25 @@ void EEMEngine::doProfilePicker() {
 	// `screen8_handler @ 1c33:1012` ??? walks *.PLR (max 25), reads 12-byte
 	// player-name field, hands list to `_DoChoose`. No profiles or
 	// 0xfffe/0xffff sentinel enters `_NewPlayer`.
+	_profileCreatedThisSession = false;
 
 	// `screen8_handler` does `_FadeOut(); _GetPalette(0); _GetBackground(0x104)`.
 	setSitePalette(0);
 
 	const SaveStateList saves = listProfiles();
 	if (saves.empty()) {
-		doNewPlayer();
+		if (isLondon())
+			showLondonCharSelect();
+		else
+			doNewPlayer();
 		return;
 	}
 
 	if (!_font.isLoaded()) {
-		doNewPlayer();
+		if (isLondon())
+			showLondonCharSelect();
+		else
+			doNewPlayer();
 		return;
 	}
 
@@ -1011,22 +1018,31 @@ void EEMEngine::doProfilePicker() {
 	}
 
 	if (createNew) {
-		doNewPlayer();
+		if (isLondon())
+			showLondonCharSelect();
+		else
+			doNewPlayer();
 		return;
 	}
 
 	// `_LoadPlayerRecord @ 1c33:1281`.
 	const ProfilePickerEntry &e = entries[sel];
-	if (!loadProfile(e.label)) {
+	if (loadProfile(e.label)) {
+		_profileCreatedThisSession = false;
+	} else {
 		warning("doProfilePicker: failed to load profile '%s' at slot %d",
 				e.label.c_str(), e.slot);
-		doNewPlayer();
+		if (isLondon())
+			showLondonCharSelect();
+		else
+			doNewPlayer();
 	}
 }
 
 void EEMEngine::doNewPlayer() {
 	// `_NewPlayer @ 1c33:0dda` ??? BG 0x104 + peek pic 0x107, prompt for
 	// up to 12 chars.
+	_profileCreatedThisSession = false;
 	if (!_font.isLoaded()) {
 		_playerName = "Detective";
 		return;
@@ -1070,7 +1086,9 @@ void EEMEngine::doNewPlayer() {
 					name = "Detective";
 				// `_NewPlayer @ 1c33:0fa0+`: `_LoadPlayerRecord` ??? if
 				// missing, zero state and `_SavePlayerRecord`.
-				if (!loadProfile(name)) {
+				if (loadProfile(name)) {
+					_profileCreatedThisSession = false;
+				} else {
 					_playerName = name;
 					memset(_mysteriesSolved, 0, sizeof(_mysteriesSolved));
 					_mystery.clear();
@@ -1078,6 +1096,7 @@ void EEMEngine::doNewPlayer() {
 					// `_NewPlayer @ 1c33:0fa3`: DAT_2d5d_3f99 = 1 (Junior).
 					_chainStage = 1;
 					saveProfile(name);
+					_profileCreatedThisSession = true;
 				}
 				done = true;
 				break;


Commit: a252c9afa9f8f0bf3406151069e11602abdebb19
    https://github.com/scummvm/scummvm/commit/a252c9afa9f8f0bf3406151069e11602abdebb19
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:21:26+02:00

Commit Message:
EEM: proper selection of partner in London

Changed paths:
    engines/eem/clues.cpp
    engines/eem/eem.cpp
    engines/eem/eem.h


diff --git a/engines/eem/clues.cpp b/engines/eem/clues.cpp
index e75fdbda8ff..a6d7f734cd0 100644
--- a/engines/eem/clues.cpp
+++ b/engines/eem/clues.cpp
@@ -45,9 +45,10 @@ const uint kPicChooseBackground = 0x8c; // _GetBackground(0x8c)
 const uint kAniJake  = 8;
 const uint kAniJenny = 9;
 
-// _DoHappiness @ 172b:27b5 ??? cursor X picks one of 4 rects @ 29be:030f.
+// EEM1 `_DoHappiness` ??? cursor X picks one of 4 rects @ 29be:030f.
 // Past rect 3 = level 4. Constexpr (Point, w, h) form to avoid a global
-// constructor (-Wglobal-constructors).
+// constructor (-Wglobal-constructors). EEM2 uses a different 10-band table
+// at 2bca:035c, handled by `happinessLevel`.
 constexpr Common::Rect kHappyZones[4] = {
 	Common::Rect(Common::Point(  0, 0),  70, 200), // far left ??? Jenny very happy, Jake neutral
 	Common::Rect(Common::Point( 70, 0),  56, 200), // Jenny's column
@@ -61,6 +62,13 @@ const int kJakeY  = 0x62; // 98
 const int kJennyX = 0x42; // 66
 const int kJennyY = 0x60; // 96
 
+// EEM2 `_DoChoosePartner @ 1abf:0728`: Jake is on the left and Jennifer
+// on the right, unlike the first game's partner screen.
+const int kLondonJakeX  = 0x05;
+const int kLondonJakeY  = 0x3a;
+const int kLondonJennyX = 0xac;
+const int kLondonJennyY = 0x3a;
+
 uint markClueBlockNotebookEntries(Mystery &mystery, const byte *clueBlock,
 								  bool isLondon) {
 	if (!clueBlock)
@@ -107,7 +115,49 @@ const uint8 kJennySeqs[5][9] = {
 	{ 0,0,0,0,0,1,0,0,0 },
 };
 
-uint happinessLevel(int x) {
+// EEM2 `FUN_17ee_26f6`: 10 rects @ 2bca:035c and 10 sequence rows for
+// Jennifer @ 2bca:03ac / Jake @ 2bca:0474. The portrait cells are 20-frame
+// ramps, so using the EEM1 5-level tables makes both characters smile in the
+// wrong places.
+const int kLondonHappyRightEdges[10] = {
+	35, 70, 98, 126, 156, 182, 208, 235, 277, 320
+};
+
+const uint8 kLondonJennySeqs[10][9] = {
+	{  0,  0,  0,  0,  0,  0,  0,  1,  0 },
+	{  2,  2,  2,  2,  2,  2,  2,  3,  2 },
+	{  4,  4,  4,  4,  4,  4,  4,  5,  4 },
+	{  6,  6,  6,  6,  6,  6,  7,  6,  6 },
+	{  8,  8,  8,  8,  8,  8,  8,  8,  9 },
+	{ 10, 10, 10, 10, 10, 10, 10, 11, 10 },
+	{ 12, 12, 12, 12, 12, 12, 12, 13, 12 },
+	{ 14, 14, 14, 14, 14, 14, 14, 15, 14 },
+	{ 16, 16, 16, 16, 16, 16, 17, 16, 16 },
+	{ 18, 18, 18, 18, 18, 18, 18, 18, 19 },
+};
+
+const uint8 kLondonJakeSeqs[10][9] = {
+	{ 18, 19, 18, 18, 18, 18, 18, 18, 18 },
+	{ 16, 16, 16, 17, 16, 16, 16, 16, 16 },
+	{ 14, 14, 15, 14, 14, 14, 14, 14, 14 },
+	{ 12, 12, 12, 12, 12, 12, 13, 12, 12 },
+	{ 10, 10, 10, 10, 10, 11, 10, 10, 10 },
+	{  8,  9,  8,  8,  8,  8,  8,  8,  8 },
+	{  6,  6,  6,  7,  6,  6,  6,  6,  6 },
+	{  4,  4,  5,  4,  4,  4,  4,  4,  4 },
+	{  2,  2,  2,  2,  2,  2,  3,  2,  2 },
+	{  0,  0,  0,  0,  0,  1,  0,  0,  0 },
+};
+
+uint happinessLevel(int x, bool london) {
+	if (london) {
+		for (uint i = 0; i < ARRAYSIZE(kLondonHappyRightEdges); i++) {
+			if (x <= kLondonHappyRightEdges[i])
+				return i;
+		}
+		return ARRAYSIZE(kLondonHappyRightEdges) - 1;
+	}
+
 	for (uint i = 0; i < ARRAYSIZE(kHappyZones); i++) {
 		if (kHappyZones[i].contains(x, 100))
 			return i;
@@ -145,10 +195,13 @@ void blitRawToScreen(const Picture &p, int x, int y) {
 							   x, y, w, h);
 }
 
-// _DoChoosePartner @ 1a35:0756. The original places Jake + Jenny animations
-// on a backdrop and polls four click rectangles (two per character); we
-// approximate with a single split at x=160 (left=Jenny, right=Jake).
+// _DoChoosePartner @ 1a35:0756 / EEM2 @ 1abf:0728. The original places
+// Jake + Jenny animations on a backdrop and polls two broad character rects;
+// we approximate those with a single split at x=160. EEM1 has Jenny left /
+// Jake right; EEM2 has Jake left / Jennifer right.
 void EEMEngine::doChoosePartner() {
+	_partner = kPartnerJake;
+
 	Picture background;
 	if (!_picsArchive.getPicture(kPicChooseBackground, background)) {
 		warning("ChoosePartner background (%u) load failed", kPicChooseBackground);
@@ -166,24 +219,31 @@ void EEMEngine::doChoosePartner() {
 		return;
 	}
 
-	setAnmPalette(Common::Path("TITLE.ANM"));
+	setSitePalette(0);
+	CursorMan.showMouse(true);
 
 	// _DoChoosePartner opens with _SetMousePos(0xa0, 0x96).
+	const int jakeX = isLondon() ? kLondonJakeX : kJakeX;
+	const int jakeY = isLondon() ? kLondonJakeY : kJakeY;
+	const int jennyX = isLondon() ? kLondonJennyX : kJennyX;
+	const int jennyY = isLondon() ? kLondonJennyY : kJennyY;
+	const uint8 (*jakeSeqs)[9] = isLondon() ? kLondonJakeSeqs : kJakeSeqs;
+	const uint8 (*jennySeqs)[9] = isLondon() ? kLondonJennySeqs : kJennySeqs;
 	int curMouseX = 0xa0;
-	uint level = happinessLevel(curMouseX);
+	uint level = happinessLevel(curMouseX, isLondon());
 	uint seqIdx = 0;
 
 	blitAt(background, 0, 0);
-	blitAt(jennyAnim[kJennySeqs[level][seqIdx % 9] % jennyAnim.size()],
-		   kJennyX, kJennyY);
-	blitAt(jakeAnim [kJakeSeqs [level][seqIdx % 9] % jakeAnim.size()],
-		   kJakeX, kJakeY);
+	blitAt(jennyAnim[jennySeqs[level][seqIdx % 9] % jennyAnim.size()],
+		   jennyX, jennyY);
+	blitAt(jakeAnim [jakeSeqs [level][seqIdx % 9] % jakeAnim.size()],
+		   jakeX, jakeY);
 	g_system->updateScreen();
 
 	debugC(1, kDebugGeneral, "ChoosePartner: %u Jake frames at (%d,%d), "
 		   "%u Jenny frames at (%d,%d)",
-		   (uint)jakeAnim.size(), kJakeX, kJakeY,
-		   (uint)jennyAnim.size(), kJennyX, kJennyY);
+		   (uint)jakeAnim.size(), jakeX, jakeY,
+		   (uint)jennyAnim.size(), jennyX, jennyY);
 
 	uint32 lastTick = g_system->getMillis();
 	while (!shouldQuit()) {
@@ -195,10 +255,10 @@ void EEMEngine::doChoosePartner() {
 			lastTick = g_system->getMillis();
 			seqIdx = (seqIdx + 1) % 9;
 			blitAt(background, 0, 0);
-			blitAt(jennyAnim[kJennySeqs[level][seqIdx % 9] % jennyAnim.size()],
-				   kJennyX, kJennyY);
-			blitAt(jakeAnim [kJakeSeqs [level][seqIdx % 9] % jakeAnim.size()],
-				   kJakeX, kJakeY);
+			blitAt(jennyAnim[jennySeqs[level][seqIdx % 9] % jennyAnim.size()],
+				   jennyX, jennyY);
+			blitAt(jakeAnim [jakeSeqs [level][seqIdx % 9] % jakeAnim.size()],
+				   jakeX, jakeY);
 			g_system->updateScreen();
 		}
 
@@ -212,20 +272,23 @@ void EEMEngine::doChoosePartner() {
 			}
 			if (ev.type == Common::EVENT_MOUSEMOVE) {
 				curMouseX = ev.mouse.x;
-				const uint newLevel = happinessLevel(curMouseX);
+				const uint newLevel = happinessLevel(curMouseX, isLondon());
 				if (newLevel != level) {
 					level = newLevel;
 					seqIdx = 0; // restart cycle so the gesture pops
 					blitAt(background, 0, 0);
-					blitAt(jennyAnim[kJennySeqs[level][seqIdx % 9] % jennyAnim.size()],
-						   kJennyX, kJennyY);
-					blitAt(jakeAnim [kJakeSeqs [level][seqIdx % 9] % jakeAnim.size()],
-						   kJakeX, kJakeY);
+					blitAt(jennyAnim[jennySeqs[level][seqIdx % 9] % jennyAnim.size()],
+						   jennyX, jennyY);
+					blitAt(jakeAnim [jakeSeqs [level][seqIdx % 9] % jakeAnim.size()],
+						   jakeX, jakeY);
 					g_system->updateScreen();
 				}
 			}
 			if (ev.type == Common::EVENT_LBUTTONDOWN) {
-				_partner = (ev.mouse.x >= 160) ? kPartnerJake : kPartnerJenny;
+				const bool leftHalf = ev.mouse.x < 160;
+				_partner = isLondon()
+					? (leftHalf ? kPartnerJake : kPartnerJenny)
+					: (leftHalf ? kPartnerJenny : kPartnerJake);
 				debugC(1, kDebugGeneral, "Partner picked: %s",
 					   _partner == kPartnerJake ? "Jake" : "Jennifer");
 				done = true;
@@ -233,10 +296,12 @@ void EEMEngine::doChoosePartner() {
 			}
 			if (ev.type == Common::EVENT_KEYDOWN) {
 				if (ev.kbd.keycode == Common::KEYCODE_LEFT) {
-					_partner = kPartnerJenny; done = true; break;
+					_partner = isLondon() ? kPartnerJake : kPartnerJenny;
+					done = true; break;
 				}
 				if (ev.kbd.keycode == Common::KEYCODE_RIGHT) {
-					_partner = kPartnerJake; done = true; break;
+					_partner = isLondon() ? kPartnerJenny : kPartnerJake;
+					done = true; break;
 				}
 				if (ev.kbd.keycode == Common::KEYCODE_RETURN ||
 					ev.kbd.keycode == Common::KEYCODE_ESCAPE) {
diff --git a/engines/eem/eem.cpp b/engines/eem/eem.cpp
index 80013325a49..705195737c8 100644
--- a/engines/eem/eem.cpp
+++ b/engines/eem/eem.cpp
@@ -390,8 +390,9 @@ Common::Error EEMEngine::run() {
 		goto screenLoop;
 
 	// EEM2 ("Eagle Eye Mysteries in London"): opening sequence, then screen
-	// 8 profile selection. A freshly-created detective starts the training
-	// case (M0); an existing profile resumes its saved menu/map state.
+	// 8 profile selection, screen 9 partner selection. A freshly-created
+	// detective starts the training case (M0) after choosing Jake/Jennifer;
+	// an existing profile resumes its saved menu/map state.
 	if (isLondon()) {
 		runLondonStartup();
 		if (!shouldQuit()) {
@@ -400,11 +401,15 @@ Common::Error EEMEngine::run() {
 			applySkipRepeatedCasesOption();
 			if (_mystery.isLoaded()) {
 				_nextScreen = kScreenMap;
-			} else if (_profileCreatedThisSession) {
-				_nextScreen = startLondonTrainingMystery()
-					? kScreenInitClues : kScreenInvalid;
 			} else {
-				_nextScreen = kScreenAction;
+				doChoosePartner();
+				(void)saveProfile(_playerName);
+				if (_profileCreatedThisSession) {
+					_nextScreen = startLondonTrainingMystery()
+						? kScreenInitClues : kScreenInvalid;
+				} else {
+					_nextScreen = kScreenAction;
+				}
 			}
 		}
 		goto screenLoop;
@@ -648,8 +653,10 @@ screenLoop:
 				applyStartupTestOverrides();
 			if (!shouldQuit())
 				applySkipRepeatedCasesOption();
-			if (!shouldQuit() && !isLondon())
+			if (!shouldQuit() && (!isLondon() || !_mystery.isLoaded()))
 				doChoosePartner();
+			if (!shouldQuit() && isLondon() && !_mystery.isLoaded())
+				(void)saveProfile(_playerName);
 			if (!shouldQuit()) {
 				if (_mystery.isLoaded())
 					_nextScreen = kScreenMap;
@@ -1240,41 +1247,59 @@ void EEMEngine::runLondonStartup() {
 
 void EEMEngine::showLondonCharSelect() {
 	// `_NewPlayer` @ 1cd3:0f27 ??? character creation over background PIC 0xc
-	// (palette 0). Two text fields then a Jake/Jenny pick (left/right arrow
-	// 0x4b/0x4d -> DAT_4c4c, Enter confirms). The field/box rects are
-	// constants in EEM2's data segment (read at 2bca:0e3a); they map to the
-	// `pr` player record's FirstName[12] / LastName[20]:
+	// (palette 0). Two text fields then a male/female player-gender pick
+	// (left/right arrow 0x4b/0x4d -> DAT_4c4c, Enter confirms). The
+	// field/box rects are constants in EEM2's data segment (read at
+	// 2bca:0e3a); they map to the `pr` player record's FirstName[12] /
+	// LastName[20]:
 	//   first name : (54,75)-(151,85)    last name : (167,75)-(266,85)
-	//   Jake box   : (110,116)-(120,122) Jenny box : (190,116)-(200,122)
+	//   male       : (110,116)-(120,122) female       : (190,116)-(200,122)
 	debugC(1, kDebugGeneral, "EEM2 (London): character creation");
 
 	const Common::Rect kFirstRect(54, 75, 151, 85);
 	const Common::Rect kLastRect(167, 75, 266, 85);
-	const Common::Rect kJakeBox(110, 116, 120, 122);
-	const Common::Rect kJennyBox(190, 116, 200, 122);
+	const Common::Rect kMaleBox(110, 116, 120, 122);
+	const Common::Rect kFemaleBox(190, 116, 200, 122);
 	const uint kMaxFirst = 12, kMaxLast = 20;
 	const uint8 kInkColor = 0x0F;     // typed-name ink
-	const uint8 kHiBox    = 0x0F;     // selected-partner box highlight
 
 	Picture bg;
 	const bool haveBg = _picsArchive.getPicture(0xc, bg) && !bg.surface.empty();
+	if (!haveBg)
+		warning("London: passport background PIC 0xc failed to load");
+	const uint8 boxBlankColor = haveBg
+		? (uint8)bg.surface.getPixel(kFirstRect.left, kFirstRect.top)
+		: 0x38;
+	const uint8 boxMarkColor = haveBg
+		? (uint8)bg.surface.getPixel(109, 115)
+		: 0x22;
 
 	byte pal[kPalSize];
 	byte black[kPalSize] = {};
 	g_system->getPaletteManager()->setPalette(black, 0, 256);
 	g_system->updateScreen();
 	const bool havePal = getSitePalette(0, pal);
+	if (!havePal)
+		warning("London: passport palette 0 failed to load");
 
-	CursorMan.showMouse(false);
+	Common::Event staleEvent;
+	while (g_system->getEventManager()->pollEvent(staleEvent)) {
+		if (staleEvent.type == Common::EVENT_QUIT ||
+			staleEvent.type == Common::EVENT_RETURN_TO_LAUNCHER)
+			return;
+	}
+
+	CursorMan.showMouse(true);
+	g_system->setFeatureState(OSystem::kFeatureVirtualKeyboard, true);
 	_profileCreatedThisSession = false;
 
-	enum { kFieldFirst = 0, kFieldLast = 1, kFieldPartner = 2 };
+	enum { kFieldFirst = 0, kFieldLast = 1, kFieldGender = 2 };
 	int field = kFieldFirst;
 	Common::String first, last;
-	uint8 partner = kPartnerJake;
+	bool female = false;
 	bool blink = true, fadedIn = false, done = false, needRedraw = true;
-	bool partnerReady = false;
-	bool partnerCanConfirm = true;
+	bool genderReady = false;
+	bool genderCanConfirm = true;
 	uint32 blinkMs = g_system->getMillis();
 
 	while (!done && !shouldQuit()) {
@@ -1298,9 +1323,15 @@ void EEMEngine::showLondonCharSelect() {
 									 kLastRect.top + 1, kLastRect.width(),
 									 kInkColor);
 			}
-			// Highlight the selected partner's indicator box.
-			scratch.fillRect(partner == kPartnerJake ? kJakeBox : kJennyBox,
-							 kHiBox);
+			if (field == kFieldGender) {
+				// DOS restores both boxes, then fills the active one with the
+				// darker passport border color sampled at (109,115).
+				scratch.fillRect(kMaleBox, boxBlankColor);
+				scratch.fillRect(kFemaleBox, boxBlankColor);
+				scratch.fillRect(female ? kFemaleBox : kMaleBox,
+								 boxMarkColor);
+			}
+			CursorMan.showMouse(true);
 			g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
 									   0, 0, kScreenWidth, kScreenHeight);
 			if (!fadedIn) {
@@ -1310,27 +1341,30 @@ void EEMEngine::showLondonCharSelect() {
 			}
 			g_system->updateScreen();
 			needRedraw = false;
-			if (field == kFieldPartner)
-				partnerReady = true;
+			if (field == kFieldGender)
+				genderReady = true;
 		}
 
 		Common::Event ev;
 		while (g_system->getEventManager()->pollEvent(ev)) {
 			if (ev.type == Common::EVENT_QUIT ||
-				ev.type == Common::EVENT_RETURN_TO_LAUNCHER)
+				ev.type == Common::EVENT_RETURN_TO_LAUNCHER) {
+				CursorMan.showMouse(true);
+				g_system->setFeatureState(OSystem::kFeatureVirtualKeyboard, false);
 				return;
-			if (field == kFieldPartner && ev.type == Common::EVENT_LBUTTONDOWN) {
-				if (!partnerReady)
+			}
+			if (field == kFieldGender && ev.type == Common::EVENT_LBUTTONDOWN) {
+				if (!genderReady)
 					continue;
-				partner = (ev.mouse.x < 160) ? kPartnerJake : kPartnerJenny;
+				female = ev.mouse.x >= 160;
 				done = true;
 				needRedraw = true;
 				break;
 			}
-			if (field == kFieldPartner && ev.type == Common::EVENT_KEYUP &&
+			if (field == kFieldGender && ev.type == Common::EVENT_KEYUP &&
 				(ev.kbd.keycode == Common::KEYCODE_RETURN ||
 				 ev.kbd.keycode == Common::KEYCODE_KP_ENTER)) {
-				partnerCanConfirm = true;
+				genderCanConfirm = true;
 				continue;
 			}
 			if (ev.type != Common::EVENT_KEYDOWN)
@@ -1345,10 +1379,10 @@ void EEMEngine::showLondonCharSelect() {
 					Common::String clean = buf;
 					clean.trim();
 					if (!clean.empty()) {
-						field++;  // advance to last name, then to partner pick
-						if (field == kFieldPartner) {
-							partnerReady = false;
-							partnerCanConfirm = false;
+						field++;  // advance to last name, then to gender pick
+						if (field == kFieldGender) {
+							genderReady = false;
+							genderCanConfirm = false;
 						}
 					}
 				} else if (k == Common::KEYCODE_BACKSPACE && !buf.empty()) {
@@ -1357,42 +1391,49 @@ void EEMEngine::showLondonCharSelect() {
 						   buf.size() < cap) {
 					buf += (char)ev.kbd.ascii;
 				}
-			} else {  // partner pick
+			} else {  // gender pick
 				if (k == Common::KEYCODE_LEFT) {
-					partner = kPartnerJake;
-					partnerCanConfirm = true;
+					female = false;
+					genderCanConfirm = true;
 				} else if (k == Common::KEYCODE_RIGHT) {
-					partner = kPartnerJenny;
-					partnerCanConfirm = true;
+					female = true;
+					genderCanConfirm = true;
 				} else if (k == Common::KEYCODE_BACKSPACE) {
 					field = kFieldLast;  // back to editing
-					partnerCanConfirm = true;
-				} else if (enter && partnerReady && partnerCanConfirm) {
+					genderCanConfirm = true;
+				} else if (enter && genderReady && genderCanConfirm) {
 					done = true;
 				}
 			}
 			needRedraw = true;
-			if (field == kFieldPartner && !partnerReady)
+			if (field == kFieldGender && !genderReady)
 				break;
 		}
 
 		const uint32 now = g_system->getMillis();
-		if (field != kFieldPartner && now - blinkMs >= 400) {
+		if (field != kFieldGender && now - blinkMs >= 400) {
 			blinkMs = now;
 			blink = !blink;
 			needRedraw = true;
 		}
 		g_system->delayMillis(15);
 	}
-	if (shouldQuit())
+	if (shouldQuit()) {
+		CursorMan.showMouse(true);
+		g_system->setFeatureState(OSystem::kFeatureVirtualKeyboard, false);
 		return;
+	}
 
-	Common::Event staleEvent;
 	while (g_system->getEventManager()->pollEvent(staleEvent)) {
 		if (staleEvent.type == Common::EVENT_QUIT ||
-			staleEvent.type == Common::EVENT_RETURN_TO_LAUNCHER)
+			staleEvent.type == Common::EVENT_RETURN_TO_LAUNCHER) {
+			CursorMan.showMouse(true);
+			g_system->setFeatureState(OSystem::kFeatureVirtualKeyboard, false);
 			return;
+		}
 	}
+	CursorMan.showMouse(true);
+	g_system->setFeatureState(OSystem::kFeatureVirtualKeyboard, false);
 
 	const Common::String displayName =
 		makeLondonProfileDisplayName(first, last);
@@ -1418,7 +1459,6 @@ void EEMEngine::showLondonCharSelect() {
 	// New profile. EEM2 `_NewPlayer @ 1cd3:0f27` initializes the player
 	// record only when `_LoadPlayerRecord` failed, then immediately saves it.
 	_playerName = displayName.empty() ? "Detective" : displayName;
-	_partner = partner;
 	_chainStage = 1;
 	_voiceOn = true;
 	if (_audio)
@@ -1432,9 +1472,9 @@ void EEMEngine::showLondonCharSelect() {
 	if (err.getCode() != Common::kNoError)
 		warning("London: failed to save new profile '%s': %s",
 				_playerName.c_str(), err.getDesc().c_str());
-	debugC(1, kDebugGeneral, "London: new player='%s' key=%s partner=%s",
+	debugC(1, kDebugGeneral, "London: new player='%s' key=%s gender=%s",
 		   _playerName.c_str(), profileKey.c_str(),
-		   partner == kPartnerJake ? "Jake" : "Jenny");
+		   female ? "female" : "male");
 }
 
 void EEMEngine::showFloppyStormLogo() {
diff --git a/engines/eem/eem.h b/engines/eem/eem.h
index 786c41a089c..89d6f9634bf 100644
--- a/engines/eem/eem.h
+++ b/engines/eem/eem.h
@@ -427,13 +427,13 @@ private:
 	/// Opening sequence + character creation ??? EEM2's `_DoOpeningAnims`
 	/// @ 2721:08e6 then screen 8 profile selection.
 	void runLondonStartup();
-	/// Start London mystery 0 for a freshly-created detective.
+	/// Start London mystery 0 after a freshly-created detective chooses a partner.
 	bool startLondonTrainingMystery();
 	/// Blit a full-screen still PIC and fade it in / hold / out using the
 	/// given SITEPALS. palette index.
 	void showLondonLogo(uint picId, uint palId, uint holdMs);
 	/// EEM2 character creation (`_NewPlayer`: palette 0 + background PIC 0xc):
-	/// first/last name entry + Jake/Jenny selection.
+	/// first/last name entry + male/female player-gender selection.
 	void showLondonCharSelect();
 	/// EEM2 case-intro animation (`_DoInitClues` @ 1abf:03b3): single partner
 	/// anim (Jake 0x18 / Jenny 0x71) faded in, then phone1.voc on caseType 1.


Commit: ad1a9413609fb00f111f87ae9a01ef015dd9076e
    https://github.com/scummvm/scummvm/commit/ad1a9413609fb00f111f87ae9a01ef015dd9076e
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:21:26+02:00

Commit Message:
EEM: proper case selection in London

Changed paths:
    engines/eem/ui.cpp


diff --git a/engines/eem/ui.cpp b/engines/eem/ui.cpp
index d2be151d480..365edd16d8e 100644
--- a/engines/eem/ui.cpp
+++ b/engines/eem/ui.cpp
@@ -1998,9 +1998,10 @@ void EEMEngine::doActionScreen() {
 }
 
 void EEMEngine::doCaseSelection() {
-	// `_DoChooseMystery @ 1a35:02b7` + `_CaseSelection @ 1c33:0a87` ???
-	// loads BOOK<stage>.NME, draws PIC 0x41 + centered "Book N" title.
-	const uint kMaxMystery = 54;
+	// `_DoChooseMystery @ 1a35:02b7` + `_CaseSelection @ 1c33:0a87`
+	// (EEM2 @ 1abf:022a / 1cd3:0a9d): load BOOK<stage>.NME, draw PIC 0x41
+	// + centered "Book N" title, then read the chosen M*.BIN.
+	const uint kMaxMystery = isLondon() ? 50 : 54;
 
 	CursorMan.showMouse(true);
 	setSitePalette(0);
@@ -2021,20 +2022,30 @@ void EEMEngine::doCaseSelection() {
 	const int kKdAnimX = 0x112;
 	const int kKdAnimY = 0x50;
 
-	// stage 1 (Junior, BOOK1.NME) = 1..24, stage 2 (Senior, BOOK2.NME) =
-	// 25..48, stage 3 (Master, BOOK3.NME) = 49..54.
-	uint stageLo = 1, stageHi = 0x18;
+	// EEM1: stage 1 (Junior, BOOK1.NME) = 1..24, stage 2 (Senior,
+	// BOOK2.NME) = 25..48, stage 3 (Master, BOOK3.NME) = 49..54.
+	// EEM2/London: only BOOK1.NME and BOOK2.NME ship, each with 25 cases.
+	uint stageLo = 1, stageHi = isLondon() ? 0x19 : 0x18;
 	uint book = 1;
 	switch (_chainStage) {
 	case 2:
-		stageLo = 0x19;
-		stageHi = 0x30;
+		stageLo = isLondon() ? 0x1a : 0x19;
+		stageHi = isLondon() ? 0x32 : 0x30;
 		book = 2;
 		break;
 	case 3:
-		stageLo = 0x31;
-		stageHi = 0x36;
-		book = 3;
+		if (isLondon()) {
+			// London has no BOOK3.NME. Until the London progression path is
+			// fully split from EEM1, keep the picker on the second book rather
+			// than attempting to open missing data.
+			stageLo = 0x1a;
+			stageHi = 0x32;
+			book = 2;
+		} else {
+			stageLo = 0x31;
+			stageHi = 0x36;
+			book = 3;
+		}
 		break;
 	default:
 		break;
@@ -2233,14 +2244,6 @@ void EEMEngine::doCaseSelection() {
 		return;
 
 	const uint mn = stageLo + selRow;
-	if (isLondon()) {
-		// EEM2: the menu (PIC 0x41, ANI 0x15/0x16, BOOK*.NME) is shared with
-		// EEM1, but loading a chosen mystery from the menu isn't wired up yet
-		// (the training case is started directly from run()), so stop here.
-		debugC(1, kDebugMystery,
-			   "London: selected mystery %u (menu load not implemented yet)", mn);
-		return;
-	}
 	if (!_mystery.load(mn, &_rng)) {
 		warning("doCaseSelection: failed to load mystery %u", mn);
 		_mystery.clear();


Commit: 3f0cccf90ae9b4eed827aaf1dff922b919c239dd
    https://github.com/scummvm/scummvm/commit/3f0cccf90ae9b4eed827aaf1dff922b919c239dd
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:21:26+02:00

Commit Message:
EEM: more side effects for clues in London

Changed paths:
    engines/eem/clues.cpp
    engines/eem/mystery.cpp
    engines/eem/mystery.h


diff --git a/engines/eem/clues.cpp b/engines/eem/clues.cpp
index a6d7f734cd0..86a627d6f18 100644
--- a/engines/eem/clues.cpp
+++ b/engines/eem/clues.cpp
@@ -97,6 +97,35 @@ uint markClueBlockNotebookEntries(Mystery &mystery, const byte *clueBlock,
 	return marked;
 }
 
+void updateLondonClueSite(Mystery &mystery, uint16 rawSite, bool siteOn,
+						  uint slot) {
+	if (rawSite == 0xFFFF)
+		return;
+
+	const bool conditional = (rawSite & 0x8000) != 0;
+	const uint16 siteVal = rawSite & 0x7FFF;
+	if (!conditional) {
+		if (siteVal < Mystery::kVisitedSiteCap)
+			mystery._onSites[siteVal] = siteOn ? 1 : 0;
+		return;
+	}
+
+	uint8 &seen = siteOn ? mystery._seenCONSITEs : mystery._seenCOFFSITEs;
+	const uint8 total = siteOn ? mystery.numCONSITEs() : mystery.numCOFFSITEs();
+	if (siteOn)
+		mystery._sawCONSITEs = true;
+	else
+		mystery._sawCOFFSITEs = true;
+
+	if (slot != 0)
+		return;
+
+	if (seen != 0xFF)
+		seen++;
+	if (seen == total && siteVal < Mystery::kVisitedSiteCap)
+		mystery._onSites[siteVal] = siteOn ? 1 : 0;
+}
+
 // _DoHappiness @ 172b:27b5 ??? per-zone sequence scripts.
 // Jake seqs @ 29be:0337 (5 ?? 0x14 bytes), Jenny seqs @ 29be:039b. 9 frames each;
 // the anim cells contain 10 cells = pairs of (neutral, smile) at 5 intensities.
@@ -800,30 +829,26 @@ void EEMEngine::applyClueSideEffects(const byte *c) {
 		// EEM2 `_DisplayClue @ 2542:05bd` per-entry side effects. With the
 		// shared `c = entryBase + 4` convention the EEM2 fields land at:
 		//   onsite  entry+0x22 (= c+0x1e), 5 ?? u16, high bit = CONSITE flag
-		//   offsite entry+0x2c (= c+0x28), 5 ?? u16, clears the site
+		//   offsite entry+0x2c (= c+0x28), 5 ?? u16, high bit = COFFSITE flag
+		//   gallery entry+0x36 (= c+0x32), 5 ?? u16 -> _InGallery[_newOrder[idx]]
 		//   notebook entry+0x40 (= c+0x3c), 5 ?? u16 -> _AddNotebook
 		//   jump    entry+0x4a (= c+0x46), destination site for direct travel
-		// (EEM2 has no gallery list here ??? that region is the onsite array.)
 		for (uint j = 0; j < 5; j++) {
 			const uint16 note = READ_LE_UINT16(c + 0x3c + j * 2);
 			if (note != 0xFFFF && note < Mystery::kCluesFoundCap)
 				_mystery._cluesFound[note] = 1;
 
-			const uint16 onIdx = READ_LE_UINT16(c + 0x1e + j * 2);
-			if (onIdx != 0xFFFF) {
-				const uint16 siteVal = onIdx & 0x7FFF;
-				if (siteVal < Mystery::kVisitedSiteCap)
-					_mystery._onSites[siteVal] = 1;
-				if (onIdx & 0x8000)
-					_mystery._sawCONSITEs = true;
+			const uint16 galIdx = READ_LE_UINT16(c + 0x32 + j * 2);
+			if (galIdx != 0xFFFF && galIdx < Mystery::kGalleryCap) {
+				const uint8 phys = _mystery._newOrder[galIdx];
+				if (phys < Mystery::kGalleryCap)
+					_mystery._inGallery[phys] = 1;
 			}
 
-			const uint16 offIdx = READ_LE_UINT16(c + 0x28 + j * 2);
-			if (offIdx != 0xFFFF && (offIdx & 0x8000) == 0) {
-				const uint16 siteVal = offIdx & 0x7FFF;
-				if (siteVal < Mystery::kVisitedSiteCap)
-					_mystery._onSites[siteVal] = 0;
-			}
+			updateLondonClueSite(_mystery, READ_LE_UINT16(c + 0x1e + j * 2),
+								 true, j);
+			updateLondonClueSite(_mystery, READ_LE_UINT16(c + 0x28 + j * 2),
+								 false, j);
 		}
 		const uint16 jumpSite = READ_LE_UINT16(c + 0x46);
 		if (jumpSite != 0xFFFF)
diff --git a/engines/eem/mystery.cpp b/engines/eem/mystery.cpp
index bb87dbc9939..3469a5d350e 100644
--- a/engines/eem/mystery.cpp
+++ b/engines/eem/mystery.cpp
@@ -61,6 +61,7 @@ void Mystery::clear() {
 	memset(_visitedSite, 0, sizeof(_visitedSite));
 	memset(_onSites, 0, sizeof(_onSites));
 	_sawCOFFSITEs = _sawCONSITEs = _sawHelpHint = _solvedPuzzle = false;
+	_seenCOFFSITEs = _seenCONSITEs = 0;
 	_firstTry = true;
 	_searchLocationNumber = _siteNumber = 0xFFFF;
 	_lastSite = 0x1B;
@@ -154,6 +155,7 @@ bool Mystery::load(uint num, Common::RandomSource *rng) {
 		memset(_visitedSite, 0, sizeof(_visitedSite));
 		memset(_onSites, 0, sizeof(_onSites));
 		_sawCOFFSITEs = _sawCONSITEs = _sawHelpHint = _solvedPuzzle = false;
+		_seenCOFFSITEs = _seenCONSITEs = 0;
 		_firstTry = true;
 		_searchLocationNumber = _siteNumber = 0xFFFF;
 		_lastSite = 0x1B;
@@ -216,6 +218,7 @@ bool Mystery::load(uint num, Common::RandomSource *rng) {
 	memset(_visitedSite, 0, sizeof(_visitedSite));
 	memset(_onSites, 0, sizeof(_onSites));
 	_sawCOFFSITEs = _sawCONSITEs = _sawHelpHint = _solvedPuzzle = false;
+	_seenCOFFSITEs = _seenCONSITEs = 0;
 	_firstTry = true;
 	_searchLocationNumber = _siteNumber = 0xFFFF;
 	_lastSite = 0x1B; // _ReadMystery _LastSite sentinel.
@@ -622,6 +625,8 @@ void Mystery::syncState(Common::Serializer &s) {
 				Common::Serializer::Uint16LE);
 	if (_siteReturnDepth > kVisitedSiteCap)
 		_siteReturnDepth = 0;
+	s.syncAsByte(_seenCOFFSITEs);
+	s.syncAsByte(_seenCONSITEs);
 }
 
 } // End of namespace EEM
diff --git a/engines/eem/mystery.h b/engines/eem/mystery.h
index 7f3d16589c3..32c22d7f806 100644
--- a/engines/eem/mystery.h
+++ b/engines/eem/mystery.h
@@ -166,6 +166,8 @@ public:
 	uint16 _onSites[kVisitedSiteCap]     = {};
 	bool   _sawCOFFSITEs = false;
 	bool   _sawCONSITEs  = false;
+	uint8  _seenCOFFSITEs = 0;
+	uint8  _seenCONSITEs  = 0;
 	bool   _sawHelpHint  = false;
 	bool   _solvedPuzzle = false;
 	bool   _firstTry     = true;


Commit: 66393f6f85a5f8a9a34eef14f51bc44cdc5b0a57
    https://github.com/scummvm/scummvm/commit/66393f6f85a5f8a9a34eef14f51bc44cdc5b0a57
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:21:27+02:00

Commit Message:
EEM: implement game progression in London

Changed paths:
    engines/eem/eem.cpp
    engines/eem/eem.h
    engines/eem/ui.cpp


diff --git a/engines/eem/eem.cpp b/engines/eem/eem.cpp
index 705195737c8..2b4ad77c7a7 100644
--- a/engines/eem/eem.cpp
+++ b/engines/eem/eem.cpp
@@ -240,8 +240,11 @@ void EEMEngine::applyStartupTestOverrides() {
 	if (!kDebugPopulateScrapbook1AtStartup)
 		return;
 
-	for (uint i = 1; i <= 0x18 && i < sizeof(_mysteriesSolved); i++)
-		_mysteriesSolved[i] = 1;
+	uint lo = 0, hi = 0;
+	if (mysteryTierRange(1, lo, hi)) {
+		for (uint i = lo; i <= hi && i < sizeof(_mysteriesSolved); i++)
+			_mysteriesSolved[i] = 1;
+	}
 
 	debugC(1, kDebugGeneral,
 		   "startup test override: populated ScrapBook 1 mystery flags");
@@ -257,47 +260,68 @@ bool EEMEngine::areMysteriesSolved(uint lo, uint hi) const {
 	return true;
 }
 
+bool EEMEngine::anyMysterySolved(uint lo, uint hi) const {
+	for (uint i = lo; i <= hi && i < sizeof(_mysteriesSolved); i++) {
+		if (_mysteriesSolved[i] != 0)
+			return true;
+	}
+	return false;
+}
+
+bool EEMEngine::mysteryTierRange(uint stage, uint &lo, uint &hi) const {
+	if (isLondon()) {
+		// EEM2/London: two 25-case books, no BOOK3.NME
+		// (`_DisplayCorrect @ 1ea1:0619`).
+		switch (stage) {
+		case 1: lo = 0x01; hi = 0x19; return true;  //  1..25
+		case 2: lo = 0x1a; hi = 0x32; return true;  // 26..50
+		default: return false;
+		}
+	}
+	// EEM1: Junior 1..24, Senior 25..48, Master 49..54
+	// (`_DisplayCorrect @ 1df2:073c`).
+	switch (stage) {
+	case 1: lo = 0x01; hi = 0x18; return true;  //  1..24
+	case 2: lo = 0x19; hi = 0x30; return true;  // 25..48
+	case 3: lo = 0x31; hi = 0x36; return true;  // 49..54
+	default: return false;
+	}
+}
+
 void EEMEngine::advanceChainStageAfterSolve(uint mysteryNum) {
 	if (mysteryNum == 0 || _chainStage >= 4)
 		return;
 
-	uint lo = 0;
-	uint hi = 0;
-	switch (_chainStage) {
-	case 1:
-		lo = 1;
-		hi = 0x18;
-		break;
-	case 2:
-		lo = 0x19;
-		hi = 0x30;
-		break;
-	case 3:
-		lo = 0x31;
-		hi = 0x36;
-		break;
-	default:
+	uint lo = 0, hi = 0;
+	if (!mysteryTierRange(_chainStage, lo, hi))
 		return;
-	}
-
 	if (!areMysteriesSolved(lo, hi))
 		return;
 
 	const uint oldStage = _chainStage;
-	// Book 2 repeats the Book 1 cases; this option keeps the original solve
-	// state but jumps the profile's active chain straight to Book 3.
-	if (_chainStage == 1 && ConfMan.getBool("skip_repeated_cases"))
+	// EEM1 only: Book 2 repeats the Book 1 cases, so this option keeps the
+	// original solve state but jumps the active chain straight to Book 3.
+	// London has 50 distinct cases and no Book 3, so the option does not apply.
+	if (!isLondon() && _chainStage == 1 &&
+		ConfMan.getBool("skip_repeated_cases"))
 		_chainStage = 3;
 	else
 		_chainStage++;
 
+	// London has only two books: `_DisplayCorrect @ 1ea1:0619` collapses
+	// chainStage 3 straight to 4 (every case solved ??? game complete).
+	if (isLondon() && _chainStage == 3)
+		_chainStage = 4;
+
 	debugC(1, kDebugMystery,
 		   "chainStage advanced from %u to %u after solving mystery %u",
 		   oldStage, _chainStage, mysteryNum);
 }
 
 void EEMEngine::applySkipRepeatedCasesOption() {
-	if (!ConfMan.getBool("skip_repeated_cases"))
+	// EEM1 only ??? the option jumps past the repeated Book 2 to Book 3, which
+	// London does not have (see `advanceChainStageAfterSolve`).
+	if (isLondon() || !ConfMan.getBool("skip_repeated_cases"))
 		return;
 	if (_mystery.isLoaded())
 		return;
diff --git a/engines/eem/eem.h b/engines/eem/eem.h
index 89d6f9634bf..8bc4136c4a4 100644
--- a/engines/eem/eem.h
+++ b/engines/eem/eem.h
@@ -302,6 +302,22 @@ public:
 private:
 	void applyStartupTestOverrides();
 	bool areMysteriesSolved(uint lo, uint hi) const;
+
+	/// True if *any* mystery in the inclusive 1-based range [lo, hi] is
+	/// marked solved.
+	bool anyMysterySolved(uint lo, uint hi) const;
+
+	/// Number of case "books"/tiers in this variant. EEM1 ships three
+	/// (BOOK1..3.NME ??? 24/24/6 cases); EEM2/London ships two (BOOK1..2.NME ???
+	/// 25 cases each, no BOOK3.NME). See `mysteryTierRange`.
+	uint mysteryTierCount() const { return isLondon() ? 2 : 3; }
+
+	/// Inclusive 1-based mystery range [lo, hi] for chain `stage` (1-based).
+	/// Returns false when `stage` is not a real tier in this variant.
+	/// EEM1 `_DisplayCorrect @ 1df2:073c` (1..24 / 25..48 / 49..54);
+	/// EEM2/London `_DisplayCorrect @ 1ea1:0619` (1..25 / 26..50).
+	bool mysteryTierRange(uint stage, uint &lo, uint &hi) const;
+
 	void advanceChainStageAfterSolve(uint mysteryNum);
 	void applySkipRepeatedCasesOption();
 
@@ -467,9 +483,10 @@ private:
 	///   +1 ??? next mystery (RIGHT/SPACE/Enter/click on last page).
 	int doShowEnding(uint num, bool firstPage = true);
 
-	/// `_ShowScrapbook(stage, 0) @ 1f78:0642`. Mystery range
-	/// `(stage-1)*0x18+1 .. (stage-1)*0x18+0x18`; skips unsolved
-	/// entries in the current chain stage.
+	/// EEM1 `_ShowScrapbook(stage, 0) @ 1f78:0642`; EEM2/London scrapbook
+	/// `FUN_2046_09dd`. Walks the `mysteryTierRange(stage)` cases, skipping
+	/// unsolved entries in the current chain stage. Tier sizes are
+	/// variant-specific (EEM1 24/24/6; London 25/25, no stage 3).
 	void doShowScrapbook(uint stage);
 
 	void doActionScreen();
diff --git a/engines/eem/ui.cpp b/engines/eem/ui.cpp
index 365edd16d8e..2613a680aee 100644
--- a/engines/eem/ui.cpp
+++ b/engines/eem/ui.cpp
@@ -612,6 +612,7 @@ struct ActionMenuView {
 	const char *const *pickLabel;
 	const bool *pickEnabled;
 	uint pick;
+	uint numPicks;  ///< Visible entries (London drops ScrapBook 3 ??? 4).
 };
 
 // `_DoChooseMystery @ 1a35:02b7` ??? opens BOOK%u.NME (CRLF strings, up
@@ -812,9 +813,10 @@ void drawActionMenuFrame(const ActionMenuView &v) {
 		const int kListY0 = 35;
 		const int kLineH  = 10;
 
-		// 11 rows: separator/item pairs (0=sep, 1=Choose A Mystery, ...,
-		// 9=See Scrapbook 3, 10=sep).
-		for (int r = 0; r < 11; r++) {
+		// Separator/item pairs (0=sep, 1=Choose A Mystery, ..., trailing sep):
+		// 11 rows for EEM1's five picks, 9 for London's four (no ScrapBook 3).
+		const int kRows = (int)(2 * v.numPicks + 1);
+		for (int r = 0; r < kRows; r++) {
 			const int y = kListY0 + r * kLineH;
 			if ((r & 1) == 0) {
 				v.vm->getFont().drawString(&scratch, v.separator,
@@ -1410,15 +1412,18 @@ int EEMEngine::doShowEnding(uint num, bool firstPage) {
 }
 
 void EEMEngine::doShowScrapbook(uint stage) {
-	// `_ShowScrapbook(stage, 0) @ 1f78:0642`. 24-entry tiers; ending
-	// viewer returns -1/0/+1 for prev/close/next. Current tier skips
-	// unsolved entries.
-	if (stage < 1 || stage > 3)
+	// EEM1 `_ShowScrapbook(stage, 0) @ 1f78:0642`; EEM2/London scrapbook
+	// `FUN_2046_09dd`. Ending viewer returns -1/0/+1 for prev/close/next;
+	// current tier skips unsolved entries. Tier sizes are variant-specific
+	// (EEM1: 24/24/6 cases over three books; London: 25/25 over two books,
+	// no BOOK3.NME) ??? `mysteryTierRange` returns false for London stage 3.
+	uint tierLo = 0, tierHi = 0;
+	if (stage < 1 || !mysteryTierRange(stage, tierLo, tierHi))
 		return;
 	const int solvedCount =
 		(int)(sizeof(_mysteriesSolved) / sizeof(_mysteriesSolved[0]));
-	const int lo = (int)(stage - 1) * 0x18 + 1;
-	const int hi = MIN<int>(lo + 0x18, solvedCount);
+	const int lo = (int)tierLo;
+	const int hi = MIN<int>((int)tierHi + 1, solvedCount);
 	if (lo >= hi)
 		return;
 	const bool currentTier = (stage == _chainStage);
@@ -1664,7 +1669,9 @@ void EEMEngine::doSetup() {
 				dirty = true;
 				continue;
 			}
-			if (kScrap3Btn.contains(mx, my) && _chainStage >= 3) {
+			// London has no third book (`mysteryTierRange` rejects stage 3).
+			if (kScrap3Btn.contains(mx, my) && !isLondon() &&
+				_chainStage >= 3) {
 				doShowScrapbook(3);
 				setSitePalette(0);
 				dirty = true;
@@ -1787,32 +1794,22 @@ void EEMEngine::doActionScreen() {
 		"         Ver Recortes  3"
 	};
 	const char * const *kPickLabel = isSpanish() ? kPickLabelES : kPickLabelEN;
-	// Gating @ 1c33:19d1-1a70 by chain stage + per-tier solves:
+	// London has no BOOK3.NME, so its action menu offers four entries
+	// (Choose / Practice / ScrapBook 1 / ScrapBook 2). EEM1 adds ScrapBook 3.
+	const uint numPicks = isLondon() ? (uint)kPickScrap3 : (uint)kNumPicks;
+
+	// Gating @ EEM1 1c33:19d1-1a70 by chain stage + per-tier solves:
 	//   stage 1: grey SB2/3; SB1 needs any tier-1 solve
 	//   stage 2: grey Practice + SB3; SB2 needs tier-2 solve
-	//   stage 3: grey Practice; SB3 needs tier-3 solve
+	//   stage 3: grey Practice; SB3 needs tier-3 solve (EEM1 only)
 	//   stage 4: grey Choose + Practice
-	bool anySolved1 = false;
-	for (uint i = 1; i <= 0x18 && i < sizeof(_mysteriesSolved); i++) {
-		if (_mysteriesSolved[i]) {
-			anySolved1 = true;
-			break;
-		}
-	}
-	bool anySolved2 = false;
-	for (uint i = 0x19; i <= 0x30 && i < sizeof(_mysteriesSolved); i++) {
-		if (_mysteriesSolved[i]) {
-			anySolved2 = true;
-			break;
-		}
-	}
-	bool anySolved3 = false;
-	for (uint i = 0x31; i <= 0x36 && i < sizeof(_mysteriesSolved); i++) {
-		if (_mysteriesSolved[i]) {
-			anySolved3 = true;
-			break;
-		}
-	}
+	uint loT = 0, hiT = 0;
+	const bool anySolved1 = mysteryTierRange(1, loT, hiT) &&
+							anyMysterySolved(loT, hiT);
+	const bool anySolved2 = mysteryTierRange(2, loT, hiT) &&
+							anyMysterySolved(loT, hiT);
+	const bool haveTier3 = mysteryTierRange(3, loT, hiT);
+	const bool anySolved3 = haveTier3 && anyMysterySolved(loT, hiT);
 
 	const bool chooseOn   = _chainStage < 4;
 	const bool practiceOn = _chainStage <= 1;
@@ -1821,13 +1818,13 @@ void EEMEngine::doActionScreen() {
 	const bool scrap2On =
 		_chainStage >= 3 || (_chainStage == 2 && anySolved2);
 	const bool scrap3On =
-		_chainStage >= 4 || (_chainStage == 3 && anySolved3);
+		haveTier3 && (_chainStage >= 4 || (_chainStage == 3 && anySolved3));
 	const bool kPickEnabled[kNumPicks] = {
 		chooseOn, practiceOn, scrap1On, scrap2On, scrap3On
 	};
 	// Seed selection on first enabled entry.
 	uint pick = 0;
-	for (uint i = 0; i < kNumPicks; i++) {
+	for (uint i = 0; i < numPicks; i++) {
 		if (kPickEnabled[i]) {
 			pick = i;
 			break;
@@ -1862,6 +1859,7 @@ void EEMEngine::doActionScreen() {
 	v.pickLabel = kPickLabel;
 	v.pickEnabled = kPickEnabled;
 	v.pick = pick;
+	v.numPicks = numPicks;
 
 	drawActionMenuFrame(v);
 	uint32 chooserLastTick = g_system->getMillis();
@@ -1895,7 +1893,7 @@ void EEMEngine::doActionScreen() {
 					const int row = (ev.mouse.y - kListRect.top) / kLineH;
 					if ((row & 1) == 1) {
 						const uint mp = (uint)(row >> 1);
-						if (mp < kNumPicks && kPickEnabled[mp]) {
+						if (mp < numPicks && kPickEnabled[mp]) {
 							pick = mp;
 							v.pick = pick;
 							drawActionMenuFrame(v);
@@ -1919,8 +1917,8 @@ void EEMEngine::doActionScreen() {
 			}
 			if (k == Common::KEYCODE_UP || k == Common::KEYCODE_LEFT) {
 				// `_DoChoose` arrow handlers @ 1c33:0514, bounded loop.
-				for (int i = 0; i < (int)kNumPicks; i++) {
-					pick = (pick == 0) ? (uint)(kNumPicks - 1) : pick - 1;
+				for (int i = 0; i < (int)numPicks; i++) {
+					pick = (pick == 0) ? (numPicks - 1) : pick - 1;
 					if (kPickEnabled[pick])
 						break;
 				}
@@ -1930,8 +1928,8 @@ void EEMEngine::doActionScreen() {
 			}
 			if (k == Common::KEYCODE_DOWN || k == Common::KEYCODE_RIGHT ||
 				k == Common::KEYCODE_TAB) {
-				for (int i = 0; i < (int)kNumPicks; i++) {
-					pick = (pick + 1) % kNumPicks;
+				for (int i = 0; i < (int)numPicks; i++) {
+					pick = (pick + 1) % numPicks;
 					if (kPickEnabled[pick])
 						break;
 				}
@@ -2022,34 +2020,20 @@ void EEMEngine::doCaseSelection() {
 	const int kKdAnimX = 0x112;
 	const int kKdAnimY = 0x50;
 
-	// EEM1: stage 1 (Junior, BOOK1.NME) = 1..24, stage 2 (Senior,
-	// BOOK2.NME) = 25..48, stage 3 (Master, BOOK3.NME) = 49..54.
-	// EEM2/London: only BOOK1.NME and BOOK2.NME ship, each with 25 cases.
-	uint stageLo = 1, stageHi = isLondon() ? 0x19 : 0x18;
-	uint book = 1;
+	// Tier ??? BOOK<n>.NME. EEM1 ships three books (Junior 1..24, Senior
+	// 25..48, Master 49..54); EEM2/London ships two (1..25 / 26..50). London
+	// has no BOOK3.NME, and its stage 3 is transient (`_DisplayCorrect @
+	// 1ea1:0619` collapses it to 4), so should it ever be reached here it
+	// reuses BOOK2.NME rather than opening a missing file. `mysteryTierRange`
+	// is the single source of truth for the per-tier mystery ranges.
+	uint book;
 	switch (_chainStage) {
-	case 2:
-		stageLo = isLondon() ? 0x1a : 0x19;
-		stageHi = isLondon() ? 0x32 : 0x30;
-		book = 2;
-		break;
-	case 3:
-		if (isLondon()) {
-			// London has no BOOK3.NME. Until the London progression path is
-			// fully split from EEM1, keep the picker on the second book rather
-			// than attempting to open missing data.
-			stageLo = 0x1a;
-			stageHi = 0x32;
-			book = 2;
-		} else {
-			stageLo = 0x31;
-			stageHi = 0x36;
-			book = 3;
-		}
-		break;
-	default:
-		break;
+	case 2:  book = 2; break;
+	case 3:  book = isLondon() ? 2 : 3; break;
+	default: book = 1; break;
 	}
+	uint stageLo = 0, stageHi = 0;
+	mysteryTierRange(book, stageLo, stageHi);
 	if (stageHi > kMaxMystery)
 		stageHi = kMaxMystery;
 
@@ -3889,10 +3873,11 @@ void EEMEngine::accuseDrawScreen(const AccuseNotesCtx &ctx) {
 }
 
 bool EEMEngine::doAccuseNotes() {
-	// `_DoAccuse @ 1df2:0bdd` head. BG PIC 0x1A7. `_AccuseNoteRect @
-	// 29be:1048` = (79, 27, 304, 159). Counter @ (209, 11) shows
-	// `6 - chainStage` (stage 1=5, 2=4, 3=3 clues). Unselected color 1
-	// (red), selected 0x3c. Click toggles selection; `_NoteButtons[4]`
+	// `_DoAccuse @ 1df2:0bdd` (EEM2 `@ 1ea1:0c03`) head. BG PIC 0x1A7.
+	// `_AccuseNoteRect @ 29be:1048` = (79, 27, 304, 159). Counter @ (209, 11)
+	// shows the required clue count (EEM1 `6 - chainStage` = 5/4/3 by tier;
+	// London a flat 5 ??? see `expected` below). Unselected color 1 (red),
+	// selected 0x3c. Click toggles selection; `_NoteButtons[4]`
 	// (180,174,201,190) SOLVE ??? `_HandleAccuseNoteButton` returns 2.
 	if (!_mystery.isLoaded() || !_font.isLoaded())
 		return false;
@@ -3907,9 +3892,13 @@ bool EEMEngine::doAccuseNotes() {
 	// `FUN_1d40_0e07 @ 1d40:0e34` zeroes _NoteSelected_Floppy on entry.
 	memset(_mystery._noteSelected, 0, sizeof(_mystery._noteSelected));
 
-	const uint expected = (_chainStage >= 1 && _chainStage <= 3)
-		? (uint)(6 - _chainStage)
-		: 5;
+	// EEM1 `_DoAccuse @ 1df2:0bdd` scales the required clue count by tier
+	// (`6 - chainStage`: stage 1=5, 2=4, 3=3). London `_DoAccuse @ 1ea1:0c03`
+	// hardcodes 5 (`local_a = 5`) for both books.
+	const uint expected = isLondon()
+		? 5
+		: ((_chainStage >= 1 && _chainStage <= 3) ? (uint)(6 - _chainStage)
+												  : 5);
 
 	// `_DrawNotes(NULL, 100, ...)` walks `_CluesFound[]`.
 	Common::Array<uint> found;
@@ -4199,7 +4188,7 @@ void EEMEngine::doAccuse() {
 	}
 
 	// `_DoAccuse @ 1df2:0bdd` + `_DoAccuseGallery @ 1df2:0a31`:
-	//   1. Accuse-notes (PIC 0x1A7) ??? pick `6 - chainStage` clues.
+	//   1. Accuse-notes (PIC 0x1A7) ??? pick `6 - chainStage` clues (London: 5).
 	//      Pass `_SolvedCheck` ??? gallery; fail ??? hint + return.
 	//   2. KD intro balloon (`KDTextIndex[+8]` + `_SayKDDigital(4)`).
 	//   3. PIC 0x3f + `_DrawGallery` portraits at 5 slots (29be:0x116).
@@ -5083,7 +5072,8 @@ void EEMEngine::doAccuseFloppy() {
 	}
 
 	// Clue selection ??? `FUN_1d40_0e07 @ 1d40:0e07`. Pick `6 - chainStage`
-	// clues (1d40:0e34: `local_c = 6 - DAT_28da_3052`).
+	// clues (1d40:0e34: `local_c = 6 - DAT_28da_3052`); London a flat 5.
+	// Count handled inside `doAccuseNotes`.
 	if (!doAccuseNotes()) {
 		if (_nextScreen == kScreenAccuse) {
 			_nextScreen = _lastScreen != kScreenInvalid


Commit: 7ca4b9f330a53a1f958f6322ba5b2a5355eff4c7
    https://github.com/scummvm/scummvm/commit/7ca4b9f330a53a1f958f6322ba5b2a5355eff4c7
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:21:27+02:00

Commit Message:
EEM: added missing musical cue in London

Changed paths:
    engines/eem/clues.cpp


diff --git a/engines/eem/clues.cpp b/engines/eem/clues.cpp
index 86a627d6f18..2887d99ab96 100644
--- a/engines/eem/clues.cpp
+++ b/engines/eem/clues.cpp
@@ -34,6 +34,7 @@
 #include "eem/audio.h"
 #include "eem/detection.h"
 #include "eem/eem.h"
+#include "eem/music.h"
 #include "eem/site.h"
 
 // Clue / briefing pipeline (SCRIPT.C + KD.C).
@@ -929,6 +930,18 @@ void EEMEngine::displayClue(const byte *clueBlock) {
 		g_system->copyRectToScreen(bg.getPixels(), bg.pitch, 0, 0, kScreenWidth, kScreenHeight);
 		const byte *c = clueBlock + 4 + i * stride;
 
+		// EEM2 `_DisplayClue @ 2542:05bd` opens each clue with a per-entry MIDI
+		// cue read at entry+0x20 (= c+0x1c, the u16 right after the two voice
+		// slots), replacing any current track (`playMus`???`playFile` calls
+		// `stop()`). 0 = no cue; EEM1 clue entries have no such field. Gated on
+		// `_voiceOn` like `startTravelMusic` ??? the DOS gate is the separate
+		// music-on / MIDI-available flags (DAT_3036_4cc0 && DAT_2bca_146a).
+		if (isLondon() && _music && _voiceOn) {
+			const uint16 clueMusic = READ_LE_UINT16(c + 0x1c);
+			if (clueMusic != 0)
+				_music->playMus(clueMusic, /* loop= */ false);
+		}
+
 		// _DisplayClue @ 2404:0635-064b: _DoKDAnim(num) runs before the
 		// speaker portrait. EEM1 stores the KD-anim number at +0x3a; EEM2
 		// `_DisplayClue @ 2542:05bd` reads it at entry+0x52 (= c+0x4e).


Commit: 53af04e55c17668bf39c9b907ad0bd0379462dc1
    https://github.com/scummvm/scummvm/commit/53af04e55c17668bf39c9b907ad0bd0379462dc1
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:21:27+02:00

Commit Message:
EEM: added missing conversation opcodes in London

Changed paths:
    engines/eem/clues.cpp
    engines/eem/eem.cpp
    engines/eem/eem.h


diff --git a/engines/eem/clues.cpp b/engines/eem/clues.cpp
index 2887d99ab96..0abed334700 100644
--- a/engines/eem/clues.cpp
+++ b/engines/eem/clues.cpp
@@ -729,22 +729,23 @@ void EEMEngine::doInitClues() {
 	}
 }
 
-// _ParseString @ 1b66:07c3 (jump table @ 1b66:0cbe). Each handler reads
-// _Partner (u16 @ 0x7918) and indexes the name table @ 29be:0c28
-// ({Jake, Jennifer, he, she, him, her, his} as far pointers).
+// _ParseString @ EEM1 1b66:07c3 / EEM2 1bff:07c4 (jump table @ EEM2
+// CS:0xd2f). Each handler indexes a name table of far pointers
+// ({Jake, Jennifer, he, she, him, her, his}).
 //   0x80 player name (auto-cap word starts, uses _PlayerRecord)
 //   0x81 _Partner == 0 ? "Jake"     : "Jennifer"  (chosen detective)
 //   0x82 _Partner == 0 ? "Jennifer" : "Jake"      (the OTHER one)
 //   0x83 _Partner == 0 ? "he"       : "she"
 //   0x84 _Partner == 0 ? "him"      : "her"
 //   0x85 _Partner == 0 ? "his"      : "her"
-//   0x86..0x88 mirror 0x83..0x85 but branch on a separate gender flag
-//     (DAT_29be_7985, handlers @ 1b66:0ad2/0b41/0bb0). That flag has no
-//     writers anywhere in EEMCD.EXE ??? only the three handlers read it ???
-//     and no shipping mystery text (CD or floppy) contains the 0x86/0x87
-//     /0x88 bytes inside parseString-formatted strings. The opcodes are
-//     dead in the original engine and dead in our data, so we drop them
-//     silently (which matches the always-0 flag path in DOS: he/him/his).
+//   0x86..0x88 emit the same he/him/his vs she/her/her strings as 0x83..0x85
+//     but branch on a SEPARATE gender flag, not _Partner (EEM2 handlers
+//     @ 1bff:0ad3/0b42/0bb1 test [0x930c]; the partner handlers test
+//     [0x9294]). They are the PLAYER's pronouns: EEM2/London sets that flag
+//     from the passport gender pick (`_NewPlayer` DAT_3036_4c4c), and London
+//     dialogue uses them (e.g. M13/M50.BIN "You and <86>..."). EEM1 never
+//     writes the flag (DAT_29be_7985) and ships no text using these bytes, so
+//     its always-male path is reproduced by `_playerFemale` defaulting false.
 //   0x89 KD hint placeholder (caller handles).
 Common::String EEMEngine::parseString(const Common::String &raw,
 									  const Common::String &playerName,
@@ -773,11 +774,16 @@ Common::String EEMEngine::parseString(const Common::String &raw,
 			out += isJake ? "his" : "her";
 			break;
 		case 0x86:
+			// Player pronoun (passport gender), mirror of 0x83.
+			out += _playerFemale ? "she" : "he";
+			break;
 		case 0x87:
+			// Player pronoun (passport gender), mirror of 0x84.
+			out += _playerFemale ? "her" : "him";
+			break;
 		case 0x88:
-			// Stubbed suspect-gender pronouns; the DOS flag they branch
-			// on is never written and no mystery msg uses these bytes
-			// (see jumptable comment above).
+			// Player pronoun (passport gender), mirror of 0x85.
+			out += _playerFemale ? "her" : "his";
 			break;
 		case 0x89:
 			// KD hint placeholder (caller handles before this point).
diff --git a/engines/eem/eem.cpp b/engines/eem/eem.cpp
index 2b4ad77c7a7..660c2ffe547 100644
--- a/engines/eem/eem.cpp
+++ b/engines/eem/eem.cpp
@@ -1484,6 +1484,10 @@ void EEMEngine::showLondonCharSelect() {
 	// record only when `_LoadPlayerRecord` failed, then immediately saves it.
 	_playerName = displayName.empty() ? "Detective" : displayName;
 	_chainStage = 1;
+	// `_NewPlayer @ 1cd3:0f27` stores the passport gender pick (`DAT_3036_4c4c`).
+	// Drives the player pronouns 0x86-0x88 in `parseString`. (Existing profiles
+	// take the gender from their save instead, via the load path above.)
+	_playerFemale = female;
 	_voiceOn = true;
 	if (_audio)
 		_audio->setVoiceEnabled(_voiceOn);
@@ -1666,6 +1670,10 @@ Common::Error EEMEngine::saveGameStream(Common::WriteStream *stream,
 	s.syncAsByte(_chainStage);
 	s.syncAsByte(_voiceOn);
 
+	// London passport gender (player pronouns 0x86-0x88).
+	byte playerFemale = _playerFemale ? 1 : 0;
+	s.syncAsByte(playerFemale);
+
 	// Mid-case resume: persist in-progress mystery (no equivalent in
 	// _LoadGame @ 2404:0dc7).
 	bool hasMystery = _mystery.isLoaded();
@@ -1706,6 +1714,11 @@ Common::Error EEMEngine::loadGameStream(Common::SeekableReadStream *stream) {
 	if (_audio)
 		_audio->setVoiceEnabled(_voiceOn);
 
+	// London passport gender (player pronouns 0x86-0x88).
+	byte playerFemale = 0;
+	s.syncAsByte(playerFemale);
+	_playerFemale = (playerFemale != 0);
+
 	bool hasMystery = false;
 	s.syncAsByte(hasMystery);
 	if (hasMystery) {
diff --git a/engines/eem/eem.h b/engines/eem/eem.h
index 8bc4136c4a4..162a041f1c4 100644
--- a/engines/eem/eem.h
+++ b/engines/eem/eem.h
@@ -551,6 +551,13 @@ private:
 
 	Common::String _playerName;  ///< Substituted into 0x80 placeholders.
 
+	/// London passport gender (EEM2 `_NewPlayer @ 1cd3:0f27` gender pick,
+	/// `DAT_3036_4c4c`: left/0 = male, right/1 = female). Drives the player
+	/// pronoun opcodes 0x86/0x87/0x88 in `parseString` (he??him??his / she??her??
+	/// her). EEM1 has no passport, so it stays false (male) ??? matching that
+	/// engine's never-written gender flag.
+	bool _playerFemale = false;
+
 	/// `_PlayerRecord.SolvedMysteries[55]`. 0=unsolved, 1=solved, 2=first-try.
 	uint8 _mysteriesSolved[55] = {};
 


Commit: 076d0f0633ade0b876e9a7851fe427f08340d15b
    https://github.com/scummvm/scummvm/commit/076d0f0633ade0b876e9a7851fe427f08340d15b
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:21:28+02:00

Commit Message:
EEM: improved TRAVIS UI in London

Changed paths:
    engines/eem/mystery.cpp
    engines/eem/mystery.h
    engines/eem/ui.cpp


diff --git a/engines/eem/mystery.cpp b/engines/eem/mystery.cpp
index 3469a5d350e..edf8261849f 100644
--- a/engines/eem/mystery.cpp
+++ b/engines/eem/mystery.cpp
@@ -402,6 +402,12 @@ uint16 Mystery::noteIndexCount() const {
 	return (uint16)((_galleryOffset - _noteOffset) / stride);
 }
 
+uint Mystery::noteSectionSize() const {
+	if (!isLoaded() || _galleryOffset <= _noteOffset)
+		return 0;
+	return _galleryOffset - _noteOffset;
+}
+
 bool Mystery::noteHasNotebookText(uint clueId) const {
 	const byte *ni = noteIndex();
 	const uint16 cnt = noteIndexCount();
diff --git a/engines/eem/mystery.h b/engines/eem/mystery.h
index 32c22d7f806..54830f70e50 100644
--- a/engines/eem/mystery.h
+++ b/engines/eem/mystery.h
@@ -83,11 +83,19 @@ public:
 	/// u8 nameLen, nameLen bytes of name.
 	const byte *floppySuspectEntry(uint suspectIdx) const;
 
-	/// NoteIndex array (4 bytes per entry: u16 textOff + u16 pts).
+	/// NoteIndex array. EEM1 CD: 4 bytes/entry (u16 textOff + u16 pts).
+	/// EEM2/London CD: 2 bytes/entry (u16 textOff only ??? no points field;
+	/// `_DrawNotes @ 16a0:01de` reads `noteIndex[clueId*2]`). Floppy: 7
+	/// bytes/entry.
 	const byte *noteIndex() const;
 
 	uint16 noteIndexCount() const;
 
+	/// Raw byte size of the CD NoteIndex section [_noteOffset, _galleryOffset).
+	/// The notebook divides this by the variant stride (EEM1 4 / London 2) to
+	/// get the clue count, since `noteIndexCount()` assumes the 4-byte stride.
+	uint noteSectionSize() const;
+
 	/// True when clueId has a notebook text entry. Floppy dialog records
 	/// may be spoken-only with zero notebook offset, skipped by _DrawNotes_Floppy.
 	bool noteHasNotebookText(uint clueId) const;
diff --git a/engines/eem/ui.cpp b/engines/eem/ui.cpp
index 2613a680aee..ed11bb57e91 100644
--- a/engines/eem/ui.cpp
+++ b/engines/eem/ui.cpp
@@ -271,6 +271,10 @@ constexpr Common::Rect kPdaAccuseRect(Common::Point(180, 174), 21, 16);
 constexpr Common::Rect kPdaPageNextRect(Common::Point(204, 174), 20, 16);
 constexpr Common::Rect kPdaPagePrevRect(Common::Point(226, 174), 21, 16);
 constexpr Common::Rect kPdaHelp2Rect(Common::Point(267, 174), 21, 16);
+// London-only: `_NoteButtons[9]` = (0,0,66,79) ??? site (EEM1's slot 9 is a
+// dead 0-rect). A secondary "close the PDA" hotspot over the device's
+// top-left corner. EEM2 `_HandleNoteButton` slot 9 ??? screen 3.
+constexpr Common::Rect kPdaLondonCloseRect(Common::Point(0, 0), 66, 79);
 
 constexpr uint16 kProfilePickerRevealPic = 0x105;
 constexpr int kProfilePickerRevealX = 0x3e;
@@ -2257,6 +2261,21 @@ void EEMEngine::doNotebook() {
 	//   [9] (  0,  0,  0,  0)  same exit as [8]
 	//   [10] (267,174,288,190) ??? `_InterfaceHelp(0)` again          (0x3f9)
 	// BG PIC 0x3f; partner ANI 1 (Jake) / 0xb (Jenny) at (5, 80).
+	//
+	// EEM2/London (`_DoNotebook @ 16a0:0517`, `_HandleNoteButton @ 16a0:03dd`,
+	// jumptable @ 16a0:0503) reuses the SAME button rect table (2bca:0151) and
+	// handler set, but reassigns two slots and revives slot 9 (verified by
+	// disassembly ??? `[0x9292]` is the next-screen code):
+	//   [1] ( 93,174,115,190) MAP  ??? screen 2  (EEM1: a 2nd InterfaceHelp).
+	//                         London's dedicated map button.
+	//   [7] (  7,177, 57,200) DOS EEM2 ??? SITE, but we keep it as MAP (??? 2) in
+	//                         both variants ??? the EEM1 partner-foot map shortcut
+	//                         (a player convenience alongside button [1]).
+	//   [9] (  0,  0, 66, 79) SITE ??? screen 3  (EEM1: dead 0-rect)
+	// Everything else (gallery, accuse, host hint, page next/prev, help [10],
+	// site [8]) is identical. The note rendering, pagination, partner ANI
+	// (same 1/0xb), and gizmo colour-cycle are shared as-is; only button [1]
+	// (London ??? map) and the extra close area [9] are gated on `isLondon()`.
 	if (!_mystery.isLoaded() || !_font.isLoaded())
 		return;
 
@@ -2307,12 +2326,20 @@ void EEMEngine::doNotebook() {
 			}
 			if (ev.type == Common::EVENT_LBUTTONDOWN) {
 				// Earlier rects win on overlap (matches `_FindButton`).
-				if (kPdaSiteRect.contains(ev.mouse.x, ev.mouse.y)) {
+				if (kPdaSiteRect.contains(ev.mouse.x, ev.mouse.y) ||
+					(isLondon() &&
+					 kPdaLondonCloseRect.contains(ev.mouse.x, ev.mouse.y))) {
 					_nextScreen = kScreenSite;
 					exitFlag = true;
 					break;  // back to site
 				}
 				if (kPdaPartnerFootMapRect.contains(ev.mouse.x, ev.mouse.y)) {
+					// EEM1 `_NoteButtons[7]` ??? map (screen 2). DOS EEM2 reassigns
+					// this slot to SITE, but we intentionally keep the partner-
+					// foot hotspot as a map shortcut in BOTH variants for parity
+					// with EEM1. London also has its own dedicated map button
+					// (`kPdaHelpRect` below) and can still close the PDA via the
+					// site button (35,111) or the top-left corner (0,0,66,79).
 					_nextScreen = kScreenMapAlt;
 					exitFlag = true;
 					break;
@@ -2334,7 +2361,14 @@ void EEMEngine::doNotebook() {
 					break;
 				}
 				if (kPdaHelpRect.contains(ev.mouse.x, ev.mouse.y)) {
-					// rect 1 ??? `_InterfaceHelp(0)` (PICs 0x63 / 0x1ae).
+					// `_NoteButtons[1]`. EEM1: a 2nd `_InterfaceHelp(0)` button
+					// (PICs 0x63 / 0x1ae). London reassigns this slot to MAP
+					// (`_HandleNoteButton` slot 1 ??? screen 2).
+					if (isLondon()) {
+						_nextScreen = kScreenMapAlt;
+						exitFlag = true;
+						break;
+					}
 					setInteractiveMouseCursor(false);
 					doInterfaceHelp(0);
 					dirty = true;
@@ -2404,7 +2438,11 @@ Common::String EEMEngine::notebookNoteText(uint clueId, const byte *ni,
 		return parseString(Common::String(p, len),
 						   _playerName, _partner);
 	}
-	const uint16 textOff = READ_LE_UINT16(ni + clueId * 4);
+	// EEM1 CD: 4-byte entries (textOff at +0, points at +2). EEM2/London CD:
+	// 2-byte entries (textOff only) ??? `_DrawNotes @ 16a0:01de` reads
+	// `noteIndex[clueId*2]`.
+	const uint stride = isLondon() ? 2 : 4;
+	const uint16 textOff = READ_LE_UINT16(ni + clueId * stride);
 	return parseString(_mystery.textAt(textOff),
 					   _playerName, _partner);
 }
@@ -2428,13 +2466,24 @@ void EEMEngine::drawNotebookFrame(int &page) {
 
 	// `_DrawNotes` walks `_NoteIndex` for current page; word-wraps each
 	// found clue in `_NotebookRect`. Selected = color 0x3c.
+	// EEM2/London NoteIndex entries are 2 bytes (no points field), so its real
+	// clue count is the section size / 2. `noteIndexCount()` assumes the EEM1
+	// 4-byte stride (and stays that way for the accuse-scoring path, which is a
+	// separate London concern), so derive the London-correct count here ??? else
+	// high-id notes get dropped.
+	const uint16 londonCount = isLondon()
+		? (uint16)(_mystery.noteSectionSize() / 2) : 0;
 	Common::Array<uint> found;
 	for (uint i = 0; i < Mystery::kCluesFoundCap; i++) {
-		if (_mystery._cluesFound[i] && _mystery.noteHasNotebookText(i))
+		const bool hasText = isLondon()
+			? (i < londonCount)
+			: _mystery.noteHasNotebookText(i);
+		if (_mystery._cluesFound[i] && hasText)
 			found.push_back(i);
 	}
 	const byte *ni = _mystery.noteIndex();
-	const uint16 niCount = _mystery.noteIndexCount();
+	const uint16 niCount = isLondon()
+		? londonCount : _mystery.noteIndexCount();
 
 	const int kRectX = kNotebookRect.left;
 	const int kRectY = kNotebookRect.top;
@@ -2619,7 +2668,15 @@ void EEMEngine::doGallery() {
 					break;
 				}
 				if (kPdaHelpRect.contains(ev.mouse.x, ev.mouse.y)) {
-					// rect 1 ??? `_InterfaceHelp(0)` (158f:0625).
+					// EEM1 slot 1 ??? a 2nd `_InterfaceHelp(0)` (158f:0625).
+					// EEM2 gallery `_HandleGalleryButton @ 160e:05c7` slot 1 ???
+					// MAP (`MOV [0x9292],2` @ 160e:05fe) ??? London's dedicated
+					// map button, same as the notebook/accuse PDA bar.
+					if (isLondon()) {
+						_nextScreen = kScreenMapAlt;
+						exitFlag = true;
+						break;
+					}
 					setInteractiveMouseCursor(false);
 					doInterfaceHelp(0);
 					lastDraw = 0;
@@ -2877,6 +2934,14 @@ bool EEMEngine::moreInfo(const byte *gd, uint suspectIdx,
 						back = true;
 						break;
 					}
+					if (isLondon() && kPdaHelpRect.contains(mx, my)) {
+						// EEM2 gallery slot 1 ??? MAP (dedicated map button);
+						// only kPdaHelp2Rect (267,174) stays help in London.
+						_nextScreen = kScreenMapAlt;
+						exitGallery = true;
+						back = true;
+						break;
+					}
 					if (kPdaHelpRect.contains(mx, my) ||
 						kPdaHelp2Rect.contains(mx, my)) {
 						setInteractiveMouseCursor(false);
@@ -4011,6 +4076,13 @@ bool EEMEngine::doAccuseNotes() {
 					_nextScreen = kScreenGallery;
 					return false;
 				}
+				if (isLondon() && kPdaHelpRect.contains(mx, my)) {
+					// EEM2 accuse `_HandleAccuseNoteButton @ 1ea1:0873` slot 1
+					// ??? MAP (`MOV [0x9292],2` @ 1ea1:089b) ??? London's dedicated
+					// map button; only kPdaHelp2Rect (267,174) stays help.
+					_nextScreen = kScreenMapAlt;
+					return false;
+				}
 				if (kPdaHelpRect.contains(mx, my) ||
 					kPdaHelp2Rect.contains(mx, my)) {
 					setInteractiveMouseCursor(false);


Commit: 1f0ad1bf77d4d333c2bf3d8272a2a1f829e458db
    https://github.com/scummvm/scummvm/commit/1f0ad1bf77d4d333c2bf3d8272a2a1f829e458db
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:21:28+02:00

Commit Message:
EEM: improved hints voices/text in London

Changed paths:
    engines/eem/audio.cpp
    engines/eem/audio.h
    engines/eem/graphics.cpp
    engines/eem/mystery.h


diff --git a/engines/eem/audio.cpp b/engines/eem/audio.cpp
index 1a553b47c00..14570379b84 100644
--- a/engines/eem/audio.cpp
+++ b/engines/eem/audio.cpp
@@ -346,19 +346,37 @@ void AudioPlayer::stopSpool() {
 		_mixer->stopHandle(_spoolHandle);
 }
 
-// _SayKDDigital @ 2404:0fbc.
-//   slot = kdspeak * 2 + (partner == Jake ? 1 : 0); sound = digital[slot+1] - 1
-// KDDigitalIndex = KDTextIndex + 0x12 (set by _ReadMystery @ 2404:0163-0167).
+// _SayKDDigital @ EEM1 2404:0fbc / EEM2 2542:1845. KDDigitalIndex =
+// KDTextIndex + 0x12. EEM1 reads index `kdspeak*2 + (Jake?1:0) + 1`; EEM2
+// drops the trailing +1 (`kdspeak*2 + (Jake?1:0)`) ??? a real per-variant
+// difference, so using EEM1's +1 for London shifts every KD line by one slot.
 void AudioPlayer::sayKDDigital(const byte *kdTextIndex, uint kdspeak,
 							   uint partner) {
 	if (!kdTextIndex || _currentMystery < 0)
 		return;
 	const byte *digital = kdTextIndex + 0x12;
-	const uint slot = (kdspeak * 2) + (partner == 0 ? 1u : 0u) + 1u;
+	const uint slot = (kdspeak * 2) + (partner == 0 ? 1u : 0u) +
+					  (_vm && _vm->isLondon() ? 0u : 1u);
 	const uint16 raw = READ_LE_UINT16(digital + slot * 2);
 	if (raw == 0 || raw == 0xFFFF)
 		return;
 	spoolSound((uint)(raw - 1));
 }
 
+// _SayKDHintDigital @ 2542:187e ??? EEM2/London partner chain-hint voice.
+// Identical to EEM2 `_SayKDDigital` but indexes the table 0x3a bytes after
+// KDTextIndex (vs 0x12); `slot` is the 0..4 hint-chain slot. EEM1 voices its
+// chain hints through `sayKDDigital(slot + 10)` instead.
+void AudioPlayer::sayKDHintDigital(const byte *kdTextIndex, uint slot,
+								   uint partner) {
+	if (!kdTextIndex || _currentMystery < 0)
+		return;
+	const byte *table = kdTextIndex + 0x3a;
+	const uint idx = (slot * 2) + (partner == 0 ? 1u : 0u);
+	const uint16 raw = READ_LE_UINT16(table + idx * 2);
+	if (raw == 0 || raw == 0xFFFF)
+		return;
+	spoolSound((uint)(raw - 1));
+}
+
 } // End of namespace EEM
diff --git a/engines/eem/audio.h b/engines/eem/audio.h
index 1d986ac47bb..fa901b971a8 100644
--- a/engines/eem/audio.h
+++ b/engines/eem/audio.h
@@ -112,6 +112,11 @@ public:
 	/// header slot). Pass the mystery's kdTextIndex() pointer.
 	void sayKDDigital(const byte *kdTextIndex, uint kdspeak, uint partner);
 
+	/// `_SayKDHintDigital @ 2542:187e` ??? EEM2/London partner chain-hint voice.
+	/// Like `sayKDDigital` but indexes the table 0x3a after KDTextIndex (vs
+	/// 0x12) with no +1 bias. @p slot is the 0..4 hint-chain slot.
+	void sayKDHintDigital(const byte *kdTextIndex, uint slot, uint partner);
+
 	/// _QuitSounds @ 1ff1:03c5.
 	void stopAll();
 
diff --git a/engines/eem/graphics.cpp b/engines/eem/graphics.cpp
index 81c553eb35f..c6bd5278563 100644
--- a/engines/eem/graphics.cpp
+++ b/engines/eem/graphics.cpp
@@ -350,23 +350,38 @@ void EEMEngine::doHelp() {
 		return;
 
 	uint16 chosenText = 0xFFFF;
-	int    soundNum   = 0;
+	int    soundNum   = 0;       // _SayKDDigital line: EEM1 chain 10/11; fallback 7/8.
+	int    hintVoiceSlot = -1;   // London chain hint ??? _SayKDHintDigital(slot).
 	bool   anyHintDefined = false;
 
+	// Required-clue chain walk. EEM1 `_KDHelp @ 1560:010a` checks the first two
+	// entries of chain A. EEM2 `_DoKDHelp @ 15c1:020b` walks all THREE chains
+	// (A,B,C @ header words 16-20/21-25/26-30) ?? 5 slots, indexes the 15-entry
+	// hint-text table `hintBlock()[chain*5 + slot]`, and voices a chain hint
+	// with `_SayKDHintDigital(slot)` (table kdTextIndex()+0x3a) instead of
+	// EEM1's `_SayKDDigital(slot + 10)`. Gate is the same: clue not yet found.
+	const uint kChains = isLondon() ? 3u : 1u;
+	const uint kSlots  = isLondon() ? Mystery::kChainLen : 2u;
 	if (hb) {
-		for (uint i = 0; i < 2; i++) {
-			const uint16 chainClue = _mystery.aChain(i);
-			if (chainClue == 0xFFFF)
-				continue;
-			const uint16 hintOff = READ_LE_UINT16(hb + i * 2);
-			if (hintOff == 0xFFFF)
-				continue;
-			anyHintDefined = true;
-			if (chainClue < Mystery::kCluesFoundCap &&
-				_mystery._cluesFound[chainClue] == 0) {
-				chosenText = hintOff;
-				soundNum   = (int)i + 10;
-				break;
+		for (uint c = 0; c < kChains && chosenText == 0xFFFF; c++) {
+			for (uint slot = 0; slot < kSlots; slot++) {
+				const uint16 chainClue = _mystery.hintChain(c, slot);
+				if (chainClue == 0xFFFF)
+					continue;
+				const uint16 hintOff = READ_LE_UINT16(
+					hb + (c * Mystery::kChainLen + slot) * 2);
+				if (hintOff == 0xFFFF)
+					continue;
+				anyHintDefined = true;
+				if (chainClue < Mystery::kCluesFoundCap &&
+					_mystery._cluesFound[chainClue] == 0) {
+					chosenText = hintOff;
+					if (isLondon())
+						hintVoiceSlot = (int)slot;
+					else
+						soundNum = (int)slot + 10;
+					break;
+				}
 			}
 		}
 	}
@@ -463,9 +478,14 @@ void EEMEngine::doHelp() {
 	// partner-specific voice line keyed to which hint type fired:
 	//   10 = first chain hint, 11 = second chain hint,
 	//    7 = generic KD (first), 8 = generic KD (second).
-	if (_audio && _mystery.kdTextIndex() && soundNum > 0)
-		_audio->sayKDDigital(_mystery.kdTextIndex(), (uint)soundNum,
-							 _partner);
+	if (_audio && _mystery.kdTextIndex()) {
+		if (hintVoiceSlot >= 0)
+			_audio->sayKDHintDigital(_mystery.kdTextIndex(),
+									 (uint)hintVoiceSlot, _partner);
+		else if (soundNum > 0)
+			_audio->sayKDDigital(_mystery.kdTextIndex(), (uint)soundNum,
+								 _partner);
+	}
 
 	waitForInput(60000);
 }
diff --git a/engines/eem/mystery.h b/engines/eem/mystery.h
index 54830f70e50..1536a61360f 100644
--- a/engines/eem/mystery.h
+++ b/engines/eem/mystery.h
@@ -113,6 +113,20 @@ public:
 		return i < kChainLen ? _aChain[i] : 0xFFFF;
 	}
 
+	/// Entry @p slot of hint chain @p chainIdx (0 = A, 1 = B, 2 = C; header
+	/// words 16-20 / 21-25 / 26-30). EEM2 `_DoKDHelp @ 15c1:020b` walks all
+	/// three chains ?? 5 slots; EEM1 `_KDHelp` only chain A slots 0..1.
+	uint16 hintChain(uint chainIdx, uint slot) const {
+		if (slot >= kChainLen)
+			return 0xFFFF;
+		switch (chainIdx) {
+		case 0: return _aChain[slot];
+		case 1: return _bChain[slot];
+		case 2: return _cChain[slot];
+		default: return 0xFFFF;
+		}
+	}
+
 	/// MapData entry for siteNum: 14 bytes; first u16 = sitepic, +4..7 = (x, y).
 	const byte *mapEntry(uint siteNum) const;
 


Commit: d7a7f626717147457245f120a62262b1fff064fc
    https://github.com/scummvm/scummvm/commit/d7a7f626717147457245f120a62262b1fff064fc
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:21:28+02:00

Commit Message:
EEM: new doPuzzle implementation for London

Changed paths:
    engines/eem/clues.cpp
    engines/eem/eem.h
    engines/eem/graphics.cpp


diff --git a/engines/eem/clues.cpp b/engines/eem/clues.cpp
index 0abed334700..e3c589691ce 100644
--- a/engines/eem/clues.cpp
+++ b/engines/eem/clues.cpp
@@ -1130,6 +1130,26 @@ void EEMEngine::displayClue(const byte *clueBlock) {
 		}
 
 		applyClueSideEffects(c);
+
+		// EEM2 `_DisplayClue @ 2542:05bd`: a clue entry can gate the rest of
+		// the clue behind a "check the manual / a real map" puzzle (the id is
+		// entry+0x54 = c+0x50, a `P<id>.BIN`). Run it once per mystery
+		// (`_mystery._solvedPuzzle` = `DAT_3036_6d5c`); a wrong answer aborts
+		// the remaining entries and re-prompts on the next visit.
+		if (isLondon() && !_mystery._solvedPuzzle) {
+			const uint16 puzzleId = READ_LE_UINT16(c + 0x50);
+			if (puzzleId != 0xFFFF) {
+				// Restore the clean site BG (drop this clue's bubble) first ???
+				// the DOS _Repaint before `_DoPuzzle` ??? so the puzzle (and its
+				// own hint) render on a clean background, not over the bubble.
+				g_system->copyRectToScreen(bg.getPixels(), bg.pitch, 0, 0,
+										   kScreenWidth, kScreenHeight);
+				g_system->updateScreen();
+				_mystery._solvedPuzzle = doPuzzle(puzzleId);
+				if (!_mystery._solvedPuzzle)
+					break;  // gate: block the rest of this clue
+			}
+		}
 	}
 
 	// _StopTheVoice @ 1ff1:0283 effect (voice only, keep MIDI). Diverges
diff --git a/engines/eem/eem.h b/engines/eem/eem.h
index 162a041f1c4..524c5d1e497 100644
--- a/engines/eem/eem.h
+++ b/engines/eem/eem.h
@@ -208,6 +208,15 @@ public:
 	/// count followed by 62-byte ClueEntries.
 	void displayClue(const byte *clueBlock);
 
+	/// EEM2/London `_DoPuzzle @ 2542:1482`. A clue entry can gate the rest of
+	/// itself behind a "check the manual / a real map" puzzle (the id is the
+	/// entry+0x54 field). Opens `P<id>.BIN` ??? a header + question, then either
+	/// a typed answer (type 0) or a click-a-region multiple choice (type 1).
+	/// Returns true when answered correctly (and true if the file is missing,
+	/// so the clue is never permanently blocked); a wrong answer replays the
+	/// partner's scolding hint, and the caller re-prompts on the next visit.
+	bool doPuzzle(uint puzzleId);
+
 	/// Floppy hotspot click. `FUN_22dc_0b80 + FUN_1652_00e6 + FUN_1652_006c`.
 	/// Locates dialog records in site_data[+6] and dispatches them.
 	void displayFloppyHotspotDialog(uint siteNum, uint hotIdx);
diff --git a/engines/eem/graphics.cpp b/engines/eem/graphics.cpp
index c6bd5278563..02ed69a8ef7 100644
--- a/engines/eem/graphics.cpp
+++ b/engines/eem/graphics.cpp
@@ -170,6 +170,250 @@ bool EEMEngine::floppyHotspotSearched(uint siteIdx, uint hotspotIdx) const {
 		   _mystery._cluesFound[textIdx] != 0;
 }
 
+// fgets-style line read (until newline / NUL / EOF) from a puzzle file.
+static Common::String readPuzzleLine(Common::File &f) {
+	Common::String s;
+	while (!f.eos()) {
+		const byte c = f.readByte();
+		if (f.eos() || c == '\n' || c == 0)
+			break;
+		if (c != '\r')
+			s += (char)c;
+	}
+	return s;
+}
+
+bool EEMEngine::doPuzzle(uint puzzleId) {
+	// `_DoPuzzle @ 2542:1482`. File `P<id>.BIN` (LE u16 fields):
+	//   type(0=typed,1=choice); mainPic{id,x,y}; extraCount; extra{id,x,y}*;
+	//   qx; qy; qw; voiceAlt(Jenny); voiceMain(Jake); question line.
+	//   type 0: answerRect{x1,y1,x2,y2}; answer line (stored UPPERCASE).
+	//   type 1: choiceCount; rects{x1,y1,x2,y2}* ??? correct = rect 0.
+	Common::File f;
+	const Common::String fname = Common::String::format("P%u.BIN", puzzleId);
+	if (!f.open(Common::Path(fname))) {
+		// Fail open: never let a missing puzzle file permanently block a clue.
+		warning("doPuzzle: %s missing ??? leaving the clue ungated", fname.c_str());
+		return true;
+	}
+
+	const uint16 type = f.readUint16LE();
+
+	// The caller (displayClue) restored the clean site background, so the
+	// current screen has no clue bubbles. Keep that as `cleanBg` and build the
+	// puzzle on a copy; `cleanBg` is restored after the answer (DOS `_DoPuzzle`
+	// _Repaint) and after the wrong-answer hint (`_KDHelp` _Repaint) so neither
+	// the puzzle pics nor any bubble is left on the background.
+	Graphics::ManagedSurface cleanBg(kScreenWidth, kScreenHeight,
+		Graphics::PixelFormat::createFormatCLUT8());
+	cleanBg.clear();
+	{
+		Graphics::Surface *cur = g_system->lockScreen();
+		if (cur) {
+			cleanBg.simpleBlitFrom(*cur);
+			g_system->unlockScreen();
+		}
+	}
+	Graphics::ManagedSurface scratch(kScreenWidth, kScreenHeight,
+		Graphics::PixelFormat::createFormatCLUT8());
+	scratch.simpleBlitFrom(cleanBg);
+
+	// Main picture, then `extraCount` more ??? each {id, x, y}, masked blit.
+	for (int phase = 0; phase < 2; phase++) {
+		uint16 n = 1;
+		if (phase == 1)
+			n = f.readUint16LE();
+		for (uint16 i = 0; i < n; i++) {
+			const uint16 id = f.readUint16LE();
+			const int16 px = (int16)f.readUint16LE();
+			const int16 py = (int16)f.readUint16LE();
+			Picture pic;
+			if (_picsArchive.getPicture(id, pic) && !pic.surface.empty())
+				scratch.transBlitFrom(pic.surface, Common::Point(px, py),
+									  (uint32)(byte)(pic.flags >> 8));
+		}
+	}
+
+	const int16 qx = (int16)f.readUint16LE();
+	const int16 qy = (int16)f.readUint16LE();
+	const int16 qw = (int16)f.readUint16LE();
+	const uint16 voiceAlt  = f.readUint16LE();  // partner != Jake
+	const uint16 voiceMain = f.readUint16LE();  // partner == Jake
+	const Common::String question =
+		parseString(readPuzzleLine(f), _playerName, _partner);
+	if (_font.isLoaded() && !question.empty())
+		_font.drawWordWrapped(&scratch, qx, qy, MAX<int>(8, (int)qw),
+							  question, 0);
+	g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
+							   0, 0, kScreenWidth, kScreenHeight);
+	g_system->updateScreen();
+
+	// Partner reads the question (`_SpoolSound(id - 1)`, gated on audio).
+	if (_audio && _voiceOn) {
+		const uint16 v = (_partner == kPartnerJake) ? voiceMain : voiceAlt;
+		if (v != 0 && v != 0xFFFF)
+			_audio->spoolSound((uint)(v - 1));
+	}
+
+	bool correct = false;
+	CursorMan.showMouse(true);
+
+	if (type == 0) {
+		// Typed answer (`_CheckTypedAnswer` ??? `_GetNameString`, toupper+strcmp).
+		const int16 ax1 = (int16)f.readUint16LE();
+		const int16 ay1 = (int16)f.readUint16LE();
+		const int16 ax2 = (int16)f.readUint16LE();
+		const int16 ay2 = (int16)f.readUint16LE();
+		const Common::Rect rect(ax1, ay1, ax2, ay2);
+		Common::String answer = readPuzzleLine(f);  // stored uppercase
+		const uint maxLen = answer.size() + 2;
+
+		Common::String input;
+		bool done = false, blink = true;
+		uint32 blinkMs = g_system->getMillis();
+		g_system->setFeatureState(OSystem::kFeatureVirtualKeyboard, true);
+		while (!done && !shouldQuit()) {
+			Graphics::ManagedSurface fld(kScreenWidth, kScreenHeight,
+				Graphics::PixelFormat::createFormatCLUT8());
+			fld.simpleBlitFrom(scratch);
+			Common::String shown = input;
+			if (blink)
+				shown += "_";
+			if (_font.isLoaded())
+				_font.drawString(&fld, shown, rect.left + 2, rect.top + 1,
+								 MAX<int>(8, rect.width()), 0x0F);
+			g_system->copyRectToScreen(fld.getPixels(), fld.pitch, 0, 0,
+									   kScreenWidth, kScreenHeight);
+			g_system->updateScreen();
+
+			Common::Event ev;
+			while (g_system->getEventManager()->pollEvent(ev)) {
+				if (ev.type == Common::EVENT_QUIT ||
+					ev.type == Common::EVENT_RETURN_TO_LAUNCHER) {
+					input.clear();
+					done = true;
+					break;
+				}
+				if (ev.type != Common::EVENT_KEYDOWN)
+					continue;
+				const Common::KeyCode k = ev.kbd.keycode;
+				if (k == Common::KEYCODE_RETURN || k == Common::KEYCODE_KP_ENTER) {
+					if (!input.empty())
+						done = true;
+				} else if (k == Common::KEYCODE_ESCAPE) {
+					input.clear();  // cancel ??? empty ??? fails the compare
+					done = true;
+				} else if (k == Common::KEYCODE_BACKSPACE) {
+					if (!input.empty())
+						input.deleteLastChar();
+				} else if (ev.kbd.ascii >= ' ' && ev.kbd.ascii < 127 &&
+						   input.size() < maxLen) {
+					input += (char)ev.kbd.ascii;
+				}
+			}
+			const uint32 now = g_system->getMillis();
+			if (now - blinkMs >= 400) {
+				blink = !blink;
+				blinkMs = now;
+			}
+			g_system->delayMillis(15);
+		}
+		g_system->setFeatureState(OSystem::kFeatureVirtualKeyboard, false);
+		input.toUppercase();
+		correct = input.equals(answer);
+	} else {
+		// Multiple choice: click a region; correct = the FIRST rect.
+		const uint16 count = f.readUint16LE();
+		Common::Array<Common::Rect> rects;
+		for (uint16 i = 0; i < count; i++) {
+			const int16 cx1 = (int16)f.readUint16LE();
+			const int16 cy1 = (int16)f.readUint16LE();
+			const int16 cx2 = (int16)f.readUint16LE();
+			const int16 cy2 = (int16)f.readUint16LE();
+			rects.push_back(Common::Rect(cx1, cy1, cx2, cy2));
+		}
+		int picked = -1;
+		while (picked == -1 && !shouldQuit()) {
+			Common::Event ev;
+			while (g_system->getEventManager()->pollEvent(ev)) {
+				if (ev.type == Common::EVENT_QUIT ||
+					ev.type == Common::EVENT_RETURN_TO_LAUNCHER ||
+					(ev.type == Common::EVENT_KEYDOWN &&
+					 ev.kbd.keycode == Common::KEYCODE_ESCAPE)) {
+					picked = -2;  // ESC / quit ??? wrong
+					break;
+				}
+				if (ev.type == Common::EVENT_LBUTTONDOWN) {
+					for (uint i = 0; i < rects.size(); i++) {
+						if (rects[i].contains(ev.mouse.x, ev.mouse.y)) {
+							picked = (int)i;
+							break;
+						}
+					}
+				}
+			}
+			g_system->updateScreen();
+			g_system->delayMillis(15);
+		}
+		correct = (picked == 0);
+	}
+	f.close();
+
+	// _Repaint after the answer: drop the puzzle pics back to the clean site.
+	g_system->copyRectToScreen(cleanBg.getPixels(), cleanBg.pitch, 0, 0,
+							   kScreenWidth, kScreenHeight);
+	g_system->updateScreen();
+
+	if (!correct && !shouldQuit()) {
+		// Wrong: partner scolds via the KD hint (`_MIDIPlay(0x28)` +
+		// `_KDHelp(TextBlock + KDTextIndex[+0xc], voice 6)`), drawn on the
+		// clean site (the puzzle has already been _Repaint-ed away).
+		const byte *kd = _mystery.kdTextIndex();
+		const uint16 hintOff = kd ? READ_LE_UINT16(kd + 0x0c) : 0xFFFF;
+		if (hintOff != 0xFFFF) {
+			Common::String hint =
+				parseString(_mystery.textAt(hintOff), _playerName, _partner);
+			Graphics::ManagedSurface ms(kScreenWidth, kScreenHeight,
+				Graphics::PixelFormat::createFormatCLUT8());
+			ms.simpleBlitFrom(cleanBg);
+			const byte firstChar = hint.empty() ? (byte)0 : (byte)hint[0];
+			uint16 bubNum = getKDTextBalloon(firstChar);
+			if (firstChar >= '0' && firstChar <= '9')
+				hint.deleteChar(0);
+			bubNum = fitBalloonToText(bubNum, hint);
+			Picture balloon;
+			const bool haveBalloon = _balloonArchive.size() > (bubNum & 0x7F) &&
+				_balloonArchive.loadEntry(bubNum & 0x7F, balloon);
+			const int balloonX = 0x21;
+			int balloonY = 1;
+			if (haveBalloon && balloon.surface.h < 0x4e)
+				balloonY = (0x50 - balloon.surface.h) / 2;
+			if (haveBalloon)
+				ms.transBlitFrom(balloon.surface,
+								 Common::Point(balloonX, balloonY),
+								 (uint32)(byte)(balloon.flags >> 8));
+			uint16 tx = 5, ty = 4, tw = 155;
+			getBalloonInsets(bubNum, tx, ty, tw);
+			if (_font.isLoaded())
+				_font.drawWordWrapped(&ms, balloonX + tx, balloonY + ty, tw,
+									  hint, haveBalloon ? 0 : 0xF);
+			g_system->copyRectToScreen(ms.getPixels(), ms.pitch, 0, 0,
+									   kScreenWidth, kScreenHeight);
+			g_system->updateScreen();
+			if (_audio && _voiceOn && kd)
+				_audio->sayKDDigital(kd, 6, _partner);
+			waitForInput(60000);
+
+			// `_KDHelp` ends with _Repaint: clear the hint balloon too.
+			g_system->copyRectToScreen(cleanBg.getPixels(), cleanBg.pitch,
+									   0, 0, kScreenWidth, kScreenHeight);
+			g_system->updateScreen();
+		}
+	}
+	setInteractiveMouseCursor(false);
+	return correct;
+}
+
 void EEMEngine::doHelp() {
 	// Floppy per-mystery H<n>.BIN hint files. Loader FUN_1503_0001 @ 1503:0001
 	// (format string "h%d.bin" @ 2608:0154), consumer FUN_1503_01a5 @ 1503:01a5.


Commit: a029ae2241c3e5b70231e488f8a5fc66452724c3
    https://github.com/scummvm/scummvm/commit/a029ae2241c3e5b70231e488f8a5fc66452724c3
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:21:29+02:00

Commit Message:
EEM: doPuzzle highlights for London

Changed paths:
    engines/eem/graphics.cpp
    engines/eem/site.cpp
    engines/eem/site.h


diff --git a/engines/eem/graphics.cpp b/engines/eem/graphics.cpp
index 02ed69a8ef7..ec1f5d089ce 100644
--- a/engines/eem/graphics.cpp
+++ b/engines/eem/graphics.cpp
@@ -34,6 +34,7 @@
 #include "eem/audio.h"
 #include "eem/detection.h"
 #include "eem/eem.h"
+#include "eem/site.h"
 
 namespace EEM {
 
@@ -332,8 +333,55 @@ bool EEMEngine::doPuzzle(uint puzzleId) {
 			const int16 cy2 = (int16)f.readUint16LE();
 			rects.push_back(Common::Rect(cx1, cy1, cx2, cy2));
 		}
+		// `_GetPuzzleChoice @ 2542:11a9`: the choice rects are the only
+		// clickable areas (no done/exit hotspot) and ESC = wrong; the Tab/arrow
+		// keys hit a global handler, not choice selection. Highlight the
+		// options two ways, both reusing the existing engine mechanisms:
+		//   * Boxes: outline each option in the marching-ants ramp ??? palette
+		//     0xF9..0xFE, the SAME original yellow as the site hotspots, not a
+		//     puzzle-specific colour. Gated like the DOS `_DrawRect`
+		//     (`DAT_3036_4c4a` ??? port `hide_highlight_boxes`).
+		//   * Cursor: recolor the default arrow over an option
+		//     (`setInteractiveMouseCursor`, the EEM1 red-pixel cursor for
+		//     "otherwise invisible" hotspots). These options carry no per-hotspot
+		//     cursor shape, so the default cursor is what's shown ??? exactly the
+		//     case that recolor is meant for.
+		applyHotspotGlowPalette();
+		const bool showBoxes = !ConfMan.getBool("hide_highlight_boxes");
 		int picked = -1;
+		uint32 lastPhase = (uint32)-1;
+		bool overOption = false;
 		while (picked == -1 && !shouldQuit()) {
+			if (showBoxes) {
+				const uint32 phase = g_system->getMillis() / 80;
+				if (phase != lastPhase) {
+					lastPhase = phase;
+					Graphics::ManagedSurface fr(kScreenWidth, kScreenHeight,
+						Graphics::PixelFormat::createFormatCLUT8());
+					fr.simpleBlitFrom(scratch);
+					for (uint i = 0; i < rects.size(); i++) {
+						const byte color =
+							(byte)(0xF9 + ((i + phase) & 0x07) % 6);
+						fr.frameRect(rects[i], color);
+					}
+					g_system->copyRectToScreen(fr.getPixels(), fr.pitch, 0, 0,
+											   kScreenWidth, kScreenHeight);
+				}
+			}
+			// Red default-cursor while hovering a clickable option; plain arrow
+			// off all of them (mirrors `updateHotspotCursor`).
+			const Common::Point mp = g_system->getEventManager()->getMousePos();
+			bool nowOver = false;
+			for (uint i = 0; i < rects.size(); i++) {
+				if (rects[i].contains(mp.x, mp.y)) {
+					nowOver = true;
+					break;
+				}
+			}
+			if (nowOver != overOption) {
+				overOption = nowOver;
+				setInteractiveMouseCursor(nowOver);
+			}
 			Common::Event ev;
 			while (g_system->getEventManager()->pollEvent(ev)) {
 				if (ev.type == Common::EVENT_QUIT ||
@@ -355,6 +403,8 @@ bool EEMEngine::doPuzzle(uint puzzleId) {
 			g_system->updateScreen();
 			g_system->delayMillis(15);
 		}
+		// Restore the plain arrow before leaving the puzzle.
+		setInteractiveMouseCursor(false);
 		correct = (picked == 0);
 	}
 	f.close();
diff --git a/engines/eem/site.cpp b/engines/eem/site.cpp
index 5dba04a5f01..7c4884ac7d6 100644
--- a/engines/eem/site.cpp
+++ b/engines/eem/site.cpp
@@ -179,6 +179,23 @@ void cyclePaletteRangeReverse(uint8 start, uint8 end) {
 	g_system->getPaletteManager()->setPalette(buf, start, count);
 }
 
+void applyHotspotGlowPalette() {
+	// SITEPALS ships palette 0xF9..0xFE as uniform yellow (3F 3E 00), so the
+	// original marching-ants was a placeholder. Override with a 6-step yellow
+	// ramp so `cyclePaletteRange(0xF9, 0xFE)` (and the per-index draw colour)
+	// produce a visible pulse. Shared by site hotspots and the clue puzzle so
+	// both highlight clickable areas with the same (original) colours.
+	static const byte kAntsGlow[6 * 3] = {
+		0x40, 0x40, 0x00, // F9 ??? dim
+		0x80, 0x80, 0x00, // FA
+		0xC0, 0xC0, 0x00, // FB
+		0xFF, 0xFF, 0x40, // FC ??? peak
+		0xC0, 0xC0, 0x00, // FD
+		0x80, 0x80, 0x00, // FE
+	};
+	g_system->getPaletteManager()->setPalette(kAntsGlow, 0xF9, 6);
+}
+
 // `_WaitAnims @ 29be:021c`. 12 bytes per entry, indexed by `siteData[+8]`:
 //   +0..1 anim Jake, +2..3 anim Jenny,
 //   +4..5 x    Jake, +6..7 x    Jenny,
@@ -994,21 +1011,9 @@ void SiteScreen::enter(uint siteNum, bool resetPartnerMood) {
 	}
 	_vm->setSitePaletteForSite(sitepic);
 
-	// SITEPALS ships palette 0xF9..0xFE as uniform yellow (3F 3E 00),
-	// so original marching-ants was a placeholder. Override with a
-	// 6-step yellow ramp so `cyclePaletteRange(0xF9, 0xFE)` produces a
-	// visible pulse on unsearched hotspots.
-	{
-		static const byte kAntsGlow[6 * 3] = {
-			0x40, 0x40, 0x00, // F9 ??? dim
-			0x80, 0x80, 0x00, // FA
-			0xC0, 0xC0, 0x00, // FB
-			0xFF, 0xFF, 0x40, // FC ??? peak
-			0xC0, 0xC0, 0x00, // FD
-			0x80, 0x80, 0x00, // FE
-		};
-		g_system->getPaletteManager()->setPalette(kAntsGlow, 0xF9, 6);
-	}
+	// Override SITEPALS' uniform-yellow 0xF9..0xFE with the marching-ants
+	// glow ramp (shared with the clue puzzle).
+	applyHotspotGlowPalette();
 
 	renderBackground(siteNum);
 
diff --git a/engines/eem/site.h b/engines/eem/site.h
index c91f0cffeb8..9bdbc88aa4a 100644
--- a/engines/eem/site.h
+++ b/engines/eem/site.h
@@ -82,6 +82,12 @@ void cyclePaletteRange(uint8 start, uint8 end);
 /// where the cycle shifts END???START rather than START???END.
 void cyclePaletteRangeReverse(uint8 start, uint8 end);
 
+/// Load the 6-step yellow marching-ants ramp into palette 0xF9..0xFE
+/// (SITEPALS ships these as uniform yellow). Shared by site hotspots and the
+/// clue puzzle so both outline clickable areas in the same original colours;
+/// `cyclePaletteRange(0xF9, 0xFE)` then pulses them.
+void applyHotspotGlowPalette();
+
 /// One hotspot (search rectangle) within a site, 14 bytes on disk.
 struct Hotspot {
 	int16  x1, y1, x2, y2;     ///< rectangle in screen coordinates


Commit: c53f9e018e04683f0979fa72eb33fcc6dc825acc
    https://github.com/scummvm/scummvm/commit/c53f9e018e04683f0979fa72eb33fcc6dc825acc
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:21:29+02:00

Commit Message:
EEM: acusation workflow for London

Changed paths:
    engines/eem/mystery.cpp
    engines/eem/mystery.h
    engines/eem/ui.cpp


diff --git a/engines/eem/mystery.cpp b/engines/eem/mystery.cpp
index edf8261849f..715971eb77c 100644
--- a/engines/eem/mystery.cpp
+++ b/engines/eem/mystery.cpp
@@ -571,6 +571,61 @@ int Mystery::selectedPoints() const {
 	return total;
 }
 
+bool Mystery::londonSolved() const {
+	// `_SolvedCheck @ 1ea1:0b1a`. Reuses the already-loaded hint chains
+	// (_aChain/_bChain/_cChain) as the three answer sets and the shared
+	// _noteSelected accuse-selection array ??? see londonSolved() doc in the
+	// header. EEM1/floppy keep the points model via solvedCheck().
+	for (uint chain = 0; chain < 3; chain++) {
+		int remaining = (int)kChainLen;
+		int wild = 0;
+		for (uint slot = 0; slot < kChainLen; slot++) {
+			const uint16 clue = hintChain(chain, slot);
+			if (clue == 0xFFFF) {
+				remaining--;
+				wild++;
+			} else if (clue < kCluesFoundCap && _noteSelected[clue]) {
+				remaining--;
+			}
+		}
+		if (wild == (int)kChainLen)
+			continue;            // all-wildcard set is unused
+		if (remaining == 0)
+			return true;         // every slot wildcard-or-selected
+	}
+	return false;
+}
+
+int Mystery::minCluesRemaining() const {
+	// `_GetMinRemaining @ 1ea1:1056`. Parallel to londonSolved() but tests the
+	// three answer sets against _cluesFound (discovered in the world). Tracks
+	// whether any non-wildcard answer clue has been found at all; if none, the
+	// player has nothing relevant yet (returns kChainLen).
+	int best = (int)kChainLen;
+	int foundReal = 0;
+	for (uint chain = 0; chain < 3; chain++) {
+		int remaining = (int)kChainLen;
+		int wild = 0;
+		for (uint slot = 0; slot < kChainLen; slot++) {
+			const uint16 clue = hintChain(chain, slot);
+			if (clue == 0xFFFF) {
+				remaining--;
+				wild++;
+			} else if (clue < kCluesFoundCap && _cluesFound[clue]) {
+				remaining--;
+				foundReal++;
+			}
+		}
+		if (wild == (int)kChainLen)
+			remaining = (int)kChainLen;   // unused set must not lower the min
+		if (remaining < best)
+			best = remaining;
+	}
+	if (foundReal == 0)
+		return (int)kChainLen;
+	return best;
+}
+
 int Mystery::foundPoints() const {
 	const byte *ni = noteIndex();
 	const uint16 cnt = noteIndexCount();
diff --git a/engines/eem/mystery.h b/engines/eem/mystery.h
index 1536a61360f..3793debedec 100644
--- a/engines/eem/mystery.h
+++ b/engines/eem/mystery.h
@@ -167,6 +167,23 @@ public:
 
 	bool solvedCheck() const { return selectedPoints() > 99; }
 
+	/// EEM2/London `_SolvedCheck @ 1ea1:0b1a`. London dropped EEM1's points
+	/// model for set matching: the three hint chains (_aChain/_bChain/_cChain,
+	/// = Ghidra g_dwSolveSet1..3, mystery header words 16/21/26) are the
+	/// accepted answer sets of up to 5 clue ids. A set is satisfied when every
+	/// slot is either 0xFFFF (wildcard / unused) or has its accuse-selected
+	/// flag (_noteSelected, = g_awSelectedClue) set; the accusation is solved
+	/// if ANY set is satisfied. An all-wildcard set is unused and never counts.
+	bool londonSolved() const;
+
+	/// EEM2/London `_GetMinRemaining @ 1ea1:1056`. Same three sets as
+	/// londonSolved() but tested against _cluesFound (clues discovered in the
+	/// world, = _CluesFound): returns the fewest clues still to find across the
+	/// usable sets ??? 0 means a full answer set has been discovered and the
+	/// accusation can begin. Returns 5 when no relevant clue has been found yet.
+	/// Drives the London accuse-readiness gate (`_AccuseEntry @ 1ea1:115c`).
+	int minCluesRemaining() const;
+
 	/// _WITCH @ 1df2:089f. GalleryData[i*0x46 + 0x02] == 0xFFFF marks the
 	/// guilty suspect; innocent suspects store their alibi TextBlock offset.
 	bool isGuilty(uint suspectIdx) const;
diff --git a/engines/eem/ui.cpp b/engines/eem/ui.cpp
index ed11bb57e91..2de366f5994 100644
--- a/engines/eem/ui.cpp
+++ b/engines/eem/ui.cpp
@@ -4177,32 +4177,58 @@ void EEMEngine::doAccuse() {
 	const byte *entryKdIdx = _mystery.kdTextIndex();
 	if (!entryKdIdx)
 		return;
-	const int foundPoints = _mystery.foundPoints();
+	// Readiness tier (0 nothing yet .. 3 ready-to-accuse). EEM1 `_AccuseEntry
+	// @ 1df2:0ff8` tiers on the top-5 found-clue score; EEM2 `_AccuseEntry @
+	// 1ea1:115c` tiers on minCluesRemaining() (jumptable @ 1ea1:1333: 0 ->
+	// ready, 1 -> almost, 2..4 -> keep looking, 5 -> nothing yet ??? only the 0
+	// case sets the can-accuse flag). Both variants then pick the same four KD
+	// lines, so map onto one shared tier.
+	int readyTier;
+	if (isLondon()) {
+		const int minRem = _mystery.minCluesRemaining();
+		readyTier = (minRem >= (int)Mystery::kChainLen) ? 0
+				  : (minRem >= 2)                       ? 1
+				  : (minRem == 1)                       ? 2
+														: 3;
+	} else {
+		const int foundPoints = _mystery.foundPoints();
+		readyTier = (foundPoints == 0)    ? 0
+				  : (foundPoints < 0x32)  ? 1
+				  : (foundPoints < 0x65)  ? 2
+											: 3;
+	}
+
 	uint entryKDSpeak = 0;
 	uint16 entryTextOff = 0xFFFF;
 	uint16 entryVoiceOverride = 0xFFFF;
 	bool canAccuse = false;
 	Common::String entryText;
-	if (foundPoints == 0) {
+	switch (readyTier) {
+	case 0:
 		entryKDSpeak = 9;
 		entryText = "3We're not ready to solve this mystery yet.  "
 					"Let's keep investigating until we have some more solid "
 					"evidence to make our case!";
 		// Practice mystery M0 ships the matching ZeroText takes as the
 		// final two SDB entries, but its KD digital table points kdspeak 9
-		// at earlier hint clips. Use the otherwise unreferenced pair.
-		if (_mystery.number() == 0)
+		// at earlier hint clips. Use the otherwise unreferenced pair. EEM1
+		// SDB indices, so EEM1 only.
+		if (!isLondon() && _mystery.number() == 0)
 			entryVoiceOverride = (_partner == kPartnerJake) ? 105 : 104;
-	} else if (foundPoints < 0x32) {
+		break;
+	case 1:
 		entryKDSpeak = 0;
 		entryTextOff = READ_LE_UINT16(entryKdIdx + 0);
-	} else if (foundPoints < 0x65) {
+		break;
+	case 2:
 		entryKDSpeak = 1;
 		entryTextOff = READ_LE_UINT16(entryKdIdx + 2);
-	} else {
+		break;
+	default:
 		entryKDSpeak = 2;
 		entryTextOff = READ_LE_UINT16(entryKdIdx + 4);
 		canAccuse = true;
+		break;
 	}
 	if (entryText.empty() && entryTextOff != 0xFFFF) {
 		entryText = parseString(_mystery.textAt(entryTextOff),
@@ -4280,8 +4306,12 @@ void EEMEngine::doAccuse() {
 		return;
 	}
 
-	// `_DoAccuse @ 1df2:0c75` ??? `_SolvedCheck` gate.
-	if (!_mystery.solvedCheck()) {
+	// `_DoAccuse @ 1df2:0c75` (EEM1) / `1ea1:0c75` (EEM2) ??? `_SolvedCheck` gate.
+	// London matches the selected notes against the answer sets; EEM1/floppy
+	// sum clue points.
+	const bool accuseSolved =
+		isLondon() ? _mystery.londonSolved() : _mystery.solvedCheck();
+	if (!accuseSolved) {
 		const byte *kdIdx = _mystery.kdTextIndex();
 		const int16 hintOff = kdIdx
 			? (int16)READ_LE_UINT16(kdIdx + 6)
@@ -4297,7 +4327,12 @@ void EEMEngine::doAccuse() {
 				hint = "Necesitamos buscar pistas antes de resolver "
 					   "el misterio. Investiguemos un poco mas!";
 			} else {
-				hint = (_mystery.selectedPoints() == 0)
+				// "nothing yet" vs "almost": London has no points, so judge by
+				// whether any answer clue has been found (minCluesRemaining).
+				const bool nothingYet = isLondon()
+					? _mystery.minCluesRemaining() >= (int)Mystery::kChainLen
+					: _mystery.selectedPoints() == 0;
+				hint = nothingYet
 					? "We're not ready to solve this mystery yet. "
 					  "Let's keep investigating until we have some "
 					  "more solid evidence."


Commit: aca47008bdbc7a8f04dc57c736f9fa6fcd21cf22
    https://github.com/scummvm/scummvm/commit/aca47008bdbc7a8f04dc57c736f9fa6fcd21cf22
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:21:30+02:00

Commit Message:
EEM: doApproach fixes for London

Changed paths:
    engines/eem/ui.cpp


diff --git a/engines/eem/ui.cpp b/engines/eem/ui.cpp
index 2de366f5994..a099e78f2fd 100644
--- a/engines/eem/ui.cpp
+++ b/engines/eem/ui.cpp
@@ -148,14 +148,18 @@ bool loadLondonApproachData(uint16 approachId, LondonApproachData &out) {
 	const uint16 pageCount = READ_LE_UINT16(data.data() + 14);
 	out.pages.clear();
 	uint32 pos = 16;
+	// `_DoApproach @ 1717:009b` reads the pages with `_fgets` into 255-byte
+	// slots, so each page is one NEWLINE-terminated record ??? NOT NUL-separated.
+	// (A*.BIN carries no NULs; splitting on '\0' lumps every page into page 0,
+	// which is why the Next/Prev arrows had nothing to page through.)
 	for (uint16 i = 0; i < pageCount && pos < size; i++) {
 		const uint32 start = pos;
-		while (pos < size && data[pos] != 0)
+		while (pos < size && data[pos] != '\n')
 			pos++;
 		out.pages.push_back(Common::String((const char *)data.data() + start,
 										   pos - start));
 		if (pos < size)
-			pos++;
+			pos++;  // skip the '\n' separator
 	}
 	return out.videoId != 0 && !out.pages.empty();
 }
@@ -3469,7 +3473,6 @@ bool EEMEngine::doLondonApproach(uint16 approachId) {
 		const Common::Rect textRect =
 			data.textRect.findIntersectingRect(Common::Rect(kScreenWidth, kScreenHeight));
 		if (!textRect.isEmpty()) {
-			scratch.fillRect(textRect, 0);
 			if (page < data.pages.size()) {
 				Common::Array<Common::String> wrapped;
 				_font.wordWrapText(data.pages[page], MAX<int>(8, textRect.width()),
@@ -3477,9 +3480,15 @@ bool EEMEngine::doLondonApproach(uint16 approachId) {
 				const int lineH = _font.getFontHeight();
 				const int maxLines = MAX<int>(1, textRect.height() / lineH);
 				for (uint i = 0; i < wrapped.size() && (int)i < maxLines; i++) {
+					// `_DoApproach @ 1717:029e`:
+					// `_WordWrap(x, y, w, page, fontColor=1, dropColor=-1)`.
+					// Font colour is palette index 1 (NOT 0x0F white), drawn
+					// straight onto the restored video-frame background ??? the
+					// DOS `vga_fvidvid` calls just restore the clean bg, they
+					// don't paint a panel, so don't fill the rect here either.
 					_font.drawString(&scratch, wrapped[i], textRect.left,
 									 textRect.top + (int)i * lineH,
-									 textRect.width(), 0x0f);
+									 textRect.width(), 1);
 				}
 			}
 		}
@@ -3562,6 +3571,10 @@ bool EEMEngine::doLondonApproach(uint16 approachId) {
 	else
 		setSitePalette(0x3b);
 	bool done = false;
+	// `_DoApproach` idle loop (1717:02d4) shimmers palette 0xF3..0xF7 via
+	// `_ColorCycle(0xf3, 0xf7)` each frame-rate tick; mirror it on the video
+	// palette (the diff-animation ramp those entries belong to).
+	uint32 lastShimmer = g_system->getMillis();
 	while (!shouldQuit() && !done) {
 		Common::Event ev;
 		while (g_system->getEventManager()->pollEvent(ev)) {
@@ -3630,6 +3643,13 @@ bool EEMEngine::doLondonApproach(uint16 approachId) {
 				}
 			}
 		}
+		if (haveVideo) {
+			const uint32 now = g_system->getMillis();
+			if (now - lastShimmer >= 70) {
+				lastShimmer = now;
+				cyclePaletteRange(0xF3, 0xF7);
+			}
+		}
 		g_system->updateScreen();
 		g_system->delayMillis(10);
 	}


Commit: b749d688e8933fc49220856b57a7ebec294af6e5
    https://github.com/scummvm/scummvm/commit/b749d688e8933fc49220856b57a7ebec294af6e5
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:21:30+02:00

Commit Message:
EEM: enter the name screen fixes for London

Changed paths:
    engines/eem/eem.cpp


diff --git a/engines/eem/eem.cpp b/engines/eem/eem.cpp
index 660c2ffe547..7fe9458d44f 100644
--- a/engines/eem/eem.cpp
+++ b/engines/eem/eem.cpp
@@ -1285,7 +1285,10 @@ void EEMEngine::showLondonCharSelect() {
 	const Common::Rect kMaleBox(110, 116, 120, 122);
 	const Common::Rect kFemaleBox(190, 116, 200, 122);
 	const uint kMaxFirst = 12, kMaxLast = 20;
-	const uint8 kInkColor = 0x0F;     // typed-name ink
+	// `_GetNameString @ 1cd3:0ddc` draws both the typed name and the caret in
+	// colour 0x22 (the passport-field ink); the old 0x0F was a guess and the
+	// underscore caret in it was effectively invisible.
+	const uint8 kInkColor = 0x22;     // typed-name ink + caret
 
 	Picture bg;
 	const bool haveBg = _picsArchive.getPicture(0xc, bg) && !bg.surface.empty();
@@ -1334,18 +1337,27 @@ void EEMEngine::showLondonCharSelect() {
 			if (haveBg)
 				scratch.simpleBlitFrom(bg.surface);
 			if (getFont().isLoaded()) {
-				Common::String f = first;
-				if (field == kFieldFirst && blink)
-					f += "_";
-				Common::String l = last;
-				if (field == kFieldLast && blink)
-					l += "_";
-				getFont().drawString(&scratch, f, kFirstRect.left + 2,
+				getFont().drawString(&scratch, first, kFirstRect.left + 2,
 									 kFirstRect.top + 1, kFirstRect.width(),
 									 kInkColor);
-				getFont().drawString(&scratch, l, kLastRect.left + 2,
+				getFont().drawString(&scratch, last, kLastRect.left + 2,
 									 kLastRect.top + 1, kLastRect.width(),
 									 kInkColor);
+				// Caret = solid block `_FillRect(x+1, y, 6, 0xb, 0x22)` at the
+				// input point (`_GetNameString @ 1cd3:0ddc`), NOT an underscore.
+				if (blink && (field == kFieldFirst || field == kFieldLast)) {
+					const Common::Rect &fr =
+						(field == kFieldFirst) ? kFirstRect : kLastRect;
+					const Common::String &buf =
+						(field == kFieldFirst) ? first : last;
+					const int caretX =
+						fr.left + 2 + getFont().getStringWidth(buf);
+					Common::Rect caret(caretX, fr.top, caretX + 6,
+									   fr.top + 0xb);
+					caret.clip(Common::Rect(kScreenWidth, kScreenHeight));
+					if (!caret.isEmpty())
+						scratch.fillRect(caret, kInkColor);
+				}
 			}
 			if (field == kFieldGender) {
 				// DOS restores both boxes, then fills the active one with the
@@ -1440,6 +1452,11 @@ void EEMEngine::showLondonCharSelect() {
 			blink = !blink;
 			needRedraw = true;
 		}
+		// Flush every frame so the mouse cursor tracks smoothly. The scratch
+		// rebuild + copyRectToScreen above is gated on needRedraw (and mouse
+		// motion doesn't set it), but the cursor is only re-composited by
+		// updateScreen(), so without this it moved only on blink/keypress.
+		g_system->updateScreen();
 		g_system->delayMillis(15);
 	}
 	if (shouldQuit()) {


Commit: f490186308e1a088e423ab6dfd60b06cca7bd2ec
    https://github.com/scummvm/scummvm/commit/f490186308e1a088e423ab6dfd60b06cca7bd2ec
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:21:30+02:00

Commit Message:
EEM: make sure the screen is updated during animations

Changed paths:
    engines/eem/clues.cpp


diff --git a/engines/eem/clues.cpp b/engines/eem/clues.cpp
index e3c589691ce..2ed40630967 100644
--- a/engines/eem/clues.cpp
+++ b/engines/eem/clues.cpp
@@ -431,6 +431,10 @@ void EEMEngine::playLondonInitCluesAnim(uint16 caseType, const Picture &bg,
 					break;
 				}
 			}
+			// Re-composite the cursor every ~10 ms so the mouse stays smooth
+			// between the 140 ms animation frames (the frame-rebuild above is
+			// throttled, but the cursor is only flushed by updateScreen()).
+			g_system->updateScreen();
 			g_system->delayMillis(10);
 		}
 	}
@@ -514,6 +518,7 @@ void EEMEngine::playCdFloppyInitCluesAnim(uint16 caseType, bool floppy,
 						break;
 					}
 				}
+				g_system->updateScreen();  // keep the cursor smooth between frames
 				g_system->delayMillis(10);
 			}
 		}
@@ -648,6 +653,7 @@ void EEMEngine::playCdFloppyInitCluesAnim(uint16 caseType, bool floppy,
 							break;
 						}
 					}
+					g_system->updateScreen();  // keep the cursor smooth between frames
 					g_system->delayMillis(10);
 				}
 			}


Commit: e9c44065c0f45b0f9724158370b40917044c51ef
    https://github.com/scummvm/scummvm/commit/e9c44065c0f45b0f9724158370b40917044c51ef
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:21:31+02:00

Commit Message:
EEM: added missing NPC in London cases intro

Changed paths:
    engines/eem/clues.cpp


diff --git a/engines/eem/clues.cpp b/engines/eem/clues.cpp
index 2ed40630967..68255504730 100644
--- a/engines/eem/clues.cpp
+++ b/engines/eem/clues.cpp
@@ -382,6 +382,27 @@ void EEMEngine::playLondonInitCluesAnim(uint16 caseType, const Picture &bg,
 	const bool haveAnim =
 		_aniArchive.loadAnimation(introAni, anim) && !anim.empty();
 
+	// `_DoInitClues @ 1abf:03b3` registers a SECOND, fixed briefing character
+	// (e.g. Nigel) on the LEFT, gated on caseType (jumptable @ CS:0x720):
+	//   caseType 0 -> _GetAnimation(0x70) @ (0, 0x3e)
+	//   caseType 2 -> _GetAnimation(0x0e) @ (0, 0x36)
+	//   caseType 3 -> _GetAnimation(0x74) @ (0, 0x1a)
+	//   caseType 1 and >= 4 -> partner only (no second character).
+	// Both animate during the entrance and the last frame is left on screen, so
+	// the following `displayClue` snapshot keeps them through the briefing
+	// dialogue. Without this the NPC is never drawn (Nigel invisible in M2).
+	uint npcAni = 0;
+	int npcX = 0, npcY = 0;
+	switch (caseType) {
+	case 0: npcAni = 0x70; npcY = 0x3e; break;
+	case 2: npcAni = 0x0e; npcY = 0x36; break;
+	case 3: npcAni = 0x74; npcY = 0x1a; break;
+	default: break;
+	}
+	Animation npc;
+	const bool haveNpc = npcAni != 0 &&
+		_aniArchive.loadAnimation(npcAni, npc) && !npc.empty();
+
 	// _AllBlack(): blank the palette so the _FadeIn after frame 0 reveals the
 	// briefing (palette 0x39 ??? EEM2's 63-entry SITEPALS. shifts EEM1's UI set).
 	byte pal[kPalSize];
@@ -397,18 +418,26 @@ void EEMEngine::playLondonInitCluesAnim(uint16 caseType, const Picture &bg,
 	// keeps this faithful if the cells/script ever diverge and matches the map/
 	// site partner rendering. One pass = one script cell per ~140 ms tick.
 	bool skip = false;
-	const uint frames = haveAnim ? (uint)anim.size() : 1;
+	uint frames = haveAnim ? (uint)anim.size() : 1;
+	if (haveNpc)
+		frames = MAX<uint>(frames, (uint)npc.size());
 	for (uint frame = 0; frame < frames && !shouldQuit() && !skip; frame++) {
 		if (haveBriefingBg)
 			blitAt(bg, 0, 0);
-		if (haveAnim) {
-			const uint cell =
-				partnerFrameAtTick(0x18, (uint)anim.size(), frame * 140);
-			Graphics::Surface *scr = g_system->lockScreen();
-			if (scr) {
+		Graphics::Surface *scr = g_system->lockScreen();
+		if (scr) {
+			if (haveAnim) {
+				const uint cell =
+					partnerFrameAtTick(0x18, (uint)anim.size(), frame * 140);
 				blitAnimFrameAnchored(scr, anim[cell], kAnchorX, kAnchorY);
-				g_system->unlockScreen();
 			}
+			if (haveNpc) {
+				// NPC frame script = 0x0e (the `_NewAnimation` animId arg).
+				const uint ncell =
+					partnerFrameAtTick(0x0e, (uint)npc.size(), frame * 140);
+				blitAnimFrameAnchored(scr, npc[ncell], npcX, npcY);
+			}
+			g_system->unlockScreen();
 		}
 		if (frame == 0 && havePal)
 			fadePaletteFromBlack(pal);  // _FadeIn @ 1abf:03b3
@@ -991,6 +1020,18 @@ void EEMEngine::displayClue(const byte *clueBlock) {
 			if (_picsArchive.getPicture(charPicId, charPic) &&
 				charX < kScreenWidth && charY < kScreenHeight) {
 				blitMaskedToScreen(charPic, charX, charY);
+				// `_DisplayClue` bakes the speaker portrait into the BACKGROUND
+				// via `_AddPicBackground`; the DOS never restores a clean BG
+				// mid-dialogue, so the portrait persists across the following
+				// clue entries. Bake it into the `bg` snapshot too ??? otherwise
+				// the per-entry full restore (top of this loop) wipes a fixed
+				// NPC portrait (e.g. London's Nigel) on every entry that carries
+				// no portrait of its own, leaving only the balloon ("rendered,
+				// then refreshed over").
+				const byte transp = (byte)(charPic.flags >> 8);
+				bg.transBlitFrom(charPic.surface,
+								 Common::Point((int)charX, (int)charY),
+								 (uint32)transp);
 			}
 		}
 


Commit: 2372aaf0180a1be5ae58f68786fd8e4cc1ad9cdb
    https://github.com/scummvm/scummvm/commit/2372aaf0180a1be5ae58f68786fd8e4cc1ad9cdb
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:21:31+02:00

Commit Message:
EEM: implement setup screen in London

Changed paths:
    engines/eem/clues.cpp
    engines/eem/eem.cpp
    engines/eem/eem.h
    engines/eem/ui.cpp


diff --git a/engines/eem/clues.cpp b/engines/eem/clues.cpp
index 68255504730..b49c45539db 100644
--- a/engines/eem/clues.cpp
+++ b/engines/eem/clues.cpp
@@ -977,7 +977,7 @@ void EEMEngine::displayClue(const byte *clueBlock) {
 		// `stop()`). 0 = no cue; EEM1 clue entries have no such field. Gated on
 		// `_voiceOn` like `startTravelMusic` ??? the DOS gate is the separate
 		// music-on / MIDI-available flags (DAT_3036_4cc0 && DAT_2bca_146a).
-		if (isLondon() && _music && _voiceOn) {
+		if (isLondon() && _music && _musicOn) {
 			const uint16 clueMusic = READ_LE_UINT16(c + 0x1c);
 			if (clueMusic != 0)
 				_music->playMus(clueMusic, /* loop= */ false);
diff --git a/engines/eem/eem.cpp b/engines/eem/eem.cpp
index 7fe9458d44f..4f455d22562 100644
--- a/engines/eem/eem.cpp
+++ b/engines/eem/eem.cpp
@@ -661,9 +661,13 @@ screenLoop:
 			break;
 
 		case kScreenSetup:
-			// Handler 6 @ 1a35:0e48 -> _DoSetup @ 1f78:044e. Entered
-			// from BigMap setup button (_NextScreen=6 @ 20fe:0c33).
-			doSetup();
+			// Handler 6 @ 1a35:0e48 -> _DoSetup. EEM1 = seg-1f78 (`doSetup`);
+			// EEM2/London = seg-2046 (`doSetupLondon`, 4 toggles + own layout).
+			// Entered from BigMap setup button (_NextScreen=6 @ 20fe:0c33).
+			if (isLondon())
+				doSetupLondon();
+			else
+				doSetup();
 			break;
 
 		case kScreenProfile:
@@ -1578,7 +1582,8 @@ void EEMEngine::startTravelMusic() {
 	// doesn't write it; combined with `_DoSiteLoop @ 168d:06c0` calling
 	// `_StopMIDI()` before the interactive phase, travel music plays
 	// ONCE during the entrance animation only.
-	if (!_music || !_mystery.isLoaded() || !_voiceOn)
+	if (!_music || !_mystery.isLoaded() ||
+		(isLondon() ? !_musicOn : !_voiceOn))
 		return;
 	const uint num = _mystery._siteNumber % 5;
 	_music->playMus(num, /* loop= */ false);
@@ -1593,7 +1598,7 @@ void EEMEngine::startLondonTravelMusic(uint8 travelKind) {
 		{ 7, 23, 17 },
 		{ 10, 21, 24 },
 	};
-	if (!_music || !_voiceOn || travelKind == 0 ||
+	if (!_music || !_musicOn || travelKind == 0 ||
 		travelKind >= ARRAYSIZE(kLondonTravelMusic))
 		return;
 
@@ -1686,6 +1691,8 @@ Common::Error EEMEngine::saveGameStream(Common::WriteStream *stream,
 	s.syncAsByte(_partner);
 	s.syncAsByte(_chainStage);
 	s.syncAsByte(_voiceOn);
+	// EEM2/London music (MIDI) toggle ??? `DAT_3036_4cc0`, separate from voice.
+	s.syncAsByte(_musicOn);
 
 	// London passport gender (player pronouns 0x86-0x88).
 	byte playerFemale = _playerFemale ? 1 : 0;
diff --git a/engines/eem/eem.h b/engines/eem/eem.h
index 524c5d1e497..9f4d98c7856 100644
--- a/engines/eem/eem.h
+++ b/engines/eem/eem.h
@@ -512,6 +512,12 @@ private:
 	Common::KeyCode setupShowFullscreenPic(uint16 picId, bool transparent);
 	void setupLeave();
 
+	/// EEM2/London setup screen ??? `_DoSetup @ 2046:067b` (distinct from EEM1's
+	/// seg-1f78 screen): 4 toggles (partner / voice / music / highlight boxes),
+	/// rearranged 13-button layout, scrapbook paging. Reuses the shared helpers.
+	void doSetupLondon();
+	void setupDrawScreenLondon();
+
 	/// `_DoInitClues @ 1a35:0411` (minus live ANI sequence playback).
 	void doInitClues();
 
@@ -589,6 +595,12 @@ private:
 	/// partner speech, intro VO).
 	bool _voiceOn = true;
 
+	/// EEM2/London music (MIDI) on/off ??? `DAT_3036_4cc0`, toggled by the music
+	/// button in `_DoSetup @ 2046:067b`. Separate from `_voiceOn` (EEM2 has both
+	/// a voice and a music toggle; EEM1 only had one sound toggle). Gates the
+	/// briefing / travel MIDI for London.
+	bool _musicOn = true;
+
 	/// Set by the profile/new-player screens. London uses it to decide whether
 	/// to start the training mystery or resume the loaded profile's menu/state.
 	bool _profileCreatedThisSession = false;
diff --git a/engines/eem/ui.cpp b/engines/eem/ui.cpp
index a099e78f2fd..6f068ed710a 100644
--- a/engines/eem/ui.cpp
+++ b/engines/eem/ui.cpp
@@ -195,6 +195,18 @@ constexpr Common::Rect kSetupKid2Rect    (Common::Point( 99,  54),  49,  8);
 constexpr Common::Rect kSetupSoundOnRect (Common::Point(106,  86),  19,  8);
 constexpr Common::Rect kSetupSoundOffRect(Common::Point(106,  96),  19,  8);
 
+// EEM2/London setup highlights ??? `_SetupSettings @ 2046:0008` (_SwapColors,
+// key 0xFE). On/off label pairs for the 4 toggles (rects @ 2bca:13ec; drawn
+// in the DOS order ON-then-OFF). Partner reuses Jake/Jenny.
+constexpr Common::Rect kLonSetJake    (Common::Point( 99,  44), 49, 8); // 0x13ec
+constexpr Common::Rect kLonSetJenny   (Common::Point( 99,  54), 49, 8); // 0x13f4
+constexpr Common::Rect kLonSetVoiceOn (Common::Point(106,  68), 20, 8); // 0x13fc
+constexpr Common::Rect kLonSetVoiceOff(Common::Point(106,  68), 40, 8); // 0x1404
+constexpr Common::Rect kLonSetMusicOn (Common::Point(106,  84), 20, 8); // 0x141c
+constexpr Common::Rect kLonSetMusicOff(Common::Point(128,  85), 18, 7); // 0x1424
+constexpr Common::Rect kLonSetHiOn    (Common::Point(106, 110), 29, 8); // 0x1414
+constexpr Common::Rect kLonSetHiOff   (Common::Point(106, 100), 29, 8); // 0x140c
+
 // `_SwapColors @ 172b:1d2a` ??? replace pixels in r where value==from
 // with to. 0xFE = BG text-key; 0x15 = active palette index, 0x00 =
 // inactive (set by `_SetupSettings @ 1f78:000d`).
@@ -1774,6 +1786,188 @@ void EEMEngine::setupLeave() {
 	saveProfile(_playerName);
 }
 
+void EEMEngine::setupDrawScreenLondon() {
+	// `_SetupSettings @ 2046:0008`: BG PIC 0x40 + _SwapColors highlights for the
+	// 4 toggles (key 0xFE, 0x15 active / 0x00 dim), drawn ON-then-OFF.
+	Graphics::ManagedSurface scratch(kScreenWidth, kScreenHeight,
+		Graphics::PixelFormat::createFormatCLUT8());
+	scratch.clear();
+	Picture bg;
+	if (_picsArchive.getPicture(0x40, bg))
+		scratch.simpleBlitFrom(bg.surface);
+
+	const byte kKey = 0xFE, kBright = 0x15, kDim = 0x00;
+	// Partner (DAT_3036_4bd4).
+	swapColors(scratch, kLonSetJake,  kKey, _partner == kPartnerJake  ? kBright : kDim);
+	swapColors(scratch, kLonSetJenny, kKey, _partner == kPartnerJenny ? kBright : kDim);
+	// Voice (DAT_3036_4c4e).
+	swapColors(scratch, kLonSetVoiceOn,  kKey, _voiceOn ? kBright : kDim);
+	swapColors(scratch, kLonSetVoiceOff, kKey, _voiceOn ? kDim : kBright);
+	// Music (DAT_3036_4cc0).
+	swapColors(scratch, kLonSetMusicOn,  kKey, _musicOn ? kBright : kDim);
+	swapColors(scratch, kLonSetMusicOff, kKey, _musicOn ? kDim : kBright);
+	// Highlight boxes (DAT_3036_4c4a; hide_highlight_boxes is the inverse).
+	const bool hiOn = !ConfMan.getBool("hide_highlight_boxes");
+	swapColors(scratch, kLonSetHiOn,  kKey, hiOn ? kBright : kDim);
+	swapColors(scratch, kLonSetHiOff, kKey, hiOn ? kDim : kBright);
+
+	g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
+							   0, 0, kScreenWidth, kScreenHeight);
+	g_system->updateScreen();
+}
+
+void EEMEngine::doSetupLondon() {
+	// `_DoSetup @ 2046:067b`. BG PIC 0x40; 13 click rects (`_SetupButtons @
+	// 2bca:12ce`); `HandleSetupButton @ 2046:01d9` dispatch (verified from the
+	// jumptable @ 2046:0661). EEM2 has FOUR toggles vs EEM1's two and a
+	// rearranged left column:
+	//   [0]( 20, 44) Partner    [1]( 20, 63) Voice    [2]( 20,101) Highlight boxes
+	//   [3]( 20,127) Profile(8) [12](20, 82) Music    [4](281, 43) ScrapBook +
+	//   [5](281, 62) ScrapBook- [6](281,108) Save     [7](281,127) New case(0xa)
+	//   [8]( 53,153) Done       [9](145,163) Quit      [10](212,153) Help
+	//   [11]( 81, 25) Credits (PIC 0x208).  _SavePlayerRecord on exit (setupLeave).
+	if (!_font.isLoaded()) {
+		_nextScreen = (ScreenId)_lastScreen;
+		return;
+	}
+	const Common::Rect kPartnerBtn ( 20,  44,  39,  61); // [0]
+	const Common::Rect kVoiceBtn   ( 20,  63,  39,  80); // [1]
+	const Common::Rect kHiBtn      ( 20, 101,  39, 118); // [2]
+	const Common::Rect kProfileBtn ( 20, 127,  39, 144); // [3]
+	const Common::Rect kScrapNext  (281,  43, 299,  60); // [4]
+	const Common::Rect kScrapPrev  (281,  62, 299,  79); // [5]
+	const Common::Rect kSaveBtn    (281, 108, 299, 125); // [6]
+	const Common::Rect kNewCaseBtn (281, 127, 299, 144); // [7]
+	const Common::Rect kDoneBtn    ( 53, 153, 108, 183); // [8]
+	const Common::Rect kQuitBtn    (145, 163, 174, 187); // [9]
+	const Common::Rect kHelpBtn    (212, 153, 266, 184); // [10]
+	const Common::Rect kCreditsBtn ( 81,  25, 238,  37); // [11]
+	const Common::Rect kMusicBtn   ( 20,  82,  38,  99); // [12]
+
+	setupDrawScreenLondon();
+	_nextScreen = kScreenSetup;  // sentinel ??? setupLeave picks the target
+	while (!shouldQuit()) {
+		Common::Event ev;
+		bool dirty = false;
+		while (g_system->getEventManager()->pollEvent(ev)) {
+			if (ev.type == Common::EVENT_QUIT ||
+				ev.type == Common::EVENT_RETURN_TO_LAUNCHER) {
+				_nextScreen = kScreenInvalid;
+				return;
+			}
+			if (ev.type == Common::EVENT_KEYDOWN &&
+				(ev.kbd.keycode == Common::KEYCODE_ESCAPE ||
+				 ev.kbd.keycode == Common::KEYCODE_RETURN)) {
+				setupLeave();
+				return;
+			}
+			if (ev.type != Common::EVENT_LBUTTONDOWN)
+				continue;
+			const int mx = ev.mouse.x, my = ev.mouse.y;
+
+			// --- toggles (stay on screen, re-render) ---
+			if (kPartnerBtn.contains(mx, my)) {
+				_partner = (_partner == kPartnerJake) ? kPartnerJenny
+													  : kPartnerJake;
+				dirty = true;
+				continue;
+			}
+			if (kVoiceBtn.contains(mx, my)) {
+				_voiceOn = !_voiceOn;
+				if (_audio)
+					_audio->setVoiceEnabled(_voiceOn);
+				dirty = true;
+				continue;
+			}
+			if (kMusicBtn.contains(mx, my)) {
+				_musicOn = !_musicOn;
+				if (!_musicOn)
+					stopMusic();
+				dirty = true;
+				continue;
+			}
+			if (kHiBtn.contains(mx, my)) {
+				ConfMan.setBool("hide_highlight_boxes",
+								!ConfMan.getBool("hide_highlight_boxes"));
+				dirty = true;
+				continue;
+			}
+
+			// --- navigation (leave the screen) ---
+			if (kProfileBtn.contains(mx, my)) {
+				saveProfile(_playerName);
+				_nextScreen = kScreenProfile;
+				return;
+			}
+			if (kNewCaseBtn.contains(mx, my)) {
+				saveProfile(_playerName);
+				_nextScreen = kScreenChooseMystery;
+				return;
+			}
+			if (kDoneBtn.contains(mx, my)) {
+				setupLeave();
+				return;
+			}
+			if (kQuitBtn.contains(mx, my)) {
+				if (areYouSure()) {
+					_nextScreen = kScreenInvalid;
+					return;
+				}
+				dirty = true;  // restore BG under the prompt
+				continue;
+			}
+
+			// --- actions that stay on screen ---
+			if (kSaveBtn.contains(mx, my)) {
+				// `_SaveGame` is gated on a game being active ([0x1966]).
+				if (_mystery.isLoaded())
+					saveProfile(_playerName);
+				continue;
+			}
+			if (kHelpBtn.contains(mx, my)) {
+				// `_InterfaceHelp(0)`. Reuse the EEM1 help-card mechanism.
+				static const uint16 kHelpPics[] = { 0x0192, 0x01B1 };
+				CursorMan.showMouse(false);
+				for (uint i = 0; i < ARRAYSIZE(kHelpPics); i++) {
+					setupDrawScreenLondon();
+					if (setupShowFullscreenPic(kHelpPics[i], true) ==
+						Common::KEYCODE_ESCAPE)
+						break;
+				}
+				CursorMan.showMouse(true);
+				dirty = true;
+				continue;
+			}
+			if (kCreditsBtn.contains(mx, my)) {
+				CursorMan.showMouse(false);
+				setupShowFullscreenPic(0x208, /* transparent= */ false);
+				CursorMan.showMouse(true);
+				setSitePalette(0);
+				dirty = true;
+				continue;
+			}
+			// ScrapBook +/- ??? London has 2 books (stage 1 / 2). The DOS pages
+			// next/prev (FUN_2046_0874); map to the two available books.
+			if (kScrapNext.contains(mx, my)) {
+				doShowScrapbook(1);
+				setSitePalette(0);
+				dirty = true;
+				continue;
+			}
+			if (kScrapPrev.contains(mx, my) && _chainStage >= 2) {
+				doShowScrapbook(2);
+				setSitePalette(0);
+				dirty = true;
+				continue;
+			}
+		}
+		if (dirty)
+			setupDrawScreenLondon();
+		g_system->updateScreen();
+		g_system->delayMillis(15);
+	}
+}
+
 void EEMEngine::doActionScreen() {
 	// `_ActionScreen @ 1c33:195b` ??? BG PIC 0x104 + PIC 9 @ (10, 0x87),
 	// `_DoChoose` with ActionNames @ 29be:0d6a. 5 picks alternating with


Commit: 44e61cbf7f00b8da6c7f915cdd60d7aca8bef2a6
    https://github.com/scummvm/scummvm/commit/44e61cbf7f00b8da6c7f915cdd60d7aca8bef2a6
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:21:31+02:00

Commit Message:
EEM: fixed regression on clean background for London

Changed paths:
    engines/eem/clues.cpp


diff --git a/engines/eem/clues.cpp b/engines/eem/clues.cpp
index b49c45539db..75d3d723f21 100644
--- a/engines/eem/clues.cpp
+++ b/engines/eem/clues.cpp
@@ -1019,19 +1019,13 @@ void EEMEngine::displayClue(const byte *clueBlock) {
 			Picture charPic;
 			if (_picsArchive.getPicture(charPicId, charPic) &&
 				charX < kScreenWidth && charY < kScreenHeight) {
+				// Draw over the per-entry clean BG (restored at the top of the
+				// loop), NOT into the persistent `bg` snapshot ??? baking it in
+				// makes successive speaker portraits stack instead of refresh
+				// (duplicate NPC portraits + labels). The briefing's fixed NPC
+				// persists via the entrance animation captured in the snapshot
+				// (playLondonInitCluesAnim), not via the per-entry portrait.
 				blitMaskedToScreen(charPic, charX, charY);
-				// `_DisplayClue` bakes the speaker portrait into the BACKGROUND
-				// via `_AddPicBackground`; the DOS never restores a clean BG
-				// mid-dialogue, so the portrait persists across the following
-				// clue entries. Bake it into the `bg` snapshot too ??? otherwise
-				// the per-entry full restore (top of this loop) wipes a fixed
-				// NPC portrait (e.g. London's Nigel) on every entry that carries
-				// no portrait of its own, leaving only the balloon ("rendered,
-				// then refreshed over").
-				const byte transp = (byte)(charPic.flags >> 8);
-				bg.transBlitFrom(charPic.surface,
-								 Common::Point((int)charX, (int)charY),
-								 (uint32)transp);
 			}
 		}
 


Commit: a79edd41f2b937a82e573bceac6e2f0a71c0a665
    https://github.com/scummvm/scummvm/commit/a79edd41f2b937a82e573bceac6e2f0a71c0a665
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:21:32+02:00

Commit Message:
EEM: missing music cue in London scrapbook

Changed paths:
    engines/eem/ui.cpp


diff --git a/engines/eem/ui.cpp b/engines/eem/ui.cpp
index 6f068ed710a..6acea3e5d58 100644
--- a/engines/eem/ui.cpp
+++ b/engines/eem/ui.cpp
@@ -1448,6 +1448,14 @@ void EEMEngine::doShowScrapbook(uint stage) {
 		return;
 	const bool currentTier = (stage == _chainStage);
 
+	// `_ShowScrapbook @ 2046:0874` (EEM2) opens with the scrapbook/newspaper
+	// MIDI tune 0x5d (same one `_ShowOneScrap` plays after a correct accusation),
+	// gated on the voice flag + MIDI availability (DOS `DAT_3036_4c4e`/`146a` ???
+	// the original gates this music on the *voice* toggle, not the music one).
+	// London only; EEM1's seg-1f78 scrapbook was not verified to play it.
+	if (isLondon() && _music && _voiceOn)
+		_music->playMus(0x5d, /* loop= */ false);
+
 	int mystery = lo;
 	if (currentTier) {
 		while (mystery < hi && _mysteriesSolved[mystery] == 0)
@@ -1485,6 +1493,9 @@ void EEMEngine::doShowScrapbook(uint stage) {
 			break;
 		}
 	}
+	// `_ShowScrapbook` / `_ShowOneScrap` stop the MIDI on the way out.
+	if (isLondon() && _music)
+		stopMusic();
 }
 
 void EEMEngine::doSetup() {


Commit: 3555a18bdbf4f1bab57d86df0e2e334bab9d31a2
    https://github.com/scummvm/scummvm/commit/3555a18bdbf4f1bab57d86df0e2e334bab9d31a2
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:21:32+02:00

Commit Message:
EEM: reduce the verbosity of the comments accross the engine

Changed paths:
    engines/eem/audio.cpp
    engines/eem/clues.cpp
    engines/eem/detection.cpp
    engines/eem/eem.cpp
    engines/eem/eem.h
    engines/eem/font.cpp
    engines/eem/font.h
    engines/eem/graphics.cpp
    engines/eem/music.cpp
    engines/eem/music.h
    engines/eem/mystery.cpp
    engines/eem/mystery.h
    engines/eem/resource.cpp
    engines/eem/resource.h
    engines/eem/site.cpp
    engines/eem/site.h
    engines/eem/ui.cpp


diff --git a/engines/eem/audio.cpp b/engines/eem/audio.cpp
index 14570379b84..d791fc1883f 100644
--- a/engines/eem/audio.cpp
+++ b/engines/eem/audio.cpp
@@ -52,7 +52,6 @@ void AudioPlayer::stopAll() {
 }
 
 void AudioPlayer::playVoc(const Common::Path &vocPath) {
-	// `_voiceEnabled` mirrors DAT_2d5d_3f97 (setup-screen voice toggle).
 	if (!_voiceEnabled) {
 		debugC(2, kDebugSound, "AudioPlayer: voice disabled, skipping %s",
 			   vocPath.toString().c_str());
@@ -75,7 +74,6 @@ void AudioPlayer::playVoc(const Common::Path &vocPath) {
 		return;
 	}
 
-	// _PlayVoice @ 1ff1:023e ??? route through kSpeechSoundType for the launcher slider.
 	_mixer->playStream(Audio::Mixer::kSpeechSoundType, &_voiceHandle,
 					   stream, -1, Audio::Mixer::kMaxChannelVolume,
 					   0, DisposeAfterUse::YES);
@@ -87,9 +85,9 @@ bool AudioPlayer::isVoicePlaying() const {
 	return _mixer->isSoundHandleActive(_voiceHandle);
 }
 
+// _WaitForVoiceDone @ 1ff1:0221 ??? pumps events while the AIL voice channel
+// drains, so animations + abort-on-click keep working during the wait.
 void AudioPlayer::waitForVoiceDone(uint32 maxMs) {
-	// _WaitForVoiceDone @ 1ff1:0221 ??? pumps events while the AIL voice channel
-	// drains, so animations + abort-on-click keep working during the wait.
 	const uint32 startMs = g_system->getMillis();
 	while (isVoicePlaying() && !_vm->shouldQuit() &&
 		   g_system->getMillis() - startMs < maxMs) {
diff --git a/engines/eem/clues.cpp b/engines/eem/clues.cpp
index 75d3d723f21..9fc34c93c95 100644
--- a/engines/eem/clues.cpp
+++ b/engines/eem/clues.cpp
@@ -127,7 +127,7 @@ void updateLondonClueSite(Mystery &mystery, uint16 rawSite, bool siteOn,
 		mystery._onSites[siteVal] = siteOn ? 1 : 0;
 }
 
-// _DoHappiness @ 172b:27b5 ??? per-zone sequence scripts.
+// _DoHappiness @ 172b:27b5 per-zone sequence scripts.
 // Jake seqs @ 29be:0337 (5 ?? 0x14 bytes), Jenny seqs @ 29be:039b. 9 frames each;
 // the anim cells contain 10 cells = pairs of (neutral, smile) at 5 intensities.
 const uint8 kJakeSeqs[5][9] = {
@@ -146,9 +146,7 @@ const uint8 kJennySeqs[5][9] = {
 };
 
 // EEM2 `FUN_17ee_26f6`: 10 rects @ 2bca:035c and 10 sequence rows for
-// Jennifer @ 2bca:03ac / Jake @ 2bca:0474. The portrait cells are 20-frame
-// ramps, so using the EEM1 5-level tables makes both characters smile in the
-// wrong places.
+// Jennifer @ 2bca:03ac / Jake @ 2bca:0474.
 const int kLondonHappyRightEdges[10] = {
 	35, 70, 98, 126, 156, 182, 208, 235, 277, 320
 };
@@ -196,9 +194,6 @@ uint happinessLevel(int x, bool london) {
 }
 
 // Masked blit: transparent colour = high byte of p.flags.
-// _AddPicBackground @ 172b:0ed4 pushes `word ptr ES:[BX] >> 8` as
-// _Rect_Move_Mask's mask byte (the on-disk u16 at file offset 0 maps to
-// Picture::flags).
 void blitMaskedToScreen(const Picture &p, int x, int y) {
 	Graphics::Surface *screen = g_system->lockScreen();
 	if (!screen)
@@ -225,10 +220,7 @@ void blitRawToScreen(const Picture &p, int x, int y) {
 							   x, y, w, h);
 }
 
-// _DoChoosePartner @ 1a35:0756 / EEM2 @ 1abf:0728. The original places
-// Jake + Jenny animations on a backdrop and polls two broad character rects;
-// we approximate those with a single split at x=160. EEM1 has Jenny left /
-// Jake right; EEM2 has Jake left / Jennifer right.
+// _DoChoosePartner @ 1a35:0756 / EEM2 @ 1abf:0728.
 void EEMEngine::doChoosePartner() {
 	_partner = kPartnerJake;
 
@@ -277,10 +269,6 @@ void EEMEngine::doChoosePartner() {
 
 	uint32 lastTick = g_system->getMillis();
 	while (!shouldQuit()) {
-		// 100ms tick: matches _CheckFrameRate's ~10 fps cadence. The seq
-		// is short and loops; _UpdateAnimations restarts at curIdx=0 on
-		// the 0x80 marker. _DoHappiness rewrites curIdx=0xFFFF on zone
-		// change (here we restart seqIdx).
 		if (g_system->getMillis() - lastTick > 100) {
 			lastTick = g_system->getMillis();
 			seqIdx = (seqIdx + 1) % 9;
@@ -345,14 +333,9 @@ void EEMEngine::doChoosePartner() {
 		g_system->delayMillis(20);
 	}
 
-	// Partner intro VOC (jake.voc / jen.voc @ 29be:0af9 / 29be:0af1),
-	// tail of _DoChoosePartner @ 1a35:097f.
 	if (_audio) {
 		if (isFloppy()) {
-			// Floppy _DoChoosePartner_Floppy @ 19bb:0a8e calls
-			// _LoadSoundName_Floppy(0x14) (slot 20); per-partner tables at
-			// 2608:0f0e / 2608:0f76 resolve to m-0113sl.voc (Jake) or
-			// f-0140sl.voc (Jenny).
+			// Floppy _DoChoosePartner_Floppy @ 19bb:0a8e 
 			_audio->playFloppyVoiceSlot(0x14, _partner);
 		} else {
 			_audio->playVoc(Common::Path(
@@ -362,18 +345,7 @@ void EEMEngine::doChoosePartner() {
 	}
 }
 
-// EEM2 case-intro animation ??? `_DoInitClues` @ 1abf:03b3. Ghidra-confirmed
-// flow (decompiled prologue):
-//   _AllBlack(); _GetBackground(0x52); _GetPalette(0x39);
-//   anim = _GetAnimation(DAT_4bd4 ? 0x71 : 0x18);   // Jake 0x18 / Jenny 0x71
-//   _NewAnimation(0xd2, 0x3f, anim, ...);            // registered at (210,63)
-//   _UpdateAnimations(); _FadeIn();                  // draw frame 0, then fade
-//   while (i != _max()) { _CheckFrameRate(); _UpdateAnimations(); kbhit->skip }
-//   if (caseType == 1) { _LoadSoundName("phone1.voc"); _PlayVoice; _Wait... }
-// So, unlike EEM1's game/book/nancy cycle + `_PlayInSequence`, EEM2 registers
-// ONE partner animation drawn by `_UpdateAnimations` ??? hence we anchor frames
-// with `blitAnimFrameAnchored` ((0xd2 - miscflags, 0x3f - rowoff), per
-// `_UpdateAnimations @ 172b:09c1`), the same helper the map/site screens use.
+// EEM2 case-intro animation ??? `_DoInitClues` @ 1abf:03b3.
 void EEMEngine::playLondonInitCluesAnim(uint16 caseType, const Picture &bg,
 										bool haveBriefingBg) {
 	const uint introAni = (_partner == kPartnerJake) ? 0x18 : 0x71;
@@ -383,14 +355,7 @@ void EEMEngine::playLondonInitCluesAnim(uint16 caseType, const Picture &bg,
 		_aniArchive.loadAnimation(introAni, anim) && !anim.empty();
 
 	// `_DoInitClues @ 1abf:03b3` registers a SECOND, fixed briefing character
-	// (e.g. Nigel) on the LEFT, gated on caseType (jumptable @ CS:0x720):
-	//   caseType 0 -> _GetAnimation(0x70) @ (0, 0x3e)
-	//   caseType 2 -> _GetAnimation(0x0e) @ (0, 0x36)
-	//   caseType 3 -> _GetAnimation(0x74) @ (0, 0x1a)
-	//   caseType 1 and >= 4 -> partner only (no second character).
-	// Both animate during the entrance and the last frame is left on screen, so
-	// the following `displayClue` snapshot keeps them through the briefing
-	// dialogue. Without this the NPC is never drawn (Nigel invisible in M2).
+	// (Nigel) on the LEFT, gated on caseType (jumptable @ CS:0x720):
 	uint npcAni = 0;
 	int npcX = 0, npcY = 0;
 	switch (caseType) {
@@ -403,20 +368,12 @@ void EEMEngine::playLondonInitCluesAnim(uint16 caseType, const Picture &bg,
 	const bool haveNpc = npcAni != 0 &&
 		_aniArchive.loadAnimation(npcAni, npc) && !npc.empty();
 
-	// _AllBlack(): blank the palette so the _FadeIn after frame 0 reveals the
-	// briefing (palette 0x39 ??? EEM2's 63-entry SITEPALS. shifts EEM1's UI set).
 	byte pal[kPalSize];
 	const bool havePal = getSitePalette(0x39, pal);
 	byte black[kPalSize] = {};
 	g_system->getPaletteManager()->setPalette(black, 0, 256);
 	g_system->updateScreen();
 
-	// Drive the cells through the animation script (`_NewAnimation` prior
-	// 0x18 -> `_AnimationSequences[0x18]`), not a raw 0..N-1 sweep ??? the
-	// frame shown each tick is `script[tick]`, not `tick`. For EEM2 that
-	// script is the count-up {0..16}, but routing through `partnerFrameAtTick`
-	// keeps this faithful if the cells/script ever diverge and matches the map/
-	// site partner rendering. One pass = one script cell per ~140 ms tick.
 	bool skip = false;
 	uint frames = haveAnim ? (uint)anim.size() : 1;
 	if (haveNpc)
@@ -460,9 +417,6 @@ void EEMEngine::playLondonInitCluesAnim(uint16 caseType, const Picture &bg,
 					break;
 				}
 			}
-			// Re-composite the cursor every ~10 ms so the mouse stays smooth
-			// between the 140 ms animation frames (the frame-rebuild above is
-			// throttled, but the cursor is only flushed by updateScreen()).
 			g_system->updateScreen();
 			g_system->delayMillis(10);
 		}
@@ -475,13 +429,7 @@ void EEMEngine::playLondonInitCluesAnim(uint16 caseType, const Picture &bg,
 	}
 }
 
-// EEM1 CD/floppy case-intro animation ??? `_DoInitClues @ 1a35:0411`:
-//   anims registered: game @ (0xcd,0x6c) [0x17 Jake / 0x3b Jenny],
-//   book @ (0,99) [0x18 / 0x3c], nancy @ (0x68,0x8b) [0x19, caseType 1 only];
-//   cycle the game anim once (click skips), then `_PlayInSequence @ 172b:2d03`
-//   plays the partner entrance per partner+caseType. A phone/news voice rings
-//   first on CD caseType 2 / floppy caseType 2-3. (London: see
-//   playLondonInitCluesAnim.)
+// EEM1 CD/floppy case-intro animation ??? `_DoInitClues @ 1a35:0411`
 void EEMEngine::playCdFloppyInitCluesAnim(uint16 caseType, bool floppy,
 										  const Picture &bg, bool haveBriefingBg) {
 	const uint gameAni = _partner == kPartnerJake ? 0x17 : 0x3b;
@@ -494,12 +442,6 @@ void EEMEngine::playCdFloppyInitCluesAnim(uint16 caseType, bool floppy,
 						  && _aniArchive.loadAnimation(0x19, nancy)
 						  && !nancy.empty();
 
-	// Cycle the game animation once at _CheckFrameRate cadence (~7 fps, 140 ms
-	// per tick; _InitFrameCounter @ 1a35:01ae). The original loop @ 1a35:0507
-	// makes `gameNum - 1` _UpdateAnimations calls, each advancing every
-	// registered slot by one script tick. _DoInitClues @ 1a35:0507/0541
-	// hard-codes Jake's script IDs (0x17/0x18/0x19) regardless of partner, so
-	// we look up scripts by those IDs unconditionally.
 	if (haveGame || haveBook || haveNancy) {
 		const uint kCheckFrameRateMs = 140;
 		const uint baseFrames = haveGame ? game.size() : 8;
@@ -555,9 +497,7 @@ void EEMEngine::playCdFloppyInitCluesAnim(uint16 caseType, bool floppy,
 
 	// _VidramRectCopy(0, 0x5a, 0x28, 0x6d, 16000, 48000/32000): freeze the
 	// lower-left book/Nancy band the original bakes into BG buffers before
-	// clearing registered animations. Width is in mode-X cols (0x28 = 160px).
-	// Intentionally drops the right-side game anim; _PlayInSequence redraws
-	// that character over a clean BG next.
+	// clearing registered animations.
 	Graphics::ManagedSurface briefingBase(kScreenWidth, kScreenHeight,
 		Graphics::PixelFormat::createFormatCLUT8());
 	briefingBase.clear();
@@ -586,15 +526,6 @@ void EEMEngine::playCdFloppyInitCluesAnim(uint16 caseType, bool floppy,
 		}
 	}
 
-	// Setup voice ??? _DoInitClues @ 1a35:061d runs this BEFORE _PlayInSequence
-	// (i.e. the phone rings, the player hears it ring out, and only then
-	// does Jake/Jenny reach for the receiver):
-	//   CD:     caseType 2 -> PHONE.VOC then _WaitForVoiceDone
-	//   Floppy (_DoInitClues_Floppy @ 19bb:042f):
-	//     caseType 2 -> slot 0xc (PHONESL.VOC)
-	//     caseType 3 -> slot 3   (NEWSCAN.VOC, news-anchor variant)
-	// While the voice plays the screen continues showing the game anim's
-	// last frame (saved into briefingBase above).
 	if (_audio) {
 		if (caseType == 2) {
 			if (floppy)
@@ -608,12 +539,7 @@ void EEMEngine::playCdFloppyInitCluesAnim(uint16 caseType, bool floppy,
 		}
 	}
 
-	// _PlayInSequence @ 172b:2d03. Anim selection per partner + caseType:
-	//   Jake:  caseType 1 -> 0x38 @ (0xcd, 0x6d)
-	//          caseType 2 -> 0x37 @ (0xcd, 0x6c)
-	//          caseType 3 -> 0x39 @ (0xcd, 0x6c)
-	//   Jenny: caseType 2 -> 0x3a @ (0xcd, 0x6c)
-	//          caseType 3 -> 0x3d @ (0xcd, 0x6c)
+	// _PlayInSequence @ 172b:2d03.
 	uint16 seqAni = 0xFFFF;
 	uint16 seqY   = 0x6c;
 	if (_partner == kPartnerJake) {
@@ -657,10 +583,7 @@ void EEMEngine::playCdFloppyInitCluesAnim(uint16 caseType, bool floppy,
 				g_system->copyRectToScreen(briefingBase.getPixels(),
 										   briefingBase.pitch, 0, 0,
 										   kScreenWidth, kScreenHeight);
-				// _PlayInSequence @ 172b:2d35-2d50:
-				//   dstX = sx - cell[+0x8]   ; signed X anchor (miscflags)
-				//   dstY = sy - cell[+0x6]   ; signed Y anchor (rowoff)
-				// asm `SUB AX, ES:[BX+0x8]` ??? NOT the cell width.
+				// _PlayInSequence @ 172b:2d35-2d50
 				const int dstX = (int)0xcd - (int)(int16)fr.miscflags;
 				const int dstY = (int)seqY - (int)(int16)fr.rowoff;
 				blitMaskedToScreen(fr, dstX, dstY);
@@ -690,11 +613,7 @@ void EEMEngine::playCdFloppyInitCluesAnim(uint16 caseType, bool floppy,
 	}
 }
 
-// _DoInitClues @ 1a35:0411 (EEM1) / 1abf:03b3 (EEM2). Sequence:
-//   1. mark the start site / visited sites from the InitBlock
-//   2. BG PIC 0x52 + briefing palette (EEM1 0x22 / EEM2 0x39)
-//   3. partner entrance animation (CD/floppy or London variant)
-//   4. _DisplayClue(InitBlock + 2) ??? briefing dialogue
+// _DoInitClues @ 1a35:0411 (EEM1) / 1abf:03b3 (EEM2)
 void EEMEngine::doInitClues() {
 	if (!_mystery.isLoaded())
 		return;
@@ -703,9 +622,6 @@ void EEMEngine::doInitClues() {
 	if (!ib)
 		return;
 
-	// CD InitBlock: u16 caseType; u16 startSite; <clue block>.
-	// Floppy InitBlock (FUN_19bb_042f): u8 caseType; u8 nSubjects;
-	// subjects[]; u8 nDialog; dialog_records[]. No startSite.
 	const bool floppy = isFloppy();
 	const uint16 caseType = floppy ? (uint16)ib[0] : READ_LE_UINT16(ib);
 
@@ -716,8 +632,6 @@ void EEMEngine::doInitClues() {
 		_mystery._siteNumber = startSite;
 		_mystery._lastSite = startSite;
 	} else {
-		// Floppy _DoMapScreen @ 1fed:1060 (FUN_1fed_07ed) walks every
-		// site unconditionally ??? mirror that by marking all visible.
 		const uint sites = _mystery.numSites();
 		for (uint s = 0; s < sites && s < Mystery::kVisitedSiteCap; s++)
 			_mystery._onSites[s] = 1;
@@ -725,21 +639,12 @@ void EEMEngine::doInitClues() {
 		_mystery._lastSite = 0;
 	}
 
-	// Case-briefing palette. EEM1 `_DoInitClues` uses SITEPALS index 0x22;
-	// EEM2 `_DoInitClues` @ 1abf:03b3 does `_GetBackground(0x52)` then
-	// `_GetPalette(0x39)` (its 63-entry SITEPALS. shifts the UI palettes).
-	// The briefing partner animation is an ANI sprite drawn under the screen
-	// palette, so the same index fixes both the background and the anim.
 	setSitePalette(isLondon() ? 0x39 : 0x22);
 	Picture bg;
 	const bool haveBriefingBg = _picsArchive.getPicture(0x52, bg);
 	if (haveBriefingBg)
 		blitAt(bg, 0, 0);
 
-	// Case-intro partner animation. EEM2 (`_DoInitClues` @ 1abf:03b3) plays a
-	// single partner anim (Jake 0x18 / Jenny 0x71); EEM1 CD/floppy runs a
-	// game/book/nancy cycle + `_PlayInSequence` entrance. Both then feed the
-	// shared briefing dialogue below.
 	if (isLondon())
 		playLondonInitCluesAnim(caseType, bg, haveBriefingBg);
 	else
@@ -831,13 +736,7 @@ Common::String EEMEngine::parseString(const Common::String &raw,
 			// `_WordWrap @ 1b03:0456` treats `^` as a forced line
 			// break (sets `cur_width = max_width`, forcing the next
 			// loop turn to wrap at the previous space and skip the
-			// `^` itself). Without this conversion the caret falls
-			// through the default case and renders as a literal,
-			// pushing the line past the balloon's visual edge ??? the
-			// "bubbles aren't large enough" symptom. Promote it to
-			// `\n` so `Font::wordWrapText` (which honours
-			// embedded newlines) picks the same break point the
-			// original engine did.
+			// `^` itself).
 			out += '\n';
 			break;
 		default:
@@ -849,11 +748,6 @@ Common::String EEMEngine::parseString(const Common::String &raw,
 	// Strip leading spaces at the start of each emitted line. Mirrors
 	// `_DoWordWrap @ 1b66:04a7`, which advances past spaces at the
 	// start of every output line via `for (; str[last] == ' '; last++)`.
-	// ~60% of mystery-text strings carry 1-2 leading spaces in the data
-	// of the original WordWrap
-	// discards them, so we do the same before the text reaches
-	// `Font::wordWrapText` (which only trims at wrap-induced line
-	// boundaries, not at start-of-input or after an embedded '\n').
 	Common::String cleaned;
 	bool atLineStart = true;
 	for (uint i = 0; i < out.size(); i++) {
@@ -866,15 +760,9 @@ Common::String EEMEngine::parseString(const Common::String &raw,
 	return cleaned;
 }
 
+// EEM2 `_DisplayClue @ 2542:05bd` per-entry side effects
 void EEMEngine::applyClueSideEffects(const byte *c) {
 	if (isLondon()) {
-		// EEM2 `_DisplayClue @ 2542:05bd` per-entry side effects. With the
-		// shared `c = entryBase + 4` convention the EEM2 fields land at:
-		//   onsite  entry+0x22 (= c+0x1e), 5 ?? u16, high bit = CONSITE flag
-		//   offsite entry+0x2c (= c+0x28), 5 ?? u16, high bit = COFFSITE flag
-		//   gallery entry+0x36 (= c+0x32), 5 ?? u16 -> _InGallery[_newOrder[idx]]
-		//   notebook entry+0x40 (= c+0x3c), 5 ?? u16 -> _AddNotebook
-		//   jump    entry+0x4a (= c+0x46), destination site for direct travel
 		for (uint j = 0; j < 5; j++) {
 			const uint16 note = READ_LE_UINT16(c + 0x3c + j * 2);
 			if (note != 0xFFFF && note < Mystery::kCluesFoundCap)
@@ -935,11 +823,6 @@ void EEMEngine::displayClue(const byte *clueBlock) {
 	if (number == 0 || number > 32)
 		return;
 
-	// ClueEntry stride. EEM1 `_DisplayClue @ 2404:05e6` packs 62-byte entries;
-	// EEM2 `_DisplayClue @ 2542:05bd` indexes `theClue + i*0x54` (84 bytes). The
-	// per-entry fields up to +0x1e are layout-identical (so entry 0 ??? at the
-	// shared base clueBlock+4 ??? reads the same either way); only the stride and
-	// the tail fields (KD anim, notebook, onsite) move. See applyClueSideEffects.
 	const uint stride = isLondon() ? 0x54 : 62;
 
 	// Snapshot BG so per-entry character pics don't stack.
@@ -964,32 +847,19 @@ void EEMEngine::displayClue(const byte *clueBlock) {
 	//   +0x1a: Jake  voice (1-based)
 	//   +0x30..+0x39: 5 notebook entries (-1 terminated)
 	//   +0x3a..+0x3b: KD-anim number (-1 = none)
-	// _DisplayClue @ 2404:05e6: partner 1 uses its own field set only when
-	// bubText1 != -1; otherwise falls back to partner 0 fields entirely.
-	// Partner 0 always uses field 0.
 	for (uint i = 0; i < number && !shouldQuit(); i++) {
 		g_system->copyRectToScreen(bg.getPixels(), bg.pitch, 0, 0, kScreenWidth, kScreenHeight);
 		const byte *c = clueBlock + 4 + i * stride;
 
-		// EEM2 `_DisplayClue @ 2542:05bd` opens each clue with a per-entry MIDI
-		// cue read at entry+0x20 (= c+0x1c, the u16 right after the two voice
-		// slots), replacing any current track (`playMus`???`playFile` calls
-		// `stop()`). 0 = no cue; EEM1 clue entries have no such field. Gated on
-		// `_voiceOn` like `startTravelMusic` ??? the DOS gate is the separate
-		// music-on / MIDI-available flags (DAT_3036_4cc0 && DAT_2bca_146a).
 		if (isLondon() && _music && _musicOn) {
 			const uint16 clueMusic = READ_LE_UINT16(c + 0x1c);
 			if (clueMusic != 0)
 				_music->playMus(clueMusic, /* loop= */ false);
 		}
 
-		// _DisplayClue @ 2404:0635-064b: _DoKDAnim(num) runs before the
-		// speaker portrait. EEM1 stores the KD-anim number at +0x3a; EEM2
-		// `_DisplayClue @ 2542:05bd` reads it at entry+0x52 (= c+0x4e).
 		const int16 kdAnimNum = (int16)READ_LE_UINT16(c + (isLondon() ? 0x4e : 0x3a));
 		if (kdAnimNum != -1) {
 			playKdAnim((uint16)kdAnimNum);
-			// _UpdateAnimations @ 172b:09c1 reactivates the wait anim.
 			g_system->copyRectToScreen(bg.getPixels(), bg.pitch,
 									   0, 0, kScreenWidth, kScreenHeight);
 		}
@@ -1022,9 +892,7 @@ void EEMEngine::displayClue(const byte *clueBlock) {
 				// Draw over the per-entry clean BG (restored at the top of the
 				// loop), NOT into the persistent `bg` snapshot ??? baking it in
 				// makes successive speaker portraits stack instead of refresh
-				// (duplicate NPC portraits + labels). The briefing's fixed NPC
-				// persists via the entrance animation captured in the snapshot
-				// (playLondonInitCluesAnim), not via the per-entry portrait.
+				// (duplicate NPC portraits + labels).
 				blitMaskedToScreen(charPic, charX, charY);
 			}
 		}
@@ -1058,21 +926,13 @@ void EEMEngine::displayClue(const byte *clueBlock) {
 			if (haveBalloon) {
 				const int bw = MIN<int>(balloon.surface.w, kScreenWidth - bubX);
 				const int bh = MIN<int>(balloon.surface.h, kScreenHeight - bubY);
-				// _AddPicBackground: transparent colour = pic->miscflags >> 8.
 				const byte transp = (byte)(balloon.flags >> 8);
-				// _GetBalloon @ 172b:1d7d mirrors horizontally when
-				// (bubNum & 0x80) ??? for right-side speakers.
 				const bool flipBalloon = (fittedBubNum & 0x80) != 0;
 				if (bw > 0 && bh > 0) {
 					scratch.transBlitFrom(balloon.surface,
 										  Common::Point(bubX, bubY),
 										  transp, flipBalloon);
 				}
-				// Per-balloon insets at 29be:0875 (52 x 10 bytes,
-				// indexed by bubNum & 0x7F). _DisplayClue does
-				// _WordWrap(bubX + table[bub].x, bubY + table[bub].y,
-				//           table[bub].w, ...).
-				// Fallback (5, 4, 155) = entry-23 inset.
 				uint16 insetX = 5;
 				uint16 insetY = 4;
 				uint16 insetW = 155;
@@ -1082,21 +942,15 @@ void EEMEngine::displayClue(const byte *clueBlock) {
 				textW = insetW;
 				copyH = bh;
 			} else {
-				// No balloon: clear band.
 				const Common::Rect band(0, bubY, kScreenWidth,
 					MIN<int>(bubY + copyH, kScreenHeight));
 				scratch.fillRect(band, 0);
 				copyY = bubY;
 			}
 
-			// _DisplayClue @ 2404:07fe passes fontColor=0 (palette
-			// index 0 of case-briefing palette 0x22).
 			_font.drawWordWrapped(&scratch, textX, textY,
 				MAX<int>(8, textW), text, 0);
 
-			// Clamp to the screen: a malformed entry (e.g. a clue-format
-			// mismatch) must never hand copyRectToScreen a negative height,
-			// which would underflow to a multi-GB memcpy.
 			copyY = CLIP<int>(copyY, 0, kScreenHeight - 1);
 			const int copyRows = CLIP<int>(MIN<int>(copyH, kScreenHeight - copyY),
 										   0, kScreenHeight - copyY);
@@ -1107,9 +961,6 @@ void EEMEngine::displayClue(const byte *clueBlock) {
 			}
 		}
 
-		// _DisplayClue @ 2404:0833-085a ??? per-clue voice gate. The gate is on
-		// the Jenny slot regardless of partner; entries with +0x18 == 0 but
-		// +0x1a set are text-only.
 		if (_audio) {
 			const uint16 voiceJenny = READ_LE_UINT16(c + 0x18);
 			if (voiceJenny != 0 && voiceJenny != 0xFFFF) {
@@ -1143,7 +994,6 @@ void EEMEngine::displayClue(const byte *clueBlock) {
 						ev.kbd.keycode == Common::KEYCODE_ESCAPE) {
 						advance = true;
 						skipAll = true;
-						// Cut voice + spool only (keep MIDI).
 						interruptAudio(/* stopMusicToo= */ false);
 						break;
 					}
@@ -1163,7 +1013,6 @@ void EEMEngine::displayClue(const byte *clueBlock) {
 				g_system->delayMillis(10);
 			}
 			if (skipAll) {
-				// Apply remaining side-effects without rendering.
 				for (uint k = i; k < number; k++)
 					applyClueSideEffects(clueBlock + 4 + k * stride);
 				return;
@@ -1172,17 +1021,9 @@ void EEMEngine::displayClue(const byte *clueBlock) {
 
 		applyClueSideEffects(c);
 
-		// EEM2 `_DisplayClue @ 2542:05bd`: a clue entry can gate the rest of
-		// the clue behind a "check the manual / a real map" puzzle (the id is
-		// entry+0x54 = c+0x50, a `P<id>.BIN`). Run it once per mystery
-		// (`_mystery._solvedPuzzle` = `DAT_3036_6d5c`); a wrong answer aborts
-		// the remaining entries and re-prompts on the next visit.
 		if (isLondon() && !_mystery._solvedPuzzle) {
 			const uint16 puzzleId = READ_LE_UINT16(c + 0x50);
 			if (puzzleId != 0xFFFF) {
-				// Restore the clean site BG (drop this clue's bubble) first ???
-				// the DOS _Repaint before `_DoPuzzle` ??? so the puzzle (and its
-				// own hint) render on a clean background, not over the bubble.
 				g_system->copyRectToScreen(bg.getPixels(), bg.pitch, 0, 0,
 										   kScreenWidth, kScreenHeight);
 				g_system->updateScreen();
@@ -1193,8 +1034,6 @@ void EEMEngine::displayClue(const byte *clueBlock) {
 		}
 	}
 
-	// _StopTheVoice @ 1ff1:0283 effect (voice only, keep MIDI). Diverges
-	// from _DisplayClue @ 2404:05e6 which lets voice bleed past dismissal.
 	interruptAudio(/* stopMusicToo= */ false);
 }
 
@@ -1245,9 +1084,6 @@ void EEMEngine::displayFloppyDialogRecords(const byte *rec, uint count,
 	//   u8  sound    @ +9     (high bit = play voice, low 7 bits = slot)
 	//   u8  textCount@ +10
 	//   u8  textIdx[]@ +11    (1 byte per ??? low 7 bits = NOTES idx)
-	// NOTES text offsets are absolute byte offsets into the mystery
-	// buffer; read via mystery.blobAt(noteEntry[+2..3]) for Jake,
-	// +4..5 for Jenny.
 	if (!rec || !isFloppy() || !_font.isLoaded() || count == 0)
 		return;
 
@@ -1270,9 +1106,6 @@ void EEMEngine::displayFloppyDialogRecords(const byte *rec, uint count,
 	const uint32 dsz       = _mystery.dataSize();
 	const uint32 notesBase = (uint32)(notes - bufBase);
 
-	// _DisplayHotspotClue_Floppy @ 22dc:05c8 writes _TextSeen_Floppy[idx]=1
-	// inline per text. Pre-mark all up front so ESC-fast-forward still
-	// records every clue in the notebook.
 	{
 		const byte *r = rec;
 		for (uint i = 0; i < count; i++) {
@@ -1301,12 +1134,7 @@ void EEMEngine::displayFloppyDialogRecords(const byte *rec, uint count,
 			const uint slot = rec[9] & 0x7f;
 			_audio->playFloppyVoiceSlot(slot, _partner);
 		}
-		// _DisplayHotspotClue_Floppy @ 22dc:0908..0922 suspect-found:
-		//   if ((rec[9] & 0x80) == 0 && rec[9] != 0)
-		//     _InGallery_Floppy[_GalleryShuffleSeed_Floppy[rec[9]]] = 1;
-		// _GalleryShuffleSeed (DS:0x2d65) overlaps _NewOrder (DS:0x2d66)
-		// by one byte: GalleryShuffleSeed[i+1] == NewOrder[i]. So:
-		// _inGallery[_newOrder[b9 - 1]].
+
 		const uint8 b9 = rec[9];
 		if ((b9 & 0x80) == 0 && b9 != 0) {
 			const uint logicalIdx = (uint)b9 - 1;
@@ -1379,9 +1207,6 @@ void EEMEngine::displayFloppyDialogRecords(const byte *rec, uint count,
 		const int textX = ballX + textXIns;
 		const int lineH    = _font.getFontHeight();
 
-		// FUN_22dc_05c8 pagination via local_1c (set from previous text's
-		// flag bit): 0 = fresh page (redraw balloon, Y at top),
-		// 1 = continuation (append below previous lines).
 		bool firstPage  = true;
 		int  cursorY    = ballY + textYIns;
 		bool skipAll    = false;
@@ -1413,7 +1238,6 @@ void EEMEngine::displayFloppyDialogRecords(const byte *rec, uint count,
 			scratch.simpleBlitFrom(*bg.surfacePtr());
 
 			if (firstPage) {
-				// Original only redraws balloon + portrait on local_1c == 0.
 				if (picID != 0 && picID != 0xFFFF) {
 					Picture pic;
 					if (_picsArchive.getPicture(picID, pic)) {
@@ -1445,10 +1269,6 @@ void EEMEngine::displayFloppyDialogRecords(const byte *rec, uint count,
 			const bool isLastText  = (t + 1 == textCount);
 			const bool isLastRec   = (i + 1 == count);
 
-			// "Click to continue" indicator: PIC 0xa0 ("more" arrow) or
-			// PIC 0xa1 (end). _DisplayHotspotClue_Floppy @ 22dc:08aa
-			// (mid-page) / @ 22dc:08c0 (end-of-record). lastIndicator == 0
-			// matches the original param_2 == 0 "no indicator" case.
 			bool waitNeeded   = false;
 			bool drawArrow    = false;
 			bool useEndPic    = false;
@@ -1457,13 +1277,12 @@ void EEMEngine::displayFloppyDialogRecords(const byte *rec, uint count,
 					firstPage = false;
 				} else {
 					waitNeeded = true;
-					drawArrow  = true;          // PIC 0xa0
+					drawArrow  = true;
 					useEndPic  = false;
 				}
 			} else {
 				waitNeeded = true;
 				if (!isLastRec) {
-					// More records follow ??? param_2 = 1 ??? PIC 0xa0.
 					drawArrow = true;
 					useEndPic = false;
 				} else {
@@ -1472,7 +1291,7 @@ void EEMEngine::displayFloppyDialogRecords(const byte *rec, uint count,
 						useEndPic = false;
 					} else if (lastIndicator == 2) {
 						drawArrow = true;
-						useEndPic = true;       // PIC 0xa1
+						useEndPic = true;
 					}
 				}
 			}
@@ -1509,8 +1328,6 @@ void EEMEngine::displayFloppyDialogRecords(const byte *rec, uint count,
 }
 
 void EEMEngine::displayFloppyBriefing(const byte *initBlock) {
-	// FUN_19bb_042f @ 19bb:042f: walks nDialog = ib[2 + nSubjects] records
-	// starting at ib + 3 + nSubjects, dispatching each to FUN_22dc_05c8.
 	if (!initBlock || !isFloppy())
 		return;
 	const uint8 nSubjects = initBlock[1];
@@ -1520,12 +1337,6 @@ void EEMEngine::displayFloppyBriefing(const byte *initBlock) {
 }
 
 void EEMEngine::displayFloppyHotspotDialog(uint siteNum, uint hotIdx) {
-	// FUN_22dc_0b80 @ 22dc:0b80 + FUN_1652_00e6 @ 1652:00e6 +
-	// FUN_1652_006c @ 1652:006c. site_data[+6..7] -> per-hotspot list:
-	//   for each hotspot:
-	//     main record (11 + textCount bytes)
-	//     u8 contFlags  (low 7 = cont count, high bit = partner pose)
-	//     contCount x { record (11 + textCount bytes) }
 	if (!_mystery.isLoaded() || !isFloppy())
 		return;
 	const byte *site = _mystery.siteData(siteNum);
@@ -1548,8 +1359,7 @@ void EEMEngine::displayFloppyHotspotDialog(uint siteNum, uint hotIdx) {
 	}
 	if (off >= _mystery.dataSize())
 		return;
-	// Snapshot clean site BG and restore between main + continuation calls
-	// so each displayFloppyDialogRecords sees a bubble-free background.
+
 	Graphics::ManagedSurface siteBG(kScreenWidth, kScreenHeight,
 		Graphics::PixelFormat::createFormatCLUT8());
 	{
@@ -1567,10 +1377,7 @@ void EEMEngine::displayFloppyHotspotDialog(uint siteNum, uint hotIdx) {
 		contFlagsByte = bufBase[off + mainLen];
 		contCount = contFlagsByte & 0x7F;
 	}
-	// _HandleHotspotClick_Floppy @ 1652:00e6 derives param_2:
-	//   contFlagsByte == 0  -> 0 (no indicator)
-	//   high bit set        -> 1 (PIC 0xa0, "more")
-	//   low 7 bits non-zero -> 2 (PIC 0xa1, alt end)
+
 	uint mainIndicator = 0;
 	if (contFlagsByte != 0) {
 		mainIndicator = (contFlagsByte & 0x80) ? 1 : 2;
@@ -1581,12 +1388,10 @@ void EEMEngine::displayFloppyHotspotDialog(uint siteNum, uint hotIdx) {
 	const uint32 contOff = off + mainLen + 1;
 	if (contOff >= _mystery.dataSize())
 		return;
-	// Wipe main bubble so the continuation chain snapshots a clean BG.
+
 	g_system->copyRectToScreen(siteBG.getPixels(), siteBG.pitch,
 							   0, 0, kScreenWidth, kScreenHeight);
 	g_system->updateScreen();
-	// _DisplayDialogContinuations_Floppy @ 1652:006c: lastIndicator=0
-	// means no indicator on the final continuation.
 	displayFloppyDialogRecords(bufBase + contOff, contCount, 0);
 }
 
@@ -1599,10 +1404,6 @@ bool EEMEngine::areYouSure() {
 		g_system->unlockScreen();
 	}
 
-	// _AreYouSure @ 1a35:0a5c (CD) / FUN_19bb_0b43 (floppy):
-	//   PIC 0x136 = dialog body; PIC 0x1fd/0x1fe = YES/NO pressed buttons.
-	//   YES hit: (x+0x0c, y+0x23) .. (x+0x20, y+0x32)
-	//   NO  hit: (x+0x60, y+0x23) .. (x+0x74, y+0x32)
 	Picture dialogPic;
 	Picture yesPic;
 	Picture noPic;
diff --git a/engines/eem/detection.cpp b/engines/eem/detection.cpp
index 714b2d81821..c27f37a7bfb 100644
--- a/engines/eem/detection.cpp
+++ b/engines/eem/detection.cpp
@@ -68,10 +68,7 @@ const ADGameDescription gameDescriptions[] = {
 		GUI_OPTIONS_EEM_FLOPPY
 	},
 	{
-		// Eagle Eye Mysteries in London (EEM2CD.EXE), the sequel.
-		// It reuses this engine's resource formats (DBD/DBX archives,
-		// SITEPALS palettes, FONT.FNT); reimplementation is in progress,
-		// so it stays flagged unstable until fully supported.
+		// Eagle Eye Mysteries in London 
 		"eem2",
 		"CD",
 		AD_ENTRY2s("EEM2CD.EXE", "211a376b23a1b6259d0c36cf46d26ed4", 172560,
diff --git a/engines/eem/eem.cpp b/engines/eem/eem.cpp
index 4f455d22562..136a45ab278 100644
--- a/engines/eem/eem.cpp
+++ b/engines/eem/eem.cpp
@@ -48,8 +48,6 @@
 
 namespace EEM {
 
-const uint kNumSitePals = 40;  // SITEPALS: 40 * 768 = 30720
-
 // 1-based picture/palette IDs.
 const uint kPicEAKidsLogo      = 0x54;  // _ShowEAKids
 const uint kPicHighScoreLogo   = 0x20c; // _ShowHScoreLogo
@@ -89,8 +87,6 @@ Common::String makeLondonProfileDisplayName(const Common::String &first,
 
 Common::String makeLondonProfileKey(const Common::String &first,
 									 const Common::String &last) {
-	// EEM2 `_GenerateFilename @ 1cd3:02fe`: copy the first 7 bytes of
-	// FirstName, pad to 8 from LastName, then replace spaces/dots with '_'.
 	Common::String cleanFirst = first;
 	Common::String cleanLast = last;
 	cleanFirst.trim();
@@ -220,13 +216,9 @@ EEMEngine::EEMEngine(OSystem *syst, const ADGameDescription *gameDesc)
 	_variant = (gameDesc && gameDesc->extra &&
 				Common::String(gameDesc->extra).contains("Floppy"))
 				 ? kVariantFloppy : kVariantCD;
-	// EEM2 ("...in London") ships as a separate detection entry (gameId
-	// "eem2"); it reuses this engine with London-specific data.
 	if (gameDesc && gameDesc->gameId &&
 		Common::String(gameDesc->gameId) == "eem2")
 		_variant = kVariantLondonCD;
-	// EEM2 ships its own `_AnimationSequences` ??? many partner/KD scripts
-	// differ from EEM1's, so route `findAnimScript` to the EEM2 table.
 	setLondonAnimScripts(isLondon());
 	_language = gameDesc ? gameDesc->language : Common::EN_ANY;
 }
@@ -270,7 +262,7 @@ bool EEMEngine::anyMysterySolved(uint lo, uint hi) const {
 
 bool EEMEngine::mysteryTierRange(uint stage, uint &lo, uint &hi) const {
 	if (isLondon()) {
-		// EEM2/London: two 25-case books, no BOOK3.NME
+		// EEM2/London: two 25-case books
 		// (`_DisplayCorrect @ 1ea1:0619`).
 		switch (stage) {
 		case 1: lo = 0x01; hi = 0x19; return true;  //  1..25
@@ -299,17 +291,12 @@ void EEMEngine::advanceChainStageAfterSolve(uint mysteryNum) {
 		return;
 
 	const uint oldStage = _chainStage;
-	// EEM1 only: Book 2 repeats the Book 1 cases, so this option keeps the
-	// original solve state but jumps the active chain straight to Book 3.
-	// London has 50 distinct cases and no Book 3, so the option does not apply.
 	if (!isLondon() && _chainStage == 1 &&
 		ConfMan.getBool("skip_repeated_cases"))
 		_chainStage = 3;
 	else
 		_chainStage++;
 
-	// London has only two books: `_DisplayCorrect @ 1ea1:0619` collapses
-	// chainStage 3 straight to 4 (every case solved ??? game complete).
 	if (isLondon() && _chainStage == 3)
 		_chainStage = 4;
 
@@ -319,8 +306,6 @@ void EEMEngine::advanceChainStageAfterSolve(uint mysteryNum) {
 }
 
 void EEMEngine::applySkipRepeatedCasesOption() {
-	// EEM1 only ??? the option jumps past the repeated Book 2 to Book 3, which
-	// London does not have (see `advanceChainStageAfterSolve`).
 	if (isLondon() || !ConfMan.getBool("skip_repeated_cases"))
 		return;
 	if (_mystery.isLoaded())
@@ -335,7 +320,6 @@ void EEMEngine::applySkipRepeatedCasesOption() {
 }
 
 Common::Error EEMEngine::run() {
-	// _SetMode13X @ 1000:0358 ??? VGA mode 13h.
 	initGraphics(kScreenWidth, kScreenHeight);
 
 	if (!isFloppy() && ConfMan.getBool("restored_content")) {
@@ -367,13 +351,6 @@ Common::Error EEMEngine::run() {
 	_audio->setVoiceEnabled(_voiceOn);
 	syncSoundSettings();
 
-	// CD `_main @ 1a35:0f59` and floppy `_main_Floppy @ 19bb:1012` both
-	// load `_GetPicture(0x50)` as the active mouse pointer before
-	// `_InitMouse`. PIC 0x51 is present in both archives but has no
-	// executable xrefs and appears to be the wait cursor.
-	// CD's `_SwitchMouse` supports swapping to a hotspot cursor ID stored
-	// at search record +0x0c, but the shipped CD mystery data only uses
-	// cursor 0; floppy search records have no cursor-id field.
 	installMouseCursor(_picsArchive, false);
 	CursorMan.showMouse(false);
 
@@ -413,10 +390,6 @@ Common::Error EEMEngine::run() {
 	if (resumed)
 		goto screenLoop;
 
-	// EEM2 ("Eagle Eye Mysteries in London"): opening sequence, then screen
-	// 8 profile selection, screen 9 partner selection. A freshly-created
-	// detective starts the training case (M0) after choosing Jake/Jennifer;
-	// an existing profile resumes its saved menu/map state.
 	if (isLondon()) {
 		runLondonStartup();
 		if (!shouldQuit()) {
@@ -439,37 +412,8 @@ Common::Error EEMEngine::run() {
 		goto screenLoop;
 	}
 
-	// _DoOpeningAnims @ 2520:082a:
-	//   EA Kids logo (PIC) -> HighScore logo (PIC) -> Storm logo
-	//   (BOLT.ANM) -> [music starts] -> 20 character-intro anims
-	//   (ANIM01.A..ANIM20.A) -> [music restarts] -> TITLE.ANM. Click /
-	//   any key skips one clip; ESC raises _skipIntro so each step bails
-	//   out.
-	// Music timing (2520:0883 + 2520:0918):
-	//   - The three logos and `_InitMysterySounds(0x3c)` run BEFORE any
-	//     `_MIDIPlayFile` ??? those segments are voice-only.
-	//   - Theme starts with `_LoopMIDI = 0x7fff` right before the
-	//     ANIM01..ANIM20 loop (2520:0883).
-	//   - After the loop the original calls `_CleanMysterySounds` then
-	//     `_MIDIPlayFile("theme.xmi")` again with `_LoopMIDI = 0xffff`
-	//     (2520:0918) to restart for TITLE.ANM.
-	//   - `_StopMIDI()` runs on keypress at the title screen (2520:094c).
 	_skipIntro = false;
 	if (isFloppy()) {
-		// Floppy opening ??? `FUN_23d2_039c @ 23d2:039c`:
-		//   FUN_23d2_0170()  ??? clear palette
-		//   FUN_23d2_004b()  ??? set up timer
-		//   FUN_23d2_050c()  ??? PIC 0x54 (EA Kids, palette 0x25)
-		//   FUN_23d2_06c6()  ??? PIC 0x20c (High Score, palette 0x27)
-		//   FUN_23d2_0605()  ??? PIC 0x20b (Storm, palette 0x26) AND
-		//                      voice slot 25 = "thunder.voc" (via Jake
-		//                      voice table 2608:0f0e slot 25 ???
-		//                      2608:11ac).
-		//   _MIDIPlayFile("theme.xmi", loop=1)
-		//   _PlayANM(0) ??? CHAT.ANM (filename table 2608:14fe[0] ???
-		//                  "chat.anm" at 2608:150a)
-		//   _PlayANM(1) ??? MOVIE.ANM (table[1] ??? 2608:1513)
-		// TITLE.ANM is shown later by screen-0xb handler @ 19bb:0ebc.
 		if (!shouldQuit() && !_skipIntro)
 			showEAKidsLogo();
 		if (!shouldQuit() && !_skipIntro)
@@ -488,10 +432,6 @@ Common::Error EEMEngine::run() {
 		showEAKidsLogo();
 		if (!shouldQuit() && !_skipIntro)
 			showHighScoreLogo();
-		// Storm Software logo: voice + animation. `_ShowStormLogo @
-		// 2520:0707` calls `_LoadSoundName("thunder.voc")` (29be:177d)
-		// and passes the buffer to `OpenDifferenceAnimation_Sound` so
-		// the thunder roar plays alongside the lightning-bolt BOLT.ANM.
 		if (!shouldQuit() && !_skipIntro) {
 			if (_audio)
 				_audio->playVoc(Common::Path("THUNDER.VOC"));
@@ -502,7 +442,7 @@ Common::Error EEMEngine::run() {
 			if (_audio)
 				_audio->stopVoice();
 		}
-		// _InitMysterySounds(0x3c) @ 2520:086a ??? load M60.SDX/SDB.
+
 		if (!shouldQuit() && !_skipIntro && _audio)
 			_audio->initMysterySounds(60);
 		if (!shouldQuit() && !_skipIntro && _music)
@@ -514,17 +454,13 @@ Common::Error EEMEngine::run() {
 			Common::String name = Common::String::format("ANIM%02d.A", i);
 			playAnm(Common::Path(name), 120,
 					/* holdLastFrame= */ false, fadeIn);
-			// _SpoolSound(uVar3 - 1) @ 2520:08c2 ??? per-anim VO, skipped
-			// when uVar3 == 0x14 @ 2520:08a8.
 			if (!shouldQuit() && !_skipIntro && i != 20 && _audio) {
 				_audio->spoolSound((uint)(i - 1));
 				_audio->waitForSpoolDone();
 			}
 		}
-		// _CleanMysterySounds @ 2520:0903.
 		if (_audio)
 			_audio->cleanMysterySounds();
-		// _MIDIPlayFile("theme.xmi") @ 2520:0918.
 		if (!shouldQuit() && !_skipIntro && _music)
 			_music->playFile(Common::Path("THEME.XMI"), /* loop= */ true);
 		if (!shouldQuit() && !_skipIntro)
@@ -539,13 +475,11 @@ Common::Error EEMEngine::run() {
 		goto screenLoop;
 	}
 
-	// Title(B) -> screen 8 (profile) -> 9 (partner) -> C (action) ->
-	// A (case selection) -> site loop.
 	CursorMan.showMouse(true);
 
 	if (_music)
 		_music->stop();
-	// screen8_handler @ 1c33:1012.
+
 	if (!shouldQuit())
 		doProfilePicker();
 	if (!shouldQuit())
@@ -555,16 +489,6 @@ Common::Error EEMEngine::run() {
 	if (!shouldQuit())
 		doChoosePartner();
 
-	// Drop into the screen-driver state machine ??? same pattern as
-	// `_ScreenDriver @ 1a35:0dc1` + the per-screen handler table at
-	// 1a35:0e5e. Sentinel `kScreenInvalid` (0xFFFF) ends the loop.
-	// `_DoChoosePartner @ 1a35:099d` writes `_NextScreen = 0xc` (the
-	// original `_ActionScreen`, which is separate from handler 10's
-	// `_DoChooseMystery` / `_CaseSelection`).
-	// Mid-mystery resume: if the loaded save had `hasMystery` set,
-	// drop straight to MAP rather than the action menu so the player
-	// doesn't walk back through the case picker (which would
-	// `_mystery.load()` fresh and discard site / clue progress).
 	if (!shouldQuit() && !resumed)
 		_nextScreen = _mystery.isLoaded() ? kScreenMap : kScreenAction;
 screenLoop:
@@ -574,9 +498,6 @@ screenLoop:
 
 		switch (current) {
 		case kScreenTitle:
-			// Floppy handler 0xb _HandleScreen11_Title_Floppy ->
-			// _DoTitle_Floppy -> _PlayTitleANM_Floppy(1)=TITLE.ANM.
-			// Writes _NextScreen=8 (profile picker).
 			_nextScreen = kScreenProfile;
 			if (isFloppy()) {
 				CursorMan.showMouse(false);
@@ -588,10 +509,6 @@ screenLoop:
 			break;
 
 		case kScreenAction:
-			// Top-level post-profile / post-mystery menu. `_ActionScreen
-			// @ 1c33:195b` shows the 5-entry "Choose A Mystery /
-			// Practice / ScrapBook 1..3" picker; writes _NextScreen=0xa
-			// only when the player picks "Choose A Mystery".
 			_nextScreen = kScreenInvalid;
 			doActionScreen();
 			if (_nextScreen == kScreenInvalid && _mystery.isLoaded())
@@ -599,8 +516,6 @@ screenLoop:
 			break;
 
 		case kScreenChooseMystery:
-			// Handler 10 @ 1a35:0e0e -> _DoChooseMystery (presets
-			// _NextScreen=0 INIT_CLUES) -> _CaseSelection.
 			_nextScreen = kScreenInvalid;
 			doCaseSelection();
 			if (_nextScreen == kScreenInvalid && _mystery.isLoaded())
@@ -608,8 +523,6 @@ screenLoop:
 			break;
 
 		case kScreenInitClues:
-			// Handler 0 @ 1a35:0e14 -> _PreLoad + _DoInitClues, writes
-			// _NextScreen=1 (MAP).
 			doInitClues();
 			_nextScreen = _mystery.isLoaded() ? kScreenMap
 											  : kScreenAction;
@@ -617,11 +530,6 @@ screenLoop:
 
 		case kScreenMap:
 		case kScreenMapAlt:
-			// Handler 1/2 @ 1a35:0e25 -> `_DoMapScreen @ 20fe:120b`
-			// (floppy 19bb:0ef3 -> 1fed map code), which manages its
-			// own _NextScreen writes: 3 (site clicked), 6 (setup), or
-			// 0xffff (quit). After `doBigMap` returns the natural
-			// next state is SITE.
 			doBigMap();
 			if (!_mystery.isLoaded())
 				_nextScreen = kScreenAction;
@@ -630,9 +538,6 @@ screenLoop:
 			break;
 
 		case kScreenSite:
-			// Handler 3 @ 1a35:0e2c -> `_DoSiteLoop @ 168d:03f4`
-			// (floppy dispatches through 1652). Site writes _NextScreen
-			// for PDA / map rather than entering those as nested modals.
 			doSiteLoop();
 			if (!_mystery.isLoaded())
 				_nextScreen = kScreenAction;
@@ -641,8 +546,6 @@ screenLoop:
 			break;
 
 		case kScreenNotebook:
-			// Handler 4 ??? PDA notebook screen. Button handler writes
-			// 2 (map), 3 (site), 5 (gallery), or 7 (accuse).
 			doNotebook();
 			if (!_mystery.isLoaded() && _nextScreen != kScreenAction)
 				_nextScreen = kScreenAction;
@@ -651,8 +554,6 @@ screenLoop:
 			break;
 
 		case kScreenGallery:
-			// Handler 5 ??? suspect gallery. ESC and the site button
-			// write 3, the map button writes 2, the PDA button writes 4.
 			doGallery();
 			if (!_mystery.isLoaded() && _nextScreen != kScreenAction)
 				_nextScreen = kScreenAction;
@@ -661,9 +562,6 @@ screenLoop:
 			break;
 
 		case kScreenSetup:
-			// Handler 6 @ 1a35:0e48 -> _DoSetup. EEM1 = seg-1f78 (`doSetup`);
-			// EEM2/London = seg-2046 (`doSetupLondon`, 4 toggles + own layout).
-			// Entered from BigMap setup button (_NextScreen=6 @ 20fe:0c33).
 			if (isLondon())
 				doSetupLondon();
 			else
@@ -671,9 +569,6 @@ screenLoop:
 			break;
 
 		case kScreenProfile:
-			// Handler 8: CD screen8_handler @ 1c33:1012 ->
-			// _NewPlayer; floppy _HandleScreen8_NewPlayer_Floppy @
-			// 19bb:0ec2 writes screen 9.
 			_nextScreen = kScreenInvalid;
 			_mystery.clear();
 			doProfilePicker();
@@ -697,8 +592,6 @@ screenLoop:
 			break;
 
 		case kScreenAccuse:
-			// Handler 7 ??? accusation flow. Failed accusation returns to
-			// `_LastScreen`; a correct solution writes 0xc (ACTION).
 			doAccuse();
 			if (!_mystery.isLoaded() && _nextScreen != kScreenAction)
 				_nextScreen = kScreenAction;
@@ -727,9 +620,6 @@ void EEMEngine::setInteractiveMouseCursor(bool active) {
 }
 
 void EEMEngine::setHotspotMouseCursor(bool active) {
-	// EEM2 swaps the cursor SHAPE per hotspot (see setSiteHotspotCursorId), so
-	// the EEM1 red-outline recolor doesn't apply. The bool path only resets to
-	// the default arrow (cursor 0) when leaving a hotspot / the site loop.
 	if (isLondon()) {
 		if (!active)
 			setSiteHotspotCursorId(0);
@@ -741,7 +631,6 @@ void EEMEngine::setHotspotMouseCursor(bool active) {
 void EEMEngine::setSiteHotspotCursorId(int cursorId) {
 	if (!isLondon())
 		return;
-	// `_SwitchMouse @ 17ee:2c83`: cursor 4 (Jake hand) becomes 5 for Jenny.
 	if (cursorId == 4 && _partner == kPartnerJenny)
 		cursorId = 5;
 	if (cursorId < 0 || cursorId >= (int)ARRAYSIZE(kLondonCursorPics))
@@ -766,7 +655,6 @@ void EEMEngine::setSiteHotspotCursorId(int cursorId) {
 }
 
 bool EEMEngine::openArchives() {
-	// _InitGraphicsSystem @ 172b:0145.
 	if (!_picsArchive.open(Common::Path("PICS.DBD"), Common::Path("PICS.DBX"))) {
 		warning("PICS archive missing");
 		return false;
@@ -779,10 +667,6 @@ bool EEMEngine::openArchives() {
 		warning("SITES archive missing ??? site backgrounds disabled");
 	if (!_balloonArchive.open(Common::Path("BALLOON.DBD"), Common::Path("BALLOON.DBX")))
 		warning("BALLOON archive missing ??? clue text will lack balloons");
-	// `_GetButton @ 172b:199d` reads from this archive (see strings
-	// 'button.dbd' / 'Button.DBX' at 29be:06bf / 29be:04bb). Each
-	// per-site map marker (used by `_StampButtons @ 20fe:0d2f` and
-	// looked up via MapData[+0]) lives here.
 	if (!_buttonArchive.open(Common::Path("BUTTON.DBD"), Common::Path("BUTTON.DBX")))
 		warning("BUTTON archive missing ??? map markers will be unlabelled");
 	return true;
@@ -790,8 +674,6 @@ bool EEMEngine::openArchives() {
 
 bool EEMEngine::loadSitePalettes() {
 	Common::File f;
-	// EEM1 ships "SITEPALS" (40 palettes); EEM2/London ships "SITEPALS."
-	// with a trailing dot (63 palettes). _ReadPalettes @ 17ee:0cdb.
 	const char *palFile = isLondon() ? "SITEPALS." : "SITEPALS";
 	if (!f.open(Common::Path(palFile))) {
 		warning("%s missing", palFile);
@@ -808,12 +690,8 @@ bool EEMEngine::loadSitePalettes() {
 }
 
 bool EEMEngine::getSitePalette(uint num, byte *out) const {
-	// EEM1 SITEPALS has kNumSitePals (40) entries; EEM2/London SITEPALS.
-	// has 63. Validate against the actual loaded buffer so both work.
 	if (_sitePals.size() < (num + 1) * kPalSize)
 		return false;
-	// SITEPALS stores 6-bit VGA-DAC values (0..63); ScummVM expects
-	// 8-bit (0..255), so left-shift by 2 like the original VGA hardware.
 	const byte *src = _sitePals.data() + num * kPalSize;
 	for (uint i = 0; i < kPalSize; i++)
 		out[i] = (byte)(src[i] << 2);
@@ -848,13 +726,6 @@ bool EEMEngine::setAnmPalette(const Common::Path &anmPath) {
 }
 
 void EEMEngine::interruptAudio(bool stopMusicToo) {
-	// Mirrors `_CleanMysterySounds @ 202f:05a5` + `_StopMIDI @
-	// 20a2:0512` ??? both fire when the player aborts the opening-anim
-	// chain or dismisses the title (`_DoOpeningAnims` writes
-	// `_LoopMIDI = 0; _StopMIDI();` after the title-input loop).
-	// Conversation / clue-dialog skip paths pass `stopMusicToo = false`
-	// so the site / briefing MIDI keeps going across an ESC ??? only the
-	// per-line voice + spool need to stop.
 	if (_audio) {
 		_audio->stopVoice();
 		_audio->stopSpool();
@@ -902,8 +773,6 @@ void EEMEngine::playAnm(const Common::Path &path, uint frameDelayMs,
 		}
 		g_system->updateScreen();
 
-		// Original uses _CheckFrameRate / _kbhit; fixed delay here.
-		// ESC sets _skipIntro and interrupts audio.
 		const uint32 frameStart = g_system->getMillis();
 		bool aborted = false;
 		while (g_system->getMillis() - frameStart < frameDelayMs && !aborted) {
@@ -926,10 +795,6 @@ void EEMEngine::playAnm(const Common::Path &path, uint frameDelayMs,
 					break;
 				}
 			}
-			// Refresh cursor overlay every tick ??? otherwise
-			// the cursor only redraws when the next frame is blitted
-			// (~8 Hz at 120 ms), perceived as choppy during long
-			// animations like SCRAPBK.ANI.
 			g_system->updateScreen();
 			g_system->delayMillis(5);
 		}
@@ -938,7 +803,6 @@ void EEMEngine::playAnm(const Common::Path &path, uint frameDelayMs,
 	}
 
 	if (holdLastFrame && !shouldQuit() && !_skipIntro) {
-		// _DoOpeningAnims tail: while (!keyDataAvailable).
 		while (!shouldQuit()) {
 			Common::Event ev;
 			bool clicked = false;
@@ -965,14 +829,12 @@ void EEMEngine::playAnm(const Common::Path &path, uint frameDelayMs,
 			g_system->updateScreen();
 			g_system->delayMillis(20);
 		}
-		// _DoOpeningAnims @ 2520:0945: _LoopMIDI=0; _StopMIDI().
 		if (_music)
 			_music->stop();
 	}
 }
 
 void EEMEngine::blitAt(const Picture &pic, int x, int y) {
-	// Clip against the 320x200 frame buffer.
 	const int w = MIN<int>(pic.surface.w, kScreenWidth - x);
 	const int h = MIN<int>(pic.surface.h, kScreenHeight - y);
 	if (w <= 0 || h <= 0)
@@ -982,9 +844,6 @@ void EEMEngine::blitAt(const Picture &pic, int x, int y) {
 }
 
 void EEMEngine::waitForInput(uint32 maxMs) {
-	// ESC: _skipIntro + interruptAudio (matches _CleanMysterySounds +
-	// _StopMIDI around _DoOpeningAnims title wait).
-	// Only Return/KP-Enter/Space/Escape advance.
 	setInteractiveMouseCursor(false);
 	const uint32 startMs = g_system->getMillis();
 	while (!shouldQuit() && (g_system->getMillis() - startMs < maxMs)) {
@@ -1017,12 +876,7 @@ void EEMEngine::waitForInput(uint32 maxMs) {
 	}
 }
 
-// _OpenColorCycle @ 2520:04f7. Rotate `fpal[start..end]` by one slot:
-//   saved = fpal[end]
-//   for u = end..start+1: fpal[u] = fpal[u-1]
-//   fpal[start] = saved
-// If `show`, upload `end - start` entries (fpal[start..end-1]) ??? note that
-// fpal[end] is rotated in memory but intentionally not uploaded each tick.
+// _OpenColorCycle @ 2520:04f7. Rotate `fpal[start..end]` by one slot
 void openColorCycle(byte *fpal, uint8 start, uint8 end, bool show) {
 	if (end <= start)
 		return;
@@ -1042,32 +896,8 @@ void openColorCycle(byte *fpal, uint8 start, uint8 end, bool show) {
 												   end - start);
 	}
 }
-
+// _ShowEAKids @ 2520:05f0:
 void EEMEngine::showEAKidsLogo() {
-	// _ShowEAKids @ 2520:05f0:
-	//   _GetPicture(0x54) + memcpy to 0xa000 (VGA).
-	//   _GetPalette(0x25) loads pal 0x25 into _fpal (NOT uploaded to DAC).
-	//   FRAME_RATE = 0x19 (25 fps); _InitFrameReg.
-	//   for j in 0..1: show = j;
-	//     for u in 0..0x37 (= 55):
-	//       if (show) wait for next 25-fps tick (abort on key/click).
-	//       _OpenColorCycle(0x01, 0x6e, show)   // bg / outer ring shimmer
-	//       _OpenColorCycle(0x81, 0xee, show)   // inner gradient shimmer
-	//       if (--delay == 0) {
-	//         delay = 8;
-	//         _OpenColorCycle(0x70, 0x80, show) // mid band
-	//       }
-	//   if (!abort) {
-	//     for i in 0..5: _OpenColorCycle(0x70, 0x80, 1);
-	//     for i in 0..0x23: wait one frame;
-	//   }
-	//   _OpenFadeOut().
-	//
-	// Pass 1 (j=0, show=0) pre-rolls _fpal 55 frames in memory only ??? no
-	// DAC upload, no frame sync. Pass 2 (j=1, show=1) uploads each shift
-	// at 25 fps. Without the pre-roll, the logo first appears at the
-	// unrotated palette-0x25 phase instead of the intended "55-shifts-in"
-	// phase.
 	Picture pic;
 	if (!_picsArchive.getPicture(kPicEAKidsLogo, pic)) {
 		warning("EA Kids logo (%u) load failed", kPicEAKidsLogo);
@@ -1075,16 +905,13 @@ void EEMEngine::showEAKidsLogo() {
 	}
 	blitAt(pic, 0, 0);
 
-	// _GetPalette(0x25) ??? load into our shadow buffer; do not upload.
-	// The logo bitmap is on screen but invisible until the first
-	// _OpenColorCycle(..., show=1) upload in pass 2 lights it up.
 	byte fpal[kPalSize];
 	if (!getSitePalette(kPalEAKids, fpal)) {
 		warning("EA Kids palette (%u) load failed", kPalEAKids);
 		return;
 	}
 
-	const uint kFrameMs = 40;  // FRAME_RATE = 0x19 (25 fps).
+	const uint kFrameMs = 40;
 	bool aborted = false;
 
 	for (uint j = 0; j < 2 && !aborted && !shouldQuit(); j++) {
@@ -1138,13 +965,11 @@ void EEMEngine::showEAKidsLogo() {
 	g_system->updateScreen();
 	waitForInput(0x23 * kFrameMs);
 
-	// _OpenFadeOut @ 2520:0093 ??? 16 linear steps from current palette to black.
 	fadeCurrentPaletteToBlack();
 }
 
+// _ShowHScoreLogo @ 2520:0799: PIC 0x20c + palette 0x27;
 void EEMEngine::showHighScoreLogo() {
-	// _ShowHScoreLogo @ 2520:0799: PIC 0x20c + palette 0x27;
-	// _OpenFadeIn; 50-tick wait @ 25 fps; _OpenFadeOut.
 	Picture pic;
 	if (!_picsArchive.getPicture(kPicHighScoreLogo, pic)) {
 		warning("HighScore logo (%u) load failed", kPicHighScoreLogo);
@@ -1152,7 +977,6 @@ void EEMEngine::showHighScoreLogo() {
 	}
 	blitAt(pic, 0, 0);
 
-	// Force black before fade-in to avoid a 1-frame full-logo flash.
 	byte target[kPalSize];
 	if (!getSitePalette(kPalHighScore, target)) {
 		warning("HighScore palette (%u) load failed", kPalHighScore);
@@ -1163,17 +987,12 @@ void EEMEngine::showHighScoreLogo() {
 	g_system->updateScreen();
 	fadePaletteFromBlack(target);
 
-	// 50 ticks @ 25 fps.
 	waitForInput(2000);
 
 	fadeCurrentPaletteToBlack();
 }
 
 void EEMEngine::showLondonLogo(uint picId, uint palId, uint holdMs) {
-	// Parameterised twin of `showHighScoreLogo` for EEM2's opening logos
-	// (`_ShowEAKids` @ 2721:05e3 and `_ShowHScoreLogo` @ 2721:084d both do
-	// _GetPicture(pic) -> blit -> _GetPalette(pal) -> fade in -> hold ->
-	// fade out).
 	Picture pic;
 	if (!_picsArchive.getPicture(picId, pic) || pic.surface.empty()) {
 		warning("London logo PIC 0x%x load failed", picId);
@@ -1211,15 +1030,6 @@ bool EEMEngine::startLondonTrainingMystery() {
 }
 
 void EEMEngine::runLondonStartup() {
-	// Full opening sequence ??? EEM2 `_DoOpeningAnims` @ 2721:08e6:
-	//   _ShowEAKids   @ 2721:05e3 ??? PIC 0x54,  palette 0x3c
-	//   FUN_2721_07be @ 2721:07be ??? PIC 0x20c, palette 0x3e (publisher logo)
-	//   _ShowStormLogo@ 2721:0729 ??? anim 0 ("bolt.anm") + "thunder.voc"
-	//   _MIDIPlay(0x65)=MUS00101.XMI; anim 1 ("movie.anm")
-	//   _MIDIPlay(0x66)=MUS00102.XMI (loop); anim 2 ("wave.anm"), looped
-	//   _MIDIPlay(0x67)=MUS00103.XMI; fade out
-	// Anim names come from the table @ DS:1b54 -> {bolt,movie,wave}.anm;
-	// _MIDIPlay(n) maps to MUS%05d.XMI (n).
 	const uint32 kHoldForever = 0xFFFFFFFFu;
 	CursorMan.showMouse(false);
 	_skipIntro = false;
@@ -1273,15 +1083,8 @@ void EEMEngine::runLondonStartup() {
 	debugC(1, kDebugGeneral, "EEM2 (London): intro + profile selection done");
 }
 
+// `_NewPlayer` @ 1cd3:0f27 ??? character creation over background PIC 0xc
 void EEMEngine::showLondonCharSelect() {
-	// `_NewPlayer` @ 1cd3:0f27 ??? character creation over background PIC 0xc
-	// (palette 0). Two text fields then a male/female player-gender pick
-	// (left/right arrow 0x4b/0x4d -> DAT_4c4c, Enter confirms). The
-	// field/box rects are constants in EEM2's data segment (read at
-	// 2bca:0e3a); they map to the `pr` player record's FirstName[12] /
-	// LastName[20]:
-	//   first name : (54,75)-(151,85)    last name : (167,75)-(266,85)
-	//   male       : (110,116)-(120,122) female       : (190,116)-(200,122)
 	debugC(1, kDebugGeneral, "EEM2 (London): character creation");
 
 	const Common::Rect kFirstRect(54, 75, 151, 85);
@@ -1289,10 +1092,7 @@ void EEMEngine::showLondonCharSelect() {
 	const Common::Rect kMaleBox(110, 116, 120, 122);
 	const Common::Rect kFemaleBox(190, 116, 200, 122);
 	const uint kMaxFirst = 12, kMaxLast = 20;
-	// `_GetNameString @ 1cd3:0ddc` draws both the typed name and the caret in
-	// colour 0x22 (the passport-field ink); the old 0x0F was a guess and the
-	// underscore caret in it was effectively invisible.
-	const uint8 kInkColor = 0x22;     // typed-name ink + caret
+	const uint8 kInkColor = 0x22;
 
 	Picture bg;
 	const bool haveBg = _picsArchive.getPicture(0xc, bg) && !bg.surface.empty();
@@ -1347,8 +1147,6 @@ void EEMEngine::showLondonCharSelect() {
 				getFont().drawString(&scratch, last, kLastRect.left + 2,
 									 kLastRect.top + 1, kLastRect.width(),
 									 kInkColor);
-				// Caret = solid block `_FillRect(x+1, y, 6, 0xb, 0x22)` at the
-				// input point (`_GetNameString @ 1cd3:0ddc`), NOT an underscore.
 				if (blink && (field == kFieldFirst || field == kFieldLast)) {
 					const Common::Rect &fr =
 						(field == kFieldFirst) ? kFirstRect : kLastRect;
@@ -1364,8 +1162,6 @@ void EEMEngine::showLondonCharSelect() {
 				}
 			}
 			if (field == kFieldGender) {
-				// DOS restores both boxes, then fills the active one with the
-				// darker passport border color sampled at (109,115).
 				scratch.fillRect(kMaleBox, boxBlankColor);
 				scratch.fillRect(kFemaleBox, boxBlankColor);
 				scratch.fillRect(female ? kFemaleBox : kMaleBox,
@@ -1456,10 +1252,6 @@ void EEMEngine::showLondonCharSelect() {
 			blink = !blink;
 			needRedraw = true;
 		}
-		// Flush every frame so the mouse cursor tracks smoothly. The scratch
-		// rebuild + copyRectToScreen above is gated on needRedraw (and mouse
-		// motion doesn't set it), but the cursor is only re-composited by
-		// updateScreen(), so without this it moved only on blink/keypress.
 		g_system->updateScreen();
 		g_system->delayMillis(15);
 	}
@@ -1501,13 +1293,8 @@ void EEMEngine::showLondonCharSelect() {
 				s.getDescription().c_str(), s.getSaveSlot());
 	}
 
-	// New profile. EEM2 `_NewPlayer @ 1cd3:0f27` initializes the player
-	// record only when `_LoadPlayerRecord` failed, then immediately saves it.
 	_playerName = displayName.empty() ? "Detective" : displayName;
 	_chainStage = 1;
-	// `_NewPlayer @ 1cd3:0f27` stores the passport gender pick (`DAT_3036_4c4c`).
-	// Drives the player pronouns 0x86-0x88 in `parseString`. (Existing profiles
-	// take the gender from their save instead, via the load path above.)
 	_playerFemale = female;
 	_voiceOn = true;
 	if (_audio)
@@ -1525,14 +1312,8 @@ void EEMEngine::showLondonCharSelect() {
 		   _playerName.c_str(), profileKey.c_str(),
 		   female ? "female" : "male");
 }
-
+// Floppy storm-logo splash ??? `23d2:0605`:
 void EEMEngine::showFloppyStormLogo() {
-	// Floppy storm-logo splash ??? `FUN_23d2_0605 @ 23d2:0605`:
-	//   GetPicture(0x20b); BlitToVGA;
-	//   if (sound) { LoadVOC(slot 25 = "thunder.voc"); PlayVOC(...); }
-	//   GetPalette(0x26); FadeIn; wait 50 ticks; FadeOut.
-	// CD plays `BOLT.ANM` here with `THUNDER.VOC` overlaid; floppy uses
-	// a static still + the same VOC.
 	Picture pic;
 	if (!_picsArchive.getPicture(kPicStormLogo, pic)) {
 		warning("Storm logo (%u) load failed", kPicStormLogo);
@@ -1561,27 +1342,13 @@ void EEMEngine::showFloppyStormLogo() {
 }
 
 void EEMEngine::doSiteLoop() {
-	// Per-mystery site loop. SiteScreen::run() handles hotspot clicks
-	// plus M (map), N (notebook), G (gallery), A (accuse), Tab (next
-	// site), ESC (exit).
 	SiteScreen screen(this, &_mystery);
 	screen.run();
 	setHotspotMouseCursor(false);
 }
 
+// _StartTravelMusic @ 20a2:0595:
 void EEMEngine::startTravelMusic() {
-	// _StartTravelMusic @ 20a2:0595:
-	//   for (num = _SiteNumber; num > 4; num -= 5) {}
-	//   if (_MIDIAvailable && _MusicEnabled) {
-	//       if (_IsMIDIPlaying()) _StopMIDI();
-	//       _MIDIPlay(num);
-	//   }
-	// Five travel tracks (MUS00000.XMI..MUS00004.XMI), picked by
-	// `_SiteNumber % 5`. ONE-SHOT ??? `_DoOpeningAnims @ 2520:0945` resets
-	// `_LoopMIDI = 0` after the title-screen wait, and the function
-	// doesn't write it; combined with `_DoSiteLoop @ 168d:06c0` calling
-	// `_StopMIDI()` before the interactive phase, travel music plays
-	// ONCE during the entrance animation only.
 	if (!_music || !_mystery.isLoaded() ||
 		(isLondon() ? !_musicOn : !_voiceOn))
 		return;
@@ -1590,8 +1357,6 @@ void EEMEngine::startTravelMusic() {
 }
 
 void EEMEngine::startLondonTravelMusic(uint8 travelKind) {
-	// EEM2 `_DoTravel @ 1717:06ae`: travelKind * 6 indexes this table,
-	// then rand() picks one of three u16 MUS IDs.
 	static const uint16 kLondonTravelMusic[4][3] = {
 		{ 0,  0,  0 },
 		{ 3, 22, 25 },
@@ -1644,10 +1409,6 @@ void EEMEngine::syncSoundSettings() {
 }
 
 bool EEMEngine::hasFeature(EngineFeature f) const {
-	// We support saving any time but loading only at startup (via the
-	// `--save-slot=N` resume path or a slot picked from the launcher).
-	// Runtime loads would replace `_mystery._data` while pointers into
-	// it are alive on the stack inside `displayClue` etc.
 	return f == kSupportsSavingDuringRuntime ||
 		   f == kSupportsReturnToLauncher;
 }
@@ -1675,7 +1436,7 @@ Common::Error EEMEngine::saveGameStream(Common::WriteStream *stream,
 	//   +0x00..+0x0b : player name (12 chars, null-padded)
 	//   +0x0c..+0x1f : random ID bytes for `_GenerateFilename`
 	//                  (29be:0dbf "C:\EEMCDSAV\%s.PLR") ??? irrelevant to
-	//                  ScummVM saves which key on slot, not filename.
+	//                  us since saves which key on slot, not filename.
 	//   +0x20..+0x28 : derived 8-char .PLR basename ??? likewise unused.
 	//   +0x2d        : voice-enable flag (`DAT_2d5d_3f97`, default 1)
 	//   +0x2f        : chain stage (`DAT_2d5d_3f99`, 1=A, 2=B, 3=C;
@@ -1691,15 +1452,11 @@ Common::Error EEMEngine::saveGameStream(Common::WriteStream *stream,
 	s.syncAsByte(_partner);
 	s.syncAsByte(_chainStage);
 	s.syncAsByte(_voiceOn);
-	// EEM2/London music (MIDI) toggle ??? `DAT_3036_4cc0`, separate from voice.
 	s.syncAsByte(_musicOn);
 
-	// London passport gender (player pronouns 0x86-0x88).
 	byte playerFemale = _playerFemale ? 1 : 0;
 	s.syncAsByte(playerFemale);
 
-	// Mid-case resume: persist in-progress mystery (no equivalent in
-	// _LoadGame @ 2404:0dc7).
 	bool hasMystery = _mystery.isLoaded();
 	s.syncAsByte(hasMystery);
 	if (hasMystery) {
@@ -1738,7 +1495,6 @@ Common::Error EEMEngine::loadGameStream(Common::SeekableReadStream *stream) {
 	if (_audio)
 		_audio->setVoiceEnabled(_voiceOn);
 
-	// London passport gender (player pronouns 0x86-0x88).
 	byte playerFemale = 0;
 	s.syncAsByte(playerFemale);
 	_playerFemale = (playerFemale != 0);
@@ -1753,13 +1509,6 @@ Common::Error EEMEngine::loadGameStream(Common::SeekableReadStream *stream) {
 			resetSiteArrivalState();
 			return Common::kReadingFailed;
 		}
-		// `_ReadMystery @ 2404:008f` tail-calls `_InitMysterySounds`
-		// (2404:0298) so the SDB index is in place for clue and
-		// partner-speech spool sounds. Floppy ships individual
-		// `M-XXXX.VOC` files instead of the bundled SDB/SDX archive,
-		// so we skip the init there to avoid "missing" warnings;
-		// `spoolSound` then no-ops via its `_currentMystery < 0` guard
-		// until the per-voice VOC mapping is wired up.
 		if (_audio && !isFloppy())
 			_audio->initMysterySounds(mysteryNum);
 		_mystery.syncState(s);
@@ -1781,9 +1530,6 @@ Common::Error EEMEngine::loadGameStream(Common::SeekableReadStream *stream) {
 }
 
 SaveStateList EEMEngine::listProfiles() const {
-	// _findfirst("*.PLR") in screen8_handler @ 1c33:1012.
-	// Filter out slot 0 (autosave) to match the original which
-	// has no autosave concept.
 	SaveStateList saves = getMetaEngine()->listSaves(_targetName.c_str());
 	for (uint i = 0; i < saves.size(); ) {
 		if (saves[i].getSaveSlot() == 0)
@@ -1813,8 +1559,7 @@ Common::Error EEMEngine::saveProfile(const Common::String &name) {
 		}
 	}
 
-	// New profile: pick lowest unused slot >=1 (slot 0 is autosave;
-	// DOS limit was 25 per screen8_handler local_8c[0x19][2]).
+	// New profile: pick lowest unused slot >=1 (slot 0 is autosave);
 	if (slot < 0) {
 		const int maxSlot = getMetaEngine()->getMaximumSaveSlot();
 		Common::Array<bool> used(maxSlot + 1);
diff --git a/engines/eem/eem.h b/engines/eem/eem.h
index 9f4d98c7856..4ea98b5aa65 100644
--- a/engines/eem/eem.h
+++ b/engines/eem/eem.h
@@ -47,18 +47,14 @@ namespace EEM {
 class AudioPlayer;
 class MusicPlayer;
 
-/// VGA palette size in bytes (256 colours ?? RGB). Defined in eem.cpp.
+/// VGA palette size in bytes (256 colours ?? RGB)
 const uint kPalSize = 768;
 
-/// Palette fade helpers (defined in eem.cpp): ramp the VGA palette to / from
-/// black over 16 steps, matching `_FadeToBlack` / `_OpenFadeIn`.
 void fadeCurrentPaletteToBlack(uint delayMs = 8);
 void fadePaletteFromBlack(const byte *target, uint delayMs = 8);
 
 /// _ScreenDriver dispatch table @ 1a35:0e5e (fallback @ 1a35:0e54).
-/// 14 entries total: each is a screen ID + near fn ptr at +0x1c; driver
-/// tail-calls via `JMP word ptr CS:[BX + 0x1c]`. Handler bodies update
-/// _NextScreen before returning. Handlers @ 1a35:0dec..0e4f:
+/// 14 entries total: 
 ///
 ///   0  INIT_CLUES ??? `_PreLoad` + `_DoInitClues`, writes _NextScreen=1
 ///   1  MAP        ??? `_DoMapScreen @ 20fe:120b` (writes 3=site clicked,
@@ -81,10 +77,6 @@ void fadePaletteFromBlack(const byte *target, uint delayMs = 8);
 ///   0xc ACTION    ??? `_ActionScreen @ 1c33:195b` ??? Choose A Mystery /
 ///                   Practice / See ScrapBook 1..3. Action 1 sets =0xa.
 ///   0xFFFF SENTINEL ??? exit loop
-///
-/// State writes (via xrefs to `_NextScreen @ 2d5d:3f26`): `_DisplayCorrect`
-/// writes 0xc, `_DisplayAlibi` writes `_LastScreen`, `_DoSiteLoop` writes
-/// 1/3/4 plus 0xffff on ESC.
 enum ScreenId {
 	kScreenInvalid        = 0xFFFF,
 	kScreenInitClues      = 0x00,
@@ -103,15 +95,11 @@ enum ScreenId {
 };
 
 /// Distribution variant from `ADGameDescription::extra` (set by
-/// `gameDescriptions[]` in `detection.cpp`). Gates filename selection
-/// (TRAVEL-N.XMI vs MUS%05u.XMI, FANFARE2.XMI vs MUS00005.XMI,
-/// PHONESL.VOC vs PHONE.VOC), opening-anim flow (MOVIE.ANM vs
-/// ANIM01..20.A), and per-variant SFX (DING.VOC / NEWSCAN.VOC ship only
-/// with floppy).
+/// `gameDescriptions[]` in `detection.cpp`).
 enum Variant {
 	kVariantCD       = 0,
 	kVariantFloppy   = 1,
-	kVariantLondonCD = 2, ///< Eagle Eye Mysteries in London (EEM2CD.EXE).
+	kVariantLondonCD = 2,
 };
 
 /// `_Partner @ 29be:7918`. Selected at the partner-pick screen
@@ -121,13 +109,9 @@ enum Partner {
 	kPartnerJenny = 1,
 };
 
-/// VGA mode 13h dimensions (initGraphics(320, 200) in `EEMEngine::run`).
 constexpr int kScreenWidth  = 320;
 constexpr int kScreenHeight = 200;
 
-/// Shared PDA-frame navigation rects (PIC 0x3f) ??? reachable from Site,
-/// Notebook, Gallery, Accuse, and MoreInfo. Original `_NoteButtons` table
-/// @ 29be:0147 + the site-screen hit tests in `_DoSiteLoop @ 168d:03f4`.
 constexpr Common::Rect kPdaSiteRect             (Common::Point(35, 111), 21, 25);
 constexpr Common::Rect kPdaPartnerFootMapRect   (Common::Point( 7, 177), 50, 23);
 constexpr Common::Rect kPdaPartnerHeadHintRect  (Common::Point( 5,  80), 39, 30);
@@ -143,37 +127,22 @@ public:
 	Common::Platform getPlatform() const;
 	Variant getVariant() const { return _variant; }
 	bool isFloppy() const { return _variant == kVariantFloppy; }
-	/// EEM2 ("Eagle Eye Mysteries in London") ??? reimplementation in progress.
-	/// Shares the EEM1 DBD/palette/font formats, but ships a 63-entry
-	/// "SITEPALS." file and uses different picture/palette IDs per screen.
 	bool isLondon() const { return _variant == kVariantLondonCD; }
 
 	bool hasFeature(EngineFeature f) const override;
 	bool canLoadGameStateCurrently(Common::U32String *msg = nullptr) override;
 	bool canSaveGameStateCurrently(Common::U32String *msg = nullptr) override;
 
-	// Autosave disabled: profile picker (screen8_handler @ 1c33:1012)
-	// lists `*.PLR` and a slot-0 autosave would appear as a profile.
 	int getAutosaveSlot() const override { return -1; }
 
 	Common::Error saveGameStream(Common::WriteStream *stream,
 								  bool isAutosave = false) override;
 	Common::Error loadGameStream(Common::SeekableReadStream *stream) override;
 
-	// Per-profile saves: `_PlayerRecord` @ 2d5d:3f6a (159 bytes), written
-	// by `_SavePlayerRecord @ 1c33:034f` to `C:\EEMCDSAV\<name>.PLR`.
-	// Each save slot maps to one profile (slot description = the
-	// player name) ??? same approach Wetlands uses. `screen8_handler @
-	// 1c33:1012` walks `*.PLR`, lets the player pick a profile, and calls
-	// `_LoadPlayerRecord`.
-
-	/// `_SavePlayerRecord @ 1c33:034f`.
 	Common::Error saveProfile(const Common::String &name);
 
-	/// `_LoadPlayerRecord @ 1c33:03a6`.
 	bool loadProfile(const Common::String &name);
 
-	/// `_findfirst("*.PLR")` walk inside `screen8_handler`.
 	SaveStateList listProfiles() const;
 
 	const ADGameDescription *_gameDescription;
@@ -204,25 +173,15 @@ public:
 	/// EEM1 cursors are all 0 so this is a no-op there.
 	void setSiteHotspotCursorId(int cursorId);
 
-	/// `_DisplayClue @ 2404:05e6`. @p clueBlock points at the u16 frame
-	/// count followed by 62-byte ClueEntries.
+	/// `_DisplayClue @ 2404:05e6`. 
 	void displayClue(const byte *clueBlock);
 
 	/// EEM2/London `_DoPuzzle @ 2542:1482`. A clue entry can gate the rest of
-	/// itself behind a "check the manual / a real map" puzzle (the id is the
-	/// entry+0x54 field). Opens `P<id>.BIN` ??? a header + question, then either
-	/// a typed answer (type 0) or a click-a-region multiple choice (type 1).
-	/// Returns true when answered correctly (and true if the file is missing,
-	/// so the clue is never permanently blocked); a wrong answer replays the
-	/// partner's scolding hint, and the caller re-prompts on the next visit.
+	/// itself behind a "check the manual / a real map" puzzle
 	bool doPuzzle(uint puzzleId);
 
-	/// Floppy hotspot click. `FUN_22dc_0b80 + FUN_1652_00e6 + FUN_1652_006c`.
-	/// Locates dialog records in site_data[+6] and dispatches them.
 	void displayFloppyHotspotDialog(uint siteNum, uint hotIdx);
 
-	/// `_HotspotSearched_Floppy @ 22dc:096c`. Walks the per-hotspot dialog
-	/// records and returns whether the hotspot's searched text index was seen.
 	bool floppyHotspotSearched(uint siteNum, uint hotspotIdx) const;
 
 	/// Active player name (= profile-save description).
@@ -231,19 +190,10 @@ public:
 	/// Apply a ClueEntry's side effects (notebook, gallery, site flags).
 	void applyClueSideEffects(const byte *entry);
 
-	/// `_DrawNotes @ 161e:01d0`.
 	void doNotebook();
-
-	/// `_DrawGallery @ 158f:0046`.
 	void doGallery();
-
-	/// `MoreInfo @ 158f:0419`. Suspect-detail view inside the gallery; button
-	/// dispatch via `_HandleMoreButton @ 158f:027d`. Returns true if the
-	/// caller should exit `doGallery` (NOTEBOOK / ACCUSE / MAP).
 	bool moreInfo(const byte *gd, uint suspectIdx,
 				   const Picture &galBg, bool haveBg);
-
-	/// `_DoBigMap @ 20fe:09e7`.
 	void doBigMap();
 
 	/// Accuse flow. `_DoAccuseGallery @ 1df2:0a31` + `_DisplayEnding @ 1df2:0548`.
@@ -254,38 +204,26 @@ public:
 	/// Returns true if the player committed (SOLVE clicked), false on ESC.
 	bool doAccuseNotes();
 
-	/// `_KDHelp @ 1560:010a` + `_DisplayHint @ 1560:0009`. Cycles two
-	/// hint slots tracked via `_SawHelpHint`.
+	/// `_KDHelp @ 1560:010a` + `_DisplayHint @ 1560:0009`. 
 	void doHelp();
 
 	/// `_InterfaceHelp @ 1560:0205`. Walks `HelpData @ 29be:00c8`.
 	void doInterfaceHelp(uint num = 0);
 
-	/// `_GetKDTextBalloon @ 1df2:0105`. Digits (0..9) ??? table @ 29be:1064;
-	/// otherwise `*(u16*)29be:1068 = 0x17`.
+	/// `_GetKDTextBalloon @ 1df2:0105`.
 	uint16 getKDTextBalloon(byte firstChar) const;
 
 	/// Pick a shorter balloon sibling when wrapped text leaves empty lines.
 	uint16 fitBalloonToText(uint16 bubNum, const Common::String &text);
 
-	/// `_ParseString @ 1b66:07c3`. Substitutes 0x80..0x89 control bytes;
-	/// jump table @ 1b66:0cbe.
+	/// `_ParseString @ 1b66:07c3`. 
 	Common::String parseString(const Common::String &raw,
 							   const Common::String &playerName,
 							   uint partner) const;
 
-	/// `_DoKDAnim @ 168d:028a` + `_PlayAnimation @ 172b:1f46`. Per-partner
-	/// (animId, x, y) from `_WaitAnims[1+num] @ 29be:0228`. Blocks until
-	/// the script's first 0x80 marker.
+	/// `_DoKDAnim @ 168d:028a` + `_PlayAnimation @ 172b:1f46`.
 	void playKdAnim(uint16 num);
 
-	/// Provide a "clean" 320x200 backdrop (site BG + static drops, no
-	/// NPCs / partner) for the next `playKdAnim` to use as the
-	/// background-erase source. Without this, the camera animation would
-	/// composite on top of the static partner sprite and the previous
-	/// resting frame would bleed through transparent pixels. `SiteScreen`
-	/// passes its `_bgSnapshot` before `displayClue` from a hotspot click.
-	/// Pass `nullptr` to clear.
 	void setPartnerEraseBg(const Graphics::ManagedSurface *bg);
 
 	/// Balloon-text-inset metadata. 52-entry table @ 29be:0875 (CD) /
@@ -299,13 +237,10 @@ public:
 	bool getBalloonIndicatorPos(uint16 bubNum, uint16 &dx,
 								 uint16 &dy) const;
 
-	/// `FUN_22dc_05c8 @ 22dc:08aa` (mid-page) / `@ 22dc:08c0` (end).
-	/// PIC 0xa0 if !endIndicator else PIC 0xa1.
 	void drawFloppyBubbleIndicator(Graphics::ManagedSurface &dst,
 								   uint16 bubNum, int ballX, int ballY,
 								   bool endIndicator);
 
-	/// `_AreYouSure @ 1a35:0a5c`. Returns true on YES.
 	bool areYouSure();
 
 private:
@@ -323,17 +258,11 @@ private:
 
 	/// Inclusive 1-based mystery range [lo, hi] for chain `stage` (1-based).
 	/// Returns false when `stage` is not a real tier in this variant.
-	/// EEM1 `_DisplayCorrect @ 1df2:073c` (1..24 / 25..48 / 49..54);
-	/// EEM2/London `_DisplayCorrect @ 1ea1:0619` (1..25 / 26..50).
 	bool mysteryTierRange(uint stage, uint &lo, uint &hi) const;
 
 	void advanceChainStageAfterSolve(uint mysteryNum);
 	void applySkipRepeatedCasesOption();
 
-	/// Central dispatch loop matching `_ScreenDriver @ 1a35:0dc1`. Each
-	/// iteration calls the handler that matches `_nextScreen`; handlers
-	/// update `_lastScreen` / `_nextScreen` and return. Loop exits when
-	/// `_nextScreen == kScreenInvalid`.
 	void screenDriver();
 
 	/// Re-render helpers for the corresponding `doX()` modal screens.
@@ -427,10 +356,7 @@ public:
 	void waitForInput(uint32 maxMs);
 
 	/// Play a difference-encoded animation file (.ANM / .A) on the full
-	/// 320x200 screen. Mirrors the data flow of `OpenDifferenceAnimation
-	/// @ 2520:0337` ??? `Load_Sequence` + `Play_Sequence`. Audio cues are
-	/// skipped for now. The default 120 ms frame delay matches the
-	/// original `FRAME_RATE = 0x78` used by `_DoOpeningAnims`.
+	/// 320x200 screen. 
 	/// If @p holdLastFrame is true the call blocks on the final frame
 	/// until the user clicks or hits a key ??? used for the title screen.
 	/// If @p fadeIn is true the first decoded frame is copied while the
@@ -440,40 +366,24 @@ public:
 				 bool setSkipIntroOnEsc = true);
 
 private:
-	/// `_CleanMysterySounds @ 202f:05a5` + `_StopMIDI @ 20a2:0512`.
-	/// `stopMusicToo=false` keeps MIDI playing across dialog skips.
 	void interruptAudio(bool stopMusicToo = true);
 
 	void showEAKidsLogo();
 	void showHighScoreLogo();
 	void showFloppyStormLogo();
 
-	// --- EEM2 ("Eagle Eye Mysteries in London") ??? reimplementation in progress ---
-	/// Opening sequence + character creation ??? EEM2's `_DoOpeningAnims`
-	/// @ 2721:08e6 then screen 8 profile selection.
 	void runLondonStartup();
 	/// Start London mystery 0 after a freshly-created detective chooses a partner.
 	bool startLondonTrainingMystery();
-	/// Blit a full-screen still PIC and fade it in / hold / out using the
-	/// given SITEPALS. palette index.
 	void showLondonLogo(uint picId, uint palId, uint holdMs);
-	/// EEM2 character creation (`_NewPlayer`: palette 0 + background PIC 0xc):
-	/// first/last name entry + male/female player-gender selection.
 	void showLondonCharSelect();
-	/// EEM2 case-intro animation (`_DoInitClues` @ 1abf:03b3): single partner
-	/// anim (Jake 0x18 / Jenny 0x71) faded in, then phone1.voc on caseType 1.
 	void playLondonInitCluesAnim(uint16 caseType, const Picture &bg,
 								 bool haveBriefingBg);
-	/// EEM1 CD/floppy case-intro animation (`_DoInitClues` @ 1a35:0411):
-	/// game/book/nancy cycle + `_PlayInSequence` partner entrance.
 	void playCdFloppyInitCluesAnim(uint16 caseType, bool floppy,
 								   const Picture &bg, bool haveBriefingBg);
 
-	/// `screen8_handler @ 1c33:1012`. Profile selector ??? walks
-	/// `listProfiles()`, falls through to `doNewPlayer()` if "New" or
-	/// no profiles exist (1c33:1170: `if (saves == 0) _NewPlayer();`).
 	void doProfilePicker();
-	void doNewPlayer();          ///< `_NewPlayer @ 1c33:0dda`.
+	void doNewPlayer();
 	void doChoosePartner();
 
 	/// Display the per-mystery ending pages from `E<num>.BIN`. Mirrors
@@ -481,11 +391,6 @@ private:
 	/// File format: u16 page count, then N pages of
 	/// `{ u16 picNum, u16 x1, u16 y1, u16 x2, u16 y2,
 	///    char text[] (null-terminated, ParseString placeholders) }`.
-	/// `_ShowOneScrap @ 1f78:0773` is just `_DisplayEnding(num, 1)`,
-	/// so this call also covers the post-mystery scrapbook view.
-	/// `firstPage=true` opens at page 0; `false` opens at the last page
-	/// (back-nav from `doShowScrapbook`, mirrors the `local_8 = 0` write
-	/// at `_ShowScrapbook @ 1f78:067e`).
 	/// Returns the caller's nav direction (per `[BP-0x18]` @ 1df2:0723):
 	///   -1 ??? previous mystery (LEFT on first page or click in PrevPageRect),
 	///    0 ??? exit scrapbook (ESC / quit),
@@ -493,28 +398,20 @@ private:
 	int doShowEnding(uint num, bool firstPage = true);
 
 	/// EEM1 `_ShowScrapbook(stage, 0) @ 1f78:0642`; EEM2/London scrapbook
-	/// `FUN_2046_09dd`. Walks the `mysteryTierRange(stage)` cases, skipping
-	/// unsolved entries in the current chain stage. Tier sizes are
-	/// variant-specific (EEM1 24/24/6; London 25/25, no stage 3).
+	/// `2046:09dd`. Walks the `mysteryTierRange(stage)` cases, skipping
+	/// unsolved entries in the current chain stage. 
 	void doShowScrapbook(uint stage);
 
 	void doActionScreen();
 	void doCaseSelection();
 	void doSiteLoop();
 
-	/// `_DoSetup @ 1f78:044e`. Voice on/off (`DAT_2d5d_3f97`), partner
-	/// pick via SwapColors on Kid1/Kid2 rects.
 	void doSetup();
 
-	/// `doSetup` helpers: redraw BG + label highlights, render a help/
-	/// credits card with blocking input wait, and the shared exit fallback.
 	void setupDrawScreen();
 	Common::KeyCode setupShowFullscreenPic(uint16 picId, bool transparent);
 	void setupLeave();
 
-	/// EEM2/London setup screen ??? `_DoSetup @ 2046:067b` (distinct from EEM1's
-	/// seg-1f78 screen): 4 toggles (partner / voice / music / highlight boxes),
-	/// rearranged 13-button layout, scrapbook paging. Reuses the shared helpers.
 	void doSetupLondon();
 	void setupDrawScreenLondon();
 
@@ -569,36 +466,14 @@ private:
 	/// London passport gender (EEM2 `_NewPlayer @ 1cd3:0f27` gender pick,
 	/// `DAT_3036_4c4c`: left/0 = male, right/1 = female). Drives the player
 	/// pronoun opcodes 0x86/0x87/0x88 in `parseString` (he??him??his / she??her??
-	/// her). EEM1 has no passport, so it stays false (male) ??? matching that
-	/// engine's never-written gender flag.
+	/// her). EEM1 has no gender selection, so it stays false (male)
 	bool _playerFemale = false;
 
 	/// `_PlayerRecord.SolvedMysteries[55]`. 0=unsolved, 1=solved, 2=first-try.
 	uint8 _mysteriesSolved[55] = {};
-
-	/// Current chain/tier the player is at ??? `DAT_2d5d_3f99`
-	/// (`_PlayerRecord +0x2f`):
-	///   1 = Junior detective  (mysteries  1..24, "A chain")
-	///   2 = Senior detective  (mysteries 25..48, "B chain")
-	///   3 = Master detective  (mysteries 49..54, "C chain")
-	/// Initialized to 1 in `_NewPlayer @ 1c33:0fa3`; bumped by
-	/// `_DisplayCorrect @ 1df2:0853` once every mystery in the current
-	/// tier is solved (range checks @ 1df2:080d / 0824 / 0837). Also
-	/// gates `_CaseSelection`'s book label and selection list
-	/// (1c33:0a87 onwards).
 	uint8 _chainStage = 1;
 
-	/// Voice / digital-audio enable flag. `DAT_2d5d_3f97` (`_PlayerRecord
-	/// +0x2d`). Set to 1 by `_NewPlayer @ 1c33:0fa3`, toggled by the
-	/// SoundOn / SoundOff hot-rects in `_DoSetup @ 1f78:044e`. Gates
-	/// every `_PlayVoice` / `_SpoolSound` call site (clue voices,
-	/// partner speech, intro VO).
 	bool _voiceOn = true;
-
-	/// EEM2/London music (MIDI) on/off ??? `DAT_3036_4cc0`, toggled by the music
-	/// button in `_DoSetup @ 2046:067b`. Separate from `_voiceOn` (EEM2 has both
-	/// a voice and a music toggle; EEM1 only had one sound toggle). Gates the
-	/// briefing / travel MIDI for London.
 	bool _musicOn = true;
 
 	/// Set by the profile/new-player screens. London uses it to decide whether
@@ -625,7 +500,7 @@ private:
 	bool _skipIntro = false;
 
 	/// Clean BG (no partner/NPC) used by `playKdAnim` between camera-anim
-	/// cells. See `setPartnerEraseBg`.
+	/// cells. 
 	Graphics::ManagedSurface _partnerEraseBg;
 
 	bool _interactiveMouseCursor = false;
@@ -643,8 +518,6 @@ private:
 	/// `_StartTravelMusic` @ 20a2:00e2-05c9). Constructed lazily in `run()`.
 	MusicPlayer *_music = nullptr;
 
-	/// `SOUND.C` / `SPOOLSND.C`. `_PlayVoice @ 1ff1:023e`,
-	/// `_SpoolSound @ 202f:068d`, `_InitMysterySounds @ 202f:05cb`.
 public:
 	AudioPlayer *_audio = nullptr;
 
diff --git a/engines/eem/font.cpp b/engines/eem/font.cpp
index a5fba38d7f1..7a6fb6006e3 100644
--- a/engines/eem/font.cpp
+++ b/engines/eem/font.cpp
@@ -95,8 +95,7 @@ bool EEMFont::load(const Common::Path &path) {
 			_maxWidth = g.widthBits;
 	}
 
-	// _LoadFont @ 1b03:0220 sets line stride to the first glyph's
-	// height (DAT_28da_30ca = DAT_28da_30ce, space glyph height).
+	// _LoadFont @ 1b03:0220 sets line stride to the first glyph's height
 	// Descenders ('g','j','p','q','y') intentionally overhang into
 	// the next row.
 	_lineHeight = !_glyphs.empty() ? _glyphs[0].height : _maxHeight;
diff --git a/engines/eem/font.h b/engines/eem/font.h
index 56275b8018e..8c77e97c1ea 100644
--- a/engines/eem/font.h
+++ b/engines/eem/font.h
@@ -55,7 +55,6 @@ public:
 	bool load(const Common::Path &path);
 	bool isLoaded() const { return !_glyphs.empty(); }
 
-	// --- Graphics::Font overrides ---
 	int getFontHeight() const override {
 		return _lineHeight ? _lineHeight : _maxHeight;
 	}
diff --git a/engines/eem/graphics.cpp b/engines/eem/graphics.cpp
index ec1f5d089ce..da96f26668c 100644
--- a/engines/eem/graphics.cpp
+++ b/engines/eem/graphics.cpp
@@ -38,12 +38,6 @@
 
 namespace EEM {
 
-// HelpData @ 29be:00c8, read by _InterfaceHelp @ 1560:0205. 5-byte entries:
-// u8 count, then up to 2 u16 picIds. Verified bytes:
-//   entry 0 (PDA / gallery HELP button): count=2, picIds = 0x0063, 0x01ae
-//   entry 1:                              count=2, picIds = 0x0192, 0x01b1
-// Only entry 0 is reachable from the PDA notebook (rect 1) and the gallery
-// (rect 1) ??? both call _InterfaceHelp(0).
 const uint16 kHelpPics[][2] = {
 	{ 0x0063, 0x01ae },
 	{ 0x0192, 0x01b1 },
@@ -114,8 +108,6 @@ bool findBalloonFamily(uint16 balloonId, uint16 &first, uint16 &last) {
 	return false;
 }
 
-// indDY is the artist-intended last text line, not just the indicator Y:
-// families 3, 4, 6 and singletons have shadow/tail decoration below indDY.
 uint getBalloonLineCapacity(uint16 balloonId, int lineH) {
 	const uint idx = balloonId & 0x7F;
 	if (idx >= ARRAYSIZE(kBalloonInsetTable) || lineH <= 0)
@@ -126,9 +118,6 @@ uint getBalloonLineCapacity(uint16 balloonId, int lineH) {
 }
 
 bool EEMEngine::floppyHotspotSearched(uint siteIdx, uint hotspotIdx) const {
-	// FUN_22dc_096c @ 22dc:096c: walks per-site dialog records at
-	// site_data[+6] to skip hotspotIdx hotspots, then returns _TextSeen for
-	// the selected hotspot's searched text index.
 	const byte *site = _mystery.siteData(siteIdx);
 	if (!site)
 		return false;
@@ -183,13 +172,8 @@ static Common::String readPuzzleLine(Common::File &f) {
 	}
 	return s;
 }
-
+// `_DoPuzzle @ 2542:1482`. 
 bool EEMEngine::doPuzzle(uint puzzleId) {
-	// `_DoPuzzle @ 2542:1482`. File `P<id>.BIN` (LE u16 fields):
-	//   type(0=typed,1=choice); mainPic{id,x,y}; extraCount; extra{id,x,y}*;
-	//   qx; qy; qw; voiceAlt(Jenny); voiceMain(Jake); question line.
-	//   type 0: answerRect{x1,y1,x2,y2}; answer line (stored UPPERCASE).
-	//   type 1: choiceCount; rects{x1,y1,x2,y2}* ??? correct = rect 0.
 	Common::File f;
 	const Common::String fname = Common::String::format("P%u.BIN", puzzleId);
 	if (!f.open(Common::Path(fname))) {
@@ -200,10 +184,6 @@ bool EEMEngine::doPuzzle(uint puzzleId) {
 
 	const uint16 type = f.readUint16LE();
 
-	// The caller (displayClue) restored the clean site background, so the
-	// current screen has no clue bubbles. Keep that as `cleanBg` and build the
-	// puzzle on a copy; `cleanBg` is restored after the answer (DOS `_DoPuzzle`
-	// _Repaint) and after the wrong-answer hint (`_KDHelp` _Repaint) so neither
 	// the puzzle pics nor any bubble is left on the background.
 	Graphics::ManagedSurface cleanBg(kScreenWidth, kScreenHeight,
 		Graphics::PixelFormat::createFormatCLUT8());
@@ -219,7 +199,6 @@ bool EEMEngine::doPuzzle(uint puzzleId) {
 		Graphics::PixelFormat::createFormatCLUT8());
 	scratch.simpleBlitFrom(cleanBg);
 
-	// Main picture, then `extraCount` more ??? each {id, x, y}, masked blit.
 	for (int phase = 0; phase < 2; phase++) {
 		uint16 n = 1;
 		if (phase == 1)
@@ -249,7 +228,6 @@ bool EEMEngine::doPuzzle(uint puzzleId) {
 							   0, 0, kScreenWidth, kScreenHeight);
 	g_system->updateScreen();
 
-	// Partner reads the question (`_SpoolSound(id - 1)`, gated on audio).
 	if (_audio && _voiceOn) {
 		const uint16 v = (_partner == kPartnerJake) ? voiceMain : voiceAlt;
 		if (v != 0 && v != 0xFFFF)
@@ -260,13 +238,12 @@ bool EEMEngine::doPuzzle(uint puzzleId) {
 	CursorMan.showMouse(true);
 
 	if (type == 0) {
-		// Typed answer (`_CheckTypedAnswer` ??? `_GetNameString`, toupper+strcmp).
 		const int16 ax1 = (int16)f.readUint16LE();
 		const int16 ay1 = (int16)f.readUint16LE();
 		const int16 ax2 = (int16)f.readUint16LE();
 		const int16 ay2 = (int16)f.readUint16LE();
 		const Common::Rect rect(ax1, ay1, ax2, ay2);
-		Common::String answer = readPuzzleLine(f);  // stored uppercase
+		Common::String answer = readPuzzleLine(f);
 		const uint maxLen = answer.size() + 2;
 
 		Common::String input;
@@ -302,7 +279,7 @@ bool EEMEngine::doPuzzle(uint puzzleId) {
 					if (!input.empty())
 						done = true;
 				} else if (k == Common::KEYCODE_ESCAPE) {
-					input.clear();  // cancel ??? empty ??? fails the compare
+					input.clear();
 					done = true;
 				} else if (k == Common::KEYCODE_BACKSPACE) {
 					if (!input.empty())
@@ -333,19 +310,6 @@ bool EEMEngine::doPuzzle(uint puzzleId) {
 			const int16 cy2 = (int16)f.readUint16LE();
 			rects.push_back(Common::Rect(cx1, cy1, cx2, cy2));
 		}
-		// `_GetPuzzleChoice @ 2542:11a9`: the choice rects are the only
-		// clickable areas (no done/exit hotspot) and ESC = wrong; the Tab/arrow
-		// keys hit a global handler, not choice selection. Highlight the
-		// options two ways, both reusing the existing engine mechanisms:
-		//   * Boxes: outline each option in the marching-ants ramp ??? palette
-		//     0xF9..0xFE, the SAME original yellow as the site hotspots, not a
-		//     puzzle-specific colour. Gated like the DOS `_DrawRect`
-		//     (`DAT_3036_4c4a` ??? port `hide_highlight_boxes`).
-		//   * Cursor: recolor the default arrow over an option
-		//     (`setInteractiveMouseCursor`, the EEM1 red-pixel cursor for
-		//     "otherwise invisible" hotspots). These options carry no per-hotspot
-		//     cursor shape, so the default cursor is what's shown ??? exactly the
-		//     case that recolor is meant for.
 		applyHotspotGlowPalette();
 		const bool showBoxes = !ConfMan.getBool("hide_highlight_boxes");
 		int picked = -1;
@@ -368,8 +332,6 @@ bool EEMEngine::doPuzzle(uint puzzleId) {
 											   kScreenWidth, kScreenHeight);
 				}
 			}
-			// Red default-cursor while hovering a clickable option; plain arrow
-			// off all of them (mirrors `updateHotspotCursor`).
 			const Common::Point mp = g_system->getEventManager()->getMousePos();
 			bool nowOver = false;
 			for (uint i = 0; i < rects.size(); i++) {
@@ -409,15 +371,11 @@ bool EEMEngine::doPuzzle(uint puzzleId) {
 	}
 	f.close();
 
-	// _Repaint after the answer: drop the puzzle pics back to the clean site.
 	g_system->copyRectToScreen(cleanBg.getPixels(), cleanBg.pitch, 0, 0,
 							   kScreenWidth, kScreenHeight);
 	g_system->updateScreen();
 
 	if (!correct && !shouldQuit()) {
-		// Wrong: partner scolds via the KD hint (`_MIDIPlay(0x28)` +
-		// `_KDHelp(TextBlock + KDTextIndex[+0xc], voice 6)`), drawn on the
-		// clean site (the puzzle has already been _Repaint-ed away).
 		const byte *kd = _mystery.kdTextIndex();
 		const uint16 hintOff = kd ? READ_LE_UINT16(kd + 0x0c) : 0xFFFF;
 		if (hintOff != 0xFFFF) {
@@ -454,7 +412,6 @@ bool EEMEngine::doPuzzle(uint puzzleId) {
 				_audio->sayKDDigital(kd, 6, _partner);
 			waitForInput(60000);
 
-			// `_KDHelp` ends with _Repaint: clear the hint balloon too.
 			g_system->copyRectToScreen(cleanBg.getPixels(), cleanBg.pitch,
 									   0, 0, kScreenWidth, kScreenHeight);
 			g_system->updateScreen();
@@ -465,14 +422,6 @@ bool EEMEngine::doPuzzle(uint puzzleId) {
 }
 
 void EEMEngine::doHelp() {
-	// Floppy per-mystery H<n>.BIN hint files. Loader FUN_1503_0001 @ 1503:0001
-	// (format string "h%d.bin" @ 2608:0154), consumer FUN_1503_01a5 @ 1503:01a5.
-	// Format:
-	//   byte numChainHints; numChainHints ?? { byte siteIdx; byte hotspotIdx; }
-	//   byte numExtraHints; numExtraHints ?? { byte siteIdx; byte hotspotIdx; }
-	//   asciiz str1, str2, str3 (post-solve, score >= 100)
-	// Selection: any chain hotspot unsearched -> str1; else any extra
-	// unsearched -> str2; else selectedPoints() >= 100 -> str3.
 	if (isFloppy() && _mystery.isLoaded()) {
 		const Common::String filename = Common::String::format("H%u.BIN",
 															   _mystery.number());
@@ -545,9 +494,6 @@ void EEMEngine::doHelp() {
 		if (!chosen || *chosen == 0)
 			return;
 
-		// _GetKDTextBalloon @ 1df2:0105 (floppy FUN_1d40_009f) indexes the
-		// per-character table at 2608:0c14 by the literal byte. Bytes at
-		// 2608:0c44 (= 0xc14 + '0') give the '0'..'9' -> balloon-id mapping:
 		static const uint8 kFloppyDigitToBalloon[10] = {
 			0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1c, 0x1d, 0x1e, 0x0a
 		};
@@ -612,27 +558,7 @@ void EEMEngine::doHelp() {
 		return;
 	}
 
-	// Mirrors _KDHelp @ 1560:010a. Walks the first two _AChain entries
-	// (the puzzle's required-clue chain ??? the "spine" of evidence the
-	// player must collect):
-	//
-	//   for (i = 0; i < 2; i++) {
-	//       if (_AChain[i] != -1 && _HintBlock[i] != -1 &&
-	//           _CluesFound[_AChain[i]] == 0) {
-	//           _DisplayHint(TextBlock + _HintBlock[i], i + 10);
-	//           shown++; break;
-	//       }
-	//       if (_HintBlock[i] != -1) defined++;
-	//   }
-	//   if (!shown) {
-	//       // Generic KD hint: KDTextIndex[+0xe] (first time) /
-	//       // KDTextIndex[+0x10] (second time, toggled by _SawHelpHint).
-	//       // If no chain hint was ever defined, render the "no hints"
-	//       // sentinel instead.
-	//       _DisplayHint(...);
-	//   }
-	//
-	// SMART per-puzzle hint: partner points at whichever chain clue the
+	// per-puzzle hint: partner points at whichever chain clue the
 	// player hasn't found yet, only falling back to the generic line once
 	// every chain hint has been triggered.
 	if (!_mystery.isLoaded() || !_font.isLoaded())
@@ -644,16 +570,10 @@ void EEMEngine::doHelp() {
 		return;
 
 	uint16 chosenText = 0xFFFF;
-	int    soundNum   = 0;       // _SayKDDigital line: EEM1 chain 10/11; fallback 7/8.
-	int    hintVoiceSlot = -1;   // London chain hint ??? _SayKDHintDigital(slot).
+	int    soundNum   = 0;
+	int    hintVoiceSlot = -1;
 	bool   anyHintDefined = false;
 
-	// Required-clue chain walk. EEM1 `_KDHelp @ 1560:010a` checks the first two
-	// entries of chain A. EEM2 `_DoKDHelp @ 15c1:020b` walks all THREE chains
-	// (A,B,C @ header words 16-20/21-25/26-30) ?? 5 slots, indexes the 15-entry
-	// hint-text table `hintBlock()[chain*5 + slot]`, and voices a chain hint
-	// with `_SayKDHintDigital(slot)` (table kdTextIndex()+0x3a) instead of
-	// EEM1's `_SayKDDigital(slot + 10)`. Gate is the same: clue not yet found.
 	const uint kChains = isLondon() ? 3u : 1u;
 	const uint kSlots  = isLondon() ? Mystery::kChainLen : 2u;
 	if (hb) {
@@ -681,7 +601,6 @@ void EEMEngine::doHelp() {
 	}
 
 	if (chosenText == 0xFFFF) {
-		// Second arm of _KDHelp (1560:0152-019b): generic KD hint fallback.
 		if (anyHintDefined) {
 			const uint16 hintFirst  = READ_LE_UINT16(kd + 0x0e);
 			const uint16 hintSecond = READ_LE_UINT16(kd + 0x10);
@@ -704,20 +623,6 @@ void EEMEngine::doHelp() {
 
 	const Common::String raw  = _mystery.textAt(chosenText);
 	Common::String text = parseString(raw, _playerName, _partner);
-
-	// Render as a speech-balloon overlay, mirroring _DisplayHint @ 1560:0009:
-	//
-	//   _GetKDTextBalloon(text, &bub);             // first-char dispatch
-	//   _GetBalloon(bub);                          // load balloon pic
-	//   y = (h < 0x4e) ? (0x50 - h) >> 1 : 1;      // vertical centre
-	//   _AddPicBackground(balloon, 0x21, y);       // overlay on screen
-	//   _WordWrap(0x21+tbl[bub].x, y+tbl[bub].y,   // text inside balloon
-	//             tbl[bub].w, text, -1, color=0);
-	//   _SayKDDigital(snd);                        // partner voice
-	//   _Wait();
-	//
-	// BG is the caller's CURRENT screen (site / PDA / gallery), not a cleared
-	// scratch.
 	Graphics::ManagedSurface ms(kScreenWidth, kScreenHeight,
 		Graphics::PixelFormat::createFormatCLUT8());
 	ms.clear();
@@ -729,13 +634,6 @@ void EEMEngine::doHelp() {
 		}
 	}
 
-	// Balloon shape dispatch via _GetKDTextBalloon @ 1df2:0105 ??? based on
-	// the first char of the parsed text. Digits select a specific balloon
-	// variant; non-digit defaults to 0x17. The digit, when present, is
-	// THEN consumed from the displayed text ??? mirrors _DisplayAlibi
-	// @ 1df2:0145's `str = pbVar7 + 1` advance after reading `*str` for
-	// bindx. _GetKDTextBalloon itself doesn't strip it (1df2:0105 just
-	// reads `*str`), so the caller has to.
 	const byte firstChar =
 		text.empty() ? (byte)0 : (byte)text[0];
 	uint16 bubNum = getKDTextBalloon(firstChar);
@@ -768,10 +666,6 @@ void EEMEngine::doHelp() {
 							   0, 0, kScreenWidth, kScreenHeight);
 	g_system->updateScreen();
 
-	// _DisplayHint @ 1560:0009 plays _SayKDDigital(soundnum) ??? a
-	// partner-specific voice line keyed to which hint type fired:
-	//   10 = first chain hint, 11 = second chain hint,
-	//    7 = generic KD (first), 8 = generic KD (second).
 	if (_audio && _mystery.kdTextIndex()) {
 		if (hintVoiceSlot >= 0)
 			_audio->sayKDHintDigital(_mystery.kdTextIndex(),
@@ -783,17 +677,8 @@ void EEMEngine::doHelp() {
 
 	waitForInput(60000);
 }
-
+// _InterfaceHelp(num) @ 1560:0205
 void EEMEngine::doInterfaceHelp(uint num) {
-	// Mirrors _InterfaceHelp(num) @ 1560:0205. The original walks
-	// HelpData @ 29be:00c8 (5-byte entries: u8 count, then up to 2 u16
-	// picIds), _GetPictures each one, blits via _Rect_Move_Mask(0, 0, ...)
-	// (a MASKED blit on top of the existing screen ??? transparent pixels
-	// show the caller's BG), and waits for click / key. ESC at 1560:02b3
-	// skips to the end. The function hides the cursor at the top
-	// (MOV [0x3a00], 0 @ 1560:0216 + _RemoveMouse @ 1000:542f at
-	// 1560:021c) and restores it at the tail (_DrawMouse @ 1000:5429
-	// at 1560:02e8). See kHelpPics comment for HelpData decoding.
 	if (num >= ARRAYSIZE(kHelpPics))
 		return;
 
@@ -824,8 +709,6 @@ void EEMEngine::doInterfaceHelp(uint num) {
 		debugC(1, kDebugScript, "doInterfaceHelp: pic 0x%x = %dx%d flags=0x%x",
 			   picId, pic.surface.w, pic.surface.h, pic.flags);
 
-		// transBlitFrom transp = pic.flags >> 8 matches _Rect_Move_Mask param_10
-		// @ 1000:03fc. Explicit (0,0) destPos: no-arg overload stretches to fill.
 		Graphics::ManagedSurface scratch(kScreenWidth, kScreenHeight,
 			Graphics::PixelFormat::createFormatCLUT8());
 		scratch.simpleBlitFrom(bg);
@@ -884,7 +767,7 @@ void EEMEngine::setPartnerEraseBg(const Graphics::ManagedSurface *bg) {
 
 uint16 EEMEngine::fitBalloonToText(uint16 bubNum,
 								   const Common::String &text) {
-	// Opt-in via "fit_dialog_balloons", CD only (floppy table unvalidated).
+	// Opt-in via "fit_dialog_balloons", CD only
 	if (isFloppy() || !ConfMan.getBool("fit_dialog_balloons"))
 		return bubNum;
 
@@ -969,12 +852,6 @@ bool EEMEngine::getBalloonIndicatorPos(uint16 bubNum, uint16 &dx,
 void EEMEngine::drawFloppyBubbleIndicator(Graphics::ManagedSurface &dst,
 										   uint16 bubNum, int ballX, int ballY,
 										   bool endIndicator) {
-	// Mirrors _DisplayHotspotClue_Floppy @ 22dc:08c0 (end-of-record) and
-	// @ 22dc:08aa (mid-pagination). Both grab a pre-loaded PIC:
-	//   DAT_28da_3034 = PIC 0xa0  "more pages" indicator
-	//   DAT_28da_3030 = PIC 0xa1  "end" indicator
-	// and stamp it at (ballX + insetTable[bubNum].indDX,
-	//                  ballY + insetTable[bubNum].indDY) via _AddPicBackground.
 	uint16 dx = 0;
 	uint16 dy = 0;
 	if (!getBalloonIndicatorPos(bubNum, dx, dy))
diff --git a/engines/eem/music.cpp b/engines/eem/music.cpp
index 31c51f225c5..80f20aa9bda 100644
--- a/engines/eem/music.cpp
+++ b/engines/eem/music.cpp
@@ -48,12 +48,7 @@ MusicPlayer::MusicPlayer(bool isFloppy) : _isFloppy(isFloppy) {
 	case MT_ADLIB:
 		// _MIDIPlayFile @ 20a2:024c opens SAMPLE.AD (string at 29be:14d6)
 		// and installs every patch the sequence requests via
-		// `_AIL_install_timbre`. ScummVM's Miles AdLib driver does the
-		// same on-demand install from SAMPLE.AD, which is what makes
-		// the notes match the 1993 release; the generic AdLib fallback
-		// would use ScummVM's built-in timbres instead. SAMPLE.OPL would
-		// be the OPL3 variant ??? the game only ships SAMPLE.AD, so the
-		// empty second path falls back to OPL2.
+		// `_AIL_install_timbre`.
 		_milesAudioMode = true;
 		_driver = Audio::MidiDriver_Miles_AdLib_create(
 			Common::Path("SAMPLE.AD"), Common::Path());
@@ -94,11 +89,7 @@ MusicPlayer::MusicPlayer(bool isFloppy) : _isFloppy(isFloppy) {
 void MusicPlayer::send(uint32 b) {
 	// Miles drivers (both AdLib and MT-32) implement their own per-
 	// source-channel mixing and timbre installation, so forward the raw
-	// event. Going through `MidiPlayer::send` would re-wrap CC 7 against
-	// `_masterVolume` AND remap the source channel via
-	// `sendToChannel` / `allocateChannel`, both of which the Miles
-	// driver already handles internally (double-applying breaks the
-	// timbre selection).
+	// event.
 	if (_milesAudioMode) {
 		_driver->send(b);
 		return;
@@ -113,7 +104,7 @@ void MusicPlayer::playFile(const Common::Path &xmiPath, bool loop) {
 	Common::StackLock lock(_mutex);
 	stop();
 
-	// _MIDIPlayFile @ 20a2:024c-029e (_fopen + _fread).
+
 	Common::File f;
 	if (!f.open(xmiPath)) {
 		warning("MusicPlayer: %s missing", xmiPath.toString().c_str());
@@ -147,15 +138,10 @@ void MusicPlayer::playFile(const Common::Path &xmiPath, bool loop) {
 		return;
 	}
 
-	// _LoopMIDI = 0xFFFF in _DoOpeningAnims.
 	_isLooping = loop;
 	_parser->property(MidiParser::mpAutoLoop, loop ? 1 : 0);
 	_parser->setTrack(0);
 
-	// Pull the launcher's music_volume slider into `_masterVolume` so
-	// the non-Miles `Audio::MidiPlayer::send` path scales correctly.
-	// (Miles drivers handle volume themselves but also honour
-	// `MidiDriver::syncSoundSettings` via `Engine::syncSoundSettings`.)
 	syncVolume();
 	_isPlaying = true;
 	debugC(1, kDebugSound, "MusicPlayer: playing %s (%u bytes, loop=%d, miles=%d)",
@@ -168,7 +154,7 @@ void MusicPlayer::playMus(uint num, bool loop) {
 	//   0..4 ??? travel music. Table at 2608:1399-13cd holds 5 entries
 	//          (Travel-6, Travel-4, Travel-7, Travel-1, Travel-8) used by
 	//          `_StartTravelMusic` via `siteNumber % 5`.
-	//   5    ??? FANFARE2.XMI (winner). String at 2608:0c64.
+	//   5    ??? FANFARE2.XMI (winner).
 	//   6    ??? no equivalent on floppy (loser sting in `_DisplayAlibi`
 	//          is CD-only); skip.
 	if (_isFloppy) {
diff --git a/engines/eem/music.h b/engines/eem/music.h
index 11001a4280a..8209df47d89 100644
--- a/engines/eem/music.h
+++ b/engines/eem/music.h
@@ -53,7 +53,6 @@ public:
 	explicit MusicPlayer(bool isFloppy = false);
 
 	/// _MIDIPlayFile @ 20a2:024c. loop=true mirrors
-	/// _LoopMIDI = 0xFFFF in _DoOpeningAnims.
 	void playFile(const Common::Path &xmiPath, bool loop = false);
 
 	/// _MIDIPlay(num) @ 20a2:047d. CD: "MUS%05u.XMI";
diff --git a/engines/eem/mystery.cpp b/engines/eem/mystery.cpp
index 715971eb77c..1734c804175 100644
--- a/engines/eem/mystery.cpp
+++ b/engines/eem/mystery.cpp
@@ -175,7 +175,6 @@ bool Mystery::load(uint num, Common::RandomSource *rng) {
 		return true;
 	}
 
-	// Header is 16-bit-word indexed (matches `int *piVar1 = __Mystery; piVar1[N]`).
 	_initOffset      = readU16(0  * 2);
 	_mapOffset       = readU16(2  * 2);
 	_siteIndexOffset = readU16(3  * 2);
@@ -191,10 +190,6 @@ bool Mystery::load(uint num, Common::RandomSource *rng) {
 	_numCONSITEs = (uint8)readU16(14 * 2);
 	_numCOFFSITEs = (uint8)readU16(15 * 2);
 
-	// Defensive clamp: the floppy header layout differs (M0.BIN CD has
-	// numSites=readU16(0x14)=3; floppy has readU16(0x14)=0x1925, clearly
-	// not a count). Cap to `_onSites` / `_visitedSite` array capacity so
-	// downstream loops don't walk off the end on malformed/floppy files.
 	if (_numSites > kVisitedSiteCap)
 		_numSites = kVisitedSiteCap;
 
@@ -204,10 +199,6 @@ bool Mystery::load(uint num, Common::RandomSource *rng) {
 		_cChain[i] = readU16((26 + i) * 2);
 	}
 
-	// Per-mystery runtime state ??? _ReadMystery zeroes these at load.
-	// _newOrder uses identity mapping; original randomly cycles gallery
-	// positions but requires matching changes in both clue side-effect
-	// and rendering paths.
 	memset(_cluesFound, 0, sizeof(_cluesFound));
 	memset(_noteSelected, 0, sizeof(_noteSelected));
 	memset(_hotSpotsSeen, 0, sizeof(_hotSpotsSeen));
@@ -326,9 +317,6 @@ void Mystery::loadFloppySiteAnimData() {
 
 const byte *Mystery::hotspots(uint siteNum) const {
 	if (_isFloppy) {
-		// Floppy hotspot table inside per-site sub-blob (FUN_22dc_0b80 @ 22dc:0b80).
-		// site_data[+4..5] = u16 file offset to count byte + N x 8-byte rects
-		// (x1, y1, x2, y2 as u16s).
 		const byte *site = siteData(siteNum);
 		if (!site || (size_t)(site - _data.data()) + 6 > _data.size())
 			return nullptr;
@@ -395,7 +383,6 @@ uint16 Mystery::noteIndexCount() const {
 	// NoteIndex runs from _noteOffset to start of GalleryData.
 	// CD entries: 4 bytes (u16 textOff; u16 points).
 	// Floppy entries: 7 bytes (u16 ?; u16 jakeOff; u16 jennyOff; u8 score)
-	// per FUN_22dc_05c8 @ 22dc:0843.
 	if (_galleryOffset <= _noteOffset)
 		return 0;
 	const uint stride = _isFloppy ? 7 : 4;
@@ -474,9 +461,6 @@ const byte *Mystery::floppySuspectEntry(uint suspectIdx) const {
 }
 
 bool Mystery::isGuilty(uint suspectIdx) const {
-	// _WITCH @ 1df2:089f (CD): GalleryData[i*0x46 + 0x02] == 0xFFFF marks
-	// guilty; innocent suspects store their alibi TextBlock offset there.
-	// Floppy uses same convention at suspect entry +2..3 (variable stride).
 	if (_isFloppy) {
 		const byte *e = floppySuspectEntry(suspectIdx);
 		return e && READ_LE_UINT16(e + 2) == 0xFFFF;
@@ -490,9 +474,6 @@ bool Mystery::isGuilty(uint suspectIdx) const {
 
 uint16 Mystery::alibiTextOffset(uint suspectIdx) const {
 	if (_isFloppy) {
-		// Floppy alibi (_DisplayAlibi_Floppy @ 1d40:0145): u16 at suspect +2..3.
-		// 0xFFFF = guilty; else high byte indexes the TEXT_BLOCK table at
-		// header[+0xc], each entry u16 = absolute alibi-text offset.
 		const byte *e = floppySuspectEntry(suspectIdx);
 		if (!e)
 			return 0xFFFF;
@@ -570,12 +551,11 @@ int Mystery::selectedPoints() const {
 	}
 	return total;
 }
-
+// `_SolvedCheck @ 1ea1:0b1a`. Reuses the already-loaded hint chains
+// (_aChain/_bChain/_cChain) as the three answer sets and the shared
+// _noteSelected accuse-selection array ??? see londonSolved() doc in the
+// header. EEM1/floppy keep the points model via solvedCheck().
 bool Mystery::londonSolved() const {
-	// `_SolvedCheck @ 1ea1:0b1a`. Reuses the already-loaded hint chains
-	// (_aChain/_bChain/_cChain) as the three answer sets and the shared
-	// _noteSelected accuse-selection array ??? see londonSolved() doc in the
-	// header. EEM1/floppy keep the points model via solvedCheck().
 	for (uint chain = 0; chain < 3; chain++) {
 		int remaining = (int)kChainLen;
 		int wild = 0;
@@ -595,12 +575,11 @@ bool Mystery::londonSolved() const {
 	}
 	return false;
 }
-
+// `_GetMinRemaining @ 1ea1:1056`. Parallel to londonSolved() but tests the
+// three answer sets against _cluesFound (discovered in the world). Tracks
+// whether any non-wildcard answer clue has been found at all; if none, the
+// player has nothing relevant yet (returns kChainLen).
 int Mystery::minCluesRemaining() const {
-	// `_GetMinRemaining @ 1ea1:1056`. Parallel to londonSolved() but tests the
-	// three answer sets against _cluesFound (discovered in the world). Tracks
-	// whether any non-wildcard answer clue has been found at all; if none, the
-	// player has nothing relevant yet (returns kChainLen).
 	int best = (int)kChainLen;
 	int foundReal = 0;
 	for (uint chain = 0; chain < 3; chain++) {
diff --git a/engines/eem/mystery.h b/engines/eem/mystery.h
index 3793debedec..86f13dc7aeb 100644
--- a/engines/eem/mystery.h
+++ b/engines/eem/mystery.h
@@ -84,9 +84,7 @@ public:
 	const byte *floppySuspectEntry(uint suspectIdx) const;
 
 	/// NoteIndex array. EEM1 CD: 4 bytes/entry (u16 textOff + u16 pts).
-	/// EEM2/London CD: 2 bytes/entry (u16 textOff only ??? no points field;
-	/// `_DrawNotes @ 16a0:01de` reads `noteIndex[clueId*2]`). Floppy: 7
-	/// bytes/entry.
+	/// Floppy: 7 bytes/entry. EEM2/London CD: 2 bytes/entry. 
 	const byte *noteIndex() const;
 
 	uint16 noteIndexCount() const;
@@ -181,7 +179,6 @@ public:
 	/// world, = _CluesFound): returns the fewest clues still to find across the
 	/// usable sets ??? 0 means a full answer set has been discovered and the
 	/// accusation can begin. Returns 5 when no relevant clue has been found yet.
-	/// Drives the London accuse-readiness gate (`_AccuseEntry @ 1ea1:115c`).
 	int minCluesRemaining() const;
 
 	/// _WITCH @ 1df2:089f. GalleryData[i*0x46 + 0x02] == 0xFFFF marks the
@@ -197,7 +194,7 @@ public:
 
 	/// Per-mystery runtime state, zeroed at load time.
 	uint8  _cluesFound[kCluesFoundCap]   = {};
-	uint8  _noteSelected[kCluesFoundCap] = {};  ///< _NoteSelected
+	uint8  _noteSelected[kCluesFoundCap] = {};
 	uint16 _hotSpotsSeen[kHotSpotsCap]   = {};
 	uint16 _inGallery[kGalleryCap]       = {};
 	uint8  _newOrder[kGalleryCap]        = {};
@@ -213,8 +210,8 @@ public:
 	uint16 _searchLocationNumber = 0xFFFF;
 	uint16 _siteNumber           = 0xFFFF;
 	uint16 _lastSite             = 0xFFFF;
-	uint16 _pendingSiteJump      = 0;      ///< EEM2 _DisplayClue destination site (DAT_2bca_0282).
-	uint16 _siteReturnDepth      = 0;      ///< EEM2 nested site return stack depth (DAT_2bca_0280).
+	uint16 _pendingSiteJump      = 0;
+	uint16 _siteReturnDepth      = 0;
 	uint16 _siteReturnStack[kVisitedSiteCap] = {};
 
 private:
@@ -240,7 +237,6 @@ private:
 	uint16 _bChain[kChainLen] = {};
 	uint16 _cChain[kChainLen] = {};
 
-	// Floppy variant ??? see Mystery::load. _ReadMystery_Floppy @ 22dc:0178.
 	bool   _isFloppy = false;
 	uint16 _floppySuspectsOff = 0;   ///< header[+4]    suspects
 	uint16 _floppyHintBlockOff = 0;  ///< header[+6]    hint -> clue table
diff --git a/engines/eem/resource.cpp b/engines/eem/resource.cpp
index d8722ceb598..1371cd177e7 100644
--- a/engines/eem/resource.cpp
+++ b/engines/eem/resource.cpp
@@ -53,7 +53,6 @@ bool DBDArchive::open(const Common::Path &dbdName, const Common::Path &dbxName)
 		return false;
 	}
 
-	// _InitGraphicsSystem @ 172b:0145: 10-byte entries until EOF.
 	const int32 dbxSize = dbx.size();
 	_index.reserve(dbxSize / 10);
 	while (dbx.pos() + 10 <= dbxSize) {
@@ -113,11 +112,7 @@ bool readFrame(Common::SeekableReadStream &stream, bool compressed, Picture &out
 
 bool DBDArchive::loadEntry(uint num, Picture &out) {
 	if (num >= _index.size()) {
-		// Out-of-range picture IDs are non-fatal ??? every caller already
-		// checks the return value (e.g. `haveDone` / `haveCrime` in
-		// `drawBigMapOverview`). Floppy PICS.DBD ships fewer entries than
-		// CD (e.g. BigMap done-marker 0x20D is CD-only), so this fires
-		// routinely on floppy and stays at debug level rather than warning.
+		// Out-of-range picture IDs are non-fatal
 		debugC(2, kDebugGfx,
 			   "DBDArchive::loadEntry: %u out of range (max %u)",
 			   num, (uint)_index.size());
@@ -130,7 +125,7 @@ bool DBDArchive::loadEntry(uint num, Picture &out) {
 		return false;
 	}
 
-	// _GetFromDB @ 172b:105d. Leading u16 = frame count (always 1 for pictures).
+	// Leading u16 = frame count (always 1 for pictures).
 	_dbd.skip(2);
 	return readFrame(_dbd, entry.compressed != 0, out);
 }
@@ -147,7 +142,6 @@ bool DBDArchive::loadAnimation(uint num, Animation &out) {
 		return false;
 	}
 
-	// _GetAnimation @ 172b:163a: u16 frame count, then N frames.
 	const uint16 frameCount = _dbd.readUint16LE();
 	if (frameCount == 0 || frameCount > 256) {
 		warning("DBDArchive::loadAnimation: %u has implausible frame count %u",
diff --git a/engines/eem/resource.h b/engines/eem/resource.h
index a984f323d35..2e19340ff4a 100644
--- a/engines/eem/resource.h
+++ b/engines/eem/resource.h
@@ -31,7 +31,6 @@
 
 namespace EEM {
 
-/// dbi struct from _InitGraphicsSystem @ 172b:0145 (10 bytes per entry).
 struct DBEntry {
 	uint32 offset;     ///< Byte offset in the .DBD file.
 	uint16 compressed; ///< Non-zero = PKWARE DCL ("Implode") packed payload.
diff --git a/engines/eem/site.cpp b/engines/eem/site.cpp
index 7c4884ac7d6..ce2b9d89ab4 100644
--- a/engines/eem/site.cpp
+++ b/engines/eem/site.cpp
@@ -91,7 +91,6 @@ void blitFrame(Graphics::ManagedSurface &dst, const Picture &p,
 
 // Masked top-left blit onto a locked screen surface. Clips both src and
 // dst against the screen, then delegates to copyRectToSurfaceWithKey
-// (Graphics::Surface's transparent-key blit).
 void keyBlitToScreen(Graphics::Surface *screen, const Picture &p,
 							int x, int y) {
 	if (!screen || p.surface.empty())
@@ -107,21 +106,12 @@ void keyBlitToScreen(Graphics::Surface *screen, const Picture &p,
 									 src, (uint32)(byte)(p.flags >> 8));
 }
 
-// Top-left masked blit. `_AddDrop @ 172b:1a77` calls
-// `_Rect_Move_Mask(..., x, y, ...)` with the raw (x, y) and IGNORES
-// per-frame anchor offsets ??? so this is the correct path for static
-// drops and any non-animated overlay. Animations must route through
-// `blitAnimFrameAnchored` instead so per-frame anchor offsets
-// (miscflags = X, rowoff = Y) apply correctly.
 void blitMaskedSurface(Graphics::Surface *screen, const Picture &p,
 					   int x, int y) {
 	keyBlitToScreen(screen, p, x, y);
 }
 
-// `_UpdateAnimations @ 172b:09c1`: blit at
-//   (anchor_x - puVar5[4], anchor_y - puVar5[3])
-// where puVar5[3]/[4] are per-frame rowoff/miscflags (signed int16) from
-// the 16-byte PicData header. Transparency = flags >> 8.
+// `_UpdateAnimations @ 172b:09c1`
 void blitAnimFrameAnchored(Graphics::Surface *screen, const Picture &p,
 						   int anchorX, int anchorY) {
 	keyBlitToScreen(screen, p,
@@ -131,9 +121,7 @@ void blitAnimFrameAnchored(Graphics::Surface *screen, const Picture &p,
 
 // `_ColorCycle @ 172b:2015` ??? rotate `_fpal[start..end]` by one slot:
 // save [start], shift [start..end-1] = [start+1..end], restore saved at
-// [end], then re-upload via `_Set_Palette`. We do the same against
-// ScummVM's palette manager. Used by per-site Loop-1 ColorCycle entries
-// and the always-on hotspot marching-ants range 0xF9..0xFE.
+// [end], then re-upload via `_Set_Palette`. 
 void cyclePaletteRange(uint8 start, uint8 end) {
 	if (end <= start)
 		return;
@@ -153,12 +141,8 @@ void cyclePaletteRange(uint8 start, uint8 end) {
 	buf[(count - 1) * 3 + 2] = savedB;
 	g_system->getPaletteManager()->setPalette(buf, start, count);
 }
-
+// `_OpenColorCycle @ 2520:04f7`
 void cyclePaletteRangeReverse(uint8 start, uint8 end) {
-	// `_OpenColorCycle @ 2520:04f7`: save END, shift every entry up by
-	// one (END-1 ??? END, ...), wrap saved END to START. Visually colors
-	// march from start toward end ??? opposite direction from
-	// `cyclePaletteRange`. Used by the EA Kids / HighScore logo cycles.
 	if (end <= start)
 		return;
 	const uint count = (uint)end - (uint)start + 1;
@@ -180,11 +164,6 @@ void cyclePaletteRangeReverse(uint8 start, uint8 end) {
 }
 
 void applyHotspotGlowPalette() {
-	// SITEPALS ships palette 0xF9..0xFE as uniform yellow (3F 3E 00), so the
-	// original marching-ants was a placeholder. Override with a 6-step yellow
-	// ramp so `cyclePaletteRange(0xF9, 0xFE)` (and the per-index draw colour)
-	// produce a visible pulse. Shared by site hotspots and the clue puzzle so
-	// both highlight clickable areas with the same (original) colours.
 	static const byte kAntsGlow[6 * 3] = {
 		0x40, 0x40, 0x00, // F9 ??? dim
 		0x80, 0x80, 0x00, // FA
@@ -211,8 +190,6 @@ const uint16 kWaitAnims[7][6] = {
 	{ 0x06, 0x06, 0x06, 0x06, 0x50, 0x50 }, // 6
 };
 
-// `_DoKDAnim` table @ 29be:0228. 6 entries (kdAnimNum 0..5).
-// Layout: { animJake, animJenny, xJake, xJenny, yJake, yJenny }.
 const uint16 kKdAnimTable[6][6] = {
 	{ 0x03, 0x0c, 6, 6, 80, 80 }, // 0 ??? speaker idx 1 wait anim
 	{ 0x01, 0x0b, 6, 6, 80, 80 }, // 1 ??? same as PDA idle
@@ -222,11 +199,7 @@ const uint16 kKdAnimTable[6][6] = {
 	{ 0x06, 0x06, 6, 6, 80, 80 }, // 5 ??? same anim both partners
 };
 
-// EEM2 `_DoKDAnim @ 1717:05bf` table @ 2bca:0238 (read from EEM2CD.EXE).
-// Same { animJake, animJenny, xJake, xJenny, yJake, yJenny } layout. Positions
-// differ (x???2, y???78 vs EEM1's 6/80) and Jenny's reactions 4/5 use distinct
-// anims 0x55/0x2d (EEM1 reused 0x05/0x06 for both partners). Selected by
-// `playKdAnim` when the London variant is active.
+// EEM2 `_DoKDAnim @ 1717:05bf` table @ 2bca:0238 
 const uint16 kKdAnimTableLondon[6][6] = {
 	{ 0x03, 0x0c, 3, 2, 66, 65 }, // 0
 	{ 0x01, 0x0b, 2, 2, 78, 78 }, // 1
@@ -236,8 +209,7 @@ const uint16 kKdAnimTableLondon[6][6] = {
 	{ 0x06, 0x2d, 2, 2, 78, 78 }, // 5 ??? Jenny uses 0x2d
 };
 
-// EEM2 `_WaitAnims @ 2bca:022c`. Entry 0 precedes `_DoKDAnim`'s
-// table; entries 1..6 overlap `kKdAnimTableLondon`, same as EEM1.
+// EEM2 `_WaitAnims @ 2bca:022c`.
 const uint16 kWaitAnimsLondon[7][6] = {
 	{ 0x00, 0x0a, 2, 2, 78, 78 }, // 0
 	{ 0x03, 0x0c, 3, 2, 66, 65 }, // 1
@@ -248,13 +220,7 @@ const uint16 kWaitAnimsLondon[7][6] = {
 	{ 0x06, 0x2d, 2, 2, 78, 78 }, // 6
 };
 
-// Animation script table ??? mirrors `_AnimationSequences @ 29be:22d4`
-// (55-entry table of far ptrs, each pointing to a u16-frame-index
-// stream). `_NewAnimation @ 172b:06e1` reads the script via
-// `_AnimationSequences[anim_id]` and stores the pointer in
-// `DAT_2d5d_3eaf[i*0xb]`; `_UpdateAnimations @ 172b:09c1` then walks it
-// one entry per `_CheckFrameRate` tick (~140 ms).
-//
+// Animation script table (`_AnimationSequences @ 29be:22d4`)
 // Script byte format:
 //   0x80         = restart (loop back to index 0; terminator for one-shots)
 //   0x81 N       = jump to byte N (not used in partner subset)
@@ -353,8 +319,6 @@ const AnimScript kAnimScripts[] = {
 	{ 0x35, 11, { 0,1,2,3,4,5,6,7,8,9,10 } },
 };
 
-// Scripts longer than 28 frames (>fits-inline limit). `findAnimScript`
-// checks both this array and `kAnimScripts`. Longest is 0x22 (115 frames).
 struct AnimScriptLong {
 	uint16 seqnum;
 	uint16 len;
@@ -362,9 +326,7 @@ struct AnimScriptLong {
 };
 
 // Briefing animations ??? `_DoInitClues @ 1a35:0411` always uses anim ID
-// 0x17 (game) / 0x18 (book) / 0x19 (nancy) regardless of partner; the
-// per-partner ANI.DBD cells come from a separate entry (e.g. 0x3b for
-// Jenny's briefing).
+// 0x17 (game) / 0x18 (book) / 0x19 (nancy) regardless of partner
 const uint8 kScript17[] = {
 	0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,
 	20,21,22,23,24,25,26,27,28,29
@@ -378,10 +340,7 @@ const uint8 kScript19[] = {
 	1,2,3,4,5,6,7,8,9,10,11,12
 };
 
-// Site / NPC drop scripts (29be:22d4 entries 0x1a..0x36). Repeated
-// frames are the original's frame-hold mechanism (one entry per tick).
-
-// 0x1a (29be:19a4) ??? count-up 0..7, idle hold, repeat 1..7, idle, mirror 7..0, idle.
+// Site / NPC drop scripts
 const uint8 kScript1a[] = {
 	0,1,2,3,4,5,6,7,
 	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
@@ -390,7 +349,7 @@ const uint8 kScript1a[] = {
 	7,6,5,4,3,2,1,
 	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
 };
-// 0x1e (29be:1a40) ??? slow walk-stutter with idle tail (76 entries).
+
 const uint8 kScript1e[] = {
 	0,1,2,3,3,3,3,4,4,3,4,4,4,4,4,3,
 	5,5,5,5,5,5,5,5,4,4,4,4,4,4,4,4,
@@ -398,8 +357,7 @@ const uint8 kScript1e[] = {
 	7,8,7,7,7,7,7,8,
 	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
 };
-// 0x1f (29be:1ada) ??? 0..5, idle, 0..5, idle, 6..8 alternation,
-// idle (50 entries).
+
 const uint8 kScript1f[] = {
 	0,1,2,3,4,5,
 	0,0,0,0,
@@ -410,13 +368,12 @@ const uint8 kScript1f[] = {
 	6,
 	0,0,0,0,0
 };
-// 0x20 (29be:1b40) ??? count-up 0..33 (34 frames).
+
 const uint8 kScript20[] = {
 	0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,
 	20,21,22,23,24,25,26,27,28,29,30,31,32,33
 };
-// 0x22 (29be:1ba4) ??? long held-frame walker 0..22 with idle tail
-// (115 entries; most frames held 4-7 ticks each).
+
 const uint8 kScript22[] = {
 	0,
 	1,1,1,1,1,
@@ -443,8 +400,7 @@ const uint8 kScript22[] = {
 	22,22,22,22,
 	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
 };
-// 0x23 (29be:1c8c) ??? 29 entries: 0, 6 holds of 1, count-up 2..4,
-// down-up gesture, 5 idle frames.
+
 const uint8 kScript23[] = {
 	0,1,1,1,1,1,1,
 	2,3,4,3,2,
@@ -452,39 +408,34 @@ const uint8 kScript23[] = {
 	2,3,4,3,3,3,3,3,
 	0,0,0,0,0,0
 };
-// 0x24 (29be:1cc8) ??? bell-curve hold (58 entries): 0,0, 1,1, 2,2,
-// 3 held for 26 ticks, mirror back, idle.
+
 const uint8 kScript24[] = {
 	0,0,1,1,2,2,
 	3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
 	2,2,1,1,
 	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
 };
-// 0x28 (29be:1d7e) ??? gentle hold 0..3 with long hold on 3, mirror
-// back, idle (45 entries).
+
 const uint8 kScript28[] = {
 	0,1,1,2,2,
 	3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
 	2,2,1,1,
 	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
 };
-// 0x29 (29be:1dda) ??? paired-step count-up 0..21 plus idle
-// (58 entries).
+
 const uint8 kScript29[] = {
 	0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,
 	11,11,12,12,13,13,14,14,15,15,16,16,17,17,18,18,19,19,
 	20,20,21,21,
 	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
 };
-// 0x2b (29be:1e5c) ??? count-up 0..11 with each frame held 4 ticks
-// (48 entries).
+
 const uint8 kScript2b[] = {
 	0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,
 	4,4,4,4,5,5,5,5,6,6,6,6,7,7,7,7,
 	8,8,8,8,9,9,9,9,10,10,10,10,11,11,11,11
 };
-// 0x2c (29be:1ebe) ??? alternation walk 0..19 with idle tail
-// (54 entries).
+
 const uint8 kScript2c[] = {
 	0,1,2,3,4,5,
 	0,
@@ -495,8 +446,7 @@ const uint8 kScript2c[] = {
 	16,17,18,19,
 	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
 };
-// 0x2d (29be:1f2c) ??? count-up 0..11 with each frame held 8 ticks
-// (96 entries).
+
 const uint8 kScript2d[] = {
 	0,0,0,0,0,0,0,0,
 	1,1,1,1,1,1,1,1,
@@ -511,8 +461,7 @@ const uint8 kScript2d[] = {
 	10,10,10,10,10,10,10,10,
 	11,11,11,11,11,11,11,11
 };
-// 0x30 (29be:1fee) ??? 0,0, count-up 1..19, idle, mirror down, extra
-// idle (86 entries).
+
 const uint8 kScript30[] = {
 	0,0,
 	1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,
@@ -521,7 +470,7 @@ const uint8 kScript30[] = {
 	5,4,4,3,3,2,2,1,1,
 	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
 };
-// 0x31 (29be:209c) ??? paired-step idle alternations (57 entries).
+
 const uint8 kScript31[] = {
 	0,0,0,1,1,1,
 	0,0,0,1,1,1,
@@ -535,8 +484,7 @@ const uint8 kScript31[] = {
 	0,0,0,
 	1,1,1
 };
-// 0x36 (29be:2110) ??? 0..8 forward, 1..8 forward, frame 1 held 20
-// ticks, 8..0 mirror, idle tail (60 entries).
+
 const uint8 kScript36[] = {
 	0,1,2,3,4,5,6,7,8,
 	1,2,3,4,5,6,7,8,
@@ -567,10 +515,9 @@ const AnimScriptLong kAnimScriptsLong[] = {
 };
 
 // `_PatientSequence` and `_ImpatientSequence` are standalone script
-// pointers, not entries in `_AnimationSequences`. CD has the data but
-// never calls the switchers; floppy calls them from `_DoSiteLoop_Floppy`
-// (via `_Switch2Patient` / `_Switch2Impatient`). We intentionally enable
-// the same switch for both builds.
+// pointers. CD has the data but never calls the switchers; floppy calls 
+// them from `_DoSiteLoop_Floppy` (via `_Switch2Patient` / `_Switch2Impatient`). 
+// We intentionally enable the same switch for both builds.
 const uint8 kPatientSequence[]   = { 0,0,0,0,0,0,0,0,0,2 };
 const uint8 kImpatientSequence[] = { 0,1,0,1,0,1,0,1,2,1 };
 
@@ -737,11 +684,6 @@ AnimScriptRef findAnimScript(uint16 seqnum) {
 }
 
 // Original frame period from `_InitFrameCounter @ 1a35:01ae`:
-//   LastFrame    = cs_within_hour + 0xe
-//   cs_within_hour = ((ti_min * 60) + ti_sec) * 100 + ti_hund
-// (Borland C `struct time` memory order is min, hour, hund, sec.)
-// `+ 0xe` is 14 centiseconds ??? ~140 ms per frame, matching
-// `_CheckFrameRate @ 1a35:0204`.
 const uint kFramePeriodMs = 140;
 
 uint frameFromScriptAtTick(const uint8 *frames, uint len,
@@ -753,109 +695,15 @@ uint frameFromScriptAtTick(const uint8 *frames, uint len,
 	return (numFrames > 0) ? MIN<uint>(frame, numFrames - 1) : 0;
 }
 
-void auditPartnerAnims(EEMEngine *vm) {
-	// Cross-check every script against the ANI.DBD entry it references;
-	// warn on out-of-range frame requests.
-	if (!vm)
-		return;
-	DBDArchive &ani = vm->getAni();
-
-	struct Walker {
-		static void check(DBDArchive &ani, uint16 id, const uint8 *frames, uint8 len) {
-			if (len == 0)
-				return;
-			Animation a;
-			if (!ani.loadAnimation(id, a) || a.empty()) {
-				debugC(1, kDebugSite,
-					   "auditPartnerAnims: anim 0x%02x failed to load", id);
-				return;
-			}
-			uint maxRequested = 0;
-			for (uint j = 0; j < len; j++)
-				if (frames[j] > maxRequested)
-					maxRequested = frames[j];
-			if (maxRequested >= a.size()) {
-				warning("anim 0x%02x: script wants frame %u but ANI.DBD has "
-						"only %u ??? frames will be clamped",
-						id, maxRequested, (uint)a.size());
-			} else {
-				debugC(2, kDebugSite,
-					   "anim 0x%02x: %u cells, script max=%u, len=%u",
-					   id, (uint)a.size(), maxRequested, len);
-			}
-		}
-	};
-
-	for (uint i = 0; i < ARRAYSIZE(kAnimScripts); i++)
-		Walker::check(ani, kAnimScripts[i].seqnum,
-					  kAnimScripts[i].frames, kAnimScripts[i].len);
-	for (uint i = 0; i < ARRAYSIZE(kAnimScriptsLong); i++)
-		Walker::check(ani, kAnimScriptsLong[i].seqnum,
-					  kAnimScriptsLong[i].frames, kAnimScriptsLong[i].len);
-
-	// Per-frame anchor-offset audit (`_UpdateAnimations @ 172b:09c1`
-	// applies (anchor_x - miscflags, anchor_y - rowoff)). Log non-zero
-	// anchors so we know which anims need `blitAnimFrameAnchored`.
-	const uint16 partnerIds[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05,
-								   0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c,
-								   0x0d, 0x0f, 0x10, 0x11, 0x12, 0x13,
-								   0x14, 0x15, 0x16, 0x17, 0x18, 0x19 };
-	for (uint i = 0; i < ARRAYSIZE(partnerIds); i++) {
-		const uint16 id = partnerIds[i];
-		Animation a;
-		if (!ani.loadAnimation(id, a) || a.empty())
-			continue;
-		bool anyAnchor = false;
-		int rowMin = 0, rowMax = 0, miscMin = 0, miscMax = 0;
-		for (uint f = 0; f < a.size(); f++) {
-			const Picture &fr = a[f];
-			const int sRow  = (int)(int16)fr.rowoff;
-			const int sMisc = (int)(int16)fr.miscflags;
-			if (sRow != 0 || sMisc != 0) {
-				if (!anyAnchor) {
-					rowMin = rowMax = sRow;
-					miscMin = miscMax = sMisc;
-					anyAnchor = true;
-				} else {
-					rowMin = MIN(rowMin, sRow);
-					rowMax = MAX(rowMax, sRow);
-					miscMin = MIN(miscMin, sMisc);
-					miscMax = MAX(miscMax, sMisc);
-				}
-			}
-		}
-		if (anyAnchor) {
-			// Signed int16 (puVar5[3]/[4] in `_UpdateAnimations`).
-			debugC(1, kDebugSite,
-				   "anim 0x%02x: per-frame anchor (rowoff [%d..%d], "
-				   "miscflags [%d..%d]) ??? handled by "
-				   "`blitAnimFrameAnchored`",
-				   id, rowMin, rowMax, miscMin, miscMax);
-		}
-	}
-}
-
 // Looping path of `_UpdateAnimations`: walk the script one entry per
 // `_CheckFrameRate` tick (`kFramePeriodMs` ~= 140 ms), wrap on 0x80.
-// If `seqnum` has no registered script, falls back to flipbook
-// (`tick % numFrames`) so unknown anims still move. The script can in
-// theory request a frame past the asset's actual cell count (misencoded
-// script) ??? `frameFromScriptAtTick` clamps to `numFrames - 1` so the
-// caller doesn't read past `anim[]`. Exported (non-static) so BigMap,
-// CaseSelection greeter, Notebook, and Gallery render paths in
-// `ui.cpp` use the same cadence.
 uint partnerFrameAtTick(uint16 seqnum, uint numFrames, uint32 tickMs) {
 	const AnimScriptRef s = findAnimScript(seqnum);
 	return frameFromScriptAtTick(s.frames, s.len, numFrames, tickMs);
 }
 
 // Play `unfold` once, then loop `waitSeq` forever. Mirrors the
-// original's slot-script-swap idiom: the entrance script runs to its
-// 0x80 terminator, then the slot's script pointer is rewritten to a
-// looping wait sequence (e.g. `_BigMapWaitSeq @ 29be:1574`,
-// `_SmallMapWaitSeq @ 29be:1548`). `partnerFrameAtTick` can't model
-// that swap on its own (it always wraps on the same script), hence
-// this helper.
+// original's slot-script-swap idiom
 uint oneShotThenLoopFrameAtTick(const uint8 *unfold, uint unfoldLen,
 									   const uint8 *waitSeq, uint waitSeqLen,
 									   uint numFrames, uint32 elapsedMs) {
@@ -867,10 +715,6 @@ uint oneShotThenLoopFrameAtTick(const uint8 *unfold, uint unfoldLen,
 }
 
 uint bigMapPartnerFrameAtTick(uint numFrames, uint32 elapsedMs) {
-	// Slot starts on script 0x14 (count-up 0..8 @ 29be:196a). On 0x80
-	// terminator, `_DoBigMap` rewrites the slot's script pointer to
-	// `_BigMapWaitSeq @ 29be:1574` = (9,9,9,9,10,9,9,9,9, 0x80) ??? the
-	// open-map hold with a fidget.
 	static const uint8 kUnfold[]  = { 0, 1, 2, 3, 4, 5, 6, 7, 8 };
 	static const uint8 kWaitSeq[] = { 9, 9, 9, 9, 10, 9, 9, 9, 9 };
 	return oneShotThenLoopFrameAtTick(kUnfold, ARRAYSIZE(kUnfold),
@@ -879,10 +723,6 @@ uint bigMapPartnerFrameAtTick(uint numFrames, uint32 elapsedMs) {
 }
 
 uint bigMapDetailPartnerFrameAtTick(uint numFrames, uint32 elapsedMs) {
-	// Slot starts on script 0x13 (count-up 0..7 @ 29be:1992). On 0x80
-	// terminator, `_DoMapScreen @ 20fe:1390` rewrites the slot pointer
-	// (`MOV [BX+0x789f],0x1548`) to `_SmallMapWaitSeq @ 29be:1548` =
-	// 18 entries holding cell 7 with a single cell-10 fidget (~1.8 s).
 	static const uint8 kUnfold[]  = { 0, 1, 2, 3, 4, 5, 6, 7 };
 	static const uint8 kWaitSeq[] = {
 		7, 7, 7, 10, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7
@@ -929,8 +769,6 @@ bool SiteScreen::playLondonTravelAnimation(uint fromSite, uint toSite) {
 		return false;
 	}
 
-	// EEM2 `_DoTravel @ 1717:06ed`: kind 3 forces partner suffix 0,
-	// so the shipped set is TRAVEL00/01/10/11/20.ANM.
 	const uint partnerSuffix = (travelKind == 3) ? 0 : _vm->getPartnerIndex();
 	const Common::String name = Common::String::format("TRAVEL%u%u.ANM",
 		(uint)travelKind - 1, partnerSuffix);
@@ -958,16 +796,12 @@ void SiteScreen::enter(uint siteNum, bool resetPartnerMood) {
 		return;
 	}
 
-	// `_DoSiteLoop @ 168d:0436`: `_NewAnimation` sets the new slot's
-	// frame index to 0xffff (-1) ??? starts at script[0].
 	_waitPhaseAnchor = g_system->getMillis();
 	if (resetPartnerMood) {
 		_partnerWaitMood = kPartnerWaitDefault;
 		initImpatienceCounter();
 	}
 
-	// `_DoSiteLoop @ 168d:03f4`:
-	//   if (_VisitedSite[_SiteNumber] == 0) _DisplayClue(...);
 	const bool firstVisit = (siteNum < Mystery::kVisitedSiteCap)
 							 && (_mystery->_visitedSite[siteNum] == 0);
 
@@ -980,8 +814,6 @@ void SiteScreen::enter(uint siteNum, bool resetPartnerMood) {
 
 	if (playArrival) {
 		if (_vm->isLondon()) {
-			// EEM2 `HandleLondonSiteLoop @ 1717:083a` calls `_DoTravel`
-			// before `_BuildBackground` for the destination site.
 			bool showedApproach = false;
 			const uint16 approachId = sd ? READ_LE_UINT16(sd + 2) : 0xffff;
 			if (firstVisit && approachId != 0xffff)
@@ -989,15 +821,10 @@ void SiteScreen::enter(uint siteNum, bool resetPartnerMood) {
 			if (!showedApproach)
 				playLondonTravelAnimation(_mystery->_lastSite, siteNum);
 		} else {
-			// `_DoTravel @ 168d:02da` calls `_StartTravelMusic`.
 			_vm->startTravelMusic();
 		}
 	}
 
-	// `_BuildBackground` calls `GetPalette(sitenum + 1)` ??? sitenum is the
-	// global SITES.DBD index (per-mystery `sitepic` field).
-	// Floppy (`_DoSiteLoop_Floppy @ 1652:03f4`): first u16 of site_data is
-	// an offset to a drops sub-struct whose byte 0 is the SITES.DBD picID.
 	uint16 sitepic = 0;
 	if (sd) {
 		if (_vm->isFloppy()) {
@@ -1011,17 +838,11 @@ void SiteScreen::enter(uint siteNum, bool resetPartnerMood) {
 	}
 	_vm->setSitePaletteForSite(sitepic);
 
-	// Override SITEPALS' uniform-yellow 0xF9..0xFE with the marching-ants
-	// glow ramp (shared with the clue puzzle).
 	applyHotspotGlowPalette();
 
 	renderBackground(siteNum);
 
-	// `_DoSiteLoop @ 168d:03f4` plays `_EnterSiteAnim` when
-	// `_LastSite != _SiteNumber`. Guard lives on the engine so PDA/gallery
-	// re-entry doesn't replay arrival.
 	if (playArrival) {
-		// `_EnterSiteAnim` snapshots the screen, so populate the BG first.
 		if (_vm->isFloppy())
 			renderFloppyDrops(siteNum);
 		else
@@ -1038,7 +859,6 @@ void SiteScreen::enter(uint siteNum, bool resetPartnerMood) {
 		renderBackground(siteNum);
 	}
 
-	// Loop 2 from `_DoSiteLoop`: static drops (baked into snapshot).
 	if (_vm->isFloppy())
 		renderFloppyDrops(siteNum);
 	else
@@ -1049,7 +869,6 @@ void SiteScreen::enter(uint siteNum, bool resetPartnerMood) {
 
 	scanColorCycles(siteNum);
 
-	// Loop 1 (animated NPCs) + partner on top of the snapshot.
 	const uint32 now = g_system->getMillis();
 	renderAnimatedDrops(siteNum, now);
 	renderPartner(siteNum, now);
@@ -1110,8 +929,6 @@ bool SiteScreen::checkImpatienceCounter() {
 }
 
 void SiteScreen::notePartnerActivity() {
-	// `_Switch2Patient(WaitHandle)` + `_InitImpatientCounter()` from
-	// the floppy site loop. Triggered on clicks/keys only (not mouse-move).
 	const bool wasImpatient = _partnerWaitMood == kPartnerWaitImpatient;
 	_partnerWaitMood = kPartnerWaitPatient;
 	initImpatienceCounter();
@@ -1135,7 +952,6 @@ void SiteScreen::run() {
 	if (!_mystery || !_mystery->isLoaded())
 		return;
 
-	// Caller seeds `_siteNumber` (via map pick or save restore).
 	uint cur = _mystery->_siteNumber;
 	if (cur >= _mystery->numSites())
 		cur = 0;
@@ -1163,14 +979,6 @@ void SiteScreen::run() {
 				break;
 
 			case Common::EVENT_LBUTTONDOWN: {
-				// `_DoSiteLoop @ 168d:03f4` calls
-				//   _FindButton(&SiteButtons, 2, MouseX, MouseY)
-				// `SiteButtons` @ 29be:0274 ??? two 8-byte rects:
-				//   Button 0: (35,111)-(56,136)  notebook (_NextScreen=4)
-				//   Button 1: (7,177)-(57,200)   map (CD=1, floppy=2)
-				// Partner-head click is port-only: `_KDHelp` shortcut
-				// mirroring `_HandleNoteButton[3]` (0x0403) /
-				// `_HandleGalleryButton[3]` (0x061e). Rect = (5,80,44,110).
 				if (kPdaSiteRect.contains(event.mouse.x, event.mouse.y)) {
 					notePartnerActivity();
 					_vm->setHotspotMouseCursor(false);
@@ -1259,8 +1067,6 @@ void SiteScreen::run() {
 
 			case Common::EVENT_KEYDOWN:
 				notePartnerActivity();
-				// `_DoSiteLoop @ 168d:07e1` routes ESC via `_ESCHit` ???
-				// "Are you sure?" ??? MAP. Port reroutes to ScummVM menu.
 				if (event.kbd.keycode == Common::KEYCODE_ESCAPE) {
 					_vm->setHotspotMouseCursor(false);
 					_vm->openMainMenuDialog();
@@ -1275,15 +1081,11 @@ void SiteScreen::run() {
 			}
 		}
 
-		// Hotspot side effects can invalidate the mystery; exit before
-		// ticking another frame against stale snapshots.
 		if (!_mystery || !_mystery->isLoaded()) {
 			_vm->stopMusic();
 			return;
 		}
 
-		// Per-tick frame pump: `_CheckFrameRate` + `_UpdateAnimations`
-		// at the top of `_DoSiteLoop`'s main loop. 14 cs (~140 ms).
 		const uint32 now = g_system->getMillis();
 		if (_snapshotSite == (int)cur &&
 			now - _lastTickMs >= kFramePeriodMs) {
@@ -1295,7 +1097,6 @@ void SiteScreen::run() {
 			renderAnimatedDrops(cur, now);
 			renderPartner(cur, now);
 			renderHotspots(cur);
-			// `_ColorCycle(start, end)` per tick (`_DoSiteLoop @ 168d:03f4`).
 			applyColorCycles();
 			_lastTickMs = now;
 		}
@@ -1303,14 +1104,12 @@ void SiteScreen::run() {
 		g_system->delayMillis(10);
 	}
 }
-
+// `_EnterSiteAnim @ 1000:9b21`. Two phases (partner-dependent):
+//   Phase 1 ??? skateboard scroll: anim 6 (Jake) / 0xe (Jenny).
+//             Slides from (320-w, 199-h) leftward off-screen.
+//   Phase 2 ??? KD slide-in: anim 7 (Jake) / 0xf (Jenny).
+//             Slides from x=-w at y=0x8b/0x8e until x=0.
 bool SiteScreen::enterSiteAnim() {
-	// `_EnterSiteAnim @ 1000:9b21`. Two phases (partner-dependent):
-	//   Phase 1 ??? skateboard scroll: anim 6 (Jake) / 0xe (Jenny).
-	//             Slides from (320-w, 199-h) leftward off-screen.
-	//   Phase 2 ??? KD slide-in: anim 7 (Jake) / 0xf (Jenny).
-	//             Slides from x=-w at y=0x8b/0x8e until x=0.
-	// Frame rate uses `_MoveSkateBoardPixels` (runtime); we use 4 px/tick.
 	if (!_vm || !_mystery)
 		return false;
 	const uint8 partner = _vm->getPartnerIndex();
@@ -1327,9 +1126,6 @@ bool SiteScreen::enterSiteAnim() {
 	g_system->unlockScreen();
 
 	if (_vm->isLondon()) {
-		// EEM2 `_EnterSiteAnim @ 17ee:27a4`: no skateboard phase.
-		// It plays the partner-specific slide-in animation at anchor
-		// (0, 0x50/0x4e), using `_CheckFrameRate` between cells.
 		const uint animId = (partner == 0) ? 7 : 0xf;
 		const int anchorY = (partner == 0) ? 0x50 : 0x4e;
 		Animation anim;
@@ -1359,10 +1155,8 @@ bool SiteScreen::enterSiteAnim() {
 		return false;
 	}
 
-	// Phase 1 ??? skateboard scroll.
 	Animation skate;
 	if (_vm->getAni().loadAnimation(kSkateAni, skate) && !skate.empty()) {
-		// `iVar4 = 199 - sprite_h`, `uVar5 = kScreenWidth - sprite_w` (frame 0).
 		const int spriteH = skate[0].surface.h;
 		const int spriteW = skate[0].surface.w;
 		int x = (kScreenWidth - spriteW) & ~3;            // 4-px aligned (mode-X)
@@ -1400,11 +1194,6 @@ bool SiteScreen::enterSiteAnim() {
 		}
 	}
 
-	// Phase 2 ??? KD slide-in. `_EnterSiteAnim` per-frame:
-	//   destX = -frame.miscflags    (signed int16; byte 8 = anchor X)
-	//   destY = kKDY - frame.rowoff (signed int16; byte 6 = anchor Y)
-	// 16-bit negation: miscflags=-2 (0xFFFE) ??? destX=+2.
-	// ~80 ms per frame (~12 FPS, one `_CheckFrameRate` tick).
 	Animation kd;
 	if (_vm->getAni().loadAnimation(kKDAni, kd) && !kd.empty()) {
 		for (uint frameIdx = 0;
@@ -1437,10 +1226,6 @@ bool SiteScreen::enterSiteAnim() {
 }
 
 void SiteScreen::renderStaticDrops(uint siteNum) {
-	// Loop 2 from `_DoSiteLoop @ 168d:03f4`:
-	//   count @ siteData[+0x4], entries @ siteData[+0xc + i*6]: {picId, x, y}.
-	//   `_AddDrop @ 172b:1a77` loads PIC picId-1 from PICS.DBD,
-	//   blits with miscflags high-byte as transparency.
 	if (!_mystery)
 		return;
 	const byte *site = _mystery->siteData(siteNum);
@@ -1474,11 +1259,6 @@ void SiteScreen::renderStaticDrops(uint siteNum) {
 }
 
 void SiteScreen::renderFloppyDrops(uint siteNum) {
-	// Floppy drops: drops sub-struct pointed to by `*site_data` u16 offset.
-	// `_DoSiteLoop_Floppy @ 1652:0418` ??? `FUN_16e2_18eb @ 16e2:18eb`:
-	//   entry stride 5: u16 picID @ +0, u16 X @ +2, u8 Y @ +4.
-	//   picID-1 indexes PICS.DBX table @ 2608:4537.
-	// drops_struct[0] = BG picID (separate), drops_struct[1] = drop count.
 	if (!_mystery)
 		return;
 	const byte *site = _mystery->siteData(siteNum);
@@ -1501,7 +1281,6 @@ void SiteScreen::renderFloppyDrops(uint siteNum) {
 		const int16  y     = (int16)e[4];
 		if (picID == 0)
 			continue;
-		// `getPicture(num)` does `loadEntry(num - 1)` (resource.h:100).
 		Picture pic;
 		if (!_vm->getPics().getPicture((uint)picID, pic))
 			continue;
@@ -1511,20 +1290,10 @@ void SiteScreen::renderFloppyDrops(uint siteNum) {
 }
 
 void SiteScreen::renderAnimatedDrops(uint siteNum, uint32 tickMs) {
-	// Loop 1 from `_DoSiteLoop @ 168d:03f4`:
-	//   count @ siteData[+0xa], entries @ siteData[+0x48 + i*6]: {animId, x, y}.
-	//   animId == -1 ??? `_ColorCycle(x, y)` (palette rotation, separate path).
-	//   else ??? `_GetAnimation` + `_NewAnimation` + `_UpdateAnimations @ 172b:09c1`
-	//          walks a sequence script (0x80=loop, 0x81=jump).
 	if (!_mystery)
 		return;
 
 	if (_vm && _vm->isFloppy()) {
-		// Floppy site anims live in ANI.BIN (per-case block from
-		// `_GetSiteAnimData_Floppy`). Per-site:
-		//   u8 cycleCount, cycleCount ?? {u8 start, u8 end},
-		//   u8 animCount, animCount ?? {u8 animId, u16 x, u8 y}.
-		// `_DoSiteLoop_Floppy` caps at 4 slots.
 		const byte *siteAnim = _mystery->floppySiteAnimData(siteNum);
 		if (!siteAnim)
 			return;
@@ -1588,8 +1357,6 @@ void SiteScreen::renderAnimatedDrops(uint siteNum, uint32 tickMs) {
 }
 
 void SiteScreen::scanColorCycles(uint siteNum) {
-	// `_DoSiteLoop @ 168d:03f4`: Loop 1 entries with animId==-1 are
-	// ColorCycle palette ranges (start @ +2, end @ +4). Max 5 slots.
 	_colorCycles.clear();
 	if (!_mystery)
 		return;
@@ -1628,8 +1395,6 @@ void SiteScreen::scanColorCycles(uint siteNum) {
 }
 
 void SiteScreen::applyColorCycles() {
-	// `_ColorCycle @ 172b:2015` per range. Always rotate 0xF9..0xFE
-	// (hotspot marching ants ??? `_ColorCycle(0xf9, 0xfe)` in `_DoSiteLoop`).
 	for (uint i = 0; i < _colorCycles.size(); i++) {
 		cyclePaletteRange(_colorCycles[i].start, _colorCycles[i].end);
 	}
@@ -1655,11 +1420,6 @@ void SiteScreen::restoreBgSnapshot() {
 }
 
 void SiteScreen::syncCompositedScreen() {
-	// OpenGL screenshots read the current backbuffer. After a buffer swap,
-	// that backbuffer can contain an older site frame unless the engine keeps
-	// presenting the full composited screen, even while no animation tick
-	// fired. Copying the current game surface through copyRectToScreen keeps
-	// screenshots in sync without backend-specific changes.
 	Graphics::ManagedSurface snapshot(kScreenWidth, kScreenHeight,
 		Graphics::PixelFormat::createFormatCLUT8());
 
@@ -1674,14 +1434,6 @@ void SiteScreen::syncCompositedScreen() {
 }
 
 void SiteScreen::renderPartner(uint siteNum, uint32 tickMs) {
-	// `_DoSiteLoop @ 168d:03f4` reads `siteData[+8]` as the speaker
-	// table index, then for each (speaker x partner) loads:
-	//   anim = WaitAnims[speakerIdx].anim[partner]
-	//   x    = WaitAnims[speakerIdx].x[partner]
-	//   y    = WaitAnims[speakerIdx].y[partner]
-	// from `_WaitAnims @ 29be:021c` (see `kWaitAnims` at file scope for
-	// 12-byte / 6-u16 entry layout). Rendering caps at speaker < 7
-	// (entries past 6 are `_SiteButtons` rect data in the binary).
 	const byte *site = _mystery->siteData(siteNum);
 	if (!site)
 		return;
@@ -1690,10 +1442,6 @@ void SiteScreen::renderPartner(uint siteNum, uint32 tickMs) {
 	int    x;
 	int    y;
 	if (_vm->isFloppy()) {
-		// `_DoSiteLoop_Floppy @ 1652:042b`: site_data+8 is a u16 OFFSET
-		// to `_SpeakerInfo_Floppy`; the first 10 bytes are the idle pair:
-		//   +0..1 Jake anim, +2..3 Jake X, +4 Jake Y,
-		//   +5..6 Jenny anim, +7..8 Jenny X, +9 Jenny Y.
 		const uint16 spkOff = READ_LE_UINT16(site + 8);
 		const byte *spk = _mystery->blobAt(spkOff);
 		if (!spk)
@@ -1725,9 +1473,6 @@ void SiteScreen::renderPartner(uint siteNum, uint32 tickMs) {
 	if (!_vm->getAni().loadAnimation(animId, anim) || anim.empty())
 		return;
 
-	// Relative phase anchor (not raw tickMs) so wait anim resumes
-	// from script[0] after each kdAnim one-shot ??? matches
-	// `_PlayAnimation @ 172b:1f5d` reset to frame 0xffff.
 	const uint32 elapsed = (tickMs >= _waitPhaseAnchor)
 							? (tickMs - _waitPhaseAnchor)
 							: tickMs;
@@ -1760,8 +1505,6 @@ bool SiteScreen::renderFloppyHotspotPartnerPose(uint siteNum) {
 		return false;
 
 	const uint16 spkOff = READ_LE_UINT16(site + 8);
-	// `_OnSiteHotspotClicked_Floppy @ 1652:017a`: after restoring the site
-	// it loads the active pair from `_SpeakerInfo_Floppy + 0x28/0x2d`.
 	const uint poseOff = (_vm->getPartnerIndex() == kPartnerJake)
 		? 0x28 : 0x2d;
 	if ((uint32)spkOff + poseOff + 5 > _mystery->dataSize())
@@ -1790,12 +1533,6 @@ bool SiteScreen::renderFloppyHotspotPartnerPose(uint siteNum) {
 }
 
 void SiteScreen::renderBackground(uint siteNum) {
-	// `_BuildBackground(sitepic, 0x42, 0x14)` from `_DoSiteLoop @
-	// 168d:03f4` / `_DisplayCorrect`:
-	//   1. `_GetFromDB(_PicIndex, 0x3d)` ??? DBI entry 0x3d (0-based).
-	//   2. SITES.DBD entry `sitepic` (0-based, SiteData[+0..+1]).
-	//   3. `_Rect_Move(..., 0x42, 0x14, 48000, ...)` composes at (66, 20).
-	//   4. `_GetPalette(sitepic + 1)`.
 	Picture frame;
 	if (_vm->getPics().loadEntry(0x3d, frame)) {
 		g_system->copyRectToScreen(frame.surface.getPixels(),
@@ -1803,11 +1540,6 @@ void SiteScreen::renderBackground(uint siteNum) {
 								   0, 0, frame.surface.w, frame.surface.h);
 	}
 
-	// `_BuildBackground @ 172b:13e2` ??? `_GetFromDB(_siteFile,
-	// &_SiteDBIndex, sitenum)` uses SiteData[+0] as a 0-based SITES.DBD
-	// index (stride 10, asm `IMUL BX,BX,0xa` @ 172b:14c8; no -1 adjust).
-	// Floppy: site_data[0] is u16 offset to drops sub-struct; drops[0]
-	// byte is the SITES.DBD index (`FUN_16e2_12fd @ 16e2:12fd`).
 	const byte *site = _mystery->siteData(siteNum);
 	uint16 sitepic = 0;
 	if (site) {
@@ -1855,7 +1587,6 @@ byte currentWhitePaletteIndex(byte fallback) {
 }
 
 void SiteScreen::renderHotspots(uint siteNum) {
-	// `_DrawSearchButtons`. Port adds optional "hide hint" setting.
 	if (ConfMan.getBool("hide_highlight_boxes"))
 		return;
 
@@ -1868,21 +1599,6 @@ void SiteScreen::renderHotspots(uint siteNum) {
 	if (!screen)
 		return;
 
-	// `_DrawSearchButtons @ 2404:0a8f`:
-	//   for each hotspot:
-	//     if _Sawit(theSite, loc) == 0 (NOT seen yet):
-	//       _DrawRect(rect)       ??? outline in cycling colors
-	//                                0xF9..0xFE; `_ColorCycle(0xF9, 0xFE)`
-	//                                rotates them every tick ??? "marching
-	//                                ants" glow on unsearched spots.
-	//     else (seen):
-	//       _DrawSolidRect(rect)  ??? outline in solid colour 0xFF.
-	// (Branch order per asm `2404:0af6 OR AX,AX; 2404:0af8 JZ` ??? the
-	// C-level decompile mis-reordered the if/else.)
-	// Palette 0xF9..0xFE is already rotated by `applyColorCycles` each
-	// tick, so drawing a single colour from that range pulses on its
-	// own. Phase the start colour by hotspot index so adjacent spots
-	// don't glow in lock-step.
 	const uint32 tickMs = g_system->getMillis();
 
 	// CD hotspot row = 14 bytes:
@@ -1918,12 +1634,8 @@ void SiteScreen::renderHotspots(uint siteNum) {
 				   _mystery->_hotSpotsSeen[seenKey];
 		}
 		if (seen) {
-			// `_DrawSolidRect @ 172b:0506` ??? solid, non-cycling outline.
 			screen->frameRect(rect, searchedColor);
 		} else {
-			// `_DrawRect @ 172b:03e2` ??? walk all four edges incrementing
-			// the colour per pixel through palette indices 0xF9..0xFE,
-			// which `_ColorCycle(0xF9, 0xFE)` rotates every tick.
 			byte color = (byte)(0xF9 + ((i + (tickMs / 80)) & 0x07) % 6);
 			// Top edge
 			for (int x = rect.left; x < rect.right; x++) {
@@ -2069,12 +1781,10 @@ void SiteScreen::onHotspotClicked(uint siteNum, uint hotIdx) {
 			// Snapshot `_cluesFound` ??? detect new-clue 0???1 ??? autosave.
 			byte before[Mystery::kCluesFoundCap];
 			memcpy(before, _mystery->_cluesFound, sizeof(before));
-			// Partner-less BG for `_DoKDAnim` / `playKdAnim` to erase
-			// the resting idle (fires when ClueEntry +0x3a != -1).
 			_vm->setPartnerEraseBg(&_bgSnapshot);
 			_vm->displayClue(clueBlock);
 			_vm->setPartnerEraseBg(nullptr);
-			// Port-only autosave (original = manual `_SaveGame @ 2404:0c87`).
+			// New feature: autosave on new clue.
 			bool foundNewClue = false;
 			for (uint i = 0; i < Mystery::kCluesFoundCap; i++) {
 				if (!before[i] && _mystery->_cluesFound[i]) {
@@ -2092,24 +1802,16 @@ void SiteScreen::onHotspotClicked(uint siteNum, uint hotIdx) {
 		}
 	}
 }
-
+// `_DoKDAnim(num) @ 168d:028a` + `_PlayAnimation @ 172b:1f46`:
+//   _SuspendAnimation(WaitHandle);
+//   anim = kKdAnimTable[num].anim[partner]   (@ 29be:0228)
+//   x    = kKdAnimTable[num].x[partner]
+//   y    = kKdAnimTable[num].y[partner]
+//   _PlayAnimation(anim, x, y, WaitHandle)
+//     -> registers a state-4 (one-shot) animation slot and lets
+//        `_UpdateAnimations` walk the script until 0x80, then
+//        frees the slot and re-activates `WaitHandle`.
 void EEMEngine::playKdAnim(uint16 num) {
-	// Mirrors `_DoKDAnim(num) @ 168d:028a` + `_PlayAnimation @ 172b:1f46`:
-	//   _SuspendAnimation(WaitHandle);
-	//   anim = kKdAnimTable[num].anim[partner]   (@ 29be:0228)
-	//   x    = kKdAnimTable[num].x[partner]
-	//   y    = kKdAnimTable[num].y[partner]
-	//   _PlayAnimation(anim, x, y, WaitHandle)
-	//     -> registers a state-4 (one-shot) animation slot and lets
-	//        `_UpdateAnimations` walk the script until 0x80, then
-	//        frees the slot and re-activates `WaitHandle`.
-	// Port renders the partner's idle inline in each redraw rather
-	// than via a slot system, so we play the one-shot synchronously
-	// (blocking) and resume normal idle rendering when the caller
-	// returns ??? matches the visible effect (partner gesture finishes
-	// before the speaker portrait + speech balloon appear).
-	// `kKdAnimTable` and `kAnimScripts` live at file scope above. EEM2 ships a
-	// different table (positions + Jenny reactions) ??? use it for London.
 	if (num >= ARRAYSIZE(kKdAnimTable))
 		return;
 
@@ -2158,19 +1860,12 @@ void EEMEngine::playKdAnim(uint16 num) {
 		Graphics::ManagedSurface scratch(kScreenWidth, kScreenHeight,
 			Graphics::PixelFormat::createFormatCLUT8());
 		scratch.simpleBlitFrom(bg);
-		// Anchor-aware: kdAnim cells (0x03/0x04/0x0c/0x0d ...) have
-		// non-zero per-frame `miscflags`/`rowoff` (anim 0x03 has rowoff
-		// up to 9, anim 0x04 has miscflags = -2). Routes through
-		// `blitAnimFrameAnchored` so the gesture translates across
-		// cells instead of pinning to a fixed pixel.
 		(void)transp;  // anchored blitter recomputes from p.flags
 		blitAnimFrameAnchored(scratch.surfacePtr(), fr, px, py);
 		g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
 								   0, 0, kScreenWidth, kScreenHeight);
 		g_system->updateScreen();
 
-		// 100 ms per frame (~10 fps). Pump updateScreen inside the wait
-		// so cursor overlay refreshes at 100 Hz.
 		const uint32 wakeup = g_system->getMillis() + 100;
 		while (g_system->getMillis() < wakeup && !shouldQuit()) {
 			Common::Event ev;
diff --git a/engines/eem/site.h b/engines/eem/site.h
index 9bdbc88aa4a..30567b2339d 100644
--- a/engines/eem/site.h
+++ b/engines/eem/site.h
@@ -36,11 +36,10 @@ class EEMEngine;
 class Mystery;
 
 /// partnerFrameAtTick: frame index for `seqnum` at `tickMs`. Walks `kAnimScripts`
-/// at `kFramePeriodMs` (~140 ms = `_CheckFrameRate` cadence) per entry; wraps
+/// at `kFramePeriodMs` per entry; wraps
 /// on the script's 0x80 terminator. Falls back to flipbook (`tick % numFrames`)
 /// when no script is registered. `numFrames` is the ANI.DBD entry's cell count,
 /// used both for the fallback and to clamp script values past the asset.
-/// Mirrors the looping path of `_UpdateAnimations @ 172b:09c1`.
 uint partnerFrameAtTick(uint16 seqnum, uint numFrames, uint32 tickMs);
 
 /// Select the EEM2 ("London") animation-script table inside `findAnimScript`.
@@ -49,43 +48,25 @@ uint partnerFrameAtTick(uint16 seqnum, uint numFrames, uint32 tickMs);
 void setLondonAnimScripts(bool enabled);
 
 /// bigMapPartnerFrameAtTick: count-up 0..8 once, then loop `_BigMapWaitSeq`
-/// (9,9,9,9,10,9,9,9,9). Mirrors `_DoBigMap @ 20fe:09e7` two-phase swap from
-/// script 0x14 (count-up @ 29be:196a) to `_BigMapWaitSeq @ 29be:1574`.
 uint bigMapPartnerFrameAtTick(uint numFrames, uint32 elapsedMs);
 
 /// bigMapDetailPartnerFrameAtTick: zoomed-view partner frame. Same two-phase shape
-/// as `bigMapPartnerFrameAtTick`. `_DoMapScreen @ 20fe:120b` runs script 0x13
-/// (count-up 0..7 @ 29be:1992) then swaps to `_SmallMapWaitSeq @ 29be:1548`
-/// (`MOV [BX+0x789f],0x1548` at 20fe:1390).
+/// as `bigMapPartnerFrameAtTick`.
 uint bigMapDetailPartnerFrameAtTick(uint numFrames, uint32 elapsedMs);
 
 /// blitAnimFrameAnchored: mask-blit at (anchorX - frame.miscflags,
-/// anchorY - frame.rowoff). Mirrors per-frame anchor math in
-/// `_UpdateAnimations @ 172b:09c1`. Both anchors are SIGNED int16
-/// (e.g. anim 0x14 BigMap walk-cycle has miscflags = -2 per cell, so
-/// the sprite translates across the screen as it cycles). Use for any
-/// animation rendered through `_NewAnimation` in the original ??? partner
-/// sprites, animated drops, briefing animations. Transparency comes
-/// from `flags >> 8` (NOT miscflags).
+/// anchorY - frame.rowoff).
 void blitAnimFrameAnchored(Graphics::Surface *screen, const Picture &p,
 						   int anchorX, int anchorY);
 
 /// Rotate one VGA palette range by one slot (START???END direction).
-/// Mirrors `_ColorCycle`. Used by per-site Loop-1 ColorCycle entries,
-/// hotspot marching ants (0xF9..0xFE), and the BigMap marker shine.
 void cyclePaletteRange(uint8 start, uint8 end);
 
 /// Rotate one VGA palette range by one slot in the OPPOSITE direction
-/// (END???START): save END, shift every entry up by one (END-1 ??? END, ...),
-/// wrap saved END to START. Mirrors `_OpenColorCycle @ 2520:04f7` (CD) /
-/// `_ReverseColorCycle_Floppy`. Used by opening-anim logos (EA Kids, etc.)
-/// where the cycle shifts END???START rather than START???END.
+/// (END???START).
 void cyclePaletteRangeReverse(uint8 start, uint8 end);
 
 /// Load the 6-step yellow marching-ants ramp into palette 0xF9..0xFE
-/// (SITEPALS ships these as uniform yellow). Shared by site hotspots and the
-/// clue puzzle so both outline clickable areas in the same original colours;
-/// `cyclePaletteRange(0xF9, 0xFE)` then pulses them.
 void applyHotspotGlowPalette();
 
 /// One hotspot (search rectangle) within a site, 14 bytes on disk.
@@ -98,8 +79,7 @@ struct Hotspot {
 	Common::Rect rect() const { return Common::Rect(x1, y1, x2, y2); }
 };
 
-/// Site / scene controller. Mirrors `_DrawSearchButtons @ 2404:0a8f` /
-/// `_SearchButtons @ 2404:0bfb` site loop.
+/// Site / scene controller. 
 class SiteScreen : public Common::EventObserver {
 public:
 	SiteScreen(EEMEngine *vm, Mystery *mystery)
@@ -126,32 +106,17 @@ private:
 	void notePartnerActivity();
 	bool playLondonTravelAnimation(uint fromSite, uint toSite);
 
-	/// Partner site-arrival sequence (when `_LastSite != _SiteNumber`).
-	/// Mirrors `_EnterSiteAnim @ 1000:9b21`: anim 6/14 (Jake/Jenny) skateboards
-	/// in from right, then anim 7/15 slides KD in from left. Returns true if skipped.
+	/// Partner site-arrival sequence
 	bool enterSiteAnim();
 
-	/// renderPartner: persistent in-site partner sprite at `_WaitAnims @ 29be:021c`.
-	/// Mirrors `_GetAnimation` + `_NewAnimation` tail of `_DoSiteLoop @ 168d:03f4`.
+	/// renderPartner: persistent in-site partner sprite 
 	void renderPartner(uint siteNum, uint32 tickMs);
 
-	/// Floppy active speaker pose shown before `_HandleHotspotClick_Floppy`.
-	/// Uses `_SpeakerInfo_Floppy + 0x28/0x2d`, replacing the idle partner.
+	/// Floppy active speaker pose 
 	bool renderFloppyHotspotPartnerPose(uint siteNum);
 
-	/// renderStaticDrops: `_AddDrop` static decorations (Loop 2).
-	/// siteData[+0x4] count, siteData[+0xc] entries (6 bytes: {picId, x, y}).
 	void renderStaticDrops(uint siteNum);
-
-	/// renderFloppyDrops: floppy variant. `*site_data`???drops; drops[1]=count;
-	/// entries at drops+2 are 5 bytes ({u16 X, u16 Y, byte picID}); PIC loaded as picID-1.
-	/// Per `_DoSiteLoop_Floppy @ 1652:0418` and `FUN_16e2_18eb`.
 	void renderFloppyDrops(uint siteNum);
-
-	/// renderAnimatedDrops: per-site animated NPCs (Loop 1) at current tick.
-	/// `_DoSiteLoop` registers each via `_NewAnimation` (siteData[+0xa] count,
-	/// siteData[+0x48] entries: {animId (-1=ColorCycle), x, y}); frames advance via
-	/// `_UpdateAnimations @ 172b:09c1`.
 	void renderAnimatedDrops(uint siteNum, uint32 tickMs);
 
 	/// Snapshot the post-BG, post-static-drops screen so the per-tick
@@ -168,11 +133,10 @@ private:
 	void syncCompositedScreen();
 
 	/// scanColorCycles: scan Loop 1 for ColorCycle entries (animId == -1),
-	/// cache (start, end) palette ranges. Mirrors `_DoSiteLoop @ 168d:03f4` init scan.
+	/// cache (start, end) palette ranges.
 	void scanColorCycles(uint siteNum);
 
-	/// applyColorCycles: rotate cached ColorCycle ranges + 0xf9..0xfe (hotspot ants)
-	/// one step. Mirrors per-tick `_ColorCycle(start, end)` calls in `_DoSiteLoop`.
+	/// applyColorCycles: rotate cached ColorCycle ranges
 	void applyColorCycles();
 
 	EEMEngine *_vm;
@@ -188,10 +152,7 @@ private:
 	uint32 _impatientDeadlineMs = 0; ///< Test-shortened impatience deadline.
 	PartnerWaitMood _partnerWaitMood = kPartnerWaitDefault;
 
-	/// Wall-clock anchor for partner wait anim phase: rendered as
-	/// `partnerFrameAtTick(animId, ..., now - _waitPhaseAnchor)`.
-	/// Bumped on site entry (`_NewAnimation` writes 0xffff at `_DoSiteLoop @ 168d:0436`)
-	/// and on return from a one-shot kdAnim (`_PlayAnimation @ 172b:1f5d` state=4).
+	/// Wall-clock anchor for partner wait anim phase
 	uint32 _waitPhaseAnchor = 0;
 
 	/// Per-site cached ColorCycle ranges (up to 5, matching original 5-slot anim table).
diff --git a/engines/eem/ui.cpp b/engines/eem/ui.cpp
index 6acea3e5d58..dc7c47c74a4 100644
--- a/engines/eem/ui.cpp
+++ b/engines/eem/ui.cpp
@@ -40,8 +40,6 @@
 
 namespace EEM {
 
-// Gallery slot positions @ 29be:0x116. Used by `_DrawGallery @ 158f:0046`
-// and the accuse portrait grid.
 struct GallerySlot { int x; int y; };
 const GallerySlot kGallerySlots[5] = {
 	{  83,  14 }, // 0
@@ -149,9 +147,7 @@ bool loadLondonApproachData(uint16 approachId, LondonApproachData &out) {
 	out.pages.clear();
 	uint32 pos = 16;
 	// `_DoApproach @ 1717:009b` reads the pages with `_fgets` into 255-byte
-	// slots, so each page is one NEWLINE-terminated record ??? NOT NUL-separated.
-	// (A*.BIN carries no NULs; splitting on '\0' lumps every page into page 0,
-	// which is why the Next/Prev arrows had nothing to page through.)
+	// slots, so each page is one NEWLINE-terminated record, NOT NUL-separated.
 	for (uint16 i = 0; i < pageCount && pos < size; i++) {
 		const uint32 start = pos;
 		while (pos < size && data[pos] != '\n')
@@ -188,16 +184,13 @@ bool decodeLondonApproachFirstFrame(uint16 videoId,
 	return true;
 }
 
-// Setup-screen highlight rects (referenced by both `doSetup` and the
-// helper `EEMEngine::setupDrawScreen`). `_SetupHighlights @ 29be:1320`.
+// Setup-screen highlight rects
 constexpr Common::Rect kSetupKid1Rect    (Common::Point( 99,  44),  49,  8);
 constexpr Common::Rect kSetupKid2Rect    (Common::Point( 99,  54),  49,  8);
 constexpr Common::Rect kSetupSoundOnRect (Common::Point(106,  86),  19,  8);
 constexpr Common::Rect kSetupSoundOffRect(Common::Point(106,  96),  19,  8);
 
-// EEM2/London setup highlights ??? `_SetupSettings @ 2046:0008` (_SwapColors,
-// key 0xFE). On/off label pairs for the 4 toggles (rects @ 2bca:13ec; drawn
-// in the DOS order ON-then-OFF). Partner reuses Jake/Jenny.
+// EEM2/London setup highlights
 constexpr Common::Rect kLonSetJake    (Common::Point( 99,  44), 49, 8); // 0x13ec
 constexpr Common::Rect kLonSetJenny   (Common::Point( 99,  54), 49, 8); // 0x13f4
 constexpr Common::Rect kLonSetVoiceOn (Common::Point(106,  68), 20, 8); // 0x13fc
@@ -207,9 +200,6 @@ constexpr Common::Rect kLonSetMusicOff(Common::Point(128,  85), 18, 7); // 0x142
 constexpr Common::Rect kLonSetHiOn    (Common::Point(106, 110), 29, 8); // 0x1414
 constexpr Common::Rect kLonSetHiOff   (Common::Point(106, 100), 29, 8); // 0x140c
 
-// `_SwapColors @ 172b:1d2a` ??? replace pixels in r where value==from
-// with to. 0xFE = BG text-key; 0x15 = active palette index, 0x00 =
-// inactive (set by `_SetupSettings @ 1f78:000d`).
 void swapColors(Graphics::ManagedSurface &dst,
 					   const Common::Rect &r, byte from, byte to) {
 	const int x1 = MAX<int>(0, r.left);
@@ -225,9 +215,7 @@ void swapColors(Graphics::ManagedSurface &dst,
 	}
 }
 
-// PDA-frame partner sprite specs. `(5, 0x50)` is the partner-at-desk anchor
-// shared by _DoNotebook (script 0x01, anim 1/0xb) and _DoGallery / _DoAccuse
-// / _MoreInfo (script 0x02, anim 2/0x10).
+// TODO: change Pda naming to TRAVIS
 struct PdaPartnerSpec {
 	uint16 scriptId;
 	uint16 animJake;
@@ -277,9 +265,6 @@ constexpr uint16 kFloppyEndingBackgroundPic = 0x8b;
 constexpr uint16 kFirstTryBadgePic = 0x205;
 constexpr Common::Point kFirstTryBadgePos(0x1e, 9);
 
-// kPdaSiteRect / kPdaPartnerFootMapRect / kPdaPartnerHeadHintRect live in
-// eem.h (shared with site.cpp). The button-row rects below are PDA-screen-
-// only (`_NoteButtons @ 29be:0147`).
 constexpr Common::Rect kPdaHelpRect(Common::Point(93, 174), 22, 16);
 constexpr Common::Rect kPdaNotebookRect(Common::Point(134, 174), 21, 16);
 constexpr Common::Rect kPdaGalleryRect(Common::Point(157, 174), 21, 16);
@@ -287,9 +272,6 @@ constexpr Common::Rect kPdaAccuseRect(Common::Point(180, 174), 21, 16);
 constexpr Common::Rect kPdaPageNextRect(Common::Point(204, 174), 20, 16);
 constexpr Common::Rect kPdaPagePrevRect(Common::Point(226, 174), 21, 16);
 constexpr Common::Rect kPdaHelp2Rect(Common::Point(267, 174), 21, 16);
-// London-only: `_NoteButtons[9]` = (0,0,66,79) ??? site (EEM1's slot 9 is a
-// dead 0-rect). A secondary "close the PDA" hotspot over the device's
-// top-left corner. EEM2 `_HandleNoteButton` slot 9 ??? screen 3.
 constexpr Common::Rect kPdaLondonCloseRect(Common::Point(0, 0), 66, 79);
 
 constexpr uint16 kProfilePickerRevealPic = 0x105;
@@ -404,8 +386,7 @@ const byte *advanceFloppyDialogRecords(const byte *rec, uint count,
 	return rec;
 }
 
-// Floppy gallery slot positions @ 2608:0x16c. Read by `_DrawGallery_Floppy
-// @ 154e:0045`'s [BX + 0x16c] (x) and [BX + 0x16e] (y) loads.
+// Floppy gallery slot positions @ 2608:0x16c.
 const GallerySlot kFloppyGallerySlots[5] = {
 	{ 0x53, 0x0e }, // 0
 	{ 0x9b, 0x0e }, // 1
@@ -414,9 +395,7 @@ const GallerySlot kFloppyGallerySlots[5] = {
 	{ 0xbf, 0x5a }  // 4
 };
 
-// `_GetKDTextBalloon @ 1df2:0105` digit-balloon table @ 29be:1064:
-//   '0'???0x15  '1'???0x16  '2'???0x17  '3'???0x18  '4'???0x19
-//   '5'???0x1a  '6'???0x20  '7'???0x21  '8'???0x22  '9'???0x1e
+// `_GetKDTextBalloon @ 1df2:0105` digit-balloon table @ 29be:1064
 const uint16 kDigitBalloons[10] = {
 	0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x20, 0x21, 0x22, 0x1e
 };
@@ -445,7 +424,6 @@ void copyToScreen(Graphics::ManagedSurface &scratch) {
 }
 
 void cycleChooserPalette() {
-	// `_DoChoose` / `_DoListPicker_Floppy` rotate 0x6f..0x73 per tick.
 	cyclePaletteRange(kChooserCycleStart, kChooserCycleEnd);
 }
 
@@ -632,12 +610,10 @@ struct ActionMenuView {
 	const char *const *pickLabel;
 	const bool *pickEnabled;
 	uint pick;
-	uint numPicks;  ///< Visible entries (London drops ScrapBook 3 ??? 4).
+	uint numPicks;
 };
 
-// `_DoChooseMystery @ 1a35:02b7` ??? opens BOOK%u.NME (CRLF strings, up
-// to 25 ?? 40 bytes, whitespace-line sentinel). `_DoChoose @ 1c33:0514`
-// walks until {0, 0}.
+// `_DoChooseMystery @ 1a35:02b7`
 Common::StringArray loadBookNames(uint book) {
 	Common::StringArray names;
 	const Common::String fname = Common::String::format("BOOK%u.NME", book);
@@ -672,8 +648,6 @@ void clampCaseTopRow(uint &topRow, uint listLen, uint visibleRows) {
 		topRow = maxTop;
 }
 
-// "Choose A Mystery" sub-chooser view. names = BOOK%d.NME entries
-// (mystery number = tierLo + index). solvedFlags marks greyed entries.
 struct CaseSubmenuView {
 	EEMEngine *vm;
 	const Picture *caseBg;
@@ -688,7 +662,7 @@ struct CaseSubmenuView {
 	const Common::Array<bool> *solvedFlags;
 	uint topRow;
 	uint selRow;
-	uint book;            ///< 1..3 ??? for the "Book N" / "Challenge Book" title
+	uint book;
 };
 
 void drawCaseGreeter(Graphics::ManagedSurface &scratch,
@@ -697,8 +671,6 @@ void drawCaseGreeter(Graphics::ManagedSurface &scratch,
 	if (!haveKdAnim || !kdAnim || kdAnim->empty())
 		return;
 
-	// `_CaseSelection` drives the partner ANI with script 0x15 regardless
-	// of partner.
 	const uint32 now = g_system->getMillis();
 	const uint frameIdx = partnerFrameAtTick(0x15, (uint)kdAnim->size(), now);
 	blitAnimFrameAnchored(scratch.surfacePtr(), (*kdAnim)[frameIdx],
@@ -755,9 +727,7 @@ bool animateCaseSelectionReveal(EEMEngine *vm, const Picture *caseBg,
 	return false;
 }
 
-// `_DoChoose`'s `DrawList @ 1c33:040d`. 12 rows ?? 10 px at (61, 35).
-// Original colours 0x13 sel / 0x1B greyed / 0x5C default approximated
-// here as 0xF / 0x8 / 0x7 from site palette 0.
+// `DrawList @ 1c33:040d`
 void drawCaseSubmenu(const CaseSubmenuView &v) {
 	Graphics::ManagedSurface scratch(kScreenWidth, kScreenHeight,
 		Graphics::PixelFormat::createFormatCLUT8());
@@ -792,7 +762,7 @@ void drawCaseSubmenu(const CaseSubmenuView &v) {
 			kListX, kListY0 + r * kLineH, kListW, color);
 	}
 
-	// Selection arrow (ScummVM-only ??? original uses colour change).
+	// Selection arrow.
 	if (v.selRow >= v.topRow && v.selRow < v.topRow + (uint)kVisible) {
 		const int r = (int)(v.selRow - v.topRow);
 		v.vm->getFont().drawString(&scratch, ">",
@@ -855,12 +825,8 @@ void drawActionMenuFrame(const ActionMenuView &v) {
 }
 
 void EEMEngine::doProfilePicker() {
-	// `screen8_handler @ 1c33:1012` ??? walks *.PLR (max 25), reads 12-byte
-	// player-name field, hands list to `_DoChoose`. No profiles or
-	// 0xfffe/0xffff sentinel enters `_NewPlayer`.
 	_profileCreatedThisSession = false;
 
-	// `screen8_handler` does `_FadeOut(); _GetPalette(0); _GetBackground(0x104)`.
 	setSitePalette(0);
 
 	const SaveStateList saves = listProfiles();
@@ -880,11 +846,6 @@ void EEMEngine::doProfilePicker() {
 		return;
 	}
 
-	// Existing profiles only ??? the original `screen8_handler @ 1c33:1012`
-	// passes the bare *.PLR list to `_DoChoose` with NO synthesized "new
-	// player" entry. _DoChoose returns 0xfffe / 0xffff for the bottom click
-	// rect @ 29be:0d08 / ESC, which routes to `_NewPlayer` (handled below
-	// via kChooserNewPlayerRect / KEYCODE_ESCAPE).
 	Common::Array<ProfilePickerEntry> entries;
 	for (const SaveStateDescriptor &s : saves) {
 		ProfilePickerEntry e;
@@ -903,8 +864,6 @@ void EEMEngine::doProfilePicker() {
 	const bool haveReveal =
 		_picsArchive.getPicture(kProfilePickerRevealPic, reveal);
 
-	// Geometry: list origin (61, 35), 10 px/row, 12 visible. PIC 0x105
-	// slides into lower strip via `screen8_handler` before `_DoChoose`.
 	ProfilePickerView view;
 	view.vm = this;
 	view.bg = &bg;
@@ -1000,7 +959,6 @@ void EEMEngine::doProfilePicker() {
 					break;
 				}
 				if (kChooserHelpRect.contains(ev.mouse.x, ev.mouse.y)) {
-					// `screen8_handler` sets Chelp=0 ??? button ignored.
 					break;
 				}
 				if (kChooserListRect.contains(ev.mouse.x, ev.mouse.y)) {
@@ -1047,7 +1005,6 @@ void EEMEngine::doProfilePicker() {
 		return;
 	}
 
-	// `_LoadPlayerRecord @ 1c33:1281`.
 	const ProfilePickerEntry &e = entries[sel];
 	if (loadProfile(e.label)) {
 		_profileCreatedThisSession = false;
@@ -1060,10 +1017,8 @@ void EEMEngine::doProfilePicker() {
 			doNewPlayer();
 	}
 }
-
+// `_NewPlayer @ 1c33:0dda`
 void EEMEngine::doNewPlayer() {
-	// `_NewPlayer @ 1c33:0dda` ??? BG 0x104 + peek pic 0x107, prompt for
-	// up to 12 chars.
 	_profileCreatedThisSession = false;
 	if (!_font.isLoaded()) {
 		_playerName = "Detective";
@@ -1106,8 +1061,6 @@ void EEMEngine::doNewPlayer() {
 			if (k == Common::KEYCODE_RETURN) {
 				if (name.empty())
 					name = "Detective";
-				// `_NewPlayer @ 1c33:0fa0+`: `_LoadPlayerRecord` ??? if
-				// missing, zero state and `_SavePlayerRecord`.
 				if (loadProfile(name)) {
 					_profileCreatedThisSession = false;
 				} else {
@@ -1115,7 +1068,6 @@ void EEMEngine::doNewPlayer() {
 					memset(_mysteriesSolved, 0, sizeof(_mysteriesSolved));
 					_mystery.clear();
 					_partner = kPartnerJake;
-					// `_NewPlayer @ 1c33:0fa3`: DAT_2d5d_3f99 = 1 (Junior).
 					_chainStage = 1;
 					saveProfile(name);
 					_profileCreatedThisSession = true;
@@ -1151,26 +1103,8 @@ void EEMEngine::doNewPlayer() {
 
 	g_system->setFeatureState(OSystem::kFeatureVirtualKeyboard, false);
 }
-
+// `_DisplayEnding @ 1df2:0548` + `_DisplayEndingPage @ 1df2:044c`
 int EEMEngine::doShowEnding(uint num, bool firstPage) {
-	// `_DisplayEnding @ 1df2:0548` + `_DisplayEndingPage @ 1df2:044c`
-	// (CD); `FUN_1d40_05b7` + `FUN_1d40_031e` (floppy).
-	// CD E<num>.BIN format:
-	//   u16 pageCount
-	//   per page: u16 picNum
-	//             u16 x1, y1, x2, y2   (story rect ??? passed to WordWrap2)
-	//             char text[]          (null-terminated, ParseString opcodes)
-	// Floppy: small title header + shared newspaper BG (PIC 0x8b) +
-	//   per-page overlay pictures.
-	// Render: _FreeFont; _LoadFont("tiny.fnt") @ 1df2:055f; _GetPalette(0);
-	// per-page _GetBackground(picNum) + _WordWrap2(x1, y1, x2-x1, text,
-	// fontColor=0, dropColor=-1). fontColor=0 is the newspaper body-text
-	// colour (asm 1df2:04cf-04f4 ??? Ghidra mis-paired the trailing args).
-	// LEFT/RIGHT page nav (1df2:0689 / 06a0): boundary sets [BP-0x18] to
-	// -1 / +1 ??? that return value drives `_ShowScrapbook` walking forward/
-	// back through solved mysteries (1f78:0664-069c).
-	// firstPage=false opens at LAST page (used by doShowScrapbook after
-	// "previous mystery"; matches `local_8 = 0` @ 1f78:067e).
 	const Common::String fname = Common::String::format("E%u.BIN", num);
 	Common::File f;
 	if (!f.open(Common::Path(fname))) {
@@ -1189,18 +1123,16 @@ int EEMEngine::doShowEnding(uint num, bool firstPage) {
 		return 0;
 	}
 
-	// 1df2:0558-056a: TINY.FNT; restored at 1df2:0625.
 	EEMFont tinyFont;
 	const bool haveTinyFont = tinyFont.load(Common::Path("TINY.FNT"));
 	if (!haveTinyFont)
 		warning("doShowEnding: TINY.FNT failed to load ??? falling back");
 
-	// 1df2:055f `_GetPalette(0)` ??? newspaper CLUT (body text = idx 0).
 	setSitePalette(0);
 	CursorMan.showMouse(true);
 
 	const bool floppyEnding = isFloppy();
-	uint pageOffsets[8];   // ENDING_RANGE_MAX from `_DisplayEnding`
+	uint pageOffsets[8];
 	const uint pageOffsetCap =
 		(uint)(sizeof(pageOffsets) / sizeof(pageOffsets[0]));
 	uint validPages = 0;
@@ -1264,7 +1196,7 @@ int EEMEngine::doShowEnding(uint num, bool firstPage) {
 		_picsArchive.getPicture(kFirstTryBadgePic, firstTryBadge);
 
 	uint pageIdx = firstPage ? 0 : (validPages - 1);
-	int direction = 0;     // -1 / 0 / +1, see header doc.
+	int direction = 0;
 	bool exitLoop = false;
 	bool dirty = true;
 	const Common::Point mousePos = g_system->getEventManager()->getMousePos();
@@ -1334,10 +1266,8 @@ int EEMEngine::doShowEnding(uint num, bool firstPage) {
 									  kFirstTryBadgePos, transp);
 			}
 
-			// `_ParseString` placeholders (0x80=name, 0x82=partner first).
 			const Common::String text = parseString(raw, _playerName, _partner);
 
-			// TINY.FNT + color 0 (asm 1df2:04cf ??? not 0xF as Ghidra shows).
 			const EEMFont &renderFont = haveTinyFont ? tinyFont : _font;
 			if (renderFont.isLoaded() && x2 > x1) {
 				const int textW = MIN<int>((int)x2 - (int)x1, kScreenWidth - (int)x1);
@@ -1430,13 +1360,9 @@ int EEMEngine::doShowEnding(uint num, bool firstPage) {
 	setInteractiveMouseCursor(false);
 	return direction;
 }
-
+// EEM1 `_ShowScrapbook(stage, 0) @ 1f78:0642`; EEM2/London scrapbook `2046::09dd`.
 void EEMEngine::doShowScrapbook(uint stage) {
-	// EEM1 `_ShowScrapbook(stage, 0) @ 1f78:0642`; EEM2/London scrapbook
-	// `FUN_2046_09dd`. Ending viewer returns -1/0/+1 for prev/close/next;
-	// current tier skips unsolved entries. Tier sizes are variant-specific
-	// (EEM1: 24/24/6 cases over three books; London: 25/25 over two books,
-	// no BOOK3.NME) ??? `mysteryTierRange` returns false for London stage 3.
+
 	uint tierLo = 0, tierHi = 0;
 	if (stage < 1 || !mysteryTierRange(stage, tierLo, tierHi))
 		return;
@@ -1448,11 +1374,6 @@ void EEMEngine::doShowScrapbook(uint stage) {
 		return;
 	const bool currentTier = (stage == _chainStage);
 
-	// `_ShowScrapbook @ 2046:0874` (EEM2) opens with the scrapbook/newspaper
-	// MIDI tune 0x5d (same one `_ShowOneScrap` plays after a correct accusation),
-	// gated on the voice flag + MIDI availability (DOS `DAT_3036_4c4e`/`146a` ???
-	// the original gates this music on the *voice* toggle, not the music one).
-	// London only; EEM1's seg-1f78 scrapbook was not verified to play it.
 	if (isLondon() && _music && _voiceOn)
 		_music->playMus(0x5d, /* loop= */ false);
 
@@ -1493,40 +1414,16 @@ void EEMEngine::doShowScrapbook(uint stage) {
 			break;
 		}
 	}
-	// `_ShowScrapbook` / `_ShowOneScrap` stop the MIDI on the way out.
 	if (isLondon() && _music)
 		stopMusic();
 }
-
+// `_DoSetup @ 1f78:044e` (CD) / `_DoSetup_Floppy @ 1ee2:0387`.
 void EEMEngine::doSetup() {
-	// `_DoSetup @ 1f78:044e` (CD) / `_DoSetup_Floppy @ 1ee2:0387`.
-	// PIC 0x40 BG with labels baked in palette key 0xFE; `_SetupSettings
-	// @ 1f78:000d` runs `_SwapColors @ 172b:1d2a` per label rect
-	// (0x15 active, 0x00 inactive ??? nothing drawn as text). 13?? 8-byte
-	// click rects at `_SetupButtons @ 29be:1218` (CD) / 2608:0d8c (floppy);
-	// `HandleSetupButton @ 1f78:0158` dispatches via 12-entry jumptable
-	// at 1f78:0436. Rect map (x1, y1, x2, y2 ??? handler):
-	//   [0]  ( 20, 44, 39, 61)   Partner toggle (1f78:017a)
-	//   [1]  ( 20, 87, 39,104)   Voice toggle   (1f78:0196 ??? DAT_2d5d_3f97)
-	//   [2]  ( 20,127, 39,144)   back to profile (NextScreen=8)
-	//   [3]  (281, 43,299, 60)   ScrapBook 1   (_ShowScrapbook(0,1))
-	//   [4]  (281, 62,299, 79)   ScrapBook 2   gated chainStage>=2
-	//   [5]  (281, 81,299, 98)   ScrapBook 3   gated chainStage>=3
-	//   [6]  (281,108,299,125)   Save game     (_SaveGame @ 2404:0c87)
-	//   [7]  (281,127,299,144)   New Case      (NextScreen=0xa)
-	//   [8]  ( 53,153,108,183)   Done          (SI=1, exit)
-	//   [9]  (145,163,174,187)   Help          (_InterfaceHelp(1))
-	//   [10] (212,153,266,184)   Quit          (_AreYouSure ??? NextScreen=0xffff)
-	//   [11] ( 81, 25,238, 37)   Credits       (PIC 0x208 fullscreen)
-	//   [12] ( 11,  1,  3,  3)   debug placeholder
-	// Highlight rects (Kid1/Kid2/SoundOn/SoundOff @ 29be:1320..1338) drive
-	// `_SwapColors` only ??? not click targets in the original.
 	if (!_font.isLoaded()) {
 		_nextScreen = (ScreenId)_lastScreen;
 		return;
 	}
 
-	// Original button rects (`_SetupButtons` indices wired here).
 	const Common::Rect kPartnerBtn   ( 20,  44,  39,  61); // [0]
 	const Common::Rect kVoiceBtn     ( 20,  87,  39, 104); // [1]
 	const Common::Rect kProfileBtn   ( 20, 127,  39, 144); // [2]
@@ -1539,7 +1436,7 @@ void EEMEngine::doSetup() {
 	const Common::Rect kHelpBtn      (145, 163, 174, 187); // [9]
 	const Common::Rect kQuitBtn      (212, 153, 266, 184); // [10]
 	const Common::Rect kCreditsBtn   ( 81,  25, 238,  37); // [11]
-	// Highlight / fallback-click rects (file-scope copies in `kSetup*Rect`).
+
 	const Common::Rect &kKid1Rect     = kSetupKid1Rect;
 	const Common::Rect &kKid2Rect     = kSetupKid2Rect;
 	const Common::Rect &kSoundOnRect  = kSetupSoundOnRect;
@@ -1547,7 +1444,7 @@ void EEMEngine::doSetup() {
 
 	setupDrawScreen();
 
-	_nextScreen = kScreenSetup;  // sentinel ??? setupLeave picks target
+	_nextScreen = kScreenSetup;
 	while (!shouldQuit()) {
 		Common::Event ev;
 		bool dirty = false;
@@ -1569,8 +1466,6 @@ void EEMEngine::doSetup() {
 			const int mx = ev.mouse.x;
 			const int my = ev.mouse.y;
 
-			// Partner toggle [0]. Direct Jake/Jenny label clicks are a
-			// ScummVM-only fallback.
 			if (kPartnerBtn.contains(mx, my)) {
 				_partner = _partner == kPartnerJake ? kPartnerJenny : kPartnerJake;
 				dirty = true;
@@ -1591,7 +1486,6 @@ void EEMEngine::doSetup() {
 				continue;
 			}
 
-			// Voice toggle [1].
 			if (kVoiceBtn.contains(mx, my)) {
 				_voiceOn = !_voiceOn;
 				if (_audio)
@@ -1618,46 +1512,35 @@ void EEMEngine::doSetup() {
 				continue;
 			}
 
-			// New Case [7] @ 1f78:01ad ??? NextScreen=0xa (CHOOSE_MYSTERY).
 			if (kNewCaseBtn.contains(mx, my)) {
 				saveProfile(_playerName);
 				_nextScreen = kScreenChooseMystery;
 				return;
 			}
 
-			// Save [6] ??? `_SaveGame @ 2404:0c87`.
 			if (kSaveBtn.contains(mx, my)) {
 				saveProfile(_playerName);
 				continue;
 			}
 
-			// Done [8] ??? MOV SI,1; JMP exit (NextScreen stays = LastScreen).
 			if (kDoneBtn.contains(mx, my)) {
 				setupLeave();
 				return;
 			}
 
-			// Quit [10] ??? `_AreYouSure(0)` ??? NextScreen=0xffff.
 			if (kQuitBtn.contains(mx, my)) {
 				if (areYouSure()) {
 					_nextScreen = kScreenInvalid;
 					return;
 				}
-				dirty = true;  // restore the BG after the prompt
+				dirty = true;
 				continue;
 			}
 
-			// Help [9] ??? `_InterfaceHelp(1) @ 1560:0205`. Help-pic table
-			// @ 29be:00c8: each `num` slot = 5 bytes (count + two u16
-			// PIC IDs). num=1 ??? count=2, pics = {0x0192, 0x01B1}. Each
-			// pic blitted via `_Rect_Move_Mask` (transparent = pic
-			// `miscflags >> 8`, so setup BG shows through). Also hides
-			// cursor (1560:0216-021c). ESC breaks (1560:02b3).
 			if (kHelpBtn.contains(mx, my)) {
 				static const uint16 kHelp1Pics[] = { 0x0192, 0x01B1 };
 				CursorMan.showMouse(false);
 				for (uint i = 0; i < ARRAYSIZE(kHelp1Pics); i++) {
-					// Restore BG between cards (1560:02e5 `_vga_fvidvid(0)`).
 					setupDrawScreen();
 					const Common::KeyCode k =
 						setupShowFullscreenPic(kHelp1Pics[i], /* transparent= */ true);
@@ -1669,7 +1552,6 @@ void EEMEngine::doSetup() {
 				continue;
 			}
 
-			// Credits [11] @ 1f78:025a ??? PIC 0x208 fullscreen.
 			if (kCreditsBtn.contains(mx, my)) {
 				CursorMan.showMouse(false);
 				setupShowFullscreenPic(0x208, /* transparent= */ false);
@@ -1680,14 +1562,12 @@ void EEMEngine::doSetup() {
 				continue;
 			}
 
-			// Profile [2] ??? NextScreen=8.
 			if (kProfileBtn.contains(mx, my)) {
 				saveProfile(_playerName);
 				_nextScreen = kScreenProfile;
 				return;
 			}
 
-			// ScrapBook [3]/[4]/[5] ??? `_ShowScrapbook(stage, 0)`.
 			if (kScrap1Btn.contains(mx, my)) {
 				doShowScrapbook(1);
 				setSitePalette(0);
@@ -1700,7 +1580,7 @@ void EEMEngine::doSetup() {
 				dirty = true;
 				continue;
 			}
-			// London has no third book (`mysteryTierRange` rejects stage 3).
+
 			if (kScrap3Btn.contains(mx, my) && !isLondon() &&
 				_chainStage >= 3) {
 				doShowScrapbook(3);
@@ -1741,9 +1621,6 @@ void EEMEngine::setupDrawScreen() {
 	g_system->updateScreen();
 }
 
-// Render picId and block until input. transparent=true: overlay via
-// `_Rect_Move_Mask` (`_InterfaceHelp @ 1560:0205`). false: raw fullscreen
-// blit via `_vga_fbuffvid` (credits @ 1f78:0281).
 Common::KeyCode EEMEngine::setupShowFullscreenPic(uint16 picId, bool transparent) {
 	Picture pic;
 	if (!_picsArchive.getPicture(picId, pic)) {
@@ -1759,7 +1636,6 @@ Common::KeyCode EEMEngine::setupShowFullscreenPic(uint16 picId, bool transparent
 			g_system->unlockScreen();
 		}
 		const byte transp = (byte)(pic.flags >> 8);
-		// Explicit destPos ??? no-destPos overload stretches to dst.
 		scratch.transBlitFrom(pic.surface, Common::Point(0, 0),
 							  (uint32)transp);
 	} else {
@@ -1786,8 +1662,6 @@ Common::KeyCode EEMEngine::setupShowFullscreenPic(uint16 picId, bool transparent
 }
 
 void EEMEngine::setupLeave() {
-	// `_DoSetup` entry: _NextScreen = _LastScreen. Fall back to it
-	// unless a handler already overrode _nextScreen.
 	if (_nextScreen == kScreenSetup) {
 		_nextScreen = (ScreenId)_lastScreen;
 		if (_nextScreen == kScreenSetup ||
@@ -1796,10 +1670,8 @@ void EEMEngine::setupLeave() {
 	}
 	saveProfile(_playerName);
 }
-
+// `_SetupSettings @ 2046:0008`
 void EEMEngine::setupDrawScreenLondon() {
-	// `_SetupSettings @ 2046:0008`: BG PIC 0x40 + _SwapColors highlights for the
-	// 4 toggles (key 0xFE, 0x15 active / 0x00 dim), drawn ON-then-OFF.
 	Graphics::ManagedSurface scratch(kScreenWidth, kScreenHeight,
 		Graphics::PixelFormat::createFormatCLUT8());
 	scratch.clear();
@@ -1808,16 +1680,16 @@ void EEMEngine::setupDrawScreenLondon() {
 		scratch.simpleBlitFrom(bg.surface);
 
 	const byte kKey = 0xFE, kBright = 0x15, kDim = 0x00;
-	// Partner (DAT_3036_4bd4).
+
 	swapColors(scratch, kLonSetJake,  kKey, _partner == kPartnerJake  ? kBright : kDim);
 	swapColors(scratch, kLonSetJenny, kKey, _partner == kPartnerJenny ? kBright : kDim);
-	// Voice (DAT_3036_4c4e).
+
 	swapColors(scratch, kLonSetVoiceOn,  kKey, _voiceOn ? kBright : kDim);
 	swapColors(scratch, kLonSetVoiceOff, kKey, _voiceOn ? kDim : kBright);
-	// Music (DAT_3036_4cc0).
+
 	swapColors(scratch, kLonSetMusicOn,  kKey, _musicOn ? kBright : kDim);
 	swapColors(scratch, kLonSetMusicOff, kKey, _musicOn ? kDim : kBright);
-	// Highlight boxes (DAT_3036_4c4a; hide_highlight_boxes is the inverse).
+
 	const bool hiOn = !ConfMan.getBool("hide_highlight_boxes");
 	swapColors(scratch, kLonSetHiOn,  kKey, hiOn ? kBright : kDim);
 	swapColors(scratch, kLonSetHiOff, kKey, hiOn ? kDim : kBright);
@@ -1827,16 +1699,8 @@ void EEMEngine::setupDrawScreenLondon() {
 	g_system->updateScreen();
 }
 
+// `_DoSetup @ 2046:067b`
 void EEMEngine::doSetupLondon() {
-	// `_DoSetup @ 2046:067b`. BG PIC 0x40; 13 click rects (`_SetupButtons @
-	// 2bca:12ce`); `HandleSetupButton @ 2046:01d9` dispatch (verified from the
-	// jumptable @ 2046:0661). EEM2 has FOUR toggles vs EEM1's two and a
-	// rearranged left column:
-	//   [0]( 20, 44) Partner    [1]( 20, 63) Voice    [2]( 20,101) Highlight boxes
-	//   [3]( 20,127) Profile(8) [12](20, 82) Music    [4](281, 43) ScrapBook +
-	//   [5](281, 62) ScrapBook- [6](281,108) Save     [7](281,127) New case(0xa)
-	//   [8]( 53,153) Done       [9](145,163) Quit      [10](212,153) Help
-	//   [11]( 81, 25) Credits (PIC 0x208).  _SavePlayerRecord on exit (setupLeave).
 	if (!_font.isLoaded()) {
 		_nextScreen = (ScreenId)_lastScreen;
 		return;
@@ -1856,7 +1720,7 @@ void EEMEngine::doSetupLondon() {
 	const Common::Rect kMusicBtn   ( 20,  82,  38,  99); // [12]
 
 	setupDrawScreenLondon();
-	_nextScreen = kScreenSetup;  // sentinel ??? setupLeave picks the target
+	_nextScreen = kScreenSetup;
 	while (!shouldQuit()) {
 		Common::Event ev;
 		bool dirty = false;
@@ -1876,7 +1740,6 @@ void EEMEngine::doSetupLondon() {
 				continue;
 			const int mx = ev.mouse.x, my = ev.mouse.y;
 
-			// --- toggles (stay on screen, re-render) ---
 			if (kPartnerBtn.contains(mx, my)) {
 				_partner = (_partner == kPartnerJake) ? kPartnerJenny
 													  : kPartnerJake;
@@ -1904,7 +1767,6 @@ void EEMEngine::doSetupLondon() {
 				continue;
 			}
 
-			// --- navigation (leave the screen) ---
 			if (kProfileBtn.contains(mx, my)) {
 				saveProfile(_playerName);
 				_nextScreen = kScreenProfile;
@@ -1924,19 +1786,16 @@ void EEMEngine::doSetupLondon() {
 					_nextScreen = kScreenInvalid;
 					return;
 				}
-				dirty = true;  // restore BG under the prompt
+				dirty = true;
 				continue;
 			}
 
-			// --- actions that stay on screen ---
 			if (kSaveBtn.contains(mx, my)) {
-				// `_SaveGame` is gated on a game being active ([0x1966]).
 				if (_mystery.isLoaded())
 					saveProfile(_playerName);
 				continue;
 			}
 			if (kHelpBtn.contains(mx, my)) {
-				// `_InterfaceHelp(0)`. Reuse the EEM1 help-card mechanism.
 				static const uint16 kHelpPics[] = { 0x0192, 0x01B1 };
 				CursorMan.showMouse(false);
 				for (uint i = 0; i < ARRAYSIZE(kHelpPics); i++) {
@@ -1957,8 +1816,7 @@ void EEMEngine::doSetupLondon() {
 				dirty = true;
 				continue;
 			}
-			// ScrapBook +/- ??? London has 2 books (stage 1 / 2). The DOS pages
-			// next/prev (FUN_2046_0874); map to the two available books.
+
 			if (kScrapNext.contains(mx, my)) {
 				doShowScrapbook(1);
 				setSitePalette(0);
@@ -1978,11 +1836,8 @@ void EEMEngine::doSetupLondon() {
 		g_system->delayMillis(15);
 	}
 }
-
+// `_ActionScreen @ 1c33:195b`
 void EEMEngine::doActionScreen() {
-	// `_ActionScreen @ 1c33:195b` ??? BG PIC 0x104 + PIC 9 @ (10, 0x87),
-	// `_DoChoose` with ActionNames @ 29be:0d6a. 5 picks alternating with
-	// separators.
 	enum MenuPick {
 		kPickChoose = 0,
 		kPickPractice,
@@ -2008,14 +1863,8 @@ void EEMEngine::doActionScreen() {
 	};
 	const char * const *kPickLabel = isSpanish() ? kPickLabelES : kPickLabelEN;
 	// London has no BOOK3.NME, so its action menu offers four entries
-	// (Choose / Practice / ScrapBook 1 / ScrapBook 2). EEM1 adds ScrapBook 3.
 	const uint numPicks = isLondon() ? (uint)kPickScrap3 : (uint)kNumPicks;
 
-	// Gating @ EEM1 1c33:19d1-1a70 by chain stage + per-tier solves:
-	//   stage 1: grey SB2/3; SB1 needs any tier-1 solve
-	//   stage 2: grey Practice + SB3; SB2 needs tier-2 solve
-	//   stage 3: grey Practice; SB3 needs tier-3 solve (EEM1 only)
-	//   stage 4: grey Choose + Practice
 	uint loT = 0, hiT = 0;
 	const bool anySolved1 = mysteryTierRange(1, loT, hiT) &&
 							anyMysterySolved(loT, hiT);
@@ -2035,7 +1884,7 @@ void EEMEngine::doActionScreen() {
 	const bool kPickEnabled[kNumPicks] = {
 		chooseOn, practiceOn, scrap1On, scrap2On, scrap3On
 	};
-	// Seed selection on first enabled entry.
+
 	uint pick = 0;
 	for (uint i = 0; i < numPicks; i++) {
 		if (kPickEnabled[i]) {
@@ -2046,7 +1895,6 @@ void EEMEngine::doActionScreen() {
 
 	const char *kSeparator = "----------------------------------";
 
-	// `_DoChoose @ 1c33:0514` click rects (4??u16 {x1,y1,x2,y2} in seg 29be):
 	const Common::Rect kOkRect      ( 12,  63,  41,  87); // 29be:0cd8 confirm
 	const Common::Rect kHelpRect    ( 12, 100,  41, 124); // 29be:0ce0 help
 	const Common::Rect kExitRect    ( 12, 137,  41, 161); // 29be:0ce8 cancel
@@ -2054,7 +1902,6 @@ void EEMEngine::doActionScreen() {
 
 	CursorMan.showMouse(true);
 
-	// Launcher-resume path can enter from `_AllBlack` palette.
 	setSitePalette(0);
 
 	Picture bg;
@@ -2087,7 +1934,6 @@ void EEMEngine::doActionScreen() {
 				return;
 			if (ev.type == Common::EVENT_LBUTTONDOWN) {
 				if (kOkRect.contains(ev.mouse.x, ev.mouse.y)) {
-					// Greyed entries ignored (`_DoChoose @ 1c33:0635`).
 					if (kPickEnabled[pick])
 						confirmed = true;
 					break;
@@ -2098,7 +1944,6 @@ void EEMEngine::doActionScreen() {
 					break;
 				}
 				if (kHelpRect.contains(ev.mouse.x, ev.mouse.y)) {
-					// `_ActionScreen` sets Chelp=0.
 					continue;
 				}
 				if (kListRect.contains(ev.mouse.x, ev.mouse.y)) {
@@ -2129,7 +1974,6 @@ void EEMEngine::doActionScreen() {
 				break;
 			}
 			if (k == Common::KEYCODE_UP || k == Common::KEYCODE_LEFT) {
-				// `_DoChoose` arrow handlers @ 1c33:0514, bounded loop.
 				for (int i = 0; i < (int)numPicks; i++) {
 					pick = (pick == 0) ? (numPicks - 1) : pick - 1;
 					if (kPickEnabled[pick])
@@ -2179,7 +2023,6 @@ void EEMEngine::doActionScreen() {
 		return;
 	}
 
-	// "Practice Mystery" is the tutorial ??? mystery 0.
 	if (pick == kPickPractice) {
 		if (!_mystery.load(0, &_rng)) {
 			warning("doActionScreen: failed to load practice mystery");
@@ -2194,8 +2037,6 @@ void EEMEngine::doActionScreen() {
 	}
 
 	if (pick == kPickScrap1 || pick == kPickScrap2 || pick == kPickScrap3) {
-		// `_ActionScreen` handlers @ 1c33:1B13 / 1B26 / 1B40 ???
-		// `_ShowScrapbook(0, stage)`. Returns here afterwards.
 		const uint stage = (pick == kPickScrap1) ? 1
 						 : (pick == kPickScrap2) ? 2 : 3;
 		doShowScrapbook(stage);
@@ -2207,11 +2048,9 @@ void EEMEngine::doActionScreen() {
 
 	_nextScreen = kScreenChooseMystery;
 }
-
+// EEM1: `_DoChooseMystery @ 1a35:02b7` + `_CaseSelection @ 1c33:0a87`
+// EEM2:  1abf:022a + 1cd3:0a9d)
 void EEMEngine::doCaseSelection() {
-	// `_DoChooseMystery @ 1a35:02b7` + `_CaseSelection @ 1c33:0a87`
-	// (EEM2 @ 1abf:022a / 1cd3:0a9d): load BOOK<stage>.NME, draw PIC 0x41
-	// + centered "Book N" title, then read the chosen M*.BIN.
 	const uint kMaxMystery = isLondon() ? 50 : 54;
 
 	CursorMan.showMouse(true);
@@ -2225,7 +2064,6 @@ void EEMEngine::doCaseSelection() {
 	const bool haveRevealPic =
 		_picsArchive.getPicture(kCaseSelectionRevealPic, revealPic);
 
-	// `_CaseSelection @ 1c33:0a87` greeter ANI 0x15 (Jake) / 0x16 (Jenny).
 	const uint kKdAniId = (_partner == kPartnerJake) ? 0x15 : 0x16;
 	Animation kdAnim;
 	const bool haveKdAnim = _aniArchive.loadAnimation(kKdAniId, kdAnim)
@@ -2233,12 +2071,6 @@ void EEMEngine::doCaseSelection() {
 	const int kKdAnimX = 0x112;
 	const int kKdAnimY = 0x50;
 
-	// Tier ??? BOOK<n>.NME. EEM1 ships three books (Junior 1..24, Senior
-	// 25..48, Master 49..54); EEM2/London ships two (1..25 / 26..50). London
-	// has no BOOK3.NME, and its stage 3 is transient (`_DisplayCorrect @
-	// 1ea1:0619` collapses it to 4), so should it ever be reached here it
-	// reuses BOOK2.NME rather than opening a missing file. `mysteryTierRange`
-	// is the single source of truth for the per-tier mystery ranges.
 	uint book;
 	switch (_chainStage) {
 	case 2:  book = 2; break;
@@ -2257,7 +2089,6 @@ void EEMEngine::doCaseSelection() {
 	}
 	const uint listLen = MIN<uint>((uint)names.size(), stageHi - stageLo + 1);
 
-	// Solved-row gating @ `_DoChoose @ 1c33:0521`.
 	Common::Array<bool> solvedFlags;
 	solvedFlags.resize(listLen);
 	for (uint i = 0; i < listLen; i++) {
@@ -2321,8 +2152,6 @@ void EEMEngine::doCaseSelection() {
 					return;
 				}
 				if (kChooserHelpRect.contains(ev.mouse.x, ev.mouse.y)) {
-					// Original returns 0xfffe ??? `_ChooseSavedGame`. ScummVM
-					// stores in-progress cases per profile.
 					saveProfile(_playerName);
 					_mystery.clear();
 					_nextScreen = kScreenProfile;
@@ -2452,39 +2281,38 @@ void EEMEngine::doCaseSelection() {
 	debugC(1, kDebugMystery, "Mystery %u loaded; %u sites, %u suspects",
 		   mn, _mystery.numSites(), _mystery.numSuspects());
 }
+// `_DoNotebook @ 161e:0500` + `_DrawNotes @ 161e:01d0` +
+// `_HandleNoteButton @ 161e:03cb`. _NotebookRect = (78, 12, 288, 152).
+// _NoteButtons @ 29be:0147 ??? 11 rects ?? 8 bytes. Jumptable @ 161e:04ec
+// dispatches handler[i-1] (rect 0's i-1 underflows = decorative slot):
+//   [0] (134,174,155,190)  decorative ??? no handler
+//   [1] ( 93,174,115,190)  HELP ??? `_InterfaceHelp(0)`           (0x3f9)
+//   [2] (157,174,178,190)  GALLERY ??? `_NextScreen = 5`          (0x477)
+//   [3] (  5, 80, 44,110)  host hint ??? `_KDHelp`                (0x403)
+//   [4] (180,174,201,190)  SOLVE ??? `_SolvedCheck` ??? NextScreen=7 (0x436)
+//   [5] (204,174,224,190)  PAGE NEXT ??? `_EraseNotes` + redraw   (0x489)
+//   [6] (226,174,247,190)  PAGE PREV ??? CurrentPage-- + redraw   (0x4ab)
+//   [7] (  7,177, 57,200)  MAP ??? `_NextScreen = 2`              (0x480)
+//   [8] ( 35,111, 56,136)  SITE ??? `_NextScreen = 3`             (0x3ed)
+//   [9] (  0,  0,  0,  0)  same exit as [8]
+//   [10] (267,174,288,190) ??? `_InterfaceHelp(0)` again          (0x3f9)
+//
+// EEM2/London (`_DoNotebook @ 16a0:0517`, `_HandleNoteButton @ 16a0:03dd`,
+// jumptable @ 16a0:0503) reuses the SAME button rect table (2bca:0151) and
+// handler set, but reassigns two slots and revives slot 9 (verified by
+// disassembly ??? `[0x9292]` is the next-screen code):
+//   [1] ( 93,174,115,190) MAP  ??? screen 2  (EEM1: a 2nd InterfaceHelp).
+//                         London's dedicated map button.
+//   [7] (  7,177, 57,200) DOS EEM2 ??? SITE, but we keep it as MAP (??? 2) in
+//                         both variants ??? the EEM1 partner-foot map shortcut
+//                         (a player convenience alongside button [1]).
+//   [9] (  0,  0, 66, 79) SITE ??? screen 3  (EEM1: dead 0-rect)
+// Everything else (gallery, accuse, host hint, page next/prev, help [10],
+// site [8]) is identical. The note rendering, pagination, partner ANI
+// (same 1/0xb), and gizmo colour-cycle are shared as-is; only button [1]
+// (London ??? map) and the extra close area [9] are gated on `isLondon()`.
 
 void EEMEngine::doNotebook() {
-	// `_DoNotebook @ 161e:0500` + `_DrawNotes @ 161e:01d0` +
-	// `_HandleNoteButton @ 161e:03cb`. _NotebookRect = (78, 12, 288, 152).
-	// _NoteButtons @ 29be:0147 ??? 11 rects ?? 8 bytes. Jumptable @ 161e:04ec
-	// dispatches handler[i-1] (rect 0's i-1 underflows = decorative slot):
-	//   [0] (134,174,155,190)  decorative ??? no handler
-	//   [1] ( 93,174,115,190)  HELP ??? `_InterfaceHelp(0)`           (0x3f9)
-	//   [2] (157,174,178,190)  GALLERY ??? `_NextScreen = 5`          (0x477)
-	//   [3] (  5, 80, 44,110)  host hint ??? `_KDHelp`                (0x403)
-	//   [4] (180,174,201,190)  SOLVE ??? `_SolvedCheck` ??? NextScreen=7 (0x436)
-	//   [5] (204,174,224,190)  PAGE NEXT ??? `_EraseNotes` + redraw   (0x489)
-	//   [6] (226,174,247,190)  PAGE PREV ??? CurrentPage-- + redraw   (0x4ab)
-	//   [7] (  7,177, 57,200)  MAP ??? `_NextScreen = 2`              (0x480)
-	//   [8] ( 35,111, 56,136)  SITE ??? `_NextScreen = 3`             (0x3ed)
-	//   [9] (  0,  0,  0,  0)  same exit as [8]
-	//   [10] (267,174,288,190) ??? `_InterfaceHelp(0)` again          (0x3f9)
-	// BG PIC 0x3f; partner ANI 1 (Jake) / 0xb (Jenny) at (5, 80).
-	//
-	// EEM2/London (`_DoNotebook @ 16a0:0517`, `_HandleNoteButton @ 16a0:03dd`,
-	// jumptable @ 16a0:0503) reuses the SAME button rect table (2bca:0151) and
-	// handler set, but reassigns two slots and revives slot 9 (verified by
-	// disassembly ??? `[0x9292]` is the next-screen code):
-	//   [1] ( 93,174,115,190) MAP  ??? screen 2  (EEM1: a 2nd InterfaceHelp).
-	//                         London's dedicated map button.
-	//   [7] (  7,177, 57,200) DOS EEM2 ??? SITE, but we keep it as MAP (??? 2) in
-	//                         both variants ??? the EEM1 partner-foot map shortcut
-	//                         (a player convenience alongside button [1]).
-	//   [9] (  0,  0, 66, 79) SITE ??? screen 3  (EEM1: dead 0-rect)
-	// Everything else (gallery, accuse, host hint, page next/prev, help [10],
-	// site [8]) is identical. The note rendering, pagination, partner ANI
-	// (same 1/0xb), and gizmo colour-cycle are shared as-is; only button [1]
-	// (London ??? map) and the extra close area [9] are gated on `isLondon()`.
 	if (!_mystery.isLoaded() || !_font.isLoaded())
 		return;
 
@@ -2534,7 +2362,6 @@ void EEMEngine::doNotebook() {
 				}
 			}
 			if (ev.type == Common::EVENT_LBUTTONDOWN) {
-				// Earlier rects win on overlap (matches `_FindButton`).
 				if (kPdaSiteRect.contains(ev.mouse.x, ev.mouse.y) ||
 					(isLondon() &&
 					 kPdaLondonCloseRect.contains(ev.mouse.x, ev.mouse.y))) {
@@ -2543,19 +2370,13 @@ void EEMEngine::doNotebook() {
 					break;  // back to site
 				}
 				if (kPdaPartnerFootMapRect.contains(ev.mouse.x, ev.mouse.y)) {
-					// EEM1 `_NoteButtons[7]` ??? map (screen 2). DOS EEM2 reassigns
-					// this slot to SITE, but we intentionally keep the partner-
-					// foot hotspot as a map shortcut in BOTH variants for parity
-					// with EEM1. London also has its own dedicated map button
-					// (`kPdaHelpRect` below) and can still close the PDA via the
-					// site button (35,111) or the top-left corner (0,0,66,79).
 					_nextScreen = kScreenMapAlt;
 					exitFlag = true;
 					break;
 				}
 				if (kPdaPartnerHeadHintRect.contains(ev.mouse.x, ev.mouse.y)) {
 					setInteractiveMouseCursor(false);
-					doHelp();              // _KDHelp = host hint
+					doHelp();
 					dirty = true;
 					continue;
 				}
@@ -2570,9 +2391,6 @@ void EEMEngine::doNotebook() {
 					break;
 				}
 				if (kPdaHelpRect.contains(ev.mouse.x, ev.mouse.y)) {
-					// `_NoteButtons[1]`. EEM1: a 2nd `_InterfaceHelp(0)` button
-					// (PICs 0x63 / 0x1ae). London reassigns this slot to MAP
-					// (`_HandleNoteButton` slot 1 ??? screen 2).
 					if (isLondon()) {
 						_nextScreen = kScreenMapAlt;
 						exitFlag = true;
@@ -2595,31 +2413,23 @@ void EEMEngine::doNotebook() {
 					continue;
 				}
 				if (kPdaHelp2Rect.contains(ev.mouse.x, ev.mouse.y)) {
-					// _NoteButtons[10] ??? handler 0x03f9 = same as [1].
 					setInteractiveMouseCursor(false);
 					doInterfaceHelp(0);
 					dirty = true;
 					continue;
 				}
-				// The notebook screen is read-only in the original:
-				// `_DoNotebook @ 161e:0500` only checks clicks against
-				// `_NoteButtons` (11 rects). Clue toggling lives in the
-				// accuse screen.
 			}
 		}
 		if (exitFlag)
 			break;
 
 		const uint32 now = g_system->getMillis();
-		// Re-render every 100 ms for partner sprite cycle.
 		if (dirty || now - lastDraw >= 100) {
 			drawNotebookFrame(page);
 			lastDraw = now;
 			mouse = g_system->getEventManager()->getMousePos();
 			setInteractiveMouseCursor(notebookButtonAt(mouse.x, mouse.y));
 		}
-		// _GizmoColorCycle @ 1c33:0002 ??? `_DoNotebook` rotates 0x6f..0x73 each
-		// _CheckFrameRate tick (the PDA gizmo / LED indicator shimmer).
 		if (now - gizmoLastTick >= kChooserCycleMillis) {
 			gizmoLastTick = now;
 			cycleChooserPalette();
@@ -2647,9 +2457,6 @@ Common::String EEMEngine::notebookNoteText(uint clueId, const byte *ni,
 		return parseString(Common::String(p, len),
 						   _playerName, _partner);
 	}
-	// EEM1 CD: 4-byte entries (textOff at +0, points at +2). EEM2/London CD:
-	// 2-byte entries (textOff only) ??? `_DrawNotes @ 16a0:01de` reads
-	// `noteIndex[clueId*2]`.
 	const uint stride = isLondon() ? 2 : 4;
 	const uint16 textOff = READ_LE_UINT16(ni + clueId * stride);
 	return parseString(_mystery.textAt(textOff),
@@ -2657,8 +2464,6 @@ Common::String EEMEngine::notebookNoteText(uint clueId, const byte *ni,
 }
 
 void EEMEngine::drawNotebookFrame(int &page) {
-	// `_DrawNotes @ 161e:01d0` per-page layout + partner sprite at (5,80)
-	// from `_DoNotebook @ 161e:0500`.
 	const Common::Rect kNotebookRect(78, 12, 288, 152);
 
 	Graphics::ManagedSurface scratch(kScreenWidth, kScreenHeight,
@@ -2669,7 +2474,6 @@ void EEMEngine::drawNotebookFrame(int &page) {
 	if (_picsArchive.getPicture(0x3f, frame))
 		scratch.simpleBlitFrom(frame.surface);
 
-	// Partner ANI 1/0xb (cells); script 0x01 (`_NewAnimation @ 161e:054c`).
 	blitPdaPartner(scratch, _aniArchive, _partner, kPdaNotebookPartner,
 				   g_system->getMillis());
 
@@ -2678,8 +2482,7 @@ void EEMEngine::drawNotebookFrame(int &page) {
 	// EEM2/London NoteIndex entries are 2 bytes (no points field), so its real
 	// clue count is the section size / 2. `noteIndexCount()` assumes the EEM1
 	// 4-byte stride (and stays that way for the accuse-scoring path, which is a
-	// separate London concern), so derive the London-correct count here ??? else
-	// high-id notes get dropped.
+	// separate London concern)
 	const uint16 londonCount = isLondon()
 		? (uint16)(_mystery.noteSectionSize() / 2) : 0;
 	Common::Array<uint> found;
@@ -2699,14 +2502,10 @@ void EEMEngine::drawNotebookFrame(int &page) {
 	const int kRectW = kNotebookRect.width();
 	const int kRectH = kNotebookRect.height();
 
-	// Walk forward to start clue of current page; fits as many as kRectH.
 	int clueCursor = 0;
 	Common::Array<int> pageStarts;
 	pageStarts.push_back(0);
-	// Floppy NoteIndex entry (7 bytes): +0 clueTextOff (absolute, what
-	// the notebook displays), +2 Jake spoken, +4 Jenny spoken, +6 score.
-	// `_DrawNotes_Floppy / FUN_15e0_01e8` reads only byte +0.
-	// CD entries are 4 bytes, offsets relative to TextBlock @ header[+0xc].
+
 	const bool floppyNb = isFloppy();
 	const byte *bufBase = _mystery.blobAt(0);
 	const uint32 mysSz  = _mystery.dataSize();
@@ -2735,10 +2534,6 @@ void EEMEngine::drawNotebookFrame(int &page) {
 			page = 0;
 	}
 
-	// `_DrawNotes @ 161e:01d0` is read-only on this screen ??? clue selection
-	// is driven exclusively from the accuse screen. The 0x3C / 0x5C colour
-	// choice still reflects `_NoteSelected[]` so picks made in accuse remain
-	// visible when the player flips back here.
 	const int startClue = (page < (int)pageStarts.size())
 							? pageStarts[page] : 0;
 	const int endClue   = (page + 1 < (int)pageStarts.size())
@@ -2764,8 +2559,6 @@ void EEMEngine::drawNotebookFrame(int &page) {
 		y += h + 7;
 	}
 
-	// `_DrawNotes @ 161e:01d0` appends a terminator line at the bottom of
-	// the last page once the clue list is exhausted ??? string @ 29be:01f4.
 	const bool isLastPage = (page + 1 >= (int)pageStarts.size());
 	if (isLastPage) {
 		const char *kEndMarker = isSpanish()
@@ -2780,11 +2573,6 @@ void EEMEngine::drawNotebookFrame(int &page) {
 }
 
 void EEMEngine::doGallery() {
-	// `_DoGallery @ 158f:065b` + `_DrawGallery @ 158f:0046`. BG PIC 0x3f.
-	// Partner ANI 2 (Jake) / 0x10 (Jenny) at (5, 0x50). 5 slot positions
-	// @ 29be:0x116. Per suspect i: picId = *(u16*)(_GalleryData + i*0x46);
-	// visible = _InGallery[_NewOrder[i]]; drawY = pos.y + (0x48 - pic.h)
-	// (bottom-aligned to baseline). Click ??? `_SearchSuspects` ??? moreInfo.
 	if (!_mystery.isLoaded())
 		return;
 
@@ -2796,16 +2584,13 @@ void EEMEngine::doGallery() {
 
 	CursorMan.showMouse(true);
 
-	// Pre-load PIC 0x3f for the MoreInfo backdrop blit further down.
-	// (`drawGalleryFrame` reloads it on its own per-call too.)
 	Picture galBg;
 	const bool haveBg = _picsArchive.getPicture(0x3f, galBg);
 
 	const uint8 num = _mystery.numSuspects();
 
-	// Cache slot rects for click hit-testing.
 	Common::Array<Common::Rect> slotRects;
-	Common::Array<int> slotSuspect; // logical suspect index in [0, num)
+	Common::Array<int> slotSuspect;
 	slotRects.resize(num);
 	slotSuspect.resize(num);
 	for (uint i = 0; i < num; i++) {
@@ -2877,10 +2662,6 @@ void EEMEngine::doGallery() {
 					break;
 				}
 				if (kPdaHelpRect.contains(ev.mouse.x, ev.mouse.y)) {
-					// EEM1 slot 1 ??? a 2nd `_InterfaceHelp(0)` (158f:0625).
-					// EEM2 gallery `_HandleGalleryButton @ 160e:05c7` slot 1 ???
-					// MAP (`MOV [0x9292],2` @ 160e:05fe) ??? London's dedicated
-					// map button, same as the notebook/accuse PDA bar.
 					if (isLondon()) {
 						_nextScreen = kScreenMapAlt;
 						exitFlag = true;
@@ -2897,7 +2678,6 @@ void EEMEngine::doGallery() {
 					lastDraw = 0;
 					continue;
 				}
-				// `_SearchSuspects` ??? per-slot rect ??? suspect index.
 				bool clicked = false;
 				for (uint i = 0; i < slotRects.size(); i++) {
 					if (slotSuspect[i] < 0)
@@ -2934,7 +2714,6 @@ void EEMEngine::doGallery() {
 									  gallerySlotAt(slotRects, slotSuspect,
 													mouse.x, mouse.y));
 		}
-		// _GizmoColorCycle @ 1c33:0002 ??? `_DoGallery` rotates 0x6f..0x73 each tick.
 		if (now - gizmoLastTick >= kChooserCycleMillis) {
 			gizmoLastTick = now;
 			cycleChooserPalette();
@@ -2944,16 +2723,9 @@ void EEMEngine::doGallery() {
 	}
 	setInteractiveMouseCursor(false);
 }
-
+// `MoreInfo @ 158f:0419`
 bool EEMEngine::moreInfo(const byte *gd, uint suspectIdx,
 						  const Picture &galBg, bool haveBg) {
-	// `MoreInfo @ 158f:0419` ??? suspect detail. PIC = entry+0,
-	// _AddPicBackground at (0x94, 0xf), notes via _DrawGalleryNotes.
-	// CD layout (0x46-byte stride): +0 picId, +8 clueCount(u16), +0xa
-	// clue IDs(u16, 0xFFFF terminator, max 30).
-	// Floppy (`_MoreInfo_Floppy = 154e:042b` ??? `FUN_15e0_01e8`):
-	// variable stride. +0 picId, +2 alibi (0xFFFF = guilty), +4 count(u8),
-	// +5 clue IDs(u8).
 	const bool floppyMI = isFloppy();
 	const byte *suspect = floppyMI
 							  ? _mystery.floppySuspectEntry(suspectIdx)
@@ -2967,8 +2739,6 @@ bool EEMEngine::moreInfo(const byte *gd, uint suspectIdx,
 
 	setInteractiveMouseCursor(false);
 
-	// _GalleryNoteRect = (78, 93, 288, 152) @ 29be:0100.
-	// `_DrawGalleryNotes @ 158f:01f4`.
 	const int rx = 78, ry = 93;
 	const int rw = 288 - 78, rh = 152 - 93;
 	const int lineH = _font.getFontHeight();
@@ -2976,7 +2746,6 @@ bool EEMEngine::moreInfo(const byte *gd, uint suspectIdx,
 	const byte *ni = _mystery.noteIndex();
 	const uint16 niCount = _mystery.noteIndexCount();
 
-	// Pagination via `_NextClue` + `_PageBreaks[]` (case 6 @ 158f:03b8).
 	uint pageStart = 0;
 	Common::Array<uint> pageStack;
 	bool back = false;
@@ -2989,7 +2758,7 @@ bool EEMEngine::moreInfo(const byte *gd, uint suspectIdx,
 		ms.clear();
 		if (haveBg)
 			ms.simpleBlitFrom(galBg.surface);
-		// Partner sprite at (5, 0x50). Re-blitted per page.
+
 		blitPdaPartner(ms, _aniArchive, _partner, kPdaGalleryPartner,
 					   g_system->getMillis());
 		Picture detail;
@@ -3097,10 +2866,6 @@ bool EEMEngine::moreInfo(const byte *gd, uint suspectIdx,
 			}
 		}
 
-		// `_HandleMoreButton @ 158f:027d`:
-		//   [0] NOTEBOOK???4, [1] HELP???_InterfaceHelp, [2] GALLERY (close),
-		//   [3] _KDHelp, [4] ACCUSE???7, [5] PAGE_NEXT, [6] PAGE_PREV,
-		//   [7] MAP???2, [8] SITE noop, [10] HELP alt.
 		bool advance = false;
 		bool prev = false;
 		bool redraw = false;
@@ -3144,8 +2909,6 @@ bool EEMEngine::moreInfo(const byte *gd, uint suspectIdx,
 						break;
 					}
 					if (isLondon() && kPdaHelpRect.contains(mx, my)) {
-						// EEM2 gallery slot 1 ??? MAP (dedicated map button);
-						// only kPdaHelp2Rect (267,174) stays help in London.
 						_nextScreen = kScreenMapAlt;
 						exitGallery = true;
 						back = true;
@@ -3200,8 +2963,7 @@ bool EEMEngine::moreInfo(const byte *gd, uint suspectIdx,
 					break;
 				}
 			}
-			// _GizmoColorCycle @ 1c33:0002 ??? `MoreInfo` (158f:0480) rotates
-			// 0x6f..0x73 each tick.
+
 			const uint32 now = g_system->getMillis();
 			if (now - gizmoLastTick >= kChooserCycleMillis) {
 				gizmoLastTick = now;
@@ -3226,10 +2988,6 @@ bool EEMEngine::moreInfo(const byte *gd, uint suspectIdx,
 void EEMEngine::drawGalleryFrame(const byte *gd, uint8 numSuspects,
 								  Common::Array<Common::Rect> &slotRects,
 								  Common::Array<int> &slotSuspect) {
-	// Gallery redraw ??? formerly the `drawFrame` lambda inside `doGallery`.
-	// Mirrors `_DrawGallery @ 158f:0046`: PIC 0x3f frame + partner sprite
-	// at (5, 0x50) + suspect portraits in their `_NewOrder` slots. Slot
-	// positions live in `kGallerySlots` in this file's anon namespace.
 	Picture galBg;
 	const bool haveBg = _picsArchive.getPicture(0x3f, galBg);
 
@@ -3240,21 +2998,9 @@ void EEMEngine::drawGalleryFrame(const byte *gd, uint8 numSuspects,
 	if (haveBg)
 		scratch.simpleBlitFrom(galBg.surface);
 
-	// Partner sprite frame @ (5, 0x50). `_DoGallery @ 158f:065b` registers
-	// _NewAnimation(..., CONCAT22(2, ...), ...) ??? script key 0x02 regardless
-	// of partner. Jake's 26-frame 0x02 script (brief wave + long hold +
-	// second wave) drives BOTH partners' cells (Jenny's own 0x10 anim is
-	// only 9 frames so it wouldn't cover the full cadence).
 	blitPdaPartner(scratch, _aniArchive, _partner, kPdaGalleryPartner,
 				   g_system->getMillis());
 
-	// Portraits ??? `_DrawGallery @ 158f:0046` (CD) /
-	// `_DrawGallery_Floppy @ 154e:0045` (floppy) walks suspects 0..N-1
-	// and only renders those flagged in `_InGallery[NewOrder[i]]`.
-	// Layout differs by variant:
-	//   * CD: fixed 0x46-byte stride, slot positions at `kGallerySlots`.
-	//   * Floppy: variable-stride entries (5 + entry[4] bytes per
-	//     suspect), slot positions at `kFloppyGallerySlots` (`2608:0x16c`).
 	const bool floppy = isFloppy();
 	const GallerySlot * const slots =
 		floppy ? kFloppyGallerySlots : kGallerySlots;
@@ -3317,16 +3063,16 @@ void EEMEngine::drawGalleryFrame(const byte *gd, uint8 numSuspects,
 	g_system->updateScreen();
 }
 
+// `_DoBigMap @ 20fe:09e7` two stage:
+//   Stage 1 (Overview): PIC 0x42 + site icons at MapData[+4/+6]
+//     (`_DrawBigMapButtons @ 20fe:0877`). Click in BigMapWindow
+//     returns scroll (mouseX*2 - 0x74, mouseY*2 - 0x55).
+//   Stage 2 (Detail): PIC 0x43 frame + 0xe9??0xab BIGMAP.PIC viewport
+//     at (2,2). Icons stamped at MapData[+8/+0xa] (`_StampButtons @
+//     20fe:0d2f`). Click icon = travel.
+// MapData entry (14 bytes): +0..3 ???, +4 BigMapX, +6 BigMapY,
+//   +8 SmallMapX, +0xa SmallMapY, +0xc crime-flag.
 void EEMEngine::doBigMap() {
-	// `_DoBigMap @ 20fe:09e7` two stage:
-	//   Stage 1 (Overview): PIC 0x42 + site icons at MapData[+4/+6]
-	//     (`_DrawBigMapButtons @ 20fe:0877`). Click in BigMapWindow
-	//     returns scroll (mouseX*2 - 0x74, mouseY*2 - 0x55).
-	//   Stage 2 (Detail): PIC 0x43 frame + 0xe9??0xab BIGMAP.PIC viewport
-	//     at (2,2). Icons stamped at MapData[+8/+0xa] (`_StampButtons @
-	//     20fe:0d2f`). Click icon = travel.
-	// MapData entry (14 bytes): +0..3 ???, +4 BigMapX, +6 BigMapY,
-	//   +8 SmallMapX, +0xa SmallMapY, +0xc crime-flag.
 
 	if (!_mystery.isLoaded())
 		return;
@@ -3341,18 +3087,12 @@ void EEMEngine::doBigMap() {
 
 	while (!shouldQuit()) {
 		setInteractiveMouseCursor(false);
-		// `_GetPalette(0x24)` @ EEM1 `_DoBigMap`; EEM2 `_DoBigMap`
-		// @ 2237:0a04 uses `_GetPalette(0x3b)` (shifted UI palettes).
 		setSitePalette(isLondon() ? 0x3b : 0x24);
 
-		// Stage 1: Overview. mapStartTick anchors partner timeline;
-		// `_NewAnimation` seeds frame to 0xffff so unfold plays once then
-		// loops the wait sequence.
 		const uint32 mapStartTick = g_system->getMillis();
 		drawBigMapOverview(0);
 		uint32 mapLastTick = mapStartTick;
 
-		// Rects @ CD 29be:1596 / floppy 2608:13fe (1 px diff on Setup).
 		const Common::Rect kBigMapWindow(0, 0, 247, 192);
 		const Common::Rect kSetupBtnRect = isFloppy()
 			? Common::Rect(251, 3, 315, 42)
@@ -3374,12 +3114,11 @@ void EEMEngine::doBigMap() {
 					continue;
 				}
 				if (ev.type == Common::EVENT_LBUTTONDOWN) {
-					// Setup ??? NextScreen=6 (`_DoBigMap @ 20fe:0c33`).
 					if (kSetupBtnRect.contains(ev.mouse.x, ev.mouse.y)) {
 						_nextScreen = kScreenSetup;
 						return;
 					}
-					// BigMapWindow click ??? zoom (sx=x*2-0x74, sy=y*2-0x55).
+
 					if (kBigMapWindow.contains(ev.mouse.x, ev.mouse.y)) {
 						int sx = ev.mouse.x * 2;
 						int sy = ev.mouse.y * 2;
@@ -3395,7 +3134,6 @@ void EEMEngine::doBigMap() {
 			if (wantZoom)
 				break;
 
-			// `_CheckFrameRate` cadence ??? 100 ms.
 			const uint32 now = g_system->getMillis();
 			if (now - mapLastTick >= 100) {
 				mapLastTick = now;
@@ -3415,7 +3153,6 @@ void EEMEngine::doBigMap() {
 		if (!wantZoom)
 			return;
 
-		// Stage 2: Detail. PIC 0x43 + BIGMAP.PIC viewport.
 		Common::File f;
 		if (!f.open(Common::Path("BIGMAP.PIC"))) {
 			warning("doBigMap: BIGMAP.PIC missing for detail view");
@@ -3439,13 +3176,11 @@ void EEMEngine::doBigMap() {
 		int scrollX = MAX<int>(0, MIN<int>(mapW - kMapWinW, zoomX));
 		int scrollY = MAX<int>(0, MIN<int>(mapH - kMapWinH, zoomY));
 
-		// Detail partner timeline (script 0x13 unfold, then wait seq).
 		const uint32 detailStartTick = g_system->getMillis();
 		drawBigMapDetail(scrollX, scrollY, mapPixels, mapW, mapH, 0);
 		uint32 detailLastTick = detailStartTick;
 		bool returnToOverview = false;
 
-		// `SmallMapButtons[4]` @ 20fe:156c ??? return to overview.
 		const Common::Rect kBigMapReturnRect(252, 43, kScreenWidth, kScreenHeight);
 		const Common::Rect kArrowYUp(237, 2, 247, 11);
 		const Common::Rect kArrowYDown(237, 163, 247, 172);
@@ -3512,7 +3247,6 @@ void EEMEngine::doBigMap() {
 						kBigMapReturnRect.contains(ev.mouse.x, ev.mouse.y) ||
 						kDetailSetupBtn.contains(ev.mouse.x, ev.mouse.y));
 					if (kDetailSetupBtn.contains(ev.mouse.x, ev.mouse.y)) {
-						// `_DoMapScreen @ 20fe:1560` ??? NextScreen=6.
 						_nextScreen = kScreenSetup;
 						setInteractiveMouseCursor(false);
 						return;
@@ -3572,8 +3306,6 @@ void EEMEngine::doBigMap() {
 							uint16 my;
 							uint16 buttonId;
 							if (fmap) {
-								// Floppy: rect +0/+2; BUTTON.DBD id at +4
-								// (`FUN_1fed_0c3e @ 1fed:0c3e`).
 								mx = READ_LE_UINT16(entry + 0x0);
 								my = READ_LE_UINT16(entry + 0x2);
 								buttonId = (uint16)entry[0x4];
@@ -3618,7 +3350,6 @@ void EEMEngine::doBigMap() {
 			if (returnToOverview)
 				break;
 
-			// 100 ms partner cycle.
 			const uint32 now = g_system->getMillis();
 			if (now - detailLastTick >= 100) {
 				detailLastTick = now;
@@ -3685,12 +3416,6 @@ bool EEMEngine::doLondonApproach(uint16 approachId) {
 				const int lineH = _font.getFontHeight();
 				const int maxLines = MAX<int>(1, textRect.height() / lineH);
 				for (uint i = 0; i < wrapped.size() && (int)i < maxLines; i++) {
-					// `_DoApproach @ 1717:029e`:
-					// `_WordWrap(x, y, w, page, fontColor=1, dropColor=-1)`.
-					// Font colour is palette index 1 (NOT 0x0F white), drawn
-					// straight onto the restored video-frame background ??? the
-					// DOS `vga_fvidvid` calls just restore the clean bg, they
-					// don't paint a panel, so don't fill the rect here either.
 					_font.drawString(&scratch, wrapped[i], textRect.left,
 									 textRect.top + (int)i * lineH,
 									 textRect.width(), 1);
@@ -3776,9 +3501,6 @@ bool EEMEngine::doLondonApproach(uint16 approachId) {
 	else
 		setSitePalette(0x3b);
 	bool done = false;
-	// `_DoApproach` idle loop (1717:02d4) shimmers palette 0xF3..0xF7 via
-	// `_ColorCycle(0xf3, 0xf7)` each frame-rate tick; mirror it on the video
-	// palette (the diff-animation ramp those entries belong to).
 	uint32 lastShimmer = g_system->getMillis();
 	while (!shouldQuit() && !done) {
 		Common::Event ev;
@@ -3866,9 +3588,6 @@ bool EEMEngine::doLondonApproach(uint16 approachId) {
 }
 
 void EEMEngine::drawBigMapOverview(uint32 elapsedMs) {
-	// PIC 0x42 + per-site Done/Crime/Site marker (`_DrawBigMapButtons @
-	// 20fe:0877`) + partner idle at (0xfd, 0x50). Idle ANI: Jake=0x14,
-	// Jenny=0x12. elapsedMs anchors unfold???wait timeline.
 	Graphics::ManagedSurface scratch(kScreenWidth, kScreenHeight,
 		Graphics::PixelFormat::createFormatCLUT8());
 	scratch.clear();
@@ -3880,7 +3599,6 @@ void EEMEngine::drawBigMapOverview(uint32 elapsedMs) {
 	// Marker PICs from `_main`:
 	//   EEM1 CD @ 1a35:0f59: Done=0x20d, Site=0xc5, Crime=0xc6.
 	//   EEM2 CD @ 1abf:11a6: Done=0x006, Site=0xc5, Crime=0xc6.
-	// In EEM2, 0x20d is a normal scene/character frame, not a map marker.
 	Picture done;
 	Picture normal;
 	Picture crimeM;
@@ -3894,13 +3612,7 @@ void EEMEngine::drawBigMapOverview(uint32 elapsedMs) {
 		const byte *entry = _mystery.mapEntry(i);
 		if (!entry)
 			continue;
-		// CD entries are 14 bytes: X at +4, Y at +6, crime at +12.
-		// Floppy entries are 11 bytes: X at +6, Y at +8, recolor at +10.
-		// Floppy layout: `FUN_1fed_07ed` (BigMap iteration):
-		//   `*(int *)(pcVar2 + i*0xb + 7)` (= entry+6, X u16)
-		//   `*(int *)(pcVar2 + i*0xb + 9)` (= entry+8, Y u16)
-		//   `pcVar2[i*0xb + 0xb]` (= entry+10, recolor flag ??? non-zero
-		//   selects the crime-marker PIC over the regular site marker).
+
 		const bool floppy  = _mystery.isLoaded() && isFloppy();
 		const uint16 mx    = floppy ? READ_LE_UINT16(entry + 0x6)
 									: READ_LE_UINT16(entry + 0x4);
@@ -3933,14 +3645,12 @@ void EEMEngine::drawBigMapOverview(uint32 elapsedMs) {
 		}
 	}
 
-	// Partner idle at (0xfd, 0x50). `_DoBigMap @ 20fe:0a47` always passes
-	// script 0x14 (count-up) to `_NewAnimation` regardless of partner.
 	const uint kMapAniId = (_partner == kPartnerJake) ? 0x14 : 0x12;
 	Animation mapAnim;
 	if (_aniArchive.loadAnimation(kMapAniId, mapAnim) && !mapAnim.empty()) {
 		const uint frameIdx = bigMapPartnerFrameAtTick((uint)mapAnim.size(),
 													   elapsedMs);
-		// BigMap walk-cycle miscflags = -2 (anchor shift left).
+
 		blitAnimFrameAnchored(scratch.surfacePtr(), mapAnim[frameIdx],
 							  0xfd, 0x50);
 	}
@@ -3954,9 +3664,7 @@ void EEMEngine::drawBigMapDetail(int scrollX, int scrollY,
 								 const Common::Array<byte> &mapPixels,
 								 uint16 mapW, uint16 mapH,
 								 uint32 elapsedMs) {
-	// PIC 0x43 + 0xe9??0xab BIGMAP.PIC viewport at (2,2), stamped buttons,
-	// partner at (0x101, 0x50). `_DoMapScreen @ 20fe:120b` (ANI 0x13 Jake
-	// / 0x11 Jenny, seq 0x13). elapsedMs anchors unfold???wait.
+
 	const int kMapWinW = 0xe9;
 	const int kMapWinH = 0xab;
 	const int kMapWinX = 2;
@@ -3975,8 +3683,6 @@ void EEMEngine::drawBigMapDetail(int scrollX, int scrollY,
 	scratch.copyRectToSurface(mapPixels.data() + scrollY * mapW + scrollX,
 							  mapW, kMapWinX, kMapWinY, copyW, copyH);
 
-	// `_StampButtons @ 20fe:0d2f` (CD): button=MapData[+0], dst=+8/+0xa.
-	// Floppy `FUN_1fed_0c3e @ 1fed:0c3e`: button.dbd id at +4, rect +0/+2.
 	const bool floppyMap = _mystery.isLoaded() && isFloppy();
 	for (uint i = 0; i < _mystery.numSites(); i++) {
 		if (!_mystery._onSites[i] && i != _mystery._siteNumber)
@@ -4015,7 +3721,6 @@ void EEMEngine::drawBigMapDetail(int scrollX, int scrollY,
 		}
 	}
 
-	// Always script 0x13 (`_NewAnimation @ _DoBigMap 20fe:0a47`).
 	const uint kDetailAniId = (_partner == kPartnerJake) ? 0x13 : 0x11;
 	Animation detailAnim;
 	if (_aniArchive.loadAnimation(kDetailAniId, detailAnim) &&
@@ -4031,14 +3736,8 @@ void EEMEngine::drawBigMapDetail(int scrollX, int scrollY,
 	g_system->updateScreen();
 }
 
+// `_GetKDTextBalloon @ 1df2:0105`
 uint16 EEMEngine::getKDTextBalloon(byte firstChar) const {
-	// `_GetKDTextBalloon @ 1df2:0105`:
-	//   if ((ctype[firstChar] & 2) == 0)  bub = *(u16*)29be:1068 = 0x17
-	//   else                              bub = *(u16*)(29be:0fe6+0x1e+c*2)
-	// `ctype` is Borland's `_ctype_` at 29be:2be1; bit 1 (0x02) is set
-	// only for '0'..'9'. Digit table @ 29be:1064 (see kDigitBalloons).
-	// `*(u16*)29be:1068` = entry for '2' = 0x17 ??? original reuses the
-	// digit-2 slot as the non-digit fallback constant.
 	if (firstChar < '0' || firstChar > '9')
 		return 0x17;
 	return kDigitBalloons[firstChar - '0'];
@@ -4095,12 +3794,9 @@ void EEMEngine::accuseDrawScreen(const AccuseNotesCtx &ctx) {
 	if (ctx.haveBg)
 		scratch.simpleBlitFrom(ctx.accuseBg->surface);
 
-	// Partner at (5, 0x50). `_DoAccuse @ 1df2:0c2c`: ANI 2/0x10,
-	// script 2, prior 1.
 	blitPdaPartner(scratch, _aniArchive, _partner, kPdaGalleryPartner,
 				   g_system->getMillis());
 
-	// Selected=0x3c, unselected=1 (`_NoteUnselectedColor` @ 1df2:0c25).
 	Common::Array<Common::Rect> &slotRects = *ctx.slotRects;
 	Common::Array<uint> &slotClues = *ctx.slotClues;
 	const Common::Array<uint> &found = *ctx.found;
@@ -4139,7 +3835,6 @@ void EEMEngine::accuseDrawScreen(const AccuseNotesCtx &ctx) {
 		y += h + 7;
 	}
 
-	// `_UpdateSelectionCount(remaining)` @ (0xd1, 0xb).
 	const uint remaining = (selectedCount < ctx.expected)
 		? ctx.expected - selectedCount
 		: 0;
@@ -4163,12 +3858,6 @@ void EEMEngine::accuseDrawScreen(const AccuseNotesCtx &ctx) {
 }
 
 bool EEMEngine::doAccuseNotes() {
-	// `_DoAccuse @ 1df2:0bdd` (EEM2 `@ 1ea1:0c03`) head. BG PIC 0x1A7.
-	// `_AccuseNoteRect @ 29be:1048` = (79, 27, 304, 159). Counter @ (209, 11)
-	// shows the required clue count (EEM1 `6 - chainStage` = 5/4/3 by tier;
-	// London a flat 5 ??? see `expected` below). Unselected color 1 (red),
-	// selected 0x3c. Click toggles selection; `_NoteButtons[4]`
-	// (180,174,201,190) SOLVE ??? `_HandleAccuseNoteButton` returns 2.
 	if (!_mystery.isLoaded() || !_font.isLoaded())
 		return false;
 	const byte *ni = _mystery.noteIndex();
@@ -4179,18 +3868,13 @@ bool EEMEngine::doAccuseNotes() {
 	Picture accuseBg;
 	const bool haveBg = _picsArchive.getPicture(0x1a7, accuseBg);
 
-	// `FUN_1d40_0e07 @ 1d40:0e34` zeroes _NoteSelected_Floppy on entry.
 	memset(_mystery._noteSelected, 0, sizeof(_mystery._noteSelected));
 
-	// EEM1 `_DoAccuse @ 1df2:0bdd` scales the required clue count by tier
-	// (`6 - chainStage`: stage 1=5, 2=4, 3=3). London `_DoAccuse @ 1ea1:0c03`
-	// hardcodes 5 (`local_a = 5`) for both books.
 	const uint expected = isLondon()
 		? 5
 		: ((_chainStage >= 1 && _chainStage <= 3) ? (uint)(6 - _chainStage)
 												  : 5);
 
-	// `_DrawNotes(NULL, 100, ...)` walks `_CluesFound[]`.
 	Common::Array<uint> found;
 	for (uint i = 0; i < niCount && i < Mystery::kCluesFoundCap; i++) {
 		if (_mystery._cluesFound[i] && _mystery.noteHasNotebookText(i))
@@ -4202,8 +3886,6 @@ bool EEMEngine::doAccuseNotes() {
 	const int rectW = 304 - 79;
 	const int rectH = 159 - 27;
 
-	// `_NoteButtons` @ 29be:0147. `_HandleAccuseNoteButton @ 1df2:0990`
-	// returns DI=2 for i==4 (SOLVE).
 	const Common::Rect kBtnSolve   (180, 174, 201, 190); // [4]
 	const Common::Rect kBtnPageNext(204, 174, 224, 190); // [5]
 	const Common::Rect kBtnPagePrev(226, 174, 247, 190); // [6]
@@ -4218,9 +3900,6 @@ bool EEMEngine::doAccuseNotes() {
 	int numPages = 1;
 	pageBreaks[0] = 0;
 
-	// CD note: 4 bytes (u16 textOff rel TextBlock, u16 score).
-	// Floppy: 7 bytes (+0 abs textOff, +2 Jake, +4 Jenny, +6 score).
-	// Notebook always uses +0 (`FUN_15e0_01e8`).
 	const bool floppyNote = isFloppy();
 	const byte *bufBaseNotes = _mystery.blobAt(0);
 
@@ -4302,9 +3981,6 @@ bool EEMEngine::doAccuseNotes() {
 					return false;
 				}
 				if (isLondon() && kPdaHelpRect.contains(mx, my)) {
-					// EEM2 accuse `_HandleAccuseNoteButton @ 1ea1:0873` slot 1
-					// ??? MAP (`MOV [0x9292],2` @ 1ea1:089b) ??? London's dedicated
-					// map button; only kPdaHelp2Rect (267,174) stays help.
 					_nextScreen = kScreenMapAlt;
 					return false;
 				}
@@ -4315,7 +3991,6 @@ bool EEMEngine::doAccuseNotes() {
 					dirty = true;
 					continue;
 				}
-				// `_NoteButtons[5]/[6]` ??? page nav guards @ 1df2:09a8/099e.
 				if (kBtnPageNext.contains(mx, my)) {
 					if (page + 1 < numPages) {
 						page++;
@@ -4330,7 +4005,6 @@ bool EEMEngine::doAccuseNotes() {
 					}
 					continue;
 				}
-				// `_NoteButtons[3]` ??? ScummVM hint, original ignores.
 				if (kBtnPartner.contains(mx, my)) {
 					doHelp();
 					dirty = true;
@@ -4343,7 +4017,6 @@ bool EEMEngine::doAccuseNotes() {
 							selected++;
 					}
 					if (selected == expected) {
-						// `_DoAccuse` gate `uStack_8 == uStack_a`.
 						return true;
 					}
 					continue;
@@ -4371,7 +4044,6 @@ bool EEMEngine::doAccuseNotes() {
 		}
 		if (dirty)
 			accuseDrawScreen(ctx);
-		// 100 ms `_CheckFrameRate` cadence @ 1df2:0bfa.
 		static uint32 sLastTick = 0;
 		const uint32 now = g_system->getMillis();
 		if (now - sLastTick >= 100) {
@@ -4388,17 +4060,11 @@ void EEMEngine::doAccuse() {
 	if (!_mystery.isLoaded() || !_font.isLoaded())
 		return;
 
-	// Floppy: variable-stride entries (5 + nameLen, not 0x46), top-5
-	// `_TextSeen` scoring (not `_NoteSelected`), header[+0x12] win-dialog.
-	// Handled by doAccuseFloppy (`FUN_1d40_11fd`, `FUN_1d40_0c79`,
-	// `_DisplayCorrect_Floppy`, `_DisplayAlibi_Floppy`).
 	if (isFloppy()) {
 		doAccuseFloppy();
 		return;
 	}
 
-	// `_AccuseEntry @ 1df2:0ff8` ??? gates picker on top-5 found clue
-	// score (???0x65 required).
 	const byte *entryKdIdx = _mystery.kdTextIndex();
 	if (!entryKdIdx)
 		return;
@@ -4567,8 +4233,6 @@ void EEMEngine::doAccuse() {
 			}
 		}
 
-		// Balloon overlay (`_GetKDTextBalloon` + `_GetBalloon` +
-		// `_AddPicBackground` + `_WordWrap` @ 1df2:0c8d-0cd1).
 		Graphics::ManagedSurface ms(kScreenWidth, kScreenHeight,
 			Graphics::PixelFormat::createFormatCLUT8());
 		ms.clear();
@@ -4580,7 +4244,7 @@ void EEMEngine::doAccuse() {
 		const byte firstChar =
 			hint.empty() ? (byte)0 : (byte)hint[0];
 		uint16 bubNum = getKDTextBalloon(firstChar);
-		// Strip digit prefix (`_DisplayAlibi @ 1df2:0163` `str=pbVar7+1`).
+
 		if (firstChar >= '0' && firstChar <= '9')
 			hint.deleteChar(0);
 		bubNum = fitBalloonToText(bubNum, hint);
@@ -4609,12 +4273,11 @@ void EEMEngine::doAccuse() {
 								   0, 0, kScreenWidth, kScreenHeight);
 		g_system->updateScreen();
 
-		// `_SayKDDigital(3)` @ 1df2:0cd9.
+
 		if (_audio && kdIdx)
 			_audio->sayKDDigital(kdIdx, 3, _partner);
 
 		waitForInput(20000);
-		// `_DoAccuse @ 1df2:0ce5` ??? NextScreen = LastScreen.
 		_nextScreen = _lastScreen != kScreenInvalid
 						? (ScreenId)_lastScreen : kScreenSite;
 		return;
@@ -4632,9 +4295,6 @@ void EEMEngine::doAccuse() {
 
 	int highlighted = 0;
 
-	// KD hint balloon @ `_DoAccuseGallery @ 1df2:0a31` (0a4c-0afe).
-	// y = (h < 0x4e) ? (0x50-h)>>1 : 1. Inset table @ 29be:0875.
-	// `_SayKDDigital(4)`.
 	const byte *kdIdx = _mystery.kdTextIndex();
 	if (kdIdx) {
 		const int16 textOff = (int16)READ_LE_UINT16(kdIdx + 8);
@@ -4677,14 +4337,12 @@ void EEMEngine::doAccuse() {
 						ms.simpleBlitFrom(accuseBg.surface);
 					}
 				}
-				// `_Rect_Move_Mask` (1000:03fc) ??? transp = pic[0]>>8.
 				if (haveBalloon) {
 					const byte transp = (byte)(balloon.flags >> 8);
 					ms.transBlitFrom(balloon.surface,
 									 Common::Point(balloonX, balloonY),
 									 transp);
 				}
-				// Inset table @ 29be:0875.
 				uint16 tx = 5;
 				uint16 ty = 4;
 				uint16 tw = 155;
@@ -4710,20 +4368,6 @@ void EEMEngine::doAccuse() {
 
 	drawAccuseGallery(num, gd, highlighted, slotRects, slotSuspect);
 
-	// Wait-for-pick loop. Mirrors `_DoAccuseGallery` 1df2:0b26-1df2:0bc8:
-	//   * `_CheckFrameRate` + `_UpdateAnimations` per tick (1df2:0b2a-0b33)
-	//   * 5-entry input dispatch table @ 1df2:0bc9:
-	//       0x09 (TAB)   ??? handler 0x0b94 (cycle highlight)
-	//       0x0d (Enter) ??? handler 0x0b72 (pick = _SearchSuspects)
-	//       0x4b (LEFT)  ??? handler 0x0b94
-	//       0x4d (RIGHT) ??? handler 0x0b94
-	//       0xFFFF (mb)  ??? handler 0x0b72
-	//   * 0x0b94: `INC DI` + wraparound + `_PutMouseInRect(&Guys[DI])`,
-	//     i.e. advance highlight and warp cursor (1df2:0b94-0bb1).
-	//   * 0x0b72: `_SearchSuspects` (158f:0584) ??? mouse-rect hit-test;
-	//     if non-0xFFFF, pick that suspect.
-	// We don't warp the cursor (unfriendly under SDL); instead the
-	// highlight is drawn as a 1px outline and Enter picks it.
 	int picked = -1;
 	uint32 lastTick = g_system->getMillis();
 	bool dirty = false;
@@ -4745,9 +4389,6 @@ void EEMEngine::doAccuse() {
 					dirty = true;
 					break;
 				case Common::KEYCODE_LEFT:
-					// 1df2:0b94 increments DI for LEFT too ??? but a
-					// keyboard-driven UX is friendlier with separate
-					// directions, so we mirror Right=+1 / Left=-1.
 					highlighted = nextLiveSlot(slotRects, highlighted, -1);
 					dirty = true;
 					break;
@@ -4782,7 +4423,6 @@ void EEMEngine::doAccuse() {
 				}
 			}
 		}
-		// 100 ms `_CheckFrameRate` @ 1df2:0b33.
 		const uint32 now = g_system->getMillis();
 		if (dirty || now - lastTick >= 100) {
 			drawAccuseGallery(num, gd, highlighted, slotRects, slotSuspect);
@@ -4795,8 +4435,6 @@ void EEMEngine::doAccuse() {
 	if (picked < 0)
 		return;
 
-	// `_WITCH @ 1df2:089f` ??? guilty when gd[picked*0x46+0x02] == 0xFFFF;
-	// innocents store an alibi TextBlock offset there.
 	const int points          = _mystery.selectedPoints();
 	const bool pickedGuilty   = _mystery.isGuilty((uint)picked);
 	const bool guessedRight   = pickedGuilty;
@@ -4815,7 +4453,6 @@ void EEMEngine::doAccuse() {
 	//   4. Partner reaction balloon @ KDTextIndex[+10], `_SayKDDigital(5)`.
 	//   5. _FirstTry = 0; NextScreen = LastScreen (1df2:043f).
 	if (!guessedRight) {
-		// Balloon-shape table @ 29be:1050.
 		static const uint16 kAlibiBubbles[16] = {
 			0x002B, 0x002C, 0x002D, 0x002E,
 			0x00AB, 0x00AC, 0x00AD, 0x00AE,
@@ -4830,7 +4467,6 @@ void EEMEngine::doAccuse() {
 			if (raw)
 				alibi = parseString(raw, _playerName, _partner);
 		}
-		// `_DisplayAlibi @ 1df2:0163` ??? bindx = digit prefix, else 2.
 		uint bindx = 2;
 		const byte firstChar = alibi.empty() ? (byte)0 : (byte)alibi[0];
 		if (firstChar >= '0' && firstChar <= '9') {
@@ -4855,7 +4491,6 @@ void EEMEngine::doAccuse() {
 			_balloonArchive.size() > (bubNum & 0x7F) &&
 			_balloonArchive.loadEntry(bubNum & 0x7F, balloon);
 
-		// Position math @ 1df2:01a4-0207.
 		int balloonX = 0x21;
 		int balloonY = 1;
 		int py = 0x5a;
@@ -4875,7 +4510,6 @@ void EEMEngine::doAccuse() {
 			balloonY = (bh < 0x4f) ? (0x50 - bh) / 2 : 1;
 		}
 
-		// `base` = BG + suspect + partner. Survives both balloon phases.
 		Graphics::ManagedSurface base(kScreenWidth, kScreenHeight,
 			Graphics::PixelFormat::createFormatCLUT8());
 		base.clear();
@@ -4887,10 +4521,7 @@ void EEMEngine::doAccuse() {
 							   Common::Point(0x82, py),
 							   (uint32)transp);
 		}
-		// Partner at (5, 0x50). ANI 2/0x10, script 0x02 (`_DoAccuse
-		// @ 1df2:0c30`). Drawn after suspect.
 
-		// scratch = base + alibi balloon/text + partner.
 		Graphics::ManagedSurface scratch(kScreenWidth, kScreenHeight,
 			Graphics::PixelFormat::createFormatCLUT8());
 		scratch.simpleBlitFrom(base);
@@ -4900,7 +4531,7 @@ void EEMEngine::doAccuse() {
 								  Common::Point(balloonX, balloonY),
 								  (uint32)transp);
 		}
-		// Inset table @ 29be:0875. Color 0 inside balloon (1df2:0240).
+
 		uint16 tx = 5, ty = 4, tw = 155;
 		getBalloonInsets(bubNum, tx, ty, tw);
 		if (_font.isLoaded() && !alibi.empty()) {
@@ -4911,7 +4542,6 @@ void EEMEngine::doAccuse() {
 		blitPdaPartner(scratch, _aniArchive, _partner, kPdaGalleryPartner,
 					   g_system->getMillis());
 
-		// MIDI 6 ??? blocks until done (or click/ESC aborts).
 		if (_music && _voiceOn) {
 			_music->playMus(6, /* loop= */ false);
 			const uint32 musStart = g_system->getMillis();
@@ -4930,7 +4560,6 @@ void EEMEngine::doAccuse() {
 						break;
 					}
 				}
-				// Hard cap if MIDI never reports finish.
 				if (g_system->getMillis() - musStart > 10000)
 					break;
 				g_system->updateScreen();
@@ -4939,8 +4568,6 @@ void EEMEngine::doAccuse() {
 			_music->stop();
 		}
 
-		// Suspect voice. talk = partner==0 ? gd[+0x6] : gd[+0x0] (1df2:0252).
-		// 1-based, so SpoolSound(talk-1).
 		g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
 								   0, 0, kScreenWidth, kScreenHeight);
 		g_system->updateScreen();
@@ -4956,8 +4583,6 @@ void EEMEngine::doAccuse() {
 		}
 		waitForInput(60000);
 
-		// Partner reaction @ 1df2:026e-02b6. Rebuild from `base` so alibi
-		// balloon clears. `_SayKDDigital(5)` auto-cancels alibi voice.
 		const byte *reactIdx = _mystery.kdTextIndex();
 		if (reactIdx) {
 			const int16 reactOff = (int16)READ_LE_UINT16(reactIdx + 10);
@@ -5008,35 +4633,25 @@ void EEMEngine::doAccuse() {
 		waitForInput(60000);
 
 		_mystery._firstTry = false;
-		// `_DisplayAlibi @ 1df2:043f` ??? NextScreen = LastScreen.
 		_nextScreen = _lastScreen != kScreenInvalid
 						? (ScreenId)_lastScreen : kScreenSite;
 		return;
 	}
 
-	// Win ??? `_DisplayCorrect @ 1df2:073c`: mark solved, advance chain,
-	// MIDI 5, SCRAPBK.ANI, ending, save, NextScreen=0xc (1df2:0895).
 	{
 		const uint mn = _mystery.number();
 		if (mn < sizeof(_mysteriesSolved)) {
 			_mysteriesSolved[mn] = _mystery._firstTry ? 2 : 1;
 		}
 
-		// Chain advance @ 1df2:0824-0850. Skip mystery 0 (practice).
-		// stage 1: 1..0x18, stage 2: 0x19..0x30, stage 3: 0x31..0x36.
 		advanceChainStageAfterSolve(mn);
 
-		// `_DisplayCorrect @ 1df2:073c`:
-		//   _AllBlack; _BuildBackground(5, 0x42, 0x14); _FadeIn;
-		//   _MIDIPlay(5); _DisplayClue(MysteryIndex[+0x10]).
-		// `_BuildBackground @ 172b:13e2` = PIC 0x3D + SITES entry 5 at
-		// (0x42, 0x14), palette = sitenum+1 = 6.
 		Graphics::Surface *blk = g_system->lockScreen();
 		if (blk) {
 			memset(blk->getPixels(), 0, kScreenWidth * kScreenHeight);
 			g_system->unlockScreen();
 		}
-		setSitePalette(6); // sitenum + 1 (`_GetPalette`).
+		setSitePalette(6);
 		Picture frame, scene;
 		if (_picsArchive.loadEntry(0x3d, frame)) {
 			g_system->copyRectToScreen(frame.surface.getPixels(),
@@ -5054,7 +4669,6 @@ void EEMEngine::doAccuse() {
 										   sw, sh);
 		}
 
-		// Stamp partner at (5, 0x50); displayClue snapshots screen.
 		if (Graphics::Surface *screen = g_system->lockScreen()) {
 			blitPdaPartner(screen, _aniArchive, _partner,
 						   kPdaGalleryPartner, g_system->getMillis());
@@ -5065,39 +4679,29 @@ void EEMEngine::doAccuse() {
 		if (_music && _voiceOn)
 			_music->playMus(5, /* loop= */ false);
 
-		// Chain recap ??? partner enumerates required clues.
 		const byte *solved = _mystery.solvedClueBlock();
 		if (solved)
 			displayClue(solved);
 		if (_music && _voiceOn)
 			_music->stop();
 
-		// `_DifferenceAnimation("scrapbk.ani")` @ 1df2:0848.
 		playAnm(Common::Path("SCRAPBK.ANI"), 120,
 				/* holdLastFrame= */ false);
 
 		displayScrapbookExtra(mn);
 
-		// `_ShowOneScrap @ 1f78:0773` = `_DisplayEnding(num, 1)`.
 		doShowEnding(mn);
 
-		// `_SavePlayerRecord` @ 1df2:0857. Order: clear() before save
-		// so hasMystery=false (matches `_DeleteSavedGame` @ 1df2:0851).
 		_mystery.clear();
 		const Common::Error err = saveProfile(_playerName);
 		if (err.getCode() != Common::kNoError)
 			warning("saveProfile after solve failed: %s",
 					err.getDesc().c_str());
 
-		// `_DisplayCorrect @ 1df2:0895` ??? NextScreen = 0xc.
 		_nextScreen = kScreenAction;
 	}
 }
 
-// Render one KDTextIndex string as a centred KD balloon over the current
-// screen, mirroring `_DisplayHint_Floppy @ 1503:00ca` (= `FUN_1d40_11fd`'s
-// body). `kdSlot` is the index into KDTextIndex (0..N) ??? entries are u16
-// absolute text offsets.
 void EEMEngine::floppyKDHint(uint kdSlot, const byte *kdIdx,
 							 const byte *bufBase, uint32 mysSize) {
 	if ((uint)(kdSlot * 2) + 2 > (uint)(mysSize - (kdIdx - bufBase)))
@@ -5112,8 +4716,6 @@ void EEMEngine::floppyKDHint(uint kdSlot, const byte *kdIdx,
 	if (lineLen == 0)
 		return;
 	Common::String raw(p, lineLen);
-	// Digit ??? balloon variant (`_GetKDTextBalloon_Floppy @ 1d40:009f`
-	// + table @ 2608:0c14).
 	static const uint8 kDigitToBalloon[10] = {
 		0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1c, 0x1d, 0x1e, 0x0a
 	};
@@ -5153,7 +4755,7 @@ void EEMEngine::floppyKDHint(uint kdSlot, const byte *kdIdx,
 						  MAX<int>(8, (int)bw), text, 0);
 	g_system->copyRectToScreen(ms.getPixels(), ms.pitch, 0, 0, kScreenWidth, kScreenHeight);
 	g_system->updateScreen();
-	// Wait for click.
+
 	while (!shouldQuit()) {
 		Common::Event ev;
 		bool advance = false;
@@ -5347,7 +4949,6 @@ void EEMEngine::accuseDrawGallery(int highlighted,
 		if (!_picsArchive.getPicture(picId, portrait))
 			continue;
 		const GallerySlot &s = kFloppyGallerySlots[phys];
-		// Bottom-align to baseline 0x48 (`154e:00ed`).
 		const int placeX = s.x;
 		const int placeY = s.y + (0x48 - portrait.surface.h);
 		const byte transp = (byte)(portrait.flags >> 8);
@@ -5369,20 +4970,12 @@ void EEMEngine::accuseDrawGallery(int highlighted,
 }
 
 void EEMEngine::doAccuseFloppy() {
-	// Floppy accuse:
-	//   `_KDHelp_Floppy / FUN_1d40_11fd @ 1d40:11fd` ??? score gate.
-	//   `FUN_1d40_0e07` ??? clue selection (skipped; selectedPoints()
-	//     already takes top-5).
-	//   `FUN_1d40_0c79 @ 1d40:0c79` ??? gallery picker.
-	//   `_DisplayCorrect_Floppy @ 1d40:0894`.
-	//   `_DisplayAlibi_Floppy @ 1d40:00df`.
 	const byte *kdIdx     = _mystery.kdTextIndex();
 	const byte *bufBase   = _mystery.blobAt(0);
 	const uint32 mysSize  = _mystery.dataSize();
 	if (!kdIdx || !bufBase)
 		return;
 
-	// `FUN_1d40_11fd` ??? score gate, ready when score >= 100.
 	const int score = _mystery.selectedPoints();
 	uint kdSlot;
 	bool readyToSolve;
@@ -5403,9 +4996,6 @@ void EEMEngine::doAccuseFloppy() {
 		return;
 	}
 
-	// Clue selection ??? `FUN_1d40_0e07 @ 1d40:0e07`. Pick `6 - chainStage`
-	// clues (1d40:0e34: `local_c = 6 - DAT_28da_3052`); London a flat 5.
-	// Count handled inside `doAccuseNotes`.
 	if (!doAccuseNotes()) {
 		if (_nextScreen == kScreenAccuse) {
 			_nextScreen = _lastScreen != kScreenInvalid
@@ -5414,8 +5004,6 @@ void EEMEngine::doAccuseFloppy() {
 		return;
 	}
 
-	// 2nd score gate on USER-PICKED clues (1d40:0f3a, FUN_1d40_0c48 =
-	// sum of byte +6 across selected entries). <100 ??? slot 3 hint.
 	int userSelectedScore = 0;
 	{
 		const byte *ni2 = _mystery.noteIndex();
@@ -5436,10 +5024,7 @@ void EEMEngine::doAccuseFloppy() {
 		return;
 	}
 
-	// `FUN_1d40_0c79` ??? gallery picker. KDTextIndex slot 4 prompt.
 	floppyKDHint(4, kdIdx, bufBase, mysSize);
-
-	// Floppy gallery: byte0 = numSuspects, then variable-stride entries.
 	const uint8 num = _mystery.numSuspects();
 	if (num == 0) {
 		_nextScreen = _lastScreen != kScreenInvalid
@@ -5513,16 +5098,8 @@ void EEMEngine::doAccuseFloppy() {
 	const bool guilty = _mystery.isGuilty((uint)picked);
 
 	if (guilty) {
-		// `_DisplayCorrect_Floppy @ 1d40:0894`:
-		//   _BuildBackground_Floppy(5, 0x42, 0x14); _FadeIn;
-		//   _MIDIPlayFile("travel-2.xmi");
-		//   Walk solved chain, SCRAPBK.ANI when 3 records left.
-		//   Mark solved, tier-promote, SavePlayerRecord,
-		//   ShowOneScrap, NextScreen = 0xc.
 		const uint mn = _mystery.number();
 
-		// `_BuildBackground_Floppy @ 16e2:12fd` ??? PIC 0x3d + SITES entry 5
-		// at (0x42, 0x14), palette 6.
 		{
 			Graphics::Surface *blk = g_system->lockScreen();
 			if (blk) {
@@ -5547,8 +5124,6 @@ void EEMEngine::doAccuseFloppy() {
 						scene.surface.pitch, sx, sy, sw, sh);
 			}
 
-			// Stamp partner at (5, 0x50); displayFloppyDialogRecords
-			// snapshots screen. ANI 2/0x10, script 0x02.
 			if (Graphics::Surface *screen = g_system->lockScreen()) {
 				blitPdaPartner(screen, _aniArchive, _partner,
 							   kPdaGalleryPartner, g_system->getMillis());
@@ -5557,12 +5132,9 @@ void EEMEngine::doAccuseFloppy() {
 			g_system->updateScreen();
 		}
 
-		// TRAVEL-2.XMI @ 2608:0c84 (`_MIDIPlayFile` @ 1d40:08c0).
 		if (_music && _voiceOn)
 			_music->playFile(Common::Path("travel-2.xmi"), false);
 
-		// Walk solved chain (header[+0x12]: count byte + dialog records).
-		// SCRAPBK.ANI fires when 3 records remain (`_PlayTitleANM_Floppy(0)`).
 		const byte *chain = _mystery.solvedClueBlock();
 		if (chain) {
 			const uint count = chain[0];
@@ -5589,15 +5161,11 @@ void EEMEngine::doAccuseFloppy() {
 		if (_music && _voiceOn)
 			_music->stop();
 
-		// 1d40:0939 ??? solved[mn] = _firstTry ? 2 : 1.
 		if (mn < sizeof(_mysteriesSolved))
 			_mysteriesSolved[mn] = _mystery._firstTry ? 2 : 1;
 
-		// Tier promotion @ 1d40:0941..0978. Skip mystery 0 (practice).
 		advanceChainStageAfterSolve(mn);
 
-		// `MakeSolvedSound`. `FUN_1d40_05b7` maps E<num>.BIN byte 0 (0..2)
-		// via table @ 2608:0c5e to VOC slots 0x15/0x16/0x17.
 		if (_audio && _voiceOn) {
 			Common::File ending;
 			const Common::String fname =
@@ -5615,10 +5183,8 @@ void EEMEngine::doAccuseFloppy() {
 			}
 		}
 
-		// 1d40:0991 ??? FUN_1ee2_06ac (= FUN_1d40_05b7 + font cleanup).
 		doShowEnding(mn);
 
-		// 1d40:0982 _SavePlayerRecord. clear() before save.
 		_mystery._solvedPuzzle = true;
 		_mystery.clear();
 		(void)saveProfile(_playerName);
@@ -5626,12 +5192,6 @@ void EEMEngine::doAccuseFloppy() {
 		return;
 	}
 
-	// `_DisplayAlibi_Floppy @ 1d40:00df`:
-	//   BG 0x3e + suspect pic at (0x82, 0x5a); MIDI fanfare2.xmi.
-	//   Balloon table @ 2608:0c0a (default 0x2c). Centered:
-	//     bx = (0x140-w)/2, by = (0x5a-h)/2.
-	//   KD reaction @ KDTextIndex[+10] = slot 5; NextScreen = LastScreen;
-	//   _firstTry = 0.
 	const byte *susp = _mystery.floppySuspectEntry((uint)picked);
 	uint16 picId = 0;
 	uint16 alibiOff = 0xFFFF;
@@ -5640,7 +5200,6 @@ void EEMEngine::doAccuseFloppy() {
 		alibiOff = _mystery.alibiTextOffset((uint)picked);
 	}
 
-	// FANFARE2.XMI alibi sting (`_MIDIPlayFile` gated on _voiceOn).
 	if (_music && _voiceOn)
 		_music->playFile(Common::Path("fanfare2.xmi"), false);
 
@@ -5660,8 +5219,6 @@ void EEMEngine::doAccuseFloppy() {
 							_playerName, _partner);
 	}
 
-	// Alibi balloon table @ 2608:0c0a (NOT 0c14). Digits @ 2608:0c3a..0c43.
-	// Default 0x2c. High bit = mirror flag.
 	static const uint8 kFloppyAlibiBalloonByDigit[10] = {
 		0x2a, 0x2b, 0x2c, 0x2d, 0xaa, 0xab, 0xac, 0xad, 0x09, 0x0a
 	};
@@ -5689,7 +5246,6 @@ void EEMEngine::doAccuseFloppy() {
 	Picture balloon;
 	const bool haveBalloon = _balloonArchive.size() > balloonIdx &&
 		_balloonArchive.loadEntry(balloonIdx, balloon);
-	// Centered @ 1d40:01a0: x=(0x140-w)/2, y=(0x5a-h)/2.
 	int balloonX = 0x21;
 	int balloonY = 1;
 	if (haveBalloon) {
@@ -5717,9 +5273,6 @@ void EEMEngine::doAccuseFloppy() {
 	g_system->copyRectToScreen(scene.getPixels(), scene.pitch, 0, 0, kScreenWidth, kScreenHeight);
 	g_system->updateScreen();
 
-	// No per-suspect VOC ??? alibi table @ 2608:0c5e is for post-win
-	// scrapbook (`FUN_1d40_05b7`), not alibi.
-
 	while (!shouldQuit()) {
 		Common::Event ev;
 		bool advance = false;
@@ -5738,7 +5291,6 @@ void EEMEngine::doAccuseFloppy() {
 		g_system->delayMillis(10);
 	}
 
-	// `_DisplayAlibi_Floppy @ 1d40:01ee` ??? KDTextIndex[+10] = slot 5.
 	floppyKDHint(5, kdIdx, bufBase, mysSize);
 	if (_music && _voiceOn)
 		_music->stop();
@@ -5747,13 +5299,11 @@ void EEMEngine::doAccuseFloppy() {
 	_nextScreen = _lastScreen != kScreenInvalid
 		? (ScreenId)_lastScreen : kScreenSite;
 }
-
+// _DoAccuseGallery @ 1df2:0a31.
 void EEMEngine::drawAccuseGallery(uint8 numSuspects, const byte *gd,
 								   int highlighted,
 								   Common::Array<Common::Rect> &slotRects,
 								   Common::Array<int> &slotSuspect) {
-	// `_DoAccuseGallery @ 1df2:0a31` portrait grid. PIC 0x3f + 5 slots.
-	// Partner (5, 0x50) ANI 2/0x10, script 0x02 (`_DoAccuse @ 1df2:0c30`).
 	Picture accuseBg;
 	const bool haveAccuseBg = _picsArchive.getPicture(0x3f, accuseBg);
 
@@ -5775,7 +5325,6 @@ void EEMEngine::drawAccuseGallery(uint8 numSuspects, const byte *gd,
 		const uint8 phys = _mystery._newOrder[i];
 		if (phys >= 5)
 			continue;
-		// `_DrawGallery @ 158f:00b9` skip on `_InGallery[phys]==0`.
 		if (_mystery._inGallery[phys] == 0)
 			continue;
 		const GallerySlot &s = kGallerySlots[phys];


Commit: 215d297ebd4887cc744870fa79e21345c27f063b
    https://github.com/scummvm/scummvm/commit/215d297ebd4887cc744870fa79e21345c27f063b
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:21:32+02:00

Commit Message:
EEM: enable fit dialog ballons for London

Changed paths:
    engines/eem/detection.cpp


diff --git a/engines/eem/detection.cpp b/engines/eem/detection.cpp
index c27f37a7bfb..056915c71f6 100644
--- a/engines/eem/detection.cpp
+++ b/engines/eem/detection.cpp
@@ -76,7 +76,7 @@ const ADGameDescription gameDescriptions[] = {
 		Common::EN_ANY,
 		Common::kPlatformDOS,
 		ADGF_UNSTABLE,
-		GUIO2(GUIO_MIDIADLIB, GUIO_MIDIMT32)
+		GUIO3(GAMEOPTION_FIT_DIALOG_BALLOONS, GUIO_MIDIADLIB, GUIO_MIDIMT32)
 	},
 
 	AD_TABLE_END_MARKER


Commit: 670bb5b82079201e1871d6d46e22dbe2c6342c25
    https://github.com/scummvm/scummvm/commit/670bb5b82079201e1871d6d46e22dbe2c6342c25
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:21:33+02:00

Commit Message:
EEM: fixed partner animation that was one frame longer in London

Changed paths:
    engines/eem/site.cpp
    engines/eem/site.h
    engines/eem/ui.cpp


diff --git a/engines/eem/site.cpp b/engines/eem/site.cpp
index ce2b9d89ab4..2c9d1561b43 100644
--- a/engines/eem/site.cpp
+++ b/engines/eem/site.cpp
@@ -714,7 +714,15 @@ uint oneShotThenLoopFrameAtTick(const uint8 *unfold, uint unfoldLen,
 	return (numFrames > 0) ? MIN<uint>(frame, numFrames - 1) : 0;
 }
 
-uint bigMapPartnerFrameAtTick(uint numFrames, uint32 elapsedMs) {
+uint bigMapPartnerFrameAtTick(uint numFrames, uint32 elapsedMs, bool london) {
+	if (london) {
+		static const uint8 kUnfoldL[]  = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
+		static const uint8 kWaitSeqL[] = { 10, 11, 10, 10, 10, 10, 10, 10, 10 };
+		return oneShotThenLoopFrameAtTick(kUnfoldL, ARRAYSIZE(kUnfoldL),
+										  kWaitSeqL, ARRAYSIZE(kWaitSeqL),
+										  numFrames, elapsedMs);
+	}
+	// EEM1 (11-frame anim): entrance {0..8}, idle `_BigMapWaitSeq` {9,..,10,..}.
 	static const uint8 kUnfold[]  = { 0, 1, 2, 3, 4, 5, 6, 7, 8 };
 	static const uint8 kWaitSeq[] = { 9, 9, 9, 9, 10, 9, 9, 9, 9 };
 	return oneShotThenLoopFrameAtTick(kUnfold, ARRAYSIZE(kUnfold),
diff --git a/engines/eem/site.h b/engines/eem/site.h
index 30567b2339d..ec7d80f370f 100644
--- a/engines/eem/site.h
+++ b/engines/eem/site.h
@@ -47,8 +47,10 @@ uint partnerFrameAtTick(uint16 seqnum, uint numFrames, uint32 tickMs);
 /// from EEM1's, so the engine must use the EEM2 sequences for that variant.
 void setLondonAnimScripts(bool enabled);
 
-/// bigMapPartnerFrameAtTick: count-up 0..8 once, then loop `_BigMapWaitSeq`
-uint bigMapPartnerFrameAtTick(uint numFrames, uint32 elapsedMs);
+/// bigMapPartnerFrameAtTick: overview-map partner walk. EEM1 (11-frame anim):
+/// count-up 0..8 once, then idle `_BigMapWaitSeq`. London's anim 0x14/0x12 has
+/// 12 frames, so it uses a longer entrance and an idle that reaches frame 11.
+uint bigMapPartnerFrameAtTick(uint numFrames, uint32 elapsedMs, bool london);
 
 /// bigMapDetailPartnerFrameAtTick: zoomed-view partner frame. Same two-phase shape
 /// as `bigMapPartnerFrameAtTick`.
diff --git a/engines/eem/ui.cpp b/engines/eem/ui.cpp
index dc7c47c74a4..c87536e9cb2 100644
--- a/engines/eem/ui.cpp
+++ b/engines/eem/ui.cpp
@@ -3649,7 +3649,7 @@ void EEMEngine::drawBigMapOverview(uint32 elapsedMs) {
 	Animation mapAnim;
 	if (_aniArchive.loadAnimation(kMapAniId, mapAnim) && !mapAnim.empty()) {
 		const uint frameIdx = bigMapPartnerFrameAtTick((uint)mapAnim.size(),
-													   elapsedMs);
+													   elapsedMs, isLondon());
 
 		blitAnimFrameAnchored(scratch.surfacePtr(), mapAnim[frameIdx],
 							  0xfd, 0x50);


Commit: 3b86f3eb9aaf7babc29aaf19bd54c8f1de3dfe88
    https://github.com/scummvm/scummvm/commit/3b86f3eb9aaf7babc29aaf19bd54c8f1de3dfe88
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:21:33+02:00

Commit Message:
EEM: do not render sublocations in London map

Changed paths:
    engines/eem/ui.cpp


diff --git a/engines/eem/ui.cpp b/engines/eem/ui.cpp
index c87536e9cb2..7979152d299 100644
--- a/engines/eem/ui.cpp
+++ b/engines/eem/ui.cpp
@@ -3296,8 +3296,8 @@ void EEMEngine::doBigMap() {
 						};
 						Common::Array<DetailMapHit> hits;
 						for (uint i = 0; i < _mystery.numSites(); i++) {
-							if (!_mystery._onSites[i] &&
-								i != _mystery._siteNumber)
+							// On-map flag alone, matching `_SearchMapButtons`.
+							if (!_mystery._onSites[i])
 								continue;
 							const byte *entry = _mystery.mapEntry(i);
 							if (!entry)
@@ -3607,7 +3607,9 @@ void EEMEngine::drawBigMapOverview(uint32 elapsedMs) {
 	const bool haveCrime  = _picsArchive.getPicture(0xc6,  crimeM);
 
 	for (uint i = 0; i < _mystery.numSites(); i++) {
-		if (!_mystery._onSites[i] && i != _mystery._siteNumber)
+		// `_DrawBigMapButtons` gates markers on the on-map flag alone, never the
+		// current site: a sublocation (in-site jump, never flagged) must not draw.
+		if (!_mystery._onSites[i])
 			continue;
 		const byte *entry = _mystery.mapEntry(i);
 		if (!entry)
@@ -3685,7 +3687,9 @@ void EEMEngine::drawBigMapDetail(int scrollX, int scrollY,
 
 	const bool floppyMap = _mystery.isLoaded() && isFloppy();
 	for (uint i = 0; i < _mystery.numSites(); i++) {
-		if (!_mystery._onSites[i] && i != _mystery._siteNumber)
+		// `_DrawBigMapButtons` gates markers on the on-map flag alone, never the
+		// current site: a sublocation (in-site jump, never flagged) must not draw.
+		if (!_mystery._onSites[i])
 			continue;
 		const byte *entry = _mystery.mapEntry(i);
 		if (!entry)


Commit: c021df15c99e73cdbde2906909e3e2e45e451433
    https://github.com/scummvm/scummvm/commit/c021df15c99e73cdbde2906909e3e2e45e451433
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:21:33+02:00

Commit Message:
EEM: added missing music in London

Changed paths:
    engines/eem/eem.cpp
    engines/eem/graphics.cpp
    engines/eem/ui.cpp


diff --git a/engines/eem/eem.cpp b/engines/eem/eem.cpp
index 136a45ab278..f8d7db79897 100644
--- a/engines/eem/eem.cpp
+++ b/engines/eem/eem.cpp
@@ -518,6 +518,8 @@ screenLoop:
 		case kScreenChooseMystery:
 			_nextScreen = kScreenInvalid;
 			doCaseSelection();
+			if (isLondon())
+				stopMusic();
 			if (_nextScreen == kScreenInvalid && _mystery.isLoaded())
 				_nextScreen = kScreenInitClues;
 			break;
@@ -547,6 +549,8 @@ screenLoop:
 
 		case kScreenNotebook:
 			doNotebook();
+			if (isLondon())
+				stopMusic();
 			if (!_mystery.isLoaded() && _nextScreen != kScreenAction)
 				_nextScreen = kScreenAction;
 			else if (_nextScreen == current)
@@ -555,6 +559,8 @@ screenLoop:
 
 		case kScreenGallery:
 			doGallery();
+			if (isLondon())
+				stopMusic();
 			if (!_mystery.isLoaded() && _nextScreen != kScreenAction)
 				_nextScreen = kScreenAction;
 			else if (_nextScreen == current)
@@ -593,6 +599,8 @@ screenLoop:
 
 		case kScreenAccuse:
 			doAccuse();
+			if (isLondon())
+				stopMusic();
 			if (!_mystery.isLoaded() && _nextScreen != kScreenAction)
 				_nextScreen = kScreenAction;
 			else if (_nextScreen == current)
diff --git a/engines/eem/graphics.cpp b/engines/eem/graphics.cpp
index da96f26668c..c6c5dd08d43 100644
--- a/engines/eem/graphics.cpp
+++ b/engines/eem/graphics.cpp
@@ -34,6 +34,7 @@
 #include "eem/audio.h"
 #include "eem/detection.h"
 #include "eem/eem.h"
+#include "eem/music.h"
 #include "eem/site.h"
 
 namespace EEM {
@@ -379,6 +380,8 @@ bool EEMEngine::doPuzzle(uint puzzleId) {
 		const byte *kd = _mystery.kdTextIndex();
 		const uint16 hintOff = kd ? READ_LE_UINT16(kd + 0x0c) : 0xFFFF;
 		if (hintOff != 0xFFFF) {
+			if (_music && _voiceOn)
+				_music->playMus(40, /* loop= */ false);
 			Common::String hint =
 				parseString(_mystery.textAt(hintOff), _playerName, _partner);
 			Graphics::ManagedSurface ms(kScreenWidth, kScreenHeight,
@@ -411,6 +414,7 @@ bool EEMEngine::doPuzzle(uint puzzleId) {
 			if (_audio && _voiceOn && kd)
 				_audio->sayKDDigital(kd, 6, _partner);
 			waitForInput(60000);
+			stopMusic();
 
 			g_system->copyRectToScreen(cleanBg.getPixels(), cleanBg.pitch,
 									   0, 0, kScreenWidth, kScreenHeight);
diff --git a/engines/eem/ui.cpp b/engines/eem/ui.cpp
index 7979152d299..6ffa2d61c46 100644
--- a/engines/eem/ui.cpp
+++ b/engines/eem/ui.cpp
@@ -1375,7 +1375,7 @@ void EEMEngine::doShowScrapbook(uint stage) {
 	const bool currentTier = (stage == _chainStage);
 
 	if (isLondon() && _music && _voiceOn)
-		_music->playMus(0x5d, /* loop= */ false);
+		_music->playMus(0x5d, /* loop= */ true);
 
 	int mystery = lo;
 	if (currentTier) {
@@ -2087,6 +2087,10 @@ void EEMEngine::doCaseSelection() {
 		warning("doCaseSelection: BOOK%u.NME failed to load", book);
 		return;
 	}
+
+	if (isLondon() && _music && _voiceOn)
+		_music->playMus(2, /* loop= */ true);
+
 	const uint listLen = MIN<uint>((uint)names.size(), stageHi - stageLo + 1);
 
 	Common::Array<bool> solvedFlags;
@@ -2322,10 +2326,24 @@ void EEMEngine::doNotebook() {
 	int hoveredNoteSlot = -1;
 	(void)hoveredNoteSlot;
 
+	const bool notebookFromSite = isLondon() && _lastScreen == kScreenSite;
+	if (_music && _voiceOn && notebookFromSite)
+		_music->playMus(30, /* loop= */ false);
+
 	drawNotebookFrame(page);
 	Common::Point mouse = g_system->getEventManager()->getMousePos();
 	setInteractiveMouseCursor(notebookButtonAt(mouse.x, mouse.y));
 
+	if (isLondon() && _music && _voiceOn) {
+		while (notebookFromSite && _music->isPlaying() && !shouldQuit()) {
+			Common::Event drain;
+			while (g_system->getEventManager()->pollEvent(drain)) {}
+			g_system->updateScreen();
+			g_system->delayMillis(10);
+		}
+		_music->playMus(5, /* loop= */ true);
+	}
+
 	uint32 lastDraw = g_system->getMillis();
 	uint32 gizmoLastTick = lastDraw;
 
@@ -2587,6 +2605,9 @@ void EEMEngine::doGallery() {
 	Picture galBg;
 	const bool haveBg = _picsArchive.getPicture(0x3f, galBg);
 
+	if (isLondon() && _music && _voiceOn)
+		_music->playMus(5, /* loop= */ true);
+
 	const uint8 num = _mystery.numSuspects();
 
 	Common::Array<Common::Rect> slotRects;
@@ -3492,7 +3513,7 @@ bool EEMEngine::doLondonApproach(uint16 approachId) {
 	CursorMan.showMouse(true);
 	setSiteHotspotCursorId(6);
 	if (_music && _voiceOn)
-		_music->playMus(0x27, /* loop= */ false);
+		_music->playMus(0x27, /* loop= */ true);
 
 	uint page = 0;
 	drawScreen(page);
@@ -4192,6 +4213,9 @@ void EEMEngine::doAccuse() {
 
 	const byte *gd = _mystery.galleryData();
 
+	if (isLondon() && _music && _voiceOn)
+		_music->playMus(4, /* loop= */ true);
+
 	// `_DoAccuse @ 1df2:0c11` outer loop; ESC ??? NextScreen=3.
 	if (!doAccuseNotes()) {
 		if (_nextScreen == kScreenAccuse) {
@@ -4366,6 +4390,9 @@ void EEMEngine::doAccuse() {
 		}
 	}
 
+	if (isLondon() && _music && _voiceOn)
+		_music->playMus(33, /* loop= */ true);
+
 	// Wrap past empty slots (matches original DI advance).
 	if (slotRects[highlighted].isEmpty())
 		highlighted = nextLiveSlot(slotRects, highlighted, +1);


Commit: 1234998c14ff024a18149b0922821c9c436e7062
    https://github.com/scummvm/scummvm/commit/1234998c14ff024a18149b0922821c9c436e7062
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:21:34+02:00

Commit Message:
EEM: corrected palette for the water in London map

Changed paths:
    engines/eem/ui.cpp


diff --git a/engines/eem/ui.cpp b/engines/eem/ui.cpp
index 6ffa2d61c46..d4c06d6a981 100644
--- a/engines/eem/ui.cpp
+++ b/engines/eem/ui.cpp
@@ -3160,11 +3160,11 @@ void EEMEngine::doBigMap() {
 				mapLastTick = now;
 				drawBigMapOverview(now - mapStartTick);
 				if (isLondon()) {
-					cyclePaletteRange(0xf4, 0xf9);
-					cyclePaletteRange(0xfa, 0xff);
+					cyclePaletteRangeReverse(0xf4, 0xf9);
+					cyclePaletteRangeReverse(0xfa, 0xff);
 				} else {
-					cyclePaletteRange(0xf7, 0xfa);
-					cyclePaletteRange(0xfb, 0xfe);
+					cyclePaletteRangeReverse(0xf7, 0xfa);
+					cyclePaletteRangeReverse(0xfb, 0xfe);
 				}
 			}
 			g_system->updateScreen();
@@ -3197,6 +3197,8 @@ void EEMEngine::doBigMap() {
 		int scrollX = MAX<int>(0, MIN<int>(mapW - kMapWinW, zoomX));
 		int scrollY = MAX<int>(0, MIN<int>(mapH - kMapWinH, zoomY));
 
+		setSitePalette(isLondon() ? 0x3a : 0x23);
+
 		const uint32 detailStartTick = g_system->getMillis();
 		drawBigMapDetail(scrollX, scrollY, mapPixels, mapW, mapH, 0);
 		uint32 detailLastTick = detailStartTick;
@@ -3381,8 +3383,8 @@ void EEMEngine::doBigMap() {
 				drawBigMapDetail(scrollX, scrollY, mapPixels, mapW, mapH,
 					now - detailStartTick);
 			if (frameTick && isLondon()) {
-				cyclePaletteRange(0xee, 0xf2);
-				cyclePaletteRange(0xea, 0xed);
+				cyclePaletteRangeReverse(0xee, 0xf2);
+				cyclePaletteRangeReverse(0xea, 0xed);
 			}
 			g_system->updateScreen();
 			g_system->delayMillis(10);


Commit: 0e72f804415c161bdcb52223a72fb28bee52fa30
    https://github.com/scummvm/scummvm/commit/0e72f804415c161bdcb52223a72fb28bee52fa30
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:21:34+02:00

Commit Message:
EEM: autosave every time we talk or get a new clue in London

Changed paths:
    engines/eem/eem.cpp
    engines/eem/site.cpp
    engines/eem/site.h


diff --git a/engines/eem/eem.cpp b/engines/eem/eem.cpp
index f8d7db79897..5c245a8793f 100644
--- a/engines/eem/eem.cpp
+++ b/engines/eem/eem.cpp
@@ -1502,6 +1502,7 @@ Common::Error EEMEngine::loadGameStream(Common::SeekableReadStream *stream) {
 	s.syncAsByte(_voiceOn);
 	if (_audio)
 		_audio->setVoiceEnabled(_voiceOn);
+	s.syncAsByte(_musicOn);
 
 	byte playerFemale = 0;
 	s.syncAsByte(playerFemale);
diff --git a/engines/eem/site.cpp b/engines/eem/site.cpp
index 2c9d1561b43..45d286e1012 100644
--- a/engines/eem/site.cpp
+++ b/engines/eem/site.cpp
@@ -896,12 +896,8 @@ void SiteScreen::enter(uint siteNum, bool resetPartnerMood) {
 			const uint16 clueOff = READ_LE_UINT16(idx + 2);
 			if (clueOff != 0xFFFF) {
 				const byte *clueBlock = _mystery->blobAt(clueOff);
-				if (clueBlock) {
-					// Partner-less BG so KD-anim doesn't ghost over the idle.
-					_vm->setPartnerEraseBg(&_bgSnapshot);
-					_vm->displayClue(clueBlock);
-					_vm->setPartnerEraseBg(nullptr);
-				}
+				if (clueBlock)
+					displayClueAndAutosave(clueBlock);
 			}
 		}
 		if (siteNum < Mystery::kVisitedSiteCap)
@@ -1721,6 +1717,26 @@ void SiteScreen::updateHotspotCursor(uint siteNum, int x, int y) {
 	_vm->setHotspotMouseCursor(siteControl || idx >= 0);
 }
 
+void SiteScreen::displayClueAndAutosave(const byte *clueBlock, bool forceSave) {
+	byte before[Mystery::kCluesFoundCap];
+	memcpy(before, _mystery->_cluesFound, sizeof(before));
+
+	_vm->setPartnerEraseBg(&_bgSnapshot);
+	_vm->displayClue(clueBlock);
+	_vm->setPartnerEraseBg(nullptr);
+
+	bool save = forceSave;
+	for (uint i = 0; !save && i < Mystery::kCluesFoundCap; i++)
+		save = !before[i] && _mystery->_cluesFound[i];
+
+	if (save) {
+		const Common::Error err = _vm->saveProfile(_vm->playerName());
+		if (err.getCode() != Common::kNoError)
+			warning("auto-save after clue failed: %s",
+					err.getDesc().c_str());
+	}
+}
+
 void SiteScreen::onHotspotClicked(uint siteNum, uint hotIdx) {
 	debugC(1, kDebugSite, "Site %u: hotspot %u clicked", siteNum, hotIdx);
 
@@ -1773,8 +1789,11 @@ void SiteScreen::onHotspotClicked(uint siteNum, uint hotIdx) {
 	if (spots) {
 		hotOrdinal = READ_LE_UINT16(spots + hotIdx * 14 + 0xa);
 	}
-	if (hotOrdinal < Mystery::kHotSpotsCap)
+	bool newlySeen = false;
+	if (hotOrdinal < Mystery::kHotSpotsCap) {
+		newlySeen = _mystery->_hotSpotsSeen[hotOrdinal] == 0;
 		_mystery->_hotSpotsSeen[hotOrdinal] = 1;
+	}
 	_mystery->_searchLocationNumber = (uint16)hotIdx;
 
 	// Bytes 8..9 of each 14-byte hotspot rect = byte offset to ClueBlock.
@@ -1785,29 +1804,8 @@ void SiteScreen::onHotspotClicked(uint siteNum, uint hotIdx) {
 		debugC(2, kDebugSite, "  hotspot %u -> clue offset 0x%04x",
 			   hotIdx, clueOff);
 		const byte *clueBlock = _mystery->blobAt(clueOff);
-		if (clueBlock) {
-			// Snapshot `_cluesFound` ??? detect new-clue 0???1 ??? autosave.
-			byte before[Mystery::kCluesFoundCap];
-			memcpy(before, _mystery->_cluesFound, sizeof(before));
-			_vm->setPartnerEraseBg(&_bgSnapshot);
-			_vm->displayClue(clueBlock);
-			_vm->setPartnerEraseBg(nullptr);
-			// New feature: autosave on new clue.
-			bool foundNewClue = false;
-			for (uint i = 0; i < Mystery::kCluesFoundCap; i++) {
-				if (!before[i] && _mystery->_cluesFound[i]) {
-					foundNewClue = true;
-					break;
-				}
-			}
-			if (foundNewClue) {
-				const Common::Error err =
-					_vm->saveProfile(_vm->playerName());
-				if (err.getCode() != Common::kNoError)
-					warning("auto-save after clue failed: %s",
-							err.getDesc().c_str());
-			}
-		}
+		if (clueBlock)
+			displayClueAndAutosave(clueBlock, /* forceSave= */ newlySeen);
 	}
 }
 // `_DoKDAnim(num) @ 168d:028a` + `_PlayAnimation @ 172b:1f46`:
diff --git a/engines/eem/site.h b/engines/eem/site.h
index ec7d80f370f..5301ab68eaa 100644
--- a/engines/eem/site.h
+++ b/engines/eem/site.h
@@ -103,6 +103,10 @@ private:
 	int  hotspotCursorId(uint siteNum, int idx) const;
 	void updateHotspotCursor(uint siteNum, int x, int y);
 	void onHotspotClicked(uint siteNum, uint hotIdx);
+	/// Show a site clue, then autosave the profile if @p forceSave is set (e.g.
+	/// a hotspot was newly marked seen) or it revealed a new notebook clue
+	/// (`_cluesFound` 0->1). Matches EEM1's per-clue persistence.
+	void displayClueAndAutosave(const byte *clueBlock, bool forceSave = false);
 	void initImpatienceCounter();
 	bool checkImpatienceCounter();
 	void notePartnerActivity();


Commit: 846a1b54522db9b23ac75c50986a457fd7497239
    https://github.com/scummvm/scummvm/commit/846a1b54522db9b23ac75c50986a457fd7497239
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:21:35+02:00

Commit Message:
EEM: show notes for each suspect in the gallery correctly in London

Changed paths:
    engines/eem/ui.cpp


diff --git a/engines/eem/ui.cpp b/engines/eem/ui.cpp
index d4c06d6a981..acb2e08f421 100644
--- a/engines/eem/ui.cpp
+++ b/engines/eem/ui.cpp
@@ -2765,7 +2765,9 @@ bool EEMEngine::moreInfo(const byte *gd, uint suspectIdx,
 	const int lineH = _font.getFontHeight();
 	const uint clueMax = floppyMI ? clueCount : 30u;
 	const byte *ni = _mystery.noteIndex();
-	const uint16 niCount = _mystery.noteIndexCount();
+	const uint16 niCount = isLondon()
+		? (uint16)(_mystery.noteSectionSize() / 2)
+		: _mystery.noteIndexCount();
 
 	uint pageStart = 0;
 	Common::Array<uint> pageStack;
@@ -2808,26 +2810,11 @@ bool EEMEngine::moreInfo(const byte *gd, uint suspectIdx,
 				continue;
 			if (!ni || clueId >= niCount)
 				continue;
-			// Floppy: 7-byte entry (+0 text, +2 Jake, +4 Jenny, +6 score).
-			// CD: 4-byte entry (u16 textOff + u16 score).
-			Common::String txt;
-			if (floppyMI) {
-				const uint16 textOff = READ_LE_UINT16(ni + clueId * 7);
-				const byte *bb = _mystery.blobAt(0);
-				const uint32 dsz = _mystery.dataSize();
-				if (bb && textOff != 0 && textOff < dsz) {
-					const char *p = (const char *)(bb + textOff);
-					uint32 len = 0;
-					while (textOff + len < dsz && p[len] != 0)
-						len++;
-					txt = parseString(Common::String(p, len),
-									  _playerName, _partner);
-				}
-			} else {
-				const uint16 textOff = READ_LE_UINT16(ni + clueId * 4);
-				txt = parseString(_mystery.textAt(textOff),
-								  _playerName, _partner);
-			}
+			// Shared notebook lookup: honours London's 2-byte NoteIndex stride
+			// (this path previously hardcoded the EEM1 4-byte stride).
+			Common::String txt = notebookNoteText(clueId, ni, niCount, floppyMI,
+												  _mystery.blobAt(0),
+												  _mystery.dataSize());
 			if (txt.empty())
 				continue;
 


Commit: 8d9720ef7419530b8321e854fd2f29798fd5c4c3
    https://github.com/scummvm/scummvm/commit/8d9720ef7419530b8321e854fd2f29798fd5c4c3
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:21:35+02:00

Commit Message:
EEM: deduction flow fixes for London

Changed paths:
    engines/eem/ui.cpp


diff --git a/engines/eem/ui.cpp b/engines/eem/ui.cpp
index acb2e08f421..de374c2bc52 100644
--- a/engines/eem/ui.cpp
+++ b/engines/eem/ui.cpp
@@ -2498,9 +2498,9 @@ void EEMEngine::drawNotebookFrame(int &page) {
 	// `_DrawNotes` walks `_NoteIndex` for current page; word-wraps each
 	// found clue in `_NotebookRect`. Selected = color 0x3c.
 	// EEM2/London NoteIndex entries are 2 bytes (no points field), so its real
-	// clue count is the section size / 2. `noteIndexCount()` assumes the EEM1
-	// 4-byte stride (and stays that way for the accuse-scoring path, which is a
-	// separate London concern)
+	// clue count is the section size / 2; `noteIndexCount()` assumes the EEM1
+	// 4-byte stride and undercounts by half. The notebook, gallery, and accuse
+	// note lists all use the London count so no found clue is dropped.
 	const uint16 londonCount = isLondon()
 		? (uint16)(_mystery.noteSectionSize() / 2) : 0;
 	Common::Array<uint> found;
@@ -3768,7 +3768,8 @@ Common::String EEMEngine::accuseNoteText(uint clueId,
 			(const char *)(ctx.bufBaseNotes + textOff),
 			_playerName, _partner);
 	}
-	const uint16 textOff = READ_LE_UINT16(ctx.ni + clueId * 4);
+	const uint stride = isLondon() ? 2 : 4;
+	const uint16 textOff = READ_LE_UINT16(ctx.ni + clueId * stride);
 	return parseString(_mystery.textAt(textOff),
 					   _playerName, _partner);
 }
@@ -3875,7 +3876,13 @@ bool EEMEngine::doAccuseNotes() {
 	if (!_mystery.isLoaded() || !_font.isLoaded())
 		return false;
 	const byte *ni = _mystery.noteIndex();
-	const uint16 niCount = _mystery.noteIndexCount();
+	// London's NoteIndex is 2-byte entries, so its real clue count is
+	// section size / 2; noteIndexCount() assumes EEM1's 4-byte stride and
+	// undercounts by half, which would drop the upper clues (e.g. answer
+	// clues 13/16 in the training case) from the selectable accuse list.
+	const uint16 niCount = isLondon()
+		? (uint16)(_mystery.noteSectionSize() / 2)
+		: _mystery.noteIndexCount();
 	if (!ni)
 		return false;
 
@@ -3891,7 +3898,10 @@ bool EEMEngine::doAccuseNotes() {
 
 	Common::Array<uint> found;
 	for (uint i = 0; i < niCount && i < Mystery::kCluesFoundCap; i++) {
-		if (_mystery._cluesFound[i] && _mystery.noteHasNotebookText(i))
+		const bool hasText = isLondon()
+			? (i < niCount)
+			: _mystery.noteHasNotebookText(i);
+		if (_mystery._cluesFound[i] && hasText)
 			found.push_back(i);
 	}
 
@@ -4671,15 +4681,19 @@ void EEMEngine::doAccuse() {
 			memset(blk->getPixels(), 0, kScreenWidth * kScreenHeight);
 			g_system->unlockScreen();
 		}
-		setSitePalette(6);
+		// `_DisplayCorrect` win background = `_BuildBackground(scene, 0x42, 0x14)`
+		// (frame PIC 0x3d + scene at 0x42,0x14, palette scene+1). EEM1 CD uses
+		// scene 5; EEM2/London uses scene 0x1b.
+		const uint winScene = isLondon() ? 0x1b : 5;
+		setSitePalette(winScene + 1);
 		Picture frame, scene;
 		if (_picsArchive.loadEntry(0x3d, frame)) {
 			g_system->copyRectToScreen(frame.surface.getPixels(),
 									   frame.surface.pitch, 0, 0,
 									   frame.surface.w, frame.surface.h);
 		}
-		if (5 < _sitesArchive.size() &&
-			_sitesArchive.loadEntry(5, scene)) {
+		if (winScene < _sitesArchive.size() &&
+			_sitesArchive.loadEntry(winScene, scene)) {
 			const int sx = 0x42, sy = 0x14;
 			const int sw = MIN<int>(scene.surface.w, kScreenWidth - sx);
 			const int sh = MIN<int>(scene.surface.h, kScreenHeight - sy);


Commit: cfed4b9586b93e7271fde79aa32d16c541ffe590
    https://github.com/scummvm/scummvm/commit/cfed4b9586b93e7271fde79aa32d16c541ffe590
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:21:35+02:00

Commit Message:
EEM: play the correct animation before showing the scrapbook in London

Changed paths:
    engines/eem/ui.cpp


diff --git a/engines/eem/ui.cpp b/engines/eem/ui.cpp
index de374c2bc52..27797d24f8f 100644
--- a/engines/eem/ui.cpp
+++ b/engines/eem/ui.cpp
@@ -4719,7 +4719,7 @@ void EEMEngine::doAccuse() {
 		if (_music && _voiceOn)
 			_music->stop();
 
-		playAnm(Common::Path("SCRAPBK.ANI"), 120,
+		playAnm(Common::Path(isLondon() ? "SCRAP.ANM" : "SCRAPBK.ANI"), 120,
 				/* holdLastFrame= */ false);
 
 		displayScrapbookExtra(mn);


Commit: 1161071df317d6d5776aee13ed5f8788440d9a92
    https://github.com/scummvm/scummvm/commit/1161071df317d6d5776aee13ed5f8788440d9a92
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:21:36+02:00

Commit Message:
EEM: concurrent partner animations during conversations

Changed paths:
    engines/eem/clues.cpp
    engines/eem/eem.h
    engines/eem/site.cpp
    engines/eem/site.h


diff --git a/engines/eem/clues.cpp b/engines/eem/clues.cpp
index 9fc34c93c95..6e312202ca0 100644
--- a/engines/eem/clues.cpp
+++ b/engines/eem/clues.cpp
@@ -858,10 +858,21 @@ void EEMEngine::displayClue(const byte *clueBlock) {
 		}
 
 		const int16 kdAnimNum = (int16)READ_LE_UINT16(c + (isLondon() ? 0x4e : 0x3a));
-		if (kdAnimNum != -1) {
-			playKdAnim((uint16)kdAnimNum);
-			g_system->copyRectToScreen(bg.getPixels(), bg.pitch,
-									   0, 0, kScreenWidth, kScreenHeight);
+		// Load the partner gesture; it animates concurrently with the balloon
+		// and voice in the wait loop below.
+		Animation kdAnim;
+		int kdPx = 0;
+		int kdPy = 0;
+		uint16 kdAnimId = 0;
+		const bool haveKd = kdAnimNum != -1 &&
+			loadKdAnim((uint16)kdAnimNum, kdAnim, kdPx, kdPy, kdAnimId);
+
+		// Animate the gesture over the partner-less scene so it doesn't ghost
+		// the static partner.
+		if (haveKd && _partnerEraseBg.w == kScreenWidth &&
+			_partnerEraseBg.h == kScreenHeight) {
+			g_system->copyRectToScreen(_partnerEraseBg.getPixels(),
+				_partnerEraseBg.pitch, 0, 0, kScreenWidth, kScreenHeight);
 		}
 
 		const bool useP1 = (_partner == kPartnerJenny) &&
@@ -978,6 +989,19 @@ void EEMEngine::displayClue(const byte *clueBlock) {
 			setInteractiveMouseCursor(false);
 			bool advance = false;
 			bool skipAll = false;
+			Graphics::ManagedSurface kdBase(kScreenWidth, kScreenHeight,
+				Graphics::PixelFormat::createFormatCLUT8());
+			bool haveKdBase = false;
+			uint kdLastFrame = (uint)-1;
+			const uint32 kdStartMs = g_system->getMillis();
+			if (haveKd) {
+				Graphics::Surface *kdScr = g_system->lockScreen();
+				if (kdScr) {
+					kdBase.simpleBlitFrom(*kdScr);
+					g_system->unlockScreen();
+					haveKdBase = true;
+				}
+			}
 			while (!advance && !shouldQuit()) {
 				Common::Event ev;
 				while (g_system->getEventManager()->pollEvent(ev)) {
@@ -1009,6 +1033,20 @@ void EEMEngine::displayClue(const byte *clueBlock) {
 						break;
 					}
 				}
+				if (haveKdBase) {
+					const uint kdFrame = oneShotFrameAtTick(kdAnimId,
+						(uint)kdAnim.size(), g_system->getMillis() - kdStartMs);
+					if (kdFrame != kdLastFrame && kdFrame < kdAnim.size()) {
+						kdLastFrame = kdFrame;
+						Graphics::ManagedSurface comp(kScreenWidth, kScreenHeight,
+							Graphics::PixelFormat::createFormatCLUT8());
+						comp.simpleBlitFrom(kdBase);
+						blitAnimFrameAnchored(comp.surfacePtr(),
+							kdAnim[kdFrame], kdPx, kdPy);
+						g_system->copyRectToScreen(comp.getPixels(), comp.pitch,
+							0, 0, kScreenWidth, kScreenHeight);
+					}
+				}
 				g_system->updateScreen();
 				g_system->delayMillis(10);
 			}
diff --git a/engines/eem/eem.h b/engines/eem/eem.h
index 4ea98b5aa65..c06781e49e0 100644
--- a/engines/eem/eem.h
+++ b/engines/eem/eem.h
@@ -221,8 +221,10 @@ public:
 							   const Common::String &playerName,
 							   uint partner) const;
 
-	/// `_DoKDAnim @ 168d:028a` + `_PlayAnimation @ 172b:1f46`.
-	void playKdAnim(uint16 num);
+	/// Load the partner gesture (animId + anchor) for concurrent playback with
+	/// the clue balloon/voice. false if out of range.
+	bool loadKdAnim(uint16 num, Animation &anim, int &px, int &py,
+					uint16 &animId);
 
 	void setPartnerEraseBg(const Graphics::ManagedSurface *bg);
 
diff --git a/engines/eem/site.cpp b/engines/eem/site.cpp
index 45d286e1012..0627cdeeb65 100644
--- a/engines/eem/site.cpp
+++ b/engines/eem/site.cpp
@@ -702,6 +702,16 @@ uint partnerFrameAtTick(uint16 seqnum, uint numFrames, uint32 tickMs) {
 	return frameFromScriptAtTick(s.frames, s.len, numFrames, tickMs);
 }
 
+uint oneShotFrameAtTick(uint16 seqnum, uint numFrames, uint32 tickMs) {
+	const AnimScriptRef s = findAnimScript(seqnum);
+	const uint tick = (uint)(tickMs / kFramePeriodMs);
+	if (!s.frames || s.len == 0)
+		return numFrames > 0 ? MIN<uint>(tick, numFrames - 1) : 0;
+	const uint scriptIdx = MIN<uint>(tick, (uint)s.len - 1);
+	const uint frame = s.frames[scriptIdx];
+	return numFrames > 0 ? MIN<uint>(frame, numFrames - 1) : 0;
+}
+
 // Play `unfold` once, then loop `waitSeq` forever. Mirrors the
 // original's slot-script-swap idiom
 uint oneShotThenLoopFrameAtTick(const uint8 *unfold, uint unfoldLen,
@@ -1817,76 +1827,21 @@ void SiteScreen::onHotspotClicked(uint siteNum, uint hotIdx) {
 //     -> registers a state-4 (one-shot) animation slot and lets
 //        `_UpdateAnimations` walk the script until 0x80, then
 //        frees the slot and re-activates `WaitHandle`.
-void EEMEngine::playKdAnim(uint16 num) {
+bool EEMEngine::loadKdAnim(uint16 num, Animation &anim, int &px, int &py,
+						   uint16 &animId) {
 	if (num >= ARRAYSIZE(kKdAnimTable))
-		return;
+		return false;
 
 	const uint16 (*kdTable)[6] = isLondon() ? kKdAnimTableLondon : kKdAnimTable;
 	const uint partner = (_partner == kPartnerJake) ? 0 : 1;
-	const uint16 animId = kdTable[num][partner];
-	const int    px     = (int)kdTable[num][2 + partner];
-	const int    py     = (int)kdTable[num][4 + partner];
+	animId = kdTable[num][partner];
+	px     = (int)kdTable[num][2 + partner];
+	py     = (int)kdTable[num][4 + partner];
 
-	Animation anim;
 	if (!_aniArchive.loadAnimation(animId, anim) || anim.empty()) {
-		warning("playKdAnim(%u): anim %u failed to load", num, animId);
-		return;
-	}
-
-	// State-4 one-shot walks the same (looping) script through once.
-	const AnimScriptRef s = findAnimScript(animId);
-	const uint8 *frames = s.frames;
-	uint frameCount     = s.len;
-	if (frameCount == 0) {
-		// Fallback: linear playback through anim cells.
-		frameCount = (uint)anim.size();
-	}
-
-	// Erase-source: caller-stashed partner-less BG (via `setPartnerEraseBg`)
-	// or fall back to current screen (works for full-screen contexts).
-	Graphics::ManagedSurface bg(kScreenWidth, kScreenHeight,
-		Graphics::PixelFormat::createFormatCLUT8());
-	if (_partnerEraseBg.w == kScreenWidth && _partnerEraseBg.h == kScreenHeight) {
-		bg.simpleBlitFrom(_partnerEraseBg);
-	} else {
-		Graphics::Surface *screen = g_system->lockScreen();
-		if (!screen)
-			return;
-		bg.simpleBlitFrom(*screen);
-		g_system->unlockScreen();
-	}
-
-	for (uint i = 0; i < frameCount && !shouldQuit(); i++) {
-		const uint frameIdx = frames ? (uint)frames[i] : i;
-		if (frameIdx >= anim.size())
-			continue;
-		const Picture &fr = anim[frameIdx];
-		const byte transp = (byte)(fr.flags >> 8);
-
-		Graphics::ManagedSurface scratch(kScreenWidth, kScreenHeight,
-			Graphics::PixelFormat::createFormatCLUT8());
-		scratch.simpleBlitFrom(bg);
-		(void)transp;  // anchored blitter recomputes from p.flags
-		blitAnimFrameAnchored(scratch.surfacePtr(), fr, px, py);
-		g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
-								   0, 0, kScreenWidth, kScreenHeight);
-		g_system->updateScreen();
-
-		const uint32 wakeup = g_system->getMillis() + 100;
-		while (g_system->getMillis() < wakeup && !shouldQuit()) {
-			Common::Event ev;
-			while (g_system->getEventManager()->pollEvent(ev)) {
-				// Drain events; don't skip mid-anim (would eat upcoming clue).
-				if (ev.type == Common::EVENT_QUIT ||
-					ev.type == Common::EVENT_RETURN_TO_LAUNCHER)
-					return;
-			}
-			g_system->updateScreen();
-			g_system->delayMillis(10);
-		}
+		warning("loadKdAnim(%u): anim %u failed to load", num, animId);
+		return false;
 	}
-
-	g_system->copyRectToScreen(bg.getPixels(), bg.pitch, 0, 0, kScreenWidth, kScreenHeight);
-	g_system->updateScreen();
+	return true;
 }
 } // End of namespace EEM
diff --git a/engines/eem/site.h b/engines/eem/site.h
index 5301ab68eaa..106bb556a35 100644
--- a/engines/eem/site.h
+++ b/engines/eem/site.h
@@ -42,6 +42,9 @@ class Mystery;
 /// used both for the fallback and to clamp script values past the asset.
 uint partnerFrameAtTick(uint16 seqnum, uint numFrames, uint32 tickMs);
 
+/// Like partnerFrameAtTick but plays the script ONCE and holds the final frame.
+uint oneShotFrameAtTick(uint16 seqnum, uint numFrames, uint32 tickMs);
+
 /// Select the EEM2 ("London") animation-script table inside `findAnimScript`.
 /// EEM2 ships its own `_AnimationSequences`; many partner/KD scripts differ
 /// from EEM1's, so the engine must use the EEM2 sequences for that variant.


Commit: ce99471d1431b4b5689278677587c1da538a9b40
    https://github.com/scummvm/scummvm/commit/ce99471d1431b4b5689278677587c1da538a9b40
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:21:36+02:00

Commit Message:
EEM: improved partner animations reducing flickering

Changed paths:
    engines/eem/clues.cpp
    engines/eem/eem.h
    engines/eem/site.cpp
    engines/eem/site.h


diff --git a/engines/eem/clues.cpp b/engines/eem/clues.cpp
index 6e312202ca0..e4f3957e725 100644
--- a/engines/eem/clues.cpp
+++ b/engines/eem/clues.cpp
@@ -968,7 +968,10 @@ void EEMEngine::displayClue(const byte *clueBlock) {
 			if (copyRows > 0) {
 				g_system->copyRectToScreen(scratch.getBasePtr(0, copyY),
 					scratch.pitch, 0, copyY, kScreenWidth, copyRows);
-				g_system->updateScreen();
+				// Gesture entry: let the wait loop present, so the partner-less
+				// base isn't flashed before the gesture's first frame.
+				if (!haveKd)
+					g_system->updateScreen();
 			}
 		}
 
@@ -1002,6 +1005,23 @@ void EEMEngine::displayClue(const byte *clueBlock) {
 					haveKdBase = true;
 				}
 			}
+			// Play the gesture one-shot, then loop the partner idle over the
+			// same partner-less base (the original resumes idle when it ends).
+			const uint32 kdDurationMs = haveKd
+				? oneShotDurationMs(kdAnimId, (uint)kdAnim.size()) : 0;
+			Animation idleAnim;
+			int idleX = 0;
+			int idleY = 0;
+			bool haveIdle = false;
+			if (haveKdBase && _hasPartnerIdle &&
+				getAni().loadAnimation(_partnerIdleAnimId, idleAnim) &&
+				!idleAnim.empty()) {
+				haveIdle = true;
+				idleX = _partnerIdleX;
+				idleY = _partnerIdleY;
+			}
+			bool kdInIdle = false;
+			uint kdLastIdleFrame = (uint)-1;
 			while (!advance && !shouldQuit()) {
 				Common::Event ev;
 				while (g_system->getEventManager()->pollEvent(ev)) {
@@ -1033,18 +1053,40 @@ void EEMEngine::displayClue(const byte *clueBlock) {
 						break;
 					}
 				}
+				if (skipAll)
+					break;
 				if (haveKdBase) {
-					const uint kdFrame = oneShotFrameAtTick(kdAnimId,
-						(uint)kdAnim.size(), g_system->getMillis() - kdStartMs);
-					if (kdFrame != kdLastFrame && kdFrame < kdAnim.size()) {
-						kdLastFrame = kdFrame;
-						Graphics::ManagedSurface comp(kScreenWidth, kScreenHeight,
-							Graphics::PixelFormat::createFormatCLUT8());
-						comp.simpleBlitFrom(kdBase);
-						blitAnimFrameAnchored(comp.surfacePtr(),
-							kdAnim[kdFrame], kdPx, kdPy);
-						g_system->copyRectToScreen(comp.getPixels(), comp.pitch,
-							0, 0, kScreenWidth, kScreenHeight);
+					const uint32 kdElapsed = g_system->getMillis() - kdStartMs;
+					if (haveIdle && kdElapsed >= kdDurationMs) {
+						// Resume the looping idle wait-anim.
+						const uint f = partnerFrameAtTick(_partnerIdleAnimId,
+							(uint)idleAnim.size(), kdElapsed - kdDurationMs);
+						if ((!kdInIdle || f != kdLastIdleFrame) &&
+							f < idleAnim.size()) {
+							kdInIdle = true;
+							kdLastIdleFrame = f;
+							Graphics::ManagedSurface comp(kScreenWidth, kScreenHeight,
+								Graphics::PixelFormat::createFormatCLUT8());
+							comp.simpleBlitFrom(kdBase);
+							blitAnimFrameAnchored(comp.surfacePtr(),
+								idleAnim[f], idleX, idleY);
+							g_system->copyRectToScreen(comp.getPixels(), comp.pitch,
+								0, 0, kScreenWidth, kScreenHeight);
+						}
+					} else {
+						// Gesture one-shot.
+						const uint f = oneShotFrameAtTick(kdAnimId,
+							(uint)kdAnim.size(), kdElapsed);
+						if (f != kdLastFrame && f < kdAnim.size()) {
+							kdLastFrame = f;
+							Graphics::ManagedSurface comp(kScreenWidth, kScreenHeight,
+								Graphics::PixelFormat::createFormatCLUT8());
+							comp.simpleBlitFrom(kdBase);
+							blitAnimFrameAnchored(comp.surfacePtr(),
+								kdAnim[f], kdPx, kdPy);
+							g_system->copyRectToScreen(comp.getPixels(), comp.pitch,
+								0, 0, kScreenWidth, kScreenHeight);
+						}
 					}
 				}
 				g_system->updateScreen();
diff --git a/engines/eem/eem.h b/engines/eem/eem.h
index c06781e49e0..4fd29ea5183 100644
--- a/engines/eem/eem.h
+++ b/engines/eem/eem.h
@@ -228,6 +228,15 @@ public:
 
 	void setPartnerEraseBg(const Graphics::ManagedSurface *bg);
 
+	/// Partner idle wait-anim that `displayClue` loops once a clue gesture's
+	/// one-shot ends. Set around `displayClue` by the site loop.
+	void setPartnerIdleAnim(bool has, uint16 animId, int x, int y) {
+		_hasPartnerIdle = has;
+		_partnerIdleAnimId = animId;
+		_partnerIdleX = x;
+		_partnerIdleY = y;
+	}
+
 	/// Balloon-text-inset metadata. 52-entry table @ 29be:0875 (CD) /
 	/// 2608:05f9 (floppy), indexed by `(bubNum & 0x7F)`. 10 bytes per
 	/// entry: the first 3 fields (x inset, y inset, text width) are used
@@ -501,10 +510,15 @@ private:
 	/// ESC during intro: skip remaining opening-anim chain.
 	bool _skipIntro = false;
 
-	/// Clean BG (no partner/NPC) used by `playKdAnim` between camera-anim
-	/// cells. 
+	/// Partner-less scene that `displayClue` composites the gesture/idle over.
 	Graphics::ManagedSurface _partnerEraseBg;
 
+	/// Idle wait-anim to resume after a clue gesture (see setPartnerIdleAnim).
+	bool   _hasPartnerIdle = false;
+	uint16 _partnerIdleAnimId = 0;
+	int    _partnerIdleX = 0;
+	int    _partnerIdleY = 0;
+
 	bool _interactiveMouseCursor = false;
 	/// Active EEM2 cursor shape (index into `kLondonCursorPics`). -1 forces a
 	/// reload on the next `setSiteHotspotCursorId`.
diff --git a/engines/eem/site.cpp b/engines/eem/site.cpp
index 0627cdeeb65..c4d7b9f9949 100644
--- a/engines/eem/site.cpp
+++ b/engines/eem/site.cpp
@@ -712,6 +712,12 @@ uint oneShotFrameAtTick(uint16 seqnum, uint numFrames, uint32 tickMs) {
 	return numFrames > 0 ? MIN<uint>(frame, numFrames - 1) : 0;
 }
 
+uint32 oneShotDurationMs(uint16 seqnum, uint numFrames) {
+	const AnimScriptRef s = findAnimScript(seqnum);
+	const uint count = (s.frames && s.len) ? (uint)s.len : numFrames;
+	return (uint32)count * kFramePeriodMs;
+}
+
 // Play `unfold` once, then loop `waitSeq` forever. Mirrors the
 // original's slot-script-swap idiom
 uint oneShotThenLoopFrameAtTick(const uint8 *unfold, uint unfoldLen,
@@ -1447,19 +1453,17 @@ void SiteScreen::syncCompositedScreen() {
 							   0, 0, kScreenWidth, kScreenHeight);
 }
 
-void SiteScreen::renderPartner(uint siteNum, uint32 tickMs) {
+bool SiteScreen::partnerIdleAnimParams(uint siteNum, uint16 &animId,
+									   int &x, int &y) {
 	const byte *site = _mystery->siteData(siteNum);
 	if (!site)
-		return;
+		return false;
 	const uint8 partner = _vm->getPartnerIndex();
-	uint   animId;
-	int    x;
-	int    y;
 	if (_vm->isFloppy()) {
 		const uint16 spkOff = READ_LE_UINT16(site + 8);
 		const byte *spk = _mystery->blobAt(spkOff);
 		if (!spk)
-			return;
+			return false;
 		if (partner == 0) {
 			animId = READ_LE_UINT16(spk + 0);
 			x      = (int)READ_LE_UINT16(spk + 2);
@@ -1473,15 +1477,21 @@ void SiteScreen::renderPartner(uint siteNum, uint32 tickMs) {
 		const uint16 speaker = READ_LE_UINT16(site + 8);
 		const uint16 (*waitTable)[6] = _vm->isLondon()
 			? kWaitAnimsLondon : kWaitAnims;
-		if (speaker >= ARRAYSIZE(kWaitAnims)) {
-			warning("renderPartner: site %u has speakerIdx=%u out of range",
-					siteNum, speaker);
-			return;
-		}
+		if (speaker >= ARRAYSIZE(kWaitAnims))
+			return false;
 		animId = waitTable[speaker][0 + partner];
 		x      = (int)(int16)waitTable[speaker][2 + partner];
 		y      = (int)(int16)waitTable[speaker][4 + partner];
 	}
+	return true;
+}
+
+void SiteScreen::renderPartner(uint siteNum, uint32 tickMs) {
+	uint16 animId;
+	int    x;
+	int    y;
+	if (!partnerIdleAnimParams(siteNum, animId, x, y))
+		return;
 
 	Animation anim;
 	if (!_vm->getAni().loadAnimation(animId, anim) || anim.empty())
@@ -1731,8 +1741,16 @@ void SiteScreen::displayClueAndAutosave(const byte *clueBlock, bool forceSave) {
 	byte before[Mystery::kCluesFoundCap];
 	memcpy(before, _mystery->_cluesFound, sizeof(before));
 
+	// Idle wait-anim for `displayClue` to resume after a gesture's one-shot.
+	uint16 idleId = 0;
+	int idleX = 0, idleY = 0;
+	const bool hasIdle =
+		partnerIdleAnimParams(_mystery->_siteNumber, idleId, idleX, idleY);
+
 	_vm->setPartnerEraseBg(&_bgSnapshot);
+	_vm->setPartnerIdleAnim(hasIdle, idleId, idleX, idleY);
 	_vm->displayClue(clueBlock);
+	_vm->setPartnerIdleAnim(false, 0, 0, 0);
 	_vm->setPartnerEraseBg(nullptr);
 
 	bool save = forceSave;
diff --git a/engines/eem/site.h b/engines/eem/site.h
index 106bb556a35..f17f8ae4e20 100644
--- a/engines/eem/site.h
+++ b/engines/eem/site.h
@@ -45,6 +45,9 @@ uint partnerFrameAtTick(uint16 seqnum, uint numFrames, uint32 tickMs);
 /// Like partnerFrameAtTick but plays the script ONCE and holds the final frame.
 uint oneShotFrameAtTick(uint16 seqnum, uint numFrames, uint32 tickMs);
 
+/// Total time for one full play of a one-shot gesture (frame count * period).
+uint32 oneShotDurationMs(uint16 seqnum, uint numFrames);
+
 /// Select the EEM2 ("London") animation-script table inside `findAnimScript`.
 /// EEM2 ships its own `_AnimationSequences`; many partner/KD scripts differ
 /// from EEM1's, so the engine must use the EEM2 sequences for that variant.
@@ -118,9 +121,14 @@ private:
 	/// Partner site-arrival sequence
 	bool enterSiteAnim();
 
-	/// renderPartner: persistent in-site partner sprite 
+	/// renderPartner: persistent in-site partner sprite
 	void renderPartner(uint siteNum, uint32 tickMs);
 
+	/// Resolve the partner idle wait-anim (animId + screen anchor) for a site.
+	/// Shared by renderPartner and the clue gesture's idle-resume. Returns
+	/// false when the site has no usable speaker/partner entry.
+	bool partnerIdleAnimParams(uint siteNum, uint16 &animId, int &x, int &y);
+
 	/// Floppy active speaker pose 
 	bool renderFloppyHotspotPartnerPose(uint siteNum);
 


Commit: 24173e32113517a95c4ce900e35eba441f7af73b
    https://github.com/scummvm/scummvm/commit/24173e32113517a95c4ce900e35eba441f7af73b
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:21:36+02:00

Commit Message:
EEM: properly mark interactive zones in London

Changed paths:
    engines/eem/eem.cpp
    engines/eem/site.cpp


diff --git a/engines/eem/eem.cpp b/engines/eem/eem.cpp
index 5c245a8793f..82613a39f66 100644
--- a/engines/eem/eem.cpp
+++ b/engines/eem/eem.cpp
@@ -625,6 +625,9 @@ void EEMEngine::setInteractiveMouseCursor(bool active) {
 
 	_interactiveMouseCursor = active;
 	installMouseCursor(_picsArchive, active);
+	// The red-outline highlight replaced any London cursor shape; force the
+	// next setSiteHotspotCursorId to reinstall.
+	_siteCursorId = -1;
 }
 
 void EEMEngine::setHotspotMouseCursor(bool active) {
@@ -660,6 +663,8 @@ void EEMEngine::setSiteHotspotCursorId(int cursorId) {
 	CursorMan.replaceCursor(cursor.surface.rawSurface(), 0, 0, transparent);
 	CursorMan.replaceCursorPalette(nullptr, 0, 0);
 	_siteCursorId = cursorId;
+	// This London cursor replaced the red-outline highlight, if it was active.
+	_interactiveMouseCursor = false;
 }
 
 bool EEMEngine::openArchives() {
diff --git a/engines/eem/site.cpp b/engines/eem/site.cpp
index c4d7b9f9949..88e6c6e919e 100644
--- a/engines/eem/site.cpp
+++ b/engines/eem/site.cpp
@@ -1725,15 +1725,22 @@ void SiteScreen::updateHotspotCursor(uint siteNum, int x, int y) {
 	if (!_vm)
 		return;
 	const int idx = hotspotAtPoint(siteNum, x, y);
-	if (_vm->isLondon()) {
-		// EEM2 (`_DoSiteLoop` @ FUN_1717_07ab) swaps the cursor shape to the
-		// hovered hotspot's own cursor id; off any hotspot it is the arrow (0).
-		_vm->setSiteHotspotCursorId(idx >= 0 ? hotspotCursorId(siteNum, idx) : 0);
-		return;
-	}
 	const bool siteControl = kPdaSiteRect.contains(x, y) ||
 							 kPdaPartnerFootMapRect.contains(x, y) ||
 							 kPdaPartnerHeadHintRect.contains(x, y);
+	if (_vm->isLondon()) {
+		// EEM2 gives some hotspots their own cursor shape; every other
+		// interactive zone (the site/foot/head controls, or a hotspot with no
+		// custom cursor) falls back to the red-outline highlight.
+		const int cursorId = idx >= 0 ? hotspotCursorId(siteNum, idx) : 0;
+		if (cursorId > 0)
+			_vm->setSiteHotspotCursorId(cursorId);
+		else if (idx >= 0 || siteControl)
+			_vm->setInteractiveMouseCursor(true);
+		else
+			_vm->setSiteHotspotCursorId(0);
+		return;
+	}
 	_vm->setHotspotMouseCursor(siteControl || idx >= 0);
 }
 


Commit: 111cc673fc7edb49681f4eddd0b2c1cd26b6f3e8
    https://github.com/scummvm/scummvm/commit/111cc673fc7edb49681f4eddd0b2c1cd26b6f3e8
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-22T00:21:37+02:00

Commit Message:
EEM: completed implementation for setup screen in London

Changed paths:
    engines/eem/eem.h
    engines/eem/ui.cpp


diff --git a/engines/eem/eem.h b/engines/eem/eem.h
index 4fd29ea5183..a91adfc73e8 100644
--- a/engines/eem/eem.h
+++ b/engines/eem/eem.h
@@ -425,6 +425,9 @@ private:
 
 	void doSetupLondon();
 	void setupDrawScreenLondon();
+	/// London "profile saved" confirmation: PIC 0x203 centred over the setup
+	/// screen, dismissed by any click/key (caller redraws the setup screen).
+	void setupShowSavedConfirm();
 
 	/// `_DoInitClues @ 1a35:0411` (minus live ANI sequence playback).
 	void doInitClues();
diff --git a/engines/eem/ui.cpp b/engines/eem/ui.cpp
index 27797d24f8f..086fd98647f 100644
--- a/engines/eem/ui.cpp
+++ b/engines/eem/ui.cpp
@@ -1699,6 +1699,37 @@ void EEMEngine::setupDrawScreenLondon() {
 	g_system->updateScreen();
 }
 
+void EEMEngine::setupShowSavedConfirm() {
+	Picture pic;
+	if (!_picsArchive.getPicture(0x203, pic) || pic.surface.empty())
+		return;
+	Graphics::ManagedSurface scratch(kScreenWidth, kScreenHeight,
+		Graphics::PixelFormat::createFormatCLUT8());
+	Graphics::Surface *cur = g_system->lockScreen();
+	if (cur) {
+		scratch.simpleBlitFrom(*cur);
+		g_system->unlockScreen();
+	}
+	const int sx = MAX<int>(0, (kScreenWidth  - pic.surface.w) / 2);
+	const int sy = MAX<int>(0, (kScreenHeight - pic.surface.h) / 2);
+	scratch.transBlitFrom(pic.surface, Common::Point(sx, sy),
+						  (uint32)(byte)(pic.flags >> 8));
+	g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
+							   0, 0, kScreenWidth, kScreenHeight);
+	g_system->updateScreen();
+	while (!shouldQuit()) {
+		Common::Event ev;
+		while (g_system->getEventManager()->pollEvent(ev)) {
+			if (ev.type == Common::EVENT_QUIT ||
+				ev.type == Common::EVENT_RETURN_TO_LAUNCHER ||
+				ev.type == Common::EVENT_KEYDOWN ||
+				ev.type == Common::EVENT_LBUTTONDOWN)
+				return;
+		}
+		g_system->delayMillis(15);
+	}
+}
+
 // `_DoSetup @ 2046:067b`
 void EEMEngine::doSetupLondon() {
 	if (!_font.isLoaded()) {
@@ -1714,8 +1745,8 @@ void EEMEngine::doSetupLondon() {
 	const Common::Rect kSaveBtn    (281, 108, 299, 125); // [6]
 	const Common::Rect kNewCaseBtn (281, 127, 299, 144); // [7]
 	const Common::Rect kDoneBtn    ( 53, 153, 108, 183); // [8]
-	const Common::Rect kQuitBtn    (145, 163, 174, 187); // [9]
-	const Common::Rect kHelpBtn    (212, 153, 266, 184); // [10]
+	const Common::Rect kHelpBtn    (145, 163, 174, 187); // [9]
+	const Common::Rect kQuitBtn    (212, 153, 266, 184); // [10]
 	const Common::Rect kCreditsBtn ( 81,  25, 238,  37); // [11]
 	const Common::Rect kMusicBtn   ( 20,  82,  38,  99); // [12]
 
@@ -1791,8 +1822,11 @@ void EEMEngine::doSetupLondon() {
 			}
 
 			if (kSaveBtn.contains(mx, my)) {
-				if (_mystery.isLoaded())
+				if (_mystery.isLoaded()) {
 					saveProfile(_playerName);
+					setupShowSavedConfirm();
+					dirty = true;
+				}
 				continue;
 			}
 			if (kHelpBtn.contains(mx, my)) {



From noreply at scummvm.org  Sun Jun 21 22:35:54 2026
From: noreply at scummvm.org (sev-)
Date: Sun, 21 Jun 2026 22:35:54 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 50ed7606612f92de1ae50d275ab21426247f7997
Message-ID: <20260621223554.3AC657005CE@ron.scummvm.net>

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

Summary:
a275be078c SDL: Add ImGui Event Recorder rendering hook
0317f20da1 Common: Promote imgui debug channel to global
3467c5fb27 Engines: Clean up duplicate local imgui debug channels
50ed760661 GUI: Implement ImGui Event Recorder controls and cleanup


Commit: a275be078c59cbdc6fff23890f6a2e5caf84df96
    https://github.com/scummvm/scummvm/commit/a275be078c59cbdc6fff23890f6a2e5caf84df96
Author: SHANKHARAJ DATTA (shankharajdatta2004 at gmail.com)
Date: 2026-06-22T00:35:49+02:00

Commit Message:
SDL: Add ImGui Event Recorder rendering hook

Changed paths:
    backends/graphics/sdl/sdl-graphics.cpp


diff --git a/backends/graphics/sdl/sdl-graphics.cpp b/backends/graphics/sdl/sdl-graphics.cpp
index ceb1fbdc387..b3d25e7fd7c 100644
--- a/backends/graphics/sdl/sdl-graphics.cpp
+++ b/backends/graphics/sdl/sdl-graphics.cpp
@@ -34,6 +34,10 @@
 #include "common/translation.h"
 #endif
 
+#if defined(USE_IMGUI) && defined(ENABLE_EVENTRECORDER)
+#include "gui/EventRecorder.h"
+#endif
+
 #ifdef EMSCRIPTEN
 #include "backends/platform/sdl/emscripten/emscripten.h"
 #endif
@@ -718,6 +722,9 @@ void SdlGraphicsManager::renderImGui() {
 
 	ImGui::NewFrame();
 	_imGuiCallbacks.render();
+#if defined(USE_IMGUI) && defined(ENABLE_EVENTRECORDER)
+	g_eventRec.showImGui();
+#endif
 	ImGui::Render();
 #ifdef USE_IMGUI_SDLRENDERER3
 	if (_imGuiSDLRenderer) {


Commit: 0317f20da1db5b83b92b42ad4ac595fe16005c28
    https://github.com/scummvm/scummvm/commit/0317f20da1db5b83b92b42ad4ac595fe16005c28
Author: SHANKHARAJ DATTA (shankharajdatta2004 at gmail.com)
Date: 2026-06-22T00:35:49+02:00

Commit Message:
Common: Promote imgui debug channel to global

Changed paths:
    common/debug.cpp
    common/debug.h


diff --git a/common/debug.cpp b/common/debug.cpp
index 784932c89af..aee96c512e8 100644
--- a/common/debug.cpp
+++ b/common/debug.cpp
@@ -41,6 +41,7 @@ const DebugChannelDef gDebugChannels[] = {
 	{ kDebugLevelGGraphics,  "ggraphics", "debug messages for global graphics" },
 	{ kDebugLevelGVideo,     "gvideo",    "debug messages for global video" },
 	{ kDebugLevelGAudio,     "gaudio",    "debug messages for global audio" },
+	{ kDebugImGui,           "imgui",     "Show ImGui debug window (if available)" },
 	DEBUG_CHANNEL_END
 };
 namespace Common {
diff --git a/common/debug.h b/common/debug.h
index 37736306349..35d9b93e9bb 100644
--- a/common/debug.h
+++ b/common/debug.h
@@ -170,6 +170,7 @@ enum GlobalDebugLevels {
 	kDebugLevelGGraphics,
 	kDebugLevelGVideo,
 	kDebugLevelGAudio,
+	kDebugImGui,
 };
 
 /** @} */


Commit: 3467c5fb272669610d2f47cdbb3e9567e652ccbc
    https://github.com/scummvm/scummvm/commit/3467c5fb272669610d2f47cdbb3e9567e652ccbc
Author: SHANKHARAJ DATTA (shankharajdatta2004 at gmail.com)
Date: 2026-06-22T00:35:49+02:00

Commit Message:
Engines: Clean up duplicate local imgui debug channels

Changed paths:
    engines/director/detection.cpp
    engines/director/director.h
    engines/qdengine/detection.cpp
    engines/qdengine/qdengine.h
    engines/scumm/detection.cpp
    engines/scumm/detection.h
    engines/scumm/scumm.cpp
    engines/twine/detection.cpp
    engines/twine/shared.h
    engines/twp/debugtools.cpp
    engines/twp/detection.cpp
    engines/twp/detection.h
    engines/ultima/detection.cpp
    engines/ultima/ultima.h
    engines/wage/detection.cpp
    engines/wage/wage.h


diff --git a/engines/director/detection.cpp b/engines/director/detection.cpp
index 94a25fb560a..942b8fb2786 100644
--- a/engines/director/detection.cpp
+++ b/engines/director/detection.cpp
@@ -92,7 +92,6 @@ static const DebugChannelDef debugFlagList[] = {
 	{Director::kDebugText, "text", "Text rendering"},
 	{Director::kDebugXObj, "xobj", "XObjects"},
 	{Director::kDebugLingoThe, "lingothe", "Lingo \"the\" entities"},
-	{Director::kDebugImGui, "imgui", "Show ImGui debug window (if available)"},
 	{Director::kDebugPaused, "paused", "Pause first movie right after start"},
 	{Director::kDebugPauseOnLoad, "pauseonload", "Pause every movie right after loading"},
 	{Director::kDebugSaving, "saving", "Show Debug output while saving movies"},
diff --git a/engines/director/director.h b/engines/director/director.h
index eab89866cd8..8c1e02b5211 100644
--- a/engines/director/director.h
+++ b/engines/director/director.h
@@ -90,7 +90,6 @@ enum {
 	kDebugConsole,
 	kDebugXObj,
 	kDebugLingoThe,
-	kDebugImGui,
 	kDebugPaused,
 	kDebugPauseOnLoad,
 	kDebugSaving,
diff --git a/engines/qdengine/detection.cpp b/engines/qdengine/detection.cpp
index ccb5f084ebe..af8c2552138 100644
--- a/engines/qdengine/detection.cpp
+++ b/engines/qdengine/detection.cpp
@@ -34,7 +34,6 @@
 
 static const DebugChannelDef debugFlagList[] = {
 	{ QDEngine::kDebugGraphics, "graphics", "Graphics debug level" },
-	{ QDEngine::kDebugImGui, "imgui", "Imgui debug output"},
 	{ QDEngine::kDebugInput, "input", "Enable user input tracing"},
 	{ QDEngine::kDebugLoad, "load", "Enable load tracing" },
 	{ QDEngine::kDebugLog, "log", "See log messages"},
diff --git a/engines/qdengine/qdengine.h b/engines/qdengine/qdengine.h
index d22ae36d66d..fcec2e4ab8b 100644
--- a/engines/qdengine/qdengine.h
+++ b/engines/qdengine/qdengine.h
@@ -46,7 +46,6 @@ class qdGameDispatcher;
 
 enum QDEngineDebugChannels {
 	kDebugGraphics = 1,
-	kDebugImGui,
 	kDebugInput,
 	kDebugLoad,
 	kDebugLog,
diff --git a/engines/scumm/detection.cpp b/engines/scumm/detection.cpp
index a878f223801..f8f7175e5e4 100644
--- a/engines/scumm/detection.cpp
+++ b/engines/scumm/detection.cpp
@@ -65,7 +65,6 @@ static const DebugChannelDef debugFlagList[] = {
 		{Scumm::DEBUG_SMUSH, "SMUSH", "Track SMUSH"},
 		{Scumm::DEBUG_MOONBASE_AI, "MOONBASEAI", "Track Moonbase AI"},
 		{Scumm::DEBUG_NETWORK, "NETWORK", "Track Networking"},
-		{Scumm::DEBUG_IMGUI, "IMGUI", "Show ImGui debug window (if available)"},
 		DEBUG_CHANNEL_END
 };
 
diff --git a/engines/scumm/detection.h b/engines/scumm/detection.h
index b04b215f58b..24b4896093e 100644
--- a/engines/scumm/detection.h
+++ b/engines/scumm/detection.h
@@ -288,7 +288,6 @@ enum {
 	DEBUG_SMUSH,		// Track SMUSH
 	DEBUG_MOONBASE_AI,	// Moonbase AI
 	DEBUG_NETWORK,		// Track Networking
-	DEBUG_IMGUI,		// Show ImGui debug window (if available)
 };
 
 } // End of namespace Scumm
diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp
index f0bb1a53d36..0c246907ee2 100644
--- a/engines/scumm/scumm.cpp
+++ b/engines/scumm/scumm.cpp
@@ -2800,7 +2800,7 @@ Common::Error ScummEngine::go() {
 #endif // ENABLE_HE
 
 #ifdef USE_IMGUI
-	if (debugChannelSet(-1, DEBUG_IMGUI)) {
+	if (debugChannelSet(-1, kDebugImGui)) {
 		ImGuiCallbacks callbacks;
 		callbacks.init = Editor::onImGuiInit;
 		callbacks.render = Editor::onImGuiRender;
diff --git a/engines/twine/detection.cpp b/engines/twine/detection.cpp
index c85773ec4fb..4647ced9231 100644
--- a/engines/twine/detection.cpp
+++ b/engines/twine/detection.cpp
@@ -38,7 +38,6 @@ static const DebugChannelDef debugFlagList[] = {
 	{TwinE::kDebugScriptsLife, "scriptslife", "Life script debugging"},
 	{TwinE::kDebugResources, "resources", "Resources debugging"},
 	{TwinE::kDebugTimers, "timers", "Timer debugging"},
-	{TwinE::kDebugImGui, "imgui", "UI for debugging"},
 	{TwinE::kDebugInput, "input", "Input debugging"},
 	{TwinE::kDebugMovies, "movies", "Movies debugging"},
 	{TwinE::kDebugPalette, "palette", "Palette debugging"},
diff --git a/engines/twine/shared.h b/engines/twine/shared.h
index 67b2c57d1b8..cded9a8643d 100644
--- a/engines/twine/shared.h
+++ b/engines/twine/shared.h
@@ -811,7 +811,6 @@ enum kDebugLevels {
 	kDebugScriptsLife,
 	kDebugTimers,
 	kDebugResources,
-	kDebugImGui,
 	kDebugInput,
 	kDebugMovies,
 	kDebugPalette,
diff --git a/engines/twp/debugtools.cpp b/engines/twp/debugtools.cpp
index b7cf9d5fbd2..3c953bad42e 100644
--- a/engines/twp/debugtools.cpp
+++ b/engines/twp/debugtools.cpp
@@ -626,7 +626,7 @@ void onImGuiInit() {
 
 void onImGuiRender() {
 	ImGuiIO& io = ImGui::GetIO();
-	if (!debugChannelSet(-1, kDebugConsole)) {
+	if (!debugChannelSet(-1, kDebugImGui)) {
 		io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange | ImGuiConfigFlags_NoMouse;
 		io.MouseDrawCursor = false;
 		return;
diff --git a/engines/twp/detection.cpp b/engines/twp/detection.cpp
index b29b43d8c66..d78cf0656f7 100644
--- a/engines/twp/detection.cpp
+++ b/engines/twp/detection.cpp
@@ -36,7 +36,6 @@ const DebugChannelDef TwpMetaEngineDetection::debugFlagList[] = {
 	{Twp::kDebugActScript, "Actor", "Enable debug actor script dump"},
 	{Twp::kDebugSndScript, "Sound", "Enable debug sound script dump"},
 	{Twp::kDebugGame, "Game", "Game debug level"},
-	{Twp::kDebugConsole, "imgui", "Show ImGui debug window (if available)"},
 	{Twp::kDebugSave, "Save", "Export savegame supported by the original game"},
 	DEBUG_CHANNEL_END};
 
diff --git a/engines/twp/detection.h b/engines/twp/detection.h
index 9924e12f81a..5cacfe3d6c4 100644
--- a/engines/twp/detection.h
+++ b/engines/twp/detection.h
@@ -38,7 +38,6 @@ enum TwpDebugChannels {
 	kDebugActScript,
 	kDebugSndScript,
 	kDebugGame,
-	kDebugConsole,
 	kDebugSave
 };
 
diff --git a/engines/ultima/detection.cpp b/engines/ultima/detection.cpp
index 3128203618c..3741fa257bd 100644
--- a/engines/ultima/detection.cpp
+++ b/engines/ultima/detection.cpp
@@ -34,7 +34,6 @@ const DebugChannelDef UltimaMetaEngineDetection::debugFlagList[] = {
 	{Ultima::kDebugActor, "Actor", "Actor debug level"},
 	{Ultima::kDebugObject, "Object", "Object debug level"},
 	{Ultima::kDebugCollision, "Collision", "Collision debug level"},
-	{Ultima::kDebugImGui, "imgui", "Imgui debug output"},
 	DEBUG_CHANNEL_END
 };
 
diff --git a/engines/ultima/ultima.h b/engines/ultima/ultima.h
index ef8e11b795d..1341184ccda 100644
--- a/engines/ultima/ultima.h
+++ b/engines/ultima/ultima.h
@@ -31,7 +31,6 @@ enum UltimaDebugChannels {
 	kDebugActor,
 	kDebugObject,
 	kDebugCollision,
-	kDebugImGui,
 };
 
 } // End of namespace Ultima
diff --git a/engines/wage/detection.cpp b/engines/wage/detection.cpp
index ef3137ef711..224d8995b59 100644
--- a/engines/wage/detection.cpp
+++ b/engines/wage/detection.cpp
@@ -45,7 +45,6 @@ static const PlainGameDescriptor wageGames[] = {
 #include "wage/detection.h"
 
 static const DebugChannelDef debugFlagList[] = {
-	{ Wage::kDebugImGui,   "imgui",   "Show ImGui debug window (if available)"},
 	{ Wage::kDebugSound,   "sound",   "Show sound debug information"},
 	{ Wage::kDebugLoading, "loading", "Show loading debug information" },
 	DEBUG_CHANNEL_END
diff --git a/engines/wage/wage.h b/engines/wage/wage.h
index 31c970f7506..5a388f90606 100644
--- a/engines/wage/wage.h
+++ b/engines/wage/wage.h
@@ -92,8 +92,7 @@ typedef Common::List<Chr *> ChrList;
 #define STORAGESCENE "STORAGE@"
 
 enum {
-	kDebugImGui = 1,
-	kDebugSound,
+	kDebugSound = 1,
 	kDebugLoading,
 };
 


Commit: 50ed7606612f92de1ae50d275ab21426247f7997
    https://github.com/scummvm/scummvm/commit/50ed7606612f92de1ae50d275ab21426247f7997
Author: SHANKHARAJ DATTA (shankharajdatta2004 at gmail.com)
Date: 2026-06-22T00:35:49+02:00

Commit Message:
GUI: Implement ImGui Event Recorder controls and cleanup

Changed paths:
    gui/EventRecorder.cpp
    gui/EventRecorder.h


diff --git a/gui/EventRecorder.cpp b/gui/EventRecorder.cpp
index e359d162a00..f8ec2dd38cd 100644
--- a/gui/EventRecorder.cpp
+++ b/gui/EventRecorder.cpp
@@ -43,6 +43,11 @@ DECLARE_SINGLETON(GUI::EventRecorder);
 #include "graphics/surface.h"
 #include "graphics/scaler.h"
 
+#ifdef USE_IMGUI
+#include "backends/imgui/imgui.h"
+#include "backends/imgui/IconsMaterialSymbols.h"
+#endif
+
 namespace GUI {
 
 
@@ -94,8 +99,11 @@ void EventRecorder::deinit() {
 	_recordMode = kPassthrough;
 	delete _fakeMixerManager;
 	_fakeMixerManager = nullptr;
-	_controlPanel->close();
-	delete _controlPanel;
+	if (_controlPanel) {
+		_controlPanel->close();
+		delete _controlPanel;
+		_controlPanel = nullptr;
+	}
 	debugC(1, kDebugLevelEventRec, "playback:action=stopplayback");
 	Common::EventDispatcher *eventDispatcher = g_system->getEventManager()->getEventDispatcher();
 	eventDispatcher->unregisterSource(this);
@@ -118,7 +126,9 @@ void EventRecorder::updateFakeTimer(uint32 millis) {
 	uint32 millisDelay = millis - _lastMillis;
 	_lastMillis = millis;
 	_fakeTimer += millisDelay;
-	_controlPanel->setReplayedTime(_fakeTimer);
+	// _controlPanel is null when the ImGui interface is used instead of the legacy OSD
+	if (_controlPanel)
+		_controlPanel->setReplayedTime(_fakeTimer);
 }
 
 void EventRecorder::processTimeAndDate(TimeDate &td, bool skipRecord) {
@@ -220,7 +230,8 @@ void EventRecorder::processMillis(uint32 &millis, bool skipRecord) {
 		updateSubsystems();
 		_nextEvent = _playbackFile->getNextEvent();
 		_timerManager->handler();
-		_controlPanel->setReplayedTime(_fakeTimer);
+		if (_controlPanel)
+			_controlPanel->setReplayedTime(_fakeTimer);
 		_processingMillis = false;
 		break;
 	case kRecorderPlaybackPause:
@@ -280,7 +291,8 @@ void EventRecorder::processScreenUpdate() {
 			takeScreenshot();
 		}
 		_timerManager->handler();
-		_controlPanel->setReplayedTime(_fakeTimer);
+		if (_controlPanel)
+			_controlPanel->setReplayedTime(_fakeTimer);
 		_processingMillis = false;
 		break;
 	default:
@@ -339,12 +351,14 @@ void EventRecorder::togglePause() {
 	case kRecorderUpdate:
 		oldState = _recordMode;
 		_recordMode = kRecorderPlaybackPause;
-		_controlPanel->runModal();
+		if (_controlPanel)
+			_controlPanel->runModal();
 		_recordMode = oldState;
 		_initialized = true;
 		break;
 	case kRecorderPlaybackPause:
-		_controlPanel->close();
+		if (_controlPanel)
+			_controlPanel->close();
 		break;
 	default:
 		break;
@@ -408,14 +422,18 @@ void EventRecorder::init(const Common::String &recordFileName, RecordMode mode)
 	if (_screenshotPeriod == 0) {
 		_screenshotPeriod = kDefaultScreenshotPeriod;
 	}
+	_controlPanel = nullptr;
 	if (!openRecordFile(recordFileName)) {
 		deinit();
 		error("playback:action=error reason=\"Record file loading error\"");
 		return;
 	}
-	if (_recordMode != kPassthrough) {
-		_controlPanel = new GUI::OnScreenDialog(_recordMode == kRecorderRecord);
-		_controlPanel->reflowLayout();
+
+	if (!isImGuiRecorderEnabled()) {
+		if (_recordMode != kPassthrough) {
+			_controlPanel = new GUI::OnScreenDialog(_recordMode == kRecorderRecord);
+			_controlPanel->reflowLayout();
+		}
 	}
 	if ((_recordMode == kRecorderPlayback) || (_recordMode == kRecorderUpdate)) {
 		applyPlaybackSettings();
@@ -580,55 +598,70 @@ void EventRecorder::updateSubsystems() {
 }
 
 bool EventRecorder::notifyEvent(const Common::Event &ev) {
-	if ((!_initialized) && (_recordMode != kRecorderPlaybackPause)) {
+	if (!_initialized && _recordMode != kRecorderPlaybackPause)
 		return false;
-	}
 
 	checkForKeyCode(ev);
 	Common::Event evt = ev;
 	evt.mouse.x = evt.mouse.x * (g_system->getOverlayWidth() / g_system->getWidth());
 	evt.mouse.y = evt.mouse.y * (g_system->getOverlayHeight() / g_system->getHeight());
-	switch (_recordMode) {
-	case kRecorderUpdate: // passthrough
-	case kRecorderPlayback:
-		// pass through screen updates to avoid loss of sync!
-		if (evt.type == Common::EVENT_SCREEN_CHANGED)
-			g_gui.processEvent(evt, _controlPanel);
-		if (_recordMode == kRecorderUpdate) {
-			// write a copy of the event to the output buffer
+
+	if (isImGuiRecorderEnabled()) {
+		if (_recordMode == kRecorderPlaybackPause)
+			return false;
+
+		if (_recordMode == kRecorderRecord || _recordMode == kRecorderUpdate) {
 			Common::RecorderEvent e(ev);
 			e.recordedtype = Common::kRecorderEventTypeNormal;
 			e.time = _fakeTimer;
 			_recordFile->writeEvent(e);
 		}
 		return false;
-	case kRecorderRecord:
-		g_gui.processEvent(evt, _controlPanel);
-		if (((evt.type == Common::EVENT_LBUTTONDOWN) || (evt.type == Common::EVENT_LBUTTONUP) || (evt.type == Common::EVENT_MOUSEMOVE)) && _controlPanel->isMouseOver()) {
+	}
+
+	switch (_recordMode) {
+	case kRecorderUpdate:
+	case kRecorderPlayback:
+		if (evt.type == Common::EVENT_SCREEN_CHANGED && _controlPanel)
+			g_gui.processEvent(evt, _controlPanel);
+		break;
+
+	case kRecorderRecord: {
+		if (_controlPanel)
+			g_gui.processEvent(evt, _controlPanel);
+		
+		if (_controlPanel && ((evt.type == Common::EVENT_LBUTTONDOWN) || (evt.type == Common::EVENT_LBUTTONUP) || (evt.type == Common::EVENT_MOUSEMOVE)) && _controlPanel->isMouseOver())
 			return true;
-		} else {
-			Common::RecorderEvent e(ev);
-			e.recordedtype = Common::kRecorderEventTypeNormal;
-			e.time = _fakeTimer;
-			_recordFile->writeEvent(e);
-			return false;
-		}
+
+		Common::RecorderEvent e(ev);
+		e.recordedtype = Common::kRecorderEventTypeNormal;
+		e.time = _fakeTimer;
+		_recordFile->writeEvent(e);
+		return false;
+	}
+
 	case kRecorderPlaybackPause: {
-		Common::Event dialogEvent;
-		if (_controlPanel->isEditDlgVisible()) {
-			dialogEvent = ev;
-		} else {
-			dialogEvent = evt;
-		}
-		g_gui.processEvent(dialogEvent, _controlPanel->getActiveDlg());
-		if (((dialogEvent.type == Common::EVENT_LBUTTONDOWN) || (dialogEvent.type == Common::EVENT_LBUTTONUP) || (dialogEvent.type == Common::EVENT_MOUSEMOVE)) && _controlPanel->isMouseOver()) {
-			return true;
+		if (_controlPanel) {
+			Common::Event dialogEvent = _controlPanel->isEditDlgVisible() ? ev : evt;
+			g_gui.processEvent(dialogEvent, _controlPanel->getActiveDlg());
+			if (((dialogEvent.type == Common::EVENT_LBUTTONDOWN) || (dialogEvent.type == Common::EVENT_LBUTTONUP) || (dialogEvent.type == Common::EVENT_MOUSEMOVE)) && _controlPanel->isMouseOver())
+				return true;
 		}
 		return false;
 	}
 	default:
-		return false;
+		break;
 	}
+
+	// Default behavior for Update/Playback when not handled above
+	if (_recordMode == kRecorderUpdate) {
+		Common::RecorderEvent e(ev);
+		e.recordedtype = Common::kRecorderEventTypeNormal;
+		e.time = _fakeTimer;
+		_recordFile->writeEvent(e);
+	}
+
+	return false;
 }
 
 void EventRecorder::setGameMd5(const ADGameDescription *gameDesc) {
@@ -703,6 +736,9 @@ Common::SaveFileManager *EventRecorder::getSaveManager(Common::SaveFileManager *
 }
 
 void EventRecorder::preDrawOverlayGui() {
+	if (isImGuiRecorderEnabled())
+		return;
+
 	if ((_initialized) || (_needRedraw)) {
 		RecordMode oldMode = _recordMode;
 		_recordMode = kPassthrough;
@@ -720,6 +756,9 @@ void EventRecorder::preDrawOverlayGui() {
 }
 
 void EventRecorder::postDrawOverlayGui() {
+	if (isImGuiRecorderEnabled())
+		return;
+
 	if ((_initialized) || (_needRedraw)) {
 		RecordMode oldMode = _recordMode;
 		_recordMode = kPassthrough;
@@ -820,6 +859,108 @@ void EventRecorder::deleteTemporarySave() {
 	_temporarySlot = -1;
 }
 
+bool EventRecorder::isImGuiRecorderEnabled() const {
+#ifdef USE_IMGUI
+	return debugChannelSet(-1, kDebugImGui);
+#else
+	return false;
+#endif
+}
+
+#ifdef USE_IMGUI
+
+void EventRecorder::showImGui() {
+	if (_recordMode == kPassthrough || !_initialized)
+		return;
+
+	if (!isImGuiRecorderEnabled())
+		return;
+
+	// --- Design ---
+	const float windowWidth = 190.0f;
+	const ImVec2 btnSize(34, 24);
+	const ImVec4 colorRed(0.95f, 0.25f, 0.25f, 1.0f);
+	const ImVec4 colorBlue(0.25f, 0.75f, 0.95f, 1.0f);
+	const ImVec4 colorAmber(0.95f, 0.75f, 0.10f, 1.0f);
+
+	ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(10, 8));
+	ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing,   ImVec2(6, 6));
+	ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 6.0f);
+	ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(0.12f, 0.12f, 0.15f, 0.96f));
+
+	ImGui::SetNextWindowSize(ImVec2(windowWidth, 0));
+	ImGui::SetNextWindowPos(ImVec2(15, 15), ImGuiCond_FirstUseEver);
+	
+	if (ImGui::Begin("Recorder", nullptr,
+		ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_AlwaysAutoResize | 
+		ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoTitleBar)) {
+
+		bool isRecording = (_recordMode == kRecorderRecord);
+		bool isPaused    = (_recordMode == kRecorderPlaybackPause);
+		bool isPlayback  = (_recordMode == kRecorderPlayback || _recordMode == kRecorderUpdate);
+
+		// --- Status & Timer ---
+		ImVec4 modeColor = isRecording ? colorRed : (isPaused ? colorAmber : colorBlue);
+		const char *modeIcon = isRecording ? ICON_MS_FIBER_MANUAL_RECORD : (isPaused ? ICON_MS_PAUSE_CIRCLE : ICON_MS_PLAY_CIRCLE);
+		
+		ImGui::TextColored(modeColor, "%s", modeIcon);
+		ImGui::SameLine();
+		ImGui::Text("%s", isRecording ? "REC" : (isPaused ? "PAUSE" : "PLAY"));
+		
+		uint32 secs = _fakeTimer / 1000;
+		ImGui::SameLine(ImGui::GetContentRegionAvail().x - ImGui::CalcTextSize("00:00:00").x + 8);
+		ImGui::TextDisabled("%02u:%02u:%02u", secs / 3600, (secs % 3600) / 60, secs % 60);
+
+		// --- Controls ---
+		float totalButtonsWidth = (btnSize.x * 3) + (ImGui::GetStyle().ItemSpacing.x * 2);
+		ImGui::SetCursorPosX((windowWidth - totalButtonsWidth) * 0.5f);
+
+		if (ImGui::Button(ICON_MS_STOP, btnSize)) {
+			deinit();
+		}
+		ImGui::SetItemTooltip("Stop");
+		ImGui::SameLine();
+
+		const char *pauseIcon = isPaused ? ICON_MS_PLAY_ARROW : ICON_MS_PAUSE;
+		if (isPaused) {
+			ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(colorAmber.x, colorAmber.y, colorAmber.z, 0.4f));
+		}
+		if (ImGui::Button(pauseIcon, btnSize)) {
+			togglePause();
+		}
+		if (isPaused) {
+			ImGui::PopStyleColor();
+		}
+		ImGui::SetItemTooltip(isPaused ? "Resume" : "Pause");
+		ImGui::SameLine();
+
+		bool canFF = (isPlayback || isPaused);
+		if (!canFF) {
+			ImGui::BeginDisabled();
+		}
+		const char *ffIcon = _fastPlayback ? ICON_MS_FAST_REWIND : ICON_MS_FAST_FORWARD;
+		if (_fastPlayback) {
+			ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(colorBlue.x, colorBlue.y, colorBlue.z, 0.4f));
+		}
+		if (ImGui::Button(ffIcon, btnSize)) {
+			setFastPlayback(!_fastPlayback);
+		}
+		if (_fastPlayback) {
+			ImGui::PopStyleColor();
+		}
+		ImGui::SetItemTooltip(_fastPlayback ? "Normal" : "Fast");
+		if (!canFF) {
+			ImGui::EndDisabled();
+		}
+	}
+	ImGui::End();
+
+	ImGui::PopStyleColor();
+	ImGui::PopStyleVar(3);
+}
+#endif // USE_IMGUI
+
 } // End of namespace GUI
 
 #endif // ENABLE_EVENTRECORDER
+
diff --git a/gui/EventRecorder.h b/gui/EventRecorder.h
index a68dd4bbdbe..c76738d3d90 100644
--- a/gui/EventRecorder.h
+++ b/gui/EventRecorder.h
@@ -188,7 +188,16 @@ public:
 	bool switchMode();
 	void switchFastMode();
 
+#ifdef USE_IMGUI
+	void showImGui();
+#endif
+
 private:
+
+#ifdef USE_IMGUI
+	bool isImGuiRecorderEnabled() const;
+#endif
+
 	bool pollEvent(Common::Event &ev) override;
 	bool notifyEvent(const Common::Event &event) override;
 	bool _initialized;



From noreply at scummvm.org  Sun Jun 21 22:35:59 2026
From: noreply at scummvm.org (ScummVM-Translations)
Date: Sun, 21 Jun 2026 22:35:59 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 87533da1e58ed7eab0e9b97c784bfc098a665fd6
Message-ID: <20260621223559.15A817005D3@ron.scummvm.net>

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

Summary:
87533da1e5 I18N: Update translations templates


Commit: 87533da1e58ed7eab0e9b97c784bfc098a665fd6
    https://github.com/scummvm/scummvm/commit/87533da1e58ed7eab0e9b97c784bfc098a665fd6
Author: Weblate (noreply at weblate.org)
Date: 2026-06-21T22:35:54Z

Commit Message:
I18N: Update translations templates

Changed paths:
    po/scummvm.pot


diff --git a/po/scummvm.pot b/po/scummvm.pot
index 9359e078e10..102877ad78f 100644
--- a/po/scummvm.pot
+++ b/po/scummvm.pot
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2026.3.1git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-21 22:22+0000\n"
+"POT-Creation-Date: 2026-06-21 22:35+0000\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL at ADDRESS>\n"
 "Language-Team: LANGUAGE <LL at li.org>\n"
@@ -571,7 +571,7 @@ msgstr ""
 
 #: gui/editgamedialog.cpp:164 gui/options.cpp:2075 gui/options.cpp:2256
 #: backends/graphics/atari/atari-graphics.cpp:1056
-#: backends/graphics/sdl/sdl-graphics.cpp:487
+#: backends/graphics/sdl/sdl-graphics.cpp:491
 msgid "Graphics"
 msgstr ""
 
@@ -1797,7 +1797,7 @@ msgstr ""
 msgid "Check on the scummvm.org website for updates of shader packs"
 msgstr ""
 
-#: gui/options.cpp:1700 backends/graphics/sdl/sdl-graphics.cpp:478
+#: gui/options.cpp:1700 backends/graphics/sdl/sdl-graphics.cpp:482
 msgid "Fullscreen mode"
 msgstr ""
 
@@ -4010,7 +4010,7 @@ msgid "Choose a new folder"
 msgstr ""
 
 #: backends/graphics/atari/atari-graphics.cpp:1060
-#: backends/graphics/sdl/sdl-graphics.cpp:514 engines/scumm/help.cpp:103
+#: backends/graphics/sdl/sdl-graphics.cpp:518 engines/scumm/help.cpp:103
 msgid "Toggle aspect ratio correction"
 msgstr ""
 
@@ -4082,34 +4082,34 @@ msgstr ""
 msgid "SDL Surface (forced 8bpp mode)"
 msgstr ""
 
-#: backends/graphics/sdl/sdl-graphics.cpp:420
+#: backends/graphics/sdl/sdl-graphics.cpp:424
 #, c-format
 msgid "Saved screenshot '%s'"
 msgstr ""
 
-#: backends/graphics/sdl/sdl-graphics.cpp:435
+#: backends/graphics/sdl/sdl-graphics.cpp:439
 msgid "Could not save screenshot"
 msgstr ""
 
-#: backends/graphics/sdl/sdl-graphics.cpp:480
+#: backends/graphics/sdl/sdl-graphics.cpp:484
 msgid "Windowed mode"
 msgstr ""
 
-#: backends/graphics/sdl/sdl-graphics.cpp:491 engines/colony/metaengine.cpp:173
+#: backends/graphics/sdl/sdl-graphics.cpp:495 engines/colony/metaengine.cpp:173
 #: engines/scumm/help.cpp:88
 msgid "Toggle fullscreen"
 msgstr ""
 
-#: backends/graphics/sdl/sdl-graphics.cpp:498 engines/scumm/help.cpp:100
+#: backends/graphics/sdl/sdl-graphics.cpp:502 engines/scumm/help.cpp:100
 #: engines/wintermute/keymapper_tables.h:960
 msgid "Toggle mouse capture"
 msgstr ""
 
-#: backends/graphics/sdl/sdl-graphics.cpp:503
+#: backends/graphics/sdl/sdl-graphics.cpp:507
 msgid "Toggle resizable window"
 msgstr ""
 
-#: backends/graphics/sdl/sdl-graphics.cpp:508 engines/stark/metaengine.cpp:240
+#: backends/graphics/sdl/sdl-graphics.cpp:512 engines/stark/metaengine.cpp:240
 #: engines/wintermute/keymapper_tables.h:646
 #: engines/wintermute/keymapper_tables.h:695
 #: engines/wintermute/keymapper_tables.h:1098
@@ -4122,27 +4122,27 @@ msgstr ""
 msgid "Save screenshot"
 msgstr ""
 
-#: backends/graphics/sdl/sdl-graphics.cpp:521
+#: backends/graphics/sdl/sdl-graphics.cpp:525
 msgid "Toggle linear filtered scaling"
 msgstr ""
 
-#: backends/graphics/sdl/sdl-graphics.cpp:528
+#: backends/graphics/sdl/sdl-graphics.cpp:532
 msgid "Cycle through stretch modes"
 msgstr ""
 
-#: backends/graphics/sdl/sdl-graphics.cpp:534
+#: backends/graphics/sdl/sdl-graphics.cpp:538
 msgid "Increase the scale factor"
 msgstr ""
 
-#: backends/graphics/sdl/sdl-graphics.cpp:540
+#: backends/graphics/sdl/sdl-graphics.cpp:544
 msgid "Decrease the scale factor"
 msgstr ""
 
-#: backends/graphics/sdl/sdl-graphics.cpp:547
+#: backends/graphics/sdl/sdl-graphics.cpp:551
 msgid "Switch to the next scaler"
 msgstr ""
 
-#: backends/graphics/sdl/sdl-graphics.cpp:552
+#: backends/graphics/sdl/sdl-graphics.cpp:556
 msgid "Switch to the previous scaler"
 msgstr ""
 
@@ -13974,7 +13974,7 @@ msgstr ""
 msgid "Toggle text speech"
 msgstr ""
 
-#: engines/twine/detection.cpp:188
+#: engines/twine/detection.cpp:187
 msgid "This version requires Giflib which was not compiled into ScummVM"
 msgstr ""
 



From noreply at scummvm.org  Sun Jun 21 22:36:33 2026
From: noreply at scummvm.org (ScummVM-Translations)
Date: Sun, 21 Jun 2026 22:36:33 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 684744fdf938962db5dd88c2631e041e9a36b3f1
Message-ID: <20260621223633.EB9757005CE@ron.scummvm.net>

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

Summary:
684744fdf9 I18N: Update translation files


Commit: 684744fdf938962db5dd88c2631e041e9a36b3f1
    https://github.com/scummvm/scummvm/commit/684744fdf938962db5dd88c2631e041e9a36b3f1
Author: Weblate (noreply at weblate.org)
Date: 2026-06-21T22:36:12Z

Commit Message:
I18N: Update translation files

Updated by "Update PO files to match POT (msgmerge)" hook in Weblate.

Translation: ScummVM/scummvm
Translate-URL: https://translations.scummvm.org/projects/scummvm/scummvm/

Changed paths:
    po/ar.po
    po/be-tarask.po
    po/be_BY.po
    po/ca_ES.po
    po/cs_CZ.po
    po/da.po
    po/de_DE.po
    po/el.po
    po/es_ES.po
    po/eu.po
    po/fi_FI.po
    po/fr_FR.po
    po/gl_ES.po
    po/he.po
    po/hi.po
    po/hu_HU.po
    po/it_IT.po
    po/ja.po
    po/ka.po
    po/ko.po
    po/nb_NO.po
    po/nl_NL.po
    po/nn_NO.po
    po/pl_PL.po
    po/pt_BR.po
    po/pt_PT.po
    po/ro.po
    po/ru_RU.po
    po/sv_SE.po
    po/tr.po
    po/uk_UA.po
    po/zh.po
    po/zh_Hans.po
    po/zh_Hant.po


diff --git a/po/ar.po b/po/ar.po
index dfba19502f4..ea320e4363c 100644
--- a/po/ar.po
+++ b/po/ar.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-21 22:22+0000\n"
+"POT-Creation-Date: 2026-06-21 22:35+0000\n"
 "PO-Revision-Date: 2026-02-24 22:26+0000\n"
 "Last-Translator: Mohamed Shaaban <mohmedbashamedo at hotmail.com>\n"
 "Language-Team: Arabic <https://translations.scummvm.org/projects/scummvm/"
@@ -612,7 +612,7 @@ msgstr "GFX"
 
 #: gui/editgamedialog.cpp:164 gui/options.cpp:2075 gui/options.cpp:2256
 #: backends/graphics/atari/atari-graphics.cpp:1056
-#: backends/graphics/sdl/sdl-graphics.cpp:487
+#: backends/graphics/sdl/sdl-graphics.cpp:491
 msgid "Graphics"
 msgstr "????????????????"
 
@@ -1882,7 +1882,7 @@ msgstr "?????????? ?????????? ????????????"
 msgid "Check on the scummvm.org website for updates of shader packs"
 msgstr "???????? ???? ???????? ?????????????? ?????????? ??????????????"
 
-#: gui/options.cpp:1700 backends/graphics/sdl/sdl-graphics.cpp:478
+#: gui/options.cpp:1700 backends/graphics/sdl/sdl-graphics.cpp:482
 msgid "Fullscreen mode"
 msgstr "?????? ?????? ????????????"
 
@@ -4361,7 +4361,7 @@ msgid "Choose a new folder"
 msgstr "???????? ???????????? ????????????"
 
 #: backends/graphics/atari/atari-graphics.cpp:1060
-#: backends/graphics/sdl/sdl-graphics.cpp:514 engines/scumm/help.cpp:103
+#: backends/graphics/sdl/sdl-graphics.cpp:518 engines/scumm/help.cpp:103
 msgid "Toggle aspect ratio correction"
 msgstr "?????????? ?????????? ???????? ?????????? ?????? ????????????????"
 
@@ -4433,34 +4433,34 @@ msgstr "?????? SDL"
 msgid "SDL Surface (forced 8bpp mode)"
 msgstr ""
 
-#: backends/graphics/sdl/sdl-graphics.cpp:420
+#: backends/graphics/sdl/sdl-graphics.cpp:424
 #, c-format
 msgid "Saved screenshot '%s'"
 msgstr "???????? ???????? ???????????? '%s'"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:435
+#: backends/graphics/sdl/sdl-graphics.cpp:439
 msgid "Could not save screenshot"
 msgstr "???????? ?????? ???????? ????????????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:480
+#: backends/graphics/sdl/sdl-graphics.cpp:484
 msgid "Windowed mode"
 msgstr "?????? ????????????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:491 engines/colony/metaengine.cpp:173
+#: backends/graphics/sdl/sdl-graphics.cpp:495 engines/colony/metaengine.cpp:173
 #: engines/scumm/help.cpp:88
 msgid "Toggle fullscreen"
 msgstr "?????????? ?????? ????????????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:498 engines/scumm/help.cpp:100
+#: backends/graphics/sdl/sdl-graphics.cpp:502 engines/scumm/help.cpp:100
 #: engines/wintermute/keymapper_tables.h:960
 msgid "Toggle mouse capture"
 msgstr "?????????? ???????????? ????????????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:503
+#: backends/graphics/sdl/sdl-graphics.cpp:507
 msgid "Toggle resizable window"
 msgstr ""
 
-#: backends/graphics/sdl/sdl-graphics.cpp:508 engines/stark/metaengine.cpp:240
+#: backends/graphics/sdl/sdl-graphics.cpp:512 engines/stark/metaengine.cpp:240
 #: engines/wintermute/keymapper_tables.h:646
 #: engines/wintermute/keymapper_tables.h:695
 #: engines/wintermute/keymapper_tables.h:1098
@@ -4473,27 +4473,27 @@ msgstr ""
 msgid "Save screenshot"
 msgstr "?????? ???????? ????????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:521
+#: backends/graphics/sdl/sdl-graphics.cpp:525
 msgid "Toggle linear filtered scaling"
 msgstr "?????????? ?????????????? ?????????? ??????????????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:528
+#: backends/graphics/sdl/sdl-graphics.cpp:532
 msgid "Cycle through stretch modes"
 msgstr "???????????? ?????? ?????????? ????????????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:534
+#: backends/graphics/sdl/sdl-graphics.cpp:538
 msgid "Increase the scale factor"
 msgstr "?????????? ?????????? ????????????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:540
+#: backends/graphics/sdl/sdl-graphics.cpp:544
 msgid "Decrease the scale factor"
 msgstr "?????????? ???????? ????????????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:547
+#: backends/graphics/sdl/sdl-graphics.cpp:551
 msgid "Switch to the next scaler"
 msgstr "???? ???????????????? ?????? ?????????????? ??????????????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:552
+#: backends/graphics/sdl/sdl-graphics.cpp:556
 msgid "Switch to the previous scaler"
 msgstr "???? ???????????????? ?????? ?????????????? ??????????????"
 
@@ -16147,7 +16147,7 @@ msgstr "?????????? ?????????? ????????????"
 msgid "Toggle text speech"
 msgstr "???????????? ?????????? ???????? ?????? ????????"
 
-#: engines/twine/detection.cpp:188
+#: engines/twine/detection.cpp:187
 msgid "This version requires Giflib which was not compiled into ScummVM"
 msgstr "?????????? ?????? ?????????????? ?????? Giflib ???????? ???? ?????? ???????????? ???? ScummVM"
 
diff --git a/po/be-tarask.po b/po/be-tarask.po
index c706b84260f..887c94621ee 100644
--- a/po/be-tarask.po
+++ b/po/be-tarask.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-21 22:22+0000\n"
+"POT-Creation-Date: 2026-06-21 22:35+0000\n"
 "PO-Revision-Date: 2023-02-26 15:01+0000\n"
 "Last-Translator: Kastu?? K. <oakum2334 at gmail.com>\n"
 "Language-Team: Belarusian (Tara??kievica) <https://translations.scummvm.org/"
@@ -621,7 +621,7 @@ msgstr "GFX"
 
 #: gui/editgamedialog.cpp:164 gui/options.cpp:2075 gui/options.cpp:2256
 #: backends/graphics/atari/atari-graphics.cpp:1056
-#: backends/graphics/sdl/sdl-graphics.cpp:487
+#: backends/graphics/sdl/sdl-graphics.cpp:491
 msgid "Graphics"
 msgstr "??????????????"
 
@@ -1899,7 +1899,7 @@ msgstr "???????????????????? ?????????????????? ??????????"
 msgid "Check on the scummvm.org website for updates of shader packs"
 msgstr "?????????????????? ????????????..."
 
-#: gui/options.cpp:1700 backends/graphics/sdl/sdl-graphics.cpp:478
+#: gui/options.cpp:1700 backends/graphics/sdl/sdl-graphics.cpp:482
 msgid "Fullscreen mode"
 msgstr "?????????? ?????????????? ????????????"
 
@@ -4323,7 +4323,7 @@ msgid "Choose a new folder"
 msgstr "???????????????? ???????? ?????? ????????????????????"
 
 #: backends/graphics/atari/atari-graphics.cpp:1060
-#: backends/graphics/sdl/sdl-graphics.cpp:514 engines/scumm/help.cpp:103
+#: backends/graphics/sdl/sdl-graphics.cpp:518 engines/scumm/help.cpp:103
 msgid "Toggle aspect ratio correction"
 msgstr "???????????????????????? ???????????????? ???????????????????? ????????????"
 
@@ -4395,34 +4395,34 @@ msgstr "???????????????? SDL"
 msgid "SDL Surface (forced 8bpp mode)"
 msgstr ""
 
-#: backends/graphics/sdl/sdl-graphics.cpp:420
+#: backends/graphics/sdl/sdl-graphics.cpp:424
 #, c-format
 msgid "Saved screenshot '%s'"
 msgstr "???????????????? ???????????? ???????????? '%s'"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:435
+#: backends/graphics/sdl/sdl-graphics.cpp:439
 msgid "Could not save screenshot"
 msgstr "???? ???????????? ???????????????? ???????????? ????????????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:480
+#: backends/graphics/sdl/sdl-graphics.cpp:484
 msgid "Windowed mode"
 msgstr "???????????? ??????????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:491 engines/colony/metaengine.cpp:173
+#: backends/graphics/sdl/sdl-graphics.cpp:495 engines/colony/metaengine.cpp:173
 #: engines/scumm/help.cpp:88
 msgid "Toggle fullscreen"
 msgstr "???????????????????????? ???????????? ?????????????? ????????????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:498 engines/scumm/help.cpp:100
+#: backends/graphics/sdl/sdl-graphics.cpp:502 engines/scumm/help.cpp:100
 #: engines/wintermute/keymapper_tables.h:960
 msgid "Toggle mouse capture"
 msgstr "???????????????????????? ???????????? ????????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:503
+#: backends/graphics/sdl/sdl-graphics.cpp:507
 msgid "Toggle resizable window"
 msgstr ""
 
-#: backends/graphics/sdl/sdl-graphics.cpp:508 engines/stark/metaengine.cpp:240
+#: backends/graphics/sdl/sdl-graphics.cpp:512 engines/stark/metaengine.cpp:240
 #: engines/wintermute/keymapper_tables.h:646
 #: engines/wintermute/keymapper_tables.h:695
 #: engines/wintermute/keymapper_tables.h:1098
@@ -4435,27 +4435,27 @@ msgstr ""
 msgid "Save screenshot"
 msgstr "???????????????? ???????????? ????????????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:521
+#: backends/graphics/sdl/sdl-graphics.cpp:525
 msgid "Toggle linear filtered scaling"
 msgstr "???????????????????????? ?????????????????? ?????????????????????????? ?????? ????????????????????????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:528
+#: backends/graphics/sdl/sdl-graphics.cpp:532
 msgid "Cycle through stretch modes"
 msgstr "???????????????????????? ???????????? ????????????????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:534
+#: backends/graphics/sdl/sdl-graphics.cpp:538
 msgid "Increase the scale factor"
 msgstr "???????????????? ???????????????????? ????????????????????????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:540
+#: backends/graphics/sdl/sdl-graphics.cpp:544
 msgid "Decrease the scale factor"
 msgstr "???????????????? ???????????????????? ????????????????????????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:547
+#: backends/graphics/sdl/sdl-graphics.cpp:551
 msgid "Switch to the next scaler"
 msgstr "???????????????????????? ???? ???????????????? ????????????????????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:552
+#: backends/graphics/sdl/sdl-graphics.cpp:556
 msgid "Switch to the previous scaler"
 msgstr "???????????????????????? ???? ?????????????????? ????????????????????"
 
@@ -16077,7 +16077,7 @@ msgstr "???????????????????????? ?????????????? ????????????"
 msgid "Toggle text speech"
 msgstr "?????????????????????? ?????????? ?? ????????"
 
-#: engines/twine/detection.cpp:188
+#: engines/twine/detection.cpp:187
 msgid "This version requires Giflib which was not compiled into ScummVM"
 msgstr "?????? ?????????? ???????????? ???????????????? Giflib, ???????? ???????? ?? ?????????? ScummVM"
 
diff --git a/po/be_BY.po b/po/be_BY.po
index a533ffb6f38..7093ff8f583 100644
--- a/po/be_BY.po
+++ b/po/be_BY.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-21 22:22+0000\n"
+"POT-Creation-Date: 2026-06-21 22:35+0000\n"
 "PO-Revision-Date: 2021-03-21 08:24+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Belarusian <https://translations.scummvm.org/projects/scummvm/"
@@ -619,7 +619,7 @@ msgstr "??????"
 
 #: gui/editgamedialog.cpp:164 gui/options.cpp:2075 gui/options.cpp:2256
 #: backends/graphics/atari/atari-graphics.cpp:1056
-#: backends/graphics/sdl/sdl-graphics.cpp:487
+#: backends/graphics/sdl/sdl-graphics.cpp:491
 msgid "Graphics"
 msgstr "??????????????"
 
@@ -1905,7 +1905,7 @@ msgstr "???????????????????? ?????????? ????????????"
 msgid "Check on the scummvm.org website for updates of shader packs"
 msgstr "???????????????? ????????????????????..."
 
-#: gui/options.cpp:1700 backends/graphics/sdl/sdl-graphics.cpp:478
+#: gui/options.cpp:1700 backends/graphics/sdl/sdl-graphics.cpp:482
 msgid "Fullscreen mode"
 msgstr "???????????????????????? ??????????"
 
@@ -4325,7 +4325,7 @@ msgid "Choose a new folder"
 msgstr "?????????????? ???????? ?????? ????????????????"
 
 #: backends/graphics/atari/atari-graphics.cpp:1060
-#: backends/graphics/sdl/sdl-graphics.cpp:514 engines/scumm/help.cpp:103
+#: backends/graphics/sdl/sdl-graphics.cpp:518 engines/scumm/help.cpp:103
 msgid "Toggle aspect ratio correction"
 msgstr "?????????????????????? ???????????????? ?????????????????? ??????????"
 
@@ -4398,35 +4398,35 @@ msgstr ""
 msgid "SDL Surface (forced 8bpp mode)"
 msgstr ""
 
-#: backends/graphics/sdl/sdl-graphics.cpp:420
+#: backends/graphics/sdl/sdl-graphics.cpp:424
 #, fuzzy, c-format
 msgid "Saved screenshot '%s'"
 msgstr "???????????????? ????????????????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:435
+#: backends/graphics/sdl/sdl-graphics.cpp:439
 #, fuzzy
 msgid "Could not save screenshot"
 msgstr "???????????????? ????????????????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:480
+#: backends/graphics/sdl/sdl-graphics.cpp:484
 msgid "Windowed mode"
 msgstr "???????????? ??????????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:491 engines/colony/metaengine.cpp:173
+#: backends/graphics/sdl/sdl-graphics.cpp:495 engines/colony/metaengine.cpp:173
 #: engines/scumm/help.cpp:88
 msgid "Toggle fullscreen"
 msgstr "???????????????????????? ???? ?????????? ??????????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:498 engines/scumm/help.cpp:100
+#: backends/graphics/sdl/sdl-graphics.cpp:502 engines/scumm/help.cpp:100
 #: engines/wintermute/keymapper_tables.h:960
 msgid "Toggle mouse capture"
 msgstr "???????????????????????? ???????????????? ????????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:503
+#: backends/graphics/sdl/sdl-graphics.cpp:507
 msgid "Toggle resizable window"
 msgstr ""
 
-#: backends/graphics/sdl/sdl-graphics.cpp:508 engines/stark/metaengine.cpp:240
+#: backends/graphics/sdl/sdl-graphics.cpp:512 engines/stark/metaengine.cpp:240
 #: engines/wintermute/keymapper_tables.h:646
 #: engines/wintermute/keymapper_tables.h:695
 #: engines/wintermute/keymapper_tables.h:1098
@@ -4439,28 +4439,28 @@ msgstr ""
 msgid "Save screenshot"
 msgstr "???????????????? ????????????????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:521
+#: backends/graphics/sdl/sdl-graphics.cpp:525
 msgid "Toggle linear filtered scaling"
 msgstr "?????????????????????? ???????????????? ???????????????????? ?????? ?????? ????????????????????????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:528
+#: backends/graphics/sdl/sdl-graphics.cpp:532
 msgid "Cycle through stretch modes"
 msgstr "?????????????????????? ?????????? ??????????????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:534
+#: backends/graphics/sdl/sdl-graphics.cpp:538
 msgid "Increase the scale factor"
 msgstr "???????????????????? ????????????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:540
+#: backends/graphics/sdl/sdl-graphics.cpp:544
 msgid "Decrease the scale factor"
 msgstr "?????????????????? ????????????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:547
+#: backends/graphics/sdl/sdl-graphics.cpp:551
 #, fuzzy
 msgid "Switch to the next scaler"
 msgstr "???????????????? ???? ???????????????????????? TV 2x"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:552
+#: backends/graphics/sdl/sdl-graphics.cpp:556
 #, fuzzy
 msgid "Switch to the previous scaler"
 msgstr "???????????????? ???? ???????????????????????? ???? ???????????????????? ??????????????"
@@ -15824,7 +15824,7 @@ msgstr "?????????????????????? ?????????? ??????????"
 msgid "Toggle text speech"
 msgstr "?????????????? ?????????????????? ????????"
 
-#: engines/twine/detection.cpp:188
+#: engines/twine/detection.cpp:187
 #, fuzzy
 msgid "This version requires Giflib which was not compiled into ScummVM"
 msgstr "???????? ???????????? ???????????????? ?????????????????? FoxTail, ?????????? ???????? ?? ????????????."
diff --git a/po/ca_ES.po b/po/ca_ES.po
index 8da36c0aa6a..0de426fdca3 100644
--- a/po/ca_ES.po
+++ b/po/ca_ES.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.6.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-21 22:22+0000\n"
+"POT-Creation-Date: 2026-06-21 22:35+0000\n"
 "PO-Revision-Date: 2026-05-26 04:22+0000\n"
 "Last-Translator: Xaviu <xaviu at xaviu.com>\n"
 "Language-Team: Catalan <https://translations.scummvm.org/projects/scummvm/"
@@ -593,7 +593,7 @@ msgstr "GFX"
 
 #: gui/editgamedialog.cpp:164 gui/options.cpp:2075 gui/options.cpp:2256
 #: backends/graphics/atari/atari-graphics.cpp:1056
-#: backends/graphics/sdl/sdl-graphics.cpp:487
+#: backends/graphics/sdl/sdl-graphics.cpp:491
 msgid "Graphics"
 msgstr "Gr??fics"
 
@@ -1854,7 +1854,7 @@ msgstr ""
 "Comprova al web de scummvm.org si hi ha actualitzacions dels paquets de "
 "\"shader\""
 
-#: gui/options.cpp:1700 backends/graphics/sdl/sdl-graphics.cpp:478
+#: gui/options.cpp:1700 backends/graphics/sdl/sdl-graphics.cpp:482
 msgid "Fullscreen mode"
 msgstr "Mode pantalla completa"
 
@@ -4245,7 +4245,7 @@ msgid "Choose a new folder"
 msgstr "Trieu una carpeta nova"
 
 #: backends/graphics/atari/atari-graphics.cpp:1060
-#: backends/graphics/sdl/sdl-graphics.cpp:514 engines/scumm/help.cpp:103
+#: backends/graphics/sdl/sdl-graphics.cpp:518 engines/scumm/help.cpp:103
 msgid "Toggle aspect ratio correction"
 msgstr "Commuta la correcci?? de la relaci?? d'aspecte"
 
@@ -4317,34 +4317,34 @@ msgstr "SDL Surface"
 msgid "SDL Surface (forced 8bpp mode)"
 msgstr "Superf??cie SDL (mode de 8bpp for??at)"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:420
+#: backends/graphics/sdl/sdl-graphics.cpp:424
 #, c-format
 msgid "Saved screenshot '%s'"
 msgstr "Captura de pantalla desada '%s'"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:435
+#: backends/graphics/sdl/sdl-graphics.cpp:439
 msgid "Could not save screenshot"
 msgstr "No s'ha pogut desar la captura de pantalla"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:480
+#: backends/graphics/sdl/sdl-graphics.cpp:484
 msgid "Windowed mode"
 msgstr "Mode de finestra"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:491 engines/colony/metaengine.cpp:173
+#: backends/graphics/sdl/sdl-graphics.cpp:495 engines/colony/metaengine.cpp:173
 #: engines/scumm/help.cpp:88
 msgid "Toggle fullscreen"
 msgstr "Commuta la pantalla completa"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:498 engines/scumm/help.cpp:100
+#: backends/graphics/sdl/sdl-graphics.cpp:502 engines/scumm/help.cpp:100
 #: engines/wintermute/keymapper_tables.h:960
 msgid "Toggle mouse capture"
 msgstr "Commuta la captura del ratol??"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:503
+#: backends/graphics/sdl/sdl-graphics.cpp:507
 msgid "Toggle resizable window"
 msgstr "Commuta la finestra redimensionable"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:508 engines/stark/metaengine.cpp:240
+#: backends/graphics/sdl/sdl-graphics.cpp:512 engines/stark/metaengine.cpp:240
 #: engines/wintermute/keymapper_tables.h:646
 #: engines/wintermute/keymapper_tables.h:695
 #: engines/wintermute/keymapper_tables.h:1098
@@ -4357,27 +4357,27 @@ msgstr "Commuta la finestra redimensionable"
 msgid "Save screenshot"
 msgstr "Desa la captura de pantalla"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:521
+#: backends/graphics/sdl/sdl-graphics.cpp:525
 msgid "Toggle linear filtered scaling"
 msgstr "Commuta l'escalat de filtratge lineal"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:528
+#: backends/graphics/sdl/sdl-graphics.cpp:532
 msgid "Cycle through stretch modes"
 msgstr "Desplaceu-vos pels modes d'ajustament"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:534
+#: backends/graphics/sdl/sdl-graphics.cpp:538
 msgid "Increase the scale factor"
 msgstr "Augmenta el factor d'escala"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:540
+#: backends/graphics/sdl/sdl-graphics.cpp:544
 msgid "Decrease the scale factor"
 msgstr "Disminueix el factor d'escala"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:547
+#: backends/graphics/sdl/sdl-graphics.cpp:551
 msgid "Switch to the next scaler"
 msgstr "Canvia al seg??ent filtre d'escalada"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:552
+#: backends/graphics/sdl/sdl-graphics.cpp:556
 msgid "Switch to the previous scaler"
 msgstr "Canvia a l'anterior filtre d'escalada"
 
@@ -14643,7 +14643,7 @@ msgstr "Commuta l'estil del panell"
 msgid "Toggle text speech"
 msgstr "Commuta la veu i el text"
 
-#: engines/twine/detection.cpp:188
+#: engines/twine/detection.cpp:187
 msgid "This version requires Giflib which was not compiled into ScummVM"
 msgstr ""
 
diff --git a/po/cs_CZ.po b/po/cs_CZ.po
index 36e5c829503..08141dadce0 100644
--- a/po/cs_CZ.po
+++ b/po/cs_CZ.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.7.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-21 22:22+0000\n"
+"POT-Creation-Date: 2026-06-21 22:35+0000\n"
 "PO-Revision-Date: 2026-02-09 20:22+0000\n"
 "Last-Translator: AsciiWolf <mail at asciiwolf.com>\n"
 "Language-Team: Czech <https://translations.scummvm.org/projects/scummvm/"
@@ -620,7 +620,7 @@ msgstr "GFX"
 
 #: gui/editgamedialog.cpp:164 gui/options.cpp:2075 gui/options.cpp:2256
 #: backends/graphics/atari/atari-graphics.cpp:1056
-#: backends/graphics/sdl/sdl-graphics.cpp:487
+#: backends/graphics/sdl/sdl-graphics.cpp:491
 msgid "Graphics"
 msgstr "Obraz"
 
@@ -1902,7 +1902,7 @@ msgstr "St??hnout soubory hry"
 msgid "Check on the scummvm.org website for updates of shader packs"
 msgstr "Zkontrolovat Aktualizace..."
 
-#: gui/options.cpp:1700 backends/graphics/sdl/sdl-graphics.cpp:478
+#: gui/options.cpp:1700 backends/graphics/sdl/sdl-graphics.cpp:482
 msgid "Fullscreen mode"
 msgstr "Re??im cel?? obrazovky"
 
@@ -4312,7 +4312,7 @@ msgid "Choose a new folder"
 msgstr "Zvolte soubor pro na??ten??"
 
 #: backends/graphics/atari/atari-graphics.cpp:1060
-#: backends/graphics/sdl/sdl-graphics.cpp:514 engines/scumm/help.cpp:103
+#: backends/graphics/sdl/sdl-graphics.cpp:518 engines/scumm/help.cpp:103
 msgid "Toggle aspect ratio correction"
 msgstr "P??epnout korekci pom??ru stran"
 
@@ -4386,35 +4386,35 @@ msgstr ""
 msgid "SDL Surface (forced 8bpp mode)"
 msgstr ""
 
-#: backends/graphics/sdl/sdl-graphics.cpp:420
+#: backends/graphics/sdl/sdl-graphics.cpp:424
 #, fuzzy, c-format
 msgid "Saved screenshot '%s'"
 msgstr "Ulo??it sn??mek obrazovky"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:435
+#: backends/graphics/sdl/sdl-graphics.cpp:439
 #, fuzzy
 msgid "Could not save screenshot"
 msgstr "Ulo??it sn??mek obrazovky"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:480
+#: backends/graphics/sdl/sdl-graphics.cpp:484
 msgid "Windowed mode"
 msgstr "Re??im do okna"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:491 engines/colony/metaengine.cpp:173
+#: backends/graphics/sdl/sdl-graphics.cpp:495 engines/colony/metaengine.cpp:173
 #: engines/scumm/help.cpp:88
 msgid "Toggle fullscreen"
 msgstr "P??epnout celou obrazovku"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:498 engines/scumm/help.cpp:100
+#: backends/graphics/sdl/sdl-graphics.cpp:502 engines/scumm/help.cpp:100
 #: engines/wintermute/keymapper_tables.h:960
 msgid "Toggle mouse capture"
 msgstr "Povolit zachycov??n?? my??i"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:503
+#: backends/graphics/sdl/sdl-graphics.cpp:507
 msgid "Toggle resizable window"
 msgstr ""
 
-#: backends/graphics/sdl/sdl-graphics.cpp:508 engines/stark/metaengine.cpp:240
+#: backends/graphics/sdl/sdl-graphics.cpp:512 engines/stark/metaengine.cpp:240
 #: engines/wintermute/keymapper_tables.h:646
 #: engines/wintermute/keymapper_tables.h:695
 #: engines/wintermute/keymapper_tables.h:1098
@@ -4427,28 +4427,28 @@ msgstr ""
 msgid "Save screenshot"
 msgstr "Ulo??it sn??mek obrazovky"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:521
+#: backends/graphics/sdl/sdl-graphics.cpp:525
 msgid "Toggle linear filtered scaling"
 msgstr "Pou????t line??rn?? ??k??lov??n??"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:528
+#: backends/graphics/sdl/sdl-graphics.cpp:532
 msgid "Cycle through stretch modes"
 msgstr "P??ep??nat mezi re??imy rozta??en??"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:534
+#: backends/graphics/sdl/sdl-graphics.cpp:538
 msgid "Increase the scale factor"
 msgstr "Zv??t??it faktor zm??ny velikosti"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:540
+#: backends/graphics/sdl/sdl-graphics.cpp:544
 msgid "Decrease the scale factor"
 msgstr "Zmen??it faktor zm??ny velikosti"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:547
+#: backends/graphics/sdl/sdl-graphics.cpp:551
 #, fuzzy
 msgid "Switch to the next scaler"
 msgstr "P??epnout na ??k??lov??n?? 2xSai"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:552
+#: backends/graphics/sdl/sdl-graphics.cpp:556
 #, fuzzy
 msgid "Switch to the previous scaler"
 msgstr "P??epnout na nejbli?????? ??k??lov??n??"
@@ -15582,7 +15582,7 @@ msgstr "Rychl?? re??im"
 msgid "Toggle text speech"
 msgstr "Pou????t Text ke ??ten??"
 
-#: engines/twine/detection.cpp:188
+#: engines/twine/detection.cpp:187
 msgid "This version requires Giflib which was not compiled into ScummVM"
 msgstr ""
 
diff --git a/po/da.po b/po/da.po
index c5f52f0917f..246721cd558 100644
--- a/po/da.po
+++ b/po/da.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-21 22:22+0000\n"
+"POT-Creation-Date: 2026-06-21 22:35+0000\n"
 "PO-Revision-Date: 2023-09-07 17:04+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Danish <https://translations.scummvm.org/projects/scummvm/"
@@ -622,7 +622,7 @@ msgstr "GFX"
 
 #: gui/editgamedialog.cpp:164 gui/options.cpp:2075 gui/options.cpp:2256
 #: backends/graphics/atari/atari-graphics.cpp:1056
-#: backends/graphics/sdl/sdl-graphics.cpp:487
+#: backends/graphics/sdl/sdl-graphics.cpp:491
 msgid "Graphics"
 msgstr "Grafik"
 
@@ -1906,7 +1906,7 @@ msgstr "Download spilfiler"
 msgid "Check on the scummvm.org website for updates of shader packs"
 msgstr "S??g efter opdateringer ..."
 
-#: gui/options.cpp:1700 backends/graphics/sdl/sdl-graphics.cpp:478
+#: gui/options.cpp:1700 backends/graphics/sdl/sdl-graphics.cpp:482
 msgid "Fullscreen mode"
 msgstr "Fuldsk??rmstilstand"
 
@@ -4324,7 +4324,7 @@ msgid "Choose a new folder"
 msgstr "V??lg fil til indl??sning"
 
 #: backends/graphics/atari/atari-graphics.cpp:1060
-#: backends/graphics/sdl/sdl-graphics.cpp:514 engines/scumm/help.cpp:103
+#: backends/graphics/sdl/sdl-graphics.cpp:518 engines/scumm/help.cpp:103
 #, fuzzy
 msgid "Toggle aspect ratio correction"
 msgstr "Rettelse af h??jde-/breddeforhold til/fra"
@@ -4398,35 +4398,35 @@ msgstr ""
 msgid "SDL Surface (forced 8bpp mode)"
 msgstr ""
 
-#: backends/graphics/sdl/sdl-graphics.cpp:420
+#: backends/graphics/sdl/sdl-graphics.cpp:424
 #, fuzzy, c-format
 msgid "Saved screenshot '%s'"
 msgstr "J??vn bev??gelse til"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:435
+#: backends/graphics/sdl/sdl-graphics.cpp:439
 #, fuzzy
 msgid "Could not save screenshot"
 msgstr "J??vn bev??gelse til"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:480
+#: backends/graphics/sdl/sdl-graphics.cpp:484
 msgid "Windowed mode"
 msgstr "Vinduestilstand"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:491 engines/colony/metaengine.cpp:173
+#: backends/graphics/sdl/sdl-graphics.cpp:495 engines/colony/metaengine.cpp:173
 #: engines/scumm/help.cpp:88
 msgid "Toggle fullscreen"
 msgstr "Fuldsk??rm til/fra"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:498 engines/scumm/help.cpp:100
+#: backends/graphics/sdl/sdl-graphics.cpp:502 engines/scumm/help.cpp:100
 #: engines/wintermute/keymapper_tables.h:960
 msgid "Toggle mouse capture"
 msgstr "Museindfangning til/fra"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:503
+#: backends/graphics/sdl/sdl-graphics.cpp:507
 msgid "Toggle resizable window"
 msgstr ""
 
-#: backends/graphics/sdl/sdl-graphics.cpp:508 engines/stark/metaengine.cpp:240
+#: backends/graphics/sdl/sdl-graphics.cpp:512 engines/stark/metaengine.cpp:240
 #: engines/wintermute/keymapper_tables.h:646
 #: engines/wintermute/keymapper_tables.h:695
 #: engines/wintermute/keymapper_tables.h:1098
@@ -4440,32 +4440,32 @@ msgstr ""
 msgid "Save screenshot"
 msgstr "J??vn bev??gelse til"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:521
+#: backends/graphics/sdl/sdl-graphics.cpp:525
 #, fuzzy
 msgid "Toggle linear filtered scaling"
 msgstr "Brug line??r filtrering n??r grafik skaleres"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:528
+#: backends/graphics/sdl/sdl-graphics.cpp:532
 #, fuzzy
 msgid "Cycle through stretch modes"
 msgstr "Kunne ikke skifte til str??ktilstanden '%s'."
 
-#: backends/graphics/sdl/sdl-graphics.cpp:534
+#: backends/graphics/sdl/sdl-graphics.cpp:538
 #, fuzzy
 msgid "Increase the scale factor"
 msgstr "H??v/s??nk skaleringsfaktor"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:540
+#: backends/graphics/sdl/sdl-graphics.cpp:544
 #, fuzzy
 msgid "Decrease the scale factor"
 msgstr "H??v/s??nk skaleringsfaktor"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:547
+#: backends/graphics/sdl/sdl-graphics.cpp:551
 #, fuzzy
 msgid "Switch to the next scaler"
 msgstr "Skift til Spil"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:552
+#: backends/graphics/sdl/sdl-graphics.cpp:556
 msgid "Switch to the previous scaler"
 msgstr ""
 
@@ -15692,7 +15692,7 @@ msgstr "Hurtigtilstand"
 msgid "Toggle text speech"
 msgstr "Brug tekst til tale"
 
-#: engines/twine/detection.cpp:188
+#: engines/twine/detection.cpp:187
 #, fuzzy
 msgid "This version requires Giflib which was not compiled into ScummVM"
 msgstr ""
diff --git a/po/de_DE.po b/po/de_DE.po
index b6d20127353..5fefeb32663 100644
--- a/po/de_DE.po
+++ b/po/de_DE.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.10.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-21 22:22+0000\n"
+"POT-Creation-Date: 2026-06-21 22:35+0000\n"
 "PO-Revision-Date: 2026-05-05 16:31+0000\n"
 "Last-Translator: Lothar Serra Mari <lothar.serramari at scummvm.org>\n"
 "Language-Team: German <https://translations.scummvm.org/projects/scummvm/"
@@ -597,7 +597,7 @@ msgstr "GFX"
 
 #: gui/editgamedialog.cpp:164 gui/options.cpp:2075 gui/options.cpp:2256
 #: backends/graphics/atari/atari-graphics.cpp:1056
-#: backends/graphics/sdl/sdl-graphics.cpp:487
+#: backends/graphics/sdl/sdl-graphics.cpp:491
 msgid "Graphics"
 msgstr "Grafik"
 
@@ -1953,7 +1953,7 @@ msgstr "Shader herunterladen"
 msgid "Check on the scummvm.org website for updates of shader packs"
 msgstr "Pr??ft die scummvm.org-Webseite auf Aktualisierungen f??r Shader-Packs"
 
-#: gui/options.cpp:1700 backends/graphics/sdl/sdl-graphics.cpp:478
+#: gui/options.cpp:1700 backends/graphics/sdl/sdl-graphics.cpp:482
 msgid "Fullscreen mode"
 msgstr "Vollbildmodus"
 
@@ -4377,7 +4377,7 @@ msgid "Choose a new folder"
 msgstr "Neuen Ordner w??hlen"
 
 #: backends/graphics/atari/atari-graphics.cpp:1060
-#: backends/graphics/sdl/sdl-graphics.cpp:514 engines/scumm/help.cpp:103
+#: backends/graphics/sdl/sdl-graphics.cpp:518 engines/scumm/help.cpp:103
 msgid "Toggle aspect ratio correction"
 msgstr "Seitenverh??ltnis-Korrektur umschalten"
 
@@ -4449,34 +4449,34 @@ msgstr "SDL Surface"
 msgid "SDL Surface (forced 8bpp mode)"
 msgstr "SDL Surface (8bpp-Modus wird erzwungen)"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:420
+#: backends/graphics/sdl/sdl-graphics.cpp:424
 #, c-format
 msgid "Saved screenshot '%s'"
 msgstr "Screenshot '%s' gespeichert"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:435
+#: backends/graphics/sdl/sdl-graphics.cpp:439
 msgid "Could not save screenshot"
 msgstr "Screenshot konnte nicht gespeichert werden"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:480
+#: backends/graphics/sdl/sdl-graphics.cpp:484
 msgid "Windowed mode"
 msgstr "Fenstermodus"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:491 engines/colony/metaengine.cpp:173
+#: backends/graphics/sdl/sdl-graphics.cpp:495 engines/colony/metaengine.cpp:173
 #: engines/scumm/help.cpp:88
 msgid "Toggle fullscreen"
 msgstr "Vollbild umschalten"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:498 engines/scumm/help.cpp:100
+#: backends/graphics/sdl/sdl-graphics.cpp:502 engines/scumm/help.cpp:100
 #: engines/wintermute/keymapper_tables.h:960
 msgid "Toggle mouse capture"
 msgstr "Mauszeiger einfangen"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:503
+#: backends/graphics/sdl/sdl-graphics.cpp:507
 msgid "Toggle resizable window"
 msgstr "??nderung der Fenstergr????e erlauben"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:508 engines/stark/metaengine.cpp:240
+#: backends/graphics/sdl/sdl-graphics.cpp:512 engines/stark/metaengine.cpp:240
 #: engines/wintermute/keymapper_tables.h:646
 #: engines/wintermute/keymapper_tables.h:695
 #: engines/wintermute/keymapper_tables.h:1098
@@ -4489,27 +4489,27 @@ msgstr "??nderung der Fenstergr????e erlauben"
 msgid "Save screenshot"
 msgstr "Screenshot speichern"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:521
+#: backends/graphics/sdl/sdl-graphics.cpp:525
 msgid "Toggle linear filtered scaling"
 msgstr "Bilineare Filterung zur Grafik-Skalierung umschalten"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:528
+#: backends/graphics/sdl/sdl-graphics.cpp:532
 msgid "Cycle through stretch modes"
 msgstr "Durch Skalier-Modi wechseln"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:534
+#: backends/graphics/sdl/sdl-graphics.cpp:538
 msgid "Increase the scale factor"
 msgstr "Skalier-Faktor erh??hen"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:540
+#: backends/graphics/sdl/sdl-graphics.cpp:544
 msgid "Decrease the scale factor"
 msgstr "Skalier-Faktor verringern"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:547
+#: backends/graphics/sdl/sdl-graphics.cpp:551
 msgid "Switch to the next scaler"
 msgstr "Zum n??chsten Skaliermodus wechseln"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:552
+#: backends/graphics/sdl/sdl-graphics.cpp:556
 msgid "Switch to the previous scaler"
 msgstr "Zum vorherigen Skaliermodus wechseln"
 
@@ -15464,7 +15464,7 @@ msgstr "Panel-Stil umschalten"
 msgid "Toggle text speech"
 msgstr "Sprachausgabe umschalten"
 
-#: engines/twine/detection.cpp:188
+#: engines/twine/detection.cpp:187
 msgid "This version requires Giflib which was not compiled into ScummVM"
 msgstr ""
 "Dieses Spiel ben??tigt die Giflib-Bibliothek, die nicht in ScummVM eingebaut "
diff --git a/po/el.po b/po/el.po
index 4a0e7a85146..e01ce17574f 100644
--- a/po/el.po
+++ b/po/el.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.10.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-21 22:22+0000\n"
+"POT-Creation-Date: 2026-06-21 22:35+0000\n"
 "PO-Revision-Date: 2026-04-23 00:22+0000\n"
 "Last-Translator: Antoniou Athanasios <a.antoniou79 at gmail.com>\n"
 "Language-Team: Greek <https://translations.scummvm.org/projects/scummvm/"
@@ -595,7 +595,7 @@ msgstr "????????."
 
 #: gui/editgamedialog.cpp:164 gui/options.cpp:2075 gui/options.cpp:2256
 #: backends/graphics/atari/atari-graphics.cpp:1056
-#: backends/graphics/sdl/sdl-graphics.cpp:487
+#: backends/graphics/sdl/sdl-graphics.cpp:491
 msgid "Graphics"
 msgstr "??????????????"
 
@@ -1975,7 +1975,7 @@ msgstr ""
 "?????????????? ???????? ???????????????? scummvm.org ?????? ?????????????????????? ?????? ?????????????? ?????? ???????????????? "
 "(shaders)"
 
-#: gui/options.cpp:1700 backends/graphics/sdl/sdl-graphics.cpp:478
+#: gui/options.cpp:1700 backends/graphics/sdl/sdl-graphics.cpp:482
 msgid "Fullscreen mode"
 msgstr "???????????????????? ?????????????? ????????????"
 
@@ -4450,7 +4450,7 @@ msgid "Choose a new folder"
 msgstr "?????????????? ???????? ??????????????"
 
 #: backends/graphics/atari/atari-graphics.cpp:1060
-#: backends/graphics/sdl/sdl-graphics.cpp:514 engines/scumm/help.cpp:103
+#: backends/graphics/sdl/sdl-graphics.cpp:518 engines/scumm/help.cpp:103
 msgid "Toggle aspect ratio correction"
 msgstr "???????????????? ?????????????????? ?????????????????? ????????????????????"
 
@@ -4522,34 +4522,34 @@ msgstr "?????????????????? SDL"
 msgid "SDL Surface (forced 8bpp mode)"
 msgstr "?????????????????? SDL (?????????????????????????? ???????????????????? 8bpp)"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:420
+#: backends/graphics/sdl/sdl-graphics.cpp:424
 #, c-format
 msgid "Saved screenshot '%s'"
 msgstr "?????????? ???????????????????? ???????????????????????? '%s'"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:435
+#: backends/graphics/sdl/sdl-graphics.cpp:439
 msgid "Could not save screenshot"
 msgstr "???????????????? ?????????????????????? ????????????????????????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:480
+#: backends/graphics/sdl/sdl-graphics.cpp:484
 msgid "Windowed mode"
 msgstr "???????????????????? ??????????????????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:491 engines/colony/metaengine.cpp:173
+#: backends/graphics/sdl/sdl-graphics.cpp:495 engines/colony/metaengine.cpp:173
 #: engines/scumm/help.cpp:88
 msgid "Toggle fullscreen"
 msgstr "???????????????????? ?????????????? ????????????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:498 engines/scumm/help.cpp:100
+#: backends/graphics/sdl/sdl-graphics.cpp:502 engines/scumm/help.cpp:100
 #: engines/wintermute/keymapper_tables.h:960
 msgid "Toggle mouse capture"
 msgstr "???????????????? ???????????????? ??????????????????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:503
+#: backends/graphics/sdl/sdl-graphics.cpp:507
 msgid "Toggle resizable window"
 msgstr "???????????????? ???????????????????? ?????????????????? ???????????????????? ????????????????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:508 engines/stark/metaengine.cpp:240
+#: backends/graphics/sdl/sdl-graphics.cpp:512 engines/stark/metaengine.cpp:240
 #: engines/wintermute/keymapper_tables.h:646
 #: engines/wintermute/keymapper_tables.h:695
 #: engines/wintermute/keymapper_tables.h:1098
@@ -4562,27 +4562,27 @@ msgstr "???????????????? ???????????????????? ?????????????????? ??????????????
 msgid "Save screenshot"
 msgstr "???????????????????? ????????????????????????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:521
+#: backends/graphics/sdl/sdl-graphics.cpp:525
 msgid "Toggle linear filtered scaling"
 msgstr "???????????????? ???????????? ?????????????????? ?????????????? ???? ?????????????????? ??????????????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:528
+#: backends/graphics/sdl/sdl-graphics.cpp:532
 msgid "Cycle through stretch modes"
 msgstr "?????????????? ?????????????????? ???????? ?????????????????????? ?????????????????? ??????????????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:534
+#: backends/graphics/sdl/sdl-graphics.cpp:538
 msgid "Increase the scale factor"
 msgstr "???????????? ???????????????????? ????????????????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:540
+#: backends/graphics/sdl/sdl-graphics.cpp:544
 msgid "Decrease the scale factor"
 msgstr "???????????? ???????????????????? ????????????????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:547
+#: backends/graphics/sdl/sdl-graphics.cpp:551
 msgid "Switch to the next scaler"
 msgstr "?????????? ???????????????? ??????????????????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:552
+#: backends/graphics/sdl/sdl-graphics.cpp:556
 msgid "Switch to the previous scaler"
 msgstr "?????????? ???????????????????????? ??????????????????"
 
@@ -15654,7 +15654,7 @@ msgstr "???????????????? ???????? ??????????"
 msgid "Toggle text speech"
 msgstr "???????????????? ?????????????????? ????????????????"
 
-#: engines/twine/detection.cpp:188
+#: engines/twine/detection.cpp:187
 msgid "This version requires Giflib which was not compiled into ScummVM"
 msgstr ""
 "???????? ?? ???????????? ?????????????? ???? Giflib ?????? ?????? ???????? ?????????????????????? ?????? ???????????????????? ?????? "
diff --git a/po/es_ES.po b/po/es_ES.po
index 89d671ed71e..d8773c2080f 100644
--- a/po/es_ES.po
+++ b/po/es_ES.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.4.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-21 22:22+0000\n"
+"POT-Creation-Date: 2026-06-21 22:35+0000\n"
 "PO-Revision-Date: 2026-06-20 15:53+0000\n"
 "Last-Translator: IlDucci <pajaroloco_2 at hotmail.com>\n"
 "Language-Team: Spanish <https://translations.scummvm.org/projects/scummvm/"
@@ -591,7 +591,7 @@ msgstr "Gr??ficos"
 
 #: gui/editgamedialog.cpp:164 gui/options.cpp:2075 gui/options.cpp:2256
 #: backends/graphics/atari/atari-graphics.cpp:1056
-#: backends/graphics/sdl/sdl-graphics.cpp:487
+#: backends/graphics/sdl/sdl-graphics.cpp:491
 msgid "Graphics"
 msgstr "Gr??ficos"
 
@@ -1944,7 +1944,7 @@ msgid "Check on the scummvm.org website for updates of shader packs"
 msgstr ""
 "Ve a la p??gina scummvm.org para buscar actualizaciones de paquetes de shaders"
 
-#: gui/options.cpp:1700 backends/graphics/sdl/sdl-graphics.cpp:478
+#: gui/options.cpp:1700 backends/graphics/sdl/sdl-graphics.cpp:482
 msgid "Fullscreen mode"
 msgstr "Pantalla completa"
 
@@ -4374,7 +4374,7 @@ msgid "Choose a new folder"
 msgstr "Elegir una carpeta nueva"
 
 #: backends/graphics/atari/atari-graphics.cpp:1060
-#: backends/graphics/sdl/sdl-graphics.cpp:514 engines/scumm/help.cpp:103
+#: backends/graphics/sdl/sdl-graphics.cpp:518 engines/scumm/help.cpp:103
 msgid "Toggle aspect ratio correction"
 msgstr "Alternar correcci??n de relaci??n de aspecto"
 
@@ -4446,34 +4446,34 @@ msgstr "SDL Surface"
 msgid "SDL Surface (forced 8bpp mode)"
 msgstr "SDL Surface (modo a 8bpp forzado)"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:420
+#: backends/graphics/sdl/sdl-graphics.cpp:424
 #, c-format
 msgid "Saved screenshot '%s'"
 msgstr "Captura de pantalla ??%s?? guardada"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:435
+#: backends/graphics/sdl/sdl-graphics.cpp:439
 msgid "Could not save screenshot"
 msgstr "No se ha podido guardar la captura de pantalla"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:480
+#: backends/graphics/sdl/sdl-graphics.cpp:484
 msgid "Windowed mode"
 msgstr "Modo ventana"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:491 engines/colony/metaengine.cpp:173
+#: backends/graphics/sdl/sdl-graphics.cpp:495 engines/colony/metaengine.cpp:173
 #: engines/scumm/help.cpp:88
 msgid "Toggle fullscreen"
 msgstr "Alternar pantalla completa"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:498 engines/scumm/help.cpp:100
+#: backends/graphics/sdl/sdl-graphics.cpp:502 engines/scumm/help.cpp:100
 #: engines/wintermute/keymapper_tables.h:960
 msgid "Toggle mouse capture"
 msgstr "Alternar captura de rat??n"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:503
+#: backends/graphics/sdl/sdl-graphics.cpp:507
 msgid "Toggle resizable window"
 msgstr "Alternar cambio de tama??o de ventana"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:508 engines/stark/metaengine.cpp:240
+#: backends/graphics/sdl/sdl-graphics.cpp:512 engines/stark/metaengine.cpp:240
 #: engines/wintermute/keymapper_tables.h:646
 #: engines/wintermute/keymapper_tables.h:695
 #: engines/wintermute/keymapper_tables.h:1098
@@ -4486,27 +4486,27 @@ msgstr "Alternar cambio de tama??o de ventana"
 msgid "Save screenshot"
 msgstr "Guardar captura de pantalla"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:521
+#: backends/graphics/sdl/sdl-graphics.cpp:525
 msgid "Toggle linear filtered scaling"
 msgstr "Activar/Desactivar escalado con filtro lineal"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:528
+#: backends/graphics/sdl/sdl-graphics.cpp:532
 msgid "Cycle through stretch modes"
 msgstr "Cambiar modo de ajuste"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:534
+#: backends/graphics/sdl/sdl-graphics.cpp:538
 msgid "Increase the scale factor"
 msgstr "Aumentar factor de escalado"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:540
+#: backends/graphics/sdl/sdl-graphics.cpp:544
 msgid "Decrease the scale factor"
 msgstr "Disminuir factor de escalado"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:547
+#: backends/graphics/sdl/sdl-graphics.cpp:551
 msgid "Switch to the next scaler"
 msgstr "Cambiar al siguiente filtro de escalado"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:552
+#: backends/graphics/sdl/sdl-graphics.cpp:556
 msgid "Switch to the previous scaler"
 msgstr "Cambiar al filtro de escalado anterior"
 
@@ -15381,7 +15381,7 @@ msgstr "Alternar estilo del panel"
 msgid "Toggle text speech"
 msgstr "Alternar voces y textos"
 
-#: engines/twine/detection.cpp:188
+#: engines/twine/detection.cpp:187
 msgid "This version requires Giflib which was not compiled into ScummVM"
 msgstr "Esta versi??n requiere Gifilib, que no ha sido compilado en ScummVM."
 
diff --git a/po/eu.po b/po/eu.po
index 1d643ae847b..cdd90d6d71f 100644
--- a/po/eu.po
+++ b/po/eu.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.5.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-21 22:22+0000\n"
+"POT-Creation-Date: 2026-06-21 22:35+0000\n"
 "PO-Revision-Date: 2024-02-05 18:42+0000\n"
 "Last-Translator: Josu Igoa <josuigoa at ni.eus>\n"
 "Language-Team: Basque <https://translations.scummvm.org/projects/scummvm/"
@@ -592,7 +592,7 @@ msgstr "GFX"
 
 #: gui/editgamedialog.cpp:164 gui/options.cpp:2075 gui/options.cpp:2256
 #: backends/graphics/atari/atari-graphics.cpp:1056
-#: backends/graphics/sdl/sdl-graphics.cpp:487
+#: backends/graphics/sdl/sdl-graphics.cpp:491
 msgid "Graphics"
 msgstr "Grafikoak"
 
@@ -1942,7 +1942,7 @@ msgstr "Shader-ak deskargatu"
 msgid "Check on the scummvm.org website for updates of shader packs"
 msgstr "Shader multzoen eguneraketa bilatu scummvm.org webgunean"
 
-#: gui/options.cpp:1700 backends/graphics/sdl/sdl-graphics.cpp:478
+#: gui/options.cpp:1700 backends/graphics/sdl/sdl-graphics.cpp:482
 msgid "Fullscreen mode"
 msgstr "Pantaila osoa"
 
@@ -4358,7 +4358,7 @@ msgid "Choose a new folder"
 msgstr "Aukeratu kargatzeko fitxategia"
 
 #: backends/graphics/atari/atari-graphics.cpp:1060
-#: backends/graphics/sdl/sdl-graphics.cpp:514 engines/scumm/help.cpp:103
+#: backends/graphics/sdl/sdl-graphics.cpp:518 engines/scumm/help.cpp:103
 #, fuzzy
 msgid "Toggle aspect ratio correction"
 msgstr "Txandakatu fFormatu-ratioaren zuzenketa"
@@ -4434,35 +4434,35 @@ msgstr ""
 msgid "SDL Surface (forced 8bpp mode)"
 msgstr ""
 
-#: backends/graphics/sdl/sdl-graphics.cpp:420
+#: backends/graphics/sdl/sdl-graphics.cpp:424
 #, fuzzy, c-format
 msgid "Saved screenshot '%s'"
 msgstr "Heldutako korritze barra gaituta"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:435
+#: backends/graphics/sdl/sdl-graphics.cpp:439
 #, fuzzy
 msgid "Could not save screenshot"
 msgstr "Heldutako korritze barra gaituta"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:480
+#: backends/graphics/sdl/sdl-graphics.cpp:484
 msgid "Windowed mode"
 msgstr "Leiho modua"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:491 engines/colony/metaengine.cpp:173
+#: backends/graphics/sdl/sdl-graphics.cpp:495 engines/colony/metaengine.cpp:173
 #: engines/scumm/help.cpp:88
 msgid "Toggle fullscreen"
 msgstr "Txandakatu pantaila osoa"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:498 engines/scumm/help.cpp:100
+#: backends/graphics/sdl/sdl-graphics.cpp:502 engines/scumm/help.cpp:100
 #: engines/wintermute/keymapper_tables.h:960
 msgid "Toggle mouse capture"
 msgstr "Saguaren kaptura"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:503
+#: backends/graphics/sdl/sdl-graphics.cpp:507
 msgid "Toggle resizable window"
 msgstr ""
 
-#: backends/graphics/sdl/sdl-graphics.cpp:508 engines/stark/metaengine.cpp:240
+#: backends/graphics/sdl/sdl-graphics.cpp:512 engines/stark/metaengine.cpp:240
 #: engines/wintermute/keymapper_tables.h:646
 #: engines/wintermute/keymapper_tables.h:695
 #: engines/wintermute/keymapper_tables.h:1098
@@ -4476,31 +4476,31 @@ msgstr ""
 msgid "Save screenshot"
 msgstr "Heldutako korritze barra gaituta"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:521
+#: backends/graphics/sdl/sdl-graphics.cpp:525
 msgid "Toggle linear filtered scaling"
 msgstr ""
 
-#: backends/graphics/sdl/sdl-graphics.cpp:528
+#: backends/graphics/sdl/sdl-graphics.cpp:532
 #, fuzzy
 msgid "Cycle through stretch modes"
 msgstr "Ezin izan da aldatu bideo modura : '"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:534
+#: backends/graphics/sdl/sdl-graphics.cpp:538
 #, fuzzy
 msgid "Increase the scale factor"
 msgstr "Eskala faktorea handitu / txikitu"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:540
+#: backends/graphics/sdl/sdl-graphics.cpp:544
 #, fuzzy
 msgid "Decrease the scale factor"
 msgstr "Eskala faktorea handitu / txikitu"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:547
+#: backends/graphics/sdl/sdl-graphics.cpp:551
 #, fuzzy
 msgid "Switch to the next scaler"
 msgstr "Aldatu jokora"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:552
+#: backends/graphics/sdl/sdl-graphics.cpp:556
 msgid "Switch to the previous scaler"
 msgstr ""
 
@@ -15651,7 +15651,7 @@ msgstr "Modu bizkorra"
 msgid "Toggle text speech"
 msgstr "Erabili \"Testua ahotsera\""
 
-#: engines/twine/detection.cpp:188
+#: engines/twine/detection.cpp:187
 msgid "This version requires Giflib which was not compiled into ScummVM"
 msgstr ""
 
diff --git a/po/fi_FI.po b/po/fi_FI.po
index be704bbd69d..bda5d7ea3a3 100644
--- a/po/fi_FI.po
+++ b/po/fi_FI.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.6.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-21 22:22+0000\n"
+"POT-Creation-Date: 2026-06-21 22:35+0000\n"
 "PO-Revision-Date: 2026-02-07 15:22+0000\n"
 "Last-Translator: Timo Mikkolainen <tmikkola at gmail.com>\n"
 "Language-Team: Finnish <https://translations.scummvm.org/projects/scummvm/"
@@ -591,7 +591,7 @@ msgstr "GFX"
 
 #: gui/editgamedialog.cpp:164 gui/options.cpp:2075 gui/options.cpp:2256
 #: backends/graphics/atari/atari-graphics.cpp:1056
-#: backends/graphics/sdl/sdl-graphics.cpp:487
+#: backends/graphics/sdl/sdl-graphics.cpp:491
 msgid "Graphics"
 msgstr "Grafiikka"
 
@@ -1987,7 +1987,7 @@ msgstr "Lataa s??vyttimi??"
 msgid "Check on the scummvm.org website for updates of shader packs"
 msgstr "Tarkista s??vytinpakettien p??ivitykset scummvm.org-sivustolta"
 
-#: gui/options.cpp:1700 backends/graphics/sdl/sdl-graphics.cpp:478
+#: gui/options.cpp:1700 backends/graphics/sdl/sdl-graphics.cpp:482
 msgid "Fullscreen mode"
 msgstr "Kokoruututila"
 
@@ -4399,7 +4399,7 @@ msgid "Choose a new folder"
 msgstr "Valitse uusi kansio"
 
 #: backends/graphics/atari/atari-graphics.cpp:1060
-#: backends/graphics/sdl/sdl-graphics.cpp:514 engines/scumm/help.cpp:103
+#: backends/graphics/sdl/sdl-graphics.cpp:518 engines/scumm/help.cpp:103
 msgid "Toggle aspect ratio correction"
 msgstr "Kuvasuhdekorjaus p????lle / pois"
 
@@ -4471,34 +4471,34 @@ msgstr "SDL-pinta"
 msgid "SDL Surface (forced 8bpp mode)"
 msgstr "SDL Surface (pakotettu 8bpp-tila)"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:420
+#: backends/graphics/sdl/sdl-graphics.cpp:424
 #, c-format
 msgid "Saved screenshot '%s'"
 msgstr "Kuvakaappaus '%s' tallennettu"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:435
+#: backends/graphics/sdl/sdl-graphics.cpp:439
 msgid "Could not save screenshot"
 msgstr "Kuvakaappauksen tallentaminen ep??onnistui"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:480
+#: backends/graphics/sdl/sdl-graphics.cpp:484
 msgid "Windowed mode"
 msgstr "Ikkunoitu tila"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:491 engines/colony/metaengine.cpp:173
+#: backends/graphics/sdl/sdl-graphics.cpp:495 engines/colony/metaengine.cpp:173
 #: engines/scumm/help.cpp:88
 msgid "Toggle fullscreen"
 msgstr "Kokoruututilan vaihto"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:498 engines/scumm/help.cpp:100
+#: backends/graphics/sdl/sdl-graphics.cpp:502 engines/scumm/help.cpp:100
 #: engines/wintermute/keymapper_tables.h:960
 msgid "Toggle mouse capture"
 msgstr "Kytke hiiren kaappaus p????lle tai pois"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:503
+#: backends/graphics/sdl/sdl-graphics.cpp:507
 msgid "Toggle resizable window"
 msgstr "Muokattava ikkunan koko p????lle/pois"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:508 engines/stark/metaengine.cpp:240
+#: backends/graphics/sdl/sdl-graphics.cpp:512 engines/stark/metaengine.cpp:240
 #: engines/wintermute/keymapper_tables.h:646
 #: engines/wintermute/keymapper_tables.h:695
 #: engines/wintermute/keymapper_tables.h:1098
@@ -4511,27 +4511,27 @@ msgstr "Muokattava ikkunan koko p????lle/pois"
 msgid "Save screenshot"
 msgstr "Tallenna kuvakaappaus"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:521
+#: backends/graphics/sdl/sdl-graphics.cpp:525
 msgid "Toggle linear filtered scaling"
 msgstr "Lineaarisesti suodatettu skaalaus p????lle / pois"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:528
+#: backends/graphics/sdl/sdl-graphics.cpp:532
 msgid "Cycle through stretch modes"
 msgstr "Selaa l??pi venytystiloja"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:534
+#: backends/graphics/sdl/sdl-graphics.cpp:538
 msgid "Increase the scale factor"
 msgstr "Kasvata skaalauskerrointa"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:540
+#: backends/graphics/sdl/sdl-graphics.cpp:544
 msgid "Decrease the scale factor"
 msgstr "Pienenn?? skaalauskerrointa"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:547
+#: backends/graphics/sdl/sdl-graphics.cpp:551
 msgid "Switch to the next scaler"
 msgstr "Vaihda seuraavaan skaalaajaan"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:552
+#: backends/graphics/sdl/sdl-graphics.cpp:556
 msgid "Switch to the previous scaler"
 msgstr "Vaihda edelliseen skaalaajaan"
 
@@ -15698,7 +15698,7 @@ msgstr "Vaihda paneelin tyyli??"
 msgid "Toggle text speech"
 msgstr "Puhutut tekstit"
 
-#: engines/twine/detection.cpp:188
+#: engines/twine/detection.cpp:187
 msgid "This version requires Giflib which was not compiled into ScummVM"
 msgstr ""
 "T??m?? versio vaatii toimiakseen Giflib-kirjaston, jota ei ole k????nnetty t??h??n "
diff --git a/po/fr_FR.po b/po/fr_FR.po
index 0d10f4b63a5..d2f5b4bd397 100644
--- a/po/fr_FR.po
+++ b/po/fr_FR.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-21 22:22+0000\n"
+"POT-Creation-Date: 2026-06-21 22:35+0000\n"
 "PO-Revision-Date: 2026-06-14 09:41+0000\n"
 "Last-Translator: Purple T <ZEONK at hotmail.com>\n"
 "Language-Team: French <https://translations.scummvm.org/projects/scummvm/"
@@ -593,7 +593,7 @@ msgstr "GFX"
 
 #: gui/editgamedialog.cpp:164 gui/options.cpp:2075 gui/options.cpp:2256
 #: backends/graphics/atari/atari-graphics.cpp:1056
-#: backends/graphics/sdl/sdl-graphics.cpp:487
+#: backends/graphics/sdl/sdl-graphics.cpp:491
 msgid "Graphics"
 msgstr "Graphismes"
 
@@ -1956,7 +1956,7 @@ msgstr "T??l??charger des shaders"
 msgid "Check on the scummvm.org website for updates of shader packs"
 msgstr "Recherche de mises ?? jour des packs de shaders sur le site scummvm.org"
 
-#: gui/options.cpp:1700 backends/graphics/sdl/sdl-graphics.cpp:478
+#: gui/options.cpp:1700 backends/graphics/sdl/sdl-graphics.cpp:482
 msgid "Fullscreen mode"
 msgstr "Plein ??cran"
 
@@ -4397,7 +4397,7 @@ msgid "Choose a new folder"
 msgstr "Choisir un nouveau dossier"
 
 #: backends/graphics/atari/atari-graphics.cpp:1060
-#: backends/graphics/sdl/sdl-graphics.cpp:514 engines/scumm/help.cpp:103
+#: backends/graphics/sdl/sdl-graphics.cpp:518 engines/scumm/help.cpp:103
 msgid "Toggle aspect ratio correction"
 msgstr "Act. / D??sact. correction du rapport d'aspect"
 
@@ -4469,34 +4469,34 @@ msgstr "SDL Surface"
 msgid "SDL Surface (forced 8bpp mode)"
 msgstr "SDL Surface (mode 8bpp forc??)"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:420
+#: backends/graphics/sdl/sdl-graphics.cpp:424
 #, c-format
 msgid "Saved screenshot '%s'"
 msgstr "Capture d'??cran '%s' sauvegard??e"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:435
+#: backends/graphics/sdl/sdl-graphics.cpp:439
 msgid "Could not save screenshot"
 msgstr "Capture d'??cran impossible"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:480
+#: backends/graphics/sdl/sdl-graphics.cpp:484
 msgid "Windowed mode"
 msgstr "Mode Fen??tre"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:491 engines/colony/metaengine.cpp:173
+#: backends/graphics/sdl/sdl-graphics.cpp:495 engines/colony/metaengine.cpp:173
 #: engines/scumm/help.cpp:88
 msgid "Toggle fullscreen"
 msgstr "Basculer en plein ??cran"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:498 engines/scumm/help.cpp:100
+#: backends/graphics/sdl/sdl-graphics.cpp:502 engines/scumm/help.cpp:100
 #: engines/wintermute/keymapper_tables.h:960
 msgid "Toggle mouse capture"
 msgstr "Capturer/Lib??rer la souris"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:503
+#: backends/graphics/sdl/sdl-graphics.cpp:507
 msgid "Toggle resizable window"
 msgstr "Act. / D??sact. fen??tre redimensionnable"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:508 engines/stark/metaengine.cpp:240
+#: backends/graphics/sdl/sdl-graphics.cpp:512 engines/stark/metaengine.cpp:240
 #: engines/wintermute/keymapper_tables.h:646
 #: engines/wintermute/keymapper_tables.h:695
 #: engines/wintermute/keymapper_tables.h:1098
@@ -4509,27 +4509,27 @@ msgstr "Act. / D??sact. fen??tre redimensionnable"
 msgid "Save screenshot"
 msgstr "Sauver capture d'??cran"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:521
+#: backends/graphics/sdl/sdl-graphics.cpp:525
 msgid "Toggle linear filtered scaling"
 msgstr "Act. / D??sact. interpolation lors du changement d'??chelle"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:528
+#: backends/graphics/sdl/sdl-graphics.cpp:532
 msgid "Cycle through stretch modes"
 msgstr "Changer le mode d'??tirement"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:534
+#: backends/graphics/sdl/sdl-graphics.cpp:538
 msgid "Increase the scale factor"
 msgstr "Augmenter le facteur d'??chelle"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:540
+#: backends/graphics/sdl/sdl-graphics.cpp:544
 msgid "Decrease the scale factor"
 msgstr "Diminuer le facteur d'??chelle"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:547
+#: backends/graphics/sdl/sdl-graphics.cpp:551
 msgid "Switch to the next scaler"
 msgstr "Basculer vers le scaleur suivant"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:552
+#: backends/graphics/sdl/sdl-graphics.cpp:556
 msgid "Switch to the previous scaler"
 msgstr "Basculer vers le scaleur pr??c??dent"
 
@@ -15425,7 +15425,7 @@ msgstr "Basculer entre styles de panneau"
 msgid "Toggle text speech"
 msgstr "Basculer entre lignes de dialogue"
 
-#: engines/twine/detection.cpp:188
+#: engines/twine/detection.cpp:187
 msgid "This version requires Giflib which was not compiled into ScummVM"
 msgstr ""
 "Cette version n??cessite Giflib qui n'a pas ??t?? inclus dans la compilation"
diff --git a/po/gl_ES.po b/po/gl_ES.po
index 33b91d1e67d..9d7b948a878 100644
--- a/po/gl_ES.po
+++ b/po/gl_ES.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-21 22:22+0000\n"
+"POT-Creation-Date: 2026-06-21 22:35+0000\n"
 "PO-Revision-Date: 2020-09-26 21:13+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Galician <https://translations.scummvm.org/projects/scummvm/"
@@ -612,7 +612,7 @@ msgstr "Efectos gr??ficos"
 
 #: gui/editgamedialog.cpp:164 gui/options.cpp:2075 gui/options.cpp:2256
 #: backends/graphics/atari/atari-graphics.cpp:1056
-#: backends/graphics/sdl/sdl-graphics.cpp:487
+#: backends/graphics/sdl/sdl-graphics.cpp:491
 msgid "Graphics"
 msgstr "Gr??ficos"
 
@@ -1899,7 +1899,7 @@ msgstr "Erro ao descargar."
 msgid "Check on the scummvm.org website for updates of shader packs"
 msgstr "Buscar actualizaci??ns..."
 
-#: gui/options.cpp:1700 backends/graphics/sdl/sdl-graphics.cpp:478
+#: gui/options.cpp:1700 backends/graphics/sdl/sdl-graphics.cpp:482
 msgid "Fullscreen mode"
 msgstr "Pantalla completa"
 
@@ -4321,7 +4321,7 @@ msgid "Choose a new folder"
 msgstr "Escoller ficheiro para cargar"
 
 #: backends/graphics/atari/atari-graphics.cpp:1060
-#: backends/graphics/sdl/sdl-graphics.cpp:514 engines/scumm/help.cpp:103
+#: backends/graphics/sdl/sdl-graphics.cpp:518 engines/scumm/help.cpp:103
 #, fuzzy
 msgid "Toggle aspect ratio correction"
 msgstr "Activar/desactivar correcci??n de proporci??n"
@@ -4395,35 +4395,35 @@ msgstr ""
 msgid "SDL Surface (forced 8bpp mode)"
 msgstr ""
 
-#: backends/graphics/sdl/sdl-graphics.cpp:420
+#: backends/graphics/sdl/sdl-graphics.cpp:424
 #, fuzzy, c-format
 msgid "Saved screenshot '%s'"
 msgstr "Axuste de desprazamento SI"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:435
+#: backends/graphics/sdl/sdl-graphics.cpp:439
 #, fuzzy
 msgid "Could not save screenshot"
 msgstr "Axuste de desprazamento SI"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:480
+#: backends/graphics/sdl/sdl-graphics.cpp:484
 msgid "Windowed mode"
 msgstr "Modo en vent??"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:491 engines/colony/metaengine.cpp:173
+#: backends/graphics/sdl/sdl-graphics.cpp:495 engines/colony/metaengine.cpp:173
 #: engines/scumm/help.cpp:88
 msgid "Toggle fullscreen"
 msgstr "Activar/desactivar pantalla completa"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:498 engines/scumm/help.cpp:100
+#: backends/graphics/sdl/sdl-graphics.cpp:502 engines/scumm/help.cpp:100
 #: engines/wintermute/keymapper_tables.h:960
 msgid "Toggle mouse capture"
 msgstr "Activar/desactivar captura de rato"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:503
+#: backends/graphics/sdl/sdl-graphics.cpp:507
 msgid "Toggle resizable window"
 msgstr ""
 
-#: backends/graphics/sdl/sdl-graphics.cpp:508 engines/stark/metaengine.cpp:240
+#: backends/graphics/sdl/sdl-graphics.cpp:512 engines/stark/metaengine.cpp:240
 #: engines/wintermute/keymapper_tables.h:646
 #: engines/wintermute/keymapper_tables.h:695
 #: engines/wintermute/keymapper_tables.h:1098
@@ -4437,32 +4437,32 @@ msgstr ""
 msgid "Save screenshot"
 msgstr "Axuste de desprazamento SI"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:521
+#: backends/graphics/sdl/sdl-graphics.cpp:525
 #, fuzzy
 msgid "Toggle linear filtered scaling"
 msgstr "Empregar filtrado linear ao escalar gr??ficos"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:528
+#: backends/graphics/sdl/sdl-graphics.cpp:532
 #, fuzzy
 msgid "Cycle through stretch modes"
 msgstr "Non se puido cambiar ao modo de v??deo: '"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:534
+#: backends/graphics/sdl/sdl-graphics.cpp:538
 #, fuzzy
 msgid "Increase the scale factor"
 msgstr "Aumentar/reducir factor de escala"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:540
+#: backends/graphics/sdl/sdl-graphics.cpp:544
 #, fuzzy
 msgid "Decrease the scale factor"
 msgstr "Aumentar/reducir factor de escala"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:547
+#: backends/graphics/sdl/sdl-graphics.cpp:551
 #, fuzzy
 msgid "Switch to the next scaler"
 msgstr "Cambiar ao xogo"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:552
+#: backends/graphics/sdl/sdl-graphics.cpp:556
 msgid "Switch to the previous scaler"
 msgstr ""
 
@@ -15595,7 +15595,7 @@ msgstr "Modo R??pido"
 msgid "Toggle text speech"
 msgstr "Texto e voz:"
 
-#: engines/twine/detection.cpp:188
+#: engines/twine/detection.cpp:187
 msgid "This version requires Giflib which was not compiled into ScummVM"
 msgstr ""
 
diff --git a/po/he.po b/po/he.po
index 5634a1a4b51..fdbd6378f7b 100644
--- a/po/he.po
+++ b/po/he.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.1.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-21 22:22+0000\n"
+"POT-Creation-Date: 2026-06-21 22:35+0000\n"
 "PO-Revision-Date: 2025-04-09 20:18+0000\n"
 "Last-Translator: Niv Baehr <bloop93 at gmail.com>\n"
 "Language-Team: Hebrew <https://translations.scummvm.org/projects/scummvm/"
@@ -587,7 +587,7 @@ msgstr "??????????"
 
 #: gui/editgamedialog.cpp:164 gui/options.cpp:2075 gui/options.cpp:2256
 #: backends/graphics/atari/atari-graphics.cpp:1056
-#: backends/graphics/sdl/sdl-graphics.cpp:487
+#: backends/graphics/sdl/sdl-graphics.cpp:491
 msgid "Graphics"
 msgstr "??????????"
 
@@ -1831,7 +1831,7 @@ msgstr "?????????? ????????????"
 msgid "Check on the scummvm.org website for updates of shader packs"
 msgstr "?????????? ?????????????? ???? ???????????? ???????????? ???????? scummvm.org"
 
-#: gui/options.cpp:1700 backends/graphics/sdl/sdl-graphics.cpp:478
+#: gui/options.cpp:1700 backends/graphics/sdl/sdl-graphics.cpp:482
 msgid "Fullscreen mode"
 msgstr "?????? ?????? ??????"
 
@@ -4248,7 +4248,7 @@ msgid "Choose a new folder"
 msgstr "?????????? ?????????? ????????"
 
 #: backends/graphics/atari/atari-graphics.cpp:1060
-#: backends/graphics/sdl/sdl-graphics.cpp:514 engines/scumm/help.cpp:103
+#: backends/graphics/sdl/sdl-graphics.cpp:518 engines/scumm/help.cpp:103
 msgid "Toggle aspect ratio correction"
 msgstr "?????????? ?????????? ?????? ??????????"
 
@@ -4320,34 +4320,34 @@ msgstr "SDL Surface"
 msgid "SDL Surface (forced 8bpp mode)"
 msgstr ""
 
-#: backends/graphics/sdl/sdl-graphics.cpp:420
+#: backends/graphics/sdl/sdl-graphics.cpp:424
 #, c-format
 msgid "Saved screenshot '%s'"
 msgstr "?????????? ?????? ???????? '%s'"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:435
+#: backends/graphics/sdl/sdl-graphics.cpp:439
 msgid "Could not save screenshot"
 msgstr "?????????? ?????????? ??????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:480
+#: backends/graphics/sdl/sdl-graphics.cpp:484
 msgid "Windowed mode"
 msgstr "?????? ????????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:491 engines/colony/metaengine.cpp:173
+#: backends/graphics/sdl/sdl-graphics.cpp:495 engines/colony/metaengine.cpp:173
 #: engines/scumm/help.cpp:88
 msgid "Toggle fullscreen"
 msgstr "?????????? ?????? ??????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:498 engines/scumm/help.cpp:100
+#: backends/graphics/sdl/sdl-graphics.cpp:502 engines/scumm/help.cpp:100
 #: engines/wintermute/keymapper_tables.h:960
 msgid "Toggle mouse capture"
 msgstr "?????????? ?????????? ????????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:503
+#: backends/graphics/sdl/sdl-graphics.cpp:507
 msgid "Toggle resizable window"
 msgstr ""
 
-#: backends/graphics/sdl/sdl-graphics.cpp:508 engines/stark/metaengine.cpp:240
+#: backends/graphics/sdl/sdl-graphics.cpp:512 engines/stark/metaengine.cpp:240
 #: engines/wintermute/keymapper_tables.h:646
 #: engines/wintermute/keymapper_tables.h:695
 #: engines/wintermute/keymapper_tables.h:1098
@@ -4360,27 +4360,27 @@ msgstr ""
 msgid "Save screenshot"
 msgstr "?????????? ?????????? ??????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:521
+#: backends/graphics/sdl/sdl-graphics.cpp:525
 msgid "Toggle linear filtered scaling"
 msgstr "?????????? ?????????? ?????? ???????? ??????????????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:528
+#: backends/graphics/sdl/sdl-graphics.cpp:532
 msgid "Cycle through stretch modes"
 msgstr "???????? ?????? ???????? ??????????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:534
+#: backends/graphics/sdl/sdl-graphics.cpp:538
 msgid "Increase the scale factor"
 msgstr "?????????? ?????? ????????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:540
+#: backends/graphics/sdl/sdl-graphics.cpp:544
 msgid "Decrease the scale factor"
 msgstr "?????????? ?????? ????????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:547
+#: backends/graphics/sdl/sdl-graphics.cpp:551
 msgid "Switch to the next scaler"
 msgstr "???????? ???? ???????? ?????? ?????????? ??????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:552
+#: backends/graphics/sdl/sdl-graphics.cpp:556
 msgid "Switch to the previous scaler"
 msgstr "???????? ???? ???????? ?????? ?????????? ??????????"
 
@@ -15932,7 +15932,7 @@ msgstr "?????????? ?????? ????????"
 msgid "Toggle text speech"
 msgstr "?????????? ?????????? ???????? ????????????"
 
-#: engines/twine/detection.cpp:188
+#: engines/twine/detection.cpp:187
 msgid "This version requires Giflib which was not compiled into ScummVM"
 msgstr "???????? ???? ?????????? ???? ???????????? Giflib ?????? ?????????? ???? ?????? ScummVM"
 
diff --git a/po/hi.po b/po/hi.po
index 158b2f9a793..d540fcb91fc 100644
--- a/po/hi.po
+++ b/po/hi.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-21 22:22+0000\n"
+"POT-Creation-Date: 2026-06-21 22:35+0000\n"
 "PO-Revision-Date: 2026-05-12 22:22+0000\n"
 "Last-Translator: KARTIK SAXENA <kartiksaxena2005 at gmail.com>\n"
 "Language-Team: Hindi <https://translations.scummvm.org/projects/scummvm/"
@@ -587,7 +587,7 @@ msgstr "GFX"
 
 #: gui/editgamedialog.cpp:164 gui/options.cpp:2075 gui/options.cpp:2256
 #: backends/graphics/atari/atari-graphics.cpp:1056
-#: backends/graphics/sdl/sdl-graphics.cpp:487
+#: backends/graphics/sdl/sdl-graphics.cpp:491
 msgid "Graphics"
 msgstr "???????????????????????????"
 
@@ -1880,7 +1880,7 @@ msgstr "???????????? ????????????????????? ????????????"
 msgid "Check on the scummvm.org website for updates of shader packs"
 msgstr "scummvm.org ????????????????????? ?????? ???????????? ????????? ?????? ??????????????? ?????? ???????????? ????????????"
 
-#: gui/options.cpp:1700 backends/graphics/sdl/sdl-graphics.cpp:478
+#: gui/options.cpp:1700 backends/graphics/sdl/sdl-graphics.cpp:482
 msgid "Fullscreen mode"
 msgstr "??????????????? ????????????????????? ?????????"
 
@@ -4163,7 +4163,7 @@ msgid "Choose a new folder"
 msgstr ""
 
 #: backends/graphics/atari/atari-graphics.cpp:1060
-#: backends/graphics/sdl/sdl-graphics.cpp:514 engines/scumm/help.cpp:103
+#: backends/graphics/sdl/sdl-graphics.cpp:518 engines/scumm/help.cpp:103
 msgid "Toggle aspect ratio correction"
 msgstr ""
 
@@ -4235,34 +4235,34 @@ msgstr ""
 msgid "SDL Surface (forced 8bpp mode)"
 msgstr ""
 
-#: backends/graphics/sdl/sdl-graphics.cpp:420
+#: backends/graphics/sdl/sdl-graphics.cpp:424
 #, c-format
 msgid "Saved screenshot '%s'"
 msgstr ""
 
-#: backends/graphics/sdl/sdl-graphics.cpp:435
+#: backends/graphics/sdl/sdl-graphics.cpp:439
 msgid "Could not save screenshot"
 msgstr ""
 
-#: backends/graphics/sdl/sdl-graphics.cpp:480
+#: backends/graphics/sdl/sdl-graphics.cpp:484
 msgid "Windowed mode"
 msgstr ""
 
-#: backends/graphics/sdl/sdl-graphics.cpp:491 engines/colony/metaengine.cpp:173
+#: backends/graphics/sdl/sdl-graphics.cpp:495 engines/colony/metaengine.cpp:173
 #: engines/scumm/help.cpp:88
 msgid "Toggle fullscreen"
 msgstr ""
 
-#: backends/graphics/sdl/sdl-graphics.cpp:498 engines/scumm/help.cpp:100
+#: backends/graphics/sdl/sdl-graphics.cpp:502 engines/scumm/help.cpp:100
 #: engines/wintermute/keymapper_tables.h:960
 msgid "Toggle mouse capture"
 msgstr ""
 
-#: backends/graphics/sdl/sdl-graphics.cpp:503
+#: backends/graphics/sdl/sdl-graphics.cpp:507
 msgid "Toggle resizable window"
 msgstr ""
 
-#: backends/graphics/sdl/sdl-graphics.cpp:508 engines/stark/metaengine.cpp:240
+#: backends/graphics/sdl/sdl-graphics.cpp:512 engines/stark/metaengine.cpp:240
 #: engines/wintermute/keymapper_tables.h:646
 #: engines/wintermute/keymapper_tables.h:695
 #: engines/wintermute/keymapper_tables.h:1098
@@ -4275,27 +4275,27 @@ msgstr ""
 msgid "Save screenshot"
 msgstr ""
 
-#: backends/graphics/sdl/sdl-graphics.cpp:521
+#: backends/graphics/sdl/sdl-graphics.cpp:525
 msgid "Toggle linear filtered scaling"
 msgstr ""
 
-#: backends/graphics/sdl/sdl-graphics.cpp:528
+#: backends/graphics/sdl/sdl-graphics.cpp:532
 msgid "Cycle through stretch modes"
 msgstr ""
 
-#: backends/graphics/sdl/sdl-graphics.cpp:534
+#: backends/graphics/sdl/sdl-graphics.cpp:538
 msgid "Increase the scale factor"
 msgstr ""
 
-#: backends/graphics/sdl/sdl-graphics.cpp:540
+#: backends/graphics/sdl/sdl-graphics.cpp:544
 msgid "Decrease the scale factor"
 msgstr ""
 
-#: backends/graphics/sdl/sdl-graphics.cpp:547
+#: backends/graphics/sdl/sdl-graphics.cpp:551
 msgid "Switch to the next scaler"
 msgstr "???????????? ?????????????????? ????????? ??????????????? ????????????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:552
+#: backends/graphics/sdl/sdl-graphics.cpp:556
 msgid "Switch to the previous scaler"
 msgstr ""
 
@@ -15053,7 +15053,7 @@ msgstr ""
 msgid "Toggle text speech"
 msgstr "Text ?????? Speech ??????????????? ????????????"
 
-#: engines/twine/detection.cpp:188
+#: engines/twine/detection.cpp:187
 msgid "This version requires Giflib which was not compiled into ScummVM"
 msgstr ""
 
diff --git a/po/hu_HU.po b/po/hu_HU.po
index d38854c02d3..0032caa1792 100644
--- a/po/hu_HU.po
+++ b/po/hu_HU.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-21 22:22+0000\n"
+"POT-Creation-Date: 2026-06-21 22:35+0000\n"
 "PO-Revision-Date: 2025-03-07 00:18+0000\n"
 "Last-Translator: Hov??nszki Tam??s <gahex68379 at lofiey.com>\n"
 "Language-Team: Hungarian <https://translations.scummvm.org/projects/scummvm/"
@@ -595,7 +595,7 @@ msgstr "GFX"
 
 #: gui/editgamedialog.cpp:164 gui/options.cpp:2075 gui/options.cpp:2256
 #: backends/graphics/atari/atari-graphics.cpp:1056
-#: backends/graphics/sdl/sdl-graphics.cpp:487
+#: backends/graphics/sdl/sdl-graphics.cpp:491
 msgid "Graphics"
 msgstr "Grafika"
 
@@ -1987,7 +1987,7 @@ msgstr "Shaderek let??lt??se"
 msgid "Check on the scummvm.org website for updates of shader packs"
 msgstr "L??togasson el a scummvm.org oldalra a shader csomag friss??t??sek??rt"
 
-#: gui/options.cpp:1700 backends/graphics/sdl/sdl-graphics.cpp:478
+#: gui/options.cpp:1700 backends/graphics/sdl/sdl-graphics.cpp:482
 msgid "Fullscreen mode"
 msgstr "Teljesk??perny??s m??d"
 
@@ -4415,7 +4415,7 @@ msgid "Choose a new folder"
 msgstr "??j mappa kiv??laszt??sa"
 
 #: backends/graphics/atari/atari-graphics.cpp:1060
-#: backends/graphics/sdl/sdl-graphics.cpp:514 engines/scumm/help.cpp:103
+#: backends/graphics/sdl/sdl-graphics.cpp:518 engines/scumm/help.cpp:103
 msgid "Toggle aspect ratio correction"
 msgstr "K??par??ny korrekci?? v??lt??sa"
 
@@ -4487,34 +4487,34 @@ msgstr "SDL Fel??let"
 msgid "SDL Surface (forced 8bpp mode)"
 msgstr "SDL Fel??let (k??nyszer??tett 8bpp m??d)"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:420
+#: backends/graphics/sdl/sdl-graphics.cpp:424
 #, c-format
 msgid "Saved screenshot '%s'"
 msgstr "'%s' k??perny??k??p mentve"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:435
+#: backends/graphics/sdl/sdl-graphics.cpp:439
 msgid "Could not save screenshot"
 msgstr "Sikertelen k??perny??k??p ment??s"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:480
+#: backends/graphics/sdl/sdl-graphics.cpp:484
 msgid "Windowed mode"
 msgstr "Ablakos m??d"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:491 engines/colony/metaengine.cpp:173
+#: backends/graphics/sdl/sdl-graphics.cpp:495 engines/colony/metaengine.cpp:173
 #: engines/scumm/help.cpp:88
 msgid "Toggle fullscreen"
 msgstr "Teljesk??perny??s kapcsol??"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:498 engines/scumm/help.cpp:100
+#: backends/graphics/sdl/sdl-graphics.cpp:502 engines/scumm/help.cpp:100
 #: engines/wintermute/keymapper_tables.h:960
 msgid "Toggle mouse capture"
 msgstr "Eg??r r??gz??t??s kapcsol??"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:503
+#: backends/graphics/sdl/sdl-graphics.cpp:507
 msgid "Toggle resizable window"
 msgstr "??jra-m??retezhet?? ablak bekapcsol??sa"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:508 engines/stark/metaengine.cpp:240
+#: backends/graphics/sdl/sdl-graphics.cpp:512 engines/stark/metaengine.cpp:240
 #: engines/wintermute/keymapper_tables.h:646
 #: engines/wintermute/keymapper_tables.h:695
 #: engines/wintermute/keymapper_tables.h:1098
@@ -4527,27 +4527,27 @@ msgstr "??jra-m??retezhet?? ablak bekapcsol??sa"
 msgid "Save screenshot"
 msgstr "K??perny??k??p ment??se"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:521
+#: backends/graphics/sdl/sdl-graphics.cpp:525
 msgid "Toggle linear filtered scaling"
 msgstr "Line??ris filter m??retez??s kapcsol??"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:528
+#: backends/graphics/sdl/sdl-graphics.cpp:532
 msgid "Cycle through stretch modes"
 msgstr "Kiny??jt??si m??dok k??z??tti v??lt??s"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:534
+#: backends/graphics/sdl/sdl-graphics.cpp:538
 msgid "Increase the scale factor"
 msgstr "M??retez?? t??nyez?? n??vel??se"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:540
+#: backends/graphics/sdl/sdl-graphics.cpp:544
 msgid "Decrease the scale factor"
 msgstr "M??retez?? t??nyez?? cs??kkent??se"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:547
+#: backends/graphics/sdl/sdl-graphics.cpp:551
 msgid "Switch to the next scaler"
 msgstr "V??lt??s a k??vetkez?? sk??l??z??ra"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:552
+#: backends/graphics/sdl/sdl-graphics.cpp:556
 msgid "Switch to the previous scaler"
 msgstr "V??lt??s az el??z?? sk??l??z??ra"
 
@@ -16362,7 +16362,7 @@ msgstr "Ablak st??lus kapcsol??"
 msgid "Toggle text speech"
 msgstr "Sz??veg besz??d kapcsol??"
 
-#: engines/twine/detection.cpp:188
+#: engines/twine/detection.cpp:187
 msgid "This version requires Giflib which was not compiled into ScummVM"
 msgstr "Ehhez a verzi??hoz Giflib sz??ks??ges, ami nincs beford??tva a ScummVM-be"
 
diff --git a/po/it_IT.po b/po/it_IT.po
index f10ca4c04c9..9cb39e4a4e9 100644
--- a/po/it_IT.po
+++ b/po/it_IT.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-21 22:22+0000\n"
+"POT-Creation-Date: 2026-06-21 22:35+0000\n"
 "PO-Revision-Date: 2026-06-20 15:07+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Italian <https://translations.scummvm.org/projects/scummvm/"
@@ -592,7 +592,7 @@ msgstr "Graf."
 
 #: gui/editgamedialog.cpp:164 gui/options.cpp:2075 gui/options.cpp:2256
 #: backends/graphics/atari/atari-graphics.cpp:1056
-#: backends/graphics/sdl/sdl-graphics.cpp:487
+#: backends/graphics/sdl/sdl-graphics.cpp:491
 msgid "Graphics"
 msgstr "Grafica"
 
@@ -1944,7 +1944,7 @@ msgstr "Scarica Shaders"
 msgid "Check on the scummvm.org website for updates of shader packs"
 msgstr "Ricerca aggiornamenti dei pacchetti shaders sul sito scummvm.org"
 
-#: gui/options.cpp:1700 backends/graphics/sdl/sdl-graphics.cpp:478
+#: gui/options.cpp:1700 backends/graphics/sdl/sdl-graphics.cpp:482
 msgid "Fullscreen mode"
 msgstr "Modalit?? a schermo intero"
 
@@ -4364,7 +4364,7 @@ msgid "Choose a new folder"
 msgstr "Seleziona cartella"
 
 #: backends/graphics/atari/atari-graphics.cpp:1060
-#: backends/graphics/sdl/sdl-graphics.cpp:514 engines/scumm/help.cpp:103
+#: backends/graphics/sdl/sdl-graphics.cpp:518 engines/scumm/help.cpp:103
 msgid "Toggle aspect ratio correction"
 msgstr "Attiva/disattiva correzione proporzioni"
 
@@ -4436,34 +4436,34 @@ msgstr "SDL Surface"
 msgid "SDL Surface (forced 8bpp mode)"
 msgstr "SDL Surface (modalit?? a 8bpp forzata)"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:420
+#: backends/graphics/sdl/sdl-graphics.cpp:424
 #, c-format
 msgid "Saved screenshot '%s'"
 msgstr "Salvato screenshot '%s'"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:435
+#: backends/graphics/sdl/sdl-graphics.cpp:439
 msgid "Could not save screenshot"
 msgstr "Non ?? stato possibile salvare lo screenshot"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:480
+#: backends/graphics/sdl/sdl-graphics.cpp:484
 msgid "Windowed mode"
 msgstr "Modalit?? finestra"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:491 engines/colony/metaengine.cpp:173
+#: backends/graphics/sdl/sdl-graphics.cpp:495 engines/colony/metaengine.cpp:173
 #: engines/scumm/help.cpp:88
 msgid "Toggle fullscreen"
 msgstr "Attiva/disattiva schermo intero"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:498 engines/scumm/help.cpp:100
+#: backends/graphics/sdl/sdl-graphics.cpp:502 engines/scumm/help.cpp:100
 #: engines/wintermute/keymapper_tables.h:960
 msgid "Toggle mouse capture"
 msgstr "Attiva/disattiva cattura puntatore"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:503
+#: backends/graphics/sdl/sdl-graphics.cpp:507
 msgid "Toggle resizable window"
 msgstr "Attiva/Disattiva finestra ridimensionabile"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:508 engines/stark/metaengine.cpp:240
+#: backends/graphics/sdl/sdl-graphics.cpp:512 engines/stark/metaengine.cpp:240
 #: engines/wintermute/keymapper_tables.h:646
 #: engines/wintermute/keymapper_tables.h:695
 #: engines/wintermute/keymapper_tables.h:1098
@@ -4476,27 +4476,27 @@ msgstr "Attiva/Disattiva finestra ridimensionabile"
 msgid "Save screenshot"
 msgstr "Salva screenshot"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:521
+#: backends/graphics/sdl/sdl-graphics.cpp:525
 msgid "Toggle linear filtered scaling"
 msgstr "Attiva/disattiva filtro grafico"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:528
+#: backends/graphics/sdl/sdl-graphics.cpp:532
 msgid "Cycle through stretch modes"
 msgstr "Itera tra le modalit?? di ridimensionamento immagine"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:534
+#: backends/graphics/sdl/sdl-graphics.cpp:538
 msgid "Increase the scale factor"
 msgstr "Aumenta fattore di scala"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:540
+#: backends/graphics/sdl/sdl-graphics.cpp:544
 msgid "Decrease the scale factor"
 msgstr "Riduci fattore di scala"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:547
+#: backends/graphics/sdl/sdl-graphics.cpp:551
 msgid "Switch to the next scaler"
 msgstr "Passa alla modalit?? (scaler) successiva"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:552
+#: backends/graphics/sdl/sdl-graphics.cpp:556
 msgid "Switch to the previous scaler"
 msgstr "Passa alla modalit?? (scaler) precedente"
 
@@ -15353,7 +15353,7 @@ msgstr "Alterna stile pannello comandi"
 msgid "Toggle text speech"
 msgstr "Attiva/Disattiva sintesi vocale"
 
-#: engines/twine/detection.cpp:188
+#: engines/twine/detection.cpp:187
 msgid "This version requires Giflib which was not compiled into ScummVM"
 msgstr ""
 "Questa versione richiede Giflib che non ?? stata inclusa durante la "
diff --git a/po/ja.po b/po/ja.po
index e095095ea8c..c561ca49da3 100644
--- a/po/ja.po
+++ b/po/ja.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-21 22:22+0000\n"
+"POT-Creation-Date: 2026-06-21 22:35+0000\n"
 "PO-Revision-Date: 2026-06-04 07:22+0000\n"
 "Last-Translator: VAN-Gluon <van.gluonp at gmail.com>\n"
 "Language-Team: Japanese <https://translations.scummvm.org/projects/scummvm/"
@@ -589,7 +589,7 @@ msgstr "GFX"
 
 #: gui/editgamedialog.cpp:164 gui/options.cpp:2075 gui/options.cpp:2256
 #: backends/graphics/atari/atari-graphics.cpp:1056
-#: backends/graphics/sdl/sdl-graphics.cpp:487
+#: backends/graphics/sdl/sdl-graphics.cpp:491
 msgid "Graphics"
 msgstr "??????"
 
@@ -1866,7 +1866,7 @@ msgstr "??????????????????DL"
 msgid "Check on the scummvm.org website for updates of shader packs"
 msgstr "????????????????????????????????????scummvm.org????????????????????????"
 
-#: gui/options.cpp:1700 backends/graphics/sdl/sdl-graphics.cpp:478
+#: gui/options.cpp:1700 backends/graphics/sdl/sdl-graphics.cpp:482
 msgid "Fullscreen mode"
 msgstr "???????????????"
 
@@ -4230,7 +4230,7 @@ msgid "Choose a new folder"
 msgstr "???????????????????????????"
 
 #: backends/graphics/atari/atari-graphics.cpp:1060
-#: backends/graphics/sdl/sdl-graphics.cpp:514 engines/scumm/help.cpp:103
+#: backends/graphics/sdl/sdl-graphics.cpp:518 engines/scumm/help.cpp:103
 msgid "Toggle aspect ratio correction"
 msgstr "????????????????????????"
 
@@ -4302,34 +4302,34 @@ msgstr "SDL???????????????"
 msgid "SDL Surface (forced 8bpp mode)"
 msgstr "SDL???????????????(??????8bpp?????????)"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:420
+#: backends/graphics/sdl/sdl-graphics.cpp:424
 #, c-format
 msgid "Saved screenshot '%s'"
 msgstr "???????????????????????????'%s'?????????????????????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:435
+#: backends/graphics/sdl/sdl-graphics.cpp:439
 msgid "Could not save screenshot"
 msgstr "????????????????????????????????????????????????????????????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:480
+#: backends/graphics/sdl/sdl-graphics.cpp:484
 msgid "Windowed mode"
 msgstr "????????????????????????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:491 engines/colony/metaengine.cpp:173
+#: backends/graphics/sdl/sdl-graphics.cpp:495 engines/colony/metaengine.cpp:173
 #: engines/scumm/help.cpp:88
 msgid "Toggle fullscreen"
 msgstr "????????????????????????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:498 engines/scumm/help.cpp:100
+#: backends/graphics/sdl/sdl-graphics.cpp:502 engines/scumm/help.cpp:100
 #: engines/wintermute/keymapper_tables.h:960
 msgid "Toggle mouse capture"
 msgstr "????????????????????????????????????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:503
+#: backends/graphics/sdl/sdl-graphics.cpp:507
 msgid "Toggle resizable window"
 msgstr "???????????????/???????????????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:508 engines/stark/metaengine.cpp:240
+#: backends/graphics/sdl/sdl-graphics.cpp:512 engines/stark/metaengine.cpp:240
 #: engines/wintermute/keymapper_tables.h:646
 #: engines/wintermute/keymapper_tables.h:695
 #: engines/wintermute/keymapper_tables.h:1098
@@ -4342,27 +4342,27 @@ msgstr "???????????????/???????????????"
 msgid "Save screenshot"
 msgstr "???????????????????????????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:521
+#: backends/graphics/sdl/sdl-graphics.cpp:525
 msgid "Toggle linear filtered scaling"
 msgstr "??????????????????????????????????????????????????????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:528
+#: backends/graphics/sdl/sdl-graphics.cpp:532
 msgid "Cycle through stretch modes"
 msgstr "??????????????????????????????????????????????????????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:534
+#: backends/graphics/sdl/sdl-graphics.cpp:538
 msgid "Increase the scale factor"
 msgstr "???????????????????????????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:540
+#: backends/graphics/sdl/sdl-graphics.cpp:544
 msgid "Decrease the scale factor"
 msgstr "???????????????????????????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:547
+#: backends/graphics/sdl/sdl-graphics.cpp:551
 msgid "Switch to the next scaler"
 msgstr "????????????????????????????????????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:552
+#: backends/graphics/sdl/sdl-graphics.cpp:556
 msgid "Switch to the previous scaler"
 msgstr "????????????????????????????????????"
 
@@ -16021,7 +16021,7 @@ msgstr "??????????????????????????????"
 msgid "Toggle text speech"
 msgstr "?????????????????????"
 
-#: engines/twine/detection.cpp:188
+#: engines/twine/detection.cpp:187
 msgid "This version requires Giflib which was not compiled into ScummVM"
 msgstr ""
 "???????????????????????????Giflib??????????????????????????????ScummVM?????????????????????????????????"
diff --git a/po/ka.po b/po/ka.po
index 6b5da6fca50..bc4aa66f181 100644
--- a/po/ka.po
+++ b/po/ka.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.8.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-21 22:22+0000\n"
+"POT-Creation-Date: 2026-06-21 22:35+0000\n"
 "PO-Revision-Date: 2026-06-05 10:22+0000\n"
 "Last-Translator: NorwayFun <temuri.doghonadze at gmail.com>\n"
 "Language-Team: Georgian <https://translations.scummvm.org/projects/scummvm/"
@@ -578,7 +578,7 @@ msgstr "GFX"
 
 #: gui/editgamedialog.cpp:164 gui/options.cpp:2075 gui/options.cpp:2256
 #: backends/graphics/atari/atari-graphics.cpp:1056
-#: backends/graphics/sdl/sdl-graphics.cpp:487
+#: backends/graphics/sdl/sdl-graphics.cpp:491
 msgid "Graphics"
 msgstr "?????????????????????"
 
@@ -1806,7 +1806,7 @@ msgstr "?????????????????????????????? ???????????????????????????"
 msgid "Check on the scummvm.org website for updates of shader packs"
 msgstr ""
 
-#: gui/options.cpp:1700 backends/graphics/sdl/sdl-graphics.cpp:478
+#: gui/options.cpp:1700 backends/graphics/sdl/sdl-graphics.cpp:482
 msgid "Fullscreen mode"
 msgstr "??????????????? ????????????????????? ??????????????????"
 
@@ -4037,7 +4037,7 @@ msgid "Choose a new folder"
 msgstr "????????????????????? ??????????????? ???????????????????????????"
 
 #: backends/graphics/atari/atari-graphics.cpp:1060
-#: backends/graphics/sdl/sdl-graphics.cpp:514 engines/scumm/help.cpp:103
+#: backends/graphics/sdl/sdl-graphics.cpp:518 engines/scumm/help.cpp:103
 msgid "Toggle aspect ratio correction"
 msgstr "??????????????? ???????????????????????? ?????????????????????????????? ????????????????????????"
 
@@ -4109,34 +4109,34 @@ msgstr "SDL ????????????????????????"
 msgid "SDL Surface (forced 8bpp mode)"
 msgstr ""
 
-#: backends/graphics/sdl/sdl-graphics.cpp:420
+#: backends/graphics/sdl/sdl-graphics.cpp:424
 #, c-format
 msgid "Saved screenshot '%s'"
 msgstr "??????????????????????????? ????????????????????? ???????????????????????? '%s'"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:435
+#: backends/graphics/sdl/sdl-graphics.cpp:439
 msgid "Could not save screenshot"
 msgstr "????????????????????? ??????????????????????????? ????????????????????? ?????????????????????????????????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:480
+#: backends/graphics/sdl/sdl-graphics.cpp:484
 msgid "Windowed mode"
 msgstr "????????????????????? ??????????????????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:491 engines/colony/metaengine.cpp:173
+#: backends/graphics/sdl/sdl-graphics.cpp:495 engines/colony/metaengine.cpp:173
 #: engines/scumm/help.cpp:88
 msgid "Toggle fullscreen"
 msgstr "???????????? ????????????????????? ????????????????????? ????????????/??????????????????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:498 engines/scumm/help.cpp:100
+#: backends/graphics/sdl/sdl-graphics.cpp:502 engines/scumm/help.cpp:100
 #: engines/wintermute/keymapper_tables.h:960
 msgid "Toggle mouse capture"
 msgstr "????????????????????? ????????????????????? ????????????????????????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:503
+#: backends/graphics/sdl/sdl-graphics.cpp:507
 msgid "Toggle resizable window"
 msgstr "???????????????????????????????????? ????????????????????? ????????????????????????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:508 engines/stark/metaengine.cpp:240
+#: backends/graphics/sdl/sdl-graphics.cpp:512 engines/stark/metaengine.cpp:240
 #: engines/wintermute/keymapper_tables.h:646
 #: engines/wintermute/keymapper_tables.h:695
 #: engines/wintermute/keymapper_tables.h:1098
@@ -4149,27 +4149,27 @@ msgstr "???????????????????????????????????? ????????????????????? ????????????
 msgid "Save screenshot"
 msgstr "????????????????????? ??????????????????????????? ?????????????????????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:521
+#: backends/graphics/sdl/sdl-graphics.cpp:525
 msgid "Toggle linear filtered scaling"
 msgstr "????????????????????????????????? ?????????????????? ?????????????????????????????? ????????????????????????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:528
+#: backends/graphics/sdl/sdl-graphics.cpp:532
 msgid "Cycle through stretch modes"
 msgstr "???????????????????????? ???????????????????????? ???????????????????????? ???????????????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:534
+#: backends/graphics/sdl/sdl-graphics.cpp:538
 msgid "Increase the scale factor"
 msgstr "??????????????????????????? ???????????????????????? ??????????????????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:540
+#: backends/graphics/sdl/sdl-graphics.cpp:544
 msgid "Decrease the scale factor"
 msgstr "??????????????????????????? ???????????????????????? ???????????????????????????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:547
+#: backends/graphics/sdl/sdl-graphics.cpp:551
 msgid "Switch to the next scaler"
 msgstr ""
 
-#: backends/graphics/sdl/sdl-graphics.cpp:552
+#: backends/graphics/sdl/sdl-graphics.cpp:556
 msgid "Switch to the previous scaler"
 msgstr ""
 
@@ -14042,7 +14042,7 @@ msgstr "????????????????????? ?????????????????? ????????????????????????"
 msgid "Toggle text speech"
 msgstr "?????????????????????-????????????????????????-??????????????????????????? ????????????????????????"
 
-#: engines/twine/detection.cpp:188
+#: engines/twine/detection.cpp:187
 msgid "This version requires Giflib which was not compiled into ScummVM"
 msgstr ""
 
diff --git a/po/ko.po b/po/ko.po
index 957d26d342b..70058da7c8a 100644
--- a/po/ko.po
+++ b/po/ko.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-21 22:22+0000\n"
+"POT-Creation-Date: 2026-06-21 22:35+0000\n"
 "PO-Revision-Date: 2026-05-19 20:22+0000\n"
 "Last-Translator: Hoseok Seo <ddinghoya at gmail.com>\n"
 "Language-Team: Korean <https://translations.scummvm.org/projects/scummvm/"
@@ -589,7 +589,7 @@ msgstr "GFX"
 
 #: gui/editgamedialog.cpp:164 gui/options.cpp:2075 gui/options.cpp:2256
 #: backends/graphics/atari/atari-graphics.cpp:1056
-#: backends/graphics/sdl/sdl-graphics.cpp:487
+#: backends/graphics/sdl/sdl-graphics.cpp:491
 msgid "Graphics"
 msgstr "?????????"
 
@@ -1917,7 +1917,7 @@ msgstr "????????? ????????????"
 msgid "Check on the scummvm.org website for updates of shader packs"
 msgstr "????????? ??? ??????????????? scummvm.org ?????????????????? ??????"
 
-#: gui/options.cpp:1700 backends/graphics/sdl/sdl-graphics.cpp:478
+#: gui/options.cpp:1700 backends/graphics/sdl/sdl-graphics.cpp:482
 msgid "Fullscreen mode"
 msgstr "???????????? ??????"
 
@@ -4284,7 +4284,7 @@ msgid "Choose a new folder"
 msgstr "??? ????????? ??????"
 
 #: backends/graphics/atari/atari-graphics.cpp:1060
-#: backends/graphics/sdl/sdl-graphics.cpp:514 engines/scumm/help.cpp:103
+#: backends/graphics/sdl/sdl-graphics.cpp:518 engines/scumm/help.cpp:103
 msgid "Toggle aspect ratio correction"
 msgstr "????????? ?????? ??????"
 
@@ -4356,34 +4356,34 @@ msgstr "SDL ??????"
 msgid "SDL Surface (forced 8bpp mode)"
 msgstr "SDL ?????? (?????? 8bpp ??????)"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:420
+#: backends/graphics/sdl/sdl-graphics.cpp:424
 #, c-format
 msgid "Saved screenshot '%s'"
 msgstr "'%s' ???????????? ???????????????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:435
+#: backends/graphics/sdl/sdl-graphics.cpp:439
 msgid "Could not save screenshot"
 msgstr "??????????????? ????????? ??? ??????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:480
+#: backends/graphics/sdl/sdl-graphics.cpp:484
 msgid "Windowed mode"
 msgstr "??? ??????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:491 engines/colony/metaengine.cpp:173
+#: backends/graphics/sdl/sdl-graphics.cpp:495 engines/colony/metaengine.cpp:173
 #: engines/scumm/help.cpp:88
 msgid "Toggle fullscreen"
 msgstr "???????????? ??????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:498 engines/scumm/help.cpp:100
+#: backends/graphics/sdl/sdl-graphics.cpp:502 engines/scumm/help.cpp:100
 #: engines/wintermute/keymapper_tables.h:960
 msgid "Toggle mouse capture"
 msgstr "????????? ?????? ??????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:503
+#: backends/graphics/sdl/sdl-graphics.cpp:507
 msgid "Toggle resizable window"
 msgstr "?????? ?????? ????????? ??? ??????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:508 engines/stark/metaengine.cpp:240
+#: backends/graphics/sdl/sdl-graphics.cpp:512 engines/stark/metaengine.cpp:240
 #: engines/wintermute/keymapper_tables.h:646
 #: engines/wintermute/keymapper_tables.h:695
 #: engines/wintermute/keymapper_tables.h:1098
@@ -4396,27 +4396,27 @@ msgstr "?????? ?????? ????????? ??? ??????"
 msgid "Save screenshot"
 msgstr "???????????? ??????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:521
+#: backends/graphics/sdl/sdl-graphics.cpp:525
 msgid "Toggle linear filtered scaling"
 msgstr "?????? ?????? ?????? ?????? ??????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:528
+#: backends/graphics/sdl/sdl-graphics.cpp:532
 msgid "Cycle through stretch modes"
 msgstr "????????? ????????? ??????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:534
+#: backends/graphics/sdl/sdl-graphics.cpp:538
 msgid "Increase the scale factor"
 msgstr "??????/?????? ?????? ??????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:540
+#: backends/graphics/sdl/sdl-graphics.cpp:544
 msgid "Decrease the scale factor"
 msgstr "??????/?????? ?????? ??????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:547
+#: backends/graphics/sdl/sdl-graphics.cpp:551
 msgid "Switch to the next scaler"
 msgstr "?????? ?????? ?????? ??????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:552
+#: backends/graphics/sdl/sdl-graphics.cpp:556
 msgid "Switch to the previous scaler"
 msgstr "?????? ?????? ?????? ??????"
 
@@ -15046,7 +15046,7 @@ msgstr "?????? ????????? ??????"
 msgid "Toggle text speech"
 msgstr "TTS"
 
-#: engines/twine/detection.cpp:188
+#: engines/twine/detection.cpp:187
 msgid "This version requires Giflib which was not compiled into ScummVM"
 msgstr "??? ???????????? ScummVM??? ??????????????? ?????? Giflib??? ?????????"
 
diff --git a/po/nb_NO.po b/po/nb_NO.po
index 27df739d374..8516ba6b299 100644
--- a/po/nb_NO.po
+++ b/po/nb_NO.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-21 22:22+0000\n"
+"POT-Creation-Date: 2026-06-21 22:35+0000\n"
 "PO-Revision-Date: 2026-02-07 15:22+0000\n"
 "Last-Translator: Einar Johan Tr??an S??m??en <einarjohants at gmail.com>\n"
 "Language-Team: Norwegian Bokm??l <https://translations.scummvm.org/projects/"
@@ -593,7 +593,7 @@ msgstr "GFX"
 
 #: gui/editgamedialog.cpp:164 gui/options.cpp:2075 gui/options.cpp:2256
 #: backends/graphics/atari/atari-graphics.cpp:1056
-#: backends/graphics/sdl/sdl-graphics.cpp:487
+#: backends/graphics/sdl/sdl-graphics.cpp:491
 msgid "Graphics"
 msgstr "Grafikk"
 
@@ -1990,7 +1990,7 @@ msgstr "Last ned Shaders"
 msgid "Check on the scummvm.org website for updates of shader packs"
 msgstr "Sjekk nettsiden, scummvm.org, for oppdateringer av shaderpakker"
 
-#: gui/options.cpp:1700 backends/graphics/sdl/sdl-graphics.cpp:478
+#: gui/options.cpp:1700 backends/graphics/sdl/sdl-graphics.cpp:482
 msgid "Fullscreen mode"
 msgstr "Fullskjermsmodus"
 
@@ -4417,7 +4417,7 @@ msgid "Choose a new folder"
 msgstr "Velg ny mappe"
 
 #: backends/graphics/atari/atari-graphics.cpp:1060
-#: backends/graphics/sdl/sdl-graphics.cpp:514 engines/scumm/help.cpp:103
+#: backends/graphics/sdl/sdl-graphics.cpp:518 engines/scumm/help.cpp:103
 msgid "Toggle aspect ratio correction"
 msgstr "Skru korrigering av bildeformat av/p??"
 
@@ -4489,34 +4489,34 @@ msgstr "SDL-overflate"
 msgid "SDL Surface (forced 8bpp mode)"
 msgstr "SDL Surface (tvungen 8bpp modus)"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:420
+#: backends/graphics/sdl/sdl-graphics.cpp:424
 #, c-format
 msgid "Saved screenshot '%s'"
 msgstr "Lagret skjermbilde (%s)"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:435
+#: backends/graphics/sdl/sdl-graphics.cpp:439
 msgid "Could not save screenshot"
 msgstr "Kunne ikke lagre skjermbilde"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:480
+#: backends/graphics/sdl/sdl-graphics.cpp:484
 msgid "Windowed mode"
 msgstr "Vindusmodus"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:491 engines/colony/metaengine.cpp:173
+#: backends/graphics/sdl/sdl-graphics.cpp:495 engines/colony/metaengine.cpp:173
 #: engines/scumm/help.cpp:88
 msgid "Toggle fullscreen"
 msgstr "Veksle fullskjerm"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:498 engines/scumm/help.cpp:100
+#: backends/graphics/sdl/sdl-graphics.cpp:502 engines/scumm/help.cpp:100
 #: engines/wintermute/keymapper_tables.h:960
 msgid "Toggle mouse capture"
 msgstr "Veksle musl??sing"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:503
+#: backends/graphics/sdl/sdl-graphics.cpp:507
 msgid "Toggle resizable window"
 msgstr ""
 
-#: backends/graphics/sdl/sdl-graphics.cpp:508 engines/stark/metaengine.cpp:240
+#: backends/graphics/sdl/sdl-graphics.cpp:512 engines/stark/metaengine.cpp:240
 #: engines/wintermute/keymapper_tables.h:646
 #: engines/wintermute/keymapper_tables.h:695
 #: engines/wintermute/keymapper_tables.h:1098
@@ -4529,27 +4529,27 @@ msgstr ""
 msgid "Save screenshot"
 msgstr "Lagre skjermbilde"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:521
+#: backends/graphics/sdl/sdl-graphics.cpp:525
 msgid "Toggle linear filtered scaling"
 msgstr "Skru skalering med line??r filtrering av/p??"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:528
+#: backends/graphics/sdl/sdl-graphics.cpp:532
 msgid "Cycle through stretch modes"
 msgstr "Bla gjennom strekkmoduser"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:534
+#: backends/graphics/sdl/sdl-graphics.cpp:538
 msgid "Increase the scale factor"
 msgstr "??k skaleringsfaktoren"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:540
+#: backends/graphics/sdl/sdl-graphics.cpp:544
 msgid "Decrease the scale factor"
 msgstr "Minsk skaleringsfaktoren"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:547
+#: backends/graphics/sdl/sdl-graphics.cpp:551
 msgid "Switch to the next scaler"
 msgstr "Bytt til neste skaleringsmetode"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:552
+#: backends/graphics/sdl/sdl-graphics.cpp:556
 msgid "Switch to the previous scaler"
 msgstr "Bytt til forrige skaleringsmetode"
 
@@ -16524,7 +16524,7 @@ msgstr "Skru Rask modus av/p??"
 msgid "Toggle text speech"
 msgstr "Bruk tekst og tale"
 
-#: engines/twine/detection.cpp:188
+#: engines/twine/detection.cpp:187
 msgid "This version requires Giflib which was not compiled into ScummVM"
 msgstr ""
 "Denne versjonen krever Giflib som ikke har blitt innkompilert i ScummVM"
diff --git a/po/nl_NL.po b/po/nl_NL.po
index 7993a2d28cf..76b6c65b66c 100644
--- a/po/nl_NL.po
+++ b/po/nl_NL.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.9.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-21 22:22+0000\n"
+"POT-Creation-Date: 2026-06-21 22:35+0000\n"
 "PO-Revision-Date: 2026-02-11 14:23+0000\n"
 "Last-Translator: SecularSteve <fairfull.playing at gmail.com>\n"
 "Language-Team: Dutch <https://translations.scummvm.org/projects/scummvm/"
@@ -590,7 +590,7 @@ msgstr "GFX"
 
 #: gui/editgamedialog.cpp:164 gui/options.cpp:2075 gui/options.cpp:2256
 #: backends/graphics/atari/atari-graphics.cpp:1056
-#: backends/graphics/sdl/sdl-graphics.cpp:487
+#: backends/graphics/sdl/sdl-graphics.cpp:491
 msgid "Graphics"
 msgstr "Beeld"
 
@@ -1919,7 +1919,7 @@ msgstr "Shaders Downloaden"
 msgid "Check on the scummvm.org website for updates of shader packs"
 msgstr "Controleer de scummvm.org website op updates van shader packs"
 
-#: gui/options.cpp:1700 backends/graphics/sdl/sdl-graphics.cpp:478
+#: gui/options.cpp:1700 backends/graphics/sdl/sdl-graphics.cpp:482
 msgid "Fullscreen mode"
 msgstr "Volledig-scherm modus"
 
@@ -4343,7 +4343,7 @@ msgid "Choose a new folder"
 msgstr "Selecteer een nieuwe map"
 
 #: backends/graphics/atari/atari-graphics.cpp:1060
-#: backends/graphics/sdl/sdl-graphics.cpp:514 engines/scumm/help.cpp:103
+#: backends/graphics/sdl/sdl-graphics.cpp:518 engines/scumm/help.cpp:103
 msgid "Toggle aspect ratio correction"
 msgstr "Pixelverhoudingcorrectie aan-/uitzetten"
 
@@ -4415,34 +4415,34 @@ msgstr "SDL Oppervlak"
 msgid "SDL Surface (forced 8bpp mode)"
 msgstr "SDL Surface (geforceerde 8bpp modus)"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:420
+#: backends/graphics/sdl/sdl-graphics.cpp:424
 #, c-format
 msgid "Saved screenshot '%s'"
 msgstr "Schermafdruk '%s' bewaard"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:435
+#: backends/graphics/sdl/sdl-graphics.cpp:439
 msgid "Could not save screenshot"
 msgstr "Schermafdruk opslaan mislukt"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:480
+#: backends/graphics/sdl/sdl-graphics.cpp:484
 msgid "Windowed mode"
 msgstr "Venstermodus"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:491 engines/colony/metaengine.cpp:173
+#: backends/graphics/sdl/sdl-graphics.cpp:495 engines/colony/metaengine.cpp:173
 #: engines/scumm/help.cpp:88
 msgid "Toggle fullscreen"
 msgstr "Volledig scherm aan-/uitzetten"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:498 engines/scumm/help.cpp:100
+#: backends/graphics/sdl/sdl-graphics.cpp:502 engines/scumm/help.cpp:100
 #: engines/wintermute/keymapper_tables.h:960
 msgid "Toggle mouse capture"
 msgstr "Mousecapture aan-/uitzetten"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:503
+#: backends/graphics/sdl/sdl-graphics.cpp:507
 msgid "Toggle resizable window"
 msgstr "Schakel aanpasbaar venster in/uit"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:508 engines/stark/metaengine.cpp:240
+#: backends/graphics/sdl/sdl-graphics.cpp:512 engines/stark/metaengine.cpp:240
 #: engines/wintermute/keymapper_tables.h:646
 #: engines/wintermute/keymapper_tables.h:695
 #: engines/wintermute/keymapper_tables.h:1098
@@ -4455,28 +4455,28 @@ msgstr "Schakel aanpasbaar venster in/uit"
 msgid "Save screenshot"
 msgstr "Bewaar schermafdruk"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:521
+#: backends/graphics/sdl/sdl-graphics.cpp:525
 msgid "Toggle linear filtered scaling"
 msgstr "Lineair filteren tijdens het schalen aan-/uitzetten"
 
 # can this be changed into "Could not switch to video mode '%s'"?
-#: backends/graphics/sdl/sdl-graphics.cpp:528
+#: backends/graphics/sdl/sdl-graphics.cpp:532
 msgid "Cycle through stretch modes"
 msgstr "Loop door uitrekmodi"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:534
+#: backends/graphics/sdl/sdl-graphics.cpp:538
 msgid "Increase the scale factor"
 msgstr "Verhoog de schalingsfactor"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:540
+#: backends/graphics/sdl/sdl-graphics.cpp:544
 msgid "Decrease the scale factor"
 msgstr "Verlaag de schalingsfactor"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:547
+#: backends/graphics/sdl/sdl-graphics.cpp:551
 msgid "Switch to the next scaler"
 msgstr "Schakel naar de volgende schaalmode"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:552
+#: backends/graphics/sdl/sdl-graphics.cpp:556
 msgid "Switch to the previous scaler"
 msgstr "Schakel naar de vorige schaalmode"
 
@@ -15450,7 +15450,7 @@ msgstr "Paneelstijl wisselen"
 msgid "Toggle text speech"
 msgstr "Tekst spraak in-/uitschakelen"
 
-#: engines/twine/detection.cpp:188
+#: engines/twine/detection.cpp:187
 msgid "This version requires Giflib which was not compiled into ScummVM"
 msgstr "Dit spel vereist Giflib, welke niet is meegecompileerd in ScummVM"
 
diff --git a/po/nn_NO.po b/po/nn_NO.po
index 92e3cdb0cca..474eaf6ef5f 100644
--- a/po/nn_NO.po
+++ b/po/nn_NO.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-21 22:22+0000\n"
+"POT-Creation-Date: 2026-06-21 22:35+0000\n"
 "PO-Revision-Date: 2023-12-14 14:28+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Norwegian Nynorsk <https://translations.scummvm.org/projects/"
@@ -600,7 +600,7 @@ msgstr "GFX"
 
 #: gui/editgamedialog.cpp:164 gui/options.cpp:2075 gui/options.cpp:2256
 #: backends/graphics/atari/atari-graphics.cpp:1056
-#: backends/graphics/sdl/sdl-graphics.cpp:487
+#: backends/graphics/sdl/sdl-graphics.cpp:491
 msgid "Graphics"
 msgstr "Grafikk"
 
@@ -1874,7 +1874,7 @@ msgstr "Last fil:"
 msgid "Check on the scummvm.org website for updates of shader packs"
 msgstr "SJ?? etter oppdateringar..."
 
-#: gui/options.cpp:1700 backends/graphics/sdl/sdl-graphics.cpp:478
+#: gui/options.cpp:1700 backends/graphics/sdl/sdl-graphics.cpp:482
 msgid "Fullscreen mode"
 msgstr "Fullskjermsmodus"
 
@@ -4285,7 +4285,7 @@ msgid "Choose a new folder"
 msgstr "Vel fil for lasting"
 
 #: backends/graphics/atari/atari-graphics.cpp:1060
-#: backends/graphics/sdl/sdl-graphics.cpp:514 engines/scumm/help.cpp:103
+#: backends/graphics/sdl/sdl-graphics.cpp:518 engines/scumm/help.cpp:103
 #, fuzzy
 msgid "Toggle aspect ratio correction"
 msgstr "Veksle aspekt-korrigering"
@@ -4361,35 +4361,35 @@ msgstr ""
 msgid "SDL Surface (forced 8bpp mode)"
 msgstr ""
 
-#: backends/graphics/sdl/sdl-graphics.cpp:420
+#: backends/graphics/sdl/sdl-graphics.cpp:424
 #, fuzzy, c-format
 msgid "Saved screenshot '%s'"
 msgstr "Bytt karakter"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:435
+#: backends/graphics/sdl/sdl-graphics.cpp:439
 #, fuzzy
 msgid "Could not save screenshot"
 msgstr "Bytt karakter"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:480
+#: backends/graphics/sdl/sdl-graphics.cpp:484
 msgid "Windowed mode"
 msgstr "Vindusmodus"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:491 engines/colony/metaengine.cpp:173
+#: backends/graphics/sdl/sdl-graphics.cpp:495 engines/colony/metaengine.cpp:173
 #: engines/scumm/help.cpp:88
 msgid "Toggle fullscreen"
 msgstr "Veksle fullskjerm"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:498 engines/scumm/help.cpp:100
+#: backends/graphics/sdl/sdl-graphics.cpp:502 engines/scumm/help.cpp:100
 #: engines/wintermute/keymapper_tables.h:960
 msgid "Toggle mouse capture"
 msgstr "Veksle musl??sing"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:503
+#: backends/graphics/sdl/sdl-graphics.cpp:507
 msgid "Toggle resizable window"
 msgstr ""
 
-#: backends/graphics/sdl/sdl-graphics.cpp:508 engines/stark/metaengine.cpp:240
+#: backends/graphics/sdl/sdl-graphics.cpp:512 engines/stark/metaengine.cpp:240
 #: engines/wintermute/keymapper_tables.h:646
 #: engines/wintermute/keymapper_tables.h:695
 #: engines/wintermute/keymapper_tables.h:1098
@@ -4403,31 +4403,31 @@ msgstr ""
 msgid "Save screenshot"
 msgstr "Bytt karakter"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:521
+#: backends/graphics/sdl/sdl-graphics.cpp:525
 msgid "Toggle linear filtered scaling"
 msgstr ""
 
-#: backends/graphics/sdl/sdl-graphics.cpp:528
+#: backends/graphics/sdl/sdl-graphics.cpp:532
 #, fuzzy
 msgid "Cycle through stretch modes"
 msgstr "Kunne ikkje veksle til videomodus: '"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:534
+#: backends/graphics/sdl/sdl-graphics.cpp:538
 #, fuzzy
 msgid "Increase the scale factor"
 msgstr "??k/Minsk skaleringsfaktor"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:540
+#: backends/graphics/sdl/sdl-graphics.cpp:544
 #, fuzzy
 msgid "Decrease the scale factor"
 msgstr "??k/Minsk skaleringsfaktor"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:547
+#: backends/graphics/sdl/sdl-graphics.cpp:551
 #, fuzzy
 msgid "Switch to the next scaler"
 msgstr "Bytt til spel"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:552
+#: backends/graphics/sdl/sdl-graphics.cpp:556
 msgid "Switch to the previous scaler"
 msgstr ""
 
@@ -15513,7 +15513,7 @@ msgstr "Rask modus"
 msgid "Toggle text speech"
 msgstr "Tekst og Tale:"
 
-#: engines/twine/detection.cpp:188
+#: engines/twine/detection.cpp:187
 msgid "This version requires Giflib which was not compiled into ScummVM"
 msgstr ""
 
diff --git a/po/pl_PL.po b/po/pl_PL.po
index 4a35b618290..c970b4ab074 100644
--- a/po/pl_PL.po
+++ b/po/pl_PL.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-21 22:22+0000\n"
+"POT-Creation-Date: 2026-06-21 22:35+0000\n"
 "PO-Revision-Date: 2026-04-17 00:22+0000\n"
 "Last-Translator: SkiffPL <skiff at windowslive.com>\n"
 "Language-Team: Polish <https://translations.scummvm.org/projects/scummvm/"
@@ -596,7 +596,7 @@ msgstr "Grafika"
 
 #: gui/editgamedialog.cpp:164 gui/options.cpp:2075 gui/options.cpp:2256
 #: backends/graphics/atari/atari-graphics.cpp:1056
-#: backends/graphics/sdl/sdl-graphics.cpp:487
+#: backends/graphics/sdl/sdl-graphics.cpp:491
 msgid "Graphics"
 msgstr "Grafika"
 
@@ -1934,7 +1934,7 @@ msgstr "Pobierz shadery"
 msgid "Check on the scummvm.org website for updates of shader packs"
 msgstr "Szukaj aktualizacji pakiet??w shader??w na stronie scummvm.org"
 
-#: gui/options.cpp:1700 backends/graphics/sdl/sdl-graphics.cpp:478
+#: gui/options.cpp:1700 backends/graphics/sdl/sdl-graphics.cpp:482
 msgid "Fullscreen mode"
 msgstr "Pe??ny ekran"
 
@@ -4335,7 +4335,7 @@ msgid "Choose a new folder"
 msgstr "Wybiersz nowy katalog"
 
 #: backends/graphics/atari/atari-graphics.cpp:1060
-#: backends/graphics/sdl/sdl-graphics.cpp:514 engines/scumm/help.cpp:103
+#: backends/graphics/sdl/sdl-graphics.cpp:518 engines/scumm/help.cpp:103
 msgid "Toggle aspect ratio correction"
 msgstr "Prze????cz korekt?? proporcji ekranu"
 
@@ -4407,34 +4407,34 @@ msgstr "Powierzchnia SDL"
 msgid "SDL Surface (forced 8bpp mode)"
 msgstr "Powierzchnia SDL (wymuszony tryb 8bpp)"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:420
+#: backends/graphics/sdl/sdl-graphics.cpp:424
 #, c-format
 msgid "Saved screenshot '%s'"
 msgstr "Zapisano zrzut ekranu '%s'"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:435
+#: backends/graphics/sdl/sdl-graphics.cpp:439
 msgid "Could not save screenshot"
 msgstr "Nie uda??o si?? zapisa?? zrzutu ekranu"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:480
+#: backends/graphics/sdl/sdl-graphics.cpp:484
 msgid "Windowed mode"
 msgstr "Tryb okienkowy"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:491 engines/colony/metaengine.cpp:173
+#: backends/graphics/sdl/sdl-graphics.cpp:495 engines/colony/metaengine.cpp:173
 #: engines/scumm/help.cpp:88
 msgid "Toggle fullscreen"
 msgstr "Prze????cz tryb pe??noekranowy"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:498 engines/scumm/help.cpp:100
+#: backends/graphics/sdl/sdl-graphics.cpp:502 engines/scumm/help.cpp:100
 #: engines/wintermute/keymapper_tables.h:960
 msgid "Toggle mouse capture"
 msgstr "Prze????cz przechwytywanie myszy"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:503
+#: backends/graphics/sdl/sdl-graphics.cpp:507
 msgid "Toggle resizable window"
 msgstr "Prze????cz zmian?? rozmiaru okna"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:508 engines/stark/metaengine.cpp:240
+#: backends/graphics/sdl/sdl-graphics.cpp:512 engines/stark/metaengine.cpp:240
 #: engines/wintermute/keymapper_tables.h:646
 #: engines/wintermute/keymapper_tables.h:695
 #: engines/wintermute/keymapper_tables.h:1098
@@ -4447,27 +4447,27 @@ msgstr "Prze????cz zmian?? rozmiaru okna"
 msgid "Save screenshot"
 msgstr "Zapisz zrzut ekranu"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:521
+#: backends/graphics/sdl/sdl-graphics.cpp:525
 msgid "Toggle linear filtered scaling"
 msgstr "Prze????cz skalowanie filtrowane liniowo"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:528
+#: backends/graphics/sdl/sdl-graphics.cpp:532
 msgid "Cycle through stretch modes"
 msgstr "Prze????cz pomi??dzy trybami rozci??gania"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:534
+#: backends/graphics/sdl/sdl-graphics.cpp:538
 msgid "Increase the scale factor"
 msgstr "Zwi??ksz wsp????czynnik skalowania"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:540
+#: backends/graphics/sdl/sdl-graphics.cpp:544
 msgid "Decrease the scale factor"
 msgstr "Zmniejsz wsp????czynnik skalowania"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:547
+#: backends/graphics/sdl/sdl-graphics.cpp:551
 msgid "Switch to the next scaler"
 msgstr "Prze????cz na nast??pny silnik skalowania"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:552
+#: backends/graphics/sdl/sdl-graphics.cpp:556
 msgid "Switch to the previous scaler"
 msgstr "Prze????cz na poprzedni silnik skalowania"
 
@@ -15718,7 +15718,7 @@ msgstr "Prze????cz styl panelu"
 msgid "Toggle text speech"
 msgstr "W????cz/wy????cz przetwarzanie tekstu na mow??"
 
-#: engines/twine/detection.cpp:188
+#: engines/twine/detection.cpp:187
 msgid "This version requires Giflib which was not compiled into ScummVM"
 msgstr ""
 "Ta wersja gry wymaga obs??ugi dla 'Giflib' kt??re nie jest wkompilowane w "
diff --git a/po/pt_BR.po b/po/pt_BR.po
index ee864a518f8..d59fd93035f 100644
--- a/po/pt_BR.po
+++ b/po/pt_BR.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.3.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-21 22:22+0000\n"
+"POT-Creation-Date: 2026-06-21 22:35+0000\n"
 "PO-Revision-Date: 2026-06-17 22:10+0000\n"
 "Last-Translator: Marcel Souza Lemes <marcosoutsider at gmail.com>\n"
 "Language-Team: Portuguese (Brazil) <https://translations.scummvm.org/"
@@ -597,7 +597,7 @@ msgstr "GFX"
 
 #: gui/editgamedialog.cpp:164 gui/options.cpp:2075 gui/options.cpp:2256
 #: backends/graphics/atari/atari-graphics.cpp:1056
-#: backends/graphics/sdl/sdl-graphics.cpp:487
+#: backends/graphics/sdl/sdl-graphics.cpp:491
 msgid "Graphics"
 msgstr "Gr??ficos"
 
@@ -1998,7 +1998,7 @@ msgstr "Baixar Shaders"
 msgid "Check on the scummvm.org website for updates of shader packs"
 msgstr "Verifique no site scummvm.org atualiza????es de pacotes de shaders"
 
-#: gui/options.cpp:1700 backends/graphics/sdl/sdl-graphics.cpp:478
+#: gui/options.cpp:1700 backends/graphics/sdl/sdl-graphics.cpp:482
 msgid "Fullscreen mode"
 msgstr "Modo de Tela Cheia"
 
@@ -4443,7 +4443,7 @@ msgid "Choose a new folder"
 msgstr "Escolher nova pasta"
 
 #: backends/graphics/atari/atari-graphics.cpp:1060
-#: backends/graphics/sdl/sdl-graphics.cpp:514 engines/scumm/help.cpp:103
+#: backends/graphics/sdl/sdl-graphics.cpp:518 engines/scumm/help.cpp:103
 msgid "Toggle aspect ratio correction"
 msgstr "Alternar aspecto da corre????o de propor????o"
 
@@ -4515,34 +4515,34 @@ msgstr "Superf??cie SDL"
 msgid "SDL Surface (forced 8bpp mode)"
 msgstr "Superf??cie SDL (modo 8bpp for??ado)"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:420
+#: backends/graphics/sdl/sdl-graphics.cpp:424
 #, c-format
 msgid "Saved screenshot '%s'"
 msgstr "Captura de tela '%s' foi salva"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:435
+#: backends/graphics/sdl/sdl-graphics.cpp:439
 msgid "Could not save screenshot"
 msgstr "N??o foi poss??vel salvar captura de tela"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:480
+#: backends/graphics/sdl/sdl-graphics.cpp:484
 msgid "Windowed mode"
 msgstr "Modo janela"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:491 engines/colony/metaengine.cpp:173
+#: backends/graphics/sdl/sdl-graphics.cpp:495 engines/colony/metaengine.cpp:173
 #: engines/scumm/help.cpp:88
 msgid "Toggle fullscreen"
 msgstr "Alternar modo de tela cheia"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:498 engines/scumm/help.cpp:100
+#: backends/graphics/sdl/sdl-graphics.cpp:502 engines/scumm/help.cpp:100
 #: engines/wintermute/keymapper_tables.h:960
 msgid "Toggle mouse capture"
 msgstr "Habilitar captura do mouse"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:503
+#: backends/graphics/sdl/sdl-graphics.cpp:507
 msgid "Toggle resizable window"
 msgstr "Alternar janela redimension??vel"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:508 engines/stark/metaengine.cpp:240
+#: backends/graphics/sdl/sdl-graphics.cpp:512 engines/stark/metaengine.cpp:240
 #: engines/wintermute/keymapper_tables.h:646
 #: engines/wintermute/keymapper_tables.h:695
 #: engines/wintermute/keymapper_tables.h:1098
@@ -4555,27 +4555,27 @@ msgstr "Alternar janela redimension??vel"
 msgid "Save screenshot"
 msgstr "Salvar captura de tela"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:521
+#: backends/graphics/sdl/sdl-graphics.cpp:525
 msgid "Toggle linear filtered scaling"
 msgstr "Alternar redimensionamento linear filtrado"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:528
+#: backends/graphics/sdl/sdl-graphics.cpp:532
 msgid "Cycle through stretch modes"
 msgstr "Percorrer entre os modos de redimensionamento"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:534
+#: backends/graphics/sdl/sdl-graphics.cpp:538
 msgid "Increase the scale factor"
 msgstr "Aumentar o fator de redimensionamento"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:540
+#: backends/graphics/sdl/sdl-graphics.cpp:544
 msgid "Decrease the scale factor"
 msgstr "Diminuir o fator de redimensionamento"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:547
+#: backends/graphics/sdl/sdl-graphics.cpp:551
 msgid "Switch to the next scaler"
 msgstr "Alternar para o pr??ximo redimensionador"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:552
+#: backends/graphics/sdl/sdl-graphics.cpp:556
 msgid "Switch to the previous scaler"
 msgstr "Alternar para o redimensionador anterior"
 
@@ -16528,7 +16528,7 @@ msgstr "Alternar estilo do painel"
 msgid "Toggle text speech"
 msgstr "Alternar texto da fala"
 
-#: engines/twine/detection.cpp:188
+#: engines/twine/detection.cpp:187
 msgid "This version requires Giflib which was not compiled into ScummVM"
 msgstr "Esta vers??o requer Giflib que n??o foi compilado no ScummVM"
 
diff --git a/po/pt_PT.po b/po/pt_PT.po
index a6946bf6ef8..51a2230d847 100644
--- a/po/pt_PT.po
+++ b/po/pt_PT.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.10.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-21 22:22+0000\n"
+"POT-Creation-Date: 2026-06-21 22:35+0000\n"
 "PO-Revision-Date: 2022-06-05 14:09+0000\n"
 "Last-Translator: Daniel <supsuper at gmail.com>\n"
 "Language-Team: Portuguese (Portugal) <https://translations.scummvm.org/"
@@ -620,7 +620,7 @@ msgstr "Gr??f."
 
 #: gui/editgamedialog.cpp:164 gui/options.cpp:2075 gui/options.cpp:2256
 #: backends/graphics/atari/atari-graphics.cpp:1056
-#: backends/graphics/sdl/sdl-graphics.cpp:487
+#: backends/graphics/sdl/sdl-graphics.cpp:491
 msgid "Graphics"
 msgstr "Gr??ficos"
 
@@ -1905,7 +1905,7 @@ msgstr "Transferir ficheiros do jogo"
 msgid "Check on the scummvm.org website for updates of shader packs"
 msgstr "Procurar por Atualiza????es..."
 
-#: gui/options.cpp:1700 backends/graphics/sdl/sdl-graphics.cpp:478
+#: gui/options.cpp:1700 backends/graphics/sdl/sdl-graphics.cpp:482
 msgid "Fullscreen mode"
 msgstr "Modo de ecr?? inteiro"
 
@@ -4305,7 +4305,7 @@ msgid "Choose a new folder"
 msgstr "Escolher ficheiro para carregar"
 
 #: backends/graphics/atari/atari-graphics.cpp:1060
-#: backends/graphics/sdl/sdl-graphics.cpp:514 engines/scumm/help.cpp:103
+#: backends/graphics/sdl/sdl-graphics.cpp:518 engines/scumm/help.cpp:103
 msgid "Toggle aspect ratio correction"
 msgstr "Alternar corre????o da taxa de propor????o"
 
@@ -4377,34 +4377,34 @@ msgstr "SDL Surface"
 msgid "SDL Surface (forced 8bpp mode)"
 msgstr ""
 
-#: backends/graphics/sdl/sdl-graphics.cpp:420
+#: backends/graphics/sdl/sdl-graphics.cpp:424
 #, c-format
 msgid "Saved screenshot '%s'"
 msgstr "Ecr?? capturado em '%s'"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:435
+#: backends/graphics/sdl/sdl-graphics.cpp:439
 msgid "Could not save screenshot"
 msgstr "N??o foi poss??vel capturar o ecr??"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:480
+#: backends/graphics/sdl/sdl-graphics.cpp:484
 msgid "Windowed mode"
 msgstr "Modo janela"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:491 engines/colony/metaengine.cpp:173
+#: backends/graphics/sdl/sdl-graphics.cpp:495 engines/colony/metaengine.cpp:173
 #: engines/scumm/help.cpp:88
 msgid "Toggle fullscreen"
 msgstr "Alternar entre janela / ecr?? inteiro"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:498 engines/scumm/help.cpp:100
+#: backends/graphics/sdl/sdl-graphics.cpp:502 engines/scumm/help.cpp:100
 #: engines/wintermute/keymapper_tables.h:960
 msgid "Toggle mouse capture"
 msgstr "Alternar captura do rato"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:503
+#: backends/graphics/sdl/sdl-graphics.cpp:507
 msgid "Toggle resizable window"
 msgstr ""
 
-#: backends/graphics/sdl/sdl-graphics.cpp:508 engines/stark/metaengine.cpp:240
+#: backends/graphics/sdl/sdl-graphics.cpp:512 engines/stark/metaengine.cpp:240
 #: engines/wintermute/keymapper_tables.h:646
 #: engines/wintermute/keymapper_tables.h:695
 #: engines/wintermute/keymapper_tables.h:1098
@@ -4417,27 +4417,27 @@ msgstr ""
 msgid "Save screenshot"
 msgstr "Capturar ecr??"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:521
+#: backends/graphics/sdl/sdl-graphics.cpp:525
 msgid "Toggle linear filtered scaling"
 msgstr "Alternar filtragem linear no redimensionamento"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:528
+#: backends/graphics/sdl/sdl-graphics.cpp:532
 msgid "Cycle through stretch modes"
 msgstr "Percorrer modos de redimensionamento"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:534
+#: backends/graphics/sdl/sdl-graphics.cpp:538
 msgid "Increase the scale factor"
 msgstr "Aumentar a escala"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:540
+#: backends/graphics/sdl/sdl-graphics.cpp:544
 msgid "Decrease the scale factor"
 msgstr "Diminuir a escala"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:547
+#: backends/graphics/sdl/sdl-graphics.cpp:551
 msgid "Switch to the next scaler"
 msgstr "Scaler seguinte"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:552
+#: backends/graphics/sdl/sdl-graphics.cpp:556
 msgid "Switch to the previous scaler"
 msgstr "Scaler anterior"
 
@@ -15662,7 +15662,7 @@ msgstr "Alternar modo r??pido"
 msgid "Toggle text speech"
 msgstr "Usar Texto para Voz"
 
-#: engines/twine/detection.cpp:188
+#: engines/twine/detection.cpp:187
 msgid "This version requires Giflib which was not compiled into ScummVM"
 msgstr "Esta vers??o requer Giflib que n??o est?? dispon??vel neste ScummVM"
 
diff --git a/po/ro.po b/po/ro.po
index 09ce29f1ee9..2449a3adf40 100644
--- a/po/ro.po
+++ b/po/ro.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.10.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-21 22:22+0000\n"
+"POT-Creation-Date: 2026-06-21 22:35+0000\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: Automatically generated\n"
 "Language-Team: none\n"
@@ -570,7 +570,7 @@ msgstr ""
 
 #: gui/editgamedialog.cpp:164 gui/options.cpp:2075 gui/options.cpp:2256
 #: backends/graphics/atari/atari-graphics.cpp:1056
-#: backends/graphics/sdl/sdl-graphics.cpp:487
+#: backends/graphics/sdl/sdl-graphics.cpp:491
 msgid "Graphics"
 msgstr ""
 
@@ -1796,7 +1796,7 @@ msgstr ""
 msgid "Check on the scummvm.org website for updates of shader packs"
 msgstr ""
 
-#: gui/options.cpp:1700 backends/graphics/sdl/sdl-graphics.cpp:478
+#: gui/options.cpp:1700 backends/graphics/sdl/sdl-graphics.cpp:482
 msgid "Fullscreen mode"
 msgstr ""
 
@@ -4009,7 +4009,7 @@ msgid "Choose a new folder"
 msgstr ""
 
 #: backends/graphics/atari/atari-graphics.cpp:1060
-#: backends/graphics/sdl/sdl-graphics.cpp:514 engines/scumm/help.cpp:103
+#: backends/graphics/sdl/sdl-graphics.cpp:518 engines/scumm/help.cpp:103
 msgid "Toggle aspect ratio correction"
 msgstr ""
 
@@ -4081,34 +4081,34 @@ msgstr ""
 msgid "SDL Surface (forced 8bpp mode)"
 msgstr ""
 
-#: backends/graphics/sdl/sdl-graphics.cpp:420
+#: backends/graphics/sdl/sdl-graphics.cpp:424
 #, c-format
 msgid "Saved screenshot '%s'"
 msgstr ""
 
-#: backends/graphics/sdl/sdl-graphics.cpp:435
+#: backends/graphics/sdl/sdl-graphics.cpp:439
 msgid "Could not save screenshot"
 msgstr ""
 
-#: backends/graphics/sdl/sdl-graphics.cpp:480
+#: backends/graphics/sdl/sdl-graphics.cpp:484
 msgid "Windowed mode"
 msgstr ""
 
-#: backends/graphics/sdl/sdl-graphics.cpp:491 engines/colony/metaengine.cpp:173
+#: backends/graphics/sdl/sdl-graphics.cpp:495 engines/colony/metaengine.cpp:173
 #: engines/scumm/help.cpp:88
 msgid "Toggle fullscreen"
 msgstr ""
 
-#: backends/graphics/sdl/sdl-graphics.cpp:498 engines/scumm/help.cpp:100
+#: backends/graphics/sdl/sdl-graphics.cpp:502 engines/scumm/help.cpp:100
 #: engines/wintermute/keymapper_tables.h:960
 msgid "Toggle mouse capture"
 msgstr ""
 
-#: backends/graphics/sdl/sdl-graphics.cpp:503
+#: backends/graphics/sdl/sdl-graphics.cpp:507
 msgid "Toggle resizable window"
 msgstr ""
 
-#: backends/graphics/sdl/sdl-graphics.cpp:508 engines/stark/metaengine.cpp:240
+#: backends/graphics/sdl/sdl-graphics.cpp:512 engines/stark/metaengine.cpp:240
 #: engines/wintermute/keymapper_tables.h:646
 #: engines/wintermute/keymapper_tables.h:695
 #: engines/wintermute/keymapper_tables.h:1098
@@ -4121,27 +4121,27 @@ msgstr ""
 msgid "Save screenshot"
 msgstr ""
 
-#: backends/graphics/sdl/sdl-graphics.cpp:521
+#: backends/graphics/sdl/sdl-graphics.cpp:525
 msgid "Toggle linear filtered scaling"
 msgstr ""
 
-#: backends/graphics/sdl/sdl-graphics.cpp:528
+#: backends/graphics/sdl/sdl-graphics.cpp:532
 msgid "Cycle through stretch modes"
 msgstr ""
 
-#: backends/graphics/sdl/sdl-graphics.cpp:534
+#: backends/graphics/sdl/sdl-graphics.cpp:538
 msgid "Increase the scale factor"
 msgstr ""
 
-#: backends/graphics/sdl/sdl-graphics.cpp:540
+#: backends/graphics/sdl/sdl-graphics.cpp:544
 msgid "Decrease the scale factor"
 msgstr ""
 
-#: backends/graphics/sdl/sdl-graphics.cpp:547
+#: backends/graphics/sdl/sdl-graphics.cpp:551
 msgid "Switch to the next scaler"
 msgstr ""
 
-#: backends/graphics/sdl/sdl-graphics.cpp:552
+#: backends/graphics/sdl/sdl-graphics.cpp:556
 msgid "Switch to the previous scaler"
 msgstr ""
 
@@ -13973,7 +13973,7 @@ msgstr ""
 msgid "Toggle text speech"
 msgstr ""
 
-#: engines/twine/detection.cpp:188
+#: engines/twine/detection.cpp:187
 msgid "This version requires Giflib which was not compiled into ScummVM"
 msgstr ""
 
diff --git a/po/ru_RU.po b/po/ru_RU.po
index 3e568279bad..d22a8b21bc9 100644
--- a/po/ru_RU.po
+++ b/po/ru_RU.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.8.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-21 22:22+0000\n"
+"POT-Creation-Date: 2026-06-21 22:35+0000\n"
 "PO-Revision-Date: 2026-06-20 15:03+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Russian <https://translations.scummvm.org/projects/scummvm/"
@@ -591,7 +591,7 @@ msgstr "??????"
 
 #: gui/editgamedialog.cpp:164 gui/options.cpp:2075 gui/options.cpp:2256
 #: backends/graphics/atari/atari-graphics.cpp:1056
-#: backends/graphics/sdl/sdl-graphics.cpp:487
+#: backends/graphics/sdl/sdl-graphics.cpp:491
 msgid "Graphics"
 msgstr "??????????????"
 
@@ -1932,7 +1932,7 @@ msgstr "?????????????? ??????????????"
 msgid "Check on the scummvm.org website for updates of shader packs"
 msgstr "?????????????????? ???????????????????? ?????????????? ???????????????? ???? ?????????? scummvm.org"
 
-#: gui/options.cpp:1700 backends/graphics/sdl/sdl-graphics.cpp:478
+#: gui/options.cpp:1700 backends/graphics/sdl/sdl-graphics.cpp:482
 msgid "Fullscreen mode"
 msgstr "?????????????????????????? ??????????"
 
@@ -4340,7 +4340,7 @@ msgid "Choose a new folder"
 msgstr "?????????????? ?????????? ??????????"
 
 #: backends/graphics/atari/atari-graphics.cpp:1060
-#: backends/graphics/sdl/sdl-graphics.cpp:514 engines/scumm/help.cpp:103
+#: backends/graphics/sdl/sdl-graphics.cpp:518 engines/scumm/help.cpp:103
 msgid "Toggle aspect ratio correction"
 msgstr "?????????????????????? ?????????????????? ?????????????????????? ????????????"
 
@@ -4412,34 +4412,34 @@ msgstr "?????????????????????? SDL"
 msgid "SDL Surface (forced 8bpp mode)"
 msgstr "SDL Surface (8-???????????? ??????????)"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:420
+#: backends/graphics/sdl/sdl-graphics.cpp:424
 #, c-format
 msgid "Saved screenshot '%s'"
 msgstr "???????????????? ???????????????? '%s'"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:435
+#: backends/graphics/sdl/sdl-graphics.cpp:439
 msgid "Could not save screenshot"
 msgstr "???? ?????????????? ?????????????????? ????????????????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:480
+#: backends/graphics/sdl/sdl-graphics.cpp:484
 msgid "Windowed mode"
 msgstr "?????????????? ??????????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:491 engines/colony/metaengine.cpp:173
+#: backends/graphics/sdl/sdl-graphics.cpp:495 engines/colony/metaengine.cpp:173
 #: engines/scumm/help.cpp:88
 msgid "Toggle fullscreen"
 msgstr "?????????????????????? ?????????????????????????? ??????????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:498 engines/scumm/help.cpp:100
+#: backends/graphics/sdl/sdl-graphics.cpp:502 engines/scumm/help.cpp:100
 #: engines/wintermute/keymapper_tables.h:960
 msgid "Toggle mouse capture"
 msgstr "?????????????????????? ???????????????? ????????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:503
+#: backends/graphics/sdl/sdl-graphics.cpp:507
 msgid "Toggle resizable window"
 msgstr "?????????????????????? ?????????????????? ???????????????? ????????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:508 engines/stark/metaengine.cpp:240
+#: backends/graphics/sdl/sdl-graphics.cpp:512 engines/stark/metaengine.cpp:240
 #: engines/wintermute/keymapper_tables.h:646
 #: engines/wintermute/keymapper_tables.h:695
 #: engines/wintermute/keymapper_tables.h:1098
@@ -4452,27 +4452,27 @@ msgstr "?????????????????????? ?????????????????? ???????????????? ????????"
 msgid "Save screenshot"
 msgstr "?????????????????? ????????????????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:521
+#: backends/graphics/sdl/sdl-graphics.cpp:525
 msgid "Toggle linear filtered scaling"
 msgstr "???????????????????????? ???????????????? ?????????????????????? ?????? ??????????????????????????????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:528
+#: backends/graphics/sdl/sdl-graphics.cpp:532
 msgid "Cycle through stretch modes"
 msgstr "?????????????????????? ?????????? ????????????????????????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:534
+#: backends/graphics/sdl/sdl-graphics.cpp:538
 msgid "Increase the scale factor"
 msgstr "?????????????????? ??????????????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:540
+#: backends/graphics/sdl/sdl-graphics.cpp:544
 msgid "Decrease the scale factor"
 msgstr "?????????????????? ??????????????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:547
+#: backends/graphics/sdl/sdl-graphics.cpp:551
 msgid "Switch to the next scaler"
 msgstr "?????????????????????? ???? ???????????????????? ???????????? ??????????????????????????????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:552
+#: backends/graphics/sdl/sdl-graphics.cpp:556
 msgid "Switch to the previous scaler"
 msgstr "?????????????????????? ???? ?????????????????? ???????????? ??????????????????????????????"
 
@@ -15230,7 +15230,7 @@ msgstr "?????????????????????? ?????????? ????????????"
 msgid "Toggle text speech"
 msgstr "?????????????????????? ?????????? ????????"
 
-#: engines/twine/detection.cpp:188
+#: engines/twine/detection.cpp:187
 msgid "This version requires Giflib which was not compiled into ScummVM"
 msgstr "?????? ???????? ?????????????? ???????????????????? Giflib ?????????????? ???? ???????? ????????????????????????????"
 
diff --git a/po/sv_SE.po b/po/sv_SE.po
index bdddf1426dd..dccb3048670 100644
--- a/po/sv_SE.po
+++ b/po/sv_SE.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.5.0svn\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-21 22:22+0000\n"
+"POT-Creation-Date: 2026-06-21 22:35+0000\n"
 "PO-Revision-Date: 2026-04-19 13:22+0000\n"
 "Last-Translator: Daniel Nylander <po at danielnylander.se>\n"
 "Language-Team: Swedish <https://translations.scummvm.org/projects/scummvm/"
@@ -592,7 +592,7 @@ msgstr "GFX"
 
 #: gui/editgamedialog.cpp:164 gui/options.cpp:2075 gui/options.cpp:2256
 #: backends/graphics/atari/atari-graphics.cpp:1056
-#: backends/graphics/sdl/sdl-graphics.cpp:487
+#: backends/graphics/sdl/sdl-graphics.cpp:491
 msgid "Graphics"
 msgstr "Grafik"
 
@@ -1933,7 +1933,7 @@ msgstr "H??mta shaders"
 msgid "Check on the scummvm.org website for updates of shader packs"
 msgstr "Leta p?? webbplatsen scummvm.org efter uppdateringar av shader-paket"
 
-#: gui/options.cpp:1700 backends/graphics/sdl/sdl-graphics.cpp:478
+#: gui/options.cpp:1700 backends/graphics/sdl/sdl-graphics.cpp:482
 msgid "Fullscreen mode"
 msgstr "Helsk??rmsl??ge"
 
@@ -4335,7 +4335,7 @@ msgid "Choose a new folder"
 msgstr "V??lj en ny mapp"
 
 #: backends/graphics/atari/atari-graphics.cpp:1060
-#: backends/graphics/sdl/sdl-graphics.cpp:514 engines/scumm/help.cpp:103
+#: backends/graphics/sdl/sdl-graphics.cpp:518 engines/scumm/help.cpp:103
 msgid "Toggle aspect ratio correction"
 msgstr "V??xla korrigering av bildf??rh??llande"
 
@@ -4407,34 +4407,34 @@ msgstr "SDL Surface"
 msgid "SDL Surface (forced 8bpp mode)"
 msgstr "SDL Surface (tvingat 8bpp-l??ge)"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:420
+#: backends/graphics/sdl/sdl-graphics.cpp:424
 #, c-format
 msgid "Saved screenshot '%s'"
 msgstr "Sparade sk??rmbilden '%s'"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:435
+#: backends/graphics/sdl/sdl-graphics.cpp:439
 msgid "Could not save screenshot"
 msgstr "Kunde inte spara sk??rmbild"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:480
+#: backends/graphics/sdl/sdl-graphics.cpp:484
 msgid "Windowed mode"
 msgstr "F??nsterl??ge"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:491 engines/colony/metaengine.cpp:173
+#: backends/graphics/sdl/sdl-graphics.cpp:495 engines/colony/metaengine.cpp:173
 #: engines/scumm/help.cpp:88
 msgid "Toggle fullscreen"
 msgstr "V??xla helsk??rmsl??ge"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:498 engines/scumm/help.cpp:100
+#: backends/graphics/sdl/sdl-graphics.cpp:502 engines/scumm/help.cpp:100
 #: engines/wintermute/keymapper_tables.h:960
 msgid "Toggle mouse capture"
 msgstr "V??xla musf??ngst"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:503
+#: backends/graphics/sdl/sdl-graphics.cpp:507
 msgid "Toggle resizable window"
 msgstr "V??xla storleks??ndringsbart f??nster"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:508 engines/stark/metaengine.cpp:240
+#: backends/graphics/sdl/sdl-graphics.cpp:512 engines/stark/metaengine.cpp:240
 #: engines/wintermute/keymapper_tables.h:646
 #: engines/wintermute/keymapper_tables.h:695
 #: engines/wintermute/keymapper_tables.h:1098
@@ -4447,27 +4447,27 @@ msgstr "V??xla storleks??ndringsbart f??nster"
 msgid "Save screenshot"
 msgstr "Spara sk??rmbild"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:521
+#: backends/graphics/sdl/sdl-graphics.cpp:525
 msgid "Toggle linear filtered scaling"
 msgstr "V??xla linj??rt filtrerad skalning"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:528
+#: backends/graphics/sdl/sdl-graphics.cpp:532
 msgid "Cycle through stretch modes"
 msgstr "Byt till n??sta utstr??ckningsl??ge"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:534
+#: backends/graphics/sdl/sdl-graphics.cpp:538
 msgid "Increase the scale factor"
 msgstr "??ka skalningsfaktorn"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:540
+#: backends/graphics/sdl/sdl-graphics.cpp:544
 msgid "Decrease the scale factor"
 msgstr "Minska skalningsfaktorn"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:547
+#: backends/graphics/sdl/sdl-graphics.cpp:551
 msgid "Switch to the next scaler"
 msgstr "Byt till n??sta skalare"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:552
+#: backends/graphics/sdl/sdl-graphics.cpp:556
 msgid "Switch to the previous scaler"
 msgstr "Byt till f??reg??ende skalare"
 
@@ -15323,7 +15323,7 @@ msgstr "V??xla panelstil"
 msgid "Toggle text speech"
 msgstr "V??xla texttal"
 
-#: engines/twine/detection.cpp:188
+#: engines/twine/detection.cpp:187
 msgid "This version requires Giflib which was not compiled into ScummVM"
 msgstr "Denna version kr??ver Giflib vilket inte kompilerats in i ScummVM"
 
diff --git a/po/tr.po b/po/tr.po
index e2328dc3c79..df2c14215d6 100644
--- a/po/tr.po
+++ b/po/tr.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-21 22:22+0000\n"
+"POT-Creation-Date: 2026-06-21 22:35+0000\n"
 "PO-Revision-Date: 2024-10-10 12:33+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Turkish <https://translations.scummvm.org/projects/scummvm/"
@@ -599,7 +599,7 @@ msgstr "GFX"
 
 #: gui/editgamedialog.cpp:164 gui/options.cpp:2075 gui/options.cpp:2256
 #: backends/graphics/atari/atari-graphics.cpp:1056
-#: backends/graphics/sdl/sdl-graphics.cpp:487
+#: backends/graphics/sdl/sdl-graphics.cpp:491
 msgid "Graphics"
 msgstr "Grafik Ayarlar??"
 
@@ -1886,7 +1886,7 @@ msgstr "Oyun dosyalar??n?? indir"
 msgid "Check on the scummvm.org website for updates of shader packs"
 msgstr ""
 
-#: gui/options.cpp:1700 backends/graphics/sdl/sdl-graphics.cpp:478
+#: gui/options.cpp:1700 backends/graphics/sdl/sdl-graphics.cpp:482
 msgid "Fullscreen mode"
 msgstr "Tam ekran modu"
 
@@ -4273,7 +4273,7 @@ msgid "Choose a new folder"
 msgstr "Y??kleme i??in dosyay?? se??in"
 
 #: backends/graphics/atari/atari-graphics.cpp:1060
-#: backends/graphics/sdl/sdl-graphics.cpp:514 engines/scumm/help.cpp:103
+#: backends/graphics/sdl/sdl-graphics.cpp:518 engines/scumm/help.cpp:103
 #, fuzzy
 msgid "Toggle aspect ratio correction"
 msgstr "G??r??nt?? oran?? d??zeltme"
@@ -4350,36 +4350,36 @@ msgstr ""
 msgid "SDL Surface (forced 8bpp mode)"
 msgstr ""
 
-#: backends/graphics/sdl/sdl-graphics.cpp:420
+#: backends/graphics/sdl/sdl-graphics.cpp:424
 #, c-format
 msgid "Saved screenshot '%s'"
 msgstr ""
 
-#: backends/graphics/sdl/sdl-graphics.cpp:435
+#: backends/graphics/sdl/sdl-graphics.cpp:439
 #, fuzzy
 msgid "Could not save screenshot"
 msgstr "Tam ekran ayar?? uygulanamad??."
 
-#: backends/graphics/sdl/sdl-graphics.cpp:480
+#: backends/graphics/sdl/sdl-graphics.cpp:484
 #, fuzzy
 msgid "Windowed mode"
 msgstr "Render modu:"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:491 engines/colony/metaengine.cpp:173
+#: backends/graphics/sdl/sdl-graphics.cpp:495 engines/colony/metaengine.cpp:173
 #: engines/scumm/help.cpp:88
 msgid "Toggle fullscreen"
 msgstr ""
 
-#: backends/graphics/sdl/sdl-graphics.cpp:498 engines/scumm/help.cpp:100
+#: backends/graphics/sdl/sdl-graphics.cpp:502 engines/scumm/help.cpp:100
 #: engines/wintermute/keymapper_tables.h:960
 msgid "Toggle mouse capture"
 msgstr ""
 
-#: backends/graphics/sdl/sdl-graphics.cpp:503
+#: backends/graphics/sdl/sdl-graphics.cpp:507
 msgid "Toggle resizable window"
 msgstr ""
 
-#: backends/graphics/sdl/sdl-graphics.cpp:508 engines/stark/metaengine.cpp:240
+#: backends/graphics/sdl/sdl-graphics.cpp:512 engines/stark/metaengine.cpp:240
 #: engines/wintermute/keymapper_tables.h:646
 #: engines/wintermute/keymapper_tables.h:695
 #: engines/wintermute/keymapper_tables.h:1098
@@ -4392,29 +4392,29 @@ msgstr ""
 msgid "Save screenshot"
 msgstr ""
 
-#: backends/graphics/sdl/sdl-graphics.cpp:521
+#: backends/graphics/sdl/sdl-graphics.cpp:525
 #, fuzzy
 msgid "Toggle linear filtered scaling"
 msgstr "Grafik orant??lama yap??l??rken do??rusal filtreleme"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:528
+#: backends/graphics/sdl/sdl-graphics.cpp:532
 msgid "Cycle through stretch modes"
 msgstr ""
 
-#: backends/graphics/sdl/sdl-graphics.cpp:534
+#: backends/graphics/sdl/sdl-graphics.cpp:538
 msgid "Increase the scale factor"
 msgstr ""
 
-#: backends/graphics/sdl/sdl-graphics.cpp:540
+#: backends/graphics/sdl/sdl-graphics.cpp:544
 msgid "Decrease the scale factor"
 msgstr ""
 
-#: backends/graphics/sdl/sdl-graphics.cpp:547
+#: backends/graphics/sdl/sdl-graphics.cpp:551
 #, fuzzy
 msgid "Switch to the next scaler"
 msgstr "Oyunu se??"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:552
+#: backends/graphics/sdl/sdl-graphics.cpp:556
 msgid "Switch to the previous scaler"
 msgstr ""
 
@@ -15035,7 +15035,7 @@ msgstr "Altyaz??lar"
 msgid "Toggle text speech"
 msgstr "Konu??ma i??in metin kullan"
 
-#: engines/twine/detection.cpp:188
+#: engines/twine/detection.cpp:187
 msgid "This version requires Giflib which was not compiled into ScummVM"
 msgstr ""
 
diff --git a/po/uk_UA.po b/po/uk_UA.po
index 4e9161ca932..47c9a4ef38a 100644
--- a/po/uk_UA.po
+++ b/po/uk_UA.po
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 1.9.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-21 22:22+0000\n"
+"POT-Creation-Date: 2026-06-21 22:35+0000\n"
 "PO-Revision-Date: 2026-06-20 14:56+0000\n"
 "Last-Translator: Eugene Sandulenko <sev at scummvm.org>\n"
 "Language-Team: Ukrainian <https://translations.scummvm.org/projects/scummvm/"
@@ -593,7 +593,7 @@ msgstr "??????"
 
 #: gui/editgamedialog.cpp:164 gui/options.cpp:2075 gui/options.cpp:2256
 #: backends/graphics/atari/atari-graphics.cpp:1056
-#: backends/graphics/sdl/sdl-graphics.cpp:487
+#: backends/graphics/sdl/sdl-graphics.cpp:491
 msgid "Graphics"
 msgstr "??????????????"
 
@@ -1934,7 +1934,7 @@ msgstr "?????????????????????? ??????????????"
 msgid "Check on the scummvm.org website for updates of shader packs"
 msgstr "???????????????????? ?????????????????? ?????????????? ???????????????? ???? ?????????? scummvm.org"
 
-#: gui/options.cpp:1700 backends/graphics/sdl/sdl-graphics.cpp:478
+#: gui/options.cpp:1700 backends/graphics/sdl/sdl-graphics.cpp:482
 msgid "Fullscreen mode"
 msgstr "?????????????????????????? ??????????"
 
@@ -4333,7 +4333,7 @@ msgid "Choose a new folder"
 msgstr "?????????????? ???????? ??????????"
 
 #: backends/graphics/atari/atari-graphics.cpp:1060
-#: backends/graphics/sdl/sdl-graphics.cpp:514 engines/scumm/help.cpp:103
+#: backends/graphics/sdl/sdl-graphics.cpp:518 engines/scumm/help.cpp:103
 msgid "Toggle aspect ratio correction"
 msgstr "???????????????????? ???????????????? ???????????????????????????? ????????????"
 
@@ -4405,34 +4405,34 @@ msgstr "???????????????? SDL"
 msgid "SDL Surface (forced 8bpp mode)"
 msgstr "SDL Surface (8-???????????? ??????????)"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:420
+#: backends/graphics/sdl/sdl-graphics.cpp:424
 #, c-format
 msgid "Saved screenshot '%s'"
 msgstr "?????????????????? ?????????? '%s'"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:435
+#: backends/graphics/sdl/sdl-graphics.cpp:439
 msgid "Could not save screenshot"
 msgstr "???? ?????????????? ???????????????? ??????????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:480
+#: backends/graphics/sdl/sdl-graphics.cpp:484
 msgid "Windowed mode"
 msgstr "???????????????? ??????????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:491 engines/colony/metaengine.cpp:173
+#: backends/graphics/sdl/sdl-graphics.cpp:495 engines/colony/metaengine.cpp:173
 #: engines/scumm/help.cpp:88
 msgid "Toggle fullscreen"
 msgstr "???????????????????? ?????????????????????????? ??????????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:498 engines/scumm/help.cpp:100
+#: backends/graphics/sdl/sdl-graphics.cpp:502 engines/scumm/help.cpp:100
 #: engines/wintermute/keymapper_tables.h:960
 msgid "Toggle mouse capture"
 msgstr "???????????????????? ???????????????????? ????????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:503
+#: backends/graphics/sdl/sdl-graphics.cpp:507
 msgid "Toggle resizable window"
 msgstr "???????????????????? ?????????? ???????????????? ??????????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:508 engines/stark/metaengine.cpp:240
+#: backends/graphics/sdl/sdl-graphics.cpp:512 engines/stark/metaengine.cpp:240
 #: engines/wintermute/keymapper_tables.h:646
 #: engines/wintermute/keymapper_tables.h:695
 #: engines/wintermute/keymapper_tables.h:1098
@@ -4445,27 +4445,27 @@ msgstr "???????????????????? ?????????? ???????????????? ??????????"
 msgid "Save screenshot"
 msgstr "???????????????? ??????????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:521
+#: backends/graphics/sdl/sdl-graphics.cpp:525
 msgid "Toggle linear filtered scaling"
 msgstr "???????????????????? ???????????????????? ?? ???????????????? ??????????????????????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:528
+#: backends/graphics/sdl/sdl-graphics.cpp:532
 msgid "Cycle through stretch modes"
 msgstr "?????????????????????? ?????? ?????????????? ????????????????????????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:534
+#: backends/graphics/sdl/sdl-graphics.cpp:538
 msgid "Increase the scale factor"
 msgstr "?????????????????? ??????????????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:540
+#: backends/graphics/sdl/sdl-graphics.cpp:544
 msgid "Decrease the scale factor"
 msgstr "???????????????? ??????????????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:547
+#: backends/graphics/sdl/sdl-graphics.cpp:551
 msgid "Switch to the next scaler"
 msgstr "?????????????????????? ???? ???????????????????? ??????????????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:552
+#: backends/graphics/sdl/sdl-graphics.cpp:556
 msgid "Switch to the previous scaler"
 msgstr "?????????????????????? ???? ???????????????????????? ??????????????"
 
@@ -15183,7 +15183,7 @@ msgstr "???????????????????? ?????????? ????????????"
 msgid "Toggle text speech"
 msgstr "???????????????????? ?????????????? ????????????"
 
-#: engines/twine/detection.cpp:188
+#: engines/twine/detection.cpp:187
 msgid "This version requires Giflib which was not compiled into ScummVM"
 msgstr "???? ?????? ????????e?????? ???????????????????? Giflib, ?????? ???????????????? ?? ????????????"
 
diff --git a/po/zh.po b/po/zh.po
index aa130636f1f..9cfdc27bcb8 100644
--- a/po/zh.po
+++ b/po/zh.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.3.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-21 22:22+0000\n"
+"POT-Creation-Date: 2026-06-21 22:35+0000\n"
 "PO-Revision-Date: 2024-05-14 12:42+0000\n"
 "Last-Translator: ?????? <clonewith at qq.com>\n"
 "Language-Team: Chinese <https://translations.scummvm.org/projects/scummvm/"
@@ -586,7 +586,7 @@ msgstr "??????"
 
 #: gui/editgamedialog.cpp:164 gui/options.cpp:2075 gui/options.cpp:2256
 #: backends/graphics/atari/atari-graphics.cpp:1056
-#: backends/graphics/sdl/sdl-graphics.cpp:487
+#: backends/graphics/sdl/sdl-graphics.cpp:491
 msgid "Graphics"
 msgstr "??????"
 
@@ -1819,7 +1819,7 @@ msgstr "???????????????"
 msgid "Check on the scummvm.org website for updates of shader packs"
 msgstr ""
 
-#: gui/options.cpp:1700 backends/graphics/sdl/sdl-graphics.cpp:478
+#: gui/options.cpp:1700 backends/graphics/sdl/sdl-graphics.cpp:482
 msgid "Fullscreen mode"
 msgstr "????????????"
 
@@ -4074,7 +4074,7 @@ msgid "Choose a new folder"
 msgstr "??????????????????"
 
 #: backends/graphics/atari/atari-graphics.cpp:1060
-#: backends/graphics/sdl/sdl-graphics.cpp:514 engines/scumm/help.cpp:103
+#: backends/graphics/sdl/sdl-graphics.cpp:518 engines/scumm/help.cpp:103
 msgid "Toggle aspect ratio correction"
 msgstr ""
 
@@ -4146,34 +4146,34 @@ msgstr ""
 msgid "SDL Surface (forced 8bpp mode)"
 msgstr ""
 
-#: backends/graphics/sdl/sdl-graphics.cpp:420
+#: backends/graphics/sdl/sdl-graphics.cpp:424
 #, c-format
 msgid "Saved screenshot '%s'"
 msgstr ""
 
-#: backends/graphics/sdl/sdl-graphics.cpp:435
+#: backends/graphics/sdl/sdl-graphics.cpp:439
 msgid "Could not save screenshot"
 msgstr ""
 
-#: backends/graphics/sdl/sdl-graphics.cpp:480
+#: backends/graphics/sdl/sdl-graphics.cpp:484
 msgid "Windowed mode"
 msgstr ""
 
-#: backends/graphics/sdl/sdl-graphics.cpp:491 engines/colony/metaengine.cpp:173
+#: backends/graphics/sdl/sdl-graphics.cpp:495 engines/colony/metaengine.cpp:173
 #: engines/scumm/help.cpp:88
 msgid "Toggle fullscreen"
 msgstr ""
 
-#: backends/graphics/sdl/sdl-graphics.cpp:498 engines/scumm/help.cpp:100
+#: backends/graphics/sdl/sdl-graphics.cpp:502 engines/scumm/help.cpp:100
 #: engines/wintermute/keymapper_tables.h:960
 msgid "Toggle mouse capture"
 msgstr ""
 
-#: backends/graphics/sdl/sdl-graphics.cpp:503
+#: backends/graphics/sdl/sdl-graphics.cpp:507
 msgid "Toggle resizable window"
 msgstr ""
 
-#: backends/graphics/sdl/sdl-graphics.cpp:508 engines/stark/metaengine.cpp:240
+#: backends/graphics/sdl/sdl-graphics.cpp:512 engines/stark/metaengine.cpp:240
 #: engines/wintermute/keymapper_tables.h:646
 #: engines/wintermute/keymapper_tables.h:695
 #: engines/wintermute/keymapper_tables.h:1098
@@ -4186,27 +4186,27 @@ msgstr ""
 msgid "Save screenshot"
 msgstr ""
 
-#: backends/graphics/sdl/sdl-graphics.cpp:521
+#: backends/graphics/sdl/sdl-graphics.cpp:525
 msgid "Toggle linear filtered scaling"
 msgstr ""
 
-#: backends/graphics/sdl/sdl-graphics.cpp:528
+#: backends/graphics/sdl/sdl-graphics.cpp:532
 msgid "Cycle through stretch modes"
 msgstr ""
 
-#: backends/graphics/sdl/sdl-graphics.cpp:534
+#: backends/graphics/sdl/sdl-graphics.cpp:538
 msgid "Increase the scale factor"
 msgstr ""
 
-#: backends/graphics/sdl/sdl-graphics.cpp:540
+#: backends/graphics/sdl/sdl-graphics.cpp:544
 msgid "Decrease the scale factor"
 msgstr ""
 
-#: backends/graphics/sdl/sdl-graphics.cpp:547
+#: backends/graphics/sdl/sdl-graphics.cpp:551
 msgid "Switch to the next scaler"
 msgstr "????????????????????????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:552
+#: backends/graphics/sdl/sdl-graphics.cpp:556
 msgid "Switch to the previous scaler"
 msgstr ""
 
@@ -14914,7 +14914,7 @@ msgstr "??????????????????"
 msgid "Toggle text speech"
 msgstr "??????????????????"
 
-#: engines/twine/detection.cpp:188
+#: engines/twine/detection.cpp:187
 msgid "This version requires Giflib which was not compiled into ScummVM"
 msgstr ""
 
diff --git a/po/zh_Hans.po b/po/zh_Hans.po
index b22cd2a8af1..bf759f89d47 100644
--- a/po/zh_Hans.po
+++ b/po/zh_Hans.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.9.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-21 22:22+0000\n"
+"POT-Creation-Date: 2026-06-21 22:35+0000\n"
 "PO-Revision-Date: 2024-12-01 22:18+0000\n"
 "Last-Translator: ?????? <clonewith at qq.com>\n"
 "Language-Team: Chinese (Simplified Han script) <https://"
@@ -586,7 +586,7 @@ msgstr "GFX"
 
 #: gui/editgamedialog.cpp:164 gui/options.cpp:2075 gui/options.cpp:2256
 #: backends/graphics/atari/atari-graphics.cpp:1056
-#: backends/graphics/sdl/sdl-graphics.cpp:487
+#: backends/graphics/sdl/sdl-graphics.cpp:491
 msgid "Graphics"
 msgstr "??????"
 
@@ -1945,7 +1945,7 @@ msgstr "???????????????"
 msgid "Check on the scummvm.org website for updates of shader packs"
 msgstr "?????? scummvm.org ???????????????????????????"
 
-#: gui/options.cpp:1700 backends/graphics/sdl/sdl-graphics.cpp:478
+#: gui/options.cpp:1700 backends/graphics/sdl/sdl-graphics.cpp:482
 msgid "Fullscreen mode"
 msgstr "????????????"
 
@@ -4293,7 +4293,7 @@ msgid "Choose a new folder"
 msgstr "??????????????????"
 
 #: backends/graphics/atari/atari-graphics.cpp:1060
-#: backends/graphics/sdl/sdl-graphics.cpp:514 engines/scumm/help.cpp:103
+#: backends/graphics/sdl/sdl-graphics.cpp:518 engines/scumm/help.cpp:103
 msgid "Toggle aspect ratio correction"
 msgstr "?????????????????????"
 
@@ -4365,34 +4365,34 @@ msgstr "SDL ??????"
 msgid "SDL Surface (forced 8bpp mode)"
 msgstr "SDL ????????????????????? 8bpp ?????????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:420
+#: backends/graphics/sdl/sdl-graphics.cpp:424
 #, c-format
 msgid "Saved screenshot '%s'"
 msgstr "??????????????????%s???"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:435
+#: backends/graphics/sdl/sdl-graphics.cpp:439
 msgid "Could not save screenshot"
 msgstr "??????????????????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:480
+#: backends/graphics/sdl/sdl-graphics.cpp:484
 msgid "Windowed mode"
 msgstr "????????????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:491 engines/colony/metaengine.cpp:173
+#: backends/graphics/sdl/sdl-graphics.cpp:495 engines/colony/metaengine.cpp:173
 #: engines/scumm/help.cpp:88
 msgid "Toggle fullscreen"
 msgstr "????????????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:498 engines/scumm/help.cpp:100
+#: backends/graphics/sdl/sdl-graphics.cpp:502 engines/scumm/help.cpp:100
 #: engines/wintermute/keymapper_tables.h:960
 msgid "Toggle mouse capture"
 msgstr "??????????????????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:503
+#: backends/graphics/sdl/sdl-graphics.cpp:507
 msgid "Toggle resizable window"
 msgstr ""
 
-#: backends/graphics/sdl/sdl-graphics.cpp:508 engines/stark/metaengine.cpp:240
+#: backends/graphics/sdl/sdl-graphics.cpp:512 engines/stark/metaengine.cpp:240
 #: engines/wintermute/keymapper_tables.h:646
 #: engines/wintermute/keymapper_tables.h:695
 #: engines/wintermute/keymapper_tables.h:1098
@@ -4405,27 +4405,27 @@ msgstr ""
 msgid "Save screenshot"
 msgstr "????????????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:521
+#: backends/graphics/sdl/sdl-graphics.cpp:525
 msgid "Toggle linear filtered scaling"
 msgstr "????????????????????????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:528
+#: backends/graphics/sdl/sdl-graphics.cpp:532
 msgid "Cycle through stretch modes"
 msgstr "????????????????????????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:534
+#: backends/graphics/sdl/sdl-graphics.cpp:538
 msgid "Increase the scale factor"
 msgstr "??????????????????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:540
+#: backends/graphics/sdl/sdl-graphics.cpp:544
 msgid "Decrease the scale factor"
 msgstr "??????????????????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:547
+#: backends/graphics/sdl/sdl-graphics.cpp:551
 msgid "Switch to the next scaler"
 msgstr "???????????????????????????"
 
-#: backends/graphics/sdl/sdl-graphics.cpp:552
+#: backends/graphics/sdl/sdl-graphics.cpp:556
 msgid "Switch to the previous scaler"
 msgstr "???????????????????????????"
 
@@ -15969,7 +15969,7 @@ msgstr "??????????????????"
 msgid "Toggle text speech"
 msgstr "??????????????????"
 
-#: engines/twine/detection.cpp:188
+#: engines/twine/detection.cpp:187
 msgid "This version requires Giflib which was not compiled into ScummVM"
 msgstr "??????????????? Giflib????????????????????? ScummVM"
 
diff --git a/po/zh_Hant.po b/po/zh_Hant.po
index cda9a7527c0..b7420a58073 100644
--- a/po/zh_Hant.po
+++ b/po/zh_Hant.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: ScummVM 2.9.0git\n"
 "Report-Msgid-Bugs-To: scummvm-devel at lists.scummvm.org\n"
-"POT-Creation-Date: 2026-06-21 22:22+0000\n"
+"POT-Creation-Date: 2026-06-21 22:35+0000\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: Automatically generated\n"
 "Language-Team: none\n"
@@ -571,7 +571,7 @@ msgstr ""
 
 #: gui/editgamedialog.cpp:164 gui/options.cpp:2075 gui/options.cpp:2256
 #: backends/graphics/atari/atari-graphics.cpp:1056
-#: backends/graphics/sdl/sdl-graphics.cpp:487
+#: backends/graphics/sdl/sdl-graphics.cpp:491
 msgid "Graphics"
 msgstr ""
 
@@ -1797,7 +1797,7 @@ msgstr ""
 msgid "Check on the scummvm.org website for updates of shader packs"
 msgstr ""
 
-#: gui/options.cpp:1700 backends/graphics/sdl/sdl-graphics.cpp:478
+#: gui/options.cpp:1700 backends/graphics/sdl/sdl-graphics.cpp:482
 msgid "Fullscreen mode"
 msgstr ""
 
@@ -4010,7 +4010,7 @@ msgid "Choose a new folder"
 msgstr ""
 
 #: backends/graphics/atari/atari-graphics.cpp:1060
-#: backends/graphics/sdl/sdl-graphics.cpp:514 engines/scumm/help.cpp:103
+#: backends/graphics/sdl/sdl-graphics.cpp:518 engines/scumm/help.cpp:103
 msgid "Toggle aspect ratio correction"
 msgstr ""
 
@@ -4082,34 +4082,34 @@ msgstr ""
 msgid "SDL Surface (forced 8bpp mode)"
 msgstr ""
 
-#: backends/graphics/sdl/sdl-graphics.cpp:420
+#: backends/graphics/sdl/sdl-graphics.cpp:424
 #, c-format
 msgid "Saved screenshot '%s'"
 msgstr ""
 
-#: backends/graphics/sdl/sdl-graphics.cpp:435
+#: backends/graphics/sdl/sdl-graphics.cpp:439
 msgid "Could not save screenshot"
 msgstr ""
 
-#: backends/graphics/sdl/sdl-graphics.cpp:480
+#: backends/graphics/sdl/sdl-graphics.cpp:484
 msgid "Windowed mode"
 msgstr ""
 
-#: backends/graphics/sdl/sdl-graphics.cpp:491 engines/colony/metaengine.cpp:173
+#: backends/graphics/sdl/sdl-graphics.cpp:495 engines/colony/metaengine.cpp:173
 #: engines/scumm/help.cpp:88
 msgid "Toggle fullscreen"
 msgstr ""
 
-#: backends/graphics/sdl/sdl-graphics.cpp:498 engines/scumm/help.cpp:100
+#: backends/graphics/sdl/sdl-graphics.cpp:502 engines/scumm/help.cpp:100
 #: engines/wintermute/keymapper_tables.h:960
 msgid "Toggle mouse capture"
 msgstr ""
 
-#: backends/graphics/sdl/sdl-graphics.cpp:503
+#: backends/graphics/sdl/sdl-graphics.cpp:507
 msgid "Toggle resizable window"
 msgstr ""
 
-#: backends/graphics/sdl/sdl-graphics.cpp:508 engines/stark/metaengine.cpp:240
+#: backends/graphics/sdl/sdl-graphics.cpp:512 engines/stark/metaengine.cpp:240
 #: engines/wintermute/keymapper_tables.h:646
 #: engines/wintermute/keymapper_tables.h:695
 #: engines/wintermute/keymapper_tables.h:1098
@@ -4122,27 +4122,27 @@ msgstr ""
 msgid "Save screenshot"
 msgstr ""
 
-#: backends/graphics/sdl/sdl-graphics.cpp:521
+#: backends/graphics/sdl/sdl-graphics.cpp:525
 msgid "Toggle linear filtered scaling"
 msgstr ""
 
-#: backends/graphics/sdl/sdl-graphics.cpp:528
+#: backends/graphics/sdl/sdl-graphics.cpp:532
 msgid "Cycle through stretch modes"
 msgstr ""
 
-#: backends/graphics/sdl/sdl-graphics.cpp:534
+#: backends/graphics/sdl/sdl-graphics.cpp:538
 msgid "Increase the scale factor"
 msgstr ""
 
-#: backends/graphics/sdl/sdl-graphics.cpp:540
+#: backends/graphics/sdl/sdl-graphics.cpp:544
 msgid "Decrease the scale factor"
 msgstr ""
 
-#: backends/graphics/sdl/sdl-graphics.cpp:547
+#: backends/graphics/sdl/sdl-graphics.cpp:551
 msgid "Switch to the next scaler"
 msgstr ""
 
-#: backends/graphics/sdl/sdl-graphics.cpp:552
+#: backends/graphics/sdl/sdl-graphics.cpp:556
 msgid "Switch to the previous scaler"
 msgstr ""
 
@@ -13974,7 +13974,7 @@ msgstr ""
 msgid "Toggle text speech"
 msgstr ""
 
-#: engines/twine/detection.cpp:188
+#: engines/twine/detection.cpp:187
 msgid "This version requires Giflib which was not compiled into ScummVM"
 msgstr ""
 



From noreply at scummvm.org  Sun Jun 21 22:37:28 2026
From: noreply at scummvm.org (sev-)
Date: Sun, 21 Jun 2026 22:37:28 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 1457e7179cbb788daa64d69859b819b0b4b40a88
Message-ID: <20260621223728.788EF7005CE@ron.scummvm.net>

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

Summary:
1457e7179c AUDIO: Change asound_fm.h include


Commit: 1457e7179cbb788daa64d69859b819b0b4b40a88
    https://github.com/scummvm/scummvm/commit/1457e7179cbb788daa64d69859b819b0b4b40a88
Author: tunnelsociety (tunnelsociety at mm.st)
Date: 2026-06-22T00:37:24+02:00

Commit Message:
AUDIO: Change asound_fm.h include

Let ScummVM use ALSA on FreeBSD.
`sound/asound_fm.h` is admittedly correct on Linux, but `alsa/sound/asound_fm.h`
works for even more systems.

Changed paths:
    audio/alsa_opl.cpp


diff --git a/audio/alsa_opl.cpp b/audio/alsa_opl.cpp
index edb0f09c502..02e9cca4c41 100644
--- a/audio/alsa_opl.cpp
+++ b/audio/alsa_opl.cpp
@@ -38,7 +38,7 @@
 
 #include <sys/ioctl.h>
 #include <alsa/asoundlib.h>
-#include <sound/asound_fm.h>
+#include <alsa/sound/asound_fm.h>
 
 namespace OPL {
 namespace ALSA {



From noreply at scummvm.org  Sun Jun 21 22:39:01 2026
From: noreply at scummvm.org (sev-)
Date: Sun, 21 Jun 2026 22:39:01 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 f43ebd3a88d0b9b6b91f0ca080f0e914cef522bb
Message-ID: <20260621223901.EB9E77005CE@ron.scummvm.net>

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:
2bbb9d16a7 CHAMBER: Seed table RNG offset via Common::RandomSource
302bc60901 CHAMBER: Fix EGA MURSM sprite assembly squashing The Wall doors
f160896826 CHAMBER: Fix EGA endgame saucer animation and THE END screen
7e61d411d3 CHAMBER: Remove debug zone number from banner and hide cursor during text/room transitions
f43ebd3a88 CHAMBER: Add EGA zone transitions (Ring/passage spiral, lift, dot, zoom)


Commit: 2bbb9d16a7e9a4f494abf22c2f6628fa0c2cf5fb
    https://github.com/scummvm/scummvm/commit/2bbb9d16a7e9a4f494abf22c2f6628fa0c2cf5fb
Author: Ion Andrei Cristian (lecturatul2017 at gmail.com)
Date: 2026-06-22T00:38:55+02:00

Commit Message:
CHAMBER: Seed table RNG offset via Common::RandomSource

Derive rand_seed from the engine's Common::RandomSource instead of
reading ConfMan/getMillis directly. RandomSource already honors the
"random_seed" config key for reproducible runs and otherwise seeds
from the time/date, so this drops the hand-rolled seeding while keeping
the original aleat_data[] table (and its exact distribution) intact.

Expose _rnd as public to match the other engine members reached through
g_vm, and drop the now-unused config-manager.h include in kult.cpp.

Changed paths:
    engines/chamber/chamber.h
    engines/chamber/kult.cpp


diff --git a/engines/chamber/chamber.h b/engines/chamber/chamber.h
index 2559d76b7d4..b23bf427327 100644
--- a/engines/chamber/chamber.h
+++ b/engines/chamber/chamber.h
@@ -47,11 +47,10 @@ enum CHAMBERActions {
 };
 
 class ChamberEngine : public Engine {
-private:
+public:
 	// We need random numbers
 	Common::RandomSource *_rnd;
 
-public:
 	ChamberEngine(OSystem *syst, const ADGameDescription *desc);
 	~ChamberEngine();
 
diff --git a/engines/chamber/kult.cpp b/engines/chamber/kult.cpp
index 58907294db0..ef110b3c611 100644
--- a/engines/chamber/kult.cpp
+++ b/engines/chamber/kult.cpp
@@ -19,7 +19,6 @@
  *
  */
 
-#include "common/config-manager.h"
 #include "common/error.h"
 #include "common/system.h"
 #include "engines/advancedDetector.h"
@@ -113,11 +112,12 @@ uint16 benchmarkCpu(void) {
 
 void randomize(void) {
 	// Original read the low byte of the BIOS timer-tick count (int 0x1A) into
-	// rand_seed. Use the host millisecond timer as an equivalent entropy source.
-	if (ConfMan.hasKey("random_seed"))
-		rand_seed = (byte)ConfMan.getInt("random_seed");
-	else
-		rand_seed = (byte)(g_system->getMillis());
+	// rand_seed, the starting offset into the fixed aleat_data[] table that
+	// getRand() walks. Keep that table (it reproduces the DOS game's exact
+	// random distribution) but derive the offset from ScummVM's RandomSource:
+	// its constructor already honors the "random_seed" config key for
+	// reproducible runs and otherwise seeds from the time/date.
+	rand_seed = (byte)g_vm->_rnd->getRandomNumber(255);
 	getRand();
 }
 


Commit: 302bc609013480a937a3e156124700f01f6dd574
    https://github.com/scummvm/scummvm/commit/302bc609013480a937a3e156124700f01f6dd574
Author: Ion Andrei Cristian (lecturatul2017 at gmail.com)
Date: 2026-06-22T00:38:55+02:00

Commit Message:
CHAMBER: Fix EGA MURSM sprite assembly squashing The Wall doors

loadMursmSprite placed each door sub-sprite at half spacing on both
axes (row = cgaOfs/40, col = (cgaOfs%40)/2), collapsing the 4x2 grid
into the top-left quarter and leaving the rest of the gate black.
Mirror the CGA layout (byte offset in a 20-byte-pitch buffer):
row = cgaOfs/20, col = cgaOfs%20. Fixes the black centre on the
closed/half-closed wall and the open/close animation frames.

Changed paths:
    engines/chamber/room.cpp


diff --git a/engines/chamber/room.cpp b/engines/chamber/room.cpp
index c72977ba08e..3f79018456c 100644
--- a/engines/chamber/room.cpp
+++ b/engines/chamber/room.cpp
@@ -1573,11 +1573,13 @@ byte *loadMursmSprite(byte index) {
 			byte sprIdx = *pinfo++;
 			uint16 flags = *pinfo++;
 			flags |= (*pinfo++) << 8;
-			uint16 cgaOfs = flags & 0x3FFF; /* offset in CGA flat buffer (pitch=20, 2 bytes/unit) */
+			uint16 cgaOfs = flags & 0x3FFF; /* byte offset into the CGA flat buffer (pitch = 20 bytes = 80 px) */
 
-			/* Convert CGA flat offset ??? EGA flat offset */
-			uint16 row   = cgaOfs / (20 * 2);       /* 20 CGA bytes ?? 2 bytes/unit = 40 bytes/row */
-			uint16 colCga = (cgaOfs % (20 * 2)) / 2; /* column in CGA bytes */
+			/* Convert CGA flat offset ??? EGA flat offset. Mirror the CGA path,
+			   which places the sub-sprite at byte cgaOfs in a 20-byte-pitch
+			   buffer: row = cgaOfs / 20, column = cgaOfs % 20 CGA bytes (?? 4 px). */
+			uint16 row    = cgaOfs / 20;
+			uint16 colCga = cgaOfs % 20;
 			uint16 egaBufOfs = row * egaPitch + colCga * 4;
 
 			Graphics::Surface *surf = ega_puzzl_res->getSprite(sprIdx & 0x7F);


Commit: f16089682650d894ba7657c414d5cdd348416042
    https://github.com/scummvm/scummvm/commit/f16089682650d894ba7657c414d5cdd348416042
Author: Ion Andrei Cristian (lecturatul2017 at gmail.com)
Date: 2026-06-22T00:38:55+02:00

Commit Message:
CHAMBER: Fix EGA endgame saucer animation and THE END screen

The EGA build crashed with a divide-by-zero (SIGFPE) at the winning end
sequence, the flying-saucer cutscene flew off-screen instead of receding,
and THE END logo was clipped or never drawn. Fix the EGA-specific paths:

- Add a real nearest-neighbour scaler (ega_scaleImage) and route zoomImage /
  zoomInplaceXY through it. The EGA versions were non-scaling stubs that
  ignored the target dimensions, so AnimSaucer redrew the saucer at native
  size every frame instead of shrinking it.
- Treat the saucer path x coordinate from SOUCO.BIN as a raw pixel column,
  not a 4-pixel-byte column (drop the *4). Every frame after the first was
  landing far off-screen to the right, so the saucer appeared to shoot right
  and vanish with no ascent.
- Sample the source with srcW/dstW rather than (srcW-1)/dstW so a 1:1 draw is
  the identity map. The old form dropped the final source column, clipping the
  right stroke of the 'D' in THE END.
- Add the missing pers_frames[9] = {9,11,0,0,0,0} end-logo frame. Its absence
  made the EGA portrait builder read a zero frame width and divide by it
  (SIGFPE), and left the logo unbuilt. Also guard the divide defensively.
- Clear the whole back buffer (and, in EGA, the front buffer) at the start of
  AnimSaucer. The original's 'sizeof - 2' left the bottom-right two pixels
  uncleared; in EGA the scroll-reveal lifted them up the screen as a red dot
  rising with the saucer.

Changed paths:
    engines/chamber/ega.cpp
    engines/chamber/portrait.cpp
    engines/chamber/script.cpp


diff --git a/engines/chamber/ega.cpp b/engines/chamber/ega.cpp
index 98a093a0004..7c61d61a2cd 100644
--- a/engines/chamber/ega.cpp
+++ b/engines/chamber/ega.cpp
@@ -492,16 +492,74 @@ void EGARenderer::traceLine(uint16 sx, uint16 ex, uint16 sy, uint16 ey, byte *so
 	  flushing the whole screen here turned each line into an updateScreen.*/
 }
 
-void EGARenderer::zoomImage(byte *pixels, byte w, byte h, byte /*nw*/, byte /*nh*/, byte *target, uint16 ofs) {
-	blitAndWait(pixels, w, w, h, target, ofs);
+// Nearest-neighbour scale an 8-bpp source (srcW x srcH pixels) into a
+// dstW x dstH pixel rect at (dstX, dstY). This is the 8-bpp equivalent of the
+// CGA cga_ZoomInplace math; the CGA partial-pixel/xbase packing logic collapses
+// to a plain sample-and-copy here since every EGA pixel is one byte. Every write
+// is clipped to the 320x200 screen so the saucer animation can never bleed past
+// the framebuffer bounds.
+static void ega_scaleImage(const byte *pixels, uint16 srcW, uint16 srcH,
+                           int16 dstX, int16 dstY, uint16 dstW, uint16 dstH, byte *target) {
+	if (dstW == 0 || dstH == 0 || srcW == 0 || srcH == 0)
+		return;
+
+	// 8.8 fixed-point source advance per target pixel. Use srcW/dstW (not
+	// (srcW-1)/dstW): at a 1:1 ratio that yields the identity map so the final
+	// source column/row is preserved -- the -1 form dropped it, which clipped the
+	// last pixel column (visible as the missing stroke of the "D" in THE END).
+	uint32 xstep = ((uint32)srcW << 8) / dstW;
+	uint32 ystep = ((uint32)srcH << 8) / dstH;
+
+	uint32 yval = 0;
+	for (uint16 ty = 0; ty < dstH; ty++, yval += ystep) {
+		int16 py = dstY + ty;
+		if (py < 0 || py >= EGA_HEIGHT)
+			continue;
+		uint16 sy = yval >> 8;
+		if (sy >= srcH)
+			sy = srcH - 1;
+		const byte *srcRow = pixels + sy * srcW;
+		byte *dstRow = target + py * EGA_BYTES_PER_LINE;
+		uint32 xval = 0;
+		for (uint16 tx = 0; tx < dstW; tx++, xval += xstep) {
+			int16 px = dstX + tx;
+			if (px < 0 || px >= EGA_WIDTH)
+				continue;
+			uint16 sx = xval >> 8;
+			if (sx >= srcW)
+				sx = srcW - 1;
+			dstRow[px] = srcRow[sx];
+		}
+	}
+
+	if (target == SCREENBUFFER) {
+		// flush the clipped destination rect
+		int16 x0 = dstX < 0 ? 0 : dstX;
+		int16 y0 = dstY < 0 ? 0 : dstY;
+		int16 x1 = dstX + (int16)dstW;
+		int16 y1 = dstY + (int16)dstH;
+		if (x1 > EGA_WIDTH)  x1 = EGA_WIDTH;
+		if (y1 > EGA_HEIGHT) y1 = EGA_HEIGHT;
+		if (x1 > x0 && y1 > y0)
+			g_vm->_renderer->blitToScreen(x0, y0, x1 - x0, y1 - y0);
+	}
+}
+
+void EGARenderer::zoomImage(byte *pixels, byte w, byte h, byte nw, byte nh, byte *target, uint16 ofs) {
+	// nw is the target width in 4-pixel bytes, nh the height in rows (see CGA zoomImage).
+	ega_scaleImage(pixels, w * 4, h, ofs % EGA_BYTES_PER_LINE, ofs / EGA_BYTES_PER_LINE, nw * 4, nh, target);
 }
 
 void EGARenderer::animZoomIn(byte *pixels, byte w, byte h, byte *target, uint16 ofs) {
 	blitAndWait(pixels, w, w, h, target, ofs);
 }
 
-void EGARenderer::zoomInplaceXY(byte *pixels, byte w, byte h, byte /*nw*/, byte /*nh*/, uint16 x, uint16 y, byte *target) {
-	blit(pixels, w, w, h, target, calcXY_p(x, y));
+void EGARenderer::zoomInplaceXY(byte *pixels, byte w, byte h, byte nw, byte nh, uint16 x, uint16 y, byte *target) {
+	// AnimSaucer's only caller passes the saucer path data from SOUCO.BIN, whose
+	// x/y/nw/nh are raw pixel coordinates (0..319), not 4-pixel-byte columns. So x
+	// is already a pixel column here -- pass it straight through (no *4), otherwise
+	// every frame after the first lands far off-screen to the right.
+	ega_scaleImage(pixels, w * 4, h, x, y, nw, nh, target);
 }
 
 void EGARenderer::drawSprite(byte *sprite, byte *screen, uint16 ofs) {
diff --git a/engines/chamber/portrait.cpp b/engines/chamber/portrait.cpp
index 8e56711ffc1..1c956b95811 100644
--- a/engines/chamber/portrait.cpp
+++ b/engines/chamber/portrait.cpp
@@ -70,7 +70,8 @@ persframe_t pers_frames[] = {
 	{47, 13, 0xFF,    0, 0xC0,    3},
 	{65, 18, 0xFF, 0xAA, 0xEA, 0xAB},
 	{38, 11, 0xFF,    0, 0xC0,    3},
-	{27, 34,    0,    0,    0,    0}
+	{27, 34,    0,    0,    0,    0},
+	{ 9, 11,    0,    0,    0,    0}   /*end-screen logo frame (icon 167); missing entry caused a divide-by-zero in EGA*/
 };
 
 void makePortraitFrame(byte index, byte *target) {
@@ -205,6 +206,8 @@ static byte *ega_loadPortrait(byte **pinfo, byte *end) {
 		int16 spitch = surf->pitch;
 
 		uint16 cga_ofs = flags & 0x3FFF;
+		if (cur_frame_width == 0) /*degenerate frame: avoid the divide below*/
+			continue;
 		uint16 row = cga_ofs / cur_frame_width;
 		uint16 col_cga = cga_ofs % cur_frame_width;
 		byte *dst = sprit_load_buffer + 2 + 2 + row * frame_pw + col_cga * 4;
diff --git a/engines/chamber/script.cpp b/engines/chamber/script.cpp
index 9359788b66b..5ec0ba077ad 100644
--- a/engines/chamber/script.cpp
+++ b/engines/chamber/script.cpp
@@ -2875,7 +2875,17 @@ static void AnimSaucer(void) {
 	uint16 delay;
 	byte scroll_done = 0;
 
-	memset(backbuffer, 0, sizeof(backbuffer) - 2);  /*TODO: original bug?*/
+	/*Clear the whole buffer: the original's "- 2" left the bottom-right two
+	  bytes (row 199, x=318..319) holding stale pixels. In EGA those are visible
+	  and the scroll-reveal lifts them up the screen as a red dot rising with the
+	  saucer, so clear all of it.*/
+	memset(backbuffer, 0, sizeof(backbuffer));
+	/*EGA: also blank the front buffer. The saucer animation only paints the
+	  saucer region, so leftover sprites from the final game frame (player,
+	  HUD pixels) would otherwise survive on the black backdrop right through to
+	  THE END screen.*/
+	if (g_vm->_videoMode == Common::kRenderEGA)
+		memset(frontbuffer, 0, sizeof(SCREENBUFFER));
 	g_vm->_renderer->backBufferToRealFull();
 	g_vm->_renderer->colorSelect(0x30);
 


Commit: 7e61d411d343178250e273576755c851e8b1b2cf
    https://github.com/scummvm/scummvm/commit/7e61d411d343178250e273576755c851e8b1b2cf
Author: Ion Andrei Cristian (lecturatul2017 at gmail.com)
Date: 2026-06-22T00:38:55+02:00

Commit Message:
CHAMBER: Remove debug zone number from banner and hide cursor during text/room transitions

Disable DEBUG_ZONE so the 3-digit zone index no longer prints on the
location banner. Hide the mouse pointer before text boxes/bubbles are
drawn and while a room's entry animation plays; the game loop re-shows
it once it waits for player input.

Changed paths:
    engines/chamber/common.h
    engines/chamber/cursor.cpp
    engines/chamber/cursor.h
    engines/chamber/dialog.cpp
    engines/chamber/print.cpp
    engines/chamber/room.cpp


diff --git a/engines/chamber/common.h b/engines/chamber/common.h
index a2314c736b9..eb9dbe66480 100644
--- a/engines/chamber/common.h
+++ b/engines/chamber/common.h
@@ -58,7 +58,7 @@ typedef struct rect_t {
 #define DEBUG_SKIP_INTRO 0xFF
 #endif
 
-#if 1
+#if 0
 #define DEBUG_ZONE
 #endif
 
diff --git a/engines/chamber/cursor.cpp b/engines/chamber/cursor.cpp
index 7474ed31785..38d0e6cd84b 100644
--- a/engines/chamber/cursor.cpp
+++ b/engines/chamber/cursor.cpp
@@ -153,6 +153,13 @@ Restore background pixels under cursor
 void undrawCursor(byte *target) {
 }
 
+/*
+Hide the system mouse pointer (used during non-interactive room transitions)
+*/
+void hideMouseCursor(void) {
+	CursorMan.showMouse(false);
+}
+
 /*
 Restore pixels under cursor and update cursor sprite
 */
diff --git a/engines/chamber/cursor.h b/engines/chamber/cursor.h
index 0a22c84a352..bf898096b74 100644
--- a/engines/chamber/cursor.h
+++ b/engines/chamber/cursor.h
@@ -52,6 +52,7 @@ void updateCursor(void);
 void drawCursor(byte *target);
 void undrawCursor(byte *target);
 void updateUndrawCursor(byte *target);
+void hideMouseCursor(void);
 
 } // End of namespace Chamber
 
diff --git a/engines/chamber/dialog.cpp b/engines/chamber/dialog.cpp
index 7235a716442..7b92359c6b8 100644
--- a/engines/chamber/dialog.cpp
+++ b/engines/chamber/dialog.cpp
@@ -20,6 +20,7 @@
  */
 
 #include "common/system.h"
+#include "graphics/cursorman.h"
 
 #include "chamber/chamber.h"
 #include "chamber/common.h"
@@ -114,6 +115,10 @@ void drawPersonBubble(byte x, byte y, byte flags, byte *msg) {
 	byte w, h;
 	uint16 ww, nw;
 
+	// Hide the mouse pointer before the bubble is drawn, so it is gone from the
+	// moment the text appears. selectCursor() re-shows it when play resumes.
+	CursorMan.showMouse(false);
+
 	char_draw_max_width = flags & 0x1F;
 	char_xlat_table = chars_color_bonw;
 
diff --git a/engines/chamber/print.cpp b/engines/chamber/print.cpp
index e16ce3484e5..42fc5f9d632 100644
--- a/engines/chamber/print.cpp
+++ b/engines/chamber/print.cpp
@@ -19,6 +19,8 @@
  *
  */
 
+#include "graphics/cursorman.h"
+
 #include "chamber/chamber.h"
 #include "chamber/common.h"
 #include "chamber/cga.h"
@@ -198,6 +200,11 @@ void cga_DrawTextBox(byte *msg, byte *target) {
 	uint16 x, y, w, i;
 	uint16 ww, nw;
 
+	// Hide the mouse pointer before the text box is drawn, so it is gone from
+	// the moment the box appears (not only once promptWait() begins). The game
+	// loop's selectCursor() re-shows it when interactive play resumes.
+	CursorMan.showMouse(false);
+
 	char_xlat_table = chars_color_bonc;
 
 	if (g_vm->getLanguage() == Common::EN_USA) {
diff --git a/engines/chamber/room.cpp b/engines/chamber/room.cpp
index 3f79018456c..c0b39c7281b 100644
--- a/engines/chamber/room.cpp
+++ b/engines/chamber/room.cpp
@@ -1040,6 +1040,11 @@ void drawZoneSpots(void) {
 }
 
 void refreshZone(void) {
+	// Hide the mouse pointer while the new room is drawn and its entry
+	// animation plays. The game loop's selectCursor() re-shows it once it is
+	// waiting for player input again.
+	hideMouseCursor();
+
 	popDirtyRects(DirtyRectSprite);
 	popDirtyRects(DirtyRectBubble);
 	popDirtyRects(DirtyRectText);


Commit: f43ebd3a88d0b9b6b91f0ca080f0e914cef522bb
    https://github.com/scummvm/scummvm/commit/f43ebd3a88d0b9b6b91f0ca080f0e914cef522bb
Author: Ion Andrei Cristian (lecturatul2017 at gmail.com)
Date: 2026-06-22T00:38:55+02:00

Commit Message:
CHAMBER: Add EGA zone transitions (Ring/passage spiral, lift, dot, zoom)

Implement the EGA renderer's stubbed transition effects so room changes
animate like CGA: the spiral background reveal when walking The Ring and
passages (gated by the previously-unused skip_zone_transition), plus the
lift wipes, dot dissolve and zoom-in reveals.

Changed paths:
    engines/chamber/anim.cpp
    engines/chamber/ega.cpp
    engines/chamber/ega.h
    engines/chamber/room.cpp
    engines/chamber/script.cpp


diff --git a/engines/chamber/anim.cpp b/engines/chamber/anim.cpp
index 8ba0b2d9e9f..18a8108a2e3 100644
--- a/engines/chamber/anim.cpp
+++ b/engines/chamber/anim.cpp
@@ -116,6 +116,36 @@ void clipSprite(byte *x, byte *y, byte *sprw, byte *sprh, byte **sprite, int8 dx
 
 void copyScreenBlockWithDotEffect(byte *source, byte x, byte y, byte width, byte height, byte *target) {
 	if (g_vm->_videoMode == Common::kRenderEGA) {
+		/* EGA: linear 1 byte/pixel. Reveal the block in the same scattered
+		   ("dot dissolve") order as the CGA path, blitting periodically so the
+		   transition is animated instead of an instant copy. */
+		uint16 xx = x * 4;
+		uint16 ww = width * 4;
+		uint32 end = (uint32)ww * height;
+		uint16 step = dot_effect_step ? dot_effect_step : 17;
+		uint32 offs = 0;
+		uint32 guard = 0;
+
+		if (target == SCREENBUFFER) {
+			do {
+				uint16 px = xx + offs % ww;
+				uint16 py = y + offs / ww;
+				uint16 ofs = py * EGA_BYTES_PER_LINE + px;
+				target[ofs] = source[ofs];
+
+				offs += step;
+				if (offs > end)
+					offs -= end;
+
+				/* blit roughly once per block-row's worth of dots */
+				if ((++guard % ww) == 0) {
+					g_vm->_renderer->blitToScreen(xx, y, ww, height);
+					waitVBlank();
+				}
+			} while (offs != 0 && guard <= end);
+		}
+
+		/* ensure the block is fully copied (the scatter may skip pixels) */
 		g_vm->_renderer->copyScreenBlock(source, width, height, target, g_vm->_renderer->calcXY_p(x, y));
 		return;
 	}
diff --git a/engines/chamber/ega.cpp b/engines/chamber/ega.cpp
index 7c61d61a2cd..d9a346e025a 100644
--- a/engines/chamber/ega.cpp
+++ b/engines/chamber/ega.cpp
@@ -65,6 +65,65 @@ void ega_drawBackground(byte *target) {
 		g_vm->_renderer->blitToScreen(0, 0, EGA_WIDTH, EGA_HEIGHT);
 }
 
+#define EGA_WIPE_BLOCK 20
+
+static int s_wipeBlocks;
+
+/*Reveal one clean-background block onto the screen; flush+throttle every few blocks*/
+static void egaRevealBlock(int bx, int by) {
+	int w = (bx + EGA_WIPE_BLOCK > EGA_WIDTH) ? (EGA_WIDTH - bx) : EGA_WIPE_BLOCK;
+	int h = (by + EGA_WIPE_BLOCK > EGA_HEIGHT) ? (EGA_HEIGHT - by) : EGA_WIPE_BLOCK;
+	if (w <= 0 || h <= 0)
+		return;
+	for (int i = 0; i < h; i++)
+		memcpy(SCREENBUFFER + (by + i) * EGA_BYTES_PER_LINE + bx,
+		       ega_fond_clean + (by + i) * EGA_BYTES_PER_LINE + bx, w);
+	if (++s_wipeBlocks % 3 == 0) {
+		g_vm->_renderer->blitToScreen(0, 0, EGA_WIDTH, EGA_HEIGHT);
+		waitVBlank();
+	}
+}
+
+void ega_zoneRevealWipe(void) {
+	// Original zone-change transition for The Ring / passages: first the empty
+	// background pattern spirals in over the old room (no room underneath), then
+	// the new room is shown. Here we spiral in the clean background, then blit the
+	// new room (already composited in the backbuffer) on top.
+	const int B = EGA_WIPE_BLOCK;
+	int left = 0, top = 0, right = EGA_WIDTH, bottom = EGA_HEIGHT;
+	s_wipeBlocks = 0;
+
+	while (left < right && top < bottom) {
+		for (int x = left; x < right; x += B)        /*top row, L->R*/
+			egaRevealBlock(x, top);
+		top += B;
+		for (int y = top; y < bottom; y += B)        /*right col, T->B*/
+			egaRevealBlock(right - B, y);
+		right -= B;
+		if (top < bottom) {
+			for (int x = right - B; x >= left; x -= B)   /*bottom row, R->L*/
+				egaRevealBlock(x, bottom - B);
+			bottom -= B;
+		}
+		if (left < right) {
+			for (int y = bottom - B; y >= top; y -= B)   /*left col, B->T*/
+				egaRevealBlock(left, y);
+			left += B;
+		}
+	}
+
+	// make sure the whole play area is the clean background (no room), then hold
+	// briefly so the empty background is visible before the room appears
+	memcpy(SCREENBUFFER, ega_fond_clean, EGA_SCREEN_SIZE);
+	g_vm->_renderer->blitToScreen(0, 0, EGA_WIDTH, EGA_HEIGHT);
+	for (int i = 0; i < 8; i++)
+		waitVBlank();
+
+	// now show the new room (already composited in the backbuffer)
+	memcpy(SCREENBUFFER, ega_backbuffer, EGA_SCREEN_SIZE);
+	g_vm->_renderer->blitToScreen(0, 0, EGA_WIDTH, EGA_HEIGHT);
+}
+
 Graphics::Surface *ega_loadFond(const char *filename) {
 	Common::File fd;
 	if (!fd.open(filename)) {
@@ -452,20 +511,83 @@ void EGARenderer::animLiftToRight(uint16 n, byte *pixels, uint16 pw, uint16 w, u
 	}
 }
 
+/* EGA port of the CGA lift transitions: shift the block one line/column per
+   step, fill the freed edge from source (the new image), then blit+vblank so
+   the move is animated. ofs is a linear EGA offset; w is in CGA bytes, so a
+   block column is 4 EGA pixels wide. */
+
+/*Shift block n lines down, filling the freed top line from source*/
 void EGARenderer::hideScreenBlockLiftToDown(uint16 n, byte *screen, byte *source, uint16 w, uint16 h, byte *target, uint16 ofs) {
-	ega_CopyScreenBlock(source, w * 4, h + n, target, ofs);
+	uint16 ew = w * 4;
+	while (n--) {
+		uint16 sofs = ofs;
+		uint16 tofs = ofs + EGA_BYTES_PER_LINE;
+		for (uint16 i = 0; i < h; i++) {
+			memcpy(target + tofs, screen + sofs, ew);
+			tofs = sofs;
+			sofs -= EGA_BYTES_PER_LINE;
+		}
+		memcpy(target + tofs, source + tofs, ew);
+		if (screen == SCREENBUFFER)
+			blitToScreen(0, 0, EGA_WIDTH, EGA_HEIGHT);
+		waitVBlank();
+		ofs += EGA_BYTES_PER_LINE;
+	}
 }
 
+/*Shift block n lines up, filling the freed bottom line from source*/
 void EGARenderer::hideScreenBlockLiftToUp(uint16 n, byte *screen, byte *source, uint16 w, uint16 h, byte *target, uint16 ofs) {
-	ega_CopyScreenBlock(source, w * 4, h + n, target, ofs);
+	uint16 ew = w * 4;
+	while (n--) {
+		uint16 sofs = ofs;
+		uint16 tofs = ofs - EGA_BYTES_PER_LINE;
+		for (uint16 i = 0; i < h; i++) {
+			memcpy(target + tofs, screen + sofs, ew);
+			tofs = sofs;
+			sofs += EGA_BYTES_PER_LINE;
+		}
+		memcpy(target + tofs, source + tofs, ew);
+		if (screen == SCREENBUFFER)
+			blitToScreen(0, 0, EGA_WIDTH, EGA_HEIGHT);
+		waitVBlank();
+		ofs -= EGA_BYTES_PER_LINE;
+	}
 }
 
+/*Shift block n columns left, filling the freed right column from source*/
 void EGARenderer::hideScreenBlockLiftToLeft(uint16 n, byte *screen, byte *source, uint16 w, uint16 h, byte *target, uint16 ofs) {
-	ega_CopyScreenBlock(source, (w + n) * 4, h, target, ofs - n * 4);
+	uint16 ew = w * 4;
+	while (n--) {
+		uint16 sofs = ofs;
+		for (uint16 i = 0; i < h; i++) {
+			uint16 tofs = sofs - 4;
+			memmove(target + tofs, screen + sofs, ew);
+			memcpy(target + tofs + ew, source + tofs + ew, 4);
+			sofs += EGA_BYTES_PER_LINE;
+		}
+		if (screen == SCREENBUFFER)
+			blitToScreen(0, 0, EGA_WIDTH, EGA_HEIGHT);
+		waitVBlank();
+		ofs -= 4;
+	}
 }
 
+/*Shift block n columns right, filling the freed left column from source*/
 void EGARenderer::hideScreenBlockLiftToRight(uint16 n, byte *screen, byte *source, uint16 w, uint16 h, byte *target, uint16 ofs) {
-	ega_CopyScreenBlock(source, (w + n) * 4, h, target, ofs - w * 4);
+	uint16 ew = w * 4;
+	while (n--) {
+		uint16 sofs = ofs;
+		for (uint16 i = 0; i < h; i++) {
+			uint16 tofs = sofs + 4;
+			memmove(target + tofs - ew, screen + sofs - ew, ew);
+			memcpy(target + tofs - ew, source + tofs - ew, 4);
+			sofs += EGA_BYTES_PER_LINE;
+		}
+		if (screen == SCREENBUFFER)
+			blitToScreen(0, 0, EGA_WIDTH, EGA_HEIGHT);
+		waitVBlank();
+		ofs += 4;
+	}
 }
 
 void EGARenderer::hideShatterFall(byte *screen, byte *source, uint16 w, uint16 h, byte *target, uint16 ofs) {
@@ -551,6 +673,38 @@ void EGARenderer::zoomImage(byte *pixels, byte w, byte h, byte nw, byte nh, byte
 }
 
 void EGARenderer::animZoomIn(byte *pixels, byte w, byte h, byte *target, uint16 ofs) {
+	/* Progressive zoom-in reveal (CGA animZoomIn equivalent for EGA): draw the
+	   image scaled from a small size up to full, growing out of its centre.
+	   Each larger frame is centred and covers the previous one, so no per-step
+	   restore is needed. zoomImage() draws and flushes the scaled rect. */
+	uint16 fw = (uint16)w * 4;          /* final width in pixels */
+	uint16 fh = h;                      /* final height in rows */
+	uint16 ox = ofs % EGA_BYTES_PER_LINE;
+	uint16 oy = ofs / EGA_BYTES_PER_LINE;
+	uint16 cx = ox + fw / 2;            /* zoom centre */
+	uint16 cy = oy + fh / 2;
+
+	uint16 maxside = (fw > fh) ? fw : fh;
+	uint16 nsteps = maxside / 2;
+	if (nsteps < 8)
+		nsteps = 8;
+	if (nsteps > 64)
+		nsteps = 64;
+
+	for (uint16 s = 1; s < nsteps; s++) {
+		byte nw = (byte)((uint16)w * s / nsteps);
+		byte nh = (byte)((uint16)h * s / nsteps);
+		if (nw < 1)
+			nw = 1;
+		if (nh < 1)
+			nh = 1;
+		uint16 tx = cx - (uint16)nw * 4 / 2;
+		uint16 ty = cy - (uint16)nh / 2;
+		zoomImage(pixels, w, h, nw, nh, target, ty * EGA_BYTES_PER_LINE + tx);
+		waitVBlank();
+	}
+
+	/* final full-size image */
 	blitAndWait(pixels, w, w, h, target, ofs);
 }
 
diff --git a/engines/chamber/ega.h b/engines/chamber/ega.h
index 9d3d7f5050a..9516de42274 100644
--- a/engines/chamber/ega.h
+++ b/engines/chamber/ega.h
@@ -50,6 +50,10 @@ Graphics::Surface *ega_loadFond(const char *filename);
 extern byte ega_fond_clean[EGA_SCREEN_SIZE];
 void ega_drawBackground(byte *target);
 
+// --- zone-change transition ---
+// Reveal the new room (backbuffer) over the screen with a serpentine line wipe.
+void ega_zoneRevealWipe(void);
+
 } // End of namespace Chamber
 
 #endif // CHAMBER_EGA_H
diff --git a/engines/chamber/room.cpp b/engines/chamber/room.cpp
index c0b39c7281b..63cdfb33c2d 100644
--- a/engines/chamber/room.cpp
+++ b/engines/chamber/room.cpp
@@ -1039,6 +1039,23 @@ void drawZoneSpots(void) {
 	drawPersons();
 }
 
+/*
+The line-wipe zone transition only plays while moving through the corridor-like
+areas: The Ring and the Passages. Normal room entries stay instant (or use their
+own scripted drawIcone effect).
+*/
+static bool zoneHasWalkTransition(byte area) {
+	switch (area) {
+	case kAreaTheRing1: case kAreaTheRing2: case kAreaTheRing3:
+	case kAreaTheRing4: case kAreaTheRing5: case kAreaTheRing6:
+	case kAreaPassage1: case kAreaPassage2: case kAreaPassage3:
+	case kAreaPassage4: case kAreaPassage5: case kAreaPassage6: case kAreaPassage7:
+		return true;
+	default:
+		return false;
+	}
+}
+
 void refreshZone(void) {
 	// Hide the mouse pointer while the new room is drawn and its entry
 	// animation plays. The game loop's selectCursor() re-shows it once it is
@@ -1049,7 +1066,16 @@ void refreshZone(void) {
 	popDirtyRects(DirtyRectBubble);
 	popDirtyRects(DirtyRectText);
 
-	g_vm->_renderer->backBufferToRealFull();
+	// Reveal the new room with an animated transition unless this change asked to
+	// skip it (e.g. entering through an already-animated door). skip_zone_transition
+	// is set by SCR_42_LoadZone; plain zone changes (walking around The Ring / through
+	// passages via SCR_36_ChangeZone) leave it 0 and so get the dot-dissolve reveal.
+	if (!skip_zone_transition && g_vm->_videoMode == Common::kRenderEGA
+	        && zoneHasWalkTransition(script_byte_vars.zone_area)) {
+		ega_zoneRevealWipe();
+	} else {
+		g_vm->_renderer->backBufferToRealFull();
+	}
 
 	in_de_profundis = 0;
 	IFGM_StopSample();
diff --git a/engines/chamber/script.cpp b/engines/chamber/script.cpp
index 5ec0ba077ad..754faf0fc36 100644
--- a/engines/chamber/script.cpp
+++ b/engines/chamber/script.cpp
@@ -1109,7 +1109,13 @@ uint16 SCR_1B_HidePortraitLiftUp(void) {
 	}
 
 	if (g_vm->_videoMode == Common::kRenderEGA) {
-		g_vm->_renderer->copyScreenBlock(backbuffer, width, height, SCREENBUFFER, offs);
+		offs = g_vm->_renderer->calcXY_p(x, y + 1);
+		while (--height)
+			g_vm->_renderer->hideScreenBlockLiftToUp(1, SCREENBUFFER, backbuffer, width, height, SCREENBUFFER, offs);
+		/*hide topmost line*/
+		offs -= EGA_BYTES_PER_LINE;
+		memcpy(SCREENBUFFER + offs, backbuffer + offs, width * 4);
+		g_vm->_renderer->blitToScreen(offs % EGA_BYTES_PER_LINE, offs / EGA_BYTES_PER_LINE, width * 4, 1);
 		return 0;
 	}
 
@@ -1150,7 +1156,13 @@ uint16 SCR_1C_HidePortraitLiftDown(void) {
 	}
 
 	if (g_vm->_videoMode == Common::kRenderEGA) {
-		g_vm->_renderer->copyScreenBlock(backbuffer, width, height, SCREENBUFFER, offs);
+		offs = g_vm->_renderer->calcXY_p(x, y + height - 2);
+		while (--height)
+			g_vm->_renderer->hideScreenBlockLiftToDown(1, SCREENBUFFER, backbuffer, width, height, SCREENBUFFER, offs);
+		/*hide bottommost line*/
+		offs += EGA_BYTES_PER_LINE;
+		memcpy(SCREENBUFFER + offs, backbuffer + offs, width * 4);
+		g_vm->_renderer->blitToScreen(offs % EGA_BYTES_PER_LINE, offs / EGA_BYTES_PER_LINE, width * 4, 1);
 		return 0;
 	}
 
@@ -1612,6 +1624,8 @@ Draw new zone
 */
 uint16 SCR_43_RefreshZone(void) {
 	script_ptr++;
+	// A standalone refresh (not a zone change) should just redraw instantly.
+	skip_zone_transition = 1;
 	refreshZone();
 	return 0;
 }



From noreply at scummvm.org  Sun Jun 21 22:40:14 2026
From: noreply at scummvm.org (sev-)
Date: Sun, 21 Jun 2026 22:40:14 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 c736cbc2c016eabf5075ce12fb73ea17c344f512
Message-ID: <20260621224014.AE6D57005CE@ron.scummvm.net>

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

Summary:
c736cbc2c0 GUI: Fix MIDI / MT-32 device selection in Game Options override


Commit: c736cbc2c016eabf5075ce12fb73ea17c344f512
    https://github.com/scummvm/scummvm/commit/c736cbc2c016eabf5075ce12fb73ea17c344f512
Author: Kartik Saxena (kartiksaxena2005 at gmail.com)
Date: 2026-06-22T00:40:11+02:00

Commit Message:
GUI: Fix MIDI / MT-32 device selection in Game Options override

GUI: Removing tab

Changed paths:
    gui/options.cpp


diff --git a/gui/options.cpp b/gui/options.cpp
index 0f58e53cca0..91c73b3a537 100644
--- a/gui/options.cpp
+++ b/gui/options.cpp
@@ -1350,8 +1350,8 @@ void OptionsDialog::setMIDISettingsState(bool enabled) {
 	if (_guioptions.contains(GUIO_NOMIDI))
 		enabled = false;
 
-	_gmDevicePopUpDesc->setEnabled(_domain.equals(Common::ConfigManager::kApplicationDomain) ? enabled : false);
-	_gmDevicePopUp->setEnabled(_domain.equals(Common::ConfigManager::kApplicationDomain) ? enabled : false);
+	_gmDevicePopUpDesc->setEnabled(enabled);
+	_gmDevicePopUp->setEnabled(enabled);
 
 	_enableMIDISettings = enabled;
 
@@ -1372,8 +1372,8 @@ void OptionsDialog::setMIDISettingsState(bool enabled) {
 void OptionsDialog::setMT32SettingsState(bool enabled) {
 	_enableMT32Settings = enabled;
 
-	_mt32DevicePopUpDesc->setEnabled(_domain.equals(Common::ConfigManager::kApplicationDomain) ? enabled : false);
-	_mt32DevicePopUp->setEnabled(_domain.equals(Common::ConfigManager::kApplicationDomain) ? enabled : false);
+	_mt32DevicePopUpDesc->setEnabled(enabled);
+	_mt32DevicePopUp->setEnabled(enabled);
 
 	_mt32Checkbox->setEnabled(enabled);
 	_enableGSCheckbox->setEnabled(enabled);



From noreply at scummvm.org  Sun Jun 21 22:44:39 2026
From: noreply at scummvm.org (sev-)
Date: Sun, 21 Jun 2026 22:44:39 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 9c6e2fbb3dc2b5377dc200a258a3de391c185278
Message-ID: <20260621224440.006A97005CE@ron.scummvm.net>

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

Summary:
9c6e2fbb3d NEWS: Mention new changes in the General section


Commit: 9c6e2fbb3dc2b5377dc200a258a3de391c185278
    https://github.com/scummvm/scummvm/commit/9c6e2fbb3dc2b5377dc200a258a3de391c185278
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2026-06-22T00:44:07+02:00

Commit Message:
NEWS: Mention new changes in the General section

Changed paths:
    NEWS.md


diff --git a/NEWS.md b/NEWS.md
index feaca0e7c33..8e5dfbc53ab 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -4,6 +4,9 @@ For a more comprehensive changelog of the latest experimental code, see:
 #### 2026.4.0 "Carousels & Killer Whales" (2026-10-XX)
  General:
    - Optimised mixing and rate converters, for better performance.
+   - Implemented ImGui-based interface for the Event Recorder.
+   - Fixed bug with inability to specify game overrides for MIDI and MT-32
+     devices.
 
  Atari port:
    - Added integration with nFM library.



From noreply at scummvm.org  Sun Jun 21 22:47:33 2026
From: noreply at scummvm.org (sev-)
Date: Sun, 21 Jun 2026 22:47:33 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 f216fd288060e14f8811629bd670588c5b128a0b
Message-ID: <20260621224733.426E07005CE@ron.scummvm.net>

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

Summary:
f651362f97 DIRECTOR: Stub DateTime Xtra for the L??wenzahn, TKKG and Oscar series of games
8d254fbbe5 DIRECTOR: Stub NetLingo Xtra for the L??wenzahn, TKKG & Oscar series of games
9034771ccf DIRECTOR: Stub PaintX Xtra for L??wenzahn 2
c79750df71 DIRECTOR: Stub SetMouse (SMXTRA) Xtra for L??wenzahn 2-4
846ab92664 DIRECTOR: LINGO: Implement FileXtra4
d535c2f9a7 DIRECTOR: LINGO: Implement FileXtra DirectoryExists and DirectoryToList
4bb528d1a6 DIRECTOR: Load Xtras from nested folders
a2f863f792 DIRECTOR: LINGO: Recognise QTASSET as the QuickTime asset Xtra
e8c1920cfb DIRECTOR: LINGO: Fix FileXtra DirectoryToList path resolution
f216fd2880 DIRECTOR: List German Terzio/Tivola titles in Xtra USED IN blocks


Commit: f651362f9763ea3730eb7f9817ae21be4370ab5b
    https://github.com/scummvm/scummvm/commit/f651362f9763ea3730eb7f9817ae21be4370ab5b
Author: Lariaa (30549703+Lariaa at users.noreply.github.com)
Date: 2026-06-22T00:47:24+02:00

Commit Message:
DIRECTOR: Stub DateTime Xtra for the L??wenzahn, TKKG and Oscar series of games

Changed paths:
  A engines/director/lingo/xtras/d/datetime.cpp
  A engines/director/lingo/xtras/d/datetime.h
    engines/director/lingo/lingo-object.cpp
    engines/director/module.mk


diff --git a/engines/director/lingo/lingo-object.cpp b/engines/director/lingo/lingo-object.cpp
index 4355af923f4..a8231502aab 100644
--- a/engines/director/lingo/lingo-object.cpp
+++ b/engines/director/lingo/lingo-object.cpp
@@ -160,12 +160,12 @@
 #include "director/lingo/xtras/b/budapi.h"
 #include "director/lingo/xtras/d/directsound.h"
 #include "director/lingo/xtras/d/displayres.h"
+#include "director/lingo/xtras/d/datetime.h"
 #include "director/lingo/xtras/f/filextra.h"
 #include "director/lingo/xtras/g/getdir.h"
 #include "director/lingo/xtras/k/keypoll.h"
 #include "director/lingo/xtras/m/masterapp.h"
 #include "director/lingo/xtras/m/mui.h"
-#include "director/lingo/xtras/m/mui.h"
 #include "director/lingo/xtras/o/openurl.h"
 #include "director/lingo/xtras/o/oscheck.h"
 #include "director/lingo/xtras/q/qtvrxtra.h"
@@ -276,6 +276,7 @@ static const struct XLibProto {
 	XLIBDEF(DPWAVIXObj,			kXObj,			300),	// D3
 	XLIBDEF(DPWQTWXObj,			kXObj,			300),	// D3
 	XLIBDEF(DarkenScreen,		kXObj,			300),	// D3
+	XLIBDEF(DateTimeXtra,			kXtraObj,					500),	// D5
 	XLIBDEF(DateUtilXObj,		kXObj,			400),	// D4
 	XLIBDEF(DeveloperStack,		kXObj,			300),	// D3
 	XLIBDEF(DialogsXObj,		kXObj,			400),	// D4
diff --git a/engines/director/lingo/xtras/d/datetime.cpp b/engines/director/lingo/xtras/d/datetime.cpp
new file mode 100644
index 00000000000..dc682d9cc0f
--- /dev/null
+++ b/engines/director/lingo/xtras/d/datetime.cpp
@@ -0,0 +1,174 @@
+/* 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/system.h"
+
+#include "director/director.h"
+#include "director/lingo/lingo.h"
+#include "director/lingo/lingo-object.h"
+#include "director/lingo/lingo-utils.h"
+#include "director/lingo/xtras/d/datetime.h"
+
+/**************************************************
+ *
+ * USED IN:
+ * Loewenzahn 2 / 3 / 4 / 5 / 6 / 7 / 8 / Adventskalender / Spielebox
+ *
+ **************************************************/
+
+/*
+-- xtra DateTimeXtra    -- Version 1.0.1 (32 Bit)
+
+-- --------------------------------------------------------------------
+-- this Xtra returns date and time related information
+-- --------------------------------------------------------------------
+
+-- ???1997 by Stephan Eichhorn, Scirius Multimedia
+-- e-mail:  xtras at scririus.com
+-- WWW:     http://www.scirius.com
+
+-- global methods --
+--------------------------------------------------------
+* GetSeconds         -- get the seconds (0...59) 
+* GetMinutes         -- get the minutes (0...59) 
+* GetHours           -- get the hours (0...23) 
+* GetDay             -- get the day (1...31) 
+* GetMonth           -- get the month (1...12) 
+* GetYear            -- get the year  
+* GetWeekDay         -- get the weekday (0...6) 0=sunday
+--------------------------------------------------------
+
+
+ */
+
+namespace Director {
+
+const char *DateTimeXtra::xlibName = "DateTime";
+const XlibFileDesc DateTimeXtra::fileNames[] = {
+	{ "DATETIME",   nullptr },
+	{ nullptr,        nullptr },
+};
+
+static MethodProto xlibMethods[] = {
+
+
+	{ nullptr, nullptr, 0, 0, 0 }
+};
+
+static BuiltinProto xlibBuiltins[] = {
+	{ "GetSeconds", DateTimeXtra::m_GetSeconds, 0, 0, 500, HBLTIN },
+	{ "GetMinutes", DateTimeXtra::m_GetMinutes, 0, 0, 500, HBLTIN },
+	{ "GetHours", DateTimeXtra::m_GetHours, 0, 0, 500, HBLTIN },
+	{ "GetDay", DateTimeXtra::m_GetDay, 0, 0, 500, HBLTIN },
+	{ "GetMonth", DateTimeXtra::m_GetMonth, 0, 0, 500, HBLTIN },
+	{ "GetYear", DateTimeXtra::m_GetYear, 0, 0, 500, HBLTIN },
+	{ "GetWeekDay", DateTimeXtra::m_GetWeekDay, 0, 0, 500, HBLTIN },
+	{ nullptr, nullptr, 0, 0, 0, VOIDSYM }
+};
+
+DateTimeXtraObject::DateTimeXtraObject(ObjectType ObjectType) :Object<DateTimeXtraObject>("DateTime") {
+	_objType = ObjectType;
+}
+
+bool DateTimeXtraObject::hasProp(const Common::String &propName) {
+	return (propName == "name");
+}
+
+Datum DateTimeXtraObject::getProp(const Common::String &propName) {
+	if (propName == "name")
+		return Datum(DateTimeXtra::xlibName);
+	warning("DateTimeXtra::getProp: unknown property '%s'", propName.c_str());
+	return Datum();
+}
+
+void DateTimeXtra::open(ObjectType type, const Common::Path &path) {
+    DateTimeXtraObject::initMethods(xlibMethods);
+    DateTimeXtraObject *xobj = new DateTimeXtraObject(type);
+    if (type == kXtraObj) {
+        g_lingo->_openXtras.push_back(xlibName);
+		g_lingo->_openXtraObjects.push_back(xobj);
+	}
+    g_lingo->exposeXObject(xlibName, xobj);
+    g_lingo->initBuiltIns(xlibBuiltins);
+}
+
+void DateTimeXtra::close(ObjectType type) {
+    DateTimeXtraObject::cleanupMethods();
+    g_lingo->_globalvars[xlibName] = Datum();
+
+}
+
+void DateTimeXtra::m_new(int nargs) {
+	g_lingo->printSTUBWithArglist("DateTimeXtra::m_new", nargs);
+	g_lingo->dropStack(nargs);
+	g_lingo->push(g_lingo->_state->me);
+}
+
+void DateTimeXtra::m_GetSeconds(int nargs) {
+	g_lingo->dropStack(nargs);
+	TimeDate td;
+	g_system->getTimeAndDate(td);
+	g_lingo->push(Datum(td.tm_sec));
+}
+
+void DateTimeXtra::m_GetMinutes(int nargs) {
+	g_lingo->dropStack(nargs);
+	TimeDate td;
+	g_system->getTimeAndDate(td);
+	g_lingo->push(Datum(td.tm_min));
+}
+
+void DateTimeXtra::m_GetHours(int nargs) {
+	g_lingo->dropStack(nargs);
+	TimeDate td;
+	g_system->getTimeAndDate(td);
+	g_lingo->push(Datum(td.tm_hour));
+}
+
+void DateTimeXtra::m_GetDay(int nargs) {
+	g_lingo->dropStack(nargs);
+	TimeDate td;
+	g_system->getTimeAndDate(td);
+	g_lingo->push(Datum(td.tm_mday));
+}
+
+void DateTimeXtra::m_GetMonth(int nargs) {
+	g_lingo->dropStack(nargs);
+	TimeDate td;
+	g_system->getTimeAndDate(td);
+	g_lingo->push(Datum(td.tm_mon + 1));
+}
+
+void DateTimeXtra::m_GetYear(int nargs) {
+	g_lingo->dropStack(nargs);
+	TimeDate td;
+	g_system->getTimeAndDate(td);
+	g_lingo->push(Datum(td.tm_year + 1900));
+}
+
+void DateTimeXtra::m_GetWeekDay(int nargs) {
+	g_lingo->dropStack(nargs);
+	TimeDate td;
+	g_system->getTimeAndDate(td);
+	g_lingo->push(Datum(td.tm_wday));
+}
+
+}
diff --git a/engines/director/lingo/xtras/d/datetime.h b/engines/director/lingo/xtras/d/datetime.h
new file mode 100644
index 00000000000..3d06f046795
--- /dev/null
+++ b/engines/director/lingo/xtras/d/datetime.h
@@ -0,0 +1,56 @@
+/* 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 DIRECTOR_LINGO_XTRAS_D_DATETIME_H
+#define DIRECTOR_LINGO_XTRAS_D_DATETIME_H
+
+namespace Director {
+
+class DateTimeXtraObject : public Object<DateTimeXtraObject> {
+public:
+	DateTimeXtraObject(ObjectType objType);
+
+	bool hasProp(const Common::String &propName) override;
+	Datum getProp(const Common::String &propName) override;
+};
+
+namespace DateTimeXtra {
+
+extern const char *xlibName;
+extern const XlibFileDesc fileNames[];
+
+void open(ObjectType type, const Common::Path &path);
+void close(ObjectType type);
+
+void m_new(int nargs);
+void m_GetSeconds(int nargs);
+void m_GetMinutes(int nargs);
+void m_GetHours(int nargs);
+void m_GetDay(int nargs);
+void m_GetMonth(int nargs);
+void m_GetYear(int nargs);
+void m_GetWeekDay(int nargs);
+
+} // End of namespace DateTimeXtra
+
+} // End of namespace Director
+
+#endif
diff --git a/engines/director/module.mk b/engines/director/module.mk
index bc153d60146..c7d636fa74e 100644
--- a/engines/director/module.mk
+++ b/engines/director/module.mk
@@ -197,6 +197,7 @@ MODULE_OBJS = \
 	lingo/xtras/k/keypoll.o \
 	lingo/xtras/m/masterapp.o \
 	lingo/xtras/m/mui.o \
+	lingo/xtras/d/datetime.o \
 	lingo/xtras/o/openurl.o \
 	lingo/xtras/o/oscheck.o \
 	lingo/xtras/q/qtvrxtra.o \


Commit: 8d254fbbe51d82302be48b81fd95c8f1596b0bc3
    https://github.com/scummvm/scummvm/commit/8d254fbbe51d82302be48b81fd95c8f1596b0bc3
Author: Lariaa (30549703+Lariaa at users.noreply.github.com)
Date: 2026-06-22T00:47:24+02:00

Commit Message:
DIRECTOR: Stub NetLingo Xtra for the L??wenzahn, TKKG & Oscar series of games

Changed paths:
  A engines/director/lingo/xtras/lingo - Verkn??pfung.lnk
  A engines/director/lingo/xtras/n/netlingo.cpp
  A engines/director/lingo/xtras/n/netlingo.h
    engines/director/lingo/lingo-object.cpp
    engines/director/module.mk


diff --git a/engines/director/lingo/lingo-object.cpp b/engines/director/lingo/lingo-object.cpp
index a8231502aab..ae9ff02f3b1 100644
--- a/engines/director/lingo/lingo-object.cpp
+++ b/engines/director/lingo/lingo-object.cpp
@@ -166,6 +166,7 @@
 #include "director/lingo/xtras/k/keypoll.h"
 #include "director/lingo/xtras/m/masterapp.h"
 #include "director/lingo/xtras/m/mui.h"
+#include "director/lingo/xtras/n/netlingo.h"
 #include "director/lingo/xtras/o/openurl.h"
 #include "director/lingo/xtras/o/oscheck.h"
 #include "director/lingo/xtras/q/qtvrxtra.h"
@@ -342,6 +343,7 @@ static const struct XLibProto {
 	XLIBDEF(MuiXtra,			kXtraObj,					500),	// D5
 	XLIBDEF(MyFolderXObj,			kXObj,					400),	// D4
 	XLIBDEF(MystIsleXObj,		kXObj,			400),	// D4
+	XLIBDEF(NetLingoXtra,			kXtraObj,					500),	// D5
 	XLIBDEF(OSCheckXtra,		kXtraObj,		400),	// D4
 	XLIBDEF(OpenBleedWindowXCMD,kXObj,			300),	// D3
 	XLIBDEF(OpenURLXtra,		kXtraObj,		500),	// D5
diff --git "a/engines/director/lingo/xtras/lingo - Verkn\303\274pfung.lnk" "b/engines/director/lingo/xtras/lingo - Verkn\303\274pfung.lnk"
new file mode 100644
index 00000000000..ca42fcdf9f5
Binary files /dev/null and "b/engines/director/lingo/xtras/lingo - Verkn\303\274pfung.lnk" differ
diff --git a/engines/director/lingo/xtras/n/netlingo.cpp b/engines/director/lingo/xtras/n/netlingo.cpp
new file mode 100644
index 00000000000..f3f58b8a0c0
--- /dev/null
+++ b/engines/director/lingo/xtras/n/netlingo.cpp
@@ -0,0 +1,164 @@
+/* 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/system.h"
+
+#include "director/director.h"
+#include "director/lingo/lingo.h"
+#include "director/lingo/lingo-object.h"
+#include "director/lingo/lingo-utils.h"
+#include "director/lingo/xtras/n/netlingo.h"
+
+/**************************************************
+ *
+ * USED IN:
+ * Loewenzahn 1 / 2 / 3 / 4 / 5 / 6 / 7 / 8 / Adventskalender / Spielebox
+ * TKKG 4 / 7 / 8 / 9 / 10 / 11 / 13 / 14
+ * Oscar the Balloonist Discovers the Sea
+ *
+ **************************************************/
+
+/*
+-- xtra NetLingo 
+* netStatus * --  
+* getNetText  * -- 
+* gotoNetMovie * -- 
+* preloadNetThing  * -- 
+* netAbort * -- 
+* gotoNetPage * -- 
+* getLatestNetID * -- 
+* netError * -- 
+* netDone * -- 
+* netTextResult * -- 
+* netMime * -- 
+* netLastModDate * -- 
+* externalEvent * -- 
+* netPresent * -- 
+* downloadNetThing  * -- 
+* clearCache * -- 
+* cacheSize * -- 
+* cacheDocVerify * -- 
+* proxyServer * -- 
+* browserName * -- 
+* tellStreamStatus * -- 
+
+ */
+
+namespace Director {
+
+const char *NetLingoXtra::xlibName = "NetLingo";
+const XlibFileDesc NetLingoXtra::fileNames[] = {
+	{ "netlingo",   nullptr },
+	{ nullptr,        nullptr },
+};
+
+static MethodProto xlibMethods[] = {
+
+
+	{ nullptr, nullptr, 0, 0, 0 }
+};
+
+static BuiltinProto xlibBuiltins[] = {
+	{ "netStatus", NetLingoXtra::m_netStatus, -1, 0, 500, HBLTIN },
+	{ "getNetText", NetLingoXtra::m_getNetText, -1, 0, 500, HBLTIN },
+	{ "gotoNetMovie", NetLingoXtra::m_gotoNetMovie, -1, 0, 500, HBLTIN },
+	{ "preloadNetThing", NetLingoXtra::m_preloadNetThing, -1, 0, 500, HBLTIN },
+	{ "netAbort", NetLingoXtra::m_netAbort, -1, 0, 500, HBLTIN },
+	{ "gotoNetPage", NetLingoXtra::m_gotoNetPage, -1, 0, 500, HBLTIN },
+	{ "getLatestNetID", NetLingoXtra::m_getLatestNetID, -1, 0, 500, HBLTIN },
+	{ "netError", NetLingoXtra::m_netError, -1, 0, 500, HBLTIN },
+	{ "netDone", NetLingoXtra::m_netDone, -1, 0, 500, HBLTIN },
+	{ "netTextResult", NetLingoXtra::m_netTextResult, -1, 0, 500, HBLTIN },
+	{ "netMime", NetLingoXtra::m_netMime, -1, 0, 500, HBLTIN },
+	{ "netLastModDate", NetLingoXtra::m_netLastModDate, -1, 0, 500, HBLTIN },
+	{ "externalEvent", NetLingoXtra::m_externalEvent, -1, 0, 500, HBLTIN },
+	{ "netPresent", NetLingoXtra::m_netPresent, -1, 0, 500, HBLTIN },
+	{ "downloadNetThing", NetLingoXtra::m_downloadNetThing, -1, 0, 500, HBLTIN },
+	{ "clearCache", NetLingoXtra::m_clearCache, -1, 0, 500, HBLTIN },
+	{ "cacheSize", NetLingoXtra::m_cacheSize, -1, 0, 500, HBLTIN },
+	{ "cacheDocVerify", NetLingoXtra::m_cacheDocVerify, -1, 0, 500, HBLTIN },
+	{ "proxyServer", NetLingoXtra::m_proxyServer, -1, 0, 500, HBLTIN },
+	{ "browserName", NetLingoXtra::m_browserName, -1, 0, 500, HBLTIN },
+	{ "tellStreamStatus", NetLingoXtra::m_tellStreamStatus, -1, 0, 500, HBLTIN },
+	{ nullptr, nullptr, 0, 0, 0, VOIDSYM }
+};
+
+NetLingoXtraObject::NetLingoXtraObject(ObjectType ObjectType) :Object<NetLingoXtraObject>("NetLingo") {
+	_objType = ObjectType;
+}
+
+bool NetLingoXtraObject::hasProp(const Common::String &propName) {
+	return (propName == "name");
+}
+
+Datum NetLingoXtraObject::getProp(const Common::String &propName) {
+	if (propName == "name")
+		return Datum(NetLingoXtra::xlibName);
+	warning("NetLingoXtra::getProp: unknown property '%s'", propName.c_str());
+	return Datum();
+}
+
+void NetLingoXtra::open(ObjectType type, const Common::Path &path) {
+    NetLingoXtraObject::initMethods(xlibMethods);
+    NetLingoXtraObject *xobj = new NetLingoXtraObject(type);
+    if (type == kXtraObj) {
+        g_lingo->_openXtras.push_back(xlibName);
+		g_lingo->_openXtraObjects.push_back(xobj);
+	}
+    g_lingo->exposeXObject(xlibName, xobj);
+    g_lingo->initBuiltIns(xlibBuiltins);
+}
+
+void NetLingoXtra::close(ObjectType type) {
+    NetLingoXtraObject::cleanupMethods();
+    g_lingo->_globalvars[xlibName] = Datum();
+
+}
+
+void NetLingoXtra::m_new(int nargs) {
+	g_lingo->printSTUBWithArglist("NetLingoXtra::m_new", nargs);
+	g_lingo->dropStack(nargs);
+	g_lingo->push(g_lingo->_state->me);
+}
+
+XOBJSTUB(NetLingoXtra::m_netStatus, 0)
+XOBJSTUB(NetLingoXtra::m_getNetText, 0)
+XOBJSTUB(NetLingoXtra::m_gotoNetMovie, 0)
+XOBJSTUB(NetLingoXtra::m_preloadNetThing, 0)
+XOBJSTUB(NetLingoXtra::m_netAbort, 0)
+XOBJSTUB(NetLingoXtra::m_gotoNetPage, 0)
+XOBJSTUB(NetLingoXtra::m_getLatestNetID, 0)
+XOBJSTUB(NetLingoXtra::m_netError, "OK")
+XOBJSTUB(NetLingoXtra::m_netDone, 1)
+XOBJSTUB(NetLingoXtra::m_netTextResult, "")
+XOBJSTUB(NetLingoXtra::m_netMime, 0)
+XOBJSTUB(NetLingoXtra::m_netLastModDate, 0)
+XOBJSTUB(NetLingoXtra::m_externalEvent, 0)
+XOBJSTUB(NetLingoXtra::m_netPresent, 0)
+XOBJSTUB(NetLingoXtra::m_downloadNetThing, 0)
+XOBJSTUB(NetLingoXtra::m_clearCache, 0)
+XOBJSTUB(NetLingoXtra::m_cacheSize, 0)
+XOBJSTUB(NetLingoXtra::m_cacheDocVerify, 0)
+XOBJSTUB(NetLingoXtra::m_proxyServer, 0)
+XOBJSTUB(NetLingoXtra::m_browserName, "")
+XOBJSTUB(NetLingoXtra::m_tellStreamStatus, 0)
+
+}
diff --git a/engines/director/lingo/xtras/n/netlingo.h b/engines/director/lingo/xtras/n/netlingo.h
new file mode 100644
index 00000000000..3a51f952a5c
--- /dev/null
+++ b/engines/director/lingo/xtras/n/netlingo.h
@@ -0,0 +1,70 @@
+/* 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 DIRECTOR_LINGO_XTRAS_N_NETLINGO_H
+#define DIRECTOR_LINGO_XTRAS_N_NETLINGO_H
+
+namespace Director {
+
+class NetLingoXtraObject : public Object<NetLingoXtraObject> {
+public:
+	NetLingoXtraObject(ObjectType objType);
+
+	bool hasProp(const Common::String &propName) override;
+	Datum getProp(const Common::String &propName) override;
+};
+
+namespace NetLingoXtra {
+
+extern const char *xlibName;
+extern const XlibFileDesc fileNames[];
+
+void open(ObjectType type, const Common::Path &path);
+void close(ObjectType type);
+
+void m_new(int nargs);
+void m_netStatus(int nargs);
+void m_getNetText(int nargs);
+void m_gotoNetMovie(int nargs);
+void m_preloadNetThing(int nargs);
+void m_netAbort(int nargs);
+void m_gotoNetPage(int nargs);
+void m_getLatestNetID(int nargs);
+void m_netError(int nargs);
+void m_netDone(int nargs);
+void m_netTextResult(int nargs);
+void m_netMime(int nargs);
+void m_netLastModDate(int nargs);
+void m_externalEvent(int nargs);
+void m_netPresent(int nargs);
+void m_downloadNetThing(int nargs);
+void m_clearCache(int nargs);
+void m_cacheSize(int nargs);
+void m_cacheDocVerify(int nargs);
+void m_proxyServer(int nargs);
+void m_browserName(int nargs);
+void m_tellStreamStatus(int nargs);
+
+} // End of namespace NetLingoXtra
+
+} // End of namespace Director
+
+#endif
diff --git a/engines/director/module.mk b/engines/director/module.mk
index c7d636fa74e..cffea9f447b 100644
--- a/engines/director/module.mk
+++ b/engines/director/module.mk
@@ -198,6 +198,7 @@ MODULE_OBJS = \
 	lingo/xtras/m/masterapp.o \
 	lingo/xtras/m/mui.o \
 	lingo/xtras/d/datetime.o \
+	lingo/xtras/n/netlingo.o \
 	lingo/xtras/o/openurl.o \
 	lingo/xtras/o/oscheck.o \
 	lingo/xtras/q/qtvrxtra.o \


Commit: 9034771ccfb6d7675925f830d74c26f4ea62f399
    https://github.com/scummvm/scummvm/commit/9034771ccfb6d7675925f830d74c26f4ea62f399
Author: Lariaa (30549703+Lariaa at users.noreply.github.com)
Date: 2026-06-22T00:47:24+02:00

Commit Message:
DIRECTOR: Stub PaintX Xtra for L??wenzahn 2

Changed paths:
  A engines/director/lingo/xtras/p/paintx.cpp
  A engines/director/lingo/xtras/p/paintx.h
    engines/director/lingo/lingo-object.cpp
    engines/director/module.mk


diff --git a/engines/director/lingo/lingo-object.cpp b/engines/director/lingo/lingo-object.cpp
index ae9ff02f3b1..663560aa311 100644
--- a/engines/director/lingo/lingo-object.cpp
+++ b/engines/director/lingo/lingo-object.cpp
@@ -169,6 +169,7 @@
 #include "director/lingo/xtras/n/netlingo.h"
 #include "director/lingo/xtras/o/openurl.h"
 #include "director/lingo/xtras/o/oscheck.h"
+#include "director/lingo/xtras/p/paintx.h"
 #include "director/lingo/xtras/q/qtvrxtra.h"
 #include "director/lingo/xtras/r/registryreader.h"
 #include "director/lingo/xtras/r/rtk.h"
@@ -349,6 +350,7 @@ static const struct XLibProto {
 	XLIBDEF(OpenURLXtra,		kXtraObj,		500),	// D5
 	XLIBDEF(OrthoPlayXObj,		kXObj,			400),	// D4
 	XLIBDEF(PACoXObj,			kXObj,			300),	// D3
+	XLIBDEF(PaintXXtra,			kXtraObj,					500),	// D5
 	XLIBDEF(PalXObj,			kXObj,			400),	// D4
 	XLIBDEF(PanelXObj,			kXObj,			200),	// D2
 	XLIBDEF(PharaohsXObj,		kXObj,			400),	// D4
diff --git a/engines/director/lingo/xtras/p/paintx.cpp b/engines/director/lingo/xtras/p/paintx.cpp
new file mode 100644
index 00000000000..d3fbba2c8f6
--- /dev/null
+++ b/engines/director/lingo/xtras/p/paintx.cpp
@@ -0,0 +1,163 @@
+/* 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/system.h"
+
+#include "director/director.h"
+#include "director/lingo/lingo.h"
+#include "director/lingo/lingo-object.h"
+#include "director/lingo/lingo-utils.h"
+#include "director/lingo/xtras/p/paintx.h"
+
+/**************************************************
+ *
+ * USED IN:
+ * Loewenzahn 2
+ *
+ **************************************************/
+
+/*
+-- xtra PaintX    -- Version 1.0 (WIN/32 Bit)
+
+-- --------------------------------------------------------------------
+-- Painting Xtra
+-- --------------------------------------------------------------------
+
+-- ???1998 by Stephan Eichhorn, Scirius Development
+-- e-mail:  xtras at scririus.com
+-- WWW:     http://www.scirius.com
+
+-- special version for <Loewenzahn 2> only
+-- --------------------------------------------------------------------
+new object me                           -- create a new instance
+-- Methods --
+setRunMode object me, integer Mode      -- set the runMode (1,2,3)
+           -- 1: normal
+           -- 2: framed
+           -- 3: mirrored
+getRunMode object me                    -- returns the runMode
+setRunState object me, integer State    -- set the runState (0,1)
+           -- 0: stopped
+           -- 1: running
+getRunState object me                   -- returns the runState
+setData object me, string Data          -- set the session data
+getPos object me                        -- get position in data
+getDataSize object me                   -- get the size of data
+SetDrawRect object me, rect DrawRect    -- set the drawing rectangle
+SetClipRect object me, rect ClipRect    -- set the clipping rectangle
+Draw object me                          -- draw the next stroke
+StageToCast object me, rect CaptureRect, integer MemberNum  
+StageToBuffer object me, rect CaptureRect
+BufferToStage object me, rect DestRect
+setFile object me, string filePath      -- reads file as data
+Paint object me, integer Pensize, integer R, integer G, integer B, integer SL, integer ST
+
+ */
+
+namespace Director {
+
+const char *PaintXXtra::xlibName = "PaintX";
+const XlibFileDesc PaintXXtra::fileNames[] = {
+	{ "paintx",   nullptr },
+	{ nullptr,        nullptr },
+};
+
+static MethodProto xlibMethods[] = {
+	{ "new",				PaintXXtra::m_new,		 0, 0,	500 },
+	{ "setRunMode",				PaintXXtra::m_setRunMode,		 1, 1,	500 },
+	{ "getRunMode",				PaintXXtra::m_getRunMode,		 0, 0,	500 },
+	{ "setRunState",				PaintXXtra::m_setRunState,		 1, 1,	500 },
+	{ "getRunState",				PaintXXtra::m_getRunState,		 0, 0,	500 },
+	{ "setData",				PaintXXtra::m_setData,		 1, 1,	500 },
+	{ "getPos",				PaintXXtra::m_getPos,		 0, 0,	500 },
+	{ "getDataSize",				PaintXXtra::m_getDataSize,		 0, 0,	500 },
+	{ "SetDrawRect",				PaintXXtra::m_SetDrawRect,		 1, 1,	500 },
+	{ "SetClipRect",				PaintXXtra::m_SetClipRect,		 1, 1,	500 },
+	{ "Draw",				PaintXXtra::m_Draw,		 0, 0,	500 },
+	{ "StageToCast",				PaintXXtra::m_StageToCast,		 2, 2,	500 },
+	{ "StageToBuffer",				PaintXXtra::m_StageToBuffer,		 1, 1,	500 },
+	{ "BufferToStage",				PaintXXtra::m_BufferToStage,		 1, 1,	500 },
+	{ "setFile",				PaintXXtra::m_setFile,		 1, 1,	500 },
+	{ "Paint",				PaintXXtra::m_Paint,		 6, 6,	500 },
+
+	{ nullptr, nullptr, 0, 0, 0 }
+};
+
+static BuiltinProto xlibBuiltins[] = {
+
+	{ nullptr, nullptr, 0, 0, 0, VOIDSYM }
+};
+
+PaintXXtraObject::PaintXXtraObject(ObjectType ObjectType) :Object<PaintXXtraObject>("PaintX") {
+	_objType = ObjectType;
+}
+
+bool PaintXXtraObject::hasProp(const Common::String &propName) {
+	return (propName == "name");
+}
+
+Datum PaintXXtraObject::getProp(const Common::String &propName) {
+	if (propName == "name")
+		return Datum(PaintXXtra::xlibName);
+	warning("PaintXXtra::getProp: unknown property '%s'", propName.c_str());
+	return Datum();
+}
+
+void PaintXXtra::open(ObjectType type, const Common::Path &path) {
+    PaintXXtraObject::initMethods(xlibMethods);
+    PaintXXtraObject *xobj = new PaintXXtraObject(type);
+    if (type == kXtraObj) {
+        g_lingo->_openXtras.push_back(xlibName);
+		g_lingo->_openXtraObjects.push_back(xobj);
+	}
+    g_lingo->exposeXObject(xlibName, xobj);
+    g_lingo->initBuiltIns(xlibBuiltins);
+}
+
+void PaintXXtra::close(ObjectType type) {
+    PaintXXtraObject::cleanupMethods();
+    g_lingo->_globalvars[xlibName] = Datum();
+
+}
+
+void PaintXXtra::m_new(int nargs) {
+	g_lingo->printSTUBWithArglist("PaintXXtra::m_new", nargs);
+	g_lingo->dropStack(nargs);
+	g_lingo->push(g_lingo->_state->me);
+}
+
+XOBJSTUB(PaintXXtra::m_setRunMode, 0)
+XOBJSTUB(PaintXXtra::m_getRunMode, 0)
+XOBJSTUB(PaintXXtra::m_setRunState, 0)
+XOBJSTUB(PaintXXtra::m_getRunState, 0)
+XOBJSTUB(PaintXXtra::m_setData, 0)
+XOBJSTUB(PaintXXtra::m_getPos, 0)
+XOBJSTUB(PaintXXtra::m_getDataSize, 0)
+XOBJSTUB(PaintXXtra::m_SetDrawRect, 0)
+XOBJSTUB(PaintXXtra::m_SetClipRect, 0)
+XOBJSTUB(PaintXXtra::m_Draw, 0)
+XOBJSTUB(PaintXXtra::m_StageToCast, 0)
+XOBJSTUB(PaintXXtra::m_StageToBuffer, 0)
+XOBJSTUB(PaintXXtra::m_BufferToStage, 0)
+XOBJSTUB(PaintXXtra::m_setFile, 0)
+XOBJSTUB(PaintXXtra::m_Paint, 0)
+
+}
diff --git a/engines/director/lingo/xtras/p/paintx.h b/engines/director/lingo/xtras/p/paintx.h
new file mode 100644
index 00000000000..9e62bd0b7c8
--- /dev/null
+++ b/engines/director/lingo/xtras/p/paintx.h
@@ -0,0 +1,64 @@
+/* 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 DIRECTOR_LINGO_XTRAS_P_PAINTX_H
+#define DIRECTOR_LINGO_XTRAS_P_PAINTX_H
+
+namespace Director {
+
+class PaintXXtraObject : public Object<PaintXXtraObject> {
+public:
+	PaintXXtraObject(ObjectType objType);
+
+	bool hasProp(const Common::String &propName) override;
+	Datum getProp(const Common::String &propName) override;
+};
+
+namespace PaintXXtra {
+
+extern const char *xlibName;
+extern const XlibFileDesc fileNames[];
+
+void open(ObjectType type, const Common::Path &path);
+void close(ObjectType type);
+
+void m_new(int nargs);
+void m_setRunMode(int nargs);
+void m_getRunMode(int nargs);
+void m_setRunState(int nargs);
+void m_getRunState(int nargs);
+void m_setData(int nargs);
+void m_getPos(int nargs);
+void m_getDataSize(int nargs);
+void m_SetDrawRect(int nargs);
+void m_SetClipRect(int nargs);
+void m_Draw(int nargs);
+void m_StageToCast(int nargs);
+void m_StageToBuffer(int nargs);
+void m_BufferToStage(int nargs);
+void m_setFile(int nargs);
+void m_Paint(int nargs);
+
+} // End of namespace PaintXXtra
+
+} // End of namespace Director
+
+#endif
diff --git a/engines/director/module.mk b/engines/director/module.mk
index cffea9f447b..80ec539cc47 100644
--- a/engines/director/module.mk
+++ b/engines/director/module.mk
@@ -201,6 +201,7 @@ MODULE_OBJS = \
 	lingo/xtras/n/netlingo.o \
 	lingo/xtras/o/openurl.o \
 	lingo/xtras/o/oscheck.o \
+	lingo/xtras/p/paintx.o \
 	lingo/xtras/q/qtvrxtra.o \
 	lingo/xtras/r/registryreader.o \
 	lingo/xtras/r/rtk.o \


Commit: c79750df71330618eebb442ba992348ebd658ac6
    https://github.com/scummvm/scummvm/commit/c79750df71330618eebb442ba992348ebd658ac6
Author: Lariaa (30549703+Lariaa at users.noreply.github.com)
Date: 2026-06-22T00:47:24+02:00

Commit Message:
DIRECTOR: Stub SetMouse (SMXTRA) Xtra for L??wenzahn 2-4

Changed paths:
  A engines/director/lingo/xtras/s/setmouse.cpp
  A engines/director/lingo/xtras/s/setmouse.h
    engines/director/lingo/lingo-object.cpp
    engines/director/module.mk


diff --git a/engines/director/lingo/lingo-object.cpp b/engines/director/lingo/lingo-object.cpp
index 663560aa311..23290f1d8dc 100644
--- a/engines/director/lingo/lingo-object.cpp
+++ b/engines/director/lingo/lingo-object.cpp
@@ -181,6 +181,7 @@
 #include "director/lingo/xtras/s/staytoonedhigh.h"
 #include "director/lingo/xtras/s/staytoonedober.h"
 #include "director/lingo/xtras/s/staytoonedtoon.h"
+#include "director/lingo/xtras/s/setmouse.h"
 #include "director/lingo/xtras/t/timextra.h"
 #include "director/lingo/xtras/x/xsound.h"
 
@@ -375,6 +376,7 @@ static const struct XLibProto {
 	XLIBDEF(SaveNRestoreXObj,			kXObj,					400),	// D4
 	XLIBDEF(ScrnUtilXtra,		kXtraObj,		500),	// D5
 	XLIBDEF(SerialPortXObj,		kXObj,			200),	// D2
+	XLIBDEF(SetMouseXtra,			kXtraObj,					500),	// D5
 	XLIBDEF(SmackerXtra,			kXtraObj,					500),	// D5
 	XLIBDEF(SmallUtilXObj,		kXObj,			400),	// D4
 	XLIBDEF(SoundJam,			kXObj,			400),	// D4
diff --git a/engines/director/lingo/xtras/s/setmouse.cpp b/engines/director/lingo/xtras/s/setmouse.cpp
new file mode 100644
index 00000000000..987e5775514
--- /dev/null
+++ b/engines/director/lingo/xtras/s/setmouse.cpp
@@ -0,0 +1,125 @@
+/* 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/system.h"
+
+#include "director/director.h"
+#include "director/lingo/lingo.h"
+#include "director/lingo/lingo-object.h"
+#include "director/lingo/lingo-utils.h"
+#include "director/lingo/xtras/s/setmouse.h"
+
+/**************************************************
+ *
+ * USED IN:
+ * Loewenzahn 2 / 3
+ *
+ **************************************************/
+
+/*
+-- xtra SetMouseXtra               -- Version 1.0
+-- --------------------------------------------------------------------
+-- this Xtra allows to set the mouse position through Lingo
+-- --------------------------------------------------------------------
+
+-- ???1997 by Stephan Eichhorn, Scirius Multimedia
+-- e-mail:  xtras at scririus.com
+-- WWW:     http://www.scirius.com
+
+* SetMouse integer x,integer y     -- set the cursor to position x,y 
+-- 
+-- all x,y are in screen coordinates,
+-- not relative to the stage! 
+-- 
+
+ */
+
+namespace Director {
+
+const char *SetMouseXtra::xlibName = "SetMouse";
+const XlibFileDesc SetMouseXtra::fileNames[] = {
+	{ "SMXTRA",   nullptr },
+	{ nullptr,        nullptr },
+};
+
+static MethodProto xlibMethods[] = {
+
+
+	{ nullptr, nullptr, 0, 0, 0 }
+};
+
+static BuiltinProto xlibBuiltins[] = {
+	{ "SetMouse", SetMouseXtra::m_SetMouse, 2, 2, 500, HBLTIN },
+	{ nullptr, nullptr, 0, 0, 0, VOIDSYM }
+};
+
+SetMouseXtraObject::SetMouseXtraObject(ObjectType ObjectType) :Object<SetMouseXtraObject>("SetMouse") {
+	_objType = ObjectType;
+}
+
+bool SetMouseXtraObject::hasProp(const Common::String &propName) {
+	return (propName == "name");
+}
+
+Datum SetMouseXtraObject::getProp(const Common::String &propName) {
+	if (propName == "name")
+		return Datum(SetMouseXtra::xlibName);
+	warning("SetMouseXtra::getProp: unknown property '%s'", propName.c_str());
+	return Datum();
+}
+
+void SetMouseXtra::open(ObjectType type, const Common::Path &path) {
+    SetMouseXtraObject::initMethods(xlibMethods);
+    SetMouseXtraObject *xobj = new SetMouseXtraObject(type);
+    if (type == kXtraObj) {
+        g_lingo->_openXtras.push_back(xlibName);
+		g_lingo->_openXtraObjects.push_back(xobj);
+	}
+    g_lingo->exposeXObject(xlibName, xobj);
+    g_lingo->initBuiltIns(xlibBuiltins);
+}
+
+void SetMouseXtra::close(ObjectType type) {
+    SetMouseXtraObject::cleanupMethods();
+    g_lingo->_globalvars[xlibName] = Datum();
+
+}
+
+void SetMouseXtra::m_new(int nargs) {
+	g_lingo->printSTUBWithArglist("SetMouseXtra::m_new", nargs);
+	g_lingo->dropStack(nargs);
+	g_lingo->push(g_lingo->_state->me);
+}
+
+void SetMouseXtra::m_SetMouse(int nargs) {
+	if (nargs != 2) {
+		warning("SetMouseXtra::m_SetMouse: expected 2 arguments, got %d", nargs);
+		g_lingo->dropStack(nargs);
+		g_lingo->push(Datum(0));
+		return;
+	}
+	int y = g_lingo->pop().asInt();
+	int x = g_lingo->pop().asInt();
+	g_system->warpMouse(x, y);
+	g_lingo->push(Datum(0));
+}
+
+}
diff --git a/engines/director/lingo/xtras/s/setmouse.h b/engines/director/lingo/xtras/s/setmouse.h
new file mode 100644
index 00000000000..22daf7a6e11
--- /dev/null
+++ b/engines/director/lingo/xtras/s/setmouse.h
@@ -0,0 +1,50 @@
+/* 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 DIRECTOR_LINGO_XTRAS_S_SETMOUSE_H
+#define DIRECTOR_LINGO_XTRAS_S_SETMOUSE_H
+
+namespace Director {
+
+class SetMouseXtraObject : public Object<SetMouseXtraObject> {
+public:
+	SetMouseXtraObject(ObjectType objType);
+
+	bool hasProp(const Common::String &propName) override;
+	Datum getProp(const Common::String &propName) override;
+};
+
+namespace SetMouseXtra {
+
+extern const char *xlibName;
+extern const XlibFileDesc fileNames[];
+
+void open(ObjectType type, const Common::Path &path);
+void close(ObjectType type);
+
+void m_new(int nargs);
+void m_SetMouse(int nargs);
+
+} // End of namespace SetMouseXtra
+
+} // End of namespace Director
+
+#endif
diff --git a/engines/director/module.mk b/engines/director/module.mk
index 80ec539cc47..be7bbdbbf47 100644
--- a/engines/director/module.mk
+++ b/engines/director/module.mk
@@ -202,6 +202,7 @@ MODULE_OBJS = \
 	lingo/xtras/o/openurl.o \
 	lingo/xtras/o/oscheck.o \
 	lingo/xtras/p/paintx.o \
+	lingo/xtras/s/setmouse.o \
 	lingo/xtras/q/qtvrxtra.o \
 	lingo/xtras/r/registryreader.o \
 	lingo/xtras/r/rtk.o \


Commit: 846ab92664d9a96dd3f9599c370b8e81eb4c511f
    https://github.com/scummvm/scummvm/commit/846ab92664d9a96dd3f9599c370b8e81eb4c511f
Author: Lariaa (30549703+Lariaa at users.noreply.github.com)
Date: 2026-06-22T00:47:24+02:00

Commit Message:
DIRECTOR: LINGO: Implement FileXtra4

FileXtra4 is used by TKKG 11/13/14, the Loewenzahn 1 D10 reissue
and Oscar Mountain for their cd-checks.

Changed paths:
  A engines/director/lingo/xtras/f/filextra4.cpp
  A engines/director/lingo/xtras/f/filextra4.h
    engines/director/lingo/lingo-object.cpp
    engines/director/module.mk


diff --git a/engines/director/lingo/lingo-object.cpp b/engines/director/lingo/lingo-object.cpp
index 23290f1d8dc..50c5803071b 100644
--- a/engines/director/lingo/lingo-object.cpp
+++ b/engines/director/lingo/lingo-object.cpp
@@ -162,6 +162,7 @@
 #include "director/lingo/xtras/d/displayres.h"
 #include "director/lingo/xtras/d/datetime.h"
 #include "director/lingo/xtras/f/filextra.h"
+#include "director/lingo/xtras/f/filextra4.h"
 #include "director/lingo/xtras/g/getdir.h"
 #include "director/lingo/xtras/k/keypoll.h"
 #include "director/lingo/xtras/m/masterapp.h"
@@ -300,6 +301,7 @@ static const struct XLibProto {
 	XLIBDEF(FileExists,			kXObj,			300),	// D3
 	XLIBDEF(FileIO,				kXObj | kXtraObj,200),	// D2
 	XLIBDEF(FileXtra,			kXtraObj,		500),	// D5
+	XLIBDEF(FileXtra4Xtra,			kXtraObj,					500),	// D5
 	XLIBDEF(FindFolder,			kXObj,			300),	// D3
 	XLIBDEF(FindSys,			kXObj,			400),	// D4
 	XLIBDEF(FindWin,			kXObj,			400),	// D4
diff --git a/engines/director/lingo/xtras/f/filextra4.cpp b/engines/director/lingo/xtras/f/filextra4.cpp
new file mode 100644
index 00000000000..b6a94cecb5b
--- /dev/null
+++ b/engines/director/lingo/xtras/f/filextra4.cpp
@@ -0,0 +1,342 @@
+/* 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/system.h"
+#include "common/fs.h"
+
+#include "director/director.h"
+#include "director/util.h"
+#include "director/lingo/lingo.h"
+#include "director/lingo/lingo-object.h"
+#include "director/lingo/lingo-utils.h"
+#include "director/lingo/xtras/f/filextra4.h"
+
+/**************************************************
+ *
+ * USED IN:
+ * Loewenzahn 1 (D10, v2.0)
+ * TKKG 11 / 13 / 14
+ * Oscar the Balloonist Flies into the Mountains
+ *
+ **************************************************/
+
+/*
+-- xtra FileXtra4
+new object me
+fx_GetVersion object me
+fx_FileOpenDialog object me, string initialFolder, string filtStr, string dlogTitle, Boolean createPrompt, Boolean fileMustExist
+fx_FileSaveAsDialog object me, string initialFolder, string filename, string dlogTitle, Boolean overwritePrompt
+fx_FileExists object me, string fileName
+fx_FileIsLink object me, string fileName
+fx_FileRename object me, string oldName, string newName
+fx_FileDelete object me, string fileName
+fx_FileRecycle object me, string fileName
+fx_FileCopy object me, string fromFName, string toFName
+fx_FileMove object me, string fromFName, string toFName
+fx_FileGetWriteState object me, string fileName
+fx_FileSetWriteState object me, string fileName, Boolean writeable
+fx_FileGetModDate object me, string fileName
+fx_FileGetModNumber object me, string fileName
+fx_FileGetSize object me, string fileName
+fx_FileGetType object me, string fileName
+fx_FileSetType object me, string fileName, string fileType
+fx_FileCompare object me, string filename1, string filename2
+fx_FileOpenDocument object me, string filename
+fx_FilePrintDocument object me, string filename
+fx_FileGetAppPath object me, string fileType
+fx_FileRunApp object me, string commandLine
+fx_LinkCreate object me, string filename, string destPath
+fx_LinkResolve object me, string filename
+fx_FolderSelectDialog object me, string infoString
+fx_FolderGetSpecialPath object me, string folderType
+fx_FolderExists object me, string folderName
+fx_FolderCreate object me, string folderName
+fx_FolderRename object me, string oldFolderName, string newFolderName
+fx_FolderDelete object me, string folderName, Boolean recursive
+fx_FolderRecycle object me, string folderName
+fx_FolderCopy object me, string fromFolderName, string toFolderName, Boolean recursive
+fx_FolderMove object me, string fromFolderName, string toFolderName
+fx_FolderGetWriteState object me, string folderName
+fx_FolderSetWriteState object me, string folderName, Boolean writeable, Boolean recursive
+fx_FolderSyncOneWay object me, string fromFolderName, string toFolderName, Boolean recursive, Boolean deleteStrays
+fx_FolderSyncBothWays object me, string fromFolderName, string toFolderName, Boolean recursive
+fx_FolderToList object me, string folderName
+fx_VolumeSelectDialog object me, string infoString
+fx_VolumeExists object me, string volumeName
+fx_VolumeGetFreeBytes object me, string volumeName
+fx_VolumeGetTotalBytes object me, string volumeName
+fx_VolumeIsCDROM object me, string volumeName
+fx_VolumeIsRemovable object me, string volumeName
+fx_VolumeEject object me, string volumeName
+fx_VolumesToList object me
+fx_ErrorNumber object me
+fx_ErrorString object me
+
+ */
+
+namespace Director {
+
+const char *FileXtra4Xtra::xlibName = "FileXtra4";
+const XlibFileDesc FileXtra4Xtra::fileNames[] = {
+	{ "filextra4",   nullptr },
+	{ nullptr,        nullptr },
+};
+
+static MethodProto xlibMethods[] = {
+	{ "new",				FileXtra4Xtra::m_new,		 0, 0,	500 },
+	{ "fx_GetVersion",				FileXtra4Xtra::m_fx_GetVersion,		 0, 0,	500 },
+	{ "fx_FileOpenDialog",				FileXtra4Xtra::m_fx_FileOpenDialog,		 5, 5,	500 },
+	{ "fx_FileSaveAsDialog",				FileXtra4Xtra::m_fx_FileSaveAsDialog,		 4, 4,	500 },
+	{ "fx_FileExists",				FileXtra4Xtra::m_fx_FileExists,		 1, 1,	500 },
+	{ "fx_FileIsLink",				FileXtra4Xtra::m_fx_FileIsLink,		 1, 1,	500 },
+	{ "fx_FileRename",				FileXtra4Xtra::m_fx_FileRename,		 2, 2,	500 },
+	{ "fx_FileDelete",				FileXtra4Xtra::m_fx_FileDelete,		 1, 1,	500 },
+	{ "fx_FileRecycle",				FileXtra4Xtra::m_fx_FileRecycle,		 1, 1,	500 },
+	{ "fx_FileCopy",				FileXtra4Xtra::m_fx_FileCopy,		 2, 2,	500 },
+	{ "fx_FileMove",				FileXtra4Xtra::m_fx_FileMove,		 2, 2,	500 },
+	{ "fx_FileGetWriteState",				FileXtra4Xtra::m_fx_FileGetWriteState,		 1, 1,	500 },
+	{ "fx_FileSetWriteState",				FileXtra4Xtra::m_fx_FileSetWriteState,		 2, 2,	500 },
+	{ "fx_FileGetModDate",				FileXtra4Xtra::m_fx_FileGetModDate,		 1, 1,	500 },
+	{ "fx_FileGetModNumber",				FileXtra4Xtra::m_fx_FileGetModNumber,		 1, 1,	500 },
+	{ "fx_FileGetSize",				FileXtra4Xtra::m_fx_FileGetSize,		 1, 1,	500 },
+	{ "fx_FileGetType",				FileXtra4Xtra::m_fx_FileGetType,		 1, 1,	500 },
+	{ "fx_FileSetType",				FileXtra4Xtra::m_fx_FileSetType,		 2, 2,	500 },
+	{ "fx_FileCompare",				FileXtra4Xtra::m_fx_FileCompare,		 2, 2,	500 },
+	{ "fx_FileOpenDocument",				FileXtra4Xtra::m_fx_FileOpenDocument,		 1, 1,	500 },
+	{ "fx_FilePrintDocument",				FileXtra4Xtra::m_fx_FilePrintDocument,		 1, 1,	500 },
+	{ "fx_FileGetAppPath",				FileXtra4Xtra::m_fx_FileGetAppPath,		 1, 1,	500 },
+	{ "fx_FileRunApp",				FileXtra4Xtra::m_fx_FileRunApp,		 1, 1,	500 },
+	{ "fx_LinkCreate",				FileXtra4Xtra::m_fx_LinkCreate,		 2, 2,	500 },
+	{ "fx_LinkResolve",				FileXtra4Xtra::m_fx_LinkResolve,		 1, 1,	500 },
+	{ "fx_FolderSelectDialog",				FileXtra4Xtra::m_fx_FolderSelectDialog,		 1, 1,	500 },
+	{ "fx_FolderGetSpecialPath",				FileXtra4Xtra::m_fx_FolderGetSpecialPath,		 1, 1,	500 },
+	{ "fx_FolderExists",				FileXtra4Xtra::m_fx_FolderExists,		 1, 1,	500 },
+	{ "fx_FolderCreate",				FileXtra4Xtra::m_fx_FolderCreate,		 1, 1,	500 },
+	{ "fx_FolderRename",				FileXtra4Xtra::m_fx_FolderRename,		 2, 2,	500 },
+	{ "fx_FolderDelete",				FileXtra4Xtra::m_fx_FolderDelete,		 2, 2,	500 },
+	{ "fx_FolderRecycle",				FileXtra4Xtra::m_fx_FolderRecycle,		 1, 1,	500 },
+	{ "fx_FolderCopy",				FileXtra4Xtra::m_fx_FolderCopy,		 3, 3,	500 },
+	{ "fx_FolderMove",				FileXtra4Xtra::m_fx_FolderMove,		 2, 2,	500 },
+	{ "fx_FolderGetWriteState",				FileXtra4Xtra::m_fx_FolderGetWriteState,		 1, 1,	500 },
+	{ "fx_FolderSetWriteState",				FileXtra4Xtra::m_fx_FolderSetWriteState,		 3, 3,	500 },
+	{ "fx_FolderSyncOneWay",				FileXtra4Xtra::m_fx_FolderSyncOneWay,		 4, 4,	500 },
+	{ "fx_FolderSyncBothWays",				FileXtra4Xtra::m_fx_FolderSyncBothWays,		 3, 3,	500 },
+	{ "fx_FolderToList",				FileXtra4Xtra::m_fx_FolderToList,		 1, 1,	500 },
+	{ "fx_VolumeSelectDialog",				FileXtra4Xtra::m_fx_VolumeSelectDialog,		 1, 1,	500 },
+	{ "fx_VolumeExists",				FileXtra4Xtra::m_fx_VolumeExists,		 1, 1,	500 },
+	{ "fx_VolumeGetFreeBytes",				FileXtra4Xtra::m_fx_VolumeGetFreeBytes,		 1, 1,	500 },
+	{ "fx_VolumeGetTotalBytes",				FileXtra4Xtra::m_fx_VolumeGetTotalBytes,		 1, 1,	500 },
+	{ "fx_VolumeIsCDROM",				FileXtra4Xtra::m_fx_VolumeIsCDROM,		 1, 1,	500 },
+	{ "fx_VolumeIsRemovable",				FileXtra4Xtra::m_fx_VolumeIsRemovable,		 1, 1,	500 },
+	{ "fx_VolumeEject",				FileXtra4Xtra::m_fx_VolumeEject,		 1, 1,	500 },
+	{ "fx_VolumesToList",				FileXtra4Xtra::m_fx_VolumesToList,		 0, 0,	500 },
+	{ "fx_ErrorNumber",				FileXtra4Xtra::m_fx_ErrorNumber,		 0, 0,	500 },
+	{ "fx_ErrorString",				FileXtra4Xtra::m_fx_ErrorString,		 0, 0,	500 },
+
+	{ nullptr, nullptr, 0, 0, 0 }
+};
+
+static BuiltinProto xlibBuiltins[] = {
+
+	{ nullptr, nullptr, 0, 0, 0, VOIDSYM }
+};
+
+FileXtra4XtraObject::FileXtra4XtraObject(ObjectType ObjectType) :Object<FileXtra4XtraObject>("FileXtra4") {
+	_objType = ObjectType;
+}
+
+bool FileXtra4XtraObject::hasProp(const Common::String &propName) {
+	return (propName == "name");
+}
+
+Datum FileXtra4XtraObject::getProp(const Common::String &propName) {
+	if (propName == "name")
+		return Datum(FileXtra4Xtra::xlibName);
+	warning("FileXtra4Xtra::getProp: unknown property '%s'", propName.c_str());
+	return Datum();
+}
+
+void FileXtra4Xtra::open(ObjectType type, const Common::Path &path) {
+    FileXtra4XtraObject::initMethods(xlibMethods);
+    FileXtra4XtraObject *xobj = new FileXtra4XtraObject(type);
+    if (type == kXtraObj) {
+        g_lingo->_openXtras.push_back(xlibName);
+		g_lingo->_openXtraObjects.push_back(xobj);
+	}
+    g_lingo->exposeXObject(xlibName, xobj);
+    g_lingo->initBuiltIns(xlibBuiltins);
+}
+
+void FileXtra4Xtra::close(ObjectType type) {
+    FileXtra4XtraObject::cleanupMethods();
+    g_lingo->_globalvars[xlibName] = Datum();
+
+}
+
+void FileXtra4Xtra::m_new(int nargs) {
+	g_lingo->printSTUBWithArglist("FileXtra4Xtra::m_new", nargs);
+	g_lingo->dropStack(nargs);
+	g_lingo->push(g_lingo->_state->me);
+}
+
+static Common::String fx4FirstArg(int nargs) {
+	Common::String first;
+	for (int i = 0; i < nargs; i++) {
+		Datum d = g_lingo->pop();
+		if (i == nargs - 1)
+			first = d.asString();
+	}
+	return first;
+}
+
+static Common::Path fx4ResolveAbs(const Common::String &raw, bool directory) {
+	Common::Path rel = findPath(raw, true, true, directory);
+	if (rel.empty())
+		return Common::Path();
+	Common::Path abs = Common::Path(g_director->getGameDataDir()->getPath());
+	abs.appendInPlace(Common::String(g_director->_dirSeparator), g_director->_dirSeparator);
+	abs.appendInPlace(rel);
+	return abs;
+}
+
+void FileXtra4Xtra::m_fx_GetVersion(int nargs) {
+	g_lingo->dropStack(nargs);
+	g_lingo->push(Datum(Common::String("FileXtra 4.0.2 (ScummVM)")));
+}
+XOBJSTUB(FileXtra4Xtra::m_fx_FileOpenDialog, 0)
+XOBJSTUB(FileXtra4Xtra::m_fx_FileSaveAsDialog, 0)
+void FileXtra4Xtra::m_fx_FileExists(int nargs) {
+	Common::String name = fx4FirstArg(nargs);
+	g_lingo->push(Datum(findPath(name, true, true, false).empty() ? 0 : 1));
+}
+XOBJSTUB(FileXtra4Xtra::m_fx_FileIsLink, 0)
+XOBJSTUB(FileXtra4Xtra::m_fx_FileRename, 1)
+XOBJSTUB(FileXtra4Xtra::m_fx_FileDelete, 0)
+XOBJSTUB(FileXtra4Xtra::m_fx_FileRecycle, 0)
+XOBJSTUB(FileXtra4Xtra::m_fx_FileCopy, 0)
+XOBJSTUB(FileXtra4Xtra::m_fx_FileMove, 0)
+XOBJSTUB(FileXtra4Xtra::m_fx_FileGetWriteState, 0)
+XOBJSTUB(FileXtra4Xtra::m_fx_FileSetWriteState, 0)
+XOBJSTUB(FileXtra4Xtra::m_fx_FileGetModDate, 0)
+XOBJSTUB(FileXtra4Xtra::m_fx_FileGetModNumber, 0)
+XOBJSTUB(FileXtra4Xtra::m_fx_FileGetSize, 0)
+void FileXtra4Xtra::m_fx_FileGetType(int nargs) {
+	Common::String name = fx4FirstArg(nargs);
+	Common::String ext;
+	for (int i = (int)name.size() - 1; i >= 0; i--) {
+		char c = name[i];
+		if (c == '\\' || c == '/' || c == ':')
+			break;
+		if (c == '.') {
+			ext = Common::String(name.c_str() + i);
+			break;
+		}
+	}
+	g_lingo->push(Datum(ext));
+}
+XOBJSTUB(FileXtra4Xtra::m_fx_FileSetType, 0)
+XOBJSTUB(FileXtra4Xtra::m_fx_FileCompare, 0)
+XOBJSTUB(FileXtra4Xtra::m_fx_FileOpenDocument, 0)
+XOBJSTUB(FileXtra4Xtra::m_fx_FilePrintDocument, 0)
+XOBJSTUB(FileXtra4Xtra::m_fx_FileGetAppPath, 0)
+XOBJSTUB(FileXtra4Xtra::m_fx_FileRunApp, 0)
+XOBJSTUB(FileXtra4Xtra::m_fx_LinkCreate, 0)
+XOBJSTUB(FileXtra4Xtra::m_fx_LinkResolve, 0)
+XOBJSTUB(FileXtra4Xtra::m_fx_FolderSelectDialog, 0)
+void FileXtra4Xtra::m_fx_FolderGetSpecialPath(int nargs) {
+	g_lingo->dropStack(nargs);
+	Common::Path base = g_director->getGameDataDir()->getPath();
+	Common::String path = base.toString(g_director->_dirSeparator);
+	if (!path.empty() && path.lastChar() != g_director->_dirSeparator)
+		path += g_director->_dirSeparator;
+	g_lingo->push(Datum(path));
+}
+void FileXtra4Xtra::m_fx_FolderExists(int nargs) {
+	Common::String name = fx4FirstArg(nargs);
+	g_lingo->push(Datum(findPath(name, true, true, true).empty() ? 0 : 1));
+}
+XOBJSTUB(FileXtra4Xtra::m_fx_FolderCreate, 1)
+XOBJSTUB(FileXtra4Xtra::m_fx_FolderRename, 0)
+XOBJSTUB(FileXtra4Xtra::m_fx_FolderDelete, 0)
+XOBJSTUB(FileXtra4Xtra::m_fx_FolderRecycle, 0)
+XOBJSTUB(FileXtra4Xtra::m_fx_FolderCopy, 0)
+XOBJSTUB(FileXtra4Xtra::m_fx_FolderMove, 0)
+XOBJSTUB(FileXtra4Xtra::m_fx_FolderGetWriteState, 0)
+XOBJSTUB(FileXtra4Xtra::m_fx_FolderSetWriteState, 0)
+XOBJSTUB(FileXtra4Xtra::m_fx_FolderSyncOneWay, 0)
+XOBJSTUB(FileXtra4Xtra::m_fx_FolderSyncBothWays, 0)
+void FileXtra4Xtra::m_fx_FolderToList(int nargs) {
+	Common::String name = fx4FirstArg(nargs);
+
+	Datum result;
+	result.type = ARRAY;
+	result.u.farr = new FArray();
+
+	Common::Path dirPath = fx4ResolveAbs(name, true);
+	if (!dirPath.empty()) {
+		Common::FSNode dir(dirPath);
+		Common::FSList fslist;
+		if (dir.isDirectory() && dir.getChildren(fslist, Common::FSNode::kListAll)) {
+			for (auto &node : fslist) {
+				Common::String childName = node.getName();
+				if (node.isDirectory())
+					childName += g_director->_dirSeparator;
+				result.u.farr->arr.push_back(Datum(childName));
+			}
+		}
+	}
+	g_lingo->push(result);
+}
+XOBJSTUB(FileXtra4Xtra::m_fx_VolumeSelectDialog, 0)
+void FileXtra4Xtra::m_fx_VolumeExists(int nargs) {
+	g_lingo->dropStack(nargs);
+	g_lingo->push(Datum(1));
+}
+XOBJSTUB(FileXtra4Xtra::m_fx_VolumeGetFreeBytes, 0)
+XOBJSTUB(FileXtra4Xtra::m_fx_VolumeGetTotalBytes, 0)
+void FileXtra4Xtra::m_fx_VolumeIsCDROM(int nargs) {
+	g_lingo->dropStack(nargs);
+	g_lingo->push(Datum(0));
+}
+void FileXtra4Xtra::m_fx_VolumeIsRemovable(int nargs) {
+	g_lingo->dropStack(nargs);
+	g_lingo->push(Datum(1));
+}
+XOBJSTUB(FileXtra4Xtra::m_fx_VolumeEject, 0)
+void FileXtra4Xtra::m_fx_VolumesToList(int nargs) {
+	g_lingo->dropStack(nargs);
+	Datum result;
+	result.type = ARRAY;
+	result.u.farr = new FArray();
+	result.u.farr->arr.push_back(Datum(Common::String("c:\\")));
+	g_lingo->push(result);
+}
+void FileXtra4Xtra::m_fx_ErrorNumber(int nargs) {
+	g_lingo->dropStack(nargs);
+	g_lingo->push(Datum(0));
+}
+
+void FileXtra4Xtra::m_fx_ErrorString(int nargs) {
+	g_lingo->dropStack(nargs);
+	g_lingo->push(Datum(Common::String("")));
+}
+
+}
diff --git a/engines/director/lingo/xtras/f/filextra4.h b/engines/director/lingo/xtras/f/filextra4.h
new file mode 100644
index 00000000000..eee09832b52
--- /dev/null
+++ b/engines/director/lingo/xtras/f/filextra4.h
@@ -0,0 +1,97 @@
+/* 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 DIRECTOR_LINGO_XTRAS_F_FILEXTRA4_H
+#define DIRECTOR_LINGO_XTRAS_F_FILEXTRA4_H
+
+namespace Director {
+
+class FileXtra4XtraObject : public Object<FileXtra4XtraObject> {
+public:
+	FileXtra4XtraObject(ObjectType objType);
+
+	bool hasProp(const Common::String &propName) override;
+	Datum getProp(const Common::String &propName) override;
+};
+
+namespace FileXtra4Xtra {
+
+extern const char *xlibName;
+extern const XlibFileDesc fileNames[];
+
+void open(ObjectType type, const Common::Path &path);
+void close(ObjectType type);
+
+void m_new(int nargs);
+void m_fx_GetVersion(int nargs);
+void m_fx_FileOpenDialog(int nargs);
+void m_fx_FileSaveAsDialog(int nargs);
+void m_fx_FileExists(int nargs);
+void m_fx_FileIsLink(int nargs);
+void m_fx_FileRename(int nargs);
+void m_fx_FileDelete(int nargs);
+void m_fx_FileRecycle(int nargs);
+void m_fx_FileCopy(int nargs);
+void m_fx_FileMove(int nargs);
+void m_fx_FileGetWriteState(int nargs);
+void m_fx_FileSetWriteState(int nargs);
+void m_fx_FileGetModDate(int nargs);
+void m_fx_FileGetModNumber(int nargs);
+void m_fx_FileGetSize(int nargs);
+void m_fx_FileGetType(int nargs);
+void m_fx_FileSetType(int nargs);
+void m_fx_FileCompare(int nargs);
+void m_fx_FileOpenDocument(int nargs);
+void m_fx_FilePrintDocument(int nargs);
+void m_fx_FileGetAppPath(int nargs);
+void m_fx_FileRunApp(int nargs);
+void m_fx_LinkCreate(int nargs);
+void m_fx_LinkResolve(int nargs);
+void m_fx_FolderSelectDialog(int nargs);
+void m_fx_FolderGetSpecialPath(int nargs);
+void m_fx_FolderExists(int nargs);
+void m_fx_FolderCreate(int nargs);
+void m_fx_FolderRename(int nargs);
+void m_fx_FolderDelete(int nargs);
+void m_fx_FolderRecycle(int nargs);
+void m_fx_FolderCopy(int nargs);
+void m_fx_FolderMove(int nargs);
+void m_fx_FolderGetWriteState(int nargs);
+void m_fx_FolderSetWriteState(int nargs);
+void m_fx_FolderSyncOneWay(int nargs);
+void m_fx_FolderSyncBothWays(int nargs);
+void m_fx_FolderToList(int nargs);
+void m_fx_VolumeSelectDialog(int nargs);
+void m_fx_VolumeExists(int nargs);
+void m_fx_VolumeGetFreeBytes(int nargs);
+void m_fx_VolumeGetTotalBytes(int nargs);
+void m_fx_VolumeIsCDROM(int nargs);
+void m_fx_VolumeIsRemovable(int nargs);
+void m_fx_VolumeEject(int nargs);
+void m_fx_VolumesToList(int nargs);
+void m_fx_ErrorNumber(int nargs);
+void m_fx_ErrorString(int nargs);
+
+} // End of namespace FileXtra4Xtra
+
+} // End of namespace Director
+
+#endif
diff --git a/engines/director/module.mk b/engines/director/module.mk
index be7bbdbbf47..f2ce92bd4d3 100644
--- a/engines/director/module.mk
+++ b/engines/director/module.mk
@@ -199,6 +199,7 @@ MODULE_OBJS = \
 	lingo/xtras/m/mui.o \
 	lingo/xtras/d/datetime.o \
 	lingo/xtras/n/netlingo.o \
+	lingo/xtras/f/filextra4.o \
 	lingo/xtras/o/openurl.o \
 	lingo/xtras/o/oscheck.o \
 	lingo/xtras/p/paintx.o \


Commit: d535c2f9a74d112f0d8ab8ea603641e3f2ba80c1
    https://github.com/scummvm/scummvm/commit/d535c2f9a74d112f0d8ab8ea603641e3f2ba80c1
Author: Lariaa (30549703+Lariaa at users.noreply.github.com)
Date: 2026-06-22T00:47:24+02:00

Commit Message:
DIRECTOR: LINGO: Implement FileXtra DirectoryExists and DirectoryToList

DirectoryExists returns 0 if the directory exists and a negative error code
otherwise, matching the original FileXtra convention. DirectoryToList returns
a Lingo list of the entries in a directory or VOID for an invalid directory.

Changed paths:
    engines/director/lingo/xtras/f/filextra.cpp


diff --git a/engines/director/lingo/xtras/f/filextra.cpp b/engines/director/lingo/xtras/f/filextra.cpp
index 2531ea12a89..15ebd234e40 100644
--- a/engines/director/lingo/xtras/f/filextra.cpp
+++ b/engines/director/lingo/xtras/f/filextra.cpp
@@ -20,8 +20,10 @@
  */
 
 #include "common/system.h"
+#include "common/fs.h"
 
 #include "director/director.h"
+#include "director/util.h"
 #include "director/lingo/lingo.h"
 #include "director/lingo/lingo-object.h"
 #include "director/lingo/lingo-utils.h"
@@ -153,12 +155,57 @@ XOBJSTUB(FileXtra::m_RenameFile, 0)
 XOBJSTUB(FileXtra::m_DeleteFile, 0)
 XOBJSTUB(FileXtra::m_CopyFile, 0)
 XOBJSTUB(FileXtra::m_GetFileModDate, 0)
-XOBJSTUB(FileXtra::m_DirectoryExists, 0)
+// DirectoryExists string dirName
+// FileXtra convention: returns 0 if the directory exists, a negative error
+// code otherwise.
+void FileXtra::m_DirectoryExists(int nargs) {
+	ARGNUMCHECK(1)
+	Common::String dirName = g_lingo->pop().asString();
+
+	Common::Path path = findPath(dirName, true, true, true);
+	g_lingo->push(Datum(path.empty() ? -1 : 0));
+}
+
+// DirectoryToList string dirName
+// Returns a Lingo list of the files and folders in dirName. Folder names are
+// suffixed with the platform path delimiter. Returns VOID if dirName is not a
+// valid directory (matching the original FileXtra behaviour).
+void FileXtra::m_DirectoryToList(int nargs) {
+	ARGNUMCHECK(1)
+	Common::String dirName = g_lingo->pop().asString();
+
+	Datum result;
+
+	Common::Path path = findPath(dirName, true, true, true);
+	if (path.empty()) {
+		warning("FileXtra::m_DirectoryToList(): directory not found: %s", dirName.c_str());
+		g_lingo->push(result);
+		return;
+	}
+
+	Common::FSNode dir(path);
+	Common::FSList fslist;
+	if (!dir.isDirectory() || !dir.getChildren(fslist, Common::FSNode::kListAll)) {
+		g_lingo->push(result);
+		return;
+	}
+
+	result.type = ARRAY;
+	result.u.farr = new FArray();
+	for (auto &node : fslist) {
+		Common::String name = node.getName();
+		if (node.isDirectory())
+			name += g_director->_dirSeparator;
+		result.u.farr->arr.push_back(Datum(name));
+	}
+
+	g_lingo->push(result);
+}
+
 XOBJSTUB(FileXtra::m_CreateDirectory, 0)
 XOBJSTUB(FileXtra::m_DeleteDirectory, 0)
 XOBJSTUB(FileXtra::m_XDeleteDirectory, 0)
 XOBJSTUB(FileXtra::m_CopyDirectory, 0)
 XOBJSTUB(FileXtra::m_XCopyDirectory, 0)
-XOBJSTUB(FileXtra::m_DirectoryToList, 0)
 
 }


Commit: 4bb528d1a6d63a0ce311cecd2ad68e0871b8bae0
    https://github.com/scummvm/scummvm/commit/4bb528d1a6d63a0ce311cecd2ad68e0871b8bae0
Author: Lariaa (30549703+Lariaa at users.noreply.github.com)
Date: 2026-06-22T00:47:24+02:00

Commit Message:
DIRECTOR: Load Xtras from nested folders

Director treats the Xtras folder as the one next to the application. For titles
whose projector is nested below the game root also match an "xtras"
folder at any depth.

Changed paths:
    engines/director/resource.cpp


diff --git a/engines/director/resource.cpp b/engines/director/resource.cpp
index ca9e4961ffb..89ad17e2c71 100644
--- a/engines/director/resource.cpp
+++ b/engines/director/resource.cpp
@@ -605,6 +605,7 @@ void Window::loadXtrasFromPath() {
 	// and they'll still be recognized.
 	Common::ArchiveMemberList targets;
 	SearchMan.listMatchingMembers(targets, Common::Path("xtras/*"), true);
+	SearchMan.listMatchingMembers(targets, Common::Path("*/xtras/*"), false);
 	for (auto &it : targets) {
 		if (it->isDirectory())
 			continue;


Commit: a2f863f792884dc80ec8c1b0457f42b04cc5847f
    https://github.com/scummvm/scummvm/commit/a2f863f792884dc80ec8c1b0457f42b04cc5847f
Author: Lariaa (30549703+Lariaa at users.noreply.github.com)
Date: 2026-06-22T00:47:24+02:00

Commit Message:
DIRECTOR: LINGO: Recognise QTASSET as the QuickTime asset Xtra
Fixes L??wenzahn 2 & 3 as well as Oscar the Balloonist Drops into the Countryside

Changed paths:
    engines/director/lingo/xlibs/q/qtsupport.cpp


diff --git a/engines/director/lingo/xlibs/q/qtsupport.cpp b/engines/director/lingo/xlibs/q/qtsupport.cpp
index e440c5a2d9c..a1597237368 100644
--- a/engines/director/lingo/xlibs/q/qtsupport.cpp
+++ b/engines/director/lingo/xlibs/q/qtsupport.cpp
@@ -23,6 +23,8 @@
  *
  * USED IN:
  * The Legend of Lotus Spring
+ * L??wenzahn 2&3
+ * Oscar the Balloonist Drops into the Countryside
  *
  *************************************/
 
@@ -65,6 +67,7 @@ namespace Director {
 const char *QTSupport::xlibName = "QuickTimeSupport";
 const XlibFileDesc QTSupport::fileNames[] = {
 	{ "QuickTime Asset",	nullptr },
+	{ "QTASSET",			nullptr },
 	{ nullptr,		nullptr },
 };
 


Commit: e8c1920cfbc1c3feadb0313f92657a016c455966
    https://github.com/scummvm/scummvm/commit/e8c1920cfbc1c3feadb0313f92657a016c455966
Author: Lariaa (30549703+Lariaa at users.noreply.github.com)
Date: 2026-06-22T00:47:24+02:00

Commit Message:
DIRECTOR: LINGO: Fix FileXtra DirectoryToList path resolution

Resolve the FSNode against the game directory instead of the process working
directory, so the listing works regardless of where ScummVM was launched.

Fixes the L??wenzahn 3 cd-check

Changed paths:
    engines/director/lingo/xtras/f/filextra.cpp


diff --git a/engines/director/lingo/xtras/f/filextra.cpp b/engines/director/lingo/xtras/f/filextra.cpp
index 15ebd234e40..511a267c89f 100644
--- a/engines/director/lingo/xtras/f/filextra.cpp
+++ b/engines/director/lingo/xtras/f/filextra.cpp
@@ -33,6 +33,9 @@
  *
  * USED IN:
  * I Spy
+ * Loewenzahn 2 / 3 / 4 / 8 / Adventskalender
+ * TKKG 7 / 8 / 9 / 10
+ * Oscar the Balloonist Discovers the Sea
  *
  **************************************************/
 
@@ -155,9 +158,7 @@ XOBJSTUB(FileXtra::m_RenameFile, 0)
 XOBJSTUB(FileXtra::m_DeleteFile, 0)
 XOBJSTUB(FileXtra::m_CopyFile, 0)
 XOBJSTUB(FileXtra::m_GetFileModDate, 0)
-// DirectoryExists string dirName
-// FileXtra convention: returns 0 if the directory exists, a negative error
-// code otherwise.
+
 void FileXtra::m_DirectoryExists(int nargs) {
 	ARGNUMCHECK(1)
 	Common::String dirName = g_lingo->pop().asString();
@@ -166,10 +167,6 @@ void FileXtra::m_DirectoryExists(int nargs) {
 	g_lingo->push(Datum(path.empty() ? -1 : 0));
 }
 
-// DirectoryToList string dirName
-// Returns a Lingo list of the files and folders in dirName. Folder names are
-// suffixed with the platform path delimiter. Returns VOID if dirName is not a
-// valid directory (matching the original FileXtra behaviour).
 void FileXtra::m_DirectoryToList(int nargs) {
 	ARGNUMCHECK(1)
 	Common::String dirName = g_lingo->pop().asString();
@@ -183,7 +180,10 @@ void FileXtra::m_DirectoryToList(int nargs) {
 		return;
 	}
 
-	Common::FSNode dir(path);
+	Common::Path absPath = Common::Path(g_director->getGameDataDir()->getPath());
+	absPath.appendInPlace(Common::String(g_director->_dirSeparator), g_director->_dirSeparator);
+	absPath.appendInPlace(path);
+	Common::FSNode dir(absPath);
 	Common::FSList fslist;
 	if (!dir.isDirectory() || !dir.getChildren(fslist, Common::FSNode::kListAll)) {
 		g_lingo->push(result);


Commit: f216fd288060e14f8811629bd670588c5b128a0b
    https://github.com/scummvm/scummvm/commit/f216fd288060e14f8811629bd670588c5b128a0b
Author: Lariaa (30549703+Lariaa at users.noreply.github.com)
Date: 2026-06-22T00:47:24+02:00

Commit Message:
DIRECTOR: List German Terzio/Tivola titles in Xtra USED IN blocks

Record which Loewenzahn, TKKG and Oscar titles use each Xtra

Changed paths:
    engines/director/lingo/xtras/a/audio.cpp
    engines/director/lingo/xtras/b/budapi.cpp
    engines/director/lingo/xtras/d/directsound.cpp
    engines/director/lingo/xtras/f/filextra.cpp
    engines/director/lingo/xtras/k/keypoll.cpp
    engines/director/lingo/xtras/m/mui.cpp
    engines/director/lingo/xtras/r/rtk.cpp
    engines/director/lingo/xtras/s/setmouse.cpp


diff --git a/engines/director/lingo/xtras/a/audio.cpp b/engines/director/lingo/xtras/a/audio.cpp
index 96c0f711f22..396e6bea2d7 100644
--- a/engines/director/lingo/xtras/a/audio.cpp
+++ b/engines/director/lingo/xtras/a/audio.cpp
@@ -30,7 +30,7 @@
 /**************************************************
  *
  * USED IN:
- * [insert game here]
+ * TKKG 6 / 7
  *
  **************************************************/
 
diff --git a/engines/director/lingo/xtras/b/budapi.cpp b/engines/director/lingo/xtras/b/budapi.cpp
index a6246d05d4c..7db2e31a146 100644
--- a/engines/director/lingo/xtras/b/budapi.cpp
+++ b/engines/director/lingo/xtras/b/budapi.cpp
@@ -31,6 +31,9 @@
  *
  * USED IN:
  * I Spy Spooky Mansion
+ * Loewenzahn 2 / 3 / 4 / 5 / 6 / 7 / 8 / Adventskalender / Spielebox
+ * TKKG 6 / 7 / 8 / 9 / 11 / 13 / 14
+ * Oscar the Balloonist Flies into the Mountains
  *
  **************************************************/
 
diff --git a/engines/director/lingo/xtras/d/directsound.cpp b/engines/director/lingo/xtras/d/directsound.cpp
index bbc24f7e489..82c3f8559bf 100644
--- a/engines/director/lingo/xtras/d/directsound.cpp
+++ b/engines/director/lingo/xtras/d/directsound.cpp
@@ -33,6 +33,9 @@
  *
  * USED IN:
  * safecracker
+ * Loewenzahn 1 / 2 / 3 / 4 / 5 / 6 / 7 / 8 / Adventskalender / Spielebox
+ * TKKG 7 / 8 / 9 / 10 / 11 / 13 / 14
+ * Oscar the Balloonist (Sea / Mountains)
  *
  **************************************************/
 
diff --git a/engines/director/lingo/xtras/f/filextra.cpp b/engines/director/lingo/xtras/f/filextra.cpp
index 511a267c89f..29c1a626fbc 100644
--- a/engines/director/lingo/xtras/f/filextra.cpp
+++ b/engines/director/lingo/xtras/f/filextra.cpp
@@ -33,7 +33,7 @@
  *
  * USED IN:
  * I Spy
- * Loewenzahn 2 / 3 / 4 / 8 / Adventskalender
+ * Loewenzahn 2 / 3 / 4 / 5 / 7 / 8 / Adventskalender / Spielebox
  * TKKG 7 / 8 / 9 / 10
  * Oscar the Balloonist Discovers the Sea
  *
diff --git a/engines/director/lingo/xtras/k/keypoll.cpp b/engines/director/lingo/xtras/k/keypoll.cpp
index f11da3e04cb..07468fd6cb2 100644
--- a/engines/director/lingo/xtras/k/keypoll.cpp
+++ b/engines/director/lingo/xtras/k/keypoll.cpp
@@ -32,6 +32,7 @@
  * USED IN:
  * Safecracker
  * Teazle
+ * Loewenzahn 2 / 4 / 5
  *
  **************************************************/
 
diff --git a/engines/director/lingo/xtras/m/mui.cpp b/engines/director/lingo/xtras/m/mui.cpp
index 7d256fd44f0..02818223094 100644
--- a/engines/director/lingo/xtras/m/mui.cpp
+++ b/engines/director/lingo/xtras/m/mui.cpp
@@ -31,6 +31,8 @@
  *
  * USED IN:
  * I Spy Spooky Mansion
+ * TKKG 8 / 9 / 10 / 11 / 13 / 14
+ * Oscar the Balloonist Discovers the Sea
  *
  **************************************************/
 
diff --git a/engines/director/lingo/xtras/r/rtk.cpp b/engines/director/lingo/xtras/r/rtk.cpp
index ffbb1d54317..27b289679cb 100644
--- a/engines/director/lingo/xtras/r/rtk.cpp
+++ b/engines/director/lingo/xtras/r/rtk.cpp
@@ -34,6 +34,7 @@
  *
  * USED IN:
  * Cracking the Conspiracy
+ * Loewenzahn 1 (D5)
  *
  **************************************************/
 
diff --git a/engines/director/lingo/xtras/s/setmouse.cpp b/engines/director/lingo/xtras/s/setmouse.cpp
index 987e5775514..29d7e6d3742 100644
--- a/engines/director/lingo/xtras/s/setmouse.cpp
+++ b/engines/director/lingo/xtras/s/setmouse.cpp
@@ -30,7 +30,7 @@
 /**************************************************
  *
  * USED IN:
- * Loewenzahn 2 / 3
+ * Loewenzahn 2 / 3 / 4
  *
  **************************************************/
 



From noreply at scummvm.org  Sun Jun 21 23:51:26 2026
From: noreply at scummvm.org (sev-)
Date: Sun, 21 Jun 2026 23:51:26 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 95699a6edca96f35bf69a7a0e1981cfbc1ec8748
Message-ID: <20260621235126.A7FEC7005CE@ron.scummvm.net>

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

Summary:
95699a6edc Revert "AUDIO: Don't clamp muted channels"


Commit: 95699a6edca96f35bf69a7a0e1981cfbc1ec8748
    https://github.com/scummvm/scummvm/commit/95699a6edca96f35bf69a7a0e1981cfbc1ec8748
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2026-06-22T01:49:37+02:00

Commit Message:
Revert "AUDIO: Don't clamp muted channels"

This reverts commit 995c360cfc03ce037959ddaaab00c5b5b224f630.

Changed paths:
    audio/mixer.cpp


diff --git a/audio/mixer.cpp b/audio/mixer.cpp
index cbb47186ae6..0f6ab5298e9 100644
--- a/audio/mixer.cpp
+++ b/audio/mixer.cpp
@@ -143,11 +143,6 @@ public:
 	 */
 	uint8 getFaderR() const;
 
-	/**
-	 * Queries whether the channel is silent, i.e. will produce no audio output.
-	 */
-	bool isSilent() const { return _volL == 0 && _volR == 0; }
-
 	/**
 	 * Set the channel's sample rate.
 	 *
@@ -351,13 +346,15 @@ int MixerImpl::mixCallback(byte *samples, uint len) {
 	// Since the mixer callback has been called, the mixer must be ready...
 	_mixerReady = true;
 
+	// zero the sample buffer
+	memset(samples, 0, len);
+
 	// we store samples of size defined by the backend
 	const uint bytesPerFrame = _outBytesPerSample * (_stereo ? 2 : 1);
 	assert(len % bytesPerFrame == 0);
-	const uint numFrames = len / bytesPerFrame;
+	len /= bytesPerFrame;
 
-	// mix all channels, zeroing the buffer lazily on first non-silent channel
-	bool zeroed = false;
+	// mix all channels
 	int res = 0, tmp;
 	for (int i = 0; i != NUM_CHANNELS; i++)
 		if (_channels[i]) {
@@ -365,19 +362,14 @@ int MixerImpl::mixCallback(byte *samples, uint len) {
 				delete _channels[i];
 				_channels[i] = nullptr;
 			} else if (!_channels[i]->isPaused()) {
-				if (!_channels[i]->isSilent() && !zeroed) {
-					memset(samples, 0, len);
-					zeroed = true;
-				}
-				tmp = _channels[i]->mix(samples, numFrames);
+				tmp = _channels[i]->mix(samples, len);
 
 				if (tmp > res)
 					res = tmp;
 			}
 		}
 
-	// optimisation: let the caller know that there's nothing to clamp
-	return (!_clamp && !zeroed) ? 0 : res;
+	return res;
 }
 
 void MixerImpl::stopAll() {



From noreply at scummvm.org  Sun Jun 21 23:54:45 2026
From: noreply at scummvm.org (lotharsm)
Date: Sun, 21 Jun 2026 23:54:45 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm-web master ->
 815f8814f57cbc8d2b41feac7dabadc600fa58a2
Message-ID: <20260621235445.1A8457005CE@ron.scummvm.net>

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

Summary:
815f8814f5 DATA: Update data files


Commit: 815f8814f57cbc8d2b41feac7dabadc600fa58a2
    https://github.com/scummvm/scummvm-web/commit/815f8814f57cbc8d2b41feac7dabadc600fa58a2
Author: sev- (sev at scummvm.org)
Date: 2026-06-21T23:54:38Z

Commit Message:
DATA: Update data files

Changed paths:
    data/en/scummvm_downloads.yaml


diff --git a/data/en/scummvm_downloads.yaml b/data/en/scummvm_downloads.yaml
index 45fd0bdf..0899a2ca 100644
--- a/data/en/scummvm_downloads.yaml
+++ b/data/en/scummvm_downloads.yaml
@@ -372,20 +372,20 @@
 -
     enabled: true
     category: scummvm
-    subcategory: old
+    subcategory: release
     category_icon: macos
     user_agent: ''
-    version: 2026.2.0
+    version: 2026.3.0
     url: 'scummvm-{$version}-macosx-x86_64.dmg'
     name: 'Mac OS X 10.9+ Intel 64 Bits (without Sparkle) Disk Image'
     notes: ''
 -
     enabled: true
     category: scummvm
-    subcategory: old
+    subcategory: release
     category_icon: macostiger
     user_agent: ''
-    version: 2026.2.0
+    version: 2026.3.0
     url: 'scummvm-{$version}-macosx-i386.dmg'
     name: 'Mac OS X 10.6+ Intel 32 bits (without Sparkle) Disk Image'
     notes: ''



From noreply at scummvm.org  Mon Jun 22 00:03:25 2026
From: noreply at scummvm.org (dreammaster)
Date: Mon, 22 Jun 2026 00:03:25 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 bd0dd3d825451a883d3e1852f3de5531ceb2d875
Message-ID: <20260622000325.83A5A7005CC@ron.scummvm.net>

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:
504c93db1f MADS: FOREST: Implement journal open/close code
bd0dd3d825 MADS: FOREST: Implement daemon trigger when digi finishes playing


Commit: 504c93db1f1cd3c54240916add46a2c314114d71
    https://github.com/scummvm/scummvm/commit/504c93db1f1cd3c54240916add46a2c314114d71
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-06-22T10:03:15+10:00

Commit Message:
MADS: FOREST: Implement journal open/close code

Changed paths:
    engines/mads/madsv2/core/sprite.cpp
    engines/mads/madsv2/core/sprite.h
    engines/mads/madsv2/forest/journal.cpp
    engines/mads/madsv2/forest/journal.h


diff --git a/engines/mads/madsv2/core/sprite.cpp b/engines/mads/madsv2/core/sprite.cpp
index 2346a8e6160..9c06e9f4655 100644
--- a/engines/mads/madsv2/core/sprite.cpp
+++ b/engines/mads/madsv2/core/sprite.cpp
@@ -65,6 +65,11 @@ void sprite_draw(SeriesPtr series, int id, Buffer *buf, int target_x, int target
 #undef translate
 #undef interface
 
+void sprite_draw_clipped(SeriesPtr series, int id, int clip_x, Buffer *buf, int target_x, int target_y) {
+	// TODO: implement clipped/partial draw; for now delegate to full draw
+	sprite_draw(series, id, buf, target_x, target_y);
+}
+
 //=== sprite_draw_scaled =====================================
 #define three_d         false           /* depth coding  OFF */
 #define bresenham       true            /* bresenham     ON  */
diff --git a/engines/mads/madsv2/core/sprite.h b/engines/mads/madsv2/core/sprite.h
index 5daebd7c955..006be642afa 100644
--- a/engines/mads/madsv2/core/sprite.h
+++ b/engines/mads/madsv2/core/sprite.h
@@ -300,6 +300,7 @@ extern int sprite_error;
 
 
 void sprite_draw(SeriesPtr series, int id, Buffer *buf, int target_x, int target_y);
+void sprite_draw_clipped(SeriesPtr series, int id, int clip_x, Buffer *buf, int target_x, int target_y);
 void sprite_draw_scaled(SeriesPtr series, int id, Buffer *buf,
 	int target_x, int target_y, int scale_factor);
 void sprite_draw_3d_scaled(SeriesPtr series, int id, Buffer *buf, Buffer *attr,
diff --git a/engines/mads/madsv2/forest/journal.cpp b/engines/mads/madsv2/forest/journal.cpp
index 07ee1c45009..a3e152e4b38 100644
--- a/engines/mads/madsv2/forest/journal.cpp
+++ b/engines/mads/madsv2/forest/journal.cpp
@@ -20,25 +20,165 @@
  */
 
 #include "mads/madsv2/forest/journal.h"
+#include "mads/madsv2/core/buffer.h"
+#include "mads/madsv2/core/global.h"
+#include "mads/madsv2/core/kernel.h"
+#include "mads/madsv2/core/matte.h"
+#include "mads/madsv2/core/mouse.h"
+#include "mads/madsv2/core/player.h"
+#include "mads/madsv2/core/sprite.h"
+#include "mads/madsv2/core/timer.h"
+#include "mads/madsv2/forest/digi.h"
+#include "mads/madsv2/forest/global.h"
+#include "mads/madsv2/forest/midi.h"
 
 namespace MADS {
 namespace MADSV2 {
 namespace Forest {
 
+static int prior_room;
+
+static void change_to_journal() {
+	save_player();
+	prior_room = room_id;
+	new_room = 199;
+}
+
+static int get_prior_room() {
+	return prior_room;
+}
+
 void display_journal() {
-	// TODO
+	open_journal(1);
+	change_to_journal();
 }
 
 void leave_journal() {
-	// TODO
+	close_journal(1);
+	global[g009] = 0;
+	midi_stop();
+	new_room = get_prior_room();
 }
 
-void open_journal() {
-	// TODO
+void open_journal(int mode) {
+	mouse_hide();
+
+	int count = 0;
+	do {
+		digi_read_another_chunk();
+		if (global[g009])
+			midi_loop();
+
+		long current_time = timer_read();
+
+		buffer_rect_copy(scr_inter_orig, scr_inter, 0, 0, 165, 44);
+
+		if (mode == 1) {
+			if (count != 23) {
+				int id = count * 2 + 46;
+				int clip_x = 100 - count * 9;
+				sprite_draw_clipped(series_list[0], id, clip_x, &scr_inter, 1, 30);
+			}
+		} else {
+			sprite_draw(series_list[0], 3, &scr_inter, 1, 8);
+		}
+
+		if (mode == 2) {
+			if (count != 23) {
+				int id = count * 2 + 40;
+				int clip_x = 100 - count * 9;
+				sprite_draw_clipped(series_list[1], id, clip_x, &scr_inter, 1, 96);
+			}
+		} else {
+			sprite_draw(series_list[1], 1, &scr_inter, 1, 68);
+		}
+
+		buffer_rect_copy_2(scr_inter, scr_main, 0, 0, 0, 156, 165, 44);
+		buffer_rect_copy(scr_inter_orig, scr_inter, 168, 0, 152, 44);
+
+		if (mode == 3)
+			count = 24;
+
+		if (global[walker_converse_state])
+			sprite_draw(series_list[5], 4, &scr_inter, 1, 194);
+		else
+			sprite_draw(series_list[2], 4, &scr_inter, 1, 194);
+
+		if (mode == 4) {
+			sprite_draw(series_list[4], 2, &scr_inter, 1, 264);
+			count = 24;
+		} else {
+			sprite_draw(series_list[3], 2, &scr_inter, 1, 264);
+		}
+
+		buffer_rect_copy_2(scr_inter, scr_main, 168, 0, 168, 156, 152, 44);
+
+		while (timer_read() - current_time < 2) {}
+
+		count++;
+	} while (count < 24);
+
+	mouse_show();
 }
 
 void close_journal(int mode) {
-	// TODO
+	mouse_hide();
+
+	int count = 24;
+	do {
+		digi_read_another_chunk();
+		if (global[g009])
+			midi_loop();
+
+		long current_time = timer_read();
+
+		buffer_rect_copy(scr_inter_orig, scr_inter, 0, 0, 165, 44);
+
+		if (mode == 1) {
+			if (count != 0) {
+				int id = count * 2 + 43;
+				int clip_x = 100 - count * 9;
+				sprite_draw_clipped(series_list[0], id, clip_x, &scr_inter, 1, 30);
+			} else {
+				sprite_draw(series_list[0], 3, &scr_inter, 1, 8);
+			}
+		} else {
+			sprite_draw(series_list[0], 3, &scr_inter, 1, 8);
+		}
+
+		if (mode == 2) {
+			if (count != 0) {
+				int id = count * 2 + 37;
+				int clip_x = 100 - count * 9;
+				sprite_draw_clipped(series_list[1], id, clip_x, &scr_inter, 1, 96);
+			} else {
+				sprite_draw(series_list[1], 1, &scr_inter, 1, 68);
+			}
+		} else {
+			sprite_draw(series_list[1], 1, &scr_inter, 1, 68);
+		}
+
+		buffer_rect_copy_2(scr_inter, scr_main, 0, 0, 0, 156, 165, 44);
+		buffer_rect_copy(scr_inter_orig, scr_inter, 168, 0, 152, 44);
+
+		if (mode == 3)
+			count = 0;
+
+		if (global[walker_converse_state])
+			sprite_draw(series_list[5], 4, &scr_inter, 1, 194);
+		else
+			sprite_draw(series_list[2], 4, &scr_inter, 1, 194);
+
+		sprite_draw(series_list[3], 2, &scr_inter, 1, 264);
+
+		buffer_rect_copy_2(scr_inter, scr_main, 168, 0, 168, 156, 152, 44);
+
+		while (timer_read() - current_time < 2) {}
+
+		count--;
+	} while (count >= 0);
+
+	mouse_show();
 }
 
 } // namespace Forest
diff --git a/engines/mads/madsv2/forest/journal.h b/engines/mads/madsv2/forest/journal.h
index c0bf195f389..0d1a0bc1804 100644
--- a/engines/mads/madsv2/forest/journal.h
+++ b/engines/mads/madsv2/forest/journal.h
@@ -30,7 +30,7 @@ namespace Forest {
 
 extern void display_journal();
 extern void leave_journal();
-extern void open_journal();
+extern void open_journal(int num);
 extern void close_journal(int mode);
 
 } // namespace Forest


Commit: bd0dd3d825451a883d3e1852f3de5531ceb2d875
    https://github.com/scummvm/scummvm/commit/bd0dd3d825451a883d3e1852f3de5531ceb2d875
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-06-22T10:03:15+10:00

Commit Message:
MADS: FOREST: Implement daemon trigger when digi finishes playing

Changed paths:
    engines/mads/madsv2/core/game.cpp
    engines/mads/madsv2/core/kernel.cpp
    engines/mads/madsv2/forest/digi.cpp
    engines/mads/madsv2/forest/digi.h


diff --git a/engines/mads/madsv2/core/game.cpp b/engines/mads/madsv2/core/game.cpp
index 649ca02bf4e..9e2118ef213 100644
--- a/engines/mads/madsv2/core/game.cpp
+++ b/engines/mads/madsv2/core/game.cpp
@@ -66,6 +66,7 @@
 #include "mads/madsv2/core/imath.h"
 #include "mads/madsv2/core/screen.h"
 #include "mads/madsv2/forest/extra.h"
+#include "mads/madsv2/forest/digi.h"
 
 namespace MADS {
 namespace MADSV2 {
@@ -2300,11 +2301,13 @@ static void game_control_loop() {
 			}
 		}
 	}
-#if 0
-	digi_stop(1);
-	digi_stop(2);
-	digi_stop(3);
-#endif
+
+	if (g_engine->getGameID() == GType_Forest) {
+		Forest::digi_stop(1);
+		Forest::digi_stop(2);
+		Forest::digi_stop(3);
+	}
+
 	if (debugger) game_exec_function(debugger_reset);
 }
 
diff --git a/engines/mads/madsv2/core/kernel.cpp b/engines/mads/madsv2/core/kernel.cpp
index 94adbca3805..d23424393bf 100644
--- a/engines/mads/madsv2/core/kernel.cpp
+++ b/engines/mads/madsv2/core/kernel.cpp
@@ -59,6 +59,7 @@
 #include "mads/madsv2/core/quote.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/imath.h"
+#include "mads/madsv2/forest/digi.h"
 
 namespace MADS {
 namespace MADSV2 {
@@ -125,8 +126,6 @@ KernelGame game;                        /* Kernel level game data */
 
 Sequence sequence_list[KERNEL_MAX_SEQUENCES];
 
-int stop_speech_on_run_anim = true;
-
 KernelMessage kernel_message[KERNEL_MAX_MESSAGES];
 FontPtr kernel_message_font;
 int kernel_message_spacing;
@@ -1456,15 +1455,13 @@ int kernel_run_animation(const char *name, int trigger_code) {
 	int count;
 	int load_flags;
 	int id;
-	long largest_block;
+	int largest_block;
 
-#if 0
-	if (stop_speech_on_run_anim) {
-		digi_stop(1);
-		digi_stop(2);
-		digi_stop(3);
+	if (g_engine->getGameID() == GType_Forest && stop_speech_on_run_animation) {
+		Forest::digi_stop(1);
+		Forest::digi_stop(2);
+		Forest::digi_stop(3);
 	}
-#endif
 
 	anim_error = -2;
 
@@ -1492,7 +1489,7 @@ int kernel_run_animation(const char *name, int trigger_code) {
 	if (kernel_anim[found].anim->misc_any_packed) {
 		kernel_anim[found].buffer_id = -1;
 		id = kernel_anim[found].anim->series_id[kernel_anim[found].anim->misc_packed_series];
-		memcpy(&largest_block, &series_list[id]->misc_largest_block, sizeof(long));
+		memcpy(&largest_block, &series_list[id]->misc_largest_block, sizeof(int));
 		if (mem_get_avail() - 128 >= largest_block) {
 			mem_free(series_list[id]->arena);
 			series_list[id]->arena = (byte *)mem_get_name(largest_block * 2, "$arena$");
@@ -2960,7 +2957,7 @@ void init_kernel() {
 	memset(&kernel, 0, sizeof(Kernel));
 	memset(&game, 0, sizeof(KernelGame));
 	memset(sequence_list, 0, sizeof(sequence_list));
-	stop_speech_on_run_anim = true;
+	stop_speech_on_run_animation = true;
 	memset(kernel_message, 0, sizeof(kernel_message));
 	kernel_message_font = NULL;
 	kernel_message_spacing = 0;
@@ -2979,7 +2976,6 @@ void init_kernel() {
 	random_message_color = 0;
 	random_message_duration = 0;
 	memset(kernel_interface_loaded, 0, sizeof(kernel_interface_loaded));
-	stop_speech_on_run_animation = false;
 }
 
 void kernel_random_frame(int handle, int16 *frame, int mode) {
diff --git a/engines/mads/madsv2/forest/digi.cpp b/engines/mads/madsv2/forest/digi.cpp
index 3c4cef59988..970d2e14ba5 100644
--- a/engines/mads/madsv2/forest/digi.cpp
+++ b/engines/mads/madsv2/forest/digi.cpp
@@ -23,6 +23,7 @@
 #include "mads/madsv2/forest/digi.h"
 #include "mads/madsv2/forest/forest.h"
 #include "mads/madsv2/core/env.h"
+#include "mads/madsv2/core/kernel.h"
 
 namespace MADS {
 namespace MADSV2 {
@@ -32,9 +33,19 @@ int digi_val2;
 int digi_timing_index;
 bool digi_flag1, digi_flag2;
 
+DigiPlayer::DigiPlayer(Audio::Mixer *mixer) : _mixer(mixer) {
+	_channels[0]._triggerId = 7;
+	_channels[1]._triggerId = 8;
+	_channels[2]._triggerId = 9;
+
+	digi_val2 = 0;
+	digi_timing_index = 0;
+	digi_flag1 = digi_flag2 = false;
+}
+
 void DigiPlayer::play(const char *name, int slot) {
-	assert(slot >= 0 && slot < 8);
-	_mixer->stopHandle(_slots[slot]);
+	stop(slot);
+	DigiChannel &c = _channels[slot - 1];
 
 	Common::SeekableReadStream *src;
 	src = env_open(Common::String::format("*%s.rac", name).c_str());
@@ -51,21 +62,34 @@ void DigiPlayer::play(const char *name, int slot) {
 
 	Audio::AudioStream *audioStream = Audio::makeADPCMStream(src, DisposeAfterUse::YES,
 		src->size() - 0x20, Audio::kADPCMDVI, 11025, 1);
-	_mixer->playStream(Audio::Mixer::kSFXSoundType, &_slots[slot], audioStream);
+	_mixer->playStream(Audio::Mixer::kSFXSoundType, &c._soundHandle, audioStream);
+	c._isPlaying = true;
 }
 
 void DigiPlayer::stop(int slot) {
-	assert(slot >= 0 && slot < 8);
-	_mixer->stopHandle(_slots[slot]);
-}
+	assert(slot >= 1 && slot <= MAX_DIGI_CHANNELS);
+	DigiChannel &c = _channels[slot - 1];
 
+	if (c._isPlaying) {
+		_mixer->stopHandle(c._soundHandle);
+		c._isPlaying = false;
 
-void digi_install() {
-	digi_val2 = 0;
-	digi_timing_index = 0;
+		if (c._endTrigger) {
+			kernel.trigger_setup_mode = KERNEL_TRIGGER_DAEMON;
+			kernel_timing_trigger(1, c._triggerId);
+		}
+
+		c._endTrigger = true;
+	}
 }
 
-void digi_uninstall() {
+void DigiPlayer::poll() {
+	for (int slot = 1; slot <= MAX_DIGI_CHANNELS; ++slot) {
+		DigiChannel &c = _channels[slot - 1];
+
+		if (c._isPlaying && !_mixer->isSoundHandleActive(c._soundHandle))
+			digi_stop(slot);
+	}
 }
 
 void digi_play_build_ii(char thing, int num, int slot) {
@@ -74,9 +98,9 @@ void digi_play_build_ii(char thing, int num, int slot) {
 	digi_play(name.c_str(), slot);
 }
 
-void digi_play_build(int room, char thing, int num, int slot) {
+void digi_play_build(int roomNum, char thing, int num, int slot) {
 	Common::String name = Common::String::format("%c%d%c%03d",
-		(thing == '_') ? 's' : 'd', room, thing, num);
+		(thing == '_') ? 's' : 'd', roomNum, thing, num);
 	digi_play(name.c_str(), slot);
 }
 
@@ -88,6 +112,10 @@ void digi_stop(int which_one) {
 	g_engine->_digiPlayer.stop(which_one);
 }
 
+void digi_read_another_chunk() {
+	g_engine->_digiPlayer.poll();
+}
+
 void digi_initial_volume(int vol) {
 }
 
diff --git a/engines/mads/madsv2/forest/digi.h b/engines/mads/madsv2/forest/digi.h
index accd62d3520..1561a375399 100644
--- a/engines/mads/madsv2/forest/digi.h
+++ b/engines/mads/madsv2/forest/digi.h
@@ -29,31 +29,52 @@ namespace MADS {
 namespace MADSV2 {
 namespace Forest {
 
+#define MAX_DIGI_CHANNELS 3
+
 class DigiPlayer {
+	struct DigiChannel {
+		Audio::SoundHandle _soundHandle;
+		bool _isPlaying = false;
+		int _triggerId;
+		bool _endTrigger = true;
+	};
 private:
 	Audio::Mixer *_mixer;
-	Audio::SoundHandle _slots[8];
+	DigiChannel _channels[MAX_DIGI_CHANNELS];
+
 public:
-	DigiPlayer(Audio::Mixer *mixer) : _mixer(mixer) {
-	}
+	DigiPlayer(Audio::Mixer *mixer);
 
+	/**
+	 * Play a digital sound
+	 * @param name	Resource name
+	 * @param slot	Channel to play it in
+	*/
 	void play(const char *name, int slot);
+
+	/**
+	 * Stop any playing digital sound in a particular channel
+	 * @param slot	Channel to stop
+	 */
 	void stop(int slot);
+
+	/**
+	 * Polls for any playing sounds that are finished
+	 */
+	void poll();
 };
 
 extern int digi_val2;
 extern int digi_timing_index;
 extern bool digi_flag1, digi_flag2;
 
-extern void digi_install();
 extern void digi_play(const char *name, int slot);
-extern void digi_play_build(int room, char thing, int num, int slot);
+extern void digi_play_build(int roomNum, char thing, int num, int slot);
 extern void digi_play_build_ii(char thing, int num, int slot);
 extern void digi_stop(int which_one);
-extern void digi_uninstall();
 extern void digi_initial_volume(int vol);
 extern void digi_set_volume(int vol, int slot);
-inline void digi_read_another_chunk() {}
+extern void digi_read_another_chunk();
 
 } // namespace Forest
 } // namespace MADSV2



From noreply at scummvm.org  Mon Jun 22 01:22:31 2026
From: noreply at scummvm.org (bluegr)
Date: Mon, 22 Jun 2026 01:22:31 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 b2cabfb4d1d28c318b34a1671eeda3122e6f80f8
Message-ID: <20260622012231.0A03F7005CC@ron.scummvm.net>

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

Summary:
b2cabfb4d1 NANCY: Fix font related TODOs for ConversationPopup in Nancy10+


Commit: b2cabfb4d1d28c318b34a1671eeda3122e6f80f8
    https://github.com/scummvm/scummvm/commit/b2cabfb4d1d28c318b34a1671eeda3122e6f80f8
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2026-06-22T04:22:21+03:00

Commit Message:
NANCY: Fix font related TODOs for ConversationPopup in Nancy10+

Changed paths:
    engines/nancy/misc/hypertext.cpp
    engines/nancy/ui/conversationpopup.cpp


diff --git a/engines/nancy/misc/hypertext.cpp b/engines/nancy/misc/hypertext.cpp
index 39ea5c730e1..99ad71b1d98 100644
--- a/engines/nancy/misc/hypertext.cpp
+++ b/engines/nancy/misc/hypertext.cpp
@@ -60,6 +60,13 @@ void HypertextParser::setImageName(const Common::Path &name) {
 	_imageName = name;
 }
 
+static uint lineStep(const Graphics::Font *font) {
+	const uint h = font->getFontHeight();
+	if (g_nancy->getGameType() >= kGameTypeNancy10)
+		return h + h / 9;
+	return h;
+}
+
 void HypertextParser::drawAllText(const Common::Rect &textBounds, uint leftOffsetNonNewline, uint fontID, uint highlightFontID) {
 	using namespace Common;
 
@@ -235,7 +242,7 @@ void HypertextParser::drawAllText(const Common::Rect &textBounds, uint leftOffse
 		// Setup most of the hotspot; textbox
 		if (hasHotspot) {
 			hotspot.left = textBounds.left;
-			hotspot.top = textBounds.top + (_numDrawnLines * font->getFontHeight()) - 1;
+			hotspot.top = textBounds.top + (_numDrawnLines * lineStep(font)) - 1;
 			hotspot.setHeight(0);
 			hotspot.setWidth(0);
 		}
@@ -274,7 +281,7 @@ void HypertextParser::drawAllText(const Common::Rect &textBounds, uint leftOffse
 
 						_fullSurface.blitFrom(image, _imageSrcs[i],
 							Common::Point(	textBounds.left + horizontalOffset + 1,
-											textBounds.top + _numDrawnLines * highlightFont->getFontHeight() + _imageVerticalOffset));
+											textBounds.top + _numDrawnLines * lineStep(highlightFont) + _imageVerticalOffset));
 						_imageVerticalOffset += _imageSrcs[i].height() - 1;
 
 						if (lineNumber == 0) {
@@ -330,7 +337,7 @@ void HypertextParser::drawAllText(const Common::Rect &textBounds, uint leftOffse
 						markDest.moveTo(textBounds.left + horizontalOffset + (newLineStart ? 0 : leftOffsetNonNewline) + 1,
 							lineNumber == 0 ?
 								textBounds.top - ((font->getFontHeight() + 1) / 2) + _imageVerticalOffset + 4 :
-								textBounds.top + _numDrawnLines * font->getFontHeight() + _imageVerticalOffset - 4);
+								textBounds.top + _numDrawnLines * lineStep(font) + _imageVerticalOffset - 4);
 
 						// For now we do not check if we need to go to new line; neither does the original
 						_fullSurface.blitFrom(g_nancy->_graphics->_object0, markSrc, markDest);
@@ -344,7 +351,7 @@ void HypertextParser::drawAllText(const Common::Rect &textBounds, uint leftOffse
 
 						if (hasHotspot) {
 							hotspot.left = textBounds.left + (newLineStart ? 0 : horizontalOffset + leftOffsetNonNewline);
-							hotspot.top = textBounds.top + _numDrawnLines * font->getFontHeight() + _imageVerticalOffset - 1;
+							hotspot.top = textBounds.top + _numDrawnLines * lineStep(font) + _imageVerticalOffset - 1;
 							hotspot.setHeight(0);
 							hotspot.setWidth(0);
 						} else {
@@ -382,7 +389,7 @@ void HypertextParser::drawAllText(const Common::Rect &textBounds, uint leftOffse
 				font->drawString(				&_fullSurface,
 												stringToDraw,
 												textBounds.left + horizontalOffset + (newLineStart ? 0 : leftOffsetNonNewline),
-												textBounds.top + _numDrawnLines * font->getFontHeight() + _imageVerticalOffset,
+												textBounds.top + _numDrawnLines * lineStep(font) + _imageVerticalOffset,
 												textBounds.width(),
 												colorID);
 
@@ -391,7 +398,7 @@ void HypertextParser::drawAllText(const Common::Rect &textBounds, uint leftOffse
 					highlightFont->drawString(	&_textHighlightSurface,
 												stringToDraw,
 												textBounds.left + horizontalOffset + (newLineStart ? leftOffsetNonNewline : 0),
-												textBounds.top + _numDrawnLines * highlightFont->getFontHeight() + _imageVerticalOffset,
+												textBounds.top + _numDrawnLines * lineStep(highlightFont) + _imageVerticalOffset,
 												textBounds.width(),
 												colorID);
 				}
@@ -409,7 +416,7 @@ void HypertextParser::drawAllText(const Common::Rect &textBounds, uint leftOffse
 					hotspot.setWidth(MAX<int16>(hotspot.width(), font->getStringWidth(stringToDraw)));
 
 					if (!stringToDraw.empty() && newWrappedLine) {
-						hotspot.setHeight(hotspot.height() + font->getFontHeight());
+						hotspot.setHeight(hotspot.height() + lineStep(font));
 					}
 				}
 
@@ -425,7 +432,7 @@ void HypertextParser::drawAllText(const Common::Rect &textBounds, uint leftOffse
 			++_numDrawnLines;
 
 			// Record the height of the text currently drawn. Used for textbox scrolling
-			_drawnTextHeight = (_numDrawnLines - 1) * font->getFontHeight() + _imageVerticalOffset;
+			_drawnTextHeight = (_numDrawnLines - 1) * lineStep(font) + _imageVerticalOffset;
 		}
 
 		// Draw the footer image(s)
@@ -435,14 +442,14 @@ void HypertextParser::drawAllText(const Common::Rect &textBounds, uint leftOffse
 
 				_fullSurface.blitFrom(image, _imageSrcs[i],
 					Common::Point(	textBounds.left + horizontalOffset + 1,
-									textBounds.top + _numDrawnLines * highlightFont->getFontHeight() + _imageVerticalOffset));
+									textBounds.top + _numDrawnLines * lineStep(highlightFont) + _imageVerticalOffset));
 				_imageVerticalOffset += _imageSrcs[i].height() - 1;
 
 				if (i < _imageLineIDs.size() - 1) {
 					_imageVerticalOffset += (font->getFontHeight() + 1) / 2 + 3;
 				}
 
-				_drawnTextHeight = (_numDrawnLines - 1) * font->getFontHeight() + _imageVerticalOffset;
+				_drawnTextHeight = (_numDrawnLines - 1) * lineStep(font) + _imageVerticalOffset;
 			}
 		}
 
@@ -466,12 +473,12 @@ void HypertextParser::drawAllText(const Common::Rect &textBounds, uint leftOffse
 
 		// Add a newline after every full piece of text
 		++_numDrawnLines;
-		_drawnTextHeight += font->getFontHeight();
+		_drawnTextHeight += lineStep(font);
 	}
 
 	// Add a line's height at end of text to replicate original behavior
 	if (font) {
-		_drawnTextHeight += font->getFontHeight();
+		_drawnTextHeight += lineStep(font);
 	}
 
 	_needsTextRedraw = false;
diff --git a/engines/nancy/ui/conversationpopup.cpp b/engines/nancy/ui/conversationpopup.cpp
index dbc44d6abc3..13a84a1a891 100644
--- a/engines/nancy/ui/conversationpopup.cpp
+++ b/engines/nancy/ui/conversationpopup.cpp
@@ -124,15 +124,9 @@ void ConversationPopup::drawContent() {
 	_fullSurface.fillRect(Common::Rect(0, 0, _fullSurface.w, _fullSurface.h), 0);
 	_textHighlightSurface.fillRect(Common::Rect(0, 0, _textHighlightSurface.w, _textHighlightSurface.h), 0);
 
-	// TODO: Padding doesn't match the original game. leftOffset returns an
-	// unexpected value for Nancy 10+, so upOffset is used as a fallback for
-	// both axes until the TBOX layout for Nancy 10+ is better understood.
-
-	// TODO: Line spacing is tighter than the original game.
-	
 	Common::Rect textBounds(0, 0, _fullSurface.w, _fullSurface.h);
-	textBounds.top += _tboxData->upOffset;
-	textBounds.left += _tboxData->upOffset;
+	textBounds.top  += _tboxData->scrollbarDefaultPos.y;
+	textBounds.left += _tboxData->scrollbarDefaultPos.x;
 
 	drawAllText(textBounds, 0, _tboxData->conversationFontID, _tboxData->highlightConversationFontID);
 
@@ -156,7 +150,7 @@ void ConversationPopup::drawContent() {
 }
 
 uint16 ConversationPopup::getInnerHeight() const {
-	return _drawnTextHeight + _tboxData->upOffset;
+	return _drawnTextHeight + _tboxData->scrollbarDefaultPos.y;
 }
 
 Common::Rect ConversationPopup::toPopupLocal(const Common::Rect &chunkRect, bool useGameFrame) const {



From noreply at scummvm.org  Mon Jun 22 01:25:41 2026
From: noreply at scummvm.org (bluegr)
Date: Mon, 22 Jun 2026 01:25:41 +0000 (UTC)
Subject: [Scummvm-git-logs] scummvm master ->
 84a8cc128fee19f0e4707d2cd16bb2c2e23cd946
Message-ID: <20260622012541.A99907005CC@ron.scummvm.net>

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

Summary:
84a8cc128f GUI: Fix bitmap rendering with RGB332 overlays


Commit: 84a8cc128fee19f0e4707d2cd16bb2c2e23cd946
    https://github.com/scummvm/scummvm/commit/84a8cc128fee19f0e4707d2cd16bb2c2e23cd946
Author: Cameron Cawley (ccawley2011 at gmail.com)
Date: 2026-06-22T04:25:38+03:00

Commit Message:
GUI: Fix bitmap rendering with RGB332 overlays

Changed paths:
    graphics/VectorRendererSpec.cpp


diff --git a/graphics/VectorRendererSpec.cpp b/graphics/VectorRendererSpec.cpp
index bbdcbe585fe..ec668830090 100644
--- a/graphics/VectorRendererSpec.cpp
+++ b/graphics/VectorRendererSpec.cpp
@@ -803,7 +803,12 @@ blitManagedSurface(const Graphics::ManagedSurface *source, const Common::Point &
 		np = p;
 	}
 
-	_activeSurface->simpleBlitFrom(*source, drawRect, np, Graphics::FLIP_NONE, alphaType != Graphics::ALPHA_OPAQUE);
+	if (_activeSurface->format.bytesPerPixel == 1 && alphaType != Graphics::ALPHA_OPAQUE) {
+		// simpleBlitFrom doesn't currently support alpha blending to an RGB332 surface.
+		_activeSurface->transBlitFrom(*source, drawRect, np);
+	} else {
+		_activeSurface->simpleBlitFrom(*source, drawRect, np, Graphics::FLIP_NONE, alphaType != Graphics::ALPHA_OPAQUE);
+	}
 }
 
 template<typename PixelType>



